Compare commits

..

No commits in common. 'master' and 'hzy_branch' have entirely different histories.

@ -26,16 +26,9 @@ import android.util.Log;
import java.util.HashMap;
public class Contact {
// 静态缓存,用于存储已查询过的联系人信息,避免重复查询数据库
private static HashMap<String, String> sContactCache;
// 日志标签,用于调试和记录日志信息
private static final String TAG = "Contact";
// 查询联系人的SQL语句模板
// 该语句用于根据电话号码查询联系人名称
// PHONE_NUMBERS_EQUAL 是一个辅助函数,用于比较电话号码是否匹配
// Data.MIMETYPE 用于指定查询的数据类型为电话号码
// Data.RAW_CONTACT_ID 用于关联 phone_lookup 表,确保查询结果的准确性
private static final String CALLER_ID_SELECTION = "PHONE_NUMBERS_EQUAL(" + Phone.NUMBER
+ ",?) AND " + Data.MIMETYPE + "='" + Phone.CONTENT_ITEM_TYPE + "'"
+ " AND " + Data.RAW_CONTACT_ID + " IN "
@ -43,57 +36,36 @@ public class Contact {
+ " FROM phone_lookup"
+ " WHERE min_match = '+')";
/**
*
*
* @param context
* @param phoneNumber
* @return null
*/
public static String getContact(Context context, String phoneNumber) {
// 如果缓存为空,则初始化缓存
if(sContactCache == null) {
sContactCache = new HashMap<String, String>();
}
// 检查缓存中是否已经存在该电话号码的联系人信息
if(sContactCache.containsKey(phoneNumber)) {
// 如果缓存中有,直接返回缓存中的联系人名称
return sContactCache.get(phoneNumber);
}
// 替换 SQL 查询语句中的占位符为实际的电话号码
// PhoneNumberUtils.toCallerIDMinMatch 用于将电话号码转换为适合查询的格式
String selection = CALLER_ID_SELECTION.replace("+",
PhoneNumberUtils.toCallerIDMinMatch(phoneNumber));
// 查询联系人数据库
Cursor cursor = context.getContentResolver().query(
Data.CONTENT_URI, // 查询的 URI
new String [] { Phone.DISPLAY_NAME }, // 查询返回的列,这里只需要联系人名称
selection, // 查询条件
new String[] { phoneNumber }, // 查询参数
null); // 不需要排序
Data.CONTENT_URI,
new String [] { Phone.DISPLAY_NAME },
selection,
new String[] { phoneNumber },
null);
// 检查查询结果
if (cursor != null && cursor.moveToFirst()) {
try {
// 获取查询结果中的联系人名称
String name = cursor.getString(0);
// 将查询结果存入缓存
sContactCache.put(phoneNumber, name);
// 返回联系人名称
return name;
} catch (IndexOutOfBoundsException e) {
// 如果发生异常,记录错误日志
Log.e(TAG, " Cursor get string error " + e.toString());
return null;
} finally {
// 关闭游标,释放资源
cursor.close();
}
} else {
// 如果没有找到匹配的联系人,记录调试日志
Log.d(TAG, "No contact matched with number:" + phoneNumber);
return null;
}

@ -16,471 +16,264 @@
package net.micode.notes.data;
import android.content.ContentResolver;
import android.content.ContentUris;
import android.content.ContentValues;
import android.database.Cursor;
import android.net.Uri;
import android.provider.BaseColumns;
import android.text.TextUtils;
/**
* 便
* 便URI
* 便
*/
public class Notes {
// 内容提供者的URI前缀
public static final String AUTHORITY = "net.micode.notes";
// 内容提供者的完整URI
public static final Uri CONTENT_URI = Uri.parse("content://" + AUTHORITY);
public static final String AUTHORITY = "micode_notes";
public static final String TAG = "Notes";
public static final int TYPE_NOTE = 0;
public static final int TYPE_FOLDER = 1;
public static final int TYPE_SYSTEM = 2;
/**
* 便
* Following IDs are system folders' identifiers
* {@link Notes#ID_ROOT_FOLDER } is default folder
* {@link Notes#ID_TEMPARAY_FOLDER } is for notes belonging no folder
* {@link Notes#ID_CALL_RECORD_FOLDER} is to store call records
*/
public interface NoteColumns extends BaseColumns {
// 便签ID
public static final String ID = BaseColumns._ID;
// 父文件夹ID
public static final String PARENT_ID = "parent_id";
// 提醒日期时间戳
public static final String ALERTED_DATE = "alerted_date";
// 便签背景颜色ID
public static final String BG_COLOR_ID = "bg_color_id";
// 创建日期
public static final String CREATED_DATE = "created_date";
// 是否有附件
public static final String HAS_ATTACHMENT = "has_attachment";
// 修改日期
public static final String MODIFIED_DATE = "modified_date";
// 便签数量(文件夹才有)
public static final String NOTES_COUNT = "notes_count";
// 便签摘要
public static final String SNIPPET = "snippet";
// 便签类型
public static final String TYPE = "type";
// 桌面小部件ID
public static final String WIDGET_ID = "widget_id";
// 桌面小部件类型
public static final String WIDGET_TYPE = "widget_type";
// 同步ID
public static final String SYNC_ID = "sync_id";
// 本地修改标记
public static final String LOCAL_MODIFIED = "local_modified";
// 原始父文件夹ID
public static final String ORIGIN_PARENT_ID = "origin_parent_id";
// Google任务ID
public static final String GTASK_ID = "gtask_id";
// 版本号
public static final String VERSION = "version";
public static final int ID_ROOT_FOLDER = 0;
public static final int ID_TEMPARAY_FOLDER = -1;
public static final int ID_CALL_RECORD_FOLDER = -2;
public static final int ID_TRASH_FOLER = -3;
public static final String INTENT_EXTRA_ALERT_DATE = "net.micode.notes.alert_date";
public static final String INTENT_EXTRA_BACKGROUND_ID = "net.micode.notes.background_color_id";
public static final String INTENT_EXTRA_WIDGET_ID = "net.micode.notes.widget_id";
public static final String INTENT_EXTRA_WIDGET_TYPE = "net.micode.notes.widget_type";
public static final String INTENT_EXTRA_FOLDER_ID = "net.micode.notes.folder_id";
public static final String INTENT_EXTRA_CALL_DATE = "net.micode.notes.call_date";
public static final int TYPE_WIDGET_INVALIDE = -1;
public static final int TYPE_WIDGET_2X = 0;
public static final int TYPE_WIDGET_4X = 1;
public static class DataConstants {
public static final String NOTE = TextNote.CONTENT_ITEM_TYPE;
public static final String CALL_NOTE = CallNote.CONTENT_ITEM_TYPE;
}
/**
* 便
* Uri to query all notes and folders
*/
public interface DataColumns extends BaseColumns {
// 数据ID
public static final String ID = BaseColumns._ID;
// MIME类型标识数据类型
public static final String MIME_TYPE = "mime_type";
// 关联的便签ID
public static final String NOTE_ID = "note_id";
// 创建日期
public static final String CREATED_DATE = "created_date";
// 修改日期
public static final String MODIFIED_DATE = "modified_date";
// 内容文本
public static final String CONTENT = "content";
// 附加数据1可存储电话号码等
public static final String DATA1 = "data1";
// 附加数据2
public static final String DATA2 = "data2";
// 附加数据3可存储链接地址等
public static final String DATA3 = "data3";
// 附加数据4
public static final String DATA4 = "data4";
// 附加数据5
public static final String DATA5 = "data5";
}
public static final Uri CONTENT_NOTE_URI = Uri.parse("content://" + AUTHORITY + "/note");
/**
* 便
* Uri to query data
*/
public interface TypeColumns {
// 普通便签类型
public static final int TYPE_NOTE = 0;
// 文件夹类型
public static final int TYPE_FOLDER = 1;
// 系统文件夹类型
public static final int TYPE_SYSTEM = 2;
}
public static final Uri CONTENT_DATA_URI = Uri.parse("content://" + AUTHORITY + "/data");
/**
* MIME
*/
public interface DataConstants {
// 文本便签MIME类型
public static final String NOTE = "vnd.android.cursor.item/vnd.micode.note";
// 通话记录MIME类型
public static final String CALL_RECORD = "vnd.android.cursor.item/vnd.micode.note.call";
// 提醒MIME类型
public static final String ALERT = "vnd.android.cursor.item/vnd.micode.note.alert";
// 链接MIME类型
public static final String URL = "vnd.android.cursor.item/vnd.micode.note.url";
// 图片MIME类型
public static final String IMAGE = "vnd.android.cursor.item/vnd.micode.note.image";
// 音频MIME类型
public static final String AUDIO = "vnd.android.cursor.item/vnd.micode.note.audio";
}
public interface NoteColumns {
/**
* The unique ID for a row
* <P> Type: INTEGER (long) </P>
*/
public static final String ID = "_id";
/**
* ID
*/
public interface FolderConstants {
// 通话记录文件夹ID
public static final long ID_CALL_RECORD_FOLDER = 1;
// 根文件夹ID
public static final long ID_ROOT_FOLDER = 2;
// 临时文件夹ID
public static final long ID_TEMPARAY_FOLDER = 3;
// 回收站文件夹ID
public static final long ID_TRASH_FOLER = 4;
}
/**
* The parent's id for note or folder
* <P> Type: INTEGER (long) </P>
*/
public static final String PARENT_ID = "parent_id";
/**
*
*/
public interface WidgetConstants {
// 无小部件
public static final int WIDGET_TYPE_NONE = -1;
// 4x2小部件
public static final int WIDGET_TYPE_INFORMATION = 0;
// 4x4小部件
public static final int WIDGET_TYPE_BIG = 1;
// 1x1小部件
public static final int WIDGET_TYPE_MINI = 2;
}
/**
* Created data for note or folder
* <P> Type: INTEGER (long) </P>
*/
public static final String CREATED_DATE = "created_date";
/**
* Latest modified date
* <P> Type: INTEGER (long) </P>
*/
public static final String MODIFIED_DATE = "modified_date";
/**
* 便URI
*/
public static final class NoteContent implements NoteColumns {
public static final String CONTENT_DIRECTORY = "notes";
public static final Uri CONTENT_URI = Uri.withAppendedPath(Notes.CONTENT_URI, CONTENT_DIRECTORY);
public static final String CONTENT_TYPE = "vnd.android.cursor.dir/vnd.micode.note";
public static final String CONTENT_ITEM_TYPE = "vnd.android.cursor.item/vnd.micode.note";
/**
* 便URI
* Alert date
* <P> Type: INTEGER (long) </P>
*/
public static Uri buildUri(long id) {
return ContentUris.withAppendedId(CONTENT_URI, id);
}
public static final String ALERTED_DATE = "alert_date";
/**
* URI便ID
* Folder's name or text content of note
* <P> Type: TEXT </P>
*/
public static long getId(Uri uri) {
return ContentUris.parseId(uri);
}
}
public static final String SNIPPET = "snippet";
/**
* 便URI
*/
public static final class DataContent implements DataColumns {
public static final String CONTENT_DIRECTORY = "data";
public static final Uri CONTENT_URI = Uri.withAppendedPath(Notes.CONTENT_URI, CONTENT_DIRECTORY);
public static final String CONTENT_TYPE = "vnd.android.cursor.dir/vnd.micode.note.data";
public static final String CONTENT_ITEM_TYPE = "vnd.android.cursor.item/vnd.micode.note.data";
/**
* Note's widget id
* <P> Type: INTEGER (long) </P>
*/
public static final String WIDGET_ID = "widget_id";
/**
* URI
* Note's widget type
* <P> Type: INTEGER (long) </P>
*/
public static Uri buildUri(long id) {
return ContentUris.withAppendedId(CONTENT_URI, id);
}
public static final String WIDGET_TYPE = "widget_type";
/**
* URIID
* Note's background color's id
* <P> Type: INTEGER (long) </P>
*/
public static long getId(Uri uri) {
return ContentUris.parseId(uri);
}
public static final String BG_COLOR_ID = "bg_color_id";
/**
* 便URI
* For text note, it doesn't has attachment, for multi-media
* note, it has at least one attachment
* <P> Type: INTEGER </P>
*/
public static Uri buildDataDirUri(long noteId) {
return Uri.withAppendedPath(buildUri(noteId), CONTENT_DIRECTORY);
}
public static final String HAS_ATTACHMENT = "has_attachment";
/**
* URI
* Folder's count of notes
* <P> Type: INTEGER (long) </P>
*/
public static Uri buildDataUri(long noteId, String mimeType) {
return Uri.withAppendedPath(buildDataDirUri(noteId), mimeType);
}
}
public static final String NOTES_COUNT = "notes_count";
/**
* URI
*/
public static final class FolderContent implements NoteColumns {
public static final String CONTENT_DIRECTORY = "folders";
public static final Uri CONTENT_URI = Uri.withAppendedPath(Notes.CONTENT_URI, CONTENT_DIRECTORY);
public static final String CONTENT_TYPE = "vnd.android.cursor.dir/vnd.micode.note.folder";
public static final String CONTENT_ITEM_TYPE = "vnd.android.cursor.item/vnd.micode.note.folder";
/**
* The file type: folder or note
* <P> Type: INTEGER </P>
*/
public static final String TYPE = "type";
/**
* The last sync id
* <P> Type: INTEGER (long) </P>
*/
public static final String SYNC_ID = "sync_id";
/**
* Sign to indicate local modified or not
* <P> Type: INTEGER </P>
*/
public static final String LOCAL_MODIFIED = "local_modified";
/**
* Original parent id before moving into temporary folder
* <P> Type : INTEGER </P>
*/
public static final String ORIGIN_PARENT_ID = "origin_parent_id";
/**
* URI
* The gtask id
* <P> Type : TEXT </P>
*/
public static Uri buildUri(long id) {
return ContentUris.withAppendedId(CONTENT_URI, id);
}
public static final String GTASK_ID = "gtask_id";
/**
* URIID
* The version code
* <P> Type : INTEGER (long) </P>
*/
public static long getId(Uri uri) {
return ContentUris.parseId(uri);
}
public static final String VERSION = "version";
}
/**
* 便
*/
public static class Utils {
public interface DataColumns {
/**
* 便
* The unique ID for a row
* <P> Type: INTEGER (long) </P>
*/
public static long createNewNote(ContentResolver resolver, long parentId, String snippet) {
ContentValues values = new ContentValues();
values.put(NoteColumns.PARENT_ID, parentId);
values.put(NoteColumns.SNIPPET, snippet);
values.put(NoteColumns.TYPE, TypeColumns.TYPE_NOTE);
Uri uri = resolver.insert(NoteContent.CONTENT_URI, values);
if (uri != null) {
return ContentUris.parseId(uri);
}
return -1;
}
public static final String ID = "_id";
/**
*
* The MIME type of the item represented by this row.
* <P> Type: Text </P>
*/
public static long createNewFolder(ContentResolver resolver, long parentId, String name) {
ContentValues values = new ContentValues();
values.put(NoteColumns.PARENT_ID, parentId);
values.put(NoteColumns.SNIPPET, name);
values.put(NoteColumns.TYPE, TypeColumns.TYPE_FOLDER);
Uri uri = resolver.insert(NoteContent.CONTENT_URI, values);
if (uri != null) {
return ContentUris.parseId(uri);
}
return -1;
}
public static final String MIME_TYPE = "mime_type";
/**
* 便
* The reference id to note that this data belongs to
* <P> Type: INTEGER (long) </P>
*/
public static long addTextNote(ContentResolver resolver, long noteId, String content) {
ContentValues values = new ContentValues();
values.put(DataColumns.NOTE_ID, noteId);
values.put(DataColumns.MIME_TYPE, DataConstants.NOTE);
values.put(DataColumns.CONTENT, content);
Uri uri = resolver.insert(DataContent.CONTENT_URI, values);
if (uri != null) {
return ContentUris.parseId(uri);
}
return -1;
}
public static final String NOTE_ID = "note_id";
/**
* 便
* Created data for note or folder
* <P> Type: INTEGER (long) </P>
*/
public static long addAlert(ContentResolver resolver, long noteId, long alertDate) {
ContentValues values = new ContentValues();
values.put(DataColumns.NOTE_ID, noteId);
values.put(DataColumns.MIME_TYPE, DataConstants.ALERT);
values.put(DataColumns.DATA1, alertDate);
Uri uri = resolver.insert(DataContent.CONTENT_URI, values);
if (uri != null) {
return ContentUris.parseId(uri);
}
return -1;
}
public static final String CREATED_DATE = "created_date";
/**
* 便
* Latest modified date
* <P> Type: INTEGER (long) </P>
*/
public static long addUrl(ContentResolver resolver, long noteId, String url, String title) {
ContentValues values = new ContentValues();
values.put(DataColumns.NOTE_ID, noteId);
values.put(DataColumns.MIME_TYPE, DataConstants.URL);
values.put(DataColumns.DATA1, url);
values.put(DataColumns.CONTENT, title);
Uri uri = resolver.insert(DataContent.CONTENT_URI, values);
if (uri != null) {
return ContentUris.parseId(uri);
}
return -1;
}
public static final String MODIFIED_DATE = "modified_date";
/**
* 便
* Data's content
* <P> Type: TEXT </P>
*/
public static long addCallRecord(ContentResolver resolver, long noteId, String phoneNumber, String callTime) {
ContentValues values = new ContentValues();
values.put(DataColumns.NOTE_ID, noteId);
values.put(DataColumns.MIME_TYPE, DataConstants.CALL_RECORD);
values.put(DataColumns.DATA1, phoneNumber);
values.put(DataColumns.CONTENT, callTime);
Uri uri = resolver.insert(DataContent.CONTENT_URI, values);
if (uri != null) {
return ContentUris.parseId(uri);
}
return -1;
}
public static final String CONTENT = "content";
/**
*
* Generic data column, the meaning is {@link #MIMETYPE} specific, used for
* integer data type
* <P> Type: INTEGER </P>
*/
public static boolean isSystemFolder(long folderId) {
return folderId == FolderConstants.ID_CALL_RECORD_FOLDER ||
folderId == FolderConstants.ID_ROOT_FOLDER ||
folderId == FolderConstants.ID_TEMPARAY_FOLDER ||
folderId == FolderConstants.ID_TRASH_FOLER;
}
public static final String DATA1 = "data1";
/**
*
* Generic data column, the meaning is {@link #MIMETYPE} specific, used for
* integer data type
* <P> Type: INTEGER </P>
*/
public static boolean isTrashFolder(long folderId) {
return folderId == FolderConstants.ID_TRASH_FOLER;
}
public static final String DATA2 = "data2";
/**
*
* Generic data column, the meaning is {@link #MIMETYPE} specific, used for
* TEXT data type
* <P> Type: TEXT </P>
*/
public static boolean isCallRecordFolder(long folderId) {
return folderId == FolderConstants.ID_CALL_RECORD_FOLDER;
}
public static final String DATA3 = "data3";
/**
* 便
* Generic data column, the meaning is {@link #MIMETYPE} specific, used for
* TEXT data type
* <P> Type: TEXT </P>
*/
public static boolean hasAlert(ContentResolver resolver, long noteId) {
String[] projection = new String[] { DataColumns.ID };
String selection = DataColumns.NOTE_ID + " = ? AND " +
DataColumns.MIME_TYPE + " = ?";
String[] selectionArgs = new String[] {
String.valueOf(noteId),
DataConstants.ALERT
};
Cursor cursor = resolver.query(
DataContent.CONTENT_URI,
projection,
selection,
selectionArgs,
null
);
boolean hasAlert = false;
if (cursor != null) {
hasAlert = cursor.getCount() > 0;
cursor.close();
}
return hasAlert;
}
public static final String DATA4 = "data4";
/**
* 便
* Generic data column, the meaning is {@link #MIMETYPE} specific, used for
* TEXT data type
* <P> Type: TEXT </P>
*/
public static long getAlertDate(ContentResolver resolver, long noteId) {
String[] projection = new String[] { DataColumns.DATA1 };
String selection = DataColumns.NOTE_ID + " = ? AND " +
DataColumns.MIME_TYPE + " = ?";
String[] selectionArgs = new String[] {
String.valueOf(noteId),
DataConstants.ALERT
};
Cursor cursor = resolver.query(
DataContent.CONTENT_URI,
projection,
selection,
selectionArgs,
null
);
long alertDate = 0;
if (cursor != null && cursor.moveToFirst()) {
alertDate = cursor.getLong(0);
cursor.close();
}
return alertDate;
}
public static final String DATA5 = "data5";
}
public static final class TextNote implements DataColumns {
/**
* 便
* Mode to indicate the text in check list mode or not
* <P> Type: Integer 1:check list mode 0: normal mode </P>
*/
public static boolean hasUrl(ContentResolver resolver, long noteId) {
String[] projection = new String[] { DataColumns.ID };
String selection = DataColumns.NOTE_ID + " = ? AND " +
DataColumns.MIME_TYPE + " = ?";
String[] selectionArgs = new String[] {
String.valueOf(noteId),
DataConstants.URL
};
Cursor cursor = resolver.query(
DataContent.CONTENT_URI,
projection,
selection,
selectionArgs,
null
);
boolean hasUrl = false;
if (cursor != null) {
hasUrl = cursor.getCount() > 0;
cursor.close();
}
return hasUrl;
}
public static final String MODE = DATA1;
public static final int MODE_CHECK_LIST = 1;
public static final String CONTENT_TYPE = "vnd.android.cursor.dir/text_note";
public static final String CONTENT_ITEM_TYPE = "vnd.android.cursor.item/text_note";
public static final Uri CONTENT_URI = Uri.parse("content://" + AUTHORITY + "/text_note");
}
public static final class CallNote implements DataColumns {
/**
* 便
* Call date for this record
* <P> Type: INTEGER (long) </P>
*/
public static boolean hasCallRecord(ContentResolver resolver, long noteId) {
String[] projection = new String[] { DataColumns.ID };
String selection = DataColumns.NOTE_ID + " = ? AND " +
DataColumns.MIME_TYPE + " = ?";
String[] selectionArgs = new String[] {
String.valueOf(noteId),
DataConstants.CALL_RECORD
};
Cursor cursor = resolver.query(
DataContent.CONTENT_URI,
projection,
selection,
selectionArgs,
null
);
boolean hasCallRecord = false;
if (cursor != null) {
hasCallRecord = cursor.getCount() > 0;
cursor.close();
}
return hasCallRecord;
}
public static final String CALL_DATE = DATA1;
/**
* Phone number for this record
* <P> Type: TEXT </P>
*/
public static final String PHONE_NUMBER = DATA3;
public static final String CONTENT_TYPE = "vnd.android.cursor.dir/call_note";
public static final String CONTENT_ITEM_TYPE = "vnd.android.cursor.item/call_note";
public static final Uri CONTENT_URI = Uri.parse("content://" + AUTHORITY + "/call_note");
}
}

@ -26,39 +26,27 @@ import net.micode.notes.data.Notes.DataColumns;
import net.micode.notes.data.Notes.DataConstants;
import net.micode.notes.data.Notes.NoteColumns;
/**
* 便
* 便
* 便
*/
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;
/**
* 便SQL
* 便IDID
*/
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.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," +
@ -75,32 +63,27 @@ public class NotesDatabaseHelper extends SQLiteOpenHelper {
NoteColumns.VERSION + " INTEGER NOT NULL DEFAULT 0" +
")";
/**
* 便SQL
* 便
*/
private static final String CREATE_DATA_TABLE_SQL =
"CREATE TABLE " + TABLE.DATA + "(" +
DataColumns.ID + " INTEGER PRIMARY KEY," +
DataColumns.MIME_TYPE + " TEXT NOT NULL," + // 数据类型字段,用于区分不同功能的数据
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," + // 附加数据1可用于存储电话号码等
DataColumns.DATA2 + " INTEGER," + // 附加数据2
DataColumns.DATA3 + " TEXT NOT NULL DEFAULT ''," + // 附加数据3可用于存储链接地址等
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 ''" +
")";
// 创建便签ID索引的SQL语句提高查询效率
private static final String CREATE_DATA_NOTE_ID_INDEX_SQL =
"CREATE INDEX IF NOT EXISTS note_id_index ON " +
TABLE.DATA + "(" + DataColumns.NOTE_ID + ");";
/**
* 便便
* Increase folder's note count when move note to the folder
*/
private static final String NOTE_INCREASE_FOLDER_COUNT_ON_UPDATE_TRIGGER =
"CREATE TRIGGER increase_folder_count_on_update "+
@ -112,7 +95,7 @@ public class NotesDatabaseHelper extends SQLiteOpenHelper {
" END";
/**
* 便便
* Decrease folder's note count when move note from folder
*/
private static final String NOTE_DECREASE_FOLDER_COUNT_ON_UPDATE_TRIGGER =
"CREATE TRIGGER decrease_folder_count_on_update " +
@ -125,7 +108,7 @@ public class NotesDatabaseHelper extends SQLiteOpenHelper {
" END";
/**
* 便便
* Increase folder's note count when insert new note to the folder
*/
private static final String NOTE_INCREASE_FOLDER_COUNT_ON_INSERT_TRIGGER =
"CREATE TRIGGER increase_folder_count_on_insert " +
@ -137,7 +120,7 @@ public class NotesDatabaseHelper extends SQLiteOpenHelper {
" END";
/**
* 便便
* Decrease folder's note count when delete note from the folder
*/
private static final String NOTE_DECREASE_FOLDER_COUNT_ON_DELETE_TRIGGER =
"CREATE TRIGGER decrease_folder_count_on_delete " +
@ -150,7 +133,7 @@ public class NotesDatabaseHelper extends SQLiteOpenHelper {
" END";
/**
* 便DataConstants.NOTE便
* 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 " +
@ -163,7 +146,7 @@ public class NotesDatabaseHelper extends SQLiteOpenHelper {
" END";
/**
* 便DataConstants.NOTE便
* 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 " +
@ -176,7 +159,7 @@ public class NotesDatabaseHelper extends SQLiteOpenHelper {
" END";
/**
* 便DataConstants.NOTE便
* 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 " +
@ -189,7 +172,7 @@ public class NotesDatabaseHelper extends SQLiteOpenHelper {
" END";
/**
* 便便
* Delete datas belong to note which has been deleted
*/
private static final String NOTE_DELETE_DATA_ON_DELETE_TRIGGER =
"CREATE TRIGGER delete_data_on_delete " +
@ -200,7 +183,7 @@ public class NotesDatabaseHelper extends SQLiteOpenHelper {
" END";
/**
* 便
* Delete notes belong to folder which has been deleted
*/
private static final String FOLDER_DELETE_NOTES_ON_DELETE_TRIGGER =
"CREATE TRIGGER folder_delete_notes_on_delete " +
@ -211,7 +194,7 @@ public class NotesDatabaseHelper extends SQLiteOpenHelper {
" END";
/**
* 便
* Move notes belong to folder which has been moved to trash folder
*/
private static final String FOLDER_MOVE_NOTES_ON_TRASH_TRIGGER =
"CREATE TRIGGER folder_move_notes_on_trash " +
@ -223,16 +206,10 @@ public class NotesDatabaseHelper extends SQLiteOpenHelper {
" 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);
@ -240,9 +217,6 @@ public class NotesDatabaseHelper extends SQLiteOpenHelper {
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");
@ -261,23 +235,18 @@ public class NotesDatabaseHelper extends SQLiteOpenHelper {
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);
@ -285,7 +254,7 @@ public class NotesDatabaseHelper extends SQLiteOpenHelper {
db.insert(TABLE.NOTE, null, values);
/**
* - 便
* temporary folder which is used for moving note
*/
values.clear();
values.put(NoteColumns.ID, Notes.ID_TEMPARAY_FOLDER);
@ -293,7 +262,7 @@ public class NotesDatabaseHelper extends SQLiteOpenHelper {
db.insert(TABLE.NOTE, null, values);
/**
*
* create trash folder
*/
values.clear();
values.put(NoteColumns.ID, Notes.ID_TRASH_FOLER);
@ -301,9 +270,6 @@ public class NotesDatabaseHelper extends SQLiteOpenHelper {
db.insert(TABLE.NOTE, null, values);
}
/**
* 便
*/
public void createDataTable(SQLiteDatabase db) {
db.execSQL(CREATE_DATA_TABLE_SQL);
reCreateDataTableTriggers(db);
@ -311,9 +277,6 @@ public class NotesDatabaseHelper extends SQLiteOpenHelper {
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");
@ -324,9 +287,6 @@ public class NotesDatabaseHelper extends SQLiteOpenHelper {
db.execSQL(DATA_UPDATE_NOTE_CONTENT_ON_DELETE_TRIGGER);
}
/**
*
*/
static synchronized NotesDatabaseHelper getInstance(Context context) {
if (mInstance == null) {
mInstance = new NotesDatabaseHelper(context);
@ -334,60 +294,45 @@ public class NotesDatabaseHelper extends SQLiteOpenHelper {
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;
// 升级到版本2
if (oldVersion == 1) {
upgradeToV2(db);
skipV2 = true; // this upgrade including the upgrade from v2 to v3
oldVersion++;
}
// 升级到版本3
if (oldVersion == 2 && !skipV2) {
upgradeToV3(db);
reCreateTriggers = true;
oldVersion++;
}
// 升级到版本4
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");
}
}
/**
* 2
*
*/
private void upgradeToV2(SQLiteDatabase db) {
db.execSQL("DROP TABLE IF EXISTS " + TABLE.NOTE);
db.execSQL("DROP TABLE IF EXISTS " + TABLE.DATA);
@ -395,33 +340,21 @@ public class NotesDatabaseHelper extends SQLiteOpenHelper {
createDataTable(db);
}
/**
* 3
* 1.
* 2. GoogleID
* 3.
*/
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");
// 添加Google任务ID字段
// 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);
}
/**
* 4
*
*/
private void upgradeToV4(SQLiteDatabase db) {
db.execSQL("ALTER TABLE " + TABLE.NOTE + " ADD COLUMN " + NoteColumns.VERSION
+ " INTEGER NOT NULL DEFAULT 0");

@ -14,466 +14,292 @@
* limitations under the License.
*/
package net.micode.notes.provider;
package net.micode.notes.data;
import android.app.SearchManager;
import android.content.ContentProvider;
import android.content.ContentResolver;
import android.content.ContentUris;
import android.content.ContentValues;
import android.content.Context;
import android.content.Intent;
import android.content.UriMatcher;
import android.database.Cursor;
import android.database.sqlite.SQLiteDatabase;
import android.database.sqlite.SQLiteQueryBuilder;
import android.net.Uri;
import android.text.TextUtils;
import android.util.Log;
import net.micode.notes.data.Notes;
import net.micode.notes.data.NotesDatabaseHelper;
import net.micode.notes.R;
import net.micode.notes.data.Notes.DataColumns;
import net.micode.notes.data.Notes.NoteColumns;
import net.micode.notes.data.NotesDatabaseHelper.TABLE;
import java.util.HashMap;
/**
* 便
* 便CRUD
* URI访便
*/
public class NotesProvider extends ContentProvider {
// 日志标签
private static final String TAG = "NotesProvider";
// 数据库帮助类
private NotesDatabaseHelper mOpenHelper;
// 内容解析器
private ContentResolver mContentResolver;
private static final UriMatcher mMatcher;
// URI匹配器常量
private static final int NOTES = 1;
private static final int NOTE_ID = 2;
private static final int FOLDERS = 3;
private static final int FOLDER_ID = 4;
private static final int DATA = 5;
private static final int DATA_ID = 6;
private static final int DATA_NOTE_ID = 7;
private static final int DATA_NOTE_ID_MIME_TYPE = 8;
private NotesDatabaseHelper mHelper;
// URI匹配器
private static final UriMatcher sUriMatcher;
// 便签查询投影映射
private static HashMap<String, String> sNotesProjectionMap;
// 数据查询投影映射
private static HashMap<String, String> sDataProjectionMap;
private static final String TAG = "NotesProvider";
static {
// 初始化URI匹配器
sUriMatcher = new UriMatcher(UriMatcher.NO_MATCH);
sUriMatcher.addURI(Notes.AUTHORITY, "notes", NOTES);
sUriMatcher.addURI(Notes.AUTHORITY, "notes/#", NOTE_ID);
sUriMatcher.addURI(Notes.AUTHORITY, "folders", FOLDERS);
sUriMatcher.addURI(Notes.AUTHORITY, "folders/#", FOLDER_ID);
sUriMatcher.addURI(Notes.AUTHORITY, "data", DATA);
sUriMatcher.addURI(Notes.AUTHORITY, "data/#", DATA_ID);
sUriMatcher.addURI(Notes.AUTHORITY, "data/#/data", DATA_NOTE_ID);
sUriMatcher.addURI(Notes.AUTHORITY, "data/#/data/*", DATA_NOTE_ID_MIME_TYPE);
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;
// 初始化便签查询投影映射
sNotesProjectionMap = new HashMap<String, String>();
sNotesProjectionMap.put(Notes.NoteColumns.ID, Notes.NoteColumns.ID);
sNotesProjectionMap.put(Notes.NoteColumns.PARENT_ID, Notes.NoteColumns.PARENT_ID);
sNotesProjectionMap.put(Notes.NoteColumns.ALERTED_DATE, Notes.NoteColumns.ALERTED_DATE);
sNotesProjectionMap.put(Notes.NoteColumns.BG_COLOR_ID, Notes.NoteColumns.BG_COLOR_ID);
sNotesProjectionMap.put(Notes.NoteColumns.CREATED_DATE, Notes.NoteColumns.CREATED_DATE);
sNotesProjectionMap.put(Notes.NoteColumns.HAS_ATTACHMENT, Notes.NoteColumns.HAS_ATTACHMENT);
sNotesProjectionMap.put(Notes.NoteColumns.MODIFIED_DATE, Notes.NoteColumns.MODIFIED_DATE);
sNotesProjectionMap.put(Notes.NoteColumns.NOTES_COUNT, Notes.NoteColumns.NOTES_COUNT);
sNotesProjectionMap.put(Notes.NoteColumns.SNIPPET, Notes.NoteColumns.SNIPPET);
sNotesProjectionMap.put(Notes.NoteColumns.TYPE, Notes.NoteColumns.TYPE);
sNotesProjectionMap.put(Notes.NoteColumns.WIDGET_ID, Notes.NoteColumns.WIDGET_ID);
sNotesProjectionMap.put(Notes.NoteColumns.WIDGET_TYPE, Notes.NoteColumns.WIDGET_TYPE);
sNotesProjectionMap.put(Notes.NoteColumns.SYNC_ID, Notes.NoteColumns.SYNC_ID);
sNotesProjectionMap.put(Notes.NoteColumns.LOCAL_MODIFIED, Notes.NoteColumns.LOCAL_MODIFIED);
sNotesProjectionMap.put(Notes.NoteColumns.ORIGIN_PARENT_ID, Notes.NoteColumns.ORIGIN_PARENT_ID);
sNotesProjectionMap.put(Notes.NoteColumns.GTASK_ID, Notes.NoteColumns.GTASK_ID);
sNotesProjectionMap.put(Notes.NoteColumns.VERSION, Notes.NoteColumns.VERSION);
private static final int URI_SEARCH = 5;
private static final int URI_SEARCH_SUGGEST = 6;
// 初始化数据查询投影映射
sDataProjectionMap = new HashMap<String, String>();
sDataProjectionMap.put(Notes.DataColumns.ID, Notes.DataColumns.ID);
sDataProjectionMap.put(Notes.DataColumns.MIME_TYPE, Notes.DataColumns.MIME_TYPE);
sDataProjectionMap.put(Notes.DataColumns.NOTE_ID, Notes.DataColumns.NOTE_ID);
sDataProjectionMap.put(Notes.DataColumns.CREATED_DATE, Notes.DataColumns.CREATED_DATE);
sDataProjectionMap.put(Notes.DataColumns.MODIFIED_DATE, Notes.DataColumns.MODIFIED_DATE);
sDataProjectionMap.put(Notes.DataColumns.CONTENT, Notes.DataColumns.CONTENT);
sDataProjectionMap.put(Notes.DataColumns.DATA1, Notes.DataColumns.DATA1);
sDataProjectionMap.put(Notes.DataColumns.DATA2, Notes.DataColumns.DATA2);
sDataProjectionMap.put(Notes.DataColumns.DATA3, Notes.DataColumns.DATA3);
sDataProjectionMap.put(Notes.DataColumns.DATA4, Notes.DataColumns.DATA4);
sDataProjectionMap.put(Notes.DataColumns.DATA5, Notes.DataColumns.DATA5);
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);
}
/**
*
* 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 + ","
+ NoteColumns.ID + " AS " + SearchManager.SUGGEST_COLUMN_INTENT_EXTRA_DATA + ","
+ "TRIM(REPLACE(" + NoteColumns.SNIPPET + ", x'0A','')) AS " + SearchManager.SUGGEST_COLUMN_TEXT_1 + ","
+ "TRIM(REPLACE(" + NoteColumns.SNIPPET + ", x'0A','')) AS " + SearchManager.SUGGEST_COLUMN_TEXT_2 + ","
+ R.drawable.search_result + " AS " + SearchManager.SUGGEST_COLUMN_ICON_1 + ","
+ "'" + Intent.ACTION_VIEW + "' AS " + SearchManager.SUGGEST_COLUMN_INTENT_ACTION + ","
+ "'" + Notes.TextNote.CONTENT_TYPE + "' AS " + SearchManager.SUGGEST_COLUMN_INTENT_DATA;
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() {
mOpenHelper = NotesDatabaseHelper.getInstance(getContext());
mContentResolver = getContext().getContentResolver();
mHelper = NotesDatabaseHelper.getInstance(getContext());
return true;
}
/**
* 便
*/
@Override
public Cursor query(Uri uri, String[] projection, String selection,
String[] selectionArgs, String sortOrder) {
SQLiteQueryBuilder qb = new SQLiteQueryBuilder();
String groupBy = null;
// 根据URI匹配结果设置查询条件
switch (sUriMatcher.match(uri)) {
case NOTES:
qb.setTables(NotesDatabaseHelper.TABLE.NOTE);
qb.setProjectionMap(sNotesProjectionMap);
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 NOTE_ID:
qb.setTables(NotesDatabaseHelper.TABLE.NOTE);
qb.setProjectionMap(sNotesProjectionMap);
qb.appendWhere(Notes.NoteColumns.ID + "=" + uri.getPathSegments().get(1));
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 FOLDERS:
qb.setTables(NotesDatabaseHelper.TABLE.NOTE);
qb.setProjectionMap(sNotesProjectionMap);
qb.appendWhere(Notes.NoteColumns.TYPE + "=" + Notes.TypeColumns.TYPE_FOLDER);
break;
case FOLDER_ID:
qb.setTables(NotesDatabaseHelper.TABLE.NOTE);
qb.setProjectionMap(sNotesProjectionMap);
qb.appendWhere(Notes.NoteColumns.ID + "=" + uri.getPathSegments().get(1));
case URI_DATA:
c = db.query(TABLE.DATA, projection, selection, selectionArgs, null, null,
sortOrder);
break;
case DATA:
qb.setTables(NotesDatabaseHelper.TABLE.DATA);
qb.setProjectionMap(sDataProjectionMap);
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");
}
case DATA_ID:
qb.setTables(NotesDatabaseHelper.TABLE.DATA);
qb.setProjectionMap(sDataProjectionMap);
qb.appendWhere(Notes.DataColumns.ID + "=" + uri.getPathSegments().get(1));
break;
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");
}
case DATA_NOTE_ID:
qb.setTables(NotesDatabaseHelper.TABLE.DATA);
qb.setProjectionMap(sDataProjectionMap);
qb.appendWhere(Notes.DataColumns.NOTE_ID + "=" + uri.getPathSegments().get(1));
break;
if (TextUtils.isEmpty(searchString)) {
return null;
}
case DATA_NOTE_ID_MIME_TYPE:
qb.setTables(NotesDatabaseHelper.TABLE.DATA);
qb.setProjectionMap(sDataProjectionMap);
qb.appendWhere(Notes.DataColumns.NOTE_ID + "=" + uri.getPathSegments().get(1));
qb.appendWhere(" AND " + Notes.DataColumns.MIME_TYPE + "='" +
uri.getPathSegments().get(3) + "'");
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);
}
// 如果没有指定排序方式,使用默认排序
String orderBy;
if (TextUtils.isEmpty(sortOrder)) {
orderBy = Notes.NoteColumns.MODIFIED_DATE + " DESC";
} else {
orderBy = sortOrder;
if (c != null) {
c.setNotificationUri(getContext().getContentResolver(), uri);
}
// 获取数据库并执行查询
SQLiteDatabase db = mOpenHelper.getReadableDatabase();
Cursor c = qb.query(db, projection, selection, selectionArgs, groupBy, null, orderBy);
// 设置通知URI当数据变化时通知ContentResolver
c.setNotificationUri(mContentResolver, uri);
return c;
}
/**
*
*/
@Override
public String getType(Uri uri) {
switch (sUriMatcher.match(uri)) {
case NOTES:
return Notes.NoteContent.CONTENT_TYPE;
case NOTE_ID:
return Notes.NoteContent.CONTENT_ITEM_TYPE;
case FOLDERS:
return Notes.FolderContent.CONTENT_TYPE;
case FOLDER_ID:
return Notes.FolderContent.CONTENT_ITEM_TYPE;
case DATA:
return Notes.DataContent.CONTENT_TYPE;
case DATA_ID:
return Notes.DataContent.CONTENT_ITEM_TYPE;
case DATA_NOTE_ID:
return Notes.DataContent.CONTENT_TYPE;
case DATA_NOTE_ID_MIME_TYPE:
return Notes.DataContent.CONTENT_TYPE;
default:
throw new IllegalArgumentException("Unknown URI " + uri);
}
}
/**
* 便
*/
@Override
public Uri insert(Uri uri, ContentValues initialValues) {
// 确保ContentValues不为空
ContentValues values;
if (initialValues != null) {
values = new ContentValues(initialValues);
} else {
values = new ContentValues();
}
SQLiteDatabase db = mOpenHelper.getWritableDatabase();
long rowId;
Uri baseUri;
// 根据URI匹配结果执行不同的插入操作
switch (sUriMatcher.match(uri)) {
case NOTES:
// 处理父文件夹ID为空的情况
if (!values.containsKey(Notes.NoteColumns.PARENT_ID)) {
values.put(Notes.NoteColumns.PARENT_ID, Notes.FolderConstants.ID_ROOT_FOLDER);
}
// 处理类型为空的情况
if (!values.containsKey(Notes.NoteColumns.TYPE)) {
values.put(Notes.NoteColumns.TYPE, Notes.TypeColumns.TYPE_NOTE);
}
// 处理便签摘要为空的情况
if (!values.containsKey(Notes.NoteColumns.SNIPPET)) {
values.put(Notes.NoteColumns.SNIPPET, "");
}
// 插入便签数据
rowId = db.insert(NotesDatabaseHelper.TABLE.NOTE, null, values);
if (rowId > 0) {
baseUri = Notes.NoteContent.CONTENT_URI;
Uri newUri = ContentUris.withAppendedId(baseUri, rowId);
mContentResolver.notifyChange(newUri, null);
return newUri;
}
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 DATA:
// 处理关联便签ID为空的情况
if (!values.containsKey(Notes.DataColumns.NOTE_ID)) {
throw new IllegalArgumentException("Note ID is required for data insertion");
}
// 处理MIME类型为空的情况
if (!values.containsKey(Notes.DataColumns.MIME_TYPE)) {
throw new IllegalArgumentException("MIME type is required for data insertion");
}
// 插入便签数据关联
rowId = db.insert(NotesDatabaseHelper.TABLE.DATA, null, values);
if (rowId > 0) {
baseUri = Notes.DataContent.CONTENT_URI;
Uri newUri = ContentUris.withAppendedId(baseUri, rowId);
// 如果是提醒类型,更新便签的提醒日期
if (Notes.DataConstants.ALERT.equals(values.getAsString(Notes.DataColumns.MIME_TYPE))) {
long alertDate = values.getAsLong(Notes.DataColumns.DATA1);
updateAlertDate(values.getAsLong(Notes.DataColumns.NOTE_ID), alertDate);
}
mContentResolver.notifyChange(newUri, null);
return newUri;
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);
}
throw new IllegalArgumentException("Failed to insert row into " + uri);
return ContentUris.withAppendedId(uri, insertedId);
}
/**
* 便
*/
@Override
public int update(Uri uri, ContentValues values, String where, String[] whereArgs) {
SQLiteDatabase db = mOpenHelper.getWritableDatabase();
int count;
String finalWhere;
// 根据URI匹配结果执行不同的更新操作
switch (sUriMatcher.match(uri)) {
case NOTES:
count = db.update(NotesDatabaseHelper.TABLE.NOTE, values, where, whereArgs);
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 NOTE_ID:
finalWhere = Notes.NoteColumns.ID + "=" + uri.getPathSegments().get(1);
if (where != null) {
finalWhere = finalWhere + " AND " + where;
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;
}
// 如果更新了便签类型,处理特殊情况
if (values.containsKey(Notes.NoteColumns.TYPE)) {
int type = values.getAsInteger(Notes.NoteColumns.TYPE);
if (type == Notes.TypeColumns.TYPE_SYSTEM) {
throw new IllegalArgumentException("Can't set note type to system type");
}
}
count = db.update(NotesDatabaseHelper.TABLE.NOTE, values, finalWhere, whereArgs);
count = db.delete(TABLE.NOTE,
NoteColumns.ID + "=" + id + parseSelection(selection), selectionArgs);
break;
case DATA:
count = db.update(NotesDatabaseHelper.TABLE.DATA, values, where, whereArgs);
case URI_DATA:
count = db.delete(TABLE.DATA, selection, selectionArgs);
deleteData = true;
break;
case DATA_ID:
finalWhere = Notes.DataColumns.ID + "=" + uri.getPathSegments().get(1);
if (where != null) {
finalWhere = finalWhere + " AND " + where;
}
// 如果更新了提醒数据,更新便签的提醒日期
if (values.containsKey(Notes.DataColumns.MIME_TYPE) &&
Notes.DataConstants.ALERT.equals(values.getAsString(Notes.DataColumns.MIME_TYPE))) {
long alertDate = values.getAsLong(Notes.DataColumns.DATA1);
updateAlertDate(getNoteIdFromDataId(ContentUris.parseId(uri)), alertDate);
}
count = db.update(NotesDatabaseHelper.TABLE.DATA, values, finalWhere, whereArgs);
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);
}
// 通知内容解析器数据已更改
mContentResolver.notifyChange(uri, null);
if (count > 0) {
if (deleteData) {
getContext().getContentResolver().notifyChange(Notes.CONTENT_NOTE_URI, null);
}
getContext().getContentResolver().notifyChange(uri, null);
}
return count;
}
/**
* 便
*/
@Override
public int delete(Uri uri, String where, String[] whereArgs) {
SQLiteDatabase db = mOpenHelper.getWritableDatabase();
int count;
String finalWhere;
// 根据URI匹配结果执行不同的删除操作
switch (sUriMatcher.match(uri)) {
case NOTES:
// 不能删除系统文件夹
finalWhere = Notes.NoteColumns.TYPE + "!=" + Notes.TypeColumns.TYPE_SYSTEM;
if (where != null) {
finalWhere = finalWhere + " AND " + where;
}
count = db.delete(NotesDatabaseHelper.TABLE.NOTE, finalWhere, whereArgs);
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 NOTE_ID:
// 不能删除系统文件夹
finalWhere = Notes.NoteColumns.ID + "=" + uri.getPathSegments().get(1) +
" AND " + Notes.NoteColumns.TYPE + "!=" + Notes.TypeColumns.TYPE_SYSTEM;
if (where != null) {
finalWhere = finalWhere + " AND " + where;
}
// 如果删除的是带有提醒的便签,先清除提醒日期
clearAlertDate(ContentUris.parseId(uri));
count = db.delete(NotesDatabaseHelper.TABLE.NOTE, finalWhere, whereArgs);
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 DATA:
count = db.delete(NotesDatabaseHelper.TABLE.DATA, where, whereArgs);
case URI_DATA:
count = db.update(TABLE.DATA, values, selection, selectionArgs);
updateData = true;
break;
case DATA_ID:
finalWhere = Notes.DataColumns.ID + "=" + uri.getPathSegments().get(1);
if (where != null) {
finalWhere = finalWhere + " AND " + where;
}
// 如果删除的是提醒数据,清除便签的提醒日期
clearAlertDate(getNoteIdFromDataId(ContentUris.parseId(uri)));
count = db.delete(NotesDatabaseHelper.TABLE.DATA, finalWhere, whereArgs);
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);
}
// 通知内容解析器数据已更改
mContentResolver.notifyChange(uri, null);
if (count > 0) {
if (updateData) {
getContext().getContentResolver().notifyChange(Notes.CONTENT_NOTE_URI, null);
}
getContext().getContentResolver().notifyChange(uri, null);
}
return count;
}
/**
* 便
*/
private void updateAlertDate(long noteId, long alertDate) {
ContentValues values = new ContentValues();
values.put(Notes.NoteColumns.ALERTED_DATE, alertDate);
values.put(Notes.NoteColumns.LOCAL_MODIFIED, 1);
SQLiteDatabase db = mOpenHelper.getWritableDatabase();
db.update(NotesDatabaseHelper.TABLE.NOTE, values,
Notes.NoteColumns.ID + "=" + noteId, null);
private String parseSelection(String selection) {
return (!TextUtils.isEmpty(selection) ? " AND (" + selection + ')' : "");
}
/**
* 便
*/
private void clearAlertDate(long noteId) {
ContentValues values = new ContentValues();
values.put(Notes.NoteColumns.ALERTED_DATE, 0);
values.put(Notes.NoteColumns.LOCAL_MODIFIED, 1);
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 ");
SQLiteDatabase db = mOpenHelper.getWritableDatabase();
db.update(NotesDatabaseHelper.TABLE.NOTE, values,
Notes.NoteColumns.ID + "=" + noteId, null);
}
/**
* ID便ID
*/
private long getNoteIdFromDataId(long dataId) {
String[] projection = new String[] { Notes.DataColumns.NOTE_ID };
String selection = Notes.DataColumns.ID + " = ?";
String[] selectionArgs = new String[] { String.valueOf(dataId) };
SQLiteDatabase db = mOpenHelper.getReadableDatabase();
Cursor cursor = db.query(NotesDatabaseHelper.TABLE.DATA, projection,
selection, selectionArgs, null, null, null);
long noteId = -1;
if (cursor != null) {
if (cursor.moveToFirst()) {
noteId = cursor.getLong(0);
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);
}
cursor.close();
sql.append(selectString);
}
return noteId;
mHelper.getWritableDatabase().execSQL(sql.toString());
}
@Override
public String getType(Uri uri) {
// TODO Auto-generated method stub
return null;
}
}

@ -24,69 +24,37 @@ import net.micode.notes.tool.GTaskStringUtils;
import org.json.JSONException;
import org.json.JSONObject;
/**
* MetaData Google Tasks
* Task Google Tasks ID
* JSON
*/
public class MetaData extends Task {
private final static String TAG = MetaData.class.getSimpleName();
// 关联的 Google Task ID
private String mRelatedGid = null;
/**
*
*
* @param gid Google Task ID
* @param metaInfo JSON
*/
public void setMeta(String gid, JSONObject metaInfo) {
try {
// 将 Google Task ID 添加到元数据中
metaInfo.put(GTaskStringUtils.META_HEAD_GTASK_ID, gid);
} catch (JSONException e) {
Log.e(TAG, "failed to put related gid");
}
// 将元数据转换为字符串并存储在笔记内容中
setNotes(metaInfo.toString());
// 设置固定的元数据笔记名称
setName(GTaskStringUtils.META_NOTE_NAME);
}
/**
* Google Task ID
*
* @return Google Task ID
*/
public String getRelatedGid() {
return mRelatedGid;
}
/**
*
*
* @return true false
*/
@Override
public boolean isWorthSaving() {
return getNotes() != null;
}
/**
* JSON
*
* @param js JSON
*/
@Override
public void setContentByRemoteJSON(JSONObject js) {
// 调用父类方法设置基本内容
super.setContentByRemoteJSON(js);
if (getNotes() != null) {
try {
// 解析笔记内容中的 JSON 元数据
JSONObject metaInfo = new JSONObject(getNotes().trim());
// 提取并存储关联的 Google Task ID
mRelatedGid = metaInfo.getString(GTaskStringUtils.META_HEAD_GTASK_ID);
} catch (JSONException e) {
Log.w(TAG, "failed to get related gid");
@ -95,31 +63,20 @@ public class MetaData extends Task {
}
}
/**
* JSON -
* MetaData JSON
*/
@Override
public void setContentByLocalJSON(JSONObject js) {
// this function should not be called
throw new IllegalAccessError("MetaData:setContentByLocalJSON should not be called");
}
/**
* JSON -
* MetaData JSON
*/
@Override
public JSONObject getLocalJSONFromContent() {
throw new IllegalAccessError("MetaData:getLocalJSONFromContent should not be called");
}
/**
* -
* MetaData
*/
@Override
public int getSyncAction(Cursor c) {
throw new IllegalAccessError("MetaData:getSyncAction should not be called");
}
}

@ -20,36 +20,33 @@ import android.database.Cursor;
import org.json.JSONObject;
/**
* Node Google Tasks
*
* Google Tasks
*/
public abstract class Node {
// 同步动作类型常量定义
public static final int SYNC_ACTION_NONE = 0; // 无需同步
public static final int SYNC_ACTION_NONE = 0;
public static final int SYNC_ACTION_ADD_REMOTE = 1;
public static final int SYNC_ACTION_ADD_LOCAL = 2;
public static final int SYNC_ACTION_DEL_REMOTE = 3;
public static final int SYNC_ACTION_DEL_LOCAL = 4;
public static final int SYNC_ACTION_ADD_REMOTE = 1; // 添加到远程
public static final int SYNC_ACTION_ADD_LOCAL = 2; // 添加到本地
public static final int SYNC_ACTION_UPDATE_REMOTE = 5;
public static final int SYNC_ACTION_DEL_REMOTE = 3; // 从远程删除
public static final int SYNC_ACTION_DEL_LOCAL = 4; // 从本地删除
public static final int SYNC_ACTION_UPDATE_LOCAL = 6;
public static final int SYNC_ACTION_UPDATE_REMOTE = 5; // 更新远程数据
public static final int SYNC_ACTION_UPDATE_LOCAL = 6; // 更新本地数据
public static final int SYNC_ACTION_UPDATE_CONFLICT = 7;
public static final int SYNC_ACTION_UPDATE_CONFLICT = 7; // 同步冲突
public static final int SYNC_ACTION_ERROR = 8; // 同步错误
public static final int SYNC_ACTION_ERROR = 8;
// 节点基本属性
private String mGid; // Google Task ID用于标识远程任务
private String mName; // 节点名称
private long mLastModified; // 最后修改时间戳
private boolean mDeleted; // 删除标记
private String mGid;
private String mName;
private long mLastModified;
private boolean mDeleted;
/**
*
*/
public Node() {
mGid = null;
mName = "";
@ -57,46 +54,18 @@ public abstract class Node {
mDeleted = false;
}
/**
* JSON
* @param actionId ID
* @return JSON
*/
public abstract JSONObject getCreateAction(int actionId);
/**
* JSON
* @param actionId ID
* @return JSON
*/
public abstract JSONObject getUpdateAction(int actionId);
/**
* JSON
* @param js JSON
*/
public abstract void setContentByRemoteJSON(JSONObject js);
/**
* JSON
* @param js JSON
*/
public abstract void setContentByLocalJSON(JSONObject js);
/**
* JSON
* @return JSON
*/
public abstract JSONObject getLocalJSONFromContent();
/**
*
* @param c
* @return
*/
public abstract int getSyncAction(Cursor c);
// 基本属性的 getter 和 setter 方法
public void setGid(String gid) {
this.mGid = gid;
}
@ -128,4 +97,5 @@ public abstract class Node {
public boolean getDeleted() {
return this.mDeleted;
}
}

@ -34,43 +34,43 @@ import net.micode.notes.gtask.exception.ActionFailureException;
import org.json.JSONException;
import org.json.JSONObject;
/**
* SqlData
*
* JSON 便 Google Tasks
*/
public class SqlData {
private static final String TAG = SqlData.class.getSimpleName();
// 无效 ID 标记
private static final int INVALID_ID = -99999;
// 数据库查询投影
public static final String[] PROJECTION_DATA = new String[] {
DataColumns.ID, DataColumns.MIME_TYPE, DataColumns.CONTENT, DataColumns.DATA1,
DataColumns.DATA3
};
// 投影列索引常量
public static final int DATA_ID_COLUMN = 0;
public static final int DATA_MIME_TYPE_COLUMN = 1;
public static final int DATA_CONTENT_COLUMN = 2;
public static final int DATA_CONTENT_DATA_1_COLUMN = 3;
public static final int DATA_CONTENT_DATA_3_COLUMN = 4;
// 成员变量
private ContentResolver mContentResolver; // 内容解析器,用于数据库操作
private boolean mIsCreate; // 是否为新创建的数据
private long mDataId; // 数据 ID
private String mDataMimeType; // 数据 MIME 类型
private String mDataContent; // 数据内容
private long mDataContentData1; // 附加数据字段1
private String mDataContentData3; // 附加数据字段3
private ContentValues mDiffDataValues; // 存储数据变更,用于批量更新
/**
* SqlData
*/
private ContentResolver mContentResolver;
private boolean mIsCreate;
private long mDataId;
private String mDataMimeType;
private String mDataContent;
private long mDataContentData1;
private String mDataContentData3;
private ContentValues mDiffDataValues;
public SqlData(Context context) {
mContentResolver = context.getContentResolver();
mIsCreate = true;
@ -82,9 +82,6 @@ public class SqlData {
mDiffDataValues = new ContentValues();
}
/**
* SqlData
*/
public SqlData(Context context, Cursor c) {
mContentResolver = context.getContentResolver();
mIsCreate = false;
@ -92,9 +89,6 @@ public class SqlData {
mDiffDataValues = new ContentValues();
}
/**
*
*/
private void loadFromCursor(Cursor c) {
mDataId = c.getLong(DATA_ID_COLUMN);
mDataMimeType = c.getString(DATA_MIME_TYPE_COLUMN);
@ -103,10 +97,6 @@ public class SqlData {
mDataContentData3 = c.getString(DATA_CONTENT_DATA_3_COLUMN);
}
/**
* JSON
*
*/
public void setContent(JSONObject js) throws JSONException {
long dataId = js.has(DataColumns.ID) ? js.getLong(DataColumns.ID) : INVALID_ID;
if (mIsCreate || mDataId != dataId) {
@ -140,9 +130,6 @@ public class SqlData {
mDataContentData3 = dataContentData3;
}
/**
* JSON
*/
public JSONObject getContent() throws JSONException {
if (mIsCreate) {
Log.e(TAG, "it seems that we haven't created this in database yet");
@ -157,21 +144,13 @@ public class SqlData {
return js;
}
/**
*
* @param noteId ID
* @param validateVersion
* @param version
*/
public void commit(long noteId, boolean validateVersion, long version) {
// 插入新数据
if (mIsCreate) {
// 移除无效 ID
if (mDataId == INVALID_ID && mDiffDataValues.containsKey(DataColumns.ID)) {
mDiffDataValues.remove(DataColumns.ID);
}
// 设置关联笔记 ID 并插入数据
mDiffDataValues.put(DataColumns.NOTE_ID, noteId);
Uri uri = mContentResolver.insert(Notes.CONTENT_DATA_URI, mDiffDataValues);
try {
@ -180,12 +159,9 @@ 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);
@ -203,14 +179,10 @@ public class SqlData {
}
}
// 重置状态,清除变更记录
mDiffDataValues.clear();
mIsCreate = false;
}
/**
* ID
*/
public long getId() {
return mDataId;
}

@ -37,19 +37,12 @@ import org.json.JSONObject;
import java.util.ArrayList;
/**
* SqlNote
*
* Google Tasks JSON
*
*/
public class SqlNote {
private static final String TAG = SqlNote.class.getSimpleName();
// 无效ID标记用于标识未创建的笔记
private static final int INVALID_ID = -99999;
// 数据库查询投影字段,定义查询笔记时需要返回的列
public static final String[] PROJECTION_NOTE = new String[] {
NoteColumns.ID, NoteColumns.ALERTED_DATE, NoteColumns.BG_COLOR_ID,
NoteColumns.CREATED_DATE, NoteColumns.HAS_ATTACHMENT, NoteColumns.MODIFIED_DATE,
@ -59,55 +52,82 @@ public class SqlNote {
NoteColumns.VERSION
};
// 投影字段索引常量,用于快速访问查询结果中的列
public static final int ID_COLUMN = 0;
public static final int ALERTED_DATE_COLUMN = 1; // 提醒日期列索引(与闹钟功能相关)
public static final int ALERTED_DATE_COLUMN = 1;
public static final int BG_COLOR_ID_COLUMN = 2;
public static final int CREATED_DATE_COLUMN = 3;
public static final int HAS_ATTACHMENT_COLUMN = 4;
public static final int MODIFIED_DATE_COLUMN = 5;
public static final int NOTES_COUNT_COLUMN = 6;
public static final int PARENT_ID_COLUMN = 7;
public static final int SNIPPET_COLUMN = 8;
public static final int TYPE_COLUMN = 9;
public static final int WIDGET_ID_COLUMN = 10;
public static final int WIDGET_TYPE_COLUMN = 11;
public static final int SYNC_ID_COLUMN = 12;
public static final int LOCAL_MODIFIED_COLUMN = 13;
public static final int ORIGIN_PARENT_ID_COLUMN = 14;
public static final int GTASK_ID_COLUMN = 15;
public static final int VERSION_COLUMN = 16;
// 成员变量
private Context mContext; // 应用上下文
private ContentResolver mContentResolver; // 内容解析器,用于数据库操作
private boolean mIsCreate; // 是否为新建笔记标志
private long mId; // 笔记ID
private long mAlertDate; // 提醒日期(核心闹钟相关字段)
private int mBgColorId; // 背景颜色ID
private long mCreatedDate; // 创建日期
private int mHasAttachment; // 是否有附件标志
private long mModifiedDate; // 修改日期
private long mParentId; // 父文件夹ID
private String mSnippet; // 笔记摘要
private int mType; // 笔记类型(普通笔记、文件夹等)
private int mWidgetId; // 桌面小部件ID
private int mWidgetType; // 桌面小部件类型
private long mOriginParent; // 原始父文件夹ID
private long mVersion; // 版本号(用于并发控制)
private ContentValues mDiffNoteValues; // 记录笔记变更,用于高效更新
private ArrayList<SqlData> mDataList; // 关联的数据项列表
/**
*
*
*/
private Context mContext;
private ContentResolver mContentResolver;
private boolean mIsCreate;
private long mId;
private long mAlertDate;
private int mBgColorId;
private long mCreatedDate;
private int mHasAttachment;
private long mModifiedDate;
private long mParentId;
private String mSnippet;
private int mType;
private int mWidgetId;
private int mWidgetType;
private long mOriginParent;
private long mVersion;
private ContentValues mDiffNoteValues;
private ArrayList<SqlData> mDataList;
public SqlNote(Context context) {
mContext = context;
mContentResolver = context.getContentResolver();
mIsCreate = true;
mId = INVALID_ID;
mAlertDate = 0; // 初始提醒时间为0无提醒
mAlertDate = 0;
mBgColorId = ResourceParser.getDefaultBgId(context);
mCreatedDate = System.currentTimeMillis();
mHasAttachment = 0;
@ -123,10 +143,6 @@ public class SqlNote {
mDataList = new ArrayList<SqlData>();
}
/**
*
* @param c
*/
public SqlNote(Context context, Cursor c) {
mContext = context;
mContentResolver = context.getContentResolver();
@ -138,10 +154,6 @@ public class SqlNote {
mDiffNoteValues = new ContentValues();
}
/**
* ID
* @param id ID
*/
public SqlNote(Context context, long id) {
mContext = context;
mContentResolver = context.getContentResolver();
@ -151,17 +163,16 @@ public class SqlNote {
if (mType == Notes.TYPE_NOTE)
loadDataContent();
mDiffNoteValues = new ContentValues();
}
/**
*
* @param id ID
*/
private void loadFromCursor(long id) {
Cursor c = null;
try {
c = mContentResolver.query(Notes.CONTENT_NOTE_URI, PROJECTION_NOTE, "(_id=?)",
new String[] { String.valueOf(id) }, null);
new String[] {
String.valueOf(id)
}, null);
if (c != null) {
c.moveToNext();
loadFromCursor(c);
@ -174,13 +185,9 @@ public class SqlNote {
}
}
/**
*
* @param c
*/
private void loadFromCursor(Cursor c) {
mId = c.getLong(ID_COLUMN);
mAlertDate = c.getLong(ALERTED_DATE_COLUMN); // 加载提醒日期
mAlertDate = c.getLong(ALERTED_DATE_COLUMN);
mBgColorId = c.getInt(BG_COLOR_ID_COLUMN);
mCreatedDate = c.getLong(CREATED_DATE_COLUMN);
mHasAttachment = c.getInt(HAS_ATTACHMENT_COLUMN);
@ -193,15 +200,14 @@ public class SqlNote {
mVersion = c.getLong(VERSION_COLUMN);
}
/**
*
*/
private void loadDataContent() {
Cursor c = null;
mDataList.clear();
try {
c = mContentResolver.query(Notes.CONTENT_DATA_URI, SqlData.PROJECTION_DATA,
"(note_id=?)", new String[] { String.valueOf(mId) }, null);
"(note_id=?)", new String[] {
String.valueOf(mId)
}, null);
if (c != null) {
if (c.getCount() == 0) {
Log.w(TAG, "it seems that the note has not data");
@ -220,18 +226,13 @@ public class SqlNote {
}
}
/**
* JSON
* @param js JSON
* @return
*/
public boolean setContent(JSONObject js) {
try {
JSONObject note = js.getJSONObject(GTaskStringUtils.META_HEAD_NOTE);
if (note.getInt(NoteColumns.TYPE) == Notes.TYPE_SYSTEM) {
Log.w(TAG, "cannot set system folder");
} else if (note.getInt(NoteColumns.TYPE) == Notes.TYPE_FOLDER) {
// 处理文件夹类型,仅更新摘要和类型
// for folder we can only update the snnipet and type
String snippet = note.has(NoteColumns.SNIPPET) ? note
.getString(NoteColumns.SNIPPET) : "";
if (mIsCreate || !mSnippet.equals(snippet)) {
@ -246,7 +247,6 @@ public class SqlNote {
}
mType = type;
} else if (note.getInt(NoteColumns.TYPE) == Notes.TYPE_NOTE) {
// 处理普通笔记类型,更新所有字段及关联数据
JSONArray dataArray = js.getJSONArray(GTaskStringUtils.META_HEAD_DATA);
long id = note.has(NoteColumns.ID) ? note.getLong(NoteColumns.ID) : INVALID_ID;
if (mIsCreate || mId != id) {
@ -254,7 +254,6 @@ public class SqlNote {
}
mId = id;
// 设置提醒日期(核心闹钟相关字段)
long alertDate = note.has(NoteColumns.ALERTED_DATE) ? note
.getLong(NoteColumns.ALERTED_DATE) : 0;
if (mIsCreate || mAlertDate != alertDate) {
@ -262,9 +261,76 @@ public class SqlNote {
}
mAlertDate = alertDate;
// 其他字段设置...
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();
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;
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();
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;
if (mIsCreate || mParentId != parentId) {
mDiffNoteValues.put(NoteColumns.PARENT_ID, parentId);
}
mParentId = parentId;
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;
if (mIsCreate || mType != type) {
mDiffNoteValues.put(NoteColumns.TYPE, type);
}
mType = type;
int widgetId = note.has(NoteColumns.WIDGET_ID) ? note.getInt(NoteColumns.WIDGET_ID)
: AppWidgetManager.INVALID_APPWIDGET_ID;
if (mIsCreate || mWidgetId != widgetId) {
mDiffNoteValues.put(NoteColumns.WIDGET_ID, widgetId);
}
mWidgetId = widgetId;
int widgetType = note.has(NoteColumns.WIDGET_TYPE) ? note
.getInt(NoteColumns.WIDGET_TYPE) : Notes.TYPE_WIDGET_INVALIDE;
if (mIsCreate || mWidgetType != widgetType) {
mDiffNoteValues.put(NoteColumns.WIDGET_TYPE, widgetType);
}
mWidgetType = widgetType;
long originParent = note.has(NoteColumns.ORIGIN_PARENT_ID) ? note
.getLong(NoteColumns.ORIGIN_PARENT_ID) : 0;
if (mIsCreate || mOriginParent != originParent) {
mDiffNoteValues.put(NoteColumns.ORIGIN_PARENT_ID, originParent);
}
mOriginParent = originParent;
// 处理关联的数据项
for (int i = 0; i < dataArray.length(); i++) {
JSONObject data = dataArray.getJSONObject(i);
SqlData sqlData = null;
@ -293,10 +359,6 @@ public class SqlNote {
return true;
}
/**
* JSON
* @return JSON
*/
public JSONObject getContent() {
try {
JSONObject js = new JSONObject();
@ -308,15 +370,20 @@ public class SqlNote {
JSONObject note = new JSONObject();
if (mType == Notes.TYPE_NOTE) {
// 构建普通笔记的JSON数据
note.put(NoteColumns.ID, mId);
note.put(NoteColumns.ALERTED_DATE, mAlertDate); // 添加提醒日期到JSON
note.put(NoteColumns.ALERTED_DATE, mAlertDate);
note.put(NoteColumns.BG_COLOR_ID, mBgColorId);
// 其他字段添加...
note.put(NoteColumns.CREATED_DATE, mCreatedDate);
note.put(NoteColumns.HAS_ATTACHMENT, mHasAttachment);
note.put(NoteColumns.MODIFIED_DATE, mModifiedDate);
note.put(NoteColumns.PARENT_ID, mParentId);
note.put(NoteColumns.SNIPPET, mSnippet);
note.put(NoteColumns.TYPE, mType);
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);
// 添加关联的数据项到JSON数组
JSONArray dataArray = new JSONArray();
for (SqlData sqlData : mDataList) {
JSONObject data = sqlData.getContent();
@ -326,7 +393,6 @@ public class SqlNote {
}
js.put(GTaskStringUtils.META_HEAD_DATA, dataArray);
} else if (mType == Notes.TYPE_FOLDER || mType == Notes.TYPE_SYSTEM) {
// 构建文件夹的JSON数据
note.put(NoteColumns.ID, mId);
note.put(NoteColumns.TYPE, mType);
note.put(NoteColumns.SNIPPET, mSnippet);
@ -341,70 +407,41 @@ public class SqlNote {
return null;
}
/**
* ID
*/
public void setParentId(long id) {
mParentId = id;
mDiffNoteValues.put(NoteColumns.PARENT_ID, id);
}
/**
* Google TasksID
*/
public void setGtaskId(String gid) {
mDiffNoteValues.put(NoteColumns.GTASK_ID, gid);
}
/**
* ID
*/
public void setSyncId(long syncId) {
mDiffNoteValues.put(NoteColumns.SYNC_ID, syncId);
}
/**
*
*/
public void resetLocalModified() {
mDiffNoteValues.put(NoteColumns.LOCAL_MODIFIED, 0);
}
/**
* ID
*/
public long getId() {
return mId;
}
/**
* ID
*/
public long getParentId() {
return mParentId;
}
/**
*
*/
public String getSnippet() {
return mSnippet;
}
/**
*
*/
public boolean isNoteType() {
return mType == Notes.TYPE_NOTE;
}
/**
*
* @param validateVersion
*/
public void commit(boolean validateVersion) {
if (mIsCreate) {
// 新建笔记的插入操作
if (mId == INVALID_ID && mDiffNoteValues.containsKey(NoteColumns.ID)) {
mDiffNoteValues.remove(NoteColumns.ID);
}
@ -420,14 +457,12 @@ public class SqlNote {
throw new IllegalStateException("Create thread id failed");
}
// 保存关联的数据项
if (mType == Notes.TYPE_NOTE) {
for (SqlData sqlData : mDataList) {
sqlData.commit(mId, false, -1);
}
}
} else {
// 更新现有笔记
if (mId <= 0 && mId != Notes.ID_ROOT_FOLDER && mId != Notes.ID_CALL_RECORD_FOLDER) {
Log.e(TAG, "No such note");
throw new IllegalStateException("Try to update note with invalid id");
@ -437,18 +472,21 @@ public class SqlNote {
int result = 0;
if (!validateVersion) {
result = mContentResolver.update(Notes.CONTENT_NOTE_URI, mDiffNoteValues, "("
+ NoteColumns.ID + "=?)", new String[] { String.valueOf(mId) });
+ NoteColumns.ID + "=?)", new String[] {
String.valueOf(mId)
});
} else {
result = mContentResolver.update(Notes.CONTENT_NOTE_URI, mDiffNoteValues, "("
+ NoteColumns.ID + "=?) AND (" + NoteColumns.VERSION + "<=?)",
new String[] { String.valueOf(mId), String.valueOf(mVersion) });
new String[] {
String.valueOf(mId), String.valueOf(mVersion)
});
}
if (result == 0) {
Log.w(TAG, "there is no update. maybe user updates note when syncing");
}
}
// 更新关联的数据项
if (mType == Notes.TYPE_NOTE) {
for (SqlData sqlData : mDataList) {
sqlData.commit(mId, validateVersion, mVersion);
@ -456,7 +494,7 @@ public class SqlNote {
}
}
// 重新加载数据以更新本地状态
// refresh local info
loadFromCursor(mId);
if (mType == Notes.TYPE_NOTE)
loadDataContent();

@ -31,19 +31,19 @@ import org.json.JSONArray;
import org.json.JSONException;
import org.json.JSONObject;
/**
* Task Google Tasks
*
*
*/
public class Task extends Node {
private static final String TAG = Task.class.getSimpleName();
private boolean mCompleted; // 任务完成状态
private String mNotes; // 任务备注信息
private JSONObject mMetaInfo; // 任务元数据,存储与本地笔记关联信息
private Task mPriorSibling; // 同级前一个任务,用于任务排序
private TaskList mParent; // 父任务列表
private boolean mCompleted;
private String mNotes;
private JSONObject mMetaInfo;
private Task mPriorSibling;
private TaskList mParent;
public Task() {
super();
@ -54,12 +54,6 @@ public class Task extends Node {
mMetaInfo = null;
}
/**
* JSON
* Google Tasks
* @param actionId ID
* @return JSON
*/
public JSONObject getCreateAction(int actionId) {
JSONObject js = new JSONObject();
@ -109,12 +103,6 @@ public class Task extends Node {
return js;
}
/**
* JSON
* Google Tasks
* @param actionId ID
* @return JSON
*/
public JSONObject getUpdateAction(int actionId) {
JSONObject js = new JSONObject();
@ -147,11 +135,6 @@ public class Task extends Node {
return js;
}
/**
* JSON
* Google Tasks
* @param js JSON
*/
public void setContentByRemoteJSON(JSONObject js) {
if (js != null) {
try {
@ -192,11 +175,6 @@ public class Task extends Node {
}
}
/**
* JSON
* Google Tasks
* @param js JSON
*/
public void setContentByLocalJSON(JSONObject js) {
if (js == null || !js.has(GTaskStringUtils.META_HEAD_NOTE)
|| !js.has(GTaskStringUtils.META_HEAD_DATA)) {
@ -226,11 +204,6 @@ public class Task extends Node {
}
}
/**
* JSON
* Google Tasks
* @return JSON
*/
public JSONObject getLocalJSONFromContent() {
String name = getName();
try {
@ -274,11 +247,6 @@ public class Task extends Node {
}
}
/**
*
*
* @param metaData
*/
public void setMetaInfo(MetaData metaData) {
if (metaData != null && metaData.getNotes() != null) {
try {
@ -290,12 +258,6 @@ public class Task extends Node {
}
}
/**
*
*
* @param c
* @return
*/
public int getSyncAction(Cursor c) {
try {
JSONObject noteInfo = null;
@ -349,17 +311,11 @@ public class Task extends Node {
return SYNC_ACTION_ERROR;
}
/**
*
*
* @return
*/
public boolean isWorthSaving() {
return mMetaInfo != null || (getName() != null && getName().trim().length() > 0)
|| (getNotes() != null && getNotes().trim().length() > 0);
}
// Getter and Setter 方法
public void setCompleted(boolean completed) {
this.mCompleted = completed;
}
@ -391,4 +347,5 @@ public class Task extends Node {
public TaskList getParent() {
return this.mParent;
}
}

@ -29,16 +29,13 @@ import org.json.JSONObject;
import java.util.ArrayList;
/**
* TaskList Google Tasks
*
*
*/
public class TaskList extends Node {
private static final String TAG = TaskList.class.getSimpleName();
private int mIndex; // 任务列表在父容器中的索引位置
private ArrayList<Task> mChildren; // 包含的子任务列表
private int mIndex;
private ArrayList<Task> mChildren;
public TaskList() {
super();
@ -46,12 +43,6 @@ public class TaskList extends Node {
mIndex = 1;
}
/**
* JSON
* Google Tasks
* @param actionId ID
* @return JSON
*/
public JSONObject getCreateAction(int actionId) {
JSONObject js = new JSONObject();
@ -83,12 +74,6 @@ public class TaskList extends Node {
return js;
}
/**
* JSON
* Google Tasks
* @param actionId ID
* @return JSON
*/
public JSONObject getUpdateAction(int actionId) {
JSONObject js = new JSONObject();
@ -118,11 +103,6 @@ public class TaskList extends Node {
return js;
}
/**
* JSON
* Google Tasks
* @param js JSON
*/
public void setContentByRemoteJSON(JSONObject js) {
if (js != null) {
try {
@ -149,11 +129,6 @@ public class TaskList extends Node {
}
}
/**
* JSON
* Google Tasks
* @param js JSON
*/
public void setContentByLocalJSON(JSONObject js) {
if (js == null || !js.has(GTaskStringUtils.META_HEAD_NOTE)) {
Log.w(TAG, "setContentByLocalJSON: nothing is avaiable");
@ -163,11 +138,9 @@ public class TaskList extends Node {
JSONObject folder = js.getJSONObject(GTaskStringUtils.META_HEAD_NOTE);
if (folder.getInt(NoteColumns.TYPE) == Notes.TYPE_FOLDER) {
// 普通文件夹处理
String name = folder.getString(NoteColumns.SNIPPET);
setName(GTaskStringUtils.MIUI_FOLDER_PREFFIX + name);
} else if (folder.getInt(NoteColumns.TYPE) == Notes.TYPE_SYSTEM) {
// 系统文件夹处理
if (folder.getLong(NoteColumns.ID) == Notes.ID_ROOT_FOLDER)
setName(GTaskStringUtils.MIUI_FOLDER_PREFFIX + GTaskStringUtils.FOLDER_DEFAULT);
else if (folder.getLong(NoteColumns.ID) == Notes.ID_CALL_RECORD_FOLDER)
@ -184,24 +157,16 @@ public class TaskList extends Node {
}
}
/**
* JSON
* Google Tasks
* @return JSON
*/
public JSONObject getLocalJSONFromContent() {
try {
JSONObject js = new JSONObject();
JSONObject folder = new JSONObject();
// 处理任务列表名称,移除可能的前缀
String folderName = getName();
if (getName().startsWith(GTaskStringUtils.MIUI_FOLDER_PREFFIX))
folderName = folderName.substring(GTaskStringUtils.MIUI_FOLDER_PREFFIX.length(),
folderName.length());
folder.put(NoteColumns.SNIPPET, folderName);
// 根据文件夹名称确定类型(系统文件夹或普通文件夹)
if (folderName.equals(GTaskStringUtils.FOLDER_DEFAULT)
|| folderName.equals(GTaskStringUtils.FOLDER_CALL_NOTE))
folder.put(NoteColumns.TYPE, Notes.TYPE_SYSTEM);
@ -218,34 +183,28 @@ public class TaskList extends Node {
}
}
/**
*
*
* @param c
* @return
*/
public int getSyncAction(Cursor c) {
try {
if (c.getInt(SqlNote.LOCAL_MODIFIED_COLUMN) == 0) {
// 本地无更新
// there is no local update
if (c.getLong(SqlNote.SYNC_ID_COLUMN) == getLastModified()) {
// 两边都无更新
// no update both side
return SYNC_ACTION_NONE;
} else {
// 应用远程更新到本地
// apply remote to local
return SYNC_ACTION_UPDATE_LOCAL;
}
} else {
// 验证gtask id
// validate gtask id
if (!c.getString(SqlNote.GTASK_ID_COLUMN).equals(getGid())) {
Log.e(TAG, "gtask id doesn't match");
return SYNC_ACTION_ERROR;
}
if (c.getLong(SqlNote.SYNC_ID_COLUMN) == getLastModified()) {
// 只有本地修改
// local modification only
return SYNC_ACTION_UPDATE_REMOTE;
} else {
// 对于文件夹冲突,默认应用本地修改
// for folder conflicts, just apply local modification
return SYNC_ACTION_UPDATE_REMOTE;
}
}
@ -257,24 +216,16 @@ public class TaskList extends Node {
return SYNC_ACTION_ERROR;
}
/**
*
*/
public int getChildTaskCount() {
return mChildren.size();
}
/**
*
* @param task
* @return
*/
public boolean addChildTask(Task task) {
boolean ret = false;
if (task != null && !mChildren.contains(task)) {
ret = mChildren.add(task);
if (ret) {
// 设置任务的前一个兄弟任务和父任务列表
// need to set prior sibling and parent
task.setPriorSibling(mChildren.isEmpty() ? null : mChildren
.get(mChildren.size() - 1));
task.setParent(this);
@ -283,12 +234,6 @@ public class TaskList extends Node {
return ret;
}
/**
*
* @param task
* @param index
* @return
*/
public boolean addChildTask(Task task, int index) {
if (index < 0 || index > mChildren.size()) {
Log.e(TAG, "add child task: invalid index");
@ -299,7 +244,7 @@ public class TaskList extends Node {
if (task != null && pos == -1) {
mChildren.add(index, task);
// 更新任务列表中的前后关系
// update the task list
Task preTask = null;
Task afterTask = null;
if (index != 0)
@ -315,11 +260,6 @@ public class TaskList extends Node {
return true;
}
/**
*
* @param task
* @return
*/
public boolean removeChildTask(Task task) {
boolean ret = false;
int index = mChildren.indexOf(task);
@ -327,11 +267,11 @@ public class TaskList extends Node {
ret = mChildren.remove(task);
if (ret) {
// 重置任务的前一个兄弟任务和父任务列表
// reset prior sibling and parent
task.setPriorSibling(null);
task.setParent(null);
// 更新任务列表中的前后关系
// update the task list
if (index != mChildren.size()) {
mChildren.get(index).setPriorSibling(
index == 0 ? null : mChildren.get(index - 1));
@ -341,13 +281,8 @@ public class TaskList extends Node {
return ret;
}
/**
*
* @param task
* @param index
* @return
*/
public boolean moveChildTask(Task task, int index) {
if (index < 0 || index >= mChildren.size()) {
Log.e(TAG, "move child task: invalid index");
return false;
@ -364,11 +299,6 @@ public class TaskList extends Node {
return (removeChildTask(task) && addChildTask(task, index));
}
/**
* Google Tasks ID
* @param gid Google Tasks ID
* @return null
*/
public Task findChildTaskByGid(String gid) {
for (int i = 0; i < mChildren.size(); i++) {
Task t = mChildren.get(i);
@ -379,20 +309,10 @@ public class TaskList extends Node {
return null;
}
/**
*
* @param task
* @return -1
*/
public int getChildTaskIndex(Task task) {
return mChildren.indexOf(task);
}
/**
*
* @param index
* @return null
*/
public Task getChildTaskByIndex(int index) {
if (index < 0 || index >= mChildren.size()) {
Log.e(TAG, "getTaskByIndex: invalid index");
@ -401,11 +321,6 @@ public class TaskList extends Node {
return mChildren.get(index);
}
/**
* Google Tasks ID
* @param gid Google Tasks ID
* @return null
*/
public Task getChilTaskByGid(String gid) {
for (Task task : mChildren) {
if (task.getGid().equals(gid))
@ -414,24 +329,14 @@ public class TaskList extends Node {
return null;
}
/**
*
* @return
*/
public ArrayList<Task> getChildTaskList() {
return this.mChildren;
}
/**
*
*/
public void setIndex(int index) {
this.mIndex = index;
}
/**
*
*/
public int getIndex() {
return this.mIndex;
}

@ -1,3 +1,4 @@
/*
* Copyright (c) 2010-2011, The MiCode Open Source Community (www.micode.net)
*
@ -27,36 +28,23 @@ import net.micode.notes.R;
import net.micode.notes.ui.NotesListActivity;
import net.micode.notes.ui.NotesPreferenceActivity;
/**
* AsyncTask Google
*
*/
public class GTaskASyncTask extends AsyncTask<Void, String, Integer> {
// 同步通知的唯一 ID
private static int GTASK_SYNC_NOTIFICATION_ID = 5234235;
/**
*
*/
public interface OnCompleteListener {
void onComplete();
}
// 应用上下文
private Context mContext;
// 通知管理器
private NotificationManager mNotifiManager;
// GTask 管理器实例
private GTaskManager mTaskManager;
// 同步完成监听器
private OnCompleteListener mOnCompleteListener;
/**
* GTask
* @param context
* @param listener
*/
public GTaskASyncTask(Context context, OnCompleteListener listener) {
mContext = context;
mOnCompleteListener = listener;
@ -65,31 +53,18 @@ public class GTaskASyncTask extends AsyncTask<Void, String, Integer> {
mTaskManager = GTaskManager.getInstance();
}
/**
*
*/
public void cancelSync() {
mTaskManager.cancelSync();
}
/**
*
* @param message
*/
public void publishProgess(String message) {
publishProgress(new String[]{
message
});
}
/**
*
* @param tickerId ID
* @param content
*/
private void showNotification(int tickerId, String content) {
PendingIntent pendingIntent;
// 根据不同的提示信息 ID 设置不同的点击跳转意图
if (tickerId != R.string.ticker_success) {
pendingIntent = PendingIntent.getActivity(mContext, 0, new Intent(mContext,
NotesPreferenceActivity.class), PendingIntent.FLAG_IMMUTABLE);
@ -108,24 +83,13 @@ public class GTaskASyncTask extends AsyncTask<Void, String, Integer> {
mNotifiManager.notify(GTASK_SYNC_NOTIFICATION_ID, notification);
}
/**
*
* @param unused 使
* @return
*/
@Override
protected Integer doInBackground(Void... unused) {
// 发布登录进度信息
publishProgess(mContext.getString(R.string.sync_progress_login, NotesPreferenceActivity
.getSyncAccountName(mContext)));
// 调用 GTask 管理器的同步方法
return mTaskManager.sync(mContext, this);
}
/**
* 广
* @param progress
*/
@Override
protected void onProgressUpdate(String... progress) {
showNotification(R.string.ticker_syncing, progress[0]);
@ -134,16 +98,11 @@ public class GTaskASyncTask extends AsyncTask<Void, String, Integer> {
}
}
/**
*
* @param result
*/
@Override
protected void onPostExecute(Integer result) {
if (result == GTaskManager.STATE_SUCCESS) {
showNotification(R.string.ticker_success, mContext.getString(
R.string.success_sync_account, mTaskManager.getSyncAccount()));
// 设置最后同步时间
NotesPreferenceActivity.setLastSyncTime(mContext, System.currentTimeMillis());
} else if (result == GTaskManager.STATE_NETWORK_ERROR) {
showNotification(R.string.ticker_fail, mContext.getString(R.string.error_sync_network));
@ -154,8 +113,8 @@ public class GTaskASyncTask extends AsyncTask<Void, String, Integer> {
.getString(R.string.error_sync_cancelled));
}
if (mOnCompleteListener != null) {
// 启动新线程调用同步完成监听器的方法
new Thread(new Runnable() {
public void run() {
mOnCompleteListener.onComplete();
}

@ -60,42 +60,36 @@ import java.util.zip.GZIPInputStream;
import java.util.zip.Inflater;
import java.util.zip.InflaterInputStream;
/**
* Google
*/
public class GTaskClient {
// 日志标签
private static final String TAG = GTaskClient.class.getSimpleName();
// Google 任务的基本 URL
private static final String GTASK_URL = "https://mail.google.com/tasks/";
// 获取任务列表的 URL
private static final String GTASK_GET_URL = "https://mail.google.com/tasks/ig";
// 提交任务操作的 URL
private static final String GTASK_POST_URL = "https://mail.google.com/tasks/r/ig";
// 单例实例
private static GTaskClient mInstance = null;
// HTTP 客户端
private DefaultHttpClient mHttpClient;
// 获取任务列表的实际 URL
private String mGetUrl;
// 提交任务操作的实际 URL
private String mPostUrl;
// 客户端版本号
private long mClientVersion;
// 是否登录标志
private boolean mLoggedin;
// 最后登录时间
private long mLastLoginTime;
// 操作 ID
private int mActionId;
// 同步账户
private Account mAccount;
// 更新操作数组
private JSONArray mUpdateArray;
/**
*
*/
private GTaskClient() {
mHttpClient = null;
mGetUrl = GTASK_GET_URL;
@ -108,10 +102,6 @@ public class GTaskClient {
mUpdateArray = null;
}
/**
*
* @return GTaskClient
*/
public static synchronized GTaskClient getInstance() {
if (mInstance == null) {
mInstance = new GTaskClient();
@ -119,35 +109,34 @@ public class GTaskClient {
return mInstance;
}
/**
* Google
* @param activity
* @return true false
*/
public boolean login(Activity activity) {
// 假设 cookie 有效期为 5 分钟,超过时间需要重新登录
// we suppose that the cookie would expire after 5 minutes
// then we need to re-login
final long interval = 1000 * 60 * 5;
if (mLastLoginTime + interval < System.currentTimeMillis()) {
mLoggedin = false;
}
// 切换账户后需要重新登录
// need to re-login after account switch
if (mLoggedin
&& !TextUtils.equals(getSyncAccount().name, NotesPreferenceActivity
.getSyncAccountName(activity))) {
mLoggedin = false;
}
if (mLoggedin) {
Log.d(TAG, "already logged in");
return true;
}
mLastLoginTime = System.currentTimeMillis();
// 登录 Google 账户获取认证令牌
String authToken = loginGoogleAccount(activity, false);
if (authToken == null) {
Log.e(TAG, "login google account failed");
return false;
}
// 非 gmail 或 googlemail 账户需要尝试自定义域名登录
// 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/");
@ -156,11 +145,13 @@ public class GTaskClient {
url.append(suffix + "/");
mGetUrl = url.toString() + "ig";
mPostUrl = url.toString() + "r/ig";
if (tryToLoginGtask(activity, authToken)) {
mLoggedin = true;
}
}
// 尝试使用 Google 官方 URL 登录
// try to login with google official url
if (!mLoggedin) {
mGetUrl = GTASK_GET_URL;
mPostUrl = GTASK_POST_URL;
@ -168,24 +159,21 @@ public class GTaskClient {
return false;
}
}
mLoggedin = true;
return true;
}
/**
* Google
* @param activity
* @param invalidateToken 使
* @return null
*/
private String loginGoogleAccount(Activity activity, boolean invalidateToken) {
String authToken;
AccountManager accountManager = AccountManager.get(activity);
Account[] accounts = accountManager.getAccountsByType("com.google");
if (accounts.length == 0) {
Log.e(TAG, "there is no available google account");
return null;
}
String accountName = NotesPreferenceActivity.getSyncAccountName(activity);
Account account = null;
for (Account a : accounts) {
@ -200,7 +188,8 @@ public class GTaskClient {
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 {
@ -214,23 +203,20 @@ public class GTaskClient {
Log.e(TAG, "get auth token failed");
authToken = null;
}
return authToken;
}
/**
* Google
* @param activity
* @param authToken
* @return true false
*/
private boolean tryToLoginGtask(Activity activity, String authToken) {
if (!loginGtask(authToken)) {
// 可能认证令牌过期,使旧令牌失效并重新尝试登录
// maybe the auth token is out of date, now let's invalidate the
// token and try again
authToken = loginGoogleAccount(activity, true);
if (authToken == null) {
Log.e(TAG, "login google account failed");
return false;
}
if (!loginGtask(authToken)) {
Log.e(TAG, "login gtask failed");
return false;
@ -239,11 +225,6 @@ public class GTaskClient {
return true;
}
/**
* 使 Google
* @param authToken
* @return true false
*/
private boolean loginGtask(String authToken) {
int timeoutConnection = 10000;
int timeoutSocket = 15000;
@ -254,12 +235,15 @@ public class GTaskClient {
BasicCookieStore localBasicCookieStore = new BasicCookieStore();
mHttpClient.setCookieStore(localBasicCookieStore);
HttpProtocolParams.setUseExpectContinue(mHttpClient.getParams(), false);
// login gtask
try {
String loginUrl = mGetUrl + "?auth=" + authToken;
HttpGet httpGet = new HttpGet(loginUrl);
HttpResponse response = null;
response = mHttpClient.execute(httpGet);
// 获取认证 cookie
// get the cookie now
List<Cookie> cookies = mHttpClient.getCookieStore().getCookies();
boolean hasAuthCookie = false;
for (Cookie cookie : cookies) {
@ -270,7 +254,8 @@ public class GTaskClient {
if (!hasAuthCookie) {
Log.w(TAG, "it seems that there is no auth cookie");
}
// 获取客户端版本号
// get the client version
String resString = getResponseContent(response.getEntity());
String jsBegin = "_setup(";
String jsEnd = ")}</script>";
@ -287,24 +272,18 @@ public class GTaskClient {
e.printStackTrace();
return false;
} catch (Exception e) {
// simply catch all exceptions
Log.e(TAG, "httpget gtask_url failed");
return false;
}
return true;
}
/**
* ID
* @return ID
*/
private int getActionId() {
return mActionId++;
}
/**
* HttpPost
* @return HttpPost
*/
private HttpPost createHttpPost() {
HttpPost httpPost = new HttpPost(mPostUrl);
httpPost.setHeader("Content-Type", "application/x-www-form-urlencoded;charset=utf-8");
@ -312,18 +291,13 @@ public class GTaskClient {
return httpPost;
}
/**
* HTTP
* @param entity HTTP
* @return
* @throws IOException
*/
private String getResponseContent(HttpEntity entity) throws IOException {
String contentEncoding = null;
if (entity.getContentEncoding() != null) {
contentEncoding = entity.getContentEncoding().getValue();
Log.d(TAG, "encoding: " + contentEncoding);
}
InputStream input = entity.getContent();
if (contentEncoding != null && contentEncoding.equalsIgnoreCase("gzip")) {
input = new GZIPInputStream(entity.getContent());
@ -331,10 +305,12 @@ public class GTaskClient {
Inflater inflater = new Inflater(true);
input = new InflaterInputStream(entity.getContent(), inflater);
}
try {
InputStreamReader isr = new InputStreamReader(input);
BufferedReader br = new BufferedReader(isr);
StringBuilder sb = new StringBuilder();
while (true) {
String buff = br.readLine();
if (buff == null) {
@ -347,27 +323,24 @@ public class GTaskClient {
}
}
/**
* JSON
* @param js JSON
* @return JSON
* @throws NetworkFailureException
*/
private JSONObject postRequest(JSONObject js) throws NetworkFailureException {
if (!mLoggedin) {
Log.e(TAG, "please login first");
throw new ActionFailureException("not logged in");
}
HttpPost httpPost = createHttpPost();
try {
LinkedList<BasicNameValuePair> list = new LinkedList<BasicNameValuePair>();
list.add(new BasicNameValuePair("r", js.toString()));
UrlEncodedFormEntity entity = new UrlEncodedFormEntity(list, "UTF-8");
httpPost.setEntity(entity);
// 执行 POST 请求
// execute the post
HttpResponse response = mHttpClient.execute(httpPost);
String jsString = getResponseContent(response.getEntity());
return new JSONObject(jsString);
} catch (ClientProtocolException e) {
Log.e(TAG, e.toString());
e.printStackTrace();
@ -387,27 +360,25 @@ public class GTaskClient {
}
}
/**
*
* @param task
* @throws NetworkFailureException
*/
public void createTask(Task task) throws NetworkFailureException {
commitUpdate();
try {
JSONObject jsPost = new JSONObject();
JSONArray actionList = new JSONArray();
// 添加创建任务的操作
// action_list
actionList.put(task.getCreateAction(getActionId()));
jsPost.put(GTaskStringUtils.GTASK_JSON_ACTION_LIST, actionList);
// 设置客户端版本号
// client_version
jsPost.put(GTaskStringUtils.GTASK_JSON_CLIENT_VERSION, mClientVersion);
// 提交请求
// post
JSONObject jsResponse = postRequest(jsPost);
JSONObject jsResult = (JSONObject) jsResponse.getJSONArray(
GTaskStringUtils.GTASK_JSON_RESULTS).get(0);
// 设置任务的 Google ID
task.setGid(jsResult.getString(GTaskStringUtils.GTASK_JSON_NEW_ID));
} catch (JSONException e) {
Log.e(TAG, e.toString());
e.printStackTrace();
@ -415,27 +386,25 @@ public class GTaskClient {
}
}
/**
*
* @param tasklist
* @throws NetworkFailureException
*/
public void createTaskList(TaskList tasklist) throws NetworkFailureException {
commitUpdate();
try {
JSONObject jsPost = new JSONObject();
JSONArray actionList = new JSONArray();
// 添加创建任务列表的操作
// action_list
actionList.put(tasklist.getCreateAction(getActionId()));
jsPost.put(GTaskStringUtils.GTASK_JSON_ACTION_LIST, actionList);
// 设置客户端版本号
// client version
jsPost.put(GTaskStringUtils.GTASK_JSON_CLIENT_VERSION, mClientVersion);
// 提交请求
// post
JSONObject jsResponse = postRequest(jsPost);
JSONObject jsResult = (JSONObject) jsResponse.getJSONArray(
GTaskStringUtils.GTASK_JSON_RESULTS).get(0);
// 设置任务列表的 Google ID
tasklist.setGid(jsResult.getString(GTaskStringUtils.GTASK_JSON_NEW_ID));
} catch (JSONException e) {
Log.e(TAG, e.toString());
e.printStackTrace();
@ -443,19 +412,17 @@ public class GTaskClient {
}
}
/**
*
* @throws NetworkFailureException
*/
public void commitUpdate() throws NetworkFailureException {
if (mUpdateArray != null) {
try {
JSONObject jsPost = new JSONObject();
// 添加更新操作数组
// action_list
jsPost.put(GTaskStringUtils.GTASK_JSON_ACTION_LIST, mUpdateArray);
// 设置客户端版本号
// client_version
jsPost.put(GTaskStringUtils.GTASK_JSON_CLIENT_VERSION, mClientVersion);
// 提交请求
postRequest(jsPost);
mUpdateArray = null;
} catch (JSONException e) {
@ -466,31 +433,20 @@ public class GTaskClient {
}
}
/**
*
* @param node
* @throws NetworkFailureException
*/
public void addUpdateNode(Node node) throws NetworkFailureException {
if (node != null) {
// 更新项过多时先提交更新
// too many update items may result in an error
// set max to 10 items
if (mUpdateArray != null && mUpdateArray.length() > 10) {
commitUpdate();
}
if (mUpdateArray == null)
mUpdateArray = new JSONArray();
// 添加更新操作
mUpdateArray.put(node.getUpdateAction(getActionId()));
}
}
/**
*
* @param task
* @param preParent
* @param curParent
* @throws NetworkFailureException
*/
public void moveTask(Task task, TaskList preParent, TaskList curParent)
throws NetworkFailureException {
commitUpdate();
@ -498,25 +454,31 @@ public class GTaskClient {
JSONObject jsPost = new JSONObject();
JSONArray actionList = new JSONArray();
JSONObject action = new JSONObject();
// 设置移动任务的操作
// action_list
action.put(GTaskStringUtils.GTASK_JSON_ACTION_TYPE,
GTaskStringUtils.GTASK_JSON_ACTION_TYPE_MOVE);
action.put(GTaskStringUtils.GTASK_JSON_ACTION_ID, getActionId());
action.put(GTaskStringUtils.GTASK_JSON_ID, task.getGid());
if (preParent == curParent && task.getPriorSibling() != null) {
// put prioring_sibing_id only if moving within the tasklist and
// it is not the first one
action.put(GTaskStringUtils.GTASK_JSON_PRIOR_SIBLING_ID, task.getPriorSibling());
}
action.put(GTaskStringUtils.GTASK_JSON_SOURCE_LIST, preParent.getGid());
action.put(GTaskStringUtils.GTASK_JSON_DEST_PARENT, curParent.getGid());
if (preParent != curParent) {
// put the dest_list only if moving between tasklists
action.put(GTaskStringUtils.GTASK_JSON_DEST_LIST, curParent.getGid());
}
actionList.put(action);
jsPost.put(GTaskStringUtils.GTASK_JSON_ACTION_LIST, actionList);
// 设置客户端版本号
// client_version
jsPost.put(GTaskStringUtils.GTASK_JSON_CLIENT_VERSION, mClientVersion);
// 提交请求
postRequest(jsPost);
} catch (JSONException e) {
Log.e(TAG, e.toString());
e.printStackTrace();
@ -524,24 +486,20 @@ public class GTaskClient {
}
}
/**
*
* @param node
* @throws NetworkFailureException
*/
public void deleteNode(Node node) throws NetworkFailureException {
commitUpdate();
try {
JSONObject jsPost = new JSONObject();
JSONArray actionList = new JSONArray();
// 设置节点为已删除
// action_list
node.setDeleted(true);
// 添加删除操作
actionList.put(node.getUpdateAction(getActionId()));
jsPost.put(GTaskStringUtils.GTASK_JSON_ACTION_LIST, actionList);
// 设置客户端版本号
// client_version
jsPost.put(GTaskStringUtils.GTASK_JSON_CLIENT_VERSION, mClientVersion);
// 提交请求
postRequest(jsPost);
mUpdateArray = null;
} catch (JSONException e) {
@ -551,21 +509,18 @@ public class GTaskClient {
}
}
/**
*
* @return JSON
* @throws NetworkFailureException
*/
public JSONArray getTaskLists() throws NetworkFailureException {
if (!mLoggedin) {
Log.e(TAG, "please login first");
throw new ActionFailureException("not logged in");
}
try {
HttpGet httpGet = new HttpGet(mGetUrl);
HttpResponse response = null;
response = mHttpClient.execute(httpGet);
// 获取任务列表的 JSON 数据
// get the task list
String resString = getResponseContent(response.getEntity());
String jsBegin = "_setup(";
String jsEnd = ")}</script>";
@ -592,19 +547,14 @@ public class GTaskClient {
}
}
/**
*
* @param listGid Google ID
* @return JSON
* @throws NetworkFailureException
*/
public JSONArray getTaskList(String listGid) throws NetworkFailureException {
commitUpdate();
try {
JSONObject jsPost = new JSONObject();
JSONArray actionList = new JSONArray();
JSONObject action = new JSONObject();
// 设置获取任务列表的操作
// action_list
action.put(GTaskStringUtils.GTASK_JSON_ACTION_TYPE,
GTaskStringUtils.GTASK_JSON_ACTION_TYPE_GETALL);
action.put(GTaskStringUtils.GTASK_JSON_ACTION_ID, getActionId());
@ -612,9 +562,10 @@ public class GTaskClient {
action.put(GTaskStringUtils.GTASK_JSON_GET_DELETED, false);
actionList.put(action);
jsPost.put(GTaskStringUtils.GTASK_JSON_ACTION_LIST, actionList);
// 设置客户端版本号
// client_version
jsPost.put(GTaskStringUtils.GTASK_JSON_CLIENT_VERSION, mClientVersion);
// 提交请求
JSONObject jsResponse = postRequest(jsPost);
return jsResponse.getJSONArray(GTaskStringUtils.GTASK_JSON_TASKS);
} catch (JSONException e) {
@ -624,17 +575,10 @@ public class GTaskClient {
}
}
/**
*
* @return
*/
public Account getSyncAccount() {
return mAccount;
}
/**
*
*/
public void resetUpdateArray() {
mUpdateArray = null;
}

@ -47,54 +47,46 @@ import java.util.HashSet;
import java.util.Iterator;
import java.util.Map;
/**
* GTaskManager Google
* Google
*/
public class GTaskManager {
// 日志标签
private static final String TAG = GTaskManager.class.getSimpleName();
// 同步成功的状态码
public static final int STATE_SUCCESS = 0;
// 网络错误的状态码
public static final int STATE_NETWORK_ERROR = 1;
// 内部错误的状态码
public static final int STATE_INTERNAL_ERROR = 2;
// 同步正在进行的状态码
public static final int STATE_SYNC_IN_PROGRESS = 3;
// 同步已取消的状态码
public static final int STATE_SYNC_CANCELLED = 4;
// 单例对象
private static GTaskManager mInstance = null;
// 用于获取认证令牌的 Activity 对象
private Activity mActivity;
// 上下文对象
private Context mContext;
// 内容解析器对象
private ContentResolver mContentResolver;
// 表示是否正在同步的标志
private boolean mSyncing;
// 表示是否取消同步的标志
private boolean mCancelled;
// 存储 Google 任务列表的 HashMap
private HashMap<String, TaskList> mGTaskListHashMap;
// 存储 Google 任务节点的 HashMap
private HashMap<String, Node> mGTaskHashMap;
// 存储元数据的 HashMap
private HashMap<String, MetaData> mMetaHashMap;
// 元数据任务列表
private TaskList mMetaList;
// 存储本地已删除笔记 ID 的 HashSet
private HashSet<Long> mLocalDeleteIdMap;
// 存储 Google 任务 ID 到本地笔记 ID 的映射
private HashMap<String, Long> mGidToNid;
// 存储本地笔记 ID 到 Google 任务 ID 的映射
private HashMap<Long, String> mNidToGid;
/**
* GTaskManager
*
*/
private GTaskManager() {
mSyncing = false;
mCancelled = false;
@ -107,11 +99,6 @@ public class GTaskManager {
mNidToGid = new HashMap<Long, String>();
}
/**
* GTaskManager
*
* @return GTaskManager
*/
public static synchronized GTaskManager getInstance() {
if (mInstance == null) {
mInstance = new GTaskManager();
@ -119,22 +106,11 @@ public class GTaskManager {
return mInstance;
}
/**
* Activity
* @param activity Activity
*/
public synchronized void setActivityContext(Activity activity) {
// used for getting authtoken
mActivity = activity;
}
/**
* Google
* Google
* @param context Context
* @param asyncTask
* @return
*/
public int sync(Context context, GTaskASyncTask asyncTask) {
if (mSyncing) {
Log.d(TAG, "Sync is in progress");
@ -155,18 +131,18 @@ public class GTaskManager {
GTaskClient client = GTaskClient.getInstance();
client.resetUpdateArray();
// 登录 Google 任务
// login google task
if (!mCancelled) {
if (!client.login(mActivity)) {
throw new NetworkFailureException("login google task failed");
}
}
// 获取 Google 任务列表
// 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) {
@ -192,11 +168,6 @@ public class GTaskManager {
return mCancelled ? STATE_SYNC_CANCELLED : STATE_SUCCESS;
}
/**
* Google
* Google
* @throws NetworkFailureException
*/
private void initGTaskList() throws NetworkFailureException {
if (mCancelled)
return;
@ -204,7 +175,7 @@ public class GTaskManager {
try {
JSONArray jsTaskLists = client.getTaskLists();
// 初始化元数据列表
// init meta list first
mMetaList = null;
for (int i = 0; i < jsTaskLists.length(); i++) {
JSONObject object = jsTaskLists.getJSONObject(i);
@ -216,7 +187,7 @@ public class GTaskManager {
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);
@ -232,7 +203,7 @@ public class GTaskManager {
}
}
// 如果元数据列表不存在,则创建一个新的元数据列表
// create meta list if not existed
if (mMetaList == null) {
mMetaList = new TaskList();
mMetaList.setName(GTaskStringUtils.MIUI_FOLDER_PREFFIX
@ -240,7 +211,7 @@ public class GTaskManager {
GTaskClient.getInstance().createTaskList(mMetaList);
}
// 初始化任务列表
// init task list
for (int i = 0; i < jsTaskLists.length(); i++) {
JSONObject object = jsTaskLists.getJSONObject(i);
String gid = object.getString(GTaskStringUtils.GTASK_JSON_ID);
@ -254,7 +225,7 @@ public class GTaskManager {
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);
@ -276,11 +247,6 @@ public class GTaskManager {
}
}
/**
*
* ID
* @throws NetworkFailureException
*/
private void syncContent() throws NetworkFailureException {
int syncType;
Cursor c = null;
@ -293,7 +259,7 @@ public class GTaskManager {
return;
}
// 处理本地已删除的笔记
// for local deleted note
try {
c = mContentResolver.query(Notes.CONTENT_NOTE_URI, SqlNote.PROJECTION_NOTE,
"(type<>? AND parent_id=?)", new String[] {
@ -320,10 +286,10 @@ public class GTaskManager {
}
}
// 同步文件夹
// sync folder first
syncFolder();
// 处理数据库中存在的笔记
// for note existing in database
try {
c = mContentResolver.query(Notes.CONTENT_NOTE_URI, SqlNote.PROJECTION_NOTE,
"(type=? AND parent_id<>?)", new String[] {
@ -340,10 +306,10 @@ public class GTaskManager {
syncType = node.getSyncAction(c);
} else {
if (c.getString(SqlNote.GTASK_ID_COLUMN).trim().length() == 0) {
// 本地新增
// local add
syncType = Node.SYNC_ACTION_ADD_REMOTE;
} else {
// 远程删除
// remote delete
syncType = Node.SYNC_ACTION_DEL_LOCAL;
}
}
@ -360,7 +326,7 @@ public class GTaskManager {
}
}
// 处理剩余的任务项
// go through remaining items
Iterator<Map.Entry<String, Node>> iter = mGTaskHashMap.entrySet().iterator();
while (iter.hasNext()) {
Map.Entry<String, Node> entry = iter.next();
@ -368,14 +334,16 @@ public class GTaskManager {
doContentSync(Node.SYNC_ACTION_ADD_LOCAL, node, null);
}
// 清除本地已删除的笔记
// mCancelled can be set by another thread, so we neet to check one by
// one
// clear local delete table
if (!mCancelled) {
if (!DataUtils.batchDeleteNotes(mContentResolver, mLocalDeleteIdMap)) {
throw new ActionFailureException("failed to batch-delete local deleted notes");
}
}
// 刷新本地同步 ID
// refresh local sync id
if (!mCancelled) {
GTaskClient.getInstance().commitUpdate();
refreshLocalSyncId();
@ -383,11 +351,6 @@ public class GTaskManager {
}
/**
*
*
* @throws NetworkFailureException
*/
private void syncFolder() throws NetworkFailureException {
Cursor c = null;
String gid;
@ -398,7 +361,7 @@ public class GTaskManager {
return;
}
// 处理根文件夹
// for root folder
try {
c = mContentResolver.query(ContentUris.withAppendedId(Notes.CONTENT_NOTE_URI,
Notes.ID_ROOT_FOLDER), SqlNote.PROJECTION_NOTE, null, null, null);
@ -410,7 +373,7 @@ public class GTaskManager {
mGTaskHashMap.remove(gid);
mGidToNid.put(gid, (long) Notes.ID_ROOT_FOLDER);
mNidToGid.put((long) Notes.ID_ROOT_FOLDER, gid);
// 对于系统文件夹,仅在必要时更新远程名称
// for system folder, only update remote name if necessary
if (!node.getName().equals(
GTaskStringUtils.MIUI_FOLDER_PREFFIX + GTaskStringUtils.FOLDER_DEFAULT))
doContentSync(Node.SYNC_ACTION_UPDATE_REMOTE, node, c);
@ -427,7 +390,7 @@ public class GTaskManager {
}
}
// 处理通话记录文件夹
// for call-note folder
try {
c = mContentResolver.query(Notes.CONTENT_NOTE_URI, SqlNote.PROJECTION_NOTE, "(_id=?)",
new String[] {
@ -441,7 +404,8 @@ public class GTaskManager {
mGTaskHashMap.remove(gid);
mGidToNid.put(gid, (long) Notes.ID_CALL_RECORD_FOLDER);
mNidToGid.put((long) Notes.ID_CALL_RECORD_FOLDER, gid);
// 对于系统文件夹,仅在必要时更新远程名称
// for system folder, only update remote name if
// necessary
if (!node.getName().equals(
GTaskStringUtils.MIUI_FOLDER_PREFFIX
+ GTaskStringUtils.FOLDER_CALL_NOTE))
@ -460,7 +424,7 @@ public class GTaskManager {
}
}
// 处理本地现有文件夹
// for local existing folders
try {
c = mContentResolver.query(Notes.CONTENT_NOTE_URI, SqlNote.PROJECTION_NOTE,
"(type=? AND parent_id<>?)", new String[] {
@ -477,10 +441,10 @@ public class GTaskManager {
syncType = node.getSyncAction(c);
} else {
if (c.getString(SqlNote.GTASK_ID_COLUMN).trim().length() == 0) {
// 本地新增
// local add
syncType = Node.SYNC_ACTION_ADD_REMOTE;
} else {
// 远程删除
// remote delete
syncType = Node.SYNC_ACTION_DEL_LOCAL;
}
}
@ -496,7 +460,7 @@ public class GTaskManager {
}
}
// 处理远程新增文件夹
// for remote add folders
Iterator<Map.Entry<String, TaskList>> iter = mGTaskListHashMap.entrySet().iterator();
while (iter.hasNext()) {
Map.Entry<String, TaskList> entry = iter.next();
@ -512,14 +476,6 @@ public class GTaskManager {
GTaskClient.getInstance().commitUpdate();
}
/**
*
*
* @param syncType
* @param node
* @param c
* @throws NetworkFailureException
*/
private void doContentSync(int syncType, Node node, Cursor c) throws NetworkFailureException {
if (mCancelled) {
return;
@ -554,8 +510,8 @@ public class GTaskManager {
updateRemoteNode(node, c);
break;
case Node.SYNC_ACTION_UPDATE_CONFLICT:
// 合并双方的修改可能是个好主意
// 目前简单地使用本地更新
// merging both modifications maybe a good idea
// right now just use local update simply
updateRemoteNode(node, c);
break;
case Node.SYNC_ACTION_NONE:
@ -566,12 +522,6 @@ public class GTaskManager {
}
}
/**
*
*
* @param node
* @throws NetworkFailureException
*/
private void addLocalNode(Node node) throws NetworkFailureException {
if (mCancelled) {
return;
@ -599,7 +549,7 @@ public class GTaskManager {
if (note.has(NoteColumns.ID)) {
long id = note.getLong(NoteColumns.ID);
if (DataUtils.existInNoteDatabase(mContentResolver, id)) {
// 该 ID 不可用,必须创建一个新的 ID
// the id is not available, have to create a new one
note.remove(NoteColumns.ID);
}
}
@ -612,7 +562,8 @@ public class GTaskManager {
if (data.has(DataColumns.ID)) {
long dataId = data.getLong(DataColumns.ID);
if (DataUtils.existInDataDatabase(mContentResolver, dataId)) {
// 该数据 ID 不可用,必须创建一个新的 ID
// the data id is not available, have to create
// a new one
data.remove(DataColumns.ID);
}
}
@ -633,32 +584,25 @@ public class GTaskManager {
sqlNote.setParentId(parentId.longValue());
}
// 创建本地节点
// create the local node
sqlNote.setGtaskId(node.getGid());
sqlNote.commit(false);
// 更新 gid-nid 映射
// update gid-nid mapping
mGidToNid.put(node.getGid(), sqlNote.getId());
mNidToGid.put(sqlNote.getId(), node.getGid());
// 更新元数据
// update meta
updateRemoteMeta(node.getGid(), sqlNote);
}
/**
*
*
* @param node
* @param c
* @throws NetworkFailureException
*/
private void updateLocalNode(Node node, Cursor c) throws NetworkFailureException {
if (mCancelled) {
return;
}
SqlNote sqlNote;
// 本地更新笔记
// update the note locally
sqlNote = new SqlNote(mContext, c);
sqlNote.setContent(node.getLocalJSONFromContent());
@ -671,17 +615,10 @@ public class GTaskManager {
sqlNote.setParentId(parentId.longValue());
sqlNote.commit(true);
// 更新元数据信息
// update meta info
updateRemoteMeta(node.getGid(), sqlNote);
}
/**
*
* GTask ID
* @param node
* @param c
* @throws NetworkFailureException
*/
private void addRemoteNode(Node node, Cursor c) throws NetworkFailureException {
if (mCancelled) {
return;
@ -690,7 +627,7 @@ public class GTaskManager {
SqlNote sqlNote = new SqlNote(mContext, c);
Node n;
// 远程更新
// update remotely
if (sqlNote.isNoteType()) {
Task task = new Task();
task.setContentByLocalJSON(sqlNote.getContent());
@ -705,12 +642,12 @@ public class GTaskManager {
GTaskClient.getInstance().createTask(task);
n = (Node) task;
// 添加元数据
// add meta
updateRemoteMeta(task.getGid(), sqlNote);
} else {
TaskList tasklist = null;
// 如果文件夹已经存在,则跳过
// we need to skip folder if it has already existed
String folderName = GTaskStringUtils.MIUI_FOLDER_PREFFIX;
if (sqlNote.getId() == Notes.ID_ROOT_FOLDER)
folderName += GTaskStringUtils.FOLDER_DEFAULT;
@ -734,7 +671,7 @@ public class GTaskManager {
}
}
// 如果没有匹配的文件夹,则添加新的文件夹
// no match we can add now
if (tasklist == null) {
tasklist = new TaskList();
tasklist.setContentByLocalJSON(sqlNote.getContent());
@ -744,24 +681,17 @@ public class GTaskManager {
n = (Node) tasklist;
}
// 更新本地笔记
// update local note
sqlNote.setGtaskId(n.getGid());
sqlNote.commit(false);
sqlNote.resetLocalModified();
sqlNote.commit(true);
// gid-id 映射
// gid-id mapping
mGidToNid.put(n.getGid(), sqlNote.getId());
mNidToGid.put(sqlNote.getId(), n.getGid());
}
/**
*
*
* @param node
* @param c
* @throws NetworkFailureException
*/
private void updateRemoteNode(Node node, Cursor c) throws NetworkFailureException {
if (mCancelled) {
return;
@ -769,14 +699,14 @@ public class GTaskManager {
SqlNote sqlNote = new SqlNote(mContext, c);
// 远程更新
// update remotely
node.setContentByLocalJSON(sqlNote.getContent());
GTaskClient.getInstance().addUpdateNode(node);
// 更新元数据
// update meta
updateRemoteMeta(node.getGid(), sqlNote);
// 必要时移动任务
// move task if necessary
if (sqlNote.isNoteType()) {
Task task = (Task) node;
TaskList preParentList = task.getParent();
@ -795,18 +725,11 @@ public class GTaskManager {
}
}
// 清除本地修改标志
// clear local modified flag
sqlNote.resetLocalModified();
sqlNote.commit(true);
}
/**
*
*
* @param gid GTask ID
* @param sqlNote
* @throws NetworkFailureException
*/
private void updateRemoteMeta(String gid, SqlNote sqlNote) throws NetworkFailureException {
if (sqlNote != null && sqlNote.isNoteType()) {
MetaData metaData = mMetaHashMap.get(gid);
@ -823,17 +746,12 @@ public class GTaskManager {
}
}
/**
* ID
* Google ID
* @throws NetworkFailureException
*/
private void refreshLocalSyncId() throws NetworkFailureException {
if (mCancelled) {
return;
}
// 获取最新的 Google 任务列表
// get the latest gtask list
mGTaskHashMap.clear();
mGTaskListHashMap.clear();
mMetaHashMap.clear();
@ -872,18 +790,10 @@ public class GTaskManager {
}
}
/**
*
* @return
*/
public String getSyncAccount() {
return GTaskClient.getInstance().getSyncAccount().name;
}
/**
*
* true
*/
public void cancelSync() {
mCancelled = true;
}

@ -23,114 +23,106 @@ import android.content.Intent;
import android.os.Bundle;
import android.os.IBinder;
// GTaskSyncService 是一个用于管理 Google 任务同步的服务
public class GTaskSyncService extends Service {
// 定义广播相关的常量
public final static String ACTION_STRING_NAME = "sync_action_type"; // 广播中用于标识同步操作类型的键
public final static int ACTION_START_SYNC = 0; // 开始同步操作
public final static int ACTION_CANCEL_SYNC = 1; // 取消同步操作
public final static int ACTION_INVALID = 2; // 无效操作
public final static String ACTION_STRING_NAME = "sync_action_type";
public final static String GTASK_SERVICE_BROADCAST_NAME = "net.micode.notes.gtask.remote.gtask_sync_service"; // 广播的名称
public final static String GTASK_SERVICE_BROADCAST_IS_SYNCING = "isSyncing"; // 广播中用于标识是否正在同步的键
public final static String GTASK_SERVICE_BROADCAST_PROGRESS_MSG = "progressMsg"; // 广播中用于标识同步进度信息的键
public final static int ACTION_START_SYNC = 0;
// 静态变量,用于管理同步任务
private static GTaskASyncTask mSyncTask = null; // 当前的同步任务
private static String mSyncProgress = ""; // 同步进度信息
public final static int ACTION_CANCEL_SYNC = 1;
public final static int ACTION_INVALID = 2;
public final static String GTASK_SERVICE_BROADCAST_NAME = "net.micode.notes.gtask.remote.gtask_sync_service";
public final static String GTASK_SERVICE_BROADCAST_IS_SYNCING = "isSyncing";
public final static String GTASK_SERVICE_BROADCAST_PROGRESS_MSG = "progressMsg";
private static GTaskASyncTask mSyncTask = null;
private static String mSyncProgress = "";
// 开始同步任务
private void startSync() {
if (mSyncTask == null) {
mSyncTask = new GTaskASyncTask(this, new GTaskASyncTask.OnCompleteListener() {
public void onComplete() {
mSyncTask = null; // 同步完成时,清空同步任务
sendBroadcast(""); // 发送广播通知同步完成
stopSelf(); // 停止服务
mSyncTask = null;
sendBroadcast("");
stopSelf();
}
});
sendBroadcast(""); // 发送广播通知同步开始
mSyncTask.execute(); // 执行同步任务
sendBroadcast("");
mSyncTask.execute();
}
}
// 取消同步任务
private void cancelSync() {
if (mSyncTask != null) {
mSyncTask.cancelSync(); // 调用同步任务的取消方法
mSyncTask.cancelSync();
}
}
// 服务创建时调用
@Override
public void onCreate() {
mSyncTask = null; // 初始化同步任务为 null
mSyncTask = null;
}
// 服务启动时调用
@Override
public int onStartCommand(Intent intent, int flags, int startId) {
Bundle bundle = intent.getExtras(); // 获取启动服务时传递的额外数据
Bundle bundle = intent.getExtras();
if (bundle != null && bundle.containsKey(ACTION_STRING_NAME)) {
switch (bundle.getInt(ACTION_STRING_NAME, ACTION_INVALID)) {
case ACTION_START_SYNC: // 如果是开始同步操作
startSync(); // 调用 startSync 方法开始同步
case ACTION_START_SYNC:
startSync();
break;
case ACTION_CANCEL_SYNC: // 如果是取消同步操作
cancelSync(); // 调用 cancelSync 方法取消同步
case ACTION_CANCEL_SYNC:
cancelSync();
break;
default: // 其他无效操作
default:
break;
}
return START_STICKY; // 返回 START_STICKY表示如果服务被系统杀死系统会尝试重新启动服务
return START_STICKY;
}
return super.onStartCommand(intent, flags, startId); // 如果没有传递有效的操作类型,调用父类方法
return super.onStartCommand(intent, flags, startId);
}
// 在低内存情况下调用
@Override
public void onLowMemory() {
if (mSyncTask != null) {
mSyncTask.cancelSync(); // 在低内存情况下取消同步任务
mSyncTask.cancelSync();
}
}
// 服务绑定时调用
public IBinder onBind(Intent intent) {
return null; // 该服务不支持绑定,返回 null
return null;
}
// 发送广播通知同步状态或进度
public void sendBroadcast(String msg) {
mSyncProgress = msg; // 更新同步进度信息
Intent intent = new Intent(GTASK_SERVICE_BROADCAST_NAME); // 创建广播意图
intent.putExtra(GTASK_SERVICE_BROADCAST_IS_SYNCING, mSyncTask != null); // 添加是否正在同步的信息
intent.putExtra(GTASK_SERVICE_BROADCAST_PROGRESS_MSG, msg); // 添加同步进度信息
sendBroadcast(intent); // 发送广播
mSyncProgress = msg;
Intent intent = new Intent(GTASK_SERVICE_BROADCAST_NAME);
intent.putExtra(GTASK_SERVICE_BROADCAST_IS_SYNCING, mSyncTask != null);
intent.putExtra(GTASK_SERVICE_BROADCAST_PROGRESS_MSG, msg);
sendBroadcast(intent);
}
// 静态方法,用于从 Activity 启动同步服务
public static void startSync(Activity activity) {
GTaskManager.getInstance().setActivityContext(activity); // 设置 Activity 上下文到 GTaskManager
Intent intent = new Intent(activity, GTaskSyncService.class); // 创建启动服务的意图
intent.putExtra(GTASK_SERVICE_BROADCAST_NAME, ACTION_START_SYNC); // 添加开始同步的操作类型
activity.startService(intent); // 启动服务
GTaskManager.getInstance().setActivityContext(activity);
Intent intent = new Intent(activity, GTaskSyncService.class);
intent.putExtra(GTaskSyncService.ACTION_STRING_NAME, GTaskSyncService.ACTION_START_SYNC);
activity.startService(intent);
}
// 静态方法,用于取消同步
public static void cancelSync(Context context) {
Intent intent = new Intent(context, GTaskSyncService.class); // 创建启动服务的意图
intent.putExtra(GTASK_SERVICE_BROADCAST_NAME, ACTION_CANCEL_SYNC); // 添加取消同步的操作类型
context.startService(intent); // 启动服务
Intent intent = new Intent(context, GTaskSyncService.class);
intent.putExtra(GTaskSyncService.ACTION_STRING_NAME, GTaskSyncService.ACTION_CANCEL_SYNC);
context.startService(intent);
}
// 静态方法,用于检查是否正在同步
public static boolean isSyncing() {
return mSyncTask != null; // 如果 mSyncTask 不为 null则表示正在同步
return mSyncTask != null;
}
// 静态方法,用于获取同步进度信息
public static String getProgressString() {
return mSyncProgress; // 返回同步进度信息
return mSyncProgress;
}
}

@ -15,7 +15,6 @@
*/
package net.micode.notes.model;
import android.content.ContentProviderOperation;
import android.content.ContentProviderResult;
import android.content.ContentUris;
@ -34,25 +33,16 @@ import net.micode.notes.data.Notes.TextNote;
import java.util.ArrayList;
/**
* Note ID
*/
public class Note {
// 笔记差异值
private ContentValues mNoteDiffValues;
// 笔记数据对象
private NoteData mNoteData;
// 日志标签
private static final String TAG = "Note";
/**
* ID
* @param context
* @param folderId ID
* @return ID
* Create a new note id for adding a new note to databases
*/
public static synchronized long getNewNoteId(Context context, long folderId) {
// 创建一个新的笔记
// Create a new note in the database
ContentValues values = new ContentValues();
long createdTime = System.currentTimeMillis();
values.put(NoteColumns.CREATED_DATE, createdTime);
@ -60,7 +50,6 @@ public class Note {
values.put(NoteColumns.TYPE, Notes.TYPE_NOTE);
values.put(NoteColumns.LOCAL_MODIFIED, 1);
values.put(NoteColumns.PARENT_ID, folderId);
// 插入新笔记到数据库
Uri uri = context.getContentResolver().insert(Notes.CONTENT_NOTE_URI, values);
long noteId = 0;
@ -76,81 +65,41 @@ public class Note {
return noteId;
}
/**
*
*/
public Note() {
mNoteDiffValues = new ContentValues();
mNoteData = new NoteData();
}
/**
*
* @param key
* @param value
*/
public void setNoteValue(String key, String value) {
mNoteDiffValues.put(key, value);
mNoteDiffValues.put(NoteColumns.LOCAL_MODIFIED, 1);
mNoteDiffValues.put(NoteColumns.MODIFIED_DATE, System.currentTimeMillis());
}
/**
*
* @param key
* @param value
*/
public void setTextData(String key, String value) {
mNoteData.setTextData(key, value);
}
/**
* ID
* @param id ID
*/
public void setTextDataId(long id) {
mNoteData.setTextDataId(id);
}
/**
* ID
* @return ID
*/
public long getTextDataId() {
return mNoteData.mTextDataId;
}
/**
* ID
* @param id ID
*/
public void setCallDataId(long id) {
mNoteData.setCallDataId(id);
}
/**
*
* @param key
* @param value
*/
public void setCallData(String key, String value) {
mNoteData.setCallData(key, value);
}
/**
*
* @return
*/
public boolean isLocalModified() {
return mNoteDiffValues.size() > 0 || mNoteData.isLocalModified();
}
/**
*
* @param context
* @param noteId ID
* @return
*/
public boolean syncNote(Context context, long noteId) {
if (noteId <= 0) {
throw new IllegalArgumentException("Wrong note id:" + noteId);
@ -161,14 +110,15 @@ public class Note {
}
/**
* LOCAL_MODIFIED MODIFIED_DATE
* 使
* In theory, once data changed, the note should be updated on {@link NoteColumns#LOCAL_MODIFIED} and
* {@link NoteColumns#MODIFIED_DATE}. For data safety, though update note fails, we also update the
* note data info
*/
if (context.getContentResolver().update(
ContentUris.withAppendedId(Notes.CONTENT_NOTE_URI, noteId), mNoteDiffValues, null,
null) == 0) {
Log.e(TAG, "Update note error, should not happen");
// 不返回,继续执行
// Do not return, fall through
}
mNoteDiffValues.clear();
@ -180,24 +130,17 @@ public class Note {
return true;
}
/**
*
*/
private class NoteData {
// 文本笔记数据的 ID
private long mTextDataId;
// 文本笔记数据的值
private ContentValues mTextDataValues;
// 通话笔记数据的 ID
private long mCallDataId;
// 通话笔记数据的值
private ContentValues mCallDataValues;
// 日志标签
private static final String TAG = "NoteData";
/**
* ID
*/
public NoteData() {
mTextDataValues = new ContentValues();
mCallDataValues = new ContentValues();
@ -205,18 +148,10 @@ public class Note {
mCallDataId = 0;
}
/**
*
* @return
*/
boolean isLocalModified() {
return mTextDataValues.size() > 0 || mCallDataValues.size() > 0;
}
/**
* ID
* @param id ID
*/
void setTextDataId(long id) {
if(id <= 0) {
throw new IllegalArgumentException("Text data id should larger than 0");
@ -224,10 +159,6 @@ public class Note {
mTextDataId = id;
}
/**
* ID
* @param id ID
*/
void setCallDataId(long id) {
if (id <= 0) {
throw new IllegalArgumentException("Call data id should larger than 0");
@ -235,37 +166,21 @@ public class Note {
mCallDataId = id;
}
/**
*
* @param key
* @param value
*/
void setCallData(String key, String value) {
mCallDataValues.put(key, value);
mNoteDiffValues.put(NoteColumns.LOCAL_MODIFIED, 1);
mNoteDiffValues.put(NoteColumns.MODIFIED_DATE, System.currentTimeMillis());
}
/**
*
* @param key
* @param value
*/
void setTextData(String key, String value) {
mTextDataValues.put(key, value);
mNoteDiffValues.put(NoteColumns.LOCAL_MODIFIED, 1);
mNoteDiffValues.put(NoteColumns.MODIFIED_DATE, System.currentTimeMillis());
}
/**
* ContentResolver
* @param context
* @param noteId ID
* @return Uri
*/
Uri pushIntoContentResolver(Context context, long noteId) {
/**
*
* Check for safety
*/
if (noteId <= 0) {
throw new IllegalArgumentException("Wrong note id:" + noteId);
@ -277,7 +192,6 @@ public class Note {
if(mTextDataValues.size() > 0) {
mTextDataValues.put(DataColumns.NOTE_ID, noteId);
if (mTextDataId == 0) {
// 插入新的文本笔记数据
mTextDataValues.put(DataColumns.MIME_TYPE, TextNote.CONTENT_ITEM_TYPE);
Uri uri = context.getContentResolver().insert(Notes.CONTENT_DATA_URI,
mTextDataValues);
@ -289,7 +203,6 @@ public class Note {
return null;
}
} else {
// 更新现有的文本笔记数据
builder = ContentProviderOperation.newUpdate(ContentUris.withAppendedId(
Notes.CONTENT_DATA_URI, mTextDataId));
builder.withValues(mTextDataValues);
@ -301,7 +214,6 @@ public class Note {
if(mCallDataValues.size() > 0) {
mCallDataValues.put(DataColumns.NOTE_ID, noteId);
if (mCallDataId == 0) {
// 插入新的通话笔记数据
mCallDataValues.put(DataColumns.MIME_TYPE, CallNote.CONTENT_ITEM_TYPE);
Uri uri = context.getContentResolver().insert(Notes.CONTENT_DATA_URI,
mCallDataValues);
@ -313,7 +225,6 @@ public class Note {
return null;
}
} else {
// 更新现有的通话笔记数据
builder = ContentProviderOperation.newUpdate(ContentUris.withAppendedId(
Notes.CONTENT_DATA_URI, mCallDataId));
builder.withValues(mCallDataValues);
@ -324,7 +235,6 @@ public class Note {
if (operationList.size() > 0) {
try {
// 批量执行操作
ContentProviderResult[] results = context.getContentResolver().applyBatch(
Notes.AUTHORITY, operationList);
return (results == null || results.length == 0 || results[0] == null) ? null

@ -31,41 +31,37 @@ import net.micode.notes.data.Notes.NoteColumns;
import net.micode.notes.data.Notes.TextNote;
import net.micode.notes.tool.ResourceParser.NoteBgResources;
/**
* WorkingNote
*
*/
public class WorkingNote {
// 笔记对象
// Note for the working note
private Note mNote;
// 笔记的 ID
// Note Id
private long mNoteId;
// 笔记的内容
// Note content
private String mContent;
// 笔记的模式
// Note mode
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 static final String TAG = "WorkingNote";
// 笔记是否被删除的标志
private boolean mIsDeleted;
// 笔记设置变化监听器
private NoteSettingChangedListener mNoteSettingStatusListener;
// 数据查询的投影列
public static final String[] DATA_PROJECTION = new String[] {
DataColumns.ID,
DataColumns.CONTENT,
@ -76,7 +72,6 @@ public class WorkingNote {
DataColumns.DATA4,
};
// 笔记查询的投影列
public static final String[] NOTE_PROJECTION = new String[] {
NoteColumns.PARENT_ID,
NoteColumns.ALERTED_DATE,
@ -86,32 +81,27 @@ public class WorkingNote {
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;
/**
*
* @param context
* @param folderId ID
*/
// New note construct
private WorkingNote(Context context, long folderId) {
mContext = context;
mAlertDate = 0;
@ -124,12 +114,7 @@ public class WorkingNote {
mWidgetType = Notes.TYPE_WIDGET_INVALIDE;
}
/**
*
* @param context
* @param noteId ID
* @param folderId ID
*/
// Existing note construct
private WorkingNote(Context context, long noteId, long folderId) {
mContext = context;
mNoteId = noteId;
@ -139,18 +124,13 @@ public class WorkingNote {
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);
@ -163,15 +143,10 @@ public class WorkingNote {
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)
@ -180,15 +155,12 @@ public class WorkingNote {
if (cursor != null) {
if (cursor.moveToFirst()) {
do {
// 获取数据的 MIME 类型
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);
@ -202,15 +174,6 @@ public class WorkingNote {
}
}
/**
*
* @param context
* @param folderId ID
* @param widgetId ID
* @param widgetType
* @param defaultBgColorId ID
* @return WorkingNote
*/
public static WorkingNote createEmptyNote(Context context, long folderId, int widgetId,
int widgetType, int defaultBgColorId) {
WorkingNote note = new WorkingNote(context, folderId);
@ -220,34 +183,23 @@ public class WorkingNote {
return note;
}
/**
*
* @param context
* @param id ID
* @return WorkingNote
*/
public static WorkingNote load(Context context, long id) {
return new WorkingNote(context, id, 0);
}
/**
*
* @return
*/
public synchronized boolean saveNote() {
if (isWorthSaving()) {
if (!existInDatabase()) {
// 如果笔记不存在于数据库中,创建新的笔记 ID
if ((mNoteId = Note.getNewNoteId(mContext, mFolderId)) == 0) {
Log.e(TAG, "Create new note fail with id:" + mNoteId);
return false;
}
}
// 同步笔记数据
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
@ -260,18 +212,10 @@ public class WorkingNote {
}
}
/**
*
* @return
*/
public boolean existInDatabase() {
return mNoteId > 0;
}
/**
*
* @return
*/
private boolean isWorthSaving() {
if (mIsDeleted || (!existInDatabase() && TextUtils.isEmpty(mContent))
|| (existInDatabase() && !mNote.isLocalModified())) {
@ -281,19 +225,10 @@ public class WorkingNote {
}
}
/**
*
* @param l
*/
public void setOnSettingStatusChangedListener(NoteSettingChangedListener l) {
mNoteSettingStatusListener = l;
}
/**
*
* @param date
* @param set
*/
public void setAlertDate(long date, boolean set) {
if (date != mAlertDate) {
mAlertDate = date;
@ -304,10 +239,6 @@ public class WorkingNote {
}
}
/**
*
* @param mark
*/
public void markDeleted(boolean mark) {
mIsDeleted = mark;
if (mWidgetId != AppWidgetManager.INVALID_APPWIDGET_ID
@ -316,10 +247,6 @@ public class WorkingNote {
}
}
/**
* ID
* @param id ID
*/
public void setBgColorId(int id) {
if (id != mBgColorId) {
mBgColorId = id;
@ -330,10 +257,6 @@ public class WorkingNote {
}
}
/**
*
* @param mode
*/
public void setCheckListMode(int mode) {
if (mMode != mode) {
if (mNoteSettingStatusListener != null) {
@ -344,10 +267,6 @@ public class WorkingNote {
}
}
/**
*
* @param type
*/
public void setWidgetType(int type) {
if (type != mWidgetType) {
mWidgetType = type;
@ -355,10 +274,6 @@ public class WorkingNote {
}
}
/**
* ID
* @param id ID
*/
public void setWidgetId(int id) {
if (id != mWidgetId) {
mWidgetId = id;
@ -366,10 +281,6 @@ public class WorkingNote {
}
}
/**
*
* @param text
*/
public void setWorkingText(String text) {
if (!TextUtils.equals(mContent, text)) {
mContent = text;
@ -377,138 +288,80 @@ public class WorkingNote {
}
}
/**
*
* @param phoneNumber
* @param callDate
*/
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));
}
/**
*
* @return
*/
public boolean hasClockAlert() {
return (mAlertDate > 0 ? true : false);
}
/**
*
* @return
*/
public String getContent() {
return mContent;
}
/**
*
* @return
*/
public long getAlertDate() {
return mAlertDate;
}
/**
*
* @return
*/
public long getModifiedDate() {
return mModifiedDate;
}
/**
* ID
* @return ID
*/
public int getBgColorResId() {
return NoteBgResources.getNoteBgResource(mBgColorId);
}
/**
* ID
* @return ID
*/
public int getBgColorId() {
return mBgColorId;
}
/**
* ID
* @return ID
*/
public int getTitleBgResId() {
return NoteBgResources.getNoteTitleBgResource(mBgColorId);
}
/**
*
* @return
*/
public int getCheckListMode() {
return mMode;
}
/**
* ID
* @return ID
*/
public long getNoteId() {
return mNoteId;
}
/**
* ID
* @return ID
*/
public long getFolderId() {
return mFolderId;
}
/**
* ID
* @return ID
*/
public int getWidgetId() {
return mWidgetId;
}
/**
*
* @return
*/
public int getWidgetType() {
return mWidgetType;
}
/**
*
*/
public interface NoteSettingChangedListener {
/**
*
* Called when the background color of current note has just changed
*/
void onBackgroundColorChanged();
/**
*
* @param date
* @param set
* Called when user set clock
*/
void onClockAlertChanged(long date, boolean set);
/**
*
* Call when user create note from widget
*/
void onWidgetChanged();
/**
*
* @param oldMode
* @param newMode
* 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);
}

@ -1,69 +0,0 @@
<?xml version="1.0" encoding="utf-8"?>
<!-- Copyright (c) 2010-2011, The MiCode Open Source Community (www.micode.net)
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
-->
<resources>
<style name="TextAppearanceSuper">
<item name="android:textSize">@dimen/text_font_size_super</item>
<item name="android:textColorLink">#0000ff</item>
</style>
<style name="TextAppearanceLarge">
<item name="android:textSize">@dimen/text_font_size_large</item>
<item name="android:textColorLink">#0000ff</item>
</style>
<style name="TextAppearanceMedium">
<item name="android:textSize">@dimen/text_font_size_medium</item>
<item name="android:textColorLink">#0000ff</item>
</style>
<style name="TextAppearanceNormal">
<item name="android:textSize">@dimen/text_font_size_normal</item>
<item name="android:textColorLink">#0000ff</item>
</style>
<style name="TextAppearancePrimaryItem">
<item name="android:textSize">@dimen/text_font_size_normal</item>
<item name="android:textColor">@color/primary_text_dark</item>
</style>
<style name="TextAppearanceSecondaryItem">
<item name="android:textSize">@dimen/text_font_size_small</item>
<item name="android:textColor">@color/secondary_text_dark</item>
</style>
<style name="TextAppearanceUnderMenuIcon">
<item name="android:textSize">@dimen/text_font_size_normal</item>
<item name="android:textColor">@android:color/black</item>
</style>
<style name="HighlightTextAppearancePrimary">
<item name="android:textSize">@dimen/text_font_size_normal</item>
<item name="android:textColor">@color/primary_text_dark</item>
</style>
<style name="HighlightTextAppearanceSecondary">
<item name="android:textSize">@dimen/text_font_size_small</item>
<item name="android:textColor">@color/secondary_text_dark</item>
</style>
<style name="NoteTheme" parent="@android:style/Theme.Holo.Light">
<item name="android:actionBarStyle">@style/NoteActionBarStyle</item>
</style>
<style name="NoteActionBarStyle" parent="@android:style/Widget.Holo.Light.ActionBar.Solid">
<!--<item name="android:displayOptions" />-->
<item name="android:visibility">visible</item>
</style>
</resources>

Binary file not shown.
Loading…
Cancel
Save