From c7d0138c6097a96675f0fb6305e503a0467408ec Mon Sep 17 00:00:00 2001 From: zhangqing <1770666340@qq.com> Date: Tue, 23 Dec 2025 20:28:03 +0800 Subject: [PATCH] 1 --- .../notes/gtask/remote/GTaskManager.java | 357 +++++++++++------- 1 file changed, 224 insertions(+), 133 deletions(-) diff --git a/src/小米标签代码/src/net/micode/notes/gtask/remote/GTaskManager.java b/src/小米标签代码/src/net/micode/notes/gtask/remote/GTaskManager.java index d2b4082..080c0c8 100644 --- a/src/小米标签代码/src/net/micode/notes/gtask/remote/GTaskManager.java +++ b/src/小米标签代码/src/net/micode/notes/gtask/remote/GTaskManager.java @@ -1,3 +1,5 @@ +[file name]: GTaskManager.java +[file content begin] /* * Copyright (c) 2010-2011, The MiCode Open Source Community (www.micode.net) * @@ -47,46 +49,46 @@ import java.util.HashSet; import java.util.Iterator; import java.util.Map; - +/** + * Google Tasks 同步管理器 + * 负责本地笔记数据和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; + // 同步状态常量 + 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; // 同步已取消 + // 单例实例 private static GTaskManager mInstance = null; - private Activity mActivity; - - private Context mContext; - - private ContentResolver mContentResolver; - - private boolean mSyncing; - - private boolean mCancelled; - - private HashMap mGTaskListHashMap; - - private HashMap mGTaskHashMap; - - private HashMap mMetaHashMap; - - private TaskList mMetaList; - - private HashSet mLocalDeleteIdMap; - - private HashMap mGidToNid; - - private HashMap mNidToGid; - + // 上下文和组件 + private Activity mActivity; // 用于获取认证令牌的Activity + private Context mContext; // 应用上下文 + private ContentResolver mContentResolver; // 内容解析器 + + // 同步状态标志 + private boolean mSyncing; // 是否正在同步 + private boolean mCancelled; // 是否已取消 + + // 数据存储结构 + private HashMap mGTaskListHashMap; // 任务列表映射(gid -> TaskList) + private HashMap mGTaskHashMap; // 任务节点映射(gid -> Node) + private HashMap mMetaHashMap; // 元数据映射(相关gid -> MetaData) + private TaskList mMetaList; // 元数据任务列表 + + // ID映射管理 + private HashSet mLocalDeleteIdMap; // 本地删除的笔记ID集合 + private HashMap mGidToNid; // Google ID到本地ID的映射 + private HashMap mNidToGid; // 本地ID到Google ID的映射 + + /** + * 私有构造函数 - 单例模式 + */ private GTaskManager() { mSyncing = false; mCancelled = false; @@ -99,6 +101,9 @@ public class GTaskManager { mNidToGid = new HashMap(); } + /** + * 获取单例实例 + */ public static synchronized GTaskManager getInstance() { if (mInstance == null) { mInstance = new GTaskManager(); @@ -106,20 +111,34 @@ public class GTaskManager { return mInstance; } + /** + * 设置Activity上下文(用于Google账户认证) + * @param activity 当前Activity + */ public synchronized void setActivityContext(Activity activity) { - // used for getting authtoken mActivity = activity; } + /** + * 主同步方法 + * @param context 上下文 + * @param asyncTask 异步任务,用于进度更新 + * @return 同步状态代码 + */ public int sync(Context context, GTaskASyncTask asyncTask) { + // 检查是否已在同步中 if (mSyncing) { Log.d(TAG, "Sync is in progress"); return STATE_SYNC_IN_PROGRESS; } + + // 初始化同步环境 mContext = context; mContentResolver = mContext.getContentResolver(); mSyncing = true; mCancelled = false; + + // 清空所有数据存储 mGTaskListHashMap.clear(); mGTaskHashMap.clear(); mMetaHashMap.clear(); @@ -131,20 +150,21 @@ public class GTaskManager { GTaskClient client = GTaskClient.getInstance(); client.resetUpdateArray(); - // login google task + // 步骤1: 登录Google Tasks if (!mCancelled) { if (!client.login(mActivity)) { throw new NetworkFailureException("login google task failed"); } } - // get the task list from google + // 步骤2: 从Google获取任务列表 asyncTask.publishProgess(mContext.getString(R.string.sync_progress_init_list)); initGTaskList(); - // do content sync work + // 步骤3: 执行内容同步 asyncTask.publishProgess(mContext.getString(R.string.sync_progress_syncing)); syncContent(); + } catch (NetworkFailureException e) { Log.e(TAG, e.toString()); return STATE_NETWORK_ERROR; @@ -156,6 +176,7 @@ public class GTaskManager { e.printStackTrace(); return STATE_INTERNAL_ERROR; } finally { + // 清理资源 mGTaskListHashMap.clear(); mGTaskHashMap.clear(); mMetaHashMap.clear(); @@ -168,26 +189,32 @@ public class GTaskManager { return mCancelled ? STATE_SYNC_CANCELLED : STATE_SUCCESS; } + /** + * 初始化Google任务列表 + * 从Google Tasks API获取所有任务列表和任务 + */ private void initGTaskList() throws NetworkFailureException { if (mCancelled) return; + GTaskClient client = GTaskClient.getInstance(); try { + // 获取所有任务列表 JSONArray jsTaskLists = client.getTaskLists(); - // init meta list first + // 1. 先初始化元数据列表(用于存储笔记的详细内容) 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)) { + // 查找元数据文件夹(特殊文件夹,存储笔记的元数据) + 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); @@ -196,6 +223,7 @@ public class GTaskManager { if (metaData.isWorthSaving()) { mMetaList.addChildTask(metaData); if (metaData.getGid() != null) { + // 以相关GID为键存储元数据 mMetaHashMap.put(metaData.getRelatedGid(), metaData); } } @@ -203,29 +231,28 @@ public class GTaskManager { } } - // create meta list if not existed + // 如果元数据列表不存在,则创建 if (mMetaList == null) { mMetaList = new TaskList(); - mMetaList.setName(GTaskStringUtils.MIUI_FOLDER_PREFFIX - + GTaskStringUtils.FOLDER_META); + mMetaList.setName(GTaskStringUtils.MIUI_FOLDER_PREFFIX + GTaskStringUtils.FOLDER_META); GTaskClient.getInstance().createTaskList(mMetaList); } - // init task list + // 2. 初始化普通任务列表(MIUI相关的文件夹) 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); + // 只处理MIUI相关的文件夹(排除元数据文件夹) if (name.startsWith(GTaskStringUtils.MIUI_FOLDER_PREFFIX) - && !name.equals(GTaskStringUtils.MIUI_FOLDER_PREFFIX - + GTaskStringUtils.FOLDER_META)) { + && !name.equals(GTaskStringUtils.MIUI_FOLDER_PREFFIX + GTaskStringUtils.FOLDER_META)) { TaskList tasklist = new TaskList(); 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++) { object = (JSONObject) jsTasks.getJSONObject(j); @@ -233,6 +260,7 @@ public class GTaskManager { Task task = new Task(); task.setContentByRemoteJSON(object); if (task.isWorthSaving()) { + // 关联元数据 task.setMetaInfo(mMetaHashMap.get(gid)); tasklist.addChildTask(task); mGTaskHashMap.put(gid, task); @@ -247,6 +275,10 @@ public class GTaskManager { } } + /** + * 同步内容 - 核心同步逻辑 + * 比较本地和远程数据,执行相应的同步操作 + */ private void syncContent() throws NetworkFailureException { int syncType; Cursor c = null; @@ -259,7 +291,7 @@ public class GTaskManager { return; } - // for local deleted note + // 阶段1: 处理本地已删除的笔记(在回收站中) try { c = mContentResolver.query(Notes.CONTENT_NOTE_URI, SqlNote.PROJECTION_NOTE, "(type<>? AND parent_id=?)", new String[] { @@ -270,10 +302,12 @@ public class GTaskManager { gid = c.getString(SqlNote.GTASK_ID_COLUMN); node = mGTaskHashMap.get(gid); if (node != null) { + // 本地已删除,需要从远程删除 mGTaskHashMap.remove(gid); doContentSync(Node.SYNC_ACTION_DEL_REMOTE, node, c); } + // 记录本地删除的ID,后续批量删除 mLocalDeleteIdMap.add(c.getLong(SqlNote.ID_COLUMN)); } } else { @@ -286,10 +320,10 @@ public class GTaskManager { } } - // sync folder first + // 阶段2: 先同步文件夹(因为笔记需要引用文件夹ID) syncFolder(); - // for note existing in database + // 阶段3: 处理数据库中存在的笔记(不在回收站中) try { c = mContentResolver.query(Notes.CONTENT_NOTE_URI, SqlNote.PROJECTION_NOTE, "(type=? AND parent_id<>?)", new String[] { @@ -300,16 +334,18 @@ public class GTaskManager { gid = c.getString(SqlNote.GTASK_ID_COLUMN); node = mGTaskHashMap.get(gid); if (node != null) { + // 双方都存在,需要确定同步类型 mGTaskHashMap.remove(gid); mGidToNid.put(gid, c.getLong(SqlNote.ID_COLUMN)); mNidToGid.put(c.getLong(SqlNote.ID_COLUMN), gid); - syncType = node.getSyncAction(c); + syncType = node.getSyncAction(c); // 根据修改时间判断同步类型 } else { + // 根据是否有gid判断是本地新增还是远程删除 if (c.getString(SqlNote.GTASK_ID_COLUMN).trim().length() == 0) { - // local add + // 本地新增(无gid) syncType = Node.SYNC_ACTION_ADD_REMOTE; } else { - // remote delete + // 远程已删除 syncType = Node.SYNC_ACTION_DEL_LOCAL; } } @@ -326,7 +362,7 @@ public class GTaskManager { } } - // go through remaining items + // 阶段4: 处理剩余的项目(远程新增,本地不存在) Iterator> iter = mGTaskHashMap.entrySet().iterator(); while (iter.hasNext()) { Map.Entry entry = iter.next(); @@ -334,23 +370,24 @@ public class GTaskManager { 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 + // 清理本地删除表 if (!mCancelled) { if (!DataUtils.batchDeleteNotes(mContentResolver, mLocalDeleteIdMap)) { throw new ActionFailureException("failed to batch-delete local deleted notes"); } } - // refresh local sync id + // 刷新本地同步ID(使用远程的最后修改时间) if (!mCancelled) { GTaskClient.getInstance().commitUpdate(); refreshLocalSyncId(); } - } + /** + * 同步文件夹 + * 先同步文件夹,因为笔记需要引用文件夹ID + */ private void syncFolder() throws NetworkFailureException { Cursor c = null; String gid; @@ -361,7 +398,7 @@ public class GTaskManager { return; } - // for root folder + // 1. 根文件夹同步 try { c = mContentResolver.query(ContentUris.withAppendedId(Notes.CONTENT_NOTE_URI, Notes.ID_ROOT_FOLDER), SqlNote.PROJECTION_NOTE, null, null, null); @@ -370,14 +407,15 @@ public class GTaskManager { gid = c.getString(SqlNote.GTASK_ID_COLUMN); node = mGTaskHashMap.get(gid); if (node != null) { + // 双方都存在 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)) + // 系统文件夹只更新远程名称(如果需要) + if (!node.getName().equals(GTaskStringUtils.MIUI_FOLDER_PREFFIX + GTaskStringUtils.FOLDER_DEFAULT)) doContentSync(Node.SYNC_ACTION_UPDATE_REMOTE, node, c); } else { + // 仅本地存在 doContentSync(Node.SYNC_ACTION_ADD_REMOTE, node, c); } } else { @@ -390,12 +428,10 @@ public class GTaskManager { } } - // for call-note folder + // 2. 通话记录文件夹同步 try { c = mContentResolver.query(Notes.CONTENT_NOTE_URI, SqlNote.PROJECTION_NOTE, "(_id=?)", - new String[] { - String.valueOf(Notes.ID_CALL_RECORD_FOLDER) - }, null); + new String[] { String.valueOf(Notes.ID_CALL_RECORD_FOLDER) }, null); if (c != null) { if (c.moveToNext()) { gid = c.getString(SqlNote.GTASK_ID_COLUMN); @@ -404,11 +440,8 @@ 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)) + // 系统文件夹只更新远程名称(如果需要) + if (!node.getName().equals(GTaskStringUtils.MIUI_FOLDER_PREFFIX + GTaskStringUtils.FOLDER_CALL_NOTE)) doContentSync(Node.SYNC_ACTION_UPDATE_REMOTE, node, c); } else { doContentSync(Node.SYNC_ACTION_ADD_REMOTE, node, c); @@ -424,7 +457,7 @@ public class GTaskManager { } } - // for local existing folders + // 3. 本地存在的普通文件夹同步 try { c = mContentResolver.query(Notes.CONTENT_NOTE_URI, SqlNote.PROJECTION_NOTE, "(type=? AND parent_id<>?)", new String[] { @@ -438,13 +471,13 @@ public class GTaskManager { mGTaskHashMap.remove(gid); mGidToNid.put(gid, c.getLong(SqlNote.ID_COLUMN)); mNidToGid.put(c.getLong(SqlNote.ID_COLUMN), gid); - syncType = node.getSyncAction(c); + syncType = node.getSyncAction(c); // 根据时间戳判断同步类型 } else { if (c.getString(SqlNote.GTASK_ID_COLUMN).trim().length() == 0) { - // local add + // 本地新增(无gid) syncType = Node.SYNC_ACTION_ADD_REMOTE; } else { - // remote delete + // 远程已删除 syncType = Node.SYNC_ACTION_DEL_LOCAL; } } @@ -460,7 +493,7 @@ public class GTaskManager { } } - // for remote add folders + // 4. 远程新增的文件夹(本地不存在) Iterator> iter = mGTaskListHashMap.entrySet().iterator(); while (iter.hasNext()) { Map.Entry entry = iter.next(); @@ -472,89 +505,106 @@ public class GTaskManager { } } + // 提交所有更新到Google Tasks if (!mCancelled) GTaskClient.getInstance().commitUpdate(); } + /** + * 执行具体的同步操作 + * @param syncType 同步类型 + * @param node 远程节点 + * @param c 本地数据游标 + */ private void doContentSync(int syncType, Node node, Cursor c) throws NetworkFailureException { if (mCancelled) { return; } MetaData meta; + // 根据同步类型执行相应操作 switch (syncType) { case Node.SYNC_ACTION_ADD_LOCAL: - addLocalNode(node); + addLocalNode(node); // 远程新增,添加到本地 break; case Node.SYNC_ACTION_ADD_REMOTE: - addRemoteNode(node, c); + 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); + GTaskClient.getInstance().deleteNode(meta); // 先删除元数据 } - mLocalDeleteIdMap.add(c.getLong(SqlNote.ID_COLUMN)); + mLocalDeleteIdMap.add(c.getLong(SqlNote.ID_COLUMN)); // 记录删除的本地ID break; case Node.SYNC_ACTION_DEL_REMOTE: + // 本地已删除(在回收站),从远程删除 meta = mMetaHashMap.get(node.getGid()); if (meta != null) { - GTaskClient.getInstance().deleteNode(meta); + GTaskClient.getInstance().deleteNode(meta); // 先删除元数据 } - GTaskClient.getInstance().deleteNode(node); + GTaskClient.getInstance().deleteNode(node); // 删除主任务 break; case Node.SYNC_ACTION_UPDATE_LOCAL: - updateLocalNode(node, c); + updateLocalNode(node, c); // 远程有更新,更新本地 break; case Node.SYNC_ACTION_UPDATE_REMOTE: - updateRemoteNode(node, c); + updateRemoteNode(node, c); // 本地有更新,更新远程 break; case Node.SYNC_ACTION_UPDATE_CONFLICT: - // merging both modifications maybe a good idea - // right now just use local update simply + // 冲突情况:目前简单使用本地更新覆盖远程 + // TODO: 可以考虑更复杂的冲突解决策略,如合并或让用户选择 updateRemoteNode(node, c); break; case Node.SYNC_ACTION_NONE: - break; + break; // 无变化,无需同步 case Node.SYNC_ACTION_ERROR: default: - throw new ActionFailureException("unkown sync action type"); + throw new ActionFailureException("unknown sync action type"); } } + /** + * 添加本地节点(远程新增) + * @param node 远程节点 + */ private void addLocalNode(Node node) throws NetworkFailureException { if (mCancelled) { return; } SqlNote sqlNote; + // 根据节点类型处理 if (node instanceof TaskList) { - if (node.getName().equals( - GTaskStringUtils.MIUI_FOLDER_PREFFIX + GTaskStringUtils.FOLDER_DEFAULT)) { - sqlNote = new SqlNote(mContext, Notes.ID_ROOT_FOLDER); - } else if (node.getName().equals( - GTaskStringUtils.MIUI_FOLDER_PREFFIX + GTaskStringUtils.FOLDER_CALL_NOTE)) { - sqlNote = new SqlNote(mContext, Notes.ID_CALL_RECORD_FOLDER); + // 处理任务列表(文件夹) + if (node.getName().equals(GTaskStringUtils.MIUI_FOLDER_PREFFIX + GTaskStringUtils.FOLDER_DEFAULT)) { + sqlNote = new SqlNote(mContext, Notes.ID_ROOT_FOLDER); // 根文件夹 + } else if (node.getName().equals(GTaskStringUtils.MIUI_FOLDER_PREFFIX + GTaskStringUtils.FOLDER_CALL_NOTE)) { + sqlNote = new SqlNote(mContext, Notes.ID_CALL_RECORD_FOLDER); // 通话记录文件夹 } else { sqlNote = new SqlNote(mContext); sqlNote.setContent(node.getLocalJSONFromContent()); - sqlNote.setParentId(Notes.ID_ROOT_FOLDER); + sqlNote.setParentId(Notes.ID_ROOT_FOLDER); // 普通文件夹的父级是根文件夹 } } else { + // 处理任务(笔记) sqlNote = new SqlNote(mContext); JSONObject js = node.getLocalJSONFromContent(); try { + // 处理可能存在的ID冲突 if (js.has(GTaskStringUtils.META_HEAD_NOTE)) { JSONObject note = js.getJSONObject(GTaskStringUtils.META_HEAD_NOTE); 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); } } } + // 处理数据项的ID冲突 if (js.has(GTaskStringUtils.META_HEAD_DATA)) { JSONArray dataArray = js.getJSONArray(GTaskStringUtils.META_HEAD_DATA); for (int i = 0; i < dataArray.length(); i++) { @@ -562,13 +612,11 @@ 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); } } } - } } catch (JSONException e) { Log.w(TAG, e.toString()); @@ -576,6 +624,7 @@ public class GTaskManager { } sqlNote.setContent(js); + // 查找父文件夹的本地ID Long parentId = mGidToNid.get(((Task) node).getParent().getGid()); if (parentId == null) { Log.e(TAG, "cannot find task's parent id locally"); @@ -584,41 +633,53 @@ public class GTaskManager { sqlNote.setParentId(parentId.longValue()); } - // create the local node - sqlNote.setGtaskId(node.getGid()); - sqlNote.commit(false); + // 创建本地节点 + sqlNote.setGtaskId(node.getGid()); // 设置Google任务ID + sqlNote.commit(false); // 提交到数据库 - // update gid-nid mapping + // 更新ID映射关系 mGidToNid.put(node.getGid(), sqlNote.getId()); mNidToGid.put(sqlNote.getId(), node.getGid()); - // update meta + // 更新远程元数据 updateRemoteMeta(node.getGid(), sqlNote); } + /** + * 更新本地节点 + * @param node 远程节点 + * @param c 本地数据游标 + */ 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()); - Long parentId = (node instanceof Task) ? mGidToNid.get(((Task) node).getParent().getGid()) - : new Long(Notes.ID_ROOT_FOLDER); + // 确定父文件夹ID + Long parentId = (node instanceof Task) ? + mGidToNid.get(((Task) node).getParent().getGid()) : + new Long(Notes.ID_ROOT_FOLDER); if (parentId == null) { Log.e(TAG, "cannot find task's parent id locally"); throw new ActionFailureException("cannot update local node"); } sqlNote.setParentId(parentId.longValue()); - sqlNote.commit(true); + sqlNote.commit(true); // 提交更新 - // update meta info + // 更新元数据信息 updateRemoteMeta(node.getGid(), sqlNote); } + /** + * 添加远程节点(本地新增) + * @param node 远程节点(可能为null) + * @param c 本地数据游标 + */ private void addRemoteNode(Node node, Cursor c) throws NetworkFailureException { if (mCancelled) { return; @@ -627,11 +688,13 @@ public class GTaskManager { SqlNote sqlNote = new SqlNote(mContext, c); Node n; - // update remotely + // 远程更新 if (sqlNote.isNoteType()) { + // 处理笔记 Task task = new Task(); task.setContentByLocalJSON(sqlNote.getContent()); + // 查找父任务列表的Google ID String parentGid = mNidToGid.get(sqlNote.getParentId()); if (parentGid == null) { Log.e(TAG, "cannot find task's parent tasklist"); @@ -639,15 +702,16 @@ public class GTaskManager { } mGTaskListHashMap.get(parentGid).addChildTask(task); - GTaskClient.getInstance().createTask(task); + 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) folderName += GTaskStringUtils.FOLDER_DEFAULT; @@ -656,6 +720,7 @@ public class GTaskManager { else folderName += sqlNote.getSnippet(); + // 检查是否已存在同名文件夹(防止重复创建) Iterator> iter = mGTaskListHashMap.entrySet().iterator(); while (iter.hasNext()) { Map.Entry entry = iter.next(); @@ -663,7 +728,7 @@ public class GTaskManager { TaskList list = entry.getValue(); if (list.getName().equals(folderName)) { - tasklist = list; + tasklist = list; // 已存在,复用 if (mGTaskHashMap.containsKey(gid)) { mGTaskHashMap.remove(gid); } @@ -671,27 +736,32 @@ public class GTaskManager { } } - // no match we can add now + // 不存在则创建新文件夹 if (tasklist == null) { tasklist = new TaskList(); tasklist.setContentByLocalJSON(sqlNote.getContent()); - GTaskClient.getInstance().createTaskList(tasklist); + GTaskClient.getInstance().createTaskList(tasklist); // 创建远程任务列表 mGTaskListHashMap.put(tasklist.getGid(), tasklist); } n = (Node) tasklist; } - // update local note - sqlNote.setGtaskId(n.getGid()); + // 更新本地笔记 + sqlNote.setGtaskId(n.getGid()); // 设置Google ID sqlNote.commit(false); - sqlNote.resetLocalModified(); + sqlNote.resetLocalModified(); // 重置本地修改标志 sqlNote.commit(true); - // gid-id mapping + // 更新ID映射 mGidToNid.put(n.getGid(), sqlNote.getId()); mNidToGid.put(sqlNote.getId(), n.getGid()); } + /** + * 更新远程节点 + * @param node 远程节点 + * @param c 本地数据游标 + */ private void updateRemoteNode(Node node, Cursor c) throws NetworkFailureException { if (mCancelled) { return; @@ -699,25 +769,26 @@ public class GTaskManager { SqlNote sqlNote = new SqlNote(mContext, c); - // update remotely + // 远程更新 node.setContentByLocalJSON(sqlNote.getContent()); - GTaskClient.getInstance().addUpdateNode(node); + GTaskClient.getInstance().addUpdateNode(node); // 添加到更新队列 - // update meta + // 更新元数据 updateRemoteMeta(node.getGid(), sqlNote); - // move task if necessary + // 检查是否需要移动任务到其他列表 if (sqlNote.isNoteType()) { Task task = (Task) node; - TaskList preParentList = task.getParent(); + TaskList preParentList = task.getParent(); // 原父列表 String curParentGid = mNidToGid.get(sqlNote.getParentId()); if (curParentGid == null) { Log.e(TAG, "cannot find task's parent tasklist"); throw new ActionFailureException("cannot update remote task"); } - TaskList curParentList = mGTaskListHashMap.get(curParentGid); + TaskList curParentList = mGTaskListHashMap.get(curParentGid); // 新父列表 + // 如果父列表发生变化,则移动任务 if (preParentList != curParentList) { preParentList.removeChildTask(task); curParentList.addChildTask(task); @@ -725,18 +796,25 @@ public class GTaskManager { } } - // clear local modified flag + // 清除本地修改标志 sqlNote.resetLocalModified(); sqlNote.commit(true); } + /** + * 更新远程元数据 + * @param gid Google任务ID + * @param sqlNote 本地笔记对象 + */ private void updateRemoteMeta(String gid, SqlNote sqlNote) throws NetworkFailureException { if (sqlNote != null && sqlNote.isNoteType()) { MetaData metaData = mMetaHashMap.get(gid); if (metaData != null) { + // 更新现有元数据 metaData.setMeta(gid, sqlNote.getContent()); GTaskClient.getInstance().addUpdateNode(metaData); } else { + // 创建新元数据 metaData = new MetaData(); metaData.setMeta(gid, sqlNote.getContent()); mMetaList.addChildTask(metaData); @@ -746,12 +824,16 @@ public class GTaskManager { } } + /** + * 刷新本地同步ID(使用远程最后修改时间) + * 用于下次同步时判断哪些项目需要更新 + */ private void refreshLocalSyncId() throws NetworkFailureException { if (mCancelled) { return; } - // get the latest gtask list + // 重新获取最新的Google任务列表 mGTaskHashMap.clear(); mGTaskListHashMap.clear(); mMetaHashMap.clear(); @@ -759,6 +841,7 @@ public class GTaskManager { Cursor c = null; try { + // 查询所有本地笔记(排除系统和回收站项目) c = mContentResolver.query(Notes.CONTENT_NOTE_URI, SqlNote.PROJECTION_NOTE, "(type<>? AND parent_id<>?)", new String[] { String.valueOf(Notes.TYPE_SYSTEM), String.valueOf(Notes.ID_TRASH_FOLER) @@ -769,14 +852,14 @@ public class GTaskManager { Node node = mGTaskHashMap.get(gid); if (node != null) { mGTaskHashMap.remove(gid); + // 使用远程的最后修改时间作为本地同步ID ContentValues values = new ContentValues(); values.put(NoteColumns.SYNC_ID, node.getLastModified()); mContentResolver.update(ContentUris.withAppendedId(Notes.CONTENT_NOTE_URI, c.getLong(SqlNote.ID_COLUMN)), values, null, null); } else { Log.e(TAG, "something is missed"); - throw new ActionFailureException( - "some local items don't have gid after sync"); + throw new ActionFailureException("some local items don't have gid after sync"); } } } else { @@ -790,11 +873,19 @@ public class GTaskManager { } } + /** + * 获取同步账户信息 + * @return 同步账户名称 + */ public String getSyncAccount() { return GTaskClient.getInstance().getSyncAccount().name; } + /** + * 取消同步 + */ public void cancelSync() { mCancelled = true; } } +[file content end] \ No newline at end of file