- * Type: INTEGER (long) - *
+ *Type: INTEGER (long)
*/ public static final String ID = "_id"; /** * The parent's id for note or folder - *- * Type: INTEGER (long) - *
+ *Type: INTEGER (long)
*/ public static final String PARENT_ID = "parent_id"; /** * Created data for note or folder - *- * Type: INTEGER (long) - *
+ *Type: INTEGER (long)
*/ public static final String CREATED_DATE = "created_date"; /** * Latest modified date - *- * Type: INTEGER (long) - *
+ *Type: INTEGER (long)
*/ public static final String MODIFIED_DATE = "modified_date"; + /** * Alert date - *- * Type: INTEGER (long) - *
+ *Type: INTEGER (long)
*/ public static final String ALERTED_DATE = "alert_date"; /** * Folder's name or text content of note - *- * Type: TEXT - *
+ *Type: TEXT
*/ public static final String SNIPPET = "snippet"; /** * Note's widget id - *- * Type: INTEGER (long) - *
+ *Type: INTEGER (long)
*/ public static final String WIDGET_ID = "widget_id"; /** * Note's widget type - *- * Type: INTEGER (long) - *
+ *Type: INTEGER (long)
*/ public static final String WIDGET_TYPE = "widget_type"; /** * Note's background color's id - *- * Type: INTEGER (long) - *
+ *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 - *
+ *Type: INTEGER
*/ public static final String HAS_ATTACHMENT = "has_attachment"; /** * Folder's count of notes - *- * Type: INTEGER (long) - *
+ *Type: INTEGER (long)
*/ public static final String NOTES_COUNT = "notes_count"; /** * The file type: folder or note - *- * Type: INTEGER - *
+ *Type: INTEGER
*/ public static final String TYPE = "type"; /** * The last sync id - *- * Type: INTEGER (long) - *
+ *Type: INTEGER (long)
*/ public static final String SYNC_ID = "sync_id"; /** * Sign to indicate local modified or not - *- * Type: INTEGER - *
+ *Type: INTEGER
*/ public static final String LOCAL_MODIFIED = "local_modified"; /** * Original parent id before moving into temporary folder - *- * Type : INTEGER - *
+ *Type : INTEGER
*/ public static final String ORIGIN_PARENT_ID = "origin_parent_id"; /** * The gtask id - *- * Type : TEXT - *
+ *Type : TEXT
*/ public static final String GTASK_ID = "gtask_id"; /** * The version code - *- * Type : INTEGER (long) - *
+ *Type : INTEGER (long)
*/ public static final String VERSION = "version"; } + //接口:DataColumns(数据表字段定义) public interface DataColumns { /** * The unique ID for a row - *- * Type: INTEGER (long) - *
+ *Type: INTEGER (long)
*/ public static final String ID = "_id"; /** * The MIME type of the item represented by this row. - *- * Type: Text - *
+ *Type: Text
*/ public static final String MIME_TYPE = "mime_type"; /** * The reference id to note that this data belongs to - *- * Type: INTEGER (long) - *
+ *Type: INTEGER (long)
*/ public static final String NOTE_ID = "note_id"; /** * Created data for note or folder - *- * Type: INTEGER (long) - *
+ *Type: INTEGER (long)
*/ public static final String CREATED_DATE = "created_date"; /** * Latest modified date - *- * Type: INTEGER (long) - *
+ *Type: INTEGER (long)
*/ public static final String MODIFIED_DATE = "modified_date"; /** * Data's content - *- * Type: TEXT - *
+ *Type: TEXT
*/ public static final String CONTENT = "content"; + /** * Generic data column, the meaning is {@link #MIMETYPE} specific, used for * integer data type - *- * Type: INTEGER - *
+ *Type: INTEGER
*/ public static final String DATA1 = "data1"; /** * Generic data column, the meaning is {@link #MIMETYPE} specific, used for * integer data type - *- * Type: INTEGER - *
+ *Type: INTEGER
*/ public static final String DATA2 = "data2"; /** * Generic data column, the meaning is {@link #MIMETYPE} specific, used for * TEXT data type - *- * Type: TEXT - *
+ *Type: TEXT
*/ public static final String DATA3 = "data3"; /** * Generic data column, the meaning is {@link #MIMETYPE} specific, used for * TEXT data type - *- * Type: TEXT - *
+ *Type: TEXT
*/ public static final String DATA4 = "data4"; /** * Generic data column, the meaning is {@link #MIMETYPE} specific, used for * TEXT data type - *- * Type: TEXT - *
+ *Type: TEXT
*/ public static final String DATA5 = "data5"; } + //静态内部类: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 - *
+ *Type: Integer 1:check list mode 0: normal mode
*/ public static final String MODE = DATA1; @@ -325,20 +266,17 @@ public class Notes { 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) - *
+ *Type: INTEGER (long)
*/ public static final String CALL_DATE = DATA1; /** * Phone number for this record - *- * Type: TEXT - *
+ *Type: TEXT
*/ public static final String PHONE_NUMBER = DATA3; diff --git a/Notes-master/src/net/micode/notes/data/NotesDatabaseHelper.java b/Notes-master/src/net/micode/notes/data/NotesDatabaseHelper.java index 666a97f..12c96b6 100644 --- a/Notes-master/src/net/micode/notes/data/NotesDatabaseHelper.java +++ b/Notes-master/src/net/micode/notes/data/NotesDatabaseHelper.java @@ -26,23 +26,29 @@ import net.micode.notes.data.Notes.DataColumns; import net.micode.notes.data.Notes.DataConstants; import net.micode.notes.data.Notes.NoteColumns; -//管理 SQLite 数据库的创建、升级和访问 +//类定义:NotesDatabaseHelper public class NotesDatabaseHelper extends SQLiteOpenHelper { + //数据库名称和版本 private static final String DB_NAME = "note.db"; private static final int DB_VERSION = 4; + //表名接口 public interface TABLE { public static final String NOTE = "note"; public static final String DATA = "data"; } + //日志标签 private static final String TAG = "NotesDatabaseHelper"; + //单例实例 private static NotesDatabaseHelper mInstance; - private static final String CREATE_NOTE_TABLE_SQL = "CREATE TABLE " + TABLE.NOTE + "(" + + //创建 note 表 + 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," + @@ -60,9 +66,11 @@ public class NotesDatabaseHelper extends SQLiteOpenHelper { NoteColumns.ORIGIN_PARENT_ID + " INTEGER NOT NULL DEFAULT 0," + NoteColumns.GTASK_ID + " TEXT NOT NULL DEFAULT ''," + NoteColumns.VERSION + " INTEGER NOT NULL DEFAULT 0" + - ")"; + ")"; - private static final String CREATE_DATA_TABLE_SQL = "CREATE TABLE " + TABLE.DATA + "(" + + //创建 data 表 + 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," + @@ -74,132 +82,147 @@ public class NotesDatabaseHelper extends SQLiteOpenHelper { DataColumns.DATA3 + " TEXT NOT NULL DEFAULT ''," + DataColumns.DATA4 + " TEXT NOT NULL DEFAULT ''," + DataColumns.DATA5 + " TEXT NOT NULL DEFAULT ''" + - ")"; + ")"; - private static final String CREATE_DATA_NOTE_ID_INDEX_SQL = "CREATE INDEX IF NOT EXISTS note_id_index ON " + - TABLE.DATA + "(" + DataColumns.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 + ");"; + //触发器(Triggers)笔记移动/插入时更新文件夹计数 /** * Increase folder's note count when move note to the folder */ - 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"; - - 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"; + 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 + */ + 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 */ - 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"; + 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 */ - 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"; - + 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"; + 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 + * 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"; + 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 + * 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 + */ + 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 */ - 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"; - - // 数据库触发,当删除一条笔记时,自动删除与该笔记相关的所有数据(DATA 表中的记录) - 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"; - - // 当删除一个文件夹时,自动删除该文件夹下的所有笔记 - 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"; - - // 当一个文件夹被移动到回收站时,自动将其子笔记也移动到回收站 - 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"; + 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 + */ + 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"; + + //构造函数 public NotesDatabaseHelper(Context context) { super(context, DB_NAME, null, DB_VERSION); } - // 创建笔记表(NOTE 表),并初始化相关触发器。 - // 创建系统文件夹(如回收站、临时文件夹等)。 + //数据库创建方法 public void createNoteTable(SQLiteDatabase db) { db.execSQL(CREATE_NOTE_TABLE_SQL); reCreateNoteTableTriggers(db); @@ -207,6 +230,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"); @@ -225,6 +249,7 @@ public class NotesDatabaseHelper extends SQLiteOpenHelper { db.execSQL(FOLDER_MOVE_NOTES_ON_TRASH_TRIGGER); } + //创建系统文件夹 private void createSystemFolder(SQLiteDatabase db) { ContentValues values = new ContentValues(); @@ -277,7 +302,6 @@ public class NotesDatabaseHelper extends SQLiteOpenHelper { db.execSQL(DATA_UPDATE_NOTE_CONTENT_ON_DELETE_TRIGGER); } - // 数据库实例管理 static synchronized NotesDatabaseHelper getInstance(Context context) { if (mInstance == null) { mInstance = new NotesDatabaseHelper(context); @@ -285,16 +309,13 @@ public class NotesDatabaseHelper extends SQLiteOpenHelper { return mInstance; } - // 数据库创建与升级,在数据库首次创建时,调用 createNoteTable 和 createDataTable 方法,创建笔记表和数据表。 + //生命周期方法 @Override public void onCreate(SQLiteDatabase db) { createNoteTable(db); createDataTable(db); } - // 处理数据库版本升级。 - // 根据旧版本和新版本之间的差异,依次调用 upgradeToV2、upgradeToV3 和 upgradeToV4 方法。 - // 如果需要重新创建触发器,则调用 reCreateNoteTableTriggers 和 reCreateDataTableTriggers。 @Override public void onUpgrade(SQLiteDatabase db, int oldVersion, int newVersion) { boolean reCreateTriggers = false; @@ -328,33 +349,31 @@ public class NotesDatabaseHelper extends SQLiteOpenHelper { } } - // 在升级到版本 2 时,删除旧表并重新创建笔记表和数据表 private void upgradeToV2(SQLiteDatabase db) { + // 如果存在旧的 note 表和 data 表,则先删除(用于重新创建) db.execSQL("DROP TABLE IF EXISTS " + TABLE.NOTE); db.execSQL("DROP TABLE IF EXISTS " + TABLE.DATA); + // 重新创建 note 表(包含新字段和约束) createNoteTable(db); + // 创建 data 表(存储笔记内容等详细信息 createDataTable(db); } - // 删除无用的触发器。 - // 在笔记表中新增 GTASK_ID 列,用于支持 Google - // Tasks 功能。添加回收站文件夹。 private void upgradeToV3(SQLiteDatabase db) { - // drop unused triggers + // 删除不再使用的触发器(v2 中可能存在的旧触发器) 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 + // 添加一个用于同步 Google Tasks 的字段 gtask_id db.execSQL("ALTER TABLE " + TABLE.NOTE + " ADD COLUMN " + NoteColumns.GTASK_ID + " TEXT NOT NULL DEFAULT ''"); - // add a trash system folder + // 插入一个新的系统文件夹:回收站(Trash Folder) 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); } - // 在笔记表中新增 VERSION 列,用于版本控制。 private void upgradeToV4(SQLiteDatabase db) { db.execSQL("ALTER TABLE " + TABLE.NOTE + " ADD COLUMN " + NoteColumns.VERSION + " INTEGER NOT NULL DEFAULT 0"); diff --git a/Notes-master/src/net/micode/notes/data/NotesProvider.java b/Notes-master/src/net/micode/notes/data/NotesProvider.java index 1e28f0d..06586c8 100644 --- a/Notes-master/src/net/micode/notes/data/NotesProvider.java +++ b/Notes-master/src/net/micode/notes/data/NotesProvider.java @@ -16,6 +16,7 @@ package net.micode.notes.data; + import android.app.SearchManager; import android.content.ContentProvider; import android.content.ContentUris; @@ -33,25 +34,21 @@ import net.micode.notes.data.Notes.DataColumns; import net.micode.notes.data.Notes.NoteColumns; import net.micode.notes.data.NotesDatabaseHelper.TABLE; -//使用 UriMatcher 定义了多个 URI 的匹配规则。 -//每种 URI 对应一种特定的操作(如查询笔记、查询数据、搜索等) -//在 Android 应用中提供对笔记数据的统一访问接口 + public class NotesProvider extends ContentProvider { private static final UriMatcher mMatcher; - // mHelper:负责管理数据库的创建和升级。 - // TAG:日志标签,方便在调试时定位日志信息。 private NotesDatabaseHelper mHelper; 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_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; + private static final int URI_SEARCH = 5; + private static final int URI_SEARCH_SUGGEST = 6; static { mMatcher = new UriMatcher(UriMatcher.NO_MATCH); @@ -65,30 +62,31 @@ public class NotesProvider extends ContentProvider { } /** - * x'0A' represents the '\n' character in sqlite. For title and content in the - * search result, + * 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; + + 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; + + " FROM " + TABLE.NOTE + + " WHERE " + NoteColumns.SNIPPET + " LIKE ?" + + " AND " + NoteColumns.PARENT_ID + "<>" + Notes.ID_TRASH_FOLER + + " AND " + NoteColumns.TYPE + "=" + Notes.TYPE_NOTE; + //ContentProvider 的初始化方法,在 ContentProvider 被创建时调用。 @Override public boolean onCreate() { mHelper = NotesDatabaseHelper.getInstance(getContext()); return true; } + //实现 ContentProvider 的查询接口,用于从数据库中读取数据 @Override public Cursor query(Uri uri, String[] projection, String selection, String[] selectionArgs, String sortOrder) { @@ -96,26 +94,26 @@ public class NotesProvider extends ContentProvider { SQLiteDatabase db = mHelper.getReadableDatabase(); String id = null; switch (mMatcher.match(uri)) { - case URI_NOTE: + case URI_NOTE://URI_NOTE(查询 note 表) c = db.query(TABLE.NOTE, projection, selection, selectionArgs, null, null, sortOrder); break; - case URI_NOTE_ITEM: + case URI_NOTE_ITEM://URI_NOTE_ITEM(查询单个笔记) id = uri.getPathSegments().get(1); c = db.query(TABLE.NOTE, projection, NoteColumns.ID + "=" + id + parseSelection(selection), selectionArgs, null, null, sortOrder); break; - case URI_DATA: + case URI_DATA://URI_DATA(查询 data 表) c = db.query(TABLE.DATA, projection, selection, selectionArgs, null, null, sortOrder); break; - case URI_DATA_ITEM: + case URI_DATA_ITEM://URI_DATA_ITEM(查询单个数据项) id = uri.getPathSegments().get(1); c = db.query(TABLE.DATA, projection, DataColumns.ID + "=" + id + parseSelection(selection), selectionArgs, null, null, sortOrder); break; case URI_SEARCH: - case URI_SEARCH_SUGGEST: + case URI_SEARCH_SUGGEST://URI_SEARCH / URI_SEARCH_SUGGEST(搜索相关) if (sortOrder != null || projection != null) { throw new IllegalArgumentException( "do not specify sortOrder, selection, selectionArgs, or projection" + "with this query"); @@ -156,9 +154,10 @@ public class NotesProvider extends ContentProvider { SQLiteDatabase db = mHelper.getWritableDatabase(); long dataId = 0, noteId = 0, insertedId = 0; switch (mMatcher.match(uri)) { - case URI_NOTE: + case URI_NOTE://URI_NOTE(插入笔记) insertedId = noteId = db.insert(TABLE.NOTE, null, values); break; + //URI_DATA(插入数据项) case URI_DATA: if (values.containsKey(DataColumns.NOTE_ID)) { noteId = values.getAsLong(DataColumns.NOTE_ID); @@ -185,6 +184,7 @@ public class NotesProvider extends ContentProvider { return ContentUris.withAppendedId(uri, insertedId); } + //方法作用:实现 ContentProvider 的 delete() 接口,用于删除数据库中的数据(对应 SQLite 的 DELETE 语句) @Override public int delete(Uri uri, String selection, String[] selectionArgs) { int count = 0; @@ -233,70 +233,91 @@ public class NotesProvider extends ContentProvider { @Override public int update(Uri uri, ContentValues values, String selection, String[] selectionArgs) { - int count = 0; - String id = null; - SQLiteDatabase db = mHelper.getWritableDatabase(); - boolean updateData = false; - switch (mMatcher.match(uri)) { + int count = 0; // 记录受影响的行数 + String id = null; // 存储从URI路径中获取的ID + SQLiteDatabase db = mHelper.getWritableDatabase(); // 获取可写数据库实例 + boolean updateData = false; // 标记是否更新了data表 + switch (mMatcher.match(uri)) { // 根据不同的URI匹配模式进行分支 case URI_NOTE: + // 在更新前增加笔记版本号(假设increaseNoteVersion方法实现递增逻辑) increaseNoteVersion(-1, selection, selectionArgs); + // 更新note表中的记录 count = db.update(TABLE.NOTE, values, selection, selectionArgs); break; + case URI_NOTE_ITEM: + // 从URI中获取特定笔记的ID id = uri.getPathSegments().get(1); - increaseNoteVersion(Long.valueOf(id), selection, selectionArgs); - count = db.update(TABLE.NOTE, values, NoteColumns.ID + "=" + id - + parseSelection(selection), selectionArgs); + // 更新特定ID的笔记记录,并添加额外的选择条件(如果有) + count = db.update(TABLE.NOTE, values, NoteColumns.ID + "=" + id + parseSelection(selection), selectionArgs); + + // 下面这部分代码可能是缺失的部分,基于上下文推断应该在这里添加一些逻辑。 + // 比如可以添加日志输出或者检查更新是否成功等。 + break; + case URI_DATA: + // 更新data表中的记录 count = db.update(TABLE.DATA, values, selection, selectionArgs); - updateData = true; + updateData = true; // 设置标志位表示更新了data表 break; + case URI_DATA_ITEM: + // 从URI中获取特定数据项的ID id = uri.getPathSegments().get(1); - count = db.update(TABLE.DATA, values, DataColumns.ID + "=" + id - + parseSelection(selection), selectionArgs); - updateData = true; + // 更新特定ID的数据项,并添加额外的选择条件(如果有) + count = db.update(TABLE.DATA, values, DataColumns.ID + "=" + id + parseSelection(selection), selectionArgs); + updateData = true; // 设置标志位表示更新了data表 break; + default: - throw new IllegalArgumentException("Unknown URI " + uri); + throw new IllegalArgumentException("Unknown URI " + uri); // 如果没有匹配到任何已知的URI模式,则抛出异常 } - if (count > 0) { + if (count > 0) { // 如果有记录被更新 if (updateData) { + // 如果更新的是data表,通知监听CONTENT_NOTE_URI的所有观察者 getContext().getContentResolver().notifyChange(Notes.CONTENT_NOTE_URI, null); } + // 无论更新的是哪个表,都需要通知监听当前uri的所有观察者 getContext().getContentResolver().notifyChange(uri, null); } - return count; + return count; // 返回受影响的行数 } private String parseSelection(String selection) { + // 如果selection不为空,则在前面加上 " AND (" 并在末尾加上 ")",否则返回空字符串 return (!TextUtils.isEmpty(selection) ? " AND (" + selection + ')' : ""); } private void increaseNoteVersion(long id, String selection, String[] selectionArgs) { - StringBuilder sql = new StringBuilder(120); - sql.append("UPDATE "); - sql.append(TABLE.NOTE); - sql.append(" SET "); - sql.append(NoteColumns.VERSION); - sql.append("=" + NoteColumns.VERSION + "+1 "); + StringBuilder sql = new StringBuilder(120); // 创建StringBuilder对象,初始容量为120字符 + sql.append("UPDATE "); // 追加SQL语句的UPDATE部分 + sql.append(TABLE.NOTE); // 追加表名 + sql.append(" SET "); // 追加SET关键字 + sql.append(NoteColumns.VERSION); // 追加字段名VERSION + sql.append("=" + NoteColumns.VERSION + "+1 "); // 设置版本号自增 + // 判断是否需要追加WHERE子句 if (id > 0 || !TextUtils.isEmpty(selection)) { sql.append(" WHERE "); } + // 如果有指定ID,则直接根据ID过滤 if (id > 0) { sql.append(NoteColumns.ID + "=" + String.valueOf(id)); } + // 如果有其他筛选条件,则根据情况拼接 if (!TextUtils.isEmpty(selection)) { + // 使用parseSelection方法处理selection,确保格式正确 String selectString = id > 0 ? parseSelection(selection) : selection; + // 替换selection中的占位符"?"为实际参数值 for (String args : selectionArgs) { selectString = selectString.replaceFirst("\\?", args); } sql.append(selectString); } + // 执行生成的SQL语句 mHelper.getWritableDatabase().execSQL(sql.toString()); } diff --git a/Notes-master/src/net/micode/notes/gtask/data/MetaData.java b/Notes-master/src/net/micode/notes/gtask/data/MetaData.java index 361b6ff..68a3fc4 100644 --- a/Notes-master/src/net/micode/notes/gtask/data/MetaData.java +++ b/Notes-master/src/net/micode/notes/gtask/data/MetaData.java @@ -24,11 +24,13 @@ import net.micode.notes.tool.GTaskStringUtils; import org.json.JSONException; import org.json.JSONObject; + public class MetaData extends Task { private final static String TAG = MetaData.class.getSimpleName(); private String mRelatedGid = null; + //作用:设置与 Google Task 相关的元数据信息 public void setMeta(String gid, JSONObject metaInfo) { try { metaInfo.put(GTaskStringUtils.META_HEAD_GTASK_ID, gid); @@ -43,11 +45,14 @@ public class MetaData extends Task { return mRelatedGid; } + //获取当前元数据所关联的 Google Task 的 GID + //判断当前元数据是否值得保存 @Override public boolean isWorthSaving() { return getNotes() != null; } + //从远程 JSON 数据中恢复内容 @Override public void setContentByRemoteJSON(JSONObject js) { super.setContentByRemoteJSON(js); @@ -62,17 +67,20 @@ public class MetaData extends Task { } } + //禁止从本地 JSON 设置内容 @Override public void setContentByLocalJSON(JSONObject js) { // this function should not be called throw new IllegalAccessError("MetaData:setContentByLocalJSON should not be called"); } + //禁止导出本地 JSON 内容 @Override public JSONObject getLocalJSONFromContent() { throw new IllegalAccessError("MetaData:getLocalJSONFromContent should not be called"); } + //禁止获取同步动作 @Override public int getSyncAction(Cursor c) { throw new IllegalAccessError("MetaData:getSyncAction should not be called"); diff --git a/Notes-master/src/net/micode/notes/gtask/data/Node.java b/Notes-master/src/net/micode/notes/gtask/data/Node.java index 63950e0..024fa60 100644 --- a/Notes-master/src/net/micode/notes/gtask/data/Node.java +++ b/Notes-master/src/net/micode/notes/gtask/data/Node.java @@ -14,6 +14,7 @@ * limitations under the License. */ +//与 Google Tasks 数据同步相关的类 package net.micode.notes.gtask.data; import android.database.Cursor; @@ -21,6 +22,7 @@ import android.database.Cursor; import org.json.JSONObject; public abstract class Node { + //定义同步过程中可能发生的操作类型 public static final int SYNC_ACTION_NONE = 0; public static final int SYNC_ACTION_ADD_REMOTE = 1; @@ -47,6 +49,8 @@ public abstract class Node { private boolean mDeleted; + //提供对私有成员变量的标准访问接口 + //用于设置或获取节点的基本属性 public Node() { mGid = null; mName = ""; diff --git a/Notes-master/src/net/micode/notes/gtask/data/SqlData.java b/Notes-master/src/net/micode/notes/gtask/data/SqlData.java index d3ec3be..04dfbd7 100644 --- a/Notes-master/src/net/micode/notes/gtask/data/SqlData.java +++ b/Notes-master/src/net/micode/notes/gtask/data/SqlData.java @@ -34,7 +34,7 @@ import net.micode.notes.gtask.exception.ActionFailureException; import org.json.JSONException; import org.json.JSONObject; - +//封装对数据库中 Data 表的操作,用于读写笔记内容相关的数据 public class SqlData { private static final String TAG = SqlData.class.getSimpleName(); @@ -45,6 +45,8 @@ public class SqlData { DataColumns.DATA3 }; + //PROJECTION_DATA:指定从数据库查询时要获取的列; + //索引常量:用于快速访问 Cursor 中的列位置。 public static final int DATA_ID_COLUMN = 0; public static final int DATA_MIME_TYPE_COLUMN = 1; @@ -55,6 +57,7 @@ public class SqlData { public static final int DATA_CONTENT_DATA_3_COLUMN = 4; + //用于保存当前数据项的状态,并在提交时更新数据库 private ContentResolver mContentResolver; private boolean mIsCreate; @@ -71,6 +74,8 @@ public class SqlData { private ContentValues mDiffDataValues; + //初始化一个新的数据对象,默认为“未创建”状态; + //设置默认值,准备插入新数据。 public SqlData(Context context) { mContentResolver = context.getContentResolver(); mIsCreate = true; @@ -82,6 +87,8 @@ public class SqlData { mDiffDataValues = new ContentValues(); } + //通过传入的 Cursor 对象初始化一个已存在的数据项; + //调用 loadFromCursor() 方法从游标中读取数据。 public SqlData(Context context, Cursor c) { mContentResolver = context.getContentResolver(); mIsCreate = false; @@ -89,6 +96,8 @@ public class SqlData { mDiffDataValues = new ContentValues(); } + //从 Cursor 中读取并赋值到成员变量; + //用于构建已有数据对象。 private void loadFromCursor(Cursor c) { mDataId = c.getLong(DATA_ID_COLUMN); mDataMimeType = c.getString(DATA_MIME_TYPE_COLUMN); @@ -97,6 +106,9 @@ public class SqlData { mDataContentData3 = c.getString(DATA_CONTENT_DATA_3_COLUMN); } + //从远程 JSON 数据中设置本地数据字段; + //只有当值发生变化时才记录进 mDiffDataValues; + //支持字段包括:ID、MIME类型、内容、DATA1、DATA3。 public void setContent(JSONObject js) throws JSONException { long dataId = js.has(DataColumns.ID) ? js.getLong(DataColumns.ID) : INVALID_ID; if (mIsCreate || mDataId != dataId) { @@ -130,6 +142,8 @@ public class SqlData { mDataContentData3 = dataContentData3; } + //将当前数据项导出为 JSON 对象; + //如果是新建对象,则返回 null 并输出警告。 public JSONObject getContent() throws JSONException { if (mIsCreate) { Log.e(TAG, "it seems that we haven't created this in database yet"); @@ -183,6 +197,7 @@ public class SqlData { mIsCreate = false; } + //获取当前数据项的唯一标识 ID。 public long getId() { return mDataId; } diff --git a/Notes-master/src/net/micode/notes/gtask/data/SqlNote.java b/Notes-master/src/net/micode/notes/gtask/data/SqlNote.java index 79a4095..e85e4e8 100644 --- a/Notes-master/src/net/micode/notes/gtask/data/SqlNote.java +++ b/Notes-master/src/net/micode/notes/gtask/data/SqlNote.java @@ -37,7 +37,7 @@ import org.json.JSONObject; import java.util.ArrayList; - +//封装对数据库中笔记 (Note) 表的操作,用于读写笔记元信息; public class SqlNote { private static final String TAG = SqlNote.class.getSimpleName(); @@ -52,6 +52,8 @@ public class SqlNote { NoteColumns.VERSION }; + //PROJECTION_NOTE:指定从数据库查询时要获取的列; + //索引常量:用于快速访问 Cursor 中的列位置 public static final int ID_COLUMN = 0; public static final int ALERTED_DATE_COLUMN = 1; @@ -122,6 +124,8 @@ public class SqlNote { private ArrayList