Compare commits

..

3 Commits
main ... master

@ -1,3 +1,2 @@
# xiaominote_code_analysis
教辅求捞捞o(╥﹏╥)o

@ -1 +0,0 @@
这是最终版本的小米便签泛读文档

@ -1,15 +1,17 @@
/*
* (c) 2010-2011, MiCode (www.micode.net)
* Copyright (c) 2010-2011, The MiCode Open Source Community (www.micode.net)
*
* 使 Apache License, Version 2.0
* 使
*
* 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.
*/
package net.micode.notes.data;
@ -24,80 +26,70 @@ import android.util.Log;
import java.util.HashMap;
/**
*
* 使
*
* @ProjectName: minode
* @Package: net.micode.notes.data
* @ClassName: Contact
* @Description:
* @Author:
* @Date: 2023-12-16 17:57
*/
public class Contact {
/**
*
*/
private static HashMap<String, String> sContactCache;
/**
*
*/
private static final String TAG = "Contact";
/**
* SQL
*
*/
private static final String CALLER_ID_SELECTION = "PHONE_NUMBERS_EQUAL(" + Phone.NUMBER
+ ",?) AND " + Data.MIMETYPE + "='" + Phone.CONTENT_ITEM_TYPE + "'"
+ " AND " + Data.RAW_CONTACT_ID + " IN "
+ ",?) AND " + Data.MIMETYPE + "='" + Phone.CONTENT_ITEM_TYPE + "'"
+ " AND " + Data.RAW_CONTACT_ID + " IN "
+ "(SELECT raw_contact_id "
+ " FROM phone_lookup"
+ " WHERE min_match = '+')";
/**
*
*
* @param context
* @param phoneNumber
* @return null
*/
/**
* @method getContact
* @description
* @date: 2023-12-16 19:24
* @author:
* @return string
*/
public static String getContact(Context context, String phoneNumber) {
// 初始化缓存
if (sContactCache == null) {
if(sContactCache == null) {
sContactCache = new HashMap<String, String>();
}
// 首先检查缓存中是否存在该电话号码对应的联系人姓名
if (sContactCache.containsKey(phoneNumber)) {
// 查找HashMap中是否有phoneNumber的信息
// 2023-12-16 19:43
if(sContactCache.containsKey(phoneNumber)) {
return sContactCache.get(phoneNumber);
}
// 将 SQL 语句中的占位符替换为实际的电话号码匹配模式
String selection = CALLER_ID_SELECTION.replace("+",
PhoneNumberUtils.toCallerIDMinMatch(phoneNumber));
// 执行数据库查询操作
// 查找数据库中phoneNumber的信息
// 2023-12-16 19:43
Cursor cursor = context.getContentResolver().query(
Data.CONTENT_URI,
new String[]{Phone.DISPLAY_NAME},
new String [] { Phone.DISPLAY_NAME },
selection,
new String[]{phoneNumber},
new String[] { phoneNumber },
null);
// 处理查询结果
// 检查是否查询到联系人找到则将相关信息加入HashMap中未找到则处理异常
// 2023-12-16 19:41
if (cursor != null && cursor.moveToFirst()) {
try {
// 获取联系人姓名并缓存
String name = cursor.getString(0);
sContactCache.put(phoneNumber, name);
return name;
} catch (IndexOutOfBoundsException e) {
// 记录异常信息
Log.e(TAG, "游标获取字符串时出错:" + e.toString());
Log.e(TAG, " Cursor get string error " + e.toString());
return null;
} finally {
// 确保游标关闭,释放资源
cursor.close();
}
} else {
// 记录未找到匹配联系人的信息
Log.d(TAG, "未找到与号码匹配的联系人:" + phoneNumber);
Log.d(TAG, "No contact matched with number:" + phoneNumber);
return null;
}
}
}

@ -1,54 +1,40 @@
/*
* (c) 2010-2011, MiCode (www.micode.net)
* Copyright (c) 2010-2011, The MiCode Open Source Community (www.micode.net)
*
* 使 Apache License, Version 2.0
* 使
*
* 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.
*/
package net.micode.notes.data;
import android.net.Uri;
/**
* Notes 便使 URI
* 便便
*/
public class Notes {
/**
* ContentProvider
*/
public static final String AUTHORITY = "micode_notes";
/**
*
*/
public static final String TAG = "Notes";
public static final int TYPE_NOTE = 0;
public static final int TYPE_FOLDER = 1;
public static final int TYPE_SYSTEM = 2;
/**
* 便
* 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 static final int TYPE_NOTE = 0; // 普通便签
public static final int TYPE_FOLDER = 1; // 文件夹
public static final int TYPE_SYSTEM = 2; // 系统便签
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; // TODO: 2023/12/25 有trash文件夹
/**
* ID
*/
public static final int ID_ROOT_FOLDER = 0; // 根文件夹 ID
public static final int ID_TEMPARAY_FOLDER = -1; // 临时文件夹 ID
public static final int ID_CALL_RECORD_FOLDER = -2; // 通话记录文件夹 ID
public static final int ID_TRASH_FOLDER = -3; // 回收站文件夹 ID
/**
*
*/
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";
@ -56,98 +42,244 @@ public class Notes {
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_INVALID = -1; // 无效小部件类型
public static final int TYPE_WIDGET_2X = 0; // 2x 小部件
public static final int TYPE_WIDGET_4X = 1; // 4x 小部件
public static final int TYPE_WIDGET_INVALIDE = -1;
public static final int TYPE_WIDGET_2X = 0;
public static final int TYPE_WIDGET_4X = 1;
/**
* 便 MIME
*/
public static class DataConstants {
public static final String NOTE = TextNote.CONTENT_ITEM_TYPE; // 文本便签的 MIME 类型
public static final String CALL_NOTE = CallNote.CONTENT_ITEM_TYPE; // 通话记录便签的 MIME 类型
public static final String NOTE = TextNote.CONTENT_ITEM_TYPE;
public static final String CALL_NOTE = CallNote.CONTENT_ITEM_TYPE;
}
/**
* 便 URI
* Uri to query all notes and folders
*/
public static final Uri CONTENT_NOTE_URI = Uri.parse("content://" + AUTHORITY + "/note");
/**
* URI
* Uri to query data
*/
public static final Uri CONTENT_DATA_URI = Uri.parse("content://" + AUTHORITY + "/data");
/**
* 便便
*/
public interface NoteColumns {
public static final String ID = "_id"; // 便签 ID
public static final String PARENT_ID = "parent_id"; // 父文件夹 ID
public static final String CREATED_DATE = "created_date"; // 创建日期
public static final String MODIFIED_DATE = "modified_date"; // 修改日期
public static final String ALERTED_DATE = "alert_date"; // 提醒日期
public static final String SNIPPET = "snippet"; // 便签摘要
public static final String WIDGET_ID = "widget_id"; // 小部件 ID
public static final String WIDGET_TYPE = "widget_type"; // 小部件类型
public static final String BG_COLOR_ID = "bg_color_id"; // 背景颜色 ID
public static final String HAS_ATTACHMENT = "has_attachment"; // 是否有附件
public static final String NOTES_COUNT = "notes_count"; // 便签数量
public static final String TYPE = "type"; // 便签类型
public static final String SYNC_ID = "sync_id"; // 同步 ID
public static final String LOCAL_MODIFIED = "local_modified"; // 本地修改标记
public static final String ORIGIN_PARENT_ID = "origin_parent_id"; // 原始父文件夹 ID
public static final String GTASK_ID = "gtask_id"; // Google 任务 ID
public static final String VERSION = "version"; // 版本
public static final String TOP = "top"; // 置顶标记
/**
* The unique ID for a row
* <P> Type: INTEGER (long) </P>
*/
public static final String ID = "_id";
/**
* The parent's id for note or folder
* <P> Type: INTEGER (long) </P>
*/
public static final String PARENT_ID = "parent_id";
/**
* 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";
/**
* Alert date
* <P> Type: INTEGER (long) </P>
*/
public static final String ALERTED_DATE = "alert_date";
/**
* Folder's name or text content of note
* <P> Type: TEXT </P>
*/
public static final String SNIPPET = "snippet";
/**
* Note's widget id
* <P> Type: INTEGER (long) </P>
*/
public static final String WIDGET_ID = "widget_id";
/**
* Note's widget type
* <P> Type: INTEGER (long) </P>
*/
public static final String WIDGET_TYPE = "widget_type";
/**
* Note's background color's id
* <P> Type: INTEGER (long) </P>
*/
public static final String BG_COLOR_ID = "bg_color_id";
/**
* For text note, it doesn't has attachment, for multi-media
* note, it has at least one attachment
* <P> Type: INTEGER </P>
*/
public static final String HAS_ATTACHMENT = "has_attachment";
/**
* Folder's count of notes
* <P> Type: INTEGER (long) </P>
*/
public static final String NOTES_COUNT = "notes_count";
/**
* 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";
/**
* The gtask id
* <P> Type : TEXT </P>
*/
public static final String GTASK_ID = "gtask_id";
/**
* the version code
* <p> type : integer (long) </p>
*/
public static final String VERSION = "version";
/**
* set top state
* <p> type : integer (long) </p>
*/
public static final String TOP = "top";
}
/**
*
*/
public interface DataColumns {
public static final String ID = "_id"; // 数据 ID
public static final String MIME_TYPE = "mime_type"; // MIME 类型
public static final String NOTE_ID = "note_id"; // 便签 ID
public static final String CREATED_DATE = "created_date"; // 创建日期
public static final String MODIFIED_DATE = "modified_date"; // 修改日期
public static final String CONTENT = "content"; // 内容
public static final String DATA1 = "data1"; // 数据 1
public static final String DATA2 = "data2"; // 数据 2
public static final String DATA3 = "data3"; // 数据 3
public static final String DATA4 = "data4"; // 数据 4
public static final String DATA5 = "data5"; // 数据 5
/**
* The unique ID for a row
* <P> Type: INTEGER (long) </P>
*/
public static final String ID = "_id";
/**
* The MIME type of the item represented by this row.
* <P> Type: Text </P>
*/
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 final String NOTE_ID = "note_id";
/**
* 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";
/**
* Data's content
* <P> Type: TEXT </P>
*/
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 final String DATA1 = "data1";
/**
* Generic data column, the meaning is {@link #MIMETYPE} specific, used for
* integer data type
* <P> Type: INTEGER </P>
*/
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 final String DATA3 = "data3";
/**
* Generic data column, the meaning is {@link #MIMETYPE} specific, used for
* TEXT data type
* <P> Type: TEXT </P>
*/
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 final String DATA5 = "data5";
}
/**
* 便 DataColumns 便 URI
*/
public static final class TextNote implements DataColumns {
public static final String MODE = DATA1; // 模式
/**
* Mode to indicate the text in check list mode or not
* <P> Type: Integer 1:check list mode 0: normal mode </P>
*/
public static final String MODE = DATA1;
public static final int MODE_CHECK_LIST = 1;
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_TYPE = "vnd.android.cursor.dir/text_note"; // 文本便签的目录 MIME 类型
public static final String CONTENT_ITEM_TYPE = "vnd.android.cursor.item/text_note"; // 文本便签的项 MIME 类型
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"); // 文本便签的 URI
public static final Uri CONTENT_URI = Uri.parse("content://" + AUTHORITY + "/text_note");
}
/**
* 便 DataColumns 便 URI
*/
public static final class CallNote implements DataColumns {
public static final String CALL_DATE = DATA1; // 通话日期
public static final String PHONE_NUMBER = DATA3; // 电话号码
/**
* Call date for this record
* <P> Type: INTEGER (long) </P>
*/
public static final String CALL_DATE = DATA1;
public static final String CONTENT_TYPE = "vnd.android.cursor.dir/call_note"; // 通话记录便签的目录 MIME 类型
public static final String CONTENT_ITEM_TYPE = "vnd.android.cursor.item/call_note"; // 通话记录便签的项 MIME 类型
/**
* Phone number for this record
* <P> Type: TEXT </P>
*/
public static final String PHONE_NUMBER = DATA3;
public static final Uri CONTENT_URI = Uri.parse("content://" + AUTHORITY + "/call_note"); // 通话记录便签的 URI
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");
}
}

@ -1,15 +1,17 @@
/*
* (c) 2010-2011, MiCode (www.micode.net)
* Copyright (c) 2010-2011, The MiCode Open Source Community (www.micode.net)
*
* 使 Apache License, Version 2.0
* 使
*
* 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.
*/
package net.micode.notes.data;
@ -20,48 +22,26 @@ import android.database.sqlite.SQLiteDatabase;
import android.database.sqlite.SQLiteOpenHelper;
import android.util.Log;
/**
* NotesDatabaseHelper 便
* SQLiteOpenHelper
* 访线
*/
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 = 5;
/**
*
*/
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
*/
private static final String CREATE_NOTE_TABLE_SQL =
"CREATE TABLE " + TABLE.NOTE + "(" +
NoteColumns.ID + " INTEGER PRIMARY KEY," +
@ -84,9 +64,6 @@ public class NotesDatabaseHelper extends SQLiteOpenHelper {
NoteColumns.TOP + " INTEGER NOT NULL DEFAULT 0" +
")";
/**
* SQL
*/
private static final String CREATE_DATA_TABLE_SQL =
"CREATE TABLE " + TABLE.DATA + "(" +
DataColumns.ID + " INTEGER PRIMARY KEY," +
@ -102,38 +79,146 @@ public class NotesDatabaseHelper extends SQLiteOpenHelper {
DataColumns.DATA5 + " TEXT NOT NULL DEFAULT ''" +
")";
/**
* 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 + ");";
/**
*
* @param context
* Increase folder's note count when move note to the folder
*/
private static final String NOTE_INCREASE_FOLDER_COUNT_ON_UPDATE_TRIGGER =
"CREATE TRIGGER increase_folder_count_on_update "+
" AFTER UPDATE OF " + NoteColumns.PARENT_ID + " ON " + TABLE.NOTE +
" BEGIN " +
" UPDATE " + TABLE.NOTE +
" SET " + NoteColumns.NOTES_COUNT + "=" + NoteColumns.NOTES_COUNT + " + 1" +
" WHERE " + NoteColumns.ID + "=new." + NoteColumns.PARENT_ID + ";" +
" END";
/**
* Decrease folder's note count when move note from folder
*/
private static final String NOTE_DECREASE_FOLDER_COUNT_ON_UPDATE_TRIGGER =
"CREATE TRIGGER decrease_folder_count_on_update " +
" AFTER UPDATE OF " + NoteColumns.PARENT_ID + " ON " + TABLE.NOTE +
" BEGIN " +
" UPDATE " + TABLE.NOTE +
" SET " + NoteColumns.NOTES_COUNT + "=" + NoteColumns.NOTES_COUNT + "-1" +
" WHERE " + NoteColumns.ID + "=old." + NoteColumns.PARENT_ID +
" AND " + NoteColumns.NOTES_COUNT + ">0" + ";" +
" END";
/**
* Increase folder's note count when insert new note to the folder
*/
private static final String NOTE_INCREASE_FOLDER_COUNT_ON_INSERT_TRIGGER =
"CREATE TRIGGER increase_folder_count_on_insert " +
" AFTER INSERT ON " + TABLE.NOTE +
" BEGIN " +
" UPDATE " + TABLE.NOTE +
" SET " + NoteColumns.NOTES_COUNT + "=" + NoteColumns.NOTES_COUNT + " + 1" +
" WHERE " + NoteColumns.ID + "=new." + NoteColumns.PARENT_ID + ";" +
" END";
/**
* Decrease folder's note count when delete note from the folder
*/
private static final String NOTE_DECREASE_FOLDER_COUNT_ON_DELETE_TRIGGER =
"CREATE TRIGGER decrease_folder_count_on_delete " +
" AFTER DELETE ON " + TABLE.NOTE +
" BEGIN " +
" UPDATE " + TABLE.NOTE +
" SET " + NoteColumns.NOTES_COUNT + "=" + NoteColumns.NOTES_COUNT + "-1" +
" WHERE " + NoteColumns.ID + "=old." + NoteColumns.PARENT_ID +
" AND " + NoteColumns.NOTES_COUNT + ">0;" +
" END";
/**
* Update note's content when insert data with type {@link DataConstants#NOTE}
*/
private static final String DATA_UPDATE_NOTE_CONTENT_ON_INSERT_TRIGGER =
"CREATE TRIGGER update_note_content_on_insert " +
" AFTER INSERT ON " + TABLE.DATA +
" WHEN new." + DataColumns.MIME_TYPE + "='" + DataConstants.NOTE + "'" +
" BEGIN" +
" UPDATE " + TABLE.NOTE +
" SET " + NoteColumns.SNIPPET + "=new." + DataColumns.CONTENT +
" WHERE " + NoteColumns.ID + "=new." + DataColumns.NOTE_ID + ";" +
" END";
/**
* Update note's content when data with {@link DataConstants#NOTE} type has changed
*/
private static final String DATA_UPDATE_NOTE_CONTENT_ON_UPDATE_TRIGGER =
"CREATE TRIGGER update_note_content_on_update " +
" AFTER UPDATE ON " + TABLE.DATA +
" WHEN old." + DataColumns.MIME_TYPE + "='" + DataConstants.NOTE + "'" +
" BEGIN" +
" UPDATE " + TABLE.NOTE +
" SET " + NoteColumns.SNIPPET + "=new." + DataColumns.CONTENT +
" WHERE " + NoteColumns.ID + "=new." + DataColumns.NOTE_ID + ";" +
" END";
/**
* Update note's content when data with {@link DataConstants#NOTE} type has deleted
*/
private static final String DATA_UPDATE_NOTE_CONTENT_ON_DELETE_TRIGGER =
"CREATE TRIGGER update_note_content_on_delete " +
" AFTER delete ON " + TABLE.DATA +
" WHEN old." + DataColumns.MIME_TYPE + "='" + DataConstants.NOTE + "'" +
" BEGIN" +
" UPDATE " + TABLE.NOTE +
" SET " + NoteColumns.SNIPPET + "=''" +
" WHERE " + NoteColumns.ID + "=old." + DataColumns.NOTE_ID + ";" +
" END";
/**
* Delete datas belong to note which has been deleted
*/
private static final String NOTE_DELETE_DATA_ON_DELETE_TRIGGER =
"CREATE TRIGGER delete_data_on_delete " +
" AFTER DELETE ON " + TABLE.NOTE +
" BEGIN" +
" DELETE FROM " + TABLE.DATA +
" WHERE " + DataColumns.NOTE_ID + "=old." + NoteColumns.ID + ";" +
" END";
/**
* Delete notes belong to folder which has been deleted
*/
private static final String FOLDER_DELETE_NOTES_ON_DELETE_TRIGGER =
"CREATE TRIGGER folder_delete_notes_on_delete " +
" AFTER DELETE ON " + TABLE.NOTE +
" BEGIN" +
" DELETE FROM " + TABLE.NOTE +
" WHERE " + NoteColumns.PARENT_ID + "=old." + NoteColumns.ID + ";" +
" END";
/**
* Move notes belong to folder which has been moved to trash folder
*/
private static final String FOLDER_MOVE_NOTES_ON_TRASH_TRIGGER =
"CREATE TRIGGER folder_move_notes_on_trash " +
" AFTER UPDATE ON " + TABLE.NOTE +
" WHEN new." + NoteColumns.PARENT_ID + "=" + Notes.ID_TRASH_FOLER +
" BEGIN" +
" UPDATE " + TABLE.NOTE +
" SET " + NoteColumns.PARENT_ID + "=" + Notes.ID_TRASH_FOLER +
" WHERE " + NoteColumns.PARENT_ID + "=old." + NoteColumns.ID + ";" +
" END";
public NotesDatabaseHelper(Context context) {
super(context, DB_NAME, null, DB_VERSION);
}
/**
* 便
* @param db
*/
public void createNoteTable(SQLiteDatabase db) {
db.execSQL(CREATE_NOTE_TABLE_SQL);
reCreateNoteTableTriggers(db);
createSystemFolder(db);
Log.d(TAG, "便签表已创建");
Log.d(TAG, "note table has been created");
}
/**
* 便
* @param db
*/
private void reCreateNoteTableTriggers(SQLiteDatabase db) {
// 删除旧触发器
db.execSQL("DROP TRIGGER IF EXISTS increase_folder_count_on_update");
db.execSQL("DROP TRIGGER IF EXISTS decrease_folder_count_on_update");
db.execSQL("DROP TRIGGER IF EXISTS decrease_folder_count_on_delete");
@ -142,7 +227,6 @@ public class NotesDatabaseHelper extends SQLiteOpenHelper {
db.execSQL("DROP TRIGGER IF EXISTS folder_delete_notes_on_delete");
db.execSQL("DROP TRIGGER IF EXISTS folder_move_notes_on_trash");
// 创建新触发器
db.execSQL(NOTE_INCREASE_FOLDER_COUNT_ON_UPDATE_TRIGGER);
db.execSQL(NOTE_DECREASE_FOLDER_COUNT_ON_UPDATE_TRIGGER);
db.execSQL(NOTE_DECREASE_FOLDER_COUNT_ON_DELETE_TRIGGER);
@ -152,61 +236,58 @@ public class NotesDatabaseHelper extends SQLiteOpenHelper {
db.execSQL(FOLDER_MOVE_NOTES_ON_TRASH_TRIGGER);
}
/**
*
* @param db
*/
private void createSystemFolder(SQLiteDatabase db) {
ContentValues values = new ContentValues();
/**
* call record foler for call notes
*/
values.put(NoteColumns.ID, Notes.ID_CALL_RECORD_FOLDER);
values.put(NoteColumns.TYPE, Notes.TYPE_SYSTEM);
db.insert(TABLE.NOTE, null, values);
/**
* root folder which is default folder
*/
values.clear();
values.put(NoteColumns.ID, Notes.ID_ROOT_FOLDER);
values.put(NoteColumns.TYPE, Notes.TYPE_SYSTEM);
db.insert(TABLE.NOTE, null, values);
/**
* temporary folder which is used for moving note
*/
values.clear();
values.put(NoteColumns.ID, Notes.ID_TEMPARAY_FOLDER);
values.put(NoteColumns.TYPE, Notes.TYPE_SYSTEM);
db.insert(TABLE.NOTE, null, values);
/**
* create trash folder
*/
values.clear();
values.put(NoteColumns.ID, Notes.ID_TRASH_FOLDER);
values.put(NoteColumns.ID, Notes.ID_TRASH_FOLER);
values.put(NoteColumns.TYPE, Notes.TYPE_SYSTEM);
db.insert(TABLE.NOTE, null, values);
}
/**
*
* @param db
*/
public void createDataTable(SQLiteDatabase db) {
db.execSQL(CREATE_DATA_TABLE_SQL);
reCreateDataTableTriggers(db);
db.execSQL(CREATE_DATA_NOTE_ID_INDEX_SQL);
Log.d(TAG, "数据表已创建");
Log.d(TAG, "data table has been created");
}
/**
*
* @param db
*/
private void reCreateDataTableTriggers(SQLiteDatabase db) {
// 删除旧触发器
db.execSQL("DROP TRIGGER IF EXISTS update_note_content_on_insert");
db.execSQL("DROP TRIGGER IF EXISTS update_note_content_on_update");
db.execSQL("DROP TRIGGER IF EXISTS update_note_content_on_delete");
// 创建新触发器
db.execSQL(DATA_UPDATE_NOTE_CONTENT_ON_INSERT_TRIGGER);
db.execSQL(DATA_UPDATE_NOTE_CONTENT_ON_UPDATE_TRIGGER);
db.execSQL(DATA_UPDATE_NOTE_CONTENT_ON_DELETE_TRIGGER);
}
/**
*
* @param context
* @return
*/
static synchronized NotesDatabaseHelper getInstance(Context context) {
if (mInstance == null) {
mInstance = new NotesDatabaseHelper(context);
@ -214,22 +295,12 @@ public class NotesDatabaseHelper extends SQLiteOpenHelper {
return mInstance;
}
/**
*
* @param db
*/
@Override
public void onCreate(SQLiteDatabase db) {
createNoteTable(db);
createDataTable(db);
}
/**
*
* @param db
* @param oldVersion
* @param newVersion
*/
@Override
public void onUpgrade(SQLiteDatabase db, int oldVersion, int newVersion) {
boolean reCreateTriggers = false;
@ -264,14 +335,10 @@ public class NotesDatabaseHelper extends SQLiteOpenHelper {
if (oldVersion != newVersion) {
throw new IllegalStateException("Upgrade notes database to version " + newVersion
+ " fails");
+ "fails");
}
}
/**
* 2
* @param db
*/
private void upgradeToV2(SQLiteDatabase db) {
db.execSQL("DROP TABLE IF EXISTS " + TABLE.NOTE);
db.execSQL("DROP TABLE IF EXISTS " + TABLE.DATA);
@ -279,39 +346,28 @@ public class NotesDatabaseHelper extends SQLiteOpenHelper {
createDataTable(db);
}
/**
* 3
* @param db
*/
private void upgradeToV3(SQLiteDatabase db) {
// drop unused triggers
db.execSQL("DROP TRIGGER IF EXISTS update_note_modified_date_on_insert");
db.execSQL("DROP TRIGGER IF EXISTS update_note_modified_date_on_delete");
db.execSQL("DROP TRIGGER IF EXISTS update_note_modified_date_on_update");
// add a column for gtask id
db.execSQL("ALTER TABLE " + TABLE.NOTE + " ADD COLUMN " + NoteColumns.GTASK_ID
+ " TEXT NOT NULL DEFAULT ''");
// add a trash system folder
ContentValues values = new ContentValues();
values.put(NoteColumns.ID, Notes.ID_TRASH_FOLDER);
values.put(NoteColumns.ID, Notes.ID_TRASH_FOLER);
values.put(NoteColumns.TYPE, Notes.TYPE_SYSTEM);
db.insert(TABLE.NOTE, null, values);
}
/**
* 4
* @param db
*/
private void upgradeToV4(SQLiteDatabase db) {
db.execSQL("ALTER TABLE " + TABLE.NOTE + " ADD COLUMN " + NoteColumns.VERSION
+ " INTEGER NOT NULL DEFAULT 0");
}
/**
* 5
* @param db
*/
private void upgradeToV5(SQLiteDatabase db) {
db.execSQL("ALTER TABLE " + TABLE.NOTE + " ADD COLUMN " + NoteColumns.TOP
+ " INTEGER NOT NULL DEFAULT 0");
}
}

@ -1,19 +1,22 @@
/*
* (c) 2010-2011, MiCode (www.micode.net)
* Copyright (c) 2010-2011, The MiCode Open Source Community (www.micode.net)
*
* 使 Apache License, Version 2.0
* 使
*
* 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.
*/
package net.micode.notes.data;
import android.app.SearchManager;
import android.content.ContentProvider;
import android.content.ContentUris;
@ -31,40 +34,23 @@ import net.micode.notes.data.Notes.DataColumns;
import net.micode.notes.data.Notes.NoteColumns;
import net.micode.notes.data.NotesDatabaseHelper.TABLE;
/**
* NotesProvider ContentProvider便
* 便
* UriMatcher URI
*/
public class NotesProvider extends ContentProvider {
/**
* URI UriMatcher
*/
private static final UriMatcher mMatcher;
/**
* NotesDatabaseHelper
*/
private NotesDatabaseHelper mHelper;
/**
*
*/
private static final String TAG = "NotesProvider";
/**
* URI
*/
private static final int URI_NOTE = 1; // 便签表的 URI
private static final int URI_NOTE_ITEM = 2; // 便签表中单个便签的 URI
private static final int URI_DATA = 3; // 数据表的 URI
private static final int URI_DATA_ITEM = 4; // 数据表中单个数据的 URI
private static final int URI_SEARCH = 5; // 搜索 URI
private static final int URI_SEARCH_SUGGEST = 6; // 搜索建议 URI
private static final int URI_NOTE = 1;
private static final int URI_NOTE_ITEM = 2;
private static final int URI_DATA = 3;
private static final int URI_DATA_ITEM = 4;
private static final int URI_SEARCH = 5;
private static final int URI_SEARCH_SUGGEST = 6;
/**
* UriMatcher URI
*/
static {
mMatcher = new UriMatcher(UriMatcher.NO_MATCH);
mMatcher.addURI(Notes.AUTHORITY, "note", URI_NOTE);
@ -77,45 +63,29 @@ public class NotesProvider extends ContentProvider {
}
/**
*
* x'0A' represents the '\n' character in sqlite. For title and content in the search result,
* we will trim '\n' and white space in order to show more information.
*/
private static final String NOTES_SEARCH_PROJECTION = NoteColumns.ID + ","
+ NoteColumns.ID + " AS " + SearchManager.SUGGEST_COLUMN_INTENT_EXTRA_DATA + ","
+ "TRIM(REPLACE(" + NoteColumns.SNIPPET + ", x'0A','')) AS " + SearchManager.SUGGEST_COLUMN_TEXT_1 + ","
+ "TRIM(REPLACE(" + NoteColumns.SNIPPET + ", x'0A','')) AS " + SearchManager.SUGGEST_COLUMN_TEXT_2 + ","
+ R.drawable.search_result + " AS " + SearchManager.SUGGEST_COLUMN_ICON_1 + ","
+ "'" + Intent.ACTION_VIEW + "' AS " + SearchManager.SUGGEST_COLUMN_INTENT_ACTION + ","
+ "'" + Notes.TextNote.CONTENT_TYPE + "' AS " + SearchManager.SUGGEST_COLUMN_INTENT_DATA;
+ NoteColumns.ID + " AS " + SearchManager.SUGGEST_COLUMN_INTENT_EXTRA_DATA + ","
+ "TRIM(REPLACE(" + NoteColumns.SNIPPET + ", x'0A','')) AS " + SearchManager.SUGGEST_COLUMN_TEXT_1 + ","
+ "TRIM(REPLACE(" + NoteColumns.SNIPPET + ", x'0A','')) AS " + SearchManager.SUGGEST_COLUMN_TEXT_2 + ","
+ R.drawable.search_result + " AS " + SearchManager.SUGGEST_COLUMN_ICON_1 + ","
+ "'" + Intent.ACTION_VIEW + "' AS " + SearchManager.SUGGEST_COLUMN_INTENT_ACTION + ","
+ "'" + Notes.TextNote.CONTENT_TYPE + "' AS " + SearchManager.SUGGEST_COLUMN_INTENT_DATA;
/**
* 便 SQL
*/
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_FOLDER
+ " AND " + NoteColumns.TYPE + "=" + Notes.TYPE_NOTE;
+ " FROM " + TABLE.NOTE
+ " WHERE " + NoteColumns.SNIPPET + " LIKE ?"
+ " AND " + NoteColumns.PARENT_ID + "<>" + Notes.ID_TRASH_FOLER
+ " AND " + NoteColumns.TYPE + "=" + Notes.TYPE_NOTE;
/**
* NotesDatabaseHelper
* @return true
*/
@Override
public boolean onCreate() {
mHelper = NotesDatabaseHelper.getInstance(getContext());
return true;
}
/**
* URI
* 便便
* @param uri URI
* @param projection
* @param selection
* @param selectionArgs
* @param sortOrder
* @return
*/
@Override
public Cursor query(Uri uri, String[] projection, String selection, String[] selectionArgs,
String sortOrder) {
@ -178,14 +148,6 @@ public class NotesProvider extends ContentProvider {
return c;
}
/**
* URI URI
* 便
* URI
* @param uri URI
* @param values
* @return URI
*/
@Override
public Uri insert(Uri uri, ContentValues values) {
SQLiteDatabase db = mHelper.getWritableDatabase();
@ -220,173 +182,125 @@ public class NotesProvider extends ContentProvider {
return ContentUris.withAppendedId(uri, insertedId);
}
/**
* URI
* */
/**
* URI
* 便便
* URI
* @param uri URI
* @param selection
* @param selectionArgs
* @return
*/
@Override
public int delete(Uri uri, String selection, String[] selectionArgs) {
int count = 0;
String id = null;
SQLiteDatabase db = mHelper.getWritableDatabase();
boolean deleteData = false;
switch (mMatcher.match(uri)) {
case URI_NOTE:
// 删除便签表中的记录,确保 ID 大于 0
selection = "(" + selection + ") AND " + NoteColumns.ID + ">0 ";
count = db.delete(TABLE.NOTE, selection, selectionArgs);
break;
case URI_NOTE_ITEM:
// 删除便签表中的单个记录
id = uri.getPathSegments().get(1);
long noteId = Long.valueOf(id);
// ID 小于等于 0 的是系统文件夹,不允许删除
if (noteId <= 0) {
break;
}
count = db.delete(TABLE.NOTE,
NoteColumns.ID + "=" + id + parseSelection(selection), selectionArgs);
break;
case URI_DATA:
// 删除数据表中的记录
count = db.delete(TABLE.DATA, selection, selectionArgs);
deleteData = true;
break;
case URI_DATA_ITEM:
// 删除数据表中的单个记录
id = uri.getPathSegments().get(1);
count = db.delete(TABLE.DATA,
DataColumns.ID + "=" + id + parseSelection(selection), selectionArgs);
deleteData = true;
break;
default:
// 未知 URI 抛出异常
throw new IllegalArgumentException("Unknown URI " + uri);
}
// 如果删除成功,通知相关 URI 的变化
if (count > 0) {
if (deleteData) {
getContext().getContentResolver().notifyChange(Notes.CONTENT_NOTE_URI, null);
}
getContext().getContentResolver().notifyChange(uri, null);
}
return count;
}
/**
* URI
* 便便
* URI
* @param uri URI
* @param values
* @param selection
* @param selectionArgs
* @return
*/
@Override
public int update(Uri uri, ContentValues values, String selection, String[] selectionArgs) {
int count = 0;
String id = null;
SQLiteDatabase db = mHelper.getWritableDatabase();
boolean updateData = false;
switch (mMatcher.match(uri)) {
case URI_NOTE:
// 更新便签表中的记录,增加版本号
increaseNoteVersion(-1, selection, selectionArgs);
count = db.update(TABLE.NOTE, values, selection, selectionArgs);
break;
case URI_NOTE_ITEM:
// 更新便签表中的单个记录,增加版本号
id = uri.getPathSegments().get(1);
increaseNoteVersion(Long.valueOf(id), selection, selectionArgs);
count = db.update(TABLE.NOTE, values, NoteColumns.ID + "=" + id
+ parseSelection(selection), selectionArgs);
break;
case URI_DATA:
// 更新数据表中的记录
count = db.update(TABLE.DATA, values, selection, selectionArgs);
updateData = true;
break;
case URI_DATA_ITEM:
// 更新数据表中的单个记录
id = uri.getPathSegments().get(1);
count = db.update(TABLE.DATA, values, DataColumns.ID + "=" + id
+ parseSelection(selection), selectionArgs);
updateData = true;
break;
default:
// 未知 URI 抛出异常
throw new IllegalArgumentException("Unknown URI " + uri);
}
// 如果更新成功,通知相关 URI 的变化
if (count > 0) {
if (updateData) {
getContext().getContentResolver().notifyChange(Notes.CONTENT_NOTE_URI, null);
}
getContext().getContentResolver().notifyChange(uri, null);
}
return count;
}
@Override
public int delete(Uri uri, String selection, String[] selectionArgs) {
int count = 0;
String id = null;
SQLiteDatabase db = mHelper.getWritableDatabase();
boolean deleteData = false;
switch (mMatcher.match(uri)) {
case URI_NOTE:
selection = "(" + selection + ") AND " + NoteColumns.ID + ">0 ";
count = db.delete(TABLE.NOTE, selection, selectionArgs);
break;
case URI_NOTE_ITEM:
id = uri.getPathSegments().get(1);
/**
* ID that smaller than 0 is system folder which is not allowed to
* trash
*/
long noteId = Long.valueOf(id);
if (noteId <= 0) {
break;
}
count = db.delete(TABLE.NOTE,
NoteColumns.ID + "=" + id + parseSelection(selection), selectionArgs);
break;
case URI_DATA:
count = db.delete(TABLE.DATA, selection, selectionArgs);
deleteData = true;
break;
case URI_DATA_ITEM:
id = uri.getPathSegments().get(1);
count = db.delete(TABLE.DATA,
DataColumns.ID + "=" + id + parseSelection(selection), selectionArgs);
deleteData = true;
break;
default:
throw new IllegalArgumentException("Unknown URI " + uri);
}
if (count > 0) {
if (deleteData) {
getContext().getContentResolver().notifyChange(Notes.CONTENT_NOTE_URI, null);
}
getContext().getContentResolver().notifyChange(uri, null);
}
return count;
}
/**
*
* @param selection
* @return
*/
private String parseSelection(String selection) {
return (!TextUtils.isEmpty(selection) ? " AND (" + selection + ')' : "");
}
@Override
public int update(Uri uri, ContentValues values, String selection, String[] selectionArgs) {
int count = 0;
String id = null;
SQLiteDatabase db = mHelper.getWritableDatabase();
boolean updateData = false;
switch (mMatcher.match(uri)) {
case URI_NOTE:
increaseNoteVersion(-1, selection, selectionArgs);
count = db.update(TABLE.NOTE, values, selection, selectionArgs);
break;
case URI_NOTE_ITEM:
id = uri.getPathSegments().get(1);
increaseNoteVersion(Long.valueOf(id), selection, selectionArgs);
count = db.update(TABLE.NOTE, values, NoteColumns.ID + "=" + id
+ parseSelection(selection), selectionArgs);
break;
case URI_DATA:
count = db.update(TABLE.DATA, values, selection, selectionArgs);
updateData = true;
break;
case URI_DATA_ITEM:
id = uri.getPathSegments().get(1);
count = db.update(TABLE.DATA, values, DataColumns.ID + "=" + id
+ parseSelection(selection), selectionArgs);
updateData = true;
break;
default:
throw new IllegalArgumentException("Unknown URI " + uri);
}
/**
* 便
* @param id 便 ID-1 便
* @param selection
* @param selectionArgs
*/
private void increaseNoteVersion(long id, String selection, String[] selectionArgs) {
StringBuilder sql = new StringBuilder(120);
sql.append("UPDATE ");
sql.append(TABLE.NOTE);
sql.append(" SET ");
sql.append(NoteColumns.VERSION);
sql.append("=" + NoteColumns.VERSION + "+1 ");
if (count > 0) {
if (updateData) {
getContext().getContentResolver().notifyChange(Notes.CONTENT_NOTE_URI, null);
}
getContext().getContentResolver().notifyChange(uri, null);
}
return count;
}
if (id > 0 || !TextUtils.isEmpty(selection)) {
sql.append(" WHERE ");
}
if (id > 0) {
sql.append(NoteColumns.ID + "=" + String.valueOf(id));
}
if (!TextUtils.isEmpty(selection)) {
String selectString = id > 0 ? parseSelection(selection) : selection;
for (String args : selectionArgs) {
selectString = selectString.replaceFirst("\\?", args);
}
sql.append(selectString);
}
private String parseSelection(String selection) {
return (!TextUtils.isEmpty(selection) ? " AND (" + selection + ')' : "");
}
mHelper.getWritableDatabase().execSQL(sql.toString());
}
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 ");
/**
* URI MIME
* @param uri URI
* @return URI MIME
*/
@Override
public String getType(Uri uri) {
return null;
}
}
if (id > 0 || !TextUtils.isEmpty(selection)) {
sql.append(" WHERE ");
}
if (id > 0) {
sql.append(NoteColumns.ID + "=" + String.valueOf(id));
}
if (!TextUtils.isEmpty(selection)) {
String selectString = id > 0 ? parseSelection(selection) : selection;
for (String args : selectionArgs) {
selectString = selectString.replaceFirst("\\?", args);
}
sql.append(selectString);
}
mHelper.getWritableDatabase().execSQL(sql.toString());
}
@Override
public String getType(Uri uri) {
// TODO Auto-generated method stub
return null;
}
}

@ -26,105 +26,57 @@ import org.json.JSONObject;
public class MetaData extends Task {
// 类名的简写,用于日志记录
private final static String TAG = MetaData.class.getSimpleName();
// 与MetaData关联的GTask ID
private String mRelatedGid = null;
/**
* MetaData
*
* @param gid GTask ID
* @param metaInfo JSONObject
*/
public void setMeta(String gid, JSONObject metaInfo) {
try {
// 将gid添加到metaInfo中
metaInfo.put(GTaskStringUtils.META_HEAD_GTASK_ID, gid);
} catch (JSONException e) {
// 如果添加gid失败记录错误日志
Log.e(TAG, "failed to put related gid");
}
// 将metaInfo转换为字符串并设置为notes
setNotes(metaInfo.toString());
// 设置MetaData的名称
setName(GTaskStringUtils.META_NOTE_NAME);
}
/**
* MetaDataGTask ID
*
* @return GTask ID
*/
public String getRelatedGid() {
return mRelatedGid;
}
/**
* MetaData
*
* @return notestrue
*/
@Override
public boolean isWorthSaving() {
return getNotes() != null;
}
/**
* JSONMetaData
*
* @param js JSON
*/
@Override
public void setContentByRemoteJSON(JSONObject js) {
super.setContentByRemoteJSON(js);
if (getNotes() != null) {
try {
// 从notes中获取metaInfo
JSONObject metaInfo = new JSONObject(getNotes().trim());
// 获取关联的GTask ID
mRelatedGid = metaInfo.getString(GTaskStringUtils.META_HEAD_GTASK_ID);
} catch (JSONException e) {
// 如果获取gid失败记录警告日志
Log.w(TAG, "failed to get related gid");
mRelatedGid = null;
}
}
}
/**
* JSONMetaData
*
* @param js JSON
* @throws IllegalAccessError
*/
@Override
public void setContentByLocalJSON(JSONObject js) {
// 该方法不应该被调用,抛出异常
// this function should not be called
throw new IllegalAccessError("MetaData:setContentByLocalJSON should not be called");
}
/**
* JSON
*
* @throws IllegalAccessError
*/
@Override
public JSONObject getLocalJSONFromContent() {
// 该方法不应该被调用,抛出异常
throw new IllegalAccessError("MetaData:getLocalJSONFromContent should not be called");
}
/**
*
*
* @param c Cursor
* @throws IllegalAccessError
*/
@Override
public int getSyncAction(Cursor c) {
// 该方法不应该被调用,抛出异常
throw new IllegalAccessError("MetaData:getSyncAction should not be called");
}
}

@ -20,78 +20,33 @@ import android.database.Cursor;
import org.json.JSONObject;
/**
*
*/
public abstract class Node {
/**
*
*/
public static final int SYNC_ACTION_NONE = 0;
/**
*
*/
public static final int SYNC_ACTION_ADD_REMOTE = 1;
/**
*
*/
public static final int SYNC_ACTION_ADD_LOCAL = 2;
/**
*
*/
public static final int SYNC_ACTION_DEL_REMOTE = 3;
/**
*
*/
public static final int SYNC_ACTION_DEL_LOCAL = 4;
/**
*
*/
public static final int SYNC_ACTION_UPDATE_REMOTE = 5;
/**
*
*/
public static final int SYNC_ACTION_UPDATE_LOCAL = 6;
/**
*
*/
public static final int SYNC_ACTION_UPDATE_CONFLICT = 7;
/**
*
*/
public static final int SYNC_ACTION_ERROR = 8;
/**
*
*/
private String mGid;
/**
*
*/
private String mName;
/**
*
*/
private long mLastModified;
/**
*
*/
private boolean mDeleted;
/**
*
*/
public Node() {
mGid = null;
mName = "";
@ -99,120 +54,48 @@ 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 Cursor
* @return
*/
public abstract int getSyncAction(Cursor c);
/**
*
*
* @param gid
*/
public void setGid(String gid) {
this.mGid = gid;
}
/**
*
*
* @param name
*/
public void setName(String name) {
this.mName = name;
}
/**
*
*
* @param lastModified
*/
public void setLastModified(long lastModified) {
this.mLastModified = lastModified;
}
/**
*
*
* @param deleted
*/
public void setDeleted(boolean deleted) {
this.mDeleted = deleted;
}
/**
*
*
* @return
*/
public String getGid() {
return this.mGid;
}
/**
*
*
* @return
*/
public String getName() {
return this.mName;
}
/**
*
*
* @return
*/
public long getLastModified() {
return this.mLastModified;
}
/**
*
*
* @return
*/
public boolean getDeleted() {
return this.mDeleted;
}
}

@ -35,66 +35,42 @@ import org.json.JSONException;
import org.json.JSONObject;
/**
* SqlData
*/
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
};
// 数据ID列的索引
public static final int DATA_ID_COLUMN = 0;
// 数据MIME类型列的索引
public static final int DATA_MIME_TYPE_COLUMN = 1;
// 数据内容列的索引
public static final int DATA_CONTENT_COLUMN = 2;
// 数据内容DATA1列的索引
public static final int DATA_CONTENT_DATA_1_COLUMN = 3;
// 数据内容DATA3列的索引
public static final int DATA_CONTENT_DATA_3_COLUMN = 4;
// 内容解析器
private ContentResolver mContentResolver;
// 是否为创建状态
private boolean mIsCreate;
// 数据ID
private long mDataId;
// 数据MIME类型
private String mDataMimeType;
// 数据内容
private String mDataContent;
// 数据内容DATA1
private long mDataContentData1;
// 数据内容DATA3
private String mDataContentData3;
// 差异数据值
private ContentValues mDiffDataValues;
/**
* SqlData
*
* @param context
*/
public SqlData(Context context) {
mContentResolver = context.getContentResolver();
mIsCreate = true;
@ -106,12 +82,6 @@ public class SqlData {
mDiffDataValues = new ContentValues();
}
/**
* CursorSqlData
*
* @param context
* @param c Cursor
*/
public SqlData(Context context, Cursor c) {
mContentResolver = context.getContentResolver();
mIsCreate = false;
@ -119,11 +89,6 @@ public class SqlData {
mDiffDataValues = new ContentValues();
}
/**
* Cursor
*
* @param c Cursor
*/
private void loadFromCursor(Cursor c) {
mDataId = c.getLong(DATA_ID_COLUMN);
mDataMimeType = c.getString(DATA_MIME_TYPE_COLUMN);
@ -132,12 +97,6 @@ public class SqlData {
mDataContentData3 = c.getString(DATA_CONTENT_DATA_3_COLUMN);
}
/**
*
*
* @param js JSON
* @throws JSONException JSON
*/
public void setContent(JSONObject js) throws JSONException {
long dataId = js.has(DataColumns.ID) ? js.getLong(DataColumns.ID) : INVALID_ID;
if (mIsCreate || mDataId != dataId) {
@ -171,12 +130,6 @@ public class SqlData {
mDataContentData3 = dataContentData3;
}
/**
*
*
* @return JSON
* @throws JSONException JSON
*/
public JSONObject getContent() throws JSONException {
if (mIsCreate) {
Log.e(TAG, "it seems that we haven't created this in database yet");
@ -191,13 +144,6 @@ public class SqlData {
return js;
}
/**
*
*
* @param noteId ID
* @param validateVersion
* @param version
*/
public void commit(long noteId, boolean validateVersion, long version) {
if (mIsCreate) {
@ -237,11 +183,6 @@ public class SqlData {
mIsCreate = false;
}
/**
* ID
*
* @return ID
*/
public long getId() {
return mDataId;
}

@ -38,17 +38,11 @@ import org.json.JSONObject;
import java.util.ArrayList;
/**
* SqlNote
*/
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,
@ -58,84 +52,76 @@ public class SqlNote {
NoteColumns.VERSION
};
// 各列的索引
public static final int ID_COLUMN = 0;
public static final int ALERTED_DATE_COLUMN = 1;
public static final int BG_COLOR_ID_COLUMN = 2;
public static final int CREATED_DATE_COLUMN = 3;
public static final int HAS_ATTACHMENT_COLUMN = 4;
public static final int MODIFIED_DATE_COLUMN = 5;
public static final int NOTES_COUNT_COLUMN = 6;
public static final int PARENT_ID_COLUMN = 7;
public static final int SNIPPET_COLUMN = 8;
public static final int TYPE_COLUMN = 9;
public static final int WIDGET_ID_COLUMN = 10;
public static final int WIDGET_TYPE_COLUMN = 11;
public static final int SYNC_ID_COLUMN = 12;
public static final int LOCAL_MODIFIED_COLUMN = 13;
public static final int ORIGIN_PARENT_ID_COLUMN = 14;
public static final int GTASK_ID_COLUMN = 15;
public static final int VERSION_COLUMN = 16;
// 上下文
private Context mContext;
// 内容解析器
private ContentResolver mContentResolver;
// 是否为创建状态
private boolean mIsCreate;
// 笔记ID
private long mId;
// 提醒日期
private long mAlertDate;
// 背景颜色ID
private int mBgColorId;
// 创建日期
private long mCreatedDate;
// 是否有附件
private int mHasAttachment;
// 修改日期
private long mModifiedDate;
// 父ID
private long mParentId;
// 摘要
private String mSnippet;
// 类型
private int mType;
// 小部件ID
private int mWidgetId;
// 小部件类型
private int mWidgetType;
// 原始父ID
private long mOriginParent;
// 版本号
private long mVersion;
// 差异笔记值
private ContentValues mDiffNoteValues;
// 数据列表
private ArrayList<SqlData> mDataList;
/**
* SqlNote
*
* @param context
*/
public SqlNote(Context context) {
mContext = context;
mContentResolver = context.getContentResolver();
@ -157,12 +143,6 @@ public class SqlNote {
mDataList = new ArrayList<SqlData>();
}
/**
* CursorSqlNote
*
* @param context
* @param c Cursor
*/
public SqlNote(Context context, Cursor c) {
mContext = context;
mContentResolver = context.getContentResolver();
@ -174,12 +154,6 @@ public class SqlNote {
mDiffNoteValues = new ContentValues();
}
/**
* IDSqlNote
*
* @param context
* @param id ID
*/
public SqlNote(Context context, long id) {
mContext = context;
mContentResolver = context.getContentResolver();
@ -189,13 +163,9 @@ public class SqlNote {
if (mType == Notes.TYPE_NOTE)
loadDataContent();
mDiffNoteValues = new ContentValues();
}
/**
* ID
*
* @param id ID
*/
private void loadFromCursor(long id) {
Cursor c = null;
try {
@ -215,11 +185,6 @@ public class SqlNote {
}
}
/**
* Cursor
*
* @param c Cursor
*/
private void loadFromCursor(Cursor c) {
mId = c.getLong(ID_COLUMN);
mAlertDate = c.getLong(ALERTED_DATE_COLUMN);
@ -235,9 +200,6 @@ public class SqlNote {
mVersion = c.getLong(VERSION_COLUMN);
}
/**
*
*/
private void loadDataContent() {
Cursor c = null;
mDataList.clear();
@ -264,19 +226,13 @@ public class SqlNote {
}
}
/**
*
*
* @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 snippet and type
// 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)) {
@ -306,262 +262,244 @@ public class SqlNote {
mAlertDate = alertDate;
int bgColorId = note.has(NoteColumns.BG_COLOR_ID) ? note
.getInt(NoteColumnsmParentId != 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;
.getInt(NoteColumns.BG_COLOR_ID) : ResourceParser.getDefaultBgId(mContext);
if (mIsCreate || mBgColorId != bgColorId) {
mDiffNoteValues.put(NoteColumns.BG_COLOR_ID, bgColorId);
}
mBgColorId = bgColorId;
int type = note.has(NoteColumns.TYPE) ? note.getInt(NoteColumns.TYPE)
: Notes.TYPE_NOTE;
if (mIsCreate || mType != type) {
mDiffNoteValues.put(NoteColumns.TYPE, type);
}
mType = type;
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 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 hasAttachment = note.has(NoteColumns.HAS_ATTACHMENT) ? note
.getInt(NoteColumns.HAS_ATTACHMENT) : 0;
if (mIsCreate || mHasAttachment != hasAttachment) {
mDiffNoteValues.put(NoteColumns.HAS_ATTACHMENT, hasAttachment);
}
mHasAttachment = hasAttachment;
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 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 originParent = note.has(NoteColumns.ORIGIN_PARENT_ID) ? note
.getLong(NoteColumns.ORIGIN_PARENT_ID) : 0;
if (mIsCreate || mOriginParent != originParent) {
mDiffNoteValues.put(NoteColumns.ORIGIN_PARENT_ID, originParent);
}
mOriginParent = originParent;
// 处理数据内容
for (int i = 0; i < dataArray.length(); i++) {
JSONObject data = dataArray.getJSONObject(i);
SqlData sqlData = null;
if (data.has(DataColumns.ID)) {
long dataId = data.getLong(DataColumns.ID);
for (SqlData temp : mDataList) {
if (dataId == temp.getId()) {
sqlData = temp;
}
}
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;
if (sqlData == null) {
sqlData = new SqlData(mContext);
mDataList.add(sqlData);
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;
sqlData.setContent(data);
long originParent = note.has(NoteColumns.ORIGIN_PARENT_ID) ? note
.getLong(NoteColumns.ORIGIN_PARENT_ID) : 0;
if (mIsCreate || mOriginParent != originParent) {
mDiffNoteValues.put(NoteColumns.ORIGIN_PARENT_ID, originParent);
}
mOriginParent = originParent;
for (int i = 0; i < dataArray.length(); i++) {
JSONObject data = dataArray.getJSONObject(i);
SqlData sqlData = null;
if (data.has(DataColumns.ID)) {
long dataId = data.getLong(DataColumns.ID);
for (SqlData temp : mDataList) {
if (dataId == temp.getId()) {
sqlData = temp;
}
}
}
if (sqlData == null) {
sqlData = new SqlData(mContext);
mDataList.add(sqlData);
}
sqlData.setContent(data);
}
}
} catch (JSONException e) {
Log.e(TAG, e.toString());
e.printStackTrace();
return false;
}
} catch (JSONException e) {
Log.e(TAG, e.toString());
e.printStackTrace();
return false;
return true;
}
return true;
}
/**
*
*
* @return JSON
*/
public JSONObject getContent() {
try {
JSONObject js = new JSONObject();
public JSONObject getContent() {
try {
JSONObject js = new JSONObject();
if (mIsCreate) {
Log.e(TAG, "it seems that we haven't created this in database yet");
return null;
}
if (mIsCreate) {
Log.e(TAG, "it seems that we haven't created this in database yet");
return null;
}
JSONObject note = new JSONObject();
if (mType == Notes.TYPE_NOTE) {
note.put(NoteColumns.ID, mId);
note.put(NoteColumns.ALERTED_DATE, mAlertDate);
note.put(NoteColumns.BG_COLOR_ID, mBgColorId);
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);
JSONArray dataArray = new JSONArray();
for (SqlData sqlData : mDataList) {
JSONObject data = sqlData.getContent();
if (data != null) {
dataArray.put(data);
JSONObject note = new JSONObject();
if (mType == Notes.TYPE_NOTE) {
note.put(NoteColumns.ID, mId);
note.put(NoteColumns.ALERTED_DATE, mAlertDate);
note.put(NoteColumns.BG_COLOR_ID, mBgColorId);
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);
JSONArray dataArray = new JSONArray();
for (SqlData sqlData : mDataList) {
JSONObject data = sqlData.getContent();
if (data != null) {
dataArray.put(data);
}
}
js.put(GTaskStringUtils.META_HEAD_DATA, dataArray);
} else if (mType == Notes.TYPE_FOLDER || mType == Notes.TYPE_SYSTEM) {
note.put(NoteColumns.ID, mId);
note.put(NoteColumns.TYPE, mType);
note.put(NoteColumns.SNIPPET, mSnippet);
js.put(GTaskStringUtils.META_HEAD_NOTE, note);
}
js.put(GTaskStringUtils.META_HEAD_DATA, dataArray);
} else if (mType == Notes.TYPE_FOLDER || mType == Notes.TYPE_SYSTEM) {
note.put(NoteColumns.ID, mId);
note.put(NoteColumns.TYPE, mType);
note.put(NoteColumns.SNIPPET, mSnippet);
js.put(GTaskStringUtils.META_HEAD_NOTE, note);
}
return js;
} catch (JSONException e) {
Log.e(TAG, e.toString());
e.printStackTrace();
return js;
} catch (JSONException e) {
Log.e(TAG, e.toString());
e.printStackTrace();
}
return null;
}
return null;
}
/**
* ID
*
* @param id ID
*/
public void setParentId(long id) {
mParentId = id;
mDiffNoteValues.put(NoteColumns.PARENT_ID, id);
}
public void setParentId(long id) {
mParentId = id;
mDiffNoteValues.put(NoteColumns.PARENT_ID, id);
}
/**
* GTask ID
*
* @param gid GTask ID
*/
public void setGtaskId(String gid) {
mDiffNoteValues.put(NoteColumns.GTASK_ID, gid);
}
public void setGtaskId(String gid) {
mDiffNoteValues.put(NoteColumns.GTASK_ID, gid);
}
/**
* ID
*
* @param syncId ID
*/
public void setSyncId(long syncId) {
mDiffNoteValues.put(NoteColumns.SYNC_ID, syncId);
}
public void setSyncId(long syncId) {
mDiffNoteValues.put(NoteColumns.SYNC_ID, syncId);
}
/**
*
*/
public void resetLocalModified() {
mDiffNoteValues.put(NoteColumns.LOCAL_MODIFIED, 0);
}
public void resetLocalModified() {
mDiffNoteValues.put(NoteColumns.LOCAL_MODIFIED, 0);
}
/**
* ID
*
* @return ID
*/
public long getId() {
return mId;
}
public long getId() {
return mId;
}
/**
* ID
*
* @return ID
*/
public long getParentId() {
return mParentId;
}
public long getParentId() {
return mParentId;
}
/**
*
*
* @return
*/
public String getSnippet() {
return mSnippet;
}
public String getSnippet() {
return mSnippet;
}
/**
*
*
* @return
*/
public boolean isNoteType() {
return mType == Notes.TYPE_NOTE;
}
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);
}
public void commit(boolean validateVersion) {
if (mIsCreate) {
if (mId == INVALID_ID && mDiffNoteValues.containsKey(NoteColumns.ID)) {
mDiffNoteValues.remove(NoteColumns.ID);
}
Uri uri = mContentResolver.insert(Notes.CONTENT_NOTE_URI, mDiffNoteValues);
try {
mId = Long.valueOf(uri.getPathSegments().get(1));
} catch (NumberFormatException e) {
Log.e(TAG, "Get note id error :" + e.toString());
throw new ActionFailureException("create note failed");
}
if (mId == 0) {
throw new IllegalStateException("Create thread id failed");
}
Uri uri = mContentResolver.insert(Notes.CONTENT_NOTE_URI, mDiffNoteValues);
try {
mId = Long.valueOf(uri.getPathSegments().get(1));
} catch (NumberFormatException e) {
Log.e(TAG, "Get note id error :" + e.toString());
throw new ActionFailureException("create note failed");
}
if (mId == 0) {
throw new IllegalStateException("Create thread id failed");
}
if (mType == Notes.TYPE_NOTE) {
for (SqlData sqlData : mDataList) {
sqlData.commit(mId, false, -1);
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");
}
if (mDiffNoteValues.size() > 0) {
mVersion++;
int result = 0;
if (!validateVersion) {
result = mContentResolver.update(Notes.CONTENT_NOTE_URI, mDiffNoteValues, "("
+ NoteColumns.ID + "=?)", new String[] {
String.valueOf(mId)
});
} else {
result = mContentResolver.update(Notes.CONTENT_NOTE_URI, mDiffNoteValues, "("
+ NoteColumns.ID + "=?) AND (" + NoteColumns.VERSION + "<=?)",
new String[] {
String.valueOf(mId), String.valueOf(mVersion)
});
} else {
if (mId <= 0 && mId != Notes.ID_ROOT_FOLDER && mId != Notes.ID_CALL_RECORD_FOLDER) {
Log.e(TAG, "No such note");
throw new IllegalStateException("Try to update note with invalid id");
}
if (result == 0) {
Log.w(TAG, "there is no update. maybe user updates note when syncing");
if (mDiffNoteValues.size() > 0) {
mVersion ++;
int result = 0;
if (!validateVersion) {
result = mContentResolver.update(Notes.CONTENT_NOTE_URI, mDiffNoteValues, "("
+ NoteColumns.ID + "=?)", new String[] {
String.valueOf(mId)
});
} else {
result = mContentResolver.update(Notes.CONTENT_NOTE_URI, mDiffNoteValues, "("
+ NoteColumns.ID + "=?) AND (" + NoteColumns.VERSION + "<=?)",
new String[] {
String.valueOf(mId), String.valueOf(mVersion)
});
}
if (result == 0) {
Log.w(TAG, "there is no update. maybe user updates note when syncing");
}
}
}
if (mType == Notes.TYPE_NOTE) {
for (SqlData sqlData : mDataList) {
sqlData.commit(mId, validateVersion, mVersion);
if (mType == Notes.TYPE_NOTE) {
for (SqlData sqlData : mDataList) {
sqlData.commit(mId, validateVersion, mVersion);
}
}
}
}
// 刷新本地信息
loadFromCursor(mId);
if (mType == Notes.TYPE_NOTE)
loadDataContent();
// refresh local info
loadFromCursor(mId);
if (mType == Notes.TYPE_NOTE)
loadDataContent();
mDiffNoteValues.clear();
mIsCreate = false;
mDiffNoteValues.clear();
mIsCreate = false;
}
}

@ -32,31 +32,19 @@ import org.json.JSONException;
import org.json.JSONObject;
/**
* TaskNode
*/
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;
/**
*
*/
public Task() {
super();
mCompleted = false;
@ -66,27 +54,21 @@ public class Task extends Node {
mMetaInfo = null;
}
/**
* JSON
*
* @param actionId ID
* @return JSON
*/
public JSONObject getCreateAction(int actionId) {
JSONObject js = new JSONObject();
try {
// 操作类型
// action_type
js.put(GTaskStringUtils.GTASK_JSON_ACTION_TYPE,
GTaskStringUtils.GTASK_JSON_ACTION_TYPE_CREATE);
// 操作ID
// action_id
js.put(GTaskStringUtils.GTASK_JSON_ACTION_ID, actionId);
// 索引
// index
js.put(GTaskStringUtils.GTASK_JSON_INDEX, mParent.getChildTaskIndex(this));
// 实体差异
// entity_delta
JSONObject entity = new JSONObject();
entity.put(GTaskStringUtils.GTASK_JSON_NAME, getName());
entity.put(GTaskStringUtils.GTASK_JSON_CREATOR_ID, "null");
@ -97,17 +79,17 @@ public class Task extends Node {
}
js.put(GTaskStringUtils.GTASK_JSON_ENTITY_DELTA, entity);
// 父ID
// parent_id
js.put(GTaskStringUtils.GTASK_JSON_PARENT_ID, mParent.getGid());
// 目标父类型
// dest_parent_type
js.put(GTaskStringUtils.GTASK_JSON_DEST_PARENT_TYPE,
GTaskStringUtils.GTASK_JSON_TYPE_GROUP);
// 列表ID
// list_id
js.put(GTaskStringUtils.GTASK_JSON_LIST_ID, mParent.getGid());
// 前一个兄弟任务ID
// prior_sibling_id
if (mPriorSibling != null) {
js.put(GTaskStringUtils.GTASK_JSON_PRIOR_SIBLING_ID, mPriorSibling.getGid());
}
@ -121,27 +103,21 @@ public class Task extends Node {
return js;
}
/**
* JSON
*
* @param actionId ID
* @return JSON
*/
public JSONObject getUpdateAction(int actionId) {
JSONObject js = new JSONObject();
try {
// 操作类型
// action_type
js.put(GTaskStringUtils.GTASK_JSON_ACTION_TYPE,
GTaskStringUtils.GTASK_JSON_ACTION_TYPE_UPDATE);
// 操作ID
// action_id
js.put(GTaskStringUtils.GTASK_JSON_ACTION_ID, actionId);
// ID
// id
js.put(GTaskStringUtils.GTASK_JSON_ID, getGid());
// 实体差异
// entity_delta
JSONObject entity = new JSONObject();
entity.put(GTaskStringUtils.GTASK_JSON_NAME, getName());
if (getNotes() != null) {
@ -159,40 +135,35 @@ public class Task extends Node {
return js;
}
/**
* JSON
*
* @param js JSON
*/
public void setContentByRemoteJSON(JSONObject js) {
if (js != null) {
try {
// ID
// id
if (js.has(GTaskStringUtils.GTASK_JSON_ID)) {
setGid(js.getString(GTaskStringUtils.GTASK_JSON_ID));
}
// 最后修改时间
// last_modified
if (js.has(GTaskStringUtils.GTASK_JSON_LAST_MODIFIED)) {
setLastModified(js.getLong(GTaskStringUtils.GTASK_JSON_LAST_MODIFIED));
}
// 名称
// name
if (js.has(GTaskStringUtils.GTASK_JSON_NAME)) {
setName(js.getString(GTaskStringUtils.GTASK_JSON_NAME));
}
// 备注
// notes
if (js.has(GTaskStringUtils.GTASK_JSON_NOTES)) {
setNotes(js.getString(GTaskStringUtils.GTASK_JSON_NOTES));
}
// 是否删除
// deleted
if (js.has(GTaskStringUtils.GTASK_JSON_DELETED)) {
setDeleted(js.getBoolean(GTaskStringUtils.GTASK_JSON_DELETED));
}
// 是否完成
// completed
if (js.has(GTaskStringUtils.GTASK_JSON_COMPLETED)) {
setCompleted(js.getBoolean(GTaskStringUtils.GTASK_JSON_COMPLETED));
}
@ -204,11 +175,6 @@ public class Task extends Node {
}
}
/**
* JSON
*
* @param js JSON
*/
public void setContentByLocalJSON(JSONObject js) {
if (js == null || !js.has(GTaskStringUtils.META_HEAD_NOTE)
|| !js.has(GTaskStringUtils.META_HEAD_DATA)) {
@ -238,16 +204,11 @@ public class Task extends Node {
}
}
/**
* JSON
*
* @return JSON
*/
public JSONObject getLocalJSONFromContent() {
String name = getName();
try {
if (mMetaInfo == null) {
// 从Web创建的新任务
// new task created from web
if (name == null) {
Log.w(TAG, "the note seems to be an empty one");
return null;
@ -264,7 +225,7 @@ public class Task extends Node {
js.put(GTaskStringUtils.META_HEAD_NOTE, note);
return js;
} else {
// 同步后的任务
// synced task
JSONObject note = mMetaInfo.getJSONObject(GTaskStringUtils.META_HEAD_NOTE);
JSONArray dataArray = mMetaInfo.getJSONArray(GTaskStringUtils.META_HEAD_DATA);
@ -286,11 +247,6 @@ public class Task extends Node {
}
}
/**
*
*
* @param metaData
*/
public void setMetaInfo(MetaData metaData) {
if (metaData != null && metaData.getNotes() != null) {
try {
@ -302,12 +258,6 @@ public class Task extends Node {
}
}
/**
*
*
* @param c Cursor
* @return
*/
public int getSyncAction(Cursor c) {
try {
JSONObject noteInfo = null;
@ -325,117 +275,77 @@ public class Task extends Node {
return SYNC_ACTION_UPDATE_LOCAL;
}
// 验证note ID
// validate the note id now
if (c.getLong(SqlNote.ID_COLUMN) != noteInfo.getLong(NoteColumns.ID)) {
Log.w(TAG, "note id doesn't match");
if (c.getInt(SqlNote.LOCAL_MODIFIED_COLUMN) == 0) {
// 本地没有更新
if (c.getLong(SqlNote.SYNC_ID_COLUMN) == getLastModified()) {
// 两边都没有更新
return SYNC_ACTION_NONE;
} else {
// 应用远程更新到本地
return SYNC_ACTION_UPDATE_LOCAL;
}
} else {
// 验证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()) {
// 只有本地修改
return SYNC_ACTION_UPDATE_REMOTE;
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 {
return SYNC_ACTION_UPDATE_CONFLICT;
// 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 {
return SYNC_ACTION_UPDATE_CONFLICT;
}
}
} catch (Exception e) {
Log.e(TAG, e.toString());
e.printStackTrace();
}
} catch (Exception e) {
Log.e(TAG, e.toString());
e.printStackTrace();
return SYNC_ACTION_ERROR;
}
return SYNC_ACTION_ERROR;
}
public boolean isWorthSaving() {
return mMetaInfo != null || (getName() != null && getName().trim().length() > 0)
|| (getNotes() != null && getNotes().trim().length() > 0);
}
/**
*
*
* @return
*/
public boolean isWorthSaving() {
return mMetaInfo != null || (getName() != null && getName().trim().length() > 0)
|| (getNotes() != null && getNotes().trim().length() > 0);
}
public void setCompleted(boolean completed) {
this.mCompleted = completed;
}
/**
*
*
* @param completed
*/
public void setCompleted(boolean completed) {
this.mCompleted = completed;
}
public void setNotes(String notes) {
this.mNotes = notes;
}
/**
*
*
* @param notes
*/
public void setNotes(String notes) {
this.mNotes = notes;
}
public void setPriorSibling(Task priorSibling) {
this.mPriorSibling = priorSibling;
}
/**
*
*
* @param priorSibling
*/
public void setPriorSibling(Task priorSibling) {
this.mPriorSibling = priorSibling;
}
public void setParent(TaskList parent) {
this.mParent = parent;
}
/**
*
*
* @param parent
*/
public void setParent(TaskList parent) {
this.mParent = parent;
}
public boolean getCompleted() {
return this.mCompleted;
}
/**
*
*
* @return
*/
public boolean getCompleted() {
return this.mCompleted;
}
public String getNotes() {
return this.mNotes;
}
/**
*
*
* @return
*/
public String getNotes() {
return this.mNotes;
}
public Task getPriorSibling() {
return this.mPriorSibling;
}
/**
*
*
* @return
*/
public Task getPriorSibling() {
return this.mPriorSibling;
}
public TaskList getParent() {
return this.mParent;
}
/**
*
*
* @return
*/
public TaskList getParent() {
return this.mParent;
}

@ -30,49 +30,34 @@ import org.json.JSONObject;
import java.util.ArrayList;
/**
* TaskListNode
*/
public class TaskList extends Node {
// 类名的简写,用于日志记录
private static final String TAG = TaskList.class.getSimpleName();
// 任务列表的索引
private int mIndex;
// 任务列表中的任务列表
private ArrayList<Task> mChildren;
/**
*
*/
public TaskList() {
super();
mChildren = new ArrayList<Task>();
mIndex = 1;
}
/**
* JSON
*
* @param actionId ID
* @return JSON
*/
public JSONObject getCreateAction(int actionId) {
JSONObject js = new JSONObject();
try {
// 操作类型
// action_type
js.put(GTaskStringUtils.GTASK_JSON_ACTION_TYPE,
GTaskStringUtils.GTASK_JSON_ACTION_TYPE_CREATE);
// 操作ID
// action_id
js.put(GTaskStringUtils.GTASK_JSON_ACTION_ID, actionId);
// 索引
// index
js.put(GTaskStringUtils.GTASK_JSON_INDEX, mIndex);
// 实体差异
// entity_delta
JSONObject entity = new JSONObject();
entity.put(GTaskStringUtils.GTASK_JSON_NAME, getName());
entity.put(GTaskStringUtils.GTASK_JSON_CREATOR_ID, "null");
@ -89,27 +74,21 @@ public class TaskList extends Node {
return js;
}
/**
* JSON
*
* @param actionId ID
* @return JSON
*/
public JSONObject getUpdateAction(int actionId) {
JSONObject js = new JSONObject();
try {
// 操作类型
// action_type
js.put(GTaskStringUtils.GTASK_JSON_ACTION_TYPE,
GTaskStringUtils.GTASK_JSON_ACTION_TYPE_UPDATE);
// 操作ID
// action_id
js.put(GTaskStringUtils.GTASK_JSON_ACTION_ID, actionId);
// ID
// id
js.put(GTaskStringUtils.GTASK_JSON_ID, getGid());
// 实体差异
// entity_delta
JSONObject entity = new JSONObject();
entity.put(GTaskStringUtils.GTASK_JSON_NAME, getName());
entity.put(GTaskStringUtils.GTASK_JSON_DELETED, getDeleted());
@ -124,25 +103,20 @@ public class TaskList extends Node {
return js;
}
/**
* JSON
*
* @param js JSON
*/
public void setContentByRemoteJSON(JSONObject js) {
if (js != null) {
try {
// ID
// id
if (js.has(GTaskStringUtils.GTASK_JSON_ID)) {
setGid(js.getString(GTaskStringUtils.GTASK_JSON_ID));
}
// 最后修改时间
// last_modified
if (js.has(GTaskStringUtils.GTASK_JSON_LAST_MODIFIED)) {
setLastModified(js.getLong(GTaskStringUtils.GTASK_JSON_LAST_MODIFIED));
}
// 名称
// name
if (js.has(GTaskStringUtils.GTASK_JSON_NAME)) {
setName(js.getString(GTaskStringUtils.GTASK_JSON_NAME));
}
@ -155,11 +129,6 @@ public class TaskList extends Node {
}
}
/**
* JSON
*
* @param js JSON
*/
public void setContentByLocalJSON(JSONObject js) {
if (js == null || !js.has(GTaskStringUtils.META_HEAD_NOTE)) {
Log.w(TAG, "setContentByLocalJSON: nothing is avaiable");
@ -188,11 +157,6 @@ public class TaskList extends Node {
}
}
/**
* JSON
*
* @return JSON
*/
public JSONObject getLocalJSONFromContent() {
try {
JSONObject js = new JSONObject();
@ -219,34 +183,28 @@ public class TaskList extends Node {
}
}
/**
*
*
* @param c Cursor
* @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;
}
}
@ -258,27 +216,16 @@ public class TaskList extends Node {
return SYNC_ACTION_ERROR;
}
/**
*
*
* @return
*/
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);
@ -287,13 +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");
@ -304,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)
@ -320,12 +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);
@ -333,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));
@ -347,14 +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;
@ -362,22 +290,15 @@ public class TaskList extends Node {
int pos = mChildren.indexOf(task);
if (pos == -1) {
Log.e(TAG, "move child task: the task should be in the list");
Log.e(TAG, "move child task: the task should in the list");
return false;
}
if (pos == index)
return true; // 如果位置没有变化,直接返回
return (removeChildTask(task) && addChildTask(task, index)); // 移除并添加到新位置
return true;
return (removeChildTask(task) && addChildTask(task, index));
}
/**
* GID
*
* @param gid
* @return null
*/
public Task findChildTaskByGid(String gid) {
for (int i = 0; i < mChildren.size(); i++) {
Task t = mChildren.get(i);
@ -388,22 +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");
@ -412,12 +321,6 @@ public class TaskList extends Node {
return mChildren.get(index);
}
/**
* GID
*
* @param gid
* @return null
*/
public Task getChilTaskByGid(String gid) {
for (Task task : mChildren) {
if (task.getGid().equals(gid))
@ -426,29 +329,14 @@ public class TaskList extends Node {
return null;
}
/**
*
*
* @return
*/
public ArrayList<Task> getChildTaskList() {
return this.mChildren;
}
/**
*
*
* @param index
*/
public void setIndex(int index) {
this.mIndex = index;
}
/**
*
*
* @return
*/
public int getIndex() {
return this.mIndex;
}

@ -16,45 +16,18 @@
package net.micode.notes.gtask.exception;
/**
* ActionFailureException Google
* RuntimeException
*/
public class ActionFailureException extends RuntimeException {
private static final long serialVersionUID = 4425249765923293627L;
/**
* ActionFailureException
*/
public ActionFailureException() {
super();
}
/**
* ActionFailureException
* @param paramString
*/
public ActionFailureException(String paramString) {
super(paramString);
}
/**
* ActionFailureException
* @param paramString
* @param paramThrowable
*/
public ActionFailureException(String paramString, Throwable paramThrowable) {
super(paramString, paramThrowable);
}
}
/**
*
ActionFailureException Google RuntimeException
serialVersionUID
ActionFailureException() ActionFailureException
ActionFailureException(String paramString) ActionFailureException
ActionFailureException(String paramString, Throwable paramThrowable) ActionFailureException
*/

@ -15,46 +15,17 @@
*/
package net.micode.notes.gtask.exception;
/**
* NetworkFailureException
* Exception
*/
public class NetworkFailureException extends Exception {
private static final long serialVersionUID = 2107610287180234136L;
/**
* NetworkFailureException
*/
public NetworkFailureException() {
super();
}
/**
* NetworkFailureException
* @param paramString
*/
public NetworkFailureException(String paramString) {
super(paramString);
}
/**
* NetworkFailureException
* @param paramString
* @param paramThrowable
*/
public NetworkFailureException(String paramString, Throwable paramThrowable) {
super(paramString, paramThrowable);
}
}
/**
*
NetworkFailureException Exception
serialVersionUID
NetworkFailureException() NetworkFailureException
NetworkFailureException(String paramString) NetworkFailureExceptionparamString
NetworkFailureException(String paramString, Throwable paramThrowable) NetworkFailureExceptionparamString paramThrowable
*/

@ -1,3 +1,20 @@
/*
* 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.
*/
package net.micode.notes.gtask.remote;
import android.app.Notification;
@ -11,118 +28,92 @@ import net.micode.notes.R;
import net.micode.notes.ui.NotesListActivity;
import net.micode.notes.ui.NotesPreferenceActivity;
/**
* Google
* AsyncTask Google
*/
public class GTaskASyncTask extends AsyncTask<Void, String, Integer> {
private static int GTASK_SYNC_NOTIFICATION_ID = 5234235; // 同步通知的ID
private static int GTASK_SYNC_NOTIFICATION_ID = 5234235;
/**
*
*
*/
public interface OnCompleteListener {
void onComplete(); // 当同步完成时调用
void onComplete();
}
private Context mContext; // 应用程序上下文
private NotificationManager mNotifiManager; // 通知管理器
private GTaskManager mTaskManager; // Google 任务管理器
private OnCompleteListener mOnCompleteListener; // 完成监听器
private Context mContext;
private NotificationManager mNotifiManager;
private GTaskManager mTaskManager;
private OnCompleteListener mOnCompleteListener;
/**
*
* @param context
* @param listener
*/
public GTaskASyncTask(Context context, OnCompleteListener listener) {
mContext = context;
mOnCompleteListener = listener;
mNotifiManager = (NotificationManager) mContext.getSystemService(Context.NOTIFICATION_SERVICE);
mNotifiManager = (NotificationManager) mContext
.getSystemService(Context.NOTIFICATION_SERVICE);
mTaskManager = GTaskManager.getInstance();
}
/**
*
* Google cancelSync
*/
public void cancelSync() {
mTaskManager.cancelSync();
}
/**
*
* @param message
*/
public void publishProgess(String message) {
publishProgress(new String[] { message });
publishProgress(new String[] {
message
});
}
/**
*
* @param tickerId ID
* @param content
*/
private void showNotification(int tickerId, String content) {
Notification notification = new Notification(R.drawable.notification, mContext.getString(tickerId), System.currentTimeMillis());
notification.defaults = Notification.DEFAULT_LIGHTS; // 设置默认灯光
notification.flags = Notification.FLAG_AUTO_CANCEL; // 设置通知自动取消
PendingIntent pendingIntent; // 待决意图
// 根据 tickerId 设置不同的待决意图
Notification notification = new Notification(R.drawable.notification, mContext
.getString(tickerId), System.currentTimeMillis());
notification.defaults = Notification.DEFAULT_LIGHTS;
notification.flags = Notification.FLAG_AUTO_CANCEL;
PendingIntent pendingIntent;
if (tickerId != R.string.ticker_success) {
pendingIntent = PendingIntent.getActivity(mContext, 0, new Intent(mContext, NotesPreferenceActivity.class), 0);
pendingIntent = PendingIntent.getActivity(mContext, 0, new Intent(mContext,
NotesPreferenceActivity.class), 0);
} else {
pendingIntent = PendingIntent.getActivity(mContext, 0, new Intent(mContext, NotesListActivity.class), 0);
pendingIntent = PendingIntent.getActivity(mContext, 0, new Intent(mContext,
NotesListActivity.class), 0);
}
notification.setLatestEventInfo(mContext, mContext.getString(R.string.app_name), content, pendingIntent); // 设置通知信息
mNotifiManager.notify(GTASK_SYNC_NOTIFICATION_ID, notification); // 显示通知
notification.setLatestEventInfo(mContext, mContext.getString(R.string.app_name), content,
pendingIntent);
mNotifiManager.notify(GTASK_SYNC_NOTIFICATION_ID, notification);
}
/**
*
* @return
*/
@Override
protected Integer doInBackground(Void... unused) {
publishProgess(mContext.getString(R.string.sync_progress_login, NotesPreferenceActivity.getSyncAccountName(mContext)));
return mTaskManager.sync(mContext, this); // 同步任务
publishProgess(mContext.getString(R.string.sync_progress_login, NotesPreferenceActivity
.getSyncAccountName(mContext)));
return mTaskManager.sync(mContext, this);
}
/**
*
* @param progress
*/
@Override
protected void onProgressUpdate(String... progress) {
showNotification(R.string.ticker_syncing, progress[0]); // 显示同步中的通知
showNotification(R.string.ticker_syncing, progress[0]);
if (mContext instanceof GTaskSyncService) {
((GTaskSyncService) mContext).sendBroadcast(progress[0]); // 如果上下文是 GTaskSyncService发送广播
((GTaskSyncService) mContext).sendBroadcast(progress[0]);
}
}
/**
*
* @param result
*/
@Override
protected void onPostExecute(Integer result) {
// 根据结果状态显示不同的通知
if (result == GTaskManager.STATE_SUCCESS) {
showNotification(R.string.ticker_success, mContext.getString(R.string.success_sync_account, mTaskManager.getSyncAccount()));
NotesPreferenceActivity.setLastSyncTime(mContext, System.currentTimeMillis()); // 设置最后同步时间
showNotification(R.string.ticker_success, mContext.getString(
R.string.success_sync_account, mTaskManager.getSyncAccount()));
NotesPreferenceActivity.setLastSyncTime(mContext, System.currentTimeMillis());
} else if (result == GTaskManager.STATE_NETWORK_ERROR) {
showNotification(R.string.ticker_fail, mContext.getString(R.string.error_sync_network));
} else if (result == GTaskManager.STATE_INTERNAL_ERROR) {
showNotification(R.string.ticker_fail, mContext.getString(R.string.error_sync_internal));
} else if (result == GTaskManager.STATE_SYNC_CANCELLED) {
showNotification(R.string.ticker_cancel, mContext.getString(R.string.error_sync_cancelled));
showNotification(R.string.ticker_cancel, mContext
.getString(R.string.error_sync_cancelled));
}
// 如果设置了完成监听器,则在新线程中调用 onComplete 方法
if (mOnCompleteListener != null) {
new Thread(new Runnable() {
public void run() {
mOnCompleteListener.onComplete();
}
@ -130,26 +121,3 @@ public class GTaskASyncTask extends AsyncTask<Void, String, Integer> {
}
}
}
/**
*
GTaskASyncTask Google AsyncTask Google
GTASK_SYNC_NOTIFICATION_ID ID
OnCompleteListener
mContext
mNotifiManager
mTaskManagerGoogle
mOnCompleteListener
GTaskASyncTask(Context context, OnCompleteListener listener) Google
cancelSync() Google cancelSync
publishProgess(String message) publishProgress
showNotification(int tickerId, String content) tickerId
doInBackground(Void... unused) Google sync
onProgressUpdate(String... progress) GTaskSyncService广
onPostExecute(Integer result)线 onComplete
*/

@ -1,3 +1,19 @@
/*
* 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.
*/
package net.micode.notes.gtask.remote;
import android.accounts.Account;
@ -44,32 +60,36 @@ import java.util.zip.GZIPInputStream;
import java.util.zip.Inflater;
import java.util.zip.InflaterInputStream;
/**
* GTaskClient Google
*
*/
public class GTaskClient {
private static final String TAG = GTaskClient.class.getSimpleName(); // 类名的简写,用于日志输出
private static final String GTASK_URL = "https://mail.google.com/tasks/"; // Google任务的URL地址
private static final String GTASK_GET_URL = "https://mail.google.com/tasks/ig"; // 获取任务的URL
private static final String GTASK_POST_URL = "https://mail.google.com/tasks/r/ig"; // 发布任务的URL
private static GTaskClient mInstance = null; // 单例对象
private DefaultHttpClient mHttpClient; // HTTP客户端
private String mGetUrl; // 获取任务的URL
private String mPostUrl; // 发布任务的URL
private long mClientVersion; // 客户端版本号
private boolean mLoggedin; // 登录状态
private long mLastLoginTime; // 上次登录时间
private int mActionId; // 动作ID
private Account mAccount; // 账户信息
private JSONArray mUpdateArray; // 更新数组
/**
*
*/
private static final String TAG = GTaskClient.class.getSimpleName();
private static final String GTASK_URL = "https://mail.google.com/tasks/";
private static final String GTASK_GET_URL = "https://mail.google.com/tasks/ig";
private static final String GTASK_POST_URL = "https://mail.google.com/tasks/r/ig";
private static GTaskClient mInstance = null;
private DefaultHttpClient mHttpClient;
private String mGetUrl;
private String mPostUrl;
private long mClientVersion;
private boolean mLoggedin;
private long mLastLoginTime;
private int mActionId;
private Account mAccount;
private JSONArray mUpdateArray;
private GTaskClient() {
mHttpClient = null;
mGetUrl = GTASK_GET_URL;
@ -82,10 +102,6 @@ public class GTaskClient {
mUpdateArray = null;
}
/**
*
* @return GTaskClient
*/
public static synchronized GTaskClient getInstance() {
if (mInstance == null) {
mInstance = new GTaskClient();
@ -93,39 +109,36 @@ public class GTaskClient {
return mInstance;
}
/**
*
* @param activity Activity
* @return truefalse
*/
public boolean login(Activity activity) {
final long interval = 1000 * 60 * 5; // 假设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;
}
// 切换账户后需要重新登录
if (mLoggedin && !TextUtils.equals(getSyncAccount().name, NotesPreferenceActivity.getSyncAccountName(activity))) {
// 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则尝试使用自定义域名登录
if (!(mAccount.name.toLowerCase().endsWith("gmail.com") || mAccount.name.toLowerCase().endsWith("googlemail.com"))) {
// login with custom domain if necessary
if (!(mAccount.name.toLowerCase().endsWith("gmail.com") || mAccount.name.toLowerCase()
.endsWith("googlemail.com"))) {
StringBuilder url = new StringBuilder(GTASK_URL).append("a/");
int index = mAccount.name.indexOf('@') + 1;
String suffix = mAccount.name.substring(index);
@ -133,13 +146,12 @@ public class GTaskClient {
mGetUrl = url.toString() + "ig";
mPostUrl = url.toString() + "r/ig";
// 尝试使用自定义域名登录Google任务
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;
@ -148,59 +160,46 @@ public class GTaskClient {
}
}
// 登录成功
mLoggedin = true;
return true;
}
/**
* Google
* @param activity Activity
* @param invalidateToken 使
* @return
*/
private String loginGoogleAccount(Activity activity, boolean invalidateToken) {
String authToken;
AccountManager accountManager = AccountManager.get(activity); // 获取账户管理器
Account[] accounts = accountManager.getAccountsByType("com.google"); // 获取所有Google账户
AccountManager accountManager = AccountManager.get(activity);
Account[] accounts = accountManager.getAccountsByType("com.google");
// 如果没有Google账户则返回null
if (accounts.length == 0) {
Log.e(TAG, "there is no available google account");
return null;
}
// 获取同步账户的名称
String accountName = NotesPreferenceActivity.getSyncAccountName(activity);
Account account = null;
// 查找与设置中账户名称相同的账户
for (Account a : accounts) {
if (a.name.equals(accountName)) {
account = a;
break;
}
}
// 如果找到账户,则保存账户信息
if (account != null) {
mAccount = account;
} else {
// 如果没有找到账户则返回null
Log.e(TAG, "unable to get an account with the same name in the settings");
return null;
}
// 获取认证令牌
AccountManagerFuture<Bundle> accountManagerFuture = accountManager.getAuthToken(account, "goanna_mobile", null, activity, null, null);
// get the token now
AccountManagerFuture<Bundle> accountManagerFuture = accountManager.getAuthToken(account,
"goanna_mobile", null, activity, null, null);
try {
Bundle authTokenBundle = accountManagerFuture.getResult(); // 获取认证令牌的Bundle对象
authToken = authTokenBundle.getString(AccountManager.KEY_AUTHTOKEN); // 获取认证令牌
// 如果需要,则使令牌无效并重新登录
Bundle authTokenBundle = accountManagerFuture.getResult();
authToken = authTokenBundle.getString(AccountManager.KEY_AUTHTOKEN);
if (invalidateToken) {
accountManager.invalidateAuthToken("com.google", authToken);
loginGoogleAccount(activity, false);
}
} catch (Exception e) {
// 如果获取认证令牌失败则返回null
Log.e(TAG, "get auth token failed");
authToken = null;
}
@ -208,24 +207,16 @@ public class GTaskClient {
return authToken;
}
/**
* 使Google
* @param activity Activity
* @param authToken
* @return truefalse
*/
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;
@ -234,31 +225,28 @@ public class GTaskClient {
return true;
}
/**
* 使Google
* @param authToken
* @return truefalse
*/
private boolean loginGtask(String authToken) {
int timeoutConnection = 10000; // 设置连接超时时间
int timeoutSocket = 15000; // 设置套接字超时时间
int timeoutConnection = 10000;
int timeoutSocket = 15000;
HttpParams httpParameters = new BasicHttpParams();
HttpConnectionParams.setConnectionTimeout(httpParameters, timeoutConnection);
HttpConnectionParams.setSoTimeout(httpParameters, timeoutSocket);
mHttpClient = new DefaultHttpClient(httpParameters); // 创建HTTP客户端
mHttpClient = new DefaultHttpClient(httpParameters);
BasicCookieStore localBasicCookieStore = new BasicCookieStore();
mHttpClient.setCookieStore(localBasicCookieStore);
HttpProtocolParams.setUseExpectContinue(mHttpClient.getParams(), false); // 设置HTTP协议参数
HttpProtocolParams.setUseExpectContinue(mHttpClient.getParams(), false);
// login gtask
try {
String loginUrl = mGetUrl + "?auth=" + authToken; // 构建登录URL
String loginUrl = mGetUrl + "?auth=" + authToken;
HttpGet httpGet = new HttpGet(loginUrl);
HttpResponse response = mHttpClient.execute(httpGet); // 执行HTTP GET请求
HttpResponse response = null;
response = mHttpClient.execute(httpGet);
// get the cookie now
List<Cookie> cookies = mHttpClient.getCookieStore().getCookies();
boolean hasAuthCookie = false;
for (Cookie cookie : cookies) {
// 检查是否存在认证Cookie
if (cookie.getName().contains("GTL")) {
hasAuthCookie = true;
}
@ -267,6 +255,7 @@ public class GTaskClient {
Log.w(TAG, "it seems that there is no auth cookie");
}
// get the client version
String resString = getResponseContent(response.getEntity());
String jsBegin = "_setup(";
String jsEnd = ")}</script>";
@ -283,7 +272,7 @@ public class GTaskClient {
e.printStackTrace();
return false;
} catch (Exception e) {
// 捕获所有异常
// simply catch all exceptions
Log.e(TAG, "httpget gtask_url failed");
return false;
}
@ -291,18 +280,10 @@ public class GTaskClient {
return true;
}
/**
* ID
* @return ID
*/
private int getActionId() {
return mActionId++; // 返回并递增动作ID
return mActionId++;
}
/**
* HTTP POST
* @return HttpPost
*/
private HttpPost createHttpPost() {
HttpPost httpPost = new HttpPost(mPostUrl);
httpPost.setHeader("Content-Type", "application/x-www-form-urlencoded;charset=utf-8");
@ -310,12 +291,6 @@ public class GTaskClient {
return httpPost;
}
/**
* HTTP
* @param entity HttpEntity
* @return
* @throws IOException IO
*/
private String getResponseContent(HttpEntity entity) throws IOException {
String contentEncoding = null;
if (entity.getContentEncoding() != null) {
@ -348,12 +323,6 @@ public class GTaskClient {
}
}
/**
* POST
* @param js JSON
* @return JSON
* @throws NetworkFailureException
*/
private JSONObject postRequest(JSONObject js) throws NetworkFailureException {
if (!mLoggedin) {
Log.e(TAG, "please login first");
@ -367,9 +336,11 @@ public class GTaskClient {
UrlEncodedFormEntity entity = new UrlEncodedFormEntity(list, "UTF-8");
httpPost.setEntity(entity);
// execute the post
HttpResponse response = mHttpClient.execute(httpPost);
String jsString = getResponseContent(response.getEntity());
return new JSONObject(jsString);
} catch (ClientProtocolException e) {
Log.e(TAG, e.toString());
e.printStackTrace();
@ -389,24 +360,25 @@ public class GTaskClient {
}
}
/**
*
* @param task 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);
JSONObject jsResult = (JSONObject) jsResponse.getJSONArray(
GTaskStringUtils.GTASK_JSON_RESULTS).get(0);
task.setGid(jsResult.getString(GTaskStringUtils.GTASK_JSON_NEW_ID));
} catch (JSONException e) {
Log.e(TAG, e.toString());
e.printStackTrace();
@ -414,24 +386,25 @@ public class GTaskClient {
}
}
/**
*
* @param tasklist 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);
JSONObject jsResult = (JSONObject) jsResponse.getJSONArray(
GTaskStringUtils.GTASK_JSON_RESULTS).get(0);
tasklist.setGid(jsResult.getString(GTaskStringUtils.GTASK_JSON_NEW_ID));
} catch (JSONException e) {
Log.e(TAG, e.toString());
e.printStackTrace();
@ -439,16 +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) {
@ -459,54 +433,52 @@ public class GTaskClient {
}
}
/**
*
* @param node 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) {
if (mUpdateArray == null)
mUpdateArray = new JSONArray();
}
mUpdateArray.put(node.getUpdateAction(getActionId()));
}
}
/**
*
* @param task Task
* @param preParent
* @param curParent
* @throws NetworkFailureException
*/
public void moveTask(Task task, TaskList preParent, TaskList curParent) throws NetworkFailureException {
public void moveTask(Task task, TaskList preParent, TaskList curParent)
throws NetworkFailureException {
commitUpdate();
try {
JSONObject jsPost = new JSONObject();
JSONArray actionList = new JSONArray();
JSONObject action = new JSONObject();
action.put(GTaskStringUtils.GTASK_JSON_ACTION_TYPE, GTaskStringUtils.GTASK_JSON_ACTION_TYPE_MOVE);
// 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();
@ -514,20 +486,18 @@ public class GTaskClient {
}
}
/**
*
* @param node 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);
@ -539,11 +509,6 @@ public class GTaskClient {
}
}
/**
*
* @return JSON
* @throws NetworkFailureException
*/
public JSONArray getTaskLists() throws NetworkFailureException {
if (!mLoggedin) {
Log.e(TAG, "please login first");
@ -552,8 +517,10 @@ public class GTaskClient {
try {
HttpGet httpGet = new HttpGet(mGetUrl);
HttpResponse response = mHttpClient.execute(httpGet);
HttpResponse response = null;
response = mHttpClient.execute(httpGet);
// get the task list
String resString = getResponseContent(response.getEntity());
String jsBegin = "_setup(";
String jsEnd = ")}</script>";
@ -580,12 +547,6 @@ public class GTaskClient {
}
}
/**
*
* @param listGid GID
* @return JSON
* @throws NetworkFailureException
*/
public JSONArray getTaskList(String listGid) throws NetworkFailureException {
commitUpdate();
try {
@ -593,12 +554,16 @@ public class GTaskClient {
JSONArray actionList = new JSONArray();
JSONObject action = new JSONObject();
action.put(GTaskStringUtils.GTASK_JSON_ACTION_TYPE, GTaskStringUtils.GTASK_JSON_ACTION_TYPE_GETALL);
// action_list
action.put(GTaskStringUtils.GTASK_JSON_ACTION_TYPE,
GTaskStringUtils.GTASK_JSON_ACTION_TYPE_GETALL);
action.put(GTaskStringUtils.GTASK_JSON_ACTION_ID, getActionId());
action.put(GTaskStringUtils.GTASK_JSON_LIST_ID, listGid);
action.put(GTaskStringUtils.GTASK_JSON_GET_DELETED, false);
actionList.put(action);
jsPost.put(GTaskStringUtils.GTASK_JSON_ACTION_LIST, actionList);
// client_version
jsPost.put(GTaskStringUtils.GTASK_JSON_CLIENT_VERSION, mClientVersion);
JSONObject jsResponse = postRequest(jsPost);
@ -610,58 +575,11 @@ public class GTaskClient {
}
}
/**
*
* @return
*/
public Account getSyncAccount() {
return mAccount;
}
/**
*
*/
public void resetUpdateArray() {
mUpdateArray = null;
}
}
/**
*
GTaskClient Google
TAG
GTASK_URLGTASK_GET_URLGTASK_POST_URLGoogle URL
mInstance
mHttpClientHTTP
mGetUrlmPostUrl URL
mClientVersion
mLoggedin
mLastLoginTime
mActionId ID
mAccount
mUpdateArray
GTaskClient
getInstance
login
loginGoogleAccount Google
tryToLoginGtask使 Google
loginGtask使 Google
getActionId ID
createHttpPost HTTP POST
getResponseContent HTTP
postRequest POST
createTask
createTaskList
commitUpdate
addUpdateNode
moveTask
deleteNode
getTaskLists
getTaskList
getSyncAccount
resetUpdateArray
*/

File diff suppressed because it is too large Load Diff

@ -14,198 +14,148 @@
* limitations under the License.
*/
package net.micode.notes.gtask.remote;
import android.app.Activity;
import android.app.Service;
import android.content.Context;
import android.content.Intent;
import android.os.Bundle;
import android.os.IBinder;
/**
* GTaskSyncService GTask
package net.micode.notes.gtask.remote;
import android.app.Activity;
import android.app.Service;
import android.content.Context;
import android.content.Intent;
import android.os.Bundle;
import android.os.IBinder;
/**
*
* @ProjectName: minode
* @Package: net.micode.notes.gtask.remote
* @ClassName: GTaskSyncService
* @Description: GTask
* @Author:
* @Description: Gtask
* @Author:
* @Date: 2023-12-17 23:38
*/
public class GTaskSyncService extends Service {
// 定义广播的 Action 字符串
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 class GTaskSyncService extends Service {
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 int ACTION_START_SYNC = 0;
// 定义广播的额外信息键
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_CANCEL_SYNC = 1;
// 同步任务对象
private static GTaskASyncTask mSyncTask = null;
public final static int ACTION_INVALID = 2;
// 同步进度字符串
private static String mSyncProgress = "";
public final static String GTASK_SERVICE_BROADCAST_NAME = "net.micode.notes.gtask.remote.gtask_sync_service";
/**
* startSync GTask
*
* @date: 2023-12-17 23:42
* @author:
*/
private void startSync() {
// 如果同步任务为空,则创建并启动同步任务
if (mSyncTask == null) {
mSyncTask = new GTaskASyncTask(this, new GTaskASyncTask.OnCompleteListener() {
public void onComplete() {
// 同步完成时,将同步任务置空并发送广播通知
mSyncTask = null;
sendBroadcast("");
stopSelf();
}
});
// 发送广播通知同步开始
sendBroadcast("");
// 以单线程队列方式启动同步任务
mSyncTask.execute();
}
}
public final static String GTASK_SERVICE_BROADCAST_IS_SYNCING = "isSyncing";
/**
* cancelSync GTask
*
* @date: 2023-12-17 23:49
* @author:
*/
private void cancelSync() {
// 如果同步任务不为空,则取消同步任务
if (mSyncTask != null) {
mSyncTask.cancelSync();
}
}
@Override
public void onCreate() {
// 在服务创建时,将同步任务置空
mSyncTask = null;
}
public final static String GTASK_SERVICE_BROADCAST_PROGRESS_MSG = "progressMsg";
/**
* onStartCommand 使 GTaskSyncService
*
* @date: 2023-12-17 23:53
* @author:
*/
@Override
public int onStartCommand(Intent intent, int flags, int startId) {
// 获取传递过来的 Bundle 对象
Bundle bundle = intent.getExtras();
// 如果 Bundle 对象不为空且包含同步操作类型键,则根据操作类型执行相应操作
if (bundle != null && bundle.containsKey(ACTION_STRING_NAME)) {
switch (bundle.getInt(ACTION_STRING_NAME, ACTION_INVALID)) {
// 开始同步
case ACTION_START_SYNC:
startSync();
break;
// 取消同步
case ACTION_CANCEL_SYNC:
cancelSync();
break;
default:
break;
}
// 返回 START_STICKY表示如果服务被杀死系统会尝试重新创建服务
return START_STICKY;
}
// 如果 Bundle 对象为空或不包含同步操作类型键,则调用父类的 onStartCommand 方法
return super.onStartCommand(intent, flags, startId);
}
@Override
public void onLowMemory() {
// 在内存不足时,取消同步任务
if (mSyncTask != null) {
mSyncTask.cancelSync();
}
}
public IBinder onBind(Intent intent) {
// 返回 null表示不支持绑定
return null;
}
private static GTaskASyncTask mSyncTask = null;
/**
* sendBroadcast 广
*
* @param msg
*/
public void sendBroadcast(String msg) {
// 更新同步进度字符串
mSyncProgress = msg;
// 创建 Intent 对象
Intent intent = new Intent(GTASK_SERVICE_BROADCAST_NAME);
// 将是否正在同步和同步进度消息作为额外信息添加到 Intent 对象中
intent.putExtra(GTASK_SERVICE_BROADCAST_IS_SYNCING, mSyncTask != null);
intent.putExtra(GTASK_SERVICE_BROADCAST_PROGRESS_MSG, msg);
// 发送广播通知
sendBroadcast(intent);
}
private static String mSyncProgress = "";
/**
* startSync GTask
*
* @param activity Activity
* @method startSync
* @description Gtask
* @date: 2023-12-17 23:42
* @author:
* @return void
*/
public static void startSync(Activity activity) {
// 设置 Activity 上下文
GTaskManager.getInstance().setActivityContext(activity);
// 创建 Intent 对象
Intent intent = new Intent(activity, GTaskSyncService.class);
// 将同步操作类型设置为开始同步
intent.putExtra(GTaskSyncService.ACTION_STRING_NAME, GTaskSyncService.ACTION_START_SYNC);
// 启动服务
activity.startService(intent);
}
private void startSync() {
if (mSyncTask == null) {
mSyncTask = new GTaskASyncTask(this, new GTaskASyncTask.OnCompleteListener() {
public void onComplete() {
mSyncTask = null;
sendBroadcast("");
stopSelf();
}
});
sendBroadcast("");
// 使同步任务以单线程队列方式开启运行
// 2023-12-17 23:46
mSyncTask.execute();
}
}
/**
* cancelSync GTask
*
* @param context Context
* @method cancelSync
* @description Gtask
* @date: 2023-12-17 23:49
* @author:
* @return void
*/
public static void cancelSync(Context context) {
// 创建 Intent 对象
Intent intent = new Intent(context, GTaskSyncService.class);
// 将同步操作类型设置为取消同步
intent.putExtra(GTaskSyncService.ACTION_STRING_NAME, GTaskSyncService.ACTION_CANCEL_SYNC);
// 启动服务
context.startService(intent);
}
private void cancelSync() {
if (mSyncTask != null) {
mSyncTask.cancelSync();
}
}
/**
* isSyncing
*
* @return
*/
public static boolean isSyncing() {
// 如果同步任务不为空,则表示正在同步
return mSyncTask != null;
}
@Override
public void onCreate() {
mSyncTask = null;
}
/**
* getProgressString
*
* @return
* @method onStartCommand
* @description 使GtaskSyncService
* @date: 2023-12-17 23:53
* @author:
* @return int
*/
public static String getProgressString() {
// 返回同步进度字符串
return mSyncProgress;
}
}
@Override
public int onStartCommand(Intent intent, int flags, int startId) {
Bundle bundle = intent.getExtras();
if (bundle != null && bundle.containsKey(ACTION_STRING_NAME)) {
switch (bundle.getInt(ACTION_STRING_NAME, ACTION_INVALID)) {
// 开始同步或取消同步
// 2023-12-17 23:52
case ACTION_START_SYNC:
startSync();
break;
case ACTION_CANCEL_SYNC:
cancelSync();
break;
default:
break;
}
return START_STICKY;
}
return super.onStartCommand(intent, flags, startId);
}
@Override
public void onLowMemory() {
if (mSyncTask != null) {
mSyncTask.cancelSync();
}
}
public IBinder onBind(Intent intent) {
return null;
}
public void sendBroadcast(String msg) {
mSyncProgress = msg;
Intent intent = new Intent(GTASK_SERVICE_BROADCAST_NAME);
intent.putExtra(GTASK_SERVICE_BROADCAST_IS_SYNCING, mSyncTask != null);
intent.putExtra(GTASK_SERVICE_BROADCAST_PROGRESS_MSG, msg);
sendBroadcast(intent);
}
public static void startSync(Activity activity) {
GTaskManager.getInstance().setActivityContext(activity);
Intent intent = new Intent(activity, GTaskSyncService.class);
intent.putExtra(GTaskSyncService.ACTION_STRING_NAME, GTaskSyncService.ACTION_START_SYNC);
activity.startService(intent);
}
public static void cancelSync(Context context) {
Intent intent = new Intent(context, GTaskSyncService.class);
intent.putExtra(GTaskSyncService.ACTION_STRING_NAME, GTaskSyncService.ACTION_CANCEL_SYNC);
context.startService(intent);
}
public static boolean isSyncing() {
return mSyncTask != null;
}
public static String getProgressString() {
return mSyncProgress;
}
}

@ -15,7 +15,6 @@
*/
package net.micode.notes.model;
import android.content.ContentProviderOperation;
import android.content.ContentProviderResult;
import android.content.ContentUris;
@ -34,23 +33,16 @@ import net.micode.notes.data.Notes.TextNote;
import java.util.ArrayList;
/**
* Note 便
* 便便便
*/
public class Note {
private ContentValues mNoteDiffValues; // 便签差异值
private NoteData mNoteData; // 便签数据
private static final String TAG = "Note"; // 日志标签
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);
@ -73,90 +65,45 @@ public class Note {
return noteId;
}
/**
* 便便
*/
public Note() {
mNoteDiffValues = new ContentValues();
mNoteData = new NoteData();
}
/**
* 便
* @param key
* @param value
*/
public void setTopState(String key, int value) {
mNoteDiffValues.put(key, value);
}
/**
* 便
* @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 truefalse
*/
public boolean isLocalModified() {
return mNoteDiffValues.size() > 0 || mNoteData.isLocalModified();
}
/**
* 便
* @param context
* @param noteId 便ID
* @return truefalse
*/
public boolean syncNote(Context context, long noteId) {
if (noteId <= 0) {
throw new IllegalArgumentException("Wrong note id:" + noteId);
@ -166,10 +113,16 @@ public class Note {
return true;
}
/**
* In theory, once data changed, the note should be updated on {@link NoteColumns#LOCAL_MODIFIED} and
* {@link NoteColumns#MODIFIED_DATE}. For data safety, though update note fails, we also update the
* note data info
*/
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();
@ -181,19 +134,17 @@ public class Note {
return true;
}
/**
* NoteData 便
*/
private class NoteData {
private long mTextDataId; // 文本数据ID
private ContentValues mTextDataValues; // 文本数据值
private long mCallDataId; // 通话数据ID
private ContentValues mCallDataValues; // 通话数据值
private static final String TAG = "NoteData"; // 日志标签
private long mTextDataId;
private ContentValues mTextDataValues;
private long mCallDataId;
private ContentValues mCallDataValues;
private static final String TAG = "NoteData";
/**
*
*/
public NoteData() {
mTextDataValues = new ContentValues();
mCallDataValues = new ContentValues();
@ -201,18 +152,10 @@ public class Note {
mCallDataId = 0;
}
/**
* 便
* @return truefalse
*/
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");
@ -220,10 +163,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");
@ -231,35 +170,22 @@ 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 URInull
*/
Uri pushIntoContentResolver(Context context, long noteId) {
/**
* Check for safety
*/
if (noteId <= 0) {
throw new IllegalArgumentException("Wrong note id:" + noteId);
}
@ -281,7 +207,8 @@ public class Note {
return null;
}
} else {
builder = ContentProviderOperation.newUpdate(ContentUris .withAppendedId(Notes.CONTENT_DATA_URI, mTextDataId));
builder = ContentProviderOperation.newUpdate(ContentUris.withAppendedId(
Notes.CONTENT_DATA_URI, mTextDataId));
builder.withValues(mTextDataValues);
operationList.add(builder.build());
}

@ -31,74 +31,82 @@ 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; // 修改日期
private int mBgColorId; // 背景颜色ID
private int mWidgetId; // 小部件ID
private int mWidgetType; // 小部件类型
private int mTop; // 置顶状态
private long mFolderId; // 文件夹ID
private Context mContext; // 上下文
private long mAlertDate;
private long mModifiedDate;
private int mBgColorId;
private int mWidgetId;
private int mWidgetType;
private static final String TAG = "WorkingNote"; // 日志标签
private int mTop;
private boolean mIsDeleted; // 是否已删除
private NoteSettingChangedListener mNoteSettingStatusListener; // 设置变化监听器
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, // 数据ID
DataColumns.CONTENT, // 数据内容
DataColumns.MIME_TYPE, // 数据类型
DataColumns.DATA1, // 数据1
DataColumns.DATA2, // 数据2
DataColumns.DATA3, // 数据3
DataColumns.DATA4 // 数据4
DataColumns.ID,
DataColumns.CONTENT,
DataColumns.MIME_TYPE,
DataColumns.DATA1,
DataColumns.DATA2,
DataColumns.DATA3,
DataColumns.DATA4,
};
// 便签查询投影
public static final String[] NOTE_PROJECTION = new String[] {
NoteColumns.PARENT_ID, // 父ID
NoteColumns.ALERTED_DATE, // 提醒日期
NoteColumns.BG_COLOR_ID, // 背景颜色ID
NoteColumns.WIDGET_ID, // 小部件ID
NoteColumns.WIDGET_TYPE, // 小部件类型
NoteColumns.MODIFIED_DATE, // 修改日期
NoteColumns.TOP // 置顶状态
NoteColumns.PARENT_ID,
NoteColumns.ALERTED_DATE,
NoteColumns.BG_COLOR_ID,
NoteColumns.WIDGET_ID,
NoteColumns.WIDGET_TYPE,
NoteColumns.MODIFIED_DATE,
NoteColumns.TOP
};
// 列索引
private static final int DATA_ID_COLUMN = 0;
private static final int DATA_CONTENT_COLUMN = 1;
private static final int DATA_MIME_TYPE_COLUMN = 2;
private static final int DATA_MODE_COLUMN = 3;
private static final int NOTE_PARENT_ID_COLUMN = 0;
private static final int NOTE_ALERTED_DATE_COLUMN = 1;
private static final int NOTE_BG_COLOR_ID_COLUMN = 2;
private static final int NOTE_WIDGET_ID_COLUMN = 3;
private static final int NOTE_WIDGET_TYPE_COLUMN = 4;
private static final int NOTE_MODIFIED_DATE_COLUMN = 5;
private static final int NOTE_TOP_STATE_COLUMN = 6;
/**
* 便
* @param context
* @param folderId ID
*/
// New note construct
private WorkingNote(Context context, long folderId) {
mContext = context;
mAlertDate = 0;
@ -112,12 +120,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;
@ -127,11 +130,7 @@ public class WorkingNote {
loadNote();
}
/**
* 便
*/
private void loadNote() {
// 查询便签信息
Cursor cursor = mContext.getContentResolver().query(
ContentUris.withAppendedId(Notes.CONTENT_NOTE_URI, mNoteId), NOTE_PROJECTION, null,
null, null);
@ -154,11 +153,7 @@ public class WorkingNote {
loadNoteData();
}
/**
* 便
*/
private void loadNoteData() {
// 查询便签数据
Cursor cursor = mContext.getContentResolver().query(Notes.CONTENT_DATA_URI, DATA_PROJECTION,
DataColumns.NOTE_ID + "=?", new String[] {
String.valueOf(mNoteId)
@ -186,17 +181,16 @@ 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) {
/**
* @method: createEmptyNote
* @description: 便便
* @date: 2023/12/20 23:55
* @author: zhoukexing
* @param: [context, folderId, widgetId, widgetType, defaultBgColorId]
* @return: net.micode.notes.model.WorkingNote WorkingNote
*/
WorkingNote note = new WorkingNote(context, folderId);
note.setBgColorId(defaultBgColorId);
note.setWidgetId(widgetId);
@ -204,20 +198,47 @@ 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 truefalse
*/
public synchronized boolean saveNote() {// TODO: 2023/12/19 要仔细溯源看看
/**
* @method: saveNote
* @description: 便true
* @date: 2023/12/19 23:44
* @author: zhoukexing
* @param: []
* @return: boolean
*/
if (isWorthSaving()) { // 判断是否有新内容-->是否值得注释 @zhoukexing 2023/12/19 23:45
if (!existInDatabase()) { // 判断是否在数据库里存在==是新便签还是已有便签 @zhoukexing 2023/12/19 23:45
if ((mNoteId = Note.getNewNoteId(mContext, mFolderId)) == 0) {
Log.e(TAG, "Create new note fail with id:" + mNoteId);
return false;
}
}
mNote.syncNote(mContext, mNoteId); // 和远程同步 @zhoukexing 2023/12/19 23:46
/**
* Update widget content if there exist any widget of this note
*/
if (mWidgetId != AppWidgetManager.INVALID_APPWIDGET_ID
&& mWidgetType != Notes.TYPE_WIDGET_INVALIDE
&& mNoteSettingStatusListener != null) {
mNoteSettingStatusListener.onWidgetChanged();
}
return true;
} else {
return false; // 没有需要保存的就返回false @zhoukexing 2023/12/19 23:46
}
}
public boolean existInDatabase() {
return mNoteId > 0;
}
private boolean isWorthSaving() {
if (mIsDeleted || (!existInDatabase() && TextUtils.isEmpty(mContent))
|| (existInDatabase() && !mNote.isLocalModified())) {
@ -228,60 +249,63 @@ public class WorkingNote {
}
/**
*
* @param l
* @method: setOnSettingStatusChangedListener
* @description: NoteEditActivityNoteSettingChangedListener
* @date: 2023/12/21 0:10
* @author: zhoukexing
* @param: [l] NoteEditActivity
* @return: void
*/
public void setOnSettingStatusChangedListener(NoteSettingChangedListener l) {
//Q: 这里的l是怎么获取的。l是NoteEditActivity @zkx 2023/12/21
mNoteSettingStatusListener = l;
}
/**
* 便
*/
public void reverseTopState() {
mTop ^= 1;
mNote.setTopState(NoteColumns.TOP, mTop);
}
/**
* 便
* @return
*/
public int getmTop() {
public int getmTop(){
return mTop;
}
/**
* 便
* @param date
* @param set
* @Method setAlertDate
* @Date 2023/12/13 11:43
* @param date
* @param set
* @Author lenovo
* @Return void
* @Description
*/
public void setAlertDate(long date, boolean set) {
// 更新便签项中的提醒日期
if (date != mAlertDate) {
mAlertDate = date;
mNote.setNoteValue(NoteColumns.ALERTED_DATE, String.valueOf(mAlertDate));
}
// 设置提醒的监听事件
if (mNoteSettingStatusListener != null) {
mNoteSettingStatusListener.onClockAlertChanged(date, set);
}
}
/**
* 便
* @param mark
* @method: markDeleted
* @description:
* @date: 2023/12/21 0:50
* @author: zhoukexing
* @param: [mark]
* @return: void
*/
public void markDeleted(boolean mark) {
mIsDeleted = mark;
if (mWidgetId != AppWidgetManager.INVALID_APPWIDGET_ID
&& mWidgetType != Notes.TYPE_WIDGET_INVALIDE && mNoteSettingStatusListener != null) {
mNoteSettingStatusListener.onWidgetChanged();
mNoteSettingStatusListener.onWidgetChanged();
}
}
/**
* 便ID
* @param id ID
*/
public void setBgColorId(int id) {
if (id != mBgColorId) {
mBgColorId = id;
@ -292,10 +316,6 @@ public class WorkingNote {
}
}
/**
* 便
* @param mode
*/
public void setCheckListMode(int mode) {
if (mMode != mode) {
if (mNoteSettingStatusListener != null) {
@ -306,10 +326,6 @@ public class WorkingNote {
}
}
/**
* 便
* @param type
*/
public void setWidgetType(int type) {
if (type != mWidgetType) {
mWidgetType = type;
@ -317,10 +333,6 @@ public class WorkingNote {
}
}
/**
* 便ID
* @param id ID
*/
public void setWidgetId(int id) {
if (id != mWidgetId) {
mWidgetId = id;
@ -328,10 +340,6 @@ public class WorkingNote {
}
}
/**
* 便
* @param text
*/
public void setWorkingText(String text) {
if (!TextUtils.equals(mContent, text)) {
mContent = text;
@ -339,138 +347,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 truefalse
*/
public boolean hasClockAlert() {
return (mAlertDate > 0);
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,15 +1,17 @@
/*
* (c) 2010-2011, MiCode (www.micode.net)
* Copyright (c) 2010-2011, The MiCode Open Source Community (www.micode.net)
*
* 使 Apache License, Version 2.0
* 使
*
* 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.
*/
// 导入必要的类和接口
@ -19,6 +21,8 @@ import android.app.Activity;
import android.app.AlertDialog;
import android.content.Context;
import android.content.DialogInterface;
import android.content.DialogInterface.OnClickListener;
import android.content.DialogInterface.OnDismissListener;
import android.content.Intent;
import android.media.AudioManager;
import android.media.MediaPlayer;
@ -36,137 +40,170 @@ import net.micode.notes.tool.DataUtils;
import java.io.IOException;
/**
* AlarmAlertActivity
* Activity
*/
public class AlarmAlertActivity extends Activity implements DialogInterface.OnClickListener, DialogInterface.OnDismissListener {
private long mNoteId; // 便签在数据库中的 ID
private String mSnippet; // 闹钟提示时显示的文本片段
private static final int SNIPPET_PREVIEW_MAX_LEN = 60; // 文本片段的最大长度
private MediaPlayer mPlayer; // 用于播放闹钟声音的 MediaPlayer
// AlarmAlertActivity类继承自Activity实现OnClickListener和OnDismissListener接口
public class AlarmAlertActivity extends Activity implements OnClickListener, OnDismissListener {
private long mNoteId; //文本在数据库存储中的ID号
private String mSnippet; //闹钟提示时出现的文本片段
private static final int SNIPPET_PREW_MAX_LEN = 60;
MediaPlayer mPlayer;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
requestWindowFeature(Window.FEATURE_NO_TITLE); // 去掉标题栏
//Bundle类型的数据与Map类型的数据相似都是以key-value的形式存储数据的
//onsaveInstanceState方法是用来保存Activity的状态的
//能从onCreate的参数savedInsanceState中获得状态数据
requestWindowFeature(Window.FEATURE_NO_TITLE);
//界面显示——无标题
final Window win = getWindow();
win.addFlags(WindowManager.LayoutParams.FLAG_SHOW_WHEN_LOCKED); // 在锁屏状态下显示
win.addFlags(WindowManager.LayoutParams.FLAG_SHOW_WHEN_LOCKED);
if (!isScreenOn()) {
// 如果屏幕是关闭的,点亮屏幕并允许锁屏
win.addFlags(WindowManager.LayoutParams.FLAG_KEEP_SCREEN_ON
//保持窗体点亮
| WindowManager.LayoutParams.FLAG_TURN_SCREEN_ON
//将窗体点亮
| WindowManager.LayoutParams.FLAG_ALLOW_LOCK_WHILE_SCREEN_ON
//允许窗体点亮时锁屏
| WindowManager.LayoutParams.FLAG_LAYOUT_INSET_DECOR);
}
}//在手机锁屏后如果到了闹钟提示时间,点亮屏幕
Intent intent = getIntent();
try {
mNoteId = Long.valueOf(intent.getData().getPathSegments().get(1));
mSnippet = DataUtils.getSnippetById(this.getContentResolver(), mNoteId);
if (mSnippet.length() > SNIPPET_PREVIEW_MAX_LEN) {
mSnippet = mSnippet.substring(0, SNIPPET_PREVIEW_MAX_LEN) + getResources().getString(R.string.notelist_string_info);
}
//根据ID从数据库中获取标签的内容
//getContentResolver是实现数据共享实例存储。
mSnippet = mSnippet.length() > SNIPPET_PREW_MAX_LEN ? mSnippet.substring(0,
SNIPPET_PREW_MAX_LEN) + getResources().getString(R.string.notelist_string_info)
: mSnippet;
//判断标签片段是否达到符合长度
} catch (IllegalArgumentException e) {
e.printStackTrace();
return;
}
/*
try
{
// 代码区
}
catch(Exception e)
{
// 异常处理
}
*/
mPlayer = new MediaPlayer();
if (DataUtils.visibleInNoteDatabase(getContentResolver(), mNoteId, Notes.TYPE_NOTE)) {
showActionDialog();
//弹出对话框
playAlarmSound();
//闹钟提示音激发
} else {
finish();
//完成闹钟动作
}
}
/**
*
* @return true false
*/
private boolean isScreenOn() {
//判断屏幕是否锁屏,调用系统函数判断,最后返回值是布尔类型
PowerManager pm = (PowerManager) getSystemService(Context.POWER_SERVICE);
return pm.isScreenOn();
}
/**
*
*/
private void playAlarmSound() {
//闹钟提示音激发
Uri url = RingtoneManager.getActualDefaultRingtoneUri(this, RingtoneManager.TYPE_ALARM);
int silentModeStreams = Settings.System.getInt(getContentResolver(), Settings.System.MODE_RINGER_STREAMS_AFFECTED, 0);
//调用系统的铃声管理URI得到闹钟提示音
int silentModeStreams = Settings.System.getInt(getContentResolver(),
Settings.System.MODE_RINGER_STREAMS_AFFECTED, 0);
if ((silentModeStreams & (1 << AudioManager.STREAM_ALARM)) != 0) {
mPlayer.setAudioStreamType(silentModeStreams);
} else {
mPlayer.setAudioStreamType(AudioManager.STREAM_ALARM);
}
try {
mPlayer.setDataSource(this, url);
//方法setDataSource(Context context, Uri uri)
//解释:无返回值,设置多媒体数据来源【根据 Uri】
mPlayer.prepare();
//准备同步
mPlayer.setLooping(true);
//设置是否循环播放
mPlayer.start();
} catch (IllegalArgumentException | SecurityException | IllegalStateException | IOException e) {
//开始播放
} catch (IllegalArgumentException e) {
// TODO Auto-generated catch block
e.printStackTrace();
//e.printStackTrace()函数功能是抛出异常, 还将显示出更深的调用信息
//System.out.println(e),这个方法打印出异常,并且输出在哪里出现的异常
} catch (SecurityException e) {
// TODO Auto-generated catch block
e.printStackTrace();
} catch (IllegalStateException e) {
// TODO Auto-generated catch block
e.printStackTrace();
} catch (IOException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
}
/**
*
*/
private void showActionDialog() {
AlertDialog.Builder dialog = new AlertDialog.Builder(this);
//AlertDialog的构造方法全部是Protected的
//所以不能直接通过new一个AlertDialog来创建出一个AlertDialog。
//要创建一个AlertDialog就要用到AlertDialog.Builder中的create()方法
//如这里的dialog就是新建了一个AlertDialog
dialog.setTitle(R.string.app_name);
//为对话框设置标题
dialog.setMessage(mSnippet);
//为对话框设置内容
dialog.setPositiveButton(R.string.notealert_ok, this);
//给对话框添加"Yes"按钮
if (isScreenOn()) {
dialog.setNegativeButton(R.string.notealert_enter, this);
}
}//对话框添加"No"按钮
dialog.show().setOnDismissListener(this);
}
/**
*
* @param dialog
* @param which
*/
@Override
public void onClick(DialogInterface dialog, int which) {
switch (which) {
//用which来选择click后下一步的操作
case DialogInterface.BUTTON_NEGATIVE:
// 取消操作,跳转到便签编辑页面
//这是取消操作
Intent intent = new Intent(this, NoteEditActivity.class);
//实现两个类间的数据传输
intent.setAction(Intent.ACTION_VIEW);
//设置动作属性
intent.putExtra(Intent.EXTRA_UID, mNoteId);
//实现key-value对
//EXTRA_UID为keymNoteId为键
startActivity(intent);
//开始动作
break;
default:
// 确定操作,不做任何处理
//这是确定操作
break;
}
}
/**
*
* @param dialog
*/
@Override
public void onDismiss(DialogInterface dialog) {
//忽略
stopAlarmSound();
//停止闹钟声音
finish();
//完成该动作
}
/**
*
*/
private void stopAlarmSound() {
if (mPlayer != null) {
mPlayer.stop();
//停止播放
mPlayer.release();
//释放MediaPlayer对象
mPlayer = null;
}
}

@ -1,15 +1,17 @@
/*
* (c) 2010-2011, MiCode (www.micode.net)
* Copyright (c) 2010-2011, The MiCode Open Source Community (www.micode.net)
*
* 使 Apache License, Version 2.0
* 使
*
* 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.
*/
package net.micode.notes.ui;
@ -25,21 +27,18 @@ import android.database.Cursor;
import net.micode.notes.data.Notes;
import net.micode.notes.data.Notes.NoteColumns;
/**
* AlarmInitReceiver
* /
*/
public class AlarmInitReceiver extends BroadcastReceiver {
// 定义查询数据库时要获取的列
private static final String[] PROJECTION = new String[]{
private static final String [] PROJECTION = new String [] {
NoteColumns.ID, // 笔记的ID
NoteColumns.ALERTED_DATE // 笔记的提醒日期
};
// 定义列的索引以便在Cursor中快速访问
private static final int COLUMN_ID = 0;
private static final int COLUMN_ALERTED_DATE = 1;
private static final int COLUMN_ID = 0;
private static final int COLUMN_ALERTED_DATE = 1;
@Override
public void onReceive(Context context, Intent intent) {
@ -52,7 +51,7 @@ public class AlarmInitReceiver extends BroadcastReceiver {
Cursor c = context.getContentResolver().query(Notes.CONTENT_NOTE_URI,
PROJECTION,
NoteColumns.ALERTED_DATE + ">? AND " + NoteColumns.TYPE + "=" + Notes.TYPE_NOTE,
new String[]{String.valueOf(currentDate)}, // 查询参数,这里只有当前日期
new String[] { String.valueOf(currentDate) }, // 查询参数,这里只有当前日期
null); // 不需要排序
// 检查Cursor是否为null
@ -75,11 +74,11 @@ public class AlarmInitReceiver extends BroadcastReceiver {
PendingIntent pendingIntent = PendingIntent.getBroadcast(context, 0, sender, 0);
// 获取AlarmManager服务
AlarmManager alarmManager = (AlarmManager) context.getSystemService(Context.ALARM_SERVICE);
AlarmManager alermManager = (AlarmManager) context.getSystemService(Context.ALARM_SERVICE);
// 设置一个闹钟当到达提醒日期时触发PendingIntent即发送广播给AlarmReceiver
// AlarmManager.RTC_WAKEUP表示在指定时间唤醒设备并发送广播
alarmManager.set(AlarmManager.RTC_WAKEUP, alertDate, pendingIntent);
alermManager.set(AlarmManager.RTC_WAKEUP, alertDate, pendingIntent);
} while (c.moveToNext()); // 移动到下一条记录,继续循环
}

@ -1,15 +1,17 @@
/*
* (c) 2010-2011, MiCode (www.micode.net)
* Copyright (c) 2010-2011, The MiCode Open Source Community (www.micode.net)
*
* 使 Apache License, Version 2.0
* 使
*
* 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.
*/
// 指定类所在的包名
@ -20,28 +22,22 @@ import android.content.BroadcastReceiver; // 广播接收器基类
import android.content.Context; // 提供应用环境信息的类
import android.content.Intent; // 用于组件间通信的消息传递对象
/**
* AlarmReceiver 广
* AlarmAlertActivity
*/
// 定义一个公开类AlarmReceiver它继承自BroadcastReceiver
public class AlarmReceiver extends BroadcastReceiver {
/**
* 广
* AlarmAlertActivity
*
* @param context
* @param intent 广 Intent
*/
// 重写onReceive方法当接收到广播时调用此方法
@Override
public void onReceive(Context context, Intent intent) {
// 设置 intent 的目标组件为 AlarmAlertActivity
// 设置intent的目标组件为AlarmAlertActivity类
// 这意味着当这个广播接收器接收到广播时它希望启动AlarmAlertActivity活动
intent.setClass(context, AlarmAlertActivity.class);
// 添加 FLAG_ACTIVITY_NEW_TASK 标志,确保活动作为新的任务启动
// 为intent添加FLAG_ACTIVITY_NEW_TASK标志
// 这通常用于从非活动上下文中启动活动时,确保活动作为新的任务启动
intent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
// 启动 AlarmAlertActivity 活动
// 使用context对象调用startActivity方法来启动由intent指定的活动
// 由于intent已经被设置为启动AlarmAlertActivity这行代码的作用就是启动AlarmAlertActivity活动
context.startActivity(intent);
}
}

@ -1,42 +1,41 @@
/*
* (c) 2010-2011, MiCode (www.micode.net)
* Copyright (c) 2010-2011, The MiCode Open Source Community (www.micode.net)
*
* 使 Apache License, Version 2.0
* 使
*
* 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.
*/
// 指定类所在的包名
// 指定类所在的包名
package net.micode.notes.ui;
// 导入所需的 Java Android
// 导入所需的Java和Android类
import java.text.DateFormatSymbols;
import java.util.Calendar;
import net.micode.notes.R;
import android.content.Context;
import android.text.format.DateFormat;
import android.view.View;
import android.widget.FrameLayout;
import android.widget.NumberPicker;
// 定义一个名为DateTimePicker的类它继承自FrameLayout
/**
* DateTimePicker
* FrameLayout
* 24 12 AM/PM
*/
public class DateTimePicker extends FrameLayout {
// 默认启用状态
//FrameLayout是布局模板之一
//所有的子元素全部在屏幕的右上方
private static final boolean DEFAULT_ENABLE_STATE = true;
// 常量定义
private static final int HOURS_IN_HALF_DAY = 12;
private static final int HOURS_IN_ALL_DAY = 24;
private static final int DAYS_IN_ALL_WEEK = 7;
@ -50,26 +49,27 @@ public class DateTimePicker extends FrameLayout {
private static final int MINUT_SPINNER_MAX_VAL = 59;
private static final int AMPM_SPINNER_MIN_VAL = 0;
private static final int AMPM_SPINNER_MAX_VAL = 1;
// 初始化控件
//初始化控件
private final NumberPicker mDateSpinner;
private final NumberPicker mHourSpinner;
private final NumberPicker mMinuteSpinner;
private final NumberPicker mAmPmSpinner;
// NumberPicker 是数字选择器
// 这里定义的四个变量分别用于选择日期、小时、分钟和上午/下午
//NumberPicker是数字选择器
//这里定义的四个变量全部是在设置闹钟时需要选择的变量(如日期、时、分、上午或者下午)
private Calendar mDate;
//定义了Calendar类型的变量mDate用于操作时间
private String[] mDateDisplayValues = new String[DAYS_IN_ALL_WEEK];
private boolean mIsAm;
private boolean mIs24HourView;
private boolean mIsEnabled = DEFAULT_ENABLE_STATE;
private boolean mInitialising;
private OnDateTimeChangedListener mOnDateTimeChangedListener;
// 监听器定义
private NumberPicker.OnValueChangeListener mOnDateChangedListener = new NumberPicker.OnValueChangeListener() {
@Override
public void onValueChange(NumberPicker picker, int oldVal, int newVal) {
@ -77,41 +77,49 @@ public class DateTimePicker extends FrameLayout {
updateDateControl();
onDateTimeChanged();
}
};
};//OnValueChangeListener这是时间改变监听器这里主要是对日期的监听
//将现在日期的值传递给mDateupdateDateControl是同步操作
private NumberPicker.OnValueChangeListener mOnHourChangedListener = new NumberPicker.OnValueChangeListener() {
@Override
//这里是对 小时Hour 的监听
@Override
public void onValueChange(NumberPicker picker, int oldVal, int newVal) {
boolean isDateChanged = false;
Calendar cal = Calendar.getInstance();
//声明一个Calendar的变量cal便于后续的操作
if (!mIs24HourView) {
if (!mIsAm && oldVal == HOURS_IN_HALF_DAY - 1 && newVal == HOURS_IN_HALF_DAY) {
cal.setTimeInMillis(mDate.getTimeInMillis());
cal.add(Calendar.DAY_OF_YEAR, 1);
isDateChanged = true;
//这里是对于12小时制时晚上11点和12点交替时对日期的更改
} else if (mIsAm && oldVal == HOURS_IN_HALF_DAY && newVal == HOURS_IN_HALF_DAY - 1) {
cal.setTimeInMillis(mDate.getTimeInMillis());
cal.add(Calendar.DAY_OF_YEAR, -1);
isDateChanged = true;
}
//这里是对于12小时制时凌晨11点和12点交替时对日期的更改
if (oldVal == HOURS_IN_HALF_DAY - 1 && newVal == HOURS_IN_HALF_DAY ||
oldVal == HOURS_IN_HALF_DAY && newVal == HOURS_IN_HALF_DAY - 1) {
mIsAm = !mIsAm;
updateAmPmControl();
}
}//这里是对于12小时制时中午11点和12点交替时对AM和PM的更改
} else {
if (oldVal == HOURS_IN_ALL_DAY - 1 && newVal == 0) {
cal.setTimeInMillis(mDate.getTimeInMillis());
cal.add(Calendar.DAY_OF_YEAR, 1);
isDateChanged = true;
//这里是对于24小时制时晚上11点和12点交替时对日期的更改
} else if (oldVal == 0 && newVal == HOURS_IN_ALL_DAY - 1) {
cal.setTimeInMillis(mDate.getTimeInMillis());
cal.add(Calendar.DAY_OF_YEAR, -1);
isDateChanged = true;
}
}
} //这里是对于12小时制时凌晨11点和12点交替时对日期的更改
int newHour = mHourSpinner.getValue() % HOURS_IN_HALF_DAY + (mIsAm ? 0 : HOURS_IN_HALF_DAY);
//通过数字选择器对newHour的赋值
mDate.set(Calendar.HOUR_OF_DAY, newHour);
//通过set函数将新的Hour值传给mDate
onDateTimeChanged();
if (isDateChanged) {
setCurrentYear(cal.get(Calendar.YEAR));
@ -123,15 +131,19 @@ public class DateTimePicker extends FrameLayout {
private NumberPicker.OnValueChangeListener mOnMinuteChangedListener = new NumberPicker.OnValueChangeListener() {
@Override
//这里是对 分钟Minute改变的监听
public void onValueChange(NumberPicker picker, int oldVal, int newVal) {
int minValue = mMinuteSpinner.getMinValue();
int maxValue = mMinuteSpinner.getMaxValue();
int offset = 0;
//设置offset作为小时改变的一个记录数据
if (oldVal == maxValue && newVal == minValue) {
offset += 1;
} else if (oldVal == minValue && newVal == maxValue) {
offset -= 1;
}
//如果原值为59新值为0则offset加1
//如果原值为0新值为59则offset减1
if (offset != 0) {
mDate.add(Calendar.HOUR_OF_DAY, offset);
mHourSpinner.setValue(getCurrentHour());
@ -151,7 +163,8 @@ public class DateTimePicker extends FrameLayout {
};
private NumberPicker.OnValueChangeListener mOnAmPmChangedListener = new NumberPicker.OnValueChangeListener() {
@Override
//对AM和PM的监听
@Override
public void onValueChange(NumberPicker picker, int oldVal, int newVal) {
mIsAm = !mIsAm;
if (mIsAm) {
@ -164,42 +177,29 @@ public class DateTimePicker extends FrameLayout {
}
};
/**
*
*/
public interface OnDateTimeChangedListener {
void onDateTimeChanged(DateTimePicker view, int year, int month, int dayOfMonth, int hourOfDay, int minute);
void onDateTimeChanged(DateTimePicker view, int year, int month,
int dayOfMonth, int hourOfDay, int minute);
}
/**
* DateTimePicker
* @param context
*/
public DateTimePicker(Context context) {
this(context, System.currentTimeMillis());
}
}//通过对数据库的访问,获取当前的系统时间
/**
* DateTimePicker
* @param context
* @param date
*/
public DateTimePicker(Context context, long date) {
this(context, date, DateFormat.is24HourFormat(context));
}
}//上面函数的得到的是一个天文数字1970至今的秒数需要DateFormat将其变得有意义
/**
* DateTimePicker
* @param context
* @param date
* @param is24HourView 24
*/
public DateTimePicker(Context context, long date, boolean is24HourView) {
super(context);
//获取系统时间
mDate = Calendar.getInstance();
mInitialising = true;
mIsAm = getCurrentHourOfDay() >= HOURS_IN_HALF_DAY;
inflate(context, R.layout.datetime_picker, this);
//如果当前Activity里用到别的layout比如对话框layout
//还要设置这个layout上的其他组件的内容就必须用inflate()方法先将对话框的layout找出来
//然后再用findViewById()找到它上面的其它组件
mDateSpinner = (NumberPicker) findViewById(R.id.date);
mDateSpinner.setMinValue(DATE_SPINNER_MIN_VAL);
mDateSpinner.setMaxValue(DATE_SPINNER_MAX_VAL);
@ -214,33 +214,28 @@ public class DateTimePicker extends FrameLayout {
mMinuteSpinner.setOnValueChangedListener(mOnMinuteChangedListener);
String[] stringsForAmPm = new DateFormatSymbols().getAmPmStrings();
mAmPmSpinner = (NumberPicker) findViewById
(R.id.amPm);
mAmPmSpinner = (NumberPicker) findViewById(R.id.amPm);
mAmPmSpinner.setMinValue(AMPM_SPINNER_MIN_VAL);
mAmPmSpinner.setMaxValue(AMPM_SPINNER_MAX_VAL);
mAmPmSpinner.setDisplayedValues(stringsForAmPm);
mAmPmSpinner.setOnValueChangedListener(mOnAmPmChangedListener);
// 更新控件到初始状态
// update controls to initial state
updateDateControl();
updateHourControl();
updateAmPmControl();
set24HourView(is24HourView);
// 设置当前时间
// set to current time
setCurrentDate(date);
setEnabled(isEnabled());
// 设置内容描述
// set the content descriptions
mInitialising = false;
}
/**
* DateTimePicker
* @param enabled
*/
@Override
public void setEnabled(boolean enabled) {
if (mIsEnabled == enabled) {
@ -253,62 +248,67 @@ public class DateTimePicker extends FrameLayout {
mAmPmSpinner.setEnabled(enabled);
mIsEnabled = enabled;
}
/**
* DateTimePicker
* @return true false
*/
//存在疑问setEnabled的作用
//下面的代码通过原程序的注释已经比较清晰,另外可以通过函数名来判断
//下面的各函数主要是对上面代码引用到的各函数功能的实现
@Override
public boolean isEnabled() {
return mIsEnabled;
}
/**
*
* @return
* Get the current date in millis
*
* @return the current date in millis
*/
public long getCurrentDateInTimeMillis() {
return mDate.getTimeInMillis();
}
}//实现函数——得到当前的秒数
/**
*
* @param date
* Set the current date
*
* @param date The current date in millis
*/
public void setCurrentDate(long date) {
Calendar cal = Calendar.getInstance();
cal.setTimeInMillis(date);
setCurrentDate(cal.get(Calendar.YEAR), cal.get(Calendar.MONTH), cal.get(Calendar.DAY_OF_MONTH),
cal.get(Calendar.HOUR_OF_DAY), cal.get(Calendar.MINUTE));
}
}//实现函数功能——设置当前的时间参数是date
/**
*
* @param year
* @param month 0-11
* @param dayOfMonth
* @param hourOfDay 0-23
* @param minute
* Set the current date
*
* @param year The current year
* @param month The current month
* @param dayOfMonth The current dayOfMonth
* @param hourOfDay The current hourOfDay
* @param minute The current minute
*/
public void setCurrentDate(int year, int month, int dayOfMonth, int hourOfDay, int minute) {
public void setCurrentDate(int year, int month,
int dayOfMonth, int hourOfDay, int minute) {
setCurrentYear(year);
setCurrentMonth(month);
setCurrentDay(dayOfMonth);
setCurrentHour(hourOfDay);
setCurrentMinute(minute);
}
}//实现函数功能——设置当前的时间,参数是各详细的变量
/**
*
* @return
* Get current year
*
* @return The current year
*/
//下面是得到year、month、day等值
public int getCurrentYear() {
return mDate.get(Calendar.YEAR);
}
/**
*
* @param year
* Set current year
*
* @param year The current year
*/
public void setCurrentYear(int year) {
if (!mInitialising && year == getCurrentYear()) {
@ -320,16 +320,18 @@ public class DateTimePicker extends FrameLayout {
}
/**
* 0-11
* @return 0-11
* Get current month in the year
*
* @return The current month in the year
*/
public int getCurrentMonth() {
return mDate.get(Calendar.MONTH);
}
/**
* 0-11
* @param month 0-11
* Set current month in the year
*
* @param month The month in the year
*/
public void setCurrentMonth(int month) {
if (!mInitialising && month == getCurrentMonth()) {
@ -341,16 +343,18 @@ public class DateTimePicker extends FrameLayout {
}
/**
*
* @return
* Get current day of the month
*
* @return The day of the month
*/
public int getCurrentDay() {
return mDate.get(Calendar.DAY_OF_MONTH);
}
/**
*
* @param dayOfMonth
* Set current day of the month
*
* @param dayOfMonth The day of the month
*/
public void setCurrentDay(int dayOfMonth) {
if (!mInitialising && dayOfMonth == getCurrentDay()) {
@ -362,15 +366,15 @@ public class DateTimePicker extends FrameLayout {
}
/**
* 0-23
* @return 0-23
* Get current hour in 24 hour mode, in the range (0~23)
* @return The current hour in 24 hour mode
*/
public int getCurrentHourOfDay() {
return mDate.get(Calendar.HOUR_OF_DAY);
}
private int getCurrentHour() {
if (mIs24HourView) {
if (mIs24HourView){
return getCurrentHourOfDay();
} else {
int hour = getCurrentHourOfDay();
@ -383,8 +387,9 @@ public class DateTimePicker extends FrameLayout {
}
/**
* 0-23
* @param hourOfDay 0-23
* Set current hour in 24 hour mode, in the range (0~23)
*
* @param hourOfDay
*/
public void setCurrentHour(int hourOfDay) {
if (!mInitialising && hourOfDay == getCurrentHourOfDay()) {
@ -410,16 +415,16 @@ public class DateTimePicker extends FrameLayout {
}
/**
*
* @return
* Get currentMinute
*
* @return The Current Minute
*/
public int getCurrentMinute() {
return mDate.get(Calendar.MINUTE);
}
/**
*
* @param minute
* Set current minute
*/
public void setCurrentMinute(int minute) {
if (!mInitialising && minute == getCurrentMinute()) {
@ -431,16 +436,16 @@ public class DateTimePicker extends FrameLayout {
}
/**
* 24
* @return true 24 false 12
* @return true if this is in 24 hour view else false.
*/
public boolean is24HourView() {
public boolean is24HourView () {
return mIs24HourView;
}
/**
* 24
* @param is24HourView true 24 false 12
* Set whether in 24 hour or AM/PM mode.
*
* @param is24HourView True for 24 hour mode. False for AM/PM mode.
*/
public void set24HourView(boolean is24HourView) {
if (mIs24HourView == is24HourView) {
@ -466,7 +471,7 @@ public class DateTimePicker extends FrameLayout {
mDateSpinner.setDisplayedValues(mDateDisplayValues);
mDateSpinner.setValue(DAYS_IN_ALL_WEEK / 2);
mDateSpinner.invalidate();
}
}// 对于星期几的算法
private void updateAmPmControl() {
if (mIs24HourView) {
@ -475,7 +480,7 @@ public class DateTimePicker extends FrameLayout {
int index = mIsAm ? Calendar.AM : Calendar.PM;
mAmPmSpinner.setValue(index);
mAmPmSpinner.setVisibility(View.VISIBLE);
}
}// 对于上下午操作的算法
}
private void updateHourControl() {
@ -485,12 +490,12 @@ public class DateTimePicker extends FrameLayout {
} else {
mHourSpinner.setMinValue(HOUR_SPINNER_MIN_VAL_12_HOUR_VIEW);
mHourSpinner.setMaxValue(HOUR_SPINNER_MAX_VAL_12_HOUR_VIEW);
}
}// 对与小时的算法
}
/**
*
* @param callback null
* Set the callback that indicates the 'Set' button has been pressed.
* @param callback the callback, if null will do nothing
*/
public void setOnDateTimeChangedListener(OnDateTimeChangedListener callback) {
mOnDateTimeChangedListener = callback;
@ -503,4 +508,3 @@ public class DateTimePicker extends FrameLayout {
}
}
}

@ -1,120 +1,102 @@
/*
* (c) 2010-2011, MiCode (www.micode.net)
* Copyright (c) 2010-2011, The MiCode Open Source Community (www.micode.net)
*
* 使 Apache License, Version 2.0
* 使
*
* 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.
*/
package net.micode.notes.ui;
package net.micode.notes.ui;
import java.util.Calendar;
import java.util.Calendar;
import net.micode.notes.R;
import net.micode.notes.ui.DateTimePicker;
import net.micode.notes.ui.DateTimePicker.OnDateTimeChangedListener;
import net.micode.notes.R;
import net.micode.notes.ui.DateTimePicker;
import net.micode.notes.ui.DateTimePicker.OnDateTimeChangedListener;
import android.app.AlertDialog;
import android.content.Context;
import android.content.DialogInterface;
import android.content.DialogInterface.OnClickListener;
import android.text.format.DateFormat;
import android.text.format.DateUtils;
import android.app.AlertDialog;
import android.content.Context;
import android.content.DialogInterface;
import android.content.DialogInterface.OnClickListener;
import android.text.format.DateFormat;
import android.text.format.DateUtils;
/**
* DateTimePickerDialog
* AlertDialog
* 24 12 AM/PM
*/
public class DateTimePickerDialog extends AlertDialog implements OnClickListener {
public class DateTimePickerDialog extends AlertDialog implements OnClickListener {
private Calendar mDate = Calendar.getInstance(); // 创建一个 Calendar 实例,用于时间操作
private boolean mIs24HourView; // 是否为 24 小时制
private OnDateTimeSetListener mOnDateTimeSetListener; // 日期时间设置监听器
private DateTimePicker mDateTimePicker; // 日期时间选择器控件
private Calendar mDate = Calendar.getInstance();
//创建一个Calendar类型的变量 mDate方便时间的操作
private boolean mIs24HourView;
private OnDateTimeSetListener mOnDateTimeSetListener;
//声明一个时间日期滚动选择控件 mOnDateTimeSetListener
private DateTimePicker mDateTimePicker;
//DateTimePicker控件控件一般用于让用户可以从日期列表中选择单个值。
//运行时,单击控件边上的下拉箭头,会显示为两个部分:一个下拉列表,一个用于选择日期的
/**
*
*/
public interface OnDateTimeSetListener {
void OnDateTimeSet(AlertDialog dialog, long date);
}
public interface OnDateTimeSetListener {
void OnDateTimeSet(AlertDialog dialog, long date);
}
/**
* DateTimePickerDialog
* @param context
* @param date
*/
public DateTimePickerDialog(Context context, long date) {
super(context);
mDateTimePicker = new DateTimePicker(context);
setView(mDateTimePicker); // 设置对话框的视图
mDateTimePicker.setOnDateTimeChangedListener(new OnDateTimeChangedListener() {
@Override
public void onDateTimeChanged(DateTimePicker view, int year, int month, int dayOfMonth, int hourOfDay, int minute) {
mDate.set(Calendar.YEAR, year);
mDate.set(Calendar.MONTH, month);
mDate.set(Calendar.DAY_OF_MONTH, dayOfMonth);
mDate.set(Calendar.HOUR_OF_DAY, hourOfDay);
mDate.set(Calendar.MINUTE, minute);
updateTitle(mDate.getTimeInMillis()); // 更新对话框标题
}
});
mDate.setTimeInMillis(date); // 设置当前时间
mDate.set(Calendar.SECOND, 0); // 将秒数设置为 0
mDateTimePicker.setCurrentDate(mDate.getTimeInMillis()); // 设置日期时间选择器的当前时间
setButton(context.getString(R.string.datetime_dialog_ok), this); // 设置“确定”按钮
setButton2(context.getString(R.string.datetime_dialog_cancel), (OnClickListener) null); // 设置“取消”按钮
set24HourView(DateFormat.is24HourFormat(this.getContext())); // 设置时间格式
updateTitle(mDate.getTimeInMillis()); // 更新对话框标题
}
public DateTimePickerDialog(Context context, long date) {
//对该界面对话框的实例化
super(context);
//对数据库的操作
mDateTimePicker = new DateTimePicker(context);
setView(mDateTimePicker);
//添加一个子视图
mDateTimePicker.setOnDateTimeChangedListener(new OnDateTimeChangedListener() {
public void onDateTimeChanged(DateTimePicker view, int year, int month,
int dayOfMonth, int hourOfDay, int minute) {
mDate.set(Calendar.YEAR, year);
mDate.set(Calendar.MONTH, month);
mDate.set(Calendar.DAY_OF_MONTH, dayOfMonth);
mDate.set(Calendar.HOUR_OF_DAY, hourOfDay);
mDate.set(Calendar.MINUTE, minute);
//将视图中的各选项设置为系统当前时间
updateTitle(mDate.getTimeInMillis());
}
});
mDate.setTimeInMillis(date);
//得到系统时间
mDate.set(Calendar.SECOND, 0);
//将秒数设置为0
mDateTimePicker.setCurrentDate(mDate.getTimeInMillis());
setButton(context.getString(R.string.datetime_dialog_ok), this);
setButton2(context.getString(R.string.datetime_dialog_cancel), (OnClickListener)null);
//设置按钮
set24HourView(DateFormat.is24HourFormat(this.getContext()));
//时间标准化打印
updateTitle(mDate.getTimeInMillis());
}
/**
* 24
* @param is24HourView true 24 false 12
*/
public void set24HourView(boolean is24HourView) {
mIs24HourView = is24HourView;
mDateTimePicker.set24HourView(is24HourView); // 更新日期时间选择器的时间格式
}
public void set24HourView(boolean is24HourView) {
mIs24HourView = is24HourView;
}
/**
*
* @param callBack
*/
public void setOnDateTimeSetListener(OnDateTimeSetListener callBack) {
mOnDateTimeSetListener = callBack;
}
public void setOnDateTimeSetListener(OnDateTimeSetListener callBack) {
mOnDateTimeSetListener = callBack;
}//将时间日期滚动选择控件实例化
/**
*
* @param date
*/
private void updateTitle(long date) {
int flag =
DateUtils.FORMAT_SHOW_YEAR |
DateUtils.FORMAT_SHOW_DATE |
DateUtils.FORMAT_SHOW_TIME;
flag |= mIs24HourView ? DateUtils.FORMAT_24HOUR : DateUtils.FORMAT_12HOUR;
setTitle(DateUtils.formatDateTime(this.getContext(), date, flag)); // 设置对话框标题
}
private void updateTitle(long date) {
int flag =
DateUtils.FORMAT_SHOW_YEAR |
DateUtils.FORMAT_SHOW_DATE |
DateUtils.FORMAT_SHOW_TIME;
flag |= mIs24HourView ? DateUtils.FORMAT_24HOUR : DateUtils.FORMAT_24HOUR;
setTitle(DateUtils.formatDateTime(this.getContext(), date, flag));
}//android开发中常见日期管理工具类API——DateUtils按照上下午显示时间
/**
*
* @param dialog
* @param which
*/
@Override
public void onClick(DialogInterface dialog, int which) {
if (mOnDateTimeSetListener != null) {
mOnDateTimeSetListener.OnDateTimeSet(this, mDate.getTimeInMillis()); // 通知监听器日期时间已设置
}
}
public void onClick(DialogInterface arg0, int arg1) {
if (mOnDateTimeSetListener != null) {
mOnDateTimeSetListener.OnDateTimeSet(this, mDate.getTimeInMillis());
}
}//第一个参数arg0是接收到点击事件的对话框
//第二个参数arg1是该对话框上的按钮
}

@ -1,15 +1,17 @@
/*
* (c) 2010-2011, MiCode (www.micode.net)
* Copyright (c) 2010-2011, The MiCode Open Source Community (www.micode.net)
*
* 使 Apache License, Version 2.0
* 使
*
* 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.
*/
package net.micode.notes.ui;
@ -25,59 +27,39 @@ import android.widget.PopupMenu.OnMenuItemClickListener;
import net.micode.notes.R;
/**
* DropdownMenu
*
*/
public class DropdownMenu {
private Button mButton; // 按钮控件
private PopupMenu mPopupMenu; // 弹出菜单
private Menu mMenu; // 菜单对象
private Button mButton;
private PopupMenu mPopupMenu;
//声明一个下拉菜单
private Menu mMenu;
/**
* DropdownMenu
* @param context
* @param button
* @param menuId ID
*/
public DropdownMenu(Context context, Button button, int menuId) {
mButton = button;
mButton.setBackgroundResource(R.drawable.dropdown_icon); // 设置按钮背景
mButton.setBackgroundResource(R.drawable.dropdown_icon);
//设置这个view的背景
mPopupMenu = new PopupMenu(context, mButton);
mMenu = mPopupMenu.getMenu();
mPopupMenu.getMenuInflater().inflate(menuId, mMenu); // 加载菜单资源
mPopupMenu.getMenuInflater().inflate(menuId, mMenu);
//MenuInflater是用来实例化Menu目录下的Menu布局文件
//根据ID来确认menu的内容选项
mButton.setOnClickListener(new OnClickListener() {
@Override
public void onClick(View v) {
mPopupMenu.show(); // 显示弹出菜单
mPopupMenu.show();
}
});
}
/**
*
* @param listener
*/
public void setOnDropdownMenuItemClickListener(OnMenuItemClickListener listener) {
if (mPopupMenu != null) {
mPopupMenu.setOnMenuItemClickListener(listener); // 设置菜单项点击监听器
}
mPopupMenu.setOnMenuItemClickListener(listener);
}//设置菜单的监听
}
/**
*
* @param id ID
* @return
*/
public MenuItem findItem(int id) {
return mMenu.findItem(id); // 查找菜单项
}
return mMenu.findItem(id);
}//对于菜单选项的初始化,根据索引搜索菜单需要的选项
/**
*
* @param title
*/
public void setTitle(CharSequence title) {
mButton.setText(title); // 设置按钮标题
}
mButton.setText(title);
}//布局文件,设置标题
}

@ -1,114 +1,87 @@
/*
* (c) 2010-2011, MiCode (www.micode.net)
* Copyright (c) 2010-2011, The MiCode Open Source Community (www.micode.net)
*
* 使 Apache License, Version 2.0
* 使
*
* 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.
*/
package net.micode.notes.ui;
package net.micode.notes.ui;
import android.content.Context;
import android.database.Cursor;
import android.view.View;
import android.view.ViewGroup;
import android.widget.CursorAdapter;
import android.widget.LinearLayout;
import android.widget.TextView;
import android.content.Context;
import android.database.Cursor;
import android.view.View;
import android.view.ViewGroup;
import android.widget.CursorAdapter;
import android.widget.LinearLayout;
import android.widget.TextView;
import net.micode.notes.R;
import net.micode.notes.data.Notes;
import net.micode.notes.data.Notes.NoteColumns;
import net.micode.notes.R;
import net.micode.notes.data.Notes;
import net.micode.notes.data.Notes.NoteColumns;
/**
* FoldersListAdapter
* CursorAdapter ListView
*/
public class FoldersListAdapter extends CursorAdapter {
public static final String[] PROJECTION = {
NoteColumns.ID,
NoteColumns.SNIPPET
}; // 查询数据库时需要的列
public static final int ID_COLUMN = 0;
public static final int NAME_COLUMN = 1;
public class FoldersListAdapter extends CursorAdapter {
//CursorAdapter是Cursor和ListView的接口
//FoldersListAdapter继承了CursorAdapter的类
//主要作用是便签数据库和用户的交互
//这里就是用folder文件夹的形式展现给用户
public static final String [] PROJECTION = {
NoteColumns.ID,
NoteColumns.SNIPPET
};//调用数据库中便签的ID和片段
public static final int ID_COLUMN = 0;
public static final int NAME_COLUMN = 1;
public FoldersListAdapter(Context context, Cursor c) {
super(context, c);
// TODO Auto-generated constructor stub
}//数据库操作
/**
* FoldersListAdapter
* @param context
* @param c
*/
public FoldersListAdapter(Context context, Cursor c) {
super(context, c);
}
@Override
public View newView(Context context, Cursor cursor, ViewGroup parent) {
//ViewGroup是容器
return new FolderListItem(context);
}//创建一个文件夹,对于各文件夹中子标签的初始化
/**
*
* @param context
* @param cursor
* @param parent
* @return
*/
@Override
public View newView(Context context, Cursor cursor, ViewGroup parent) {
return new FolderListItem(context); // 创建一个新的文件夹列表项
}
@Override
public void bindView(View view, Context context, Cursor cursor) {
if (view instanceof FolderListItem) {
String folderName = (cursor.getLong(ID_COLUMN) == Notes.ID_ROOT_FOLDER) ? context
.getString(R.string.menu_move_parent_folder) : cursor.getString(NAME_COLUMN);
((FolderListItem) view).bind(folderName);
}
}//将各个布局文件绑定起来
/**
*
* @param view
* @param context
* @param cursor
*/
@Override
public void bindView(View view, Context context, Cursor cursor) {
if (view instanceof FolderListItem) {
String folderName = (cursor.getLong(ID_COLUMN) == Notes.ID_ROOT_FOLDER) ? context
.getString(R.string.menu_move_parent_folder) : cursor.getString(NAME_COLUMN);
((FolderListItem) view).bind(folderName); // 绑定文件夹名称
}
}
public String getFolderName(Context context, int position) {
Cursor cursor = (Cursor) getItem(position);
return (cursor.getLong(ID_COLUMN) == Notes.ID_ROOT_FOLDER) ? context
.getString(R.string.menu_move_parent_folder) : cursor.getString(NAME_COLUMN);
}//根据数据库中标签的ID得到标签的各项内容
/**
*
* @param context
* @param position
* @return
*/
public String getFolderName(Context context, int position) {
Cursor cursor = (Cursor) getItem(position);
return (cursor.getLong(ID_COLUMN) == Notes.ID_ROOT_FOLDER) ? context
.getString(R.string.menu_move_parent_folder) : cursor.getString(NAME_COLUMN);
}
private class FolderListItem extends LinearLayout {
private TextView mName;
/**
* FolderListItem
*/
private class FolderListItem extends LinearLayout {
private TextView mName;
public FolderListItem(Context context) {
super(context);
//操作数据库
inflate(context, R.layout.folder_list_item, this);
//根据布局文件的名字等信息将其找出来
mName = (TextView) findViewById(R.id.tv_folder_name);
}
/**
* FolderListItem
* @param context
*/
public FolderListItem(Context context) {
super(context);
inflate(context, R.layout.folder_list_item, this); // 加载布局
mName = (TextView) findViewById(R.id.tv_folder_name); // 获取文件夹名称文本视图
}
public void bind(String name) {
mName.setText(name);
}
}
/**
*
* @param name
*/
public void bind(String name) {
mName.setText(name); // 设置文件夹名称
}
}
}
}

@ -1,237 +1,286 @@
/*
* (c) 2010-2011, MiCode (www.micode.net)
* Copyright (c) 2010-2011, The MiCode Open Source Community (www.micode.net)
*
* 使 Apache License, Version 2.0
* 使
*
* 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.
*/
package net.micode.notes.ui;
import android.content.Context;
import android.graphics.Rect;
import android.text.Layout;
import android.text.Selection;
import android.text.Spanned;
import android.text.TextUtils;
import android.text.style.URLSpan;
import android.util.AttributeSet;
import android.util.Log;
import android.view.ContextMenu;
import android.view.KeyEvent;
import android.view.MenuItem;
import android.view.MenuItem.OnMenuItemClickListener;
import android.view.MotionEvent;
import android.widget.EditText;
import net.micode.notes.R;
import java.util.HashMap;
import java.util.Map;
/**
* NoteEditText EditText便
*
*/
public class NoteEditText extends EditText {
private static final String TAG = "NoteEditText";
private int mIndex;
private int mSelectionStartBeforeDelete;
private static final String SCHEME_TEL = "tel:";
private static final String SCHEME_HTTP = "http:";
private static final String SCHEME_EMAIL = "mailto:";
private static final Map<String, Integer> sSchemaActionResMap = new HashMap<String, Integer>();
static {
sSchemaActionResMap.put(SCHEME_TEL, R.string.note_link_tel);
sSchemaActionResMap.put(SCHEME_HTTP, R.string.note_link_web);
sSchemaActionResMap.put(SCHEME_EMAIL, R.string.note_link_email);
}
/**
*
*/
public interface OnTextViewChangeListener {
/**
*
*/
void onEditTextDelete(int index, String text);
/**
*
*/
void onEditTextEnter(int index, String text);
/**
*
*/
void onTextChange(int index, boolean hasText);
}
private OnTextViewChangeListener mOnTextViewChangeListener;
/**
* NoteEditText
* @param context
*/
public NoteEditText(Context context) {
super(context, null);
mIndex = 0;
}
/**
*
* @param index
*/
public void setIndex(int index) {
mIndex = index;
}
/**
*
* @param listener
*/
public void setOnTextViewChangeListener(OnTextViewChangeListener listener) {
mOnTextViewChangeListener = listener;
}
/**
* NoteEditText
* @param context
* @param attrs
*/
public NoteEditText(Context context, AttributeSet attrs) {
super(context, attrs, android.R.attr.editTextStyle);
}
/**
* NoteEditText
* @param context
* @param attrs
* @param defStyle
*/
public NoteEditText(Context context, AttributeSet attrs, int defStyle) {
super(context, attrs, defStyle);
}
@Override
public boolean onTouchEvent(MotionEvent event) {
switch (event.getAction()) {
case MotionEvent.ACTION_DOWN:
int x = (int) event.getX();
int y = (int) event.getY();
x -= getTotalPaddingLeft();
y -= getTotalPaddingTop();
x += getScrollX();
y += getScrollY();
Layout layout = getLayout();
int line = layout.getLineForVertical(y);
int off = layout.getOffsetForHorizontal(line, x);
Selection.setSelection(getText(), off);
break;
}
return super.onTouchEvent(event);
}
@Override
public boolean onKeyDown(int keyCode, KeyEvent event) {
switch (keyCode) {
case KeyEvent.KEYCODE_ENTER:
if (mOnTextViewChangeListener != null) {
return false;
}
break;
case KeyEvent.KEYCODE_DEL:
mSelectionStartBeforeDelete = getSelectionStart();
break;
default:
break;
}
return super.onKeyDown(keyCode, event);
}
@Override
public boolean onKeyUp(int keyCode, KeyEvent event) {
switch (keyCode) {
case KeyEvent.KEYCODE_DEL:
if (mOnTextViewChangeListener != null) {
if (0 == mSelectionStartBeforeDelete && mIndex != 0) {
mOnTextViewChangeListener.onEditTextDelete(mIndex, getText().toString());
return true;
}
} else {
Log.d(TAG, "OnTextViewChangeListener was not set");
}
break;
case KeyEvent.KEYCODE_ENTER:
if (mOnTextViewChangeListener != null) {
int selectionStart = getSelectionStart();
String text = getText().subSequence(selectionStart, length()).toString();
setText(getText().subSequence(0, selectionStart));
mOnTextViewChangeListener.onEditTextEnter(mIndex + 1, text);
} else {
Log.d(TAG, "OnTextViewChangeListener was not set");
}
break;
default:
break;
}
return super.onKeyUp(keyCode, event);
}
@Override
protected void onFocusChanged(boolean focused, int direction, Rect previouslyFocusedRect) {
if (mOnTextViewChangeListener != null) {
if (!focused && TextUtils.isEmpty(getText())) {
mOnTextViewChangeListener.onTextChange(mIndex, false);
} else {
mOnTextViewChangeListener.onTextChange(mIndex, true);
}
}
super.onFocusChanged(focused, direction, previouslyFocusedRect);
}
@Override
protected void onCreateContextMenu(ContextMenu menu) {
if (getText() instanceof Spanned) {
int selStart = getSelectionStart();
int selEnd = getSelectionEnd();
int min = Math.min(selStart, selEnd);
int max = Math.max(selStart, selEnd);
final URLSpan[] urls = ((Spanned) getText()).getSpans(min, max, URLSpan.class);
if (urls.length == 1) {
int defaultResId = 0;
for (String schema : sSchemaActionResMap.keySet()) {
if (urls[0].getURL().indexOf(schema) >= 0) {
defaultResId = sSchemaActionResMap.get(schema);
break;
}
}
if (defaultResId == 0) {
defaultResId = R.string.note_link_other;
}
menu.add(0, 0, 0, defaultResId).setOnMenuItemClickListener(
new OnMenuItemClickListener() {
public boolean onMenuItemClick(MenuItem item) {
urls[0].onClick(NoteEditText.this);
return true;
}
});
}
}
super.onCreateContextMenu(menu);
}
}
package net.micode.notes.ui;
import android.content.Context;
import android.graphics.Rect;
import android.text.Layout;
import android.text.Selection;
import android.text.Spanned;
import android.text.TextUtils;
import android.text.style.URLSpan;
import android.util.AttributeSet;
import android.util.Log;
import android.view.ContextMenu;
import android.view.KeyEvent;
import android.view.MenuItem;
import android.view.MenuItem.OnMenuItemClickListener;
import android.view.MotionEvent;
import android.widget.EditText;
import net.micode.notes.R;
import java.util.HashMap;
import java.util.Map;
//继承edittext设置便签设置文本框
public class NoteEditText extends EditText {
private static final String TAG = "NoteEditText";
private int mIndex;
private int mSelectionStartBeforeDelete;
private static final String SCHEME_TEL = "tel:" ;
private static final String SCHEME_HTTP = "http:" ;
private static final String SCHEME_EMAIL = "mailto:" ;
///建立一个字符和整数的hash表用于链接电话网站还有邮箱
private static final Map<String, Integer> sSchemaActionResMap = new HashMap<String, Integer>();
static {
sSchemaActionResMap.put(SCHEME_TEL, R.string.note_link_tel);
sSchemaActionResMap.put(SCHEME_HTTP, R.string.note_link_web);
sSchemaActionResMap.put(SCHEME_EMAIL, R.string.note_link_email);
}
/**
* Call by the {@link NoteEditActivity} to delete or add edit text
*/
//在NoteEditActivity中删除或添加文本的操作可以看做是一个文本是否被变的标记英文注释已说明的很清楚
public interface OnTextViewChangeListener {
/**
* Delete current edit text when {@link KeyEvent#KEYCODE_DEL} happens
* and the text is null
*/
//处理删除按键时的操作
void onEditTextDelete(int index, String text);
/**
* Add edit text after current edit text when {@link KeyEvent#KEYCODE_ENTER}
* happen
*/
//处理进入按键时的操作
void onEditTextEnter(int index, String text);
/**
* Hide or show item option when text change
*/
void onTextChange(int index, boolean hasText);
}
private OnTextViewChangeListener mOnTextViewChangeListener;
//根据context设置文本
public NoteEditText(Context context) {
super(context, null);//用super引用父类变量
mIndex = 0;
}
//设置当前光标
public void setIndex(int index) {
mIndex = index;
}
//初始化文本修改标记
public void setOnTextViewChangeListener(OnTextViewChangeListener listener) {
mOnTextViewChangeListener = listener;
}
//AttributeSet 百度了一下是自定义空控件属性,用于维护便签动态变化的属性
//初始化便签
public NoteEditText(Context context, AttributeSet attrs) {
super(context, attrs, android.R.attr.editTextStyle);
}
// 根据defstyle自动初始化
public NoteEditText(Context context, AttributeSet attrs, int defStyle) {
super(context, attrs, defStyle);
// TODO Auto-generated construct or stub
}
@Override
//view里的函数处理手机屏幕的所有事件
/*event
*/
public boolean onTouchEvent(MotionEvent event) {
switch (event.getAction()) {
//重写了需要处理屏幕被按下的事件
case MotionEvent.ACTION_DOWN:
//跟新当前坐标值
int x = (int) event.getX();
int y = (int) event.getY();
x -= getTotalPaddingLeft();
y -= getTotalPaddingTop();
x += getScrollX();
y += getScrollY();
//用布局控件layout根据x,y的新值设置新的位置
Layout layout = getLayout();
int line = layout.getLineForVertical(y);
int off = layout.getOffsetForHorizontal(line, x);
//更新光标新的位置
Selection.setSelection(getText(), off);
break;
}
return super.onTouchEvent(event);
}
@Override
/*
*
*
*/
public boolean onKeyDown(int keyCode, KeyEvent event) {
switch (keyCode) {
//根据按键的 Unicode 编码值来处理
case KeyEvent.KEYCODE_ENTER:
//“进入”按键
if (mOnTextViewChangeListener != null) {
return false;
}
break;
case KeyEvent.KEYCODE_DEL:
//“删除”按键
mSelectionStartBeforeDelete = getSelectionStart();
break;
default:
break;
}
//继续执行父类的其他点击事件
return super.onKeyDown(keyCode, event);
}
@Override
/*
*
*
*/
public boolean onKeyUp(int keyCode, KeyEvent event) {
switch(keyCode) {
//根据按键的 Unicode 编码值来处理有删除和进入2种操作
case KeyEvent.KEYCODE_DEL:
if (mOnTextViewChangeListener != null) {
//若是被修改过
if (0 == mSelectionStartBeforeDelete && mIndex != 0) {
//若之前有被修改并且文档不为空
mOnTextViewChangeListener.onEditTextDelete(mIndex, getText().toString());
//利用上文OnTextViewChangeListener对KEYCODE_DEL按键情况的删除函数进行删除
return true;
}
} else {
Log.d(TAG, "OnTextViewChangeListener was not seted");
//其他情况报错,文档的改动监听器并没有建立
}
break;
case KeyEvent.KEYCODE_ENTER:
//同上也是分为监听器是否建立2种情况
if (mOnTextViewChangeListener != null) {
int selectionStart = getSelectionStart();
//获取当前位置
String text = getText().subSequence(selectionStart, length()).toString();
//获取当前文本
setText(getText().subSequence(0, selectionStart));
//根据获取的文本设置当前文本
mOnTextViewChangeListener.onEditTextEnter(mIndex + 1, text);
//当{@link KeyEvent#KEYCODE_ENTER}添加新文本
} else {
Log.d(TAG, "OnTextViewChangeListener was not seted");
//其他情况报错,文档的改动监听器并没有建立
}
break;
default:
break;
}
//继续执行父类的其他按键弹起的事件
return super.onKeyUp(keyCode, event);
}
@Override
/*
*
*
* focusedViewFocusedtruefalse
direction
RectViewnull
*/
protected void onFocusChanged(boolean focused, int direction, Rect previouslyFocusedRect) {
if (mOnTextViewChangeListener != null) {
//若监听器已经建立
if (!focused && TextUtils.isEmpty(getText())) {
//获取到焦点并且文本不为空
mOnTextViewChangeListener.onTextChange(mIndex, false);
//mOnTextViewChangeListener子函数置false隐藏事件选项
} else {
mOnTextViewChangeListener.onTextChange(mIndex, true);
//mOnTextViewChangeListener子函数置true显示事件选项
}
}
//继续执行父类的其他焦点变化的事件
super.onFocusChanged(focused, direction, previouslyFocusedRect);
}
@Override
/*
*
*
*/
protected void onCreateContextMenu(ContextMenu menu) {
if (getText() instanceof Spanned) {
//有文本存在
int selStart = getSelectionStart();
int selEnd = getSelectionEnd();
//获取文本开始和结尾位置
int min = Math.min(selStart, selEnd);
int max = Math.max(selStart, selEnd);
//获取开始到结尾的最大值和最小值
final URLSpan[] urls = ((Spanned) getText()).getSpans(min, max, URLSpan.class);
//设置url的信息的范围值
if (urls.length == 1) {
int defaultResId = 0;
for(String schema: sSchemaActionResMap.keySet()) {
//获取计划表中所有的key值
if(urls[0].getURL().indexOf(schema) >= 0) {
//若url可以添加则在添加后将defaultResId置为key所映射的值
defaultResId = sSchemaActionResMap.get(schema);
break;
}
}
if (defaultResId == 0) {
//defaultResId == 0则说明url并没有添加任何东西所以置为连接其他SchemaActionResMap的值
defaultResId = R.string.note_link_other;
}
//建立菜单
menu.add(0, 0, 0, defaultResId).setOnMenuItemClickListener(
new OnMenuItemClickListener() {
//新建按键监听器
public boolean onMenuItemClick(MenuItem item) {
// goto a new intent
urls[0].onClick(NoteEditText.this);
//根据相应的文本设置菜单的按键
return true;
}
});
}
}
//继续执行父类的其他菜单创建的事件
super.onCreateContextMenu(menu);
}
}

@ -1,15 +1,17 @@
/*
* (c) 2010-2011, MiCode (www.micode.net)
* Copyright (c) 2010-2011, The MiCode Open Source Community (www.micode.net)
*
* 使 Apache License, Version 2.0
* 使
*
* 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.
*/
package net.micode.notes.ui;
@ -23,12 +25,9 @@ import net.micode.notes.data.Notes;
import net.micode.notes.data.Notes.NoteColumns;
import net.micode.notes.tool.DataUtils;
/**
* NoteItemData 便
* 便访
*/
public class NoteItemData {
static final String[] PROJECTION = new String[] {
static final String [] PROJECTION = new String [] {
NoteColumns.ID,
NoteColumns.ALERTED_DATE,
NoteColumns.BG_COLOR_ID,
@ -44,20 +43,23 @@ public class NoteItemData {
NoteColumns.TOP,
};
private static final int ID_COLUMN = 0;
private static final int ALERTED_DATE_COLUMN = 1;
private static final int BG_COLOR_ID_COLUMN = 2;
private static final int CREATED_DATE_COLUMN = 3;
private static final int HAS_ATTACHMENT_COLUMN = 4;
private static final int MODIFIED_DATE_COLUMN = 5;
private static final int NOTES_COUNT_COLUMN = 6;
private static final int PARENT_ID_COLUMN = 7;
private static final int SNIPPET_COLUMN = 8;
private static final int TYPE_COLUMN = 9;
private static final int WIDGET_ID_COLUMN = 10;
private static final int WIDGET_TYPE_COLUMN = 11;
private static final int TOP_STATE_COLUMN = 12;
private static final int ID_COLUMN = 0;
private static final int ALERTED_DATE_COLUMN = 1;
private static final int BG_COLOR_ID_COLUMN = 2;
private static final int CREATED_DATE_COLUMN = 3;
private static final int HAS_ATTACHMENT_COLUMN = 4;
private static final int MODIFIED_DATE_COLUMN = 5;
private static final int NOTES_COUNT_COLUMN = 6;
private static final int PARENT_ID_COLUMN = 7;
private static final int SNIPPET_COLUMN = 8;
private static final int TYPE_COLUMN = 9;
private static final int WIDGET_ID_COLUMN = 10;
private static final int WIDGET_TYPE_COLUMN = 11;
private static final int TOP_STATE_COLUMN = 12;
/** NotesDatabaseHelper.java
*
* @zhoukexing 2023/12/25 20:22 */
private long mId;
private long mAlertDate;
private int mBgColorId;
@ -81,16 +83,19 @@ public class NoteItemData {
private boolean mIsMultiNotesFollowingFolder;
/**
* 便
* @param context
* @param cursor
* @method: NoteItemData
* @description:
* @date: 2023/12/25 19:58
* @author: zhoukexing
* @param: [context, cursor]
* @return:
*/
public NoteItemData(Context context, Cursor cursor) {
mId = cursor.getLong(ID_COLUMN);
public NoteItemData(Context context, Cursor cursor) { // 把cursor理解为这样一个指针指向一个表格 @zhoukexing 2023/12/25 20:12
mId = cursor.getLong(ID_COLUMN); // 可以根据传入的列号获取到表格里对应列的值 @zhoukexing 2023/12/25 20:12
mAlertDate = cursor.getLong(ALERTED_DATE_COLUMN);
mBgColorId = cursor.getInt(BG_COLOR_ID_COLUMN);
mCreatedDate = cursor.getLong(CREATED_DATE_COLUMN);
mHasAttachment = (cursor.getInt(HAS_ATTACHMENT_COLUMN) > 0);
mHasAttachment = (cursor.getInt(HAS_ATTACHMENT_COLUMN) > 0) ? true : false;
mModifiedDate = cursor.getLong(MODIFIED_DATE_COLUMN);
mNotesCount = cursor.getInt(NOTES_COUNT_COLUMN);
mParentId = cursor.getLong(PARENT_ID_COLUMN);
@ -103,8 +108,9 @@ public class NoteItemData {
mTop = cursor.getInt(TOP_STATE_COLUMN);
mPhoneNumber = "";
if (mParentId == Notes.ID_CALL_RECORD_FOLDER) {
if (mParentId == Notes.ID_CALL_RECORD_FOLDER) { //Q: 文件夹为什么有电话记录之说?怎么是通过一个便签的父文件夹来判断便签内有无电话号码?@zkx 2023/12/25
mPhoneNumber = DataUtils.getCallNumberByNoteId(context.getContentResolver(), mId);
// 根据电话号码锁定联系人名称,若不在联系人里,直接使用电话号码 @zhoukexing 2023/12/25 20:17
if (!TextUtils.isEmpty(mPhoneNumber)) {
mName = Contact.getContact(context, mPhoneNumber);
if (mName == null) {
@ -116,16 +122,12 @@ public class NoteItemData {
if (mName == null) {
mName = "";
}
checkPosition(cursor);
checkPostion(cursor);
}
/**
* 便
* @param cursor
*/
private void checkPosition(Cursor cursor) {
mIsLastItem = cursor.isLast();
mIsFirstItem = cursor.isFirst();
private void checkPostion(Cursor cursor) {
mIsLastItem = cursor.isLast() ? true : false;
mIsFirstItem = cursor.isFirst() ? true : false;
mIsOnlyOneItem = (cursor.getCount() == 1);
mIsMultiNotesFollowingFolder = false;
mIsOneNoteFollowingFolder = false;
@ -142,148 +144,101 @@ public class NoteItemData {
}
}
if (!cursor.moveToNext()) {
throw new IllegalStateException("Cursor moved to previous but can't move back");
throw new IllegalStateException("cursor move to previous but can't move back");
}
}
}
}
/**
* 便
* @return true 便false
*/
public boolean isOneFollowingFolder() {
return mIsOneNoteFollowingFolder;
}
/**
* 便
* @return true 便false
*/
public boolean isMultiFollowingFolder() {
return mIsMultiNotesFollowingFolder;
}
/**
* 便
* @return true 便false
*/
public boolean isLast() {
return mIsLastItem;
}
/**
*
* @return
*/
public String getCallName() {
return mName;
}
/**
* 便
* @return true 便false
*/
public boolean isFirst() {
return mIsFirstItem;
}
/**
* 便
* @return true 便false
*/
public boolean isSingle() {
return mIsOnlyOneItem;
}
/**
* 便
* @return 便
*/
public int getTop() {
public int getTop(){
return mTop;
}
/**
* 便 ID
* @return 便 ID
*/
public long getId() {
return mId;
}
/**
* 便
* @return 便
*/
public long getAlertDate() {
return mAlertDate;
}
/**
* 便
* @return 便
*/
public long getCreatedDate() {
return mCreatedDate;
}
/**
* 便
* @return true false
*/
public boolean hasAttachment() {
return mHasAttachment;
}
/**
* 便
* @return 便
*/
public long getModifiedDate() {
return mModifiedDate;
}
/**
* 便 ID
* @return 便 ID
*/
public int getBgColorId() {
return mBgColorId;
}
/**
* 便 ID
* @return 便 ID
*/
public long getParentId() {
return mParentId;
}
/**
* 便
* @return 便
*/
public int getNotesCount() {
return mNotesCount;
}
/**
* 便 ID
* @return 便 ID
*/
public long getFolderId() {
public long getFolderId () {
return mParentId;
}
/**
* 便
* @return 便
*/
public int getType() {
return mType;
}
/**
* 便
* @return 便
public int getWidgetType() {
return mWidgetType;
}
public int getWidgetId() {
return mWidgetId;
}
public String getSnippet() {
return mSnippet;
}
public boolean hasAlert() {
return (mAlertDate > 0);
}
public boolean isCallRecord() {
return (mParentId == Notes.ID_CALL_RECORD_FOLDER && !TextUtils.isEmpty(mPhoneNumber));
}
public static int getNoteType(Cursor cursor) {
return cursor.getInt(TYPE_COLUMN);
}
}

@ -1,249 +1,273 @@
/*
* (c) 2010-2011, MiCode (www.micode.net)
* Copyright (c) 2010-2011, The MiCode Open Source Community (www.micode.net)
*
* 使 Apache License, Version 2.0
* 使
*
* 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.
*/
package net.micode.notes.ui;
package net.micode.notes.ui;
import android.content.Context;
import android.database.Cursor;
import android.util.Log;
import android.view.View;
import android.view.ViewGroup;
import android.widget.CursorAdapter;
import android.content.Context;
import android.database.Cursor;
import android.util.Log;
import android.view.View;
import android.view.ViewGroup;
import android.widget.CursorAdapter;
import net.micode.notes.data.Notes;
import java.util.Collection;
import java.util.HashMap;
import java.util.HashSet;
import java.util.Iterator;
import net.micode.notes.data.Notes;
/**
* NotesListAdapter 便
* CursorAdapter便 ListView
*/
public class NotesListAdapter extends CursorAdapter {
private static final String TAG = "NotesListAdapter";
private Context mContext;
private HashMap<Integer, Boolean> mSelectedIndex;
private int mNotesCount; // 便签数
private boolean mChoiceMode; // 选择模式标记
/**
*
*/
public static class AppWidgetAttribute {
public int widgetId;
public int widgetType;
}
/**
* NotesListAdapter
* @param context
*/
public NotesListAdapter(Context context) {
super(context, null); // 父类对象置空
mSelectedIndex = new HashMap<Integer, Boolean>(); // 新建选项下标的哈希表
mContext = context;
mNotesCount = 0;
}
@Override
/**
*
* @param context
* @param cursor
* @param parent
* @return
*/
public View newView(Context context, Cursor cursor, ViewGroup parent) {
return new NotesListItem(context);
}
@Override
/**
*
* @param view
* @param context
* @param cursor
*/
public void bindView(View view, Context context, Cursor cursor) {
if (view instanceof NotesListItem) {
NoteItemData itemData = new NoteItemData(context, cursor);
((NotesListItem) view).bind(context, itemData, mChoiceMode,
isSelectedItem(cursor.getPosition()));
}
}
/**
*
* @param position
* @param checked
*/
public void setCheckedItem(final int position, final boolean checked) {
mSelectedIndex.put(position, checked);
notifyDataSetChanged();
}
/**
*
* @return true false
*/
public boolean isInChoiceMode() {
return mChoiceMode;
}
/**
*
* @param mode
*/
public void setChoiceMode(boolean mode) {
mSelectedIndex.clear();
mChoiceMode = mode;
}
/**
*
* @param checked
*/
public void selectAll(boolean checked) {
Cursor cursor = getCursor();
for (int i = 0; i < getCount(); i++) {
if (cursor.moveToPosition(i)) {
if (NoteItemData.getNoteType(cursor) == Notes.TYPE_NOTE) {
setCheckedItem(i, checked);
}
}
}
}
/**
* ID
* @return ID
*/
public HashSet<Long> getSelectedItemIds() {
HashSet<Long> itemSet = new HashSet<Long>();
for (Integer position : mSelectedIndex.keySet()) {
if (mSelectedIndex.get(position) == true) {
Long id = getItemId(position);
if (id == Notes.ID_ROOT_FOLDER) {
Log.d(TAG, "Wrong item id, should not happen");
} else {
itemSet.add(id);
}
}
}
return itemSet;
}
/**
*
* @return
*/
public HashSet<AppWidgetAttribute> getSelectedWidget() {
HashSet<AppWidgetAttribute> itemSet = new HashSet<AppWidgetAttribute>();
for (Integer position : mSelectedIndex.keySet()) {
if (mSelectedIndex.get(position) == true) {
Cursor c = (Cursor) getItem(position);
if (c != null) {
AppWidgetAttribute widget = new AppWidgetAttribute();
NoteItemData item = new NoteItemData(mContext, c);
widget.widgetId = item.getWidgetId();
widget.widgetType = item.getWidgetType();
itemSet.add(widget);
} else {
Log.e(TAG, "Invalid cursor");
return null;
}
}
}
return itemSet;
}
/**
*
* @return
*/
public int getSelectedCount() {
Collection<Boolean> values = mSelectedIndex.values();
if (null == values) {
return 0;
}
Iterator<Boolean> iter = values.iterator();
int count = 0;
while (iter.hasNext()) {
if (true == iter.next()) {
count++;
}
}
return count;
}
/**
*
* @return true false
*/
public boolean isAllSelected() {
int checkedCount = getSelectedCount();
return (checkedCount != 0 && checkedCount == mNotesCount);
}
/**
*
* @param position
* @return true false
*/
public boolean isSelectedItem(final int position) {
if (null == mSelectedIndex.get(position)) {
return false;
}
return mSelectedIndex.get(position);
}
@Override
/**
* 便
*/
protected void onContentChanged() {
super.onContentChanged();
calcNotesCount();
}
@Override
/**
* 便
* @param cursor
*/
public void changeCursor(Cursor cursor) {
super.changeCursor(cursor);
calcNotesCount();
}
/**
* 便
*/
private void calcNotesCount() {
mNotesCount = 0;
for (int i = 0; i < getCount(); i++) {
Cursor c = (Cursor) getItem(i);
if (c != null) {
if (NoteItemData.getNoteType(c) == Notes.TYPE_NOTE) {
mNotesCount++;
}
} else {
Log.e(TAG, "Invalid cursor");
return;
}
}
}
}
import java.util.Collection;
import java.util.HashMap;
import java.util.HashSet;
import java.util.Iterator;
/*
* 便CursorAdaptercursorListView
* NotesListAdapter便
*/
public class NotesListAdapter extends CursorAdapter {
private static final String TAG = "NotesListAdapter";
private Context mContext;
private HashMap<Integer, Boolean> mSelectedIndex;
private int mNotesCount; //便签数
private boolean mChoiceMode; //选择模式标记
/*
* widget
*/
public static class AppWidgetAttribute {
public int widgetId;
public int widgetType;
};
/*
* 便
*
*/
public NotesListAdapter(Context context) {
super(context, null); //父类对象置空
mSelectedIndex = new HashMap<Integer, Boolean>(); //新建选项下标的hash表
mContext = context;
mNotesCount = 0;
}
@Override
/*
*
* 使NotesListItem
*/
public View newView(Context context, Cursor cursor, ViewGroup parent) {
return new NotesListItem(context);
}
/*
*
*
*/
@Override
public void bindView(View view, Context context, Cursor cursor) {
if (view instanceof NotesListItem) {
//若view是NotesListItem的一个实例
NoteItemData itemData = new NoteItemData(context, cursor);
((NotesListItem) view).bind(context, itemData, mChoiceMode,
isSelectedItem(cursor.getPosition()));
//则新建一个项目选项并且用bind跟将view和鼠标内容便签数据捆绑在一起
}
}
/*
*
*
*/
public void setCheckedItem(final int position, final boolean checked) {
mSelectedIndex.put(position, checked);
//根据定位和是否勾选设置下标
notifyDataSetChanged();
//在修改后刷新activity
}
/*
*
*/
public boolean isInChoiceMode() {
return mChoiceMode;
}
/*
*
* mode
*/
public void setChoiceMode(boolean mode) {
mSelectedIndex.clear();
mChoiceMode = mode;
}
/*
*
*
*/
public void selectAll(boolean checked) {
Cursor cursor = getCursor();
//获取光标位置
for (int i = 0; i < getCount(); i++) {
if (cursor.moveToPosition(i)) {
if (NoteItemData.getNoteType(cursor) == Notes.TYPE_NOTE) {
setCheckedItem(i, checked);
}
}
}
//遍历所有光标可用的位置在判断为便签类型之后勾选单项框
}
/*
*
*
*/
public HashSet<Long> getSelectedItemIds() {
HashSet<Long> itemSet = new HashSet<Long>();
//建立hash表
for (Integer position : mSelectedIndex.keySet()) {
//遍历所有的关键
if (mSelectedIndex.get(position) == true) {
//若光标位置可用
Long id = getItemId(position);
if (id == Notes.ID_ROOT_FOLDER) {
//原文件不需要添加
Log.d(TAG, "Wrong item id, should not happen");
} else {
itemSet.add(id);
}
//则将id该下标假如选项集合中
}
}
return itemSet;
}
/*
* Widget
*
*/
public HashSet<AppWidgetAttribute> getSelectedWidget() {
HashSet<AppWidgetAttribute> itemSet = new HashSet<AppWidgetAttribute>();
for (Integer position : mSelectedIndex.keySet()) {
if (mSelectedIndex.get(position) == true) {
Cursor c = (Cursor) getItem(position);
//以上4句和getSelectedItemIds一样不再重复
if (c != null) {
//光标位置可用的话就建立新的Widget属性并编辑下标和类型最后添加到选项集中
AppWidgetAttribute widget = new AppWidgetAttribute();
NoteItemData item = new NoteItemData(mContext, c);
widget.widgetId = item.getWidgetId();
widget.widgetType = item.getWidgetType();
itemSet.add(widget);
/**
* Don't close cursor here, only the adapter could close it
*/
} else {
Log.e(TAG, "Invalid cursor");
return null;
}
}
}
return itemSet;
}
/*
*
*
*/
public int getSelectedCount() {
Collection<Boolean> values = mSelectedIndex.values();
//首先获取选项下标的值
if (null == values) {
return 0;
}
Iterator<Boolean> iter = values.iterator();
//初始化叠加器
int count = 0;
while (iter.hasNext()) {
if (true == iter.next()) {
//若value值为真计数+1
count++;
}
}
return count;
}
/*
*
*
*/
public boolean isAllSelected() {
int checkedCount = getSelectedCount();
return (checkedCount != 0 && checkedCount == mNotesCount);
//获取选项数看是否等于便签的个数
}
/*
*
*
*/
public boolean isSelectedItem(final int position) {
if (null == mSelectedIndex.get(position)) {
return false;
}
return mSelectedIndex.get(position);
}
@Override
/*
* activity便
*
*/
protected void onContentChanged() {
super.onContentChanged();
//执行基类函数
calcNotesCount();
}
@Override
/*
* activity便
*/
public void changeCursor(Cursor cursor) {
super.changeCursor(cursor);
//执行基类函数
calcNotesCount();
}
/*
* 便
*
*/
private void calcNotesCount() {
mNotesCount = 0;
for (int i = 0; i < getCount(); i++) {
//获取总数同时遍历
Cursor c = (Cursor) getItem(i);
if (c != null) {
if (NoteItemData.getNoteType(c) == Notes.TYPE_NOTE) {
mNotesCount++;
//若该位置不为空并且文本类型为便签就+1
}
} else {
Log.e(TAG, "Invalid cursor");
return;
}
//否则报错
}
}
}

@ -1,141 +1,132 @@
/*
* (c) 2010-2011, MiCode (www.micode.net)
* Copyright (c) 2010-2011, The MiCode Open Source Community (www.micode.net)
*
* 使 Apache License, Version 2.0
* 使
*
* 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.
*/
package net.micode.notes.ui;
package net.micode.notes.ui;
import android.content.Context;
import android.text.format.DateUtils;
import android.view.View;
import android.widget.CheckBox;
import android.widget.ImageView;
import android.widget.LinearLayout;
import android.widget.TextView;
import android.content.Context;
import android.text.format.DateUtils;
import android.view.View;
import android.widget.CheckBox;
import android.widget.ImageView;
import android.widget.LinearLayout;
import android.widget.TextView;
import net.micode.notes.R;
import net.micode.notes.data.Notes;
import net.micode.notes.tool.DataUtils;
import net.micode.notes.tool.ResourceParser.NoteItemBgResources;
import net.micode.notes.R;
import net.micode.notes.data.Notes;
import net.micode.notes.tool.DataUtils;
import net.micode.notes.tool.ResourceParser.NoteItemBgResources;
/**
* NotesListItem 便
* LinearLayout便
*/
public class NotesListItem extends LinearLayout {
private ImageView mAlert; // 闹钟图标
private TextView mTitle; // 标题
private TextView mTime; // 时间
private TextView mCallName; // 电话联系人名称
private NoteItemData mItemData; // 便签数据
private CheckBox mCheckBox; // 复选框
/**
* NotesListItem
* @param context
*/
public NotesListItem(Context context) {
super(context);
inflate(context, R.layout.note_item, this);
mAlert = (ImageView) findViewById(R.id.iv_alert_icon);
mTitle = (TextView) findViewById(R.id.tv_title);
mTime = (TextView) findViewById(R.id.tv_time);
mCallName = (TextView) findViewById(R.id.tv_name);
mCheckBox = (CheckBox) findViewById(android.R.id.checkbox);
}
/**
* 便
* @param context
* @param data 便
* @param choiceMode
* @param checked
*/
public void bind(Context context, NoteItemData data, boolean choiceMode, boolean checked) {
if (choiceMode && data.getType() == Notes.TYPE_NOTE) {
mCheckBox.setVisibility(View.VISIBLE);
mCheckBox.setChecked(checked);
} else {
mCheckBox.setVisibility(View.GONE);
}
mItemData = data;
//创建便签列表项目选项
public class NotesListItem extends LinearLayout {
private ImageView mAlert;//闹钟图片
private TextView mTitle; //标题
private TextView mTime; //时间
private TextView mCallName; //
private NoteItemData mItemData; //标签数据
private CheckBox mCheckBox; //打钩框
if (data.getId() == Notes.ID_CALL_RECORD_FOLDER) {
mCallName.setVisibility(View.GONE);
mAlert.setVisibility(View.VISIBLE);
mTitle.setTextAppearance(context, R.style.TextAppearancePrimaryItem);
mTitle.setText(context.getString(R.string.call_record_folder_name)
+ context.getString(R.string.format_folder_files_count, data.getNotesCount()));
mAlert.setImageResource(R.drawable.call_record);
} else if (data.getParentId() == Notes.ID_CALL_RECORD_FOLDER) {
mCallName.setVisibility(View.VISIBLE);
mCallName.setText(data.getCallName());
mTitle.setTextAppearance(context, R.style.TextAppearanceSecondaryItem);
mTitle.setText(DataUtils.getFormattedSnippet(data.getSnippet()));
if (data.hasAlert()) {
mAlert.setImageResource(R.drawable.clock);
mAlert.setVisibility(View.VISIBLE);
} else {
mAlert.setVisibility(View.GONE);
}
} else {
mCallName.setVisibility(View.GONE);
mTitle.setTextAppearance(context, R.style.TextAppearancePrimaryItem);
if (data.getType() == Notes.TYPE_FOLDER) {
mTitle.setText(data.getSnippet()
+ context.getString(R.string.format_folder_files_count,
data.getNotesCount()));
mAlert.setVisibility(View.GONE);
} else {
mTitle.setText(DataUtils.getFormattedSnippet(data.getSnippet()));
if (data.hasAlert()) {
mAlert.setImageResource(R.drawable.clock);
mAlert.setVisibility(View.VISIBLE);
} else {
mAlert.setVisibility(View.GONE);
}
}
}
mTime.setText(DateUtils.getRelativeTimeSpanString(data.getModifiedDate()));
setBackground(data);
}
/*初始化基本信息*/
public NotesListItem(Context context) {
super(context); //super()它的主要作用是调整调用父类构造函数的顺序
inflate(context, R.layout.note_item, this);//Inflate可用于将一个xml中定义的布局控件找出来,这里的xml是r。layout
//findViewById用于从contentView中查找指定ID的View转换出来的形式根据需要而定;
mAlert = (ImageView) findViewById(R.id.iv_alert_icon);
mTitle = (TextView) findViewById(R.id.tv_title);
mTime = (TextView) findViewById(R.id.tv_time);
mCallName = (TextView) findViewById(R.id.tv_name);
mCheckBox = (CheckBox) findViewById(android.R.id.checkbox);
}
///根据data的属性对各个控件的属性的控制主要是可见性Visibility内容setText格式setTextAppearance
public void bind(Context context, NoteItemData data, boolean choiceMode, boolean checked) {
if (choiceMode && data.getType() == Notes.TYPE_NOTE) {
mCheckBox.setVisibility(View.VISIBLE); ///设置可见行为可见
mCheckBox.setChecked(checked); ///格子打钩
} else {
mCheckBox.setVisibility(View.GONE);
}
/**
* 便
* @param data 便
*/
private void setBackground(NoteItemData data) {
int id = data.getBgColorId();
if (data.getType() == Notes.TYPE_NOTE) {
if (data.isSingle() || data.isOneFollowingFolder()) {
setBackgroundResource(NoteItemBgResources.getNoteBgSingleRes(id));
} else if (data.isLast()) {
setBackgroundResource(NoteItemBgResources.getNoteBgLastRes(id));
} else if (data.isFirst() || data.isMultiFollowingFolder()) {
setBackgroundResource(NoteItemBgResources.getNoteBgFirstRes(id));
} else {
setBackgroundResource(NoteItemBgResources.getNoteBgNormalRes(id));
}
} else {
setBackgroundResource(NoteItemBgResources.getFolderBgRes());
}
}
mItemData = data;
///设置控件属性一共三种情况由data的id和父id是否与保存到文件夹的id一致来决定
if (data.getId() == Notes.ID_CALL_RECORD_FOLDER) {
mCallName.setVisibility(View.GONE);
mAlert.setVisibility(View.VISIBLE);
//设置该textview的style
mTitle.setTextAppearance(context, R.style.TextAppearancePrimaryItem);
//settext为设置内容
mTitle.setText(context.getString(R.string.call_record_folder_name)
+ context.getString(R.string.format_folder_files_count, data.getNotesCount()));
mAlert.setImageResource(R.drawable.call_record);
} else if (data.getParentId() == Notes.ID_CALL_RECORD_FOLDER) {
mCallName.setVisibility(View.VISIBLE);
mCallName.setText(data.getCallName());
mTitle.setTextAppearance(context,R.style.TextAppearanceSecondaryItem);
mTitle.setText(DataUtils.getFormattedSnippet(data.getSnippet()));
///关于闹钟的设置
if (data.hasAlert()) {
mAlert.setImageResource(R.drawable.clock);//图片来源的设置
mAlert.setVisibility(View.VISIBLE);
} else {
mAlert.setVisibility(View.GONE);
}
} else {
mCallName.setVisibility(View.GONE);
mTitle.setTextAppearance(context, R.style.TextAppearancePrimaryItem);
///设置title格式
if (data.getType() == Notes.TYPE_FOLDER) {
mTitle.setText(data.getSnippet()
+ context.getString(R.string.format_folder_files_count,
data.getNotesCount()));
mAlert.setVisibility(View.GONE);
} else {
mTitle.setText(DataUtils.getFormattedSnippet(data.getSnippet()));
if (data.hasAlert()) {
mAlert.setImageResource(R.drawable.clock);///设置图片来源
mAlert.setVisibility(View.VISIBLE);
} else {
mAlert.setVisibility(View.GONE);
}
}
}
///设置内容获取相关时间从data里编辑的日期中获取
mTime. setText(DateUtils.getRelativeTimeSpanString(data.getModifiedDate()));
/**
* 便
* @return 便
*/
public NoteItemData getItemData() {
return mItemData;
}
}
setBackground(data);
}
//根据data的文件属性来设置背景
private void setBackground(NoteItemData data) {
int id = data.getBgColorId();
//若是note型文件则4种情况对于4种不同情况的背景来源
if (data.getType() == Notes.TYPE_NOTE) {
//单个数据并且只有一个子文件夹
if (data.isSingle() || data.isOneFollowingFolder()) {
setBackgroundResource(NoteItemBgResources.getNoteBgSingleRes(id));
} else if (data.isLast()) {//是最后一个数据
setBackgroundResource(NoteItemBgResources.getNoteBgLastRes(id));
} else if (data.isFirst() || data.isMultiFollowingFolder()) {//是一个数据并有多个子文件夹
setBackgroundResource(NoteItemBgResources.getNoteBgFirstRes(id));
} else {
setBackgroundResource(NoteItemBgResources.getNoteBgNormalRes(id));
}
} else {
//若不是note直接调用文件夹的背景来源
setBackgroundResource(NoteItemBgResources.getFolderBgRes());
}
}
public NoteItemData getItemData() {
return mItemData;
}
}

@ -1,354 +1,530 @@
/*
* (c) 2010-2011, MiCode (www.micode.net)
* Copyright (c) 2010-2011, The MiCode Open Source Community (www.micode.net)
*
* 使 Apache License, Version 2.0
* 使
*
* 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.
*/
package net.micode.notes.ui;
import android.accounts.Account;
import android.accounts.AccountManager;
import android.app.ActionBar;
import android.app.AlertDialog;
import android.content.BroadcastReceiver;
import android.content.ContentValues;
import android.content.Context;
import android.content.DialogInterface;
import android.content.Intent;
import android.content.IntentFilter;
import android.content.SharedPreferences;
import android.os.Bundle;
import android.preference.Preference;
import android.preference.Preference.OnPreferenceClickListener;
import android.preference.PreferenceActivity;
import android.preference.PreferenceCategory;
import android.text.TextUtils;
import android.text.format.DateFormat;
import android.view.LayoutInflater;
import android.view.Menu;
import android.view.MenuItem;
import android.view.View;
import android.widget.Button;
import android.widget.TextView;
import android.widget.Toast;
import net.micode.notes.R;
import net.micode.notes.data.Notes;
import net.micode.notes.data.Notes.NoteColumns;
import net.micode.notes.gtask.remote.GTaskSyncService;
/**
* NotesPreferenceActivity 便
* PreferenceActivity
*/
public class NotesPreferenceActivity extends PreferenceActivity {
public static final String PREFERENCE_NAME = "notes_preferences";
public static final String PREFERENCE_SYNC_ACCOUNT_NAME = "pref_key_account_name";
public static final String PREFERENCE_LAST_SYNC_TIME = "pref_last_sync_time";
public static final String PREFERENCE_SET_BG_COLOR_KEY = "pref_key_bg_random_appear";
private static final String PREFERENCE_SYNC_ACCOUNT_KEY = "pref_sync_account_key";
private static final String AUTHORITIES_FILTER_KEY = "authorities";
private PreferenceCategory mAccountCategory;
private GTaskReceiver mReceiver;
private Account[] mOriAccounts;
private boolean mHasAddedAccount;
@Override
protected void onCreate(Bundle icicle) {
super.onCreate(icicle);
getActionBar().setDisplayHomeAsUpEnabled(true);
addPreferencesFromResource(R.xml.preferences);
mAccountCategory = (PreferenceCategory) findPreference(PREFERENCE_SYNC_ACCOUNT_KEY);
mReceiver = new GTaskReceiver();
IntentFilter filter = new IntentFilter();
filter.addAction(GTaskSyncService.GTASK_SERVICE_BROADCAST_NAME);
registerReceiver(mReceiver, filter);
mOriAccounts = null;
View header = LayoutInflater.from(this).inflate(R.layout.settings_header, null);
getListView().addHeaderView(header, null, true);
}
@Override
protected void onResume() {
super.onResume();
if (mHasAddedAccount) {
Account[] accounts = getGoogleAccounts();
if (mOriAccounts != null && accounts.length > mOriAccounts.length) {
for (Account accountNew : accounts) {
boolean found = false;
for (Account accountOld : mOriAccounts) {
if (TextUtils.equals(accountOld.name, accountNew.name)) {
found = true;
break;
}
}
if (!found) {
setSyncAccount(accountNew.name);
break;
}
}
}
}
refreshUI();
}
@Override
protected void onDestroy() {
if (mReceiver != null) {
unregisterReceiver(mReceiver);
}
super.onDestroy();
}
private void loadAccountPreference() {
mAccountCategory.removeAll();
Preference accountPref = new Preference(this);
final String defaultAccount = getSyncAccountName(this);
accountPref.setTitle(getString(R.string.preferences_account_title));
accountPref.setSummary(getString(R.string.preferences_account_summary));
accountPref.setOnPreferenceClickListener(new OnPreferenceClickListener() {
public boolean onPreferenceClick(Preference preference) {
if (!GTaskSyncService.isSyncing()) {
if (TextUtils.isEmpty(defaultAccount)) {
showSelectAccountAlertDialog();
} else {
showChangeAccountConfirmAlertDialog();
}
} else {
Toast.makeText(NotesPreferenceActivity.this,
R.string.preferences_toast_cannot_change_account, Toast.LENGTH_SHORT)
.show();
}
return true;
}
});
mAccountCategory.addPreference(accountPref);
}
private void loadSyncButton() {
Button syncButton = (Button) findViewById(R.id.preference_sync_button);
TextView lastSyncTimeView = (TextView) findViewById(R.id.prefenerece_sync_status_textview);
if (GTaskSyncService.isSyncing()) {
syncButton.setText(getString(R.string.preferences_button_sync_cancel));
syncButton.setOnClickListener(new View.OnClickListener() {
public void onClick(View v) {
GTaskSyncService.cancelSync(NotesPreferenceActivity.this);
}
});
} else {
syncButton.setText(getString(R.string.preferences_button_sync_immediately));
syncButton.setOnClickListener(new View.OnClickListener() {
public void onClick(View v) {
GTaskSyncService.startSync(NotesPreferenceActivity.this);
}
});
}
syncButton.setEnabled(!TextUtils.isEmpty(getSyncAccountName(this)));
if (GTaskSyncService.isSyncing()) {
lastSyncTimeView.setText(GTaskSyncService.getProgressString());
lastSyncTimeView.setVisibility(View.VISIBLE);
} else {
long lastSyncTime = getLastSyncTime(this);
if (lastSyncTime != 0) {
lastSyncTimeView.setText(getString(R.string.preferences_last_sync_time,
DateFormat.format(getString(R.string.preferences_last_sync_time_format),
lastSyncTime)));
lastSyncTimeView.setVisibility(View.VISIBLE);
} else {
lastSyncTimeView.setVisibility(View.GONE);
}
}
}
private void refreshUI() {
loadAccountPreference();
loadSyncButton();
}
private void showSelectAccountAlertDialog() {
AlertDialog.Builder dialogBuilder = new AlertDialog.Builder(this);
View titleView = LayoutInflater.from(this).inflate(R.layout.account_dialog_title, null);
TextView titleTextView = (TextView) titleView.findViewById(R.id.account_dialog_title);
titleTextView.setText(getString(R.string.preferences_dialog_select_account_title));
TextView subtitleTextView = (TextView) titleView.findViewById(R.id.account_dialog_subtitle);
subtitleTextView.setText(getString(R.string.preferences_dialog_select_account_tips));
dialogBuilder.setCustomTitle(titleView);
dialogBuilder.setPositiveButton(null, null);
Account[] accounts = getGoogleAccounts();
String defAccount = getSyncAccountName(this);
mOriAccounts = accounts;
mHasAddedAccount = false;
if (accounts.length > 0) {
CharSequence[] items = new CharSequence[accounts.length];
final CharSequence[] itemMapping = items;
int checkedItem = -1;
int index = 0;
for (Account account : accounts) {
if (TextUtils.equals(account.name, defAccount)) {
checkedItem = index;
}
items[index++] = account.name;
}
dialogBuilder.setSingleChoiceItems(items, checkedItem,
new DialogInterface.OnClickListener() {
public void onClick(DialogInterface dialog, int which) {
setSyncAccount(itemMapping[which].toString());
dialog.dismiss();
refreshUI();
}
});
}
View addAccountView = LayoutInflater.from(this).inflate(R.layout.add_account_text, null);
dialogBuilder.setView(addAccountView);
final AlertDialog dialog = dialogBuilder.show();
addAccountView.setOnClickListener(new View.OnClickListener() {
public void onClick(View v) {
mHasAddedAccount = true;
Intent intent = new Intent("android.settings.ADD_ACCOUNT_SETTINGS");
intent.putExtra(AUTHORITIES_FILTER_KEY, new String[] {
"gmail-ls"
});
startActivityForResult(intent, -1);
dialog.dismiss();
}
});
}
private void showChangeAccountConfirmAlertDialog() {
AlertDialog.Builder dialogBuilder = new AlertDialog.Builder(this);
View titleView = LayoutInflater.from(this).inflate(R.layout.account_dialog_title, null);
TextView titleTextView = (TextView) titleView.findViewById(R.id.account_dialog_title);
titleTextView.setText(getString(R.string.preferences_dialog_change_account_title,
getSyncAccountName(this)));
TextView subtitleTextView = (TextView) titleView.findViewById(R.id.account_dialog_subtitle);
subtitleTextView.setText(getString(R.string.preferences_dialog_change_account_warn_msg));
dialogBuilder.setCustomTitle(titleView);
CharSequence[] menuItemArray = new CharSequence[] {
getString(R.string.preferences_menu_change_account),
getString(R.string.preferences_menu_remove_account),
getString(R.string.preferences_menu_cancel)
};
dialogBuilder.setItems(menuItemArray, new DialogInterface.OnClickListener() {
public void onClick(DialogInterface dialog, int which) {
if (which == 0) {
showSelectAccountAlertDialog();
} else if (which == 1) {
removeSyncAccount();
refreshUI();
}
}
});
dialogBuilder.show();
}
private Account[] getGoogleAccounts() {
AccountManager accountManager = AccountManager.get(this);
return accountManager.getAccountsByType("com.google");
}
private void setSyncAccount(String account) {
if (!getSyncAccountName(this).equals(account)) {
SharedPreferences settings = getSharedPreferences(PREFERENCE_NAME, Context.MODE_PRIVATE);
SharedPreferences.Editor editor = settings.edit();
if (account != null) {
editor.putString(PREFERENCE_SYNC_ACCOUNT_NAME, account);
} else {
editor.putString(PREFERENCE_SYNC_ACCOUNT_NAME, "");
}
editor.commit();
setLastSyncTime(this, 0);
new Thread(new Runnable() {
public void run() {
ContentValues values = new ContentValues();
values.put(NoteColumns.GTASK_ID, "");
values.put(NoteColumns.SYNC_ID, 0);
getContentResolver().update(Notes.CONTENT_NOTE_URI, values, null, null);
}
}).start();
Toast.makeText(NotesPreferenceActivity.this,
getString(R.string.preferences_toast_success_set_account, account),
Toast.LENGTH_SHORT).show();
}
}
private void removeSyncAccount() {
SharedPreferences settings = getSharedPreferences(PREFERENCE_NAME, Context.MODE_PRIVATE);
SharedPreferences.Editor editor = settings.edit();
if (settings.contains(PREFERENCE_SYNC_ACCOUNT_NAME)) {
editor.remove(PREFERENCE_SYNC_ACCOUNT_NAME);
}
if (settings.contains(PREFERENCE_LAST_SYNC_TIME)) {
editor.remove(PREFERENCE_LAST_SYNC_TIME);
}
editor.commit();
new Thread(new Runnable() {
public void run() {
ContentValues values = new ContentValues();
values.put(NoteColumns.GTASK_ID, "");
values.put(NoteColumns.SYNC_ID, 0);
getContentResolver().update(Notes.CONTENT_NOTE_URI, values, null, null);
}
}).start();
}
public static String getSyncAccountName(Context context) {
SharedPreferences settings = context.getSharedPreferences(PREFERENCE_NAME, Context.MODE_PRIVATE);
return settings.getString(PREFERENCE_SYNC_ACCOUNT_NAME, "");
}
public static void setLastSyncTime(Context context, long time) {
SharedPreferences settings = context.getSharedPreferences(PREFERENCE_NAME, Context.MODE_PRIVATE);
SharedPreferences.Editor editor = settings.edit();
editor.putLong(PREFERENCE_LAST_SYNC_TIME, time);
editor.commit();
}
public static long getLastSyncTime(Context context) {
SharedPreferences settings = context.getSharedPreferences(PREFERENCE_NAME, Context.MODE_PRIVATE);
return settings.getLong(PREFERENCE_LAST_SYNC_TIME, 0);
}
private class GTaskReceiver extends BroadcastReceiver {
@Override
public void onReceive(Context context, Intent intent) {
refreshUI();
if (intent.getBooleanExtra(GTaskSyncService.GTASK_SERVICE_BROADCAST_IS_SYNCING, false)) {
TextView syncStatus = (TextView) findViewById(R.id.prefenerece_sync_status_textview);
syncStatus.setText(intent.getStringExtra(GTaskSyncService.GTASK_SERVICE_BROADCAST_PROGRESS_MSG));
}
}
}
@Override
public boolean onOptionsItemSelected(MenuItem item) {
switch (item.getItemId()) {
case android.R.id.home:
Intent intent = new Intent(this, NotesListActivity.class);
intent.addFlags(Intent.FLAG_ACTIVITY_CLEAR_TOP);
startActivity(intent);
return true;
default:
return false;
}
}
}
package net.micode.notes.ui;
import android.accounts.Account;
import android.accounts.AccountManager;
import android.app.ActionBar;
import android.app.AlertDialog;
import android.content.BroadcastReceiver;
import android.content.ContentValues;
import android.content.Context;
import android.content.DialogInterface;
import android.content.Intent;
import android.content.IntentFilter;
import android.content.SharedPreferences;
import android.os.Bundle;
import android.preference.Preference;
import android.preference.Preference.OnPreferenceClickListener;
import android.preference.PreferenceActivity;
import android.preference.PreferenceCategory;
import android.text.TextUtils;
import android.text.format.DateFormat;
import android.view.LayoutInflater;
import android.view.Menu;
import android.view.MenuItem;
import android.view.View;
import android.widget.Button;
import android.widget.TextView;
import android.widget.Toast;
import net.micode.notes.R;
import net.micode.notes.data.Notes;
import net.micode.notes.data.Notes.NoteColumns;
import net.micode.notes.gtask.remote.GTaskSyncService;
/*
*NotesPreferenceActivity便
* PreferenceActivityActivity
*/
public class NotesPreferenceActivity extends PreferenceActivity {
public static final String PREFERENCE_NAME = "notes_preferences";
//优先名
public static final String PREFERENCE_SYNC_ACCOUNT_NAME = "pref_key_account_name";
//同步账号
public static final String PREFERENCE_LAST_SYNC_TIME = "pref_last_sync_time";
//同步时间
public static final String PREFERENCE_SET_BG_COLOR_KEY = "pref_key_bg_random_appear";
private static final String PREFERENCE_SYNC_ACCOUNT_KEY = "pref_sync_account_key";
//同步密码
private static final String AUTHORITIES_FILTER_KEY = "authorities";
//本地密码
private PreferenceCategory mAccountCategory;
//账户分组
private GTaskReceiver mReceiver;
//同步任务接收器
private Account[] mOriAccounts;
//账户
private boolean mHasAddedAccount;
//账户的hash标记
@Override
/*
*activity
*Bundle icicle activity
*
*/
protected void onCreate(Bundle icicle) {
//先执行父类的创建函数
super.onCreate(icicle);
/* using the app icon for navigation */
getActionBar().setDisplayHomeAsUpEnabled(true);
//给左上角图标的左边加上一个返回的图标
addPreferencesFromResource(R.xml.preferences);
//添加xml来源并显示 xml
mAccountCategory = (PreferenceCategory) findPreference(PREFERENCE_SYNC_ACCOUNT_KEY);
//根据同步账户关键码来初始化分组
mReceiver = new GTaskReceiver();
IntentFilter filter = new IntentFilter();
filter.addAction(GTaskSyncService.GTASK_SERVICE_BROADCAST_NAME);
registerReceiver(mReceiver, filter);
//初始化同步组件
mOriAccounts = null;
View header = LayoutInflater.from(this).inflate(R.layout.settings_header, null);
//获取listvivewListView的作用:用于列出所有选择
getListView().addHeaderView(header, null, true);
//在listview组件上方添加其他组件
}
@Override
/*
* activity
*
*/
protected void onResume() {
//先执行父类 的交互实现
super.onResume();
// need to set sync account automatically if user has added a new
// account
if (mHasAddedAccount) {
//若用户新加了账户则自动设置同步账户
Account[] accounts = getGoogleAccounts();
//获取google同步账户
if (mOriAccounts != null && accounts.length > mOriAccounts.length) {
//若原账户不为空且当前账户有增加
for (Account accountNew : accounts) {
boolean found = false;
for (Account accountOld : mOriAccounts) {
if (TextUtils.equals(accountOld.name, accountNew.name)) {
//更新账户
found = true;
break;
}
}
if (!found) {
setSyncAccount(accountNew.name);
//若是没有找到旧的账户,那么同步账号中就只添加新账户
break;
}
}
}
}
refreshUI();
//刷新标签界面
}
@Override
/*
* activity
*
*/
protected void onDestroy() {
if (mReceiver != null) {
unregisterReceiver(mReceiver);
//注销接收器
}
super.onDestroy();
//执行父类的销毁动作
}
/*
*
*
*/
private void loadAccountPreference() {
mAccountCategory.removeAll();
//销毁所有的分组
Preference accountPref = new Preference(this);
//建立首选项
final String defaultAccount = getSyncAccountName(this);
accountPref.setTitle(getString(R.string.preferences_account_title));
accountPref.setSummary(getString(R.string.preferences_account_summary));
//设置首选项的大标题和小标题
accountPref.setOnPreferenceClickListener(new OnPreferenceClickListener() {
public boolean onPreferenceClick(Preference preference) {
//建立监听器
if (!GTaskSyncService.isSyncing()) {
if (TextUtils.isEmpty(defaultAccount)) {
// the first time to set account
//若是第一次建立账户显示选择账户提示对话框
showSelectAccountAlertDialog();
} else {
// if the account has already been set, we need to promp
// user about the risk
//若是已经建立则显示修改对话框并进行修改操作
showChangeAccountConfirmAlertDialog();
}
} else {
//若在没有同步的情况下则在toast中显示不能修改
Toast.makeText(NotesPreferenceActivity.this,
R.string.preferences_toast_cannot_change_account, Toast.LENGTH_SHORT)
.show();
}
return true;
}
});
//根据新建首选项编辑新的账户分组
mAccountCategory.addPreference(accountPref);
}
/*
*
*
*/
private void loadSyncButton() {
Button syncButton = (Button) findViewById(R.id.preference_sync_button);
TextView lastSyncTimeView = (TextView) findViewById(R.id.prefenerece_sync_status_textview);
//获取同步按钮控件和最终同步时间的的窗口
// set button state
//设置按钮的状态
if (GTaskSyncService.isSyncing()) {
//若是在同步状态下
syncButton.setText(getString(R.string.preferences_button_sync_cancel));
syncButton.setOnClickListener(new View.OnClickListener() {
public void onClick(View v) {
GTaskSyncService.cancelSync(NotesPreferenceActivity.this);
}
});
//设置按钮显示的文本为“取消同步”以及监听器
} else {
syncButton.setText(getString(R.string.preferences_button_sync_immediately));
syncButton.setOnClickListener(new View.OnClickListener() {
public void onClick(View v) {
GTaskSyncService.startSync(NotesPreferenceActivity.this);
}
});
//若是不同步则设置按钮显示的文本为“立即同步”以及对应监听器
}
syncButton.setEnabled(!TextUtils.isEmpty(getSyncAccountName(this)));
//设置按键可用还是不可用
// set last sync time
// 设置最终同步时间
if (GTaskSyncService.isSyncing()) {
//若是在同步的情况下
lastSyncTimeView.setText(GTaskSyncService.getProgressString());
lastSyncTimeView.setVisibility(View.VISIBLE);
// 根据当前同步服务器设置时间显示框的文本以及可见性
} else {
//若是非同步情况
long lastSyncTime = getLastSyncTime(this);
if (lastSyncTime != 0) {
lastSyncTimeView.setText(getString(R.string.preferences_last_sync_time,
DateFormat.format(getString(R.string.preferences_last_sync_time_format),
lastSyncTime)));
lastSyncTimeView.setVisibility(View.VISIBLE);
//则根据最后同步时间的信息来编辑时间显示框的文本内容和可见性
} else {
//若时间为空直接设置为不可见状态
lastSyncTimeView.setVisibility(View.GONE);
}
}
}
/*
*
*
*/
private void refreshUI() {
loadAccountPreference();
loadSyncButton();
}
/*
*
*
*/
private void showSelectAccountAlertDialog() {
AlertDialog.Builder dialogBuilder = new AlertDialog.Builder(this);
//创建一个新的对话框
View titleView = LayoutInflater.from(this).inflate(R.layout.account_dialog_title, null);
TextView titleTextView = (TextView) titleView.findViewById(R.id.account_dialog_title);
titleTextView.setText(getString(R.string.preferences_dialog_select_account_title));
TextView subtitleTextView = (TextView) titleView.findViewById(R.id.account_dialog_subtitle);
subtitleTextView.setText(getString(R.string.preferences_dialog_select_account_tips));
//设置标题以及子标题的内容
dialogBuilder.setCustomTitle(titleView);
dialogBuilder.setPositiveButton(null, null);
//设置对话框的自定义标题建立一个YES的按钮
Account[] accounts = getGoogleAccounts();
String defAccount = getSyncAccountName(this);
//获取同步账户信息
mOriAccounts = accounts;
mHasAddedAccount = false;
if (accounts.length > 0) {
//若账户不为空
CharSequence[] items = new CharSequence[accounts.length];
final CharSequence[] itemMapping = items;
int checkedItem = -1;
int index = 0;
for (Account account : accounts) {
if (TextUtils.equals(account.name, defAccount)) {
checkedItem = index;
//在账户列表中查询到所需账户
}
items[index++] = account.name;
}
dialogBuilder.setSingleChoiceItems(items, checkedItem,
//在对话框建立一个单选的复选框
new DialogInterface.OnClickListener() {
public void onClick(DialogInterface dialog, int which) {
setSyncAccount(itemMapping[which].toString());
dialog.dismiss();
//取消对话框
refreshUI();
}
//设置点击后执行的事件,包括检录新同步账户和刷新标签界面
});
//建立对话框网络版的监听器
}
View addAccountView = LayoutInflater.from(this).inflate(R.layout.add_account_text, null);
dialogBuilder.setView(addAccountView);
//给新加账户对话框设置自定义样式
final AlertDialog dialog = dialogBuilder.show();
//显示对话框
addAccountView.setOnClickListener(new View.OnClickListener() {
public void onClick(View v) {
mHasAddedAccount = true;
//将新加账户的hash置true
Intent intent = new Intent("android.settings.ADD_ACCOUNT_SETTINGS");
//建立网络建立组件
intent.putExtra(AUTHORITIES_FILTER_KEY, new String[] {
"gmail-ls"
});
startActivityForResult(intent, -1);
//跳回上一个选项
dialog.dismiss();
}
});
//建立新加账户对话框的监听器
}
/*
*
*
*/
private void showChangeAccountConfirmAlertDialog() {
AlertDialog.Builder dialogBuilder = new AlertDialog.Builder(this);
//创建一个新的对话框
View titleView = LayoutInflater.from(this).inflate(R.layout.account_dialog_title, null);
TextView titleTextView = (TextView) titleView.findViewById(R.id.account_dialog_title);
titleTextView.setText(getString(R.string.preferences_dialog_change_account_title,
getSyncAccountName(this)));
TextView subtitleTextView = (TextView) titleView.findViewById(R.id.account_dialog_subtitle);
subtitleTextView.setText(getString(R.string.preferences_dialog_change_account_warn_msg));
//根据同步修改的账户信息设置标题以及子标题的内容
dialogBuilder.setCustomTitle(titleView);
//设置对话框的自定义标题
CharSequence[] menuItemArray = new CharSequence[] {
getString(R.string.preferences_menu_change_account),
getString(R.string.preferences_menu_remove_account),
getString(R.string.preferences_menu_cancel)
};
//定义一些标记字符串
dialogBuilder.setItems(menuItemArray, new DialogInterface.OnClickListener() {
//设置对话框要显示的一个list用于显示几个命令时,即changeremovecancel
public void onClick(DialogInterface dialog, int which) {
//按键功能由which来决定
if (which == 0) {
//进入账户选择对话框
showSelectAccountAlertDialog();
} else if (which == 1) {
//删除账户并且跟新便签界面
removeSyncAccount();
refreshUI();
}
}
});
dialogBuilder.show();
//显示对话框
}
/*
*
*
*/
private Account[] getGoogleAccounts() {
AccountManager accountManager = AccountManager.get(this);
return accountManager.getAccountsByType("com.google");
}
/*
*
*
*/
private void setSyncAccount(String account) {
if (!getSyncAccountName(this).equals(account)) {
//假如该账号不在同步账号列表中
SharedPreferences settings = getSharedPreferences(PREFERENCE_NAME, Context.MODE_PRIVATE);
SharedPreferences.Editor editor = settings.edit();
//编辑共享的首选项
if (account != null) {
editor.putString(PREFERENCE_SYNC_ACCOUNT_NAME, account);
} else {
editor.putString(PREFERENCE_SYNC_ACCOUNT_NAME, "");
}
//将该账号加入到首选项中
editor.commit();
//提交修改的数据
setLastSyncTime(this, 0);
//将最后同步时间清零
// clean up local gtask related info
new Thread(new Runnable() {
public void run() {
ContentValues values = new ContentValues();
values.put(NoteColumns.GTASK_ID, "");
values.put(NoteColumns.SYNC_ID, 0);
getContentResolver().update(Notes.CONTENT_NOTE_URI, values, null, null);
}
}).start();
//重置当地同步任务的信息
Toast.makeText(NotesPreferenceActivity.this,
getString(R.string.preferences_toast_success_set_accout, account),
Toast.LENGTH_SHORT).show();
//将toast的文本信息置为“设置账户成功”并显示出来
}
}
/*
*
*
*/
private void removeSyncAccount() {
SharedPreferences settings = getSharedPreferences(PREFERENCE_NAME, Context.MODE_PRIVATE);
SharedPreferences.Editor editor = settings.edit();
//设置共享首选项
if (settings.contains(PREFERENCE_SYNC_ACCOUNT_NAME)) {
editor.remove(PREFERENCE_SYNC_ACCOUNT_NAME);
//假如当前首选项中有账户就删除
}
if (settings.contains(PREFERENCE_LAST_SYNC_TIME)) {
editor.remove(PREFERENCE_LAST_SYNC_TIME);
//删除当前首选项中有账户时间
}
editor.commit();
//提交更新后的数据
// clean up local gtask related info
new Thread(new Runnable() {
public void run() {
ContentValues values = new ContentValues();
values.put(NoteColumns.GTASK_ID, "");
values.put(NoteColumns.SYNC_ID, 0);
getContentResolver().update(Notes.CONTENT_NOTE_URI, values, null, null);
}
}).start();
//重置当地同步任务的信息
}
/*
*
*
*/
public static String getSyncAccountName(Context context) {
SharedPreferences settings = context.getSharedPreferences(PREFERENCE_NAME,
Context.MODE_PRIVATE);
return settings.getString(PREFERENCE_SYNC_ACCOUNT_NAME, "");
}
/*
*
*
*/
public static void setLastSyncTime(Context context, long time) {
SharedPreferences settings = context.getSharedPreferences(PREFERENCE_NAME,
Context.MODE_PRIVATE);
SharedPreferences.Editor editor = settings.edit();
// 从共享首选项中找到相关账户并获取其编辑器
editor.putLong(PREFERENCE_LAST_SYNC_TIME, time);
editor.commit();
//编辑最终同步时间并提交更新
}
/*
*
*
*/
public static long getLastSyncTime(Context context) {
SharedPreferences settings = context.getSharedPreferences(PREFERENCE_NAME,
Context.MODE_PRIVATE);
return settings.getLong(PREFERENCE_LAST_SYNC_TIME, 0);
}
/*
*
* BroadcastReceiver
*/
private class GTaskReceiver extends BroadcastReceiver {
@Override
public void onReceive(Context context, Intent intent) {
refreshUI();
if (intent.getBooleanExtra(GTaskSyncService.GTASK_SERVICE_BROADCAST_IS_SYNCING, false)) {
//获取随广播而来的Intent中的同步服务的数据
TextView syncStatus = (TextView) findViewById(R.id.prefenerece_sync_status_textview);
syncStatus.setText(intent
.getStringExtra(GTaskSyncService.GTASK_SERVICE_BROADCAST_PROGRESS_MSG));
//通过获取的数据在设置系统的状态
}
}
}
/*
*
*
* :MenuItem
*/
public boolean onOptionsItemSelected(MenuItem item) {
switch (item.getItemId()) {
//根据选项的id选择这里只有一个主页
case android.R.id.home:
Intent intent = new Intent(this, NotesListActivity.class);
intent.addFlags(Intent.FLAG_ACTIVITY_CLEAR_TOP);
startActivity(intent);
return true;
//在主页情况下在创建连接组件intent发出清空的信号并开始一个相应的activity
default:
return false;
}
}
}

@ -15,7 +15,6 @@
*/
package net.micode.notes.widget;
import android.app.PendingIntent;
import android.appwidget.AppWidgetManager;
import android.appwidget.AppWidgetProvider;
@ -34,28 +33,32 @@ import net.micode.notes.ui.NoteEditActivity;
import net.micode.notes.ui.NotesListActivity;
/**
* NoteWidgetProvider AppWidgetProvider便
*
* @ProjectName: minode
* @Package: net.micode.notes.widget
* @ClassName: NoteWidgetProvider
* @Description: AppWidgetProvider
* @Date: 2024-12-18 21:13
*/
public abstract class NoteWidgetProvider extends AppWidgetProvider {
// 定义查询便签数据库时需要的列
public static final String[] PROJECTION = new String[]{
NoteColumns.ID, // 便签ID
NoteColumns.BG_COLOR_ID, // 背景颜色ID
NoteColumns.SNIPPET // 便签摘要
public static final String [] PROJECTION = new String [] {
NoteColumns.ID,
NoteColumns.BG_COLOR_ID,
NoteColumns.SNIPPET
};
// 定义列的索引
public static final int COLUMN_ID = 0;
public static final int COLUMN_BG_COLOR_ID = 1;
public static final int COLUMN_SNIPPET = 2;
public static final int COLUMN_ID = 0;
public static final int COLUMN_BG_COLOR_ID = 1;
public static final int COLUMN_SNIPPET = 2;
private static final String TAG = "NoteWidgetProvider"; // 日志标签
private static final String TAG = "NoteWidgetProvider";
/**
*
* ID便ID
* @param context
* @param appWidgetIds ID
* @method onDeleted
* @description Widget
* @date: 2024-12-18 21:18
* @return void
*/
@Override
public void onDeleted(Context context, int[] appWidgetIds) {
@ -65,61 +68,48 @@ public abstract class NoteWidgetProvider extends AppWidgetProvider {
context.getContentResolver().update(Notes.CONTENT_NOTE_URI,
values,
NoteColumns.WIDGET_ID + "=?",
new String[]{String.valueOf(appWidgetIds[i])});
new String[] { String.valueOf(appWidgetIds[i])});
}
}
/**
* ID便
* @param context
* @param widgetId ID
* @return
*/
private Cursor getNoteWidgetInfo(Context context, int widgetId) {
return context.getContentResolver().query(Notes.CONTENT_NOTE_URI,
PROJECTION,
NoteColumns.WIDGET_ID + "=? AND " + NoteColumns.PARENT_ID + "<>?",
new String[]{String.valueOf(widgetId), String.valueOf(Notes.ID_TRASH_FOLDER)},
new String[] { String.valueOf(widgetId), String.valueOf(Notes.ID_TRASH_FOLER) },
null);
}
/**
*
* @param context
* @param appWidgetManager
* @param appWidgetIds ID
* @method update
* @description Widget
* @date: 2024-12-18 21:11
* @return void
*/
protected void update(Context context, AppWidgetManager appWidgetManager, int[] appWidgetIds) {
update(context, appWidgetManager, appWidgetIds, false);
}
/**
*
* @param context
* @param appWidgetManager
* @param appWidgetIds ID
* @param privacyMode
*/
private void update(Context context, AppWidgetManager appWidgetManager, int[] appWidgetIds,
boolean privacyMode) {
for (int i = 0; i < appWidgetIds.length; i++) {
if (appWidgetIds[i] != AppWidgetManager.INVALID_APPWIDGET_ID) {
int bgId = ResourceParser.getDefaultBgId(context); // 获取默认背景ID
String snippet = ""; // 便签摘要
Intent intent = new Intent(context, NoteEditActivity.class); // 创建意图
int bgId = ResourceParser.getDefaultBgId(context);
String snippet = "";
Intent intent = new Intent(context, NoteEditActivity.class);
intent.setFlags(Intent.FLAG_ACTIVITY_SINGLE_TOP);
intent.putExtra(Notes.INTENT_EXTRA_WIDGET_ID, appWidgetIds[i]);
intent.putExtra(Notes.INTENT_EXTRA_WIDGET_TYPE, getWidgetType());
Cursor c = getNoteWidgetInfo(context, appWidgetIds[i]); // 获取便签信息
Cursor c = getNoteWidgetInfo(context, appWidgetIds[i]);
if (c != null && c.moveToFirst()) {
if (c.getCount() > 1) {
Log.e(TAG, "Multiple messages with the same widget id: " + appWidgetIds[i]);
Log.e(TAG, "Multiple message with same widget id:" + appWidgetIds[i]);
c.close();
return;
}
snippet = c.getString(COLUMN_SNIPPET); // 获取便签摘要
bgId = c.getInt(COLUMN_BG_COLOR_ID); // 获取背景ID
snippet = c.getString(COLUMN_SNIPPET);
bgId = c.getInt(COLUMN_BG_COLOR_ID);
intent.putExtra(Intent.EXTRA_UID, c.getLong(COLUMN_ID));
intent.setAction(Intent.ACTION_VIEW);
} else {
@ -135,7 +125,7 @@ public abstract class NoteWidgetProvider extends AppWidgetProvider {
rv.setImageViewResource(R.id.widget_bg_image, getBgResourceId(bgId));
intent.putExtra(Notes.INTENT_EXTRA_BACKGROUND_ID, bgId);
/**
* 宿PendingIntent
* Generate the pending intent to start host for the widget
*/
PendingIntent pendingIntent = null;
if (privacyMode) {
@ -155,66 +145,9 @@ public abstract class NoteWidgetProvider extends AppWidgetProvider {
}
}
/**
* ID
* @param bgId ID
* @return ID
*/
protected abstract int getBgResourceId(int bgId);
/**
* ID
* @return ID
*/
protected abstract int getLayoutId();
/**
*
* @return
*/
protected abstract int getWidgetType();
}
/**
*
NoteWidgetProvider AppWidgetProvider便
onDeleted
context
appWidgetIdsID
ID便ID
getNoteWidgetInfo
context
widgetIdID
ID便
ID便便IDID便
update
context
appWidgetManager AppWidgetManager
appWidgetIdsID
privacyMode
UIID便便宿 PendingIntent
getBgResourceId
bgIdID
ID
IDID
getLayoutId
ID
ID
getWidgetType
*/

@ -23,76 +23,25 @@ import net.micode.notes.R;
import net.micode.notes.data.Notes;
import net.micode.notes.tool.ResourceParser;
/**
* NoteWidgetProvider_2x NoteWidgetProvider 2x 便
*
*/
public class NoteWidgetProvider_2x extends NoteWidgetProvider {
/**
*
* update
* @param context
* @param appWidgetManager
* @param appWidgetIds ID
*/
@Override
public void onUpdate(Context context, AppWidgetManager appWidgetManager, int[] appWidgetIds) {
super.update(context, appWidgetManager, appWidgetIds);
}
/**
* ID
* @return ID
*/
@Override
protected int getLayoutId() {
return R.layout.widget_2x; // 返回2x小部件的布局资源ID
return R.layout.widget_2x;
}
/**
* IDID
* @param bgId ID
* @return ID
*/
@Override
protected int getBgResourceId(int bgId) {
return ResourceParser.WidgetBgResources.getWidget2xBgResource(bgId); // 获取2x小部件的背景资源ID
return ResourceParser.WidgetBgResources.getWidget2xBgResource(bgId);
}
/**
*
* @return
*/
@Override
protected int getWidgetType() {
return Notes.TYPE_WIDGET_2X; // 返回2x小部件的类型
return Notes.TYPE_WIDGET_2X;
}
}
/**
*
NoteWidgetProvider_2x NoteWidgetProvider 2x 便 2x
onUpdate
context
appWidgetManager AppWidgetManager
appWidgetIdsID
update UI
getLayoutId
2xID R.layout.widget_2x
ID
getBgResourceId
bgIdID
2xID
ID ResourceParser.WidgetBgResources ID
getWidgetType
2x Notes.TYPE_WIDGET_2X
*/

@ -23,76 +23,24 @@ import net.micode.notes.R;
import net.micode.notes.data.Notes;
import net.micode.notes.tool.ResourceParser;
/**
* NoteWidgetProvider_4x NoteWidgetProvider 4x 便
*
*/
public class NoteWidgetProvider_4x extends NoteWidgetProvider {
/**
*
* update
* @param context
* @param appWidgetManager
* @param appWidgetIds ID
*/
@Override
public void onUpdate(Context context, AppWidgetManager appWidgetManager, int[] appWidgetIds) {
super.update(context, appWidgetManager, appWidgetIds);
}
/**
* ID
* @return ID
*/
@Override
protected int getLayoutId() {
return R.layout.widget_4x; // 返回4x小部件的布局资源ID
return R.layout.widget_4x;
}
/**
* IDID
* @param bgId ID
* @return ID
*/
@Override
protected int getBgResourceId(int bgId) {
return ResourceParser.WidgetBgResources.getWidget4xBgResource(bgId); // 获取4x小部件的背景资源ID
return ResourceParser.WidgetBgResources.getWidget4xBgResource(bgId);
}
/**
*
* @return
*/
@Override
protected int getWidgetType() {
return Notes.TYPE_WIDGET_4X; // 返回4x小部件的类型
return Notes.TYPE_WIDGET_4X;
}
}
/**
*
NoteWidgetProvider_4x NoteWidgetProvider 4x 便 4x
onUpdate
context
appWidgetManager AppWidgetManager
appWidgetIdsID
update UI
getLayoutId
4xID R.layout.widget_4x
ID
getBgResourceId
bgIdID
4xID
ID ResourceParser.WidgetBgResources ID
getWidgetType
4x Notes.TYPE_WIDGET_4X
*/
Loading…
Cancel
Save