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

2 months ago
/*
* 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;
}