Merge remote-tracking branch 'origin/master'

master
蔡文涛 3 years ago
commit e60a315b5b

@ -27,336 +27,348 @@ import net.micode.notes.data.Notes.DataConstants;
import net.micode.notes.data.Notes.NoteColumns;
/**
Notes
*/
public class NotesDatabaseHelper extends SQLiteOpenHelper {
private static final String DB_NAME = "note.db";
private static final int DB_VERSION = 4;
public interface TABLE {
public static final String NOTE = "note";
public static final String DATA = "data";
}
private static final String TAG = "NotesDatabaseHelper";
private static NotesDatabaseHelper mInstance;
private static final String CREATE_NOTE_TABLE_SQL =
"CREATE TABLE " + TABLE.NOTE + "(" +
NoteColumns.ID + " INTEGER PRIMARY KEY," +
NoteColumns.PARENT_ID + " INTEGER NOT NULL DEFAULT 0," +
NoteColumns.ALERTED_DATE + " INTEGER NOT NULL DEFAULT 0," +
NoteColumns.BG_COLOR_ID + " INTEGER NOT NULL DEFAULT 0," +
NoteColumns.CREATED_DATE + " INTEGER NOT NULL DEFAULT (strftime('%s','now') * 1000)," +
NoteColumns.HAS_ATTACHMENT + " INTEGER NOT NULL DEFAULT 0," +
NoteColumns.MODIFIED_DATE + " INTEGER NOT NULL DEFAULT (strftime('%s','now') * 1000)," +
NoteColumns.NOTES_COUNT + " INTEGER NOT NULL DEFAULT 0," +
NoteColumns.SNIPPET + " TEXT NOT NULL DEFAULT ''," +
NoteColumns.TYPE + " INTEGER NOT NULL DEFAULT 0," +
NoteColumns.WIDGET_ID + " INTEGER NOT NULL DEFAULT 0," +
NoteColumns.WIDGET_TYPE + " INTEGER NOT NULL DEFAULT -1," +
NoteColumns.SYNC_ID + " INTEGER NOT NULL DEFAULT 0," +
NoteColumns.LOCAL_MODIFIED + " INTEGER NOT NULL DEFAULT 0," +
NoteColumns.ORIGIN_PARENT_ID + " INTEGER NOT NULL DEFAULT 0," +
NoteColumns.GTASK_ID + " TEXT NOT NULL DEFAULT ''," +
NoteColumns.VERSION + " INTEGER NOT NULL DEFAULT 0" +
")";
private static final String CREATE_DATA_TABLE_SQL =
"CREATE TABLE " + TABLE.DATA + "(" +
DataColumns.ID + " INTEGER PRIMARY KEY," +
DataColumns.MIME_TYPE + " TEXT NOT NULL," +
DataColumns.NOTE_ID + " INTEGER NOT NULL DEFAULT 0," +
NoteColumns.CREATED_DATE + " INTEGER NOT NULL DEFAULT (strftime('%s','now') * 1000)," +
NoteColumns.MODIFIED_DATE + " INTEGER NOT NULL DEFAULT (strftime('%s','now') * 1000)," +
DataColumns.CONTENT + " TEXT NOT NULL DEFAULT ''," +
DataColumns.DATA1 + " INTEGER," +
DataColumns.DATA2 + " INTEGER," +
DataColumns.DATA3 + " TEXT NOT NULL DEFAULT ''," +
DataColumns.DATA4 + " TEXT NOT NULL DEFAULT ''," +
DataColumns.DATA5 + " TEXT NOT NULL DEFAULT ''" +
")";
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 "+
" AFTER UPDATE OF " + NoteColumns.PARENT_ID + " ON " + TABLE.NOTE +
" BEGIN " +
" UPDATE " + TABLE.NOTE +
" SET " + NoteColumns.NOTES_COUNT + "=" + NoteColumns.NOTES_COUNT + " + 1" +
" WHERE " + NoteColumns.ID + "=new." + NoteColumns.PARENT_ID + ";" +
" 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 " +
" AFTER UPDATE OF " + NoteColumns.PARENT_ID + " ON " + TABLE.NOTE +
" BEGIN " +
" UPDATE " + TABLE.NOTE +
" SET " + NoteColumns.NOTES_COUNT + "=" + NoteColumns.NOTES_COUNT + "-1" +
" WHERE " + NoteColumns.ID + "=old." + NoteColumns.PARENT_ID +
" AND " + NoteColumns.NOTES_COUNT + ">0" + ";" +
" 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 " +
" AFTER INSERT ON " + TABLE.NOTE +
" BEGIN " +
" UPDATE " + TABLE.NOTE +
" SET " + NoteColumns.NOTES_COUNT + "=" + NoteColumns.NOTES_COUNT + " + 1" +
" WHERE " + NoteColumns.ID + "=new." + NoteColumns.PARENT_ID + ";" +
" 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 " +
" AFTER DELETE ON " + TABLE.NOTE +
" BEGIN " +
" UPDATE " + TABLE.NOTE +
" SET " + NoteColumns.NOTES_COUNT + "=" + NoteColumns.NOTES_COUNT + "-1" +
" WHERE " + NoteColumns.ID + "=old." + NoteColumns.PARENT_ID +
" AND " + NoteColumns.NOTES_COUNT + ">0;" +
" END";
/**
* Update note's content when insert data with type {@link DataConstants#NOTE}
*/
private static final String DATA_UPDATE_NOTE_CONTENT_ON_INSERT_TRIGGER =
"CREATE TRIGGER update_note_content_on_insert " +
" AFTER INSERT ON " + TABLE.DATA +
" WHEN new." + DataColumns.MIME_TYPE + "='" + DataConstants.NOTE + "'" +
" BEGIN" +
" UPDATE " + TABLE.NOTE +
" SET " + NoteColumns.SNIPPET + "=new." + DataColumns.CONTENT +
" WHERE " + NoteColumns.ID + "=new." + DataColumns.NOTE_ID + ";" +
" END";
/**
* Update note's content when data with {@link DataConstants#NOTE} type has changed
*/
private static final String DATA_UPDATE_NOTE_CONTENT_ON_UPDATE_TRIGGER =
"CREATE TRIGGER update_note_content_on_update " +
" AFTER UPDATE ON " + TABLE.DATA +
" WHEN old." + DataColumns.MIME_TYPE + "='" + DataConstants.NOTE + "'" +
" BEGIN" +
" UPDATE " + TABLE.NOTE +
" SET " + NoteColumns.SNIPPET + "=new." + DataColumns.CONTENT +
" WHERE " + NoteColumns.ID + "=new." + DataColumns.NOTE_ID + ";" +
" END";
/**
* Update note's content when data with {@link DataConstants#NOTE} type has deleted
*/
private static final String DATA_UPDATE_NOTE_CONTENT_ON_DELETE_TRIGGER =
"CREATE TRIGGER update_note_content_on_delete " +
" AFTER delete ON " + TABLE.DATA +
" WHEN old." + DataColumns.MIME_TYPE + "='" + DataConstants.NOTE + "'" +
" BEGIN" +
" UPDATE " + TABLE.NOTE +
" SET " + NoteColumns.SNIPPET + "=''" +
" WHERE " + NoteColumns.ID + "=old." + DataColumns.NOTE_ID + ";" +
" 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 " +
" AFTER DELETE ON " + TABLE.NOTE +
" BEGIN" +
" DELETE FROM " + TABLE.DATA +
" WHERE " + DataColumns.NOTE_ID + "=old." + NoteColumns.ID + ";" +
" 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 " +
" AFTER DELETE ON " + TABLE.NOTE +
" BEGIN" +
" DELETE FROM " + TABLE.NOTE +
" WHERE " + NoteColumns.PARENT_ID + "=old." + NoteColumns.ID + ";" +
" 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 " +
" AFTER UPDATE ON " + TABLE.NOTE +
" WHEN new." + NoteColumns.PARENT_ID + "=" + Notes.ID_TRASH_FOLER +
" BEGIN" +
" UPDATE " + TABLE.NOTE +
" SET " + NoteColumns.PARENT_ID + "=" + Notes.ID_TRASH_FOLER +
" WHERE " + NoteColumns.PARENT_ID + "=old." + NoteColumns.ID + ";" +
" END";
public NotesDatabaseHelper(Context context) {
super(context, DB_NAME, null, DB_VERSION);
}
public void createNoteTable(SQLiteDatabase db) {
db.execSQL(CREATE_NOTE_TABLE_SQL);
reCreateNoteTableTriggers(db);
createSystemFolder(db);
Log.d(TAG, "note table has been created");
}
private void reCreateNoteTableTriggers(SQLiteDatabase db) {
db.execSQL("DROP TRIGGER IF EXISTS increase_folder_count_on_update");
db.execSQL("DROP TRIGGER IF EXISTS decrease_folder_count_on_update");
db.execSQL("DROP TRIGGER IF EXISTS decrease_folder_count_on_delete");
db.execSQL("DROP TRIGGER IF EXISTS delete_data_on_delete");
db.execSQL("DROP TRIGGER IF EXISTS increase_folder_count_on_insert");
db.execSQL("DROP TRIGGER IF EXISTS folder_delete_notes_on_delete");
db.execSQL("DROP TRIGGER IF EXISTS folder_move_notes_on_trash");
db.execSQL(NOTE_INCREASE_FOLDER_COUNT_ON_UPDATE_TRIGGER);
db.execSQL(NOTE_DECREASE_FOLDER_COUNT_ON_UPDATE_TRIGGER);
db.execSQL(NOTE_DECREASE_FOLDER_COUNT_ON_DELETE_TRIGGER);
db.execSQL(NOTE_DELETE_DATA_ON_DELETE_TRIGGER);
db.execSQL(NOTE_INCREASE_FOLDER_COUNT_ON_INSERT_TRIGGER);
db.execSQL(FOLDER_DELETE_NOTES_ON_DELETE_TRIGGER);
db.execSQL(FOLDER_MOVE_NOTES_ON_TRASH_TRIGGER);
}
private void createSystemFolder(SQLiteDatabase db) {
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);
values.put(NoteColumns.TYPE, Notes.TYPE_SYSTEM);
db.insert(TABLE.NOTE, null, values);
/**
* temporary folder which is used for moving note
*/
values.clear();
values.put(NoteColumns.ID, Notes.ID_TEMPARAY_FOLDER);
values.put(NoteColumns.TYPE, Notes.TYPE_SYSTEM);
db.insert(TABLE.NOTE, null, values);
/**
* create trash folder
*/
values.clear();
values.put(NoteColumns.ID, Notes.ID_TRASH_FOLER);
values.put(NoteColumns.TYPE, Notes.TYPE_SYSTEM);
db.insert(TABLE.NOTE, null, values);
}
public void createDataTable(SQLiteDatabase db) {
db.execSQL(CREATE_DATA_TABLE_SQL);
reCreateDataTableTriggers(db);
db.execSQL(CREATE_DATA_NOTE_ID_INDEX_SQL);
Log.d(TAG, "data table has been created");
}
private void reCreateDataTableTriggers(SQLiteDatabase db) {
db.execSQL("DROP TRIGGER IF EXISTS update_note_content_on_insert");
db.execSQL("DROP TRIGGER IF EXISTS update_note_content_on_update");
db.execSQL("DROP TRIGGER IF EXISTS update_note_content_on_delete");
db.execSQL(DATA_UPDATE_NOTE_CONTENT_ON_INSERT_TRIGGER);
db.execSQL(DATA_UPDATE_NOTE_CONTENT_ON_UPDATE_TRIGGER);
db.execSQL(DATA_UPDATE_NOTE_CONTENT_ON_DELETE_TRIGGER);
}
static synchronized NotesDatabaseHelper getInstance(Context context) {
if (mInstance == null) {
mInstance = new NotesDatabaseHelper(context);
}
return mInstance;
}
@Override
public void onCreate(SQLiteDatabase db) {
createNoteTable(db);
createDataTable(db);
}
@Override
public void onUpgrade(SQLiteDatabase db, int oldVersion, int newVersion) {
boolean reCreateTriggers = false;
boolean skipV2 = false;
if (oldVersion == 1) {
upgradeToV2(db);
skipV2 = true; // this upgrade including the upgrade from v2 to v3
oldVersion++;
}
if (oldVersion == 2 && !skipV2) {
upgradeToV3(db);
reCreateTriggers = true;
oldVersion++;
}
if (oldVersion == 3) {
upgradeToV4(db);
oldVersion++;
}
if (reCreateTriggers) {
reCreateNoteTableTriggers(db);
reCreateDataTableTriggers(db);
}
if (oldVersion != newVersion) {
throw new IllegalStateException("Upgrade notes database to version " + newVersion
+ "fails");
}
}
private void upgradeToV2(SQLiteDatabase db) {
db.execSQL("DROP TABLE IF EXISTS " + TABLE.NOTE);
db.execSQL("DROP TABLE IF EXISTS " + TABLE.DATA);
createNoteTable(db);
createDataTable(db);
}
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
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);
db.insert(TABLE.NOTE, null, values);
}
private void upgradeToV4(SQLiteDatabase db) {
db.execSQL("ALTER TABLE " + TABLE.NOTE + " ADD COLUMN " + NoteColumns.VERSION
+ " INTEGER NOT NULL DEFAULT 0");
}
/**
*/
private static final String DB_NAME = "note.db";
/**
*/
private static final int DB_VERSION = 4;
/**
*/
public interface TABLE {
public static final String NOTE = "note";
public static final String DATA = "data";
}
/**
*/
private static final String TAG = "NotesDatabaseHelper";
/**
*/
private static NotesDatabaseHelper mInstance;
/**
NOTESQL
*/
private static final String CREATE_NOTE_TABLE_SQL = "CREATE TABLE " + TABLE.NOTE + "(" +
NoteColumns.ID + " INTEGER PRIMARY KEY," +
NoteColumns.PARENT_ID + " INTEGER NOT NULL DEFAULT 0," +
NoteColumns.ALERTED_DATE + " INTEGER NOT NULL DEFAULT 0," +
NoteColumns.BG_COLOR_ID + " INTEGER NOT NULL DEFAULT 0," +
NoteColumns.CREATED_DATE + " INTEGER NOT NULL DEFAULT (strftime('%s','now') * 1000)," +
NoteColumns.HAS_ATTACHMENT + " INTEGER NOT NULL DEFAULT 0," +
NoteColumns.MODIFIED_DATE + " INTEGER NOT NULL DEFAULT (strftime('%s','now') * 1000)," +
NoteColumns.NOTES_COUNT + " INTEGER NOT NULL DEFAULT 0," +
NoteColumns.SNIPPET + " TEXT NOT NULL DEFAULT ''," +
NoteColumns.TYPE + " INTEGER NOT NULL DEFAULT 0," +
NoteColumns.WIDGET_ID + " INTEGER NOT NULL DEFAULT 0," +
NoteColumns.WIDGET_TYPE + " INTEGER NOT NULL DEFAULT -1," +
NoteColumns.SYNC_ID + " INTEGER NOT NULL DEFAULT 0," +
NoteColumns.LOCAL_MODIFIED + " INTEGER NOT NULL DEFAULT 0," +
NoteColumns.ORIGIN_PARENT_ID + " INTEGER NOT NULL DEFAULT 0," +
NoteColumns.GTASK_ID + " TEXT NOT NULL DEFAULT ''," +
NoteColumns.VERSION + " INTEGER NOT NULL DEFAULT 0" +
")";
/**
DATASQL
*/
private static final String CREATE_DATA_TABLE_SQL = "CREATE TABLE " + TABLE.DATA + "(" +
DataColumns.ID + " INTEGER PRIMARY KEY," +
DataColumns.MIME_TYPE + " TEXT NOT NULL," +
DataColumns.NOTE_ID + " INTEGER NOT NULL DEFAULT 0," +
NoteColumns.CREATED_DATE + " INTEGER NOT NULL DEFAULT (strftime('%s','now') * 1000)," +
NoteColumns.MODIFIED_DATE + " INTEGER NOT NULL DEFAULT (strftime('%s','now') * 1000)," +
DataColumns.CONTENT + " TEXT NOT NULL DEFAULT ''," +
DataColumns.DATA1 + " INTEGER," +
DataColumns.DATA2 + " INTEGER," +
DataColumns.DATA3 + " TEXT NOT NULL DEFAULT ''," +
DataColumns.DATA4 + " TEXT NOT NULL DEFAULT ''," +
DataColumns.DATA5 + " TEXT NOT NULL DEFAULT ''" +
")";
/**
DATANOTE_IDSQL
*/
private static final String CREATE_DATA_NOTE_ID_INDEX_SQL = "CREATE INDEX IF NOT EXISTS note_id_index ON " +
TABLE.DATA + "(" + DataColumns.NOTE_ID + ");";
/**
NOTEPARENT_ID
*/
private static final String NOTE_INCREASE_FOLDER_COUNT_ON_UPDATE_TRIGGER = "CREATE TRIGGER increase_folder_count_on_update "+
" AFTER UPDATE OF " + NoteColumns.PARENT_ID + " ON " + TABLE.NOTE +
" BEGIN " +
" UPDATE " + TABLE.NOTE +
" SET " + NoteColumns.NOTES_COUNT + "=" + NoteColumns.NOTES_COUNT + " + 1" +
" WHERE " + NoteColumns.ID + "=new." + NoteColumns.PARENT_ID + ";" +
" END";
/**
NOTEPARENT_ID
*/
private static final String NOTE_DECREASE_FOLDER_COUNT_ON_UPDATE_TRIGGER = "CREATE TRIGGER decrease_folder_count_on_update " +
" AFTER UPDATE OF " + NoteColumns.PARENT_ID + " ON " + TABLE.NOTE +
" BEGIN " +
" UPDATE " + TABLE.NOTE +
" SET " + NoteColumns.NOTES_COUNT + "=" + NoteColumns.NOTES_COUNT + "-1" +
" WHERE " + NoteColumns.ID + "=old." + NoteColumns.PARENT_ID +
" AND " + NoteColumns.NOTES_COUNT + ">0" + ";" +
" END";
// 插入新笔记时自增父文件夹中笔记数量的触发器
private static final String NOTE_INCREASE_FOLDER_COUNT_ON_INSERT_TRIGGER =
"CREATE TRIGGER increase_folder_count_on_insert " +
" AFTER INSERT ON " + TABLE.NOTE +
" BEGIN " +
" UPDATE " + TABLE.NOTE +
" SET " + NoteColumns.NOTES_COUNT + "=" + NoteColumns.NOTES_COUNT + " + 1" +
" WHERE " + NoteColumns.ID + "=new." + NoteColumns.PARENT_ID + ";" +
" END";
// 删除笔记时自减父文件夹中笔记数量的触发器
private static final String NOTE_DECREASE_FOLDER_COUNT_ON_DELETE_TRIGGER =
"CREATE TRIGGER decrease_folder_count_on_delete " +
" AFTER DELETE ON " + TABLE.NOTE +
" BEGIN " +
" UPDATE " + TABLE.NOTE +
" SET " + NoteColumns.NOTES_COUNT + "=" + NoteColumns.NOTES_COUNT + "-1" +
" WHERE " + NoteColumns.ID + "=old." + NoteColumns.PARENT_ID +
" AND " + NoteColumns.NOTES_COUNT + ">0;" +
" END";
// 在插入类型为 "note" 的数据时更新笔记内容的触发器
private static final String DATA_UPDATE_NOTE_CONTENT_ON_INSERT_TRIGGER =
"CREATE TRIGGER update_note_content_on_insert " +
" AFTER INSERT ON " + TABLE.DATA +
" WHEN new." + DataColumns.MIME_TYPE + "='" + DataConstants.NOTE + "'" +
" BEGIN" +
" UPDATE " + TABLE.NOTE +
" SET " + NoteColumns.SNIPPET + "=new." + DataColumns.CONTENT +
" WHERE " + NoteColumns.ID + "=new." + DataColumns.NOTE_ID + ";" +
" END";
// 在更新类型为 "note" 的数据时更新笔记内容的触发器
private static final String DATA_UPDATE_NOTE_CONTENT_ON_UPDATE_TRIGGER =
"CREATE TRIGGER update_note_content_on_update " +
" AFTER UPDATE ON " + TABLE.DATA +
" WHEN old." + DataColumns.MIME_TYPE + "='" + DataConstants.NOTE + "'" +
" BEGIN" +
" UPDATE " + TABLE.NOTE +
" SET " + NoteColumns.SNIPPET + "=new." + DataColumns.CONTENT +
" WHERE " + NoteColumns.ID + "=new." + DataColumns.NOTE_ID + ";" +
" END";
// 在删除类型为 "note" 的数据时更新笔记内容的触发器
private static final String DATA_UPDATE_NOTE_CONTENT_ON_DELETE_TRIGGER =
"CREATE TRIGGER update_note_content_on_delete " +
" AFTER delete ON " + TABLE.DATA +
" WHEN old." + DataColumns.MIME_TYPE + "='" + DataConstants.NOTE + "'" +
" BEGIN" +
" UPDATE " + TABLE.NOTE +
" SET " + NoteColumns.SNIPPET + "=''" +
" WHERE " + NoteColumns.ID + "=old." + DataColumns.NOTE_ID + ";" +
" END";
// 在删除笔记时删除属于该笔记的数据的触发器
private static final String NOTE_DELETE_DATA_ON_DELETE_TRIGGER =
"CREATE TRIGGER delete_data_on_delete " +
" AFTER DELETE ON " + TABLE.NOTE +
" BEGIN" +
" DELETE FROM " + TABLE.DATA +
" WHERE " + DataColumns.NOTE_ID + "=old." + NoteColumns.ID + ";" +
" END";
// 在删除文件夹时删除属于该文件夹的笔记的触发器
private static final String FOLDER_DELETE_NOTES_ON_DELETE_TRIGGER =
"CREATE TRIGGER folder_delete_notes_on_delete " +
" AFTER DELETE ON " + TABLE.NOTE +
" BEGIN" +
" DELETE FROM " + TABLE.NOTE +
" WHERE " + NoteColumns.PARENT_ID + "=old." + NoteColumns.ID + ";" +
" END";
// 在将文件夹移动到垃圾桶文件夹时移动该文件夹中的笔记的触发器
private static final String FOLDER_MOVE_NOTES_ON_TRASH_TRIGGER =
"CREATE TRIGGER folder_move_notes_on_trash " +
" AFTER UPDATE ON " + TABLE.NOTE +
" WHEN new." + NoteColumns.PARENT_ID + "=" + Notes.ID_TRASH_FOLER +
" BEGIN" +
" UPDATE " + TABLE.NOTE +
" SET " + NoteColumns.PARENT_ID + "=" + Notes.ID_TRASH_FOLER +
" WHERE " + NoteColumns.PARENT_ID + "=old." + NoteColumns.ID + ";" +
" END";
public NotesDatabaseHelper(Context context) {
super(context, DB_NAME, null, DB_VERSION);
}
// 创建笔记表
public void createNoteTable(SQLiteDatabase db) {
db.execSQL(CREATE_NOTE_TABLE_SQL); // 创建note表
reCreateNoteTableTriggers(db); // 重新创建note表的触发器
createSystemFolder(db); // 创建系统文件夹
Log.d(TAG, "note table has been created"); // 记录日志提示note表已被创建
}
// 重新创建笔记表的触发器
private void reCreateNoteTableTriggers(SQLiteDatabase db) {
db.execSQL("DROP TRIGGER IF EXISTS increase_folder_count_on_update"); // 删除更新文件夹计数器触发器
db.execSQL("DROP TRIGGER IF EXISTS decrease_folder_count_on_update"); // 删除更新文件夹计数器触发器
db.execSQL("DROP TRIGGER IF EXISTS decrease_folder_count_on_delete"); // 删除删除文件夹计数器触发器
db.execSQL("DROP TRIGGER IF EXISTS delete_data_on_delete"); // 删除删除关联数据触发器
db.execSQL("DROP TRIGGER IF EXISTS increase_folder_count_on_insert"); // 删除插入文件夹计数器触发器
db.execSQL("DROP TRIGGER IF EXISTS folder_delete_notes_on_delete"); // 删除文件夹删除笔记触发器
db.execSQL("DROP TRIGGER IF EXISTS folder_move_notes_on_trash"); // 删除移动笔记到回收站触发器
db.execSQL(NOTE_INCREASE_FOLDER_COUNT_ON_UPDATE_TRIGGER); // 创建更新文件夹计数器触发器
db.execSQL(NOTE_DECREASE_FOLDER_COUNT_ON_UPDATE_TRIGGER); // 创建更新文件夹计数器触发器
db.execSQL(NOTE_DECREASE_FOLDER_COUNT_ON_DELETE_TRIGGER); // 创建删除文件夹计数器触发器
db.execSQL(NOTE_DELETE_DATA_ON_DELETE_TRIGGER); // 创建删除关联数据触发器
db.execSQL(NOTE_INCREASE_FOLDER_COUNT_ON_INSERT_TRIGGER); // 创建插入文件夹计数器触发器
db.execSQL(FOLDER_DELETE_NOTES_ON_DELETE_TRIGGER); // 创建文件夹删除笔记触发器
db.execSQL(FOLDER_MOVE_NOTES_ON_TRASH_TRIGGER); // 创建移动笔记到回收站触发器
}
private void createSystemFolder(SQLiteDatabase db) {
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);
values.put(NoteColumns.TYPE, Notes.TYPE_SYSTEM);
db.insert(TABLE.NOTE, null, values);
/**
* temporary folder which is used for moving note
*
*/
values.clear();
values.put(NoteColumns.ID, Notes.ID_TEMPARAY_FOLDER);
values.put(NoteColumns.TYPE, Notes.TYPE_SYSTEM);
db.insert(TABLE.NOTE, null, values);
/**
* create trash folder
*
*/
values.clear();
values.put(NoteColumns.ID, Notes.ID_TRASH_FOLER);
values.put(NoteColumns.TYPE, Notes.TYPE_SYSTEM);
db.insert(TABLE.NOTE, null, values);
}
public void createDataTable(SQLiteDatabase db) {
db.execSQL(CREATE_DATA_TABLE_SQL); // 创建data表
reCreateDataTableTriggers(db); // 重新创建data表的触发器
db.execSQL(CREATE_DATA_NOTE_ID_INDEX_SQL); // 创建data表的note_id索引
Log.d(TAG, "data table has been created"); // 记录日志提示data表已被创建
}
private void reCreateDataTableTriggers(SQLiteDatabase db) {
db.execSQL("DROP TRIGGER IF EXISTS update_note_content_on_insert"); // 删除插入note时更新数据触发器
db.execSQL("DROP TRIGGER IF EXISTS update_note_content_on_update"); // 删除更新note时更新数据触发器
db.execSQL("DROP TRIGGER IF EXISTS update_note_content_on_delete"); // 删除删除note时更新数据触发器
db.execSQL(DATA_UPDATE_NOTE_CONTENT_ON_INSERT_TRIGGER); // 创建插入note时更新数据触发器
db.execSQL(DATA_UPDATE_NOTE_CONTENT_ON_UPDATE_TRIGGER); // 创建更新note时更新数据触发器
db.execSQL(DATA_UPDATE_NOTE_CONTENT_ON_DELETE_TRIGGER); // 创建删除note时更新数据触发器
}
static synchronized NotesDatabaseHelper getInstance(Context context) {
if (mInstance == null) {
mInstance = new NotesDatabaseHelper(context); // 单例模式获取NotesDatabaseHelper实例
}
return mInstance;
}
@Override
public void onCreate(SQLiteDatabase db) {
createNoteTable(db); // 创建note表
createDataTable(db); // 创建data表
}
@Override
public void onUpgrade(SQLiteDatabase db, int oldVersion, int newVersion) {
boolean reCreateTriggers = false;
boolean skipV2 = false;
if (oldVersion == 1) {
upgradeToV2(db); // 更新到版本2
skipV2 = true; // 这次更新同时包含版本2到版本3的更新
oldVersion++;
}
if (oldVersion == 2 && !skipV2) {
upgradeToV3(db); // 更新到版本3
reCreateTriggers = true;
oldVersion++;
}
if (oldVersion == 3) {
upgradeToV4(db); // 更新到版本4
oldVersion++;
}
if (reCreateTriggers) {
reCreateNoteTableTriggers(db); // 重新创建note表触发器
reCreateDataTableTriggers(db); // 重新创建data表触发器
}
if (oldVersion != newVersion) {
throw new IllegalStateException("Upgrade notes database to version " + newVersion
+ "fails"); // 如果更新版本不是最新版本,则输出错误信息
}
}
private void upgradeToV2(SQLiteDatabase db) {
db.execSQL("DROP TABLE IF EXISTS " + TABLE.NOTE); // 删除note表
db.execSQL("DROP TABLE IF EXISTS " + TABLE.DATA); // 删除data表
createNoteTable(db); // 创建note表
createDataTable(db); // 创建data表
}
private void upgradeToV3(SQLiteDatabase db) {
// drop unused triggers
db.execSQL("DROP TRIGGER IF EXISTS update_note_modified_date_on_insert"); // 删除插入note时更新修改日期触发器
db.execSQL("DROP TRIGGER IF EXISTS update_note_modified_date_on_delete"); // 删除删除note时更新修改日期触发器
db.execSQL("DROP TRIGGER IF EXISTS update_note_modified_date_on_update"); // 删除更新note时更新修改日期触发器
// add a column for gtask id
db.execSQL("ALTER TABLE " + TABLE.NOTE + " ADD COLUMN " + NoteColumns.GTASK_ID
+ " TEXT NOT NULL DEFAULT ''"); // 在note表中添加gtask列
// add a trash system folder
ContentValues values = new ContentValues();
values.put(NoteColumns.ID, Notes.ID_TRASH_FOLER); // 添加回收站文件夹
values.put(NoteColumns.TYPE, Notes.TYPE_SYSTEM);
db.insert(TABLE.NOTE, null, values);
}
private void upgradeToV4(SQLiteDatabase db) {
db.execSQL("ALTER TABLE " + TABLE.NOTE + " ADD COLUMN " + NoteColumns.VERSION
+ " INTEGER NOT NULL DEFAULT 0"); // 在note表中添加版本列
}

@ -36,36 +36,36 @@ import net.micode.notes.data.NotesDatabaseHelper.TABLE;
public class NotesProvider extends ContentProvider {
private static final UriMatcher mMatcher;
// 定义UriMatcher用于匹配Uri
private static final UriMatcher mMatcher;
// 数据库帮助类
private NotesDatabaseHelper mHelper;
// 日志标识
private static final String TAG = "NotesProvider";
// Uri匹配的常量值
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 NotesDatabaseHelper mHelper;
private static final String TAG = "NotesProvider";
private static final int URI_NOTE = 1;
private static final int URI_NOTE_ITEM = 2;
private static final int URI_DATA = 3;
private static final int URI_DATA_ITEM = 4;
private static final int URI_SEARCH = 5;
private static final int URI_SEARCH_SUGGEST = 6;
static {
mMatcher = new UriMatcher(UriMatcher.NO_MATCH);
mMatcher.addURI(Notes.AUTHORITY, "note", URI_NOTE);
mMatcher.addURI(Notes.AUTHORITY, "note/#", URI_NOTE_ITEM);
mMatcher.addURI(Notes.AUTHORITY, "data", URI_DATA);
mMatcher.addURI(Notes.AUTHORITY, "data/#", URI_DATA_ITEM);
mMatcher.addURI(Notes.AUTHORITY, "search", URI_SEARCH);
mMatcher.addURI(Notes.AUTHORITY, SearchManager.SUGGEST_URI_PATH_QUERY, URI_SEARCH_SUGGEST);
mMatcher.addURI(Notes.AUTHORITY, SearchManager.SUGGEST_URI_PATH_QUERY + "/*", URI_SEARCH_SUGGEST);
}
static {
mMatcher = new UriMatcher(UriMatcher.NO_MATCH);
// 添加Uri匹配规则
mMatcher.addURI(Notes.AUTHORITY, "note", URI_NOTE);
mMatcher.addURI(Notes.AUTHORITY, "note/#", URI_NOTE_ITEM);
mMatcher.addURI(Notes.AUTHORITY, "data", URI_DATA);
mMatcher.addURI(Notes.AUTHORITY, "data/#", URI_DATA_ITEM);
mMatcher.addURI(Notes.AUTHORITY, "search", URI_SEARCH);
mMatcher.addURI(Notes.AUTHORITY, SearchManager.SUGGEST_URI_PATH_QUERY, URI_SEARCH_SUGGEST);
mMatcher.addURI(Notes.AUTHORITY, SearchManager.SUGGEST_URI_PATH_QUERY + "/*", URI_SEARCH_SUGGEST);
}
/**
* x'0A' represents the '\n' character in sqlite. For title and content in the search result,
* we will trim '\n' and white space in order to show more information.
*/
private static final String NOTES_SEARCH_PROJECTION = NoteColumns.ID + ","
/**
* '\n'
*/
private static final String NOTES_SEARCH_PROJECTION = NoteColumns.ID + ","
+ NoteColumns.ID + " AS " + SearchManager.SUGGEST_COLUMN_INTENT_EXTRA_DATA + ","
+ "TRIM(REPLACE(" + NoteColumns.SNIPPET + ", x'0A','')) AS " + SearchManager.SUGGEST_COLUMN_TEXT_1 + ","
+ "TRIM(REPLACE(" + NoteColumns.SNIPPET + ", x'0A','')) AS " + SearchManager.SUGGEST_COLUMN_TEXT_2 + ","
@ -73,233 +73,226 @@ public class NotesProvider extends ContentProvider {
+ "'" + Intent.ACTION_VIEW + "' AS " + SearchManager.SUGGEST_COLUMN_INTENT_ACTION + ","
+ "'" + Notes.TextNote.CONTENT_TYPE + "' AS " + SearchManager.SUGGEST_COLUMN_INTENT_DATA;
private static String NOTES_SNIPPET_SEARCH_QUERY = "SELECT " + NOTES_SEARCH_PROJECTION
+ " FROM " + TABLE.NOTE
+ " WHERE " + NoteColumns.SNIPPET + " LIKE ?"
+ " AND " + NoteColumns.PARENT_ID + "<>" + Notes.ID_TRASH_FOLER
+ " AND " + NoteColumns.TYPE + "=" + Notes.TYPE_NOTE;
@Override
public boolean onCreate() {
mHelper = NotesDatabaseHelper.getInstance(getContext());
return true;
}
@Override
public Cursor query(Uri uri, String[] projection, String selection, String[] selectionArgs,
String sortOrder) {
Cursor c = null;
SQLiteDatabase db = mHelper.getReadableDatabase();
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
+ parseSelection(selection), selectionArgs, null, null, sortOrder);
break;
case URI_SEARCH:
case URI_SEARCH_SUGGEST:
if (sortOrder != null || projection != null) {
throw new IllegalArgumentException(
"do not specify sortOrder, selection, selectionArgs, or projection" + "with this query");
}
String searchString = null;
if (mMatcher.match(uri) == URI_SEARCH_SUGGEST) {
if (uri.getPathSegments().size() > 1) {
searchString = uri.getPathSegments().get(1);
}
} else {
searchString = uri.getQueryParameter("pattern");
}
if (TextUtils.isEmpty(searchString)) {
return null;
}
try {
searchString = String.format("%%%s%%", searchString);
c = db.rawQuery(NOTES_SNIPPET_SEARCH_QUERY,
new String[] { searchString });
} catch (IllegalStateException ex) {
Log.e(TAG, "got exception: " + ex.toString());
}
break;
default:
throw new IllegalArgumentException("Unknown URI " + uri);
}
if (c != null) {
c.setNotificationUri(getContext().getContentResolver(), uri);
}
return c;
}
@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:
if (values.containsKey(DataColumns.NOTE_ID)) {
noteId = values.getAsLong(DataColumns.NOTE_ID);
} 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
if (noteId > 0) {
getContext().getContentResolver().notifyChange(
ContentUris.withAppendedId(Notes.CONTENT_NOTE_URI, noteId), null);
}
// Notify the data uri
if (dataId > 0) {
getContext().getContentResolver().notifyChange(
ContentUris.withAppendedId(Notes.CONTENT_DATA_URI, dataId), null);
}
// 搜索语句
private static String NOTES_SNIPPET_SEARCH_QUERY =
"SELECT " + NOTES_SEARCH_PROJECTION
+ " FROM " + TABLE.NOTE
+ " WHERE " + NoteColumns.SNIPPET + " LIKE ?"
+ " AND " + NoteColumns.PARENT_ID + "<>" + Notes.ID_TRASH_FOLER
+ " AND " + NoteColumns.TYPE + "=" + Notes.TYPE_NOTE;
return ContentUris.withAppendedId(uri, insertedId);
}
@Override
public boolean onCreate() {
// 获取数据库帮助类实例
mHelper = NotesDatabaseHelper.getInstance(getContext());
return true;
}
@Override
public int delete(Uri uri, String selection, String[] selectionArgs) {
int count = 0;
String id = null;
SQLiteDatabase db = mHelper.getWritableDatabase();
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
*/
long noteId = Long.valueOf(id);
if (noteId <= 0) {
break;
@Override
public Cursor query(Uri uri, String[] projection, String selection, String[] selectionArgs,
String sortOrder) {
Cursor c = null;
SQLiteDatabase db = mHelper.getReadableDatabase();
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
+ parseSelection(selection), selectionArgs, null, null, sortOrder);
break;
case URI_SEARCH:
case URI_SEARCH_SUGGEST:
if (sortOrder != null || projection != null) {
throw new IllegalArgumentException(
"do not specify sortOrder, selection, selectionArgs, or projection"
+ "with this query");
}
String searchString = null;
if (mMatcher.match(uri) == URI_SEARCH_SUGGEST) {
if (uri.getPathSegments().size() > 1) {
searchString = uri.getPathSegments().get(1);
}
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;
break;
default:
throw new IllegalArgumentException("Unknown URI " + uri);
}
if (count > 0) {
if (deleteData) {
getContext().getContentResolver().notifyChange(Notes.CONTENT_NOTE_URI, null);
} else {
searchString = uri.getQueryParameter("pattern");
}
getContext().getContentResolver().notifyChange(uri, null);
}
return count;
}
@Override
public int update(Uri uri, ContentValues values, String selection, String[] selectionArgs) {
int count = 0;
String id = null;
SQLiteDatabase db = mHelper.getWritableDatabase();
boolean updateData = false;
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;
break;
default:
throw new IllegalArgumentException("Unknown URI " + uri);
}
if (count > 0) {
if (updateData) {
getContext().getContentResolver().notifyChange(Notes.CONTENT_NOTE_URI, null);
if (TextUtils.isEmpty(searchString)) {
return null;
}
try {
searchString = String.format("%%%s%%", searchString);
c = db.rawQuery(NOTES_SNIPPET_SEARCH_QUERY,
new String[] { searchString });
} catch (IllegalStateException ex) {
Log.e(TAG, "got exception: " + ex.toString());
}
getContext().getContentResolver().notifyChange(uri, null);
}
return count;
break;
default:
throw new IllegalArgumentException("Unknown URI " + uri);
}
private String parseSelection(String selection) {
return (!TextUtils.isEmpty(selection) ? " AND (" + selection + ')' : "");
if (c != null) {
// 设置查询结果发生改变时更新的Uri
c.setNotificationUri(getContext().getContentResolver(), uri);
}
return c;
}
}
@Override
public Uri insert(Uri uri, ContentValues values) {
SQLiteDatabase db = mHelper.getWritableDatabase();//获取写入模式的数据库
long dataId = 0, noteId = 0, insertedId = 0;//初始化三个变量
switch (mMatcher.match(uri)) {//根据Uri匹配相应的操作
case URI_NOTE://如果是Note表
insertedId = noteId = db.insert(TABLE.NOTE, null, values);//插入数据并获取行号
break;
case URI_DATA://如果是Data表
if (values.containsKey(DataColumns.NOTE_ID)) {//判断是否包含note id
noteId = values.getAsLong(DataColumns.NOTE_ID);//获取note id
} 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);//如果Uri不匹配则抛异常
}
// Notify the note uri
if (noteId > 0) {//如果note id不为空
getContext().getContentResolver().notifyChange(//调用notifyChange方法通知Uri已经被更改
ContentUris.withAppendedId(Notes.CONTENT_NOTE_URI, noteId), null);//ContentUris.withAppendedId方法是在后面添加一个id用来获取Uri
}
// Notify the data uri
if (dataId > 0) {//如果data id不为空
getContext().getContentResolver().notifyChange(//调用notifyChange方法通知Uri已经被更改
ContentUris.withAppendedId(Notes.CONTENT_DATA_URI, dataId), null);//ContentUris.withAppendedId方法是在后面添加一个id用来获取Uri
}
return ContentUris.withAppendedId(uri, insertedId);//最终返回更新后的Uri
}
private void increaseNoteVersion(long id, String selection, String[] selectionArgs) {
StringBuilder sql = new StringBuilder(120);
sql.append("UPDATE ");
sql.append(TABLE.NOTE);
sql.append(" SET ");
sql.append(NoteColumns.VERSION);
sql.append("=" + NoteColumns.VERSION + "+1 ");
@Override
public int delete(Uri uri, String selection, String[] selectionArgs) {
int count = 0;
String id = null;
SQLiteDatabase db = mHelper.getWritableDatabase();//获取写入模式的数据库
boolean deleteData = false;//初始化deleteData变量
switch (mMatcher.match(uri)) {//根据Uri匹配相应的操作
case URI_NOTE://如果是Note表
selection = "(" + selection + ") AND " + NoteColumns.ID + ">0 ";//删除条件
count = db.delete(TABLE.NOTE, selection, selectionArgs);//删除数据,并获取删除行数
break;
case URI_NOTE_ITEM:
id = uri.getPathSegments().get(1);//获取Uri中的id
/*ID that smaller than 0 is system folder which is not allowed to trash*/
long noteId = Long.valueOf(id);//将id转化为long类型
if (noteId <= 0) {
break;
}
count = db.delete(TABLE.NOTE,
NoteColumns.ID + "=" + id + parseSelection(selection), selectionArgs);//删除数据,并获取删除行数
break;
case URI_DATA://如果是Data表
count = db.delete(TABLE.DATA, selection, selectionArgs);//删除数据,并获取删除行数
deleteData = true;//deleteData为true
break;
case URI_DATA_ITEM:
id = uri.getPathSegments().get(1);//获取Uri中的id
count = db.delete(TABLE.DATA,
DataColumns.ID + "=" + id + parseSelection(selection), selectionArgs);//删除数据,并获取删除行数
deleteData = true;//deleteData为true
break;
default:
throw new IllegalArgumentException("Unknown URI " + uri);//如果Uri不匹配则抛异常
}
if (count > 0) {//如果删除行数大于0
if (deleteData) {//如果是Data表
getContext().getContentResolver().notifyChange(Notes.CONTENT_NOTE_URI, null);//调用notifyChange方法通知Uri已经被更改
}
getContext().getContentResolver().notifyChange(uri, null);//调用notifyChange方法通知Uri已经被更改
}
return count;//返回删除行数
}
if (id > 0 || !TextUtils.isEmpty(selection)) {
sql.append(" WHERE ");
}
if (id > 0) {
sql.append(NoteColumns.ID + "=" + String.valueOf(id));
}
if (!TextUtils.isEmpty(selection)) {
String selectString = id > 0 ? parseSelection(selection) : selection;
for (String args : selectionArgs) {
selectString = selectString.replaceFirst("\\?", args);
}
sql.append(selectString);
}
@Override
public int update(Uri uri, ContentValues values, String selection, String[] selectionArgs) {
int count = 0;
String id = null;
SQLiteDatabase db = mHelper.getWritableDatabase();//获取写入模式的数据库
boolean updateData = false;//初始化updateData变量
switch (mMatcher.match(uri)) {//根据Uri匹配相应的操作
case URI_NOTE://如果是Note表
increaseNoteVersion(-1, selection, selectionArgs);//更新版本号
count = db.update(TABLE.NOTE, values, selection, selectionArgs);//更新数据,并获取更新行数
break;
case URI_NOTE_ITEM:
id = uri.getPathSegments().get(1);//获取Uri中的id
increaseNoteVersion(Long.valueOf(id), selection, selectionArgs);//更新版本号
count = db.update(TABLE.NOTE, values, NoteColumns.ID + "=" + id
+ parseSelection(selection), selectionArgs);//更新数据,并获取更新行数
break;
case URI_DATA://如果是Data表
count = db.update(TABLE.DATA, values, selection, selectionArgs);//更新数据,并获取更新行数
updateData = true;//updateData为true
break;
case URI_DATA_ITEM:
id = uri.getPathSegments().get(1);//获取Uri中的id
count = db.update(TABLE.DATA, values, DataColumns.ID + "=" + id
+ parseSelection(selection), selectionArgs);//更新数据,并获取更新行数
updateData = true;//updateData为true
break;
default:
throw new IllegalArgumentException("Unknown URI " + uri);//如果Uri不匹配则抛异常
}
if (count > 0) {//如果更新行数大于0
if (updateData) {//如果是Data表
getContext().getContentResolver().notifyChange(Notes.CONTENT_NOTE_URI, null);//调用notifyChange方法通知Uri已经被更改
}
getContext().getContentResolver().notifyChange(uri, null);//调用notifyChange方法通知Uri已经被更改
}
return count;//返回更新行数
}
mHelper.getWritableDatabase().execSQL(sql.toString());
}
private String parseSelection(String selection) {
return (!TextUtils.isEmpty(selection) ? " AND (" + selection + ')' : "");//解析选择条件
}
@Override
public String getType(Uri uri) {
// TODO Auto-generated method stub
return null;
}
private void increaseNoteVersion(long id, String selection, String[] selectionArgs) {
StringBuilder sql = new StringBuilder(120);//创建一个StringBuilder
sql.append("UPDATE ");
sql.append(TABLE.NOTE);//更新Note表
sql.append(" SET ");
sql.append(NoteColumns.VERSION);
sql.append("=" + NoteColumns.VERSION + "+1 ");//版本号加1
if (id > 0 || !TextUtils.isEmpty(selection)) {//如果id大于0或有选择条件
sql.append(" WHERE ");//添加WHERE子句
}
if (id > 0) {//如果id大于0
sql.append(NoteColumns.ID + "=" + String.valueOf(id));//更新对应的id
}
if (!TextUtils.isEmpty(selection)) {//如果选择条件不为空
String selectString = id > 0 ? parseSelection(selection) : selection;
for (String args : selectionArgs) {
selectString = selectString.replaceFirst("\?", args);
}
sql.append(selectString);//解析选择条件并添加进sql中
}
mHelper.getWritableDatabase().execSQL(sql.toString());//执行sql语句
}
@Override
public String getType(Uri uri) {
// TODO Auto-generated method stub
return null;//返回空类型
}

@ -30,54 +30,59 @@ public abstract class Node {//定义各种常量
private long mLastModified;//记录最后一次修改时间
private boolean mDeleted;//表征是否被删除
public Node() {
//mGid: 字符串类型,用于标识 Node 对象的唯一 ID。
//mName: 字符串类型,表示 Node 对象的名称。
//mLastModified: 长整型,表示 Node 对象最后一次修改的时间戳。
//mDeleted: 布尔型,表示 Node 对象是否已被删除。
public Node() {
mGid = null;
mName = "";
mLastModified = 0;
mDeleted = false;
}
//getCreateAction(int actionId): 返回一个 JSON 对象,用于创建一个新的 Node 对象。
public abstract JSONObject getCreateAction(int actionId);
//getUpdateAction(int actionId): 返回一个 JSON 对象,用于更新已有的 Node 对象。
public abstract JSONObject getUpdateAction(int actionId);
//setContentByRemoteJSON(JSONObject js): 从远程 JSON 对象中解析数据并设置 Node 对象的属性。
public abstract void setContentByRemoteJSON(JSONObject js);
//setContentByLocalJSON(JSONObject js): 从本地 JSON 对象中解析数据并设置 Node 对象的属性。
public abstract void setContentByLocalJSON(JSONObject js);
//getLocalJSONFromContent(): 返回一个本地 JSON 对象,包含了 Node 对象的所有属性。
public abstract JSONObject getLocalJSONFromContent();
//getSyncAction(Cursor c): 返回一个整型值,表示需要执行的同步操作类型。
public abstract int getSyncAction(Cursor c);
//setGid(String gid): 设置 mGid 属性的值。
public void setGid(String gid) {
this.mGid = gid;
}
//setName(String name): 设置 mName 属性的值。
public void setName(String name) {
this.mName = name;
}
//setLastModified(long lastModified): 设置 mLastModified 属性的值。
public void setLastModified(long lastModified) {
this.mLastModified = lastModified;
}
//setDeleted(boolean deleted): 设置 mDeleted 属性的值。
public void setDeleted(boolean deleted) {
this.mDeleted = deleted;
}
//getGid(): 返回 mGid 属性的值。
public String getGid() {
return this.mGid;
}
//getName(): 返回 mName 属性的值。
public String getName() {
return this.mName;
}
//getLastModified(): 返回 mLastModified 属性的值。
public long getLastModified() {
return this.mLastModified;
}
//getDeleted(): 返回 mDeleted 属性的值。
public boolean getDeleted() {
return this.mDeleted;
}

@ -74,47 +74,66 @@ public class SqlData {
}
private void loadFromCursor(Cursor c) {
// 从 Cursor 对象中获取列索引为 DATA_ID_COLUMN 的长整型数据,并将其赋值给当前对象的 mDataId 属性
mDataId = c.getLong(DATA_ID_COLUMN);
// 从 Cursor 对象中获取列索引为 DATA_MIME_TYPE_COLUMN 的字符串数据,并将其赋值给当前对象的 mDataMimeType 属性
mDataMimeType = c.getString(DATA_MIME_TYPE_COLUMN);
// 从 Cursor 对象中获取列索引为 DATA_CONTENT_COLUMN 的字符串数据,并将其赋值给当前对象的 mDataContent 属性
mDataContent = c.getString(DATA_CONTENT_COLUMN);
// 从 Cursor 对象中获取列索引为 DATA_CONTENT_DATA_1_COLUMN 的长整型数据,并将其赋值给当前对象的 mDataContentData1 属性
mDataContentData1 = c.getLong(DATA_CONTENT_DATA_1_COLUMN);
// 从 Cursor 对象中获取列索引为 DATA_CONTENT_DATA_3_COLUMN 的字符串数据,并将其赋值给当前对象的 mDataContentData3 属性
mDataContentData3 = c.getString(DATA_CONTENT_DATA_3_COLUMN);
}
public void setContent(JSONObject js) throws JSONException {
//如果传入的JSONObject对象中有DataColumns.ID这一项则设置否则设为INVALID_ID
// 如果传入的JSONObject对象中有DataColumns.ID这一项则设置否则设为INVALID_ID
long dataId = js.has(DataColumns.ID) ? js.getLong(DataColumns.ID) : INVALID_ID;
// 如果当前是新增操作或数据ID已发生变化将数据ID加入到待更新列表中
if (mIsCreate || mDataId != dataId) {
mDiffDataValues.put(DataColumns.ID, dataId);
}
// 更新当前数据ID
mDataId = dataId;
// 获取数据的MIME类型如果不存在则设置为DataConstants.NOTE
String dataMimeType = js.has(DataColumns.MIME_TYPE) ? js.getString(DataColumns.MIME_TYPE)
: DataConstants.NOTE;
// 如果当前是新增操作或数据的MIME类型已发生变化将MIME类型加入到待更新列表中
if (mIsCreate || !mDataMimeType.equals(dataMimeType)) {
mDiffDataValues.put(DataColumns.MIME_TYPE, dataMimeType);
}
// 更新当前数据的MIME类型
mDataMimeType = dataMimeType;
// 获取数据的内容,如果不存在则设置为空字符串
String dataContent = js.has(DataColumns.CONTENT) ? js.getString(DataColumns.CONTENT) : "";
// 如果当前是新增操作或数据的内容已发生变化,将数据内容加入到待更新列表中
if (mIsCreate || !mDataContent.equals(dataContent)) {
mDiffDataValues.put(DataColumns.CONTENT, dataContent);
}
// 更新当前数据的内容
mDataContent = dataContent;
// 获取数据的DATA1如果不存在则设置为0
long dataContentData1 = js.has(DataColumns.DATA1) ? js.getLong(DataColumns.DATA1) : 0;
// 如果当前是新增操作或数据的DATA1已发生变化将DATA1加入到待更新列表中
if (mIsCreate || mDataContentData1 != dataContentData1) {
mDiffDataValues.put(DataColumns.DATA1, dataContentData1);
}
// 更新当前数据的DATA1
mDataContentData1 = dataContentData1;
// 获取数据的DATA3如果不存在则设置为空字符串
String dataContentData3 = js.has(DataColumns.DATA3) ? js.getString(DataColumns.DATA3) : "";
// 如果当前是新增操作或数据的DATA3已发生变化将DATA3加入到待更新列表中
if (mIsCreate || !mDataContentData3.equals(dataContentData3)) {
mDiffDataValues.put(DataColumns.DATA3, dataContentData3);
}
// 更新当前数据的DATA3
mDataContentData3 = dataContentData3;
}
public JSONObject getContent() throws JSONException {//创建JSONObject对象。并将相关数据放入其中并返回
if (mIsCreate) {
Log.e(TAG, "it seems that we haven't created this in database yet");
@ -129,14 +148,15 @@ public class SqlData {
return js;
}
public void commit(long noteId, boolean validateVersion, long version) {//commit函数用于把当前造作所做的修改保存到数据库
public void commit(long noteId, boolean validateVersion, long version) {
// 如果是新建数据,则需要把 noteId 设置进去,并插入到数据库中
if (mIsCreate) {
if (mDataId == INVALID_ID && mDiffDataValues.containsKey(DataColumns.ID)) {
mDiffDataValues.remove(DataColumns.ID);
}
// 把 noteId 设置进去
mDiffDataValues.put(DataColumns.NOTE_ID, noteId);
// 插入到数据库中
Uri uri = mContentResolver.insert(Notes.CONTENT_DATA_URI, mDiffDataValues);
try {
mDataId = Long.valueOf(uri.getPathSegments().get(1));
@ -144,13 +164,14 @@ public class SqlData {
Log.e(TAG, "Get note id error :" + e.toString());
throw new ActionFailureException("create note failed");
}
} else {
} else { // 如果不是新建数据,则需要更新数据
if (mDiffDataValues.size() > 0) {
int result = 0;
// 如果不需要验证版本号,则直接更新数据库
if (!validateVersion) {
result = mContentResolver.update(ContentUris.withAppendedId(
Notes.CONTENT_DATA_URI, mDataId), mDiffDataValues, null, null);
} else {
} else { // 如果需要验证版本号,则需要在更新前进行检查
result = mContentResolver.update(ContentUris.withAppendedId(
Notes.CONTENT_DATA_URI, mDataId), mDiffDataValues,
" ? in (SELECT " + NoteColumns.ID + " FROM " + TABLE.NOTE
@ -163,7 +184,7 @@ public class SqlData {
}
}
}
// 清空修改数据的 Map并标记为非新建数据
mDiffDataValues.clear();
mIsCreate = false;
}

@ -169,6 +169,15 @@ public class SqlNote {
}
}
//具体地说这个方法依次从Cursor对象中读取ID_COLUMN、ALERTED_DATE_COLUMN、BG_COLOR_ID_COLUMN、
//CREATED_DATE_COLUMN、HAS_ATTACHMENT_COLUMN、MODIFIED_DATE_COLUMN、PARENT_ID_COLUMN、SNIPPET_COLUMN、
//TYPE_COLUMN、WIDGET_ID_COLUMN、WIDGET_TYPE_COLUMN和VERSION_COLUMN对应的列的值
//并将这些值设置为该类的成员变量mId、mAlertDate、mBgColorId、mCreatedDate、mHasAttachment、mModifiedDate、
//mParentId、mSnippet、mType、mWidgetId、mWidgetType和mVersion的值。
// oadFromCursor方法是一个私有方法它有一个参数c该参数是一个Cursor对象。
// 这个方法的作用是从Cursor对象中读取数据并将其设置为该类的成员变量的值。
private void loadFromCursor(Cursor c) {
mId = c.getLong(ID_COLUMN);
mAlertDate = c.getLong(ALERTED_DATE_COLUMN);
@ -183,7 +192,7 @@ public class SqlNote {
mWidgetType = c.getInt(WIDGET_TYPE_COLUMN);
mVersion = c.getLong(VERSION_COLUMN);
}
//loadDataContent方法也是一个私有方法它不接受任何参数。这个方法的作用是从数据库中读取笔记的数据内容。
private void loadDataContent() {
Cursor c = null;
mDataList.clear();
@ -213,24 +222,26 @@ public class SqlNote {
public boolean setContent(JSONObject js) {
try {
JSONObject note = js.getJSONObject(GTaskStringUtils.META_HEAD_NOTE);
// 获取JSON对象中META_HEAD_NOTE字段对应的JSONObject对象并赋值给note
if (note.getInt(NoteColumns.TYPE) == Notes.TYPE_SYSTEM) {
// 如果note的TYPE字段为TYPE_SYSTEM打印warning信息
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) : "";
// 如果note的TYPE字段为TYPE_FOLDER更新Java对象mSnippet和mType的值
String snippet = note.has(NoteColumns.SNIPPET) ? note.getString(NoteColumns.SNIPPET) : "";
if (mIsCreate || !mSnippet.equals(snippet)) {
mDiffNoteValues.put(NoteColumns.SNIPPET, snippet);
}
mSnippet = snippet;
int type = note.has(NoteColumns.TYPE) ? note.getInt(NoteColumns.TYPE)
: Notes.TYPE_NOTE;
int type = note.has(NoteColumns.TYPE) ? note.getInt(NoteColumns.TYPE) : Notes.TYPE_NOTE;
if (mIsCreate || mType != type) {
mDiffNoteValues.put(NoteColumns.TYPE, type);
}
mType = type;
} else if (note.getInt(NoteColumns.TYPE) == Notes.TYPE_NOTE) {
// 如果note的TYPE字段为TYPE_NOTE更新Java对象的各个字段值
JSONArray dataArray = js.getJSONArray(GTaskStringUtils.META_HEAD_DATA);
long id = note.has(NoteColumns.ID) ? note.getLong(NoteColumns.ID) : INVALID_ID;
if (mIsCreate || mId != id) {
@ -238,43 +249,37 @@ public class SqlNote {
}
mId = id;
long alertDate = note.has(NoteColumns.ALERTED_DATE) ? note
.getLong(NoteColumns.ALERTED_DATE) : 0;
long alertDate = note.has(NoteColumns.ALERTED_DATE) ? note.getLong(NoteColumns.ALERTED_DATE) : 0;
if (mIsCreate || mAlertDate != alertDate) {
mDiffNoteValues.put(NoteColumns.ALERTED_DATE, alertDate);
}
mAlertDate = alertDate;
int bgColorId = note.has(NoteColumns.BG_COLOR_ID) ? note
.getInt(NoteColumns.BG_COLOR_ID) : ResourceParser.getDefaultBgId(mContext);
int bgColorId = note.has(NoteColumns.BG_COLOR_ID) ? note.getInt(NoteColumns.BG_COLOR_ID) : ResourceParser.getDefaultBgId(mContext);
if (mIsCreate || mBgColorId != bgColorId) {
mDiffNoteValues.put(NoteColumns.BG_COLOR_ID, bgColorId);
}
mBgColorId = bgColorId;
long createDate = note.has(NoteColumns.CREATED_DATE) ? note
.getLong(NoteColumns.CREATED_DATE) : System.currentTimeMillis();
long createDate = note.has(NoteColumns.CREATED_DATE) ? note.getLong(NoteColumns.CREATED_DATE) : System.currentTimeMillis();
if (mIsCreate || mCreatedDate != createDate) {
mDiffNoteValues.put(NoteColumns.CREATED_DATE, createDate);
}
mCreatedDate = createDate;
int hasAttachment = note.has(NoteColumns.HAS_ATTACHMENT) ? note
.getInt(NoteColumns.HAS_ATTACHMENT) : 0;
int hasAttachment = note.has(NoteColumns.HAS_ATTACHMENT) ? note.getInt(NoteColumns.HAS_ATTACHMENT) : 0;
if (mIsCreate || mHasAttachment != hasAttachment) {
mDiffNoteValues.put(NoteColumns.HAS_ATTACHMENT, hasAttachment);
}
mHasAttachment = hasAttachment;
long modifiedDate = note.has(NoteColumns.MODIFIED_DATE) ? note
.getLong(NoteColumns.MODIFIED_DATE) : System.currentTimeMillis();
long modifiedDate = note.has(NoteColumns.MODIFIED_DATE) ? note.getLong(NoteColumns.MODIFIED_DATE) : System.currentTimeMillis();
if (mIsCreate || mModifiedDate != modifiedDate) {
mDiffNoteValues.put(NoteColumns.MODIFIED_DATE, modifiedDate);
}
mModifiedDate = modifiedDate;
long parentId = note.has(NoteColumns.PARENT_ID) ? note
.getLong(NoteColumns.PARENT_ID) : 0;
long parentId = note.has(NoteColumns.PARENT_ID) ? note.getLong(NoteColumns.PARENT_ID) : 0;
if (mIsCreate || mParentId != parentId) {
mDiffNoteValues.put(NoteColumns.PARENT_ID, parentId);
}
@ -308,6 +313,12 @@ public class SqlNote {
}
mWidgetType = widgetType;
/* JSON
@param jsonData JSON
@return
*/
long originParent = note.has(NoteColumns.ORIGIN_PARENT_ID) ? note
.getLong(NoteColumns.ORIGIN_PARENT_ID) : 0;
if (mIsCreate || mOriginParent != originParent) {
@ -347,13 +358,14 @@ public class SqlNote {
try {
JSONObject js = new JSONObject();
if (mIsCreate) {
if (mIsCreate) { // 如果这个对象还没有被创建
Log.e(TAG, "it seems that we haven't created this in database yet");
return null;
}
// 创建一个名为 note 的 JSON 对象,并将这个对象的属性添加到里面
JSONObject note = new JSONObject();
if (mType == Notes.TYPE_NOTE) {
if (mType == Notes.TYPE_NOTE) { // 如果这个对象是一条笔记
note.put(NoteColumns.ID, mId);
note.put(NoteColumns.ALERTED_DATE, mAlertDate);
note.put(NoteColumns.BG_COLOR_ID, mBgColorId);
@ -366,8 +378,9 @@ public class SqlNote {
note.put(NoteColumns.WIDGET_ID, mWidgetId);
note.put(NoteColumns.WIDGET_TYPE, mWidgetType);
note.put(NoteColumns.ORIGIN_PARENT_ID, mOriginParent);
js.put(GTaskStringUtils.META_HEAD_NOTE, note);
js.put(GTaskStringUtils.META_HEAD_NOTE, note); // 将这个 JSON 对象添加到 js 中
// 创建一个名为 dataArray 的 JSON 数组,并将每个 SqlData 对象的 content 属性添加到里面
JSONArray dataArray = new JSONArray();
for (SqlData sqlData : mDataList) {
JSONObject data = sqlData.getContent();
@ -375,84 +388,85 @@ public class SqlNote {
dataArray.put(data);
}
}
js.put(GTaskStringUtils.META_HEAD_DATA, dataArray);
} else if (mType == Notes.TYPE_FOLDER || mType == Notes.TYPE_SYSTEM) {
js.put(GTaskStringUtils.META_HEAD_DATA, dataArray); // 将这个 JSON 数组添加到 js 中
} 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);
js.put(GTaskStringUtils.META_HEAD_NOTE, note);
js.put(GTaskStringUtils.META_HEAD_NOTE, note); // 将这个 JSON 对象添加到 js 中
}
return js;
return js; // 返回 JSON 对象
} catch (JSONException e) {
Log.e(TAG, e.toString());
e.printStackTrace();
}
return null;
return null; // 如果出现异常返回 null
}
public void setParentId(long id) {
mParentId = id;
mDiffNoteValues.put(NoteColumns.PARENT_ID, id);
public void setParentId(long id) { //设置笔记的父笔记ID
mParentId = id; //保存父笔记ID
mDiffNoteValues.put(NoteColumns.PARENT_ID, id); //将更改的值加入到 mDiffNoteValues 中
}
public void setGtaskId(String gid) {
mDiffNoteValues.put(NoteColumns.GTASK_ID, gid);
public void setGtaskId(String gid) { //设置笔记的 Google 任务ID
mDiffNoteValues.put(NoteColumns.GTASK_ID, gid); //将更改的值加入到 mDiffNoteValues 中
}
public void setSyncId(long syncId) {
mDiffNoteValues.put(NoteColumns.SYNC_ID, syncId);
public void setSyncId(long syncId) { //设置笔记的同步ID
mDiffNoteValues.put(NoteColumns.SYNC_ID, syncId); //将更改的值加入到 mDiffNoteValues 中
}
public void resetLocalModified() {
mDiffNoteValues.put(NoteColumns.LOCAL_MODIFIED, 0);
public void resetLocalModified() { //重置本地修改状态
mDiffNoteValues.put(NoteColumns.LOCAL_MODIFIED, 0); //将更改的值加入到 mDiffNoteValues 中
}
public long getId() {
return mId;
public long getId() { //获取笔记ID
return mId; //返回笔记ID
}
public long getParentId() {
return mParentId;
public long getParentId() { //获取笔记的父笔记ID
return mParentId; //返回笔记的父笔记ID
}
public String getSnippet() {
return mSnippet;
public String getSnippet() { //获取笔记片段
return mSnippet; //返回笔记片段
}
public boolean isNoteType() {
return mType == Notes.TYPE_NOTE;
public boolean isNoteType() { //判断笔记是否为Note类型
return mType == Notes.TYPE_NOTE; //如果笔记类型是Note则返回true否则返回false
}
public void commit(boolean validateVersion) {
if (mIsCreate) {
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));
mId = Long.valueOf(uri.getPathSegments().get(1)); //获取新笔记ID
} catch (NumberFormatException e) {
Log.e(TAG, "Get note id error :" + e.toString());
throw new ActionFailureException("create note failed");
throw new ActionFailureException("create note failed"); //抛出异常,创建笔记失败
}
if (mId == 0) {
throw new IllegalStateException("Create thread id failed");
}
if (mType == Notes.TYPE_NOTE) {
if (mType == Notes.TYPE_NOTE) { //如果是笔记类型
for (SqlData sqlData : mDataList) {
sqlData.commit(mId, false, -1);
sqlData.commit(mId, false, -1); //保存笔记的数据
}
}
} else {
} 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");
}
if (mDiffNoteValues.size() > 0) {
mVersion ++;
if (mDiffNoteValues.size() > 0) { //如果有更改需要提交
mVersion ++; //更新版本号
int result = 0;
if (!validateVersion) {
result = mContentResolver.update(Notes.CONTENT_NOTE_URI, mDiffNoteValues, "("
@ -479,6 +493,7 @@ public class SqlNote {
}
// refresh local info
//loadFromCursor(mId) 方法是一个私有方法,它会查询指定 ID 的笔记记录,并将返回的 Cursor 对象的数据填充到NoteItem 的成员变量中
loadFromCursor(mId);
if (mType == Notes.TYPE_NOTE)
loadDataContent();

@ -35,62 +35,58 @@ import org.json.JSONObject;
public class Task extends Node {
private static final String TAG = Task.class.getSimpleName();
private boolean mCompleted;
private String mNotes;
private JSONObject mMetaInfo;
private Task mPriorSibling;
private TaskList mParent;
private boolean mCompleted; // 声明一个布尔类型的变量,表示任务是否已完成
private String mNotes; // 声明一个字符串类型的变量,表示任务的备注信息
private JSONObject mMetaInfo; // 声明一个JSONObject类型的变量表示任务的元信息
private Task mPriorSibling; // 声明一个Task类型的变量表示当前任务的上一个任务
private TaskList mParent; // 声明一个TaskList类型的变量表示当前任务所属的任务列表
public Task() {
super();
mCompleted = false;
mNotes = null;
mPriorSibling = null;
mParent = null;
mMetaInfo = null;
super(); // 调用父类的无参构造函数
mCompleted = false; // 初始化mCompleted为false
mNotes = null; // 初始化mNotes为null
mPriorSibling = null; // 初始化mPriorSibling为null
mParent = null; // 初始化mParent为null
mMetaInfo = null; // 初始化mMetaInfo为null
}
public JSONObject getCreateAction(int actionId) {
JSONObject js = new JSONObject();
JSONObject js = new JSONObject(); // 声明并初始化一个JSONObject对象
try {
// action_type
js.put(GTaskStringUtils.GTASK_JSON_ACTION_TYPE,
GTaskStringUtils.GTASK_JSON_ACTION_TYPE_CREATE);
GTaskStringUtils.GTASK_JSON_ACTION_TYPE_CREATE); // 将指定的键值对放入JSONObject对象中
// action_id
js.put(GTaskStringUtils.GTASK_JSON_ACTION_ID, actionId);
js.put(GTaskStringUtils.GTASK_JSON_ACTION_ID, actionId); // 将指定的键值对放入JSONObject对象中
// index
js.put(GTaskStringUtils.GTASK_JSON_INDEX, mParent.getChildTaskIndex(this));
js.put(GTaskStringUtils.GTASK_JSON_INDEX, mParent.getChildTaskIndex(this)); // 将指定的键值对放入JSONObject对象中
// entity_delta
JSONObject entity = new JSONObject();
entity.put(GTaskStringUtils.GTASK_JSON_NAME, getName());
entity.put(GTaskStringUtils.GTASK_JSON_CREATOR_ID, "null");
JSONObject entity = new JSONObject(); // 声明并初始化一个JSONObject对象
entity.put(GTaskStringUtils.GTASK_JSON_NAME, getName()); // 将指定的键值对放入JSONObject对象中
entity.put(GTaskStringUtils.GTASK_JSON_CREATOR_ID, "null"); // 将指定的键值对放入JSONObject对象中
entity.put(GTaskStringUtils.GTASK_JSON_ENTITY_TYPE,
GTaskStringUtils.GTASK_JSON_TYPE_TASK);
if (getNotes() != null) {
entity.put(GTaskStringUtils.GTASK_JSON_NOTES, getNotes());
GTaskStringUtils.GTASK_JSON_TYPE_TASK); // 将指定的键值对放入JSONObject对象中
if (getNotes() != null) { // 如果任务备注信息不为空
entity.put(GTaskStringUtils.GTASK_JSON_NOTES, getNotes()); // 将指定的键值对放入JSONObject对象中
}
js.put(GTaskStringUtils.GTASK_JSON_ENTITY_DELTA, entity);
js.put(GTaskStringUtils.GTASK_JSON_ENTITY_DELTA, entity); // 将指定的键值对放入JSONObject对象中
// parent_id
js.put(GTaskStringUtils.GTASK_JSON_PARENT_ID, mParent.getGid());
js.put(GTaskStringUtils.GTASK_JSON_PARENT_ID, mParent.getGid()); // 将指定的键值对放入JSONObject对象中
// dest_parent_type
js.put(GTaskStringUtils.GTASK_JSON_DEST_PARENT_TYPE,
GTaskStringUtils.GTASK_JSON_TYPE_GROUP);
GTaskStringUtils.GTASK_JSON_TYPE_GROUP); // 将指定的键值对放入JSONObject对象中
// list_id
js.put(GTaskStringUtils.GTASK_JSON_LIST_ID, mParent.getGid());
js.put(GTaskStringUtils.GTASK_JSON_LIST_ID, mParent.getGid()); // 将指定的键值对放入JSONObject对象中
// prior_sibling_id
if (mPriorSibling != null) {
if (mPriorSibling != null) { // 如果当前任务的上一个任务不为空
js.put(GTaskStringUtils.GTASK_JSON_PRIOR_SIBLING_ID, mPriorSibling.getGid());
}
@ -102,7 +98,11 @@ public class Task extends Node {
return js;
}
/*getUpdateAction(int actionId)JSONObject
JSONObjectjsaction_typeID action_id
IDJSONObjectentityjs
JSONActionFailureException
*/
public JSONObject getUpdateAction(int actionId) {
JSONObject js = new JSONObject();
@ -134,7 +134,11 @@ public class Task extends Node {
return js;
}
/*setContentByRemoteJSON(JSONObject js)JSONObject
JSONObjectnullJSONObjectidlast_modifiedname
notesdeletedcompleted
JSONActionFailureException
*/
public void setContentByRemoteJSON(JSONObject js) {
if (js != null) {
try {
@ -176,20 +180,24 @@ public class Task extends Node {
}
public void setContentByLocalJSON(JSONObject js) {
// 检查传入的 JSON 对象是否合法
if (js == null || !js.has(GTaskStringUtils.META_HEAD_NOTE)
|| !js.has(GTaskStringUtils.META_HEAD_DATA)) {
Log.w(TAG, "setContentByLocalJSON: nothing is avaiable");
}
try {
// 获取元数据中的笔记和数据部分
JSONObject note = js.getJSONObject(GTaskStringUtils.META_HEAD_NOTE);
JSONArray dataArray = js.getJSONArray(GTaskStringUtils.META_HEAD_DATA);
// 检查笔记类型是否为 Notes.TYPE_NOTE
if (note.getInt(NoteColumns.TYPE) != Notes.TYPE_NOTE) {
Log.e(TAG, "invalid type");
return;
}
// 在数据部分中查找笔记内容
for (int i = 0; i < dataArray.length(); i++) {
JSONObject data = dataArray.getJSONObject(i);
if (TextUtils.equals(data.getString(DataColumns.MIME_TYPE), DataConstants.NOTE)) {
@ -208,12 +216,13 @@ public class Task extends Node {
String name = getName();
try {
if (mMetaInfo == null) {
// new task created from web
// 新任务从网络创建
if (name == null) {
Log.w(TAG, "the note seems to be an empty one");
return null;
}
// 创建 JSON 对象,并设置笔记和数据
JSONObject js = new JSONObject();
JSONObject note = new JSONObject();
JSONArray dataArray = new JSONArray();
@ -225,10 +234,11 @@ public class Task extends Node {
js.put(GTaskStringUtils.META_HEAD_NOTE, note);
return js;
} else {
// synced task
// 已同步的任务
JSONObject note = mMetaInfo.getJSONObject(GTaskStringUtils.META_HEAD_NOTE);
JSONArray dataArray = mMetaInfo.getJSONArray(GTaskStringUtils.META_HEAD_DATA);
// 在数据部分中查找笔记内容,并设置新名称
for (int i = 0; i < dataArray.length(); i++) {
JSONObject data = dataArray.getJSONObject(i);
if (TextUtils.equals(data.getString(DataColumns.MIME_TYPE), DataConstants.NOTE)) {
@ -237,6 +247,7 @@ public class Task extends Node {
}
}
// 设置笔记类型为 Notes.TYPE_NOTE
note.put(NoteColumns.TYPE, Notes.TYPE_NOTE);
return mMetaInfo;
}
@ -246,7 +257,12 @@ public class Task extends Node {
return null;
}
}
/*
MetaData
metaData.getNotes()
metaDatanullmetaData.getNotes()nullJSONObjectmMetaInfo
mMetaInfonull
*/
public void setMetaInfo(MetaData metaData) {
if (metaData != null && metaData.getNotes() != null) {
try {
@ -257,7 +273,12 @@ public class Task extends Node {
}
}
}
/*
Cursor
noteInfonull
mMetaInfonullnoteInfomMetaInfoJSONObject
*/
public int getSyncAction(Cursor c) {
try {
JSONObject noteInfo = null;
@ -310,7 +331,16 @@ public class Task extends Node {
return SYNC_ACTION_ERROR;
}
/*isWorthSaving(): TaskmMetaInfonullnamenotestruefalse
setCompleted(boolean completed): Taskcompleted
setNotes(String notes): Tasknotes
setPriorSibling(Task priorSibling): TaskpriorSibling
setParent(TaskList parent): Taskparent
getCompleted(): Taskcompleted
getNotes(): Tasknotes
getPriorSibling(): TaskpriorSibling
getParent(): Taskparent
*/
public boolean isWorthSaving() {
return mMetaInfo != null || (getName() != null && getName().trim().length() > 0)
|| (getNotes() != null && getNotes().trim().length() > 0);

@ -29,20 +29,26 @@ import org.json.JSONObject;
import java.util.ArrayList;
//定义了一个名为TaskList的类并继承自Node类
public class TaskList extends Node {
//定义了一个名为TAG的静态字符串变量用于在日志中标记该类
private static final String TAG = TaskList.class.getSimpleName();
//定义了一个名为mIndex的整型变量用于表示任务列表的索引
private int mIndex;
//定义了一个名为mChildren的Task类型的ArrayList变量用于存储该任务列表的子任务。
private ArrayList<Task> mChildren;
//定义了一个无参构造函数初始化了mChildren变量和mIndex变量
public TaskList() {
super();
mChildren = new ArrayList<Task>();
mIndex = 1;
}
/* getCreateActionJSONObject
actionIdjsJSONObject
jsaction_typeaction_idindexentity_delta
entity_deltanamecreator_identity_type
jsActionFailureExceptionjs
*/
public JSONObject getCreateAction(int actionId) {
JSONObject js = new JSONObject();
@ -128,7 +134,15 @@ public class TaskList extends Node {
}
}
}
/*setContentByLocalJSON(JSONObject js)JSON
JSONGTaskStringUtils.META_HEAD_NOTE
JSONGTaskStringUtils.META_HEAD_NOTEJSONObject
JSONObjectNoteColumns.TYPENotes.TYPE_FOLDER
NoteColumns.SNIPPETsetName()
Notes.TYPE_SYSTEMNoteColumns.IDNotes.ID_ROOT_FOLDER
Notes.ID_CALL_RECORD_FOLDER
*/
public void setContentByLocalJSON(JSONObject js) {
if (js == null || !js.has(GTaskStringUtils.META_HEAD_NOTE)) {
Log.w(TAG, "setContentByLocalJSON: nothing is avaiable");
@ -156,7 +170,13 @@ public class TaskList extends Node {
e.printStackTrace();
}
}
/*getLocalJSONFromContent()JSONJSONObjectfolder
GTaskStringUtils.MIUI_FOLDER_PREFFIX
NoteColumns.SNIPPET
NoteColumns.TYPENotes.TYPE_SYSTEM
Notes.TYPE_FOLDERfolderMETA_HEAD_NOTEJSONObject
*/
public JSONObject getLocalJSONFromContent() {
try {
JSONObject js = new JSONObject();
@ -182,7 +202,14 @@ public class TaskList extends Node {
return null;
}
}
/*getSyncAction(Cursor c)
IDID
SYNC_ACTION_NONEIDSYNC_ACTION_UPDATE_LOCAL
gtask idIDSYNC_ACTION_ERROR
gtask idIDID
IDSYNC_ACTION_UPDATE_REMOTE
IDSYNC_ACTION_UPDATE_REMOTE
*/
public int getSyncAction(Cursor c) {
try {
if (c.getInt(SqlNote.LOCAL_MODIFIED_COLUMN) == 0) {
@ -215,11 +242,11 @@ public class TaskList extends Node {
return SYNC_ACTION_ERROR;
}
//getChildTaskCount(): 返回子任务的数量。
public int getChildTaskCount() {
return mChildren.size();
}
//addChildTask(Task task): 添加一个子任务。如果任务不为null且不在子任务列表中则添加到列表中并设置前一个兄弟和父任务。
public boolean addChildTask(Task task) {
boolean ret = false;
if (task != null && !mChildren.contains(task)) {
@ -233,7 +260,7 @@ public class TaskList extends Node {
}
return ret;
}
//添加一个子任务到特定的位置。如果位置无效则返回false。如果任务不为null且不在子任务列表中则添加到列表中并设置前一个
public boolean addChildTask(Task task, int index) {
if (index < 0 || index > mChildren.size()) {
Log.e(TAG, "add child task: invalid index");
@ -259,7 +286,7 @@ public class TaskList extends Node {
return true;
}
//从子任务列表中移除一个任务。如果任务不在列表中则返回false。否则从列表中移除任务并重置前一个兄弟和父任务。如果任务后面有其他任务则将它们的前一个兄弟设置为它的前一个兄弟。
public boolean removeChildTask(Task task) {
boolean ret = false;
int index = mChildren.indexOf(task);
@ -280,7 +307,7 @@ public class TaskList extends Node {
}
return ret;
}
// 移动一个任务到一个新的位置。如果索引无效则返回false。如果任务不在列表中则返回false。如果任务已经在目标位置则返回true。否则将任务从当前位置移除并添加到新位置。
public boolean moveChildTask(Task task, int index) {
if (index < 0 || index >= mChildren.size()) {
@ -298,7 +325,7 @@ public class TaskList extends Node {
return true;
return (removeChildTask(task) && addChildTask(task, index));
}
//根据GID查找一个子任务。如果找到则返回该任务否则返回null。
public Task findChildTaskByGid(String gid) {
for (int i = 0; i < mChildren.size(); i++) {
Task t = mChildren.get(i);
@ -308,11 +335,11 @@ public class TaskList extends Node {
}
return null;
}
//获取一个任务的索引。如果任务不在列表中,则返回-1。
public int getChildTaskIndex(Task task) {
return mChildren.indexOf(task);
}
//根据索引获取一个任务。如果索引无效则返回null。
public Task getChildTaskByIndex(int index) {
if (index < 0 || index >= mChildren.size()) {
Log.e(TAG, "getTaskByIndex: invalid index");
@ -320,7 +347,7 @@ public class TaskList extends Node {
}
return mChildren.get(index);
}
//根据GID获取一个任务。如果找到则返回该任务否则返回null。
public Task getChilTaskByGid(String gid) {
for (Task task : mChildren) {
if (task.getGid().equals(gid))
@ -328,15 +355,15 @@ public class TaskList extends Node {
}
return null;
}
//获取子任务列表。
public ArrayList<Task> getChildTaskList() {
return this.mChildren;
}
//设置任务的索引
public void setIndex(int index) {
this.mIndex = index;
}
//设置任务的索引
public int getIndex() {
return this.mIndex;
}

@ -15,7 +15,7 @@
*/
package net.micode.notes.gtask.exception;
//ActionFailureException 继承自 RuntimeException通常用于表示在执行某个操作时发生了异常或错误
public class ActionFailureException extends RuntimeException {
private static final long serialVersionUID = 4425249765923293627L;

@ -15,7 +15,7 @@
*/
package net.micode.notes.gtask.exception;
//NetworkFailureException 继承自 Exception通常用于表示网络相关的异常或错误。
public class NetworkFailureException extends Exception {
private static final long serialVersionUID = 2107610287180234136L;

@ -28,15 +28,15 @@ import net.micode.notes.R;
import net.micode.notes.ui.NotesListActivity;
import net.micode.notes.ui.NotesPreferenceActivity;
//定义了一个类GTaskASyncTask继承自AsyncTask类。AsyncTask是一个抽象类定义了异步任务的处理流程开发者需要实现其抽象方法来处理具体的任务。
public class GTaskASyncTask extends AsyncTask<Void, String, Integer> {
//定义一个静态的通知ID
private static int GTASK_SYNC_NOTIFICATION_ID = 5234235;
//定义一个接口OnCompleteListener包含一个方法onComplete()。在任务完成时调用该接口的onComplete()方法,通知任务执行完毕。
public interface OnCompleteListener {
void onComplete();
}
//定义了一些成员变量,包括上下文、通知管理器、任务管理器和任务完成的监听器。
private Context mContext;
private NotificationManager mNotifiManager;
@ -44,7 +44,7 @@ public class GTaskASyncTask extends AsyncTask<Void, String, Integer> {
private GTaskManager mTaskManager;
private OnCompleteListener mOnCompleteListener;
//构造函数,初始化类中的成员变量。
public GTaskASyncTask(Context context, OnCompleteListener listener) {
mContext = context;
mOnCompleteListener = listener;
@ -52,17 +52,20 @@ public class GTaskASyncTask extends AsyncTask<Void, String, Integer> {
.getSystemService(Context.NOTIFICATION_SERVICE);
mTaskManager = GTaskManager.getInstance();
}
//定义了cancelSync()方法,用于取消同步任务
public void cancelSync() {
mTaskManager.cancelSync();
}
//定义了publishProgess()方法,用于发布同步进度。
public void publishProgess(String message) {
publishProgress(new String[] {
message
});
}
/*showNotification()tickerId
contentNotification
tickerIdPendingIntentNotificationManagernotify()
*/
private void showNotification(int tickerId, String content) {
Notification notification = new Notification(R.drawable.notification, mContext
.getString(tickerId), System.currentTimeMillis());
@ -83,6 +86,7 @@ public class GTaskASyncTask extends AsyncTask<Void, String, Integer> {
}
@Override
//doInBackground() 方法是在新的线程中执行的,主要是用来执行耗时操作。在这里,它调用了 publishProgress() 方法来通知 UI 线程更新进度,然后调用了 mTaskManager.sync() 方法来进行同步操作。
protected Integer doInBackground(Void... unused) {
publishProgess(mContext.getString(R.string.sync_progress_login, NotesPreferenceActivity
.getSyncAccountName(mContext)));
@ -90,6 +94,7 @@ public class GTaskASyncTask extends AsyncTask<Void, String, Integer> {
}
@Override
//onProgressUpdate() 方法在 UI 线程中执行,它用来更新 UI。在这里它调用了 showNotification() 方法来显示通知,并且如果当前的 mContext 对象是 GTaskSyncService 的实例,它还会发送一个广播
protected void onProgressUpdate(String... progress) {
showNotification(R.string.ticker_syncing, progress[0]);
if (mContext instanceof GTaskSyncService) {
@ -98,6 +103,10 @@ public class GTaskASyncTask extends AsyncTask<Void, String, Integer> {
}
@Override
/*onPostExecute() UI 线
mTaskManager.sync() showNotification() mOnCompleteListener null
线 mOnCompleteListener.onComplete()
*/
protected void onPostExecute(Integer result) {
if (result == GTaskManager.STATE_SUCCESS) {
showNotification(R.string.ticker_success, mContext.getString(

@ -64,30 +64,40 @@ import java.util.zip.InflaterInputStream;
public class GTaskClient {
private static final String TAG = GTaskClient.class.getSimpleName();
// Google任务的URL
private static final String GTASK_URL = "https://mail.google.com/tasks/";
// Google任务的获取URL
private static final String GTASK_GET_URL = "https://mail.google.com/tasks/ig";
// Google任务的发布URL
private static final String GTASK_POST_URL = "https://mail.google.com/tasks/r/ig";
private static GTaskClient mInstance = null;
// Http客户端
private DefaultHttpClient mHttpClient;
private String mGetUrl;
private String mPostUrl;
// 客户端版本号
private long mClientVersion;
// 是否已登录
private boolean mLoggedin;
// 上次登录时间
private long mLastLoginTime;
// 操作ID
private int mActionId;
// 账户信息
private Account mAccount;
// 更新任务的JSONArray
private JSONArray mUpdateArray;
private GTaskClient() {
@ -110,14 +120,13 @@ public class GTaskClient {
}
public boolean login(Activity activity) {
// we suppose that the cookie would expire after 5 minutes
// then we need to re-login
// cookie过期时间为5分钟
final long interval = 1000 * 60 * 5;
if (mLastLoginTime + interval < System.currentTimeMillis()) {
mLoggedin = false;
}
// need to re-login after account switch
// 账户切换后需要重新登录
if (mLoggedin
&& !TextUtils.equals(getSyncAccount().name, NotesPreferenceActivity
.getSyncAccountName(activity))) {
@ -129,14 +138,17 @@ public class GTaskClient {
return true;
}
// 记录上次登录时间
mLastLoginTime = System.currentTimeMillis();
// 登录Google账户
String authToken = loginGoogleAccount(activity, false);
if (authToken == null) {
Log.e(TAG, "login google account failed");
return false;
}
// login with custom domain if necessary
// 如果有自定义域名,则使用该域名登录
if (!(mAccount.name.toLowerCase().endsWith("gmail.com") || mAccount.name.toLowerCase()
.endsWith("googlemail.com"))) {
StringBuilder url = new StringBuilder(GTASK_URL).append("a/");
@ -151,7 +163,7 @@ public class GTaskClient {
}
}
// try to login with google official url
// 尝试使用Google官方URL登录
if (!mLoggedin) {
mGetUrl = GTASK_GET_URL;
mPostUrl = GTASK_POST_URL;
@ -159,24 +171,27 @@ public class GTaskClient {
return false;
}
}
mLoggedin = true;
return true;
}
private String loginGoogleAccount(Activity activity, boolean invalidateToken) {
String authToken;
// 从 Google 帐户管理器中获取指定类型的所有帐户
AccountManager accountManager = AccountManager.get(activity);
Account[] accounts = accountManager.getAccountsByType("com.google");
// 如果没有 Google 帐户,则返回 null
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;
@ -185,38 +200,44 @@ public class GTaskClient {
if (account != null) {
mAccount = account;
} else {
// 如果无法获取与应用程序设置中的帐户名称匹配的帐户,则返回 null
Log.e(TAG, "unable to get an account with the same name in the settings");
return null;
}
// get the token now
// 现在获取令牌
AccountManagerFuture<Bundle> 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) {
// 如果无法获取令牌,则返回 null
Log.e(TAG, "get auth token failed");
authToken = null;
}
// 返回令牌字符串
return authToken;
}
// 尝试使用给定的令牌登录到 Gtask
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;
}
// 如果新的令牌也无法登录,则返回 false
if (!loginGtask(authToken)) {
Log.e(TAG, "login gtask failed");
return false;
@ -225,7 +246,9 @@ public class GTaskClient {
return true;
}
// 使用给定的令牌登录到 Gtask
private boolean loginGtask(String authToken) {
// 设置 HTTP 连接和套接字超时时间
int timeoutConnection = 10000;
int timeoutSocket = 15000;
HttpParams httpParameters = new BasicHttpParams();
@ -238,12 +261,14 @@ public class GTaskClient {
// login gtask
try {
// 构造登录URL
String loginUrl = mGetUrl + "?auth=" + authToken;
// 创建一个 HTTP GET 请求
HttpGet httpGet = new HttpGet(loginUrl);
HttpResponse response = null;
response = mHttpClient.execute(httpGet);
// 发送请求并获取响应
HttpResponse response = mHttpClient.execute(httpGet);
// get the cookie now
// 获取响应中的 cookie
List<Cookie> cookies = mHttpClient.getCookieStore().getCookies();
boolean hasAuthCookie = false;
for (Cookie cookie : cookies) {
@ -255,7 +280,7 @@ public class GTaskClient {
Log.w(TAG, "it seems that there is no auth cookie");
}
// get the client version
// 获取客户端版本号
String resString = getResponseContent(response.getEntity());
String jsBegin = "_setup(";
String jsEnd = ")}</script>";
@ -277,20 +302,21 @@ public class GTaskClient {
return false;
}
// 返回值为 true 表示请求成功
return true;
}
//这是一个私有方法返回一个自增的整数值即操作ID。在应用程序中每个HTTP请求都有一个唯一的操作ID用于跟踪该请求。
private int getActionId() {
return mActionId++;
}
//这个方法返回一个HttpPost对象该对象具有两个请求头Content-Type和AT。Content-Type指定请求正文的媒体类型AT是一个自定义请求头表示访问令牌
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响应实体的内容。它首先检查响应实体的内容编码是否为gzip或deflate并相应地创建一个解压缩输入流。然后它使用InputStreamReader和BufferedReader逐行读取响应实体的内容并将其添加到StringBuilder中。最后返回StringBuilder的字符串表示形式
private String getResponseContent(HttpEntity entity) throws IOException {
String contentEncoding = null;
if (entity.getContentEncoding() != null) {
@ -323,23 +349,30 @@ public class GTaskClient {
}
}
/**
* HTTP POSTGoogleAPIJSON
*
* @param js JSONJSONObject
* @return JSONObject
* @throws NetworkFailureException NetworkFailureException
*/
private JSONObject postRequest(JSONObject js) throws NetworkFailureException {
if (!mLoggedin) {
if (!mLoggedin) { // 如果未登录,则抛出异常
Log.e(TAG, "please login first");
throw new ActionFailureException("not logged in");
}
HttpPost httpPost = createHttpPost();
HttpPost httpPost = createHttpPost(); // 创建HttpPost对象
try {
LinkedList<BasicNameValuePair> list = new LinkedList<BasicNameValuePair>();
list.add(new BasicNameValuePair("r", js.toString()));
list.add(new BasicNameValuePair("r", js.toString())); // 将JSON数据作为参数添加到HttpPost对象中
UrlEncodedFormEntity entity = new UrlEncodedFormEntity(list, "UTF-8");
httpPost.setEntity(entity);
httpPost.setEntity(entity); // 设置HttpPost对象的实体
// execute the post
HttpResponse response = mHttpClient.execute(httpPost);
String jsString = getResponseContent(response.getEntity());
return new JSONObject(jsString);
// 执行HttpPost请求
HttpResponse response = mHttpClient.execute(httpPost); // 发送HttpPost请求并接收响应
String jsString = getResponseContent(response.getEntity()); // 获取响应的实体内容
return new JSONObject(jsString); // 将响应的实体内容转换为JSONObject对象并返回
} catch (ClientProtocolException e) {
Log.e(TAG, e.toString());
@ -360,20 +393,26 @@ public class GTaskClient {
}
}
/**
*
*
* @param task
* @throws NetworkFailureException NetworkFailureException
*/
public void createTask(Task task) throws NetworkFailureException {
commitUpdate();
commitUpdate(); // 提交更改
try {
JSONObject jsPost = new JSONObject();
JSONArray actionList = new JSONArray();
// action_list
// 添加“创建任务”操作到操作列表中
actionList.put(task.getCreateAction(getActionId()));
jsPost.put(GTaskStringUtils.GTASK_JSON_ACTION_LIST, actionList);
// client_version
// 将操作列表和客户端版本添加到jsPost对象中
jsPost.put(GTaskStringUtils.GTASK_JSON_ACTION_LIST, actionList);
jsPost.put(GTaskStringUtils.GTASK_JSON_CLIENT_VERSION, mClientVersion);
// post
// 发送HttpPost请求并从响应中获取新任务的ID并将其设置到任务对象中
JSONObject jsResponse = postRequest(jsPost);
JSONObject jsResult = (JSONObject) jsResponse.getJSONArray(
GTaskStringUtils.GTASK_JSON_RESULTS).get(0);
@ -385,10 +424,10 @@ public class GTaskClient {
throw new ActionFailureException("create task: handing jsonobject failed");
}
}
//createTaskList 方法创建一个新的任务列表
public void createTaskList(TaskList tasklist) throws NetworkFailureException {
commitUpdate();
try {
try {//JSONObject 和 JSONArray 是 Java 中的 JSON 对象和数组getActionId() 方法返回任务的 ID。
JSONObject jsPost = new JSONObject();
JSONArray actionList = new JSONArray();
@ -400,21 +439,25 @@ public class GTaskClient {
jsPost.put(GTaskStringUtils.GTASK_JSON_CLIENT_VERSION, mClientVersion);
// post
//postRequest 方法向服务器发送请求并返回响应的 JSON 对象。
JSONObject jsResponse = postRequest(jsPost);
JSONObject jsResult = (JSONObject) jsResponse.getJSONArray(
GTaskStringUtils.GTASK_JSON_RESULTS).get(0);
tasklist.setGid(jsResult.getString(GTaskStringUtils.GTASK_JSON_NEW_ID));
} catch (JSONException e) {
//JSONException 是在处理 JSON 时可能出现的异常。
Log.e(TAG, e.toString());
e.printStackTrace();
throw new ActionFailureException("create tasklist: handing jsonobject failed");
}
}
}//JSONException 是在处理 JSON 时可能出现的异常。
//commitUpdate 方法提交之前对任务列表的修改
public void commitUpdate() throws NetworkFailureException {
if (mUpdateArray != null) {
//if 语句检查是否存在要提交的更新
try {
//mUpdateArray 是一个 JSONArray 对象,包含要提交的更新操作。
JSONObject jsPost = new JSONObject();
// action_list
@ -423,7 +466,7 @@ public class GTaskClient {
// client_version
jsPost.put(GTaskStringUtils.GTASK_JSON_CLIENT_VERSION, mClientVersion);
postRequest(jsPost);
postRequest(jsPost);//postRequest 方法向服务器发送请求并返回响应的 JSON 对象
mUpdateArray = null;
} catch (JSONException e) {
Log.e(TAG, e.toString());
@ -432,13 +475,13 @@ public class GTaskClient {
}
}
}
//addUpdateNode 方法向任务列表中添加一个更新操作。
public void addUpdateNode(Node node) throws NetworkFailureException {
if (node != null) {
// too many update items may result in an error
// set max to 10 items
// set max to 10 itemsmUpdateArray.length() > 10 判断要提交的更新操作数量是否超过 10 个。
if (mUpdateArray != null && mUpdateArray.length() > 10) {
commitUpdate();
commitUpdate();//commitUpdate() 方法提交之前对任务列表的修改。
}
if (mUpdateArray == null)
@ -446,7 +489,7 @@ public class GTaskClient {
mUpdateArray.put(node.getUpdateAction(getActionId()));
}
}
//在 moveTask 方法中,首先调用了 commitUpdate 方法,然后创建了一个 JSON 对象 jsPost它包含一个 JSON 数组 actionList
public void moveTask(Task task, TaskList preParent, TaskList curParent)
throws NetworkFailureException {
commitUpdate();
@ -485,7 +528,7 @@ public class GTaskClient {
throw new ActionFailureException("move task: handing jsonobject failed");
}
}
//在 deleteNode 方法中,也调用了 commitUpdate 方法,然后创建了一个 JSON 对象 jsPost它包含一个 JSON 数组 actionList。然后将 node 对象的 deleted 属性设置为 true并将其 updateAction 添加到 actionList 中。最后,将客户端版本设置为 jsPost 对象的属性,并使用 postRequest 方法将其发送到 Google Tasks API。如果出现任何异常则会抛出 ActionFailureException
public void deleteNode(Node node) throws NetworkFailureException {
commitUpdate();
try {
@ -508,7 +551,7 @@ public class GTaskClient {
throw new ActionFailureException("delete node: handing jsonobject failed");
}
}
//返回一个 JSONArray 对象,表示当前用户在 Google Tasks 上的所有任务列表。如果用户未登录,则抛出一个 ActionFailureException 异常。如果获取任务列表时发生网络故障,则抛出一个 NetworkFailureException 异常。
public JSONArray getTaskLists() throws NetworkFailureException {
if (!mLoggedin) {
Log.e(TAG, "please login first");
@ -546,7 +589,7 @@ public class GTaskClient {
throw new ActionFailureException("get task lists: handing jasonobject failed");
}
}
//返回一个 JSONArray 对象,表示具有给定 ID 的任务列表中的所有任务。该方法将先执行一个 commitUpdate() 方法,该方法的实现未在代码中给出。如果获取任务列表时发生网络故障,则抛出一个 NetworkFailureException 异常。如果在处理 JSON 数据时发生错误,则抛出一个 ActionFailureException 异常。
public JSONArray getTaskList(String listGid) throws NetworkFailureException {
commitUpdate();
try {
@ -574,11 +617,11 @@ public class GTaskClient {
throw new ActionFailureException("get task list: handing jsonobject failed");
}
}
//返回一个 Account 对象,表示当前用户在应用中同步的 Google 帐户。
public Account getSyncAccount() {
return mAccount;
}
// 重置 mUpdateArray 变量,但在代码中未给出 mUpdateArray 的定义。
public void resetUpdateArray() {
mUpdateArray = null;
}

@ -50,41 +50,39 @@ import java.util.Map;
public class GTaskManager {
private static final String TAG = GTaskManager.class.getSimpleName();
//定义了一些实例变量,如 mActivity、mContext、mContentResolver 等,以及一些数据结构,如 mGTaskListHashMap、mGTaskHashMap、mMetaHashMap 等,用于存储同步任务的相关信息。
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;
//GTaskManager 采用单例模式,使用 getInstance() 方法获取实例。
private static GTaskManager mInstance = null;
private Activity mActivity;
private Activity mActivity; // 用于获取身份验证令牌
private Context mContext;
private ContentResolver mContentResolver;
private boolean mSyncing;
private boolean mCancelled;
private boolean mSyncing; // 表示是否正在进行同步
private boolean mCancelled; // 表示同步是否被取消
// 存储 Google Tasks 列表的哈希映射
private HashMap<String, TaskList> mGTaskListHashMap;
// 存储 Google Tasks 节点(任务)的哈希映射
private HashMap<String, Node> mGTaskHashMap;
// 存储 Google Tasks 的元数据的哈希映射
private HashMap<String, MetaData> mMetaHashMap;
private TaskList mMetaList; // 元数据对应的任务列表
private TaskList mMetaList;
// 存储本地已删除的任务ID
private HashSet<Long> mLocalDeleteIdMap;
// 存储 Google Tasks ID 到本地 ID 的哈希映射
private HashMap<String, Long> mGidToNid;
// 存储本地 ID 到 Google Tasks ID 的哈希映射
private HashMap<Long, String> mNidToGid;
private GTaskManager() {
@ -107,19 +105,22 @@ public class GTaskManager {
}
public synchronized void setActivityContext(Activity activity) {
// used for getting authtoken
mActivity = activity;
}
// 同步方法,接收一个上下文对象和 GTaskASyncTask 对象
public int sync(Context context, GTaskASyncTask asyncTask) {
if (mSyncing) {
if (mSyncing) { // 如果正在同步
Log.d(TAG, "Sync is in progress");
return STATE_SYNC_IN_PROGRESS;
return STATE_SYNC_IN_PROGRESS; // 返回同步进行中的状态
}
mContext = context;
mContentResolver = mContext.getContentResolver();
mSyncing = true;
mCancelled = false;
mSyncing = true; // 表示同步开始
mCancelled = false; // 同步未被取消
// 清空所有的哈希映射和集合
mGTaskListHashMap.clear();
mGTaskHashMap.clear();
mMetaHashMap.clear();
@ -128,34 +129,36 @@ public class GTaskManager {
mNidToGid.clear();
try {
// 获取Google任务客户端实例并重置更新数组
GTaskClient client = GTaskClient.getInstance();
client.resetUpdateArray();
// login google task
// 登录Google任务
if (!mCancelled) {
if (!client.login(mActivity)) {
throw new NetworkFailureException("login google task failed");
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;
// 从Google获取任务列表
asyncTask.publishProgess(mContext.getString(R.string.sync_progress_init_list)); // 发布任务进度到UI线程
initGTaskList(); // 初始化Google任务列表
// 同步内容
asyncTask.publishProgess(mContext.getString(R.string.sync_progress_syncing)); // 发布任务进度到UI线程
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 {
// 清空任务列表HashMap、任务HashMap、元数据HashMap、本地删除ID映射、GID和NID映射以及同步标记
mGTaskListHashMap.clear();
mGTaskHashMap.clear();
mMetaHashMap.clear();
@ -165,32 +168,33 @@ public class GTaskManager {
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);
private void initGTaskList() throws NetworkFailureException {
if (mCancelled) // 如果取消标记为true则直接返回
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); // 获取任务列表的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); // 根据JSON数据填充元数据列表
// 加载元数据
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()) {
@ -204,42 +208,43 @@ public class GTaskManager {
}
// 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);
}
// 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);
// init task list
for (int i = 0; i < jsTaskLists.length(); i++) { // 遍历任务列表
JSONObject object = jsTaskLists.getJSONObject(i); // 获取任务列表对象
String gid = object.getString(GTaskStringUtils.GTASK_JSON_ID); // 获取任务列表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); // 从远程JSON设置任务列表内容
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); // 获取任务ID
Task task = new Task(); // 新建任务
task.setContentByRemoteJSON(object); // 从远程JSON设置任务内容
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();
@ -477,65 +482,65 @@ public class GTaskManager {
}
private void doContentSync(int syncType, Node node, Cursor c) throws NetworkFailureException {
if (mCancelled) {
if (mCancelled) { // 如果已经取消,则直接返回
return;
}
MetaData meta;
switch (syncType) {
case Node.SYNC_ACTION_ADD_LOCAL:
MetaData meta; // 定义一个MetaData类型的变量
switch (syncType) { // 根据syncType的值进行不同的操作
case Node.SYNC_ACTION_ADD_LOCAL: // 添加本地节点
addLocalNode(node);
break;
case Node.SYNC_ACTION_ADD_REMOTE:
case Node.SYNC_ACTION_ADD_REMOTE: // 添加远程节点
addRemoteNode(node, c);
break;
case Node.SYNC_ACTION_DEL_LOCAL:
meta = mMetaHashMap.get(c.getString(SqlNote.GTASK_ID_COLUMN));
case Node.SYNC_ACTION_DEL_LOCAL: // 删除本地节点
meta = mMetaHashMap.get(c.getString(SqlNote.GTASK_ID_COLUMN)); // 获取该节点的meta数据
if (meta != null) {
GTaskClient.getInstance().deleteNode(meta);
GTaskClient.getInstance().deleteNode(meta); // 删除该节点的meta数据
}
mLocalDeleteIdMap.add(c.getLong(SqlNote.ID_COLUMN));
mLocalDeleteIdMap.add(c.getLong(SqlNote.ID_COLUMN)); // 添加该节点的ID到本地删除ID Map中
break;
case Node.SYNC_ACTION_DEL_REMOTE:
meta = mMetaHashMap.get(node.getGid());
case Node.SYNC_ACTION_DEL_REMOTE: // 删除远程节点
meta = mMetaHashMap.get(node.getGid()); // 获取该节点的meta数据
if (meta != null) {
GTaskClient.getInstance().deleteNode(meta);
GTaskClient.getInstance().deleteNode(meta); // 删除该节点的meta数据
}
GTaskClient.getInstance().deleteNode(node);
GTaskClient.getInstance().deleteNode(node); // 删除该节点
break;
case Node.SYNC_ACTION_UPDATE_LOCAL:
case Node.SYNC_ACTION_UPDATE_LOCAL: // 更新本地节点
updateLocalNode(node, c);
break;
case Node.SYNC_ACTION_UPDATE_REMOTE:
case Node.SYNC_ACTION_UPDATE_REMOTE: // 更新远程节点
updateRemoteNode(node, c);
break;
case Node.SYNC_ACTION_UPDATE_CONFLICT:
// merging both modifications maybe a good idea
// right now just use local update simply
case Node.SYNC_ACTION_UPDATE_CONFLICT: // 更新冲突节点
// 将两个修改合并可能是个好主意
// 现在只是简单地使用本地更新
updateRemoteNode(node, c);
break;
case Node.SYNC_ACTION_NONE:
case Node.SYNC_ACTION_NONE: // 没有操作
break;
case Node.SYNC_ACTION_ERROR:
case Node.SYNC_ACTION_ERROR: // 操作错误
default:
throw new ActionFailureException("unkown sync action type");
throw new ActionFailureException("unkown sync action type"); // 抛出一个ActionFailureException异常
}
}
private void addLocalNode(Node node) throws NetworkFailureException {
if (mCancelled) {
private void addLocalNode(Node node) throws NetworkFailureException { // 添加本地节点
if (mCancelled) { // 如果已经取消,则直接返回
return;
}
SqlNote sqlNote;
if (node instanceof TaskList) {
if (node.getName().equals(
SqlNote sqlNote; // 定义一个SqlNote类型的变量
if (node instanceof TaskList) { // 如果该节点是任务列表
if (node.getName().equals( // 如果该节点的名称为MIUI_FOLDER_PREFFIX + FOLDER_DEFAULT
GTaskStringUtils.MIUI_FOLDER_PREFFIX + GTaskStringUtils.FOLDER_DEFAULT)) {
sqlNote = new SqlNote(mContext, Notes.ID_ROOT_FOLDER);
} else if (node.getName().equals(
sqlNote = new SqlNote(mContext, Notes.ID_ROOT_FOLDER); // 创建一个根文件夹SqlNote
} else if (node.getName().equals( // 如果该节点的名称为MIUI_FOLDER_PREFFIX + FOLDER_CALL_NOTE
GTaskStringUtils.MIUI_FOLDER_PREFFIX + GTaskStringUtils.FOLDER_CALL_NOTE)) {
sqlNote = new SqlNote(mContext, Notes.ID_CALL_RECORD_FOLDER);
} else {
sqlNote = new SqlNote(mContext, Notes.ID_CALL_RECORD_FOLDER); // 创建一个通话记录文件夹SqlNote
} else { // 否则创建一个普通文件夹
sqlNote = new SqlNote(mContext);
sqlNote.setContent(node.getLocalJSONFromContent());
sqlNote.setParentId(Notes.ID_ROOT_FOLDER);
@ -731,16 +736,24 @@ public class GTaskManager {
}
private void updateRemoteMeta(String gid, SqlNote sqlNote) throws NetworkFailureException {
// 如果 SqlNote 是笔记类型的
if (sqlNote != null && sqlNote.isNoteType()) {
// 获取 gid 对应的 MetaData
MetaData metaData = mMetaHashMap.get(gid);
if (metaData != null) {
// 更新 metaData 中 gid 对应的 meta 值
metaData.setMeta(gid, sqlNote.getContent());
// 将 metaData 加入到更新队列中
GTaskClient.getInstance().addUpdateNode(metaData);
} else {
// 创建一个新的 MetaData
metaData = new MetaData();
metaData.setMeta(gid, sqlNote.getContent());
// 将 metaData 加入到 MetaList 中,等待更新
mMetaList.addChildTask(metaData);
// 将 metaData 加入到 mMetaHashMap 中
mMetaHashMap.put(gid, metaData);
// 在 Google Tasks 中创建一个新任务
GTaskClient.getInstance().createTask(metaData);
}
}
@ -751,7 +764,7 @@ public class GTaskManager {
return;
}
// get the latest gtask list
// 获取最新的 Google 任务列表
mGTaskHashMap.clear();
mGTaskListHashMap.clear();
mMetaHashMap.clear();
@ -759,21 +772,26 @@ 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)
}, NoteColumns.TYPE + " DESC");
if (c != null) {
while (c.moveToNext()) {
// 获取本地笔记的 gid
String gid = c.getString(SqlNote.GTASK_ID_COLUMN);
// 获取 gid 对应的 Google 任务节点
Node node = mGTaskHashMap.get(gid);
if (node != null) {
// 如果 Google 任务中存在该节点,则更新本地笔记的同步 ID
mGTaskHashMap.remove(gid);
ContentValues values = new ContentValues();
values.put(NoteColumns.SYNC_ID, node.getLastModified());
mContentResolver.update(ContentUris.withAppendedId(Notes.CONTENT_NOTE_URI,
c.getLong(SqlNote.ID_COLUMN)), values, null, null);
} else {
// 如果 Google 任务中不存在该节点,则说明有同步问题,抛出异常
Log.e(TAG, "something is missed");
throw new ActionFailureException(
"some local items don't have gid after sync");
@ -791,10 +809,12 @@ public class GTaskManager {
}
public String getSyncAccount() {
// 获取当前同步账户的名称
return GTaskClient.getInstance().getSyncAccount().name;
}
public void cancelSync() {
// 取消同步
mCancelled = true;
}
}

@ -22,8 +22,9 @@ import android.content.Context;
import android.content.Intent;
import android.os.Bundle;
import android.os.IBinder;
//定义一个继承自 Service 的服务类 GTaskSyncService
public class GTaskSyncService extends Service {
//定义一些常量,其中 ACTION_STRING_NAME 是字符串类型的常量表示同步操作的类型ACTION_START_SYNC 和 ACTION_CANCEL_SYNC 是整型常量分别表示开始同步和取消同步操作的类型ACTION_INVALID 表示无效操作的类型。
public final static String ACTION_STRING_NAME = "sync_action_type";
public final static int ACTION_START_SYNC = 0;
@ -37,11 +38,11 @@ public class GTaskSyncService extends Service {
public final static String GTASK_SERVICE_BROADCAST_IS_SYNCING = "isSyncing";
public final static String GTASK_SERVICE_BROADCAST_PROGRESS_MSG = "progressMsg";
//定义静态变量 mSyncTask 和 mSyncProgress分别表示当前的同步任务和同步进度。
private static GTaskASyncTask mSyncTask = null;
private static String mSyncProgress = "";
//定义 startSync 方法,用于开始同步任务。如果当前没有正在运行的同步任务,那么创建一个新的 GTaskASyncTask 异步任务并执行,执行完成后发送广播并停止当前服务。
private void startSync() {
if (mSyncTask == null) {
mSyncTask = new GTaskASyncTask(this, new GTaskASyncTask.OnCompleteListener() {
@ -55,19 +56,20 @@ public class GTaskSyncService extends Service {
mSyncTask.execute();
}
}
//取消正在运行的同步任务
private void cancelSync() {
if (mSyncTask != null) {
mSyncTask.cancelSync();
}
}
//重写 onCreate 方法,在服务创建时将 mSyncTask 置为 null
@Override
public void onCreate() {
mSyncTask = null;
}
@Override
//重写 onStartCommand 方法,处理服务的启动命令。如果启动命令中包含同步操作的类型,
public int onStartCommand(Intent intent, int flags, int startId) {
Bundle bundle = intent.getExtras();
if (bundle != null && bundle.containsKey(ACTION_STRING_NAME)) {
@ -87,16 +89,17 @@ public class GTaskSyncService extends Service {
}
@Override
//onLowMemory() 方法在系统内存不足时被调用,可以在此方法中释放一些占用内存的资源。在这里,如果 mSyncTask 不为空,则调用其 cancelSync() 方法取消同步任务。
public void onLowMemory() {
if (mSyncTask != null) {
mSyncTask.cancelSync();
}
}
//onBind(Intent intent) 方法是 Service 的一个回调方法,用于绑定服务。这个方法的实现在本例中返回 null表示这个 Service 不支持绑定。
public IBinder onBind(Intent intent) {
return null;
}
//sendBroadcast(String msg) 方法将同步进度信息通过广播的形式发送出去。这里将 mSyncProgress 赋值为 msg并创建一个 Intent 对象,设置 action 为 GTASK_SERVICE_BROADCAST_NAME 常量。然后将 mSyncTask 是否为空和 mSyncProgress 信息放到 Intent 中,调用 sendBroadcast() 方法发送广播
public void sendBroadcast(String msg) {
mSyncProgress = msg;
Intent intent = new Intent(GTASK_SERVICE_BROADCAST_NAME);
@ -104,24 +107,24 @@ public class GTaskSyncService extends Service {
intent.putExtra(GTASK_SERVICE_BROADCAST_PROGRESS_MSG, msg);
sendBroadcast(intent);
}
//startSync(Activity activity) 方法用于启动同步任务。首先通过 GTaskManager.getInstance() 获取 GTaskManager 实例,然后设置其 ActivityContext 为传入的 activity 参数。接着创建一个 Intent 对象,设置其目标服务为 GTaskSyncService并将 ACTION_STRING_NAME 常量和 ACTION_START_SYNC 常量放到 Intent 中。最后调用 activity 的 startService() 方法启动服务。
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);
}
//cancelSync(Context context) 方法用于取消同步任务。创建一个 Intent 对象,设置其目标服务为 GTaskSyncService并将 ACTION_STRING_NAME 常量和 ACTION_CANCEL_SYNC 常量放到 Intent 中。最后调用 context 的 startService() 方法启动服务。
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);
}
//isSyncing() 方法用于判断是否正在进行同步任务。直接返回 mSyncTask 是否为空的结果。
public static boolean isSyncing() {
return mSyncTask != null;
}
//isSyncing() 方法用于判断是否正在进行同步任务。直接返回 mSyncTask 是否为空的结果。
public static String getProgressString() {
return mSyncProgress;
}

@ -33,336 +33,358 @@ 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[] {// 声明 DATA_PROJECTION字符串数组
DataColumns.ID,
DataColumns.CONTENT,
DataColumns.MIME_TYPE,
DataColumns.DATA1,
DataColumns.DATA2,
DataColumns.DATA3,
DataColumns.DATA4,
};
public static final String[] NOTE_PROJECTION = new String[] { // 声明 NOTE_PROJECTION字符串数组
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;
// 工作笔记的类定义
// 笔记对象
private Note mNote;
// 笔记ID
private long mNoteId;
// 笔记内容
private String mContent;
// 笔记模式
private int mMode;
// 提醒日期
private long mAlertDate;
// 修改日期
private long mModifiedDate;
// 背景颜色ID
private int mBgColorId;
// 小部件ID
private int mWidgetId;
// 小部件类型
private int mWidgetType;
// 文件夹ID
private long mFolderId;
// 上下文
private Context mContext;
// 删除标记
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
};
// 数据 ID 列
private static final int DATA_ID_COLUMN = 0;
// 数据内容列
private static final int DATA_CONTENT_COLUMN = 1;
// 数据 MIME 类型列
private static final int DATA_MIME_TYPE_COLUMN = 2;
// 数据模式列
private static final int DATA_MODE_COLUMN = 3;
// 笔记父ID列
private static final int NOTE_PARENT_ID_COLUMN = 0;
// 笔记提醒日期列
private static final int NOTE_ALERTED_DATE_COLUMN = 1;
// 笔记背景颜色ID列
private static final int NOTE_BG_COLOR_ID_COLUMN = 2;
// 笔记小部件ID列
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;
// 新建笔记构造函数
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;
}
// 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;
}
// WorkingNote的构造函数
// Existing note construct
private WorkingNote(Context context, long noteId, long folderId) {
mContext = context;
mNoteId = noteId;
mFolderId = folderId;
mIsDeleted = false;
mNote = new Note();
loadNote();
}
// 已有笔记构造函数
private WorkingNote(Context context, long noteId, long folderId) {
mContext = context;
mNoteId = noteId;
mFolderId = folderId;
mIsDeleted = false;
mNote = new Note();
loadNote();
}
private void loadNote() {
Cursor cursor = mContext.getContentResolver().query(
ContentUris.withAppendedId(Notes.CONTENT_NOTE_URI, mNoteId), NOTE_PROJECTION, null,
null, null);
if (cursor != null) {
if (cursor.moveToFirst()) {
mFolderId = cursor.getLong(NOTE_PARENT_ID_COLUMN);
mBgColorId = cursor.getInt(NOTE_BG_COLOR_ID_COLUMN);
mWidgetId = cursor.getInt(NOTE_WIDGET_ID_COLUMN);
mWidgetType = cursor.getInt(NOTE_WIDGET_TYPE_COLUMN);
mAlertDate = cursor.getLong(NOTE_ALERTED_DATE_COLUMN);
mModifiedDate = cursor.getLong(NOTE_MODIFIED_DATE_COLUMN);
}
cursor.close();
} else {
Log.e(TAG, "No note with id:" + mNoteId);
throw new IllegalArgumentException("Unable to find note with id " + mNoteId);
// 载入笔记
private void loadNote() {
Cursor cursor = mContext.getContentResolver().query(
ContentUris.withAppendedId(Notes.CONTENT_NOTE_URI, mNoteId),
NOTE_PROJECTION,
null,
null,
null);
if (cursor != null) {
if (cursor.moveToFirst()) {
mFolderId = cursor.getLong(NOTE_PARENT_ID_COLUMN);
mBgColorId = cursor.getInt(NOTE_BG_COLOR_ID_COLUMN);
mWidgetId = cursor.getInt(NOTE_WIDGET_ID_COLUMN);
mWidgetType = cursor.getInt(NOTE_WIDGET_TYPE_COLUMN);
mAlertDate = cursor.getLong(NOTE_ALERTED_DATE_COLUMN);
mModifiedDate = cursor.getLong(NOTE_MODIFIED_DATE_COLUMN);
}
loadNoteData();
cursor.close();
} else {
Log.e(TAG, "No note with id:" + mNoteId);
throw new IllegalArgumentException("Unable to find note with id " + mNoteId);
}
loadNoteData();
}
private void loadNoteData() {
Cursor cursor = mContext.getContentResolver().query(Notes.CONTENT_DATA_URI, DATA_PROJECTION,
DataColumns.NOTE_ID + "=?", new String[] {
String.valueOf(mNoteId)
}, null);
if (cursor != null) {
if (cursor.moveToFirst()) {
do {
String type = cursor.getString(DATA_MIME_TYPE_COLUMN);
if (DataConstants.NOTE.equals(type)) {
mContent = cursor.getString(DATA_CONTENT_COLUMN);
mMode = cursor.getInt(DATA_MODE_COLUMN);
mNote.setTextDataId(cursor.getLong(DATA_ID_COLUMN));
} else if (DataConstants.CALL_NOTE.equals(type)) {
mNote.setCallDataId(cursor.getLong(DATA_ID_COLUMN));
} else {
Log.d(TAG, "Wrong note type with type:" + type);
}
} while (cursor.moveToNext());
}
cursor.close();
} else {
Log.e(TAG, "No data with id:" + mNoteId);
throw new IllegalArgumentException("Unable to find note's data with id " + mNoteId);
// 载入笔记的数据
private void loadNoteData() {
Cursor cursor = mContext.getContentResolver().query(
Notes.CONTENT_DATA_URI,
DATA_PROJECTION,
DataColumns.NOTE_ID + "=?",
new String[] { String.valueOf(mNoteId) },
null);
if (cursor != null) {
if (cursor.moveToFirst()) {
do {
String type = cursor.getString(DATA_MIME_TYPE_COLUMN);
if (DataConstants.NOTE.equals(type)) {
mContent = cursor.getString(DATA_CONTENT_COLUMN);
mMode = cursor.getInt(DATA_MODE_COLUMN);
mNote.setTextDataId(cursor.getLong(DATA_ID_COLUMN));
} else if (DataConstants.CALL_NOTE.equals(type)) {
mNote.setCallDataId(cursor.getLong(DATA_ID_COLUMN));
} else {
Log.d(TAG, "Wrong note type with type:" + type);
}
} while (cursor.moveToNext());
}
cursor.close();
} else {
Log.e(TAG, "No data with id:" + mNoteId);
throw new IllegalArgumentException("Unable to find note's data with id " + mNoteId);
}
}
public static WorkingNote createEmptyNote(Context context, long folderId, int widgetId,
int widgetType, int defaultBgColorId) {
WorkingNote note = new WorkingNote(context, folderId);
note.setBgColorId(defaultBgColorId);
note.setWidgetId(widgetId);
note.setWidgetType(widgetType);
return note;
}
public static WorkingNote load(Context context, long id) {
return new WorkingNote(context, id, 0);
}
// 创建空笔记
public static WorkingNote createEmptyNote(
Context context,
long folderId,
int widgetId,
int widgetType,
int defaultBgColorId) {
WorkingNote note = new WorkingNote(context, folderId);
note.setBgColorId(defaultBgColorId);
note.setWidgetId(widgetId);
note.setWidgetType(widgetType);
return note;
}
public synchronized boolean saveNote() {
if (isWorthSaving()) {
if (!existInDatabase()) {
if ((mNoteId = Note.getNewNoteId(mContext, mFolderId)) == 0) {
Log.e(TAG, "Create new note fail with id:" + mNoteId);
return false;
}
// 载入笔记
public static WorkingNote load(Context context, long id) {
return new WorkingNote(context, id, 0);
}
}
// 保存笔记
public synchronized boolean saveNote() {
if (isWorthSaving()) {
// 新建笔记
if (!existInDatabase()) {
if ((mNoteId = Note.getNewNoteId(mContext, mFolderId)) == 0) {
Log.e(TAG, "Create new note fail with id:" + mNoteId);
return false;
}
}
// 更新笔记
mNote.syncNote(mContext, mNoteId);
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
&& mNoteSettingStatusListener != null) {
mNoteSettingStatusListener.onWidgetChanged();
}
return true;
} else {
return false;
/**
*
*/
if (mWidgetId != AppWidgetManager.INVALID_APPWIDGET_ID
&& mWidgetType != Notes.TYPE_WIDGET_INVALIDE
&& mNoteSettingStatusListener != null) {
mNoteSettingStatusListener.onWidgetChanged();
}
return true;
} else {
return false;
}
}
public boolean existInDatabase() {
return mNoteId > 0;
// 判断笔记是否已存在于数据库
public boolean existInDatabase() {
return mNoteId > 0;
}
// 判断是否需要保存笔记
private boolean isWorthSaving() {
if (mIsDeleted || (!existInDatabase() && TextUtils.isEmpty(mContent))
|| (existInDatabase() && !mNote.isLocalModified())) {
return false;
} else {
return true;
}
}
private boolean isWorthSaving() {
if (mIsDeleted || (!existInDatabase() && TextUtils.isEmpty(mContent))
|| (existInDatabase() && !mNote.isLocalModified())) {
return false;
} else {
return true;
}
// 设置笔记状态变化的监听器
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 setOnSettingStatusChangedListener(NoteSettingChangedListener l) {
mNoteSettingStatusListener = l;
// 对笔记进行标记
public void markDeleted(boolean mark) {
mIsDeleted = mark;
if (mWidgetId != AppWidgetManager.INVALID_APPWIDGET_ID
&& mWidgetType != Notes.TYPE_WIDGET_INVALIDE && mNoteSettingStatusListener != null) {
mNoteSettingStatusListener.onWidgetChanged();
}
}
public void setAlertDate(long date, boolean set) {
if (date != mAlertDate) {
mAlertDate = date;
mNote.setNoteValue(NoteColumns.ALERTED_DATE, String.valueOf(mAlertDate));
}
// 设置背景颜色
public void setBgColorId(int id) {
if (id != mBgColorId) {
mBgColorId = id;
if (mNoteSettingStatusListener != null) {
mNoteSettingStatusListener.onClockAlertChanged(date, set);
mNoteSettingStatusListener.onBackgroundColorChanged();
}
mNote.setNoteValue(NoteColumns.BG_COLOR_ID, String.valueOf(id));
}
}
public void markDeleted(boolean mark) {
mIsDeleted = mark;
if (mWidgetId != AppWidgetManager.INVALID_APPWIDGET_ID
&& mWidgetType != Notes.TYPE_WIDGET_INVALIDE && mNoteSettingStatusListener != null) {
mNoteSettingStatusListener.onWidgetChanged();
// 设置检查清单
public void setCheckListMode(int mode) {
if (mMode != mode) {
if (mNoteSettingStatusListener != null) {
mNoteSettingStatusListener.onCheckListModeChanged(mMode, mode);
}
mMode = mode;
mNote.setTextData(TextNote.MODE, String.valueOf(mMode));
}
}
public void setBgColorId(int id) {
if (id != mBgColorId) {
mBgColorId = id;
if (mNoteSettingStatusListener != null) {
mNoteSettingStatusListener.onBackgroundColorChanged();
}
mNote.setNoteValue(NoteColumns.BG_COLOR_ID, String.valueOf(id));
}
// 设置部件类型
public void setWidgetType(int type) {
if (type != mWidgetType) {
mWidgetType = type;
mNote.setNoteValue(NoteColumns.WIDGET_TYPE, String.valueOf(mWidgetType));
}
}
public void setCheckListMode(int mode) {
if (mMode != mode) {
if (mNoteSettingStatusListener != null) {
mNoteSettingStatusListener.onCheckListModeChanged(mMode, mode);
}
mMode = mode;
mNote.setTextData(TextNote.MODE, String.valueOf(mMode));
}
// 设置部件ID
public void setWidgetId(int id) {
if (id != mWidgetId) {
mWidgetId = id;
mNote.setNoteValue(NoteColumns.WIDGET_ID, String.valueOf(mWidgetId));
}
}
public void setWidgetType(int type) {
if (type != mWidgetType) {
mWidgetType = type;
mNote.setNoteValue(NoteColumns.WIDGET_TYPE, String.valueOf(mWidgetType));
}
// 设置工作文本
public void setWorkingText(String text) {
if (!TextUtils.equals(mContent, text)) {
mContent = text;
mNote.setTextData(DataColumns.CONTENT, mContent);
}
}
public void setWidgetId(int id) {
if (id != mWidgetId) {
mWidgetId = id;
mNote.setNoteValue(NoteColumns.WIDGET_ID, String.valueOf(mWidgetId));
}
}
// 转换成通话记录
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 void setWorkingText(String text) {
if (!TextUtils.equals(mContent, text)) {
mContent = text;
mNote.setTextData(DataColumns.CONTENT, mContent);
}
}
// 是否包含闹钟提醒
public boolean hasClockAlert() {
return (mAlertDate > 0 ? true : false);
}
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 String getContent() {
return mContent;
}
public boolean hasClockAlert() {
return (mAlertDate > 0 ? true : false);
}
public long getAlertDate() {
return mAlertDate;
}
public String getContent() {
return mContent;
}
public long getModifiedDate() {
return mModifiedDate;
}
public long getAlertDate() {
return mAlertDate;
}
public int getBgColorResId() {
return NoteBgResources.getNoteBgResource(mBgColorId);
}
public long getModifiedDate() {
return mModifiedDate;
}
public int getBgColorId() {
return mBgColorId;
}
public int getBgColorResId() {
return NoteBgResources.getNoteBgResource(mBgColorId);
}
public int getTitleBgResId() {
return NoteBgResources.getNoteTitleBgResource(mBgColorId);
}
public int getBgColorId() {
return mBgColorId;
}
public int getCheckListMode() {
return mMode;
}
public int getTitleBgResId() {
return NoteBgResources.getNoteTitleBgResource(mBgColorId);
}
public long getNoteId() {
return mNoteId;
}
public int getCheckListMode() {
return mMode;
}
public long getFolderId() {
return mFolderId;
}
public long getNoteId() {
return mNoteId;
}
public int getWidgetId() {
return mWidgetId;
}
public long getFolderId() {
return mFolderId;
}
public int getWidgetType() {
return mWidgetType;
}
public int getWidgetId() {
return mWidgetId;
}
// 笔记状态监听器接口
public interface NoteSettingChangedListener {
// 当当前笔记的背景颜色已改变时调用
void onBackgroundColorChanged();
public int getWidgetType() {
return mWidgetType;
}
// 当用户设置闹钟时调用
void onClockAlertChanged(long date, boolean set);
public interface NoteSettingChangedListener {
/**
* Called when the background color of current note has just changed
*/
void onBackgroundColorChanged();
/**
* Called when user set clock
*/
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
*/
void onCheckListModeChanged(int oldMode, int newMode);
}
// 当从部件创建笔记时调用
void onWidgetChanged();
// 当检查清单模式与普通模式之间切换时调用
void onCheckListModeChanged(int oldMode, int newMode);
}
}

@ -36,309 +36,393 @@ import java.io.IOException;
import java.io.PrintStream;
/**
*/
public class BackupUtils {
private static final String TAG = "BackupUtils";
// Singleton stuff
private static BackupUtils sInstance;
public static synchronized BackupUtils getInstance(Context context) {
if (sInstance == null) {
sInstance = new BackupUtils(context);
}
return sInstance;
}
/**
* Following states are signs to represents backup or restore
* status
*/
// Currently, the sdcard is not mounted
public static final int STATE_SD_CARD_UNMOUONTED = 0;
// The backup file not exist
public static final int STATE_BACKUP_FILE_NOT_EXIST = 1;
// The data is not well formated, may be changed by other programs
public static final int STATE_DATA_DESTROIED = 2;
// Some run-time exception which causes restore or backup fails
public static final int STATE_SYSTEM_ERROR = 3;
// Backup or restore success
public static final int STATE_SUCCESS = 4;
private TextExport mTextExport;
private BackupUtils(Context context) {
mTextExport = new TextExport(context);
}
private static boolean externalStorageAvailable() {
return Environment.MEDIA_MOUNTED.equals(Environment.getExternalStorageState());
}
public int exportToText() {
return mTextExport.exportToText();
}
public String getExportedTextFileName() {
return mTextExport.mFileName;
}
public String getExportedTextFileDir() {
return mTextExport.mFileDirectory;
}
private static class TextExport {
private static final String[] NOTE_PROJECTION = {
NoteColumns.ID,
NoteColumns.MODIFIED_DATE,
NoteColumns.SNIPPET,
NoteColumns.TYPE
};
private static final int NOTE_COLUMN_ID = 0;
private static final int NOTE_COLUMN_MODIFIED_DATE = 1;
private static final int NOTE_COLUMN_SNIPPET = 2;
private static final String[] DATA_PROJECTION = {
DataColumns.CONTENT,
DataColumns.MIME_TYPE,
DataColumns.DATA1,
DataColumns.DATA2,
DataColumns.DATA3,
DataColumns.DATA4,
};
private static final int DATA_COLUMN_CONTENT = 0;
private static final int DATA_COLUMN_MIME_TYPE = 1;
private static final int DATA_COLUMN_CALL_DATE = 2;
private static final int DATA_COLUMN_PHONE_NUMBER = 4;
private final String [] TEXT_FORMAT;
private static final int FORMAT_FOLDER_NAME = 0;
private static final int FORMAT_NOTE_DATE = 1;
private static final int FORMAT_NOTE_CONTENT = 2;
private Context mContext;
private String mFileName;
private String mFileDirectory;
public TextExport(Context context) {
TEXT_FORMAT = context.getResources().getStringArray(R.array.format_for_exported_note);
mContext = context;
mFileName = "";
mFileDirectory = "";
}
private String getFormat(int id) {
return TEXT_FORMAT[id];
}
/**
* Export the folder identified by folder id to text
*/
private void exportFolderToText(String folderId, PrintStream ps) {
// Query notes belong to this folder
Cursor notesCursor = mContext.getContentResolver().query(Notes.CONTENT_NOTE_URI,
NOTE_PROJECTION, NoteColumns.PARENT_ID + "=?", new String[] {
folderId
}, null);
if (notesCursor != null) {
if (notesCursor.moveToFirst()) {
do {
// Print note's last modified date
ps.println(String.format(getFormat(FORMAT_NOTE_DATE), DateFormat.format(
mContext.getString(R.string.format_datetime_mdhm),
notesCursor.getLong(NOTE_COLUMN_MODIFIED_DATE))));
// Query data belong to this note
String noteId = notesCursor.getString(NOTE_COLUMN_ID);
exportNoteToText(noteId, ps);
} while (notesCursor.moveToNext());
}
notesCursor.close();
}
}
/**
* Export note identified by id to a print stream
*/
private void exportNoteToText(String noteId, PrintStream ps) {
Cursor dataCursor = mContext.getContentResolver().query(Notes.CONTENT_DATA_URI,
DATA_PROJECTION, DataColumns.NOTE_ID + "=?", new String[] {
noteId
}, null);
if (dataCursor != null) {
if (dataCursor.moveToFirst()) {
do {
String mimeType = dataCursor.getString(DATA_COLUMN_MIME_TYPE);
if (DataConstants.CALL_NOTE.equals(mimeType)) {
// Print phone number
String phoneNumber = dataCursor.getString(DATA_COLUMN_PHONE_NUMBER);
long callDate = dataCursor.getLong(DATA_COLUMN_CALL_DATE);
String location = dataCursor.getString(DATA_COLUMN_CONTENT);
if (!TextUtils.isEmpty(phoneNumber)) {
ps.println(String.format(getFormat(FORMAT_NOTE_CONTENT),
phoneNumber));
}
// Print call date
ps.println(String.format(getFormat(FORMAT_NOTE_CONTENT), DateFormat
.format(mContext.getString(R.string.format_datetime_mdhm),
callDate)));
// Print call attachment location
if (!TextUtils.isEmpty(location)) {
ps.println(String.format(getFormat(FORMAT_NOTE_CONTENT),
location));
}
} else if (DataConstants.NOTE.equals(mimeType)) {
String content = dataCursor.getString(DATA_COLUMN_CONTENT);
if (!TextUtils.isEmpty(content)) {
ps.println(String.format(getFormat(FORMAT_NOTE_CONTENT),
content));
}
}
} while (dataCursor.moveToNext());
}
dataCursor.close();
}
// print a line separator between note
try {
ps.write(new byte[] {
Character.LINE_SEPARATOR, Character.LETTER_NUMBER
});
} catch (IOException e) {
Log.e(TAG, e.toString());
}
}
/**
* Note will be exported as text which is user readable
*/
public int exportToText() {
if (!externalStorageAvailable()) {
Log.d(TAG, "Media was not mounted");
return STATE_SD_CARD_UNMOUONTED;
}
PrintStream ps = getExportToTextPrintStream();
if (ps == null) {
Log.e(TAG, "get print stream error");
return STATE_SYSTEM_ERROR;
}
// First export folder and its notes
Cursor folderCursor = mContext.getContentResolver().query(
Notes.CONTENT_NOTE_URI,
NOTE_PROJECTION,
"(" + NoteColumns.TYPE + "=" + Notes.TYPE_FOLDER + " AND "
+ NoteColumns.PARENT_ID + "<>" + Notes.ID_TRASH_FOLER + ") OR "
+ NoteColumns.ID + "=" + Notes.ID_CALL_RECORD_FOLDER, null, null);
if (folderCursor != null) {
if (folderCursor.moveToFirst()) {
do {
// Print folder's name
String folderName = "";
if(folderCursor.getLong(NOTE_COLUMN_ID) == Notes.ID_CALL_RECORD_FOLDER) {
folderName = mContext.getString(R.string.call_record_folder_name);
} else {
folderName = folderCursor.getString(NOTE_COLUMN_SNIPPET);
}
if (!TextUtils.isEmpty(folderName)) {
ps.println(String.format(getFormat(FORMAT_FOLDER_NAME), folderName));
}
String folderId = folderCursor.getString(NOTE_COLUMN_ID);
exportFolderToText(folderId, ps);
} while (folderCursor.moveToNext());
}
folderCursor.close();
}
// Export notes in root's folder
Cursor noteCursor = mContext.getContentResolver().query(
Notes.CONTENT_NOTE_URI,
NOTE_PROJECTION,
NoteColumns.TYPE + "=" + +Notes.TYPE_NOTE + " AND " + NoteColumns.PARENT_ID
+ "=0", null, null);
if (noteCursor != null) {
if (noteCursor.moveToFirst()) {
do {
ps.println(String.format(getFormat(FORMAT_NOTE_DATE), DateFormat.format(
mContext.getString(R.string.format_datetime_mdhm),
noteCursor.getLong(NOTE_COLUMN_MODIFIED_DATE))));
// Query data belong to this note
String noteId = noteCursor.getString(NOTE_COLUMN_ID);
exportNoteToText(noteId, ps);
} while (noteCursor.moveToNext());
}
noteCursor.close();
}
ps.close();
return STATE_SUCCESS;
}
/**
* Get a print stream pointed to the file {@generateExportedTextFile}
*/
private PrintStream getExportToTextPrintStream() {
File file = generateFileMountedOnSDcard(mContext, R.string.file_path,
R.string.file_name_txt_format);
if (file == null) {
Log.e(TAG, "create file to exported failed");
return null;
}
mFileName = file.getName();
mFileDirectory = mContext.getString(R.string.file_path);
PrintStream ps = null;
try {
FileOutputStream fos = new FileOutputStream(file);
ps = new PrintStream(fos);
} catch (FileNotFoundException e) {
e.printStackTrace();
return null;
} catch (NullPointerException e) {
e.printStackTrace();
return null;
}
return ps;
}
}
/**
* Generate the text file to store imported data
*/
private static File generateFileMountedOnSDcard(Context context, int filePathResId, int fileNameFormatResId) {
StringBuilder sb = new StringBuilder();
sb.append(Environment.getExternalStorageDirectory());
sb.append(context.getString(filePathResId));
File filedir = new File(sb.toString());
sb.append(context.getString(
fileNameFormatResId,
DateFormat.format(context.getString(R.string.format_date_ymd),
System.currentTimeMillis())));
File file = new File(sb.toString());
try {
if (!filedir.exists()) {
filedir.mkdir();
}
if (!file.exists()) {
file.createNewFile();
}
return file;
} catch (SecurityException e) {
e.printStackTrace();
} catch (IOException e) {
e.printStackTrace();
}
return null;
}
// 日志标签
private static final String TAG = "BackupUtils";
// 单例实例
private static BackupUtils sInstance;
/**
@param context
@return
*/
public static synchronized BackupUtils getInstance(Context context) {
if (sInstance == null) {
sInstance = new BackupUtils(context);
}
return sInstance;
}
/**
*/
// 当前SD卡未挂载
public static final int STATE_SD_CARD_UNMOUONTED = 0;
// 备份文件不存在
public static final int STATE_BACKUP_FILE_NOT_EXIST = 1;
// 数据格式不正确,可能被其他程序改变
public static final int STATE_DATA_DESTROIED = 2;
// 运行时异常导致备份或还原失败
public static final int STATE_SYSTEM_ERROR = 3;
// 备份或还原成功
public static final int STATE_SUCCESS = 4;
// 文本导出对象
private TextExport mTextExport;
/**
@param context
*/
private BackupUtils(Context context) {
mTextExport = new TextExport(context);
}
/**
@return
*/
private static boolean externalStorageAvailable() {
return Environment.MEDIA_MOUNTED.equals(Environment.getExternalStorageState());
}
/**
@return
*/
public int exportToText() {
return mTextExport.exportToText();
}
/**
@return
*/
public String getExportedTextFileName() {
return mTextExport.mFileName;
}
/**
@return
*/
public String getExportedTextFileDir() {
return mTextExport.mFileDirectory;
}
}
/**
Note
*/
private static class TextExport {
// 定义查询Note时需要使用的列的名字
private static final String[] NOTE_PROJECTION = {
NoteColumns.ID,
NoteColumns.MODIFIED_DATE,
NoteColumns.SNIPPET,
NoteColumns.TYPE
};
// 定义查询结果中各列的索引
private static final int NOTE_COLUMN_ID = 0;
private static final int NOTE_COLUMN_MODIFIED_DATE = 1;
private static final int NOTE_COLUMN_SNIPPET = 2;
// 定义查询Data时需要使用的列的名字
private static final String[] DATA_PROJECTION = {
DataColumns.CONTENT,
DataColumns.MIME_TYPE,
DataColumns.DATA1,
DataColumns.DATA2,
DataColumns.DATA3,
DataColumns.DATA4,
};
// 定义查询结果中各列的索引
private static final int DATA_COLUMN_CONTENT = 0;
private static final int DATA_COLUMN_MIME_TYPE = 1;
private static final int DATA_COLUMN_CALL_DATE = 2;
private static final int DATA_COLUMN_PHONE_NUMBER = 4;
// 定义文本格式的数组
private final String [] TEXT_FORMAT;
// 相关变量
private Context mContext;
private String mFileName;
private String mFileDirectory;
/**
@param context
*/
public TextExport(Context context) {
// 获取文本格式数组
TEXT_FORMAT = context.getResources().getStringArray(R.array.format_for_exported_note);
mContext = context;
mFileName = "";
mFileDirectory = "";
}
/**
@param id
@return
*/
private String getFormat(int id) {
return TEXT_FORMAT[id];
}
/**
idnote
@param folderId id
@param ps PrintStream
*/
private void exportFolderToText(String folderId, PrintStream ps) {
// 查询属于指定文件夹的note
Cursor notesCursor = mContext.getContentResolver().query(
Notes.CONTENT_NOTE_URI,
NOTE_PROJECTION,
NoteColumns.PARENT_ID + "=?",
new String[] { folderId },
null);
if (notesCursor != null) {
if (notesCursor.moveToFirst()) {
do {
// 输出note的最后修改日期
ps.println(String.format(getFormat(FORMAT_NOTE_DATE), DateFormat.format(
mContext.getString(R.string.format_datetime_mdhm),
notesCursor.getLong(NOTE_COLUMN_MODIFIED_DATE))));
// 查询属于指定note的data
String noteId = notesCursor.getString(NOTE_COLUMN_ID);
exportNoteToText(noteId, ps);
} while (notesCursor.moveToNext());
}
notesCursor.close();
}
}
/**
idnote
@param noteId noteid
@param ps PrintStream
*/
private void exportNoteToText(String noteId, PrintStream ps) {
Cursor dataCursor = mContext.getContentResolver().query(
Notes.CONTENT_DATA_URI,
DATA_PROJECTION,
DataColumns.NOTE_ID + "=?",
new String[] { noteId },
null);
if (dataCursor != null) {
if (dataCursor.moveToFirst()) {
do {
String mimeType = dataCursor.getString(DATA_COLUMN_MIME_TYPE);
if (DataConstants.CALL_NOTE.equals(mimeType)) {
// 如果是Call Note则输出电话号码、通话日期和附件位置
String phoneNumber = dataCursor.getString(DATA_COLUMN_PHONE_NUMBER);
long callDate = dataCursor.getLong(DATA_COLUMN_CALL_DATE);
String location = dataCursor.getString(DATA_COLUMN_CONTENT);
// 如果电话号码不为空,则输出
if (!TextUtils.isEmpty(phoneNumber)) {
ps.println(String.format(getFormat(FORMAT_NOTE_CONTENT),
phoneNumber));
}
// 输出通话日期
ps.println(String.format(getFormat(FORMAT_NOTE_CONTENT), DateFormat
.format(mContext.getString(R.string.format_datetime_mdhm),
callDate)));
// 如果附件位置不为空,则输出
if (!TextUtils.isEmpty(location)) {
ps.println(String.format(getFormat(FORMAT_NOTE_CONTENT),
location));
}
} else if (DataConstants.NOTE.equals(mimeType)) {
// 如果是普通Note则输出内容
String content = dataCursor.getString(DATA_COLUMN_CONTENT);
if (!TextUtils.isEmpty(content)) {
ps.println(String.format(getFormat(FORMAT_NOTE_CONTENT),
content));
}
}
} while (dataCursor.moveToNext());
}
dataCursor.close();
}
// 在不同note之间输出一个空行
try {
ps.write(new byte[] { Character.LINE_SEPARATOR, Character.LETTER_NUMBER });
} catch (IOException e) {
Log.e(TAG, e.toString());
}
}
/**
note
@return
*/
public int exportToText() {
if (!externalStorageAvailable()) {
// 检查外部存储是否挂载,未挂载则返回错误
Log.d(TAG, "Media was not mounted");
return STATE_SD_CARD_UNMOUONTED;
}
// 获取输出流
PrintStream ps = getExportToTextPrintStream();
if (ps == null) {
Log.e(TAG, "get print stream error");
return STATE_SYSTEM_ERROR;
}
// 首先导出各个文件夹及其子note
Cursor folderCursor = mContext.getContentResolver().query(
Notes.CONTENT_NOTE_URI,
NOTE_PROJECTION,
"(" + NoteColumns.TYPE + "=" + Notes.TYPE_FOLDER + " AND "
+ NoteColumns.PARENT_ID + "<>" + Notes.ID_TRASH_FOLER + ") OR "
+ NoteColumns.ID + "=" + Notes.ID_CALL_RECORD_FOLDER,
null,
null);
if (folderCursor != null) {
if (folderCursor.moveToFirst()) {
do {
// 输出文件夹的名称
String folderName = "";
if(folderCursor.getLong(NOTE_COLUMN_ID) == Notes.ID_CALL_RECORD_FOLDER) {
folderName = mContext.getString(R.string.call_record_folder_name);
} else {
folderName = folderCursor.getString(NOTE_COLUMN_SNIPPET);
}
if (!TextUtils.isEmpty(folderName)) {
ps.println(String.format(getFormat(FORMAT_FOLDER_NAME), folderName));
}
String folderId = folderCursor.getString(NOTE_COLUMN_ID);
exportFolderToText(folderId, ps);
} while (folderCursor.moveToNext());
}
folderCursor.close();
}
// 导出Root文件夹中的note
Cursor noteCursor = mContext.getContentResolver().query(
Notes.CONTENT_NOTE_URI,
NOTE_PROJECTION,
NoteColumns.TYPE + "=" + +Notes.TYPE_NOTE + " AND " + NoteColumns.PARENT_ID
+ "=0",
null,
null);
if (noteCursor != null) {
if (noteCursor.moveToFirst()) {
do {
// 输出note的最后修改日期
ps.println(String.format(getFormat(FORMAT_NOTE_DATE), DateFormat.format(
mContext.getString(R.string.format_datetime_mdhm),
noteCursor.getLong(NOTE_COLUMN_MODIFIED_DATE))));
// 查询属于指定note的data
String noteId = noteCursor.getString(NOTE_COLUMN_ID);
exportNoteToText(noteId, ps);
} while (noteCursor.moveToNext());
}
noteCursor.close();
}
ps.close();
return STATE_SUCCESS;
}
/**
SD
@return PrintStream
*/
private PrintStream getExportToTextPrintStream() {
File file = generateFileMountedOnSDcard(
mContext,
R.string.file_path,
R.string.file_name_txt_format);
if (file == null) {
Log.e(TAG, "create file to exported failed");
return null;
}
mFileName = file.getName();
mFileDirectory = mContext.getString(R.string.file_path);
PrintStream ps = null;
try {
FileOutputStream fos = new FileOutputStream(file);
ps = new PrintStream(fos);
} catch (FileNotFoundException e) {
e.printStackTrace();
return null;
} catch (NullPointerException e) {
e.printStackTrace();
return null;
}
return ps;
}
}
/**
*/
private static File generateFileMountedOnSDcard(Context context, int filePathResId, int fileNameFormatResId) {
// 使用StringBuilder的效率更高
StringBuilder sb = new StringBuilder();
// 获取SD卡目录并添加文件路径
sb.append(Environment.getExternalStorageDirectory());
sb.append(context.getString(filePathResId));
// 创建目录
File filedir = new File(sb.toString());
// 添加文件名
sb.append(context.getString(
fileNameFormatResId,
DateFormat.format(context.getString(R.string.format_date_ymd),
System.currentTimeMillis())));
// 创建文件
File file = new File(sb.toString());
try {
// 判断目录是否存在,如不存在则创建该目录
if (!filedir.exists()) {
filedir.mkdir();
}
// 判断文件是否存在,如不存在则创建该文件
if (!file.exists()) {
file.createNewFile();
}
return file;
} catch (SecurityException e) {
e.printStackTrace();
} catch (IOException e) {
e.printStackTrace();
}
return null;
}

@ -36,260 +36,269 @@ import java.util.HashSet;
public class DataUtils {
public static final String TAG = "DataUtils";
public static boolean batchDeleteNotes(ContentResolver resolver, HashSet<Long> ids) {
if (ids == null) {
Log.d(TAG, "the ids is null");
return true;
}
if (ids.size() == 0) {
Log.d(TAG, "no id is in the hashset");
return true;
}
ArrayList<ContentProviderOperation> operationList = new ArrayList<ContentProviderOperation>();
for (long id : ids) {
if(id == Notes.ID_ROOT_FOLDER) {
Log.e(TAG, "Don't delete system folder root");
continue;
}
ContentProviderOperation.Builder builder = ContentProviderOperation
.newDelete(ContentUris.withAppendedId(Notes.CONTENT_NOTE_URI, id));
operationList.add(builder.build());
}
try {
ContentProviderResult[] results = resolver.applyBatch(Notes.AUTHORITY, operationList);
if (results == null || results.length == 0 || results[0] == null) {
Log.d(TAG, "delete notes failed, ids:" + ids.toString());
return false;
}
return true;
} catch (RemoteException e) {
Log.e(TAG, String.format("%s: %s", e.toString(), e.getMessage()));
} catch (OperationApplicationException e) {
Log.e(TAG, String.format("%s: %s", e.toString(), e.getMessage()));
}
return false;
}
public static void moveNoteToFoler(ContentResolver resolver, long id, long srcFolderId, long desFolderId) {
ContentValues values = new ContentValues();
values.put(NoteColumns.PARENT_ID, desFolderId);
values.put(NoteColumns.ORIGIN_PARENT_ID, srcFolderId);
values.put(NoteColumns.LOCAL_MODIFIED, 1);
resolver.update(ContentUris.withAppendedId(Notes.CONTENT_NOTE_URI, id), values, null, null);
}
public static boolean batchMoveToFolder(ContentResolver resolver, HashSet<Long> ids,
long folderId) {
if (ids == null) {
Log.d(TAG, "the ids is null");
return true;
}
ArrayList<ContentProviderOperation> operationList = new ArrayList<ContentProviderOperation>();
for (long id : ids) {
ContentProviderOperation.Builder builder = ContentProviderOperation
.newUpdate(ContentUris.withAppendedId(Notes.CONTENT_NOTE_URI, id));
builder.withValue(NoteColumns.PARENT_ID, folderId);
builder.withValue(NoteColumns.LOCAL_MODIFIED, 1);
operationList.add(builder.build());
}
try {
ContentProviderResult[] results = resolver.applyBatch(Notes.AUTHORITY, operationList);
if (results == null || results.length == 0 || results[0] == null) {
Log.d(TAG, "delete notes failed, ids:" + ids.toString());
return false;
}
return true;
} catch (RemoteException e) {
Log.e(TAG, String.format("%s: %s", e.toString(), e.getMessage()));
} catch (OperationApplicationException e) {
Log.e(TAG, String.format("%s: %s", e.toString(), e.getMessage()));
}
return false;
}
/**
* Get the all folder count except system folders {@link Notes#TYPE_SYSTEM}}
*/
public static int getUserFolderCount(ContentResolver resolver) {
Cursor cursor =resolver.query(Notes.CONTENT_NOTE_URI,
new String[] { "COUNT(*)" },
NoteColumns.TYPE + "=? AND " + NoteColumns.PARENT_ID + "<>?",
new String[] { String.valueOf(Notes.TYPE_FOLDER), String.valueOf(Notes.ID_TRASH_FOLER)},
null);
int count = 0;
if(cursor != null) {
if(cursor.moveToFirst()) {
try {
count = cursor.getInt(0);
} catch (IndexOutOfBoundsException e) {
Log.e(TAG, "get folder count failed:" + e.toString());
} finally {
cursor.close();
}
}
}
return count;
}
public static boolean visibleInNoteDatabase(ContentResolver resolver, long noteId, int type) {
Cursor cursor = resolver.query(ContentUris.withAppendedId(Notes.CONTENT_NOTE_URI, noteId),
null,
NoteColumns.TYPE + "=? AND " + NoteColumns.PARENT_ID + "<>" + Notes.ID_TRASH_FOLER,
new String [] {String.valueOf(type)},
null);
boolean exist = false;
if (cursor != null) {
if (cursor.getCount() > 0) {
exist = true;
}
cursor.close();
}
return exist;
}
public static boolean existInNoteDatabase(ContentResolver resolver, long noteId) {
Cursor cursor = resolver.query(ContentUris.withAppendedId(Notes.CONTENT_NOTE_URI, noteId),
null, null, null, null);
boolean exist = false;
if (cursor != null) {
if (cursor.getCount() > 0) {
exist = true;
}
cursor.close();
}
return exist;
}
public static boolean existInDataDatabase(ContentResolver resolver, long dataId) {
Cursor cursor = resolver.query(ContentUris.withAppendedId(Notes.CONTENT_DATA_URI, dataId),
null, null, null, null);
boolean exist = false;
if (cursor != null) {
if (cursor.getCount() > 0) {
exist = true;
}
cursor.close();
}
return exist;
}
public static boolean checkVisibleFolderName(ContentResolver resolver, String name) {
Cursor cursor = resolver.query(Notes.CONTENT_NOTE_URI, null,
NoteColumns.TYPE + "=" + Notes.TYPE_FOLDER +
" AND " + NoteColumns.PARENT_ID + "<>" + Notes.ID_TRASH_FOLER +
" AND " + NoteColumns.SNIPPET + "=?",
new String[] { name }, null);
boolean exist = false;
if(cursor != null) {
if(cursor.getCount() > 0) {
exist = true;
}
cursor.close();
}
return exist;
}
public static HashSet<AppWidgetAttribute> getFolderNoteWidget(ContentResolver resolver, long folderId) {
Cursor c = resolver.query(Notes.CONTENT_NOTE_URI,
new String[] { NoteColumns.WIDGET_ID, NoteColumns.WIDGET_TYPE },
NoteColumns.PARENT_ID + "=?",
new String[] { String.valueOf(folderId) },
null);
HashSet<AppWidgetAttribute> set = null;
if (c != null) {
if (c.moveToFirst()) {
set = new HashSet<AppWidgetAttribute>();
do {
try {
AppWidgetAttribute widget = new AppWidgetAttribute();
widget.widgetId = c.getInt(0);
widget.widgetType = c.getInt(1);
set.add(widget);
} catch (IndexOutOfBoundsException e) {
Log.e(TAG, e.toString());
}
} while (c.moveToNext());
}
c.close();
}
return set;
}
public static String getCallNumberByNoteId(ContentResolver resolver, long noteId) {
Cursor cursor = resolver.query(Notes.CONTENT_DATA_URI,
new String [] { CallNote.PHONE_NUMBER },
CallNote.NOTE_ID + "=? AND " + CallNote.MIME_TYPE + "=?",
new String [] { String.valueOf(noteId), CallNote.CONTENT_ITEM_TYPE },
null);
if (cursor != null && cursor.moveToFirst()) {
try {
return cursor.getString(0);
} catch (IndexOutOfBoundsException e) {
Log.e(TAG, "Get call number fails " + e.toString());
} finally {
cursor.close();
}
}
return "";
}
public static long getNoteIdByPhoneNumberAndCallDate(ContentResolver resolver, String phoneNumber, long callDate) {
Cursor cursor = resolver.query(Notes.CONTENT_DATA_URI,
new String [] { CallNote.NOTE_ID },
CallNote.CALL_DATE + "=? AND " + CallNote.MIME_TYPE + "=? AND PHONE_NUMBERS_EQUAL("
+ CallNote.PHONE_NUMBER + ",?)",
new String [] { String.valueOf(callDate), CallNote.CONTENT_ITEM_TYPE, phoneNumber },
null);
if (cursor != null) {
if (cursor.moveToFirst()) {
try {
return cursor.getLong(0);
} catch (IndexOutOfBoundsException e) {
Log.e(TAG, "Get call note id fails " + e.toString());
}
}
cursor.close();
}
return 0;
}
public static final String TAG = "DataUtils"; //静态常量,日志标记
//批量删除笔记,返回操作成功与否的布尔值
public static boolean batchDeleteNotes(ContentResolver resolver, HashSet ids) {
if (ids == null) { //如果id集合为空记录日志并返回true
Log.d(TAG, "the ids is null");
return true;
}
if (ids.size() == 0) { //如果集合中没有id记录日志并返回true
Log.d(TAG, "no id is in the hashset");
return true;
}
//创建操作列表
ArrayList operationList = new ArrayList();
//遍历id集合为每个id创建一个对应的删除操作并添加到操作列表中
for (long id : ids) {
if(id == Notes.ID_ROOT_FOLDER) { //如果是系统文件夹根目录,记录日志并跳过
Log.e(TAG, "Don't delete system folder root");
continue;
}
ContentProviderOperation.Builder builder = ContentProviderOperation
.newDelete(ContentUris.withAppendedId(Notes.CONTENT_NOTE_URI, id));
operationList.add(builder.build());
}
try { //调用applyBatch方法执行操作
ContentProviderResult[] results = resolver.applyBatch(Notes.AUTHORITY, operationList);
//检查是否操作成功如不成功记录日志并返回false成功返回true
if (results == null || results.length == 0 || results[0] == null) {
Log.d(TAG, "delete notes failed, ids:" + ids.toString());
return false;
}
return true;
} catch (RemoteException e) { //捕获异常并记录日志返回false
Log.e(TAG, String.format("%s: %s", e.toString(), e.getMessage()));
} catch (OperationApplicationException e) { //同上
Log.e(TAG, String.format("%s: %s", e.toString(), e.getMessage()));
}
return false;
}
//移动某个笔记到指定文件夹
public static void moveNoteToFoler(ContentResolver resolver, long id, long srcFolderId, long desFolderId) {
//设置更新值
ContentValues values = new ContentValues();
values.put(NoteColumns.PARENT_ID, desFolderId);
values.put(NoteColumns.ORIGIN_PARENT_ID, srcFolderId);
values.put(NoteColumns.LOCAL_MODIFIED, 1);
resolver.update(ContentUris.withAppendedId(Notes.CONTENT_NOTE_URI, id), values, null, null);
}
//批量移动笔记到指定文件夹
public static boolean batchMoveToFolder(ContentResolver resolver, HashSet ids, long folderId) {
if (ids == null) { //同上判断id集合是否合法
Log.d(TAG, "the ids is null");
return true;
}
ArrayList operationList = new ArrayList();
//遍历id集合为每个id创建一个对应的更新操作添加到操作列表中
for (long id : ids) {
ContentProviderOperation.Builder builder = ContentProviderOperation
.newUpdate(ContentUris.withAppendedId(Notes.CONTENT_NOTE_URI, id));
builder.withValue(NoteColumns.PARENT_ID, folderId);
builder.withValue(NoteColumns.LOCAL_MODIFIED, 1);
operationList.add(builder.build());
}
try { //同上,批量执行操作,并检查是否成功
ContentProviderResult[] results = resolver.applyBatch(Notes.AUTHORITY, operationList);
if (results == null || results.length == 0 || results[0] == null) {
Log.d(TAG, "delete notes failed, ids:" + ids.toString());
return false;
}
return true;
} catch (RemoteException e) {
Log.e(TAG, String.format("%s: %s", e.toString(), e.getMessage()));
} catch (OperationApplicationException e) {
Log.e(TAG, String.format("%s: %s", e.toString(), e.getMessage()));
}
return false;
}
//获取用户文件夹数量(除系统文件夹)
public static int getUserFolderCount(ContentResolver resolver) {
Cursor cursor =resolver.query(Notes.CONTENT_NOTE_URI,
new String[] { "COUNT(*)" }, //查询总数
NoteColumns.TYPE + "=? AND " + NoteColumns.PARENT_ID + "<>?",
new String[] { String.valueOf(Notes.TYPE_FOLDER), String.valueOf(Notes.ID_TRASH_FOLER)},
null);
int count = 0;
if(cursor != null) { //如果返回的游标不为空
if(cursor.moveToFirst()) {
try {
count = cursor.getInt(0);
} catch (IndexOutOfBoundsException e) {
Log.e(TAG, "get folder count failed:" + e.toString());
} finally {
cursor.close();
}
}
}
return count;
}
//判断指定类型笔记是否在数据库中可见(不在回收站)
public static boolean visibleInNoteDatabase(ContentResolver resolver, long noteId, int type) {
Cursor cursor = resolver.query(ContentUris.withAppendedId(Notes.CONTENT_NOTE_URI, noteId),
null,
NoteColumns.TYPE + "=? AND " + NoteColumns.PARENT_ID + "<>" + Notes.ID_TRASH_FOLER,
new String [] {String.valueOf(type)},
null);
boolean exist = false;
if (cursor != null) {
if (cursor.getCount() > 0) {
exist = true;
}
cursor.close();
}
return exist;
}
//判断指定id的笔记是否存在于数据库中
public static boolean existInNoteDatabase(ContentResolver resolver, long noteId) {
Cursor cursor = resolver.query(ContentUris.withAppendedId(Notes.CONTENT_NOTE_URI, noteId),
null, null, null, null);
boolean exist = false;
if (cursor != null) {
if (cursor.getCount() > 0) {
exist = true;
}
cursor.close();
}
return exist;
}
//判断指定id的数据是否存在于数据库中
public static boolean existInDataDatabase(ContentResolver resolver, long dataId) {
Cursor cursor = resolver.query(ContentUris.withAppendedId(Notes.CONTENT_DATA_URI, dataId),
null, null, null, null);
boolean exist = false;
if (cursor != null) {
if (cursor.getCount() > 0) {
exist = true;
}
cursor.close();
}
return exist;
}
//判断指定文件夹名称是否已存在于已有的文件夹名称中
public static boolean checkVisibleFolderName(ContentResolver resolver, String name) {
Cursor cursor = resolver.query(Notes.CONTENT_NOTE_URI, null,
NoteColumns.TYPE + "=" + Notes.TYPE_FOLDER + " AND " + NoteColumns.PARENT_ID + "<>" + Notes.ID_TRASH_FOLER + " AND " + NoteColumns.SNIPPET + "=?",
new String[] { name }, null);
boolean exist = false;
if(cursor != null) {
if(cursor.getCount() > 0) {
exist = true;
}
cursor.close();
}
return exist;
}
//获取某个文件夹内的所有笔记的小部件属性
public static HashSet getFolderNoteWidget(ContentResolver resolver, long folderId) {
Cursor c = resolver.query(Notes.CONTENT_NOTE_URI,
new String[] { NoteColumns.WIDGET_ID, NoteColumns.WIDGET_TYPE },
NoteColumns.PARENT_ID + "=?",
new String[] { String.valueOf(folderId) },
null);
HashSet set = null;
if (c != null) {
if (c.moveToFirst()) {
set = new HashSet();
do {
try {
AppWidgetAttribute widget = new AppWidgetAttribute();
widget.widgetId = c.getInt(0);
widget.widgetType = c.getInt(1);
set.add(widget);
} catch (IndexOutOfBoundsException e) {
Log.e(TAG, e.toString());
}
} while (c.moveToNext());
}
c.close();
}
return set;
}
// 获取通话笔记中的电话号码
public static String getCallNumberByNoteId(ContentResolver resolver, long noteId) {
// 构建查询参数
Cursor cursor = resolver.query(
Notes.CONTENT_DATA_URI, // 数据URI
new String[]{CallNote.PHONE_NUMBER}, // 返回的列
// 查询条件
CallNote.NOTE_ID + "=? AND " + CallNote.MIME_TYPE + "=?",
// 查询参数
new String[]{String.valueOf(noteId), CallNote.CONTENT_ITEM_TYPE},
null // 排序
);
if (cursor != null && cursor.moveToFirst()) {
try {
return cursor.getString(0); // 返回电话号码
} catch (IndexOutOfBoundsException e) {
Log.e(TAG, "获取电话号码失败 " + e.toString());
} finally {
cursor.close();
}
}
return ""; // 没查到电话号码,返回空字符串
}
public static String getSnippetById(ContentResolver resolver, long noteId) {
Cursor cursor = resolver.query(Notes.CONTENT_NOTE_URI,
new String [] { NoteColumns.SNIPPET },
NoteColumns.ID + "=?",
new String [] { String.valueOf(noteId)},
null);
// 根据电话号码和通话时间获取通话笔记的ID
public static long getNoteIdByPhoneNumberAndCallDate(ContentResolver resolver, String phoneNumber, long callDate) {
// 构建查询参数
Cursor cursor = resolver.query(
Notes.CONTENT_DATA_URI, // 数据URI
new String[]{CallNote.NOTE_ID}, // 返回的列
// 查询条件
CallNote.CALL_DATE + "=? AND " + CallNote.MIME_TYPE + "=? AND PHONE_NUMBERS_EQUAL("
+ CallNote.PHONE_NUMBER + ",?)",
// 查询参数
new String[]{String.valueOf(callDate), CallNote.CONTENT_ITEM_TYPE, phoneNumber},
null // 排序
);
if (cursor != null) {
if (cursor.moveToFirst()) {
try {
return cursor.getLong(0); // 返回通话笔记的ID
} catch (IndexOutOfBoundsException e) {
Log.e(TAG, "获取通话笔记ID失败 " + e.toString());
}
}
cursor.close();
}
return 0; // 没查到通话笔记返回0
}
if (cursor != null) {
String snippet = "";
if (cursor.moveToFirst()) {
snippet = cursor.getString(0);
}
cursor.close();
return snippet;
}
throw new IllegalArgumentException("Note is not found with id: " + noteId);
}
// 根据笔记ID获取笔记片段
public static String getSnippetById(ContentResolver resolver, long noteId) {
// 构建查询参数
Cursor cursor = resolver.query(
Notes.CONTENT_NOTE_URI, // 笔记URI
new String[]{NoteColumns.SNIPPET}, // 返回的列
// 查询条件
NoteColumns.ID + "=?",
// 查询参数
new String[]{String.valueOf(noteId)},
null // 排序
);
if (cursor != null) {
String snippet = "";
if (cursor.moveToFirst()) {
snippet = cursor.getString(0); // 返回笔记片段
}
cursor.close();
return snippet;
}
// 没查到笔记,抛出异常
throw new IllegalArgumentException("未找到ID为" + noteId + "的笔记");
}
public static String getFormattedSnippet(String snippet) {
if (snippet != null) {
snippet = snippet.trim();
int index = snippet.indexOf('\n');
if (index != -1) {
snippet = snippet.substring(0, index);
}
}
return snippet;
}
// 获取格式化后的笔记片段
public static String getFormattedSnippet(String snippet) {
if (snippet != null) {
// 去掉首尾空格
snippet = snippet.trim();
// 找到第一个换行符
int index = snippet.indexOf('\n');
if (index != -1) {
snippet = snippet.substring(0, index); // 截取第一个换行符之前的片段
}
}
return snippet;
}

@ -17,97 +17,63 @@
package net.micode.notes.tool;
public class GTaskStringUtils {
public final static String GTASK_JSON_ACTION_ID = "action_id";
public final static String GTASK_JSON_ACTION_LIST = "action_list";
public final static String GTASK_JSON_ACTION_TYPE = "action_type";
public final static String GTASK_JSON_ACTION_TYPE_CREATE = "create";
public final static String GTASK_JSON_ACTION_TYPE_GETALL = "get_all";
public final static String GTASK_JSON_ACTION_TYPE_MOVE = "move";
public final static String GTASK_JSON_ACTION_TYPE_UPDATE = "update";
public final static String GTASK_JSON_CREATOR_ID = "creator_id";
public final static String GTASK_JSON_CHILD_ENTITY = "child_entity";
public final static String GTASK_JSON_CLIENT_VERSION = "client_version";
public final static String GTASK_JSON_COMPLETED = "completed";
public final static String GTASK_JSON_CURRENT_LIST_ID = "current_list_id";
public final static String GTASK_JSON_DEFAULT_LIST_ID = "default_list_id";
public final static String GTASK_JSON_DELETED = "deleted";
public final static String GTASK_JSON_DEST_LIST = "dest_list";
public final static String GTASK_JSON_DEST_PARENT = "dest_parent";
public final static String GTASK_JSON_DEST_PARENT_TYPE = "dest_parent_type";
public final static String GTASK_JSON_ENTITY_DELTA = "entity_delta";
public final static String GTASK_JSON_ENTITY_TYPE = "entity_type";
public final static String GTASK_JSON_GET_DELETED = "get_deleted";
public final static String GTASK_JSON_ID = "id";
public final static String GTASK_JSON_INDEX = "index";
public final static String GTASK_JSON_LAST_MODIFIED = "last_modified";
public final static String GTASK_JSON_LATEST_SYNC_POINT = "latest_sync_point";
public final static String GTASK_JSON_LIST_ID = "list_id";
public final static String GTASK_JSON_LISTS = "lists";
public final static String GTASK_JSON_NAME = "name";
public final static String GTASK_JSON_NEW_ID = "new_id";
public final static String GTASK_JSON_NOTES = "notes";
public final static String GTASK_JSON_PARENT_ID = "parent_id";
public final static String GTASK_JSON_PRIOR_SIBLING_ID = "prior_sibling_id";
public final static String GTASK_JSON_RESULTS = "results";
public final static String GTASK_JSON_SOURCE_LIST = "source_list";
public final static String GTASK_JSON_TASKS = "tasks";
public final static String GTASK_JSON_TYPE = "type";
public final static String GTASK_JSON_TYPE_GROUP = "GROUP";
public final static String GTASK_JSON_TYPE_TASK = "TASK";
public final static String GTASK_JSON_USER = "user";
public final static String MIUI_FOLDER_PREFFIX = "[MIUI_Notes]";
public final static String FOLDER_DEFAULT = "Default";
public final static String FOLDER_CALL_NOTE = "Call_Note";
public final static String FOLDER_META = "METADATA";
public final static String META_HEAD_GTASK_ID = "meta_gid";
public final static String META_HEAD_NOTE = "meta_note";
public final static String META_HEAD_DATA = "meta_data";
public final static String META_NOTE_NAME = "[META INFO] DON'T UPDATE AND DELETE";
}
// Google Tasks JSON中的键
public final static String GTASK_JSON_ACTION_ID = "action_id"; // 动作id
public final static String GTASK_JSON_ACTION_LIST = "action_list"; // 动作列表
public final static String GTASK_JSON_ACTION_TYPE = "action_type"; // 动作类型
public final static String GTASK_JSON_ACTION_TYPE_CREATE = "create"; // 创建动作
public final static String GTASK_JSON_ACTION_TYPE_GETALL = "get_all"; // 获取所有动作
public final static String GTASK_JSON_ACTION_TYPE_MOVE = "move"; // 移动动作
public final static String GTASK_JSON_ACTION_TYPE_UPDATE = "update"; // 更新动作
public final static String GTASK_JSON_CREATOR_ID = "creator_id"; // 创建者id
public final static String GTASK_JSON_CHILD_ENTITY = "child_entity"; // 子实体
public final static String GTASK_JSON_CLIENT_VERSION = "client_version"; // 客户端版本
public final static String GTASK_JSON_COMPLETED = "completed"; // 完成标志
public final static String GTASK_JSON_CURRENT_LIST_ID = "current_list_id"; // 当前列表id
public final static String GTASK_JSON_DEFAULT_LIST_ID = "default_list_id"; // 默认列表id
public final static String GTASK_JSON_DELETED = "deleted"; // 删除标志
public final static String GTASK_JSON_DEST_LIST = "dest_list"; // 目标列表
public final static String GTASK_JSON_DEST_PARENT = "dest_parent"; // 目标父节点
public final static String GTASK_JSON_DEST_PARENT_TYPE = "dest_parent_type"; // 目标父节点类型
public final static String GTASK_JSON_ENTITY_DELTA = "entity_delta"; // 实体差异
public final static String GTASK_JSON_ENTITY_TYPE = "entity_type"; // 实体类型
public final static String GTASK_JSON_GET_DELETED = "get_deleted"; // 获取删除的实体
public final static String GTASK_JSON_ID = "id"; // 实体id
public final static String GTASK_JSON_INDEX = "index"; // 序号
public final static String GTASK_JSON_LAST_MODIFIED = "last_modified"; // 最后修改时间
public final static String GTASK_JSON_LATEST_SYNC_POINT = "latest_sync_point"; // 最新同步时间点
public final static String GTASK_JSON_LIST_ID = "list_id"; // 列表id
public final static String GTASK_JSON_LISTS = "lists"; // 列表
public final static String GTASK_JSON_NAME = "name"; // 名称
public final static String GTASK_JSON_NEW_ID = "new_id"; // 新的实体id
public final static String GTASK_JSON_NOTES = "notes"; // 备注
public final static String GTASK_JSON_PARENT_ID = "parent_id"; // 父节点id
public final static String GTASK_JSON_PRIOR_SIBLING_ID = "prior_sibling_id"; // 前置兄弟节点id
public final static String GTASK_JSON_RESULTS = "results"; // 结果
public final static String GTASK_JSON_SOURCE_LIST = "source_list"; // 源列表
public final static String GTASK_JSON_TASKS = "tasks"; // 任务
public final static String GTASK_JSON_TYPE = "type"; // 类型
public final static String GTASK_JSON_TYPE_GROUP = "GROUP"; // 分组类型
public final static String GTASK_JSON_TYPE_TASK = "TASK"; // 任务类型
public final static String GTASK_JSON_USER = "user"; // 用户
// MIUI备忘录文件夹前缀
public final static String MIUI_FOLDER_PREFFIX = "[MIUI_Notes]";
// 默认文件夹名
public final static String FOLDER_DEFAULT = "Default";
// 呼叫备忘录文件夹名
public final static String FOLDER_CALL_NOTE = "Call_Note";
// 元数据文件夹名
public final static String FOLDER_META = "METADATA";
// 元数据键名
public final static String META_HEAD_GTASK_ID = "meta_gid"; // Google Tasks id
public final static String META_HEAD_NOTE = "meta_note"; // 备注
public final static String META_HEAD_DATA = "meta_data"; // 数据
// 元数据备注名
public final static String META_NOTE_NAME = "[META INFO] DON'T UPDATE AND DELETE"; // 不要更新和删除的元数据备注
}

@ -22,160 +22,200 @@ import android.preference.PreferenceManager;
import net.micode.notes.R;
import net.micode.notes.ui.NotesPreferenceActivity;
/**
*/
public class ResourceParser {
public static final int YELLOW = 0;
public static final int BLUE = 1;
public static final int WHITE = 2;
public static final int GREEN = 3;
public static final int RED = 4;
public static final int BG_DEFAULT_COLOR = YELLOW;
public static final int TEXT_SMALL = 0;
public static final int TEXT_MEDIUM = 1;
public static final int TEXT_LARGE = 2;
public static final int TEXT_SUPER = 3;
public static final int BG_DEFAULT_FONT_SIZE = TEXT_MEDIUM;
public static class NoteBgResources {
private final static int [] BG_EDIT_RESOURCES = new int [] {
R.drawable.edit_yellow,
R.drawable.edit_blue,
R.drawable.edit_white,
R.drawable.edit_green,
R.drawable.edit_red
};
private final static int [] BG_EDIT_TITLE_RESOURCES = new int [] {
R.drawable.edit_title_yellow,
R.drawable.edit_title_blue,
R.drawable.edit_title_white,
R.drawable.edit_title_green,
R.drawable.edit_title_red
};
public static int getNoteBgResource(int id) {
return BG_EDIT_RESOURCES[id];
}
public static int getNoteTitleBgResource(int id) {
return BG_EDIT_TITLE_RESOURCES[id];
}
}
public static int getDefaultBgId(Context context) {
if (PreferenceManager.getDefaultSharedPreferences(context).getBoolean(
NotesPreferenceActivity.PREFERENCE_SET_BG_COLOR_KEY, false)) {
return (int) (Math.random() * NoteBgResources.BG_EDIT_RESOURCES.length);
} else {
return BG_DEFAULT_COLOR;
}
}
public static class NoteItemBgResources {
private final static int [] BG_FIRST_RESOURCES = new int [] {
R.drawable.list_yellow_up,
R.drawable.list_blue_up,
R.drawable.list_white_up,
R.drawable.list_green_up,
R.drawable.list_red_up
};
private final static int [] BG_NORMAL_RESOURCES = new int [] {
R.drawable.list_yellow_middle,
R.drawable.list_blue_middle,
R.drawable.list_white_middle,
R.drawable.list_green_middle,
R.drawable.list_red_middle
};
private final static int [] BG_LAST_RESOURCES = new int [] {
R.drawable.list_yellow_down,
R.drawable.list_blue_down,
R.drawable.list_white_down,
R.drawable.list_green_down,
R.drawable.list_red_down,
};
private final static int [] BG_SINGLE_RESOURCES = new int [] {
R.drawable.list_yellow_single,
R.drawable.list_blue_single,
R.drawable.list_white_single,
R.drawable.list_green_single,
R.drawable.list_red_single
};
public static int getNoteBgFirstRes(int id) {
return BG_FIRST_RESOURCES[id];
}
public static int getNoteBgLastRes(int id) {
return BG_LAST_RESOURCES[id];
}
public static int getNoteBgSingleRes(int id) {
return BG_SINGLE_RESOURCES[id];
}
public static int getNoteBgNormalRes(int id) {
return BG_NORMAL_RESOURCES[id];
}
public static int getFolderBgRes() {
return R.drawable.list_folder;
}
}
public static class WidgetBgResources {
private final static int [] BG_2X_RESOURCES = new int [] {
R.drawable.widget_2x_yellow,
R.drawable.widget_2x_blue,
R.drawable.widget_2x_white,
R.drawable.widget_2x_green,
R.drawable.widget_2x_red,
};
public static int getWidget2xBgResource(int id) {
return BG_2X_RESOURCES[id];
}
private final static int [] BG_4X_RESOURCES = new int [] {
R.drawable.widget_4x_yellow,
R.drawable.widget_4x_blue,
R.drawable.widget_4x_white,
R.drawable.widget_4x_green,
R.drawable.widget_4x_red
};
public static int getWidget4xBgResource(int id) {
return BG_4X_RESOURCES[id];
}
}
public static class TextAppearanceResources {
private final static int [] TEXTAPPEARANCE_RESOURCES = new int [] {
R.style.TextAppearanceNormal,
R.style.TextAppearanceMedium,
R.style.TextAppearanceLarge,
R.style.TextAppearanceSuper
};
public static int getTexAppearanceResource(int id) {
/**
* HACKME: Fix bug of store the resource id in shared preference.
* The id may larger than the length of resources, in this case,
* return the {@link ResourceParser#BG_DEFAULT_FONT_SIZE}
*/
if (id >= TEXTAPPEARANCE_RESOURCES.length) {
return BG_DEFAULT_FONT_SIZE;
}
return TEXTAPPEARANCE_RESOURCES[id];
}
public static int getResourcesSize() {
return TEXTAPPEARANCE_RESOURCES.length;
}
}
//定义颜色常量
public static final int YELLOW = 0;
public static final int BLUE = 1;
public static final int WHITE = 2;
public static final int GREEN = 3;
public static final int RED = 4;
//设置默认背景颜色和字体大小
public static final int BG_DEFAULT_COLOR = YELLOW;
public static final int TEXT_SMALL = 0;
public static final int TEXT_MEDIUM = 1;
public static final int TEXT_LARGE = 2;
public static final int TEXT_SUPER = 3;
public static final int BG_DEFAULT_FONT_SIZE = TEXT_MEDIUM;
/**
*/
public static class NoteBgResources {
//编辑背景资源
private final static int [] BG_EDIT_RESOURCES = new int [] {
R.drawable.edit_yellow,
R.drawable.edit_blue,
R.drawable.edit_white,
R.drawable.edit_green,
R.drawable.edit_red
};
//编辑标题背景资源
private final static int [] BG_EDIT_TITLE_RESOURCES = new int [] {
R.drawable.edit_title_yellow,
R.drawable.edit_title_blue,
R.drawable.edit_title_white,
R.drawable.edit_title_green,
R.drawable.edit_title_red
};
//获取背景资源
public static int getNoteBgResource(int id) {
return BG_EDIT_RESOURCES[id];
}
//获取标题背景资源
public static int getNoteTitleBgResource(int id) {
return BG_EDIT_TITLE_RESOURCES[id];
}
}
/**
*/
public static class NoteItemBgResources {
//列表项第一个背景资源
private final static int [] BG_FIRST_RESOURCES = new int [] {
R.drawable.list_yellow_up,
R.drawable.list_blue_up,
R.drawable.list_white_up,
R.drawable.list_green_up,
R.drawable.list_red_up
};
//列表项中间背景资源
private final static int [] BG_NORMAL_RESOURCES = new int [] {
R.drawable.list_yellow_middle,
R.drawable.list_blue_middle,
R.drawable.list_white_middle,
R.drawable.list_green_middle,
R.drawable.list_red_middle
};
//列表项最后一个背景资源
private final static int [] BG_LAST_RESOURCES = new int [] {
R.drawable.list_yellow_down,
R.drawable.list_blue_down,
R.drawable.list_white_down,
R.drawable.list_green_down,
R.drawable.list_red_down
};
//单选列表项背景资源
private final static int [] BG_SINGLE_RESOURCES = new int [] {
R.drawable.list_yellow_single,
R.drawable.list_blue_single,
R.drawable.list_white_single,
R.drawable.list_green_single,
R.drawable.list_red_single
};
//获取列表项第一个背景资源
public static int getNoteBgFirstRes(int id) {
return BG_FIRST_RESOURCES[id];
}
//获取列表项最后一个背景资源
public static int getNoteBgLastRes(int id) {
return BG_LAST_RESOURCES[id];
}
//获取单选列表项背景资源
public static int getNoteBgSingleRes(int id) {
return BG_SINGLE_RESOURCES[id];
}
//获取列表项中间背景资源
public static int getNoteBgNormalRes(int id) {
return BG_NORMAL_RESOURCES[id];
}
//获取文件夹背景资源
public static int getFolderBgRes() {
return R.drawable.list_folder;
}
}
/**
*/
public static class WidgetBgResources {
//2x桌面小部件背景资源
private final static int [] BG_2X_RESOURCES = new int [] {
R.drawable.widget_2x_yellow,
R.drawable.widget_2x_blue,
R.drawable.widget_2x_white,
R.drawable.widget_2x_green,
R.drawable.widget_2x_red
};
//获取2x桌面小部件背景资源
public static int getWidget2xBgResource(int id) {
return BG_2X_RESOURCES[id];
}
//4x桌面小部件背景资源
private final static int [] BG_4X_RESOURCES = new int [] {
R.drawable.widget_4x_yellow,
R.drawable.widget_4x_blue,
R.drawable.widget_4x_white,
R.drawable.widget_4x_green,
R.drawable.widget_4x_red
};
//获取4x桌面小部件背景资源
public static int getWidget4xBgResource(int id) {
return BG_4X_RESOURCES[id];
}
}
/**
*/
public static class TextAppearanceResources {
//文字外观资源
private final static int [] TEXTAPPEARANCE_RESOURCES = new int [] {
R.style.TextAppearanceNormal,
R.style.TextAppearanceMedium,
R.style.TextAppearanceLarge,
R.style.TextAppearanceSuper
};
//获取文字外观资源
public static int getTexAppearanceResource(int id) {
/**
* HACKME: id
* id{@link ResourceParser#BG_DEFAULT_FONT_SIZE}
*/
if (id >= TEXTAPPEARANCE_RESOURCES.length) {
return BG_DEFAULT_FONT_SIZE;
}
return TEXTAPPEARANCE_RESOURCES[id];
}
//获取资源大小
public static int getResourcesSize() {
return TEXTAPPEARANCE_RESOURCES.length;
}
}
}
Loading…
Cancel
Save