From 1a3da5bc845365c98894e8f1999363e3beda9d45 Mon Sep 17 00:00:00 2001 From: zcs <2755796131@qq.com> Date: Mon, 30 Dec 2024 12:00:07 +0800 Subject: [PATCH 1/4] =?UTF-8?q?=E5=BC=A0=E6=98=8C=E7=9B=9B=20202201002062?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../src/net/micode/notes/data/Contact.java | 26 ++- .../notes/data/NotesDatabaseHelper.java | 66 +++--- .../net/micode/notes/data/NotesProvider.java | 67 ++++-- .../net/micode/notes/gtask/data/MetaData.java | 36 +++- .../src/net/micode/notes/gtask/data/Node.java | 64 +++--- .../net/micode/notes/gtask/data/SqlData.java | 84 ++++---- .../net/micode/notes/gtask/data/SqlNote.java | 151 ++++++++------ .../src/net/micode/notes/gtask/data/Task.java | 111 ++++++---- .../net/micode/notes/gtask/data/TaskList.java | 99 ++++++--- .../exception/ActionFailureException.java | 13 +- .../exception/NetworkFailureException.java | 17 +- .../notes/gtask/remote/GTaskASyncTask.java | 41 +++- .../notes/gtask/remote/GTaskClient.java | 121 +++++++---- .../notes/gtask/remote/GTaskManager.java | 191 +++++++++++------ .../notes/gtask/remote/GTaskSyncService.java | 61 ++++-- .../src/net/micode/notes/model/Note.java | 88 ++++++-- .../net/micode/notes/model/WorkingNote.java | 195 +++++++++++++----- .../net/micode/notes/tool/BackupUtils.java | 75 +++++-- .../src/net/micode/notes/tool/DataUtils.java | 113 +++++++--- .../micode/notes/tool/GTaskStringUtils.java | 139 +++++++++---- 20 files changed, 1190 insertions(+), 568 deletions(-) diff --git a/src/Notes-master/src/net/micode/notes/data/Contact.java b/src/Notes-master/src/net/micode/notes/data/Contact.java index e1f8942..16c899f 100644 --- a/src/Notes-master/src/net/micode/notes/data/Contact.java +++ b/src/Notes-master/src/net/micode/notes/data/Contact.java @@ -24,29 +24,34 @@ import android.telephony.PhoneNumberUtils; import android.util.Log; import java.util.HashMap; - //ceshihebing2 +// Contact类,主要用于根据电话号码获取对应的联系人姓名 public class Contact { + // 用于缓存联系人信息(电话号码与对应姓名的映射)的哈希表,以避免重复查询数据库 private static HashMap sContactCache; + // 用于日志记录的标签,方便在日志中识别相关输出所属的类 private static final String TAG = "Contact"; - + // 构建查询联系人的SQL语句的选择条件部分,用于从联系人数据中筛选出符合条件的记录 + // 条件基于电话号码匹配、MIME类型匹配以及原始联系人ID的相关限定等 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 = '+')"; - +// 根据给定的上下文(Context)和电话号码获取对应的联系人姓名 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)); Cursor cursor = context.getContentResolver().query( @@ -55,19 +60,28 @@ public class Contact { selection, new String[] { phoneNumber }, null); - + // 通过上下文的内容解析器(ContentResolver)发起数据库查询操作 + // 查询联系人数据的相关表(Data.CONTENT_URI),只获取联系人的显示名称(Phone.DISPLAY_NAME)字段 + // 使用上面构建好的选择条件(selection),并传入电话号码作为查询参数 +// 如果查询结果游标(Cursor)不为空,并且游标能够移动到第一条记录(意味着有匹配的联系人数据) if (cursor != null && cursor.moveToFirst()) { try { + // 从游标中获取联系人的显示名称(假设显示名称在结果集的第0个位置) String name = cursor.getString(0); + // 从游标中获取联系人的显示名称(假设显示名称在结果集的第0个位置) sContactCache.put(phoneNumber, name); + // 返回获取到的联系人姓名 return name; } catch (IndexOutOfBoundsException e) { + // 如果在从游标获取数据时发生越界异常(比如结果集格式不符合预期等情况),记录错误日志 Log.e(TAG, " Cursor get string error " + e.toString()); return null; } finally { + // 无论是否发生异常,都要关闭游标,释放相关资源 cursor.close(); } } else { + // 如果游标为空或者游标中没有匹配的记录,记录调试日志表示没有找到对应联系人 Log.d(TAG, "No contact matched with number:" + phoneNumber); return null; } diff --git a/src/Notes-master/src/net/micode/notes/data/NotesDatabaseHelper.java b/src/Notes-master/src/net/micode/notes/data/NotesDatabaseHelper.java index ffe5d57..2965719 100644 --- a/src/Notes-master/src/net/micode/notes/data/NotesDatabaseHelper.java +++ b/src/Notes-master/src/net/micode/notes/data/NotesDatabaseHelper.java @@ -25,23 +25,26 @@ 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; +// NotesDatabaseHelper类继承自SQLiteOpenHelper,用于管理与笔记相关的数据库的创建、升级以及一些数据库表结构和触发器的初始化操作 public class NotesDatabaseHelper extends SQLiteOpenHelper { + // 定义数据库的名称,这里是"note.db",即该应用对应的数据库文件名 private static final String DB_NAME = "note.db"; - +// 定义数据库的版本号,用于数据库升级等场景判断,当前版本为4 private static final int DB_VERSION = 4; - +// TABLE接口,用于定义数据库中不同表的名称常量,方便在代码中统一引用,避免硬编码表名 public interface TABLE { public static final String NOTE = "note"; public static final String DATA = "data"; } - +// TABLE接口,用于定义数据库中不同表的名称常量,方便在代码中统一引用,避免硬编码表名 private static final String TAG = "NotesDatabaseHelper"; - +// 采用单例模式,保存该类的唯一实例,确保整个应用中只有一个数据库帮助类实例存在 private static NotesDatabaseHelper mInstance; - + // 创建"note"表的SQL语句,定义了表的各个列名、数据类型以及默认值等信息 + // 例如定义了笔记的ID、父级ID、提醒日期、背景颜色ID等列,为笔记相关数据存储做准备 private static final String CREATE_NOTE_TABLE_SQL = "CREATE TABLE " + TABLE.NOTE + "(" + NoteColumns.ID + " INTEGER PRIMARY KEY," + @@ -62,7 +65,7 @@ public class NotesDatabaseHelper extends SQLiteOpenHelper { NoteColumns.GTASK_ID + " TEXT NOT NULL DEFAULT ''," + NoteColumns.VERSION + " INTEGER NOT NULL DEFAULT 0" + ")"; - +// 创建"data"表的SQL语句,同样定义了表的各列信息,用于存储笔记相关的数据内容等信息,如MIME类型、所属笔记ID、创建日期等 private static final String CREATE_DATA_TABLE_SQL = "CREATE TABLE " + TABLE.DATA + "(" + DataColumns.ID + " INTEGER PRIMARY KEY," + @@ -77,13 +80,14 @@ public class NotesDatabaseHelper extends SQLiteOpenHelper { DataColumns.DATA4 + " TEXT NOT NULL DEFAULT ''," + DataColumns.DATA5 + " TEXT NOT NULL DEFAULT ''" + ")"; - +// 创建针对"data"表中NOTE_ID列的索引的SQL语句,有助于提高基于笔记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 + * 当笔记移动到文件夹时,增加文件夹的笔记数量的触发器SQL语句定义 */ private static final String NOTE_INCREASE_FOLDER_COUNT_ON_UPDATE_TRIGGER = "CREATE TRIGGER increase_folder_count_on_update "+ @@ -96,6 +100,7 @@ public class NotesDatabaseHelper extends SQLiteOpenHelper { /** * Decrease folder's note count when move note from folder + * 当笔记移动到文件夹时,增加文件夹的笔记数量的触发器SQL语句定义 */ private static final String NOTE_DECREASE_FOLDER_COUNT_ON_UPDATE_TRIGGER = "CREATE TRIGGER decrease_folder_count_on_update " + @@ -109,6 +114,7 @@ public class NotesDatabaseHelper extends SQLiteOpenHelper { /** * Increase folder's note count when insert new note to the folder + * 当从文件夹中删除笔记时,减少文件夹的笔记数量的触发器SQL语句定义,同样要确保当前笔记数量大于0才进行减少操作 */ private static final String NOTE_INCREASE_FOLDER_COUNT_ON_INSERT_TRIGGER = "CREATE TRIGGER increase_folder_count_on_insert " + @@ -121,6 +127,7 @@ public class NotesDatabaseHelper extends SQLiteOpenHelper { /** * Decrease folder's note count when delete note from the folder + * 当插入类型为DataConstants.NOTE的数据时,更新笔记内容的触发器SQL语句定义 */ private static final String NOTE_DECREASE_FOLDER_COUNT_ON_DELETE_TRIGGER = "CREATE TRIGGER decrease_folder_count_on_delete " + @@ -134,6 +141,7 @@ public class NotesDatabaseHelper extends SQLiteOpenHelper { /** * Update note's content when insert data with type {@link DataConstants#NOTE} + * 当类型为DataConstants.NOTE的数据发生变化时,更新笔记内容的触发器SQL语句定义 */ private static final String DATA_UPDATE_NOTE_CONTENT_ON_INSERT_TRIGGER = "CREATE TRIGGER update_note_content_on_insert " + @@ -147,6 +155,7 @@ public class NotesDatabaseHelper extends SQLiteOpenHelper { /** * Update note's content when data with {@link DataConstants#NOTE} type has changed + * 当类型为DataConstants.NOTE的数据被删除时,更新笔记内容的触发器SQL语句定义,将对应笔记的内容设置为空字符串 */ private static final String DATA_UPDATE_NOTE_CONTENT_ON_UPDATE_TRIGGER = "CREATE TRIGGER update_note_content_on_update " + @@ -184,6 +193,7 @@ public class NotesDatabaseHelper extends SQLiteOpenHelper { /** * Delete notes belong to folder which has been deleted + * 当笔记被删除时,删除属于该笔记的数据的触发器SQL语句定义 */ private static final String FOLDER_DELETE_NOTES_ON_DELETE_TRIGGER = "CREATE TRIGGER folder_delete_notes_on_delete " + @@ -195,6 +205,7 @@ public class NotesDatabaseHelper extends SQLiteOpenHelper { /** * Move notes belong to folder which has been moved to trash folder + * 当文件夹被移动到回收站文件夹时,将属于该文件夹的笔记也移动到回收站文件夹的触发器SQL语句定义 */ private static final String FOLDER_MOVE_NOTES_ON_TRASH_TRIGGER = "CREATE TRIGGER folder_move_notes_on_trash " + @@ -205,18 +216,18 @@ public class NotesDatabaseHelper extends SQLiteOpenHelper { " SET " + NoteColumns.PARENT_ID + "=" + Notes.ID_TRASH_FOLER + " WHERE " + NoteColumns.PARENT_ID + "=old." + NoteColumns.ID + ";" + " END"; - +// 构造函数,调用父类SQLiteOpenHelper的构造函数,传入数据库名称、版本号等信息 public NotesDatabaseHelper(Context context) { super(context, DB_NAME, null, DB_VERSION); } - +// 在给定的SQLiteDatabase对象上执行创建"note"表的操作,包括创建表、重新创建相关触发器以及创建系统文件夹,并输出日志表示表已创建 public void createNoteTable(SQLiteDatabase db) { db.execSQL(CREATE_NOTE_TABLE_SQL); reCreateNoteTableTriggers(db); createSystemFolder(db); Log.d(TAG, "note table has been created"); } - +// 先删除已存在的"note"表相关的触发器(如果有),然后再重新创建所有"note"表对应的触发器 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"); @@ -234,17 +245,17 @@ public class NotesDatabaseHelper extends SQLiteOpenHelper { db.execSQL(FOLDER_DELETE_NOTES_ON_DELETE_TRIGGER); db.execSQL(FOLDER_MOVE_NOTES_ON_TRASH_TRIGGER); } - + // 向"note"表中插入代表不同系统文件夹的记录,如通话记录文件夹、根文件夹、临时文件夹、回收站文件夹等,通过构造ContentValues对象并调用db.insert方法来实现 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 */ @@ -252,7 +263,7 @@ public class NotesDatabaseHelper extends SQLiteOpenHelper { 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 */ @@ -269,14 +280,14 @@ public class NotesDatabaseHelper extends SQLiteOpenHelper { values.put(NoteColumns.TYPE, Notes.TYPE_SYSTEM); db.insert(TABLE.NOTE, null, values); } - + // 在给定数据库对象上创建"data"表,创建表后会重新创建"data"表相关的触发器以及创建针对"data"表NOTE_ID列的索引,并输出日志表示表已创建 public void createDataTable(SQLiteDatabase db) { db.execSQL(CREATE_DATA_TABLE_SQL); reCreateDataTableTriggers(db); db.execSQL(CREATE_DATA_NOTE_ID_INDEX_SQL); 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"); @@ -286,6 +297,7 @@ public class NotesDatabaseHelper extends SQLiteOpenHelper { db.execSQL(DATA_UPDATE_NOTE_CONTENT_ON_UPDATE_TRIGGER); db.execSQL(DATA_UPDATE_NOTE_CONTENT_ON_DELETE_TRIGGER); } +// 采用静态同步方法实现单例模式获取类的实例,若实例不存在则创建新的NotesDatabaseHelper实例并返回,保证整个应用只有一个该类的实例在使用 static synchronized NotesDatabaseHelper getInstance(Context context) { if (mInstance == null) { @@ -293,59 +305,61 @@ public class NotesDatabaseHelper extends SQLiteOpenHelper { } return mInstance; } - +// 重写SQLiteOpenHelper的onCreate方法,在数据库首次创建时调用,内部会分别调用createNoteTable(db)和createDataTable(db)来创建"note"表和"data"表 @Override public void onCreate(SQLiteDatabase db) { createNoteTable(db); createDataTable(db); } - +// 重写SQLiteOpenHelper的onUpgrade方法,用于处理数据库升级操作,根据旧版本号和新版本号进行不同的升级逻辑判断与处理 @Override public void onUpgrade(SQLiteDatabase db, int oldVersion, int newVersion) { boolean reCreateTriggers = false; boolean skipV2 = false; - +// 重写SQLiteOpenHelper的onUpgrade方法,用于处理数据库升级操作,根据旧版本号和新版本号进行不同的升级逻辑判断与处理 if (oldVersion == 1) { upgradeToV2(db); skipV2 = true; // this upgrade including the upgrade from v2 to v3 oldVersion++; } - +// 如果旧版本是2且没有跳过v2的升级(即不是从版本1升上来的情况),执行升级到版本3的操作,设置需要重新创建触发器的标记为true,再将旧版本号加1 if (oldVersion == 2 && !skipV2) { upgradeToV3(db); reCreateTriggers = true; oldVersion++; } - +// 如果旧版本是3,执行升级到版本4的操作,然后将旧版本号加1 if (oldVersion == 3) { upgradeToV4(db); oldVersion++; } - +// 如果需要重新创建触发器(根据前面的升级逻辑判断),则分别重新创建"note"表和"data"表相关的触发器 if (reCreateTriggers) { reCreateNoteTableTriggers(db); reCreateDataTableTriggers(db); } - + // 如果最终旧版本号和新版本号不一致,说明升级出现问题,抛出异常表示升级失败 if (oldVersion != newVersion) { throw new IllegalStateException("Upgrade notes database to version " + newVersion + "fails"); } } - +// 升级到版本2的具体操作,先删除已存在的"note"表和"data"表(如果有),然后重新创建这两个表 private void upgradeToV2(SQLiteDatabase db) { db.execSQL("DROP TABLE IF EXISTS " + TABLE.NOTE); db.execSQL("DROP TABLE IF EXISTS " + TABLE.DATA); createNoteTable(db); createDataTable(db); } - +// 升级到版本3的具体操作,包括删除一些不再使用的触发器,向"note"表添加gtask id列,以及插入一个回收站系统文件夹记录 private void upgradeToV3(SQLiteDatabase db) { + // drop unused triggers,删除不再使用的用于更新笔记修改日期的相关触发器 // 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 + // 向"note"表添加gtask id列,数据类型为文本,默认值为空字符串 db.execSQL("ALTER TABLE " + TABLE.NOTE + " ADD COLUMN " + NoteColumns.GTASK_ID + " TEXT NOT NULL DEFAULT ''"); // add a trash system folder @@ -354,7 +368,7 @@ public class NotesDatabaseHelper extends SQLiteOpenHelper { values.put(NoteColumns.TYPE, Notes.TYPE_SYSTEM); db.insert(TABLE.NOTE, null, values); } - +// 升级到版本4的具体操作,向"note"表添加version列,数据类型为整数,默认值为0 private void upgradeToV4(SQLiteDatabase db) { db.execSQL("ALTER TABLE " + TABLE.NOTE + " ADD COLUMN " + NoteColumns.VERSION + " INTEGER NOT NULL DEFAULT 0"); diff --git a/src/Notes-master/src/net/micode/notes/data/NotesProvider.java b/src/Notes-master/src/net/micode/notes/data/NotesProvider.java index edb0a60..12cbc3d 100644 --- a/src/Notes-master/src/net/micode/notes/data/NotesProvider.java +++ b/src/Notes-master/src/net/micode/notes/data/NotesProvider.java @@ -33,15 +33,17 @@ import net.micode.notes.R; import net.micode.notes.data.Notes.DataColumns; import net.micode.notes.data.Notes.NoteColumns; import net.micode.notes.data.NotesDatabaseHelper.TABLE; - +// NotesProvider类继承自ContentProvider,用于作为内容提供者,对外提供对笔记相关数据的增删改查等操作, +// 并处理不同类型的Uri请求以区分操作的具体对象(如笔记、笔记数据等)以及搜索相关功能。 public class NotesProvider extends ContentProvider { + // 用于匹配不同的Uri请求的UriMatcher对象,通过静态代码块进行初始化配置 private static final UriMatcher mMatcher; - +// 用于匹配不同的Uri请求的UriMatcher对象,通过静态代码块进行初始化配置 private NotesDatabaseHelper mHelper; - +// 用于日志输出的标识字符串,便于在调试和记录运行情况时准确识别相关日志信息 private static final String TAG = "NotesProvider"; - +// 定义不同的Uri匹配码,用于区分不同类型的Uri请求,对应不同的笔记或数据相关操作 private static final int URI_NOTE = 1; private static final int URI_NOTE_ITEM = 2; private static final int URI_DATA = 3; @@ -49,7 +51,7 @@ public class NotesProvider extends ContentProvider { private static final int URI_SEARCH = 5; private static final int URI_SEARCH_SUGGEST = 6; - +// 静态代码块,初始化UriMatcher对象,添加各种不同的Uri匹配规则,以便后续根据传入的Uri来确定具体的操作类型 static { mMatcher = new UriMatcher(UriMatcher.NO_MATCH); mMatcher.addURI(Notes.AUTHORITY, "note", URI_NOTE); @@ -64,6 +66,8 @@ public class NotesProvider extends ContentProvider { /** * 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. + * 定义搜索结果的投影(查询返回的列)字符串,用于在搜索笔记时获取特定的列信息, + * 例如笔记的ID、将笔记摘要列处理后作为搜索建议的文本等,同时设置了图标、意图动作、意图数据类型等相关信息。 */ private static final String NOTES_SEARCH_PROJECTION = NoteColumns.ID + "," + NoteColumns.ID + " AS " + SearchManager.SUGGEST_COLUMN_INTENT_EXTRA_DATA + "," @@ -72,46 +76,53 @@ public class NotesProvider extends ContentProvider { + 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; - +// 在ContentProvider创建时调用,获取NotesDatabaseHelper的单例实例,用于后续数据库操作,返回true表示初始化成功 @Override public boolean onCreate() { mHelper = NotesDatabaseHelper.getInstance(getContext()); return true; } - +// 根据传入的Uri等参数执行查询操作,根据不同的Uri匹配情况,从相应的数据库表(笔记表或数据表)中查询数据,并返回结果游标 @Override public Cursor query(Uri uri, String[] projection, String selection, String[] selectionArgs, String sortOrder) { Cursor c = null; + // 获取可读的数据库连接对象 SQLiteDatabase db = mHelper.getReadableDatabase(); String id = null; + // 通过UriMatcher匹配传入的Uri,根据匹配结果执行不同的查询逻辑 switch (mMatcher.match(uri)) { case URI_NOTE: + // 查询笔记表,使用传入的投影、选择条件等参数进行查询 c = db.query(TABLE.NOTE, projection, selection, selectionArgs, null, null, sortOrder); break; case URI_NOTE_ITEM: + // 获取具体笔记项的ID(从Uri路径中解析),然后根据该ID构建更精确的查询条件进行查询 id = uri.getPathSegments().get(1); c = db.query(TABLE.NOTE, projection, NoteColumns.ID + "=" + id + parseSelection(selection), selectionArgs, null, null, sortOrder); break; case URI_DATA: + // 查询数据表,同样使用传入的相关参数进行查询 c = db.query(TABLE.DATA, projection, selection, selectionArgs, null, null, sortOrder); break; case URI_DATA_ITEM: + // 获取具体数据项的ID(从Uri路径中解析),然后基于该ID构建查询条件查询数据表 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: + // 对于搜索相关的Uri请求,如果传入了排序、投影等额外参数则抛出异常,因为搜索操作有其固定的逻辑 if (sortOrder != null || projection != null) { throw new IllegalArgumentException( "do not specify sortOrder, selection, selectionArgs, or projection" + "with this query"); @@ -119,10 +130,12 @@ public class NotesProvider extends ContentProvider { String searchString = null; if (mMatcher.match(uri) == URI_SEARCH_SUGGEST) { + // 如果是搜索建议的Uri且路径中有搜索字符串,则获取该字符串 if (uri.getPathSegments().size() > 1) { searchString = uri.getPathSegments().get(1); } } else { + // 否则从Uri的查询参数中获取名为"pattern"的搜索字符串 searchString = uri.getQueryParameter("pattern"); } @@ -131,7 +144,9 @@ public class NotesProvider extends ContentProvider { } try { + // 格式化搜索字符串,添加通配符用于模糊匹配查询 searchString = String.format("%%%s%%", searchString); + // 使用格式化后的搜索字符串执行原始查询,查询符合条件的笔记信息 c = db.rawQuery(NOTES_SNIPPET_SEARCH_QUERY, new String[] { searchString }); } catch (IllegalStateException ex) { @@ -141,21 +156,25 @@ public class NotesProvider extends ContentProvider { default: throw new IllegalArgumentException("Unknown URI " + uri); } + // 如果查询结果游标不为空,设置其通知Uri,以便在数据变化时能收到通知更新相关UI等 if (c != null) { c.setNotificationUri(getContext().getContentResolver(), uri); } return c; } - + // 根据传入的Uri和数据值,向相应的数据库表(笔记表或数据表)插入数据,并返回插入后生成的新记录的Uri @Override public Uri insert(Uri uri, ContentValues values) { SQLiteDatabase db = mHelper.getWritableDatabase(); long dataId = 0, noteId = 0, insertedId = 0; + // 通过UriMatcher匹配Uri,根据匹配结果执行不同的插入逻辑 switch (mMatcher.match(uri)) { case URI_NOTE: + // 向笔记表插入数据,获取插入后生成的记录ID insertedId = noteId = db.insert(TABLE.NOTE, null, values); break; case URI_DATA: + // 如果插入的数据中包含笔记ID,则获取该ID,然后向数据表插入数据并获取插入后的记录ID if (values.containsKey(DataColumns.NOTE_ID)) { noteId = values.getAsLong(DataColumns.NOTE_ID); } else { @@ -167,32 +186,36 @@ public class NotesProvider extends ContentProvider { throw new IllegalArgumentException("Unknown URI " + uri); } // Notify the note uri + // 如果插入的是笔记数据且笔记ID大于0,通知笔记相关的Uri数据发生了变化 if (noteId > 0) { getContext().getContentResolver().notifyChange( ContentUris.withAppendedId(Notes.CONTENT_NOTE_URI, noteId), null); } - + // 如果插入的是数据且数据ID大于0,通知数据相关的Uri数据发生了变化 // Notify the data uri if (dataId > 0) { getContext().getContentResolver().notifyChange( ContentUris.withAppendedId(Notes.CONTENT_DATA_URI, dataId), null); } - +// 返回插入后新记录对应的Uri,通过将插入的记录ID附加到原始Uri上生成 return ContentUris.withAppendedId(uri, insertedId); } - +// 根据传入的Uri和选择条件,从相应的数据库表(笔记表或数据表)中删除数据,并返回删除的记录数量 @Override public int delete(Uri uri, String selection, String[] selectionArgs) { int count = 0; String id = null; SQLiteDatabase db = mHelper.getWritableDatabase(); boolean deleteData = false; + // 通过UriMatcher匹配Uri,根据匹配结果执行不同的删除逻辑 switch (mMatcher.match(uri)) { case URI_NOTE: + // 构建笔记表删除的选择条件,确保ID大于0(可能有额外的传入选择条件拼接),然后执行删除操作并获取删除的记录数量 selection = "(" + selection + ") AND " + NoteColumns.ID + ">0 "; count = db.delete(TABLE.NOTE, selection, selectionArgs); break; case URI_NOTE_ITEM: + // 获取要删除的具体笔记项的ID,先判断是否为不允许删除的系统文件夹(ID小于等于0),如果不是则构建精确的删除条件进行删除操作 id = uri.getPathSegments().get(1); /** * ID that smaller than 0 is system folder which is not allowed to @@ -206,10 +229,12 @@ public class NotesProvider extends ContentProvider { NoteColumns.ID + "=" + id + parseSelection(selection), selectionArgs); break; case URI_DATA: + // 直接从数据表中根据选择条件执行删除操作,标记要通知数据相关的Uri变化,并获取删除的记录数量 count = db.delete(TABLE.DATA, selection, selectionArgs); deleteData = true; break; case URI_DATA_ITEM: + // 获取要删除的具体数据项的ID,构建精确的删除条件从数据表中删除数据,同样标记要通知数据相关的Uri变化,并获取删除的记录数量 id = uri.getPathSegments().get(1); count = db.delete(TABLE.DATA, DataColumns.ID + "=" + id + parseSelection(selection), selectionArgs); @@ -220,35 +245,42 @@ public class NotesProvider extends ContentProvider { } if (count > 0) { if (deleteData) { + // 如果是更新数据相关操作且有数据被更新,通知笔记相关的Uri数据发生了变化(可能关联的笔记数据有变动) getContext().getContentResolver().notifyChange(Notes.CONTENT_NOTE_URI, null); } + // 通知传入的Uri对应的数据发生了变化 getContext().getContentResolver().notifyChange(uri, null); } 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(); boolean updateData = false; + // 通过UriMatcher匹配Uri,根据匹配结果执行不同的更新逻辑 switch (mMatcher.match(uri)) { case URI_NOTE: + // 在更新笔记表数据前,调用方法更新笔记的版本号(可能根据选择条件等情况),然后执行更新操作并获取更新的记录数量 increaseNoteVersion(-1, selection, selectionArgs); count = db.update(TABLE.NOTE, values, selection, selectionArgs); break; case URI_NOTE_ITEM: + // 获取要更新的具体笔记项的ID,调用方法根据该ID等情况更新笔记的版本号,然后构建精确的更新条件对笔记表执行更新操作并获取记录数量 id = uri.getPathSegments().get(1); increaseNoteVersion(Long.valueOf(id), selection, selectionArgs); count = db.update(TABLE.NOTE, values, NoteColumns.ID + "=" + id + parseSelection(selection), selectionArgs); break; case URI_DATA: + // 直接从数据表中根据选择条件执行删除操作,标记要通知数据相关的Uri变化,并获取删除的记录数量 count = db.update(TABLE.DATA, values, selection, selectionArgs); updateData = true; break; case URI_DATA_ITEM: + // 获取要删除的具体数据项的ID,构建精确的删除条件从数据表中删除数据,同样标记要通知数据相关的Uri变化,并获取删除的记录数量 id = uri.getPathSegments().get(1); count = db.update(TABLE.DATA, values, DataColumns.ID + "=" + id + parseSelection(selection), selectionArgs); @@ -260,17 +292,18 @@ public class NotesProvider extends ContentProvider { if (count > 0) { if (updateData) { + // 如果是删除数据相关操作且有数据被删除,通知笔记相关的Uri数据发生了变化(可能关联的笔记数据有变动) getContext().getContentResolver().notifyChange(Notes.CONTENT_NOTE_URI, null); - } + } // 通知传入的Uri对应的数据发生了变化 getContext().getContentResolver().notifyChange(uri, null); } return count; } - + // 根据传入的Uri、更新的数据值和选择条件,对相应的数据库表(笔记表或数据表)中的数据进行更新,并返回更新的记录数量 private String parseSelection(String selection) { return (!TextUtils.isEmpty(selection) ? " AND (" + selection + ')' : ""); } - + // 根据传入的笔记ID、选择条件和选择参数,构建并执行更新笔记版本号的SQL语句,实现笔记版本号的增加操作 private void increaseNoteVersion(long id, String selection, String[] selectionArgs) { StringBuilder sql = new StringBuilder(120); sql.append("UPDATE "); @@ -292,7 +325,7 @@ public class NotesProvider extends ContentProvider { } sql.append(selectString); } - +// 用于获取 mHelper.getWritableDatabase().execSQL(sql.toString()); } diff --git a/src/Notes-master/src/net/micode/notes/gtask/data/MetaData.java b/src/Notes-master/src/net/micode/notes/gtask/data/MetaData.java index 41021a3..cbedbd5 100644 --- a/src/Notes-master/src/net/micode/notes/gtask/data/MetaData.java +++ b/src/Notes-master/src/net/micode/notes/gtask/data/MetaData.java @@ -14,7 +14,7 @@ * limitations under the License. */ -package net.micode.notes.gtask.data;//测试 +package net.micode.notes.gtask.data; import android.database.Cursor; import android.util.Log; @@ -23,38 +23,51 @@ import net.micode.notes.tool.GTaskStringUtils; import org.json.JSONException; import org.json.JSONObject; - +// MetaData类继承自Task类,主要用于处理与任务相关的元数据操作,例如设置元数据、获取关联的GID(可能是某种任务相关的唯一标识)等, +// 并且重写了一些父类的方法来适配自身对于元数据处理的特殊逻辑。 public class MetaData extends Task { + // 用于日志输出的标识字符串,其值为类的简单名称,便于在调试和记录运行情况时准确识别相关日志信息 private final static String TAG = MetaData.class.getSimpleName(); - +// 用于存储与该元数据相关联的GID(可能是Google Tasks等相关任务系统中的任务唯一标识之类的信息),初始化为null private String mRelatedGid = null; - +// 设置元数据的方法,接收一个GID(任务标识)和一个JSONObject类型的元数据信息对象 public void setMeta(String gid, JSONObject metaInfo) { try { + // 将传入的GID放入元数据信息对象中,对应的键由GTaskStringUtils.META_HEAD_GTASK_ID指定, + // 如果放入过程中出现JSON异常则记录错误日志 metaInfo.put(GTaskStringUtils.META_HEAD_GTASK_ID, gid); } catch (JSONException e) { Log.e(TAG, "failed to put related gid"); } + // 将处理后的元数据信息对象转换为字符串,并调用父类的方法(假设父类有相应的设置笔记内容的方法)设置为笔记内容 setNotes(metaInfo.toString()); + // 设置名称为特定的字符串,该字符串由GTaskStringUtils.META_NOTE_NAME指定,可能用于标识这是元数据相关的名称 setName(GTaskStringUtils.META_NOTE_NAME); - } + } +// 获取关联的GID的方法,返回之前存储的mRelatedGid字符串 public String getRelatedGid() { return mRelatedGid; } - +// 重写父类的方法,用于判断当前的元数据是否值得保存,判断依据是看获取到的笔记内容(通过父类方法获取,可能存储着关键元数据信息)是否为null, + // 如果不为null则表示有值得保存的数据,返回true,否则返回false @Override public boolean isWorthSaving() { return getNotes() != null; } - +// 重写父类的方法,用于根据远程的JSON对象来设置自身的内容(包括解析出关联的GID等元数据相关操作) @Override public void setContentByRemoteJSON(JSONObject js) { + // 先调用父类的同名方法来执行一些通用的设置内容的基础操作(假设父类该方法有相应逻辑) super.setContentByRemoteJSON(js); + // 如果获取到的笔记内容不为null(说明有元数据信息) if (getNotes() != null) { try { + // 将笔记内容字符串转换为JSONObject对象,以便解析其中的元数据信息 JSONObject metaInfo = new JSONObject(getNotes().trim()); + // 从解析后的元数据信息对象中获取关联的GID,并赋值给mRelatedGid成员变量, + // 如果获取过程中出现JSON异常,则记录警告日志并将mRelatedGid设置为null mRelatedGid = metaInfo.getString(GTaskStringUtils.META_HEAD_GTASK_ID); } catch (JSONException e) { Log.w(TAG, "failed to get related gid"); @@ -62,18 +75,21 @@ public class MetaData extends Task { } } } - +// 重写父类的方法,这里明确表示该方法不应该被调用,直接抛出 IllegalAccessError异常, + // 可能是因为对于元数据对象来说,通过本地JSON来设置内容不符合其业务逻辑设计 @Override public void setContentByLocalJSON(JSONObject js) { // this function should not be called throw new IllegalAccessError("MetaData:setContentByLocalJSON should not be called"); } - +// 重写父类的方法,同样明确表示该方法不应该被调用,直接抛出 IllegalAccessError异常, + // 可能是因为获取本地JSON对象的操作在元数据对象的业务逻辑中没有对应的合理实现或者不需要这样的操作 @Override public JSONObject getLocalJSONFromContent() { throw new IllegalAccessError("MetaData:getLocalJSONFromContent should not be called"); } - +// 重写父类的方法,再次明确表示该方法不应该被调用,直接抛出 IllegalAccessError异常, + // 可能是因为获取同步操作相关信息在元数据对象的业务逻辑中没有对应的处理逻辑或者不需要这样的操作 @Override public int getSyncAction(Cursor c) { throw new IllegalAccessError("MetaData:getSyncAction should not be called"); diff --git a/src/Notes-master/src/net/micode/notes/gtask/data/Node.java b/src/Notes-master/src/net/micode/notes/gtask/data/Node.java index 63950e0..3d82677 100644 --- a/src/Notes-master/src/net/micode/notes/gtask/data/Node.java +++ b/src/Notes-master/src/net/micode/notes/gtask/data/Node.java @@ -13,83 +13,91 @@ * See the License for the specific language governing permissions and * limitations under the License. */ - +// 包声明,表明该类所属的包名,此处是net.micode.notes.gtask.data包 package net.micode.notes.gtask.data; import android.database.Cursor; import org.json.JSONObject; - +// 定义抽象类Node,通常作为节点相关数据结构或业务逻辑的抽象表示, +// 具体的节点类可能会继承该抽象类并实现其中的抽象方法 public abstract class Node { + // 定义同步操作的各种类型常量,用于表示不同的同步相关动作 + // 表示无同步操作 public static final int SYNC_ACTION_NONE = 0; - + // 表示向远程添加的同步操作 public static final int SYNC_ACTION_ADD_REMOTE = 1; - +// 表示在本地添加的同步操作 public static final int SYNC_ACTION_ADD_LOCAL = 2; - +// 表示从远程删除的同步操作 public static final int SYNC_ACTION_DEL_REMOTE = 3; - + // 表示从本地删除的同步操作 public static final int SYNC_ACTION_DEL_LOCAL = 4; - + // 表示更新远程的同步操作 public static final int SYNC_ACTION_UPDATE_REMOTE = 5; - +// 表示更新本地的同步操作 public static final int SYNC_ACTION_UPDATE_LOCAL = 6; - + // 表示更新冲突的同步操作,可能在同步过程中出现两端数据不一致等冲突情况时用到 public static final int SYNC_ACTION_UPDATE_CONFLICT = 7; - + // 表示同步操作出现错误的情况 public static final int SYNC_ACTION_ERROR = 8; - +// 用于存储节点的全局唯一标识符(可能是对应任务等的唯一标识),初始化为null private String mGid; - +// 存储节点的名称,初始化为空字符串 private String mName; - + // 记录节点最后一次被修改的时间戳,初始化为0 private long mLastModified; - +// 标记该节点是否已被删除,初始化为false private boolean mDeleted; - +// 默认构造函数,用于初始化Node对象的各个属性的初始值 public Node() { mGid = null; mName = ""; mLastModified = 0; mDeleted = false; } - + // 抽象方法,用于获取创建动作对应的JSONObject,具体的创建动作由actionId参数指定, + // 不同的子类需要根据自身业务逻辑实现该方法来构造相应的创建动作数据 public abstract JSONObject getCreateAction(int actionId); - +// 抽象方法,用于获取更新动作对应的JSONObject,根据传入的actionId来确定具体的更新动作, + // 子类需实现该方法以按照实际情况生成合适的更新动作相关的JSON数据 public abstract JSONObject getUpdateAction(int actionId); - +// 抽象方法,根据远程传来的JSONObject数据设置节点的内容, + // 具体的设置逻辑由继承该抽象类的子类来实现,以适配不同类型节点的数据解析和设置 public abstract void setContentByRemoteJSON(JSONObject js); - +// 抽象方法,根据本地的JSONObject数据设置节点的内容,同样子类要实现该方法来处理本地数据到节点内容的设置 public abstract void setContentByLocalJSON(JSONObject js); - +// 抽象方法,从节点当前内容获取对应的本地JSON表示形式, + // 子类需根据自身保存的数据结构等情况将节点内容转换为合适的JSONObject返回 public abstract JSONObject getLocalJSONFromContent(); - + // 抽象方法,根据给定的Cursor(通常用于从数据库查询结果中获取数据)来确定节点的同步操作类型, + // 子类要实现该方法以解析Cursor中的数据从而判断出对应的同步动作 public abstract int getSyncAction(Cursor c); - + // 设置节点的全局唯一标识符(Gid)的方法 public void setGid(String gid) { this.mGid = gid; - } + }// 设置节点名称的方法 public void setName(String name) { this.mName = name; } - +// 设置节点最后一次修改时间戳的方法 public void setLastModified(long lastModified) { this.mLastModified = lastModified; } - +// 设置节点是否已删除的标记的方法 public void setDeleted(boolean deleted) { this.mDeleted = deleted; } - + // 获取节点的全局唯一标识符(Gid)的方法 public String getGid() { return this.mGid; } - + // 获取节点名称的方法 public String getName() { return this.mName; } - +// 获取节点最后一次修改时间戳的方法 public long getLastModified() { return this.mLastModified; } diff --git a/src/Notes-master/src/net/micode/notes/gtask/data/SqlData.java b/src/Notes-master/src/net/micode/notes/gtask/data/SqlData.java index d3ec3be..69405c1 100644 --- a/src/Notes-master/src/net/micode/notes/gtask/data/SqlData.java +++ b/src/Notes-master/src/net/micode/notes/gtask/data/SqlData.java @@ -13,7 +13,7 @@ * See the License for the specific language governing permissions and * limitations under the License. */ - +// 包声明,表明该类所属的包名为net.micode.notes.gtask.data package net.micode.notes.gtask.data; import android.content.ContentResolver; @@ -33,45 +33,48 @@ import net.micode.notes.gtask.exception.ActionFailureException; import org.json.JSONException; import org.json.JSONObject; - +// SqlData类,主要用于处理与数据库相关的数据操作,可能涉及到将数据在数据库和JSON格式之间进行转换、保存等功能 public class SqlData { + // 用于日志记录的标签,取当前类的简单名称 private static final String TAG = SqlData.class.getSimpleName(); - +// 表示无效的ID值,通常用于初始化或者标识某个非法的ID状态 private static final int INVALID_ID = -99999; - + // 定义一个字符串数组,用于指定从数据库查询数据时的投影(即要查询的列),包含了数据相关的多个列名 public static final String[] PROJECTION_DATA = new String[] { DataColumns.ID, DataColumns.MIME_TYPE, DataColumns.CONTENT, DataColumns.DATA1, DataColumns.DATA3 }; - +// 定义常量,表示在投影数组中ID列对应的索引位置,方便后续从查询结果的Cursor中获取对应列的数据 public static final int DATA_ID_COLUMN = 0; - + // 定义常量,表示在投影数组中MIME_TYPE列对应的索引位置 public static final int DATA_MIME_TYPE_COLUMN = 1; - +// 定义常量,表示在投影数组中CONTENT列对应的索引位置 public static final int DATA_CONTENT_COLUMN = 2; - +// 定义常量,表示在投影数组中DATA1列对应的索引位置 public static final int DATA_CONTENT_DATA_1_COLUMN = 3; - +// 定义常量,表示在投影数组中DATA3列对应的索引位置 public static final int DATA_CONTENT_DATA_3_COLUMN = 4; - +// 用于与内容提供器进行交互,以便执行数据库相关的操作,如查询、插入、更新等 private ContentResolver mContentResolver; - + // 标记当前操作是否是创建新数据的操作,初始化为true private boolean mIsCreate; - +// 存储数据对应的ID,初始化为无效ID值 private long mDataId; - +// 存储数据的MIME类型,初始化为默认的Note类型(可能对应某种笔记的数据类型) private String mDataMimeType; - + // 存储数据的具体内容,初始化为空字符串 private String mDataContent; - +// 存储数据内容中相关的一个长整型数据(具体含义可能根据业务而定),初始化为0 private long mDataContentData1; - +// 存储数据内容中相关的一个字符串数据(具体含义可能根据业务而定),初始化为空字符串 private String mDataContentData3; - +// 用于存储要进行差异更新的数据值,即记录数据有变化的部分,初始化为一个新的ContentValues对象 private ContentValues mDiffDataValues; - + // 构造函数,用于创建一个新的SqlData对象,通常在创建新数据时使用 + // 接收一个Context上下文对象,用于获取ContentResolver来操作数据库 public SqlData(Context context) { + // 获取上下文对应的ContentResolver,以便后续进行数据库操作 mContentResolver = context.getContentResolver(); mIsCreate = true; mDataId = INVALID_ID; @@ -81,14 +84,15 @@ public class SqlData { mDataContentData3 = ""; mDiffDataValues = new ContentValues(); } - +// 另一个构造函数,用于根据已有的Cursor(通常是从数据库查询返回的结果集)来创建SqlData对象,常用于读取已有数据 + // 接收一个Context上下文对象和一个Cursor对象,用于初始化该对象的各个属性 public SqlData(Context context, Cursor c) { mContentResolver = context.getContentResolver(); mIsCreate = false; loadFromCursor(c); mDiffDataValues = new ContentValues(); } - + // 私有方法,用于从给定的Cursor中加载数据到当前对象的各个属性中,根据之前定义的列索引常量来获取对应列的数据 private void loadFromCursor(Cursor c) { mDataId = c.getLong(DATA_ID_COLUMN); mDataMimeType = c.getString(DATA_MIME_TYPE_COLUMN); @@ -96,40 +100,47 @@ public class SqlData { mDataContentData1 = c.getLong(DATA_CONTENT_DATA_1_COLUMN); mDataContentData3 = c.getString(DATA_CONTENT_DATA_3_COLUMN); } - + // 用于根据传入的JSONObject来设置当前对象的数据内容,比较传入数据与当前已有数据的差异, + // 将有差异的部分放入mDiffDataValues中,以便后续进行更新操作 public void setContent(JSONObject js) throws JSONException { + // 获取JSON对象中ID字段的值,如果不存在则使用无效ID值 long dataId = js.has(DataColumns.ID) ? js.getLong(DataColumns.ID) : INVALID_ID; + // 如果是创建操作或者当前对象的ID与传入JSON中的ID不同,则将新的ID放入差异数据值中 if (mIsCreate || mDataId != dataId) { mDiffDataValues.put(DataColumns.ID, dataId); } mDataId = dataId; - + // 获取JSON对象中MIME_TYPE字段的值,如果不存在则使用默认的Note类型 String dataMimeType = js.has(DataColumns.MIME_TYPE) ? js.getString(DataColumns.MIME_TYPE) : DataConstants.NOTE; + // 如果是创建操作或者当前对象的MIME_TYPE与传入JSON中的不同,则将新的MIME_TYPE放入差异数据值中 if (mIsCreate || !mDataMimeType.equals(dataMimeType)) { mDiffDataValues.put(DataColumns.MIME_TYPE, dataMimeType); - } + } // 获取JSON对象中CONTENT字段的值,如果不存在则使用空字符串 mDataMimeType = dataMimeType; String dataContent = js.has(DataColumns.CONTENT) ? js.getString(DataColumns.CONTENT) : ""; + // 如果是创建操作或者当前对象的CONTENT与传入JSON中的不同,则将新的CONTENT放入差异数据值中 if (mIsCreate || !mDataContent.equals(dataContent)) { mDiffDataValues.put(DataColumns.CONTENT, dataContent); } mDataContent = dataContent; - +// 获取JSON对象中DATA1字段的值,如果不存在则使用0 long dataContentData1 = js.has(DataColumns.DATA1) ? js.getLong(DataColumns.DATA1) : 0; + // 如果是创建操作或者当前对象的DATA1值与传入JSON中的不同,则将新的DATA1放入差异数据值中 if (mIsCreate || mDataContentData1 != dataContentData1) { mDiffDataValues.put(DataColumns.DATA1, dataContentData1); - } + } // 获取JSON对象中DATA3字段的值,如果不存在则使用空字符串 mDataContentData1 = dataContentData1; - + // 如果是创建操作或者当前对象的DATA3与传入JSON中的不同,则将新的DATA3放入差异数据值中 String dataContentData3 = js.has(DataColumns.DATA3) ? js.getString(DataColumns.DATA3) : ""; if (mIsCreate || !mDataContentData3.equals(dataContentData3)) { mDiffDataValues.put(DataColumns.DATA3, dataContentData3); } mDataContentData3 = dataContentData3; } - + // 用于获取当前对象的数据内容,并将其转换为JSONObject格式返回, + // 如果当前操作是创建操作(还未在数据库中创建该数据),则记录错误日志并返回null public JSONObject getContent() throws JSONException { if (mIsCreate) { Log.e(TAG, "it seems that we haven't created this in database yet"); @@ -143,46 +154,49 @@ public class SqlData { js.put(DataColumns.DATA3, mDataContentData3); return js; } - +// 用于将当前对象的数据提交到数据库中,根据是创建操作还是更新操作执行不同的逻辑 public void commit(long noteId, boolean validateVersion, long version) { if (mIsCreate) { + // 如果是创建操作,且当前数据ID是无效ID且差异数据值中包含ID字段,则移除该ID字段(可能有默认生成机制) if (mDataId == INVALID_ID && mDiffDataValues.containsKey(DataColumns.ID)) { mDiffDataValues.remove(DataColumns.ID); } - + // 将关联的笔记ID添加到差异数据值中 mDiffDataValues.put(DataColumns.NOTE_ID, noteId); + // 使用ContentResolver插入数据到指定的数据库URI,并获取插入后生成的URI Uri uri = mContentResolver.insert(Notes.CONTENT_DATA_URI, mDiffDataValues); try { + // 尝试从插入后的URI中获取生成的ID值,如果解析失败则记录错误日志并抛出异常 mDataId = Long.valueOf(uri.getPathSegments().get(1)); } catch (NumberFormatException e) { Log.e(TAG, "Get note id error :" + e.toString()); throw new ActionFailureException("create note failed"); } - } else { + } else { // 如果是更新操作,且差异数据值中有数据需要更新 if (mDiffDataValues.size() > 0) { int result = 0; - if (!validateVersion) { + if (!validateVersion) {// 如果不需要验证版本,则直接更新指定ID的数据 result = mContentResolver.update(ContentUris.withAppendedId( Notes.CONTENT_DATA_URI, mDataId), mDiffDataValues, null, null); - } else { + } else {// 如果需要验证版本,则根据指定的版本条件更新数据,通过SQL语句的条件筛选来确保更新的准确性 result = mContentResolver.update(ContentUris.withAppendedId( Notes.CONTENT_DATA_URI, mDataId), mDiffDataValues, " ? in (SELECT " + NoteColumns.ID + " FROM " + TABLE.NOTE + " WHERE " + NoteColumns.VERSION + "=?)", new String[] { String.valueOf(noteId), String.valueOf(version) }); - } + } // 如果更新结果为0(即没有数据被更新),记录警告日志,提示可能用户在同步时更新了笔记 if (result == 0) { Log.w(TAG, "there is no update. maybe user updates note when syncing"); } } } - + // 清空差异数据值,准备下一次的数据操作 mDiffDataValues.clear(); mIsCreate = false; } - + // 获取当前数据对象的ID值的方法 public long getId() { return mDataId; } diff --git a/src/Notes-master/src/net/micode/notes/gtask/data/SqlNote.java b/src/Notes-master/src/net/micode/notes/gtask/data/SqlNote.java index 79a4095..d0a9c51 100644 --- a/src/Notes-master/src/net/micode/notes/gtask/data/SqlNote.java +++ b/src/Notes-master/src/net/micode/notes/gtask/data/SqlNote.java @@ -13,7 +13,7 @@ * See the License for the specific language governing permissions and * limitations under the License. */ - +// 包声明,表明该类所属的包名为net.micode.notes.gtask.data package net.micode.notes.gtask.data; import android.appwidget.AppWidgetManager; @@ -37,12 +37,12 @@ import org.json.JSONObject; import java.util.ArrayList; - -public class SqlNote { +// SqlNote类,主要用于处理与笔记相关的数据操作,包括从数据库读取笔记信息、设置笔记内容、将笔记内容转换为JSON格式以及将笔记数据提交到数据库等功能 +public class SqlNote {// 用于日志记录的标签,取当前类的简单名称 private static final String TAG = SqlNote.class.getSimpleName(); - + // 表示无效的ID值,通常用于初始化或者标识某个非法的ID状态 private static final int INVALID_ID = -99999; - +// 定义一个字符串数组,用于指定从数据库查询笔记数据时的投影(即要查询的列),包含了笔记相关的多个列名 public static final String[] PROJECTION_NOTE = new String[] { NoteColumns.ID, NoteColumns.ALERTED_DATE, NoteColumns.BG_COLOR_ID, NoteColumns.CREATED_DATE, NoteColumns.HAS_ATTACHMENT, NoteColumns.MODIFIED_DATE, @@ -51,77 +51,80 @@ public class SqlNote { NoteColumns.LOCAL_MODIFIED, NoteColumns.ORIGIN_PARENT_ID, NoteColumns.GTASK_ID, NoteColumns.VERSION }; - +// 定义常量,表示在投影数组中ID列对应的索引位置,方便后续从查询结果的Cursor中获取对应列的数据 public static final int ID_COLUMN = 0; - + // 定义常量,表示在投影数组中ALERTED_DATE列对应的索引位置 public static final int ALERTED_DATE_COLUMN = 1; - + // 定义常量,表示在投影数组中BG_COLOR_ID列对应的索引位置 public static final int BG_COLOR_ID_COLUMN = 2; - +// 定义常量,表示在投影数组中CREATED_DATE列对应的索引位置 public static final int CREATED_DATE_COLUMN = 3; - +// 定义常量,表示在投影数组中HAS_ATTACHMENT列对应的索引位置 public static final int HAS_ATTACHMENT_COLUMN = 4; - + // 定义常量,表示在投影数组中MODIFIED_DATE列对应的索引位置 public static final int MODIFIED_DATE_COLUMN = 5; - + // 定义常量,表示在投影数组中NOTES_COUNT列对应的索引位置 public static final int NOTES_COUNT_COLUMN = 6; - + // 定义常量,表示在投影数组中PARENT_ID列对应的索引位置 public static final int PARENT_ID_COLUMN = 7; - + // 定义常量,表示在投影数组中SNIPPET列对应的索引位置 public static final int SNIPPET_COLUMN = 8; - + // 定义常量,表示在投影数组中TYPE列对应的索引位置 public static final int TYPE_COLUMN = 9; - +// 定义常量,表示在投影数组中WIDGET_ID列对应的索引位置 public static final int WIDGET_ID_COLUMN = 10; - +// 定义常量,表示在投影数组中WIDGET_TYPE列对应的索引位置 public static final int WIDGET_TYPE_COLUMN = 11; - +// 定义常量,表示在投影数组中SYNC_ID列对应的索引位置 public static final int SYNC_ID_COLUMN = 12; - +// 定义常量,表示在投影数组中LOCAL_MODIFIED列对应的索引位置 public static final int LOCAL_MODIFIED_COLUMN = 13; - +// 定义常量,表示在投影数组中ORIGIN_PARENT_ID列对应的索引位置 public static final int ORIGIN_PARENT_ID_COLUMN = 14; - +// 定义常量,表示在投影数组中GTASK_ID列对应的索引位置 public static final int GTASK_ID_COLUMN = 15; - + // 定义常量,表示在投影数组中VERSION列对应的索引位置 public static final int VERSION_COLUMN = 16; - +// 上下文对象,用于获取系统资源、执行与应用上下文相关的操作等 private Context mContext; - + // 用于与内容提供器进行交互,以便执行数据库相关的操作,如查询、插入、更新等 private ContentResolver mContentResolver; - + // 标记当前操作是否是创建新笔记的操作,初始化为true private boolean mIsCreate; - + // 存储笔记的ID,初始化为无效ID值 private long mId; - +// 存储笔记的提醒日期(时间戳相关,具体含义根据业务而定),初始化为0 private long mAlertDate; - +// 存储笔记的背景颜色ID,初始化为通过ResourceParser获取的默认背景颜色ID private int mBgColorId; - +// 存储笔记的创建日期(时间戳),初始化为当前系统时间 private long mCreatedDate; - + // 存储笔记是否有附件的标识,0表示没有,初始化为0 private int mHasAttachment; - +// 存储笔记的最后修改日期(时间戳),初始化为当前系统时间 private long mModifiedDate; - + // 存储笔记的父级ID(可能用于表示笔记的层级关系等),初始化为0 private long mParentId; - +// 存储笔记的摘要信息(简短描述等),初始化为空字符串 private String mSnippet; - + // 存储笔记的类型,初始化为普通笔记类型(Notes.TYPE_NOTE) private int mType; - +// 存储笔记关联的桌面小部件ID,初始化为无效的小部件ID private int mWidgetId; - + // 存储笔记关联的桌面小部件类型,初始化为无效的小部件类型(Notes.TYPE_WIDGET_INVALIDE) private int mWidgetType; - +// 存储笔记的原始父级ID(具体用途可能与笔记的来源、历史关系等相关),初始化为0 private long mOriginParent; - +// 存储笔记的版本号,初始化为0 private long mVersion; +// 用于存储要进行差异更新的笔记数据值,即记录笔记数据有变化的部分,初始化为一个新的ContentValues对象 private ContentValues mDiffNoteValues; - +// 存储与该笔记相关的数据列表(可能是笔记包含的多个具体数据项等,每个数据项用SqlData类表示) + private ArrayList mDataList; - + // 构造函数,用于创建一个新的SqlNote对象,通常在创建新笔记时使用 + // 接收一个Context上下文对象,用于获取ContentResolver等资源来操作数据库以及进行其他相关操作 public SqlNote(Context context) { mContext = context; mContentResolver = context.getContentResolver(); @@ -142,7 +145,8 @@ public class SqlNote { mDiffNoteValues = new ContentValues(); mDataList = new ArrayList(); } - +// 构造函数,用于根据已有的Cursor(通常是从数据库查询返回的结果集)来创建SqlNote对象,常用于读取已有笔记数据 + // 接收一个Context上下文对象和一个Cursor对象,用于初始化该对象的各个属性 public SqlNote(Context context, Cursor c) { mContext = context; mContentResolver = context.getContentResolver(); @@ -153,7 +157,8 @@ public class SqlNote { loadDataContent(); mDiffNoteValues = new ContentValues(); } - +// 构造函数,用于根据指定的笔记ID从数据库中加载笔记数据来创建SqlNote对象 + // 接收一个Context上下文对象和一个笔记ID(long类型),用于初始化该对象的各个属性 public SqlNote(Context context, long id) { mContext = context; mContentResolver = context.getContentResolver(); @@ -165,10 +170,11 @@ public class SqlNote { mDiffNoteValues = new ContentValues(); } - +// 私有方法,根据给定的笔记ID从数据库中查询并加载笔记数据到当前对象的各个属性中,通过调用另一个loadFromCursor方法实现具体的属性赋值 + private void loadFromCursor(long id) { Cursor c = null; - try { + try {// 使用ContentResolver根据指定的URI、查询条件(通过ID匹配)查询笔记数据 c = mContentResolver.query(Notes.CONTENT_NOTE_URI, PROJECTION_NOTE, "(_id=?)", new String[] { String.valueOf(id) @@ -184,7 +190,8 @@ public class SqlNote { c.close(); } } - + // 私有方法,用于从给定的Cursor中加载笔记数据到当前对象的各个属性中,根据之前定义的列索引常量来获取对应列的数据 + private void loadFromCursor(Cursor c) { mId = c.getLong(ID_COLUMN); mAlertDate = c.getLong(ALERTED_DATE_COLUMN); @@ -199,11 +206,13 @@ public class SqlNote { mWidgetType = c.getInt(WIDGET_TYPE_COLUMN); mVersion = c.getLong(VERSION_COLUMN); } - +// 私有方法,用于加载与当前笔记相关的数据内容(具体数据项,用SqlData类表示),通过查询数据库并将结果封装到SqlData对象中,添加到mDataList列表里 + private void loadDataContent() { Cursor c = null; mDataList.clear(); - try { + try { + // 使用ContentResolver根据指定的URI、查询条件(通过笔记ID匹配)查询与笔记相关的数据 c = mContentResolver.query(Notes.CONTENT_DATA_URI, SqlData.PROJECTION_DATA, "(note_id=?)", new String[] { String.valueOf(mId) @@ -225,6 +234,8 @@ public class SqlNote { c.close(); } } +// 用于根据传入的JSONObject来设置当前笔记对象的数据内容,根据笔记类型(如普通笔记、文件夹等)进行不同的处理, + // 将有差异的部分放入mDiffNoteValues中,以便后续进行更新操作,返回设置是否成功的布尔值 public boolean setContent(JSONObject js) { try { @@ -232,7 +243,7 @@ public class SqlNote { if (note.getInt(NoteColumns.TYPE) == Notes.TYPE_SYSTEM) { Log.w(TAG, "cannot set system folder"); } else if (note.getInt(NoteColumns.TYPE) == Notes.TYPE_FOLDER) { - // for folder we can only update the snnipet and type + // for folder we can only update the snnipet and type// 如果是文件夹类型,只能更新摘要和类型信息 String snippet = note.has(NoteColumns.SNIPPET) ? note .getString(NoteColumns.SNIPPET) : ""; if (mIsCreate || !mSnippet.equals(snippet)) { @@ -247,6 +258,7 @@ public class SqlNote { } mType = type; } else if (note.getInt(NoteColumns.TYPE) == Notes.TYPE_NOTE) { + // 如果是普通笔记类型 JSONArray dataArray = js.getJSONArray(GTaskStringUtils.META_HEAD_DATA); long id = note.has(NoteColumns.ID) ? note.getLong(NoteColumns.ID) : INVALID_ID; if (mIsCreate || mId != id) { @@ -330,24 +342,24 @@ public class SqlNote { mDiffNoteValues.put(NoteColumns.ORIGIN_PARENT_ID, originParent); } mOriginParent = originParent; - + // 遍历JSON数组中的每个数据项(对应笔记包含的具体数据内容) for (int i = 0; i < dataArray.length(); i++) { JSONObject data = dataArray.getJSONObject(i); - SqlData sqlData = null; + SqlData sqlData = null; // 如果数据项中有ID字段,尝试在已有的数据列表中查找对应ID的数据对象 if (data.has(DataColumns.ID)) { long dataId = data.getLong(DataColumns.ID); for (SqlData temp : mDataList) { if (dataId == temp.getId()) { sqlData = temp; } - } + }// 如果未找到,则创建一个新的SqlData对象,并添加到数据列表中 } if (sqlData == null) { sqlData = new SqlData(mContext); mDataList.add(sqlData); } - + // 调用SqlData对象的方法设置其具体内容 sqlData.setContent(data); } } @@ -358,7 +370,8 @@ public class SqlNote { } return true; } - + // 用于获取当前笔记对象的数据内容,并将其转换为JSONObject格式返回, + // 如果当前操作是创建操作(还未在数据库中创建该笔记),则记录错误日志并返回null public JSONObject getContent() { try { JSONObject js = new JSONObject(); @@ -406,46 +419,47 @@ public class SqlNote { } return null; } - +// 设置笔记的父级ID的方法,同时将该变化记录到差异更新数据值中,以便后续更新数据库时使用 public void setParentId(long id) { mParentId = id; mDiffNoteValues.put(NoteColumns.PARENT_ID, id); } - + // 设置笔记的Gtask ID(具体含义可能与相关任务关联等有关)的方法,将该值记录到差异更新数据值中 public void setGtaskId(String gid) { mDiffNoteValues.put(NoteColumns.GTASK_ID, gid); } - + // 设置笔记的同步ID的方法,将该值记录到差异更新数据值中 public void setSyncId(long syncId) { mDiffNoteValues.put(NoteColumns.SYNC_ID, syncId); } - + // 重置笔记的本地修改标记(将其设置为0)的方法,通过更新差异数据值来实现 public void resetLocalModified() { mDiffNoteValues.put(NoteColumns.LOCAL_MODIFIED, 0); } - + // 获取笔记ID的方法 public long getId() { return mId; } - +// 获取笔记父级ID的方法 public long getParentId() { return mParentId; } - + // 获取笔记摘要信息的方法 public String getSnippet() { return mSnippet; } - + // 判断笔记是否为普通笔记类型的方法,返回布尔值 public boolean isNoteType() { return mType == Notes.TYPE_NOTE; } - + // 用于将当前笔记对象的数据提交到数据库中,根据是创建操作还是更新操作执行不同的逻辑, + // 同时也会处理与之关联的数据(通过调用SqlData的commit方法)的提交操作 public void commit(boolean validateVersion) { - if (mIsCreate) { + if (mIsCreate) {// 如果是创建操作,且当前笔记ID是无效ID且差异数据值中包含ID字段,则移除该ID字段(可能有默认生成机制) if (mId == INVALID_ID && mDiffNoteValues.containsKey(NoteColumns.ID)) { mDiffNoteValues.remove(NoteColumns.ID); } - + // 使用ContentResolver插入笔记数据到指定的数据库URI,并获取插入后生成的URI Uri uri = mContentResolver.insert(Notes.CONTENT_NOTE_URI, mDiffNoteValues); try { mId = Long.valueOf(uri.getPathSegments().get(1)); @@ -458,18 +472,19 @@ public class SqlNote { } if (mType == Notes.TYPE_NOTE) { + // 如果是普通笔记类型,遍历与之关联的数据列表,调用每个SqlData对象的commit方法提交数据 for (SqlData sqlData : mDataList) { sqlData.commit(mId, false, -1); } } - } else { + } else {// 如果是更新操作 if (mId <= 0 && mId != Notes.ID_ROOT_FOLDER && mId != Notes.ID_CALL_RECORD_FOLDER) { Log.e(TAG, "No such note"); throw new IllegalStateException("Try to update note with invalid id"); } if (mDiffNoteValues.size() > 0) { mVersion ++; - int result = 0; + int result = 0;// 根据是否验证版本来决定更新数据库的条件 if (!validateVersion) { result = mContentResolver.update(Notes.CONTENT_NOTE_URI, mDiffNoteValues, "(" + NoteColumns.ID + "=?)", new String[] { @@ -493,12 +508,12 @@ public class SqlNote { } } } - + // 重新从数据库加载笔记数据,刷新本地信息(通过调用相关加载方法) // refresh local info loadFromCursor(mId); if (mType == Notes.TYPE_NOTE) loadDataContent(); - +// 清空差异数据值,准备下一次的数据操作 mDiffNoteValues.clear(); mIsCreate = false; } diff --git a/src/Notes-master/src/net/micode/notes/gtask/data/Task.java b/src/Notes-master/src/net/micode/notes/gtask/data/Task.java index 6a19454..556d5cc 100644 --- a/src/Notes-master/src/net/micode/notes/gtask/data/Task.java +++ b/src/Notes-master/src/net/micode/notes/gtask/data/Task.java @@ -30,21 +30,22 @@ import net.micode.notes.tool.GTaskStringUtils; import org.json.JSONArray; import org.json.JSONException; import org.json.JSONObject; +// Task类继承自抽象类Node,代表一个任务相关的实体,包含任务自身的各种属性以及与任务操作相关的方法, +// 例如创建、更新任务的JSON表示,从JSON数据设置任务内容等功能,用于处理任务相关的数据逻辑 - -public class Task extends Node { +public class Task extends Node { // 用于日志记录的标签,取当前类的简单名称 private static final String TAG = Task.class.getSimpleName(); - +// 标记任务是否已完成,初始化为false private boolean mCompleted; - + // 存储任务的备注信息(可能是对任务的详细描述等),初始化为null private String mNotes; - +// 存储任务的元信息(以JSONObject形式,具体内容结构可能根据业务而定),初始化为null private JSONObject mMetaInfo; - + // 指向当前任务的前一个兄弟任务(在任务列表等场景下用于表示顺序关系),初始化为null private Task mPriorSibling; - + // 指向当前任务所属的父任务列表(表明任务的归属层级关系),初始化为null private TaskList mParent; - +// 构造函数,调用父类(Node类)的构造函数进行初始化,并对Task类特有的属性进行默认初始化 public Task() { super(); mCompleted = false; @@ -53,21 +54,24 @@ public class Task extends Node { mParent = null; mMetaInfo = null; } - + // 实现抽象类Node中定义的抽象方法,用于获取创建任务的操作对应的JSONObject,该JSON对象包含了创建任务所需的各种信息, + // 如操作类型、任务的基本属性(名称、备注等)、所属父任务等相关信息,按照特定的格式进行组装 public JSONObject getCreateAction(int actionId) { JSONObject js = new JSONObject(); - try { + try { // 设置操作类型为创建任务,使用预定义的字符串常量标识 + // action_type // action_type js.put(GTaskStringUtils.GTASK_JSON_ACTION_TYPE, GTaskStringUtils.GTASK_JSON_ACTION_TYPE_CREATE); - +// 设置操作的唯一标识符(具体业务中用于区分不同的创建操作实例) // action_id js.put(GTaskStringUtils.GTASK_JSON_ACTION_ID, actionId); - + // 设置任务在父任务列表中的索引位置(通过调用父任务列表的方法获取) // index js.put(GTaskStringUtils.GTASK_JSON_INDEX, mParent.getChildTaskIndex(this)); - + // 构建表示任务实体的详细信息的JSONObject,包含任务名称、创建者ID(此处设为"null",可能需根据实际情况调整)、 + // 实体类型(标识为任务类型)以及任务备注(如果存在)等信息 // entity_delta JSONObject entity = new JSONObject(); entity.put(GTaskStringUtils.GTASK_JSON_NAME, getName()); @@ -78,17 +82,17 @@ public class Task extends Node { entity.put(GTaskStringUtils.GTASK_JSON_NOTES, getNotes()); } js.put(GTaskStringUtils.GTASK_JSON_ENTITY_DELTA, entity); - + // 设置任务所属父任务的全局唯一标识符(Gid) // parent_id js.put(GTaskStringUtils.GTASK_JSON_PARENT_ID, mParent.getGid()); - + // 设置目标父任务的类型(此处设为组类型,可能与任务的分组、归属等概念相关,具体依业务而定) // dest_parent_type js.put(GTaskStringUtils.GTASK_JSON_DEST_PARENT_TYPE, GTaskStringUtils.GTASK_JSON_TYPE_GROUP); - + // 设置任务所属列表的全局唯一标识符(通常与父任务列表的Gid一致) // list_id js.put(GTaskStringUtils.GTASK_JSON_LIST_ID, mParent.getGid()); - +// 如果存在前一个兄弟任务,则设置其全局唯一标识符,用于表示任务的顺序关系 // prior_sibling_id if (mPriorSibling != null) { js.put(GTaskStringUtils.GTASK_JSON_PRIOR_SIBLING_ID, mPriorSibling.getGid()); @@ -102,21 +106,22 @@ public class Task extends Node { return js; } - +// 实现抽象类Node中定义的抽象方法,用于获取更新任务的操作对应的JSONObject,包含了更新任务所需的关键信息, + // 如操作类型、任务的唯一标识符、任务实体的更新内容(如名称、备注、是否删除等信息),按照特定格式组装JSON数据 public JSONObject getUpdateAction(int actionId) { JSONObject js = new JSONObject(); - try { + try {// 设置操作类型为更新任务,使用预定义的字符串常量标识 // action_type js.put(GTaskStringUtils.GTASK_JSON_ACTION_TYPE, GTaskStringUtils.GTASK_JSON_ACTION_TYPE_UPDATE); - + // 设置操作的唯一标识符 // action_id js.put(GTaskStringUtils.GTASK_JSON_ACTION_ID, actionId); - +// 设置任务的全局唯一标识符(用于定位要更新的具体任务) // id js.put(GTaskStringUtils.GTASK_JSON_ID, getGid()); - + // 构建表示任务实体更新内容的JSONObject,包含任务名称、任务备注(如果存在)以及任务是否已删除的标记等信息 // entity_delta JSONObject entity = new JSONObject(); entity.put(GTaskStringUtils.GTASK_JSON_NAME, getName()); @@ -134,35 +139,38 @@ public class Task extends Node { return js; } - +// 实现抽象类Node中定义的抽象方法,用于根据远程传来的JSONObject数据设置任务的内容, + // 从JSON对象中解析出任务的各个属性值(如ID、最后修改时间、名称、备注、是否删除、是否完成等)并设置到当前任务对象中, + // 如果解析过程出现JSONException异常,则记录错误日志并抛出相应异常 public void setContentByRemoteJSON(JSONObject js) { if (js != null) { - try { + try {// 从JSON对象中获取任务的全局唯一标识符(Gid)并设置到当前任务对象中 // id if (js.has(GTaskStringUtils.GTASK_JSON_ID)) { setGid(js.getString(GTaskStringUtils.GTASK_JSON_ID)); } - +// 从JSON对象中获取任务最后一次修改的时间戳并设置到当前任务对象中 // last_modified if (js.has(GTaskStringUtils.GTASK_JSON_LAST_MODIFIED)) { setLastModified(js.getLong(GTaskStringUtils.GTASK_JSON_LAST_MODIFIED)); } - + // 从JSON对象中获取任务的名称并设置到当前任务对象中 // name if (js.has(GTaskStringUtils.GTASK_JSON_NAME)) { setName(js.getString(GTaskStringUtils.GTASK_JSON_NAME)); } + // 从JSON对象中获取任务的备注信息并设置到当前任务对象中 // notes if (js.has(GTaskStringUtils.GTASK_JSON_NOTES)) { setNotes(js.getString(GTaskStringUtils.GTASK_JSON_NOTES)); } - +// 从JSON对象中获取任务是否已删除的标记并设置到当前任务对象中 // deleted if (js.has(GTaskStringUtils.GTASK_JSON_DELETED)) { setDeleted(js.getBoolean(GTaskStringUtils.GTASK_JSON_DELETED)); } - + // 从JSON对象中获取任务是否已完成的标记并设置到当前任务对象中 // completed if (js.has(GTaskStringUtils.GTASK_JSON_COMPLETED)) { setCompleted(js.getBoolean(GTaskStringUtils.GTASK_JSON_COMPLETED)); @@ -174,7 +182,10 @@ public class Task extends Node { } } } - + // 实现抽象类Node中定义的抽象方法,用于根据本地的JSONObject数据设置任务的内容, + // 首先进行一些基本的校验(判断JSON对象是否可用,是否包含必要的头部信息等),然后尝试从JSON数据中解析出笔记类型, + // 如果类型为普通笔记类型,则遍历数据数组,找到MIME类型为笔记类型的数据项,从中获取内容并设置为任务的名称, + // 如果解析过程出现JSONException异常,则记录错误日志 public void setContentByLocalJSON(JSONObject js) { if (js == null || !js.has(GTaskStringUtils.META_HEAD_NOTE) || !js.has(GTaskStringUtils.META_HEAD_DATA)) { @@ -203,11 +214,14 @@ public class Task extends Node { e.printStackTrace(); } } - +// 实现抽象类Node中定义的抽象方法,用于从任务当前内容获取对应的本地JSON表示形式, + // 根据任务的元信息是否存在分为两种情况处理:如果是新创建的任务(元信息为空),则按照一定格式构建包含任务名称的JSON对象; + // 如果是已同步过的任务(元信息不为空),则从元信息中提取相关数据并更新任务名称所在的数据项,最后返回构建好的JSON对象, + // 如果在构建JSON对象过程中出现JSONException异常,则记录错误日志并返回null public JSONObject getLocalJSONFromContent() { String name = getName(); try { - if (mMetaInfo == null) { + if (mMetaInfo == null) { // 新任务创建自网络(可能表示从远程创建后还未完全同步等情况) // new task created from web if (name == null) { Log.w(TAG, "the note seems to be an empty one"); @@ -224,7 +238,7 @@ public class Task extends Node { note.put(NoteColumns.TYPE, Notes.TYPE_NOTE); js.put(GTaskStringUtils.META_HEAD_NOTE, note); return js; - } else { + } else { // 已同步的任务(已经有过同步操作,存在相关元信息) // synced task JSONObject note = mMetaInfo.getJSONObject(GTaskStringUtils.META_HEAD_NOTE); JSONArray dataArray = mMetaInfo.getJSONArray(GTaskStringUtils.META_HEAD_DATA); @@ -246,7 +260,8 @@ public class Task extends Node { return null; } } - + // 用于设置任务的元信息,将传入的MetaData对象中的笔记信息(如果不为空且可转换为JSONObject)转换为JSONObject并赋值给mMetaInfo属性, + // 如果转换过程出现JSONException异常,则记录警告日志并将mMetaInfo设为null public void setMetaInfo(MetaData metaData) { if (metaData != null && metaData.getNotes() != null) { try { @@ -257,7 +272,9 @@ public class Task extends Node { } } } - + // 实现抽象类Node中定义的抽象方法,用于根据给定的Cursor(通常用于从数据库查询结果中获取数据)来确定任务的同步操作类型, + // 通过一系列的条件判断来分析任务在本地和远程数据之间的差异情况,从而确定是无同步操作、更新本地、更新远程、更新冲突还是出现错误等同步动作, + // 如果在判断过程中出现异常,则记录错误日志并返回表示错误的同步操作类型 public int getSyncAction(Cursor c) { try { JSONObject noteInfo = null; @@ -274,25 +291,30 @@ public class Task extends Node { Log.w(TAG, "remote note id seems to be deleted"); return SYNC_ACTION_UPDATE_LOCAL; } - +// 验证笔记的ID是否匹配(本地数据库中的ID与从元信息中获取的远程ID进行比较) // validate the note id now if (c.getLong(SqlNote.ID_COLUMN) != noteInfo.getLong(NoteColumns.ID)) { Log.w(TAG, "note id doesn't match"); return SYNC_ACTION_UPDATE_LOCAL; } - if (c.getInt(SqlNote.LOCAL_MODIFIED_COLUMN) == 0) { + if (c.getInt(SqlNote.LOCAL_MODIFIED_COLUMN) == 0) { + // 本地没有更新(通过判断本地修改标记是否为0来确定) // there is no local update if (c.getLong(SqlNote.SYNC_ID_COLUMN) == getLastModified()) { + // 两边都没有更新(本地同步ID与任务最后修改时间戳相同) // no update both side return SYNC_ACTION_NONE; } else { + // 应用远程到本地(本地没有更新但远程有更新,需将远程数据应用到本地) // apply remote to local return SYNC_ACTION_UPDATE_LOCAL; } } else { + // 验证任务的Gtask ID是否匹配(本地数据库中的Gtask ID与当前任务对象的Gid进行比较) // validate gtask id if (!c.getString(SqlNote.GTASK_ID_COLUMN).equals(getGid())) { + // 只有本地修改(本地修改了但远程没有更新,只需将本地修改同步到远程) Log.e(TAG, "gtask id doesn't match"); return SYNC_ACTION_ERROR; } @@ -310,36 +332,37 @@ public class Task extends Node { return SYNC_ACTION_ERROR; } - +// 判断任务是否值得保存的方法,根据任务的元信息是否存在、任务名称以及任务备注是否非空(去除空格后长度大于0)来综合判断, + // 如果满足上述条件之一,则认为任务值得保存,返回true,否则返回false public boolean isWorthSaving() { return mMetaInfo != null || (getName() != null && getName().trim().length() > 0) || (getNotes() != null && getNotes().trim().length() > 0); } - + // 设置任务是否完成的方法,用于更新任务的完成状态 public void setCompleted(boolean completed) { this.mCompleted = completed; } - +// 设置任务备注信息的方法,用于更新任务的备注内容 public void setNotes(String notes) { this.mNotes = notes; } - + // 设置任务前一个兄弟任务的方法,用于建立任务之间的顺序关联 public void setPriorSibling(Task priorSibling) { this.mPriorSibling = priorSibling; } - +// 设置任务所属父任务列表的方法,用于明确任务的层级归属关系 public void setParent(TaskList parent) { this.mParent = parent; } - + // 获取任务是否完成的方法,返回任务的完成状态(true表示已完成,false表示未完成) public boolean getCompleted() { return this.mCompleted; } - +// 获取任务备注信息的方法,返回任务的备注内容 public String getNotes() { return this.mNotes; } - + // 获取任务所属父任务列表的方法,返回指向当前任务所属父任务列表的引用 public Task getPriorSibling() { return this.mPriorSibling; } diff --git a/src/Notes-master/src/net/micode/notes/gtask/data/TaskList.java b/src/Notes-master/src/net/micode/notes/gtask/data/TaskList.java index 4ea21c5..62ca7b5 100644 --- a/src/Notes-master/src/net/micode/notes/gtask/data/TaskList.java +++ b/src/Notes-master/src/net/micode/notes/gtask/data/TaskList.java @@ -28,35 +28,41 @@ import org.json.JSONException; import org.json.JSONObject; import java.util.ArrayList; - +// TaskList类继承自抽象类Node,代表一个任务列表相关的实体,包含任务列表自身的各种属性以及与任务列表操作相关的方法, +// 例如创建、更新任务列表的JSON表示,从JSON数据设置任务列表内容,对任务列表中的子任务进行添加、删除、移动等操作的功能,用于处理任务列表相关的数据逻辑 public class TaskList extends Node { + // 用于日志记录的标签,取当前类的简单名称 private static final String TAG = TaskList.class.getSimpleName(); - + // 存储任务列表的索引(具体含义可能与任务列表在某个整体结构中的顺序、位置等相关,依业务而定),初始化为1 private int mIndex; - +// 存储该任务列表包含的所有子任务,以ArrayList形式存储,初始化为一个空的ArrayList private ArrayList mChildren; - +// 构造函数,调用父类(Node类)的构造函数进行初始化,并对TaskList类特有的属性进行默认初始化,创建一个空的任务列表对象并设置初始索引值 + public TaskList() { super(); mChildren = new ArrayList(); mIndex = 1; } - +// 实现抽象类Node中定义的抽象方法,用于获取创建任务列表的操作对应的JSONObject,该JSON对象包含了创建任务列表所需的各种信息, + // 如操作类型、操作的唯一标识符、任务列表的索引位置、任务列表实体的基本属性(名称、创建者ID、实体类型等),按照特定的格式进行组装 public JSONObject getCreateAction(int actionId) { JSONObject js = new JSONObject(); - try { + try { + // 设置操作类型为创建任务列表,使用预定义的字符串常量标识 // action_type js.put(GTaskStringUtils.GTASK_JSON_ACTION_TYPE, GTaskStringUtils.GTASK_JSON_ACTION_TYPE_CREATE); - + // 设置操作的唯一标识符(具体业务中用于区分不同的创建操作实例) // action_id js.put(GTaskStringUtils.GTASK_JSON_ACTION_ID, actionId); - +// 设置任务列表的索引位置 // index js.put(GTaskStringUtils.GTASK_JSON_INDEX, mIndex); - +// 构建表示任务列表实体的详细信息的JSONObject,包含任务列表名称、创建者ID(此处设为"null",可能需根据实际情况调整)、 + // 实体类型(标识为组类型,表示任务列表在整体结构中的类型为组)等信息 // entity_delta JSONObject entity = new JSONObject(); entity.put(GTaskStringUtils.GTASK_JSON_NAME, getName()); @@ -73,21 +79,24 @@ public class TaskList extends Node { return js; } +// 实现抽象类Node中定义的抽象方法,用于获取更新任务列表的操作对应的JSONObject,包含了更新任务列表所需的关键信息, + // 如操作类型、操作的唯一标识符、任务列表的唯一标识符、任务列表实体的更新内容(如名称、是否删除等信息),按照特定格式组装JSON数据 public JSONObject getUpdateAction(int actionId) { JSONObject js = new JSONObject(); try { + // 设置操作类型为更新任务列表,使用预定义的字符串常量标识 // action_type js.put(GTaskStringUtils.GTASK_JSON_ACTION_TYPE, GTaskStringUtils.GTASK_JSON_ACTION_TYPE_UPDATE); - + // 设置操作的唯一标识符 // action_id js.put(GTaskStringUtils.GTASK_JSON_ACTION_ID, actionId); - + // 设置任务列表的全局唯一标识符(用于定位要更新的具体任务列表) // id js.put(GTaskStringUtils.GTASK_JSON_ID, getGid()); - +// 构建表示任务列表实体更新内容的JSONObject,包含任务列表名称以及任务列表是否已删除的标记等信息 // entity_delta JSONObject entity = new JSONObject(); entity.put(GTaskStringUtils.GTASK_JSON_NAME, getName()); @@ -101,21 +110,24 @@ public class TaskList extends Node { } return js; - } + } // 实现抽象类Node中定义的抽象方法,用于根据远程传来的JSONObject数据设置任务列表的内容, + // 从JSON对象中解析出任务列表的各个属性值(如ID、最后修改时间、名称等)并设置到当前任务列表对象中, + // 如果解析过程出现JSONException异常,则记录错误日志并抛出相应异常 public void setContentByRemoteJSON(JSONObject js) { if (js != null) { try { + // 从JSON对象中获取任务列表的全局唯一标识符(Gid)并设置到当前任务列表对象中 // id if (js.has(GTaskStringUtils.GTASK_JSON_ID)) { setGid(js.getString(GTaskStringUtils.GTASK_JSON_ID)); } - +// 从JSON对象中获取任务列表最后一次修改的时间戳并设置到当前任务列表对象中 // last_modified if (js.has(GTaskStringUtils.GTASK_JSON_LAST_MODIFIED)) { setLastModified(js.getLong(GTaskStringUtils.GTASK_JSON_LAST_MODIFIED)); } - +// 从JSON对象中获取任务列表的名称并设置到当前任务列表对象中 // name if (js.has(GTaskStringUtils.GTASK_JSON_NAME)) { setName(js.getString(GTaskStringUtils.GTASK_JSON_NAME)); @@ -128,7 +140,12 @@ public class TaskList extends Node { } } } - + // 实现抽象类Node中定义的抽象方法,用于根据本地的JSONObject数据设置任务列表的内容, + // 首先进行一些基本的校验(判断JSON对象是否可用,是否包含必要的头部信息等),然后根据笔记类型进行不同的处理: + // 如果是文件夹类型,从JSON中获取摘要信息并设置任务列表名称(添加特定前缀); + // 如果是系统类型,根据特定的系统文件夹ID设置对应的名称; + // 如果类型不符合预期,则记录错误日志, + // 如果解析过程出现JSONException异常,则记录错误日志并打印异常堆栈信息 public void setContentByLocalJSON(JSONObject js) { if (js == null || !js.has(GTaskStringUtils.META_HEAD_NOTE)) { Log.w(TAG, "setContentByLocalJSON: nothing is avaiable"); @@ -156,7 +173,9 @@ public class TaskList extends Node { e.printStackTrace(); } } - + // 实现抽象类Node中定义的抽象方法,用于从任务列表当前内容获取对应的本地JSON表示形式, + // 按照特定格式构建包含任务列表名称和类型信息的JSON对象,根据名称是否包含特定前缀来判断并设置类型(文件夹类型或系统类型), + // 如果构建JSON对象过程中出现JSONException异常,则记录错误日志并返回null public JSONObject getLocalJSONFromContent() { try { JSONObject js = new JSONObject(); @@ -182,28 +201,37 @@ public class TaskList extends Node { return null; } } + // 实现抽象类Node中定义的抽象方法,根据给定的Cursor(通常用于从数据库查询结果中获取数据)来确定任务列表的同步操作类型, + // 通过判断本地是否有更新、任务列表的Gtask ID是否匹配以及两边的修改时间戳是否一致等条件,来确定是无同步操作、更新本地、更新远程还是出现错误等同步动作, + // 如果在判断过程中出现异常,则记录错误日志并返回表示错误的同步操作类型,对于文件夹冲突的情况,默认应用本地修改(返回更新远程操作类型) public int getSyncAction(Cursor c) { try { if (c.getInt(SqlNote.LOCAL_MODIFIED_COLUMN) == 0) { + // 本地没有更新(通过判断本地修改标记是否为0来确定) // there is no local update if (c.getLong(SqlNote.SYNC_ID_COLUMN) == getLastModified()) { + // 两边都没有更新(本地同步ID与任务列表最后修改时间戳相同) // no update both side return SYNC_ACTION_NONE; } else { + // 应用远程到本地(本地没有更新但远程有更新,需将远程数据应用到本地) // apply remote to local return SYNC_ACTION_UPDATE_LOCAL; } } else { + // 验证任务列表的Gtask ID是否匹配(本地数据库中的Gtask ID与当前任务列表对象的Gid进行比较) // validate gtask id if (!c.getString(SqlNote.GTASK_ID_COLUMN).equals(getGid())) { Log.e(TAG, "gtask id doesn't match"); return SYNC_ACTION_ERROR; } if (c.getLong(SqlNote.SYNC_ID_COLUMN) == getLastModified()) { + // 只有本地修改(本地修改了但远程没有更新,只需将本地修改同步到远程) // local modification only return SYNC_ACTION_UPDATE_REMOTE; } else { + // 对于文件夹冲突的情况,直接应用本地修改 // for folder conflicts, just apply local modification return SYNC_ACTION_UPDATE_REMOTE; } @@ -215,16 +243,19 @@ public class TaskList extends Node { return SYNC_ACTION_ERROR; } - +// 获取任务列表中子任务的数量的方法,直接返回存储子任务的ArrayList的大小 public int getChildTaskCount() { return mChildren.size(); } - + // 向任务列表中添加子任务的方法,将传入的任务对象添加到子任务列表中, + // 如果任务对象不为空且不在子任务列表中,则添加成功后,设置该任务的前一个兄弟任务(如果列表为空则为null,否则为列表中最后一个任务)以及父任务(当前任务列表), + // 返回添加操作是否成功的布尔值 public boolean addChildTask(Task task) { boolean ret = false; if (task != null && !mChildren.contains(task)) { ret = mChildren.add(task); if (ret) { + // 需要设置前一个兄弟任务和父任务 // need to set prior sibling and parent task.setPriorSibling(mChildren.isEmpty() ? null : mChildren .get(mChildren.size() - 1)); @@ -233,7 +264,11 @@ public class TaskList extends Node { } return ret; } - + // 在指定索引位置向任务列表中添加子任务的方法, + // 首先进行索引合法性校验,如果索引无效则记录错误日志并返回false; + // 然后判断任务是否已在列表中,如果不在且任务对象不为空,则将任务添加到指定索引位置, + // 接着更新任务列表中相关任务的前一个兄弟任务关系(设置新添加任务的前一个兄弟任务以及后续任务的前一个兄弟任务指向新添加任务), + // 返回添加操作是否成功的布尔值 public boolean addChildTask(Task task, int index) { if (index < 0 || index > mChildren.size()) { Log.e(TAG, "add child task: invalid index"); @@ -243,7 +278,7 @@ public class TaskList extends Node { int pos = mChildren.indexOf(task); if (task != null && pos == -1) { mChildren.add(index, task); - +// 更新任务列表 // update the task list Task preTask = null; Task afterTask = null; @@ -259,7 +294,10 @@ public class TaskList extends Node { return true; } - +// 从任务列表中移除指定子任务的方法, + // 首先查找任务在列表中的索引位置,如果存在则尝试移除该任务,移除成功后,重置该任务的前一个兄弟任务和父任务为null, + // 并且更新任务列表中后续任务的前一个兄弟任务关系(如果移除位置不是列表末尾), + // 返回移除操作是否成功的布尔值 public boolean removeChildTask(Task task) { boolean ret = false; int index = mChildren.indexOf(task); @@ -267,10 +305,11 @@ public class TaskList extends Node { ret = mChildren.remove(task); if (ret) { + // 重置前一个兄弟任务和父任务 // reset prior sibling and parent task.setPriorSibling(null); task.setParent(null); - + // 更新任务列表 // update the task list if (index != mChildren.size()) { mChildren.get(index).setPriorSibling( @@ -280,6 +319,9 @@ public class TaskList extends Node { } return ret; } + // 在任务列表中移动指定子任务到新的索引位置的方法, + // 首先进行索引合法性校验以及判断任务是否在列表中,如果校验不通过则记录错误日志并返回false; + // 如果任务当前位置和目标位置相同则直接返回true,否则先移除该任务再添加到新的索引位置,返回操作是否成功的布尔值 public boolean moveChildTask(Task task, int index) { @@ -298,6 +340,7 @@ public class TaskList extends Node { return true; return (removeChildTask(task) && addChildTask(task, index)); } + // 根据任务的全局唯一标识符(Gid)查找子任务的方法,遍历子任务列表,找到Gid匹配的任务并返回,如果没找到则返回null public Task findChildTaskByGid(String gid) { for (int i = 0; i < mChildren.size(); i++) { @@ -308,10 +351,12 @@ public class TaskList extends Node { } return null; } +// 获取指定子任务在任务列表中的索引位置的方法,通过调用ArrayList的indexOf方法来查找并返回任务在列表中的索引,若不存在则返回 -1 public int getChildTaskIndex(Task task) { return mChildren.indexOf(task); } + // 根据索引获取子任务的方法,先对索引进行合法性校验,如果索引无效则记录错误日志并返回null,否则返回对应索引位置的子任务 public Task getChildTaskByIndex(int index) { if (index < 0 || index >= mChildren.size()) { @@ -320,7 +365,8 @@ public class TaskList extends Node { } return mChildren.get(index); } - +// 根据任务的全局唯一标识符(Gid)获取子任务的方法(与findChildTaskByGid方法功能类似,可能是重复定义或者不同使用场景下的同名方法), + // 遍历子任务列表,找到Gid匹配的任务并返回,如果没找到则返回null public Task getChilTaskByGid(String gid) { for (Task task : mChildren) { if (task.getGid().equals(gid)) @@ -328,15 +374,16 @@ public class TaskList extends Node { } return null; } +// 获取整个子任务列表的方法,直接返回存储子任务的ArrayList对象,外部可通过该方法获取任务列表包含的所有子任务进行进一步操作 public ArrayList getChildTaskList() { return this.mChildren; } - + // 设置任务列表索引的方法,用于更新任务列表的索引值(可能用于改变其在相关结构中的顺序、位置等属性) public void setIndex(int index) { this.mIndex = index; } - + // 获取任务列表索引的方法,用于获取当前任务列表的索引值 public int getIndex() { return this.mIndex; } diff --git a/src/Notes-master/src/net/micode/notes/gtask/exception/ActionFailureException.java b/src/Notes-master/src/net/micode/notes/gtask/exception/ActionFailureException.java index 15504be..bc69af7 100644 --- a/src/Notes-master/src/net/micode/notes/gtask/exception/ActionFailureException.java +++ b/src/Notes-master/src/net/micode/notes/gtask/exception/ActionFailureException.java @@ -15,18 +15,25 @@ */ package net.micode.notes.gtask.exception; - +// ActionFailureException类继承自RuntimeException,意味着它是一个运行时异常,不需要在方法签名中显式声明抛出该异常(与受检异常相对) +// 此类用于表示在执行某个操作时发生了失败的情况,方便在代码中针对操作失败进行统一的异常处理和错误信息传递 public class ActionFailureException extends RuntimeException { + // 用于序列化版本控制的唯一标识符,在Java的序列化机制中使用,保证不同版本的类在序列化和反序列化时的兼容性 private static final long serialVersionUID = 4425249765923293627L; - + // 无参构造函数,调用父类(RuntimeException)的无参构造函数,用于创建一个默认的ActionFailureException实例, + // 通常在不需要传递具体错误信息时使用 public ActionFailureException() { super(); } +// 带有字符串参数的构造函数,接收一个表示错误信息的字符串参数,调用父类(RuntimeException)的对应构造函数, + // 用于创建一个带有特定错误信息的ActionFailureException实例,方便在出现操作失败时传递相应的错误提示内容 public ActionFailureException(String paramString) { super(paramString); } - +// 带有字符串参数和Throwable参数的构造函数,接收一个表示错误信息的字符串参数以及一个Throwable对象(通常是导致当前异常的底层异常), + // 调用父类(RuntimeException)的对应构造函数,用于创建一个既能传递自定义错误信息,又能关联底层引发异常原因的ActionFailureException实例, + // 方便在复杂的异常链场景中进行准确的异常追踪和错误分析 public ActionFailureException(String paramString, Throwable paramThrowable) { super(paramString, paramThrowable); } diff --git a/src/Notes-master/src/net/micode/notes/gtask/exception/NetworkFailureException.java b/src/Notes-master/src/net/micode/notes/gtask/exception/NetworkFailureException.java index b08cfb1..5a83b04 100644 --- a/src/Notes-master/src/net/micode/notes/gtask/exception/NetworkFailureException.java +++ b/src/Notes-master/src/net/micode/notes/gtask/exception/NetworkFailureException.java @@ -15,18 +15,29 @@ */ package net.micode.notes.gtask.exception; +// NetworkFailureException类继承自Exception,属于受检异常(Checked Exception),这意味着在方法中如果可能抛出该异常, +// 必须在方法签名中使用throws关键字显式声明,调用该方法的代码也需要进行相应的异常处理(try-catch块或者继续向上抛出)。 +// 此类专门用于表示在网络相关操作出现故障、失败时抛出的异常情况,方便对网络故障进行统一的异常处理和错误信息传递。 public class NetworkFailureException extends Exception { + // 用于序列化版本控制的唯一标识符,在Java的序列化机制中发挥作用。当类实现了Serializable接口(Exception类间接实现了该接口), + // 这个标识符可以确保不同版本的类在进行序列化和反序列化操作时能够正确匹配,避免因类结构变化等原因导致的兼容性问题。 private static final long serialVersionUID = 2107610287180234136L; - + // 无参构造函数,调用父类(Exception)的无参构造函数,用于创建一个默认的NetworkFailureException实例, + // 一般在不需要传递具体错误信息,仅表示网络出现故障这种通用情况时使用。 public NetworkFailureException() { super(); } - +// 带有字符串参数的构造函数,接收一个表示错误信息的字符串参数,调用父类(Exception)的对应构造函数, + // 这样就可以创建一个带有明确错误提示内容的NetworkFailureException实例,便于在网络操作失败时, + // 根据具体的失败原因传递相应有意义的错误信息给调用者或者进行日志记录等操作。 public NetworkFailureException(String paramString) { super(paramString); } - +// 带有字符串参数和Throwable参数的构造函数,接收一个表示错误信息的字符串参数以及一个Throwable对象(通常是导致当前网络故障异常的底层异常, + // 比如IOException等更具体的网络相关底层异常),调用父类(Exception)的对应构造函数, + // 此构造函数用于创建一个既能传递自定义的网络故障相关错误信息,又能关联底层引发异常原因的NetworkFailureException实例, + // 在复杂的网络异常处理场景中,方便进行准确的异常追踪、错误分析以及更完善的异常处理逻辑编写。 public NetworkFailureException(String paramString, Throwable paramThrowable) { super(paramString, paramThrowable); } diff --git a/src/Notes-master/src/net/micode/notes/gtask/remote/GTaskASyncTask.java b/src/Notes-master/src/net/micode/notes/gtask/remote/GTaskASyncTask.java index b3b61e7..4ad596f 100644 --- a/src/Notes-master/src/net/micode/notes/gtask/remote/GTaskASyncTask.java +++ b/src/Notes-master/src/net/micode/notes/gtask/remote/GTaskASyncTask.java @@ -27,24 +27,33 @@ import android.os.AsyncTask; import net.micode.notes.R; import net.micode.notes.ui.NotesListActivity; import net.micode.notes.ui.NotesPreferenceActivity; +// GTaskASyncTask类继承自AsyncTask,用于在后台线程执行一些耗时的操作(通常与远程任务同步相关),并能在操作过程中更新进度、在操作完成后处理结果, +// 同时通过接口回调的方式通知外部操作已完成,还涉及到与通知系统的交互来展示同步状态相关的通知信息 public class GTaskASyncTask extends AsyncTask { +// 定义一个静态的整型常量,作为同步相关通知的唯一标识符(ID),用于在通知系统中区分不同的通知,此处固定赋值为5234235 private static int GTASK_SYNC_NOTIFICATION_ID = 5234235; +// 定义一个内部接口OnCompleteListener,用于定义当异步任务完成时需要执行的回调方法,外部类实现该接口来处理任务完成后的逻辑 public interface OnCompleteListener { void onComplete(); } +// 存储上下文对象,用于获取系统服务、资源等,方便在类中进行各种与Android系统相关的操作,如创建通知、启动Activity等 private Context mContext; - +// 用于管理通知的显示、取消等操作,通过获取系统的通知服务来实例化,负责向用户展示同步进度、结果等相关通知信息 + private NotificationManager mNotifiManager; - +// 用于管理远程任务相关的操作(可能涉及与服务器交互、任务同步等具体逻辑,具体功能由GTaskManager类实现),通过单例模式获取实例 + private GTaskManager mTaskManager; - + // 存储实现了OnCompleteListener接口的对象引用,用于在异步任务完成时回调相应的方法,通知外部任务已结束 + private OnCompleteListener mOnCompleteListener; - +// 构造函数,接收上下文对象和OnCompleteListener接口实现对象作为参数,用于初始化类中的相关成员变量, + // 同时获取通知管理器实例,并获取GTaskManager的单例实例,为后续操作做准备 public GTaskASyncTask(Context context, OnCompleteListener listener) { mContext = context; mOnCompleteListener = listener; @@ -52,17 +61,21 @@ public class GTaskASyncTask extends AsyncTask { .getSystemService(Context.NOTIFICATION_SERVICE); mTaskManager = GTaskManager.getInstance(); } - + // 用于取消正在进行的同步操作的方法,通过调用GTaskManager中的取消同步方法来实现具体的取消逻辑,外部可调用此方法来中断同步过程 + public void cancelSync() { mTaskManager.cancelSync(); } - +// 用于发布进度信息的方法,接收一个字符串消息参数,将其包装成字符串数组后调用AsyncTask的publishProgress方法, + // 触发onProgressUpdate方法的执行,从而实现向UI线程传递进度信息,以便更新界面展示等相关操作 public void publishProgess(String message) { publishProgress(new String[] { message }); } - + // 私有方法,用于显示通知信息,根据传入的通知文本资源ID(tickerId)和通知具体内容(content)创建并配置一个Notification对象, + // 然后通过通知管理器将通知显示出来,设置了通知的图标、默认灯光效果、自动取消等属性,并根据不同的文本资源ID设置点击通知后的跳转意图(PendingIntent) + private void showNotification(int tickerId, String content) { Notification notification = new Notification(R.drawable.notification, mContext .getString(tickerId), System.currentTimeMillis()); @@ -81,14 +94,18 @@ public class GTaskASyncTask extends AsyncTask { pendingIntent); mNotifiManager.notify(GTASK_SYNC_NOTIFICATION_ID, notification); } - +// 在后台线程执行的方法,重写了AsyncTask的抽象方法,是异步任务的核心逻辑所在。 + // 首先发布一条表示正在登录进行同步的进度消息(包含同步账号名称),然后调用GTaskManager的sync方法进行实际的同步操作, + // 并返回同步结果(以整数形式表示不同的状态,如成功、网络错误等状态码),该结果将传递给onPostExecute方法进行后续处理 @Override protected Integer doInBackground(Void... unused) { publishProgess(mContext.getString(R.string.sync_progress_login, NotesPreferenceActivity .getSyncAccountName(mContext))); return mTaskManager.sync(mContext, this); } - +// 在UI线程执行的方法,用于接收并处理在后台线程通过publishProgress方法传递过来的进度信息, + // 调用showNotification方法展示同步进度通知,并且如果当前上下文是GTaskSyncService类型,还会发送广播(可能用于通知其他组件同步进度情况) + @Override protected void onProgressUpdate(String... progress) { showNotification(R.string.ticker_syncing, progress[0]); @@ -96,7 +113,11 @@ public class GTaskASyncTask extends AsyncTask { ((GTaskSyncService) mContext).sendBroadcast(progress[0]); } } - +// 在UI线程执行的方法,当异步任务执行完毕后(无论是正常完成还是出现异常结束),根据返回的结果状态码进行不同的处理: + // 如果结果是成功状态,展示成功同步的通知信息,并记录最后同步时间; + // 如果是网络错误状态、内部错误状态或同步取消状态,分别展示对应的错误通知信息; + // 最后,如果存在实现了OnCompleteListener接口的对象(即外部注册了任务完成回调),则通过开启一个新线程来执行回调方法,通知外部任务已完成 + @Override protected void onPostExecute(Integer result) { if (result == GTaskManager.STATE_SUCCESS) { diff --git a/src/Notes-master/src/net/micode/notes/gtask/remote/GTaskClient.java b/src/Notes-master/src/net/micode/notes/gtask/remote/GTaskClient.java index c67dfdf..ce10c61 100644 --- a/src/Notes-master/src/net/micode/notes/gtask/remote/GTaskClient.java +++ b/src/Notes-master/src/net/micode/notes/gtask/remote/GTaskClient.java @@ -59,37 +59,40 @@ import java.util.List; import java.util.zip.GZIPInputStream; import java.util.zip.Inflater; import java.util.zip.InflaterInputStream; - +// GTaskClient类用于与Google Tasks等相关服务进行交互,实现诸如登录、创建任务、创建任务列表、更新任务、获取任务列表等功能, +// 内部管理着与服务器通信的HttpClient对象、登录状态、账户信息等,是应用与远程任务服务交互的核心类之一 public class GTaskClient { + // 用于日志记录的标签,取当前类的简单名称 private static final String TAG = GTaskClient.class.getSimpleName(); + // Google Tasks服务的基础URL private static final String GTASK_URL = "https://mail.google.com/tasks/"; - +// 用于获取任务相关数据的URL(可能是获取初始数据等情况) private static final String GTASK_GET_URL = "https://mail.google.com/tasks/ig"; - + // 用于向服务器提交任务相关操作(如创建、更新等)的URL private static final String GTASK_POST_URL = "https://mail.google.com/tasks/r/ig"; - +// 单例模式,存储唯一的GTaskClient实例,初始化为null private static GTaskClient mInstance = null; - + // 用于与服务器进行HTTP通信的HttpClient对象,初始化为null,后续根据需要进行初始化和配置 private DefaultHttpClient mHttpClient; - + // 当前用于获取数据的URL,初始化为GTASK_GET_URL,可能会根据登录情况(如自定义域名等)进行调整 private String mGetUrl; - + // 当前用于提交数据的URL,初始化为GTASK_POST_URL,可能会根据登录情况(如自定义域名等)进行调整 private String mPostUrl; - +// 客户端版本号,初始化为 -1,登录成功后会从服务器响应中获取实际版本号 private long mClientVersion; - +// 标记用户是否已登录,初始化为false private boolean mLoggedin; - + // 记录上次登录的时间戳,初始化为0 private long mLastLoginTime; - + // 用于为每个操作生成唯一的标识符(每次操作后自增),初始化为1 private int mActionId; - + // 存储当前登录的账户信息,初始化为null private Account mAccount; - + // 用于存储待提交的更新操作对应的JSON数组(多个更新操作可批量提交),初始化为null private JSONArray mUpdateArray; - +// 私有构造函数,用于初始化GTaskClient类的各个成员变量,采用单例模式,限制外部直接实例化该类 private GTaskClient() { mHttpClient = null; mGetUrl = GTASK_GET_URL; @@ -101,41 +104,44 @@ public class GTaskClient { mAccount = null; mUpdateArray = null; } - + // 静态方法,用于获取GTaskClient的单例实例,如果实例不存在则创建一个新的实例,保证整个应用中只有一个GTaskClient对象在使用 + public static synchronized GTaskClient getInstance() { if (mInstance == null) { mInstance = new GTaskClient(); } return mInstance; } - +// 用于登录到Google Tasks服务的方法,根据一些条件判断是否需要重新登录(如登录间隔超过5分钟、账户切换等情况), + // 先获取Google账户的认证令牌,然后根据账户域名情况尝试不同的登录方式(自定义域名或官方URL),最后返回登录是否成功的布尔值 + public boolean login(Activity activity) { + // 假设Cookie在5分钟后过期,超过此时间间隔则需要重新登录 // we suppose that the cookie would expire after 5 minutes // then we need to re-login final long interval = 1000 * 60 * 5; if (mLastLoginTime + interval < System.currentTimeMillis()) { mLoggedin = false; } - + // 在账户切换后需要重新登录 // need to re-login after account switch if (mLoggedin && !TextUtils.equals(getSyncAccount().name, NotesPreferenceActivity .getSyncAccountName(activity))) { mLoggedin = false; } - if (mLoggedin) { Log.d(TAG, "already logged in"); return true; } - + mLastLoginTime = System.currentTimeMillis(); String authToken = loginGoogleAccount(activity, false); if (authToken == null) { Log.e(TAG, "login google account failed"); return false; } - + // 如果账户名不是以gmail.com或googlemail.com结尾,尝试使用自定义域名登录 // login with custom domain if necessary if (!(mAccount.name.toLowerCase().endsWith("gmail.com") || mAccount.name.toLowerCase() .endsWith("googlemail.com"))) { @@ -150,7 +156,7 @@ public class GTaskClient { mLoggedin = true; } } - + // 如果使用自定义域名登录失败,尝试使用Google官方URL登录 // try to login with google official url if (!mLoggedin) { mGetUrl = GTASK_GET_URL; @@ -163,7 +169,10 @@ public class GTaskClient { mLoggedin = true; return true; } - + // 用于获取Google账户的认证令牌的私有方法,通过AccountManager获取所有Google类型的账户, + // 根据应用设置中指定的同步账户名称查找对应的账户,然后向AccountManager请求获取认证令牌, + // 如果获取失败则返回null,若需要使令牌失效(invalidateToken为true时),则先失效令牌再重新获取 + private String loginGoogleAccount(Activity activity, boolean invalidateToken) { String authToken; AccountManager accountManager = AccountManager.get(activity); @@ -188,7 +197,7 @@ public class GTaskClient { Log.e(TAG, "unable to get an account with the same name in the settings"); return null; } - +// 获取令牌 // get the token now AccountManagerFuture accountManagerFuture = accountManager.getAuthToken(account, "goanna_mobile", null, activity, null, null); @@ -206,9 +215,12 @@ public class GTaskClient { return authToken; } - +// 尝试登录到Google Tasks服务的私有方法,先调用loginGtask方法进行登录,如果登录失败(可能是认证令牌过期等原因), + // 则使令牌失效后重新获取并再次尝试登录,返回最终登录是否成功的布尔值 + private boolean tryToLoginGtask(Activity activity, String authToken) { if (!loginGtask(authToken)) { + // 可能认证令牌已过期,现在使令牌失效并再次尝试 // maybe the auth token is out of date, now let's invalidate the // token and try again authToken = loginGoogleAccount(activity, true); @@ -224,7 +236,10 @@ public class GTaskClient { } return true; } - + // 实际执行登录到Google Tasks服务的私有方法,配置HttpClient的连接超时、读取超时等参数,设置Cookie存储, + // 禁用Expect-Continue机制,然后向服务器发送带有认证令牌的GET请求进行登录,从响应中获取Cookie判断是否登录成功, + // 同时从响应内容中解析出客户端版本号,若在过程中出现JSONException或其他异常则返回false表示登录失败 + private boolean loginGtask(String authToken) { int timeoutConnection = 10000; int timeoutSocket = 15000; @@ -235,14 +250,14 @@ public class GTaskClient { BasicCookieStore localBasicCookieStore = new BasicCookieStore(); mHttpClient.setCookieStore(localBasicCookieStore); HttpProtocolParams.setUseExpectContinue(mHttpClient.getParams(), false); - + // 登录Google Tasks // login gtask try { String loginUrl = mGetUrl + "?auth=" + authToken; HttpGet httpGet = new HttpGet(loginUrl); HttpResponse response = null; response = mHttpClient.execute(httpGet); - + // 获取Cookie // get the cookie now List cookies = mHttpClient.getCookieStore().getCookies(); boolean hasAuthCookie = false; @@ -254,7 +269,7 @@ public class GTaskClient { if (!hasAuthCookie) { Log.w(TAG, "it seems that there is no auth cookie"); } - +// 获取客户端版本号 // get the client version String resString = getResponseContent(response.getEntity()); String jsBegin = "_setup("; @@ -272,6 +287,7 @@ public class GTaskClient { e.printStackTrace(); return false; } catch (Exception e) { + // 简单捕获所有异常 // simply catch all exceptions Log.e(TAG, "httpget gtask_url failed"); return false; @@ -279,18 +295,20 @@ public class GTaskClient { return true; } - + // 获取下一个操作的唯一标识符(每次调用自增)的私有方法,用于区分不同的操作请求 private int getActionId() { return mActionId++; } - + // 创建一个用于POST请求的HttpPost对象的私有方法,设置请求头的Content-Type和AT字段,返回配置好的HttpPost对象, + // 方便后续设置请求体并发送POST请求 private HttpPost createHttpPost() { HttpPost httpPost = new HttpPost(mPostUrl); httpPost.setHeader("Content-Type", "application/x-www-form-urlencoded;charset=utf-8"); httpPost.setHeader("AT", "1"); return httpPost; } - +// 从HttpEntity中获取响应内容的私有方法,根据响应内容的编码方式(如gzip、deflate等)对输入流进行相应的解压处理, + // 然后逐行读取内容并拼接成字符串返回,最后关闭输入流,若在读取过程中出现IOException则抛出异常 private String getResponseContent(HttpEntity entity) throws IOException { String contentEncoding = null; if (entity.getContentEncoding() != null) { @@ -322,7 +340,10 @@ public class GTaskClient { input.close(); } } - + // 向服务器发送POST请求并处理响应的私有方法,首先检查是否已登录,若未登录则抛出异常,然后创建HttpPost对象, + // 设置请求体(将传入的JSONObject转换为表单数据),发送请求并获取响应内容,将响应内容转换为JSONObject返回, + // 在过程中若出现ClientProtocolException、IOException、JSONException等异常则分别抛出对应的异常表示请求失败 + private JSONObject postRequest(JSONObject js) throws NetworkFailureException { if (!mLoggedin) { Log.e(TAG, "please login first"); @@ -335,7 +356,7 @@ public class GTaskClient { list.add(new BasicNameValuePair("r", js.toString())); UrlEncodedFormEntity entity = new UrlEncodedFormEntity(list, "UTF-8"); httpPost.setEntity(entity); - + // 执行POST请求 // execute the post HttpResponse response = mHttpClient.execute(httpPost); String jsString = getResponseContent(response.getEntity()); @@ -359,7 +380,9 @@ public class GTaskClient { throw new ActionFailureException("error occurs when posting request"); } } - + // 创建任务的方法,首先调用commitUpdate方法提交之前可能积累的更新操作,然后构建包含创建任务操作的JSON数据, + // 设置客户端版本号等信息,向服务器发送POST请求,从响应中获取新创建任务的全局唯一标识符(Gid)并设置到任务对象中, + // 若在处理JSON数据过程中出现异常则抛出相应异常表示创建任务失败 public void createTask(Task task) throws NetworkFailureException { commitUpdate(); try { @@ -385,7 +408,8 @@ public class GTaskClient { throw new ActionFailureException("create task: handing jsonobject failed"); } } - + // 创建任务列表的方法,与创建任务的逻辑类似,先提交已有更新,构建包含创建任务列表操作的JSON数据,发送POST请求, + // 从响应中获取新创建任务列表的Gid并设置到任务列表对象中,若处理JSON出现问题则抛出异常表示创建失败 public void createTaskList(TaskList tasklist) throws NetworkFailureException { commitUpdate(); try { @@ -411,7 +435,9 @@ public class GTaskClient { throw new ActionFailureException("create tasklist: handing jsonobject failed"); } } - + // 提交更新操作的方法,若存在待提交的更新操作数组(mUpdateArray不为null),则构建包含更新操作列表和客户端版本号的JSON数据, + // 发送POST请求将更新提交到服务器,之后将更新操作数组置为null,表示已提交,若处理JSON过程中出现异常则抛出相应异常表示提交失败 + public void commitUpdate() throws NetworkFailureException { if (mUpdateArray != null) { try { @@ -432,7 +458,9 @@ public class GTaskClient { } } } - +// 添加待更新节点(任务、任务列表等)操作到更新操作数组的方法,首先判断节点是否为空,若不为空且更新操作数组长度超过10个(避免过多更新导致错误), + // 则先提交已有的更新操作,若更新操作数组为空则创建一个新的JSONArray,然后将节点的更新操作对应的JSON数据添加到数组中,用于后续批量提交更新 + public void addUpdateNode(Node node) throws NetworkFailureException { if (node != null) { // too many update items may result in an error @@ -446,7 +474,9 @@ public class GTaskClient { mUpdateArray.put(node.getUpdateAction(getActionId())); } } - + // 移动任务的方法,先提交已有更新操作,然后构建包含移动任务操作相关信息(如操作类型、任务ID、源任务列表、目标任务列表等)的JSON数据, + // 设置客户端版本号后发送POST请求到服务器,若处理JSON数据过程中出现异常则抛出相应异常表示移动任务失败 + public void moveTask(Task task, TaskList preParent, TaskList curParent) throws NetworkFailureException { commitUpdate(); @@ -485,7 +515,9 @@ public class GTaskClient { throw new ActionFailureException("move task: handing jsonobject failed"); } } - + // 删除节点(任务、任务列表等)的方法,先提交已有更新操作,将节点标记为已删除后构建包含删除操作的JSON数据,设置客户端版本号并发送POST请求到服务器, + // 提交成功后将更新操作数组置为null,若处理JSON过程中出现异常则抛出相应异常表示删除节点失败 + public void deleteNode(Node node) throws NetworkFailureException { commitUpdate(); try { @@ -508,7 +540,9 @@ public class GTaskClient { throw new ActionFailureException("delete node: handing jsonobject failed"); } } - + // 获取所有任务列表的方法,首先检查是否已登录,若未登录则抛出异常,然后发送GET请求到服务器获取任务列表相关数据, + // 从响应内容中解析出任务列表的JSON数组并返回,若在请求过程中出现ClientProtocolException、IOException、JSONException等异常则抛出相应异常表示获取失败 + public JSONArray getTaskLists() throws NetworkFailureException { if (!mLoggedin) { Log.e(TAG, "please login first"); @@ -546,7 +580,9 @@ public class GTaskClient { throw new ActionFailureException("get task lists: handing jasonobject failed"); } } - + // 获取指定任务列表(根据任务列表的Gid)的方法,先提交已有更新操作,然后构建包含获取指定任务列表操作相关信息的JSON数据,发送POST请求到服务器, + // 从响应中获取任务列表包含的任务的JSON数组并返回,若处理JSON过程中出现异常则抛出相应异常表示获取任务列表失败 + public JSONArray getTaskList(String listGid) throws NetworkFailureException { commitUpdate(); try { @@ -574,11 +610,12 @@ public class GTaskClient { throw new ActionFailureException("get task list: handing jsonobject failed"); } } - + // 获取当前同步账户的方法,直接返回存储的账户对象,外部可通过此方法获取当前登录用于同步的账户信息 public Account getSyncAccount() { return mAccount; } + // 重置更新操作数组的方法,将更新操作数组置为null,用于清空之前积累的待更新操作,通常在一些特定场景下(如重新开始一轮更新操作等)使用 public void resetUpdateArray() { mUpdateArray = null; } diff --git a/src/Notes-master/src/net/micode/notes/gtask/remote/GTaskManager.java b/src/Notes-master/src/net/micode/notes/gtask/remote/GTaskManager.java index d2b4082..626a6a3 100644 --- a/src/Notes-master/src/net/micode/notes/gtask/remote/GTaskManager.java +++ b/src/Notes-master/src/net/micode/notes/gtask/remote/GTaskManager.java @@ -46,47 +46,59 @@ import java.util.HashMap; import java.util.HashSet; import java.util.Iterator; import java.util.Map; - - +// GTaskManager类用于管理与远程任务服务(如Google Tasks)的同步操作以及相关数据的处理,包括任务列表、任务、元数据等的同步逻辑, +// 通过协调本地数据和远程数据的差异来实现数据的一致性,是整个应用中任务同步功能的核心管理类,采用单例模式确保在应用内只有一个实例进行管理操作。 public class GTaskManager { + // 用于日志记录的标签,取当前类的简单名称 private static final String TAG = GTaskManager.class.getSimpleName(); - +// 表示同步成功的状态码 public static final int STATE_SUCCESS = 0; - + // 表示网络错误导致同步失败的状态码 public static final int STATE_NETWORK_ERROR = 1; - +// 表示内部逻辑错误导致同步失败的状态码 public static final int STATE_INTERNAL_ERROR = 2; - + // 表示同步正在进行中的状态码 public static final int STATE_SYNC_IN_PROGRESS = 3; - + // 表示同步被取消的状态码 public static final int STATE_SYNC_CANCELLED = 4; + // 单例模式,存储唯一的GTaskManager实例,初始化为null private static GTaskManager mInstance = null; - +// 用于获取认证令牌等相关操作的Activity对象,可在不同的同步相关操作中使用,例如登录时获取账户认证信息等 + private Activity mActivity; - + // 应用上下文对象,用于获取内容解析器(ContentResolver)等系统服务,方便与本地数据库等进行交互操作 + private Context mContext; - + // 用于与本地内容提供者(Content Provider)进行交互,实现对本地数据(如数据库中的笔记、任务等相关数据)的查询、更新等操作 private ContentResolver mContentResolver; - +// 标记当前是否正在进行同步操作,初始化为false,在同步开始时设置为true,结束时设置为false private boolean mSyncing; - +// 标记当前同步操作是否已被取消,初始化为false,可通过相应方法设置为true来取消正在进行的同步过程 private boolean mCancelled; + // 以任务列表的全局唯一标识符(Gid)为键,存储任务列表对象的哈希映射,用于缓存从远程获取的任务列表信息以及在同步过程中管理本地和远程任务列表的对应关系 private HashMap mGTaskListHashMap; - +// 以节点(任务、任务列表、元数据等都可视为节点,通过共同的父类Node进行抽象)的Gid为键,存储节点对象的哈希映射,方便在同步过程中快速查找和操作各个节点 + private HashMap mGTaskHashMap; - + // 以相关Gid为键,存储元数据(MetaData)对象的哈希映射,用于管理和跟踪与任务等相关的元数据信息,例如可能包含一些额外的描述、配置等数据 + private HashMap mMetaHashMap; - + // 用于存储特殊的元数据列表(可能是具有特定用途、结构的元数据集合),初始化为null,在同步过程中根据情况进行创建和填充 + private TaskList mMetaList; - +// 用于存储本地已删除的任务或数据对应的唯一标识符(可能是本地数据库中的主键等),通过哈希集合来避免重复记录,方便后续统一处理删除相关逻辑 + private HashSet mLocalDeleteIdMap; - + // 以节点的Gid为键,存储对应本地唯一标识符(如数据库中的记录ID)的哈希映射,用于建立远程节点Gid和本地数据ID之间的对应关系,便于同步时查找和关联数据 + private HashMap mGidToNid; - + // 以本地唯一标识符为键,存储对应节点Gid的哈希映射,与mGidToNid作用类似,是反向的映射关系,用于在不同的同步操作场景下根据需要进行查找和转换 + private HashMap mNidToGid; - +// 私有构造函数,用于初始化GTaskManager类的各个成员变量,采用单例模式,限制外部直接实例化该类,在这里初始化了各种用于存储数据和管理同步状态的集合、映射等变量。 + private GTaskManager() { mSyncing = false; mCancelled = false; @@ -98,6 +110,7 @@ public class GTaskManager { mGidToNid = new HashMap(); mNidToGid = new HashMap(); } + // 静态方法,用于获取GTaskManager的单例实例,如果实例不存在则创建一个新的实例,保证整个应用中只有一个GTaskManager对象在管理任务同步等相关操作 public static synchronized GTaskManager getInstance() { if (mInstance == null) { @@ -105,12 +118,16 @@ public class GTaskManager { } return mInstance; } - +// 用于设置关联的Activity上下文对象的方法,主要是为了后续在需要获取认证令牌等与Activity相关的操作时能够获取到相应的上下文环境, + // 该方法是同步的,以保证在多线程环境下设置上下文对象的操作的原子性和一致性。 public synchronized void setActivityContext(Activity activity) { // used for getting authtoken mActivity = activity; } - +// 执行同步操作的核心方法,负责协调本地数据和远程数据的同步过程,首先检查是否正在同步,如果正在同步则直接返回相应状态码, + // 然后初始化一系列用于同步的数据结构,进行登录操作(通过GTaskClient),获取远程任务列表,执行具体的内容同步逻辑(包括处理本地删除、新增、更新等不同情况的同步), + // 最后根据同步过程中的各种情况(如是否取消、是否出现异常等)返回对应的同步状态码,并且在finally块中清理相关的数据结构,重置同步状态。 + public int sync(Context context, GTaskASyncTask asyncTask) { if (mSyncing) { Log.d(TAG, "Sync is in progress"); @@ -130,18 +147,18 @@ public class GTaskManager { try { GTaskClient client = GTaskClient.getInstance(); client.resetUpdateArray(); - + // 登录Google任务服务 // login google task if (!mCancelled) { if (!client.login(mActivity)) { throw new NetworkFailureException("login google task failed"); } } - + // 获取Google任务列表 // get the task list from google asyncTask.publishProgess(mContext.getString(R.string.sync_progress_init_list)); initGTaskList(); - + // 执行内容同步工作 // do content sync work asyncTask.publishProgess(mContext.getString(R.string.sync_progress_syncing)); syncContent(); @@ -167,14 +184,16 @@ public class GTaskManager { return mCancelled ? STATE_SYNC_CANCELLED : STATE_SUCCESS; } - + // 初始化远程任务列表相关数据的私有方法,首先检查同步是否已取消,如果取消则直接返回,然后通过GTaskClient获取远程任务列表的JSON数据, + // 接着分别处理元数据列表(如果不存在则创建)以及普通任务列表,解析JSON数据创建对应的任务列表和任务对象,并建立相关的映射关系(如将任务列表、任务等存入对应的哈希映射中), + // 若在处理JSON数据过程中出现异常则抛出相应异常表示初始化任务列表失败。 private void initGTaskList() throws NetworkFailureException { if (mCancelled) return; GTaskClient client = GTaskClient.getInstance(); try { JSONArray jsTaskLists = client.getTaskLists(); - + // 首先初始化元数据列表 // init meta list first mMetaList = null; for (int i = 0; i < jsTaskLists.length(); i++) { @@ -186,7 +205,7 @@ public class GTaskManager { .equals(GTaskStringUtils.MIUI_FOLDER_PREFFIX + GTaskStringUtils.FOLDER_META)) { mMetaList = new TaskList(); mMetaList.setContentByRemoteJSON(object); - + // 加载元数据 // load meta data JSONArray jsMetas = client.getTaskList(gid); for (int j = 0; j < jsMetas.length(); j++) { @@ -202,7 +221,7 @@ public class GTaskManager { } } } - + // 如果元数据列表不存在则创建它 // create meta list if not existed if (mMetaList == null) { mMetaList = new TaskList(); @@ -210,7 +229,7 @@ public class GTaskManager { + GTaskStringUtils.FOLDER_META); GTaskClient.getInstance().createTaskList(mMetaList); } - + // 初始化任务列表 // init task list for (int i = 0; i < jsTaskLists.length(); i++) { JSONObject object = jsTaskLists.getJSONObject(i); @@ -224,7 +243,7 @@ public class GTaskManager { tasklist.setContentByRemoteJSON(object); mGTaskListHashMap.put(gid, tasklist); mGTaskHashMap.put(gid, tasklist); - + // 加载任务 // load tasks JSONArray jsTasks = client.getTaskList(gid); for (int j = 0; j < jsTasks.length(); j++) { @@ -246,7 +265,9 @@ public class GTaskManager { throw new ActionFailureException("initGTaskList: handing JSONObject failed"); } } - + // 执行具体内容同步的私有方法,负责协调本地和远程数据在内容层面的同步操作,包括处理本地已删除的笔记、文件夹同步、数据库中现有笔记的同步等多种情况, + // 在不同的同步场景下调用相应的辅助方法(如doContentSync)来执行具体的添加、删除、更新等操作,并且在合适的时机提交更新到远程(通过GTaskClient)以及清理本地删除记录等操作, + // 整个过程中会不断检查同步是否已被取消,若已取消则及时终止操作。 private void syncContent() throws NetworkFailureException { int syncType; Cursor c = null; @@ -258,7 +279,7 @@ public class GTaskManager { if (mCancelled) { return; } - + // 处理本地已删除的笔记 // for local deleted note try { c = mContentResolver.query(Notes.CONTENT_NOTE_URI, SqlNote.PROJECTION_NOTE, @@ -285,10 +306,10 @@ public class GTaskManager { c = null; } } - + // 同步文件夹 // sync folder first syncFolder(); - + // 处理数据库中已存在的笔记 // for note existing in database try { c = mContentResolver.query(Notes.CONTENT_NOTE_URI, SqlNote.PROJECTION_NOTE, @@ -306,9 +327,11 @@ public class GTaskManager { syncType = node.getSyncAction(c); } else { if (c.getString(SqlNote.GTASK_ID_COLUMN).trim().length() == 0) { + // 本地新增 // local add syncType = Node.SYNC_ACTION_ADD_REMOTE; } else { + // 远程已删除 // remote delete syncType = Node.SYNC_ACTION_DEL_LOCAL; } @@ -325,7 +348,7 @@ public class GTaskManager { c = null; } } - +// 处理剩余的项目(可能是本地新增但还未处理完全的等情况) // go through remaining items Iterator> iter = mGTaskHashMap.entrySet().iterator(); while (iter.hasNext()) { @@ -333,16 +356,16 @@ public class GTaskManager { node = entry.getValue(); doContentSync(Node.SYNC_ACTION_ADD_LOCAL, node, null); } - + // mCancelled可以被其他线程设置,所以我们需要逐个检查 // mCancelled can be set by another thread, so we neet to check one by - // one + // one // 清除本地删除表(如果同步未取消) // clear local delete table if (!mCancelled) { if (!DataUtils.batchDeleteNotes(mContentResolver, mLocalDeleteIdMap)) { throw new ActionFailureException("failed to batch-delete local deleted notes"); } } - + // 刷新本地同步ID(如果同步未取消) // refresh local sync id if (!mCancelled) { GTaskClient.getInstance().commitUpdate(); @@ -350,7 +373,9 @@ public class GTaskManager { } } - + // 专门用于文件夹同步的私有方法,处理各种不同类型的文件夹(如根文件夹、通话记录文件夹、本地已存在的文件夹以及远程新增的文件夹等)的同步逻辑, + // 在不同情况下判断文件夹在本地和远程的存在情况,调用相应的辅助方法(如doContentSync)来执行添加、更新等操作,并且在合适的时机提交更新到远程(通过GTaskClient), + // 整个过程中会不断检查同步是否已被取消,若已取消则及时终止操作。 private void syncFolder() throws NetworkFailureException { Cursor c = null; String gid; @@ -360,7 +385,7 @@ public class GTaskManager { if (mCancelled) { return; } - + // 处理根文件夹 // for root folder try { c = mContentResolver.query(ContentUris.withAppendedId(Notes.CONTENT_NOTE_URI, @@ -373,6 +398,7 @@ public class GTaskManager { mGTaskHashMap.remove(gid); mGidToNid.put(gid, (long) Notes.ID_ROOT_FOLDER); mNidToGid.put((long) Notes.ID_ROOT_FOLDER, gid); + // 对于系统文件夹,仅在必要时更新远程名称 // for system folder, only update remote name if necessary if (!node.getName().equals( GTaskStringUtils.MIUI_FOLDER_PREFFIX + GTaskStringUtils.FOLDER_DEFAULT)) @@ -389,7 +415,7 @@ public class GTaskManager { c = null; } } - + // 处理通话记录文件夹 // for call-note folder try { c = mContentResolver.query(Notes.CONTENT_NOTE_URI, SqlNote.PROJECTION_NOTE, "(_id=?)", @@ -404,6 +430,7 @@ public class GTaskManager { mGTaskHashMap.remove(gid); mGidToNid.put(gid, (long) Notes.ID_CALL_RECORD_FOLDER); mNidToGid.put((long) Notes.ID_CALL_RECORD_FOLDER, gid); + // 对于系统文件夹,仅在必要时更新远程名称 // for system folder, only update remote name if // necessary if (!node.getName().equals( @@ -423,7 +450,7 @@ public class GTaskManager { c = null; } } - + // 处理本地已存在的文件夹 // for local existing folders try { c = mContentResolver.query(Notes.CONTENT_NOTE_URI, SqlNote.PROJECTION_NOTE, @@ -441,9 +468,11 @@ public class GTaskManager { syncType = node.getSyncAction(c); } else { if (c.getString(SqlNote.GTASK_ID_COLUMN).trim().length() == 0) { + // 本地新增 // local add syncType = Node.SYNC_ACTION_ADD_REMOTE; } else { + // 远程已删除 // remote delete syncType = Node.SYNC_ACTION_DEL_LOCAL; } @@ -459,7 +488,7 @@ public class GTaskManager { c = null; } } - + // 处理远程新增的文件夹 // for remote add folders Iterator> iter = mGTaskListHashMap.entrySet().iterator(); while (iter.hasNext()) { @@ -475,7 +504,9 @@ public class GTaskManager { if (!mCancelled) GTaskClient.getInstance().commitUpdate(); } - + // 根据不同的同步操作类型执行具体同步动作的私有方法,通过一个switch语句根据传入的同步类型(syncType)来决定调用相应的添加、删除、更新等具体操作方法, + // 整个过程中会不断检查同步是否已被取消,若已取消则及时终止操作,若传入的同步类型不合法则抛出异常表示未知的同步操作类型。 + private void doContentSync(int syncType, Node node, Cursor c) throws NetworkFailureException { if (mCancelled) { return; @@ -510,6 +541,7 @@ public class GTaskManager { updateRemoteNode(node, c); break; case Node.SYNC_ACTION_UPDATE_CONFLICT: + // 合并双方的修改可能是个好主意,目前简单使用本地更新 // merging both modifications maybe a good idea // right now just use local update simply updateRemoteNode(node, c); @@ -521,7 +553,10 @@ public class GTaskManager { throw new ActionFailureException("unkown sync action type"); } } - +// 将本地节点添加到本地数据库以及处理相关同步逻辑的私有方法,首先判断同步是否已取消,如果取消则直接返回, + // 然后根据节点类型(是任务列表还是普通任务等)进行不同的处理,创建对应的本地数据记录(通过SqlNote),设置相关属性(如父节点ID、全局唯一标识符等), + // 将数据提交到本地数据库,更新本地和远程标识符的映射关系,并且调用updateRemoteMeta方法来处理与元数据相关的更新操作,若在过程中出现找不到父节点ID等问题则抛出异常表示无法添加本地节点。 + private void addLocalNode(Node node) throws NetworkFailureException { if (mCancelled) { return; @@ -549,6 +584,7 @@ public class GTaskManager { if (note.has(NoteColumns.ID)) { long id = note.getLong(NoteColumns.ID); if (DataUtils.existInNoteDatabase(mContentResolver, id)) { + // 该ID不可用,必须创建一个新的 // the id is not available, have to create a new one note.remove(NoteColumns.ID); } @@ -562,6 +598,7 @@ public class GTaskManager { if (data.has(DataColumns.ID)) { long dataId = data.getLong(DataColumns.ID); if (DataUtils.existInDataDatabase(mContentResolver, dataId)) { + // 数据ID不可用,必须创建一个新的 // the data id is not available, have to create // a new one data.remove(DataColumns.ID); @@ -583,25 +620,29 @@ public class GTaskManager { } sqlNote.setParentId(parentId.longValue()); } - +// 创建本地节点 // create the local node sqlNote.setGtaskId(node.getGid()); sqlNote.commit(false); - +// 更新Gid-Nid映射 // update gid-nid mapping mGidToNid.put(node.getGid(), sqlNote.getId()); mNidToGid.put(sqlNote.getId(), node.getGid()); - +// 更新元数据 // update meta updateRemoteMeta(node.getGid(), sqlNote); } - +// 更新本地节点数据以及相关同步逻辑的私有方法,首先判断同步是否已取消,如果取消则直接返回, + // 然后根据节点类型(是任务还是其他类型等)获取对应的本地数据记录(通过SqlNote),更新其内容、父节点ID等属性,将更新后的数据提交到本地数据库, + // 并调用updateRemoteMeta方法来处理与元数据相关的更新操作,若在过程中出现找不到父节点ID等问题则抛出异常表示无法更新本地节点。 + private void updateLocalNode(Node node, Cursor c) throws NetworkFailureException { if (mCancelled) { return; } SqlNote sqlNote; + // 更新本地笔记 // update the note locally sqlNote = new SqlNote(mContext, c); sqlNote.setContent(node.getLocalJSONFromContent()); @@ -614,11 +655,15 @@ public class GTaskManager { } sqlNote.setParentId(parentId.longValue()); sqlNote.commit(true); - +// 更新元数据信息 // update meta info updateRemoteMeta(node.getGid(), sqlNote); } - + // 将远程节点添加到本地数据库以及处理相关同步逻辑的私有方法,首先判断同步是否已取消,如果取消则直接返回, + // 然后根据节点类型(是任务还是任务列表等)进行不同的处理,创建对应的本地数据记录(通过SqlNote), + // 对于任务,找到其对应的父任务列表,添加到任务列表中,然后向远程服务创建任务(通过GTaskClient),对于任务列表,判断是否已存在,不存在则创建, + // 接着更新本地数据记录的相关属性(如全局唯一标识符等),提交到本地数据库,更新本地和远程标识符的映射关系,若在过程中出现找不到父任务列表等问题则抛出异常表示无法添加远程节点。 + private void addRemoteNode(Node node, Cursor c) throws NetworkFailureException { if (mCancelled) { return; @@ -626,7 +671,7 @@ public class GTaskManager { SqlNote sqlNote = new SqlNote(mContext, c); Node n; - + // 远程更新 // update remotely if (sqlNote.isNoteType()) { Task task = new Task(); @@ -641,12 +686,12 @@ public class GTaskManager { GTaskClient.getInstance().createTask(task); n = (Node) task; - + // 添加元数据 // add meta updateRemoteMeta(task.getGid(), sqlNote); } else { TaskList tasklist = null; - + // 我们需要跳过已存在的文件夹 // we need to skip folder if it has already existed String folderName = GTaskStringUtils.MIUI_FOLDER_PREFFIX; if (sqlNote.getId() == Notes.ID_ROOT_FOLDER) @@ -670,7 +715,7 @@ public class GTaskManager { break; } } - +// 没有匹配的则添加 // no match we can add now if (tasklist == null) { tasklist = new TaskList(); @@ -680,32 +725,36 @@ public class GTaskManager { } n = (Node) tasklist; } - + // 更新本地笔记 // update local note sqlNote.setGtaskId(n.getGid()); sqlNote.commit(false); sqlNote.resetLocalModified(); sqlNote.commit(true); - + // gid-id映射 // gid-id mapping mGidToNid.put(n.getGid(), sqlNote.getId()); mNidToGid.put(sqlNote.getId(), n.getGid()); } - + // 更新远程节点数据以及相关同步逻辑的私有方法,首先判断同步是否已取消,如果取消则直接返回, + // 然后获取对应的本地数据记录(通过SqlNote),将本地数据内容设置到远程节点,向远程服务添加更新节点的请求(通过GTaskClient), + // 接着调用updateRemoteMeta方法处理元数据更新,对于任务类型的节点,还会判断是否需要移动任务(根据父任务列表的变化),若需要则执行移动任务操作(通过GTaskClient), + // 最后清除本地数据的修改标记并提交到本地数据库,若在过程中出现找不到父任务列表等问题则抛出异常表示无法更新远程任务。 + private void updateRemoteNode(Node node, Cursor c) throws NetworkFailureException { if (mCancelled) { return; } SqlNote sqlNote = new SqlNote(mContext, c); - + // 远程更新 // update remotely node.setContentByLocalJSON(sqlNote.getContent()); GTaskClient.getInstance().addUpdateNode(node); - + // 更新元数据 // update meta updateRemoteMeta(node.getGid(), sqlNote); - + // 如果必要的话移动任务 // move task if necessary if (sqlNote.isNoteType()) { Task task = (Task) node; @@ -724,12 +773,15 @@ public class GTaskManager { GTaskClient.getInstance().moveTask(task, preParentList, curParentList); } } - + // 清除本地修改标记 // clear local modified flag sqlNote.resetLocalModified(); sqlNote.commit(true); } - + // 更新远程元数据的私有方法,首先判断传入的本地数据记录(sqlNote)是否为笔记类型且不为空, + // 如果是,则先从元数据映射中查找对应Gid的元数据对象,若存在则更新其元数据内容并向远程服务添加更新节点请求(通过GTaskClient), + // 若不存在则创建新的元数据对象,添加到元数据列表中,然后向远程服务创建元数据任务(通过GTaskClient),以此来保证元数据在本地和远程的同步更新。 + private void updateRemoteMeta(String gid, SqlNote sqlNote) throws NetworkFailureException { if (sqlNote != null && sqlNote.isNoteType()) { MetaData metaData = mMetaHashMap.get(gid); @@ -745,12 +797,15 @@ public class GTaskManager { } } } - + // 刷新本地同步ID的私有方法,用于在同步完成后,根据最新的远程任务列表数据来更新本地数据的同步ID,首先判断同步是否已取消,如果取消则直接返回, + // 然后清空之前缓存的相关任务数据结构(如任务哈希映射、任务列表哈希映射、元数据哈希映射等),重新初始化任务列表数据(通过initGTaskList方法), + // 接着查询本地数据库中的相关数据,对于在远程任务列表中能找到对应节点的数据,更新其同步ID,若在过程中出现找不到对应节点等问题则抛出异常表示同步后本地部分数据缺失对应的Gid。 + private void refreshLocalSyncId() throws NetworkFailureException { if (mCancelled) { return; } - + // 获取最新的gtask列表 // get the latest gtask list mGTaskHashMap.clear(); mGTaskListHashMap.clear(); @@ -789,11 +844,13 @@ public class GTaskManager { } } } - + // 获取当前同步账户名称的方法,通过调用GTaskClient的实例获取当前同步账户对象,然后返回其账户名称,外部可通过此方法获取当前正在用于同步的账户信息。 + public String getSyncAccount() { return GTaskClient.getInstance().getSyncAccount().name; } - + // 用于取消正在进行的同步操作的方法,将同步取消标记(mCancelled)设置为true,在同步过程中的各个关键步骤都会检查这个标记, + // 一旦发现被设置为true,则会及时终止相应的同步操作,避免不必要的资源消耗和数据不一致问题。 public void cancelSync() { mCancelled = true; } diff --git a/src/Notes-master/src/net/micode/notes/gtask/remote/GTaskSyncService.java b/src/Notes-master/src/net/micode/notes/gtask/remote/GTaskSyncService.java index cca36f7..b3b6f12 100644 --- a/src/Notes-master/src/net/micode/notes/gtask/remote/GTaskSyncService.java +++ b/src/Notes-master/src/net/micode/notes/gtask/remote/GTaskSyncService.java @@ -22,26 +22,37 @@ import android.content.Context; import android.content.Intent; import android.os.Bundle; import android.os.IBinder; +// GTaskSyncService类继承自Service,是一个用于处理任务同步相关操作的后台服务类,它可以接收不同的意图(Intent)来启动或取消任务同步, +// 并且在同步过程中能够发送广播来通知同步状态、进度等信息,同时对外提供了一些静态方法方便在不同的地方控制同步操作以及获取同步相关的状态信息。 public class GTaskSyncService extends Service { + // 用于在Intent中传递同步操作类型的字符串常量名称,外部组件(如Activity等)可以通过在Intent中设置该名称对应的额外数据来指定要执行的同步相关操作。 public final static String ACTION_STRING_NAME = "sync_action_type"; - + // 表示启动同步操作的常量值,在通过Intent传递同步操作类型时使用该值表示要开始进行任务同步。 public final static int ACTION_START_SYNC = 0; - + // 表示取消同步操作的常量值,用于指示服务取消正在进行的任务同步过程。 public final static int ACTION_CANCEL_SYNC = 1; - + // 表示无效的同步操作类型的常量值,当接收到无法识别的操作类型时可能用到。 public final static int ACTION_INVALID = 2; - +// 用于发送广播的意图(Intent)的动作(Action)名称,通过这个名称可以在应用内识别来自该服务发送的广播,其他组件可以注册接收该广播来获取同步服务相关的信息。 + public final static String GTASK_SERVICE_BROADCAST_NAME = "net.micode.notes.gtask.remote.gtask_sync_service"; - + // 用于在广播中传递是否正在同步这个状态信息的键(Key)名称,接收广播的组件可以通过该键从广播附带的数据中获取当前是否正在进行同步的状态。 + public final static String GTASK_SERVICE_BROADCAST_IS_SYNCING = "isSyncing"; - + // 用于在广播中传递同步进度消息的键(Key)名称,服务可以在同步过程中更新并发送相应的进度消息,其他组件接收广播后能展示给用户同步的进展情况。 + public final static String GTASK_SERVICE_BROADCAST_PROGRESS_MSG = "progressMsg"; - +// 静态变量,用于存储当前正在执行的同步任务(GTaskASyncTask类型)实例,初始化为null,在启动同步时创建并赋值,取消或完成同步后设置为null,用于跟踪同步任务的执行情况。 + private static GTaskASyncTask mSyncTask = null; - + // 静态变量,用于存储同步进度相关的消息字符串,初始为空字符串,服务在同步过程中可以更新该字符串内容,并通过广播发送出去,方便外部组件展示同步进度给用户。 + private static String mSyncProgress = ""; - +// 私有方法,用于启动同步任务的逻辑,首先判断当前是否已经存在正在执行的同步任务(mSyncTask是否为null), + // 如果不存在,则创建一个新的GTaskASyncTask实例,并传入当前服务(this)以及一个完成监听器(OnCompleteListener), + // 在监听器中,当同步任务完成时,会将mSyncTask设置为null,发送一个空消息的广播,并停止该服务自身,然后发送广播通知同步开始,最后执行同步任务。 + private void startSync() { if (mSyncTask == null) { mSyncTask = new GTaskASyncTask(this, new GTaskASyncTask.OnCompleteListener() { @@ -55,18 +66,24 @@ public class GTaskSyncService extends Service { mSyncTask.execute(); } } - + // 私有方法,用于取消正在进行的同步任务,如果当前存在正在执行的同步任务(mSyncTask不为null),则调用其cancelSync方法来取消同步操作。 + private void cancelSync() { if (mSyncTask != null) { mSyncTask.cancelSync(); } } - + // 重写Service的onCreate方法,在服务创建时被调用,这里将mSyncTask设置为null,确保每次服务启动时都处于初始状态,准备执行新的同步任务。 + @Override public void onCreate() { mSyncTask = null; } - + // 重写Service的onStartCommand方法,当通过startService方法启动服务时会调用此方法,用于处理传入的意图(Intent)并根据其中携带的同步操作类型执行相应操作, + // 首先从意图中获取额外数据的Bundle对象,然后判断Bundle是否存在且包含指定的同步操作类型键(ACTION_STRING_NAME), + // 如果满足条件,则根据操作类型的值进行switch判断,若是ACTION_START_SYNC则调用startSync方法启动同步,若是ACTION_CANCEL_SYNC则调用cancelSync方法取消同步, + // 最后返回START_STICKY表示服务在被系统意外终止后应该尝试重新启动,以保证同步功能的持续可用性,若不满足条件则调用父类的onStartCommand方法进行默认处理。 + @Override public int onStartCommand(Intent intent, int flags, int startId) { Bundle bundle = intent.getExtras(); @@ -85,18 +102,22 @@ public class GTaskSyncService extends Service { } return super.onStartCommand(intent, flags, startId); } - +// 重写Service的onLowMemory方法,当系统内存不足时会调用此方法,在这里如果存在正在执行的同步任务(mSyncTask不为null),则调用其cancelSync方法取消同步操作, + // 以释放内存资源,避免因内存不足导致应用出现异常或性能问题。 @Override public void onLowMemory() { if (mSyncTask != null) { mSyncTask.cancelSync(); } } - + // 重写Service的onBind方法,用于处理服务的绑定操作,这里返回null表示该服务不支持绑定操作,即它是通过startService方式启动来执行后台任务的,而不是通过绑定来与其他组件交互。 + public IBinder onBind(Intent intent) { return null; } - + // 用于发送广播的方法,更新同步进度消息(mSyncProgress),创建一个新的意图(Intent),设置其动作(Action)为预定义的广播名称(GTASK_SERVICE_BROADCAST_NAME), + // 并将是否正在同步(mSyncTask是否为null)以及同步进度消息(mSyncProgress)作为额外数据添加到意图中,最后通过sendBroadcast方法发送广播, + // 以便其他组件能够接收到这些同步相关的状态和进度信息。 public void sendBroadcast(String msg) { mSyncProgress = msg; Intent intent = new Intent(GTASK_SERVICE_BROADCAST_NAME); @@ -104,6 +125,8 @@ public class GTaskSyncService extends Service { intent.putExtra(GTASK_SERVICE_BROADCAST_PROGRESS_MSG, msg); sendBroadcast(intent); } + // 静态方法,用于方便地从Activity中启动同步操作,首先通过GTaskManager的单例实例设置关联的Activity上下文对象(用于后续可能的与账户认证等相关操作), + // 然后创建一个意图(Intent),指定要启动的服务为GTaskSyncService,并在意图中设置同步操作类型为ACTION_START_SYNC,最后通过Activity的startService方法启动服务来开始同步。 public static void startSync(Activity activity) { GTaskManager.getInstance().setActivityContext(activity); @@ -111,17 +134,21 @@ public class GTaskSyncService extends Service { intent.putExtra(GTaskSyncService.ACTION_STRING_NAME, GTaskSyncService.ACTION_START_SYNC); activity.startService(intent); } + // 静态方法,用于从任意上下文(Context)中取消同步操作,创建一个意图(Intent),指定要操作的服务为GTaskSyncService,并在意图中设置同步操作类型为ACTION_CANCEL_SYNC, + // 最后通过上下文的startService方法启动服务来触发取消同步的逻辑,这样在应用的不同地方都可以方便地取消正在进行的同步任务。 public static void cancelSync(Context context) { Intent intent = new Intent(context, GTaskSyncService.class); intent.putExtra(GTaskSyncService.ACTION_STRING_NAME, GTaskSyncService.ACTION_CANCEL_SYNC); context.startService(intent); } - +// 静态方法,用于外部组件判断当前是否正在进行同步操作,通过检查静态变量mSyncTask是否为null来返回相应的布尔值,表示当前是否有同步任务正在执行。 + public static boolean isSyncing() { return mSyncTask != null; } - + // 静态方法,用于外部组件获取当前的同步进度消息字符串,直接返回存储同步进度消息的静态变量mSyncProgress,以便展示给用户同步的进展情况。 + public static String getProgressString() { return mSyncProgress; } diff --git a/src/Notes-master/src/net/micode/notes/model/Note.java b/src/Notes-master/src/net/micode/notes/model/Note.java index 6706cf6..0484117 100644 --- a/src/Notes-master/src/net/micode/notes/model/Note.java +++ b/src/Notes-master/src/net/micode/notes/model/Note.java @@ -33,11 +33,24 @@ import net.micode.notes.data.Notes.TextNote; import java.util.ArrayList; +// Note类代表笔记的数据模型,用于管理笔记的各种属性数据(如文本数据、通话记录数据等)以及与本地数据库(通过Content Provider)的交互操作, +// 包括创建新笔记、更新笔记、判断笔记是否有本地修改等功能,内部还嵌套了一个NoteData类用于更细致地管理笔记相关的数据内容。 public class Note { + // 用于存储笔记中发生变化的属性值(以键值对形式),通过ContentValues来方便后续与内容提供者进行更新操作,记录笔记在本地修改后产生的数据差异。 + private ContentValues mNoteDiffValues; + // NoteData类型的实例,用于管理笔记中的具体数据内容,如文本数据、通话数据等,将不同类型的数据操作封装在这个内部类中,使代码结构更清晰。 + private NoteData mNoteData; + // 用于日志记录的标签,方便在日志中识别该类相关的输出信息,取固定的字符串"Note"。 private static final String TAG = "Note"; + // 静态同步方法,用于创建一个新的笔记并返回其在数据库中的唯一标识符(ID),接收应用上下文(Context)和所属文件夹的ID作为参数, + // 在方法内部首先创建一个ContentValues对象,设置笔记的创建时间、修改时间、类型、本地修改标记以及父文件夹ID等初始属性值, + // 然后通过内容解析器(ContentResolver)将这些数据插入到本地数据库对应的笔记内容URI(Notes.CONTENT_NOTE_URI)中, + // 接着尝试从插入后返回的Uri中解析出笔记的ID,如果解析出现异常或者ID值为非法值(如 -1)则进行相应的错误处理并返回0或抛出异常,正常情况下返回新创建笔记的ID。 + public static synchronized long getNewNoteId(Context context, long folderId) { + /** * Create a new note id for adding a new note to databases */ @@ -64,42 +77,55 @@ public class Note { } return noteId; } - + // Note类的默认构造函数,用于初始化Note对象,创建一个新的ContentValues对象用于存储笔记的差异数据,同时创建一个NoteData对象来管理具体的笔记数据内容。 + public Note() { mNoteDiffValues = new ContentValues(); mNoteData = new NoteData(); } - +// 用于设置笔记的通用属性值的方法,接收属性的键(key)和值(value)作为参数,将键值对添加到mNoteDiffValues中, + // 同时更新笔记的本地修改标记(LOCAL_MODIFIED)为已修改状态,并设置修改日期(MODIFIED_DATE)为当前时间,以便后续能准确判断笔记是否有修改以及进行相应的更新操作。 + public void setNoteValue(String key, String value) { mNoteDiffValues.put(key, value); mNoteDiffValues.put(NoteColumns.LOCAL_MODIFIED, 1); mNoteDiffValues.put(NoteColumns.MODIFIED_DATE, System.currentTimeMillis()); } - + // 用于设置笔记文本数据的方法,调用内部NoteData对象的setTextData方法,将文本数据的键值对传递给它进行处理,内部会根据情况更新相关的修改标记和日期等信息。 + public void setTextData(String key, String value) { mNoteData.setTextData(key, value); } - + // 用于设置笔记文本数据的唯一标识符(ID)的方法,直接调用内部NoteData对象的setTextDataId方法,将传入的ID值传递给它进行设置,同时会进行参数合法性检查(ID需大于0)。 + public void setTextDataId(long id) { mNoteData.setTextDataId(id); } - + // 用于获取笔记文本数据的唯一标识符(ID)的方法,直接返回内部NoteData对象中存储的文本数据ID(mTextDataId)。 + public long getTextDataId() { return mNoteData.mTextDataId; } - + // 用于设置笔记通话数据的唯一标识符(ID)的方法,调用内部NoteData对象的setCallDataId方法,将传入的ID值传递给它进行设置,同时会进行参数合法性检查(ID需大于0)。 + public void setCallDataId(long id) { mNoteData.setCallDataId(id); } - + // 用于设置笔记通话数据的方法,调用内部NoteData对象的setCallData方法,将通话数据的键值对传递给它进行处理,内部会根据情况更新相关的修改标记和日期等信息。 + public void setCallData(String key, String value) { mNoteData.setCallData(key, value); } - +// 用于判断笔记是否在本地有修改的方法,通过检查mNoteDiffValues中是否有数据(即是否有属性被修改)以及内部NoteData对象是否有本地修改来综合判断, + // 如果两者中任意一个有数据变化,则表示笔记在本地有修改,返回true,否则返回false。 public boolean isLocalModified() { return mNoteDiffValues.size() > 0 || mNoteData.isLocalModified(); } - + // 用于将本地修改后的笔记数据同步到本地数据库的方法,接收应用上下文(Context)和笔记的ID(noteId)作为参数,首先进行参数合法性检查(noteId需大于0), + // 如果笔记没有本地修改则直接返回true,表示无需同步操作。若有本地修改,则先尝试通过内容解析器(ContentResolver)根据mNoteDiffValues中的数据更新笔记的基本属性信息, + // 如果更新失败(返回0表示影响的行数为0,即更新未成功)则记录错误日志但不立即返回,继续往下执行,接着清空mNoteDiffValues已同步的数据。 + // 然后检查内部NoteData对象是否有本地修改,如果有且将其数据推送到内容解析器(通过pushIntoContentResolver方法)失败,则返回false表示同步失败, + // 否则返回true表示整个笔记数据(包括基本属性和具体数据内容)同步成功。 public boolean syncNote(Context context, long noteId) { if (noteId <= 0) { throw new IllegalArgumentException("Wrong note id:" + noteId); @@ -129,35 +155,44 @@ public class Note { return true; } - +// 内部私有类NoteData,用于更细致地管理笔记中的具体数据内容,包括文本数据和通话数据相关的属性、操作以及与内容提供者的交互逻辑,将这些与数据内容相关的操作封装在内部类中, + // 使Note类整体结构更清晰,职责更明确,避免不同类型数据操作的代码相互混杂。 private class NoteData { private long mTextDataId; - + // 用于存储笔记文本数据的唯一标识符(ID),初始化为0,在插入或更新文本数据时会根据情况进行赋值或更新。 + // 用于存储笔记文本数据的具体属性值(以键值对形式),通过ContentValues方便后续与内容提供者进行插入或更新操作,记录文本数据的相关内容。 + private ContentValues mTextDataValues; - +// 用于存储笔记通话数据的唯一标识符(ID),初始化为0,在插入或更新通话数据时会根据情况进行赋值或更新。 + private long mCallDataId; - +// 用于存储笔记通话数据的具体属性值(以键值对形式),通过ContentValues方便后续与内容提供者进行插入或更新操作,记录通话数据的相关内容。 + private ContentValues mCallDataValues; - + // 用于日志记录的标签,方便在日志中识别该内部类相关的输出信息,取固定的字符串"NoteData"。 private static final String TAG = "NoteData"; - + // NoteData类的默认构造函数,用于初始化NoteData对象,创建新的ContentValues对象分别用于存储文本数据和通话数据的属性值,同时将文本数据和通话数据的ID初始化为0。 + public NoteData() { mTextDataValues = new ContentValues(); mCallDataValues = new ContentValues(); mTextDataId = 0; mCallDataId = 0; } - +// 用于判断NoteData对象中的文本数据或通话数据是否有本地修改的方法,通过检查mTextDataValues和mCallDataValues中是否有数据(即是否有属性被修改)来综合判断, + // 如果两者中任意一个有数据变化,则表示有本地修改,返回true,否则返回false。 boolean isLocalModified() { return mTextDataValues.size() > 0 || mCallDataValues.size() > 0; } - +// 用于设置笔记文本数据的唯一标识符(ID)的方法,进行参数合法性检查(ID需大于0),如果合法则将传入的ID值赋值给mTextDataId,用于后续对文本数据的操作定位等。 + void setTextDataId(long id) { if(id <= 0) { throw new IllegalArgumentException("Text data id should larger than 0"); } mTextDataId = id; - } + } // 用于设置笔记通话数据的唯一标识符(ID)的方法,进行参数合法性检查(ID需大于0),如果合法则将传入的ID值赋值给mCallDataId,用于后续对通话数据的操作定位等。 + void setCallDataId(long id) { if (id <= 0) { @@ -165,19 +200,30 @@ public class Note { } mCallDataId = id; } - + // 用于设置笔记通话数据的方法,接收通话数据的键(key)和值(value)作为参数,将键值对添加到mCallDataValues中, + // 同时更新笔记的本地修改标记(通过外部的mNoteDiffValues间接更新,因为NoteData是内部类可以访问外部类的成员)以及修改日期为当前时间,以便能准确判断笔记整体是否有修改以及进行相应的更新操作。 + void setCallData(String key, String value) { mCallDataValues.put(key, value); mNoteDiffValues.put(NoteColumns.LOCAL_MODIFIED, 1); mNoteDiffValues.put(NoteColumns.MODIFIED_DATE, System.currentTimeMillis()); } - +// 用于设置笔记文本数据的方法,接收文本数据的键(key)和值(value)作为参数,将键值对添加到mTextDataValues中, + // 同时更新笔记的本地修改标记(通过外部的mNoteDiffValues间接更新)以及修改日期为当前时间,以便能准确判断笔记整体是否有修改以及进行相应的更新操作。 + void setTextData(String key, String value) { mTextDataValues.put(key, value); mNoteDiffValues.put(NoteColumns.LOCAL_MODIFIED, 1); mNoteDiffValues.put(NoteColumns.MODIFIED_DATE, System.currentTimeMillis()); } - + // 用于将NoteData对象中管理的文本数据和通话数据推送到内容解析器(即更新或插入到本地数据库)的方法,接收应用上下文(Context)和笔记的ID(noteId)作为参数, + // 首先进行参数合法性检查(noteId需大于0),然后创建一个ContentProviderOperation列表(用于批量操作),根据mTextDataValues和mCallDataValues中的数据情况进行不同的操作: + // 如果文本数据有值(mTextDataValues.size() > 0),则设置笔记ID到文本数据属性中,如果文本数据的ID为0(表示是新数据需要插入),则设置文本数据的MIME类型, + // 通过内容解析器将文本数据插入到本地数据库对应的内容URI(Notes.CONTENT_DATA_URI)中,并尝试从插入后返回的Uri中解析出文本数据的ID,如果解析失败则记录错误日志并清空文本数据属性值,返回null表示操作失败; + // 如果文本数据的ID不为0(表示是已有数据需要更新),则构建一个更新操作并添加到操作列表中。然后对通话数据进行类似的处理(根据mCallDataValues的情况进行插入或更新操作)。 + // 最后如果操作列表中有操作需要执行,则通过内容解析器批量应用这些操作(applyBatch方法),根据操作结果返回相应的Uri(如果成功则返回笔记对应的Uri,失败则返回null), + // 如果在批量操作过程中出现RemoteException或OperationApplicationException异常则记录错误日志并返回null表示操作失败。如果操作列表为空则直接返回null表示无需进行数据推送操作。 + Uri pushIntoContentResolver(Context context, long noteId) { /** * Check for safety diff --git a/src/Notes-master/src/net/micode/notes/model/WorkingNote.java b/src/Notes-master/src/net/micode/notes/model/WorkingNote.java index be081e4..01faf6e 100644 --- a/src/Notes-master/src/net/micode/notes/model/WorkingNote.java +++ b/src/Notes-master/src/net/micode/notes/model/WorkingNote.java @@ -30,38 +30,55 @@ import net.micode.notes.data.Notes.DataConstants; import net.micode.notes.data.Notes.NoteColumns; import net.micode.notes.data.Notes.TextNote; import net.micode.notes.tool.ResourceParser.NoteBgResources; - +// `WorkingNote` 类代表正在操作(编辑、修改等)的笔记对象,它封装了笔记的各种属性(如内容、ID、提醒日期、背景颜色等)以及相关的操作方法(如保存、加载、设置属性值等), +// 同时定义了一个接口用于监听笔记设置相关属性变化的情况,以便在属性改变时能做出相应的业务逻辑处理,是笔记在应用中实际使用场景下的一个综合数据和操作模型。 public class WorkingNote { + // 关联的 `Note` 类对象,用于处理笔记底层的数据操作(如同步到数据库等),借助 `Note` 类已有的功能来实现 `WorkingNote` 的相关持久化等操作。 + // Note for the working note private Note mNote; + // 笔记的唯一标识符(ID),用于在数据库等场景中唯一标识该笔记,初始值根据不同的构造函数可能为0(新创建笔记时)或从已有数据中获取的具体ID值。 + // Note Id private long mNoteId; + // 笔记的具体内容,比如用户输入的文本等信息,用于展示和编辑笔记的核心文本数据。 + // Note content private String mContent; + // 笔记的模式,可能用于表示不同的展示或编辑模式(例如是否为清单模式等),具体含义由应用业务逻辑决定,初始化为0。 + // Note mode private int mMode; - + // 笔记的提醒日期,用于设置和记录该笔记是否有提醒以及提醒的时间点,初始化为0表示无提醒。 private long mAlertDate; - + // 笔记的最后修改日期,用于记录笔记最后一次被修改的时间,初始化为当前系统时间(在构造新笔记时)。 private long mModifiedDate; - +// 笔记的背景颜色资源ID,用于设置和获取笔记展示时的背景颜色,初始值会根据具体业务场景设定或默认值赋值。 private int mBgColorId; - +// 关联的桌面小部件(Widget)的ID,用于标识该笔记是否与某个桌面小部件相关联以及是哪个小部件,初始值根据情况设定,若未关联则可能为无效值(`AppWidgetManager.INVALID_APPWIDGET_ID`)。 + private int mWidgetId; - + // 桌面小部件的类型,用于区分不同类型的小部件与笔记的关联情况,初始值根据业务逻辑设定,可能有特定的枚举值(如 `Notes.TYPE_WIDGET_INVALIDE` 表示无效类型等)。 + private int mWidgetType; - + // 笔记所属文件夹的ID,用于表示笔记在文件系统结构中的所属位置,便于分类管理笔记,初始值根据构造函数传入参数确定。 + private long mFolderId; - + // 应用的上下文(Context)对象,用于获取系统服务、资源以及与内容提供者(Content Provider)等进行交互,是整个类中很多操作的基础依赖。 + private Context mContext; - +// 用于日志记录的标签,方便在日志中识别该类相关的输出信息,取固定的字符串"WorkingNote"。 + private static final String TAG = "WorkingNote"; - + // 标记笔记是否已被删除的布尔值,初始化为 `false`,通过相关方法可以设置为 `true` 来表示笔记已被删除状态,在保存等操作中会根据此标记进行相应处理。 + private boolean mIsDeleted; - + // 定义一个接口类型的变量,用于监听笔记设置相关属性(如背景颜色、提醒等)变化的情况,外部类可以实现该接口并注册进来,以便在属性改变时能收到通知并执行相应逻辑。 + private NoteSettingChangedListener mNoteSettingStatusListener; - +// 定义一个字符串数组,用于查询笔记数据时指定要获取的列,包含了数据的ID、内容、MIME类型以及一些其他自定义的数据字段(DATA1 - DATA4),方便从数据库中获取完整的笔记数据信息。 + public static final String[] DATA_PROJECTION = new String[] { DataColumns.ID, DataColumns.CONTENT, @@ -71,7 +88,8 @@ public class WorkingNote { DataColumns.DATA3, DataColumns.DATA4, }; - + // 定义一个字符串数组,用于查询笔记基本属性时指定要获取的列,包含了父文件夹ID、提醒日期、背景颜色ID、小部件ID、小部件类型以及修改日期等字段,用于从数据库中获取笔记的关键属性信息。 + public static final String[] NOTE_PROJECTION = new String[] { NoteColumns.PARENT_ID, NoteColumns.ALERTED_DATE, @@ -80,27 +98,39 @@ public class WorkingNote { NoteColumns.WIDGET_TYPE, NoteColumns.MODIFIED_DATE }; - + // 定义一个常量,表示在 `DATA_PROJECTION` 数组中数据ID列的索引位置,方便在查询结果的游标(Cursor)中准确获取对应的数据,值为0,对应数组的第一个元素位置。 + private static final int DATA_ID_COLUMN = 0; - + // 定义一个常量,表示在 `DATA_PROJECTION` 数组中数据内容列的索引位置,用于从游标中获取笔记的具体内容数据,值为1。 + private static final int DATA_CONTENT_COLUMN = 1; - +// 定义一个常量,表示在 `DATA_PROJECTION` 数组中数据MIME类型列的索引位置,用于判断数据的类型(如文本笔记、通话笔记等),值为2。 + private static final int DATA_MIME_TYPE_COLUMN = 2; - + // 定义一个常量,表示在 `DATA_PROJECTION` 数组中数据模式列(可能用于表示笔记的特定展示或编辑模式)的索引位置,值为3。 + private static final int DATA_MODE_COLUMN = 3; - + // 定义一个常量,表示在 `NOTE_PROJECTION` 数组中父文件夹ID列的索引位置,用于从游标中获取笔记所属文件夹的ID信息,值为0。 + private static final int NOTE_PARENT_ID_COLUMN = 0; - + // 定义一个常量,表示在 `NOTE_PROJECTION` 数组中提醒日期列的索引位置,用于获取笔记的提醒时间信息,值为1。 + private static final int NOTE_ALERTED_DATE_COLUMN = 1; - + // 定义一个常量,表示在 `NOTE_PROJECTION` 数组中背景颜色ID列的索引位置,用于获取笔记的背景颜色设置信息,值为2。 + private static final int NOTE_BG_COLOR_ID_COLUMN = 2; - + // 定义一个常量,表示在 `NOTE_PROJECTION` 数组中小部件ID列的索引位置,用于获取与笔记关联的小部件的ID信息,值为3。 + private static final int NOTE_WIDGET_ID_COLUMN = 3; - - private static final int NOTE_WIDGET_TYPE_COLUMN = 4; - + // 定义一个常量,表示在 `NOTE_PROJECTION` 数组中小部件类型列的索引位置,用于获取小部件的类型信息,值为4。 + + private static fina int NOTE_WIDGET_TYPE_COLUMN = 4; +// 定义一个常量,表示在 `NOTE_PROJECTION` 数组中修改日期列的索引位置,用于获取笔记最后一次被修改的时间信息,值为5。 + private static final int NOTE_MODIFIED_DATE_COLUMN = 5; - + // 私有构造函数,用于创建一个新的(空的)工作笔记对象,接收应用上下文(Context)和所属文件夹的ID作为参数,初始化各种属性的初始值, + // 如提醒日期设为0,修改日期设为当前系统时间,创建一个新的 `Note` 对象用于后续数据操作,笔记ID初始化为0,标记为未删除状态,笔记模式设为0,小部件类型设为无效类型等。 + // New note construct private WorkingNote(Context context, long folderId) { mContext = context; @@ -113,7 +143,9 @@ public class WorkingNote { mMode = 0; mWidgetType = Notes.TYPE_WIDGET_INVALIDE; } - +// 私有构造函数,用于加载一个已存在的笔记对象,接收应用上下文(Context)、笔记的ID(noteId)以及所属文件夹的ID作为参数,初始化相关属性, + // 然后调用 `loadNote` 方法从数据库中加载笔记的基本属性信息,再调用 `loadNoteData` 方法加载笔记的具体数据内容(如文本内容等)。 + // Existing note construct private WorkingNote(Context context, long noteId, long folderId) { mContext = context; @@ -124,7 +156,11 @@ public class WorkingNote { loadNote(); } - private void loadNote() { + // 私有方法,用于从数据库中加载笔记的基本属性信息,通过内容解析器(ContentResolver)根据笔记的ID查询指定列(`NOTE_PROJECTION`)的数据, + // 如果查询到游标(Cursor)不为空且能移动到第一条记录(表示有数据),则从游标中获取父文件夹ID、背景颜色ID、小部件ID、小部件类型、提醒日期以及修改日期等属性值, + // 最后关闭游标,若游标为空则表示找不到对应的笔记,记录错误日志并抛出异常表示无法找到指定ID的笔记。 + + private void loadNote() Cursor cursor = mContext.getContentResolver().query( ContentUris.withAppendedId(Notes.CONTENT_NOTE_URI, mNoteId), NOTE_PROJECTION, null, null, null); @@ -145,7 +181,10 @@ public class WorkingNote { } loadNoteData(); } - +// 私有方法,用于从数据库中加载笔记的具体数据内容,通过内容解析器查询笔记对应的数据(根据 `DATA_PROJECTION` 指定列),条件是数据所属的笔记ID与当前笔记ID匹配, + // 如果查询到游标不为空且能移动到第一条记录,则循环遍历游标(因为可能有多条数据,比如不同类型的数据),根据数据的MIME类型判断是文本笔记还是通话笔记等, + // 分别进行相应的处理(如设置文本数据ID、通话数据ID或者记录错误日志表示类型错误),最后关闭游标,若游标为空则表示找不到对应笔记的数据,记录错误日志并抛出异常。 + private void loadNoteData() { Cursor cursor = mContext.getContentResolver().query(Notes.CONTENT_DATA_URI, DATA_PROJECTION, DataColumns.NOTE_ID + "=?", new String[] { @@ -173,7 +212,9 @@ public class WorkingNote { throw new IllegalArgumentException("Unable to find note's data with id " + mNoteId); } } - +// 静态工厂方法,用于创建一个新的空笔记对象,接收应用上下文(Context)、所属文件夹的ID、小部件ID、小部件类型以及默认背景颜色资源ID作为参数, + // 创建一个 `WorkingNote` 对象,设置其背景颜色ID、小部件ID和小部件类型属性值,最后返回创建好的对象,方便在需要创建新笔记时统一调用此方法进行初始化操作。 + public static WorkingNote createEmptyNote(Context context, long folderId, int widgetId, int widgetType, int defaultBgColorId) { WorkingNote note = new WorkingNote(context, folderId); @@ -182,7 +223,9 @@ public class WorkingNote { note.setWidgetType(widgetType); return note; } - + // 静态工厂方法,用于加载一个已存在的笔记对象,接收应用上下文(Context)和笔记的ID作为参数,通过调用私有构造函数(传入相应参数)创建并返回对应的 `WorkingNote` 对象, + // 方便在需要获取并操作已有笔记时统一使用此方法进行加载操作。 + public static WorkingNote load(Context context, long id) { return new WorkingNote(context, id, 0); } @@ -211,11 +254,17 @@ public class WorkingNote { return false; } } - + // 同步方法,用于保存笔记的相关数据到数据库,首先通过 `isWorthSaving` 方法判断笔记是否值得保存(比如是否已删除、内容是否为空且不存在于数据库、是否有本地修改等情况), + // 如果值得保存,若笔记不存在于数据库中(通过 `existInDatabase` 方法判断,即笔记ID小于等于0),则调用 `Note` 类的 `getNewNoteId` 方法尝试创建一个新的笔记ID, + // 如果创建失败则记录错误日志并返回 `false`,若创建成功则获取到新的笔记ID。然后调用 `mNote`(`Note` 类对象)的 `syncNote` 方法将笔记数据同步到数据库中。 + // 最后,如果笔记关联了有效的小部件(小部件ID不为无效值且小部件类型有效)并且设置了 `NoteSettingChangedListener` 监听器,则调用监听器的 `onWidgetChanged` 方法通知小部件相关内容可能已改变, + // 整个保存操作成功则返回 `true`,否则返回 `false`。 + public boolean existInDatabase() { return mNoteId > 0; } - + // 用于判断笔记是否已经存在于数据库中的方法,通过检查笔记的ID是否大于0来返回相应的布尔值,如果大于0则表示已存在,否则表示不存在。 + private boolean isWorthSaving() { if (mIsDeleted || (!existInDatabase() && TextUtils.isEmpty(mContent)) || (existInDatabase() && !mNote.isLocalModified())) { @@ -224,11 +273,15 @@ public class WorkingNote { return true; } } - + // 私有方法,用于判断笔记是否值得保存,根据多种情况综合判断,比如如果笔记已被标记为删除(`mIsDeleted` 为 `true`), + // 或者笔记不存在于数据库中且内容为空(通过 `TextUtils.isEmpty` 判断内容是否为空字符串),或者笔记已存在于数据库中但没有本地修改(通过 `mNote.isLocalModified` 判断), + // 这些情况下返回 `false`,表示不值得保存,否则返回 `true`,表示值得保存。 public void setOnSettingStatusChangedListener(NoteSettingChangedListener l) { mNoteSettingStatusListener = l; } - + // 用于设置笔记设置状态变化监听器的方法,接收一个实现了 `NoteSettingChangedListener` 接口的对象作为参数, + // 将其赋值给 `mNoteSettingStatusListener` 变量,这样当笔记的相关设置属性(如背景颜色、提醒等)发生变化时,对应的实现类中的方法就会被调用,实现相应的业务逻辑处理。 + public void setAlertDate(long date, boolean set) { if (date != mAlertDate) { mAlertDate = date; @@ -238,7 +291,10 @@ public class WorkingNote { mNoteSettingStatusListener.onClockAlertChanged(date, set); } } - + // 用于标记笔记是否被删除的方法,接收一个布尔值(`mark`)作为参数,将 `mIsDeleted` 属性设置为传入的布尔值, + // 如果笔记关联了有效的小部件(小部件ID不为无效值且小部件类型有效)并且设置了 `NoteSettingChangedListener` 监听器,则调用监听器的 `onWidgetChanged` 方法通知小部件相关内容可能已改变, + // 以此来应对笔记删除状态变化后可能需要的业务逻辑处理(比如小部件中对应笔记的显示更新等)。 + public void markDeleted(boolean mark) { mIsDeleted = mark; if (mWidgetId != AppWidgetManager.INVALID_APPWIDGET_ID @@ -246,7 +302,10 @@ public class WorkingNote { mNoteSettingStatusListener.onWidgetChanged(); } } - + // 用于设置笔记背景颜色资源ID的方法,接收一个整数(`id`)作为参数, + // 首先比较传入的ID和当前的背景颜色ID是否不同,如果不同则更新 `mBgColorId` 属性为传入的ID,并通过 `mNote` 对象调用 `setNoteValue` 方法将背景颜色ID的值设置到笔记的对应属性中, + // 然后如果设置了 `NoteSettingChangedListener` 监听器,则调用其 `onBackgroundColorChanged` 方法通知背景颜色已改变,以便外部进行相应的界面更新等操作。 + public void setBgColorId(int id) { if (id != mBgColorId) { mBgColorId = id; @@ -256,7 +315,10 @@ public class WorkingNote { mNote.setNoteValue(NoteColumns.BG_COLOR_ID, String.valueOf(id)); } } - +// 用于设置笔记的清单模式(可能用于切换笔记的展示或编辑模式为清单形式等)的方法,接收一个整数(`mode`)作为参数, + // 首先比较传入的模式和当前的模式是否不同,如果不同则更新 `mMode` 属性为传入的模式,并通过 `mNote` 对象调用 `setTextData` 方法将模式的值设置到笔记的对应属性中, + // 然后如果设置了 `NoteSettingChangedListener` 监听器,则调用其 `onCheckListModeChanged` 方法通知清单模式已改变,同时传入旧模式和新模式的值,方便外部进行相应的业务逻辑处理(比如界面切换显示等)。 + public void setCheckListMode(int mode) { if (mMode != mode) { if (mNoteSettingStatusListener != null) { @@ -266,13 +328,17 @@ public class WorkingNote { mNote.setTextData(TextNote.MODE, String.valueOf(mMode)); } } - +// 用于设置笔记关联的桌面小部件类型的方法,接收一个整数(`type`)作为参数, + // 比较传入的类型和当前的小部件类型是否不同,如果不同则更新 `mWidgetType` 属性为传入的类型,并通过 `mNote` 对象调用 `setNoteValue` 方法将小部件类型的值设置到笔记的对应属性中。 + public void setWidgetType(int type) { if (type != mWidgetType) { mWidgetType = type; mNote.setNoteValue(NoteColumns.WIDGET_TYPE, String.valueOf(mWidgetType)); } - } + } // 用于设置笔记关联的桌面小部件ID的方法,接收一个整数(`id`)作为参数, + // 比较传入的ID和当前的小部件ID是否不同,如果不同则更新 `mWidgetId` 属性为传入的ID,并通过 `mNote` 对象调用 `setNoteValue` 方法将小部件ID的值设置到笔记的对应属性中。 + public void setWidgetId(int id) { if (id != mWidgetId) { @@ -280,68 +346,89 @@ public class WorkingNote { mNote.setNoteValue(NoteColumns.WIDGET_ID, String.valueOf(mWidgetId)); } } - + // 用于设置笔记的工作文本内容(即核心文本数据)的方法,接收一个字符串(`text`)作为参数, + // 比较传入的文本和当前的文本内容是否相同(通过 `TextUtils.equals` 判断),如果不同则更新 `mContent` 属性为传入的文本,并通过 `mNote` 对象调用 `setTextData` 方法将文本内容设置到笔记的对应属性中。 + public void setWorkingText(String text) { if (!TextUtils.equals(mContent, text)) { mContent = text; mNote.setTextData(DataColumns.CONTENT, mContent); } } - + // 用于将笔记转换为通话笔记的方法,接收电话号码(`phoneNumber`)和通话日期(`callDate`)作为参数, + // 通过 `mNote` 对象分别调用 `setCallData` 方法设置通话日期和电话号码到笔记的对应属性中,同时调用 `setNoteValue` 方法将笔记的父文件夹ID设置为通话记录文件夹的ID(`Notes.ID_CALL_RECORD_FOLDER`), + // 以此来完成笔记类型向通话笔记的转换,可能涉及到对应数据结构和展示等方面的改变,具体由应用的业务逻辑决定。 + public void convertToCallNote(String phoneNumber, long callDate) { mNote.setCallData(CallNote.CALL_DATE, String.valueOf(callDate)); mNote.setCallData(CallNote.PHONE_NUMBER, phoneNumber); mNote.setNoteValue(NoteColumns.PARENT_ID, String.valueOf(Notes.ID_CALL_RECORD_FOLDER)); } - + // 用于判断笔记是否设置了时钟提醒的方法,通过检查 `mAlertDate` 是否大于0来返回相应的布尔值,如果大于0则表示设置了提醒,返回 `true`,否则返回 `false`。 + public boolean hasClockAlert() { return (mAlertDate > 0 ? true : false); } - +// 用于获取笔记内容的方法,直接返回 `mContent` 属性,即笔记的核心文本数据,方便外部获取并展示笔记的具体内容。 + public String getContent() { return mContent; } - + // 用于获取笔记提醒日期的方法,直接返回 `mAlertDate` 属性,即笔记设置的提醒时间点,方便外部查询和使用该提醒日期信息。 + public long getAlertDate() { return mAlertDate; } - + // 用于获取笔记最后修改日期的方法,直接返回 `mModifiedDate` 属性,即笔记最后一次被修改的时间记录,可用于展示、排序等业务场景。 + public long getModifiedDate() { return mModifiedDate; } - + // 用于获取笔记背景颜色资源ID对应的实际背景颜色资源的方法,通过调用 `NoteBgResources` 类的 `getNoteBgResource` 方法(具体实现可能根据资源映射等逻辑获取实际颜色资源), + // 将 `mBgColorId` 作为参数传入,返回获取到的背景颜色资源ID,方便外部进行界面绘制等操作时使用正确的背景颜色资源。 + public int getBgColorResId() { return NoteBgResources.getNoteBgResource(mBgColorId); } - + // 用于获取笔记背景颜色资源ID的方法,直接返回 `mBgColorId` 属性,方便外部获取并记录笔记的背景颜色设置情况。 + public int getBgColorId() { return mBgColorId; } - + // 用于获取笔记标题背景颜色资源ID的方法,通过调用 `NoteBgResources` 类的 `getNoteTitleBgResource` 方法(具体实现类似获取背景颜色资源的逻辑,根据ID获取对应的标题背景颜色资源), + // 将 `mBgColorId` 作为参数传入,返回获取到的标题背景颜色资源ID,方便外部进行界面绘制等操作时使用正确的标题背景颜色资源。 + public int getTitleBgResId() { return NoteBgResources.getNoteTitleBgResource(mBgColorId); } - + // 用于获取笔记的清单模式的方法,直接返回 `mMode` 属性,即笔记当前设置的清单模式值,方便外部获取并判断笔记处于何种展示或编辑模式。 + public int getCheckListMode() { return mMode; - } + } // 用于获取笔记的唯一标识符(ID)的方法,直接返回 `mNoteId` 属性,方便外部在数据库操作、关联查询等场景中使用该笔记的唯一标识。 + public long getNoteId() { return mNoteId; } - + // 用于获取笔记所属文件夹的ID的方法,直接返回 `mFolderId` 属性,方便外部了解笔记在文件系统结构中的位置分类情况,比如用于文件夹相关的查询、统计等操作。 + public long getFolderId() { return mFolderId; } - + // 用于获取笔记关联的桌面小部件ID的方法,直接返回 `mWidgetId` 属性,方便外部获取并操作与笔记相关联的桌面小部件(比如更新小部件显示内容等)。 + public int getWidgetId() { return mWidgetId; } - + // 用于获取笔记关联的桌面小部件类型的方法,直接返回 `mWidgetType` 属性,方便外部获取并判断笔记关联的小部件属于何种类型,以进行相应的业务逻辑处理。 + public int getWidgetType() { return mWidgetType; } - + // 定义一个接口,用于监听笔记设置相关属性变化的情况,外部类需要实现该接口并通过 `setOnSettingStatusChangedListener` 方法注册进来, + // 接口中定义了多个方法,分别对应不同属性变化时的回调通知,比如背景颜色改变、时钟提醒改变、小部件相关改变以及清单模式改变等情况,方便在这些属性变化时执行相应的业务逻辑处理。 + public interface NoteSettingChangedListener { /** * Called when the background color of current note has just changed diff --git a/src/Notes-master/src/net/micode/notes/tool/BackupUtils.java b/src/Notes-master/src/net/micode/notes/tool/BackupUtils.java index 39f6ec4..a95273a 100644 --- a/src/Notes-master/src/net/micode/notes/tool/BackupUtils.java +++ b/src/Notes-master/src/net/micode/notes/tool/BackupUtils.java @@ -35,57 +35,66 @@ import java.io.FileOutputStream; import java.io.IOException; import java.io.PrintStream; - +// BackupUtils类用于处理笔记数据的备份相关功能,支持将笔记数据导出为文本格式等操作 public class BackupUtils { private static final String TAG = "BackupUtils"; + // 单例模式相关,用于保存唯一的BackupUtils实例 // Singleton stuff private static BackupUtils sInstance; - +// 获取BackupUtils的单例实例,若不存在则创建新的实例 public static synchronized BackupUtils getInstance(Context context) { if (sInstance == null) { sInstance = new BackupUtils(context); } return sInstance; } - + /** + * 以下是用于表示备份或恢复状态的常量定义 + */ + // 当前外部存储(SD卡)未挂载的状态码 /** * Following states are signs to represents backup or restore * status */ // Currently, the sdcard is not mounted public static final int STATE_SD_CARD_UNMOUONTED = 0; + // 备份文件不存在的状态码 // The backup file not exist public static final int STATE_BACKUP_FILE_NOT_EXIST = 1; // The data is not well formated, may be changed by other programs + // 数据格式不正确,可能被其他程序修改的状态码 public static final int STATE_DATA_DESTROIED = 2; // Some run-time exception which causes restore or backup fails + // 运行时出现异常导致备份或恢复失败的状态码 public static final int STATE_SYSTEM_ERROR = 3; // Backup or restore success + // 备份或恢复成功的状态码 public static final int STATE_SUCCESS = 4; private TextExport mTextExport; - + // 私有构造函数,初始化时创建TextExport对象,用于文本导出相关操作 private BackupUtils(Context context) { mTextExport = new TextExport(context); } - +// 判断外部存储(通常指SD卡)是否可用,通过比较外部存储状态与MEDIA_MOUNTED常量来判断 private static boolean externalStorageAvailable() { return Environment.MEDIA_MOUNTED.equals(Environment.getExternalStorageState()); } - + // 调用TextExport的exportToText方法进行文本导出,并返回导出结果状态码 public int exportToText() { return mTextExport.exportToText(); } - + // 获取导出的文本文件名,实际是通过TextExport对象获取其内部保存的文件名 public String getExportedTextFileName() { return mTextExport.mFileName; } - + // 获取导出的文本文件所在目录,通过TextExport对象获取其内部保存的文件目录 public String getExportedTextFileDir() { return mTextExport.mFileDirectory; } - + // TextExport内部类,主要负责具体的文本导出逻辑实现 private static class TextExport { + // 查询笔记相关信息的投影数组,定义了要从数据库中获取的笔记字段 private static final String[] NOTE_PROJECTION = { NoteColumns.ID, NoteColumns.MODIFIED_DATE, @@ -98,7 +107,7 @@ public class BackupUtils { private static final int NOTE_COLUMN_MODIFIED_DATE = 1; private static final int NOTE_COLUMN_SNIPPET = 2; - + // 查询笔记数据相关信息的投影数组,定义了要从数据库中获取的笔记数据字段 private static final String[] DATA_PROJECTION = { DataColumns.CONTENT, DataColumns.MIME_TYPE, @@ -116,6 +125,7 @@ public class BackupUtils { private static final int DATA_COLUMN_PHONE_NUMBER = 4; + // 用于格式化导出文本内容的字符串数组,通过资源获取,不同索引对应不同的格式化用途 private final String [] TEXT_FORMAT; private static final int FORMAT_FOLDER_NAME = 0; private static final int FORMAT_NOTE_DATE = 1; @@ -124,22 +134,25 @@ public class BackupUtils { private Context mContext; private String mFileName; private String mFileDirectory; - + // 构造函数,初始化TEXT_FORMAT数组以及保存传入的上下文对象等信息 public TextExport(Context context) { TEXT_FORMAT = context.getResources().getStringArray(R.array.format_for_exported_note); mContext = context; mFileName = ""; mFileDirectory = ""; } - + // 根据传入的索引获取对应的格式化字符串 private String getFormat(int id) { return TEXT_FORMAT[id]; } - + /** + * 将指定文件夹(通过folderId标识)中的笔记导出为文本格式,并写入到给定的PrintStream中 + */ /** * Export the folder identified by folder id to text */ private void exportFolderToText(String folderId, PrintStream ps) { + // 通过内容解析器查询属于该文件夹的笔记信息 // Query notes belong to this folder Cursor notesCursor = mContext.getContentResolver().query(Notes.CONTENT_NOTE_URI, NOTE_PROJECTION, NoteColumns.PARENT_ID + "=?", new String[] { @@ -148,24 +161,29 @@ public class BackupUtils { if (notesCursor != null) { if (notesCursor.moveToFirst()) { - do { + do { // 格式化并打印笔记的最后修改日期 // Print note's last modified date ps.println(String.format(getFormat(FORMAT_NOTE_DATE), DateFormat.format( mContext.getString(R.string.format_datetime_mdhm), notesCursor.getLong(NOTE_COLUMN_MODIFIED_DATE)))); + // 获取当前笔记的ID,用于进一步查询该笔记相关的数据信息并导出 // Query data belong to this note String noteId = notesCursor.getString(NOTE_COLUMN_ID); exportNoteToText(noteId, ps); } while (notesCursor.moveToNext()); } + // 关闭游标,释放资源 notesCursor.close(); } } - + /** + * 将指定ID的笔记导出为文本格式,并写入到给定的PrintStream中 + */ /** * Export note identified by id to a print stream */ private void exportNoteToText(String noteId, PrintStream ps) { + // 通过内容解析器查询属于该笔记的相关数据信息 Cursor dataCursor = mContext.getContentResolver().query(Notes.CONTENT_DATA_URI, DATA_PROJECTION, DataColumns.NOTE_ID + "=?", new String[] { noteId @@ -174,6 +192,7 @@ public class BackupUtils { if (dataCursor != null) { if (dataCursor.moveToFirst()) { do { + // 获取电话号码并打印(如果非空) String mimeType = dataCursor.getString(DATA_COLUMN_MIME_TYPE); if (DataConstants.CALL_NOTE.equals(mimeType)) { // Print phone number @@ -185,10 +204,12 @@ public class BackupUtils { ps.println(String.format(getFormat(FORMAT_NOTE_CONTENT), phoneNumber)); } + // 格式化并打印通话日期 // Print call date ps.println(String.format(getFormat(FORMAT_NOTE_CONTENT), DateFormat .format(mContext.getString(R.string.format_datetime_mdhm), callDate))); + // 打印通话附件位置(如果非空) // Print call attachment location if (!TextUtils.isEmpty(location)) { ps.println(String.format(getFormat(FORMAT_NOTE_CONTENT), @@ -203,8 +224,10 @@ public class BackupUtils { } } while (dataCursor.moveToNext()); } + // 关闭游标,释放资源 dataCursor.close(); } + // 在每个笔记内容之间写入换行符,用于分隔不同笔记内容 // print a line separator between note try { ps.write(new byte[] { @@ -214,11 +237,14 @@ public class BackupUtils { Log.e(TAG, e.toString()); } } - + /** + * 执行将笔记导出为用户可读的文本格式的主要逻辑,整合了文件夹和笔记的导出操作 + */ /** * Note will be exported as text which is user readable */ public int exportToText() { + // 首先判断外部存储是否可用,如果不可用则返回对应状态码并记录日志 if (!externalStorageAvailable()) { Log.d(TAG, "Media was not mounted"); return STATE_SD_CARD_UNMOUONTED; @@ -229,6 +255,7 @@ public class BackupUtils { Log.e(TAG, "get print stream error"); return STATE_SYSTEM_ERROR; } + // 第一步:查询并导出文件夹(特定条件筛选的文件夹,如通话记录文件夹等)及其内部笔记信息 // First export folder and its notes Cursor folderCursor = mContext.getContentResolver().query( Notes.CONTENT_NOTE_URI, @@ -253,10 +280,10 @@ public class BackupUtils { String folderId = folderCursor.getString(NOTE_COLUMN_ID); exportFolderToText(folderId, ps); } while (folderCursor.moveToNext()); - } + } // 关闭游标,释放资源 folderCursor.close(); } - + // 第二步:查询并导出根文件夹下的笔记信息 // Export notes in root's folder Cursor noteCursor = mContext.getContentResolver().query( Notes.CONTENT_NOTE_URI, @@ -274,14 +301,16 @@ public class BackupUtils { String noteId = noteCursor.getString(NOTE_COLUMN_ID); exportNoteToText(noteId, ps); } while (noteCursor.moveToNext()); - } + } // 关闭游标,释放资源 noteCursor.close(); } ps.close(); - + // 关闭PrintStream,完成文件写入操作 return STATE_SUCCESS; } - + /** + * 获取一个指向要导出的文本文件的PrintStream对象,用于后续写入导出内容 + */ /** * Get a print stream pointed to the file {@generateExportedTextFile} */ @@ -308,7 +337,9 @@ public class BackupUtils { return ps; } } - + /** + * 在外部存储(SD卡)上生成用于存储导入数据的文本文件,如果生成过程出现异常则返回null + */ /** * Generate the text file to store imported data */ diff --git a/src/Notes-master/src/net/micode/notes/tool/DataUtils.java b/src/Notes-master/src/net/micode/notes/tool/DataUtils.java index 2a14982..85123d3 100644 --- a/src/Notes-master/src/net/micode/notes/tool/DataUtils.java +++ b/src/Notes-master/src/net/micode/notes/tool/DataUtils.java @@ -33,88 +33,118 @@ import net.micode.notes.ui.NotesListAdapter.AppWidgetAttribute; import java.util.ArrayList; import java.util.HashSet; +// DataUtils类提供了一系列与笔记数据操作相关的实用方法,例如批量删除笔记、移动笔记到文件夹、查询各种数据状态等 public class DataUtils { public static final String TAG = "DataUtils"; + // 批量删除给定ID集合对应的笔记,跳过系统根文件夹(Notes.ID_ROOT_FOLDER)的删除操作 public static boolean batchDeleteNotes(ContentResolver resolver, HashSet ids) { + // 如果传入的ID集合为null,记录日志并返回true,表示无需执行删除操作 if (ids == null) { Log.d(TAG, "the ids is null"); return true; - } + }// 如果ID集合为空,记录日志并返回true,表示没有要删除的笔记 if (ids.size() == 0) { Log.d(TAG, "no id is in the hashset"); return true; } - + // 用于存储一系列内容提供器操作的列表,后续将批量执行这些操作来删除笔记 ArrayList operationList = new ArrayList(); for (long id : ids) { + // 不允许删除系统根文件夹,若当前ID是系统根文件夹ID,则记录错误日志并跳过该ID的删除操作 if(id == Notes.ID_ROOT_FOLDER) { Log.e(TAG, "Don't delete system folder root"); continue; - } + } // 创建一个用于删除指定笔记的内容提供器操作构建器,指定要删除的笔记的URI(通过ID构建) ContentProviderOperation.Builder builder = ContentProviderOperation .newDelete(ContentUris.withAppendedId(Notes.CONTENT_NOTE_URI, id)); + // 将构建好的操作添加到操作列表中 operationList.add(builder.build()); } try { + // 批量应用内容提供器操作列表,执行删除笔记的操作,并获取操作结果数组 ContentProviderResult[] results = resolver.applyBatch(Notes.AUTHORITY, operationList); + // 如果结果数组为null,或者长度为0,或者第一个结果为null,表示删除笔记失败,记录日志并返回false + if (results == null || results.length == 0 || results[0] == null) { Log.d(TAG, "delete notes failed, ids:" + ids.toString()); return false; } + // 如果成功执行删除操作,返回true return true; } catch (RemoteException e) { + // 捕获远程异常,记录详细的错误日志(包含异常的toString和getMessage信息) Log.e(TAG, String.format("%s: %s", e.toString(), e.getMessage())); } catch (OperationApplicationException e) { + // 捕获操作应用异常,记录详细的错误日志(包含异常的toString和getMessage信息) Log.e(TAG, String.format("%s: %s", e.toString(), e.getMessage())); } return false; } - + // 将指定ID的笔记从源文件夹移动到目标文件夹,通过更新笔记的相关字段来实现移动操作 public static void moveNoteToFoler(ContentResolver resolver, long id, long srcFolderId, long desFolderId) { + // 创建一个ContentValues对象,用于存储要更新的字段和对应的值 ContentValues values = new ContentValues(); + // 设置笔记的新父文件夹ID(目标文件夹ID) values.put(NoteColumns.PARENT_ID, desFolderId); + // 设置笔记的原始父文件夹ID(源文件夹ID) values.put(NoteColumns.ORIGIN_PARENT_ID, srcFolderId); + // 设置本地修改标志为1,表示该笔记有本地修改操作 values.put(NoteColumns.LOCAL_MODIFIED, 1); + // 通过内容解析器更新指定笔记(根据ID确定)的相关字段信息 resolver.update(ContentUris.withAppendedId(Notes.CONTENT_NOTE_URI, id), values, null, null); } - + // 批量将给定ID集合中的笔记移动到指定的文件夹,通过构建一系列更新操作并批量执行来实现 public static boolean batchMoveToFolder(ContentResolver resolver, HashSet ids, long folderId) { + // 如果传入的ID集合为null,记录日志并返回true,表示无需执行移动操作 if (ids == null) { Log.d(TAG, "the ids is null"); return true; } - + // 用于存储一系列内容提供器操作的列表,后续将批量执行这些操作来移动笔记 ArrayList operationList = new ArrayList(); for (long id : ids) { + // 创建一个用于更新指定笔记的内容提供器操作构建器,指定要更新的笔记的URI(通过ID构建) ContentProviderOperation.Builder builder = ContentProviderOperation .newUpdate(ContentUris.withAppendedId(Notes.CONTENT_NOTE_URI, id)); + // 设置笔记的新父文件夹ID(目标文件夹ID) builder.withValue(NoteColumns.PARENT_ID, folderId); + // 设置本地修改标志为1,表示该笔记有本地修改操作 builder.withValue(NoteColumns.LOCAL_MODIFIED, 1); + // 将构建好的操作添加到操作列表中 operationList.add(builder.build()); } try { + // 批量应用内容提供器操作列表,执行移动笔记的操作,并获取操作结果数组 ContentProviderResult[] results = resolver.applyBatch(Notes.AUTHORITY, operationList); + // 如果结果数组为null,或者长度为0,或者第一个结果为null,表示移动笔记失败,记录日志并返回false if (results == null || results.length == 0 || results[0] == null) { Log.d(TAG, "delete notes failed, ids:" + ids.toString()); return false; } + // 如果成功执行移动操作,返回true return true; } catch (RemoteException e) { + // 捕获远程异常,记录详细的错误日志(包含异常的toString和getMessage信息) Log.e(TAG, String.format("%s: %s", e.toString(), e.getMessage())); } catch (OperationApplicationException e) { + // 捕获操作应用异常,记录详细的错误日志(包含异常的toString和getMessage信息) Log.e(TAG, String.format("%s: %s", e.toString(), e.getMessage())); } return false; } - + /** + * 获取除系统文件夹({@link Notes#TYPE_SYSTEM}类型的文件夹)之外的所有文件夹数量,通过数据库查询统计符合条件的文件夹数量 + */ /** * Get the all folder count except system folders {@link Notes#TYPE_SYSTEM}} */ public static int getUserFolderCount(ContentResolver resolver) { + // 执行数据库查询,从Notes.CONTENT_NOTE_URI对应的表中查询符合条件的文件夹数量, + // 查询条件是文件夹类型为普通文件夹(Notes.TYPE_FOLDER)且父文件夹ID不是回收站文件夹ID(Notes.ID_TRASH_FOLER) Cursor cursor =resolver.query(Notes.CONTENT_NOTE_URI, new String[] { "COUNT(*)" }, NoteColumns.TYPE + "=? AND " + NoteColumns.PARENT_ID + "<>?", @@ -125,18 +155,23 @@ public class DataUtils { if(cursor != null) { if(cursor.moveToFirst()) { try { + // 尝试从查询结果游标中获取第一列(即统计的数量值)并赋值给count变量 count = cursor.getInt(0); } catch (IndexOutOfBoundsException e) { + // 若获取数据时出现越界异常,记录错误日志 Log.e(TAG, "get folder count failed:" + e.toString()); } finally { + // 无论是否出现异常,都关闭游标,释放资源 cursor.close(); } } } return count; } - + // 检查指定ID和类型的笔记是否在笔记数据库中可见(不在回收站文件夹中),通过查询数据库判断是否存在符合条件的笔记 + public static boolean visibleInNoteDatabase(ContentResolver resolver, long noteId, int type) { + // 执行数据库查询,从Notes.CONTENT_NOTE_URI对应的表中查询指定ID和类型且不在回收站文件夹中的笔记是否存在 Cursor cursor = resolver.query(ContentUris.withAppendedId(Notes.CONTENT_NOTE_URI, noteId), null, NoteColumns.TYPE + "=? AND " + NoteColumns.PARENT_ID + "<>" + Notes.ID_TRASH_FOLER, @@ -145,60 +180,73 @@ public class DataUtils { boolean exist = false; if (cursor != null) { + // 如果查询结果游标中的记录数量大于0,表示存在符合条件的笔记,将exist设为true if (cursor.getCount() > 0) { exist = true; } + // 关闭游标,释放资源 cursor.close(); } return exist; } - +// 检查指定ID的笔记是否存在于笔记数据库中,通过简单查询判断是否有对应记录 public static boolean existInNoteDatabase(ContentResolver resolver, long noteId) { - Cursor cursor = resolver.query(ContentUris.withAppendedId(Notes.CONTENT_NOTE_URI, noteId), + // 执行数据库查询,从Notes.CONTENT_NOTE_URI对应的表中查询指定ID的笔记是否存在,无其他额外查询条件 + Cursor cursor = resolver.query(ContentUris.withAppendedId(Notes.CONTENT_NOTE_URI, noteId), null, null, null, null); boolean exist = false; if (cursor != null) { + // 如果查询结果游标中的记录数量大于0,表示存在符合条件的笔记,将exist设为true if (cursor.getCount() > 0) { exist = true; } + // 关闭游标,释放资源 cursor.close(); } return exist; } - + // 检查指定ID的数据是否存在于数据数据库中(此处指与笔记相关的数据,例如可能是附件等数据),通过查询判断是否有对应记录 + public static boolean existInDataDatabase(ContentResolver resolver, long dataId) { + // 执行数据库查询,从Notes.CONTENT_DATA_URI对应的表中查询指定ID的数据是否存在,无其他额外查询条件 Cursor cursor = resolver.query(ContentUris.withAppendedId(Notes.CONTENT_DATA_URI, dataId), null, null, null, null); boolean exist = false; if (cursor != null) { + // 如果查询结果游标中的记录数量大于0,表示存在符合条件的数据,将exist设为true if (cursor.getCount() > 0) { exist = true; } + // 关闭游标,释放资源 cursor.close(); } return exist; } - + // 检查指定名称的文件夹是否在可见文件夹中(非系统文件夹且不在回收站文件夹中),通过查询数据库判断是否存在符合条件的文件夹 public static boolean checkVisibleFolderName(ContentResolver resolver, String name) { - Cursor cursor = resolver.query(Notes.CONTENT_NOTE_URI, null, + // 执行数据库查询,从Notes.CONTENT_NOTE_URI对应的表中查询指定名称、类型为文件夹且不在回收站文件夹中的文件夹是否存在 + Cursor cursor = resolver.query(Notes.CONTENT_NOTE_URI, null, NoteColumns.TYPE + "=" + Notes.TYPE_FOLDER + " AND " + NoteColumns.PARENT_ID + "<>" + Notes.ID_TRASH_FOLER + " AND " + NoteColumns.SNIPPET + "=?", new String[] { name }, null); boolean exist = false; if(cursor != null) { + // 如果查询结果游标中的记录数量大于0,表示存在符合条件的文件夹,将exist设为true if(cursor.getCount() > 0) { exist = true; } + // 关闭游标,释放资源 cursor.close(); } return exist; } - + // 获取指定文件夹下所有笔记对应的小部件属性集合,通过查询数据库获取相关信息并构建属性集合 public static HashSet getFolderNoteWidget(ContentResolver resolver, long folderId) { - Cursor c = resolver.query(Notes.CONTENT_NOTE_URI, + // 执行数据库查询,从Notes.CONTENT_NOTE_URI对应的表中查询指定文件夹下笔记的小部件ID和小部件类型信息 + Cursor c = resolver.query(Notes.CONTENT_NOTE_URI, new String[] { NoteColumns.WIDGET_ID, NoteColumns.WIDGET_TYPE }, NoteColumns.PARENT_ID + "=?", new String[] { String.valueOf(folderId) }, @@ -210,40 +258,48 @@ public class DataUtils { set = new HashSet(); do { try { + // 构建一个AppWidgetAttribute对象,用于存储小部件的属性信息 AppWidgetAttribute widget = new AppWidgetAttribute(); + // 从查询结果游标中获取小部件ID并赋值给widget对象的对应属性 widget.widgetId = c.getInt(0); + // 从查询结果游标中获取小部件类型并赋值给widget对象的对应属性 widget.widgetType = c.getInt(1); + // 将构建好的widget对象添加到属性集合中 set.add(widget); } catch (IndexOutOfBoundsException e) { + // 若获取数据时出现越界异常,记录错误日志 Log.e(TAG, e.toString()); } } while (c.moveToNext()); - } + } // 关闭游标,释放资源 c.close(); } return set; - } + } // 根据笔记ID获取对应的电话号码(从通话记录相关的笔记数据中获取),通过数据库查询获取电话号码信息 public static String getCallNumberByNoteId(ContentResolver resolver, long noteId) { - Cursor cursor = resolver.query(Notes.CONTENT_DATA_URI, + // 执行数据库查询,从Notes.CONTENT_DATA_URI对应的表中查询指定笔记ID且类型为通话记录相关的电话号码信息 + Cursor cursor = resolver.query(Notes.CONTENT_DATA_URI, new String [] { CallNote.PHONE_NUMBER }, CallNote.NOTE_ID + "=? AND " + CallNote.MIME_TYPE + "=?", new String [] { String.valueOf(noteId), CallNote.CONTENT_ITEM_TYPE }, null); if (cursor != null && cursor.moveToFirst()) { - try { + try { // 尝试从查询结果游标中获取电话号码字符串并返回 return cursor.getString(0); - } catch (IndexOutOfBoundsException e) { + } catch (IndexOutOfBoundsException e) { // 若获取数据时出现越界异常,记录错误日志 Log.e(TAG, "Get call number fails " + e.toString()); - } finally { + } finally { // 关闭游标,释放资源 cursor.close(); } } return ""; } + // 根据电话号码和通话日期获取对应的笔记ID(从通话记录相关数据中查找匹配的笔记),通过数据库查询获取笔记ID信息 public static long getNoteIdByPhoneNumberAndCallDate(ContentResolver resolver, String phoneNumber, long callDate) { + // 执行数据库查询,从Notes.CONTENT_DATA_URI对应的表中查询指定电话号码、通话日期且类型为通话记录相关的笔记ID信息 Cursor cursor = resolver.query(Notes.CONTENT_DATA_URI, new String [] { CallNote.NOTE_ID }, CallNote.CALL_DATE + "=? AND " + CallNote.MIME_TYPE + "=? AND PHONE_NUMBERS_EQUAL(" @@ -253,18 +309,19 @@ public class DataUtils { if (cursor != null) { if (cursor.moveToFirst()) { - try { + try { // 尝试从查询结果游标中获取笔记ID并返回 return cursor.getLong(0); - } catch (IndexOutOfBoundsException e) { + } catch (IndexOutOfBoundsException e) { // 若获取数据时出现越界异常,记录错误日志 Log.e(TAG, "Get call note id fails " + e.toString()); } - } + } // 关闭游标,释放资源 cursor.close(); } return 0; } - + // 根据笔记ID获取对应的摘要信息(通常是笔记的简短描述等内容),通过数据库查询获取摘要字符串 public static String getSnippetById(ContentResolver resolver, long noteId) { + // 执行数据库查询,从Notes.CONTENT_NOTE_URI对应的表中查询指定笔记ID的摘要信息 Cursor cursor = resolver.query(Notes.CONTENT_NOTE_URI, new String [] { NoteColumns.SNIPPET }, NoteColumns.ID + "=?", @@ -272,16 +329,18 @@ public class DataUtils { null); if (cursor != null) { + // 若查询到结果,从游标中获取摘要字符串 String snippet = ""; if (cursor.moveToFirst()) { snippet = cursor.getString(0); } + // 关闭游标,释放资源 cursor.close(); return snippet; - } + } // 如果查询失败(游标为null,表示没有找到对应笔记),抛出异常表示未找到指定ID的笔记 throw new IllegalArgumentException("Note is not found with id: " + noteId); } - +// 对传入的摘要字符串进行格式化处理,去除两端空白字符,并截取到换行符之前的内容(如果有换行符) public static String getFormattedSnippet(String snippet) { if (snippet != null) { snippet = snippet.trim(); diff --git a/src/Notes-master/src/net/micode/notes/tool/GTaskStringUtils.java b/src/Notes-master/src/net/micode/notes/tool/GTaskStringUtils.java index 666b729..23aa88a 100644 --- a/src/Notes-master/src/net/micode/notes/tool/GTaskStringUtils.java +++ b/src/Notes-master/src/net/micode/notes/tool/GTaskStringUtils.java @@ -15,99 +15,154 @@ */ package net.micode.notes.tool; - +// GTaskStringUtils类用于定义一系列与GTask操作、数据结构以及相关文件夹、元数据等有关的字符串常量。 +// 这些常量在涉及到GTask相关的JSON数据处理、文件夹标识和元数据管理等功能中会被使用到。 public class GTaskStringUtils { + // 以下是与GTask JSON数据中动作(action)相关的字段名及对应动作类型值的常量定义 + // JSON数据中表示动作(action)的唯一标识符的字段名,在整个操作流程中可用于区分不同的具体操作。 + public final static String GTASK_JSON_ACTION_ID = "action_id"; - + // JSON数据中表示动作列表(action_list)的字段名,通常可能用来存放一组操作相关信息的集合, + // 比如包含多个具体操作的详细描述等内容。 public final static String GTASK_JSON_ACTION_LIST = "action_list"; - +// JSON数据中表示动作类型(action_type)的字段名,通过不同的值来区分具体是哪种操作行为, + // 例如创建、获取、移动、更新等操作。 public final static String GTASK_JSON_ACTION_TYPE = "action_type"; - +// JSON数据中表示创建(create)类型动作的具体值,当"action_type"字段取值为此常量时, + // 意味着对应的操作是创建相关的数据实体等操作。 public final static String GTASK_JSON_ACTION_TYPE_CREATE = "create"; - +// JSON数据中表示获取全部(get_all)类型动作的具体值,用于指示要获取所有相关数据的操作, + // 比如获取所有任务、所有列表等情况。 public final static String GTASK_JSON_ACTION_TYPE_GETALL = "get_all"; - + // JSON数据中表示移动(move)类型动作的具体值,表明对应的操作是将某个数据实体从一个位置移动到另一个位置, + // 例如移动任务到不同的列表等情况。 public final static String GTASK_JSON_ACTION_TYPE_MOVE = "move"; - + // JSON数据中表示更新(update)类型动作的具体值,代表要对已存在的数据实体进行更新修改的操作, + // 像更新任务的名称、属性等操作会用到此标识。 public final static String GTASK_JSON_ACTION_TYPE_UPDATE = "update"; - +// JSON数据中表示创建者(creator)的ID字段名,可用于确定执行创建操作的具体用户或者相关主体的唯一标识, + // 在涉及多用户或者多来源数据创建时,可借此追踪创建源头 public final static String GTASK_JSON_CREATOR_ID = "creator_id"; - + // JSON数据中表示子实体(child_entity)的字段名,可能用于描述某个主实体下包含的子级相关的数据结构, + // 例如一个任务列表下包含的多个子任务等情况可以通过此字段来关联和表示。 public final static String GTASK_JSON_CHILD_ENTITY = "child_entity"; - +// JSON数据中表示客户端版本(client_version)的字段名,主要用于记录使用该GTask相关功能的客户端应用的版本信息, + // 在数据同步、兼容性处理等场景中可根据此版本信息来判断是否支持某些操作或进行相应的适配。 public final static String GTASK_JSON_CLIENT_VERSION = "client_version"; - + // JSON数据中表示是否完成(completed)的字段名,常用于任务相关的数据中,用来标记某个任务是否已经完成, + // 例如任务完成状态的记录与判断会依赖于此字段。 public final static String GTASK_JSON_COMPLETED = "completed"; - +// JSON数据中表示当前列表(current_list_id)的ID字段名,可能用于标识当前操作所涉及的列表的唯一标识, + // 比如在任务移动操作中,可指明当前所在的列表以及要移动到的目标列表等情况。 public final static String GTASK_JSON_CURRENT_LIST_ID = "current_list_id"; - + // JSON数据中表示默认列表(default_list_id)的ID字段名,用于指定某个默认的列表, + // 例如新创建任务时默认归属的列表等情况可以通过此标识来确定。 public final static String GTASK_JSON_DEFAULT_LIST_ID = "default_list_id"; - + // JSON数据中表示是否已删除(deleted)的字段名,可用于标记某个数据实体是否已经被删除, + // 在数据清理、回收站相关功能或者同步时判断数据是否已不存在等场景中会用到。 public final static String GTASK_JSON_DELETED = "deleted"; - +// JSON数据中表示目标列表(dest_list)的字段名,常用于操作涉及到将数据移动或关联到其他列表的情况, + // 比如在移动任务操作中,此字段指明任务要移动到的目标列表的相关信息。 public final static String GTASK_JSON_DEST_LIST = "dest_list"; - + // JSON数据中表示目标父级(dest_parent)的字段名,可用于表示某个数据实体在移动、关联等操作后的目标父级对象相关信息, + // 例如任务移动后所属的新的父任务或者父列表等情况通过此字段来体现。 public final static String GTASK_JSON_DEST_PARENT = "dest_parent"; - + // JSON数据中表示目标父级类型(dest_parent_type)的字段名,配合"dest_parent"字段使用, + // 用于明确目标父级对象具体是什么类型,比如是任务组还是普通任务列表等类型区分。 public final static String GTASK_JSON_DEST_PARENT_TYPE = "dest_parent_type"; - +// JSON数据中表示实体变化量(entity_delta)的字段名,可能用于记录某个数据实体在更新、修改等操作前后的变化差异情况, + // 例如任务的某些属性值发生了改变,通过此字段可以详细记录具体的变化内容。 public final static String GTASK_JSON_ENTITY_DELTA = "entity_delta"; - + // JSON数据中表示实体类型(entity_type)的字段名,用于区分不同的数据实体类型, + // 像任务(TASK)、任务组(GROUP)等不同类型的数据实体可以通过此字段来标识区分。 public final static String GTASK_JSON_ENTITY_TYPE = "entity_type"; - + // JSON数据中表示获取已删除数据(get_deleted)的字段名,可能用于发起获取已被标记为删除的数据的相关操作, + // 比如在回收站功能或者数据同步时,查看哪些数据已被删除等情况会用到。 public final static String GTASK_JSON_GET_DELETED = "get_deleted"; - + // JSON数据中表示数据实体的唯一标识符(id)的字段名,用于唯一确定某个具体的数据实体, + // 无论是任务、列表还是其他相关数据结构,都可以通过此ID进行查找、关联等操作。 public final static String GTASK_JSON_ID = "id"; - + // JSON数据中表示索引(index)的字段名,可用于在有序的数据集合(如任务列表等)中确定某个数据实体的位置顺序, + // 例如在排序、定位特定位置的任务等场景中会使用到。 public final static String GTASK_JSON_INDEX = "index"; - + // JSON数据中表示最后修改时间(last_modified)的字段名,用于记录某个数据实体最后一次被修改的时间戳, + // 在数据同步、版本控制以及判断数据是否有更新等场景中,此时间信息非常关键。 public final static String GTASK_JSON_LAST_MODIFIED = "last_modified"; - + // JSON数据中表示最新同步点(latest_sync_point)的字段名,用于标记在数据同步过程中的最新有效同步位置或者时间点, + // 可帮助确定下次同步从何处开始、哪些数据需要重新同步等情况。 public final static String GTASK_JSON_LATEST_SYNC_POINT = "latest_sync_point"; - +// JSON数据中表示列表(list_id)的ID字段名,用于唯一标识不同的列表, + // 在涉及到多个列表管理、任务在不同列表间移动等操作时,通过此ID来区分不同的列表。 public final static String GTASK_JSON_LIST_ID = "list_id"; - + // JSON数据中表示列表(lists)的字段名,通常可能用来存放多个列表相关信息的集合, + // 例如获取所有列表信息或者返回一组列表数据时,可通过此字段进行表示。 public final static String GTASK_JSON_LISTS = "lists"; - + // JSON数据中表示名称(name)的字段名,常用于表示数据实体(如任务、列表等)的名称属性, + // 方便展示、查找以及用户识别不同的数据实体。 public final static String GTASK_JSON_NAME = "name"; + // JSON数据中表示新的标识符(new_id)的字段名,可能在某些操作(如数据复制、克隆或者重新生成ID等情况)下, + // 用于标记新产生的数据实体的唯一标识。 public final static String GTASK_JSON_NEW_ID = "new_id"; - + // JSON数据中表示笔记(notes)的字段名,可能用于存放与任务、列表等相关的笔记信息, + // 比如用户针对某个任务添加的备注、说明等内容可通过此字段关联存储。 public final static String GTASK_JSON_NOTES = "notes"; - + // JSON数据中表示父级ID(parent_id)的字段名,用于确定某个数据实体所属的父级对象的唯一标识, + // 例如任务所属的任务组或者列表的ID等情况通过此字段关联表示。 public final static String GTASK_JSON_PARENT_ID = "parent_id"; - + // JSON数据中表示前一个兄弟节点ID(prior_sibling_id)的字段名,在有序的数据结构(如任务列表按顺序排列)中, + // 可用于定位某个数据实体前一个相邻的兄弟节点的标识,方便进行顺序调整、插入等操作。 public final static String GTASK_JSON_PRIOR_SIBLING_ID = "prior_sibling_id"; - + // JSON数据中表示操作结果(results)的字段名,常用于存放执行某个操作(如创建、更新、移动等操作)后的返回结果信息, + // 比如操作是否成功、返回的数据等情况通过此字段进行传递和展示。 public final static String GTASK_JSON_RESULTS = "results"; - + // JSON数据中表示源列表(source_list)的字段名,在涉及数据移动、复制等操作时, + // 用于指明操作数据的原始来源列表的相关信息,与目标列表(dest_list)相对应。 public final static String GTASK_JSON_SOURCE_LIST = "source_list"; - + // JSON数据中表示任务(tasks)的字段名,通常用于存放一组任务相关信息的集合, + // 比如获取所有任务列表、返回一批任务数据等情况会通过此字段来表示。 public final static String GTASK_JSON_TASKS = "tasks"; + // JSON数据中表示类型(type)的字段名,和前面提到的"entity_type"类似,用于区分不同的数据实体类型, + // 不过可能使用场景更通用一些,可用于各种需要区分类型的情况。 public final static String GTASK_JSON_TYPE = "type"; - + // JSON数据中表示任务组(GROUP)类型的具体值,用于明确某个数据实体是任务组类型, + // 在通过"type"或者"entity_type"字段判断类型时,此常量可作为对应的值来标识任务组。 public final static String GTASK_JSON_TYPE_GROUP = "GROUP"; - +// JSON数据中表示任务(TASK)类型的具体值,用于确定某个数据实体是普通任务类型, + // 同样在类型判断场景中,以此常量来标识任务类型的数据实体。 public final static String GTASK_JSON_TYPE_TASK = "TASK"; - + // JSON数据中表示用户(user)的字段名,可能用于存放与操作相关的用户信息, + // 比如操作执行者的用户名、用户ID等相关属性,便于记录操作的归属主体等情况。 public final static String GTASK_JSON_USER = "user"; + // 以下是与特定文件夹相关的字符串常量定义,可能用于标识不同功能或用途的文件夹 + // 表示与MIUI笔记相关的文件夹前缀字符串,用于在文件夹命名或者标识中区分出属于MIUI笔记相关的文件夹, + // 方便进行针对性的处理或者识别。 public final static String MIUI_FOLDER_PREFFIX = "[MIUI_Notes]"; - + // 表示默认文件夹的名称常量,可能用于指代某个默认创建或者默认使用的文件夹, + // 例如新笔记、新任务等默认存放的文件夹可以用此名称来表示。 public final static String FOLDER_DEFAULT = "Default"; - + // 表示通话记录笔记相关的文件夹名称常量,用于专门存放与通话记录笔记相关内容的文件夹标识, + // 便于对通话记录相关笔记进行分类管理和查找等操作。 public final static String FOLDER_CALL_NOTE = "Call_Note"; - +// 表示元数据(METADATA)相关的文件夹名称常量,可能用于存放各种数据的元数据信息的文件夹, + // 在数据管理、备份恢复以及数据关联等场景中,此文件夹的元数据起着重要作用。 public final static String FOLDER_META = "METADATA"; + // 以下是与元数据头部相关的字段名常量定义,用于明确元数据中不同部分的标识 + // 表示元数据中GTask的ID(meta_gid)的字段名,在元数据结构里用于存放与之关联的GTask的唯一标识信息, + // 可用于数据关联、同步以及根据GTask查找对应元数据等操作。 public final static String META_HEAD_GTASK_ID = "meta_gid"; - +// 表示元数据中笔记(meta_note)相关信息的字段名,可能用于存放具体的笔记内容或者笔记相关属性等元数据信息, + // 在处理笔记的元数据时,通过此字段来获取对应的笔记相关数据。 public final static String META_HEAD_NOTE = "meta_note"; - +// 表示元数据中其他数据(meta_data)相关信息的字段名,可用于存放除了前面特定标识之外的其他各种数据的元数据, + // 例如一些自定义的扩展数据、辅助数据等的元数据都可以通过此字段来表示和管理。 public final static String META_HEAD_DATA = "meta_data"; - +// 表示元数据中其他数据(meta_data)相关信息的字段名,可用于存放除了前面特定标识之外的其他各种数据的元数据, + // 例如一些自定义的扩展数据、辅助数据等的元数据都可以通过此字段来表示和管理。 public final static String META_NOTE_NAME = "[META INFO] DON'T UPDATE AND DELETE"; } From 518bc5f14d4bda9f08da96ffcbed9f0c14c085dc Mon Sep 17 00:00:00 2001 From: yunci <1185560693@qq.com> Date: Mon, 30 Dec 2024 12:07:18 +0800 Subject: [PATCH 2/4] =?UTF-8?q?Revert=20"=E5=BC=A0=E6=98=8C=E7=9B=9B=20202?= =?UTF-8?q?201002062"?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit This reverts commit 1a3da5bc845365c98894e8f1999363e3beda9d45. --- .../src/net/micode/notes/data/Contact.java | 26 +-- .../notes/data/NotesDatabaseHelper.java | 66 +++--- .../net/micode/notes/data/NotesProvider.java | 67 ++---- .../net/micode/notes/gtask/data/MetaData.java | 36 +--- .../src/net/micode/notes/gtask/data/Node.java | 64 +++--- .../net/micode/notes/gtask/data/SqlData.java | 84 ++++---- .../net/micode/notes/gtask/data/SqlNote.java | 151 ++++++-------- .../src/net/micode/notes/gtask/data/Task.java | 111 ++++------ .../net/micode/notes/gtask/data/TaskList.java | 99 +++------ .../exception/ActionFailureException.java | 13 +- .../exception/NetworkFailureException.java | 17 +- .../notes/gtask/remote/GTaskASyncTask.java | 41 +--- .../notes/gtask/remote/GTaskClient.java | 121 ++++------- .../notes/gtask/remote/GTaskManager.java | 191 ++++++----------- .../notes/gtask/remote/GTaskSyncService.java | 61 ++---- .../src/net/micode/notes/model/Note.java | 88 ++------ .../net/micode/notes/model/WorkingNote.java | 195 +++++------------- .../net/micode/notes/tool/BackupUtils.java | 75 ++----- .../src/net/micode/notes/tool/DataUtils.java | 113 +++------- .../micode/notes/tool/GTaskStringUtils.java | 139 ++++--------- 20 files changed, 568 insertions(+), 1190 deletions(-) diff --git a/src/Notes-master/src/net/micode/notes/data/Contact.java b/src/Notes-master/src/net/micode/notes/data/Contact.java index 16c899f..e1f8942 100644 --- a/src/Notes-master/src/net/micode/notes/data/Contact.java +++ b/src/Notes-master/src/net/micode/notes/data/Contact.java @@ -24,34 +24,29 @@ import android.telephony.PhoneNumberUtils; import android.util.Log; import java.util.HashMap; + //ceshihebing2 -// Contact类,主要用于根据电话号码获取对应的联系人姓名 public class Contact { - // 用于缓存联系人信息(电话号码与对应姓名的映射)的哈希表,以避免重复查询数据库 private static HashMap sContactCache; - // 用于日志记录的标签,方便在日志中识别相关输出所属的类 private static final String TAG = "Contact"; - // 构建查询联系人的SQL语句的选择条件部分,用于从联系人数据中筛选出符合条件的记录 - // 条件基于电话号码匹配、MIME类型匹配以及原始联系人ID的相关限定等 + 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 = '+')"; -// 根据给定的上下文(Context)和电话号码获取对应的联系人姓名 + 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)); Cursor cursor = context.getContentResolver().query( @@ -60,28 +55,19 @@ public class Contact { selection, new String[] { phoneNumber }, null); - // 通过上下文的内容解析器(ContentResolver)发起数据库查询操作 - // 查询联系人数据的相关表(Data.CONTENT_URI),只获取联系人的显示名称(Phone.DISPLAY_NAME)字段 - // 使用上面构建好的选择条件(selection),并传入电话号码作为查询参数 -// 如果查询结果游标(Cursor)不为空,并且游标能够移动到第一条记录(意味着有匹配的联系人数据) + if (cursor != null && cursor.moveToFirst()) { try { - // 从游标中获取联系人的显示名称(假设显示名称在结果集的第0个位置) String name = cursor.getString(0); - // 从游标中获取联系人的显示名称(假设显示名称在结果集的第0个位置) sContactCache.put(phoneNumber, name); - // 返回获取到的联系人姓名 return name; } catch (IndexOutOfBoundsException e) { - // 如果在从游标获取数据时发生越界异常(比如结果集格式不符合预期等情况),记录错误日志 Log.e(TAG, " Cursor get string error " + e.toString()); return null; } finally { - // 无论是否发生异常,都要关闭游标,释放相关资源 cursor.close(); } } else { - // 如果游标为空或者游标中没有匹配的记录,记录调试日志表示没有找到对应联系人 Log.d(TAG, "No contact matched with number:" + phoneNumber); return null; } diff --git a/src/Notes-master/src/net/micode/notes/data/NotesDatabaseHelper.java b/src/Notes-master/src/net/micode/notes/data/NotesDatabaseHelper.java index 2965719..ffe5d57 100644 --- a/src/Notes-master/src/net/micode/notes/data/NotesDatabaseHelper.java +++ b/src/Notes-master/src/net/micode/notes/data/NotesDatabaseHelper.java @@ -25,26 +25,23 @@ 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; -// NotesDatabaseHelper类继承自SQLiteOpenHelper,用于管理与笔记相关的数据库的创建、升级以及一些数据库表结构和触发器的初始化操作 public class NotesDatabaseHelper extends SQLiteOpenHelper { - // 定义数据库的名称,这里是"note.db",即该应用对应的数据库文件名 private static final String DB_NAME = "note.db"; -// 定义数据库的版本号,用于数据库升级等场景判断,当前版本为4 + private static final int DB_VERSION = 4; -// TABLE接口,用于定义数据库中不同表的名称常量,方便在代码中统一引用,避免硬编码表名 + public interface TABLE { public static final String NOTE = "note"; public static final String DATA = "data"; } -// TABLE接口,用于定义数据库中不同表的名称常量,方便在代码中统一引用,避免硬编码表名 + private static final String TAG = "NotesDatabaseHelper"; -// 采用单例模式,保存该类的唯一实例,确保整个应用中只有一个数据库帮助类实例存在 + private static NotesDatabaseHelper mInstance; - // 创建"note"表的SQL语句,定义了表的各个列名、数据类型以及默认值等信息 - // 例如定义了笔记的ID、父级ID、提醒日期、背景颜色ID等列,为笔记相关数据存储做准备 + private static final String CREATE_NOTE_TABLE_SQL = "CREATE TABLE " + TABLE.NOTE + "(" + NoteColumns.ID + " INTEGER PRIMARY KEY," + @@ -65,7 +62,7 @@ public class NotesDatabaseHelper extends SQLiteOpenHelper { NoteColumns.GTASK_ID + " TEXT NOT NULL DEFAULT ''," + NoteColumns.VERSION + " INTEGER NOT NULL DEFAULT 0" + ")"; -// 创建"data"表的SQL语句,同样定义了表的各列信息,用于存储笔记相关的数据内容等信息,如MIME类型、所属笔记ID、创建日期等 + private static final String CREATE_DATA_TABLE_SQL = "CREATE TABLE " + TABLE.DATA + "(" + DataColumns.ID + " INTEGER PRIMARY KEY," + @@ -80,14 +77,13 @@ public class NotesDatabaseHelper extends SQLiteOpenHelper { DataColumns.DATA4 + " TEXT NOT NULL DEFAULT ''," + DataColumns.DATA5 + " TEXT NOT NULL DEFAULT ''" + ")"; -// 创建针对"data"表中NOTE_ID列的索引的SQL语句,有助于提高基于笔记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 - * 当笔记移动到文件夹时,增加文件夹的笔记数量的触发器SQL语句定义 */ private static final String NOTE_INCREASE_FOLDER_COUNT_ON_UPDATE_TRIGGER = "CREATE TRIGGER increase_folder_count_on_update "+ @@ -100,7 +96,6 @@ public class NotesDatabaseHelper extends SQLiteOpenHelper { /** * Decrease folder's note count when move note from folder - * 当笔记移动到文件夹时,增加文件夹的笔记数量的触发器SQL语句定义 */ private static final String NOTE_DECREASE_FOLDER_COUNT_ON_UPDATE_TRIGGER = "CREATE TRIGGER decrease_folder_count_on_update " + @@ -114,7 +109,6 @@ public class NotesDatabaseHelper extends SQLiteOpenHelper { /** * Increase folder's note count when insert new note to the folder - * 当从文件夹中删除笔记时,减少文件夹的笔记数量的触发器SQL语句定义,同样要确保当前笔记数量大于0才进行减少操作 */ private static final String NOTE_INCREASE_FOLDER_COUNT_ON_INSERT_TRIGGER = "CREATE TRIGGER increase_folder_count_on_insert " + @@ -127,7 +121,6 @@ public class NotesDatabaseHelper extends SQLiteOpenHelper { /** * Decrease folder's note count when delete note from the folder - * 当插入类型为DataConstants.NOTE的数据时,更新笔记内容的触发器SQL语句定义 */ private static final String NOTE_DECREASE_FOLDER_COUNT_ON_DELETE_TRIGGER = "CREATE TRIGGER decrease_folder_count_on_delete " + @@ -141,7 +134,6 @@ public class NotesDatabaseHelper extends SQLiteOpenHelper { /** * Update note's content when insert data with type {@link DataConstants#NOTE} - * 当类型为DataConstants.NOTE的数据发生变化时,更新笔记内容的触发器SQL语句定义 */ private static final String DATA_UPDATE_NOTE_CONTENT_ON_INSERT_TRIGGER = "CREATE TRIGGER update_note_content_on_insert " + @@ -155,7 +147,6 @@ public class NotesDatabaseHelper extends SQLiteOpenHelper { /** * Update note's content when data with {@link DataConstants#NOTE} type has changed - * 当类型为DataConstants.NOTE的数据被删除时,更新笔记内容的触发器SQL语句定义,将对应笔记的内容设置为空字符串 */ private static final String DATA_UPDATE_NOTE_CONTENT_ON_UPDATE_TRIGGER = "CREATE TRIGGER update_note_content_on_update " + @@ -193,7 +184,6 @@ public class NotesDatabaseHelper extends SQLiteOpenHelper { /** * Delete notes belong to folder which has been deleted - * 当笔记被删除时,删除属于该笔记的数据的触发器SQL语句定义 */ private static final String FOLDER_DELETE_NOTES_ON_DELETE_TRIGGER = "CREATE TRIGGER folder_delete_notes_on_delete " + @@ -205,7 +195,6 @@ public class NotesDatabaseHelper extends SQLiteOpenHelper { /** * Move notes belong to folder which has been moved to trash folder - * 当文件夹被移动到回收站文件夹时,将属于该文件夹的笔记也移动到回收站文件夹的触发器SQL语句定义 */ private static final String FOLDER_MOVE_NOTES_ON_TRASH_TRIGGER = "CREATE TRIGGER folder_move_notes_on_trash " + @@ -216,18 +205,18 @@ public class NotesDatabaseHelper extends SQLiteOpenHelper { " SET " + NoteColumns.PARENT_ID + "=" + Notes.ID_TRASH_FOLER + " WHERE " + NoteColumns.PARENT_ID + "=old." + NoteColumns.ID + ";" + " END"; -// 构造函数,调用父类SQLiteOpenHelper的构造函数,传入数据库名称、版本号等信息 + public NotesDatabaseHelper(Context context) { super(context, DB_NAME, null, DB_VERSION); } -// 在给定的SQLiteDatabase对象上执行创建"note"表的操作,包括创建表、重新创建相关触发器以及创建系统文件夹,并输出日志表示表已创建 + public void createNoteTable(SQLiteDatabase db) { db.execSQL(CREATE_NOTE_TABLE_SQL); reCreateNoteTableTriggers(db); createSystemFolder(db); Log.d(TAG, "note table has been created"); } -// 先删除已存在的"note"表相关的触发器(如果有),然后再重新创建所有"note"表对应的触发器 + 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"); @@ -245,17 +234,17 @@ public class NotesDatabaseHelper extends SQLiteOpenHelper { db.execSQL(FOLDER_DELETE_NOTES_ON_DELETE_TRIGGER); db.execSQL(FOLDER_MOVE_NOTES_ON_TRASH_TRIGGER); } - // 向"note"表中插入代表不同系统文件夹的记录,如通话记录文件夹、根文件夹、临时文件夹、回收站文件夹等,通过构造ContentValues对象并调用db.insert方法来实现 + 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 */ @@ -263,7 +252,7 @@ public class NotesDatabaseHelper extends SQLiteOpenHelper { 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 */ @@ -280,14 +269,14 @@ public class NotesDatabaseHelper extends SQLiteOpenHelper { values.put(NoteColumns.TYPE, Notes.TYPE_SYSTEM); db.insert(TABLE.NOTE, null, values); } - // 在给定数据库对象上创建"data"表,创建表后会重新创建"data"表相关的触发器以及创建针对"data"表NOTE_ID列的索引,并输出日志表示表已创建 + public void createDataTable(SQLiteDatabase db) { db.execSQL(CREATE_DATA_TABLE_SQL); reCreateDataTableTriggers(db); db.execSQL(CREATE_DATA_NOTE_ID_INDEX_SQL); 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"); @@ -297,7 +286,6 @@ public class NotesDatabaseHelper extends SQLiteOpenHelper { db.execSQL(DATA_UPDATE_NOTE_CONTENT_ON_UPDATE_TRIGGER); db.execSQL(DATA_UPDATE_NOTE_CONTENT_ON_DELETE_TRIGGER); } -// 采用静态同步方法实现单例模式获取类的实例,若实例不存在则创建新的NotesDatabaseHelper实例并返回,保证整个应用只有一个该类的实例在使用 static synchronized NotesDatabaseHelper getInstance(Context context) { if (mInstance == null) { @@ -305,61 +293,59 @@ public class NotesDatabaseHelper extends SQLiteOpenHelper { } return mInstance; } -// 重写SQLiteOpenHelper的onCreate方法,在数据库首次创建时调用,内部会分别调用createNoteTable(db)和createDataTable(db)来创建"note"表和"data"表 + @Override public void onCreate(SQLiteDatabase db) { createNoteTable(db); createDataTable(db); } -// 重写SQLiteOpenHelper的onUpgrade方法,用于处理数据库升级操作,根据旧版本号和新版本号进行不同的升级逻辑判断与处理 + @Override public void onUpgrade(SQLiteDatabase db, int oldVersion, int newVersion) { boolean reCreateTriggers = false; boolean skipV2 = false; -// 重写SQLiteOpenHelper的onUpgrade方法,用于处理数据库升级操作,根据旧版本号和新版本号进行不同的升级逻辑判断与处理 + if (oldVersion == 1) { upgradeToV2(db); skipV2 = true; // this upgrade including the upgrade from v2 to v3 oldVersion++; } -// 如果旧版本是2且没有跳过v2的升级(即不是从版本1升上来的情况),执行升级到版本3的操作,设置需要重新创建触发器的标记为true,再将旧版本号加1 + if (oldVersion == 2 && !skipV2) { upgradeToV3(db); reCreateTriggers = true; oldVersion++; } -// 如果旧版本是3,执行升级到版本4的操作,然后将旧版本号加1 + if (oldVersion == 3) { upgradeToV4(db); oldVersion++; } -// 如果需要重新创建触发器(根据前面的升级逻辑判断),则分别重新创建"note"表和"data"表相关的触发器 + if (reCreateTriggers) { reCreateNoteTableTriggers(db); reCreateDataTableTriggers(db); } - // 如果最终旧版本号和新版本号不一致,说明升级出现问题,抛出异常表示升级失败 + if (oldVersion != newVersion) { throw new IllegalStateException("Upgrade notes database to version " + newVersion + "fails"); } } -// 升级到版本2的具体操作,先删除已存在的"note"表和"data"表(如果有),然后重新创建这两个表 + private void upgradeToV2(SQLiteDatabase db) { db.execSQL("DROP TABLE IF EXISTS " + TABLE.NOTE); db.execSQL("DROP TABLE IF EXISTS " + TABLE.DATA); createNoteTable(db); createDataTable(db); } -// 升级到版本3的具体操作,包括删除一些不再使用的触发器,向"note"表添加gtask id列,以及插入一个回收站系统文件夹记录 + private void upgradeToV3(SQLiteDatabase db) { - // drop unused triggers,删除不再使用的用于更新笔记修改日期的相关触发器 // 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 - // 向"note"表添加gtask id列,数据类型为文本,默认值为空字符串 db.execSQL("ALTER TABLE " + TABLE.NOTE + " ADD COLUMN " + NoteColumns.GTASK_ID + " TEXT NOT NULL DEFAULT ''"); // add a trash system folder @@ -368,7 +354,7 @@ public class NotesDatabaseHelper extends SQLiteOpenHelper { values.put(NoteColumns.TYPE, Notes.TYPE_SYSTEM); db.insert(TABLE.NOTE, null, values); } -// 升级到版本4的具体操作,向"note"表添加version列,数据类型为整数,默认值为0 + private void upgradeToV4(SQLiteDatabase db) { db.execSQL("ALTER TABLE " + TABLE.NOTE + " ADD COLUMN " + NoteColumns.VERSION + " INTEGER NOT NULL DEFAULT 0"); diff --git a/src/Notes-master/src/net/micode/notes/data/NotesProvider.java b/src/Notes-master/src/net/micode/notes/data/NotesProvider.java index 12cbc3d..edb0a60 100644 --- a/src/Notes-master/src/net/micode/notes/data/NotesProvider.java +++ b/src/Notes-master/src/net/micode/notes/data/NotesProvider.java @@ -33,17 +33,15 @@ import net.micode.notes.R; import net.micode.notes.data.Notes.DataColumns; import net.micode.notes.data.Notes.NoteColumns; import net.micode.notes.data.NotesDatabaseHelper.TABLE; -// NotesProvider类继承自ContentProvider,用于作为内容提供者,对外提供对笔记相关数据的增删改查等操作, -// 并处理不同类型的Uri请求以区分操作的具体对象(如笔记、笔记数据等)以及搜索相关功能。 + public class NotesProvider extends ContentProvider { - // 用于匹配不同的Uri请求的UriMatcher对象,通过静态代码块进行初始化配置 private static final UriMatcher mMatcher; -// 用于匹配不同的Uri请求的UriMatcher对象,通过静态代码块进行初始化配置 + private NotesDatabaseHelper mHelper; -// 用于日志输出的标识字符串,便于在调试和记录运行情况时准确识别相关日志信息 + private static final String TAG = "NotesProvider"; -// 定义不同的Uri匹配码,用于区分不同类型的Uri请求,对应不同的笔记或数据相关操作 + private static final int URI_NOTE = 1; private static final int URI_NOTE_ITEM = 2; private static final int URI_DATA = 3; @@ -51,7 +49,7 @@ public class NotesProvider extends ContentProvider { private static final int URI_SEARCH = 5; private static final int URI_SEARCH_SUGGEST = 6; -// 静态代码块,初始化UriMatcher对象,添加各种不同的Uri匹配规则,以便后续根据传入的Uri来确定具体的操作类型 + static { mMatcher = new UriMatcher(UriMatcher.NO_MATCH); mMatcher.addURI(Notes.AUTHORITY, "note", URI_NOTE); @@ -66,8 +64,6 @@ public class NotesProvider extends ContentProvider { /** * 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. - * 定义搜索结果的投影(查询返回的列)字符串,用于在搜索笔记时获取特定的列信息, - * 例如笔记的ID、将笔记摘要列处理后作为搜索建议的文本等,同时设置了图标、意图动作、意图数据类型等相关信息。 */ private static final String NOTES_SEARCH_PROJECTION = NoteColumns.ID + "," + NoteColumns.ID + " AS " + SearchManager.SUGGEST_COLUMN_INTENT_EXTRA_DATA + "," @@ -76,53 +72,46 @@ public class NotesProvider extends ContentProvider { + 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; -// 在ContentProvider创建时调用,获取NotesDatabaseHelper的单例实例,用于后续数据库操作,返回true表示初始化成功 + @Override public boolean onCreate() { mHelper = NotesDatabaseHelper.getInstance(getContext()); return true; } -// 根据传入的Uri等参数执行查询操作,根据不同的Uri匹配情况,从相应的数据库表(笔记表或数据表)中查询数据,并返回结果游标 + @Override public Cursor query(Uri uri, String[] projection, String selection, String[] selectionArgs, String sortOrder) { Cursor c = null; - // 获取可读的数据库连接对象 SQLiteDatabase db = mHelper.getReadableDatabase(); String id = null; - // 通过UriMatcher匹配传入的Uri,根据匹配结果执行不同的查询逻辑 switch (mMatcher.match(uri)) { case URI_NOTE: - // 查询笔记表,使用传入的投影、选择条件等参数进行查询 c = db.query(TABLE.NOTE, projection, selection, selectionArgs, null, null, sortOrder); break; case URI_NOTE_ITEM: - // 获取具体笔记项的ID(从Uri路径中解析),然后根据该ID构建更精确的查询条件进行查询 id = uri.getPathSegments().get(1); c = db.query(TABLE.NOTE, projection, NoteColumns.ID + "=" + id + parseSelection(selection), selectionArgs, null, null, sortOrder); break; case URI_DATA: - // 查询数据表,同样使用传入的相关参数进行查询 c = db.query(TABLE.DATA, projection, selection, selectionArgs, null, null, sortOrder); break; case URI_DATA_ITEM: - // 获取具体数据项的ID(从Uri路径中解析),然后基于该ID构建查询条件查询数据表 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: - // 对于搜索相关的Uri请求,如果传入了排序、投影等额外参数则抛出异常,因为搜索操作有其固定的逻辑 if (sortOrder != null || projection != null) { throw new IllegalArgumentException( "do not specify sortOrder, selection, selectionArgs, or projection" + "with this query"); @@ -130,12 +119,10 @@ public class NotesProvider extends ContentProvider { String searchString = null; if (mMatcher.match(uri) == URI_SEARCH_SUGGEST) { - // 如果是搜索建议的Uri且路径中有搜索字符串,则获取该字符串 if (uri.getPathSegments().size() > 1) { searchString = uri.getPathSegments().get(1); } } else { - // 否则从Uri的查询参数中获取名为"pattern"的搜索字符串 searchString = uri.getQueryParameter("pattern"); } @@ -144,9 +131,7 @@ public class NotesProvider extends ContentProvider { } try { - // 格式化搜索字符串,添加通配符用于模糊匹配查询 searchString = String.format("%%%s%%", searchString); - // 使用格式化后的搜索字符串执行原始查询,查询符合条件的笔记信息 c = db.rawQuery(NOTES_SNIPPET_SEARCH_QUERY, new String[] { searchString }); } catch (IllegalStateException ex) { @@ -156,25 +141,21 @@ public class NotesProvider extends ContentProvider { default: throw new IllegalArgumentException("Unknown URI " + uri); } - // 如果查询结果游标不为空,设置其通知Uri,以便在数据变化时能收到通知更新相关UI等 if (c != null) { c.setNotificationUri(getContext().getContentResolver(), uri); } return c; } - // 根据传入的Uri和数据值,向相应的数据库表(笔记表或数据表)插入数据,并返回插入后生成的新记录的Uri + @Override public Uri insert(Uri uri, ContentValues values) { SQLiteDatabase db = mHelper.getWritableDatabase(); long dataId = 0, noteId = 0, insertedId = 0; - // 通过UriMatcher匹配Uri,根据匹配结果执行不同的插入逻辑 switch (mMatcher.match(uri)) { case URI_NOTE: - // 向笔记表插入数据,获取插入后生成的记录ID insertedId = noteId = db.insert(TABLE.NOTE, null, values); break; case URI_DATA: - // 如果插入的数据中包含笔记ID,则获取该ID,然后向数据表插入数据并获取插入后的记录ID if (values.containsKey(DataColumns.NOTE_ID)) { noteId = values.getAsLong(DataColumns.NOTE_ID); } else { @@ -186,36 +167,32 @@ public class NotesProvider extends ContentProvider { throw new IllegalArgumentException("Unknown URI " + uri); } // Notify the note uri - // 如果插入的是笔记数据且笔记ID大于0,通知笔记相关的Uri数据发生了变化 if (noteId > 0) { getContext().getContentResolver().notifyChange( ContentUris.withAppendedId(Notes.CONTENT_NOTE_URI, noteId), null); } - // 如果插入的是数据且数据ID大于0,通知数据相关的Uri数据发生了变化 + // Notify the data uri if (dataId > 0) { getContext().getContentResolver().notifyChange( ContentUris.withAppendedId(Notes.CONTENT_DATA_URI, dataId), null); } -// 返回插入后新记录对应的Uri,通过将插入的记录ID附加到原始Uri上生成 + return ContentUris.withAppendedId(uri, insertedId); } -// 根据传入的Uri和选择条件,从相应的数据库表(笔记表或数据表)中删除数据,并返回删除的记录数量 + @Override public int delete(Uri uri, String selection, String[] selectionArgs) { int count = 0; String id = null; SQLiteDatabase db = mHelper.getWritableDatabase(); boolean deleteData = false; - // 通过UriMatcher匹配Uri,根据匹配结果执行不同的删除逻辑 switch (mMatcher.match(uri)) { case URI_NOTE: - // 构建笔记表删除的选择条件,确保ID大于0(可能有额外的传入选择条件拼接),然后执行删除操作并获取删除的记录数量 selection = "(" + selection + ") AND " + NoteColumns.ID + ">0 "; count = db.delete(TABLE.NOTE, selection, selectionArgs); break; case URI_NOTE_ITEM: - // 获取要删除的具体笔记项的ID,先判断是否为不允许删除的系统文件夹(ID小于等于0),如果不是则构建精确的删除条件进行删除操作 id = uri.getPathSegments().get(1); /** * ID that smaller than 0 is system folder which is not allowed to @@ -229,12 +206,10 @@ public class NotesProvider extends ContentProvider { NoteColumns.ID + "=" + id + parseSelection(selection), selectionArgs); break; case URI_DATA: - // 直接从数据表中根据选择条件执行删除操作,标记要通知数据相关的Uri变化,并获取删除的记录数量 count = db.delete(TABLE.DATA, selection, selectionArgs); deleteData = true; break; case URI_DATA_ITEM: - // 获取要删除的具体数据项的ID,构建精确的删除条件从数据表中删除数据,同样标记要通知数据相关的Uri变化,并获取删除的记录数量 id = uri.getPathSegments().get(1); count = db.delete(TABLE.DATA, DataColumns.ID + "=" + id + parseSelection(selection), selectionArgs); @@ -245,42 +220,35 @@ public class NotesProvider extends ContentProvider { } if (count > 0) { if (deleteData) { - // 如果是更新数据相关操作且有数据被更新,通知笔记相关的Uri数据发生了变化(可能关联的笔记数据有变动) getContext().getContentResolver().notifyChange(Notes.CONTENT_NOTE_URI, null); } - // 通知传入的Uri对应的数据发生了变化 getContext().getContentResolver().notifyChange(uri, null); } 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(); boolean updateData = false; - // 通过UriMatcher匹配Uri,根据匹配结果执行不同的更新逻辑 switch (mMatcher.match(uri)) { case URI_NOTE: - // 在更新笔记表数据前,调用方法更新笔记的版本号(可能根据选择条件等情况),然后执行更新操作并获取更新的记录数量 increaseNoteVersion(-1, selection, selectionArgs); count = db.update(TABLE.NOTE, values, selection, selectionArgs); break; case URI_NOTE_ITEM: - // 获取要更新的具体笔记项的ID,调用方法根据该ID等情况更新笔记的版本号,然后构建精确的更新条件对笔记表执行更新操作并获取记录数量 id = uri.getPathSegments().get(1); increaseNoteVersion(Long.valueOf(id), selection, selectionArgs); count = db.update(TABLE.NOTE, values, NoteColumns.ID + "=" + id + parseSelection(selection), selectionArgs); break; case URI_DATA: - // 直接从数据表中根据选择条件执行删除操作,标记要通知数据相关的Uri变化,并获取删除的记录数量 count = db.update(TABLE.DATA, values, selection, selectionArgs); updateData = true; break; case URI_DATA_ITEM: - // 获取要删除的具体数据项的ID,构建精确的删除条件从数据表中删除数据,同样标记要通知数据相关的Uri变化,并获取删除的记录数量 id = uri.getPathSegments().get(1); count = db.update(TABLE.DATA, values, DataColumns.ID + "=" + id + parseSelection(selection), selectionArgs); @@ -292,18 +260,17 @@ public class NotesProvider extends ContentProvider { if (count > 0) { if (updateData) { - // 如果是删除数据相关操作且有数据被删除,通知笔记相关的Uri数据发生了变化(可能关联的笔记数据有变动) getContext().getContentResolver().notifyChange(Notes.CONTENT_NOTE_URI, null); - } // 通知传入的Uri对应的数据发生了变化 + } getContext().getContentResolver().notifyChange(uri, null); } return count; } - // 根据传入的Uri、更新的数据值和选择条件,对相应的数据库表(笔记表或数据表)中的数据进行更新,并返回更新的记录数量 + private String parseSelection(String selection) { return (!TextUtils.isEmpty(selection) ? " AND (" + selection + ')' : ""); } - // 根据传入的笔记ID、选择条件和选择参数,构建并执行更新笔记版本号的SQL语句,实现笔记版本号的增加操作 + private void increaseNoteVersion(long id, String selection, String[] selectionArgs) { StringBuilder sql = new StringBuilder(120); sql.append("UPDATE "); @@ -325,7 +292,7 @@ public class NotesProvider extends ContentProvider { } sql.append(selectString); } -// 用于获取 + mHelper.getWritableDatabase().execSQL(sql.toString()); } diff --git a/src/Notes-master/src/net/micode/notes/gtask/data/MetaData.java b/src/Notes-master/src/net/micode/notes/gtask/data/MetaData.java index cbedbd5..41021a3 100644 --- a/src/Notes-master/src/net/micode/notes/gtask/data/MetaData.java +++ b/src/Notes-master/src/net/micode/notes/gtask/data/MetaData.java @@ -14,7 +14,7 @@ * limitations under the License. */ -package net.micode.notes.gtask.data; +package net.micode.notes.gtask.data;//测试 import android.database.Cursor; import android.util.Log; @@ -23,51 +23,38 @@ import net.micode.notes.tool.GTaskStringUtils; import org.json.JSONException; import org.json.JSONObject; -// MetaData类继承自Task类,主要用于处理与任务相关的元数据操作,例如设置元数据、获取关联的GID(可能是某种任务相关的唯一标识)等, -// 并且重写了一些父类的方法来适配自身对于元数据处理的特殊逻辑。 + public class MetaData extends Task { - // 用于日志输出的标识字符串,其值为类的简单名称,便于在调试和记录运行情况时准确识别相关日志信息 private final static String TAG = MetaData.class.getSimpleName(); -// 用于存储与该元数据相关联的GID(可能是Google Tasks等相关任务系统中的任务唯一标识之类的信息),初始化为null + private String mRelatedGid = null; -// 设置元数据的方法,接收一个GID(任务标识)和一个JSONObject类型的元数据信息对象 + public void setMeta(String gid, JSONObject metaInfo) { try { - // 将传入的GID放入元数据信息对象中,对应的键由GTaskStringUtils.META_HEAD_GTASK_ID指定, - // 如果放入过程中出现JSON异常则记录错误日志 metaInfo.put(GTaskStringUtils.META_HEAD_GTASK_ID, gid); } catch (JSONException e) { Log.e(TAG, "failed to put related gid"); } - // 将处理后的元数据信息对象转换为字符串,并调用父类的方法(假设父类有相应的设置笔记内容的方法)设置为笔记内容 setNotes(metaInfo.toString()); - // 设置名称为特定的字符串,该字符串由GTaskStringUtils.META_NOTE_NAME指定,可能用于标识这是元数据相关的名称 setName(GTaskStringUtils.META_NOTE_NAME); - } -// 获取关联的GID的方法,返回之前存储的mRelatedGid字符串 + public String getRelatedGid() { return mRelatedGid; } -// 重写父类的方法,用于判断当前的元数据是否值得保存,判断依据是看获取到的笔记内容(通过父类方法获取,可能存储着关键元数据信息)是否为null, - // 如果不为null则表示有值得保存的数据,返回true,否则返回false + @Override public boolean isWorthSaving() { return getNotes() != null; } -// 重写父类的方法,用于根据远程的JSON对象来设置自身的内容(包括解析出关联的GID等元数据相关操作) + @Override public void setContentByRemoteJSON(JSONObject js) { - // 先调用父类的同名方法来执行一些通用的设置内容的基础操作(假设父类该方法有相应逻辑) super.setContentByRemoteJSON(js); - // 如果获取到的笔记内容不为null(说明有元数据信息) if (getNotes() != null) { try { - // 将笔记内容字符串转换为JSONObject对象,以便解析其中的元数据信息 JSONObject metaInfo = new JSONObject(getNotes().trim()); - // 从解析后的元数据信息对象中获取关联的GID,并赋值给mRelatedGid成员变量, - // 如果获取过程中出现JSON异常,则记录警告日志并将mRelatedGid设置为null mRelatedGid = metaInfo.getString(GTaskStringUtils.META_HEAD_GTASK_ID); } catch (JSONException e) { Log.w(TAG, "failed to get related gid"); @@ -75,21 +62,18 @@ public class MetaData extends Task { } } } -// 重写父类的方法,这里明确表示该方法不应该被调用,直接抛出 IllegalAccessError异常, - // 可能是因为对于元数据对象来说,通过本地JSON来设置内容不符合其业务逻辑设计 + @Override public void setContentByLocalJSON(JSONObject js) { // this function should not be called throw new IllegalAccessError("MetaData:setContentByLocalJSON should not be called"); } -// 重写父类的方法,同样明确表示该方法不应该被调用,直接抛出 IllegalAccessError异常, - // 可能是因为获取本地JSON对象的操作在元数据对象的业务逻辑中没有对应的合理实现或者不需要这样的操作 + @Override public JSONObject getLocalJSONFromContent() { throw new IllegalAccessError("MetaData:getLocalJSONFromContent should not be called"); } -// 重写父类的方法,再次明确表示该方法不应该被调用,直接抛出 IllegalAccessError异常, - // 可能是因为获取同步操作相关信息在元数据对象的业务逻辑中没有对应的处理逻辑或者不需要这样的操作 + @Override public int getSyncAction(Cursor c) { throw new IllegalAccessError("MetaData:getSyncAction should not be called"); diff --git a/src/Notes-master/src/net/micode/notes/gtask/data/Node.java b/src/Notes-master/src/net/micode/notes/gtask/data/Node.java index 3d82677..63950e0 100644 --- a/src/Notes-master/src/net/micode/notes/gtask/data/Node.java +++ b/src/Notes-master/src/net/micode/notes/gtask/data/Node.java @@ -13,91 +13,83 @@ * See the License for the specific language governing permissions and * limitations under the License. */ -// 包声明,表明该类所属的包名,此处是net.micode.notes.gtask.data包 + package net.micode.notes.gtask.data; import android.database.Cursor; import org.json.JSONObject; -// 定义抽象类Node,通常作为节点相关数据结构或业务逻辑的抽象表示, -// 具体的节点类可能会继承该抽象类并实现其中的抽象方法 + public abstract class Node { - // 定义同步操作的各种类型常量,用于表示不同的同步相关动作 - // 表示无同步操作 public static final int SYNC_ACTION_NONE = 0; - // 表示向远程添加的同步操作 + public static final int SYNC_ACTION_ADD_REMOTE = 1; -// 表示在本地添加的同步操作 + public static final int SYNC_ACTION_ADD_LOCAL = 2; -// 表示从远程删除的同步操作 + public static final int SYNC_ACTION_DEL_REMOTE = 3; - // 表示从本地删除的同步操作 + public static final int SYNC_ACTION_DEL_LOCAL = 4; - // 表示更新远程的同步操作 + public static final int SYNC_ACTION_UPDATE_REMOTE = 5; -// 表示更新本地的同步操作 + public static final int SYNC_ACTION_UPDATE_LOCAL = 6; - // 表示更新冲突的同步操作,可能在同步过程中出现两端数据不一致等冲突情况时用到 + public static final int SYNC_ACTION_UPDATE_CONFLICT = 7; - // 表示同步操作出现错误的情况 + public static final int SYNC_ACTION_ERROR = 8; -// 用于存储节点的全局唯一标识符(可能是对应任务等的唯一标识),初始化为null + private String mGid; -// 存储节点的名称,初始化为空字符串 + private String mName; - // 记录节点最后一次被修改的时间戳,初始化为0 + private long mLastModified; -// 标记该节点是否已被删除,初始化为false + private boolean mDeleted; -// 默认构造函数,用于初始化Node对象的各个属性的初始值 + public Node() { mGid = null; mName = ""; mLastModified = 0; mDeleted = false; } - // 抽象方法,用于获取创建动作对应的JSONObject,具体的创建动作由actionId参数指定, - // 不同的子类需要根据自身业务逻辑实现该方法来构造相应的创建动作数据 + public abstract JSONObject getCreateAction(int actionId); -// 抽象方法,用于获取更新动作对应的JSONObject,根据传入的actionId来确定具体的更新动作, - // 子类需实现该方法以按照实际情况生成合适的更新动作相关的JSON数据 + public abstract JSONObject getUpdateAction(int actionId); -// 抽象方法,根据远程传来的JSONObject数据设置节点的内容, - // 具体的设置逻辑由继承该抽象类的子类来实现,以适配不同类型节点的数据解析和设置 + public abstract void setContentByRemoteJSON(JSONObject js); -// 抽象方法,根据本地的JSONObject数据设置节点的内容,同样子类要实现该方法来处理本地数据到节点内容的设置 + public abstract void setContentByLocalJSON(JSONObject js); -// 抽象方法,从节点当前内容获取对应的本地JSON表示形式, - // 子类需根据自身保存的数据结构等情况将节点内容转换为合适的JSONObject返回 + public abstract JSONObject getLocalJSONFromContent(); - // 抽象方法,根据给定的Cursor(通常用于从数据库查询结果中获取数据)来确定节点的同步操作类型, - // 子类要实现该方法以解析Cursor中的数据从而判断出对应的同步动作 + public abstract int getSyncAction(Cursor c); - // 设置节点的全局唯一标识符(Gid)的方法 + public void setGid(String gid) { this.mGid = gid; - }// 设置节点名称的方法 + } public void setName(String name) { this.mName = name; } -// 设置节点最后一次修改时间戳的方法 + public void setLastModified(long lastModified) { this.mLastModified = lastModified; } -// 设置节点是否已删除的标记的方法 + public void setDeleted(boolean deleted) { this.mDeleted = deleted; } - // 获取节点的全局唯一标识符(Gid)的方法 + public String getGid() { return this.mGid; } - // 获取节点名称的方法 + public String getName() { return this.mName; } -// 获取节点最后一次修改时间戳的方法 + public long getLastModified() { return this.mLastModified; } diff --git a/src/Notes-master/src/net/micode/notes/gtask/data/SqlData.java b/src/Notes-master/src/net/micode/notes/gtask/data/SqlData.java index 69405c1..d3ec3be 100644 --- a/src/Notes-master/src/net/micode/notes/gtask/data/SqlData.java +++ b/src/Notes-master/src/net/micode/notes/gtask/data/SqlData.java @@ -13,7 +13,7 @@ * See the License for the specific language governing permissions and * limitations under the License. */ -// 包声明,表明该类所属的包名为net.micode.notes.gtask.data + package net.micode.notes.gtask.data; import android.content.ContentResolver; @@ -33,48 +33,45 @@ import net.micode.notes.gtask.exception.ActionFailureException; import org.json.JSONException; import org.json.JSONObject; -// SqlData类,主要用于处理与数据库相关的数据操作,可能涉及到将数据在数据库和JSON格式之间进行转换、保存等功能 + public class SqlData { - // 用于日志记录的标签,取当前类的简单名称 private static final String TAG = SqlData.class.getSimpleName(); -// 表示无效的ID值,通常用于初始化或者标识某个非法的ID状态 + private static final int INVALID_ID = -99999; - // 定义一个字符串数组,用于指定从数据库查询数据时的投影(即要查询的列),包含了数据相关的多个列名 + public static final String[] PROJECTION_DATA = new String[] { DataColumns.ID, DataColumns.MIME_TYPE, DataColumns.CONTENT, DataColumns.DATA1, DataColumns.DATA3 }; -// 定义常量,表示在投影数组中ID列对应的索引位置,方便后续从查询结果的Cursor中获取对应列的数据 + public static final int DATA_ID_COLUMN = 0; - // 定义常量,表示在投影数组中MIME_TYPE列对应的索引位置 + public static final int DATA_MIME_TYPE_COLUMN = 1; -// 定义常量,表示在投影数组中CONTENT列对应的索引位置 + public static final int DATA_CONTENT_COLUMN = 2; -// 定义常量,表示在投影数组中DATA1列对应的索引位置 + public static final int DATA_CONTENT_DATA_1_COLUMN = 3; -// 定义常量,表示在投影数组中DATA3列对应的索引位置 + public static final int DATA_CONTENT_DATA_3_COLUMN = 4; -// 用于与内容提供器进行交互,以便执行数据库相关的操作,如查询、插入、更新等 + private ContentResolver mContentResolver; - // 标记当前操作是否是创建新数据的操作,初始化为true + private boolean mIsCreate; -// 存储数据对应的ID,初始化为无效ID值 + private long mDataId; -// 存储数据的MIME类型,初始化为默认的Note类型(可能对应某种笔记的数据类型) + private String mDataMimeType; - // 存储数据的具体内容,初始化为空字符串 + private String mDataContent; -// 存储数据内容中相关的一个长整型数据(具体含义可能根据业务而定),初始化为0 + private long mDataContentData1; -// 存储数据内容中相关的一个字符串数据(具体含义可能根据业务而定),初始化为空字符串 + private String mDataContentData3; -// 用于存储要进行差异更新的数据值,即记录数据有变化的部分,初始化为一个新的ContentValues对象 + private ContentValues mDiffDataValues; - // 构造函数,用于创建一个新的SqlData对象,通常在创建新数据时使用 - // 接收一个Context上下文对象,用于获取ContentResolver来操作数据库 + public SqlData(Context context) { - // 获取上下文对应的ContentResolver,以便后续进行数据库操作 mContentResolver = context.getContentResolver(); mIsCreate = true; mDataId = INVALID_ID; @@ -84,15 +81,14 @@ public class SqlData { mDataContentData3 = ""; mDiffDataValues = new ContentValues(); } -// 另一个构造函数,用于根据已有的Cursor(通常是从数据库查询返回的结果集)来创建SqlData对象,常用于读取已有数据 - // 接收一个Context上下文对象和一个Cursor对象,用于初始化该对象的各个属性 + public SqlData(Context context, Cursor c) { mContentResolver = context.getContentResolver(); mIsCreate = false; loadFromCursor(c); mDiffDataValues = new ContentValues(); } - // 私有方法,用于从给定的Cursor中加载数据到当前对象的各个属性中,根据之前定义的列索引常量来获取对应列的数据 + private void loadFromCursor(Cursor c) { mDataId = c.getLong(DATA_ID_COLUMN); mDataMimeType = c.getString(DATA_MIME_TYPE_COLUMN); @@ -100,47 +96,40 @@ public class SqlData { mDataContentData1 = c.getLong(DATA_CONTENT_DATA_1_COLUMN); mDataContentData3 = c.getString(DATA_CONTENT_DATA_3_COLUMN); } - // 用于根据传入的JSONObject来设置当前对象的数据内容,比较传入数据与当前已有数据的差异, - // 将有差异的部分放入mDiffDataValues中,以便后续进行更新操作 + public void setContent(JSONObject js) throws JSONException { - // 获取JSON对象中ID字段的值,如果不存在则使用无效ID值 long dataId = js.has(DataColumns.ID) ? js.getLong(DataColumns.ID) : INVALID_ID; - // 如果是创建操作或者当前对象的ID与传入JSON中的ID不同,则将新的ID放入差异数据值中 if (mIsCreate || mDataId != dataId) { mDiffDataValues.put(DataColumns.ID, dataId); } mDataId = dataId; - // 获取JSON对象中MIME_TYPE字段的值,如果不存在则使用默认的Note类型 + String dataMimeType = js.has(DataColumns.MIME_TYPE) ? js.getString(DataColumns.MIME_TYPE) : DataConstants.NOTE; - // 如果是创建操作或者当前对象的MIME_TYPE与传入JSON中的不同,则将新的MIME_TYPE放入差异数据值中 if (mIsCreate || !mDataMimeType.equals(dataMimeType)) { mDiffDataValues.put(DataColumns.MIME_TYPE, dataMimeType); - } // 获取JSON对象中CONTENT字段的值,如果不存在则使用空字符串 + } mDataMimeType = dataMimeType; String dataContent = js.has(DataColumns.CONTENT) ? js.getString(DataColumns.CONTENT) : ""; - // 如果是创建操作或者当前对象的CONTENT与传入JSON中的不同,则将新的CONTENT放入差异数据值中 if (mIsCreate || !mDataContent.equals(dataContent)) { mDiffDataValues.put(DataColumns.CONTENT, dataContent); } mDataContent = dataContent; -// 获取JSON对象中DATA1字段的值,如果不存在则使用0 + long dataContentData1 = js.has(DataColumns.DATA1) ? js.getLong(DataColumns.DATA1) : 0; - // 如果是创建操作或者当前对象的DATA1值与传入JSON中的不同,则将新的DATA1放入差异数据值中 if (mIsCreate || mDataContentData1 != dataContentData1) { mDiffDataValues.put(DataColumns.DATA1, dataContentData1); - } // 获取JSON对象中DATA3字段的值,如果不存在则使用空字符串 + } mDataContentData1 = dataContentData1; - // 如果是创建操作或者当前对象的DATA3与传入JSON中的不同,则将新的DATA3放入差异数据值中 + String dataContentData3 = js.has(DataColumns.DATA3) ? js.getString(DataColumns.DATA3) : ""; if (mIsCreate || !mDataContentData3.equals(dataContentData3)) { mDiffDataValues.put(DataColumns.DATA3, dataContentData3); } mDataContentData3 = dataContentData3; } - // 用于获取当前对象的数据内容,并将其转换为JSONObject格式返回, - // 如果当前操作是创建操作(还未在数据库中创建该数据),则记录错误日志并返回null + public JSONObject getContent() throws JSONException { if (mIsCreate) { Log.e(TAG, "it seems that we haven't created this in database yet"); @@ -154,49 +143,46 @@ public class SqlData { js.put(DataColumns.DATA3, mDataContentData3); return js; } -// 用于将当前对象的数据提交到数据库中,根据是创建操作还是更新操作执行不同的逻辑 + public void commit(long noteId, boolean validateVersion, long version) { if (mIsCreate) { - // 如果是创建操作,且当前数据ID是无效ID且差异数据值中包含ID字段,则移除该ID字段(可能有默认生成机制) if (mDataId == INVALID_ID && mDiffDataValues.containsKey(DataColumns.ID)) { mDiffDataValues.remove(DataColumns.ID); } - // 将关联的笔记ID添加到差异数据值中 + mDiffDataValues.put(DataColumns.NOTE_ID, noteId); - // 使用ContentResolver插入数据到指定的数据库URI,并获取插入后生成的URI Uri uri = mContentResolver.insert(Notes.CONTENT_DATA_URI, mDiffDataValues); try { - // 尝试从插入后的URI中获取生成的ID值,如果解析失败则记录错误日志并抛出异常 mDataId = Long.valueOf(uri.getPathSegments().get(1)); } catch (NumberFormatException e) { Log.e(TAG, "Get note id error :" + e.toString()); throw new ActionFailureException("create note failed"); } - } else { // 如果是更新操作,且差异数据值中有数据需要更新 + } else { if (mDiffDataValues.size() > 0) { int result = 0; - if (!validateVersion) {// 如果不需要验证版本,则直接更新指定ID的数据 + if (!validateVersion) { result = mContentResolver.update(ContentUris.withAppendedId( Notes.CONTENT_DATA_URI, mDataId), mDiffDataValues, null, null); - } else {// 如果需要验证版本,则根据指定的版本条件更新数据,通过SQL语句的条件筛选来确保更新的准确性 + } else { result = mContentResolver.update(ContentUris.withAppendedId( Notes.CONTENT_DATA_URI, mDataId), mDiffDataValues, " ? in (SELECT " + NoteColumns.ID + " FROM " + TABLE.NOTE + " WHERE " + NoteColumns.VERSION + "=?)", new String[] { String.valueOf(noteId), String.valueOf(version) }); - } // 如果更新结果为0(即没有数据被更新),记录警告日志,提示可能用户在同步时更新了笔记 + } if (result == 0) { Log.w(TAG, "there is no update. maybe user updates note when syncing"); } } } - // 清空差异数据值,准备下一次的数据操作 + mDiffDataValues.clear(); mIsCreate = false; } - // 获取当前数据对象的ID值的方法 + public long getId() { return mDataId; } diff --git a/src/Notes-master/src/net/micode/notes/gtask/data/SqlNote.java b/src/Notes-master/src/net/micode/notes/gtask/data/SqlNote.java index d0a9c51..79a4095 100644 --- a/src/Notes-master/src/net/micode/notes/gtask/data/SqlNote.java +++ b/src/Notes-master/src/net/micode/notes/gtask/data/SqlNote.java @@ -13,7 +13,7 @@ * See the License for the specific language governing permissions and * limitations under the License. */ -// 包声明,表明该类所属的包名为net.micode.notes.gtask.data + package net.micode.notes.gtask.data; import android.appwidget.AppWidgetManager; @@ -37,12 +37,12 @@ import org.json.JSONObject; import java.util.ArrayList; -// SqlNote类,主要用于处理与笔记相关的数据操作,包括从数据库读取笔记信息、设置笔记内容、将笔记内容转换为JSON格式以及将笔记数据提交到数据库等功能 -public class SqlNote {// 用于日志记录的标签,取当前类的简单名称 + +public class SqlNote { private static final String TAG = SqlNote.class.getSimpleName(); - // 表示无效的ID值,通常用于初始化或者标识某个非法的ID状态 + private static final int INVALID_ID = -99999; -// 定义一个字符串数组,用于指定从数据库查询笔记数据时的投影(即要查询的列),包含了笔记相关的多个列名 + public static final String[] PROJECTION_NOTE = new String[] { NoteColumns.ID, NoteColumns.ALERTED_DATE, NoteColumns.BG_COLOR_ID, NoteColumns.CREATED_DATE, NoteColumns.HAS_ATTACHMENT, NoteColumns.MODIFIED_DATE, @@ -51,80 +51,77 @@ public class SqlNote {// 用于日志记录的标签,取当前类的简单名 NoteColumns.LOCAL_MODIFIED, NoteColumns.ORIGIN_PARENT_ID, NoteColumns.GTASK_ID, NoteColumns.VERSION }; -// 定义常量,表示在投影数组中ID列对应的索引位置,方便后续从查询结果的Cursor中获取对应列的数据 + public static final int ID_COLUMN = 0; - // 定义常量,表示在投影数组中ALERTED_DATE列对应的索引位置 + public static final int ALERTED_DATE_COLUMN = 1; - // 定义常量,表示在投影数组中BG_COLOR_ID列对应的索引位置 + public static final int BG_COLOR_ID_COLUMN = 2; -// 定义常量,表示在投影数组中CREATED_DATE列对应的索引位置 + public static final int CREATED_DATE_COLUMN = 3; -// 定义常量,表示在投影数组中HAS_ATTACHMENT列对应的索引位置 + public static final int HAS_ATTACHMENT_COLUMN = 4; - // 定义常量,表示在投影数组中MODIFIED_DATE列对应的索引位置 + public static final int MODIFIED_DATE_COLUMN = 5; - // 定义常量,表示在投影数组中NOTES_COUNT列对应的索引位置 + public static final int NOTES_COUNT_COLUMN = 6; - // 定义常量,表示在投影数组中PARENT_ID列对应的索引位置 + public static final int PARENT_ID_COLUMN = 7; - // 定义常量,表示在投影数组中SNIPPET列对应的索引位置 + public static final int SNIPPET_COLUMN = 8; - // 定义常量,表示在投影数组中TYPE列对应的索引位置 + public static final int TYPE_COLUMN = 9; -// 定义常量,表示在投影数组中WIDGET_ID列对应的索引位置 + public static final int WIDGET_ID_COLUMN = 10; -// 定义常量,表示在投影数组中WIDGET_TYPE列对应的索引位置 + public static final int WIDGET_TYPE_COLUMN = 11; -// 定义常量,表示在投影数组中SYNC_ID列对应的索引位置 + public static final int SYNC_ID_COLUMN = 12; -// 定义常量,表示在投影数组中LOCAL_MODIFIED列对应的索引位置 + public static final int LOCAL_MODIFIED_COLUMN = 13; -// 定义常量,表示在投影数组中ORIGIN_PARENT_ID列对应的索引位置 + public static final int ORIGIN_PARENT_ID_COLUMN = 14; -// 定义常量,表示在投影数组中GTASK_ID列对应的索引位置 + public static final int GTASK_ID_COLUMN = 15; - // 定义常量,表示在投影数组中VERSION列对应的索引位置 + public static final int VERSION_COLUMN = 16; -// 上下文对象,用于获取系统资源、执行与应用上下文相关的操作等 + private Context mContext; - // 用于与内容提供器进行交互,以便执行数据库相关的操作,如查询、插入、更新等 + private ContentResolver mContentResolver; - // 标记当前操作是否是创建新笔记的操作,初始化为true + private boolean mIsCreate; - // 存储笔记的ID,初始化为无效ID值 + private long mId; -// 存储笔记的提醒日期(时间戳相关,具体含义根据业务而定),初始化为0 + private long mAlertDate; -// 存储笔记的背景颜色ID,初始化为通过ResourceParser获取的默认背景颜色ID + private int mBgColorId; -// 存储笔记的创建日期(时间戳),初始化为当前系统时间 + private long mCreatedDate; - // 存储笔记是否有附件的标识,0表示没有,初始化为0 + private int mHasAttachment; -// 存储笔记的最后修改日期(时间戳),初始化为当前系统时间 + private long mModifiedDate; - // 存储笔记的父级ID(可能用于表示笔记的层级关系等),初始化为0 + private long mParentId; -// 存储笔记的摘要信息(简短描述等),初始化为空字符串 + private String mSnippet; - // 存储笔记的类型,初始化为普通笔记类型(Notes.TYPE_NOTE) + private int mType; -// 存储笔记关联的桌面小部件ID,初始化为无效的小部件ID + private int mWidgetId; - // 存储笔记关联的桌面小部件类型,初始化为无效的小部件类型(Notes.TYPE_WIDGET_INVALIDE) + private int mWidgetType; -// 存储笔记的原始父级ID(具体用途可能与笔记的来源、历史关系等相关),初始化为0 + private long mOriginParent; -// 存储笔记的版本号,初始化为0 + private long mVersion; -// 用于存储要进行差异更新的笔记数据值,即记录笔记数据有变化的部分,初始化为一个新的ContentValues对象 private ContentValues mDiffNoteValues; -// 存储与该笔记相关的数据列表(可能是笔记包含的多个具体数据项等,每个数据项用SqlData类表示) - + private ArrayList mDataList; - // 构造函数,用于创建一个新的SqlNote对象,通常在创建新笔记时使用 - // 接收一个Context上下文对象,用于获取ContentResolver等资源来操作数据库以及进行其他相关操作 + public SqlNote(Context context) { mContext = context; mContentResolver = context.getContentResolver(); @@ -145,8 +142,7 @@ public class SqlNote {// 用于日志记录的标签,取当前类的简单名 mDiffNoteValues = new ContentValues(); mDataList = new ArrayList(); } -// 构造函数,用于根据已有的Cursor(通常是从数据库查询返回的结果集)来创建SqlNote对象,常用于读取已有笔记数据 - // 接收一个Context上下文对象和一个Cursor对象,用于初始化该对象的各个属性 + public SqlNote(Context context, Cursor c) { mContext = context; mContentResolver = context.getContentResolver(); @@ -157,8 +153,7 @@ public class SqlNote {// 用于日志记录的标签,取当前类的简单名 loadDataContent(); mDiffNoteValues = new ContentValues(); } -// 构造函数,用于根据指定的笔记ID从数据库中加载笔记数据来创建SqlNote对象 - // 接收一个Context上下文对象和一个笔记ID(long类型),用于初始化该对象的各个属性 + public SqlNote(Context context, long id) { mContext = context; mContentResolver = context.getContentResolver(); @@ -170,11 +165,10 @@ public class SqlNote {// 用于日志记录的标签,取当前类的简单名 mDiffNoteValues = new ContentValues(); } -// 私有方法,根据给定的笔记ID从数据库中查询并加载笔记数据到当前对象的各个属性中,通过调用另一个loadFromCursor方法实现具体的属性赋值 - + private void loadFromCursor(long id) { Cursor c = null; - try {// 使用ContentResolver根据指定的URI、查询条件(通过ID匹配)查询笔记数据 + try { c = mContentResolver.query(Notes.CONTENT_NOTE_URI, PROJECTION_NOTE, "(_id=?)", new String[] { String.valueOf(id) @@ -190,8 +184,7 @@ public class SqlNote {// 用于日志记录的标签,取当前类的简单名 c.close(); } } - // 私有方法,用于从给定的Cursor中加载笔记数据到当前对象的各个属性中,根据之前定义的列索引常量来获取对应列的数据 - + private void loadFromCursor(Cursor c) { mId = c.getLong(ID_COLUMN); mAlertDate = c.getLong(ALERTED_DATE_COLUMN); @@ -206,13 +199,11 @@ public class SqlNote {// 用于日志记录的标签,取当前类的简单名 mWidgetType = c.getInt(WIDGET_TYPE_COLUMN); mVersion = c.getLong(VERSION_COLUMN); } -// 私有方法,用于加载与当前笔记相关的数据内容(具体数据项,用SqlData类表示),通过查询数据库并将结果封装到SqlData对象中,添加到mDataList列表里 - + private void loadDataContent() { Cursor c = null; mDataList.clear(); - try { - // 使用ContentResolver根据指定的URI、查询条件(通过笔记ID匹配)查询与笔记相关的数据 + try { c = mContentResolver.query(Notes.CONTENT_DATA_URI, SqlData.PROJECTION_DATA, "(note_id=?)", new String[] { String.valueOf(mId) @@ -234,8 +225,6 @@ public class SqlNote {// 用于日志记录的标签,取当前类的简单名 c.close(); } } -// 用于根据传入的JSONObject来设置当前笔记对象的数据内容,根据笔记类型(如普通笔记、文件夹等)进行不同的处理, - // 将有差异的部分放入mDiffNoteValues中,以便后续进行更新操作,返回设置是否成功的布尔值 public boolean setContent(JSONObject js) { try { @@ -243,7 +232,7 @@ public class SqlNote {// 用于日志记录的标签,取当前类的简单名 if (note.getInt(NoteColumns.TYPE) == Notes.TYPE_SYSTEM) { Log.w(TAG, "cannot set system folder"); } else if (note.getInt(NoteColumns.TYPE) == Notes.TYPE_FOLDER) { - // for folder we can only update the snnipet and type// 如果是文件夹类型,只能更新摘要和类型信息 + // for folder we can only update the snnipet and type String snippet = note.has(NoteColumns.SNIPPET) ? note .getString(NoteColumns.SNIPPET) : ""; if (mIsCreate || !mSnippet.equals(snippet)) { @@ -258,7 +247,6 @@ public class SqlNote {// 用于日志记录的标签,取当前类的简单名 } mType = type; } else if (note.getInt(NoteColumns.TYPE) == Notes.TYPE_NOTE) { - // 如果是普通笔记类型 JSONArray dataArray = js.getJSONArray(GTaskStringUtils.META_HEAD_DATA); long id = note.has(NoteColumns.ID) ? note.getLong(NoteColumns.ID) : INVALID_ID; if (mIsCreate || mId != id) { @@ -342,24 +330,24 @@ public class SqlNote {// 用于日志记录的标签,取当前类的简单名 mDiffNoteValues.put(NoteColumns.ORIGIN_PARENT_ID, originParent); } mOriginParent = originParent; - // 遍历JSON数组中的每个数据项(对应笔记包含的具体数据内容) + for (int i = 0; i < dataArray.length(); i++) { JSONObject data = dataArray.getJSONObject(i); - SqlData sqlData = null; // 如果数据项中有ID字段,尝试在已有的数据列表中查找对应ID的数据对象 + SqlData sqlData = null; if (data.has(DataColumns.ID)) { long dataId = data.getLong(DataColumns.ID); for (SqlData temp : mDataList) { if (dataId == temp.getId()) { sqlData = temp; } - }// 如果未找到,则创建一个新的SqlData对象,并添加到数据列表中 + } } if (sqlData == null) { sqlData = new SqlData(mContext); mDataList.add(sqlData); } - // 调用SqlData对象的方法设置其具体内容 + sqlData.setContent(data); } } @@ -370,8 +358,7 @@ public class SqlNote {// 用于日志记录的标签,取当前类的简单名 } return true; } - // 用于获取当前笔记对象的数据内容,并将其转换为JSONObject格式返回, - // 如果当前操作是创建操作(还未在数据库中创建该笔记),则记录错误日志并返回null + public JSONObject getContent() { try { JSONObject js = new JSONObject(); @@ -419,47 +406,46 @@ public class SqlNote {// 用于日志记录的标签,取当前类的简单名 } return null; } -// 设置笔记的父级ID的方法,同时将该变化记录到差异更新数据值中,以便后续更新数据库时使用 + public void setParentId(long id) { mParentId = id; mDiffNoteValues.put(NoteColumns.PARENT_ID, id); } - // 设置笔记的Gtask ID(具体含义可能与相关任务关联等有关)的方法,将该值记录到差异更新数据值中 + public void setGtaskId(String gid) { mDiffNoteValues.put(NoteColumns.GTASK_ID, gid); } - // 设置笔记的同步ID的方法,将该值记录到差异更新数据值中 + public void setSyncId(long syncId) { mDiffNoteValues.put(NoteColumns.SYNC_ID, syncId); } - // 重置笔记的本地修改标记(将其设置为0)的方法,通过更新差异数据值来实现 + public void resetLocalModified() { mDiffNoteValues.put(NoteColumns.LOCAL_MODIFIED, 0); } - // 获取笔记ID的方法 + public long getId() { return mId; } -// 获取笔记父级ID的方法 + public long getParentId() { return mParentId; } - // 获取笔记摘要信息的方法 + public String getSnippet() { return mSnippet; } - // 判断笔记是否为普通笔记类型的方法,返回布尔值 + public boolean isNoteType() { return mType == Notes.TYPE_NOTE; } - // 用于将当前笔记对象的数据提交到数据库中,根据是创建操作还是更新操作执行不同的逻辑, - // 同时也会处理与之关联的数据(通过调用SqlData的commit方法)的提交操作 + public void commit(boolean validateVersion) { - if (mIsCreate) {// 如果是创建操作,且当前笔记ID是无效ID且差异数据值中包含ID字段,则移除该ID字段(可能有默认生成机制) + if (mIsCreate) { if (mId == INVALID_ID && mDiffNoteValues.containsKey(NoteColumns.ID)) { mDiffNoteValues.remove(NoteColumns.ID); } - // 使用ContentResolver插入笔记数据到指定的数据库URI,并获取插入后生成的URI + Uri uri = mContentResolver.insert(Notes.CONTENT_NOTE_URI, mDiffNoteValues); try { mId = Long.valueOf(uri.getPathSegments().get(1)); @@ -472,19 +458,18 @@ public class SqlNote {// 用于日志记录的标签,取当前类的简单名 } if (mType == Notes.TYPE_NOTE) { - // 如果是普通笔记类型,遍历与之关联的数据列表,调用每个SqlData对象的commit方法提交数据 for (SqlData sqlData : mDataList) { sqlData.commit(mId, false, -1); } } - } else {// 如果是更新操作 + } else { if (mId <= 0 && mId != Notes.ID_ROOT_FOLDER && mId != Notes.ID_CALL_RECORD_FOLDER) { Log.e(TAG, "No such note"); throw new IllegalStateException("Try to update note with invalid id"); } if (mDiffNoteValues.size() > 0) { mVersion ++; - int result = 0;// 根据是否验证版本来决定更新数据库的条件 + int result = 0; if (!validateVersion) { result = mContentResolver.update(Notes.CONTENT_NOTE_URI, mDiffNoteValues, "(" + NoteColumns.ID + "=?)", new String[] { @@ -508,12 +493,12 @@ public class SqlNote {// 用于日志记录的标签,取当前类的简单名 } } } - // 重新从数据库加载笔记数据,刷新本地信息(通过调用相关加载方法) + // refresh local info loadFromCursor(mId); if (mType == Notes.TYPE_NOTE) loadDataContent(); -// 清空差异数据值,准备下一次的数据操作 + mDiffNoteValues.clear(); mIsCreate = false; } diff --git a/src/Notes-master/src/net/micode/notes/gtask/data/Task.java b/src/Notes-master/src/net/micode/notes/gtask/data/Task.java index 556d5cc..6a19454 100644 --- a/src/Notes-master/src/net/micode/notes/gtask/data/Task.java +++ b/src/Notes-master/src/net/micode/notes/gtask/data/Task.java @@ -30,22 +30,21 @@ import net.micode.notes.tool.GTaskStringUtils; import org.json.JSONArray; import org.json.JSONException; import org.json.JSONObject; -// Task类继承自抽象类Node,代表一个任务相关的实体,包含任务自身的各种属性以及与任务操作相关的方法, -// 例如创建、更新任务的JSON表示,从JSON数据设置任务内容等功能,用于处理任务相关的数据逻辑 -public class Task extends Node { // 用于日志记录的标签,取当前类的简单名称 + +public class Task extends Node { private static final String TAG = Task.class.getSimpleName(); -// 标记任务是否已完成,初始化为false + private boolean mCompleted; - // 存储任务的备注信息(可能是对任务的详细描述等),初始化为null + private String mNotes; -// 存储任务的元信息(以JSONObject形式,具体内容结构可能根据业务而定),初始化为null + private JSONObject mMetaInfo; - // 指向当前任务的前一个兄弟任务(在任务列表等场景下用于表示顺序关系),初始化为null + private Task mPriorSibling; - // 指向当前任务所属的父任务列表(表明任务的归属层级关系),初始化为null + private TaskList mParent; -// 构造函数,调用父类(Node类)的构造函数进行初始化,并对Task类特有的属性进行默认初始化 + public Task() { super(); mCompleted = false; @@ -54,24 +53,21 @@ public class Task extends Node { // 用于日志记录的标签,取当前类 mParent = null; mMetaInfo = null; } - // 实现抽象类Node中定义的抽象方法,用于获取创建任务的操作对应的JSONObject,该JSON对象包含了创建任务所需的各种信息, - // 如操作类型、任务的基本属性(名称、备注等)、所属父任务等相关信息,按照特定的格式进行组装 + public JSONObject getCreateAction(int actionId) { JSONObject js = new JSONObject(); - try { // 设置操作类型为创建任务,使用预定义的字符串常量标识 - // action_type + try { // action_type js.put(GTaskStringUtils.GTASK_JSON_ACTION_TYPE, GTaskStringUtils.GTASK_JSON_ACTION_TYPE_CREATE); -// 设置操作的唯一标识符(具体业务中用于区分不同的创建操作实例) + // action_id js.put(GTaskStringUtils.GTASK_JSON_ACTION_ID, actionId); - // 设置任务在父任务列表中的索引位置(通过调用父任务列表的方法获取) + // index js.put(GTaskStringUtils.GTASK_JSON_INDEX, mParent.getChildTaskIndex(this)); - // 构建表示任务实体的详细信息的JSONObject,包含任务名称、创建者ID(此处设为"null",可能需根据实际情况调整)、 - // 实体类型(标识为任务类型)以及任务备注(如果存在)等信息 + // entity_delta JSONObject entity = new JSONObject(); entity.put(GTaskStringUtils.GTASK_JSON_NAME, getName()); @@ -82,17 +78,17 @@ public class Task extends Node { // 用于日志记录的标签,取当前类 entity.put(GTaskStringUtils.GTASK_JSON_NOTES, getNotes()); } js.put(GTaskStringUtils.GTASK_JSON_ENTITY_DELTA, entity); - // 设置任务所属父任务的全局唯一标识符(Gid) + // parent_id js.put(GTaskStringUtils.GTASK_JSON_PARENT_ID, mParent.getGid()); - // 设置目标父任务的类型(此处设为组类型,可能与任务的分组、归属等概念相关,具体依业务而定) + // dest_parent_type js.put(GTaskStringUtils.GTASK_JSON_DEST_PARENT_TYPE, GTaskStringUtils.GTASK_JSON_TYPE_GROUP); - // 设置任务所属列表的全局唯一标识符(通常与父任务列表的Gid一致) + // list_id js.put(GTaskStringUtils.GTASK_JSON_LIST_ID, mParent.getGid()); -// 如果存在前一个兄弟任务,则设置其全局唯一标识符,用于表示任务的顺序关系 + // prior_sibling_id if (mPriorSibling != null) { js.put(GTaskStringUtils.GTASK_JSON_PRIOR_SIBLING_ID, mPriorSibling.getGid()); @@ -106,22 +102,21 @@ public class Task extends Node { // 用于日志记录的标签,取当前类 return js; } -// 实现抽象类Node中定义的抽象方法,用于获取更新任务的操作对应的JSONObject,包含了更新任务所需的关键信息, - // 如操作类型、任务的唯一标识符、任务实体的更新内容(如名称、备注、是否删除等信息),按照特定格式组装JSON数据 + public JSONObject getUpdateAction(int actionId) { JSONObject js = new JSONObject(); - try {// 设置操作类型为更新任务,使用预定义的字符串常量标识 + try { // action_type js.put(GTaskStringUtils.GTASK_JSON_ACTION_TYPE, GTaskStringUtils.GTASK_JSON_ACTION_TYPE_UPDATE); - // 设置操作的唯一标识符 + // action_id js.put(GTaskStringUtils.GTASK_JSON_ACTION_ID, actionId); -// 设置任务的全局唯一标识符(用于定位要更新的具体任务) + // id js.put(GTaskStringUtils.GTASK_JSON_ID, getGid()); - // 构建表示任务实体更新内容的JSONObject,包含任务名称、任务备注(如果存在)以及任务是否已删除的标记等信息 + // entity_delta JSONObject entity = new JSONObject(); entity.put(GTaskStringUtils.GTASK_JSON_NAME, getName()); @@ -139,38 +134,35 @@ public class Task extends Node { // 用于日志记录的标签,取当前类 return js; } -// 实现抽象类Node中定义的抽象方法,用于根据远程传来的JSONObject数据设置任务的内容, - // 从JSON对象中解析出任务的各个属性值(如ID、最后修改时间、名称、备注、是否删除、是否完成等)并设置到当前任务对象中, - // 如果解析过程出现JSONException异常,则记录错误日志并抛出相应异常 + public void setContentByRemoteJSON(JSONObject js) { if (js != null) { - try {// 从JSON对象中获取任务的全局唯一标识符(Gid)并设置到当前任务对象中 + try { // id if (js.has(GTaskStringUtils.GTASK_JSON_ID)) { setGid(js.getString(GTaskStringUtils.GTASK_JSON_ID)); } -// 从JSON对象中获取任务最后一次修改的时间戳并设置到当前任务对象中 + // last_modified if (js.has(GTaskStringUtils.GTASK_JSON_LAST_MODIFIED)) { setLastModified(js.getLong(GTaskStringUtils.GTASK_JSON_LAST_MODIFIED)); } - // 从JSON对象中获取任务的名称并设置到当前任务对象中 + // name if (js.has(GTaskStringUtils.GTASK_JSON_NAME)) { setName(js.getString(GTaskStringUtils.GTASK_JSON_NAME)); } - // 从JSON对象中获取任务的备注信息并设置到当前任务对象中 // notes if (js.has(GTaskStringUtils.GTASK_JSON_NOTES)) { setNotes(js.getString(GTaskStringUtils.GTASK_JSON_NOTES)); } -// 从JSON对象中获取任务是否已删除的标记并设置到当前任务对象中 + // deleted if (js.has(GTaskStringUtils.GTASK_JSON_DELETED)) { setDeleted(js.getBoolean(GTaskStringUtils.GTASK_JSON_DELETED)); } - // 从JSON对象中获取任务是否已完成的标记并设置到当前任务对象中 + // completed if (js.has(GTaskStringUtils.GTASK_JSON_COMPLETED)) { setCompleted(js.getBoolean(GTaskStringUtils.GTASK_JSON_COMPLETED)); @@ -182,10 +174,7 @@ public class Task extends Node { // 用于日志记录的标签,取当前类 } } } - // 实现抽象类Node中定义的抽象方法,用于根据本地的JSONObject数据设置任务的内容, - // 首先进行一些基本的校验(判断JSON对象是否可用,是否包含必要的头部信息等),然后尝试从JSON数据中解析出笔记类型, - // 如果类型为普通笔记类型,则遍历数据数组,找到MIME类型为笔记类型的数据项,从中获取内容并设置为任务的名称, - // 如果解析过程出现JSONException异常,则记录错误日志 + public void setContentByLocalJSON(JSONObject js) { if (js == null || !js.has(GTaskStringUtils.META_HEAD_NOTE) || !js.has(GTaskStringUtils.META_HEAD_DATA)) { @@ -214,14 +203,11 @@ public class Task extends Node { // 用于日志记录的标签,取当前类 e.printStackTrace(); } } -// 实现抽象类Node中定义的抽象方法,用于从任务当前内容获取对应的本地JSON表示形式, - // 根据任务的元信息是否存在分为两种情况处理:如果是新创建的任务(元信息为空),则按照一定格式构建包含任务名称的JSON对象; - // 如果是已同步过的任务(元信息不为空),则从元信息中提取相关数据并更新任务名称所在的数据项,最后返回构建好的JSON对象, - // 如果在构建JSON对象过程中出现JSONException异常,则记录错误日志并返回null + public JSONObject getLocalJSONFromContent() { String name = getName(); try { - if (mMetaInfo == null) { // 新任务创建自网络(可能表示从远程创建后还未完全同步等情况) + if (mMetaInfo == null) { // new task created from web if (name == null) { Log.w(TAG, "the note seems to be an empty one"); @@ -238,7 +224,7 @@ public class Task extends Node { // 用于日志记录的标签,取当前类 note.put(NoteColumns.TYPE, Notes.TYPE_NOTE); js.put(GTaskStringUtils.META_HEAD_NOTE, note); return js; - } else { // 已同步的任务(已经有过同步操作,存在相关元信息) + } else { // synced task JSONObject note = mMetaInfo.getJSONObject(GTaskStringUtils.META_HEAD_NOTE); JSONArray dataArray = mMetaInfo.getJSONArray(GTaskStringUtils.META_HEAD_DATA); @@ -260,8 +246,7 @@ public class Task extends Node { // 用于日志记录的标签,取当前类 return null; } } - // 用于设置任务的元信息,将传入的MetaData对象中的笔记信息(如果不为空且可转换为JSONObject)转换为JSONObject并赋值给mMetaInfo属性, - // 如果转换过程出现JSONException异常,则记录警告日志并将mMetaInfo设为null + public void setMetaInfo(MetaData metaData) { if (metaData != null && metaData.getNotes() != null) { try { @@ -272,9 +257,7 @@ public class Task extends Node { // 用于日志记录的标签,取当前类 } } } - // 实现抽象类Node中定义的抽象方法,用于根据给定的Cursor(通常用于从数据库查询结果中获取数据)来确定任务的同步操作类型, - // 通过一系列的条件判断来分析任务在本地和远程数据之间的差异情况,从而确定是无同步操作、更新本地、更新远程、更新冲突还是出现错误等同步动作, - // 如果在判断过程中出现异常,则记录错误日志并返回表示错误的同步操作类型 + public int getSyncAction(Cursor c) { try { JSONObject noteInfo = null; @@ -291,30 +274,25 @@ public class Task extends Node { // 用于日志记录的标签,取当前类 Log.w(TAG, "remote note id seems to be deleted"); return SYNC_ACTION_UPDATE_LOCAL; } -// 验证笔记的ID是否匹配(本地数据库中的ID与从元信息中获取的远程ID进行比较) + // validate the note id now if (c.getLong(SqlNote.ID_COLUMN) != noteInfo.getLong(NoteColumns.ID)) { Log.w(TAG, "note id doesn't match"); return SYNC_ACTION_UPDATE_LOCAL; } - if (c.getInt(SqlNote.LOCAL_MODIFIED_COLUMN) == 0) { - // 本地没有更新(通过判断本地修改标记是否为0来确定) + if (c.getInt(SqlNote.LOCAL_MODIFIED_COLUMN) == 0) { // there is no local update if (c.getLong(SqlNote.SYNC_ID_COLUMN) == getLastModified()) { - // 两边都没有更新(本地同步ID与任务最后修改时间戳相同) // no update both side return SYNC_ACTION_NONE; } else { - // 应用远程到本地(本地没有更新但远程有更新,需将远程数据应用到本地) // apply remote to local return SYNC_ACTION_UPDATE_LOCAL; } } else { - // 验证任务的Gtask ID是否匹配(本地数据库中的Gtask ID与当前任务对象的Gid进行比较) // validate gtask id if (!c.getString(SqlNote.GTASK_ID_COLUMN).equals(getGid())) { - // 只有本地修改(本地修改了但远程没有更新,只需将本地修改同步到远程) Log.e(TAG, "gtask id doesn't match"); return SYNC_ACTION_ERROR; } @@ -332,37 +310,36 @@ public class Task extends Node { // 用于日志记录的标签,取当前类 return SYNC_ACTION_ERROR; } -// 判断任务是否值得保存的方法,根据任务的元信息是否存在、任务名称以及任务备注是否非空(去除空格后长度大于0)来综合判断, - // 如果满足上述条件之一,则认为任务值得保存,返回true,否则返回false + public boolean isWorthSaving() { return mMetaInfo != null || (getName() != null && getName().trim().length() > 0) || (getNotes() != null && getNotes().trim().length() > 0); } - // 设置任务是否完成的方法,用于更新任务的完成状态 + public void setCompleted(boolean completed) { this.mCompleted = completed; } -// 设置任务备注信息的方法,用于更新任务的备注内容 + public void setNotes(String notes) { this.mNotes = notes; } - // 设置任务前一个兄弟任务的方法,用于建立任务之间的顺序关联 + public void setPriorSibling(Task priorSibling) { this.mPriorSibling = priorSibling; } -// 设置任务所属父任务列表的方法,用于明确任务的层级归属关系 + public void setParent(TaskList parent) { this.mParent = parent; } - // 获取任务是否完成的方法,返回任务的完成状态(true表示已完成,false表示未完成) + public boolean getCompleted() { return this.mCompleted; } -// 获取任务备注信息的方法,返回任务的备注内容 + public String getNotes() { return this.mNotes; } - // 获取任务所属父任务列表的方法,返回指向当前任务所属父任务列表的引用 + public Task getPriorSibling() { return this.mPriorSibling; } diff --git a/src/Notes-master/src/net/micode/notes/gtask/data/TaskList.java b/src/Notes-master/src/net/micode/notes/gtask/data/TaskList.java index 62ca7b5..4ea21c5 100644 --- a/src/Notes-master/src/net/micode/notes/gtask/data/TaskList.java +++ b/src/Notes-master/src/net/micode/notes/gtask/data/TaskList.java @@ -28,41 +28,35 @@ import org.json.JSONException; import org.json.JSONObject; import java.util.ArrayList; -// TaskList类继承自抽象类Node,代表一个任务列表相关的实体,包含任务列表自身的各种属性以及与任务列表操作相关的方法, -// 例如创建、更新任务列表的JSON表示,从JSON数据设置任务列表内容,对任务列表中的子任务进行添加、删除、移动等操作的功能,用于处理任务列表相关的数据逻辑 + public class TaskList extends Node { - // 用于日志记录的标签,取当前类的简单名称 private static final String TAG = TaskList.class.getSimpleName(); - // 存储任务列表的索引(具体含义可能与任务列表在某个整体结构中的顺序、位置等相关,依业务而定),初始化为1 + private int mIndex; -// 存储该任务列表包含的所有子任务,以ArrayList形式存储,初始化为一个空的ArrayList + private ArrayList mChildren; -// 构造函数,调用父类(Node类)的构造函数进行初始化,并对TaskList类特有的属性进行默认初始化,创建一个空的任务列表对象并设置初始索引值 - + public TaskList() { super(); mChildren = new ArrayList(); mIndex = 1; } -// 实现抽象类Node中定义的抽象方法,用于获取创建任务列表的操作对应的JSONObject,该JSON对象包含了创建任务列表所需的各种信息, - // 如操作类型、操作的唯一标识符、任务列表的索引位置、任务列表实体的基本属性(名称、创建者ID、实体类型等),按照特定的格式进行组装 + public JSONObject getCreateAction(int actionId) { JSONObject js = new JSONObject(); - try { - // 设置操作类型为创建任务列表,使用预定义的字符串常量标识 + try { // action_type js.put(GTaskStringUtils.GTASK_JSON_ACTION_TYPE, GTaskStringUtils.GTASK_JSON_ACTION_TYPE_CREATE); - // 设置操作的唯一标识符(具体业务中用于区分不同的创建操作实例) + // action_id js.put(GTaskStringUtils.GTASK_JSON_ACTION_ID, actionId); -// 设置任务列表的索引位置 + // index js.put(GTaskStringUtils.GTASK_JSON_INDEX, mIndex); -// 构建表示任务列表实体的详细信息的JSONObject,包含任务列表名称、创建者ID(此处设为"null",可能需根据实际情况调整)、 - // 实体类型(标识为组类型,表示任务列表在整体结构中的类型为组)等信息 + // entity_delta JSONObject entity = new JSONObject(); entity.put(GTaskStringUtils.GTASK_JSON_NAME, getName()); @@ -79,24 +73,21 @@ public class TaskList extends Node { return js; } -// 实现抽象类Node中定义的抽象方法,用于获取更新任务列表的操作对应的JSONObject,包含了更新任务列表所需的关键信息, - // 如操作类型、操作的唯一标识符、任务列表的唯一标识符、任务列表实体的更新内容(如名称、是否删除等信息),按照特定格式组装JSON数据 public JSONObject getUpdateAction(int actionId) { JSONObject js = new JSONObject(); try { - // 设置操作类型为更新任务列表,使用预定义的字符串常量标识 // action_type js.put(GTaskStringUtils.GTASK_JSON_ACTION_TYPE, GTaskStringUtils.GTASK_JSON_ACTION_TYPE_UPDATE); - // 设置操作的唯一标识符 + // action_id js.put(GTaskStringUtils.GTASK_JSON_ACTION_ID, actionId); - // 设置任务列表的全局唯一标识符(用于定位要更新的具体任务列表) + // id js.put(GTaskStringUtils.GTASK_JSON_ID, getGid()); -// 构建表示任务列表实体更新内容的JSONObject,包含任务列表名称以及任务列表是否已删除的标记等信息 + // entity_delta JSONObject entity = new JSONObject(); entity.put(GTaskStringUtils.GTASK_JSON_NAME, getName()); @@ -110,24 +101,21 @@ public class TaskList extends Node { } return js; - } // 实现抽象类Node中定义的抽象方法,用于根据远程传来的JSONObject数据设置任务列表的内容, - // 从JSON对象中解析出任务列表的各个属性值(如ID、最后修改时间、名称等)并设置到当前任务列表对象中, - // 如果解析过程出现JSONException异常,则记录错误日志并抛出相应异常 + } public void setContentByRemoteJSON(JSONObject js) { if (js != null) { try { - // 从JSON对象中获取任务列表的全局唯一标识符(Gid)并设置到当前任务列表对象中 // id if (js.has(GTaskStringUtils.GTASK_JSON_ID)) { setGid(js.getString(GTaskStringUtils.GTASK_JSON_ID)); } -// 从JSON对象中获取任务列表最后一次修改的时间戳并设置到当前任务列表对象中 + // last_modified if (js.has(GTaskStringUtils.GTASK_JSON_LAST_MODIFIED)) { setLastModified(js.getLong(GTaskStringUtils.GTASK_JSON_LAST_MODIFIED)); } -// 从JSON对象中获取任务列表的名称并设置到当前任务列表对象中 + // name if (js.has(GTaskStringUtils.GTASK_JSON_NAME)) { setName(js.getString(GTaskStringUtils.GTASK_JSON_NAME)); @@ -140,12 +128,7 @@ public class TaskList extends Node { } } } - // 实现抽象类Node中定义的抽象方法,用于根据本地的JSONObject数据设置任务列表的内容, - // 首先进行一些基本的校验(判断JSON对象是否可用,是否包含必要的头部信息等),然后根据笔记类型进行不同的处理: - // 如果是文件夹类型,从JSON中获取摘要信息并设置任务列表名称(添加特定前缀); - // 如果是系统类型,根据特定的系统文件夹ID设置对应的名称; - // 如果类型不符合预期,则记录错误日志, - // 如果解析过程出现JSONException异常,则记录错误日志并打印异常堆栈信息 + public void setContentByLocalJSON(JSONObject js) { if (js == null || !js.has(GTaskStringUtils.META_HEAD_NOTE)) { Log.w(TAG, "setContentByLocalJSON: nothing is avaiable"); @@ -173,9 +156,7 @@ public class TaskList extends Node { e.printStackTrace(); } } - // 实现抽象类Node中定义的抽象方法,用于从任务列表当前内容获取对应的本地JSON表示形式, - // 按照特定格式构建包含任务列表名称和类型信息的JSON对象,根据名称是否包含特定前缀来判断并设置类型(文件夹类型或系统类型), - // 如果构建JSON对象过程中出现JSONException异常,则记录错误日志并返回null + public JSONObject getLocalJSONFromContent() { try { JSONObject js = new JSONObject(); @@ -201,37 +182,28 @@ public class TaskList extends Node { return null; } } - // 实现抽象类Node中定义的抽象方法,根据给定的Cursor(通常用于从数据库查询结果中获取数据)来确定任务列表的同步操作类型, - // 通过判断本地是否有更新、任务列表的Gtask ID是否匹配以及两边的修改时间戳是否一致等条件,来确定是无同步操作、更新本地、更新远程还是出现错误等同步动作, - // 如果在判断过程中出现异常,则记录错误日志并返回表示错误的同步操作类型,对于文件夹冲突的情况,默认应用本地修改(返回更新远程操作类型) public int getSyncAction(Cursor c) { try { if (c.getInt(SqlNote.LOCAL_MODIFIED_COLUMN) == 0) { - // 本地没有更新(通过判断本地修改标记是否为0来确定) // there is no local update if (c.getLong(SqlNote.SYNC_ID_COLUMN) == getLastModified()) { - // 两边都没有更新(本地同步ID与任务列表最后修改时间戳相同) // no update both side return SYNC_ACTION_NONE; } else { - // 应用远程到本地(本地没有更新但远程有更新,需将远程数据应用到本地) // apply remote to local return SYNC_ACTION_UPDATE_LOCAL; } } else { - // 验证任务列表的Gtask ID是否匹配(本地数据库中的Gtask ID与当前任务列表对象的Gid进行比较) // validate gtask id if (!c.getString(SqlNote.GTASK_ID_COLUMN).equals(getGid())) { Log.e(TAG, "gtask id doesn't match"); return SYNC_ACTION_ERROR; } if (c.getLong(SqlNote.SYNC_ID_COLUMN) == getLastModified()) { - // 只有本地修改(本地修改了但远程没有更新,只需将本地修改同步到远程) // local modification only return SYNC_ACTION_UPDATE_REMOTE; } else { - // 对于文件夹冲突的情况,直接应用本地修改 // for folder conflicts, just apply local modification return SYNC_ACTION_UPDATE_REMOTE; } @@ -243,19 +215,16 @@ public class TaskList extends Node { return SYNC_ACTION_ERROR; } -// 获取任务列表中子任务的数量的方法,直接返回存储子任务的ArrayList的大小 + public int getChildTaskCount() { return mChildren.size(); } - // 向任务列表中添加子任务的方法,将传入的任务对象添加到子任务列表中, - // 如果任务对象不为空且不在子任务列表中,则添加成功后,设置该任务的前一个兄弟任务(如果列表为空则为null,否则为列表中最后一个任务)以及父任务(当前任务列表), - // 返回添加操作是否成功的布尔值 + public boolean addChildTask(Task task) { boolean ret = false; if (task != null && !mChildren.contains(task)) { ret = mChildren.add(task); if (ret) { - // 需要设置前一个兄弟任务和父任务 // need to set prior sibling and parent task.setPriorSibling(mChildren.isEmpty() ? null : mChildren .get(mChildren.size() - 1)); @@ -264,11 +233,7 @@ public class TaskList extends Node { } return ret; } - // 在指定索引位置向任务列表中添加子任务的方法, - // 首先进行索引合法性校验,如果索引无效则记录错误日志并返回false; - // 然后判断任务是否已在列表中,如果不在且任务对象不为空,则将任务添加到指定索引位置, - // 接着更新任务列表中相关任务的前一个兄弟任务关系(设置新添加任务的前一个兄弟任务以及后续任务的前一个兄弟任务指向新添加任务), - // 返回添加操作是否成功的布尔值 + public boolean addChildTask(Task task, int index) { if (index < 0 || index > mChildren.size()) { Log.e(TAG, "add child task: invalid index"); @@ -278,7 +243,7 @@ public class TaskList extends Node { int pos = mChildren.indexOf(task); if (task != null && pos == -1) { mChildren.add(index, task); -// 更新任务列表 + // update the task list Task preTask = null; Task afterTask = null; @@ -294,10 +259,7 @@ public class TaskList extends Node { return true; } -// 从任务列表中移除指定子任务的方法, - // 首先查找任务在列表中的索引位置,如果存在则尝试移除该任务,移除成功后,重置该任务的前一个兄弟任务和父任务为null, - // 并且更新任务列表中后续任务的前一个兄弟任务关系(如果移除位置不是列表末尾), - // 返回移除操作是否成功的布尔值 + public boolean removeChildTask(Task task) { boolean ret = false; int index = mChildren.indexOf(task); @@ -305,11 +267,10 @@ public class TaskList extends Node { ret = mChildren.remove(task); if (ret) { - // 重置前一个兄弟任务和父任务 // reset prior sibling and parent task.setPriorSibling(null); task.setParent(null); - // 更新任务列表 + // update the task list if (index != mChildren.size()) { mChildren.get(index).setPriorSibling( @@ -319,9 +280,6 @@ public class TaskList extends Node { } return ret; } - // 在任务列表中移动指定子任务到新的索引位置的方法, - // 首先进行索引合法性校验以及判断任务是否在列表中,如果校验不通过则记录错误日志并返回false; - // 如果任务当前位置和目标位置相同则直接返回true,否则先移除该任务再添加到新的索引位置,返回操作是否成功的布尔值 public boolean moveChildTask(Task task, int index) { @@ -340,7 +298,6 @@ public class TaskList extends Node { return true; return (removeChildTask(task) && addChildTask(task, index)); } - // 根据任务的全局唯一标识符(Gid)查找子任务的方法,遍历子任务列表,找到Gid匹配的任务并返回,如果没找到则返回null public Task findChildTaskByGid(String gid) { for (int i = 0; i < mChildren.size(); i++) { @@ -351,12 +308,10 @@ public class TaskList extends Node { } return null; } -// 获取指定子任务在任务列表中的索引位置的方法,通过调用ArrayList的indexOf方法来查找并返回任务在列表中的索引,若不存在则返回 -1 public int getChildTaskIndex(Task task) { return mChildren.indexOf(task); } - // 根据索引获取子任务的方法,先对索引进行合法性校验,如果索引无效则记录错误日志并返回null,否则返回对应索引位置的子任务 public Task getChildTaskByIndex(int index) { if (index < 0 || index >= mChildren.size()) { @@ -365,8 +320,7 @@ public class TaskList extends Node { } return mChildren.get(index); } -// 根据任务的全局唯一标识符(Gid)获取子任务的方法(与findChildTaskByGid方法功能类似,可能是重复定义或者不同使用场景下的同名方法), - // 遍历子任务列表,找到Gid匹配的任务并返回,如果没找到则返回null + public Task getChilTaskByGid(String gid) { for (Task task : mChildren) { if (task.getGid().equals(gid)) @@ -374,16 +328,15 @@ public class TaskList extends Node { } return null; } -// 获取整个子任务列表的方法,直接返回存储子任务的ArrayList对象,外部可通过该方法获取任务列表包含的所有子任务进行进一步操作 public ArrayList getChildTaskList() { return this.mChildren; } - // 设置任务列表索引的方法,用于更新任务列表的索引值(可能用于改变其在相关结构中的顺序、位置等属性) + public void setIndex(int index) { this.mIndex = index; } - // 获取任务列表索引的方法,用于获取当前任务列表的索引值 + public int getIndex() { return this.mIndex; } diff --git a/src/Notes-master/src/net/micode/notes/gtask/exception/ActionFailureException.java b/src/Notes-master/src/net/micode/notes/gtask/exception/ActionFailureException.java index bc69af7..15504be 100644 --- a/src/Notes-master/src/net/micode/notes/gtask/exception/ActionFailureException.java +++ b/src/Notes-master/src/net/micode/notes/gtask/exception/ActionFailureException.java @@ -15,25 +15,18 @@ */ package net.micode.notes.gtask.exception; -// ActionFailureException类继承自RuntimeException,意味着它是一个运行时异常,不需要在方法签名中显式声明抛出该异常(与受检异常相对) -// 此类用于表示在执行某个操作时发生了失败的情况,方便在代码中针对操作失败进行统一的异常处理和错误信息传递 + public class ActionFailureException extends RuntimeException { - // 用于序列化版本控制的唯一标识符,在Java的序列化机制中使用,保证不同版本的类在序列化和反序列化时的兼容性 private static final long serialVersionUID = 4425249765923293627L; - // 无参构造函数,调用父类(RuntimeException)的无参构造函数,用于创建一个默认的ActionFailureException实例, - // 通常在不需要传递具体错误信息时使用 + public ActionFailureException() { super(); } -// 带有字符串参数的构造函数,接收一个表示错误信息的字符串参数,调用父类(RuntimeException)的对应构造函数, - // 用于创建一个带有特定错误信息的ActionFailureException实例,方便在出现操作失败时传递相应的错误提示内容 public ActionFailureException(String paramString) { super(paramString); } -// 带有字符串参数和Throwable参数的构造函数,接收一个表示错误信息的字符串参数以及一个Throwable对象(通常是导致当前异常的底层异常), - // 调用父类(RuntimeException)的对应构造函数,用于创建一个既能传递自定义错误信息,又能关联底层引发异常原因的ActionFailureException实例, - // 方便在复杂的异常链场景中进行准确的异常追踪和错误分析 + public ActionFailureException(String paramString, Throwable paramThrowable) { super(paramString, paramThrowable); } diff --git a/src/Notes-master/src/net/micode/notes/gtask/exception/NetworkFailureException.java b/src/Notes-master/src/net/micode/notes/gtask/exception/NetworkFailureException.java index 5a83b04..b08cfb1 100644 --- a/src/Notes-master/src/net/micode/notes/gtask/exception/NetworkFailureException.java +++ b/src/Notes-master/src/net/micode/notes/gtask/exception/NetworkFailureException.java @@ -15,29 +15,18 @@ */ package net.micode.notes.gtask.exception; -// NetworkFailureException类继承自Exception,属于受检异常(Checked Exception),这意味着在方法中如果可能抛出该异常, -// 必须在方法签名中使用throws关键字显式声明,调用该方法的代码也需要进行相应的异常处理(try-catch块或者继续向上抛出)。 -// 此类专门用于表示在网络相关操作出现故障、失败时抛出的异常情况,方便对网络故障进行统一的异常处理和错误信息传递。 public class NetworkFailureException extends Exception { - // 用于序列化版本控制的唯一标识符,在Java的序列化机制中发挥作用。当类实现了Serializable接口(Exception类间接实现了该接口), - // 这个标识符可以确保不同版本的类在进行序列化和反序列化操作时能够正确匹配,避免因类结构变化等原因导致的兼容性问题。 private static final long serialVersionUID = 2107610287180234136L; - // 无参构造函数,调用父类(Exception)的无参构造函数,用于创建一个默认的NetworkFailureException实例, - // 一般在不需要传递具体错误信息,仅表示网络出现故障这种通用情况时使用。 + public NetworkFailureException() { super(); } -// 带有字符串参数的构造函数,接收一个表示错误信息的字符串参数,调用父类(Exception)的对应构造函数, - // 这样就可以创建一个带有明确错误提示内容的NetworkFailureException实例,便于在网络操作失败时, - // 根据具体的失败原因传递相应有意义的错误信息给调用者或者进行日志记录等操作。 + public NetworkFailureException(String paramString) { super(paramString); } -// 带有字符串参数和Throwable参数的构造函数,接收一个表示错误信息的字符串参数以及一个Throwable对象(通常是导致当前网络故障异常的底层异常, - // 比如IOException等更具体的网络相关底层异常),调用父类(Exception)的对应构造函数, - // 此构造函数用于创建一个既能传递自定义的网络故障相关错误信息,又能关联底层引发异常原因的NetworkFailureException实例, - // 在复杂的网络异常处理场景中,方便进行准确的异常追踪、错误分析以及更完善的异常处理逻辑编写。 + public NetworkFailureException(String paramString, Throwable paramThrowable) { super(paramString, paramThrowable); } diff --git a/src/Notes-master/src/net/micode/notes/gtask/remote/GTaskASyncTask.java b/src/Notes-master/src/net/micode/notes/gtask/remote/GTaskASyncTask.java index 4ad596f..b3b61e7 100644 --- a/src/Notes-master/src/net/micode/notes/gtask/remote/GTaskASyncTask.java +++ b/src/Notes-master/src/net/micode/notes/gtask/remote/GTaskASyncTask.java @@ -27,33 +27,24 @@ import android.os.AsyncTask; import net.micode.notes.R; import net.micode.notes.ui.NotesListActivity; import net.micode.notes.ui.NotesPreferenceActivity; -// GTaskASyncTask类继承自AsyncTask,用于在后台线程执行一些耗时的操作(通常与远程任务同步相关),并能在操作过程中更新进度、在操作完成后处理结果, -// 同时通过接口回调的方式通知外部操作已完成,还涉及到与通知系统的交互来展示同步状态相关的通知信息 public class GTaskASyncTask extends AsyncTask { -// 定义一个静态的整型常量,作为同步相关通知的唯一标识符(ID),用于在通知系统中区分不同的通知,此处固定赋值为5234235 private static int GTASK_SYNC_NOTIFICATION_ID = 5234235; -// 定义一个内部接口OnCompleteListener,用于定义当异步任务完成时需要执行的回调方法,外部类实现该接口来处理任务完成后的逻辑 public interface OnCompleteListener { void onComplete(); } -// 存储上下文对象,用于获取系统服务、资源等,方便在类中进行各种与Android系统相关的操作,如创建通知、启动Activity等 private Context mContext; -// 用于管理通知的显示、取消等操作,通过获取系统的通知服务来实例化,负责向用户展示同步进度、结果等相关通知信息 - + private NotificationManager mNotifiManager; -// 用于管理远程任务相关的操作(可能涉及与服务器交互、任务同步等具体逻辑,具体功能由GTaskManager类实现),通过单例模式获取实例 - + private GTaskManager mTaskManager; - // 存储实现了OnCompleteListener接口的对象引用,用于在异步任务完成时回调相应的方法,通知外部任务已结束 - + private OnCompleteListener mOnCompleteListener; -// 构造函数,接收上下文对象和OnCompleteListener接口实现对象作为参数,用于初始化类中的相关成员变量, - // 同时获取通知管理器实例,并获取GTaskManager的单例实例,为后续操作做准备 + public GTaskASyncTask(Context context, OnCompleteListener listener) { mContext = context; mOnCompleteListener = listener; @@ -61,21 +52,17 @@ public class GTaskASyncTask extends AsyncTask { .getSystemService(Context.NOTIFICATION_SERVICE); mTaskManager = GTaskManager.getInstance(); } - // 用于取消正在进行的同步操作的方法,通过调用GTaskManager中的取消同步方法来实现具体的取消逻辑,外部可调用此方法来中断同步过程 - + public void cancelSync() { mTaskManager.cancelSync(); } -// 用于发布进度信息的方法,接收一个字符串消息参数,将其包装成字符串数组后调用AsyncTask的publishProgress方法, - // 触发onProgressUpdate方法的执行,从而实现向UI线程传递进度信息,以便更新界面展示等相关操作 + public void publishProgess(String message) { publishProgress(new String[] { message }); } - // 私有方法,用于显示通知信息,根据传入的通知文本资源ID(tickerId)和通知具体内容(content)创建并配置一个Notification对象, - // 然后通过通知管理器将通知显示出来,设置了通知的图标、默认灯光效果、自动取消等属性,并根据不同的文本资源ID设置点击通知后的跳转意图(PendingIntent) - + private void showNotification(int tickerId, String content) { Notification notification = new Notification(R.drawable.notification, mContext .getString(tickerId), System.currentTimeMillis()); @@ -94,18 +81,14 @@ public class GTaskASyncTask extends AsyncTask { pendingIntent); mNotifiManager.notify(GTASK_SYNC_NOTIFICATION_ID, notification); } -// 在后台线程执行的方法,重写了AsyncTask的抽象方法,是异步任务的核心逻辑所在。 - // 首先发布一条表示正在登录进行同步的进度消息(包含同步账号名称),然后调用GTaskManager的sync方法进行实际的同步操作, - // 并返回同步结果(以整数形式表示不同的状态,如成功、网络错误等状态码),该结果将传递给onPostExecute方法进行后续处理 + @Override protected Integer doInBackground(Void... unused) { publishProgess(mContext.getString(R.string.sync_progress_login, NotesPreferenceActivity .getSyncAccountName(mContext))); return mTaskManager.sync(mContext, this); } -// 在UI线程执行的方法,用于接收并处理在后台线程通过publishProgress方法传递过来的进度信息, - // 调用showNotification方法展示同步进度通知,并且如果当前上下文是GTaskSyncService类型,还会发送广播(可能用于通知其他组件同步进度情况) - + @Override protected void onProgressUpdate(String... progress) { showNotification(R.string.ticker_syncing, progress[0]); @@ -113,11 +96,7 @@ public class GTaskASyncTask extends AsyncTask { ((GTaskSyncService) mContext).sendBroadcast(progress[0]); } } -// 在UI线程执行的方法,当异步任务执行完毕后(无论是正常完成还是出现异常结束),根据返回的结果状态码进行不同的处理: - // 如果结果是成功状态,展示成功同步的通知信息,并记录最后同步时间; - // 如果是网络错误状态、内部错误状态或同步取消状态,分别展示对应的错误通知信息; - // 最后,如果存在实现了OnCompleteListener接口的对象(即外部注册了任务完成回调),则通过开启一个新线程来执行回调方法,通知外部任务已完成 - + @Override protected void onPostExecute(Integer result) { if (result == GTaskManager.STATE_SUCCESS) { diff --git a/src/Notes-master/src/net/micode/notes/gtask/remote/GTaskClient.java b/src/Notes-master/src/net/micode/notes/gtask/remote/GTaskClient.java index ce10c61..c67dfdf 100644 --- a/src/Notes-master/src/net/micode/notes/gtask/remote/GTaskClient.java +++ b/src/Notes-master/src/net/micode/notes/gtask/remote/GTaskClient.java @@ -59,40 +59,37 @@ import java.util.List; import java.util.zip.GZIPInputStream; import java.util.zip.Inflater; import java.util.zip.InflaterInputStream; -// GTaskClient类用于与Google Tasks等相关服务进行交互,实现诸如登录、创建任务、创建任务列表、更新任务、获取任务列表等功能, -// 内部管理着与服务器通信的HttpClient对象、登录状态、账户信息等,是应用与远程任务服务交互的核心类之一 + public class GTaskClient { - // 用于日志记录的标签,取当前类的简单名称 private static final String TAG = GTaskClient.class.getSimpleName(); - // Google Tasks服务的基础URL private static final String GTASK_URL = "https://mail.google.com/tasks/"; -// 用于获取任务相关数据的URL(可能是获取初始数据等情况) + private static final String GTASK_GET_URL = "https://mail.google.com/tasks/ig"; - // 用于向服务器提交任务相关操作(如创建、更新等)的URL + private static final String GTASK_POST_URL = "https://mail.google.com/tasks/r/ig"; -// 单例模式,存储唯一的GTaskClient实例,初始化为null + private static GTaskClient mInstance = null; - // 用于与服务器进行HTTP通信的HttpClient对象,初始化为null,后续根据需要进行初始化和配置 + private DefaultHttpClient mHttpClient; - // 当前用于获取数据的URL,初始化为GTASK_GET_URL,可能会根据登录情况(如自定义域名等)进行调整 + private String mGetUrl; - // 当前用于提交数据的URL,初始化为GTASK_POST_URL,可能会根据登录情况(如自定义域名等)进行调整 + private String mPostUrl; -// 客户端版本号,初始化为 -1,登录成功后会从服务器响应中获取实际版本号 + private long mClientVersion; -// 标记用户是否已登录,初始化为false + private boolean mLoggedin; - // 记录上次登录的时间戳,初始化为0 + private long mLastLoginTime; - // 用于为每个操作生成唯一的标识符(每次操作后自增),初始化为1 + private int mActionId; - // 存储当前登录的账户信息,初始化为null + private Account mAccount; - // 用于存储待提交的更新操作对应的JSON数组(多个更新操作可批量提交),初始化为null + private JSONArray mUpdateArray; -// 私有构造函数,用于初始化GTaskClient类的各个成员变量,采用单例模式,限制外部直接实例化该类 + private GTaskClient() { mHttpClient = null; mGetUrl = GTASK_GET_URL; @@ -104,44 +101,41 @@ public class GTaskClient { mAccount = null; mUpdateArray = null; } - // 静态方法,用于获取GTaskClient的单例实例,如果实例不存在则创建一个新的实例,保证整个应用中只有一个GTaskClient对象在使用 - + public static synchronized GTaskClient getInstance() { if (mInstance == null) { mInstance = new GTaskClient(); } return mInstance; } -// 用于登录到Google Tasks服务的方法,根据一些条件判断是否需要重新登录(如登录间隔超过5分钟、账户切换等情况), - // 先获取Google账户的认证令牌,然后根据账户域名情况尝试不同的登录方式(自定义域名或官方URL),最后返回登录是否成功的布尔值 - + public boolean login(Activity activity) { - // 假设Cookie在5分钟后过期,超过此时间间隔则需要重新登录 // we suppose that the cookie would expire after 5 minutes // then we need to re-login final long interval = 1000 * 60 * 5; if (mLastLoginTime + interval < System.currentTimeMillis()) { mLoggedin = false; } - // 在账户切换后需要重新登录 + // need to re-login after account switch if (mLoggedin && !TextUtils.equals(getSyncAccount().name, NotesPreferenceActivity .getSyncAccountName(activity))) { mLoggedin = false; } + if (mLoggedin) { Log.d(TAG, "already logged in"); return true; } - + mLastLoginTime = System.currentTimeMillis(); String authToken = loginGoogleAccount(activity, false); if (authToken == null) { Log.e(TAG, "login google account failed"); return false; } - // 如果账户名不是以gmail.com或googlemail.com结尾,尝试使用自定义域名登录 + // login with custom domain if necessary if (!(mAccount.name.toLowerCase().endsWith("gmail.com") || mAccount.name.toLowerCase() .endsWith("googlemail.com"))) { @@ -156,7 +150,7 @@ public class GTaskClient { mLoggedin = true; } } - // 如果使用自定义域名登录失败,尝试使用Google官方URL登录 + // try to login with google official url if (!mLoggedin) { mGetUrl = GTASK_GET_URL; @@ -169,10 +163,7 @@ public class GTaskClient { mLoggedin = true; return true; } - // 用于获取Google账户的认证令牌的私有方法,通过AccountManager获取所有Google类型的账户, - // 根据应用设置中指定的同步账户名称查找对应的账户,然后向AccountManager请求获取认证令牌, - // 如果获取失败则返回null,若需要使令牌失效(invalidateToken为true时),则先失效令牌再重新获取 - + private String loginGoogleAccount(Activity activity, boolean invalidateToken) { String authToken; AccountManager accountManager = AccountManager.get(activity); @@ -197,7 +188,7 @@ public class GTaskClient { Log.e(TAG, "unable to get an account with the same name in the settings"); return null; } -// 获取令牌 + // get the token now AccountManagerFuture accountManagerFuture = accountManager.getAuthToken(account, "goanna_mobile", null, activity, null, null); @@ -215,12 +206,9 @@ public class GTaskClient { return authToken; } -// 尝试登录到Google Tasks服务的私有方法,先调用loginGtask方法进行登录,如果登录失败(可能是认证令牌过期等原因), - // 则使令牌失效后重新获取并再次尝试登录,返回最终登录是否成功的布尔值 - + private boolean tryToLoginGtask(Activity activity, String authToken) { if (!loginGtask(authToken)) { - // 可能认证令牌已过期,现在使令牌失效并再次尝试 // maybe the auth token is out of date, now let's invalidate the // token and try again authToken = loginGoogleAccount(activity, true); @@ -236,10 +224,7 @@ public class GTaskClient { } return true; } - // 实际执行登录到Google Tasks服务的私有方法,配置HttpClient的连接超时、读取超时等参数,设置Cookie存储, - // 禁用Expect-Continue机制,然后向服务器发送带有认证令牌的GET请求进行登录,从响应中获取Cookie判断是否登录成功, - // 同时从响应内容中解析出客户端版本号,若在过程中出现JSONException或其他异常则返回false表示登录失败 - + private boolean loginGtask(String authToken) { int timeoutConnection = 10000; int timeoutSocket = 15000; @@ -250,14 +235,14 @@ public class GTaskClient { BasicCookieStore localBasicCookieStore = new BasicCookieStore(); mHttpClient.setCookieStore(localBasicCookieStore); HttpProtocolParams.setUseExpectContinue(mHttpClient.getParams(), false); - // 登录Google Tasks + // login gtask try { String loginUrl = mGetUrl + "?auth=" + authToken; HttpGet httpGet = new HttpGet(loginUrl); HttpResponse response = null; response = mHttpClient.execute(httpGet); - // 获取Cookie + // get the cookie now List cookies = mHttpClient.getCookieStore().getCookies(); boolean hasAuthCookie = false; @@ -269,7 +254,7 @@ public class GTaskClient { if (!hasAuthCookie) { Log.w(TAG, "it seems that there is no auth cookie"); } -// 获取客户端版本号 + // get the client version String resString = getResponseContent(response.getEntity()); String jsBegin = "_setup("; @@ -287,7 +272,6 @@ public class GTaskClient { e.printStackTrace(); return false; } catch (Exception e) { - // 简单捕获所有异常 // simply catch all exceptions Log.e(TAG, "httpget gtask_url failed"); return false; @@ -295,20 +279,18 @@ public class GTaskClient { return true; } - // 获取下一个操作的唯一标识符(每次调用自增)的私有方法,用于区分不同的操作请求 + private int getActionId() { return mActionId++; } - // 创建一个用于POST请求的HttpPost对象的私有方法,设置请求头的Content-Type和AT字段,返回配置好的HttpPost对象, - // 方便后续设置请求体并发送POST请求 + private HttpPost createHttpPost() { HttpPost httpPost = new HttpPost(mPostUrl); httpPost.setHeader("Content-Type", "application/x-www-form-urlencoded;charset=utf-8"); httpPost.setHeader("AT", "1"); return httpPost; } -// 从HttpEntity中获取响应内容的私有方法,根据响应内容的编码方式(如gzip、deflate等)对输入流进行相应的解压处理, - // 然后逐行读取内容并拼接成字符串返回,最后关闭输入流,若在读取过程中出现IOException则抛出异常 + private String getResponseContent(HttpEntity entity) throws IOException { String contentEncoding = null; if (entity.getContentEncoding() != null) { @@ -340,10 +322,7 @@ public class GTaskClient { input.close(); } } - // 向服务器发送POST请求并处理响应的私有方法,首先检查是否已登录,若未登录则抛出异常,然后创建HttpPost对象, - // 设置请求体(将传入的JSONObject转换为表单数据),发送请求并获取响应内容,将响应内容转换为JSONObject返回, - // 在过程中若出现ClientProtocolException、IOException、JSONException等异常则分别抛出对应的异常表示请求失败 - + private JSONObject postRequest(JSONObject js) throws NetworkFailureException { if (!mLoggedin) { Log.e(TAG, "please login first"); @@ -356,7 +335,7 @@ public class GTaskClient { list.add(new BasicNameValuePair("r", js.toString())); UrlEncodedFormEntity entity = new UrlEncodedFormEntity(list, "UTF-8"); httpPost.setEntity(entity); - // 执行POST请求 + // execute the post HttpResponse response = mHttpClient.execute(httpPost); String jsString = getResponseContent(response.getEntity()); @@ -380,9 +359,7 @@ public class GTaskClient { throw new ActionFailureException("error occurs when posting request"); } } - // 创建任务的方法,首先调用commitUpdate方法提交之前可能积累的更新操作,然后构建包含创建任务操作的JSON数据, - // 设置客户端版本号等信息,向服务器发送POST请求,从响应中获取新创建任务的全局唯一标识符(Gid)并设置到任务对象中, - // 若在处理JSON数据过程中出现异常则抛出相应异常表示创建任务失败 + public void createTask(Task task) throws NetworkFailureException { commitUpdate(); try { @@ -408,8 +385,7 @@ public class GTaskClient { throw new ActionFailureException("create task: handing jsonobject failed"); } } - // 创建任务列表的方法,与创建任务的逻辑类似,先提交已有更新,构建包含创建任务列表操作的JSON数据,发送POST请求, - // 从响应中获取新创建任务列表的Gid并设置到任务列表对象中,若处理JSON出现问题则抛出异常表示创建失败 + public void createTaskList(TaskList tasklist) throws NetworkFailureException { commitUpdate(); try { @@ -435,9 +411,7 @@ public class GTaskClient { throw new ActionFailureException("create tasklist: handing jsonobject failed"); } } - // 提交更新操作的方法,若存在待提交的更新操作数组(mUpdateArray不为null),则构建包含更新操作列表和客户端版本号的JSON数据, - // 发送POST请求将更新提交到服务器,之后将更新操作数组置为null,表示已提交,若处理JSON过程中出现异常则抛出相应异常表示提交失败 - + public void commitUpdate() throws NetworkFailureException { if (mUpdateArray != null) { try { @@ -458,9 +432,7 @@ public class GTaskClient { } } } -// 添加待更新节点(任务、任务列表等)操作到更新操作数组的方法,首先判断节点是否为空,若不为空且更新操作数组长度超过10个(避免过多更新导致错误), - // 则先提交已有的更新操作,若更新操作数组为空则创建一个新的JSONArray,然后将节点的更新操作对应的JSON数据添加到数组中,用于后续批量提交更新 - + public void addUpdateNode(Node node) throws NetworkFailureException { if (node != null) { // too many update items may result in an error @@ -474,9 +446,7 @@ public class GTaskClient { mUpdateArray.put(node.getUpdateAction(getActionId())); } } - // 移动任务的方法,先提交已有更新操作,然后构建包含移动任务操作相关信息(如操作类型、任务ID、源任务列表、目标任务列表等)的JSON数据, - // 设置客户端版本号后发送POST请求到服务器,若处理JSON数据过程中出现异常则抛出相应异常表示移动任务失败 - + public void moveTask(Task task, TaskList preParent, TaskList curParent) throws NetworkFailureException { commitUpdate(); @@ -515,9 +485,7 @@ public class GTaskClient { throw new ActionFailureException("move task: handing jsonobject failed"); } } - // 删除节点(任务、任务列表等)的方法,先提交已有更新操作,将节点标记为已删除后构建包含删除操作的JSON数据,设置客户端版本号并发送POST请求到服务器, - // 提交成功后将更新操作数组置为null,若处理JSON过程中出现异常则抛出相应异常表示删除节点失败 - + public void deleteNode(Node node) throws NetworkFailureException { commitUpdate(); try { @@ -540,9 +508,7 @@ public class GTaskClient { throw new ActionFailureException("delete node: handing jsonobject failed"); } } - // 获取所有任务列表的方法,首先检查是否已登录,若未登录则抛出异常,然后发送GET请求到服务器获取任务列表相关数据, - // 从响应内容中解析出任务列表的JSON数组并返回,若在请求过程中出现ClientProtocolException、IOException、JSONException等异常则抛出相应异常表示获取失败 - + public JSONArray getTaskLists() throws NetworkFailureException { if (!mLoggedin) { Log.e(TAG, "please login first"); @@ -580,9 +546,7 @@ public class GTaskClient { throw new ActionFailureException("get task lists: handing jasonobject failed"); } } - // 获取指定任务列表(根据任务列表的Gid)的方法,先提交已有更新操作,然后构建包含获取指定任务列表操作相关信息的JSON数据,发送POST请求到服务器, - // 从响应中获取任务列表包含的任务的JSON数组并返回,若处理JSON过程中出现异常则抛出相应异常表示获取任务列表失败 - + public JSONArray getTaskList(String listGid) throws NetworkFailureException { commitUpdate(); try { @@ -610,12 +574,11 @@ public class GTaskClient { throw new ActionFailureException("get task list: handing jsonobject failed"); } } - // 获取当前同步账户的方法,直接返回存储的账户对象,外部可通过此方法获取当前登录用于同步的账户信息 + public Account getSyncAccount() { return mAccount; } - // 重置更新操作数组的方法,将更新操作数组置为null,用于清空之前积累的待更新操作,通常在一些特定场景下(如重新开始一轮更新操作等)使用 public void resetUpdateArray() { mUpdateArray = null; } diff --git a/src/Notes-master/src/net/micode/notes/gtask/remote/GTaskManager.java b/src/Notes-master/src/net/micode/notes/gtask/remote/GTaskManager.java index 626a6a3..d2b4082 100644 --- a/src/Notes-master/src/net/micode/notes/gtask/remote/GTaskManager.java +++ b/src/Notes-master/src/net/micode/notes/gtask/remote/GTaskManager.java @@ -46,59 +46,47 @@ import java.util.HashMap; import java.util.HashSet; import java.util.Iterator; import java.util.Map; -// GTaskManager类用于管理与远程任务服务(如Google Tasks)的同步操作以及相关数据的处理,包括任务列表、任务、元数据等的同步逻辑, -// 通过协调本地数据和远程数据的差异来实现数据的一致性,是整个应用中任务同步功能的核心管理类,采用单例模式确保在应用内只有一个实例进行管理操作。 + + public class GTaskManager { - // 用于日志记录的标签,取当前类的简单名称 private static final String TAG = GTaskManager.class.getSimpleName(); -// 表示同步成功的状态码 + public static final int STATE_SUCCESS = 0; - // 表示网络错误导致同步失败的状态码 + public static final int STATE_NETWORK_ERROR = 1; -// 表示内部逻辑错误导致同步失败的状态码 + public static final int STATE_INTERNAL_ERROR = 2; - // 表示同步正在进行中的状态码 + public static final int STATE_SYNC_IN_PROGRESS = 3; - // 表示同步被取消的状态码 + public static final int STATE_SYNC_CANCELLED = 4; - // 单例模式,存储唯一的GTaskManager实例,初始化为null private static GTaskManager mInstance = null; -// 用于获取认证令牌等相关操作的Activity对象,可在不同的同步相关操作中使用,例如登录时获取账户认证信息等 - + private Activity mActivity; - // 应用上下文对象,用于获取内容解析器(ContentResolver)等系统服务,方便与本地数据库等进行交互操作 - + private Context mContext; - // 用于与本地内容提供者(Content Provider)进行交互,实现对本地数据(如数据库中的笔记、任务等相关数据)的查询、更新等操作 + private ContentResolver mContentResolver; -// 标记当前是否正在进行同步操作,初始化为false,在同步开始时设置为true,结束时设置为false + private boolean mSyncing; -// 标记当前同步操作是否已被取消,初始化为false,可通过相应方法设置为true来取消正在进行的同步过程 + private boolean mCancelled; - // 以任务列表的全局唯一标识符(Gid)为键,存储任务列表对象的哈希映射,用于缓存从远程获取的任务列表信息以及在同步过程中管理本地和远程任务列表的对应关系 private HashMap mGTaskListHashMap; -// 以节点(任务、任务列表、元数据等都可视为节点,通过共同的父类Node进行抽象)的Gid为键,存储节点对象的哈希映射,方便在同步过程中快速查找和操作各个节点 - + private HashMap mGTaskHashMap; - // 以相关Gid为键,存储元数据(MetaData)对象的哈希映射,用于管理和跟踪与任务等相关的元数据信息,例如可能包含一些额外的描述、配置等数据 - + private HashMap mMetaHashMap; - // 用于存储特殊的元数据列表(可能是具有特定用途、结构的元数据集合),初始化为null,在同步过程中根据情况进行创建和填充 - + private TaskList mMetaList; -// 用于存储本地已删除的任务或数据对应的唯一标识符(可能是本地数据库中的主键等),通过哈希集合来避免重复记录,方便后续统一处理删除相关逻辑 - + private HashSet mLocalDeleteIdMap; - // 以节点的Gid为键,存储对应本地唯一标识符(如数据库中的记录ID)的哈希映射,用于建立远程节点Gid和本地数据ID之间的对应关系,便于同步时查找和关联数据 - + private HashMap mGidToNid; - // 以本地唯一标识符为键,存储对应节点Gid的哈希映射,与mGidToNid作用类似,是反向的映射关系,用于在不同的同步操作场景下根据需要进行查找和转换 - + private HashMap mNidToGid; -// 私有构造函数,用于初始化GTaskManager类的各个成员变量,采用单例模式,限制外部直接实例化该类,在这里初始化了各种用于存储数据和管理同步状态的集合、映射等变量。 - + private GTaskManager() { mSyncing = false; mCancelled = false; @@ -110,7 +98,6 @@ public class GTaskManager { mGidToNid = new HashMap(); mNidToGid = new HashMap(); } - // 静态方法,用于获取GTaskManager的单例实例,如果实例不存在则创建一个新的实例,保证整个应用中只有一个GTaskManager对象在管理任务同步等相关操作 public static synchronized GTaskManager getInstance() { if (mInstance == null) { @@ -118,16 +105,12 @@ public class GTaskManager { } return mInstance; } -// 用于设置关联的Activity上下文对象的方法,主要是为了后续在需要获取认证令牌等与Activity相关的操作时能够获取到相应的上下文环境, - // 该方法是同步的,以保证在多线程环境下设置上下文对象的操作的原子性和一致性。 + public synchronized void setActivityContext(Activity activity) { // used for getting authtoken mActivity = activity; } -// 执行同步操作的核心方法,负责协调本地数据和远程数据的同步过程,首先检查是否正在同步,如果正在同步则直接返回相应状态码, - // 然后初始化一系列用于同步的数据结构,进行登录操作(通过GTaskClient),获取远程任务列表,执行具体的内容同步逻辑(包括处理本地删除、新增、更新等不同情况的同步), - // 最后根据同步过程中的各种情况(如是否取消、是否出现异常等)返回对应的同步状态码,并且在finally块中清理相关的数据结构,重置同步状态。 - + public int sync(Context context, GTaskASyncTask asyncTask) { if (mSyncing) { Log.d(TAG, "Sync is in progress"); @@ -147,18 +130,18 @@ public class GTaskManager { try { GTaskClient client = GTaskClient.getInstance(); client.resetUpdateArray(); - // 登录Google任务服务 + // login google task if (!mCancelled) { if (!client.login(mActivity)) { throw new NetworkFailureException("login google task failed"); } } - // 获取Google任务列表 + // get the task list from google asyncTask.publishProgess(mContext.getString(R.string.sync_progress_init_list)); initGTaskList(); - // 执行内容同步工作 + // do content sync work asyncTask.publishProgess(mContext.getString(R.string.sync_progress_syncing)); syncContent(); @@ -184,16 +167,14 @@ public class GTaskManager { return mCancelled ? STATE_SYNC_CANCELLED : STATE_SUCCESS; } - // 初始化远程任务列表相关数据的私有方法,首先检查同步是否已取消,如果取消则直接返回,然后通过GTaskClient获取远程任务列表的JSON数据, - // 接着分别处理元数据列表(如果不存在则创建)以及普通任务列表,解析JSON数据创建对应的任务列表和任务对象,并建立相关的映射关系(如将任务列表、任务等存入对应的哈希映射中), - // 若在处理JSON数据过程中出现异常则抛出相应异常表示初始化任务列表失败。 + private void initGTaskList() throws NetworkFailureException { if (mCancelled) return; GTaskClient client = GTaskClient.getInstance(); try { JSONArray jsTaskLists = client.getTaskLists(); - // 首先初始化元数据列表 + // init meta list first mMetaList = null; for (int i = 0; i < jsTaskLists.length(); i++) { @@ -205,7 +186,7 @@ public class GTaskManager { .equals(GTaskStringUtils.MIUI_FOLDER_PREFFIX + GTaskStringUtils.FOLDER_META)) { mMetaList = new TaskList(); mMetaList.setContentByRemoteJSON(object); - // 加载元数据 + // load meta data JSONArray jsMetas = client.getTaskList(gid); for (int j = 0; j < jsMetas.length(); j++) { @@ -221,7 +202,7 @@ public class GTaskManager { } } } - // 如果元数据列表不存在则创建它 + // create meta list if not existed if (mMetaList == null) { mMetaList = new TaskList(); @@ -229,7 +210,7 @@ public class GTaskManager { + GTaskStringUtils.FOLDER_META); GTaskClient.getInstance().createTaskList(mMetaList); } - // 初始化任务列表 + // init task list for (int i = 0; i < jsTaskLists.length(); i++) { JSONObject object = jsTaskLists.getJSONObject(i); @@ -243,7 +224,7 @@ public class GTaskManager { tasklist.setContentByRemoteJSON(object); mGTaskListHashMap.put(gid, tasklist); mGTaskHashMap.put(gid, tasklist); - // 加载任务 + // load tasks JSONArray jsTasks = client.getTaskList(gid); for (int j = 0; j < jsTasks.length(); j++) { @@ -265,9 +246,7 @@ public class GTaskManager { throw new ActionFailureException("initGTaskList: handing JSONObject failed"); } } - // 执行具体内容同步的私有方法,负责协调本地和远程数据在内容层面的同步操作,包括处理本地已删除的笔记、文件夹同步、数据库中现有笔记的同步等多种情况, - // 在不同的同步场景下调用相应的辅助方法(如doContentSync)来执行具体的添加、删除、更新等操作,并且在合适的时机提交更新到远程(通过GTaskClient)以及清理本地删除记录等操作, - // 整个过程中会不断检查同步是否已被取消,若已取消则及时终止操作。 + private void syncContent() throws NetworkFailureException { int syncType; Cursor c = null; @@ -279,7 +258,7 @@ public class GTaskManager { if (mCancelled) { return; } - // 处理本地已删除的笔记 + // for local deleted note try { c = mContentResolver.query(Notes.CONTENT_NOTE_URI, SqlNote.PROJECTION_NOTE, @@ -306,10 +285,10 @@ public class GTaskManager { c = null; } } - // 同步文件夹 + // sync folder first syncFolder(); - // 处理数据库中已存在的笔记 + // for note existing in database try { c = mContentResolver.query(Notes.CONTENT_NOTE_URI, SqlNote.PROJECTION_NOTE, @@ -327,11 +306,9 @@ public class GTaskManager { syncType = node.getSyncAction(c); } else { if (c.getString(SqlNote.GTASK_ID_COLUMN).trim().length() == 0) { - // 本地新增 // local add syncType = Node.SYNC_ACTION_ADD_REMOTE; } else { - // 远程已删除 // remote delete syncType = Node.SYNC_ACTION_DEL_LOCAL; } @@ -348,7 +325,7 @@ public class GTaskManager { c = null; } } -// 处理剩余的项目(可能是本地新增但还未处理完全的等情况) + // go through remaining items Iterator> iter = mGTaskHashMap.entrySet().iterator(); while (iter.hasNext()) { @@ -356,16 +333,16 @@ public class GTaskManager { node = entry.getValue(); doContentSync(Node.SYNC_ACTION_ADD_LOCAL, node, null); } - // mCancelled可以被其他线程设置,所以我们需要逐个检查 + // mCancelled can be set by another thread, so we neet to check one by - // one // 清除本地删除表(如果同步未取消) + // one // clear local delete table if (!mCancelled) { if (!DataUtils.batchDeleteNotes(mContentResolver, mLocalDeleteIdMap)) { throw new ActionFailureException("failed to batch-delete local deleted notes"); } } - // 刷新本地同步ID(如果同步未取消) + // refresh local sync id if (!mCancelled) { GTaskClient.getInstance().commitUpdate(); @@ -373,9 +350,7 @@ public class GTaskManager { } } - // 专门用于文件夹同步的私有方法,处理各种不同类型的文件夹(如根文件夹、通话记录文件夹、本地已存在的文件夹以及远程新增的文件夹等)的同步逻辑, - // 在不同情况下判断文件夹在本地和远程的存在情况,调用相应的辅助方法(如doContentSync)来执行添加、更新等操作,并且在合适的时机提交更新到远程(通过GTaskClient), - // 整个过程中会不断检查同步是否已被取消,若已取消则及时终止操作。 + private void syncFolder() throws NetworkFailureException { Cursor c = null; String gid; @@ -385,7 +360,7 @@ public class GTaskManager { if (mCancelled) { return; } - // 处理根文件夹 + // for root folder try { c = mContentResolver.query(ContentUris.withAppendedId(Notes.CONTENT_NOTE_URI, @@ -398,7 +373,6 @@ public class GTaskManager { mGTaskHashMap.remove(gid); mGidToNid.put(gid, (long) Notes.ID_ROOT_FOLDER); mNidToGid.put((long) Notes.ID_ROOT_FOLDER, gid); - // 对于系统文件夹,仅在必要时更新远程名称 // for system folder, only update remote name if necessary if (!node.getName().equals( GTaskStringUtils.MIUI_FOLDER_PREFFIX + GTaskStringUtils.FOLDER_DEFAULT)) @@ -415,7 +389,7 @@ public class GTaskManager { c = null; } } - // 处理通话记录文件夹 + // for call-note folder try { c = mContentResolver.query(Notes.CONTENT_NOTE_URI, SqlNote.PROJECTION_NOTE, "(_id=?)", @@ -430,7 +404,6 @@ public class GTaskManager { mGTaskHashMap.remove(gid); mGidToNid.put(gid, (long) Notes.ID_CALL_RECORD_FOLDER); mNidToGid.put((long) Notes.ID_CALL_RECORD_FOLDER, gid); - // 对于系统文件夹,仅在必要时更新远程名称 // for system folder, only update remote name if // necessary if (!node.getName().equals( @@ -450,7 +423,7 @@ public class GTaskManager { c = null; } } - // 处理本地已存在的文件夹 + // for local existing folders try { c = mContentResolver.query(Notes.CONTENT_NOTE_URI, SqlNote.PROJECTION_NOTE, @@ -468,11 +441,9 @@ public class GTaskManager { syncType = node.getSyncAction(c); } else { if (c.getString(SqlNote.GTASK_ID_COLUMN).trim().length() == 0) { - // 本地新增 // local add syncType = Node.SYNC_ACTION_ADD_REMOTE; } else { - // 远程已删除 // remote delete syncType = Node.SYNC_ACTION_DEL_LOCAL; } @@ -488,7 +459,7 @@ public class GTaskManager { c = null; } } - // 处理远程新增的文件夹 + // for remote add folders Iterator> iter = mGTaskListHashMap.entrySet().iterator(); while (iter.hasNext()) { @@ -504,9 +475,7 @@ public class GTaskManager { if (!mCancelled) GTaskClient.getInstance().commitUpdate(); } - // 根据不同的同步操作类型执行具体同步动作的私有方法,通过一个switch语句根据传入的同步类型(syncType)来决定调用相应的添加、删除、更新等具体操作方法, - // 整个过程中会不断检查同步是否已被取消,若已取消则及时终止操作,若传入的同步类型不合法则抛出异常表示未知的同步操作类型。 - + private void doContentSync(int syncType, Node node, Cursor c) throws NetworkFailureException { if (mCancelled) { return; @@ -541,7 +510,6 @@ public class GTaskManager { updateRemoteNode(node, c); break; case Node.SYNC_ACTION_UPDATE_CONFLICT: - // 合并双方的修改可能是个好主意,目前简单使用本地更新 // merging both modifications maybe a good idea // right now just use local update simply updateRemoteNode(node, c); @@ -553,10 +521,7 @@ public class GTaskManager { throw new ActionFailureException("unkown sync action type"); } } -// 将本地节点添加到本地数据库以及处理相关同步逻辑的私有方法,首先判断同步是否已取消,如果取消则直接返回, - // 然后根据节点类型(是任务列表还是普通任务等)进行不同的处理,创建对应的本地数据记录(通过SqlNote),设置相关属性(如父节点ID、全局唯一标识符等), - // 将数据提交到本地数据库,更新本地和远程标识符的映射关系,并且调用updateRemoteMeta方法来处理与元数据相关的更新操作,若在过程中出现找不到父节点ID等问题则抛出异常表示无法添加本地节点。 - + private void addLocalNode(Node node) throws NetworkFailureException { if (mCancelled) { return; @@ -584,7 +549,6 @@ public class GTaskManager { if (note.has(NoteColumns.ID)) { long id = note.getLong(NoteColumns.ID); if (DataUtils.existInNoteDatabase(mContentResolver, id)) { - // 该ID不可用,必须创建一个新的 // the id is not available, have to create a new one note.remove(NoteColumns.ID); } @@ -598,7 +562,6 @@ public class GTaskManager { if (data.has(DataColumns.ID)) { long dataId = data.getLong(DataColumns.ID); if (DataUtils.existInDataDatabase(mContentResolver, dataId)) { - // 数据ID不可用,必须创建一个新的 // the data id is not available, have to create // a new one data.remove(DataColumns.ID); @@ -620,29 +583,25 @@ public class GTaskManager { } sqlNote.setParentId(parentId.longValue()); } -// 创建本地节点 + // create the local node sqlNote.setGtaskId(node.getGid()); sqlNote.commit(false); -// 更新Gid-Nid映射 + // update gid-nid mapping mGidToNid.put(node.getGid(), sqlNote.getId()); mNidToGid.put(sqlNote.getId(), node.getGid()); -// 更新元数据 + // update meta updateRemoteMeta(node.getGid(), sqlNote); } -// 更新本地节点数据以及相关同步逻辑的私有方法,首先判断同步是否已取消,如果取消则直接返回, - // 然后根据节点类型(是任务还是其他类型等)获取对应的本地数据记录(通过SqlNote),更新其内容、父节点ID等属性,将更新后的数据提交到本地数据库, - // 并调用updateRemoteMeta方法来处理与元数据相关的更新操作,若在过程中出现找不到父节点ID等问题则抛出异常表示无法更新本地节点。 - + private void updateLocalNode(Node node, Cursor c) throws NetworkFailureException { if (mCancelled) { return; } SqlNote sqlNote; - // 更新本地笔记 // update the note locally sqlNote = new SqlNote(mContext, c); sqlNote.setContent(node.getLocalJSONFromContent()); @@ -655,15 +614,11 @@ public class GTaskManager { } sqlNote.setParentId(parentId.longValue()); sqlNote.commit(true); -// 更新元数据信息 + // update meta info updateRemoteMeta(node.getGid(), sqlNote); } - // 将远程节点添加到本地数据库以及处理相关同步逻辑的私有方法,首先判断同步是否已取消,如果取消则直接返回, - // 然后根据节点类型(是任务还是任务列表等)进行不同的处理,创建对应的本地数据记录(通过SqlNote), - // 对于任务,找到其对应的父任务列表,添加到任务列表中,然后向远程服务创建任务(通过GTaskClient),对于任务列表,判断是否已存在,不存在则创建, - // 接着更新本地数据记录的相关属性(如全局唯一标识符等),提交到本地数据库,更新本地和远程标识符的映射关系,若在过程中出现找不到父任务列表等问题则抛出异常表示无法添加远程节点。 - + private void addRemoteNode(Node node, Cursor c) throws NetworkFailureException { if (mCancelled) { return; @@ -671,7 +626,7 @@ public class GTaskManager { SqlNote sqlNote = new SqlNote(mContext, c); Node n; - // 远程更新 + // update remotely if (sqlNote.isNoteType()) { Task task = new Task(); @@ -686,12 +641,12 @@ public class GTaskManager { GTaskClient.getInstance().createTask(task); n = (Node) task; - // 添加元数据 + // add meta updateRemoteMeta(task.getGid(), sqlNote); } else { TaskList tasklist = null; - // 我们需要跳过已存在的文件夹 + // we need to skip folder if it has already existed String folderName = GTaskStringUtils.MIUI_FOLDER_PREFFIX; if (sqlNote.getId() == Notes.ID_ROOT_FOLDER) @@ -715,7 +670,7 @@ public class GTaskManager { break; } } -// 没有匹配的则添加 + // no match we can add now if (tasklist == null) { tasklist = new TaskList(); @@ -725,36 +680,32 @@ public class GTaskManager { } n = (Node) tasklist; } - // 更新本地笔记 + // update local note sqlNote.setGtaskId(n.getGid()); sqlNote.commit(false); sqlNote.resetLocalModified(); sqlNote.commit(true); - // gid-id映射 + // gid-id mapping mGidToNid.put(n.getGid(), sqlNote.getId()); mNidToGid.put(sqlNote.getId(), n.getGid()); } - // 更新远程节点数据以及相关同步逻辑的私有方法,首先判断同步是否已取消,如果取消则直接返回, - // 然后获取对应的本地数据记录(通过SqlNote),将本地数据内容设置到远程节点,向远程服务添加更新节点的请求(通过GTaskClient), - // 接着调用updateRemoteMeta方法处理元数据更新,对于任务类型的节点,还会判断是否需要移动任务(根据父任务列表的变化),若需要则执行移动任务操作(通过GTaskClient), - // 最后清除本地数据的修改标记并提交到本地数据库,若在过程中出现找不到父任务列表等问题则抛出异常表示无法更新远程任务。 - + private void updateRemoteNode(Node node, Cursor c) throws NetworkFailureException { if (mCancelled) { return; } SqlNote sqlNote = new SqlNote(mContext, c); - // 远程更新 + // update remotely node.setContentByLocalJSON(sqlNote.getContent()); GTaskClient.getInstance().addUpdateNode(node); - // 更新元数据 + // update meta updateRemoteMeta(node.getGid(), sqlNote); - // 如果必要的话移动任务 + // move task if necessary if (sqlNote.isNoteType()) { Task task = (Task) node; @@ -773,15 +724,12 @@ public class GTaskManager { GTaskClient.getInstance().moveTask(task, preParentList, curParentList); } } - // 清除本地修改标记 + // clear local modified flag sqlNote.resetLocalModified(); sqlNote.commit(true); } - // 更新远程元数据的私有方法,首先判断传入的本地数据记录(sqlNote)是否为笔记类型且不为空, - // 如果是,则先从元数据映射中查找对应Gid的元数据对象,若存在则更新其元数据内容并向远程服务添加更新节点请求(通过GTaskClient), - // 若不存在则创建新的元数据对象,添加到元数据列表中,然后向远程服务创建元数据任务(通过GTaskClient),以此来保证元数据在本地和远程的同步更新。 - + private void updateRemoteMeta(String gid, SqlNote sqlNote) throws NetworkFailureException { if (sqlNote != null && sqlNote.isNoteType()) { MetaData metaData = mMetaHashMap.get(gid); @@ -797,15 +745,12 @@ public class GTaskManager { } } } - // 刷新本地同步ID的私有方法,用于在同步完成后,根据最新的远程任务列表数据来更新本地数据的同步ID,首先判断同步是否已取消,如果取消则直接返回, - // 然后清空之前缓存的相关任务数据结构(如任务哈希映射、任务列表哈希映射、元数据哈希映射等),重新初始化任务列表数据(通过initGTaskList方法), - // 接着查询本地数据库中的相关数据,对于在远程任务列表中能找到对应节点的数据,更新其同步ID,若在过程中出现找不到对应节点等问题则抛出异常表示同步后本地部分数据缺失对应的Gid。 - + private void refreshLocalSyncId() throws NetworkFailureException { if (mCancelled) { return; } - // 获取最新的gtask列表 + // get the latest gtask list mGTaskHashMap.clear(); mGTaskListHashMap.clear(); @@ -844,13 +789,11 @@ public class GTaskManager { } } } - // 获取当前同步账户名称的方法,通过调用GTaskClient的实例获取当前同步账户对象,然后返回其账户名称,外部可通过此方法获取当前正在用于同步的账户信息。 - + public String getSyncAccount() { return GTaskClient.getInstance().getSyncAccount().name; } - // 用于取消正在进行的同步操作的方法,将同步取消标记(mCancelled)设置为true,在同步过程中的各个关键步骤都会检查这个标记, - // 一旦发现被设置为true,则会及时终止相应的同步操作,避免不必要的资源消耗和数据不一致问题。 + public void cancelSync() { mCancelled = true; } diff --git a/src/Notes-master/src/net/micode/notes/gtask/remote/GTaskSyncService.java b/src/Notes-master/src/net/micode/notes/gtask/remote/GTaskSyncService.java index b3b6f12..cca36f7 100644 --- a/src/Notes-master/src/net/micode/notes/gtask/remote/GTaskSyncService.java +++ b/src/Notes-master/src/net/micode/notes/gtask/remote/GTaskSyncService.java @@ -22,37 +22,26 @@ import android.content.Context; import android.content.Intent; import android.os.Bundle; import android.os.IBinder; -// GTaskSyncService类继承自Service,是一个用于处理任务同步相关操作的后台服务类,它可以接收不同的意图(Intent)来启动或取消任务同步, -// 并且在同步过程中能够发送广播来通知同步状态、进度等信息,同时对外提供了一些静态方法方便在不同的地方控制同步操作以及获取同步相关的状态信息。 public class GTaskSyncService extends Service { - // 用于在Intent中传递同步操作类型的字符串常量名称,外部组件(如Activity等)可以通过在Intent中设置该名称对应的额外数据来指定要执行的同步相关操作。 public final static String ACTION_STRING_NAME = "sync_action_type"; - // 表示启动同步操作的常量值,在通过Intent传递同步操作类型时使用该值表示要开始进行任务同步。 + public final static int ACTION_START_SYNC = 0; - // 表示取消同步操作的常量值,用于指示服务取消正在进行的任务同步过程。 + public final static int ACTION_CANCEL_SYNC = 1; - // 表示无效的同步操作类型的常量值,当接收到无法识别的操作类型时可能用到。 + public final static int ACTION_INVALID = 2; -// 用于发送广播的意图(Intent)的动作(Action)名称,通过这个名称可以在应用内识别来自该服务发送的广播,其他组件可以注册接收该广播来获取同步服务相关的信息。 - + public final static String GTASK_SERVICE_BROADCAST_NAME = "net.micode.notes.gtask.remote.gtask_sync_service"; - // 用于在广播中传递是否正在同步这个状态信息的键(Key)名称,接收广播的组件可以通过该键从广播附带的数据中获取当前是否正在进行同步的状态。 - + public final static String GTASK_SERVICE_BROADCAST_IS_SYNCING = "isSyncing"; - // 用于在广播中传递同步进度消息的键(Key)名称,服务可以在同步过程中更新并发送相应的进度消息,其他组件接收广播后能展示给用户同步的进展情况。 - + public final static String GTASK_SERVICE_BROADCAST_PROGRESS_MSG = "progressMsg"; -// 静态变量,用于存储当前正在执行的同步任务(GTaskASyncTask类型)实例,初始化为null,在启动同步时创建并赋值,取消或完成同步后设置为null,用于跟踪同步任务的执行情况。 - + private static GTaskASyncTask mSyncTask = null; - // 静态变量,用于存储同步进度相关的消息字符串,初始为空字符串,服务在同步过程中可以更新该字符串内容,并通过广播发送出去,方便外部组件展示同步进度给用户。 - + private static String mSyncProgress = ""; -// 私有方法,用于启动同步任务的逻辑,首先判断当前是否已经存在正在执行的同步任务(mSyncTask是否为null), - // 如果不存在,则创建一个新的GTaskASyncTask实例,并传入当前服务(this)以及一个完成监听器(OnCompleteListener), - // 在监听器中,当同步任务完成时,会将mSyncTask设置为null,发送一个空消息的广播,并停止该服务自身,然后发送广播通知同步开始,最后执行同步任务。 - + private void startSync() { if (mSyncTask == null) { mSyncTask = new GTaskASyncTask(this, new GTaskASyncTask.OnCompleteListener() { @@ -66,24 +55,18 @@ public class GTaskSyncService extends Service { mSyncTask.execute(); } } - // 私有方法,用于取消正在进行的同步任务,如果当前存在正在执行的同步任务(mSyncTask不为null),则调用其cancelSync方法来取消同步操作。 - + private void cancelSync() { if (mSyncTask != null) { mSyncTask.cancelSync(); } } - // 重写Service的onCreate方法,在服务创建时被调用,这里将mSyncTask设置为null,确保每次服务启动时都处于初始状态,准备执行新的同步任务。 - + @Override public void onCreate() { mSyncTask = null; } - // 重写Service的onStartCommand方法,当通过startService方法启动服务时会调用此方法,用于处理传入的意图(Intent)并根据其中携带的同步操作类型执行相应操作, - // 首先从意图中获取额外数据的Bundle对象,然后判断Bundle是否存在且包含指定的同步操作类型键(ACTION_STRING_NAME), - // 如果满足条件,则根据操作类型的值进行switch判断,若是ACTION_START_SYNC则调用startSync方法启动同步,若是ACTION_CANCEL_SYNC则调用cancelSync方法取消同步, - // 最后返回START_STICKY表示服务在被系统意外终止后应该尝试重新启动,以保证同步功能的持续可用性,若不满足条件则调用父类的onStartCommand方法进行默认处理。 - + @Override public int onStartCommand(Intent intent, int flags, int startId) { Bundle bundle = intent.getExtras(); @@ -102,22 +85,18 @@ public class GTaskSyncService extends Service { } return super.onStartCommand(intent, flags, startId); } -// 重写Service的onLowMemory方法,当系统内存不足时会调用此方法,在这里如果存在正在执行的同步任务(mSyncTask不为null),则调用其cancelSync方法取消同步操作, - // 以释放内存资源,避免因内存不足导致应用出现异常或性能问题。 + @Override public void onLowMemory() { if (mSyncTask != null) { mSyncTask.cancelSync(); } } - // 重写Service的onBind方法,用于处理服务的绑定操作,这里返回null表示该服务不支持绑定操作,即它是通过startService方式启动来执行后台任务的,而不是通过绑定来与其他组件交互。 - + public IBinder onBind(Intent intent) { return null; } - // 用于发送广播的方法,更新同步进度消息(mSyncProgress),创建一个新的意图(Intent),设置其动作(Action)为预定义的广播名称(GTASK_SERVICE_BROADCAST_NAME), - // 并将是否正在同步(mSyncTask是否为null)以及同步进度消息(mSyncProgress)作为额外数据添加到意图中,最后通过sendBroadcast方法发送广播, - // 以便其他组件能够接收到这些同步相关的状态和进度信息。 + public void sendBroadcast(String msg) { mSyncProgress = msg; Intent intent = new Intent(GTASK_SERVICE_BROADCAST_NAME); @@ -125,8 +104,6 @@ public class GTaskSyncService extends Service { intent.putExtra(GTASK_SERVICE_BROADCAST_PROGRESS_MSG, msg); sendBroadcast(intent); } - // 静态方法,用于方便地从Activity中启动同步操作,首先通过GTaskManager的单例实例设置关联的Activity上下文对象(用于后续可能的与账户认证等相关操作), - // 然后创建一个意图(Intent),指定要启动的服务为GTaskSyncService,并在意图中设置同步操作类型为ACTION_START_SYNC,最后通过Activity的startService方法启动服务来开始同步。 public static void startSync(Activity activity) { GTaskManager.getInstance().setActivityContext(activity); @@ -134,21 +111,17 @@ public class GTaskSyncService extends Service { intent.putExtra(GTaskSyncService.ACTION_STRING_NAME, GTaskSyncService.ACTION_START_SYNC); activity.startService(intent); } - // 静态方法,用于从任意上下文(Context)中取消同步操作,创建一个意图(Intent),指定要操作的服务为GTaskSyncService,并在意图中设置同步操作类型为ACTION_CANCEL_SYNC, - // 最后通过上下文的startService方法启动服务来触发取消同步的逻辑,这样在应用的不同地方都可以方便地取消正在进行的同步任务。 public static void cancelSync(Context context) { Intent intent = new Intent(context, GTaskSyncService.class); intent.putExtra(GTaskSyncService.ACTION_STRING_NAME, GTaskSyncService.ACTION_CANCEL_SYNC); context.startService(intent); } -// 静态方法,用于外部组件判断当前是否正在进行同步操作,通过检查静态变量mSyncTask是否为null来返回相应的布尔值,表示当前是否有同步任务正在执行。 - + public static boolean isSyncing() { return mSyncTask != null; } - // 静态方法,用于外部组件获取当前的同步进度消息字符串,直接返回存储同步进度消息的静态变量mSyncProgress,以便展示给用户同步的进展情况。 - + public static String getProgressString() { return mSyncProgress; } diff --git a/src/Notes-master/src/net/micode/notes/model/Note.java b/src/Notes-master/src/net/micode/notes/model/Note.java index 0484117..6706cf6 100644 --- a/src/Notes-master/src/net/micode/notes/model/Note.java +++ b/src/Notes-master/src/net/micode/notes/model/Note.java @@ -33,24 +33,11 @@ import net.micode.notes.data.Notes.TextNote; import java.util.ArrayList; -// Note类代表笔记的数据模型,用于管理笔记的各种属性数据(如文本数据、通话记录数据等)以及与本地数据库(通过Content Provider)的交互操作, -// 包括创建新笔记、更新笔记、判断笔记是否有本地修改等功能,内部还嵌套了一个NoteData类用于更细致地管理笔记相关的数据内容。 public class Note { - // 用于存储笔记中发生变化的属性值(以键值对形式),通过ContentValues来方便后续与内容提供者进行更新操作,记录笔记在本地修改后产生的数据差异。 - private ContentValues mNoteDiffValues; - // NoteData类型的实例,用于管理笔记中的具体数据内容,如文本数据、通话数据等,将不同类型的数据操作封装在这个内部类中,使代码结构更清晰。 - private NoteData mNoteData; - // 用于日志记录的标签,方便在日志中识别该类相关的输出信息,取固定的字符串"Note"。 private static final String TAG = "Note"; - // 静态同步方法,用于创建一个新的笔记并返回其在数据库中的唯一标识符(ID),接收应用上下文(Context)和所属文件夹的ID作为参数, - // 在方法内部首先创建一个ContentValues对象,设置笔记的创建时间、修改时间、类型、本地修改标记以及父文件夹ID等初始属性值, - // 然后通过内容解析器(ContentResolver)将这些数据插入到本地数据库对应的笔记内容URI(Notes.CONTENT_NOTE_URI)中, - // 接着尝试从插入后返回的Uri中解析出笔记的ID,如果解析出现异常或者ID值为非法值(如 -1)则进行相应的错误处理并返回0或抛出异常,正常情况下返回新创建笔记的ID。 - public static synchronized long getNewNoteId(Context context, long folderId) { - /** * Create a new note id for adding a new note to databases */ @@ -77,55 +64,42 @@ public class Note { } return noteId; } - // Note类的默认构造函数,用于初始化Note对象,创建一个新的ContentValues对象用于存储笔记的差异数据,同时创建一个NoteData对象来管理具体的笔记数据内容。 - + public Note() { mNoteDiffValues = new ContentValues(); mNoteData = new NoteData(); } -// 用于设置笔记的通用属性值的方法,接收属性的键(key)和值(value)作为参数,将键值对添加到mNoteDiffValues中, - // 同时更新笔记的本地修改标记(LOCAL_MODIFIED)为已修改状态,并设置修改日期(MODIFIED_DATE)为当前时间,以便后续能准确判断笔记是否有修改以及进行相应的更新操作。 - + public void setNoteValue(String key, String value) { mNoteDiffValues.put(key, value); mNoteDiffValues.put(NoteColumns.LOCAL_MODIFIED, 1); mNoteDiffValues.put(NoteColumns.MODIFIED_DATE, System.currentTimeMillis()); } - // 用于设置笔记文本数据的方法,调用内部NoteData对象的setTextData方法,将文本数据的键值对传递给它进行处理,内部会根据情况更新相关的修改标记和日期等信息。 - + public void setTextData(String key, String value) { mNoteData.setTextData(key, value); } - // 用于设置笔记文本数据的唯一标识符(ID)的方法,直接调用内部NoteData对象的setTextDataId方法,将传入的ID值传递给它进行设置,同时会进行参数合法性检查(ID需大于0)。 - + public void setTextDataId(long id) { mNoteData.setTextDataId(id); } - // 用于获取笔记文本数据的唯一标识符(ID)的方法,直接返回内部NoteData对象中存储的文本数据ID(mTextDataId)。 - + public long getTextDataId() { return mNoteData.mTextDataId; } - // 用于设置笔记通话数据的唯一标识符(ID)的方法,调用内部NoteData对象的setCallDataId方法,将传入的ID值传递给它进行设置,同时会进行参数合法性检查(ID需大于0)。 - + public void setCallDataId(long id) { mNoteData.setCallDataId(id); } - // 用于设置笔记通话数据的方法,调用内部NoteData对象的setCallData方法,将通话数据的键值对传递给它进行处理,内部会根据情况更新相关的修改标记和日期等信息。 - + public void setCallData(String key, String value) { mNoteData.setCallData(key, value); } -// 用于判断笔记是否在本地有修改的方法,通过检查mNoteDiffValues中是否有数据(即是否有属性被修改)以及内部NoteData对象是否有本地修改来综合判断, - // 如果两者中任意一个有数据变化,则表示笔记在本地有修改,返回true,否则返回false。 + public boolean isLocalModified() { return mNoteDiffValues.size() > 0 || mNoteData.isLocalModified(); } - // 用于将本地修改后的笔记数据同步到本地数据库的方法,接收应用上下文(Context)和笔记的ID(noteId)作为参数,首先进行参数合法性检查(noteId需大于0), - // 如果笔记没有本地修改则直接返回true,表示无需同步操作。若有本地修改,则先尝试通过内容解析器(ContentResolver)根据mNoteDiffValues中的数据更新笔记的基本属性信息, - // 如果更新失败(返回0表示影响的行数为0,即更新未成功)则记录错误日志但不立即返回,继续往下执行,接着清空mNoteDiffValues已同步的数据。 - // 然后检查内部NoteData对象是否有本地修改,如果有且将其数据推送到内容解析器(通过pushIntoContentResolver方法)失败,则返回false表示同步失败, - // 否则返回true表示整个笔记数据(包括基本属性和具体数据内容)同步成功。 + public boolean syncNote(Context context, long noteId) { if (noteId <= 0) { throw new IllegalArgumentException("Wrong note id:" + noteId); @@ -155,44 +129,35 @@ public class Note { return true; } -// 内部私有类NoteData,用于更细致地管理笔记中的具体数据内容,包括文本数据和通话数据相关的属性、操作以及与内容提供者的交互逻辑,将这些与数据内容相关的操作封装在内部类中, - // 使Note类整体结构更清晰,职责更明确,避免不同类型数据操作的代码相互混杂。 + private class NoteData { private long mTextDataId; - // 用于存储笔记文本数据的唯一标识符(ID),初始化为0,在插入或更新文本数据时会根据情况进行赋值或更新。 - // 用于存储笔记文本数据的具体属性值(以键值对形式),通过ContentValues方便后续与内容提供者进行插入或更新操作,记录文本数据的相关内容。 - + private ContentValues mTextDataValues; -// 用于存储笔记通话数据的唯一标识符(ID),初始化为0,在插入或更新通话数据时会根据情况进行赋值或更新。 - + private long mCallDataId; -// 用于存储笔记通话数据的具体属性值(以键值对形式),通过ContentValues方便后续与内容提供者进行插入或更新操作,记录通话数据的相关内容。 - + private ContentValues mCallDataValues; - // 用于日志记录的标签,方便在日志中识别该内部类相关的输出信息,取固定的字符串"NoteData"。 + private static final String TAG = "NoteData"; - // NoteData类的默认构造函数,用于初始化NoteData对象,创建新的ContentValues对象分别用于存储文本数据和通话数据的属性值,同时将文本数据和通话数据的ID初始化为0。 - + public NoteData() { mTextDataValues = new ContentValues(); mCallDataValues = new ContentValues(); mTextDataId = 0; mCallDataId = 0; } -// 用于判断NoteData对象中的文本数据或通话数据是否有本地修改的方法,通过检查mTextDataValues和mCallDataValues中是否有数据(即是否有属性被修改)来综合判断, - // 如果两者中任意一个有数据变化,则表示有本地修改,返回true,否则返回false。 + boolean isLocalModified() { return mTextDataValues.size() > 0 || mCallDataValues.size() > 0; } -// 用于设置笔记文本数据的唯一标识符(ID)的方法,进行参数合法性检查(ID需大于0),如果合法则将传入的ID值赋值给mTextDataId,用于后续对文本数据的操作定位等。 - + void setTextDataId(long id) { if(id <= 0) { throw new IllegalArgumentException("Text data id should larger than 0"); } mTextDataId = id; - } // 用于设置笔记通话数据的唯一标识符(ID)的方法,进行参数合法性检查(ID需大于0),如果合法则将传入的ID值赋值给mCallDataId,用于后续对通话数据的操作定位等。 - + } void setCallDataId(long id) { if (id <= 0) { @@ -200,30 +165,19 @@ public class Note { } mCallDataId = id; } - // 用于设置笔记通话数据的方法,接收通话数据的键(key)和值(value)作为参数,将键值对添加到mCallDataValues中, - // 同时更新笔记的本地修改标记(通过外部的mNoteDiffValues间接更新,因为NoteData是内部类可以访问外部类的成员)以及修改日期为当前时间,以便能准确判断笔记整体是否有修改以及进行相应的更新操作。 - + void setCallData(String key, String value) { mCallDataValues.put(key, value); mNoteDiffValues.put(NoteColumns.LOCAL_MODIFIED, 1); mNoteDiffValues.put(NoteColumns.MODIFIED_DATE, System.currentTimeMillis()); } -// 用于设置笔记文本数据的方法,接收文本数据的键(key)和值(value)作为参数,将键值对添加到mTextDataValues中, - // 同时更新笔记的本地修改标记(通过外部的mNoteDiffValues间接更新)以及修改日期为当前时间,以便能准确判断笔记整体是否有修改以及进行相应的更新操作。 - + void setTextData(String key, String value) { mTextDataValues.put(key, value); mNoteDiffValues.put(NoteColumns.LOCAL_MODIFIED, 1); mNoteDiffValues.put(NoteColumns.MODIFIED_DATE, System.currentTimeMillis()); } - // 用于将NoteData对象中管理的文本数据和通话数据推送到内容解析器(即更新或插入到本地数据库)的方法,接收应用上下文(Context)和笔记的ID(noteId)作为参数, - // 首先进行参数合法性检查(noteId需大于0),然后创建一个ContentProviderOperation列表(用于批量操作),根据mTextDataValues和mCallDataValues中的数据情况进行不同的操作: - // 如果文本数据有值(mTextDataValues.size() > 0),则设置笔记ID到文本数据属性中,如果文本数据的ID为0(表示是新数据需要插入),则设置文本数据的MIME类型, - // 通过内容解析器将文本数据插入到本地数据库对应的内容URI(Notes.CONTENT_DATA_URI)中,并尝试从插入后返回的Uri中解析出文本数据的ID,如果解析失败则记录错误日志并清空文本数据属性值,返回null表示操作失败; - // 如果文本数据的ID不为0(表示是已有数据需要更新),则构建一个更新操作并添加到操作列表中。然后对通话数据进行类似的处理(根据mCallDataValues的情况进行插入或更新操作)。 - // 最后如果操作列表中有操作需要执行,则通过内容解析器批量应用这些操作(applyBatch方法),根据操作结果返回相应的Uri(如果成功则返回笔记对应的Uri,失败则返回null), - // 如果在批量操作过程中出现RemoteException或OperationApplicationException异常则记录错误日志并返回null表示操作失败。如果操作列表为空则直接返回null表示无需进行数据推送操作。 - + Uri pushIntoContentResolver(Context context, long noteId) { /** * Check for safety diff --git a/src/Notes-master/src/net/micode/notes/model/WorkingNote.java b/src/Notes-master/src/net/micode/notes/model/WorkingNote.java index 01faf6e..be081e4 100644 --- a/src/Notes-master/src/net/micode/notes/model/WorkingNote.java +++ b/src/Notes-master/src/net/micode/notes/model/WorkingNote.java @@ -30,55 +30,38 @@ import net.micode.notes.data.Notes.DataConstants; import net.micode.notes.data.Notes.NoteColumns; import net.micode.notes.data.Notes.TextNote; import net.micode.notes.tool.ResourceParser.NoteBgResources; -// `WorkingNote` 类代表正在操作(编辑、修改等)的笔记对象,它封装了笔记的各种属性(如内容、ID、提醒日期、背景颜色等)以及相关的操作方法(如保存、加载、设置属性值等), -// 同时定义了一个接口用于监听笔记设置相关属性变化的情况,以便在属性改变时能做出相应的业务逻辑处理,是笔记在应用中实际使用场景下的一个综合数据和操作模型。 + public class WorkingNote { - // 关联的 `Note` 类对象,用于处理笔记底层的数据操作(如同步到数据库等),借助 `Note` 类已有的功能来实现 `WorkingNote` 的相关持久化等操作。 - // Note for the working note private Note mNote; - // 笔记的唯一标识符(ID),用于在数据库等场景中唯一标识该笔记,初始值根据不同的构造函数可能为0(新创建笔记时)或从已有数据中获取的具体ID值。 - // Note Id private long mNoteId; - // 笔记的具体内容,比如用户输入的文本等信息,用于展示和编辑笔记的核心文本数据。 - // Note content private String mContent; - // 笔记的模式,可能用于表示不同的展示或编辑模式(例如是否为清单模式等),具体含义由应用业务逻辑决定,初始化为0。 - // Note mode private int mMode; - // 笔记的提醒日期,用于设置和记录该笔记是否有提醒以及提醒的时间点,初始化为0表示无提醒。 + private long mAlertDate; - // 笔记的最后修改日期,用于记录笔记最后一次被修改的时间,初始化为当前系统时间(在构造新笔记时)。 + private long mModifiedDate; -// 笔记的背景颜色资源ID,用于设置和获取笔记展示时的背景颜色,初始值会根据具体业务场景设定或默认值赋值。 + private int mBgColorId; -// 关联的桌面小部件(Widget)的ID,用于标识该笔记是否与某个桌面小部件相关联以及是哪个小部件,初始值根据情况设定,若未关联则可能为无效值(`AppWidgetManager.INVALID_APPWIDGET_ID`)。 - + private int mWidgetId; - // 桌面小部件的类型,用于区分不同类型的小部件与笔记的关联情况,初始值根据业务逻辑设定,可能有特定的枚举值(如 `Notes.TYPE_WIDGET_INVALIDE` 表示无效类型等)。 - + private int mWidgetType; - // 笔记所属文件夹的ID,用于表示笔记在文件系统结构中的所属位置,便于分类管理笔记,初始值根据构造函数传入参数确定。 - + private long mFolderId; - // 应用的上下文(Context)对象,用于获取系统服务、资源以及与内容提供者(Content Provider)等进行交互,是整个类中很多操作的基础依赖。 - + private Context mContext; -// 用于日志记录的标签,方便在日志中识别该类相关的输出信息,取固定的字符串"WorkingNote"。 - + private static final String TAG = "WorkingNote"; - // 标记笔记是否已被删除的布尔值,初始化为 `false`,通过相关方法可以设置为 `true` 来表示笔记已被删除状态,在保存等操作中会根据此标记进行相应处理。 - + private boolean mIsDeleted; - // 定义一个接口类型的变量,用于监听笔记设置相关属性(如背景颜色、提醒等)变化的情况,外部类可以实现该接口并注册进来,以便在属性改变时能收到通知并执行相应逻辑。 - + private NoteSettingChangedListener mNoteSettingStatusListener; -// 定义一个字符串数组,用于查询笔记数据时指定要获取的列,包含了数据的ID、内容、MIME类型以及一些其他自定义的数据字段(DATA1 - DATA4),方便从数据库中获取完整的笔记数据信息。 - + public static final String[] DATA_PROJECTION = new String[] { DataColumns.ID, DataColumns.CONTENT, @@ -88,8 +71,7 @@ public class WorkingNote { DataColumns.DATA3, DataColumns.DATA4, }; - // 定义一个字符串数组,用于查询笔记基本属性时指定要获取的列,包含了父文件夹ID、提醒日期、背景颜色ID、小部件ID、小部件类型以及修改日期等字段,用于从数据库中获取笔记的关键属性信息。 - + public static final String[] NOTE_PROJECTION = new String[] { NoteColumns.PARENT_ID, NoteColumns.ALERTED_DATE, @@ -98,39 +80,27 @@ public class WorkingNote { NoteColumns.WIDGET_TYPE, NoteColumns.MODIFIED_DATE }; - // 定义一个常量,表示在 `DATA_PROJECTION` 数组中数据ID列的索引位置,方便在查询结果的游标(Cursor)中准确获取对应的数据,值为0,对应数组的第一个元素位置。 - + private static final int DATA_ID_COLUMN = 0; - // 定义一个常量,表示在 `DATA_PROJECTION` 数组中数据内容列的索引位置,用于从游标中获取笔记的具体内容数据,值为1。 - + private static final int DATA_CONTENT_COLUMN = 1; -// 定义一个常量,表示在 `DATA_PROJECTION` 数组中数据MIME类型列的索引位置,用于判断数据的类型(如文本笔记、通话笔记等),值为2。 - + private static final int DATA_MIME_TYPE_COLUMN = 2; - // 定义一个常量,表示在 `DATA_PROJECTION` 数组中数据模式列(可能用于表示笔记的特定展示或编辑模式)的索引位置,值为3。 - + private static final int DATA_MODE_COLUMN = 3; - // 定义一个常量,表示在 `NOTE_PROJECTION` 数组中父文件夹ID列的索引位置,用于从游标中获取笔记所属文件夹的ID信息,值为0。 - + private static final int NOTE_PARENT_ID_COLUMN = 0; - // 定义一个常量,表示在 `NOTE_PROJECTION` 数组中提醒日期列的索引位置,用于获取笔记的提醒时间信息,值为1。 - + private static final int NOTE_ALERTED_DATE_COLUMN = 1; - // 定义一个常量,表示在 `NOTE_PROJECTION` 数组中背景颜色ID列的索引位置,用于获取笔记的背景颜色设置信息,值为2。 - + private static final int NOTE_BG_COLOR_ID_COLUMN = 2; - // 定义一个常量,表示在 `NOTE_PROJECTION` 数组中小部件ID列的索引位置,用于获取与笔记关联的小部件的ID信息,值为3。 - + private static final int NOTE_WIDGET_ID_COLUMN = 3; - // 定义一个常量,表示在 `NOTE_PROJECTION` 数组中小部件类型列的索引位置,用于获取小部件的类型信息,值为4。 - - private static fina int NOTE_WIDGET_TYPE_COLUMN = 4; -// 定义一个常量,表示在 `NOTE_PROJECTION` 数组中修改日期列的索引位置,用于获取笔记最后一次被修改的时间信息,值为5。 - + + private static final int NOTE_WIDGET_TYPE_COLUMN = 4; + private static final int NOTE_MODIFIED_DATE_COLUMN = 5; - // 私有构造函数,用于创建一个新的(空的)工作笔记对象,接收应用上下文(Context)和所属文件夹的ID作为参数,初始化各种属性的初始值, - // 如提醒日期设为0,修改日期设为当前系统时间,创建一个新的 `Note` 对象用于后续数据操作,笔记ID初始化为0,标记为未删除状态,笔记模式设为0,小部件类型设为无效类型等。 - + // New note construct private WorkingNote(Context context, long folderId) { mContext = context; @@ -143,9 +113,7 @@ public class WorkingNote { mMode = 0; mWidgetType = Notes.TYPE_WIDGET_INVALIDE; } -// 私有构造函数,用于加载一个已存在的笔记对象,接收应用上下文(Context)、笔记的ID(noteId)以及所属文件夹的ID作为参数,初始化相关属性, - // 然后调用 `loadNote` 方法从数据库中加载笔记的基本属性信息,再调用 `loadNoteData` 方法加载笔记的具体数据内容(如文本内容等)。 - + // Existing note construct private WorkingNote(Context context, long noteId, long folderId) { mContext = context; @@ -156,11 +124,7 @@ public class WorkingNote { loadNote(); } - // 私有方法,用于从数据库中加载笔记的基本属性信息,通过内容解析器(ContentResolver)根据笔记的ID查询指定列(`NOTE_PROJECTION`)的数据, - // 如果查询到游标(Cursor)不为空且能移动到第一条记录(表示有数据),则从游标中获取父文件夹ID、背景颜色ID、小部件ID、小部件类型、提醒日期以及修改日期等属性值, - // 最后关闭游标,若游标为空则表示找不到对应的笔记,记录错误日志并抛出异常表示无法找到指定ID的笔记。 - - private void loadNote() + private void loadNote() { Cursor cursor = mContext.getContentResolver().query( ContentUris.withAppendedId(Notes.CONTENT_NOTE_URI, mNoteId), NOTE_PROJECTION, null, null, null); @@ -181,10 +145,7 @@ public class WorkingNote { } loadNoteData(); } -// 私有方法,用于从数据库中加载笔记的具体数据内容,通过内容解析器查询笔记对应的数据(根据 `DATA_PROJECTION` 指定列),条件是数据所属的笔记ID与当前笔记ID匹配, - // 如果查询到游标不为空且能移动到第一条记录,则循环遍历游标(因为可能有多条数据,比如不同类型的数据),根据数据的MIME类型判断是文本笔记还是通话笔记等, - // 分别进行相应的处理(如设置文本数据ID、通话数据ID或者记录错误日志表示类型错误),最后关闭游标,若游标为空则表示找不到对应笔记的数据,记录错误日志并抛出异常。 - + private void loadNoteData() { Cursor cursor = mContext.getContentResolver().query(Notes.CONTENT_DATA_URI, DATA_PROJECTION, DataColumns.NOTE_ID + "=?", new String[] { @@ -212,9 +173,7 @@ public class WorkingNote { throw new IllegalArgumentException("Unable to find note's data with id " + mNoteId); } } -// 静态工厂方法,用于创建一个新的空笔记对象,接收应用上下文(Context)、所属文件夹的ID、小部件ID、小部件类型以及默认背景颜色资源ID作为参数, - // 创建一个 `WorkingNote` 对象,设置其背景颜色ID、小部件ID和小部件类型属性值,最后返回创建好的对象,方便在需要创建新笔记时统一调用此方法进行初始化操作。 - + public static WorkingNote createEmptyNote(Context context, long folderId, int widgetId, int widgetType, int defaultBgColorId) { WorkingNote note = new WorkingNote(context, folderId); @@ -223,9 +182,7 @@ public class WorkingNote { note.setWidgetType(widgetType); return note; } - // 静态工厂方法,用于加载一个已存在的笔记对象,接收应用上下文(Context)和笔记的ID作为参数,通过调用私有构造函数(传入相应参数)创建并返回对应的 `WorkingNote` 对象, - // 方便在需要获取并操作已有笔记时统一使用此方法进行加载操作。 - + public static WorkingNote load(Context context, long id) { return new WorkingNote(context, id, 0); } @@ -254,17 +211,11 @@ public class WorkingNote { return false; } } - // 同步方法,用于保存笔记的相关数据到数据库,首先通过 `isWorthSaving` 方法判断笔记是否值得保存(比如是否已删除、内容是否为空且不存在于数据库、是否有本地修改等情况), - // 如果值得保存,若笔记不存在于数据库中(通过 `existInDatabase` 方法判断,即笔记ID小于等于0),则调用 `Note` 类的 `getNewNoteId` 方法尝试创建一个新的笔记ID, - // 如果创建失败则记录错误日志并返回 `false`,若创建成功则获取到新的笔记ID。然后调用 `mNote`(`Note` 类对象)的 `syncNote` 方法将笔记数据同步到数据库中。 - // 最后,如果笔记关联了有效的小部件(小部件ID不为无效值且小部件类型有效)并且设置了 `NoteSettingChangedListener` 监听器,则调用监听器的 `onWidgetChanged` 方法通知小部件相关内容可能已改变, - // 整个保存操作成功则返回 `true`,否则返回 `false`。 - + public boolean existInDatabase() { return mNoteId > 0; } - // 用于判断笔记是否已经存在于数据库中的方法,通过检查笔记的ID是否大于0来返回相应的布尔值,如果大于0则表示已存在,否则表示不存在。 - + private boolean isWorthSaving() { if (mIsDeleted || (!existInDatabase() && TextUtils.isEmpty(mContent)) || (existInDatabase() && !mNote.isLocalModified())) { @@ -273,15 +224,11 @@ public class WorkingNote { return true; } } - // 私有方法,用于判断笔记是否值得保存,根据多种情况综合判断,比如如果笔记已被标记为删除(`mIsDeleted` 为 `true`), - // 或者笔记不存在于数据库中且内容为空(通过 `TextUtils.isEmpty` 判断内容是否为空字符串),或者笔记已存在于数据库中但没有本地修改(通过 `mNote.isLocalModified` 判断), - // 这些情况下返回 `false`,表示不值得保存,否则返回 `true`,表示值得保存。 + public void setOnSettingStatusChangedListener(NoteSettingChangedListener l) { mNoteSettingStatusListener = l; } - // 用于设置笔记设置状态变化监听器的方法,接收一个实现了 `NoteSettingChangedListener` 接口的对象作为参数, - // 将其赋值给 `mNoteSettingStatusListener` 变量,这样当笔记的相关设置属性(如背景颜色、提醒等)发生变化时,对应的实现类中的方法就会被调用,实现相应的业务逻辑处理。 - + public void setAlertDate(long date, boolean set) { if (date != mAlertDate) { mAlertDate = date; @@ -291,10 +238,7 @@ public class WorkingNote { mNoteSettingStatusListener.onClockAlertChanged(date, set); } } - // 用于标记笔记是否被删除的方法,接收一个布尔值(`mark`)作为参数,将 `mIsDeleted` 属性设置为传入的布尔值, - // 如果笔记关联了有效的小部件(小部件ID不为无效值且小部件类型有效)并且设置了 `NoteSettingChangedListener` 监听器,则调用监听器的 `onWidgetChanged` 方法通知小部件相关内容可能已改变, - // 以此来应对笔记删除状态变化后可能需要的业务逻辑处理(比如小部件中对应笔记的显示更新等)。 - + public void markDeleted(boolean mark) { mIsDeleted = mark; if (mWidgetId != AppWidgetManager.INVALID_APPWIDGET_ID @@ -302,10 +246,7 @@ public class WorkingNote { mNoteSettingStatusListener.onWidgetChanged(); } } - // 用于设置笔记背景颜色资源ID的方法,接收一个整数(`id`)作为参数, - // 首先比较传入的ID和当前的背景颜色ID是否不同,如果不同则更新 `mBgColorId` 属性为传入的ID,并通过 `mNote` 对象调用 `setNoteValue` 方法将背景颜色ID的值设置到笔记的对应属性中, - // 然后如果设置了 `NoteSettingChangedListener` 监听器,则调用其 `onBackgroundColorChanged` 方法通知背景颜色已改变,以便外部进行相应的界面更新等操作。 - + public void setBgColorId(int id) { if (id != mBgColorId) { mBgColorId = id; @@ -315,10 +256,7 @@ public class WorkingNote { mNote.setNoteValue(NoteColumns.BG_COLOR_ID, String.valueOf(id)); } } -// 用于设置笔记的清单模式(可能用于切换笔记的展示或编辑模式为清单形式等)的方法,接收一个整数(`mode`)作为参数, - // 首先比较传入的模式和当前的模式是否不同,如果不同则更新 `mMode` 属性为传入的模式,并通过 `mNote` 对象调用 `setTextData` 方法将模式的值设置到笔记的对应属性中, - // 然后如果设置了 `NoteSettingChangedListener` 监听器,则调用其 `onCheckListModeChanged` 方法通知清单模式已改变,同时传入旧模式和新模式的值,方便外部进行相应的业务逻辑处理(比如界面切换显示等)。 - + public void setCheckListMode(int mode) { if (mMode != mode) { if (mNoteSettingStatusListener != null) { @@ -328,17 +266,13 @@ public class WorkingNote { mNote.setTextData(TextNote.MODE, String.valueOf(mMode)); } } -// 用于设置笔记关联的桌面小部件类型的方法,接收一个整数(`type`)作为参数, - // 比较传入的类型和当前的小部件类型是否不同,如果不同则更新 `mWidgetType` 属性为传入的类型,并通过 `mNote` 对象调用 `setNoteValue` 方法将小部件类型的值设置到笔记的对应属性中。 - + public void setWidgetType(int type) { if (type != mWidgetType) { mWidgetType = type; mNote.setNoteValue(NoteColumns.WIDGET_TYPE, String.valueOf(mWidgetType)); } - } // 用于设置笔记关联的桌面小部件ID的方法,接收一个整数(`id`)作为参数, - // 比较传入的ID和当前的小部件ID是否不同,如果不同则更新 `mWidgetId` 属性为传入的ID,并通过 `mNote` 对象调用 `setNoteValue` 方法将小部件ID的值设置到笔记的对应属性中。 - + } public void setWidgetId(int id) { if (id != mWidgetId) { @@ -346,89 +280,68 @@ public class WorkingNote { mNote.setNoteValue(NoteColumns.WIDGET_ID, String.valueOf(mWidgetId)); } } - // 用于设置笔记的工作文本内容(即核心文本数据)的方法,接收一个字符串(`text`)作为参数, - // 比较传入的文本和当前的文本内容是否相同(通过 `TextUtils.equals` 判断),如果不同则更新 `mContent` 属性为传入的文本,并通过 `mNote` 对象调用 `setTextData` 方法将文本内容设置到笔记的对应属性中。 - + public void setWorkingText(String text) { if (!TextUtils.equals(mContent, text)) { mContent = text; mNote.setTextData(DataColumns.CONTENT, mContent); } } - // 用于将笔记转换为通话笔记的方法,接收电话号码(`phoneNumber`)和通话日期(`callDate`)作为参数, - // 通过 `mNote` 对象分别调用 `setCallData` 方法设置通话日期和电话号码到笔记的对应属性中,同时调用 `setNoteValue` 方法将笔记的父文件夹ID设置为通话记录文件夹的ID(`Notes.ID_CALL_RECORD_FOLDER`), - // 以此来完成笔记类型向通话笔记的转换,可能涉及到对应数据结构和展示等方面的改变,具体由应用的业务逻辑决定。 - + public void convertToCallNote(String phoneNumber, long callDate) { mNote.setCallData(CallNote.CALL_DATE, String.valueOf(callDate)); mNote.setCallData(CallNote.PHONE_NUMBER, phoneNumber); mNote.setNoteValue(NoteColumns.PARENT_ID, String.valueOf(Notes.ID_CALL_RECORD_FOLDER)); } - // 用于判断笔记是否设置了时钟提醒的方法,通过检查 `mAlertDate` 是否大于0来返回相应的布尔值,如果大于0则表示设置了提醒,返回 `true`,否则返回 `false`。 - + public boolean hasClockAlert() { return (mAlertDate > 0 ? true : false); } -// 用于获取笔记内容的方法,直接返回 `mContent` 属性,即笔记的核心文本数据,方便外部获取并展示笔记的具体内容。 - + public String getContent() { return mContent; } - // 用于获取笔记提醒日期的方法,直接返回 `mAlertDate` 属性,即笔记设置的提醒时间点,方便外部查询和使用该提醒日期信息。 - + public long getAlertDate() { return mAlertDate; } - // 用于获取笔记最后修改日期的方法,直接返回 `mModifiedDate` 属性,即笔记最后一次被修改的时间记录,可用于展示、排序等业务场景。 - + public long getModifiedDate() { return mModifiedDate; } - // 用于获取笔记背景颜色资源ID对应的实际背景颜色资源的方法,通过调用 `NoteBgResources` 类的 `getNoteBgResource` 方法(具体实现可能根据资源映射等逻辑获取实际颜色资源), - // 将 `mBgColorId` 作为参数传入,返回获取到的背景颜色资源ID,方便外部进行界面绘制等操作时使用正确的背景颜色资源。 - + public int getBgColorResId() { return NoteBgResources.getNoteBgResource(mBgColorId); } - // 用于获取笔记背景颜色资源ID的方法,直接返回 `mBgColorId` 属性,方便外部获取并记录笔记的背景颜色设置情况。 - + public int getBgColorId() { return mBgColorId; } - // 用于获取笔记标题背景颜色资源ID的方法,通过调用 `NoteBgResources` 类的 `getNoteTitleBgResource` 方法(具体实现类似获取背景颜色资源的逻辑,根据ID获取对应的标题背景颜色资源), - // 将 `mBgColorId` 作为参数传入,返回获取到的标题背景颜色资源ID,方便外部进行界面绘制等操作时使用正确的标题背景颜色资源。 - + public int getTitleBgResId() { return NoteBgResources.getNoteTitleBgResource(mBgColorId); } - // 用于获取笔记的清单模式的方法,直接返回 `mMode` 属性,即笔记当前设置的清单模式值,方便外部获取并判断笔记处于何种展示或编辑模式。 - + public int getCheckListMode() { return mMode; - } // 用于获取笔记的唯一标识符(ID)的方法,直接返回 `mNoteId` 属性,方便外部在数据库操作、关联查询等场景中使用该笔记的唯一标识。 - + } public long getNoteId() { return mNoteId; } - // 用于获取笔记所属文件夹的ID的方法,直接返回 `mFolderId` 属性,方便外部了解笔记在文件系统结构中的位置分类情况,比如用于文件夹相关的查询、统计等操作。 - + public long getFolderId() { return mFolderId; } - // 用于获取笔记关联的桌面小部件ID的方法,直接返回 `mWidgetId` 属性,方便外部获取并操作与笔记相关联的桌面小部件(比如更新小部件显示内容等)。 - + public int getWidgetId() { return mWidgetId; } - // 用于获取笔记关联的桌面小部件类型的方法,直接返回 `mWidgetType` 属性,方便外部获取并判断笔记关联的小部件属于何种类型,以进行相应的业务逻辑处理。 - + public int getWidgetType() { return mWidgetType; } - // 定义一个接口,用于监听笔记设置相关属性变化的情况,外部类需要实现该接口并通过 `setOnSettingStatusChangedListener` 方法注册进来, - // 接口中定义了多个方法,分别对应不同属性变化时的回调通知,比如背景颜色改变、时钟提醒改变、小部件相关改变以及清单模式改变等情况,方便在这些属性变化时执行相应的业务逻辑处理。 - + public interface NoteSettingChangedListener { /** * Called when the background color of current note has just changed diff --git a/src/Notes-master/src/net/micode/notes/tool/BackupUtils.java b/src/Notes-master/src/net/micode/notes/tool/BackupUtils.java index a95273a..39f6ec4 100644 --- a/src/Notes-master/src/net/micode/notes/tool/BackupUtils.java +++ b/src/Notes-master/src/net/micode/notes/tool/BackupUtils.java @@ -35,66 +35,57 @@ import java.io.FileOutputStream; import java.io.IOException; import java.io.PrintStream; -// BackupUtils类用于处理笔记数据的备份相关功能,支持将笔记数据导出为文本格式等操作 + public class BackupUtils { private static final String TAG = "BackupUtils"; - // 单例模式相关,用于保存唯一的BackupUtils实例 // Singleton stuff private static BackupUtils sInstance; -// 获取BackupUtils的单例实例,若不存在则创建新的实例 + public static synchronized BackupUtils getInstance(Context context) { if (sInstance == null) { sInstance = new BackupUtils(context); } return sInstance; } - /** - * 以下是用于表示备份或恢复状态的常量定义 - */ - // 当前外部存储(SD卡)未挂载的状态码 + /** * Following states are signs to represents backup or restore * status */ // Currently, the sdcard is not mounted public static final int STATE_SD_CARD_UNMOUONTED = 0; - // 备份文件不存在的状态码 // The backup file not exist public static final int STATE_BACKUP_FILE_NOT_EXIST = 1; // The data is not well formated, may be changed by other programs - // 数据格式不正确,可能被其他程序修改的状态码 public static final int STATE_DATA_DESTROIED = 2; // Some run-time exception which causes restore or backup fails - // 运行时出现异常导致备份或恢复失败的状态码 public static final int STATE_SYSTEM_ERROR = 3; // Backup or restore success - // 备份或恢复成功的状态码 public static final int STATE_SUCCESS = 4; private TextExport mTextExport; - // 私有构造函数,初始化时创建TextExport对象,用于文本导出相关操作 + private BackupUtils(Context context) { mTextExport = new TextExport(context); } -// 判断外部存储(通常指SD卡)是否可用,通过比较外部存储状态与MEDIA_MOUNTED常量来判断 + private static boolean externalStorageAvailable() { return Environment.MEDIA_MOUNTED.equals(Environment.getExternalStorageState()); } - // 调用TextExport的exportToText方法进行文本导出,并返回导出结果状态码 + public int exportToText() { return mTextExport.exportToText(); } - // 获取导出的文本文件名,实际是通过TextExport对象获取其内部保存的文件名 + public String getExportedTextFileName() { return mTextExport.mFileName; } - // 获取导出的文本文件所在目录,通过TextExport对象获取其内部保存的文件目录 + public String getExportedTextFileDir() { return mTextExport.mFileDirectory; } - // TextExport内部类,主要负责具体的文本导出逻辑实现 + private static class TextExport { - // 查询笔记相关信息的投影数组,定义了要从数据库中获取的笔记字段 private static final String[] NOTE_PROJECTION = { NoteColumns.ID, NoteColumns.MODIFIED_DATE, @@ -107,7 +98,7 @@ public class BackupUtils { private static final int NOTE_COLUMN_MODIFIED_DATE = 1; private static final int NOTE_COLUMN_SNIPPET = 2; - // 查询笔记数据相关信息的投影数组,定义了要从数据库中获取的笔记数据字段 + private static final String[] DATA_PROJECTION = { DataColumns.CONTENT, DataColumns.MIME_TYPE, @@ -125,7 +116,6 @@ public class BackupUtils { private static final int DATA_COLUMN_PHONE_NUMBER = 4; - // 用于格式化导出文本内容的字符串数组,通过资源获取,不同索引对应不同的格式化用途 private final String [] TEXT_FORMAT; private static final int FORMAT_FOLDER_NAME = 0; private static final int FORMAT_NOTE_DATE = 1; @@ -134,25 +124,22 @@ public class BackupUtils { private Context mContext; private String mFileName; private String mFileDirectory; - // 构造函数,初始化TEXT_FORMAT数组以及保存传入的上下文对象等信息 + public TextExport(Context context) { TEXT_FORMAT = context.getResources().getStringArray(R.array.format_for_exported_note); mContext = context; mFileName = ""; mFileDirectory = ""; } - // 根据传入的索引获取对应的格式化字符串 + private String getFormat(int id) { return TEXT_FORMAT[id]; } - /** - * 将指定文件夹(通过folderId标识)中的笔记导出为文本格式,并写入到给定的PrintStream中 - */ + /** * Export the folder identified by folder id to text */ private void exportFolderToText(String folderId, PrintStream ps) { - // 通过内容解析器查询属于该文件夹的笔记信息 // Query notes belong to this folder Cursor notesCursor = mContext.getContentResolver().query(Notes.CONTENT_NOTE_URI, NOTE_PROJECTION, NoteColumns.PARENT_ID + "=?", new String[] { @@ -161,29 +148,24 @@ public class BackupUtils { if (notesCursor != null) { if (notesCursor.moveToFirst()) { - do { // 格式化并打印笔记的最后修改日期 + do { // Print note's last modified date ps.println(String.format(getFormat(FORMAT_NOTE_DATE), DateFormat.format( mContext.getString(R.string.format_datetime_mdhm), notesCursor.getLong(NOTE_COLUMN_MODIFIED_DATE)))); - // 获取当前笔记的ID,用于进一步查询该笔记相关的数据信息并导出 // Query data belong to this note String noteId = notesCursor.getString(NOTE_COLUMN_ID); exportNoteToText(noteId, ps); } while (notesCursor.moveToNext()); } - // 关闭游标,释放资源 notesCursor.close(); } } - /** - * 将指定ID的笔记导出为文本格式,并写入到给定的PrintStream中 - */ + /** * Export note identified by id to a print stream */ private void exportNoteToText(String noteId, PrintStream ps) { - // 通过内容解析器查询属于该笔记的相关数据信息 Cursor dataCursor = mContext.getContentResolver().query(Notes.CONTENT_DATA_URI, DATA_PROJECTION, DataColumns.NOTE_ID + "=?", new String[] { noteId @@ -192,7 +174,6 @@ public class BackupUtils { if (dataCursor != null) { if (dataCursor.moveToFirst()) { do { - // 获取电话号码并打印(如果非空) String mimeType = dataCursor.getString(DATA_COLUMN_MIME_TYPE); if (DataConstants.CALL_NOTE.equals(mimeType)) { // Print phone number @@ -204,12 +185,10 @@ public class BackupUtils { ps.println(String.format(getFormat(FORMAT_NOTE_CONTENT), phoneNumber)); } - // 格式化并打印通话日期 // Print call date ps.println(String.format(getFormat(FORMAT_NOTE_CONTENT), DateFormat .format(mContext.getString(R.string.format_datetime_mdhm), callDate))); - // 打印通话附件位置(如果非空) // Print call attachment location if (!TextUtils.isEmpty(location)) { ps.println(String.format(getFormat(FORMAT_NOTE_CONTENT), @@ -224,10 +203,8 @@ public class BackupUtils { } } while (dataCursor.moveToNext()); } - // 关闭游标,释放资源 dataCursor.close(); } - // 在每个笔记内容之间写入换行符,用于分隔不同笔记内容 // print a line separator between note try { ps.write(new byte[] { @@ -237,14 +214,11 @@ public class BackupUtils { Log.e(TAG, e.toString()); } } - /** - * 执行将笔记导出为用户可读的文本格式的主要逻辑,整合了文件夹和笔记的导出操作 - */ + /** * Note will be exported as text which is user readable */ public int exportToText() { - // 首先判断外部存储是否可用,如果不可用则返回对应状态码并记录日志 if (!externalStorageAvailable()) { Log.d(TAG, "Media was not mounted"); return STATE_SD_CARD_UNMOUONTED; @@ -255,7 +229,6 @@ public class BackupUtils { Log.e(TAG, "get print stream error"); return STATE_SYSTEM_ERROR; } - // 第一步:查询并导出文件夹(特定条件筛选的文件夹,如通话记录文件夹等)及其内部笔记信息 // First export folder and its notes Cursor folderCursor = mContext.getContentResolver().query( Notes.CONTENT_NOTE_URI, @@ -280,10 +253,10 @@ public class BackupUtils { String folderId = folderCursor.getString(NOTE_COLUMN_ID); exportFolderToText(folderId, ps); } while (folderCursor.moveToNext()); - } // 关闭游标,释放资源 + } folderCursor.close(); } - // 第二步:查询并导出根文件夹下的笔记信息 + // Export notes in root's folder Cursor noteCursor = mContext.getContentResolver().query( Notes.CONTENT_NOTE_URI, @@ -301,16 +274,14 @@ public class BackupUtils { String noteId = noteCursor.getString(NOTE_COLUMN_ID); exportNoteToText(noteId, ps); } while (noteCursor.moveToNext()); - } // 关闭游标,释放资源 + } noteCursor.close(); } ps.close(); - // 关闭PrintStream,完成文件写入操作 + return STATE_SUCCESS; } - /** - * 获取一个指向要导出的文本文件的PrintStream对象,用于后续写入导出内容 - */ + /** * Get a print stream pointed to the file {@generateExportedTextFile} */ @@ -337,9 +308,7 @@ public class BackupUtils { return ps; } } - /** - * 在外部存储(SD卡)上生成用于存储导入数据的文本文件,如果生成过程出现异常则返回null - */ + /** * Generate the text file to store imported data */ diff --git a/src/Notes-master/src/net/micode/notes/tool/DataUtils.java b/src/Notes-master/src/net/micode/notes/tool/DataUtils.java index 85123d3..2a14982 100644 --- a/src/Notes-master/src/net/micode/notes/tool/DataUtils.java +++ b/src/Notes-master/src/net/micode/notes/tool/DataUtils.java @@ -33,118 +33,88 @@ import net.micode.notes.ui.NotesListAdapter.AppWidgetAttribute; import java.util.ArrayList; import java.util.HashSet; -// DataUtils类提供了一系列与笔记数据操作相关的实用方法,例如批量删除笔记、移动笔记到文件夹、查询各种数据状态等 public class DataUtils { public static final String TAG = "DataUtils"; - // 批量删除给定ID集合对应的笔记,跳过系统根文件夹(Notes.ID_ROOT_FOLDER)的删除操作 public static boolean batchDeleteNotes(ContentResolver resolver, HashSet ids) { - // 如果传入的ID集合为null,记录日志并返回true,表示无需执行删除操作 if (ids == null) { Log.d(TAG, "the ids is null"); return true; - }// 如果ID集合为空,记录日志并返回true,表示没有要删除的笔记 + } if (ids.size() == 0) { Log.d(TAG, "no id is in the hashset"); return true; } - // 用于存储一系列内容提供器操作的列表,后续将批量执行这些操作来删除笔记 + ArrayList operationList = new ArrayList(); for (long id : ids) { - // 不允许删除系统根文件夹,若当前ID是系统根文件夹ID,则记录错误日志并跳过该ID的删除操作 if(id == Notes.ID_ROOT_FOLDER) { Log.e(TAG, "Don't delete system folder root"); continue; - } // 创建一个用于删除指定笔记的内容提供器操作构建器,指定要删除的笔记的URI(通过ID构建) + } ContentProviderOperation.Builder builder = ContentProviderOperation .newDelete(ContentUris.withAppendedId(Notes.CONTENT_NOTE_URI, id)); - // 将构建好的操作添加到操作列表中 operationList.add(builder.build()); } try { - // 批量应用内容提供器操作列表,执行删除笔记的操作,并获取操作结果数组 ContentProviderResult[] results = resolver.applyBatch(Notes.AUTHORITY, operationList); - // 如果结果数组为null,或者长度为0,或者第一个结果为null,表示删除笔记失败,记录日志并返回false - if (results == null || results.length == 0 || results[0] == null) { Log.d(TAG, "delete notes failed, ids:" + ids.toString()); return false; } - // 如果成功执行删除操作,返回true return true; } catch (RemoteException e) { - // 捕获远程异常,记录详细的错误日志(包含异常的toString和getMessage信息) Log.e(TAG, String.format("%s: %s", e.toString(), e.getMessage())); } catch (OperationApplicationException e) { - // 捕获操作应用异常,记录详细的错误日志(包含异常的toString和getMessage信息) Log.e(TAG, String.format("%s: %s", e.toString(), e.getMessage())); } return false; } - // 将指定ID的笔记从源文件夹移动到目标文件夹,通过更新笔记的相关字段来实现移动操作 + public static void moveNoteToFoler(ContentResolver resolver, long id, long srcFolderId, long desFolderId) { - // 创建一个ContentValues对象,用于存储要更新的字段和对应的值 ContentValues values = new ContentValues(); - // 设置笔记的新父文件夹ID(目标文件夹ID) values.put(NoteColumns.PARENT_ID, desFolderId); - // 设置笔记的原始父文件夹ID(源文件夹ID) values.put(NoteColumns.ORIGIN_PARENT_ID, srcFolderId); - // 设置本地修改标志为1,表示该笔记有本地修改操作 values.put(NoteColumns.LOCAL_MODIFIED, 1); - // 通过内容解析器更新指定笔记(根据ID确定)的相关字段信息 resolver.update(ContentUris.withAppendedId(Notes.CONTENT_NOTE_URI, id), values, null, null); } - // 批量将给定ID集合中的笔记移动到指定的文件夹,通过构建一系列更新操作并批量执行来实现 + public static boolean batchMoveToFolder(ContentResolver resolver, HashSet ids, long folderId) { - // 如果传入的ID集合为null,记录日志并返回true,表示无需执行移动操作 if (ids == null) { Log.d(TAG, "the ids is null"); return true; } - // 用于存储一系列内容提供器操作的列表,后续将批量执行这些操作来移动笔记 + ArrayList operationList = new ArrayList(); for (long id : ids) { - // 创建一个用于更新指定笔记的内容提供器操作构建器,指定要更新的笔记的URI(通过ID构建) ContentProviderOperation.Builder builder = ContentProviderOperation .newUpdate(ContentUris.withAppendedId(Notes.CONTENT_NOTE_URI, id)); - // 设置笔记的新父文件夹ID(目标文件夹ID) builder.withValue(NoteColumns.PARENT_ID, folderId); - // 设置本地修改标志为1,表示该笔记有本地修改操作 builder.withValue(NoteColumns.LOCAL_MODIFIED, 1); - // 将构建好的操作添加到操作列表中 operationList.add(builder.build()); } try { - // 批量应用内容提供器操作列表,执行移动笔记的操作,并获取操作结果数组 ContentProviderResult[] results = resolver.applyBatch(Notes.AUTHORITY, operationList); - // 如果结果数组为null,或者长度为0,或者第一个结果为null,表示移动笔记失败,记录日志并返回false if (results == null || results.length == 0 || results[0] == null) { Log.d(TAG, "delete notes failed, ids:" + ids.toString()); return false; } - // 如果成功执行移动操作,返回true return true; } catch (RemoteException e) { - // 捕获远程异常,记录详细的错误日志(包含异常的toString和getMessage信息) Log.e(TAG, String.format("%s: %s", e.toString(), e.getMessage())); } catch (OperationApplicationException e) { - // 捕获操作应用异常,记录详细的错误日志(包含异常的toString和getMessage信息) Log.e(TAG, String.format("%s: %s", e.toString(), e.getMessage())); } return false; } - /** - * 获取除系统文件夹({@link Notes#TYPE_SYSTEM}类型的文件夹)之外的所有文件夹数量,通过数据库查询统计符合条件的文件夹数量 - */ + /** * Get the all folder count except system folders {@link Notes#TYPE_SYSTEM}} */ public static int getUserFolderCount(ContentResolver resolver) { - // 执行数据库查询,从Notes.CONTENT_NOTE_URI对应的表中查询符合条件的文件夹数量, - // 查询条件是文件夹类型为普通文件夹(Notes.TYPE_FOLDER)且父文件夹ID不是回收站文件夹ID(Notes.ID_TRASH_FOLER) Cursor cursor =resolver.query(Notes.CONTENT_NOTE_URI, new String[] { "COUNT(*)" }, NoteColumns.TYPE + "=? AND " + NoteColumns.PARENT_ID + "<>?", @@ -155,23 +125,18 @@ public class DataUtils { if(cursor != null) { if(cursor.moveToFirst()) { try { - // 尝试从查询结果游标中获取第一列(即统计的数量值)并赋值给count变量 count = cursor.getInt(0); } catch (IndexOutOfBoundsException e) { - // 若获取数据时出现越界异常,记录错误日志 Log.e(TAG, "get folder count failed:" + e.toString()); } finally { - // 无论是否出现异常,都关闭游标,释放资源 cursor.close(); } } } return count; } - // 检查指定ID和类型的笔记是否在笔记数据库中可见(不在回收站文件夹中),通过查询数据库判断是否存在符合条件的笔记 - + public static boolean visibleInNoteDatabase(ContentResolver resolver, long noteId, int type) { - // 执行数据库查询,从Notes.CONTENT_NOTE_URI对应的表中查询指定ID和类型且不在回收站文件夹中的笔记是否存在 Cursor cursor = resolver.query(ContentUris.withAppendedId(Notes.CONTENT_NOTE_URI, noteId), null, NoteColumns.TYPE + "=? AND " + NoteColumns.PARENT_ID + "<>" + Notes.ID_TRASH_FOLER, @@ -180,73 +145,60 @@ public class DataUtils { boolean exist = false; if (cursor != null) { - // 如果查询结果游标中的记录数量大于0,表示存在符合条件的笔记,将exist设为true if (cursor.getCount() > 0) { exist = true; } - // 关闭游标,释放资源 cursor.close(); } return exist; } -// 检查指定ID的笔记是否存在于笔记数据库中,通过简单查询判断是否有对应记录 + public static boolean existInNoteDatabase(ContentResolver resolver, long noteId) { - // 执行数据库查询,从Notes.CONTENT_NOTE_URI对应的表中查询指定ID的笔记是否存在,无其他额外查询条件 - Cursor cursor = resolver.query(ContentUris.withAppendedId(Notes.CONTENT_NOTE_URI, noteId), + Cursor cursor = resolver.query(ContentUris.withAppendedId(Notes.CONTENT_NOTE_URI, noteId), null, null, null, null); boolean exist = false; if (cursor != null) { - // 如果查询结果游标中的记录数量大于0,表示存在符合条件的笔记,将exist设为true if (cursor.getCount() > 0) { exist = true; } - // 关闭游标,释放资源 cursor.close(); } return exist; } - // 检查指定ID的数据是否存在于数据数据库中(此处指与笔记相关的数据,例如可能是附件等数据),通过查询判断是否有对应记录 - + public static boolean existInDataDatabase(ContentResolver resolver, long dataId) { - // 执行数据库查询,从Notes.CONTENT_DATA_URI对应的表中查询指定ID的数据是否存在,无其他额外查询条件 Cursor cursor = resolver.query(ContentUris.withAppendedId(Notes.CONTENT_DATA_URI, dataId), null, null, null, null); boolean exist = false; if (cursor != null) { - // 如果查询结果游标中的记录数量大于0,表示存在符合条件的数据,将exist设为true if (cursor.getCount() > 0) { exist = true; } - // 关闭游标,释放资源 cursor.close(); } return exist; } - // 检查指定名称的文件夹是否在可见文件夹中(非系统文件夹且不在回收站文件夹中),通过查询数据库判断是否存在符合条件的文件夹 + public static boolean checkVisibleFolderName(ContentResolver resolver, String name) { - // 执行数据库查询,从Notes.CONTENT_NOTE_URI对应的表中查询指定名称、类型为文件夹且不在回收站文件夹中的文件夹是否存在 - Cursor cursor = resolver.query(Notes.CONTENT_NOTE_URI, null, + Cursor cursor = resolver.query(Notes.CONTENT_NOTE_URI, null, NoteColumns.TYPE + "=" + Notes.TYPE_FOLDER + " AND " + NoteColumns.PARENT_ID + "<>" + Notes.ID_TRASH_FOLER + " AND " + NoteColumns.SNIPPET + "=?", new String[] { name }, null); boolean exist = false; if(cursor != null) { - // 如果查询结果游标中的记录数量大于0,表示存在符合条件的文件夹,将exist设为true if(cursor.getCount() > 0) { exist = true; } - // 关闭游标,释放资源 cursor.close(); } return exist; } - // 获取指定文件夹下所有笔记对应的小部件属性集合,通过查询数据库获取相关信息并构建属性集合 + public static HashSet getFolderNoteWidget(ContentResolver resolver, long folderId) { - // 执行数据库查询,从Notes.CONTENT_NOTE_URI对应的表中查询指定文件夹下笔记的小部件ID和小部件类型信息 - Cursor c = resolver.query(Notes.CONTENT_NOTE_URI, + Cursor c = resolver.query(Notes.CONTENT_NOTE_URI, new String[] { NoteColumns.WIDGET_ID, NoteColumns.WIDGET_TYPE }, NoteColumns.PARENT_ID + "=?", new String[] { String.valueOf(folderId) }, @@ -258,48 +210,40 @@ public class DataUtils { set = new HashSet(); do { try { - // 构建一个AppWidgetAttribute对象,用于存储小部件的属性信息 AppWidgetAttribute widget = new AppWidgetAttribute(); - // 从查询结果游标中获取小部件ID并赋值给widget对象的对应属性 widget.widgetId = c.getInt(0); - // 从查询结果游标中获取小部件类型并赋值给widget对象的对应属性 widget.widgetType = c.getInt(1); - // 将构建好的widget对象添加到属性集合中 set.add(widget); } catch (IndexOutOfBoundsException e) { - // 若获取数据时出现越界异常,记录错误日志 Log.e(TAG, e.toString()); } } while (c.moveToNext()); - } // 关闭游标,释放资源 + } c.close(); } return set; - } // 根据笔记ID获取对应的电话号码(从通话记录相关的笔记数据中获取),通过数据库查询获取电话号码信息 + } public static String getCallNumberByNoteId(ContentResolver resolver, long noteId) { - // 执行数据库查询,从Notes.CONTENT_DATA_URI对应的表中查询指定笔记ID且类型为通话记录相关的电话号码信息 - Cursor cursor = resolver.query(Notes.CONTENT_DATA_URI, + Cursor cursor = resolver.query(Notes.CONTENT_DATA_URI, new String [] { CallNote.PHONE_NUMBER }, CallNote.NOTE_ID + "=? AND " + CallNote.MIME_TYPE + "=?", new String [] { String.valueOf(noteId), CallNote.CONTENT_ITEM_TYPE }, null); if (cursor != null && cursor.moveToFirst()) { - try { // 尝试从查询结果游标中获取电话号码字符串并返回 + try { return cursor.getString(0); - } catch (IndexOutOfBoundsException e) { // 若获取数据时出现越界异常,记录错误日志 + } catch (IndexOutOfBoundsException e) { Log.e(TAG, "Get call number fails " + e.toString()); - } finally { // 关闭游标,释放资源 + } finally { cursor.close(); } } return ""; } - // 根据电话号码和通话日期获取对应的笔记ID(从通话记录相关数据中查找匹配的笔记),通过数据库查询获取笔记ID信息 public static long getNoteIdByPhoneNumberAndCallDate(ContentResolver resolver, String phoneNumber, long callDate) { - // 执行数据库查询,从Notes.CONTENT_DATA_URI对应的表中查询指定电话号码、通话日期且类型为通话记录相关的笔记ID信息 Cursor cursor = resolver.query(Notes.CONTENT_DATA_URI, new String [] { CallNote.NOTE_ID }, CallNote.CALL_DATE + "=? AND " + CallNote.MIME_TYPE + "=? AND PHONE_NUMBERS_EQUAL(" @@ -309,19 +253,18 @@ public class DataUtils { if (cursor != null) { if (cursor.moveToFirst()) { - try { // 尝试从查询结果游标中获取笔记ID并返回 + try { return cursor.getLong(0); - } catch (IndexOutOfBoundsException e) { // 若获取数据时出现越界异常,记录错误日志 + } catch (IndexOutOfBoundsException e) { Log.e(TAG, "Get call note id fails " + e.toString()); } - } // 关闭游标,释放资源 + } cursor.close(); } return 0; } - // 根据笔记ID获取对应的摘要信息(通常是笔记的简短描述等内容),通过数据库查询获取摘要字符串 + public static String getSnippetById(ContentResolver resolver, long noteId) { - // 执行数据库查询,从Notes.CONTENT_NOTE_URI对应的表中查询指定笔记ID的摘要信息 Cursor cursor = resolver.query(Notes.CONTENT_NOTE_URI, new String [] { NoteColumns.SNIPPET }, NoteColumns.ID + "=?", @@ -329,18 +272,16 @@ public class DataUtils { null); if (cursor != null) { - // 若查询到结果,从游标中获取摘要字符串 String snippet = ""; if (cursor.moveToFirst()) { snippet = cursor.getString(0); } - // 关闭游标,释放资源 cursor.close(); return snippet; - } // 如果查询失败(游标为null,表示没有找到对应笔记),抛出异常表示未找到指定ID的笔记 + } throw new IllegalArgumentException("Note is not found with id: " + noteId); } -// 对传入的摘要字符串进行格式化处理,去除两端空白字符,并截取到换行符之前的内容(如果有换行符) + public static String getFormattedSnippet(String snippet) { if (snippet != null) { snippet = snippet.trim(); diff --git a/src/Notes-master/src/net/micode/notes/tool/GTaskStringUtils.java b/src/Notes-master/src/net/micode/notes/tool/GTaskStringUtils.java index 23aa88a..666b729 100644 --- a/src/Notes-master/src/net/micode/notes/tool/GTaskStringUtils.java +++ b/src/Notes-master/src/net/micode/notes/tool/GTaskStringUtils.java @@ -15,154 +15,99 @@ */ package net.micode.notes.tool; -// GTaskStringUtils类用于定义一系列与GTask操作、数据结构以及相关文件夹、元数据等有关的字符串常量。 -// 这些常量在涉及到GTask相关的JSON数据处理、文件夹标识和元数据管理等功能中会被使用到。 + public class GTaskStringUtils { - // 以下是与GTask JSON数据中动作(action)相关的字段名及对应动作类型值的常量定义 - // JSON数据中表示动作(action)的唯一标识符的字段名,在整个操作流程中可用于区分不同的具体操作。 - public final static String GTASK_JSON_ACTION_ID = "action_id"; - // JSON数据中表示动作列表(action_list)的字段名,通常可能用来存放一组操作相关信息的集合, - // 比如包含多个具体操作的详细描述等内容。 + public final static String GTASK_JSON_ACTION_LIST = "action_list"; -// JSON数据中表示动作类型(action_type)的字段名,通过不同的值来区分具体是哪种操作行为, - // 例如创建、获取、移动、更新等操作。 + public final static String GTASK_JSON_ACTION_TYPE = "action_type"; -// JSON数据中表示创建(create)类型动作的具体值,当"action_type"字段取值为此常量时, - // 意味着对应的操作是创建相关的数据实体等操作。 + public final static String GTASK_JSON_ACTION_TYPE_CREATE = "create"; -// JSON数据中表示获取全部(get_all)类型动作的具体值,用于指示要获取所有相关数据的操作, - // 比如获取所有任务、所有列表等情况。 + public final static String GTASK_JSON_ACTION_TYPE_GETALL = "get_all"; - // JSON数据中表示移动(move)类型动作的具体值,表明对应的操作是将某个数据实体从一个位置移动到另一个位置, - // 例如移动任务到不同的列表等情况。 + public final static String GTASK_JSON_ACTION_TYPE_MOVE = "move"; - // JSON数据中表示更新(update)类型动作的具体值,代表要对已存在的数据实体进行更新修改的操作, - // 像更新任务的名称、属性等操作会用到此标识。 + public final static String GTASK_JSON_ACTION_TYPE_UPDATE = "update"; -// JSON数据中表示创建者(creator)的ID字段名,可用于确定执行创建操作的具体用户或者相关主体的唯一标识, - // 在涉及多用户或者多来源数据创建时,可借此追踪创建源头 + public final static String GTASK_JSON_CREATOR_ID = "creator_id"; - // JSON数据中表示子实体(child_entity)的字段名,可能用于描述某个主实体下包含的子级相关的数据结构, - // 例如一个任务列表下包含的多个子任务等情况可以通过此字段来关联和表示。 + public final static String GTASK_JSON_CHILD_ENTITY = "child_entity"; -// JSON数据中表示客户端版本(client_version)的字段名,主要用于记录使用该GTask相关功能的客户端应用的版本信息, - // 在数据同步、兼容性处理等场景中可根据此版本信息来判断是否支持某些操作或进行相应的适配。 + public final static String GTASK_JSON_CLIENT_VERSION = "client_version"; - // JSON数据中表示是否完成(completed)的字段名,常用于任务相关的数据中,用来标记某个任务是否已经完成, - // 例如任务完成状态的记录与判断会依赖于此字段。 + public final static String GTASK_JSON_COMPLETED = "completed"; -// JSON数据中表示当前列表(current_list_id)的ID字段名,可能用于标识当前操作所涉及的列表的唯一标识, - // 比如在任务移动操作中,可指明当前所在的列表以及要移动到的目标列表等情况。 + public final static String GTASK_JSON_CURRENT_LIST_ID = "current_list_id"; - // JSON数据中表示默认列表(default_list_id)的ID字段名,用于指定某个默认的列表, - // 例如新创建任务时默认归属的列表等情况可以通过此标识来确定。 + public final static String GTASK_JSON_DEFAULT_LIST_ID = "default_list_id"; - // JSON数据中表示是否已删除(deleted)的字段名,可用于标记某个数据实体是否已经被删除, - // 在数据清理、回收站相关功能或者同步时判断数据是否已不存在等场景中会用到。 + public final static String GTASK_JSON_DELETED = "deleted"; -// JSON数据中表示目标列表(dest_list)的字段名,常用于操作涉及到将数据移动或关联到其他列表的情况, - // 比如在移动任务操作中,此字段指明任务要移动到的目标列表的相关信息。 + public final static String GTASK_JSON_DEST_LIST = "dest_list"; - // JSON数据中表示目标父级(dest_parent)的字段名,可用于表示某个数据实体在移动、关联等操作后的目标父级对象相关信息, - // 例如任务移动后所属的新的父任务或者父列表等情况通过此字段来体现。 + public final static String GTASK_JSON_DEST_PARENT = "dest_parent"; - // JSON数据中表示目标父级类型(dest_parent_type)的字段名,配合"dest_parent"字段使用, - // 用于明确目标父级对象具体是什么类型,比如是任务组还是普通任务列表等类型区分。 + public final static String GTASK_JSON_DEST_PARENT_TYPE = "dest_parent_type"; -// JSON数据中表示实体变化量(entity_delta)的字段名,可能用于记录某个数据实体在更新、修改等操作前后的变化差异情况, - // 例如任务的某些属性值发生了改变,通过此字段可以详细记录具体的变化内容。 + public final static String GTASK_JSON_ENTITY_DELTA = "entity_delta"; - // JSON数据中表示实体类型(entity_type)的字段名,用于区分不同的数据实体类型, - // 像任务(TASK)、任务组(GROUP)等不同类型的数据实体可以通过此字段来标识区分。 + public final static String GTASK_JSON_ENTITY_TYPE = "entity_type"; - // JSON数据中表示获取已删除数据(get_deleted)的字段名,可能用于发起获取已被标记为删除的数据的相关操作, - // 比如在回收站功能或者数据同步时,查看哪些数据已被删除等情况会用到。 + public final static String GTASK_JSON_GET_DELETED = "get_deleted"; - // JSON数据中表示数据实体的唯一标识符(id)的字段名,用于唯一确定某个具体的数据实体, - // 无论是任务、列表还是其他相关数据结构,都可以通过此ID进行查找、关联等操作。 + public final static String GTASK_JSON_ID = "id"; - // JSON数据中表示索引(index)的字段名,可用于在有序的数据集合(如任务列表等)中确定某个数据实体的位置顺序, - // 例如在排序、定位特定位置的任务等场景中会使用到。 + public final static String GTASK_JSON_INDEX = "index"; - // JSON数据中表示最后修改时间(last_modified)的字段名,用于记录某个数据实体最后一次被修改的时间戳, - // 在数据同步、版本控制以及判断数据是否有更新等场景中,此时间信息非常关键。 + public final static String GTASK_JSON_LAST_MODIFIED = "last_modified"; - // JSON数据中表示最新同步点(latest_sync_point)的字段名,用于标记在数据同步过程中的最新有效同步位置或者时间点, - // 可帮助确定下次同步从何处开始、哪些数据需要重新同步等情况。 + public final static String GTASK_JSON_LATEST_SYNC_POINT = "latest_sync_point"; -// JSON数据中表示列表(list_id)的ID字段名,用于唯一标识不同的列表, - // 在涉及到多个列表管理、任务在不同列表间移动等操作时,通过此ID来区分不同的列表。 + public final static String GTASK_JSON_LIST_ID = "list_id"; - // JSON数据中表示列表(lists)的字段名,通常可能用来存放多个列表相关信息的集合, - // 例如获取所有列表信息或者返回一组列表数据时,可通过此字段进行表示。 + public final static String GTASK_JSON_LISTS = "lists"; - // JSON数据中表示名称(name)的字段名,常用于表示数据实体(如任务、列表等)的名称属性, - // 方便展示、查找以及用户识别不同的数据实体。 + public final static String GTASK_JSON_NAME = "name"; - // JSON数据中表示新的标识符(new_id)的字段名,可能在某些操作(如数据复制、克隆或者重新生成ID等情况)下, - // 用于标记新产生的数据实体的唯一标识。 public final static String GTASK_JSON_NEW_ID = "new_id"; - // JSON数据中表示笔记(notes)的字段名,可能用于存放与任务、列表等相关的笔记信息, - // 比如用户针对某个任务添加的备注、说明等内容可通过此字段关联存储。 + public final static String GTASK_JSON_NOTES = "notes"; - // JSON数据中表示父级ID(parent_id)的字段名,用于确定某个数据实体所属的父级对象的唯一标识, - // 例如任务所属的任务组或者列表的ID等情况通过此字段关联表示。 + public final static String GTASK_JSON_PARENT_ID = "parent_id"; - // JSON数据中表示前一个兄弟节点ID(prior_sibling_id)的字段名,在有序的数据结构(如任务列表按顺序排列)中, - // 可用于定位某个数据实体前一个相邻的兄弟节点的标识,方便进行顺序调整、插入等操作。 + public final static String GTASK_JSON_PRIOR_SIBLING_ID = "prior_sibling_id"; - // JSON数据中表示操作结果(results)的字段名,常用于存放执行某个操作(如创建、更新、移动等操作)后的返回结果信息, - // 比如操作是否成功、返回的数据等情况通过此字段进行传递和展示。 + public final static String GTASK_JSON_RESULTS = "results"; - // JSON数据中表示源列表(source_list)的字段名,在涉及数据移动、复制等操作时, - // 用于指明操作数据的原始来源列表的相关信息,与目标列表(dest_list)相对应。 + public final static String GTASK_JSON_SOURCE_LIST = "source_list"; - // JSON数据中表示任务(tasks)的字段名,通常用于存放一组任务相关信息的集合, - // 比如获取所有任务列表、返回一批任务数据等情况会通过此字段来表示。 + public final static String GTASK_JSON_TASKS = "tasks"; - // JSON数据中表示类型(type)的字段名,和前面提到的"entity_type"类似,用于区分不同的数据实体类型, - // 不过可能使用场景更通用一些,可用于各种需要区分类型的情况。 public final static String GTASK_JSON_TYPE = "type"; - // JSON数据中表示任务组(GROUP)类型的具体值,用于明确某个数据实体是任务组类型, - // 在通过"type"或者"entity_type"字段判断类型时,此常量可作为对应的值来标识任务组。 + public final static String GTASK_JSON_TYPE_GROUP = "GROUP"; -// JSON数据中表示任务(TASK)类型的具体值,用于确定某个数据实体是普通任务类型, - // 同样在类型判断场景中,以此常量来标识任务类型的数据实体。 + public final static String GTASK_JSON_TYPE_TASK = "TASK"; - // JSON数据中表示用户(user)的字段名,可能用于存放与操作相关的用户信息, - // 比如操作执行者的用户名、用户ID等相关属性,便于记录操作的归属主体等情况。 + public final static String GTASK_JSON_USER = "user"; - // 以下是与特定文件夹相关的字符串常量定义,可能用于标识不同功能或用途的文件夹 - // 表示与MIUI笔记相关的文件夹前缀字符串,用于在文件夹命名或者标识中区分出属于MIUI笔记相关的文件夹, - // 方便进行针对性的处理或者识别。 public final static String MIUI_FOLDER_PREFFIX = "[MIUI_Notes]"; - // 表示默认文件夹的名称常量,可能用于指代某个默认创建或者默认使用的文件夹, - // 例如新笔记、新任务等默认存放的文件夹可以用此名称来表示。 + public final static String FOLDER_DEFAULT = "Default"; - // 表示通话记录笔记相关的文件夹名称常量,用于专门存放与通话记录笔记相关内容的文件夹标识, - // 便于对通话记录相关笔记进行分类管理和查找等操作。 + public final static String FOLDER_CALL_NOTE = "Call_Note"; -// 表示元数据(METADATA)相关的文件夹名称常量,可能用于存放各种数据的元数据信息的文件夹, - // 在数据管理、备份恢复以及数据关联等场景中,此文件夹的元数据起着重要作用。 + public final static String FOLDER_META = "METADATA"; - // 以下是与元数据头部相关的字段名常量定义,用于明确元数据中不同部分的标识 - // 表示元数据中GTask的ID(meta_gid)的字段名,在元数据结构里用于存放与之关联的GTask的唯一标识信息, - // 可用于数据关联、同步以及根据GTask查找对应元数据等操作。 public final static String META_HEAD_GTASK_ID = "meta_gid"; -// 表示元数据中笔记(meta_note)相关信息的字段名,可能用于存放具体的笔记内容或者笔记相关属性等元数据信息, - // 在处理笔记的元数据时,通过此字段来获取对应的笔记相关数据。 + public final static String META_HEAD_NOTE = "meta_note"; -// 表示元数据中其他数据(meta_data)相关信息的字段名,可用于存放除了前面特定标识之外的其他各种数据的元数据, - // 例如一些自定义的扩展数据、辅助数据等的元数据都可以通过此字段来表示和管理。 + public final static String META_HEAD_DATA = "meta_data"; -// 表示元数据中其他数据(meta_data)相关信息的字段名,可用于存放除了前面特定标识之外的其他各种数据的元数据, - // 例如一些自定义的扩展数据、辅助数据等的元数据都可以通过此字段来表示和管理。 + public final static String META_NOTE_NAME = "[META INFO] DON'T UPDATE AND DELETE"; } From c68c9767d1a516f64ceeed51df2516718ccd105c Mon Sep 17 00:00:00 2001 From: zcs <2755796131@qq.com> Date: Mon, 30 Dec 2024 12:19:08 +0800 Subject: [PATCH 3/4] =?UTF-8?q?=E5=BC=A0=E6=98=8C=E7=9B=9B=20202201002062?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../net/micode/notes/tool/BackupUtils.java | 75 +++++++--- .../src/net/micode/notes/tool/DataUtils.java | 113 ++++++++++---- .../micode/notes/tool/GTaskStringUtils.java | 139 ++++++++++++------ 3 files changed, 236 insertions(+), 91 deletions(-) diff --git a/src/Notes-master/src/net/micode/notes/tool/BackupUtils.java b/src/Notes-master/src/net/micode/notes/tool/BackupUtils.java index 39f6ec4..a95273a 100644 --- a/src/Notes-master/src/net/micode/notes/tool/BackupUtils.java +++ b/src/Notes-master/src/net/micode/notes/tool/BackupUtils.java @@ -35,57 +35,66 @@ import java.io.FileOutputStream; import java.io.IOException; import java.io.PrintStream; - +// BackupUtils类用于处理笔记数据的备份相关功能,支持将笔记数据导出为文本格式等操作 public class BackupUtils { private static final String TAG = "BackupUtils"; + // 单例模式相关,用于保存唯一的BackupUtils实例 // Singleton stuff private static BackupUtils sInstance; - +// 获取BackupUtils的单例实例,若不存在则创建新的实例 public static synchronized BackupUtils getInstance(Context context) { if (sInstance == null) { sInstance = new BackupUtils(context); } return sInstance; } - + /** + * 以下是用于表示备份或恢复状态的常量定义 + */ + // 当前外部存储(SD卡)未挂载的状态码 /** * Following states are signs to represents backup or restore * status */ // Currently, the sdcard is not mounted public static final int STATE_SD_CARD_UNMOUONTED = 0; + // 备份文件不存在的状态码 // The backup file not exist public static final int STATE_BACKUP_FILE_NOT_EXIST = 1; // The data is not well formated, may be changed by other programs + // 数据格式不正确,可能被其他程序修改的状态码 public static final int STATE_DATA_DESTROIED = 2; // Some run-time exception which causes restore or backup fails + // 运行时出现异常导致备份或恢复失败的状态码 public static final int STATE_SYSTEM_ERROR = 3; // Backup or restore success + // 备份或恢复成功的状态码 public static final int STATE_SUCCESS = 4; private TextExport mTextExport; - + // 私有构造函数,初始化时创建TextExport对象,用于文本导出相关操作 private BackupUtils(Context context) { mTextExport = new TextExport(context); } - +// 判断外部存储(通常指SD卡)是否可用,通过比较外部存储状态与MEDIA_MOUNTED常量来判断 private static boolean externalStorageAvailable() { return Environment.MEDIA_MOUNTED.equals(Environment.getExternalStorageState()); } - + // 调用TextExport的exportToText方法进行文本导出,并返回导出结果状态码 public int exportToText() { return mTextExport.exportToText(); } - + // 获取导出的文本文件名,实际是通过TextExport对象获取其内部保存的文件名 public String getExportedTextFileName() { return mTextExport.mFileName; } - + // 获取导出的文本文件所在目录,通过TextExport对象获取其内部保存的文件目录 public String getExportedTextFileDir() { return mTextExport.mFileDirectory; } - + // TextExport内部类,主要负责具体的文本导出逻辑实现 private static class TextExport { + // 查询笔记相关信息的投影数组,定义了要从数据库中获取的笔记字段 private static final String[] NOTE_PROJECTION = { NoteColumns.ID, NoteColumns.MODIFIED_DATE, @@ -98,7 +107,7 @@ public class BackupUtils { private static final int NOTE_COLUMN_MODIFIED_DATE = 1; private static final int NOTE_COLUMN_SNIPPET = 2; - + // 查询笔记数据相关信息的投影数组,定义了要从数据库中获取的笔记数据字段 private static final String[] DATA_PROJECTION = { DataColumns.CONTENT, DataColumns.MIME_TYPE, @@ -116,6 +125,7 @@ public class BackupUtils { private static final int DATA_COLUMN_PHONE_NUMBER = 4; + // 用于格式化导出文本内容的字符串数组,通过资源获取,不同索引对应不同的格式化用途 private final String [] TEXT_FORMAT; private static final int FORMAT_FOLDER_NAME = 0; private static final int FORMAT_NOTE_DATE = 1; @@ -124,22 +134,25 @@ public class BackupUtils { private Context mContext; private String mFileName; private String mFileDirectory; - + // 构造函数,初始化TEXT_FORMAT数组以及保存传入的上下文对象等信息 public TextExport(Context context) { TEXT_FORMAT = context.getResources().getStringArray(R.array.format_for_exported_note); mContext = context; mFileName = ""; mFileDirectory = ""; } - + // 根据传入的索引获取对应的格式化字符串 private String getFormat(int id) { return TEXT_FORMAT[id]; } - + /** + * 将指定文件夹(通过folderId标识)中的笔记导出为文本格式,并写入到给定的PrintStream中 + */ /** * Export the folder identified by folder id to text */ private void exportFolderToText(String folderId, PrintStream ps) { + // 通过内容解析器查询属于该文件夹的笔记信息 // Query notes belong to this folder Cursor notesCursor = mContext.getContentResolver().query(Notes.CONTENT_NOTE_URI, NOTE_PROJECTION, NoteColumns.PARENT_ID + "=?", new String[] { @@ -148,24 +161,29 @@ public class BackupUtils { if (notesCursor != null) { if (notesCursor.moveToFirst()) { - do { + do { // 格式化并打印笔记的最后修改日期 // Print note's last modified date ps.println(String.format(getFormat(FORMAT_NOTE_DATE), DateFormat.format( mContext.getString(R.string.format_datetime_mdhm), notesCursor.getLong(NOTE_COLUMN_MODIFIED_DATE)))); + // 获取当前笔记的ID,用于进一步查询该笔记相关的数据信息并导出 // Query data belong to this note String noteId = notesCursor.getString(NOTE_COLUMN_ID); exportNoteToText(noteId, ps); } while (notesCursor.moveToNext()); } + // 关闭游标,释放资源 notesCursor.close(); } } - + /** + * 将指定ID的笔记导出为文本格式,并写入到给定的PrintStream中 + */ /** * Export note identified by id to a print stream */ private void exportNoteToText(String noteId, PrintStream ps) { + // 通过内容解析器查询属于该笔记的相关数据信息 Cursor dataCursor = mContext.getContentResolver().query(Notes.CONTENT_DATA_URI, DATA_PROJECTION, DataColumns.NOTE_ID + "=?", new String[] { noteId @@ -174,6 +192,7 @@ public class BackupUtils { if (dataCursor != null) { if (dataCursor.moveToFirst()) { do { + // 获取电话号码并打印(如果非空) String mimeType = dataCursor.getString(DATA_COLUMN_MIME_TYPE); if (DataConstants.CALL_NOTE.equals(mimeType)) { // Print phone number @@ -185,10 +204,12 @@ public class BackupUtils { ps.println(String.format(getFormat(FORMAT_NOTE_CONTENT), phoneNumber)); } + // 格式化并打印通话日期 // Print call date ps.println(String.format(getFormat(FORMAT_NOTE_CONTENT), DateFormat .format(mContext.getString(R.string.format_datetime_mdhm), callDate))); + // 打印通话附件位置(如果非空) // Print call attachment location if (!TextUtils.isEmpty(location)) { ps.println(String.format(getFormat(FORMAT_NOTE_CONTENT), @@ -203,8 +224,10 @@ public class BackupUtils { } } while (dataCursor.moveToNext()); } + // 关闭游标,释放资源 dataCursor.close(); } + // 在每个笔记内容之间写入换行符,用于分隔不同笔记内容 // print a line separator between note try { ps.write(new byte[] { @@ -214,11 +237,14 @@ public class BackupUtils { Log.e(TAG, e.toString()); } } - + /** + * 执行将笔记导出为用户可读的文本格式的主要逻辑,整合了文件夹和笔记的导出操作 + */ /** * Note will be exported as text which is user readable */ public int exportToText() { + // 首先判断外部存储是否可用,如果不可用则返回对应状态码并记录日志 if (!externalStorageAvailable()) { Log.d(TAG, "Media was not mounted"); return STATE_SD_CARD_UNMOUONTED; @@ -229,6 +255,7 @@ public class BackupUtils { Log.e(TAG, "get print stream error"); return STATE_SYSTEM_ERROR; } + // 第一步:查询并导出文件夹(特定条件筛选的文件夹,如通话记录文件夹等)及其内部笔记信息 // First export folder and its notes Cursor folderCursor = mContext.getContentResolver().query( Notes.CONTENT_NOTE_URI, @@ -253,10 +280,10 @@ public class BackupUtils { String folderId = folderCursor.getString(NOTE_COLUMN_ID); exportFolderToText(folderId, ps); } while (folderCursor.moveToNext()); - } + } // 关闭游标,释放资源 folderCursor.close(); } - + // 第二步:查询并导出根文件夹下的笔记信息 // Export notes in root's folder Cursor noteCursor = mContext.getContentResolver().query( Notes.CONTENT_NOTE_URI, @@ -274,14 +301,16 @@ public class BackupUtils { String noteId = noteCursor.getString(NOTE_COLUMN_ID); exportNoteToText(noteId, ps); } while (noteCursor.moveToNext()); - } + } // 关闭游标,释放资源 noteCursor.close(); } ps.close(); - + // 关闭PrintStream,完成文件写入操作 return STATE_SUCCESS; } - + /** + * 获取一个指向要导出的文本文件的PrintStream对象,用于后续写入导出内容 + */ /** * Get a print stream pointed to the file {@generateExportedTextFile} */ @@ -308,7 +337,9 @@ public class BackupUtils { return ps; } } - + /** + * 在外部存储(SD卡)上生成用于存储导入数据的文本文件,如果生成过程出现异常则返回null + */ /** * Generate the text file to store imported data */ diff --git a/src/Notes-master/src/net/micode/notes/tool/DataUtils.java b/src/Notes-master/src/net/micode/notes/tool/DataUtils.java index 2a14982..85123d3 100644 --- a/src/Notes-master/src/net/micode/notes/tool/DataUtils.java +++ b/src/Notes-master/src/net/micode/notes/tool/DataUtils.java @@ -33,88 +33,118 @@ import net.micode.notes.ui.NotesListAdapter.AppWidgetAttribute; import java.util.ArrayList; import java.util.HashSet; +// DataUtils类提供了一系列与笔记数据操作相关的实用方法,例如批量删除笔记、移动笔记到文件夹、查询各种数据状态等 public class DataUtils { public static final String TAG = "DataUtils"; + // 批量删除给定ID集合对应的笔记,跳过系统根文件夹(Notes.ID_ROOT_FOLDER)的删除操作 public static boolean batchDeleteNotes(ContentResolver resolver, HashSet ids) { + // 如果传入的ID集合为null,记录日志并返回true,表示无需执行删除操作 if (ids == null) { Log.d(TAG, "the ids is null"); return true; - } + }// 如果ID集合为空,记录日志并返回true,表示没有要删除的笔记 if (ids.size() == 0) { Log.d(TAG, "no id is in the hashset"); return true; } - + // 用于存储一系列内容提供器操作的列表,后续将批量执行这些操作来删除笔记 ArrayList operationList = new ArrayList(); for (long id : ids) { + // 不允许删除系统根文件夹,若当前ID是系统根文件夹ID,则记录错误日志并跳过该ID的删除操作 if(id == Notes.ID_ROOT_FOLDER) { Log.e(TAG, "Don't delete system folder root"); continue; - } + } // 创建一个用于删除指定笔记的内容提供器操作构建器,指定要删除的笔记的URI(通过ID构建) ContentProviderOperation.Builder builder = ContentProviderOperation .newDelete(ContentUris.withAppendedId(Notes.CONTENT_NOTE_URI, id)); + // 将构建好的操作添加到操作列表中 operationList.add(builder.build()); } try { + // 批量应用内容提供器操作列表,执行删除笔记的操作,并获取操作结果数组 ContentProviderResult[] results = resolver.applyBatch(Notes.AUTHORITY, operationList); + // 如果结果数组为null,或者长度为0,或者第一个结果为null,表示删除笔记失败,记录日志并返回false + if (results == null || results.length == 0 || results[0] == null) { Log.d(TAG, "delete notes failed, ids:" + ids.toString()); return false; } + // 如果成功执行删除操作,返回true return true; } catch (RemoteException e) { + // 捕获远程异常,记录详细的错误日志(包含异常的toString和getMessage信息) Log.e(TAG, String.format("%s: %s", e.toString(), e.getMessage())); } catch (OperationApplicationException e) { + // 捕获操作应用异常,记录详细的错误日志(包含异常的toString和getMessage信息) Log.e(TAG, String.format("%s: %s", e.toString(), e.getMessage())); } return false; } - + // 将指定ID的笔记从源文件夹移动到目标文件夹,通过更新笔记的相关字段来实现移动操作 public static void moveNoteToFoler(ContentResolver resolver, long id, long srcFolderId, long desFolderId) { + // 创建一个ContentValues对象,用于存储要更新的字段和对应的值 ContentValues values = new ContentValues(); + // 设置笔记的新父文件夹ID(目标文件夹ID) values.put(NoteColumns.PARENT_ID, desFolderId); + // 设置笔记的原始父文件夹ID(源文件夹ID) values.put(NoteColumns.ORIGIN_PARENT_ID, srcFolderId); + // 设置本地修改标志为1,表示该笔记有本地修改操作 values.put(NoteColumns.LOCAL_MODIFIED, 1); + // 通过内容解析器更新指定笔记(根据ID确定)的相关字段信息 resolver.update(ContentUris.withAppendedId(Notes.CONTENT_NOTE_URI, id), values, null, null); } - + // 批量将给定ID集合中的笔记移动到指定的文件夹,通过构建一系列更新操作并批量执行来实现 public static boolean batchMoveToFolder(ContentResolver resolver, HashSet ids, long folderId) { + // 如果传入的ID集合为null,记录日志并返回true,表示无需执行移动操作 if (ids == null) { Log.d(TAG, "the ids is null"); return true; } - + // 用于存储一系列内容提供器操作的列表,后续将批量执行这些操作来移动笔记 ArrayList operationList = new ArrayList(); for (long id : ids) { + // 创建一个用于更新指定笔记的内容提供器操作构建器,指定要更新的笔记的URI(通过ID构建) ContentProviderOperation.Builder builder = ContentProviderOperation .newUpdate(ContentUris.withAppendedId(Notes.CONTENT_NOTE_URI, id)); + // 设置笔记的新父文件夹ID(目标文件夹ID) builder.withValue(NoteColumns.PARENT_ID, folderId); + // 设置本地修改标志为1,表示该笔记有本地修改操作 builder.withValue(NoteColumns.LOCAL_MODIFIED, 1); + // 将构建好的操作添加到操作列表中 operationList.add(builder.build()); } try { + // 批量应用内容提供器操作列表,执行移动笔记的操作,并获取操作结果数组 ContentProviderResult[] results = resolver.applyBatch(Notes.AUTHORITY, operationList); + // 如果结果数组为null,或者长度为0,或者第一个结果为null,表示移动笔记失败,记录日志并返回false if (results == null || results.length == 0 || results[0] == null) { Log.d(TAG, "delete notes failed, ids:" + ids.toString()); return false; } + // 如果成功执行移动操作,返回true return true; } catch (RemoteException e) { + // 捕获远程异常,记录详细的错误日志(包含异常的toString和getMessage信息) Log.e(TAG, String.format("%s: %s", e.toString(), e.getMessage())); } catch (OperationApplicationException e) { + // 捕获操作应用异常,记录详细的错误日志(包含异常的toString和getMessage信息) Log.e(TAG, String.format("%s: %s", e.toString(), e.getMessage())); } return false; } - + /** + * 获取除系统文件夹({@link Notes#TYPE_SYSTEM}类型的文件夹)之外的所有文件夹数量,通过数据库查询统计符合条件的文件夹数量 + */ /** * Get the all folder count except system folders {@link Notes#TYPE_SYSTEM}} */ public static int getUserFolderCount(ContentResolver resolver) { + // 执行数据库查询,从Notes.CONTENT_NOTE_URI对应的表中查询符合条件的文件夹数量, + // 查询条件是文件夹类型为普通文件夹(Notes.TYPE_FOLDER)且父文件夹ID不是回收站文件夹ID(Notes.ID_TRASH_FOLER) Cursor cursor =resolver.query(Notes.CONTENT_NOTE_URI, new String[] { "COUNT(*)" }, NoteColumns.TYPE + "=? AND " + NoteColumns.PARENT_ID + "<>?", @@ -125,18 +155,23 @@ public class DataUtils { if(cursor != null) { if(cursor.moveToFirst()) { try { + // 尝试从查询结果游标中获取第一列(即统计的数量值)并赋值给count变量 count = cursor.getInt(0); } catch (IndexOutOfBoundsException e) { + // 若获取数据时出现越界异常,记录错误日志 Log.e(TAG, "get folder count failed:" + e.toString()); } finally { + // 无论是否出现异常,都关闭游标,释放资源 cursor.close(); } } } return count; } - + // 检查指定ID和类型的笔记是否在笔记数据库中可见(不在回收站文件夹中),通过查询数据库判断是否存在符合条件的笔记 + public static boolean visibleInNoteDatabase(ContentResolver resolver, long noteId, int type) { + // 执行数据库查询,从Notes.CONTENT_NOTE_URI对应的表中查询指定ID和类型且不在回收站文件夹中的笔记是否存在 Cursor cursor = resolver.query(ContentUris.withAppendedId(Notes.CONTENT_NOTE_URI, noteId), null, NoteColumns.TYPE + "=? AND " + NoteColumns.PARENT_ID + "<>" + Notes.ID_TRASH_FOLER, @@ -145,60 +180,73 @@ public class DataUtils { boolean exist = false; if (cursor != null) { + // 如果查询结果游标中的记录数量大于0,表示存在符合条件的笔记,将exist设为true if (cursor.getCount() > 0) { exist = true; } + // 关闭游标,释放资源 cursor.close(); } return exist; } - +// 检查指定ID的笔记是否存在于笔记数据库中,通过简单查询判断是否有对应记录 public static boolean existInNoteDatabase(ContentResolver resolver, long noteId) { - Cursor cursor = resolver.query(ContentUris.withAppendedId(Notes.CONTENT_NOTE_URI, noteId), + // 执行数据库查询,从Notes.CONTENT_NOTE_URI对应的表中查询指定ID的笔记是否存在,无其他额外查询条件 + Cursor cursor = resolver.query(ContentUris.withAppendedId(Notes.CONTENT_NOTE_URI, noteId), null, null, null, null); boolean exist = false; if (cursor != null) { + // 如果查询结果游标中的记录数量大于0,表示存在符合条件的笔记,将exist设为true if (cursor.getCount() > 0) { exist = true; } + // 关闭游标,释放资源 cursor.close(); } return exist; } - + // 检查指定ID的数据是否存在于数据数据库中(此处指与笔记相关的数据,例如可能是附件等数据),通过查询判断是否有对应记录 + public static boolean existInDataDatabase(ContentResolver resolver, long dataId) { + // 执行数据库查询,从Notes.CONTENT_DATA_URI对应的表中查询指定ID的数据是否存在,无其他额外查询条件 Cursor cursor = resolver.query(ContentUris.withAppendedId(Notes.CONTENT_DATA_URI, dataId), null, null, null, null); boolean exist = false; if (cursor != null) { + // 如果查询结果游标中的记录数量大于0,表示存在符合条件的数据,将exist设为true if (cursor.getCount() > 0) { exist = true; } + // 关闭游标,释放资源 cursor.close(); } return exist; } - + // 检查指定名称的文件夹是否在可见文件夹中(非系统文件夹且不在回收站文件夹中),通过查询数据库判断是否存在符合条件的文件夹 public static boolean checkVisibleFolderName(ContentResolver resolver, String name) { - Cursor cursor = resolver.query(Notes.CONTENT_NOTE_URI, null, + // 执行数据库查询,从Notes.CONTENT_NOTE_URI对应的表中查询指定名称、类型为文件夹且不在回收站文件夹中的文件夹是否存在 + Cursor cursor = resolver.query(Notes.CONTENT_NOTE_URI, null, NoteColumns.TYPE + "=" + Notes.TYPE_FOLDER + " AND " + NoteColumns.PARENT_ID + "<>" + Notes.ID_TRASH_FOLER + " AND " + NoteColumns.SNIPPET + "=?", new String[] { name }, null); boolean exist = false; if(cursor != null) { + // 如果查询结果游标中的记录数量大于0,表示存在符合条件的文件夹,将exist设为true if(cursor.getCount() > 0) { exist = true; } + // 关闭游标,释放资源 cursor.close(); } return exist; } - + // 获取指定文件夹下所有笔记对应的小部件属性集合,通过查询数据库获取相关信息并构建属性集合 public static HashSet getFolderNoteWidget(ContentResolver resolver, long folderId) { - Cursor c = resolver.query(Notes.CONTENT_NOTE_URI, + // 执行数据库查询,从Notes.CONTENT_NOTE_URI对应的表中查询指定文件夹下笔记的小部件ID和小部件类型信息 + Cursor c = resolver.query(Notes.CONTENT_NOTE_URI, new String[] { NoteColumns.WIDGET_ID, NoteColumns.WIDGET_TYPE }, NoteColumns.PARENT_ID + "=?", new String[] { String.valueOf(folderId) }, @@ -210,40 +258,48 @@ public class DataUtils { set = new HashSet(); do { try { + // 构建一个AppWidgetAttribute对象,用于存储小部件的属性信息 AppWidgetAttribute widget = new AppWidgetAttribute(); + // 从查询结果游标中获取小部件ID并赋值给widget对象的对应属性 widget.widgetId = c.getInt(0); + // 从查询结果游标中获取小部件类型并赋值给widget对象的对应属性 widget.widgetType = c.getInt(1); + // 将构建好的widget对象添加到属性集合中 set.add(widget); } catch (IndexOutOfBoundsException e) { + // 若获取数据时出现越界异常,记录错误日志 Log.e(TAG, e.toString()); } } while (c.moveToNext()); - } + } // 关闭游标,释放资源 c.close(); } return set; - } + } // 根据笔记ID获取对应的电话号码(从通话记录相关的笔记数据中获取),通过数据库查询获取电话号码信息 public static String getCallNumberByNoteId(ContentResolver resolver, long noteId) { - Cursor cursor = resolver.query(Notes.CONTENT_DATA_URI, + // 执行数据库查询,从Notes.CONTENT_DATA_URI对应的表中查询指定笔记ID且类型为通话记录相关的电话号码信息 + Cursor cursor = resolver.query(Notes.CONTENT_DATA_URI, new String [] { CallNote.PHONE_NUMBER }, CallNote.NOTE_ID + "=? AND " + CallNote.MIME_TYPE + "=?", new String [] { String.valueOf(noteId), CallNote.CONTENT_ITEM_TYPE }, null); if (cursor != null && cursor.moveToFirst()) { - try { + try { // 尝试从查询结果游标中获取电话号码字符串并返回 return cursor.getString(0); - } catch (IndexOutOfBoundsException e) { + } catch (IndexOutOfBoundsException e) { // 若获取数据时出现越界异常,记录错误日志 Log.e(TAG, "Get call number fails " + e.toString()); - } finally { + } finally { // 关闭游标,释放资源 cursor.close(); } } return ""; } + // 根据电话号码和通话日期获取对应的笔记ID(从通话记录相关数据中查找匹配的笔记),通过数据库查询获取笔记ID信息 public static long getNoteIdByPhoneNumberAndCallDate(ContentResolver resolver, String phoneNumber, long callDate) { + // 执行数据库查询,从Notes.CONTENT_DATA_URI对应的表中查询指定电话号码、通话日期且类型为通话记录相关的笔记ID信息 Cursor cursor = resolver.query(Notes.CONTENT_DATA_URI, new String [] { CallNote.NOTE_ID }, CallNote.CALL_DATE + "=? AND " + CallNote.MIME_TYPE + "=? AND PHONE_NUMBERS_EQUAL(" @@ -253,18 +309,19 @@ public class DataUtils { if (cursor != null) { if (cursor.moveToFirst()) { - try { + try { // 尝试从查询结果游标中获取笔记ID并返回 return cursor.getLong(0); - } catch (IndexOutOfBoundsException e) { + } catch (IndexOutOfBoundsException e) { // 若获取数据时出现越界异常,记录错误日志 Log.e(TAG, "Get call note id fails " + e.toString()); } - } + } // 关闭游标,释放资源 cursor.close(); } return 0; } - + // 根据笔记ID获取对应的摘要信息(通常是笔记的简短描述等内容),通过数据库查询获取摘要字符串 public static String getSnippetById(ContentResolver resolver, long noteId) { + // 执行数据库查询,从Notes.CONTENT_NOTE_URI对应的表中查询指定笔记ID的摘要信息 Cursor cursor = resolver.query(Notes.CONTENT_NOTE_URI, new String [] { NoteColumns.SNIPPET }, NoteColumns.ID + "=?", @@ -272,16 +329,18 @@ public class DataUtils { null); if (cursor != null) { + // 若查询到结果,从游标中获取摘要字符串 String snippet = ""; if (cursor.moveToFirst()) { snippet = cursor.getString(0); } + // 关闭游标,释放资源 cursor.close(); return snippet; - } + } // 如果查询失败(游标为null,表示没有找到对应笔记),抛出异常表示未找到指定ID的笔记 throw new IllegalArgumentException("Note is not found with id: " + noteId); } - +// 对传入的摘要字符串进行格式化处理,去除两端空白字符,并截取到换行符之前的内容(如果有换行符) public static String getFormattedSnippet(String snippet) { if (snippet != null) { snippet = snippet.trim(); diff --git a/src/Notes-master/src/net/micode/notes/tool/GTaskStringUtils.java b/src/Notes-master/src/net/micode/notes/tool/GTaskStringUtils.java index 666b729..23aa88a 100644 --- a/src/Notes-master/src/net/micode/notes/tool/GTaskStringUtils.java +++ b/src/Notes-master/src/net/micode/notes/tool/GTaskStringUtils.java @@ -15,99 +15,154 @@ */ package net.micode.notes.tool; - +// GTaskStringUtils类用于定义一系列与GTask操作、数据结构以及相关文件夹、元数据等有关的字符串常量。 +// 这些常量在涉及到GTask相关的JSON数据处理、文件夹标识和元数据管理等功能中会被使用到。 public class GTaskStringUtils { + // 以下是与GTask JSON数据中动作(action)相关的字段名及对应动作类型值的常量定义 + // JSON数据中表示动作(action)的唯一标识符的字段名,在整个操作流程中可用于区分不同的具体操作。 + public final static String GTASK_JSON_ACTION_ID = "action_id"; - + // JSON数据中表示动作列表(action_list)的字段名,通常可能用来存放一组操作相关信息的集合, + // 比如包含多个具体操作的详细描述等内容。 public final static String GTASK_JSON_ACTION_LIST = "action_list"; - +// JSON数据中表示动作类型(action_type)的字段名,通过不同的值来区分具体是哪种操作行为, + // 例如创建、获取、移动、更新等操作。 public final static String GTASK_JSON_ACTION_TYPE = "action_type"; - +// JSON数据中表示创建(create)类型动作的具体值,当"action_type"字段取值为此常量时, + // 意味着对应的操作是创建相关的数据实体等操作。 public final static String GTASK_JSON_ACTION_TYPE_CREATE = "create"; - +// JSON数据中表示获取全部(get_all)类型动作的具体值,用于指示要获取所有相关数据的操作, + // 比如获取所有任务、所有列表等情况。 public final static String GTASK_JSON_ACTION_TYPE_GETALL = "get_all"; - + // JSON数据中表示移动(move)类型动作的具体值,表明对应的操作是将某个数据实体从一个位置移动到另一个位置, + // 例如移动任务到不同的列表等情况。 public final static String GTASK_JSON_ACTION_TYPE_MOVE = "move"; - + // JSON数据中表示更新(update)类型动作的具体值,代表要对已存在的数据实体进行更新修改的操作, + // 像更新任务的名称、属性等操作会用到此标识。 public final static String GTASK_JSON_ACTION_TYPE_UPDATE = "update"; - +// JSON数据中表示创建者(creator)的ID字段名,可用于确定执行创建操作的具体用户或者相关主体的唯一标识, + // 在涉及多用户或者多来源数据创建时,可借此追踪创建源头 public final static String GTASK_JSON_CREATOR_ID = "creator_id"; - + // JSON数据中表示子实体(child_entity)的字段名,可能用于描述某个主实体下包含的子级相关的数据结构, + // 例如一个任务列表下包含的多个子任务等情况可以通过此字段来关联和表示。 public final static String GTASK_JSON_CHILD_ENTITY = "child_entity"; - +// JSON数据中表示客户端版本(client_version)的字段名,主要用于记录使用该GTask相关功能的客户端应用的版本信息, + // 在数据同步、兼容性处理等场景中可根据此版本信息来判断是否支持某些操作或进行相应的适配。 public final static String GTASK_JSON_CLIENT_VERSION = "client_version"; - + // JSON数据中表示是否完成(completed)的字段名,常用于任务相关的数据中,用来标记某个任务是否已经完成, + // 例如任务完成状态的记录与判断会依赖于此字段。 public final static String GTASK_JSON_COMPLETED = "completed"; - +// JSON数据中表示当前列表(current_list_id)的ID字段名,可能用于标识当前操作所涉及的列表的唯一标识, + // 比如在任务移动操作中,可指明当前所在的列表以及要移动到的目标列表等情况。 public final static String GTASK_JSON_CURRENT_LIST_ID = "current_list_id"; - + // JSON数据中表示默认列表(default_list_id)的ID字段名,用于指定某个默认的列表, + // 例如新创建任务时默认归属的列表等情况可以通过此标识来确定。 public final static String GTASK_JSON_DEFAULT_LIST_ID = "default_list_id"; - + // JSON数据中表示是否已删除(deleted)的字段名,可用于标记某个数据实体是否已经被删除, + // 在数据清理、回收站相关功能或者同步时判断数据是否已不存在等场景中会用到。 public final static String GTASK_JSON_DELETED = "deleted"; - +// JSON数据中表示目标列表(dest_list)的字段名,常用于操作涉及到将数据移动或关联到其他列表的情况, + // 比如在移动任务操作中,此字段指明任务要移动到的目标列表的相关信息。 public final static String GTASK_JSON_DEST_LIST = "dest_list"; - + // JSON数据中表示目标父级(dest_parent)的字段名,可用于表示某个数据实体在移动、关联等操作后的目标父级对象相关信息, + // 例如任务移动后所属的新的父任务或者父列表等情况通过此字段来体现。 public final static String GTASK_JSON_DEST_PARENT = "dest_parent"; - + // JSON数据中表示目标父级类型(dest_parent_type)的字段名,配合"dest_parent"字段使用, + // 用于明确目标父级对象具体是什么类型,比如是任务组还是普通任务列表等类型区分。 public final static String GTASK_JSON_DEST_PARENT_TYPE = "dest_parent_type"; - +// JSON数据中表示实体变化量(entity_delta)的字段名,可能用于记录某个数据实体在更新、修改等操作前后的变化差异情况, + // 例如任务的某些属性值发生了改变,通过此字段可以详细记录具体的变化内容。 public final static String GTASK_JSON_ENTITY_DELTA = "entity_delta"; - + // JSON数据中表示实体类型(entity_type)的字段名,用于区分不同的数据实体类型, + // 像任务(TASK)、任务组(GROUP)等不同类型的数据实体可以通过此字段来标识区分。 public final static String GTASK_JSON_ENTITY_TYPE = "entity_type"; - + // JSON数据中表示获取已删除数据(get_deleted)的字段名,可能用于发起获取已被标记为删除的数据的相关操作, + // 比如在回收站功能或者数据同步时,查看哪些数据已被删除等情况会用到。 public final static String GTASK_JSON_GET_DELETED = "get_deleted"; - + // JSON数据中表示数据实体的唯一标识符(id)的字段名,用于唯一确定某个具体的数据实体, + // 无论是任务、列表还是其他相关数据结构,都可以通过此ID进行查找、关联等操作。 public final static String GTASK_JSON_ID = "id"; - + // JSON数据中表示索引(index)的字段名,可用于在有序的数据集合(如任务列表等)中确定某个数据实体的位置顺序, + // 例如在排序、定位特定位置的任务等场景中会使用到。 public final static String GTASK_JSON_INDEX = "index"; - + // JSON数据中表示最后修改时间(last_modified)的字段名,用于记录某个数据实体最后一次被修改的时间戳, + // 在数据同步、版本控制以及判断数据是否有更新等场景中,此时间信息非常关键。 public final static String GTASK_JSON_LAST_MODIFIED = "last_modified"; - + // JSON数据中表示最新同步点(latest_sync_point)的字段名,用于标记在数据同步过程中的最新有效同步位置或者时间点, + // 可帮助确定下次同步从何处开始、哪些数据需要重新同步等情况。 public final static String GTASK_JSON_LATEST_SYNC_POINT = "latest_sync_point"; - +// JSON数据中表示列表(list_id)的ID字段名,用于唯一标识不同的列表, + // 在涉及到多个列表管理、任务在不同列表间移动等操作时,通过此ID来区分不同的列表。 public final static String GTASK_JSON_LIST_ID = "list_id"; - + // JSON数据中表示列表(lists)的字段名,通常可能用来存放多个列表相关信息的集合, + // 例如获取所有列表信息或者返回一组列表数据时,可通过此字段进行表示。 public final static String GTASK_JSON_LISTS = "lists"; - + // JSON数据中表示名称(name)的字段名,常用于表示数据实体(如任务、列表等)的名称属性, + // 方便展示、查找以及用户识别不同的数据实体。 public final static String GTASK_JSON_NAME = "name"; + // JSON数据中表示新的标识符(new_id)的字段名,可能在某些操作(如数据复制、克隆或者重新生成ID等情况)下, + // 用于标记新产生的数据实体的唯一标识。 public final static String GTASK_JSON_NEW_ID = "new_id"; - + // JSON数据中表示笔记(notes)的字段名,可能用于存放与任务、列表等相关的笔记信息, + // 比如用户针对某个任务添加的备注、说明等内容可通过此字段关联存储。 public final static String GTASK_JSON_NOTES = "notes"; - + // JSON数据中表示父级ID(parent_id)的字段名,用于确定某个数据实体所属的父级对象的唯一标识, + // 例如任务所属的任务组或者列表的ID等情况通过此字段关联表示。 public final static String GTASK_JSON_PARENT_ID = "parent_id"; - + // JSON数据中表示前一个兄弟节点ID(prior_sibling_id)的字段名,在有序的数据结构(如任务列表按顺序排列)中, + // 可用于定位某个数据实体前一个相邻的兄弟节点的标识,方便进行顺序调整、插入等操作。 public final static String GTASK_JSON_PRIOR_SIBLING_ID = "prior_sibling_id"; - + // JSON数据中表示操作结果(results)的字段名,常用于存放执行某个操作(如创建、更新、移动等操作)后的返回结果信息, + // 比如操作是否成功、返回的数据等情况通过此字段进行传递和展示。 public final static String GTASK_JSON_RESULTS = "results"; - + // JSON数据中表示源列表(source_list)的字段名,在涉及数据移动、复制等操作时, + // 用于指明操作数据的原始来源列表的相关信息,与目标列表(dest_list)相对应。 public final static String GTASK_JSON_SOURCE_LIST = "source_list"; - + // JSON数据中表示任务(tasks)的字段名,通常用于存放一组任务相关信息的集合, + // 比如获取所有任务列表、返回一批任务数据等情况会通过此字段来表示。 public final static String GTASK_JSON_TASKS = "tasks"; + // JSON数据中表示类型(type)的字段名,和前面提到的"entity_type"类似,用于区分不同的数据实体类型, + // 不过可能使用场景更通用一些,可用于各种需要区分类型的情况。 public final static String GTASK_JSON_TYPE = "type"; - + // JSON数据中表示任务组(GROUP)类型的具体值,用于明确某个数据实体是任务组类型, + // 在通过"type"或者"entity_type"字段判断类型时,此常量可作为对应的值来标识任务组。 public final static String GTASK_JSON_TYPE_GROUP = "GROUP"; - +// JSON数据中表示任务(TASK)类型的具体值,用于确定某个数据实体是普通任务类型, + // 同样在类型判断场景中,以此常量来标识任务类型的数据实体。 public final static String GTASK_JSON_TYPE_TASK = "TASK"; - + // JSON数据中表示用户(user)的字段名,可能用于存放与操作相关的用户信息, + // 比如操作执行者的用户名、用户ID等相关属性,便于记录操作的归属主体等情况。 public final static String GTASK_JSON_USER = "user"; + // 以下是与特定文件夹相关的字符串常量定义,可能用于标识不同功能或用途的文件夹 + // 表示与MIUI笔记相关的文件夹前缀字符串,用于在文件夹命名或者标识中区分出属于MIUI笔记相关的文件夹, + // 方便进行针对性的处理或者识别。 public final static String MIUI_FOLDER_PREFFIX = "[MIUI_Notes]"; - + // 表示默认文件夹的名称常量,可能用于指代某个默认创建或者默认使用的文件夹, + // 例如新笔记、新任务等默认存放的文件夹可以用此名称来表示。 public final static String FOLDER_DEFAULT = "Default"; - + // 表示通话记录笔记相关的文件夹名称常量,用于专门存放与通话记录笔记相关内容的文件夹标识, + // 便于对通话记录相关笔记进行分类管理和查找等操作。 public final static String FOLDER_CALL_NOTE = "Call_Note"; - +// 表示元数据(METADATA)相关的文件夹名称常量,可能用于存放各种数据的元数据信息的文件夹, + // 在数据管理、备份恢复以及数据关联等场景中,此文件夹的元数据起着重要作用。 public final static String FOLDER_META = "METADATA"; + // 以下是与元数据头部相关的字段名常量定义,用于明确元数据中不同部分的标识 + // 表示元数据中GTask的ID(meta_gid)的字段名,在元数据结构里用于存放与之关联的GTask的唯一标识信息, + // 可用于数据关联、同步以及根据GTask查找对应元数据等操作。 public final static String META_HEAD_GTASK_ID = "meta_gid"; - +// 表示元数据中笔记(meta_note)相关信息的字段名,可能用于存放具体的笔记内容或者笔记相关属性等元数据信息, + // 在处理笔记的元数据时,通过此字段来获取对应的笔记相关数据。 public final static String META_HEAD_NOTE = "meta_note"; - +// 表示元数据中其他数据(meta_data)相关信息的字段名,可用于存放除了前面特定标识之外的其他各种数据的元数据, + // 例如一些自定义的扩展数据、辅助数据等的元数据都可以通过此字段来表示和管理。 public final static String META_HEAD_DATA = "meta_data"; - +// 表示元数据中其他数据(meta_data)相关信息的字段名,可用于存放除了前面特定标识之外的其他各种数据的元数据, + // 例如一些自定义的扩展数据、辅助数据等的元数据都可以通过此字段来表示和管理。 public final static String META_NOTE_NAME = "[META INFO] DON'T UPDATE AND DELETE"; } From 9f0a17390586b1ab4443a89a43536d02472159ea Mon Sep 17 00:00:00 2001 From: zcs <2755796131@qq.com> Date: Mon, 30 Dec 2024 12:21:30 +0800 Subject: [PATCH 4/4] =?UTF-8?q?=E5=BC=A0=E6=98=8C=E7=9B=9B=20202201002062?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../src/net/micode/notes/data/Contact.java | 26 ++- .../notes/data/NotesDatabaseHelper.java | 66 +++--- .../net/micode/notes/data/NotesProvider.java | 67 ++++-- .../net/micode/notes/gtask/data/MetaData.java | 36 +++- .../src/net/micode/notes/gtask/data/Node.java | 64 +++--- .../net/micode/notes/gtask/data/SqlData.java | 84 ++++---- .../net/micode/notes/gtask/data/SqlNote.java | 151 ++++++++------ .../src/net/micode/notes/gtask/data/Task.java | 111 ++++++---- .../net/micode/notes/gtask/data/TaskList.java | 99 ++++++--- .../exception/ActionFailureException.java | 13 +- .../exception/NetworkFailureException.java | 17 +- .../notes/gtask/remote/GTaskASyncTask.java | 41 +++- .../notes/gtask/remote/GTaskClient.java | 121 +++++++---- .../notes/gtask/remote/GTaskManager.java | 191 +++++++++++------ .../notes/gtask/remote/GTaskSyncService.java | 61 ++++-- .../src/net/micode/notes/model/Note.java | 88 ++++++-- .../net/micode/notes/model/WorkingNote.java | 195 +++++++++++++----- 17 files changed, 954 insertions(+), 477 deletions(-) diff --git a/src/Notes-master/src/net/micode/notes/data/Contact.java b/src/Notes-master/src/net/micode/notes/data/Contact.java index e1f8942..16c899f 100644 --- a/src/Notes-master/src/net/micode/notes/data/Contact.java +++ b/src/Notes-master/src/net/micode/notes/data/Contact.java @@ -24,29 +24,34 @@ import android.telephony.PhoneNumberUtils; import android.util.Log; import java.util.HashMap; - //ceshihebing2 +// Contact类,主要用于根据电话号码获取对应的联系人姓名 public class Contact { + // 用于缓存联系人信息(电话号码与对应姓名的映射)的哈希表,以避免重复查询数据库 private static HashMap sContactCache; + // 用于日志记录的标签,方便在日志中识别相关输出所属的类 private static final String TAG = "Contact"; - + // 构建查询联系人的SQL语句的选择条件部分,用于从联系人数据中筛选出符合条件的记录 + // 条件基于电话号码匹配、MIME类型匹配以及原始联系人ID的相关限定等 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 = '+')"; - +// 根据给定的上下文(Context)和电话号码获取对应的联系人姓名 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)); Cursor cursor = context.getContentResolver().query( @@ -55,19 +60,28 @@ public class Contact { selection, new String[] { phoneNumber }, null); - + // 通过上下文的内容解析器(ContentResolver)发起数据库查询操作 + // 查询联系人数据的相关表(Data.CONTENT_URI),只获取联系人的显示名称(Phone.DISPLAY_NAME)字段 + // 使用上面构建好的选择条件(selection),并传入电话号码作为查询参数 +// 如果查询结果游标(Cursor)不为空,并且游标能够移动到第一条记录(意味着有匹配的联系人数据) if (cursor != null && cursor.moveToFirst()) { try { + // 从游标中获取联系人的显示名称(假设显示名称在结果集的第0个位置) String name = cursor.getString(0); + // 从游标中获取联系人的显示名称(假设显示名称在结果集的第0个位置) sContactCache.put(phoneNumber, name); + // 返回获取到的联系人姓名 return name; } catch (IndexOutOfBoundsException e) { + // 如果在从游标获取数据时发生越界异常(比如结果集格式不符合预期等情况),记录错误日志 Log.e(TAG, " Cursor get string error " + e.toString()); return null; } finally { + // 无论是否发生异常,都要关闭游标,释放相关资源 cursor.close(); } } else { + // 如果游标为空或者游标中没有匹配的记录,记录调试日志表示没有找到对应联系人 Log.d(TAG, "No contact matched with number:" + phoneNumber); return null; } diff --git a/src/Notes-master/src/net/micode/notes/data/NotesDatabaseHelper.java b/src/Notes-master/src/net/micode/notes/data/NotesDatabaseHelper.java index ffe5d57..2965719 100644 --- a/src/Notes-master/src/net/micode/notes/data/NotesDatabaseHelper.java +++ b/src/Notes-master/src/net/micode/notes/data/NotesDatabaseHelper.java @@ -25,23 +25,26 @@ 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; +// NotesDatabaseHelper类继承自SQLiteOpenHelper,用于管理与笔记相关的数据库的创建、升级以及一些数据库表结构和触发器的初始化操作 public class NotesDatabaseHelper extends SQLiteOpenHelper { + // 定义数据库的名称,这里是"note.db",即该应用对应的数据库文件名 private static final String DB_NAME = "note.db"; - +// 定义数据库的版本号,用于数据库升级等场景判断,当前版本为4 private static final int DB_VERSION = 4; - +// TABLE接口,用于定义数据库中不同表的名称常量,方便在代码中统一引用,避免硬编码表名 public interface TABLE { public static final String NOTE = "note"; public static final String DATA = "data"; } - +// TABLE接口,用于定义数据库中不同表的名称常量,方便在代码中统一引用,避免硬编码表名 private static final String TAG = "NotesDatabaseHelper"; - +// 采用单例模式,保存该类的唯一实例,确保整个应用中只有一个数据库帮助类实例存在 private static NotesDatabaseHelper mInstance; - + // 创建"note"表的SQL语句,定义了表的各个列名、数据类型以及默认值等信息 + // 例如定义了笔记的ID、父级ID、提醒日期、背景颜色ID等列,为笔记相关数据存储做准备 private static final String CREATE_NOTE_TABLE_SQL = "CREATE TABLE " + TABLE.NOTE + "(" + NoteColumns.ID + " INTEGER PRIMARY KEY," + @@ -62,7 +65,7 @@ public class NotesDatabaseHelper extends SQLiteOpenHelper { NoteColumns.GTASK_ID + " TEXT NOT NULL DEFAULT ''," + NoteColumns.VERSION + " INTEGER NOT NULL DEFAULT 0" + ")"; - +// 创建"data"表的SQL语句,同样定义了表的各列信息,用于存储笔记相关的数据内容等信息,如MIME类型、所属笔记ID、创建日期等 private static final String CREATE_DATA_TABLE_SQL = "CREATE TABLE " + TABLE.DATA + "(" + DataColumns.ID + " INTEGER PRIMARY KEY," + @@ -77,13 +80,14 @@ public class NotesDatabaseHelper extends SQLiteOpenHelper { DataColumns.DATA4 + " TEXT NOT NULL DEFAULT ''," + DataColumns.DATA5 + " TEXT NOT NULL DEFAULT ''" + ")"; - +// 创建针对"data"表中NOTE_ID列的索引的SQL语句,有助于提高基于笔记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 + * 当笔记移动到文件夹时,增加文件夹的笔记数量的触发器SQL语句定义 */ private static final String NOTE_INCREASE_FOLDER_COUNT_ON_UPDATE_TRIGGER = "CREATE TRIGGER increase_folder_count_on_update "+ @@ -96,6 +100,7 @@ public class NotesDatabaseHelper extends SQLiteOpenHelper { /** * Decrease folder's note count when move note from folder + * 当笔记移动到文件夹时,增加文件夹的笔记数量的触发器SQL语句定义 */ private static final String NOTE_DECREASE_FOLDER_COUNT_ON_UPDATE_TRIGGER = "CREATE TRIGGER decrease_folder_count_on_update " + @@ -109,6 +114,7 @@ public class NotesDatabaseHelper extends SQLiteOpenHelper { /** * Increase folder's note count when insert new note to the folder + * 当从文件夹中删除笔记时,减少文件夹的笔记数量的触发器SQL语句定义,同样要确保当前笔记数量大于0才进行减少操作 */ private static final String NOTE_INCREASE_FOLDER_COUNT_ON_INSERT_TRIGGER = "CREATE TRIGGER increase_folder_count_on_insert " + @@ -121,6 +127,7 @@ public class NotesDatabaseHelper extends SQLiteOpenHelper { /** * Decrease folder's note count when delete note from the folder + * 当插入类型为DataConstants.NOTE的数据时,更新笔记内容的触发器SQL语句定义 */ private static final String NOTE_DECREASE_FOLDER_COUNT_ON_DELETE_TRIGGER = "CREATE TRIGGER decrease_folder_count_on_delete " + @@ -134,6 +141,7 @@ public class NotesDatabaseHelper extends SQLiteOpenHelper { /** * Update note's content when insert data with type {@link DataConstants#NOTE} + * 当类型为DataConstants.NOTE的数据发生变化时,更新笔记内容的触发器SQL语句定义 */ private static final String DATA_UPDATE_NOTE_CONTENT_ON_INSERT_TRIGGER = "CREATE TRIGGER update_note_content_on_insert " + @@ -147,6 +155,7 @@ public class NotesDatabaseHelper extends SQLiteOpenHelper { /** * Update note's content when data with {@link DataConstants#NOTE} type has changed + * 当类型为DataConstants.NOTE的数据被删除时,更新笔记内容的触发器SQL语句定义,将对应笔记的内容设置为空字符串 */ private static final String DATA_UPDATE_NOTE_CONTENT_ON_UPDATE_TRIGGER = "CREATE TRIGGER update_note_content_on_update " + @@ -184,6 +193,7 @@ public class NotesDatabaseHelper extends SQLiteOpenHelper { /** * Delete notes belong to folder which has been deleted + * 当笔记被删除时,删除属于该笔记的数据的触发器SQL语句定义 */ private static final String FOLDER_DELETE_NOTES_ON_DELETE_TRIGGER = "CREATE TRIGGER folder_delete_notes_on_delete " + @@ -195,6 +205,7 @@ public class NotesDatabaseHelper extends SQLiteOpenHelper { /** * Move notes belong to folder which has been moved to trash folder + * 当文件夹被移动到回收站文件夹时,将属于该文件夹的笔记也移动到回收站文件夹的触发器SQL语句定义 */ private static final String FOLDER_MOVE_NOTES_ON_TRASH_TRIGGER = "CREATE TRIGGER folder_move_notes_on_trash " + @@ -205,18 +216,18 @@ public class NotesDatabaseHelper extends SQLiteOpenHelper { " SET " + NoteColumns.PARENT_ID + "=" + Notes.ID_TRASH_FOLER + " WHERE " + NoteColumns.PARENT_ID + "=old." + NoteColumns.ID + ";" + " END"; - +// 构造函数,调用父类SQLiteOpenHelper的构造函数,传入数据库名称、版本号等信息 public NotesDatabaseHelper(Context context) { super(context, DB_NAME, null, DB_VERSION); } - +// 在给定的SQLiteDatabase对象上执行创建"note"表的操作,包括创建表、重新创建相关触发器以及创建系统文件夹,并输出日志表示表已创建 public void createNoteTable(SQLiteDatabase db) { db.execSQL(CREATE_NOTE_TABLE_SQL); reCreateNoteTableTriggers(db); createSystemFolder(db); Log.d(TAG, "note table has been created"); } - +// 先删除已存在的"note"表相关的触发器(如果有),然后再重新创建所有"note"表对应的触发器 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"); @@ -234,17 +245,17 @@ public class NotesDatabaseHelper extends SQLiteOpenHelper { db.execSQL(FOLDER_DELETE_NOTES_ON_DELETE_TRIGGER); db.execSQL(FOLDER_MOVE_NOTES_ON_TRASH_TRIGGER); } - + // 向"note"表中插入代表不同系统文件夹的记录,如通话记录文件夹、根文件夹、临时文件夹、回收站文件夹等,通过构造ContentValues对象并调用db.insert方法来实现 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 */ @@ -252,7 +263,7 @@ public class NotesDatabaseHelper extends SQLiteOpenHelper { 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 */ @@ -269,14 +280,14 @@ public class NotesDatabaseHelper extends SQLiteOpenHelper { values.put(NoteColumns.TYPE, Notes.TYPE_SYSTEM); db.insert(TABLE.NOTE, null, values); } - + // 在给定数据库对象上创建"data"表,创建表后会重新创建"data"表相关的触发器以及创建针对"data"表NOTE_ID列的索引,并输出日志表示表已创建 public void createDataTable(SQLiteDatabase db) { db.execSQL(CREATE_DATA_TABLE_SQL); reCreateDataTableTriggers(db); db.execSQL(CREATE_DATA_NOTE_ID_INDEX_SQL); 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"); @@ -286,6 +297,7 @@ public class NotesDatabaseHelper extends SQLiteOpenHelper { db.execSQL(DATA_UPDATE_NOTE_CONTENT_ON_UPDATE_TRIGGER); db.execSQL(DATA_UPDATE_NOTE_CONTENT_ON_DELETE_TRIGGER); } +// 采用静态同步方法实现单例模式获取类的实例,若实例不存在则创建新的NotesDatabaseHelper实例并返回,保证整个应用只有一个该类的实例在使用 static synchronized NotesDatabaseHelper getInstance(Context context) { if (mInstance == null) { @@ -293,59 +305,61 @@ public class NotesDatabaseHelper extends SQLiteOpenHelper { } return mInstance; } - +// 重写SQLiteOpenHelper的onCreate方法,在数据库首次创建时调用,内部会分别调用createNoteTable(db)和createDataTable(db)来创建"note"表和"data"表 @Override public void onCreate(SQLiteDatabase db) { createNoteTable(db); createDataTable(db); } - +// 重写SQLiteOpenHelper的onUpgrade方法,用于处理数据库升级操作,根据旧版本号和新版本号进行不同的升级逻辑判断与处理 @Override public void onUpgrade(SQLiteDatabase db, int oldVersion, int newVersion) { boolean reCreateTriggers = false; boolean skipV2 = false; - +// 重写SQLiteOpenHelper的onUpgrade方法,用于处理数据库升级操作,根据旧版本号和新版本号进行不同的升级逻辑判断与处理 if (oldVersion == 1) { upgradeToV2(db); skipV2 = true; // this upgrade including the upgrade from v2 to v3 oldVersion++; } - +// 如果旧版本是2且没有跳过v2的升级(即不是从版本1升上来的情况),执行升级到版本3的操作,设置需要重新创建触发器的标记为true,再将旧版本号加1 if (oldVersion == 2 && !skipV2) { upgradeToV3(db); reCreateTriggers = true; oldVersion++; } - +// 如果旧版本是3,执行升级到版本4的操作,然后将旧版本号加1 if (oldVersion == 3) { upgradeToV4(db); oldVersion++; } - +// 如果需要重新创建触发器(根据前面的升级逻辑判断),则分别重新创建"note"表和"data"表相关的触发器 if (reCreateTriggers) { reCreateNoteTableTriggers(db); reCreateDataTableTriggers(db); } - + // 如果最终旧版本号和新版本号不一致,说明升级出现问题,抛出异常表示升级失败 if (oldVersion != newVersion) { throw new IllegalStateException("Upgrade notes database to version " + newVersion + "fails"); } } - +// 升级到版本2的具体操作,先删除已存在的"note"表和"data"表(如果有),然后重新创建这两个表 private void upgradeToV2(SQLiteDatabase db) { db.execSQL("DROP TABLE IF EXISTS " + TABLE.NOTE); db.execSQL("DROP TABLE IF EXISTS " + TABLE.DATA); createNoteTable(db); createDataTable(db); } - +// 升级到版本3的具体操作,包括删除一些不再使用的触发器,向"note"表添加gtask id列,以及插入一个回收站系统文件夹记录 private void upgradeToV3(SQLiteDatabase db) { + // drop unused triggers,删除不再使用的用于更新笔记修改日期的相关触发器 // 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 + // 向"note"表添加gtask id列,数据类型为文本,默认值为空字符串 db.execSQL("ALTER TABLE " + TABLE.NOTE + " ADD COLUMN " + NoteColumns.GTASK_ID + " TEXT NOT NULL DEFAULT ''"); // add a trash system folder @@ -354,7 +368,7 @@ public class NotesDatabaseHelper extends SQLiteOpenHelper { values.put(NoteColumns.TYPE, Notes.TYPE_SYSTEM); db.insert(TABLE.NOTE, null, values); } - +// 升级到版本4的具体操作,向"note"表添加version列,数据类型为整数,默认值为0 private void upgradeToV4(SQLiteDatabase db) { db.execSQL("ALTER TABLE " + TABLE.NOTE + " ADD COLUMN " + NoteColumns.VERSION + " INTEGER NOT NULL DEFAULT 0"); diff --git a/src/Notes-master/src/net/micode/notes/data/NotesProvider.java b/src/Notes-master/src/net/micode/notes/data/NotesProvider.java index edb0a60..12cbc3d 100644 --- a/src/Notes-master/src/net/micode/notes/data/NotesProvider.java +++ b/src/Notes-master/src/net/micode/notes/data/NotesProvider.java @@ -33,15 +33,17 @@ import net.micode.notes.R; import net.micode.notes.data.Notes.DataColumns; import net.micode.notes.data.Notes.NoteColumns; import net.micode.notes.data.NotesDatabaseHelper.TABLE; - +// NotesProvider类继承自ContentProvider,用于作为内容提供者,对外提供对笔记相关数据的增删改查等操作, +// 并处理不同类型的Uri请求以区分操作的具体对象(如笔记、笔记数据等)以及搜索相关功能。 public class NotesProvider extends ContentProvider { + // 用于匹配不同的Uri请求的UriMatcher对象,通过静态代码块进行初始化配置 private static final UriMatcher mMatcher; - +// 用于匹配不同的Uri请求的UriMatcher对象,通过静态代码块进行初始化配置 private NotesDatabaseHelper mHelper; - +// 用于日志输出的标识字符串,便于在调试和记录运行情况时准确识别相关日志信息 private static final String TAG = "NotesProvider"; - +// 定义不同的Uri匹配码,用于区分不同类型的Uri请求,对应不同的笔记或数据相关操作 private static final int URI_NOTE = 1; private static final int URI_NOTE_ITEM = 2; private static final int URI_DATA = 3; @@ -49,7 +51,7 @@ public class NotesProvider extends ContentProvider { private static final int URI_SEARCH = 5; private static final int URI_SEARCH_SUGGEST = 6; - +// 静态代码块,初始化UriMatcher对象,添加各种不同的Uri匹配规则,以便后续根据传入的Uri来确定具体的操作类型 static { mMatcher = new UriMatcher(UriMatcher.NO_MATCH); mMatcher.addURI(Notes.AUTHORITY, "note", URI_NOTE); @@ -64,6 +66,8 @@ public class NotesProvider extends ContentProvider { /** * 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. + * 定义搜索结果的投影(查询返回的列)字符串,用于在搜索笔记时获取特定的列信息, + * 例如笔记的ID、将笔记摘要列处理后作为搜索建议的文本等,同时设置了图标、意图动作、意图数据类型等相关信息。 */ private static final String NOTES_SEARCH_PROJECTION = NoteColumns.ID + "," + NoteColumns.ID + " AS " + SearchManager.SUGGEST_COLUMN_INTENT_EXTRA_DATA + "," @@ -72,46 +76,53 @@ public class NotesProvider extends ContentProvider { + 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; - +// 在ContentProvider创建时调用,获取NotesDatabaseHelper的单例实例,用于后续数据库操作,返回true表示初始化成功 @Override public boolean onCreate() { mHelper = NotesDatabaseHelper.getInstance(getContext()); return true; } - +// 根据传入的Uri等参数执行查询操作,根据不同的Uri匹配情况,从相应的数据库表(笔记表或数据表)中查询数据,并返回结果游标 @Override public Cursor query(Uri uri, String[] projection, String selection, String[] selectionArgs, String sortOrder) { Cursor c = null; + // 获取可读的数据库连接对象 SQLiteDatabase db = mHelper.getReadableDatabase(); String id = null; + // 通过UriMatcher匹配传入的Uri,根据匹配结果执行不同的查询逻辑 switch (mMatcher.match(uri)) { case URI_NOTE: + // 查询笔记表,使用传入的投影、选择条件等参数进行查询 c = db.query(TABLE.NOTE, projection, selection, selectionArgs, null, null, sortOrder); break; case URI_NOTE_ITEM: + // 获取具体笔记项的ID(从Uri路径中解析),然后根据该ID构建更精确的查询条件进行查询 id = uri.getPathSegments().get(1); c = db.query(TABLE.NOTE, projection, NoteColumns.ID + "=" + id + parseSelection(selection), selectionArgs, null, null, sortOrder); break; case URI_DATA: + // 查询数据表,同样使用传入的相关参数进行查询 c = db.query(TABLE.DATA, projection, selection, selectionArgs, null, null, sortOrder); break; case URI_DATA_ITEM: + // 获取具体数据项的ID(从Uri路径中解析),然后基于该ID构建查询条件查询数据表 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: + // 对于搜索相关的Uri请求,如果传入了排序、投影等额外参数则抛出异常,因为搜索操作有其固定的逻辑 if (sortOrder != null || projection != null) { throw new IllegalArgumentException( "do not specify sortOrder, selection, selectionArgs, or projection" + "with this query"); @@ -119,10 +130,12 @@ public class NotesProvider extends ContentProvider { String searchString = null; if (mMatcher.match(uri) == URI_SEARCH_SUGGEST) { + // 如果是搜索建议的Uri且路径中有搜索字符串,则获取该字符串 if (uri.getPathSegments().size() > 1) { searchString = uri.getPathSegments().get(1); } } else { + // 否则从Uri的查询参数中获取名为"pattern"的搜索字符串 searchString = uri.getQueryParameter("pattern"); } @@ -131,7 +144,9 @@ public class NotesProvider extends ContentProvider { } try { + // 格式化搜索字符串,添加通配符用于模糊匹配查询 searchString = String.format("%%%s%%", searchString); + // 使用格式化后的搜索字符串执行原始查询,查询符合条件的笔记信息 c = db.rawQuery(NOTES_SNIPPET_SEARCH_QUERY, new String[] { searchString }); } catch (IllegalStateException ex) { @@ -141,21 +156,25 @@ public class NotesProvider extends ContentProvider { default: throw new IllegalArgumentException("Unknown URI " + uri); } + // 如果查询结果游标不为空,设置其通知Uri,以便在数据变化时能收到通知更新相关UI等 if (c != null) { c.setNotificationUri(getContext().getContentResolver(), uri); } return c; } - + // 根据传入的Uri和数据值,向相应的数据库表(笔记表或数据表)插入数据,并返回插入后生成的新记录的Uri @Override public Uri insert(Uri uri, ContentValues values) { SQLiteDatabase db = mHelper.getWritableDatabase(); long dataId = 0, noteId = 0, insertedId = 0; + // 通过UriMatcher匹配Uri,根据匹配结果执行不同的插入逻辑 switch (mMatcher.match(uri)) { case URI_NOTE: + // 向笔记表插入数据,获取插入后生成的记录ID insertedId = noteId = db.insert(TABLE.NOTE, null, values); break; case URI_DATA: + // 如果插入的数据中包含笔记ID,则获取该ID,然后向数据表插入数据并获取插入后的记录ID if (values.containsKey(DataColumns.NOTE_ID)) { noteId = values.getAsLong(DataColumns.NOTE_ID); } else { @@ -167,32 +186,36 @@ public class NotesProvider extends ContentProvider { throw new IllegalArgumentException("Unknown URI " + uri); } // Notify the note uri + // 如果插入的是笔记数据且笔记ID大于0,通知笔记相关的Uri数据发生了变化 if (noteId > 0) { getContext().getContentResolver().notifyChange( ContentUris.withAppendedId(Notes.CONTENT_NOTE_URI, noteId), null); } - + // 如果插入的是数据且数据ID大于0,通知数据相关的Uri数据发生了变化 // Notify the data uri if (dataId > 0) { getContext().getContentResolver().notifyChange( ContentUris.withAppendedId(Notes.CONTENT_DATA_URI, dataId), null); } - +// 返回插入后新记录对应的Uri,通过将插入的记录ID附加到原始Uri上生成 return ContentUris.withAppendedId(uri, insertedId); } - +// 根据传入的Uri和选择条件,从相应的数据库表(笔记表或数据表)中删除数据,并返回删除的记录数量 @Override public int delete(Uri uri, String selection, String[] selectionArgs) { int count = 0; String id = null; SQLiteDatabase db = mHelper.getWritableDatabase(); boolean deleteData = false; + // 通过UriMatcher匹配Uri,根据匹配结果执行不同的删除逻辑 switch (mMatcher.match(uri)) { case URI_NOTE: + // 构建笔记表删除的选择条件,确保ID大于0(可能有额外的传入选择条件拼接),然后执行删除操作并获取删除的记录数量 selection = "(" + selection + ") AND " + NoteColumns.ID + ">0 "; count = db.delete(TABLE.NOTE, selection, selectionArgs); break; case URI_NOTE_ITEM: + // 获取要删除的具体笔记项的ID,先判断是否为不允许删除的系统文件夹(ID小于等于0),如果不是则构建精确的删除条件进行删除操作 id = uri.getPathSegments().get(1); /** * ID that smaller than 0 is system folder which is not allowed to @@ -206,10 +229,12 @@ public class NotesProvider extends ContentProvider { NoteColumns.ID + "=" + id + parseSelection(selection), selectionArgs); break; case URI_DATA: + // 直接从数据表中根据选择条件执行删除操作,标记要通知数据相关的Uri变化,并获取删除的记录数量 count = db.delete(TABLE.DATA, selection, selectionArgs); deleteData = true; break; case URI_DATA_ITEM: + // 获取要删除的具体数据项的ID,构建精确的删除条件从数据表中删除数据,同样标记要通知数据相关的Uri变化,并获取删除的记录数量 id = uri.getPathSegments().get(1); count = db.delete(TABLE.DATA, DataColumns.ID + "=" + id + parseSelection(selection), selectionArgs); @@ -220,35 +245,42 @@ public class NotesProvider extends ContentProvider { } if (count > 0) { if (deleteData) { + // 如果是更新数据相关操作且有数据被更新,通知笔记相关的Uri数据发生了变化(可能关联的笔记数据有变动) getContext().getContentResolver().notifyChange(Notes.CONTENT_NOTE_URI, null); } + // 通知传入的Uri对应的数据发生了变化 getContext().getContentResolver().notifyChange(uri, null); } 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(); boolean updateData = false; + // 通过UriMatcher匹配Uri,根据匹配结果执行不同的更新逻辑 switch (mMatcher.match(uri)) { case URI_NOTE: + // 在更新笔记表数据前,调用方法更新笔记的版本号(可能根据选择条件等情况),然后执行更新操作并获取更新的记录数量 increaseNoteVersion(-1, selection, selectionArgs); count = db.update(TABLE.NOTE, values, selection, selectionArgs); break; case URI_NOTE_ITEM: + // 获取要更新的具体笔记项的ID,调用方法根据该ID等情况更新笔记的版本号,然后构建精确的更新条件对笔记表执行更新操作并获取记录数量 id = uri.getPathSegments().get(1); increaseNoteVersion(Long.valueOf(id), selection, selectionArgs); count = db.update(TABLE.NOTE, values, NoteColumns.ID + "=" + id + parseSelection(selection), selectionArgs); break; case URI_DATA: + // 直接从数据表中根据选择条件执行删除操作,标记要通知数据相关的Uri变化,并获取删除的记录数量 count = db.update(TABLE.DATA, values, selection, selectionArgs); updateData = true; break; case URI_DATA_ITEM: + // 获取要删除的具体数据项的ID,构建精确的删除条件从数据表中删除数据,同样标记要通知数据相关的Uri变化,并获取删除的记录数量 id = uri.getPathSegments().get(1); count = db.update(TABLE.DATA, values, DataColumns.ID + "=" + id + parseSelection(selection), selectionArgs); @@ -260,17 +292,18 @@ public class NotesProvider extends ContentProvider { if (count > 0) { if (updateData) { + // 如果是删除数据相关操作且有数据被删除,通知笔记相关的Uri数据发生了变化(可能关联的笔记数据有变动) getContext().getContentResolver().notifyChange(Notes.CONTENT_NOTE_URI, null); - } + } // 通知传入的Uri对应的数据发生了变化 getContext().getContentResolver().notifyChange(uri, null); } return count; } - + // 根据传入的Uri、更新的数据值和选择条件,对相应的数据库表(笔记表或数据表)中的数据进行更新,并返回更新的记录数量 private String parseSelection(String selection) { return (!TextUtils.isEmpty(selection) ? " AND (" + selection + ')' : ""); } - + // 根据传入的笔记ID、选择条件和选择参数,构建并执行更新笔记版本号的SQL语句,实现笔记版本号的增加操作 private void increaseNoteVersion(long id, String selection, String[] selectionArgs) { StringBuilder sql = new StringBuilder(120); sql.append("UPDATE "); @@ -292,7 +325,7 @@ public class NotesProvider extends ContentProvider { } sql.append(selectString); } - +// 用于获取 mHelper.getWritableDatabase().execSQL(sql.toString()); } diff --git a/src/Notes-master/src/net/micode/notes/gtask/data/MetaData.java b/src/Notes-master/src/net/micode/notes/gtask/data/MetaData.java index 41021a3..cbedbd5 100644 --- a/src/Notes-master/src/net/micode/notes/gtask/data/MetaData.java +++ b/src/Notes-master/src/net/micode/notes/gtask/data/MetaData.java @@ -14,7 +14,7 @@ * limitations under the License. */ -package net.micode.notes.gtask.data;//测试 +package net.micode.notes.gtask.data; import android.database.Cursor; import android.util.Log; @@ -23,38 +23,51 @@ import net.micode.notes.tool.GTaskStringUtils; import org.json.JSONException; import org.json.JSONObject; - +// MetaData类继承自Task类,主要用于处理与任务相关的元数据操作,例如设置元数据、获取关联的GID(可能是某种任务相关的唯一标识)等, +// 并且重写了一些父类的方法来适配自身对于元数据处理的特殊逻辑。 public class MetaData extends Task { + // 用于日志输出的标识字符串,其值为类的简单名称,便于在调试和记录运行情况时准确识别相关日志信息 private final static String TAG = MetaData.class.getSimpleName(); - +// 用于存储与该元数据相关联的GID(可能是Google Tasks等相关任务系统中的任务唯一标识之类的信息),初始化为null private String mRelatedGid = null; - +// 设置元数据的方法,接收一个GID(任务标识)和一个JSONObject类型的元数据信息对象 public void setMeta(String gid, JSONObject metaInfo) { try { + // 将传入的GID放入元数据信息对象中,对应的键由GTaskStringUtils.META_HEAD_GTASK_ID指定, + // 如果放入过程中出现JSON异常则记录错误日志 metaInfo.put(GTaskStringUtils.META_HEAD_GTASK_ID, gid); } catch (JSONException e) { Log.e(TAG, "failed to put related gid"); } + // 将处理后的元数据信息对象转换为字符串,并调用父类的方法(假设父类有相应的设置笔记内容的方法)设置为笔记内容 setNotes(metaInfo.toString()); + // 设置名称为特定的字符串,该字符串由GTaskStringUtils.META_NOTE_NAME指定,可能用于标识这是元数据相关的名称 setName(GTaskStringUtils.META_NOTE_NAME); - } + } +// 获取关联的GID的方法,返回之前存储的mRelatedGid字符串 public String getRelatedGid() { return mRelatedGid; } - +// 重写父类的方法,用于判断当前的元数据是否值得保存,判断依据是看获取到的笔记内容(通过父类方法获取,可能存储着关键元数据信息)是否为null, + // 如果不为null则表示有值得保存的数据,返回true,否则返回false @Override public boolean isWorthSaving() { return getNotes() != null; } - +// 重写父类的方法,用于根据远程的JSON对象来设置自身的内容(包括解析出关联的GID等元数据相关操作) @Override public void setContentByRemoteJSON(JSONObject js) { + // 先调用父类的同名方法来执行一些通用的设置内容的基础操作(假设父类该方法有相应逻辑) super.setContentByRemoteJSON(js); + // 如果获取到的笔记内容不为null(说明有元数据信息) if (getNotes() != null) { try { + // 将笔记内容字符串转换为JSONObject对象,以便解析其中的元数据信息 JSONObject metaInfo = new JSONObject(getNotes().trim()); + // 从解析后的元数据信息对象中获取关联的GID,并赋值给mRelatedGid成员变量, + // 如果获取过程中出现JSON异常,则记录警告日志并将mRelatedGid设置为null mRelatedGid = metaInfo.getString(GTaskStringUtils.META_HEAD_GTASK_ID); } catch (JSONException e) { Log.w(TAG, "failed to get related gid"); @@ -62,18 +75,21 @@ public class MetaData extends Task { } } } - +// 重写父类的方法,这里明确表示该方法不应该被调用,直接抛出 IllegalAccessError异常, + // 可能是因为对于元数据对象来说,通过本地JSON来设置内容不符合其业务逻辑设计 @Override public void setContentByLocalJSON(JSONObject js) { // this function should not be called throw new IllegalAccessError("MetaData:setContentByLocalJSON should not be called"); } - +// 重写父类的方法,同样明确表示该方法不应该被调用,直接抛出 IllegalAccessError异常, + // 可能是因为获取本地JSON对象的操作在元数据对象的业务逻辑中没有对应的合理实现或者不需要这样的操作 @Override public JSONObject getLocalJSONFromContent() { throw new IllegalAccessError("MetaData:getLocalJSONFromContent should not be called"); } - +// 重写父类的方法,再次明确表示该方法不应该被调用,直接抛出 IllegalAccessError异常, + // 可能是因为获取同步操作相关信息在元数据对象的业务逻辑中没有对应的处理逻辑或者不需要这样的操作 @Override public int getSyncAction(Cursor c) { throw new IllegalAccessError("MetaData:getSyncAction should not be called"); diff --git a/src/Notes-master/src/net/micode/notes/gtask/data/Node.java b/src/Notes-master/src/net/micode/notes/gtask/data/Node.java index 63950e0..3d82677 100644 --- a/src/Notes-master/src/net/micode/notes/gtask/data/Node.java +++ b/src/Notes-master/src/net/micode/notes/gtask/data/Node.java @@ -13,83 +13,91 @@ * See the License for the specific language governing permissions and * limitations under the License. */ - +// 包声明,表明该类所属的包名,此处是net.micode.notes.gtask.data包 package net.micode.notes.gtask.data; import android.database.Cursor; import org.json.JSONObject; - +// 定义抽象类Node,通常作为节点相关数据结构或业务逻辑的抽象表示, +// 具体的节点类可能会继承该抽象类并实现其中的抽象方法 public abstract class Node { + // 定义同步操作的各种类型常量,用于表示不同的同步相关动作 + // 表示无同步操作 public static final int SYNC_ACTION_NONE = 0; - + // 表示向远程添加的同步操作 public static final int SYNC_ACTION_ADD_REMOTE = 1; - +// 表示在本地添加的同步操作 public static final int SYNC_ACTION_ADD_LOCAL = 2; - +// 表示从远程删除的同步操作 public static final int SYNC_ACTION_DEL_REMOTE = 3; - + // 表示从本地删除的同步操作 public static final int SYNC_ACTION_DEL_LOCAL = 4; - + // 表示更新远程的同步操作 public static final int SYNC_ACTION_UPDATE_REMOTE = 5; - +// 表示更新本地的同步操作 public static final int SYNC_ACTION_UPDATE_LOCAL = 6; - + // 表示更新冲突的同步操作,可能在同步过程中出现两端数据不一致等冲突情况时用到 public static final int SYNC_ACTION_UPDATE_CONFLICT = 7; - + // 表示同步操作出现错误的情况 public static final int SYNC_ACTION_ERROR = 8; - +// 用于存储节点的全局唯一标识符(可能是对应任务等的唯一标识),初始化为null private String mGid; - +// 存储节点的名称,初始化为空字符串 private String mName; - + // 记录节点最后一次被修改的时间戳,初始化为0 private long mLastModified; - +// 标记该节点是否已被删除,初始化为false private boolean mDeleted; - +// 默认构造函数,用于初始化Node对象的各个属性的初始值 public Node() { mGid = null; mName = ""; mLastModified = 0; mDeleted = false; } - + // 抽象方法,用于获取创建动作对应的JSONObject,具体的创建动作由actionId参数指定, + // 不同的子类需要根据自身业务逻辑实现该方法来构造相应的创建动作数据 public abstract JSONObject getCreateAction(int actionId); - +// 抽象方法,用于获取更新动作对应的JSONObject,根据传入的actionId来确定具体的更新动作, + // 子类需实现该方法以按照实际情况生成合适的更新动作相关的JSON数据 public abstract JSONObject getUpdateAction(int actionId); - +// 抽象方法,根据远程传来的JSONObject数据设置节点的内容, + // 具体的设置逻辑由继承该抽象类的子类来实现,以适配不同类型节点的数据解析和设置 public abstract void setContentByRemoteJSON(JSONObject js); - +// 抽象方法,根据本地的JSONObject数据设置节点的内容,同样子类要实现该方法来处理本地数据到节点内容的设置 public abstract void setContentByLocalJSON(JSONObject js); - +// 抽象方法,从节点当前内容获取对应的本地JSON表示形式, + // 子类需根据自身保存的数据结构等情况将节点内容转换为合适的JSONObject返回 public abstract JSONObject getLocalJSONFromContent(); - + // 抽象方法,根据给定的Cursor(通常用于从数据库查询结果中获取数据)来确定节点的同步操作类型, + // 子类要实现该方法以解析Cursor中的数据从而判断出对应的同步动作 public abstract int getSyncAction(Cursor c); - + // 设置节点的全局唯一标识符(Gid)的方法 public void setGid(String gid) { this.mGid = gid; - } + }// 设置节点名称的方法 public void setName(String name) { this.mName = name; } - +// 设置节点最后一次修改时间戳的方法 public void setLastModified(long lastModified) { this.mLastModified = lastModified; } - +// 设置节点是否已删除的标记的方法 public void setDeleted(boolean deleted) { this.mDeleted = deleted; } - + // 获取节点的全局唯一标识符(Gid)的方法 public String getGid() { return this.mGid; } - + // 获取节点名称的方法 public String getName() { return this.mName; } - +// 获取节点最后一次修改时间戳的方法 public long getLastModified() { return this.mLastModified; } diff --git a/src/Notes-master/src/net/micode/notes/gtask/data/SqlData.java b/src/Notes-master/src/net/micode/notes/gtask/data/SqlData.java index d3ec3be..69405c1 100644 --- a/src/Notes-master/src/net/micode/notes/gtask/data/SqlData.java +++ b/src/Notes-master/src/net/micode/notes/gtask/data/SqlData.java @@ -13,7 +13,7 @@ * See the License for the specific language governing permissions and * limitations under the License. */ - +// 包声明,表明该类所属的包名为net.micode.notes.gtask.data package net.micode.notes.gtask.data; import android.content.ContentResolver; @@ -33,45 +33,48 @@ import net.micode.notes.gtask.exception.ActionFailureException; import org.json.JSONException; import org.json.JSONObject; - +// SqlData类,主要用于处理与数据库相关的数据操作,可能涉及到将数据在数据库和JSON格式之间进行转换、保存等功能 public class SqlData { + // 用于日志记录的标签,取当前类的简单名称 private static final String TAG = SqlData.class.getSimpleName(); - +// 表示无效的ID值,通常用于初始化或者标识某个非法的ID状态 private static final int INVALID_ID = -99999; - + // 定义一个字符串数组,用于指定从数据库查询数据时的投影(即要查询的列),包含了数据相关的多个列名 public static final String[] PROJECTION_DATA = new String[] { DataColumns.ID, DataColumns.MIME_TYPE, DataColumns.CONTENT, DataColumns.DATA1, DataColumns.DATA3 }; - +// 定义常量,表示在投影数组中ID列对应的索引位置,方便后续从查询结果的Cursor中获取对应列的数据 public static final int DATA_ID_COLUMN = 0; - + // 定义常量,表示在投影数组中MIME_TYPE列对应的索引位置 public static final int DATA_MIME_TYPE_COLUMN = 1; - +// 定义常量,表示在投影数组中CONTENT列对应的索引位置 public static final int DATA_CONTENT_COLUMN = 2; - +// 定义常量,表示在投影数组中DATA1列对应的索引位置 public static final int DATA_CONTENT_DATA_1_COLUMN = 3; - +// 定义常量,表示在投影数组中DATA3列对应的索引位置 public static final int DATA_CONTENT_DATA_3_COLUMN = 4; - +// 用于与内容提供器进行交互,以便执行数据库相关的操作,如查询、插入、更新等 private ContentResolver mContentResolver; - + // 标记当前操作是否是创建新数据的操作,初始化为true private boolean mIsCreate; - +// 存储数据对应的ID,初始化为无效ID值 private long mDataId; - +// 存储数据的MIME类型,初始化为默认的Note类型(可能对应某种笔记的数据类型) private String mDataMimeType; - + // 存储数据的具体内容,初始化为空字符串 private String mDataContent; - +// 存储数据内容中相关的一个长整型数据(具体含义可能根据业务而定),初始化为0 private long mDataContentData1; - +// 存储数据内容中相关的一个字符串数据(具体含义可能根据业务而定),初始化为空字符串 private String mDataContentData3; - +// 用于存储要进行差异更新的数据值,即记录数据有变化的部分,初始化为一个新的ContentValues对象 private ContentValues mDiffDataValues; - + // 构造函数,用于创建一个新的SqlData对象,通常在创建新数据时使用 + // 接收一个Context上下文对象,用于获取ContentResolver来操作数据库 public SqlData(Context context) { + // 获取上下文对应的ContentResolver,以便后续进行数据库操作 mContentResolver = context.getContentResolver(); mIsCreate = true; mDataId = INVALID_ID; @@ -81,14 +84,15 @@ public class SqlData { mDataContentData3 = ""; mDiffDataValues = new ContentValues(); } - +// 另一个构造函数,用于根据已有的Cursor(通常是从数据库查询返回的结果集)来创建SqlData对象,常用于读取已有数据 + // 接收一个Context上下文对象和一个Cursor对象,用于初始化该对象的各个属性 public SqlData(Context context, Cursor c) { mContentResolver = context.getContentResolver(); mIsCreate = false; loadFromCursor(c); mDiffDataValues = new ContentValues(); } - + // 私有方法,用于从给定的Cursor中加载数据到当前对象的各个属性中,根据之前定义的列索引常量来获取对应列的数据 private void loadFromCursor(Cursor c) { mDataId = c.getLong(DATA_ID_COLUMN); mDataMimeType = c.getString(DATA_MIME_TYPE_COLUMN); @@ -96,40 +100,47 @@ public class SqlData { mDataContentData1 = c.getLong(DATA_CONTENT_DATA_1_COLUMN); mDataContentData3 = c.getString(DATA_CONTENT_DATA_3_COLUMN); } - + // 用于根据传入的JSONObject来设置当前对象的数据内容,比较传入数据与当前已有数据的差异, + // 将有差异的部分放入mDiffDataValues中,以便后续进行更新操作 public void setContent(JSONObject js) throws JSONException { + // 获取JSON对象中ID字段的值,如果不存在则使用无效ID值 long dataId = js.has(DataColumns.ID) ? js.getLong(DataColumns.ID) : INVALID_ID; + // 如果是创建操作或者当前对象的ID与传入JSON中的ID不同,则将新的ID放入差异数据值中 if (mIsCreate || mDataId != dataId) { mDiffDataValues.put(DataColumns.ID, dataId); } mDataId = dataId; - + // 获取JSON对象中MIME_TYPE字段的值,如果不存在则使用默认的Note类型 String dataMimeType = js.has(DataColumns.MIME_TYPE) ? js.getString(DataColumns.MIME_TYPE) : DataConstants.NOTE; + // 如果是创建操作或者当前对象的MIME_TYPE与传入JSON中的不同,则将新的MIME_TYPE放入差异数据值中 if (mIsCreate || !mDataMimeType.equals(dataMimeType)) { mDiffDataValues.put(DataColumns.MIME_TYPE, dataMimeType); - } + } // 获取JSON对象中CONTENT字段的值,如果不存在则使用空字符串 mDataMimeType = dataMimeType; String dataContent = js.has(DataColumns.CONTENT) ? js.getString(DataColumns.CONTENT) : ""; + // 如果是创建操作或者当前对象的CONTENT与传入JSON中的不同,则将新的CONTENT放入差异数据值中 if (mIsCreate || !mDataContent.equals(dataContent)) { mDiffDataValues.put(DataColumns.CONTENT, dataContent); } mDataContent = dataContent; - +// 获取JSON对象中DATA1字段的值,如果不存在则使用0 long dataContentData1 = js.has(DataColumns.DATA1) ? js.getLong(DataColumns.DATA1) : 0; + // 如果是创建操作或者当前对象的DATA1值与传入JSON中的不同,则将新的DATA1放入差异数据值中 if (mIsCreate || mDataContentData1 != dataContentData1) { mDiffDataValues.put(DataColumns.DATA1, dataContentData1); - } + } // 获取JSON对象中DATA3字段的值,如果不存在则使用空字符串 mDataContentData1 = dataContentData1; - + // 如果是创建操作或者当前对象的DATA3与传入JSON中的不同,则将新的DATA3放入差异数据值中 String dataContentData3 = js.has(DataColumns.DATA3) ? js.getString(DataColumns.DATA3) : ""; if (mIsCreate || !mDataContentData3.equals(dataContentData3)) { mDiffDataValues.put(DataColumns.DATA3, dataContentData3); } mDataContentData3 = dataContentData3; } - + // 用于获取当前对象的数据内容,并将其转换为JSONObject格式返回, + // 如果当前操作是创建操作(还未在数据库中创建该数据),则记录错误日志并返回null public JSONObject getContent() throws JSONException { if (mIsCreate) { Log.e(TAG, "it seems that we haven't created this in database yet"); @@ -143,46 +154,49 @@ public class SqlData { js.put(DataColumns.DATA3, mDataContentData3); return js; } - +// 用于将当前对象的数据提交到数据库中,根据是创建操作还是更新操作执行不同的逻辑 public void commit(long noteId, boolean validateVersion, long version) { if (mIsCreate) { + // 如果是创建操作,且当前数据ID是无效ID且差异数据值中包含ID字段,则移除该ID字段(可能有默认生成机制) if (mDataId == INVALID_ID && mDiffDataValues.containsKey(DataColumns.ID)) { mDiffDataValues.remove(DataColumns.ID); } - + // 将关联的笔记ID添加到差异数据值中 mDiffDataValues.put(DataColumns.NOTE_ID, noteId); + // 使用ContentResolver插入数据到指定的数据库URI,并获取插入后生成的URI Uri uri = mContentResolver.insert(Notes.CONTENT_DATA_URI, mDiffDataValues); try { + // 尝试从插入后的URI中获取生成的ID值,如果解析失败则记录错误日志并抛出异常 mDataId = Long.valueOf(uri.getPathSegments().get(1)); } catch (NumberFormatException e) { Log.e(TAG, "Get note id error :" + e.toString()); throw new ActionFailureException("create note failed"); } - } else { + } else { // 如果是更新操作,且差异数据值中有数据需要更新 if (mDiffDataValues.size() > 0) { int result = 0; - if (!validateVersion) { + if (!validateVersion) {// 如果不需要验证版本,则直接更新指定ID的数据 result = mContentResolver.update(ContentUris.withAppendedId( Notes.CONTENT_DATA_URI, mDataId), mDiffDataValues, null, null); - } else { + } else {// 如果需要验证版本,则根据指定的版本条件更新数据,通过SQL语句的条件筛选来确保更新的准确性 result = mContentResolver.update(ContentUris.withAppendedId( Notes.CONTENT_DATA_URI, mDataId), mDiffDataValues, " ? in (SELECT " + NoteColumns.ID + " FROM " + TABLE.NOTE + " WHERE " + NoteColumns.VERSION + "=?)", new String[] { String.valueOf(noteId), String.valueOf(version) }); - } + } // 如果更新结果为0(即没有数据被更新),记录警告日志,提示可能用户在同步时更新了笔记 if (result == 0) { Log.w(TAG, "there is no update. maybe user updates note when syncing"); } } } - + // 清空差异数据值,准备下一次的数据操作 mDiffDataValues.clear(); mIsCreate = false; } - + // 获取当前数据对象的ID值的方法 public long getId() { return mDataId; } diff --git a/src/Notes-master/src/net/micode/notes/gtask/data/SqlNote.java b/src/Notes-master/src/net/micode/notes/gtask/data/SqlNote.java index 79a4095..d0a9c51 100644 --- a/src/Notes-master/src/net/micode/notes/gtask/data/SqlNote.java +++ b/src/Notes-master/src/net/micode/notes/gtask/data/SqlNote.java @@ -13,7 +13,7 @@ * See the License for the specific language governing permissions and * limitations under the License. */ - +// 包声明,表明该类所属的包名为net.micode.notes.gtask.data package net.micode.notes.gtask.data; import android.appwidget.AppWidgetManager; @@ -37,12 +37,12 @@ import org.json.JSONObject; import java.util.ArrayList; - -public class SqlNote { +// SqlNote类,主要用于处理与笔记相关的数据操作,包括从数据库读取笔记信息、设置笔记内容、将笔记内容转换为JSON格式以及将笔记数据提交到数据库等功能 +public class SqlNote {// 用于日志记录的标签,取当前类的简单名称 private static final String TAG = SqlNote.class.getSimpleName(); - + // 表示无效的ID值,通常用于初始化或者标识某个非法的ID状态 private static final int INVALID_ID = -99999; - +// 定义一个字符串数组,用于指定从数据库查询笔记数据时的投影(即要查询的列),包含了笔记相关的多个列名 public static final String[] PROJECTION_NOTE = new String[] { NoteColumns.ID, NoteColumns.ALERTED_DATE, NoteColumns.BG_COLOR_ID, NoteColumns.CREATED_DATE, NoteColumns.HAS_ATTACHMENT, NoteColumns.MODIFIED_DATE, @@ -51,77 +51,80 @@ public class SqlNote { NoteColumns.LOCAL_MODIFIED, NoteColumns.ORIGIN_PARENT_ID, NoteColumns.GTASK_ID, NoteColumns.VERSION }; - +// 定义常量,表示在投影数组中ID列对应的索引位置,方便后续从查询结果的Cursor中获取对应列的数据 public static final int ID_COLUMN = 0; - + // 定义常量,表示在投影数组中ALERTED_DATE列对应的索引位置 public static final int ALERTED_DATE_COLUMN = 1; - + // 定义常量,表示在投影数组中BG_COLOR_ID列对应的索引位置 public static final int BG_COLOR_ID_COLUMN = 2; - +// 定义常量,表示在投影数组中CREATED_DATE列对应的索引位置 public static final int CREATED_DATE_COLUMN = 3; - +// 定义常量,表示在投影数组中HAS_ATTACHMENT列对应的索引位置 public static final int HAS_ATTACHMENT_COLUMN = 4; - + // 定义常量,表示在投影数组中MODIFIED_DATE列对应的索引位置 public static final int MODIFIED_DATE_COLUMN = 5; - + // 定义常量,表示在投影数组中NOTES_COUNT列对应的索引位置 public static final int NOTES_COUNT_COLUMN = 6; - + // 定义常量,表示在投影数组中PARENT_ID列对应的索引位置 public static final int PARENT_ID_COLUMN = 7; - + // 定义常量,表示在投影数组中SNIPPET列对应的索引位置 public static final int SNIPPET_COLUMN = 8; - + // 定义常量,表示在投影数组中TYPE列对应的索引位置 public static final int TYPE_COLUMN = 9; - +// 定义常量,表示在投影数组中WIDGET_ID列对应的索引位置 public static final int WIDGET_ID_COLUMN = 10; - +// 定义常量,表示在投影数组中WIDGET_TYPE列对应的索引位置 public static final int WIDGET_TYPE_COLUMN = 11; - +// 定义常量,表示在投影数组中SYNC_ID列对应的索引位置 public static final int SYNC_ID_COLUMN = 12; - +// 定义常量,表示在投影数组中LOCAL_MODIFIED列对应的索引位置 public static final int LOCAL_MODIFIED_COLUMN = 13; - +// 定义常量,表示在投影数组中ORIGIN_PARENT_ID列对应的索引位置 public static final int ORIGIN_PARENT_ID_COLUMN = 14; - +// 定义常量,表示在投影数组中GTASK_ID列对应的索引位置 public static final int GTASK_ID_COLUMN = 15; - + // 定义常量,表示在投影数组中VERSION列对应的索引位置 public static final int VERSION_COLUMN = 16; - +// 上下文对象,用于获取系统资源、执行与应用上下文相关的操作等 private Context mContext; - + // 用于与内容提供器进行交互,以便执行数据库相关的操作,如查询、插入、更新等 private ContentResolver mContentResolver; - + // 标记当前操作是否是创建新笔记的操作,初始化为true private boolean mIsCreate; - + // 存储笔记的ID,初始化为无效ID值 private long mId; - +// 存储笔记的提醒日期(时间戳相关,具体含义根据业务而定),初始化为0 private long mAlertDate; - +// 存储笔记的背景颜色ID,初始化为通过ResourceParser获取的默认背景颜色ID private int mBgColorId; - +// 存储笔记的创建日期(时间戳),初始化为当前系统时间 private long mCreatedDate; - + // 存储笔记是否有附件的标识,0表示没有,初始化为0 private int mHasAttachment; - +// 存储笔记的最后修改日期(时间戳),初始化为当前系统时间 private long mModifiedDate; - + // 存储笔记的父级ID(可能用于表示笔记的层级关系等),初始化为0 private long mParentId; - +// 存储笔记的摘要信息(简短描述等),初始化为空字符串 private String mSnippet; - + // 存储笔记的类型,初始化为普通笔记类型(Notes.TYPE_NOTE) private int mType; - +// 存储笔记关联的桌面小部件ID,初始化为无效的小部件ID private int mWidgetId; - + // 存储笔记关联的桌面小部件类型,初始化为无效的小部件类型(Notes.TYPE_WIDGET_INVALIDE) private int mWidgetType; - +// 存储笔记的原始父级ID(具体用途可能与笔记的来源、历史关系等相关),初始化为0 private long mOriginParent; - +// 存储笔记的版本号,初始化为0 private long mVersion; +// 用于存储要进行差异更新的笔记数据值,即记录笔记数据有变化的部分,初始化为一个新的ContentValues对象 private ContentValues mDiffNoteValues; - +// 存储与该笔记相关的数据列表(可能是笔记包含的多个具体数据项等,每个数据项用SqlData类表示) + private ArrayList mDataList; - + // 构造函数,用于创建一个新的SqlNote对象,通常在创建新笔记时使用 + // 接收一个Context上下文对象,用于获取ContentResolver等资源来操作数据库以及进行其他相关操作 public SqlNote(Context context) { mContext = context; mContentResolver = context.getContentResolver(); @@ -142,7 +145,8 @@ public class SqlNote { mDiffNoteValues = new ContentValues(); mDataList = new ArrayList(); } - +// 构造函数,用于根据已有的Cursor(通常是从数据库查询返回的结果集)来创建SqlNote对象,常用于读取已有笔记数据 + // 接收一个Context上下文对象和一个Cursor对象,用于初始化该对象的各个属性 public SqlNote(Context context, Cursor c) { mContext = context; mContentResolver = context.getContentResolver(); @@ -153,7 +157,8 @@ public class SqlNote { loadDataContent(); mDiffNoteValues = new ContentValues(); } - +// 构造函数,用于根据指定的笔记ID从数据库中加载笔记数据来创建SqlNote对象 + // 接收一个Context上下文对象和一个笔记ID(long类型),用于初始化该对象的各个属性 public SqlNote(Context context, long id) { mContext = context; mContentResolver = context.getContentResolver(); @@ -165,10 +170,11 @@ public class SqlNote { mDiffNoteValues = new ContentValues(); } - +// 私有方法,根据给定的笔记ID从数据库中查询并加载笔记数据到当前对象的各个属性中,通过调用另一个loadFromCursor方法实现具体的属性赋值 + private void loadFromCursor(long id) { Cursor c = null; - try { + try {// 使用ContentResolver根据指定的URI、查询条件(通过ID匹配)查询笔记数据 c = mContentResolver.query(Notes.CONTENT_NOTE_URI, PROJECTION_NOTE, "(_id=?)", new String[] { String.valueOf(id) @@ -184,7 +190,8 @@ public class SqlNote { c.close(); } } - + // 私有方法,用于从给定的Cursor中加载笔记数据到当前对象的各个属性中,根据之前定义的列索引常量来获取对应列的数据 + private void loadFromCursor(Cursor c) { mId = c.getLong(ID_COLUMN); mAlertDate = c.getLong(ALERTED_DATE_COLUMN); @@ -199,11 +206,13 @@ public class SqlNote { mWidgetType = c.getInt(WIDGET_TYPE_COLUMN); mVersion = c.getLong(VERSION_COLUMN); } - +// 私有方法,用于加载与当前笔记相关的数据内容(具体数据项,用SqlData类表示),通过查询数据库并将结果封装到SqlData对象中,添加到mDataList列表里 + private void loadDataContent() { Cursor c = null; mDataList.clear(); - try { + try { + // 使用ContentResolver根据指定的URI、查询条件(通过笔记ID匹配)查询与笔记相关的数据 c = mContentResolver.query(Notes.CONTENT_DATA_URI, SqlData.PROJECTION_DATA, "(note_id=?)", new String[] { String.valueOf(mId) @@ -225,6 +234,8 @@ public class SqlNote { c.close(); } } +// 用于根据传入的JSONObject来设置当前笔记对象的数据内容,根据笔记类型(如普通笔记、文件夹等)进行不同的处理, + // 将有差异的部分放入mDiffNoteValues中,以便后续进行更新操作,返回设置是否成功的布尔值 public boolean setContent(JSONObject js) { try { @@ -232,7 +243,7 @@ public class SqlNote { if (note.getInt(NoteColumns.TYPE) == Notes.TYPE_SYSTEM) { Log.w(TAG, "cannot set system folder"); } else if (note.getInt(NoteColumns.TYPE) == Notes.TYPE_FOLDER) { - // for folder we can only update the snnipet and type + // for folder we can only update the snnipet and type// 如果是文件夹类型,只能更新摘要和类型信息 String snippet = note.has(NoteColumns.SNIPPET) ? note .getString(NoteColumns.SNIPPET) : ""; if (mIsCreate || !mSnippet.equals(snippet)) { @@ -247,6 +258,7 @@ public class SqlNote { } mType = type; } else if (note.getInt(NoteColumns.TYPE) == Notes.TYPE_NOTE) { + // 如果是普通笔记类型 JSONArray dataArray = js.getJSONArray(GTaskStringUtils.META_HEAD_DATA); long id = note.has(NoteColumns.ID) ? note.getLong(NoteColumns.ID) : INVALID_ID; if (mIsCreate || mId != id) { @@ -330,24 +342,24 @@ public class SqlNote { mDiffNoteValues.put(NoteColumns.ORIGIN_PARENT_ID, originParent); } mOriginParent = originParent; - + // 遍历JSON数组中的每个数据项(对应笔记包含的具体数据内容) for (int i = 0; i < dataArray.length(); i++) { JSONObject data = dataArray.getJSONObject(i); - SqlData sqlData = null; + SqlData sqlData = null; // 如果数据项中有ID字段,尝试在已有的数据列表中查找对应ID的数据对象 if (data.has(DataColumns.ID)) { long dataId = data.getLong(DataColumns.ID); for (SqlData temp : mDataList) { if (dataId == temp.getId()) { sqlData = temp; } - } + }// 如果未找到,则创建一个新的SqlData对象,并添加到数据列表中 } if (sqlData == null) { sqlData = new SqlData(mContext); mDataList.add(sqlData); } - + // 调用SqlData对象的方法设置其具体内容 sqlData.setContent(data); } } @@ -358,7 +370,8 @@ public class SqlNote { } return true; } - + // 用于获取当前笔记对象的数据内容,并将其转换为JSONObject格式返回, + // 如果当前操作是创建操作(还未在数据库中创建该笔记),则记录错误日志并返回null public JSONObject getContent() { try { JSONObject js = new JSONObject(); @@ -406,46 +419,47 @@ public class SqlNote { } return null; } - +// 设置笔记的父级ID的方法,同时将该变化记录到差异更新数据值中,以便后续更新数据库时使用 public void setParentId(long id) { mParentId = id; mDiffNoteValues.put(NoteColumns.PARENT_ID, id); } - + // 设置笔记的Gtask ID(具体含义可能与相关任务关联等有关)的方法,将该值记录到差异更新数据值中 public void setGtaskId(String gid) { mDiffNoteValues.put(NoteColumns.GTASK_ID, gid); } - + // 设置笔记的同步ID的方法,将该值记录到差异更新数据值中 public void setSyncId(long syncId) { mDiffNoteValues.put(NoteColumns.SYNC_ID, syncId); } - + // 重置笔记的本地修改标记(将其设置为0)的方法,通过更新差异数据值来实现 public void resetLocalModified() { mDiffNoteValues.put(NoteColumns.LOCAL_MODIFIED, 0); } - + // 获取笔记ID的方法 public long getId() { return mId; } - +// 获取笔记父级ID的方法 public long getParentId() { return mParentId; } - + // 获取笔记摘要信息的方法 public String getSnippet() { return mSnippet; } - + // 判断笔记是否为普通笔记类型的方法,返回布尔值 public boolean isNoteType() { return mType == Notes.TYPE_NOTE; } - + // 用于将当前笔记对象的数据提交到数据库中,根据是创建操作还是更新操作执行不同的逻辑, + // 同时也会处理与之关联的数据(通过调用SqlData的commit方法)的提交操作 public void commit(boolean validateVersion) { - if (mIsCreate) { + if (mIsCreate) {// 如果是创建操作,且当前笔记ID是无效ID且差异数据值中包含ID字段,则移除该ID字段(可能有默认生成机制) if (mId == INVALID_ID && mDiffNoteValues.containsKey(NoteColumns.ID)) { mDiffNoteValues.remove(NoteColumns.ID); } - + // 使用ContentResolver插入笔记数据到指定的数据库URI,并获取插入后生成的URI Uri uri = mContentResolver.insert(Notes.CONTENT_NOTE_URI, mDiffNoteValues); try { mId = Long.valueOf(uri.getPathSegments().get(1)); @@ -458,18 +472,19 @@ public class SqlNote { } if (mType == Notes.TYPE_NOTE) { + // 如果是普通笔记类型,遍历与之关联的数据列表,调用每个SqlData对象的commit方法提交数据 for (SqlData sqlData : mDataList) { sqlData.commit(mId, false, -1); } } - } else { + } else {// 如果是更新操作 if (mId <= 0 && mId != Notes.ID_ROOT_FOLDER && mId != Notes.ID_CALL_RECORD_FOLDER) { Log.e(TAG, "No such note"); throw new IllegalStateException("Try to update note with invalid id"); } if (mDiffNoteValues.size() > 0) { mVersion ++; - int result = 0; + int result = 0;// 根据是否验证版本来决定更新数据库的条件 if (!validateVersion) { result = mContentResolver.update(Notes.CONTENT_NOTE_URI, mDiffNoteValues, "(" + NoteColumns.ID + "=?)", new String[] { @@ -493,12 +508,12 @@ public class SqlNote { } } } - + // 重新从数据库加载笔记数据,刷新本地信息(通过调用相关加载方法) // refresh local info loadFromCursor(mId); if (mType == Notes.TYPE_NOTE) loadDataContent(); - +// 清空差异数据值,准备下一次的数据操作 mDiffNoteValues.clear(); mIsCreate = false; } diff --git a/src/Notes-master/src/net/micode/notes/gtask/data/Task.java b/src/Notes-master/src/net/micode/notes/gtask/data/Task.java index 6a19454..556d5cc 100644 --- a/src/Notes-master/src/net/micode/notes/gtask/data/Task.java +++ b/src/Notes-master/src/net/micode/notes/gtask/data/Task.java @@ -30,21 +30,22 @@ import net.micode.notes.tool.GTaskStringUtils; import org.json.JSONArray; import org.json.JSONException; import org.json.JSONObject; +// Task类继承自抽象类Node,代表一个任务相关的实体,包含任务自身的各种属性以及与任务操作相关的方法, +// 例如创建、更新任务的JSON表示,从JSON数据设置任务内容等功能,用于处理任务相关的数据逻辑 - -public class Task extends Node { +public class Task extends Node { // 用于日志记录的标签,取当前类的简单名称 private static final String TAG = Task.class.getSimpleName(); - +// 标记任务是否已完成,初始化为false private boolean mCompleted; - + // 存储任务的备注信息(可能是对任务的详细描述等),初始化为null private String mNotes; - +// 存储任务的元信息(以JSONObject形式,具体内容结构可能根据业务而定),初始化为null private JSONObject mMetaInfo; - + // 指向当前任务的前一个兄弟任务(在任务列表等场景下用于表示顺序关系),初始化为null private Task mPriorSibling; - + // 指向当前任务所属的父任务列表(表明任务的归属层级关系),初始化为null private TaskList mParent; - +// 构造函数,调用父类(Node类)的构造函数进行初始化,并对Task类特有的属性进行默认初始化 public Task() { super(); mCompleted = false; @@ -53,21 +54,24 @@ public class Task extends Node { mParent = null; mMetaInfo = null; } - + // 实现抽象类Node中定义的抽象方法,用于获取创建任务的操作对应的JSONObject,该JSON对象包含了创建任务所需的各种信息, + // 如操作类型、任务的基本属性(名称、备注等)、所属父任务等相关信息,按照特定的格式进行组装 public JSONObject getCreateAction(int actionId) { JSONObject js = new JSONObject(); - try { + try { // 设置操作类型为创建任务,使用预定义的字符串常量标识 + // action_type // action_type js.put(GTaskStringUtils.GTASK_JSON_ACTION_TYPE, GTaskStringUtils.GTASK_JSON_ACTION_TYPE_CREATE); - +// 设置操作的唯一标识符(具体业务中用于区分不同的创建操作实例) // action_id js.put(GTaskStringUtils.GTASK_JSON_ACTION_ID, actionId); - + // 设置任务在父任务列表中的索引位置(通过调用父任务列表的方法获取) // index js.put(GTaskStringUtils.GTASK_JSON_INDEX, mParent.getChildTaskIndex(this)); - + // 构建表示任务实体的详细信息的JSONObject,包含任务名称、创建者ID(此处设为"null",可能需根据实际情况调整)、 + // 实体类型(标识为任务类型)以及任务备注(如果存在)等信息 // entity_delta JSONObject entity = new JSONObject(); entity.put(GTaskStringUtils.GTASK_JSON_NAME, getName()); @@ -78,17 +82,17 @@ public class Task extends Node { entity.put(GTaskStringUtils.GTASK_JSON_NOTES, getNotes()); } js.put(GTaskStringUtils.GTASK_JSON_ENTITY_DELTA, entity); - + // 设置任务所属父任务的全局唯一标识符(Gid) // parent_id js.put(GTaskStringUtils.GTASK_JSON_PARENT_ID, mParent.getGid()); - + // 设置目标父任务的类型(此处设为组类型,可能与任务的分组、归属等概念相关,具体依业务而定) // dest_parent_type js.put(GTaskStringUtils.GTASK_JSON_DEST_PARENT_TYPE, GTaskStringUtils.GTASK_JSON_TYPE_GROUP); - + // 设置任务所属列表的全局唯一标识符(通常与父任务列表的Gid一致) // list_id js.put(GTaskStringUtils.GTASK_JSON_LIST_ID, mParent.getGid()); - +// 如果存在前一个兄弟任务,则设置其全局唯一标识符,用于表示任务的顺序关系 // prior_sibling_id if (mPriorSibling != null) { js.put(GTaskStringUtils.GTASK_JSON_PRIOR_SIBLING_ID, mPriorSibling.getGid()); @@ -102,21 +106,22 @@ public class Task extends Node { return js; } - +// 实现抽象类Node中定义的抽象方法,用于获取更新任务的操作对应的JSONObject,包含了更新任务所需的关键信息, + // 如操作类型、任务的唯一标识符、任务实体的更新内容(如名称、备注、是否删除等信息),按照特定格式组装JSON数据 public JSONObject getUpdateAction(int actionId) { JSONObject js = new JSONObject(); - try { + try {// 设置操作类型为更新任务,使用预定义的字符串常量标识 // action_type js.put(GTaskStringUtils.GTASK_JSON_ACTION_TYPE, GTaskStringUtils.GTASK_JSON_ACTION_TYPE_UPDATE); - + // 设置操作的唯一标识符 // action_id js.put(GTaskStringUtils.GTASK_JSON_ACTION_ID, actionId); - +// 设置任务的全局唯一标识符(用于定位要更新的具体任务) // id js.put(GTaskStringUtils.GTASK_JSON_ID, getGid()); - + // 构建表示任务实体更新内容的JSONObject,包含任务名称、任务备注(如果存在)以及任务是否已删除的标记等信息 // entity_delta JSONObject entity = new JSONObject(); entity.put(GTaskStringUtils.GTASK_JSON_NAME, getName()); @@ -134,35 +139,38 @@ public class Task extends Node { return js; } - +// 实现抽象类Node中定义的抽象方法,用于根据远程传来的JSONObject数据设置任务的内容, + // 从JSON对象中解析出任务的各个属性值(如ID、最后修改时间、名称、备注、是否删除、是否完成等)并设置到当前任务对象中, + // 如果解析过程出现JSONException异常,则记录错误日志并抛出相应异常 public void setContentByRemoteJSON(JSONObject js) { if (js != null) { - try { + try {// 从JSON对象中获取任务的全局唯一标识符(Gid)并设置到当前任务对象中 // id if (js.has(GTaskStringUtils.GTASK_JSON_ID)) { setGid(js.getString(GTaskStringUtils.GTASK_JSON_ID)); } - +// 从JSON对象中获取任务最后一次修改的时间戳并设置到当前任务对象中 // last_modified if (js.has(GTaskStringUtils.GTASK_JSON_LAST_MODIFIED)) { setLastModified(js.getLong(GTaskStringUtils.GTASK_JSON_LAST_MODIFIED)); } - + // 从JSON对象中获取任务的名称并设置到当前任务对象中 // name if (js.has(GTaskStringUtils.GTASK_JSON_NAME)) { setName(js.getString(GTaskStringUtils.GTASK_JSON_NAME)); } + // 从JSON对象中获取任务的备注信息并设置到当前任务对象中 // notes if (js.has(GTaskStringUtils.GTASK_JSON_NOTES)) { setNotes(js.getString(GTaskStringUtils.GTASK_JSON_NOTES)); } - +// 从JSON对象中获取任务是否已删除的标记并设置到当前任务对象中 // deleted if (js.has(GTaskStringUtils.GTASK_JSON_DELETED)) { setDeleted(js.getBoolean(GTaskStringUtils.GTASK_JSON_DELETED)); } - + // 从JSON对象中获取任务是否已完成的标记并设置到当前任务对象中 // completed if (js.has(GTaskStringUtils.GTASK_JSON_COMPLETED)) { setCompleted(js.getBoolean(GTaskStringUtils.GTASK_JSON_COMPLETED)); @@ -174,7 +182,10 @@ public class Task extends Node { } } } - + // 实现抽象类Node中定义的抽象方法,用于根据本地的JSONObject数据设置任务的内容, + // 首先进行一些基本的校验(判断JSON对象是否可用,是否包含必要的头部信息等),然后尝试从JSON数据中解析出笔记类型, + // 如果类型为普通笔记类型,则遍历数据数组,找到MIME类型为笔记类型的数据项,从中获取内容并设置为任务的名称, + // 如果解析过程出现JSONException异常,则记录错误日志 public void setContentByLocalJSON(JSONObject js) { if (js == null || !js.has(GTaskStringUtils.META_HEAD_NOTE) || !js.has(GTaskStringUtils.META_HEAD_DATA)) { @@ -203,11 +214,14 @@ public class Task extends Node { e.printStackTrace(); } } - +// 实现抽象类Node中定义的抽象方法,用于从任务当前内容获取对应的本地JSON表示形式, + // 根据任务的元信息是否存在分为两种情况处理:如果是新创建的任务(元信息为空),则按照一定格式构建包含任务名称的JSON对象; + // 如果是已同步过的任务(元信息不为空),则从元信息中提取相关数据并更新任务名称所在的数据项,最后返回构建好的JSON对象, + // 如果在构建JSON对象过程中出现JSONException异常,则记录错误日志并返回null public JSONObject getLocalJSONFromContent() { String name = getName(); try { - if (mMetaInfo == null) { + if (mMetaInfo == null) { // 新任务创建自网络(可能表示从远程创建后还未完全同步等情况) // new task created from web if (name == null) { Log.w(TAG, "the note seems to be an empty one"); @@ -224,7 +238,7 @@ public class Task extends Node { note.put(NoteColumns.TYPE, Notes.TYPE_NOTE); js.put(GTaskStringUtils.META_HEAD_NOTE, note); return js; - } else { + } else { // 已同步的任务(已经有过同步操作,存在相关元信息) // synced task JSONObject note = mMetaInfo.getJSONObject(GTaskStringUtils.META_HEAD_NOTE); JSONArray dataArray = mMetaInfo.getJSONArray(GTaskStringUtils.META_HEAD_DATA); @@ -246,7 +260,8 @@ public class Task extends Node { return null; } } - + // 用于设置任务的元信息,将传入的MetaData对象中的笔记信息(如果不为空且可转换为JSONObject)转换为JSONObject并赋值给mMetaInfo属性, + // 如果转换过程出现JSONException异常,则记录警告日志并将mMetaInfo设为null public void setMetaInfo(MetaData metaData) { if (metaData != null && metaData.getNotes() != null) { try { @@ -257,7 +272,9 @@ public class Task extends Node { } } } - + // 实现抽象类Node中定义的抽象方法,用于根据给定的Cursor(通常用于从数据库查询结果中获取数据)来确定任务的同步操作类型, + // 通过一系列的条件判断来分析任务在本地和远程数据之间的差异情况,从而确定是无同步操作、更新本地、更新远程、更新冲突还是出现错误等同步动作, + // 如果在判断过程中出现异常,则记录错误日志并返回表示错误的同步操作类型 public int getSyncAction(Cursor c) { try { JSONObject noteInfo = null; @@ -274,25 +291,30 @@ public class Task extends Node { Log.w(TAG, "remote note id seems to be deleted"); return SYNC_ACTION_UPDATE_LOCAL; } - +// 验证笔记的ID是否匹配(本地数据库中的ID与从元信息中获取的远程ID进行比较) // validate the note id now if (c.getLong(SqlNote.ID_COLUMN) != noteInfo.getLong(NoteColumns.ID)) { Log.w(TAG, "note id doesn't match"); return SYNC_ACTION_UPDATE_LOCAL; } - if (c.getInt(SqlNote.LOCAL_MODIFIED_COLUMN) == 0) { + if (c.getInt(SqlNote.LOCAL_MODIFIED_COLUMN) == 0) { + // 本地没有更新(通过判断本地修改标记是否为0来确定) // there is no local update if (c.getLong(SqlNote.SYNC_ID_COLUMN) == getLastModified()) { + // 两边都没有更新(本地同步ID与任务最后修改时间戳相同) // no update both side return SYNC_ACTION_NONE; } else { + // 应用远程到本地(本地没有更新但远程有更新,需将远程数据应用到本地) // apply remote to local return SYNC_ACTION_UPDATE_LOCAL; } } else { + // 验证任务的Gtask ID是否匹配(本地数据库中的Gtask ID与当前任务对象的Gid进行比较) // validate gtask id if (!c.getString(SqlNote.GTASK_ID_COLUMN).equals(getGid())) { + // 只有本地修改(本地修改了但远程没有更新,只需将本地修改同步到远程) Log.e(TAG, "gtask id doesn't match"); return SYNC_ACTION_ERROR; } @@ -310,36 +332,37 @@ public class Task extends Node { return SYNC_ACTION_ERROR; } - +// 判断任务是否值得保存的方法,根据任务的元信息是否存在、任务名称以及任务备注是否非空(去除空格后长度大于0)来综合判断, + // 如果满足上述条件之一,则认为任务值得保存,返回true,否则返回false public boolean isWorthSaving() { return mMetaInfo != null || (getName() != null && getName().trim().length() > 0) || (getNotes() != null && getNotes().trim().length() > 0); } - + // 设置任务是否完成的方法,用于更新任务的完成状态 public void setCompleted(boolean completed) { this.mCompleted = completed; } - +// 设置任务备注信息的方法,用于更新任务的备注内容 public void setNotes(String notes) { this.mNotes = notes; } - + // 设置任务前一个兄弟任务的方法,用于建立任务之间的顺序关联 public void setPriorSibling(Task priorSibling) { this.mPriorSibling = priorSibling; } - +// 设置任务所属父任务列表的方法,用于明确任务的层级归属关系 public void setParent(TaskList parent) { this.mParent = parent; } - + // 获取任务是否完成的方法,返回任务的完成状态(true表示已完成,false表示未完成) public boolean getCompleted() { return this.mCompleted; } - +// 获取任务备注信息的方法,返回任务的备注内容 public String getNotes() { return this.mNotes; } - + // 获取任务所属父任务列表的方法,返回指向当前任务所属父任务列表的引用 public Task getPriorSibling() { return this.mPriorSibling; } diff --git a/src/Notes-master/src/net/micode/notes/gtask/data/TaskList.java b/src/Notes-master/src/net/micode/notes/gtask/data/TaskList.java index 4ea21c5..62ca7b5 100644 --- a/src/Notes-master/src/net/micode/notes/gtask/data/TaskList.java +++ b/src/Notes-master/src/net/micode/notes/gtask/data/TaskList.java @@ -28,35 +28,41 @@ import org.json.JSONException; import org.json.JSONObject; import java.util.ArrayList; - +// TaskList类继承自抽象类Node,代表一个任务列表相关的实体,包含任务列表自身的各种属性以及与任务列表操作相关的方法, +// 例如创建、更新任务列表的JSON表示,从JSON数据设置任务列表内容,对任务列表中的子任务进行添加、删除、移动等操作的功能,用于处理任务列表相关的数据逻辑 public class TaskList extends Node { + // 用于日志记录的标签,取当前类的简单名称 private static final String TAG = TaskList.class.getSimpleName(); - + // 存储任务列表的索引(具体含义可能与任务列表在某个整体结构中的顺序、位置等相关,依业务而定),初始化为1 private int mIndex; - +// 存储该任务列表包含的所有子任务,以ArrayList形式存储,初始化为一个空的ArrayList private ArrayList mChildren; - +// 构造函数,调用父类(Node类)的构造函数进行初始化,并对TaskList类特有的属性进行默认初始化,创建一个空的任务列表对象并设置初始索引值 + public TaskList() { super(); mChildren = new ArrayList(); mIndex = 1; } - +// 实现抽象类Node中定义的抽象方法,用于获取创建任务列表的操作对应的JSONObject,该JSON对象包含了创建任务列表所需的各种信息, + // 如操作类型、操作的唯一标识符、任务列表的索引位置、任务列表实体的基本属性(名称、创建者ID、实体类型等),按照特定的格式进行组装 public JSONObject getCreateAction(int actionId) { JSONObject js = new JSONObject(); - try { + try { + // 设置操作类型为创建任务列表,使用预定义的字符串常量标识 // action_type js.put(GTaskStringUtils.GTASK_JSON_ACTION_TYPE, GTaskStringUtils.GTASK_JSON_ACTION_TYPE_CREATE); - + // 设置操作的唯一标识符(具体业务中用于区分不同的创建操作实例) // action_id js.put(GTaskStringUtils.GTASK_JSON_ACTION_ID, actionId); - +// 设置任务列表的索引位置 // index js.put(GTaskStringUtils.GTASK_JSON_INDEX, mIndex); - +// 构建表示任务列表实体的详细信息的JSONObject,包含任务列表名称、创建者ID(此处设为"null",可能需根据实际情况调整)、 + // 实体类型(标识为组类型,表示任务列表在整体结构中的类型为组)等信息 // entity_delta JSONObject entity = new JSONObject(); entity.put(GTaskStringUtils.GTASK_JSON_NAME, getName()); @@ -73,21 +79,24 @@ public class TaskList extends Node { return js; } +// 实现抽象类Node中定义的抽象方法,用于获取更新任务列表的操作对应的JSONObject,包含了更新任务列表所需的关键信息, + // 如操作类型、操作的唯一标识符、任务列表的唯一标识符、任务列表实体的更新内容(如名称、是否删除等信息),按照特定格式组装JSON数据 public JSONObject getUpdateAction(int actionId) { JSONObject js = new JSONObject(); try { + // 设置操作类型为更新任务列表,使用预定义的字符串常量标识 // action_type js.put(GTaskStringUtils.GTASK_JSON_ACTION_TYPE, GTaskStringUtils.GTASK_JSON_ACTION_TYPE_UPDATE); - + // 设置操作的唯一标识符 // action_id js.put(GTaskStringUtils.GTASK_JSON_ACTION_ID, actionId); - + // 设置任务列表的全局唯一标识符(用于定位要更新的具体任务列表) // id js.put(GTaskStringUtils.GTASK_JSON_ID, getGid()); - +// 构建表示任务列表实体更新内容的JSONObject,包含任务列表名称以及任务列表是否已删除的标记等信息 // entity_delta JSONObject entity = new JSONObject(); entity.put(GTaskStringUtils.GTASK_JSON_NAME, getName()); @@ -101,21 +110,24 @@ public class TaskList extends Node { } return js; - } + } // 实现抽象类Node中定义的抽象方法,用于根据远程传来的JSONObject数据设置任务列表的内容, + // 从JSON对象中解析出任务列表的各个属性值(如ID、最后修改时间、名称等)并设置到当前任务列表对象中, + // 如果解析过程出现JSONException异常,则记录错误日志并抛出相应异常 public void setContentByRemoteJSON(JSONObject js) { if (js != null) { try { + // 从JSON对象中获取任务列表的全局唯一标识符(Gid)并设置到当前任务列表对象中 // id if (js.has(GTaskStringUtils.GTASK_JSON_ID)) { setGid(js.getString(GTaskStringUtils.GTASK_JSON_ID)); } - +// 从JSON对象中获取任务列表最后一次修改的时间戳并设置到当前任务列表对象中 // last_modified if (js.has(GTaskStringUtils.GTASK_JSON_LAST_MODIFIED)) { setLastModified(js.getLong(GTaskStringUtils.GTASK_JSON_LAST_MODIFIED)); } - +// 从JSON对象中获取任务列表的名称并设置到当前任务列表对象中 // name if (js.has(GTaskStringUtils.GTASK_JSON_NAME)) { setName(js.getString(GTaskStringUtils.GTASK_JSON_NAME)); @@ -128,7 +140,12 @@ public class TaskList extends Node { } } } - + // 实现抽象类Node中定义的抽象方法,用于根据本地的JSONObject数据设置任务列表的内容, + // 首先进行一些基本的校验(判断JSON对象是否可用,是否包含必要的头部信息等),然后根据笔记类型进行不同的处理: + // 如果是文件夹类型,从JSON中获取摘要信息并设置任务列表名称(添加特定前缀); + // 如果是系统类型,根据特定的系统文件夹ID设置对应的名称; + // 如果类型不符合预期,则记录错误日志, + // 如果解析过程出现JSONException异常,则记录错误日志并打印异常堆栈信息 public void setContentByLocalJSON(JSONObject js) { if (js == null || !js.has(GTaskStringUtils.META_HEAD_NOTE)) { Log.w(TAG, "setContentByLocalJSON: nothing is avaiable"); @@ -156,7 +173,9 @@ public class TaskList extends Node { e.printStackTrace(); } } - + // 实现抽象类Node中定义的抽象方法,用于从任务列表当前内容获取对应的本地JSON表示形式, + // 按照特定格式构建包含任务列表名称和类型信息的JSON对象,根据名称是否包含特定前缀来判断并设置类型(文件夹类型或系统类型), + // 如果构建JSON对象过程中出现JSONException异常,则记录错误日志并返回null public JSONObject getLocalJSONFromContent() { try { JSONObject js = new JSONObject(); @@ -182,28 +201,37 @@ public class TaskList extends Node { return null; } } + // 实现抽象类Node中定义的抽象方法,根据给定的Cursor(通常用于从数据库查询结果中获取数据)来确定任务列表的同步操作类型, + // 通过判断本地是否有更新、任务列表的Gtask ID是否匹配以及两边的修改时间戳是否一致等条件,来确定是无同步操作、更新本地、更新远程还是出现错误等同步动作, + // 如果在判断过程中出现异常,则记录错误日志并返回表示错误的同步操作类型,对于文件夹冲突的情况,默认应用本地修改(返回更新远程操作类型) public int getSyncAction(Cursor c) { try { if (c.getInt(SqlNote.LOCAL_MODIFIED_COLUMN) == 0) { + // 本地没有更新(通过判断本地修改标记是否为0来确定) // there is no local update if (c.getLong(SqlNote.SYNC_ID_COLUMN) == getLastModified()) { + // 两边都没有更新(本地同步ID与任务列表最后修改时间戳相同) // no update both side return SYNC_ACTION_NONE; } else { + // 应用远程到本地(本地没有更新但远程有更新,需将远程数据应用到本地) // apply remote to local return SYNC_ACTION_UPDATE_LOCAL; } } else { + // 验证任务列表的Gtask ID是否匹配(本地数据库中的Gtask ID与当前任务列表对象的Gid进行比较) // validate gtask id if (!c.getString(SqlNote.GTASK_ID_COLUMN).equals(getGid())) { Log.e(TAG, "gtask id doesn't match"); return SYNC_ACTION_ERROR; } if (c.getLong(SqlNote.SYNC_ID_COLUMN) == getLastModified()) { + // 只有本地修改(本地修改了但远程没有更新,只需将本地修改同步到远程) // local modification only return SYNC_ACTION_UPDATE_REMOTE; } else { + // 对于文件夹冲突的情况,直接应用本地修改 // for folder conflicts, just apply local modification return SYNC_ACTION_UPDATE_REMOTE; } @@ -215,16 +243,19 @@ public class TaskList extends Node { return SYNC_ACTION_ERROR; } - +// 获取任务列表中子任务的数量的方法,直接返回存储子任务的ArrayList的大小 public int getChildTaskCount() { return mChildren.size(); } - + // 向任务列表中添加子任务的方法,将传入的任务对象添加到子任务列表中, + // 如果任务对象不为空且不在子任务列表中,则添加成功后,设置该任务的前一个兄弟任务(如果列表为空则为null,否则为列表中最后一个任务)以及父任务(当前任务列表), + // 返回添加操作是否成功的布尔值 public boolean addChildTask(Task task) { boolean ret = false; if (task != null && !mChildren.contains(task)) { ret = mChildren.add(task); if (ret) { + // 需要设置前一个兄弟任务和父任务 // need to set prior sibling and parent task.setPriorSibling(mChildren.isEmpty() ? null : mChildren .get(mChildren.size() - 1)); @@ -233,7 +264,11 @@ public class TaskList extends Node { } return ret; } - + // 在指定索引位置向任务列表中添加子任务的方法, + // 首先进行索引合法性校验,如果索引无效则记录错误日志并返回false; + // 然后判断任务是否已在列表中,如果不在且任务对象不为空,则将任务添加到指定索引位置, + // 接着更新任务列表中相关任务的前一个兄弟任务关系(设置新添加任务的前一个兄弟任务以及后续任务的前一个兄弟任务指向新添加任务), + // 返回添加操作是否成功的布尔值 public boolean addChildTask(Task task, int index) { if (index < 0 || index > mChildren.size()) { Log.e(TAG, "add child task: invalid index"); @@ -243,7 +278,7 @@ public class TaskList extends Node { int pos = mChildren.indexOf(task); if (task != null && pos == -1) { mChildren.add(index, task); - +// 更新任务列表 // update the task list Task preTask = null; Task afterTask = null; @@ -259,7 +294,10 @@ public class TaskList extends Node { return true; } - +// 从任务列表中移除指定子任务的方法, + // 首先查找任务在列表中的索引位置,如果存在则尝试移除该任务,移除成功后,重置该任务的前一个兄弟任务和父任务为null, + // 并且更新任务列表中后续任务的前一个兄弟任务关系(如果移除位置不是列表末尾), + // 返回移除操作是否成功的布尔值 public boolean removeChildTask(Task task) { boolean ret = false; int index = mChildren.indexOf(task); @@ -267,10 +305,11 @@ public class TaskList extends Node { ret = mChildren.remove(task); if (ret) { + // 重置前一个兄弟任务和父任务 // reset prior sibling and parent task.setPriorSibling(null); task.setParent(null); - + // 更新任务列表 // update the task list if (index != mChildren.size()) { mChildren.get(index).setPriorSibling( @@ -280,6 +319,9 @@ public class TaskList extends Node { } return ret; } + // 在任务列表中移动指定子任务到新的索引位置的方法, + // 首先进行索引合法性校验以及判断任务是否在列表中,如果校验不通过则记录错误日志并返回false; + // 如果任务当前位置和目标位置相同则直接返回true,否则先移除该任务再添加到新的索引位置,返回操作是否成功的布尔值 public boolean moveChildTask(Task task, int index) { @@ -298,6 +340,7 @@ public class TaskList extends Node { return true; return (removeChildTask(task) && addChildTask(task, index)); } + // 根据任务的全局唯一标识符(Gid)查找子任务的方法,遍历子任务列表,找到Gid匹配的任务并返回,如果没找到则返回null public Task findChildTaskByGid(String gid) { for (int i = 0; i < mChildren.size(); i++) { @@ -308,10 +351,12 @@ public class TaskList extends Node { } return null; } +// 获取指定子任务在任务列表中的索引位置的方法,通过调用ArrayList的indexOf方法来查找并返回任务在列表中的索引,若不存在则返回 -1 public int getChildTaskIndex(Task task) { return mChildren.indexOf(task); } + // 根据索引获取子任务的方法,先对索引进行合法性校验,如果索引无效则记录错误日志并返回null,否则返回对应索引位置的子任务 public Task getChildTaskByIndex(int index) { if (index < 0 || index >= mChildren.size()) { @@ -320,7 +365,8 @@ public class TaskList extends Node { } return mChildren.get(index); } - +// 根据任务的全局唯一标识符(Gid)获取子任务的方法(与findChildTaskByGid方法功能类似,可能是重复定义或者不同使用场景下的同名方法), + // 遍历子任务列表,找到Gid匹配的任务并返回,如果没找到则返回null public Task getChilTaskByGid(String gid) { for (Task task : mChildren) { if (task.getGid().equals(gid)) @@ -328,15 +374,16 @@ public class TaskList extends Node { } return null; } +// 获取整个子任务列表的方法,直接返回存储子任务的ArrayList对象,外部可通过该方法获取任务列表包含的所有子任务进行进一步操作 public ArrayList getChildTaskList() { return this.mChildren; } - + // 设置任务列表索引的方法,用于更新任务列表的索引值(可能用于改变其在相关结构中的顺序、位置等属性) public void setIndex(int index) { this.mIndex = index; } - + // 获取任务列表索引的方法,用于获取当前任务列表的索引值 public int getIndex() { return this.mIndex; } diff --git a/src/Notes-master/src/net/micode/notes/gtask/exception/ActionFailureException.java b/src/Notes-master/src/net/micode/notes/gtask/exception/ActionFailureException.java index 15504be..bc69af7 100644 --- a/src/Notes-master/src/net/micode/notes/gtask/exception/ActionFailureException.java +++ b/src/Notes-master/src/net/micode/notes/gtask/exception/ActionFailureException.java @@ -15,18 +15,25 @@ */ package net.micode.notes.gtask.exception; - +// ActionFailureException类继承自RuntimeException,意味着它是一个运行时异常,不需要在方法签名中显式声明抛出该异常(与受检异常相对) +// 此类用于表示在执行某个操作时发生了失败的情况,方便在代码中针对操作失败进行统一的异常处理和错误信息传递 public class ActionFailureException extends RuntimeException { + // 用于序列化版本控制的唯一标识符,在Java的序列化机制中使用,保证不同版本的类在序列化和反序列化时的兼容性 private static final long serialVersionUID = 4425249765923293627L; - + // 无参构造函数,调用父类(RuntimeException)的无参构造函数,用于创建一个默认的ActionFailureException实例, + // 通常在不需要传递具体错误信息时使用 public ActionFailureException() { super(); } +// 带有字符串参数的构造函数,接收一个表示错误信息的字符串参数,调用父类(RuntimeException)的对应构造函数, + // 用于创建一个带有特定错误信息的ActionFailureException实例,方便在出现操作失败时传递相应的错误提示内容 public ActionFailureException(String paramString) { super(paramString); } - +// 带有字符串参数和Throwable参数的构造函数,接收一个表示错误信息的字符串参数以及一个Throwable对象(通常是导致当前异常的底层异常), + // 调用父类(RuntimeException)的对应构造函数,用于创建一个既能传递自定义错误信息,又能关联底层引发异常原因的ActionFailureException实例, + // 方便在复杂的异常链场景中进行准确的异常追踪和错误分析 public ActionFailureException(String paramString, Throwable paramThrowable) { super(paramString, paramThrowable); } diff --git a/src/Notes-master/src/net/micode/notes/gtask/exception/NetworkFailureException.java b/src/Notes-master/src/net/micode/notes/gtask/exception/NetworkFailureException.java index b08cfb1..5a83b04 100644 --- a/src/Notes-master/src/net/micode/notes/gtask/exception/NetworkFailureException.java +++ b/src/Notes-master/src/net/micode/notes/gtask/exception/NetworkFailureException.java @@ -15,18 +15,29 @@ */ package net.micode.notes.gtask.exception; +// NetworkFailureException类继承自Exception,属于受检异常(Checked Exception),这意味着在方法中如果可能抛出该异常, +// 必须在方法签名中使用throws关键字显式声明,调用该方法的代码也需要进行相应的异常处理(try-catch块或者继续向上抛出)。 +// 此类专门用于表示在网络相关操作出现故障、失败时抛出的异常情况,方便对网络故障进行统一的异常处理和错误信息传递。 public class NetworkFailureException extends Exception { + // 用于序列化版本控制的唯一标识符,在Java的序列化机制中发挥作用。当类实现了Serializable接口(Exception类间接实现了该接口), + // 这个标识符可以确保不同版本的类在进行序列化和反序列化操作时能够正确匹配,避免因类结构变化等原因导致的兼容性问题。 private static final long serialVersionUID = 2107610287180234136L; - + // 无参构造函数,调用父类(Exception)的无参构造函数,用于创建一个默认的NetworkFailureException实例, + // 一般在不需要传递具体错误信息,仅表示网络出现故障这种通用情况时使用。 public NetworkFailureException() { super(); } - +// 带有字符串参数的构造函数,接收一个表示错误信息的字符串参数,调用父类(Exception)的对应构造函数, + // 这样就可以创建一个带有明确错误提示内容的NetworkFailureException实例,便于在网络操作失败时, + // 根据具体的失败原因传递相应有意义的错误信息给调用者或者进行日志记录等操作。 public NetworkFailureException(String paramString) { super(paramString); } - +// 带有字符串参数和Throwable参数的构造函数,接收一个表示错误信息的字符串参数以及一个Throwable对象(通常是导致当前网络故障异常的底层异常, + // 比如IOException等更具体的网络相关底层异常),调用父类(Exception)的对应构造函数, + // 此构造函数用于创建一个既能传递自定义的网络故障相关错误信息,又能关联底层引发异常原因的NetworkFailureException实例, + // 在复杂的网络异常处理场景中,方便进行准确的异常追踪、错误分析以及更完善的异常处理逻辑编写。 public NetworkFailureException(String paramString, Throwable paramThrowable) { super(paramString, paramThrowable); } diff --git a/src/Notes-master/src/net/micode/notes/gtask/remote/GTaskASyncTask.java b/src/Notes-master/src/net/micode/notes/gtask/remote/GTaskASyncTask.java index b3b61e7..4ad596f 100644 --- a/src/Notes-master/src/net/micode/notes/gtask/remote/GTaskASyncTask.java +++ b/src/Notes-master/src/net/micode/notes/gtask/remote/GTaskASyncTask.java @@ -27,24 +27,33 @@ import android.os.AsyncTask; import net.micode.notes.R; import net.micode.notes.ui.NotesListActivity; import net.micode.notes.ui.NotesPreferenceActivity; +// GTaskASyncTask类继承自AsyncTask,用于在后台线程执行一些耗时的操作(通常与远程任务同步相关),并能在操作过程中更新进度、在操作完成后处理结果, +// 同时通过接口回调的方式通知外部操作已完成,还涉及到与通知系统的交互来展示同步状态相关的通知信息 public class GTaskASyncTask extends AsyncTask { +// 定义一个静态的整型常量,作为同步相关通知的唯一标识符(ID),用于在通知系统中区分不同的通知,此处固定赋值为5234235 private static int GTASK_SYNC_NOTIFICATION_ID = 5234235; +// 定义一个内部接口OnCompleteListener,用于定义当异步任务完成时需要执行的回调方法,外部类实现该接口来处理任务完成后的逻辑 public interface OnCompleteListener { void onComplete(); } +// 存储上下文对象,用于获取系统服务、资源等,方便在类中进行各种与Android系统相关的操作,如创建通知、启动Activity等 private Context mContext; - +// 用于管理通知的显示、取消等操作,通过获取系统的通知服务来实例化,负责向用户展示同步进度、结果等相关通知信息 + private NotificationManager mNotifiManager; - +// 用于管理远程任务相关的操作(可能涉及与服务器交互、任务同步等具体逻辑,具体功能由GTaskManager类实现),通过单例模式获取实例 + private GTaskManager mTaskManager; - + // 存储实现了OnCompleteListener接口的对象引用,用于在异步任务完成时回调相应的方法,通知外部任务已结束 + private OnCompleteListener mOnCompleteListener; - +// 构造函数,接收上下文对象和OnCompleteListener接口实现对象作为参数,用于初始化类中的相关成员变量, + // 同时获取通知管理器实例,并获取GTaskManager的单例实例,为后续操作做准备 public GTaskASyncTask(Context context, OnCompleteListener listener) { mContext = context; mOnCompleteListener = listener; @@ -52,17 +61,21 @@ public class GTaskASyncTask extends AsyncTask { .getSystemService(Context.NOTIFICATION_SERVICE); mTaskManager = GTaskManager.getInstance(); } - + // 用于取消正在进行的同步操作的方法,通过调用GTaskManager中的取消同步方法来实现具体的取消逻辑,外部可调用此方法来中断同步过程 + public void cancelSync() { mTaskManager.cancelSync(); } - +// 用于发布进度信息的方法,接收一个字符串消息参数,将其包装成字符串数组后调用AsyncTask的publishProgress方法, + // 触发onProgressUpdate方法的执行,从而实现向UI线程传递进度信息,以便更新界面展示等相关操作 public void publishProgess(String message) { publishProgress(new String[] { message }); } - + // 私有方法,用于显示通知信息,根据传入的通知文本资源ID(tickerId)和通知具体内容(content)创建并配置一个Notification对象, + // 然后通过通知管理器将通知显示出来,设置了通知的图标、默认灯光效果、自动取消等属性,并根据不同的文本资源ID设置点击通知后的跳转意图(PendingIntent) + private void showNotification(int tickerId, String content) { Notification notification = new Notification(R.drawable.notification, mContext .getString(tickerId), System.currentTimeMillis()); @@ -81,14 +94,18 @@ public class GTaskASyncTask extends AsyncTask { pendingIntent); mNotifiManager.notify(GTASK_SYNC_NOTIFICATION_ID, notification); } - +// 在后台线程执行的方法,重写了AsyncTask的抽象方法,是异步任务的核心逻辑所在。 + // 首先发布一条表示正在登录进行同步的进度消息(包含同步账号名称),然后调用GTaskManager的sync方法进行实际的同步操作, + // 并返回同步结果(以整数形式表示不同的状态,如成功、网络错误等状态码),该结果将传递给onPostExecute方法进行后续处理 @Override protected Integer doInBackground(Void... unused) { publishProgess(mContext.getString(R.string.sync_progress_login, NotesPreferenceActivity .getSyncAccountName(mContext))); return mTaskManager.sync(mContext, this); } - +// 在UI线程执行的方法,用于接收并处理在后台线程通过publishProgress方法传递过来的进度信息, + // 调用showNotification方法展示同步进度通知,并且如果当前上下文是GTaskSyncService类型,还会发送广播(可能用于通知其他组件同步进度情况) + @Override protected void onProgressUpdate(String... progress) { showNotification(R.string.ticker_syncing, progress[0]); @@ -96,7 +113,11 @@ public class GTaskASyncTask extends AsyncTask { ((GTaskSyncService) mContext).sendBroadcast(progress[0]); } } - +// 在UI线程执行的方法,当异步任务执行完毕后(无论是正常完成还是出现异常结束),根据返回的结果状态码进行不同的处理: + // 如果结果是成功状态,展示成功同步的通知信息,并记录最后同步时间; + // 如果是网络错误状态、内部错误状态或同步取消状态,分别展示对应的错误通知信息; + // 最后,如果存在实现了OnCompleteListener接口的对象(即外部注册了任务完成回调),则通过开启一个新线程来执行回调方法,通知外部任务已完成 + @Override protected void onPostExecute(Integer result) { if (result == GTaskManager.STATE_SUCCESS) { diff --git a/src/Notes-master/src/net/micode/notes/gtask/remote/GTaskClient.java b/src/Notes-master/src/net/micode/notes/gtask/remote/GTaskClient.java index c67dfdf..ce10c61 100644 --- a/src/Notes-master/src/net/micode/notes/gtask/remote/GTaskClient.java +++ b/src/Notes-master/src/net/micode/notes/gtask/remote/GTaskClient.java @@ -59,37 +59,40 @@ import java.util.List; import java.util.zip.GZIPInputStream; import java.util.zip.Inflater; import java.util.zip.InflaterInputStream; - +// GTaskClient类用于与Google Tasks等相关服务进行交互,实现诸如登录、创建任务、创建任务列表、更新任务、获取任务列表等功能, +// 内部管理着与服务器通信的HttpClient对象、登录状态、账户信息等,是应用与远程任务服务交互的核心类之一 public class GTaskClient { + // 用于日志记录的标签,取当前类的简单名称 private static final String TAG = GTaskClient.class.getSimpleName(); + // Google Tasks服务的基础URL private static final String GTASK_URL = "https://mail.google.com/tasks/"; - +// 用于获取任务相关数据的URL(可能是获取初始数据等情况) private static final String GTASK_GET_URL = "https://mail.google.com/tasks/ig"; - + // 用于向服务器提交任务相关操作(如创建、更新等)的URL private static final String GTASK_POST_URL = "https://mail.google.com/tasks/r/ig"; - +// 单例模式,存储唯一的GTaskClient实例,初始化为null private static GTaskClient mInstance = null; - + // 用于与服务器进行HTTP通信的HttpClient对象,初始化为null,后续根据需要进行初始化和配置 private DefaultHttpClient mHttpClient; - + // 当前用于获取数据的URL,初始化为GTASK_GET_URL,可能会根据登录情况(如自定义域名等)进行调整 private String mGetUrl; - + // 当前用于提交数据的URL,初始化为GTASK_POST_URL,可能会根据登录情况(如自定义域名等)进行调整 private String mPostUrl; - +// 客户端版本号,初始化为 -1,登录成功后会从服务器响应中获取实际版本号 private long mClientVersion; - +// 标记用户是否已登录,初始化为false private boolean mLoggedin; - + // 记录上次登录的时间戳,初始化为0 private long mLastLoginTime; - + // 用于为每个操作生成唯一的标识符(每次操作后自增),初始化为1 private int mActionId; - + // 存储当前登录的账户信息,初始化为null private Account mAccount; - + // 用于存储待提交的更新操作对应的JSON数组(多个更新操作可批量提交),初始化为null private JSONArray mUpdateArray; - +// 私有构造函数,用于初始化GTaskClient类的各个成员变量,采用单例模式,限制外部直接实例化该类 private GTaskClient() { mHttpClient = null; mGetUrl = GTASK_GET_URL; @@ -101,41 +104,44 @@ public class GTaskClient { mAccount = null; mUpdateArray = null; } - + // 静态方法,用于获取GTaskClient的单例实例,如果实例不存在则创建一个新的实例,保证整个应用中只有一个GTaskClient对象在使用 + public static synchronized GTaskClient getInstance() { if (mInstance == null) { mInstance = new GTaskClient(); } return mInstance; } - +// 用于登录到Google Tasks服务的方法,根据一些条件判断是否需要重新登录(如登录间隔超过5分钟、账户切换等情况), + // 先获取Google账户的认证令牌,然后根据账户域名情况尝试不同的登录方式(自定义域名或官方URL),最后返回登录是否成功的布尔值 + public boolean login(Activity activity) { + // 假设Cookie在5分钟后过期,超过此时间间隔则需要重新登录 // we suppose that the cookie would expire after 5 minutes // then we need to re-login final long interval = 1000 * 60 * 5; if (mLastLoginTime + interval < System.currentTimeMillis()) { mLoggedin = false; } - + // 在账户切换后需要重新登录 // need to re-login after account switch if (mLoggedin && !TextUtils.equals(getSyncAccount().name, NotesPreferenceActivity .getSyncAccountName(activity))) { mLoggedin = false; } - if (mLoggedin) { Log.d(TAG, "already logged in"); return true; } - + mLastLoginTime = System.currentTimeMillis(); String authToken = loginGoogleAccount(activity, false); if (authToken == null) { Log.e(TAG, "login google account failed"); return false; } - + // 如果账户名不是以gmail.com或googlemail.com结尾,尝试使用自定义域名登录 // login with custom domain if necessary if (!(mAccount.name.toLowerCase().endsWith("gmail.com") || mAccount.name.toLowerCase() .endsWith("googlemail.com"))) { @@ -150,7 +156,7 @@ public class GTaskClient { mLoggedin = true; } } - + // 如果使用自定义域名登录失败,尝试使用Google官方URL登录 // try to login with google official url if (!mLoggedin) { mGetUrl = GTASK_GET_URL; @@ -163,7 +169,10 @@ public class GTaskClient { mLoggedin = true; return true; } - + // 用于获取Google账户的认证令牌的私有方法,通过AccountManager获取所有Google类型的账户, + // 根据应用设置中指定的同步账户名称查找对应的账户,然后向AccountManager请求获取认证令牌, + // 如果获取失败则返回null,若需要使令牌失效(invalidateToken为true时),则先失效令牌再重新获取 + private String loginGoogleAccount(Activity activity, boolean invalidateToken) { String authToken; AccountManager accountManager = AccountManager.get(activity); @@ -188,7 +197,7 @@ public class GTaskClient { Log.e(TAG, "unable to get an account with the same name in the settings"); return null; } - +// 获取令牌 // get the token now AccountManagerFuture accountManagerFuture = accountManager.getAuthToken(account, "goanna_mobile", null, activity, null, null); @@ -206,9 +215,12 @@ public class GTaskClient { return authToken; } - +// 尝试登录到Google Tasks服务的私有方法,先调用loginGtask方法进行登录,如果登录失败(可能是认证令牌过期等原因), + // 则使令牌失效后重新获取并再次尝试登录,返回最终登录是否成功的布尔值 + private boolean tryToLoginGtask(Activity activity, String authToken) { if (!loginGtask(authToken)) { + // 可能认证令牌已过期,现在使令牌失效并再次尝试 // maybe the auth token is out of date, now let's invalidate the // token and try again authToken = loginGoogleAccount(activity, true); @@ -224,7 +236,10 @@ public class GTaskClient { } return true; } - + // 实际执行登录到Google Tasks服务的私有方法,配置HttpClient的连接超时、读取超时等参数,设置Cookie存储, + // 禁用Expect-Continue机制,然后向服务器发送带有认证令牌的GET请求进行登录,从响应中获取Cookie判断是否登录成功, + // 同时从响应内容中解析出客户端版本号,若在过程中出现JSONException或其他异常则返回false表示登录失败 + private boolean loginGtask(String authToken) { int timeoutConnection = 10000; int timeoutSocket = 15000; @@ -235,14 +250,14 @@ public class GTaskClient { BasicCookieStore localBasicCookieStore = new BasicCookieStore(); mHttpClient.setCookieStore(localBasicCookieStore); HttpProtocolParams.setUseExpectContinue(mHttpClient.getParams(), false); - + // 登录Google Tasks // login gtask try { String loginUrl = mGetUrl + "?auth=" + authToken; HttpGet httpGet = new HttpGet(loginUrl); HttpResponse response = null; response = mHttpClient.execute(httpGet); - + // 获取Cookie // get the cookie now List cookies = mHttpClient.getCookieStore().getCookies(); boolean hasAuthCookie = false; @@ -254,7 +269,7 @@ public class GTaskClient { if (!hasAuthCookie) { Log.w(TAG, "it seems that there is no auth cookie"); } - +// 获取客户端版本号 // get the client version String resString = getResponseContent(response.getEntity()); String jsBegin = "_setup("; @@ -272,6 +287,7 @@ public class GTaskClient { e.printStackTrace(); return false; } catch (Exception e) { + // 简单捕获所有异常 // simply catch all exceptions Log.e(TAG, "httpget gtask_url failed"); return false; @@ -279,18 +295,20 @@ public class GTaskClient { return true; } - + // 获取下一个操作的唯一标识符(每次调用自增)的私有方法,用于区分不同的操作请求 private int getActionId() { return mActionId++; } - + // 创建一个用于POST请求的HttpPost对象的私有方法,设置请求头的Content-Type和AT字段,返回配置好的HttpPost对象, + // 方便后续设置请求体并发送POST请求 private HttpPost createHttpPost() { HttpPost httpPost = new HttpPost(mPostUrl); httpPost.setHeader("Content-Type", "application/x-www-form-urlencoded;charset=utf-8"); httpPost.setHeader("AT", "1"); return httpPost; } - +// 从HttpEntity中获取响应内容的私有方法,根据响应内容的编码方式(如gzip、deflate等)对输入流进行相应的解压处理, + // 然后逐行读取内容并拼接成字符串返回,最后关闭输入流,若在读取过程中出现IOException则抛出异常 private String getResponseContent(HttpEntity entity) throws IOException { String contentEncoding = null; if (entity.getContentEncoding() != null) { @@ -322,7 +340,10 @@ public class GTaskClient { input.close(); } } - + // 向服务器发送POST请求并处理响应的私有方法,首先检查是否已登录,若未登录则抛出异常,然后创建HttpPost对象, + // 设置请求体(将传入的JSONObject转换为表单数据),发送请求并获取响应内容,将响应内容转换为JSONObject返回, + // 在过程中若出现ClientProtocolException、IOException、JSONException等异常则分别抛出对应的异常表示请求失败 + private JSONObject postRequest(JSONObject js) throws NetworkFailureException { if (!mLoggedin) { Log.e(TAG, "please login first"); @@ -335,7 +356,7 @@ public class GTaskClient { list.add(new BasicNameValuePair("r", js.toString())); UrlEncodedFormEntity entity = new UrlEncodedFormEntity(list, "UTF-8"); httpPost.setEntity(entity); - + // 执行POST请求 // execute the post HttpResponse response = mHttpClient.execute(httpPost); String jsString = getResponseContent(response.getEntity()); @@ -359,7 +380,9 @@ public class GTaskClient { throw new ActionFailureException("error occurs when posting request"); } } - + // 创建任务的方法,首先调用commitUpdate方法提交之前可能积累的更新操作,然后构建包含创建任务操作的JSON数据, + // 设置客户端版本号等信息,向服务器发送POST请求,从响应中获取新创建任务的全局唯一标识符(Gid)并设置到任务对象中, + // 若在处理JSON数据过程中出现异常则抛出相应异常表示创建任务失败 public void createTask(Task task) throws NetworkFailureException { commitUpdate(); try { @@ -385,7 +408,8 @@ public class GTaskClient { throw new ActionFailureException("create task: handing jsonobject failed"); } } - + // 创建任务列表的方法,与创建任务的逻辑类似,先提交已有更新,构建包含创建任务列表操作的JSON数据,发送POST请求, + // 从响应中获取新创建任务列表的Gid并设置到任务列表对象中,若处理JSON出现问题则抛出异常表示创建失败 public void createTaskList(TaskList tasklist) throws NetworkFailureException { commitUpdate(); try { @@ -411,7 +435,9 @@ public class GTaskClient { throw new ActionFailureException("create tasklist: handing jsonobject failed"); } } - + // 提交更新操作的方法,若存在待提交的更新操作数组(mUpdateArray不为null),则构建包含更新操作列表和客户端版本号的JSON数据, + // 发送POST请求将更新提交到服务器,之后将更新操作数组置为null,表示已提交,若处理JSON过程中出现异常则抛出相应异常表示提交失败 + public void commitUpdate() throws NetworkFailureException { if (mUpdateArray != null) { try { @@ -432,7 +458,9 @@ public class GTaskClient { } } } - +// 添加待更新节点(任务、任务列表等)操作到更新操作数组的方法,首先判断节点是否为空,若不为空且更新操作数组长度超过10个(避免过多更新导致错误), + // 则先提交已有的更新操作,若更新操作数组为空则创建一个新的JSONArray,然后将节点的更新操作对应的JSON数据添加到数组中,用于后续批量提交更新 + public void addUpdateNode(Node node) throws NetworkFailureException { if (node != null) { // too many update items may result in an error @@ -446,7 +474,9 @@ public class GTaskClient { mUpdateArray.put(node.getUpdateAction(getActionId())); } } - + // 移动任务的方法,先提交已有更新操作,然后构建包含移动任务操作相关信息(如操作类型、任务ID、源任务列表、目标任务列表等)的JSON数据, + // 设置客户端版本号后发送POST请求到服务器,若处理JSON数据过程中出现异常则抛出相应异常表示移动任务失败 + public void moveTask(Task task, TaskList preParent, TaskList curParent) throws NetworkFailureException { commitUpdate(); @@ -485,7 +515,9 @@ public class GTaskClient { throw new ActionFailureException("move task: handing jsonobject failed"); } } - + // 删除节点(任务、任务列表等)的方法,先提交已有更新操作,将节点标记为已删除后构建包含删除操作的JSON数据,设置客户端版本号并发送POST请求到服务器, + // 提交成功后将更新操作数组置为null,若处理JSON过程中出现异常则抛出相应异常表示删除节点失败 + public void deleteNode(Node node) throws NetworkFailureException { commitUpdate(); try { @@ -508,7 +540,9 @@ public class GTaskClient { throw new ActionFailureException("delete node: handing jsonobject failed"); } } - + // 获取所有任务列表的方法,首先检查是否已登录,若未登录则抛出异常,然后发送GET请求到服务器获取任务列表相关数据, + // 从响应内容中解析出任务列表的JSON数组并返回,若在请求过程中出现ClientProtocolException、IOException、JSONException等异常则抛出相应异常表示获取失败 + public JSONArray getTaskLists() throws NetworkFailureException { if (!mLoggedin) { Log.e(TAG, "please login first"); @@ -546,7 +580,9 @@ public class GTaskClient { throw new ActionFailureException("get task lists: handing jasonobject failed"); } } - + // 获取指定任务列表(根据任务列表的Gid)的方法,先提交已有更新操作,然后构建包含获取指定任务列表操作相关信息的JSON数据,发送POST请求到服务器, + // 从响应中获取任务列表包含的任务的JSON数组并返回,若处理JSON过程中出现异常则抛出相应异常表示获取任务列表失败 + public JSONArray getTaskList(String listGid) throws NetworkFailureException { commitUpdate(); try { @@ -574,11 +610,12 @@ public class GTaskClient { throw new ActionFailureException("get task list: handing jsonobject failed"); } } - + // 获取当前同步账户的方法,直接返回存储的账户对象,外部可通过此方法获取当前登录用于同步的账户信息 public Account getSyncAccount() { return mAccount; } + // 重置更新操作数组的方法,将更新操作数组置为null,用于清空之前积累的待更新操作,通常在一些特定场景下(如重新开始一轮更新操作等)使用 public void resetUpdateArray() { mUpdateArray = null; } diff --git a/src/Notes-master/src/net/micode/notes/gtask/remote/GTaskManager.java b/src/Notes-master/src/net/micode/notes/gtask/remote/GTaskManager.java index d2b4082..626a6a3 100644 --- a/src/Notes-master/src/net/micode/notes/gtask/remote/GTaskManager.java +++ b/src/Notes-master/src/net/micode/notes/gtask/remote/GTaskManager.java @@ -46,47 +46,59 @@ import java.util.HashMap; import java.util.HashSet; import java.util.Iterator; import java.util.Map; - - +// GTaskManager类用于管理与远程任务服务(如Google Tasks)的同步操作以及相关数据的处理,包括任务列表、任务、元数据等的同步逻辑, +// 通过协调本地数据和远程数据的差异来实现数据的一致性,是整个应用中任务同步功能的核心管理类,采用单例模式确保在应用内只有一个实例进行管理操作。 public class GTaskManager { + // 用于日志记录的标签,取当前类的简单名称 private static final String TAG = GTaskManager.class.getSimpleName(); - +// 表示同步成功的状态码 public static final int STATE_SUCCESS = 0; - + // 表示网络错误导致同步失败的状态码 public static final int STATE_NETWORK_ERROR = 1; - +// 表示内部逻辑错误导致同步失败的状态码 public static final int STATE_INTERNAL_ERROR = 2; - + // 表示同步正在进行中的状态码 public static final int STATE_SYNC_IN_PROGRESS = 3; - + // 表示同步被取消的状态码 public static final int STATE_SYNC_CANCELLED = 4; + // 单例模式,存储唯一的GTaskManager实例,初始化为null private static GTaskManager mInstance = null; - +// 用于获取认证令牌等相关操作的Activity对象,可在不同的同步相关操作中使用,例如登录时获取账户认证信息等 + private Activity mActivity; - + // 应用上下文对象,用于获取内容解析器(ContentResolver)等系统服务,方便与本地数据库等进行交互操作 + private Context mContext; - + // 用于与本地内容提供者(Content Provider)进行交互,实现对本地数据(如数据库中的笔记、任务等相关数据)的查询、更新等操作 private ContentResolver mContentResolver; - +// 标记当前是否正在进行同步操作,初始化为false,在同步开始时设置为true,结束时设置为false private boolean mSyncing; - +// 标记当前同步操作是否已被取消,初始化为false,可通过相应方法设置为true来取消正在进行的同步过程 private boolean mCancelled; + // 以任务列表的全局唯一标识符(Gid)为键,存储任务列表对象的哈希映射,用于缓存从远程获取的任务列表信息以及在同步过程中管理本地和远程任务列表的对应关系 private HashMap mGTaskListHashMap; - +// 以节点(任务、任务列表、元数据等都可视为节点,通过共同的父类Node进行抽象)的Gid为键,存储节点对象的哈希映射,方便在同步过程中快速查找和操作各个节点 + private HashMap mGTaskHashMap; - + // 以相关Gid为键,存储元数据(MetaData)对象的哈希映射,用于管理和跟踪与任务等相关的元数据信息,例如可能包含一些额外的描述、配置等数据 + private HashMap mMetaHashMap; - + // 用于存储特殊的元数据列表(可能是具有特定用途、结构的元数据集合),初始化为null,在同步过程中根据情况进行创建和填充 + private TaskList mMetaList; - +// 用于存储本地已删除的任务或数据对应的唯一标识符(可能是本地数据库中的主键等),通过哈希集合来避免重复记录,方便后续统一处理删除相关逻辑 + private HashSet mLocalDeleteIdMap; - + // 以节点的Gid为键,存储对应本地唯一标识符(如数据库中的记录ID)的哈希映射,用于建立远程节点Gid和本地数据ID之间的对应关系,便于同步时查找和关联数据 + private HashMap mGidToNid; - + // 以本地唯一标识符为键,存储对应节点Gid的哈希映射,与mGidToNid作用类似,是反向的映射关系,用于在不同的同步操作场景下根据需要进行查找和转换 + private HashMap mNidToGid; - +// 私有构造函数,用于初始化GTaskManager类的各个成员变量,采用单例模式,限制外部直接实例化该类,在这里初始化了各种用于存储数据和管理同步状态的集合、映射等变量。 + private GTaskManager() { mSyncing = false; mCancelled = false; @@ -98,6 +110,7 @@ public class GTaskManager { mGidToNid = new HashMap(); mNidToGid = new HashMap(); } + // 静态方法,用于获取GTaskManager的单例实例,如果实例不存在则创建一个新的实例,保证整个应用中只有一个GTaskManager对象在管理任务同步等相关操作 public static synchronized GTaskManager getInstance() { if (mInstance == null) { @@ -105,12 +118,16 @@ public class GTaskManager { } return mInstance; } - +// 用于设置关联的Activity上下文对象的方法,主要是为了后续在需要获取认证令牌等与Activity相关的操作时能够获取到相应的上下文环境, + // 该方法是同步的,以保证在多线程环境下设置上下文对象的操作的原子性和一致性。 public synchronized void setActivityContext(Activity activity) { // used for getting authtoken mActivity = activity; } - +// 执行同步操作的核心方法,负责协调本地数据和远程数据的同步过程,首先检查是否正在同步,如果正在同步则直接返回相应状态码, + // 然后初始化一系列用于同步的数据结构,进行登录操作(通过GTaskClient),获取远程任务列表,执行具体的内容同步逻辑(包括处理本地删除、新增、更新等不同情况的同步), + // 最后根据同步过程中的各种情况(如是否取消、是否出现异常等)返回对应的同步状态码,并且在finally块中清理相关的数据结构,重置同步状态。 + public int sync(Context context, GTaskASyncTask asyncTask) { if (mSyncing) { Log.d(TAG, "Sync is in progress"); @@ -130,18 +147,18 @@ public class GTaskManager { try { GTaskClient client = GTaskClient.getInstance(); client.resetUpdateArray(); - + // 登录Google任务服务 // login google task if (!mCancelled) { if (!client.login(mActivity)) { throw new NetworkFailureException("login google task failed"); } } - + // 获取Google任务列表 // get the task list from google asyncTask.publishProgess(mContext.getString(R.string.sync_progress_init_list)); initGTaskList(); - + // 执行内容同步工作 // do content sync work asyncTask.publishProgess(mContext.getString(R.string.sync_progress_syncing)); syncContent(); @@ -167,14 +184,16 @@ public class GTaskManager { return mCancelled ? STATE_SYNC_CANCELLED : STATE_SUCCESS; } - + // 初始化远程任务列表相关数据的私有方法,首先检查同步是否已取消,如果取消则直接返回,然后通过GTaskClient获取远程任务列表的JSON数据, + // 接着分别处理元数据列表(如果不存在则创建)以及普通任务列表,解析JSON数据创建对应的任务列表和任务对象,并建立相关的映射关系(如将任务列表、任务等存入对应的哈希映射中), + // 若在处理JSON数据过程中出现异常则抛出相应异常表示初始化任务列表失败。 private void initGTaskList() throws NetworkFailureException { if (mCancelled) return; GTaskClient client = GTaskClient.getInstance(); try { JSONArray jsTaskLists = client.getTaskLists(); - + // 首先初始化元数据列表 // init meta list first mMetaList = null; for (int i = 0; i < jsTaskLists.length(); i++) { @@ -186,7 +205,7 @@ public class GTaskManager { .equals(GTaskStringUtils.MIUI_FOLDER_PREFFIX + GTaskStringUtils.FOLDER_META)) { mMetaList = new TaskList(); mMetaList.setContentByRemoteJSON(object); - + // 加载元数据 // load meta data JSONArray jsMetas = client.getTaskList(gid); for (int j = 0; j < jsMetas.length(); j++) { @@ -202,7 +221,7 @@ public class GTaskManager { } } } - + // 如果元数据列表不存在则创建它 // create meta list if not existed if (mMetaList == null) { mMetaList = new TaskList(); @@ -210,7 +229,7 @@ public class GTaskManager { + GTaskStringUtils.FOLDER_META); GTaskClient.getInstance().createTaskList(mMetaList); } - + // 初始化任务列表 // init task list for (int i = 0; i < jsTaskLists.length(); i++) { JSONObject object = jsTaskLists.getJSONObject(i); @@ -224,7 +243,7 @@ public class GTaskManager { tasklist.setContentByRemoteJSON(object); mGTaskListHashMap.put(gid, tasklist); mGTaskHashMap.put(gid, tasklist); - + // 加载任务 // load tasks JSONArray jsTasks = client.getTaskList(gid); for (int j = 0; j < jsTasks.length(); j++) { @@ -246,7 +265,9 @@ public class GTaskManager { throw new ActionFailureException("initGTaskList: handing JSONObject failed"); } } - + // 执行具体内容同步的私有方法,负责协调本地和远程数据在内容层面的同步操作,包括处理本地已删除的笔记、文件夹同步、数据库中现有笔记的同步等多种情况, + // 在不同的同步场景下调用相应的辅助方法(如doContentSync)来执行具体的添加、删除、更新等操作,并且在合适的时机提交更新到远程(通过GTaskClient)以及清理本地删除记录等操作, + // 整个过程中会不断检查同步是否已被取消,若已取消则及时终止操作。 private void syncContent() throws NetworkFailureException { int syncType; Cursor c = null; @@ -258,7 +279,7 @@ public class GTaskManager { if (mCancelled) { return; } - + // 处理本地已删除的笔记 // for local deleted note try { c = mContentResolver.query(Notes.CONTENT_NOTE_URI, SqlNote.PROJECTION_NOTE, @@ -285,10 +306,10 @@ public class GTaskManager { c = null; } } - + // 同步文件夹 // sync folder first syncFolder(); - + // 处理数据库中已存在的笔记 // for note existing in database try { c = mContentResolver.query(Notes.CONTENT_NOTE_URI, SqlNote.PROJECTION_NOTE, @@ -306,9 +327,11 @@ public class GTaskManager { syncType = node.getSyncAction(c); } else { if (c.getString(SqlNote.GTASK_ID_COLUMN).trim().length() == 0) { + // 本地新增 // local add syncType = Node.SYNC_ACTION_ADD_REMOTE; } else { + // 远程已删除 // remote delete syncType = Node.SYNC_ACTION_DEL_LOCAL; } @@ -325,7 +348,7 @@ public class GTaskManager { c = null; } } - +// 处理剩余的项目(可能是本地新增但还未处理完全的等情况) // go through remaining items Iterator> iter = mGTaskHashMap.entrySet().iterator(); while (iter.hasNext()) { @@ -333,16 +356,16 @@ public class GTaskManager { node = entry.getValue(); doContentSync(Node.SYNC_ACTION_ADD_LOCAL, node, null); } - + // mCancelled可以被其他线程设置,所以我们需要逐个检查 // mCancelled can be set by another thread, so we neet to check one by - // one + // one // 清除本地删除表(如果同步未取消) // clear local delete table if (!mCancelled) { if (!DataUtils.batchDeleteNotes(mContentResolver, mLocalDeleteIdMap)) { throw new ActionFailureException("failed to batch-delete local deleted notes"); } } - + // 刷新本地同步ID(如果同步未取消) // refresh local sync id if (!mCancelled) { GTaskClient.getInstance().commitUpdate(); @@ -350,7 +373,9 @@ public class GTaskManager { } } - + // 专门用于文件夹同步的私有方法,处理各种不同类型的文件夹(如根文件夹、通话记录文件夹、本地已存在的文件夹以及远程新增的文件夹等)的同步逻辑, + // 在不同情况下判断文件夹在本地和远程的存在情况,调用相应的辅助方法(如doContentSync)来执行添加、更新等操作,并且在合适的时机提交更新到远程(通过GTaskClient), + // 整个过程中会不断检查同步是否已被取消,若已取消则及时终止操作。 private void syncFolder() throws NetworkFailureException { Cursor c = null; String gid; @@ -360,7 +385,7 @@ public class GTaskManager { if (mCancelled) { return; } - + // 处理根文件夹 // for root folder try { c = mContentResolver.query(ContentUris.withAppendedId(Notes.CONTENT_NOTE_URI, @@ -373,6 +398,7 @@ public class GTaskManager { mGTaskHashMap.remove(gid); mGidToNid.put(gid, (long) Notes.ID_ROOT_FOLDER); mNidToGid.put((long) Notes.ID_ROOT_FOLDER, gid); + // 对于系统文件夹,仅在必要时更新远程名称 // for system folder, only update remote name if necessary if (!node.getName().equals( GTaskStringUtils.MIUI_FOLDER_PREFFIX + GTaskStringUtils.FOLDER_DEFAULT)) @@ -389,7 +415,7 @@ public class GTaskManager { c = null; } } - + // 处理通话记录文件夹 // for call-note folder try { c = mContentResolver.query(Notes.CONTENT_NOTE_URI, SqlNote.PROJECTION_NOTE, "(_id=?)", @@ -404,6 +430,7 @@ public class GTaskManager { mGTaskHashMap.remove(gid); mGidToNid.put(gid, (long) Notes.ID_CALL_RECORD_FOLDER); mNidToGid.put((long) Notes.ID_CALL_RECORD_FOLDER, gid); + // 对于系统文件夹,仅在必要时更新远程名称 // for system folder, only update remote name if // necessary if (!node.getName().equals( @@ -423,7 +450,7 @@ public class GTaskManager { c = null; } } - + // 处理本地已存在的文件夹 // for local existing folders try { c = mContentResolver.query(Notes.CONTENT_NOTE_URI, SqlNote.PROJECTION_NOTE, @@ -441,9 +468,11 @@ public class GTaskManager { syncType = node.getSyncAction(c); } else { if (c.getString(SqlNote.GTASK_ID_COLUMN).trim().length() == 0) { + // 本地新增 // local add syncType = Node.SYNC_ACTION_ADD_REMOTE; } else { + // 远程已删除 // remote delete syncType = Node.SYNC_ACTION_DEL_LOCAL; } @@ -459,7 +488,7 @@ public class GTaskManager { c = null; } } - + // 处理远程新增的文件夹 // for remote add folders Iterator> iter = mGTaskListHashMap.entrySet().iterator(); while (iter.hasNext()) { @@ -475,7 +504,9 @@ public class GTaskManager { if (!mCancelled) GTaskClient.getInstance().commitUpdate(); } - + // 根据不同的同步操作类型执行具体同步动作的私有方法,通过一个switch语句根据传入的同步类型(syncType)来决定调用相应的添加、删除、更新等具体操作方法, + // 整个过程中会不断检查同步是否已被取消,若已取消则及时终止操作,若传入的同步类型不合法则抛出异常表示未知的同步操作类型。 + private void doContentSync(int syncType, Node node, Cursor c) throws NetworkFailureException { if (mCancelled) { return; @@ -510,6 +541,7 @@ public class GTaskManager { updateRemoteNode(node, c); break; case Node.SYNC_ACTION_UPDATE_CONFLICT: + // 合并双方的修改可能是个好主意,目前简单使用本地更新 // merging both modifications maybe a good idea // right now just use local update simply updateRemoteNode(node, c); @@ -521,7 +553,10 @@ public class GTaskManager { throw new ActionFailureException("unkown sync action type"); } } - +// 将本地节点添加到本地数据库以及处理相关同步逻辑的私有方法,首先判断同步是否已取消,如果取消则直接返回, + // 然后根据节点类型(是任务列表还是普通任务等)进行不同的处理,创建对应的本地数据记录(通过SqlNote),设置相关属性(如父节点ID、全局唯一标识符等), + // 将数据提交到本地数据库,更新本地和远程标识符的映射关系,并且调用updateRemoteMeta方法来处理与元数据相关的更新操作,若在过程中出现找不到父节点ID等问题则抛出异常表示无法添加本地节点。 + private void addLocalNode(Node node) throws NetworkFailureException { if (mCancelled) { return; @@ -549,6 +584,7 @@ public class GTaskManager { if (note.has(NoteColumns.ID)) { long id = note.getLong(NoteColumns.ID); if (DataUtils.existInNoteDatabase(mContentResolver, id)) { + // 该ID不可用,必须创建一个新的 // the id is not available, have to create a new one note.remove(NoteColumns.ID); } @@ -562,6 +598,7 @@ public class GTaskManager { if (data.has(DataColumns.ID)) { long dataId = data.getLong(DataColumns.ID); if (DataUtils.existInDataDatabase(mContentResolver, dataId)) { + // 数据ID不可用,必须创建一个新的 // the data id is not available, have to create // a new one data.remove(DataColumns.ID); @@ -583,25 +620,29 @@ public class GTaskManager { } sqlNote.setParentId(parentId.longValue()); } - +// 创建本地节点 // create the local node sqlNote.setGtaskId(node.getGid()); sqlNote.commit(false); - +// 更新Gid-Nid映射 // update gid-nid mapping mGidToNid.put(node.getGid(), sqlNote.getId()); mNidToGid.put(sqlNote.getId(), node.getGid()); - +// 更新元数据 // update meta updateRemoteMeta(node.getGid(), sqlNote); } - +// 更新本地节点数据以及相关同步逻辑的私有方法,首先判断同步是否已取消,如果取消则直接返回, + // 然后根据节点类型(是任务还是其他类型等)获取对应的本地数据记录(通过SqlNote),更新其内容、父节点ID等属性,将更新后的数据提交到本地数据库, + // 并调用updateRemoteMeta方法来处理与元数据相关的更新操作,若在过程中出现找不到父节点ID等问题则抛出异常表示无法更新本地节点。 + private void updateLocalNode(Node node, Cursor c) throws NetworkFailureException { if (mCancelled) { return; } SqlNote sqlNote; + // 更新本地笔记 // update the note locally sqlNote = new SqlNote(mContext, c); sqlNote.setContent(node.getLocalJSONFromContent()); @@ -614,11 +655,15 @@ public class GTaskManager { } sqlNote.setParentId(parentId.longValue()); sqlNote.commit(true); - +// 更新元数据信息 // update meta info updateRemoteMeta(node.getGid(), sqlNote); } - + // 将远程节点添加到本地数据库以及处理相关同步逻辑的私有方法,首先判断同步是否已取消,如果取消则直接返回, + // 然后根据节点类型(是任务还是任务列表等)进行不同的处理,创建对应的本地数据记录(通过SqlNote), + // 对于任务,找到其对应的父任务列表,添加到任务列表中,然后向远程服务创建任务(通过GTaskClient),对于任务列表,判断是否已存在,不存在则创建, + // 接着更新本地数据记录的相关属性(如全局唯一标识符等),提交到本地数据库,更新本地和远程标识符的映射关系,若在过程中出现找不到父任务列表等问题则抛出异常表示无法添加远程节点。 + private void addRemoteNode(Node node, Cursor c) throws NetworkFailureException { if (mCancelled) { return; @@ -626,7 +671,7 @@ public class GTaskManager { SqlNote sqlNote = new SqlNote(mContext, c); Node n; - + // 远程更新 // update remotely if (sqlNote.isNoteType()) { Task task = new Task(); @@ -641,12 +686,12 @@ public class GTaskManager { GTaskClient.getInstance().createTask(task); n = (Node) task; - + // 添加元数据 // add meta updateRemoteMeta(task.getGid(), sqlNote); } else { TaskList tasklist = null; - + // 我们需要跳过已存在的文件夹 // we need to skip folder if it has already existed String folderName = GTaskStringUtils.MIUI_FOLDER_PREFFIX; if (sqlNote.getId() == Notes.ID_ROOT_FOLDER) @@ -670,7 +715,7 @@ public class GTaskManager { break; } } - +// 没有匹配的则添加 // no match we can add now if (tasklist == null) { tasklist = new TaskList(); @@ -680,32 +725,36 @@ public class GTaskManager { } n = (Node) tasklist; } - + // 更新本地笔记 // update local note sqlNote.setGtaskId(n.getGid()); sqlNote.commit(false); sqlNote.resetLocalModified(); sqlNote.commit(true); - + // gid-id映射 // gid-id mapping mGidToNid.put(n.getGid(), sqlNote.getId()); mNidToGid.put(sqlNote.getId(), n.getGid()); } - + // 更新远程节点数据以及相关同步逻辑的私有方法,首先判断同步是否已取消,如果取消则直接返回, + // 然后获取对应的本地数据记录(通过SqlNote),将本地数据内容设置到远程节点,向远程服务添加更新节点的请求(通过GTaskClient), + // 接着调用updateRemoteMeta方法处理元数据更新,对于任务类型的节点,还会判断是否需要移动任务(根据父任务列表的变化),若需要则执行移动任务操作(通过GTaskClient), + // 最后清除本地数据的修改标记并提交到本地数据库,若在过程中出现找不到父任务列表等问题则抛出异常表示无法更新远程任务。 + private void updateRemoteNode(Node node, Cursor c) throws NetworkFailureException { if (mCancelled) { return; } SqlNote sqlNote = new SqlNote(mContext, c); - + // 远程更新 // update remotely node.setContentByLocalJSON(sqlNote.getContent()); GTaskClient.getInstance().addUpdateNode(node); - + // 更新元数据 // update meta updateRemoteMeta(node.getGid(), sqlNote); - + // 如果必要的话移动任务 // move task if necessary if (sqlNote.isNoteType()) { Task task = (Task) node; @@ -724,12 +773,15 @@ public class GTaskManager { GTaskClient.getInstance().moveTask(task, preParentList, curParentList); } } - + // 清除本地修改标记 // clear local modified flag sqlNote.resetLocalModified(); sqlNote.commit(true); } - + // 更新远程元数据的私有方法,首先判断传入的本地数据记录(sqlNote)是否为笔记类型且不为空, + // 如果是,则先从元数据映射中查找对应Gid的元数据对象,若存在则更新其元数据内容并向远程服务添加更新节点请求(通过GTaskClient), + // 若不存在则创建新的元数据对象,添加到元数据列表中,然后向远程服务创建元数据任务(通过GTaskClient),以此来保证元数据在本地和远程的同步更新。 + private void updateRemoteMeta(String gid, SqlNote sqlNote) throws NetworkFailureException { if (sqlNote != null && sqlNote.isNoteType()) { MetaData metaData = mMetaHashMap.get(gid); @@ -745,12 +797,15 @@ public class GTaskManager { } } } - + // 刷新本地同步ID的私有方法,用于在同步完成后,根据最新的远程任务列表数据来更新本地数据的同步ID,首先判断同步是否已取消,如果取消则直接返回, + // 然后清空之前缓存的相关任务数据结构(如任务哈希映射、任务列表哈希映射、元数据哈希映射等),重新初始化任务列表数据(通过initGTaskList方法), + // 接着查询本地数据库中的相关数据,对于在远程任务列表中能找到对应节点的数据,更新其同步ID,若在过程中出现找不到对应节点等问题则抛出异常表示同步后本地部分数据缺失对应的Gid。 + private void refreshLocalSyncId() throws NetworkFailureException { if (mCancelled) { return; } - + // 获取最新的gtask列表 // get the latest gtask list mGTaskHashMap.clear(); mGTaskListHashMap.clear(); @@ -789,11 +844,13 @@ public class GTaskManager { } } } - + // 获取当前同步账户名称的方法,通过调用GTaskClient的实例获取当前同步账户对象,然后返回其账户名称,外部可通过此方法获取当前正在用于同步的账户信息。 + public String getSyncAccount() { return GTaskClient.getInstance().getSyncAccount().name; } - + // 用于取消正在进行的同步操作的方法,将同步取消标记(mCancelled)设置为true,在同步过程中的各个关键步骤都会检查这个标记, + // 一旦发现被设置为true,则会及时终止相应的同步操作,避免不必要的资源消耗和数据不一致问题。 public void cancelSync() { mCancelled = true; } diff --git a/src/Notes-master/src/net/micode/notes/gtask/remote/GTaskSyncService.java b/src/Notes-master/src/net/micode/notes/gtask/remote/GTaskSyncService.java index cca36f7..b3b6f12 100644 --- a/src/Notes-master/src/net/micode/notes/gtask/remote/GTaskSyncService.java +++ b/src/Notes-master/src/net/micode/notes/gtask/remote/GTaskSyncService.java @@ -22,26 +22,37 @@ import android.content.Context; import android.content.Intent; import android.os.Bundle; import android.os.IBinder; +// GTaskSyncService类继承自Service,是一个用于处理任务同步相关操作的后台服务类,它可以接收不同的意图(Intent)来启动或取消任务同步, +// 并且在同步过程中能够发送广播来通知同步状态、进度等信息,同时对外提供了一些静态方法方便在不同的地方控制同步操作以及获取同步相关的状态信息。 public class GTaskSyncService extends Service { + // 用于在Intent中传递同步操作类型的字符串常量名称,外部组件(如Activity等)可以通过在Intent中设置该名称对应的额外数据来指定要执行的同步相关操作。 public final static String ACTION_STRING_NAME = "sync_action_type"; - + // 表示启动同步操作的常量值,在通过Intent传递同步操作类型时使用该值表示要开始进行任务同步。 public final static int ACTION_START_SYNC = 0; - + // 表示取消同步操作的常量值,用于指示服务取消正在进行的任务同步过程。 public final static int ACTION_CANCEL_SYNC = 1; - + // 表示无效的同步操作类型的常量值,当接收到无法识别的操作类型时可能用到。 public final static int ACTION_INVALID = 2; - +// 用于发送广播的意图(Intent)的动作(Action)名称,通过这个名称可以在应用内识别来自该服务发送的广播,其他组件可以注册接收该广播来获取同步服务相关的信息。 + public final static String GTASK_SERVICE_BROADCAST_NAME = "net.micode.notes.gtask.remote.gtask_sync_service"; - + // 用于在广播中传递是否正在同步这个状态信息的键(Key)名称,接收广播的组件可以通过该键从广播附带的数据中获取当前是否正在进行同步的状态。 + public final static String GTASK_SERVICE_BROADCAST_IS_SYNCING = "isSyncing"; - + // 用于在广播中传递同步进度消息的键(Key)名称,服务可以在同步过程中更新并发送相应的进度消息,其他组件接收广播后能展示给用户同步的进展情况。 + public final static String GTASK_SERVICE_BROADCAST_PROGRESS_MSG = "progressMsg"; - +// 静态变量,用于存储当前正在执行的同步任务(GTaskASyncTask类型)实例,初始化为null,在启动同步时创建并赋值,取消或完成同步后设置为null,用于跟踪同步任务的执行情况。 + private static GTaskASyncTask mSyncTask = null; - + // 静态变量,用于存储同步进度相关的消息字符串,初始为空字符串,服务在同步过程中可以更新该字符串内容,并通过广播发送出去,方便外部组件展示同步进度给用户。 + private static String mSyncProgress = ""; - +// 私有方法,用于启动同步任务的逻辑,首先判断当前是否已经存在正在执行的同步任务(mSyncTask是否为null), + // 如果不存在,则创建一个新的GTaskASyncTask实例,并传入当前服务(this)以及一个完成监听器(OnCompleteListener), + // 在监听器中,当同步任务完成时,会将mSyncTask设置为null,发送一个空消息的广播,并停止该服务自身,然后发送广播通知同步开始,最后执行同步任务。 + private void startSync() { if (mSyncTask == null) { mSyncTask = new GTaskASyncTask(this, new GTaskASyncTask.OnCompleteListener() { @@ -55,18 +66,24 @@ public class GTaskSyncService extends Service { mSyncTask.execute(); } } - + // 私有方法,用于取消正在进行的同步任务,如果当前存在正在执行的同步任务(mSyncTask不为null),则调用其cancelSync方法来取消同步操作。 + private void cancelSync() { if (mSyncTask != null) { mSyncTask.cancelSync(); } } - + // 重写Service的onCreate方法,在服务创建时被调用,这里将mSyncTask设置为null,确保每次服务启动时都处于初始状态,准备执行新的同步任务。 + @Override public void onCreate() { mSyncTask = null; } - + // 重写Service的onStartCommand方法,当通过startService方法启动服务时会调用此方法,用于处理传入的意图(Intent)并根据其中携带的同步操作类型执行相应操作, + // 首先从意图中获取额外数据的Bundle对象,然后判断Bundle是否存在且包含指定的同步操作类型键(ACTION_STRING_NAME), + // 如果满足条件,则根据操作类型的值进行switch判断,若是ACTION_START_SYNC则调用startSync方法启动同步,若是ACTION_CANCEL_SYNC则调用cancelSync方法取消同步, + // 最后返回START_STICKY表示服务在被系统意外终止后应该尝试重新启动,以保证同步功能的持续可用性,若不满足条件则调用父类的onStartCommand方法进行默认处理。 + @Override public int onStartCommand(Intent intent, int flags, int startId) { Bundle bundle = intent.getExtras(); @@ -85,18 +102,22 @@ public class GTaskSyncService extends Service { } return super.onStartCommand(intent, flags, startId); } - +// 重写Service的onLowMemory方法,当系统内存不足时会调用此方法,在这里如果存在正在执行的同步任务(mSyncTask不为null),则调用其cancelSync方法取消同步操作, + // 以释放内存资源,避免因内存不足导致应用出现异常或性能问题。 @Override public void onLowMemory() { if (mSyncTask != null) { mSyncTask.cancelSync(); } } - + // 重写Service的onBind方法,用于处理服务的绑定操作,这里返回null表示该服务不支持绑定操作,即它是通过startService方式启动来执行后台任务的,而不是通过绑定来与其他组件交互。 + public IBinder onBind(Intent intent) { return null; } - + // 用于发送广播的方法,更新同步进度消息(mSyncProgress),创建一个新的意图(Intent),设置其动作(Action)为预定义的广播名称(GTASK_SERVICE_BROADCAST_NAME), + // 并将是否正在同步(mSyncTask是否为null)以及同步进度消息(mSyncProgress)作为额外数据添加到意图中,最后通过sendBroadcast方法发送广播, + // 以便其他组件能够接收到这些同步相关的状态和进度信息。 public void sendBroadcast(String msg) { mSyncProgress = msg; Intent intent = new Intent(GTASK_SERVICE_BROADCAST_NAME); @@ -104,6 +125,8 @@ public class GTaskSyncService extends Service { intent.putExtra(GTASK_SERVICE_BROADCAST_PROGRESS_MSG, msg); sendBroadcast(intent); } + // 静态方法,用于方便地从Activity中启动同步操作,首先通过GTaskManager的单例实例设置关联的Activity上下文对象(用于后续可能的与账户认证等相关操作), + // 然后创建一个意图(Intent),指定要启动的服务为GTaskSyncService,并在意图中设置同步操作类型为ACTION_START_SYNC,最后通过Activity的startService方法启动服务来开始同步。 public static void startSync(Activity activity) { GTaskManager.getInstance().setActivityContext(activity); @@ -111,17 +134,21 @@ public class GTaskSyncService extends Service { intent.putExtra(GTaskSyncService.ACTION_STRING_NAME, GTaskSyncService.ACTION_START_SYNC); activity.startService(intent); } + // 静态方法,用于从任意上下文(Context)中取消同步操作,创建一个意图(Intent),指定要操作的服务为GTaskSyncService,并在意图中设置同步操作类型为ACTION_CANCEL_SYNC, + // 最后通过上下文的startService方法启动服务来触发取消同步的逻辑,这样在应用的不同地方都可以方便地取消正在进行的同步任务。 public static void cancelSync(Context context) { Intent intent = new Intent(context, GTaskSyncService.class); intent.putExtra(GTaskSyncService.ACTION_STRING_NAME, GTaskSyncService.ACTION_CANCEL_SYNC); context.startService(intent); } - +// 静态方法,用于外部组件判断当前是否正在进行同步操作,通过检查静态变量mSyncTask是否为null来返回相应的布尔值,表示当前是否有同步任务正在执行。 + public static boolean isSyncing() { return mSyncTask != null; } - + // 静态方法,用于外部组件获取当前的同步进度消息字符串,直接返回存储同步进度消息的静态变量mSyncProgress,以便展示给用户同步的进展情况。 + public static String getProgressString() { return mSyncProgress; } diff --git a/src/Notes-master/src/net/micode/notes/model/Note.java b/src/Notes-master/src/net/micode/notes/model/Note.java index 6706cf6..0484117 100644 --- a/src/Notes-master/src/net/micode/notes/model/Note.java +++ b/src/Notes-master/src/net/micode/notes/model/Note.java @@ -33,11 +33,24 @@ import net.micode.notes.data.Notes.TextNote; import java.util.ArrayList; +// Note类代表笔记的数据模型,用于管理笔记的各种属性数据(如文本数据、通话记录数据等)以及与本地数据库(通过Content Provider)的交互操作, +// 包括创建新笔记、更新笔记、判断笔记是否有本地修改等功能,内部还嵌套了一个NoteData类用于更细致地管理笔记相关的数据内容。 public class Note { + // 用于存储笔记中发生变化的属性值(以键值对形式),通过ContentValues来方便后续与内容提供者进行更新操作,记录笔记在本地修改后产生的数据差异。 + private ContentValues mNoteDiffValues; + // NoteData类型的实例,用于管理笔记中的具体数据内容,如文本数据、通话数据等,将不同类型的数据操作封装在这个内部类中,使代码结构更清晰。 + private NoteData mNoteData; + // 用于日志记录的标签,方便在日志中识别该类相关的输出信息,取固定的字符串"Note"。 private static final String TAG = "Note"; + // 静态同步方法,用于创建一个新的笔记并返回其在数据库中的唯一标识符(ID),接收应用上下文(Context)和所属文件夹的ID作为参数, + // 在方法内部首先创建一个ContentValues对象,设置笔记的创建时间、修改时间、类型、本地修改标记以及父文件夹ID等初始属性值, + // 然后通过内容解析器(ContentResolver)将这些数据插入到本地数据库对应的笔记内容URI(Notes.CONTENT_NOTE_URI)中, + // 接着尝试从插入后返回的Uri中解析出笔记的ID,如果解析出现异常或者ID值为非法值(如 -1)则进行相应的错误处理并返回0或抛出异常,正常情况下返回新创建笔记的ID。 + public static synchronized long getNewNoteId(Context context, long folderId) { + /** * Create a new note id for adding a new note to databases */ @@ -64,42 +77,55 @@ public class Note { } return noteId; } - + // Note类的默认构造函数,用于初始化Note对象,创建一个新的ContentValues对象用于存储笔记的差异数据,同时创建一个NoteData对象来管理具体的笔记数据内容。 + public Note() { mNoteDiffValues = new ContentValues(); mNoteData = new NoteData(); } - +// 用于设置笔记的通用属性值的方法,接收属性的键(key)和值(value)作为参数,将键值对添加到mNoteDiffValues中, + // 同时更新笔记的本地修改标记(LOCAL_MODIFIED)为已修改状态,并设置修改日期(MODIFIED_DATE)为当前时间,以便后续能准确判断笔记是否有修改以及进行相应的更新操作。 + public void setNoteValue(String key, String value) { mNoteDiffValues.put(key, value); mNoteDiffValues.put(NoteColumns.LOCAL_MODIFIED, 1); mNoteDiffValues.put(NoteColumns.MODIFIED_DATE, System.currentTimeMillis()); } - + // 用于设置笔记文本数据的方法,调用内部NoteData对象的setTextData方法,将文本数据的键值对传递给它进行处理,内部会根据情况更新相关的修改标记和日期等信息。 + public void setTextData(String key, String value) { mNoteData.setTextData(key, value); } - + // 用于设置笔记文本数据的唯一标识符(ID)的方法,直接调用内部NoteData对象的setTextDataId方法,将传入的ID值传递给它进行设置,同时会进行参数合法性检查(ID需大于0)。 + public void setTextDataId(long id) { mNoteData.setTextDataId(id); } - + // 用于获取笔记文本数据的唯一标识符(ID)的方法,直接返回内部NoteData对象中存储的文本数据ID(mTextDataId)。 + public long getTextDataId() { return mNoteData.mTextDataId; } - + // 用于设置笔记通话数据的唯一标识符(ID)的方法,调用内部NoteData对象的setCallDataId方法,将传入的ID值传递给它进行设置,同时会进行参数合法性检查(ID需大于0)。 + public void setCallDataId(long id) { mNoteData.setCallDataId(id); } - + // 用于设置笔记通话数据的方法,调用内部NoteData对象的setCallData方法,将通话数据的键值对传递给它进行处理,内部会根据情况更新相关的修改标记和日期等信息。 + public void setCallData(String key, String value) { mNoteData.setCallData(key, value); } - +// 用于判断笔记是否在本地有修改的方法,通过检查mNoteDiffValues中是否有数据(即是否有属性被修改)以及内部NoteData对象是否有本地修改来综合判断, + // 如果两者中任意一个有数据变化,则表示笔记在本地有修改,返回true,否则返回false。 public boolean isLocalModified() { return mNoteDiffValues.size() > 0 || mNoteData.isLocalModified(); } - + // 用于将本地修改后的笔记数据同步到本地数据库的方法,接收应用上下文(Context)和笔记的ID(noteId)作为参数,首先进行参数合法性检查(noteId需大于0), + // 如果笔记没有本地修改则直接返回true,表示无需同步操作。若有本地修改,则先尝试通过内容解析器(ContentResolver)根据mNoteDiffValues中的数据更新笔记的基本属性信息, + // 如果更新失败(返回0表示影响的行数为0,即更新未成功)则记录错误日志但不立即返回,继续往下执行,接着清空mNoteDiffValues已同步的数据。 + // 然后检查内部NoteData对象是否有本地修改,如果有且将其数据推送到内容解析器(通过pushIntoContentResolver方法)失败,则返回false表示同步失败, + // 否则返回true表示整个笔记数据(包括基本属性和具体数据内容)同步成功。 public boolean syncNote(Context context, long noteId) { if (noteId <= 0) { throw new IllegalArgumentException("Wrong note id:" + noteId); @@ -129,35 +155,44 @@ public class Note { return true; } - +// 内部私有类NoteData,用于更细致地管理笔记中的具体数据内容,包括文本数据和通话数据相关的属性、操作以及与内容提供者的交互逻辑,将这些与数据内容相关的操作封装在内部类中, + // 使Note类整体结构更清晰,职责更明确,避免不同类型数据操作的代码相互混杂。 private class NoteData { private long mTextDataId; - + // 用于存储笔记文本数据的唯一标识符(ID),初始化为0,在插入或更新文本数据时会根据情况进行赋值或更新。 + // 用于存储笔记文本数据的具体属性值(以键值对形式),通过ContentValues方便后续与内容提供者进行插入或更新操作,记录文本数据的相关内容。 + private ContentValues mTextDataValues; - +// 用于存储笔记通话数据的唯一标识符(ID),初始化为0,在插入或更新通话数据时会根据情况进行赋值或更新。 + private long mCallDataId; - +// 用于存储笔记通话数据的具体属性值(以键值对形式),通过ContentValues方便后续与内容提供者进行插入或更新操作,记录通话数据的相关内容。 + private ContentValues mCallDataValues; - + // 用于日志记录的标签,方便在日志中识别该内部类相关的输出信息,取固定的字符串"NoteData"。 private static final String TAG = "NoteData"; - + // NoteData类的默认构造函数,用于初始化NoteData对象,创建新的ContentValues对象分别用于存储文本数据和通话数据的属性值,同时将文本数据和通话数据的ID初始化为0。 + public NoteData() { mTextDataValues = new ContentValues(); mCallDataValues = new ContentValues(); mTextDataId = 0; mCallDataId = 0; } - +// 用于判断NoteData对象中的文本数据或通话数据是否有本地修改的方法,通过检查mTextDataValues和mCallDataValues中是否有数据(即是否有属性被修改)来综合判断, + // 如果两者中任意一个有数据变化,则表示有本地修改,返回true,否则返回false。 boolean isLocalModified() { return mTextDataValues.size() > 0 || mCallDataValues.size() > 0; } - +// 用于设置笔记文本数据的唯一标识符(ID)的方法,进行参数合法性检查(ID需大于0),如果合法则将传入的ID值赋值给mTextDataId,用于后续对文本数据的操作定位等。 + void setTextDataId(long id) { if(id <= 0) { throw new IllegalArgumentException("Text data id should larger than 0"); } mTextDataId = id; - } + } // 用于设置笔记通话数据的唯一标识符(ID)的方法,进行参数合法性检查(ID需大于0),如果合法则将传入的ID值赋值给mCallDataId,用于后续对通话数据的操作定位等。 + void setCallDataId(long id) { if (id <= 0) { @@ -165,19 +200,30 @@ public class Note { } mCallDataId = id; } - + // 用于设置笔记通话数据的方法,接收通话数据的键(key)和值(value)作为参数,将键值对添加到mCallDataValues中, + // 同时更新笔记的本地修改标记(通过外部的mNoteDiffValues间接更新,因为NoteData是内部类可以访问外部类的成员)以及修改日期为当前时间,以便能准确判断笔记整体是否有修改以及进行相应的更新操作。 + void setCallData(String key, String value) { mCallDataValues.put(key, value); mNoteDiffValues.put(NoteColumns.LOCAL_MODIFIED, 1); mNoteDiffValues.put(NoteColumns.MODIFIED_DATE, System.currentTimeMillis()); } - +// 用于设置笔记文本数据的方法,接收文本数据的键(key)和值(value)作为参数,将键值对添加到mTextDataValues中, + // 同时更新笔记的本地修改标记(通过外部的mNoteDiffValues间接更新)以及修改日期为当前时间,以便能准确判断笔记整体是否有修改以及进行相应的更新操作。 + void setTextData(String key, String value) { mTextDataValues.put(key, value); mNoteDiffValues.put(NoteColumns.LOCAL_MODIFIED, 1); mNoteDiffValues.put(NoteColumns.MODIFIED_DATE, System.currentTimeMillis()); } - + // 用于将NoteData对象中管理的文本数据和通话数据推送到内容解析器(即更新或插入到本地数据库)的方法,接收应用上下文(Context)和笔记的ID(noteId)作为参数, + // 首先进行参数合法性检查(noteId需大于0),然后创建一个ContentProviderOperation列表(用于批量操作),根据mTextDataValues和mCallDataValues中的数据情况进行不同的操作: + // 如果文本数据有值(mTextDataValues.size() > 0),则设置笔记ID到文本数据属性中,如果文本数据的ID为0(表示是新数据需要插入),则设置文本数据的MIME类型, + // 通过内容解析器将文本数据插入到本地数据库对应的内容URI(Notes.CONTENT_DATA_URI)中,并尝试从插入后返回的Uri中解析出文本数据的ID,如果解析失败则记录错误日志并清空文本数据属性值,返回null表示操作失败; + // 如果文本数据的ID不为0(表示是已有数据需要更新),则构建一个更新操作并添加到操作列表中。然后对通话数据进行类似的处理(根据mCallDataValues的情况进行插入或更新操作)。 + // 最后如果操作列表中有操作需要执行,则通过内容解析器批量应用这些操作(applyBatch方法),根据操作结果返回相应的Uri(如果成功则返回笔记对应的Uri,失败则返回null), + // 如果在批量操作过程中出现RemoteException或OperationApplicationException异常则记录错误日志并返回null表示操作失败。如果操作列表为空则直接返回null表示无需进行数据推送操作。 + Uri pushIntoContentResolver(Context context, long noteId) { /** * Check for safety diff --git a/src/Notes-master/src/net/micode/notes/model/WorkingNote.java b/src/Notes-master/src/net/micode/notes/model/WorkingNote.java index be081e4..01faf6e 100644 --- a/src/Notes-master/src/net/micode/notes/model/WorkingNote.java +++ b/src/Notes-master/src/net/micode/notes/model/WorkingNote.java @@ -30,38 +30,55 @@ import net.micode.notes.data.Notes.DataConstants; import net.micode.notes.data.Notes.NoteColumns; import net.micode.notes.data.Notes.TextNote; import net.micode.notes.tool.ResourceParser.NoteBgResources; - +// `WorkingNote` 类代表正在操作(编辑、修改等)的笔记对象,它封装了笔记的各种属性(如内容、ID、提醒日期、背景颜色等)以及相关的操作方法(如保存、加载、设置属性值等), +// 同时定义了一个接口用于监听笔记设置相关属性变化的情况,以便在属性改变时能做出相应的业务逻辑处理,是笔记在应用中实际使用场景下的一个综合数据和操作模型。 public class WorkingNote { + // 关联的 `Note` 类对象,用于处理笔记底层的数据操作(如同步到数据库等),借助 `Note` 类已有的功能来实现 `WorkingNote` 的相关持久化等操作。 + // Note for the working note private Note mNote; + // 笔记的唯一标识符(ID),用于在数据库等场景中唯一标识该笔记,初始值根据不同的构造函数可能为0(新创建笔记时)或从已有数据中获取的具体ID值。 + // Note Id private long mNoteId; + // 笔记的具体内容,比如用户输入的文本等信息,用于展示和编辑笔记的核心文本数据。 + // Note content private String mContent; + // 笔记的模式,可能用于表示不同的展示或编辑模式(例如是否为清单模式等),具体含义由应用业务逻辑决定,初始化为0。 + // Note mode private int mMode; - + // 笔记的提醒日期,用于设置和记录该笔记是否有提醒以及提醒的时间点,初始化为0表示无提醒。 private long mAlertDate; - + // 笔记的最后修改日期,用于记录笔记最后一次被修改的时间,初始化为当前系统时间(在构造新笔记时)。 private long mModifiedDate; - +// 笔记的背景颜色资源ID,用于设置和获取笔记展示时的背景颜色,初始值会根据具体业务场景设定或默认值赋值。 private int mBgColorId; - +// 关联的桌面小部件(Widget)的ID,用于标识该笔记是否与某个桌面小部件相关联以及是哪个小部件,初始值根据情况设定,若未关联则可能为无效值(`AppWidgetManager.INVALID_APPWIDGET_ID`)。 + private int mWidgetId; - + // 桌面小部件的类型,用于区分不同类型的小部件与笔记的关联情况,初始值根据业务逻辑设定,可能有特定的枚举值(如 `Notes.TYPE_WIDGET_INVALIDE` 表示无效类型等)。 + private int mWidgetType; - + // 笔记所属文件夹的ID,用于表示笔记在文件系统结构中的所属位置,便于分类管理笔记,初始值根据构造函数传入参数确定。 + private long mFolderId; - + // 应用的上下文(Context)对象,用于获取系统服务、资源以及与内容提供者(Content Provider)等进行交互,是整个类中很多操作的基础依赖。 + private Context mContext; - +// 用于日志记录的标签,方便在日志中识别该类相关的输出信息,取固定的字符串"WorkingNote"。 + private static final String TAG = "WorkingNote"; - + // 标记笔记是否已被删除的布尔值,初始化为 `false`,通过相关方法可以设置为 `true` 来表示笔记已被删除状态,在保存等操作中会根据此标记进行相应处理。 + private boolean mIsDeleted; - + // 定义一个接口类型的变量,用于监听笔记设置相关属性(如背景颜色、提醒等)变化的情况,外部类可以实现该接口并注册进来,以便在属性改变时能收到通知并执行相应逻辑。 + private NoteSettingChangedListener mNoteSettingStatusListener; - +// 定义一个字符串数组,用于查询笔记数据时指定要获取的列,包含了数据的ID、内容、MIME类型以及一些其他自定义的数据字段(DATA1 - DATA4),方便从数据库中获取完整的笔记数据信息。 + public static final String[] DATA_PROJECTION = new String[] { DataColumns.ID, DataColumns.CONTENT, @@ -71,7 +88,8 @@ public class WorkingNote { DataColumns.DATA3, DataColumns.DATA4, }; - + // 定义一个字符串数组,用于查询笔记基本属性时指定要获取的列,包含了父文件夹ID、提醒日期、背景颜色ID、小部件ID、小部件类型以及修改日期等字段,用于从数据库中获取笔记的关键属性信息。 + public static final String[] NOTE_PROJECTION = new String[] { NoteColumns.PARENT_ID, NoteColumns.ALERTED_DATE, @@ -80,27 +98,39 @@ public class WorkingNote { NoteColumns.WIDGET_TYPE, NoteColumns.MODIFIED_DATE }; - + // 定义一个常量,表示在 `DATA_PROJECTION` 数组中数据ID列的索引位置,方便在查询结果的游标(Cursor)中准确获取对应的数据,值为0,对应数组的第一个元素位置。 + private static final int DATA_ID_COLUMN = 0; - + // 定义一个常量,表示在 `DATA_PROJECTION` 数组中数据内容列的索引位置,用于从游标中获取笔记的具体内容数据,值为1。 + private static final int DATA_CONTENT_COLUMN = 1; - +// 定义一个常量,表示在 `DATA_PROJECTION` 数组中数据MIME类型列的索引位置,用于判断数据的类型(如文本笔记、通话笔记等),值为2。 + private static final int DATA_MIME_TYPE_COLUMN = 2; - + // 定义一个常量,表示在 `DATA_PROJECTION` 数组中数据模式列(可能用于表示笔记的特定展示或编辑模式)的索引位置,值为3。 + private static final int DATA_MODE_COLUMN = 3; - + // 定义一个常量,表示在 `NOTE_PROJECTION` 数组中父文件夹ID列的索引位置,用于从游标中获取笔记所属文件夹的ID信息,值为0。 + private static final int NOTE_PARENT_ID_COLUMN = 0; - + // 定义一个常量,表示在 `NOTE_PROJECTION` 数组中提醒日期列的索引位置,用于获取笔记的提醒时间信息,值为1。 + private static final int NOTE_ALERTED_DATE_COLUMN = 1; - + // 定义一个常量,表示在 `NOTE_PROJECTION` 数组中背景颜色ID列的索引位置,用于获取笔记的背景颜色设置信息,值为2。 + private static final int NOTE_BG_COLOR_ID_COLUMN = 2; - + // 定义一个常量,表示在 `NOTE_PROJECTION` 数组中小部件ID列的索引位置,用于获取与笔记关联的小部件的ID信息,值为3。 + private static final int NOTE_WIDGET_ID_COLUMN = 3; - - private static final int NOTE_WIDGET_TYPE_COLUMN = 4; - + // 定义一个常量,表示在 `NOTE_PROJECTION` 数组中小部件类型列的索引位置,用于获取小部件的类型信息,值为4。 + + private static fina int NOTE_WIDGET_TYPE_COLUMN = 4; +// 定义一个常量,表示在 `NOTE_PROJECTION` 数组中修改日期列的索引位置,用于获取笔记最后一次被修改的时间信息,值为5。 + private static final int NOTE_MODIFIED_DATE_COLUMN = 5; - + // 私有构造函数,用于创建一个新的(空的)工作笔记对象,接收应用上下文(Context)和所属文件夹的ID作为参数,初始化各种属性的初始值, + // 如提醒日期设为0,修改日期设为当前系统时间,创建一个新的 `Note` 对象用于后续数据操作,笔记ID初始化为0,标记为未删除状态,笔记模式设为0,小部件类型设为无效类型等。 + // New note construct private WorkingNote(Context context, long folderId) { mContext = context; @@ -113,7 +143,9 @@ public class WorkingNote { mMode = 0; mWidgetType = Notes.TYPE_WIDGET_INVALIDE; } - +// 私有构造函数,用于加载一个已存在的笔记对象,接收应用上下文(Context)、笔记的ID(noteId)以及所属文件夹的ID作为参数,初始化相关属性, + // 然后调用 `loadNote` 方法从数据库中加载笔记的基本属性信息,再调用 `loadNoteData` 方法加载笔记的具体数据内容(如文本内容等)。 + // Existing note construct private WorkingNote(Context context, long noteId, long folderId) { mContext = context; @@ -124,7 +156,11 @@ public class WorkingNote { loadNote(); } - private void loadNote() { + // 私有方法,用于从数据库中加载笔记的基本属性信息,通过内容解析器(ContentResolver)根据笔记的ID查询指定列(`NOTE_PROJECTION`)的数据, + // 如果查询到游标(Cursor)不为空且能移动到第一条记录(表示有数据),则从游标中获取父文件夹ID、背景颜色ID、小部件ID、小部件类型、提醒日期以及修改日期等属性值, + // 最后关闭游标,若游标为空则表示找不到对应的笔记,记录错误日志并抛出异常表示无法找到指定ID的笔记。 + + private void loadNote() Cursor cursor = mContext.getContentResolver().query( ContentUris.withAppendedId(Notes.CONTENT_NOTE_URI, mNoteId), NOTE_PROJECTION, null, null, null); @@ -145,7 +181,10 @@ public class WorkingNote { } loadNoteData(); } - +// 私有方法,用于从数据库中加载笔记的具体数据内容,通过内容解析器查询笔记对应的数据(根据 `DATA_PROJECTION` 指定列),条件是数据所属的笔记ID与当前笔记ID匹配, + // 如果查询到游标不为空且能移动到第一条记录,则循环遍历游标(因为可能有多条数据,比如不同类型的数据),根据数据的MIME类型判断是文本笔记还是通话笔记等, + // 分别进行相应的处理(如设置文本数据ID、通话数据ID或者记录错误日志表示类型错误),最后关闭游标,若游标为空则表示找不到对应笔记的数据,记录错误日志并抛出异常。 + private void loadNoteData() { Cursor cursor = mContext.getContentResolver().query(Notes.CONTENT_DATA_URI, DATA_PROJECTION, DataColumns.NOTE_ID + "=?", new String[] { @@ -173,7 +212,9 @@ public class WorkingNote { throw new IllegalArgumentException("Unable to find note's data with id " + mNoteId); } } - +// 静态工厂方法,用于创建一个新的空笔记对象,接收应用上下文(Context)、所属文件夹的ID、小部件ID、小部件类型以及默认背景颜色资源ID作为参数, + // 创建一个 `WorkingNote` 对象,设置其背景颜色ID、小部件ID和小部件类型属性值,最后返回创建好的对象,方便在需要创建新笔记时统一调用此方法进行初始化操作。 + public static WorkingNote createEmptyNote(Context context, long folderId, int widgetId, int widgetType, int defaultBgColorId) { WorkingNote note = new WorkingNote(context, folderId); @@ -182,7 +223,9 @@ public class WorkingNote { note.setWidgetType(widgetType); return note; } - + // 静态工厂方法,用于加载一个已存在的笔记对象,接收应用上下文(Context)和笔记的ID作为参数,通过调用私有构造函数(传入相应参数)创建并返回对应的 `WorkingNote` 对象, + // 方便在需要获取并操作已有笔记时统一使用此方法进行加载操作。 + public static WorkingNote load(Context context, long id) { return new WorkingNote(context, id, 0); } @@ -211,11 +254,17 @@ public class WorkingNote { return false; } } - + // 同步方法,用于保存笔记的相关数据到数据库,首先通过 `isWorthSaving` 方法判断笔记是否值得保存(比如是否已删除、内容是否为空且不存在于数据库、是否有本地修改等情况), + // 如果值得保存,若笔记不存在于数据库中(通过 `existInDatabase` 方法判断,即笔记ID小于等于0),则调用 `Note` 类的 `getNewNoteId` 方法尝试创建一个新的笔记ID, + // 如果创建失败则记录错误日志并返回 `false`,若创建成功则获取到新的笔记ID。然后调用 `mNote`(`Note` 类对象)的 `syncNote` 方法将笔记数据同步到数据库中。 + // 最后,如果笔记关联了有效的小部件(小部件ID不为无效值且小部件类型有效)并且设置了 `NoteSettingChangedListener` 监听器,则调用监听器的 `onWidgetChanged` 方法通知小部件相关内容可能已改变, + // 整个保存操作成功则返回 `true`,否则返回 `false`。 + public boolean existInDatabase() { return mNoteId > 0; } - + // 用于判断笔记是否已经存在于数据库中的方法,通过检查笔记的ID是否大于0来返回相应的布尔值,如果大于0则表示已存在,否则表示不存在。 + private boolean isWorthSaving() { if (mIsDeleted || (!existInDatabase() && TextUtils.isEmpty(mContent)) || (existInDatabase() && !mNote.isLocalModified())) { @@ -224,11 +273,15 @@ public class WorkingNote { return true; } } - + // 私有方法,用于判断笔记是否值得保存,根据多种情况综合判断,比如如果笔记已被标记为删除(`mIsDeleted` 为 `true`), + // 或者笔记不存在于数据库中且内容为空(通过 `TextUtils.isEmpty` 判断内容是否为空字符串),或者笔记已存在于数据库中但没有本地修改(通过 `mNote.isLocalModified` 判断), + // 这些情况下返回 `false`,表示不值得保存,否则返回 `true`,表示值得保存。 public void setOnSettingStatusChangedListener(NoteSettingChangedListener l) { mNoteSettingStatusListener = l; } - + // 用于设置笔记设置状态变化监听器的方法,接收一个实现了 `NoteSettingChangedListener` 接口的对象作为参数, + // 将其赋值给 `mNoteSettingStatusListener` 变量,这样当笔记的相关设置属性(如背景颜色、提醒等)发生变化时,对应的实现类中的方法就会被调用,实现相应的业务逻辑处理。 + public void setAlertDate(long date, boolean set) { if (date != mAlertDate) { mAlertDate = date; @@ -238,7 +291,10 @@ public class WorkingNote { mNoteSettingStatusListener.onClockAlertChanged(date, set); } } - + // 用于标记笔记是否被删除的方法,接收一个布尔值(`mark`)作为参数,将 `mIsDeleted` 属性设置为传入的布尔值, + // 如果笔记关联了有效的小部件(小部件ID不为无效值且小部件类型有效)并且设置了 `NoteSettingChangedListener` 监听器,则调用监听器的 `onWidgetChanged` 方法通知小部件相关内容可能已改变, + // 以此来应对笔记删除状态变化后可能需要的业务逻辑处理(比如小部件中对应笔记的显示更新等)。 + public void markDeleted(boolean mark) { mIsDeleted = mark; if (mWidgetId != AppWidgetManager.INVALID_APPWIDGET_ID @@ -246,7 +302,10 @@ public class WorkingNote { mNoteSettingStatusListener.onWidgetChanged(); } } - + // 用于设置笔记背景颜色资源ID的方法,接收一个整数(`id`)作为参数, + // 首先比较传入的ID和当前的背景颜色ID是否不同,如果不同则更新 `mBgColorId` 属性为传入的ID,并通过 `mNote` 对象调用 `setNoteValue` 方法将背景颜色ID的值设置到笔记的对应属性中, + // 然后如果设置了 `NoteSettingChangedListener` 监听器,则调用其 `onBackgroundColorChanged` 方法通知背景颜色已改变,以便外部进行相应的界面更新等操作。 + public void setBgColorId(int id) { if (id != mBgColorId) { mBgColorId = id; @@ -256,7 +315,10 @@ public class WorkingNote { mNote.setNoteValue(NoteColumns.BG_COLOR_ID, String.valueOf(id)); } } - +// 用于设置笔记的清单模式(可能用于切换笔记的展示或编辑模式为清单形式等)的方法,接收一个整数(`mode`)作为参数, + // 首先比较传入的模式和当前的模式是否不同,如果不同则更新 `mMode` 属性为传入的模式,并通过 `mNote` 对象调用 `setTextData` 方法将模式的值设置到笔记的对应属性中, + // 然后如果设置了 `NoteSettingChangedListener` 监听器,则调用其 `onCheckListModeChanged` 方法通知清单模式已改变,同时传入旧模式和新模式的值,方便外部进行相应的业务逻辑处理(比如界面切换显示等)。 + public void setCheckListMode(int mode) { if (mMode != mode) { if (mNoteSettingStatusListener != null) { @@ -266,13 +328,17 @@ public class WorkingNote { mNote.setTextData(TextNote.MODE, String.valueOf(mMode)); } } - +// 用于设置笔记关联的桌面小部件类型的方法,接收一个整数(`type`)作为参数, + // 比较传入的类型和当前的小部件类型是否不同,如果不同则更新 `mWidgetType` 属性为传入的类型,并通过 `mNote` 对象调用 `setNoteValue` 方法将小部件类型的值设置到笔记的对应属性中。 + public void setWidgetType(int type) { if (type != mWidgetType) { mWidgetType = type; mNote.setNoteValue(NoteColumns.WIDGET_TYPE, String.valueOf(mWidgetType)); } - } + } // 用于设置笔记关联的桌面小部件ID的方法,接收一个整数(`id`)作为参数, + // 比较传入的ID和当前的小部件ID是否不同,如果不同则更新 `mWidgetId` 属性为传入的ID,并通过 `mNote` 对象调用 `setNoteValue` 方法将小部件ID的值设置到笔记的对应属性中。 + public void setWidgetId(int id) { if (id != mWidgetId) { @@ -280,68 +346,89 @@ public class WorkingNote { mNote.setNoteValue(NoteColumns.WIDGET_ID, String.valueOf(mWidgetId)); } } - + // 用于设置笔记的工作文本内容(即核心文本数据)的方法,接收一个字符串(`text`)作为参数, + // 比较传入的文本和当前的文本内容是否相同(通过 `TextUtils.equals` 判断),如果不同则更新 `mContent` 属性为传入的文本,并通过 `mNote` 对象调用 `setTextData` 方法将文本内容设置到笔记的对应属性中。 + public void setWorkingText(String text) { if (!TextUtils.equals(mContent, text)) { mContent = text; mNote.setTextData(DataColumns.CONTENT, mContent); } } - + // 用于将笔记转换为通话笔记的方法,接收电话号码(`phoneNumber`)和通话日期(`callDate`)作为参数, + // 通过 `mNote` 对象分别调用 `setCallData` 方法设置通话日期和电话号码到笔记的对应属性中,同时调用 `setNoteValue` 方法将笔记的父文件夹ID设置为通话记录文件夹的ID(`Notes.ID_CALL_RECORD_FOLDER`), + // 以此来完成笔记类型向通话笔记的转换,可能涉及到对应数据结构和展示等方面的改变,具体由应用的业务逻辑决定。 + public void convertToCallNote(String phoneNumber, long callDate) { mNote.setCallData(CallNote.CALL_DATE, String.valueOf(callDate)); mNote.setCallData(CallNote.PHONE_NUMBER, phoneNumber); mNote.setNoteValue(NoteColumns.PARENT_ID, String.valueOf(Notes.ID_CALL_RECORD_FOLDER)); } - + // 用于判断笔记是否设置了时钟提醒的方法,通过检查 `mAlertDate` 是否大于0来返回相应的布尔值,如果大于0则表示设置了提醒,返回 `true`,否则返回 `false`。 + public boolean hasClockAlert() { return (mAlertDate > 0 ? true : false); } - +// 用于获取笔记内容的方法,直接返回 `mContent` 属性,即笔记的核心文本数据,方便外部获取并展示笔记的具体内容。 + public String getContent() { return mContent; } - + // 用于获取笔记提醒日期的方法,直接返回 `mAlertDate` 属性,即笔记设置的提醒时间点,方便外部查询和使用该提醒日期信息。 + public long getAlertDate() { return mAlertDate; } - + // 用于获取笔记最后修改日期的方法,直接返回 `mModifiedDate` 属性,即笔记最后一次被修改的时间记录,可用于展示、排序等业务场景。 + public long getModifiedDate() { return mModifiedDate; } - + // 用于获取笔记背景颜色资源ID对应的实际背景颜色资源的方法,通过调用 `NoteBgResources` 类的 `getNoteBgResource` 方法(具体实现可能根据资源映射等逻辑获取实际颜色资源), + // 将 `mBgColorId` 作为参数传入,返回获取到的背景颜色资源ID,方便外部进行界面绘制等操作时使用正确的背景颜色资源。 + public int getBgColorResId() { return NoteBgResources.getNoteBgResource(mBgColorId); } - + // 用于获取笔记背景颜色资源ID的方法,直接返回 `mBgColorId` 属性,方便外部获取并记录笔记的背景颜色设置情况。 + public int getBgColorId() { return mBgColorId; } - + // 用于获取笔记标题背景颜色资源ID的方法,通过调用 `NoteBgResources` 类的 `getNoteTitleBgResource` 方法(具体实现类似获取背景颜色资源的逻辑,根据ID获取对应的标题背景颜色资源), + // 将 `mBgColorId` 作为参数传入,返回获取到的标题背景颜色资源ID,方便外部进行界面绘制等操作时使用正确的标题背景颜色资源。 + public int getTitleBgResId() { return NoteBgResources.getNoteTitleBgResource(mBgColorId); } - + // 用于获取笔记的清单模式的方法,直接返回 `mMode` 属性,即笔记当前设置的清单模式值,方便外部获取并判断笔记处于何种展示或编辑模式。 + public int getCheckListMode() { return mMode; - } + } // 用于获取笔记的唯一标识符(ID)的方法,直接返回 `mNoteId` 属性,方便外部在数据库操作、关联查询等场景中使用该笔记的唯一标识。 + public long getNoteId() { return mNoteId; } - + // 用于获取笔记所属文件夹的ID的方法,直接返回 `mFolderId` 属性,方便外部了解笔记在文件系统结构中的位置分类情况,比如用于文件夹相关的查询、统计等操作。 + public long getFolderId() { return mFolderId; } - + // 用于获取笔记关联的桌面小部件ID的方法,直接返回 `mWidgetId` 属性,方便外部获取并操作与笔记相关联的桌面小部件(比如更新小部件显示内容等)。 + public int getWidgetId() { return mWidgetId; } - + // 用于获取笔记关联的桌面小部件类型的方法,直接返回 `mWidgetType` 属性,方便外部获取并判断笔记关联的小部件属于何种类型,以进行相应的业务逻辑处理。 + public int getWidgetType() { return mWidgetType; } - + // 定义一个接口,用于监听笔记设置相关属性变化的情况,外部类需要实现该接口并通过 `setOnSettingStatusChangedListener` 方法注册进来, + // 接口中定义了多个方法,分别对应不同属性变化时的回调通知,比如背景颜色改变、时钟提醒改变、小部件相关改变以及清单模式改变等情况,方便在这些属性变化时执行相应的业务逻辑处理。 + public interface NoteSettingChangedListener { /** * Called when the background color of current note has just changed