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