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.
rass/gtask/data/TaskList.java

379 lines
19 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.data;
import android.database.Cursor;
import android.util.Log;
import net.micode.notes.data.Notes;
import net.micode.notes.data.Notes.NoteColumns;
import net.micode.notes.gtask.exception.ActionFailureException;
import net.micode.notes.tool.GTaskStringUtils;
import org.json.JSONException;
import org.json.JSONObject;
import java.util.ArrayList;
// TaskList类继承自Node类代表一个任务列表相关的实体包含任务列表的各种属性以及对任务列表进行操作的诸多方法
// 例如创建、更新任务列表的JSON表示从不同来源的JSON数据设置任务列表内容获取任务列表的本地JSON表示
// 还有对任务列表中包含的子任务Task类型进行添加、删除、移动、查找等操作的方法。
public class TaskList extends Node {
// 用于在日志输出中标识该类的标签,取类的简单名称,方便在日志里区分该类相关的记录
private static final String TAG = TaskList.class.getSimpleName();
// 存储任务列表在某个顺序中的索引值初始化为1可通过相应方法进行设置和获取用于确定任务列表的顺序位置等情况
private int mIndex;
// 用于存储该任务列表包含的所有子任务以ArrayList<Task>形式存在,初始化为空列表,后续可通过相关方法对子任务进行添加、删除等操作
private ArrayList<Task> mChildren;
// 默认构造函数调用父类的构造函数通过super()并初始化任务列表相关的成员变量创建一个空的子任务列表并设置初始索引值为1
public TaskList() {
super();
mChildren = new ArrayList<Task>();
mIndex = 1;
}
// 生成用于创建任务列表的JSON对象的方法按照特定的格式和要求构建包含任务列表创建相关信息的JSON对象若JSON构建过程出现异常则抛出异常
public JSONObject getCreateAction(int actionId) {
JSONObject js = new JSONObject();
try {
// 设置action_type字段表示操作类型为创建任务列表对应的值从GTaskStringUtils中获取
js.put(GTaskStringUtils.GTASK_JSON_ACTION_TYPE,
GTaskStringUtils.GTASK_JSON_ACTION_TYPE_CREATE);
// 设置action_id字段传入给定的actionId作为该创建操作的唯一标识符
js.put(GTaskStringUtils.GTASK_JSON_ACTION_ID, actionId);
// 设置index字段使用当前任务列表的索引值mIndex来指定其在相关顺序中的位置
js.put(GTaskStringUtils.GTASK_JSON_INDEX, mIndex);
// 构建表示任务列表实体信息的JSONObject包含任务列表的名称、创建者ID这里设为"null",可能需根据实际情况调整)、实体类型等关键信息
JSONObject entity = new JSONObject();
entity.put(GTaskStringUtils.GTASK_JSON_NAME, getName());
entity.put(GTaskStringUtils.GTASK_JSON_CREATOR_ID, "null");
entity.put(GTaskStringUtils.GTASK_JSON_ENTITY_TYPE,
GTaskStringUtils.GTASK_JSON_TYPE_GROUP);
// 将包含任务列表实体信息的JSONObject添加到外层的js对象中对应键为GTaskStringUtils.GTASK_JSON_ENTITY_DELTA
js.put(GTaskStringUtils.GTASK_JSON_ENTITY_DELTA, entity);
} catch (JSONException e) {
// 如果在构建JSON对象过程中出现异常记录错误日志打印异常堆栈信息并抛出表示创建任务列表JSON对象失败的异常
Log.e(TAG, e.toString());
e.printStackTrace();
throw new ActionFailureException("fail to generate tasklist-create jsonobject");
}
return js;
}
// 生成用于更新任务列表的JSON对象的方法按照特定格式构建包含任务列表更新相关信息的JSON对象若JSON构建出现异常则抛出异常
public JSONObject getUpdateAction(int actionId) {
JSONObject js = new JSONObject();
try {
// 设置action_type字段表示操作类型为更新任务列表对应的值从GTaskStringUtils中获取
js.put(GTaskStringUtils.GTASK_JSON_ACTION_TYPE,
GTaskStringUtils.GTASK_JSON_ACTION_TYPE_UPDATE);
// 设置action_id字段传入给定的actionId作为该更新操作的唯一标识符
js.put(GTaskStringUtils.GTASK_JSON_ACTION_ID, actionId);
// 设置id字段使用任务列表自身的唯一标识符通过getGid方法获取来指定要更新的任务列表ID
js.put(GTaskStringUtils.GTASK_JSON_ID, getGid());
// 构建表示任务列表实体信息更新部分的JSONObject包含任务列表的名称以及是否已删除等关键更新信息
JSONObject entity = new JSONObject();
entity.put(GTaskStringUtils.GTASK_JSON_NAME, getName());
entity.put(GTaskStringUtils.GTASK_JSON_DELETED, getDeleted());
// 将包含任务列表实体更新信息的JSONObject添加到外层的js对象中对应键为GTaskStringUtils.GTASK_JSON_ENTITY_DELTA
js.put(GTaskStringUtils.GTASK_JSON_ENTITY_DELTA, entity);
} catch (JSONException e) {
// 如果在构建JSON对象过程中出现异常记录错误日志打印异常堆栈信息并抛出表示更新任务列表JSON对象失败的异常
Log.e(TAG, e.toString());
e.printStackTrace();
throw new ActionFailureException("fail to generate tasklist-update jsonobject");
}
return js;
}
// 从远程传来的JSON对象中设置任务列表内容的方法根据JSON对象中包含的不同字段信息来更新任务列表的对应属性若JSON解析出现异常则抛出异常
public void setContentByRemoteJSON(JSONObject js) {
if (js!= null) {
try {
// 如果JSON对象中包含任务列表ID字段GTaskStringUtils.GTASK_JSON_ID则设置任务列表的唯一标识符通过setGid方法
if (js.has(GTaskStringUtils.GTASK_JSON_ID)) {
setGid(js.getString(GTaskStringUtils.GTASK_JSON_ID));
}
// 如果JSON对象中包含最后修改时间字段GTaskStringUtils.GTASK_JSON_LAST_MODIFIED则设置任务列表的最后修改时间通过setLastModified方法
if (js.has(GTaskStringUtils.GTASK_JSON_LAST_MODIFIED)) {
setLastModified(js.getLong(GTaskStringUtils.GTASK_JSON_LAST_MODIFIED));
}
// 如果JSON对象中包含任务列表名称字段GTaskStringUtils.GTASK_JSON_NAME则设置任务列表的名称通过setName方法
if (js.has(GTaskStringUtils.GTASK_JSON_NAME)) {
setName(js.getString(GTaskStringUtils.GTASK_JSON_NAME));
}
} catch (JSONException e) {
// 如果在解析JSON对象设置任务列表内容过程中出现异常记录错误日志打印异常堆栈信息并抛出表示从JSON对象获取任务列表内容失败的异常
Log.e(TAG, e.toString());
e.printStackTrace();
throw new ActionFailureException("fail to get tasklist content from jsonobject");
}
}
}
// 从本地的JSON对象中设置任务列表内容的方法根据本地JSON格式特点包含特定的头部信息等提取相关信息来设置任务列表名称若格式不符合要求会记录相应日志
public void setContentByLocalJSON(JSONObject js) {
if (js == null ||!js.has(GTaskStringUtils.META_HEAD_NOTE)) {
Log.w(TAG, "setContentByLocalJSON: nothing is avaiable");
}
try {
JSONObject folder = js.getJSONObject(GTaskStringUtils.META_HEAD_NOTE);
if (folder.getInt(NoteColumns.TYPE) == Notes.TYPE_FOLDER) {
// 如果是文件夹类型Notes.TYPE_FOLDER从JSON对象中获取摘要信息作为名称并添加特定前缀GTaskStringUtils.MIUI_FOLDER_PREFFIX后设置为任务列表名称
String name = folder.getString(NoteColumns.SNIPPET);
setName(GTaskStringUtils.MIUI_FOLDER_PREFFIX + name);
} else if (folder.getInt(NoteColumns.TYPE) == Notes.TYPE_SYSTEM) {
// 如果是系统类型Notes.TYPE_SYSTEM根据不同的系统文件夹ID设置相应的默认名称添加特定前缀
if (folder.getLong(NoteColumns.ID) == Notes.ID_ROOT_FOLDER)
setName(GTaskStringUtils.MIUI_FOLDER_PREFFIX + GTaskStringUtils.FOLDER_DEFAULT);
else if (folder.getLong(NoteColumns.ID) == Notes.ID_CALL_RECORD_FOLDER)
setName(GTaskStringUtils.MIUI_FOLDER_PREFFIX
+ GTaskStringUtils.FOLDER_CALL_NOTE);
else
Log.e(TAG, "invalid system folder");
} else {
Log.e(TAG, "error type");
}
} catch (JSONException e) {
Log.e(TAG, e.toString());
e.printStackTrace();
}
}
// 根据任务列表当前内容生成用于本地表示的JSON对象的方法按照特定格式构建包含任务列表相关信息的JSON对象若JSON构建出现异常则返回null
public JSONObject getLocalJSONFromContent() {
try {
JSONObject js = new JSONObject();
JSONObject folder = new JSONObject();
String folderName = getName();
// 如果任务列表名称以特定前缀GTaskStringUtils.MIUI_FOLDER_PREFFIX开头则去除该前缀获取实际名称
if (getName().startsWith(GTaskStringUtils.MIUI_FOLDER_PREFFIX))
folderName = folderName.substring(GTaskStringUtils.MIUI_FOLDER_PREFFIX.length(),
folderName.length());
// 将实际名称设置到JSON对象的对应字段NoteColumns.SNIPPET用于表示任务列表的摘要或显示名称等信息
folder.put(NoteColumns.SNIPPET, folderName);
// 根据实际名称判断任务列表类型系统类型或文件夹类型并设置到JSON对象的对应字段NoteColumns.TYPE
if (folderName.equals(GTaskStringUtils.FOLDER_DEFAULT)
|| folderName.equals(GTaskStringUtils.FOLDER_CALL_NOTE))
folder.put(NoteColumns.TYPE, Notes.TYPE_SYSTEM);
else
folder.put(NoteColumns.TYPE, Notes.TYPE_FOLDER);
// 将包含任务列表相关信息的folder对象添加到外层的js对象中对应键为GTaskStringUtils.META_HEAD_NOTE
js.put(GTaskStringUtils.META_HEAD_NOTE, folder);
return js;
} catch (JSONException e) {
Log.e(TAG, e.toString());
e.printStackTrace();
return null;
}
}
// 根据数据库游标Cursor以及任务列表当前状态判断同步操作类型的方法通过比较数据库中的记录和任务列表的相关属性如Gtask ID、修改时间等来确定需要执行的同步动作若出现异常则返回表示错误的同步动作类型
public int getSyncAction(Cursor c) {
try {
if (c.getInt(SqlNote.LOCAL_MODIFIED_COLUMN) == 0) {
// 如果本地没有更新本地修改标记为0
if (c.getLong(SqlNote.SYNC_ID_COLUMN) == getLastModified()) {
// 两边都没有更新,返回无需同步的动作类型
return SYNC_ACTION_NONE;
} else {
// 应用远程数据到本地,返回更新本地的同步动作类型
return SYNC_ACTION_UPDATE_LOCAL;
}
} else {
// 验证Gtask ID是否匹配不匹配则记录错误日志并返回表示错误的同步动作类型
if (!c.getString(SqlNote.GTASK_ID_COLUMN).equals(getGid())) {
Log.e(TAG, "gtask id doesn't match");
return SYNC_ACTION_ERROR;
}
if (c.getLong(SqlNote.SYNC_ID_COLUMN) == getLastModified()) {
// 只有本地有修改,返回更新远程的同步动作类型
return SYNC_ACTION_UPDATE_REMOTE;
} else {
// 对于文件夹类型的任务列表冲突情况,直接应用本地修改(这里与普通任务可能有不同处理逻辑),返回更新远程的同步动作类型
return SYNC_ACTION_UPDATE_REMOTE;
}
}
} catch (Exception e) {
Log.e(TAG, e.toString());
e.printStackTrace();
}
return SYNC_ACTION_ERROR;
}
// 获取任务列表中子任务数量的方法直接返回存储子任务的列表mChildren的大小即子任务的个数
public int getChildTaskCount() {
return mChildren.size();
}
// 向任务列表中添加子任务的方法将给定的Task对象添加到子任务列表mChildren同时设置该子任务的前一个兄弟任务和父任务相关属性返回添加操作是否成功的布尔值结果
public boolean addChildTask(Task task) {
boolean ret = false;
if (task!= null &&!mChildren.contains(task)) {
ret = mChildren.add(task);
if (ret) {
// 需要设置子任务的前一个兄弟任务如果列表为空则为null否则为列表中最后一个元素和父任务当前任务列表本身
task.setPriorSibling(mChildren.isEmpty()? null : mChildren
.get(mChildren.size() - 1));
task.setParent(this);
}
}
return ret;
}
// 向任务列表指定索引位置添加子任务的方法根据给定的索引值将Task对象插入到子任务列表中合适位置同时更新相关子任务之间的顺序关系前一个兄弟任务的指向等
// 如果索引值不合法则记录错误日志并返回false表示添加操作失败。
public boolean addChildTask(Task task, int index) {
if (index < 0 || index > mChildren.size()) {
Log.e(TAG, "add child task: invalid index");
return false;
}
int pos = mChildren.indexOf(task);
if (task!= null && pos == -1) {
mChildren.add(index, task);
// 更新任务列表中相关子任务的顺序关系
Task preTask = null;
Task afterTask = null;
if (index!= 0)
preTask = mChildren.get(index - 1);
if (index!= mChildren.size() - 1)
afterTask = mChildren.get(index + 1);
task.setPriorSibling(preTask);
if (afterTask!= null)
afterTask.setPriorSibling(task);
}
return true;
}
// 从任务列表中移除指定子任务的方法先从子任务列表中移除该Task对象然后重置该子任务的前一个兄弟任务和父任务相关属性为null
// 同时更新任务列表中剩余子任务之间的顺序关系(前一个兄弟任务的指向等),返回移除操作是否成功的布尔值结果。
public boolean removeChildTask(Task task) {
boolean ret = false;
int index = mChildren.indexOf(task);
if (index!= -1) {
ret = mChildren.remove(task);
if (ret) {
// 重置子任务的前一个兄弟任务和父任务相关属性为null
task.setPriorSibling(null);
task.setParent(null);
// 更新任务列表中剩余子任务的顺序关系
if (index!= mChildren.size()) {
mChildren.get(index).setPriorSibling(
index == 0? null : mChildren.get(index - 1));
}
}
}
return ret;
}
// 在任务列表中移动指定子任务到新的索引位置的方法,先判断索引值是否合法以及子任务是否在列表中,然后通过先移除再添加到新位置的方式实现移动操作,
// 如果操作过程中出现索引不合法、子任务不在列表中等情况则记录相应错误日志并返回false表示移动操作失败成功则返回true。
public boolean moveChildTask(Task task, int index) {
if (index < 0 || index >= mChildren.size()) {
Log.e(TAG, "move child task: invalid index");
return false;
}
int pos = mChildren.indexOf(task);
if (pos == -1) {
Log.e(TAG, "move child task: the task should in the list");
return false;
}
if (pos == index)
return true;
return (removeChildTask(task) && addChildTask(task, index));
}
// 根据给定的子任务唯一标识符Gid在任务列表中查找对应的子任务的方法遍历子任务列表比较每个子任务的唯一标识符与给定值是否相等
// 如果找到则返回对应的子任务对象未找到则返回null。
public Task findChildTaskByGid(String gid) {
for (int i = 0; i < mChildren.size(); i++) {
Task t = mChildren.get(i);
if (t.getGid().equals(gid)) {
return t;
}
}
return null;
}
// 获取指定子任务在任务列表中的索引位置的方法通过调用子任务列表mChildren的indexOf方法来获取给定子任务的索引值并返回
public int getChildTaskIndex(Task task) {
return mChildren.indexOf(task);
}
// 根据给定的索引值从任务列表中获取对应子任务的方法,如果索引
public Task getChildTaskByIndex(int index) {
if (index < 0 || index >= mChildren.size()) {
Log.e(TAG, "getTaskByIndex: invalid index");
return null;
}
return mChildren.get(index);
}
public Task getChilTaskByGid(String gid) {
for (Task task : mChildren) {
if (task.getGid().equals(gid))
return task;
}
return null;
}
public ArrayList<Task> getChildTaskList() {
return this.mChildren;
}
public void setIndex(int index) {
this.mIndex = index;
}
public int getIndex() {
return this.mIndex;
}
}