diff --git a/.editorconfig b/.editorconfig new file mode 100644 index 0000000..b9c127d --- /dev/null +++ b/.editorconfig @@ -0,0 +1,12 @@ +# EditorConfig is awesome: https://EditorConfig.org + +# top-most EditorConfig file +root = true + +[*] +indent_style = space +indent_size = 4 +end_of_line = crlf +charset = utf-8 +trim_trailing_whitespace = false +insert_final_newline = false \ No newline at end of file diff --git a/src/app/src/main/AndroidManifest.xml b/src/app/src/main/AndroidManifest.xml index ac07441..6bf5987 100644 --- a/src/app/src/main/AndroidManifest.xml +++ b/src/app/src/main/AndroidManifest.xml @@ -1,6 +1,7 @@ - + @@ -33,18 +34,25 @@ 支持从右到左的布局 应用主题 目标 API 级别--> - - + - android:configChanges="keyboardHidden|orientation|screenSize" - android:label="@string/app_name" - android:launchMode="singleTop" - android:theme="@style/NoteTheme" - android:uiOptions="splitActionBarWhenNarrow" - android:windowSoftInputMode="adjustPan" - android:exported="true"> - + android:name=".ui.NotesListActivity" + android:configChanges="keyboardHidden|orientation|screenSize" + android:label="@string/app_name" + android:launchMode="singleTop" + android:theme="@style/NoteTheme" + android:uiOptions="splitActionBarWhenNarrow" + android:windowSoftInputMode="adjustPan" + android:exported="true"> + @@ -55,14 +63,22 @@ - android:configChanges="keyboardHidden|orientation|screenSize" - android:exported="true" - android:launchMode="singleTop" - android:theme="@style/NoteTheme"> + android:name=".ui.NoteEditActivity" + android:configChanges="keyboardHidden|orientation|screenSize" + android:exported="true" + android:launchMode="singleTop" + android:theme="@style/NoteTheme"> + + + + + + + - + @@ -90,15 +106,22 @@ - android:authorities="micode_notes" - android:multiprocess="true" /> - - + android:name="net.micode.notes.data.NotesProvider" + android:authorities="micode_notes" + android:multiprocess="true" /> + + + + + - android:exported="true" - android:label="@string/app_widget2x2"> + android:name=".widget.NoteWidgetProvider_2x" + android:exported="true" + android:label="@string/app_widget2x2"> + + + + @@ -113,14 +136,19 @@ - android:exported="true" - android:label="@string/app_widget4x4"> + android:name=".widget.NoteWidgetProvider_4x" + android:exported="true" + android:label="@string/app_widget4x4"> + + + + + - + - android:exported="true"> + android:name=".ui.AlarmInitReceiver" + android:exported="true"> + + + + @@ -138,28 +170,40 @@ - android:process=":remote"> + android:name="net.micode.notes.ui.AlarmReceiver" + android:process=":remote"> + + - android:label="@string/app_name" - android:launchMode="singleInstance" - android:theme="@android:style/Theme.Holo.Wallpaper.NoTitleBar"> + android:name=".ui.AlarmAlertActivity" + android:label="@string/app_name" + android:launchMode="singleInstance" + android:theme="@android:style/Theme.Holo.Wallpaper.NoTitleBar"> + + + + - android:label="@string/preferences_title" - android:launchMode="singleTop" - android:theme="@android:style/Theme.Holo.Light" /> + android:name="net.micode.notes.ui.NotesPreferenceActivity" + android:label="@string/preferences_title" + android:launchMode="singleTop" + android:theme="@android:style/Theme.Holo.Light" /> + + + + - android:exported="false"> + android:name="net.micode.notes.gtask.remote.GTaskSyncService" + android:exported="false"> + + - \ No newline at end of file + diff --git a/src/app/src/main/java/net/micode/notes/data/Contact.java b/src/app/src/main/java/net/micode/notes/data/Contact.java index c328a90..53d24e9 100644 --- a/src/app/src/main/java/net/micode/notes/data/Contact.java +++ b/src/app/src/main/java/net/micode/notes/data/Contact.java @@ -26,47 +26,72 @@ import android.util.Log; import java.util.HashMap; public class Contact { + // 用于日志记录的标签,方便调试和错误追踪 private static final String TAG = "Contact"; + + // SQL 查询条件,用于查找与给定电话号码匹配的联系人 private static final String CALLER_ID_SELECTION = "PHONE_NUMBERS_EQUAL(" + Phone.NUMBER - + ",?) AND " + Data.MIMETYPE + "='" + Phone.CONTENT_ITEM_TYPE + "'" - + " AND " + Data.RAW_CONTACT_ID + " IN " - + "(SELECT raw_contact_id " - + " FROM phone_lookup" - + " WHERE min_match = '+')"; + + ",?) AND " + Data.MIMETYPE + "='" + Phone.CONTENT_ITEM_TYPE + "'" + + " AND " + Data.RAW_CONTACT_ID + " IN " + + "(SELECT raw_contact_id " + + " FROM phone_lookup" + + " WHERE min_match = '+')"; + + // 用于缓存联系人名称的 HashMap,避免重复查询 private static HashMap sContactCache; + /** + * 根据电话号码获取联系人名称 + * + * @param context 应用的上下文,用于访问内容解析器 + * @param phoneNumber 要查询的电话号码,格式可以是国际格式或本地格式 + * @return 返回联系人名称,如果未找到匹配的联系人则返回 null + */ public static String getContact(Context context, String phoneNumber) { + // 初始化缓存,如果缓存尚未创建 if (sContactCache == null) { sContactCache = new HashMap(); } + // 检查缓存中是否已有该电话号码的联系人名称 if (sContactCache.containsKey(phoneNumber)) { + // 如果缓存中存在,直接返回联系人名称 return sContactCache.get(phoneNumber); } + // 使用 PhoneNumberUtils 将电话号码转换为最小匹配格式,并替换选择条件中的 "+" String selection = CALLER_ID_SELECTION.replace("+", - PhoneNumberUtils.toCallerIDMinMatch(phoneNumber)); + PhoneNumberUtils.toCallerIDMinMatch(phoneNumber)); + + // 查询联系人数据,使用内容解析器访问联系人数据库 Cursor cursor = context.getContentResolver().query( - Data.CONTENT_URI, - new String[]{Phone.DISPLAY_NAME}, - selection, - new String[]{phoneNumber}, - null); + Data.CONTENT_URI, // 查询的 URI + new String[]{Phone.DISPLAY_NAME}, // 查询的字段,这里只查询联系人名称 + selection, // 查询条件 + new String[]{phoneNumber}, // 查询参数,电话号码 + null); // 排序条件,设置为 null 表示不排序 + // 处理查询结果 if (cursor != null && cursor.moveToFirst()) { try { + // 获取查询结果中的联系人名称 String name = cursor.getString(0); + // 将联系人名称缓存到 HashMap 中,以便下次快速访问 sContactCache.put(phoneNumber, name); + // 返回联系人名称 return name; } catch (IndexOutOfBoundsException e) { - Log.e(TAG, " Cursor get string error " + e.toString()); - return null; + // 处理可能的索引越界异常,通常是由于查询结果为空或字段索引不正确 + Log.e(TAG, "Cursor get string error " + e.toString()); + return null; // 返回 null 表示未找到联系人 } finally { + // 确保游标在使用后关闭,避免内存泄漏 cursor.close(); } } else { + // 如果没有找到匹配的联系人,记录调试信息 Log.d(TAG, "No contact matched with number:" + phoneNumber); - return null; + return null; // 返回 null 表示未找到联系人 } } } diff --git a/src/app/src/main/java/net/micode/notes/data/Notes.java b/src/app/src/main/java/net/micode/notes/data/Notes.java index 1e070f6..283a98b 100644 --- a/src/app/src/main/java/net/micode/notes/data/Notes.java +++ b/src/app/src/main/java/net/micode/notes/data/Notes.java @@ -1,41 +1,45 @@ /* * Copyright (c) 2010-2011, The MiCode Open Source Community (www.micode.net) * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. + * Licensed under Apache License, Version 2.0 (the "License"); + * 按照Apache许可证2.0版本许可。 + * 详情请参阅 http://www.apache.org/licenses/LICENSE-2.0 */ package net.micode.notes.data; import android.net.Uri; +/** + * Notes类用于定义便签和文件夹相关的数据结构和常量。 + * 包括URI定义、字段映射和系统相关的标识符。 + */ public class Notes { + + // 内容提供者的授权标识符,用于构建URI public static final String AUTHORITY = "micode_notes"; + + // 用于日志记录的标识符 public static final String TAG = "Notes"; - public static final int TYPE_NOTE = 0; - public static final int TYPE_FOLDER = 1; - public static final int TYPE_SYSTEM = 2; + + // 类型常量,用于区分便签、文件夹和系统文件夹 + public static final int TYPE_NOTE = 0; // 普通便签 + public static final int TYPE_FOLDER = 1; // 文件夹 + public static final int TYPE_SYSTEM = 2; // 系统文件夹 /** - * Following IDs are system folders' identifiers - * {@link Notes#ID_ROOT_FOLDER } is default folder - * {@link Notes#ID_TEMPARAY_FOLDER } is for notes belonging no folder - * {@link Notes#ID_CALL_RECORD_FOLDER} is to store call records + * 系统文件夹的预定义ID: + * - ID_ROOT_FOLDER:根文件夹,包含所有便签和文件夹。 + * - ID_TEMPARAY_FOLDER:临时文件夹,存储未分配到具体文件夹的便签。 + * - ID_CALL_RECORD_FOLDER:存储通话记录的文件夹。 + * - ID_TRASH_FOLER:回收站文件夹,用于存储已删除的便签。 */ public static final int ID_ROOT_FOLDER = 0; public static final int ID_TEMPARAY_FOLDER = -1; public static final int ID_CALL_RECORD_FOLDER = -2; public static final int ID_TRASH_FOLER = -3; + // Intent附加数据的键名,用于便签的属性和元数据传递 public static final String INTENT_EXTRA_ALERT_DATE = "net.micode.notes.alert_date"; public static final String INTENT_EXTRA_BACKGROUND_ID = "net.micode.notes.background_color_id"; public static final String INTENT_EXTRA_WIDGET_ID = "net.micode.notes.widget_id"; @@ -43,236 +47,86 @@ public class Notes { public static final String INTENT_EXTRA_FOLDER_ID = "net.micode.notes.folder_id"; public static final String INTENT_EXTRA_CALL_DATE = "net.micode.notes.call_date"; - public static final int TYPE_WIDGET_INVALIDE = -1; - public static final int TYPE_WIDGET_2X = 0; - public static final int TYPE_WIDGET_4X = 1; + // 小部件类型常量,用于管理便签的小部件 + public static final int TYPE_WIDGET_INVALIDE = -1; // 无效的小部件类型 + public static final int TYPE_WIDGET_2X = 0; // 2x2的小部件 + public static final int TYPE_WIDGET_4X = 1; // 4x4的小部件 + /** - * Uri to query all notes and folders + * URI定义: + * - CONTENT_NOTE_URI:用于查询所有便签和文件夹的数据。 + * - CONTENT_DATA_URI:用于查询便签相关的数据内容。 */ public static final Uri CONTENT_NOTE_URI = Uri.parse("content://" + AUTHORITY + "/note"); - /** - * Uri to query data - */ public static final Uri CONTENT_DATA_URI = Uri.parse("content://" + AUTHORITY + "/data"); + /** + * NoteColumns接口定义便签表的字段常量。 + */ public interface NoteColumns { - /** - * The unique ID for a row - *

Type: INTEGER (long)

- */ - public static final String ID = "_id"; - - /** - * The parent's id for note or folder - *

Type: INTEGER (long)

- */ - public static final String PARENT_ID = "parent_id"; - - /** - * Created data for note or folder - *

Type: INTEGER (long)

- */ - public static final String CREATED_DATE = "created_date"; - - /** - * Latest modified date - *

Type: INTEGER (long)

- */ - public static final String MODIFIED_DATE = "modified_date"; - - - /** - * Alert date - *

Type: INTEGER (long)

- */ - public static final String ALERTED_DATE = "alert_date"; - - /** - * Folder's name or text content of note - *

Type: TEXT

- */ - public static final String SNIPPET = "snippet"; - - /** - * Note's widget id - *

Type: INTEGER (long)

- */ - public static final String WIDGET_ID = "widget_id"; - - /** - * Note's widget type - *

Type: INTEGER (long)

- */ - public static final String WIDGET_TYPE = "widget_type"; - - /** - * Note's background color's id - *

Type: INTEGER (long)

- */ - public static final String BG_COLOR_ID = "bg_color_id"; - - /** - * For text note, it doesn't has attachment, for multi-media - * note, it has at least one attachment - *

Type: INTEGER

- */ - public static final String HAS_ATTACHMENT = "has_attachment"; - - /** - * Folder's count of notes - *

Type: INTEGER (long)

- */ - public static final String NOTES_COUNT = "notes_count"; - - /** - * The file type: folder or note - *

Type: INTEGER

- */ - public static final String TYPE = "type"; - - /** - * The last sync id - *

Type: INTEGER (long)

- */ - public static final String SYNC_ID = "sync_id"; - - /** - * Sign to indicate local modified or not - *

Type: INTEGER

- */ - public static final String LOCAL_MODIFIED = "local_modified"; - - /** - * Original parent id before moving into temporary folder - *

Type : INTEGER

- */ - public static final String ORIGIN_PARENT_ID = "origin_parent_id"; - - /** - * The gtask id - *

Type : TEXT

- */ - public static final String GTASK_ID = "gtask_id"; - - /** - * The version code - *

Type : INTEGER (long)

- */ - public static final String VERSION = "version"; + String ID = "_id"; // 唯一标识ID + String PARENT_ID = "parent_id"; // 父节点ID,标识便签或文件夹的父节点 + String CREATED_DATE = "created_date"; // 创建时间戳 + String MODIFIED_DATE = "modified_date"; // 最近修改时间戳 + String ALERTED_DATE = "alert_date"; // 提醒时间 + String SNIPPET = "snippet"; // 文件夹名称或便签内容 + String WIDGET_ID = "widget_id"; // 关联小部件的ID + String WIDGET_TYPE = "widget_type"; // 小部件类型 + String BG_COLOR_ID = "bg_color_id"; // 背景颜色ID + String HAS_ATTACHMENT = "has_attachment"; // 是否包含附件 + String NOTES_COUNT = "notes_count"; // 文件夹中便签的数量 + String TYPE = "type"; // 文件类型(便签或文件夹) + String SYNC_ID = "sync_id"; // 云同步ID + String LOCAL_MODIFIED = "local_modified"; // 是否本地修改 + String ORIGIN_PARENT_ID = "origin_parent_id"; // 移动前的原父节点ID + String GTASK_ID = "gtask_id"; // 用于同步的Google任务ID + String VERSION = "version"; // 版本号 } + /** + * DataColumns接口定义数据表的字段常量。 + */ public interface DataColumns { - /** - * The unique ID for a row - *

Type: INTEGER (long)

- */ - public static final String ID = "_id"; - - /** - * The MIME type of the item represented by this row. - *

Type: Text

- */ - public static final String MIME_TYPE = "mime_type"; - - /** - * The reference id to note that this data belongs to - *

Type: INTEGER (long)

- */ - public static final String NOTE_ID = "note_id"; - - /** - * Created data for note or folder - *

Type: INTEGER (long)

- */ - public static final String CREATED_DATE = "created_date"; - - /** - * Latest modified date - *

Type: INTEGER (long)

- */ - public static final String MODIFIED_DATE = "modified_date"; - - /** - * Data's content - *

Type: TEXT

- */ - public static final String CONTENT = "content"; - - - /** - * Generic data column, the meaning is {@link #MIMETYPE} specific, used for - * integer data type - *

Type: INTEGER

- */ - public static final String DATA1 = "data1"; - - /** - * Generic data column, the meaning is {@link #MIMETYPE} specific, used for - * integer data type - *

Type: INTEGER

- */ - public static final String DATA2 = "data2"; - - /** - * Generic data column, the meaning is {@link #MIMETYPE} specific, used for - * TEXT data type - *

Type: TEXT

- */ - public static final String DATA3 = "data3"; - - /** - * Generic data column, the meaning is {@link #MIMETYPE} specific, used for - * TEXT data type - *

Type: TEXT

- */ - public static final String DATA4 = "data4"; - - /** - * Generic data column, the meaning is {@link #MIMETYPE} specific, used for - * TEXT data type - *

Type: TEXT

- */ - public static final String DATA5 = "data5"; + String ID = "_id"; // 唯一标识ID + String MIME_TYPE = "mime_type"; // 数据的MIME类型 + String NOTE_ID = "note_id"; // 所属便签的引用ID + String CREATED_DATE = "created_date"; // 创建时间戳 + String MODIFIED_DATE = "modified_date"; // 最近修改时间戳 + String CONTENT = "content"; // 数据内容 + String DATA1 = "data1"; // 通用数据字段1 + String DATA2 = "data2"; // 通用数据字段2 + String DATA3 = "data3"; // 通用数据字段3 + String DATA4 = "data4"; // 通用数据字段4 + String DATA5 = "data5"; // 通用数据字段5 } + /** + * DataConstants类定义数据的特定类型常量。 + */ public static class DataConstants { - public static final String NOTE = TextNote.CONTENT_ITEM_TYPE; - public static final String CALL_NOTE = CallNote.CONTENT_ITEM_TYPE; + public static final String NOTE = TextNote.CONTENT_ITEM_TYPE; // 文本便签 + public static final String CALL_NOTE = CallNote.CONTENT_ITEM_TYPE; // 通话记录便签 } + /** + * TextNote类定义与文本便签相关的常量。 + */ public static final class TextNote implements DataColumns { - /** - * Mode to indicate the text in check list mode or not - *

Type: Integer 1:check list mode 0: normal mode

- */ - public static final String MODE = DATA1; - - public static final int MODE_CHECK_LIST = 1; - + public static final String MODE = DATA1; // 检查列表模式 + public static final int MODE_CHECK_LIST = 1; // 检查列表模式标识 public static final String CONTENT_TYPE = "vnd.android.cursor.dir/text_note"; - public static final String CONTENT_ITEM_TYPE = "vnd.android.cursor.item/text_note"; - public static final Uri CONTENT_URI = Uri.parse("content://" + AUTHORITY + "/text_note"); } + /** + * CallNote类定义与通话记录便签相关的常量。 + */ public static final class CallNote implements DataColumns { - /** - * Call date for this record - *

Type: INTEGER (long)

- */ - public static final String CALL_DATE = DATA1; - - /** - * Phone number for this record - *

Type: TEXT

- */ - public static final String PHONE_NUMBER = DATA3; - + public static final String CALL_DATE = DATA1; // 通话时间 + public static final String PHONE_NUMBER = DATA3; // 电话号码 public static final String CONTENT_TYPE = "vnd.android.cursor.dir/call_note"; - public static final String CONTENT_ITEM_TYPE = "vnd.android.cursor.item/call_note"; - public static final Uri CONTENT_URI = Uri.parse("content://" + AUTHORITY + "/call_note"); } } diff --git a/src/app/src/main/java/net/micode/notes/data/NotesDatabaseHelper.java b/src/app/src/main/java/net/micode/notes/data/NotesDatabaseHelper.java index d9f6a6a..7984b01 100644 --- a/src/app/src/main/java/net/micode/notes/data/NotesDatabaseHelper.java +++ b/src/app/src/main/java/net/micode/notes/data/NotesDatabaseHelper.java @@ -23,172 +23,140 @@ import android.database.sqlite.SQLiteOpenHelper; import android.util.Log; import net.micode.notes.data.Notes.DataColumns; -import net.micode.notes.data.Notes.DataConstants; import net.micode.notes.data.Notes.NoteColumns; - public class NotesDatabaseHelper extends SQLiteOpenHelper { + // 数据库名称 private static final String DB_NAME = "note.db"; - + // 数据库版本号 private static final int DB_VERSION = 4; private static final String TAG = "NotesDatabaseHelper"; + + // 创建 note 表的 SQL 语句 private static final String CREATE_NOTE_TABLE_SQL = - "CREATE TABLE " + TABLE.NOTE + "(" + - NoteColumns.ID + " INTEGER PRIMARY KEY," + - NoteColumns.PARENT_ID + " INTEGER NOT NULL DEFAULT 0," + - NoteColumns.ALERTED_DATE + " INTEGER NOT NULL DEFAULT 0," + - NoteColumns.BG_COLOR_ID + " INTEGER NOT NULL DEFAULT 0," + - NoteColumns.CREATED_DATE + " INTEGER NOT NULL DEFAULT (strftime('%s','now') * 1000)," + - NoteColumns.HAS_ATTACHMENT + " INTEGER NOT NULL DEFAULT 0," + - NoteColumns.MODIFIED_DATE + " INTEGER NOT NULL DEFAULT (strftime('%s','now') * 1000)," + - NoteColumns.NOTES_COUNT + " INTEGER NOT NULL DEFAULT 0," + - NoteColumns.SNIPPET + " TEXT NOT NULL DEFAULT ''," + - NoteColumns.TYPE + " INTEGER NOT NULL DEFAULT 0," + - NoteColumns.WIDGET_ID + " INTEGER NOT NULL DEFAULT 0," + - NoteColumns.WIDGET_TYPE + " INTEGER NOT NULL DEFAULT -1," + - NoteColumns.SYNC_ID + " INTEGER NOT NULL DEFAULT 0," + - NoteColumns.LOCAL_MODIFIED + " INTEGER NOT NULL DEFAULT 0," + - NoteColumns.ORIGIN_PARENT_ID + " INTEGER NOT NULL DEFAULT 0," + - NoteColumns.GTASK_ID + " TEXT NOT NULL DEFAULT ''," + - NoteColumns.VERSION + " INTEGER NOT NULL DEFAULT 0" + - ")"; + "CREATE TABLE " + TABLE.NOTE + "(" + + NoteColumns.ID + " INTEGER PRIMARY KEY," + // 主键 + NoteColumns.PARENT_ID + " INTEGER NOT NULL DEFAULT 0," + // 父级 ID + NoteColumns.ALERTED_DATE + " INTEGER NOT NULL DEFAULT 0," + // 提醒日期 + NoteColumns.BG_COLOR_ID + " INTEGER NOT NULL DEFAULT 0," + // 背景颜色 ID + NoteColumns.CREATED_DATE + " INTEGER NOT NULL DEFAULT (strftime('%s','now') * 1000)," + // 创建日期,默认为当前时间 + NoteColumns.HAS_ATTACHMENT + " INTEGER NOT NULL DEFAULT 0," + // 是否有附件 + NoteColumns.MODIFIED_DATE + " INTEGER NOT NULL DEFAULT (strftime('%s','now') * 1000)," + // 修改日期,默认为当前时间 + NoteColumns.NOTES_COUNT + " INTEGER NOT NULL DEFAULT 0," + // 笔记数量 + NoteColumns.SNIPPET + " TEXT NOT NULL DEFAULT ''," + // 摘要 + NoteColumns.TYPE + " INTEGER NOT NULL DEFAULT 0," + // 类型 + NoteColumns.WIDGET_ID + " INTEGER NOT NULL DEFAULT 0," + // 小部件 ID + NoteColumns.WIDGET_TYPE + " INTEGER NOT NULL DEFAULT -1," + // 小部件类型 + NoteColumns.SYNC_ID + " INTEGER NOT NULL DEFAULT 0," + // 同步 ID + NoteColumns.LOCAL_MODIFIED + " INTEGER NOT NULL DEFAULT 0," + // 本地修改标识 + NoteColumns.ORIGIN_PARENT_ID + " INTEGER NOT NULL DEFAULT 0," + // 原始父级 ID + NoteColumns.GTASK_ID + " TEXT NOT NULL DEFAULT ''," + // Google 任务 ID + NoteColumns.VERSION + " INTEGER NOT NULL DEFAULT 0" + // 版本号 + ")"; + + // 创建 data 表的 SQL 语句 private static final String CREATE_DATA_TABLE_SQL = - "CREATE TABLE " + TABLE.DATA + "(" + - DataColumns.ID + " INTEGER PRIMARY KEY," + - DataColumns.MIME_TYPE + " TEXT NOT NULL," + - DataColumns.NOTE_ID + " INTEGER NOT NULL DEFAULT 0," + - NoteColumns.CREATED_DATE + " INTEGER NOT NULL DEFAULT (strftime('%s','now') * 1000)," + - NoteColumns.MODIFIED_DATE + " INTEGER NOT NULL DEFAULT (strftime('%s','now') * 1000)," + - DataColumns.CONTENT + " TEXT NOT NULL DEFAULT ''," + - DataColumns.DATA1 + " INTEGER," + - DataColumns.DATA2 + " INTEGER," + - DataColumns.DATA3 + " TEXT NOT NULL DEFAULT ''," + - DataColumns.DATA4 + " TEXT NOT NULL DEFAULT ''," + - DataColumns.DATA5 + " TEXT NOT NULL DEFAULT ''" + - ")"; + "CREATE TABLE " + TABLE.DATA + "(" + + DataColumns.ID + " INTEGER PRIMARY KEY," + // 主键 + DataColumns.MIME_TYPE + " TEXT NOT NULL," + // MIME 类型 + DataColumns.NOTE_ID + " INTEGER NOT NULL DEFAULT 0," + // 关联笔记 ID + NoteColumns.CREATED_DATE + " INTEGER NOT NULL DEFAULT (strftime('%s','now') * 1000)," + // 创建日期,默认为当前时间 + NoteColumns.MODIFIED_DATE + " INTEGER NOT NULL DEFAULT (strftime('%s','now') * 1000)," + // 修改日期,默认为当前时间 + DataColumns.CONTENT + " TEXT NOT NULL DEFAULT ''," + // 内容 + DataColumns.DATA1 + " INTEGER," + // 可选数据字段 + DataColumns.DATA2 + " INTEGER," + // 可选数据字段 + DataColumns.DATA3 + " TEXT NOT NULL DEFAULT ''," + // 可选数据字段 + DataColumns.DATA4 + " TEXT NOT NULL DEFAULT ''," + // 可选数据字段 + DataColumns.DATA5 + " TEXT NOT NULL DEFAULT ''" + // 可选数据字段 + ")"; + + // 创建数据表的索引,基于 note_id private static final String CREATE_DATA_NOTE_ID_INDEX_SQL = - "CREATE INDEX IF NOT EXISTS note_id_index ON " + - TABLE.DATA + "(" + DataColumns.NOTE_ID + ");"; - /** - * Increase folder's note count when move note to the folder - */ + "CREATE INDEX IF NOT EXISTS note_id_index ON " + + TABLE.DATA + "(" + DataColumns.NOTE_ID + ");"; + + // 以下是针对数据表进行的触发器操作 + + // 移动笔记到文件夹时增加文件夹内的笔记数量 private static final String NOTE_INCREASE_FOLDER_COUNT_ON_UPDATE_TRIGGER = - "CREATE TRIGGER increase_folder_count_on_update " + - " AFTER UPDATE OF " + NoteColumns.PARENT_ID + " ON " + TABLE.NOTE + - " BEGIN " + - " UPDATE " + TABLE.NOTE + - " SET " + NoteColumns.NOTES_COUNT + "=" + NoteColumns.NOTES_COUNT + " + 1" + - " WHERE " + NoteColumns.ID + "=new." + NoteColumns.PARENT_ID + ";" + - " END"; - /** - * Decrease folder's note count when move note from folder - */ + "CREATE TRIGGER increase_folder_count_on_update " + + " AFTER UPDATE OF " + NoteColumns.PARENT_ID + " ON " + TABLE.NOTE + + " BEGIN " + + " UPDATE " + TABLE.NOTE + + " SET " + NoteColumns.NOTES_COUNT + "=" + NoteColumns.NOTES_COUNT + " + 1" + + " WHERE " + NoteColumns.ID + "=new." + NoteColumns.PARENT_ID + ";" + + " END"; + + // 移动笔记出文件夹时减少文件夹内的笔记数量 private static final String NOTE_DECREASE_FOLDER_COUNT_ON_UPDATE_TRIGGER = - "CREATE TRIGGER decrease_folder_count_on_update " + - " AFTER UPDATE OF " + NoteColumns.PARENT_ID + " ON " + TABLE.NOTE + - " BEGIN " + - " UPDATE " + TABLE.NOTE + - " SET " + NoteColumns.NOTES_COUNT + "=" + NoteColumns.NOTES_COUNT + "-1" + - " WHERE " + NoteColumns.ID + "=old." + NoteColumns.PARENT_ID + - " AND " + NoteColumns.NOTES_COUNT + ">0" + ";" + - " END"; - /** - * Increase folder's note count when insert new note to the folder - */ + "CREATE TRIGGER decrease_folder_count_on_update " + + " AFTER UPDATE OF " + NoteColumns.PARENT_ID + " ON " + TABLE.NOTE + + " BEGIN " + + " UPDATE " + TABLE.NOTE + + " SET " + NoteColumns.NOTES_COUNT + "=" + NoteColumns.NOTES_COUNT + "-1" + + " WHERE " + NoteColumns.ID + "=old." + NoteColumns.PARENT_ID + + " AND " + NoteColumns.NOTES_COUNT + ">0" + ";" + + " END"; + + // 插入笔记到文件夹时增加文件夹内的笔记数量 private static final String NOTE_INCREASE_FOLDER_COUNT_ON_INSERT_TRIGGER = - "CREATE TRIGGER increase_folder_count_on_insert " + - " AFTER INSERT ON " + TABLE.NOTE + - " BEGIN " + - " UPDATE " + TABLE.NOTE + - " SET " + NoteColumns.NOTES_COUNT + "=" + NoteColumns.NOTES_COUNT + " + 1" + - " WHERE " + NoteColumns.ID + "=new." + NoteColumns.PARENT_ID + ";" + - " END"; - /** - * Decrease folder's note count when delete note from the folder - */ + "CREATE TRIGGER increase_folder_count_on_insert " + + " AFTER INSERT ON " + TABLE.NOTE + + " BEGIN " + + " UPDATE " + TABLE.NOTE + + " SET " + NoteColumns.NOTES_COUNT + "=" + NoteColumns.NOTES_COUNT + " + 1" + + " WHERE " + NoteColumns.ID + "=new." + NoteColumns.PARENT_ID + ";" + + " END"; + + // 删除笔记时减少文件夹内的笔记数量 private static final String NOTE_DECREASE_FOLDER_COUNT_ON_DELETE_TRIGGER = - "CREATE TRIGGER decrease_folder_count_on_delete " + - " AFTER DELETE ON " + TABLE.NOTE + - " BEGIN " + - " UPDATE " + TABLE.NOTE + - " SET " + NoteColumns.NOTES_COUNT + "=" + NoteColumns.NOTES_COUNT + "-1" + - " WHERE " + NoteColumns.ID + "=old." + NoteColumns.PARENT_ID + - " AND " + NoteColumns.NOTES_COUNT + ">0;" + - " END"; - /** - * Update note's content when insert data with type {@link DataConstants#NOTE} - */ - private static final String DATA_UPDATE_NOTE_CONTENT_ON_INSERT_TRIGGER = - "CREATE TRIGGER update_note_content_on_insert " + - " AFTER INSERT ON " + TABLE.DATA + - " WHEN new." + DataColumns.MIME_TYPE + "='" + DataConstants.NOTE + "'" + - " BEGIN" + - " UPDATE " + TABLE.NOTE + - " SET " + NoteColumns.SNIPPET + "=new." + DataColumns.CONTENT + - " WHERE " + NoteColumns.ID + "=new." + DataColumns.NOTE_ID + ";" + - " END"; - /** - * Update note's content when data with {@link DataConstants#NOTE} type has changed - */ - private static final String DATA_UPDATE_NOTE_CONTENT_ON_UPDATE_TRIGGER = - "CREATE TRIGGER update_note_content_on_update " + - " AFTER UPDATE ON " + TABLE.DATA + - " WHEN old." + DataColumns.MIME_TYPE + "='" + DataConstants.NOTE + "'" + - " BEGIN" + - " UPDATE " + TABLE.NOTE + - " SET " + NoteColumns.SNIPPET + "=new." + DataColumns.CONTENT + - " WHERE " + NoteColumns.ID + "=new." + DataColumns.NOTE_ID + ";" + - " END"; - /** - * Update note's content when data with {@link DataConstants#NOTE} type has deleted - */ - private static final String DATA_UPDATE_NOTE_CONTENT_ON_DELETE_TRIGGER = - "CREATE TRIGGER update_note_content_on_delete " + - " AFTER delete ON " + TABLE.DATA + - " WHEN old." + DataColumns.MIME_TYPE + "='" + DataConstants.NOTE + "'" + - " BEGIN" + - " UPDATE " + TABLE.NOTE + - " SET " + NoteColumns.SNIPPET + "=''" + - " WHERE " + NoteColumns.ID + "=old." + DataColumns.NOTE_ID + ";" + - " END"; - /** - * Delete datas belong to note which has been deleted - */ + "CREATE TRIGGER decrease_folder_count_on_delete " + + " AFTER DELETE ON " + TABLE.NOTE + + " BEGIN " + + " UPDATE " + TABLE.NOTE + + " SET " + NoteColumns.NOTES_COUNT + "=" + NoteColumns.NOTES_COUNT + "-1" + + " WHERE " + NoteColumns.ID + "=old." + NoteColumns.PARENT_ID + + " AND " + NoteColumns.NOTES_COUNT + ">0;" + + " END"; + + // 删除笔记时删除关联的数据 private static final String NOTE_DELETE_DATA_ON_DELETE_TRIGGER = - "CREATE TRIGGER delete_data_on_delete " + - " AFTER DELETE ON " + TABLE.NOTE + - " BEGIN" + - " DELETE FROM " + TABLE.DATA + - " WHERE " + DataColumns.NOTE_ID + "=old." + NoteColumns.ID + ";" + - " END"; - /** - * Delete notes belong to folder which has been deleted - */ + "CREATE TRIGGER delete_data_on_delete " + + " AFTER DELETE ON " + TABLE.NOTE + + " BEGIN" + + " DELETE FROM " + TABLE.DATA + + " WHERE " + DataColumns.NOTE_ID + "=old." + NoteColumns.ID + ";" + + " END"; + + // 删除文件夹时删除其中的所有笔记 private static final String FOLDER_DELETE_NOTES_ON_DELETE_TRIGGER = - "CREATE TRIGGER folder_delete_notes_on_delete " + - " AFTER DELETE ON " + TABLE.NOTE + - " BEGIN" + - " DELETE FROM " + TABLE.NOTE + - " WHERE " + NoteColumns.PARENT_ID + "=old." + NoteColumns.ID + ";" + - " END"; - /** - * Move notes belong to folder which has been moved to trash folder - */ + "CREATE TRIGGER folder_delete_notes_on_delete " + + " AFTER DELETE ON " + TABLE.NOTE + + " BEGIN" + + " DELETE FROM " + TABLE.NOTE + + " WHERE " + NoteColumns.PARENT_ID + "=old." + NoteColumns.ID + ";" + + " END"; + + // 当文件夹被移到垃圾桶时,文件夹内的笔记也移动到垃圾桶 private static final String FOLDER_MOVE_NOTES_ON_TRASH_TRIGGER = - "CREATE TRIGGER folder_move_notes_on_trash " + - " AFTER UPDATE ON " + TABLE.NOTE + - " WHEN new." + NoteColumns.PARENT_ID + "=" + Notes.ID_TRASH_FOLER + - " BEGIN" + - " UPDATE " + TABLE.NOTE + - " SET " + NoteColumns.PARENT_ID + "=" + Notes.ID_TRASH_FOLER + - " WHERE " + NoteColumns.PARENT_ID + "=old." + NoteColumns.ID + ";" + - " END"; + "CREATE TRIGGER folder_move_notes_on_trash " + + " AFTER UPDATE ON " + TABLE.NOTE + + " WHEN new." + NoteColumns.PARENT_ID + "=" + Notes.ID_TRASH_FOLER + + " BEGIN" + + " UPDATE " + TABLE.NOTE + + " SET " + NoteColumns.PARENT_ID + "=" + Notes.ID_TRASH_FOLER + + " WHERE " + NoteColumns.PARENT_ID + "=old." + NoteColumns.ID + ";" + + " END"; + + // 唯一实例 private static NotesDatabaseHelper mInstance; + // 构造方法 public NotesDatabaseHelper(Context context) { super(context, DB_NAME, null, DB_VERSION); } + // 获取唯一实例 static synchronized NotesDatabaseHelper getInstance(Context context) { if (mInstance == null) { mInstance = new NotesDatabaseHelper(context); @@ -196,6 +164,7 @@ public class NotesDatabaseHelper extends SQLiteOpenHelper { return mInstance; } + // 创建 note 表 public void createNoteTable(SQLiteDatabase db) { db.execSQL(CREATE_NOTE_TABLE_SQL); reCreateNoteTableTriggers(db); @@ -203,6 +172,7 @@ public class NotesDatabaseHelper extends SQLiteOpenHelper { Log.d(TAG, "note table has been created"); } + // 重新创建触发器 private void reCreateNoteTableTriggers(SQLiteDatabase db) { db.execSQL("DROP TRIGGER IF EXISTS increase_folder_count_on_update"); db.execSQL("DROP TRIGGER IF EXISTS decrease_folder_count_on_update"); @@ -221,41 +191,35 @@ public class NotesDatabaseHelper extends SQLiteOpenHelper { db.execSQL(FOLDER_MOVE_NOTES_ON_TRASH_TRIGGER); } + // 创建系统文件夹(如根文件夹、回收站等) private void createSystemFolder(SQLiteDatabase db) { ContentValues values = new ContentValues(); - /** - * call record foler for call notes - */ + // 插入“通话记录”文件夹 values.put(NoteColumns.ID, Notes.ID_CALL_RECORD_FOLDER); values.put(NoteColumns.TYPE, Notes.TYPE_SYSTEM); db.insert(TABLE.NOTE, null, values); - /** - * root folder which is default folder - */ + // 插入“根文件夹” values.clear(); values.put(NoteColumns.ID, Notes.ID_ROOT_FOLDER); values.put(NoteColumns.TYPE, Notes.TYPE_SYSTEM); db.insert(TABLE.NOTE, null, values); - /** - * temporary folder which is used for moving note - */ + // 插入“临时文件夹” values.clear(); values.put(NoteColumns.ID, Notes.ID_TEMPARAY_FOLDER); values.put(NoteColumns.TYPE, Notes.TYPE_SYSTEM); db.insert(TABLE.NOTE, null, values); - /** - * create trash folder - */ + // 插入“回收站”文件夹 values.clear(); values.put(NoteColumns.ID, Notes.ID_TRASH_FOLER); values.put(NoteColumns.TYPE, Notes.TYPE_SYSTEM); db.insert(TABLE.NOTE, null, values); } + // 创建 data 表 public void createDataTable(SQLiteDatabase db) { db.execSQL(CREATE_DATA_TABLE_SQL); reCreateDataTableTriggers(db); @@ -263,6 +227,7 @@ public class NotesDatabaseHelper extends SQLiteOpenHelper { Log.d(TAG, "data table has been created"); } + // 重新创建 data 表的触发器 private void reCreateDataTableTriggers(SQLiteDatabase db) { db.execSQL("DROP TRIGGER IF EXISTS update_note_content_on_insert"); db.execSQL("DROP TRIGGER IF EXISTS update_note_content_on_update"); @@ -273,12 +238,14 @@ public class NotesDatabaseHelper extends SQLiteOpenHelper { db.execSQL(DATA_UPDATE_NOTE_CONTENT_ON_DELETE_TRIGGER); } + // 创建数据库 @Override public void onCreate(SQLiteDatabase db) { createNoteTable(db); createDataTable(db); } + // 升级数据库 @Override public void onUpgrade(SQLiteDatabase db, int oldVersion, int newVersion) { boolean reCreateTriggers = false; @@ -308,10 +275,11 @@ public class NotesDatabaseHelper extends SQLiteOpenHelper { if (oldVersion != newVersion) { throw new IllegalStateException("Upgrade notes database to version " + newVersion - + "fails"); + + "fails"); } } + // 升级到版本 2 private void upgradeToV2(SQLiteDatabase db) { db.execSQL("DROP TABLE IF EXISTS " + TABLE.NOTE); db.execSQL("DROP TABLE IF EXISTS " + TABLE.DATA); @@ -319,29 +287,31 @@ public class NotesDatabaseHelper extends SQLiteOpenHelper { createDataTable(db); } + // 升级到版本 3 private void upgradeToV3(SQLiteDatabase db) { - // drop unused triggers + // 删除不再使用的触发器 db.execSQL("DROP TRIGGER IF EXISTS update_note_modified_date_on_insert"); db.execSQL("DROP TRIGGER IF EXISTS update_note_modified_date_on_delete"); db.execSQL("DROP TRIGGER IF EXISTS update_note_modified_date_on_update"); - // add a column for gtask id + // 为笔记表增加一个字段,用于存储 gtask ID db.execSQL("ALTER TABLE " + TABLE.NOTE + " ADD COLUMN " + NoteColumns.GTASK_ID - + " TEXT NOT NULL DEFAULT ''"); - // add a trash system folder + + " TEXT NOT NULL DEFAULT ''"); + // 创建回收站文件夹 ContentValues values = new ContentValues(); values.put(NoteColumns.ID, Notes.ID_TRASH_FOLER); values.put(NoteColumns.TYPE, Notes.TYPE_SYSTEM); db.insert(TABLE.NOTE, null, values); } + // 升级到版本 4 private void upgradeToV4(SQLiteDatabase db) { db.execSQL("ALTER TABLE " + TABLE.NOTE + " ADD COLUMN " + NoteColumns.VERSION - + " INTEGER NOT NULL DEFAULT 0"); + + " INTEGER NOT NULL DEFAULT 0"); } + // 定义表名称常量 public interface TABLE { public static final String NOTE = "note"; - public static final String DATA = "data"; } } diff --git a/src/app/src/main/java/net/micode/notes/data/NotesProvider.java b/src/app/src/main/java/net/micode/notes/data/NotesProvider.java index 92f00a3..8098e3f 100644 --- a/src/app/src/main/java/net/micode/notes/data/NotesProvider.java +++ b/src/app/src/main/java/net/micode/notes/data/NotesProvider.java @@ -16,7 +16,6 @@ package net.micode.notes.data; - import android.app.SearchManager; import android.content.ContentProvider; import android.content.ContentUris; @@ -34,123 +33,144 @@ import net.micode.notes.data.Notes.DataColumns; import net.micode.notes.data.Notes.NoteColumns; import net.micode.notes.data.NotesDatabaseHelper.TABLE; - public class NotesProvider extends ContentProvider { + // 定义 URI 匹配器,用来将传入的 URI 路径与预定义的常量匹配 private static final UriMatcher mMatcher; private static final String TAG = "NotesProvider"; - private static final int URI_NOTE = 1; - private static final int URI_NOTE_ITEM = 2; - private static final int URI_DATA = 3; - private static final int URI_DATA_ITEM = 4; - private static final int URI_SEARCH = 5; - private static final int URI_SEARCH_SUGGEST = 6; - /** - * x'0A' represents the '\n' character in sqlite. For title and content in the search result, - * we will trim '\n' and white space in order to show more information. - */ - private static final String NOTES_SEARCH_PROJECTION = NoteColumns.ID + "," - + NoteColumns.ID + " AS " + SearchManager.SUGGEST_COLUMN_INTENT_EXTRA_DATA + "," - + "TRIM(REPLACE(" + NoteColumns.SNIPPET + ", x'0A','')) AS " + SearchManager.SUGGEST_COLUMN_TEXT_1 + "," - + "TRIM(REPLACE(" + NoteColumns.SNIPPET + ", x'0A','')) AS " + SearchManager.SUGGEST_COLUMN_TEXT_2 + "," - + R.drawable.search_result + " AS " + SearchManager.SUGGEST_COLUMN_ICON_1 + "," - + "'" + Intent.ACTION_VIEW + "' AS " + SearchManager.SUGGEST_COLUMN_INTENT_ACTION + "," - + "'" + Notes.TextNote.CONTENT_TYPE + "' AS " + SearchManager.SUGGEST_COLUMN_INTENT_DATA; - private static String NOTES_SNIPPET_SEARCH_QUERY = "SELECT " + NOTES_SEARCH_PROJECTION - + " FROM " + TABLE.NOTE - + " WHERE " + NoteColumns.SNIPPET + " LIKE ?" - + " AND " + NoteColumns.PARENT_ID + "<>" + Notes.ID_TRASH_FOLER - + " AND " + NoteColumns.TYPE + "=" + Notes.TYPE_NOTE; + + // 定义常量,用于标识不同类型的 URI 请求 + private static final int URI_NOTE = 1; // 获取所有笔记 + private static final int URI_NOTE_ITEM = 2; // 获取单个笔记 + private static final int URI_DATA = 3; // 获取所有数据 + private static final int URI_DATA_ITEM = 4; // 获取单个数据 + private static final int URI_SEARCH = 5; // 搜索笔记 + private static final int URI_SEARCH_SUGGEST = 6; // 搜索建议 + + // 定义搜索时返回的列,主要包括笔记 ID、笔记内容片段等 + private static final String NOTES_SEARCH_PROJECTION = NoteColumns.ID + "," + + NoteColumns.ID + " AS " + SearchManager.SUGGEST_COLUMN_INTENT_EXTRA_DATA + "," + + "TRIM(REPLACE(" + NoteColumns.SNIPPET + ", x'0A','')) AS " + SearchManager.SUGGEST_COLUMN_TEXT_1 + "," + + "TRIM(REPLACE(" + NoteColumns.SNIPPET + ", x'0A','')) AS " + SearchManager.SUGGEST_COLUMN_TEXT_2 + "," + + R.drawable.search_result + " AS " + SearchManager.SUGGEST_COLUMN_ICON_1 + "," + + "'" + Intent.ACTION_VIEW + "' AS " + SearchManager.SUGGEST_COLUMN_INTENT_ACTION + "," + + "'" + Notes.TextNote.CONTENT_TYPE + "' AS " + SearchManager.SUGGEST_COLUMN_INTENT_DATA; + + // 定义搜索时的 SQL 查询语句,用于匹配包含特定关键词的笔记内容 + private static String NOTES_SNIPPET_SEARCH_QUERY = "SELECT " + NOTES_SEARCH_PROJECTION + + " FROM " + TABLE.NOTE + + " WHERE " + NoteColumns.SNIPPET + " LIKE ?" + + " AND " + NoteColumns.PARENT_ID + "<>" + Notes.ID_TRASH_FOLER + // 排除垃圾桶文件夹 + " AND " + NoteColumns.TYPE + "=" + Notes.TYPE_NOTE; // 只查询普通笔记类型 static { - mMatcher = new UriMatcher(UriMatcher.NO_MATCH); - mMatcher.addURI(Notes.AUTHORITY, "note", URI_NOTE); - mMatcher.addURI(Notes.AUTHORITY, "note/#", URI_NOTE_ITEM); - mMatcher.addURI(Notes.AUTHORITY, "data", URI_DATA); - mMatcher.addURI(Notes.AUTHORITY, "data/#", URI_DATA_ITEM); - mMatcher.addURI(Notes.AUTHORITY, "search", URI_SEARCH); - mMatcher.addURI(Notes.AUTHORITY, SearchManager.SUGGEST_URI_PATH_QUERY, URI_SEARCH_SUGGEST); - mMatcher.addURI(Notes.AUTHORITY, SearchManager.SUGGEST_URI_PATH_QUERY + "/*", URI_SEARCH_SUGGEST); + mMatcher = new UriMatcher(UriMatcher.NO_MATCH); // 初始化 URI 匹配器 + + // 为不同的 URI 路径添加匹配规则 + mMatcher.addURI(Notes.AUTHORITY, "note", URI_NOTE); // 查询所有笔记 + mMatcher.addURI(Notes.AUTHORITY, "note/#", URI_NOTE_ITEM); // 查询单个笔记 + mMatcher.addURI(Notes.AUTHORITY, "data", URI_DATA); // 查询所有数据 + mMatcher.addURI(Notes.AUTHORITY, "data/#", URI_DATA_ITEM); // 查询单个数据 + mMatcher.addURI(Notes.AUTHORITY, "search", URI_SEARCH); // 搜索笔记 + mMatcher.addURI(Notes.AUTHORITY, SearchManager.SUGGEST_URI_PATH_QUERY, URI_SEARCH_SUGGEST); // 搜索建议 + mMatcher.addURI(Notes.AUTHORITY, SearchManager.SUGGEST_URI_PATH_QUERY + "/*", URI_SEARCH_SUGGEST); // 搜索建议 } + // NotesDatabaseHelper 是数据库的辅助类,用来管理数据库的创建和版本更新 private NotesDatabaseHelper mHelper; + // onCreate 方法,在 ContentProvider 创建时调用,用来初始化数据库助手 @Override public boolean onCreate() { - mHelper = NotesDatabaseHelper.getInstance(getContext()); - return true; + mHelper = NotesDatabaseHelper.getInstance(getContext()); // 获取数据库助手实例 + return true; // 表示 ContentProvider 成功创建 } + // 查询操作,根据 URI 的不同,查询不同的数据 @Override public Cursor query(Uri uri, String[] projection, String selection, String[] selectionArgs, String sortOrder) { - Cursor c = null; - SQLiteDatabase db = mHelper.getReadableDatabase(); + Cursor c = null; // 定义一个 Cursor,用于返回查询结果 + SQLiteDatabase db = mHelper.getReadableDatabase(); // 获取可读的 SQLite 数据库 String id = null; + + // 根据匹配的 URI 执行不同的查询操作 switch (mMatcher.match(uri)) { case URI_NOTE: - c = db.query(TABLE.NOTE, projection, selection, selectionArgs, null, null, - sortOrder); + // 查询所有笔记 + c = db.query(TABLE.NOTE, projection, selection, selectionArgs, null, null, sortOrder); break; case URI_NOTE_ITEM: - id = uri.getPathSegments().get(1); + // 查询单个笔记 + id = uri.getPathSegments().get(1); // 获取 URI 中的笔记 ID c = db.query(TABLE.NOTE, projection, NoteColumns.ID + "=" + id - + parseSelection(selection), selectionArgs, null, null, sortOrder); + + parseSelection(selection), selectionArgs, null, null, sortOrder); break; case URI_DATA: - c = db.query(TABLE.DATA, projection, selection, selectionArgs, null, null, - sortOrder); + // 查询所有数据 + c = db.query(TABLE.DATA, projection, selection, selectionArgs, null, null, sortOrder); break; case URI_DATA_ITEM: - id = uri.getPathSegments().get(1); + // 查询单个数据 + id = uri.getPathSegments().get(1); // 获取 URI 中的数据 ID c = db.query(TABLE.DATA, projection, DataColumns.ID + "=" + id - + parseSelection(selection), selectionArgs, null, null, sortOrder); + + parseSelection(selection), selectionArgs, null, null, sortOrder); break; case URI_SEARCH: case URI_SEARCH_SUGGEST: + // 执行搜索操作,根据传入的关键词搜索笔记内容 if (sortOrder != null || projection != null) { throw new IllegalArgumentException( - "do not specify sortOrder, selection, selectionArgs, or projection" + "with this query"); + "do not specify sortOrder, selection, selectionArgs, or projection" + "with this query"); } - String searchString = null; + String searchString = null; // 搜索的关键词 if (mMatcher.match(uri) == URI_SEARCH_SUGGEST) { + // 如果是搜索建议,获取 URI 中的搜索词 if (uri.getPathSegments().size() > 1) { searchString = uri.getPathSegments().get(1); } } else { + // 如果是普通搜索,获取查询参数中的 "pattern" searchString = uri.getQueryParameter("pattern"); } + // 如果搜索字符串为空,返回 null if (TextUtils.isEmpty(searchString)) { return null; } try { - searchString = String.format("%%%s%%", searchString); - c = db.rawQuery(NOTES_SNIPPET_SEARCH_QUERY, - new String[]{searchString}); + searchString = String.format("%%%s%%", searchString); // 模糊匹配搜索词 + c = db.rawQuery(NOTES_SNIPPET_SEARCH_QUERY, new String[]{searchString}); } catch (IllegalStateException ex) { - Log.e(TAG, "got exception: " + ex.toString()); + Log.e(TAG, "got exception: " + ex.toString()); // 如果查询出错,打印错误日志 } break; default: - throw new IllegalArgumentException("Unknown URI " + uri); + throw new IllegalArgumentException("Unknown URI " + uri); // 如果 URI 无法匹配,抛出异常 } + + // 如果查询结果不为空,设置通知 URI,用于通知数据变化 if (c != null) { c.setNotificationUri(getContext().getContentResolver(), uri); } - return c; + return c; // 返回查询结果 } + // 插入数据操作,根据 URI 判断插入的表 @Override public Uri insert(Uri uri, ContentValues values) { - SQLiteDatabase db = mHelper.getWritableDatabase(); + SQLiteDatabase db = mHelper.getWritableDatabase(); // 获取可写的 SQLite 数据库 long dataId = 0, noteId = 0, insertedId = 0; + + // 根据 URI 匹配的结果执行不同的插入操作 switch (mMatcher.match(uri)) { case URI_NOTE: + // 插入一条笔记数据 insertedId = noteId = db.insert(TABLE.NOTE, null, values); break; case URI_DATA: + // 插入数据,首先确保 values 中包含 Note ID if (values.containsKey(DataColumns.NOTE_ID)) { noteId = values.getAsLong(DataColumns.NOTE_ID); } else { @@ -159,142 +179,157 @@ public class NotesProvider extends ContentProvider { insertedId = dataId = db.insert(TABLE.DATA, null, values); break; default: - throw new IllegalArgumentException("Unknown URI " + uri); + throw new IllegalArgumentException("Unknown URI " + uri); // 无效 URI,抛出异常 } - // Notify the note uri + + // 通知更新笔记数据 if (noteId > 0) { getContext().getContentResolver().notifyChange( - ContentUris.withAppendedId(Notes.CONTENT_NOTE_URI, noteId), null); + ContentUris.withAppendedId(Notes.CONTENT_NOTE_URI, noteId), null); } - // Notify the data uri + // 通知更新数据 if (dataId > 0) { getContext().getContentResolver().notifyChange( - ContentUris.withAppendedId(Notes.CONTENT_DATA_URI, dataId), null); + ContentUris.withAppendedId(Notes.CONTENT_DATA_URI, dataId), null); } - return ContentUris.withAppendedId(uri, insertedId); + return ContentUris.withAppendedId(uri, insertedId); // 返回插入数据的 URI } + // 删除数据操作,根据 URI 匹配执行不同的删除操作 @Override public int delete(Uri uri, String selection, String[] selectionArgs) { - int count = 0; + int count = 0; // 记录删除的行数 String id = null; - SQLiteDatabase db = mHelper.getWritableDatabase(); + SQLiteDatabase db = mHelper.getWritableDatabase(); // 获取可写的 SQLite 数据库 boolean deleteData = false; + + // 根据 URI 匹配结果执行不同的删除操作 switch (mMatcher.match(uri)) { case URI_NOTE: - selection = "(" + selection + ") AND " + NoteColumns.ID + ">0 "; - count = db.delete(TABLE.NOTE, selection, selectionArgs); + // 删除所有笔记 + selection = "(" + selection + ") AND " + NoteColumns.ID + ">0 "; // 添加 ID 限制条件 + count = db.delete(TABLE.NOTE, selection, selectionArgs); // 删除数据 break; case URI_NOTE_ITEM: - id = uri.getPathSegments().get(1); - /** - * ID that smaller than 0 is system folder which is not allowed to - * trash - */ + // 删除单个笔记 + id = uri.getPathSegments().get(1); // 获取笔记 ID long noteId = Long.valueOf(id); if (noteId <= 0) { break; } count = db.delete(TABLE.NOTE, - NoteColumns.ID + "=" + id + parseSelection(selection), selectionArgs); + NoteColumns.ID + "=" + id + parseSelection(selection), selectionArgs); // 删除指定笔记 break; case URI_DATA: - count = db.delete(TABLE.DATA, selection, selectionArgs); + // 删除所有数据 + count = db.delete(TABLE.DATA, selection, selectionArgs); // 删除数据 deleteData = true; break; case URI_DATA_ITEM: - id = uri.getPathSegments().get(1); + // 删除单个数据 + id = uri.getPathSegments().get(1); // 获取数据 ID count = db.delete(TABLE.DATA, - DataColumns.ID + "=" + id + parseSelection(selection), selectionArgs); + DataColumns.ID + "=" + id + parseSelection(selection), selectionArgs); // 删除指定数据 deleteData = true; break; default: - throw new IllegalArgumentException("Unknown URI " + uri); + throw new IllegalArgumentException("Unknown URI " + uri); // 无效 URI,抛出异常 } + + // 删除成功后,通知相应的数据变化 if (count > 0) { if (deleteData) { - getContext().getContentResolver().notifyChange(Notes.CONTENT_NOTE_URI, null); + getContext().getContentResolver().notifyChange(Notes.CONTENT_NOTE_URI, null); // 通知笔记数据变化 } - getContext().getContentResolver().notifyChange(uri, null); + getContext().getContentResolver().notifyChange(uri, null); // 通知数据变化 } - return count; + return count; // 返回删除的行数 } + // 更新数据操作,根据 URI 匹配执行不同的更新操作 @Override public int update(Uri uri, ContentValues values, String selection, String[] selectionArgs) { int count = 0; String id = null; - SQLiteDatabase db = mHelper.getWritableDatabase(); + SQLiteDatabase db = mHelper.getWritableDatabase(); // 获取可写的 SQLite 数据库 boolean updateData = false; + + // 根据 URI 匹配的结果执行不同的更新操作 switch (mMatcher.match(uri)) { case URI_NOTE: - increaseNoteVersion(-1, selection, selectionArgs); - count = db.update(TABLE.NOTE, values, selection, selectionArgs); + // 更新所有笔记 + increaseNoteVersion(-1, selection, selectionArgs); // 增加版本号 + count = db.update(TABLE.NOTE, values, selection, selectionArgs); // 更新笔记数据 break; case URI_NOTE_ITEM: - id = uri.getPathSegments().get(1); - increaseNoteVersion(Long.valueOf(id), selection, selectionArgs); + // 更新单个笔记 + id = uri.getPathSegments().get(1); // 获取笔记 ID + increaseNoteVersion(Long.valueOf(id), selection, selectionArgs); // 增加指定笔记的版本号 count = db.update(TABLE.NOTE, values, NoteColumns.ID + "=" + id - + parseSelection(selection), selectionArgs); + + parseSelection(selection), selectionArgs); // 更新指定笔记 break; case URI_DATA: - count = db.update(TABLE.DATA, values, selection, selectionArgs); + // 更新所有数据 + count = db.update(TABLE.DATA, values, selection, selectionArgs); // 更新数据 updateData = true; break; case URI_DATA_ITEM: - id = uri.getPathSegments().get(1); + // 更新单个数据 + id = uri.getPathSegments().get(1); // 获取数据 ID count = db.update(TABLE.DATA, values, DataColumns.ID + "=" + id - + parseSelection(selection), selectionArgs); + + parseSelection(selection), selectionArgs); // 更新指定数据 updateData = true; break; default: - throw new IllegalArgumentException("Unknown URI " + uri); + throw new IllegalArgumentException("Unknown URI " + uri); // 无效 URI,抛出异常 } + // 更新成功后,通知相应的数据变化 if (count > 0) { if (updateData) { - getContext().getContentResolver().notifyChange(Notes.CONTENT_NOTE_URI, null); + getContext().getContentResolver().notifyChange(Notes.CONTENT_NOTE_URI, null); // 通知笔记数据变化 } - getContext().getContentResolver().notifyChange(uri, null); + getContext().getContentResolver().notifyChange(uri, null); // 通知数据变化 } - return count; + return count; // 返回更新的行数 } + // 辅助方法:解析选择条件,生成 SQL 查询的 WHERE 子句 private String parseSelection(String selection) { return (!TextUtils.isEmpty(selection) ? " AND (" + selection + ')' : ""); } + // 辅助方法:增加笔记的版本号 private void increaseNoteVersion(long id, String selection, String[] selectionArgs) { - StringBuilder sql = new StringBuilder(120); + StringBuilder sql = new StringBuilder(120); // 使用 StringBuilder 构建 SQL 查询 sql.append("UPDATE "); - sql.append(TABLE.NOTE); + sql.append(TABLE.NOTE); // 更新笔记表 sql.append(" SET "); - sql.append(NoteColumns.VERSION); - sql.append("=" + NoteColumns.VERSION + "+1 "); + sql.append(NoteColumns.VERSION); // 更新版本号字段 + sql.append("=" + NoteColumns.VERSION + "+1 "); // 将版本号加1 if (id > 0 || !TextUtils.isEmpty(selection)) { - sql.append(" WHERE "); + sql.append(" WHERE "); // 如果指定了 ID 或者其他选择条件,添加 WHERE 子句 } if (id > 0) { - sql.append(NoteColumns.ID + "=" + String.valueOf(id)); + sql.append(NoteColumns.ID + "=" + String.valueOf(id)); // 如果指定了笔记 ID,更新对应笔记 } if (!TextUtils.isEmpty(selection)) { String selectString = id > 0 ? parseSelection(selection) : selection; for (String args : selectionArgs) { - selectString = selectString.replaceFirst("\\?", args); + selectString = selectString.replaceFirst("\\?", args); // 替换选择条件中的占位符 } - sql.append(selectString); + sql.append(selectString); // 添加选择条件 } - mHelper.getWritableDatabase().execSQL(sql.toString()); + mHelper.getWritableDatabase().execSQL(sql.toString()); // 执行 SQL 更新语句 } + // 获取 MIME 类型,当前未实现,直接返回 null @Override public String getType(Uri uri) { - // TODO Auto-generated method stub return null; } - }