/* * Copyright (c) 2010-2011, The MiCode Open Source Community (www.micode.net) * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package net.micode.notes.gtask.remote; import android.app.Activity; import android.content.ContentResolver; import android.content.ContentUris; import android.content.ContentValues; import android.content.Context; import android.database.Cursor; import android.util.Log; import net.micode.notes.R; import net.micode.notes.data.Notes; import net.micode.notes.data.Notes.DataColumns; import net.micode.notes.data.Notes.NoteColumns; import net.micode.notes.gtask.data.MetaData; import net.micode.notes.gtask.data.Node; import net.micode.notes.gtask.data.SqlNote; import net.micode.notes.gtask.data.Task; import net.micode.notes.gtask.data.TaskList; import net.micode.notes.gtask.exception.ActionFailureException; import net.micode.notes.gtask.exception.NetworkFailureException; import net.micode.notes.tool.DataUtils; import net.micode.notes.tool.GTaskStringUtils; import org.json.JSONArray; import org.json.JSONException; import org.json.JSONObject; import java.util.HashMap; import java.util.HashSet; import java.util.Iterator; import java.util.Map; // GTaskManager类,用于管理与Google Tasks相关的操作 public class GTaskManager { // 用于日志记录的标签,方便在日志中定位该类相关的信息 private static final String TAG = GTaskManager.class.getSimpleName(); // 定义同步操作的成功状态码 public static final int STATE_SUCCESS = 0; // 定义同步操作时发生网络错误的状态码 public static final int STATE_NETWORK_ERROR = 1; // 定义同步操作时发生内部错误的状态码 public static final int STATE_INTERNAL_ERROR = 2; // 定义同步操作正在进行中的状态码 public static final int STATE_SYNC_IN_PROGRESS = 3; // 定义同步操作被取消的状态码 public static final int STATE_SYNC_CANCELLED = 4; // 单例实例,用于确保整个应用中只有一个GTaskManager实例 private static GTaskManager mInstance = null; // 当前关联的Activity对象,可能用于一些需要Activity上下文的操作 private Activity mActivity; // 当前上下文对象,用于获取系统服务等 private Context mContext; // 用于内容解析的对象,可能用于与内容提供者进行交互 private ContentResolver mContentResolver; // 标记同步操作是否正在进行 private boolean mSyncing; // 标记同步操作是否被取消 private boolean mCancelled; // 用于存储任务列表的HashMap,键为任务列表的唯一标识符,值为对应的任务列表对象 private HashMap mGTaskListHashMap; // 用于存储节点(可能是任务相关节点)的HashMap,键为节点的唯一标识符,值为对应的节点对象 private HashMap mGTaskHashMap; // 用于存储元数据的HashMap,键为元数据的唯一标识符,值为对应的元数据对象 private HashMap mMetaHashMap; // 存储元数据的任务列表 private TaskList mMetaList; // 用于存储本地删除的任务ID的HashSet private HashSet mLocalDeleteIdMap; // 用于将全局唯一标识符(Gid)映射到本地节点标识符(Nid)的HashMap private HashMap mGidToNid; // 用于将本地节点标识符(Nid)映射到全局唯一标识符(Gid)的HashMap private HashMap mNidToGid; // 私有构造函数,用于初始化GTaskManager的实例 private GTaskManager() { // 初始化同步状态为未进行 mSyncing = false; // 初始化取消状态为未取消 mCancelled = false; // 初始化用于存储任务列表的HashMap mGTaskListHashMap = new HashMap(); // 初始化用于存储节点的HashMap mGTaskHashMap = new HashMap(); // 初始化用于存储元数据的HashMap mMetaHashMap = new HashMap(); // 初始化元数据任务列表为null mMetaList = null; // 初始化用于存储本地删除ID的HashSet mLocalDeleteIdMap = new HashSet(); // 初始化从全局唯一标识符(Gid)到本地节点标识符(Nid)的HashMap mGidToNid = new HashMap(); // 初始化从本地节点标识符(Nid)到全局唯一标识符(Gid)的HashMap mNidToGid = new HashMap(); } // 获取GTaskManager单例实例的方法,使用synchronized关键字确保线程安全 public static synchronized GTaskManager getInstance() { // 如果单例实例尚未创建 if (mInstance == null) { // 创建一个新的GTaskManager实例 mInstance = new GTaskManager(); } // 返回单例实例 return mInstance; } // 设置Activity上下文的方法,使用synchronized关键字确保线程安全 public synchronized void setActivityContext(Activity activity) { // 此方法用于获取认证令牌,将传入的Activity赋值给成员变量mActivity mActivity = activity; } // 执行同步操作的方法 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; // 清空存储任务列表的HashMap mGTaskListHashMap.clear(); // 清空存储节点的HashMap mGTaskHashMap.clear(); // 清空存储元数据的HashMap mMetaHashMap.clear(); // 清空存储本地删除ID的HashSet mLocalDeleteIdMap.clear(); // 清空从Gid到Nid的HashMap mGidToNid.clear(); // 清空从Nid到Gid的HashMap mNidToGid.clear(); try { // 获取GTaskClient的单例实例 GTaskClient client = GTaskClient.getInstance(); // 重置GTaskClient的更新数组 client.resetUpdateArray(); // 登录Google Tasks服务 if (!mCancelled) { // 如果登录失败 if (!client.login(mActivity)) { // 抛出网络失败异常,提示登录Google Tasks失败 throw new NetworkFailureException("login google task failed"); } } // 从Google获取任务列表 asyncTask.publishProgess(mContext.getString(R.string.sync_progress_init_list)); // 初始化任务列表 initGTaskList(); // 执行内容同步工作 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) { // 如果捕获到其他异常,记录错误日志并打印异常堆栈信息 Log.e(TAG, e.toString()); e.printStackTrace(); // 返回内部错误状态码 return STATE_INTERNAL_ERROR; } finally { // 无论是否发生异常,都进行清理工作 mGTaskListHashMap.clear(); mGTaskHashMap.clear(); mMetaHashMap.clear(); mLocalDeleteIdMap.clear(); mGidToNid.clear(); mNidToGid.clear(); // 标记同步操作结束 mSyncing = false; } // 根据是否取消同步操作,返回相应的状态码 return mCancelled? STATE_SYNC_CANCELLED : STATE_SUCCESS; } // 初始化Google任务列表的私有方法,可能会抛出NetworkFailureException异常 private void initGTaskList() throws NetworkFailureException { // 如果同步操作已被取消,则直接返回,不进行后续操作 if (mCancelled) return; // 获取GTaskClient的单例实例 GTaskClient client = GTaskClient.getInstance(); try { // 从GTaskClient获取任务列表的JSONArray JSONArray jsTaskLists = client.getTaskLists(); // 首先初始化元数据列表 mMetaList = null; // 遍历任务列表的JSONArray for (int i = 0; i < jsTaskLists.length(); i++) { // 获取当前索引位置的JSONObject JSONObject object = jsTaskLists.getJSONObject(i); // 从JSONObject中获取任务列表的全局唯一标识符(Gid) String gid = object.getString(GTaskStringUtils.GTASK_JSON_ID); // 从JSONObject中获取任务列表的名称 String name = object.getString(GTaskStringUtils.GTASK_JSON_NAME); // 如果任务列表名称符合特定的元数据列表名称格式 if (name.equals(GTaskStringUtils.MIUI_FOLDER_PREFFIX + GTaskStringUtils.FOLDER_META)) { // 创建一个新的任务列表对象作为元数据列表 mMetaList = new TaskList(); // 根据从远程获取的JSON数据设置元数据列表的内容 mMetaList.setContentByRemoteJSON(object); // 加载元数据 // 根据任务列表的Gid获取该列表中的元数据JSONArray JSONArray jsMetas = client.getTaskList(gid); // 遍历元数据的JSONArray for (int j = 0; j < jsMetas.length(); j++) { // 获取当前索引位置的JSONObject object = (JSONObject) jsMetas.getJSONObject(j); // 创建一个新的元数据对象 MetaData metaData = new MetaData(); // 根据从远程获取的JSON数据设置元数据的内容 metaData.setContentByRemoteJSON(object); // 如果元数据值得保存 if (metaData.isWorthSaving()) { // 将元数据添加为元数据列表的子任务 mMetaList.addChildTask(metaData); // 如果元数据有Gid if (metaData.getGid()!= null) { // 将元数据添加到元数据HashMap中,以相关的Gid作为键 mMetaHashMap.put(metaData.getRelatedGid(), metaData); } } } } } // 如果元数据列表为空(即没有找到符合条件的元数据列表) if (mMetaList == null) { // 创建一个新的任务列表对象作为元数据列表 mMetaList = new TaskList(); // 设置元数据列表的名称 mMetaList.setName(GTaskStringUtils.MIUI_FOLDER_PREFFIX + GTaskStringUtils.FOLDER_META); // 使用GTaskClient创建该元数据列表 GTaskClient.getInstance().createTaskList(mMetaList); } // 初始化任务列表 // 再次遍历任务列表的JSONArray for (int i = 0; i < jsTaskLists.length(); i++) { // 获取当前索引位置的JSONObject JSONObject object = jsTaskLists.getJSONObject(i); // 从JSONObject中获取任务列表的Gid String gid = object.getString(GTaskStringUtils.GTASK_JSON_ID); // 从JSONObject中获取任务列表的名称 String name = object.getString(GTaskStringUtils.GTASK_JSON_NAME); // 如果任务列表名称以特定前缀开头,且不是元数据列表的名称 if (name.startsWith(GTaskStringUtils.MIUI_FOLDER_PREFFIX) &&!name.equals(GTaskStringUtils.MIUI_FOLDER_PREFFIX + GTaskStringUtils.FOLDER_META)) { // 创建一个新的任务列表对象 TaskList tasklist = new TaskList(); // 根据从远程获取的JSON数据设置任务列表的内容 tasklist.setContentByRemoteJSON(object); // 将任务列表添加到任务列表HashMap中,以Gid作为键 mGTaskListHashMap.put(gid, tasklist); // 将任务列表添加到任务HashMap中,以Gid作为键 mGTaskHashMap.put(gid, tasklist); // 加载任务 // 根据任务列表的Gid获取该列表中的任务JSONArray JSONArray jsTasks = client.getTaskList(gid); // 遍历任务的JSONArray for (int j = 0; j < jsTasks.length(); j++) { // 获取当前索引位置的JSONObject object = (JSONObject) jsTasks.getJSONObject(j); // 从JSONObject中获取任务的Gid gid = object.getString(GTaskStringUtils.GTASK_JSON_ID); // 创建一个新的任务对象 Task task = new Task(); // 根据从远程获取的JSON数据设置任务的内容 task.setContentByRemoteJSON(object); // 如果任务值得保存 if (task.isWorthSaving()) { // 设置任务的元数据信息,从元数据HashMap中获取 task.setMetaInfo(mMetaHashMap.get(gid)); // 将任务添加为任务列表的子任务 tasklist.addChildTask(task); // 将任务添加到任务HashMap中,以Gid作为键 mGTaskHashMap.put(gid, task); } } } } } catch (JSONException e) { // 如果在处理JSON数据时发生异常 Log.e(TAG, e.toString()); // 记录错误日志,包含异常信息 e.printStackTrace(); // 打印异常堆栈信息,便于定位问题 // 抛出ActionFailureException异常,提示初始化任务列表时处理JSONObject失败 throw new ActionFailureException("initGTaskList: handing JSONObject failed"); } } // 同步内容的私有方法,可能会抛出NetworkFailureException异常 private void syncContent() throws NetworkFailureException { int syncType; // 用于存储同步类型 Cursor c = null; // 用于存储数据库查询结果的游标 String gid; // 任务或文件夹的全局唯一标识符(Gid) Node node; // 表示任务或文件夹的节点对象 mLocalDeleteIdMap.clear(); // 清空本地删除ID的集合,为本次同步做准备 if (mCancelled) { // 如果同步操作已被取消 return; // 直接返回,不进行后续同步操作 } // 处理本地删除的笔记 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) }, null); if (c!= null) { // 如果查询结果不为空 while (c.moveToNext()) { // 遍历查询结果 gid = c.getString(SqlNote.GTASK_ID_COLUMN); // 获取笔记的Gid node = mGTaskHashMap.get(gid); // 根据Gid从任务哈希表中获取对应的节点 if (node!= null) { // 如果节点存在 mGTaskHashMap.remove(gid); // 从任务哈希表中移除该节点 // 执行内容同步操作,标记为远程删除 doContentSync(Node.SYNC_ACTION_DEL_REMOTE, node, c); } mLocalDeleteIdMap.add(c.getLong(SqlNote.ID_COLUMN)); // 将本地笔记的ID添加到本地删除ID集合中 } } else { // 如果查询失败 Log.w(TAG, "failed to query trash folder"); // 记录警告日志 } } finally { // 无论查询是否成功,都要关闭游标 if (c!= null) { c.close(); c = null; } } // 首先同步文件夹 syncFolder(); // 处理数据库中存在的笔记 try { // 查询数据库中类型为普通笔记且不在回收站的笔记 c = mContentResolver.query(Notes.CONTENT_NOTE_URI, SqlNote.PROJECTION_NOTE, "(type=? AND parent_id<>?)", new String[] { String.valueOf(Notes.TYPE_NOTE), String.valueOf(Notes.ID_TRASH_FOLER) }, NoteColumns.TYPE + " DESC"); if (c!= null) { // 如果查询结果不为空 while (c.moveToNext()) { // 遍历查询结果 gid = c.getString(SqlNote.GTASK_ID_COLUMN); // 获取笔记的Gid node = mGTaskHashMap.get(gid); // 根据Gid从任务哈希表中获取对应的节点 if (node!= null) { // 如果节点存在 mGTaskHashMap.remove(gid); // 从任务哈希表中移除该节点 // 建立Gid和本地ID的映射关系 mGidToNid.put(gid, c.getLong(SqlNote.ID_COLUMN)); mNidToGid.put(c.getLong(SqlNote.ID_COLUMN), gid); syncType = node.getSyncAction(c); // 获取节点的同步操作类型 } else { // 如果节点不存在 if (c.getString(SqlNote.GTASK_ID_COLUMN).trim().length() == 0) { // 如果GTask ID为空 // 表示本地新增 syncType = Node.SYNC_ACTION_ADD_REMOTE; } else { // 如果GTask ID不为空 // 表示远程删除 syncType = Node.SYNC_ACTION_DEL_LOCAL; } } doContentSync(syncType, node, c); // 执行内容同步操作 } } else { // 如果查询失败 Log.w(TAG, "failed to query existing note in database"); // 记录警告日志 } } finally { // 无论查询是否成功,都要关闭游标 if (c!= null) { c.close(); c = null; } } // 处理剩余的未同步项 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可能被其他线程设置,所以需要再次检查 // 清除本地删除表 if (!mCancelled) { // 如果同步未被取消 if (!DataUtils.batchDeleteNotes(mContentResolver, mLocalDeleteIdMap)) { // 如果批量删除本地删除笔记失败 throw new ActionFailureException("failed to batch-delete local deleted notes"); // 抛出操作失败异常 } } // 刷新本地同步ID if (!mCancelled) { // 如果同步未被取消 GTaskClient.getInstance().commitUpdate(); // 提交GTaskClient的更新 refreshLocalSyncId(); // 刷新本地同步ID } } // 同步文件夹的私有方法,可能会抛出NetworkFailureException异常 private void syncFolder() throws NetworkFailureException { Cursor c = null; // 用于存储数据库查询结果的游标 String gid; // 文件夹的全局唯一标识符(Gid) Node node; // 表示文件夹的节点对象 int syncType; // 同步类型 if (mCancelled) { // 如果同步操作已被取消 return; // 直接返回,不进行后续同步操作 } // 处理根文件夹 try { // 查询数据库中根文件夹的信息 c = mContentResolver.query(ContentUris.withAppendedId(Notes.CONTENT_NOTE_URI, Notes.ID_ROOT_FOLDER), SqlNote.PROJECTION_NOTE, null, null, null); if (c!= null) { // 如果查询结果不为空 c.moveToNext(); // 移动到查询结果的第一条记录 gid = c.getString(SqlNote.GTASK_ID_COLUMN); // 获取根文件夹的Gid node = mGTaskHashMap.get(gid); // 根据Gid从任务哈希表中获取对应的节点 if (node!= null) { // 如果节点存在 mGTaskHashMap.remove(gid); // 从任务哈希表中移除该节点 // 建立Gid和本地根文件夹ID的映射关系 mGidToNid.put(gid, (long) Notes.ID_ROOT_FOLDER); mNidToGid.put((long) Notes.ID_ROOT_FOLDER, gid); // 对于系统文件夹,仅在必要时更新远程名称 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 { // 如果查询失败 Log.w(TAG, "failed to query root folder"); // 记录警告日志 } } finally { // 无论查询是否成功,都要关闭游标 if (c!= null) { c.close(); c = null; } } // 处理通话记录文件夹 try { // 查询数据库中通话记录文件夹的信息 c = mContentResolver.query(Notes.CONTENT_NOTE_URI, SqlNote.PROJECTION_NOTE, "(_id=?)", new String[] { String.valueOf(Notes.ID_CALL_RECORD_FOLDER) }, null); if (c!= null) { // 如果查询结果不为空 if (c.moveToNext()) { // 移动到查询结果的第一条记录 gid = c.getString(SqlNote.GTASK_ID_COLUMN); // 获取通话记录文件夹的Gid node = mGTaskHashMap.get(gid); // 根据Gid从任务哈希表中获取对应的节点 if (node!= null) { // 如果节点存在 mGTaskHashMap.remove(gid); // 从任务哈希表中移除该节点 // 建立Gid和本地通话记录文件夹ID的映射关系 mGidToNid.put(gid, (long) Notes.ID_CALL_RECORD_FOLDER); mNidToGid.put((long) Notes.ID_CALL_RECORD_FOLDER, gid); // 对于系统文件夹,仅在必要时更新远程名称 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); } } } else { // 如果查询失败 Log.w(TAG, "failed to query call note folder"); // 记录警告日志 } } finally { // 无论查询是否成功,都要关闭游标 if (c!= null) { c.close(); c = null; } } // 处理本地存在的文件夹 try { // 查询数据库中类型为文件夹且不在回收站的文件夹信息 c = mContentResolver.query(Notes.CONTENT_NOTE_URI, SqlNote.PROJECTION_NOTE, "(type=? AND parent_id<>?)", new String[] { String.valueOf(Notes.TYPE_FOLDER), String.valueOf(Notes.ID_TRASH_FOLER) }, NoteColumns.TYPE + " DESC"); if (c!= null) { // 如果查询结果不为空 while (c.moveToNext()) { // 遍历查询结果 gid = c.getString(SqlNote.GTASK_ID_COLUMN); // 获取文件夹的Gid node = mGTaskHashMap.get(gid); // 根据Gid从任务哈希表中获取对应的节点 if (node!= null) { // 如果节点存在 mGTaskHashMap.remove(gid); // 从任务哈希表中移除该节点 // 建立Gid和本地文件夹ID的映射关系 mGidToNid.put(gid, c.getLong(SqlNote.ID_COLUMN)); mNidToGid.put(c.getLong(SqlNote.ID_COLUMN), gid); syncType = node.getSyncAction(c); // 获取节点的同步操作类型 } else { if (c.getString(SqlNote.GTASK_ID_COLUMN).trim().length() == 0) { // 如果GTask ID为空 // 表示本地新增 syncType = Node.SYNC_ACTION_ADD_REMOTE; } else { // 如果GTask ID不为空 // 表示远程删除 syncType = Node.SYNC_ACTION_DEL_LOCAL; } } // 执行相应的同步操作 doContentSync(syncType, node, c); } } else { // 如果查询失败 Log.w(TAG, "failed to query existing folder"); // 记录警告日志 } } finally { // 无论查询是否成功,都要关闭游标 if (c!= null) { c.close(); c = null; } } // 处理远程新增的文件夹 Iterator> iter = mGTaskListHashMap.entrySet().iterator(); while (iter.hasNext()) { // 遍历任务列表哈希表中的每一项 Map.Entry entry = iter.next(); gid = entry.getKey(); // 获取文件夹的Gid node = entry.getValue(); // 获取对应的任务列表(即文件夹)对象 if (mGTaskHashMap.containsKey(gid)) { // 如果任务哈希表中包含该Gid mGTaskHashMap.remove(gid); // 从任务哈希表中移除该Gid对应的节点 // 执行添加本地文件夹的同步操作 doContentSync(Node.SYNC_ACTION_ADD_LOCAL, node, null); } } if (!mCancelled) // 如果同步操作未被取消 // 提交GTaskClient的更新 GTaskClient.getInstance().commitUpdate(); } // 执行内容同步操作的私有方法,根据不同的同步类型执行相应的操作,可能会抛出NetworkFailureException异常 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); break; case Node.SYNC_ACTION_ADD_REMOTE: // 执行添加远程节点的操作 addRemoteNode(node, c); break; case Node.SYNC_ACTION_DEL_LOCAL: // 根据游标获取的GTask ID从元数据哈希表中获取元数据 meta = mMetaHashMap.get(c.getString(SqlNote.GTASK_ID_COLUMN)); if (meta!= null) { // 如果元数据存在,使用GTaskClient删除该元数据节点 GTaskClient.getInstance().deleteNode(meta); } // 将本地记录的ID添加到本地删除ID集合中 mLocalDeleteIdMap.add(c.getLong(SqlNote.ID_COLUMN)); break; case Node.SYNC_ACTION_DEL_REMOTE: // 根据节点的Gid从元数据哈希表中获取元数据 meta = mMetaHashMap.get(node.getGid()); if (meta!= null) { // 如果元数据存在,使用GTaskClient删除该元数据节点 GTaskClient.getInstance().deleteNode(meta); } // 使用GTaskClient删除远程节点 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: // 目前简单地使用本地更新来处理冲突 updateRemoteNode(node, c); break; case Node.SYNC_ACTION_NONE: // 不进行任何操作 break; case Node.SYNC_ACTION_ERROR: default: // 如果遇到未知的同步操作类型,抛出操作失败异常 throw new ActionFailureException("unkown sync action type"); } } // 向本地添加节点的私有方法,可能会抛出NetworkFailureException异常 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对象 sqlNote = new SqlNote(mContext, Notes.ID_ROOT_FOLDER); } // 如果任务列表名称是通话记录文件夹名称 else if (node.getName().equals( GTaskStringUtils.MIUI_FOLDER_PREFFIX + GTaskStringUtils.FOLDER_CALL_NOTE)) { // 创建一个代表通话记录文件夹的SqlNote对象 sqlNote = new SqlNote(mContext, Notes.ID_CALL_RECORD_FOLDER); } // 其他普通任务列表 else { // 创建一个新的SqlNote对象 sqlNote = new SqlNote(mContext); // 设置SqlNote的内容为节点的本地JSON内容 sqlNote.setContent(node.getLocalJSONFromContent()); // 设置父节点ID为根文件夹ID sqlNote.setParentId(Notes.ID_ROOT_FOLDER); } } // 如果节点是任务 else { // 创建一个新的SqlNote对象 sqlNote = new SqlNote(mContext); // 获取节点的本地JSON内容 JSONObject js = node.getLocalJSONFromContent(); try { // 如果JSON数据包含元数据头中的笔记信息 if (js.has(GTaskStringUtils.META_HEAD_NOTE)) { // 获取笔记信息的JSONObject JSONObject note = js.getJSONObject(GTaskStringUtils.META_HEAD_NOTE); // 如果笔记信息包含ID字段 if (note.has(NoteColumns.ID)) { // 获取ID long id = note.getLong(NoteColumns.ID); // 如果该ID在本地笔记数据库中已存在 if (DataUtils.existInNoteDatabase(mContentResolver, id)) { // 该ID不可用,需要移除该ID字段 note.remove(NoteColumns.ID); } } } // 如果JSON数据包含元数据头中的数据信息 if (js.has(GTaskStringUtils.META_HEAD_DATA)) { // 获取数据信息的JSONArray JSONArray dataArray = js.getJSONArray(GTaskStringUtils.META_HEAD_DATA); // 遍历数据信息的JSONArray for (int i = 0; i < dataArray.length(); i++) { // 获取每个数据的JSONObject JSONObject data = dataArray.getJSONObject(i); // 如果数据包含ID字段 if (data.has(DataColumns.ID)) { // 获取ID long dataId = data.getLong(DataColumns.ID); // 如果该ID在本地数据数据库中已存在 if (DataUtils.existInDataDatabase(mContentResolver, dataId)) { // 该ID不可用,需要移除该ID字段 data.remove(DataColumns.ID); } } } } } catch (JSONException e) { // 如果在处理JSON数据时发生异常,记录警告日志并打印异常堆栈信息 Log.w(TAG, e.toString()); e.printStackTrace(); } // 设置SqlNote的内容为处理后的JSON数据 sqlNote.setContent(js); // 获取任务父节点的Gid对应的本地ID Long parentId = mGidToNid.get(((Task) node).getParent().getGid()); // 如果找不到任务父节点的本地ID if (parentId == null) { // 记录错误日志 Log.e(TAG, "cannot find task's parent id locally"); // 抛出操作失败异常 throw new ActionFailureException("cannot add local node"); } // 设置SqlNote的父节点ID sqlNote.setParentId(parentId.longValue()); } // 创建本地节点 // 设置SqlNote的GTask ID为节点的Gid sqlNote.setGtaskId(node.getGid()); // 提交SqlNote,但不立即刷新(false表示不立即刷新) sqlNote.commit(false); // 更新Gid和本地ID的映射关系 mGidToNid.put(node.getGid(), sqlNote.getId()); mNidToGid.put(sqlNote.getId(), node.getGid()); // 更新远程元数据 updateRemoteMeta(node.getGid(), sqlNote); } // 更新本地节点的私有方法,可能会抛出NetworkFailureException异常 private void updateLocalNode(Node node, Cursor c) throws NetworkFailureException { // 如果同步操作已被取消 if (mCancelled) { // 直接返回,不进行后续操作 return; } SqlNote sqlNote; // 使用游标创建一个代表本地节点的SqlNote对象 sqlNote = new SqlNote(mContext, c); // 设置SqlNote的内容为节点的本地JSON内容 sqlNote.setContent(node.getLocalJSONFromContent()); // 获取节点的父节点ID Long parentId = (node instanceof Task)? mGidToNid.get(((Task) node).getParent().getGid()) : new Long(Notes.ID_ROOT_FOLDER); // 如果找不到任务父节点的本地ID if (parentId == null) { // 记录错误日志 Log.e(TAG, "cannot find task's parent id locally"); // 抛出操作失败异常 throw new ActionFailureException("cannot update local node"); } // 设置SqlNote的父节点ID sqlNote.setParentId(parentId.longValue()); // 提交SqlNote并立即刷新(true表示立即刷新) sqlNote.commit(true); // 更新远程元数据 updateRemoteMeta(node.getGid(), sqlNote); } // 向远程添加节点的私有方法,可能会抛出NetworkFailureException异常 private void addRemoteNode(Node node, Cursor c) throws NetworkFailureException { // 如果同步操作已被取消 if (mCancelled) { // 直接返回,不进行后续操作 return; } // 使用游标创建一个代表本地相关信息的SqlNote对象 SqlNote sqlNote = new SqlNote(mContext, c); Node n; // 如果是笔记类型 if (sqlNote.isNoteType()) { // 创建一个新的任务对象 Task task = new Task(); // 根据SqlNote的内容设置任务的内容 task.setContentByLocalJSON(sqlNote.getContent()); // 获取任务父节点的Gid String parentGid = mNidToGid.get(sqlNote.getParentId()); // 如果找不到任务父节点的Gid if (parentGid == null) { // 记录错误日志 Log.e(TAG, "cannot find task's parent tasklist"); // 抛出操作失败异常 throw new ActionFailureException("cannot add remote task"); } // 将任务添加到其父任务列表中 mGTaskListHashMap.get(parentGid).addChildTask(task); // 使用GTaskClient创建远程任务 GTaskClient.getInstance().createTask(task); // 将任务赋值给n n = (Node) task; // 更新远程元数据 updateRemoteMeta(task.getGid(), sqlNote); } // 如果是文件夹类型 else { TaskList tasklist = null; // 构建文件夹名称 String folderName = GTaskStringUtils.MIUI_FOLDER_PREFFIX; if (sqlNote.getId() == Notes.ID_ROOT_FOLDER) folderName += GTaskStringUtils.FOLDER_DEFAULT; else if (sqlNote.getId() == Notes.ID_CALL_RECORD_FOLDER) folderName += GTaskStringUtils.FOLDER_CALL_NOTE; else folderName += sqlNote.getSnippet(); // 检查是否已经存在同名的文件夹 Iterator> iter = mGTaskListHashMap.entrySet().iterator(); while (iter.hasNext()) { Map.Entry entry = iter.next(); String gid = entry.getKey(); TaskList list = entry.getValue(); // 如果找到同名文件夹 if (list.getName().equals(folderName)) { // 将该文件夹赋值给tasklist tasklist = list; // 如果任务哈希表中包含该文件夹的Gid if (mGTaskHashMap.containsKey(gid)) { // 从任务哈希表中移除该Gid mGTaskHashMap.remove(gid); } break; } } // 如果不存在同名文件夹,则添加新的文件夹 if (tasklist == null) { // 创建一个新的任务列表对象 tasklist = new TaskList(); // 根据SqlNote的内容设置任务列表的内容 tasklist.setContentByLocalJSON(sqlNote.getContent()); // 使用GTaskClient创建远程任务列表 GTaskClient.getInstance().createTaskList(tasklist); // 将新创建的任务列表添加到任务列表哈希表中 mGTaskListHashMap.put(tasklist.getGid(), tasklist); } // 将任务列表赋值给n n = (Node) tasklist; } // 更新本地笔记 // 设置SqlNote的GTask ID为远程节点的Gid sqlNote.setGtaskId(n.getGid()); // 提交SqlNote但不立即刷新(false表示不立即刷新) sqlNote.commit(false); // 重置本地修改标记 sqlNote.resetLocalModified(); // 再次提交SqlNote并立即刷新(true表示立即刷新) sqlNote.commit(true); // 更新Gid和本地ID的映射关系 mGidToNid.put(n.getGid(), sqlNote.getId()); mNidToGid.put(sqlNote.getId(), n.getGid()); } // 更新远程节点的私有方法,可能会抛出NetworkFailureException异常 private void updateRemoteNode(Node node, Cursor c) throws NetworkFailureException { // 如果同步操作已被取消 if (mCancelled) { // 直接返回,不进行后续操作 return; } // 根据游标创建一个SqlNote对象,用于获取本地相关信息 SqlNote sqlNote = new SqlNote(mContext, c); // 更新远程节点内容 // 根据SqlNote的内容更新节点内容 node.setContentByLocalJSON(sqlNote.getContent()); // 使用GTaskClient将节点的更新操作添加到更新队列 GTaskClient.getInstance().addUpdateNode(node); // 更新远程元数据 updateRemoteMeta(node.getGid(), sqlNote); // 如果是笔记类型(任务),且有必要,执行移动任务操作 if (sqlNote.isNoteType()) { // 将节点转换为任务对象 Task task = (Task) node; // 获取任务原来的父任务列表 TaskList preParentList = task.getParent(); // 根据SqlNote的父节点ID获取对应的远程任务列表的Gid String curParentGid = mNidToGid.get(sqlNote.getParentId()); // 如果找不到任务父任务列表的Gid if (curParentGid == null) { // 记录错误日志 Log.e(TAG, "cannot find task's parent tasklist"); // 抛出操作失败异常 throw new ActionFailureException("cannot update remote task"); } // 根据Gid获取当前的父任务列表 TaskList curParentList = mGTaskListHashMap.get(curParentGid); // 如果任务的父任务列表发生了变化 if (preParentList!= curParentList) { // 从原来的父任务列表中移除该任务 preParentList.removeChildTask(task); // 将任务添加到新的父任务列表中 curParentList.addChildTask(task); // 使用GTaskClient执行移动任务的操作 GTaskClient.getInstance().moveTask(task, preParentList, curParentList); } } // 清除本地修改标记 // 重置SqlNote的本地修改标记 sqlNote.resetLocalModified(); // 提交SqlNote并立即刷新(true表示立即刷新) sqlNote.commit(true); } // 更新远程元数据的私有方法,可能会抛出NetworkFailureException异常 private void updateRemoteMeta(String gid, SqlNote sqlNote) throws NetworkFailureException { // 如果SqlNote不为空且是笔记类型 if (sqlNote!= null && sqlNote.isNoteType()) { // 根据Gid从元数据哈希表中获取元数据 MetaData metaData = mMetaHashMap.get(gid); if (metaData!= null) { // 根据SqlNote的内容更新元数据 metaData.setMeta(gid, sqlNote.getContent()); // 使用GTaskClient将元数据的更新操作添加到更新队列 GTaskClient.getInstance().addUpdateNode(metaData); } else { // 如果元数据不存在,则创建一个新的元数据对象 metaData = new MetaData(); // 根据SqlNote的内容设置元数据 metaData.setMeta(gid, sqlNote.getContent()); // 将新创建的元数据添加到元数据列表中 mMetaList.addChildTask(metaData); // 将新创建的元数据添加到元数据哈希表中 mMetaHashMap.put(gid, metaData); // 使用GTaskClient创建该元数据任务 GTaskClient.getInstance().createTask(metaData); } } } // 刷新本地同步ID的私有方法,可能会抛出NetworkFailureException异常 private void refreshLocalSyncId() throws NetworkFailureException { // 如果同步操作已被取消 if (mCancelled) { // 直接返回,不进行后续操作 return; } // 清空现有的任务和元数据哈希表 mGTaskHashMap.clear(); mGTaskListHashMap.clear(); mMetaHashMap.clear(); // 重新初始化任务列表,获取最新的任务和元数据信息 initGTaskList(); 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) }, NoteColumns.TYPE + " DESC"); if (c!= null) { // 遍历查询结果 while (c.moveToNext()) { // 获取当前笔记的Gid String gid = c.getString(SqlNote.GTASK_ID_COLUMN); // 根据Gid从任务哈希表中获取对应的节点 Node node = mGTaskHashMap.get(gid); if (node!= null) { // 从任务哈希表中移除该节点 mGTaskHashMap.remove(gid); // 创建一个ContentValues对象,用于更新数据库 ContentValues values = new ContentValues(); // 设置要更新的同步ID为节点的最后修改时间 values.put(NoteColumns.SYNC_ID, node.getLastModified()); // 更新本地数据库中对应笔记的同步ID mContentResolver.update(ContentUris.withAppendedId(Notes.CONTENT_NOTE_URI, c.getLong(SqlNote.ID_COLUMN)), values, null, null); } else { // 如果找不到对应的节点,记录错误日志 Log.e(TAG, "something is missed"); // 抛出操作失败异常,提示同步后一些本地项没有Gid throw new ActionFailureException( "some local items don't have gid after sync"); } } } else { // 如果查询失败,记录警告日志 Log.w(TAG, "failed to query local note to refresh sync id"); } } finally { // 无论查询是否成功,都关闭游标 if (c!= null) { c.close(); c = null; } } } // 获取同步账户名称的公共方法 public String getSyncAccount() { // 通过GTaskClient获取同步账户的名称 return GTaskClient.getInstance().getSyncAccount().name; } // 取消同步操作的公共方法 public void cancelSync() { // 将取消标志设置为true mCancelled = true; }}