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

876 lines
38 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;
// 导入必要的Android和Java类
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类
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;
// 声明单例实例和成员变量
private static GTaskManager mInstance = null;
private Activity mActivity;
private Context mContext;
private ContentResolver mContentResolver;
private boolean mSyncing;
private boolean mCancelled;
private HashMap<String, TaskList> mGTaskListHashMap;
private HashMap<String, Node> mGTaskHashMap;
private HashMap<String, MetaData> mMetaHashMap;
private TaskList mMetaList;
private HashSet<Long> mLocalDeleteIdMap;
private HashMap<String, Long> mGidToNid;
private HashMap<Long, String> mNidToGid;
// 私有构造函数,用于创建单例
private GTaskManager() {
// 初始化成员变量
mSyncing = false;
mCancelled = false;
mGTaskListHashMap = new HashMap<String, TaskList>();
mGTaskHashMap = new HashMap<String, Node>();
mMetaHashMap = new HashMap<String, MetaData>();
mMetaList = null;
mLocalDeleteIdMap = new HashSet<Long>();
mGidToNid = new HashMap<String, Long>();
mNidToGid = new HashMap<Long, String>();
}
// 获取GTaskManager单例实例
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;
// 清除所有哈希映射和集合
mGTaskListHashMap.clear();
mGTaskHashMap.clear();
mMetaHashMap.clear();
mLocalDeleteIdMap.clear();
mGidToNid.clear();
mNidToGid.clear();
try {
// 获取GTaskClient实例
GTaskClient client = GTaskClient.getInstance();
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 {
// 清除所有哈希映射和集合,重置同步状态
mGTaskListHashMap.clear();
mGTaskHashMap.clear();
mMetaHashMap.clear();
mLocalDeleteIdMap.clear();
mGidToNid.clear();
mNidToGid.clear();
mSyncing = false;
}
// 返回同步结果状态
return mCancelled ? STATE_SYNC_CANCELLED : STATE_SUCCESS;
}
// 定义一个初始化任务列表的方法,该方法可能会抛出网络失败异常
private void initGTaskList() throws NetworkFailureException {
// 如果任务被取消,则直接返回,不执行后续操作
if (mCancelled)
return;
// 获取GTaskClient的实例
GTaskClient client = GTaskClient.getInstance();
try {
// 从客户端获取任务列表的JSON数组
JSONArray jsTaskLists = client.getTaskLists();
// 首先初始化元数据列表
mMetaList = null;
// 遍历所有的任务列表
for (int i = 0; i < jsTaskLists.length(); i++) {
// 获取当前任务列表的JSON对象
JSONObject object = jsTaskLists.getJSONObject(i);
// 获取任务列表的ID
String gid = object.getString(GTaskStringUtils.GTASK_JSON_ID);
// 获取任务列表的名称
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);
// 加载元数据
JSONArray jsMetas = client.getTaskList(gid);
// 遍历所有的元数据
for (int j = 0; j < jsMetas.length(); j++) {
object = (JSONObject) jsMetas.getJSONObject(j);
MetaData metaData = new MetaData();
// 使用JSON对象设置元数据的内容
metaData.setContentByRemoteJSON(object);
// 如果该元数据值得保存
if (metaData.isWorthSaving()) {
// 将元数据添加到元数据列表中
mMetaList.addChildTask(metaData);
// 如果元数据有GID则将其添加到哈希映射中
if (metaData.getGid() != null) {
mMetaHashMap.put(metaData.getRelatedGid(), metaData);
}
}
}
}
}
// 如果元数据列表不存在,则创建它
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);
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);
// 将任务列表添加到哈希映射中
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);
gid = object.getString(GTaskStringUtils.GTASK_JSON_ID);
Task task = new Task();
// 使用JSON对象设置任务的内容
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: handing JSONObject failed");
}
}
// 定义一个名为syncContent的私有方法该方法可能会抛出NetworkFailureException异常
private void syncContent() throws NetworkFailureException {
// 定义一个变量syncType用于存储同步类型
int syncType;
// 定义一个Cursor对象c初始化为null用于查询数据库
Cursor c = null;
// 定义一个字符串变量gid用于存储Google任务的ID
String gid;
// 定义一个Node对象node用于表示Google任务节点
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()) {
// 获取Google任务的ID
gid = c.getString(SqlNote.GTASK_ID_COLUMN);
// 从HashMap中获取对应的Node对象
node = mGTaskHashMap.get(gid);
if (node != null) {
// 从HashMap中移除该Node对象
mGTaskHashMap.remove(gid);
// 执行远程删除操作
doContentSync(Node.SYNC_ACTION_DEL_REMOTE, node, c);
}
// 将本地笔记ID添加到删除ID映射表中
mLocalDeleteIdMap.add(c.getLong(SqlNote.ID_COLUMN));
}
} else {
// 如果查询失败,记录警告日志
Log.w(TAG, "failed to query trash folder");
}
} finally {
// 关闭Cursor对象释放资源
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()) {
// 获取Google任务的ID
gid = c.getString(SqlNote.GTASK_ID_COLUMN);
// 从HashMap中获取对应的Node对象
node = mGTaskHashMap.get(gid);
if (node != null) {
// 从HashMap中移除该Node对象
mGTaskHashMap.remove(gid);
// 更新gid到nid的映射以及nid到gid的映射
mGidToNid.put(gid, c.getLong(SqlNote.ID_COLUMN));
mNidToGid.put(c.getLong(SqlNote.ID_COLUMN), gid);
// 获取同步类型
syncType = node.getSyncAction(c);
} else {
// 如果gid为空表示是本地新增的笔记
if (c.getString(SqlNote.GTASK_ID_COLUMN).trim().length() == 0) {
syncType = Node.SYNC_ACTION_ADD_REMOTE;
} else {
// 如果gid不为空但不在HashMap中表示远程已删除
syncType = Node.SYNC_ACTION_DEL_LOCAL;
}
}
// 执行同步操作
doContentSync(syncType, node, c);
}
} else {
// 如果查询失败,记录警告日志
Log.w(TAG, "failed to query existing note in database");
}
} finally {
// 关闭Cursor对象释放资源
if (c != null) {
c.close();
c = null;
}
}
// 遍历HashMap中剩余的项执行本地添加操作
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)) {
// 如果批量删除失败抛出ActionFailureException异常
throw new ActionFailureException("failed to batch-delete local deleted notes");
}
}
// 刷新本地同步ID
if (!mCancelled) {
// 提交更新并刷新本地同步ID
GTaskClient.getInstance().commitUpdate();
refreshLocalSyncId();
}
}
// 定义一个私有的同步文件夹方法,该方法可能会抛出网络失败异常
private void syncFolder() throws NetworkFailureException {
Cursor c = null; // 初始化一个游标对象,用于数据库查询
String gid; // 用于存储从数据库查询得到的Google任务ID
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); // 获取Google任务ID
node = mGTaskHashMap.get(gid); // 从哈希表中获取节点对象
if (node != null) {
// 如果节点存在,则更新本地和远程的映射关系,并根据需要更新远程名称
mGTaskHashMap.remove(gid);
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,
"(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);
node = mGTaskHashMap.get(gid);
if (node != null) {
// 如果节点存在,则更新映射关系,并根据节点的同步动作进行同步
mGTaskHashMap.remove(gid);
mGidToNid.put(gid, c.getLong(SqlNote.ID_COLUMN));
mNidToGid.put(c.getLong(SqlNote.ID_COLUMN), gid);
syncType = node.getSyncAction(c);
} else {
// 如果节点不存在则根据Google任务ID是否为空决定是本地添加还是远程删除
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 {
// 确保游标被关闭
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();
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; // 定义一个MetaData对象用于后续操作
// 根据不同的同步类型执行不同的操作
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)); // 从哈希映射中获取MetaData对象
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()); // 从哈希映射中获取MetaData对象
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: // 更新冲突,当前简单使用本地更新
// merging both modifications maybe a good idea
// right now just use local update simply
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; // 定义一个SqlNote对象用于后续操作
// 判断节点类型,如果是任务列表,则进行特殊处理
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); // 设置父节点ID为根文件夹ID
}
} else { // 如果是任务节点,则进行通用处理
sqlNote = new SqlNote(mContext);
JSONObject js = node.getLocalJSONFromContent(); // 获取节点内容的JSON对象
try {
// 检查JSON对象中是否包含特定字段并根据需要进行处理
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); // 设置节点内容的JSON对象
// 获取父节点的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());
}
// 创建本地节点
sqlNote.setGtaskId(node.getGid()); // 设置节点的全局ID
sqlNote.commit(false); // 提交节点到数据库
// 更新全局ID到本地ID的映射
mGidToNid.put(node.getGid(), sqlNote.getId());
mNidToGid.put(sqlNote.getId(), node.getGid());
// 更新MetaData
updateRemoteMeta(node.getGid(), sqlNote);
}
// 定义一个私有方法,用于更新本地节点,参数包括节点和数据库游标
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如果是任务则通过GID查找否则使用根文件夹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());
// 提交更改到数据库true表示立即写入
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);
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.getInstance().createTask(task);
n = (Node) task;
// 更新远程元数据
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();
// 遍历所有任务列表,查找是否存在同名文件夹
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;
// 如果存在同名文件夹且已经在GTaskHashMap中则移除
if (mGTaskHashMap.containsKey(gid)) {
mGTaskHashMap.remove(gid);
}
break;
}
}
// 如果没有找到同名文件夹,则创建新文件夹
if (tasklist == null) {
tasklist = new TaskList();
tasklist.setContentByLocalJSON(sqlNote.getContent());
GTaskClient.getInstance().createTaskList(tasklist);
mGTaskListHashMap.put(tasklist.getGid(), tasklist);
}
n = (Node) tasklist;
}
// 更新本地SqlNote的GtaskID
sqlNote.setGtaskId(n.getGid());
// 提交更改到数据库false表示延迟写入
sqlNote.commit(false);
// 重置本地修改标记
sqlNote.resetLocalModified();
// 再次提交更改到数据库true表示立即写入
sqlNote.commit(true);
// 更新GID和ID的映射关系
mGidToNid.put(n.getGid(), sqlNote.getId());
mNidToGid.put(sqlNote.getId(), n.getGid());
}
// 定义一个方法,用于更新远程节点。该方法接收一个节点和一个游标作为参数,并可能抛出网络失败异常。
private void updateRemoteNode(Node node, Cursor c) throws NetworkFailureException {
// 如果同步被取消,则直接返回。
if (mCancelled) {
return;
}
// 使用上下文和游标创建一个SqlNote对象。
SqlNote sqlNote = new SqlNote(mContext, c);
// 更新远程内容将节点的内容设置为从SqlNote获取的本地JSON内容。
node.setContentByLocalJSON(sqlNote.getContent());
// 调用GTaskClient的实例来添加或更新这个节点。
GTaskClient.getInstance().addUpdateNode(node);
// 更新节点的元数据。
updateRemoteMeta(node.getGid(), sqlNote);
// 如果需要,移动任务。
if (sqlNote.isNoteType()) {
Task task = (Task) node;
TaskList preParentList = task.getParent();
// 获取当前父任务的GID。
String curParentGid = mNidToGid.get(sqlNote.getParentId());
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);
}
}
// 清除本地的修改标志,并提交更改。
sqlNote.resetLocalModified();
sqlNote.commit(true);
}
// 定义一个方法,用于更新远程元数据。
private void updateRemoteMeta(String gid, SqlNote sqlNote) throws NetworkFailureException {
if (sqlNote != null && sqlNote.isNoteType()) {
MetaData metaData = mMetaHashMap.get(gid);
if (metaData != null) {
// 如果元数据已存在,则更新它。
metaData.setMeta(gid, sqlNote.getContent());
GTaskClient.getInstance().addUpdateNode(metaData);
} 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;
}
// 清除现有的哈希映射,并初始化任务列表。
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()) {
String gid = c.getString(SqlNote.GTASK_ID_COLUMN);
Node node = mGTaskHashMap.get(gid);
if (node != null) {
// 如果找到了对应的节点则更新其同步ID。
mGTaskHashMap.remove(gid);
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 {
// 如果找不到对应的节点,则记录错误。
Log.e(TAG, "something is missed");
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() {
return GTaskClient.getInstance().getSyncAccount().name;
}
// 定义一个方法,用于取消同步。
public void cancelSync() {
mCancelled = true;
}