diff --git a/src/Notes-master/res/values/styles.xml b/src/Notes-master/res/values/styles.xml index d750e65..c6e2581 100644 --- a/src/Notes-master/res/values/styles.xml +++ b/src/Notes-master/res/values/styles.xml @@ -63,7 +63,7 @@ \ No newline at end of file diff --git a/src/Notes-master/src/net/micode/notes/data/Contact.java b/src/Notes-master/src/net/micode/notes/data/Contact.java index d97ac5d..c5fa8b4 100644 --- a/src/Notes-master/src/net/micode/notes/data/Contact.java +++ b/src/Notes-master/src/net/micode/notes/data/Contact.java @@ -14,8 +14,10 @@ * limitations under the License. */ +// 定义联系人相关操作的类,位于小米便签的数据模块 package net.micode.notes.data; +// 导入所需的Android和Java库 import android.content.Context; import android.database.Cursor; import android.provider.ContactsContract.CommonDataKinds.Phone; @@ -25,28 +27,45 @@ import android.util.Log; import java.util.HashMap; +// 联系人工具类,用于通过电话号码获取联系人信息 public class Contact { + // 缓存联系人信息的HashMap,提高查询效率 private static HashMap sContactCache; + + // 日志标签 private static final String TAG = "Contact"; + // 查询联系人信息的SQL选择语句模板 + // 用于匹配电话号码和获取联系人姓名 private static final String CALLER_ID_SELECTION = "PHONE_NUMBERS_EQUAL(" + Phone.NUMBER - + ",?) AND " + Data.MIMETYPE + "='" + Phone.CONTENT_ITEM_TYPE + "'" - + " AND " + Data.RAW_CONTACT_ID + " IN " + + ",?) AND " + Data.MIMETYPE + "='" + Phone.CONTENT_ITEM_TYPE + "'" + + " AND " + Data.RAW_CONTACT_ID + " IN " + "(SELECT raw_contact_id " + " FROM phone_lookup" + " WHERE min_match = '+')"; + /** + * 根据电话号码获取联系人姓名 + * @param context Android上下文对象 + * @param phoneNumber 要查询的电话号码 + * @return 联系人姓名,如果找不到则返回null + */ public static String getContact(Context context, String phoneNumber) { + // 如果缓存未初始化,则初始化缓存 if(sContactCache == null) { sContactCache = new HashMap(); } + // 先检查缓存中是否已有该号码对应的联系人 if(sContactCache.containsKey(phoneNumber)) { return sContactCache.get(phoneNumber); } + // 准备查询语句,替换最小匹配参数 String selection = CALLER_ID_SELECTION.replace("+", PhoneNumberUtils.toCallerIDMinMatch(phoneNumber)); + + // 查询联系人数据库 Cursor cursor = context.getContentResolver().query( Data.CONTENT_URI, new String [] { Phone.DISPLAY_NAME }, @@ -54,20 +73,26 @@ public class Contact { new String[] { phoneNumber }, null); + // 处理查询结果 if (cursor != null && cursor.moveToFirst()) { try { + // 获取联系人姓名 String name = cursor.getString(0); + // 将结果存入缓存 sContactCache.put(phoneNumber, name); return name; } catch (IndexOutOfBoundsException e) { + // 处理数据越界异常 Log.e(TAG, " Cursor get string error " + e.toString()); return null; } finally { + // 确保关闭Cursor释放资源 cursor.close(); } } else { + // 没有找到匹配的联系人 Log.d(TAG, "No contact matched with number:" + phoneNumber); return null; } } -} +} \ No newline at end of file diff --git a/src/Notes-master/src/net/micode/notes/data/Notes.java b/src/Notes-master/src/net/micode/notes/data/Notes.java index f240604..f5bbec0 100644 --- a/src/Notes-master/src/net/micode/notes/data/Notes.java +++ b/src/Notes-master/src/net/micode/notes/data/Notes.java @@ -14,266 +14,277 @@ * limitations under the License. */ +// 定义便签应用的核心常量、URI和数据结构 package net.micode.notes.data; +// 导入Android Uri类 import android.net.Uri; + +// 便签应用的核心常量类,包含数据类型、URI和列定义 public class Notes { + // ContentProvider的授权标识符 public static final String AUTHORITY = "micode_notes"; + + // 日志标签 public static final String TAG = "Notes"; - public static final int TYPE_NOTE = 0; - public static final int TYPE_FOLDER = 1; - public static final int TYPE_SYSTEM = 2; + + // 便签类型常量 + public static final int TYPE_NOTE = 0; // 普通便签类型 + public static final int TYPE_FOLDER = 1; // 文件夹类型 + public static final int TYPE_SYSTEM = 2; // 系统文件夹类型 /** - * Following IDs are system folders' identifiers - * {@link Notes#ID_ROOT_FOLDER } is default folder - * {@link Notes#ID_TEMPARAY_FOLDER } is for notes belonging no folder - * {@link Notes#ID_CALL_RECORD_FOLDER} is to store call records + * 系统文件夹的标识符常量 + * {@link Notes#ID_ROOT_FOLDER } 是默认根文件夹 + * {@link Notes#ID_TEMPARAY_FOLDER } 用于存放无文件夹的便签 + * {@link Notes#ID_CALL_RECORD_FOLDER} 用于存储通话记录 */ - public static final int ID_ROOT_FOLDER = 0; - public static final int ID_TEMPARAY_FOLDER = -1; - public static final int ID_CALL_RECORD_FOLDER = -2; - public static final int ID_TRASH_FOLER = -3; - - public static final String INTENT_EXTRA_ALERT_DATE = "net.micode.notes.alert_date"; - public static final String INTENT_EXTRA_BACKGROUND_ID = "net.micode.notes.background_color_id"; - public static final String INTENT_EXTRA_WIDGET_ID = "net.micode.notes.widget_id"; - public static final String INTENT_EXTRA_WIDGET_TYPE = "net.micode.notes.widget_type"; - public static final String INTENT_EXTRA_FOLDER_ID = "net.micode.notes.folder_id"; - public static final String INTENT_EXTRA_CALL_DATE = "net.micode.notes.call_date"; - - public static final int TYPE_WIDGET_INVALIDE = -1; - public static final int TYPE_WIDGET_2X = 0; - public static final int TYPE_WIDGET_4X = 1; - + public static final int ID_ROOT_FOLDER = 0; // 根文件夹ID + public static final int ID_TEMPARAY_FOLDER = -1; // 临时文件夹ID + public static final int ID_CALL_RECORD_FOLDER = -2; // 通话记录文件夹ID + public static final int ID_TRASH_FOLER = -3; // 回收站文件夹ID + + // Intent附加数据键名常量 + public static final String INTENT_EXTRA_ALERT_DATE = "net.micode.notes.alert_date"; // 提醒日期 + public static final String INTENT_EXTRA_BACKGROUND_ID = "net.micode.notes.background_color_id"; // 背景色ID + public static final String INTENT_EXTRA_WIDGET_ID = "net.micode.notes.widget_id"; // 小部件ID + public static final String INTENT_EXTRA_WIDGET_TYPE = "net.micode.notes.widget_type"; // 小部件类型 + public static final String INTENT_EXTRA_FOLDER_ID = "net.micode.notes.folder_id"; // 文件夹ID + public static final String INTENT_EXTRA_CALL_DATE = "net.micode.notes.call_date"; // 通话日期 + + // 小部件类型常量 + public static final int TYPE_WIDGET_INVALIDE = -1; // 无效小部件类型 + public static final int TYPE_WIDGET_2X = 0; // 2x大小的小部件 + public static final int TYPE_WIDGET_4X = 1; // 4x大小的小部件 + + // 数据类型的常量定义 public static class DataConstants { - public static final String NOTE = TextNote.CONTENT_ITEM_TYPE; - public static final String CALL_NOTE = CallNote.CONTENT_ITEM_TYPE; + public static final String NOTE = TextNote.CONTENT_ITEM_TYPE; // 文本便签类型 + public static final String CALL_NOTE = CallNote.CONTENT_ITEM_TYPE; // 通话记录便签类型 } /** - * Uri to query all notes and folders + * 查询所有便签和文件夹的URI */ public static final Uri CONTENT_NOTE_URI = Uri.parse("content://" + AUTHORITY + "/note"); /** - * Uri to query data + * 查询便签数据的URI */ public static final Uri CONTENT_DATA_URI = Uri.parse("content://" + AUTHORITY + "/data"); + // 便签表的列定义接口 public interface NoteColumns { /** - * The unique ID for a row - *

Type: INTEGER (long)

+ * 行的唯一ID + *

类型: INTEGER (long)

*/ public static final String ID = "_id"; /** - * The parent's id for note or folder - *

Type: INTEGER (long)

+ * 便签或文件夹的父ID + *

类型: INTEGER (long)

*/ public static final String PARENT_ID = "parent_id"; /** - * Created data for note or folder - *

Type: INTEGER (long)

+ * 便签或文件夹的创建日期 + *

类型: INTEGER (long)

*/ public static final String CREATED_DATE = "created_date"; /** - * Latest modified date - *

Type: INTEGER (long)

+ * 最后修改日期 + *

类型: INTEGER (long)

*/ public static final String MODIFIED_DATE = "modified_date"; - /** - * Alert date - *

Type: INTEGER (long)

+ * 提醒日期 + *

类型: INTEGER (long)

*/ public static final String ALERTED_DATE = "alert_date"; /** - * Folder's name or text content of note - *

Type: TEXT

+ * 文件夹名称或便签的文本内容摘要 + *

类型: TEXT

*/ public static final String SNIPPET = "snippet"; /** - * Note's widget id - *

Type: INTEGER (long)

+ * 便签关联的小部件ID + *

类型: INTEGER (long)

*/ public static final String WIDGET_ID = "widget_id"; /** - * Note's widget type - *

Type: INTEGER (long)

+ * 便签关联的小部件类型 + *

类型: INTEGER (long)

*/ public static final String WIDGET_TYPE = "widget_type"; /** - * Note's background color's id - *

Type: INTEGER (long)

+ * 便签背景颜色的ID + *

类型: INTEGER (long)

*/ public static final String BG_COLOR_ID = "bg_color_id"; /** - * For text note, it doesn't has attachment, for multi-media - * note, it has at least one attachment - *

Type: INTEGER

+ * 是否有附件:文本便签没有附件,多媒体便签至少有一个附件 + *

类型: INTEGER

*/ public static final String HAS_ATTACHMENT = "has_attachment"; /** - * Folder's count of notes - *

Type: INTEGER (long)

+ * 文件夹中的便签数量 + *

类型: INTEGER (long)

*/ public static final String NOTES_COUNT = "notes_count"; /** - * The file type: folder or note - *

Type: INTEGER

+ * 文件类型:文件夹或便签 + *

类型: INTEGER

*/ public static final String TYPE = "type"; /** - * The last sync id - *

Type: INTEGER (long)

+ * 最后同步ID + *

类型: INTEGER (long)

*/ public static final String SYNC_ID = "sync_id"; /** - * Sign to indicate local modified or not - *

Type: INTEGER

+ * 本地修改标志 + *

类型: INTEGER

*/ public static final String LOCAL_MODIFIED = "local_modified"; /** - * Original parent id before moving into temporary folder - *

Type : INTEGER

+ * 移动到临时文件夹前的原始父ID + *

类型 : INTEGER

*/ public static final String ORIGIN_PARENT_ID = "origin_parent_id"; /** - * The gtask id - *

Type : TEXT

+ * Google Task ID + *

类型 : TEXT

*/ public static final String GTASK_ID = "gtask_id"; /** - * The version code - *

Type : INTEGER (long)

+ * 版本号 + *

类型 : INTEGER (long)

*/ public static final String VERSION = "version"; } + // 数据表的列定义接口 public interface DataColumns { /** - * The unique ID for a row - *

Type: INTEGER (long)

+ * 行的唯一ID + *

类型: INTEGER (long)

*/ public static final String ID = "_id"; /** - * The MIME type of the item represented by this row. - *

Type: Text

+ * 该行数据项的MIME类型 + *

类型: Text

*/ public static final String MIME_TYPE = "mime_type"; /** - * The reference id to note that this data belongs to - *

Type: INTEGER (long)

+ * 该数据所属便签的引用ID + *

类型: INTEGER (long)

*/ public static final String NOTE_ID = "note_id"; /** - * Created data for note or folder - *

Type: INTEGER (long)

+ * 数据项的创建日期 + *

类型: INTEGER (long)

*/ public static final String CREATED_DATE = "created_date"; /** - * Latest modified date - *

Type: INTEGER (long)

+ * 最后修改日期 + *

类型: INTEGER (long)

*/ public static final String MODIFIED_DATE = "modified_date"; /** - * Data's content - *

Type: TEXT

+ * 数据内容 + *

类型: TEXT

*/ public static final String CONTENT = "content"; - /** - * Generic data column, the meaning is {@link #MIMETYPE} specific, used for - * integer data type - *

Type: INTEGER

+ * 通用数据列1,具体含义由{@link #MIMETYPE}决定,用于整数类型 + *

类型: INTEGER

*/ public static final String DATA1 = "data1"; /** - * Generic data column, the meaning is {@link #MIMETYPE} specific, used for - * integer data type - *

Type: INTEGER

+ * 通用数据列2,具体含义由{@link #MIMETYPE}决定,用于整数类型 + *

类型: INTEGER

*/ public static final String DATA2 = "data2"; /** - * Generic data column, the meaning is {@link #MIMETYPE} specific, used for - * TEXT data type - *

Type: TEXT

+ * 通用数据列3,具体含义由{@link #MIMETYPE}决定,用于文本类型 + *

类型: TEXT

*/ public static final String DATA3 = "data3"; /** - * Generic data column, the meaning is {@link #MIMETYPE} specific, used for - * TEXT data type - *

Type: TEXT

+ * 通用数据列4,具体含义由{@link #MIMETYPE}决定,用于文本类型 + *

类型: TEXT

*/ public static final String DATA4 = "data4"; /** - * Generic data column, the meaning is {@link #MIMETYPE} specific, used for - * TEXT data type - *

Type: TEXT

+ * 通用数据列5,具体含义由{@link #MIMETYPE}决定,用于文本类型 + *

类型: TEXT

*/ public static final String DATA5 = "data5"; } + // 文本便签的数据列定义 public static final class TextNote implements DataColumns { /** - * Mode to indicate the text in check list mode or not - *

Type: Integer 1:check list mode 0: normal mode

+ * 模式标志:是否为清单模式 + *

类型: Integer 1:清单模式 0:普通模式

*/ public static final String MODE = DATA1; + // 清单模式常量 public static final int MODE_CHECK_LIST = 1; - public static final String CONTENT_TYPE = "vnd.android.cursor.dir/text_note"; - - public static final String CONTENT_ITEM_TYPE = "vnd.android.cursor.item/text_note"; + // 文本便签的MIME类型 + public static final String CONTENT_TYPE = "vnd.android.cursor.dir/text_note"; // 多项目类型 + public static final String CONTENT_ITEM_TYPE = "vnd.android.cursor.item/text_note"; // 单项目类型 + // 文本便签的URI public static final Uri CONTENT_URI = Uri.parse("content://" + AUTHORITY + "/text_note"); } + // 通话记录便签的数据列定义 public static final class CallNote implements DataColumns { /** - * Call date for this record - *

Type: INTEGER (long)

+ * 通话记录日期 + *

类型: INTEGER (long)

*/ public static final String CALL_DATE = DATA1; /** - * Phone number for this record - *

Type: TEXT

+ * 电话号码 + *

类型: TEXT

*/ public static final String PHONE_NUMBER = DATA3; - public static final String CONTENT_TYPE = "vnd.android.cursor.dir/call_note"; - - public static final String CONTENT_ITEM_TYPE = "vnd.android.cursor.item/call_note"; + // 通话记录便签的MIME类型 + public static final String CONTENT_TYPE = "vnd.android.cursor.dir/call_note"; // 多项目类型 + public static final String CONTENT_ITEM_TYPE = "vnd.android.cursor.item/call_note"; // 单项目类型 + // 通话记录便签的URI public static final Uri CONTENT_URI = Uri.parse("content://" + AUTHORITY + "/call_note"); } -} +} \ No newline at end of file diff --git a/src/Notes-master/src/net/micode/notes/gtask/data/MetaData.java b/src/Notes-master/src/net/micode/notes/gtask/data/MetaData.java index 3a2050b..f78d05b 100644 --- a/src/Notes-master/src/net/micode/notes/gtask/data/MetaData.java +++ b/src/Notes-master/src/net/micode/notes/gtask/data/MetaData.java @@ -14,69 +14,116 @@ * limitations under the License. */ +// 元数据类,用于处理与Google Task相关的元数据信息 package net.micode.notes.gtask.data; +// 导入所需的Android和Java类 import android.database.Cursor; import android.util.Log; +// 导入工具类 import net.micode.notes.tool.GTaskStringUtils; +// 导入JSON处理类 import org.json.JSONException; import org.json.JSONObject; - +// 元数据类,继承自Task基类 public class MetaData extends Task { + // 日志标签,使用类名作为标签 private final static String TAG = MetaData.class.getSimpleName(); + // 关联的Google Task ID private String mRelatedGid = null; + /** + * 设置元数据信息 + * @param gid 关联的Google Task ID + * @param metaInfo 包含元数据的JSON对象 + */ public void setMeta(String gid, JSONObject metaInfo) { try { + // 将Google Task ID添加到元数据中 metaInfo.put(GTaskStringUtils.META_HEAD_GTASK_ID, gid); } catch (JSONException e) { + // 记录添加关联ID失败的错误 Log.e(TAG, "failed to put related gid"); } + // 将JSON对象转换为字符串并设置为备注 setNotes(metaInfo.toString()); + // 设置元数据的名称为固定值 setName(GTaskStringUtils.META_NOTE_NAME); } + /** + * 获取关联的Google Task ID + * @return 关联的Google Task ID + */ public String getRelatedGid() { return mRelatedGid; } + /** + * 检查是否需要保存 + * @return 如果备注不为null则返回true,表示需要保存 + */ @Override public boolean isWorthSaving() { return getNotes() != null; } + /** + * 从远程JSON数据设置内容 + * @param js 包含远程数据的JSON对象 + */ @Override public void setContentByRemoteJSON(JSONObject js) { + // 调用父类方法设置基本内容 super.setContentByRemoteJSON(js); + // 如果备注不为空,尝试解析关联的Google Task ID if (getNotes() != null) { try { + // 从备注字符串创建JSON对象 JSONObject metaInfo = new JSONObject(getNotes().trim()); + // 获取关联的Google Task ID mRelatedGid = metaInfo.getString(GTaskStringUtils.META_HEAD_GTASK_ID); } catch (JSONException e) { + // 记录获取关联ID失败的警告 Log.w(TAG, "failed to get related gid"); + // 将关联ID设为null mRelatedGid = null; } } } + /** + * 从本地JSON数据设置内容(不应被调用) + * @param js 包含本地数据的JSON对象 + */ @Override public void setContentByLocalJSON(JSONObject js) { - // this function should not be called + // 抛出错误,表示该方法不应被调用 throw new IllegalAccessError("MetaData:setContentByLocalJSON should not be called"); } + /** + * 从内容获取本地JSON数据(不应被调用) + * @return 不应被调用,总是抛出异常 + */ @Override public JSONObject getLocalJSONFromContent() { + // 抛出错误,表示该方法不应被调用 throw new IllegalAccessError("MetaData:getLocalJSONFromContent should not be called"); } + /** + * 获取同步动作(不应被调用) + * @param c 数据库游标 + * @return 不应被调用,总是抛出异常 + */ @Override public int getSyncAction(Cursor c) { + // 抛出错误,表示该方法不应被调用 throw new IllegalAccessError("MetaData:getSyncAction should not be called"); } - -} +} \ No newline at end of file diff --git a/src/Notes-master/src/net/micode/notes/gtask/data/Node.java b/src/Notes-master/src/net/micode/notes/gtask/data/Node.java index 63950e0..b1b27fd 100644 --- a/src/Notes-master/src/net/micode/notes/gtask/data/Node.java +++ b/src/Notes-master/src/net/micode/notes/gtask/data/Node.java @@ -20,82 +20,162 @@ import android.database.Cursor; import org.json.JSONObject; +/** + * 表示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服务中的唯一标识符 private String mGid; + // 节点名称/标题 private String mName; + // 最后修改时间戳 private long mLastModified; + // 标记节点是否已删除 private boolean mDeleted; + /** + * 默认构造函数,初始化节点属性 + */ public Node() { - mGid = null; - mName = ""; - mLastModified = 0; - mDeleted = false; + mGid = null; // 初始化为空,等待从服务器获取或本地生成 + mName = ""; // 默认空名称 + mLastModified = 0; // 默认时间戳为0 + 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 同步操作类型常量 + */ public abstract int getSyncAction(Cursor c); + // 以下是节点属性的访问器和修改器方法 + + /** + * 设置节点的Google Tasks标识符 + * @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 true表示已删除,false表示未删除 + */ public void setDeleted(boolean deleted) { this.mDeleted = deleted; } + /** + * 获取节点的Google Tasks标识符 + * @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 true表示已删除,false表示未删除 + */ public boolean getDeleted() { return this.mDeleted; } - -} +} \ No newline at end of file diff --git a/src/Notes-master/src/net/micode/notes/gtask/data/SqlData.java b/src/Notes-master/src/net/micode/notes/gtask/data/SqlData.java index d3ec3be..8e3c7c6 100644 --- a/src/Notes-master/src/net/micode/notes/gtask/data/SqlData.java +++ b/src/Notes-master/src/net/micode/notes/gtask/data/SqlData.java @@ -34,61 +34,72 @@ import net.micode.notes.gtask.exception.ActionFailureException; import org.json.JSONException; import org.json.JSONObject; - +/** + * 处理与本地SQLite数据库中笔记数据的交互 + * 提供从数据库加载数据、更新数据和提交更改的功能 + */ public class SqlData { private static final String TAG = SqlData.class.getSimpleName(); + // 无效ID值,用于标识尚未从数据库获取ID的新数据 private static final int INVALID_ID = -99999; + // 查询数据时使用的投影列 public static final String[] PROJECTION_DATA = new String[] { - DataColumns.ID, DataColumns.MIME_TYPE, DataColumns.CONTENT, DataColumns.DATA1, - DataColumns.DATA3 + DataColumns.ID, // 数据ID + DataColumns.MIME_TYPE, // 数据MIME类型 + DataColumns.CONTENT, // 数据内容 + DataColumns.DATA1, // 额外数据字段1 + DataColumns.DATA3 // 额外数据字段3 }; + // 投影列的索引位置 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; - + 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; // 存储与原始数据不同的值,用于更新 + + /** + * 构造函数,用于创建新的SQL数据对象 + * @param context 应用上下文 + */ public SqlData(Context context) { mContentResolver = context.getContentResolver(); - mIsCreate = true; - mDataId = INVALID_ID; - mDataMimeType = DataConstants.NOTE; - mDataContent = ""; - mDataContentData1 = 0; - mDataContentData3 = ""; - mDiffDataValues = new ContentValues(); + mIsCreate = true; // 标记为新创建的数据 + mDataId = INVALID_ID; // 初始化为无效ID + mDataMimeType = DataConstants.NOTE; // 默认MIME类型为笔记 + mDataContent = ""; // 空内容 + mDataContentData1 = 0; // 初始化数据字段1 + mDataContentData3 = ""; // 初始化数据字段3 + mDiffDataValues = new ContentValues(); // 初始化差异值容器 } + /** + * 构造函数,用于从数据库游标加载现有数据 + * @param context 应用上下文 + * @param c 包含数据的数据库游标 + */ public SqlData(Context context, Cursor c) { mContentResolver = context.getContentResolver(); - mIsCreate = false; - loadFromCursor(c); - mDiffDataValues = new ContentValues(); + mIsCreate = false; // 标记为现有数据 + loadFromCursor(c); // 从游标加载数据 + mDiffDataValues = new ContentValues(); // 初始化差异值容器 } + /** + * 从数据库游标加载数据 + * @param c 包含数据的游标 + */ private void loadFromCursor(Cursor c) { mDataId = c.getLong(DATA_ID_COLUMN); mDataMimeType = c.getString(DATA_MIME_TYPE_COLUMN); @@ -97,13 +108,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 { + // 从JSON中获取ID并设置 long dataId = js.has(DataColumns.ID) ? js.getLong(DataColumns.ID) : INVALID_ID; if (mIsCreate || mDataId != dataId) { mDiffDataValues.put(DataColumns.ID, dataId); } mDataId = dataId; + // 从JSON中获取MIME类型并设置 String dataMimeType = js.has(DataColumns.MIME_TYPE) ? js.getString(DataColumns.MIME_TYPE) : DataConstants.NOTE; if (mIsCreate || !mDataMimeType.equals(dataMimeType)) { @@ -111,18 +129,21 @@ public class SqlData { } 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中获取数据字段1并设置 long dataContentData1 = js.has(DataColumns.DATA1) ? js.getLong(DataColumns.DATA1) : 0; if (mIsCreate || mDataContentData1 != dataContentData1) { mDiffDataValues.put(DataColumns.DATA1, dataContentData1); } mDataContentData1 = dataContentData1; + // 从JSON中获取数据字段3并设置 String dataContentData3 = js.has(DataColumns.DATA3) ? js.getString(DataColumns.DATA3) : ""; if (mIsCreate || !mDataContentData3.equals(dataContentData3)) { mDiffDataValues.put(DataColumns.DATA3, dataContentData3); @@ -130,9 +151,14 @@ 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"); + Log.e(TAG, "数据尚未提交到数据库,无法获取内容"); return null; } JSONObject js = new JSONObject(); @@ -144,46 +170,59 @@ 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.remove(DataColumns.ID); // 移除无效ID } - mDiffDataValues.put(DataColumns.NOTE_ID, noteId); + mDiffDataValues.put(DataColumns.NOTE_ID, noteId); // 设置关联笔记ID Uri uri = mContentResolver.insert(Notes.CONTENT_DATA_URI, mDiffDataValues); try { - mDataId = Long.valueOf(uri.getPathSegments().get(1)); + mDataId = Long.valueOf(uri.getPathSegments().get(1)); // 获取新生成的ID } catch (NumberFormatException e) { - Log.e(TAG, "Get note id error :" + e.toString()); - throw new ActionFailureException("create note failed"); + Log.e(TAG, "获取笔记ID错误: " + e.toString()); + throw new ActionFailureException("创建笔记失败"); } } 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) }); } if (result == 0) { - Log.w(TAG, "there is no update. maybe user updates note when syncing"); + Log.w(TAG, "没有数据更新,可能用户在同步时修改了笔记"); } } } - mDiffDataValues.clear(); - mIsCreate = false; + mDiffDataValues.clear(); // 清空差异值 + mIsCreate = false; // 标记为已创建 } + /** + * 获取数据ID + * @return 数据ID + */ public long getId() { return mDataId; } -} +} \ No newline at end of file diff --git a/src/Notes-master/src/net/micode/notes/gtask/data/SqlNote.java b/src/Notes-master/src/net/micode/notes/gtask/data/SqlNote.java index 79a4095..ffc529b 100644 --- a/src/Notes-master/src/net/micode/notes/gtask/data/SqlNote.java +++ b/src/Notes-master/src/net/micode/notes/gtask/data/SqlNote.java @@ -14,8 +14,10 @@ * limitations under the License. */ +// 数据库笔记实体类,负责处理便签数据的本地存储和同步操作 package net.micode.notes.gtask.data; +// Android基础组件导入 import android.appwidget.AppWidgetManager; import android.content.ContentResolver; import android.content.ContentValues; @@ -24,6 +26,7 @@ import android.database.Cursor; import android.net.Uri; import android.util.Log; +// 项目内部类导入 import net.micode.notes.data.Notes; import net.micode.notes.data.Notes.DataColumns; import net.micode.notes.data.Notes.NoteColumns; @@ -31,18 +34,25 @@ import net.micode.notes.gtask.exception.ActionFailureException; import net.micode.notes.tool.GTaskStringUtils; import net.micode.notes.tool.ResourceParser; +// JSON处理类导入 import org.json.JSONArray; import org.json.JSONException; import org.json.JSONObject; +// Java工具类导入 import java.util.ArrayList; - +/** + * SQLite数据库笔记操作类,封装便签的CRUD操作和Google Task同步功能 + */ public class SqlNote { + // 日志标签 private static final String TAG = SqlNote.class.getSimpleName(); + // 无效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,80 +62,92 @@ public class SqlNote { NoteColumns.VERSION }; + // 字段列索引常量定义 public static final int ID_COLUMN = 0; - public static final int ALERTED_DATE_COLUMN = 1; - public static final int BG_COLOR_ID_COLUMN = 2; - public static final int CREATED_DATE_COLUMN = 3; - public static final int HAS_ATTACHMENT_COLUMN = 4; - public static final int MODIFIED_DATE_COLUMN = 5; - public static final int NOTES_COUNT_COLUMN = 6; - public static final int PARENT_ID_COLUMN = 7; - public static final int SNIPPET_COLUMN = 8; - public static final int TYPE_COLUMN = 9; - public static final int WIDGET_ID_COLUMN = 10; - public static final int WIDGET_TYPE_COLUMN = 11; - public static final int SYNC_ID_COLUMN = 12; - public static final int LOCAL_MODIFIED_COLUMN = 13; - public static final int ORIGIN_PARENT_ID_COLUMN = 14; - public static final int GTASK_ID_COLUMN = 15; - 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; mBgColorId = ResourceParser.getDefaultBgId(context); @@ -139,40 +161,65 @@ public class SqlNote { mWidgetType = Notes.TYPE_WIDGET_INVALIDE; mOriginParent = 0; mVersion = 0; + + // 初始化差异值和数据列表 mDiffNoteValues = new ContentValues(); 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(); + 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) - }, null); + new String[] { String.valueOf(id) }, null); + if (c != null) { c.moveToNext(); loadFromCursor(c); @@ -185,7 +232,12 @@ public class SqlNote { } } + /** + * 从游标加载笔记数据 + * @param c 数据库游标 + */ private void loadFromCursor(Cursor c) { + // 从游标读取各字段值 mId = c.getLong(ID_COLUMN); mAlertDate = c.getLong(ALERTED_DATE_COLUMN); mBgColorId = c.getInt(BG_COLOR_ID_COLUMN); @@ -200,19 +252,24 @@ 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) - }, null); + "(note_id=?)", new String[] { 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 data = new SqlData(mContext, c); mDataList.add(data); @@ -226,85 +283,93 @@ public class SqlNote { } } + /** + * 从JSON对象设置笔记内容 + * @param js JSON对象 + * @return 是否设置成功 + */ public boolean setContent(JSONObject js) { try { + // 获取笔记节点 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) : ""; + } + // 处理普通文件夹 + else if (note.getInt(NoteColumns.TYPE) == Notes.TYPE_FOLDER) { + // 更新摘要和类型 + String snippet = note.has(NoteColumns.SNIPPET) ? note.getString(NoteColumns.SNIPPET) : ""; if (mIsCreate || !mSnippet.equals(snippet)) { mDiffNoteValues.put(NoteColumns.SNIPPET, snippet); } mSnippet = snippet; - int type = note.has(NoteColumns.TYPE) ? note.getInt(NoteColumns.TYPE) - : Notes.TYPE_NOTE; + int type = note.has(NoteColumns.TYPE) ? note.getInt(NoteColumns.TYPE) : Notes.TYPE_NOTE; if (mIsCreate || mType != type) { mDiffNoteValues.put(NoteColumns.TYPE, type); } mType = type; - } else if (note.getInt(NoteColumns.TYPE) == Notes.TYPE_NOTE) { + } + // 处理普通笔记 + else if (note.getInt(NoteColumns.TYPE) == Notes.TYPE_NOTE) { + // 获取数据数组 JSONArray dataArray = js.getJSONArray(GTaskStringUtils.META_HEAD_DATA); + + // 更新各字段值 long id = note.has(NoteColumns.ID) ? note.getLong(NoteColumns.ID) : INVALID_ID; if (mIsCreate || mId != id) { mDiffNoteValues.put(NoteColumns.ID, id); } mId = id; - long alertDate = note.has(NoteColumns.ALERTED_DATE) ? note - .getLong(NoteColumns.ALERTED_DATE) : 0; + long alertDate = note.has(NoteColumns.ALERTED_DATE) ? note.getLong(NoteColumns.ALERTED_DATE) : 0; if (mIsCreate || mAlertDate != alertDate) { mDiffNoteValues.put(NoteColumns.ALERTED_DATE, alertDate); } mAlertDate = alertDate; - int bgColorId = note.has(NoteColumns.BG_COLOR_ID) ? note - .getInt(NoteColumns.BG_COLOR_ID) : ResourceParser.getDefaultBgId(mContext); + int bgColorId = note.has(NoteColumns.BG_COLOR_ID) ? note.getInt(NoteColumns.BG_COLOR_ID) + : ResourceParser.getDefaultBgId(mContext); if (mIsCreate || mBgColorId != bgColorId) { mDiffNoteValues.put(NoteColumns.BG_COLOR_ID, bgColorId); } mBgColorId = bgColorId; - long createDate = note.has(NoteColumns.CREATED_DATE) ? note - .getLong(NoteColumns.CREATED_DATE) : System.currentTimeMillis(); + long createDate = note.has(NoteColumns.CREATED_DATE) ? note.getLong(NoteColumns.CREATED_DATE) + : System.currentTimeMillis(); if (mIsCreate || mCreatedDate != createDate) { mDiffNoteValues.put(NoteColumns.CREATED_DATE, createDate); } mCreatedDate = createDate; - int hasAttachment = note.has(NoteColumns.HAS_ATTACHMENT) ? note - .getInt(NoteColumns.HAS_ATTACHMENT) : 0; + int hasAttachment = note.has(NoteColumns.HAS_ATTACHMENT) ? note.getInt(NoteColumns.HAS_ATTACHMENT) : 0; if (mIsCreate || mHasAttachment != hasAttachment) { mDiffNoteValues.put(NoteColumns.HAS_ATTACHMENT, hasAttachment); } mHasAttachment = hasAttachment; - long modifiedDate = note.has(NoteColumns.MODIFIED_DATE) ? note - .getLong(NoteColumns.MODIFIED_DATE) : System.currentTimeMillis(); + long modifiedDate = note.has(NoteColumns.MODIFIED_DATE) ? note.getLong(NoteColumns.MODIFIED_DATE) + : System.currentTimeMillis(); if (mIsCreate || mModifiedDate != modifiedDate) { mDiffNoteValues.put(NoteColumns.MODIFIED_DATE, modifiedDate); } mModifiedDate = modifiedDate; - long parentId = note.has(NoteColumns.PARENT_ID) ? note - .getLong(NoteColumns.PARENT_ID) : 0; + long parentId = note.has(NoteColumns.PARENT_ID) ? note.getLong(NoteColumns.PARENT_ID) : 0; if (mIsCreate || mParentId != parentId) { mDiffNoteValues.put(NoteColumns.PARENT_ID, parentId); } mParentId = parentId; - String snippet = note.has(NoteColumns.SNIPPET) ? note - .getString(NoteColumns.SNIPPET) : ""; + String snippet = note.has(NoteColumns.SNIPPET) ? note.getString(NoteColumns.SNIPPET) : ""; if (mIsCreate || !mSnippet.equals(snippet)) { mDiffNoteValues.put(NoteColumns.SNIPPET, snippet); } mSnippet = snippet; - int type = note.has(NoteColumns.TYPE) ? note.getInt(NoteColumns.TYPE) - : Notes.TYPE_NOTE; + int type = note.has(NoteColumns.TYPE) ? note.getInt(NoteColumns.TYPE) : Notes.TYPE_NOTE; if (mIsCreate || mType != type) { mDiffNoteValues.put(NoteColumns.TYPE, type); } @@ -317,23 +382,25 @@ public class SqlNote { } mWidgetId = widgetId; - int widgetType = note.has(NoteColumns.WIDGET_TYPE) ? note - .getInt(NoteColumns.WIDGET_TYPE) : Notes.TYPE_WIDGET_INVALIDE; + int widgetType = note.has(NoteColumns.WIDGET_TYPE) ? note.getInt(NoteColumns.WIDGET_TYPE) + : Notes.TYPE_WIDGET_INVALIDE; if (mIsCreate || mWidgetType != widgetType) { mDiffNoteValues.put(NoteColumns.WIDGET_TYPE, widgetType); } mWidgetType = widgetType; - long originParent = note.has(NoteColumns.ORIGIN_PARENT_ID) ? note - .getLong(NoteColumns.ORIGIN_PARENT_ID) : 0; + long originParent = note.has(NoteColumns.ORIGIN_PARENT_ID) ? note.getLong(NoteColumns.ORIGIN_PARENT_ID) : 0; if (mIsCreate || mOriginParent != originParent) { mDiffNoteValues.put(NoteColumns.ORIGIN_PARENT_ID, originParent); } mOriginParent = originParent; + // 处理关联数据 for (int i = 0; i < dataArray.length(); i++) { JSONObject data = dataArray.getJSONObject(i); SqlData sqlData = null; + + // 查找已有数据 if (data.has(DataColumns.ID)) { long dataId = data.getLong(DataColumns.ID); for (SqlData temp : mDataList) { @@ -343,11 +410,13 @@ public class SqlNote { } } + // 创建新数据对象 if (sqlData == null) { sqlData = new SqlData(mContext); mDataList.add(sqlData); } + // 设置数据内容 sqlData.setContent(data); } } @@ -359,17 +428,25 @@ public class SqlNote { return true; } + /** + * 获取笔记内容的JSON表示 + * @return JSON对象 + */ 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) { + // 添加笔记字段 note.put(NoteColumns.ID, mId); note.put(NoteColumns.ALERTED_DATE, mAlertDate); note.put(NoteColumns.BG_COLOR_ID, mBgColorId); @@ -384,6 +461,7 @@ public class SqlNote { note.put(NoteColumns.ORIGIN_PARENT_ID, mOriginParent); js.put(GTaskStringUtils.META_HEAD_NOTE, note); + // 添加关联数据 JSONArray dataArray = new JSONArray(); for (SqlData sqlData : mDataList) { JSONObject data = sqlData.getContent(); @@ -392,7 +470,9 @@ public class SqlNote { } } js.put(GTaskStringUtils.META_HEAD_DATA, dataArray); - } else if (mType == Notes.TYPE_FOLDER || mType == Notes.TYPE_SYSTEM) { + } + // 处理文件夹和系统文件夹 + else if (mType == Notes.TYPE_FOLDER || mType == Notes.TYPE_SYSTEM) { note.put(NoteColumns.ID, mId); note.put(NoteColumns.TYPE, mType); note.put(NoteColumns.SNIPPET, mSnippet); @@ -407,45 +487,83 @@ public class SqlNote { return null; } + /** + * 设置父节点ID + * @param id 父节点ID + */ public void setParentId(long id) { mParentId = id; mDiffNoteValues.put(NoteColumns.PARENT_ID, id); } + /** + * 设置Google Task ID + * @param gid Google Task 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 是否为笔记类型 + */ public boolean isNoteType() { return mType == Notes.TYPE_NOTE; } + /** + * 提交更改到数据库 + * @param validateVersion 是否验证版本号 + */ public void commit(boolean validateVersion) { + // 处理新建笔记 if (mIsCreate) { + // 处理无效ID if (mId == INVALID_ID && mDiffNoteValues.containsKey(NoteColumns.ID)) { mDiffNoteValues.remove(NoteColumns.ID); } + // 插入新笔记 Uri uri = mContentResolver.insert(Notes.CONTENT_NOTE_URI, mDiffNoteValues); try { mId = Long.valueOf(uri.getPathSegments().get(1)); @@ -453,40 +571,50 @@ public class SqlNote { Log.e(TAG, "Get note id error :" + e.toString()); throw new ActionFailureException("create note failed"); } + + // 检查ID是否有效 if (mId == 0) { throw new IllegalStateException("Create thread id failed"); } + // 提交关联数据 if (mType == Notes.TYPE_NOTE) { for (SqlData sqlData : mDataList) { sqlData.commit(mId, false, -1); } } - } else { + } + // 处理已有笔记更新 + else { + // 检查ID有效性 if (mId <= 0 && mId != Notes.ID_ROOT_FOLDER && mId != Notes.ID_CALL_RECORD_FOLDER) { Log.e(TAG, "No such note"); throw new IllegalStateException("Try to update note with invalid id"); } + + // 如果有差异值需要更新 if (mDiffNoteValues.size() > 0) { mVersion ++; int result = 0; + + // 根据是否验证版本选择更新方式 if (!validateVersion) { - result = mContentResolver.update(Notes.CONTENT_NOTE_URI, mDiffNoteValues, "(" - + NoteColumns.ID + "=?)", new String[] { - String.valueOf(mId) - }); + result = mContentResolver.update(Notes.CONTENT_NOTE_URI, mDiffNoteValues, + "(" + NoteColumns.ID + "=?)", + new String[] { String.valueOf(mId) }); } else { - result = mContentResolver.update(Notes.CONTENT_NOTE_URI, mDiffNoteValues, "(" - + NoteColumns.ID + "=?) AND (" + NoteColumns.VERSION + "<=?)", - new String[] { - String.valueOf(mId), String.valueOf(mVersion) - }); + result = mContentResolver.update(Notes.CONTENT_NOTE_URI, mDiffNoteValues, + "(" + 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); @@ -494,12 +622,13 @@ public class SqlNote { } } - // refresh local info + // 刷新本地数据 loadFromCursor(mId); if (mType == Notes.TYPE_NOTE) loadDataContent(); + // 重置差异值和创建标志 mDiffNoteValues.clear(); mIsCreate = false; } -} +} \ No newline at end of file diff --git a/src/Notes-master/src/net/micode/notes/gtask/data/Task.java b/src/Notes-master/src/net/micode/notes/gtask/data/Task.java index 6a19454..cb35d4d 100644 --- a/src/Notes-master/src/net/micode/notes/gtask/data/Task.java +++ b/src/Notes-master/src/net/micode/notes/gtask/data/Task.java @@ -2,16 +2,13 @@ * Copyright (c) 2010-2011, The MiCode Open Source Community (www.micode.net) * * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at + * 您可以在遵守 License 的前提下使用本文件。 + * 您可以从以下地址获取 License 副本: * * http://www.apache.org/licenses/LICENSE-2.0 * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. + * 除非适用法律要求或书面同意,本软件根据 License 分发时“按现状”提供, + * 不附带任何明示或暗示的保证或条件。请参阅 License 了解具体的权限和限制。 */ package net.micode.notes.gtask.data; @@ -31,44 +28,48 @@ import org.json.JSONArray; import org.json.JSONException; import org.json.JSONObject; - +/** + * 表示Google Tasks中的任务实体 + * 负责与远程任务数据和本地笔记数据的转换和同步 + */ 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(); - mCompleted = false; - mNotes = null; - mPriorSibling = null; - mParent = null; - mMetaInfo = null; + mCompleted = false; // 默认未完成 + mNotes = null; // 默认无备注 + mPriorSibling = null; // 默认无前兄弟任务 + mParent = null; // 默认无父任务列表 + 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 +80,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 +102,26 @@ 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 js.put(GTaskStringUtils.GTASK_JSON_ACTION_ID, actionId); - // id + // 设置要更新的任务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 +139,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 +183,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 +194,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 +219,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 +245,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,6 +268,10 @@ public class Task extends Node { } } + /** + * 设置任务的元信息(从本地存储的元数据中解析) + * @param metaData 包含元信息的对象 + */ public void setMetaInfo(MetaData metaData) { if (metaData != null && metaData.getNotes() != null) { try { @@ -258,48 +283,57 @@ public class Task extends Node { } } + /** + * 根据本地数据库记录和任务状态确定同步动作类型 + * @param c 包含本地笔记数据的游标 + * @return 同步动作类型(见常量定义) + */ 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; + return SYNC_ACTION_UPDATE_REMOTE; // 元信息丢失,需要更新远程 } if (!noteInfo.has(NoteColumns.ID)) { Log.w(TAG, "remote note id seems to be deleted"); - return SYNC_ACTION_UPDATE_LOCAL; + return SYNC_ACTION_UPDATE_LOCAL; // 缺少笔记ID,需要更新本地 } - // 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; + return SYNC_ACTION_UPDATE_LOCAL; // ID不匹配,需要更新本地 } + // 判断本地是否有修改 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; + return SYNC_ACTION_ERROR; // GTask ID不匹配,同步错误 } if (c.getLong(SqlNote.SYNC_ID_COLUMN) == getLastModified()) { - // local modification only + // 只有本地修改,更新远程 return SYNC_ACTION_UPDATE_REMOTE; } else { + // 本地和远程都有修改,发生冲突 return SYNC_ACTION_UPDATE_CONFLICT; } } @@ -308,14 +342,21 @@ public class Task extends Node { e.printStackTrace(); } - return SYNC_ACTION_ERROR; + 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); } + // 以下为属性的setter和getter方法 + public void setCompleted(boolean completed) { this.mCompleted = completed; } @@ -348,4 +389,4 @@ public class Task extends Node { return this.mParent; } -} +} \ No newline at end of file diff --git a/src/Notes-master/src/net/micode/notes/gtask/data/TaskList.java b/src/Notes-master/src/net/micode/notes/gtask/data/TaskList.java index 4ea21c5..d204618 100644 --- a/src/Notes-master/src/net/micode/notes/gtask/data/TaskList.java +++ b/src/Notes-master/src/net/micode/notes/gtask/data/TaskList.java @@ -2,16 +2,13 @@ * Copyright (c) 2010-2011, The MiCode Open Source Community (www.micode.net) * * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at + * 您可以在遵守 License 的前提下使用本文件。 + * 您可以从以下地址获取 License 副本: * * http://www.apache.org/licenses/LICENSE-2.0 * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. + * 除非适用法律要求或书面同意,本软件根据 License 分发时“按现状”提供, + * 不附带任何明示或暗示的保证或条件。请参阅 License 了解具体的权限和限制。 */ package net.micode.notes.gtask.data; @@ -29,35 +26,42 @@ import org.json.JSONObject; import java.util.ArrayList; - +/** + * 表示Google Tasks中的任务列表(相当于文件夹) + * 负责与远程任务列表数据和本地笔记数据的转换和同步 + */ 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(); - mChildren = new ArrayList(); - mIndex = 1; + mChildren = new ArrayList(); // 初始化子任务列表 + mIndex = 1; // 默认索引为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 +78,26 @@ 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 js.put(GTaskStringUtils.GTASK_JSON_ACTION_ID, actionId); - // id + // 设置要更新的任务列表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 +112,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,23 +142,30 @@ 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"); } 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); + setName(GTaskStringUtils.MIUI_FOLDER_PREFFIX + name); // 添加前缀标识为MIUI文件夹 } 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,16 +177,24 @@ 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()); + folderName = folderName.substring(GTaskStringUtils.MIUI_FOLDER_PREFFIX.length()); + + // 设置文件夹摘要(名称) folder.put(NoteColumns.SNIPPET, folderName); + + // 根据名称判断文件夹类型(系统文件夹或普通文件夹) if (folderName.equals(GTaskStringUtils.FOLDER_DEFAULT) || folderName.equals(GTaskStringUtils.FOLDER_CALL_NOTE)) folder.put(NoteColumns.TYPE, Notes.TYPE_SYSTEM); @@ -183,28 +211,35 @@ public class TaskList extends Node { } } + /** + * 根据本地数据库记录和任务列表状态确定同步动作类型 + * @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 + // 本地有修改 + // 验证GTask ID是否匹配 if (!c.getString(SqlNote.GTASK_ID_COLUMN).equals(getGid())) { Log.e(TAG, "gtask id doesn't match"); - return SYNC_ACTION_ERROR; + return SYNC_ACTION_ERROR; // GTask ID不匹配,同步错误 } 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; } } @@ -213,27 +248,43 @@ public class TaskList extends Node { e.printStackTrace(); } - return SYNC_ACTION_ERROR; + 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 +295,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 +311,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 +323,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 +337,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 +357,16 @@ public class TaskList extends Node { if (pos == index) return true; + + // 通过先移除再添加的方式实现移动 return (removeChildTask(task) && addChildTask(task, index)); } + /** + * 根据GTask ID查找子任务 + * @param gid GTask ID + * @return 找到的任务,未找到返回null + */ public Task findChildTaskByGid(String gid) { for (int i = 0; i < mChildren.size(); i++) { Task t = mChildren.get(i); @@ -309,10 +377,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 +399,11 @@ public class TaskList extends Node { return mChildren.get(index); } + /** + * 根据GTask ID获取子任务(与findChildTaskByGid功能相同) + * @param gid GTask ID + * @return 任务对象,未找到返回null + */ public Task getChilTaskByGid(String gid) { for (Task task : mChildren) { if (task.getGid().equals(gid)) @@ -329,15 +412,29 @@ public class TaskList extends Node { return null; } + /** + * 获取所有子任务列表 + * @return 子任务列表 + */ public ArrayList getChildTaskList() { return this.mChildren; } + // 以下为属性的setter和getter方法 + + /** + * 设置任务列表的排序索引 + * @param index 排序索引 + */ public void setIndex(int index) { this.mIndex = index; } + /** + * 获取任务列表的排序索引 + * @return 排序索引 + */ public int getIndex() { return this.mIndex; } -} +} \ No newline at end of file diff --git a/src/Notes-master/src/net/micode/notes/gtask/exception/ActionFailureException.java b/src/Notes-master/src/net/micode/notes/gtask/exception/ActionFailureException.java index 15504be..66a94cf 100644 --- a/src/Notes-master/src/net/micode/notes/gtask/exception/ActionFailureException.java +++ b/src/Notes-master/src/net/micode/notes/gtask/exception/ActionFailureException.java @@ -2,32 +2,45 @@ * Copyright (c) 2010-2011, The MiCode Open Source Community (www.micode.net) * * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at + * 您可以在遵守 License 的前提下使用本文件。 + * 您可以从以下地址获取 License 副本: * * http://www.apache.org/licenses/LICENSE-2.0 * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. + * 除非适用法律要求或书面同意,本软件根据 License 分发时“按现状”提供, + * 不附带任何明示或暗示的保证或条件。请参阅 License 了解具体的权限和限制。 */ package net.micode.notes.gtask.exception; +/** + * 操作失败异常类 + * 用于表示任务同步、数据操作等过程中发生的不可恢复错误 + */ public class ActionFailureException extends RuntimeException { - private static final long serialVersionUID = 4425249765923293627L; + private static final long serialVersionUID = 4425249765923293627L; // 序列化版本号 + /** + * 无参构造函数 + */ public ActionFailureException() { super(); } + /** + * 带错误消息的构造函数 + * @param paramString 错误消息字符串 + */ public ActionFailureException(String paramString) { super(paramString); } + /** + * 带错误消息和原因的构造函数 + * @param paramString 错误消息字符串 + * @param paramThrowable 导致异常的根本原因 + */ public ActionFailureException(String paramString, Throwable paramThrowable) { super(paramString, paramThrowable); } -} +} \ No newline at end of file diff --git a/src/Notes-master/src/net/micode/notes/gtask/exception/NetworkFailureException.java b/src/Notes-master/src/net/micode/notes/gtask/exception/NetworkFailureException.java index b08cfb1..9d7cd2c 100644 --- a/src/Notes-master/src/net/micode/notes/gtask/exception/NetworkFailureException.java +++ b/src/Notes-master/src/net/micode/notes/gtask/exception/NetworkFailureException.java @@ -2,32 +2,46 @@ * Copyright (c) 2010-2011, The MiCode Open Source Community (www.micode.net) * * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at + * 您可以在遵守 License 的前提下使用本文件。 + * 您可以从以下地址获取 License 副本: * * http://www.apache.org/licenses/LICENSE-2.0 * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. + * 除非适用法律要求或书面同意,本软件根据 License 分发时“按现状”提供, + * 不附带任何明示或暗示的保证或条件。请参阅 License 了解具体的权限和限制。 */ package net.micode.notes.gtask.exception; +/** + * 网络失败异常类 + * 用于表示网络连接相关的异常(如超时、连接中断等) + */ public class NetworkFailureException extends Exception { - private static final long serialVersionUID = 2107610287180234136L; + private static final long serialVersionUID = 2107610287180234136L; // 序列化版本号,用于保持类版本兼容性 + /** + * 无参构造函数 + * 创建一个默认的网络失败异常对象 + */ public NetworkFailureException() { super(); } + /** + * 带错误消息的构造函数 + * @param paramString 描述异常原因的消息字符串 + */ public NetworkFailureException(String paramString) { super(paramString); } + /** + * 带错误消息和根源异常的构造函数 + * @param paramString 描述异常原因的消息字符串 + * @param paramThrowable 导致当前异常的根本异常对象 + */ public NetworkFailureException(String paramString, Throwable paramThrowable) { super(paramString, paramThrowable); } -} +} \ No newline at end of file diff --git a/src/Notes-master/src/net/micode/notes/gtask/remote/GTaskASyncTask.java b/src/Notes-master/src/net/micode/notes/gtask/remote/GTaskASyncTask.java index b3b61e7..3d3edae 100644 --- a/src/Notes-master/src/net/micode/notes/gtask/remote/GTaskASyncTask.java +++ b/src/Notes-master/src/net/micode/notes/gtask/remote/GTaskASyncTask.java @@ -1,18 +1,14 @@ - /* * Copyright (c) 2010-2011, The MiCode Open Source Community (www.micode.net) * * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at + * 您可以在遵守 License 的前提下使用本文件。 + * 您可以从以下地址获取 License 副本: * * http://www.apache.org/licenses/LICENSE-2.0 * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. + * 除非适用法律要求或书面同意,本软件根据 License 分发时“按现状”提供, + * 不附带任何明示或暗示的保证或条件。请参阅 License 了解具体的权限和限制。 */ package net.micode.notes.gtask.remote; @@ -28,96 +24,154 @@ import net.micode.notes.R; import net.micode.notes.ui.NotesListActivity; import net.micode.notes.ui.NotesPreferenceActivity; - +/** + * 处理Google Tasks同步的异步任务类 + * 负责在后台执行同步操作,并通过通知和回调机制反馈状态 + */ public class GTaskASyncTask extends AsyncTask { + // 同步通知的唯一标识ID private static int GTASK_SYNC_NOTIFICATION_ID = 5234235; + // 完成回调接口,用于通知同步结束 public interface OnCompleteListener { - void onComplete(); + void onComplete(); // 同步完成时的回调方法 } - private Context mContext; - - private NotificationManager mNotifiManager; - - private GTaskManager mTaskManager; - - private OnCompleteListener mOnCompleteListener; + private Context mContext; // 应用上下文 + private NotificationManager mNotifiManager; // 通知管理器 + private GTaskManager mTaskManager; // 任务管理实例 + private OnCompleteListener mOnCompleteListener; // 完成回调监听器 + /** + * 构造函数 + * @param context 应用上下文 + * @param listener 同步完成回调监听器 + */ public GTaskASyncTask(Context context, OnCompleteListener listener) { mContext = context; mOnCompleteListener = listener; - mNotifiManager = (NotificationManager) mContext - .getSystemService(Context.NOTIFICATION_SERVICE); + // 获取系统通知服务 + mNotifiManager = (NotificationManager) mContext.getSystemService(Context.NOTIFICATION_SERVICE); + // 获取任务管理单例 mTaskManager = GTaskManager.getInstance(); } + /** + * 取消同步操作 + */ public void cancelSync() { - mTaskManager.cancelSync(); + mTaskManager.cancelSync(); // 调用任务管理器的取消同步方法 } + /** + * 发布同步进度消息(支持字符串数组参数) + * @param message 进度消息内容 + */ public void publishProgess(String message) { - publishProgress(new String[] { - message - }); + publishProgress(new String[]{message}); // 调用父类方法发布进度 } + /** + * 显示同步状态通知 + * @param tickerId 通知标题的资源ID + * @param content 通知内容文本 + */ private void showNotification(int tickerId, String content) { - Notification notification = new Notification(R.drawable.notification, mContext - .getString(tickerId), System.currentTimeMillis()); - notification.defaults = Notification.DEFAULT_LIGHTS; - notification.flags = Notification.FLAG_AUTO_CANCEL; PendingIntent pendingIntent; + // 根据通知类型设置不同的点击跳转目标 if (tickerId != R.string.ticker_success) { - pendingIntent = PendingIntent.getActivity(mContext, 0, new Intent(mContext, - NotesPreferenceActivity.class), 0); - + // 非成功状态跳转到设置界面 + pendingIntent = PendingIntent.getActivity(mContext, 0, + new Intent(mContext, NotesPreferenceActivity.class), + PendingIntent.FLAG_IMMUTABLE); } else { - pendingIntent = PendingIntent.getActivity(mContext, 0, new Intent(mContext, - NotesListActivity.class), 0); + // 成功状态跳转到笔记列表界面 + pendingIntent = PendingIntent.getActivity(mContext, 0, + new Intent(mContext, NotesListActivity.class), + PendingIntent.FLAG_IMMUTABLE); } - notification.setLatestEventInfo(mContext, mContext.getString(R.string.app_name), content, - pendingIntent); + // 构建通知对象 + Notification.Builder builder = new Notification.Builder(mContext) + .setAutoCancel(true) // 点击后自动清除通知 + .setContentTitle(mContext.getString(R.string.app_name)) // 应用名称作为标题 + .setContentText(content) // 通知内容 + .setContentIntent(pendingIntent) // 点击通知后的跳转意图 + .setWhen(System.currentTimeMillis()) // 通知时间 + .setOngoing(true); // 显示为进行中状态(不可滑动清除) + Notification notification = builder.getNotification(); + // 显示通知 mNotifiManager.notify(GTASK_SYNC_NOTIFICATION_ID, notification); } + /** + * 后台执行同步操作的核心方法 + * @param unused 未使用的参数(继承自AsyncTask) + * @return 同步结果状态码(来自GTaskManager的定义) + */ @Override protected Integer doInBackground(Void... unused) { - publishProgess(mContext.getString(R.string.sync_progress_login, NotesPreferenceActivity - .getSyncAccountName(mContext))); + // 发布登录进度消息(包含当前同步账户名) + publishProgess(mContext.getString(R.string.sync_progress_login, + NotesPreferenceActivity.getSyncAccountName(mContext))); + // 调用任务管理器的同步方法,传入上下文和当前任务实例 return mTaskManager.sync(mContext, this); } + /** + * 处理进度更新的回调方法(在主线程执行) + * @param progress 包含进度消息的字符串数组 + */ @Override protected void onProgressUpdate(String... progress) { + // 显示同步中通知 showNotification(R.string.ticker_syncing, progress[0]); + // 如果上下文是同步服务,发送广播通知进度 if (mContext instanceof GTaskSyncService) { ((GTaskSyncService) mContext).sendBroadcast(progress[0]); } } + /** + * 处理后台任务完成的回调方法(在主线程执行) + * @param result 同步结果状态码 + */ @Override protected void onPostExecute(Integer result) { - if (result == GTaskManager.STATE_SUCCESS) { - showNotification(R.string.ticker_success, mContext.getString( - R.string.success_sync_account, mTaskManager.getSyncAccount())); - NotesPreferenceActivity.setLastSyncTime(mContext, System.currentTimeMillis()); - } else if (result == GTaskManager.STATE_NETWORK_ERROR) { - showNotification(R.string.ticker_fail, mContext.getString(R.string.error_sync_network)); - } else if (result == GTaskManager.STATE_INTERNAL_ERROR) { - showNotification(R.string.ticker_fail, mContext.getString(R.string.error_sync_internal)); - } else if (result == GTaskManager.STATE_SYNC_CANCELLED) { - showNotification(R.string.ticker_cancel, mContext - .getString(R.string.error_sync_cancelled)); + String content; + int tickerId; + // 根据不同的同步结果显示对应的通知 + switch (result) { + case GTaskManager.STATE_SUCCESS: + tickerId = R.string.ticker_success; + content = mContext.getString(R.string.success_sync_account, + mTaskManager.getSyncAccount()); // 显示成功消息和账户名 + NotesPreferenceActivity.setLastSyncTime(mContext, System.currentTimeMillis()); // 记录最后同步时间 + break; + case GTaskManager.STATE_NETWORK_ERROR: + tickerId = R.string.ticker_fail; + content = mContext.getString(R.string.error_sync_network); // 网络错误消息 + break; + case GTaskManager.STATE_INTERNAL_ERROR: + tickerId = R.string.ticker_fail; + content = mContext.getString(R.string.error_sync_internal); // 内部错误消息 + break; + case GTaskManager.STATE_SYNC_CANCELLED: + tickerId = R.string.ticker_cancel; + content = mContext.getString(R.string.error_sync_cancelled); // 取消同步消息 + break; + default: + return; } + showNotification(tickerId, content); // 显示结果通知 + + // 在后台线程执行回调,避免阻塞主线程 if (mOnCompleteListener != null) { new Thread(new Runnable() { - public void run() { - mOnCompleteListener.onComplete(); + mOnCompleteListener.onComplete(); // 触发完成回调 } }).start(); } } -} +} \ No newline at end of file diff --git a/src/Notes-master/src/net/micode/notes/gtask/remote/GTaskClient.java b/src/Notes-master/src/net/micode/notes/gtask/remote/GTaskClient.java index c67dfdf..7637e41 100644 --- a/src/Notes-master/src/net/micode/notes/gtask/remote/GTaskClient.java +++ b/src/Notes-master/src/net/micode/notes/gtask/remote/GTaskClient.java @@ -14,8 +14,10 @@ * limitations under the License. */ +// Google Tasks API客户端实现类,负责与Google Tasks服务通信 package net.micode.notes.gtask.remote; +// Android基础组件导入 import android.accounts.Account; import android.accounts.AccountManager; import android.accounts.AccountManagerFuture; @@ -24,6 +26,7 @@ import android.os.Bundle; import android.text.TextUtils; import android.util.Log; +// 项目内部类导入 import net.micode.notes.gtask.data.Node; import net.micode.notes.gtask.data.Task; import net.micode.notes.gtask.data.TaskList; @@ -32,6 +35,7 @@ import net.micode.notes.gtask.exception.NetworkFailureException; import net.micode.notes.tool.GTaskStringUtils; import net.micode.notes.ui.NotesPreferenceActivity; +// HTTP客户端和JSON处理类导入 import org.apache.http.HttpEntity; import org.apache.http.HttpResponse; import org.apache.http.client.ClientProtocolException; @@ -50,6 +54,7 @@ import org.json.JSONArray; import org.json.JSONException; import org.json.JSONObject; +// Java IO和工具类导入 import java.io.BufferedReader; import java.io.IOException; import java.io.InputStream; @@ -60,36 +65,55 @@ import java.util.zip.GZIPInputStream; import java.util.zip.Inflater; import java.util.zip.InflaterInputStream; - +/** + * Google Tasks API客户端类,封装了与Google Tasks服务的所有交互 + */ public class GTaskClient { + // 日志标签 private static final String TAG = GTaskClient.class.getSimpleName(); + // Google Tasks基础URL private static final String GTASK_URL = "https://mail.google.com/tasks/"; + // Google Tasks获取数据URL private static final String GTASK_GET_URL = "https://mail.google.com/tasks/ig"; + // Google Tasks提交数据URL private static final String GTASK_POST_URL = "https://mail.google.com/tasks/r/ig"; + // 单例实例 private static GTaskClient mInstance = null; + // HTTP客户端 private DefaultHttpClient mHttpClient; + // 获取数据URL private String mGetUrl; + // 提交数据URL private String mPostUrl; + // 客户端版本号 private long mClientVersion; + // 登录状态标志 private boolean mLoggedin; + // 最后登录时间 private long mLastLoginTime; + // 操作ID计数器 private int mActionId; + // 关联的Google账户 private Account mAccount; + // 待更新操作数组 private JSONArray mUpdateArray; + /** + * 私有构造函数,初始化默认值 + */ private GTaskClient() { mHttpClient = null; mGetUrl = GTASK_GET_URL; @@ -102,6 +126,10 @@ public class GTaskClient { mUpdateArray = null; } + /** + * 获取单例实例 + * @return GTaskClient单例实例 + */ public static synchronized GTaskClient getInstance() { if (mInstance == null) { mInstance = new GTaskClient(); @@ -109,36 +137,43 @@ public class GTaskClient { return mInstance; } + /** + * 登录Google Tasks服务 + * @param activity 上下文Activity + * @return 是否登录成功 + */ public boolean login(Activity activity) { - // we suppose that the cookie would expire after 5 minutes - // then we need to re-login + // 假设cookie在5分钟后过期,需要重新登录 final long interval = 1000 * 60 * 5; if (mLastLoginTime + interval < System.currentTimeMillis()) { mLoggedin = false; } - // need to re-login after account switch - if (mLoggedin - && !TextUtils.equals(getSyncAccount().name, NotesPreferenceActivity - .getSyncAccountName(activity))) { + // 账户切换后需要重新登录 + if (mLoggedin && !TextUtils.equals(getSyncAccount().name, + NotesPreferenceActivity.getSyncAccountName(activity))) { mLoggedin = false; } + // 如果已经登录则直接返回 if (mLoggedin) { Log.d(TAG, "already logged in"); return true; } + // 更新最后登录时间 mLastLoginTime = System.currentTimeMillis(); + + // 获取Google账户认证令牌 String authToken = loginGoogleAccount(activity, false); if (authToken == null) { Log.e(TAG, "login google account failed"); return false; } - // login with custom domain if necessary - if (!(mAccount.name.toLowerCase().endsWith("gmail.com") || mAccount.name.toLowerCase() - .endsWith("googlemail.com"))) { + // 处理自定义域名的登录(非gmail.com或googlemail.com) + if (!(mAccount.name.toLowerCase().endsWith("gmail.com") || + mAccount.name.toLowerCase().endsWith("googlemail.com"))) { StringBuilder url = new StringBuilder(GTASK_URL).append("a/"); int index = mAccount.name.indexOf('@') + 1; String suffix = mAccount.name.substring(index); @@ -146,12 +181,13 @@ public class GTaskClient { mGetUrl = url.toString() + "ig"; mPostUrl = url.toString() + "r/ig"; + // 尝试使用自定义域名登录 if (tryToLoginGtask(activity, authToken)) { mLoggedin = true; } } - // try to login with google official url + // 如果自定义域名登录失败,尝试使用官方URL登录 if (!mLoggedin) { mGetUrl = GTASK_GET_URL; mPostUrl = GTASK_POST_URL; @@ -164,16 +200,26 @@ public class GTaskClient { return true; } + /** + * 登录Google账户并获取认证令牌 + * @param activity 上下文Activity + * @param invalidateToken 是否使旧令牌失效 + * @return 认证令牌 + */ private String loginGoogleAccount(Activity activity, boolean invalidateToken) { String authToken; AccountManager accountManager = AccountManager.get(activity); + + // 获取所有Google账户 Account[] accounts = accountManager.getAccountsByType("com.google"); + // 检查是否有可用账户 if (accounts.length == 0) { Log.e(TAG, "there is no available google account"); return null; } + // 获取设置中指定的同步账户 String accountName = NotesPreferenceActivity.getSyncAccountName(activity); Account account = null; for (Account a : accounts) { @@ -182,6 +228,8 @@ public class GTaskClient { break; } } + + // 验证账户是否存在 if (account != null) { mAccount = account; } else { @@ -189,12 +237,14 @@ public class GTaskClient { return null; } - // get the token now + // 获取认证令牌 AccountManagerFuture accountManagerFuture = accountManager.getAuthToken(account, "goanna_mobile", null, activity, null, null); try { Bundle authTokenBundle = accountManagerFuture.getResult(); authToken = authTokenBundle.getString(AccountManager.KEY_AUTHTOKEN); + + // 如果需要使旧令牌失效 if (invalidateToken) { accountManager.invalidateAuthToken("com.google", authToken); loginGoogleAccount(activity, false); @@ -207,16 +257,23 @@ public class GTaskClient { return authToken; } + /** + * 尝试登录Google Tasks服务 + * @param activity 上下文Activity + * @param authToken 认证令牌 + * @return 是否登录成功 + */ private boolean tryToLoginGtask(Activity activity, String authToken) { + // 第一次尝试登录 if (!loginGtask(authToken)) { - // maybe the auth token is out of date, now let's invalidate the - // token and try again + // 如果失败,可能是令牌过期,使旧令牌失效并获取新令牌 authToken = loginGoogleAccount(activity, true); if (authToken == null) { Log.e(TAG, "login google account failed"); return false; } + // 使用新令牌再次尝试登录 if (!loginGtask(authToken)) { Log.e(TAG, "login gtask failed"); return false; @@ -225,25 +282,33 @@ public class GTaskClient { return true; } + /** + * 实际执行Google Tasks登录操作 + * @param authToken 认证令牌 + * @return 是否登录成功 + */ private boolean loginGtask(String authToken) { + // 设置HTTP连接参数 int timeoutConnection = 10000; int timeoutSocket = 15000; HttpParams httpParameters = new BasicHttpParams(); HttpConnectionParams.setConnectionTimeout(httpParameters, timeoutConnection); HttpConnectionParams.setSoTimeout(httpParameters, timeoutSocket); + + // 创建HTTP客户端 mHttpClient = new DefaultHttpClient(httpParameters); BasicCookieStore localBasicCookieStore = new BasicCookieStore(); mHttpClient.setCookieStore(localBasicCookieStore); HttpProtocolParams.setUseExpectContinue(mHttpClient.getParams(), false); - // login gtask + // 执行登录请求 try { String loginUrl = mGetUrl + "?auth=" + authToken; HttpGet httpGet = new HttpGet(loginUrl); HttpResponse response = null; response = mHttpClient.execute(httpGet); - // get the cookie now + // 检查是否获取到认证cookie List cookies = mHttpClient.getCookieStore().getCookies(); boolean hasAuthCookie = false; for (Cookie cookie : cookies) { @@ -255,7 +320,7 @@ public class GTaskClient { Log.w(TAG, "it seems that there is no auth cookie"); } - // get the client version + // 从响应中提取客户端版本号 String resString = getResponseContent(response.getEntity()); String jsBegin = "_setup("; String jsEnd = ")}"; @@ -272,7 +337,6 @@ public class GTaskClient { e.printStackTrace(); return false; } catch (Exception e) { - // simply catch all exceptions Log.e(TAG, "httpget gtask_url failed"); return false; } @@ -280,10 +344,18 @@ public class GTaskClient { return true; } + /** + * 获取下一个操作ID + * @return 操作ID + */ private int getActionId() { return mActionId++; } + /** + * 创建HTTP POST请求 + * @return 配置好的HttpPost对象 + */ private HttpPost createHttpPost() { HttpPost httpPost = new HttpPost(mPostUrl); httpPost.setHeader("Content-Type", "application/x-www-form-urlencoded;charset=utf-8"); @@ -291,13 +363,21 @@ public class GTaskClient { return httpPost; } + /** + * 从HTTP实体获取响应内容 + * @param entity HTTP实体 + * @return 响应内容字符串 + * @throws IOException 如果读取失败 + */ private String getResponseContent(HttpEntity entity) throws IOException { + // 检查内容编码 String contentEncoding = null; if (entity.getContentEncoding() != null) { contentEncoding = entity.getContentEncoding().getValue(); Log.d(TAG, "encoding: " + contentEncoding); } + // 根据编码类型创建适当的输入流 InputStream input = entity.getContent(); if (contentEncoding != null && contentEncoding.equalsIgnoreCase("gzip")) { input = new GZIPInputStream(entity.getContent()); @@ -306,6 +386,7 @@ public class GTaskClient { input = new InflaterInputStream(entity.getContent(), inflater); } + // 读取响应内容 try { InputStreamReader isr = new InputStreamReader(input); BufferedReader br = new BufferedReader(isr); @@ -323,7 +404,14 @@ public class GTaskClient { } } + /** + * 发送POST请求 + * @param js 要发送的JSON对象 + * @return 响应JSON对象 + * @throws NetworkFailureException 网络错误时抛出 + */ private JSONObject postRequest(JSONObject js) throws NetworkFailureException { + // 检查登录状态 if (!mLoggedin) { Log.e(TAG, "please login first"); throw new ActionFailureException("not logged in"); @@ -331,12 +419,13 @@ public class GTaskClient { HttpPost httpPost = createHttpPost(); try { + // 准备请求参数 LinkedList list = new LinkedList(); list.add(new BasicNameValuePair("r", js.toString())); UrlEncodedFormEntity entity = new UrlEncodedFormEntity(list, "UTF-8"); httpPost.setEntity(entity); - // execute the post + // 执行请求并处理响应 HttpResponse response = mHttpClient.execute(httpPost); String jsString = getResponseContent(response.getEntity()); return new JSONObject(jsString); @@ -360,23 +449,31 @@ public class GTaskClient { } } + /** + * 创建任务 + * @param task 要创建的任务对象 + * @throws NetworkFailureException 网络错误时抛出 + */ public void createTask(Task task) throws NetworkFailureException { + // 提交所有待更新操作 commitUpdate(); try { JSONObject jsPost = new JSONObject(); JSONArray actionList = new JSONArray(); - // action_list + // 添加创建任务操作 actionList.put(task.getCreateAction(getActionId())); jsPost.put(GTaskStringUtils.GTASK_JSON_ACTION_LIST, actionList); - // client_version + // 设置客户端版本 jsPost.put(GTaskStringUtils.GTASK_JSON_CLIENT_VERSION, mClientVersion); - // post + // 发送请求并处理响应 JSONObject jsResponse = postRequest(jsPost); JSONObject jsResult = (JSONObject) jsResponse.getJSONArray( GTaskStringUtils.GTASK_JSON_RESULTS).get(0); + + // 设置任务ID task.setGid(jsResult.getString(GTaskStringUtils.GTASK_JSON_NEW_ID)); } catch (JSONException e) { @@ -386,23 +483,31 @@ public class GTaskClient { } } + /** + * 创建任务列表 + * @param tasklist 要创建的任务列表对象 + * @throws NetworkFailureException 网络错误时抛出 + */ public void createTaskList(TaskList tasklist) throws NetworkFailureException { + // 提交所有待更新操作 commitUpdate(); try { JSONObject jsPost = new JSONObject(); JSONArray actionList = new JSONArray(); - // action_list + // 添加创建任务列表操作 actionList.put(tasklist.getCreateAction(getActionId())); jsPost.put(GTaskStringUtils.GTASK_JSON_ACTION_LIST, actionList); - // client version + // 设置客户端版本 jsPost.put(GTaskStringUtils.GTASK_JSON_CLIENT_VERSION, mClientVersion); - // post + // 发送请求并处理响应 JSONObject jsResponse = postRequest(jsPost); JSONObject jsResult = (JSONObject) jsResponse.getJSONArray( GTaskStringUtils.GTASK_JSON_RESULTS).get(0); + + // 设置任务列表ID tasklist.setGid(jsResult.getString(GTaskStringUtils.GTASK_JSON_NEW_ID)); } catch (JSONException e) { @@ -412,17 +517,22 @@ public class GTaskClient { } } + /** + * 提交所有待更新操作 + * @throws NetworkFailureException 网络错误时抛出 + */ public void commitUpdate() throws NetworkFailureException { if (mUpdateArray != null) { try { JSONObject jsPost = new JSONObject(); - // action_list + // 添加待更新操作列表 jsPost.put(GTaskStringUtils.GTASK_JSON_ACTION_LIST, mUpdateArray); - // client_version + // 设置客户端版本 jsPost.put(GTaskStringUtils.GTASK_JSON_CLIENT_VERSION, mClientVersion); + // 发送请求 postRequest(jsPost); mUpdateArray = null; } catch (JSONException e) { @@ -433,50 +543,67 @@ public class GTaskClient { } } + /** + * 添加待更新节点 + * @param node 要更新的节点 + * @throws NetworkFailureException 网络错误时抛出 + */ public void addUpdateNode(Node node) throws NetworkFailureException { if (node != null) { - // too many update items may result in an error - // set max to 10 items + // 如果待更新操作过多(超过10个),先提交 if (mUpdateArray != null && mUpdateArray.length() > 10) { commitUpdate(); } + // 初始化更新数组(如果为空) if (mUpdateArray == null) mUpdateArray = new JSONArray(); + + // 添加更新操作 mUpdateArray.put(node.getUpdateAction(getActionId())); } } + /** + * 移动任务 + * @param task 要移动的任务 + * @param preParent 原父任务列表 + * @param curParent 新父任务列表 + * @throws NetworkFailureException 网络错误时抛出 + */ public void moveTask(Task task, TaskList preParent, TaskList curParent) throws NetworkFailureException { + // 提交所有待更新操作 commitUpdate(); try { JSONObject jsPost = new JSONObject(); JSONArray actionList = new JSONArray(); JSONObject action = new JSONObject(); - // action_list + // 设置移动操作参数 action.put(GTaskStringUtils.GTASK_JSON_ACTION_TYPE, GTaskStringUtils.GTASK_JSON_ACTION_TYPE_MOVE); action.put(GTaskStringUtils.GTASK_JSON_ACTION_ID, getActionId()); action.put(GTaskStringUtils.GTASK_JSON_ID, task.getGid()); + + // 如果在同一任务列表中移动且有前驱兄弟节点 if (preParent == curParent && task.getPriorSibling() != null) { - // put prioring_sibing_id only if moving within the tasklist and - // it is not the first one action.put(GTaskStringUtils.GTASK_JSON_PRIOR_SIBLING_ID, task.getPriorSibling()); } + + // 设置源列表和目标父节点 action.put(GTaskStringUtils.GTASK_JSON_SOURCE_LIST, preParent.getGid()); action.put(GTaskStringUtils.GTASK_JSON_DEST_PARENT, curParent.getGid()); + + // 如果是在不同任务列表间移动 if (preParent != curParent) { - // put the dest_list only if moving between tasklists action.put(GTaskStringUtils.GTASK_JSON_DEST_LIST, curParent.getGid()); } + + // 添加操作并发送请求 actionList.put(action); jsPost.put(GTaskStringUtils.GTASK_JSON_ACTION_LIST, actionList); - - // client_version jsPost.put(GTaskStringUtils.GTASK_JSON_CLIENT_VERSION, mClientVersion); - postRequest(jsPost); } catch (JSONException e) { @@ -486,20 +613,25 @@ public class GTaskClient { } } + /** + * 删除节点 + * @param node 要删除的节点 + * @throws NetworkFailureException 网络错误时抛出 + */ public void deleteNode(Node node) throws NetworkFailureException { + // 提交所有待更新操作 commitUpdate(); try { JSONObject jsPost = new JSONObject(); JSONArray actionList = new JSONArray(); - // action_list + // 标记节点为已删除并添加更新操作 node.setDeleted(true); actionList.put(node.getUpdateAction(getActionId())); jsPost.put(GTaskStringUtils.GTASK_JSON_ACTION_LIST, actionList); - // client_version + // 设置客户端版本并发送请求 jsPost.put(GTaskStringUtils.GTASK_JSON_CLIENT_VERSION, mClientVersion); - postRequest(jsPost); mUpdateArray = null; } catch (JSONException e) { @@ -509,18 +641,25 @@ public class GTaskClient { } } + /** + * 获取所有任务列表 + * @return 任务列表JSON数组 + * @throws NetworkFailureException 网络错误时抛出 + */ public JSONArray getTaskLists() throws NetworkFailureException { + // 检查登录状态 if (!mLoggedin) { Log.e(TAG, "please login first"); throw new ActionFailureException("not logged in"); } try { + // 发送GET请求获取任务列表 HttpGet httpGet = new HttpGet(mGetUrl); HttpResponse response = null; response = mHttpClient.execute(httpGet); - // get the task list + // 从响应中提取任务列表数据 String resString = getResponseContent(response.getEntity()); String jsBegin = "_setup("; String jsEnd = ")}"; @@ -547,25 +686,33 @@ public class GTaskClient { } } + /** + * 获取指定任务列表的所有任务 + * @param listGid 任务列表ID + * @return 任务JSON数组 + * @throws NetworkFailureException 网络错误时抛出 + */ public JSONArray getTaskList(String listGid) throws NetworkFailureException { + // 提交所有待更新操作 commitUpdate(); try { JSONObject jsPost = new JSONObject(); JSONArray actionList = new JSONArray(); JSONObject action = new JSONObject(); - // action_list + // 设置获取所有任务操作参数 action.put(GTaskStringUtils.GTASK_JSON_ACTION_TYPE, GTaskStringUtils.GTASK_JSON_ACTION_TYPE_GETALL); action.put(GTaskStringUtils.GTASK_JSON_ACTION_ID, getActionId()); action.put(GTaskStringUtils.GTASK_JSON_LIST_ID, listGid); action.put(GTaskStringUtils.GTASK_JSON_GET_DELETED, false); + + // 添加操作并发送请求 actionList.put(action); jsPost.put(GTaskStringUtils.GTASK_JSON_ACTION_LIST, actionList); - - // client_version jsPost.put(GTaskStringUtils.GTASK_JSON_CLIENT_VERSION, mClientVersion); + // 处理响应 JSONObject jsResponse = postRequest(jsPost); return jsResponse.getJSONArray(GTaskStringUtils.GTASK_JSON_TASKS); } catch (JSONException e) { @@ -575,11 +722,18 @@ public class GTaskClient { } } + /** + * 获取同步账户 + * @return 同步账户对象 + */ public Account getSyncAccount() { return mAccount; } + /** + * 重置待更新数组 + */ public void resetUpdateArray() { mUpdateArray = null; } -} +} \ No newline at end of file diff --git a/src/Notes-master/src/net/micode/notes/gtask/remote/GTaskManager.java b/src/Notes-master/src/net/micode/notes/gtask/remote/GTaskManager.java index d2b4082..a312c03 100644 --- a/src/Notes-master/src/net/micode/notes/gtask/remote/GTaskManager.java +++ b/src/Notes-master/src/net/micode/notes/gtask/remote/GTaskManager.java @@ -14,8 +14,10 @@ * limitations under the License. */ +// Google Tasks同步管理类,负责本地便签与Google Tasks的同步操作 package net.micode.notes.gtask.remote; +// Android基础组件导入 import android.app.Activity; import android.content.ContentResolver; import android.content.ContentUris; @@ -24,7 +26,10 @@ import android.content.Context; import android.database.Cursor; import android.util.Log; +// 项目资源文件导入 import net.micode.notes.R; + +// 数据模型和工具类导入 import net.micode.notes.data.Notes; import net.micode.notes.data.Notes.DataColumns; import net.micode.notes.data.Notes.NoteColumns; @@ -38,88 +43,125 @@ import net.micode.notes.gtask.exception.NetworkFailureException; import net.micode.notes.tool.DataUtils; import net.micode.notes.tool.GTaskStringUtils; +// JSON处理类导入 import org.json.JSONArray; import org.json.JSONException; import org.json.JSONObject; +// Java集合类导入 import java.util.HashMap; import java.util.HashSet; import java.util.Iterator; import java.util.Map; - +// Google Tasks同步管理主类 public class GTaskManager { + // 日志标签 private static final String TAG = GTaskManager.class.getSimpleName(); + // 同步状态常量定义 public static final int STATE_SUCCESS = 0; - public static final int STATE_NETWORK_ERROR = 1; - public static final int STATE_INTERNAL_ERROR = 2; - public static final int STATE_SYNC_IN_PROGRESS = 3; - public static final int STATE_SYNC_CANCELLED = 4; + // 单例实例 private static GTaskManager mInstance = null; + // Activity上下文 private Activity mActivity; + // 应用上下文 private Context mContext; + // 内容解析器 private ContentResolver mContentResolver; + // 同步状态标志 private boolean mSyncing; + // 取消同步标志 private boolean mCancelled; + // 任务列表哈希表(gid -> TaskList) private HashMap mGTaskListHashMap; + // 任务节点哈希表(gid -> Node) private HashMap mGTaskHashMap; + // 元数据哈希表(gid -> MetaData) private HashMap mMetaHashMap; + // 元数据任务列表 private TaskList mMetaList; + // 本地删除ID集合 private HashSet mLocalDeleteIdMap; + // Google任务ID到本地ID的映射 private HashMap mGidToNid; + // 本地ID到Google任务ID的映射 private HashMap mNidToGid; + // 私有构造函数 private GTaskManager() { + // 初始化同步状态标志 mSyncing = false; mCancelled = false; + + // 初始化任务列表哈希表 mGTaskListHashMap = new HashMap(); + + // 初始化任务节点哈希表 mGTaskHashMap = new HashMap(); + + // 初始化元数据哈希表 mMetaHashMap = new HashMap(); + + // 初始化元数据任务列表 mMetaList = null; + + // 初始化本地删除ID集合 mLocalDeleteIdMap = new HashSet(); + + // 初始化ID映射表 mGidToNid = new HashMap(); mNidToGid = new HashMap(); } + // 获取单例实例 public static synchronized GTaskManager getInstance() { + // 如果实例不存在则创建新实例 if (mInstance == null) { mInstance = new GTaskManager(); } return mInstance; } + // 设置Activity上下文(用于获取认证令牌) public synchronized void setActivityContext(Activity activity) { - // used for getting authtoken mActivity = activity; } + // 执行同步操作 public int sync(Context context, GTaskASyncTask asyncTask) { + // 检查是否正在同步 if (mSyncing) { Log.d(TAG, "Sync is in progress"); return STATE_SYNC_IN_PROGRESS; } + + // 初始化上下文和内容解析器 mContext = context; mContentResolver = mContext.getContentResolver(); + + // 设置同步状态标志 mSyncing = true; mCancelled = false; + + // 清空各种缓存和映射表 mGTaskListHashMap.clear(); mGTaskHashMap.clear(); mMetaHashMap.clear(); @@ -128,34 +170,39 @@ public class GTaskManager { mNidToGid.clear(); try { + // 获取GTask客户端实例 GTaskClient client = GTaskClient.getInstance(); client.resetUpdateArray(); - // login google task + // 登录Google Tasks(如果未取消) if (!mCancelled) { if (!client.login(mActivity)) { throw new NetworkFailureException("login google task failed"); } } - // get the task list from google + // 初始化任务列表 asyncTask.publishProgess(mContext.getString(R.string.sync_progress_init_list)); initGTaskList(); - // do content sync work + // 执行内容同步 asyncTask.publishProgess(mContext.getString(R.string.sync_progress_syncing)); syncContent(); } catch (NetworkFailureException e) { + // 处理网络错误 Log.e(TAG, e.toString()); return STATE_NETWORK_ERROR; } catch (ActionFailureException e) { + // 处理操作错误 Log.e(TAG, e.toString()); return STATE_INTERNAL_ERROR; } catch (Exception e) { + // 处理其他异常 Log.e(TAG, e.toString()); e.printStackTrace(); return STATE_INTERNAL_ERROR; } finally { + // 清理资源 mGTaskListHashMap.clear(); mGTaskHashMap.clear(); mMetaHashMap.clear(); @@ -165,29 +212,37 @@ public class GTaskManager { mSyncing = false; } + // 返回同步结果状态 return mCancelled ? STATE_SYNC_CANCELLED : STATE_SUCCESS; } + // 初始化Google Tasks列表 private void initGTaskList() throws NetworkFailureException { + // 检查是否已取消 if (mCancelled) return; + + // 获取GTask客户端实例 GTaskClient client = GTaskClient.getInstance(); try { + // 获取任务列表JSON数组 JSONArray jsTaskLists = client.getTaskLists(); - // init meta list first + // 首先初始化元数据列表 mMetaList = null; for (int i = 0; i < jsTaskLists.length(); i++) { + // 获取任务列表JSON对象 JSONObject object = jsTaskLists.getJSONObject(i); String gid = object.getString(GTaskStringUtils.GTASK_JSON_ID); String name = object.getString(GTaskStringUtils.GTASK_JSON_NAME); - if (name - .equals(GTaskStringUtils.MIUI_FOLDER_PREFFIX + GTaskStringUtils.FOLDER_META)) { + // 检查是否为元数据文件夹 + if (name.equals(GTaskStringUtils.MIUI_FOLDER_PREFFIX + GTaskStringUtils.FOLDER_META)) { + // 创建元数据任务列表 mMetaList = new TaskList(); mMetaList.setContentByRemoteJSON(object); - // load meta data + // 加载元数据 JSONArray jsMetas = client.getTaskList(gid); for (int j = 0; j < jsMetas.length(); j++) { object = (JSONObject) jsMetas.getJSONObject(j); @@ -203,7 +258,7 @@ public class GTaskManager { } } - // create meta list if not existed + // 如果元数据列表不存在则创建 if (mMetaList == null) { mMetaList = new TaskList(); mMetaList.setName(GTaskStringUtils.MIUI_FOLDER_PREFFIX @@ -211,21 +266,23 @@ public class GTaskManager { GTaskClient.getInstance().createTaskList(mMetaList); } - // init task list + // 初始化普通任务列表 for (int i = 0; i < jsTaskLists.length(); i++) { JSONObject object = jsTaskLists.getJSONObject(i); String gid = object.getString(GTaskStringUtils.GTASK_JSON_ID); String name = object.getString(GTaskStringUtils.GTASK_JSON_NAME); + // 检查是否为MIUI文件夹(非元数据文件夹) if (name.startsWith(GTaskStringUtils.MIUI_FOLDER_PREFFIX) && !name.equals(GTaskStringUtils.MIUI_FOLDER_PREFFIX - + GTaskStringUtils.FOLDER_META)) { + + GTaskStringUtils.FOLDER_META)) { + // 创建任务列表并添加到哈希表 TaskList tasklist = new TaskList(); tasklist.setContentByRemoteJSON(object); mGTaskListHashMap.put(gid, tasklist); mGTaskHashMap.put(gid, tasklist); - // load tasks + // 加载任务列表中的任务 JSONArray jsTasks = client.getTaskList(gid); for (int j = 0; j < jsTasks.length(); j++) { object = (JSONObject) jsTasks.getJSONObject(j); @@ -241,78 +298,91 @@ public class GTaskManager { } } } catch (JSONException e) { + // 处理JSON解析错误 Log.e(TAG, e.toString()); e.printStackTrace(); throw new ActionFailureException("initGTaskList: handing JSONObject failed"); } } + // 同步内容数据 private void syncContent() throws NetworkFailureException { + // 同步类型和临时变量 int syncType; Cursor c = null; String gid; Node node; + // 清空本地删除ID集合 mLocalDeleteIdMap.clear(); + // 检查是否已取消 if (mCancelled) { return; } - // for local deleted note + // 处理本地已删除的便签 try { + // 查询回收站中的便签 c = mContentResolver.query(Notes.CONTENT_NOTE_URI, SqlNote.PROJECTION_NOTE, "(type<>? AND parent_id=?)", new String[] { String.valueOf(Notes.TYPE_SYSTEM), String.valueOf(Notes.ID_TRASH_FOLER) }, null); if (c != null) { while (c.moveToNext()) { + // 获取Google任务ID和对应节点 gid = c.getString(SqlNote.GTASK_ID_COLUMN); node = mGTaskHashMap.get(gid); if (node != null) { + // 从哈希表中移除并执行同步 mGTaskHashMap.remove(gid); doContentSync(Node.SYNC_ACTION_DEL_REMOTE, node, c); } + // 添加到本地删除集合 mLocalDeleteIdMap.add(c.getLong(SqlNote.ID_COLUMN)); } } else { Log.w(TAG, "failed to query trash folder"); } } finally { + // 关闭Cursor if (c != null) { c.close(); c = null; } } - // sync folder first + // 首先同步文件夹 syncFolder(); - // for note existing in database + // 处理数据库中存在的便签 try { + // 查询非回收站中的便签 c = mContentResolver.query(Notes.CONTENT_NOTE_URI, SqlNote.PROJECTION_NOTE, "(type=? AND parent_id<>?)", new String[] { String.valueOf(Notes.TYPE_NOTE), String.valueOf(Notes.ID_TRASH_FOLER) }, NoteColumns.TYPE + " DESC"); if (c != null) { while (c.moveToNext()) { + // 获取Google任务ID和对应节点 gid = c.getString(SqlNote.GTASK_ID_COLUMN); node = mGTaskHashMap.get(gid); if (node != null) { + // 从哈希表中移除并确定同步类型 mGTaskHashMap.remove(gid); mGidToNid.put(gid, c.getLong(SqlNote.ID_COLUMN)); mNidToGid.put(c.getLong(SqlNote.ID_COLUMN), gid); syncType = node.getSyncAction(c); } else { + // 根据情况确定是本地添加还是远程删除 if (c.getString(SqlNote.GTASK_ID_COLUMN).trim().length() == 0) { - // local add syncType = Node.SYNC_ACTION_ADD_REMOTE; } else { - // remote delete syncType = Node.SYNC_ACTION_DEL_LOCAL; } } + // 执行内容同步 doContentSync(syncType, node, c); } } else { @@ -320,13 +390,14 @@ public class GTaskManager { } } finally { + // 关闭Cursor if (c != null) { c.close(); c = null; } } - // go through remaining items + // 处理剩余的项目(远程添加) Iterator> iter = mGTaskHashMap.entrySet().iterator(); while (iter.hasNext()) { Map.Entry entry = iter.next(); @@ -334,46 +405,50 @@ public class GTaskManager { doContentSync(Node.SYNC_ACTION_ADD_LOCAL, node, null); } - // mCancelled can be set by another thread, so we neet to check one by - // one - // clear local delete table + // 检查是否已取消(可能由其他线程设置) + // 清理本地删除表 if (!mCancelled) { if (!DataUtils.batchDeleteNotes(mContentResolver, mLocalDeleteIdMap)) { throw new ActionFailureException("failed to batch-delete local deleted notes"); } } - // refresh local sync id + // 刷新本地同步ID(如果未取消) if (!mCancelled) { GTaskClient.getInstance().commitUpdate(); refreshLocalSyncId(); } - } + // 同步文件夹 private void syncFolder() throws NetworkFailureException { + // 临时变量 Cursor c = null; String gid; Node node; int syncType; + // 检查是否已取消 if (mCancelled) { return; } - // for root folder + // 处理根文件夹 try { + // 查询根文件夹 c = mContentResolver.query(ContentUris.withAppendedId(Notes.CONTENT_NOTE_URI, Notes.ID_ROOT_FOLDER), SqlNote.PROJECTION_NOTE, null, null, null); if (c != null) { c.moveToNext(); + // 获取Google任务ID和对应节点 gid = c.getString(SqlNote.GTASK_ID_COLUMN); node = mGTaskHashMap.get(gid); if (node != null) { + // 从哈希表中移除并更新映射 mGTaskHashMap.remove(gid); mGidToNid.put(gid, (long) Notes.ID_ROOT_FOLDER); mNidToGid.put((long) Notes.ID_ROOT_FOLDER, gid); - // for system folder, only update remote name if necessary + // 对于系统文件夹,只在必要时更新远程名称 if (!node.getName().equals( GTaskStringUtils.MIUI_FOLDER_PREFFIX + GTaskStringUtils.FOLDER_DEFAULT)) doContentSync(Node.SYNC_ACTION_UPDATE_REMOTE, node, c); @@ -384,28 +459,31 @@ public class GTaskManager { Log.w(TAG, "failed to query root folder"); } } finally { + // 关闭Cursor if (c != null) { c.close(); c = null; } } - // for call-note folder + // 处理通话记录文件夹 try { + // 查询通话记录文件夹 c = mContentResolver.query(Notes.CONTENT_NOTE_URI, SqlNote.PROJECTION_NOTE, "(_id=?)", new String[] { - String.valueOf(Notes.ID_CALL_RECORD_FOLDER) + String.valueOf(Notes.ID_CALL_RECORD_FOLDER) }, null); if (c != null) { if (c.moveToNext()) { + // 获取Google任务ID和对应节点 gid = c.getString(SqlNote.GTASK_ID_COLUMN); node = mGTaskHashMap.get(gid); if (node != null) { + // 从哈希表中移除并更新映射 mGTaskHashMap.remove(gid); mGidToNid.put(gid, (long) Notes.ID_CALL_RECORD_FOLDER); mNidToGid.put((long) Notes.ID_CALL_RECORD_FOLDER, gid); - // for system folder, only update remote name if - // necessary + // 对于系统文件夹,只在必要时更新远程名称 if (!node.getName().equals( GTaskStringUtils.MIUI_FOLDER_PREFFIX + GTaskStringUtils.FOLDER_CALL_NOTE)) @@ -418,49 +496,54 @@ public class GTaskManager { Log.w(TAG, "failed to query call note folder"); } } finally { + // 关闭Cursor if (c != null) { c.close(); c = null; } } - // for local existing folders + // 处理本地存在的文件夹 try { + // 查询非回收站中的文件夹 c = mContentResolver.query(Notes.CONTENT_NOTE_URI, SqlNote.PROJECTION_NOTE, "(type=? AND parent_id<>?)", new String[] { String.valueOf(Notes.TYPE_FOLDER), String.valueOf(Notes.ID_TRASH_FOLER) }, NoteColumns.TYPE + " DESC"); if (c != null) { while (c.moveToNext()) { + // 获取Google任务ID和对应节点 gid = c.getString(SqlNote.GTASK_ID_COLUMN); node = mGTaskHashMap.get(gid); if (node != null) { + // 从哈希表中移除并确定同步类型 mGTaskHashMap.remove(gid); mGidToNid.put(gid, c.getLong(SqlNote.ID_COLUMN)); mNidToGid.put(c.getLong(SqlNote.ID_COLUMN), gid); syncType = node.getSyncAction(c); } else { + // 根据情况确定是本地添加还是远程删除 if (c.getString(SqlNote.GTASK_ID_COLUMN).trim().length() == 0) { - // local add syncType = Node.SYNC_ACTION_ADD_REMOTE; } else { - // remote delete syncType = Node.SYNC_ACTION_DEL_LOCAL; } } + // 执行内容同步 doContentSync(syncType, node, c); } } else { Log.w(TAG, "failed to query existing folder"); } } finally { + // 关闭Cursor if (c != null) { c.close(); c = null; } } - // for remote add folders + // 处理远程添加的文件夹 Iterator> iter = mGTaskListHashMap.entrySet().iterator(); while (iter.hasNext()) { Map.Entry entry = iter.next(); @@ -472,24 +555,33 @@ public class GTaskManager { } } + // 如果未取消则提交更新 if (!mCancelled) GTaskClient.getInstance().commitUpdate(); } + // 执行具体的内容同步操作 private void doContentSync(int syncType, Node node, Cursor c) throws NetworkFailureException { + // 检查是否已取消 if (mCancelled) { return; } + // 元数据临时变量 MetaData meta; + + // 根据同步类型执行不同操作 switch (syncType) { case Node.SYNC_ACTION_ADD_LOCAL: + // 本地添加节点 addLocalNode(node); break; case Node.SYNC_ACTION_ADD_REMOTE: + // 远程添加节点 addRemoteNode(node, c); break; case Node.SYNC_ACTION_DEL_LOCAL: + // 本地删除节点 meta = mMetaHashMap.get(c.getString(SqlNote.GTASK_ID_COLUMN)); if (meta != null) { GTaskClient.getInstance().deleteNode(meta); @@ -497,6 +589,7 @@ public class GTaskManager { mLocalDeleteIdMap.add(c.getLong(SqlNote.ID_COLUMN)); break; case Node.SYNC_ACTION_DEL_REMOTE: + // 远程删除节点 meta = mMetaHashMap.get(node.getGid()); if (meta != null) { GTaskClient.getInstance().deleteNode(meta); @@ -504,57 +597,70 @@ public class GTaskManager { GTaskClient.getInstance().deleteNode(node); break; case Node.SYNC_ACTION_UPDATE_LOCAL: + // 更新本地节点 updateLocalNode(node, c); break; case Node.SYNC_ACTION_UPDATE_REMOTE: + // 更新远程节点 updateRemoteNode(node, c); break; case Node.SYNC_ACTION_UPDATE_CONFLICT: - // merging both modifications maybe a good idea - // right now just use local update simply + // 更新冲突(目前简单使用本地更新) updateRemoteNode(node, c); break; case Node.SYNC_ACTION_NONE: + // 无操作 break; case Node.SYNC_ACTION_ERROR: default: + // 未知操作类型错误 throw new ActionFailureException("unkown sync action type"); } } + // 添加本地节点 private void addLocalNode(Node node) throws NetworkFailureException { + // 检查是否已取消 if (mCancelled) { return; } + // 创建SQLite便签对象 SqlNote sqlNote; if (node instanceof TaskList) { + // 处理任务列表(文件夹) if (node.getName().equals( GTaskStringUtils.MIUI_FOLDER_PREFFIX + GTaskStringUtils.FOLDER_DEFAULT)) { + // 根文件夹 sqlNote = new SqlNote(mContext, Notes.ID_ROOT_FOLDER); } else if (node.getName().equals( GTaskStringUtils.MIUI_FOLDER_PREFFIX + GTaskStringUtils.FOLDER_CALL_NOTE)) { + // 通话记录文件夹 sqlNote = new SqlNote(mContext, Notes.ID_CALL_RECORD_FOLDER); } else { + // 普通文件夹 sqlNote = new SqlNote(mContext); sqlNote.setContent(node.getLocalJSONFromContent()); sqlNote.setParentId(Notes.ID_ROOT_FOLDER); } } else { + // 处理普通便签 sqlNote = new SqlNote(mContext); JSONObject js = node.getLocalJSONFromContent(); try { + // 处理便签ID冲突 if (js.has(GTaskStringUtils.META_HEAD_NOTE)) { JSONObject note = js.getJSONObject(GTaskStringUtils.META_HEAD_NOTE); if (note.has(NoteColumns.ID)) { long id = note.getLong(NoteColumns.ID); if (DataUtils.existInNoteDatabase(mContentResolver, id)) { - // the id is not available, have to create a new one + // ID不可用,需要创建新ID note.remove(NoteColumns.ID); } } } + // 处理数据ID冲突 if (js.has(GTaskStringUtils.META_HEAD_DATA)) { JSONArray dataArray = js.getJSONArray(GTaskStringUtils.META_HEAD_DATA); for (int i = 0; i < dataArray.length(); i++) { @@ -562,13 +668,11 @@ public class GTaskManager { if (data.has(DataColumns.ID)) { long dataId = data.getLong(DataColumns.ID); if (DataUtils.existInDataDatabase(mContentResolver, dataId)) { - // the data id is not available, have to create - // a new one + // 数据ID不可用,需要创建新ID data.remove(DataColumns.ID); } } } - } } catch (JSONException e) { Log.w(TAG, e.toString()); @@ -576,6 +680,7 @@ public class GTaskManager { } sqlNote.setContent(js); + // 设置父节点ID Long parentId = mGidToNid.get(((Task) node).getParent().getGid()); if (parentId == null) { Log.e(TAG, "cannot find task's parent id locally"); @@ -584,28 +689,32 @@ public class GTaskManager { sqlNote.setParentId(parentId.longValue()); } - // create the local node + // 创建本地节点 sqlNote.setGtaskId(node.getGid()); sqlNote.commit(false); - // update gid-nid mapping + // 更新gid-nid映射 mGidToNid.put(node.getGid(), sqlNote.getId()); mNidToGid.put(sqlNote.getId(), node.getGid()); - // update meta + // 更新元数据 updateRemoteMeta(node.getGid(), sqlNote); } + // 更新本地节点 private void updateLocalNode(Node node, Cursor c) throws NetworkFailureException { + // 检查是否已取消 if (mCancelled) { return; } + // 创建SQLite便签对象 SqlNote sqlNote; - // update the note locally + // 更新本地便签 sqlNote = new SqlNote(mContext, c); sqlNote.setContent(node.getLocalJSONFromContent()); + // 设置父节点ID Long parentId = (node instanceof Task) ? mGidToNid.get(((Task) node).getParent().getGid()) : new Long(Notes.ID_ROOT_FOLDER); if (parentId == null) { @@ -615,23 +724,28 @@ public class GTaskManager { sqlNote.setParentId(parentId.longValue()); sqlNote.commit(true); - // update meta info + // 更新元数据 updateRemoteMeta(node.getGid(), sqlNote); } + // 添加远程节点 private void addRemoteNode(Node node, Cursor c) throws NetworkFailureException { + // 检查是否已取消 if (mCancelled) { return; } + // 创建SQLite便签对象 SqlNote sqlNote = new SqlNote(mContext, c); Node n; - // update remotely + // 远程更新 if (sqlNote.isNoteType()) { + // 处理普通便签 Task task = new Task(); task.setContentByLocalJSON(sqlNote.getContent()); + // 设置父任务列表 String parentGid = mNidToGid.get(sqlNote.getParentId()); if (parentGid == null) { Log.e(TAG, "cannot find task's parent tasklist"); @@ -639,15 +753,17 @@ public class GTaskManager { } mGTaskListHashMap.get(parentGid).addChildTask(task); + // 创建远程任务 GTaskClient.getInstance().createTask(task); n = (Node) task; - // add meta + // 添加元数据 updateRemoteMeta(task.getGid(), sqlNote); } else { + // 处理文件夹 TaskList tasklist = null; - // we need to skip folder if it has already existed + // 构建文件夹名称 String folderName = GTaskStringUtils.MIUI_FOLDER_PREFFIX; if (sqlNote.getId() == Notes.ID_ROOT_FOLDER) folderName += GTaskStringUtils.FOLDER_DEFAULT; @@ -656,6 +772,7 @@ public class GTaskManager { else folderName += sqlNote.getSnippet(); + // 检查是否已存在同名文件夹 Iterator> iter = mGTaskListHashMap.entrySet().iterator(); while (iter.hasNext()) { Map.Entry entry = iter.next(); @@ -671,7 +788,7 @@ public class GTaskManager { } } - // no match we can add now + // 如果没有匹配则创建新文件夹 if (tasklist == null) { tasklist = new TaskList(); tasklist.setContentByLocalJSON(sqlNote.getContent()); @@ -681,36 +798,40 @@ public class GTaskManager { n = (Node) tasklist; } - // update local note + // 更新本地便签 sqlNote.setGtaskId(n.getGid()); sqlNote.commit(false); sqlNote.resetLocalModified(); sqlNote.commit(true); - // gid-id mapping + // 更新gid-id映射 mGidToNid.put(n.getGid(), sqlNote.getId()); mNidToGid.put(sqlNote.getId(), n.getGid()); } + // 更新远程节点 private void updateRemoteNode(Node node, Cursor c) throws NetworkFailureException { + // 检查是否已取消 if (mCancelled) { return; } + // 创建SQLite便签对象 SqlNote sqlNote = new SqlNote(mContext, c); - // update remotely + // 远程更新 node.setContentByLocalJSON(sqlNote.getContent()); GTaskClient.getInstance().addUpdateNode(node); - // update meta + // 更新元数据 updateRemoteMeta(node.getGid(), sqlNote); - // move task if necessary + // 如果需要则移动任务 if (sqlNote.isNoteType()) { Task task = (Task) node; TaskList preParentList = task.getParent(); + // 获取当前父任务列表 String curParentGid = mNidToGid.get(sqlNote.getParentId()); if (curParentGid == null) { Log.e(TAG, "cannot find task's parent tasklist"); @@ -718,6 +839,7 @@ public class GTaskManager { } TaskList curParentList = mGTaskListHashMap.get(curParentGid); + // 如果父任务列表发生变化则移动任务 if (preParentList != curParentList) { preParentList.removeChildTask(task); curParentList.addChildTask(task); @@ -725,18 +847,23 @@ public class GTaskManager { } } - // clear local modified flag + // 清除本地修改标志 sqlNote.resetLocalModified(); sqlNote.commit(true); } + // 更新远程元数据 private void updateRemoteMeta(String gid, SqlNote sqlNote) throws NetworkFailureException { + // 只处理普通便签的元数据 if (sqlNote != null && sqlNote.isNoteType()) { + // 获取或创建元数据 MetaData metaData = mMetaHashMap.get(gid); if (metaData != null) { + // 更新现有元数据 metaData.setMeta(gid, sqlNote.getContent()); GTaskClient.getInstance().addUpdateNode(metaData); } else { + // 创建新元数据 metaData = new MetaData(); metaData.setMeta(gid, sqlNote.getContent()); mMetaList.addChildTask(metaData); @@ -746,17 +873,20 @@ public class GTaskManager { } } + // 刷新本地同步ID private void refreshLocalSyncId() throws NetworkFailureException { + // 检查是否已取消 if (mCancelled) { return; } - // get the latest gtask list + // 获取最新的Google任务列表 mGTaskHashMap.clear(); mGTaskListHashMap.clear(); mMetaHashMap.clear(); initGTaskList(); + // 查询并更新本地同步ID Cursor c = null; try { c = mContentResolver.query(Notes.CONTENT_NOTE_URI, SqlNote.PROJECTION_NOTE, @@ -765,9 +895,11 @@ public class GTaskManager { }, NoteColumns.TYPE + " DESC"); if (c != null) { while (c.moveToNext()) { + // 获取Google任务ID和对应节点 String gid = c.getString(SqlNote.GTASK_ID_COLUMN); Node node = mGTaskHashMap.get(gid); if (node != null) { + // 从哈希表中移除并更新同步ID mGTaskHashMap.remove(gid); ContentValues values = new ContentValues(); values.put(NoteColumns.SYNC_ID, node.getLastModified()); @@ -783,6 +915,7 @@ public class GTaskManager { Log.w(TAG, "failed to query local note to refresh sync id"); } } finally { + // 关闭Cursor if (c != null) { c.close(); c = null; @@ -790,11 +923,13 @@ public class GTaskManager { } } + // 获取同步账户名称 public String getSyncAccount() { return GTaskClient.getInstance().getSyncAccount().name; } + // 取消同步操作 public void cancelSync() { mCancelled = true; } -} +} \ No newline at end of file diff --git a/src/Notes-master/src/net/micode/notes/gtask/remote/GTaskSyncService.java b/src/Notes-master/src/net/micode/notes/gtask/remote/GTaskSyncService.java index cca36f7..af41346 100644 --- a/src/Notes-master/src/net/micode/notes/gtask/remote/GTaskSyncService.java +++ b/src/Notes-master/src/net/micode/notes/gtask/remote/GTaskSyncService.java @@ -2,127 +2,192 @@ * Copyright (c) 2010-2011, The MiCode Open Source Community (www.micode.net) * * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at + * 您可以在遵守 License 的前提下使用本文件。 + * 您可以从以下地址获取 License 副本: * * http://www.apache.org/licenses/LICENSE-2.0 * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. + * 除非适用法律要求或书面同意,本软件根据 License 分发时“按现状”提供, + * 不附带任何明示或暗示的保证或条件。请参阅 License 了解具体的权限和限制。 */ -package net.micode.notes.gtask.remote; +package net.micode.notes.gtask.remote; // 指定代码所属的包名 -import android.app.Activity; -import android.app.Service; -import android.content.Context; -import android.content.Intent; -import android.os.Bundle; -import android.os.IBinder; +import android.app.Activity; // 导入Activity类,用于获取上下文 +import android.app.Service; // 导入Service类,用于创建后台服务 +import android.content.Context; // 导入Context类,提供应用环境信息 +import android.content.Intent; // 导入Intent类,用于组件间通信 +import android.os.Bundle; // 导入Bundle类,用于传递数据 +import android.os.IBinder; // 导入IBinder接口,用于服务间通信 +/** + * Google Tasks同步服务类,负责在后台执行任务同步操作 + * 通过Service机制实现,支持启动、取消同步等操作,并通过广播通知同步状态 + */ public class GTaskSyncService extends Service { + // 同步动作类型的Extra键名 public final static String ACTION_STRING_NAME = "sync_action_type"; + // 启动同步的动作类型常量 public final static int ACTION_START_SYNC = 0; + // 取消同步的动作类型常量 public final static int ACTION_CANCEL_SYNC = 1; + // 无效动作的类型常量 public final static int ACTION_INVALID = 2; + // 同步服务广播的名称,用于发送同步状态更新 public final static String GTASK_SERVICE_BROADCAST_NAME = "net.micode.notes.gtask.remote.gtask_sync_service"; + // 广播中表示是否正在同步的Extra键名 public final static String GTASK_SERVICE_BROADCAST_IS_SYNCING = "isSyncing"; + // 广播中表示同步进度消息的Extra键名 public final static String GTASK_SERVICE_BROADCAST_PROGRESS_MSG = "progressMsg"; + // 静态变量,保存当前执行同步的异步任务实例 private static GTaskASyncTask mSyncTask = null; + // 静态变量,保存当前同步进度消息 private static String mSyncProgress = ""; + /** + * 启动Google Tasks同步操作 + * 检查是否已有同步任务在运行,若无则创建并执行新的同步任务 + */ private void startSync() { - if (mSyncTask == null) { + if (mSyncTask == null) { // 检查是否已有同步任务在运行 + // 创建新的异步同步任务实例,传入服务上下文和完成监听器 mSyncTask = new GTaskASyncTask(this, new GTaskASyncTask.OnCompleteListener() { + @Override public void onComplete() { - mSyncTask = null; - sendBroadcast(""); - stopSelf(); + mSyncTask = null; // 同步完成后将任务实例置为null + sendBroadcast(""); // 发送同步完成的广播 + stopSelf(); // 停止当前服务 } }); - sendBroadcast(""); - mSyncTask.execute(); + sendBroadcast(""); // 发送开始同步的广播 + mSyncTask.execute(); // 执行异步同步任务 } } + /** + * 取消正在进行的同步操作 + * 如果有同步任务正在运行,则调用其取消方法 + */ private void cancelSync() { - if (mSyncTask != null) { - mSyncTask.cancelSync(); + if (mSyncTask != null) { // 检查是否有同步任务在运行 + mSyncTask.cancelSync(); // 调用任务的取消方法 } } + /** + * 服务创建时调用的方法 + * 初始化服务状态,将同步任务实例置为null + */ @Override public void onCreate() { - mSyncTask = null; + mSyncTask = null; // 确保服务创建时没有残留的同步任务 } + /** + * 处理启动服务的命令 + * 根据Intent中携带的动作类型,执行相应的同步操作 + * @param intent 启动服务的Intent,可能包含同步动作类型 + * @param flags 启动标志 + * @param startId 启动ID + * @return 服务启动模式,此处返回START_STICKY表示服务被终止后会自动重启 + */ @Override public int onStartCommand(Intent intent, int flags, int startId) { - Bundle bundle = intent.getExtras(); - if (bundle != null && bundle.containsKey(ACTION_STRING_NAME)) { - switch (bundle.getInt(ACTION_STRING_NAME, ACTION_INVALID)) { - case ACTION_START_SYNC: - startSync(); + Bundle bundle = intent.getExtras(); // 获取Intent携带的额外数据 + if (bundle != null && bundle.containsKey(ACTION_STRING_NAME)) { // 检查是否包含动作类型 + switch (bundle.getInt(ACTION_STRING_NAME, ACTION_INVALID)) { // 获取动作类型并处理 + case ACTION_START_SYNC: // 启动同步动作 + startSync(); // 调用启动同步方法 break; - case ACTION_CANCEL_SYNC: - cancelSync(); + case ACTION_CANCEL_SYNC: // 取消同步动作 + cancelSync(); // 调用取消同步方法 break; - default: + default: // 无效动作类型 break; } - return START_STICKY; + return START_STICKY; // 返回START_STICKY表示服务被终止后会自动重启 } - return super.onStartCommand(intent, flags, startId); + return super.onStartCommand(intent, flags, startId); // 处理默认情况 } + /** + * 系统内存不足时调用的方法 + * 取消正在进行的同步任务,释放资源 + */ @Override public void onLowMemory() { - if (mSyncTask != null) { - mSyncTask.cancelSync(); + if (mSyncTask != null) { // 检查是否有同步任务在运行 + mSyncTask.cancelSync(); // 取消同步任务以释放资源 } } + /** + * 绑定服务时调用的方法 + * 由于本服务不支持绑定,返回null + * @param intent 绑定意图 + * @return null,表示不支持绑定 + */ public IBinder onBind(Intent intent) { - return null; + return null; // 本服务不支持绑定,返回null } + /** + * 发送同步状态广播 + * 将同步进度消息封装到Intent中并发送广播 + * @param msg 同步进度消息 + */ public void sendBroadcast(String msg) { - mSyncProgress = msg; - Intent intent = new Intent(GTASK_SERVICE_BROADCAST_NAME); - intent.putExtra(GTASK_SERVICE_BROADCAST_IS_SYNCING, mSyncTask != null); - intent.putExtra(GTASK_SERVICE_BROADCAST_PROGRESS_MSG, msg); - sendBroadcast(intent); + mSyncProgress = msg; // 更新同步进度消息 + Intent intent = new Intent(GTASK_SERVICE_BROADCAST_NAME); // 创建广播Intent + intent.putExtra(GTASK_SERVICE_BROADCAST_IS_SYNCING, mSyncTask != null); // 添加是否正在同步的状态 + intent.putExtra(GTASK_SERVICE_BROADCAST_PROGRESS_MSG, msg); // 添加同步进度消息 + sendBroadcast(intent); // 发送广播 } + /** + * 静态方法:启动Google Tasks同步服务 + * 从Activity上下文启动同步服务并传递启动同步的动作类型 + * @param activity 调用此方法的Activity,提供上下文 + */ public static void startSync(Activity activity) { - GTaskManager.getInstance().setActivityContext(activity); - Intent intent = new Intent(activity, GTaskSyncService.class); - intent.putExtra(GTaskSyncService.ACTION_STRING_NAME, GTaskSyncService.ACTION_START_SYNC); - activity.startService(intent); + GTaskManager.getInstance().setActivityContext(activity); // 设置GTaskManager的Activity上下文 + Intent intent = new Intent(activity, GTaskSyncService.class); // 创建启动服务的Intent + intent.putExtra(GTaskSyncService.ACTION_STRING_NAME, GTaskSyncService.ACTION_START_SYNC); // 添加启动同步的动作类型 + activity.startService(intent); // 启动服务 } + /** + * 静态方法:取消Google Tasks同步服务 + * 从上下文启动服务并传递取消同步的动作类型 + * @param context 调用此方法的上下文 + */ public static void cancelSync(Context context) { - Intent intent = new Intent(context, GTaskSyncService.class); - intent.putExtra(GTaskSyncService.ACTION_STRING_NAME, GTaskSyncService.ACTION_CANCEL_SYNC); - context.startService(intent); + Intent intent = new Intent(context, GTaskSyncService.class); // 创建启动服务的Intent + intent.putExtra(GTaskSyncService.ACTION_STRING_NAME, GTaskSyncService.ACTION_CANCEL_SYNC); // 添加取消同步的动作类型 + context.startService(intent); // 启动服务 } + /** + * 静态方法:检查是否正在进行同步 + * @return true表示正在同步,false表示未同步 + */ public static boolean isSyncing() { - return mSyncTask != null; + return mSyncTask != null; // 通过检查同步任务实例是否存在判断是否正在同步 } + /** + * 静态方法:获取当前同步进度消息 + * @return 当前同步进度消息字符串 + */ public static String getProgressString() { - return mSyncProgress; + return mSyncProgress; // 返回当前同步进度消息 } -} +} \ No newline at end of file diff --git a/src/Notes-master/src/net/micode/notes/tool/BackupUtils.java b/src/Notes-master/src/net/micode/notes/tool/BackupUtils.java index 39f6ec4..3932b45 100644 --- a/src/Notes-master/src/net/micode/notes/tool/BackupUtils.java +++ b/src/Notes-master/src/net/micode/notes/tool/BackupUtils.java @@ -2,234 +2,277 @@ * Copyright (c) 2010-2011, The MiCode Open Source Community (www.micode.net) * * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at + * 您可以在遵守 License 的前提下使用本文件。 + * 您可以从以下地址获取 License 副本: * * http://www.apache.org/licenses/LICENSE-2.0 * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. + * 除非适用法律要求或书面同意,本软件根据 License 分发时“按现状”提供, + * 不附带任何明示或暗示的保证或条件。请参阅 License 了解具体的权限和限制。 */ -package net.micode.notes.tool; - -import android.content.Context; -import android.database.Cursor; -import android.os.Environment; -import android.text.TextUtils; -import android.text.format.DateFormat; -import android.util.Log; - -import net.micode.notes.R; -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 java.io.File; -import java.io.FileNotFoundException; -import java.io.FileOutputStream; -import java.io.IOException; -import java.io.PrintStream; - - +package net.micode.notes.tool; // 指定代码所属的包名 + +import android.content.Context; // 导入Context类,提供应用环境信息 +import android.database.Cursor; // 导入Cursor类,用于查询数据库结果集 +import android.os.Environment; // 导入Environment类,获取外部存储信息 +import android.text.TextUtils; // 导入TextUtils类,提供文本处理工具 +import android.text.format.DateFormat; // 导入DateFormat类,用于日期格式化 +import android.util.Log; // 导入Log类,用于日志记录 + +import net.micode.notes.R; // 导入R资源类,访问应用资源 +import net.micode.notes.data.Notes; // 导入Notes类,访问笔记数据结构 +import net.micode.notes.data.Notes.DataColumns; // 导入笔记数据列定义 +import net.micode.notes.data.Notes.DataConstants; // 导入笔记数据常量 +import net.micode.notes.data.Notes.NoteColumns; // 导入笔记列定义 + +import java.io.File; // 导入File类,用于文件操作 +import java.io.FileNotFoundException; // 导入文件未找到异常类 +import java.io.FileOutputStream; // 导入文件输出流类 +import java.io.IOException; // 导入IO异常类 +import java.io.PrintStream; // 导入打印流类,用于文本输出 + +/** + * 备份工具类,用于将笔记数据导出为文本文件 + * 支持导出到SD卡,提供备份状态反馈和文件管理功能 + */ public class BackupUtils { - private static final String TAG = "BackupUtils"; - // Singleton stuff + private static final String TAG = "BackupUtils"; // 日志标签 + // 单例模式实现 private static BackupUtils sInstance; + /** + * 获取BackupUtils的单例实例 + * @param context 应用上下文 + * @return BackupUtils实例 + */ public static synchronized BackupUtils getInstance(Context context) { - if (sInstance == null) { - sInstance = new BackupUtils(context); + if (sInstance == null) { // 检查实例是否已创建 + sInstance = new BackupUtils(context); // 创建新实例 } - return sInstance; + return sInstance; // 返回实例 } /** - * Following states are signs to represents backup or restore - * status + * 以下状态常量用于表示备份或恢复操作的状态 */ - // Currently, the sdcard is not mounted + // 当前SD卡未挂载 public static final int STATE_SD_CARD_UNMOUONTED = 0; - // The backup file not exist + // 备份文件不存在 public static final int STATE_BACKUP_FILE_NOT_EXIST = 1; - // The data is not well formated, may be changed by other programs + // 数据格式不正确,可能被其他程序修改 public static final int STATE_DATA_DESTROIED = 2; - // Some run-time exception which causes restore or backup fails + // 运行时异常导致备份或恢复失败 public static final int STATE_SYSTEM_ERROR = 3; - // Backup or restore success + // 备份或恢复成功 public static final int STATE_SUCCESS = 4; - private TextExport mTextExport; + private TextExport mTextExport; // 文本导出器实例 + /** + * 私有构造函数,初始化文本导出器 + * @param context 应用上下文 + */ private BackupUtils(Context context) { - mTextExport = new TextExport(context); + mTextExport = new TextExport(context); // 初始化文本导出器 } + /** + * 检查外部存储是否可用 + * @return 可用返回true,否则返回false + */ private static boolean externalStorageAvailable() { - return Environment.MEDIA_MOUNTED.equals(Environment.getExternalStorageState()); + return Environment.MEDIA_MOUNTED.equals(Environment.getExternalStorageState()); // 检查SD卡挂载状态 } + /** + * 导出笔记到文本文件 + * @return 导出状态码 + */ public int exportToText() { - return mTextExport.exportToText(); + return mTextExport.exportToText(); // 调用文本导出器的导出方法 } + /** + * 获取导出的文本文件名 + * @return 文件名 + */ public String getExportedTextFileName() { - return mTextExport.mFileName; + return mTextExport.mFileName; // 返回导出的文件名 } + /** + * 获取导出的文本文件目录 + * @return 文件目录路径 + */ public String getExportedTextFileDir() { - return mTextExport.mFileDirectory; + return mTextExport.mFileDirectory; // 返回导出的文件目录 } + /** + * 内部类:文本导出器,负责将笔记导出为文本格式 + */ private static class TextExport { + // 笔记查询投影,指定要查询的列 private static final String[] NOTE_PROJECTION = { - NoteColumns.ID, - NoteColumns.MODIFIED_DATE, - NoteColumns.SNIPPET, - NoteColumns.TYPE + NoteColumns.ID, // 笔记ID + NoteColumns.MODIFIED_DATE, // 修改日期 + NoteColumns.SNIPPET, // 摘要 + NoteColumns.TYPE // 类型 }; - private static final int NOTE_COLUMN_ID = 0; - - private static final int NOTE_COLUMN_MODIFIED_DATE = 1; - - private static final int NOTE_COLUMN_SNIPPET = 2; + // 笔记列索引常量 + private static final int NOTE_COLUMN_ID = 0; // ID列索引 + private static final int NOTE_COLUMN_MODIFIED_DATE = 1; // 修改日期列索引 + private static final int NOTE_COLUMN_SNIPPET = 2; // 摘要列索引 + // 数据查询投影,指定要查询的列 private static final String[] DATA_PROJECTION = { - DataColumns.CONTENT, - DataColumns.MIME_TYPE, - DataColumns.DATA1, - DataColumns.DATA2, - DataColumns.DATA3, - DataColumns.DATA4, + DataColumns.CONTENT, // 内容 + DataColumns.MIME_TYPE, // MIME类型 + DataColumns.DATA1, // 数据1(通话日期) + DataColumns.DATA2, // 数据2 + DataColumns.DATA3, // 数据3 + DataColumns.DATA4, // 数据4(电话号码) }; - private static final int DATA_COLUMN_CONTENT = 0; - - private static final int DATA_COLUMN_MIME_TYPE = 1; - - private static final int DATA_COLUMN_CALL_DATE = 2; - - private static final int DATA_COLUMN_PHONE_NUMBER = 4; + // 数据列索引常量 + private static final int DATA_COLUMN_CONTENT = 0; // 内容列索引 + private static final int DATA_COLUMN_MIME_TYPE = 1; // MIME类型列索引 + private static final int DATA_COLUMN_CALL_DATE = 2; // 通话日期列索引 + private static final int DATA_COLUMN_PHONE_NUMBER = 4; // 电话号码列索引 + // 文本格式数组,从资源文件获取 private final String [] TEXT_FORMAT; - private static final int FORMAT_FOLDER_NAME = 0; - private static final int FORMAT_NOTE_DATE = 1; - private static final int FORMAT_NOTE_CONTENT = 2; + // 格式索引常量 + private static final int FORMAT_FOLDER_NAME = 0; // 文件夹名称格式索引 + private static final int FORMAT_NOTE_DATE = 1; // 笔记日期格式索引 + private static final int FORMAT_NOTE_CONTENT = 2; // 笔记内容格式索引 - private Context mContext; - private String mFileName; - private String mFileDirectory; + private Context mContext; // 应用上下文 + private String mFileName; // 导出的文件名 + private String mFileDirectory; // 导出的文件目录 + /** + * 构造函数,初始化文本导出器 + * @param context 应用上下文 + */ public TextExport(Context context) { - TEXT_FORMAT = context.getResources().getStringArray(R.array.format_for_exported_note); - mContext = context; - mFileName = ""; - mFileDirectory = ""; + TEXT_FORMAT = context.getResources().getStringArray(R.array.format_for_exported_note); // 获取文本格式数组 + mContext = context; // 保存上下文 + mFileName = ""; // 初始化文件名 + mFileDirectory = ""; // 初始化文件目录 } + /** + * 获取指定索引的文本格式 + * @param id 格式索引 + * @return 格式字符串 + */ private String getFormat(int id) { - return TEXT_FORMAT[id]; + return TEXT_FORMAT[id]; // 返回指定索引的格式字符串 } /** - * Export the folder identified by folder id to text + * 将指定文件夹下的所有笔记导出为文本 + * @param folderId 文件夹ID + * @param ps 打印流,用于输出文本 */ private void exportFolderToText(String folderId, PrintStream ps) { - // Query notes belong to this folder + // 查询属于该文件夹的所有笔记 Cursor notesCursor = mContext.getContentResolver().query(Notes.CONTENT_NOTE_URI, NOTE_PROJECTION, NoteColumns.PARENT_ID + "=?", new String[] { - folderId + folderId }, null); - if (notesCursor != null) { - if (notesCursor.moveToFirst()) { + if (notesCursor != null) { // 检查游标是否有效 + if (notesCursor.moveToFirst()) { // 移动到第一条记录 do { - // Print note's last modified date + // 打印笔记的最后修改日期 ps.println(String.format(getFormat(FORMAT_NOTE_DATE), DateFormat.format( mContext.getString(R.string.format_datetime_mdhm), notesCursor.getLong(NOTE_COLUMN_MODIFIED_DATE)))); - // Query data belong to this note + // 查询属于该笔记的数据 String noteId = notesCursor.getString(NOTE_COLUMN_ID); - exportNoteToText(noteId, ps); - } while (notesCursor.moveToNext()); + exportNoteToText(noteId, ps); // 导出笔记内容 + } while (notesCursor.moveToNext()); // 移动到下一条记录 } - notesCursor.close(); + notesCursor.close(); // 关闭游标 } } /** - * Export note identified by id to a print stream + * 将指定ID的笔记导出为文本 + * @param noteId 笔记ID + * @param ps 打印流,用于输出文本 */ private void exportNoteToText(String noteId, PrintStream ps) { + // 查询属于该笔记的所有数据 Cursor dataCursor = mContext.getContentResolver().query(Notes.CONTENT_DATA_URI, DATA_PROJECTION, DataColumns.NOTE_ID + "=?", new String[] { - noteId + noteId }, null); - if (dataCursor != null) { - if (dataCursor.moveToFirst()) { + if (dataCursor != null) { // 检查游标是否有效 + if (dataCursor.moveToFirst()) { // 移动到第一条记录 do { - String mimeType = dataCursor.getString(DATA_COLUMN_MIME_TYPE); - if (DataConstants.CALL_NOTE.equals(mimeType)) { - // Print phone number + String mimeType = dataCursor.getString(DATA_COLUMN_MIME_TYPE); // 获取MIME类型 + if (DataConstants.CALL_NOTE.equals(mimeType)) { // 处理通话记录类型 + // 打印电话号码 String phoneNumber = dataCursor.getString(DATA_COLUMN_PHONE_NUMBER); long callDate = dataCursor.getLong(DATA_COLUMN_CALL_DATE); String location = dataCursor.getString(DATA_COLUMN_CONTENT); - if (!TextUtils.isEmpty(phoneNumber)) { + if (!TextUtils.isEmpty(phoneNumber)) { // 检查电话号码是否为空 ps.println(String.format(getFormat(FORMAT_NOTE_CONTENT), - phoneNumber)); + phoneNumber)); // 打印电话号码 } - // Print call date + // 打印通话日期 ps.println(String.format(getFormat(FORMAT_NOTE_CONTENT), DateFormat .format(mContext.getString(R.string.format_datetime_mdhm), callDate))); - // Print call attachment location - if (!TextUtils.isEmpty(location)) { + // 打印通话附件位置 + if (!TextUtils.isEmpty(location)) { // 检查位置信息是否为空 ps.println(String.format(getFormat(FORMAT_NOTE_CONTENT), - location)); + location)); // 打印位置信息 } - } else if (DataConstants.NOTE.equals(mimeType)) { - String content = dataCursor.getString(DATA_COLUMN_CONTENT); - if (!TextUtils.isEmpty(content)) { + } else if (DataConstants.NOTE.equals(mimeType)) { // 处理普通笔记类型 + String content = dataCursor.getString(DATA_COLUMN_CONTENT); // 获取笔记内容 + if (!TextUtils.isEmpty(content)) { // 检查内容是否为空 ps.println(String.format(getFormat(FORMAT_NOTE_CONTENT), - content)); + content)); // 打印笔记内容 } } - } while (dataCursor.moveToNext()); + } while (dataCursor.moveToNext()); // 移动到下一条记录 } - dataCursor.close(); + dataCursor.close(); // 关闭游标 } - // print a line separator between note + // 在笔记之间打印行分隔符 try { ps.write(new byte[] { Character.LINE_SEPARATOR, Character.LETTER_NUMBER }); } catch (IOException e) { - Log.e(TAG, e.toString()); + Log.e(TAG, e.toString()); // 记录异常信息 } } /** - * Note will be exported as text which is user readable + * 将笔记导出为用户可读的文本格式 + * @return 导出状态码 */ public int exportToText() { - if (!externalStorageAvailable()) { - Log.d(TAG, "Media was not mounted"); - return STATE_SD_CARD_UNMOUONTED; + if (!externalStorageAvailable()) { // 检查外部存储是否可用 + Log.d(TAG, "Media was not mounted"); // 记录日志 + return STATE_SD_CARD_UNMOUONTED; // 返回SD卡未挂载状态 } - PrintStream ps = getExportToTextPrintStream(); - if (ps == null) { - Log.e(TAG, "get print stream error"); - return STATE_SYSTEM_ERROR; + PrintStream ps = getExportToTextPrintStream(); // 获取打印流 + if (ps == null) { // 检查打印流是否获取成功 + Log.e(TAG, "get print stream error"); // 记录错误日志 + return STATE_SYSTEM_ERROR; // 返回系统错误状态 } - // First export folder and its notes + // 首先导出文件夹及其包含的笔记 Cursor folderCursor = mContext.getContentResolver().query( Notes.CONTENT_NOTE_URI, NOTE_PROJECTION, @@ -237,108 +280,112 @@ public class BackupUtils { + NoteColumns.PARENT_ID + "<>" + Notes.ID_TRASH_FOLER + ") OR " + NoteColumns.ID + "=" + Notes.ID_CALL_RECORD_FOLDER, null, null); - if (folderCursor != null) { - if (folderCursor.moveToFirst()) { + if (folderCursor != null) { // 检查游标是否有效 + if (folderCursor.moveToFirst()) { // 移动到第一条记录 do { - // Print folder's name + // 打印文件夹名称 String folderName = ""; if(folderCursor.getLong(NOTE_COLUMN_ID) == Notes.ID_CALL_RECORD_FOLDER) { - folderName = mContext.getString(R.string.call_record_folder_name); + folderName = mContext.getString(R.string.call_record_folder_name); // 获取通话记录文件夹名称 } else { - folderName = folderCursor.getString(NOTE_COLUMN_SNIPPET); + folderName = folderCursor.getString(NOTE_COLUMN_SNIPPET); // 获取文件夹摘要作为名称 } - if (!TextUtils.isEmpty(folderName)) { - ps.println(String.format(getFormat(FORMAT_FOLDER_NAME), folderName)); + if (!TextUtils.isEmpty(folderName)) { // 检查文件夹名称是否为空 + ps.println(String.format(getFormat(FORMAT_FOLDER_NAME), folderName)); // 打印文件夹名称 } - String folderId = folderCursor.getString(NOTE_COLUMN_ID); - exportFolderToText(folderId, ps); - } while (folderCursor.moveToNext()); + String folderId = folderCursor.getString(NOTE_COLUMN_ID); // 获取文件夹ID + exportFolderToText(folderId, ps); // 导出文件夹中的笔记 + } while (folderCursor.moveToNext()); // 移动到下一条记录 } - folderCursor.close(); + folderCursor.close(); // 关闭游标 } - // Export notes in root's folder + // 导出根文件夹中的笔记 Cursor noteCursor = mContext.getContentResolver().query( Notes.CONTENT_NOTE_URI, NOTE_PROJECTION, NoteColumns.TYPE + "=" + +Notes.TYPE_NOTE + " AND " + NoteColumns.PARENT_ID + "=0", null, null); - if (noteCursor != null) { - if (noteCursor.moveToFirst()) { + if (noteCursor != null) { // 检查游标是否有效 + if (noteCursor.moveToFirst()) { // 移动到第一条记录 do { + // 打印笔记的最后修改日期 ps.println(String.format(getFormat(FORMAT_NOTE_DATE), DateFormat.format( mContext.getString(R.string.format_datetime_mdhm), noteCursor.getLong(NOTE_COLUMN_MODIFIED_DATE)))); - // Query data belong to this note + // 查询属于该笔记的数据 String noteId = noteCursor.getString(NOTE_COLUMN_ID); - exportNoteToText(noteId, ps); - } while (noteCursor.moveToNext()); + exportNoteToText(noteId, ps); // 导出笔记内容 + } while (noteCursor.moveToNext()); // 移动到下一条记录 } - noteCursor.close(); + noteCursor.close(); // 关闭游标 } - ps.close(); + ps.close(); // 关闭打印流 - return STATE_SUCCESS; + return STATE_SUCCESS; // 返回成功状态 } /** - * Get a print stream pointed to the file {@generateExportedTextFile} + * 获取指向导出文本文件的打印流 + * @return 打印流对象,失败时返回null */ private PrintStream getExportToTextPrintStream() { File file = generateFileMountedOnSDcard(mContext, R.string.file_path, - R.string.file_name_txt_format); - if (file == null) { - Log.e(TAG, "create file to exported failed"); - return null; + R.string.file_name_txt_format); // 生成SD卡上的文件 + if (file == null) { // 检查文件是否生成成功 + Log.e(TAG, "create file to exported failed"); // 记录错误日志 + return null; // 返回null表示失败 } - mFileName = file.getName(); - mFileDirectory = mContext.getString(R.string.file_path); - PrintStream ps = null; + mFileName = file.getName(); // 保存文件名 + mFileDirectory = mContext.getString(R.string.file_path); // 保存文件目录 + PrintStream ps = null; // 初始化打印流 try { - FileOutputStream fos = new FileOutputStream(file); - ps = new PrintStream(fos); - } catch (FileNotFoundException e) { - e.printStackTrace(); - return null; - } catch (NullPointerException e) { - e.printStackTrace(); - return null; + FileOutputStream fos = new FileOutputStream(file); // 创建文件输出流 + ps = new PrintStream(fos); // 创建打印流 + } catch (FileNotFoundException e) { // 处理文件未找到异常 + e.printStackTrace(); // 打印异常堆栈 + return null; // 返回null表示失败 + } catch (NullPointerException e) { // 处理空指针异常 + e.printStackTrace(); // 打印异常堆栈 + return null; // 返回null表示失败 } - return ps; + return ps; // 返回打印流 } } /** - * Generate the text file to store imported data + * 生成挂载在SD卡上的文件 + * @param context 应用上下文 + * @param filePathResId 文件路径资源ID + * @param fileNameFormatResId 文件名格式资源ID + * @return 生成的文件对象,失败时返回null */ private static File generateFileMountedOnSDcard(Context context, int filePathResId, int fileNameFormatResId) { - StringBuilder sb = new StringBuilder(); - sb.append(Environment.getExternalStorageDirectory()); - sb.append(context.getString(filePathResId)); - File filedir = new File(sb.toString()); + StringBuilder sb = new StringBuilder(); // 创建字符串构建器 + sb.append(Environment.getExternalStorageDirectory()); // 添加外部存储目录 + sb.append(context.getString(filePathResId)); // 添加文件路径 + File filedir = new File(sb.toString()); // 创建目录对象 sb.append(context.getString( fileNameFormatResId, DateFormat.format(context.getString(R.string.format_date_ymd), - System.currentTimeMillis()))); - File file = new File(sb.toString()); + System.currentTimeMillis()))); // 添加带日期的文件名 + File file = new File(sb.toString()); // 创建文件对象 try { - if (!filedir.exists()) { - filedir.mkdir(); + if (!filedir.exists()) { // 检查目录是否存在 + filedir.mkdir(); // 创建目录 } - if (!file.exists()) { - file.createNewFile(); + if (!file.exists()) { // 检查文件是否存在 + file.createNewFile(); // 创建文件 } - return file; - } catch (SecurityException e) { - e.printStackTrace(); - } catch (IOException e) { - e.printStackTrace(); + return file; // 返回文件对象 + } catch (SecurityException e) { // 处理安全异常 + e.printStackTrace(); // 打印异常堆栈 + } catch (IOException e) { // 处理IO异常 + e.printStackTrace(); // 打印异常堆栈 } - return null; + return null; // 返回null表示失败 } -} - - +} \ No newline at end of file diff --git a/src/Notes-master/src/net/micode/notes/tool/DataUtils.java b/src/Notes-master/src/net/micode/notes/tool/DataUtils.java index 2a14982..652740b 100644 --- a/src/Notes-master/src/net/micode/notes/tool/DataUtils.java +++ b/src/Notes-master/src/net/micode/notes/tool/DataUtils.java @@ -2,294 +2,379 @@ * Copyright (c) 2010-2011, The MiCode Open Source Community (www.micode.net) * * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at + * 您可以在遵守 License 的前提下使用本文件。 + * 您可以从以下地址获取 License 副本: * * http://www.apache.org/licenses/LICENSE-2.0 * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. + * 除非适用法律要求或书面同意,本软件根据 License 分发时“按现状”提供, + * 不附带任何明示或暗示的保证或条件。请参阅 License 了解具体的权限和限制。 */ -package net.micode.notes.tool; +package net.micode.notes.tool; // 指定代码所属的包名 -import android.content.ContentProviderOperation; -import android.content.ContentProviderResult; -import android.content.ContentResolver; -import android.content.ContentUris; -import android.content.ContentValues; -import android.content.OperationApplicationException; -import android.database.Cursor; -import android.os.RemoteException; -import android.util.Log; +import android.content.ContentProviderOperation; // 导入内容提供者操作类 +import android.content.ContentProviderResult; // 导入内容提供者结果类 +import android.content.ContentResolver; // 导入内容解析器类 +import android.content.ContentUris; // 导入内容URI工具类 +import android.content.ContentValues; // 导入内容值类 +import android.content.OperationApplicationException; // 导入操作应用异常类 +import android.database.Cursor; // 导入数据库游标类 +import android.os.RemoteException; // 导入远程异常类 +import android.util.Log; // 导入日志记录类 -import net.micode.notes.data.Notes; -import net.micode.notes.data.Notes.CallNote; -import net.micode.notes.data.Notes.NoteColumns; -import net.micode.notes.ui.NotesListAdapter.AppWidgetAttribute; - -import java.util.ArrayList; -import java.util.HashSet; +import net.micode.notes.data.Notes; // 导入笔记数据类 +import net.micode.notes.data.Notes.CallNote; // 导入通话记录数据类 +import net.micode.notes.data.Notes.NoteColumns; // 导入笔记列定义类 +import net.micode.notes.ui.NotesListAdapter.AppWidgetAttribute; // 导入应用小部件属性类 +import java.util.ArrayList; // 导入ArrayList类 +import java.util.HashSet; // 导入HashSet类 +/** + * 数据工具类,提供与笔记数据操作相关的实用方法 + * 包括批量删除、移动笔记、查询文件夹和笔记信息等功能 + */ public class DataUtils { - public static final String TAG = "DataUtils"; + public static final String TAG = "DataUtils"; // 日志标签 + + /** + * 批量删除笔记 + * @param resolver 内容解析器 + * @param ids 要删除的笔记ID集合 + * @return 删除成功返回true,失败返回false + */ public static boolean batchDeleteNotes(ContentResolver resolver, HashSet ids) { - if (ids == null) { + if (ids == null) { // 检查ID集合是否为空 Log.d(TAG, "the ids is null"); - return true; + return true; // 空集合视为操作成功 } - if (ids.size() == 0) { + if (ids.size() == 0) { // 检查ID集合是否为空 Log.d(TAG, "no id is in the hashset"); - return true; + return true; // 空集合视为操作成功 } - ArrayList operationList = new ArrayList(); - for (long id : ids) { - if(id == Notes.ID_ROOT_FOLDER) { + ArrayList operationList = new ArrayList<>(); // 创建操作列表 + for (long id : ids) { // 遍历ID集合 + if(id == Notes.ID_ROOT_FOLDER) { // 检查是否为根文件夹 Log.e(TAG, "Don't delete system folder root"); - continue; + continue; // 跳过根文件夹,不删除 } ContentProviderOperation.Builder builder = ContentProviderOperation - .newDelete(ContentUris.withAppendedId(Notes.CONTENT_NOTE_URI, id)); - operationList.add(builder.build()); + .newDelete(ContentUris.withAppendedId(Notes.CONTENT_NOTE_URI, id)); // 创建删除操作 + operationList.add(builder.build()); // 将操作添加到列表 } try { - ContentProviderResult[] results = resolver.applyBatch(Notes.AUTHORITY, operationList); - if (results == null || results.length == 0 || results[0] == null) { + ContentProviderResult[] results = resolver.applyBatch(Notes.AUTHORITY, operationList); // 执行批量操作 + if (results == null || results.length == 0 || results[0] == null) { // 检查操作结果 Log.d(TAG, "delete notes failed, ids:" + ids.toString()); - return false; + return false; // 操作失败 } - return true; - } catch (RemoteException e) { + return true; // 操作成功 + } catch (RemoteException e) { // 处理远程异常 Log.e(TAG, String.format("%s: %s", e.toString(), e.getMessage())); - } catch (OperationApplicationException e) { + } catch (OperationApplicationException e) { // 处理操作应用异常 Log.e(TAG, String.format("%s: %s", e.toString(), e.getMessage())); } - return false; + return false; // 发生异常,操作失败 } + /** + * 将笔记移动到指定文件夹 + * @param resolver 内容解析器 + * @param id 笔记ID + * @param srcFolderId 源文件夹ID + * @param desFolderId 目标文件夹ID + */ public static void moveNoteToFoler(ContentResolver resolver, long id, long srcFolderId, long desFolderId) { - ContentValues values = new ContentValues(); - values.put(NoteColumns.PARENT_ID, desFolderId); - values.put(NoteColumns.ORIGIN_PARENT_ID, srcFolderId); - values.put(NoteColumns.LOCAL_MODIFIED, 1); - resolver.update(ContentUris.withAppendedId(Notes.CONTENT_NOTE_URI, id), values, null, null); + ContentValues values = new ContentValues(); // 创建内容值对象 + values.put(NoteColumns.PARENT_ID, desFolderId); // 设置目标文件夹ID + values.put(NoteColumns.ORIGIN_PARENT_ID, srcFolderId); // 设置源文件夹ID + values.put(NoteColumns.LOCAL_MODIFIED, 1); // 标记为本地已修改 + resolver.update(ContentUris.withAppendedId(Notes.CONTENT_NOTE_URI, id), values, null, null); // 更新笔记 } + /** + * 批量将笔记移动到指定文件夹 + * @param resolver 内容解析器 + * @param ids 要移动的笔记ID集合 + * @param folderId 目标文件夹ID + * @return 移动成功返回true,失败返回false + */ public static boolean batchMoveToFolder(ContentResolver resolver, HashSet ids, - long folderId) { - if (ids == null) { + long folderId) { + if (ids == null) { // 检查ID集合是否为空 Log.d(TAG, "the ids is null"); - return true; + return true; // 空集合视为操作成功 } - ArrayList operationList = new ArrayList(); - for (long id : ids) { + ArrayList operationList = new ArrayList<>(); // 创建操作列表 + for (long id : ids) { // 遍历ID集合 ContentProviderOperation.Builder builder = ContentProviderOperation - .newUpdate(ContentUris.withAppendedId(Notes.CONTENT_NOTE_URI, id)); - builder.withValue(NoteColumns.PARENT_ID, folderId); - builder.withValue(NoteColumns.LOCAL_MODIFIED, 1); - operationList.add(builder.build()); + .newUpdate(ContentUris.withAppendedId(Notes.CONTENT_NOTE_URI, id)); // 创建更新操作 + builder.withValue(NoteColumns.PARENT_ID, folderId); // 设置目标文件夹ID + builder.withValue(NoteColumns.LOCAL_MODIFIED, 1); // 标记为本地已修改 + operationList.add(builder.build()); // 将操作添加到列表 } try { - ContentProviderResult[] results = resolver.applyBatch(Notes.AUTHORITY, operationList); - if (results == null || results.length == 0 || results[0] == null) { + ContentProviderResult[] results = resolver.applyBatch(Notes.AUTHORITY, operationList); // 执行批量操作 + if (results == null || results.length == 0 || results[0] == null) { // 检查操作结果 Log.d(TAG, "delete notes failed, ids:" + ids.toString()); - return false; + return false; // 操作失败 } - return true; - } catch (RemoteException e) { + return true; // 操作成功 + } catch (RemoteException e) { // 处理远程异常 Log.e(TAG, String.format("%s: %s", e.toString(), e.getMessage())); - } catch (OperationApplicationException e) { + } catch (OperationApplicationException e) { // 处理操作应用异常 Log.e(TAG, String.format("%s: %s", e.toString(), e.getMessage())); } - return false; + return false; // 发生异常,操作失败 } /** - * Get the all folder count except system folders {@link Notes#TYPE_SYSTEM}} + * 获取用户文件夹数量(不包括系统文件夹) + * @param resolver 内容解析器 + * @return 用户文件夹数量 */ public static int getUserFolderCount(ContentResolver resolver) { - Cursor cursor =resolver.query(Notes.CONTENT_NOTE_URI, - new String[] { "COUNT(*)" }, - NoteColumns.TYPE + "=? AND " + NoteColumns.PARENT_ID + "<>?", - new String[] { String.valueOf(Notes.TYPE_FOLDER), String.valueOf(Notes.ID_TRASH_FOLER)}, - null); + Cursor cursor = resolver.query(Notes.CONTENT_NOTE_URI, // 查询笔记表 + new String[] { "COUNT(*)" }, // 查询数量 + NoteColumns.TYPE + "=? AND " + NoteColumns.PARENT_ID + "<>?", // 查询条件:类型为文件夹且不在回收站 + new String[] { String.valueOf(Notes.TYPE_FOLDER), String.valueOf(Notes.ID_TRASH_FOLER)}, // 查询参数 + null); // 排序方式 - int count = 0; - if(cursor != null) { - if(cursor.moveToFirst()) { + int count = 0; // 初始化数量为0 + if(cursor != null) { // 检查游标是否有效 + if(cursor.moveToFirst()) { // 移动到第一条记录 try { - count = cursor.getInt(0); - } catch (IndexOutOfBoundsException e) { + count = cursor.getInt(0); // 获取数量值 + } catch (IndexOutOfBoundsException e) { // 处理索引越界异常 Log.e(TAG, "get folder count failed:" + e.toString()); } finally { - cursor.close(); + cursor.close(); // 关闭游标 } } } - return count; + return count; // 返回文件夹数量 } + /** + * 检查笔记是否在数据库中可见(存在且不在回收站) + * @param resolver 内容解析器 + * @param noteId 笔记ID + * @param type 笔记类型 + * @return 可见返回true,否则返回false + */ public static boolean visibleInNoteDatabase(ContentResolver resolver, long noteId, int type) { - Cursor cursor = resolver.query(ContentUris.withAppendedId(Notes.CONTENT_NOTE_URI, noteId), - null, - NoteColumns.TYPE + "=? AND " + NoteColumns.PARENT_ID + "<>" + Notes.ID_TRASH_FOLER, - new String [] {String.valueOf(type)}, - null); + Cursor cursor = resolver.query(ContentUris.withAppendedId(Notes.CONTENT_NOTE_URI, noteId), // 查询指定笔记 + null, // 查询所有列 + NoteColumns.TYPE + "=? AND " + NoteColumns.PARENT_ID + "<>" + Notes.ID_TRASH_FOLER, // 查询条件:类型匹配且不在回收站 + new String [] {String.valueOf(type)}, // 查询参数 + null); // 排序方式 - boolean exist = false; - if (cursor != null) { - if (cursor.getCount() > 0) { - exist = true; + boolean exist = false; // 初始化存在标志为false + if (cursor != null) { // 检查游标是否有效 + if (cursor.getCount() > 0) { // 检查记录数量 + exist = true; // 存在记录,设置标志为true } - cursor.close(); + cursor.close(); // 关闭游标 } - return exist; + return exist; // 返回存在标志 } + /** + * 检查笔记是否存在于数据库中 + * @param resolver 内容解析器 + * @param noteId 笔记ID + * @return 存在返回true,否则返回false + */ public static boolean existInNoteDatabase(ContentResolver resolver, long noteId) { - Cursor cursor = resolver.query(ContentUris.withAppendedId(Notes.CONTENT_NOTE_URI, noteId), - null, null, null, null); + Cursor cursor = resolver.query(ContentUris.withAppendedId(Notes.CONTENT_NOTE_URI, noteId), // 查询指定笔记 + null, // 查询所有列 + null, // 无查询条件 + null, // 无查询参数 + null); // 排序方式 - boolean exist = false; - if (cursor != null) { - if (cursor.getCount() > 0) { - exist = true; + boolean exist = false; // 初始化存在标志为false + if (cursor != null) { // 检查游标是否有效 + if (cursor.getCount() > 0) { // 检查记录数量 + exist = true; // 存在记录,设置标志为true } - cursor.close(); + cursor.close(); // 关闭游标 } - return exist; + return exist; // 返回存在标志 } + /** + * 检查数据项是否存在于数据库中 + * @param resolver 内容解析器 + * @param dataId 数据ID + * @return 存在返回true,否则返回false + */ public static boolean existInDataDatabase(ContentResolver resolver, long dataId) { - Cursor cursor = resolver.query(ContentUris.withAppendedId(Notes.CONTENT_DATA_URI, dataId), - null, null, null, null); + Cursor cursor = resolver.query(ContentUris.withAppendedId(Notes.CONTENT_DATA_URI, dataId), // 查询指定数据项 + null, // 查询所有列 + null, // 无查询条件 + null, // 无查询参数 + null); // 排序方式 - boolean exist = false; - if (cursor != null) { - if (cursor.getCount() > 0) { - exist = true; + boolean exist = false; // 初始化存在标志为false + if (cursor != null) { // 检查游标是否有效 + if (cursor.getCount() > 0) { // 检查记录数量 + exist = true; // 存在记录,设置标志为true } - cursor.close(); + cursor.close(); // 关闭游标 } - return exist; + return exist; // 返回存在标志 } + /** + * 检查可见文件夹名称是否已存在 + * @param resolver 内容解析器 + * @param name 文件夹名称 + * @return 存在返回true,否则返回false + */ public static boolean checkVisibleFolderName(ContentResolver resolver, String name) { - Cursor cursor = resolver.query(Notes.CONTENT_NOTE_URI, null, - NoteColumns.TYPE + "=" + Notes.TYPE_FOLDER + - " AND " + NoteColumns.PARENT_ID + "<>" + Notes.ID_TRASH_FOLER + - " AND " + NoteColumns.SNIPPET + "=?", - new String[] { name }, null); - boolean exist = false; - if(cursor != null) { - if(cursor.getCount() > 0) { - exist = true; + Cursor cursor = resolver.query(Notes.CONTENT_NOTE_URI, null, // 查询笔记表 + NoteColumns.TYPE + "=" + Notes.TYPE_FOLDER + // 查询条件:类型为文件夹 + " AND " + NoteColumns.PARENT_ID + "<>" + Notes.ID_TRASH_FOLER + // 且不在回收站 + " AND " + NoteColumns.SNIPPET + "=?", // 且名称匹配 + new String[] { name }, null); // 查询参数 + boolean exist = false; // 初始化存在标志为false + if(cursor != null) { // 检查游标是否有效 + if(cursor.getCount() > 0) { // 检查记录数量 + exist = true; // 存在记录,设置标志为true } - cursor.close(); + cursor.close(); // 关闭游标 } - return exist; + return exist; // 返回存在标志 } + /** + * 获取指定文件夹下的所有笔记小部件属性 + * @param resolver 内容解析器 + * @param folderId 文件夹ID + * @return 小部件属性集合 + */ public static HashSet getFolderNoteWidget(ContentResolver resolver, long folderId) { - Cursor c = resolver.query(Notes.CONTENT_NOTE_URI, - new String[] { NoteColumns.WIDGET_ID, NoteColumns.WIDGET_TYPE }, - NoteColumns.PARENT_ID + "=?", - new String[] { String.valueOf(folderId) }, - null); + Cursor c = resolver.query(Notes.CONTENT_NOTE_URI, // 查询笔记表 + new String[] { NoteColumns.WIDGET_ID, NoteColumns.WIDGET_TYPE }, // 查询小部件ID和类型 + NoteColumns.PARENT_ID + "=?", // 查询条件:父文件夹ID匹配 + new String[] { String.valueOf(folderId) }, // 查询参数 + null); // 排序方式 - HashSet set = null; - if (c != null) { - if (c.moveToFirst()) { - set = new HashSet(); + HashSet set = null; // 初始化集合为null + if (c != null) { // 检查游标是否有效 + if (c.moveToFirst()) { // 移动到第一条记录 + set = new HashSet<>(); // 创建集合 do { try { - AppWidgetAttribute widget = new AppWidgetAttribute(); - widget.widgetId = c.getInt(0); - widget.widgetType = c.getInt(1); - set.add(widget); - } catch (IndexOutOfBoundsException e) { + AppWidgetAttribute widget = new AppWidgetAttribute(); // 创建小部件属性对象 + widget.widgetId = c.getInt(0); // 设置小部件ID + widget.widgetType = c.getInt(1); // 设置小部件类型 + set.add(widget); // 将小部件属性添加到集合 + } catch (IndexOutOfBoundsException e) { // 处理索引越界异常 Log.e(TAG, e.toString()); } - } while (c.moveToNext()); + } while (c.moveToNext()); // 移动到下一条记录 } - c.close(); + c.close(); // 关闭游标 } - return set; + return set; // 返回集合 } + /** + * 根据笔记ID获取通话号码 + * @param resolver 内容解析器 + * @param noteId 笔记ID + * @return 通话号码,不存在则返回空字符串 + */ public static String getCallNumberByNoteId(ContentResolver resolver, long noteId) { - Cursor cursor = resolver.query(Notes.CONTENT_DATA_URI, - new String [] { CallNote.PHONE_NUMBER }, - CallNote.NOTE_ID + "=? AND " + CallNote.MIME_TYPE + "=?", - new String [] { String.valueOf(noteId), CallNote.CONTENT_ITEM_TYPE }, - null); + Cursor cursor = resolver.query(Notes.CONTENT_DATA_URI, // 查询数据表 + new String [] { CallNote.PHONE_NUMBER }, // 查询电话号码列 + CallNote.NOTE_ID + "=? AND " + CallNote.MIME_TYPE + "=?", // 查询条件:笔记ID匹配且类型为通话记录 + new String [] { String.valueOf(noteId), CallNote.CONTENT_ITEM_TYPE }, // 查询参数 + null); // 排序方式 - if (cursor != null && cursor.moveToFirst()) { + if (cursor != null && cursor.moveToFirst()) { // 检查游标是否有效并移动到第一条记录 try { - return cursor.getString(0); - } catch (IndexOutOfBoundsException e) { + return cursor.getString(0); // 返回电话号码 + } catch (IndexOutOfBoundsException e) { // 处理索引越界异常 Log.e(TAG, "Get call number fails " + e.toString()); } finally { - cursor.close(); + cursor.close(); // 关闭游标 } } - return ""; + return ""; // 未找到返回空字符串 } + /** + * 根据电话号码和通话日期获取笔记ID + * @param resolver 内容解析器 + * @param phoneNumber 电话号码 + * @param callDate 通话日期 + * @return 笔记ID,不存在则返回0 + */ public static long getNoteIdByPhoneNumberAndCallDate(ContentResolver resolver, String phoneNumber, long callDate) { - Cursor cursor = resolver.query(Notes.CONTENT_DATA_URI, - new String [] { CallNote.NOTE_ID }, - CallNote.CALL_DATE + "=? AND " + CallNote.MIME_TYPE + "=? AND PHONE_NUMBERS_EQUAL(" - + CallNote.PHONE_NUMBER + ",?)", - new String [] { String.valueOf(callDate), CallNote.CONTENT_ITEM_TYPE, phoneNumber }, - null); + Cursor cursor = resolver.query(Notes.CONTENT_DATA_URI, // 查询数据表 + new String [] { CallNote.NOTE_ID }, // 查询笔记ID列 + CallNote.CALL_DATE + "=? AND " + CallNote.MIME_TYPE + "=? AND PHONE_NUMBERS_EQUAL(" // 查询条件:日期匹配、类型为通话记录且电话号码相等 + + CallNote.PHONE_NUMBER + ",?)", + new String [] { String.valueOf(callDate), CallNote.CONTENT_ITEM_TYPE, phoneNumber }, // 查询参数 + null); // 排序方式 - if (cursor != null) { - if (cursor.moveToFirst()) { + if (cursor != null) { // 检查游标是否有效 + if (cursor.moveToFirst()) { // 移动到第一条记录 try { - return cursor.getLong(0); - } catch (IndexOutOfBoundsException e) { + return cursor.getLong(0); // 返回笔记ID + } catch (IndexOutOfBoundsException e) { // 处理索引越界异常 Log.e(TAG, "Get call note id fails " + e.toString()); } } - cursor.close(); + cursor.close(); // 关闭游标 } - return 0; + return 0; // 未找到返回0 } + /** + * 根据笔记ID获取摘要 + * @param resolver 内容解析器 + * @param noteId 笔记ID + * @return 摘要内容 + * @throws IllegalArgumentException 笔记不存在时抛出异常 + */ public static String getSnippetById(ContentResolver resolver, long noteId) { - Cursor cursor = resolver.query(Notes.CONTENT_NOTE_URI, - new String [] { NoteColumns.SNIPPET }, - NoteColumns.ID + "=?", - new String [] { String.valueOf(noteId)}, - null); + Cursor cursor = resolver.query(Notes.CONTENT_NOTE_URI, // 查询笔记表 + new String [] { NoteColumns.SNIPPET }, // 查询摘要列 + NoteColumns.ID + "=?", // 查询条件:笔记ID匹配 + new String [] { String.valueOf(noteId)}, // 查询参数 + null); // 排序方式 - if (cursor != null) { - String snippet = ""; - if (cursor.moveToFirst()) { - snippet = cursor.getString(0); + if (cursor != null) { // 检查游标是否有效 + String snippet = ""; // 初始化摘要为空字符串 + if (cursor.moveToFirst()) { // 移动到第一条记录 + snippet = cursor.getString(0); // 获取摘要内容 } - cursor.close(); - return snippet; + cursor.close(); // 关闭游标 + return snippet; // 返回摘要 } - throw new IllegalArgumentException("Note is not found with id: " + noteId); + throw new IllegalArgumentException("Note is not found with id: " + noteId); // 未找到笔记,抛出异常 } + /** + * 格式化摘要内容(截取第一行) + * @param snippet 原始摘要 + * @return 格式化后的摘要 + */ public static String getFormattedSnippet(String snippet) { - if (snippet != null) { - snippet = snippet.trim(); - int index = snippet.indexOf('\n'); - if (index != -1) { - snippet = snippet.substring(0, index); + if (snippet != null) { // 检查摘要是否为空 + snippet = snippet.trim(); // 去除前后空格 + int index = snippet.indexOf('\n'); // 查找换行符位置 + if (index != -1) { // 如果存在换行符 + snippet = snippet.substring(0, index); // 截取第一行 } } - return snippet; + return snippet; // 返回格式化后的摘要 } -} +} \ No newline at end of file diff --git a/src/Notes-master/src/net/micode/notes/tool/GTaskStringUtils.java b/src/Notes-master/src/net/micode/notes/tool/GTaskStringUtils.java index 666b729..38f639b 100644 --- a/src/Notes-master/src/net/micode/notes/tool/GTaskStringUtils.java +++ b/src/Notes-master/src/net/micode/notes/tool/GTaskStringUtils.java @@ -2,112 +2,118 @@ * Copyright (c) 2010-2011, The MiCode Open Source Community (www.micode.net) * * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at + * 您可以在遵守 License 的前提下使用本文件。 + * 您可以从以下地址获取 License 副本: * * http://www.apache.org/licenses/LICENSE-2.0 * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. + * 除非适用法律要求或书面同意,本软件根据 License 分发时“按现状”提供, + * 不附带任何明示或暗示的保证或条件。请参阅 License 了解具体的权限和限制。 */ -package net.micode.notes.tool; +package net.micode.notes.tool; // 指定代码所属的包名 +/** + * Google Tasks 相关字符串常量工具类 + * 定义与Google Tasks服务交互时使用的JSON字段名、文件夹名称、元数据标识等常量 + */ public class GTaskStringUtils { + // --------------------------- JSON 字段常量 --------------------------- // + // 操作ID字段名 public final static String GTASK_JSON_ACTION_ID = "action_id"; - + // 操作列表字段名 public final static String GTASK_JSON_ACTION_LIST = "action_list"; - + // 操作类型字段名 public final static String GTASK_JSON_ACTION_TYPE = "action_type"; - + // 创建操作类型值 public final static String GTASK_JSON_ACTION_TYPE_CREATE = "create"; - + // 获取全部数据操作类型值 public final static String GTASK_JSON_ACTION_TYPE_GETALL = "get_all"; - + // 移动操作类型值 public final static String GTASK_JSON_ACTION_TYPE_MOVE = "move"; - + // 更新操作类型值 public final static String GTASK_JSON_ACTION_TYPE_UPDATE = "update"; - + // 创建者ID字段名 public final static String GTASK_JSON_CREATOR_ID = "creator_id"; - + // 子实体字段名 public final static String GTASK_JSON_CHILD_ENTITY = "child_entity"; - + // 客户端版本字段名 public final static String GTASK_JSON_CLIENT_VERSION = "client_version"; - + // 完成状态字段名 public final static String GTASK_JSON_COMPLETED = "completed"; - + // 当前列表ID字段名 public final static String GTASK_JSON_CURRENT_LIST_ID = "current_list_id"; - + // 默认列表ID字段名 public final static String GTASK_JSON_DEFAULT_LIST_ID = "default_list_id"; - + // 已删除状态字段名 public final static String GTASK_JSON_DELETED = "deleted"; - + // 目标列表字段名(移动操作) public final static String GTASK_JSON_DEST_LIST = "dest_list"; - + // 目标父级字段名(移动操作) public final static String GTASK_JSON_DEST_PARENT = "dest_parent"; - + // 目标父级类型字段名 public final static String GTASK_JSON_DEST_PARENT_TYPE = "dest_parent_type"; - + // 实体变更字段名 public final static String GTASK_JSON_ENTITY_DELTA = "entity_delta"; - + // 实体类型字段名 public final static String GTASK_JSON_ENTITY_TYPE = "entity_type"; - + // 是否获取已删除数据字段名 public final static String GTASK_JSON_GET_DELETED = "get_deleted"; - + // ID字段名 public final static String GTASK_JSON_ID = "id"; - + // 索引字段名 public final static String GTASK_JSON_INDEX = "index"; - + // 最后修改时间字段名 public final static String GTASK_JSON_LAST_MODIFIED = "last_modified"; - + // 最新同步点字段名 public final static String GTASK_JSON_LATEST_SYNC_POINT = "latest_sync_point"; - + // 列表ID字段名 public final static String GTASK_JSON_LIST_ID = "list_id"; - + // 列表集合字段名 public final static String GTASK_JSON_LISTS = "lists"; - + // 名称字段名 public final static String GTASK_JSON_NAME = "name"; - + // 新ID字段名(创建操作返回) public final static String GTASK_JSON_NEW_ID = "new_id"; - + // 备注字段名 public final static String GTASK_JSON_NOTES = "notes"; - + // 父级ID字段名 public final static String GTASK_JSON_PARENT_ID = "parent_id"; - + // 前一个兄弟ID字段名(排序用) public final static String GTASK_JSON_PRIOR_SIBLING_ID = "prior_sibling_id"; - + // 结果集合字段名 public final static String GTASK_JSON_RESULTS = "results"; - + // 源列表字段名(移动操作) public final static String GTASK_JSON_SOURCE_LIST = "source_list"; - + // 任务集合字段名 public final static String GTASK_JSON_TASKS = "tasks"; - + // 类型字段名 public final static String GTASK_JSON_TYPE = "type"; - + // 分组类型值 public final static String GTASK_JSON_TYPE_GROUP = "GROUP"; - + // 任务类型值 public final static String GTASK_JSON_TYPE_TASK = "TASK"; - + // 用户字段名 public final static String GTASK_JSON_USER = "user"; + // --------------------------- 文件夹相关常量 --------------------------- // + // MIUI笔记文件夹前缀(用于标识本地创建的文件夹) public final static String MIUI_FOLDER_PREFFIX = "[MIUI_Notes]"; - + // 默认文件夹名称 public final static String FOLDER_DEFAULT = "Default"; - + // 通话记录文件夹名称 public final static String FOLDER_CALL_NOTE = "Call_Note"; - + // 元数据文件夹名称(存储同步元数据) public final static String FOLDER_META = "METADATA"; + // --------------------------- 元数据相关常量 --------------------------- // + // 元数据头部:Google Tasks ID标识 public final static String META_HEAD_GTASK_ID = "meta_gid"; - + // 元数据头部:笔记内容标识 public final static String META_HEAD_NOTE = "meta_note"; - + // 元数据头部:数据项标识 public final static String META_HEAD_DATA = "meta_data"; - + // 元数据笔记名称(系统自动生成,禁止用户修改/删除) public final static String META_NOTE_NAME = "[META INFO] DON'T UPDATE AND DELETE"; - -} +} \ No newline at end of file diff --git a/src/Notes-master/src/net/micode/notes/tool/ResourceParser.java b/src/Notes-master/src/net/micode/notes/tool/ResourceParser.java index 1ad3ad6..ebb3dbd 100644 --- a/src/Notes-master/src/net/micode/notes/tool/ResourceParser.java +++ b/src/Notes-master/src/net/micode/notes/tool/ResourceParser.java @@ -2,180 +2,270 @@ * Copyright (c) 2010-2011, The MiCode Open Source Community (www.micode.net) * * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at + * 您可以在遵守 License 的前提下使用本文件。 + * 您可以从以下地址获取 License 副本: * * http://www.apache.org/licenses/LICENSE-2.0 * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. + * 除非适用法律要求或书面同意,本软件根据 License 分发时“按现状”提供, + * 不附带任何明示或暗示的保证或条件。请参阅 License 了解具体的权限和限制。 */ -package net.micode.notes.tool; +package net.micode.notes.tool; // 指定代码所属的包名 -import android.content.Context; -import android.preference.PreferenceManager; +import android.content.Context; // 导入Context类,提供应用环境信息 +import android.preference.PreferenceManager; // 导入偏好设置管理类 -import net.micode.notes.R; -import net.micode.notes.ui.NotesPreferenceActivity; +import net.micode.notes.R; // 导入R资源类,访问应用资源 +import net.micode.notes.ui.NotesPreferenceActivity; // 导入笔记偏好设置活动类 +/** + * 资源解析工具类,用于管理和获取笔记应用中的各种资源 + * 包括背景颜色、文本大小、布局样式等资源的ID映射和解析 + */ public class ResourceParser { - public static final int YELLOW = 0; - public static final int BLUE = 1; - public static final int WHITE = 2; - public static final int GREEN = 3; - public static final int RED = 4; + // 笔记背景颜色常量定义 + public static final int YELLOW = 0; // 黄色背景 + public static final int BLUE = 1; // 蓝色背景 + public static final int WHITE = 2; // 白色背景 + public static final int GREEN = 3; // 绿色背景 + public static final int RED = 4; // 红色背景 + // 默认背景颜色(黄色) public static final int BG_DEFAULT_COLOR = YELLOW; - public static final int TEXT_SMALL = 0; - public static final int TEXT_MEDIUM = 1; - public static final int TEXT_LARGE = 2; - public static final int TEXT_SUPER = 3; + // 文本大小常量定义 + public static final int TEXT_SMALL = 0; // 小号文本 + public static final int TEXT_MEDIUM = 1; // 中号文本(默认) + public static final int TEXT_LARGE = 2; // 大号文本 + public static final int TEXT_SUPER = 3; // 超大号文本 + // 默认文本大小(中号) public static final int BG_DEFAULT_FONT_SIZE = TEXT_MEDIUM; + /** + * 笔记编辑界面背景资源内部类 + * 管理笔记编辑界面的背景和标题栏背景资源ID + */ public static class NoteBgResources { + // 笔记编辑界面背景资源数组 private final static int [] BG_EDIT_RESOURCES = new int [] { - R.drawable.edit_yellow, - R.drawable.edit_blue, - R.drawable.edit_white, - R.drawable.edit_green, - R.drawable.edit_red + R.drawable.edit_yellow, // 黄色背景 + R.drawable.edit_blue, // 蓝色背景 + R.drawable.edit_white, // 白色背景 + R.drawable.edit_green, // 绿色背景 + R.drawable.edit_red // 红色背景 }; + // 笔记编辑界面标题栏背景资源数组 private final static int [] BG_EDIT_TITLE_RESOURCES = new int [] { - R.drawable.edit_title_yellow, - R.drawable.edit_title_blue, - R.drawable.edit_title_white, - R.drawable.edit_title_green, - R.drawable.edit_title_red + R.drawable.edit_title_yellow, // 黄色标题栏 + R.drawable.edit_title_blue, // 蓝色标题栏 + R.drawable.edit_title_white, // 白色标题栏 + R.drawable.edit_title_green, // 绿色标题栏 + R.drawable.edit_title_red // 红色标题栏 }; + /** + * 根据颜色ID获取笔记编辑界面背景资源ID + * @param id 颜色ID(0-4) + * @return 对应的背景资源ID + */ public static int getNoteBgResource(int id) { - return BG_EDIT_RESOURCES[id]; + return BG_EDIT_RESOURCES[id]; // 返回对应颜色的背景资源ID } + /** + * 根据颜色ID获取笔记编辑界面标题栏背景资源ID + * @param id 颜色ID(0-4) + * @return 对应的标题栏背景资源ID + */ public static int getNoteTitleBgResource(int id) { - return BG_EDIT_TITLE_RESOURCES[id]; + return BG_EDIT_TITLE_RESOURCES[id]; // 返回对应颜色的标题栏背景资源ID } } + /** + * 获取默认笔记背景ID + * 根据用户偏好设置决定是返回随机背景还是默认背景 + * @param context 应用上下文 + * @return 背景ID + */ public static int getDefaultBgId(Context context) { if (PreferenceManager.getDefaultSharedPreferences(context).getBoolean( NotesPreferenceActivity.PREFERENCE_SET_BG_COLOR_KEY, false)) { + // 如果用户开启了随机背景选项,则返回随机背景ID return (int) (Math.random() * NoteBgResources.BG_EDIT_RESOURCES.length); } else { + // 否则返回默认背景ID(黄色) return BG_DEFAULT_COLOR; } } + /** + * 笔记列表项背景资源内部类 + * 管理笔记在列表中不同位置(首项、中间项、末项、单项)的背景资源ID + */ public static class NoteItemBgResources { + // 列表中第一项的背景资源数组 private final static int [] BG_FIRST_RESOURCES = new int [] { - R.drawable.list_yellow_up, - R.drawable.list_blue_up, - R.drawable.list_white_up, - R.drawable.list_green_up, - R.drawable.list_red_up + R.drawable.list_yellow_up, // 黄色首项背景 + R.drawable.list_blue_up, // 蓝色首项背景 + R.drawable.list_white_up, // 白色首项背景 + R.drawable.list_green_up, // 绿色首项背景 + R.drawable.list_red_up // 红色首项背景 }; + // 列表中中间项的背景资源数组 private final static int [] BG_NORMAL_RESOURCES = new int [] { - R.drawable.list_yellow_middle, - R.drawable.list_blue_middle, - R.drawable.list_white_middle, - R.drawable.list_green_middle, - R.drawable.list_red_middle + R.drawable.list_yellow_middle, // 黄色中间项背景 + R.drawable.list_blue_middle, // 蓝色中间项背景 + R.drawable.list_white_middle, // 白色中间项背景 + R.drawable.list_green_middle, // 绿色中间项背景 + R.drawable.list_red_middle // 红色中间项背景 }; + // 列表中最后一项的背景资源数组 private final static int [] BG_LAST_RESOURCES = new int [] { - R.drawable.list_yellow_down, - R.drawable.list_blue_down, - R.drawable.list_white_down, - R.drawable.list_green_down, - R.drawable.list_red_down, + R.drawable.list_yellow_down, // 黄色末项背景 + R.drawable.list_blue_down, // 蓝色末项背景 + R.drawable.list_white_down, // 白色末项背景 + R.drawable.list_green_down, // 绿色末项背景 + R.drawable.list_red_down, // 红色末项背景 }; + // 列表中单独一项(唯一项)的背景资源数组 private final static int [] BG_SINGLE_RESOURCES = new int [] { - R.drawable.list_yellow_single, - R.drawable.list_blue_single, - R.drawable.list_white_single, - R.drawable.list_green_single, - R.drawable.list_red_single + R.drawable.list_yellow_single, // 黄色单项背景 + R.drawable.list_blue_single, // 蓝色单项背景 + R.drawable.list_white_single, // 白色单项背景 + R.drawable.list_green_single, // 绿色单项背景 + R.drawable.list_red_single // 红色单项背景 }; + /** + * 根据颜色ID获取列表首项背景资源ID + * @param id 颜色ID(0-4) + * @return 对应的首项背景资源ID + */ public static int getNoteBgFirstRes(int id) { - return BG_FIRST_RESOURCES[id]; + return BG_FIRST_RESOURCES[id]; // 返回对应颜色的首项背景资源ID } + /** + * 根据颜色ID获取列表末项背景资源ID + * @param id 颜色ID(0-4) + * @return 对应的末项背景资源ID + */ public static int getNoteBgLastRes(int id) { - return BG_LAST_RESOURCES[id]; + return BG_LAST_RESOURCES[id]; // 返回对应颜色的末项背景资源ID } + /** + * 根据颜色ID获取列表单项背景资源ID + * @param id 颜色ID(0-4) + * @return 对应的单项背景资源ID + */ public static int getNoteBgSingleRes(int id) { - return BG_SINGLE_RESOURCES[id]; + return BG_SINGLE_RESOURCES[id]; // 返回对应颜色的单项背景资源ID } + /** + * 根据颜色ID获取列表中间项背景资源ID + * @param id 颜色ID(0-4) + * @return 对应的中间项背景资源ID + */ public static int getNoteBgNormalRes(int id) { - return BG_NORMAL_RESOURCES[id]; + return BG_NORMAL_RESOURCES[id]; // 返回对应颜色的中间项背景资源ID } + /** + * 获取文件夹列表项的背景资源ID + * @return 文件夹背景资源ID + */ public static int getFolderBgRes() { - return R.drawable.list_folder; + return R.drawable.list_folder; // 返回文件夹背景资源ID } } + /** + * 桌面小部件背景资源内部类 + * 管理不同尺寸(2x和4x)桌面小部件的背景资源ID + */ public static class WidgetBgResources { + // 2x尺寸桌面小部件背景资源数组 private final static int [] BG_2X_RESOURCES = new int [] { - R.drawable.widget_2x_yellow, - R.drawable.widget_2x_blue, - R.drawable.widget_2x_white, - R.drawable.widget_2x_green, - R.drawable.widget_2x_red, + R.drawable.widget_2x_yellow, // 黄色2x小部件背景 + R.drawable.widget_2x_blue, // 蓝色2x小部件背景 + R.drawable.widget_2x_white, // 白色2x小部件背景 + R.drawable.widget_2x_green, // 绿色2x小部件背景 + R.drawable.widget_2x_red, // 红色2x小部件背景 }; + /** + * 根据颜色ID获取2x尺寸桌面小部件背景资源ID + * @param id 颜色ID(0-4) + * @return 对应的2x小部件背景资源ID + */ public static int getWidget2xBgResource(int id) { - return BG_2X_RESOURCES[id]; + return BG_2X_RESOURCES[id]; // 返回对应颜色的2x小部件背景资源ID } + // 4x尺寸桌面小部件背景资源数组 private final static int [] BG_4X_RESOURCES = new int [] { - R.drawable.widget_4x_yellow, - R.drawable.widget_4x_blue, - R.drawable.widget_4x_white, - R.drawable.widget_4x_green, - R.drawable.widget_4x_red + R.drawable.widget_4x_yellow, // 黄色4x小部件背景 + R.drawable.widget_4x_blue, // 蓝色4x小部件背景 + R.drawable.widget_4x_white, // 白色4x小部件背景 + R.drawable.widget_4x_green, // 绿色4x小部件背景 + R.drawable.widget_4x_red // 红色4x小部件背景 }; + /** + * 根据颜色ID获取4x尺寸桌面小部件背景资源ID + * @param id 颜色ID(0-4) + * @return 对应的4x小部件背景资源ID + */ public static int getWidget4xBgResource(int id) { - return BG_4X_RESOURCES[id]; + return BG_4X_RESOURCES[id]; // 返回对应颜色的4x小部件背景资源ID } } + /** + * 文本样式资源内部类 + * 管理不同文本大小的样式资源ID + */ public static class TextAppearanceResources { + // 文本样式资源数组 private final static int [] TEXTAPPEARANCE_RESOURCES = new int [] { - R.style.TextAppearanceNormal, - R.style.TextAppearanceMedium, - R.style.TextAppearanceLarge, - R.style.TextAppearanceSuper + R.style.TextAppearanceNormal, // 小号文本样式 + R.style.TextAppearanceMedium, // 中号文本样式 + R.style.TextAppearanceLarge, // 大号文本样式 + R.style.TextAppearanceSuper // 超大号文本样式 }; + /** + * 根据文本大小ID获取对应的文本样式资源ID + * @param id 文本大小ID(0-3) + * @return 对应的文本样式资源ID + */ public static int getTexAppearanceResource(int id) { /** - * HACKME: Fix bug of store the resource id in shared preference. - * The id may larger than the length of resources, in this case, - * return the {@link ResourceParser#BG_DEFAULT_FONT_SIZE} + * HACKME: 修复在共享偏好中存储资源ID可能导致的问题。 + * 如果ID大于资源数组长度,返回默认文本大小(中号) */ if (id >= TEXTAPPEARANCE_RESOURCES.length) { - return BG_DEFAULT_FONT_SIZE; + return BG_DEFAULT_FONT_SIZE; // ID越界时返回默认文本大小 } - return TEXTAPPEARANCE_RESOURCES[id]; + return TEXTAPPEARANCE_RESOURCES[id]; // 返回对应文本大小的样式资源ID } + /** + * 获取文本样式资源数组的长度 + * @return 资源数组长度 + */ public static int getResourcesSize() { - return TEXTAPPEARANCE_RESOURCES.length; + return TEXTAPPEARANCE_RESOURCES.length; // 返回资源数组长度 } } -} +} \ No newline at end of file