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..ead74e5 100644 --- a/src/Notes-master/src/net/micode/notes/data/Contact.java +++ b/src/Notes-master/src/net/micode/notes/data/Contact.java @@ -25,49 +25,75 @@ import android.util.Log; import java.util.HashMap; +<<<<<<< HEAD //ceshihebing2 public class Contact { private static HashMap sContactCache; private static final String TAG = "Contact"; +======= +// 该类用于获取与给定电话号码对应的联系人姓名,通过缓存已查询结果来提高性能 +>>>>>>> xinyajie +public class Contact { + // 该类用于获取与给定电话号码对应的联系人姓名,通过缓存已查询结果来提高性能 + private static HashMap sContactCache; + // 日志标签,用于在 Log 输出中标识该类的相关信息 + private static final String TAG = "Contact"; + // 查询条件字符串,用于从联系人数据库中筛选出匹配电话号码的记录 + // 它通过比较电话号码和联系人数据中的电话号码,确保数据类型为电话类型,并限制在特定的 raw_contact_id 范围内进行查询 + // 其中,PHONE_NUMBERS_EQUAL 是一个函数,用于比较电话号码是否相等,这里使用了占位符 '?',后续会替换为实际的电话号码 + // Data.MIMETYPE 用于指定数据类型,Phone.CONTENT_ITEM_TYPE 表示电话类型的数据 + // Data.RAW_CONTACT_ID 用于指定原始联系人 ID,这里通过子查询从 phone_lookup 表中获取满足 min_match = '+' 条件的 raw_contact_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 = '+')"; - + // 根据给定的上下文和电话号码获取联系人姓名的静态方法 public static String getContact(Context context, String phoneNumber) { + // 如果缓存为空,则创建一个新的 HashMap 用于缓存 if(sContactCache == null) { sContactCache = new HashMap(); } - + // 首先检查缓存中是否已经存在该电话号码对应的联系人姓名,如果存在则直接返回缓存中的结果 if(sContactCache.containsKey(phoneNumber)) { return sContactCache.get(phoneNumber); } - + // 替换查询条件中的 '+' 为电话号码的最小匹配格式,以便进行更准确的查询 String selection = CALLER_ID_SELECTION.replace("+", PhoneNumberUtils.toCallerIDMinMatch(phoneNumber)); + // 使用 ContentResolver 查询联系人数据库,获取匹配电话号码的联系人姓名 + // 查询的 URI 为 Data.CONTENT_URI,表示联系人数据的通用 URI + // 查询的列仅包含 Phone.DISPLAY_NAME,即联系人的显示名称 + // 选择条件为上面生成的 selection,选择参数为实际的电话号码 + // 排序方式为默认,这里传入 null Cursor cursor = context.getContentResolver().query( Data.CONTENT_URI, new String [] { Phone.DISPLAY_NAME }, selection, new String[] { phoneNumber }, null); - + // 如果查询结果不为空且游标能够移动到第一条记录,则表示找到了匹配的联系人 if (cursor != null && cursor.moveToFirst()) { try { + // 从游标中获取联系人姓名,索引为 0,因为查询结果只包含一列联系人姓名 String name = cursor.getString(0); + // 将查询到的电话号码和对应的联系人姓名存入缓存 sContactCache.put(phoneNumber, name); + // 返回联系人姓名 return name; } catch (IndexOutOfBoundsException e) { + // 如果在获取游标数据时发生越界异常,则记录错误日志,并返回 null Log.e(TAG, " Cursor get string error " + e.toString()); return null; } finally { + // 无论是否发生异常,都需要关闭游标,释放资源 cursor.close(); } } else { + // 如果没有找到匹配的联系人,则记录调试日志,并返回 null Log.d(TAG, "No contact matched with number:" + phoneNumber); return null; } diff --git a/src/Notes-master/src/net/micode/notes/data/Notes.java b/src/Notes-master/src/net/micode/notes/data/Notes.java index f240604..827493e 100644 --- a/src/Notes-master/src/net/micode/notes/data/Notes.java +++ b/src/Notes-master/src/net/micode/notes/data/Notes.java @@ -17,12 +17,20 @@ package net.micode.notes.data; import android.net.Uri; + +// 该类用于定义笔记应用程序中的各种常量和数据结构相关信息 public class Notes { + // 定义笔记应用的授权(Authority),用于 Content Provider 的标识 public static final String AUTHORITY = "micode_notes"; + // 日志标签,用于在应用中标记与笔记相关的日志信息 public static final String TAG = "Notes"; + + // 定义不同类型的笔记或文件夹 public static final int TYPE_NOTE = 0; public static final int TYPE_FOLDER = 1; public static final int TYPE_SYSTEM = 2; + // 系统文件夹的标识符 + // ID_ROOT_FOLDER 是默认文件夹的 ID /** * Following IDs are system folders' identifiers @@ -31,179 +39,194 @@ public class Notes { * {@link Notes#ID_CALL_RECORD_FOLDER} is to store call records */ public static final int ID_ROOT_FOLDER = 0; + // ID_TEMPARAY_FOLDER 用于存放不属于任何文件夹的笔记 public static final int ID_TEMPARAY_FOLDER = -1; + // ID_CALL_RECORD_FOLDER 用于存储通话记录的文件夹 ID public static final int ID_CALL_RECORD_FOLDER = -2; + // ID_TRASH_FOLER 垃圾桶文件夹 ID public static final int ID_TRASH_FOLER = -3; + // 用于传递提醒日期的 Intent 额外数据键 public static final String INTENT_EXTRA_ALERT_DATE = "net.micode.notes.alert_date"; + // 用于传递背景颜色 ID 的 Intent 额外数据键 public static final String INTENT_EXTRA_BACKGROUND_ID = "net.micode.notes.background_color_id"; + // 用于传递小部件 ID 的 Intent 额外数据键 public static final String INTENT_EXTRA_WIDGET_ID = "net.micode.notes.widget_id"; + // 用于传递小部件类型的 Intent 额外数据键 public static final String INTENT_EXTRA_WIDGET_TYPE = "net.micode.notes.widget_type"; + // 用于传递文件夹 ID 的 Intent 额外数据键 public static final String INTENT_EXTRA_FOLDER_ID = "net.micode.notes.folder_id"; + // 用于传递通话日期的 Intent 额外数据键 public static final String INTENT_EXTRA_CALL_DATE = "net.micode.notes.call_date"; - + // 无效的小部件类型 public static final int TYPE_WIDGET_INVALIDE = -1; + // 2x 小部件类型 public static final int TYPE_WIDGET_2X = 0; + // 4x 小部件类型 public static final int TYPE_WIDGET_4X = 1; - + // 内部类,用于定义数据常量 public static class DataConstants { + // 文本笔记的 Content Item Type public static final String NOTE = TextNote.CONTENT_ITEM_TYPE; + // 通话笔记的 Content Item Type public static final String CALL_NOTE = CallNote.CONTENT_ITEM_TYPE; } - + // 用于查询所有笔记和文件夹的 Uri /** * Uri to query all notes and folders */ public static final Uri CONTENT_NOTE_URI = Uri.parse("content://" + AUTHORITY + "/note"); - + // 用于查询数据的 Uri /** * Uri to query data */ public static final Uri CONTENT_DATA_URI = Uri.parse("content://" + AUTHORITY + "/data"); - + // 定义笔记列的接口 public interface NoteColumns { + // 行的唯一 ID,类型为长整型整数 /** * The unique ID for a row *

Type: INTEGER (long)

*/ public static final String ID = "_id"; - + // 笔记或文件夹的父 ID,类型为长整型整数 /** * The parent's id for note or folder *

Type: INTEGER (long)

*/ public static final String PARENT_ID = "parent_id"; - + // 笔记或文件夹的创建日期,类型为长整型整数 /** * Created data for note or folder *

Type: INTEGER (long)

*/ public static final String CREATED_DATE = "created_date"; - + // 最新修改日期,类型为长整型整数 /** * Latest modified date *

Type: INTEGER (long)

*/ public static final String MODIFIED_DATE = "modified_date"; - + // 提醒日期,类型为长整型整数 /** * Alert date *

Type: INTEGER (long)

*/ public static final String ALERTED_DATE = "alert_date"; - + // 文件夹名称或笔记的文本内容,类型为文本 /** * Folder's name or text content of note *

Type: TEXT

*/ public static final String SNIPPET = "snippet"; - + // 笔记的小部件 ID,类型为长整型整数 /** * Note's widget id *

Type: INTEGER (long)

*/ public static final String WIDGET_ID = "widget_id"; - + // 笔记的小部件类型,类型为长整型整数 /** * Note's widget type *

Type: INTEGER (long)

*/ public static final String WIDGET_TYPE = "widget_type"; - + // 笔记的背景颜色 ID,类型为长整型整数 /** * Note's background color's id *

Type: INTEGER (long)

*/ public static final String BG_COLOR_ID = "bg_color_id"; - + // 对于文本笔记,无附件时为 0,多媒体笔记至少有一个附件时为 1,类型为整数 /** * For text note, it doesn't has attachment, for multi-media * note, it has at least one attachment *

Type: INTEGER

*/ public static final String HAS_ATTACHMENT = "has_attachment"; - + // 文件夹中的笔记数量,类型为长整型整数 /** * Folder's count of notes *

Type: INTEGER (long)

*/ public static final String NOTES_COUNT = "notes_count"; - + // 文件类型(文件夹或笔记),类型为整数 /** * The file type: folder or note *

Type: INTEGER

*/ public static final String TYPE = "type"; - + // 最后同步 ID,类型为长整型整数 /** * The last sync id *

Type: INTEGER (long)

*/ public static final String SYNC_ID = "sync_id"; - + // 表示本地是否修改的标志,类型为整数 /** * Sign to indicate local modified or not *

Type: INTEGER

*/ public static final String LOCAL_MODIFIED = "local_modified"; - + // 移动到临时文件夹之前的原始父 ID,类型为整数 /** * Original parent id before moving into temporary folder *

Type : INTEGER

*/ public static final String ORIGIN_PARENT_ID = "origin_parent_id"; - + // gtask ID,类型为文本 /** * The gtask id *

Type : TEXT

*/ public static final String GTASK_ID = "gtask_id"; - + // 版本代码,类型为长整型整数 /** * The version code *

Type : INTEGER (long)

*/ public static final String VERSION = "version"; } - + // 定义数据列的接口 public interface DataColumns { + // 行的唯一 ID,类型为长整型整数 /** * The unique ID for a row *

Type: INTEGER (long)

*/ public static final String ID = "_id"; - + // 此行表示的项目的 MIME 类型,类型为文本 /** * The MIME type of the item represented by this row. *

Type: Text

*/ public static final String MIME_TYPE = "mime_type"; - + // 此数据所属笔记的引用 ID,类型为长整型整数 /** * The reference id to note that this data belongs to *

Type: INTEGER (long)

*/ public static final String NOTE_ID = "note_id"; - + // 笔记或文件夹的创建日期,类型为长整型整数 /** * Created data for note or folder *

Type: INTEGER (long)

*/ public static final String CREATED_DATE = "created_date"; - + // 最新修改日期,类型为长整型整数 /** * Latest modified date *

Type: INTEGER (long)

*/ public static final String MODIFIED_DATE = "modified_date"; - + // 数据内容,类型为文本 /** * Data's content *

Type: TEXT

*/ public static final String CONTENT = "content"; - + // 通用数据列,含义取决于 MIME_TYPE,用于整数数据类型 /** * Generic data column, the meaning is {@link #MIMETYPE} specific, used for @@ -211,28 +234,28 @@ public class Notes { *

Type: INTEGER

*/ public static final String DATA1 = "data1"; - + // 通用数据列,含义取决于 MIME_TYPE,用于整数数据类型 /** * Generic data column, the meaning is {@link #MIMETYPE} specific, used for * integer data type *

Type: INTEGER

*/ public static final String DATA2 = "data2"; - + // 通用数据列,含义取决于 MIME_TYPE,用于文本数据类型 /** * Generic data column, the meaning is {@link #MIMETYPE} specific, used for * TEXT data type *

Type: TEXT

*/ public static final String DATA3 = "data3"; - + // 通用数据列,含义取决于 MIME_TYPE,用于文本数据类型 /** * Generic data column, the meaning is {@link #MIMETYPE} specific, used for * TEXT data type *

Type: TEXT

*/ public static final String DATA4 = "data4"; - + // 通用数据列,含义取决于 MIME_TYPE,用于文本数据类型 /** * Generic data column, the meaning is {@link #MIMETYPE} specific, used for * TEXT data type @@ -240,8 +263,10 @@ public class Notes { */ public static final String DATA5 = "data5"; } + // 文本笔记类,实现 DataColumns 接口 public static final class TextNote implements DataColumns { + // 表示文本是否处于清单模式的模式,1 为清单模式,0 为正常模式,类型为整数 /** * Mode to indicate the text in check list mode or not *

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

@@ -249,31 +274,32 @@ public class Notes { public static final String MODE = DATA1; public static final int MODE_CHECK_LIST = 1; - + // 文本笔记的 Content Type(目录) public static final String CONTENT_TYPE = "vnd.android.cursor.dir/text_note"; - + // 文本笔记的 Content Item Type(项目) public static final String CONTENT_ITEM_TYPE = "vnd.android.cursor.item/text_note"; - + // 文本笔记的 Content Uri public static final Uri CONTENT_URI = Uri.parse("content://" + AUTHORITY + "/text_note"); } - + // 通话笔记类,实现 DataColumns 接口 public static final class CallNote implements DataColumns { + // 通话记录的通话日期,类型为长整型整数 /** * Call date for this record *

Type: INTEGER (long)

*/ public static final String CALL_DATE = DATA1; - + // 通话记录的电话号码,类型为文本 /** * Phone number for this record *

Type: TEXT

*/ public static final String PHONE_NUMBER = DATA3; - + // 通话笔记的 Content Type(目录) public static final String CONTENT_TYPE = "vnd.android.cursor.dir/call_note"; - + // 通话笔记的 Content Item Type(项目) public static final String CONTENT_ITEM_TYPE = "vnd.android.cursor.item/call_note"; - + // 通话笔记的 Content Uri public static final Uri CONTENT_URI = Uri.parse("content://" + AUTHORITY + "/call_note"); } } 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..59c388c 100644 --- a/src/Notes-master/src/net/micode/notes/data/NotesDatabaseHelper.java +++ b/src/Notes-master/src/net/micode/notes/data/NotesDatabaseHelper.java @@ -21,27 +21,28 @@ import android.content.Context; import android.database.sqlite.SQLiteDatabase; import android.database.sqlite.SQLiteOpenHelper; import android.util.Log; - +// 该类继承自 SQLiteOpenHelper,用于管理笔记应用的数据库创建、升级和相关操作 import net.micode.notes.data.Notes.DataColumns; import net.micode.notes.data.Notes.DataConstants; import net.micode.notes.data.Notes.NoteColumns; public class NotesDatabaseHelper extends SQLiteOpenHelper { + // 数据库名称 private static final String DB_NAME = "note.db"; - + // 数据库版本号 private static final int DB_VERSION = 4; - + // 定义数据库表名的接口 public interface TABLE { public static final String NOTE = "note"; public static final String DATA = "data"; } - + // 日志标签,用于在数据库操作过程中记录日志信息 private static final String TAG = "NotesDatabaseHelper"; - + // 单例模式实例 private static NotesDatabaseHelper mInstance; - + // 创建笔记表的 SQL 语句 private static final String CREATE_NOTE_TABLE_SQL = "CREATE TABLE " + TABLE.NOTE + "(" + NoteColumns.ID + " INTEGER PRIMARY KEY," + @@ -62,7 +63,7 @@ public class NotesDatabaseHelper extends SQLiteOpenHelper { NoteColumns.GTASK_ID + " TEXT NOT NULL DEFAULT ''," + NoteColumns.VERSION + " INTEGER NOT NULL DEFAULT 0" + ")"; - + // 创建数据表的 SQL 语句 private static final String CREATE_DATA_TABLE_SQL = "CREATE TABLE " + TABLE.DATA + "(" + DataColumns.ID + " INTEGER PRIMARY KEY," + @@ -77,11 +78,11 @@ public class NotesDatabaseHelper extends SQLiteOpenHelper { DataColumns.DATA4 + " TEXT NOT NULL DEFAULT ''," + DataColumns.DATA5 + " TEXT NOT NULL DEFAULT ''" + ")"; - + // 在数据表上创建基于 note_id 的索引 SQL 语句,如果不存在则创建 private static final String CREATE_DATA_NOTE_ID_INDEX_SQL = "CREATE INDEX IF NOT EXISTS note_id_index ON " + TABLE.DATA + "(" + DataColumns.NOTE_ID + ");"; - + // 当笔记移动到文件夹时增加文件夹笔记数量的触发器 SQL 语句 /** * Increase folder's note count when move note to the folder */ @@ -93,7 +94,7 @@ public class NotesDatabaseHelper extends SQLiteOpenHelper { " SET " + NoteColumns.NOTES_COUNT + "=" + NoteColumns.NOTES_COUNT + " + 1" + " WHERE " + NoteColumns.ID + "=new." + NoteColumns.PARENT_ID + ";" + " END"; - + // 当笔记从文件夹移出时减少文件夹笔记数量的触发器 SQL 语句 /** * Decrease folder's note count when move note from folder */ @@ -106,7 +107,7 @@ public class NotesDatabaseHelper extends SQLiteOpenHelper { " WHERE " + NoteColumns.ID + "=old." + NoteColumns.PARENT_ID + " AND " + NoteColumns.NOTES_COUNT + ">0" + ";" + " END"; - + // 当在文件夹中插入新笔记时增加文件夹笔记数量的触发器 SQL 语句 /** * Increase folder's note count when insert new note to the folder */ @@ -118,7 +119,7 @@ public class NotesDatabaseHelper extends SQLiteOpenHelper { " SET " + NoteColumns.NOTES_COUNT + "=" + NoteColumns.NOTES_COUNT + " + 1" + " WHERE " + NoteColumns.ID + "=new." + NoteColumns.PARENT_ID + ";" + " END"; - + // 当从文件夹中删除笔记时减少文件夹笔记数量的触发器 SQL 语句 /** * Decrease folder's note count when delete note from the folder */ @@ -131,7 +132,7 @@ public class NotesDatabaseHelper extends SQLiteOpenHelper { " WHERE " + NoteColumns.ID + "=old." + NoteColumns.PARENT_ID + " AND " + NoteColumns.NOTES_COUNT + ">0;" + " END"; - + // 当插入类型为笔记的数据时更新笔记内容的触发器 SQL 语句 /** * Update note's content when insert data with type {@link DataConstants#NOTE} */ @@ -144,7 +145,7 @@ public class NotesDatabaseHelper extends SQLiteOpenHelper { " SET " + NoteColumns.SNIPPET + "=new." + DataColumns.CONTENT + " WHERE " + NoteColumns.ID + "=new." + DataColumns.NOTE_ID + ";" + " END"; - + // 当笔记类型的数据更新时更新笔记内容的触发器 SQL 语句 /** * Update note's content when data with {@link DataConstants#NOTE} type has changed */ @@ -157,7 +158,7 @@ public class NotesDatabaseHelper extends SQLiteOpenHelper { " SET " + NoteColumns.SNIPPET + "=new." + DataColumns.CONTENT + " WHERE " + NoteColumns.ID + "=new." + DataColumns.NOTE_ID + ";" + " END"; - + // 当笔记类型的数据删除时更新笔记内容的触发器 SQL 语句 /** * Update note's content when data with {@link DataConstants#NOTE} type has deleted */ @@ -170,7 +171,7 @@ public class NotesDatabaseHelper extends SQLiteOpenHelper { " SET " + NoteColumns.SNIPPET + "=''" + " WHERE " + NoteColumns.ID + "=old." + DataColumns.NOTE_ID + ";" + " END"; - + // 当笔记被删除时删除其关联数据的触发器 SQL 语句 /** * Delete datas belong to note which has been deleted */ @@ -181,7 +182,7 @@ public class NotesDatabaseHelper extends SQLiteOpenHelper { " DELETE FROM " + TABLE.DATA + " WHERE " + DataColumns.NOTE_ID + "=old." + NoteColumns.ID + ";" + " END"; - + // 当文件夹被删除时删除其关联笔记的触发器 SQL 语句 /** * Delete notes belong to folder which has been deleted */ @@ -192,7 +193,7 @@ public class NotesDatabaseHelper extends SQLiteOpenHelper { " DELETE FROM " + TABLE.NOTE + " WHERE " + NoteColumns.PARENT_ID + "=old." + NoteColumns.ID + ";" + " END"; - + // 当文件夹被移动到垃圾桶文件夹时移动其关联笔记的触发器 SQL 语句 /** * Move notes belong to folder which has been moved to trash folder */ @@ -205,18 +206,18 @@ public class NotesDatabaseHelper extends SQLiteOpenHelper { " SET " + NoteColumns.PARENT_ID + "=" + Notes.ID_TRASH_FOLER + " WHERE " + NoteColumns.PARENT_ID + "=old." + NoteColumns.ID + ";" + " END"; - + // 构造函数,调用父类 SQLiteOpenHelper 的构造函数,传入上下文、数据库名称、游标工厂(这里为 null)和版本号 public NotesDatabaseHelper(Context context) { super(context, DB_NAME, null, DB_VERSION); } - + // 创建笔记表的方法,执行创建表的 SQL 语句,重新创建触发器,并创建系统文件夹 public void createNoteTable(SQLiteDatabase db) { db.execSQL(CREATE_NOTE_TABLE_SQL); reCreateNoteTableTriggers(db); createSystemFolder(db); Log.d(TAG, "note table has been created"); } - + // 重新创建笔记表触发器的方法,先删除已存在的相关触发器,再创建新的触发器 private void reCreateNoteTableTriggers(SQLiteDatabase db) { db.execSQL("DROP TRIGGER IF EXISTS increase_folder_count_on_update"); db.execSQL("DROP TRIGGER IF EXISTS decrease_folder_count_on_update"); @@ -234,17 +235,17 @@ public class NotesDatabaseHelper extends SQLiteOpenHelper { db.execSQL(FOLDER_DELETE_NOTES_ON_DELETE_TRIGGER); db.execSQL(FOLDER_MOVE_NOTES_ON_TRASH_TRIGGER); } - + // 创建系统文件夹的方法,向笔记表中插入通话记录文件夹、根文件夹、临时文件夹和垃圾桶文件夹的记录 private void createSystemFolder(SQLiteDatabase db) { ContentValues values = new ContentValues(); - + // 通话记录文件夹 /** * call record foler for call notes */ values.put(NoteColumns.ID, Notes.ID_CALL_RECORD_FOLDER); values.put(NoteColumns.TYPE, Notes.TYPE_SYSTEM); db.insert(TABLE.NOTE, null, values); - + // 根文件夹 /** * root folder which is default folder */ @@ -252,7 +253,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 */ @@ -260,7 +261,7 @@ public class NotesDatabaseHelper extends SQLiteOpenHelper { values.put(NoteColumns.ID, Notes.ID_TEMPARAY_FOLDER); values.put(NoteColumns.TYPE, Notes.TYPE_SYSTEM); db.insert(TABLE.NOTE, null, values); - + // 垃圾桶文件夹 /** * create trash folder */ @@ -269,14 +270,14 @@ public class NotesDatabaseHelper extends SQLiteOpenHelper { values.put(NoteColumns.TYPE, Notes.TYPE_SYSTEM); db.insert(TABLE.NOTE, null, values); } - + // 创建数据表的方法,执行创建表的 SQL 语句,重新创建触发器,并创建索引 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"); } - + // 重新创建数据表触发器的方法,先删除已存在的相关触发器,再创建新的触发器 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,20 +287,20 @@ 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) { mInstance = new NotesDatabaseHelper(context); } return mInstance; } - + // 重写 onCreate 方法,在数据库首次创建时调用,创建笔记表和数据表 @Override public void onCreate(SQLiteDatabase db) { createNoteTable(db); createDataTable(db); } - + // 重写 onUpgrade 方法,在数据库版本升级时调用,根据不同的旧版本执行相应的升级操作 @Override public void onUpgrade(SQLiteDatabase db, int oldVersion, int newVersion) { boolean reCreateTriggers = false; @@ -307,7 +308,7 @@ public class NotesDatabaseHelper extends SQLiteOpenHelper { if (oldVersion == 1) { upgradeToV2(db); - skipV2 = true; // this upgrade including the upgrade from v2 to v3 + skipV2 = true; // 此次升级包含从 v2 到 v3 的升级 oldVersion++; } @@ -332,29 +333,29 @@ public class NotesDatabaseHelper extends SQLiteOpenHelper { + "fails"); } } - + // 从版本 1 升级到版本 2 的方法,删除旧表并重新创建新表 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); } - + // 从版本 2 升级到版本 3 的方法,删除无用触发器,添加 gtask_id 列,并添加垃圾桶系统文件夹 private void upgradeToV3(SQLiteDatabase db) { - // drop unused triggers + // 删除无用触发器 db.execSQL("DROP TRIGGER IF EXISTS update_note_modified_date_on_insert"); db.execSQL("DROP TRIGGER IF EXISTS update_note_modified_date_on_delete"); db.execSQL("DROP TRIGGER IF EXISTS update_note_modified_date_on_update"); - // add a column for gtask id + // 添加 gtask_id 列 db.execSQL("ALTER TABLE " + TABLE.NOTE + " ADD COLUMN " + NoteColumns.GTASK_ID + " TEXT NOT NULL DEFAULT ''"); - // add a trash system folder + // 添加垃圾桶系统文件夹 ContentValues values = new ContentValues(); values.put(NoteColumns.ID, Notes.ID_TRASH_FOLER); values.put(NoteColumns.TYPE, Notes.TYPE_SYSTEM); db.insert(TABLE.NOTE, null, values); } - + // 从版本 3 升级到版本 4 的方法,添加 version 列 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..47889d8 100644 --- a/src/Notes-master/src/net/micode/notes/data/NotesProvider.java +++ b/src/Notes-master/src/net/micode/notes/data/NotesProvider.java @@ -34,14 +34,15 @@ import net.micode.notes.data.Notes.DataColumns; import net.micode.notes.data.Notes.NoteColumns; import net.micode.notes.data.NotesDatabaseHelper.TABLE; - +// 该类继承自 ContentProvider,用于在 Android 系统中提供笔记数据的增删改查功能,并处理搜索相关操作 public class NotesProvider extends ContentProvider { + // 用于匹配不同的 Uri 模式的 UriMatcher 实例 private static final UriMatcher mMatcher; - + // 数据库帮助类实例,用于操作数据库 private NotesDatabaseHelper mHelper; - + // 日志标签,用于记录日志信息 private static final String TAG = "NotesProvider"; - + // 定义不同 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 +50,7 @@ public class NotesProvider extends ContentProvider { private static final int URI_SEARCH = 5; private static final int URI_SEARCH_SUGGEST = 6; - + // 静态代码块,用于初始化 UriMatcher,添加各种 Uri 模式的匹配规则 static { mMatcher = new UriMatcher(UriMatcher.NO_MATCH); mMatcher.addURI(Notes.AUTHORITY, "note", URI_NOTE); @@ -60,7 +61,8 @@ public class NotesProvider extends ContentProvider { mMatcher.addURI(Notes.AUTHORITY, SearchManager.SUGGEST_URI_PATH_QUERY, URI_SEARCH_SUGGEST); mMatcher.addURI(Notes.AUTHORITY, SearchManager.SUGGEST_URI_PATH_QUERY + "/*", URI_SEARCH_SUGGEST); } - + // 搜索结果投影字符串,用于定义搜索结果中返回的列信息 + // 包括笔记 ID、将笔记 ID 作为意图额外数据、修剪和替换换行符后的笔记摘要作为搜索建议的文本列、图标资源 ID、意图动作和意图数据类型 /** * 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. @@ -72,52 +74,61 @@ 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; - + // 搜索查询语句,用于从笔记表中查询符合搜索条件的笔记信息 + // 搜索条件是笔记摘要包含指定字符串,且笔记不在垃圾桶文件夹中,并且是普通笔记类型 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 的 onCreate 方法,在 ContentProvider 启动时调用 + // 用于获取 NotesDatabaseHelper 的单例实例 @Override public boolean onCreate() { mHelper = NotesDatabaseHelper.getInstance(getContext()); return true; } - + // 重写 ContentProvider 的 query 方法,用于处理数据查询请求 @Override public Cursor query(Uri uri, String[] projection, String selection, String[] selectionArgs, String sortOrder) { Cursor c = null; SQLiteDatabase db = mHelper.getReadableDatabase(); String id = null; + // 根据 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,查询指定 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,查询指定 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: + // 检查是否指定了不允许的参数,如果指定则抛出异常 if (sortOrder != null || projection != null) { throw new IllegalArgumentException( "do not specify sortOrder, selection, selectionArgs, or projection" + "with this query"); } String searchString = null; + // 根据 Uri 匹配码获取搜索字符串 if (mMatcher.match(uri) == URI_SEARCH_SUGGEST) { if (uri.getPathSegments().size() > 1) { searchString = uri.getPathSegments().get(1); @@ -125,12 +136,13 @@ public class NotesProvider extends ContentProvider { } else { searchString = uri.getQueryParameter("pattern"); } - + // 如果搜索字符串为空,则返回 null if (TextUtils.isEmpty(searchString)) { return null; } try { + // 格式化搜索字符串,执行原始查询并返回游标 searchString = String.format("%%%s%%", searchString); c = db.rawQuery(NOTES_SNIPPET_SEARCH_QUERY, new String[] { searchString }); @@ -139,61 +151,71 @@ public class NotesProvider extends ContentProvider { } break; default: + // 如果 Uri 不匹配任何已知模式,则抛出异常 throw new IllegalArgumentException("Unknown URI " + uri); } + // 如果游标不为空,则设置通知 Uri,以便在数据变化时接收通知 if (c != null) { c.setNotificationUri(getContext().getContentResolver(), uri); } return c; } - + // 重写 ContentProvider 的 insert 方法,用于处理数据插入请求 @Override public Uri insert(Uri uri, ContentValues values) { SQLiteDatabase db = mHelper.getWritableDatabase(); long dataId = 0, noteId = 0, insertedId = 0; + // 根据 Uri 匹配码执行不同的插入操作 switch (mMatcher.match(uri)) { case URI_NOTE: + // 插入笔记数据,获取插入的笔记 ID insertedId = noteId = db.insert(TABLE.NOTE, null, values); break; case URI_DATA: + // 获取数据所属的笔记 ID,如果不存在则记录错误日志 if (values.containsKey(DataColumns.NOTE_ID)) { noteId = values.getAsLong(DataColumns.NOTE_ID); } else { Log.d(TAG, "Wrong data format without note id:" + values.toString()); } + // 插入数据,获取插入的数据 ID insertedId = dataId = db.insert(TABLE.DATA, null, values); break; default: + // 如果 Uri 不匹配任何已知模式,则抛出异常 throw new IllegalArgumentException("Unknown URI " + uri); } - // Notify the note uri + // 如果插入了笔记数据,则通知笔记 Uri 数据变化 if (noteId > 0) { getContext().getContentResolver().notifyChange( ContentUris.withAppendedId(Notes.CONTENT_NOTE_URI, noteId), null); } - - // Notify the data uri + // 如果插入了数据,则通知数据 Uri 数据变化 if (dataId > 0) { getContext().getContentResolver().notifyChange( ContentUris.withAppendedId(Notes.CONTENT_DATA_URI, dataId), null); } - + // 返回插入数据后的 Uri,包含插入的 ID return ContentUris.withAppendedId(uri, insertedId); } - + // 重写 ContentProvider 的 delete 方法,用于处理数据删除请求 @Override public int delete(Uri uri, String selection, String[] selectionArgs) { int count = 0; String id = null; SQLiteDatabase db = mHelper.getWritableDatabase(); boolean deleteData = false; + // 根据 Uri 匹配码执行不同的删除操作 switch (mMatcher.match(uri)) { case URI_NOTE: + // 添加额外的条件,确保只删除有效 ID 的笔记 selection = "(" + selection + ") AND " + NoteColumns.ID + ">0 "; count = db.delete(TABLE.NOTE, selection, selectionArgs); break; case URI_NOTE_ITEM: + // 获取路径片段中的笔记 ID id = uri.getPathSegments().get(1); + // 如果笔记 ID 小于等于 0,则不允许删除(可能是系统文件夹) /** * ID that smaller than 0 is system folder which is not allowed to * trash @@ -206,18 +228,22 @@ public class NotesProvider extends ContentProvider { NoteColumns.ID + "=" + id + parseSelection(selection), selectionArgs); break; case URI_DATA: + // 删除数据,设置标志位 count = db.delete(TABLE.DATA, selection, selectionArgs); deleteData = true; break; case URI_DATA_ITEM: + // 获取路径片段中的数据 ID id = uri.getPathSegments().get(1); count = db.delete(TABLE.DATA, DataColumns.ID + "=" + id + parseSelection(selection), selectionArgs); deleteData = true; break; default: + // 如果 Uri 不匹配任何已知模式,则抛出异常 throw new IllegalArgumentException("Unknown URI " + uri); } + // 如果删除了数据且有数据被删除,则通知笔记 Uri 和删除的 Uri 数据变化 if (count > 0) { if (deleteData) { getContext().getContentResolver().notifyChange(Notes.CONTENT_NOTE_URI, null); @@ -226,38 +252,44 @@ public class NotesProvider extends ContentProvider { } return count; } - + // 重写 ContentProvider 的 update 方法,用于处理数据更新请求 @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; + // 根据 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 = 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: + // 更新数据,设置标志位 count = db.update(TABLE.DATA, values, selection, selectionArgs); updateData = true; break; case URI_DATA_ITEM: + // 获取路径片段中的数据 ID,更新数据 id = uri.getPathSegments().get(1); count = db.update(TABLE.DATA, values, DataColumns.ID + "=" + id + parseSelection(selection), selectionArgs); updateData = true; break; default: + // 如果 Uri 不匹配任何已知模式,则抛出异常 throw new IllegalArgumentException("Unknown URI " + uri); } - + // 如果更新了数据且有数据被更新,则通知笔记 Uri 和更新的 Uri 数据变化 if (count > 0) { if (updateData) { getContext().getContentResolver().notifyChange(Notes.CONTENT_NOTE_URI, null); @@ -266,11 +298,11 @@ public class NotesProvider extends ContentProvider { } return count; } - + // 辅助方法,用于解析选择条件,添加括号和 AND 连接符 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 "); @@ -295,10 +327,10 @@ public class NotesProvider extends ContentProvider { mHelper.getWritableDatabase().execSQL(sql.toString()); } - + // 重写 ContentProvider 的 getType 方法,目前未实现,返回 null @Override public String getType(Uri uri) { - // TODO Auto-generated method stub + // TODO Auto-generated method stub return null; } 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..bdb66ef 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 @@ -28,47 +28,53 @@ import net.micode.notes.R; import net.micode.notes.ui.NotesListActivity; import net.micode.notes.ui.NotesPreferenceActivity; - +// 该类继承自 AsyncTask,用于在后台执行与 GTask 同步相关的任务,并在任务执行过程中更新进度、处理结果并显示通知 public class GTaskASyncTask extends AsyncTask { - + // 用于标识 GTask 同步通知的 ID private static int GTASK_SYNC_NOTIFICATION_ID = 5234235; - + // 定义任务完成监听器接口 public interface OnCompleteListener { void onComplete(); } - + // 上下文对象,用于获取系统服务和资源 private Context mContext; - + // 通知管理器,用于显示通知 private NotificationManager mNotifiManager; - + // GTask 管理器实例,用于执行同步操作 private GTaskManager mTaskManager; - + // 任务完成监听器实例 private OnCompleteListener mOnCompleteListener; - + // 构造函数,初始化相关成员变量 public GTaskASyncTask(Context context, OnCompleteListener listener) { mContext = context; mOnCompleteListener = listener; + // 获取通知管理器服务 mNotifiManager = (NotificationManager) mContext .getSystemService(Context.NOTIFICATION_SERVICE); + // 获取 GTask 管理器单例实例 mTaskManager = GTaskManager.getInstance(); } - + // 取消同步的方法,调用 GTask 管理器的取消同步方法 public void cancelSync() { mTaskManager.cancelSync(); } - + // 发布进度的方法,调用父类的 publishProgress 方法传递进度信息 public void publishProgess(String message) { publishProgress(new String[] { message }); } - + // 显示通知的私有方法,根据给定的 tickerId 和内容创建并显示通知 private void showNotification(int tickerId, String content) { + // 创建通知对象,设置图标、标题和时间 Notification notification = new Notification(R.drawable.notification, mContext .getString(tickerId), System.currentTimeMillis()); + // 设置通知默认的灯光效果 notification.defaults = Notification.DEFAULT_LIGHTS; + // 设置通知自动取消 notification.flags = Notification.FLAG_AUTO_CANCEL; PendingIntent pendingIntent; + // 根据 tickerId 设置不同的点击意图 if (tickerId != R.string.ticker_success) { pendingIntent = PendingIntent.getActivity(mContext, 0, new Intent(mContext, NotesPreferenceActivity.class), 0); @@ -77,32 +83,40 @@ public class GTaskASyncTask extends AsyncTask { pendingIntent = PendingIntent.getActivity(mContext, 0, new Intent(mContext, NotesListActivity.class), 0); } + // 设置通知的详细信息和点击意图 notification.setLatestEventInfo(mContext, mContext.getString(R.string.app_name), content, pendingIntent); + // 显示通知 mNotifiManager.notify(GTASK_SYNC_NOTIFICATION_ID, notification); } - + // 重写 doInBackground 方法,在后台线程执行同步任务 @Override protected Integer doInBackground(Void... unused) { + // 发布登录进度通知 publishProgess(mContext.getString(R.string.sync_progress_login, NotesPreferenceActivity .getSyncAccountName(mContext))); + // 调用 GTask 管理器的同步方法并返回结果 return mTaskManager.sync(mContext, this); } - + // 重写 onProgressUpdate 方法,在主线程更新进度通知 @Override protected void onProgressUpdate(String... progress) { + // 显示同步进度通知 showNotification(R.string.ticker_syncing, progress[0]); + // 如果上下文是 GTaskSyncService,则发送广播传递进度信息 if (mContext instanceof GTaskSyncService) { ((GTaskSyncService) mContext).sendBroadcast(progress[0]); } } - + // 重写 onPostExecute 方法,在主线程处理同步任务结果 @Override protected void onPostExecute(Integer result) { + // 根据不同的结果显示相应的通知 if (result == GTaskManager.STATE_SUCCESS) { showNotification(R.string.ticker_success, mContext.getString( R.string.success_sync_account, mTaskManager.getSyncAccount())); - NotesPreferenceActivity.setLastSyncTime(mContext, System.currentTimeMillis()); + // 设置最后同步时间 + NotesPreferenceActivity.setLastSyncTime(mContext, System.currentTimeMillis()); } else if (result == GTaskManager.STATE_NETWORK_ERROR) { showNotification(R.string.ticker_fail, mContext.getString(R.string.error_sync_network)); } else if (result == GTaskManager.STATE_INTERNAL_ERROR) { @@ -111,6 +125,7 @@ public class GTaskASyncTask extends AsyncTask { showNotification(R.string.ticker_cancel, mContext .getString(R.string.error_sync_cancelled)); } + // 如果有任务完成监听器,则在新线程中触发监听器的 onComplete 方法 if (mOnCompleteListener != null) { new Thread(new Runnable() { 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..eb5ab19 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,39 @@ import java.util.List; import java.util.zip.GZIPInputStream; import java.util.zip.Inflater; import java.util.zip.InflaterInputStream; - +// 该类是与 Google Tasks(GTask)进行交互的客户端类,负责处理登录、任务和任务列表的创建、更新、移动、删除以及获取等操作 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"; - + // 单例模式实例 private static GTaskClient mInstance = null; - + // HTTP 客户端实例,用于发送 HTTP 请求 private DefaultHttpClient mHttpClient; - + // 当前使用的获取任务数据的 URL private String mGetUrl; - + // 当前使用的提交任务操作的 URL private String mPostUrl; + // 客户端版本号 private long mClientVersion; - + // 登录状态 private boolean mLoggedin; - + // 上次登录时间 private long mLastLoginTime; - + // 操作 ID,用于唯一标识每个操作 private int mActionId; - + // 同步的 Google 账户 private Account mAccount; - + // 用于存储待提交的更新操作数组 private JSONArray mUpdateArray; - + // 私有构造函数,初始化成员变量 private GTaskClient() { mHttpClient = null; mGetUrl = GTASK_GET_URL; @@ -101,23 +103,21 @@ public class GTaskClient { mAccount = null; mUpdateArray = null; } - + // 单例模式获取实例的方法 public static synchronized GTaskClient getInstance() { if (mInstance == null) { mInstance = new GTaskClient(); } return mInstance; } - + // 登录方法,根据账户状态和设置进行登录操作 public boolean login(Activity activity) { - // we suppose that the cookie would expire after 5 minutes - // then we need to re-login + // 假设 cookie 在 5 分钟后过期,需要重新登录 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))) { @@ -130,13 +130,13 @@ public class GTaskClient { } mLastLoginTime = System.currentTimeMillis(); + // 登录 Google 账户获取授权令牌 String authToken = loginGoogleAccount(activity, false); if (authToken == null) { Log.e(TAG, "login google account failed"); return false; } - - // login with custom domain if necessary + // 如果账户不是 gmail.com 或 googlemail.com 结尾,使用自定义域名登录 GTask if (!(mAccount.name.toLowerCase().endsWith("gmail.com") || mAccount.name.toLowerCase() .endsWith("googlemail.com"))) { StringBuilder url = new StringBuilder(GTASK_URL).append("a/"); @@ -150,8 +150,8 @@ public class GTaskClient { mLoggedin = true; } } - - // try to login with google official url + // 使用 Google 官方 URL 登录 GTask + if (!mLoggedin) { mGetUrl = GTASK_GET_URL; mPostUrl = GTASK_POST_URL; @@ -159,21 +159,23 @@ public class GTaskClient { return false; } } - + mLoggedin = true; return true; } - + // 登录 Google 账户的私有方法 private String loginGoogleAccount(Activity activity, boolean invalidateToken) { String authToken; + // 获取账户管理器实例 AccountManager accountManager = AccountManager.get(activity); + // 获取所有 Google 账户 Account[] accounts = accountManager.getAccountsByType("com.google"); if (accounts.length == 0) { Log.e(TAG, "there is no available google account"); return null; } - + // 获取同步账户名称 String accountName = NotesPreferenceActivity.getSyncAccountName(activity); Account account = null; for (Account a : accounts) { @@ -188,8 +190,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); try { @@ -206,9 +207,10 @@ public class GTaskClient { return authToken; } - + // 尝试登录 GTask 的私有方法 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,10 +226,11 @@ public class GTaskClient { } return true; } - + // 登录 GTask 的私有方法,获取客户端版本号和设置 HTTP 客户端相关参数 private boolean loginGtask(String authToken) { int timeoutConnection = 10000; int timeoutSocket = 15000; + // 设置 HTTP 请求参数 HttpParams httpParameters = new BasicHttpParams(); HttpConnectionParams.setConnectionTimeout(httpParameters, timeoutConnection); HttpConnectionParams.setSoTimeout(httpParameters, timeoutSocket); @@ -236,14 +239,14 @@ public class GTaskClient { mHttpClient.setCookieStore(localBasicCookieStore); HttpProtocolParams.setUseExpectContinue(mHttpClient.getParams(), false); - // login gtask + // 登录 GTask try { String loginUrl = mGetUrl + "?auth=" + authToken; HttpGet httpGet = new HttpGet(loginUrl); HttpResponse response = null; response = mHttpClient.execute(httpGet); - // get the cookie now + // 获取登录后的 cookie List cookies = mHttpClient.getCookieStore().getCookies(); boolean hasAuthCookie = false; for (Cookie cookie : cookies) { @@ -255,7 +258,7 @@ public class GTaskClient { Log.w(TAG, "it seems that there is no auth cookie"); } - // get the client version + // 获取客户端版本号 String resString = getResponseContent(response.getEntity()); String jsBegin = "_setup("; String jsEnd = ")}"; @@ -279,18 +282,18 @@ public class GTaskClient { return true; } - + // 获取操作 ID 的方法,每次调用后自增 private int getActionId() { return mActionId++; } - + // 创建用于提交任务操作的 HttpPost 对象的方法 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; } - + // 从 HTTP 响应实体中获取响应内容的方法,处理不同的编码格式 private String getResponseContent(HttpEntity entity) throws IOException { String contentEncoding = null; if (entity.getContentEncoding() != null) { @@ -322,7 +325,7 @@ public class GTaskClient { input.close(); } } - + // 提交 POST 请求的方法,将 JSON 数据发送到 GTask 服务器并处理响应 private JSONObject postRequest(JSONObject js) throws NetworkFailureException { if (!mLoggedin) { Log.e(TAG, "please login first"); @@ -336,7 +339,7 @@ public class GTaskClient { UrlEncodedFormEntity entity = new UrlEncodedFormEntity(list, "UTF-8"); httpPost.setEntity(entity); - // execute the post + // 执行 POST 请求 HttpResponse response = mHttpClient.execute(httpPost); String jsString = getResponseContent(response.getEntity()); return new JSONObject(jsString); @@ -359,21 +362,21 @@ public class GTaskClient { throw new ActionFailureException("error occurs when posting request"); } } - + // 创建任务的方法,将任务创建操作提交到 GTask 服务器 public void createTask(Task task) throws NetworkFailureException { commitUpdate(); try { JSONObject jsPost = new JSONObject(); JSONArray actionList = new JSONArray(); - // action_list + // 添加任务创建操作到操作列表 actionList.put(task.getCreateAction(getActionId())); jsPost.put(GTaskStringUtils.GTASK_JSON_ACTION_LIST, actionList); - // client_version + // 设置客户端版本号 jsPost.put(GTaskStringUtils.GTASK_JSON_CLIENT_VERSION, mClientVersion); - // post + // 提交请求并处理响应,设置任务的全局 ID JSONObject jsResponse = postRequest(jsPost); JSONObject jsResult = (JSONObject) jsResponse.getJSONArray( GTaskStringUtils.GTASK_JSON_RESULTS).get(0); @@ -385,21 +388,21 @@ public class GTaskClient { throw new ActionFailureException("create task: handing jsonobject failed"); } } - + // 创建任务列表的方法,将任务列表创建操作提交到 GTask 服务器 public void createTaskList(TaskList tasklist) throws NetworkFailureException { commitUpdate(); try { JSONObject jsPost = new JSONObject(); JSONArray actionList = new JSONArray(); - // action_list + // 添加任务列表创建操作到操作列表 actionList.put(tasklist.getCreateAction(getActionId())); jsPost.put(GTaskStringUtils.GTASK_JSON_ACTION_LIST, actionList); - // client version + // 设置客户端版本号 jsPost.put(GTaskStringUtils.GTASK_JSON_CLIENT_VERSION, mClientVersion); - // post + // 提交请求并处理响应,设置任务列表的全局 ID JSONObject jsResponse = postRequest(jsPost); JSONObject jsResult = (JSONObject) jsResponse.getJSONArray( GTaskStringUtils.GTASK_JSON_RESULTS).get(0); @@ -411,16 +414,16 @@ public class GTaskClient { throw new ActionFailureException("create tasklist: handing jsonobject failed"); } } - + // 提交更新操作的方法,将存储的更新操作数组提交到 GTask 服务器 public void commitUpdate() throws NetworkFailureException { if (mUpdateArray != null) { try { JSONObject jsPost = new JSONObject(); - // action_list + // 设置操作列表 jsPost.put(GTaskStringUtils.GTASK_JSON_ACTION_LIST, mUpdateArray); - // client_version + // 设置客户端版本号 jsPost.put(GTaskStringUtils.GTASK_JSON_CLIENT_VERSION, mClientVersion); postRequest(jsPost); @@ -432,11 +435,10 @@ public class GTaskClient { } } } - + // 添加更新节点操作的方法,将节点更新操作添加到更新操作数组 public void addUpdateNode(Node node) throws NetworkFailureException { if (node != null) { - // too many update items may result in an error - // set max to 10 items + // 为避免更新项过多导致错误,设置最大更新项为 10 if (mUpdateArray != null && mUpdateArray.length() > 10) { commitUpdate(); } @@ -446,81 +448,98 @@ public class GTaskClient { mUpdateArray.put(node.getUpdateAction(getActionId())); } } - + // 移动任务的方法,将任务移动操作提交到 GTask 服务器 public void moveTask(Task task, TaskList preParent, TaskList curParent) throws NetworkFailureException { + // 先提交之前可能存在的更新操作 commitUpdate(); try { JSONObject jsPost = new JSONObject(); JSONArray actionList = new JSONArray(); JSONObject action = new JSONObject(); - // action_list + // 设置操作类型为移动任务 action.put(GTaskStringUtils.GTASK_JSON_ACTION_TYPE, GTaskStringUtils.GTASK_JSON_ACTION_TYPE_MOVE); - action.put(GTaskStringUtils.GTASK_JSON_ACTION_ID, getActionId()); - action.put(GTaskStringUtils.GTASK_JSON_ID, task.getGid()); - if (preParent == curParent && task.getPriorSibling() != null) { + // 设置操作 ID + action.put(GTaskStringUtils.GTASK_JSON_ACTION_ID, getActionId()); + // 设置要移动的任务的 ID + action.put(GTaskStringUtils.GTASK_JSON_ID, task.getGid()); + // 如果源任务列表和目标任务列表相同且任务有前驱兄弟节点,则设置前驱兄弟节点 ID + if (preParent == curParent && task.getPriorSibling() != null) { // put prioring_sibing_id only if moving within the tasklist and // it is not the first one action.put(GTaskStringUtils.GTASK_JSON_PRIOR_SIBLING_ID, task.getPriorSibling()); } + // 设置源任务列表 ID action.put(GTaskStringUtils.GTASK_JSON_SOURCE_LIST, preParent.getGid()); + // 设置目标父任务列表 ID action.put(GTaskStringUtils.GTASK_JSON_DEST_PARENT, curParent.getGid()); + // 如果源任务列表和目标任务列表不同,则设置目标任务列表 ID if (preParent != curParent) { // put the dest_list only if moving between tasklists action.put(GTaskStringUtils.GTASK_JSON_DEST_LIST, curParent.getGid()); } + // 将操作添加到操作列表 actionList.put(action); + // 将操作列表添加到提交的 JSON 对象中 jsPost.put(GTaskStringUtils.GTASK_JSON_ACTION_LIST, actionList); - // client_version + // 设置客户端版本号 jsPost.put(GTaskStringUtils.GTASK_JSON_CLIENT_VERSION, mClientVersion); - + // 提交请求 postRequest(jsPost); } catch (JSONException e) { Log.e(TAG, e.toString()); e.printStackTrace(); + // 如果 JSON 处理出现问题,抛出操作失败异常 throw new ActionFailureException("move task: handing jsonobject failed"); } } - + // 删除节点的方法,接受要删除的节点作为参数,并可能抛出网络异常 public void deleteNode(Node node) throws NetworkFailureException { + // 先提交之前可能存在的更新操作 commitUpdate(); try { JSONObject jsPost = new JSONObject(); JSONArray actionList = new JSONArray(); - // action_list + // 设置节点为已删除状态 node.setDeleted(true); + // 将节点的更新操作添加到操作列表 actionList.put(node.getUpdateAction(getActionId())); + // 将操作列表添加到提交的 JSON 对象中 jsPost.put(GTaskStringUtils.GTASK_JSON_ACTION_LIST, actionList); - // client_version + // 设置客户端版本号 jsPost.put(GTaskStringUtils.GTASK_JSON_CLIENT_VERSION, mClientVersion); - + // 提交请求并删除后将更新数组置空 postRequest(jsPost); mUpdateArray = null; } catch (JSONException e) { Log.e(TAG, e.toString()); e.printStackTrace(); + // 如果 JSON 处理出现问题,抛出操作失败异常 throw new ActionFailureException("delete node: handing jsonobject failed"); } } - + // 获取所有任务列表的方法,可能抛出网络异常 public JSONArray getTaskLists() throws NetworkFailureException { + // 检查是否已登录,未登录则抛出异常 if (!mLoggedin) { Log.e(TAG, "please login first"); throw new ActionFailureException("not logged in"); } try { + // 创建 HTTP GET 请求 HttpGet httpGet = new HttpGet(mGetUrl); HttpResponse response = null; + // 执行请求 response = mHttpClient.execute(httpGet); - // get the task list + // 获取响应内容并解析出任务列表的 JSON 数组 String resString = getResponseContent(response.getEntity()); String jsBegin = "_setup("; String jsEnd = ")}"; @@ -535,50 +554,60 @@ public class GTaskClient { } catch (ClientProtocolException e) { Log.e(TAG, e.toString()); e.printStackTrace(); + // 如果 HTTP 协议出现问题,抛出网络异常 throw new NetworkFailureException("gettasklists: httpget failed"); } catch (IOException e) { Log.e(TAG, e.toString()); e.printStackTrace(); + // 如果 I/O 出现问题,抛出网络异常 throw new NetworkFailureException("gettasklists: httpget failed"); } catch (JSONException e) { Log.e(TAG, e.toString()); e.printStackTrace(); + // 如果 JSON 解析出现问题,抛出操作失败异常 throw new ActionFailureException("get task lists: handing jasonobject failed"); } } - + // 根据任务列表 ID 获取任务列表的方法,可能抛出网络异常 public JSONArray getTaskList(String listGid) throws NetworkFailureException { + // 先提交之前可能存在的更新操作 commitUpdate(); try { JSONObject jsPost = new JSONObject(); JSONArray actionList = new JSONArray(); JSONObject action = new JSONObject(); - // action_list + // 设置操作类型为获取所有任务 action.put(GTaskStringUtils.GTASK_JSON_ACTION_TYPE, GTaskStringUtils.GTASK_JSON_ACTION_TYPE_GETALL); - action.put(GTaskStringUtils.GTASK_JSON_ACTION_ID, getActionId()); - action.put(GTaskStringUtils.GTASK_JSON_LIST_ID, listGid); - action.put(GTaskStringUtils.GTASK_JSON_GET_DELETED, false); - actionList.put(action); - jsPost.put(GTaskStringUtils.GTASK_JSON_ACTION_LIST, actionList); - - // client_version + // 设置操作 ID + action.put(GTaskStringUtils.GTASK_JSON_ACTION_ID, getActionId()); + // 设置任务列表 ID + action.put(GTaskStringUtils.GTASK_JSON_LIST_ID, listGid); + // 设置不获取已删除任务 + action.put(GTaskStringUtils.GTASK_JSON_GET_DELETED, false); + // 将操作添加到操作列表 + actionList.put(action); + // 将操作列表添加到提交的 JSON 对象中 + jsPost.put(GTaskStringUtils.GTASK_JSON_ACTION_LIST, actionList); + + // 设置客户端版本号 jsPost.put(GTaskStringUtils.GTASK_JSON_CLIENT_VERSION, mClientVersion); - + // 提交请求并获取响应中的任务列表 JSON 数组 JSONObject jsResponse = postRequest(jsPost); return jsResponse.getJSONArray(GTaskStringUtils.GTASK_JSON_TASKS); } catch (JSONException e) { Log.e(TAG, e.toString()); e.printStackTrace(); + // 如果 JSON 处理出现问题,抛出操作失败异常 throw new ActionFailureException("get task list: handing jsonobject failed"); } } - + // 获取同步账户的方法 public Account getSyncAccount() { return mAccount; } - + // 重置更新数组的方法,将更新数组置空 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..c32e8cf 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 @@ -47,10 +47,10 @@ import java.util.HashSet; import java.util.Iterator; import java.util.Map; - +// GTaskManager类用于管理与Google Task的同步操作以及本地数据和远程数据的交互 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; @@ -60,33 +60,33 @@ public class GTaskManager { public static final int STATE_SYNC_IN_PROGRESS = 3; public static final int STATE_SYNC_CANCELLED = 4; - + // 单例模式,确保只有一个GTaskManager实例 private static GTaskManager mInstance = null; - + // 用于获取认证令牌等操作的Activity private Activity mActivity; - + // 应用上下文 private Context mContext; - + // 用于访问内容提供者的ContentResolver private ContentResolver mContentResolver; - + // 表示是否正在同步 private boolean mSyncing; - + // 表示同步是否已取消 private boolean mCancelled; - + // 存储Google Task列表的哈希表,键为任务列表的全局唯一标识符(gid),值为TaskList对象 private HashMap mGTaskListHashMap; - + // 存储Google Task节点(任务或任务列表)的哈希表,键为节点的gid,值为Node对象 private HashMap mGTaskHashMap; - + // 存储元数据的哈希表,键为相关的gid,值为MetaData对象 private HashMap mMetaHashMap; - + // 元数据列表 private TaskList mMetaList; - + // 存储本地已删除笔记的ID集合 private HashSet mLocalDeleteIdMap; - + // 用于将Google Task的gid映射到本地笔记的ID private HashMap mGidToNid; - + // 用于将本地笔记的ID映射到Google Task的gid private HashMap mNidToGid; - + // 私有构造函数,用于初始化各种数据结构和变量 private GTaskManager() { mSyncing = false; mCancelled = false; @@ -98,21 +98,22 @@ public class GTaskManager { mGidToNid = new HashMap(); mNidToGid = new HashMap(); } - + // 获取GTaskManager的单例实例 public static synchronized GTaskManager getInstance() { if (mInstance == null) { mInstance = new GTaskManager(); } return mInstance; } - + // 设置Activity上下文,主要用于获取认证令牌 public synchronized void setActivityContext(Activity activity) { // used for getting authtoken mActivity = activity; } - + // 执行同步操作的主要方法 public int sync(Context context, GTaskASyncTask asyncTask) { if (mSyncing) { + // 如果同步已经在进行中,记录日志并返回相应状态码 Log.d(TAG, "Sync is in progress"); return STATE_SYNC_IN_PROGRESS; } @@ -120,6 +121,7 @@ public class GTaskManager { mContentResolver = mContext.getContentResolver(); mSyncing = true; mCancelled = false; + // 清除之前同步过程中存储的数据 mGTaskListHashMap.clear(); mGTaskHashMap.clear(); mMetaHashMap.clear(); @@ -131,24 +133,26 @@ public class GTaskManager { GTaskClient client = GTaskClient.getInstance(); client.resetUpdateArray(); - // login google task + // 登录Google Task if (!mCancelled) { if (!client.login(mActivity)) { throw new NetworkFailureException("login google task failed"); } } - // get the task list from google + // 获取Google Task列表并进行初始化 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(); } catch (NetworkFailureException e) { + // 处理网络故障异常,记录日志并返回相应状态码 Log.e(TAG, e.toString()); return STATE_NETWORK_ERROR; } catch (ActionFailureException e) { + // 处理操作失败异常,记录日志并返回相应状态码 Log.e(TAG, e.toString()); return STATE_INTERNAL_ERROR; } catch (Exception e) { @@ -156,6 +160,7 @@ public class GTaskManager { e.printStackTrace(); return STATE_INTERNAL_ERROR; } finally { + // 无论同步是否成功,都清除相关数据并重置同步状态 mGTaskListHashMap.clear(); mGTaskHashMap.clear(); mMetaHashMap.clear(); @@ -164,7 +169,7 @@ public class GTaskManager { mNidToGid.clear(); mSyncing = false; } - + // 初始化Google Task列表的方法 return mCancelled ? STATE_SYNC_CANCELLED : STATE_SUCCESS; } @@ -173,21 +178,22 @@ public class GTaskManager { return; GTaskClient client = GTaskClient.getInstance(); try { + // 从Google获取任务列表的JSON数组 JSONArray jsTaskLists = client.getTaskLists(); - // init meta list first + // 初始化元数据列表 mMetaList = null; for (int i = 0; i < jsTaskLists.length(); i++) { JSONObject object = jsTaskLists.getJSONObject(i); String gid = object.getString(GTaskStringUtils.GTASK_JSON_ID); String name = object.getString(GTaskStringUtils.GTASK_JSON_NAME); - + // 查找元数据文件夹 if (name .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++) { object = (JSONObject) jsMetas.getJSONObject(j); @@ -203,7 +209,7 @@ public class GTaskManager { } } - // create meta list if not existed + // 如果元数据列表不存在,则创建一个新的元数据列表 if (mMetaList == null) { mMetaList = new TaskList(); mMetaList.setName(GTaskStringUtils.MIUI_FOLDER_PREFFIX @@ -211,12 +217,12 @@ public class GTaskManager { GTaskClient.getInstance().createTaskList(mMetaList); } - // init task list + // 初始化任务列表和任务 for (int i = 0; i < jsTaskLists.length(); i++) { JSONObject object = jsTaskLists.getJSONObject(i); String gid = object.getString(GTaskStringUtils.GTASK_JSON_ID); String name = object.getString(GTaskStringUtils.GTASK_JSON_NAME); - + // 处理以特定前缀开头且不是元数据文件夹的任务列表 if (name.startsWith(GTaskStringUtils.MIUI_FOLDER_PREFFIX) && !name.equals(GTaskStringUtils.MIUI_FOLDER_PREFFIX + GTaskStringUtils.FOLDER_META)) { @@ -225,7 +231,7 @@ public class GTaskManager { mGTaskListHashMap.put(gid, tasklist); mGTaskHashMap.put(gid, tasklist); - // load tasks + // 加载任务列表中的任务 JSONArray jsTasks = client.getTaskList(gid); for (int j = 0; j < jsTasks.length(); j++) { object = (JSONObject) jsTasks.getJSONObject(j); @@ -241,12 +247,13 @@ public class GTaskManager { } } } catch (JSONException e) { + // 处理JSON解析异常,记录日志、打印堆栈信息并抛出操作失败异常 Log.e(TAG, e.toString()); e.printStackTrace(); throw new ActionFailureException("initGTaskList: handing JSONObject failed"); } } - + // 执行内容同步的方法 private void syncContent() throws NetworkFailureException { int syncType; Cursor c = null; @@ -259,7 +266,7 @@ public class GTaskManager { return; } - // for local deleted note + // 处理本地已删除的笔记 try { c = mContentResolver.query(Notes.CONTENT_NOTE_URI, SqlNote.PROJECTION_NOTE, "(type<>? AND parent_id=?)", new String[] { @@ -286,10 +293,10 @@ public class GTaskManager { } } - // sync folder first + // 同步文件夹 syncFolder(); - // for note existing in database + // 处理数据库中存在的笔记 try { c = mContentResolver.query(Notes.CONTENT_NOTE_URI, SqlNote.PROJECTION_NOTE, "(type=? AND parent_id<>?)", new String[] { @@ -306,10 +313,10 @@ 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; } } @@ -326,14 +333,14 @@ public class GTaskManager { } } - // go through remaining items + // 处理剩余的未同步节点 Iterator> iter = mGTaskHashMap.entrySet().iterator(); while (iter.hasNext()) { Map.Entry entry = iter.next(); node = entry.getValue(); doContentSync(Node.SYNC_ACTION_ADD_LOCAL, node, null); } - + // 检查同步是否取消,如果未取消则执行本地删除操作和提交更新操作 // mCancelled can be set by another thread, so we neet to check one by // one // clear local delete table @@ -342,15 +349,14 @@ public class GTaskManager { throw new ActionFailureException("failed to batch-delete local deleted notes"); } } - - // refresh local sync id + // 检查同步是否取消,如果未取消则提交更新并刷新本地同步ID if (!mCancelled) { GTaskClient.getInstance().commitUpdate(); refreshLocalSyncId(); } } - + // 同步文件夹的方法 private void syncFolder() throws NetworkFailureException { Cursor c = null; String gid; @@ -361,7 +367,7 @@ public class GTaskManager { return; } - // for root folder + // 处理根文件夹 try { c = mContentResolver.query(ContentUris.withAppendedId(Notes.CONTENT_NOTE_URI, Notes.ID_ROOT_FOLDER), SqlNote.PROJECTION_NOTE, null, null, null); @@ -373,7 +379,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)) doContentSync(Node.SYNC_ACTION_UPDATE_REMOTE, node, c); @@ -390,7 +396,7 @@ public class GTaskManager { } } - // for call-note folder + // 处理通话记录文件夹 try { c = mContentResolver.query(Notes.CONTENT_NOTE_URI, SqlNote.PROJECTION_NOTE, "(_id=?)", new String[] { @@ -404,8 +410,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( GTaskStringUtils.MIUI_FOLDER_PREFFIX + GTaskStringUtils.FOLDER_CALL_NOTE)) @@ -424,7 +429,7 @@ public class GTaskManager { } } - // for local existing folders + // 处理本地现有文件夹 try { c = mContentResolver.query(Notes.CONTENT_NOTE_URI, SqlNote.PROJECTION_NOTE, "(type=? AND parent_id<>?)", new String[] { @@ -441,10 +446,10 @@ 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; } } @@ -460,7 +465,7 @@ public class GTaskManager { } } - // for remote add folders + // 处理远程新增文件夹 Iterator> iter = mGTaskListHashMap.entrySet().iterator(); while (iter.hasNext()) { Map.Entry entry = iter.next(); @@ -475,7 +480,7 @@ public class GTaskManager { if (!mCancelled) GTaskClient.getInstance().commitUpdate(); } - + // 根据同步类型执行具体同步操作的方法 private void doContentSync(int syncType, Node node, Cursor c) throws NetworkFailureException { if (mCancelled) { return; @@ -484,12 +489,15 @@ public class GTaskManager { MetaData meta; switch (syncType) { case Node.SYNC_ACTION_ADD_LOCAL: + // 本地新增节点操作 addLocalNode(node); break; case Node.SYNC_ACTION_ADD_REMOTE: + // 远程新增节点操作 addRemoteNode(node, c); break; case Node.SYNC_ACTION_DEL_LOCAL: + // 本地删除节点操作 meta = mMetaHashMap.get(c.getString(SqlNote.GTASK_ID_COLUMN)); if (meta != null) { GTaskClient.getInstance().deleteNode(meta); @@ -497,6 +505,7 @@ public class GTaskManager { mLocalDeleteIdMap.add(c.getLong(SqlNote.ID_COLUMN)); break; case Node.SYNC_ACTION_DEL_REMOTE: + // 远程删除节点操作 meta = mMetaHashMap.get(node.getGid()); if (meta != null) { GTaskClient.getInstance().deleteNode(meta); @@ -504,24 +513,28 @@ public class GTaskManager { GTaskClient.getInstance().deleteNode(node); break; case Node.SYNC_ACTION_UPDATE_LOCAL: + // 本地更新节点操作 updateLocalNode(node, c); break; case Node.SYNC_ACTION_UPDATE_REMOTE: + // 远程更新节点操作 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); break; case Node.SYNC_ACTION_NONE: + // 无需同步操作 break; case Node.SYNC_ACTION_ERROR: default: + // 未知同步操作类型,抛出异常 throw new ActionFailureException("unkown sync action type"); } } - + // 本地新增节点的具体实现方法 private void addLocalNode(Node node) throws NetworkFailureException { if (mCancelled) { return; @@ -549,7 +562,7 @@ public class GTaskManager { if (note.has(NoteColumns.ID)) { long id = note.getLong(NoteColumns.ID); if (DataUtils.existInNoteDatabase(mContentResolver, id)) { - // the id is not available, have to create a new one + // 如果本地数据库中已存在该笔记ID,则移除该ID,重新生成新的笔记 note.remove(NoteColumns.ID); } } @@ -562,8 +575,7 @@ public class GTaskManager { if (data.has(DataColumns.ID)) { long dataId = data.getLong(DataColumns.ID); if (DataUtils.existInDataDatabase(mContentResolver, dataId)) { - // the data id is not available, have to create - // a new one + // 如果本地数据库中已存在该数据ID,则移除该ID,重新生成新的数据 data.remove(DataColumns.ID); } } @@ -584,25 +596,25 @@ public class GTaskManager { sqlNote.setParentId(parentId.longValue()); } - // create the local node + // 创建本地节点 sqlNote.setGtaskId(node.getGid()); sqlNote.commit(false); - // update gid-nid mapping + // 更新gid - nid映射 mGidToNid.put(node.getGid(), sqlNote.getId()); mNidToGid.put(sqlNote.getId(), node.getGid()); - // update meta + // 更新远程元数据 updateRemoteMeta(node.getGid(), sqlNote); } - + // 本地更新节点的具体实现方法 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()); @@ -615,10 +627,10 @@ public class GTaskManager { sqlNote.setParentId(parentId.longValue()); sqlNote.commit(true); - // update meta info + // 更新远程元数据 updateRemoteMeta(node.getGid(), sqlNote); } - + // 远程新增节点的具体实现方法 private void addRemoteNode(Node node, Cursor c) throws NetworkFailureException { if (mCancelled) { return; @@ -627,7 +639,7 @@ public class GTaskManager { SqlNote sqlNote = new SqlNote(mContext, c); Node n; - // update remotely + // 远程更新操作 if (sqlNote.isNoteType()) { Task task = new Task(); task.setContentByLocalJSON(sqlNote.getContent()); @@ -642,12 +654,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 + // 检查文件夹是否已存在,如果存在则获取对应的TaskList对象 String folderName = GTaskStringUtils.MIUI_FOLDER_PREFFIX; if (sqlNote.getId() == Notes.ID_ROOT_FOLDER) folderName += GTaskStringUtils.FOLDER_DEFAULT; @@ -671,7 +683,7 @@ public class GTaskManager { } } - // no match we can add now + // 如果未找到匹配的文件夹,则创建新的TaskList对象 if (tasklist == null) { tasklist = new TaskList(); tasklist.setContentByLocalJSON(sqlNote.getContent()); @@ -681,17 +693,17 @@ public class GTaskManager { n = (Node) tasklist; } - // update local note + // 更新本地笔记 sqlNote.setGtaskId(n.getGid()); sqlNote.commit(false); sqlNote.resetLocalModified(); sqlNote.commit(true); - // gid-id mapping + // 更新gid - id映射 mGidToNid.put(n.getGid(), sqlNote.getId()); mNidToGid.put(sqlNote.getId(), n.getGid()); } - + // 远程更新节点的具体实现方法 private void updateRemoteNode(Node node, Cursor c) throws NetworkFailureException { if (mCancelled) { return; @@ -699,11 +711,11 @@ public class GTaskManager { 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 @@ -725,11 +737,11 @@ public class GTaskManager { } } - // clear local modified flag + // 清除本地修改标志 sqlNote.resetLocalModified(); sqlNote.commit(true); } - + // 更新远程元数据的方法 private void updateRemoteMeta(String gid, SqlNote sqlNote) throws NetworkFailureException { if (sqlNote != null && sqlNote.isNoteType()) { MetaData metaData = mMetaHashMap.get(gid); @@ -745,13 +757,13 @@ public class GTaskManager { } } } - + // 刷新本地同步ID的方法 private void refreshLocalSyncId() throws NetworkFailureException { if (mCancelled) { return; } - // get the latest gtask list + // 重新获取最新的Google Task列表,清除之前的数据缓存 mGTaskHashMap.clear(); mGTaskListHashMap.clear(); mMetaHashMap.clear(); @@ -789,11 +801,11 @@ public class GTaskManager { } } } - + // 获取同步账户名称的方法 public String getSyncAccount() { return GTaskClient.getInstance().getSyncAccount().name; } - + // 取消同步操作的方法 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..0f305c6 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,70 +22,89 @@ import android.content.Context; import android.content.Intent; import android.os.Bundle; import android.os.IBinder; - +// GTaskSyncService类是一个Android服务,用于管理Google Task的同步任务 public class GTaskSyncService extends Service { + // 定义用于表示同步操作类型的字符串常量 public final static String ACTION_STRING_NAME = "sync_action_type"; - + // 表示开始同步的操作类型值 public final static int ACTION_START_SYNC = 0; - + // 表示取消同步的操作类型值 public final static int ACTION_CANCEL_SYNC = 1; - + // 表示无效操作类型的默认值 public final static int ACTION_INVALID = 2; - + // 定义用于广播同步服务相关信息的动作名称 public final static String GTASK_SERVICE_BROADCAST_NAME = "net.micode.notes.gtask.remote.gtask_sync_service"; - + // 用于在广播中表示是否正在同步的键 public final static String GTASK_SERVICE_BROADCAST_IS_SYNCING = "isSyncing"; - + // 用于在广播中表示同步进度消息的键 public final static String GTASK_SERVICE_BROADCAST_PROGRESS_MSG = "progressMsg"; - + // 存储当前正在执行的同步任务实例,初始化为null private static GTaskASyncTask mSyncTask = null; - + // 存储同步进度消息,初始化为空字符串 private static String mSyncProgress = ""; - + // 启动同步任务的方法 private void startSync() { + // 如果当前没有正在执行的同步任务 if (mSyncTask == null) { + // 创建一个新的GTaskASyncTask实例,并传入当前服务实例和一个完成监听器 mSyncTask = new GTaskASyncTask(this, new GTaskASyncTask.OnCompleteListener() { + // 当同步任务完成时调用的方法 public void onComplete() { + // 将当前同步任务实例设置为null,表示任务已完成 mSyncTask = null; + // 发送一个空消息的广播 sendBroadcast(""); + // 停止当前服务 stopSelf(); } }); + // 发送一个空消息的广播,可能用于通知相关组件同步任务即将开始 sendBroadcast(""); + // 执行同步任务 mSyncTask.execute(); } } - + // 取消同步任务的方法 private void cancelSync() { + // 如果当前有正在执行的同步任务 if (mSyncTask != null) { + // 调用同步任务的取消同步方法 mSyncTask.cancelSync(); } } - + // 服务创建时调用的方法,将同步任务实例设置为null @Override public void onCreate() { mSyncTask = null; } - + // 服务接收到启动命令时调用的方法 @Override public int onStartCommand(Intent intent, int flags, int startId) { + // 获取启动意图中的额外数据 Bundle bundle = intent.getExtras(); + // 如果额外数据不为空且包含同步操作类型键 if (bundle != null && bundle.containsKey(ACTION_STRING_NAME)) { + // 根据操作类型值执行相应操作 switch (bundle.getInt(ACTION_STRING_NAME, ACTION_INVALID)) { case ACTION_START_SYNC: + // 启动同步任务 startSync(); break; case ACTION_CANCEL_SYNC: + // 取消同步任务 cancelSync(); break; default: break; } + // 返回粘性服务标志,表示服务在被异常终止后会尝试重新启动 return START_STICKY; } + // 如果不满足上述条件,则调用父类的onStartCommand方法 return super.onStartCommand(intent, flags, startId); } + // 系统内存不足时调用的方法,如果有正在执行的同步任务则取消它 @Override public void onLowMemory() { if (mSyncTask != null) { @@ -93,35 +112,47 @@ public class GTaskSyncService extends Service { } } + // 服务绑定方法,返回null表示不支持绑定操作 public IBinder onBind(Intent intent) { return null; } + // 发送广播的方法,用于向感兴趣的组件发送同步服务的相关信息 public void sendBroadcast(String msg) { + // 更新同步进度消息 mSyncProgress = msg; + // 创建一个意图,指定广播动作名称 Intent intent = new Intent(GTASK_SERVICE_BROADCAST_NAME); + // 将是否正在同步的状态添加到意图的额外数据中 intent.putExtra(GTASK_SERVICE_BROADCAST_IS_SYNCING, mSyncTask != null); + // 将同步进度消息添加到意图的额外数据中 intent.putExtra(GTASK_SERVICE_BROADCAST_PROGRESS_MSG, msg); + // 发送广播 sendBroadcast(intent); } - + // 静态方法,用于启动同步任务,在Activity中调用 public static void startSync(Activity activity) { + // 设置同步任务的Activity上下文 GTaskManager.getInstance().setActivityContext(activity); + // 创建一个启动当前服务的意图,并指定操作类型为开始同步 Intent intent = new Intent(activity, GTaskSyncService.class); intent.putExtra(GTaskSyncService.ACTION_STRING_NAME, GTaskSyncService.ACTION_START_SYNC); + // 启动服务 activity.startService(intent); } - + // 静态方法,用于取消同步任务,在Context中调用 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); } - + // 静态方法,用于检查是否正在进行同步任务 public static boolean isSyncing() { return mSyncTask != null; } - + // 静态方法,用于获取同步进度消息 public static String getProgressString() { return mSyncProgress; }