diff --git a/src/src/net/micode/notes/data/Contact.java b/src/src/net/micode/notes/data/Contact.java index d97ac5d..973be57 100644 --- a/src/src/net/micode/notes/data/Contact.java +++ b/src/src/net/micode/notes/data/Contact.java @@ -28,14 +28,25 @@ import java.util.HashMap; public class Contact { private static HashMap sContactCache; private static final String TAG = "Contact"; - + //常量字符串,用于日志记录 +/*SQL选择语句,用于根据电话号码查询联系人数据库。它 + *使用"PHONE_NUMBERS_EQUAL"函数来比较电话号码,使用"Data.MIMETYPE"常量来 *过滤电话号码数据,使用"Data.RAW_CONTACT_ID"来过滤原始联系人ID。 + */ 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 " + "(SELECT raw_contact_id " + " FROM phone_lookup" + " WHERE min_match = '+')"; - +//接受一个Context对象和电话号码作为参数,并返回与电话号码关联的联系人姓名。 +/*该方法首先检查联系人姓名是否已经缓存在"sContactCache" HashMap中。如果 +*是,则检索并返回缓存的姓名。 +*如果联系人姓名未缓存,则使用"PhoneNumberUtils.toCallerIDMinMatch"方法 +*将"CALLER_ID_SELECTION"中的"+"字符替换为电话号码的呼叫者ID最小匹配。 +*使用选择字符串和电话号码查询联系人数据库。 +*如果找到匹配的联系人,则从游标中检索联系人姓名,并在返回姓名之前将其添加到*缓存中。 +*如果未找到匹配的联系人,则记录调试消息并返回null。 +*/ public static String getContact(Context context, String phoneNumber) { if(sContactCache == null) { sContactCache = new HashMap(); @@ -44,7 +55,7 @@ public class Contact { if(sContactCache.containsKey(phoneNumber)) { return sContactCache.get(phoneNumber); } - + // 构建选择字符串,用于查询联系人数据库 String selection = CALLER_ID_SELECTION.replace("+", PhoneNumberUtils.toCallerIDMinMatch(phoneNumber)); Cursor cursor = context.getContentResolver().query( diff --git a/src/src/net/micode/notes/data/Notes.java b/src/src/net/micode/notes/data/Notes.java index f240604..6c7a193 100644 --- a/src/src/net/micode/notes/data/Notes.java +++ b/src/src/net/micode/notes/data/Notes.java @@ -17,9 +17,13 @@ package net.micode.notes.data; import android.net.Uri; +//Notes类包含与笔记和文件夹相关的各种常量和URI public class Notes { + //内容提供者的授权字符串 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; @@ -30,11 +34,12 @@ public class Notes { * {@link Notes#ID_TEMPARAY_FOLDER } is for notes belonging no folder * {@link Notes#ID_CALL_RECORD_FOLDER} is to store call records */ + //系统文件夹的标识符 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"; @@ -60,7 +65,7 @@ public class Notes { * Uri to query data */ public static final Uri CONTENT_DATA_URI = Uri.parse("content://" + AUTHORITY + "/data"); - + //NoteColumns接口定义了笔记表的列名 public interface NoteColumns { /** * The unique ID for a row diff --git a/src/src/net/micode/notes/data/NotesDatabaseHelper.java b/src/src/net/micode/notes/data/NotesDatabaseHelper.java index ffe5d57..7267801 100644 --- a/src/src/net/micode/notes/data/NotesDatabaseHelper.java +++ b/src/src/net/micode/notes/data/NotesDatabaseHelper.java @@ -33,15 +33,15 @@ public class NotesDatabaseHelper extends SQLiteOpenHelper { private static final int DB_VERSION = 4; public interface TABLE { - public static final String NOTE = "note"; + public static final String NOTE = "note";//笔记表 - public static final String DATA = "data"; + public static final String DATA = "data";//数据表 } private static final String TAG = "NotesDatabaseHelper"; private static NotesDatabaseHelper mInstance; - + //创建笔记表的SQL语句 private static final String CREATE_NOTE_TABLE_SQL = "CREATE TABLE " + TABLE.NOTE + "(" + NoteColumns.ID + " INTEGER PRIMARY KEY," + @@ -62,7 +62,7 @@ public class NotesDatabaseHelper extends SQLiteOpenHelper { NoteColumns.GTASK_ID + " TEXT NOT NULL DEFAULT ''," + NoteColumns.VERSION + " INTEGER NOT NULL DEFAULT 0" + ")"; - + // 创建数据表的SQL语句 private static final String CREATE_DATA_TABLE_SQL = "CREATE TABLE " + TABLE.DATA + "(" + DataColumns.ID + " INTEGER PRIMARY KEY," + @@ -77,13 +77,13 @@ public class NotesDatabaseHelper extends SQLiteOpenHelper { DataColumns.DATA4 + " TEXT NOT NULL DEFAULT ''," + DataColumns.DATA5 + " TEXT NOT NULL DEFAULT ''" + ")"; - + //创建数据表的NOTE_ID索引的SQL语句 private static final String CREATE_DATA_NOTE_ID_INDEX_SQL = "CREATE INDEX IF NOT EXISTS note_id_index ON " + TABLE.DATA + "(" + DataColumns.NOTE_ID + ");"; /** - * Increase folder's note count when move note to the folder + * 当将笔记移动到文件夹时,增加文件夹的笔记计数 */ private static final String NOTE_INCREASE_FOLDER_COUNT_ON_UPDATE_TRIGGER = "CREATE TRIGGER increase_folder_count_on_update "+ @@ -95,7 +95,7 @@ public class NotesDatabaseHelper extends SQLiteOpenHelper { " END"; /** - * Decrease folder's note count when move note from folder + * 当将笔记从文件夹中移出时,减少文件夹的笔记计数 */ private static final String NOTE_DECREASE_FOLDER_COUNT_ON_UPDATE_TRIGGER = "CREATE TRIGGER decrease_folder_count_on_update " + @@ -108,7 +108,7 @@ public class NotesDatabaseHelper extends SQLiteOpenHelper { " END"; /** - * Increase folder's note count when insert new note to the folder + * 当向文件夹中插入新笔记时,增加文件夹的笔记计数 */ private static final String NOTE_INCREASE_FOLDER_COUNT_ON_INSERT_TRIGGER = "CREATE TRIGGER increase_folder_count_on_insert " + @@ -120,7 +120,7 @@ public class NotesDatabaseHelper extends SQLiteOpenHelper { " END"; /** - * Decrease folder's note count when delete note from the folder + * 从文件夹中删除笔记时,减少文件夹的笔记计数 */ private static final String NOTE_DECREASE_FOLDER_COUNT_ON_DELETE_TRIGGER = "CREATE TRIGGER decrease_folder_count_on_delete " + @@ -133,7 +133,7 @@ public class NotesDatabaseHelper extends SQLiteOpenHelper { " END"; /** - * Update note's content when insert data with type {@link DataConstants#NOTE} + * 当插入类型为{@link DataConstants#NOTE}的数据时,更新笔记的内容 */ private static final String DATA_UPDATE_NOTE_CONTENT_ON_INSERT_TRIGGER = "CREATE TRIGGER update_note_content_on_insert " + @@ -146,7 +146,7 @@ public class NotesDatabaseHelper extends SQLiteOpenHelper { " END"; /** - * Update note's content when data with {@link DataConstants#NOTE} type has changed + * 当类型为{@link DataConstants#NOTE}的数据发生更改时,更新笔记的内容 */ private static final String DATA_UPDATE_NOTE_CONTENT_ON_UPDATE_TRIGGER = "CREATE TRIGGER update_note_content_on_update " + @@ -159,7 +159,7 @@ public class NotesDatabaseHelper extends SQLiteOpenHelper { " END"; /** - * Update note's content when data with {@link DataConstants#NOTE} type has deleted + 当类型为{@link DataConstants#NOTE}的数据被删除时,更新笔记的内容 */ private static final String DATA_UPDATE_NOTE_CONTENT_ON_DELETE_TRIGGER = "CREATE TRIGGER update_note_content_on_delete " + @@ -172,7 +172,7 @@ public class NotesDatabaseHelper extends SQLiteOpenHelper { " END"; /** - * Delete datas belong to note which has been deleted + *删除已删除的笔记所属的数据 */ private static final String NOTE_DELETE_DATA_ON_DELETE_TRIGGER = "CREATE TRIGGER delete_data_on_delete " + @@ -183,7 +183,7 @@ public class NotesDatabaseHelper extends SQLiteOpenHelper { " END"; /** - * Delete notes belong to folder which has been deleted + * 删除已删除的文件夹所属的笔记 */ private static final String FOLDER_DELETE_NOTES_ON_DELETE_TRIGGER = "CREATE TRIGGER folder_delete_notes_on_delete " + @@ -194,7 +194,7 @@ public class NotesDatabaseHelper extends SQLiteOpenHelper { " END"; /** - * Move notes belong to folder which has been moved to trash folder + * 将已移至回收站文件夹的笔记移动到回收站 */ private static final String FOLDER_MOVE_NOTES_ON_TRASH_TRIGGER = "CREATE TRIGGER folder_move_notes_on_trash " + @@ -239,14 +239,14 @@ public class NotesDatabaseHelper extends SQLiteOpenHelper { ContentValues values = new ContentValues(); /** - * call record foler for call notes + * 用于通话记录的文件夹 */ values.put(NoteColumns.ID, Notes.ID_CALL_RECORD_FOLDER); values.put(NoteColumns.TYPE, Notes.TYPE_SYSTEM); db.insert(TABLE.NOTE, null, values); /** - * root folder which is default folder + * 默认的根文件夹 */ values.clear(); values.put(NoteColumns.ID, Notes.ID_ROOT_FOLDER); @@ -254,7 +254,7 @@ public class NotesDatabaseHelper extends SQLiteOpenHelper { db.insert(TABLE.NOTE, null, values); /** - * temporary folder which is used for moving note + * 用于移动笔记的临时文件夹 */ values.clear(); values.put(NoteColumns.ID, Notes.ID_TEMPARAY_FOLDER); @@ -262,7 +262,7 @@ public class NotesDatabaseHelper extends SQLiteOpenHelper { db.insert(TABLE.NOTE, null, values); /** - * create trash folder + * 创建回收站文件夹 */ values.clear(); values.put(NoteColumns.ID, Notes.ID_TRASH_FOLER); @@ -307,7 +307,7 @@ public class NotesDatabaseHelper extends SQLiteOpenHelper { if (oldVersion == 1) { upgradeToV2(db); - skipV2 = true; // this upgrade including the upgrade from v2 to v3 + skipV2 = true; // 这个升级包括从v2到v3的升级 oldVersion++; } @@ -341,14 +341,14 @@ public class NotesDatabaseHelper extends SQLiteOpenHelper { } private void upgradeToV3(SQLiteDatabase db) { - // drop unused triggers + // 删除未使用的触发器 db.execSQL("DROP TRIGGER IF EXISTS update_note_modified_date_on_insert"); db.execSQL("DROP TRIGGER IF EXISTS update_note_modified_date_on_delete"); db.execSQL("DROP TRIGGER IF EXISTS update_note_modified_date_on_update"); - // add a column for gtask id + // 添加一个用于 gtask id 的列 db.execSQL("ALTER TABLE " + TABLE.NOTE + " ADD COLUMN " + NoteColumns.GTASK_ID + " TEXT NOT NULL DEFAULT ''"); - // add a trash system folder + // 添加一个垃圾箱系统文件夹 ContentValues values = new ContentValues(); values.put(NoteColumns.ID, Notes.ID_TRASH_FOLER); values.put(NoteColumns.TYPE, Notes.TYPE_SYSTEM); diff --git a/src/src/net/micode/notes/data/NotesProvider.java b/src/src/net/micode/notes/data/NotesProvider.java index edb0a60..afa5013 100644 --- a/src/src/net/micode/notes/data/NotesProvider.java +++ b/src/src/net/micode/notes/data/NotesProvider.java @@ -42,14 +42,13 @@ public class NotesProvider extends ContentProvider { private static final String TAG = "NotesProvider"; - private static final int URI_NOTE = 1; - private static final int URI_NOTE_ITEM = 2; - private static final int URI_DATA = 3; - private static final int URI_DATA_ITEM = 4; - - private static final int URI_SEARCH = 5; - private static final int URI_SEARCH_SUGGEST = 6; + private static final int URI_NOTE = 1; // 笔记URI + private static final int URI_NOTE_ITEM = 2; // 单个笔记URI + private static final int URI_DATA = 3; // 数据URI + private static final int URI_DATA_ITEM = 4; // 单个数据URI + private static final int URI_SEARCH = 5; // 搜索URI + private static final int URI_SEARCH_SUGGEST = 6; // 搜索建议URI static { mMatcher = new UriMatcher(UriMatcher.NO_MATCH); mMatcher.addURI(Notes.AUTHORITY, "note", URI_NOTE); @@ -62,8 +61,8 @@ public class NotesProvider extends ContentProvider { } /** - * x'0A' represents the '\n' character in sqlite. For title and content in the search result, - * we will trim '\n' and white space in order to show more information. + * x'0A'表示sqlite中的'\n'字符。对于搜索结果中的标题和内容,我们将 + *修剪掉'\n'和空格,以显示更多信息。 */ private static final String NOTES_SEARCH_PROJECTION = NoteColumns.ID + "," + NoteColumns.ID + " AS " + SearchManager.SUGGEST_COLUMN_INTENT_EXTRA_DATA + "," @@ -72,7 +71,9 @@ public class NotesProvider extends ContentProvider { + R.drawable.search_result + " AS " + SearchManager.SUGGEST_COLUMN_ICON_1 + "," + "'" + Intent.ACTION_VIEW + "' AS " + SearchManager.SUGGEST_COLUMN_INTENT_ACTION + "," + "'" + Notes.TextNote.CONTENT_TYPE + "' AS " + SearchManager.SUGGEST_COLUMN_INTENT_DATA; - +/*执行笔记搜索的查询。在onCreate()方法中,我们初始化了mHelper变量,该变量 *NotesDatabaseHelper的实例,用于管理数据库操作。返回true表示 +*ContentProvider的创建成功。 +*/ private static String NOTES_SNIPPET_SEARCH_QUERY = "SELECT " + NOTES_SEARCH_PROJECTION + " FROM " + TABLE.NOTE + " WHERE " + NoteColumns.SNIPPET + " LIKE ?" @@ -84,7 +85,9 @@ public class NotesProvider extends ContentProvider { mHelper = NotesDatabaseHelper.getInstance(getContext()); return true; } - +/*据提供的参数查询数据库中的数据。根据不同的URI匹配结果,执行不同的查询操 +*作。其中包括查询笔记、查询单个笔记、查询数据、查询单个数据以及查询搜索结果*等操作。注释已经添加在相应的位置,以便理解每个查询的目的和功能。 +*/ @Override public Cursor query(Uri uri, String[] projection, String selection, String[] selectionArgs, String sortOrder) { @@ -93,18 +96,22 @@ public class NotesProvider extends ContentProvider { String id = null; switch (mMatcher.match(uri)) { case URI_NOTE: + //查询笔记 c = db.query(TABLE.NOTE, projection, selection, selectionArgs, null, null, sortOrder); break; case URI_NOTE_ITEM: + //查询单个笔记 id = uri.getPathSegments().get(1); c = db.query(TABLE.NOTE, projection, NoteColumns.ID + "=" + id + parseSelection(selection), selectionArgs, null, null, sortOrder); break; case URI_DATA: + // 查询数据 c = db.query(TABLE.DATA, projection, selection, selectionArgs, null, null, sortOrder); break; + //查询单个数据 case URI_DATA_ITEM: id = uri.getPathSegments().get(1); c = db.query(TABLE.DATA, projection, DataColumns.ID + "=" + id @@ -112,6 +119,7 @@ public class NotesProvider extends ContentProvider { break; case URI_SEARCH: case URI_SEARCH_SUGGEST: +//查询搜索结果或搜索建议 if (sortOrder != null || projection != null) { throw new IllegalArgumentException( "do not specify sortOrder, selection, selectionArgs, or projection" + "with this query"); @@ -146,13 +154,16 @@ public class NotesProvider extends ContentProvider { } return c; } - +/*根据提供的URI和ContentValues对象将数据插入到数据库中。根据不同的URI匹配结*果,执行不同的插入操作。其中包括插入笔记和插入数据的操作。在插入完成后,通*过通知机制通知相关的URI数据发生了变化。注释已经添加在相应的位置,以便理解 +*每个插入操作的目的和功能。 +*/ @Override public Uri insert(Uri uri, ContentValues values) { SQLiteDatabase db = mHelper.getWritableDatabase(); long dataId = 0, noteId = 0, insertedId = 0; switch (mMatcher.match(uri)) { case URI_NOTE: + // 插入笔记 insertedId = noteId = db.insert(TABLE.NOTE, null, values); break; case URI_DATA: @@ -161,18 +172,19 @@ public class NotesProvider extends ContentProvider { } else { Log.d(TAG, "Wrong data format without note id:" + values.toString()); } + // 插入数据 insertedId = dataId = db.insert(TABLE.DATA, null, values); break; default: throw new IllegalArgumentException("Unknown URI " + uri); } - // Notify the note uri + // 通知笔记URI if (noteId > 0) { getContext().getContentResolver().notifyChange( ContentUris.withAppendedId(Notes.CONTENT_NOTE_URI, noteId), null); } - // Notify the data uri + // 通知数据URI if (dataId > 0) { getContext().getContentResolver().notifyChange( ContentUris.withAppendedId(Notes.CONTENT_DATA_URI, dataId), null); @@ -180,7 +192,10 @@ public class NotesProvider extends ContentProvider { return ContentUris.withAppendedId(uri, insertedId); } - +/*删除数据,根据提供的URI、选择条件和选择参数从数据库中删除数据。根据不同的*URI匹配结果,执行不同的删除操作。其中包括删除笔记、删除单个笔记、删除数据 +*和删除单个数据的操作。在删除完成后,通过通知机制通知相关的URI数据发生了变 +*化。 +*/ @Override public int delete(Uri uri, String selection, String[] selectionArgs) { int count = 0; @@ -189,28 +204,31 @@ public class NotesProvider extends ContentProvider { boolean deleteData = false; switch (mMatcher.match(uri)) { case URI_NOTE: + // 删除笔记 selection = "(" + selection + ") AND " + NoteColumns.ID + ">0 "; count = db.delete(TABLE.NOTE, selection, selectionArgs); break; case URI_NOTE_ITEM: id = uri.getPathSegments().get(1); /** - * ID that smaller than 0 is system folder which is not allowed to - * trash + * ID小于等于0的是系统文件夹,不允许删除 */ long noteId = Long.valueOf(id); if (noteId <= 0) { break; } + //删除单个笔记 count = db.delete(TABLE.NOTE, NoteColumns.ID + "=" + id + parseSelection(selection), selectionArgs); break; case URI_DATA: + //删除数据 count = db.delete(TABLE.DATA, selection, selectionArgs); deleteData = true; break; case URI_DATA_ITEM: id = uri.getPathSegments().get(1); + //删除单个数据 count = db.delete(TABLE.DATA, DataColumns.ID + "=" + id + parseSelection(selection), selectionArgs); deleteData = true; @@ -226,7 +244,10 @@ public class NotesProvider extends ContentProvider { } return count; } - +/*更新数据,根据提供的URI、ContentValues对象、选择条件和选择参数更新数据库 +*中的数据。根据不同的URI匹配结果,执行不同的更新操作。其中包括增加笔记版本 +*号、更新笔记、更新单个笔记、更新数据和更新单个数据的操作。在更新完成后,通*过通知机制通知相关的URI数据发生了变化。 +*/ @Override public int update(Uri uri, ContentValues values, String selection, String[] selectionArgs) { int count = 0; @@ -235,21 +256,27 @@ public class NotesProvider extends ContentProvider { boolean updateData = false; switch (mMatcher.match(uri)) { case URI_NOTE: + //增加笔记版本号 increaseNoteVersion(-1, selection, selectionArgs); + // 更新笔记 count = db.update(TABLE.NOTE, values, selection, selectionArgs); break; case URI_NOTE_ITEM: id = uri.getPathSegments().get(1); + // 增加笔记版本号 increaseNoteVersion(Long.valueOf(id), selection, selectionArgs); + // 更新单个笔记 count = db.update(TABLE.NOTE, values, NoteColumns.ID + "=" + id + parseSelection(selection), selectionArgs); break; case URI_DATA: + // 更新数据 count = db.update(TABLE.DATA, values, selection, selectionArgs); updateData = true; break; case URI_DATA_ITEM: id = uri.getPathSegments().get(1); + // 更新单个数据 count = db.update(TABLE.DATA, values, DataColumns.ID + "=" + id + parseSelection(selection), selectionArgs); updateData = true; @@ -270,7 +297,10 @@ public class NotesProvider extends ContentProvider { private String parseSelection(String selection) { return (!TextUtils.isEmpty(selection) ? " AND (" + selection + ')' : ""); } - +/*用于增加笔记的版本号。它接受一个ID、选择条件和选择参数作为参数,并构建相 +*应的SQL语句来更新笔记表中的版本号字段。如果提供了ID或选择条件,则将它们添 +*加为更新条件。在构建完SQL语句后,通过执行SQL语句来增加笔记的版本号。 +*/ private void increaseNoteVersion(long id, String selection, String[] selectionArgs) { StringBuilder sql = new StringBuilder(120); sql.append("UPDATE "); @@ -278,13 +308,15 @@ public class NotesProvider extends ContentProvider { sql.append(" SET "); sql.append(NoteColumns.VERSION); sql.append("=" + NoteColumns.VERSION + "+1 "); - + if (id > 0 || !TextUtils.isEmpty(selection)) { sql.append(" WHERE "); } + // 如果ID大于0,则添加ID作为条件 if (id > 0) { sql.append(NoteColumns.ID + "=" + String.valueOf(id)); } + // 如果选择条件不为空,则解析选择条件并添加到SQL语句中 if (!TextUtils.isEmpty(selection)) { String selectString = id > 0 ? parseSelection(selection) : selection; for (String args : selectionArgs) { @@ -292,10 +324,13 @@ public class NotesProvider extends ContentProvider { } sql.append(selectString); } - + // 执行SQL语句,增加笔记版本号 mHelper.getWritableDatabase().execSQL(sql.toString()); } - +/*用于获取URI类型的方法。目前方法体为空,需要根据具体需求进行实现。根据URI +*的不同,可以返回不同的类型,例如MIME类型。根据实际需求,您可以根据URI的特 +*征来判断并返回相应的类型。 +*/ @Override public String getType(Uri uri) { // TODO Auto-generated method stub diff --git a/src/src/net/micode/notes/gtask/data/MetaData.java b/src/src/net/micode/notes/gtask/data/MetaData.java index 3a2050b..c615843 100644 --- a/src/src/net/micode/notes/gtask/data/MetaData.java +++ b/src/src/net/micode/notes/gtask/data/MetaData.java @@ -29,7 +29,8 @@ public class MetaData extends Task { private final static String TAG = MetaData.class.getSimpleName(); private String mRelatedGid = null; - +/*设置元数据信息,它接受一个字符串 gid 和一个 JSONObject 类型的 metaInfo 参*数。在方法内部,它将 gid 添加到 metaInfo 中,并通过调用 setNotes 方法将 *metaInfo 转换为字符串并设置为任务的注释。同时,它调用 setName 方法设置任务*的名称为预定义的常量值。 +*/ public void setMeta(String gid, JSONObject metaInfo) { try { metaInfo.put(GTaskStringUtils.META_HEAD_GTASK_ID, gid); @@ -39,16 +40,18 @@ public class MetaData extends Task { setNotes(metaInfo.toString()); setName(GTaskStringUtils.META_NOTE_NAME); } - +//获取相关的 gid public String getRelatedGid() { return mRelatedGid; } - +//判断任务是否值得保存。它通过检查任务的注释是否为空来决定返回值。 @Override public boolean isWorthSaving() { return getNotes() != null; } - +/*根据远程的 JSON 数据设置任务的内容。它首先调用父类的同名方法,然后从任务 +*的注释中解析出相关的 gid +*/ @Override public void setContentByRemoteJSON(JSONObject js) { super.setContentByRemoteJSON(js); @@ -62,18 +65,18 @@ public class MetaData extends Task { } } } - +//抛出 IllegalAccessError 异常 @Override public void setContentByLocalJSON(JSONObject js) { // this function should not be called throw new IllegalAccessError("MetaData:setContentByLocalJSON should not be called"); } - +//抛出 IllegalAccessError 异常。 @Override public JSONObject getLocalJSONFromContent() { throw new IllegalAccessError("MetaData:getLocalJSONFromContent should not be called"); } - +//抛出 IllegalAccessError 异常 @Override public int getSyncAction(Cursor c) { throw new IllegalAccessError("MetaData:getSyncAction should not be called"); diff --git a/src/src/net/micode/notes/gtask/data/Node.java b/src/src/net/micode/notes/gtask/data/Node.java index 63950e0..b93dbd2 100644 --- a/src/src/net/micode/notes/gtask/data/Node.java +++ b/src/src/net/micode/notes/gtask/data/Node.java @@ -21,31 +21,29 @@ import android.database.Cursor; import org.json.JSONObject; public abstract class Node { - public static final int SYNC_ACTION_NONE = 0; - + public static final int SYNC_ACTION_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_ADD_LOCAL = 2; +// 同步操作:本地添加 public static final int SYNC_ACTION_DEL_REMOTE = 3; - + // 同步操作:远程删除 public static final int SYNC_ACTION_DEL_LOCAL = 4; - + // 同步操作:本地删除 public static final int SYNC_ACTION_UPDATE_REMOTE = 5; - - public static final int SYNC_ACTION_UPDATE_LOCAL = 6; - - public static final int SYNC_ACTION_UPDATE_CONFLICT = 7; - - public static final int SYNC_ACTION_ERROR = 8; - - private String mGid; - - private String mName; - - private long mLastModified; - - private boolean mDeleted; + // 同步操作:远程更新 + public static final int SYNC_ACTION_UPDATE_LOCAL = 6; +// 同步操作:本地更新 + public static final int SYNC_ACTION_UPDATE_CONFLICT = 7; +// 同步操作:更新冲突 + public static final int SYNC_ACTION_ERROR = 8; +// 同步操作:错误 + + private String mGid; // 节点的唯一标识符 + private String mName; // 节点的名称 + private long mLastModified; // 最后修改时间 + private boolean mDeleted; // 是否已删除 public Node() { mGid = null; @@ -54,18 +52,18 @@ public abstract class Node { mDeleted = false; } - public abstract JSONObject getCreateAction(int actionId); - - public abstract JSONObject getUpdateAction(int actionId); - - public abstract void setContentByRemoteJSON(JSONObject js); - + public abstract JSONObject getCreateAction(int actionId); + // 获取创建操作的 JSON 对象 + public abstract JSONObject getUpdateAction(int actionId); +// 获取更新操作的 JSON 对象 + public abstract void setContentByRemoteJSON(JSONObject js); +// 根据远程的 JSON 数据设置节点内容 public abstract void setContentByLocalJSON(JSONObject js); - - public abstract JSONObject getLocalJSONFromContent(); - - public abstract int getSyncAction(Cursor c); - + // 根据本地的 JSON 数据设置节点内容 + public abstract JSONObject getLocalJSONFromContent(); +// 从节点内容中获取本地的 JSON 对象 + public abstract int getSyncAction(Cursor c); +// 获取同步操作 public void setGid(String gid) { this.mGid = gid; } diff --git a/src/src/net/micode/notes/gtask/data/SqlData.java b/src/src/net/micode/notes/gtask/data/SqlData.java index d3ec3be..76aea79 100644 --- a/src/src/net/micode/notes/gtask/data/SqlData.java +++ b/src/src/net/micode/notes/gtask/data/SqlData.java @@ -35,42 +35,41 @@ import org.json.JSONException; import org.json.JSONObject; -public class SqlData { - private static final String TAG = SqlData.class.getSimpleName(); +private static final String TAG = SqlData.class.getSimpleName(); // TAG 常量用于日志输出,表示类名 - private static final int INVALID_ID = -99999; + private static final int INVALID_ID = -99999; // 无效的 ID 值 - public static final String[] PROJECTION_DATA = new String[] { - DataColumns.ID, DataColumns.MIME_TYPE, DataColumns.CONTENT, DataColumns.DATA1, - DataColumns.DATA3 + public static final String[] PROJECTION_DATA = new String[] { // 数据查询的投影数组 + DataColumns.ID, DataColumns.MIME_TYPE, DataColumns.CONTENT, DataColumns.DATA1, + DataColumns.DATA3 }; - public static final int DATA_ID_COLUMN = 0; + public static final int DATA_ID_COLUMN = 0; +// 数据 ID 列的索引 + public static final int DATA_MIME_TYPE_COLUMN = 1; +// 数据 MIME 类型列的索引 + public static final int DATA_CONTENT_COLUMN = 2; +// 数据内容列的索引 + public static final int DATA_CONTENT_DATA_1_COLUMN = 3; +// 数据内容中 DATA1 列的索引 + public static final int DATA_CONTENT_DATA_3_COLUMN = 4; +// 数据内容中 DATA3 列的索引 + private ContentResolver mContentResolver; +// ContentResolver 对象,用于访问数据 - public static final int DATA_MIME_TYPE_COLUMN = 1; + private boolean mIsCreate; // 是否为创建操作 - public static final int DATA_CONTENT_COLUMN = 2; + private long mDataId; // 数据的 ID - public static final int DATA_CONTENT_DATA_1_COLUMN = 3; + private String mDataMimeType; // 数据的 MIME 类型 - public static final int DATA_CONTENT_DATA_3_COLUMN = 4; + private String mDataContent; // 数据的内容 - private ContentResolver mContentResolver; + private long mDataContentData1; // 数据内容中的 DATA1 列 - private boolean mIsCreate; - - private long mDataId; - - private String mDataMimeType; - - private String mDataContent; - - private long mDataContentData1; - - private String mDataContentData3; - - private ContentValues mDiffDataValues; + private String mDataContentData3; // 数据内容中的 DATA3 列 + private ContentValues mDiffDataValues; // 差异数据的 ContentValues 对象 public SqlData(Context context) { mContentResolver = context.getContentResolver(); mIsCreate = true; @@ -90,58 +89,73 @@ public class SqlData { } private void loadFromCursor(Cursor c) { - mDataId = c.getLong(DATA_ID_COLUMN); - mDataMimeType = c.getString(DATA_MIME_TYPE_COLUMN); - mDataContent = c.getString(DATA_CONTENT_COLUMN); - mDataContentData1 = c.getLong(DATA_CONTENT_DATA_1_COLUMN); - mDataContentData3 = c.getString(DATA_CONTENT_DATA_3_COLUMN); + mDataId = c.getLong(SqlData.DATA_ID_COLUMN); +// 从 Cursor 中读取数据 ID + mDataMimeType = c.getString(SqlData.DATA_MIME_TYPE_COLUMN); + // 从 Cursor 中读取数据 MIME 类型 + mDataContent = c.getString(SqlData.DATA_CONTENT_COLUMN); +// 从 Cursor 中读取数据内容 + mDataContentData1 = c.getLong(SqlData.DATA_CONTENT_DATA_1_COLUMN); +// 从 Cursor 中读取数据内容中的 DATA1 列 + mDataContentData3 = c.getString(SqlData.DATA_CONTENT_DATA_3_COLUMN); +// 从 Cursor 中读取数据内容中的 DATA3 列 } public void setContent(JSONObject js) throws JSONException { long dataId = js.has(DataColumns.ID) ? js.getLong(DataColumns.ID) : INVALID_ID; if (mIsCreate || mDataId != dataId) { mDiffDataValues.put(DataColumns.ID, dataId); +//将数据的 ID 存储到差异数据集合中 } - mDataId = dataId; + mDataId = dataId;// 更新数据的 ID String dataMimeType = js.has(DataColumns.MIME_TYPE) ? js.getString(DataColumns.MIME_TYPE) : DataConstants.NOTE; if (mIsCreate || !mDataMimeType.equals(dataMimeType)) { mDiffDataValues.put(DataColumns.MIME_TYPE, dataMimeType); +// 将数据的 MIME 类型存储到差异数据集合中 } - mDataMimeType = dataMimeType; + mDataMimeType = dataMimeType;// 更新数据的 MIME 类型 String dataContent = js.has(DataColumns.CONTENT) ? js.getString(DataColumns.CONTENT) : ""; if (mIsCreate || !mDataContent.equals(dataContent)) { mDiffDataValues.put(DataColumns.CONTENT, dataContent); +// 将数据的内容存储到差异数据集合中 } - mDataContent = dataContent; + mDataContent = dataContent;// 更新数据的内容 long dataContentData1 = js.has(DataColumns.DATA1) ? js.getLong(DataColumns.DATA1) : 0; if (mIsCreate || mDataContentData1 != dataContentData1) { mDiffDataValues.put(DataColumns.DATA1, dataContentData1); +// 将数据内容中的 DATA1 列存储到差异数据集合中 } - mDataContentData1 = dataContentData1; + mDataContentData1 = dataContentData1;// 更新数据内容中的 DATA1 列 String dataContentData3 = js.has(DataColumns.DATA3) ? js.getString(DataColumns.DATA3) : ""; if (mIsCreate || !mDataContentData3.equals(dataContentData3)) { mDiffDataValues.put(DataColumns.DATA3, dataContentData3); +// 将数据内容中的 DATA3 列存储到差异数据集合中 } - mDataContentData3 = dataContentData3; + mDataContentData3 = dataContentData3;// 更新数据内容中的 DATA3 列 } public JSONObject getContent() throws JSONException { if (mIsCreate) { - Log.e(TAG, "it seems that we haven't created this in database yet"); + Log.e(TAG, "it seems that we haven't created this in database yet");// 如果是创建操作,则输出错误日志并返回 null return null; } JSONObject js = new JSONObject(); - js.put(DataColumns.ID, mDataId); + js.put(DataColumns.ID, mDataId); +// 将数据的 ID 存储到 JSONObject 中 js.put(DataColumns.MIME_TYPE, mDataMimeType); - js.put(DataColumns.CONTENT, mDataContent); - js.put(DataColumns.DATA1, mDataContentData1); - js.put(DataColumns.DATA3, mDataContentData3); - return js; + // 将数据的 MIME 类型存储到 JSONObject 中 + js.put(DataColumns.CONTENT, mDataContent); +// 将数据的内容存储到 JSONObject 中 + js.put(DataColumns.DATA1, mDataContentData1); +// 将数据内容中的 DATA1 列存储到 JSONObject 中 + js.put(DataColumns.DATA3, mDataContentData3); / +/ 将数据内容中的 DATA3 列存储到 JSONObject 中 + return js; // 返回 JSONObject } public void commit(long noteId, boolean validateVersion, long version) { @@ -149,22 +163,26 @@ public class SqlData { if (mIsCreate) { if (mDataId == INVALID_ID && mDiffDataValues.containsKey(DataColumns.ID)) { mDiffDataValues.remove(DataColumns.ID); +// 如果数据的 ID 无效且差异数据集合中包含 ID,则从差异数据集合中移除 ID } mDiffDataValues.put(DataColumns.NOTE_ID, noteId); - Uri uri = mContentResolver.insert(Notes.CONTENT_DATA_URI, mDiffDataValues); +// 将笔记的 ID 存储到差异数据集合中 + Uri uri = mContentResolver.insert(Notes.CONTENT_DATA_URI, mDiffDataValues);// 插入数据并获取 URI try { mDataId = Long.valueOf(uri.getPathSegments().get(1)); + // 从 URI 中获取数据的 ID } catch (NumberFormatException e) { Log.e(TAG, "Get note id error :" + e.toString()); throw new ActionFailureException("create note failed"); + // 抛出异常,表示创建笔记失败 } } else { if (mDiffDataValues.size() > 0) { int result = 0; if (!validateVersion) { result = mContentResolver.update(ContentUris.withAppendedId( - Notes.CONTENT_DATA_URI, mDataId), mDiffDataValues, null, null); + Notes.CONTENT_DATA_URI, mDataId), mDiffDataValues, null, null);// 更新数据 } else { result = mContentResolver.update(ContentUris.withAppendedId( Notes.CONTENT_DATA_URI, mDataId), mDiffDataValues, @@ -172,15 +190,17 @@ public class SqlData { + " WHERE " + NoteColumns.VERSION + "=?)", new String[] { String.valueOf(noteId), String.valueOf(version) }); - } + }// 根据笔记的 ID 和版本号更新数据 if (result == 0) { - Log.w(TAG, "there is no update. maybe user updates note when syncing"); + Log.w(TAG, "there is no update. maybe user updates note when syncing"); +// 如果更新的结果为 0,则输出警告日志,表示可能在同步时用户已更新了笔记 } } } - mDiffDataValues.clear(); - mIsCreate = false; + mDiffDataValues.clear();// 清空差异数据集合 + mIsCreate = false;// 将创建标志设置为 false + } public long getId() { diff --git a/src/src/net/micode/notes/gtask/data/SqlNote.java b/src/src/net/micode/notes/gtask/data/SqlNote.java index 79a4095..540b74c 100644 --- a/src/src/net/micode/notes/gtask/data/SqlNote.java +++ b/src/src/net/micode/notes/gtask/data/SqlNote.java @@ -40,9 +40,9 @@ import java.util.ArrayList; public class SqlNote { private static final String TAG = SqlNote.class.getSimpleName(); - +// 标记日志的标签 private static final int INVALID_ID = -99999; - +// 无效的 ID 值 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, @@ -50,122 +50,134 @@ public class SqlNote { NoteColumns.WIDGET_ID, NoteColumns.WIDGET_TYPE, NoteColumns.SYNC_ID, NoteColumns.LOCAL_MODIFIED, NoteColumns.ORIGIN_PARENT_ID, NoteColumns.GTASK_ID, 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 ID_COLUMN = 0; + // ID 列的索引 + public static final int ALERTED_DATE_COLUMN = 1; + // ALERTED_DATE 列的索引 + public static final int BG_COLOR_ID_COLUMN = 2; + // BG_COLOR_ID 列的索引 public static final int CREATED_DATE_COLUMN = 3; + // CREATED_DATE 列的索引 + public static final int HAS_ATTACHMENT_COLUMN = 4; + // HAS_ATTACHMENT 列的索引 + public static final int MODIFIED_DATE_COLUMN = 5; + // MODIFIED_DATE 列的索引 + public static final int NOTES_COUNT_COLUMN = 6; + // NOTES_COUNT 列的索引 + public static final int PARENT_ID_COLUMN = 7; + // PARENT_ID 列的索引 + public static final int SNIPPET_COLUMN = 8; + // SNIPPET 列的索引 + public static final int TYPE_COLUMN = 9; + // TYPE 列的索引 + public static final int WIDGET_ID_COLUMN = 10; + // WIDGET_ID 列的索引 + public static final int WIDGET_TYPE_COLUMN = 11; + // WIDGET_TYPE 列的索引 + public static final int SYNC_ID_COLUMN = 12; + // SYNC_ID 列的索引 + public static final int LOCAL_MODIFIED_COLUMN = 13; + // LOCAL_MODIFIED 列的索引 + public static final int ORIGIN_PARENT_ID_COLUMN = 14; + // ORIGIN_PARENT_ID 列的索引 + public static final int GTASK_ID_COLUMN = 15; + // GTASK_ID 列的索引 + public static final int VERSION_COLUMN = 16; + // VERSION 列的索引 + private Context mContext; // 上下文对象 - 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; + private ContentResolver mContentResolver; // 内容解析器对象 - public static final int GTASK_ID_COLUMN = 15; + private boolean mIsCreate; // 标记是否为创建操作 - public static final int VERSION_COLUMN = 16; + private long mId; // ID - private Context mContext; + private long mAlertDate; // ALERTED_DATE - private ContentResolver mContentResolver; + private int mBgColorId; // BG_COLOR_ID - private boolean mIsCreate; + private long mCreatedDate; // CREATED_DATE - private long mId; + private int mHasAttachment; // HAS_ATTACHMENT - private long mAlertDate; + private long mModifiedDate; // MODIFIED_DATE - private int mBgColorId; + private long mParentId; // PARENT_ID - private long mCreatedDate; + private String mSnippet; // SNIPPET - private int mHasAttachment; + private int mType; // TYPE - private long mModifiedDate; + private int mWidgetId; // WIDGET_ID - private long mParentId; + private int mWidgetType; // WIDGET_TYPE - private String mSnippet; + private long mOriginParent; // ORIGIN_PARENT_ID - private int mType; + private long mVersion; // VERSION - private int mWidgetId; - - private int mWidgetType; - - private long mOriginParent; - - private long mVersion; - - private ContentValues mDiffNoteValues; - - private ArrayList mDataList; + private ContentValues mDiffNoteValues; // 笔记的差异值 + private ArrayList mDataList; // SqlData 对象的列表 public SqlNote(Context context) { - mContext = context; - mContentResolver = context.getContentResolver(); - mIsCreate = true; - mId = INVALID_ID; - mAlertDate = 0; - mBgColorId = ResourceParser.getDefaultBgId(context); - mCreatedDate = System.currentTimeMillis(); - mHasAttachment = 0; - mModifiedDate = System.currentTimeMillis(); - mParentId = 0; - mSnippet = ""; - mType = Notes.TYPE_NOTE; - mWidgetId = AppWidgetManager.INVALID_APPWIDGET_ID; + mContext = context; // 初始化上下文对象 + mContentResolver = context.getContentResolver(); + // 初始化内容解析器对象 + mIsCreate = true; + // 设置为创建操作 + mId = INVALID_ID; + // 初始化 ID 为无效值 + mAlertDate = 0; + // 初始化提醒日期为 0 + mBgColorId = ResourceParser.getDefaultBgId(context); + // 使用 ResourceParser 获取默认背景颜色 ID + mCreatedDate = System.currentTimeMillis(); + // 初始化创建日期为当前时间 + mHasAttachment = 0; // 初始化附件数量为 0 + mModifiedDate = System.currentTimeMillis(); + // 初始化修改日期为当前时间 + mParentId = 0; + // 初始化父笔记 ID 为 0 + mSnippet = ""; + // 初始化摘要为空字符串 + mType = Notes.TYPE_NOTE; + // 初始化类型为普通笔记 + mWidgetId = AppWidgetManager.INVALID_APPWIDGET_ID; + // 初始化小部件 ID 为无效值 mWidgetType = Notes.TYPE_WIDGET_INVALIDE; - mOriginParent = 0; - mVersion = 0; - mDiffNoteValues = new ContentValues(); - mDataList = new ArrayList(); + // 初始化小部件类型为无效类型 + mOriginParent = 0; // 初始化原始父笔记 ID 为 0 + mVersion = 0; // 初始化版本号为 0 + mDiffNoteValues = new ContentValues(); + // 创建一个空的 ContentValues 对象 + mDataList = new ArrayList(); + // 创建一个空的 SqlData 对象列表 } public SqlNote(Context context, Cursor c) { - mContext = context; - mContentResolver = context.getContentResolver(); - mIsCreate = false; - loadFromCursor(c); - mDataList = new ArrayList(); + mContext = context; // 上下文对象 + mContentResolver = context.getContentResolver(); // 内容解析器对象 + mIsCreate = false; // 是否创建标志位,设置为 false + loadFromCursor(c); // 从 Cursor 加载数据 + mDataList = new ArrayList(); // 数据列表,初始值为一个空的 SqlData 对象列表 if (mType == Notes.TYPE_NOTE) - loadDataContent(); - mDiffNoteValues = new ContentValues(); + loadDataContent(); // 加载数据内容 + mDiffNoteValues = new ContentValues(); // 差异值对象,初始值为一个空的 ContentValues 对象 } public SqlNote(Context context, long id) { - mContext = context; - mContentResolver = context.getContentResolver(); - mIsCreate = false; - loadFromCursor(id); - mDataList = new ArrayList(); + mContext = context; // 上下文对象 + mContentResolver = context.getContentResolver(); // 内容解析器对象 + mIsCreate = false; // 是否创建标志位,设置为 false + loadFromCursor(id); // 根据 ID 从数据库加载数据 + mDataList = new ArrayList(); // 数据列表,初始值为一个空的 SqlData 对象列表 if (mType == Notes.TYPE_NOTE) - loadDataContent(); - mDiffNoteValues = new ContentValues(); - + loadDataContent(); // 加载数据内容 + mDiffNoteValues = new ContentValues(); // 差异值对象,初始值为一个空的 ContentValues 对象 } - +//根据传入的 ID 值查询数据库获取 Cursor,并调用 loadFromCursor(Cursor c) 方法加载数据。 private void loadFromCursor(long id) { Cursor c = null; try { @@ -184,24 +196,27 @@ public class SqlNote { c.close(); } } - + 据 Cursor 的列索引获取对应的数据,并设置到 SqlNote 对象的成员变量中。 private void loadFromCursor(Cursor c) { - mId = c.getLong(ID_COLUMN); - mAlertDate = c.getLong(ALERTED_DATE_COLUMN); - mBgColorId = c.getInt(BG_COLOR_ID_COLUMN); - mCreatedDate = c.getLong(CREATED_DATE_COLUMN); - mHasAttachment = c.getInt(HAS_ATTACHMENT_COLUMN); - mModifiedDate = c.getLong(MODIFIED_DATE_COLUMN); - mParentId = c.getLong(PARENT_ID_COLUMN); - mSnippet = c.getString(SNIPPET_COLUMN); - mType = c.getInt(TYPE_COLUMN); - mWidgetId = c.getInt(WIDGET_ID_COLUMN); - mWidgetType = c.getInt(WIDGET_TYPE_COLUMN); - mVersion = c.getLong(VERSION_COLUMN); + mId = c.getLong(ID_COLUMN); // 从 Cursor 中获取并设置 ID + mAlertDate = c.getLong(ALERTED_DATE_COLUMN); // 从 Cursor 中获取并设置提醒日期 + mBgColorId = c.getInt(BG_COLOR_ID_COLUMN); // 从 Cursor 中获取并设置背景颜色 ID + mCreatedDate = c.getLong(CREATED_DATE_COLUMN); // 从 Cursor 中获取并设置创建日期 + mHasAttachment = c.getInt(HAS_ATTACHMENT_COLUMN); // 从 Cursor 中获取并设置附件数量 + mModifiedDate = c.getLong(MODIFIED_DATE_COLUMN); // 从 Cursor 中获取并设置修改日期 + mParentId = c.getLong(PARENT_ID_COLUMN); // 从 Cursor 中获取并设置父笔记 ID + mSnippet = c.getString(SNIPPET_COLUMN); // 从 Cursor 中获取并设置摘要 + mType = c.getInt(TYPE_COLUMN); // 从 Cursor 中获取并设置笔记类型 + mWidgetId = c.getInt(WIDGET_ID_COLUMN); // 从 Cursor 中获取并设置小部件 ID + mWidgetType = c.getInt(WIDGET_TYPE_COLUMN); // 从 Cursor 中获取并设置小部件类型 + mVersion = c.getLong(VERSION_COLUMN); // 从 Cursor 中获取并设置版本号 } - + /*加载笔记的数据内容。首先清空数据列表(mDataList), + * 然后根据当前笔记的 ID 值查询数据库获取相应的数据 Cursor,并遍历 Cursor,创建 SqlData 对象并加载数据, + * 最后将 SqlData 对象添加到数据列表中。如果查询的 Cursor 为空或者没有数据,会打印相应的日志信息。 + */ private void loadDataContent() { - Cursor c = null; + Cursor c = null;// 清空数据列表 mDataList.clear(); try { c = mContentResolver.query(Notes.CONTENT_DATA_URI, SqlData.PROJECTION_DATA, @@ -214,8 +229,8 @@ public class SqlNote { return; } while (c.moveToNext()) { - SqlData data = new SqlData(mContext, c); - mDataList.add(data); + SqlData data = new SqlData(mContext, c);// 创建 SqlData 对象并从 Cursor 加载数据 + mDataList.add(data); // 将 SqlData 对象添加到数据列表中 } } else { Log.w(TAG, "loadDataContent: cursor = null"); @@ -229,19 +244,21 @@ public class SqlNote { 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) : ""; + // 如果是文件夹类型的笔记,只能更新摘要和类型 + 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); } @@ -249,93 +266,96 @@ public class SqlNote { } 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; + // 如果是新建笔记或者当前笔记的ID与传入的ID不同,更新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); + // 如果是新建笔记或者当前笔记的背景颜色ID与传入的背景颜色ID不同,更新背景颜色ID 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; + // 如果是新建笔记或者当前笔记的父ID与传入的父ID不同,更新父ID 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); } mType = type; - int widgetId = note.has(NoteColumns.WIDGET_ID) ? note.getInt(NoteColumns.WIDGET_ID) - : AppWidgetManager.INVALID_APPWIDGET_ID; + int widgetId = note.has(NoteColumns.WIDGET_ID) ? note.getInt(NoteColumns.WIDGET_ID) : AppWidgetManager.INVALID_APPWIDGET_ID; + // 如果是新建笔记或者当前笔记的小部件ID与传入的小部件ID不同,更新小部件ID if (mIsCreate || mWidgetId != widgetId) { mDiffNoteValues.put(NoteColumns.WIDGET_ID, widgetId); } 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; + // 如果是新建笔记或者当前笔记的原始父ID与传入的原始父ID不同,更新原始父ID 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); + // 查找是否存在对应的SqlData对象 for (SqlData temp : mDataList) { if (dataId == temp.getId()) { sqlData = temp; @@ -344,10 +364,12 @@ public class SqlNote { } if (sqlData == null) { + // 如果不存在对应的SqlData对象,则创建一个新的对象并添加到列表中 sqlData = new SqlData(mContext); mDataList.add(sqlData); } + // 设置SqlData对象的内容 sqlData.setContent(data); } } @@ -358,18 +380,19 @@ public class SqlNote { } return true; } - - public JSONObject getContent() { + ublic 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); @@ -393,6 +416,7 @@ public class SqlNote { } js.put(GTaskStringUtils.META_HEAD_DATA, dataArray); } 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); @@ -406,76 +430,90 @@ public class SqlNote { } return null; } - public void setParentId(long id) { + // 设置父ID mParentId = id; mDiffNoteValues.put(NoteColumns.PARENT_ID, id); } public void setGtaskId(String gid) { + // 设置GTask ID mDiffNoteValues.put(NoteColumns.GTASK_ID, gid); } public void setSyncId(long syncId) { + // 设置同步ID mDiffNoteValues.put(NoteColumns.SYNC_ID, syncId); } public void resetLocalModified() { + // 重置本地修改标志 mDiffNoteValues.put(NoteColumns.LOCAL_MODIFIED, 0); } public long getId() { + // 获取ID return mId; } public long getParentId() { + // 获取父ID return mParentId; } public String getSnippet() { + // 获取摘要 return mSnippet; } public boolean isNoteType() { + // 判断是否为笔记类型 return mType == Notes.TYPE_NOTE; } public void commit(boolean validateVersion) { if (mIsCreate) { + // 如果对象是新创建的 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)); } 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("创建笔记失败"); } if (mId == 0) { - throw new IllegalStateException("Create thread id failed"); + throw new IllegalStateException("创建笔记ID失败"); } if (mType == Notes.TYPE_NOTE) { + // 如果对象是笔记类型,提交每个SqlData对象 for (SqlData sqlData : mDataList) { sqlData.commit(mId, false, -1); } } } else { + // 如果对象不是新创建的 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"); + Log.e(TAG, "Get note id error :"); + throw new IllegalStateException("create note failed"); } if (mDiffNoteValues.size() > 0) { - mVersion ++; + // 更新笔记 + mVersion++; int result = 0; if (!validateVersion) { + // 如果不需要验证版本号,直接更新数据库 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[] { @@ -488,13 +526,14 @@ public class SqlNote { } if (mType == Notes.TYPE_NOTE) { + // 如果对象是笔记类型,提交每个SqlData对象 for (SqlData sqlData : mDataList) { sqlData.commit(mId, validateVersion, mVersion); } } } - // refresh local info + // 刷新本地信息 loadFromCursor(mId); if (mType == Notes.TYPE_NOTE) loadDataContent(); diff --git a/src/src/net/micode/notes/gtask/data/Task.java b/src/src/net/micode/notes/gtask/data/Task.java index 6a19454..24504cf 100644 --- a/src/src/net/micode/notes/gtask/data/Task.java +++ b/src/src/net/micode/notes/gtask/data/Task.java @@ -35,25 +35,26 @@ import org.json.JSONObject; public class Task extends Node { private static final String TAG = Task.class.getSimpleName(); - private boolean mCompleted; + private boolean mCompleted; // 任务是否已完成的标志 - private String mNotes; + private String mNotes; // 任务的备注信息 - private JSONObject mMetaInfo; + private JSONObject mMetaInfo; // 任务的元信息,使用JSONObject表示 - private Task mPriorSibling; + private Task mPriorSibling; // 任务的前一个兄弟节点 - private TaskList mParent; + private TaskList mParent; // 任务所属的任务列表 public Task() { - super(); - mCompleted = false; - mNotes = null; - mPriorSibling = null; - mParent = null; - mMetaInfo = null; - } + super(); // 调用父类的构造方法初始化节点 + mCompleted = false; // 默认任务未完成 + mNotes = null; // 备注信息为空 + mPriorSibling = null; // 前一个兄弟节点为空 + mParent = null; // 所属任务列表为空 + mMetaInfo = null; // 元信息为空 + } +//生成创建任务的JSON对象。该方法根据任务的属性和信息构建了一个包含创建任务所需字段的JSON对象。 public JSONObject getCreateAction(int actionId) { JSONObject js = new JSONObject(); @@ -102,7 +103,7 @@ public class Task extends Node { return js; } - +//生成更新任务的JSON对象。该方法根据任务的属性和信息构建了一个包含更新任务所需字段的JSON对象。 public JSONObject getUpdateAction(int actionId) { JSONObject js = new JSONObject(); @@ -134,7 +135,7 @@ public class Task extends Node { return js; } - +//从远程JSON对象中提取任务的内容并设置到相应的属性中 public void setContentByRemoteJSON(JSONObject js) { if (js != null) { try { @@ -174,7 +175,7 @@ public class Task extends Node { } } } - +//从本地JSON对象中提取任务的内容并设置到相应的属性中 public void setContentByLocalJSON(JSONObject js) { if (js == null || !js.has(GTaskStringUtils.META_HEAD_NOTE) || !js.has(GTaskStringUtils.META_HEAD_DATA)) { @@ -203,12 +204,12 @@ public class Task extends Node { e.printStackTrace(); } } - +//根据任务的内容生成本地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; @@ -225,7 +226,7 @@ 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); @@ -246,7 +247,7 @@ public class Task extends Node { return null; } } - +//设置元数据信息 public void setMetaInfo(MetaData metaData) { if (metaData != null && metaData.getNotes() != null) { try { @@ -266,40 +267,43 @@ public class Task extends Node { } if (noteInfo == null) { + // 如果note元数据为空,表示note元数据已被删除 Log.w(TAG, "it seems that note meta has been deleted"); return SYNC_ACTION_UPDATE_REMOTE; } if (!noteInfo.has(NoteColumns.ID)) { + // 如果note元数据中不包含ID属性,表示远程note的ID已被删除 Log.w(TAG, "remote note id seems to be deleted"); return SYNC_ACTION_UPDATE_LOCAL; } - // validate the note id now + // 验证note的ID是否匹配 if (c.getLong(SqlNote.ID_COLUMN) != noteInfo.getLong(NoteColumns.ID)) { Log.w(TAG, "note id doesn't match"); return SYNC_ACTION_UPDATE_LOCAL; } if (c.getInt(SqlNote.LOCAL_MODIFIED_COLUMN) == 0) { - // there is no local update + // 没有本地更新 if (c.getLong(SqlNote.SYNC_ID_COLUMN) == getLastModified()) { - // no update both side + // 两边都没有更新 return SYNC_ACTION_NONE; } else { - // apply remote to local + // 应用远程更新到本地 return SYNC_ACTION_UPDATE_LOCAL; } } else { - // validate gtask id + // 验证gtask的ID是否匹配 if (!c.getString(SqlNote.GTASK_ID_COLUMN).equals(getGid())) { Log.e(TAG, "gtask id doesn't match"); return SYNC_ACTION_ERROR; } if (c.getLong(SqlNote.SYNC_ID_COLUMN) == getLastModified()) { - // local modification only + // 只有本地修改 return SYNC_ACTION_UPDATE_REMOTE; } else { + // 发生冲突,需要更新解决 return SYNC_ACTION_UPDATE_CONFLICT; } } @@ -312,39 +316,48 @@ public class Task extends Node { } public boolean isWorthSaving() { + // 判断是否值得保存 return mMetaInfo != null || (getName() != null && getName().trim().length() > 0) || (getNotes() != null && getNotes().trim().length() > 0); } public void setCompleted(boolean completed) { + // 设置任务是否已完成 this.mCompleted = completed; } public void setNotes(String notes) { + // 设置任务的备注信息 this.mNotes = notes; } public void setPriorSibling(Task priorSibling) { + // 设置前一个兄弟任务 this.mPriorSibling = priorSibling; } public void setParent(TaskList parent) { + // 设置任务所属的任务列表 this.mParent = parent; } public boolean getCompleted() { + // 获取任务是否已完成 return this.mCompleted; } public String getNotes() { + // 获取任务的备注信息 return this.mNotes; } public Task getPriorSibling() { + // 获取前一个兄弟任务 return this.mPriorSibling; } public TaskList getParent() { + // 获取任务所属的任务列表 return this.mParent; } diff --git a/src/src/net/micode/notes/gtask/data/TaskList.java b/src/src/net/micode/notes/gtask/data/TaskList.java index 4ea21c5..d979ab5 100644 --- a/src/src/net/micode/notes/gtask/data/TaskList.java +++ b/src/src/net/micode/notes/gtask/data/TaskList.java @@ -31,38 +31,38 @@ import java.util.ArrayList; public class TaskList extends Node { - private static final String TAG = TaskList.class.getSimpleName(); + private static final String TAG = TaskList.class.getSimpleName(); - private int mIndex; + private int mIndex; // 任务列表的索引 - private ArrayList mChildren; + private ArrayList mChildren; // 存储任务列表中的子任务的ArrayList对象 - public TaskList() { - super(); - mChildren = new ArrayList(); - mIndex = 1; - } + public TaskList() { + super(); // 调用父类的构造函数(假设父类是superclass) + mChildren = new ArrayList(); // 初始化mChildren为一个新的空的ArrayList对象 + mIndex = 1; // 将mIndex初始化为1 + } public JSONObject getCreateAction(int actionId) { JSONObject js = new JSONObject(); try { - // action_type + // 设置了action_type字段,表示操作类型为创建任务。 js.put(GTaskStringUtils.GTASK_JSON_ACTION_TYPE, GTaskStringUtils.GTASK_JSON_ACTION_TYPE_CREATE); - // action_id + // 设置了action_id字段,表示操作的ID js.put(GTaskStringUtils.GTASK_JSON_ACTION_ID, actionId); - // index + // 设置了index字段,表示任务的索引 js.put(GTaskStringUtils.GTASK_JSON_INDEX, mIndex); - // entity_delta + // 设置了entity对象的name字段,表示任务的名称。 JSONObject entity = new JSONObject(); - entity.put(GTaskStringUtils.GTASK_JSON_NAME, getName()); - entity.put(GTaskStringUtils.GTASK_JSON_CREATOR_ID, "null"); + entity.put(GTaskStringUtils.GTASK_JSON_NAME, getName()); // 设置任务名称 + entity.put(GTaskStringUtils.GTASK_JSON_CREATOR_ID, "null"); // 设置任务创建者ID entity.put(GTaskStringUtils.GTASK_JSON_ENTITY_TYPE, - GTaskStringUtils.GTASK_JSON_TYPE_GROUP); + GTaskStringUtils.GTASK_JSON_TYPE_GROUP); // 设置任务实体类型为组 js.put(GTaskStringUtils.GTASK_JSON_ENTITY_DELTA, entity); } catch (JSONException e) { @@ -90,8 +90,8 @@ public class TaskList extends Node { // entity_delta JSONObject entity = new JSONObject(); - entity.put(GTaskStringUtils.GTASK_JSON_NAME, getName()); - entity.put(GTaskStringUtils.GTASK_JSON_DELETED, getDeleted()); + entity.put(GTaskStringUtils.GTASK_JSON_NAME, getName()); // 设置任务名称 + entity.put(GTaskStringUtils.GTASK_JSON_DELETED, getDeleted()); // 设置任务是否已删除 js.put(GTaskStringUtils.GTASK_JSON_ENTITY_DELTA, entity); } catch (JSONException e) { @@ -139,13 +139,13 @@ public class TaskList extends Node { 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_FOLDER_PREFFIX + 文件夹名称 } 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); + setName(GTaskStringUtils.MIUI_FOLDER_PREFFIX + GTaskStringUtils.FOLDER_DEFAULT);// 设置任务名称为 MIUI_FOLDER_PREFFIX + FOLDER_DEFAULT else if (folder.getLong(NoteColumns.ID) == Notes.ID_CALL_RECORD_FOLDER) setName(GTaskStringUtils.MIUI_FOLDER_PREFFIX - + GTaskStringUtils.FOLDER_CALL_NOTE); + + GTaskStringUtils.FOLDER_CALL_NOTE);// 设置任务名称为 MIUI_FOLDER_PREFFIX + FOLDER_CALL_NOTE else Log.e(TAG, "invalid system folder"); } else { @@ -156,7 +156,7 @@ public class TaskList extends Node { e.printStackTrace(); } } - +//从任务内容生成一个包含任务创建信息的JSONObject对象 public JSONObject getLocalJSONFromContent() { try { JSONObject js = new JSONObject(); @@ -166,14 +166,14 @@ public class TaskList extends Node { if (getName().startsWith(GTaskStringUtils.MIUI_FOLDER_PREFFIX)) folderName = folderName.substring(GTaskStringUtils.MIUI_FOLDER_PREFFIX.length(), folderName.length()); - folder.put(NoteColumns.SNIPPET, folderName); + folder.put(NoteColumns.SNIPPET, folderName); // 将文件夹名称添加到 folder 对象中的 SNIPPET 字段 if (folderName.equals(GTaskStringUtils.FOLDER_DEFAULT) || folderName.equals(GTaskStringUtils.FOLDER_CALL_NOTE)) - folder.put(NoteColumns.TYPE, Notes.TYPE_SYSTEM); + folder.put(NoteColumns.TYPE, Notes.TYPE_SYSTEM); // 如果文件夹名称为 FOLDER_DEFAULT 或 FOLDER_CALL_NOTE,则将文件夹类型设置为 TYPE_SYSTEM else - folder.put(NoteColumns.TYPE, Notes.TYPE_FOLDER); + folder.put(NoteColumns.TYPE, Notes.TYPE_FOLDER); // 否则将文件夹类型设置为 TYPE_FOLDER - js.put(GTaskStringUtils.META_HEAD_NOTE, folder); + js.put(GTaskStringUtils.META_HEAD_NOTE, folder); // 将 folder 对象添加到 js 对象中的 META_HEAD_NOTE 字段 return js; } catch (JSONException e) { @@ -183,29 +183,30 @@ public class TaskList extends Node { } } + 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; + // 双方都没有更新 + return SYNC_ACTION_NONE; // 返回不需要同步的操作 } else { - // apply remote to local - return SYNC_ACTION_UPDATE_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; + Log.e(TAG, "gtask id doesn't match"); // 打印错误日志,表示 gtask id 不匹配 + return SYNC_ACTION_ERROR; // 返回同步操作错误 } if (c.getLong(SqlNote.SYNC_ID_COLUMN) == getLastModified()) { - // local modification only - return SYNC_ACTION_UPDATE_REMOTE; + // 仅本地修改 + return SYNC_ACTION_UPDATE_REMOTE; // 返回将本地修改更新到远程的操作 } else { - // for folder conflicts, just apply local modification - return SYNC_ACTION_UPDATE_REMOTE; + // 对于文件夹冲突,仅应用本地修改 + return SYNC_ACTION_UPDATE_REMOTE; // 返回将本地修改更新到远程的操作 } } } catch (Exception e) { @@ -213,131 +214,130 @@ public class TaskList extends Node { e.printStackTrace(); } - return SYNC_ACTION_ERROR; + return SYNC_ACTION_ERROR; // 返回同步操作错误 } public int getChildTaskCount() { - return mChildren.size(); + return mChildren.size(); // 返回子任务列表的大小,即子任务的数量 } public boolean addChildTask(Task task) { - boolean ret = false; - if (task != null && !mChildren.contains(task)) { - ret = mChildren.add(task); + boolean ret = false; // 初始化返回值为 false + if (task != null && !mChildren.contains(task)) { // 如果传入的任务不为空且子任务列表中不包含该任务 + ret = mChildren.add(task); // 将任务添加到子任务列表中,并将返回值赋给 ret if (ret) { - // need to set prior sibling and parent - task.setPriorSibling(mChildren.isEmpty() ? null : mChildren - .get(mChildren.size() - 1)); - task.setParent(this); + // 需要设置前一个兄弟任务和父任务 + task.setPriorSibling(mChildren.isEmpty() ? null : mChildren.get(mChildren.size() - 1)); // 设置任务的前一个兄弟任务,如果子任务列表为空,则设置为 null,否则设置为最后一个子任务 + task.setParent(this); // 设置任务的父任务为当前任务 } } - return ret; + return ret; // 返回添加子任务的结果,如果成功添加,则返回 true,否则返回 false } public boolean addChildTask(Task task, int index) { - if (index < 0 || index > mChildren.size()) { - Log.e(TAG, "add child task: invalid index"); - return false; + if (index < 0 || index > mChildren.size()) { // 如果索引小于0或大于子任务列表的大小 + Log.e(TAG, "add child task: invalid index"); // 打印错误日志,表示索引无效 + return false; // 返回添加子任务失败 } - int pos = mChildren.indexOf(task); - if (task != null && pos == -1) { - mChildren.add(index, task); + int pos = mChildren.indexOf(task); // 获取任务在子任务列表中的索引 + if (task != null && pos == -1) { // 如果任务不为空且不在子任务列表中 + mChildren.add(index, task); // 将任务添加到指定索引位置 - // update the task list + // 更新任务列表 Task preTask = null; Task afterTask = null; if (index != 0) - preTask = mChildren.get(index - 1); + preTask = mChildren.get(index - 1); // 获取插入位置的前一个任务 if (index != mChildren.size() - 1) - afterTask = mChildren.get(index + 1); + afterTask = mChildren.get(index + 1); // 获取插入位置的后一个任务 - task.setPriorSibling(preTask); + task.setPriorSibling(preTask); // 设置任务的前一个兄弟任务为 preTask if (afterTask != null) - afterTask.setPriorSibling(task); + afterTask.setPriorSibling(task); // 如果后一个任务不为空,设置后一个任务的前一个兄弟任务为当前任务 } - return true; + return true; // 返回添加子任务成功 } public boolean removeChildTask(Task task) { - boolean ret = false; - int index = mChildren.indexOf(task); - if (index != -1) { - ret = mChildren.remove(task); + boolean ret = false; // 初始化返回值为 false + int index = mChildren.indexOf(task); // 获取任务在子任务列表中的索引 + if (index != -1) { // 如果任务存在于子任务列表中 + ret = mChildren.remove(task); // 从子任务列表中移除任务 if (ret) { - // reset prior sibling and parent - task.setPriorSibling(null); - task.setParent(null); + // 重置前一个兄弟任务和父任务 + task.setPriorSibling(null); // 将任务的前一个兄弟任务设置为 null + task.setParent(null); // 将任务的父任务设置为 null - // update the task list + // 更新任务列表 if (index != mChildren.size()) { mChildren.get(index).setPriorSibling( - index == 0 ? null : mChildren.get(index - 1)); + index == 0 ? null : mChildren.get(index - 1)); // 如果移除任务后还有后续任务,将后续任务的前一个兄弟任务设置为移除任务的前一个任务 } } } - return ret; + return ret; // 返回移除子任务的结果,如果成功移除,则返回 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; + if (index < 0 || index >= mChildren.size()) { // 如果索引小于0或大于等于子任务列表的大小 + Log.e(TAG, "move child task: invalid index"); // 打印错误日志,表示索引无效 + return false; // 返回移动子任务失败 } - int pos = mChildren.indexOf(task); - if (pos == -1) { - Log.e(TAG, "move child task: the task should in the list"); - return false; + int pos = mChildren.indexOf(task); // 获取任务在子任务列表中的索引 + if (pos == -1) { // 如果任务不在子任务列表中 + Log.e(TAG, "move child task: the task should be in the list"); // 打印错误日志,表示任务应该在列表中 + return false; // 返回移动子任务失败 } if (pos == index) - return true; - return (removeChildTask(task) && addChildTask(task, index)); + return true; // 如果任务的当前索引等于目标索引,直接返回 true + + return (removeChildTask(task) && addChildTask(task, index)); // 移除任务并将其添加到目标索引位置 } public Task findChildTaskByGid(String gid) { - for (int i = 0; i < mChildren.size(); i++) { - Task t = mChildren.get(i); - if (t.getGid().equals(gid)) { - return t; + for (int i = 0; i < mChildren.size(); i++) { // 遍历子任务列表 + Task t = mChildren.get(i); // 获取当前位置的任务 + if (t.getGid().equals(gid)) { // 如果任务的 GID(全局唯一标识符)与给定的 GID 相等 + return t; // 返回找到的任务 } } - return null; + return null; // 如果未找到匹配的任务,返回 null } public int getChildTaskIndex(Task task) { - return mChildren.indexOf(task); + return mChildren.indexOf(task); // 返回任务在子任务列表中的索引,如果任务不在列表中,则返回 -1 } public Task getChildTaskByIndex(int index) { - if (index < 0 || index >= mChildren.size()) { - Log.e(TAG, "getTaskByIndex: invalid index"); - return null; + if (index < 0 || index >= mChildren.size()) { // 如果索引小于0或大于等于子任务列表的大小 + Log.e(TAG, "getTaskByIndex: invalid index"); // 打印错误日志,表示索引无效 + return null; // 返回 null } - return mChildren.get(index); + return mChildren.get(index); // 返回指定索引位置的子任务 } - public Task getChilTaskByGid(String gid) { - for (Task task : mChildren) { - if (task.getGid().equals(gid)) - return task; + public Task getChildTaskByGid(String gid) { + for (Task task : mChildren) { // 遍历子任务列表 + if (task.getGid().equals(gid)) // 如果任务的 GID(全局唯一标识符)与给定的 GID 相等 + return task; // 返回找到的任务 } - return null; + return null; // 如果未找到匹配的任务,返回 null } public ArrayList getChildTaskList() { - return this.mChildren; + return this.mChildren; // 返回子任务列表 } public void setIndex(int index) { - this.mIndex = index; + this.mIndex = index; // 设置索引 } public int getIndex() { - return this.mIndex; + return this.mIndex; // 返回索引 } } diff --git a/src/src/net/micode/notes/gtask/exception/ActionFailureException.java b/src/src/net/micode/notes/gtask/exception/ActionFailureException.java index 15504be..6b20158 100644 --- a/src/src/net/micode/notes/gtask/exception/ActionFailureException.java +++ b/src/src/net/micode/notes/gtask/exception/ActionFailureException.java @@ -15,7 +15,7 @@ */ package net.micode.notes.gtask.exception; - +//自定义异常类可以用于表示动作执行失败的情况。在代码中,如果某个动作失败,可以抛出ActionFailureException异常,并在异常处理的地方进行相应的处理。 public class ActionFailureException extends RuntimeException { private static final long serialVersionUID = 4425249765923293627L; diff --git a/src/src/net/micode/notes/gtask/exception/NetworkFailureException.java b/src/src/net/micode/notes/gtask/exception/NetworkFailureException.java index b08cfb1..33847b9 100644 --- a/src/src/net/micode/notes/gtask/exception/NetworkFailureException.java +++ b/src/src/net/micode/notes/gtask/exception/NetworkFailureException.java @@ -15,7 +15,7 @@ */ package net.micode.notes.gtask.exception; - +//用于表示网络连接失败的情况。在代码中,如果发生网络连接失败的情况,可以抛出NetworkFailureException异常,并在异常处理的地方进行相应的处理。 public class NetworkFailureException extends Exception { private static final long serialVersionUID = 2107610287180234136L; diff --git a/src/src/net/micode/notes/gtask/remote/GTaskASyncTask.java b/src/src/net/micode/notes/gtask/remote/GTaskASyncTask.java index b3b61e7..2d7c48e 100644 --- a/src/src/net/micode/notes/gtask/remote/GTaskASyncTask.java +++ b/src/src/net/micode/notes/gtask/remote/GTaskASyncTask.java @@ -31,91 +31,85 @@ import net.micode.notes.ui.NotesPreferenceActivity; public class GTaskASyncTask extends AsyncTask { - private static int GTASK_SYNC_NOTIFICATION_ID = 5234235; - - public interface OnCompleteListener { - void onComplete(); - } - - private Context mContext; - - private NotificationManager mNotifiManager; - - private GTaskManager mTaskManager; - - private OnCompleteListener mOnCompleteListener; - - public GTaskASyncTask(Context context, OnCompleteListener listener) { - mContext = context; - mOnCompleteListener = listener; - mNotifiManager = (NotificationManager) mContext - .getSystemService(Context.NOTIFICATION_SERVICE); - mTaskManager = GTaskManager.getInstance(); - } - - public void cancelSync() { - mTaskManager.cancelSync(); - } - - public void publishProgess(String message) { - publishProgress(new String[] { - message - }); - } - - 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); - - } else { - pendingIntent = PendingIntent.getActivity(mContext, 0, new Intent(mContext, - NotesListActivity.class), 0); - } - notification.setLatestEventInfo(mContext, mContext.getString(R.string.app_name), content, - pendingIntent); - mNotifiManager.notify(GTASK_SYNC_NOTIFICATION_ID, notification); - } + private static int GTASK_SYNC_NOTIFICATION_ID = 5234235; + + public interface OnCompleteListener { + void onComplete(); + } + + private Context mContext; // 上下文对象 + private NotificationManager mNotifiManager; // 通知管理器 + private GTaskManager mTaskManager; // GTask管理器 + private OnCompleteListener mOnCompleteListener; // 完成监听器 + + public GTaskASyncTask(Context context, OnCompleteListener listener) { + mContext = context; + mOnCompleteListener = listener; + mNotifiManager = (NotificationManager) mContext.getSystemService(Context.NOTIFICATION_SERVICE); + mTaskManager = GTaskManager.getInstance(); + } + + public void cancelSync() { + mTaskManager.cancelSync(); // 取消同步任务 + } + + public void publishProgess(String message) { + publishProgress(new String[] { + message + }); // 发布进度消息 + } + + 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) { + // 如果tickerId不是R.string.ticker_success,创建一个打开NotesPreferenceActivity的PendingIntent + pendingIntent = PendingIntent.getActivity(mContext, 0, new Intent(mContext, NotesPreferenceActivity.class), 0); + } else { + // 否则,创建一个打开NotesListActivity的PendingIntent + pendingIntent = PendingIntent.getActivity(mContext, 0, new Intent(mContext, NotesListActivity.class), 0); + } + + notification.setLatestEventInfo(mContext, mContext.getString(R.string.app_name), content, pendingIntent); // 设置通知的标题、内容和点击事件 + + mNotifiManager.notify(GTASK_SYNC_NOTIFICATION_ID, notification); // 发送通知 + } @Override protected Integer doInBackground(Void... unused) { - publishProgess(mContext.getString(R.string.sync_progress_login, NotesPreferenceActivity - .getSyncAccountName(mContext))); - return mTaskManager.sync(mContext, this); + publishProgess(mContext.getString(R.string.sync_progress_login, NotesPreferenceActivity.getSyncAccountName(mContext))); + return mTaskManager.sync(mContext, this); // 执行后台任务,同步数据,并返回结果 } @Override protected void onProgressUpdate(String... progress) { - showNotification(R.string.ticker_syncing, progress[0]); + showNotification(R.string.ticker_syncing, progress[0]); // 更新通知显示同步进度 if (mContext instanceof GTaskSyncService) { - ((GTaskSyncService) mContext).sendBroadcast(progress[0]); + ((GTaskSyncService) mContext).sendBroadcast(progress[0]); // 如果上下文是GTaskSyncService的实例,发送广播更新同步进度 } } @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()); + 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)); + 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)); + 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)); + showNotification(R.string.ticker_cancel, mContext.getString(R.string.error_sync_cancelled)); // 显示同步取消的通知,并显示取消同步的错误信息 } if (mOnCompleteListener != null) { new Thread(new Runnable() { - public void run() { - mOnCompleteListener.onComplete(); + mOnCompleteListener.onComplete(); // 如果完成监听器不为空,创建一个新线程并调用onComplete方法 } }).start(); } diff --git a/src/src/net/micode/notes/gtask/remote/GTaskClient.java b/src/src/net/micode/notes/gtask/remote/GTaskClient.java index c67dfdf..817fd3f 100644 --- a/src/src/net/micode/notes/gtask/remote/GTaskClient.java +++ b/src/src/net/micode/notes/gtask/remote/GTaskClient.java @@ -62,268 +62,271 @@ import java.util.zip.InflaterInputStream; public class GTaskClient { - private static final String TAG = GTaskClient.class.getSimpleName(); - - private static final String GTASK_URL = "https://mail.google.com/tasks/"; - - private static final String GTASK_GET_URL = "https://mail.google.com/tasks/ig"; - - private static final String GTASK_POST_URL = "https://mail.google.com/tasks/r/ig"; - - private static GTaskClient mInstance = null; - - private DefaultHttpClient mHttpClient; - - private String mGetUrl; - - private String mPostUrl; - - private long mClientVersion; - - private boolean mLoggedin; - - private long mLastLoginTime; - - private int mActionId; - - private Account mAccount; - - private JSONArray mUpdateArray; - - private GTaskClient() { - mHttpClient = null; - mGetUrl = GTASK_GET_URL; - mPostUrl = GTASK_POST_URL; - mClientVersion = -1; - mLoggedin = false; - mLastLoginTime = 0; - mActionId = 1; - mAccount = null; - mUpdateArray = null; - } - - public static synchronized GTaskClient getInstance() { - if (mInstance == null) { - mInstance = new GTaskClient(); - } - return mInstance; - } - - public boolean login(Activity activity) { - // we suppose that the cookie would expire after 5 minutes - // then we need to re-login - 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))) { - mLoggedin = false; - } - - if (mLoggedin) { - Log.d(TAG, "already logged in"); - return true; - } - - mLastLoginTime = System.currentTimeMillis(); - 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"))) { - StringBuilder url = new StringBuilder(GTASK_URL).append("a/"); - int index = mAccount.name.indexOf('@') + 1; - String suffix = mAccount.name.substring(index); - url.append(suffix + "/"); - mGetUrl = url.toString() + "ig"; - mPostUrl = url.toString() + "r/ig"; - - if (tryToLoginGtask(activity, authToken)) { - mLoggedin = true; - } - } - - // try to login with google official url - if (!mLoggedin) { - mGetUrl = GTASK_GET_URL; - mPostUrl = GTASK_POST_URL; - if (!tryToLoginGtask(activity, authToken)) { - return false; - } - } - - mLoggedin = true; - return true; - } - - private String loginGoogleAccount(Activity activity, boolean invalidateToken) { - String authToken; - AccountManager accountManager = AccountManager.get(activity); - 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) { - if (a.name.equals(accountName)) { - account = a; - break; - } - } - if (account != null) { - mAccount = account; - } else { - Log.e(TAG, "unable to get an account with the same name in the settings"); - 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); - } - } catch (Exception e) { - Log.e(TAG, "get auth token failed"); - authToken = null; - } - - return authToken; - } - - 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; - } - } - return true; - } - - private boolean loginGtask(String authToken) { - int timeoutConnection = 10000; - int timeoutSocket = 15000; - HttpParams httpParameters = new BasicHttpParams(); - HttpConnectionParams.setConnectionTimeout(httpParameters, timeoutConnection); - HttpConnectionParams.setSoTimeout(httpParameters, timeoutSocket); - 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 - List cookies = mHttpClient.getCookieStore().getCookies(); - boolean hasAuthCookie = false; - for (Cookie cookie : cookies) { - if (cookie.getName().contains("GTL")) { - hasAuthCookie = true; - } - } - if (!hasAuthCookie) { - 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 = ")}"; - int begin = resString.indexOf(jsBegin); - int end = resString.lastIndexOf(jsEnd); - String jsString = null; - if (begin != -1 && end != -1 && begin < end) { - jsString = resString.substring(begin + jsBegin.length(), end); - } - JSONObject js = new JSONObject(jsString); - mClientVersion = js.getLong("v"); - } catch (JSONException e) { - Log.e(TAG, e.toString()); - e.printStackTrace(); - return false; - } catch (Exception e) { - // simply catch all exceptions - Log.e(TAG, "httpget gtask_url failed"); - return false; - } - - return true; - } - - private int getActionId() { - return mActionId++; - } - - private HttpPost createHttpPost() { - HttpPost httpPost = new HttpPost(mPostUrl); - httpPost.setHeader("Content-Type", "application/x-www-form-urlencoded;charset=utf-8"); - httpPost.setHeader("AT", "1"); - return httpPost; - } - - 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()); - } else if (contentEncoding != null && contentEncoding.equalsIgnoreCase("deflate")) { - Inflater inflater = new Inflater(true); - input = new InflaterInputStream(entity.getContent(), inflater); - } - - try { - InputStreamReader isr = new InputStreamReader(input); - BufferedReader br = new BufferedReader(isr); - StringBuilder sb = new StringBuilder(); - - while (true) { - String buff = br.readLine(); - if (buff == null) { - return sb.toString(); - } - sb = sb.append(buff); - } - } finally { - input.close(); - } - } + private static final String TAG = GTaskClient.class.getSimpleName(); + + private static final String GTASK_URL = "https://mail.google.com/tasks/"; + private static final String GTASK_GET_URL = "https://mail.google.com/tasks/ig"; + private static final String GTASK_POST_URL = "https://mail.google.com/tasks/r/ig"; + + private static GTaskClient mInstance = null; + + private DefaultHttpClient mHttpClient; // HTTP客户端对象 + + private String mGetUrl; // GET请求的URL + private String mPostUrl; // POST请求的URL + + private long mClientVersion; // 客户端版本号 + + private boolean mLoggedin; // 是否已登录 + private long mLastLoginTime; // 上次登录时间 + + private int mActionId; // 操作ID + + private Account mAccount; // 账户信息 + + private JSONArray mUpdateArray; // 更新数据的JSON数组 + + private GTaskClient() { + mHttpClient = null; + mGetUrl = GTASK_GET_URL; + mPostUrl = GTASK_POST_URL; + mClientVersion = -1; + mLoggedin = false; + mLastLoginTime = 0; + mActionId = 1; + mAccount = null; + mUpdateArray = null; + } + + public static synchronized GTaskClient getInstance() { + if (mInstance == null) { + mInstance = new GTaskClient(); // 获取GTaskClient的单例实例 + } + return mInstance; + } + + public boolean login(Activity activity) { + // 假设Cookie在5分钟后过期,需要重新登录 + final long interval = 1000 * 60 * 5; + if (mLastLoginTime + interval < System.currentTimeMillis()) { + mLoggedin = false; + } + + // 在切换账户后需要重新登录 + if (mLoggedin && !TextUtils.equals(getSyncAccount().name, NotesPreferenceActivity.getSyncAccountName(activity))) { + mLoggedin = false; + } + + if (mLoggedin) { + Log.d(TAG, "already logged in"); // 已经登录,直接返回true + return true; + } + + mLastLoginTime = System.currentTimeMillis(); + String authToken = loginGoogleAccount(activity, false); // 使用Google账户登录,获取授权令牌 + if (authToken == null) { + Log.e(TAG, "login google account failed"); // Google账户登录失败,返回false + return false; + } + + // 如果需要,使用自定义域名登录 + 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); + url.append(suffix + "/"); + mGetUrl = url.toString() + "ig"; + mPostUrl = url.toString() + "r/ig"; + + if (tryToLoginGtask(activity, authToken)) { + mLoggedin = true; + } + } + + // 尝试使用Google官方URL登录 + if (!mLoggedin) { + mGetUrl = GTASK_GET_URL; + mPostUrl = GTASK_POST_URL; + if (!tryToLoginGtask(activity, authToken)) { + return false; + } + } + + mLoggedin = true; + return true; + } +//使用Google账户登录并获取授权令牌 + private String loginGoogleAccount(Activity activity, boolean invalidateToken) { + String authToken; + AccountManager accountManager = AccountManager.get(activity); + Account[] accounts = accountManager.getAccountsByType("com.google"); + + if (accounts.length == 0) { + Log.e(TAG, "there is no available google account"); // 没有可用的Google账户,返回null + return null; + } + + String accountName = NotesPreferenceActivity.getSyncAccountName(activity); + Account account = null; + for (Account a : accounts) { + if (a.name.equals(accountName)) { + account = a; + break; + } + } + if (account != null) { + mAccount = account; // 设置当前账户 + } else { + Log.e(TAG, "unable to get an account with the same name in the settings"); // 无法获取与设置中的同名账户,返回null + return null; + } + + // 获取授权令牌 + 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); // 重新登录 + } + } catch (Exception e) { + Log.e(TAG, "get auth token failed"); // 获取授权令牌失败,返回null + authToken = null; + } + + return authToken; + } + + private boolean tryToLoginGtask(Activity activity, String authToken) { + if (!loginGtask(authToken)) { + // 如果登录GTask失败,则可能是授权令牌已过期,现在让我们使令牌无效并重试 + authToken = loginGoogleAccount(activity, true); // 使授权令牌无效并重新登录Google账户 + if (authToken == null) { + Log.e(TAG, "login google account failed"); // Google账户登录失败,返回false + return false; + } + + if (!loginGtask(authToken)) { + Log.e(TAG, "login gtask failed"); // GTask登录失败,返回false + return false; + } + } + return true; + } + + private boolean loginGtask(String authToken) { + int timeoutConnection = 10000; + int timeoutSocket = 15000; + HttpParams httpParameters = new BasicHttpParams(); + HttpConnectionParams.setConnectionTimeout(httpParameters, timeoutConnection); + HttpConnectionParams.setSoTimeout(httpParameters, timeoutSocket); + mHttpClient = new DefaultHttpClient(httpParameters); + BasicCookieStore localBasicCookieStore = new BasicCookieStore(); + mHttpClient.setCookieStore(localBasicCookieStore); + HttpProtocolParams.setUseExpectContinue(mHttpClient.getParams(), false); + + // 登录GTask + try { + String loginUrl = mGetUrl + "?auth=" + authToken; + HttpGet httpGet = new HttpGet(loginUrl); + HttpResponse response = null; + response = mHttpClient.execute(httpGet); + + // 获取Cookie + List cookies = mHttpClient.getCookieStore().getCookies(); + boolean hasAuthCookie = false; + for (Cookie cookie : cookies) { + if (cookie.getName().contains("GTL")) { + hasAuthCookie = true; + } + } + if (!hasAuthCookie) { + Log.w(TAG, "it seems that there is no auth cookie"); // 似乎没有授权Cookie + } + + // 获取客户端版本 + String resString = getResponseContent(response.getEntity()); + String jsBegin = "_setup("; + String jsEnd = ")}"; + int begin = resString.indexOf(jsBegin); + int end = resString.lastIndexOf(jsEnd); + String jsString = null; + if (begin != -1 && end != -1 && begin < end) { + jsString = resString.substring(begin + jsBegin.length(), end); + } + JSONObject js = new JSONObject(jsString); + mClientVersion = js.getLong("v"); + } catch (JSONException e) { + Log.e(TAG, e.toString()); // 解析JSON异常 + e.printStackTrace(); + return false; + } catch (Exception e) { + // 捕获所有异常 + Log.e(TAG, "httpget gtask_url failed"); // GTask URL请求失败 + return false; + } + + return true; + } + + private int getActionId() { + return mActionId++; + } + + /** + * 创建一个HttpPost对象 + * 设置请求头部的Content-Type和AT字段 + * @return 创建的HttpPost对象 + */ + private HttpPost createHttpPost() { + HttpPost httpPost = new HttpPost(mPostUrl); + httpPost.setHeader("Content-Type", "application/x-www-form-urlencoded;charset=utf-8"); + httpPost.setHeader("AT", "1"); + return httpPost; + } + + /** + * 获取HTTP响应的内容 + * @param entity HTTP实体对象 + * @return 响应内容的字符串表示 + * @throws IOException 当读取响应内容时发生I/O错误 + */ + 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()); + } else if (contentEncoding != null && contentEncoding.equalsIgnoreCase("deflate")) { + Inflater inflater = new Inflater(true); + input = new InflaterInputStream(entity.getContent(), inflater); + } + + try { + InputStreamReader isr = new InputStreamReader(input); + BufferedReader br = new BufferedReader(isr); + StringBuilder sb = new StringBuilder(); + + while (true) { + String buff = br.readLine(); + if (buff == null) { + return sb.toString(); + } + sb = sb.append(buff); + } + } finally { + input.close(); + } + } private JSONObject postRequest(JSONObject js) throws NetworkFailureException { + // 检查是否已登录 if (!mLoggedin) { Log.e(TAG, "please login first"); throw new ActionFailureException("not logged in"); @@ -336,7 +339,7 @@ public class GTaskClient { UrlEncodedFormEntity entity = new UrlEncodedFormEntity(list, "UTF-8"); httpPost.setEntity(entity); - // execute the post + // 执行 POST 请求 HttpResponse response = mHttpClient.execute(httpPost); String jsString = getResponseContent(response.getEntity()); return new JSONObject(jsString); @@ -366,17 +369,20 @@ public class GTaskClient { JSONObject jsPost = new JSONObject(); JSONArray actionList = new JSONArray(); - // action_list + // 创建 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 + // 发起 POST 请求并获取响应 JSONObject jsResponse = postRequest(jsPost); - JSONObject jsResult = (JSONObject) jsResponse.getJSONArray( - GTaskStringUtils.GTASK_JSON_RESULTS).get(0); + + // 从响应中获取结果数组,并取第一个元素 + JSONObject jsResult = (JSONObject) jsResponse.getJSONArray(GTaskStringUtils.GTASK_JSON_RESULTS).get(0); + + // 从结果中获取新创建任务的 ID,并设置给 Task 对象 task.setGid(jsResult.getString(GTaskStringUtils.GTASK_JSON_NEW_ID)); } catch (JSONException e) { @@ -392,14 +398,14 @@ public class GTaskClient { JSONObject jsPost = new JSONObject(); JSONArray actionList = new JSONArray(); - // action_list + // 创建 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 + // 发起 POST 请求并获取响应 JSONObject jsResponse = postRequest(jsPost); JSONObject jsResult = (JSONObject) jsResponse.getJSONArray( GTaskStringUtils.GTASK_JSON_RESULTS).get(0); @@ -417,10 +423,10 @@ public class GTaskClient { try { JSONObject jsPost = new JSONObject(); - // action_list + // 设置 action_list jsPost.put(GTaskStringUtils.GTASK_JSON_ACTION_LIST, mUpdateArray); - // client_version + // 设置客户端版本号 jsPost.put(GTaskStringUtils.GTASK_JSON_CLIENT_VERSION, mClientVersion); postRequest(jsPost); @@ -435,8 +441,7 @@ public class GTaskClient { 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(); } @@ -455,26 +460,25 @@ public class GTaskClient { JSONArray actionList = new JSONArray(); JSONObject action = new JSONObject(); - // action_list + // 创建 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 + // 仅在任务在同一任务列表内移动且不是第一个任务时,添加 prior_sibling_id 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 + // 仅在任务在不同任务列表之间移动时,添加 dest_list 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); @@ -492,12 +496,12 @@ public class GTaskClient { JSONObject jsPost = new JSONObject(); JSONArray actionList = new JSONArray(); - // action_list + // // 创建 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); @@ -520,7 +524,7 @@ public class GTaskClient { HttpResponse response = null; response = mHttpClient.execute(httpGet); - // get the task list + // 获取任务列表 String resString = getResponseContent(response.getEntity()); String jsBegin = "_setup("; String jsEnd = ")}"; @@ -554,7 +558,7 @@ public class GTaskClient { JSONArray actionList = new JSONArray(); JSONObject action = new JSONObject(); - // action_list + // 创建 action_list 数组并添加获取任务列表的操作 action.put(GTaskStringUtils.GTASK_JSON_ACTION_TYPE, GTaskStringUtils.GTASK_JSON_ACTION_TYPE_GETALL); action.put(GTaskStringUtils.GTASK_JSON_ACTION_ID, getActionId()); @@ -563,7 +567,7 @@ public class GTaskClient { 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); diff --git a/src/src/net/micode/notes/gtask/remote/GTaskManager.java b/src/src/net/micode/notes/gtask/remote/GTaskManager.java index d2b4082..22d0621 100644 --- a/src/src/net/micode/notes/gtask/remote/GTaskManager.java +++ b/src/src/net/micode/notes/gtask/remote/GTaskManager.java @@ -49,432 +49,415 @@ import java.util.Map; 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; - - private Activity mActivity; - - private Context mContext; - - private ContentResolver mContentResolver; - - private boolean mSyncing; - - private boolean mCancelled; - - private HashMap mGTaskListHashMap; - - private HashMap mGTaskHashMap; - - private HashMap mMetaHashMap; - - private TaskList mMetaList; - - private HashSet mLocalDeleteIdMap; - - private HashMap mGidToNid; - - private HashMap mNidToGid; - - private GTaskManager() { - mSyncing = false; - mCancelled = false; - mGTaskListHashMap = new HashMap(); - mGTaskHashMap = new HashMap(); - mMetaHashMap = new HashMap(); - mMetaList = null; - mLocalDeleteIdMap = new HashSet(); - mGidToNid = new HashMap(); - mNidToGid = new HashMap(); - } - - public static synchronized GTaskManager getInstance() { - if (mInstance == null) { - mInstance = new GTaskManager(); - } - return mInstance; - } - - 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(); - mLocalDeleteIdMap.clear(); - mGidToNid.clear(); - mNidToGid.clear(); - - try { - GTaskClient client = GTaskClient.getInstance(); - client.resetUpdateArray(); - - // login google task - 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(); - mLocalDeleteIdMap.clear(); - mGidToNid.clear(); - mNidToGid.clear(); - mSyncing = false; - } - - return mCancelled ? STATE_SYNC_CANCELLED : STATE_SUCCESS; - } - - private void initGTaskList() throws NetworkFailureException { - if (mCancelled) - return; - GTaskClient client = GTaskClient.getInstance(); - try { - JSONArray jsTaskLists = client.getTaskLists(); - - // init meta list first - mMetaList = null; - 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); - - 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); - MetaData metaData = new MetaData(); - metaData.setContentByRemoteJSON(object); - if (metaData.isWorthSaving()) { - mMetaList.addChildTask(metaData); - if (metaData.getGid() != null) { - mMetaHashMap.put(metaData.getRelatedGid(), metaData); - } - } - } - } - } - - // create meta list if not existed - if (mMetaList == null) { - mMetaList = new TaskList(); - mMetaList.setName(GTaskStringUtils.MIUI_FOLDER_PREFFIX - + GTaskStringUtils.FOLDER_META); - 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); - - if (name.startsWith(GTaskStringUtils.MIUI_FOLDER_PREFFIX) - && !name.equals(GTaskStringUtils.MIUI_FOLDER_PREFFIX - + 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); - gid = object.getString(GTaskStringUtils.GTASK_JSON_ID); - Task task = new Task(); - task.setContentByRemoteJSON(object); - if (task.isWorthSaving()) { - task.setMetaInfo(mMetaHashMap.get(gid)); - tasklist.addChildTask(task); - mGTaskHashMap.put(gid, task); - } - } - } - } - } catch (JSONException e) { - 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; - - 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()) { - 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 { - 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()) { - 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 note in database"); - } - - } finally { - if (c != null) { - c.close(); - c = null; - } - } - - // go through remaining items - Iterator> iter = mGTaskHashMap.entrySet().iterator(); - while (iter.hasNext()) { - Map.Entry entry = iter.next(); - node = entry.getValue(); - 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 - 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(); - 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); - } else { - doContentSync(Node.SYNC_ACTION_ADD_REMOTE, node, c); - } - } else { - Log.w(TAG, "failed to query root folder"); - } - } finally { - 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) - }, null); - if (c != null) { - if (c.moveToNext()) { - 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)) - doContentSync(Node.SYNC_ACTION_UPDATE_REMOTE, node, c); - } else { - doContentSync(Node.SYNC_ACTION_ADD_REMOTE, node, c); - } - } - } else { - Log.w(TAG, "failed to query call note folder"); - } - } finally { - 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()) { - 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 { - if (c != null) { - c.close(); - c = null; - } - } - - // for remote add folders - Iterator> iter = mGTaskListHashMap.entrySet().iterator(); - while (iter.hasNext()) { - Map.Entry entry = iter.next(); - gid = entry.getKey(); - node = entry.getValue(); - if (mGTaskHashMap.containsKey(gid)) { - mGTaskHashMap.remove(gid); - doContentSync(Node.SYNC_ACTION_ADD_LOCAL, node, null); - } - } - - if (!mCancelled) - GTaskClient.getInstance().commitUpdate(); - } + 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; // GTaskManager 单例实例 + private Activity mActivity; // 当前活动的 Activity + private Context mContext; // 上下文对象 + private ContentResolver mContentResolver; // ContentResolver 对象,用于访问应用程序的数据 + private boolean mSyncing; // 是否正在进行同步 + private boolean mCancelled; // 是否已取消同步 + private HashMap mGTaskListHashMap; // 任务列表的哈希映射,键为列表的 GID,值为 TaskList 对象 + private HashMap mGTaskHashMap; // 任务的哈希映射,键为任务的 GID,值为 Node 对象 + private HashMap mMetaHashMap; // 元数据的哈希映射,键为元数据的 GID,值为 MetaData 对象 + private TaskList mMetaList; // 元数据列表 + private HashSet mLocalDeleteIdMap; // 本地已删除任务的 ID 集合 + private HashMap mGidToNid; // GID 到 NID 的映射,用于快速查找任务的本地 ID + private HashMap mNidToGid; // NID 到 GID 的映射,用于快速查找任务的全局 ID + + private GTaskManager() { + mSyncing = false; // 初始化同步状态为未进行同步 + mCancelled = false; // 初始化取消状态为未取消 + mGTaskListHashMap = new HashMap(); // 初始化任务列表的哈希映射 + mGTaskHashMap = new HashMap(); // 初始化任务的哈希映射 + mMetaHashMap = new HashMap(); // 初始化元数据的哈希映射 + mMetaList = null; // 初始化元数据列表为 null + mLocalDeleteIdMap = new HashSet(); // 初始化本地已删除任务的 ID 集合 + mGidToNid = new HashMap(); // 初始化 GID 到 NID 的映射 + mNidToGid = new HashMap(); // 初始化 NID 到 GID 的映射 + } + + public static synchronized GTaskManager getInstance() { + if (mInstance == null) { + mInstance = new GTaskManager(); // 如果单例实例为空,则创建新的实例 + } + return mInstance; // 返回单例实例 + } + + public synchronized void setActivityContext(Activity activity) { + // 用于获取授权令牌的活动上下文 + 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(); // 清空元数据的哈希映射 + mLocalDeleteIdMap.clear(); // 清空本地已删除任务的 ID 集合 + mGidToNid.clear(); // 清空 GID 到 NID 的映射 + mNidToGid.clear(); // 清空 NID 到 GID 的映射 + + try { + GTaskClient client = GTaskClient.getInstance(); + client.resetUpdateArray(); // 重置更新数组 + + // 登录 Google 任务 + if (!mCancelled) { + if (!client.login(mActivity)) { + throw new NetworkFailureException("login google task failed"); + } + } + + // 从 Google 获取任务列表 + asyncTask.publishProgess(mContext.getString(R.string.sync_progress_init_list)); + initGTaskList(); + + // 执行内容同步工作 + 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(); // 清空元数据的哈希映射 + mLocalDeleteIdMap.clear(); // 清空本地已删除任务的 ID 集合 + mGidToNid.clear(); // 清空 GID 到 NID 的映射 + mNidToGid.clear(); // 清空 NID 到 GID 的映射 + mSyncing = false; // 设置同步状态为未进行同步 + } + + return mCancelled ? STATE_SYNC_CANCELLED : STATE_SUCCESS; // 返回同步状态 + } + + private void initGTaskList() throws NetworkFailureException { + if (mCancelled) + return; + GTaskClient client = GTaskClient.getInstance(); + try { + // 获取任务列表 + JSONArray jsTaskLists = client.getTaskLists(); + + // 初始化元数据列表 + mMetaList = null; + 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); + + // 查找元数据列表 + if (name.equals(GTaskStringUtils.MIUI_FOLDER_PREFFIX + GTaskStringUtils.FOLDER_META)) { + mMetaList = new TaskList(); + mMetaList.setContentByRemoteJSON(object); + + // 加载元数据 + JSONArray jsMetas = client.getTaskList(gid); + for (int j = 0; j < jsMetas.length(); j++) { + object = (JSONObject) jsMetas.getJSONObject(j); + MetaData metaData = new MetaData(); + metaData.setContentByRemoteJSON(object); + if (metaData.isWorthSaving()) { + mMetaList.addChildTask(metaData); + if (metaData.getGid() != null) { + mMetaHashMap.put(metaData.getRelatedGid(), metaData); + } + } + } + } + } + + // 如果元数据列表不存在,则创建元数据列表 + if (mMetaList == null) { + mMetaList = new TaskList(); + mMetaList.setName(GTaskStringUtils.MIUI_FOLDER_PREFFIX + GTaskStringUtils.FOLDER_META); + GTaskClient.getInstance().createTaskList(mMetaList); + } + + // 初始化任务列表 + 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); + + // 查找以特定前缀开始且不是元数据列表的任务列表 + if (name.startsWith(GTaskStringUtils.MIUI_FOLDER_PREFFIX) + && !name.equals(GTaskStringUtils.MIUI_FOLDER_PREFFIX + GTaskStringUtils.FOLDER_META)) { + TaskList tasklist = new TaskList(); + tasklist.setContentByRemoteJSON(object); + mGTaskListHashMap.put(gid, tasklist); + mGTaskHashMap.put(gid, tasklist); + + // 加载任务 + JSONArray jsTasks = client.getTaskList(gid); + for (int j = 0; j < jsTasks.length(); j++) { + object = (JSONObject) jsTasks.getJSONObject(j); + gid = object.getString(GTaskStringUtils.GTASK_JSON_ID); + Task task = new Task(); + task.setContentByRemoteJSON(object); + if (task.isWorthSaving()) { + task.setMetaInfo(mMetaHashMap.get(gid)); + tasklist.addChildTask(task); + mGTaskHashMap.put(gid, task); + } + } + } + } + } catch (JSONException e) { + 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; + + mLocalDeleteIdMap.clear(); + + if (mCancelled) { + return; + } + + // 处理本地已删除的笔记 + 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()) { + 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 { + if (c != null) { + c.close(); + c = null; + } + } + + // 先同步文件夹 + syncFolder(); + + // 处理数据库中已存在的笔记 + 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()) { + 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) { + // 本地新增 + syncType = Node.SYNC_ACTION_ADD_REMOTE; + } else { + // 远程删除 + syncType = Node.SYNC_ACTION_DEL_LOCAL; + } + } + doContentSync(syncType, node, c); + } + } else { + Log.w(TAG, "failed to query existing note in database"); + } + + } finally { + if (c != null) { + c.close(); + c = null; + } + } + + // 处理剩余的项 + Iterator> iter = mGTaskHashMap.entrySet().iterator(); + while (iter.hasNext()) { + Map.Entry entry = iter.next(); + node = entry.getValue(); + doContentSync(Node.SYNC_ACTION_ADD_LOCAL, node, null); + } + + // 逐个检查以防 mCancelled 被另一个线程设置 + // 清除本地删除表 + if (!mCancelled) { + if (!DataUtils.batchDeleteNotes(mContentResolver, mLocalDeleteIdMap)) { + throw new ActionFailureException("failed to batch-delete local deleted notes"); + } + } + + // 刷新本地同步 ID + if (!mCancelled) { + GTaskClient.getInstance().commitUpdate(); + refreshLocalSyncId(); + } + } +/*该方法用于同步 GTask 的文件夹。首先处理根文件夹,然后处理通话记录文件夹。接下来,处理数据库中已存在的文件夹。对于每 + * 个已存在的文件夹,根据其在 GTask 中的状态确定同步类型,并进行相应的同步操作。然后,处理远程新增的文件夹。最后,提交更新到 GTask 服务。 + * 如果出现网络请求失败或其他异常,会抛出 NetworkFailureException 异常。 + */ + private void syncFolder() throws NetworkFailureException { + Cursor c = null; + String gid; + Node node; + int syncType; + + if (mCancelled) { + return; + } + + // 处理根文件夹 + 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(); + 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); + // 对于系统文件夹,仅在必要时更新远程名称 + if (!node.getName().equals( + GTaskStringUtils.MIUI_FOLDER_PREFFIX + GTaskStringUtils.FOLDER_DEFAULT)) + doContentSync(Node.SYNC_ACTION_UPDATE_REMOTE, node, c); + } else { + doContentSync(Node.SYNC_ACTION_ADD_REMOTE, node, c); + } + } else { + Log.w(TAG, "failed to query root folder"); + } + } finally { + if (c != null) { + c.close(); + c = null; + } + } + + // 处理通话记录文件夹 + try { + c = mContentResolver.query(Notes.CONTENT_NOTE_URI, SqlNote.PROJECTION_NOTE, "(_id=?)", + new String[] { + String.valueOf(Notes.ID_CALL_RECORD_FOLDER) + }, null); + if (c != null) { + if (c.moveToNext()) { + 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); + // 对于系统文件夹,仅在必要时更新远程名称 + if (!node.getName().equals( + GTaskStringUtils.MIUI_FOLDER_PREFFIX + + GTaskStringUtils.FOLDER_CALL_NOTE)) + doContentSync(Node.SYNC_ACTION_UPDATE_REMOTE, node, c); + } else { + doContentSync(Node.SYNC_ACTION_ADD_REMOTE, node, c); + } + } + } else { + Log.w(TAG, "failed to query call note folder"); + } + } finally { + if (c != null) { + c.close(); + c = null; + } + } + + // 处理本地已存在的文件夹 + 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()) { + 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) { + // 本地新增 + syncType = Node.SYNC_ACTION_ADD_REMOTE; + } else { + // 远程删除 + syncType = Node.SYNC_ACTION_DEL_LOCAL; + } + } + doContentSync(syncType, node, c); + } + } else { + Log.w(TAG, "failed to query existing folder"); + } + } finally { + if (c != null) { + c.close(); + c = null; + } + } + + // 处理远程新增的文件夹 + Iterator> iter = mGTaskListHashMap.entrySet().iterator(); + while (iter.hasNext()) { + Map.Entry entry = iter.next(); + gid = entry.getKey(); + node = entry.getValue(); + if (mGTaskHashMap.containsKey(gid)) { + mGTaskHashMap.remove(gid); + doContentSync(Node.SYNC_ACTION_ADD_LOCAL, node, null); + } + } + + if (!mCancelled) + GTaskClient.getInstance().commitUpdate(); + } private void doContentSync(int syncType, Node node, Cursor c) throws NetworkFailureException { if (mCancelled) { @@ -521,7 +504,13 @@ public class GTaskManager { throw new ActionFailureException("unkown sync action type"); } } - +/*该方法用于添加本地节点(即在本地创建一个新的文件夹或任务)。首先,检查是否取消了同步操作,如果是,则直接返回。 + * 然后,根据节点的类型创建一个 SqlNote 对象。如果节点是一个任务列表(TaskList),根据节点的名称判断是根文件夹还是通话记录文件夹,并创建相应的 SqlNote 对象。 + * 如果节点是一个任务(Task),则创建一个默认的 SqlNote 对象,并根据节点的内容设置其属性。 + * 接下来,如果节点的内容中包含任务或数据的 ID,检查这些 ID 在本地数据库中是否已存在,如果存在,将其移除,以便创建新的 ID。 + * 然后,设置 SqlNote 的 GTask ID,并将其提交到本地数据库。接着,更新 GTask ID 到本地 ID 的映射关系。 + * 最后,更新远程元数据。 + */ private void addLocalNode(Node node) throws NetworkFailureException { if (mCancelled) { return; @@ -529,18 +518,23 @@ public class GTaskManager { 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 { @@ -549,7 +543,7 @@ public class GTaskManager { 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,以便创建新的 ID note.remove(NoteColumns.ID); } } @@ -562,13 +556,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,以便创建新的 ID data.remove(DataColumns.ID); } } } - } } catch (JSONException e) { Log.w(TAG, e.toString()); @@ -584,15 +576,15 @@ 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); } @@ -618,17 +610,30 @@ public class GTaskManager { // update meta info updateRemoteMeta(node.getGid(), sqlNote); } - +/*用于添加远程节点(即在远程服务器上创建一个新的文件夹或任务)。 + * 首先,检查是否取消了同步操作,如果是,则直接返回。 + * 然后,根据 Cursor 创建一个 SqlNote 对象。接下来,根据节点的类型进行不同的处理。 + * 如果是笔记类型的节点,创建一个 Task 对象,并根据 SqlNote 对象的内容设置任务的属性。 + * 然后,获取父节点的 GTask ID,并将该任务添加到相应的任务列表中。 + * 接着,调用 GTask 服务的 createTask 方法创建远程任务,并将其返回的节点赋值给变量 n。 + * 最后,添加元数据并更新本地笔记的属性。 + * 如果是任务列表类型的节点,首先根据文件夹名称查找是否已存在相应的任务列表, + * 如果存在,则更新任务列表的属性; + * 如果不存在,则创建一个新的任务列表,并调用 GTask 服务的 createTaskList 方法创建远程任务列表。 + * 然后,将任务列表返回的节点赋值给变量 n。接下来,更新本地笔记的属性,并更新 gid-id 的映射关系。 + */ private void addRemoteNode(Node node, Cursor c) throws NetworkFailureException { if (mCancelled) { return; } + // 根据 Cursor 创建 SqlNote 对象 SqlNote sqlNote = new SqlNote(mContext, c); Node n; - // update remotely + // 更新远程节点 if (sqlNote.isNoteType()) { + // 如果是笔记类型的节点 Task task = new Task(); task.setContentByLocalJSON(sqlNote.getContent()); @@ -639,15 +644,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; @@ -671,7 +678,7 @@ public class GTaskManager { } } - // no match we can add now + // 如果没有匹配的任务列表,则现在可以添加 if (tasklist == null) { tasklist = new TaskList(); tasklist.setContentByLocalJSON(sqlNote.getContent()); @@ -681,32 +688,41 @@ 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()); } - +/*该方法用于更新远程节点(即在远程服务器上更新一个文件夹或任务的属性)。 + * 首先,检查是否取消了同步操作,如果是,则直接返回。 + * 然后,根据 Cursor 创建一个 SqlNote 对象。 + * 接下来,更新远程节点的内容,并调用 GTask 服务的 addUpdateNode 方法将更新后的节点添加到待更新列表中。 + * 然后,更新元数据。如果是笔记类型的节点,可能需要移动任务到不同的任务列表中。 + * 首先,获取节点的当前父任务列表和新的父任务列表。如果当前父任务列表和新的父任务列表不同,说明需要移动任务。 + * 先将任务从当前父任务列表中移除,然后将任务添加到新的父任务列表中,并调用 GTask 服务的 moveTask 方法执行任务的移动操作。 + * 最后,清除本地修改标志位,将修改后的笔记保存到本地数据库中。 + */ private void updateRemoteNode(Node node, Cursor c) throws NetworkFailureException { if (mCancelled) { return; } + // 根据 Cursor 创建 SqlNote 对象 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(); @@ -725,18 +741,19 @@ 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); @@ -751,7 +768,7 @@ public class GTaskManager { return; } - // get the latest gtask list + // 获取最新的 GTask 列表 mGTaskHashMap.clear(); mGTaskListHashMap.clear(); mMetaHashMap.clear(); @@ -759,6 +776,7 @@ public class GTaskManager { Cursor c = null; 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) @@ -771,6 +789,7 @@ public class GTaskManager { mGTaskHashMap.remove(gid); ContentValues values = new ContentValues(); values.put(NoteColumns.SYNC_ID, node.getLastModified()); + // 更新本地笔记的同步 ID mContentResolver.update(ContentUris.withAppendedId(Notes.CONTENT_NOTE_URI, c.getLong(SqlNote.ID_COLUMN)), values, null, null); } else { @@ -791,10 +810,12 @@ public class GTaskManager { } public String getSyncAccount() { + // 获取同步账户 return GTaskClient.getInstance().getSyncAccount().name; } public void cancelSync() { + // 取消同步操作 mCancelled = true; } } diff --git a/src/src/net/micode/notes/gtask/remote/GTaskSyncService.java b/src/src/net/micode/notes/gtask/remote/GTaskSyncService.java index cca36f7..806c50d 100644 --- a/src/src/net/micode/notes/gtask/remote/GTaskSyncService.java +++ b/src/src/net/micode/notes/gtask/remote/GTaskSyncService.java @@ -23,106 +23,103 @@ import android.content.Intent; import android.os.Bundle; import android.os.IBinder; -public class GTaskSyncService extends Service { - 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"; - - public final static String GTASK_SERVICE_BROADCAST_IS_SYNCING = "isSyncing"; - - public final static String GTASK_SERVICE_BROADCAST_PROGRESS_MSG = "progressMsg"; - - private static GTaskASyncTask mSyncTask = null; - - private static String mSyncProgress = ""; - - private void startSync() { - if (mSyncTask == null) { - mSyncTask = new GTaskASyncTask(this, new GTaskASyncTask.OnCompleteListener() { - public void onComplete() { - mSyncTask = null; - sendBroadcast(""); - stopSelf(); - } - }); - sendBroadcast(""); - mSyncTask.execute(); - } +private static GTaskASyncTask mSyncTask = null; +// GTaskASyncTask对象,用于执行GTask的异步任务 + +private static String mSyncProgress = ""; +// 同步进度信息 + +private void startSync() { + // 开始同步操作 + if (mSyncTask == null) { + mSyncTask = new GTaskASyncTask(this, new GTaskASyncTask.OnCompleteListener() { + public void onComplete() { + // 同步完成时的回调方法 + mSyncTask = null; + sendBroadcast(""); + stopSelf(); + } + }); + sendBroadcast(""); + mSyncTask.execute(); } +} - private void cancelSync() { - if (mSyncTask != null) { - mSyncTask.cancelSync(); - } +private void cancelSync() { + // 取消同步操作 + if (mSyncTask != null) { + mSyncTask.cancelSync(); } +} - @Override - public void onCreate() { - mSyncTask = null; - } +@Override +public void onCreate() { + mSyncTask = null; +} - @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(); - break; - case ACTION_CANCEL_SYNC: - cancelSync(); - break; - default: - break; - } - 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(); + break; + case ACTION_CANCEL_SYNC: + // 取消同步操作 + cancelSync(); + break; + default: + break; } - return super.onStartCommand(intent, flags, startId); + return START_STICKY; } + return super.onStartCommand(intent, flags, startId); +} - @Override - public void onLowMemory() { - if (mSyncTask != null) { - mSyncTask.cancelSync(); - } +@Override +public void onLowMemory() { + if (mSyncTask != null) { + mSyncTask.cancelSync(); } +} - public IBinder onBind(Intent intent) { - return null; - } +public IBinder onBind(Intent intent) { + return null; +} - 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); - } +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); +} - 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); - } +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); +} - 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); - } +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); +} - public static boolean isSyncing() { - return mSyncTask != null; - } +public static boolean isSyncing() { + // 检查是否正在进行同步操作 + return mSyncTask != null; +} - public static String getProgressString() { - return mSyncProgress; - } +public static String getProgressString() { + // 获取同步进度信息 + return mSyncProgress; +} } diff --git a/src/src/net/micode/notes/model/Note.java b/src/src/net/micode/notes/model/Note.java index 6706cf6..6b33bc4 100644 --- a/src/src/net/micode/notes/model/Note.java +++ b/src/src/net/micode/notes/model/Note.java @@ -39,10 +39,10 @@ public class Note { private NoteData mNoteData; private static final String TAG = "Note"; /** - * Create a new note id for adding a new note to databases + * 为添加新的笔记到数据库创建一个新的笔记ID */ public static synchronized long getNewNoteId(Context context, long folderId) { - // Create a new note in the database + // 在数据库中创建一个新的笔记 ContentValues values = new ContentValues(); long createdTime = System.currentTimeMillis(); values.put(NoteColumns.CREATED_DATE, createdTime); @@ -71,35 +71,45 @@ public class Note { } public void setNoteValue(String key, String value) { + // 设置笔记的键值对 mNoteDiffValues.put(key, value); mNoteDiffValues.put(NoteColumns.LOCAL_MODIFIED, 1); mNoteDiffValues.put(NoteColumns.MODIFIED_DATE, System.currentTimeMillis()); } public void setTextData(String key, String value) { + // 设置笔记数据的文本值 mNoteData.setTextData(key, value); } public void setTextDataId(long id) { + // 设置文本数据的ID mNoteData.setTextDataId(id); } public long getTextDataId() { + // 获取文本数据的ID return mNoteData.mTextDataId; } public void setCallDataId(long id) { + // 设置通话数据的ID mNoteData.setCallDataId(id); } public void setCallData(String key, String value) { + // 设置通话数据的键值对 mNoteData.setCallData(key, value); } - + // 检查笔记是否有本地修改 public boolean isLocalModified() { return mNoteDiffValues.size() > 0 || mNoteData.isLocalModified(); } - + /*syncNote 方法用于同步笔记。首先,检查传入的笔记ID是否合法,如果小于等于0,则抛出非法参数异常。然后,检查笔记是否有本地修改,如果没有,则直接返回true。 + * 接下来,根据传入的笔记ID和 mNoteDiffValues,通过内容解析器的 update 方法更新数据库中的笔记。如果更新操作返回0,则记录错误日志。然后,清空 mNoteDiffValues。 + * 接着,检查 mNoteData 是否有本地修改,并调用其 pushIntoContentResolver 方法将数据推送到内容解析器中。如果推送操作返回null,则返回false。 + * 最后,如果所有操作都成功完成,则返回true。 + */ public boolean syncNote(Context context, long noteId) { if (noteId <= 0) { throw new IllegalArgumentException("Wrong note id:" + noteId); @@ -109,16 +119,14 @@ public class Note { return true; } - /** - * In theory, once data changed, the note should be updated on {@link NoteColumns#LOCAL_MODIFIED} and - * {@link NoteColumns#MODIFIED_DATE}. For data safety, though update note fails, we also update the - * note data info + /*理论上,一旦数据发生变化,笔记应该在 {@link NoteColumns#LOCAL_MODIFIED} 和 {@link NoteColumns#MODIFIED_DATE} 上进行更新。 + * 为了数据的安全性,即使更新笔记失败,我们也会更新笔记数据的信息。 */ if (context.getContentResolver().update( ContentUris.withAppendedId(Notes.CONTENT_NOTE_URI, noteId), mNoteDiffValues, null, null) == 0) { Log.e(TAG, "Update note error, should not happen"); - // Do not return, fall through + // 不返回,继续执行 } mNoteDiffValues.clear(); @@ -131,14 +139,10 @@ public class Note { } private class NoteData { - private long mTextDataId; - - private ContentValues mTextDataValues; - - private long mCallDataId; - - private ContentValues mCallDataValues; - + private long mTextDataId; // 文本数据的ID + private ContentValues mTextDataValues; // 存储文本数据的键值对 + private long mCallDataId; // 通话数据的ID + private ContentValues mCallDataValues; // 存储通话数据的键值对 private static final String TAG = "NoteData"; public NoteData() { @@ -147,7 +151,7 @@ public class Note { mTextDataId = 0; mCallDataId = 0; } - + // 检查笔记数据是否有本地修改 boolean isLocalModified() { return mTextDataValues.size() > 0 || mCallDataValues.size() > 0; } @@ -158,20 +162,20 @@ public class Note { } mTextDataId = id; } - + void setCallDataId(long id) { if (id <= 0) { throw new IllegalArgumentException("Call data id should larger than 0"); } mCallDataId = id; } - + // 设置通话数据的键值对 void setCallData(String key, String value) { mCallDataValues.put(key, value); mNoteDiffValues.put(NoteColumns.LOCAL_MODIFIED, 1); mNoteDiffValues.put(NoteColumns.MODIFIED_DATE, System.currentTimeMillis()); } - + // 设置文本数据的键值对 void setTextData(String key, String value) { mTextDataValues.put(key, value); mNoteDiffValues.put(NoteColumns.LOCAL_MODIFIED, 1); @@ -180,29 +184,33 @@ public class Note { Uri pushIntoContentResolver(Context context, long noteId) { /** - * Check for safety + * 检查安全性 */ if (noteId <= 0) { - throw new IllegalArgumentException("Wrong note id:" + noteId); + throw new IllegalArgumentException("错误的笔记ID:" + noteId); } ArrayList operationList = new ArrayList(); ContentProviderOperation.Builder builder = null; - if(mTextDataValues.size() > 0) { + if (mTextDataValues.size() > 0) { + // 设置文本数据的笔记ID mTextDataValues.put(DataColumns.NOTE_ID, noteId); if (mTextDataId == 0) { + // 如果文本数据的ID为0,表示是新的文本数据 mTextDataValues.put(DataColumns.MIME_TYPE, TextNote.CONTENT_ITEM_TYPE); Uri uri = context.getContentResolver().insert(Notes.CONTENT_DATA_URI, mTextDataValues); try { + // 解析插入新文本数据后返回的URI,获取新的文本数据ID setTextDataId(Long.valueOf(uri.getPathSegments().get(1))); } catch (NumberFormatException e) { - Log.e(TAG, "Insert new text data fail with noteId" + noteId); + Log.e(TAG, "插入新的文本数据失败,笔记ID:" + noteId); mTextDataValues.clear(); return null; } } else { + // 如果文本数据的ID不为0,表示是已存在的文本数据,进行更新操作 builder = ContentProviderOperation.newUpdate(ContentUris.withAppendedId( Notes.CONTENT_DATA_URI, mTextDataId)); builder.withValues(mTextDataValues); @@ -211,20 +219,24 @@ public class Note { mTextDataValues.clear(); } - if(mCallDataValues.size() > 0) { + if (mCallDataValues.size() > 0) { + // 设置通话数据的笔记ID mCallDataValues.put(DataColumns.NOTE_ID, noteId); if (mCallDataId == 0) { + // 如果通话数据的ID为0,表示是新的通话数据 mCallDataValues.put(DataColumns.MIME_TYPE, CallNote.CONTENT_ITEM_TYPE); Uri uri = context.getContentResolver().insert(Notes.CONTENT_DATA_URI, mCallDataValues); try { + // 解析插入新通话数据后返回的URI,获取新的通话数据ID setCallDataId(Long.valueOf(uri.getPathSegments().get(1))); } catch (NumberFormatException e) { - Log.e(TAG, "Insert new call data fail with noteId" + noteId); + Log.e(TAG, "插入新的通话数据失败,笔记ID:" + noteId); mCallDataValues.clear(); return null; } } else { + // 如果通话数据的ID不为0,表示是已存在的通话数据,进行更新操作 builder = ContentProviderOperation.newUpdate(ContentUris.withAppendedId( Notes.CONTENT_DATA_URI, mCallDataId)); builder.withValues(mCallDataValues); @@ -235,8 +247,10 @@ public class Note { if (operationList.size() > 0) { try { + // 将操作列表应用到内容提供器,进行批量操作 ContentProviderResult[] results = context.getContentResolver().applyBatch( Notes.AUTHORITY, operationList); + // 返回结果 return (results == null || results.length == 0 || results[0] == null) ? null : ContentUris.withAppendedId(Notes.CONTENT_NOTE_URI, noteId); } catch (RemoteException e) { diff --git a/src/src/net/micode/notes/model/WorkingNote.java b/src/src/net/micode/notes/model/WorkingNote.java index be081e4..26f4942 100644 --- a/src/src/net/micode/notes/model/WorkingNote.java +++ b/src/src/net/micode/notes/model/WorkingNote.java @@ -34,96 +34,124 @@ import net.micode.notes.tool.ResourceParser.NoteBgResources; public class WorkingNote { // Note for the working note - private Note mNote; - // Note Id - private long mNoteId; - // Note content - private String mContent; - // Note mode - private int mMode; - - private long mAlertDate; - - private long mModifiedDate; - - private int mBgColorId; - - private int mWidgetId; - - private int mWidgetType; - - private long mFolderId; - - private Context mContext; - - private static final String TAG = "WorkingNote"; - - private boolean mIsDeleted; - - private NoteSettingChangedListener mNoteSettingStatusListener; - - public static final String[] DATA_PROJECTION = new String[] { - DataColumns.ID, - DataColumns.CONTENT, - DataColumns.MIME_TYPE, - DataColumns.DATA1, - DataColumns.DATA2, - DataColumns.DATA3, - DataColumns.DATA4, - }; - - public static final String[] NOTE_PROJECTION = new String[] { - NoteColumns.PARENT_ID, - NoteColumns.ALERTED_DATE, - NoteColumns.BG_COLOR_ID, - NoteColumns.WIDGET_ID, - NoteColumns.WIDGET_TYPE, - NoteColumns.MODIFIED_DATE - }; - - private static final int DATA_ID_COLUMN = 0; - - private static final int DATA_CONTENT_COLUMN = 1; - - private static final int DATA_MIME_TYPE_COLUMN = 2; - - private static final int DATA_MODE_COLUMN = 3; - - private static final int NOTE_PARENT_ID_COLUMN = 0; - - private static final int NOTE_ALERTED_DATE_COLUMN = 1; - - private static final int NOTE_BG_COLOR_ID_COLUMN = 2; - - private static final int NOTE_WIDGET_ID_COLUMN = 3; - - private static final int NOTE_WIDGET_TYPE_COLUMN = 4; - - private static final int NOTE_MODIFIED_DATE_COLUMN = 5; - - // New note construct - private WorkingNote(Context context, long folderId) { - mContext = context; - mAlertDate = 0; - mModifiedDate = System.currentTimeMillis(); - mFolderId = folderId; - mNote = new Note(); - mNoteId = 0; - mIsDeleted = false; - mMode = 0; - mWidgetType = Notes.TYPE_WIDGET_INVALIDE; - } - - // Existing note construct - private WorkingNote(Context context, long noteId, long folderId) { - mContext = context; - mNoteId = noteId; - mFolderId = folderId; - mIsDeleted = false; - mNote = new Note(); - loadNote(); - } - + private Note mNote; + // 笔记对象 + private long mNoteId; + // 笔记ID + private String mContent; + // 笔记内容 + private int mMode; + // 笔记模式 + + private long mAlertDate; + // 提醒日期 + + private long mModifiedDate; + // 修改日期 + + private int mBgColorId; + // 背景颜色ID + + private int mWidgetId; + // 小部件ID + + private int mWidgetType; + // 小部件类型 + + private long mFolderId; + // 文件夹ID + + private Context mContext; + // 上下文 + + private static final String TAG = "WorkingNote"; + // 日志标签 + + private boolean mIsDeleted; + // 是否已删除 + + private NoteSettingChangedListener mNoteSettingStatusListener; + // 笔记设置状态监听器 + + public static final String[] DATA_PROJECTION = new String[] { + DataColumns.ID, + DataColumns.CONTENT, + DataColumns.MIME_TYPE, + DataColumns.DATA1, + DataColumns.DATA2, + DataColumns.DATA3, + DataColumns.DATA4, + }; + // 数据查询投影数组 + + + public static final String[] NOTE_PROJECTION = new String[] { + NoteColumns.PARENT_ID, + NoteColumns.ALERTED_DATE, + NoteColumns.BG_COLOR_ID, + NoteColumns.WIDGET_ID, + NoteColumns.WIDGET_TYPE, + NoteColumns.MODIFIED_DATE + }; + // 笔记查询投影数组,用于指定查询笔记数据时返回的列。包含列名 NoteColumns.PARENT_ID、NoteColumns.ALERTED_DATE、NoteColumns.BG_COLOR_ID、NoteColumns.WIDGET_ID、NoteColumns.WIDGET_TYPE、NoteColumns.MODIFIED_DATE。 + + private static final int DATA_ID_COLUMN = 0; + // 数据查询结果中ID列的索引 + + private static final int DATA_CONTENT_COLUMN = 1; + // 数据查询结果中内容列的索引 + + private static final int DATA_MIME_TYPE_COLUMN = 2; + // 数据查询结果中MIME类型列的索引 + + private static final int DATA_MODE_COLUMN = 3; + // 数据查询结果中模式列的索引 + + private static final int NOTE_PARENT_ID_COLUMN = 0; + // 笔记查询结果中父ID列的索引 + + private static final int NOTE_ALERTED_DATE_COLUMN = 1; + // 笔记查询结果中提醒日期列的索引 + + private static final int NOTE_BG_COLOR_ID_COLUMN = 2; + // 笔记查询结果中背景颜色ID列的索引 + + private static final int NOTE_WIDGET_ID_COLUMN = 3; + // 笔记查询结果中小部件ID列的索引 + + private static final int NOTE_WIDGET_TYPE_COLUMN = 4; + // 笔记查询结果中小部件类型列的索引 + + private static final int NOTE_MODIFIED_DATE_COLUMN = 5; + // 笔记查询结果中修改日期列的索引 + + // 新建笔记构造函数 + private WorkingNote(Context context, long folderId) { + mContext = context; + mAlertDate = 0; + mModifiedDate = System.currentTimeMillis(); + mFolderId = folderId; + mNote = new Note(); + mNoteId = 0; + mIsDeleted = false; + mMode = 0; + mWidgetType = Notes.TYPE_WIDGET_INVALIDE; + } + + // 已存在的笔记构造函数 + private WorkingNote(Context context, long noteId, long folderId) { + mContext = context; + mNoteId = noteId; + mFolderId = folderId; + mIsDeleted = false; + mNote = new Note(); + loadNote(); + } + /*loadNote() 方法用于加载已存在的笔记的数据。 + * 它首先通过内容解析器查询具有指定ID的笔记数据,并使用 NOTE_PROJECTION 数组指定要返回的列。 + * 然后,它从查询结果中提取相关数据,并将其设置到对应的成员变量中,包括文件夹ID、背景颜色ID、小部件ID、小部件类型、提醒日期和修改日期。 + * 如果查询结果为空,它会记录错误日志并抛出异常。最后,它调用 loadNoteData() 方法加载笔记的具体数据。 + */ private void loadNote() { Cursor cursor = mContext.getContentResolver().query( ContentUris.withAppendedId(Notes.CONTENT_NOTE_URI, mNoteId), NOTE_PROJECTION, null, @@ -145,7 +173,13 @@ public class WorkingNote { } loadNoteData(); } - + /*loadNoteData() 方法用于加载笔记的具体数据。 + * 它通过内容解析器查询具有指定笔记ID的数据,并使用 DATA_PROJECTION 数组指定要返回的列。 + * 然后,它从查询结果中遍历每一行数据,并根据数据的类型将其设置到对应的成员变量中。 + * 如果数据类型是普通笔记类型,它会设置笔记的内容、模式和文本数据ID; + * 如果数据类型是电话笔记类型,它会设置电话数据ID。 + * 如果查询结果为空,它会记录错误日志并抛出异常。 + */ private void loadNoteData() { Cursor cursor = mContext.getContentResolver().query(Notes.CONTENT_DATA_URI, DATA_PROJECTION, DataColumns.NOTE_ID + "=?", new String[] { @@ -173,7 +207,10 @@ public class WorkingNote { throw new IllegalArgumentException("Unable to find note's data with id " + mNoteId); } } - + /*createEmptyNote() 方法是一个静态方法,用于创建一个空的笔记对象。 + * 它接受上下文对象、文件夹ID、小部件ID、小部件类型和默认背景颜色ID作为参数。 + * 它首先创建一个新的 WorkingNote 对象,并设置背景颜色ID、小部件ID和小部件类型,然后返回该对象。 + */ public static WorkingNote createEmptyNote(Context context, long folderId, int widgetId, int widgetType, int defaultBgColorId) { WorkingNote note = new WorkingNote(context, folderId); @@ -182,11 +219,17 @@ public class WorkingNote { note.setWidgetType(widgetType); return note; } - + /*load() 方法是一个静态方法,用于加载已存在的笔记对象。 + * 它接受上下文对象和笔记ID作为参数,并创建一个已存在的 WorkingNote 对象,并返回该对象。 + */ public static WorkingNote load(Context context, long id) { return new WorkingNote(context, id, 0); } + /** + * 保存笔记 + * @return 保存成功返回true,否则返回false + */ public synchronized boolean saveNote() { if (isWorthSaving()) { if (!existInDatabase()) { @@ -199,7 +242,7 @@ public class WorkingNote { mNote.syncNote(mContext, mNoteId); /** - * Update widget content if there exist any widget of this note + * 如果存在与该笔记相关的小部件,则更新小部件内容 */ if (mWidgetId != AppWidgetManager.INVALID_APPWIDGET_ID && mWidgetType != Notes.TYPE_WIDGET_INVALIDE @@ -212,10 +255,18 @@ public class WorkingNote { } } + /** + * 判断笔记是否存在于数据库中 + * @return 存在返回true,否则返回false + */ public boolean existInDatabase() { return mNoteId > 0; } + /** + * 判断笔记是否值得保存 + * @return 值得保存返回true,否则返回false + */ private boolean isWorthSaving() { if (mIsDeleted || (!existInDatabase() && TextUtils.isEmpty(mContent)) || (existInDatabase() && !mNote.isLocalModified())) { @@ -225,32 +276,40 @@ public class WorkingNote { } } + public void setOnSettingStatusChangedListener(NoteSettingChangedListener l) { + // 设置笔记设置状态变化监听器 mNoteSettingStatusListener = l; } public void setAlertDate(long date, boolean set) { if (date != mAlertDate) { + // 更新提醒日期 mAlertDate = date; mNote.setNoteValue(NoteColumns.ALERTED_DATE, String.valueOf(mAlertDate)); } if (mNoteSettingStatusListener != null) { + // 通知监听器提醒时间发生变化 mNoteSettingStatusListener.onClockAlertChanged(date, set); } } public void markDeleted(boolean mark) { + // 标记笔记是否已删除 mIsDeleted = mark; if (mWidgetId != AppWidgetManager.INVALID_APPWIDGET_ID && mWidgetType != Notes.TYPE_WIDGET_INVALIDE && mNoteSettingStatusListener != null) { + // 如果存在与该笔记相关的小部件,并且小部件类型有效且监听器不为空,则通知监听器小部件发生变化 mNoteSettingStatusListener.onWidgetChanged(); } } public void setBgColorId(int id) { if (id != mBgColorId) { + // 更新背景颜色ID mBgColorId = id; if (mNoteSettingStatusListener != null) { + // 通知监听器背景颜色发生变化 mNoteSettingStatusListener.onBackgroundColorChanged(); } mNote.setNoteValue(NoteColumns.BG_COLOR_ID, String.valueOf(id)); @@ -260,8 +319,10 @@ public class WorkingNote { public void setCheckListMode(int mode) { if (mMode != mode) { if (mNoteSettingStatusListener != null) { + // 通知监听器清单模式发生变化 mNoteSettingStatusListener.onCheckListModeChanged(mMode, mode); } + // 更新清单模式 mMode = mode; mNote.setTextData(TextNote.MODE, String.valueOf(mMode)); } @@ -269,6 +330,7 @@ public class WorkingNote { public void setWidgetType(int type) { if (type != mWidgetType) { + // 更新小部件类型 mWidgetType = type; mNote.setNoteValue(NoteColumns.WIDGET_TYPE, String.valueOf(mWidgetType)); } @@ -276,6 +338,7 @@ public class WorkingNote { public void setWidgetId(int id) { if (id != mWidgetId) { + // 更新小部件ID mWidgetId = id; mNote.setNoteValue(NoteColumns.WIDGET_ID, String.valueOf(mWidgetId)); } @@ -283,85 +346,101 @@ public class WorkingNote { public void setWorkingText(String text) { if (!TextUtils.equals(mContent, text)) { + // 更新工作文本 mContent = text; mNote.setTextData(DataColumns.CONTENT, mContent); } } public void convertToCallNote(String phoneNumber, long callDate) { + // 将笔记转换为电话笔记 mNote.setCallData(CallNote.CALL_DATE, String.valueOf(callDate)); mNote.setCallData(CallNote.PHONE_NUMBER, phoneNumber); mNote.setNoteValue(NoteColumns.PARENT_ID, String.valueOf(Notes.ID_CALL_RECORD_FOLDER)); } public boolean hasClockAlert() { + // 判断笔记是否设置了闹钟提醒 return (mAlertDate > 0 ? true : false); } public String getContent() { + // 获取笔记内容 return mContent; } public long getAlertDate() { + // 获取提醒日期 return mAlertDate; } public long getModifiedDate() { + // 获取修改日期 return mModifiedDate; } public int getBgColorResId() { + // 获取背景颜色资源ID return NoteBgResources.getNoteBgResource(mBgColorId); } public int getBgColorId() { + // 获取背景颜色ID return mBgColorId; } public int getTitleBgResId() { + // 获取标题背景资源ID return NoteBgResources.getNoteTitleBgResource(mBgColorId); } public int getCheckListMode() { + // 获取清单模式 return mMode; } public long getNoteId() { + // 获取笔记ID return mNoteId; } public long getFolderId() { + // 获取文件夹ID return mFolderId; } public int getWidgetId() { + // 获取小部件ID return mWidgetId; } public int getWidgetType() { + // 获取小部件类型 return mWidgetType; } public interface NoteSettingChangedListener { /** - * Called when the background color of current note has just changed + * 当当前笔记的背景颜色刚刚改变时调用 */ void onBackgroundColorChanged(); /** - * Called when user set clock + * 当用户设置闹钟提醒时调用 + * @param date 新的提醒日期 + * @param set 是否设置了提醒 */ void onClockAlertChanged(long date, boolean set); /** - * Call when user create note from widget + * 当用户从小部件创建笔记时调用 */ void onWidgetChanged(); /** - * Call when switch between check list mode and normal mode - * @param oldMode is previous mode before change - * @param newMode is new mode + * 当切换清单模式和普通模式时调用 + * @param oldMode 切换前的模式 + * @param newMode 新的模式 */ void onCheckListModeChanged(int oldMode, int newMode); }