Compare commits

..

3 Commits
main ... master

@ -1,3 +1,2 @@
# xiaominote_code_analysis # 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 * 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; package net.micode.notes.data;
@ -23,81 +25,71 @@ import android.util.Log;
import java.util.HashMap; import java.util.HashMap;
/** /**
* *
* 使 * @ProjectName: minode
* @Package: net.micode.notes.data
* @ClassName: Contact
* @Description:
* @Author:
* @Date: 2023-12-16 17:57
*/ */
public class Contact { public class Contact {
/**
*
*/
private static HashMap<String, String> sContactCache; private static HashMap<String, String> sContactCache;
/**
*
*/
private static final String TAG = "Contact"; private static final String TAG = "Contact";
/**
* SQL
*
*/
private static final String CALLER_ID_SELECTION = "PHONE_NUMBERS_EQUAL(" + Phone.NUMBER private static final String CALLER_ID_SELECTION = "PHONE_NUMBERS_EQUAL(" + Phone.NUMBER
+ ",?) AND " + Data.MIMETYPE + "='" + Phone.CONTENT_ITEM_TYPE + "'" + ",?) AND " + Data.MIMETYPE + "='" + Phone.CONTENT_ITEM_TYPE + "'"
+ " AND " + Data.RAW_CONTACT_ID + " IN " + " AND " + Data.RAW_CONTACT_ID + " IN "
+ "(SELECT raw_contact_id " + "(SELECT raw_contact_id "
+ " FROM phone_lookup" + " FROM phone_lookup"
+ " WHERE min_match = '+')"; + " WHERE min_match = '+')";
/**
/** * @method getContact
* * @description
* * @date: 2023-12-16 19:24
* @param context * @author:
* @param phoneNumber * @return string
* @return null */
*/
public static String getContact(Context context, String phoneNumber) { public static String getContact(Context context, String phoneNumber) {
// 初始化缓存 if(sContactCache == null) {
if (sContactCache == null) {
sContactCache = new HashMap<String, String>(); sContactCache = new HashMap<String, String>();
} }
// 首先检查缓存中是否存在该电话号码对应的联系人姓名 // 查找HashMap中是否有phoneNumber的信息
if (sContactCache.containsKey(phoneNumber)) { // 2023-12-16 19:43
if(sContactCache.containsKey(phoneNumber)) {
return sContactCache.get(phoneNumber); return sContactCache.get(phoneNumber);
} }
// 将 SQL 语句中的占位符替换为实际的电话号码匹配模式
String selection = CALLER_ID_SELECTION.replace("+", String selection = CALLER_ID_SELECTION.replace("+",
PhoneNumberUtils.toCallerIDMinMatch(phoneNumber)); PhoneNumberUtils.toCallerIDMinMatch(phoneNumber));
// 执行数据库查询操作 // 查找数据库中phoneNumber的信息
// 2023-12-16 19:43
Cursor cursor = context.getContentResolver().query( Cursor cursor = context.getContentResolver().query(
Data.CONTENT_URI, Data.CONTENT_URI,
new String[]{Phone.DISPLAY_NAME}, new String [] { Phone.DISPLAY_NAME },
selection, selection,
new String[]{phoneNumber}, new String[] { phoneNumber },
null); null);
// 处理查询结果 // 检查是否查询到联系人找到则将相关信息加入HashMap中未找到则处理异常
// 2023-12-16 19:41
if (cursor != null && cursor.moveToFirst()) { if (cursor != null && cursor.moveToFirst()) {
try { try {
// 获取联系人姓名并缓存
String name = cursor.getString(0); String name = cursor.getString(0);
sContactCache.put(phoneNumber, name); sContactCache.put(phoneNumber, name);
return name; return name;
} catch (IndexOutOfBoundsException e) { } catch (IndexOutOfBoundsException e) {
// 记录异常信息 Log.e(TAG, " Cursor get string error " + e.toString());
Log.e(TAG, "游标获取字符串时出错:" + e.toString());
return null; return null;
} finally { } finally {
// 确保游标关闭,释放资源
cursor.close(); cursor.close();
} }
} else { } else {
// 记录未找到匹配联系人的信息 Log.d(TAG, "No contact matched with number:" + phoneNumber);
Log.d(TAG, "未找到与号码匹配的联系人:" + phoneNumber);
return null; 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 * 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; package net.micode.notes.data;
import android.net.Uri; import android.net.Uri;
/**
* Notes 便使 URI
* 便便
*/
public class Notes { public class Notes {
/**
* ContentProvider
*/
public static final String AUTHORITY = "micode_notes"; public static final String AUTHORITY = "micode_notes";
/**
*
*/
public static final String TAG = "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 ID_ROOT_FOLDER = 0;
public static final int TYPE_FOLDER = 1; // 文件夹 public static final int ID_TEMPARAY_FOLDER = -1;
public static final int TYPE_SYSTEM = 2; // 系统便签 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_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_BACKGROUND_ID = "net.micode.notes.background_color_id";
public static final String INTENT_EXTRA_WIDGET_ID = "net.micode.notes.widget_id"; public static final String INTENT_EXTRA_WIDGET_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_FOLDER_ID = "net.micode.notes.folder_id";
public static final String INTENT_EXTRA_CALL_DATE = "net.micode.notes.call_date"; public static final String INTENT_EXTRA_CALL_DATE = "net.micode.notes.call_date";
/** public static final int TYPE_WIDGET_INVALIDE = -1;
* public static final int TYPE_WIDGET_2X = 0;
*/ public static final int TYPE_WIDGET_4X = 1;
public static final int TYPE_WIDGET_INVALID = -1; // 无效小部件类型
public static final int TYPE_WIDGET_2X = 0; // 2x 小部件
public static final int TYPE_WIDGET_4X = 1; // 4x 小部件
/**
* 便 MIME
*/
public static class DataConstants { public static class DataConstants {
public static final String NOTE = TextNote.CONTENT_ITEM_TYPE; // 文本便签的 MIME 类型 public static final String NOTE = TextNote.CONTENT_ITEM_TYPE;
public static final String CALL_NOTE = CallNote.CONTENT_ITEM_TYPE; // 通话记录便签的 MIME 类型 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"); 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 static final Uri CONTENT_DATA_URI = Uri.parse("content://" + AUTHORITY + "/data");
/**
* 便便
*/
public interface NoteColumns { public interface NoteColumns {
public static final String ID = "_id"; // 便签 ID /**
public static final String PARENT_ID = "parent_id"; // 父文件夹 ID * The unique ID for a row
* <P> Type: INTEGER (long) </P>
public static final String CREATED_DATE = "created_date"; // 创建日期 */
public static final String MODIFIED_DATE = "modified_date"; // 修改日期 public static final String ID = "_id";
public static final String ALERTED_DATE = "alert_date"; // 提醒日期
public static final String SNIPPET = "snippet"; // 便签摘要 /**
public static final String WIDGET_ID = "widget_id"; // 小部件 ID * The parent's id for note or folder
public static final String WIDGET_TYPE = "widget_type"; // 小部件类型 * <P> Type: INTEGER (long) </P>
public static final String BG_COLOR_ID = "bg_color_id"; // 背景颜色 ID */
public static final String HAS_ATTACHMENT = "has_attachment"; // 是否有附件 public static final String PARENT_ID = "parent_id";
public static final String NOTES_COUNT = "notes_count"; // 便签数量
public static final String TYPE = "type"; // 便签类型 /**
public static final String SYNC_ID = "sync_id"; // 同步 ID * Created data for note or folder
public static final String LOCAL_MODIFIED = "local_modified"; // 本地修改标记 * <P> Type: INTEGER (long) </P>
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 CREATED_DATE = "created_date";
public static final String VERSION = "version"; // 版本
public static final String TOP = "top"; // 置顶标记 /**
* 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 interface DataColumns {
public static final String ID = "_id"; // 数据 ID /**
public static final String MIME_TYPE = "mime_type"; // MIME 类型 * The unique ID for a row
public static final String NOTE_ID = "note_id"; // 便签 ID * <P> Type: INTEGER (long) </P>
public static final String CREATED_DATE = "created_date"; // 创建日期 */
public static final String MODIFIED_DATE = "modified_date"; // 修改日期 public static final String ID = "_id";
public static final String CONTENT = "content"; // 内容
public static final String DATA1 = "data1"; // 数据 1 /**
public static final String DATA2 = "data2"; // 数据 2 * The MIME type of the item represented by this row.
public static final String DATA3 = "data3"; // 数据 3 * <P> Type: Text </P>
public static final String DATA4 = "data4"; // 数据 4 */
public static final String DATA5 = "data5"; // 数据 5 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 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";
public static final String CONTENT_ITEM_TYPE = "vnd.android.cursor.item/text_note"; // 文本便签的项 MIME 类型
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 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 * 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; package net.micode.notes.data;
@ -20,48 +22,26 @@ import android.database.sqlite.SQLiteDatabase;
import android.database.sqlite.SQLiteOpenHelper; import android.database.sqlite.SQLiteOpenHelper;
import android.util.Log; import android.util.Log;
/** import net.micode.notes.data.Notes.DataColumns;
* NotesDatabaseHelper 便 import net.micode.notes.data.Notes.DataConstants;
* SQLiteOpenHelper import net.micode.notes.data.Notes.NoteColumns;
* 访线
*/
public class NotesDatabaseHelper extends SQLiteOpenHelper { public class NotesDatabaseHelper extends SQLiteOpenHelper {
/**
*
*/
private static final String DB_NAME = "note.db"; private static final String DB_NAME = "note.db";
/**
*
*/
private static final int DB_VERSION = 5; private static final int DB_VERSION = 5;
/**
*
*/
public interface TABLE { public interface TABLE {
/**
* 便
*/
public static final String NOTE = "note"; public static final String NOTE = "note";
/**
*
*/
public static final String DATA = "data"; public static final String DATA = "data";
} }
/**
*
*/
private static final String TAG = "NotesDatabaseHelper"; private static final String TAG = "NotesDatabaseHelper";
/**
* 访
*/
private static NotesDatabaseHelper mInstance; private static NotesDatabaseHelper mInstance;
/**
* 便 SQL
*/
private static final String CREATE_NOTE_TABLE_SQL = private static final String CREATE_NOTE_TABLE_SQL =
"CREATE TABLE " + TABLE.NOTE + "(" + "CREATE TABLE " + TABLE.NOTE + "(" +
NoteColumns.ID + " INTEGER PRIMARY KEY," + NoteColumns.ID + " INTEGER PRIMARY KEY," +
@ -84,9 +64,6 @@ public class NotesDatabaseHelper extends SQLiteOpenHelper {
NoteColumns.TOP + " INTEGER NOT NULL DEFAULT 0" + NoteColumns.TOP + " INTEGER NOT NULL DEFAULT 0" +
")"; ")";
/**
* SQL
*/
private static final String CREATE_DATA_TABLE_SQL = private static final String CREATE_DATA_TABLE_SQL =
"CREATE TABLE " + TABLE.DATA + "(" + "CREATE TABLE " + TABLE.DATA + "(" +
DataColumns.ID + " INTEGER PRIMARY KEY," + DataColumns.ID + " INTEGER PRIMARY KEY," +
@ -102,38 +79,146 @@ public class NotesDatabaseHelper extends SQLiteOpenHelper {
DataColumns.DATA5 + " TEXT NOT NULL DEFAULT ''" + DataColumns.DATA5 + " TEXT NOT NULL DEFAULT ''" +
")"; ")";
/**
* SQL
*/
private static final String CREATE_DATA_NOTE_ID_INDEX_SQL = private static final String CREATE_DATA_NOTE_ID_INDEX_SQL =
"CREATE INDEX IF NOT EXISTS note_id_index ON " + "CREATE INDEX IF NOT EXISTS note_id_index ON " +
TABLE.DATA + "(" + DataColumns.NOTE_ID + ");"; TABLE.DATA + "(" + DataColumns.NOTE_ID + ");";
/** /**
* * Increase folder's note count when move note to the folder
* @param context */
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) { public NotesDatabaseHelper(Context context) {
super(context, DB_NAME, null, DB_VERSION); super(context, DB_NAME, null, DB_VERSION);
} }
/**
* 便
* @param db
*/
public void createNoteTable(SQLiteDatabase db) { public void createNoteTable(SQLiteDatabase db) {
db.execSQL(CREATE_NOTE_TABLE_SQL); db.execSQL(CREATE_NOTE_TABLE_SQL);
reCreateNoteTableTriggers(db); reCreateNoteTableTriggers(db);
createSystemFolder(db); createSystemFolder(db);
Log.d(TAG, "便签表已创建"); Log.d(TAG, "note table has been created");
} }
/**
* 便
* @param db
*/
private void reCreateNoteTableTriggers(SQLiteDatabase db) { private void reCreateNoteTableTriggers(SQLiteDatabase db) {
// 删除旧触发器
db.execSQL("DROP TRIGGER IF EXISTS increase_folder_count_on_update"); 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_update");
db.execSQL("DROP TRIGGER IF EXISTS decrease_folder_count_on_delete"); 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_delete_notes_on_delete");
db.execSQL("DROP TRIGGER IF EXISTS folder_move_notes_on_trash"); db.execSQL("DROP TRIGGER IF EXISTS folder_move_notes_on_trash");
// 创建新触发器
db.execSQL(NOTE_INCREASE_FOLDER_COUNT_ON_UPDATE_TRIGGER); 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_UPDATE_TRIGGER);
db.execSQL(NOTE_DECREASE_FOLDER_COUNT_ON_DELETE_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); db.execSQL(FOLDER_MOVE_NOTES_ON_TRASH_TRIGGER);
} }
/**
*
* @param db
*/
private void createSystemFolder(SQLiteDatabase db) { private void createSystemFolder(SQLiteDatabase db) {
ContentValues values = new ContentValues(); ContentValues values = new ContentValues();
/**
* call record foler for call notes
*/
values.put(NoteColumns.ID, Notes.ID_CALL_RECORD_FOLDER); values.put(NoteColumns.ID, Notes.ID_CALL_RECORD_FOLDER);
values.put(NoteColumns.TYPE, Notes.TYPE_SYSTEM); values.put(NoteColumns.TYPE, Notes.TYPE_SYSTEM);
db.insert(TABLE.NOTE, null, values); db.insert(TABLE.NOTE, null, values);
/**
* root folder which is default folder
*/
values.clear(); values.clear();
values.put(NoteColumns.ID, Notes.ID_ROOT_FOLDER); values.put(NoteColumns.ID, Notes.ID_ROOT_FOLDER);
values.put(NoteColumns.TYPE, Notes.TYPE_SYSTEM); values.put(NoteColumns.TYPE, Notes.TYPE_SYSTEM);
db.insert(TABLE.NOTE, null, values); db.insert(TABLE.NOTE, null, values);
/**
* temporary folder which is used for moving note
*/
values.clear(); values.clear();
values.put(NoteColumns.ID, Notes.ID_TEMPARAY_FOLDER); values.put(NoteColumns.ID, Notes.ID_TEMPARAY_FOLDER);
values.put(NoteColumns.TYPE, Notes.TYPE_SYSTEM); values.put(NoteColumns.TYPE, Notes.TYPE_SYSTEM);
db.insert(TABLE.NOTE, null, values); db.insert(TABLE.NOTE, null, values);
/**
* create trash folder
*/
values.clear(); 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); values.put(NoteColumns.TYPE, Notes.TYPE_SYSTEM);
db.insert(TABLE.NOTE, null, values); db.insert(TABLE.NOTE, null, values);
} }
/**
*
* @param db
*/
public void createDataTable(SQLiteDatabase db) { public void createDataTable(SQLiteDatabase db) {
db.execSQL(CREATE_DATA_TABLE_SQL); db.execSQL(CREATE_DATA_TABLE_SQL);
reCreateDataTableTriggers(db); reCreateDataTableTriggers(db);
db.execSQL(CREATE_DATA_NOTE_ID_INDEX_SQL); 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) { 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_insert");
db.execSQL("DROP TRIGGER IF EXISTS update_note_content_on_update"); db.execSQL("DROP TRIGGER IF EXISTS update_note_content_on_update");
db.execSQL("DROP TRIGGER IF EXISTS update_note_content_on_delete"); 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_INSERT_TRIGGER);
db.execSQL(DATA_UPDATE_NOTE_CONTENT_ON_UPDATE_TRIGGER); db.execSQL(DATA_UPDATE_NOTE_CONTENT_ON_UPDATE_TRIGGER);
db.execSQL(DATA_UPDATE_NOTE_CONTENT_ON_DELETE_TRIGGER); db.execSQL(DATA_UPDATE_NOTE_CONTENT_ON_DELETE_TRIGGER);
} }
/**
*
* @param context
* @return
*/
static synchronized NotesDatabaseHelper getInstance(Context context) { static synchronized NotesDatabaseHelper getInstance(Context context) {
if (mInstance == null) { if (mInstance == null) {
mInstance = new NotesDatabaseHelper(context); mInstance = new NotesDatabaseHelper(context);
@ -214,22 +295,12 @@ public class NotesDatabaseHelper extends SQLiteOpenHelper {
return mInstance; return mInstance;
} }
/**
*
* @param db
*/
@Override @Override
public void onCreate(SQLiteDatabase db) { public void onCreate(SQLiteDatabase db) {
createNoteTable(db); createNoteTable(db);
createDataTable(db); createDataTable(db);
} }
/**
*
* @param db
* @param oldVersion
* @param newVersion
*/
@Override @Override
public void onUpgrade(SQLiteDatabase db, int oldVersion, int newVersion) { public void onUpgrade(SQLiteDatabase db, int oldVersion, int newVersion) {
boolean reCreateTriggers = false; boolean reCreateTriggers = false;
@ -264,14 +335,10 @@ public class NotesDatabaseHelper extends SQLiteOpenHelper {
if (oldVersion != newVersion) { if (oldVersion != newVersion) {
throw new IllegalStateException("Upgrade notes database to version " + newVersion throw new IllegalStateException("Upgrade notes database to version " + newVersion
+ " fails"); + "fails");
} }
} }
/**
* 2
* @param db
*/
private void upgradeToV2(SQLiteDatabase db) { private void upgradeToV2(SQLiteDatabase db) {
db.execSQL("DROP TABLE IF EXISTS " + TABLE.NOTE); db.execSQL("DROP TABLE IF EXISTS " + TABLE.NOTE);
db.execSQL("DROP TABLE IF EXISTS " + TABLE.DATA); db.execSQL("DROP TABLE IF EXISTS " + TABLE.DATA);
@ -279,39 +346,28 @@ public class NotesDatabaseHelper extends SQLiteOpenHelper {
createDataTable(db); createDataTable(db);
} }
/**
* 3
* @param db
*/
private void upgradeToV3(SQLiteDatabase db) { private void upgradeToV3(SQLiteDatabase db) {
// drop unused triggers // 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_insert");
db.execSQL("DROP TRIGGER IF EXISTS update_note_modified_date_on_delete"); db.execSQL("DROP TRIGGER IF EXISTS update_note_modified_date_on_delete");
db.execSQL("DROP TRIGGER IF EXISTS update_note_modified_date_on_update"); 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 db.execSQL("ALTER TABLE " + TABLE.NOTE + " ADD COLUMN " + NoteColumns.GTASK_ID
+ " TEXT NOT NULL DEFAULT ''"); + " TEXT NOT NULL DEFAULT ''");
// add a trash system folder
ContentValues values = new ContentValues(); 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); values.put(NoteColumns.TYPE, Notes.TYPE_SYSTEM);
db.insert(TABLE.NOTE, null, values); db.insert(TABLE.NOTE, null, values);
} }
/**
* 4
* @param db
*/
private void upgradeToV4(SQLiteDatabase db) { private void upgradeToV4(SQLiteDatabase db) {
db.execSQL("ALTER TABLE " + TABLE.NOTE + " ADD COLUMN " + NoteColumns.VERSION db.execSQL("ALTER TABLE " + TABLE.NOTE + " ADD COLUMN " + NoteColumns.VERSION
+ " INTEGER NOT NULL DEFAULT 0"); + " INTEGER NOT NULL DEFAULT 0");
} }
/**
* 5
* @param db
*/
private void upgradeToV5(SQLiteDatabase db) { private void upgradeToV5(SQLiteDatabase db) {
db.execSQL("ALTER TABLE " + TABLE.NOTE + " ADD COLUMN " + NoteColumns.TOP db.execSQL("ALTER TABLE " + TABLE.NOTE + " ADD COLUMN " + NoteColumns.TOP
+ " INTEGER NOT NULL DEFAULT 0"); + " 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 * 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; package net.micode.notes.data;
import android.app.SearchManager; import android.app.SearchManager;
import android.content.ContentProvider; import android.content.ContentProvider;
import android.content.ContentUris; 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.Notes.NoteColumns;
import net.micode.notes.data.NotesDatabaseHelper.TABLE; import net.micode.notes.data.NotesDatabaseHelper.TABLE;
/**
* NotesProvider ContentProvider便
* 便
* UriMatcher URI
*/
public class NotesProvider extends ContentProvider { public class NotesProvider extends ContentProvider {
/**
* URI UriMatcher
*/
private static final UriMatcher mMatcher; private static final UriMatcher mMatcher;
/**
* NotesDatabaseHelper
*/
private NotesDatabaseHelper mHelper; private NotesDatabaseHelper mHelper;
/**
*
*/
private static final String TAG = "NotesProvider"; private static final String TAG = "NotesProvider";
/** private static final int URI_NOTE = 1;
* URI private static final int URI_NOTE_ITEM = 2;
*/ private static final int URI_DATA = 3;
private static final int URI_NOTE = 1; // 便签表的 URI private static final int URI_DATA_ITEM = 4;
private static final int URI_NOTE_ITEM = 2; // 便签表中单个便签的 URI
private static final int URI_DATA = 3; // 数据表的 URI private static final int URI_SEARCH = 5;
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;
private static final int URI_SEARCH_SUGGEST = 6; // 搜索建议 URI
/**
* UriMatcher URI
*/
static { static {
mMatcher = new UriMatcher(UriMatcher.NO_MATCH); mMatcher = new UriMatcher(UriMatcher.NO_MATCH);
mMatcher.addURI(Notes.AUTHORITY, "note", URI_NOTE); 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 + "," private static final String NOTES_SEARCH_PROJECTION = NoteColumns.ID + ","
+ NoteColumns.ID + " AS " + SearchManager.SUGGEST_COLUMN_INTENT_EXTRA_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_1 + ","
+ "TRIM(REPLACE(" + NoteColumns.SNIPPET + ", x'0A','')) AS " + SearchManager.SUGGEST_COLUMN_TEXT_2 + "," + "TRIM(REPLACE(" + NoteColumns.SNIPPET + ", x'0A','')) AS " + SearchManager.SUGGEST_COLUMN_TEXT_2 + ","
+ R.drawable.search_result + " AS " + SearchManager.SUGGEST_COLUMN_ICON_1 + "," + R.drawable.search_result + " AS " + SearchManager.SUGGEST_COLUMN_ICON_1 + ","
+ "'" + Intent.ACTION_VIEW + "' AS " + SearchManager.SUGGEST_COLUMN_INTENT_ACTION + "," + "'" + Intent.ACTION_VIEW + "' AS " + SearchManager.SUGGEST_COLUMN_INTENT_ACTION + ","
+ "'" + Notes.TextNote.CONTENT_TYPE + "' AS " + SearchManager.SUGGEST_COLUMN_INTENT_DATA; + "'" + Notes.TextNote.CONTENT_TYPE + "' AS " + SearchManager.SUGGEST_COLUMN_INTENT_DATA;
/**
* 便 SQL
*/
private static String NOTES_SNIPPET_SEARCH_QUERY = "SELECT " + NOTES_SEARCH_PROJECTION private static String NOTES_SNIPPET_SEARCH_QUERY = "SELECT " + NOTES_SEARCH_PROJECTION
+ " FROM " + TABLE.NOTE + " FROM " + TABLE.NOTE
+ " WHERE " + NoteColumns.SNIPPET + " LIKE ?" + " WHERE " + NoteColumns.SNIPPET + " LIKE ?"
+ " AND " + NoteColumns.PARENT_ID + "<>" + Notes.ID_TRASH_FOLDER + " AND " + NoteColumns.PARENT_ID + "<>" + Notes.ID_TRASH_FOLER
+ " AND " + NoteColumns.TYPE + "=" + Notes.TYPE_NOTE; + " AND " + NoteColumns.TYPE + "=" + Notes.TYPE_NOTE;
/**
* NotesDatabaseHelper
* @return true
*/
@Override @Override
public boolean onCreate() { public boolean onCreate() {
mHelper = NotesDatabaseHelper.getInstance(getContext()); mHelper = NotesDatabaseHelper.getInstance(getContext());
return true; return true;
} }
/**
* URI
* 便便
* @param uri URI
* @param projection
* @param selection
* @param selectionArgs
* @param sortOrder
* @return
*/
@Override @Override
public Cursor query(Uri uri, String[] projection, String selection, String[] selectionArgs, public Cursor query(Uri uri, String[] projection, String selection, String[] selectionArgs,
String sortOrder) { String sortOrder) {
@ -178,14 +148,6 @@ public class NotesProvider extends ContentProvider {
return c; return c;
} }
/**
* URI URI
* 便
* URI
* @param uri URI
* @param values
* @return URI
*/
@Override @Override
public Uri insert(Uri uri, ContentValues values) { public Uri insert(Uri uri, ContentValues values) {
SQLiteDatabase db = mHelper.getWritableDatabase(); SQLiteDatabase db = mHelper.getWritableDatabase();
@ -220,173 +182,125 @@ public class NotesProvider extends ContentProvider {
return ContentUris.withAppendedId(uri, insertedId); return ContentUris.withAppendedId(uri, insertedId);
} }
/** @Override
* URI 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;
}
/** @Override
* URI public int update(Uri uri, ContentValues values, String selection, String[] selectionArgs) {
* 便便 int count = 0;
* URI String id = null;
* @param uri URI SQLiteDatabase db = mHelper.getWritableDatabase();
* @param selection boolean updateData = false;
* @param selectionArgs switch (mMatcher.match(uri)) {
* @return case URI_NOTE:
*/ increaseNoteVersion(-1, selection, selectionArgs);
@Override count = db.update(TABLE.NOTE, values, selection, selectionArgs);
public int delete(Uri uri, String selection, String[] selectionArgs) { break;
int count = 0; case URI_NOTE_ITEM:
String id = null; id = uri.getPathSegments().get(1);
SQLiteDatabase db = mHelper.getWritableDatabase(); increaseNoteVersion(Long.valueOf(id), selection, selectionArgs);
boolean deleteData = false; count = db.update(TABLE.NOTE, values, NoteColumns.ID + "=" + id
switch (mMatcher.match(uri)) { + parseSelection(selection), selectionArgs);
case URI_NOTE: break;
// 删除便签表中的记录,确保 ID 大于 0 case URI_DATA:
selection = "(" + selection + ") AND " + NoteColumns.ID + ">0 "; count = db.update(TABLE.DATA, values, selection, selectionArgs);
count = db.delete(TABLE.NOTE, selection, selectionArgs); updateData = true;
break; break;
case URI_NOTE_ITEM: case URI_DATA_ITEM:
// 删除便签表中的单个记录 id = uri.getPathSegments().get(1);
id = uri.getPathSegments().get(1); count = db.update(TABLE.DATA, values, DataColumns.ID + "=" + id
long noteId = Long.valueOf(id); + parseSelection(selection), selectionArgs);
// ID 小于等于 0 的是系统文件夹,不允许删除 updateData = true;
if (noteId <= 0) { break;
break; default:
} throw new IllegalArgumentException("Unknown URI " + uri);
count = db.delete(TABLE.NOTE, }
NoteColumns.ID + "=" + id + parseSelection(selection), selectionArgs);
break; if (count > 0) {
case URI_DATA: if (updateData) {
// 删除数据表中的记录 getContext().getContentResolver().notifyChange(Notes.CONTENT_NOTE_URI, null);
count = db.delete(TABLE.DATA, selection, selectionArgs); }
deleteData = true; getContext().getContentResolver().notifyChange(uri, null);
break; }
case URI_DATA_ITEM: return count;
// 删除数据表中的单个记录 }
id = uri.getPathSegments().get(1);
count = db.delete(TABLE.DATA, private String parseSelection(String selection) {
DataColumns.ID + "=" + id + parseSelection(selection), selectionArgs); return (!TextUtils.isEmpty(selection) ? " AND (" + selection + ')' : "");
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;
}
/**
*
* @param selection
* @return
*/
private String parseSelection(String selection) {
return (!TextUtils.isEmpty(selection) ? " AND (" + selection + ')' : "");
}
/**
* 便
* @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 (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());
}
/**
* URI MIME
* @param uri URI
* @return URI MIME
*/
@Override
public String getType(Uri uri) {
return null;
}
}
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 (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 { public class MetaData extends Task {
// 类名的简写,用于日志记录
private final static String TAG = MetaData.class.getSimpleName(); private final static String TAG = MetaData.class.getSimpleName();
// 与MetaData关联的GTask ID
private String mRelatedGid = null; private String mRelatedGid = null;
/**
* MetaData
*
* @param gid GTask ID
* @param metaInfo JSONObject
*/
public void setMeta(String gid, JSONObject metaInfo) { public void setMeta(String gid, JSONObject metaInfo) {
try { try {
// 将gid添加到metaInfo中
metaInfo.put(GTaskStringUtils.META_HEAD_GTASK_ID, gid); metaInfo.put(GTaskStringUtils.META_HEAD_GTASK_ID, gid);
} catch (JSONException e) { } catch (JSONException e) {
// 如果添加gid失败记录错误日志
Log.e(TAG, "failed to put related gid"); Log.e(TAG, "failed to put related gid");
} }
// 将metaInfo转换为字符串并设置为notes
setNotes(metaInfo.toString()); setNotes(metaInfo.toString());
// 设置MetaData的名称
setName(GTaskStringUtils.META_NOTE_NAME); setName(GTaskStringUtils.META_NOTE_NAME);
} }
/**
* MetaDataGTask ID
*
* @return GTask ID
*/
public String getRelatedGid() { public String getRelatedGid() {
return mRelatedGid; return mRelatedGid;
} }
/**
* MetaData
*
* @return notestrue
*/
@Override @Override
public boolean isWorthSaving() { public boolean isWorthSaving() {
return getNotes() != null; return getNotes() != null;
} }
/**
* JSONMetaData
*
* @param js JSON
*/
@Override @Override
public void setContentByRemoteJSON(JSONObject js) { public void setContentByRemoteJSON(JSONObject js) {
super.setContentByRemoteJSON(js); super.setContentByRemoteJSON(js);
if (getNotes() != null) { if (getNotes() != null) {
try { try {
// 从notes中获取metaInfo
JSONObject metaInfo = new JSONObject(getNotes().trim()); JSONObject metaInfo = new JSONObject(getNotes().trim());
// 获取关联的GTask ID
mRelatedGid = metaInfo.getString(GTaskStringUtils.META_HEAD_GTASK_ID); mRelatedGid = metaInfo.getString(GTaskStringUtils.META_HEAD_GTASK_ID);
} catch (JSONException e) { } catch (JSONException e) {
// 如果获取gid失败记录警告日志
Log.w(TAG, "failed to get related gid"); Log.w(TAG, "failed to get related gid");
mRelatedGid = null; mRelatedGid = null;
} }
} }
} }
/**
* JSONMetaData
*
* @param js JSON
* @throws IllegalAccessError
*/
@Override @Override
public void setContentByLocalJSON(JSONObject js) { public void setContentByLocalJSON(JSONObject js) {
// 该方法不应该被调用,抛出异常 // this function should not be called
throw new IllegalAccessError("MetaData:setContentByLocalJSON should not be called"); throw new IllegalAccessError("MetaData:setContentByLocalJSON should not be called");
} }
/**
* JSON
*
* @throws IllegalAccessError
*/
@Override @Override
public JSONObject getLocalJSONFromContent() { public JSONObject getLocalJSONFromContent() {
// 该方法不应该被调用,抛出异常
throw new IllegalAccessError("MetaData:getLocalJSONFromContent should not be called"); throw new IllegalAccessError("MetaData:getLocalJSONFromContent should not be called");
} }
/**
*
*
* @param c Cursor
* @throws IllegalAccessError
*/
@Override @Override
public int getSyncAction(Cursor c) { public int getSyncAction(Cursor c) {
// 该方法不应该被调用,抛出异常
throw new IllegalAccessError("MetaData:getSyncAction should not be called"); throw new IllegalAccessError("MetaData:getSyncAction should not be called");
} }
} }

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

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

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

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

@ -30,49 +30,34 @@ import org.json.JSONObject;
import java.util.ArrayList; import java.util.ArrayList;
/**
* TaskListNode
*/
public class TaskList extends Node { public class TaskList extends Node {
// 类名的简写,用于日志记录
private static final String TAG = TaskList.class.getSimpleName(); private static final String TAG = TaskList.class.getSimpleName();
// 任务列表的索引
private int mIndex; private int mIndex;
// 任务列表中的任务列表
private ArrayList<Task> mChildren; private ArrayList<Task> mChildren;
/**
*
*/
public TaskList() { public TaskList() {
super(); super();
mChildren = new ArrayList<Task>(); mChildren = new ArrayList<Task>();
mIndex = 1; mIndex = 1;
} }
/**
* JSON
*
* @param actionId ID
* @return JSON
*/
public JSONObject getCreateAction(int actionId) { public JSONObject getCreateAction(int actionId) {
JSONObject js = new JSONObject(); JSONObject js = new JSONObject();
try { try {
// 操作类型 // action_type
js.put(GTaskStringUtils.GTASK_JSON_ACTION_TYPE, js.put(GTaskStringUtils.GTASK_JSON_ACTION_TYPE,
GTaskStringUtils.GTASK_JSON_ACTION_TYPE_CREATE); GTaskStringUtils.GTASK_JSON_ACTION_TYPE_CREATE);
// 操作ID // action_id
js.put(GTaskStringUtils.GTASK_JSON_ACTION_ID, actionId); js.put(GTaskStringUtils.GTASK_JSON_ACTION_ID, actionId);
// 索引 // index
js.put(GTaskStringUtils.GTASK_JSON_INDEX, mIndex); js.put(GTaskStringUtils.GTASK_JSON_INDEX, mIndex);
// 实体差异 // entity_delta
JSONObject entity = new JSONObject(); JSONObject entity = new JSONObject();
entity.put(GTaskStringUtils.GTASK_JSON_NAME, getName()); entity.put(GTaskStringUtils.GTASK_JSON_NAME, getName());
entity.put(GTaskStringUtils.GTASK_JSON_CREATOR_ID, "null"); entity.put(GTaskStringUtils.GTASK_JSON_CREATOR_ID, "null");
@ -89,27 +74,21 @@ public class TaskList extends Node {
return js; return js;
} }
/**
* JSON
*
* @param actionId ID
* @return JSON
*/
public JSONObject getUpdateAction(int actionId) { public JSONObject getUpdateAction(int actionId) {
JSONObject js = new JSONObject(); JSONObject js = new JSONObject();
try { try {
// 操作类型 // action_type
js.put(GTaskStringUtils.GTASK_JSON_ACTION_TYPE, js.put(GTaskStringUtils.GTASK_JSON_ACTION_TYPE,
GTaskStringUtils.GTASK_JSON_ACTION_TYPE_UPDATE); GTaskStringUtils.GTASK_JSON_ACTION_TYPE_UPDATE);
// 操作ID // action_id
js.put(GTaskStringUtils.GTASK_JSON_ACTION_ID, actionId); js.put(GTaskStringUtils.GTASK_JSON_ACTION_ID, actionId);
// ID // id
js.put(GTaskStringUtils.GTASK_JSON_ID, getGid()); js.put(GTaskStringUtils.GTASK_JSON_ID, getGid());
// 实体差异 // entity_delta
JSONObject entity = new JSONObject(); JSONObject entity = new JSONObject();
entity.put(GTaskStringUtils.GTASK_JSON_NAME, getName()); entity.put(GTaskStringUtils.GTASK_JSON_NAME, getName());
entity.put(GTaskStringUtils.GTASK_JSON_DELETED, getDeleted()); entity.put(GTaskStringUtils.GTASK_JSON_DELETED, getDeleted());
@ -124,25 +103,20 @@ public class TaskList extends Node {
return js; return js;
} }
/**
* JSON
*
* @param js JSON
*/
public void setContentByRemoteJSON(JSONObject js) { public void setContentByRemoteJSON(JSONObject js) {
if (js != null) { if (js != null) {
try { try {
// ID // id
if (js.has(GTaskStringUtils.GTASK_JSON_ID)) { if (js.has(GTaskStringUtils.GTASK_JSON_ID)) {
setGid(js.getString(GTaskStringUtils.GTASK_JSON_ID)); setGid(js.getString(GTaskStringUtils.GTASK_JSON_ID));
} }
// 最后修改时间 // last_modified
if (js.has(GTaskStringUtils.GTASK_JSON_LAST_MODIFIED)) { if (js.has(GTaskStringUtils.GTASK_JSON_LAST_MODIFIED)) {
setLastModified(js.getLong(GTaskStringUtils.GTASK_JSON_LAST_MODIFIED)); setLastModified(js.getLong(GTaskStringUtils.GTASK_JSON_LAST_MODIFIED));
} }
// 名称 // name
if (js.has(GTaskStringUtils.GTASK_JSON_NAME)) { if (js.has(GTaskStringUtils.GTASK_JSON_NAME)) {
setName(js.getString(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) { public void setContentByLocalJSON(JSONObject js) {
if (js == null || !js.has(GTaskStringUtils.META_HEAD_NOTE)) { if (js == null || !js.has(GTaskStringUtils.META_HEAD_NOTE)) {
Log.w(TAG, "setContentByLocalJSON: nothing is avaiable"); Log.w(TAG, "setContentByLocalJSON: nothing is avaiable");
@ -188,11 +157,6 @@ public class TaskList extends Node {
} }
} }
/**
* JSON
*
* @return JSON
*/
public JSONObject getLocalJSONFromContent() { public JSONObject getLocalJSONFromContent() {
try { try {
JSONObject js = new JSONObject(); JSONObject js = new JSONObject();
@ -219,34 +183,28 @@ public class TaskList extends Node {
} }
} }
/**
*
*
* @param c Cursor
* @return
*/
public int getSyncAction(Cursor c) { public int getSyncAction(Cursor c) {
try { try {
if (c.getInt(SqlNote.LOCAL_MODIFIED_COLUMN) == 0) { if (c.getInt(SqlNote.LOCAL_MODIFIED_COLUMN) == 0) {
// 本地没有更新 // there is no local update
if (c.getLong(SqlNote.SYNC_ID_COLUMN) == getLastModified()) { if (c.getLong(SqlNote.SYNC_ID_COLUMN) == getLastModified()) {
// 两边都没有更新 // no update both side
return SYNC_ACTION_NONE; return SYNC_ACTION_NONE;
} else { } else {
// 应用远程更新到本地 // apply remote to local
return SYNC_ACTION_UPDATE_LOCAL; return SYNC_ACTION_UPDATE_LOCAL;
} }
} else { } else {
// 验证gtask ID // validate gtask id
if (!c.getString(SqlNote.GTASK_ID_COLUMN).equals(getGid())) { if (!c.getString(SqlNote.GTASK_ID_COLUMN).equals(getGid())) {
Log.e(TAG, "gtask id doesn't match"); Log.e(TAG, "gtask id doesn't match");
return SYNC_ACTION_ERROR; return SYNC_ACTION_ERROR;
} }
if (c.getLong(SqlNote.SYNC_ID_COLUMN) == getLastModified()) { if (c.getLong(SqlNote.SYNC_ID_COLUMN) == getLastModified()) {
// 只有本地修改 // local modification only
return SYNC_ACTION_UPDATE_REMOTE; return SYNC_ACTION_UPDATE_REMOTE;
} else { } else {
// 对于文件夹冲突,仅应用本地修改 // for folder conflicts, just apply local modification
return SYNC_ACTION_UPDATE_REMOTE; return SYNC_ACTION_UPDATE_REMOTE;
} }
} }
@ -258,27 +216,16 @@ public class TaskList extends Node {
return SYNC_ACTION_ERROR; return SYNC_ACTION_ERROR;
} }
/**
*
*
* @return
*/
public int getChildTaskCount() { public int getChildTaskCount() {
return mChildren.size(); return mChildren.size();
} }
/**
*
*
* @param task
* @return
*/
public boolean addChildTask(Task task) { public boolean addChildTask(Task task) {
boolean ret = false; boolean ret = false;
if (task != null && !mChildren.contains(task)) { if (task != null && !mChildren.contains(task)) {
ret = mChildren.add(task); ret = mChildren.add(task);
if (ret) { if (ret) {
// 需要设置前一个兄弟任务和父任务 // need to set prior sibling and parent
task.setPriorSibling(mChildren.isEmpty() ? null : mChildren task.setPriorSibling(mChildren.isEmpty() ? null : mChildren
.get(mChildren.size() - 1)); .get(mChildren.size() - 1));
task.setParent(this); task.setParent(this);
@ -287,13 +234,6 @@ public class TaskList extends Node {
return ret; return ret;
} }
/**
*
*
* @param task
* @param index
* @return
*/
public boolean addChildTask(Task task, int index) { public boolean addChildTask(Task task, int index) {
if (index < 0 || index > mChildren.size()) { if (index < 0 || index > mChildren.size()) {
Log.e(TAG, "add child task: invalid index"); Log.e(TAG, "add child task: invalid index");
@ -304,7 +244,7 @@ public class TaskList extends Node {
if (task != null && pos == -1) { if (task != null && pos == -1) {
mChildren.add(index, task); mChildren.add(index, task);
// 更新任务列表 // update the task list
Task preTask = null; Task preTask = null;
Task afterTask = null; Task afterTask = null;
if (index != 0) if (index != 0)
@ -320,12 +260,6 @@ public class TaskList extends Node {
return true; return true;
} }
/**
*
*
* @param task
* @return
*/
public boolean removeChildTask(Task task) { public boolean removeChildTask(Task task) {
boolean ret = false; boolean ret = false;
int index = mChildren.indexOf(task); int index = mChildren.indexOf(task);
@ -333,11 +267,11 @@ public class TaskList extends Node {
ret = mChildren.remove(task); ret = mChildren.remove(task);
if (ret) { if (ret) {
// 重置前一个兄弟任务和父任务 // reset prior sibling and parent
task.setPriorSibling(null); task.setPriorSibling(null);
task.setParent(null); task.setParent(null);
// 更新任务列表 // update the task list
if (index != mChildren.size()) { if (index != mChildren.size()) {
mChildren.get(index).setPriorSibling( mChildren.get(index).setPriorSibling(
index == 0 ? null : mChildren.get(index - 1)); index == 0 ? null : mChildren.get(index - 1));
@ -347,14 +281,8 @@ public class TaskList extends Node {
return ret; return ret;
} }
/**
*
*
* @param task
* @param index
* @return
*/
public boolean moveChildTask(Task task, int index) { public boolean moveChildTask(Task task, int index) {
if (index < 0 || index >= mChildren.size()) { if (index < 0 || index >= mChildren.size()) {
Log.e(TAG, "move child task: invalid index"); Log.e(TAG, "move child task: invalid index");
return false; return false;
@ -362,22 +290,15 @@ public class TaskList extends Node {
int pos = mChildren.indexOf(task); int pos = mChildren.indexOf(task);
if (pos == -1) { 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; return false;
} }
if (pos == index) if (pos == index)
return true; // 如果位置没有变化,直接返回 return true;
return (removeChildTask(task) && addChildTask(task, index));
return (removeChildTask(task) && addChildTask(task, index)); // 移除并添加到新位置
} }
/**
* GID
*
* @param gid
* @return null
*/
public Task findChildTaskByGid(String gid) { public Task findChildTaskByGid(String gid) {
for (int i = 0; i < mChildren.size(); i++) { for (int i = 0; i < mChildren.size(); i++) {
Task t = mChildren.get(i); Task t = mChildren.get(i);
@ -388,22 +309,10 @@ public class TaskList extends Node {
return null; return null;
} }
/**
*
*
* @param task
* @return -1
*/
public int getChildTaskIndex(Task task) { public int getChildTaskIndex(Task task) {
return mChildren.indexOf(task); return mChildren.indexOf(task);
} }
/**
*
*
* @param index
* @return null
*/
public Task getChildTaskByIndex(int index) { public Task getChildTaskByIndex(int index) {
if (index < 0 || index >= mChildren.size()) { if (index < 0 || index >= mChildren.size()) {
Log.e(TAG, "getTaskByIndex: invalid index"); Log.e(TAG, "getTaskByIndex: invalid index");
@ -412,12 +321,6 @@ public class TaskList extends Node {
return mChildren.get(index); return mChildren.get(index);
} }
/**
* GID
*
* @param gid
* @return null
*/
public Task getChilTaskByGid(String gid) { public Task getChilTaskByGid(String gid) {
for (Task task : mChildren) { for (Task task : mChildren) {
if (task.getGid().equals(gid)) if (task.getGid().equals(gid))
@ -426,30 +329,15 @@ public class TaskList extends Node {
return null; return null;
} }
/**
*
*
* @return
*/
public ArrayList<Task> getChildTaskList() { public ArrayList<Task> getChildTaskList() {
return this.mChildren; return this.mChildren;
} }
/**
*
*
* @param index
*/
public void setIndex(int index) { public void setIndex(int index) {
this.mIndex = index; this.mIndex = index;
} }
/**
*
*
* @return
*/
public int getIndex() { public int getIndex() {
return this.mIndex; return this.mIndex;
} }
} }

@ -16,45 +16,18 @@
package net.micode.notes.gtask.exception; package net.micode.notes.gtask.exception;
/**
* ActionFailureException Google
* RuntimeException
*/
public class ActionFailureException extends RuntimeException { public class ActionFailureException extends RuntimeException {
private static final long serialVersionUID = 4425249765923293627L; private static final long serialVersionUID = 4425249765923293627L;
/**
* ActionFailureException
*/
public ActionFailureException() { public ActionFailureException() {
super(); super();
} }
/**
* ActionFailureException
* @param paramString
*/
public ActionFailureException(String paramString) { public ActionFailureException(String paramString) {
super(paramString); super(paramString);
} }
/**
* ActionFailureException
* @param paramString
* @param paramThrowable
*/
public ActionFailureException(String paramString, Throwable paramThrowable) { public ActionFailureException(String paramString, Throwable paramThrowable) {
super(paramString, 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; package net.micode.notes.gtask.exception;
/**
* NetworkFailureException
* Exception
*/
public class NetworkFailureException extends Exception { public class NetworkFailureException extends Exception {
private static final long serialVersionUID = 2107610287180234136L; private static final long serialVersionUID = 2107610287180234136L;
/**
* NetworkFailureException
*/
public NetworkFailureException() { public NetworkFailureException() {
super(); super();
} }
/**
* NetworkFailureException
* @param paramString
*/
public NetworkFailureException(String paramString) { public NetworkFailureException(String paramString) {
super(paramString); super(paramString);
} }
/**
* NetworkFailureException
* @param paramString
* @param paramThrowable
*/
public NetworkFailureException(String paramString, Throwable paramThrowable) { public NetworkFailureException(String paramString, Throwable paramThrowable) {
super(paramString, 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; package net.micode.notes.gtask.remote;
import android.app.Notification; import android.app.Notification;
@ -11,118 +28,92 @@ import net.micode.notes.R;
import net.micode.notes.ui.NotesListActivity; import net.micode.notes.ui.NotesListActivity;
import net.micode.notes.ui.NotesPreferenceActivity; import net.micode.notes.ui.NotesPreferenceActivity;
/**
* Google
* AsyncTask Google
*/
public class GTaskASyncTask extends AsyncTask<Void, String, Integer> { 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 { public interface OnCompleteListener {
void onComplete(); // 当同步完成时调用 void onComplete();
} }
private Context mContext; // 应用程序上下文 private Context mContext;
private NotificationManager mNotifiManager; // 通知管理器
private GTaskManager mTaskManager; // Google 任务管理器 private NotificationManager mNotifiManager;
private OnCompleteListener mOnCompleteListener; // 完成监听器
private GTaskManager mTaskManager;
private OnCompleteListener mOnCompleteListener;
/**
*
* @param context
* @param listener
*/
public GTaskASyncTask(Context context, OnCompleteListener listener) { public GTaskASyncTask(Context context, OnCompleteListener listener) {
mContext = context; mContext = context;
mOnCompleteListener = listener; mOnCompleteListener = listener;
mNotifiManager = (NotificationManager) mContext.getSystemService(Context.NOTIFICATION_SERVICE); mNotifiManager = (NotificationManager) mContext
.getSystemService(Context.NOTIFICATION_SERVICE);
mTaskManager = GTaskManager.getInstance(); mTaskManager = GTaskManager.getInstance();
} }
/**
*
* Google cancelSync
*/
public void cancelSync() { public void cancelSync() {
mTaskManager.cancelSync(); mTaskManager.cancelSync();
} }
/**
*
* @param message
*/
public void publishProgess(String 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) { private void showNotification(int tickerId, String content) {
Notification notification = new Notification(R.drawable.notification, mContext.getString(tickerId), System.currentTimeMillis()); Notification notification = new Notification(R.drawable.notification, mContext
notification.defaults = Notification.DEFAULT_LIGHTS; // 设置默认灯光 .getString(tickerId), System.currentTimeMillis());
notification.flags = Notification.FLAG_AUTO_CANCEL; // 设置通知自动取消 notification.defaults = Notification.DEFAULT_LIGHTS;
PendingIntent pendingIntent; // 待决意图 notification.flags = Notification.FLAG_AUTO_CANCEL;
PendingIntent pendingIntent;
// 根据 tickerId 设置不同的待决意图
if (tickerId != R.string.ticker_success) { 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 { } 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); // 设置通知信息 notification.setLatestEventInfo(mContext, mContext.getString(R.string.app_name), content,
mNotifiManager.notify(GTASK_SYNC_NOTIFICATION_ID, notification); // 显示通知 pendingIntent);
mNotifiManager.notify(GTASK_SYNC_NOTIFICATION_ID, notification);
} }
/**
*
* @return
*/
@Override @Override
protected Integer doInBackground(Void... unused) { protected Integer doInBackground(Void... unused) {
publishProgess(mContext.getString(R.string.sync_progress_login, NotesPreferenceActivity.getSyncAccountName(mContext))); publishProgess(mContext.getString(R.string.sync_progress_login, NotesPreferenceActivity
return mTaskManager.sync(mContext, this); // 同步任务 .getSyncAccountName(mContext)));
return mTaskManager.sync(mContext, this);
} }
/**
*
* @param progress
*/
@Override @Override
protected void onProgressUpdate(String... progress) { protected void onProgressUpdate(String... progress) {
showNotification(R.string.ticker_syncing, progress[0]); // 显示同步中的通知 showNotification(R.string.ticker_syncing, progress[0]);
if (mContext instanceof GTaskSyncService) { if (mContext instanceof GTaskSyncService) {
((GTaskSyncService) mContext).sendBroadcast(progress[0]); // 如果上下文是 GTaskSyncService发送广播 ((GTaskSyncService) mContext).sendBroadcast(progress[0]);
} }
} }
/**
*
* @param result
*/
@Override @Override
protected void onPostExecute(Integer result) { protected void onPostExecute(Integer result) {
// 根据结果状态显示不同的通知
if (result == GTaskManager.STATE_SUCCESS) { if (result == GTaskManager.STATE_SUCCESS) {
showNotification(R.string.ticker_success, mContext.getString(R.string.success_sync_account, mTaskManager.getSyncAccount())); showNotification(R.string.ticker_success, mContext.getString(
NotesPreferenceActivity.setLastSyncTime(mContext, System.currentTimeMillis()); // 设置最后同步时间 R.string.success_sync_account, mTaskManager.getSyncAccount()));
NotesPreferenceActivity.setLastSyncTime(mContext, System.currentTimeMillis());
} else if (result == GTaskManager.STATE_NETWORK_ERROR) { } else if (result == GTaskManager.STATE_NETWORK_ERROR) {
showNotification(R.string.ticker_fail, mContext.getString(R.string.error_sync_network)); showNotification(R.string.ticker_fail, mContext.getString(R.string.error_sync_network));
} else if (result == GTaskManager.STATE_INTERNAL_ERROR) { } else if (result == GTaskManager.STATE_INTERNAL_ERROR) {
showNotification(R.string.ticker_fail, mContext.getString(R.string.error_sync_internal)); showNotification(R.string.ticker_fail, mContext.getString(R.string.error_sync_internal));
} else if (result == GTaskManager.STATE_SYNC_CANCELLED) { } 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) { if (mOnCompleteListener != null) {
new Thread(new Runnable() { new Thread(new Runnable() {
public void run() { public void run() {
mOnCompleteListener.onComplete(); 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; package net.micode.notes.gtask.remote;
import android.accounts.Account; import android.accounts.Account;
@ -44,32 +60,36 @@ import java.util.zip.GZIPInputStream;
import java.util.zip.Inflater; import java.util.zip.Inflater;
import java.util.zip.InflaterInputStream; import java.util.zip.InflaterInputStream;
/**
* GTaskClient Google
*
*/
public class GTaskClient { public class GTaskClient {
private static final String TAG = GTaskClient.class.getSimpleName(); // 类名的简写,用于日志输出 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_URL = "https://mail.google.com/tasks/";
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 final String GTASK_GET_URL = "https://mail.google.com/tasks/ig";
private static GTaskClient mInstance = null; // 单例对象 private static final String GTASK_POST_URL = "https://mail.google.com/tasks/r/ig";
private DefaultHttpClient mHttpClient; // HTTP客户端 private static GTaskClient mInstance = null;
private String mGetUrl; // 获取任务的URL
private String mPostUrl; // 发布任务的URL private DefaultHttpClient mHttpClient;
private long mClientVersion; // 客户端版本号
private boolean mLoggedin; // 登录状态 private String mGetUrl;
private long mLastLoginTime; // 上次登录时间
private int mActionId; // 动作ID private String mPostUrl;
private Account mAccount; // 账户信息
private JSONArray mUpdateArray; // 更新数组 private long mClientVersion;
/** private boolean mLoggedin;
*
*/ private long mLastLoginTime;
private int mActionId;
private Account mAccount;
private JSONArray mUpdateArray;
private GTaskClient() { private GTaskClient() {
mHttpClient = null; mHttpClient = null;
mGetUrl = GTASK_GET_URL; mGetUrl = GTASK_GET_URL;
@ -82,10 +102,6 @@ public class GTaskClient {
mUpdateArray = null; mUpdateArray = null;
} }
/**
*
* @return GTaskClient
*/
public static synchronized GTaskClient getInstance() { public static synchronized GTaskClient getInstance() {
if (mInstance == null) { if (mInstance == null) {
mInstance = new GTaskClient(); mInstance = new GTaskClient();
@ -93,39 +109,36 @@ public class GTaskClient {
return mInstance; return mInstance;
} }
/**
*
* @param activity Activity
* @return truefalse
*/
public boolean login(Activity activity) { 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()) { if (mLastLoginTime + interval < System.currentTimeMillis()) {
mLoggedin = false; mLoggedin = false;
} }
// 切换账户后需要重新登录 // need to re-login after account switch
if (mLoggedin && !TextUtils.equals(getSyncAccount().name, NotesPreferenceActivity.getSyncAccountName(activity))) { if (mLoggedin
&& !TextUtils.equals(getSyncAccount().name, NotesPreferenceActivity
.getSyncAccountName(activity))) {
mLoggedin = false; mLoggedin = false;
} }
// 如果已经登录,则直接返回
if (mLoggedin) { if (mLoggedin) {
Log.d(TAG, "already logged in"); Log.d(TAG, "already logged in");
return true; return true;
} }
// 更新登录时间
mLastLoginTime = System.currentTimeMillis(); mLastLoginTime = System.currentTimeMillis();
// 获取Google账户的认证令牌
String authToken = loginGoogleAccount(activity, false); String authToken = loginGoogleAccount(activity, false);
if (authToken == null) { if (authToken == null) {
Log.e(TAG, "login google account failed"); Log.e(TAG, "login google account failed");
return false; return false;
} }
// 如果账户不是Gmail或Googlemail则尝试使用自定义域名登录 // login with custom domain if necessary
if (!(mAccount.name.toLowerCase().endsWith("gmail.com") || mAccount.name.toLowerCase().endsWith("googlemail.com"))) { if (!(mAccount.name.toLowerCase().endsWith("gmail.com") || mAccount.name.toLowerCase()
.endsWith("googlemail.com"))) {
StringBuilder url = new StringBuilder(GTASK_URL).append("a/"); StringBuilder url = new StringBuilder(GTASK_URL).append("a/");
int index = mAccount.name.indexOf('@') + 1; int index = mAccount.name.indexOf('@') + 1;
String suffix = mAccount.name.substring(index); String suffix = mAccount.name.substring(index);
@ -133,13 +146,12 @@ public class GTaskClient {
mGetUrl = url.toString() + "ig"; mGetUrl = url.toString() + "ig";
mPostUrl = url.toString() + "r/ig"; mPostUrl = url.toString() + "r/ig";
// 尝试使用自定义域名登录Google任务
if (tryToLoginGtask(activity, authToken)) { if (tryToLoginGtask(activity, authToken)) {
mLoggedin = true; mLoggedin = true;
} }
} }
// 如果登录失败则尝试使用Google官方URL登录 // try to login with google official url
if (!mLoggedin) { if (!mLoggedin) {
mGetUrl = GTASK_GET_URL; mGetUrl = GTASK_GET_URL;
mPostUrl = GTASK_POST_URL; mPostUrl = GTASK_POST_URL;
@ -148,59 +160,46 @@ public class GTaskClient {
} }
} }
// 登录成功
mLoggedin = true; mLoggedin = true;
return true; return true;
} }
/**
* Google
* @param activity Activity
* @param invalidateToken 使
* @return
*/
private String loginGoogleAccount(Activity activity, boolean invalidateToken) { private String loginGoogleAccount(Activity activity, boolean invalidateToken) {
String authToken; String authToken;
AccountManager accountManager = AccountManager.get(activity); // 获取账户管理器 AccountManager accountManager = AccountManager.get(activity);
Account[] accounts = accountManager.getAccountsByType("com.google"); // 获取所有Google账户 Account[] accounts = accountManager.getAccountsByType("com.google");
// 如果没有Google账户则返回null
if (accounts.length == 0) { if (accounts.length == 0) {
Log.e(TAG, "there is no available google account"); Log.e(TAG, "there is no available google account");
return null; return null;
} }
// 获取同步账户的名称
String accountName = NotesPreferenceActivity.getSyncAccountName(activity); String accountName = NotesPreferenceActivity.getSyncAccountName(activity);
Account account = null; Account account = null;
// 查找与设置中账户名称相同的账户
for (Account a : accounts) { for (Account a : accounts) {
if (a.name.equals(accountName)) { if (a.name.equals(accountName)) {
account = a; account = a;
break; break;
} }
} }
// 如果找到账户,则保存账户信息
if (account != null) { if (account != null) {
mAccount = account; mAccount = account;
} else { } else {
// 如果没有找到账户则返回null
Log.e(TAG, "unable to get an account with the same name in the settings"); Log.e(TAG, "unable to get an account with the same name in the settings");
return null; return null;
} }
// 获取认证令牌 // get the token now
AccountManagerFuture<Bundle> accountManagerFuture = accountManager.getAuthToken(account, "goanna_mobile", null, activity, null, null); AccountManagerFuture<Bundle> accountManagerFuture = accountManager.getAuthToken(account,
"goanna_mobile", null, activity, null, null);
try { try {
Bundle authTokenBundle = accountManagerFuture.getResult(); // 获取认证令牌的Bundle对象 Bundle authTokenBundle = accountManagerFuture.getResult();
authToken = authTokenBundle.getString(AccountManager.KEY_AUTHTOKEN); // 获取认证令牌 authToken = authTokenBundle.getString(AccountManager.KEY_AUTHTOKEN);
// 如果需要,则使令牌无效并重新登录
if (invalidateToken) { if (invalidateToken) {
accountManager.invalidateAuthToken("com.google", authToken); accountManager.invalidateAuthToken("com.google", authToken);
loginGoogleAccount(activity, false); loginGoogleAccount(activity, false);
} }
} catch (Exception e) { } catch (Exception e) {
// 如果获取认证令牌失败则返回null
Log.e(TAG, "get auth token failed"); Log.e(TAG, "get auth token failed");
authToken = null; authToken = null;
} }
@ -208,24 +207,16 @@ public class GTaskClient {
return authToken; return authToken;
} }
/**
* 使Google
* @param activity Activity
* @param authToken
* @return truefalse
*/
private boolean tryToLoginGtask(Activity activity, String authToken) { private boolean tryToLoginGtask(Activity activity, String authToken) {
// 如果使用给定的认证令牌登录失败
if (!loginGtask(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); authToken = loginGoogleAccount(activity, true);
// 如果获取认证令牌失败
if (authToken == null) { if (authToken == null) {
Log.e(TAG, "login google account failed"); Log.e(TAG, "login google account failed");
return false; return false;
} }
// 如果使用新的认证令牌登录失败
if (!loginGtask(authToken)) { if (!loginGtask(authToken)) {
Log.e(TAG, "login gtask failed"); Log.e(TAG, "login gtask failed");
return false; return false;
@ -234,31 +225,28 @@ public class GTaskClient {
return true; return true;
} }
/**
* 使Google
* @param authToken
* @return truefalse
*/
private boolean loginGtask(String authToken) { private boolean loginGtask(String authToken) {
int timeoutConnection = 10000; // 设置连接超时时间 int timeoutConnection = 10000;
int timeoutSocket = 15000; // 设置套接字超时时间 int timeoutSocket = 15000;
HttpParams httpParameters = new BasicHttpParams(); HttpParams httpParameters = new BasicHttpParams();
HttpConnectionParams.setConnectionTimeout(httpParameters, timeoutConnection); HttpConnectionParams.setConnectionTimeout(httpParameters, timeoutConnection);
HttpConnectionParams.setSoTimeout(httpParameters, timeoutSocket); HttpConnectionParams.setSoTimeout(httpParameters, timeoutSocket);
mHttpClient = new DefaultHttpClient(httpParameters); // 创建HTTP客户端 mHttpClient = new DefaultHttpClient(httpParameters);
BasicCookieStore localBasicCookieStore = new BasicCookieStore(); BasicCookieStore localBasicCookieStore = new BasicCookieStore();
mHttpClient.setCookieStore(localBasicCookieStore); mHttpClient.setCookieStore(localBasicCookieStore);
HttpProtocolParams.setUseExpectContinue(mHttpClient.getParams(), false); // 设置HTTP协议参数 HttpProtocolParams.setUseExpectContinue(mHttpClient.getParams(), false);
// login gtask
try { try {
String loginUrl = mGetUrl + "?auth=" + authToken; // 构建登录URL String loginUrl = mGetUrl + "?auth=" + authToken;
HttpGet httpGet = new HttpGet(loginUrl); 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(); List<Cookie> cookies = mHttpClient.getCookieStore().getCookies();
boolean hasAuthCookie = false; boolean hasAuthCookie = false;
for (Cookie cookie : cookies) { for (Cookie cookie : cookies) {
// 检查是否存在认证Cookie
if (cookie.getName().contains("GTL")) { if (cookie.getName().contains("GTL")) {
hasAuthCookie = true; hasAuthCookie = true;
} }
@ -267,6 +255,7 @@ public class GTaskClient {
Log.w(TAG, "it seems that there is no auth cookie"); Log.w(TAG, "it seems that there is no auth cookie");
} }
// get the client version
String resString = getResponseContent(response.getEntity()); String resString = getResponseContent(response.getEntity());
String jsBegin = "_setup("; String jsBegin = "_setup(";
String jsEnd = ")}</script>"; String jsEnd = ")}</script>";
@ -283,7 +272,7 @@ public class GTaskClient {
e.printStackTrace(); e.printStackTrace();
return false; return false;
} catch (Exception e) { } catch (Exception e) {
// 捕获所有异常 // simply catch all exceptions
Log.e(TAG, "httpget gtask_url failed"); Log.e(TAG, "httpget gtask_url failed");
return false; return false;
} }
@ -291,18 +280,10 @@ public class GTaskClient {
return true; return true;
} }
/**
* ID
* @return ID
*/
private int getActionId() { private int getActionId() {
return mActionId++; // 返回并递增动作ID return mActionId++;
} }
/**
* HTTP POST
* @return HttpPost
*/
private HttpPost createHttpPost() { private HttpPost createHttpPost() {
HttpPost httpPost = new HttpPost(mPostUrl); HttpPost httpPost = new HttpPost(mPostUrl);
httpPost.setHeader("Content-Type", "application/x-www-form-urlencoded;charset=utf-8"); httpPost.setHeader("Content-Type", "application/x-www-form-urlencoded;charset=utf-8");
@ -310,12 +291,6 @@ public class GTaskClient {
return httpPost; return httpPost;
} }
/**
* HTTP
* @param entity HttpEntity
* @return
* @throws IOException IO
*/
private String getResponseContent(HttpEntity entity) throws IOException { private String getResponseContent(HttpEntity entity) throws IOException {
String contentEncoding = null; String contentEncoding = null;
if (entity.getContentEncoding() != 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 { private JSONObject postRequest(JSONObject js) throws NetworkFailureException {
if (!mLoggedin) { if (!mLoggedin) {
Log.e(TAG, "please login first"); Log.e(TAG, "please login first");
@ -367,9 +336,11 @@ public class GTaskClient {
UrlEncodedFormEntity entity = new UrlEncodedFormEntity(list, "UTF-8"); UrlEncodedFormEntity entity = new UrlEncodedFormEntity(list, "UTF-8");
httpPost.setEntity(entity); httpPost.setEntity(entity);
// execute the post
HttpResponse response = mHttpClient.execute(httpPost); HttpResponse response = mHttpClient.execute(httpPost);
String jsString = getResponseContent(response.getEntity()); String jsString = getResponseContent(response.getEntity());
return new JSONObject(jsString); return new JSONObject(jsString);
} catch (ClientProtocolException e) { } catch (ClientProtocolException e) {
Log.e(TAG, e.toString()); Log.e(TAG, e.toString());
e.printStackTrace(); e.printStackTrace();
@ -389,24 +360,25 @@ public class GTaskClient {
} }
} }
/**
*
* @param task Task
* @throws NetworkFailureException
*/
public void createTask(Task task) throws NetworkFailureException { public void createTask(Task task) throws NetworkFailureException {
commitUpdate(); commitUpdate();
try { try {
JSONObject jsPost = new JSONObject(); JSONObject jsPost = new JSONObject();
JSONArray actionList = new JSONArray(); JSONArray actionList = new JSONArray();
// action_list
actionList.put(task.getCreateAction(getActionId())); actionList.put(task.getCreateAction(getActionId()));
jsPost.put(GTaskStringUtils.GTASK_JSON_ACTION_LIST, actionList); jsPost.put(GTaskStringUtils.GTASK_JSON_ACTION_LIST, actionList);
// client_version
jsPost.put(GTaskStringUtils.GTASK_JSON_CLIENT_VERSION, mClientVersion); jsPost.put(GTaskStringUtils.GTASK_JSON_CLIENT_VERSION, mClientVersion);
// post
JSONObject jsResponse = postRequest(jsPost); 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)); task.setGid(jsResult.getString(GTaskStringUtils.GTASK_JSON_NEW_ID));
} catch (JSONException e) { } catch (JSONException e) {
Log.e(TAG, e.toString()); Log.e(TAG, e.toString());
e.printStackTrace(); e.printStackTrace();
@ -414,24 +386,25 @@ public class GTaskClient {
} }
} }
/**
*
* @param tasklist TaskList
* @throws NetworkFailureException
*/
public void createTaskList(TaskList tasklist) throws NetworkFailureException { public void createTaskList(TaskList tasklist) throws NetworkFailureException {
commitUpdate(); commitUpdate();
try { try {
JSONObject jsPost = new JSONObject(); JSONObject jsPost = new JSONObject();
JSONArray actionList = new JSONArray(); JSONArray actionList = new JSONArray();
// action_list
actionList.put(tasklist.getCreateAction(getActionId())); actionList.put(tasklist.getCreateAction(getActionId()));
jsPost.put(GTaskStringUtils.GTASK_JSON_ACTION_LIST, actionList); jsPost.put(GTaskStringUtils.GTASK_JSON_ACTION_LIST, actionList);
// client version
jsPost.put(GTaskStringUtils.GTASK_JSON_CLIENT_VERSION, mClientVersion); jsPost.put(GTaskStringUtils.GTASK_JSON_CLIENT_VERSION, mClientVersion);
// post
JSONObject jsResponse = postRequest(jsPost); 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)); tasklist.setGid(jsResult.getString(GTaskStringUtils.GTASK_JSON_NEW_ID));
} catch (JSONException e) { } catch (JSONException e) {
Log.e(TAG, e.toString()); Log.e(TAG, e.toString());
e.printStackTrace(); e.printStackTrace();
@ -439,16 +412,17 @@ public class GTaskClient {
} }
} }
/**
*
* @throws NetworkFailureException
*/
public void commitUpdate() throws NetworkFailureException { public void commitUpdate() throws NetworkFailureException {
if (mUpdateArray != null) { if (mUpdateArray != null) {
try { try {
JSONObject jsPost = new JSONObject(); JSONObject jsPost = new JSONObject();
// action_list
jsPost.put(GTaskStringUtils.GTASK_JSON_ACTION_LIST, mUpdateArray); jsPost.put(GTaskStringUtils.GTASK_JSON_ACTION_LIST, mUpdateArray);
// client_version
jsPost.put(GTaskStringUtils.GTASK_JSON_CLIENT_VERSION, mClientVersion); jsPost.put(GTaskStringUtils.GTASK_JSON_CLIENT_VERSION, mClientVersion);
postRequest(jsPost); postRequest(jsPost);
mUpdateArray = null; mUpdateArray = null;
} catch (JSONException e) { } catch (JSONException e) {
@ -459,54 +433,52 @@ public class GTaskClient {
} }
} }
/**
*
* @param node Node
* @throws NetworkFailureException
*/
public void addUpdateNode(Node node) throws NetworkFailureException { public void addUpdateNode(Node node) throws NetworkFailureException {
if (node != null) { if (node != null) {
// too many update items may result in an error
// set max to 10 items
if (mUpdateArray != null && mUpdateArray.length() > 10) { if (mUpdateArray != null && mUpdateArray.length() > 10) {
commitUpdate(); commitUpdate();
} }
if (mUpdateArray == null) { if (mUpdateArray == null)
mUpdateArray = new JSONArray(); mUpdateArray = new JSONArray();
}
mUpdateArray.put(node.getUpdateAction(getActionId())); mUpdateArray.put(node.getUpdateAction(getActionId()));
} }
} }
/** public void moveTask(Task task, TaskList preParent, TaskList curParent)
* throws NetworkFailureException {
* @param task Task
* @param preParent
* @param curParent
* @throws NetworkFailureException
*/
public void moveTask(Task task, TaskList preParent, TaskList curParent) throws NetworkFailureException {
commitUpdate(); commitUpdate();
try { try {
JSONObject jsPost = new JSONObject(); JSONObject jsPost = new JSONObject();
JSONArray actionList = new JSONArray(); JSONArray actionList = new JSONArray();
JSONObject action = new JSONObject(); 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_ACTION_ID, getActionId());
action.put(GTaskStringUtils.GTASK_JSON_ID, task.getGid()); action.put(GTaskStringUtils.GTASK_JSON_ID, task.getGid());
if (preParent == curParent && task.getPriorSibling() != null) { 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_PRIOR_SIBLING_ID, task.getPriorSibling());
} }
action.put(GTaskStringUtils.GTASK_JSON_SOURCE_LIST, preParent.getGid()); action.put(GTaskStringUtils.GTASK_JSON_SOURCE_LIST, preParent.getGid());
action.put(GTaskStringUtils.GTASK_JSON_DEST_PARENT, curParent.getGid()); action.put(GTaskStringUtils.GTASK_JSON_DEST_PARENT, curParent.getGid());
if (preParent != curParent) { if (preParent != curParent) {
// put the dest_list only if moving between tasklists
action.put(GTaskStringUtils.GTASK_JSON_DEST_LIST, curParent.getGid()); action.put(GTaskStringUtils.GTASK_JSON_DEST_LIST, curParent.getGid());
} }
actionList.put(action); actionList.put(action);
jsPost.put(GTaskStringUtils.GTASK_JSON_ACTION_LIST, actionList); jsPost.put(GTaskStringUtils.GTASK_JSON_ACTION_LIST, actionList);
// client_version
jsPost.put(GTaskStringUtils.GTASK_JSON_CLIENT_VERSION, mClientVersion); jsPost.put(GTaskStringUtils.GTASK_JSON_CLIENT_VERSION, mClientVersion);
postRequest(jsPost); postRequest(jsPost);
} catch (JSONException e) { } catch (JSONException e) {
Log.e(TAG, e.toString()); Log.e(TAG, e.toString());
e.printStackTrace(); e.printStackTrace();
@ -514,20 +486,18 @@ public class GTaskClient {
} }
} }
/**
*
* @param node Node
* @throws NetworkFailureException
*/
public void deleteNode(Node node) throws NetworkFailureException { public void deleteNode(Node node) throws NetworkFailureException {
commitUpdate(); commitUpdate();
try { try {
JSONObject jsPost = new JSONObject(); JSONObject jsPost = new JSONObject();
JSONArray actionList = new JSONArray(); JSONArray actionList = new JSONArray();
// action_list
node.setDeleted(true); node.setDeleted(true);
actionList.put(node.getUpdateAction(getActionId())); actionList.put(node.getUpdateAction(getActionId()));
jsPost.put(GTaskStringUtils.GTASK_JSON_ACTION_LIST, actionList); jsPost.put(GTaskStringUtils.GTASK_JSON_ACTION_LIST, actionList);
// client_version
jsPost.put(GTaskStringUtils.GTASK_JSON_CLIENT_VERSION, mClientVersion); jsPost.put(GTaskStringUtils.GTASK_JSON_CLIENT_VERSION, mClientVersion);
postRequest(jsPost); postRequest(jsPost);
@ -539,11 +509,6 @@ public class GTaskClient {
} }
} }
/**
*
* @return JSON
* @throws NetworkFailureException
*/
public JSONArray getTaskLists() throws NetworkFailureException { public JSONArray getTaskLists() throws NetworkFailureException {
if (!mLoggedin) { if (!mLoggedin) {
Log.e(TAG, "please login first"); Log.e(TAG, "please login first");
@ -552,8 +517,10 @@ public class GTaskClient {
try { try {
HttpGet httpGet = new HttpGet(mGetUrl); 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 resString = getResponseContent(response.getEntity());
String jsBegin = "_setup("; String jsBegin = "_setup(";
String jsEnd = ")}</script>"; String jsEnd = ")}</script>";
@ -580,12 +547,6 @@ public class GTaskClient {
} }
} }
/**
*
* @param listGid GID
* @return JSON
* @throws NetworkFailureException
*/
public JSONArray getTaskList(String listGid) throws NetworkFailureException { public JSONArray getTaskList(String listGid) throws NetworkFailureException {
commitUpdate(); commitUpdate();
try { try {
@ -593,12 +554,16 @@ public class GTaskClient {
JSONArray actionList = new JSONArray(); JSONArray actionList = new JSONArray();
JSONObject action = new JSONObject(); 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_ACTION_ID, getActionId());
action.put(GTaskStringUtils.GTASK_JSON_LIST_ID, listGid); action.put(GTaskStringUtils.GTASK_JSON_LIST_ID, listGid);
action.put(GTaskStringUtils.GTASK_JSON_GET_DELETED, false); action.put(GTaskStringUtils.GTASK_JSON_GET_DELETED, false);
actionList.put(action); actionList.put(action);
jsPost.put(GTaskStringUtils.GTASK_JSON_ACTION_LIST, actionList); jsPost.put(GTaskStringUtils.GTASK_JSON_ACTION_LIST, actionList);
// client_version
jsPost.put(GTaskStringUtils.GTASK_JSON_CLIENT_VERSION, mClientVersion); jsPost.put(GTaskStringUtils.GTASK_JSON_CLIENT_VERSION, mClientVersion);
JSONObject jsResponse = postRequest(jsPost); JSONObject jsResponse = postRequest(jsPost);
@ -610,58 +575,11 @@ public class GTaskClient {
} }
} }
/**
*
* @return
*/
public Account getSyncAccount() { public Account getSyncAccount() {
return mAccount; return mAccount;
} }
/**
*
*/
public void resetUpdateArray() { public void resetUpdateArray() {
mUpdateArray = null; 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

@ -5,7 +5,7 @@
* you may not use this file except in compliance with the License. * you may not use this file except in compliance with the License.
* You may obtain a copy of the License at * You may obtain a copy of the License at
* *
* http://www.apache.org/licenses/LICENSE-2.0 * http://www.apache.org/licenses/LICENSE-2.0
* *
* Unless required by applicable law or agreed to in writing, software * Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS, * distributed under the License is distributed on an "AS IS" BASIS,
@ -14,198 +14,148 @@
* limitations under the License. * limitations under the License.
*/ */
package net.micode.notes.gtask.remote; package net.micode.notes.gtask.remote;
import android.app.Activity; import android.app.Activity;
import android.app.Service; import android.app.Service;
import android.content.Context; import android.content.Context;
import android.content.Intent; import android.content.Intent;
import android.os.Bundle; import android.os.Bundle;
import android.os.IBinder; import android.os.IBinder;
/**
/** *
* GTaskSyncService GTask
*
* @ProjectName: minode * @ProjectName: minode
* @Package: net.micode.notes.gtask.remote * @Package: net.micode.notes.gtask.remote
* @ClassName: GTaskSyncService * @ClassName: GTaskSyncService
* @Description: GTask * @Description: Gtask
* @Author: * @Author:
* @Date: 2023-12-17 23:38 * @Date: 2023-12-17 23:38
*/ */
public class GTaskSyncService extends Service { public class GTaskSyncService extends Service {
// 定义广播的 Action 字符串 public final static String ACTION_STRING_NAME = "sync_action_type";
public final static String ACTION_STRING_NAME = "sync_action_type";
public final static int ACTION_START_SYNC = 0;
// 定义同步操作的类型常量
public final static int ACTION_START_SYNC = 0; public final static int ACTION_CANCEL_SYNC = 1;
public final static int ACTION_CANCEL_SYNC = 1;
public final static int ACTION_INVALID = 2; public final static int ACTION_INVALID = 2;
// 定义广播的名称 public final static String GTASK_SERVICE_BROADCAST_NAME = "net.micode.notes.gtask.remote.gtask_sync_service";
public final static String GTASK_SERVICE_BROADCAST_NAME = "net.micode.notes.gtask.remote.gtask_sync_service";
public final static String GTASK_SERVICE_BROADCAST_IS_SYNCING = "isSyncing";
// 定义广播的额外信息键
public final static String GTASK_SERVICE_BROADCAST_IS_SYNCING = "isSyncing"; public final static String GTASK_SERVICE_BROADCAST_PROGRESS_MSG = "progressMsg";
public final static String GTASK_SERVICE_BROADCAST_PROGRESS_MSG = "progressMsg";
private static GTaskASyncTask mSyncTask = null;
// 同步任务对象
private static GTaskASyncTask mSyncTask = null; private static String mSyncProgress = "";
// 同步进度字符串
private static String mSyncProgress = "";
/** /**
* startSync GTask * @method startSync
* * @description Gtask
* @date: 2023-12-17 23:42 * @date: 2023-12-17 23:42
* @author: * @author:
* @return void
*/ */
private void startSync() { private void startSync() {
// 如果同步任务为空,则创建并启动同步任务 if (mSyncTask == null) {
if (mSyncTask == null) { mSyncTask = new GTaskASyncTask(this, new GTaskASyncTask.OnCompleteListener() {
mSyncTask = new GTaskASyncTask(this, new GTaskASyncTask.OnCompleteListener() { public void onComplete() {
public void onComplete() { mSyncTask = null;
// 同步完成时,将同步任务置空并发送广播通知 sendBroadcast("");
mSyncTask = null; stopSelf();
sendBroadcast(""); }
stopSelf(); });
} sendBroadcast("");
}); // 使同步任务以单线程队列方式开启运行
// 发送广播通知同步开始 // 2023-12-17 23:46
sendBroadcast(""); mSyncTask.execute();
// 以单线程队列方式启动同步任务 }
mSyncTask.execute(); }
}
}
/** /**
* cancelSync GTask * @method cancelSync
* * @description Gtask
* @date: 2023-12-17 23:49 * @date: 2023-12-17 23:49
* @author: * @author:
* @return void
*/ */
private void cancelSync() { private void cancelSync() {
// 如果同步任务不为空,则取消同步任务 if (mSyncTask != null) {
if (mSyncTask != null) { mSyncTask.cancelSync();
mSyncTask.cancelSync(); }
} }
}
@Override
@Override public void onCreate() {
public void onCreate() { mSyncTask = null;
// 在服务创建时,将同步任务置空 }
mSyncTask = null;
}
/** /**
* onStartCommand 使 GTaskSyncService * @method onStartCommand
* * @description 使GtaskSyncService
* @date: 2023-12-17 23:53 * @date: 2023-12-17 23:53
* @author: * @author:
*/ * @return int
@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;
}
/**
* sendBroadcast 广
*
* @param msg
*/ */
public void sendBroadcast(String msg) { @Override
// 更新同步进度字符串 public int onStartCommand(Intent intent, int flags, int startId) {
mSyncProgress = msg; Bundle bundle = intent.getExtras();
// 创建 Intent 对象 if (bundle != null && bundle.containsKey(ACTION_STRING_NAME)) {
Intent intent = new Intent(GTASK_SERVICE_BROADCAST_NAME); switch (bundle.getInt(ACTION_STRING_NAME, ACTION_INVALID)) {
// 将是否正在同步和同步进度消息作为额外信息添加到 Intent 对象中 // 开始同步或取消同步
intent.putExtra(GTASK_SERVICE_BROADCAST_IS_SYNCING, mSyncTask != null); // 2023-12-17 23:52
intent.putExtra(GTASK_SERVICE_BROADCAST_PROGRESS_MSG, msg); case ACTION_START_SYNC:
// 发送广播通知 startSync();
sendBroadcast(intent); break;
} case ACTION_CANCEL_SYNC:
cancelSync();
/** break;
* startSync GTask default:
* break;
* @param activity Activity }
*/ return START_STICKY;
public static void startSync(Activity activity) { }
// 设置 Activity 上下文 return super.onStartCommand(intent, flags, startId);
GTaskManager.getInstance().setActivityContext(activity); }
// 创建 Intent 对象
Intent intent = new Intent(activity, GTaskSyncService.class); @Override
// 将同步操作类型设置为开始同步 public void onLowMemory() {
intent.putExtra(GTaskSyncService.ACTION_STRING_NAME, GTaskSyncService.ACTION_START_SYNC); if (mSyncTask != null) {
// 启动服务 mSyncTask.cancelSync();
activity.startService(intent); }
} }
/** public IBinder onBind(Intent intent) {
* cancelSync GTask return null;
* }
* @param context Context
*/ public void sendBroadcast(String msg) {
public static void cancelSync(Context context) { mSyncProgress = msg;
// 创建 Intent 对象 Intent intent = new Intent(GTASK_SERVICE_BROADCAST_NAME);
Intent intent = new Intent(context, GTaskSyncService.class); intent.putExtra(GTASK_SERVICE_BROADCAST_IS_SYNCING, mSyncTask != null);
// 将同步操作类型设置为取消同步 intent.putExtra(GTASK_SERVICE_BROADCAST_PROGRESS_MSG, msg);
intent.putExtra(GTaskSyncService.ACTION_STRING_NAME, GTaskSyncService.ACTION_CANCEL_SYNC); sendBroadcast(intent);
// 启动服务 }
context.startService(intent);
} public static void startSync(Activity activity) {
GTaskManager.getInstance().setActivityContext(activity);
/** Intent intent = new Intent(activity, GTaskSyncService.class);
* isSyncing intent.putExtra(GTaskSyncService.ACTION_STRING_NAME, GTaskSyncService.ACTION_START_SYNC);
* activity.startService(intent);
* @return }
*/
public static boolean isSyncing() { public static void cancelSync(Context context) {
// 如果同步任务不为空,则表示正在同步 Intent intent = new Intent(context, GTaskSyncService.class);
return mSyncTask != null; intent.putExtra(GTaskSyncService.ACTION_STRING_NAME, GTaskSyncService.ACTION_CANCEL_SYNC);
} context.startService(intent);
}
/**
* getProgressString public static boolean isSyncing() {
* return mSyncTask != null;
* @return }
*/
public static String getProgressString() { public static String getProgressString() {
// 返回同步进度字符串 return mSyncProgress;
return mSyncProgress; }
} }
}

@ -5,7 +5,7 @@
* you may not use this file except in compliance with the License. * you may not use this file except in compliance with the License.
* You may obtain a copy of the License at * You may obtain a copy of the License at
* *
* http://www.apache.org/licenses/LICENSE-2.0 * http://www.apache.org/licenses/LICENSE-2.0
* *
* Unless required by applicable law or agreed to in writing, software * Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS, * distributed under the License is distributed on an "AS IS" BASIS,
@ -15,7 +15,6 @@
*/ */
package net.micode.notes.model; package net.micode.notes.model;
import android.content.ContentProviderOperation; import android.content.ContentProviderOperation;
import android.content.ContentProviderResult; import android.content.ContentProviderResult;
import android.content.ContentUris; import android.content.ContentUris;
@ -34,23 +33,16 @@ import net.micode.notes.data.Notes.TextNote;
import java.util.ArrayList; 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 * Create a new note id for adding a new note to databases
* @param context
* @param folderId ID
* @return 便ID
*/ */
public static synchronized long getNewNoteId(Context context, long folderId) { public static synchronized long getNewNoteId(Context context, long folderId) {
// 创建一个新的便签 // Create a new note in the database
ContentValues values = new ContentValues(); ContentValues values = new ContentValues();
long createdTime = System.currentTimeMillis(); long createdTime = System.currentTimeMillis();
values.put(NoteColumns.CREATED_DATE, createdTime); values.put(NoteColumns.CREATED_DATE, createdTime);
@ -73,90 +65,45 @@ public class Note {
return noteId; return noteId;
} }
/**
* 便便
*/
public Note() { public Note() {
mNoteDiffValues = new ContentValues(); mNoteDiffValues = new ContentValues();
mNoteData = new NoteData(); mNoteData = new NoteData();
} }
/**
* 便
* @param key
* @param value
*/
public void setTopState(String key, int value) { public void setTopState(String key, int value) {
mNoteDiffValues.put(key, value); mNoteDiffValues.put(key, value);
} }
/**
* 便
* @param key
* @param value
*/
public void setNoteValue(String key, String value) { public void setNoteValue(String key, String value) {
mNoteDiffValues.put(key, value); mNoteDiffValues.put(key, value);
mNoteDiffValues.put(NoteColumns.LOCAL_MODIFIED, 1); mNoteDiffValues.put(NoteColumns.LOCAL_MODIFIED, 1);
mNoteDiffValues.put(NoteColumns.MODIFIED_DATE, System.currentTimeMillis()); mNoteDiffValues.put(NoteColumns.MODIFIED_DATE, System.currentTimeMillis());
} }
/**
*
* @param key
* @param value
*/
public void setTextData(String key, String value) { public void setTextData(String key, String value) {
mNoteData.setTextData(key, value); mNoteData.setTextData(key, value);
} }
/**
* ID
* @param id ID
*/
public void setTextDataId(long id) { public void setTextDataId(long id) {
mNoteData.setTextDataId(id); mNoteData.setTextDataId(id);
} }
/**
* ID
* @return ID
*/
public long getTextDataId() { public long getTextDataId() {
return mNoteData.mTextDataId; return mNoteData.mTextDataId;
} }
/**
* ID
* @param id ID
*/
public void setCallDataId(long id) { public void setCallDataId(long id) {
mNoteData.setCallDataId(id); mNoteData.setCallDataId(id);
} }
/**
*
* @param key
* @param value
*/
public void setCallData(String key, String value) { public void setCallData(String key, String value) {
mNoteData.setCallData(key, value); mNoteData.setCallData(key, value);
} }
/**
* 便
* @return truefalse
*/
public boolean isLocalModified() { public boolean isLocalModified() {
return mNoteDiffValues.size() > 0 || mNoteData.isLocalModified(); return mNoteDiffValues.size() > 0 || mNoteData.isLocalModified();
} }
/**
* 便
* @param context
* @param noteId 便ID
* @return truefalse
*/
public boolean syncNote(Context context, long noteId) { public boolean syncNote(Context context, long noteId) {
if (noteId <= 0) { if (noteId <= 0) {
throw new IllegalArgumentException("Wrong note id:" + noteId); throw new IllegalArgumentException("Wrong note id:" + noteId);
@ -166,10 +113,16 @@ public class Note {
return true; 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( if (context.getContentResolver().update(
ContentUris.withAppendedId(Notes.CONTENT_NOTE_URI, noteId), mNoteDiffValues, null, ContentUris.withAppendedId(Notes.CONTENT_NOTE_URI, noteId), mNoteDiffValues, null,
null) == 0) { null) == 0) {
Log.e(TAG, "Update note error, should not happen"); Log.e(TAG, "Update note error, should not happen");
// Do not return, fall through
} }
mNoteDiffValues.clear(); mNoteDiffValues.clear();
@ -181,19 +134,17 @@ public class Note {
return true; return true;
} }
/**
* NoteData 便
*/
private class NoteData { private class NoteData {
private long mTextDataId; // 文本数据ID private long mTextDataId;
private ContentValues mTextDataValues; // 文本数据值
private long mCallDataId; // 通话数据ID private ContentValues mTextDataValues;
private ContentValues mCallDataValues; // 通话数据值
private static final String TAG = "NoteData"; // 日志标签 private long mCallDataId;
private ContentValues mCallDataValues;
private static final String TAG = "NoteData";
/**
*
*/
public NoteData() { public NoteData() {
mTextDataValues = new ContentValues(); mTextDataValues = new ContentValues();
mCallDataValues = new ContentValues(); mCallDataValues = new ContentValues();
@ -201,18 +152,10 @@ public class Note {
mCallDataId = 0; mCallDataId = 0;
} }
/**
* 便
* @return truefalse
*/
boolean isLocalModified() { boolean isLocalModified() {
return mTextDataValues.size() > 0 || mCallDataValues.size() > 0; return mTextDataValues.size() > 0 || mCallDataValues.size() > 0;
} }
/**
* ID
* @param id ID
*/
void setTextDataId(long id) { void setTextDataId(long id) {
if(id <= 0) { if(id <= 0) {
throw new IllegalArgumentException("Text data id should larger than 0"); throw new IllegalArgumentException("Text data id should larger than 0");
@ -220,10 +163,6 @@ public class Note {
mTextDataId = id; mTextDataId = id;
} }
/**
* ID
* @param id ID
*/
void setCallDataId(long id) { void setCallDataId(long id) {
if (id <= 0) { if (id <= 0) {
throw new IllegalArgumentException("Call data id should larger than 0"); throw new IllegalArgumentException("Call data id should larger than 0");
@ -231,35 +170,22 @@ public class Note {
mCallDataId = id; mCallDataId = id;
} }
/**
*
* @param key
* @param value
*/
void setCallData(String key, String value) { void setCallData(String key, String value) {
mCallDataValues.put(key, value); mCallDataValues.put(key, value);
mNoteDiffValues.put(NoteColumns.LOCAL_MODIFIED, 1); mNoteDiffValues.put(NoteColumns.LOCAL_MODIFIED, 1);
mNoteDiffValues.put(NoteColumns.MODIFIED_DATE, System.currentTimeMillis()); mNoteDiffValues.put(NoteColumns.MODIFIED_DATE, System.currentTimeMillis());
} }
/**
*
* @param key
* @param value
*/
void setTextData(String key, String value) { void setTextData(String key, String value) {
mTextDataValues.put(key, value); mTextDataValues.put(key, value);
mNoteDiffValues.put(NoteColumns.LOCAL_MODIFIED, 1); mNoteDiffValues.put(NoteColumns.LOCAL_MODIFIED, 1);
mNoteDiffValues.put(NoteColumns.MODIFIED_DATE, System.currentTimeMillis()); mNoteDiffValues.put(NoteColumns.MODIFIED_DATE, System.currentTimeMillis());
} }
/**
* 便ContentResolver
* @param context
* @param noteId 便ID
* @return URInull
*/
Uri pushIntoContentResolver(Context context, long noteId) { Uri pushIntoContentResolver(Context context, long noteId) {
/**
* Check for safety
*/
if (noteId <= 0) { if (noteId <= 0) {
throw new IllegalArgumentException("Wrong note id:" + noteId); throw new IllegalArgumentException("Wrong note id:" + noteId);
} }
@ -281,7 +207,8 @@ public class Note {
return null; return null;
} }
} else { } else {
builder = ContentProviderOperation.newUpdate(ContentUris .withAppendedId(Notes.CONTENT_DATA_URI, mTextDataId)); builder = ContentProviderOperation.newUpdate(ContentUris.withAppendedId(
Notes.CONTENT_DATA_URI, mTextDataId));
builder.withValues(mTextDataValues); builder.withValues(mTextDataValues);
operationList.add(builder.build()); operationList.add(builder.build());
} }
@ -327,4 +254,4 @@ public class Note {
return null; return null;
} }
} }
} }

@ -5,7 +5,7 @@
* you may not use this file except in compliance with the License. * you may not use this file except in compliance with the License.
* You may obtain a copy of the License at * You may obtain a copy of the License at
* *
* http://www.apache.org/licenses/LICENSE-2.0 * http://www.apache.org/licenses/LICENSE-2.0
* *
* Unless required by applicable law or agreed to in writing, software * Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS, * distributed under the License is distributed on an "AS IS" BASIS,
@ -31,74 +31,82 @@ import net.micode.notes.data.Notes.NoteColumns;
import net.micode.notes.data.Notes.TextNote; import net.micode.notes.data.Notes.TextNote;
import net.micode.notes.tool.ResourceParser.NoteBgResources; import net.micode.notes.tool.ResourceParser.NoteBgResources;
/**
* WorkingNote 便
* 便
*/
public class WorkingNote { public class WorkingNote {
// 当前正在编辑的便签对象 // Note for the working note
private Note mNote; private Note mNote;
// 便签ID // Note Id
private long mNoteId; private long mNoteId;
// 便签内容 // Note content
private String mContent; private String mContent;
// 便签模式 // Note mode
private int mMode; private int mMode;
private long mAlertDate; // 提醒日期 private long mAlertDate;
private long mModifiedDate; // 修改日期
private int mBgColorId; // 背景颜色ID private long mModifiedDate;
private int mWidgetId; // 小部件ID
private int mWidgetType; // 小部件类型 private int mBgColorId;
private int mTop; // 置顶状态
private long mFolderId; // 文件夹ID private int mWidgetId;
private Context mContext; // 上下文
private int mWidgetType;
private static final String TAG = "WorkingNote"; // 日志标签 private int mTop;
private boolean mIsDeleted; // 是否已删除 private long mFolderId;
private NoteSettingChangedListener mNoteSettingStatusListener; // 设置变化监听器
private Context mContext;
private static final String TAG = "WorkingNote";
private boolean mIsDeleted;
private NoteSettingChangedListener mNoteSettingStatusListener;
// 数据查询投影
public static final String[] DATA_PROJECTION = new String[] { public static final String[] DATA_PROJECTION = new String[] {
DataColumns.ID, // 数据ID DataColumns.ID,
DataColumns.CONTENT, // 数据内容 DataColumns.CONTENT,
DataColumns.MIME_TYPE, // 数据类型 DataColumns.MIME_TYPE,
DataColumns.DATA1, // 数据1 DataColumns.DATA1,
DataColumns.DATA2, // 数据2 DataColumns.DATA2,
DataColumns.DATA3, // 数据3 DataColumns.DATA3,
DataColumns.DATA4 // 数据4 DataColumns.DATA4,
}; };
// 便签查询投影
public static final String[] NOTE_PROJECTION = new String[] { public static final String[] NOTE_PROJECTION = new String[] {
NoteColumns.PARENT_ID, // 父ID NoteColumns.PARENT_ID,
NoteColumns.ALERTED_DATE, // 提醒日期 NoteColumns.ALERTED_DATE,
NoteColumns.BG_COLOR_ID, // 背景颜色ID NoteColumns.BG_COLOR_ID,
NoteColumns.WIDGET_ID, // 小部件ID NoteColumns.WIDGET_ID,
NoteColumns.WIDGET_TYPE, // 小部件类型 NoteColumns.WIDGET_TYPE,
NoteColumns.MODIFIED_DATE, // 修改日期 NoteColumns.MODIFIED_DATE,
NoteColumns.TOP // 置顶状态 NoteColumns.TOP
}; };
// 列索引
private static final int DATA_ID_COLUMN = 0; private static final int DATA_ID_COLUMN = 0;
private static final int DATA_CONTENT_COLUMN = 1; private static final int DATA_CONTENT_COLUMN = 1;
private static final int DATA_MIME_TYPE_COLUMN = 2; private static final int DATA_MIME_TYPE_COLUMN = 2;
private static final int DATA_MODE_COLUMN = 3; private static final int DATA_MODE_COLUMN = 3;
private static final int NOTE_PARENT_ID_COLUMN = 0; private static final int NOTE_PARENT_ID_COLUMN = 0;
private static final int NOTE_ALERTED_DATE_COLUMN = 1; private static final int NOTE_ALERTED_DATE_COLUMN = 1;
private static final int NOTE_BG_COLOR_ID_COLUMN = 2; 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_ID_COLUMN = 3;
private static final int NOTE_WIDGET_TYPE_COLUMN = 4; private static final int NOTE_WIDGET_TYPE_COLUMN = 4;
private static final int NOTE_MODIFIED_DATE_COLUMN = 5; private static final int NOTE_MODIFIED_DATE_COLUMN = 5;
private static final int NOTE_TOP_STATE_COLUMN = 6; private static final int NOTE_TOP_STATE_COLUMN = 6;
/** // New note construct
* 便
* @param context
* @param folderId ID
*/
private WorkingNote(Context context, long folderId) { private WorkingNote(Context context, long folderId) {
mContext = context; mContext = context;
mAlertDate = 0; mAlertDate = 0;
@ -112,12 +120,7 @@ public class WorkingNote {
mWidgetType = Notes.TYPE_WIDGET_INVALIDE; mWidgetType = Notes.TYPE_WIDGET_INVALIDE;
} }
/** // Existing note construct
* 便
* @param context
* @param noteId 便ID
* @param folderId ID
*/
private WorkingNote(Context context, long noteId, long folderId) { private WorkingNote(Context context, long noteId, long folderId) {
mContext = context; mContext = context;
mNoteId = noteId; mNoteId = noteId;
@ -127,11 +130,7 @@ public class WorkingNote {
loadNote(); loadNote();
} }
/**
* 便
*/
private void loadNote() { private void loadNote() {
// 查询便签信息
Cursor cursor = mContext.getContentResolver().query( Cursor cursor = mContext.getContentResolver().query(
ContentUris.withAppendedId(Notes.CONTENT_NOTE_URI, mNoteId), NOTE_PROJECTION, null, ContentUris.withAppendedId(Notes.CONTENT_NOTE_URI, mNoteId), NOTE_PROJECTION, null,
null, null); null, null);
@ -154,11 +153,7 @@ public class WorkingNote {
loadNoteData(); loadNoteData();
} }
/**
* 便
*/
private void loadNoteData() { private void loadNoteData() {
// 查询便签数据
Cursor cursor = mContext.getContentResolver().query(Notes.CONTENT_DATA_URI, DATA_PROJECTION, Cursor cursor = mContext.getContentResolver().query(Notes.CONTENT_DATA_URI, DATA_PROJECTION,
DataColumns.NOTE_ID + "=?", new String[] { DataColumns.NOTE_ID + "=?", new String[] {
String.valueOf(mNoteId) 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, public static WorkingNote createEmptyNote(Context context, long folderId, int widgetId,
int widgetType, int defaultBgColorId) { 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); WorkingNote note = new WorkingNote(context, folderId);
note.setBgColorId(defaultBgColorId); note.setBgColorId(defaultBgColorId);
note.setWidgetId(widgetId); note.setWidgetId(widgetId);
@ -204,20 +198,47 @@ public class WorkingNote {
return note; return note;
} }
/**
* 便
* @param context
* @param id 便ID
* @return WorkingNote
*/
public static WorkingNote load(Context context, long id) { public static WorkingNote load(Context context, long id) {
return new WorkingNote(context, id, 0); return new WorkingNote(context, id, 0);
} }
/** public synchronized boolean saveNote() {// TODO: 2023/12/19 要仔细溯源看看
* 便 /**
* @return truefalse * @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() { private boolean isWorthSaving() {
if (mIsDeleted || (!existInDatabase() && TextUtils.isEmpty(mContent)) if (mIsDeleted || (!existInDatabase() && TextUtils.isEmpty(mContent))
|| (existInDatabase() && !mNote.isLocalModified())) { || (existInDatabase() && !mNote.isLocalModified())) {
@ -228,60 +249,63 @@ public class WorkingNote {
} }
/** /**
* * @method: setOnSettingStatusChangedListener
* @param l * @description: NoteEditActivityNoteSettingChangedListener
* @date: 2023/12/21 0:10
* @author: zhoukexing
* @param: [l] NoteEditActivity
* @return: void
*/ */
public void setOnSettingStatusChangedListener(NoteSettingChangedListener l) { public void setOnSettingStatusChangedListener(NoteSettingChangedListener l) {
//Q: 这里的l是怎么获取的。l是NoteEditActivity @zkx 2023/12/21
mNoteSettingStatusListener = l; mNoteSettingStatusListener = l;
} }
/**
* 便
*/
public void reverseTopState() { public void reverseTopState() {
mTop ^= 1; mTop ^= 1;
mNote.setTopState(NoteColumns.TOP, mTop); mNote.setTopState(NoteColumns.TOP, mTop);
} }
/** public int getmTop(){
* 便
* @return
*/
public int getmTop() {
return mTop; return mTop;
} }
/** /**
* 便 * @Method setAlertDate
* @param date * @Date 2023/12/13 11:43
* @param set * @param date
* @param set
* @Author lenovo
* @Return void
* @Description
*/ */
public void setAlertDate(long date, boolean set) { public void setAlertDate(long date, boolean set) {
// 更新便签项中的提醒日期
if (date != mAlertDate) { if (date != mAlertDate) {
mAlertDate = date; mAlertDate = date;
mNote.setNoteValue(NoteColumns.ALERTED_DATE, String.valueOf(mAlertDate)); mNote.setNoteValue(NoteColumns.ALERTED_DATE, String.valueOf(mAlertDate));
} }
// 设置提醒的监听事件
if (mNoteSettingStatusListener != null) { if (mNoteSettingStatusListener != null) {
mNoteSettingStatusListener.onClockAlertChanged(date, set); mNoteSettingStatusListener.onClockAlertChanged(date, set);
} }
} }
/** /**
* 便 * @method: markDeleted
* @param mark * @description:
* @date: 2023/12/21 0:50
* @author: zhoukexing
* @param: [mark]
* @return: void
*/ */
public void markDeleted(boolean mark) { public void markDeleted(boolean mark) {
mIsDeleted = mark; mIsDeleted = mark;
if (mWidgetId != AppWidgetManager.INVALID_APPWIDGET_ID if (mWidgetId != AppWidgetManager.INVALID_APPWIDGET_ID
&& mWidgetType != Notes.TYPE_WIDGET_INVALIDE && mNoteSettingStatusListener != null) { && mWidgetType != Notes.TYPE_WIDGET_INVALIDE && mNoteSettingStatusListener != null) {
mNoteSettingStatusListener.onWidgetChanged(); mNoteSettingStatusListener.onWidgetChanged();
} }
} }
/**
* 便ID
* @param id ID
*/
public void setBgColorId(int id) { public void setBgColorId(int id) {
if (id != mBgColorId) { if (id != mBgColorId) {
mBgColorId = id; mBgColorId = id;
@ -292,10 +316,6 @@ public class WorkingNote {
} }
} }
/**
* 便
* @param mode
*/
public void setCheckListMode(int mode) { public void setCheckListMode(int mode) {
if (mMode != mode) { if (mMode != mode) {
if (mNoteSettingStatusListener != null) { if (mNoteSettingStatusListener != null) {
@ -306,10 +326,6 @@ public class WorkingNote {
} }
} }
/**
* 便
* @param type
*/
public void setWidgetType(int type) { public void setWidgetType(int type) {
if (type != mWidgetType) { if (type != mWidgetType) {
mWidgetType = type; mWidgetType = type;
@ -317,10 +333,6 @@ public class WorkingNote {
} }
} }
/**
* 便ID
* @param id ID
*/
public void setWidgetId(int id) { public void setWidgetId(int id) {
if (id != mWidgetId) { if (id != mWidgetId) {
mWidgetId = id; mWidgetId = id;
@ -328,10 +340,6 @@ public class WorkingNote {
} }
} }
/**
* 便
* @param text
*/
public void setWorkingText(String text) { public void setWorkingText(String text) {
if (!TextUtils.equals(mContent, text)) { if (!TextUtils.equals(mContent, text)) {
mContent = text; mContent = text;
@ -339,139 +347,81 @@ public class WorkingNote {
} }
} }
/**
* 便便
* @param phoneNumber
* @param callDate
*/
public void convertToCallNote(String phoneNumber, long callDate) { public void convertToCallNote(String phoneNumber, long callDate) {
mNote.setCallData(CallNote.CALL_DATE, String.valueOf(callDate)); mNote.setCallData(CallNote.CALL_DATE, String.valueOf(callDate));
mNote.setCallData(CallNote.PHONE_NUMBER, phoneNumber); mNote.setCallData(CallNote.PHONE_NUMBER, phoneNumber);
mNote.setNoteValue(NoteColumns.PARENT_ID, String.valueOf(Notes.ID_CALL_RECORD_FOLDER)); mNote.setNoteValue(NoteColumns.PARENT_ID, String.valueOf(Notes.ID_CALL_RECORD_FOLDER));
} }
/**
* 便
* @return truefalse
*/
public boolean hasClockAlert() { public boolean hasClockAlert() {
return (mAlertDate > 0); return (mAlertDate > 0 ? true : false);
} }
/**
* 便
* @return 便
*/
public String getContent() { public String getContent() {
return mContent; return mContent;
} }
/**
* 便
* @return
*/
public long getAlertDate() { public long getAlertDate() {
return mAlertDate; return mAlertDate;
} }
/**
* 便
* @return
*/
public long getModifiedDate() { public long getModifiedDate() {
return mModifiedDate; return mModifiedDate;
} }
/**
* 便ID
* @return ID
*/
public int getBgColorResId() { public int getBgColorResId() {
return NoteBgResources.getNoteBgResource(mBgColorId); return NoteBgResources.getNoteBgResource(mBgColorId);
} }
/**
* 便ID
* @return ID
*/
public int getBgColorId() { public int getBgColorId() {
return mBgColorId; return mBgColorId;
} }
/**
* 便ID
* @return ID
*/
public int getTitleBgResId() { public int getTitleBgResId() {
return NoteBgResources.getNoteTitleBgResource(mBgColorId); return NoteBgResources.getNoteTitleBgResource(mBgColorId);
} }
/**
* 便
* @return
*/
public int getCheckListMode() { public int getCheckListMode() {
return mMode; return mMode;
} }
/**
* 便ID
* @return 便ID
*/
public long getNoteId() { public long getNoteId() {
return mNoteId; return mNoteId;
} }
/**
* 便ID
* @return ID
*/
public long getFolderId() { public long getFolderId() {
return mFolderId; return mFolderId;
} }
/**
* 便ID
* @return ID
*/
public int getWidgetId() { public int getWidgetId() {
return mWidgetId; return mWidgetId;
} }
/**
* 便
* @return
*/
public int getWidgetType() { public int getWidgetType() {
return mWidgetType; return mWidgetType;
} }
/**
* 便
*/
public interface NoteSettingChangedListener { public interface NoteSettingChangedListener {
/** /**
* 便 * Called when the background color of current note has just changed
*/ */
void onBackgroundColorChanged(); void onBackgroundColorChanged();
/** /**
* * Called when user set clock
* @param date
* @param set
*/ */
void onClockAlertChanged(long date, boolean set); void onClockAlertChanged(long date, boolean set);
/** /**
* 便 * Call when user create note from widget
*/ */
void onWidgetChanged(); void onWidgetChanged();
/** /**
* * Call when switch between check list mode and normal mode
* @param oldMode * @param oldMode is previous mode before change
* @param newMode * @param newMode is new mode
*/ */
void onCheckListModeChanged(int oldMode, int newMode); 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 * 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.app.AlertDialog;
import android.content.Context; import android.content.Context;
import android.content.DialogInterface; import android.content.DialogInterface;
import android.content.DialogInterface.OnClickListener;
import android.content.DialogInterface.OnDismissListener;
import android.content.Intent; import android.content.Intent;
import android.media.AudioManager; import android.media.AudioManager;
import android.media.MediaPlayer; import android.media.MediaPlayer;
@ -36,138 +40,171 @@ import net.micode.notes.tool.DataUtils;
import java.io.IOException; import java.io.IOException;
/** // AlarmAlertActivity类继承自Activity实现OnClickListener和OnDismissListener接口
* AlarmAlertActivity public class AlarmAlertActivity extends Activity implements OnClickListener, OnDismissListener {
* Activity private long mNoteId; //文本在数据库存储中的ID号
*/ private String mSnippet; //闹钟提示时出现的文本片段
public class AlarmAlertActivity extends Activity implements DialogInterface.OnClickListener, DialogInterface.OnDismissListener { private static final int SNIPPET_PREW_MAX_LEN = 60;
private long mNoteId; // 便签在数据库中的 ID MediaPlayer mPlayer;
private String mSnippet; // 闹钟提示时显示的文本片段
private static final int SNIPPET_PREVIEW_MAX_LEN = 60; // 文本片段的最大长度
private MediaPlayer mPlayer; // 用于播放闹钟声音的 MediaPlayer
@Override @Override
protected void onCreate(Bundle savedInstanceState) { protected void onCreate(Bundle savedInstanceState) {
super.onCreate(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(); final Window win = getWindow();
win.addFlags(WindowManager.LayoutParams.FLAG_SHOW_WHEN_LOCKED); // 在锁屏状态下显示 win.addFlags(WindowManager.LayoutParams.FLAG_SHOW_WHEN_LOCKED);
if (!isScreenOn()) { if (!isScreenOn()) {
// 如果屏幕是关闭的,点亮屏幕并允许锁屏
win.addFlags(WindowManager.LayoutParams.FLAG_KEEP_SCREEN_ON win.addFlags(WindowManager.LayoutParams.FLAG_KEEP_SCREEN_ON
//保持窗体点亮
| WindowManager.LayoutParams.FLAG_TURN_SCREEN_ON | WindowManager.LayoutParams.FLAG_TURN_SCREEN_ON
//将窗体点亮
| WindowManager.LayoutParams.FLAG_ALLOW_LOCK_WHILE_SCREEN_ON | WindowManager.LayoutParams.FLAG_ALLOW_LOCK_WHILE_SCREEN_ON
//允许窗体点亮时锁屏
| WindowManager.LayoutParams.FLAG_LAYOUT_INSET_DECOR); | WindowManager.LayoutParams.FLAG_LAYOUT_INSET_DECOR);
} }//在手机锁屏后如果到了闹钟提示时间,点亮屏幕
Intent intent = getIntent(); Intent intent = getIntent();
try { try {
mNoteId = Long.valueOf(intent.getData().getPathSegments().get(1)); mNoteId = Long.valueOf(intent.getData().getPathSegments().get(1));
mSnippet = DataUtils.getSnippetById(this.getContentResolver(), mNoteId); mSnippet = DataUtils.getSnippetById(this.getContentResolver(), mNoteId);
if (mSnippet.length() > SNIPPET_PREVIEW_MAX_LEN) { //根据ID从数据库中获取标签的内容
mSnippet = mSnippet.substring(0, SNIPPET_PREVIEW_MAX_LEN) + getResources().getString(R.string.notelist_string_info); //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) { } catch (IllegalArgumentException e) {
e.printStackTrace(); e.printStackTrace();
return; return;
} }
/*
try
{
// 代码区
}
catch(Exception e)
{
// 异常处理
}
*/
mPlayer = new MediaPlayer(); mPlayer = new MediaPlayer();
if (DataUtils.visibleInNoteDatabase(getContentResolver(), mNoteId, Notes.TYPE_NOTE)) { if (DataUtils.visibleInNoteDatabase(getContentResolver(), mNoteId, Notes.TYPE_NOTE)) {
showActionDialog(); showActionDialog();
//弹出对话框
playAlarmSound(); playAlarmSound();
//闹钟提示音激发
} else { } else {
finish(); finish();
//完成闹钟动作
} }
} }
/**
*
* @return true false
*/
private boolean isScreenOn() { private boolean isScreenOn() {
//判断屏幕是否锁屏,调用系统函数判断,最后返回值是布尔类型
PowerManager pm = (PowerManager) getSystemService(Context.POWER_SERVICE); PowerManager pm = (PowerManager) getSystemService(Context.POWER_SERVICE);
return pm.isScreenOn(); return pm.isScreenOn();
} }
/**
*
*/
private void playAlarmSound() { private void playAlarmSound() {
//闹钟提示音激发
Uri url = RingtoneManager.getActualDefaultRingtoneUri(this, RingtoneManager.TYPE_ALARM); 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) { if ((silentModeStreams & (1 << AudioManager.STREAM_ALARM)) != 0) {
mPlayer.setAudioStreamType(silentModeStreams); mPlayer.setAudioStreamType(silentModeStreams);
} else { } else {
mPlayer.setAudioStreamType(AudioManager.STREAM_ALARM); mPlayer.setAudioStreamType(AudioManager.STREAM_ALARM);
} }
try { try {
mPlayer.setDataSource(this, url); mPlayer.setDataSource(this, url);
//方法setDataSource(Context context, Uri uri)
//解释:无返回值,设置多媒体数据来源【根据 Uri】
mPlayer.prepare(); mPlayer.prepare();
//准备同步
mPlayer.setLooping(true); mPlayer.setLooping(true);
//设置是否循环播放
mPlayer.start(); 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(); e.printStackTrace();
} }
} }
/**
*
*/
private void showActionDialog() { private void showActionDialog() {
AlertDialog.Builder dialog = new AlertDialog.Builder(this); 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.setTitle(R.string.app_name);
//为对话框设置标题
dialog.setMessage(mSnippet); dialog.setMessage(mSnippet);
//为对话框设置内容
dialog.setPositiveButton(R.string.notealert_ok, this); dialog.setPositiveButton(R.string.notealert_ok, this);
//给对话框添加"Yes"按钮
if (isScreenOn()) { if (isScreenOn()) {
dialog.setNegativeButton(R.string.notealert_enter, this); dialog.setNegativeButton(R.string.notealert_enter, this);
} }//对话框添加"No"按钮
dialog.show().setOnDismissListener(this); dialog.show().setOnDismissListener(this);
} }
/**
*
* @param dialog
* @param which
*/
@Override
public void onClick(DialogInterface dialog, int which) { public void onClick(DialogInterface dialog, int which) {
switch (which) { switch (which) {
//用which来选择click后下一步的操作
case DialogInterface.BUTTON_NEGATIVE: case DialogInterface.BUTTON_NEGATIVE:
// 取消操作,跳转到便签编辑页面 //这是取消操作
Intent intent = new Intent(this, NoteEditActivity.class); Intent intent = new Intent(this, NoteEditActivity.class);
//实现两个类间的数据传输
intent.setAction(Intent.ACTION_VIEW); intent.setAction(Intent.ACTION_VIEW);
//设置动作属性
intent.putExtra(Intent.EXTRA_UID, mNoteId); intent.putExtra(Intent.EXTRA_UID, mNoteId);
//实现key-value对
//EXTRA_UID为keymNoteId为键
startActivity(intent); startActivity(intent);
//开始动作
break; break;
default: default:
// 确定操作,不做任何处理 //这是确定操作
break; break;
} }
} }
/**
*
* @param dialog
*/
@Override
public void onDismiss(DialogInterface dialog) { public void onDismiss(DialogInterface dialog) {
//忽略
stopAlarmSound(); stopAlarmSound();
//停止闹钟声音
finish(); finish();
//完成该动作
} }
/**
*
*/
private void stopAlarmSound() { private void stopAlarmSound() {
if (mPlayer != null) { if (mPlayer != null) {
mPlayer.stop(); mPlayer.stop();
//停止播放
mPlayer.release(); mPlayer.release();
//释放MediaPlayer对象
mPlayer = null; 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 * 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;
@ -25,21 +27,18 @@ import android.database.Cursor;
import net.micode.notes.data.Notes; import net.micode.notes.data.Notes;
import net.micode.notes.data.Notes.NoteColumns; import net.micode.notes.data.Notes.NoteColumns;
/**
* AlarmInitReceiver
* /
*/
public class AlarmInitReceiver extends BroadcastReceiver { public class AlarmInitReceiver extends BroadcastReceiver {
// 定义查询数据库时要获取的列 // 定义查询数据库时要获取的列
private static final String[] PROJECTION = new String[]{ private static final String [] PROJECTION = new String [] {
NoteColumns.ID, // 笔记的ID NoteColumns.ID, // 笔记的ID
NoteColumns.ALERTED_DATE // 笔记的提醒日期 NoteColumns.ALERTED_DATE // 笔记的提醒日期
}; };
// 定义列的索引以便在Cursor中快速访问 // 定义列的索引以便在Cursor中快速访问
private static final int COLUMN_ID = 0; private static final int COLUMN_ID = 0;
private static final int COLUMN_ALERTED_DATE = 1; private static final int COLUMN_ALERTED_DATE = 1;
@Override @Override
public void onReceive(Context context, Intent intent) { 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, Cursor c = context.getContentResolver().query(Notes.CONTENT_NOTE_URI,
PROJECTION, PROJECTION,
NoteColumns.ALERTED_DATE + ">? AND " + NoteColumns.TYPE + "=" + Notes.TYPE_NOTE, NoteColumns.ALERTED_DATE + ">? AND " + NoteColumns.TYPE + "=" + Notes.TYPE_NOTE,
new String[]{String.valueOf(currentDate)}, // 查询参数,这里只有当前日期 new String[] { String.valueOf(currentDate) }, // 查询参数,这里只有当前日期
null); // 不需要排序 null); // 不需要排序
// 检查Cursor是否为null // 检查Cursor是否为null
@ -75,11 +74,11 @@ public class AlarmInitReceiver extends BroadcastReceiver {
PendingIntent pendingIntent = PendingIntent.getBroadcast(context, 0, sender, 0); PendingIntent pendingIntent = PendingIntent.getBroadcast(context, 0, sender, 0);
// 获取AlarmManager服务 // 获取AlarmManager服务
AlarmManager alarmManager = (AlarmManager) context.getSystemService(Context.ALARM_SERVICE); AlarmManager alermManager = (AlarmManager) context.getSystemService(Context.ALARM_SERVICE);
// 设置一个闹钟当到达提醒日期时触发PendingIntent即发送广播给AlarmReceiver // 设置一个闹钟当到达提醒日期时触发PendingIntent即发送广播给AlarmReceiver
// AlarmManager.RTC_WAKEUP表示在指定时间唤醒设备并发送广播 // AlarmManager.RTC_WAKEUP表示在指定时间唤醒设备并发送广播
alarmManager.set(AlarmManager.RTC_WAKEUP, alertDate, pendingIntent); alermManager.set(AlarmManager.RTC_WAKEUP, alertDate, pendingIntent);
} while (c.moveToNext()); // 移动到下一条记录,继续循环 } 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 * 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.Context; // 提供应用环境信息的类
import android.content.Intent; // 用于组件间通信的消息传递对象 import android.content.Intent; // 用于组件间通信的消息传递对象
/** // 定义一个公开类AlarmReceiver它继承自BroadcastReceiver
* AlarmReceiver 广
* AlarmAlertActivity
*/
public class AlarmReceiver extends BroadcastReceiver { public class AlarmReceiver extends BroadcastReceiver {
/** // 重写onReceive方法当接收到广播时调用此方法
* 广
* AlarmAlertActivity
*
* @param context
* @param intent 广 Intent
*/
@Override @Override
public void onReceive(Context context, Intent intent) { public void onReceive(Context context, Intent intent) {
// 设置 intent 的目标组件为 AlarmAlertActivity // 设置intent的目标组件为AlarmAlertActivity类
// 这意味着当这个广播接收器接收到广播时它希望启动AlarmAlertActivity活动
intent.setClass(context, AlarmAlertActivity.class); intent.setClass(context, AlarmAlertActivity.class);
// 添加 FLAG_ACTIVITY_NEW_TASK 标志,确保活动作为新的任务启动 // 为intent添加FLAG_ACTIVITY_NEW_TASK标志
// 这通常用于从非活动上下文中启动活动时,确保活动作为新的任务启动
intent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK); intent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
// 启动 AlarmAlertActivity 活动 // 使用context对象调用startActivity方法来启动由intent指定的活动
// 由于intent已经被设置为启动AlarmAlertActivity这行代码的作用就是启动AlarmAlertActivity活动
context.startActivity(intent); 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 * 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;
// 导入所需的 Java Android // 导入所需的Java和Android类
import java.text.DateFormatSymbols; import java.text.DateFormatSymbols;
import java.util.Calendar; import java.util.Calendar;
import net.micode.notes.R; import net.micode.notes.R;
import android.content.Context; import android.content.Context;
import android.text.format.DateFormat; import android.text.format.DateFormat;
import android.view.View; import android.view.View;
import android.widget.FrameLayout; import android.widget.FrameLayout;
import android.widget.NumberPicker; import android.widget.NumberPicker;
// 定义一个名为DateTimePicker的类它继承自FrameLayout
/**
* DateTimePicker
* FrameLayout
* 24 12 AM/PM
*/
public class DateTimePicker extends FrameLayout { public class DateTimePicker extends FrameLayout {
// 默认启用状态 //FrameLayout是布局模板之一
//所有的子元素全部在屏幕的右上方
private static final boolean DEFAULT_ENABLE_STATE = true; private static final boolean DEFAULT_ENABLE_STATE = true;
// 常量定义
private static final int HOURS_IN_HALF_DAY = 12; private static final int HOURS_IN_HALF_DAY = 12;
private static final int HOURS_IN_ALL_DAY = 24; private static final int HOURS_IN_ALL_DAY = 24;
private static final int DAYS_IN_ALL_WEEK = 7; 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 MINUT_SPINNER_MAX_VAL = 59;
private static final int AMPM_SPINNER_MIN_VAL = 0; private static final int AMPM_SPINNER_MIN_VAL = 0;
private static final int AMPM_SPINNER_MAX_VAL = 1; private static final int AMPM_SPINNER_MAX_VAL = 1;
//初始化控件
// 初始化控件
private final NumberPicker mDateSpinner; private final NumberPicker mDateSpinner;
private final NumberPicker mHourSpinner; private final NumberPicker mHourSpinner;
private final NumberPicker mMinuteSpinner; private final NumberPicker mMinuteSpinner;
private final NumberPicker mAmPmSpinner; private final NumberPicker mAmPmSpinner;
//NumberPicker是数字选择器
// NumberPicker 是数字选择器 //这里定义的四个变量全部是在设置闹钟时需要选择的变量(如日期、时、分、上午或者下午)
// 这里定义的四个变量分别用于选择日期、小时、分钟和上午/下午
private Calendar mDate; private Calendar mDate;
//定义了Calendar类型的变量mDate用于操作时间
private String[] mDateDisplayValues = new String[DAYS_IN_ALL_WEEK]; private String[] mDateDisplayValues = new String[DAYS_IN_ALL_WEEK];
private boolean mIsAm; private boolean mIsAm;
private boolean mIs24HourView; private boolean mIs24HourView;
private boolean mIsEnabled = DEFAULT_ENABLE_STATE; private boolean mIsEnabled = DEFAULT_ENABLE_STATE;
private boolean mInitialising; private boolean mInitialising;
private OnDateTimeChangedListener mOnDateTimeChangedListener; private OnDateTimeChangedListener mOnDateTimeChangedListener;
// 监听器定义
private NumberPicker.OnValueChangeListener mOnDateChangedListener = new NumberPicker.OnValueChangeListener() { private NumberPicker.OnValueChangeListener mOnDateChangedListener = new NumberPicker.OnValueChangeListener() {
@Override @Override
public void onValueChange(NumberPicker picker, int oldVal, int newVal) { public void onValueChange(NumberPicker picker, int oldVal, int newVal) {
@ -77,41 +77,49 @@ public class DateTimePicker extends FrameLayout {
updateDateControl(); updateDateControl();
onDateTimeChanged(); onDateTimeChanged();
} }
}; };//OnValueChangeListener这是时间改变监听器这里主要是对日期的监听
//将现在日期的值传递给mDateupdateDateControl是同步操作
private NumberPicker.OnValueChangeListener mOnHourChangedListener = new NumberPicker.OnValueChangeListener() { private NumberPicker.OnValueChangeListener mOnHourChangedListener = new NumberPicker.OnValueChangeListener() {
@Override //这里是对 小时Hour 的监听
@Override
public void onValueChange(NumberPicker picker, int oldVal, int newVal) { public void onValueChange(NumberPicker picker, int oldVal, int newVal) {
boolean isDateChanged = false; boolean isDateChanged = false;
Calendar cal = Calendar.getInstance(); Calendar cal = Calendar.getInstance();
//声明一个Calendar的变量cal便于后续的操作
if (!mIs24HourView) { if (!mIs24HourView) {
if (!mIsAm && oldVal == HOURS_IN_HALF_DAY - 1 && newVal == HOURS_IN_HALF_DAY) { if (!mIsAm && oldVal == HOURS_IN_HALF_DAY - 1 && newVal == HOURS_IN_HALF_DAY) {
cal.setTimeInMillis(mDate.getTimeInMillis()); cal.setTimeInMillis(mDate.getTimeInMillis());
cal.add(Calendar.DAY_OF_YEAR, 1); cal.add(Calendar.DAY_OF_YEAR, 1);
isDateChanged = true; isDateChanged = true;
//这里是对于12小时制时晚上11点和12点交替时对日期的更改
} else if (mIsAm && oldVal == HOURS_IN_HALF_DAY && newVal == HOURS_IN_HALF_DAY - 1) { } else if (mIsAm && oldVal == HOURS_IN_HALF_DAY && newVal == HOURS_IN_HALF_DAY - 1) {
cal.setTimeInMillis(mDate.getTimeInMillis()); cal.setTimeInMillis(mDate.getTimeInMillis());
cal.add(Calendar.DAY_OF_YEAR, -1); cal.add(Calendar.DAY_OF_YEAR, -1);
isDateChanged = true; isDateChanged = true;
} }
//这里是对于12小时制时凌晨11点和12点交替时对日期的更改
if (oldVal == HOURS_IN_HALF_DAY - 1 && newVal == HOURS_IN_HALF_DAY || if (oldVal == HOURS_IN_HALF_DAY - 1 && newVal == HOURS_IN_HALF_DAY ||
oldVal == HOURS_IN_HALF_DAY && newVal == HOURS_IN_HALF_DAY - 1) { oldVal == HOURS_IN_HALF_DAY && newVal == HOURS_IN_HALF_DAY - 1) {
mIsAm = !mIsAm; mIsAm = !mIsAm;
updateAmPmControl(); updateAmPmControl();
} }//这里是对于12小时制时中午11点和12点交替时对AM和PM的更改
} else { } else {
if (oldVal == HOURS_IN_ALL_DAY - 1 && newVal == 0) { if (oldVal == HOURS_IN_ALL_DAY - 1 && newVal == 0) {
cal.setTimeInMillis(mDate.getTimeInMillis()); cal.setTimeInMillis(mDate.getTimeInMillis());
cal.add(Calendar.DAY_OF_YEAR, 1); cal.add(Calendar.DAY_OF_YEAR, 1);
isDateChanged = true; isDateChanged = true;
//这里是对于24小时制时晚上11点和12点交替时对日期的更改
} else if (oldVal == 0 && newVal == HOURS_IN_ALL_DAY - 1) { } else if (oldVal == 0 && newVal == HOURS_IN_ALL_DAY - 1) {
cal.setTimeInMillis(mDate.getTimeInMillis()); cal.setTimeInMillis(mDate.getTimeInMillis());
cal.add(Calendar.DAY_OF_YEAR, -1); cal.add(Calendar.DAY_OF_YEAR, -1);
isDateChanged = true; isDateChanged = true;
} }
} } //这里是对于12小时制时凌晨11点和12点交替时对日期的更改
int newHour = mHourSpinner.getValue() % HOURS_IN_HALF_DAY + (mIsAm ? 0 : HOURS_IN_HALF_DAY); int newHour = mHourSpinner.getValue() % HOURS_IN_HALF_DAY + (mIsAm ? 0 : HOURS_IN_HALF_DAY);
//通过数字选择器对newHour的赋值
mDate.set(Calendar.HOUR_OF_DAY, newHour); mDate.set(Calendar.HOUR_OF_DAY, newHour);
//通过set函数将新的Hour值传给mDate
onDateTimeChanged(); onDateTimeChanged();
if (isDateChanged) { if (isDateChanged) {
setCurrentYear(cal.get(Calendar.YEAR)); setCurrentYear(cal.get(Calendar.YEAR));
@ -120,18 +128,22 @@ public class DateTimePicker extends FrameLayout {
} }
} }
}; };
private NumberPicker.OnValueChangeListener mOnMinuteChangedListener = new NumberPicker.OnValueChangeListener() { private NumberPicker.OnValueChangeListener mOnMinuteChangedListener = new NumberPicker.OnValueChangeListener() {
@Override @Override
//这里是对 分钟Minute改变的监听
public void onValueChange(NumberPicker picker, int oldVal, int newVal) { public void onValueChange(NumberPicker picker, int oldVal, int newVal) {
int minValue = mMinuteSpinner.getMinValue(); int minValue = mMinuteSpinner.getMinValue();
int maxValue = mMinuteSpinner.getMaxValue(); int maxValue = mMinuteSpinner.getMaxValue();
int offset = 0; int offset = 0;
//设置offset作为小时改变的一个记录数据
if (oldVal == maxValue && newVal == minValue) { if (oldVal == maxValue && newVal == minValue) {
offset += 1; offset += 1;
} else if (oldVal == minValue && newVal == maxValue) { } else if (oldVal == minValue && newVal == maxValue) {
offset -= 1; offset -= 1;
} }
//如果原值为59新值为0则offset加1
//如果原值为0新值为59则offset减1
if (offset != 0) { if (offset != 0) {
mDate.add(Calendar.HOUR_OF_DAY, offset); mDate.add(Calendar.HOUR_OF_DAY, offset);
mHourSpinner.setValue(getCurrentHour()); mHourSpinner.setValue(getCurrentHour());
@ -149,9 +161,10 @@ public class DateTimePicker extends FrameLayout {
onDateTimeChanged(); onDateTimeChanged();
} }
}; };
private NumberPicker.OnValueChangeListener mOnAmPmChangedListener = new NumberPicker.OnValueChangeListener() { private NumberPicker.OnValueChangeListener mOnAmPmChangedListener = new NumberPicker.OnValueChangeListener() {
@Override //对AM和PM的监听
@Override
public void onValueChange(NumberPicker picker, int oldVal, int newVal) { public void onValueChange(NumberPicker picker, int oldVal, int newVal) {
mIsAm = !mIsAm; mIsAm = !mIsAm;
if (mIsAm) { if (mIsAm) {
@ -163,84 +176,66 @@ public class DateTimePicker extends FrameLayout {
onDateTimeChanged(); onDateTimeChanged();
} }
}; };
/**
*
*/
public interface OnDateTimeChangedListener { 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) { public DateTimePicker(Context context) {
this(context, System.currentTimeMillis()); this(context, System.currentTimeMillis());
} }//通过对数据库的访问,获取当前的系统时间
/**
* DateTimePicker
* @param context
* @param date
*/
public DateTimePicker(Context context, long date) { public DateTimePicker(Context context, long date) {
this(context, date, DateFormat.is24HourFormat(context)); this(context, date, DateFormat.is24HourFormat(context));
} }//上面函数的得到的是一个天文数字1970至今的秒数需要DateFormat将其变得有意义
/**
* DateTimePicker
* @param context
* @param date
* @param is24HourView 24
*/
public DateTimePicker(Context context, long date, boolean is24HourView) { public DateTimePicker(Context context, long date, boolean is24HourView) {
super(context); super(context);
//获取系统时间
mDate = Calendar.getInstance(); mDate = Calendar.getInstance();
mInitialising = true; mInitialising = true;
mIsAm = getCurrentHourOfDay() >= HOURS_IN_HALF_DAY; mIsAm = getCurrentHourOfDay() >= HOURS_IN_HALF_DAY;
inflate(context, R.layout.datetime_picker, this); inflate(context, R.layout.datetime_picker, this);
//如果当前Activity里用到别的layout比如对话框layout
//还要设置这个layout上的其他组件的内容就必须用inflate()方法先将对话框的layout找出来
//然后再用findViewById()找到它上面的其它组件
mDateSpinner = (NumberPicker) findViewById(R.id.date); mDateSpinner = (NumberPicker) findViewById(R.id.date);
mDateSpinner.setMinValue(DATE_SPINNER_MIN_VAL); mDateSpinner.setMinValue(DATE_SPINNER_MIN_VAL);
mDateSpinner.setMaxValue(DATE_SPINNER_MAX_VAL); mDateSpinner.setMaxValue(DATE_SPINNER_MAX_VAL);
mDateSpinner.setOnValueChangedListener(mOnDateChangedListener); mDateSpinner.setOnValueChangedListener(mOnDateChangedListener);
mHourSpinner = (NumberPicker) findViewById(R.id.hour); mHourSpinner = (NumberPicker) findViewById(R.id.hour);
mHourSpinner.setOnValueChangedListener(mOnHourChangedListener); mHourSpinner.setOnValueChangedListener(mOnHourChangedListener);
mMinuteSpinner = (NumberPicker) findViewById(R.id.minute); mMinuteSpinner = (NumberPicker) findViewById(R.id.minute);
mMinuteSpinner.setMinValue(MINUT_SPINNER_MIN_VAL); mMinuteSpinner.setMinValue(MINUT_SPINNER_MIN_VAL);
mMinuteSpinner.setMaxValue(MINUT_SPINNER_MAX_VAL); mMinuteSpinner.setMaxValue(MINUT_SPINNER_MAX_VAL);
mMinuteSpinner.setOnLongPressUpdateInterval(100); mMinuteSpinner.setOnLongPressUpdateInterval(100);
mMinuteSpinner.setOnValueChangedListener(mOnMinuteChangedListener); mMinuteSpinner.setOnValueChangedListener(mOnMinuteChangedListener);
String[] stringsForAmPm = new DateFormatSymbols().getAmPmStrings(); String[] stringsForAmPm = new DateFormatSymbols().getAmPmStrings();
mAmPmSpinner = (NumberPicker) findViewById mAmPmSpinner = (NumberPicker) findViewById(R.id.amPm);
(R.id.amPm);
mAmPmSpinner.setMinValue(AMPM_SPINNER_MIN_VAL); mAmPmSpinner.setMinValue(AMPM_SPINNER_MIN_VAL);
mAmPmSpinner.setMaxValue(AMPM_SPINNER_MAX_VAL); mAmPmSpinner.setMaxValue(AMPM_SPINNER_MAX_VAL);
mAmPmSpinner.setDisplayedValues(stringsForAmPm); mAmPmSpinner.setDisplayedValues(stringsForAmPm);
mAmPmSpinner.setOnValueChangedListener(mOnAmPmChangedListener); mAmPmSpinner.setOnValueChangedListener(mOnAmPmChangedListener);
// 更新控件到初始状态 // update controls to initial state
updateDateControl(); updateDateControl();
updateHourControl(); updateHourControl();
updateAmPmControl(); updateAmPmControl();
set24HourView(is24HourView); set24HourView(is24HourView);
// 设置当前时间 // set to current time
setCurrentDate(date); setCurrentDate(date);
setEnabled(isEnabled()); setEnabled(isEnabled());
// 设置内容描述 // set the content descriptions
mInitialising = false; mInitialising = false;
} }
/**
* DateTimePicker
* @param enabled
*/
@Override @Override
public void setEnabled(boolean enabled) { public void setEnabled(boolean enabled) {
if (mIsEnabled == enabled) { if (mIsEnabled == enabled) {
@ -253,62 +248,67 @@ public class DateTimePicker extends FrameLayout {
mAmPmSpinner.setEnabled(enabled); mAmPmSpinner.setEnabled(enabled);
mIsEnabled = enabled; mIsEnabled = enabled;
} }
//存在疑问setEnabled的作用
/** //下面的代码通过原程序的注释已经比较清晰,另外可以通过函数名来判断
* DateTimePicker //下面的各函数主要是对上面代码引用到的各函数功能的实现
* @return true false
*/
@Override @Override
public boolean isEnabled() { public boolean isEnabled() {
return mIsEnabled; return mIsEnabled;
} }
/** /**
* * Get the current date in millis
* @return *
* @return the current date in millis
*/ */
public long getCurrentDateInTimeMillis() { public long getCurrentDateInTimeMillis() {
return mDate.getTimeInMillis(); return mDate.getTimeInMillis();
} }//实现函数——得到当前的秒数
/** /**
* * Set the current date
* @param date *
* @param date The current date in millis
*/ */
public void setCurrentDate(long date) { public void setCurrentDate(long date) {
Calendar cal = Calendar.getInstance(); Calendar cal = Calendar.getInstance();
cal.setTimeInMillis(date); cal.setTimeInMillis(date);
setCurrentDate(cal.get(Calendar.YEAR), cal.get(Calendar.MONTH), cal.get(Calendar.DAY_OF_MONTH), 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)); cal.get(Calendar.HOUR_OF_DAY), cal.get(Calendar.MINUTE));
} }//实现函数功能——设置当前的时间参数是date
/** /**
* * Set the current date
* @param year *
* @param month 0-11 * @param year The current year
* @param dayOfMonth * @param month The current month
* @param hourOfDay 0-23 * @param dayOfMonth The current dayOfMonth
* @param minute * @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); setCurrentYear(year);
setCurrentMonth(month); setCurrentMonth(month);
setCurrentDay(dayOfMonth); setCurrentDay(dayOfMonth);
setCurrentHour(hourOfDay); setCurrentHour(hourOfDay);
setCurrentMinute(minute); setCurrentMinute(minute);
} }//实现函数功能——设置当前的时间,参数是各详细的变量
/** /**
* * Get current year
* @return *
* @return The current year
*/ */
//下面是得到year、month、day等值
public int getCurrentYear() { public int getCurrentYear() {
return mDate.get(Calendar.YEAR); return mDate.get(Calendar.YEAR);
} }
/** /**
* * Set current year
* @param year *
* @param year The current year
*/ */
public void setCurrentYear(int year) { public void setCurrentYear(int year) {
if (!mInitialising && year == getCurrentYear()) { if (!mInitialising && year == getCurrentYear()) {
@ -318,18 +318,20 @@ public class DateTimePicker extends FrameLayout {
updateDateControl(); updateDateControl();
onDateTimeChanged(); onDateTimeChanged();
} }
/** /**
* 0-11 * Get current month in the year
* @return 0-11 *
* @return The current month in the year
*/ */
public int getCurrentMonth() { public int getCurrentMonth() {
return mDate.get(Calendar.MONTH); return mDate.get(Calendar.MONTH);
} }
/** /**
* 0-11 * Set current month in the year
* @param month 0-11 *
* @param month The month in the year
*/ */
public void setCurrentMonth(int month) { public void setCurrentMonth(int month) {
if (!mInitialising && month == getCurrentMonth()) { if (!mInitialising && month == getCurrentMonth()) {
@ -339,18 +341,20 @@ public class DateTimePicker extends FrameLayout {
updateDateControl(); updateDateControl();
onDateTimeChanged(); onDateTimeChanged();
} }
/** /**
* * Get current day of the month
* @return *
* @return The day of the month
*/ */
public int getCurrentDay() { public int getCurrentDay() {
return mDate.get(Calendar.DAY_OF_MONTH); return mDate.get(Calendar.DAY_OF_MONTH);
} }
/** /**
* * Set current day of the month
* @param dayOfMonth *
* @param dayOfMonth The day of the month
*/ */
public void setCurrentDay(int dayOfMonth) { public void setCurrentDay(int dayOfMonth) {
if (!mInitialising && dayOfMonth == getCurrentDay()) { if (!mInitialising && dayOfMonth == getCurrentDay()) {
@ -360,17 +364,17 @@ public class DateTimePicker extends FrameLayout {
updateDateControl(); updateDateControl();
onDateTimeChanged(); onDateTimeChanged();
} }
/** /**
* 0-23 * Get current hour in 24 hour mode, in the range (0~23)
* @return 0-23 * @return The current hour in 24 hour mode
*/ */
public int getCurrentHourOfDay() { public int getCurrentHourOfDay() {
return mDate.get(Calendar.HOUR_OF_DAY); return mDate.get(Calendar.HOUR_OF_DAY);
} }
private int getCurrentHour() { private int getCurrentHour() {
if (mIs24HourView) { if (mIs24HourView){
return getCurrentHourOfDay(); return getCurrentHourOfDay();
} else { } else {
int hour = getCurrentHourOfDay(); int hour = getCurrentHourOfDay();
@ -381,10 +385,11 @@ public class DateTimePicker extends FrameLayout {
} }
} }
} }
/** /**
* 0-23 * Set current hour in 24 hour mode, in the range (0~23)
* @param hourOfDay 0-23 *
* @param hourOfDay
*/ */
public void setCurrentHour(int hourOfDay) { public void setCurrentHour(int hourOfDay) {
if (!mInitialising && hourOfDay == getCurrentHourOfDay()) { if (!mInitialising && hourOfDay == getCurrentHourOfDay()) {
@ -408,18 +413,18 @@ public class DateTimePicker extends FrameLayout {
mHourSpinner.setValue(hourOfDay); mHourSpinner.setValue(hourOfDay);
onDateTimeChanged(); onDateTimeChanged();
} }
/** /**
* * Get currentMinute
* @return *
* @return The Current Minute
*/ */
public int getCurrentMinute() { public int getCurrentMinute() {
return mDate.get(Calendar.MINUTE); return mDate.get(Calendar.MINUTE);
} }
/** /**
* * Set current minute
* @param minute
*/ */
public void setCurrentMinute(int minute) { public void setCurrentMinute(int minute) {
if (!mInitialising && minute == getCurrentMinute()) { if (!mInitialising && minute == getCurrentMinute()) {
@ -429,18 +434,18 @@ public class DateTimePicker extends FrameLayout {
mDate.set(Calendar.MINUTE, minute); mDate.set(Calendar.MINUTE, minute);
onDateTimeChanged(); onDateTimeChanged();
} }
/** /**
* 24 * @return true if this is in 24 hour view else false.
* @return true 24 false 12
*/ */
public boolean is24HourView() { public boolean is24HourView () {
return mIs24HourView; return mIs24HourView;
} }
/** /**
* 24 * Set whether in 24 hour or AM/PM mode.
* @param is24HourView true 24 false 12 *
* @param is24HourView True for 24 hour mode. False for AM/PM mode.
*/ */
public void set24HourView(boolean is24HourView) { public void set24HourView(boolean is24HourView) {
if (mIs24HourView == is24HourView) { if (mIs24HourView == is24HourView) {
@ -453,7 +458,7 @@ public class DateTimePicker extends FrameLayout {
setCurrentHour(hour); setCurrentHour(hour);
updateAmPmControl(); updateAmPmControl();
} }
private void updateDateControl() { private void updateDateControl() {
Calendar cal = Calendar.getInstance(); Calendar cal = Calendar.getInstance();
cal.setTimeInMillis(mDate.getTimeInMillis()); cal.setTimeInMillis(mDate.getTimeInMillis());
@ -466,8 +471,8 @@ public class DateTimePicker extends FrameLayout {
mDateSpinner.setDisplayedValues(mDateDisplayValues); mDateSpinner.setDisplayedValues(mDateDisplayValues);
mDateSpinner.setValue(DAYS_IN_ALL_WEEK / 2); mDateSpinner.setValue(DAYS_IN_ALL_WEEK / 2);
mDateSpinner.invalidate(); mDateSpinner.invalidate();
} }// 对于星期几的算法
private void updateAmPmControl() { private void updateAmPmControl() {
if (mIs24HourView) { if (mIs24HourView) {
mAmPmSpinner.setVisibility(View.GONE); mAmPmSpinner.setVisibility(View.GONE);
@ -475,9 +480,9 @@ public class DateTimePicker extends FrameLayout {
int index = mIsAm ? Calendar.AM : Calendar.PM; int index = mIsAm ? Calendar.AM : Calendar.PM;
mAmPmSpinner.setValue(index); mAmPmSpinner.setValue(index);
mAmPmSpinner.setVisibility(View.VISIBLE); mAmPmSpinner.setVisibility(View.VISIBLE);
} }// 对于上下午操作的算法
} }
private void updateHourControl() { private void updateHourControl() {
if (mIs24HourView) { if (mIs24HourView) {
mHourSpinner.setMinValue(HOUR_SPINNER_MIN_VAL_24_HOUR_VIEW); mHourSpinner.setMinValue(HOUR_SPINNER_MIN_VAL_24_HOUR_VIEW);
@ -485,22 +490,21 @@ public class DateTimePicker extends FrameLayout {
} else { } else {
mHourSpinner.setMinValue(HOUR_SPINNER_MIN_VAL_12_HOUR_VIEW); mHourSpinner.setMinValue(HOUR_SPINNER_MIN_VAL_12_HOUR_VIEW);
mHourSpinner.setMaxValue(HOUR_SPINNER_MAX_VAL_12_HOUR_VIEW); mHourSpinner.setMaxValue(HOUR_SPINNER_MAX_VAL_12_HOUR_VIEW);
} }// 对与小时的算法
} }
/** /**
* * Set the callback that indicates the 'Set' button has been pressed.
* @param callback null * @param callback the callback, if null will do nothing
*/ */
public void setOnDateTimeChangedListener(OnDateTimeChangedListener callback) { public void setOnDateTimeChangedListener(OnDateTimeChangedListener callback) {
mOnDateTimeChangedListener = callback; mOnDateTimeChangedListener = callback;
} }
private void onDateTimeChanged() { private void onDateTimeChanged() {
if (mOnDateTimeChangedListener != null) { if (mOnDateTimeChangedListener != null) {
mOnDateTimeChangedListener.onDateTimeChanged(this, getCurrentYear(), mOnDateTimeChangedListener.onDateTimeChanged(this, getCurrentYear(),
getCurrentMonth(), getCurrentDay(), getCurrentHourOfDay(), getCurrentMinute()); getCurrentMonth(), getCurrentDay(), getCurrentHourOfDay(), getCurrentMinute());
} }
} }
} }

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

@ -1,19 +1,21 @@
/* /*
* (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 * 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.content.Context;
import android.view.Menu; import android.view.Menu;
import android.view.MenuItem; import android.view.MenuItem;
@ -22,62 +24,42 @@ import android.view.View.OnClickListener;
import android.widget.Button; import android.widget.Button;
import android.widget.PopupMenu; import android.widget.PopupMenu;
import android.widget.PopupMenu.OnMenuItemClickListener; import android.widget.PopupMenu.OnMenuItemClickListener;
import net.micode.notes.R; import net.micode.notes.R;
/**
* DropdownMenu
*
*/
public class DropdownMenu { public class DropdownMenu {
private Button mButton; // 按钮控件 private Button mButton;
private PopupMenu mPopupMenu; // 弹出菜单 private PopupMenu mPopupMenu;
private Menu mMenu; // 菜单对象 //声明一个下拉菜单
private Menu mMenu;
/**
* DropdownMenu
* @param context
* @param button
* @param menuId ID
*/
public DropdownMenu(Context context, Button button, int menuId) { public DropdownMenu(Context context, Button button, int menuId) {
mButton = button; mButton = button;
mButton.setBackgroundResource(R.drawable.dropdown_icon); // 设置按钮背景 mButton.setBackgroundResource(R.drawable.dropdown_icon);
//设置这个view的背景
mPopupMenu = new PopupMenu(context, mButton); mPopupMenu = new PopupMenu(context, mButton);
mMenu = mPopupMenu.getMenu(); mMenu = mPopupMenu.getMenu();
mPopupMenu.getMenuInflater().inflate(menuId, mMenu); // 加载菜单资源 mPopupMenu.getMenuInflater().inflate(menuId, mMenu);
//MenuInflater是用来实例化Menu目录下的Menu布局文件
//根据ID来确认menu的内容选项
mButton.setOnClickListener(new OnClickListener() { mButton.setOnClickListener(new OnClickListener() {
@Override
public void onClick(View v) { public void onClick(View v) {
mPopupMenu.show(); // 显示弹出菜单 mPopupMenu.show();
} }
}); });
} }
/**
*
* @param listener
*/
public void setOnDropdownMenuItemClickListener(OnMenuItemClickListener listener) { public void setOnDropdownMenuItemClickListener(OnMenuItemClickListener listener) {
if (mPopupMenu != null) { if (mPopupMenu != null) {
mPopupMenu.setOnMenuItemClickListener(listener); // 设置菜单项点击监听器 mPopupMenu.setOnMenuItemClickListener(listener);
} }//设置菜单的监听
} }
/**
*
* @param id ID
* @return
*/
public MenuItem findItem(int id) { public MenuItem findItem(int id) {
return mMenu.findItem(id); // 查找菜单项 return mMenu.findItem(id);
} }//对于菜单选项的初始化,根据索引搜索菜单需要的选项
/**
*
* @param title
*/
public void setTitle(CharSequence 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 * 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.content.Context;
import android.database.Cursor; import android.database.Cursor;
import android.view.View; import android.view.View;
import android.view.ViewGroup; import android.view.ViewGroup;
import android.widget.CursorAdapter; import android.widget.CursorAdapter;
import android.widget.LinearLayout; import android.widget.LinearLayout;
import android.widget.TextView; import android.widget.TextView;
import net.micode.notes.R; import net.micode.notes.R;
import net.micode.notes.data.Notes; import net.micode.notes.data.Notes;
import net.micode.notes.data.Notes.NoteColumns; import net.micode.notes.data.Notes.NoteColumns;
/**
* FoldersListAdapter public class FoldersListAdapter extends CursorAdapter {
* CursorAdapter ListView //CursorAdapter是Cursor和ListView的接口
*/ //FoldersListAdapter继承了CursorAdapter的类
public class FoldersListAdapter extends CursorAdapter { //主要作用是便签数据库和用户的交互
public static final String[] PROJECTION = { //这里就是用folder文件夹的形式展现给用户
NoteColumns.ID, public static final String [] PROJECTION = {
NoteColumns.SNIPPET NoteColumns.ID,
}; // 查询数据库时需要的列 NoteColumns.SNIPPET
};//调用数据库中便签的ID和片段
public static final int ID_COLUMN = 0;
public static final int NAME_COLUMN = 1; public static final int ID_COLUMN = 0;
public static final int NAME_COLUMN = 1;
/**
* FoldersListAdapter public FoldersListAdapter(Context context, Cursor c) {
* @param context super(context, c);
* @param c // TODO Auto-generated constructor stub
*/ }//数据库操作
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 @Override
* @param parent public void bindView(View view, Context context, Cursor cursor) {
* @return if (view instanceof FolderListItem) {
*/ String folderName = (cursor.getLong(ID_COLUMN) == Notes.ID_ROOT_FOLDER) ? context
@Override .getString(R.string.menu_move_parent_folder) : cursor.getString(NAME_COLUMN);
public View newView(Context context, Cursor cursor, ViewGroup parent) { ((FolderListItem) view).bind(folderName);
return new FolderListItem(context); // 创建一个新的文件夹列表项 }
} }//将各个布局文件绑定起来
/** public String getFolderName(Context context, int position) {
* Cursor cursor = (Cursor) getItem(position);
* @param view return (cursor.getLong(ID_COLUMN) == Notes.ID_ROOT_FOLDER) ? context
* @param context .getString(R.string.menu_move_parent_folder) : cursor.getString(NAME_COLUMN);
* @param cursor }//根据数据库中标签的ID得到标签的各项内容
*/
@Override private class FolderListItem extends LinearLayout {
public void bindView(View view, Context context, Cursor cursor) { private TextView mName;
if (view instanceof FolderListItem) {
String folderName = (cursor.getLong(ID_COLUMN) == Notes.ID_ROOT_FOLDER) ? context public FolderListItem(Context context) {
.getString(R.string.menu_move_parent_folder) : cursor.getString(NAME_COLUMN); super(context);
((FolderListItem) view).bind(folderName); // 绑定文件夹名称 //操作数据库
} inflate(context, R.layout.folder_list_item, this);
} //根据布局文件的名字等信息将其找出来
mName = (TextView) findViewById(R.id.tv_folder_name);
/** }
*
* @param context public void bind(String name) {
* @param position mName.setText(name);
* @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);
}
/**
* FolderListItem
*/
private class FolderListItem extends LinearLayout {
private TextView mName;
/**
* 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); // 获取文件夹名称文本视图
}
/**
*
* @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 * 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.content.Context;
import android.graphics.Rect; import android.graphics.Rect;
import android.text.Layout; import android.text.Layout;
import android.text.Selection; import android.text.Selection;
import android.text.Spanned; import android.text.Spanned;
import android.text.TextUtils; import android.text.TextUtils;
import android.text.style.URLSpan; import android.text.style.URLSpan;
import android.util.AttributeSet; import android.util.AttributeSet;
import android.util.Log; import android.util.Log;
import android.view.ContextMenu; import android.view.ContextMenu;
import android.view.KeyEvent; import android.view.KeyEvent;
import android.view.MenuItem; import android.view.MenuItem;
import android.view.MenuItem.OnMenuItemClickListener; import android.view.MenuItem.OnMenuItemClickListener;
import android.view.MotionEvent; import android.view.MotionEvent;
import android.widget.EditText; import android.widget.EditText;
import net.micode.notes.R; import net.micode.notes.R;
import java.util.HashMap; import java.util.HashMap;
import java.util.Map; import java.util.Map;
/** //继承edittext设置便签设置文本框
* NoteEditText EditText便 public class NoteEditText extends EditText {
* private static final String TAG = "NoteEditText";
*/ private int mIndex;
public class NoteEditText extends EditText { private int mSelectionStartBeforeDelete;
private static final String TAG = "NoteEditText";
private int mIndex; private static final String SCHEME_TEL = "tel:" ;
private int mSelectionStartBeforeDelete; private static final String SCHEME_HTTP = "http:" ;
private static final String SCHEME_EMAIL = "mailto:" ;
private static final String SCHEME_TEL = "tel:";
private static final String SCHEME_HTTP = "http:"; ///建立一个字符和整数的hash表用于链接电话网站还有邮箱
private static final String SCHEME_EMAIL = "mailto:"; private static final Map<String, Integer> sSchemaActionResMap = new HashMap<String, Integer>();
static {
private static final Map<String, Integer> sSchemaActionResMap = new HashMap<String, Integer>(); sSchemaActionResMap.put(SCHEME_TEL, R.string.note_link_tel);
static { sSchemaActionResMap.put(SCHEME_HTTP, R.string.note_link_web);
sSchemaActionResMap.put(SCHEME_TEL, R.string.note_link_tel); sSchemaActionResMap.put(SCHEME_EMAIL, R.string.note_link_email);
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 {
public interface OnTextViewChangeListener { /**
/** * Delete current edit text when {@link KeyEvent#KEYCODE_DEL} happens
* * and the text is null
*/ */
void onEditTextDelete(int index, String text); //处理删除按键时的操作
void onEditTextDelete(int index, String text);
/**
* /**
*/ * Add edit text after current edit text when {@link KeyEvent#KEYCODE_ENTER}
void onEditTextEnter(int index, String text); * happen
*/
/** //处理进入按键时的操作
* void onEditTextEnter(int index, String text);
*/
void onTextChange(int index, boolean hasText); /**
} * Hide or show item option when text change
*/
private OnTextViewChangeListener mOnTextViewChangeListener; void onTextChange(int index, boolean hasText);
}
/**
* NoteEditText private OnTextViewChangeListener mOnTextViewChangeListener;
* @param context
*/ //根据context设置文本
public NoteEditText(Context context) { public NoteEditText(Context context) {
super(context, null); super(context, null);//用super引用父类变量
mIndex = 0; mIndex = 0;
} }
/** //设置当前光标
* public void setIndex(int index) {
* @param index mIndex = index;
*/ }
public void setIndex(int index) {
mIndex = index; //初始化文本修改标记
} public void setOnTextViewChangeListener(OnTextViewChangeListener listener) {
mOnTextViewChangeListener = listener;
/** }
*
* @param listener //AttributeSet 百度了一下是自定义空控件属性,用于维护便签动态变化的属性
*/ //初始化便签
public void setOnTextViewChangeListener(OnTextViewChangeListener listener) { public NoteEditText(Context context, AttributeSet attrs) {
mOnTextViewChangeListener = listener; super(context, attrs, android.R.attr.editTextStyle);
} }
/** // 根据defstyle自动初始化
* NoteEditText public NoteEditText(Context context, AttributeSet attrs, int defStyle) {
* @param context super(context, attrs, defStyle);
* @param attrs // TODO Auto-generated construct or stub
*/ }
public NoteEditText(Context context, AttributeSet attrs) {
super(context, attrs, android.R.attr.editTextStyle); @Override
} //view里的函数处理手机屏幕的所有事件
/*event
/** */
* NoteEditText public boolean onTouchEvent(MotionEvent event) {
* @param context switch (event.getAction()) {
* @param attrs //重写了需要处理屏幕被按下的事件
* @param defStyle case MotionEvent.ACTION_DOWN:
*/ //跟新当前坐标值
public NoteEditText(Context context, AttributeSet attrs, int defStyle) { int x = (int) event.getX();
super(context, attrs, defStyle); int y = (int) event.getY();
} x -= getTotalPaddingLeft();
y -= getTotalPaddingTop();
@Override x += getScrollX();
public boolean onTouchEvent(MotionEvent event) { y += getScrollY();
switch (event.getAction()) {
case MotionEvent.ACTION_DOWN: //用布局控件layout根据x,y的新值设置新的位置
int x = (int) event.getX(); Layout layout = getLayout();
int y = (int) event.getY(); int line = layout.getLineForVertical(y);
x -= getTotalPaddingLeft(); int off = layout.getOffsetForHorizontal(line, x);
y -= getTotalPaddingTop();
x += getScrollX(); //更新光标新的位置
y += getScrollY(); Selection.setSelection(getText(), off);
break;
Layout layout = getLayout(); }
int line = layout.getLineForVertical(y);
int off = layout.getOffsetForHorizontal(line, x); return super.onTouchEvent(event);
}
Selection.setSelection(getText(), off);
break; @Override
} /*
return super.onTouchEvent(event); *
} *
*/
@Override public boolean onKeyDown(int keyCode, KeyEvent event) {
public boolean onKeyDown(int keyCode, KeyEvent event) { switch (keyCode) {
switch (keyCode) { //根据按键的 Unicode 编码值来处理
case KeyEvent.KEYCODE_ENTER: case KeyEvent.KEYCODE_ENTER:
if (mOnTextViewChangeListener != null) { //“进入”按键
return false; if (mOnTextViewChangeListener != null) {
} return false;
break; }
case KeyEvent.KEYCODE_DEL: break;
mSelectionStartBeforeDelete = getSelectionStart(); case KeyEvent.KEYCODE_DEL:
break; //“删除”按键
default: mSelectionStartBeforeDelete = getSelectionStart();
break; break;
} default:
return super.onKeyDown(keyCode, event); break;
} }
//继续执行父类的其他点击事件
@Override return super.onKeyDown(keyCode, event);
public boolean onKeyUp(int keyCode, KeyEvent event) { }
switch (keyCode) {
case KeyEvent.KEYCODE_DEL: @Override
if (mOnTextViewChangeListener != null) { /*
if (0 == mSelectionStartBeforeDelete && mIndex != 0) { *
mOnTextViewChangeListener.onEditTextDelete(mIndex, getText().toString()); *
return true; */
} public boolean onKeyUp(int keyCode, KeyEvent event) {
} else { switch(keyCode) {
Log.d(TAG, "OnTextViewChangeListener was not set"); //根据按键的 Unicode 编码值来处理有删除和进入2种操作
} case KeyEvent.KEYCODE_DEL:
break; if (mOnTextViewChangeListener != null) {
case KeyEvent.KEYCODE_ENTER: //若是被修改过
if (mOnTextViewChangeListener != null) { if (0 == mSelectionStartBeforeDelete && mIndex != 0) {
int selectionStart = getSelectionStart(); //若之前有被修改并且文档不为空
String text = getText().subSequence(selectionStart, length()).toString(); mOnTextViewChangeListener.onEditTextDelete(mIndex, getText().toString());
setText(getText().subSequence(0, selectionStart)); //利用上文OnTextViewChangeListener对KEYCODE_DEL按键情况的删除函数进行删除
mOnTextViewChangeListener.onEditTextEnter(mIndex + 1, text); return true;
} else { }
Log.d(TAG, "OnTextViewChangeListener was not set"); } else {
} Log.d(TAG, "OnTextViewChangeListener was not seted");
break; //其他情况报错,文档的改动监听器并没有建立
default: }
break; break;
} case KeyEvent.KEYCODE_ENTER:
return super.onKeyUp(keyCode, event); //同上也是分为监听器是否建立2种情况
} if (mOnTextViewChangeListener != null) {
int selectionStart = getSelectionStart();
@Override //获取当前位置
protected void onFocusChanged(boolean focused, int direction, Rect previouslyFocusedRect) { String text = getText().subSequence(selectionStart, length()).toString();
if (mOnTextViewChangeListener != null) { //获取当前文本
if (!focused && TextUtils.isEmpty(getText())) { setText(getText().subSequence(0, selectionStart));
mOnTextViewChangeListener.onTextChange(mIndex, false); //根据获取的文本设置当前文本
} else { mOnTextViewChangeListener.onEditTextEnter(mIndex + 1, text);
mOnTextViewChangeListener.onTextChange(mIndex, true); //当{@link KeyEvent#KEYCODE_ENTER}添加新文本
} } else {
} Log.d(TAG, "OnTextViewChangeListener was not seted");
super.onFocusChanged(focused, direction, previouslyFocusedRect); //其他情况报错,文档的改动监听器并没有建立
} }
break;
@Override default:
protected void onCreateContextMenu(ContextMenu menu) { break;
if (getText() instanceof Spanned) { }
int selStart = getSelectionStart(); //继续执行父类的其他按键弹起的事件
int selEnd = getSelectionEnd(); return super.onKeyUp(keyCode, event);
}
int min = Math.min(selStart, selEnd);
int max = Math.max(selStart, selEnd); @Override
/*
final URLSpan[] urls = ((Spanned) getText()).getSpans(min, max, URLSpan.class); *
if (urls.length == 1) { *
int defaultResId = 0; * focusedViewFocusedtruefalse
for (String schema : sSchemaActionResMap.keySet()) { direction
if (urls[0].getURL().indexOf(schema) >= 0) { RectViewnull
defaultResId = sSchemaActionResMap.get(schema); */
break; protected void onFocusChanged(boolean focused, int direction, Rect previouslyFocusedRect) {
} if (mOnTextViewChangeListener != null) {
} //若监听器已经建立
if (!focused && TextUtils.isEmpty(getText())) {
if (defaultResId == 0) { //获取到焦点并且文本不为空
defaultResId = R.string.note_link_other; mOnTextViewChangeListener.onTextChange(mIndex, false);
} //mOnTextViewChangeListener子函数置false隐藏事件选项
} else {
menu.add(0, 0, 0, defaultResId).setOnMenuItemClickListener( mOnTextViewChangeListener.onTextChange(mIndex, true);
new OnMenuItemClickListener() { //mOnTextViewChangeListener子函数置true显示事件选项
public boolean onMenuItemClick(MenuItem item) { }
urls[0].onClick(NoteEditText.this); }
return true; //继续执行父类的其他焦点变化的事件
} super.onFocusChanged(focused, direction, previouslyFocusedRect);
}); }
}
} @Override
super.onCreateContextMenu(menu); /*
} *
} *
*/
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 * 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;
@ -23,12 +25,9 @@ import net.micode.notes.data.Notes;
import net.micode.notes.data.Notes.NoteColumns; import net.micode.notes.data.Notes.NoteColumns;
import net.micode.notes.tool.DataUtils; import net.micode.notes.tool.DataUtils;
/**
* NoteItemData 便
* 便访
*/
public class NoteItemData { public class NoteItemData {
static final String[] PROJECTION = new String[] { static final String [] PROJECTION = new String [] {
NoteColumns.ID, NoteColumns.ID,
NoteColumns.ALERTED_DATE, NoteColumns.ALERTED_DATE,
NoteColumns.BG_COLOR_ID, NoteColumns.BG_COLOR_ID,
@ -44,20 +43,23 @@ public class NoteItemData {
NoteColumns.TOP, NoteColumns.TOP,
}; };
private static final int ID_COLUMN = 0; private static final int ID_COLUMN = 0;
private static final int ALERTED_DATE_COLUMN = 1; private static final int ALERTED_DATE_COLUMN = 1;
private static final int BG_COLOR_ID_COLUMN = 2; private static final int BG_COLOR_ID_COLUMN = 2;
private static final int CREATED_DATE_COLUMN = 3; private static final int CREATED_DATE_COLUMN = 3;
private static final int HAS_ATTACHMENT_COLUMN = 4; private static final int HAS_ATTACHMENT_COLUMN = 4;
private static final int MODIFIED_DATE_COLUMN = 5; private static final int MODIFIED_DATE_COLUMN = 5;
private static final int NOTES_COUNT_COLUMN = 6; private static final int NOTES_COUNT_COLUMN = 6;
private static final int PARENT_ID_COLUMN = 7; private static final int PARENT_ID_COLUMN = 7;
private static final int SNIPPET_COLUMN = 8; private static final int SNIPPET_COLUMN = 8;
private static final int TYPE_COLUMN = 9; private static final int TYPE_COLUMN = 9;
private static final int WIDGET_ID_COLUMN = 10; private static final int WIDGET_ID_COLUMN = 10;
private static final int WIDGET_TYPE_COLUMN = 11; private static final int WIDGET_TYPE_COLUMN = 11;
private static final int TOP_STATE_COLUMN = 12; private static final int TOP_STATE_COLUMN = 12;
/** NotesDatabaseHelper.java
*
* @zhoukexing 2023/12/25 20:22 */
private long mId; private long mId;
private long mAlertDate; private long mAlertDate;
private int mBgColorId; private int mBgColorId;
@ -81,16 +83,19 @@ public class NoteItemData {
private boolean mIsMultiNotesFollowingFolder; private boolean mIsMultiNotesFollowingFolder;
/** /**
* 便 * @method: NoteItemData
* @param context * @description:
* @param cursor * @date: 2023/12/25 19:58
* @author: zhoukexing
* @param: [context, cursor]
* @return:
*/ */
public NoteItemData(Context context, Cursor cursor) { public NoteItemData(Context context, Cursor cursor) { // 把cursor理解为这样一个指针指向一个表格 @zhoukexing 2023/12/25 20:12
mId = cursor.getLong(ID_COLUMN); mId = cursor.getLong(ID_COLUMN); // 可以根据传入的列号获取到表格里对应列的值 @zhoukexing 2023/12/25 20:12
mAlertDate = cursor.getLong(ALERTED_DATE_COLUMN); mAlertDate = cursor.getLong(ALERTED_DATE_COLUMN);
mBgColorId = cursor.getInt(BG_COLOR_ID_COLUMN); mBgColorId = cursor.getInt(BG_COLOR_ID_COLUMN);
mCreatedDate = cursor.getLong(CREATED_DATE_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); mModifiedDate = cursor.getLong(MODIFIED_DATE_COLUMN);
mNotesCount = cursor.getInt(NOTES_COUNT_COLUMN); mNotesCount = cursor.getInt(NOTES_COUNT_COLUMN);
mParentId = cursor.getLong(PARENT_ID_COLUMN); mParentId = cursor.getLong(PARENT_ID_COLUMN);
@ -103,8 +108,9 @@ public class NoteItemData {
mTop = cursor.getInt(TOP_STATE_COLUMN); mTop = cursor.getInt(TOP_STATE_COLUMN);
mPhoneNumber = ""; 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); mPhoneNumber = DataUtils.getCallNumberByNoteId(context.getContentResolver(), mId);
// 根据电话号码锁定联系人名称,若不在联系人里,直接使用电话号码 @zhoukexing 2023/12/25 20:17
if (!TextUtils.isEmpty(mPhoneNumber)) { if (!TextUtils.isEmpty(mPhoneNumber)) {
mName = Contact.getContact(context, mPhoneNumber); mName = Contact.getContact(context, mPhoneNumber);
if (mName == null) { if (mName == null) {
@ -116,16 +122,12 @@ public class NoteItemData {
if (mName == null) { if (mName == null) {
mName = ""; mName = "";
} }
checkPosition(cursor); checkPostion(cursor);
} }
/** private void checkPostion(Cursor cursor) {
* 便 mIsLastItem = cursor.isLast() ? true : false;
* @param cursor mIsFirstItem = cursor.isFirst() ? true : false;
*/
private void checkPosition(Cursor cursor) {
mIsLastItem = cursor.isLast();
mIsFirstItem = cursor.isFirst();
mIsOnlyOneItem = (cursor.getCount() == 1); mIsOnlyOneItem = (cursor.getCount() == 1);
mIsMultiNotesFollowingFolder = false; mIsMultiNotesFollowingFolder = false;
mIsOneNoteFollowingFolder = false; mIsOneNoteFollowingFolder = false;
@ -142,148 +144,101 @@ public class NoteItemData {
} }
} }
if (!cursor.moveToNext()) { 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() { public boolean isOneFollowingFolder() {
return mIsOneNoteFollowingFolder; return mIsOneNoteFollowingFolder;
} }
/**
* 便
* @return true 便false
*/
public boolean isMultiFollowingFolder() { public boolean isMultiFollowingFolder() {
return mIsMultiNotesFollowingFolder; return mIsMultiNotesFollowingFolder;
} }
/**
* 便
* @return true 便false
*/
public boolean isLast() { public boolean isLast() {
return mIsLastItem; return mIsLastItem;
} }
/**
*
* @return
*/
public String getCallName() { public String getCallName() {
return mName; return mName;
} }
/**
* 便
* @return true 便false
*/
public boolean isFirst() { public boolean isFirst() {
return mIsFirstItem; return mIsFirstItem;
} }
/**
* 便
* @return true 便false
*/
public boolean isSingle() { public boolean isSingle() {
return mIsOnlyOneItem; return mIsOnlyOneItem;
} }
/** public int getTop(){
* 便
* @return 便
*/
public int getTop() {
return mTop; return mTop;
} }
/**
* 便 ID
* @return 便 ID
*/
public long getId() { public long getId() {
return mId; return mId;
} }
/**
* 便
* @return 便
*/
public long getAlertDate() { public long getAlertDate() {
return mAlertDate; return mAlertDate;
} }
/**
* 便
* @return 便
*/
public long getCreatedDate() { public long getCreatedDate() {
return mCreatedDate; return mCreatedDate;
} }
/**
* 便
* @return true false
*/
public boolean hasAttachment() { public boolean hasAttachment() {
return mHasAttachment; return mHasAttachment;
} }
/**
* 便
* @return 便
*/
public long getModifiedDate() { public long getModifiedDate() {
return mModifiedDate; return mModifiedDate;
} }
/**
* 便 ID
* @return 便 ID
*/
public int getBgColorId() { public int getBgColorId() {
return mBgColorId; return mBgColorId;
} }
/**
* 便 ID
* @return 便 ID
*/
public long getParentId() { public long getParentId() {
return mParentId; return mParentId;
} }
/**
* 便
* @return 便
*/
public int getNotesCount() { public int getNotesCount() {
return mNotesCount; return mNotesCount;
} }
/** public long getFolderId () {
* 便 ID
* @return 便 ID
*/
public long getFolderId() {
return mParentId; return mParentId;
} }
/**
* 便
* @return 便
*/
public int getType() { public int getType() {
return mType; return mType;
} }
/** public int getWidgetType() {
* 便 return mWidgetType;
* @return 便 }
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 * 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.content.Context;
import android.database.Cursor; import android.database.Cursor;
import android.util.Log; import android.util.Log;
import android.view.View; import android.view.View;
import android.view.ViewGroup; import android.view.ViewGroup;
import android.widget.CursorAdapter; import android.widget.CursorAdapter;
import net.micode.notes.data.Notes;
import net.micode.notes.data.Notes;
import java.util.Collection;
import java.util.HashMap; import java.util.Collection;
import java.util.HashSet; import java.util.HashMap;
import java.util.Iterator; import java.util.HashSet;
import java.util.Iterator;
/**
* NotesListAdapter 便
* CursorAdapter便 ListView /*
*/ * 便CursorAdaptercursorListView
public class NotesListAdapter extends CursorAdapter { * NotesListAdapter便
private static final String TAG = "NotesListAdapter"; */
private Context mContext; public class NotesListAdapter extends CursorAdapter {
private HashMap<Integer, Boolean> mSelectedIndex; private static final String TAG = "NotesListAdapter";
private int mNotesCount; // 便签数 private Context mContext;
private boolean mChoiceMode; // 选择模式标记 private HashMap<Integer, Boolean> mSelectedIndex;
private int mNotesCount; //便签数
/** private boolean mChoiceMode; //选择模式标记
*
*/ /*
public static class AppWidgetAttribute { * widget
public int widgetId; */
public int widgetType; 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>(); // 新建选项下标的哈希表 public NotesListAdapter(Context context) {
mContext = context; super(context, null); //父类对象置空
mNotesCount = 0; mSelectedIndex = new HashMap<Integer, Boolean>(); //新建选项下标的hash表
} mContext = context;
mNotesCount = 0;
@Override }
/**
* @Override
* @param context /*
* @param cursor *
* @param parent * 使NotesListItem
* @return */
*/ public View newView(Context context, Cursor cursor, ViewGroup parent) {
public View newView(Context context, Cursor cursor, ViewGroup parent) { return new NotesListItem(context);
return new NotesListItem(context); }
}
/*
@Override *
/** *
* */
* @param view @Override
* @param context public void bindView(View view, Context context, Cursor cursor) {
* @param cursor if (view instanceof NotesListItem) {
*/ //若view是NotesListItem的一个实例
public void bindView(View view, Context context, Cursor cursor) { NoteItemData itemData = new NoteItemData(context, cursor);
if (view instanceof NotesListItem) { ((NotesListItem) view).bind(context, itemData, mChoiceMode,
NoteItemData itemData = new NoteItemData(context, cursor); isSelectedItem(cursor.getPosition()));
((NotesListItem) view).bind(context, itemData, mChoiceMode, //则新建一个项目选项并且用bind跟将view和鼠标内容便签数据捆绑在一起
isSelectedItem(cursor.getPosition())); }
} }
}
/*
/** *
* *
* @param position */
* @param checked public void setCheckedItem(final int position, final boolean checked) {
*/ mSelectedIndex.put(position, checked);
public void setCheckedItem(final int position, final boolean checked) { //根据定位和是否勾选设置下标
mSelectedIndex.put(position, checked); notifyDataSetChanged();
notifyDataSetChanged(); //在修改后刷新activity
} }
/** /*
* *
* @return true false */
*/ public boolean isInChoiceMode() {
public boolean isInChoiceMode() { return mChoiceMode;
return mChoiceMode; }
}
/*
/** *
* * mode
* @param mode */
*/ public void setChoiceMode(boolean mode) {
public void setChoiceMode(boolean mode) { mSelectedIndex.clear();
mSelectedIndex.clear(); mChoiceMode = mode;
mChoiceMode = mode; }
}
/*
/** *
* *
* @param checked */
*/ public void selectAll(boolean checked) {
public void selectAll(boolean checked) { Cursor cursor = getCursor();
Cursor cursor = getCursor(); //获取光标位置
for (int i = 0; i < getCount(); i++) { for (int i = 0; i < getCount(); i++) {
if (cursor.moveToPosition(i)) { if (cursor.moveToPosition(i)) {
if (NoteItemData.getNoteType(cursor) == Notes.TYPE_NOTE) { if (NoteItemData.getNoteType(cursor) == Notes.TYPE_NOTE) {
setCheckedItem(i, checked); setCheckedItem(i, checked);
} }
} }
} }
} //遍历所有光标可用的位置在判断为便签类型之后勾选单项框
}
/**
* ID /*
* @return ID *
*/ *
public HashSet<Long> getSelectedItemIds() { */
HashSet<Long> itemSet = new HashSet<Long>(); public HashSet<Long> getSelectedItemIds() {
for (Integer position : mSelectedIndex.keySet()) { HashSet<Long> itemSet = new HashSet<Long>();
if (mSelectedIndex.get(position) == true) { //建立hash表
Long id = getItemId(position); for (Integer position : mSelectedIndex.keySet()) {
if (id == Notes.ID_ROOT_FOLDER) { //遍历所有的关键
Log.d(TAG, "Wrong item id, should not happen"); if (mSelectedIndex.get(position) == true) {
} else { //若光标位置可用
itemSet.add(id); Long id = getItemId(position);
} if (id == Notes.ID_ROOT_FOLDER) {
} //原文件不需要添加
} Log.d(TAG, "Wrong item id, should not happen");
return itemSet; } else {
} itemSet.add(id);
}
/** //则将id该下标假如选项集合中
*
* @return }
*/ }
public HashSet<AppWidgetAttribute> getSelectedWidget() {
HashSet<AppWidgetAttribute> itemSet = new HashSet<AppWidgetAttribute>(); return itemSet;
for (Integer position : mSelectedIndex.keySet()) { }
if (mSelectedIndex.get(position) == true) {
Cursor c = (Cursor) getItem(position); /*
if (c != null) { * Widget
AppWidgetAttribute widget = new AppWidgetAttribute(); *
NoteItemData item = new NoteItemData(mContext, c); */
widget.widgetId = item.getWidgetId(); public HashSet<AppWidgetAttribute> getSelectedWidget() {
widget.widgetType = item.getWidgetType(); HashSet<AppWidgetAttribute> itemSet = new HashSet<AppWidgetAttribute>();
itemSet.add(widget); for (Integer position : mSelectedIndex.keySet()) {
} else { if (mSelectedIndex.get(position) == true) {
Log.e(TAG, "Invalid cursor"); Cursor c = (Cursor) getItem(position);
return null; //以上4句和getSelectedItemIds一样不再重复
} if (c != null) {
} //光标位置可用的话就建立新的Widget属性并编辑下标和类型最后添加到选项集中
} AppWidgetAttribute widget = new AppWidgetAttribute();
return itemSet; NoteItemData item = new NoteItemData(mContext, c);
} widget.widgetId = item.getWidgetId();
widget.widgetType = item.getWidgetType();
/** itemSet.add(widget);
* /**
* @return * Don't close cursor here, only the adapter could close it
*/ */
public int getSelectedCount() { } else {
Collection<Boolean> values = mSelectedIndex.values(); Log.e(TAG, "Invalid cursor");
if (null == values) { return null;
return 0; }
} }
Iterator<Boolean> iter = values.iterator(); }
int count = 0; return itemSet;
while (iter.hasNext()) { }
if (true == iter.next()) {
count++; /*
} *
} *
return count; */
} public int getSelectedCount() {
Collection<Boolean> values = mSelectedIndex.values();
/** //首先获取选项下标的值
* if (null == values) {
* @return true false return 0;
*/ }
public boolean isAllSelected() { Iterator<Boolean> iter = values.iterator();
int checkedCount = getSelectedCount(); //初始化叠加器
return (checkedCount != 0 && checkedCount == mNotesCount); int count = 0;
} while (iter.hasNext()) {
if (true == iter.next()) {
/** //若value值为真计数+1
* count++;
* @param position }
* @return true false }
*/ return count;
public boolean isSelectedItem(final int position) { }
if (null == mSelectedIndex.get(position)) {
return false; /*
} *
return mSelectedIndex.get(position); *
} */
public boolean isAllSelected() {
@Override int checkedCount = getSelectedCount();
/** return (checkedCount != 0 && checkedCount == mNotesCount);
* 便 //获取选项数看是否等于便签的个数
*/ }
protected void onContentChanged() {
super.onContentChanged(); /*
calcNotesCount(); *
} *
*/
@Override public boolean isSelectedItem(final int position) {
/** if (null == mSelectedIndex.get(position)) {
* 便 return false;
* @param cursor }
*/ return mSelectedIndex.get(position);
public void changeCursor(Cursor cursor) { }
super.changeCursor(cursor);
calcNotesCount(); @Override
} /*
* activity便
/** *
* 便 */
*/ protected void onContentChanged() {
private void calcNotesCount() { super.onContentChanged();
mNotesCount = 0; //执行基类函数
for (int i = 0; i < getCount(); i++) { calcNotesCount();
Cursor c = (Cursor) getItem(i); }
if (c != null) {
if (NoteItemData.getNoteType(c) == Notes.TYPE_NOTE) { @Override
mNotesCount++; /*
} * activity便
} else { */
Log.e(TAG, "Invalid cursor"); public void changeCursor(Cursor cursor) {
return; 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 * 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.content.Context;
import android.text.format.DateUtils; import android.text.format.DateUtils;
import android.view.View; import android.view.View;
import android.widget.CheckBox; import android.widget.CheckBox;
import android.widget.ImageView; import android.widget.ImageView;
import android.widget.LinearLayout; import android.widget.LinearLayout;
import android.widget.TextView; import android.widget.TextView;
import net.micode.notes.R; import net.micode.notes.R;
import net.micode.notes.data.Notes; import net.micode.notes.data.Notes;
import net.micode.notes.tool.DataUtils; import net.micode.notes.tool.DataUtils;
import net.micode.notes.tool.ResourceParser.NoteItemBgResources; import net.micode.notes.tool.ResourceParser.NoteItemBgResources;
/**
* NotesListItem 便 //创建便签列表项目选项
* LinearLayout便 public class NotesListItem extends LinearLayout {
*/ private ImageView mAlert;//闹钟图片
public class NotesListItem extends LinearLayout { private TextView mTitle; //标题
private ImageView mAlert; // 闹钟图标 private TextView mTime; //时间
private TextView mTitle; // 标题 private TextView mCallName; //
private TextView mTime; // 时间 private NoteItemData mItemData; //标签数据
private TextView mCallName; // 电话联系人名称 private CheckBox mCheckBox; //打钩框
private NoteItemData mItemData; // 便签数据
private CheckBox mCheckBox; // 复选框 /*初始化基本信息*/
public NotesListItem(Context context) {
/** super(context); //super()它的主要作用是调整调用父类构造函数的顺序
* NotesListItem inflate(context, R.layout.note_item, this);//Inflate可用于将一个xml中定义的布局控件找出来,这里的xml是r。layout
* @param context //findViewById用于从contentView中查找指定ID的View转换出来的形式根据需要而定;
*/ mAlert = (ImageView) findViewById(R.id.iv_alert_icon);
public NotesListItem(Context context) { mTitle = (TextView) findViewById(R.id.tv_title);
super(context); mTime = (TextView) findViewById(R.id.tv_time);
inflate(context, R.layout.note_item, this); mCallName = (TextView) findViewById(R.id.tv_name);
mAlert = (ImageView) findViewById(R.id.iv_alert_icon); mCheckBox = (CheckBox) findViewById(android.R.id.checkbox);
mTitle = (TextView) findViewById(R.id.tv_title); }
mTime = (TextView) findViewById(R.id.tv_time); ///根据data的属性对各个控件的属性的控制主要是可见性Visibility内容setText格式setTextAppearance
mCallName = (TextView) findViewById(R.id.tv_name); public void bind(Context context, NoteItemData data, boolean choiceMode, boolean checked) {
mCheckBox = (CheckBox) findViewById(android.R.id.checkbox); if (choiceMode && data.getType() == Notes.TYPE_NOTE) {
} mCheckBox.setVisibility(View.VISIBLE); ///设置可见行为可见
mCheckBox.setChecked(checked); ///格子打钩
/** } else {
* 便 mCheckBox.setVisibility(View.GONE);
* @param context }
* @param data 便
* @param choiceMode mItemData = data;
* @param checked ///设置控件属性一共三种情况由data的id和父id是否与保存到文件夹的id一致来决定
*/ if (data.getId() == Notes.ID_CALL_RECORD_FOLDER) {
public void bind(Context context, NoteItemData data, boolean choiceMode, boolean checked) { mCallName.setVisibility(View.GONE);
if (choiceMode && data.getType() == Notes.TYPE_NOTE) { mAlert.setVisibility(View.VISIBLE);
mCheckBox.setVisibility(View.VISIBLE); //设置该textview的style
mCheckBox.setChecked(checked); mTitle.setTextAppearance(context, R.style.TextAppearancePrimaryItem);
} else { //settext为设置内容
mCheckBox.setVisibility(View.GONE); 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);
mItemData = data; } else if (data.getParentId() == Notes.ID_CALL_RECORD_FOLDER) {
mCallName.setVisibility(View.VISIBLE);
if (data.getId() == Notes.ID_CALL_RECORD_FOLDER) { mCallName.setText(data.getCallName());
mCallName.setVisibility(View.GONE); mTitle.setTextAppearance(context,R.style.TextAppearanceSecondaryItem);
mAlert.setVisibility(View.VISIBLE); mTitle.setText(DataUtils.getFormattedSnippet(data.getSnippet()));
mTitle.setTextAppearance(context, R.style.TextAppearancePrimaryItem); ///关于闹钟的设置
mTitle.setText(context.getString(R.string.call_record_folder_name) if (data.hasAlert()) {
+ context.getString(R.string.format_folder_files_count, data.getNotesCount())); mAlert.setImageResource(R.drawable.clock);//图片来源的设置
mAlert.setImageResource(R.drawable.call_record); mAlert.setVisibility(View.VISIBLE);
} else if (data.getParentId() == Notes.ID_CALL_RECORD_FOLDER) { } else {
mCallName.setVisibility(View.VISIBLE); mAlert.setVisibility(View.GONE);
mCallName.setText(data.getCallName()); }
mTitle.setTextAppearance(context, R.style.TextAppearanceSecondaryItem); } else {
mTitle.setText(DataUtils.getFormattedSnippet(data.getSnippet())); mCallName.setVisibility(View.GONE);
if (data.hasAlert()) { mTitle.setTextAppearance(context, R.style.TextAppearancePrimaryItem);
mAlert.setImageResource(R.drawable.clock); ///设置title格式
mAlert.setVisibility(View.VISIBLE); if (data.getType() == Notes.TYPE_FOLDER) {
} else { mTitle.setText(data.getSnippet()
mAlert.setVisibility(View.GONE); + context.getString(R.string.format_folder_files_count,
} data.getNotesCount()));
} else { mAlert.setVisibility(View.GONE);
mCallName.setVisibility(View.GONE); } else {
mTitle.setTextAppearance(context, R.style.TextAppearancePrimaryItem); mTitle.setText(DataUtils.getFormattedSnippet(data.getSnippet()));
if (data.getType() == Notes.TYPE_FOLDER) { if (data.hasAlert()) {
mTitle.setText(data.getSnippet() mAlert.setImageResource(R.drawable.clock);///设置图片来源
+ context.getString(R.string.format_folder_files_count, mAlert.setVisibility(View.VISIBLE);
data.getNotesCount())); } else {
mAlert.setVisibility(View.GONE); mAlert.setVisibility(View.GONE);
} else { }
mTitle.setText(DataUtils.getFormattedSnippet(data.getSnippet())); }
if (data.hasAlert()) { }
mAlert.setImageResource(R.drawable.clock); ///设置内容获取相关时间从data里编辑的日期中获取
mAlert.setVisibility(View.VISIBLE); mTime. setText(DateUtils.getRelativeTimeSpanString(data.getModifiedDate()));
} else {
mAlert.setVisibility(View.GONE); setBackground(data);
} }
} //根据data的文件属性来设置背景
} private void setBackground(NoteItemData data) {
mTime.setText(DateUtils.getRelativeTimeSpanString(data.getModifiedDate())); int id = data.getBgColorId();
setBackground(data); //若是note型文件则4种情况对于4种不同情况的背景来源
} if (data.getType() == Notes.TYPE_NOTE) {
//单个数据并且只有一个子文件夹
/** if (data.isSingle() || data.isOneFollowingFolder()) {
* 便 setBackgroundResource(NoteItemBgResources.getNoteBgSingleRes(id));
* @param data 便 } else if (data.isLast()) {//是最后一个数据
*/ setBackgroundResource(NoteItemBgResources.getNoteBgLastRes(id));
private void setBackground(NoteItemData data) { } else if (data.isFirst() || data.isMultiFollowingFolder()) {//是一个数据并有多个子文件夹
int id = data.getBgColorId(); setBackgroundResource(NoteItemBgResources.getNoteBgFirstRes(id));
if (data.getType() == Notes.TYPE_NOTE) { } else {
if (data.isSingle() || data.isOneFollowingFolder()) { setBackgroundResource(NoteItemBgResources.getNoteBgNormalRes(id));
setBackgroundResource(NoteItemBgResources.getNoteBgSingleRes(id)); }
} else if (data.isLast()) { } else {
setBackgroundResource(NoteItemBgResources.getNoteBgLastRes(id)); //若不是note直接调用文件夹的背景来源
} else if (data.isFirst() || data.isMultiFollowingFolder()) { setBackgroundResource(NoteItemBgResources.getFolderBgRes());
setBackgroundResource(NoteItemBgResources.getNoteBgFirstRes(id)); }
} else { }
setBackgroundResource(NoteItemBgResources.getNoteBgNormalRes(id)); public NoteItemData getItemData() {
} return mItemData;
} else { }
setBackgroundResource(NoteItemBgResources.getFolderBgRes()); }
}
}
/**
* 便
* @return 便
*/
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 * 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.accounts.Account; import android.accounts.Account;
import android.accounts.AccountManager; import android.accounts.AccountManager;
import android.app.ActionBar; import android.app.ActionBar;
import android.app.AlertDialog; import android.app.AlertDialog;
import android.content.BroadcastReceiver; import android.content.BroadcastReceiver;
import android.content.ContentValues; import android.content.ContentValues;
import android.content.Context; import android.content.Context;
import android.content.DialogInterface; import android.content.DialogInterface;
import android.content.Intent; import android.content.Intent;
import android.content.IntentFilter; import android.content.IntentFilter;
import android.content.SharedPreferences; import android.content.SharedPreferences;
import android.os.Bundle; import android.os.Bundle;
import android.preference.Preference; import android.preference.Preference;
import android.preference.Preference.OnPreferenceClickListener; import android.preference.Preference.OnPreferenceClickListener;
import android.preference.PreferenceActivity; import android.preference.PreferenceActivity;
import android.preference.PreferenceCategory; import android.preference.PreferenceCategory;
import android.text.TextUtils; import android.text.TextUtils;
import android.text.format.DateFormat; import android.text.format.DateFormat;
import android.view.LayoutInflater; import android.view.LayoutInflater;
import android.view.Menu; import android.view.Menu;
import android.view.MenuItem; import android.view.MenuItem;
import android.view.View; import android.view.View;
import android.widget.Button; import android.widget.Button;
import android.widget.TextView; import android.widget.TextView;
import android.widget.Toast; import android.widget.Toast;
import net.micode.notes.R; import net.micode.notes.R;
import net.micode.notes.data.Notes; import net.micode.notes.data.Notes;
import net.micode.notes.data.Notes.NoteColumns; import net.micode.notes.data.Notes.NoteColumns;
import net.micode.notes.gtask.remote.GTaskSyncService; import net.micode.notes.gtask.remote.GTaskSyncService;
/** /*
* NotesPreferenceActivity 便 *NotesPreferenceActivity便
* PreferenceActivity * PreferenceActivityActivity
*/ */
public class NotesPreferenceActivity extends PreferenceActivity { public class NotesPreferenceActivity extends PreferenceActivity {
public static final String PREFERENCE_NAME = "notes_preferences"; 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_SYNC_ACCOUNT_NAME = "pref_key_account_name";
public static final String PREFERENCE_SET_BG_COLOR_KEY = "pref_key_bg_random_appear"; //同步账号
public static final String PREFERENCE_LAST_SYNC_TIME = "pref_last_sync_time";
private static final String PREFERENCE_SYNC_ACCOUNT_KEY = "pref_sync_account_key"; //同步时间
private static final String AUTHORITIES_FILTER_KEY = "authorities"; public static final String PREFERENCE_SET_BG_COLOR_KEY = "pref_key_bg_random_appear";
private PreferenceCategory mAccountCategory; private static final String PREFERENCE_SYNC_ACCOUNT_KEY = "pref_sync_account_key";
private GTaskReceiver mReceiver; //同步密码
private Account[] mOriAccounts; private static final String AUTHORITIES_FILTER_KEY = "authorities";
private boolean mHasAddedAccount; //本地密码
private PreferenceCategory mAccountCategory;
@Override //账户分组
protected void onCreate(Bundle icicle) { private GTaskReceiver mReceiver;
super.onCreate(icicle); //同步任务接收器
getActionBar().setDisplayHomeAsUpEnabled(true); private Account[] mOriAccounts;
addPreferencesFromResource(R.xml.preferences); //账户
mAccountCategory = (PreferenceCategory) findPreference(PREFERENCE_SYNC_ACCOUNT_KEY); private boolean mHasAddedAccount;
mReceiver = new GTaskReceiver(); //账户的hash标记
IntentFilter filter = new IntentFilter();
filter.addAction(GTaskSyncService.GTASK_SERVICE_BROADCAST_NAME); @Override
registerReceiver(mReceiver, filter); /*
*activity
mOriAccounts = null; *Bundle icicle activity
View header = LayoutInflater.from(this).inflate(R.layout.settings_header, null); *
getListView().addHeaderView(header, null, true); */
} protected void onCreate(Bundle icicle) {
//先执行父类的创建函数
@Override super.onCreate(icicle);
protected void onResume() {
super.onResume(); /* using the app icon for navigation */
if (mHasAddedAccount) { getActionBar().setDisplayHomeAsUpEnabled(true);
Account[] accounts = getGoogleAccounts(); //给左上角图标的左边加上一个返回的图标
if (mOriAccounts != null && accounts.length > mOriAccounts.length) {
for (Account accountNew : accounts) { addPreferencesFromResource(R.xml.preferences);
boolean found = false; //添加xml来源并显示 xml
for (Account accountOld : mOriAccounts) { mAccountCategory = (PreferenceCategory) findPreference(PREFERENCE_SYNC_ACCOUNT_KEY);
if (TextUtils.equals(accountOld.name, accountNew.name)) { //根据同步账户关键码来初始化分组
found = true; mReceiver = new GTaskReceiver();
break; IntentFilter filter = new IntentFilter();
} filter.addAction(GTaskSyncService.GTASK_SERVICE_BROADCAST_NAME);
} registerReceiver(mReceiver, filter);
if (!found) { //初始化同步组件
setSyncAccount(accountNew.name);
break; mOriAccounts = null;
} View header = LayoutInflater.from(this).inflate(R.layout.settings_header, null);
} //获取listvivewListView的作用:用于列出所有选择
} getListView().addHeaderView(header, null, true);
} //在listview组件上方添加其他组件
refreshUI(); }
}
@Override
@Override /*
protected void onDestroy() { * activity
if (mReceiver != null) { *
unregisterReceiver(mReceiver); */
} protected void onResume() {
super.onDestroy(); //先执行父类 的交互实现
} super.onResume();
private void loadAccountPreference() { // need to set sync account automatically if user has added a new
mAccountCategory.removeAll(); // account
Preference accountPref = new Preference(this); if (mHasAddedAccount) {
final String defaultAccount = getSyncAccountName(this); //若用户新加了账户则自动设置同步账户
accountPref.setTitle(getString(R.string.preferences_account_title)); Account[] accounts = getGoogleAccounts();
accountPref.setSummary(getString(R.string.preferences_account_summary)); //获取google同步账户
accountPref.setOnPreferenceClickListener(new OnPreferenceClickListener() { if (mOriAccounts != null && accounts.length > mOriAccounts.length) {
public boolean onPreferenceClick(Preference preference) { //若原账户不为空且当前账户有增加
if (!GTaskSyncService.isSyncing()) { for (Account accountNew : accounts) {
if (TextUtils.isEmpty(defaultAccount)) { boolean found = false;
showSelectAccountAlertDialog(); for (Account accountOld : mOriAccounts) {
} else { if (TextUtils.equals(accountOld.name, accountNew.name)) {
showChangeAccountConfirmAlertDialog(); //更新账户
} found = true;
} else { break;
Toast.makeText(NotesPreferenceActivity.this, }
R.string.preferences_toast_cannot_change_account, Toast.LENGTH_SHORT) }
.show(); if (!found) {
} setSyncAccount(accountNew.name);
return true; //若是没有找到旧的账户,那么同步账号中就只添加新账户
} break;
}); }
mAccountCategory.addPreference(accountPref); }
} }
}
private void loadSyncButton() {
Button syncButton = (Button) findViewById(R.id.preference_sync_button); refreshUI();
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() { @Override
public void onClick(View v) { /*
GTaskSyncService.cancelSync(NotesPreferenceActivity.this); * activity
} *
}); */
} else { protected void onDestroy() {
syncButton.setText(getString(R.string.preferences_button_sync_immediately)); if (mReceiver != null) {
syncButton.setOnClickListener(new View.OnClickListener() { unregisterReceiver(mReceiver);
public void onClick(View v) { //注销接收器
GTaskSyncService.startSync(NotesPreferenceActivity.this); }
} super.onDestroy();
}); //执行父类的销毁动作
} }
syncButton.setEnabled(!TextUtils.isEmpty(getSyncAccountName(this)));
/*
if (GTaskSyncService.isSyncing()) { *
lastSyncTimeView.setText(GTaskSyncService.getProgressString()); *
lastSyncTimeView.setVisibility(View.VISIBLE); */
} else { private void loadAccountPreference() {
long lastSyncTime = getLastSyncTime(this); mAccountCategory.removeAll();
if (lastSyncTime != 0) { //销毁所有的分组
lastSyncTimeView.setText(getString(R.string.preferences_last_sync_time, Preference accountPref = new Preference(this);
DateFormat.format(getString(R.string.preferences_last_sync_time_format), //建立首选项
lastSyncTime))); final String defaultAccount = getSyncAccountName(this);
lastSyncTimeView.setVisibility(View.VISIBLE); accountPref.setTitle(getString(R.string.preferences_account_title));
} else { accountPref.setSummary(getString(R.string.preferences_account_summary));
lastSyncTimeView.setVisibility(View.GONE); //设置首选项的大标题和小标题
} accountPref.setOnPreferenceClickListener(new OnPreferenceClickListener() {
} public boolean onPreferenceClick(Preference preference) {
} //建立监听器
if (!GTaskSyncService.isSyncing()) {
private void refreshUI() { if (TextUtils.isEmpty(defaultAccount)) {
loadAccountPreference(); // the first time to set account
loadSyncButton(); //若是第一次建立账户显示选择账户提示对话框
} showSelectAccountAlertDialog();
} else {
private void showSelectAccountAlertDialog() { // if the account has already been set, we need to promp
AlertDialog.Builder dialogBuilder = new AlertDialog.Builder(this); // user about the risk
View titleView = LayoutInflater.from(this).inflate(R.layout.account_dialog_title, null); //若是已经建立则显示修改对话框并进行修改操作
TextView titleTextView = (TextView) titleView.findViewById(R.id.account_dialog_title); showChangeAccountConfirmAlertDialog();
titleTextView.setText(getString(R.string.preferences_dialog_select_account_title)); }
TextView subtitleTextView = (TextView) titleView.findViewById(R.id.account_dialog_subtitle); } else {
subtitleTextView.setText(getString(R.string.preferences_dialog_select_account_tips)); //若在没有同步的情况下则在toast中显示不能修改
dialogBuilder.setCustomTitle(titleView); Toast.makeText(NotesPreferenceActivity.this,
dialogBuilder.setPositiveButton(null, null); R.string.preferences_toast_cannot_change_account, Toast.LENGTH_SHORT)
Account[] accounts = getGoogleAccounts(); .show();
String defAccount = getSyncAccountName(this); }
mOriAccounts = accounts; return true;
mHasAddedAccount = false; }
});
if (accounts.length > 0) {
CharSequence[] items = new CharSequence[accounts.length]; //根据新建首选项编辑新的账户分组
final CharSequence[] itemMapping = items; mAccountCategory.addPreference(accountPref);
int checkedItem = -1; }
int index = 0;
for (Account account : accounts) { /*
if (TextUtils.equals(account.name, defAccount)) { *
checkedItem = index; *
} */
items[index++] = account.name; private void loadSyncButton() {
} Button syncButton = (Button) findViewById(R.id.preference_sync_button);
dialogBuilder.setSingleChoiceItems(items, checkedItem, TextView lastSyncTimeView = (TextView) findViewById(R.id.prefenerece_sync_status_textview);
new DialogInterface.OnClickListener() { //获取同步按钮控件和最终同步时间的的窗口
public void onClick(DialogInterface dialog, int which) { // set button state
setSyncAccount(itemMapping[which].toString()); //设置按钮的状态
dialog.dismiss(); if (GTaskSyncService.isSyncing()) {
refreshUI(); //若是在同步状态下
} syncButton.setText(getString(R.string.preferences_button_sync_cancel));
}); syncButton.setOnClickListener(new View.OnClickListener() {
} public void onClick(View v) {
GTaskSyncService.cancelSync(NotesPreferenceActivity.this);
View addAccountView = LayoutInflater.from(this).inflate(R.layout.add_account_text, null); }
dialogBuilder.setView(addAccountView); });
//设置按钮显示的文本为“取消同步”以及监听器
final AlertDialog dialog = dialogBuilder.show(); } else {
addAccountView.setOnClickListener(new View.OnClickListener() { syncButton.setText(getString(R.string.preferences_button_sync_immediately));
public void onClick(View v) { syncButton.setOnClickListener(new View.OnClickListener() {
mHasAddedAccount = true; public void onClick(View v) {
Intent intent = new Intent("android.settings.ADD_ACCOUNT_SETTINGS"); GTaskSyncService.startSync(NotesPreferenceActivity.this);
intent.putExtra(AUTHORITIES_FILTER_KEY, new String[] { }
"gmail-ls" });
}); //若是不同步则设置按钮显示的文本为“立即同步”以及对应监听器
startActivityForResult(intent, -1); }
dialog.dismiss(); syncButton.setEnabled(!TextUtils.isEmpty(getSyncAccountName(this)));
} //设置按键可用还是不可用
});
} // set last sync time
// 设置最终同步时间
private void showChangeAccountConfirmAlertDialog() { if (GTaskSyncService.isSyncing()) {
AlertDialog.Builder dialogBuilder = new AlertDialog.Builder(this); //若是在同步的情况下
View titleView = LayoutInflater.from(this).inflate(R.layout.account_dialog_title, null); lastSyncTimeView.setText(GTaskSyncService.getProgressString());
TextView titleTextView = (TextView) titleView.findViewById(R.id.account_dialog_title); lastSyncTimeView.setVisibility(View.VISIBLE);
titleTextView.setText(getString(R.string.preferences_dialog_change_account_title, // 根据当前同步服务器设置时间显示框的文本以及可见性
getSyncAccountName(this))); } else {
TextView subtitleTextView = (TextView) titleView.findViewById(R.id.account_dialog_subtitle); //若是非同步情况
subtitleTextView.setText(getString(R.string.preferences_dialog_change_account_warn_msg)); long lastSyncTime = getLastSyncTime(this);
dialogBuilder.setCustomTitle(titleView); if (lastSyncTime != 0) {
CharSequence[] menuItemArray = new CharSequence[] { lastSyncTimeView.setText(getString(R.string.preferences_last_sync_time,
getString(R.string.preferences_menu_change_account), DateFormat.format(getString(R.string.preferences_last_sync_time_format),
getString(R.string.preferences_menu_remove_account), lastSyncTime)));
getString(R.string.preferences_menu_cancel) lastSyncTimeView.setVisibility(View.VISIBLE);
}; //则根据最后同步时间的信息来编辑时间显示框的文本内容和可见性
dialogBuilder.setItems(menuItemArray, new DialogInterface.OnClickListener() { } else {
public void onClick(DialogInterface dialog, int which) { //若时间为空直接设置为不可见状态
if (which == 0) { lastSyncTimeView.setVisibility(View.GONE);
showSelectAccountAlertDialog(); }
} else if (which == 1) { }
removeSyncAccount(); }
refreshUI(); /*
} *
} *
}); */
dialogBuilder.show(); private void refreshUI() {
} loadAccountPreference();
loadSyncButton();
private Account[] getGoogleAccounts() { }
AccountManager accountManager = AccountManager.get(this);
return accountManager.getAccountsByType("com.google"); /*
} *
*
private void setSyncAccount(String account) { */
if (!getSyncAccountName(this).equals(account)) { private void showSelectAccountAlertDialog() {
SharedPreferences settings = getSharedPreferences(PREFERENCE_NAME, Context.MODE_PRIVATE); AlertDialog.Builder dialogBuilder = new AlertDialog.Builder(this);
SharedPreferences.Editor editor = settings.edit(); //创建一个新的对话框
if (account != null) {
editor.putString(PREFERENCE_SYNC_ACCOUNT_NAME, account); View titleView = LayoutInflater.from(this).inflate(R.layout.account_dialog_title, null);
} else { TextView titleTextView = (TextView) titleView.findViewById(R.id.account_dialog_title);
editor.putString(PREFERENCE_SYNC_ACCOUNT_NAME, ""); titleTextView.setText(getString(R.string.preferences_dialog_select_account_title));
} TextView subtitleTextView = (TextView) titleView.findViewById(R.id.account_dialog_subtitle);
editor.commit(); subtitleTextView.setText(getString(R.string.preferences_dialog_select_account_tips));
//设置标题以及子标题的内容
setLastSyncTime(this, 0); dialogBuilder.setCustomTitle(titleView);
dialogBuilder.setPositiveButton(null, null);
new Thread(new Runnable() { //设置对话框的自定义标题建立一个YES的按钮
public void run() { Account[] accounts = getGoogleAccounts();
ContentValues values = new ContentValues(); String defAccount = getSyncAccountName(this);
values.put(NoteColumns.GTASK_ID, ""); //获取同步账户信息
values.put(NoteColumns.SYNC_ID, 0); mOriAccounts = accounts;
getContentResolver().update(Notes.CONTENT_NOTE_URI, values, null, null); mHasAddedAccount = false;
}
}).start(); if (accounts.length > 0) {
//若账户不为空
Toast.makeText(NotesPreferenceActivity.this, CharSequence[] items = new CharSequence[accounts.length];
getString(R.string.preferences_toast_success_set_account, account), final CharSequence[] itemMapping = items;
Toast.LENGTH_SHORT).show(); int checkedItem = -1;
} int index = 0;
} for (Account account : accounts) {
if (TextUtils.equals(account.name, defAccount)) {
private void removeSyncAccount() { checkedItem = index;
SharedPreferences settings = getSharedPreferences(PREFERENCE_NAME, Context.MODE_PRIVATE); //在账户列表中查询到所需账户
SharedPreferences.Editor editor = settings.edit(); }
items[index++] = account.name;
if (settings.contains(PREFERENCE_SYNC_ACCOUNT_NAME)) { }
editor.remove(PREFERENCE_SYNC_ACCOUNT_NAME); dialogBuilder.setSingleChoiceItems(items, checkedItem,
} //在对话框建立一个单选的复选框
if (settings.contains(PREFERENCE_LAST_SYNC_TIME)) { new DialogInterface.OnClickListener() {
editor.remove(PREFERENCE_LAST_SYNC_TIME); public void onClick(DialogInterface dialog, int which) {
} setSyncAccount(itemMapping[which].toString());
editor.commit(); dialog.dismiss();
//取消对话框
new Thread(new Runnable() { refreshUI();
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(); View addAccountView = LayoutInflater.from(this).inflate(R.layout.add_account_text, null);
} dialogBuilder.setView(addAccountView);
//给新加账户对话框设置自定义样式
public static String getSyncAccountName(Context context) {
SharedPreferences settings = context.getSharedPreferences(PREFERENCE_NAME, Context.MODE_PRIVATE); final AlertDialog dialog = dialogBuilder.show();
return settings.getString(PREFERENCE_SYNC_ACCOUNT_NAME, ""); //显示对话框
} addAccountView.setOnClickListener(new View.OnClickListener() {
public void onClick(View v) {
public static void setLastSyncTime(Context context, long time) { mHasAddedAccount = true;
SharedPreferences settings = context.getSharedPreferences(PREFERENCE_NAME, Context.MODE_PRIVATE); //将新加账户的hash置true
SharedPreferences.Editor editor = settings.edit(); Intent intent = new Intent("android.settings.ADD_ACCOUNT_SETTINGS");
editor.putLong(PREFERENCE_LAST_SYNC_TIME, time); //建立网络建立组件
editor.commit(); intent.putExtra(AUTHORITIES_FILTER_KEY, new String[] {
} "gmail-ls"
});
public static long getLastSyncTime(Context context) { startActivityForResult(intent, -1);
SharedPreferences settings = context.getSharedPreferences(PREFERENCE_NAME, Context.MODE_PRIVATE); //跳回上一个选项
return settings.getLong(PREFERENCE_LAST_SYNC_TIME, 0); dialog.dismiss();
} }
});
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)); */
} private void showChangeAccountConfirmAlertDialog() {
} AlertDialog.Builder dialogBuilder = new AlertDialog.Builder(this);
} //创建一个新的对话框
View titleView = LayoutInflater.from(this).inflate(R.layout.account_dialog_title, null);
@Override TextView titleTextView = (TextView) titleView.findViewById(R.id.account_dialog_title);
public boolean onOptionsItemSelected(MenuItem item) { titleTextView.setText(getString(R.string.preferences_dialog_change_account_title,
switch (item.getItemId()) { getSyncAccountName(this)));
case android.R.id.home: TextView subtitleTextView = (TextView) titleView.findViewById(R.id.account_dialog_subtitle);
Intent intent = new Intent(this, NotesListActivity.class); subtitleTextView.setText(getString(R.string.preferences_dialog_change_account_warn_msg));
intent.addFlags(Intent.FLAG_ACTIVITY_CLEAR_TOP); //根据同步修改的账户信息设置标题以及子标题的内容
startActivity(intent); dialogBuilder.setCustomTitle(titleView);
return true; //设置对话框的自定义标题
default: CharSequence[] menuItemArray = new CharSequence[] {
return false; 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;
}
}
}

@ -5,7 +5,7 @@
* you may not use this file except in compliance with the License. * you may not use this file except in compliance with the License.
* You may obtain a copy of the License at * You may obtain a copy of the License at
* *
* http://www.apache.org/licenses/LICENSE-2.0 * http://www.apache.org/licenses/LICENSE-2.0
* *
* Unless required by applicable law or agreed to in writing, software * Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS, * distributed under the License is distributed on an "AS IS" BASIS,
@ -15,7 +15,6 @@
*/ */
package net.micode.notes.widget; package net.micode.notes.widget;
import android.app.PendingIntent; import android.app.PendingIntent;
import android.appwidget.AppWidgetManager; import android.appwidget.AppWidgetManager;
import android.appwidget.AppWidgetProvider; import android.appwidget.AppWidgetProvider;
@ -34,28 +33,32 @@ import net.micode.notes.ui.NoteEditActivity;
import net.micode.notes.ui.NotesListActivity; 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 abstract class NoteWidgetProvider extends AppWidgetProvider {
// 定义查询便签数据库时需要的列 public static final String [] PROJECTION = new String [] {
public static final String[] PROJECTION = new String[]{ NoteColumns.ID,
NoteColumns.ID, // 便签ID NoteColumns.BG_COLOR_ID,
NoteColumns.BG_COLOR_ID, // 背景颜色ID NoteColumns.SNIPPET
NoteColumns.SNIPPET // 便签摘要
}; };
// 定义列的索引 public static final int COLUMN_ID = 0;
public static final int COLUMN_ID = 0; public static final int COLUMN_BG_COLOR_ID = 1;
public static final int COLUMN_BG_COLOR_ID = 1; public static final int COLUMN_SNIPPET = 2;
public static final int COLUMN_SNIPPET = 2;
private static final String TAG = "NoteWidgetProvider"; // 日志标签 private static final String TAG = "NoteWidgetProvider";
/** /**
* * @method onDeleted
* ID便ID * @description Widget
* @param context * @date: 2024-12-18 21:18
* @param appWidgetIds ID * @return void
*/ */
@Override @Override
public void onDeleted(Context context, int[] appWidgetIds) { public void onDeleted(Context context, int[] appWidgetIds) {
@ -65,61 +68,48 @@ public abstract class NoteWidgetProvider extends AppWidgetProvider {
context.getContentResolver().update(Notes.CONTENT_NOTE_URI, context.getContentResolver().update(Notes.CONTENT_NOTE_URI,
values, values,
NoteColumns.WIDGET_ID + "=?", 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) { private Cursor getNoteWidgetInfo(Context context, int widgetId) {
return context.getContentResolver().query(Notes.CONTENT_NOTE_URI, return context.getContentResolver().query(Notes.CONTENT_NOTE_URI,
PROJECTION, PROJECTION,
NoteColumns.WIDGET_ID + "=? AND " + NoteColumns.PARENT_ID + "<>?", 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); null);
} }
/** /**
* * @method update
* @param context * @description Widget
* @param appWidgetManager * @date: 2024-12-18 21:11
* @param appWidgetIds ID * @return void
*/ */
protected void update(Context context, AppWidgetManager appWidgetManager, int[] appWidgetIds) { protected void update(Context context, AppWidgetManager appWidgetManager, int[] appWidgetIds) {
update(context, appWidgetManager, appWidgetIds, false); update(context, appWidgetManager, appWidgetIds, false);
} }
/**
*
* @param context
* @param appWidgetManager
* @param appWidgetIds ID
* @param privacyMode
*/
private void update(Context context, AppWidgetManager appWidgetManager, int[] appWidgetIds, private void update(Context context, AppWidgetManager appWidgetManager, int[] appWidgetIds,
boolean privacyMode) { boolean privacyMode) {
for (int i = 0; i < appWidgetIds.length; i++) { for (int i = 0; i < appWidgetIds.length; i++) {
if (appWidgetIds[i] != AppWidgetManager.INVALID_APPWIDGET_ID) { if (appWidgetIds[i] != AppWidgetManager.INVALID_APPWIDGET_ID) {
int bgId = ResourceParser.getDefaultBgId(context); // 获取默认背景ID int bgId = ResourceParser.getDefaultBgId(context);
String snippet = ""; // 便签摘要 String snippet = "";
Intent intent = new Intent(context, NoteEditActivity.class); // 创建意图 Intent intent = new Intent(context, NoteEditActivity.class);
intent.setFlags(Intent.FLAG_ACTIVITY_SINGLE_TOP); intent.setFlags(Intent.FLAG_ACTIVITY_SINGLE_TOP);
intent.putExtra(Notes.INTENT_EXTRA_WIDGET_ID, appWidgetIds[i]); intent.putExtra(Notes.INTENT_EXTRA_WIDGET_ID, appWidgetIds[i]);
intent.putExtra(Notes.INTENT_EXTRA_WIDGET_TYPE, getWidgetType()); 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 != null && c.moveToFirst()) {
if (c.getCount() > 1) { 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(); c.close();
return; return;
} }
snippet = c.getString(COLUMN_SNIPPET); // 获取便签摘要 snippet = c.getString(COLUMN_SNIPPET);
bgId = c.getInt(COLUMN_BG_COLOR_ID); // 获取背景ID bgId = c.getInt(COLUMN_BG_COLOR_ID);
intent.putExtra(Intent.EXTRA_UID, c.getLong(COLUMN_ID)); intent.putExtra(Intent.EXTRA_UID, c.getLong(COLUMN_ID));
intent.setAction(Intent.ACTION_VIEW); intent.setAction(Intent.ACTION_VIEW);
} else { } else {
@ -135,7 +125,7 @@ public abstract class NoteWidgetProvider extends AppWidgetProvider {
rv.setImageViewResource(R.id.widget_bg_image, getBgResourceId(bgId)); rv.setImageViewResource(R.id.widget_bg_image, getBgResourceId(bgId));
intent.putExtra(Notes.INTENT_EXTRA_BACKGROUND_ID, bgId); intent.putExtra(Notes.INTENT_EXTRA_BACKGROUND_ID, bgId);
/** /**
* 宿PendingIntent * Generate the pending intent to start host for the widget
*/ */
PendingIntent pendingIntent = null; PendingIntent pendingIntent = null;
if (privacyMode) { if (privacyMode) {
@ -155,66 +145,9 @@ public abstract class NoteWidgetProvider extends AppWidgetProvider {
} }
} }
/**
* ID
* @param bgId ID
* @return ID
*/
protected abstract int getBgResourceId(int bgId); protected abstract int getBgResourceId(int bgId);
/**
* ID
* @return ID
*/
protected abstract int getLayoutId(); protected abstract int getLayoutId();
/**
*
* @return
*/
protected abstract int getWidgetType(); 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
*/

@ -5,7 +5,7 @@
* you may not use this file except in compliance with the License. * you may not use this file except in compliance with the License.
* You may obtain a copy of the License at * You may obtain a copy of the License at
* *
* http://www.apache.org/licenses/LICENSE-2.0 * http://www.apache.org/licenses/LICENSE-2.0
* *
* Unless required by applicable law or agreed to in writing, software * Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS, * distributed under the License is distributed on an "AS IS" BASIS,
@ -23,76 +23,25 @@ import net.micode.notes.R;
import net.micode.notes.data.Notes; import net.micode.notes.data.Notes;
import net.micode.notes.tool.ResourceParser; import net.micode.notes.tool.ResourceParser;
/**
* NoteWidgetProvider_2x NoteWidgetProvider 2x 便
*
*/
public class NoteWidgetProvider_2x extends NoteWidgetProvider { public class NoteWidgetProvider_2x extends NoteWidgetProvider {
/**
*
* update
* @param context
* @param appWidgetManager
* @param appWidgetIds ID
*/
@Override @Override
public void onUpdate(Context context, AppWidgetManager appWidgetManager, int[] appWidgetIds) { public void onUpdate(Context context, AppWidgetManager appWidgetManager, int[] appWidgetIds) {
super.update(context, appWidgetManager, appWidgetIds); super.update(context, appWidgetManager, appWidgetIds);
} }
/**
* ID
* @return ID
*/
@Override @Override
protected int getLayoutId() { protected int getLayoutId() {
return R.layout.widget_2x; // 返回2x小部件的布局资源ID return R.layout.widget_2x;
} }
/**
* IDID
* @param bgId ID
* @return ID
*/
@Override @Override
protected int getBgResourceId(int bgId) { protected int getBgResourceId(int bgId) {
return ResourceParser.WidgetBgResources.getWidget2xBgResource(bgId); // 获取2x小部件的背景资源ID return ResourceParser.WidgetBgResources.getWidget2xBgResource(bgId);
} }
/**
*
* @return
*/
@Override @Override
protected int getWidgetType() { 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
*/

@ -5,7 +5,7 @@
* you may not use this file except in compliance with the License. * you may not use this file except in compliance with the License.
* You may obtain a copy of the License at * You may obtain a copy of the License at
* *
* http://www.apache.org/licenses/LICENSE-2.0 * http://www.apache.org/licenses/LICENSE-2.0
* *
* Unless required by applicable law or agreed to in writing, software * Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS, * distributed under the License is distributed on an "AS IS" BASIS,
@ -23,76 +23,24 @@ import net.micode.notes.R;
import net.micode.notes.data.Notes; import net.micode.notes.data.Notes;
import net.micode.notes.tool.ResourceParser; import net.micode.notes.tool.ResourceParser;
/**
* NoteWidgetProvider_4x NoteWidgetProvider 4x 便
*
*/
public class NoteWidgetProvider_4x extends NoteWidgetProvider { public class NoteWidgetProvider_4x extends NoteWidgetProvider {
/**
*
* update
* @param context
* @param appWidgetManager
* @param appWidgetIds ID
*/
@Override @Override
public void onUpdate(Context context, AppWidgetManager appWidgetManager, int[] appWidgetIds) { public void onUpdate(Context context, AppWidgetManager appWidgetManager, int[] appWidgetIds) {
super.update(context, appWidgetManager, appWidgetIds); super.update(context, appWidgetManager, appWidgetIds);
} }
/**
* ID
* @return ID
*/
@Override
protected int getLayoutId() { protected int getLayoutId() {
return R.layout.widget_4x; // 返回4x小部件的布局资源ID return R.layout.widget_4x;
} }
/**
* IDID
* @param bgId ID
* @return ID
*/
@Override @Override
protected int getBgResourceId(int bgId) { protected int getBgResourceId(int bgId) {
return ResourceParser.WidgetBgResources.getWidget4xBgResource(bgId); // 获取4x小部件的背景资源ID return ResourceParser.WidgetBgResources.getWidget4xBgResource(bgId);
} }
/**
*
* @return
*/
@Override @Override
protected int getWidgetType() { 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