You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
TEST1231/GTaskManager.java

1689 lines
42 KiB

This file contains ambiguous Unicode characters!

This file contains ambiguous Unicode characters that may be confused with others in your current locale. If your use case is intentional and legitimate, you can safely ignore this warning. Use the Escape button to highlight these characters.

/*
* 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.
*/
// 声明包名这是Java中组织类的一种方式确保类的唯一性
package net.micode.notes.gtask.remote;
// 导入Android框架和其他Java库中的类
import android.app.Activity; // 导入Activity类它是Android应用中用户界面的一个基本组件
import android.content.ContentResolver; // 导入ContentResolver类用于访问Android内容提供者提供的数据
import android.content.ContentUris; // 导入ContentUris类提供用于操作URI的工具方法
import android.content.ContentValues; // 导入ContentValues类用于存储一组值这些值通常用于插入或更新数据库
import android.content.Context; // 导入Context类它提供了关于应用环境和其资源的全局信息
import android.database.Cursor; // 导入Cursor类用于遍历查询结果集
import android.util.Log; // 导入Log类用于记录日志信息
// 导入项目内部的其他类
import net.micode.notes.R; // 导入R类它包含了项目中所有资源的引用如布局、字符串等
import net.micode.notes.data.Notes; // 导入Notes类可能是一个用于管理笔记数据的数据库帮助类
import net.micode.notes.data.Notes.DataColumns; // 导入DataColumns接口或类它定义了Notes数据库中数据表的列名
import net.micode.notes.data.Notes.NoteColumns; // 导入NoteColumns接口或类它定义了Notes数据库中笔记数据表的列名
import net.micode.notes.gtask.data.MetaData; // 导入MetaData类可能用于存储与Google任务相关的元数据
import net.micode.notes.gtask.data.Node; // 导入Node类可能表示Google任务中的节点或任务项
import net.micode.notes.gtask.data.SqlNote; // 导入SqlNote类可能是一个表示笔记的实体类与数据库交互
import net.micode.notes.gtask.data.Task; // 导入Task类表示Google任务中的一个任务项
import net.micode.notes.gtask.data.TaskList; // 导入TaskList类表示Google任务中的一个任务列表
import net.micode.notes.gtask.exception.ActionFailureException; // 导入ActionFailureException类表示执行动作失败时抛出的异常
import net.micode.notes.gtask.exception.NetworkFailureException; // 导入NetworkFailureException类表示网络请求失败时抛出的异常
import net.micode.notes.tool.DataUtils; // 导入DataUtils类可能包含了一些用于数据处理的工具方法
import net.micode.notes.tool.GTaskStringUtils; // 导入GTaskStringUtils类可能包含了一些用于处理Google任务相关字符串的工具方法
// 导入Java标准库中的类
import org.json.JSONArray; // 导入JSONArray类用于表示JSON数组
import org.json.JSONException; // 导入JSONException类表示在解析JSON时发生的异常
import org.json.JSONObject; // 导入JSONObject类用于表示JSON对象
import java.util.HashMap; // 导入HashMap类用于存储键值对
import java.util.HashSet; // 导入HashSet类它是一个不包含重复元素的集合
import java.util.Iterator; // 导入Iterator接口用于遍历集合中的元素
import java.util.Map; // 导入Map接口它是存储键值对的数据结构的基础接口
//1
// 声明GTaskManager类这是一个管理Google任务GTasks的类
public class GTaskManager {
// 定义一个静态常量TAG用于日志记录时标识此类的名称
private static final String TAG = GTaskManager.class.getSimpleName();
// 定义几个静态常量,用于表示不同的状态码
// 0表示操作成功
public static final int STATE_SUCCESS = 0;
// 1表示网络错误
public static final int STATE_NETWORK_ERROR = 1;
// 2表示内部错误
public static final int STATE_INTERNAL_ERROR = 2;
// 3表示同步正在进行中
public static final int STATE_SYNC_IN_PROGRESS = 3;
// 4表示同步被取消
public static final int STATE_SYNC_CANCELLED = 4;
// 定义一个私有的静态实例变量mInstance用于实现单例模式
private static GTaskManager mInstance = null;
// 定义私有成员变量用于存储Activity的引用
private Activity mActivity;
// 定义私有成员变量用于存储Context的引用
private Context mContext;
// 定义私有成员变量用于访问Android内容提供者提供的数据
private ContentResolver mContentResolver;
// 定义私有成员变量,表示当前是否正在同步
private boolean mSyncing;
// 定义私有成员变量,表示同步是否被取消
private boolean mCancelled;
// 定义一个HashMap用于存储Google任务列表TaskList的映射关系键是任务列表的字符串标识值是TaskList对象
private HashMap<String, TaskList> mGTaskListHashMap;
// 定义一个HashMap用于存储Google任务节点Node的映射关系键是节点的字符串标识值是Node对象
private HashMap<String, Node> mGTaskHashMap;
// 定义一个HashMap用于存储元数据MetaData的映射关系键是元数据的字符串标识值是MetaData对象
private HashMap<String, MetaData> mMetaHashMap;
// 定义一个TaskList对象可能用于存储元数据列表
private TaskList mMetaList;
// 定义一个HashSet用于存储本地删除的ID集合可能用于标记那些已经在本地删除但尚未同步到云端的任务ID
private HashSet<Long> mLocalDeleteIdMap;
// 定义一个HashMap用于存储Google任务IDGid到本地任务IDNid的映射关系
private HashMap<String, Long> mGidToNid;
// 定义一个HashMap用于存储本地任务IDNid到Google任务IDGid的映射关系
private HashMap<Long, String> mNidToGid;
//2
// 私有构造函数用于初始化GTaskManager类的实例变量
private GTaskManager() {
mSyncing = false; // 初始化同步状态为false
mCancelled = false; // 初始化取消状态为false
mGTaskListHashMap = new HashMap<String, TaskList>(); // 初始化任务列表的HashMap
mGTaskHashMap = new HashMap<String, Node>(); // 初始化任务节点的HashMap
mMetaHashMap = new HashMap<String, MetaData>(); // 初始化元数据的HashMap
mMetaList = null; // 初始化元数据列表为null
mLocalDeleteIdMap = new HashSet<Long>(); // 初始化本地删除ID的HashSet
mGidToNid = new HashMap<String, Long>(); // 初始化GID到NID的映射
mNidToGid = new HashMap<Long, String>(); // 初始化NID到GID的映射
}
// 获取GTaskManager类的单例实例
public static synchronized GTaskManager getInstance() {
if (mInstance == null) { // 如果实例尚未创建
mInstance = new GTaskManager(); // 创建新实例
}
return mInstance; // 返回单例实例
}
// 设置Activity上下文用于获取认证令牌
public synchronized void setActivityContext(Activity activity) {
mActivity = activity; // 保存Activity上下文
}
// 同步Google Tasks数据
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; // 设置同步状态为true
mCancelled = false; // 重置取消状态为false
// 清空所有HashMap和HashSet
mGTaskListHashMap.clear();
mGTaskHashMap.clear();
mMetaHashMap.clear();
mLocalDeleteIdMap.clear();
mGidToNid.clear();
mNidToGid.clear();
try {
GTaskClient client = GTaskClient.getInstance(); // 获取GTaskClient实例
client.resetUpdateArray(); // 重置更新数组
// 登录Google Tasks
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和HashSet
mGTaskListHashMap.clear();
mGTaskHashMap.clear();
mMetaHashMap.clear();
mLocalDeleteIdMap.clear();
mGidToNid.clear();
mNidToGid.clear();
mSyncing = false; // 设置同步状态为false
}
return mCancelled ? STATE_SYNC_CANCELLED : STATE_SUCCESS; // 根据取消状态返回相应结果
}
// 初始化Google Tasks列表
private void initGTaskList() throws NetworkFailureException {
if (mCancelled) return; // 如果已取消,则直接返回
GTaskClient client = GTaskClient.getInstance(); // 获取GTaskClient实例
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); // 获取GID
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 = jsMetas.getJSONObject(j); // 获取元数据对象
MetaData metaData = new MetaData(); // 创建元数据实例
metaData.setContentByRemoteJSON(object); // 设置内容
if (metaData.isWorthSaving()) { // 如果值得保存
mMetaList.addChildTask(metaData); // 添加到元数据列表
if (metaData.getGid() != null) { // 如果有GID
mMetaHashMap.put(metaData.getRelatedGid(), metaData); // 添加到元数据HashMap
}
}
}
}
}
// 如果元数据列表不存在,则创建
if (mMetaList == null) {
mMetaList = new TaskList(); // 创建元数据列表
mMetaList.setName(GTaskStringUtils.MIUI_FOLDER_PREFFIX + GTaskStringUtils.FOLDER_META); // 设置名称
GTaskClient.getInstance().createTaskList(mMetaList); // 创建任务列表
}
// 初始化任务列表
for (int i = 0; i < jsTaskLists.length(); i++) { // 遍历任务列表
JSONObject object = jsTaskLists.getJSONObject(i); // 获取任务对象
String gid = object.getString(GTaskStringUtils.GTASK_JSON_ID); // 获取GID
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)) {
TaskList tasklist = new TaskList(); // 创建任务列表
tasklist.setContentByRemoteJSON(object); // 设置内容
mGTaskListHashMap.put(gid, tasklist); // 添加到任务列表HashMap
mGTaskHashMap.put(gid, tasklist); // 添加到任务节点HashMap这里可能有误通常应该添加任务节点而非任务列表
// 加载任务
JSONArray jsTasks = client.getTaskList(gid); // 获取任务的JSON数组
for (int j = 0; j < jsTasks.length(); j++) { // 遍历任务
object = jsTasks.getJSONObject(j); // 获取任务对象
gid = object.getString(GTaskStringUtils.GTASK_JSON_ID); // 获取GID这里可能覆盖了之前的gid变量应该是nid或其他变量
Task task = new Task(); // 创建任务实例
task.setContentByRemoteJSON(object); // 设置内容
if (task.isWorthSaving()) { // 如果值得保存
task.setMetaInfo(mMetaHashMap.get(gid)); // 设置元数据这里gid可能应该是任务的相关GID
tasklist.addChildTask(task); // 添加到任务列表
mGTaskHashMap.put(gid, task); // 添加到任务节点HashMap这里gid可能应该是任务的唯一ID
}
}
}
}
} catch (JSONException e) { // 捕获JSON异常
Log.e(TAG, e.toString()); // 记录错误日志
e.printStackTrace(); // 打印堆栈跟踪
throw new ActionFailureException("initGTaskList: handing JSONObject failed"); // 抛出操作失败异常
}
}
//3
// 定义一个私有方法用于同步内容该方法可能会抛出NetworkFailureException异常
private void syncContent() throws NetworkFailureException {
int syncType; // 定义一个整型变量,用于存储同步类型
Cursor c = null; // 定义一个Cursor对象用于数据库查询初始化为null
String gid; // 定义一个字符串变量用于存储Google任务的ID
Node node; // 定义一个Node对象用于表示一个任务节点
// 清空本地删除ID的映射表
mLocalDeleteIdMap.clear();
// 如果同步已被取消,则直接返回
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); // 获取Google任务的ID
node = mGTaskHashMap.get(gid); // 从哈希表中获取对应的Node对象
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(); // 关闭Cursor对象
c = null; // 将Cursor对象置为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); // 获取Google任务的ID
node = mGTaskHashMap.get(gid); // 从哈希表中获取对应的Node对象
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 {
if (c.getString(SqlNote.GTASK_ID_COLUMN).trim().length() == 0) {
// 如果Google任务ID为空则为本地新增
syncType = Node.SYNC_ACTION_ADD_REMOTE;
} else {
// 如果Google任务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(); // 关闭Cursor对象
c = null; // 将Cursor对象置为null
}
}
// 遍历哈希表中剩余的项目(即远程新增的项目)
Iterator<Map.Entry<String, Node>> iter = mGTaskHashMap.entrySet().iterator();
while (iter.hasNext()) {
Map.Entry<String, Node> 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
// 检查是否未被取消,以避免在取消操作后执行不必要的步骤
if (!mCancelled) {
// 尝试批量删除本地已标记为删除的笔记
if (!DataUtils.batchDeleteNotes(mContentResolver, mLocalDeleteIdMap)) {
// 如果删除失败,抛出异常
throw new ActionFailureException("failed to batch-delete local deleted notes");
}
}
// 如果没有被取消则刷新本地同步ID
if (!mCancelled) {
// 提交更新到GTaskClient实例可能是与服务器同步的客户端
GTaskClient.getInstance().commitUpdate();
// 刷新本地同步ID
refreshLocalSyncId();
}
// 定义一个私有方法,用于同步文件夹
private void syncFolder() throws NetworkFailureException {
Cursor c = null; // 声明一个Cursor对象用于数据库查询
String gid; // 声明一个字符串变量用于存储Google任务的ID
Node node; // 声明一个Node对象可能代表一个笔记或文件夹的节点
int syncType; // 声明一个整型变量,用于存储同步类型
// 如果操作被取消,则直接返回
if (mCancelled) {
return;
}
// 查询根文件夹
try {
// 使用ContentResolver查询根文件夹的信息
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); // 获取Google任务的ID
node = mGTaskHashMap.get(gid); // 从哈希表中获取对应的Node对象
if (node != null) {
// 如果Node对象存在则进行一系列更新操作
mGTaskHashMap.remove(gid); // 从哈希表中移除该Node
mGidToNid.put(gid, (long) Notes.ID_ROOT_FOLDER); // 更新全局ID到本地ID的映射
mNidToGid.put((long) Notes.ID_ROOT_FOLDER, gid); // 更新本地ID到全局ID的映射
// 如果根文件夹的名称不是默认的,则更新远程名称
if (!node.getName().equals(
GTaskStringUtils.MIUI_FOLDER_PREFFIX + GTaskStringUtils.FOLDER_DEFAULT))
doContentSync(Node.SYNC_ACTION_UPDATE_REMOTE, node, c); // 执行内容同步
} else {
// 如果Node对象不存在则添加远程Node
doContentSync(Node.SYNC_ACTION_ADD_REMOTE, node, c);
}
} else {
// 如果查询失败,则记录警告日志
Log.w(TAG, "failed to query root folder");
}
} finally {
// 确保Cursor被关闭
if (c != null) {
c.close();
c = null;
}
}
// 查询通话记录文件夹
// ...这部分代码与上面的根文件夹查询逻辑类似只是查询条件和处理的Node不同
// 查询本地存在的其他文件夹
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); // 从哈希表中获取对应的Node对象
if (node != null) {
// 如果Node对象存在则进行一系列更新操作
mGTaskHashMap.remove(gid); // 从哈希表中移除该Node
mGidToNid.put(gid, c.getLong(SqlNote.ID_COLUMN)); // 更新全局ID到本地ID的映射
mNidToGid.put(c.getLong(SqlNote.ID_COLUMN), gid); // 更新本地ID到全局ID的映射
syncType = node.getSyncAction(c); // 获取同步类型
} else {
// 如果Node对象不存在则根据条件判断是本地添加还是远程删除
if (c.getString(SqlNote.GTASK_ID_COLUMN).trim().length() == 0) {
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 {
// 确保Cursor被关闭
if (c != null) {
c.close();
c = null;
}
}
}
//4
// 遍历mGTaskListHashMap中的每个条目用于远程添加文件夹
Iterator<Map.Entry<String, TaskList>> iter = mGTaskListHashMap.entrySet().iterator();
while (iter.hasNext()) {
Map.Entry<String, TaskList> entry = iter.next();
gid = entry.getKey(); // 获取条目的键(可能是任务列表的全局唯一标识符)
node = entry.getValue(); // 获取条目的值(任务列表对象)
// 如果mGTaskHashMap中已存在此gid的条目则移除它并调用doContentSync方法添加本地节点
if (mGTaskHashMap.containsKey(gid)) {
mGTaskHashMap.remove(gid);
doContentSync(Node.SYNC_ACTION_ADD_LOCAL, node, null);
}
}
// 如果没有取消操作则提交更新到GTaskClient
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:
// 从mMetaHashMap中根据cursor获取的gid获取MetaData对象并删除节点
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:
// 从mMetaHashMap中根据节点的gid获取MetaData对象并删除节点
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("unkown sync action type"); // 抛出异常
}
}
// 添加本地节点的方法
private void addLocalNode(Node node) throws NetworkFailureException {
if (mCancelled) {
return; // 如果已取消,则直接返回
}
SqlNote sqlNote;
// 判断节点类型如果是TaskList则根据名称决定是否为特殊文件夹
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); // 通话记录文件夹
} else {
sqlNote = new SqlNote(mContext);
sqlNote.setContent(node.getLocalJSONFromContent()); // 设置节点内容
sqlNote.setParentId(Notes.ID_ROOT_FOLDER); // 设置父节点为根文件夹
}
} else { // 如果不是TaskList则处理为Task
sqlNote = new SqlNote(mContext);
JSONObject js = node.getLocalJSONFromContent();
try {
// 处理节点内容中的note和data部分检查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)) {
// 如果ID已存在则移除ID
note.remove(NoteColumns.ID);
}
}
}
if (js.has(GTaskStringUtils.META_HEAD_DATA)) {
JSONArray dataArray = js.getJSONArray(GTaskStringUtils.META_HEAD_DATA);
for (int i = 0; i < dataArray.length(); i++) {
JSONObject data = dataArray.getJSONObject(i);
if (data.has(DataColumns.ID)) {
long dataId = data.getLong(DataColumns.ID);
if (DataUtils.existInDataDatabase(mContentResolver, dataId)) {
// 如果数据ID已存在则移除ID
data.remove(DataColumns.ID);
}
}
}
}
} catch (JSONException e) {
Log.w(TAG, e.toString()); // 打印警告日志
e.printStackTrace(); // 打印异常堆栈
}
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"); // 打印错误日志
throw new ActionFailureException("cannot add local node"); // 抛出异常
}
sqlNote.setParentId(parentId.longValue());
}
//5
// 创建一个本地节点
sqlNote.setGtaskId(node.getGid()); // 为SqlNote对象设置全局IDGid这个Gid来自传入的Node对象
sqlNote.commit(false); // 提交SqlNote对象到数据库false表示不是一次更新操作
// 更新Gid到Nid的映射
mGidToNid.put(node.getGid(), sqlNote.getId()); // 将Node的全局IDGid映射到SqlNote的本地IDNid
mNidToGid.put(sqlNote.getId(), node.getGid()); // 同时将SqlNote的本地IDNid映射回全局IDGid
// 更新元数据
updateRemoteMeta(node.getGid(), sqlNote); // 调用函数更新远程元数据传入Node的Gid和SqlNote对象
// 更新本地节点
private void updateLocalNode(Node node, Cursor c) throws NetworkFailureException {
if (mCancelled) { // 检查操作是否被取消
return;
}
SqlNote sqlNote;
// 更新本地笔记
sqlNote = new SqlNote(mContext, c); // 使用上下文和游标创建SqlNote对象
sqlNote.setContent(node.getLocalJSONFromContent()); // 设置SqlNote的内容为Node的本地JSON内容
// 获取父节点的ID如果是任务则通过Gid映射获取否则默认为根文件夹ID
Long parentId = (node instanceof Task) ? mGidToNid.get(((Task) node).getParent().getGid())
: new Long(Notes.ID_ROOT_FOLDER);
if (parentId == null) { // 如果父ID为空
Log.e(TAG, "cannot find task's parent id locally"); // 打印错误日志
throw new ActionFailureException("cannot update local node"); // 抛出操作失败异常
}
sqlNote.setParentId(parentId.longValue()); // 设置SqlNote的父ID
sqlNote.commit(true); // 提交更改到数据库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;
// 根据SqlNote的类型进行不同的处理
if (sqlNote.isNoteType()) { // 如果是笔记类型
Task task = new Task(); // 创建一个Task对象
task.setContentByLocalJSON(sqlNote.getContent()); // 设置Task的内容为SqlNote的内容
String parentGid = mNidToGid.get(sqlNote.getParentId()); // 获取父节点的Gid
if (parentGid == null) { // 如果父节点的Gid为空
Log.e(TAG, "cannot find task's parent tasklist"); // 打印错误日志
throw new ActionFailureException("cannot add remote task"); // 抛出操作失败异常
}
mGTaskListHashMap.get(parentGid).addChildTask(task); // 将Task添加到对应的TaskList中
GTaskClient.getInstance().createTask(task); // 调用GTaskClient的实例创建任务
n = (Node) task; // 将Task对象转换为Node对象
// 更新远程元数据
updateRemoteMeta(task.getGid(), sqlNote);
} else { // 如果不是笔记类型(假设是文件夹)
TaskList tasklist = null;
// 根据SqlNote的ID和名称判断文件夹是否已存在
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(); // 否则添加SqlNote的摘要作为后缀
// 遍历所有TaskList查找是否存在同名文件夹
Iterator<Map.Entry<String, TaskList>> iter = mGTaskListHashMap.entrySet().iterator();
while (iter.hasNext()) {
Map.Entry<String, TaskList> entry = iter.next();
String gid = entry.getKey();
TaskList list = entry.getValue();
if (list.getName().equals(folderName)) { // 如果找到同名文件夹
tasklist = list; // 将找到的文件夹赋值给tasklist
if (mGTaskHashMap.containsKey(gid)) {
mGTaskHashMap.remove(gid); // 从另一个映射中移除该Gid避免重复
}
break;
}
}
// 如果没有找到同名文件夹,则创建一个新的
if (tasklist == null) {
tasklist = new TaskList(); // 创建新的TaskList对象
tasklist.setContentByLocalJSON(sqlNote.getContent()); // 设置内容
GTaskClient.getInstance().createTaskList(tasklist); // 调用GTaskClient的实例创建任务列表
mGTaskListHashMap.put(tasklist.getGid(), tasklist); // 将新创建的TaskList添加到映射中
}
n = (Node) tasklist; // 将TaskList对象转换为Node对象
}
// 更新本地SqlNote的Gid和元数据
sqlNote.setGtaskId(n.getGid()); // 设置SqlNote的全局IDGid
sqlNote.commit(false); // 提交更改到数据库false表示不是更新操作
sqlNote.resetLocalModified(); // 重置SqlNote的本地修改标志
sqlNote.commit(true); // 再次提交更改true表示是更新操作
// 更新Gid到Nid和Nid到Gid的映射
mGidToNid.put(n.getGid(), sqlNote.getId()); // 更新Gid到Nid的映射
mNidToGid.put(sqlNote.getId(), n.getGid()); // 更新Nid到Gid的映射
}
// 更新远程节点
private void updateRemoteNode(Node node, Cursor c) throws NetworkFailureException {
if (mCancelled) { // 检查操作是否被取消
return;
}
SqlNote sqlNote = new SqlNote(mContext, c); // 使用上下文和游标创建SqlNote对象
// 更新远程节点
node.setContentByLocalJSON(sqlNote.getContent()); // 更新Node的内容为SqlNote的内容
GTaskClient.getInstance().addUpdateNode(node); // 调用GTaskClient的实例更新节点
// 更新元数据
updateRemoteMeta(node.getGid(), sqlNote); // 调用函数更新远程元数据
// 如果需要,移动任务
if (sqlNote.isNoteType()) { // 如果是笔记类型(任务)
Task task = (Task) node; // 将Node转换为Task对象
TaskList preParentList = task.getParent(); // 获取任务之前的父TaskList
String curParentGid = mNidToGid.get(sqlNote.getParentId()); // 获取当前父节点的Gid
if (curParentGid == null) { // 如果当前父节点的Gid为空
Log.e(TAG, "cannot find task's parent tasklist"); // 打印错误日志
throw new ActionFailureException("cannot update remote task"); // 抛出操作失败异常
}
TaskList curParentList = mGTaskListHashMap.get(curParentGid); // 获取当前的父TaskList
// 如果任务的父TaskList发生了变化则进行移动操作
if (preParentList != curParentList) {
preParentList.removeChildTask(task); // 从之前的父TaskList中移除任务
curParentList.addChildTask(task); // 将任务添加到当前的父TaskList中
GTaskClient.getInstance().moveTask(task, preParentList, curParentList); // 调用GTaskClient的实例移动任务
}
}
}
//6
// 清除本地修改标志
sqlNote.resetLocalModified();
// 提交sqlNote的更改到数据库true参数可能表示强制提交或某种特殊模式
sqlNote.commit(true);
}
// 更新远程元数据的方法,传入笔记的全局唯一标识符(gid)和SqlNote对象
private void updateRemoteMeta(String gid, SqlNote sqlNote) throws NetworkFailureException {
// 检查sqlNote对象非空且为笔记类型
if (sqlNote != null && sqlNote.isNoteType()) {
// 从哈希映射中获取gid对应的MetaData对象
MetaData metaData = mMetaHashMap.get(gid);
if (metaData != null) {
// 如果MetaData对象已存在更新其内容
metaData.setMeta(gid, sqlNote.getContent());
// 向GTaskClient实例添加更新节点的任务
GTaskClient.getInstance().addUpdateNode(metaData);
} else {
// 如果MetaData对象不存在创建一个新的
metaData = new MetaData();
// 设置MetaData的内容
metaData.setMeta(gid, sqlNote.getContent());
// 向任务列表添加子任务
mMetaList.addChildTask(metaData);
// 将MetaData对象添加到哈希映射中
mMetaHashMap.put(gid, metaData);
// 向GTaskClient实例添加创建任务的任务
GTaskClient.getInstance().createTask(metaData);
}
}
}
// 刷新本地同步ID的方法
private void refreshLocalSyncId() throws NetworkFailureException {
// 如果同步已取消,则直接返回
if (mCancelled) {
return;
}
// 清除哈希映射和任务列表,准备重新初始化
mGTaskHashMap.clear();
mGTaskListHashMap.clear();
mMetaHashMap.clear();
// 初始化任务列表
initGTaskList();
// 初始化Cursor对象用于查询数据库
Cursor c = null;
try {
// 查询笔记内容URI过滤掉系统类型和垃圾桶文件夹的笔记
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");
// 如果Cursor不为空遍历查询结果
if (c != null) {
while (c.moveToNext()) {
// 获取笔记的全局唯一标识符
String gid = c.getString(SqlNote.GTASK_ID_COLUMN);
// 从哈希映射中获取对应的Node对象
Node node = mGTaskHashMap.get(gid);
if (node != null) {
// 如果Node对象存在从哈希映射中移除
mGTaskHashMap.remove(gid);
// 更新本地数据库中该笔记的同步ID为Node的最后修改时间
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 {
// 如果Node对象不存在记录错误日志
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 {
// 最后确保Cursor被关闭
if (c != null) {
c.close();
c = null;
}
}
}
// 获取同步账户名称的方法
public String getSyncAccount() {
// 返回GTaskClient实例的同步账户名称
return GTaskClient.getInstance().getSyncAccount().name;
}
// 取消同步的方法
public void cancelSync() {
// 设置取消标志为true
mCancelled = true;
}