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.
text123/GTaskManager.java

1042 lines
45 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.
*/
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<String, TaskList> mGTaskListHashMap;
// 用于存储节点可能是任务相关节点的HashMap键为节点的唯一标识符值为对应的节点对象
private HashMap<String, Node> mGTaskHashMap;
// 用于存储元数据的HashMap键为元数据的唯一标识符值为对应的元数据对象
private HashMap<String, MetaData> mMetaHashMap;
// 存储元数据的任务列表
private TaskList mMetaList;
// 用于存储本地删除的任务ID的HashSet
private HashSet<Long> mLocalDeleteIdMap;
// 用于将全局唯一标识符Gid映射到本地节点标识符Nid的HashMap
private HashMap<String, Long> mGidToNid;
// 用于将本地节点标识符Nid映射到全局唯一标识符Gid的HashMap
private HashMap<Long, String> mNidToGid;
// 私有构造函数用于初始化GTaskManager的实例
private GTaskManager() {
// 初始化同步状态为未进行
mSyncing = false;
// 初始化取消状态为未取消
mCancelled = false;
// 初始化用于存储任务列表的HashMap
mGTaskListHashMap = new HashMap<String, TaskList>();
// 初始化用于存储节点的HashMap
mGTaskHashMap = new HashMap<String, Node>();
// 初始化用于存储元数据的HashMap
mMetaHashMap = new HashMap<String, MetaData>();
// 初始化元数据任务列表为null
mMetaList = null;
// 初始化用于存储本地删除ID的HashSet
mLocalDeleteIdMap = new HashSet<Long>();
// 初始化从全局唯一标识符Gid到本地节点标识符Nid的HashMap
mGidToNid = new HashMap<String, Long>();
// 初始化从本地节点标识符Nid到全局唯一标识符Gid的HashMap
mNidToGid = new HashMap<Long, String>();
}
// 获取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<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可能被其他线程设置所以需要再次检查
// 清除本地删除表
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<Map.Entry<String, TaskList>> iter = mGTaskListHashMap.entrySet().iterator();
while (iter.hasNext()) { // 遍历任务列表哈希表中的每一项
Map.Entry<String, TaskList> 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<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
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;
}}