diff --git a/src/Task.java b/src/Task.java new file mode 100644 index 0000000..d51d834 --- /dev/null +++ b/src/Task.java @@ -0,0 +1,334 @@ +/* + * 版权所有 (c) 2010-2011, MiCode 开源社区 (www.micode.net) + * + * 根据 Apache License, Version 2.0 (以下简称“许可协议”) 授权; + * 除非遵守许可协议,否则不得使用此文件。 + * 您可以在以下网址获取许可协议的副本: + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * 除非适用法律要求或书面同意,根据许可协议分发的软件按“原样”提供, + * 不提供任何明示或暗示的担保或条件。 + * 请参阅许可协议了解许可权限及限制。 + */ + +package net.micode.notes.gtask.data; + +import android.database.Cursor; +import android.text.TextUtils; +import android.util.Log; + +import net.micode.notes.data.Notes; +import net.micode.notes.data.Notes.DataColumns; +import net.micode.notes.data.Notes.DataConstants; +import net.micode.notes.data.Notes.NoteColumns; +import net.micode.notes.gtask.exception.ActionFailureException; +import net.micode.notes.tool.GTaskStringUtils; + +import org.json.JSONArray; +import org.json.JSONException; +import org.json.JSONObject; + +// 任务类,继承自节点类 +public class Task extends Node { + private static final String TAG = Task.class.getSimpleName(); // 日志标签 + private boolean mCompleted; // 任务完成标记 + private String mNotes; // 任务的备注 + private JSONObject mMetaInfo; // 任务的元信息 + private Task mPriorSibling; // 前一个兄弟任务 + private TaskList mParent; // 任务的父任务列表 + + // 构造方法,初始化任务属性 + public Task() { + super(); + mCompleted = false; + mNotes = null; + mPriorSibling = null; + mParent = null; + mMetaInfo = null; + } + + // 创建任务的 JSON 对象 + public JSONObject getCreateAction(int actionId) { + JSONObject js = new JSONObject(); + + try { + // 设置动作类型为创建 + js.put(GTaskStringUtils.GTASK_JSON_ACTION_TYPE, + GTaskStringUtils.GTASK_JSON_ACTION_TYPE_CREATE); + + // 设置动作 ID + js.put(GTaskStringUtils.GTASK_JSON_ACTION_ID, actionId); + + // 设置任务的索引 + js.put(GTaskStringUtils.GTASK_JSON_INDEX, mParent.getChildTaskIndex(this)); + + // 设置实体数据 + 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_TASK); + if (getNotes() != null) { + entity.put(GTaskStringUtils.GTASK_JSON_NOTES, getNotes()); + } + js.put(GTaskStringUtils.GTASK_JSON_ENTITY_DELTA, entity); + + // 设置父任务 ID + js.put(GTaskStringUtils.GTASK_JSON_PARENT_ID, mParent.getGid()); + + // 设置父任务类型 + js.put(GTaskStringUtils.GTASK_JSON_DEST_PARENT_TYPE, + GTaskStringUtils.GTASK_JSON_TYPE_GROUP); + + // 设置任务列表 ID + js.put(GTaskStringUtils.GTASK_JSON_LIST_ID, mParent.getGid()); + + // 设置前一个兄弟任务 ID + if (mPriorSibling != null) { + js.put(GTaskStringUtils.GTASK_JSON_PRIOR_SIBLING_ID, mPriorSibling.getGid()); + } + + } catch (JSONException e) { + Log.e(TAG, e.toString()); + e.printStackTrace(); + throw new ActionFailureException("创建任务 JSON 对象失败"); + } + + return js; + } + + // 更新任务的 JSON 对象 + public JSONObject getUpdateAction(int actionId) { + JSONObject js = new JSONObject(); + + try { + js.put(GTaskStringUtils.GTASK_JSON_ACTION_TYPE, + GTaskStringUtils.GTASK_JSON_ACTION_TYPE_UPDATE); + js.put(GTaskStringUtils.GTASK_JSON_ACTION_ID, actionId); + js.put(GTaskStringUtils.GTASK_JSON_ID, getGid()); + + JSONObject entity = new JSONObject(); + entity.put(GTaskStringUtils.GTASK_JSON_NAME, getName()); + if (getNotes() != null) { + entity.put(GTaskStringUtils.GTASK_JSON_NOTES, getNotes()); + } + 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("更新任务 JSON 对象失败"); + } + + return js; + } + + // 从远程 JSON 设置任务内容 + public void setContentByRemoteJSON(JSONObject js) { + if (js != null) { + try { + if (js.has(GTaskStringUtils.GTASK_JSON_ID)) { + setGid(js.getString(GTaskStringUtils.GTASK_JSON_ID)); + } + if (js.has(GTaskStringUtils.GTASK_JSON_LAST_MODIFIED)) { + setLastModified(js.getLong(GTaskStringUtils.GTASK_JSON_LAST_MODIFIED)); + } + if (js.has(GTaskStringUtils.GTASK_JSON_NAME)) { + setName(js.getString(GTaskStringUtils.GTASK_JSON_NAME)); + } + if (js.has(GTaskStringUtils.GTASK_JSON_NOTES)) { + setNotes(js.getString(GTaskStringUtils.GTASK_JSON_NOTES)); + } + if (js.has(GTaskStringUtils.GTASK_JSON_DELETED)) { + setDeleted(js.getBoolean(GTaskStringUtils.GTASK_JSON_DELETED)); + } + if (js.has(GTaskStringUtils.GTASK_JSON_COMPLETED)) { + setCompleted(js.getBoolean(GTaskStringUtils.GTASK_JSON_COMPLETED)); + } + } catch (JSONException e) { + Log.e(TAG, e.toString()); + e.printStackTrace(); + throw new ActionFailureException("从 JSON 获取任务内容失败"); + } + } + } + + // 从本地 JSON 设置任务内容 + public void setContentByLocalJSON(JSONObject js) { + if (js == null || !js.has(GTaskStringUtils.META_HEAD_NOTE) + || !js.has(GTaskStringUtils.META_HEAD_DATA)) { + Log.w(TAG, "setContentByLocalJSON: 数据为空"); + } + + try { + JSONObject note = js.getJSONObject(GTaskStringUtils.META_HEAD_NOTE); + JSONArray dataArray = js.getJSONArray(GTaskStringUtils.META_HEAD_DATA); + + if (note.getInt(NoteColumns.TYPE) != Notes.TYPE_NOTE) { + Log.e(TAG, "无效类型"); + return; + } + + for (int i = 0; i < dataArray.length(); i++) { + JSONObject data = dataArray.getJSONObject(i); + if (TextUtils.equals(data.getString(DataColumns.MIME_TYPE), DataConstants.NOTE)) { + setName(data.getString(DataColumns.CONTENT)); + break; + } + } + + } catch (JSONException e) { + Log.e(TAG, e.toString()); + e.printStackTrace(); + } + } + + // 获取本地 JSON 内容 + public JSONObject getLocalJSONFromContent() { + String name = getName(); + try { + if (mMetaInfo == null) { + if (name == null) { + Log.w(TAG, "笔记为空"); + return null; + } + JSONObject js = new JSONObject(); + JSONObject note = new JSONObject(); + JSONArray dataArray = new JSONArray(); + JSONObject data = new JSONObject(); + data.put(DataColumns.CONTENT, name); + dataArray.put(data); + js.put(GTaskStringUtils.META_HEAD_DATA, dataArray); + note.put(NoteColumns.TYPE, Notes.TYPE_NOTE); + js.put(GTaskStringUtils.META_HEAD_NOTE, note); + return js; + } else { + JSONObject note = mMetaInfo.getJSONObject(GTaskStringUtils.META_HEAD_NOTE); + JSONArray dataArray = mMetaInfo.getJSONArray(GTaskStringUtils.META_HEAD_DATA); + + for (int i = 0; i < dataArray.length(); i++) { + JSONObject data = dataArray.getJSONObject(i); + if (TextUtils.equals(data.getString(DataColumns.MIME_TYPE), DataConstants.NOTE)) { + data.put(DataColumns.CONTENT, getName()); + break; + } + } + + note.put(NoteColumns.TYPE, Notes.TYPE_NOTE); + return mMetaInfo; + } + } catch (JSONException e) { + Log.e(TAG, e.toString()); + e.printStackTrace(); + return null; + } + } + + // 设置元信息 + public void setMetaInfo(MetaData metaData) { + if (metaData != null && metaData.getNotes() != null) { + try { + mMetaInfo = new JSONObject(metaData.getNotes()); + } catch (JSONException e) { + Log.w(TAG, e.toString()); + mMetaInfo = null; + } + } + } + + // 获取同步操作 + public int getSyncAction(Cursor c) { + try { + JSONObject noteInfo = null; + if (mMetaInfo != null && mMetaInfo.has(GTaskStringUtils.META_HEAD_NOTE)) { + noteInfo = mMetaInfo.getJSONObject(GTaskStringUtils.META_HEAD_NOTE); + } + + if (noteInfo == null) { + Log.w(TAG, "笔记元数据已删除"); + return SYNC_ACTION_UPDATE_REMOTE; + } + + if (!noteInfo.has(NoteColumns.ID)) { + Log.w(TAG, "远程笔记 ID 似乎被删除"); + return SYNC_ACTION_UPDATE_LOCAL; + } + + if (c.getLong(SqlNote.ID_COLUMN) != noteInfo.getLong(NoteColumns.ID)) { + Log.w(TAG, "笔记 ID 不匹配"); + return SYNC_ACTION_UPDATE_LOCAL; + } + + if (c.getInt(SqlNote.LOCAL_MODIFIED_COLUMN) == 0) { + if (c.getLong(SqlNote.SYNC_ID_COLUMN) == getLastModified()) { + return SYNC_ACTION_NONE; + } else { + return SYNC_ACTION_UPDATE_LOCAL; + } + } else { + if (!c.getString(SqlNote.GTASK_ID_COLUMN).equals(getGid())) { + Log.e(TAG, "gtask ID 不匹配"); + return SYNC_ACTION_ERROR; + } + if (c.getLong(SqlNote.SYNC_ID_COLUMN) == getLastModified()) { + return SYNC_ACTION_UPDATE_REMOTE; + } else { + return SYNC_ACTION_UPDATE_CONFLICT; + } + } + } catch (Exception e) { + Log.e(TAG, e.toString()); + e.printStackTrace(); + } + + return SYNC_ACTION_ERROR; + } + + // 检查任务是否值得保存 + public boolean isWorthSaving() { + return mMetaInfo != null || (getName() != null && getName().trim().length() > 0) + || (getNotes() != null && getNotes().trim().length() > 0); + } + + // 设置任务完成状态 + public void setCompleted(boolean completed) { + this.mCompleted = completed; + } + + // 设置备注 + public void setNotes(String notes) { + this.mNotes = notes; + } + + // 设置前一个兄弟任务 + public void setPriorSibling(Task priorSibling) { + this.mPriorSibling = priorSibling; + } + + // 设置父任务列表 + public void setParent(TaskList parent) { + this.mParent = parent; + } + + // 获取任务完成状态 + public boolean getCompleted() { + return this.mCompleted; + } + + // 获取备注 + public String getNotes() { + return this.mNotes; + } + + // 获取前一个兄弟任务 + public Task getPriorSibling() { + return this.mPriorSibling; + } + + // 获取父任务列表 + public TaskList getParent() { + return this.mParent; + } +} diff --git a/src/TaskList.java b/src/TaskList.java new file mode 100644 index 0000000..814b349 --- /dev/null +++ b/src/TaskList.java @@ -0,0 +1,351 @@ +/* + * 版权所有 (c) 2010-2011, MiCode 开源社区 (www.micode.net) + * + * 根据 Apache 许可证 2.0 版授权 ("许可证"); + * 只有在符合许可证的情况下,您才能使用此文件。 + * 您可以在以下位置获取许可证副本: + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * 除非适用法律要求或书面同意,按“原样”分发的许可证软件, + * 无任何明示或暗示的担保或条件。 + * 请参阅许可证以获取许可下的具体语言和限制。 + */ + +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; + +/** + * 任务列表类,继承自节点类 + */ +public class TaskList extends Node { + private static final String TAG = TaskList.class.getSimpleName(); // 日志标签 + + private int mIndex; // 索引号 + + private ArrayList mChildren; // 子任务列表 + + // 构造函数 + public TaskList() { + super(); + mChildren = new ArrayList(); + mIndex = 1; + } + + /** + * 创建任务列表的JSON对象 + * @param actionId 动作ID + * @return 创建的JSON对象 + */ + public JSONObject getCreateAction(int actionId) { + JSONObject js = new JSONObject(); + try { + js.put(GTaskStringUtils.GTASK_JSON_ACTION_TYPE, GTaskStringUtils.GTASK_JSON_ACTION_TYPE_CREATE); // 动作类型 + js.put(GTaskStringUtils.GTASK_JSON_ACTION_ID, actionId); // 动作ID + js.put(GTaskStringUtils.GTASK_JSON_INDEX, mIndex); // 索引 + + // 设置实体信息 + JSONObject entity = new JSONObject(); + entity.put(GTaskStringUtils.GTASK_JSON_NAME, getName()); // 名称 + entity.put(GTaskStringUtils.GTASK_JSON_CREATOR_ID, "null"); // 创建者ID + 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("生成任务列表创建JSON对象失败"); + } + return js; + } + + /** + * 更新任务列表的JSON对象 + * @param actionId 动作ID + * @return 更新的JSON对象 + */ + public JSONObject getUpdateAction(int actionId) { + JSONObject js = new JSONObject(); + try { + js.put(GTaskStringUtils.GTASK_JSON_ACTION_TYPE, GTaskStringUtils.GTASK_JSON_ACTION_TYPE_UPDATE); // 动作类型 + js.put(GTaskStringUtils.GTASK_JSON_ACTION_ID, actionId); // 动作ID + js.put(GTaskStringUtils.GTASK_JSON_ID, getGid()); // 任务ID + + // 设置实体信息 + 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("生成任务列表更新JSON对象失败"); + } + return js; + } + + /** + * 从远程JSON设置任务列表内容 + */ + public void setContentByRemoteJSON(JSONObject js) { + if (js != null) { + try { + if (js.has(GTaskStringUtils.GTASK_JSON_ID)) { + setGid(js.getString(GTaskStringUtils.GTASK_JSON_ID)); // 设置ID + } + if (js.has(GTaskStringUtils.GTASK_JSON_LAST_MODIFIED)) { + setLastModified(js.getLong(GTaskStringUtils.GTASK_JSON_LAST_MODIFIED)); // 设置最后修改时间 + } + 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("从JSON对象获取任务列表内容失败"); + } + } + } + + /** + * 从本地JSON设置任务列表内容 + */ + public void setContentByLocalJSON(JSONObject js) { + if (js == null || !js.has(GTaskStringUtils.META_HEAD_NOTE)) { + Log.w(TAG, "setContentByLocalJSON: 无可用内容"); + return; + } + + 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, "无效的系统文件夹"); + } else { + Log.e(TAG, "错误的类型"); + } + } catch (JSONException e) { + Log.e(TAG, e.toString()); + e.printStackTrace(); + } + } + + /** + * 获取任务列表内容并转换为本地JSON对象 + */ + 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()); + } + + folder.put(NoteColumns.SNIPPET, folderName); + folder.put(NoteColumns.TYPE, folderName.equals(GTaskStringUtils.FOLDER_DEFAULT) + || folderName.equals(GTaskStringUtils.FOLDER_CALL_NOTE) + ? Notes.TYPE_SYSTEM : Notes.TYPE_FOLDER); + + js.put(GTaskStringUtils.META_HEAD_NOTE, folder); + + return js; + } catch (JSONException e) { + Log.e(TAG, e.toString()); + e.printStackTrace(); + return null; + } + } + + /** + * 根据游标信息获取同步操作类型 + */ + public int getSyncAction(Cursor c) { + try { + if (c.getInt(SqlNote.LOCAL_MODIFIED_COLUMN) == 0) { + // 没有本地更新 + return c.getLong(SqlNote.SYNC_ID_COLUMN) == getLastModified() ? SYNC_ACTION_NONE : SYNC_ACTION_UPDATE_LOCAL; + } else { + // 验证任务ID + if (!c.getString(SqlNote.GTASK_ID_COLUMN).equals(getGid())) { + Log.e(TAG, "任务ID不匹配"); + return SYNC_ACTION_ERROR; + } + return c.getLong(SqlNote.SYNC_ID_COLUMN) == getLastModified() ? SYNC_ACTION_UPDATE_REMOTE : SYNC_ACTION_UPDATE_REMOTE; + } + } catch (Exception e) { + Log.e(TAG, e.toString()); + e.printStackTrace(); + } + return SYNC_ACTION_ERROR; + } + + /** + * 获取子任务数量 + */ + public int getChildTaskCount() { + return mChildren.size(); + } + + /** + * 添加子任务 + */ + public boolean addChildTask(Task task) { + if (task != null && !mChildren.contains(task)) { + boolean ret = mChildren.add(task); + if (ret) { + task.setPriorSibling(mChildren.isEmpty() ? null : mChildren.get(mChildren.size() - 1)); + task.setParent(this); + } + return ret; + } + return false; + } + + /** + * 添加子任务到指定位置 + */ + public boolean addChildTask(Task task, int index) { + if (index < 0 || index > mChildren.size()) { + Log.e(TAG, "添加子任务:无效索引"); + return false; + } + if (task != null && mChildren.indexOf(task) == -1) { + mChildren.add(index, task); + + // 更新子任务链 + Task preTask = index != 0 ? mChildren.get(index - 1) : null; + Task afterTask = index != mChildren.size() - 1 ? mChildren.get(index + 1) : null; + + task.setPriorSibling(preTask); + if (afterTask != null) { + afterTask.setPriorSibling(task); + } + } + return true; + } + + /** + * 从任务列表中删除子任务 + */ + public boolean removeChildTask(Task task) { + int index = mChildren.indexOf(task); + if (index != -1) { + boolean ret = mChildren.remove(task); + if (ret) { + task.setPriorSibling(null); + task.setParent(null); + + // 更新任务链 + if (index != mChildren.size()) { + mChildren.get(index).setPriorSibling(index == 0 ? null : mChildren.get(index - 1)); + } + } + return ret; + } + return false; + } + + /** + * 移动子任务到指定位置 + */ + public boolean moveChildTask(Task task, int index) { + if (index < 0 || index >= mChildren.size()) { + Log.e(TAG, "移动子任务:无效索引"); + return false; + } + int pos = mChildren.indexOf(task); + if (pos == -1) { + Log.e(TAG, "移动子任务:任务应在列表中"); + return false; + } + return pos == index || (removeChildTask(task) && addChildTask(task, index)); + } + + /** + * 通过GID查找子任务 + */ + public Task findChildTaskByGid(String gid) { + for (Task t : mChildren) { + if (t.getGid().equals(gid)) { + return t; + } + } + return null; + } + + /** + * 获取子任务的索引 + */ + public int getChildTaskIndex(Task task) { + return mChildren.indexOf(task); + } + + /** + * 通过索引获取子任务 + */ + public Task getChildTaskByIndex(int index) { + if (index < 0 || index >= mChildren.size()) { + Log.e(TAG, "getTaskByIndex: 无效索引"); + return null; + } + return mChildren.get(index); + } + + /** + * 通过GID获取子任务 + */ + public Task getChilTaskByGid(String gid) { + for (Task task : mChildren) { + if (task.getGid().equals(gid)) { + return task; + } + } + return null; + } + + /** + * 获取子任务列表 + */ + public ArrayList getChildTaskList() { + return this.mChildren; + } + + /** + * 设置索引 + */ + public void setIndex(int index) { + this.mIndex = index; + } + + /** + * 获取索引 + */ + public int getIndex() { + return this.mIndex; + } +}