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.
git.text/src/gtask/data/TaskList.java

496 lines
16 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;
/**
* Google任务列表的数据模型类继承自Node类。
* <p>
* 负责管理任务列表的元数据信息和子任务集合支持与Google Tasks服务的同步操作。
* 主要功能包括:
* <ul>
* <li>生成创建/更新任务列表的JSON动作对象</li>
* <li>从本地/远程JSON数据加载任务列表内容</li>
* <li>管理子任务集合(添加、删除、移动、查找)</li>
* <li>确定同步动作类型</li>
* </ul>
* </p>
*/
public class TaskList extends Node {
private static final String TAG = TaskList.class.getSimpleName();
/**
* 任务列表在Google任务中的索引位置用于排序
*/
private int mIndex;
/**
* 子任务集合,存储该任务列表下的所有任务对象
*/
private ArrayList<Task> mChildren;
/**
* 构造函数,初始化任务列表对象
* <p>
* 创建空的子任务集合并将索引初始化为1
* </p>
*/
public TaskList() {
super();
mChildren = new ArrayList<Task>();
mIndex = 1;
}
/**
* 生成创建任务列表的JSON动作对象
* <p>
* 用于向Google Tasks服务发送创建任务列表的请求
* </p>
* @param actionId 动作ID用于标识该创建操作
* @return 创建任务列表的JSON对象
* @throws ActionFailureException 如果生成JSON对象失败
*/
public JSONObject getCreateAction(int actionId) {
JSONObject js = new JSONObject();
try {
// action_type
js.put(GTaskStringUtils.GTASK_JSON_ACTION_TYPE,
GTaskStringUtils.GTASK_JSON_ACTION_TYPE_CREATE);
// action_id
js.put(GTaskStringUtils.GTASK_JSON_ACTION_ID, actionId);
// index
js.put(GTaskStringUtils.GTASK_JSON_INDEX, mIndex);
// entity_delta
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);
js.put(GTaskStringUtils.GTASK_JSON_ENTITY_DELTA, entity);
} catch (JSONException e) {
Log.e(TAG, e.toString());
e.printStackTrace();
throw new ActionFailureException("fail to generate tasklist-create jsonobject");
}
return js;
}
/**
* 生成更新任务列表的JSON动作对象
* <p>
* 用于向Google Tasks服务发送更新任务列表的请求
* </p>
* @param actionId 动作ID用于标识该更新操作
* @return 更新任务列表的JSON对象
* @throws ActionFailureException 如果生成JSON对象失败
*/
public JSONObject getUpdateAction(int actionId) {
JSONObject js = new JSONObject();
try {
// action_type
js.put(GTaskStringUtils.GTASK_JSON_ACTION_TYPE,
GTaskStringUtils.GTASK_JSON_ACTION_TYPE_UPDATE);
// action_id
js.put(GTaskStringUtils.GTASK_JSON_ACTION_ID, actionId);
// id
js.put(GTaskStringUtils.GTASK_JSON_ID, getGid());
// entity_delta
JSONObject entity = new JSONObject();
entity.put(GTaskStringUtils.GTASK_JSON_NAME, getName());
entity.put(GTaskStringUtils.GTASK_JSON_DELETED, getDeleted());
js.put(GTaskStringUtils.GTASK_JSON_ENTITY_DELTA, entity);
} catch (JSONException e) {
Log.e(TAG, e.toString());
e.printStackTrace();
throw new ActionFailureException("fail to generate tasklist-update jsonobject");
}
return js;
}
/**
* 从远程JSON数据加载任务列表内容
* <p>
* 将Google Tasks服务返回的JSON数据解析到当前任务列表对象中
* </p>
* @param js 包含任务列表信息的JSON对象
* @throws ActionFailureException 如果解析JSON对象失败
*/
public void setContentByRemoteJSON(JSONObject js) {
if (js != null) {
try {
// id
if (js.has(GTaskStringUtils.GTASK_JSON_ID)) {
setGid(js.getString(GTaskStringUtils.GTASK_JSON_ID));
}
// last_modified
if (js.has(GTaskStringUtils.GTASK_JSON_LAST_MODIFIED)) {
setLastModified(js.getLong(GTaskStringUtils.GTASK_JSON_LAST_MODIFIED));
}
// name
if (js.has(GTaskStringUtils.GTASK_JSON_NAME)) {
setName(js.getString(GTaskStringUtils.GTASK_JSON_NAME));
}
} catch (JSONException e) {
Log.e(TAG, e.toString());
e.printStackTrace();
throw new ActionFailureException("fail to get tasklist content from jsonobject");
}
}
}
/**
* 从本地JSON数据加载任务列表内容
* <p>
* 将本地数据库中的JSON数据解析到当前任务列表对象中
* </p>
* @param js 包含本地任务列表信息的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) {
String name = folder.getString(NoteColumns.SNIPPET);
setName(GTaskStringUtils.MIUI_FOLDER_PREFFIX + name);
} else if (folder.getInt(NoteColumns.TYPE) == Notes.TYPE_SYSTEM) {
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格式
* <p>
* 生成用于存储到本地数据库的JSON对象
* </p>
* @return 包含任务列表本地信息的JSON对象解析失败时返回null
*/
public JSONObject getLocalJSONFromContent() {
try {
JSONObject js = new JSONObject();
JSONObject folder = new JSONObject();
String folderName = getName();
if (getName().startsWith(GTaskStringUtils.MIUI_FOLDER_PREFFIX))
folderName = folderName.substring(GTaskStringUtils.MIUI_FOLDER_PREFFIX.length(),
folderName.length());
folder.put(NoteColumns.SNIPPET, folderName);
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);
js.put(GTaskStringUtils.META_HEAD_NOTE, folder);
return js;
} catch (JSONException e) {
Log.e(TAG, e.toString());
e.printStackTrace();
return null;
}
}
/**
* 根据本地数据库游标确定同步动作类型
* <p>
* 比较本地和远程数据的状态,决定执行哪种同步操作
* </p>
* @param c 本地数据库游标,包含任务列表的本地状态信息
* @return 同步动作类型,可能的值:
* <ul>
* <li>{@link Node#SYNC_ACTION_NONE} - 无需同步</li>
* <li>{@link Node#SYNC_ACTION_UPDATE_LOCAL} - 更新本地数据</li>
* <li>{@link Node#SYNC_ACTION_UPDATE_REMOTE} - 更新远程数据</li>
* <li>{@link Node#SYNC_ACTION_ERROR} - 同步错误</li>
* </ul>
*/
public int getSyncAction(Cursor c) {
try {
if (c.getInt(SqlNote.LOCAL_MODIFIED_COLUMN) == 0) {
// there is no local update
if (c.getLong(SqlNote.SYNC_ID_COLUMN) == getLastModified()) {
// no update both side
return SYNC_ACTION_NONE;
} else {
// apply remote to local
return SYNC_ACTION_UPDATE_LOCAL;
}
} else {
// validate 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()) {
// local modification only
return SYNC_ACTION_UPDATE_REMOTE;
} else {
// for folder conflicts, just apply local modification
return SYNC_ACTION_UPDATE_REMOTE;
}
}
} catch (Exception e) {
Log.e(TAG, e.toString());
e.printStackTrace();
}
return SYNC_ACTION_ERROR;
}
/**
* 获取子任务数量
* @return 子任务集合的大小
*/
public int getChildTaskCount() {
return mChildren.size();
}
/**
* 添加子任务到任务列表末尾
* <p>
* 设置任务的前一个兄弟任务和父任务列表
* </p>
* @param task 要添加的子任务对象
* @return true if the task was added successfully, false otherwise
*/
public boolean addChildTask(Task task) {
boolean ret = false;
if (task != null && !mChildren.contains(task)) {
ret = mChildren.add(task);
if (ret) {
// need to set prior sibling and parent
task.setPriorSibling(mChildren.isEmpty() ? null : mChildren
.get(mChildren.size() - 1));
task.setParent(this);
}
}
return ret;
}
/**
* 在指定索引位置添加子任务
* <p>
* 更新相关任务的前一个兄弟任务引用
* </p>
* @param task 要添加的子任务对象
* @param index 要添加的位置索引
* @return true if the task was added successfully, false otherwise
*/
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);
// update the task list
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;
}
/**
* 从任务列表中移除指定的子任务
* <p>
* 重置被移除任务的前一个兄弟任务和父任务列表引用
* </p>
* @param task 要移除的子任务对象
* @return true if the task was removed successfully, false otherwise
*/
public boolean removeChildTask(Task task) {
boolean ret = false;
int index = mChildren.indexOf(task);
if (index != -1) {
ret = mChildren.remove(task);
if (ret) {
// reset prior sibling and parent
task.setPriorSibling(null);
task.setParent(null);
// update the task list
if (index != mChildren.size()) {
mChildren.get(index).setPriorSibling(
index == 0 ? null : mChildren.get(index - 1));
}
}
}
return ret;
}
/**
* 移动子任务到新的索引位置
* <p>
* 通过先移除后添加的方式实现移动操作
* </p>
* @param task 要移动的子任务对象
* @param index 新的位置索引
* @return true if the task was moved successfully, false otherwise
*/
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));
}
/**
* 根据Google任务ID查找子任务
* @param gid Google任务ID
* @return 找到的任务对象未找到时返回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;
}
/**
* 获取子任务在列表中的索引位置
* @param task 子任务对象
* @return 子任务的索引位置,未找到时返回-1
*/
public int getChildTaskIndex(Task task) {
return mChildren.indexOf(task);
}
/**
* 根据索引位置获取子任务
* @param index 子任务的索引位置
* @return 对应的任务对象索引无效时返回null
*/
public Task getChildTaskByIndex(int index) {
if (index < 0 || index >= mChildren.size()) {
Log.e(TAG, "getTaskByIndex: invalid index");
return null;
}
return mChildren.get(index);
}
/**
* 根据Google任务ID查找子任务
* <p>
* 与{@link #findChildTaskByGid(String)}功能相同,是该方法的别名
* </p>
* @param gid Google任务ID
* @return 找到的任务对象未找到时返回null
*/
public Task getChilTaskByGid(String gid) {
for (Task task : mChildren) {
if (task.getGid().equals(gid))
return task;
}
return null;
}
/**
* 获取所有子任务的集合
* @return 子任务集合的引用
*/
public ArrayList<Task> getChildTaskList() {
return this.mChildren;
}
/**
* 设置任务列表的索引位置
* @param index 新的索引位置
*/
public void setIndex(int index) {
this.mIndex = index;
}
/**
* 获取任务列表的索引位置
* @return 当前索引位置
*/
public int getIndex() {
return this.mIndex;
}
}