diff --git a/.idea/deploymentTargetSelector.xml b/.idea/deploymentTargetSelector.xml index b268ef3..e03b986 100644 --- a/.idea/deploymentTargetSelector.xml +++ b/.idea/deploymentTargetSelector.xml @@ -5,6 +5,9 @@ + + \ No newline at end of file diff --git a/.idea/misc.xml b/.idea/misc.xml index 74dd639..b2c751a 100644 --- a/.idea/misc.xml +++ b/.idea/misc.xml @@ -1,4 +1,3 @@ - diff --git a/app/src/main/java/net/micode/notes/data/NotesProvider.java b/app/src/main/java/net/micode/notes/data/NotesProvider.java index edb0a60..b612723 100644 --- a/app/src/main/java/net/micode/notes/data/NotesProvider.java +++ b/app/src/main/java/net/micode/notes/data/NotesProvider.java @@ -35,89 +35,123 @@ import net.micode.notes.data.Notes.NoteColumns; import net.micode.notes.data.NotesDatabaseHelper.TABLE; +/** + * 笔记应用的内容提供者,用于管理笔记数据的增删改查操作 + * 实现了Android ContentProvider接口,支持跨应用数据访问 + */ public class NotesProvider extends ContentProvider { + // URI匹配器,用于解析不同的URI请求 private static final UriMatcher mMatcher; + // 数据库帮助类实例,用于操作数据库 private NotesDatabaseHelper mHelper; private static final String TAG = "NotesProvider"; - private static final int URI_NOTE = 1; - private static final int URI_NOTE_ITEM = 2; - private static final int URI_DATA = 3; - private static final int URI_DATA_ITEM = 4; - - private static final int URI_SEARCH = 5; - private static final int URI_SEARCH_SUGGEST = 6; + // URI匹配码定义 + private static final int URI_NOTE = 1; // 笔记集合URI + private static final int URI_NOTE_ITEM = 2; // 单条笔记URI + private static final int URI_DATA = 3; // 笔记数据集合URI + private static final int URI_DATA_ITEM = 4; // 单条笔记数据URI + private static final int URI_SEARCH = 5; // 搜索URI + private static final int URI_SEARCH_SUGGEST = 6; // 搜索建议URI + // 静态代码块:初始化URI匹配器 static { mMatcher = new UriMatcher(UriMatcher.NO_MATCH); + // 注册笔记相关URI mMatcher.addURI(Notes.AUTHORITY, "note", URI_NOTE); mMatcher.addURI(Notes.AUTHORITY, "note/#", URI_NOTE_ITEM); + // 注册笔记数据相关URI mMatcher.addURI(Notes.AUTHORITY, "data", URI_DATA); mMatcher.addURI(Notes.AUTHORITY, "data/#", URI_DATA_ITEM); + // 注册搜索相关URI mMatcher.addURI(Notes.AUTHORITY, "search", URI_SEARCH); mMatcher.addURI(Notes.AUTHORITY, SearchManager.SUGGEST_URI_PATH_QUERY, URI_SEARCH_SUGGEST); mMatcher.addURI(Notes.AUTHORITY, SearchManager.SUGGEST_URI_PATH_QUERY + "/*", URI_SEARCH_SUGGEST); } /** - * x'0A' represents the '\n' character in sqlite. For title and content in the search result, - * we will trim '\n' and white space in order to show more information. + * 搜索投影定义:指定搜索结果的字段映射 + * 包含ID、搜索建议文本、图标、意图动作等搜索管理器所需字段 + * 处理了文本中的换行符(x'0A')和空格,优化显示效果 */ private static final String NOTES_SEARCH_PROJECTION = NoteColumns.ID + "," - + NoteColumns.ID + " AS " + SearchManager.SUGGEST_COLUMN_INTENT_EXTRA_DATA + "," - + "TRIM(REPLACE(" + NoteColumns.SNIPPET + ", x'0A','')) AS " + SearchManager.SUGGEST_COLUMN_TEXT_1 + "," - + "TRIM(REPLACE(" + NoteColumns.SNIPPET + ", x'0A','')) AS " + SearchManager.SUGGEST_COLUMN_TEXT_2 + "," - + R.drawable.search_result + " AS " + SearchManager.SUGGEST_COLUMN_ICON_1 + "," - + "'" + Intent.ACTION_VIEW + "' AS " + SearchManager.SUGGEST_COLUMN_INTENT_ACTION + "," - + "'" + Notes.TextNote.CONTENT_TYPE + "' AS " + SearchManager.SUGGEST_COLUMN_INTENT_DATA; + + NoteColumns.ID + " AS " + SearchManager.SUGGEST_COLUMN_INTENT_EXTRA_DATA + "," + + "TRIM(REPLACE(" + NoteColumns.SNIPPET + ", x'0A','')) AS " + SearchManager.SUGGEST_COLUMN_TEXT_1 + "," + + "TRIM(REPLACE(" + NoteColumns.SNIPPET + ", x'0A','')) AS " + SearchManager.SUGGEST_COLUMN_TEXT_2 + "," + + R.drawable.search_result + " AS " + SearchManager.SUGGEST_COLUMN_ICON_1 + "," + + "'" + Intent.ACTION_VIEW + "' AS " + SearchManager.SUGGEST_COLUMN_INTENT_ACTION + "," + + "'" + Notes.TextNote.CONTENT_TYPE + "' AS " + SearchManager.SUGGEST_COLUMN_INTENT_DATA; + /** + * 搜索查询SQL:从笔记表中查询包含搜索词的记录 + * 条件:排除垃圾桶文件夹(PARENT_ID != 垃圾桶ID)、仅普通笔记(TYPE=NOTE) + */ private static String NOTES_SNIPPET_SEARCH_QUERY = "SELECT " + NOTES_SEARCH_PROJECTION - + " FROM " + TABLE.NOTE - + " WHERE " + NoteColumns.SNIPPET + " LIKE ?" - + " AND " + NoteColumns.PARENT_ID + "<>" + Notes.ID_TRASH_FOLER - + " AND " + NoteColumns.TYPE + "=" + Notes.TYPE_NOTE; + + " FROM " + TABLE.NOTE + + " WHERE " + NoteColumns.SNIPPET + " LIKE ?" + + " AND " + NoteColumns.PARENT_ID + "<>" + Notes.ID_TRASH_FOLER + + " AND " + NoteColumns.TYPE + "=" + Notes.TYPE_NOTE; + /** + * 内容提供者初始化方法 + * 初始化数据库帮助类实例 + */ @Override public boolean onCreate() { mHelper = NotesDatabaseHelper.getInstance(getContext()); return true; } + /** + * 数据查询方法 + * 根据URI匹配结果执行不同的查询逻辑 + * @param uri 请求URI + * @param projection 返回字段 + * @param selection 条件子句 + * @param selectionArgs 条件参数 + * @param sortOrder 排序规则 + * @return 查询结果Cursor + */ @Override public Cursor query(Uri uri, String[] projection, String selection, String[] selectionArgs, - String sortOrder) { + String sortOrder) { Cursor c = null; SQLiteDatabase db = mHelper.getReadableDatabase(); String id = null; + // 根据URI匹配结果执行不同查询 switch (mMatcher.match(uri)) { case URI_NOTE: - c = db.query(TABLE.NOTE, projection, selection, selectionArgs, null, null, - sortOrder); + // 查询所有笔记 + c = db.query(TABLE.NOTE, projection, selection, selectionArgs, null, null, sortOrder); break; case URI_NOTE_ITEM: + // 查询单条笔记,通过URI路径获取ID id = uri.getPathSegments().get(1); c = db.query(TABLE.NOTE, projection, NoteColumns.ID + "=" + id + parseSelection(selection), selectionArgs, null, null, sortOrder); break; case URI_DATA: - c = db.query(TABLE.DATA, projection, selection, selectionArgs, null, null, - sortOrder); + // 查询所有笔记数据 + c = db.query(TABLE.DATA, projection, selection, selectionArgs, null, null, sortOrder); break; case URI_DATA_ITEM: + // 查询单条笔记数据,通过URI路径获取ID id = uri.getPathSegments().get(1); c = db.query(TABLE.DATA, projection, DataColumns.ID + "=" + id + parseSelection(selection), selectionArgs, null, null, sortOrder); break; case URI_SEARCH: case URI_SEARCH_SUGGEST: + // 搜索相关查询,不允许指定排序和投影 if (sortOrder != null || projection != null) { throw new IllegalArgumentException( "do not specify sortOrder, selection, selectionArgs, or projection" + "with this query"); } String searchString = null; + // 从URI路径或查询参数获取搜索词 if (mMatcher.match(uri) == URI_SEARCH_SUGGEST) { if (uri.getPathSegments().size() > 1) { searchString = uri.getPathSegments().get(1); @@ -131,7 +165,9 @@ public class NotesProvider extends ContentProvider { } try { + // 构造模糊查询参数 searchString = String.format("%%%s%%", searchString); + // 执行搜索SQL c = db.rawQuery(NOTES_SNIPPET_SEARCH_QUERY, new String[] { searchString }); } catch (IllegalStateException ex) { @@ -141,21 +177,32 @@ public class NotesProvider extends ContentProvider { default: throw new IllegalArgumentException("Unknown URI " + uri); } + // 设置数据变更通知 if (c != null) { c.setNotificationUri(getContext().getContentResolver(), uri); } return c; } + /** + * 数据插入方法 + * 根据URI匹配结果插入不同类型数据 + * @param uri 插入目标URI + * @param values 插入数据 + * @return 插入后的URI + */ @Override public Uri insert(Uri uri, ContentValues values) { SQLiteDatabase db = mHelper.getWritableDatabase(); long dataId = 0, noteId = 0, insertedId = 0; + // 根据URI匹配结果执行不同插入逻辑 switch (mMatcher.match(uri)) { case URI_NOTE: + // 插入笔记记录 insertedId = noteId = db.insert(TABLE.NOTE, null, values); break; case URI_DATA: + // 插入笔记数据记录,需关联笔记ID if (values.containsKey(DataColumns.NOTE_ID)) { noteId = values.getAsLong(DataColumns.NOTE_ID); } else { @@ -166,13 +213,13 @@ public class NotesProvider extends ContentProvider { default: throw new IllegalArgumentException("Unknown URI " + uri); } - // Notify the note uri + // 通知数据变更:笔记URI if (noteId > 0) { getContext().getContentResolver().notifyChange( ContentUris.withAppendedId(Notes.CONTENT_NOTE_URI, noteId), null); } - // Notify the data uri + // 通知数据变更:数据URI if (dataId > 0) { getContext().getContentResolver().notifyChange( ContentUris.withAppendedId(Notes.CONTENT_DATA_URI, dataId), null); @@ -181,22 +228,32 @@ public class NotesProvider extends ContentProvider { return ContentUris.withAppendedId(uri, insertedId); } + /** + * 数据删除方法 + * 根据URI匹配结果删除不同类型数据 + * @param uri 删除目标URI + * @param selection 条件子句 + * @param selectionArgs 条件参数 + * @return 删除的记录数 + */ @Override public int delete(Uri uri, String selection, String[] selectionArgs) { int count = 0; String id = null; SQLiteDatabase db = mHelper.getWritableDatabase(); boolean deleteData = false; + // 根据URI匹配结果执行不同删除逻辑 switch (mMatcher.match(uri)) { case URI_NOTE: + // 删除多条笔记,补充ID>0条件(排除系统文件夹) selection = "(" + selection + ") AND " + NoteColumns.ID + ">0 "; count = db.delete(TABLE.NOTE, selection, selectionArgs); break; case URI_NOTE_ITEM: + // 删除单条笔记,通过URI路径获取ID id = uri.getPathSegments().get(1); /** - * ID that smaller than 0 is system folder which is not allowed to - * trash + * ID小于等于0为系统文件夹,不允许删除 */ long noteId = Long.valueOf(id); if (noteId <= 0) { @@ -206,10 +263,12 @@ public class NotesProvider extends ContentProvider { NoteColumns.ID + "=" + id + parseSelection(selection), selectionArgs); break; case URI_DATA: + // 删除笔记数据 count = db.delete(TABLE.DATA, selection, selectionArgs); deleteData = true; break; case URI_DATA_ITEM: + // 删除单条笔记数据,通过URI路径获取ID id = uri.getPathSegments().get(1); count = db.delete(TABLE.DATA, DataColumns.ID + "=" + id + parseSelection(selection), selectionArgs); @@ -218,6 +277,7 @@ public class NotesProvider extends ContentProvider { default: throw new IllegalArgumentException("Unknown URI " + uri); } + // 通知数据变更 if (count > 0) { if (deleteData) { getContext().getContentResolver().notifyChange(Notes.CONTENT_NOTE_URI, null); @@ -227,28 +287,42 @@ public class NotesProvider extends ContentProvider { return count; } + /** + * 数据更新方法 + * 根据URI匹配结果更新不同类型数据 + * @param uri 更新目标URI + * @param values 更新数据 + * @param selection 条件子句 + * @param selectionArgs 条件参数 + * @return 更新的记录数 + */ @Override public int update(Uri uri, ContentValues values, String selection, String[] selectionArgs) { int count = 0; String id = null; SQLiteDatabase db = mHelper.getWritableDatabase(); boolean updateData = false; + // 根据URI匹配结果执行不同更新逻辑 switch (mMatcher.match(uri)) { case URI_NOTE: + // 更新多条笔记前,先增加版本号 increaseNoteVersion(-1, selection, selectionArgs); count = db.update(TABLE.NOTE, values, selection, selectionArgs); break; case URI_NOTE_ITEM: + // 更新单条笔记前,先增加版本号 id = uri.getPathSegments().get(1); increaseNoteVersion(Long.valueOf(id), selection, selectionArgs); count = db.update(TABLE.NOTE, values, NoteColumns.ID + "=" + id + parseSelection(selection), selectionArgs); break; case URI_DATA: + // 更新笔记数据 count = db.update(TABLE.DATA, values, selection, selectionArgs); updateData = true; break; case URI_DATA_ITEM: + // 更新单条笔记数据 id = uri.getPathSegments().get(1); count = db.update(TABLE.DATA, values, DataColumns.ID + "=" + id + parseSelection(selection), selectionArgs); @@ -258,6 +332,7 @@ public class NotesProvider extends ContentProvider { throw new IllegalArgumentException("Unknown URI " + uri); } + // 通知数据变更 if (count > 0) { if (updateData) { getContext().getContentResolver().notifyChange(Notes.CONTENT_NOTE_URI, null); @@ -267,10 +342,21 @@ public class NotesProvider extends ContentProvider { return count; } + /** + * 解析查询条件:拼接额外的WHERE子句 + * @param selection 原始条件 + * @return 拼接后的条件 + */ private String parseSelection(String selection) { return (!TextUtils.isEmpty(selection) ? " AND (" + selection + ')' : ""); } + /** + * 增加笔记版本号:用于数据变更追踪 + * @param id 笔记ID(-1表示批量更新) + * @param selection 条件子句 + * @param selectionArgs 条件参数 + */ private void increaseNoteVersion(long id, String selection, String[] selectionArgs) { StringBuilder sql = new StringBuilder(120); sql.append("UPDATE "); @@ -279,6 +365,7 @@ public class NotesProvider extends ContentProvider { sql.append(NoteColumns.VERSION); sql.append("=" + NoteColumns.VERSION + "+1 "); + // 构建WHERE条件 if (id > 0 || !TextUtils.isEmpty(selection)) { sql.append(" WHERE "); } @@ -287,19 +374,24 @@ public class NotesProvider extends ContentProvider { } if (!TextUtils.isEmpty(selection)) { String selectString = id > 0 ? parseSelection(selection) : selection; + // 替换条件参数 for (String args : selectionArgs) { selectString = selectString.replaceFirst("\\?", args); } sql.append(selectString); } + // 执行SQL mHelper.getWritableDatabase().execSQL(sql.toString()); } + /** + * 获取URI对应的MIME类型(未实现) + */ @Override public String getType(Uri uri) { // TODO Auto-generated method stub return null; } -} +} \ No newline at end of file diff --git a/app/src/main/java/net/micode/notes/gtask/data/MetaData.java b/app/src/main/java/net/micode/notes/gtask/data/MetaData.java index 3a2050b..1e92254 100644 --- a/app/src/main/java/net/micode/notes/gtask/data/MetaData.java +++ b/app/src/main/java/net/micode/notes/gtask/data/MetaData.java @@ -25,36 +25,63 @@ import org.json.JSONException; import org.json.JSONObject; +/** + * 任务元数据类,继承自Task基类 + * 用于存储与Google Tasks相关的元信息,如关联的GTask ID等 + */ public class MetaData extends Task { private final static String TAG = MetaData.class.getSimpleName(); + // 关联的Google Tasks全局ID private String mRelatedGid = null; + /** + * 设置元数据信息 + * @param gid 关联的GTask全局ID + * @param metaInfo 元数据JSON对象 + */ public void setMeta(String gid, JSONObject metaInfo) { try { + // 将GTask ID存入元数据JSON的指定字段 metaInfo.put(GTaskStringUtils.META_HEAD_GTASK_ID, gid); } catch (JSONException e) { Log.e(TAG, "failed to put related gid"); } + // 将元数据JSON转为字符串存储到notes字段 setNotes(metaInfo.toString()); + // 设置固定的元数据笔记名称 setName(GTaskStringUtils.META_NOTE_NAME); } + /** + * 获取关联的GTask全局ID + * @return 关联的GTask ID,获取失败时返回null + */ public String getRelatedGid() { return mRelatedGid; } + /** + * 判断数据是否值得保存 + * @return 当notes字段不为null时返回true + */ @Override public boolean isWorthSaving() { return getNotes() != null; } + /** + * 从远程JSON数据设置内容 + * @param js 远程获取的JSON数据 + */ @Override public void setContentByRemoteJSON(JSONObject js) { super.setContentByRemoteJSON(js); if (getNotes() != null) { try { + // 解析notes字段的JSON内容 JSONObject metaInfo = new JSONObject(getNotes().trim()); + // 提取关联的GTask ID mRelatedGid = metaInfo.getString(GTaskStringUtils.META_HEAD_GTASK_ID); } catch (JSONException e) { Log.w(TAG, "failed to get related gid"); @@ -63,20 +90,34 @@ public class MetaData extends Task { } } + /** + * 本地JSON内容设置方法(禁止调用) + * @param js 本地JSON数据 + * @throws IllegalAccessError 始终抛出异常,表明该方法不应被调用 + */ @Override public void setContentByLocalJSON(JSONObject js) { - // this function should not be called throw new IllegalAccessError("MetaData:setContentByLocalJSON should not be called"); } + /** + * 从内容生成本地JSON(禁止调用) + * @return 始终抛出异常,表明该方法不应被调用 + * @throws IllegalAccessError 异常 + */ @Override public JSONObject getLocalJSONFromContent() { throw new IllegalAccessError("MetaData:getLocalJSONFromContent should not be called"); } + /** + * 获取同步操作类型(禁止调用) + * @param c 数据库游标 + * @return 始终抛出异常,表明该方法不应被调用 + * @throws IllegalAccessError 异常 + */ @Override public int getSyncAction(Cursor c) { throw new IllegalAccessError("MetaData:getSyncAction should not be called"); } - -} +} \ No newline at end of file diff --git a/app/src/main/java/net/micode/notes/gtask/data/Node.java b/app/src/main/java/net/micode/notes/gtask/data/Node.java index 63950e0..dee1ed0 100644 --- a/app/src/main/java/net/micode/notes/gtask/data/Node.java +++ b/app/src/main/java/net/micode/notes/gtask/data/Node.java @@ -20,33 +20,32 @@ 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 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; // Google Tasks全局唯一ID + private String mName; // 节点名称 + private long mLastModified; // 最后修改时间戳 + private boolean mDeleted; // 删除标记 + + /** + * 构造函数:初始化节点基本属性 + */ public Node() { mGid = null; mName = ""; @@ -54,18 +53,46 @@ public abstract class Node { mDeleted = false; } + /** + * 获取创建操作JSON + * @param actionId 操作ID + * @return 创建操作的JSON对象 + */ public abstract JSONObject getCreateAction(int actionId); + /** + * 获取更新操作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 同步动作类型(SYNC_ACTION_*常量之一) + */ public abstract int getSyncAction(Cursor c); + // 属性设置器和获取器 public void setGid(String gid) { this.mGid = gid; } @@ -97,5 +124,4 @@ public abstract class Node { public boolean getDeleted() { return this.mDeleted; } - -} +} \ No newline at end of file diff --git a/app/src/main/java/net/micode/notes/gtask/data/SqlData.java b/app/src/main/java/net/micode/notes/gtask/data/SqlData.java index d3ec3be..442096b 100644 --- a/app/src/main/java/net/micode/notes/gtask/data/SqlData.java +++ b/app/src/main/java/net/micode/notes/gtask/data/SqlData.java @@ -35,60 +35,68 @@ import org.json.JSONException; import org.json.JSONObject; +/** + * 数据库数据操作类 + * 用于封装笔记数据(Data表)的CRUD操作,支持与JSON数据的转换 + */ public class SqlData { private static final String TAG = SqlData.class.getSimpleName(); + private static final int INVALID_ID = -99999; // 无效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; - - private ContentResolver mContentResolver; - - private boolean mIsCreate; - - private long mDataId; - - private String mDataMimeType; - - private String mDataContent; - - private long mDataContentData1; - - private String mDataContentData3; - - private ContentValues mDiffDataValues; - + // 投影字段索引常量 + public static final int DATA_ID_COLUMN = 0; // 数据ID索引 + public static final int DATA_MIME_TYPE_COLUMN = 1; // MIME类型索引 + public static final int DATA_CONTENT_COLUMN = 2; // 内容索引 + public static final int DATA_CONTENT_DATA_1_COLUMN = 3;// 数据1索引 + public static final int DATA_CONTENT_DATA_3_COLUMN = 4;// 数据3索引 + + // 实例变量 + private ContentResolver mContentResolver; // 内容解析器 + private boolean mIsCreate; // 是否为新建状态 + private long mDataId; // 数据ID + private String mDataMimeType; // MIME类型 + private String mDataContent; // 数据内容 + private long mDataContentData1; // 数据1(长整型) + private String mDataContentData3; // 数据3(字符串) + private ContentValues mDiffDataValues; // 变更数据值 + + /** + * 构造函数:初始化新建状态的SqlData实例 + * @param context 上下文对象 + */ public SqlData(Context context) { mContentResolver = context.getContentResolver(); mIsCreate = true; mDataId = INVALID_ID; - mDataMimeType = DataConstants.NOTE; + mDataMimeType = DataConstants.NOTE; // 默认MIME类型为笔记 mDataContent = ""; mDataContentData1 = 0; mDataContentData3 = ""; mDiffDataValues = new ContentValues(); } + /** + * 构造函数:从游标加载数据初始化SqlData实例 + * @param context 上下文对象 + * @param c 数据库游标 + */ public SqlData(Context context, Cursor c) { mContentResolver = context.getContentResolver(); mIsCreate = false; - loadFromCursor(c); + 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); @@ -97,13 +105,20 @@ public class SqlData { mDataContentData3 = c.getString(DATA_CONTENT_DATA_3_COLUMN); } + /** + * 从JSON对象设置数据内容 + * @param js JSON数据对象 + * @throws JSONException JSON解析异常 + */ public void setContent(JSONObject js) throws JSONException { + // 处理ID字段 long dataId = js.has(DataColumns.ID) ? js.getLong(DataColumns.ID) : INVALID_ID; if (mIsCreate || mDataId != dataId) { mDiffDataValues.put(DataColumns.ID, dataId); } mDataId = dataId; + // 处理MIME类型字段 String dataMimeType = js.has(DataColumns.MIME_TYPE) ? js.getString(DataColumns.MIME_TYPE) : DataConstants.NOTE; if (mIsCreate || !mDataMimeType.equals(dataMimeType)) { @@ -111,18 +126,21 @@ public class SqlData { } mDataMimeType = dataMimeType; + // 处理内容字段 String dataContent = js.has(DataColumns.CONTENT) ? js.getString(DataColumns.CONTENT) : ""; if (mIsCreate || !mDataContent.equals(dataContent)) { mDiffDataValues.put(DataColumns.CONTENT, dataContent); } mDataContent = dataContent; + // 处理DATA1字段 long dataContentData1 = js.has(DataColumns.DATA1) ? js.getLong(DataColumns.DATA1) : 0; if (mIsCreate || mDataContentData1 != dataContentData1) { mDiffDataValues.put(DataColumns.DATA1, dataContentData1); } mDataContentData1 = dataContentData1; + // 处理DATA3字段 String dataContentData3 = js.has(DataColumns.DATA3) ? js.getString(DataColumns.DATA3) : ""; if (mIsCreate || !mDataContentData3.equals(dataContentData3)) { mDiffDataValues.put(DataColumns.DATA3, dataContentData3); @@ -130,6 +148,11 @@ public class SqlData { mDataContentData3 = dataContentData3; } + /** + * 将数据内容转换为JSON对象 + * @return JSON数据对象 + * @throws JSONException JSON构造异常 + */ public JSONObject getContent() throws JSONException { if (mIsCreate) { Log.e(TAG, "it seems that we haven't created this in database yet"); @@ -144,13 +167,18 @@ public class SqlData { return js; } + /** + * 提交数据变更到数据库 + * @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 { @@ -160,14 +188,17 @@ public class SqlData { 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, + Notes.CONTENT_DATA_URI, mDataId), mDiffDataValues, " ? in (SELECT " + NoteColumns.ID + " FROM " + TABLE.NOTE + " WHERE " + NoteColumns.VERSION + "=?)", new String[] { String.valueOf(noteId), String.valueOf(version) @@ -178,12 +209,15 @@ public class SqlData { } } } - mDiffDataValues.clear(); mIsCreate = false; } + /** + * 获取数据ID + * @return 数据ID + */ public long getId() { return mDataId; } -} +} \ No newline at end of file diff --git a/app/src/main/java/net/micode/notes/gtask/data/Task.java b/app/src/main/java/net/micode/notes/gtask/data/Task.java index 6a19454..3aa64cc 100644 --- a/app/src/main/java/net/micode/notes/gtask/data/Task.java +++ b/app/src/main/java/net/micode/notes/gtask/data/Task.java @@ -32,18 +32,18 @@ import org.json.JSONException; import org.json.JSONObject; +/** + * 任务类 - 表示Google Tasks中的一个任务项 + * 继承自Node基类,实现了任务的创建、更新、同步等操作 + */ 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; + private boolean mCompleted; // 任务完成状态 + private String mNotes; // 任务备注信息 + private JSONObject mMetaInfo; // 任务元信息(包含本地存储关联信息) + private Task mPriorSibling; // 前一个兄弟任务(排序用) + private TaskList mParent; // 父任务列表 public Task() { super(); @@ -54,21 +54,26 @@ public class Task extends Node { mMetaInfo = null; } + /** + * 生成创建任务的JSON操作对象 + * @param actionId 操作ID + * @return 创建任务的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 + // 设置操作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"); @@ -79,17 +84,15 @@ public class Task extends Node { } js.put(GTaskStringUtils.GTASK_JSON_ENTITY_DELTA, entity); - // parent_id + // 设置父任务列表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 + // 设置任务列表ID(与父ID相同) js.put(GTaskStringUtils.GTASK_JSON_LIST_ID, mParent.getGid()); - // prior_sibling_id + // 设置前一个兄弟任务ID(用于排序) if (mPriorSibling != null) { js.put(GTaskStringUtils.GTASK_JSON_PRIOR_SIBLING_ID, mPriorSibling.getGid()); } @@ -103,21 +106,24 @@ public class Task extends Node { return js; } + /** + * 生成更新任务的JSON操作对象 + * @param actionId 操作ID + * @return 更新任务的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 + // 设置操作ID和任务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) { @@ -135,35 +141,39 @@ public class Task extends Node { return js; } + /** + * 从远程JSON数据设置任务内容 + * @param js 远程JSON数据 + */ public void setContentByRemoteJSON(JSONObject js) { if (js != null) { try { - // id + // 从JSON中提取任务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)); } @@ -175,6 +185,10 @@ public class Task extends Node { } } + /** + * 从本地JSON数据设置任务内容 + * @param js 本地JSON数据 + */ public void setContentByLocalJSON(JSONObject js) { if (js == null || !js.has(GTaskStringUtils.META_HEAD_NOTE) || !js.has(GTaskStringUtils.META_HEAD_DATA)) { @@ -182,14 +196,17 @@ public class Task extends Node { } 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, "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)) { @@ -204,16 +221,21 @@ public class Task extends Node { } } + /** + * 从任务内容生成本地JSON数据 + * @return 本地JSON数据对象 + */ public JSONObject getLocalJSONFromContent() { String name = getName(); try { if (mMetaInfo == null) { - // new task created from web + // 新创建的任务(来自网页) 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(); @@ -225,10 +247,11 @@ public class Task extends Node { js.put(GTaskStringUtils.META_HEAD_NOTE, note); return js; } else { - // synced task + // 已同步的任务,更新现有元信息 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)) { @@ -247,9 +270,14 @@ public class Task extends Node { } } + /** + * 设置任务元信息 + * @param metaData 元数据对象 + */ public void setMetaInfo(MetaData metaData) { if (metaData != null && metaData.getNotes() != null) { try { + // 解析元数据中的JSON字符串 mMetaInfo = new JSONObject(metaData.getNotes()); } catch (JSONException e) { Log.w(TAG, e.toString()); @@ -258,48 +286,58 @@ public class Task extends Node { } } + /** + * 根据数据库记录确定同步操作类型 + * @param c 数据库游标 + * @return 同步操作类型(SYNC_ACTION_*常量之一) + */ 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, "it seems that note meta has been deleted"); return SYNC_ACTION_UPDATE_REMOTE; } + // 元信息中没有笔记ID,需要更新本地 if (!noteInfo.has(NoteColumns.ID)) { Log.w(TAG, "remote note id seems to be deleted"); return SYNC_ACTION_UPDATE_LOCAL; } - // validate the note id now + // 验证笔记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) { - // 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 + // 本地有修改,验证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 { + // 本地和远程都有修改,发生冲突 return SYNC_ACTION_UPDATE_CONFLICT; } } @@ -311,11 +349,16 @@ public class Task extends Node { return SYNC_ACTION_ERROR; } + /** + * 判断任务是否值得保存 + * @return 如果任务有名称或备注,返回true;否则返回false + */ 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; } @@ -347,5 +390,4 @@ public class Task extends Node { public TaskList getParent() { return this.mParent; } - -} +} \ No newline at end of file diff --git a/app/src/main/java/net/micode/notes/gtask/data/TaskList.java b/app/src/main/java/net/micode/notes/gtask/data/TaskList.java index 4ea21c5..fa91404 100644 --- a/app/src/main/java/net/micode/notes/gtask/data/TaskList.java +++ b/app/src/main/java/net/micode/notes/gtask/data/TaskList.java @@ -30,12 +30,15 @@ import org.json.JSONObject; import java.util.ArrayList; +/** + * 任务列表类 - 表示Google Tasks中的一个任务列表 + * 继承自Node基类,管理一组任务(Task)对象,实现任务列表的创建、更新、同步等操作 + */ public class TaskList extends Node { private static final String TAG = TaskList.class.getSimpleName(); - private int mIndex; - - private ArrayList mChildren; + private int mIndex; // 任务列表在父容器中的索引位置 + private ArrayList mChildren; // 子任务列表 public TaskList() { super(); @@ -43,21 +46,26 @@ public class TaskList extends Node { mIndex = 1; } + /** + * 生成创建任务列表的JSON操作对象 + * @param actionId 操作ID + * @return 创建任务列表的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 + // 设置操作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"); @@ -74,21 +82,24 @@ public class TaskList extends Node { return js; } + /** + * 生成更新任务列表的JSON操作对象 + * @param actionId 操作ID + * @return 更新任务列表的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 + // 设置操作ID和任务列表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()); @@ -103,20 +114,24 @@ public class TaskList extends Node { return js; } + /** + * 从远程JSON数据设置任务列表内容 + * @param js 远程JSON数据 + */ public void setContentByRemoteJSON(JSONObject js) { if (js != null) { try { - // id + // 从JSON中提取任务列表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)); } @@ -129,6 +144,10 @@ public class TaskList extends Node { } } + /** + * 从本地JSON数据设置任务列表内容 + * @param js 本地JSON数据 + */ public void setContentByLocalJSON(JSONObject js) { if (js == null || !js.has(GTaskStringUtils.META_HEAD_NOTE)) { Log.w(TAG, "setContentByLocalJSON: nothing is avaiable"); @@ -137,15 +156,16 @@ public class TaskList extends Node { 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); + setName(GTaskStringUtils.MIUI_FOLDER_PREFFIX + GTaskStringUtils.FOLDER_CALL_NOTE); else Log.e(TAG, "invalid system folder"); } else { @@ -157,15 +177,22 @@ public class TaskList extends Node { } } + /** + * 从任务列表内容生成本地JSON数据 + * @return 本地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(), folderName.length()); + + // 设置文件夹摘要和类型 folder.put(NoteColumns.SNIPPET, folderName); if (folderName.equals(GTaskStringUtils.FOLDER_DEFAULT) || folderName.equals(GTaskStringUtils.FOLDER_CALL_NOTE)) @@ -183,28 +210,34 @@ public class TaskList extends Node { } } + /** + * 根据数据库记录确定同步操作类型 + * @param c 数据库游标 + * @return 同步操作类型(SYNC_ACTION_*常量之一) + */ 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 + // 本地有修改,验证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; } } @@ -216,24 +249,40 @@ public class TaskList extends Node { 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) { - // need to set prior sibling and parent - task.setPriorSibling(mChildren.isEmpty() ? null : mChildren - .get(mChildren.size() - 1)); + // 设置任务的前一个兄弟任务和父任务列表 + 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, "add child task: invalid index"); @@ -244,7 +293,7 @@ public class TaskList extends Node { if (task != null && pos == -1) { mChildren.add(index, task); - // update the task list + // 更新任务列表中的前后关系 Task preTask = null; Task afterTask = null; if (index != 0) @@ -260,6 +309,11 @@ public class TaskList extends Node { return true; } + /** + * 从任务列表中移除子任务 + * @param task 要移除的任务 + * @return 移除成功返回true,失败返回false + */ public boolean removeChildTask(Task task) { boolean ret = false; int index = mChildren.indexOf(task); @@ -267,11 +321,11 @@ public class TaskList extends Node { 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)); @@ -281,8 +335,13 @@ public class TaskList extends Node { 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, "move child task: invalid index"); return false; @@ -296,9 +355,15 @@ public class TaskList extends Node { 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); @@ -309,10 +374,20 @@ public class TaskList extends Node { 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"); @@ -321,6 +396,11 @@ public class TaskList extends Node { return mChildren.get(index); } + /** + * 根据GID获取子任务 + * @param gid 任务GID + * @return 任务对象,未找到返回null + */ public Task getChilTaskByGid(String gid) { for (Task task : mChildren) { if (task.getGid().equals(gid)) @@ -329,10 +409,16 @@ public class TaskList extends Node { return null; } + /** + * 获取所有子任务列表 + * @return 子任务列表 + */ public ArrayList getChildTaskList() { return this.mChildren; } + // ----------------- 属性设置和获取方法 ----------------- + public void setIndex(int index) { this.mIndex = index; } @@ -340,4 +426,4 @@ public class TaskList extends Node { public int getIndex() { return this.mIndex; } -} +} \ No newline at end of file