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

815 lines
34 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;
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>();
}
public static synchronized GTaskManager getInstance() {
if (mInstance == null) {
mInstance = new GTaskManager();
}
return mInstance;
}
public synchronized void setActivityContext(Activity activity) {
// used for getting authtoken
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 client = GTaskClient.getInstance();
client.resetUpdateArray();
// login google task
if (!mCancelled) {
if (!client.login(mActivity)) {
throw new NetworkFailureException("login google task failed");
}
}
// get the task list from google
asyncTask.publishProgess(mContext.getString(R.string.sync_progress_init_list));
initGTaskList();
// do content sync work
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;
}
// 定义一个私有方法initGTaskList用于初始化任务列表该方法可能会抛出NetworkFailureException异常
private void initGTaskList() throws NetworkFailureException {
// 如果操作被取消,则直接返回
if (mCancelled)
return;
// 获取GTaskClient的实例用于与Google Tasks API进行交互
GTaskClient client = GTaskClient.getInstance();
try {
// 从客户端获取任务列表的JSON数组
JSONArray jsTaskLists = client.getTaskLists();
// 首先初始化元数据列表
mMetaList = null;
// 遍历任务列表的JSON数组
for (int i = 0; i < jsTaskLists.length(); i++) {
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();
// 设置元数据列表的名称为特定的前缀加上"meta"
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);
// 将任务列表添加到哈希映射中以GID为键
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);
// 将任务添加到哈希映射中以GID为键
mGTaskHashMap.put(gid, task);
}
}
}
}
} catch (JSONException e) {
// 如果处理JSON对象时发生异常则记录错误并抛出ActionFailureException异常
Log.e(TAG, e.toString());
e.printStackTrace();
throw new ActionFailureException("initGTaskList: handing JSONObject failed");
}
}
// 定义一个同步内容的方法,可能会抛出网络失败异常
private void syncContent() throws NetworkFailureException {
int syncType; // 同步类型
Cursor c = null; // 用于查询结果的游标
String gid; // Google Tasks 的全局唯一标识符
Node node; // 表示一个笔记或文件夹的节点
// 清空本地删除ID映射表
mLocalDeleteIdMap.clear();
// 如果同步被取消,则直接返回
if (mCancelled) {
return;
}
// 处理本地删除的笔记
try {
// 查询垃圾文件夹中不是系统类型且父ID为垃圾文件夹ID的笔记
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); // 从哈希映射中获取节点
if (node != null) {
mGTaskHashMap.remove(gid); // 从哈希映射中移除节点
doContentSync(Node.SYNC_ACTION_DEL_REMOTE, node, c); // 执行远程删除同步
}
mLocalDeleteIdMap.add(c.getLong(SqlNote.ID_COLUMN)); // 将本地删除笔记的ID添加到映射表中
}
} else {
Log.w(TAG, "failed to query trash folder"); // 查询失败时记录警告日志
}
} finally {
if (c != null) {
c.close(); // 关闭游标
c = null;
}
}
// 首先同步文件夹
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); // 从哈希映射中获取节点
if (node != null) {
mGTaskHashMap.remove(gid); // 从哈希映射中移除节点
mGidToNid.put(gid, c.getLong(SqlNote.ID_COLUMN)); // 将gid映射到本地ID
mNidToGid.put(c.getLong(SqlNote.ID_COLUMN), gid); // 将本地ID映射到gid
syncType = node.getSyncAction(c); // 获取同步类型
} else {
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 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); // 执行本地新增同步
}
// 检查同步是否被取消,并清除本地删除表
if (!mCancelled) {
if (!DataUtils.batchDeleteNotes(mContentResolver, mLocalDeleteIdMap)) {
throw new ActionFailureException("failed to batch-delete local deleted notes"); // 如果删除失败,则抛出异常
}
}
// 刷新本地同步ID如果同步未被取消
if (!mCancelled) {
GTaskClient.getInstance().commitUpdate(); // 提交更新
refreshLocalSyncId(); // 刷新本地同步ID
}
}
/**
* 同步文件夹信息
*
* @throws NetworkFailureException 网络异常时抛出
*/
private void syncFolder() throws NetworkFailureException {
Cursor c = null; // 用于查询数据库结果集的游标
String gid; // Google Task的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 Task 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, "(_id=?)",
new String[]{String.valueOf(Notes.ID_CALL_RECORD_FOLDER)}, null);
if (c != null && c.moveToNext()) {
gid = c.getString(SqlNote.GTASK_ID_COLUMN);
node = mGTaskHashMap.get(gid);
if (node != null) {
// 如果节点存在,更新本地缓存和映射关系,并根据需要同步更新远程信息
mGTaskHashMap.remove(gid);
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);
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 Task 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)) {
// 如果本地缓存中存在该节点,则更新本地缓存并添加本地文件夹
mGTaskHashMap.remove(gid);
doContentSync(Node.SYNC_ACTION_ADD_LOCAL, node, null);
}
}
// 如果同步操作未被取消,则提交更新
if (!mCancelled)
GTaskClient.getInstance().commitUpdate();
}
// 定义一个私有方法,用于添加本地节点
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);
}
} else {
// 对于任务节点创建SqlNote对象并处理JSON内容
sqlNote = new SqlNote(mContext);
JSONObject js = node.getLocalJSONFromContent();
try {
// 检查并处理JSON中的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)) {
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)) {
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());
}
// 创建本地节点并更新gid-nid映射和meta信息
sqlNote.setGtaskId(node.getGid());
sqlNote.commit(false);
mGidToNid.put(node.getGid(), sqlNote.getId());
mNidToGid.put(sqlNote.getId(), node.getGid());
updateRemoteMeta(node.getGid(), sqlNote);
}
// 更新本地节点的方法
private void updateLocalNode(Node node, Cursor c) throws NetworkFailureException {
// 如果操作被取消,则直接返回
if (mCancelled) {
return;
}
SqlNote sqlNote;
// 使用Cursor初始化SqlNote对象用于更新本地笔记
sqlNote = new SqlNote(mContext, c);
// 从节点内容中获取本地JSON字符串并设置给SqlNote
sqlNote.setContent(node.getLocalJSONFromContent());
// 根据节点类型获取父节点ID如果是Task则通过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创建远程任务
GTaskClient.getInstance().createTask(task);
// 将任务对象赋值给n
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;
// 如果已存在同名任务列表,则从另一个映射中移除(可能是为了同步状态)
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
n = (Node) tasklist;
}
// update local note
sqlNote.setGtaskId(n.getGid());
sqlNote.commit(false);
sqlNote.resetLocalModified();
sqlNote.commit(true);
// gid-id mapping
mGidToNid.put(n.getGid(), sqlNote.getId());
mNidToGid.put(sqlNote.getId(), n.getGid());
}
// 更新远程节点的方法接收一个节点和一个Cursor对象作为参数
private void updateRemoteNode(Node node, Cursor c) throws NetworkFailureException {
// 如果同步操作被取消,则直接返回
if (mCancelled) {
return;
}
// 根据Cursor创建一个SqlNote对象用于操作数据库中的笔记数据
SqlNote sqlNote = new SqlNote(mContext, c);
// 更新节点的内容到远程服务器
node.setContentByLocalJSON(sqlNote.getContent());
GTaskClient.getInstance().addUpdateNode(node);
// 更新节点的元数据到远程服务器
updateRemoteMeta(node.getGid(), sqlNote);
// 如果当前节点是一个任务(笔记类型为任务),则检查并移动它到正确的父任务列表
if (sqlNote.isNoteType()) {
Task task = (Task) node;
TaskList preParentList = task.getParent(); // 获取任务当前所在的父任务列表
// 根据数据库中的父ID查找对应的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);
}
// 更新远程元数据的方法接收一个GID和一个SqlNote对象作为参数
private void updateRemoteMeta(String gid, SqlNote sqlNote) throws NetworkFailureException {
if (sqlNote != null && sqlNote.isNoteType()) { // 如果SqlNote对象不为空且是任务类型
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); // 获取GID
Node node = mGTaskHashMap.get(gid); // 从哈希表中获取节点
if (node != null) {
mGTaskHashMap.remove(gid); // 从哈希表中移除节点
ContentValues values = new ContentValues();
values.put(NoteColumns.SYNC_ID, node.getLastModified()); // 更新同步ID为节点的最后修改时间
mContentResolver.update(ContentUris.withAppendedId(Notes.CONTENT_NOTE_URI,
c.getLong(SqlNote.ID_COLUMN)), values, null, null); // 更新数据库中的记录
} else {
Log.e(TAG, "something is missed"); // 如果找不到对应的节点,记录错误日志
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(); // 关闭Cursor
c = null;
}
}
}
public String getSyncAccount() {
return GTaskClient.getInstance().getSyncAccount().name;
}
public void cancelSync() {
mCancelled = true;
}
}