From 6285afbd7f35f53f5ab5da2cac973db1d8c9c9f1 Mon Sep 17 00:00:00 2001 From: 29630 <2963071932qq.com> Date: Wed, 20 Nov 2024 18:20:04 +0800 Subject: [PATCH] =?UTF-8?q?=E4=BB=A3=E7=A0=81=E8=A7=A3=E9=87=8A?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- ActionFailureException.java | 0 GTaskASyncTask.java | 0 MetaData.java | 129 ++++++++++ NetworkFailureException.java | 0 Node.java | 126 ++++++++++ NotesDatabaseHelper.java | 273 +++++++++++++++++++++ NotesProvider.java | 273 +++++++++++++++++++++ SqlData.java | 192 +++++++++++++++ SqlNote.java | 94 ++++++++ Task.java | 392 ++++++++++++++++++++++++++++++ TaskList.java | 449 +++++++++++++++++++++++++++++++++++ 11 files changed, 1928 insertions(+) create mode 100644 ActionFailureException.java create mode 100644 GTaskASyncTask.java create mode 100644 MetaData.java create mode 100644 NetworkFailureException.java create mode 100644 Node.java create mode 100644 NotesDatabaseHelper.java create mode 100644 NotesProvider.java create mode 100644 SqlData.java create mode 100644 SqlNote.java create mode 100644 Task.java create mode 100644 TaskList.java diff --git a/ActionFailureException.java b/ActionFailureException.java new file mode 100644 index 0000000..e69de29 diff --git a/GTaskASyncTask.java b/GTaskASyncTask.java new file mode 100644 index 0000000..e69de29 diff --git a/MetaData.java b/MetaData.java new file mode 100644 index 0000000..7b64f80 --- /dev/null +++ b/MetaData.java @@ -0,0 +1,129 @@ +/* + * 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. + */ + +// MetaData 类继承自 Task,用于处理任务的元数据信息 +package net.micode.notes.gtask.data; + +import android.database.Cursor; +import android.util.Log; + +import net.micode.notes.tool.GTaskStringUtils; + +import org.json.JSONException; +import org.json.JSONObject; + +public class MetaData extends Task { + // 日志标签,用于日志记录 + private final static String TAG = MetaData.class.getSimpleName(); + + // 存储相关的 Google 任务 ID + private String mRelatedGid = null; + + /** + * 设置元数据信息 + * + * @param gid 相关的 Google 任务 ID + * @param metaInfo 元数据信息,以 JSON 格式表示 + */ + public void setMeta(String gid, JSONObject metaInfo) { + try { + // 将 Google 任务 ID 添加到元数据中 + metaInfo.put(GTaskStringUtils.META_HEAD_GTASK_ID, gid); + } catch (JSONException e) { + // 如果添加失败,记录错误日志 + Log.e(TAG, "failed to put related gid"); + } + // 将元数据转换为字符串并设置为笔记 + setNotes(metaInfo.toString()); + // 设置元数据的名称 + setName(GTaskStringUtils.META_NOTE_NAME); + } + + /** + * 获取相关的 Google 任务 ID + * + * @return 返回 Google 任务 ID + */ + public String getRelatedGid() { + return mRelatedGid; + } + + /** + * 判断元数据是否值得保存 + * + * @return 如果笔记不为空,则返回 true,否则返回 false + */ + @Override + public boolean isWorthSaving() { + return getNotes() != null; + } + + /** + * 从远程 JSON 设置内容 + * + * @param js 远程 JSON 对象 + */ + @Override + public void setContentByRemoteJSON(JSONObject js) { + super.setContentByRemoteJSON(js); + if (getNotes() != null) { + try { + // 解析笔记以获取 Google 任务 ID + JSONObject metaInfo = new JSONObject(getNotes().trim()); + mRelatedGid = metaInfo.getString(GTaskStringUtils.META_HEAD_GTASK_ID); + } catch (JSONException e) { + // 如果解析失败,记录警告日志 + Log.w(TAG, "failed to get related gid"); + mRelatedGid = null; + } + } + } + + /** + * 从本地 JSON 设置内容 + * 此方法不应被调用 + * + * @param js 本地 JSON 对象 + */ + @Override + public void setContentByLocalJSON(JSONObject js) { + // 此方法不应被调用 + throw new IllegalAccessError("MetaData:setContentByLocalJSON should not be called"); + } + + /** + * 从内容中获取本地 JSON + * 此方法不应被调用 + * + * @return 永远不会返回,将抛出异常 + */ + @Override + public JSONObject getLocalJSONFromContent() { + throw new IllegalAccessError("MetaData:getLocalJSONFromContent should not be called"); + } + + /** + * 基于游标获取同步操作 + * 此方法不应被调用 + * + * @param c 游标 + * @return 永远不会返回,将抛出异常 + */ + @Override + public int getSyncAction(Cursor c) { + throw new IllegalAccessError("MetaData:getSyncAction should not be called"); + } +} diff --git a/NetworkFailureException.java b/NetworkFailureException.java new file mode 100644 index 0000000..e69de29 diff --git a/Node.java b/Node.java new file mode 100644 index 0000000..79259a3 --- /dev/null +++ b/Node.java @@ -0,0 +1,126 @@ +/* + * 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. + */ + +/** + * 抽象类Node代表一个基本的节点对象,提供了基本的属性和方法用于操作和同步节点数据 + * 主要用于笔记或任务的同步处理 + */ +package net.micode.notes.gtask.data; + +import android.database.Cursor; +import org.json.JSONObject; + +public abstract class Node { + // 定义同步操作类型常量 + public static final int SYNC_ACTION_NONE = 0; + public static final int SYNC_ACTION_ADD_REMOTE = 1; + public static final int SYNC_ACTION_ADD_LOCAL = 2; + public static final int SYNC_ACTION_DEL_REMOTE = 3; + public static final int SYNC_ACTION_DEL_LOCAL = 4; + public static final int SYNC_ACTION_UPDATE_REMOTE = 5; + public static final int SYNC_ACTION_UPDATE_LOCAL = 6; + public static final int SYNC_ACTION_UPDATE_CONFLICT = 7; + public static final int SYNC_ACTION_ERROR = 8; + + // 节点的基本属性 + private String mGid; + private String mName; + private long mLastModified; + private boolean mDeleted; + + /** + * 默认构造方法,初始化节点的基本属性 + */ + public Node() { + mGid = null; + mName = ""; + mLastModified = 0; + mDeleted = false; + } + + /** + * 根据操作ID生成创建操作的JSON对象 + * @param actionId 操作ID,表示需要执行的同步操作类型 + * @return 返回一个包含创建操作信息的JSON对象 + */ + public abstract JSONObject getCreateAction(int actionId); + + /** + * 根据操作ID生成更新操作的JSON对象 + * @param actionId 操作ID,表示需要执行的同步操作类型 + * @return 返回一个包含更新操作信息的JSON对象 + */ + public abstract JSONObject getUpdateAction(int actionId); + + /** + * 通过远程JSON对象设置节点内容 + * @param js 远程JSON对象,包含节点的最新数据 + */ + public abstract void setContentByRemoteJSON(JSONObject js); + + /** + * 通过本地JSON对象设置节点内容 + * @param js 本地JSON对象,包含节点的现有数据 + */ + public abstract void setContentByLocalJSON(JSONObject js); + + /** + * 从节点内容生成本地JSON对象 + * @return 返回一个包含节点当前状态的JSON对象 + */ + public abstract JSONObject getLocalJSONFromContent(); + + /** + * 根据数据库游标确定同步操作类型 + * @param c 数据库游标,指向查询到的节点数据 + * @return 返回一个整数,表示需要执行的同步操作类型 + */ + public abstract int getSyncAction(Cursor c); + + // 以下为节点属性的getter和setter方法 + public void setGid(String gid) { + this.mGid = gid; + } + + public void setName(String name) { + this.mName = name; + } + + public void setLastModified(long lastModified) { + this.mLastModified = lastModified; + } + + public void setDeleted(boolean deleted) { + this.mDeleted = deleted; + } + + public String getGid() { + return this.mGid; + } + + public String getName() { + return this.mName; + } + + public long getLastModified() { + return this.mLastModified; + } + + public boolean getDeleted() { + return this.mDeleted; + } + +} diff --git a/NotesDatabaseHelper.java b/NotesDatabaseHelper.java new file mode 100644 index 0000000..dc77b6b --- /dev/null +++ b/NotesDatabaseHelper.java @@ -0,0 +1,273 @@ + +// 创建笔记表的SQL语句 +private static final String CREATE_NOTE_TABLE_SQL = "CREATE TABLE " + TABLE.NOTE + " (" + + NoteColumns.ID + " TEXT PRIMARY KEY, " + + NoteColumns.TITLE + " TEXT NOT NULL, " + + NoteColumns.CONTENT + " TEXT NOT NULL, " + + NoteColumns.CREATED_DATE + " INTEGER NOT NULL, " + + NoteColumns.MODIFIED_DATE + " INTEGER NOT NULL, " + + NoteColumns.TYPE + " INTEGER NOT NULL, " + + NoteColumns.PARENT_ID + " TEXT NOT NULL, " + + NoteColumns.GTASK_ID + " TEXT NOT NULL DEFAULT '', " + + NoteColumns.VERSION + " INTEGER NOT NULL DEFAULT 0" + + ");"; + +// 创建与笔记表相关的触发器的SQL语句 +private static final String NOTE_INCREASE_FOLDER_COUNT_ON_UPDATE_TRIGGER = "CREATE TRIGGER increase_folder_count_on_update AFTER UPDATE ON " + TABLE.NOTE + + " BEGIN" + + " UPDATE " + TABLE.NOTE + " SET " + NoteColumns.COUNT + " = " + NoteColumns.COUNT + " + 1" + + " WHERE " + NoteColumns.ID + " = new." + NoteColumns.PARENT_ID + ";" + + " END;"; +// 类似的触发器SQL语句省略... + +// 创建数据表的SQL语句 +private static final String CREATE_DATA_TABLE_SQL = "CREATE TABLE " + TABLE.DATA + " (" + + DataColumns.ID + " INTEGER PRIMARY KEY AUTOINCREMENT, " + + DataColumns.Note_ID + " TEXT NOT NULL, " + + DataColumns.TYPE + " INTEGER NOT NULL, " + + DataColumns.CONTENT + " TEXT NOT NULL, " + + DataColumns.CREATED_DATE + " INTEGER NOT NULL" + + ");"; + +// 创建与数据表相关的触发器的SQL语句 +private static final String DATA_UPDATE_NOTE_CONTENT_ON_INSERT_TRIGGER = "CREATE TRIGGER update_note_content_on_insert AFTER INSERT ON " + TABLE.DATA + + " BEGIN" + + " UPDATE " + TABLE.NOTE + " SET " + NoteColumns.CONTENT + " = new." + DataColumns.CONTENT + + " WHERE " + NoteColumns.ID + " = new." + DataColumns.Note_ID + ";" + + " END;"; +// 类似的触发器SQL语句省略... + +// 在数据表上创建索引的SQL语句 +private static final String CREATE_DATA_NOTE_ID_INDEX_SQL = "CREATE INDEX " + TABLE.DATA + "_note_id_index ON " + + TABLE.DATA + " (" + DataColumns.Note_ID + ");"; + +// 删除触发器的SQL语句 +private static final String DROP_TRIGGER_SQL = "DROP TRIGGER IF EXISTS %s;"; + +// 插入系统文件夹的SQL语句 +private static final String INSERT_SYSTEM_FOLDER_SQL = "INSERT INTO " + TABLE.NOTE + " (" + + NoteColumns.ID + ", " + + NoteColumns.TYPE + ", " + + NoteColumns.TITLE + ", " + + NoteColumns.CONTENT + ", " + + NoteColumns.CREATED_DATE + ", " + + NoteColumns.MODIFIED_DATE + ", " + + NoteColumns.PARENT_ID + ")" + + " VALUES (?, ?, ?, ?, ?, ?, ?);"; + +// 更新笔记表的SQL语句 +private static final String UPDATE_NOTE_TABLE_SQL = "UPDATE " + TABLE.NOTE + " SET " + + NoteColumns.TITLE + " = ?, " + + NoteColumns.CONTENT + " = ?, " + + NoteColumns.MODIFIED_DATE + " = ?, " + + NoteColumns.PARENT_ID + " = ?" + + " WHERE " + NoteColumns.ID + " = ?;"; + +// 删除笔记的SQL语句 +private static final String DELETE_NOTE_SQL = "DELETE FROM " + TABLE.NOTE + + " WHERE " + NoteColumns.ID + " = ?;"; + +// 查询笔记的SQL语句 +private static final String QUERY_NOTES_SQL = "SELECT * FROM " + TABLE.NOTE + + " WHERE " + NoteColumns.PARENT_ID + " = ?" + + " ORDER BY " + NoteColumns.MODIFIED_DATE + " DESC;"; + +// 按ID查询笔记的SQL语句 +private static final String QUERY_NOTE_BY_ID_SQL = "SELECT * FROM " + TABLE.NOTE + + " WHERE " + NoteColumns.ID + " = ?;"; + +// 查询笔记的最大版本的SQL语句 +private static final String QUERY_MAX_VERSION_SQL = "SELECT MAX(" + NoteColumns.VERSION + ") FROM " + TABLE.NOTE + + " WHERE " + NoteColumns.ID + " = ?;"; + +// 更新笔记版本的SQL语句 +private static final String UPDATE_NOTE_VERSION_SQL = "UPDATE " + TABLE.NOTE + " SET " + + NoteColumns.VERSION + " = ?" + + " WHERE " + NoteColumns.ID + " = ?;"; + +// 删除旧笔记的SQL语句 +private static final String DELETE_OLD_NOTES_SQL = "DELETE FROM " + TABLE.NOTE + + " WHERE " + NoteColumns.VERSION + " < ?;"; + +// 创建新版本笔记的SQL语句 +private static final String CREATE_NEW_VERSION_SQL = "INSERT INTO " + TABLE.NOTE + " (" + + NoteColumns.ID + ", " + + NoteColumns.TITLE + ", " + + NoteColumns.CONTENT + ", " + + NoteColumns.CREATED_DATE + ", " + + NoteColumns.MODIFIED_DATE + ", " + + NoteColumns.TYPE + ", " + + NoteColumns.PARENT_ID + ", " + + NoteColumns.GTASK_ID + ", " + + NoteColumns.VERSION + ")" + + " VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?);"; + +// 按类型查询笔记的SQL语句 +private static final String QUERY_NOTES_BY_TYPE_SQL = "SELECT * FROM " + TABLE.NOTE + + " WHERE " + NoteColumns.TYPE + " = ?" + + " ORDER BY " + NoteColumns.MODIFIED_DATE + " DESC;"; + +// 按标题查询笔记的SQL语句 +private static final String QUERY_NOTES_BY_TITLE_SQL = "SELECT * FROM " + TABLE.NOTE + + " WHERE " + NoteColumns.TITLE + " LIKE ?" + + " ORDER BY " + NoteColumns.MODIFIED_DATE + " DESC;"; + +// 按内容查询笔记的SQL语句 +private static final String QUERY_NOTES_BY_CONTENT_SQL = "SELECT * FROM " + TABLE.NOTE + + " WHERE " + NoteColumns.CONTENT + " LIKE ?" + + " ORDER BY " + NoteColumns.MODIFIED_DATE + " DESC;"; + +// 按日期范围查询笔记的SQL语句 +private static final String QUERY_NOTES_BY_DATE_SQL = "SELECT * FROM " + TABLE.NOTE + + " WHERE " + NoteColumns.CREATED_DATE + " BETWEEN ? AND ?" + + " ORDER BY " + NoteColumns.MODIFIED_DATE + " DESC;"; + +// 按GTASK ID查询笔记的SQL语句 +private static final String QUERY_NOTES_BY_GTASK_ID_SQL = "SELECT * FROM " + TABLE.NOTE + + " WHERE " + NoteColumns.GTASK_ID + " = ?;"; + +// 更新笔记的GTASK ID的SQL语句 +private static final String UPDATE_NOTE_GTASK_ID_SQL = "UPDATE " + TABLE.NOTE + " SET " + + NoteColumns.GTASK_ID + " = ?" + + " WHERE " + NoteColumns.ID + " = ?;"; + +// 查询回收站中的笔记的SQL语句 +private static final String QUERY_TRASH_NOTES_SQL = "SELECT * FROM " + TABLE.NOTE + + " WHERE " + NoteColumns.PARENT_ID + " = " + Notes.ID_TRASH_FOLER + + " ORDER BY " + NoteColumns.MODIFIED_DATE + " DESC;"; + +// 从回收站恢复笔记的SQL语句 +private static final String RESTORE_NOTE_FROM_TRASH_SQL = "UPDATE " + TABLE.NOTE + " SET " + + NoteColumns.PARENT_ID + " = ?" + + " WHERE " + NoteColumns.ID + " = ?;"; + +// 永久删除笔记的SQL语句 +private static final String PERMANENTLY_DELETE_NOTE_SQL = "DELETE FROM " + TABLE.NOTE + + " WHERE " + NoteColumns.ID + " = ?;"; + +// 查询文件夹中笔记数量的SQL语句 +private static final String QUERY_FOLDER_COUNT_SQL = "SELECT COUNT(*) FROM " + TABLE.NOTE + + " WHERE " + NoteColumns.PARENT_ID + " = ?;"; + +// 更新文件夹中笔记数量的SQL语句 +private static final String UPDATE_FOLDER_COUNT_SQL = "UPDATE " + TABLE.NOTE + " SET " + + NoteColumns.COUNT + " = ?" + + " WHERE " + NoteColumns.ID + " = ?;"; + +// 查询笔记表中的最大ID的SQL语句 +private static final String QUERY_MAX_ID_SQL = "SELECT MAX(" + NoteColumns.ID + ") FROM " + TABLE.NOTE; + +// 插入新笔记的SQL语句 +private static final String INSERT_NOTE_SQL = "INSERT INTO " + TABLE.NOTE + " (" + + NoteColumns.ID + ", " + + NoteColumns.TITLE + ", " + + NoteColumns.CONTENT + ", " + + NoteColumns.CREATED_DATE + ", " + + NoteColumns.MODIFIED_DATE + ", " + + NoteColumns.TYPE + ", " + + NoteColumns.PARENT_ID + ")" + + " VALUES (?, ?, ?, ?, ?, ?, ?);"; + +// 更新笔记标题的SQL语句 +private static final String UPDATE_NOTE_TITLE_SQL = "UPDATE " + TABLE.NOTE + " SET " + + NoteColumns.TITLE + " = ?" + + " WHERE " + NoteColumns.ID + " = ?;"; + +// 更新笔记内容的SQL语句 +private static final String UPDATE_NOTE_CONTENT_SQL = "UPDATE " + TABLE.NOTE + " SET " + + NoteColumns.CONTENT + " = ?" + + " WHERE " + NoteColumns.ID + " = ?;"; + +// 更新笔记修改日期的SQL语句 +private static final String UPDATE_NOTE_MODIFIED_DATE_SQL = "UPDATE " + TABLE.NOTE + " SET " + + NoteColumns.MODIFIED_DATE + " = ?" + + " WHERE " + NoteColumns.ID + " = ?;"; + +// 更新笔记父ID的SQL语句 +private static final String UPDATE_NOTE_PARENT_ID_SQL = "UPDATE " + TABLE.NOTE + " SET " + + NoteColumns.PARENT_ID + " = ?" + + " WHERE " + NoteColumns.ID + " = ?;"; + +// 删除文件夹中的笔记的SQL语句 +private static final String DELETE_NOTES_IN_FOLDER_SQL = "DELETE FROM " + TABLE.NOTE + + " WHERE " + NoteColumns.PARENT_ID + " = ?;"; + +// 按父ID查询笔记的SQL语句 +private static final String QUERY_NOTES_BY_PARENT_ID_SQL = "SELECT * FROM " + TABLE.NOTE + + " WHERE " + NoteColumns.PARENT_ID + " = ?;"; + +// 更新笔记类型的SQL语句 +private static final String UPDATE_NOTE_TYPE_SQL = "UPDATE " + TABLE.NOTE + " SET " + + NoteColumns.TYPE + " = ?" + + " WHERE " + NoteColumns.ID + " = ?;"; + +// 按类型和父ID查询笔记的SQL语句 +private static final String QUERY_NOTES_BY_TYPE_AND_PARENT_ID_SQL = "SELECT * FROM " + TABLE.NOTE + + " WHERE " + NoteColumns.TYPE + " = ? AND " + + NoteColumns.PARENT_ID + " = ?;" + + " ORDER BY " + NoteColumns.MODIFIED_DATE + " DESC;"; + +/** + * 构造函数,初始化数据库帮助器。 + * + * @param context 上下文对象 + */ +public NotesDatabaseHelper(Context context) { + super(context, DB_NAME, null, DB_VERSION); +} + +/** + * 创建笔记表。 + * + * @param db 数据库对象 + */ +public void createNoteTable(SQLiteDatabase db) { + db.execSQL(CREATE_NOTE_TABLE_SQL); + reCreateNoteTableTriggers(db); + createSystemFolder(db); + Log.d(TAG, "note table has been created"); +} + +/** + * 重新创建笔记表的触发器。 + * + * @param db 数据库对象 + */ +private void reCreateNoteTableTriggers(SQLiteDatabase db) { + db.execSQL("DROP TRIGGER IF EXISTS increase_folder_count_on_update"); + db.execSQL("DROP TRIGGER IF EXISTS decrease_folder_count_on_update"); + db.execSQL("DROP TRIGGER IF EXISTS decrease_folder_count_on_delete"); + db.execSQL("DROP TRIGGER IF EXISTS delete_data_on_delete"); + db.execSQL("DROP TRIGGER IF EXISTS increase_folder_count_on_insert"); + db.execSQL("DROP TRIGGER IF EXISTS folder_delete_notes_on_delete"); + db.execSQL("DROP TRIGGER IF EXISTS folder_move_notes_on_trash"); + + db.execSQL(NOTE_INCREASE_FOLDER_COUNT_ON_UPDATE_TRIGGER); + db.execSQL(NOTE_DECREASE_FOLDER_COUNT_ON_UPDATE_TRIGGER); + db.execSQL(NOTE_DECREASE_FOLDER_COUNT_ON_DELETE_TRIGGER); + db.execSQL(NOTE_DELETE_DATA_ON_DELETE_TRIGGER); + db.execSQL(NOTE_INCREASE_FOLDER_COUNT_ON_INSERT_TRIGGER); + db.execSQL(FOLDER_DELETE_NOTES_ON_DELETE_TRIGGER); + db.execSQL(FOLDER_MOVE_NOTES_ON_TRASH_TRIGGER); +} + +/** + * 创建系统文件夹。 + * + * @param db 数据库对象 + */ +private void createSystemFolder(SQLiteDatabase db) { + ContentValues values = new ContentValues(); + + /** + * 通话记录文件夹,用于存储通话笔记 + */ + values.put(NoteColumns.ID, Notes.ID_CALL_RECORD_FOLDER); + values.put(NoteColumns.TYPE, Notes.TYPE_SYSTEM); + db.insert(TABLE.NOTE, null, values); + + /** + * 根文件夹,作为默认文件夹 + */ + values.clear(); + values.put(NoteColumns.ID, Notes.ID \ No newline at end of file diff --git a/NotesProvider.java b/NotesProvider.java new file mode 100644 index 0000000..dc77b6b --- /dev/null +++ b/NotesProvider.java @@ -0,0 +1,273 @@ + +// 创建笔记表的SQL语句 +private static final String CREATE_NOTE_TABLE_SQL = "CREATE TABLE " + TABLE.NOTE + " (" + + NoteColumns.ID + " TEXT PRIMARY KEY, " + + NoteColumns.TITLE + " TEXT NOT NULL, " + + NoteColumns.CONTENT + " TEXT NOT NULL, " + + NoteColumns.CREATED_DATE + " INTEGER NOT NULL, " + + NoteColumns.MODIFIED_DATE + " INTEGER NOT NULL, " + + NoteColumns.TYPE + " INTEGER NOT NULL, " + + NoteColumns.PARENT_ID + " TEXT NOT NULL, " + + NoteColumns.GTASK_ID + " TEXT NOT NULL DEFAULT '', " + + NoteColumns.VERSION + " INTEGER NOT NULL DEFAULT 0" + + ");"; + +// 创建与笔记表相关的触发器的SQL语句 +private static final String NOTE_INCREASE_FOLDER_COUNT_ON_UPDATE_TRIGGER = "CREATE TRIGGER increase_folder_count_on_update AFTER UPDATE ON " + TABLE.NOTE + + " BEGIN" + + " UPDATE " + TABLE.NOTE + " SET " + NoteColumns.COUNT + " = " + NoteColumns.COUNT + " + 1" + + " WHERE " + NoteColumns.ID + " = new." + NoteColumns.PARENT_ID + ";" + + " END;"; +// 类似的触发器SQL语句省略... + +// 创建数据表的SQL语句 +private static final String CREATE_DATA_TABLE_SQL = "CREATE TABLE " + TABLE.DATA + " (" + + DataColumns.ID + " INTEGER PRIMARY KEY AUTOINCREMENT, " + + DataColumns.Note_ID + " TEXT NOT NULL, " + + DataColumns.TYPE + " INTEGER NOT NULL, " + + DataColumns.CONTENT + " TEXT NOT NULL, " + + DataColumns.CREATED_DATE + " INTEGER NOT NULL" + + ");"; + +// 创建与数据表相关的触发器的SQL语句 +private static final String DATA_UPDATE_NOTE_CONTENT_ON_INSERT_TRIGGER = "CREATE TRIGGER update_note_content_on_insert AFTER INSERT ON " + TABLE.DATA + + " BEGIN" + + " UPDATE " + TABLE.NOTE + " SET " + NoteColumns.CONTENT + " = new." + DataColumns.CONTENT + + " WHERE " + NoteColumns.ID + " = new." + DataColumns.Note_ID + ";" + + " END;"; +// 类似的触发器SQL语句省略... + +// 在数据表上创建索引的SQL语句 +private static final String CREATE_DATA_NOTE_ID_INDEX_SQL = "CREATE INDEX " + TABLE.DATA + "_note_id_index ON " + + TABLE.DATA + " (" + DataColumns.Note_ID + ");"; + +// 删除触发器的SQL语句 +private static final String DROP_TRIGGER_SQL = "DROP TRIGGER IF EXISTS %s;"; + +// 插入系统文件夹的SQL语句 +private static final String INSERT_SYSTEM_FOLDER_SQL = "INSERT INTO " + TABLE.NOTE + " (" + + NoteColumns.ID + ", " + + NoteColumns.TYPE + ", " + + NoteColumns.TITLE + ", " + + NoteColumns.CONTENT + ", " + + NoteColumns.CREATED_DATE + ", " + + NoteColumns.MODIFIED_DATE + ", " + + NoteColumns.PARENT_ID + ")" + + " VALUES (?, ?, ?, ?, ?, ?, ?);"; + +// 更新笔记表的SQL语句 +private static final String UPDATE_NOTE_TABLE_SQL = "UPDATE " + TABLE.NOTE + " SET " + + NoteColumns.TITLE + " = ?, " + + NoteColumns.CONTENT + " = ?, " + + NoteColumns.MODIFIED_DATE + " = ?, " + + NoteColumns.PARENT_ID + " = ?" + + " WHERE " + NoteColumns.ID + " = ?;"; + +// 删除笔记的SQL语句 +private static final String DELETE_NOTE_SQL = "DELETE FROM " + TABLE.NOTE + + " WHERE " + NoteColumns.ID + " = ?;"; + +// 查询笔记的SQL语句 +private static final String QUERY_NOTES_SQL = "SELECT * FROM " + TABLE.NOTE + + " WHERE " + NoteColumns.PARENT_ID + " = ?" + + " ORDER BY " + NoteColumns.MODIFIED_DATE + " DESC;"; + +// 按ID查询笔记的SQL语句 +private static final String QUERY_NOTE_BY_ID_SQL = "SELECT * FROM " + TABLE.NOTE + + " WHERE " + NoteColumns.ID + " = ?;"; + +// 查询笔记的最大版本的SQL语句 +private static final String QUERY_MAX_VERSION_SQL = "SELECT MAX(" + NoteColumns.VERSION + ") FROM " + TABLE.NOTE + + " WHERE " + NoteColumns.ID + " = ?;"; + +// 更新笔记版本的SQL语句 +private static final String UPDATE_NOTE_VERSION_SQL = "UPDATE " + TABLE.NOTE + " SET " + + NoteColumns.VERSION + " = ?" + + " WHERE " + NoteColumns.ID + " = ?;"; + +// 删除旧笔记的SQL语句 +private static final String DELETE_OLD_NOTES_SQL = "DELETE FROM " + TABLE.NOTE + + " WHERE " + NoteColumns.VERSION + " < ?;"; + +// 创建新版本笔记的SQL语句 +private static final String CREATE_NEW_VERSION_SQL = "INSERT INTO " + TABLE.NOTE + " (" + + NoteColumns.ID + ", " + + NoteColumns.TITLE + ", " + + NoteColumns.CONTENT + ", " + + NoteColumns.CREATED_DATE + ", " + + NoteColumns.MODIFIED_DATE + ", " + + NoteColumns.TYPE + ", " + + NoteColumns.PARENT_ID + ", " + + NoteColumns.GTASK_ID + ", " + + NoteColumns.VERSION + ")" + + " VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?);"; + +// 按类型查询笔记的SQL语句 +private static final String QUERY_NOTES_BY_TYPE_SQL = "SELECT * FROM " + TABLE.NOTE + + " WHERE " + NoteColumns.TYPE + " = ?" + + " ORDER BY " + NoteColumns.MODIFIED_DATE + " DESC;"; + +// 按标题查询笔记的SQL语句 +private static final String QUERY_NOTES_BY_TITLE_SQL = "SELECT * FROM " + TABLE.NOTE + + " WHERE " + NoteColumns.TITLE + " LIKE ?" + + " ORDER BY " + NoteColumns.MODIFIED_DATE + " DESC;"; + +// 按内容查询笔记的SQL语句 +private static final String QUERY_NOTES_BY_CONTENT_SQL = "SELECT * FROM " + TABLE.NOTE + + " WHERE " + NoteColumns.CONTENT + " LIKE ?" + + " ORDER BY " + NoteColumns.MODIFIED_DATE + " DESC;"; + +// 按日期范围查询笔记的SQL语句 +private static final String QUERY_NOTES_BY_DATE_SQL = "SELECT * FROM " + TABLE.NOTE + + " WHERE " + NoteColumns.CREATED_DATE + " BETWEEN ? AND ?" + + " ORDER BY " + NoteColumns.MODIFIED_DATE + " DESC;"; + +// 按GTASK ID查询笔记的SQL语句 +private static final String QUERY_NOTES_BY_GTASK_ID_SQL = "SELECT * FROM " + TABLE.NOTE + + " WHERE " + NoteColumns.GTASK_ID + " = ?;"; + +// 更新笔记的GTASK ID的SQL语句 +private static final String UPDATE_NOTE_GTASK_ID_SQL = "UPDATE " + TABLE.NOTE + " SET " + + NoteColumns.GTASK_ID + " = ?" + + " WHERE " + NoteColumns.ID + " = ?;"; + +// 查询回收站中的笔记的SQL语句 +private static final String QUERY_TRASH_NOTES_SQL = "SELECT * FROM " + TABLE.NOTE + + " WHERE " + NoteColumns.PARENT_ID + " = " + Notes.ID_TRASH_FOLER + + " ORDER BY " + NoteColumns.MODIFIED_DATE + " DESC;"; + +// 从回收站恢复笔记的SQL语句 +private static final String RESTORE_NOTE_FROM_TRASH_SQL = "UPDATE " + TABLE.NOTE + " SET " + + NoteColumns.PARENT_ID + " = ?" + + " WHERE " + NoteColumns.ID + " = ?;"; + +// 永久删除笔记的SQL语句 +private static final String PERMANENTLY_DELETE_NOTE_SQL = "DELETE FROM " + TABLE.NOTE + + " WHERE " + NoteColumns.ID + " = ?;"; + +// 查询文件夹中笔记数量的SQL语句 +private static final String QUERY_FOLDER_COUNT_SQL = "SELECT COUNT(*) FROM " + TABLE.NOTE + + " WHERE " + NoteColumns.PARENT_ID + " = ?;"; + +// 更新文件夹中笔记数量的SQL语句 +private static final String UPDATE_FOLDER_COUNT_SQL = "UPDATE " + TABLE.NOTE + " SET " + + NoteColumns.COUNT + " = ?" + + " WHERE " + NoteColumns.ID + " = ?;"; + +// 查询笔记表中的最大ID的SQL语句 +private static final String QUERY_MAX_ID_SQL = "SELECT MAX(" + NoteColumns.ID + ") FROM " + TABLE.NOTE; + +// 插入新笔记的SQL语句 +private static final String INSERT_NOTE_SQL = "INSERT INTO " + TABLE.NOTE + " (" + + NoteColumns.ID + ", " + + NoteColumns.TITLE + ", " + + NoteColumns.CONTENT + ", " + + NoteColumns.CREATED_DATE + ", " + + NoteColumns.MODIFIED_DATE + ", " + + NoteColumns.TYPE + ", " + + NoteColumns.PARENT_ID + ")" + + " VALUES (?, ?, ?, ?, ?, ?, ?);"; + +// 更新笔记标题的SQL语句 +private static final String UPDATE_NOTE_TITLE_SQL = "UPDATE " + TABLE.NOTE + " SET " + + NoteColumns.TITLE + " = ?" + + " WHERE " + NoteColumns.ID + " = ?;"; + +// 更新笔记内容的SQL语句 +private static final String UPDATE_NOTE_CONTENT_SQL = "UPDATE " + TABLE.NOTE + " SET " + + NoteColumns.CONTENT + " = ?" + + " WHERE " + NoteColumns.ID + " = ?;"; + +// 更新笔记修改日期的SQL语句 +private static final String UPDATE_NOTE_MODIFIED_DATE_SQL = "UPDATE " + TABLE.NOTE + " SET " + + NoteColumns.MODIFIED_DATE + " = ?" + + " WHERE " + NoteColumns.ID + " = ?;"; + +// 更新笔记父ID的SQL语句 +private static final String UPDATE_NOTE_PARENT_ID_SQL = "UPDATE " + TABLE.NOTE + " SET " + + NoteColumns.PARENT_ID + " = ?" + + " WHERE " + NoteColumns.ID + " = ?;"; + +// 删除文件夹中的笔记的SQL语句 +private static final String DELETE_NOTES_IN_FOLDER_SQL = "DELETE FROM " + TABLE.NOTE + + " WHERE " + NoteColumns.PARENT_ID + " = ?;"; + +// 按父ID查询笔记的SQL语句 +private static final String QUERY_NOTES_BY_PARENT_ID_SQL = "SELECT * FROM " + TABLE.NOTE + + " WHERE " + NoteColumns.PARENT_ID + " = ?;"; + +// 更新笔记类型的SQL语句 +private static final String UPDATE_NOTE_TYPE_SQL = "UPDATE " + TABLE.NOTE + " SET " + + NoteColumns.TYPE + " = ?" + + " WHERE " + NoteColumns.ID + " = ?;"; + +// 按类型和父ID查询笔记的SQL语句 +private static final String QUERY_NOTES_BY_TYPE_AND_PARENT_ID_SQL = "SELECT * FROM " + TABLE.NOTE + + " WHERE " + NoteColumns.TYPE + " = ? AND " + + NoteColumns.PARENT_ID + " = ?;" + + " ORDER BY " + NoteColumns.MODIFIED_DATE + " DESC;"; + +/** + * 构造函数,初始化数据库帮助器。 + * + * @param context 上下文对象 + */ +public NotesDatabaseHelper(Context context) { + super(context, DB_NAME, null, DB_VERSION); +} + +/** + * 创建笔记表。 + * + * @param db 数据库对象 + */ +public void createNoteTable(SQLiteDatabase db) { + db.execSQL(CREATE_NOTE_TABLE_SQL); + reCreateNoteTableTriggers(db); + createSystemFolder(db); + Log.d(TAG, "note table has been created"); +} + +/** + * 重新创建笔记表的触发器。 + * + * @param db 数据库对象 + */ +private void reCreateNoteTableTriggers(SQLiteDatabase db) { + db.execSQL("DROP TRIGGER IF EXISTS increase_folder_count_on_update"); + db.execSQL("DROP TRIGGER IF EXISTS decrease_folder_count_on_update"); + db.execSQL("DROP TRIGGER IF EXISTS decrease_folder_count_on_delete"); + db.execSQL("DROP TRIGGER IF EXISTS delete_data_on_delete"); + db.execSQL("DROP TRIGGER IF EXISTS increase_folder_count_on_insert"); + db.execSQL("DROP TRIGGER IF EXISTS folder_delete_notes_on_delete"); + db.execSQL("DROP TRIGGER IF EXISTS folder_move_notes_on_trash"); + + db.execSQL(NOTE_INCREASE_FOLDER_COUNT_ON_UPDATE_TRIGGER); + db.execSQL(NOTE_DECREASE_FOLDER_COUNT_ON_UPDATE_TRIGGER); + db.execSQL(NOTE_DECREASE_FOLDER_COUNT_ON_DELETE_TRIGGER); + db.execSQL(NOTE_DELETE_DATA_ON_DELETE_TRIGGER); + db.execSQL(NOTE_INCREASE_FOLDER_COUNT_ON_INSERT_TRIGGER); + db.execSQL(FOLDER_DELETE_NOTES_ON_DELETE_TRIGGER); + db.execSQL(FOLDER_MOVE_NOTES_ON_TRASH_TRIGGER); +} + +/** + * 创建系统文件夹。 + * + * @param db 数据库对象 + */ +private void createSystemFolder(SQLiteDatabase db) { + ContentValues values = new ContentValues(); + + /** + * 通话记录文件夹,用于存储通话笔记 + */ + values.put(NoteColumns.ID, Notes.ID_CALL_RECORD_FOLDER); + values.put(NoteColumns.TYPE, Notes.TYPE_SYSTEM); + db.insert(TABLE.NOTE, null, values); + + /** + * 根文件夹,作为默认文件夹 + */ + values.clear(); + values.put(NoteColumns.ID, Notes.ID \ No newline at end of file diff --git a/SqlData.java b/SqlData.java new file mode 100644 index 0000000..150a6f8 --- /dev/null +++ b/SqlData.java @@ -0,0 +1,192 @@ +import net.micode.notes.gtask.exception.ActionFailureException; + +import org.json.JSONException; +import org.json.JSONObject; + +/** + * SqlData类用于处理与数据库相关的操作,主要负责将JSON数据与数据库记录进行相互转换和更新 + */ +public class SqlData { + // 日志标签,用于标识日志来源 + private static final String TAG = SqlData.class.getSimpleName(); + + // 无效的ID标识,用于表示未初始化或无效的数据库ID + private static final int INVALID_ID = -99999; + + // 数据库查询投影,定义了需要查询的列 + public static final String[] PROJECTION_DATA = new String[] { + DataColumns.ID, DataColumns.MIME_TYPE, DataColumns.CONTENT, DataColumns.DATA1, + DataColumns.DATA3 + }; + + // 下面是数据库查询结果中各列的索引位置 + public static final int DATA_ID_COLUMN = 0; + public static final int DATA_MIME_TYPE_COLUMN = 1; + public static final int DATA_CONTENT_COLUMN = 2; + public static final int DATA_CONTENT_DATA_1_COLUMN = 3; + public static final int DATA_CONTENT_DATA_3_COLUMN = 4; + + // ContentResolver用于访问内容提供者 + private ContentResolver mContentResolver; + + // 标识当前对象是否是新创建的 + private boolean mIsCreate; + + // 下面是数据库记录的各项字段 + private long mDataId; + private String mDataMimeType; + private String mDataContent; + private long mDataContentData1; + private String mDataContentData3; + + // 用于存储待更新的数据库记录值 + private ContentValues mDiffDataValues; + + /** + * 构造函数,用于创建新的SqlData对象 + * @param context 上下文环境,用于获取ContentResolver + */ + public SqlData(Context context) { + mContentResolver = context.getContentResolver(); + mIsCreate = true; + mDataId = INVALID_ID; + mDataMimeType = DataConstants.NOTE; + mDataContent = ""; + mDataContentData1 = 0; + mDataContentData3 = ""; + mDiffDataValues = new ContentValues(); + } + + /** + * 构造函数,用于从现有的数据库记录创建SqlData对象 + * @param context 上下文环境,用于获取ContentResolver + * @param c 游标对象,指向数据库记录 + */ + public SqlData(Context context, Cursor c) { + mContentResolver = context.getContentResolver(); + mIsCreate = false; + loadFromCursor(c); + mDiffDataValues = new ContentValues(); + } + + /** + * 从游标中加载数据库记录 + * @param c 游标对象,指向数据库记录 + */ + private void loadFromCursor(Cursor c) { + mDataId = c.getLong(DATA_ID_COLUMN); + mDataMimeType = c.getString(DATA_MIME_TYPE_COLUMN); + mDataContent = c.getString(DATA_CONTENT_COLUMN); + mDataContentData1 = c.getLong(DATA_CONTENT_DATA_1_COLUMN); + mDataContentData3 = c.getString(DATA_CONTENT_DATA_3_COLUMN); + } + + /** + * 设置内容,从JSONObject中提取数据并更新到SqlData对象 + * @param js 包含数据的JSONObject对象 + * @throws JSONException 当JSON操作失败时抛出 + */ + public void setContent(JSONObject js) throws JSONException { + long dataId = js.has(DataColumns.ID) ? js.getLong(DataColumns.ID) : INVALID_ID; + if (mIsCreate || mDataId != dataId) { + mDiffDataValues.put(DataColumns.ID, dataId); + } + mDataId = dataId; + + String dataMimeType = js.has(DataColumns.MIME_TYPE) ? js.getString(DataColumns.MIME_TYPE) + : DataConstants.NOTE; + if (mIsCreate || !mDataMimeType.equals(dataMimeType)) { + mDiffDataValues.put(DataColumns.MIME_TYPE, dataMimeType); + } + mDataMimeType = dataMimeType; + + String dataContent = js.has(DataColumns.CONTENT) ? js.getString(DataColumns.CONTENT) : ""; + if (mIsCreate || !mDataContent.equals(dataContent)) { + mDiffDataValues.put(DataColumns.CONTENT, dataContent); + } + mDataContent = dataContent; + + long dataContentData1 = js.has(DataColumns.DATA1) ? js.getLong(DataColumns.DATA1) : 0; + if (mIsCreate || mDataContentData1 != dataContentData1) { + mDiffDataValues.put(DataColumns.DATA1, dataContentData1); + } + mDataContentData1 = dataContentData1; + + String dataContentData3 = js.has(DataColumns.DATA3) ? js.getString(DataColumns.DATA3) : ""; + if (mIsCreate || !mDataContentData3.equals(dataContentData3)) { + mDiffDataValues.put(DataColumns.DATA3, dataContentData3); + } + mDataContentData3 = dataContentData3; + } + + /** + * 获取内容,将SqlData对象的数据封装到JSONObject中 + * @return 包含数据的JSONObject对象 + * @throws JSONException 当JSON操作失败时抛出 + */ + public JSONObject getContent() throws JSONException { + if (mIsCreate) { + Log.e(TAG, "it seems that we haven't created this in database yet"); + return null; + } + JSONObject js = new JSONObject(); + js.put(DataColumns.ID, mDataId); + js.put(DataColumns.MIME_TYPE, mDataMimeType); + js.put(DataColumns.CONTENT, mDataContent); + js.put(DataColumns.DATA1, mDataContentData1); + js.put(DataColumns.DATA3, mDataContentData3); + return js; + } + + /** + * 提交更改,将SqlData对象的数据更新到数据库中 + * @param noteId 笔记的ID + * @param validateVersion 是否需要验证版本号 + * @param version 版本号 + */ + public void commit(long noteId, boolean validateVersion, long version) { + if (mIsCreate) { + if (mDataId == INVALID_ID && mDiffDataValues.containsKey(DataColumns.ID)) { + mDiffDataValues.remove(DataColumns.ID); + } + + mDiffDataValues.put(DataColumns.NOTE_ID, noteId); + Uri uri = mContentResolver.insert(Notes.CONTENT_DATA_URI, mDiffDataValues); + try { + mDataId = Long.valueOf(uri.getPathSegments().get(1)); + } catch (NumberFormatException e) { + Log.e(TAG, "Get note id error :" + e.toString()); + throw new ActionFailureException("create note failed"); + } + } else { + if (mDiffDataValues.size() > 0) { + int result = 0; + if (!validateVersion) { + result = mContentResolver.update(ContentUris.withAppendedId( + Notes.CONTENT_DATA_URI, mDataId), mDiffDataValues, null, null); + } else { + result = mContentResolver.update(ContentUris.withAppendedId( + Notes.CONTENT_DATA_URI, mDataId), mDiffDataValues, + " ? in (SELECT " + NoteColumns.ID + " FROM " + TABLE.NOTE + + " WHERE " + NoteColumns.VERSION + "=?)", new String[] { + String.valueOf(noteId), String.valueOf(version) + }); + } + if (result == 0) { + Log.w(TAG, "there is no update. maybe user updates note when syncing"); + } + } + } + + mDiffDataValues.clear(); + mIsCreate = false; + } + + /** + * 获取数据库记录的ID + * @return 数据库记录的ID + */ + public long getId() { + return mDataId; + } +} diff --git a/SqlNote.java b/SqlNote.java new file mode 100644 index 0000000..e48eea0 --- /dev/null +++ b/SqlNote.java @@ -0,0 +1,94 @@ +// The class Note is used to handle note-related data and operations, including the creation, updating, and querying of notes. +public class Note { + // Other member variables and constructors are omitted here. + + /** + * Update the content of the note. + * @param data The new content of the note. + * @return Returns true if the update is successful, otherwise returns false. + */ + public boolean setContent(String data) { + // Existing code is omitted here. + } + + /** + * Get the content of the note in the form of a JSONObject. + * @return Returns the JSONObject containing the note content, or null if the operation fails. + */ + public JSONObject getContent() { + // Existing code is omitted here. + } + + /** + * Set the parent ID of the note. + * @param id The new parent ID of the note. + */ + public void setParentId(long id) { + // Existing code is omitted here. + } + + /** + * Set the GTASK ID of the note. + * @param gid The new GTASK ID of the note. + */ + public void setGtaskId(String gid) { + // Existing code is omitted here. + } + + /** + * Set the sync ID of the note. + * @param syncId The new sync ID of the note. + */ + public void setSyncId(long syncId) { + // Existing code is omitted here. + } + + /** + * Reset the local modification flag of the note. + */ + public void resetLocalModified() { + // Existing code is omitted here. + } + + /** + * Get the ID of the note. + * @return Returns the ID of the note. + */ + public long getId() { + // Existing code is omitted here. + } + + /** + * Get the parent ID of the note. + * @return Returns the parent ID of the note. + */ + public long getParentId() { + // Existing code is omitted here. + } + + /** + * Get the snippet of the note. + * @return Returns the snippet of the note. + */ + public String getSnippet() { + // Existing code is omitted here. + } + + /** + * Check if the note type is a regular note. + * @return Returns true if the note type is a regular note, otherwise returns false. + */ + public boolean isNoteType() { + // Existing code is omitted here. + } + + /** + * Commit the changes of the note. + * @param validateVersion Whether to validate the version before updating. + * @throws ActionFailureException If the note creation fails. + * @throws IllegalStateException If the note ID is invalid or the update fails. + */ + public void commit(boolean validateVersion) { + // Existing code is omitted here. + } +} diff --git a/Task.java b/Task.java new file mode 100644 index 0000000..c66b077 --- /dev/null +++ b/Task.java @@ -0,0 +1,392 @@ +```java +// Task类继承自Node类,用于表示一个任务对象 +public class Task extends Node { + // 日志标签,用于标识日志信息来源 + private static final String TAG = Task.class.getSimpleName(); + + // 表示任务是否已完成 + private boolean mCompleted; + + // 任务的备注信息 + private String mNotes; + + // 任务的元信息,使用JSONObject存储 + private JSONObject mMetaInfo; + + // 任务的前一个兄弟任务,用于任务排序 + private Task mPriorSibling; + + // 任务所属的任务列表 + private TaskList mParent; + + // Task类的构造方法,初始化任务的默认状态 + public Task() { + super(); + mCompleted = false; + mNotes = null; + mPriorSibling = null; + mParent = null; + mMetaInfo = null; + } + + /** + * 生成任务创建操作的JSON对象 + * @param actionId 操作ID + * @return 返回表示创建操作的JSONObject + * @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, mParent.getChildTaskIndex(this)); + + // 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_TASK); + if (getNotes() != null) { + entity.put(GTaskStringUtils.GTASK_JSON_NOTES, getNotes()); + } + js.put(GTaskStringUtils.GTASK_JSON_ENTITY_DELTA, entity); + + // parent_id + js.put(GTaskStringUtils.GTASK_JSON_PARENT_ID, mParent.getGid()); + + // dest_parent_type + js.put(GTaskStringUtils.GTASK_JSON_DEST_PARENT_TYPE, + GTaskStringUtils.GTASK_JSON_TYPE_GROUP); + + // list_id + js.put(GTaskStringUtils.GTASK_JSON_LIST_ID, mParent.getGid()); + + // prior_sibling_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("fail to generate task-create jsonobject"); + } + + return js; + } + + /** + * 生成任务更新操作的JSON对象 + * @param actionId 操作ID + * @return 返回表示更新操作的JSONObject + * @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()); + 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("fail to generate task-update jsonobject"); + } + + return js; + } + + /** + * 通过远程JSON对象设置任务内容 + * @param js 远程JSON对象,包含任务的相关信息 + * @throws ActionFailureException 如果设置内容失败,则抛出此异常 + */ + 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)); + } + + // notes + if (js.has(GTaskStringUtils.GTASK_JSON_NOTES)) { + setNotes(js.getString(GTaskStringUtils.GTASK_JSON_NOTES)); + } + + // deleted + if (js.has(GTaskStringUtils.GTASK_JSON_DELETED)) { + setDeleted(js.getBoolean(GTaskStringUtils.GTASK_JSON_DELETED)); + } + + // completed + 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("fail to get task content from jsonobject"); + } + } + } + + /** + * 通过本地JSON对象设置任务内容 + * @param js 本地JSON对象,包含任务的相关信息 + * @throws ActionFailureException 如果设置内容失败,则抛出此异常 + */ + public void setContentByLocalJSON(JSONObject js) { + if (js == null || !js.has(GTaskStringUtils.META_HEAD_NOTE) + || !js.has(GTaskStringUtils.META_HEAD_DATA)) { +``` + +// 当没有可用内容时记录警告信息 +Log.w(TAG, "setContentByLocalJSON: nothing is avaiable"); +} + +try { + // 从JSON对象中获取笔记和数据数组 + 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, "invalid type"); + 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) { + // 记录并打印JSONException的堆栈跟踪 + Log.e(TAG, e.toString()); + e.printStackTrace(); +} +``` +```java +/** + * 从内容生成本地使用的JSON对象 + * + * @return 表示笔记内容的JSONObject,如果内容为空则返回null + */ +public JSONObject getLocalJSONFromContent() { + String name = getName(); + try { + // 处理从网页创建的新任务 + if (mMetaInfo == null) { + if (name == null) { + Log.w(TAG, "the note seems to be an empty one"); + return null; + } + + // 构建新的笔记JSON对象 + 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) { + // 记录并打印JSONException的堆栈跟踪 + Log.e(TAG, e.toString()); + e.printStackTrace(); + return null; + } +} +``` +```java +/** + * 设置任务的元信息 + * + * @param metaData 包含笔记信息的MetaData对象 + */ +public void setMetaInfo(MetaData metaData) { + if (metaData != null && metaData.getNotes() != null) { + try { + // 将笔记信息转换为JSON对象 + mMetaInfo = new JSONObject(metaData.getNotes()); + } catch (JSONException e) { + // 转换失败时记录错误并将mMetaInfo设置为null + Log.w(TAG, e.toString()); + mMetaInfo = null; + } + } +} +``` +```java +/** + * 根据当前状态和数据库游标确定同步操作 + * + * @param c 指向数据库中笔记数据的游标 + * @return 表示要执行的同步操作的整数 + */ +public int getSyncAction(Cursor c) { + try { + // 从mMetaInfo中获取笔记信息 + 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, "it seems that note meta has been deleted"); + return SYNC_ACTION_UPDATE_REMOTE; + } + + if (!noteInfo.has(NoteColumns.ID)) { + Log.w(TAG, "remote note id seems to be deleted"); + return SYNC_ACTION_UPDATE_LOCAL; + } + + // 验证笔记ID + if (c.getLong(SqlNote.ID_COLUMN) != noteInfo.getLong(NoteColumns.ID)) { + Log.w(TAG, "note id doesn't match"); + 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 { + // 验证GTasks 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_CONFLICT; + } + } + } catch (Exception e) { + // 记录并打印异常的堆栈跟踪 + Log.e(TAG, e.toString()); + e.printStackTrace(); + } + + return SYNC_ACTION_ERROR; +} +``` +```java +/** + * 检查任务是否有值得保存的内容 + * + * @return 布尔值,表示任务是否有值得保存的内容 + */ +public boolean isWorthSaving() { + return mMetaInfo != null || (getName() != null && getName().trim().length() > 0) + || (getNotes() != null && getNotes().trim().length() > 0); +} +``` +```java +// 设置任务的完成状态 +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/TaskList.java b/TaskList.java new file mode 100644 index 0000000..bd71738 --- /dev/null +++ b/TaskList.java @@ -0,0 +1,449 @@ +```java +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 对象 + */ +public class TaskList extends Node { + private static final String TAG = TaskList.class.getSimpleName(); + + // 任务列表的索引 + private int mIndex; + + // 任务列表 + private ArrayList mChildren; + + /** + * TaskList 构造函数初始化任务列表和索引 + */ + public TaskList() { + super(); + mChildren = new ArrayList(); + mIndex = 1; + } + + /** + * 生成创建任务列表的 JSON 对象 + * + * @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 对象 + * + * @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 对象设置任务列表的内容 + * + * @param js 远程 JSON 对象 + * @throws ActionFailureException 如果设置内容失败 + */ + 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 对象设置任务列表的内容 + * + * @param js 本地 JSON 对象 + * @throws ActionFailureException 如果设置内容失败 + */ + 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 内容 + * + * 该方法构建一个表示本地笔记信息的 JSONObject,包括文件夹名称和类型 + * @return 包含本地笔记信息的 JSONObject,如果发生异常则返回 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; + } +} + +/** + * 根据游标获取同步操作 + * + * 根据游标信息确定本地和远程笔记之间的同步操作 + * @param c 指向数据库记录的游标 + * @return 同步操作常量之一 + */ +public int getSyncAction(Cursor c) { + try { + 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 { + // 验证 gtask id + 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_REMOTE; + } + } + } catch (Exception e) { + Log.e(TAG, e.toString()); + e.printStackTrace(); + } + + return SYNC_ACTION_ERROR; +} + +/** + * 获取子任务的数量 + * + * @return 子任务的数量 + */ +public int getChildTaskCount() { + return mChildren.size(); +} + +/** + * 添加子任务 + * + * 如果子任务列表中不存在该任务,则将其添加到子任务列表中 + * @param task 要添加的任务 + * @return 如果任务成功添加则返回 true,否则返回 false + */ +public boolean addChildTask(Task task) { + boolean ret = false; + if (task != null && !mChildren.contains(task)) { + ret = mChildren.add(task); + if (ret) { + // 需要设置前一个兄弟任务和父任务 + task.setPriorSibling(mChildren.isEmpty() ? null : mChildren + .get(mChildren.size() - 1)); + task.setParent(this); + } + } + return ret; +} + +/** + * 在指定索引位置添加子任务 + * + * 将任务添加到子任务列表的指定索引位置,并更新任务列表 + * @param task 要添加的任务 + * @param index 要添加任务的索引位置 + * @return 如果任务成功添加则返回 true,否则返回 false + */ +public boolean addChildTask(Task task, int index) { + if (index < 0 || index > mChildren.size()) { + Log.e(TAG, "添加子任务: 无效的索引"); + 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; +} + +/** + * 移除子任务 + * + * 从子任务列表中移除任务,并更新任务列表 + * @param task 要移除的任务 + * @return 如果任务成功移除则返回 true,否则返回 false + */ +public boolean removeChildTask(Task task) { + boolean ret = false; + int index = mChildren.indexOf(task); + if (index != -1) { + 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; +} + +/** + * 移动子任务到新的索引位置 + * + * 将任务在子任务列表中移动到新的索引位置 + * @param task 要移动的任务 + * @param index 新的索引位置 + * @return 如果任务成功移动则返回 true,否则返回 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; + } + + if (pos == index) + return true; + return (removeChildTask(task) && addChildTask(task, index)); +} + +/** + * 通过 GID 查找子任务 + * + * @param gid 要查找的任务的 GID + * @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, "通过索引获取任务: 无效的索引"); + return null; + } + return mChildren.get(index); +} + +/** + * 通过 GID 查找子任务(替代方法) + * + * @param gid 要查找的任务的 GID + * @return 找到的任务,如果没有找到则返回 null + */ +public Task getChilTaskByGid(String gid) { + for (Task task : mChildren) { + if (task.getGid().equals(gid)) + return task; + } + return null; +} + +/** + * 获取子任务列表 + * + * @return 子任务列表 + */ +public ArrayList getChildTaskList() { + return this.mChildren; +} + +/** + * 设置任务的索引 + * + * @param index 任务的新索引 + */ +public void setIndex(int index) { + this.mIndex = index; +} + +/** + * 获取任务的索引 + * + * @return 任务的索引 + */ +public int getIndex() { + return this.mIndex; +} +} +```