diff --git a/src/gtask/data/MetaData.java b/src/gtask/data/MetaData.java index 3a2050b..db8d196 100644 --- a/src/gtask/data/MetaData.java +++ b/src/gtask/data/MetaData.java @@ -14,68 +14,142 @@ * limitations under the License. */ +/** + * 该包包含与 Google Tasks 相关的数据处理类。 + */ package net.micode.notes.gtask.data; +// 导入 Android 数据库游标类,用于处理数据库查询结果 import android.database.Cursor; +// 导入 Android 日志工具类,用于记录日志信息 import android.util.Log; +// 导入应用内 Google Tasks 字符串工具类 import net.micode.notes.tool.GTaskStringUtils; +// 导入 JSON 异常处理类,用于处理 JSON 操作时可能出现的异常 import org.json.JSONException; +// 导入 JSON 对象类,用于处理 JSON 数据 import org.json.JSONObject; - +/** + * MetaData 类继承自 Task 类,用于处理与 Google Tasks 相关的元数据。 + * 该类提供了设置元数据、获取关联 GID 等功能,同时重写了一些同步相关的方法。 + */ public class MetaData extends Task { + /** + * 日志标签,用于在日志记录中标识该类的日志信息。 + */ private final static String TAG = MetaData.class.getSimpleName(); + /** + * 关联的 Google Tasks ID。 + */ private String mRelatedGid = null; + /** + * 设置元数据信息。 + * 将关联的 Google Tasks ID 放入元信息 JSON 对象中,并设置笔记内容和名称。 + * + * @param gid 关联的 Google Tasks ID + * @param metaInfo 元信息 JSON 对象 + */ public void setMeta(String gid, JSONObject metaInfo) { try { + // 将关联的 Google Tasks ID 放入元信息 JSON 对象中 metaInfo.put(GTaskStringUtils.META_HEAD_GTASK_ID, gid); } catch (JSONException e) { + // 记录放入关联 GID 失败的日志 Log.e(TAG, "failed to put related gid"); } + // 设置笔记内容为元信息 JSON 对象的字符串表示 setNotes(metaInfo.toString()); + // 设置笔记名称为元数据笔记名称 setName(GTaskStringUtils.META_NOTE_NAME); } + /** + * 获取关联的 Google Tasks ID。 + * + * @return 关联的 Google Tasks ID,如果没有则返回 null + */ public String getRelatedGid() { return mRelatedGid; } + /** + * 判断该元数据是否值得保存。 + * 如果笔记内容不为空,则认为值得保存。 + * + * @return 如果笔记内容不为 null 则返回 true,否则返回 false + */ @Override public boolean isWorthSaving() { return getNotes() != null; } + /** + * 根据远程 JSON 数据设置内容。 + * 先调用父类的方法设置内容,然后从笔记内容中解析出关联的 Google Tasks ID。 + * + * @param js 远程 JSON 数据对象 + */ @Override public void setContentByRemoteJSON(JSONObject js) { + // 调用父类方法设置内容 super.setContentByRemoteJSON(js); + // 检查笔记内容是否不为空 if (getNotes() != null) { try { + // 将笔记内容转换为 JSON 对象 JSONObject metaInfo = new JSONObject(getNotes().trim()); + // 从 JSON 对象中获取关联的 Google Tasks ID mRelatedGid = metaInfo.getString(GTaskStringUtils.META_HEAD_GTASK_ID); } catch (JSONException e) { + // 记录获取关联 GID 失败的日志,并将关联 GID 设置为 null Log.w(TAG, "failed to get related gid"); mRelatedGid = null; } } } + /** + * 根据本地 JSON 数据设置内容。 + * 该方法不应该被调用,调用时会抛出 IllegalAccessError 异常。 + * + * @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 数据。 + * 该方法不应该被调用,调用时会抛出 IllegalAccessError 异常。 + * + * @return 不会返回有效数据 + * @throws IllegalAccessError 该方法不应该被调用 + */ @Override public JSONObject getLocalJSONFromContent() { + // 抛出异常,提示该方法不应该被调用 throw new IllegalAccessError("MetaData:getLocalJSONFromContent should not be called"); } + /** + * 获取同步操作类型。 + * 该方法不应该被调用,调用时会抛出 IllegalAccessError 异常。 + * + * @param c 数据库游标对象 + * @return 不会返回有效数据 + * @throws IllegalAccessError 该方法不应该被调用 + */ @Override public int getSyncAction(Cursor c) { + // 抛出异常,提示该方法不应该被调用 throw new IllegalAccessError("MetaData:getSyncAction should not be called"); } diff --git a/src/gtask/data/Node.java b/src/gtask/data/Node.java index 63950e0..b154e14 100644 --- a/src/gtask/data/Node.java +++ b/src/gtask/data/Node.java @@ -14,39 +14,90 @@ * limitations under the License. */ +/** + * 该包包含与 Google Tasks 相关的数据处理类。 + */ package net.micode.notes.gtask.data; +// 导入 Android 数据库游标类,用于处理数据库查询结果 import android.database.Cursor; - +// 导入 JSON 对象类,用于处理 JSON 数据 import org.json.JSONObject; +/** + * Node 是一个抽象类,作为与 Google Tasks 同步相关节点的基类。 + * 定义了一系列同步操作类型常量,以及节点的基本属性和抽象方法, + * 这些抽象方法需要由子类根据具体需求进行实现。 + */ 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; + /** + * 节点的 Google Tasks ID。 + */ private String mGid; + /** + * 节点的名称。 + */ private String mName; + /** + * 节点的最后修改时间。 + */ private long mLastModified; + /** + * 节点是否已被删除的标志。 + */ private boolean mDeleted; + /** + * Node 类的构造函数,用于初始化节点的基本属性。 + */ public Node() { mGid = null; mName = ""; @@ -54,46 +105,125 @@ 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); + /** + * 设置节点的 Google Tasks ID。 + * + * @param gid 节点的 Google Tasks ID + */ public void setGid(String gid) { this.mGid = gid; } + /** + * 设置节点的名称。 + * + * @param name 节点的名称 + */ public void setName(String name) { this.mName = name; } + /** + * 设置节点的最后修改时间。 + * + * @param lastModified 节点的最后修改时间 + */ public void setLastModified(long lastModified) { this.mLastModified = lastModified; } + /** + * 设置节点是否已被删除的标志。 + * + * @param deleted 节点是否已被删除 + */ public void setDeleted(boolean deleted) { this.mDeleted = deleted; } + /** + * 获取节点的 Google Tasks ID。 + * + * @return 节点的 Google Tasks ID + */ public String getGid() { return this.mGid; } + /** + * 获取节点的名称。 + * + * @return 节点的名称 + */ public String getName() { return this.mName; } + /** + * 获取节点的最后修改时间。 + * + * @return 节点的最后修改时间 + */ public long getLastModified() { return this.mLastModified; } + /** + * 获取节点是否已被删除的标志。 + * + * @return 节点是否已被删除 + */ public boolean getDeleted() { return this.mDeleted; } diff --git a/src/gtask/data/SqlData.java b/src/gtask/data/SqlData.java index d3ec3be..41bf1fd 100644 --- a/src/gtask/data/SqlData.java +++ b/src/gtask/data/SqlData.java @@ -14,175 +14,334 @@ * limitations under the License. */ +/** + * 该包包含与 Google Tasks 相关的 SQL 数据处理类。 + */ package net.micode.notes.gtask.data; +// 导入 Android 内容解析器类,用于与内容提供者进行交互 import android.content.ContentResolver; +// 导入 Android 内容 URI 工具类,用于操作内容 URI import android.content.ContentUris; +// 导入 Android 内容值类,用于存储键值对数据 import android.content.ContentValues; +// 导入 Android 上下文类,提供对应用环境的访问 import android.content.Context; +// 导入 Android 数据库游标类,用于处理数据库查询结果 import android.database.Cursor; +// 导入 Android URI 类,用于表示统一资源标识符 import android.net.Uri; +// 导入 Android 日志工具类,用于记录日志信息 import android.util.Log; +// 导入应用笔记数据相关类 import net.micode.notes.data.Notes; +// 导入应用笔记数据列相关类 import net.micode.notes.data.Notes.DataColumns; +// 导入应用笔记数据常量相关类 import net.micode.notes.data.Notes.DataConstants; +// 导入应用笔记列相关类 import net.micode.notes.data.Notes.NoteColumns; +// 导入应用笔记数据库帮助类的表常量 import net.micode.notes.data.NotesDatabaseHelper.TABLE; +// 导入应用 Google Tasks 操作失败异常类 import net.micode.notes.gtask.exception.ActionFailureException; +// 导入 JSON 异常处理类,用于处理 JSON 操作时可能出现的异常 import org.json.JSONException; +// 导入 JSON 对象类,用于处理 JSON 数据 import org.json.JSONObject; - +/** + * SqlData 类用于处理与 SQL 数据库相关的数据操作,包括数据的创建、加载、更新和提交等操作。 + * 该类提供了从 JSON 对象设置数据、将数据转换为 JSON 对象以及将数据提交到数据库的功能。 + */ public class SqlData { + /** + * 日志标签,用于在日志记录中标识该类的日志信息。 + */ private static final String TAG = SqlData.class.getSimpleName(); + /** + * 无效 ID 的常量值,用于表示未初始化或无效的 ID。 + */ private static final int INVALID_ID = -99999; + /** + * 查询数据时使用的投影列数组,包含数据的 ID、MIME 类型、内容、DATA1 和 DATA3 列。 + */ public static final String[] PROJECTION_DATA = new String[] { DataColumns.ID, DataColumns.MIME_TYPE, DataColumns.CONTENT, DataColumns.DATA1, DataColumns.DATA3 }; + /** + * PROJECTION_DATA 数组中数据 ID 列的索引。 + */ public static final int DATA_ID_COLUMN = 0; + /** + * PROJECTION_DATA 数组中数据 MIME 类型列的索引。 + */ public static final int DATA_MIME_TYPE_COLUMN = 1; + /** + * PROJECTION_DATA 数组中数据内容列的索引。 + */ public static final int DATA_CONTENT_COLUMN = 2; + /** + * PROJECTION_DATA 数组中数据 DATA1 列的索引。 + */ public static final int DATA_CONTENT_DATA_1_COLUMN = 3; + /** + * PROJECTION_DATA 数组中数据 DATA3 列的索引。 + */ public static final int DATA_CONTENT_DATA_3_COLUMN = 4; + /** + * 内容解析器,用于与内容提供者进行交互,执行数据库操作。 + */ private ContentResolver mContentResolver; + /** + * 标识数据是否为新创建的标志。 + */ private boolean mIsCreate; + /** + * 数据的 ID。 + */ private long mDataId; + /** + * 数据的 MIME 类型。 + */ private String mDataMimeType; + /** + * 数据的内容。 + */ private String mDataContent; + /** + * 数据的 DATA1 字段值。 + */ private long mDataContentData1; + /** + * 数据的 DATA3 字段值。 + */ private String mDataContentData3; + /** + * 存储数据差异的内容值对象,用于记录需要更新或插入到数据库的数据。 + */ private ContentValues mDiffDataValues; + /** + * 构造函数,用于创建一个新的 SqlData 对象。 + * + * @param context 应用程序上下文,用于获取内容解析器。 + */ public SqlData(Context context) { + // 获取内容解析器 mContentResolver = context.getContentResolver(); + // 标记为新创建的数据 mIsCreate = true; + // 初始化数据 ID 为无效 ID mDataId = INVALID_ID; + // 初始化数据 MIME 类型为默认笔记类型 mDataMimeType = DataConstants.NOTE; + // 初始化数据内容为空字符串 mDataContent = ""; + // 初始化数据 DATA1 字段值为 0 mDataContentData1 = 0; + // 初始化数据 DATA3 字段值为空字符串 mDataContentData3 = ""; + // 初始化数据差异内容值对象 mDiffDataValues = new ContentValues(); } + /** + * 构造函数,用于从数据库游标中加载数据创建 SqlData 对象。 + * + * @param context 应用程序上下文,用于获取内容解析器。 + * @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) { + // 从游标中获取数据 ID mDataId = c.getLong(DATA_ID_COLUMN); + // 从游标中获取数据 MIME 类型 mDataMimeType = c.getString(DATA_MIME_TYPE_COLUMN); + // 从游标中获取数据内容 mDataContent = c.getString(DATA_CONTENT_COLUMN); + // 从游标中获取数据 DATA1 字段值 mDataContentData1 = c.getLong(DATA_CONTENT_DATA_1_COLUMN); + // 从游标中获取数据 DATA3 字段值 mDataContentData3 = c.getString(DATA_CONTENT_DATA_3_COLUMN); } + /** + * 根据 JSON 对象设置数据内容,并记录数据差异。 + * + * @param js 包含数据信息的 JSON 对象。 + * @throws JSONException 当 JSON 操作出现异常时抛出。 + */ public void setContent(JSONObject js) throws JSONException { + // 从 JSON 对象中获取数据 ID,如果不存在则使用无效 ID long dataId = js.has(DataColumns.ID) ? js.getLong(DataColumns.ID) : INVALID_ID; + // 如果是新创建的数据或数据 ID 发生变化,则记录数据差异 if (mIsCreate || mDataId != dataId) { mDiffDataValues.put(DataColumns.ID, dataId); } + // 更新数据 ID mDataId = dataId; + // 从 JSON 对象中获取数据 MIME 类型,如果不存在则使用默认笔记类型 String dataMimeType = js.has(DataColumns.MIME_TYPE) ? js.getString(DataColumns.MIME_TYPE) : DataConstants.NOTE; + // 如果是新创建的数据或数据 MIME 类型发生变化,则记录数据差异 if (mIsCreate || !mDataMimeType.equals(dataMimeType)) { mDiffDataValues.put(DataColumns.MIME_TYPE, dataMimeType); } + // 更新数据 MIME 类型 mDataMimeType = dataMimeType; + // 从 JSON 对象中获取数据内容,如果不存在则使用空字符串 String dataContent = js.has(DataColumns.CONTENT) ? js.getString(DataColumns.CONTENT) : ""; + // 如果是新创建的数据或数据内容发生变化,则记录数据差异 if (mIsCreate || !mDataContent.equals(dataContent)) { mDiffDataValues.put(DataColumns.CONTENT, dataContent); } + // 更新数据内容 mDataContent = dataContent; + // 从 JSON 对象中获取数据 DATA1 字段值,如果不存在则使用 0 long dataContentData1 = js.has(DataColumns.DATA1) ? js.getLong(DataColumns.DATA1) : 0; + // 如果是新创建的数据或数据 DATA1 字段值发生变化,则记录数据差异 if (mIsCreate || mDataContentData1 != dataContentData1) { mDiffDataValues.put(DataColumns.DATA1, dataContentData1); } + // 更新数据 DATA1 字段值 mDataContentData1 = dataContentData1; + // 从 JSON 对象中获取数据 DATA3 字段值,如果不存在则使用空字符串 String dataContentData3 = js.has(DataColumns.DATA3) ? js.getString(DataColumns.DATA3) : ""; + // 如果是新创建的数据或数据 DATA3 字段值发生变化,则记录数据差异 if (mIsCreate || !mDataContentData3.equals(dataContentData3)) { mDiffDataValues.put(DataColumns.DATA3, dataContentData3); } + // 更新数据 DATA3 字段值 mDataContentData3 = dataContentData3; } + /** + * 将当前对象的数据内容转换为 JSON 对象。 + * + * @return 包含当前对象数据信息的 JSON 对象,如果是新创建的数据且未提交到数据库则返回 null。 + * @throws JSONException 当 JSON 操作出现异常时抛出。 + */ public JSONObject getContent() throws JSONException { + // 如果是新创建的数据,记录日志并返回 null if (mIsCreate) { Log.e(TAG, "it seems that we haven't created this in database yet"); return null; } + // 创建 JSON 对象 JSONObject js = new JSONObject(); + // 将数据 ID 放入 JSON 对象 js.put(DataColumns.ID, mDataId); + // 将数据 MIME 类型放入 JSON 对象 js.put(DataColumns.MIME_TYPE, mDataMimeType); + // 将数据内容放入 JSON 对象 js.put(DataColumns.CONTENT, mDataContent); + // 将数据 DATA1 字段值放入 JSON 对象 js.put(DataColumns.DATA1, mDataContentData1); + // 将数据 DATA3 字段值放入 JSON 对象 js.put(DataColumns.DATA3, mDataContentData3); return js; } + /** + * 将数据差异提交到数据库,根据数据是否为新创建执行插入或更新操作。 + * + * @param noteId 关联的笔记 ID。 + * @param validateVersion 是否验证版本号。 + * @param version 版本号,用于版本验证。 + */ public void commit(long noteId, boolean validateVersion, long version) { - + // 如果是新创建的数据 if (mIsCreate) { + // 如果数据 ID 为无效 ID 且数据差异中包含数据 ID,则移除该差异 if (mDataId == INVALID_ID && mDiffDataValues.containsKey(DataColumns.ID)) { mDiffDataValues.remove(DataColumns.ID); } - + // 将关联的笔记 ID 放入数据差异中 mDiffDataValues.put(DataColumns.NOTE_ID, noteId); + // 插入数据到数据库,获取插入后的 URI Uri uri = mContentResolver.insert(Notes.CONTENT_DATA_URI, mDiffDataValues); try { + // 从 URI 中解析出数据 ID mDataId = Long.valueOf(uri.getPathSegments().get(1)); } catch (NumberFormatException e) { + // 记录获取笔记 ID 错误的日志,并抛出操作失败异常 Log.e(TAG, "Get note id error :" + e.toString()); throw new ActionFailureException("create note failed"); } } else { + // 如果数据差异不为空 if (mDiffDataValues.size() > 0) { + // 初始化更新结果为 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) }); } + // 如果更新结果为 0,记录日志提示可能用户在同步时更新了笔记 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/src/gtask/data/SqlNote.java b/src/gtask/data/SqlNote.java index 79a4095..3971e44 100644 --- a/src/gtask/data/SqlNote.java +++ b/src/gtask/data/SqlNote.java @@ -14,35 +14,67 @@ * limitations under the License. */ +/** + * 该包包含与 Google Tasks 相关的 SQL 笔记数据处理类。 + */ package net.micode.notes.gtask.data; +// 导入 Android 桌面小部件管理类 import android.appwidget.AppWidgetManager; +// 导入 Android 内容解析器类,用于与内容提供者交互 import android.content.ContentResolver; +// 导入 Android 内容值类,用于存储键值对数据 import android.content.ContentValues; +// 导入 Android 上下文类,提供应用环境信息 import android.content.Context; +// 导入 Android 数据库游标类,用于处理查询结果 import android.database.Cursor; +// 导入 Android URI 类,用于标识内容提供者中的数据 import android.net.Uri; +// 导入 Android 日志工具类,用于记录日志信息 import android.util.Log; +// 导入应用笔记数据相关类 import net.micode.notes.data.Notes; +// 导入应用笔记数据列相关类 import net.micode.notes.data.Notes.DataColumns; +// 导入应用笔记列相关类 import net.micode.notes.data.Notes.NoteColumns; +// 导入应用 Google Tasks 操作失败异常类 import net.micode.notes.gtask.exception.ActionFailureException; +// 导入应用 Google Tasks 字符串工具类 import net.micode.notes.tool.GTaskStringUtils; +// 导入应用资源解析工具类 import net.micode.notes.tool.ResourceParser; +// 导入 JSON 数组类,用于处理 JSON 数组数据 import org.json.JSONArray; +// 导入 JSON 异常处理类,用于处理 JSON 操作异常 import org.json.JSONException; +// 导入 JSON 对象类,用于处理 JSON 对象数据 import org.json.JSONObject; +// 导入 Java 集合框架中的 ArrayList 类 import java.util.ArrayList; - +/** + * SqlNote 类用于处理与 SQL 数据库中笔记数据的交互,包括笔记的创建、加载、更新和提交等操作。 + * 支持从 JSON 对象设置笔记内容,以及将笔记内容转换为 JSON 对象。 + */ public class SqlNote { + /** + * 日志标签,用于在日志记录中标识该类的日志信息。 + */ private static final String TAG = SqlNote.class.getSimpleName(); + /** + * 无效 ID 的常量值,用于表示未初始化或无效的笔记 ID。 + */ private static final int INVALID_ID = -99999; + /** + * 查询笔记时使用的投影列数组,包含笔记的各种属性列。 + */ public static final String[] PROJECTION_NOTE = new String[] { NoteColumns.ID, NoteColumns.ALERTED_DATE, NoteColumns.BG_COLOR_ID, NoteColumns.CREATED_DATE, NoteColumns.HAS_ATTACHMENT, NoteColumns.MODIFIED_DATE, @@ -52,82 +84,193 @@ public class SqlNote { NoteColumns.VERSION }; + /** + * PROJECTION_NOTE 数组中笔记 ID 列的索引。 + */ public static final int ID_COLUMN = 0; + /** + * PROJECTION_NOTE 数组中笔记提醒日期列的索引。 + */ public static final int ALERTED_DATE_COLUMN = 1; + /** + * PROJECTION_NOTE 数组中笔记背景颜色 ID 列的索引。 + */ public static final int BG_COLOR_ID_COLUMN = 2; + /** + * PROJECTION_NOTE 数组中笔记创建日期列的索引。 + */ public static final int CREATED_DATE_COLUMN = 3; + /** + * PROJECTION_NOTE 数组中笔记是否有附件列的索引。 + */ public static final int HAS_ATTACHMENT_COLUMN = 4; + /** + * PROJECTION_NOTE 数组中笔记修改日期列的索引。 + */ public static final int MODIFIED_DATE_COLUMN = 5; + /** + * PROJECTION_NOTE 数组中笔记数量列的索引。 + */ public static final int NOTES_COUNT_COLUMN = 6; + /** + * PROJECTION_NOTE 数组中笔记父 ID 列的索引。 + */ public static final int PARENT_ID_COLUMN = 7; + /** + * PROJECTION_NOTE 数组中笔记摘要列的索引。 + */ public static final int SNIPPET_COLUMN = 8; + /** + * PROJECTION_NOTE 数组中笔记类型列的索引。 + */ public static final int TYPE_COLUMN = 9; + /** + * PROJECTION_NOTE 数组中笔记小部件 ID 列的索引。 + */ public static final int WIDGET_ID_COLUMN = 10; + /** + * PROJECTION_NOTE 数组中笔记小部件类型列的索引。 + */ public static final int WIDGET_TYPE_COLUMN = 11; + /** + * PROJECTION_NOTE 数组中笔记同步 ID 列的索引。 + */ public static final int SYNC_ID_COLUMN = 12; + /** + * PROJECTION_NOTE 数组中笔记本地修改标志列的索引。 + */ public static final int LOCAL_MODIFIED_COLUMN = 13; + /** + * PROJECTION_NOTE 数组中笔记原始父 ID 列的索引。 + */ public static final int ORIGIN_PARENT_ID_COLUMN = 14; + /** + * PROJECTION_NOTE 数组中笔记 Google Tasks ID 列的索引。 + */ public static final int GTASK_ID_COLUMN = 15; + /** + * PROJECTION_NOTE 数组中笔记版本号列的索引。 + */ public static final int VERSION_COLUMN = 16; + /** + * 应用上下文对象,提供对应用资源和系统服务的访问。 + */ private Context mContext; + /** + * 内容解析器,用于与内容提供者进行交互,执行数据库操作。 + */ private ContentResolver mContentResolver; + /** + * 标识笔记是否为新创建的标志。 + */ private boolean mIsCreate; + /** + * 笔记的 ID。 + */ private long mId; + /** + * 笔记的提醒日期。 + */ private long mAlertDate; + /** + * 笔记的背景颜色 ID。 + */ private int mBgColorId; + /** + * 笔记的创建日期。 + */ private long mCreatedDate; + /** + * 笔记是否有附件的标志。 + */ private int mHasAttachment; + /** + * 笔记的修改日期。 + */ private long mModifiedDate; + /** + * 笔记的父 ID。 + */ private long mParentId; + /** + * 笔记的摘要信息。 + */ private String mSnippet; + /** + * 笔记的类型。 + */ private int mType; + /** + * 笔记关联的小部件 ID。 + */ private int mWidgetId; + /** + * 笔记关联的小部件类型。 + */ private int mWidgetType; + /** + * 笔记的原始父 ID。 + */ private long mOriginParent; + /** + * 笔记的版本号。 + */ private long mVersion; + /** + * 存储笔记差异的内容值对象,用于记录需要更新或插入到数据库的笔记数据。 + */ private ContentValues mDiffNoteValues; + /** + * 存储笔记关联数据的列表。 + */ private ArrayList mDataList; + /** + * 构造函数,用于创建一个新的笔记对象。 + * + * @param context 应用程序上下文,用于获取内容解析器和资源。 + */ public SqlNote(Context context) { mContext = context; mContentResolver = context.getContentResolver(); mIsCreate = true; mId = INVALID_ID; mAlertDate = 0; + // 获取默认的背景颜色 ID mBgColorId = ResourceParser.getDefaultBgId(context); mCreatedDate = System.currentTimeMillis(); mHasAttachment = 0; @@ -143,48 +286,78 @@ public class SqlNote { mDataList = new ArrayList(); } + /** + * 构造函数,用于从数据库游标中加载笔记数据创建笔记对象。 + * + * @param context 应用程序上下文,用于获取内容解析器和资源。 + * @param c 数据库游标,包含需要加载的笔记数据。 + */ public SqlNote(Context context, Cursor c) { mContext = context; mContentResolver = context.getContentResolver(); mIsCreate = false; + // 从游标中加载笔记数据 loadFromCursor(c); mDataList = new ArrayList(); + // 如果是笔记类型,则加载关联的数据内容 if (mType == Notes.TYPE_NOTE) loadDataContent(); mDiffNoteValues = new ContentValues(); } + /** + * 构造函数,用于根据笔记 ID 从数据库中加载笔记数据创建笔记对象。 + * + * @param context 应用程序上下文,用于获取内容解析器和资源。 + * @param id 笔记的 ID。 + */ public SqlNote(Context context, long id) { mContext = context; mContentResolver = context.getContentResolver(); mIsCreate = false; + // 根据 ID 从数据库中加载笔记数据 loadFromCursor(id); mDataList = new ArrayList(); + // 如果是笔记类型,则加载关联的数据内容 if (mType == Notes.TYPE_NOTE) loadDataContent(); mDiffNoteValues = new ContentValues(); - } + /** + * 根据笔记 ID 从数据库中查询并加载笔记数据。 + * + * @param id 笔记的 ID。 + */ private void loadFromCursor(long id) { Cursor c = null; try { + // 查询指定 ID 的笔记数据 c = mContentResolver.query(Notes.CONTENT_NOTE_URI, PROJECTION_NOTE, "(_id=?)", new String[] { - String.valueOf(id) + String.valueOf(id) }, null); if (c != null) { + // 移动到查询结果的第一行 c.moveToNext(); + // 从游标中加载笔记数据 loadFromCursor(c); } else { + // 记录游标为空的日志 Log.w(TAG, "loadFromCursor: cursor = null"); } } finally { if (c != null) + // 关闭游标 c.close(); } } + /** + * 从数据库游标中加载笔记数据到当前对象。 + * + * @param c 数据库游标,包含需要加载的笔记数据。 + */ private void loadFromCursor(Cursor c) { mId = c.getLong(ID_COLUMN); mAlertDate = c.getLong(ALERTED_DATE_COLUMN); @@ -200,39 +373,56 @@ public class SqlNote { mVersion = c.getLong(VERSION_COLUMN); } + /** + * 加载笔记关联的数据内容。 + */ private void loadDataContent() { Cursor c = null; + // 清空数据列表 mDataList.clear(); try { + // 查询与当前笔记关联的数据 c = mContentResolver.query(Notes.CONTENT_DATA_URI, SqlData.PROJECTION_DATA, "(note_id=?)", new String[] { - String.valueOf(mId) + String.valueOf(mId) }, null); if (c != null) { if (c.getCount() == 0) { + // 记录笔记没有关联数据的日志 Log.w(TAG, "it seems that the note has not data"); return; } while (c.moveToNext()) { + // 创建 SqlData 对象并添加到数据列表中 SqlData data = new SqlData(mContext, c); mDataList.add(data); } } else { + // 记录游标为空的日志 Log.w(TAG, "loadDataContent: cursor = null"); } } finally { if (c != null) + // 关闭游标 c.close(); } } + /** + * 根据 JSON 对象设置笔记内容,并记录数据差异。 + * + * @param js 包含笔记信息的 JSON 对象。 + * @return 如果设置成功返回 true,否则返回 false。 + */ public boolean setContent(JSONObject js) { try { + // 获取笔记信息的 JSON 对象 JSONObject note = js.getJSONObject(GTaskStringUtils.META_HEAD_NOTE); if (note.getInt(NoteColumns.TYPE) == Notes.TYPE_SYSTEM) { + // 记录不能设置系统文件夹的日志 Log.w(TAG, "cannot set system folder"); } else if (note.getInt(NoteColumns.TYPE) == Notes.TYPE_FOLDER) { - // for folder we can only update the snnipet and type + // 对于文件夹,只能更新摘要和类型 String snippet = note.has(NoteColumns.SNIPPET) ? note .getString(NoteColumns.SNIPPET) : ""; if (mIsCreate || !mSnippet.equals(snippet)) { @@ -247,6 +437,7 @@ public class SqlNote { } mType = type; } else if (note.getInt(NoteColumns.TYPE) == Notes.TYPE_NOTE) { + // 获取笔记关联数据的 JSON 数组 JSONArray dataArray = js.getJSONArray(GTaskStringUtils.META_HEAD_DATA); long id = note.has(NoteColumns.ID) ? note.getLong(NoteColumns.ID) : INVALID_ID; if (mIsCreate || mId != id) { @@ -332,6 +523,7 @@ public class SqlNote { mOriginParent = originParent; for (int i = 0; i < dataArray.length(); i++) { + // 获取每个关联数据的 JSON 对象 JSONObject data = dataArray.getJSONObject(i); SqlData sqlData = null; if (data.has(DataColumns.ID)) { @@ -344,14 +536,17 @@ public class SqlNote { } if (sqlData == null) { + // 如果未找到对应的 SqlData 对象,则创建新的对象并添加到列表中 sqlData = new SqlData(mContext); mDataList.add(sqlData); } + // 设置关联数据的内容 sqlData.setContent(data); } } } catch (JSONException e) { + // 记录 JSON 异常日志并打印堆栈信息 Log.e(TAG, e.toString()); e.printStackTrace(); return false; @@ -359,17 +554,24 @@ public class SqlNote { return true; } + /** + * 将当前笔记对象的内容转换为 JSON 对象。 + * + * @return 包含当前笔记信息的 JSON 对象,如果笔记未创建则返回 null。 + */ public JSONObject getContent() { try { JSONObject js = new JSONObject(); if (mIsCreate) { + // 记录笔记未创建的日志 Log.e(TAG, "it seems that we haven't created this in database yet"); return null; } JSONObject note = new JSONObject(); if (mType == Notes.TYPE_NOTE) { + // 将笔记信息放入 JSON 对象 note.put(NoteColumns.ID, mId); note.put(NoteColumns.ALERTED_DATE, mAlertDate); note.put(NoteColumns.BG_COLOR_ID, mBgColorId); @@ -384,15 +586,18 @@ public class SqlNote { note.put(NoteColumns.ORIGIN_PARENT_ID, mOriginParent); js.put(GTaskStringUtils.META_HEAD_NOTE, note); + // 创建关联数据的 JSON 数组 JSONArray dataArray = new JSONArray(); for (SqlData sqlData : mDataList) { JSONObject data = sqlData.getContent(); if (data != null) { + // 将关联数据的 JSON 对象添加到数组中 dataArray.put(data); } } js.put(GTaskStringUtils.META_HEAD_DATA, dataArray); } else if (mType == Notes.TYPE_FOLDER || mType == Notes.TYPE_SYSTEM) { + // 将文件夹或系统文件夹信息放入 JSON 对象 note.put(NoteColumns.ID, mId); note.put(NoteColumns.TYPE, mType); note.put(NoteColumns.SNIPPET, mSnippet); @@ -401,105 +606,164 @@ public class SqlNote { return js; } catch (JSONException e) { + // 记录 JSON 异常日志并打印堆栈信息 Log.e(TAG, e.toString()); e.printStackTrace(); } return null; } + /** + * 设置笔记的父 ID,并记录数据差异。 + * + * @param id 笔记的父 ID。 + */ public void setParentId(long id) { mParentId = id; mDiffNoteValues.put(NoteColumns.PARENT_ID, id); } + /** + * 设置笔记的 Google Tasks ID,并记录数据差异。 + * + * @param gid 笔记的 Google Tasks ID。 + */ public void setGtaskId(String gid) { mDiffNoteValues.put(NoteColumns.GTASK_ID, gid); } + /** + * 设置笔记的同步 ID,并记录数据差异。 + * + * @param syncId 笔记的同步 ID。 + */ public void setSyncId(long syncId) { mDiffNoteValues.put(NoteColumns.SYNC_ID, syncId); } + /** + * 重置笔记的本地修改标志,并记录数据差异。 + */ public void resetLocalModified() { mDiffNoteValues.put(NoteColumns.LOCAL_MODIFIED, 0); } + /** + * 获取笔记的 ID。 + * + * @return 笔记的 ID。 + */ public long getId() { return mId; } + /** + * 获取笔记的父 ID。 + * + * @return 笔记的父 ID。 + */ public long getParentId() { return mParentId; } + /** + * 获取笔记的摘要信息。 + * + * @return 笔记的摘要信息。 + */ public String getSnippet() { return mSnippet; } + /** + * 判断笔记是否为笔记类型。 + * + * @return 如果是笔记类型返回 true,否则返回 false。 + */ public boolean isNoteType() { return mType == Notes.TYPE_NOTE; } + /** + * 将笔记数据差异提交到数据库,根据笔记是否为新创建执行插入或更新操作。 + * + * @param validateVersion 是否验证版本号。 + */ public void commit(boolean validateVersion) { if (mIsCreate) { if (mId == INVALID_ID && mDiffNoteValues.containsKey(NoteColumns.ID)) { + // 如果笔记 ID 无效且差异中包含 ID,则移除该差异 mDiffNoteValues.remove(NoteColumns.ID); } + // 插入笔记数据到数据库,获取插入后的 URI Uri uri = mContentResolver.insert(Notes.CONTENT_NOTE_URI, mDiffNoteValues); try { + // 从 URI 中解析出笔记 ID mId = Long.valueOf(uri.getPathSegments().get(1)); } catch (NumberFormatException e) { + // 记录获取笔记 ID 错误的日志,并抛出操作失败异常 Log.e(TAG, "Get note id error :" + e.toString()); throw new ActionFailureException("create note failed"); } if (mId == 0) { + // 抛出创建笔记 ID 失败的异常 throw new IllegalStateException("Create thread id failed"); } if (mType == Notes.TYPE_NOTE) { for (SqlData sqlData : mDataList) { + // 提交关联数据到数据库 sqlData.commit(mId, false, -1); } } } else { if (mId <= 0 && mId != Notes.ID_ROOT_FOLDER && mId != Notes.ID_CALL_RECORD_FOLDER) { + // 记录笔记 ID 无效的日志,并抛出异常 Log.e(TAG, "No such note"); throw new IllegalStateException("Try to update note with invalid id"); } if (mDiffNoteValues.size() > 0) { + // 版本号加 1 mVersion ++; int result = 0; if (!validateVersion) { + // 不验证版本号,执行更新操作 result = mContentResolver.update(Notes.CONTENT_NOTE_URI, mDiffNoteValues, "(" + NoteColumns.ID + "=?)", new String[] { - String.valueOf(mId) + String.valueOf(mId) }); } else { + // 验证版本号,执行更新操作 result = mContentResolver.update(Notes.CONTENT_NOTE_URI, mDiffNoteValues, "(" - + NoteColumns.ID + "=?) AND (" + NoteColumns.VERSION + "<=?)", + + NoteColumns.ID + "=?) AND (" + NoteColumns.VERSION + "<=?)", new String[] { String.valueOf(mId), String.valueOf(mVersion) }); } if (result == 0) { + // 记录可能用户在同步时更新笔记的日志 Log.w(TAG, "there is no update. maybe user updates note when syncing"); } } if (mType == Notes.TYPE_NOTE) { for (SqlData sqlData : mDataList) { + // 提交关联数据到数据库 sqlData.commit(mId, validateVersion, mVersion); } } } - // refresh local info + // 刷新本地笔记信息 loadFromCursor(mId); if (mType == Notes.TYPE_NOTE) + // 加载关联的数据内容 loadDataContent(); + // 清空数据差异 mDiffNoteValues.clear(); + // 标记为非新创建的笔记 mIsCreate = false; } } diff --git a/src/gtask/data/Task.java b/src/gtask/data/Task.java index 6a19454..506f642 100644 --- a/src/gtask/data/Task.java +++ b/src/gtask/data/Task.java @@ -14,336 +14,531 @@ * limitations under the License. */ +/** + * 该包包含与 Google Tasks 相关的数据处理类。 + */ package net.micode.notes.gtask.data; +// 导入 Android 数据库游标类,用于处理数据库查询结果 import android.database.Cursor; +// 导入 Android 文本工具类,提供文本处理方法 import android.text.TextUtils; +// 导入 Android 日志工具类,用于记录日志信息 import android.util.Log; +// 导入应用笔记数据相关类 import net.micode.notes.data.Notes; +// 导入应用笔记数据列相关类 import net.micode.notes.data.Notes.DataColumns; +// 导入应用笔记数据常量相关类 import net.micode.notes.data.Notes.DataConstants; +// 导入应用笔记列相关类 import net.micode.notes.data.Notes.NoteColumns; +// 导入应用 Google Tasks 操作失败异常类 import net.micode.notes.gtask.exception.ActionFailureException; +// 导入应用 Google Tasks 字符串工具类 import net.micode.notes.tool.GTaskStringUtils; +// 导入 JSON 数组类,用于处理 JSON 数组数据 import org.json.JSONArray; +// 导入 JSON 异常处理类,用于处理 JSON 操作异常 import org.json.JSONException; +// 导入 JSON 对象类,用于处理 JSON 对象数据 import org.json.JSONObject; - +/** + * Task 类表示一个任务,继承自 Node 类。 + * 该类提供了任务的创建、更新、同步等操作,以及从 JSON 对象设置任务内容和将任务内容转换为 JSON 对象的功能。 + */ public class Task extends Node { + /** + * 日志标签,用于在日志记录中标识该类的日志信息。 + */ private static final String TAG = Task.class.getSimpleName(); + /** + * 任务是否完成的标志。 + */ private boolean mCompleted; + /** + * 任务的备注信息。 + */ private String mNotes; + /** + * 任务的元信息,以 JSON 对象形式存储。 + */ private JSONObject mMetaInfo; + /** + * 任务的前一个兄弟任务。 + */ private Task mPriorSibling; + /** + * 任务所属的任务列表。 + */ private TaskList mParent; + /** + * 构造函数,初始化任务对象。 + */ public Task() { super(); + // 初始化任务为未完成状态 mCompleted = false; + // 初始化备注信息为空 mNotes = null; + // 初始化前一个兄弟任务为空 mPriorSibling = null; + // 初始化所属任务列表为空 mParent = null; + // 初始化元信息为空 mMetaInfo = null; } + /** + * 生成创建任务的 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 + // 设置操作 ID js.put(GTaskStringUtils.GTASK_JSON_ACTION_ID, actionId); - // index + // 设置任务在任务列表中的索引 js.put(GTaskStringUtils.GTASK_JSON_INDEX, mParent.getChildTaskIndex(this)); - // entity_delta + // 创建任务实体的 JSON 对象 JSONObject entity = new JSONObject(); + // 设置任务名称 entity.put(GTaskStringUtils.GTASK_JSON_NAME, getName()); + // 设置任务创建者 ID 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()); } + // 将任务实体添加到操作 JSON 对象中 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 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()); } } catch (JSONException e) { + // 记录 JSON 异常日志 Log.e(TAG, e.toString()); + // 打印异常堆栈信息 e.printStackTrace(); + // 抛出操作失败异常 throw new ActionFailureException("fail to generate task-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 + // 设置操作 ID js.put(GTaskStringUtils.GTASK_JSON_ACTION_ID, actionId); - // id + // 设置任务 ID js.put(GTaskStringUtils.GTASK_JSON_ID, getGid()); - // entity_delta + // 创建任务实体的 JSON 对象 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()); + // 将任务实体添加到操作 JSON 对象中 js.put(GTaskStringUtils.GTASK_JSON_ENTITY_DELTA, entity); } catch (JSONException e) { + // 记录 JSON 异常日志 Log.e(TAG, e.toString()); + // 打印异常堆栈信息 e.printStackTrace(); + // 抛出操作失败异常 throw new ActionFailureException("fail to generate task-update jsonobject"); } return js; } + /** + * 根据远程 JSON 对象设置任务内容。 + * + * @param js 包含远程任务信息的 JSON 对象。 + * @throws ActionFailureException 如果从 JSON 对象获取任务内容失败。 + */ public void setContentByRemoteJSON(JSONObject js) { if (js != null) { try { - // id + // 如果 JSON 对象包含任务 ID,设置任务 ID if (js.has(GTaskStringUtils.GTASK_JSON_ID)) { setGid(js.getString(GTaskStringUtils.GTASK_JSON_ID)); } - // last_modified + // 如果 JSON 对象包含任务最后修改时间,设置任务最后修改时间 if (js.has(GTaskStringUtils.GTASK_JSON_LAST_MODIFIED)) { setLastModified(js.getLong(GTaskStringUtils.GTASK_JSON_LAST_MODIFIED)); } - // name + // 如果 JSON 对象包含任务名称,设置任务名称 if (js.has(GTaskStringUtils.GTASK_JSON_NAME)) { setName(js.getString(GTaskStringUtils.GTASK_JSON_NAME)); } - // notes + // 如果 JSON 对象包含任务备注信息,设置任务备注信息 if (js.has(GTaskStringUtils.GTASK_JSON_NOTES)) { setNotes(js.getString(GTaskStringUtils.GTASK_JSON_NOTES)); } - // deleted + // 如果 JSON 对象包含任务删除标志,设置任务删除标志 if (js.has(GTaskStringUtils.GTASK_JSON_DELETED)) { setDeleted(js.getBoolean(GTaskStringUtils.GTASK_JSON_DELETED)); } - // completed + // 如果 JSON 对象包含任务完成标志,设置任务完成标志 if (js.has(GTaskStringUtils.GTASK_JSON_COMPLETED)) { setCompleted(js.getBoolean(GTaskStringUtils.GTASK_JSON_COMPLETED)); } } catch (JSONException e) { + // 记录 JSON 异常日志 Log.e(TAG, e.toString()); + // 打印异常堆栈信息 e.printStackTrace(); + // 抛出操作失败异常 throw new ActionFailureException("fail to get task content from jsonobject"); } } } + /** + * 根据本地 JSON 对象设置任务内容。 + * + * @param js 包含本地任务信息的 JSON 对象。 + */ public void setContentByLocalJSON(JSONObject js) { + // 检查 JSON 对象是否有效 if (js == null || !js.has(GTaskStringUtils.META_HEAD_NOTE) || !js.has(GTaskStringUtils.META_HEAD_DATA)) { + // 记录无效 JSON 对象日志 Log.w(TAG, "setContentByLocalJSON: nothing is avaiable"); } try { + // 获取任务备注信息的 JSON 对象 JSONObject note = js.getJSONObject(GTaskStringUtils.META_HEAD_NOTE); + // 获取任务关联数据的 JSON 数组 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++) { + // 获取每个关联数据的 JSON 对象 JSONObject data = dataArray.getJSONObject(i); + // 检查关联数据的 MIME 类型是否为笔记类型 if (TextUtils.equals(data.getString(DataColumns.MIME_TYPE), DataConstants.NOTE)) { + // 设置任务名称 setName(data.getString(DataColumns.CONTENT)); break; } } } catch (JSONException e) { + // 记录 JSON 异常日志 Log.e(TAG, e.toString()); + // 打印异常堆栈信息 e.printStackTrace(); } } + /** + * 根据任务内容生成本地 JSON 对象。 + * + * @return 包含本地任务信息的 JSON 对象,如果任务为空则返回 null。 + */ 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(); + // 创建任务备注信息的 JSON 对象 JSONObject note = new JSONObject(); + // 创建任务关联数据的 JSON 数组 JSONArray dataArray = new JSONArray(); + // 创建关联数据的 JSON 对象 JSONObject data = new JSONObject(); + // 设置关联数据的内容为任务名称 data.put(DataColumns.CONTENT, name); + // 将关联数据添加到 JSON 数组中 dataArray.put(data); + // 将关联数据 JSON 数组添加到 JSON 对象中 js.put(GTaskStringUtils.META_HEAD_DATA, dataArray); + // 设置任务备注信息的类型为笔记类型 note.put(NoteColumns.TYPE, Notes.TYPE_NOTE); + // 将任务备注信息添加到 JSON 对象中 js.put(GTaskStringUtils.META_HEAD_NOTE, note); return js; } else { - // synced task + // 同步过的任务 + // 获取任务备注信息的 JSON 对象 JSONObject note = mMetaInfo.getJSONObject(GTaskStringUtils.META_HEAD_NOTE); + // 获取任务关联数据的 JSON 数组 JSONArray dataArray = mMetaInfo.getJSONArray(GTaskStringUtils.META_HEAD_DATA); + // 遍历任务关联数据 for (int i = 0; i < dataArray.length(); i++) { + // 获取每个关联数据的 JSON 对象 JSONObject data = dataArray.getJSONObject(i); + // 检查关联数据的 MIME 类型是否为笔记类型 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) { + // 记录 JSON 异常日志 Log.e(TAG, e.toString()); + // 打印异常堆栈信息 e.printStackTrace(); return null; } } + /** + * 根据元数据设置任务的元信息。 + * + * @param metaData 包含任务元信息的元数据对象。 + */ public void setMetaInfo(MetaData metaData) { + // 检查元数据对象是否有效 if (metaData != null && metaData.getNotes() != null) { try { + // 根据元数据的备注信息创建 JSON 对象 mMetaInfo = new JSONObject(metaData.getNotes()); } catch (JSONException e) { + // 记录 JSON 异常日志 Log.w(TAG, e.toString()); + // 元信息设置为空 mMetaInfo = null; } } } + /** + * 根据数据库游标获取任务的同步操作类型。 + * + * @param c 包含任务信息的数据库游标。 + * @return 任务的同步操作类型。 + */ public int getSyncAction(Cursor c) { try { + // 初始化任务备注信息的 JSON 对象 JSONObject noteInfo = null; + // 检查元信息是否有效 if (mMetaInfo != null && mMetaInfo.has(GTaskStringUtils.META_HEAD_NOTE)) { + // 获取任务备注信息的 JSON 对象 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)) { + // 记录远程任务 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)) { + // 记录任务 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 + // 验证任务的 Google Tasks ID if (!c.getString(SqlNote.GTASK_ID_COLUMN).equals(getGid())) { + // 记录 Google Tasks ID 不匹配的日志 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; } } } catch (Exception e) { + // 记录异常日志 Log.e(TAG, e.toString()); + // 打印异常堆栈信息 e.printStackTrace(); } 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); } + /** + * 设置任务的完成状态。 + * + * @param completed 任务是否完成的标志。 + */ public void setCompleted(boolean completed) { this.mCompleted = completed; } + /** + * 设置任务的备注信息。 + * + * @param notes 任务的备注信息。 + */ public void setNotes(String notes) { this.mNotes = notes; } + /** + * 设置任务的前一个兄弟任务。 + * + * @param priorSibling 任务的前一个兄弟任务。 + */ public void setPriorSibling(Task priorSibling) { this.mPriorSibling = priorSibling; } + /** + * 设置任务所属的任务列表。 + * + * @param parent 任务所属的任务列表。 + */ public void setParent(TaskList parent) { this.mParent = parent; } + /** + * 获取任务的完成状态。 + * + * @return 任务是否完成的标志。 + */ public boolean getCompleted() { return this.mCompleted; } + /** + * 获取任务的备注信息。 + * + * @return 任务的备注信息。 + */ public String getNotes() { return this.mNotes; } + /** + * 获取任务的前一个兄弟任务。 + * + * @return 任务的前一个兄弟任务。 + */ public Task getPriorSibling() { return this.mPriorSibling; } + /** + * 获取任务所属的任务列表。 + * + * @return 任务所属的任务列表。 + */ public TaskList getParent() { return this.mParent; } diff --git a/src/gtask/data/TaskList.java b/src/gtask/data/TaskList.java index 4ea21c5..903cb3b 100644 --- a/src/gtask/data/TaskList.java +++ b/src/gtask/data/TaskList.java @@ -14,265 +14,417 @@ * limitations under the License. */ +/** + * 该包包含与 Google Tasks 相关的数据处理类。 + */ package net.micode.notes.gtask.data; +// 导入 Android 数据库游标类,用于处理数据库查询结果 import android.database.Cursor; +// 导入 Android 日志工具类,用于记录日志信息 import android.util.Log; +// 导入应用笔记数据相关类 import net.micode.notes.data.Notes; +// 导入应用笔记列相关类 import net.micode.notes.data.Notes.NoteColumns; +// 导入应用 Google Tasks 操作失败异常类 import net.micode.notes.gtask.exception.ActionFailureException; +// 导入应用 Google Tasks 字符串工具类 import net.micode.notes.tool.GTaskStringUtils; +// 导入 JSON 异常处理类,用于处理 JSON 操作异常 import org.json.JSONException; +// 导入 JSON 对象类,用于处理 JSON 对象数据 import org.json.JSONObject; +// 导入 Java 集合框架中的 ArrayList 类 import java.util.ArrayList; - +/** + * TaskList 类表示一个任务列表,继承自 Node 类。 + * 该类提供了任务列表的创建、更新、同步等操作,以及从 JSON 对象设置任务列表内容和将任务列表内容转换为 JSON 对象的功能。 + */ public class TaskList extends Node { + /** + * 日志标签,用于在日志记录中标识该类的日志信息。 + */ private static final String TAG = TaskList.class.getSimpleName(); + /** + * 任务列表在其父容器中的索引。 + */ private int mIndex; + /** + * 存储任务列表中所有子任务的列表。 + */ private ArrayList mChildren; + /** + * 构造函数,初始化任务列表对象。 + */ public TaskList() { super(); + // 初始化子任务列表 mChildren = new ArrayList(); + // 初始化任务列表索引为 1 mIndex = 1; } + /** + * 生成创建任务列表的 JSON 对象。 + * + * @param actionId 操作的 ID。 + * @return 包含创建任务列表信息的 JSON 对象。 + * @throws ActionFailureException 如果生成 JSON 对象失败。 + */ public JSONObject getCreateAction(int actionId) { + // 创建一个新的 JSON 对象用于存储创建操作信息 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 + // 创建一个新的 JSON 对象用于存储任务列表实体信息 JSONObject entity = new JSONObject(); + // 设置任务列表的名称 entity.put(GTaskStringUtils.GTASK_JSON_NAME, getName()); + // 设置任务列表的创建者 ID entity.put(GTaskStringUtils.GTASK_JSON_CREATOR_ID, "null"); + // 设置任务列表的实体类型 entity.put(GTaskStringUtils.GTASK_JSON_ENTITY_TYPE, GTaskStringUtils.GTASK_JSON_TYPE_GROUP); + // 将任务列表实体信息添加到操作 JSON 对象中 js.put(GTaskStringUtils.GTASK_JSON_ENTITY_DELTA, entity); } catch (JSONException e) { + // 记录 JSON 异常日志 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) { + // 创建一个新的 JSON 对象用于存储更新操作信息 JSONObject js = new JSONObject(); try { - // action_type + // 设置操作类型为更新任务列表 js.put(GTaskStringUtils.GTASK_JSON_ACTION_TYPE, GTaskStringUtils.GTASK_JSON_ACTION_TYPE_UPDATE); - // action_id + // 设置操作 ID js.put(GTaskStringUtils.GTASK_JSON_ACTION_ID, actionId); - // id + // 设置任务列表的 ID js.put(GTaskStringUtils.GTASK_JSON_ID, getGid()); - // entity_delta + // 创建一个新的 JSON 对象用于存储任务列表实体信息 JSONObject entity = new JSONObject(); + // 设置任务列表的名称 entity.put(GTaskStringUtils.GTASK_JSON_NAME, getName()); + // 设置任务列表是否被删除的标志 entity.put(GTaskStringUtils.GTASK_JSON_DELETED, getDeleted()); + // 将任务列表实体信息添加到操作 JSON 对象中 js.put(GTaskStringUtils.GTASK_JSON_ENTITY_DELTA, entity); } catch (JSONException e) { + // 记录 JSON 异常日志 Log.e(TAG, e.toString()); + // 打印异常堆栈信息 e.printStackTrace(); + // 抛出操作失败异常 throw new ActionFailureException("fail to generate tasklist-update jsonobject"); } return js; } + /** + * 根据远程 JSON 对象设置任务列表内容。 + * + * @param js 包含远程任务列表信息的 JSON 对象。 + * @throws ActionFailureException 如果从 JSON 对象获取任务列表内容失败。 + */ public void setContentByRemoteJSON(JSONObject js) { + // 检查 JSON 对象是否为空 if (js != null) { try { - // id + // 如果 JSON 对象包含任务列表 ID,设置任务列表 ID if (js.has(GTaskStringUtils.GTASK_JSON_ID)) { setGid(js.getString(GTaskStringUtils.GTASK_JSON_ID)); } - // last_modified + // 如果 JSON 对象包含任务列表最后修改时间,设置任务列表最后修改时间 if (js.has(GTaskStringUtils.GTASK_JSON_LAST_MODIFIED)) { setLastModified(js.getLong(GTaskStringUtils.GTASK_JSON_LAST_MODIFIED)); } - // name + // 如果 JSON 对象包含任务列表名称,设置任务列表名称 if (js.has(GTaskStringUtils.GTASK_JSON_NAME)) { setName(js.getString(GTaskStringUtils.GTASK_JSON_NAME)); } } catch (JSONException e) { + // 记录 JSON 异常日志 Log.e(TAG, e.toString()); + // 打印异常堆栈信息 e.printStackTrace(); + // 抛出操作失败异常 throw new ActionFailureException("fail to get tasklist content from jsonobject"); } } } + /** + * 根据本地 JSON 对象设置任务列表内容。 + * + * @param js 包含本地任务列表信息的 JSON 对象。 + */ public void setContentByLocalJSON(JSONObject js) { + // 检查 JSON 对象是否为空或是否包含任务列表信息 if (js == null || !js.has(GTaskStringUtils.META_HEAD_NOTE)) { + // 记录没有可用信息的日志 Log.w(TAG, "setContentByLocalJSON: nothing is avaiable"); } try { + // 获取包含任务列表信息的 JSON 对象 JSONObject folder = js.getJSONObject(GTaskStringUtils.META_HEAD_NOTE); + // 判断任务列表类型是否为文件夹类型 if (folder.getInt(NoteColumns.TYPE) == Notes.TYPE_FOLDER) { + // 获取文件夹的摘要信息 String name = folder.getString(NoteColumns.SNIPPET); + // 设置任务列表名称,添加 MIUI 文件夹前缀 setName(GTaskStringUtils.MIUI_FOLDER_PREFFIX + name); } else if (folder.getInt(NoteColumns.TYPE) == Notes.TYPE_SYSTEM) { + // 判断任务列表是否为根文件夹 if (folder.getLong(NoteColumns.ID) == Notes.ID_ROOT_FOLDER) + // 设置任务列表名称为默认文件夹名称,添加 MIUI 文件夹前缀 setName(GTaskStringUtils.MIUI_FOLDER_PREFFIX + GTaskStringUtils.FOLDER_DEFAULT); + // 判断任务列表是否为通话记录文件夹 else if (folder.getLong(NoteColumns.ID) == Notes.ID_CALL_RECORD_FOLDER) + // 设置任务列表名称为通话记录文件夹名称,添加 MIUI 文件夹前缀 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) { + // 记录 JSON 异常日志 Log.e(TAG, e.toString()); + // 打印异常堆栈信息 e.printStackTrace(); } } + /** + * 根据任务列表内容生成本地 JSON 对象。 + * + * @return 包含本地任务列表信息的 JSON 对象,如果生成失败则返回 null。 + */ public JSONObject getLocalJSONFromContent() { try { + // 创建一个新的 JSON 对象用于存储本地任务列表信息 JSONObject js = new JSONObject(); + // 创建一个新的 JSON 对象用于存储文件夹信息 JSONObject folder = new JSONObject(); + // 获取任务列表名称 String folderName = getName(); + // 判断任务列表名称是否以 MIUI 文件夹前缀开头 if (getName().startsWith(GTaskStringUtils.MIUI_FOLDER_PREFFIX)) - folderName = folderName.substring(GTaskStringUtils.MIUI_FOLDER_PREFFIX.length(), - folderName.length()); + // 截取去掉 MIUI 文件夹前缀后的名称 + folderName = folderName.substring(GTaskStringUtils.MIUI_FOLDER_PREFFIX.length()); + // 将文件夹摘要信息添加到文件夹 JSON 对象中 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); + // 将文件夹信息添加到本地任务列表 JSON 对象中 js.put(GTaskStringUtils.META_HEAD_NOTE, folder); return js; } catch (JSONException e) { + // 记录 JSON 异常日志 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) { - // 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 + // 验证任务列表的 Google Tasks ID if (!c.getString(SqlNote.GTASK_ID_COLUMN).equals(getGid())) { + // 记录 Google Tasks ID 不匹配的日志 Log.e(TAG, "gtask id doesn't match"); return SYNC_ACTION_ERROR; } if (c.getLong(SqlNote.SYNC_ID_COLUMN) == getLastModified()) { - // local modification only + // 只有本地修改 return SYNC_ACTION_UPDATE_REMOTE; } else { - // for folder conflicts, just apply local modification + // 对于文件夹冲突,仅应用本地修改 return SYNC_ACTION_UPDATE_REMOTE; } } } catch (Exception e) { + // 记录异常日志 Log.e(TAG, e.toString()); + // 打印异常堆栈信息 e.printStackTrace(); } return SYNC_ACTION_ERROR; } + /** + * 获取任务列表中的子任务数量。 + * + * @return 子任务的数量。 + */ public int getChildTaskCount() { return mChildren.size(); } + /** + * 向任务列表中添加一个子任务。 + * + * @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.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"); return false; } + // 获取任务在任务列表中的位置 int pos = mChildren.indexOf(task); + // 检查任务是否为空且任务列表中不包含该任务 if (task != null && pos == -1) { + // 在指定位置添加任务 mChildren.add(index, task); - // update the task list + // 更新任务列表中的任务关系 Task preTask = null; Task afterTask = null; if (index != 0) + // 获取任务的前一个兄弟任务 preTask = mChildren.get(index - 1); if (index != mChildren.size() - 1) + // 获取任务的后一个兄弟任务 afterTask = mChildren.get(index + 1); + // 设置任务的前一个兄弟任务 task.setPriorSibling(preTask); if (afterTask != null) + // 设置后一个兄弟任务的前一个兄弟任务为当前任务 afterTask.setPriorSibling(task); } return true; } + /** + * 从任务列表中移除一个子任务。 + * + * @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) { - // 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,27 +433,48 @@ 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; } + // 获取任务在任务列表中的位置 int pos = mChildren.indexOf(task); if (pos == -1) { + // 记录任务不在列表中的日志 Log.e(TAG, "move child task: the task should in the list"); return false; } + // 如果任务已经在指定位置,直接返回 true if (pos == index) return true; + // 先移除任务,再在指定位置添加任务 return (removeChildTask(task) && addChildTask(task, index)); } + /** + * 根据任务的 Google Tasks ID 在任务列表中查找子任务。 + * + * @param gid 要查找的任务的 Google Tasks ID。 + * @return 如果找到则返回该任务,否则返回 null。 + */ public Task findChildTaskByGid(String gid) { + // 遍历任务列表 for (int i = 0; i < mChildren.size(); i++) { + // 获取当前任务 Task t = mChildren.get(i); + // 判断任务的 Google Tasks ID 是否匹配 if (t.getGid().equals(gid)) { return t; } @@ -309,34 +482,71 @@ 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"); return null; } return mChildren.get(index); } + /** + * 根据任务的 Google Tasks ID 获取任务列表中的子任务。 + * + * @param gid 要查找的任务的 Google Tasks ID。 + * @return 如果找到则返回该任务,否则返回 null。 + */ public Task getChilTaskByGid(String gid) { + // 增强 for 循环遍历任务列表 for (Task task : mChildren) { + // 判断任务的 Google Tasks ID 是否匹配 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; } diff --git a/src/gtask/remote/GTaskASyncTask.java b/src/gtask/remote/GTaskASyncTask.java index b3b61e7..97123d0 100644 --- a/src/gtask/remote/GTaskASyncTask.java +++ b/src/gtask/remote/GTaskASyncTask.java @@ -77,9 +77,9 @@ public class GTaskASyncTask extends AsyncTask { pendingIntent = PendingIntent.getActivity(mContext, 0, new Intent(mContext, NotesListActivity.class), 0); } - notification.setLatestEventInfo(mContext, mContext.getString(R.string.app_name), content, - pendingIntent); - mNotifiManager.notify(GTASK_SYNC_NOTIFICATION_ID, notification); +// notification.setLatestEventInfo(mContext, mContext.getString(R.string.app_name), content, +// pendingIntent); +// mNotifiManager.notify(GTASK_SYNC_NOTIFICATION_ID, notification); } @Override