From 9ad9a98b7dafed09cde104ca1e3aa599208dfa81 Mon Sep 17 00:00:00 2001 From: lwl <1793850169@qq.com> Date: Sun, 8 Jun 2025 00:10:39 +0800 Subject: [PATCH] =?UTF-8?q?=E4=BF=AE=E6=94=B9?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- Notes-master/.idea/.gitignore | 3 + Notes-master/.idea/misc.xml | 6 + Notes-master/.idea/modules.xml | 8 + Notes-master/.idea/vcs.xml | 7 + Notes-master/Notes-master.iml | 11 + .../src/net/micode/notes/data/Contact.java | 36 ++- .../src/net/micode/notes/data/Notes.java | 182 +++++-------- .../notes/data/NotesDatabaseHelper.java | 251 ++++++++++-------- .../net/micode/notes/data/NotesProvider.java | 121 +++++---- .../net/micode/notes/gtask/data/MetaData.java | 8 + .../src/net/micode/notes/gtask/data/Node.java | 4 + .../net/micode/notes/gtask/data/SqlData.java | 17 +- .../net/micode/notes/gtask/data/SqlNote.java | 27 +- .../src/net/micode/notes/gtask/data/Task.java | 68 ++++- .../net/micode/notes/gtask/data/TaskList.java | 26 +- 15 files changed, 453 insertions(+), 322 deletions(-) create mode 100644 Notes-master/.idea/.gitignore create mode 100644 Notes-master/.idea/misc.xml create mode 100644 Notes-master/.idea/modules.xml create mode 100644 Notes-master/.idea/vcs.xml create mode 100644 Notes-master/Notes-master.iml diff --git a/Notes-master/.idea/.gitignore b/Notes-master/.idea/.gitignore new file mode 100644 index 0000000..26d3352 --- /dev/null +++ b/Notes-master/.idea/.gitignore @@ -0,0 +1,3 @@ +# Default ignored files +/shelf/ +/workspace.xml diff --git a/Notes-master/.idea/misc.xml b/Notes-master/.idea/misc.xml new file mode 100644 index 0000000..1945ce5 --- /dev/null +++ b/Notes-master/.idea/misc.xml @@ -0,0 +1,6 @@ + + + + + + \ No newline at end of file diff --git a/Notes-master/.idea/modules.xml b/Notes-master/.idea/modules.xml new file mode 100644 index 0000000..7800270 --- /dev/null +++ b/Notes-master/.idea/modules.xml @@ -0,0 +1,8 @@ + + + + + + + + \ No newline at end of file diff --git a/Notes-master/.idea/vcs.xml b/Notes-master/.idea/vcs.xml new file mode 100644 index 0000000..27b2244 --- /dev/null +++ b/Notes-master/.idea/vcs.xml @@ -0,0 +1,7 @@ + + + + + + + \ No newline at end of file diff --git a/Notes-master/Notes-master.iml b/Notes-master/Notes-master.iml new file mode 100644 index 0000000..c90834f --- /dev/null +++ b/Notes-master/Notes-master.iml @@ -0,0 +1,11 @@ + + + + + + + + + + + \ No newline at end of file diff --git a/Notes-master/src/net/micode/notes/data/Contact.java b/Notes-master/src/net/micode/notes/data/Contact.java index db7c668..f283b23 100644 --- a/Notes-master/src/net/micode/notes/data/Contact.java +++ b/Notes-master/src/net/micode/notes/data/Contact.java @@ -16,40 +16,36 @@ package net.micode.notes.data; -// 导入必要的类库 -import android.content.Context; // 提供应用上下文,用于访问系统服务等。 -import android.database.Cursor; // 用于处理数据库查询结果。 -import android.provider.ContactsContract.CommonDataKinds.Phone; // 提供联系人电话相关的常量 -import android.provider.ContactsContract.Data; // 提供联系人数据表的常量 -import android.telephony.PhoneNumberUtils; // 提供电话号码格式化工具。 -import android.util.Log; // 用于日志记录 +import android.content.Context; +import android.database.Cursor; +import android.provider.ContactsContract.CommonDataKinds.Phone; +import android.provider.ContactsContract.Data; +import android.telephony.PhoneNumberUtils; +import android.util.Log; -import java.util.HashMap; //用于缓存联系人信息。 +import java.util.HashMap; -/** - * Contact 类用于根据电话号码从联系人数据库中获取联系人名称。 - */ public class Contact { + //缓存已经查询过的电话号码和对应的联系人姓名 private static HashMap sContactCache; + //日志输出的标记,方便在日志中识别来源 private static final String TAG = "Contact"; + //匹配电话号码,并确保数据类型是电话号码类型 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 " + + ",?) AND " + Data.MIMETYPE + "='" + Phone.CONTENT_ITEM_TYPE + "'" + + " AND " + Data.RAW_CONTACT_ID + " IN " + "(SELECT raw_contact_id " + " FROM phone_lookup" + " WHERE min_match = '+')"; - // 根据电话号码获取联系人名称。 - // @param context 应用上下文。 - // @param phoneNumber 要查询的电话号码。 - // @return 如果找到匹配的联系人,返回其名称;否则返回 null。 + //返回与该电话号码关联的联系人姓名 public static String getContact(Context context, String phoneNumber) { - if (sContactCache == null) { + if(sContactCache == null) { sContactCache = new HashMap(); } - if (sContactCache.containsKey(phoneNumber)) { + if(sContactCache.containsKey(phoneNumber)) { return sContactCache.get(phoneNumber); } @@ -57,7 +53,7 @@ public class Contact { PhoneNumberUtils.toCallerIDMinMatch(phoneNumber)); Cursor cursor = context.getContentResolver().query( Data.CONTENT_URI, - new String[] { Phone.DISPLAY_NAME }, + new String [] { Phone.DISPLAY_NAME }, selection, new String[] { phoneNumber }, null); diff --git a/Notes-master/src/net/micode/notes/data/Notes.java b/Notes-master/src/net/micode/notes/data/Notes.java index 8db5e0a..3adcc62 100644 --- a/Notes-master/src/net/micode/notes/data/Notes.java +++ b/Notes-master/src/net/micode/notes/data/Notes.java @@ -13,306 +13,247 @@ * See the License for the specific language governing permissions and * limitations under the License. */ -// URI:用于访问笔记和数据表。 -// 列字段接口:定义了笔记表和数据表的列字段及其数据类型。 -// 具体实现类:实现了普通文本笔记和通话笔记的具体功能。 + package net.micode.notes.data; -// 定义了与笔记应用相关的全局常量 import android.net.Uri; - public class Notes { 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; - - // 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 + //笔记类型 + 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 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 Extra常量(用于Activity间传阅) 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"; - // 传递小部件的id public static final String INTENT_EXTRA_WIDGET_ID = "net.micode.notes.widget_id"; - // 传递小部件的类型 public static final String INTENT_EXTRA_WIDGET_TYPE = "net.micode.notes.widget_type"; - // 文件夹的id 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; - // 定义一个内部类 DataConstants,用于集中管理数据类型常量。 + //小部件类型 + 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 class DataConstants { - // 普通笔记 public static final String NOTE = TextNote.CONTENT_ITEM_TYPE; - // 通话笔记 public static final String CALL_NOTE = CallNote.CONTENT_ITEM_TYPE; } - // uri定义 + //内容 URI(ContentProvider 查询路径) + /** + * Uri to query all notes and folders + */ public static final Uri CONTENT_NOTE_URI = Uri.parse("content://" + AUTHORITY + "/note"); + //接口:NoteColumns(笔记表字段定义) /** * Uri to query data */ public static final Uri CONTENT_DATA_URI = Uri.parse("content://" + AUTHORITY + "/data"); - // notecolums接口 public interface NoteColumns { /** * The unique ID for a row - *

- * 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 mDataList; + //初始化一个新的笔记对象,默认为“未创建”状态; + //设置默认值,准备插入新笔记。 public SqlNote(Context context) { mContext = context; mContentResolver = context.getContentResolver(); @@ -143,6 +147,8 @@ public class SqlNote { mDataList = new ArrayList(); } + //通过传入的 Cursor 对象初始化一个已存在的笔记; + //如果是普通笔记,则调用 loadDataContent() 加载子内容。 public SqlNote(Context context, Cursor c) { mContext = context; mContentResolver = context.getContentResolver(); @@ -154,6 +160,8 @@ public class SqlNote { mDiffNoteValues = new ContentValues(); } + //通过笔记 ID 查询并加载数据; + //支持直接构造已有笔记对象 public SqlNote(Context context, long id) { mContext = context; mContentResolver = context.getContentResolver(); @@ -166,6 +174,8 @@ public class SqlNote { } + //根据 ID 查询笔记数据; + //调用 loadFromCursor(Cursor) 进一步解析 private void loadFromCursor(long id) { Cursor c = null; try { @@ -185,6 +195,8 @@ public class SqlNote { } } + //从 Cursor 中读取并赋值到成员变量; + //用于构建已有笔记对象。 private void loadFromCursor(Cursor c) { mId = c.getLong(ID_COLUMN); mAlertDate = c.getLong(ALERTED_DATE_COLUMN); @@ -200,6 +212,9 @@ public class SqlNote { mVersion = c.getLong(VERSION_COLUMN); } + //加载该笔记关联的 Data 内容; + //存入 mDataList 列表中; + //支持多段内容(如多个段落、图片等) private void loadDataContent() { Cursor c = null; mDataList.clear(); @@ -226,6 +241,11 @@ public class SqlNote { } } + //从远程 JSON 数据中设置本地笔记字段; + //分三种情况处理不同类型(系统、文件夹、笔记); + //只记录发生变化的字段进 mDiffNoteValues; + //支持嵌套的 Data 数据; + //返回值表示是否成功设置 public boolean setContent(JSONObject js) { try { JSONObject note = js.getJSONObject(GTaskStringUtils.META_HEAD_NOTE); @@ -359,6 +379,9 @@ public class SqlNote { return true; } + //提交笔记及其子内容到数据库; + //支持带版本验证的更新,防止并发冲突; + //清空差异缓存。 public JSONObject getContent() { try { JSONObject js = new JSONObject(); @@ -426,7 +449,7 @@ public class SqlNote { public long getId() { return mId; - } + }//获取当前笔记的唯一标识 ID public long getParentId() { return mParentId; diff --git a/Notes-master/src/net/micode/notes/gtask/data/Task.java b/Notes-master/src/net/micode/notes/gtask/data/Task.java index 6a19454..4f91c2a 100644 --- a/Notes-master/src/net/micode/notes/gtask/data/Task.java +++ b/Notes-master/src/net/micode/notes/gtask/data/Task.java @@ -54,11 +54,17 @@ public class Task extends Node { mMetaInfo = null; } + /** + * 生成一个用于更新任务的 JSON 对象。 + * + * @param actionId 更新操作的唯一标识符。 + * @return 包含更新信息的 JSONObject。 + */ public JSONObject getCreateAction(int actionId) { JSONObject js = new JSONObject(); try { - // action_type + // 设置操作类型为更新(UPDATE) js.put(GTaskStringUtils.GTASK_JSON_ACTION_TYPE, GTaskStringUtils.GTASK_JSON_ACTION_TYPE_CREATE); @@ -89,12 +95,13 @@ public class Task extends Node { // list_id js.put(GTaskStringUtils.GTASK_JSON_LIST_ID, mParent.getGid()); - // prior_sibling_id + // 将实体变化对象放入主 JSON 对象中 if (mPriorSibling != null) { js.put(GTaskStringUtils.GTASK_JSON_PRIOR_SIBLING_ID, mPriorSibling.getGid()); } } catch (JSONException e) { + // 如果在构建 JSON 对象时发生错误,记录错误日志并抛出异 Log.e(TAG, e.toString()); e.printStackTrace(); throw new ActionFailureException("fail to generate task-create jsonobject"); @@ -135,39 +142,45 @@ public class Task extends Node { return js; } + /** + * 根据远程服务器返回的 JSON 数据设置任务内容。 + * + * @param js 包含任务信息的 JSONObject。 + */ public void setContentByRemoteJSON(JSONObject js) { if (js != null) { try { - // id + // 如果 JSON 对象包含 ID 字段,则设置任务的 GID if (js.has(GTaskStringUtils.GTASK_JSON_ID)) { setGid(js.getString(GTaskStringUtils.GTASK_JSON_ID)); } - // last_modified + // 如果包含 last_modified 字段,则设置最后修改时间 if (js.has(GTaskStringUtils.GTASK_JSON_LAST_MODIFIED)) { setLastModified(js.getLong(GTaskStringUtils.GTASK_JSON_LAST_MODIFIED)); } - // name + // 如果包含 name 字段,则设置任务名称 if (js.has(GTaskStringUtils.GTASK_JSON_NAME)) { setName(js.getString(GTaskStringUtils.GTASK_JSON_NAME)); } - // notes + // 如果包含 notes 字段,则设置任务备注 if (js.has(GTaskStringUtils.GTASK_JSON_NOTES)) { setNotes(js.getString(GTaskStringUtils.GTASK_JSON_NOTES)); } - // deleted + // 如果包含 deleted 字段,则设置删除状态 if (js.has(GTaskStringUtils.GTASK_JSON_DELETED)) { setDeleted(js.getBoolean(GTaskStringUtils.GTASK_JSON_DELETED)); } - // completed + // 如果包含 completed 字段,则设置完成状态 if (js.has(GTaskStringUtils.GTASK_JSON_COMPLETED)) { setCompleted(js.getBoolean(GTaskStringUtils.GTASK_JSON_COMPLETED)); } } catch (JSONException e) { + // 如果解析过程中出现任何问题,记录错误日志并抛出异常 Log.e(TAG, e.toString()); e.printStackTrace(); throw new ActionFailureException("fail to get task content from jsonobject"); @@ -175,21 +188,30 @@ public class Task extends Node { } } + /** + * 根据本地存储的 JSON 数据设置任务内容。 + * + * @param js 包含任务信息的 JSONObject。 + */ public void setContentByLocalJSON(JSONObject js) { + // 检查传入的 JSON 对象是否有效,以及是否包含必需的 "note" 和 "data" 部分 if (js == null || !js.has(GTaskStringUtils.META_HEAD_NOTE) || !js.has(GTaskStringUtils.META_HEAD_DATA)) { Log.w(TAG, "setContentByLocalJSON: nothing is avaiable"); } try { + // 获取 note 和 data 数组部分 JSONObject note = js.getJSONObject(GTaskStringUtils.META_HEAD_NOTE); JSONArray dataArray = js.getJSONArray(GTaskStringUtils.META_HEAD_DATA); + // 检查笔记类型是否正确 if (note.getInt(NoteColumns.TYPE) != Notes.TYPE_NOTE) { Log.e(TAG, "invalid type"); return; } + // 遍历数据数组,查找 MIME 类型为 Note 的项,并设置任务名称 for (int i = 0; i < dataArray.length(); i++) { JSONObject data = dataArray.getJSONObject(i); if (TextUtils.equals(data.getString(DataColumns.MIME_TYPE), DataConstants.NOTE)) { @@ -204,20 +226,31 @@ public class Task extends Node { } } + /** + * 从当前任务的内容生成一个本地使用的 JSON 对象。 + * 如果 mMetaInfo 为空,则表示这是一个新创建的任务,需要构建一个新的 JSON 对象; + * 如果 mMetaInfo 不为空,则表示这是一个已经同步过的任务,更新其中的内容。 + * + * @return 返回一个包含任务信息的 JSONObject,如果遇到错误则返回 null。 + */ public JSONObject getLocalJSONFromContent() { String name = getName(); try { + // 检查是否有元信息 (mMetaInfo) if (mMetaInfo == null) { // new task created from web if (name == null) { + // 如果任务名为空,则认为这个笔记是空的,并记录警告日志 Log.w(TAG, "the note seems to be an empty one"); return null; } + // 创建一个新的 JSON 对象来保存任务的信息 JSONObject js = new JSONObject(); JSONObject note = new JSONObject(); JSONArray dataArray = new JSONArray(); JSONObject data = new JSONObject(); + // 将任务名称放入数据对象中 data.put(DataColumns.CONTENT, name); dataArray.put(data); js.put(GTaskStringUtils.META_HEAD_DATA, dataArray); @@ -229,6 +262,7 @@ public class Task extends Node { JSONObject note = mMetaInfo.getJSONObject(GTaskStringUtils.META_HEAD_NOTE); JSONArray dataArray = mMetaInfo.getJSONArray(GTaskStringUtils.META_HEAD_DATA); + // 遍历数据数组,找到 MIME 类型为 Note 的项,并更新其内容 for (int i = 0; i < dataArray.length(); i++) { JSONObject data = dataArray.getJSONObject(i); if (TextUtils.equals(data.getString(DataColumns.MIME_TYPE), DataConstants.NOTE)) { @@ -236,8 +270,9 @@ public class Task extends Node { break; } } - + // 确保笔记类型设置为普通笔记 note.put(NoteColumns.TYPE, Notes.TYPE_NOTE); + // 返回更新后的元信息 JSON 对象 return mMetaInfo; } } catch (JSONException e) { @@ -247,11 +282,20 @@ public class Task extends Node { } } + /** + * 设置任务(Task)的元信息(MetaInfo),通常是从本地数据库中读取的 JSON 数据。 + * + * @param metaData 包含元数据的对象,可能包含 Note 和 Data 的结构化信息。 + * 如果为 null 或者其中的 notes 字符串无效,则不会更新 mMetaInfo。 + */ public void setMetaInfo(MetaData metaData) { + // 首先判断传入的 metaData 是否为空,并且它是否包含有效的 notes 字符串 if (metaData != null && metaData.getNotes() != null) { try { mMetaInfo = new JSONObject(metaData.getNotes()); } catch (JSONException e) { + // 如果解析失败(比如字符串不是合法的 JSON 格式),则记录警告日志, + // 并将 mMetaInfo 设为 null,表示当前没有可用的元信息 Log.w(TAG, e.toString()); mMetaInfo = null; } @@ -260,16 +304,18 @@ public class Task extends Node { public int getSyncAction(Cursor c) { try { + // 从 mMetaInfo 中获取 "note" 部分的信息,这个 mMetaInfo 是之前保存的 JSON 数据 JSONObject noteInfo = null; if (mMetaInfo != null && mMetaInfo.has(GTaskStringUtils.META_HEAD_NOTE)) { noteInfo = mMetaInfo.getJSONObject(GTaskStringUtils.META_HEAD_NOTE); } + // 如果 noteInfo 为 null,说明远程笔记可能已经被删除了 if (noteInfo == null) { Log.w(TAG, "it seems that note meta has been deleted"); return SYNC_ACTION_UPDATE_REMOTE; } - + // 如果 noteInfo 中没有 NoteColumns.ID 字段,说明远程笔记 ID 被删除了 if (!noteInfo.has(NoteColumns.ID)) { Log.w(TAG, "remote note id seems to be deleted"); return SYNC_ACTION_UPDATE_LOCAL; @@ -281,6 +327,8 @@ public class Task extends Node { return SYNC_ACTION_UPDATE_LOCAL; } + // 现在验证本地数据库中的 Note ID 是否与远程保存的一致 + // 从 Cursor 中获取当前数据库中的 Note ID if (c.getInt(SqlNote.LOCAL_MODIFIED_COLUMN) == 0) { // there is no local update if (c.getLong(SqlNote.SYNC_ID_COLUMN) == getLastModified()) { diff --git a/Notes-master/src/net/micode/notes/gtask/data/TaskList.java b/Notes-master/src/net/micode/notes/gtask/data/TaskList.java index 4ea21c5..6642b1a 100644 --- a/Notes-master/src/net/micode/notes/gtask/data/TaskList.java +++ b/Notes-master/src/net/micode/notes/gtask/data/TaskList.java @@ -29,7 +29,14 @@ import org.json.JSONObject; import java.util.ArrayList; - +/** + * TaskList 表示一个任务列表(如“文件夹”或“组”),用于管理一组子任务(Task)。 + * + * 该类继承自 Node,具备创建、更新、同步等行为,并支持本地与远程 JSON 数据之间的转换。 + */ +//初始化一个空的任务列表。 +//mChildren 存储当前列表下的所有子任务。 +//mIndex 可能是用于排序或唯一标识的索引值 public class TaskList extends Node { private static final String TAG = TaskList.class.getSimpleName(); @@ -43,6 +50,12 @@ public class TaskList extends Node { mIndex = 1; } + /** + * 生成用于在服务器上创建该任务列表的 JSON 对象。 + * + * @param actionId 操作 ID,用于唯一标识此次操作。 + * @return 包含创建信息的 JSONObject。 + */ public JSONObject getCreateAction(int actionId) { JSONObject js = new JSONObject(); @@ -74,6 +87,12 @@ public class TaskList extends Node { return js; } + /** + * 生成用于更新该任务列表的 JSON 对象。 + * + * @param actionId 操作 ID,用于唯一标识此次操作。 + * @return 包含更新信息的 JSONObject。 + */ public JSONObject getUpdateAction(int actionId) { JSONObject js = new JSONObject(); @@ -103,6 +122,11 @@ public class TaskList extends Node { return js; } + /** + * 根据远程服务器返回的 JSON 数据设置任务列表的基本属性。 + * + * @param js 包含远程数据的 JSON 对象。 + */ public void setContentByRemoteJSON(JSONObject js) { if (js != null) { try {