From 36b5482d6506e46402762443ed0c4d1838209ee8 Mon Sep 17 00:00:00 2001 From: hjr <3217821639@qq.com> Date: Fri, 20 Dec 2024 11:28:23 +0800 Subject: [PATCH] 1 --- doc/ch.jpg | Bin 0 -> 35823 bytes doc/一千注释(2).txt | 1027 +++++++++++++++++++++++++++++++++++++++ 2 files changed, 1027 insertions(+) create mode 100644 doc/ch.jpg create mode 100644 doc/一千注释(2).txt diff --git a/doc/ch.jpg b/doc/ch.jpg new file mode 100644 index 0000000000000000000000000000000000000000..a0553204406ef1ac50085da55da7f74388795b2d GIT binary patch literal 35823 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 mGTaskListHashMap; // 存储任务列表的映射 + private HashMap mGTaskHashMap; // 存储任务的映射 + private HashMap mMetaHashMap; // 存储元数据的映射 + + private TaskList mMetaList; // 元任务列表 + private HashSet mLocalDeleteIdMap; // 用于跟踪本地删除的ID集合 + private HashMap mGidToNid; // Google ID到便签ID的映射 + private HashMap mNidToGid; // 便签ID到Google ID的映射 + + // 私有构造函数以实现单例模式 + private GTaskManager() { + mSyncing = false; // 初始化为未同步 + mCancelled = false; // 初始化为未取消 + mGTaskListHashMap = new HashMap<>(); // 初始化任务列表映射 + mGTaskHashMap = new HashMap<>(); // 初始化任务映射 + mMetaHashMap = new HashMap<>(); // 初始化元数据映射 + mMetaList = null; // 初始化元列表为null + mLocalDeleteIdMap = new HashSet<>(); // 创建本地删除ID集合 + mGidToNid = new HashMap<>(); // 初始化Google到便签ID的映射 + mNidToGid = new HashMap<>(); // 初始化便签到Google ID的映射 + } + + // 获取单例访问方法 + public static synchronized GTaskManager getInstance() { + if (mInstance == null) { + mInstance = new GTaskManager(); // 如果尚未创建,则创建一个新实例 + } + return mInstance; // 返回单例实例 + } + + // 设置Activity上下文,用于获取认证令牌 + public synchronized void setActivityContext(Activity activity) { + 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(); + mGTaskHashMap.clear(); + mMetaHashMap.clear(); + mLocalDeleteIdMap.clear(); + mGidToNid.clear(); + mNidToGid.clear(); + + try { + GTaskClient client = GTaskClient.getInstance(); // 获取Google任务客户端 + client.resetUpdateArray(); // 重置更新数组 + + // 登录Google任务 + if (!mCancelled) { + if (!client.login(mActivity)) { // 如果登录失败 + 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 { + // 清空HashMap数据 + mGTaskListHashMap.clear(); + mGTaskHashMap.clear(); + mMetaHashMap.clear(); + mLocalDeleteIdMap.clear(); + mGidToNid.clear(); + mNidToGid.clear(); + mSyncing = false; // 将同步状态标记为未同步 + } + + return mCancelled ? STATE_SYNC_CANCELLED : STATE_SUCCESS; // 返回结果状态 + } + + // 初始化Google任务列表 + private void initGTaskList() throws NetworkFailureException { + if (mCancelled) // 检查同步是否被取消 + return; // 如果被取消,直接返回 + GTaskClient client = GTaskClient.getInstance(); // 获取Google任务客户端 + try { + JSONArray jsTaskLists = client.getTaskLists(); // 获取任务列表的JSON数组 + + // 首先初始化元数据列表 + mMetaList = null; // 初始化元列表为null + for (int i = 0; i < jsTaskLists.length(); i++) { // 遍历所有任务列表 + JSONObject object = jsTaskLists.getJSONObject(i); // 获取任务列表对象 + String gid = object.getString(GTaskStringUtils.GTASK_JSON_ID); // 获取任务列表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); // 设置任务列表内容 + + // 加载元数据 + JSONArray jsMetas = client.getTaskList(gid); // 获取元数据的JSON数组 + for (int j = 0; j < jsMetas.length(); j++) { // 遍历每个元数据对象 + object = (JSONObject) jsMetas.getJSONObject(j); // 获取元数据对象 + MetaData metaData = new MetaData(); // 实例化元数据 + metaData.setContentByRemoteJSON(object); // 设置元数据内容 + if (metaData.isWorthSaving()) { // 检查元数据是否值得保存 + mMetaList.addChildTask(metaData); // 添加到元任务列表 + if (metaData.getGid() != null) { + mMetaHashMap.put(metaData.getRelatedGid(), metaData); // 将元数据ID映射到元数据对象 + } + } + } + } + } + + // 如果元数据列表不存在,则创建它 + if (mMetaList == null) { + mMetaList = new TaskList(); // 创建新的任务列表 + mMetaList.setName(GTaskStringUtils.MIUI_FOLDER_PREFFIX + GTaskStringUtils.FOLDER_META); // 设置名称 + GTaskClient.getInstance().createTaskList(mMetaList); // 在Google任务中创建任务列表 + } +// 初始化任务列表 +for (int i = 0; i < jsTaskLists.length(); i++) { // 遍历所有任务列表 + JSONObject object = jsTaskLists.getJSONObject(i); // 获取当前任务列表的JSONObject + String gid = object.getString(GTaskStringUtils.GTASK_JSON_ID); // 获取任务列表的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)) { + TaskList tasklist = new TaskList(); // 创建一个新的任务列表实例 + tasklist.setContentByRemoteJSON(object); // 设置任务列表的内容 + mGTaskListHashMap.put(gid, tasklist); // 将任务列表存入任务列表映射 + mGTaskHashMap.put(gid, tasklist); // 将任务列表存入任务映射 + + // 加载任务 + JSONArray jsTasks = client.getTaskList(gid); // 获取当前任务列表下的任务 + for (int j = 0; j < jsTasks.length(); j++) { // 遍历任务 + object = (JSONObject) jsTasks.getJSONObject(j); // 获取当前任务的JSONObject + gid = object.getString(GTaskStringUtils.GTASK_JSON_ID); // 获取任务ID + Task task = new Task(); // 创建新的任务实例 + task.setContentByRemoteJSON(object); // 设置任务的内容 + if (task.isWorthSaving()) { // 检查任务是否值得保存 + task.setMetaInfo(mMetaHashMap.get(gid)); // 设置任务的元信息 + tasklist.addChildTask(task); // 将任务添加到任务列表中 + mGTaskHashMap.put(gid, task); // 将任务存入任务映射 + } + } + } +} +} catch (JSONException e) { // 处理JSON异常 + Log.e(TAG, e.toString()); // 记录错误信息 + e.printStackTrace(); // 打印堆栈跟踪 + throw new ActionFailureException("initGTaskList: handling JSONObject failed"); // 抛出动作失败异常 +} + +private void syncContent() throws NetworkFailureException { + int syncType; // 同步类型 + Cursor c = null; // 游标用于查询结果 + String gid; // 任务的Google ID + 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); // 获取任务ID + node = mGTaskHashMap.get(gid); // 从任务映射中获取节点 + if (node != null) { // 如果节点存在 + mGTaskHashMap.remove(gid); // 从映射中移除该节点 + doContentSync(Node.SYNC_ACTION_DEL_REMOTE, node, c); // 执行远程删除操作 + } + + mLocalDeleteIdMap.add(c.getLong(SqlNote.ID_COLUMN)); // 将本地删除的便签ID添加到集合 + } + } else { + Log.w(TAG, "failed to query trash folder"); // 记录查询回收站失败的警告 + } + } finally { + if (c != null) { // 确保游标关闭 + c.close(); + c = null; // 将游标设为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); // 获取任务ID + node = mGTaskHashMap.get(gid); // 从任务映射中获取节点 + if (node != null) { // 如果节点存在 + mGTaskHashMap.remove(gid); // 从映射中移除该节点 + mGidToNid.put(gid, c.getLong(SqlNote.ID_COLUMN)); // 将Google ID映射到便签ID + mNidToGid.put(c.getLong(SqlNote.ID_COLUMN), gid); // 将便签ID映射到Google ID + syncType = node.getSyncAction(c); // 获取同步操作类型 + } else { + // 检查便签ID是否为空 + if (c.getString(SqlNote.GTASK_ID_COLUMN).trim().length() == 0) { + // 如果便签ID为空,标记为本地新增 + syncType = Node.SYNC_ACTION_ADD_REMOTE; + } else { + // 否则,标记为远程删除 + syncType = Node.SYNC_ACTION_DEL_LOCAL; + } + } + doContentSync(syncType, node, c); // 执行内容同步 + } + } else { + Log.w(TAG, "failed to query existing note in database"); // 记录查询数据库中现有便签失败的警告 + } + + } +// 针对本地已有的文件夹进行同步 +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); // 获取文件夹的Google ID + node = mGTaskHashMap.get(gid); // 从任务映射中获取相应节点 + if (node != null) { // 如果找到对应的节点 + mGTaskHashMap.remove(gid); // 从任务映射中移除该节点 + mGidToNid.put(gid, c.getLong(SqlNote.ID_COLUMN)); // 将Google ID映射到本地ID + mNidToGid.put(c.getLong(SqlNote.ID_COLUMN), gid); // 将本地ID映射到Google ID + syncType = node.getSyncAction(c); // 获取当前节点的同步操作类型 + } else { + // 如果节点不为null,检查Google ID是否为空 + if (c.getString(SqlNote.GTASK_ID_COLUMN).trim().length() == 0) { + // 如果Google ID为空,标记为本地新增 + syncType = Node.SYNC_ACTION_ADD_REMOTE; + } else { + // 否则,标记为远程删除 + 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; // 将游标设为null + } +} + +// 针对远程新增的文件夹进行同步 +Iterator> iter = mGTaskListHashMap.entrySet().iterator(); // 遍历任务列表的映射 +while (iter.hasNext()) { // 循环迭代 + Map.Entry entry = iter.next(); // 获取当前条目 + gid = entry.getKey(); // 获取Google ID + node = entry.getValue(); // 获取任务列表对象 + if (mGTaskHashMap.containsKey(gid)) { // 如果任务映射中存在该Google ID + mGTaskHashMap.remove(gid); // 从任务映射中移除该节点 + doContentSync(Node.SYNC_ACTION_ADD_LOCAL, node, null); // 执行本地新增同步 + } +} + +if (!mCancelled) // 如果没有被取消 + GTaskClient.getInstance().commitUpdate(); // 提交更新 + +} + +// 执行内容同步逻辑 +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: // 本地删除 + meta = mMetaHashMap.get(c.getString(SqlNote.GTASK_ID_COLUMN)); // 获取元数据 + if (meta != null) { // 如果元数据不为空 + GTaskClient.getInstance().deleteNode(meta); // 删除远程节点 + } + 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(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("unknown sync action type"); // 抛出异常,未知同步操作类型 + } +} + +// 添加本地节点 +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); // 创建根文件夹的SqlNote + } else if (node.getName().equals( + GTaskStringUtils.MIUI_FOLDER_PREFFIX + GTaskStringUtils.FOLDER_CALL_NOTE)) { + sqlNote = new SqlNote(mContext, Notes.ID_CALL_RECORD_FOLDER); // 创建通话记录文件夹的SqlNote + } else { + sqlNote = new SqlNote(mContext); // 创建一个新的SqlNote实例 + sqlNote.setContent(node.getLocalJSONFromContent()); // 设置便签内容 + sqlNote.setParentId(Notes.ID_ROOT_FOLDER); // 将父ID设置为根文件夹 + } + } else { + sqlNote = new SqlNote(mContext); // 创建新的SqlNote实例 + JSONObject js = node.getLocalJSONFromContent(); // 获取节点的本地JSON内容 + try { + if (js.has(GTaskStringUtils.META_HEAD_NOTE)) { // 检查是否有元数据 + JSONObject note = js.getJSONObject(GTaskStringUtils.META_HEAD_NOTE); // 获取元数据对象 + if (note.has(NoteColumns.ID)) { // 如果元数据包含ID + long id = note.getLong(NoteColumns.ID); // 获取ID + if (DataUtils.existInNoteDatabase(mContentResolver, id)) { + // 如果ID在数据库中存在,需要创建一个新的ID + note.remove(NoteColumns.ID); // 从元数据中移除ID + } + } + } + + if (js.has(GTaskStringUtils.META_HEAD_DATA)) { // 检查是否有数据头 +// 初始化SqlNote以便更新便签到本地 +sqlNote = new SqlNote(mContext, c); +sqlNote.setContent(node.getLocalJSONFromContent()); // 设置便签内容为本地JSON数据 + +// 获取父任务ID。如果节点是Task类型,使用映射获取父节点,否则默认使用根文件夹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"); // 记录错误,无法找到父节点ID + throw new ActionFailureException("cannot update local node"); // 抛出异常,无法更新本地节点 +} +sqlNote.setParentId(parentId.longValue()); // 设置父节点ID +sqlNote.commit(true); // 提交更新 + +// 更新元数据 +updateRemoteMeta(node.getGid(), sqlNote); // 将远程元数据更新至当前节点 +} + +// 添加远程节点 +private void addRemoteNode(Node node, Cursor c) throws NetworkFailureException { + if (mCancelled) { + return; // 如果已取消,直接返回 + } + + SqlNote sqlNote = new SqlNote(mContext, c); // 创建SqlNote对象 + Node n; + + // 远程更新节点 + if (sqlNote.isNoteType()) { // 如果是便签类型 + Task task = new Task(); // 创建任务对象 + task.setContentByLocalJSON(sqlNote.getContent()); // 设置任务内容 + + String parentGid = mNidToGid.get(sqlNote.getParentId()); // 获取父任务的Google ID + 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.getInstance().createTask(task); // 创建远程任务 + 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(); // 获取Google ID + TaskList list = entry.getValue(); // 获取任务列表 + + if (list.getName().equals(folderName)) { // 如果文件夹名匹配 + tasklist = list; // 替换为匹配的任务列表 + if (mGTaskHashMap.containsKey(gid)) { // 如果映射中存在该Google ID + mGTaskHashMap.remove(gid); // 从映射中移除该ID + } + break; // 跳出循环 + } + } + + // 如果没有匹配的,可以添加新的 + if (tasklist == null) { // 如果没有找到文件夹,则创建新的文件夹 + tasklist = new TaskList(); // 创建新的任务列表 + tasklist.setContentByLocalJSON(sqlNote.getContent()); // 设置任务列表内容 + GTaskClient.getInstance().createTaskList(tasklist); // 创建远程任务列表 + mGTaskListHashMap.put(tasklist.getGid(), tasklist); // 将任务列表添加到映射 + } + n = (Node) tasklist; // 将新创建的任务列表赋值给节点 + } + + // 更新本地便签 + sqlNote.setGtaskId(n.getGid()); // 设置当前节点的Google ID + sqlNote.commit(false); // 提交更新,不会覆盖本地修改 + sqlNote.resetLocalModified(); // 重置本地修改标记 + sqlNote.commit(true); // 再次提交更新 + + // 进行gid-id映射更新 + mGidToNid.put(n.getGid(), sqlNote.getId()); // 将Google ID映射到本地ID + mNidToGid.put(sqlNote.getId(), n.getGid()); // 将本地ID映射到Google ID +} + +// 更新远程节点 +private void updateRemoteNode(Node node, Cursor c) throws NetworkFailureException { + if (mCancelled) { + return; // 如果已取消,直接返回 + } + + SqlNote sqlNote = new SqlNote(mContext, c); // 根据游标创建SqlNote对象 + + // 远程更新逻辑 + node.setContentByLocalJSON(sqlNote.getContent()); // 更新节点内容 + GTaskClient.getInstance().addUpdateNode(node); // 向GTask客户端添加更新 + + // 更新元数据 + updateRemoteMeta(node.getGid(), sqlNote); // 更新远程元数据 + + // 如果需要,移动任务 + if (sqlNote.isNoteType()) { // 如果是便签类型 + Task task = (Task) node; // 转换为任务类型 + TaskList preParentList = task.getParent(); // 获取任务的原父任务列表 + + String curParentGid = mNidToGid.get(sqlNote.getParentId()); // 获取当前父任务的Google ID + if (curParentGid == null) { + Log.e(TAG, "cannot find task's parent tasklist"); // 记录错误,无法找到父任务列表 + throw new ActionFailureException("cannot update remote task"); // 抛出异常,无法更新远程任务 + } + TaskList curParentList = mGTaskListHashMap.get(curParentGid); // 获取当前父任务列表 + + // 如果父任务列表发生改变 + if (preParentList != curParentList) { + preParentList.removeChildTask(task); // 从原父列表中移除任务 + curParentList.addChildTask(task); // 添加任务到当前父列表 + GTaskClient.getInstance().moveTask(task, preParentList, curParentList); // // 移动任务 + GTaskClient.getInstance().moveTask(task, preParentList, curParentList); // 调用GTaskClient移动任务到新父列表 + } + } + + // 清除本地修改标志 + sqlNote.resetLocalModified(); // 重置本地修改标记 + sqlNote.commit(true); // 提交修改 +} + +// 更新远程元数据 +private void updateRemoteMeta(String gid, SqlNote sqlNote) throws NetworkFailureException { + if (sqlNote != null && sqlNote.isNoteType()) { // 确保sqlNote不为空且为便签类型 + MetaData metaData = mMetaHashMap.get(gid); // 根据gid获取元数据 + if (metaData != null) { // 如果元数据存在 + metaData.setMeta(gid, sqlNote.getContent()); // 更新元数据内容 + GTaskClient.getInstance().addUpdateNode(metaData); // 向GTask客户端添加更新 + } else { // 如果元数据不存在 + metaData = new MetaData(); // 创建新的元数据实例 + metaData.setMeta(gid, sqlNote.getContent()); // 设置新的元数据内容 + mMetaList.addChildTask(metaData); // 将新的元数据添加到元数据列表 + mMetaHashMap.put(gid, metaData); // 更新元数据映射 + GTaskClient.getInstance().createTask(metaData); // 创建新的任务以同步元数据 + } + } +} + +// 刷新本地同步ID +private void refreshLocalSyncId() throws NetworkFailureException { + if (mCancelled) { + return; // 如果已取消,直接返回 + } + + // 获取最新的Google任务列表 + mGTaskHashMap.clear(); // 清空现有的Google任务映射 + mGTaskListHashMap.clear(); // 清空现有的任务列表映射 + mMetaHashMap.clear(); // 清空现有的元数据映射 + initGTaskList(); // 初始化Google任务列表 + + 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()) { // 遍历查询结果 + String gid = c.getString(SqlNote.GTASK_ID_COLUMN); // 获取Google ID + Node node = mGTaskHashMap.get(gid); // 从映射中查找节点 + if (node != null) { // 如果节点存在 + mGTaskHashMap.remove(gid); // 移除映射中的该节点 + ContentValues values = new ContentValues(); // 创建ContentValues实例 + 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"); // 抛出异常,某些本地条目SYNC后没有GID + } + } + } else { + Log.w(TAG, "failed to query local note to refresh sync id"); // 记录警告,查询本地便签失败 + } + } finally { + if (c != null) { + c.close(); // 释放游标资源 + c = null; // 将游标设置为null + } + } +} + +// 获取同步账号的名称 +public String getSyncAccount() { + return GTaskClient.getInstance().getSyncAccount().name; // 返回同步账户的名称 +} + +// 取消同步 +public void cancelSync() { + mCancelled = true; // 将取消标志设为true,表示请求停止同步 +} + + + + + + + + + + + + + + + + + + android:dataExtractionRules="@xml/data_extraction_rules" + android:fullBackupContent="@xml/backup_rules" + android:icon="@mipmap/ic_launcher" + android:label="@string/app_name" + android:roundIcon="@mipmap/ic_launcher_round" + android:supportsRtl="true" + android:theme="@style/Theme.Notesmaster" + tools:targetApi="31"> + + + + android:configChanges="keyboardHidden|orientation|screenSize" + android:label="@string/app_name" + android:launchMode="singleTop" + android:theme="@style/NoteTheme" + android:uiOptions="splitActionBarWhenNarrow" + android:windowSoftInputMode="adjustPan" + android:exported="true"> + + + + + + + + + + android:configChanges="keyboardHidden|orientation|screenSize" + android:launchMode="singleTop" + android:theme="@style/NoteTheme" + android:exported="true"> + + + + + + + + + + + + + + + + + + + + + + + + + + android:authorities="micode_notes" + android:multiprocess="true" /> + + + + android:label="@string/app_widget2x2" + android:exported="true"> + + + + + + + + + + + android:label="@string/app_widget4x4" + android:exported="true"> + + + + + + + + + + + + + + + + + + + + + + android:label="@string/app_name" + android:launchMode="singleInstance" + android:theme="@android:style/Theme.Holo.Wallpaper.NoTitleBar" > + + + + + android:label="@string/preferences_title" + android:launchMode="singleTop" + android:theme="@android:style/Theme.Holo.Light" /> + + + + + android:exported="false" /> + + + + + + + +/* + * 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.app.Service; +import android.content.Context; +import android.content.Intent; +import android.os.Bundle; +import android.os.IBinder; + +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"; // 同步进度消息的广播 + + private static GTaskASyncTask mSyncTask = null; // 存储当前的异步任务实例 + + private static String mSyncProgress = ""; // 存储同步进度消息 + + // 开始同步的方法 + private void startSync() { + if (mSyncTask == null) { + mSyncTask = new GTaskASyncTask(this, new GTaskASyncTask.OnCompleteListener() { + // 同步完成回调 + public void onComplete() { + mSyncTask = null; // 重置同步任务 + sendBroadcast(""); // 发送广播,更新状态 + stopSelf(); // 停止服务 + } + }); + sendBroadcast(""); // 初始广播 + mSyncTask.execute(); // 执行异步同步任务 + } + } + + // 取消同步的方法 + private void cancelSync() { + if (mSyncTask != null) { + mSyncTask.cancelSync(); // 调用任务的取消方法 + } + } + + @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; // 保持服务运行 + } + return super.onStartCommand(intent, flags, startId); // 调用父类方法 + } + + @Override + public void onLowMemory() { + if (mSyncTask != null) { + mSyncTask.cancelSync(); // 内存不足时取消同步 + } + } + + @Override + 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); // 发送广播 + } + + // 启动同步的静态方法 + public static void startSync(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); // 启动服务 + } + + // 取消同步的静态方法 + 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; // 返回当前的同步进度消息 + } +} +package net.micode.notes.gtask.data; + +import android.database.Cursor; +import android.util.Log; +import net.micode.notes.tool.GTaskStringUtils; +import org.json.JSONException; +import org.json.JSONObject; + +public class MetaData extends Task { + private final static String TAG = MetaData.class.getSimpleName(); + + private String mRelatedGid = null; + + // 设置元数据的方法 + public void setMeta(String gid, JSONObject metaInfo) { + try { + metaInfo.put(GTaskStringUtils.META_HEAD_GTASK_ID, gid); // 将 gid 插入到 metaInfo 中 + } catch (JSONException e) { + Log.e(TAG, "failed to put related gid"); // 异常处理,打印错误日志 + } + setNotes(metaInfo.toString()); // 将元数据转换成字符串并设置为笔记内容 + setName(GTaskStringUtils.META_NOTE_NAME); // 设置名称 + } + + // 获取相关 gid 的方法 + public String getRelatedGid() { + return mRelatedGid; // 返回相关 gid + } + + @Override + public boolean isWorthSaving() { + return getNotes() != null; // 如果有笔记内容则视为可保存 + } + + @Override + public void setContentByRemoteJSON(JSONObject js) { + super.setContentByRemoteJSON(js); // 调用父类方法 + if (getNotes() != null) { + try { + JSONObject metaInfo = new JSONObject(getNotes().trim()); // 解析笔记内容为 JSON 对象 + mRelatedGid = metaInfo.getString(GTaskStringUtils.META_HEAD_GTASK_ID); // 获取相关 gid + } catch (JSONException e) { + Log.w(TAG, "failed to get related gid"); // 异常处理 + mRelatedGid = null; // 失败时将相关 gid 设置为 null + } + } + } + + @Override + public void setContentByLocalJSON(JSONObject js) { + // 此方法不应被调用 + throw new IllegalAccessError("MetaData:setContentByLocalJSON should not be called"); + } + + @Override + public JSONObject getLocalJSONFromContent() { + // 此方法不应被调用 + throw new IllegalAccessError("MetaData:getLocalJSONFromContent should not be called"); + } + + @Override + public int getSyncAction(Cursor c) { + // 此方法不应被调用 + throw new IllegalAccessError("MetaData:getSyncAction should not be called"); + } +} \ No newline at end of file