diff --git a/README b/README new file mode 100644 index 0000000..fc0d824 --- /dev/null +++ b/README @@ -0,0 +1,23 @@ +[中文] + +1. MiCode便签是小米便签的社区开源版,由MIUI团队(www.miui.com) 发起并贡献第一批代码,遵循NOTICE文件所描述的开源协议, + 今后为MiCode社区(www.micode.net) 拥有,并由社区发布和维护。 + +2. Bug反馈和跟踪,请访问Github, + https://github.com/MiCode/Notes/issues?sort=created&direction=desc&state=open + +3. 功能建议和综合讨论,请访问MiCode, + http://micode.net/forum.php?mod=forumdisplay&fid=38 + + +[English] + +1. MiCode Notes is open source edition of XM notepad, it's first initiated and sponsored by MIUI team (www.miui.com). + It's opened under license described by NOTICE file. It's owned by the MiCode community (www.micode.net). In future, + the MiCode community will release and maintain this project. + +2. Regarding issue tracking, please visit Github, + https://github.com/MiCode/Notes/issues?sort=created&direction=desc&state=open + +3. Regarding feature request and general discussion, please visit Micode forum, + http://micode.net/forum.php?mod=forumdisplay&fid=38 diff --git a/doc/01_江一阳_泛读报告.docx b/doc/01_江一阳_泛读报告.docx deleted file mode 100644 index 33bf212..0000000 Binary files a/doc/01_江一阳_泛读报告.docx and /dev/null differ diff --git a/doc/02_江一阳_质量分析报告.docx b/doc/02_江一阳_质量分析报告.docx deleted file mode 100644 index 9c78f43..0000000 Binary files a/doc/02_江一阳_质量分析报告.docx and /dev/null differ diff --git a/doc/03_江一阳_维护分析报告.docx b/doc/03_江一阳_维护分析报告.docx deleted file mode 100644 index 8db8fd2..0000000 Binary files a/doc/03_江一阳_维护分析报告.docx and /dev/null differ diff --git a/other/06_江一阳_代码标注/data/Contact.java b/doc/data/Contact.java similarity index 94% rename from other/06_江一阳_代码标注/data/Contact.java rename to doc/data/Contact.java index eb48580..7bd895c 100644 --- a/other/06_江一阳_代码标注/data/Contact.java +++ b/doc/data/Contact.java @@ -86,4 +86,4 @@ public class Contact { } } } -/* 这个类主要用于根据电话号码查询联系人名称。在查询过程中,会将查询到的联系人名称缓存在一个HashMap中,以便于下次查询时直接从缓存中获取。*/ \ No newline at end of file +/*这个类主要用于根据电话号码查询联系人名称。在查询过程中,会将查询到的联系人名称缓存在一个HashMap中,以便于下次查询时直接从缓存中获取。*/ \ No newline at end of file diff --git a/other/06_江一阳_代码标注/data/Notes.java b/doc/data/Notes.java similarity index 93% rename from other/06_江一阳_代码标注/data/Notes.java rename to doc/data/Notes.java index ef3b81f..f240604 100644 --- a/other/06_江一阳_代码标注/data/Notes.java +++ b/doc/data/Notes.java @@ -17,49 +17,50 @@ package net.micode.notes.data; import android.net.Uri; -// 定义“笔记”类 public class Notes { - public static final String AUTHORITY = "micode_notes"; public static final String TAG = "Notes"; - // 定义笔记类型常量 public static final int TYPE_NOTE = 0; public static final int TYPE_FOLDER = 1; public static final int TYPE_SYSTEM = 2; /** - * 以下ID为系统文件夹的标识符 - * {@link Notes#ID_ROOT_FOLDER } 默认文件夹 - * {@link Notes#ID_TEMPARAY_FOLDER } 用于没有文件夹的笔记 - * {@link Notes#ID_CALL_RECORD_FOLDER} 存储通话记录 + * 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 ID_ROOT_FOLDER = 0; public static final int ID_TEMPARAY_FOLDER = -1; public static final int ID_CALL_RECORD_FOLDER = -2; public static final int ID_TRASH_FOLER = -3; - // 定义意图(extra)的键 + public static final String INTENT_EXTRA_ALERT_DATE = "net.micode.notes.alert_date"; public static final String INTENT_EXTRA_BACKGROUND_ID = "net.micode.notes.background_color_id"; public static final String INTENT_EXTRA_WIDGET_ID = "net.micode.notes.widget_id"; public static final String INTENT_EXTRA_WIDGET_TYPE = "net.micode.notes.widget_type"; public static final String INTENT_EXTRA_FOLDER_ID = "net.micode.notes.folder_id"; public static final String INTENT_EXTRA_CALL_DATE = "net.micode.notes.call_date"; - // 定义小部件的类型常量 + public static final int TYPE_WIDGET_INVALIDE = -1; public static final int TYPE_WIDGET_2X = 0; public static final int TYPE_WIDGET_4X = 1; - // 定义数据常量 + public static class DataConstants { public static final String NOTE = TextNote.CONTENT_ITEM_TYPE; public static final String CALL_NOTE = CallNote.CONTENT_ITEM_TYPE; } - // 定义笔记的uri,可用于查询所有笔记和文件夹 + /** + * Uri to query all notes and folders + */ public static final Uri CONTENT_NOTE_URI = Uri.parse("content://" + AUTHORITY + "/note"); - // 定义数据的uri + /** + * Uri to query data + */ public static final Uri CONTENT_DATA_URI = Uri.parse("content://" + AUTHORITY + "/data"); - // 定义笔记和文件夹的列名 + public interface NoteColumns { /** * The unique ID for a row @@ -165,7 +166,7 @@ public class Notes { */ public static final String VERSION = "version"; } - // 定义数据表的列名 + public interface DataColumns { /** * The unique ID for a row @@ -239,7 +240,7 @@ public class Notes { */ public static final String DATA5 = "data5"; } - // 定义文本笔记类 + public static final class TextNote implements DataColumns { /** * Mode to indicate the text in check list mode or not @@ -255,7 +256,7 @@ public class Notes { public static final Uri CONTENT_URI = Uri.parse("content://" + AUTHORITY + "/text_note"); } - // 定义通话笔记类 + public static final class CallNote implements DataColumns { /** * Call date for this record diff --git a/other/06_江一阳_代码标注/data/NotesDatabaseHelper.java b/doc/data/NotesDatabaseHelper.java similarity index 97% rename from other/06_江一阳_代码标注/data/NotesDatabaseHelper.java rename to doc/data/NotesDatabaseHelper.java index df1733b..ffe5d57 100644 --- a/other/06_江一阳_代码标注/data/NotesDatabaseHelper.java +++ b/doc/data/NotesDatabaseHelper.java @@ -26,22 +26,22 @@ import net.micode.notes.data.Notes.DataColumns; import net.micode.notes.data.Notes.DataConstants; import net.micode.notes.data.Notes.NoteColumns; -// 定义一个名为NotesDatabaseHelper的公共类,继承自SQLiteOpenHelper + public class NotesDatabaseHelper extends SQLiteOpenHelper { - // 定义数据库名和版本号 private static final String DB_NAME = "note.db"; + private static final int DB_VERSION = 4; - // 定义表名称常量 + public interface TABLE { public static final String NOTE = "note"; public static final String DATA = "data"; } - // 定义日志标签 + private static final String TAG = "NotesDatabaseHelper"; - // 定义一个NotesDatabaseHelper类型的私有静态实例变量 + private static NotesDatabaseHelper mInstance; - // 定义创建note表的SQL语句 + private static final String CREATE_NOTE_TABLE_SQL = "CREATE TABLE " + TABLE.NOTE + "(" + NoteColumns.ID + " INTEGER PRIMARY KEY," + @@ -85,7 +85,6 @@ public class NotesDatabaseHelper extends SQLiteOpenHelper { /** * Increase folder's note count when move note to the folder */ - private static final String NOTE_INCREASE_FOLDER_COUNT_ON_UPDATE_TRIGGER = "CREATE TRIGGER increase_folder_count_on_update "+ " AFTER UPDATE OF " + NoteColumns.PARENT_ID + " ON " + TABLE.NOTE + @@ -98,7 +97,6 @@ public class NotesDatabaseHelper extends SQLiteOpenHelper { /** * 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 + @@ -112,7 +110,6 @@ public class NotesDatabaseHelper extends SQLiteOpenHelper { /** * 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 + @@ -138,7 +135,6 @@ public class NotesDatabaseHelper extends SQLiteOpenHelper { /** * Update note's content when insert data with type {@link DataConstants#NOTE} */ - // 定义更新笔记内容的触发器(插入类型为NOTE的数据时) private static final String DATA_UPDATE_NOTE_CONTENT_ON_INSERT_TRIGGER = "CREATE TRIGGER update_note_content_on_insert " + " AFTER INSERT ON " + TABLE.DATA + @@ -152,7 +148,6 @@ public class NotesDatabaseHelper extends SQLiteOpenHelper { /** * Update note's content when data with {@link DataConstants#NOTE} type has changed */ - // 定义更新笔记内容的触发器(数据类型为NOTE时发生变化) private static final String DATA_UPDATE_NOTE_CONTENT_ON_UPDATE_TRIGGER = "CREATE TRIGGER update_note_content_on_update " + " AFTER UPDATE ON " + TABLE.DATA + diff --git a/other/06_江一阳_代码标注/data/NotesProvider.java b/doc/data/NotesProvider.java similarity index 85% rename from other/06_江一阳_代码标注/data/NotesProvider.java rename to doc/data/NotesProvider.java index b8a6d6f..edb0a60 100644 --- a/other/06_江一阳_代码标注/data/NotesProvider.java +++ b/doc/data/NotesProvider.java @@ -16,6 +16,7 @@ package net.micode.notes.data; + import android.app.SearchManager; import android.content.ContentProvider; import android.content.ContentUris; @@ -33,21 +34,21 @@ import net.micode.notes.data.Notes.DataColumns; import net.micode.notes.data.Notes.NoteColumns; import net.micode.notes.data.NotesDatabaseHelper.TABLE; + public class NotesProvider extends ContentProvider { - // 声明 UriMatcher,用于匹配 Uri 和对应的操作 private static final UriMatcher mMatcher; private NotesDatabaseHelper mHelper; private static final String TAG = "NotesProvider"; - // 声明 UriMatcher,用于匹配 Uri 和对应的操作 - private static final int URI_NOTE = 1; - private static final int URI_NOTE_ITEM = 2; - private static final int URI_DATA = 3; - private static final int URI_DATA_ITEM = 4; - private static final int URI_SEARCH = 5; - private static final int URI_SEARCH_SUGGEST = 6; + private static final int URI_NOTE = 1; + private static final int URI_NOTE_ITEM = 2; + private static final int URI_DATA = 3; + private static final int URI_DATA_ITEM = 4; + + private static final int URI_SEARCH = 5; + private static final int URI_SEARCH_SUGGEST = 6; static { mMatcher = new UriMatcher(UriMatcher.NO_MATCH); @@ -61,33 +62,29 @@ public class NotesProvider extends ContentProvider { } /** - * x'0A' represents the '\n' character in sqlite. For title and content in the - * search result, + * 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. */ - // 声明 UriMatcher,用于匹配 Uri 和对应的操作 private static final String NOTES_SEARCH_PROJECTION = NoteColumns.ID + "," - + NoteColumns.ID + " AS " + SearchManager.SUGGEST_COLUMN_INTENT_EXTRA_DATA + "," - + "TRIM(REPLACE(" + NoteColumns.SNIPPET + ", x'0A','')) AS " + SearchManager.SUGGEST_COLUMN_TEXT_1 + "," - + "TRIM(REPLACE(" + NoteColumns.SNIPPET + ", x'0A','')) AS " + SearchManager.SUGGEST_COLUMN_TEXT_2 + "," - + R.drawable.search_result + " AS " + SearchManager.SUGGEST_COLUMN_ICON_1 + "," - + "'" + Intent.ACTION_VIEW + "' AS " + SearchManager.SUGGEST_COLUMN_INTENT_ACTION + "," - + "'" + Notes.TextNote.CONTENT_TYPE + "' AS " + SearchManager.SUGGEST_COLUMN_INTENT_DATA; - // 声明常量,表示查询搜索建议的 SQL 语句 + + NoteColumns.ID + " AS " + SearchManager.SUGGEST_COLUMN_INTENT_EXTRA_DATA + "," + + "TRIM(REPLACE(" + NoteColumns.SNIPPET + ", x'0A','')) AS " + SearchManager.SUGGEST_COLUMN_TEXT_1 + "," + + "TRIM(REPLACE(" + NoteColumns.SNIPPET + ", x'0A','')) AS " + SearchManager.SUGGEST_COLUMN_TEXT_2 + "," + + R.drawable.search_result + " AS " + SearchManager.SUGGEST_COLUMN_ICON_1 + "," + + "'" + Intent.ACTION_VIEW + "' AS " + SearchManager.SUGGEST_COLUMN_INTENT_ACTION + "," + + "'" + Notes.TextNote.CONTENT_TYPE + "' AS " + SearchManager.SUGGEST_COLUMN_INTENT_DATA; + private static String NOTES_SNIPPET_SEARCH_QUERY = "SELECT " + NOTES_SEARCH_PROJECTION - + " FROM " + TABLE.NOTE - + " WHERE " + NoteColumns.SNIPPET + " LIKE ?" - + " AND " + NoteColumns.PARENT_ID + "<>" + Notes.ID_TRASH_FOLER - + " AND " + NoteColumns.TYPE + "=" + Notes.TYPE_NOTE; + + " FROM " + TABLE.NOTE + + " WHERE " + NoteColumns.SNIPPET + " LIKE ?" + + " AND " + NoteColumns.PARENT_ID + "<>" + Notes.ID_TRASH_FOLER + + " AND " + NoteColumns.TYPE + "=" + Notes.TYPE_NOTE; - // 初始化 NotesProvider @Override public boolean onCreate() { mHelper = NotesDatabaseHelper.getInstance(getContext()); return true; } - // 查询数据 @Override public Cursor query(Uri uri, String[] projection, String selection, String[] selectionArgs, String sortOrder) { @@ -230,7 +227,6 @@ public class NotesProvider extends ContentProvider { return count; } - // 实现了更新数据的功能 @Override public int update(Uri uri, ContentValues values, String selection, String[] selectionArgs) { int count = 0; @@ -275,16 +271,6 @@ public class NotesProvider extends ContentProvider { return (!TextUtils.isEmpty(selection) ? " AND (" + selection + ')' : ""); } - /** - * - * 增加Note的版本号,每次更新Note时,都需要对其版本号加1。 - * - * @param id 需要更新版本号的Note的ID,如果为-1,则更新所有Note的版本号。 - * - * @param selection 查询条件。 - * - * @param selectionArgs 查询条件中的参数。 - **/ private void increaseNoteVersion(long id, String selection, String[] selectionArgs) { StringBuilder sql = new StringBuilder(120); sql.append("UPDATE "); diff --git a/other/06_何越_代码标注/gtask/data/MetaData.java b/doc/gtask/data/MetaData.java similarity index 100% rename from other/06_何越_代码标注/gtask/data/MetaData.java rename to doc/gtask/data/MetaData.java diff --git a/other/06_何越_代码标注/gtask/data/Node.java b/doc/gtask/data/Node.java similarity index 100% rename from other/06_何越_代码标注/gtask/data/Node.java rename to doc/gtask/data/Node.java diff --git a/other/06_何越_代码标注/gtask/data/SqlData.java b/doc/gtask/data/SqlData.java similarity index 100% rename from other/06_何越_代码标注/gtask/data/SqlData.java rename to doc/gtask/data/SqlData.java diff --git a/other/06_何越_代码标注/gtask/data/SqlNote.java b/doc/gtask/data/SqlNote.java similarity index 100% rename from other/06_何越_代码标注/gtask/data/SqlNote.java rename to doc/gtask/data/SqlNote.java diff --git a/other/06_何越_代码标注/gtask/data/Task.java b/doc/gtask/data/Task.java similarity index 100% rename from other/06_何越_代码标注/gtask/data/Task.java rename to doc/gtask/data/Task.java diff --git a/other/06_何越_代码标注/gtask/data/TaskList.java b/doc/gtask/data/TaskList.java similarity index 100% rename from other/06_何越_代码标注/gtask/data/TaskList.java rename to doc/gtask/data/TaskList.java diff --git a/other/06_何越_代码标注/gtask/exception/ActionFailureException.java b/doc/gtask/exception/ActionFailureException.java similarity index 100% rename from other/06_何越_代码标注/gtask/exception/ActionFailureException.java rename to doc/gtask/exception/ActionFailureException.java diff --git a/other/06_何越_代码标注/gtask/exception/NetworkFailureException.java b/doc/gtask/exception/NetworkFailureException.java similarity index 86% rename from other/06_何越_代码标注/gtask/exception/NetworkFailureException.java rename to doc/gtask/exception/NetworkFailureException.java index 702c07f..b08cfb1 100644 --- a/other/06_何越_代码标注/gtask/exception/NetworkFailureException.java +++ b/doc/gtask/exception/NetworkFailureException.java @@ -19,17 +19,14 @@ package net.micode.notes.gtask.exception; public class NetworkFailureException extends Exception { private static final long serialVersionUID = 2107610287180234136L; - // 无参构造函数 public NetworkFailureException() { super(); } - // 构造函数,接受一个字符串消息作为参数 public NetworkFailureException(String paramString) { super(paramString); } - // 构造函数,接受一个字符串消息和一个可抛出对象作为参数 public NetworkFailureException(String paramString, Throwable paramThrowable) { super(paramString, paramThrowable); } diff --git a/other/06_何越_代码标注/gtask/remote/GTaskASyncTask.java b/doc/gtask/remote/GTaskASyncTask.java similarity index 100% rename from other/06_何越_代码标注/gtask/remote/GTaskASyncTask.java rename to doc/gtask/remote/GTaskASyncTask.java diff --git a/other/06_何越_代码标注/gtask/remote/GTaskClient.java b/doc/gtask/remote/GTaskClient.java similarity index 100% rename from other/06_何越_代码标注/gtask/remote/GTaskClient.java rename to doc/gtask/remote/GTaskClient.java diff --git a/doc/gtask/remote/GTaskManager.java b/doc/gtask/remote/GTaskManager.java new file mode 100644 index 0000000..20a81c2 --- /dev/null +++ b/doc/gtask/remote/GTaskManager.java @@ -0,0 +1,916 @@ +/* + * Copyright (c) 2010-2011, The MiCode Open Source Community (www.micode.net) + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package net.micode.notes.gtask.remote; + +import android.app.Activity; +import android.content.ContentResolver; +import android.content.ContentUris; +import android.content.ContentValues; +import android.content.Context; +import android.database.Cursor; +import android.util.Log; + +import net.micode.notes.R; +import net.micode.notes.data.Notes; +import net.micode.notes.data.Notes.DataColumns; +import net.micode.notes.data.Notes.NoteColumns; +import net.micode.notes.gtask.data.MetaData; +import net.micode.notes.gtask.data.Node; +import net.micode.notes.gtask.data.SqlNote; +import net.micode.notes.gtask.data.Task; +import net.micode.notes.gtask.data.TaskList; +import net.micode.notes.gtask.exception.ActionFailureException; +import net.micode.notes.gtask.exception.NetworkFailureException; +import net.micode.notes.tool.DataUtils; +import net.micode.notes.tool.GTaskStringUtils; + +import org.json.JSONArray; +import org.json.JSONException; +import org.json.JSONObject; + +import java.util.HashMap; +import java.util.HashSet; +import java.util.Iterator; +import java.util.Map; + + +// 定义一个GTaskManager类 +public class GTaskManager { + // 声明一个TAG常量用于记录日志 + private static final String TAG = GTaskManager.class.getSimpleName(); + + // 声明四个int类型的状态常量 + public static final int STATE_SUCCESS = 0; + public static final int STATE_NETWORK_ERROR = 1; + public static final int STATE_INTERNAL_ERROR = 2; + public static final int STATE_SYNC_IN_PROGRESS = 3; + public static final int STATE_SYNC_CANCELLED = 4; + + // 声明一个GTaskManager实例变量 + private static GTaskManager mInstance = null; + + // 声明Activity、Context、ContentResolver等变量 + private Activity mActivity; + private Context mContext; + private ContentResolver mContentResolver; + + // 声明标记同步状态和取消同步状态的变量 + private boolean mSyncing; + private boolean mCancelled; + + // 声明三个HashMap,一个TaskList对象,一个HashSet和两个映射表 + private HashMap mGTaskListHashMap; + private HashMap mGTaskHashMap; + private HashMap mMetaHashMap; + private TaskList mMetaList; + private HashSet mLocalDeleteIdMap; + private HashMap mGidToNid; + private HashMap mNidToGid; + + // 构造函数,初始化上述变量 + private GTaskManager() { + mSyncing = false; + mCancelled = false; + mGTaskListHashMap = new HashMap(); + mGTaskHashMap = new HashMap(); + mMetaHashMap = new HashMap(); + mMetaList = null; + mLocalDeleteIdMap = new HashSet(); + mGidToNid = new HashMap(); + mNidToGid = new HashMap(); + } + + // 获取GTaskManager实例的静态方法 + public static synchronized GTaskManager getInstance() { + if (mInstance == null) { + mInstance = new GTaskManager(); + } + return mInstance; + } + + // 设置Activity上下文的方法 + public synchronized void setActivityContext(Activity activity) { + // 用于获取authToken + mActivity = activity; + } +} + + +public int sync(Context context, GTaskASyncTask asyncTask) { + // 检查是否正在同步任务,如果是,则返回“正在同步”的状态 + if (mSyncing) { + Log.d(TAG, "Sync is in progress"); + return STATE_SYNC_IN_PROGRESS; + } + // 初始化变量 + mContext = context; + mContentResolver = mContext.getContentResolver(); + mSyncing = true; + mCancelled = false; + mGTaskListHashMap.clear(); + mGTaskHashMap.clear(); + mMetaHashMap.clear(); + mLocalDeleteIdMap.clear(); + mGidToNid.clear(); + mNidToGid.clear(); + + try { + // 获取GTaskClient的实例 + GTaskClient client = GTaskClient.getInstance(); + // 重置更新数组 + client.resetUpdateArray(); + + // 登录Google任务 + if (!mCancelled) { + if (!client.login(mActivity)) { + // 登录失败,抛出异常 + throw new NetworkFailureException("login google task failed"); + } + } + + // 从Google获取任务列表 + asyncTask.publishProgess(mContext.getString(R.string.sync_progress_init_list)); + initGTaskList(); + + // 进行内容同步工作 + asyncTask.publishProgess(mContext.getString(R.string.sync_progress_syncing)); + syncContent(); + } catch (NetworkFailureException e) { + // 网络连接失败,返回网络错误状态 + Log.e(TAG, e.toString()); + return STATE_NETWORK_ERROR; + } catch (ActionFailureException e) { + // 执行任务失败,返回内部错误状态 + Log.e(TAG, e.toString()); + return STATE_INTERNAL_ERROR; + } catch (Exception e) { + // 发生其他异常,打印异常信息并返回内部错误状态 + Log.e(TAG, e.toString()); + e.printStackTrace(); + return STATE_INTERNAL_ERROR; + } finally { + // 清空所有变量,标记同步完成 + mGTaskListHashMap.clear(); + mGTaskHashMap.clear(); + mMetaHashMap.clear(); + mLocalDeleteIdMap.clear(); + mGidToNid.clear(); + mNidToGid.clear(); + mSyncing = false; + } + + // 返回同步是否被取消的状态 + return mCancelled ? STATE_SYNC_CANCELLED : STATE_SUCCESS; +} + + +private void initGTaskList() throws NetworkFailureException { + // 检查任务是否已被取消 + if (mCancelled) + return; + + // 获取 GTaskClient 实例 + GTaskClient client = GTaskClient.getInstance(); + try { + // 获取任务列表的 JSON 数组 + JSONArray jsTaskLists = client.getTaskLists(); + + // 初始化元列表 + mMetaList = null; + // 遍历任务列表 + for (int i = 0; i < jsTaskLists.length(); i++) { + // 获取任务列表中的 JSON 对象 + JSONObject object = jsTaskLists.getJSONObject(i); + // 获取任务列表的 ID 和名称 + String gid = object.getString(GTaskStringUtils.GTASK_JSON_ID); + String name = object.getString(GTaskStringUtils.GTASK_JSON_NAME); + + // 如果任务列表名称为元列表,则初始化元列表 + if (name.equals(GTaskStringUtils.MIUI_FOLDER_PREFFIX + GTaskStringUtils.FOLDER_META)) { + mMetaList = new TaskList(); + // 将元列表的内容设置为远程 JSON 数据 + mMetaList.setContentByRemoteJSON(object); + + // 加载元数据 + JSONArray jsMetas = client.getTaskList(gid); + for (int j = 0; j < jsMetas.length(); j++) { + object = (JSONObject) jsMetas.getJSONObject(j); + MetaData metaData = new MetaData(); + metaData.setContentByRemoteJSON(object); + if (metaData.isWorthSaving()) { + mMetaList.addChildTask(metaData); + if (metaData.getGid() != null) { + mMetaHashMap.put(metaData.getRelatedGid(), metaData); + } + } + } + } + } + + // 如果元列表不存在,则创建元列表 + if (mMetaList == null) { + mMetaList = new TaskList(); + mMetaList.setName(GTaskStringUtils.MIUI_FOLDER_PREFFIX + GTaskStringUtils.FOLDER_META); + GTaskClient.getInstance().createTaskList(mMetaList); + } + + // 初始化任务列表 + for (int i = 0; i < jsTaskLists.length(); i++) { + JSONObject object = jsTaskLists.getJSONObject(i); + String gid = object.getString(GTaskStringUtils.GTASK_JSON_ID); + String name = object.getString(GTaskStringUtils.GTASK_JSON_NAME); + + // 如果任务列表名称以 MIUI_FOLDER_PREFFIX 开头且不是元列表,则初始化任务列表 + if (name.startsWith(GTaskStringUtils.MIUI_FOLDER_PREFFIX) && !name.equals(GTaskStringUtils.MIUI_FOLDER_PREFFIX + GTaskStringUtils.FOLDER_META)) { + TaskList tasklist = new TaskList(); + tasklist.setContentByRemoteJSON(object); + mGTaskListHashMap.put(gid, tasklist); + mGTaskHashMap.put(gid, tasklist); + + // 加载任务 + JSONArray jsTasks = client.getTaskList(gid); + for (int j = 0; j < jsTasks.length(); j++) { + object = (JSONObject) jsTasks.getJSONObject(j); + gid = object.getString(GTaskStringUtils.GTASK_JSON_ID); + Task task = new Task(); + task.setContentByRemoteJSON(object); + if (task.isWorthSaving()) { + task.setMetaInfo(mMetaHashMap.get(gid)); + tasklist.addChildTask(task); + mGTaskHashMap.put(gid, task); + } + } + } + } + } catch (JSONException e) { + Log.e(TAG, e.toString()); + e.printStackTrace(); + throw new ActionFailureException("initGTaskList: handing JSONObject failed"); + } + } + + pprivate void syncContent() throws NetworkFailureException { + int syncType; // 同步类型 + Cursor c = null; // 游标对象 + String gid; // GTasks ID + Node node; // 节点对象 + + mLocalDeleteIdMap.clear(); // 清空本地删除ID Map + + if (mCancelled) { // 如果已取消,则返回 + return; + } + + // 处理本地已删除的笔记 + try { + c = mContentResolver.query(Notes.CONTENT_NOTE_URI, SqlNote.PROJECTION_NOTE, + "(type<>? AND parent_id=?)", new String[] { + String.valueOf(Notes.TYPE_SYSTEM), String.valueOf(Notes.ID_TRASH_FOLER) + }, null); + if (c != null) { + while (c.moveToNext()) { + gid = c.getString(SqlNote.GTASK_ID_COLUMN); + node = mGTaskHashMap.get(gid); // 获取对应的节点对象 + if (node != null) { + mGTaskHashMap.remove(gid); // 从 HashMap 中移除对应的节点 + doContentSync(Node.SYNC_ACTION_DEL_REMOTE, node, c); // 执行同步 + } + + mLocalDeleteIdMap.add(c.getLong(SqlNote.ID_COLUMN)); // 将本地删除 ID 加入 Map 中 + } + } else { + Log.w(TAG, "failed to query trash folder"); + } + } finally { + if (c != null) { + c.close(); + c = null; + } + } + + // 先同步文件夹 + syncFolder(); + + // 处理存在于数据库中的笔记 + try { + c = mContentResolver.query(Notes.CONTENT_NOTE_URI, SqlNote.PROJECTION_NOTE, + "(type=? AND parent_id<>?)", new String[] { + String.valueOf(Notes.TYPE_NOTE), String.valueOf(Notes.ID_TRASH_FOLER) + }, NoteColumns.TYPE + " DESC"); + if (c != null) { + while (c.moveToNext()) { + gid = c.getString(SqlNote.GTASK_ID_COLUMN); + node = mGTaskHashMap.get(gid); // 获取对应的节点对象 + if (node != null) { + mGTaskHashMap.remove(gid); // 从 HashMap 中移除对应的节点 + mGidToNid.put(gid, c.getLong(SqlNote.ID_COLUMN)); // 将 GTasks ID 与本地 ID 对应 + mNidToGid.put(c.getLong(SqlNote.ID_COLUMN), gid); // 将本地 ID 与 GTasks ID 对应 + syncType = node.getSyncAction(c); // 获取同步类型 + } else { + if (c.getString(SqlNote.GTASK_ID_COLUMN).trim().length() == 0) { + // 本地新增 + syncType = Node.SYNC_ACTION_ADD_REMOTE; + } else { + // 远程删除 + syncType = Node.SYNC_ACTION_DEL_LOCAL; + } + } + doContentSync(syncType, node, c); // 执行同步 + } + } else { + Log.w(TAG, "failed to query existing note in database"); + } + + } finally { + if (c != null) { + c.close(); + c = null; + } + } + + // 处理剩余的项目 + Iterator> iter = mGTaskHashMap.entrySet().iterator(); + while (iter.hasNext()) { + Map.Entry entry = iter.next(); + node = entry.getValue(); + doContentSync(Node.SYNC_ACTION_ADD_LOCAL, node, null); + } + + // mCancelled can be set by another thread, so we neet to check one by + // one + // clear local delete table + // 检查操作是否已取消,如果未取消则继续执行 +if (!mCancelled) { + // 如果没有取消且无法批量删除本地已删除的笔记,则抛出一个异常 + if (!DataUtils.batchDeleteNotes(mContentResolver, mLocalDeleteIdMap)) { + throw new ActionFailureException("failed to batch-delete local deleted notes"); + } +} + +// 如果操作未取消,则执行以下操作 +if (!mCancelled) { + // 更新 GTaskClient 实例,并刷新本地同步 ID + GTaskClient.getInstance().commitUpdate(); + refreshLocalSyncId(); +} + + +private void syncFolder() throws NetworkFailureException { + Cursor c = null; + String gid; + Node node; + int syncType; + + // 如果任务已经取消了,就直接退出 + if (mCancelled) { + return; + } + + // 处理根文件夹 + try { + c = mContentResolver.query(ContentUris.withAppendedId(Notes.CONTENT_NOTE_URI, + Notes.ID_ROOT_FOLDER), SqlNote.PROJECTION_NOTE, null, null, null); + if (c != null) { + c.moveToNext(); + gid = c.getString(SqlNote.GTASK_ID_COLUMN); + node = mGTaskHashMap.get(gid); + if (node != null) { + mGTaskHashMap.remove(gid); + mGidToNid.put(gid, (long) Notes.ID_ROOT_FOLDER); + mNidToGid.put((long) Notes.ID_ROOT_FOLDER, gid); + // 对于系统文件夹,仅在必要时更新远程名称 + if (!node.getName().equals( + GTaskStringUtils.MIUI_FOLDER_PREFFIX + GTaskStringUtils.FOLDER_DEFAULT)) + doContentSync(Node.SYNC_ACTION_UPDATE_REMOTE, node, c); + } else { + doContentSync(Node.SYNC_ACTION_ADD_REMOTE, node, c); + } + } else { + Log.w(TAG, "failed to query root folder"); + } + } finally { + if (c != null) { + c.close(); + c = null; + } + } + + // 处理通话记录文件夹 + try { + c = mContentResolver.query(Notes.CONTENT_NOTE_URI, SqlNote.PROJECTION_NOTE, "(_id=?)", + new String[] { + String.valueOf(Notes.ID_CALL_RECORD_FOLDER) + }, null); + if (c != null) { + if (c.moveToNext()) { + gid = c.getString(SqlNote.GTASK_ID_COLUMN); + node = mGTaskHashMap.get(gid); + if (node != null) { + mGTaskHashMap.remove(gid); + mGidToNid.put(gid, (long) Notes.ID_CALL_RECORD_FOLDER); + mNidToGid.put((long) Notes.ID_CALL_RECORD_FOLDER, gid); + // 对于系统文件夹,仅在必要时更新远程名称 + if (!node.getName().equals( + GTaskStringUtils.MIUI_FOLDER_PREFFIX + + GTaskStringUtils.FOLDER_CALL_NOTE)) + doContentSync(Node.SYNC_ACTION_UPDATE_REMOTE, node, c); + } else { + doContentSync(Node.SYNC_ACTION_ADD_REMOTE, node, c); + } + } + } else { + Log.w(TAG, "failed to query call note folder"); + } + } finally { + if (c != null) { + c.close(); + c = null; + } + } + + // 处理本地现有文件夹 + try { + // 查询note表中非垃圾桶下的所有笔记,并按类型降序排列 + // 查询条件为:类型为folder且父文件夹不为垃圾桶 + c = mContentResolver.query(Notes.CONTENT_NOTE_URI, SqlNote.PROJECTION_NOTE, + "(type=? AND parent_id<>?)", new String[] { + String.valueOf(Notes.TYPE_FOLDER), String.valueOf(Notes.ID_TRASH_FOLER) + }, NoteColumns.TYPE + " DESC"); + + // 如果查询结果非空 + if (c != null) { + while (c.moveToNext()) { + // 获取笔记的GTasks ID + gid = c.getString(SqlNote.GTASK_ID_COLUMN); + // 从GTasks哈希表中获取笔记节点 + node = mGTaskHashMap.get(gid); + // 如果节点非空 + if (node != null) { + // 从哈希表中移除该节点 + mGTaskHashMap.remove(gid); + // 将该笔记的GTasks ID与本地ID建立映射关系 + mGidToNid.put(gid, c.getLong(SqlNote.ID_COLUMN)); + // 将本地ID与该笔记的GTasks ID建立映射关系 + mNidToGid.put(c.getLong(SqlNote.ID_COLUMN), gid); + // 获取节点的同步操作类型 + syncType = node.getSyncAction(c); + } else { + // 如果该笔记在GTasks中不存在 + if (c.getString(SqlNote.GTASK_ID_COLUMN).trim().length() == 0) { + // 本地新增 + syncType = Node.SYNC_ACTION_ADD_REMOTE; + } else { + // 远程删除 + syncType = Node.SYNC_ACTION_DEL_LOCAL; + } + } + // 执行内容同步 + doContentSync(syncType, node, c); + } + } else { + Log.w(TAG, "failed to query existing folder"); + } + } finally { + // 关闭游标 + if (c != null) { + c.close(); + c = null; + } + } + + // 遍历GTasks列表哈希表,对于每一个远程新增的文件夹,执行本地新增操作 + Iterator> iter = mGTaskListHashMap.entrySet().iterator(); + while (iter.hasNext()) { + Map.Entry entry = iter.next(); + gid = entry.getKey(); + node = entry.getValue(); + if (mGTaskHashMap.containsKey(gid)) { + mGTaskHashMap.remove(gid); + doContentSync(Node.SYNC_ACTION_ADD_LOCAL, node, null); + } + } + + // 提交更新操作 + if (!mCancelled) + GTaskClient.getInstance().commitUpdate(); + + + /** + * 这个方法用于执行同步操作。 + * + * @param syncType 同步操作的类型。 + * @param node 要同步的节点。 + * @param c 光标,用于查询本地数据库。 + * @throws NetworkFailureException 如果同步失败,则抛出 NetworkFailureException 异常。 + */ +private void doContentSync(int syncType, Node node, Cursor c) throws NetworkFailureException { + // 如果已经取消了同步,则直接返回。 + if (mCancelled) { + return; + } + + MetaData meta; + // 根据不同的同步操作类型,执行不同的同步操作。 + switch (syncType) { + case Node.SYNC_ACTION_ADD_LOCAL: + // 将本地节点添加到云端。 + addLocalNode(node); + break; + case Node.SYNC_ACTION_ADD_REMOTE: + // 将云端节点添加到本地。 + addRemoteNode(node, c); + break; + case Node.SYNC_ACTION_DEL_LOCAL: + // 删除本地节点。 + meta = mMetaHashMap.get(c.getString(SqlNote.GTASK_ID_COLUMN)); + if (meta != null) { + GTaskClient.getInstance().deleteNode(meta); + } + mLocalDeleteIdMap.add(c.getLong(SqlNote.ID_COLUMN)); + break; + case Node.SYNC_ACTION_DEL_REMOTE: + // 删除云端节点。 + meta = mMetaHashMap.get(node.getGid()); + if (meta != null) { + GTaskClient.getInstance().deleteNode(meta); + } + GTaskClient.getInstance().deleteNode(node); + break; + case Node.SYNC_ACTION_UPDATE_LOCAL: + // 更新本地节点。 + updateLocalNode(node, c); + break; + case Node.SYNC_ACTION_UPDATE_REMOTE: + // 更新云端节点。 + updateRemoteNode(node, c); + break; + case Node.SYNC_ACTION_UPDATE_CONFLICT: + // 合并本地和云端的修改。 + // 目前只是简单地使用本地的修改。 + updateRemoteNode(node, c); + break; + case Node.SYNC_ACTION_NONE: + // 没有任何同步操作需要执行。 + break; + case Node.SYNC_ACTION_ERROR: + default: + // 同步操作类型未知,抛出 ActionFailureException 异常。 + throw new ActionFailureException("unkown sync action type"); + } +} + + +private void addLocalNode(Node node) throws NetworkFailureException { + if (mCancelled) { // 检查任务是否被取消,如果被取消,则返回 + return; + } + + SqlNote sqlNote; // 声明SqlNote对象 + if (node instanceof TaskList) { // 如果节点为任务列表 + if (node.getName().equals( + GTaskStringUtils.MIUI_FOLDER_PREFFIX + GTaskStringUtils.FOLDER_DEFAULT)) { // 如果节点名称是默认文件夹 + sqlNote = new SqlNote(mContext, Notes.ID_ROOT_FOLDER); // 设置SqlNote对象的mId为root_folder + } else if (node.getName().equals( + GTaskStringUtils.MIUI_FOLDER_PREFFIX + GTaskStringUtils.FOLDER_CALL_NOTE)) { // 如果节点名称是通话记录文件夹 + sqlNote = new SqlNote(mContext, Notes.ID_CALL_RECORD_FOLDER); // 设置SqlNote对象的mId为call_record_folder + } else { + sqlNote = new SqlNote(mContext); + sqlNote.setContent(node.getLocalJSONFromContent()); // 设置SqlNote对象的内容为节点的JSON内容 + sqlNote.setParentId(Notes.ID_ROOT_FOLDER); // 设置SqlNote对象的父节点ID为root_folder + } + } else { // 如果节点不是任务列表 + sqlNote = new SqlNote(mContext); + JSONObject js = node.getLocalJSONFromContent(); // 获取节点的JSON内容 + try { + if (js.has(GTaskStringUtils.META_HEAD_NOTE)) { // 如果JSON内容中包含“meta_head_note”字段 + JSONObject note = js.getJSONObject(GTaskStringUtils.META_HEAD_NOTE); // 获取“meta_head_note”字段对应的JSONObject + if (note.has(NoteColumns.ID)) { // 如果JSONObject中包含“id”字段 + long id = note.getLong(NoteColumns.ID); // 获取“id”字段对应的值 + if (DataUtils.existInNoteDatabase(mContentResolver, id)) { // 如果本地数据库中已经存在该“id” + // the id is not available, have to create a new one + note.remove(NoteColumns.ID); // 则删除该“id”字段 + } + } + } + + if (js.has(GTaskStringUtils.META_HEAD_DATA)) { // 如果JSON内容中包含“meta_head_data”字段 + JSONArray dataArray = js.getJSONArray(GTaskStringUtils.META_HEAD_DATA); // 获取“meta_head_data”字段对应的JSONArray + for (int i = 0; i < dataArray.length(); i++) { + JSONObject data = dataArray.getJSONObject(i); // 获取JSONArray中对应位置的JSONObject + if (data.has(DataColumns.ID)) { // 如果JSONObject中包含“id”字段 + long dataId = data.getLong(DataColumns.ID); // 获取“id”字段对应的值 + if (DataUtils.existInDataDatabase(mContentResolver, dataId)) { // 如果本地数据库中已经存在该“id” + // the data id is not available, have to create a new one + data.remove(DataColumns.ID); // 则删除该“id”字段 + } + } + } + + } + } catch (JSONException e) { + Log.w(TAG, e.toString()); + e.printStackTrace(); + } + sqlNote.setContent(js); // 设置SqlNote对象的内容为节点的JSON内容 + + // 从node对象中获取其父任务的gid,再根据gid到本地gid-nid映射表中查找其对应的nid +Long parentId = mGidToNid.get(((Task) node).getParent().getGid()); + +// 如果nid为null,则打印日志并抛出ActionFailureException异常 +if (parentId == null) { + Log.e(TAG, "cannot find task's parent id locally"); + throw new ActionFailureException("cannot add local node"); +} + +// 将获取到的nid设置为sqlNote的parentId属性值 +sqlNote.setParentId(parentId.longValue()); + +// 将node的gid设置为sqlNote的GtaskId属性值 +sqlNote.setGtaskId(node.getGid()); + +// 提交sqlNote的修改到数据库中,但不包括提交事务 +sqlNote.commit(false); + +// 将node的gid和sqlNote的id映射关系加入本地gid-nid和nid-gid映射表中 +mGidToNid.put(node.getGid(), sqlNote.getId()); +mNidToGid.put(sqlNote.getId(), node.getGid()); + +// 更新远程meta数据 +updateRemoteMeta(node.getGid(), sqlNote); + + +private void updateLocalNode(Node node, Cursor c) throws NetworkFailureException { + if (mCancelled) { + return; + } + + SqlNote sqlNote; + + // 将节点的内容从JSON字符串转换为本地数据库中的SqlNote对象 + sqlNote = new SqlNote(mContext, c); + sqlNote.setContent(node.getLocalJSONFromContent()); + + // 获取节点的父节点ID + Long parentId = (node instanceof Task) ? mGidToNid.get(((Task) node).getParent().getGid()) + : new Long(Notes.ID_ROOT_FOLDER); + + // 如果无法找到父节点ID,则抛出ActionFailureException异常 + if (parentId == null) { + Log.e(TAG, "cannot find task's parent id locally"); + throw new ActionFailureException("cannot update local node"); + } + + // 将SqlNote对象的父ID设置为获取到的父节点ID + sqlNote.setParentId(parentId.longValue()); + + // 将SqlNote对象保存到本地数据库中 + sqlNote.commit(true); + + // 更新远程节点的元数据信息 + updateRemoteMeta(node.getGid(), sqlNote); +} + +private void addRemoteNode(Node node, Cursor c) throws NetworkFailureException { + // 如果任务已被取消,则直接返回,不执行任务 + if (mCancelled) { + return; + } + + // 创建 SqlNote 对象 + SqlNote sqlNote = new SqlNote(mContext, c); + Node n; + + // 如果是任务类型的注释 + if (sqlNote.isNoteType()) { + // 创建 Task 对象 + Task task = new Task(); + task.setContentByLocalJSON(sqlNote.getContent()); + + // 获取任务的父 ID,并创建父任务列表的 GID 映射 + String parentGid = mNidToGid.get(sqlNote.getParentId()); + if (parentGid == null) { + // 如果无法找到任务的父任务列表,则抛出异常 + Log.e(TAG, "cannot find task's parent tasklist"); + throw new ActionFailureException("cannot add remote task"); + } + // 将任务添加到父任务列表中 + mGTaskListHashMap.get(parentGid).addChildTask(task); + + // 使用 GTaskClient 创建任务 + GTaskClient.getInstance().createTask(task); + n = (Node) task; + + // 更新任务的元数据 + updateRemoteMeta(task.getGid(), sqlNote); + } else { + // 如果是任务列表类型的注释 + + TaskList tasklist = null; + + // 设置文件夹名 + String folderName = GTaskStringUtils.MIUI_FOLDER_PREFFIX; + if (sqlNote.getId() == Notes.ID_ROOT_FOLDER) + folderName += GTaskStringUtils.FOLDER_DEFAULT; + else if (sqlNote.getId() == Notes.ID_CALL_RECORD_FOLDER) + folderName += GTaskStringUtils.FOLDER_CALL_NOTE; + else + folderName += sqlNote.getSnippet(); + + // 遍历已存在的任务列表 + Iterator> iter = mGTaskListHashMap.entrySet().iterator(); + while (iter.hasNext()) { + Map.Entry entry = iter.next(); + String gid = entry.getKey(); + TaskList list = entry.getValue(); + + // 如果任务列表的名称与文件夹名称匹配,则将任务列表设置为当前列表 + if (list.getName().equals(folderName)) { + tasklist = list; + // 如果任务列表的 GID 已经存在,则从 mGTaskHashMap 中移除该 GID + if (mGTaskHashMap.containsKey(gid)) { + mGTaskHashMap.remove(gid); + } + break; + } + } + + // 如果任务列表不存在,则创建新任务列表并添加到 mGTaskListHashMap + if (tasklist == null) { + tasklist = new TaskList(); + tasklist.setContentByLocalJSON(sqlNote.getContent()); + GTaskClient.getInstance().createTaskList(tasklist); + mGTaskListHashMap.put(tasklist.getGid(), tasklist); + } + n = (Node) tasklist; + } + + // 更新本地 SqlNote 对象的 GID,并提交更改 + sqlNote.setGtaskId(n.getGid()); + sqlNote.commit(false); + sqlNote.resetLocalModified(); + sqlNote.commit(true); + + // 添加 GID 和 ID 的映射 + mGidToNid.put(n.getGid(), sqlNote.getId()); + mNidToGid.put(sqlNote.getId(), n.getGid()); +} + + +private void updateRemoteNode(Node node, Cursor c) throws NetworkFailureException { + if (mCancelled) { // 如果任务被取消,停止更新远程节点 + return; + } + + // 创建 SqlNote 对象 + SqlNote sqlNote = new SqlNote(mContext, c); + + // 更新远程节点 + node.setContentByLocalJSON(sqlNote.getContent()); + GTaskClient.getInstance().addUpdateNode(node); + + // 更新元数据 + updateRemoteMeta(node.getGid(), sqlNote); + + // 如果是 Note 类型,移动任务 + if (sqlNote.isNoteType()) { + Task task = (Task) node; // 将节点转换为 Task 类型 + TaskList preParentList = task.getParent(); // 获取任务的旧父任务列表 + + String curParentGid = mNidToGid.get(sqlNote.getParentId()); // 获取任务的新父任务列表的 gid + if (curParentGid == null) { + Log.e(TAG, "cannot find task's parent tasklist"); // 如果找不到新父任务列表,输出错误日志 + throw new ActionFailureException("cannot update remote task"); // 抛出异常 + } + TaskList curParentList = mGTaskListHashMap.get(curParentGid); // 获取新父任务列表 + + if (preParentList != curParentList) { // 如果任务的父任务列表发生了变化 + preParentList.removeChildTask(task); // 在旧的父任务列表中移除该任务 + curParentList.addChildTask(task); // 在新的父任务列表中添加该任务 + GTaskClient.getInstance().moveTask(task, preParentList, curParentList); // 在 Google 任务中移动任务 + } + } + + // 清除本地修改标志 + sqlNote.resetLocalModified(); + sqlNote.commit(true); // 提交更改 +} + +/** + * 在远程更新元数据 + * + * @param gid GTask 任务 ID + * @param sqlNote 笔记的 SQL 数据对象 + * @throws NetworkFailureException 网络连接异常 + */ +private void updateRemoteMeta(String gid, SqlNote sqlNote) throws NetworkFailureException { + // 检查是否为笔记类型 + if (sqlNote != null && sqlNote.isNoteType()) { + // 获取指定 gid 的 MetaData 对象 + MetaData metaData = mMetaHashMap.get(gid); + if (metaData != null) { + // 更新 MetaData 对象中指定 gid 的元数据 + metaData.setMeta(gid, sqlNote.getContent()); + // 向 GTask 服务器提交更新请求 + GTaskClient.getInstance().addUpdateNode(metaData); + } else { + // 如果 MetaData 对象不存在,则新建 MetaData 对象 + metaData = new MetaData(); + metaData.setMeta(gid, sqlNote.getContent()); + // 添加 MetaData 对象到 MetaList 中 + mMetaList.addChildTask(metaData); + // 将 MetaData 对象存储到 HashMap 中 + mMetaHashMap.put(gid, metaData); + // 向 GTask 服务器提交创建请求 + GTaskClient.getInstance().createTask(metaData); + } + } +} + +/** + * 刷新本地同步 ID + * + * @throws NetworkFailureException 网络连接异常 + */ +private void refreshLocalSyncId() throws NetworkFailureException { + // 检查是否已取消同步 + if (mCancelled) { + return; + } + + // 清空哈希表 + mGTaskHashMap.clear(); + mGTaskListHashMap.clear(); + mMetaHashMap.clear(); + // 初始化 GTaskList + initGTaskList(); + + Cursor c = null; + try { + // 查询本地未被删除且非系统笔记的 SQL 数据对象 + c = mContentResolver.query(Notes.CONTENT_NOTE_URI, SqlNote.PROJECTION_NOTE, + "(type<>? AND parent_id<>?)", new String[] { + String.valueOf(Notes.TYPE_SYSTEM), String.valueOf(Notes.ID_TRASH_FOLER) + }, NoteColumns.TYPE + " DESC"); + if (c != null) { + while (c.moveToNext()) { + String gid = c.getString(SqlNote.GTASK_ID_COLUMN); + // 获取指定 gid 的 GTask 任务节点 + Node node = mGTaskHashMap.get(gid); + if (node != null) { + // 移除哈希表中的 GTask 任务节点 + mGTaskHashMap.remove(gid); + ContentValues values = new ContentValues(); + // 更新本地 SQL 数据对象的同步 ID + values.put(NoteColumns.SYNC_ID, node.getLastModified()); + mContentResolver.update(ContentUris.withAppendedId(Notes.CONTENT_NOTE_URI, + c.getLong(SqlNote.ID_COLUMN)), values, null, null); + } else { + Log.e(TAG, "something is missed"); + // 抛出 ActionFailureException 异常 + throw new ActionFailureException( + "some local items don't have gid after sync"); + } + } + } else { + Log.w(TAG, "failed to query local note to refresh sync id"); + } + } finally { + if (c != null) { + c.close(); + c = null; + } + } +} + + /** + * 该方法用于获取同步账户名 + * @return 返回一个字符串,表示同步账户名 + */ +public String getSyncAccount() { + // 通过调用 GTaskClient 类的 getInstance() 方法获取 GTaskClient 实例,并调用该实例的 getSyncAccount() 方法,获取同步账户 + return GTaskClient.getInstance().getSyncAccount().name; +} + +/** + * 该方法用于取消同步 + */ +public void cancelSync() { + // 将 mCancelled 标志设置为 true,表示取消同步 + mCancelled = true; +} + +} diff --git a/other/06_何越_代码标注/gtask/remote/GTaskSyncService.java b/doc/gtask/remote/GTaskSyncService.java similarity index 82% rename from other/06_何越_代码标注/gtask/remote/GTaskSyncService.java rename to doc/gtask/remote/GTaskSyncService.java index 0db5471..cca36f7 100644 --- a/other/06_何越_代码标注/gtask/remote/GTaskSyncService.java +++ b/doc/gtask/remote/GTaskSyncService.java @@ -24,7 +24,6 @@ import android.os.Bundle; import android.os.IBinder; public class GTaskSyncService extends Service { - // 同步动作的广播名称,ACTION_START_SYNC 表示开始同步,ACTION_CANCEL_SYNC 表示取消同步,ACTION_INVALID 表示无效 public final static String ACTION_STRING_NAME = "sync_action_type"; public final static int ACTION_START_SYNC = 0; @@ -32,17 +31,17 @@ public class GTaskSyncService extends Service { public final static int ACTION_CANCEL_SYNC = 1; public final static int ACTION_INVALID = 2; - // 广播名称和广播消息 + public final static String GTASK_SERVICE_BROADCAST_NAME = "net.micode.notes.gtask.remote.gtask_sync_service"; public final static String GTASK_SERVICE_BROADCAST_IS_SYNCING = "isSyncing"; public final static String GTASK_SERVICE_BROADCAST_PROGRESS_MSG = "progressMsg"; - // 同步任务和同步进度 + private static GTaskASyncTask mSyncTask = null; private static String mSyncProgress = ""; - // 开始同步 + private void startSync() { if (mSyncTask == null) { mSyncTask = new GTaskASyncTask(this, new GTaskASyncTask.OnCompleteListener() { @@ -56,7 +55,7 @@ public class GTaskSyncService extends Service { mSyncTask.execute(); } } - // 取消同步 + private void cancelSync() { if (mSyncTask != null) { mSyncTask.cancelSync(); @@ -93,11 +92,11 @@ public class GTaskSyncService extends Service { mSyncTask.cancelSync(); } } - // 不支持绑定 + public IBinder onBind(Intent intent) { return null; } - // 发送同步进度广播 + public void sendBroadcast(String msg) { mSyncProgress = msg; Intent intent = new Intent(GTASK_SERVICE_BROADCAST_NAME); @@ -105,25 +104,25 @@ public class GTaskSyncService extends Service { intent.putExtra(GTASK_SERVICE_BROADCAST_PROGRESS_MSG, msg); sendBroadcast(intent); } - // 静态方法,开始同步任务 + public static void startSync(Activity activity) { GTaskManager.getInstance().setActivityContext(activity); Intent intent = new Intent(activity, GTaskSyncService.class); intent.putExtra(GTaskSyncService.ACTION_STRING_NAME, GTaskSyncService.ACTION_START_SYNC); activity.startService(intent); } - // 静态方法,取消同步任务 + public static void cancelSync(Context context) { - Intent intent = new Intent(context, GTaskSyncService.class);// 创建一个新的意图Intent,指向GTaskSyncService类,传入context参数 - intent.putExtra(GTaskSyncService.ACTION_STRING_NAME, GTaskSyncService.ACTION_CANCEL_SYNC);// 给Intent添加一个ACTION_CANCEL_SYNC的字符串Extra参数 + Intent intent = new Intent(context, GTaskSyncService.class); + intent.putExtra(GTaskSyncService.ACTION_STRING_NAME, GTaskSyncService.ACTION_CANCEL_SYNC); context.startService(intent); } - public static boolean isSyncing() {// 定义一个公共的静态方法isSyncing,返回值为布尔类型 + public static boolean isSyncing() { return mSyncTask != null; } - public static String getProgressString() {// 定义一个公共的静态方法getProgressString,返回值为字符串类型 + public static String getProgressString() { return mSyncProgress; } } diff --git a/doc/model/Note.java b/doc/model/Note.java new file mode 100644 index 0000000..6706cf6 --- /dev/null +++ b/doc/model/Note.java @@ -0,0 +1,253 @@ +/* + * 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.model; +import android.content.ContentProviderOperation; +import android.content.ContentProviderResult; +import android.content.ContentUris; +import android.content.ContentValues; +import android.content.Context; +import android.content.OperationApplicationException; +import android.net.Uri; +import android.os.RemoteException; +import android.util.Log; + +import net.micode.notes.data.Notes; +import net.micode.notes.data.Notes.CallNote; +import net.micode.notes.data.Notes.DataColumns; +import net.micode.notes.data.Notes.NoteColumns; +import net.micode.notes.data.Notes.TextNote; + +import java.util.ArrayList; + + +public class Note { + private ContentValues mNoteDiffValues; + private NoteData mNoteData; + private static final String TAG = "Note"; + /** + * Create a new note id for adding a new note to databases + */ + public static synchronized long getNewNoteId(Context context, long folderId) { + // Create a new note in the database + ContentValues values = new ContentValues(); + long createdTime = System.currentTimeMillis(); + values.put(NoteColumns.CREATED_DATE, createdTime); + values.put(NoteColumns.MODIFIED_DATE, createdTime); + values.put(NoteColumns.TYPE, Notes.TYPE_NOTE); + values.put(NoteColumns.LOCAL_MODIFIED, 1); + values.put(NoteColumns.PARENT_ID, folderId); + Uri uri = context.getContentResolver().insert(Notes.CONTENT_NOTE_URI, values); + + long noteId = 0; + try { + noteId = Long.valueOf(uri.getPathSegments().get(1)); + } catch (NumberFormatException e) { + Log.e(TAG, "Get note id error :" + e.toString()); + noteId = 0; + } + if (noteId == -1) { + throw new IllegalStateException("Wrong note id:" + noteId); + } + return noteId; + } + + public Note() { + mNoteDiffValues = new ContentValues(); + mNoteData = new NoteData(); + } + + public void setNoteValue(String key, String value) { + mNoteDiffValues.put(key, value); + mNoteDiffValues.put(NoteColumns.LOCAL_MODIFIED, 1); + mNoteDiffValues.put(NoteColumns.MODIFIED_DATE, System.currentTimeMillis()); + } + + public void setTextData(String key, String value) { + mNoteData.setTextData(key, value); + } + + public void setTextDataId(long id) { + mNoteData.setTextDataId(id); + } + + public long getTextDataId() { + return mNoteData.mTextDataId; + } + + public void setCallDataId(long id) { + mNoteData.setCallDataId(id); + } + + public void setCallData(String key, String value) { + mNoteData.setCallData(key, value); + } + + public boolean isLocalModified() { + return mNoteDiffValues.size() > 0 || mNoteData.isLocalModified(); + } + + public boolean syncNote(Context context, long noteId) { + if (noteId <= 0) { + throw new IllegalArgumentException("Wrong note id:" + noteId); + } + + if (!isLocalModified()) { + return true; + } + + /** + * In theory, once data changed, the note should be updated on {@link NoteColumns#LOCAL_MODIFIED} and + * {@link NoteColumns#MODIFIED_DATE}. For data safety, though update note fails, we also update the + * note data info + */ + if (context.getContentResolver().update( + ContentUris.withAppendedId(Notes.CONTENT_NOTE_URI, noteId), mNoteDiffValues, null, + null) == 0) { + Log.e(TAG, "Update note error, should not happen"); + // Do not return, fall through + } + mNoteDiffValues.clear(); + + if (mNoteData.isLocalModified() + && (mNoteData.pushIntoContentResolver(context, noteId) == null)) { + return false; + } + + return true; + } + + private class NoteData { + private long mTextDataId; + + private ContentValues mTextDataValues; + + private long mCallDataId; + + private ContentValues mCallDataValues; + + private static final String TAG = "NoteData"; + + public NoteData() { + mTextDataValues = new ContentValues(); + mCallDataValues = new ContentValues(); + mTextDataId = 0; + mCallDataId = 0; + } + + boolean isLocalModified() { + return mTextDataValues.size() > 0 || mCallDataValues.size() > 0; + } + + void setTextDataId(long id) { + if(id <= 0) { + throw new IllegalArgumentException("Text data id should larger than 0"); + } + mTextDataId = id; + } + + void setCallDataId(long id) { + if (id <= 0) { + throw new IllegalArgumentException("Call data id should larger than 0"); + } + mCallDataId = id; + } + + void setCallData(String key, String value) { + mCallDataValues.put(key, value); + mNoteDiffValues.put(NoteColumns.LOCAL_MODIFIED, 1); + mNoteDiffValues.put(NoteColumns.MODIFIED_DATE, System.currentTimeMillis()); + } + + void setTextData(String key, String value) { + mTextDataValues.put(key, value); + mNoteDiffValues.put(NoteColumns.LOCAL_MODIFIED, 1); + mNoteDiffValues.put(NoteColumns.MODIFIED_DATE, System.currentTimeMillis()); + } + + Uri pushIntoContentResolver(Context context, long noteId) { + /** + * Check for safety + */ + if (noteId <= 0) { + throw new IllegalArgumentException("Wrong note id:" + noteId); + } + + ArrayList operationList = new ArrayList(); + ContentProviderOperation.Builder builder = null; + + if(mTextDataValues.size() > 0) { + mTextDataValues.put(DataColumns.NOTE_ID, noteId); + if (mTextDataId == 0) { + mTextDataValues.put(DataColumns.MIME_TYPE, TextNote.CONTENT_ITEM_TYPE); + Uri uri = context.getContentResolver().insert(Notes.CONTENT_DATA_URI, + mTextDataValues); + try { + setTextDataId(Long.valueOf(uri.getPathSegments().get(1))); + } catch (NumberFormatException e) { + Log.e(TAG, "Insert new text data fail with noteId" + noteId); + mTextDataValues.clear(); + return null; + } + } else { + builder = ContentProviderOperation.newUpdate(ContentUris.withAppendedId( + Notes.CONTENT_DATA_URI, mTextDataId)); + builder.withValues(mTextDataValues); + operationList.add(builder.build()); + } + mTextDataValues.clear(); + } + + if(mCallDataValues.size() > 0) { + mCallDataValues.put(DataColumns.NOTE_ID, noteId); + if (mCallDataId == 0) { + mCallDataValues.put(DataColumns.MIME_TYPE, CallNote.CONTENT_ITEM_TYPE); + Uri uri = context.getContentResolver().insert(Notes.CONTENT_DATA_URI, + mCallDataValues); + try { + setCallDataId(Long.valueOf(uri.getPathSegments().get(1))); + } catch (NumberFormatException e) { + Log.e(TAG, "Insert new call data fail with noteId" + noteId); + mCallDataValues.clear(); + return null; + } + } else { + builder = ContentProviderOperation.newUpdate(ContentUris.withAppendedId( + Notes.CONTENT_DATA_URI, mCallDataId)); + builder.withValues(mCallDataValues); + operationList.add(builder.build()); + } + mCallDataValues.clear(); + } + + if (operationList.size() > 0) { + try { + ContentProviderResult[] results = context.getContentResolver().applyBatch( + Notes.AUTHORITY, operationList); + return (results == null || results.length == 0 || results[0] == null) ? null + : ContentUris.withAppendedId(Notes.CONTENT_NOTE_URI, noteId); + } catch (RemoteException e) { + Log.e(TAG, String.format("%s: %s", e.toString(), e.getMessage())); + return null; + } catch (OperationApplicationException e) { + Log.e(TAG, String.format("%s: %s", e.toString(), e.getMessage())); + return null; + } + } + return null; + } + } +} diff --git a/doc/model/WorkingNote.java b/doc/model/WorkingNote.java new file mode 100644 index 0000000..be081e4 --- /dev/null +++ b/doc/model/WorkingNote.java @@ -0,0 +1,368 @@ +/* + * 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.model; + +import android.appwidget.AppWidgetManager; +import android.content.ContentUris; +import android.content.Context; +import android.database.Cursor; +import android.text.TextUtils; +import android.util.Log; + +import net.micode.notes.data.Notes; +import net.micode.notes.data.Notes.CallNote; +import net.micode.notes.data.Notes.DataColumns; +import net.micode.notes.data.Notes.DataConstants; +import net.micode.notes.data.Notes.NoteColumns; +import net.micode.notes.data.Notes.TextNote; +import net.micode.notes.tool.ResourceParser.NoteBgResources; + + +public class WorkingNote { + // Note for the working note + private Note mNote; + // Note Id + private long mNoteId; + // Note content + private String mContent; + // Note mode + private int mMode; + + private long mAlertDate; + + private long mModifiedDate; + + private int mBgColorId; + + private int mWidgetId; + + private int mWidgetType; + + private long mFolderId; + + private Context mContext; + + private static final String TAG = "WorkingNote"; + + private boolean mIsDeleted; + + private NoteSettingChangedListener mNoteSettingStatusListener; + + public static final String[] DATA_PROJECTION = new String[] { + DataColumns.ID, + DataColumns.CONTENT, + DataColumns.MIME_TYPE, + DataColumns.DATA1, + DataColumns.DATA2, + DataColumns.DATA3, + DataColumns.DATA4, + }; + + public static final String[] NOTE_PROJECTION = new String[] { + NoteColumns.PARENT_ID, + NoteColumns.ALERTED_DATE, + NoteColumns.BG_COLOR_ID, + NoteColumns.WIDGET_ID, + NoteColumns.WIDGET_TYPE, + NoteColumns.MODIFIED_DATE + }; + + private static final int DATA_ID_COLUMN = 0; + + private static final int DATA_CONTENT_COLUMN = 1; + + private static final int DATA_MIME_TYPE_COLUMN = 2; + + private static final int DATA_MODE_COLUMN = 3; + + private static final int NOTE_PARENT_ID_COLUMN = 0; + + private static final int NOTE_ALERTED_DATE_COLUMN = 1; + + private static final int NOTE_BG_COLOR_ID_COLUMN = 2; + + private static final int NOTE_WIDGET_ID_COLUMN = 3; + + private static final int NOTE_WIDGET_TYPE_COLUMN = 4; + + private static final int NOTE_MODIFIED_DATE_COLUMN = 5; + + // New note construct + private WorkingNote(Context context, long folderId) { + mContext = context; + mAlertDate = 0; + mModifiedDate = System.currentTimeMillis(); + mFolderId = folderId; + mNote = new Note(); + mNoteId = 0; + mIsDeleted = false; + mMode = 0; + mWidgetType = Notes.TYPE_WIDGET_INVALIDE; + } + + // Existing note construct + private WorkingNote(Context context, long noteId, long folderId) { + mContext = context; + mNoteId = noteId; + mFolderId = folderId; + mIsDeleted = false; + mNote = new Note(); + loadNote(); + } + + private void loadNote() { + Cursor cursor = mContext.getContentResolver().query( + ContentUris.withAppendedId(Notes.CONTENT_NOTE_URI, mNoteId), NOTE_PROJECTION, null, + null, null); + + if (cursor != null) { + if (cursor.moveToFirst()) { + mFolderId = cursor.getLong(NOTE_PARENT_ID_COLUMN); + mBgColorId = cursor.getInt(NOTE_BG_COLOR_ID_COLUMN); + mWidgetId = cursor.getInt(NOTE_WIDGET_ID_COLUMN); + mWidgetType = cursor.getInt(NOTE_WIDGET_TYPE_COLUMN); + mAlertDate = cursor.getLong(NOTE_ALERTED_DATE_COLUMN); + mModifiedDate = cursor.getLong(NOTE_MODIFIED_DATE_COLUMN); + } + cursor.close(); + } else { + Log.e(TAG, "No note with id:" + mNoteId); + throw new IllegalArgumentException("Unable to find note with id " + mNoteId); + } + loadNoteData(); + } + + private void loadNoteData() { + Cursor cursor = mContext.getContentResolver().query(Notes.CONTENT_DATA_URI, DATA_PROJECTION, + DataColumns.NOTE_ID + "=?", new String[] { + String.valueOf(mNoteId) + }, null); + + if (cursor != null) { + if (cursor.moveToFirst()) { + do { + String type = cursor.getString(DATA_MIME_TYPE_COLUMN); + if (DataConstants.NOTE.equals(type)) { + mContent = cursor.getString(DATA_CONTENT_COLUMN); + mMode = cursor.getInt(DATA_MODE_COLUMN); + mNote.setTextDataId(cursor.getLong(DATA_ID_COLUMN)); + } else if (DataConstants.CALL_NOTE.equals(type)) { + mNote.setCallDataId(cursor.getLong(DATA_ID_COLUMN)); + } else { + Log.d(TAG, "Wrong note type with type:" + type); + } + } while (cursor.moveToNext()); + } + cursor.close(); + } else { + Log.e(TAG, "No data with id:" + mNoteId); + throw new IllegalArgumentException("Unable to find note's data with id " + mNoteId); + } + } + + public static WorkingNote createEmptyNote(Context context, long folderId, int widgetId, + int widgetType, int defaultBgColorId) { + WorkingNote note = new WorkingNote(context, folderId); + note.setBgColorId(defaultBgColorId); + note.setWidgetId(widgetId); + note.setWidgetType(widgetType); + return note; + } + + public static WorkingNote load(Context context, long id) { + return new WorkingNote(context, id, 0); + } + + public synchronized boolean saveNote() { + if (isWorthSaving()) { + if (!existInDatabase()) { + if ((mNoteId = Note.getNewNoteId(mContext, mFolderId)) == 0) { + Log.e(TAG, "Create new note fail with id:" + mNoteId); + return false; + } + } + + mNote.syncNote(mContext, mNoteId); + + /** + * 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; + } + } + + public boolean existInDatabase() { + return mNoteId > 0; + } + + private boolean isWorthSaving() { + if (mIsDeleted || (!existInDatabase() && TextUtils.isEmpty(mContent)) + || (existInDatabase() && !mNote.isLocalModified())) { + return false; + } else { + return true; + } + } + + public void setOnSettingStatusChangedListener(NoteSettingChangedListener l) { + mNoteSettingStatusListener = l; + } + + public void setAlertDate(long date, boolean set) { + if (date != mAlertDate) { + mAlertDate = date; + mNote.setNoteValue(NoteColumns.ALERTED_DATE, String.valueOf(mAlertDate)); + } + if (mNoteSettingStatusListener != null) { + mNoteSettingStatusListener.onClockAlertChanged(date, set); + } + } + + public void markDeleted(boolean mark) { + mIsDeleted = mark; + if (mWidgetId != AppWidgetManager.INVALID_APPWIDGET_ID + && mWidgetType != Notes.TYPE_WIDGET_INVALIDE && mNoteSettingStatusListener != null) { + mNoteSettingStatusListener.onWidgetChanged(); + } + } + + public void setBgColorId(int id) { + if (id != mBgColorId) { + mBgColorId = id; + if (mNoteSettingStatusListener != null) { + mNoteSettingStatusListener.onBackgroundColorChanged(); + } + mNote.setNoteValue(NoteColumns.BG_COLOR_ID, String.valueOf(id)); + } + } + + public void setCheckListMode(int mode) { + if (mMode != mode) { + if (mNoteSettingStatusListener != null) { + mNoteSettingStatusListener.onCheckListModeChanged(mMode, mode); + } + mMode = mode; + mNote.setTextData(TextNote.MODE, String.valueOf(mMode)); + } + } + + public void setWidgetType(int type) { + if (type != mWidgetType) { + mWidgetType = type; + mNote.setNoteValue(NoteColumns.WIDGET_TYPE, String.valueOf(mWidgetType)); + } + } + + public void setWidgetId(int id) { + if (id != mWidgetId) { + mWidgetId = id; + mNote.setNoteValue(NoteColumns.WIDGET_ID, String.valueOf(mWidgetId)); + } + } + + public void setWorkingText(String text) { + if (!TextUtils.equals(mContent, text)) { + mContent = text; + mNote.setTextData(DataColumns.CONTENT, mContent); + } + } + + public void convertToCallNote(String phoneNumber, long callDate) { + mNote.setCallData(CallNote.CALL_DATE, String.valueOf(callDate)); + mNote.setCallData(CallNote.PHONE_NUMBER, phoneNumber); + mNote.setNoteValue(NoteColumns.PARENT_ID, String.valueOf(Notes.ID_CALL_RECORD_FOLDER)); + } + + public boolean hasClockAlert() { + return (mAlertDate > 0 ? true : false); + } + + public String getContent() { + return mContent; + } + + public long getAlertDate() { + return mAlertDate; + } + + public long getModifiedDate() { + return mModifiedDate; + } + + public int getBgColorResId() { + return NoteBgResources.getNoteBgResource(mBgColorId); + } + + public int getBgColorId() { + return mBgColorId; + } + + public int getTitleBgResId() { + return NoteBgResources.getNoteTitleBgResource(mBgColorId); + } + + public int getCheckListMode() { + return mMode; + } + + public long getNoteId() { + return mNoteId; + } + + public long getFolderId() { + return mFolderId; + } + + public int getWidgetId() { + return mWidgetId; + } + + public int getWidgetType() { + return mWidgetType; + } + + public interface NoteSettingChangedListener { + /** + * Called when the background color of current note has just changed + */ + void onBackgroundColorChanged(); + + /** + * Called when user set clock + */ + void onClockAlertChanged(long date, boolean set); + + /** + * Call when user create note from widget + */ + void onWidgetChanged(); + + /** + * Call when switch between check list mode and normal mode + * @param oldMode is previous mode before change + * @param newMode is new mode + */ + void onCheckListModeChanged(int oldMode, int newMode); + } +} diff --git a/doc/tool/BackupUtils.java b/doc/tool/BackupUtils.java new file mode 100644 index 0000000..39f6ec4 --- /dev/null +++ b/doc/tool/BackupUtils.java @@ -0,0 +1,344 @@ +/* + * 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.tool; + +import android.content.Context; +import android.database.Cursor; +import android.os.Environment; +import android.text.TextUtils; +import android.text.format.DateFormat; +import android.util.Log; + +import net.micode.notes.R; +import net.micode.notes.data.Notes; +import net.micode.notes.data.Notes.DataColumns; +import net.micode.notes.data.Notes.DataConstants; +import net.micode.notes.data.Notes.NoteColumns; + +import java.io.File; +import java.io.FileNotFoundException; +import java.io.FileOutputStream; +import java.io.IOException; +import java.io.PrintStream; + + +public class BackupUtils { + private static final String TAG = "BackupUtils"; + // Singleton stuff + private static BackupUtils sInstance; + + public static synchronized BackupUtils getInstance(Context context) { + if (sInstance == null) { + sInstance = new BackupUtils(context); + } + return sInstance; + } + + /** + * Following states are signs to represents backup or restore + * status + */ + // Currently, the sdcard is not mounted + public static final int STATE_SD_CARD_UNMOUONTED = 0; + // The backup file not exist + public static final int STATE_BACKUP_FILE_NOT_EXIST = 1; + // The data is not well formated, may be changed by other programs + public static final int STATE_DATA_DESTROIED = 2; + // Some run-time exception which causes restore or backup fails + public static final int STATE_SYSTEM_ERROR = 3; + // Backup or restore success + public static final int STATE_SUCCESS = 4; + + private TextExport mTextExport; + + private BackupUtils(Context context) { + mTextExport = new TextExport(context); + } + + private static boolean externalStorageAvailable() { + return Environment.MEDIA_MOUNTED.equals(Environment.getExternalStorageState()); + } + + public int exportToText() { + return mTextExport.exportToText(); + } + + public String getExportedTextFileName() { + return mTextExport.mFileName; + } + + public String getExportedTextFileDir() { + return mTextExport.mFileDirectory; + } + + private static class TextExport { + private static final String[] NOTE_PROJECTION = { + NoteColumns.ID, + NoteColumns.MODIFIED_DATE, + NoteColumns.SNIPPET, + NoteColumns.TYPE + }; + + private static final int NOTE_COLUMN_ID = 0; + + private static final int NOTE_COLUMN_MODIFIED_DATE = 1; + + private static final int NOTE_COLUMN_SNIPPET = 2; + + private static final String[] DATA_PROJECTION = { + DataColumns.CONTENT, + DataColumns.MIME_TYPE, + DataColumns.DATA1, + DataColumns.DATA2, + DataColumns.DATA3, + DataColumns.DATA4, + }; + + private static final int DATA_COLUMN_CONTENT = 0; + + private static final int DATA_COLUMN_MIME_TYPE = 1; + + private static final int DATA_COLUMN_CALL_DATE = 2; + + private static final int DATA_COLUMN_PHONE_NUMBER = 4; + + private final String [] TEXT_FORMAT; + private static final int FORMAT_FOLDER_NAME = 0; + private static final int FORMAT_NOTE_DATE = 1; + private static final int FORMAT_NOTE_CONTENT = 2; + + private Context mContext; + private String mFileName; + private String mFileDirectory; + + public TextExport(Context context) { + TEXT_FORMAT = context.getResources().getStringArray(R.array.format_for_exported_note); + mContext = context; + mFileName = ""; + mFileDirectory = ""; + } + + private String getFormat(int id) { + return TEXT_FORMAT[id]; + } + + /** + * Export the folder identified by folder id to text + */ + private void exportFolderToText(String folderId, PrintStream ps) { + // Query notes belong to this folder + Cursor notesCursor = mContext.getContentResolver().query(Notes.CONTENT_NOTE_URI, + NOTE_PROJECTION, NoteColumns.PARENT_ID + "=?", new String[] { + folderId + }, null); + + if (notesCursor != null) { + if (notesCursor.moveToFirst()) { + do { + // Print note's last modified date + ps.println(String.format(getFormat(FORMAT_NOTE_DATE), DateFormat.format( + mContext.getString(R.string.format_datetime_mdhm), + notesCursor.getLong(NOTE_COLUMN_MODIFIED_DATE)))); + // Query data belong to this note + String noteId = notesCursor.getString(NOTE_COLUMN_ID); + exportNoteToText(noteId, ps); + } while (notesCursor.moveToNext()); + } + notesCursor.close(); + } + } + + /** + * Export note identified by id to a print stream + */ + private void exportNoteToText(String noteId, PrintStream ps) { + Cursor dataCursor = mContext.getContentResolver().query(Notes.CONTENT_DATA_URI, + DATA_PROJECTION, DataColumns.NOTE_ID + "=?", new String[] { + noteId + }, null); + + if (dataCursor != null) { + if (dataCursor.moveToFirst()) { + do { + String mimeType = dataCursor.getString(DATA_COLUMN_MIME_TYPE); + if (DataConstants.CALL_NOTE.equals(mimeType)) { + // Print phone number + String phoneNumber = dataCursor.getString(DATA_COLUMN_PHONE_NUMBER); + long callDate = dataCursor.getLong(DATA_COLUMN_CALL_DATE); + String location = dataCursor.getString(DATA_COLUMN_CONTENT); + + if (!TextUtils.isEmpty(phoneNumber)) { + ps.println(String.format(getFormat(FORMAT_NOTE_CONTENT), + phoneNumber)); + } + // Print call date + ps.println(String.format(getFormat(FORMAT_NOTE_CONTENT), DateFormat + .format(mContext.getString(R.string.format_datetime_mdhm), + callDate))); + // Print call attachment location + if (!TextUtils.isEmpty(location)) { + ps.println(String.format(getFormat(FORMAT_NOTE_CONTENT), + location)); + } + } else if (DataConstants.NOTE.equals(mimeType)) { + String content = dataCursor.getString(DATA_COLUMN_CONTENT); + if (!TextUtils.isEmpty(content)) { + ps.println(String.format(getFormat(FORMAT_NOTE_CONTENT), + content)); + } + } + } while (dataCursor.moveToNext()); + } + dataCursor.close(); + } + // print a line separator between note + try { + ps.write(new byte[] { + Character.LINE_SEPARATOR, Character.LETTER_NUMBER + }); + } catch (IOException e) { + Log.e(TAG, e.toString()); + } + } + + /** + * Note will be exported as text which is user readable + */ + public int exportToText() { + if (!externalStorageAvailable()) { + Log.d(TAG, "Media was not mounted"); + return STATE_SD_CARD_UNMOUONTED; + } + + PrintStream ps = getExportToTextPrintStream(); + if (ps == null) { + Log.e(TAG, "get print stream error"); + return STATE_SYSTEM_ERROR; + } + // First export folder and its notes + Cursor folderCursor = mContext.getContentResolver().query( + Notes.CONTENT_NOTE_URI, + NOTE_PROJECTION, + "(" + NoteColumns.TYPE + "=" + Notes.TYPE_FOLDER + " AND " + + NoteColumns.PARENT_ID + "<>" + Notes.ID_TRASH_FOLER + ") OR " + + NoteColumns.ID + "=" + Notes.ID_CALL_RECORD_FOLDER, null, null); + + if (folderCursor != null) { + if (folderCursor.moveToFirst()) { + do { + // Print folder's name + String folderName = ""; + if(folderCursor.getLong(NOTE_COLUMN_ID) == Notes.ID_CALL_RECORD_FOLDER) { + folderName = mContext.getString(R.string.call_record_folder_name); + } else { + folderName = folderCursor.getString(NOTE_COLUMN_SNIPPET); + } + if (!TextUtils.isEmpty(folderName)) { + ps.println(String.format(getFormat(FORMAT_FOLDER_NAME), folderName)); + } + String folderId = folderCursor.getString(NOTE_COLUMN_ID); + exportFolderToText(folderId, ps); + } while (folderCursor.moveToNext()); + } + folderCursor.close(); + } + + // Export notes in root's folder + Cursor noteCursor = mContext.getContentResolver().query( + Notes.CONTENT_NOTE_URI, + NOTE_PROJECTION, + NoteColumns.TYPE + "=" + +Notes.TYPE_NOTE + " AND " + NoteColumns.PARENT_ID + + "=0", null, null); + + if (noteCursor != null) { + if (noteCursor.moveToFirst()) { + do { + ps.println(String.format(getFormat(FORMAT_NOTE_DATE), DateFormat.format( + mContext.getString(R.string.format_datetime_mdhm), + noteCursor.getLong(NOTE_COLUMN_MODIFIED_DATE)))); + // Query data belong to this note + String noteId = noteCursor.getString(NOTE_COLUMN_ID); + exportNoteToText(noteId, ps); + } while (noteCursor.moveToNext()); + } + noteCursor.close(); + } + ps.close(); + + return STATE_SUCCESS; + } + + /** + * Get a print stream pointed to the file {@generateExportedTextFile} + */ + private PrintStream getExportToTextPrintStream() { + File file = generateFileMountedOnSDcard(mContext, R.string.file_path, + R.string.file_name_txt_format); + if (file == null) { + Log.e(TAG, "create file to exported failed"); + return null; + } + mFileName = file.getName(); + mFileDirectory = mContext.getString(R.string.file_path); + PrintStream ps = null; + try { + FileOutputStream fos = new FileOutputStream(file); + ps = new PrintStream(fos); + } catch (FileNotFoundException e) { + e.printStackTrace(); + return null; + } catch (NullPointerException e) { + e.printStackTrace(); + return null; + } + return ps; + } + } + + /** + * Generate the text file to store imported data + */ + private static File generateFileMountedOnSDcard(Context context, int filePathResId, int fileNameFormatResId) { + StringBuilder sb = new StringBuilder(); + sb.append(Environment.getExternalStorageDirectory()); + sb.append(context.getString(filePathResId)); + File filedir = new File(sb.toString()); + sb.append(context.getString( + fileNameFormatResId, + DateFormat.format(context.getString(R.string.format_date_ymd), + System.currentTimeMillis()))); + File file = new File(sb.toString()); + + try { + if (!filedir.exists()) { + filedir.mkdir(); + } + if (!file.exists()) { + file.createNewFile(); + } + return file; + } catch (SecurityException e) { + e.printStackTrace(); + } catch (IOException e) { + e.printStackTrace(); + } + + return null; + } +} + + diff --git a/other/07_陈七源_代码标注/tool/DataUtils.java b/doc/tool/DataUtils.java similarity index 59% rename from other/07_陈七源_代码标注/tool/DataUtils.java rename to doc/tool/DataUtils.java index a9a3030..2a14982 100644 --- a/other/07_陈七源_代码标注/tool/DataUtils.java +++ b/doc/tool/DataUtils.java @@ -37,16 +37,7 @@ import java.util.HashSet; public class DataUtils { public static final String TAG = "DataUtils"; - - /** - * 批量删除笔记方法 - * - * @param resolver ContentResolver对象 - * @param ids HashSet类型,需要删除的笔记id集合 - * @return boolean类型,表示是否删除成功 - */ public static boolean batchDeleteNotes(ContentResolver resolver, HashSet ids) { - // 如果ids为空或者size为0,则直接返回true if (ids == null) { Log.d(TAG, "the ids is null"); return true; @@ -56,119 +47,74 @@ public class DataUtils { return true; } - // 创建操作列表 ArrayList operationList = new ArrayList(); for (long id : ids) { - // 不包括系统文件夹根节点 if(id == Notes.ID_ROOT_FOLDER) { Log.e(TAG, "Don't delete system folder root"); continue; } - // 创建ContentProviderOperation.Builder对象来构建删除该id的操作,并将其添加到operationList中 ContentProviderOperation.Builder builder = ContentProviderOperation .newDelete(ContentUris.withAppendedId(Notes.CONTENT_NOTE_URI, id)); operationList.add(builder.build()); } try { - // 通过ContentResolver的applyBatch()方法将所有操作提交给ContentProvider执行 ContentProviderResult[] results = resolver.applyBatch(Notes.AUTHORITY, operationList); if (results == null || results.length == 0 || results[0] == null) { Log.d(TAG, "delete notes failed, ids:" + ids.toString()); return false; } - // 如果成功则返回true return true; } catch (RemoteException e) { Log.e(TAG, String.format("%s: %s", e.toString(), e.getMessage())); } catch (OperationApplicationException e) { Log.e(TAG, String.format("%s: %s", e.toString(), e.getMessage())); } - // 失败则返回false return false; } - - - /** - * 将笔记移动到指定文件夹下 - * - * @param resolver ContentResolver对象 - * @param id 需要移动的笔记id - * @param srcFolderId 原文件夹id - * @param desFolderId 目标文件夹id - */ public static void moveNoteToFoler(ContentResolver resolver, long id, long srcFolderId, long desFolderId) { - // 创建ContentValues对象,用于存放需要更新的字段和值 ContentValues values = new ContentValues(); - // 更新Parent_Id字段为目标文件夹id values.put(NoteColumns.PARENT_ID, desFolderId); - // 更新Origin_Parent_Id字段为原文件夹id values.put(NoteColumns.ORIGIN_PARENT_ID, srcFolderId); - // 更新Local_Modified字段为1 values.put(NoteColumns.LOCAL_MODIFIED, 1); - - // 调用ContentResolver的update()方法来更新数据 resolver.update(ContentUris.withAppendedId(Notes.CONTENT_NOTE_URI, id), values, null, null); } - /** - * 批量将笔记移动到指定文件夹下 - * - * @param resolver ContentResolver对象 - * @param ids HashSet类型,需要移动的笔记id集合 - * @param folderId 目标文件夹id - * @return boolean类型,表示是否移动成功 - */ - public static boolean batchMoveToFolder(ContentResolver resolver, HashSet ids, long folderId) { - // 如果ids为空,则直接返回true + public static boolean batchMoveToFolder(ContentResolver resolver, HashSet ids, + long folderId) { if (ids == null) { Log.d(TAG, "the ids is null"); return true; } - // 创建操作列表 ArrayList operationList = new ArrayList(); for (long id : ids) { - // 创建ContentProviderOperation.Builder对象来构建更新该id的操作,并将其添加到operationList中 ContentProviderOperation.Builder builder = ContentProviderOperation .newUpdate(ContentUris.withAppendedId(Notes.CONTENT_NOTE_URI, id)); - // 更新Parent_Id字段为目标文件夹id builder.withValue(NoteColumns.PARENT_ID, folderId); - // 更新Local_Modified字段为1 builder.withValue(NoteColumns.LOCAL_MODIFIED, 1); operationList.add(builder.build()); } try { - // 通过ContentResolver的applyBatch()方法将所有操作提交给ContentProvider执行 ContentProviderResult[] results = resolver.applyBatch(Notes.AUTHORITY, operationList); if (results == null || results.length == 0 || results[0] == null) { - Log.d(TAG, "move notes failed, ids:" + ids.toString()); + Log.d(TAG, "delete notes failed, ids:" + ids.toString()); return false; } - // 如果成功则返回true return true; } catch (RemoteException e) { Log.e(TAG, String.format("%s: %s", e.toString(), e.getMessage())); } catch (OperationApplicationException e) { Log.e(TAG, String.format("%s: %s", e.toString(), e.getMessage())); } - // 失败则返回false return false; } - /** * Get the all folder count except system folders {@link Notes#TYPE_SYSTEM}} */ - /** - * 获取用户自定义文件夹数量 - * - * @param resolver ContentResolver对象 - * @return int类型,表示用户自定义文件夹数量 - */ public static int getUserFolderCount(ContentResolver resolver) { - // 查询ContentProvider中的所有符合条件的数据,并返回查询结果Cursor对象 Cursor cursor =resolver.query(Notes.CONTENT_NOTE_URI, new String[] { "COUNT(*)" }, NoteColumns.TYPE + "=? AND " + NoteColumns.PARENT_ID + "<>?", @@ -179,30 +125,18 @@ public class DataUtils { if(cursor != null) { if(cursor.moveToFirst()) { try { - // 从Cursor对象中获取查询结果中count字段的值 count = cursor.getInt(0); } catch (IndexOutOfBoundsException e) { Log.e(TAG, "get folder count failed:" + e.toString()); } finally { - // 关闭游标 cursor.close(); } } } - // 返回用户自定义文件夹数量 return count; } - /** - * 查询指定id的笔记是否存在于ContentProvider中 - * - * @param resolver ContentResolver对象 - * @param noteId 需要查询的笔记id - * @param type 笔记类型,用于限定查询结果的笔记类型,默认为0表示查询所有类型的笔记 - * @return boolean类型,表示指定id的笔记是否存在于ContentProvider中 - */ public static boolean visibleInNoteDatabase(ContentResolver resolver, long noteId, int type) { - // 查询ContentProvider中指定id的笔记是否存在,并返回查询结果Cursor对象 Cursor cursor = resolver.query(ContentUris.withAppendedId(Notes.CONTENT_NOTE_URI, noteId), null, NoteColumns.TYPE + "=? AND " + NoteColumns.PARENT_ID + "<>" + Notes.ID_TRASH_FOLER, @@ -214,23 +148,12 @@ public class DataUtils { if (cursor.getCount() > 0) { exist = true; } - // 关闭游标 cursor.close(); } - // 返回指定id的笔记是否存在于ContentProvider中 return exist; } - - /** - * 查询指定id的笔记是否存在于ContentProvider中 - * - * @param resolver ContentResolver对象 - * @param noteId 需要查询的笔记id - * @return boolean类型,表示指定id的笔记是否存在于ContentProvider中 - */ public static boolean existInNoteDatabase(ContentResolver resolver, long noteId) { - // 查询ContentProvider中指定id的笔记是否存在,并返回查询结果Cursor对象 Cursor cursor = resolver.query(ContentUris.withAppendedId(Notes.CONTENT_NOTE_URI, noteId), null, null, null, null); @@ -239,22 +162,12 @@ public class DataUtils { if (cursor.getCount() > 0) { exist = true; } - // 关闭游标 cursor.close(); } - // 返回指定id的笔记是否存在于ContentProvider中 return exist; } - /** - * 查询指定id的数据是否存在于ContentProvider中 - * - * @param resolver ContentResolver对象 - * @param dataId 需要查询的数据id - * @return boolean类型,表示指定id的数据是否存在于ContentProvider中 - */ public static boolean existInDataDatabase(ContentResolver resolver, long dataId) { - // 查询ContentProvider中指定id的数据是否存在,并返回查询结果Cursor对象 Cursor cursor = resolver.query(ContentUris.withAppendedId(Notes.CONTENT_DATA_URI, dataId), null, null, null, null); @@ -263,48 +176,28 @@ public class DataUtils { if (cursor.getCount() > 0) { exist = true; } - // 关闭游标 cursor.close(); } - // 返回指定id的数据是否存在于ContentProvider中 return exist; } - /** - * 检查指定名称的文件夹是否存在于ContentProvider中 - * - * @param resolver ContentResolver对象 - * @param name 需要检查的文件夹名称 - * @return boolean类型,表示指定名称的文件夹是否存在于ContentProvider中 - */ public static boolean checkVisibleFolderName(ContentResolver resolver, String name) { - // 查询ContentProvider中指定名称的文件夹是否存在,并返回查询结果Cursor对象 Cursor cursor = resolver.query(Notes.CONTENT_NOTE_URI, null, NoteColumns.TYPE + "=" + Notes.TYPE_FOLDER + - " AND " + NoteColumns.PARENT_ID + "<>" + Notes.ID_TRASH_FOLER + - " AND " + NoteColumns.SNIPPET + "=?", + " AND " + NoteColumns.PARENT_ID + "<>" + Notes.ID_TRASH_FOLER + + " AND " + NoteColumns.SNIPPET + "=?", new String[] { name }, null); boolean exist = false; if(cursor != null) { if(cursor.getCount() > 0) { exist = true; } - // 关闭游标 cursor.close(); } - // 返回指定名称的文件夹是否存在于ContentProvider中 return exist; } - /** - * 获取指定文件夹下的所有Widget信息 - * - * @param resolver ContentResolver对象 - * @param folderId 需要查询的文件夹id - * @return HashSet类型,表示指定文件夹下的所有Widget信息 - */ public static HashSet getFolderNoteWidget(ContentResolver resolver, long folderId) { - // 查询ContentProvider中指定文件夹下的所有Widget信息,并返回查询结果Cursor对象 Cursor c = resolver.query(Notes.CONTENT_NOTE_URI, new String[] { NoteColumns.WIDGET_ID, NoteColumns.WIDGET_TYPE }, NoteColumns.PARENT_ID + "=?", @@ -317,7 +210,6 @@ public class DataUtils { set = new HashSet(); do { try { - // 从Cursor对象中获取查询结果中Widget的id和类型,并将其封装到AppWidgetAttribute对象中 AppWidgetAttribute widget = new AppWidgetAttribute(); widget.widgetId = c.getInt(0); widget.widgetType = c.getInt(1); @@ -327,23 +219,12 @@ public class DataUtils { } } while (c.moveToNext()); } - // 关闭游标 c.close(); } - // 返回指定文件夹下的所有Widget信息 return set; } - - /** - * 根据笔记id获取该笔记对应的电话号码 - * - * @param resolver ContentResolver对象 - * @param noteId 需要查询的笔记id - * @return String类型,表示该笔记对应的电话号码 - */ public static String getCallNumberByNoteId(ContentResolver resolver, long noteId) { - // 查询ContentProvider中指定笔记id对应的电话号码,并返回查询结果Cursor对象 Cursor cursor = resolver.query(Notes.CONTENT_DATA_URI, new String [] { CallNote.PHONE_NUMBER }, CallNote.NOTE_ID + "=? AND " + CallNote.MIME_TYPE + "=?", @@ -356,28 +237,17 @@ public class DataUtils { } catch (IndexOutOfBoundsException e) { Log.e(TAG, "Get call number fails " + e.toString()); } finally { - // 关闭游标 cursor.close(); } } - // 如果查询失败则返回空字符串 return ""; } - /** - * 根据电话号码和通话日期获取该通话对应的笔记id - * - * @param resolver ContentResolver对象 - * @param phoneNumber 需要查询的电话号码 - * @param callDate 需要查询的通话日期 - * @return long类型,表示该通话对应的笔记id - */ public static long getNoteIdByPhoneNumberAndCallDate(ContentResolver resolver, String phoneNumber, long callDate) { - // 查询ContentProvider中指定电话号码和通话日期对应的笔记id,并返回查询结果Cursor对象 Cursor cursor = resolver.query(Notes.CONTENT_DATA_URI, new String [] { CallNote.NOTE_ID }, CallNote.CALL_DATE + "=? AND " + CallNote.MIME_TYPE + "=? AND PHONE_NUMBERS_EQUAL(" - + CallNote.PHONE_NUMBER + ",?)", + + CallNote.PHONE_NUMBER + ",?)", new String [] { String.valueOf(callDate), CallNote.CONTENT_ITEM_TYPE, phoneNumber }, null); @@ -389,23 +259,12 @@ public class DataUtils { Log.e(TAG, "Get call note id fails " + e.toString()); } } - // 关闭游标 cursor.close(); } - // 如果查询失败则返回0 return 0; } - - /** - * 根据笔记id获取该笔记的摘要信息 - * - * @param resolver ContentResolver对象 - * @param noteId 需要查询的笔记id - * @return String类型,表示该笔记的摘要信息 - */ public static String getSnippetById(ContentResolver resolver, long noteId) { - // 查询ContentProvider中指定笔记id对应的摘要信息,并返回查询结果Cursor对象 Cursor cursor = resolver.query(Notes.CONTENT_NOTE_URI, new String [] { NoteColumns.SNIPPET }, NoteColumns.ID + "=?", @@ -417,29 +276,20 @@ public class DataUtils { if (cursor.moveToFirst()) { snippet = cursor.getString(0); } - // 关闭游标 cursor.close(); return snippet; } - // 如果查询失败则抛出异常 throw new IllegalArgumentException("Note is not found with id: " + noteId); } - /** - * 对给定的摘要信息进行格式化 - * - * @param snippet 需要格式化的摘要信息 - * @return String类型,表示格式化后的摘要信息 - */ public static String getFormattedSnippet(String snippet) { if (snippet != null) { - snippet = snippet.trim(); // 去除字符串两端的空格 - int index = snippet.indexOf('\n'); // 查找第一个换行符位置 + snippet = snippet.trim(); + int index = snippet.indexOf('\n'); if (index != -1) { - snippet = snippet.substring(0, index); // 截取换行符之前的部分作为摘要信息 + snippet = snippet.substring(0, index); } } return snippet; } - } diff --git a/other/07_陈七源_代码标注/tool/GTaskStringUtils.java b/doc/tool/GTaskStringUtils.java similarity index 100% rename from other/07_陈七源_代码标注/tool/GTaskStringUtils.java rename to doc/tool/GTaskStringUtils.java diff --git a/doc/tool/ResourceParser.java b/doc/tool/ResourceParser.java new file mode 100644 index 0000000..1ad3ad6 --- /dev/null +++ b/doc/tool/ResourceParser.java @@ -0,0 +1,181 @@ +/* + * 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.tool; + +import android.content.Context; +import android.preference.PreferenceManager; + +import net.micode.notes.R; +import net.micode.notes.ui.NotesPreferenceActivity; + +public class ResourceParser { + + public static final int YELLOW = 0; + public static final int BLUE = 1; + public static final int WHITE = 2; + public static final int GREEN = 3; + public static final int RED = 4; + + public static final int BG_DEFAULT_COLOR = YELLOW; + + public static final int TEXT_SMALL = 0; + public static final int TEXT_MEDIUM = 1; + public static final int TEXT_LARGE = 2; + public static final int TEXT_SUPER = 3; + + public static final int BG_DEFAULT_FONT_SIZE = TEXT_MEDIUM; + + public static class NoteBgResources { + private final static int [] BG_EDIT_RESOURCES = new int [] { + R.drawable.edit_yellow, + R.drawable.edit_blue, + R.drawable.edit_white, + R.drawable.edit_green, + R.drawable.edit_red + }; + + private final static int [] BG_EDIT_TITLE_RESOURCES = new int [] { + R.drawable.edit_title_yellow, + R.drawable.edit_title_blue, + R.drawable.edit_title_white, + R.drawable.edit_title_green, + R.drawable.edit_title_red + }; + + public static int getNoteBgResource(int id) { + return BG_EDIT_RESOURCES[id]; + } + + public static int getNoteTitleBgResource(int id) { + return BG_EDIT_TITLE_RESOURCES[id]; + } + } + + public static int getDefaultBgId(Context context) { + if (PreferenceManager.getDefaultSharedPreferences(context).getBoolean( + NotesPreferenceActivity.PREFERENCE_SET_BG_COLOR_KEY, false)) { + return (int) (Math.random() * NoteBgResources.BG_EDIT_RESOURCES.length); + } else { + return BG_DEFAULT_COLOR; + } + } + + public static class NoteItemBgResources { + private final static int [] BG_FIRST_RESOURCES = new int [] { + R.drawable.list_yellow_up, + R.drawable.list_blue_up, + R.drawable.list_white_up, + R.drawable.list_green_up, + R.drawable.list_red_up + }; + + private final static int [] BG_NORMAL_RESOURCES = new int [] { + R.drawable.list_yellow_middle, + R.drawable.list_blue_middle, + R.drawable.list_white_middle, + R.drawable.list_green_middle, + R.drawable.list_red_middle + }; + + private final static int [] BG_LAST_RESOURCES = new int [] { + R.drawable.list_yellow_down, + R.drawable.list_blue_down, + R.drawable.list_white_down, + R.drawable.list_green_down, + R.drawable.list_red_down, + }; + + private final static int [] BG_SINGLE_RESOURCES = new int [] { + R.drawable.list_yellow_single, + R.drawable.list_blue_single, + R.drawable.list_white_single, + R.drawable.list_green_single, + R.drawable.list_red_single + }; + + public static int getNoteBgFirstRes(int id) { + return BG_FIRST_RESOURCES[id]; + } + + public static int getNoteBgLastRes(int id) { + return BG_LAST_RESOURCES[id]; + } + + public static int getNoteBgSingleRes(int id) { + return BG_SINGLE_RESOURCES[id]; + } + + public static int getNoteBgNormalRes(int id) { + return BG_NORMAL_RESOURCES[id]; + } + + public static int getFolderBgRes() { + return R.drawable.list_folder; + } + } + + public static class WidgetBgResources { + private final static int [] BG_2X_RESOURCES = new int [] { + R.drawable.widget_2x_yellow, + R.drawable.widget_2x_blue, + R.drawable.widget_2x_white, + R.drawable.widget_2x_green, + R.drawable.widget_2x_red, + }; + + public static int getWidget2xBgResource(int id) { + return BG_2X_RESOURCES[id]; + } + + private final static int [] BG_4X_RESOURCES = new int [] { + R.drawable.widget_4x_yellow, + R.drawable.widget_4x_blue, + R.drawable.widget_4x_white, + R.drawable.widget_4x_green, + R.drawable.widget_4x_red + }; + + public static int getWidget4xBgResource(int id) { + return BG_4X_RESOURCES[id]; + } + } + + public static class TextAppearanceResources { + private final static int [] TEXTAPPEARANCE_RESOURCES = new int [] { + R.style.TextAppearanceNormal, + R.style.TextAppearanceMedium, + R.style.TextAppearanceLarge, + R.style.TextAppearanceSuper + }; + + public static int getTexAppearanceResource(int id) { + /** + * HACKME: Fix bug of store the resource id in shared preference. + * The id may larger than the length of resources, in this case, + * return the {@link ResourceParser#BG_DEFAULT_FONT_SIZE} + */ + if (id >= TEXTAPPEARANCE_RESOURCES.length) { + return BG_DEFAULT_FONT_SIZE; + } + return TEXTAPPEARANCE_RESOURCES[id]; + } + + public static int getResourcesSize() { + return TEXTAPPEARANCE_RESOURCES.length; + } + } +} diff --git a/other/07_陈七源_代码标注/ui/AlarmAlertActivity.java b/doc/ui/AlarmAlertActivity.java similarity index 72% rename from other/07_陈七源_代码标注/ui/AlarmAlertActivity.java rename to doc/ui/AlarmAlertActivity.java index d9505d2..85723be 100644 --- a/other/07_陈七源_代码标注/ui/AlarmAlertActivity.java +++ b/doc/ui/AlarmAlertActivity.java @@ -41,24 +41,9 @@ import java.io.IOException; public class AlarmAlertActivity extends Activity implements OnClickListener, OnDismissListener { - /** - * 当前笔记的 ID - */ private long mNoteId; - - /** - * 笔记摘录,用于在对话框中显示当前笔记的内容 - */ private String mSnippet; - - /** - * 笔记摘录预览的最大长度 - */ private static final int SNIPPET_PREW_MAX_LEN = 60; - - /** - * 媒体播放器,用于播放提醒音 - */ MediaPlayer mPlayer; @Override @@ -66,11 +51,9 @@ public class AlarmAlertActivity extends Activity implements OnClickListener, OnD super.onCreate(savedInstanceState); requestWindowFeature(Window.FEATURE_NO_TITLE); - // 将 Activity 显示在锁屏界面上 final Window win = getWindow(); win.addFlags(WindowManager.LayoutParams.FLAG_SHOW_WHEN_LOCKED); - // 如果屏幕还未点亮,保持屏幕常亮并将屏幕点亮 if (!isScreenOn()) { win.addFlags(WindowManager.LayoutParams.FLAG_KEEP_SCREEN_ON | WindowManager.LayoutParams.FLAG_TURN_SCREEN_ON @@ -78,23 +61,19 @@ public class AlarmAlertActivity extends Activity implements OnClickListener, OnD | WindowManager.LayoutParams.FLAG_LAYOUT_INSET_DECOR); } - // 获取 intent 中传递的笔记 ID,并根据 ID 获取笔记摘录 Intent intent = getIntent(); + try { mNoteId = Long.valueOf(intent.getData().getPathSegments().get(1)); mSnippet = DataUtils.getSnippetById(this.getContentResolver(), mNoteId); - - // 将笔记摘录截取到预设的最大长度,并添加省略号 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) { - // 如果 intent 中未传递有效的笔记 ID,则打印异常并退出 Activity e.printStackTrace(); return; } - // 创建媒体播放器,并在数据库中找到当前笔记,如果存在则显示提醒对话框并播放提醒音 mPlayer = new MediaPlayer(); if (DataUtils.visibleInNoteDatabase(getContentResolver(), mNoteId, Notes.TYPE_NOTE)) { showActionDialog(); @@ -104,82 +83,56 @@ public class AlarmAlertActivity extends Activity implements OnClickListener, OnD } } - - /** - * 判断屏幕是否点亮 - * - * @return 如果屏幕点亮则返回 true,否则返回 false - */ private boolean isScreenOn() { PowerManager pm = (PowerManager) getSystemService(Context.POWER_SERVICE); return pm.isScreenOn(); } - /** - * 播放提醒音 - */ private void playAlarmSound() { - // 获取默认的闹钟铃声 Uri Uri url = RingtoneManager.getActualDefaultRingtoneUri(this, RingtoneManager.TYPE_ALARM); - // 获取静音模式下允许播放的音频流类型 int silentModeStreams = Settings.System.getInt(getContentResolver(), Settings.System.MODE_RINGER_STREAMS_AFFECTED, 0); - // 根据静音模式设置媒体播放器的音频流类型 if ((silentModeStreams & (1 << AudioManager.STREAM_ALARM)) != 0) { mPlayer.setAudioStreamType(silentModeStreams); } else { mPlayer.setAudioStreamType(AudioManager.STREAM_ALARM); } - - // 设置媒体播放器的数据源,并开始播放 try { mPlayer.setDataSource(this, url); mPlayer.prepare(); mPlayer.setLooping(true); mPlayer.start(); } catch (IllegalArgumentException e) { + // TODO Auto-generated catch block e.printStackTrace(); } catch (SecurityException e) { + // TODO Auto-generated catch block e.printStackTrace(); } catch (IllegalStateException e) { + // TODO Auto-generated catch block e.printStackTrace(); } catch (IOException e) { + // TODO Auto-generated catch block e.printStackTrace(); } } - /** - * 显示提醒对话框 - */ private void showActionDialog() { - // 构建对话框,并设置标题、内容和两个按钮(确认和进入) AlertDialog.Builder dialog = new AlertDialog.Builder(this); dialog.setTitle(R.string.app_name); dialog.setMessage(mSnippet); dialog.setPositiveButton(R.string.notealert_ok, this); - - // 如果屏幕点亮,则添加“进入”按钮 if (isScreenOn()) { dialog.setNegativeButton(R.string.notealert_enter, this); } - - // 显示对话框,并设置对话框关闭时的监听器 dialog.show().setOnDismissListener(this); } - /** - * 处理对话框按钮的点击事件 - * - * @param dialog 点击的对话框 - * @param which 点击的按钮类型(确定或取消) - */ public void onClick(DialogInterface dialog, int which) { - // 根据不同的按钮类型执行不同的操作 switch (which) { case DialogInterface.BUTTON_NEGATIVE: - // 如果点击了“进入”按钮,则打开笔记编辑界面并查看当前笔记 Intent intent = new Intent(this, NoteEditActivity.class); intent.setAction(Intent.ACTION_VIEW); intent.putExtra(Intent.EXTRA_UID, mNoteId); @@ -190,28 +143,16 @@ public class AlarmAlertActivity extends Activity implements OnClickListener, OnD } } - - /** - * 对话框关闭时的回调方法 - * - * @param dialog 对话框 - */ public void onDismiss(DialogInterface dialog) { - // 停止播放提醒音并关闭 Activity stopAlarmSound(); finish(); } - /** - * 停止播放提醒音 - */ private void stopAlarmSound() { - // 如果媒体播放器不为 null,则停止播放并释放资源 if (mPlayer != null) { mPlayer.stop(); mPlayer.release(); mPlayer = null; } } - } diff --git a/other/07_陈七源_代码标注/ui/AlarmInitReceiver.java b/doc/ui/AlarmInitReceiver.java similarity index 69% rename from other/07_陈七源_代码标注/ui/AlarmInitReceiver.java rename to doc/ui/AlarmInitReceiver.java index 582b421..f221202 100644 --- a/other/07_陈七源_代码标注/ui/AlarmInitReceiver.java +++ b/doc/ui/AlarmInitReceiver.java @@ -30,58 +30,36 @@ import net.micode.notes.data.Notes.NoteColumns; public class AlarmInitReceiver extends BroadcastReceiver { - /** - * 查询提醒表中所有已提醒过的笔记 - */ - private static final String[] PROJECTION = new String[]{ - NoteColumns.ID, - NoteColumns.ALERTED_DATE + private static final String [] PROJECTION = new String [] { + NoteColumns.ID, + NoteColumns.ALERTED_DATE }; - /** - * 笔记 ID 列的索引 - */ - 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; - - /** - * 接收系统闹钟广播并处理 - * - * @param context 广播接收器上下文 - * @param intent 要处理的广播消息 - */ @Override public void onReceive(Context context, Intent intent) { - // 获取当前时间戳 long currentDate = System.currentTimeMillis(); - - // 查询提醒表中所有已提醒过的笔记,并设置提醒闹钟 Cursor c = context.getContentResolver().query(Notes.CONTENT_NOTE_URI, PROJECTION, NoteColumns.ALERTED_DATE + ">? AND " + NoteColumns.TYPE + "=" + Notes.TYPE_NOTE, - new String[]{String.valueOf(currentDate)}, + new String[] { String.valueOf(currentDate) }, null); if (c != null) { if (c.moveToFirst()) { do { - // 获取提醒时间和笔记 ID,并构建相应的 Intent 和 PendingIntent long alertDate = c.getLong(COLUMN_ALERTED_DATE); Intent sender = new Intent(context, AlarmReceiver.class); sender.setData(ContentUris.withAppendedId(Notes.CONTENT_NOTE_URI, c.getLong(COLUMN_ID))); PendingIntent pendingIntent = PendingIntent.getBroadcast(context, 0, sender, 0); - - // 设置提醒闹钟 - AlarmManager alermManager = (AlarmManager) context.getSystemService(Context.ALARM_SERVICE); + AlarmManager alermManager = (AlarmManager) context + .getSystemService(Context.ALARM_SERVICE); alermManager.set(AlarmManager.RTC_WAKEUP, alertDate, pendingIntent); } while (c.moveToNext()); } c.close(); } } - } diff --git a/other/07_陈七源_代码标注/ui/AlarmReceiver.java b/doc/ui/AlarmReceiver.java similarity index 100% rename from other/07_陈七源_代码标注/ui/AlarmReceiver.java rename to doc/ui/AlarmReceiver.java diff --git a/other/07_陈七源_代码标注/ui/DateTimePicker.java b/doc/ui/DateTimePicker.java similarity index 100% rename from other/07_陈七源_代码标注/ui/DateTimePicker.java rename to doc/ui/DateTimePicker.java diff --git a/other/07_陈七源_代码标注/ui/DateTimePickerDialog.java b/doc/ui/DateTimePickerDialog.java similarity index 100% rename from other/07_陈七源_代码标注/ui/DateTimePickerDialog.java rename to doc/ui/DateTimePickerDialog.java diff --git a/other/07_陈七源_代码标注/ui/DropdownMenu.java b/doc/ui/DropdownMenu.java similarity index 100% rename from other/07_陈七源_代码标注/ui/DropdownMenu.java rename to doc/ui/DropdownMenu.java diff --git a/other/07_陈七源_代码标注/ui/FoldersListAdapter.java b/doc/ui/FoldersListAdapter.java similarity index 100% rename from other/07_陈七源_代码标注/ui/FoldersListAdapter.java rename to doc/ui/FoldersListAdapter.java diff --git a/other/07_陈七源_代码标注/ui/NoteEditActivity.java b/doc/ui/NoteEditActivity.java similarity index 100% rename from other/07_陈七源_代码标注/ui/NoteEditActivity.java rename to doc/ui/NoteEditActivity.java diff --git a/other/07_陈七源_代码标注/ui/NoteEditText.java b/doc/ui/NoteEditText.java similarity index 100% rename from other/07_陈七源_代码标注/ui/NoteEditText.java rename to doc/ui/NoteEditText.java diff --git a/other/07_陈七源_代码标注/ui/NoteItemData.java b/doc/ui/NoteItemData.java similarity index 100% rename from other/07_陈七源_代码标注/ui/NoteItemData.java rename to doc/ui/NoteItemData.java diff --git a/other/07_陈七源_代码标注/ui/NotesListActivity.java b/doc/ui/NotesListActivity.java similarity index 100% rename from other/07_陈七源_代码标注/ui/NotesListActivity.java rename to doc/ui/NotesListActivity.java diff --git a/other/07_陈七源_代码标注/ui/NotesListAdapter.java b/doc/ui/NotesListAdapter.java similarity index 100% rename from other/07_陈七源_代码标注/ui/NotesListAdapter.java rename to doc/ui/NotesListAdapter.java diff --git a/other/07_陈七源_代码标注/ui/NotesListItem.java b/doc/ui/NotesListItem.java similarity index 100% rename from other/07_陈七源_代码标注/ui/NotesListItem.java rename to doc/ui/NotesListItem.java diff --git a/other/07_陈七源_代码标注/ui/NotesPreferenceActivity.java b/doc/ui/NotesPreferenceActivity.java similarity index 100% rename from other/07_陈七源_代码标注/ui/NotesPreferenceActivity.java rename to doc/ui/NotesPreferenceActivity.java diff --git a/other/06_刘峻豪_代码标注/widget/NoteWidgetProvider.java b/doc/widget/NoteWidgetProvider.java similarity index 98% rename from other/06_刘峻豪_代码标注/widget/NoteWidgetProvider.java rename to doc/widget/NoteWidgetProvider.java index aafce83..ec6f819 100644 --- a/other/06_刘峻豪_代码标注/widget/NoteWidgetProvider.java +++ b/doc/widget/NoteWidgetProvider.java @@ -14,7 +14,6 @@ * limitations under the License. */ - package net.micode.notes.widget; import android.app.PendingIntent; import android.appwidget.AppWidgetManager; @@ -38,7 +37,7 @@ public abstract class NoteWidgetProvider extends AppWidgetProvider { NoteColumns.ID, NoteColumns.BG_COLOR_ID, NoteColumns.SNIPPET - };//小米便签的桌面挂件 + }; public static final int COLUMN_ID = 0; public static final int COLUMN_BG_COLOR_ID = 1; @@ -105,7 +104,7 @@ public abstract class NoteWidgetProvider extends AppWidgetProvider { rv.setImageViewResource(R.id.widget_bg_image, getBgResourceId(bgId)); intent.putExtra(Notes.INTENT_EXTRA_BACKGROUND_ID, bgId); /** - * 生成为小组件启动主机的挂起意图 + * Generate the pending intent to start host for the widget */ PendingIntent pendingIntent = null; if (privacyMode) { @@ -130,4 +129,4 @@ public abstract class NoteWidgetProvider extends AppWidgetProvider { protected abstract int getLayoutId(); protected abstract int getWidgetType(); -} \ No newline at end of file +} diff --git a/other/06_刘峻豪_代码标注/widget/NoteWidgetProvider_2x.java b/doc/widget/NoteWidgetProvider_2x.java similarity index 97% rename from other/06_刘峻豪_代码标注/widget/NoteWidgetProvider_2x.java rename to doc/widget/NoteWidgetProvider_2x.java index b4912cc..adcb2f7 100644 --- a/other/06_刘峻豪_代码标注/widget/NoteWidgetProvider_2x.java +++ b/doc/widget/NoteWidgetProvider_2x.java @@ -14,7 +14,6 @@ * limitations under the License. */ - package net.micode.notes.widget; import android.appwidget.AppWidgetManager; @@ -26,7 +25,6 @@ import net.micode.notes.tool.ResourceParser; public class NoteWidgetProvider_2x extends NoteWidgetProvider { - //2倍大小的桌面挂件 @Override public void onUpdate(Context context, AppWidgetManager appWidgetManager, int[] appWidgetIds) { super.update(context, appWidgetManager, appWidgetIds); diff --git a/other/06_刘峻豪_代码标注/widget/NoteWidgetProvider_4x.java b/doc/widget/NoteWidgetProvider_4x.java similarity index 97% rename from other/06_刘峻豪_代码标注/widget/NoteWidgetProvider_4x.java rename to doc/widget/NoteWidgetProvider_4x.java index 20f1cb8..c12a02e 100644 --- a/other/06_刘峻豪_代码标注/widget/NoteWidgetProvider_4x.java +++ b/doc/widget/NoteWidgetProvider_4x.java @@ -14,7 +14,6 @@ * limitations under the License. */ - package net.micode.notes.widget; import android.appwidget.AppWidgetManager; @@ -26,7 +25,6 @@ import net.micode.notes.tool.ResourceParser; public class NoteWidgetProvider_4x extends NoteWidgetProvider { - //4倍大小的桌面挂件 @Override public void onUpdate(Context context, AppWidgetManager appWidgetManager, int[] appWidgetIds) { super.update(context, appWidgetManager, appWidgetIds); diff --git a/model/界面流设计/跳转顺序图和类图.doc b/model/界面流设计/跳转顺序图和类图.doc deleted file mode 100644 index e7d3d02..0000000 Binary files a/model/界面流设计/跳转顺序图和类图.doc and /dev/null differ diff --git a/model/详细设计/详细设计.doc b/model/详细设计/详细设计.doc deleted file mode 100644 index 7d8b482..0000000 Binary files a/model/详细设计/详细设计.doc and /dev/null differ diff --git a/model/需求分析/需求分析.doc b/model/需求分析/需求分析.doc deleted file mode 100644 index 1cb97e8..0000000 Binary files a/model/需求分析/需求分析.doc and /dev/null differ diff --git a/model/需求获取/软件需求描述文档.docx b/model/需求获取/软件需求描述文档.docx deleted file mode 100644 index b788769..0000000 Binary files a/model/需求获取/软件需求描述文档.docx and /dev/null differ diff --git a/other/04_江一阳_软件工程汇报PPT.pptx b/other/04_江一阳_软件工程汇报PPT.pptx deleted file mode 100644 index abe34c6..0000000 Binary files a/other/04_江一阳_软件工程汇报PPT.pptx and /dev/null differ diff --git a/other/05_江一阳_贡献度.xls b/other/05_江一阳_贡献度.xls deleted file mode 100644 index 31c57cc..0000000 Binary files a/other/05_江一阳_贡献度.xls and /dev/null differ diff --git a/other/06_何越_代码标注/gtask/remote/GTaskManager.java b/other/06_何越_代码标注/gtask/remote/GTaskManager.java deleted file mode 100644 index c10fc10..0000000 --- a/other/06_何越_代码标注/gtask/remote/GTaskManager.java +++ /dev/null @@ -1,929 +0,0 @@ -/* - * Copyright (c) 2010-2011, The MiCode Open Source Community (www.micode.net) - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - - package net.micode.notes.gtask.remote; - - import android.app.Activity; - import android.content.ContentResolver; - import android.content.ContentUris; - import android.content.ContentValues; - import android.content.Context; - import android.database.Cursor; - import android.util.Log; - - import net.micode.notes.R; - import net.micode.notes.data.Notes; - import net.micode.notes.data.Notes.DataColumns; - import net.micode.notes.data.Notes.NoteColumns; - import net.micode.notes.gtask.data.MetaData; - import net.micode.notes.gtask.data.Node; - import net.micode.notes.gtask.data.SqlNote; - import net.micode.notes.gtask.data.Task; - import net.micode.notes.gtask.data.TaskList; - import net.micode.notes.gtask.exception.ActionFailureException; - import net.micode.notes.gtask.exception.NetworkFailureException; - import net.micode.notes.tool.DataUtils; - import net.micode.notes.tool.GTaskStringUtils; - - import org.json.JSONArray; - import org.json.JSONException; - import org.json.JSONObject; - - import java.util.HashMap; - import java.util.HashSet; - import java.util.Iterator; - import java.util.Map; - - - public class GTaskManager { - private static final String TAG = GTaskManager.class.getSimpleName(); - - public static final int STATE_SUCCESS = 0; - - public static final int STATE_NETWORK_ERROR = 1; - - public static final int STATE_INTERNAL_ERROR = 2; - - public static final int STATE_SYNC_IN_PROGRESS = 3; - - public static final int STATE_SYNC_CANCELLED = 4; - - private static GTaskManager mInstance = null; - - private Activity mActivity; - - private Context mContext; - - private ContentResolver mContentResolver; - - private boolean mSyncing; - - private boolean mCancelled; - - private HashMap mGTaskListHashMap; - - private HashMap mGTaskHashMap; - - private HashMap mMetaHashMap; - - private TaskList mMetaList; - - private HashSet mLocalDeleteIdMap; - - private HashMap mGidToNid; - - private HashMap mNidToGid; - - // 定义了一个名为 GTaskManager 的私有类 -private GTaskManager() { - // 初始化一些变量 - mSyncing = false; // 当前不在同步状态 - mCancelled = false; // 当前没有取消 - mGTaskListHashMap = new HashMap(); // 存储多个任务列表的 HashMap - mGTaskHashMap = new HashMap(); // 存储任务的 HashMap - mMetaHashMap = new HashMap(); // 存储元数据的 HashMap - mMetaList = null; // 初始化元数据列表为空 - mLocalDeleteIdMap = new HashSet(); // 存储本地删除的任务的 ID 集合 - mGidToNid = new HashMap(); // 存储 GTask ID 到 Node ID 的映射 - mNidToGid = new HashMap(); // 存储 Node ID 到 GTask ID 的映射 -} - - - // 定义了一个名为 getInstance 的静态方法,返回一个 GTaskManager 对象 -public static synchronized GTaskManager getInstance() { - // 如果当前 GTaskManager 对象为空,就创建一个新的 GTaskManager 对象 - if (mInstance == null) { - mInstance = new GTaskManager(); - } - // 返回 GTaskManager 对象 - return mInstance; -} - - // 定义了一个名为 setActivityContext 的方法,用于设置 Activity 的上下文 -public synchronized void setActivityContext(Activity activity) { - // 用于获取授权令牌(authtoken)的 Activity 上下文 - mActivity = activity; -} - - - // 定义了一个名为 sync 的方法,用于同步 Google 任务列表 -public int sync(Context context, GTaskASyncTask asyncTask) { - // 如果正在同步,则返回“同步进行中”状态 - if (mSyncing) { - Log.d(TAG, "Sync is in progress"); - return STATE_SYNC_IN_PROGRESS; - } - // 重置相关变量 - mContext = context; - mContentResolver = mContext.getContentResolver(); - mSyncing = true; - mCancelled = false; - mGTaskListHashMap.clear(); - mGTaskHashMap.clear(); - mMetaHashMap.clear(); - mLocalDeleteIdMap.clear(); - mGidToNid.clear(); - mNidToGid.clear(); - - try { - // 获取 GTaskClient 的单例对象 - GTaskClient client = GTaskClient.getInstance(); - // 重置更新数组 - client.resetUpdateArray(); - - // 登录 Google 任务列表 - if (!mCancelled) { - if (!client.login(mActivity)) { - throw new NetworkFailureException("login google task failed"); - } - } - - // 从 Google 获取任务列表 - asyncTask.publishProgess(mContext.getString(R.string.sync_progress_init_list)); - initGTaskList(); - - // 进行内容同步工作 - asyncTask.publishProgess(mContext.getString(R.string.sync_progress_syncing)); - syncContent(); - } catch (NetworkFailureException e) { - Log.e(TAG, e.toString()); - return STATE_NETWORK_ERROR; - } catch (ActionFailureException e) { - Log.e(TAG, e.toString()); - return STATE_INTERNAL_ERROR; - } catch (Exception e) { - Log.e(TAG, e.toString()); - e.printStackTrace(); - return STATE_INTERNAL_ERROR; - } finally { - // 清空相关变量 - mGTaskListHashMap.clear(); - mGTaskHashMap.clear(); - mMetaHashMap.clear(); - mLocalDeleteIdMap.clear(); - mGidToNid.clear(); - mNidToGid.clear(); - mSyncing = false; - } - - // 如果已取消,则返回“同步已取消”状态;否则返回“成功”状态 - return mCancelled ? STATE_SYNC_CANCELLED : STATE_SUCCESS; -} - - // 初始化 GTask 列表 - - private void initGTaskList() throws NetworkFailureException { - // 检查是否已取消 - if (mCancelled) - return; - // 获取 GTask 客户端 - GTaskClient client = GTaskClient.getInstance(); - try { - // 获取任务列表的 JSON 数据 - JSONArray jsTaskLists = client.getTaskLists(); - - // 先初始化元列表 - mMetaList = null; - for (int i = 0; i < jsTaskLists.length(); i++) { - // 获取 JSON 数据中的任务列表对象 - JSONObject object = jsTaskLists.getJSONObject(i); - String gid = object.getString(GTaskStringUtils.GTASK_JSON_ID); - String name = object.getString(GTaskStringUtils.GTASK_JSON_NAME); - // 如果任务列表名称为“MIUI_FOLDER_PREFFIX + FOLDER_META”,则表示为元列表 - if (name.equals(GTaskStringUtils.MIUI_FOLDER_PREFFIX + GTaskStringUtils.FOLDER_META)) { - // 初始化元列表 - mMetaList = new TaskList(); - mMetaList.setContentByRemoteJSON(object); - - // 加载元数据 - JSONArray jsMetas = client.getTaskList(gid); - for (int j = 0; j < jsMetas.length(); j++) { - object = (JSONObject) jsMetas.getJSONObject(j); - MetaData metaData = new MetaData(); - metaData.setContentByRemoteJSON(object); - // 如果元数据值得保存,将其添加到元列表中 - if (metaData.isWorthSaving()) { - mMetaList.addChildTask(metaData); - // 将元数据关联的 GID 和元数据存储在哈希映射表中 - if (metaData.getGid() != null) { - mMetaHashMap.put(metaData.getRelatedGid(), metaData); - } - } - } - } - } - - // 如果元列表不存在,则创建元列表 - if (mMetaList == null) { - mMetaList = new TaskList(); - mMetaList.setName(GTaskStringUtils.MIUI_FOLDER_PREFFIX - + GTaskStringUtils.FOLDER_META); - GTaskClient.getInstance().createTaskList(mMetaList); - } - - // 初始化任务列表 - for (int i = 0; i < jsTaskLists.length(); i++) { - // 获取 JSON 数据中的任务列表对象 - JSONObject object = jsTaskLists.getJSONObject(i); - String gid = object.getString(GTaskStringUtils.GTASK_JSON_ID); - String name = object.getString(GTaskStringUtils.GTASK_JSON_NAME); - // 如果任务列表名称以“MIUI_FOLDER_PREFFIX”开头且不是元列表,则为任务列表 - if (name.startsWith(GTaskStringUtils.MIUI_FOLDER_PREFFIX) - && !name.equals(GTaskStringUtils.MIUI_FOLDER_PREFFIX - + GTaskStringUtils.FOLDER_META)) { - // 初始化任务列表 - TaskList tasklist = new TaskList(); - tasklist.setContentByRemoteJSON(object); - mGTaskListHashMap.put(gid, tasklist); - mGTaskHashMap.put(gid, tasklist); - - // 加载任务 - JSONArray jsTasks = client.getTaskList(gid); - for (int j = 0; j < jsTasks.length(); j++) { - object = (JSONObject) jsTasks.getJSONObject(j); - gid = object.getString(GTaskStringUtils.GTASK_JSON_ID); - Task task = new Task(); - task.setContentByRemoteJSON(object); - // 如果任务值得保存,将其添加 - if (task.isWorthSaving()) { - task.setMetaInfo(mMetaHashMap.get(gid));// 设置任务元信息 - tasklist.addChildTask(task);// 将任务添加到任务列表的子任务中 - mGTaskHashMap.put(gid, task); // 在哈希表中添加任务的ID和任务对象 - } - } - } - } - } catch (JSONException e) { - Log.e(TAG, e.toString()); - e.printStackTrace(); - throw new ActionFailureException("initGTaskList: handing JSONObject failed"); - } - } - // 同步笔记内容 - private void syncContent() throws NetworkFailureException { - int syncType; - Cursor c = null; - String gid; - Node node; - - mLocalDeleteIdMap.clear(); - if (mCancelled) {// 如果已取消,直接返回 - return; - } - - // 处理本地已删除笔记 - try { - // 查询非系统类型且父级 ID 不是回收站的笔记 - c = mContentResolver.query(Notes.CONTENT_NOTE_URI, SqlNote.PROJECTION_NOTE, - "(type<>? AND parent_id=?)", new String[] { - String.valueOf(Notes.TYPE_SYSTEM), String.valueOf(Notes.ID_TRASH_FOLER) - }, null); - if (c != null) { - while (c.moveToNext()) { - gid = c.getString(SqlNote.GTASK_ID_COLUMN); - node = mGTaskHashMap.get(gid); - if (node != null) { - mGTaskHashMap.remove(gid); - doContentSync(Node.SYNC_ACTION_DEL_REMOTE, node, c); - } - - mLocalDeleteIdMap.add(c.getLong(SqlNote.ID_COLUMN)); - } - } else { - Log.w(TAG, "failed to query trash folder"); - } - } finally { - if (c != null) { - c.close(); - c = null; - } - } - - // 先同步文件夹 - syncFolder(); - - // 处理已存在于数据库中的笔记 - try { - // 查询类型为笔记且父级 ID 不是回收站的笔记,按类型倒序排序 - c = mContentResolver.query(Notes.CONTENT_NOTE_URI, SqlNote.PROJECTION_NOTE, - "(type=? AND parent_id<>?)", new String[] { - String.valueOf(Notes.TYPE_NOTE), String.valueOf(Notes.ID_TRASH_FOLER) - }, NoteColumns.TYPE + " DESC"); - if (c != null) { - while (c.moveToNext()) { - gid = c.getString(SqlNote.GTASK_ID_COLUMN); - node = mGTaskHashMap.get(gid); - if (node != null) { - mGTaskHashMap.remove(gid); - mGidToNid.put(gid, c.getLong(SqlNote.ID_COLUMN)); - mNidToGid.put(c.getLong(SqlNote.ID_COLUMN), gid); - syncType = node.getSyncAction(c);// 获取同步类型 - } else { - if (c.getString(SqlNote.GTASK_ID_COLUMN).trim().length() == 0) { - // 本地新增 - syncType = Node.SYNC_ACTION_ADD_REMOTE; - } else { - // 远程删除 - syncType = Node.SYNC_ACTION_DEL_LOCAL; - } - } - doContentSync(syncType, node, c); - } - } else { - Log.w(TAG, "failed to query existing note in database"); - } - - } finally { - if (c != null) { - c.close(); - c = null; - } - } - - // 处理剩余项 - Iterator> iter = mGTaskHashMap.entrySet().iterator(); - while (iter.hasNext()) {// 如果还有下一个键值对 - Map.Entry entry = iter.next();// 获取下一个键值对 - node = entry.getValue();// 获取键值对中的value并将其赋值给node - doContentSync(Node.SYNC_ACTION_ADD_LOCAL, node, null);// 将node添加到本地同步 - } - - // mCancelled 可能会被另一个线程设置,因此我们需要逐个检查 - // 清除本地删除表 - if (!mCancelled) {// 如果未被取消 - if (!DataUtils.batchDeleteNotes(mContentResolver, mLocalDeleteIdMap)) {// 批量删除本地删除表中的笔记 - throw new ActionFailureException("failed to batch-delete local deleted notes"); - }// 如果删除失败则抛出异常 - } - - // 刷新本地同步id - if (!mCancelled) { - GTaskClient.getInstance().commitUpdate(); - refreshLocalSyncId(); - } - - } - - private void syncFolder() throws NetworkFailureException { - Cursor c = null;// 游标对象,用于访问数据 - String gid;// GTask任务列表的ID - Node node;// GTask任务列表对应的本地节点 - int syncType;// 本地与远程数据同步的类型 - - if (mCancelled) {// 如果同步已被取消,则直接返回 - return; - } - - // for root folder - try { - // 查询根目录的数据 - c = mContentResolver.query(ContentUris.withAppendedId(Notes.CONTENT_NOTE_URI, - Notes.ID_ROOT_FOLDER), SqlNote.PROJECTION_NOTE, null, null, null); - if (c != null) { - c.moveToNext();// 将游标移动到下一个记录 - gid = c.getString(SqlNote.GTASK_ID_COLUMN);// 获取GTask任务列表ID - node = mGTaskHashMap.get(gid);// 从GTask任务列表映射中获取对应本地节点 - if (node != null) { - mGTaskHashMap.remove(gid);// 从映射中移除该节点 - mGidToNid.put(gid, (long) Notes.ID_ROOT_FOLDER);// 将GTask ID映射到本地ID - mNidToGid.put((long) Notes.ID_ROOT_FOLDER, gid);// 将本地ID映射到GTask ID - // 仅当名称不是默认值时,才更新远程名称 - if (!node.getName().equals( - GTaskStringUtils.MIUI_FOLDER_PREFFIX + GTaskStringUtils.FOLDER_DEFAULT)) - doContentSync(Node.SYNC_ACTION_UPDATE_REMOTE, node, c);// 同步本地和远程数据 - } else { - doContentSync(Node.SYNC_ACTION_ADD_REMOTE, node, c);// 将本地数据同步到远程 - } - } else { - Log.w(TAG, "failed to query root folder");// 记录警告信息 - } - } finally { - if (c != null) { - c.close();// 关闭游标 - c = null;// 将游标对象设置为null - } - } - - // for call-note folder - try { - // 查询通话记录文件夹的数据 - c = mContentResolver.query(Notes.CONTENT_NOTE_URI, SqlNote.PROJECTION_NOTE, "(_id=?)", - new String[] { - String.valueOf(Notes.ID_CALL_RECORD_FOLDER) - }, null); - if (c != null) { - if (c.moveToNext()) { - gid = c.getString(SqlNote.GTASK_ID_COLUMN);// 获取GTask任务列表ID - node = mGTaskHashMap.get(gid);// 从GTask任务列表映射中获取对应本地节点 - if (node != null) { - mGTaskHashMap.remove(gid);// 从映射中移除该节点 - mGidToNid.put(gid, (long) Notes.ID_CALL_RECORD_FOLDER);// 将GTask ID映射到本地ID - mNidToGid.put((long) Notes.ID_CALL_RECORD_FOLDER, gid);// 将本地ID映射到GTask ID - // 仅当名称不是默认值时,才更新远端 - if (!node.getName().equals( - GTaskStringUtils.MIUI_FOLDER_PREFFIX - + GTaskStringUtils.FOLDER_CALL_NOTE)) - doContentSync(Node.SYNC_ACTION_UPDATE_REMOTE, node, c); - } else { - doContentSync(Node.SYNC_ACTION_ADD_REMOTE, node, c); - } - } - } else { - Log.w(TAG, "failed to query call note folder"); - } - } finally { - if (c != null) { - c.close(); - c = null; - } - } - - // 处理本地现有文件夹 - try { - c = mContentResolver.query(Notes.CONTENT_NOTE_URI, SqlNote.PROJECTION_NOTE, - "(type=? AND parent_id<>?)", new String[] { - String.valueOf(Notes.TYPE_FOLDER), String.valueOf(Notes.ID_TRASH_FOLER) - }, NoteColumns.TYPE + " DESC"); - if (c != null) { - while (c.moveToNext()) { - gid = c.getString(SqlNote.GTASK_ID_COLUMN); - node = mGTaskHashMap.get(gid); - if (node != null) { - mGTaskHashMap.remove(gid); - mGidToNid.put(gid, c.getLong(SqlNote.ID_COLUMN)); - mNidToGid.put(c.getLong(SqlNote.ID_COLUMN), gid); - syncType = node.getSyncAction(c); - } else { - if (c.getString(SqlNote.GTASK_ID_COLUMN).trim().length() == 0) { - // 本地新增 - syncType = Node.SYNC_ACTION_ADD_REMOTE; - } else { - // 远端删除 - syncType = Node.SYNC_ACTION_DEL_LOCAL; - } - } - doContentSync(syncType, node, c); - } - } else { - Log.w(TAG, "failed to query existing folder"); - } - } finally { - if (c != null) { - c.close(); - c = null; - } - } - - // 处理远端新增文件夹 - Iterator> iter = mGTaskListHashMap.entrySet().iterator(); - while (iter.hasNext()) { - Map.Entry entry = iter.next(); - gid = entry.getKey(); - node = entry.getValue(); - if (mGTaskHashMap.containsKey(gid)) { - mGTaskHashMap.remove(gid); - doContentSync(Node.SYNC_ACTION_ADD_LOCAL, node, null); - } - } - - if (!mCancelled) - GTaskClient.getInstance().commitUpdate(); - } - - private void doContentSync(int syncType, Node node, Cursor c) throws NetworkFailureException { - // 如果取消了同步,则直接返回 - if (mCancelled) { - return; - } - // 元数据对象 - MetaData meta; - // 根据同步类型执行相应的操作 - switch (syncType) { - // 本地添加节点 - case Node.SYNC_ACTION_ADD_LOCAL: - addLocalNode(node); - break; - // 远程添加节点 - case Node.SYNC_ACTION_ADD_REMOTE: - addRemoteNode(node, c); - break; - // 本地删除节点 - case Node.SYNC_ACTION_DEL_LOCAL: - // 获取需要删除的节点的元数据 - meta = mMetaHashMap.get(c.getString(SqlNote.GTASK_ID_COLUMN)); - if (meta != null) { - // 使用 GTaskClient 删除节点 - GTaskClient.getInstance().deleteNode(meta); - } - // 将节点 ID 添加到本地删除 ID 映射表中 - mLocalDeleteIdMap.add(c.getLong(SqlNote.ID_COLUMN)); - break; - // 远程删除节点 - case Node.SYNC_ACTION_DEL_REMOTE: - // 获取需要删除的节点的元数据 - meta = mMetaHashMap.get(node.getGid()); - if (meta != null) { - // 使用 GTaskClient 删除节点 - GTaskClient.getInstance().deleteNode(meta); - } - // 使用 GTaskClient 删除节点 - GTaskClient.getInstance().deleteNode(node); - break; - // 本地更新节点 - case Node.SYNC_ACTION_UPDATE_LOCAL: - updateLocalNode(node, c); - break; - // 远程更新节点 - case Node.SYNC_ACTION_UPDATE_REMOTE: - updateRemoteNode(node, c); - break; - // 更新冲突 - case Node.SYNC_ACTION_UPDATE_CONFLICT: - // 可以考虑合并两个修改 - // 现在先简单地使用本地更新 - updateRemoteNode(node, c); - break; - // 不执行任何操作 - case Node.SYNC_ACTION_NONE: - break; - // 同步操作失败 - case Node.SYNC_ACTION_ERROR: - default: - // 抛出异常 - throw new ActionFailureException("unkown sync action type"); - } - } - - private void addLocalNode(Node node) throws NetworkFailureException { - // 如果取消了操作,直接返回 - if (mCancelled) { - return; - } - // 声明一个 SqlNote 对象 - SqlNote sqlNote; - // 如果是 TaskList,判断是哪种文件夹 - if (node instanceof TaskList) { - // 如果是默认文件夹 - if (node.getName().equals( - GTaskStringUtils.MIUI_FOLDER_PREFFIX + GTaskStringUtils.FOLDER_DEFAULT)) { - // 新建一个根文件夹的 SqlNote 对象 - sqlNote = new SqlNote(mContext, Notes.ID_ROOT_FOLDER); - } - // 如果是通话记录文件夹 - else if (node.getName().equals( - GTaskStringUtils.MIUI_FOLDER_PREFFIX + GTaskStringUtils.FOLDER_CALL_NOTE)) { - // 新建一个通话记录文件夹的 SqlNote 对象 - sqlNote = new SqlNote(mContext, Notes.ID_CALL_RECORD_FOLDER); - } - // 如果是其他类型的文件夹 - else { - // 新建一个普通的 SqlNote 对象,并设置其内容和父文件夹 ID - sqlNote = new SqlNote(mContext); - sqlNote.setContent(node.getLocalJSONFromContent()); - sqlNote.setParentId(Notes.ID_ROOT_FOLDER); - } - } - // 如果是 Task - else { - // 新建一个普通的 SqlNote 对象 - sqlNote = new SqlNote(mContext); - // 从 Task 中获取其内容的 JSON 对象 - JSONObject js = node.getLocalJSONFromContent(); - try { - // 如果 JSON 对象中包含元数据头部信息 - if (js.has(GTaskStringUtils.META_HEAD_NOTE)) { - // 获取元数据头部信息中的 Note 对象 - JSONObject note = js.getJSONObject(GTaskStringUtils.META_HEAD_NOTE); - // 如果 Note 对象中包含 ID - if (note.has(NoteColumns.ID)) { - // 获取 Note 的 ID - long id = note.getLong(NoteColumns.ID); - // 如果该 ID 在 Note 数据库中已经存在 - if (DataUtils.existInNoteDatabase(mContentResolver, id)) { - // 该 ID 已经被占用,需要新建一个 ID - note.remove(NoteColumns.ID); - } - } - } - // 如果 JSON 对象中包含元数据数据部分 - if (js.has(GTaskStringUtils.META_HEAD_DATA)) { - // 获取元数据数据部分的 JSON 数组 - JSONArray dataArray = js.getJSONArray(GTaskStringUtils.META_HEAD_DATA); - // 遍历该 JSON 数组 - for (int i = 0; i < dataArray.length(); i++) { - // 获取数组中的 JSON 对象 - JSONObject data = dataArray.getJSONObject(i); - // 如果该 JSON 对象中包含 ID - if (data.has(DataColumns.ID)) { - // 获取该数据的 ID - long dataId = data.getLong(DataColumns.ID); - // 如果该 ID 在数据数据库中已经存在 - if (DataUtils.existInDataDatabase(mContentResolver, dataId)) { - // 该 ID 已经被占用,需要新建一个 ID - data.remove(DataColumns.ID); - } - } - } - - } - } catch (JSONException e) {// 捕获 JSON 解析异常 - Log.w(TAG, e.toString());// 记录警告日志 - e.printStackTrace();// 打印异常堆栈信息 - } - sqlNote.setContent(js);// 设置 SQLNote 的内容为解析出的 js - - Long parentId = mGidToNid.get(((Task) node).getParent().getGid());// 获取 Task 节点的父节点的 GID 对应的 NID - // 如果获取到的 NID 为空 - if (parentId == null) { - Log.e(TAG, "cannot find task's parent id locally");// 记录错误日志 - throw new ActionFailureException("cannot add local node");// 抛出 ActionFailureException 异常 - } - sqlNote.setParentId(parentId.longValue());// 将 SQLNote 的父节点 ID 设置为获取到的 NID - } - - // 创建本地节点 - sqlNote.setGtaskId(node.getGid());// 设置 SQLNote 的 GTask ID 为节点的 GID - sqlNote.commit(false);// 将 SQLNote 提交到本地数据库,不需要立即同步到远程服务器 - - // 更新 GID-NID 映射 - mGidToNid.put(node.getGid(), sqlNote.getId());// 将节点的 GID 映射为 SQLNote 的 ID - mNidToGid.put(sqlNote.getId(), node.getGid());// 将 SQLNote 的 ID 映射为节点的 GID - - // 更新元数据 - updateRemoteMeta(node.getGid(), sqlNote);// 同步 SQLNote 的元数据到远程服务器 - } - - private void updateLocalNode(Node node, Cursor c) throws NetworkFailureException { - // 如果操作已经被取消,则不进行任何操作 - if (mCancelled) { - return; - } - - SqlNote sqlNote; - // 将数据库查询结果保存到 SqlNote 对象中 - sqlNote = new SqlNote(mContext, c); - // 将节点内容转换为 JSON 格式并保存到 SqlNote 对象中 - sqlNote.setContent(node.getLocalJSONFromContent()); - - // 如果节点是任务类型,则将其父节点的 ID 设置为对应的本地 ID, - // 否则将其父节点的 ID 设置为根文件夹的 ID - Long parentId = (node instanceof Task) ? mGidToNid.get(((Task) node).getParent().getGid()) - : new Long(Notes.ID_ROOT_FOLDER); - // 如果 parentId 为 null,则说明无法在本地找到该任务的父节点 ID, - // 此时抛出一个异常 - if (parentId == null) { - Log.e(TAG, "cannot find task's parent id locally"); - throw new ActionFailureException("cannot update local node"); - } - // 将父节点 ID 设置为 sqlNote 对象的 parentId 属性 - sqlNote.setParentId(parentId.longValue()); - // 将 sqlNote 对象的属性保存到数据库中 - sqlNote.commit(true); - - // 更新远程节点的元信息 - updateRemoteMeta(node.getGid(), sqlNote); - } - - private void addRemoteNode(Node node, Cursor c) throws NetworkFailureException { - // 如果任务已经被取消,直接返回 - if (mCancelled) { - return; - } - // 通过Cursor获取SqlNote对象 - SqlNote sqlNote = new SqlNote(mContext, c); - Node n; - - // 如果SqlNote是任务类型,进行以下操作 - if (sqlNote.isNoteType()) { - // 创建一个Task对象并用SqlNote的内容更新它 - Task task = new Task(); - task.setContentByLocalJSON(sqlNote.getContent()); - - // 获取Task对象的父任务列表的gid - String parentGid = mNidToGid.get(sqlNote.getParentId()); - // 如果无法找到父任务列表的gid,抛出ActionFailureException异常 - if (parentGid == null) { - Log.e(TAG, "cannot find task's parent tasklist"); - throw new ActionFailureException("cannot add remote task"); - } - // 在父任务列表中添加子任务 - mGTaskListHashMap.get(parentGid).addChildTask(task); - // 通过GTaskClient实例创建Task任务 - GTaskClient.getInstance().createTask(task); - n = (Node) task; - - // 添加元数据 - updateRemoteMeta(task.getGid(), sqlNote); - } else { - TaskList tasklist = null; - // 如果SqlNote是文件夹类型,进行以下操作 - // 如果是根文件夹,设置文件夹名为默认文件夹 - String folderName = GTaskStringUtils.MIUI_FOLDER_PREFFIX; - if (sqlNote.getId() == Notes.ID_ROOT_FOLDER) - folderName += GTaskStringUtils.FOLDER_DEFAULT; - // 如果是通话记录文件夹,设置文件夹名为通话记录文件夹 - else if (sqlNote.getId() == Notes.ID_CALL_RECORD_FOLDER) - folderName += GTaskStringUtils.FOLDER_CALL_NOTE; - // 否则,将文件夹名设置为SqlNote对象的片段内容 - else - folderName += sqlNote.getSnippet(); - // 遍历mGTaskListHashMap查找是否存在与SqlNote对象匹配的TaskList对象 - Iterator> iter = mGTaskListHashMap.entrySet().iterator(); - while (iter.hasNext()) { - Map.Entry entry = iter.next(); - String gid = entry.getKey(); - TaskList list = entry.getValue(); - // 如果存在匹配的TaskList对象,则将tasklist设置为该对象 - if (list.getName().equals(folderName)) { - tasklist = list; - // 如果mGTaskHashMap包含gid,将其从中移除 - if (mGTaskHashMap.containsKey(gid)) { - mGTaskHashMap.remove(gid); - } - break; - } - } - - // 如果未找到匹配的TaskList对象,新建一个TaskList对象 - if (tasklist == null) { - tasklist = new TaskList(); - tasklist.setContentByLocalJSON(sqlNote.getContent()); - GTaskClient.getInstance().createTaskList(tasklist); - mGTaskListHashMap.put(tasklist.getGid(), tasklist); - } - n = (Node) tasklist; - } - - // 更新SqlNote对象的GTaskId,并将其提交到本地数据库 - sqlNote.setGtaskId(n.getGid()); - sqlNote.commit(false); - sqlNote.resetLocalModified(); - sqlNote.commit(true); - - // gid-id 映射表 - mGidToNid.put(n.getGid(), sqlNote.getId()); - mNidToGid.put(sqlNote.getId(), n.getGid()); - } - - private void updateRemoteNode(Node node, Cursor c) throws NetworkFailureException { - if (mCancelled) { - return; // 如果任务已取消,则退出此方法 - } - - SqlNote sqlNote = new SqlNote(mContext, c); // 创建 SqlNote 实例 - - // 更新远程服务器的节点内容 - node.setContentByLocalJSON(sqlNote.getContent()); - GTaskClient.getInstance().addUpdateNode(node); - - // 更新节点的元数据 - updateRemoteMeta(node.getGid(), sqlNote); - - // 如果是笔记类型,则移动任务(子任务)到另一个任务列表中 - if (sqlNote.isNoteType()) { - Task task = (Task) node; - TaskList preParentList = task.getParent(); // 获取任务当前的父任务列表 - - // 查找新的父任务列表的 GID - String curParentGid = mNidToGid.get(sqlNote.getParentId()); - if (curParentGid == null) { - Log.e(TAG, "cannot find task's parent tasklist"); // 找不到新父任务列表,记录错误并抛出异常 - throw new ActionFailureException("cannot update remote task"); - } - TaskList curParentList = mGTaskListHashMap.get(curParentGid); // 获取新父任务列表的 TaskList 实例 - - // 如果新旧父任务列表不同,则移动任务到新的父任务列表 - if (preParentList != curParentList) { - preParentList.removeChildTask(task); // 从旧父任务列表中移除该任务 - curParentList.addChildTask(task); // 添加该任务到新的父任务列表中 - GTaskClient.getInstance().moveTask(task, preParentList, curParentList); // 移动任务到新父任务列表 - } - } - - sqlNote.resetLocalModified(); // 重置本地修改标记 - sqlNote.commit(true); // 将 SqlNote 中的修改提交到本地 SQLite 数据库中 - } - - private void updateRemoteMeta(String gid, SqlNote sqlNote) throws NetworkFailureException { - // 如果 sqlNote 不为空且为笔记类型,则执行以下代码 - if (sqlNote != null && sqlNote.isNoteType()) { - // 获取 gid 对应的 metaData - MetaData metaData = mMetaHashMap.get(gid); - if (metaData != null) { - // 设置 gid 对应的 metaData 的内容为 sqlNote 的内容 - metaData.setMeta(gid, sqlNote.getContent()); - // 将 metaData 添加到更新队列中 - GTaskClient.getInstance().addUpdateNode(metaData); - } else { - // 如果 metaData 为空,则创建新的 metaData,并设置其内容为 sqlNote 的内容 - metaData = new MetaData(); - metaData.setMeta(gid, sqlNote.getContent()); - // 将 metaData 添加到 mMetaList 的子任务列表中 - mMetaList.addChildTask(metaData); - // 将 metaData 添加到 mMetaHashMap 中 - mMetaHashMap.put(gid, metaData); - // 创建任务并将其添加到 GTaskClient 的任务列表中 - GTaskClient.getInstance().createTask(metaData); - } - } - } - - - // 声明一个方法,用于更新本地同步 ID,如果网络故障则抛出 NetworkFailureException 异常 - private void refreshLocalSyncId() throws NetworkFailureException { - // 如果已取消,则直接返回 - if (mCancelled) { - return; - } - - // 清空哈希表 - mGTaskHashMap.clear(); - mGTaskListHashMap.clear(); - mMetaHashMap.clear(); - // 初始化 GTask 列表 - initGTaskList(); - - // 声明一个游标,用于查询本地便签 - Cursor c = null; - try { - // 查询不属于系统类型和垃圾桶类型的所有便签 - c = mContentResolver.query( - Notes.CONTENT_NOTE_URI, - SqlNote.PROJECTION_NOTE, - "(type<>? AND parent_id<>?)", - new String[] { - String.valueOf(Notes.TYPE_SYSTEM), - String.valueOf(Notes.ID_TRASH_FOLER) - }, - NoteColumns.TYPE + " DESC" - ); - - // 如果查询结果不为空 - if (c != null) { - // 遍历查询结果 - while (c.moveToNext()) { - // 获取当前便签的 GTask ID - String gid = c.getString(SqlNote.GTASK_ID_COLUMN); - // 根据 GTask ID 从 GTask 哈希表中获取节点信息 - Node node = mGTaskHashMap.get(gid); - // 如果节点信息不为空 - if (node != null) { - // 从 GTask 哈希表中移除当前 GTask ID 对应的节点 - mGTaskHashMap.remove(gid); - // 创建一个 ContentValues 对象,用于更新本地便签的同步 ID - ContentValues values = new ContentValues(); - values.put(NoteColumns.SYNC_ID, node.getLastModified()); - // 更新本地便签的同步 ID - mContentResolver.update( - ContentUris.withAppendedId( - Notes.CONTENT_NOTE_URI, - c.getLong(SqlNote.ID_COLUMN) - ), - values, - null, - null - ); - } else { - // 如果节点信息为空,打印日志并抛出 ActionFailureException 异常 - Log.e(TAG, "something is missed"); - throw new ActionFailureException( - "some local items don't have gid after sync" - ); - } - } - } else { - // 如果查询结果为空,打印警告日志 - Log.w(TAG, "failed to query local note to refresh sync id"); - } - } finally { - // 关闭游标 - if (c != null) { - c.close(); - c = null; - } - } - } - - - public String getSyncAccount() { - return GTaskClient.getInstance().getSyncAccount().name; - } - - public void cancelSync() { - mCancelled = true; - } - } - \ No newline at end of file diff --git a/other/06_凌子杰_代码标注/ui/AlarmReceiver.java b/other/06_凌子杰_代码标注/ui/AlarmReceiver.java deleted file mode 100644 index d0f90e6..0000000 --- a/other/06_凌子杰_代码标注/ui/AlarmReceiver.java +++ /dev/null @@ -1,39 +0,0 @@ -/* - * 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.ui; - -import android.content.BroadcastReceiver; -import android.content.Context; -import android.content.Intent; - -public class AlarmReceiver extends BroadcastReceiver { - @Override - public void onReceive(Context context, Intent intent) { - intent.setClass(context, AlarmAlertActivity.class); - //启动AlarmAlertActivity - intent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK); - //activity要存在于activity的栈中,而非activity的途径启动activity时必然不存在一个activity的栈 - //所以要新起一个栈装入启动的activity - context.startActivity(intent); - } -} -//这是实现alarm这个功能最接近用户层的包,基于上面的两个包, -<<<<<<< remotes/origin/lzj_branch -//作用还需要深究但是对于setClass和addFlags的 -======= -//作用还需要深究但是对于setClass和addFlags的 ->>>>>>> local diff --git a/other/06_凌子杰_代码标注/ui/DateTimePicker.java b/other/06_凌子杰_代码标注/ui/DateTimePicker.java deleted file mode 100644 index 30ccd1e..0000000 --- a/other/06_凌子杰_代码标注/ui/DateTimePicker.java +++ /dev/null @@ -1,495 +0,0 @@ -/* - * 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. - */ - -public class DateTimePicker extends FrameLayout { - //FrameLayout是布局模板之一 - //所有的子元素全部在屏幕的右上方 - private static final boolean DEFAULT_ENABLE_STATE = true; - - private static final int HOURS_IN_HALF_DAY = 12; - private static final int HOURS_IN_ALL_DAY = 24; - private static final int DAYS_IN_ALL_WEEK = 7; - private static final int DATE_SPINNER_MIN_VAL = 0; - private static final int DATE_SPINNER_MAX_VAL = DAYS_IN_ALL_WEEK - 1; - private static final int HOUR_SPINNER_MIN_VAL_24_HOUR_VIEW = 0; - private static final int HOUR_SPINNER_MAX_VAL_24_HOUR_VIEW = 23; - private static final int HOUR_SPINNER_MIN_VAL_12_HOUR_VIEW = 1; - private static final int HOUR_SPINNER_MAX_VAL_12_HOUR_VIEW = 12; - private static final int MINUT_SPINNER_MIN_VAL = 0; - private static final int MINUT_SPINNER_MAX_VAL = 59; - private static final int AMPM_SPINNER_MIN_VAL = 0; - private static final int AMPM_SPINNER_MAX_VAL = 1; - //初始化控件 - private final NumberPicker mDateSpinner; - private final NumberPicker mHourSpinner; - private final NumberPicker mMinuteSpinner; - private final NumberPicker mAmPmSpinner; - //NumberPicker是数字选择器 - //这里定义的四个变量全部是在设置闹钟时需要选择的变量(如日期、时、分、上午或者下午) - private Calendar mDate; - //定义了Calendar类型的变量mDate,用于操作时间 - private String[] mDateDisplayValues = new String[DAYS_IN_ALL_WEEK]; - - private boolean mIsAm; - - private boolean mIs24HourView; - - private boolean mIsEnabled = DEFAULT_ENABLE_STATE; - - private boolean mInitialising; - - private OnDateTimeChangedListener mOnDateTimeChangedListener; - - private NumberPicker.OnValueChangeListener mOnDateChangedListener = new NumberPicker.OnValueChangeListener() { - @Override - public void onValueChange(NumberPicker picker, int oldVal, int newVal) { - mDate.add(Calendar.DAY_OF_YEAR, newVal - oldVal); - updateDateControl(); - onDateTimeChanged(); - } - };//OnValueChangeListener,这是时间改变监听器,这里主要是对日期的监听 - //将现在日期的值传递给mDate;updateDateControl是同步操作 - - private NumberPicker.OnValueChangeListener mOnHourChangedListener = new NumberPicker.OnValueChangeListener() { - //这里是对 小时(Hour) 的监听 - @Override - public void onValueChange(NumberPicker picker, int oldVal, int newVal) { - boolean isDateChanged = false; - Calendar cal = Calendar.getInstance(); - //声明一个Calendar的变量cal,便于后续的操作 - if (!mIs24HourView) { - if (!mIsAm && oldVal == HOURS_IN_HALF_DAY - 1 && newVal == HOURS_IN_HALF_DAY) { - cal.setTimeInMillis(mDate.getTimeInMillis()); - cal.add(Calendar.DAY_OF_YEAR, 1); - isDateChanged = true; - //这里是对于12小时制时,晚上11点和12点交替时对日期的更改 - } else if (mIsAm && oldVal == HOURS_IN_HALF_DAY && newVal == HOURS_IN_HALF_DAY - 1) { - cal.setTimeInMillis(mDate.getTimeInMillis()); - cal.add(Calendar.DAY_OF_YEAR, -1); - isDateChanged = true; - } - //这里是对于12小时制时,凌晨11点和12点交替时对日期的更改 - if (oldVal == HOURS_IN_HALF_DAY - 1 && newVal == HOURS_IN_HALF_DAY || - oldVal == HOURS_IN_HALF_DAY && newVal == HOURS_IN_HALF_DAY - 1) { - mIsAm = !mIsAm; - updateAmPmControl(); - } - //这里是对于12小时制时,中午11点和12点交替时对AM和PM的更改 - } else { - if (oldVal == HOURS_IN_ALL_DAY - 1 && newVal == 0) { - cal.setTimeInMillis(mDate.getTimeInMillis()); - cal.add(Calendar.DAY_OF_YEAR, 1); - isDateChanged = true; - //这里是对于24小时制时,晚上11点和12点交替时对日期的更改 - } else if (oldVal == 0 && newVal == HOURS_IN_ALL_DAY - 1) { - cal.setTimeInMillis(mDate.getTimeInMillis()); - cal.add(Calendar.DAY_OF_YEAR, -1); - isDateChanged = true; - } - }//这里是对于12小时制时,凌晨11点和12点交替时对日期的更改 - int newHour = mHourSpinner.getValue() % HOURS_IN_HALF_DAY + (mIsAm ? 0 : HOURS_IN_HALF_DAY); - //通过数字选择器对newHour的赋值 - mDate.set(Calendar.HOUR_OF_DAY, newHour); - //通过set函数将新的Hour值传给mDate - onDateTimeChanged(); - if (isDateChanged) { - setCurrentYear(cal.get(Calendar.YEAR)); - setCurrentMonth(cal.get(Calendar.MONTH)); - setCurrentDay(cal.get(Calendar.DAY_OF_MONTH)); - } - } - }; - - private NumberPicker.OnValueChangeListener mOnMinuteChangedListener = new NumberPicker.OnValueChangeListener() { - @Override - //这里是对 分钟(Minute)改变的监听 - public void onValueChange(NumberPicker picker, int oldVal, int newVal) { - int minValue = mMinuteSpinner.getMinValue(); - int maxValue = mMinuteSpinner.getMaxValue(); - int offset = 0; - //设置offset,作为小时改变的一个记录数据 - if (oldVal == maxValue && newVal == minValue) { - offset += 1; - } else if (oldVal == minValue && newVal == maxValue) { - offset -= 1; - } - //如果原值为59,新值为0,则offset加1 - //如果原值为0,新值为59,则offset减1 - if (offset != 0) { - mDate.add(Calendar.HOUR_OF_DAY, offset); - mHourSpinner.setValue(getCurrentHour()); - updateDateControl(); - int newHour = getCurrentHourOfDay(); - if (newHour >= HOURS_IN_HALF_DAY) { - mIsAm = false; - updateAmPmControl(); - } else { - mIsAm = true; - updateAmPmControl(); - } - } - mDate.set(Calendar.MINUTE, newVal); - onDateTimeChanged(); - } - }; - - private NumberPicker.OnValueChangeListener mOnAmPmChangedListener = new NumberPicker.OnValueChangeListener() { - //对AM和PM的监听 - @Override - public void onValueChange(NumberPicker picker, int oldVal, int newVal) { - mIsAm = !mIsAm; - if (mIsAm) { - mDate.add(Calendar.HOUR_OF_DAY, -HOURS_IN_HALF_DAY); - } else { - mDate.add(Calendar.HOUR_OF_DAY, HOURS_IN_HALF_DAY); - } - updateAmPmControl(); - onDateTimeChanged(); - } - }; - - public interface OnDateTimeChangedListener { - void onDateTimeChanged(DateTimePicker view, int year, int month, - int dayOfMonth, int hourOfDay, int minute); - } - - public DateTimePicker(Context context) { - this(context, System.currentTimeMillis()); - } - //通过对数据库的访问,获取当前的系统时间 - public DateTimePicker(Context context, long date) { - this(context, date, DateFormat.is24HourFormat(context)); - }//上面函数的得到的是一个天文数字(1970至今的秒数),需要DateFormat将其变得有意义 - - public DateTimePicker(Context context, long date, boolean is24HourView) { - super(context);//获取系统时间 - mDate = Calendar.getInstance(); - mInitialising = true; - mIsAm = getCurrentHourOfDay() >= HOURS_IN_HALF_DAY; - inflate(context, R.layout.datetime_picker, this); - //如果当前Activity里用到别的layout,比如对话框layout - //还要设置这个layout上的其他组件的内容,就必须用inflate()方法先将对话框的layout找出来 - //然后再用findViewById()找到它上面的其它组件 - mDateSpinner = (NumberPicker) findViewById(R.id.date); - mDateSpinner.setMinValue(DATE_SPINNER_MIN_VAL); - mDateSpinner.setMaxValue(DATE_SPINNER_MAX_VAL); - mDateSpinner.setOnValueChangedListener(mOnDateChangedListener); - - mHourSpinner = (NumberPicker) findViewById(R.id.hour); - mHourSpinner.setOnValueChangedListener(mOnHourChangedListener); - mMinuteSpinner = (NumberPicker) findViewById(R.id.minute); - mMinuteSpinner.setMinValue(MINUT_SPINNER_MIN_VAL); - mMinuteSpinner.setMaxValue(MINUT_SPINNER_MAX_VAL); - mMinuteSpinner.setOnLongPressUpdateInterval(100); - mMinuteSpinner.setOnValueChangedListener(mOnMinuteChangedListener); - - String[] stringsForAmPm = new DateFormatSymbols().getAmPmStrings(); - mAmPmSpinner = (NumberPicker) findViewById(R.id.amPm); - mAmPmSpinner.setMinValue(AMPM_SPINNER_MIN_VAL); - mAmPmSpinner.setMaxValue(AMPM_SPINNER_MAX_VAL); - mAmPmSpinner.setDisplayedValues(stringsForAmPm); - mAmPmSpinner.setOnValueChangedListener(mOnAmPmChangedListener); - - // update controls to initial state - updateDateControl(); - updateHourControl(); - updateAmPmControl(); - - set24HourView(is24HourView); - - // set to current time - setCurrentDate(date); - - setEnabled(isEnabled()); - - // set the content descriptions - mInitialising = false; - } - - @Override - public void setEnabled(boolean enabled) { - if (mIsEnabled == enabled) { - return; - } - super.setEnabled(enabled); - mDateSpinner.setEnabled(enabled); - mMinuteSpinner.setEnabled(enabled); - mHourSpinner.setEnabled(enabled); - mAmPmSpinner.setEnabled(enabled); - mIsEnabled = enabled; - } - //存在疑问:setEnabled的作用 - //下面的代码通过原程序的注释已经比较清晰,另外可以通过函数名来判断 - //下面的各函数主要是对上面代码引用到的各函数功能的实现 - @Override - public boolean isEnabled() { - return mIsEnabled; - } - - /** - * Get the current date in millis - * - * @return the current date in millis - */ - public long getCurrentDateInTimeMillis() { - return mDate.getTimeInMillis(); - } - //实现函数——得到当前的秒数 - - /** - * Set the current date - * - * @param date The current date in millis - */ - public void setCurrentDate(long date) { - Calendar cal = Calendar.getInstance(); - cal.setTimeInMillis(date); - setCurrentDate(cal.get(Calendar.YEAR), cal.get(Calendar.MONTH), cal.get(Calendar.DAY_OF_MONTH), - cal.get(Calendar.HOUR_OF_DAY), cal.get(Calendar.MINUTE)); - }//实现函数功能——设置当前的时间,参数是date - - /** - * Set the current date - * - * @param year The current year - * @param month The current month - * @param dayOfMonth The current dayOfMonth - * @param hourOfDay The current hourOfDay - * @param minute The current minute - */ - public void setCurrentDate(int year, int month, - int dayOfMonth, int hourOfDay, int minute) { - setCurrentYear(year); - setCurrentMonth(month); - setCurrentDay(dayOfMonth); - setCurrentHour(hourOfDay); - setCurrentMinute(minute); - }//实现函数功能——设置当前的时间,参数是各详细的变量 - - /** - * Get current year - * - * @return The current year - */ - //下面是得到year、month、day等值 - public int getCurrentYear() { - return mDate.get(Calendar.YEAR); - } - - /** - * Set current year - * - * @param year The current year - */ - public void setCurrentYear(int year) { - if (!mInitialising && year == getCurrentYear()) { - return; - } - mDate.set(Calendar.YEAR, year); - updateDateControl(); - onDateTimeChanged(); - } - - /** - * Get current month in the year - * - * @return The current month in the year - */ - public int getCurrentMonth() { - return mDate.get(Calendar.MONTH); - } - - /** - * Set current month in the year - * - * @param month The month in the year - */ - public void setCurrentMonth(int month) { - if (!mInitialising && month == getCurrentMonth()) { - return; - } - mDate.set(Calendar.MONTH, month); - updateDateControl(); - onDateTimeChanged(); - } - - /** - * Get current day of the month - * - * @return The day of the month - */ - public int getCurrentDay() { - return mDate.get(Calendar.DAY_OF_MONTH); - } - - /** - * Set current day of the month - * - * @param dayOfMonth The day of the month - */ - public void setCurrentDay(int dayOfMonth) { - if (!mInitialising && dayOfMonth == getCurrentDay()) { - return; - } - mDate.set(Calendar.DAY_OF_MONTH, dayOfMonth); - updateDateControl(); - onDateTimeChanged(); - } - - /** - * Get current hour in 24 hour mode, in the range (0~23) - * @return The current hour in 24 hour mode - */ - public int getCurrentHourOfDay() { - return mDate.get(Calendar.HOUR_OF_DAY); - } - - private int getCurrentHour() { - if (mIs24HourView){ - return getCurrentHourOfDay(); - } else { - int hour = getCurrentHourOfDay(); - if (hour > HOURS_IN_HALF_DAY) { - return hour - HOURS_IN_HALF_DAY; - } else { - return hour == 0 ? HOURS_IN_HALF_DAY : hour; - } - } - } - - /** - * Set current hour in 24 hour mode, in the range (0~23) - * - * @param hourOfDay - */ - public void setCurrentHour(int hourOfDay) { - if (!mInitialising && hourOfDay == getCurrentHourOfDay()) { - return; - } - mDate.set(Calendar.HOUR_OF_DAY, hourOfDay); - if (!mIs24HourView) { - if (hourOfDay >= HOURS_IN_HALF_DAY) { - mIsAm = false; - if (hourOfDay > HOURS_IN_HALF_DAY) { - hourOfDay -= HOURS_IN_HALF_DAY; - } - } else { - mIsAm = true; - if (hourOfDay == 0) { - hourOfDay = HOURS_IN_HALF_DAY; - } - } - updateAmPmControl(); - } - mHourSpinner.setValue(hourOfDay); - onDateTimeChanged(); - } - - /** - * Get currentMinute - * - * @return The Current Minute - */ - public int getCurrentMinute() { - return mDate.get(Calendar.MINUTE); - } - - /** - * Set current minute - */ - public void setCurrentMinute(int minute) { - if (!mInitialising && minute == getCurrentMinute()) { - return; - } - mMinuteSpinner.setValue(minute); - mDate.set(Calendar.MINUTE, minute); - onDateTimeChanged(); - } - - /** - * @return true if this is in 24 hour view else false. - */ - public boolean is24HourView () { - return mIs24HourView; - } - - /** - * Set whether in 24 hour or AM/PM mode. - * - * @param is24HourView True for 24 hour mode. False for AM/PM mode. - */ - public void set24HourView(boolean is24HourView) { - if (mIs24HourView == is24HourView) { - return; - } - mIs24HourView = is24HourView; - mAmPmSpinner.setVisibility(is24HourView ? View.GONE : View.VISIBLE); - int hour = getCurrentHourOfDay(); - updateHourControl(); - setCurrentHour(hour); - updateAmPmControl(); - } - - private void updateDateControl() { - Calendar cal = Calendar.getInstance(); - cal.setTimeInMillis(mDate.getTimeInMillis()); - cal.add(Calendar.DAY_OF_YEAR, -DAYS_IN_ALL_WEEK / 2 - 1); - mDateSpinner.setDisplayedValues(null); - for (int i = 0; i < DAYS_IN_ALL_WEEK; ++i) { - cal.add(Calendar.DAY_OF_YEAR, 1); - mDateDisplayValues[i] = (String) DateFormat.format("MM.dd EEEE", cal); - } - mDateSpinner.setDisplayedValues(mDateDisplayValues); - mDateSpinner.setValue(DAYS_IN_ALL_WEEK / 2); - mDateSpinner.invalidate(); - }// 对于星期几的算法 - - private void updateAmPmControl() { - if (mIs24HourView) { - mAmPmSpinner.setVisibility(View.GONE); - } else { - int index = mIsAm ? Calendar.AM : Calendar.PM; - mAmPmSpinner.setValue(index); - mAmPmSpinner.setVisibility(View.VISIBLE); - } - }// 对于上下午操作的算法 - - private void updateHourControl() { - if (mIs24HourView) { - mHourSpinner.setMinValue(HOUR_SPINNER_MIN_VAL_24_HOUR_VIEW); - mHourSpinner.setMaxValue(HOUR_SPINNER_MAX_VAL_24_HOUR_VIEW); - } else { - mHourSpinner.setMinValue(HOUR_SPINNER_MIN_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 the callback, if null will do nothing - */ - public void setOnDateTimeChangedListener(OnDateTimeChangedListener callback) { - mOnDateTimeChangedListener = callback; - } - - private void onDateTimeChanged() { - if (mOnDateTimeChangedListener != null) { - mOnDateTimeChangedListener.onDateTimeChanged(this, getCurrentYear(), - getCurrentMonth(), getCurrentDay(), getCurrentHourOfDay(), getCurrentMinute()); - } - } -} - diff --git a/other/06_凌子杰_代码标注/ui/DateTimePickerDialog.java b/other/06_凌子杰_代码标注/ui/DateTimePickerDialog.java deleted file mode 100644 index 3df9568..0000000 --- a/other/06_凌子杰_代码标注/ui/DateTimePickerDialog.java +++ /dev/null @@ -1,102 +0,0 @@ -/* - * 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.ui; - -import java.util.Calendar; - -import net.micode.notes.R; -import net.micode.notes.ui.DateTimePicker; -import net.micode.notes.ui.DateTimePicker.OnDateTimeChangedListener; - -import android.app.AlertDialog; -import android.content.Context; -import android.content.DialogInterface; -import android.content.DialogInterface.OnClickListener; -import android.text.format.DateFormat; -import android.text.format.DateUtils; - -public class DateTimePickerDialog extends AlertDialog implements OnClickListener { - - private Calendar mDate = Calendar.getInstance(); - //创建一个Calendar类型的变量 mDate,方便时间的操作 - private boolean mIs24HourView; - private OnDateTimeSetListener mOnDateTimeSetListener; - //声明一个时间日期滚动选择控件 mOnDateTimeSetListener - private DateTimePicker mDateTimePicker; - //DateTimePicker控件,控件一般用于让用户可以从日期列表中选择单个值。 - //运行时,单击控件边上的下拉箭头,会显示为两个部分:一个下拉列表,一个用于选择日期的 - public interface OnDateTimeSetListener { - void OnDateTimeSet(AlertDialog dialog, long date); - } - - public DateTimePickerDialog(Context context, long date) { - //对该界面对话框的实例化 - super(context); - //对数据库的操作 - mDateTimePicker = new DateTimePicker(context); - setView(mDateTimePicker); - //添加一个子视图 - mDateTimePicker.setOnDateTimeChangedListener(new OnDateTimeChangedListener() { - public void onDateTimeChanged(DateTimePicker view, int year, int month, - int dayOfMonth, int hourOfDay, int minute) { - mDate.set(Calendar.YEAR, year); - mDate.set(Calendar.MONTH, month); - mDate.set(Calendar.DAY_OF_MONTH, dayOfMonth); - mDate.set(Calendar.HOUR_OF_DAY, hourOfDay); - mDate.set(Calendar.MINUTE, minute); - //将视图中的各选项设置为系统当前时间 - updateTitle(mDate.getTimeInMillis()); - } - }); - mDate.setTimeInMillis(date); - //得到系统时间 - mDate.set(Calendar.SECOND, 0); - //将秒数设置为0 - mDateTimePicker.setCurrentDate(mDate.getTimeInMillis()); - setButton(context.getString(R.string.datetime_dialog_ok), this); - setButton2(context.getString(R.string.datetime_dialog_cancel), (OnClickListener)null); - //设置按钮 - set24HourView(DateFormat.is24HourFormat(this.getContext())); - //时间标准化打印 - updateTitle(mDate.getTimeInMillis()); - } - - public void set24HourView(boolean is24HourView) { - mIs24HourView = is24HourView; - } - - public void setOnDateTimeSetListener(OnDateTimeSetListener callBack) { - mOnDateTimeSetListener = callBack; - }//将时间日期滚动选择控件实例化 - - private void updateTitle(long date) { - int flag = - DateUtils.FORMAT_SHOW_YEAR | - DateUtils.FORMAT_SHOW_DATE | - DateUtils.FORMAT_SHOW_TIME; - flag |= mIs24HourView ? DateUtils.FORMAT_24HOUR : DateUtils.FORMAT_24HOUR; - setTitle(DateUtils.formatDateTime(this.getContext(), date, flag)); - }//android开发中常见日期管理工具类(API)——DateUtils:按照上下午显示时间 - - public void onClick(DialogInterface arg0, int arg1) { - if (mOnDateTimeSetListener != null) { - mOnDateTimeSetListener.OnDateTimeSet(this, mDate.getTimeInMillis()); - } - }//第一个参数arg0是接收到点击事件的对话框 - //第二个参数arg1是该对话框上的按钮 - -} \ No newline at end of file diff --git a/other/06_凌子杰_代码标注/ui/DropdownMenu.java b/other/06_凌子杰_代码标注/ui/DropdownMenu.java deleted file mode 100644 index 7a4e0da..0000000 --- a/other/06_凌子杰_代码标注/ui/DropdownMenu.java +++ /dev/null @@ -1,67 +0,0 @@ -/* - * 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.ui; - -import android.content.Context; -import android.view.Menu; -import android.view.MenuItem; -import android.view.View; -import android.view.View.OnClickListener; -import android.widget.Button; -import android.widget.PopupMenu; -import android.widget.PopupMenu.OnMenuItemClickListener; - -import net.micode.notes.R; - -public class DropdownMenu { - private Button mButton; - private PopupMenu mPopupMenu; - //声明一个下拉菜单 - private Menu mMenu; - - public DropdownMenu(Context context, Button button, int menuId) { - mButton = button; - mButton.setBackgroundResource(R.drawable.dropdown_icon); - //设置这个view的背景 - mPopupMenu = new PopupMenu(context, mButton); - mMenu = mPopupMenu.getMenu(); - mPopupMenu.getMenuInflater().inflate(menuId, mMenu); - //MenuInflater是用来实例化Menu目录下的Menu布局文件 - //根据ID来确认menu的内容选项 - mButton.setOnClickListener(new OnClickListener() { - public void onClick(View v) { - mPopupMenu.show(); - } - }); - } - - public void setOnDropdownMenuItemClickListener(OnMenuItemClickListener listener) { - if (mPopupMenu != null) { - mPopupMenu.setOnMenuItemClickListener(listener); - }//设置菜单的监听 - } - - public MenuItem findItem(int id) { - return mMenu.findItem(id); - } - //对于菜单选项的初始化,根据索引搜索菜单需要的选项 - - public void setTitle(CharSequence title) { - mButton.setText(title); - } - //布局文件,设置标题 -} diff --git a/other/06_凌子杰_代码标注/ui/FoldersListAdapter.java b/other/06_凌子杰_代码标注/ui/FoldersListAdapter.java deleted file mode 100644 index ab83701..0000000 --- a/other/06_凌子杰_代码标注/ui/FoldersListAdapter.java +++ /dev/null @@ -1,87 +0,0 @@ -/* - * 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.ui; - -import android.content.Context; -import android.database.Cursor; -import android.view.View; -import android.view.ViewGroup; -import android.widget.CursorAdapter; -import android.widget.LinearLayout; -import android.widget.TextView; - -import net.micode.notes.R; -import net.micode.notes.data.Notes; -import net.micode.notes.data.Notes.NoteColumns; - - -public class FoldersListAdapter extends CursorAdapter { - //CursorAdapter是Cursor和ListView的接口 - //FoldersListAdapter继承了CursorAdapter的类 - //主要作用是便签数据库和用户的交互 - //这里就是用folder(文件夹)的形式展现给用户 - public static final String [] PROJECTION = { - NoteColumns.ID, - NoteColumns.SNIPPET - };//调用数据库中便签的ID和片段 - - public static final int ID_COLUMN = 0; - public static final int NAME_COLUMN = 1; - - public FoldersListAdapter(Context context, Cursor c) { - super(context, c); - // TODO Auto-generated constructor stub - }//数据库操作 - - @Override - public View newView(Context context, Cursor cursor, ViewGroup parent) { - //ViewGroup是容器 - return new FolderListItem(context); - }//创建一个文件夹,对于各文件夹中子标签的初始化 - - @Override - public void bindView(View view, Context context, Cursor cursor) { - if (view instanceof FolderListItem) { - String folderName = (cursor.getLong(ID_COLUMN) == Notes.ID_ROOT_FOLDER) ? context - .getString(R.string.menu_move_parent_folder) : cursor.getString(NAME_COLUMN); - ((FolderListItem) view).bind(folderName); - } - }//将各个布局文件绑定起来 - - public String getFolderName(Context context, int position) { - Cursor cursor = (Cursor) getItem(position); - return (cursor.getLong(ID_COLUMN) == Notes.ID_ROOT_FOLDER) ? context - .getString(R.string.menu_move_parent_folder) : cursor.getString(NAME_COLUMN); - }//根据数据库中标签的ID得到标签的各项内容 - - private class FolderListItem extends LinearLayout { - private TextView mName; - - public FolderListItem(Context context) { - super(context); - //操作数据库 - inflate(context, R.layout.folder_list_item, this); - //根据布局文件的名字等信息将其找出来 - mName = (TextView) findViewById(R.id.tv_folder_name); - } - - public void bind(String name) { - mName.setText(name); - } - } - -} diff --git a/other/06_凌子杰_代码标注/ui/NoteEditActivity.java b/other/06_凌子杰_代码标注/ui/NoteEditActivity.java deleted file mode 100644 index 8ba7ee3..0000000 --- a/other/06_凌子杰_代码标注/ui/NoteEditActivity.java +++ /dev/null @@ -1,1049 +0,0 @@ -/* - * 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.ui; - -import android.app.Activity; -import android.app.AlarmManager; -import android.app.AlertDialog; -import android.app.PendingIntent; -import android.app.SearchManager; -import android.appwidget.AppWidgetManager; -import android.content.ContentUris; -import android.content.Context; -import android.content.DialogInterface; -import android.content.Intent; -import android.content.SharedPreferences; -import android.graphics.Paint; -import android.os.Bundle; -import android.preference.PreferenceManager; -import android.text.Spannable; -import android.text.SpannableString; -import android.text.TextUtils; -import android.text.format.DateUtils; -import android.text.style.BackgroundColorSpan; -import android.util.Log; -import android.view.LayoutInflater; -import android.view.Menu; -import android.view.MenuItem; -import android.view.MotionEvent; -import android.view.View; -import android.view.View.OnClickListener; -import android.view.WindowManager; -import android.widget.CheckBox; -import android.widget.CompoundButton; -import android.widget.CompoundButton.OnCheckedChangeListener; -import android.widget.EditText; -import android.widget.ImageView; -import android.widget.LinearLayout; -import android.widget.TextView; -import android.widget.Toast; - -import net.micode.notes.R; -import net.micode.notes.data.Notes; -import net.micode.notes.data.Notes.TextNote; -import net.micode.notes.model.WorkingNote; -import net.micode.notes.model.WorkingNote.NoteSettingChangedListener; -import net.micode.notes.tool.DataUtils; -import net.micode.notes.tool.ResourceParser; -import net.micode.notes.tool.ResourceParser.TextAppearanceResources; -import net.micode.notes.ui.DateTimePickerDialog.OnDateTimeSetListener; -import net.micode.notes.ui.NoteEditText.OnTextViewChangeListener; -import net.micode.notes.widget.NoteWidgetProvider_2x; -import net.micode.notes.widget.NoteWidgetProvider_4x; - -import java.util.HashMap; -import java.util.HashSet; -import java.util.Map; -import java.util.regex.Matcher; -import java.util.regex.Pattern; - - -public class NoteEditActivity extends Activity implements OnClickListener, - NoteSettingChangedListener, OnTextViewChangeListener { - //该类主要是针对标签的编辑 - //继承了系统内部许多和监听有关的类 - private class HeadViewHolder { - public TextView tvModified; - - public ImageView ivAlertIcon; - - public TextView tvAlertDate; - - public ImageView ibSetBgColor; - } - //使用Map实现数据存储 - private static final Map sBgSelectorBtnsMap = new HashMap(); - static { - sBgSelectorBtnsMap.put(R.id.iv_bg_yellow, ResourceParser.YELLOW); - sBgSelectorBtnsMap.put(R.id.iv_bg_red, ResourceParser.RED); - sBgSelectorBtnsMap.put(R.id.iv_bg_blue, ResourceParser.BLUE); - sBgSelectorBtnsMap.put(R.id.iv_bg_green, ResourceParser.GREEN); - sBgSelectorBtnsMap.put(R.id.iv_bg_white, ResourceParser.WHITE); - //put函数是将指定值和指定键相连 - } - - private static final Map sBgSelectorSelectionMap = new HashMap(); - static { - sBgSelectorSelectionMap.put(ResourceParser.YELLOW, R.id.iv_bg_yellow_select); - sBgSelectorSelectionMap.put(ResourceParser.RED, R.id.iv_bg_red_select); - sBgSelectorSelectionMap.put(ResourceParser.BLUE, R.id.iv_bg_blue_select); - sBgSelectorSelectionMap.put(ResourceParser.GREEN, R.id.iv_bg_green_select); - sBgSelectorSelectionMap.put(ResourceParser.WHITE, R.id.iv_bg_white_select); - //put函数是将指定值和指定键相连 - } - - private static final Map sFontSizeBtnsMap = new HashMap(); - static { - sFontSizeBtnsMap.put(R.id.ll_font_large, ResourceParser.TEXT_LARGE); - sFontSizeBtnsMap.put(R.id.ll_font_small, ResourceParser.TEXT_SMALL); - sFontSizeBtnsMap.put(R.id.ll_font_normal, ResourceParser.TEXT_MEDIUM); - sFontSizeBtnsMap.put(R.id.ll_font_super, ResourceParser.TEXT_SUPER); - //put函数是将指定值和指定键相连 - } - - private static final Map sFontSelectorSelectionMap = new HashMap(); - static { - sFontSelectorSelectionMap.put(ResourceParser.TEXT_LARGE, R.id.iv_large_select); - sFontSelectorSelectionMap.put(ResourceParser.TEXT_SMALL, R.id.iv_small_select); - sFontSelectorSelectionMap.put(ResourceParser.TEXT_MEDIUM, R.id.iv_medium_select); - sFontSelectorSelectionMap.put(ResourceParser.TEXT_SUPER, R.id.iv_super_select); - //put函数是将指定值和指定键相连 - } - - private static final String TAG = "NoteEditActivity"; - - private HeadViewHolder mNoteHeaderHolder; - - private View mHeadViewPanel; - //私有化一个界面操作mHeadViewPanel,对表头的操作 - private View mNoteBgColorSelector; - //私有化一个界面操作mNoteBgColorSelector,对背景颜色的操作 - private View mFontSizeSelector; - //私有化一个界面操作mFontSizeSelector,对标签字体的操作 - private EditText mNoteEditor; - //声明编辑控件,对文本操作 - private View mNoteEditorPanel; - //私有化一个界面操作mNoteEditorPanel,文本编辑的控制板 - //private WorkingNote mWorkingNote; - private WorkingNote mWorkingNote; - //对模板WorkingNote的初始化 - private SharedPreferences mSharedPrefs; - //私有化SharedPreferences的数据存储方式 - //它的本质是基于XML文件存储key-value键值对数据 - private int mFontSizeId; - //用于操作字体的大小 - private static final String PREFERENCE_FONT_SIZE = "pref_font_size"; - - private static final int SHORTCUT_ICON_TITLE_MAX_LEN = 10; - - public static final String TAG_CHECKED = String.valueOf('\u221A'); - public static final String TAG_UNCHECKED = String.valueOf('\u25A1'); - - private LinearLayout mEditTextList; - //线性布局 - private String mUserQuery; - private Pattern mPattern; - - @Override - protected void onCreate(Bundle savedInstanceState) { - super.onCreate(savedInstanceState); - this.setContentView(R.layout.note_edit); - //对数据库的访问操作 - if (savedInstanceState == null && !initActivityState(getIntent())) { - finish(); - return; - } - initResources(); - } - - /** - * Current activity may be killed when the memory is low. Once it is killed, for another time - * user load this activity, we should restore the former state - */ - @Override - protected void onRestoreInstanceState(Bundle savedInstanceState) { - super.onRestoreInstanceState(savedInstanceState); - if (savedInstanceState != null && savedInstanceState.containsKey(Intent.EXTRA_UID)) { - Intent intent = new Intent(Intent.ACTION_VIEW); - intent.putExtra(Intent.EXTRA_UID, savedInstanceState.getLong(Intent.EXTRA_UID)); - if (!initActivityState(intent)) { - finish(); - return; - } - Log.d(TAG, "Restoring from killed activity"); - }//为防止内存不足时程序的终止,在这里有一个保存现场的函数 - } - - private boolean initActivityState(Intent intent) { - /** - * If the user specified the {@link Intent#ACTION_VIEW} but not provided with id, - * then jump to the NotesListActivity - */ - mWorkingNote = null; - if (TextUtils.equals(Intent.ACTION_VIEW, intent.getAction())) { - long noteId = intent.getLongExtra(Intent.EXTRA_UID, 0); - mUserQuery = ""; - //如果用户实例化标签时,系统并未给出标签ID - /** - * Starting from the searched result - */ - //根据键值查找ID - if (intent.hasExtra(SearchManager.EXTRA_DATA_KEY)) { - noteId = Long.parseLong(intent.getStringExtra(SearchManager.EXTRA_DATA_KEY)); - mUserQuery = intent.getStringExtra(SearchManager.USER_QUERY); - } - //如果ID在数据库中未找到 - if (!DataUtils.visibleInNoteDatabase(getContentResolver(), noteId, Notes.TYPE_NOTE)) { - Intent jump = new Intent(this, NotesListActivity.class); - startActivity(jump); - //程序将跳转到上面声明的intent——jump - showToast(R.string.error_note_not_exist); - finish(); - return false; - }//ID在数据库中找到 - else { - mWorkingNote = WorkingNote.load(this, noteId); - if (mWorkingNote == null) { - Log.e(TAG, "load note failed with note id" + noteId); - //打印出红色的错误信息 - finish(); - return false; - } - } - //setSoftInputMode——软键盘输入模式 - getWindow().setSoftInputMode( - WindowManager.LayoutParams.SOFT_INPUT_STATE_HIDDEN - | WindowManager.LayoutParams.SOFT_INPUT_ADJUST_RESIZE); - } else if(TextUtils.equals(Intent.ACTION_INSERT_OR_EDIT, intent.getAction())) { - // New note - // 大多用于broadcast发送广播时给机制(intent)设置一个action,就是一个字符串 - // 用户可以通过receive(接受)intent,通过 getAction得到的字符串,来决定做什么 - long folderId = intent.getLongExtra(Notes.INTENT_EXTRA_FOLDER_ID, 0); - int widgetId = intent.getIntExtra(Notes.INTENT_EXTRA_WIDGET_ID, - AppWidgetManager.INVALID_APPWIDGET_ID); - int widgetType = intent.getIntExtra(Notes.INTENT_EXTRA_WIDGET_TYPE, - Notes.TYPE_WIDGET_INVALIDE); - int bgResId = intent.getIntExtra(Notes.INTENT_EXTRA_BACKGROUND_ID, - ResourceParser.getDefaultBgId(this)); - // intent.getInt(Long、String)Extra是对各变量的语法分析 - // Parse call-record note - String phoneNumber = intent.getStringExtra(Intent.EXTRA_PHONE_NUMBER); - long callDate = intent.getLongExtra(Notes.INTENT_EXTRA_CALL_DATE, 0); - if (callDate != 0 && phoneNumber != null) { - if (TextUtils.isEmpty(phoneNumber)) { - Log.w(TAG, "The call record number is null"); - } - long noteId = 0; - if ((noteId = DataUtils.getNoteIdByPhoneNumberAndCallDate(getContentResolver(), - phoneNumber, callDate)) > 0) { - mWorkingNote = WorkingNote.load(this, noteId); - if (mWorkingNote == null) { - Log.e(TAG, "load call note failed with note id" + noteId); - finish(); - return false; - }//将电话号码与手机的号码簿相关 - } else { - mWorkingNote = WorkingNote.createEmptyNote(this, folderId, widgetId, - widgetType, bgResId); - mWorkingNote.convertToCallNote(phoneNumber, callDate); - } - } else { - mWorkingNote = WorkingNote.createEmptyNote(this, folderId, widgetId, widgetType, - bgResId); - }//创建一个新的WorkingNote - - getWindow().setSoftInputMode( - WindowManager.LayoutParams.SOFT_INPUT_ADJUST_RESIZE - | WindowManager.LayoutParams.SOFT_INPUT_STATE_VISIBLE); - } else { - Log.e(TAG, "Intent not specified action, should not support"); - finish(); - return false; - } - mWorkingNote.setOnSettingStatusChangedListener(this); - return true; - } - - @Override - protected void onResume() { - super.onResume(); - initNoteScreen(); - } - - private void initNoteScreen() { - //对界面的初始化操作 - mNoteEditor.setTextAppearance(this, TextAppearanceResources - .getTexAppearanceResource(mFontSizeId)); - //设置外观 - if (mWorkingNote.getCheckListMode() == TextNote.MODE_CHECK_LIST) { - switchToListMode(mWorkingNote.getContent()); - } else { - mNoteEditor.setText(getHighlightQueryResult(mWorkingNote.getContent(), mUserQuery)); - mNoteEditor.setSelection(mNoteEditor.getText().length()); - } - for (Integer id : sBgSelectorSelectionMap.keySet()) { - findViewById(sBgSelectorSelectionMap.get(id)).setVisibility(View.GONE); - } - mHeadViewPanel.setBackgroundResource(mWorkingNote.getTitleBgResId()); - mNoteEditorPanel.setBackgroundResource(mWorkingNote.getBgColorResId()); - - mNoteHeaderHolder.tvModified.setText(DateUtils.formatDateTime(this, - mWorkingNote.getModifiedDate(), DateUtils.FORMAT_SHOW_DATE - | DateUtils.FORMAT_NUMERIC_DATE | DateUtils.FORMAT_SHOW_TIME - | DateUtils.FORMAT_SHOW_YEAR)); - - /** - * TODO: Add the menu for setting alert. Currently disable it because the DateTimePicker - * is not ready - */ - showAlertHeader(); - } - //设置闹钟的显示 - private void showAlertHeader() { - if (mWorkingNote.hasClockAlert()) { - long time = System.currentTimeMillis(); - if (time > mWorkingNote.getAlertDate()) { - mNoteHeaderHolder.tvAlertDate.setText(R.string.note_alert_expired); - } //如果系统时间大于了闹钟设置的时间,那么闹钟失效 - else { - mNoteHeaderHolder.tvAlertDate.setText(DateUtils.getRelativeTimeSpanString( - mWorkingNote.getAlertDate(), time, DateUtils.MINUTE_IN_MILLIS)); - } - mNoteHeaderHolder.tvAlertDate.setVisibility(View.VISIBLE); - mNoteHeaderHolder.ivAlertIcon.setVisibility(View.VISIBLE); - } //显示闹钟开启的图标 - else { - mNoteHeaderHolder.tvAlertDate.setVisibility(View.GONE); - mNoteHeaderHolder.ivAlertIcon.setVisibility(View.GONE); - }; - } - - @Override - protected void onNewIntent(Intent intent) { - super.onNewIntent(intent); - initActivityState(intent); - } - - @Override - protected void onSaveInstanceState(Bundle outState) { - super.onSaveInstanceState(outState); - /** - * For new note without note id, we should firstly save it to - * generate a id. If the editing note is not worth saving, there - * is no id which is equivalent to create new note - */ - if (!mWorkingNote.existInDatabase()) { - saveNote(); - } - //在创建一个新的标签时,先在数据库中匹配 - //如果不存在,那么先在数据库中存储 - outState.putLong(Intent.EXTRA_UID, mWorkingNote.getNoteId()); - Log.d(TAG, "Save working note id: " + mWorkingNote.getNoteId() + " onSaveInstanceState"); - } - - @Override - //MotionEvent是对屏幕触控的传递机制 - public boolean dispatchTouchEvent(MotionEvent ev) { - if (mNoteBgColorSelector.getVisibility() == View.VISIBLE - && !inRangeOfView(mNoteBgColorSelector, ev)) { - mNoteBgColorSelector.setVisibility(View.GONE); - return true; - }//颜色选择器在屏幕上可见 - - if (mFontSizeSelector.getVisibility() == View.VISIBLE - && !inRangeOfView(mFontSizeSelector, ev)) { - mFontSizeSelector.setVisibility(View.GONE); - return true; - }//字体大小选择器在屏幕上可见 - return super.dispatchTouchEvent(ev); - } - //对屏幕触控的坐标进行操作 - private boolean inRangeOfView(View view, MotionEvent ev) { - int []location = new int[2]; - view.getLocationOnScreen(location); - int x = location[0]; - int y = location[1]; - if (ev.getX() < x - || ev.getX() > (x + view.getWidth()) - || ev.getY() < y - || ev.getY() > (y + view.getHeight())) - //如果触控的位置超出了给定的范围,返回false - { - return false; - } - return true; - } - - private void initResources() { - mHeadViewPanel = findViewById(R.id.note_title); - mNoteHeaderHolder = new HeadViewHolder(); - mNoteHeaderHolder.tvModified = (TextView) findViewById(R.id.tv_modified_date); - mNoteHeaderHolder.ivAlertIcon = (ImageView) findViewById(R.id.iv_alert_icon); - mNoteHeaderHolder.tvAlertDate = (TextView) findViewById(R.id.tv_alert_date); - mNoteHeaderHolder.ibSetBgColor = (ImageView) findViewById(R.id.btn_set_bg_color); - mNoteHeaderHolder.ibSetBgColor.setOnClickListener(this); - mNoteEditor = (EditText) findViewById(R.id.note_edit_view); - mNoteEditorPanel = findViewById(R.id.sv_note_edit); - mNoteBgColorSelector = findViewById(R.id.note_bg_color_selector); - for (int id : sBgSelectorBtnsMap.keySet()) { - ImageView iv = (ImageView) findViewById(id); - iv.setOnClickListener(this); - }//对标签各项属性内容的初始化 - - mFontSizeSelector = findViewById(R.id.font_size_selector); - for (int id : sFontSizeBtnsMap.keySet()) { - View view = findViewById(id); - view.setOnClickListener(this); - };//对字体大小的选择 - mSharedPrefs = PreferenceManager.getDefaultSharedPreferences(this); - mFontSizeId = mSharedPrefs.getInt(PREFERENCE_FONT_SIZE, ResourceParser.BG_DEFAULT_FONT_SIZE); - /** - * HACKME: Fix bug of store the resource id in shared preference. - * The id may larger than the length of resources, in this case, - * return the {@link ResourceParser#BG_DEFAULT_FONT_SIZE} - */ - if(mFontSizeId >= TextAppearanceResources.getResourcesSize()) { - mFontSizeId = ResourceParser.BG_DEFAULT_FONT_SIZE; - } - mEditTextList = (LinearLayout) findViewById(R.id.note_edit_list); - } - - @Override - protected void onPause() { - super.onPause(); - if(saveNote()) { - Log.d(TAG, "Note data was saved with length:" + mWorkingNote.getContent().length()); - } - clearSettingState(); - } - //和桌面小工具的同步 - private void updateWidget() { - Intent intent = new Intent(AppWidgetManager.ACTION_APPWIDGET_UPDATE); - if (mWorkingNote.getWidgetType() == Notes.TYPE_WIDGET_2X) { - intent.setClass(this, NoteWidgetProvider_2x.class); - } else if (mWorkingNote.getWidgetType() == Notes.TYPE_WIDGET_4X) { - intent.setClass(this, NoteWidgetProvider_4x.class); - } else { - Log.e(TAG, "Unspported widget type"); - return; - } - - intent.putExtra(AppWidgetManager.EXTRA_APPWIDGET_IDS, new int[] { - mWorkingNote.getWidgetId() - }); - - sendBroadcast(intent); - setResult(RESULT_OK, intent); - } - - public void onClick(View v) { - int id = v.getId(); - if (id == R.id.btn_set_bg_color) { - mNoteBgColorSelector.setVisibility(View.VISIBLE); - findViewById(sBgSelectorSelectionMap.get(mWorkingNote.getBgColorId())).setVisibility( - - View.VISIBLE); - } else if (sBgSelectorBtnsMap.containsKey(id)) { - findViewById(sBgSelectorSelectionMap.get(mWorkingNote.getBgColorId())).setVisibility( - View.GONE); - mWorkingNote.setBgColorId(sBgSelectorBtnsMap.get(id)); - mNoteBgColorSelector.setVisibility(View.GONE); - } else if (sFontSizeBtnsMap.containsKey(id)) { - findViewById(sFontSelectorSelectionMap.get(mFontSizeId)).setVisibility(View.GONE); - mFontSizeId = sFontSizeBtnsMap.get(id); - mSharedPrefs.edit().putInt(PREFERENCE_FONT_SIZE, mFontSizeId).commit(); - findViewById(sFontSelectorSelectionMap.get(mFontSizeId)).setVisibility(View.VISIBLE); - if (mWorkingNote.getCheckListMode() == TextNote.MODE_CHECK_LIST) { - getWorkingText(); - switchToListMode(mWorkingNote.getContent()); - } else { - mNoteEditor.setTextAppearance(this, - TextAppearanceResources.getTexAppearanceResource(mFontSizeId)); - } - mFontSizeSelector.setVisibility(View.GONE); - } - }//************************存在问题 - - @Override - public void onBackPressed() { - if(clearSettingState()) { - return; - } - - saveNote(); - super.onBackPressed(); - } - - private boolean clearSettingState() { - if (mNoteBgColorSelector.getVisibility() == View.VISIBLE) { - mNoteBgColorSelector.setVisibility(View.GONE); - return true; - } else if (mFontSizeSelector.getVisibility() == View.VISIBLE) { - mFontSizeSelector.setVisibility(View.GONE); - return true; - } - return false; - } - - public void onBackgroundColorChanged() { - findViewById(sBgSelectorSelectionMap.get(mWorkingNote.getBgColorId())).setVisibility( - View.VISIBLE); - mNoteEditorPanel.setBackgroundResource(mWorkingNote.getBgColorResId()); - mHeadViewPanel.setBackgroundResource(mWorkingNote.getTitleBgResId()); - } - - @Override - //对选择菜单的准备 - public boolean onPrepareOptionsMenu(Menu menu) { - if (isFinishing()) { - return true; - } - clearSettingState(); - menu.clear(); - if (mWorkingNote.getFolderId() == Notes.ID_CALL_RECORD_FOLDER) { - getMenuInflater().inflate(R.menu.call_note_edit, menu); - // MenuInflater是用来实例化Menu目录下的Menu布局文件的 - } else { - getMenuInflater().inflate(R.menu.note_edit, menu); - } - if (mWorkingNote.getCheckListMode() == TextNote.MODE_CHECK_LIST) { - menu.findItem(R.id.menu_list_mode).setTitle(R.string.menu_normal_mode); - } else { - menu.findItem(R.id.menu_list_mode).setTitle(R.string.menu_list_mode); - } - if (mWorkingNote.hasClockAlert()) { - menu.findItem(R.id.menu_alert).setVisible(false); - } else { - menu.findItem(R.id.menu_delete_remind).setVisible(false); - } - return true; - } - - @Override - public boolean onOptionsItemSelected(MenuItem item) { - switch (item.getItemId()) { - //根据菜单的id来编剧相关项目 - case R.id.menu_new_note: - //创建一个新的便签 - createNewNote(); - break; - case R.id.menu_delete: - //删除便签 - AlertDialog.Builder builder = new AlertDialog.Builder(this); - //创建关于删除操作的对话框 - builder.setTitle(getString(R.string.alert_title_delete)); - // 设置标签的标题为alert_title_delete - builder.setIcon(android.R.drawable.ic_dialog_alert); - //设置对话框图标 - builder.setMessage(getString(R.string.alert_message_delete_note)); - //设置对话框内容 - builder.setPositiveButton(android.R.string.ok, - new DialogInterface.OnClickListener() { - //建立按键监听器 - public void onClick(DialogInterface dialog, int which) { - //点击所触发事件 - deleteCurrentNote(); - // 删除单签便签 - finish(); - } - }); - //添加“YES”按钮 - builder.setNegativeButton(android.R.string.cancel, null); - //添加“NO”的按钮 - builder.show(); - //显示对话框 - break; - case R.id.menu_font_size: - //字体大小的编辑 - mFontSizeSelector.setVisibility(View.VISIBLE); - // 将字体选择器置为可见1 - findViewById(sFontSelectorSelectionMap.get(mFontSizeId)).setVisibility(View.VISIBLE); - // 通过id找到相应的大小 - break; - case R.id.menu_list_mode: - //选择列表模式 - mWorkingNote.setCheckListMode(mWorkingNote.getCheckListMode() == 0 ? - TextNote.MODE_CHECK_LIST : 0); - break; - case R.id.menu_share: - //菜单共享 - getWorkingText(); - sendTo(this, mWorkingNote.getContent()); - // 用sendto函数将运行文本发送到遍历的本文内 - break; - case R.id.menu_send_to_desktop: - //发送到桌面 - sendToDesktop(); - break; - case R.id.menu_alert: - //创建提醒器 - setReminder(); - break; - case R.id.menu_delete_remind: - //删除日期提醒 - mWorkingNote.setAlertDate(0, false); - break; - default: - break; - } - return true; - } - /* - * 函数功能:建立事件提醒器 - * 函数实现:如下注释 - */ - private void setReminder() { - DateTimePickerDialog d = new DateTimePickerDialog(this, System.currentTimeMillis()); - // 建立修改时间日期的对话框 - d.setOnDateTimeSetListener(new OnDateTimeSetListener() { - public void OnDateTimeSet(AlertDialog dialog, long date) { - mWorkingNote.setAlertDate(date , true); - //选择提醒的日期 - } - }); - //建立时间日期的监听器 - d.show(); - //显示对话框 - } - - /** - * Share note to apps that support {@link Intent#ACTION_SEND} action - * and {@text/plain} type - */ - /* - * 函数功能:共享便签 - * 函数实现:如下注释 - */ - private void sendTo(Context context, String info) { - Intent intent = new Intent(Intent.ACTION_SEND); - //建立intent链接选项 - intent.putExtra(Intent.EXTRA_TEXT, info); - //将需要传递的便签信息放入text文件中 - intent.setType("text/plain"); - //编辑连接器的类型 - context.startActivity(intent); - //在acti中进行链接 - } - - private void createNewNote() { - // Firstly, save current editing notes - //保存当前便签 - saveNote(); - - // For safety, start a new NoteEditActivity - finish(); - Intent intent = new Intent(this, NoteEditActivity.class); - //设置链接器 - intent.setAction(Intent.ACTION_INSERT_OR_EDIT); - //该活动定义为创建或编辑 - intent.putExtra(Notes.INTENT_EXTRA_FOLDER_ID, mWorkingNote.getFolderId()); - //将运行便签的id添加到INTENT_EXTRA_FOLDER_ID标记中 - startActivity(intent); - //开始activity并链接 - } - /* - * 函数功能:删除当前便签 - * 函数实现:如下注释 - */ - private void deleteCurrentNote() { - if (mWorkingNote.existInDatabase()) { - //假如当前运行的便签内存有数据 - HashSet ids = new HashSet(); - long id = mWorkingNote.getNoteId(); - if (id != Notes.ID_ROOT_FOLDER) { - ids.add(id); - //如果不是头文件夹建立一个hash表把便签id存起来 - } else { - Log.d(TAG, "Wrong note id, should not happen"); - //否则报错 - } - if (!isSyncMode()) { - //在非同步模式情况下 - //删除操作 - if (!DataUtils.batchDeleteNotes(getContentResolver(), ids)) { - Log.e(TAG, "Delete Note error"); - } - } else { - //同步模式 - //移动至垃圾文件夹的操作 - if (!DataUtils.batchMoveToFolder(getContentResolver(), ids, Notes.ID_TRASH_FOLER)) { - Log.e(TAG, "Move notes to trash folder error, should not happens"); - } - } - } - mWorkingNote.markDeleted(true); - //将这些标签的删除标记置为true - } - /* - * 函数功能:判断是否为同步模式 - * 函数实现:直接看NotesPreferenceActivity中同步名称是否为空 - */ - private boolean isSyncMode() { - return NotesPreferenceActivity.getSyncAccountName(this).trim().length() > 0; - } - /* - * 函数功能:设置提醒时间 - * 函数实现:如下注释 - */ - public void onClockAlertChanged(long date, boolean set) { - /** - * User could set clock to an unsaved note, so before setting the - * alert clock, we should save the note first - */ - if (!mWorkingNote.existInDatabase()) { - //首先保存已有的便签 - saveNote(); - } - if (mWorkingNote.getNoteId() > 0) { - Intent intent = new Intent(this, AlarmReceiver.class); - intent.setData(ContentUris.withAppendedId(Notes.CONTENT_NOTE_URI, mWorkingNote.getNoteId())); - //若有有运行的便签就是建立一个链接器将标签id都存在uri中 - PendingIntent pendingIntent = PendingIntent.getBroadcast(this, 0, intent, 0); - AlarmManager alarmManager = ((AlarmManager) getSystemService(ALARM_SERVICE)); - //设置提醒管理器 - showAlertHeader(); - if(!set) { - alarmManager.cancel(pendingIntent); - } else { - alarmManager.set(AlarmManager.RTC_WAKEUP, date, pendingIntent); - } - //如果用户设置了时间,就通过提醒管理器设置一个监听事项 - } else { - /** - * There is the condition that user has input nothing (the note is - * not worthy saving), we have no note id, remind the user that he - * should input something - */ - //没有运行的便签就报错 - Log.e(TAG, "Clock alert setting error"); - showToast(R.string.error_note_empty_for_clock); - } - } - /* - * 函数功能:Widget发生改变的所触发的事件 - */ - public void onWidgetChanged() { - updateWidget(); - } - /* - * 函数功能: 删除编辑文本框所触发的事件 - * 函数实现:如下注释 - */ - public void onEditTextDelete(int index, String text) { - int childCount = mEditTextList.getChildCount(); - if (childCount == 1) { - return; - } - //没有编辑框的话直接返回 - for (int i = index + 1; i < childCount; i++) { - ((NoteEditText) mEditTextList.getChildAt(i).findViewById(R.id.et_edit_text)) - .setIndex(i - 1); - //通过id把编辑框存在便签编辑框中 - } - - mEditTextList.removeViewAt(index); - //删除特定位置的视图 - NoteEditText edit = null; - if(index == 0) { - edit = (NoteEditText) mEditTextList.getChildAt(0).findViewById( - R.id.et_edit_text); - } else { - edit = (NoteEditText) mEditTextList.getChildAt(index - 1).findViewById( - R.id.et_edit_text); - } - //通过id把编辑框存在空的NoteEditText中 - int length = edit.length(); - edit.append(text); - edit.requestFocus();//请求优先完成该此 编辑 - edit.setSelection(length);//定位到length位置处的条目 - } - /* - * 函数功能:进入编辑文本框所触发的事件 - * 函数实现:如下注释 - */ - public void onEditTextEnter(int index, String text) { - /** - * Should not happen, check for debug - */ - if(index > mEditTextList.getChildCount()) { - Log.e(TAG, "Index out of mEditTextList boundrary, should not happen"); - //越界把偶偶 - } - - View view = getListItem(text, index); - mEditTextList.addView(view, index); - //建立一个新的视图并添加到编辑文本框内 - NoteEditText edit = (NoteEditText) view.findViewById(R.id.et_edit_text); - edit.requestFocus();//请求优先操作 - edit.setSelection(0);//定位到起始位置 - for (int i = index + 1; i < mEditTextList.getChildCount(); i++) { - ((NoteEditText) mEditTextList.getChildAt(i).findViewById(R.id.et_edit_text)) - .setIndex(i); - //遍历子文本框并设置对应对下标 - } - } - /* - * 函数功能:切换至列表模式 - * 函数实现:如下注释 - */ - private void switchToListMode(String text) { - mEditTextList.removeAllViews(); - String[] items = text.split("\n"); - int index = 0; - //清空所有视图,初始化下标 - for (String item : items) { - if(!TextUtils.isEmpty(item)) { - mEditTextList.addView(getListItem(item, index)); - index++; - //遍历所有文本单元并添加到文本框中 - } - } - mEditTextList.addView(getListItem("", index)); - mEditTextList.getChildAt(index).findViewById(R.id.et_edit_text).requestFocus(); - //优先请求此操作 - mNoteEditor.setVisibility(View.GONE);//便签编辑器不可见 - mEditTextList.setVisibility(View.VISIBLE); //将文本编辑框置为可见 - } - /* - * 函数功能:获取高亮效果的反馈情况 - * 函数实现:如下注释 - */ - private Spannable getHighlightQueryResult(String fullText, String userQuery) { - SpannableString spannable = new SpannableString(fullText == null ? "" : fullText); - //新建一个效果选项 - if (!TextUtils.isEmpty(userQuery)) { - mPattern = Pattern.compile(userQuery); - //将用户的询问进行解析 - Matcher m = mPattern.matcher(fullText); - //建立一个状态机检查Pattern并进行匹配 - int start = 0; - while (m.find(start)) { - spannable.setSpan( - new BackgroundColorSpan(this.getResources().getColor( - R.color.user_query_highlight)), m.start(), m.end(), - Spannable.SPAN_INCLUSIVE_EXCLUSIVE); - //设置背景颜色 - start = m.end(); - //跟新起始位置 - } - } - return spannable; - } - /* - * 函数功能:获取列表项 - * 函数实现:如下注释 - */ - private View getListItem(String item, int index) { - View view = LayoutInflater.from(this).inflate(R.layout.note_edit_list_item, null); - //创建一个视图 - final NoteEditText edit = (NoteEditText) view.findViewById(R.id.et_edit_text); - edit.setTextAppearance(this, TextAppearanceResources.getTexAppearanceResource(mFontSizeId)); - //创建一个文本编辑框并设置可见性 - CheckBox cb = ((CheckBox) view.findViewById(R.id.cb_edit_item)); - cb.setOnCheckedChangeListener(new OnCheckedChangeListener() { - public void onCheckedChanged(CompoundButton buttonView, boolean isChecked) { - if (isChecked) { - edit.setPaintFlags(edit.getPaintFlags() | Paint.STRIKE_THRU_TEXT_FLAG); - } else { - edit.setPaintFlags(Paint.ANTI_ALIAS_FLAG | Paint.DEV_KERN_TEXT_FLAG); - } - } - }); - //建立一个打钩框并设置监听器 - if (item.startsWith(TAG_CHECKED)) { - //选择勾选 - cb.setChecked(true); - edit.setPaintFlags(edit.getPaintFlags() | Paint.STRIKE_THRU_TEXT_FLAG); - item = item.substring(TAG_CHECKED.length(), item.length()).trim(); - } else if (item.startsWith(TAG_UNCHECKED)) { - //选择不勾选 - cb.setChecked(false); - edit.setPaintFlags(Paint.ANTI_ALIAS_FLAG | Paint.DEV_KERN_TEXT_FLAG); - item = item.substring(TAG_UNCHECKED.length(), item.length()).trim(); - } - - edit.setOnTextViewChangeListener(this); - edit.setIndex(index); - edit.setText(getHighlightQueryResult(item, mUserQuery)); - //运行编辑框的监听器对该行为作出反应,并设置下标及文本内容 - return view; - } - /* - * 函数功能:便签内容发生改变所 触发的事件 - * 函数实现:如下注释 - */ - public void onTextChange(int index, boolean hasText) { - if (index >= mEditTextList.getChildCount()) { - Log.e(TAG, "Wrong index, should not happen"); - return; - //越界报错 - } - if(hasText) { - mEditTextList.getChildAt(index).findViewById(R.id.cb_edit_item).setVisibility(View.VISIBLE); - } else { - mEditTextList.getChildAt(index).findViewById(R.id.cb_edit_item).setVisibility(View.GONE); - } - //如果内容不为空则将其子编辑框可见性置为可见,否则不可见 - } - /* - * 函数功能:检查模式和列表模式的切换 - * 函数实现:如下注释 - */ - public void onCheckListModeChanged(int oldMode, int newMode) { - if (newMode == TextNote.MODE_CHECK_LIST) { - switchToListMode(mNoteEditor.getText().toString()); - //检查模式切换到列表模式 - } else { - if (!getWorkingText()) { - mWorkingNote.setWorkingText(mWorkingNote.getContent().replace(TAG_UNCHECKED + " ", - "")); - } - //若是获取到文本就改变其检查标记 - mNoteEditor.setText(getHighlightQueryResult(mWorkingNote.getContent(), mUserQuery)); - mEditTextList.setVisibility(View.GONE); - mNoteEditor.setVisibility(View.VISIBLE); - //修改文本编辑器的内容和可见性 - } - } - /* - * 函数功能:设置勾选选项表并返回是否勾选的标记 - * 函数实现:如下注释 - */ - private boolean getWorkingText() { - boolean hasChecked = false; - //初始化check标记 - if (mWorkingNote.getCheckListMode() == TextNote.MODE_CHECK_LIST) { - //若模式为CHECK_LIST - StringBuilder sb = new StringBuilder(); - //创建可变字符串 - for (int i = 0; i < mEditTextList.getChildCount(); i++) { - View view = mEditTextList.getChildAt(i); - //遍历所有子编辑框的视图 - NoteEditText edit = (NoteEditText) view.findViewById(R.id.et_edit_text); - if (!TextUtils.isEmpty(edit.getText())) { - //若文本不为空 - if (((CheckBox) view.findViewById(R.id.cb_edit_item)).isChecked()) { - //该选项框已打钩 - sb.append(TAG_CHECKED).append(" ").append(edit.getText()).append("\n"); - hasChecked = true; - //扩展字符串为已打钩并把标记置true - } else { - sb.append(TAG_UNCHECKED).append(" ").append(edit.getText()).append("\n"); - //扩展字符串添加未打钩 - } - } - } - mWorkingNote.setWorkingText(sb.toString()); - //利用编辑好的字符串设置运行便签的内容 - } else { - mWorkingNote.setWorkingText(mNoteEditor.getText().toString()); - // 若不是该模式直接用编辑器中的内容设置运行中标签的内容 - } - return hasChecked; - } - /* - * 函数功能:保存便签 - * 函数实现:如下注释 - */ - private boolean saveNote() { - getWorkingText(); - boolean saved = mWorkingNote.saveNote(); - //运行 getWorkingText()之后保存 - if (saved) { - /** - * There are two modes from List view to edit view, open one note, - * create/edit a node. Opening node requires to the original - * position in the list when back from edit view, while creating a - * new node requires to the top of the list. This code - * {@link #RESULT_OK} is used to identify the create/edit state - */ - //如英文注释所说链接RESULT_OK是为了识别保存的2种情况,一是创建后保存,二是修改后保存 - setResult(RESULT_OK); - } - return saved; - } - /* - * 函数功能:将便签发送至桌面 - * 函数实现:如下注释 - */ - private void sendToDesktop() { - /** - * Before send message to home, we should make sure that current - * editing note is exists in databases. So, for new note, firstly - * save it - */ - if (!mWorkingNote.existInDatabase()) { - saveNote(); - //若不存在数据也就是新的标签就保存起来先 - } - - if (mWorkingNote.getNoteId() > 0) { - //若是有内容 - Intent sender = new Intent(); - Intent shortcutIntent = new Intent(this, NoteEditActivity.class); - //建立发送到桌面的连接器 - shortcutIntent.setAction(Intent.ACTION_VIEW); - //链接为一个视图 - shortcutIntent.putExtra(Intent.EXTRA_UID, mWorkingNote.getNoteId()); - sender.putExtra(Intent.EXTRA_SHORTCUT_INTENT, shortcutIntent); - sender.putExtra(Intent.EXTRA_SHORTCUT_NAME, - makeShortcutIconTitle(mWorkingNote.getContent())); - sender.putExtra(Intent.EXTRA_SHORTCUT_ICON_RESOURCE, - Intent.ShortcutIconResource.fromContext(this, R.drawable.icon_app)); - sender.putExtra("duplicate", true); - //将便签的相关信息都添加到要发送的文件里 - sender.setAction("com.android.launcher.action.INSTALL_SHORTCUT"); - //设置sneder的行为是发送 - showToast(R.string.info_note_enter_desktop); - sendBroadcast(sender); - //显示到桌面 - } else { - /** - * There is the condition that user has input nothing (the note is - * not worthy saving), we have no note id, remind the user that he - * should input something - */ - Log.e(TAG, "Send to desktop error"); - showToast(R.string.error_note_empty_for_send_to_desktop); - //空便签直接报错 - } - } - /* - * 函数功能:编辑小图标的标题 - * 函数实现:如下注释 - */ - private String makeShortcutIconTitle(String content) { - content = content.replace(TAG_CHECKED, ""); - content = content.replace(TAG_UNCHECKED, ""); - return content.length() > SHORTCUT_ICON_TITLE_MAX_LEN ? content.substring(0, - SHORTCUT_ICON_TITLE_MAX_LEN) : content; - //直接设置为content中的内容并返回,有勾选和未勾选2种 - } - /* - * 函数功能:显示提示的视图 - * 函数实现:根据下标显示对应的提示 - */ - private void showToast(int resId) { - showToast(resId, Toast.LENGTH_SHORT); - } - /* - * 函数功能:持续显示提示的视图 - * 函数实现:根据下标和持续的时间(duration)编辑提示视图并显示 - */ - private void showToast(int resId, int duration) { - Toast.makeText(this, resId, duration).show(); - } -} diff --git a/other/06_凌子杰_代码标注/ui/NoteEditText.java b/other/06_凌子杰_代码标注/ui/NoteEditText.java deleted file mode 100644 index 6dab416..0000000 --- a/other/06_凌子杰_代码标注/ui/NoteEditText.java +++ /dev/null @@ -1,272 +0,0 @@ -/* - * 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.ui; - -import android.content.Context; -import android.graphics.Rect; -import android.text.Layout; -import android.text.Selection; -import android.text.Spanned; -import android.text.TextUtils; -import android.text.style.URLSpan; -import android.util.AttributeSet; -import android.util.Log; -import android.view.ContextMenu; -import android.view.KeyEvent; -import android.view.MenuItem; -import android.view.MenuItem.OnMenuItemClickListener; -import android.view.MotionEvent; -import android.widget.EditText; - -import net.micode.notes.R; - -import java.util.HashMap; -import java.util.Map; -//继承edittext,设置便签设置文本框 -public class NoteEditText extends EditText { - private static final String TAG = "NoteEditText"; - private int mIndex; - private int mSelectionStartBeforeDelete; - - private static final String SCHEME_TEL = "tel:" ; - private static final String SCHEME_HTTP = "http:" ; - private static final String SCHEME_EMAIL = "mailto:" ; - //建立一个字符和整数的hash表,用于链接电话,网站,还有邮箱 - private static final Map sSchemaActionResMap = new HashMap(); - static { - sSchemaActionResMap.put(SCHEME_TEL, R.string.note_link_tel); - sSchemaActionResMap.put(SCHEME_HTTP, R.string.note_link_web); - sSchemaActionResMap.put(SCHEME_EMAIL, R.string.note_link_email); - } - - /** - * Call by the {@link NoteEditActivity} to delete or add edit text - */ - //在NoteEditActivity中删除或添加文本的操作,可以看做是一个文本是否被变的标记,英文注释已说明的很清楚 - public interface OnTextViewChangeListener { - /** - * Delete current edit text when {@link KeyEvent#KEYCODE_DEL} happens - * and the text is null - */ - //处理删除按键时的操作 - void onEditTextDelete(int index, String text); - - /** - * Add edit text after current edit text when {@link KeyEvent#KEYCODE_ENTER} - * happen - */ - //处理进入按键时的操作 - void onEditTextEnter(int index, String text); - - /** - * Hide or show item option when text change - */ - void onTextChange(int index, boolean hasText); - } - - private OnTextViewChangeListener mOnTextViewChangeListener; - //根据context设置文本 - public NoteEditText(Context context) { - super(context, null);//用super引用父类变量 - mIndex = 0; - } - //设置当前光标 - public void setIndex(int index) { - mIndex = index; - } - //初始化文本修改标记 - public void setOnTextViewChangeListener(OnTextViewChangeListener listener) { - mOnTextViewChangeListener = listener; - } - //AttributeSet 百度了一下是自定义空控件属性,用于维护便签动态变化的属性 - //初始化便签 - public NoteEditText(Context context, AttributeSet attrs) { - super(context, attrs, android.R.attr.editTextStyle); - } - // 根据defstyle自动初始化 - public NoteEditText(Context context, AttributeSet attrs, int defStyle) { - super(context, attrs, defStyle); - // TODO Auto-generated constructor stub - } - //view里的函数,处理手机屏幕的所有事件 - /*参数event为手机屏幕触摸事件封装类的对象,其中封装了该事件的所有信息, - 例如触摸的位置、触摸的类型以及触摸的时间等。该对象会在用户触摸手机屏幕时被创建。*/ - @Override - public boolean onTouchEvent(MotionEvent event) { - switch (event.getAction()) { - //重写了需要处理屏幕被按下的事件 - case MotionEvent.ACTION_DOWN: - //跟新当前坐标值 - int x = (int) event.getX(); - int y = (int) event.getY(); - x -= getTotalPaddingLeft(); - y -= getTotalPaddingTop(); - x += getScrollX(); - y += getScrollY(); - //用布局控件layout根据x,y的新值设置新的位置 - Layout layout = getLayout(); - int line = layout.getLineForVertical(y); - int off = layout.getOffsetForHorizontal(line, x); - //更新光标新的位置 - Selection.setSelection(getText(), off); - break; - } - - return super.onTouchEvent(event); - } - /* - * 函数功能:处理用户按下一个键盘按键时会触发 的事件 - * 实现过程:如下注释 - */ - @Override - public boolean onKeyDown(int keyCode, KeyEvent event) { - switch (keyCode) { - //根据按键的 Unicode 编码值来处理 - case KeyEvent.KEYCODE_ENTER: - //“进入”按键 - if (mOnTextViewChangeListener != null) { - return false; - } - break; - case KeyEvent.KEYCODE_DEL: - //“删除”按键 - mSelectionStartBeforeDelete = getSelectionStart(); - break; - default: - break; - } - //继续执行父类的其他点击事件 - return super.onKeyDown(keyCode, event); - } - - @Override - /* - * 函数功能:处理用户松开一个键盘按键时会触发 的事件 - * 实现方式:如下注释 - */ - public boolean onKeyUp(int keyCode, KeyEvent event) { - switch(keyCode) { - //根据按键的 Unicode 编码值来处理,有删除和进入2种操作 - case KeyEvent.KEYCODE_DEL: - if (mOnTextViewChangeListener != null) { - //若是被修改过 - if (0 == mSelectionStartBeforeDelete && mIndex != 0) { - //若之前有被修改并且文档不为空 - mOnTextViewChangeListener.onEditTextDelete(mIndex, getText().toString()); - //利用上文OnTextViewChangeListener对KEYCODE_DEL按键情况的删除函数进行删除 - return true; - } - } else { - Log.d(TAG, "OnTextViewChangeListener was not seted"); - //其他情况报错,文档的改动监听器并没有建立 - } - break; - case KeyEvent.KEYCODE_ENTER: - //同上也是分为监听器是否建立2种情况 - if (mOnTextViewChangeListener != null) { - int selectionStart = getSelectionStart(); - //获取当前位置 - String text = getText().subSequence(selectionStart, length()).toString(); - //获取当前文本 - setText(getText().subSequence(0, selectionStart)); - //根据获取的文本设置当前文本 - mOnTextViewChangeListener.onEditTextEnter(mIndex + 1, text); - //当{@link KeyEvent#KEYCODE_ENTER}添加新文本 - } else { - Log.d(TAG, "OnTextViewChangeListener was not seted"); - //其他情况报错,文档的改动监听器并没有建立 - } - break; - default: - break; - } - //继续执行父类的其他按键弹起的事件 - return super.onKeyUp(keyCode, event); - } - - @Override - /* - * 函数功能:当焦点发生变化时,会自动调用该方法来处理焦点改变的事件 - * 实现方式:如下注释 - * 参数:focused表示触发该事件的View是否获得了焦点,当该控件获得焦点时,Focused等于true,否则等于false。 - direction表示焦点移动的方向,用数值表示 - Rect:表示在触发事件的View的坐标系中,前一个获得焦点的矩形区域,即表示焦点是从哪里来的。如果不可用则为null - */ - protected void onFocusChanged(boolean focused, int direction, Rect previouslyFocusedRect) { - if (mOnTextViewChangeListener != null) { - //若监听器已经建立 - if (!focused && TextUtils.isEmpty(getText())) { - //获取到焦点并且文本不为空 - mOnTextViewChangeListener.onTextChange(mIndex, false); - //mOnTextViewChangeListener子函数,置false隐藏事件选项 - } else { - mOnTextViewChangeListener.onTextChange(mIndex, true); - //mOnTextViewChangeListener子函数,置true显示事件选项 - } - } - //继续执行父类的其他焦点变化的事件 - super.onFocusChanged(focused, direction, previouslyFocusedRect); - } - - @Override - /* - * 函数功能:生成上下文菜单 - * 函数实现:如下注释 - */ - protected void onCreateContextMenu(ContextMenu menu) { - if (getText() instanceof Spanned) { - //有文本存在 - int selStart = getSelectionStart(); - int selEnd = getSelectionEnd(); - //获取文本开始和结尾位置 - int min = Math.min(selStart, selEnd); - int max = Math.max(selStart, selEnd); - //获取开始到结尾的最大值和最小值 - final URLSpan[] urls = ((Spanned) getText()).getSpans(min, max, URLSpan.class); - //设置url的信息的范围值 - if (urls.length == 1) { - int defaultResId = 0; - for(String schema: sSchemaActionResMap.keySet()) { - //获取计划表中所有的key值 - if(urls[0].getURL().indexOf(schema) >= 0) { - //若url可以添加则在添加后将defaultResId置为key所映射的值 - defaultResId = sSchemaActionResMap.get(schema); - break; - } - } - - if (defaultResId == 0) { - //defaultResId == 0则说明url并没有添加任何东西,所以置为连接其他SchemaActionResMap的值 - defaultResId = R.string.note_link_other; - } - //建立菜单 - menu.add(0, 0, 0, defaultResId).setOnMenuItemClickListener( - new OnMenuItemClickListener() { - //新建按键监听器 - public boolean onMenuItemClick(MenuItem item) { - // goto a new intent - urls[0].onClick(NoteEditText.this); - //根据相应的文本设置菜单的按键 - return true; - } - }); - } - } - //继续执行父类的其他菜单创建的事件 - super.onCreateContextMenu(menu); - } -} diff --git a/other/06_凌子杰_代码标注/ui/NoteItemData.java b/other/06_凌子杰_代码标注/ui/NoteItemData.java deleted file mode 100644 index 85052ab..0000000 --- a/other/06_凌子杰_代码标注/ui/NoteItemData.java +++ /dev/null @@ -1,232 +0,0 @@ -/* - * 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.ui; - -import android.content.Context; -import android.database.Cursor; -import android.text.TextUtils; - -import net.micode.notes.data.Contact; -import net.micode.notes.data.Notes; -import net.micode.notes.data.Notes.NoteColumns; -import net.micode.notes.tool.DataUtils; - - -public class NoteItemData { - static final String [] PROJECTION = new String [] { - NoteColumns.ID, - NoteColumns.ALERTED_DATE, - NoteColumns.BG_COLOR_ID, - NoteColumns.CREATED_DATE, - NoteColumns.HAS_ATTACHMENT, - NoteColumns.MODIFIED_DATE, - NoteColumns.NOTES_COUNT, - NoteColumns.PARENT_ID, - NoteColumns.SNIPPET, - NoteColumns.TYPE, - NoteColumns.WIDGET_ID, - NoteColumns.WIDGET_TYPE, - }; - - private static final int ID_COLUMN = 0; - private static final int ALERTED_DATE_COLUMN = 1; - private static final int BG_COLOR_ID_COLUMN = 2; - private static final int CREATED_DATE_COLUMN = 3; - private static final int HAS_ATTACHMENT_COLUMN = 4; - private static final int MODIFIED_DATE_COLUMN = 5; - private static final int NOTES_COUNT_COLUMN = 6; - private static final int PARENT_ID_COLUMN = 7; - private static final int SNIPPET_COLUMN = 8; - private static final int TYPE_COLUMN = 9; - private static final int WIDGET_ID_COLUMN = 10; - private static final int WIDGET_TYPE_COLUMN = 11; - - private long mId; - private long mAlertDate; - private int mBgColorId; - private long mCreatedDate; - private boolean mHasAttachment; - private long mModifiedDate; - private int mNotesCount; - private long mParentId; - private String mSnippet; - private int mType; - private int mWidgetId; - private int mWidgetType; - private String mName; - private String mPhoneNumber; - - private boolean mIsLastItem; - private boolean mIsFirstItem; - private boolean mIsOnlyOneItem; - private boolean mIsOneNoteFollowingFolder; - private boolean mIsMultiNotesFollowingFolder; - //初始化NoteItemData,主要利用光标cursor获取的东西 - public NoteItemData(Context context, Cursor cursor) { - //getxxx为转换格式 - mId = cursor.getLong(ID_COLUMN); - mAlertDate = cursor.getLong(ALERTED_DATE_COLUMN); - mBgColorId = cursor.getInt(BG_COLOR_ID_COLUMN); - mCreatedDate = cursor.getLong(CREATED_DATE_COLUMN); - mHasAttachment = (cursor.getInt(HAS_ATTACHMENT_COLUMN) > 0) ? true : false; - mModifiedDate = cursor.getLong(MODIFIED_DATE_COLUMN); - mNotesCount = cursor.getInt(NOTES_COUNT_COLUMN); - mParentId = cursor.getLong(PARENT_ID_COLUMN); - mSnippet = cursor.getString(SNIPPET_COLUMN); - mSnippet = mSnippet.replace(NoteEditActivity.TAG_CHECKED, "").replace( - NoteEditActivity.TAG_UNCHECKED, ""); - mType = cursor.getInt(TYPE_COLUMN); - mWidgetId = cursor.getInt(WIDGET_ID_COLUMN); - mWidgetType = cursor.getInt(WIDGET_TYPE_COLUMN); - //初始化电话号码的信息 - mPhoneNumber = ""; - if (mParentId == Notes.ID_CALL_RECORD_FOLDER) { - mPhoneNumber = DataUtils.getCallNumberByNoteId(context.getContentResolver(), mId); - if (!TextUtils.isEmpty(mPhoneNumber)) { - //mphonenumber里有符合字符串,则用contart功能连接 - mName = Contact.getContact(context, mPhoneNumber); - if (mName == null) { - mName = mPhoneNumber; - } - } - } - - if (mName == null) { - mName = ""; - } - checkPostion(cursor); - } - //根据鼠标的位置设置标记,和位置 - private void checkPostion(Cursor cursor) { - mIsLastItem = cursor.isLast() ? true : false; - mIsFirstItem = cursor.isFirst() ? true : false; - mIsOnlyOneItem = (cursor.getCount() == 1); - //初始化“多重子文件”“单一子文件”2个标记 - mIsMultiNotesFollowingFolder = false; - mIsOneNoteFollowingFolder = false; - //主要是设置上诉2标记 - if (mType == Notes.TYPE_NOTE && !mIsFirstItem) { - //若是note格式并且不是第一个元素 - int position = cursor.getPosition(); - if (cursor.moveToPrevious()) { - //获取光标位置后看上一行 - if (cursor.getInt(TYPE_COLUMN) == Notes.TYPE_FOLDER - || cursor.getInt(TYPE_COLUMN) == Notes.TYPE_SYSTEM) { - //若光标满足系统或note格式 - if (cursor.getCount() > (position + 1)) { - mIsMultiNotesFollowingFolder = true; - //若是数据行数大于但前位置+1则设置成正确 - } else { - mIsOneNoteFollowingFolder = true; - //否则单一文件夹标记为true - } - } - if (!cursor.moveToNext()) {//若不能再往下走则报错 - throw new IllegalStateException("cursor move to previous but can't move back"); - } - } - } - } - - public boolean isOneFollowingFolder() { - return mIsOneNoteFollowingFolder; - } - - public boolean isMultiFollowingFolder() { - return mIsMultiNotesFollowingFolder; - } - - public boolean isLast() { - return mIsLastItem; - } - - public String getCallName() { - return mName; - } - - public boolean isFirst() { - return mIsFirstItem; - } - - public boolean isSingle() { - return mIsOnlyOneItem; - } - - public long getId() { - return mId; - } - - public long getAlertDate() { - return mAlertDate; - } - - public long getCreatedDate() { - return mCreatedDate; - } - - public boolean hasAttachment() { - return mHasAttachment; - } - - public long getModifiedDate() { - return mModifiedDate; - } - - public int getBgColorId() { - return mBgColorId; - } - - public long getParentId() { - return mParentId; - } - - public int getNotesCount() { - return mNotesCount; - } - - public long getFolderId () { - return mParentId; - } - - public int getType() { - return mType; - } - - public int getWidgetType() { - return mWidgetType; - } - - public int getWidgetId() { - return mWidgetId; - } - - public String getSnippet() { - return mSnippet; - } - - public boolean hasAlert() { - return (mAlertDate > 0); - } - //若数据父id为保存至文件夹模式的id且满足电话号码单元不为空,则isCallRecord为true - public boolean isCallRecord() { - return (mParentId == Notes.ID_CALL_RECORD_FOLDER && !TextUtils.isEmpty(mPhoneNumber)); - } - - public static int getNoteType(Cursor cursor) { - return cursor.getInt(TYPE_COLUMN); - } -} diff --git a/other/06_刘峻豪_代码标注/ui/NotesListActivity.java b/other/06_刘峻豪_代码标注/ui/NotesListActivity.java deleted file mode 100644 index e4c64a5..0000000 --- a/other/06_刘峻豪_代码标注/ui/NotesListActivity.java +++ /dev/null @@ -1,1020 +0,0 @@ -/* - * 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.ui; - -import android.app.Activity; -import android.app.AlertDialog; -import android.app.Dialog; -import android.appwidget.AppWidgetManager; -import android.content.AsyncQueryHandler; -import android.content.ContentResolver; -import android.content.ContentValues; -import android.content.Context; -import android.content.DialogInterface; -import android.content.Intent; -import android.content.SharedPreferences; -import android.database.Cursor; -import android.os.AsyncTask; -import android.os.Bundle; -import android.preference.PreferenceManager; -import android.text.Editable; -import android.text.TextUtils; -import android.text.TextWatcher; -import android.util.Log; -import android.view.ActionMode; -import android.view.ContextMenu; -import android.view.ContextMenu.ContextMenuInfo; -import android.view.Display; -import android.view.HapticFeedbackConstants; -import android.view.LayoutInflater; -import android.view.Menu; -import android.view.MenuItem; -import android.view.MenuItem.OnMenuItemClickListener; -import android.view.MotionEvent; -import android.view.View; -import android.view.View.OnClickListener; -import android.view.View.OnCreateContextMenuListener; -import android.view.View.OnTouchListener; -import android.view.inputmethod.InputMethodManager; -import android.widget.AdapterView; -import android.widget.AdapterView.OnItemClickListener; -import android.widget.AdapterView.OnItemLongClickListener; -import android.widget.Button; -import android.widget.EditText; -import android.widget.ListView; -import android.widget.PopupMenu; -import android.widget.TextView; -import android.widget.Toast; - -import net.micode.notes.R; -import net.micode.notes.data.Notes; -import net.micode.notes.data.Notes.NoteColumns; -import net.micode.notes.gtask.remote.GTaskSyncService; -import net.micode.notes.model.WorkingNote; -import net.micode.notes.tool.BackupUtils; -import net.micode.notes.tool.DataUtils; -import net.micode.notes.tool.ResourceParser; -import net.micode.notes.ui.NotesListAdapter.AppWidgetAttribute; -import net.micode.notes.widget.NoteWidgetProvider_2x; -import net.micode.notes.widget.NoteWidgetProvider_4x; - -import java.io.BufferedReader; -import java.io.IOException; -import java.io.InputStream; -import java.io.InputStreamReader; -import java.util.HashSet; -//主界面 -/** - * @author k - * - */ -public class NotesListActivity extends Activity implements OnClickListener, OnItemLongClickListener { - private static final int FOLDER_NOTE_LIST_QUERY_TOKEN = 0; - - private static final int FOLDER_LIST_QUERY_TOKEN = 1; - - private static final int MENU_FOLDER_DELETE = 0; - - private static final int MENU_FOLDER_VIEW = 1; - - private static final int MENU_FOLDER_CHANGE_NAME = 2; - - private static final String PREFERENCE_ADD_INTRODUCTION = "net.micode.notes.introduction"; //单行超过80个字符 - - private enum ListEditState { - NOTE_LIST, SUB_FOLDER, CALL_RECORD_FOLDER - }; - - private ListEditState mState; - - private BackgroundQueryHandler mBackgroundQueryHandler; - - private NotesListAdapter mNotesListAdapter; - - private ListView mNotesListView; - - private Button mAddNewNote; - - private boolean mDispatch; - - private int mOriginY; - - private int mDispatchY; - - private TextView mTitleBar; - - private long mCurrentFolderId; - - private ContentResolver mContentResolver; - - private ModeCallback mModeCallBack; - - private static final String TAG = "NotesListActivity"; - - public static final int NOTES_LISTVIEW_SCROLL_RATE = 30; - - private NoteItemData mFocusNoteDataItem; - - private static final String NORMAL_SELECTION = NoteColumns.PARENT_ID + "=?"; - - private static final String ROOT_FOLDER_SELECTION = "(" + NoteColumns.TYPE + "<>" - + Notes.TYPE_SYSTEM + " AND " + NoteColumns.PARENT_ID + "=?)" + " OR (" - + NoteColumns.ID + "=" + Notes.ID_CALL_RECORD_FOLDER + " AND " - + NoteColumns.NOTES_COUNT + ">0)"; - - private final static int REQUEST_CODE_OPEN_NODE = 102; - private final static int REQUEST_CODE_NEW_NODE = 103; - - @Override - // 创建类 - protected void onCreate(final Bundle savedInstanceState) { - //需要是final类型 根据程序上下文环境,Java关键字final有“这是无法改变的”或者“终态的”含义, - // 它可以修饰非抽象类、非抽象类成员方法和变量。 - // final类不能被继承,没有子类,final类中的方法默认是final的。 - //final方法不能被子类的方法覆盖,但可以被继承。 - //final成员变量表示常量,只能被赋值一次,赋值后值不再改变。 - //final不能用于修饰构造方法。 - super.onCreate(savedInstanceState); // 调用父类的onCreate函数 - setContentView(R.layout.note_list); - initResources(); - - /** - * Insert an introduction when user firstly use this application - */ - setAppInfoFromRawRes(); - } - - @Override - // 返回一些子模块完成的数据交给主Activity处理 - protected void onActivityResult(int requestCode, int resultCode, Intent data) { - // 结果值 和 要求值 符合要求 - if (resultCode == RESULT_OK - && (requestCode == REQUEST_CODE_OPEN_NODE || requestCode == REQUEST_CODE_NEW_NODE)) { - mNotesListAdapter.changeCursor(null); - } else { - super.onActivityResult(requestCode, resultCode, data); - // 调用 Activity 的onActivityResult() - } - } - - private void setAppInfoFromRawRes() { - // Android平台给提供了一个SharedPreferences类,它是一个轻量级的存储类,特别适合用于保存软件配置参数。 - SharedPreferences sp = PreferenceManager.getDefaultSharedPreferences(this); - if (!sp.getBoolean(PREFERENCE_ADD_INTRODUCTION, false)) { - StringBuilder sb = new StringBuilder(); - InputStream in = null; - try { - // 把资源文件放到应用程序的/raw/raw下,那么就可以在应用中使用getResources获取资源后, - // 以openRawResource方法(不带后缀的资源文件名)打开这个文件。 - in = getResources().openRawResource(R.raw.introduction); - if (in != null) { - InputStreamReader isr = new InputStreamReader(in); - BufferedReader br = new BufferedReader(isr); - char [] buf = new char[1024]; // 自行定义的数值 - int len = 0; - while ((len = br.read(buf)) > 0) { - sb.append(buf, 0, len); - } - } else { - Log.e(TAG, "Read introduction file error"); - return; - } - } catch (IOException e) { - e.printStackTrace(); - return; - } finally { - if (in != null) { - try { - in.close(); - } catch (IOException e) { - // TODO Auto-generated catch block - e.printStackTrace(); - } - } - } - - // 创建空的WorkingNote - WorkingNote note = WorkingNote.createEmptyNote(this, Notes.ID_ROOT_FOLDER, - AppWidgetManager.INVALID_APPWIDGET_ID, Notes.TYPE_WIDGET_INVALIDE, - ResourceParser.RED); - note.setWorkingText(sb.toString()); - if (note.saveNote()) { - // 更新保存note的信息 - sp.edit().putBoolean(PREFERENCE_ADD_INTRODUCTION, true).commit(); - } else { - Log.e(TAG, "Save introduction note error"); - return; - } - } - } - - @Override - protected void onStart() { - super.onStart(); - startAsyncNotesListQuery(); - } - - // 初始化资源 - private void initResources() { - mContentResolver = this.getContentResolver(); // 获取应用程序的数据,得到类似数据表的东西 - mBackgroundQueryHandler = new BackgroundQueryHandler(this.getContentResolver()); - mCurrentFolderId = Notes.ID_ROOT_FOLDER; - - // findViewById 是安卓编程的定位函数,主要是引用.R文件里的引用名 - mNotesListView = (ListView) findViewById(R.id.notes_list); // 绑定XML中的ListView,作为Item的容器 - mNotesListView.addFooterView(LayoutInflater.from(this).inflate(R.layout.note_list_footer, null), - null, false); - mNotesListView.setOnItemClickListener(new OnListItemClickListener()); - mNotesListView.setOnItemLongClickListener(this); - mNotesListAdapter = new NotesListAdapter(this); - mNotesListView.setAdapter(mNotesListAdapter); - mAddNewNote = (Button) findViewById(R.id.btn_new_note);// 在activity中要获取该按钮 - mAddNewNote.setOnClickListener(this); - mAddNewNote.setOnTouchListener(new NewNoteOnTouchListener()); - mDispatch = false; - mDispatchY = 0; - mOriginY = 0; - mTitleBar = (TextView) findViewById(R.id.tv_title_bar); - mState = ListEditState.NOTE_LIST; - mModeCallBack = new ModeCallback(); - } - - // 继承自ListView.MultiChoiceModeListener 和 OnMenuItemClickListener - private class ModeCallback implements ListView.MultiChoiceModeListener, OnMenuItemClickListener { - private DropdownMenu mDropDownMenu; - private ActionMode mActionMode; - private MenuItem mMoveMenu; - - public boolean onCreateActionMode(ActionMode mode, Menu menu) { - getMenuInflater().inflate(R.menu.note_list_options, menu); - menu.findItem(R.id.delete).setOnMenuItemClickListener(this); - mMoveMenu = menu.findItem(R.id.move); - if (mFocusNoteDataItem.getParentId() == Notes.ID_CALL_RECORD_FOLDER - || DataUtils.getUserFolderCount(mContentResolver) == 0) { - mMoveMenu.setVisible(false); - } else { - mMoveMenu.setVisible(true); - mMoveMenu.setOnMenuItemClickListener(this); - } - mActionMode = mode; - mNotesListAdapter.setChoiceMode(true); - mNotesListView.setLongClickable(false); - mAddNewNote.setVisibility(View.GONE); - - View customView = LayoutInflater.from(NotesListActivity.this).inflate( - R.layout.note_list_dropdown_menu, null); - mode.setCustomView(customView); - mDropDownMenu = new DropdownMenu(NotesListActivity.this, - (Button) customView.findViewById(R.id.selection_menu), - R.menu.note_list_dropdown); - mDropDownMenu.setOnDropdownMenuItemClickListener(new PopupMenu.OnMenuItemClickListener(){ - public boolean onMenuItemClick(final MenuItem item) { - mNotesListAdapter.selectAll(!mNotesListAdapter.isAllSelected()); - updateMenu(); - return true; - } - - }); - return true; - } - - // 更新菜单 - private void updateMenu() { - int selectedCount = mNotesListAdapter.getSelectedCount(); - // Update dropdown menu - String format = getResources().getString(R.string.menu_select_title, selectedCount); - mDropDownMenu.setTitle(format); // 更改标题 - MenuItem item = mDropDownMenu.findItem(R.id.action_select_all); - if (item != null) { - if (mNotesListAdapter.isAllSelected()) { - item.setChecked(true); - item.setTitle(R.string.menu_deselect_all); - } else { - item.setChecked(false); - item.setTitle(R.string.menu_select_all); - } - } - } - - public boolean onPrepareActionMode(ActionMode mode, Menu menu) { - // TODO Auto-generated method stub - return false; - } - - public boolean onActionItemClicked(ActionMode mode, MenuItem item) { - // TODO Auto-generated method stub - return false; - } - - public void onDestroyActionMode(ActionMode mode) { - mNotesListAdapter.setChoiceMode(false); - mNotesListView.setLongClickable(true); - mAddNewNote.setVisibility(View.VISIBLE); - } - - public void finishActionMode() { - mActionMode.finish(); - } - - public void onItemCheckedStateChanged(ActionMode mode, int position, long id, - boolean checked) { - mNotesListAdapter.setCheckedItem(position, checked); - updateMenu(); - } - - public boolean onMenuItemClick(MenuItem item) { - if (mNotesListAdapter.getSelectedCount() == 0) { - Toast.makeText(NotesListActivity.this, getString(R.string.menu_select_none), - Toast.LENGTH_SHORT).show(); - return true; - } - - switch (item.getItemId()) { - case R.id.delete: - AlertDialog.Builder builder = new AlertDialog.Builder(NotesListActivity.this); - builder.setTitle(getString(R.string.alert_title_delete)); - builder.setIcon(android.R.drawable.ic_dialog_alert); - builder.setMessage(getString(R.string.alert_message_delete_notes, - mNotesListAdapter.getSelectedCount())); - builder.setPositiveButton(android.R.string.ok, - new DialogInterface.OnClickListener() { - public void onClick(DialogInterface dialog, - int which) { - batchDelete(); - } - }); - builder.setNegativeButton(android.R.string.cancel, null); - builder.show(); - break; - case R.id.move: - startQueryDestinationFolders(); - break; - default: - return false; - } - return true; - } - } - - private class NewNoteOnTouchListener implements OnTouchListener { - - public boolean onTouch(View v, MotionEvent event) { - switch (event.getAction()) { - case MotionEvent.ACTION_DOWN: { - Display display = getWindowManager().getDefaultDisplay(); - int screenHeight = display.getHeight(); - int newNoteViewHeight = mAddNewNote.getHeight(); - int start = screenHeight - newNoteViewHeight; - int eventY = start + (int) event.getY(); - /** - * Minus TitleBar's height - */ - if (mState == ListEditState.SUB_FOLDER) { - eventY -= mTitleBar.getHeight(); - start -= mTitleBar.getHeight(); - } - /** - * HACKME:When click the transparent part of "New Note" button, dispatch - * the event to the list view behind this button. The transparent part of - * "New Note" button could be expressed by formula y=-0.12x+94锛圲nit:pixel锛� - * and the line top of the button. The coordinate based on left of the "New - * Note" button. The 94 represents maximum height of the transparent part. - * Notice that, if the background of the button changes, the formula should - * also change. This is very bad, just for the UI designer's strong requirement. - */ - if (event.getY() < (event.getX() * (-0.12) + 94)) { - View view = mNotesListView.getChildAt(mNotesListView.getChildCount() - 1 - - mNotesListView.getFooterViewsCount()); - if (view != null && view.getBottom() > start - && (view.getTop() < (start + 94))) { - mOriginY = (int) event.getY(); - mDispatchY = eventY; - event.setLocation(event.getX(), mDispatchY); - mDispatch = true; - return mNotesListView.dispatchTouchEvent(event); - } - } - break; - } - case MotionEvent.ACTION_MOVE: { - if (mDispatch) { - mDispatchY += (int) event.getY() - mOriginY; - event.setLocation(event.getX(), mDispatchY); - return mNotesListView.dispatchTouchEvent(event); - } - break; - } - default: { - if (mDispatch) { - event.setLocation(event.getX(), mDispatchY); - mDispatch = false; - return mNotesListView.dispatchTouchEvent(event); - } - break; - } - } - return false; - } - - }; - - private void startAsyncNotesListQuery() { - String selection = (mCurrentFolderId == Notes.ID_ROOT_FOLDER) ? ROOT_FOLDER_SELECTION - : NORMAL_SELECTION; - mBackgroundQueryHandler.startQuery(FOLDER_NOTE_LIST_QUERY_TOKEN, null, - Notes.CONTENT_NOTE_URI, NoteItemData.PROJECTION, selection, new String[] { - String.valueOf(mCurrentFolderId) - }, NoteColumns.TYPE + " DESC," + NoteColumns.MODIFIED_DATE + " DESC"); - } - - private final class BackgroundQueryHandler extends AsyncQueryHandler { - public BackgroundQueryHandler(ContentResolver contentResolver) { - super(contentResolver); - } - - @Override - protected void onQueryComplete(int token, Object cookie, Cursor cursor) { - switch (token) { - case FOLDER_NOTE_LIST_QUERY_TOKEN: - mNotesListAdapter.changeCursor(cursor); - break; - case FOLDER_LIST_QUERY_TOKEN: - if (cursor != null && cursor.getCount() > 0) { - showFolderListMenu(cursor); - } else { - Log.e(TAG, "Query folder failed"); - } - break; - default: - return; - } - } - } - - private void showFolderListMenu(Cursor cursor) { - AlertDialog.Builder builder = new AlertDialog.Builder(NotesListActivity.this); - builder.setTitle(R.string.menu_title_select_folder); - final FoldersListAdapter adapter = new FoldersListAdapter(this, cursor); - builder.setAdapter(adapter, new DialogInterface.OnClickListener() { - - public void onClick(DialogInterface dialog, int which) { - DataUtils.batchMoveToFolder(mContentResolver, - mNotesListAdapter.getSelectedItemIds(), adapter.getItemId(which)); - Toast.makeText( - NotesListActivity.this, - getString(R.string.format_move_notes_to_folder, - mNotesListAdapter.getSelectedCount(), - adapter.getFolderName(NotesListActivity.this, which)), - Toast.LENGTH_SHORT).show(); - mModeCallBack.finishActionMode(); - } - }); - builder.show(); - } - - private void createNewNote() { - Intent intent = new Intent(this, NoteEditActivity.class); - intent.setAction(Intent.ACTION_INSERT_OR_EDIT); - intent.putExtra(Notes.INTENT_EXTRA_FOLDER_ID, mCurrentFolderId); - this.startActivityForResult(intent, REQUEST_CODE_NEW_NODE); - } - - private void batchDelete() { - new AsyncTask>() { - protected HashSet doInBackground(Void... unused) { - HashSet widgets = mNotesListAdapter.getSelectedWidget(); - if (!isSyncMode()) { - // if not synced, delete notes directly - if (DataUtils.batchDeleteNotes(mContentResolver, mNotesListAdapter - .getSelectedItemIds())) { - } else { - Log.e(TAG, "Delete notes error, should not happens"); - } - } else { - // in sync mode, we'll move the deleted note into the trash - // folder - if (!DataUtils.batchMoveToFolder(mContentResolver, mNotesListAdapter - .getSelectedItemIds(), Notes.ID_TRASH_FOLER)) { - Log.e(TAG, "Move notes to trash folder error, should not happens"); - } - } - return widgets; - } - - @Override - protected void onPostExecute(HashSet widgets) { - if (widgets != null) { - for (AppWidgetAttribute widget : widgets) { - if (widget.widgetId != AppWidgetManager.INVALID_APPWIDGET_ID - && widget.widgetType != Notes.TYPE_WIDGET_INVALIDE) { - updateWidget(widget.widgetId, widget.widgetType); - } - } - } - mModeCallBack.finishActionMode(); - } - }.execute(); - } - - private void deleteFolder(long folderId) { - if (folderId == Notes.ID_ROOT_FOLDER) { - Log.e(TAG, "Wrong folder id, should not happen " + folderId); - return; - } - - HashSet ids = new HashSet(); - ids.add(folderId); - HashSet widgets = DataUtils.getFolderNoteWidget(mContentResolver, - folderId); - if (!isSyncMode()) { - // if not synced, delete folder directly - DataUtils.batchDeleteNotes(mContentResolver, ids); - } else { - // in sync mode, we'll move the deleted folder into the trash folder - DataUtils.batchMoveToFolder(mContentResolver, ids, Notes.ID_TRASH_FOLER); - } - if (widgets != null) { - for (AppWidgetAttribute widget : widgets) { - if (widget.widgetId != AppWidgetManager.INVALID_APPWIDGET_ID - && widget.widgetType != Notes.TYPE_WIDGET_INVALIDE) { - updateWidget(widget.widgetId, widget.widgetType); - } - } - } - } - - private void openNode(NoteItemData data) { - Intent intent = new Intent(this, NoteEditActivity.class); - intent.setAction(Intent.ACTION_VIEW); - intent.putExtra(Intent.EXTRA_UID, data.getId()); - this.startActivityForResult(intent, REQUEST_CODE_OPEN_NODE); - } - - private void openFolder(NoteItemData data) { - mCurrentFolderId = data.getId(); - startAsyncNotesListQuery(); - if (data.getId() == Notes.ID_CALL_RECORD_FOLDER) { - mState = ListEditState.CALL_RECORD_FOLDER; - mAddNewNote.setVisibility(View.GONE); - } else { - mState = ListEditState.SUB_FOLDER; - } - if (data.getId() == Notes.ID_CALL_RECORD_FOLDER) { - mTitleBar.setText(R.string.call_record_folder_name); - } else { - mTitleBar.setText(data.getSnippet()); - } - mTitleBar.setVisibility(View.VISIBLE); - } - - public void onClick(View v) { - switch (v.getId()) { - case R.id.btn_new_note: - createNewNote(); - break; - default: - break; - } - } - - private void showSoftInput() { - InputMethodManager inputMethodManager = (InputMethodManager) getSystemService(Context.INPUT_METHOD_SERVICE); - if (inputMethodManager != null) { - inputMethodManager.toggleSoftInput(InputMethodManager.SHOW_FORCED, 0); - } - } - - private void hideSoftInput(View view) { - InputMethodManager inputMethodManager = (InputMethodManager) getSystemService(Context.INPUT_METHOD_SERVICE); - inputMethodManager.hideSoftInputFromWindow(view.getWindowToken(), 0); - } - - private void showCreateOrModifyFolderDialog(final boolean create) { - final AlertDialog.Builder builder = new AlertDialog.Builder(this); - View view = LayoutInflater.from(this).inflate(R.layout.dialog_edit_text, null); - final EditText etName = (EditText) view.findViewById(R.id.et_foler_name); - showSoftInput(); - if (!create) { - if (mFocusNoteDataItem != null) { - etName.setText(mFocusNoteDataItem.getSnippet()); - builder.setTitle(getString(R.string.menu_folder_change_name)); - } else { - Log.e(TAG, "The long click data item is null"); - return; - } - } else { - etName.setText(""); - builder.setTitle(this.getString(R.string.menu_create_folder)); - } - - builder.setPositiveButton(android.R.string.ok, null); - builder.setNegativeButton(android.R.string.cancel, new DialogInterface.OnClickListener() { - public void onClick(DialogInterface dialog, int which) { - hideSoftInput(etName); - } - }); - - final Dialog dialog = builder.setView(view).show(); - final Button positive = (Button)dialog.findViewById(android.R.id.button1); - positive.setOnClickListener(new OnClickListener() { - public void onClick(View v) { - hideSoftInput(etName); - String name = etName.getText().toString(); - if (DataUtils.checkVisibleFolderName(mContentResolver, name)) { - Toast.makeText(NotesListActivity.this, getString(R.string.folder_exist, name), - Toast.LENGTH_LONG).show(); - etName.setSelection(0, etName.length()); - return; - } - if (!create) { - if (!TextUtils.isEmpty(name)) { - ContentValues values = new ContentValues(); - values.put(NoteColumns.SNIPPET, name); - values.put(NoteColumns.TYPE, Notes.TYPE_FOLDER); - values.put(NoteColumns.LOCAL_MODIFIED, 1); - mContentResolver.update(Notes.CONTENT_NOTE_URI, values, NoteColumns.ID - + "=?", new String[] { - String.valueOf(mFocusNoteDataItem.getId()) - }); - } - } else if (!TextUtils.isEmpty(name)) { - ContentValues values = new ContentValues(); - values.put(NoteColumns.SNIPPET, name); - values.put(NoteColumns.TYPE, Notes.TYPE_FOLDER); - mContentResolver.insert(Notes.CONTENT_NOTE_URI, values); - } - dialog.dismiss(); - } - }); - - if (TextUtils.isEmpty(etName.getText())) { - positive.setEnabled(false); - } - /** - * When the name edit text is null, disable the positive button - */ - etName.addTextChangedListener(new TextWatcher() { - public void beforeTextChanged(CharSequence s, int start, int count, int after) { - // TODO Auto-generated method stub - - } - - public void onTextChanged(CharSequence s, int start, int before, int count) { - if (TextUtils.isEmpty(etName.getText())) { - positive.setEnabled(false); - } else { - positive.setEnabled(true); - } - } - - public void afterTextChanged(Editable s) { - // TODO Auto-generated method stub - - } - }); - } - - /* (non-Javadoc) - * @see android.app.Activity#onBackPressed() - * 按返回键时根据情况更改类中的数据 - */ - @Override - public void onBackPressed() { switch (mState) { - case SUB_FOLDER: - mCurrentFolderId = Notes.ID_ROOT_FOLDER; - mState = ListEditState.NOTE_LIST; - startAsyncNotesListQuery(); - mTitleBar.setVisibility(View.GONE); - break; - case CALL_RECORD_FOLDER: - mCurrentFolderId = Notes.ID_ROOT_FOLDER; - mState = ListEditState.NOTE_LIST; - mAddNewNote.setVisibility(View.VISIBLE); - mTitleBar.setVisibility(View.GONE); - startAsyncNotesListQuery(); - break; - case NOTE_LIST: - super.onBackPressed(); - break; - default: - break; - } - } - - /** - * @param appWidgetId - * @param appWidgetType - * 根据不同类型的widget更新插件,通过intent传送数据 - */ - private void updateWidget(int appWidgetId, int appWidgetType) { - Intent intent = new Intent(AppWidgetManager.ACTION_APPWIDGET_UPDATE); - if (appWidgetType == Notes.TYPE_WIDGET_2X) { - intent.setClass(this, NoteWidgetProvider_2x.class); - } else if (appWidgetType == Notes.TYPE_WIDGET_4X) { - intent.setClass(this, NoteWidgetProvider_4x.class); - } else { - Log.e(TAG, "Unspported widget type"); - return; - } - - intent.putExtra(AppWidgetManager.EXTRA_APPWIDGET_IDS, new int[] { - appWidgetId - }); - - sendBroadcast(intent); - setResult(RESULT_OK, intent); - } - - /** - * 声明监听器,建立菜单,包括名称,视图,删除操作,更改名称操作; - */ - private final OnCreateContextMenuListener mFolderOnCreateContextMenuListener = new OnCreateContextMenuListener() { - public void onCreateContextMenu(ContextMenu menu, View v, ContextMenuInfo menuInfo) { - if (mFocusNoteDataItem != null) { - menu.setHeaderTitle(mFocusNoteDataItem.getSnippet()); - menu.add(0, MENU_FOLDER_VIEW, 0, R.string.menu_folder_view); - menu.add(0, MENU_FOLDER_DELETE, 0, R.string.menu_folder_delete); - menu.add(0, MENU_FOLDER_CHANGE_NAME, 0, R.string.menu_folder_change_name); - } - } - }; - - @Override - public void onContextMenuClosed(Menu menu) { - if (mNotesListView != null) { - mNotesListView.setOnCreateContextMenuListener(null); - } - super.onContextMenuClosed(menu); - } - - /* (non-Javadoc) - * @see android.app.Activity#onContextItemSelected(android.view.MenuItem) - * 针对menu中不同的选择进行不同的处理,里面详细注释 - */ - @Override - public boolean onContextItemSelected(MenuItem item) { - if (mFocusNoteDataItem == null) { - Log.e(TAG, "The long click data item is null"); - return false; - } - switch (item.getItemId()) { - case MENU_FOLDER_VIEW: - openFolder(mFocusNoteDataItem);//打开对应文件 - break; - case MENU_FOLDER_DELETE: - AlertDialog.Builder builder = new AlertDialog.Builder(this);//设置确认是否删除的对话框 - builder.setTitle(getString(R.string.alert_title_delete)); - builder.setIcon(android.R.drawable.ic_dialog_alert); - builder.setMessage(getString(R.string.alert_message_delete_folder)); - builder.setPositiveButton(android.R.string.ok, - new DialogInterface.OnClickListener() { - public void onClick(DialogInterface dialog, int which) { - deleteFolder(mFocusNoteDataItem.getId()); - } - }); - builder.setNegativeButton(android.R.string.cancel, null); - builder.show();//显示对话框 - break; - case MENU_FOLDER_CHANGE_NAME: - showCreateOrModifyFolderDialog(false); - break; - default: - break; - } - - return true; - } - - @Override - public boolean onPrepareOptionsMenu(Menu menu) { - menu.clear(); - if (mState == ListEditState.NOTE_LIST) { - getMenuInflater().inflate(R.menu.note_list, menu); - // set sync or sync_cancel - menu.findItem(R.id.menu_sync).setTitle( - GTaskSyncService.isSyncing() ? R.string.menu_sync_cancel : R.string.menu_sync); - } else if (mState == ListEditState.SUB_FOLDER) { - getMenuInflater().inflate(R.menu.sub_folder, menu); - } else if (mState == ListEditState.CALL_RECORD_FOLDER) { - getMenuInflater().inflate(R.menu.call_record_folder, menu); - } else { - Log.e(TAG, "Wrong state:" + mState); - } - return true; - } - - @Override - public boolean onOptionsItemSelected(MenuItem item) { - switch (item.getItemId()) { - case R.id.menu_new_folder: { - showCreateOrModifyFolderDialog(true); - break; - } - case R.id.menu_export_text: { - exportNoteToText(); - break; - } - case R.id.menu_sync: { - if (isSyncMode()) { - if (TextUtils.equals(item.getTitle(), getString(R.string.menu_sync))) { - GTaskSyncService.startSync(this); - } else { - GTaskSyncService.cancelSync(this); - } - } else { - startPreferenceActivity(); - } - break; - } - case R.id.menu_setting: { - startPreferenceActivity(); - break; - } - case R.id.menu_new_note: { - createNewNote(); - break; - } - case R.id.menu_search: - onSearchRequested(); - break; - default: - break; - } - return true; - } - - /* (non-Javadoc) - * @see android.app.Activity#onSearchRequested() - * 直接调用startSearch函数 - */ - @Override - public boolean onSearchRequested() { - startSearch(null, false, null /* appData */, false); - return true; - } - - /** - * 函数功能:实现将便签导出到文本功能 - */ - private void exportNoteToText() { - final BackupUtils backup = BackupUtils.getInstance(NotesListActivity.this); - new AsyncTask() { - - @Override - protected Integer doInBackground(Void... unused) { - return backup.exportToText(); - } - - @Override - protected void onPostExecute(Integer result) { - if (result == BackupUtils.STATE_SD_CARD_UNMOUONTED) { - AlertDialog.Builder builder = new AlertDialog.Builder(NotesListActivity.this); - builder.setTitle(NotesListActivity.this - .getString(R.string.failed_sdcard_export)); - builder.setMessage(NotesListActivity.this - .getString(R.string.error_sdcard_unmounted)); - builder.setPositiveButton(android.R.string.ok, null); - builder.show(); - } else if (result == BackupUtils.STATE_SUCCESS) { - AlertDialog.Builder builder = new AlertDialog.Builder(NotesListActivity.this); - builder.setTitle(NotesListActivity.this - .getString(R.string.success_sdcard_export)); - builder.setMessage(NotesListActivity.this.getString( - R.string.format_exported_file_location, backup - .getExportedTextFileName(), backup.getExportedTextFileDir())); - builder.setPositiveButton(android.R.string.ok, null); - builder.show(); - } else if (result == BackupUtils.STATE_SYSTEM_ERROR) { - AlertDialog.Builder builder = new AlertDialog.Builder(NotesListActivity.this); - builder.setTitle(NotesListActivity.this - .getString(R.string.failed_sdcard_export)); - builder.setMessage(NotesListActivity.this - .getString(R.string.error_sdcard_export)); - builder.setPositiveButton(android.R.string.ok, null); - builder.show(); - } - } - - }.execute(); - } - - /** - * @return - * 功能:判断是否正在同步 - */ - private boolean isSyncMode() { - return NotesPreferenceActivity.getSyncAccountName(this).trim().length() > 0; - } - - /** - * 功能:跳转到PreferenceActivity界面 - */ - private void startPreferenceActivity() { - Activity from = getParent() != null ? getParent() : this; - Intent intent = new Intent(from, NotesPreferenceActivity.class); - from.startActivityIfNeeded(intent, -1); - } - - /** - * @author k - * 函数功能:实现对便签列表项的点击事件(短按) - */ - private class OnListItemClickListener implements OnItemClickListener { - - public void onItemClick(AdapterView parent, View view, int position, long id) { - if (view instanceof NotesListItem) { - NoteItemData item = ((NotesListItem) view).getItemData(); - if (mNotesListAdapter.isInChoiceMode()) { - if (item.getType() == Notes.TYPE_NOTE) { - position = position - mNotesListView.getHeaderViewsCount(); - mModeCallBack.onItemCheckedStateChanged(null, position, id, - !mNotesListAdapter.isSelectedItem(position)); - } - return; - } - - switch (mState) { - case NOTE_LIST: - if (item.getType() == Notes.TYPE_FOLDER - || item.getType() == Notes.TYPE_SYSTEM) { - openFolder(item); - } else if (item.getType() == Notes.TYPE_NOTE) { - openNode(item); - } else { - Log.e(TAG, "Wrong note type in NOTE_LIST"); - } - break; - case SUB_FOLDER: - case CALL_RECORD_FOLDER: - if (item.getType() == Notes.TYPE_NOTE) { - openNode(item); - } else { - Log.e(TAG, "Wrong note type in SUB_FOLDER"); - } - break; - default: - break; - } - } - } - - } - - /** - * 查询目标文件 - */ - private void startQueryDestinationFolders() { - String selection = NoteColumns.TYPE + "=? AND " + NoteColumns.PARENT_ID + "<>? AND " + NoteColumns.ID + "<>?"; - selection = (mState == ListEditState.NOTE_LIST) ? selection: - "(" + selection + ") OR (" + NoteColumns.ID + "=" + Notes.ID_ROOT_FOLDER + ")"; - - mBackgroundQueryHandler.startQuery(FOLDER_LIST_QUERY_TOKEN, - null, - Notes.CONTENT_NOTE_URI, - FoldersListAdapter.PROJECTION, - selection, - new String[] { - String.valueOf(Notes.TYPE_FOLDER), - String.valueOf(Notes.ID_TRASH_FOLER), - String.valueOf(mCurrentFolderId) - }, - NoteColumns.MODIFIED_DATE + " DESC"); - } - - /* (non-Javadoc) - * @see android.widget.AdapterView.OnItemLongClickListener#onItemLongClick(android.widget.AdapterView, android.view.View, int, long) - * 长按某一项时进行的操作 - * 如果长按的是便签,则通过ActionMode菜单实现;如果长按的是文件夹,则通过ContextMenu菜单实现; - * 具体ActionMOde菜单和ContextMenu菜单的详细见精度笔记 - */ - public boolean onItemLongClick(AdapterView parent, View view, int position, long id) { - if (view instanceof NotesListItem) { - mFocusNoteDataItem = ((NotesListItem) view).getItemData(); - if (mFocusNoteDataItem.getType() == Notes.TYPE_NOTE && !mNotesListAdapter.isInChoiceMode()) { - if (mNotesListView.startActionMode(mModeCallBack) != null) { - mModeCallBack.onItemCheckedStateChanged(null, position, id, true); - mNotesListView.performHapticFeedback(HapticFeedbackConstants.LONG_PRESS); - } else { - Log.e(TAG, "startActionMode fails"); - } - } else if (mFocusNoteDataItem.getType() == Notes.TYPE_FOLDER) { - mNotesListView.setOnCreateContextMenuListener(mFolderOnCreateContextMenuListener); - } - } - return false; - } -} \ No newline at end of file diff --git a/other/06_刘峻豪_代码标注/ui/NotesListAdapter.java b/other/06_刘峻豪_代码标注/ui/NotesListAdapter.java deleted file mode 100644 index df174a2..0000000 --- a/other/06_刘峻豪_代码标注/ui/NotesListAdapter.java +++ /dev/null @@ -1,274 +0,0 @@ -/* - * 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.ui; - -import android.content.Context; -import android.database.Cursor; -import android.util.Log; -import android.view.View; -import android.view.ViewGroup; -import android.widget.CursorAdapter; - - -import net.micode.notes.data.Notes; - -import java.util.Collection; -import java.util.HashMap; -import java.util.HashSet; -import java.util.Iterator; - - -/* - * 功能:直译为便签表连接器,继承了CursorAdapter,它为cursor和ListView提供了连接的桥梁。 - * 所以NotesListAdapter实现的是鼠标和编辑便签链接的桥梁 - */ -public class NotesListAdapter extends CursorAdapter { - private static final String TAG = "NotesListAdapter"; - private Context mContext; - private HashMap mSelectedIndex; - private int mNotesCount; //便签数 - private boolean mChoiceMode; //选择模式标记 - - /* - * 桌面widget的属性,包括编号和类型 - */ - public static class AppWidgetAttribute { - public int widgetId; - public int widgetType; - }; - - /* - * 函数功能:初始化便签链接器 - * 函数实现:根据传进来的内容设置相关变量 - */ - public NotesListAdapter(Context context) { - super(context, null); //父类对象置空 - mSelectedIndex = new HashMap(); //新建选项下标的hash表 - mContext = context; - mNotesCount = 0; - } - - @Override - /* - * 函数功能:新建一个视图来存储光标所指向的数据 - * 函数实现:使用兄弟类NotesListItem新建一个项目选项 - */ - public View newView(Context context, Cursor cursor, ViewGroup parent) { - return new NotesListItem(context); - } - - /* - * 函数功能:将已经存在的视图和鼠标指向的数据进行捆绑 - * 函数实现:如下注释 - */ - @Override - public void bindView(View view, Context context, Cursor cursor) { - if (view instanceof NotesListItem) { - //若view是NotesListItem的一个实例 - NoteItemData itemData = new NoteItemData(context, cursor); - ((NotesListItem) view).bind(context, itemData, mChoiceMode, - isSelectedItem(cursor.getPosition())); - //则新建一个项目选项并且用bind跟将view和鼠标,内容,便签数据捆绑在一起 - } - } - - /* - * 函数功能:设置勾选框 - * 函数实现:如下注释 - */ - public void setCheckedItem(final int position, final boolean checked) { - mSelectedIndex.put(position, checked); - //根据定位和是否勾选设置下标 - notifyDataSetChanged(); - //在修改后刷新activity - } - - /* - * 函数功能:判断单选按钮是否勾选 - */ - public boolean isInChoiceMode() { - return mChoiceMode; - } - - /* - * 函数功能:设置单项选项框 - * 函数实现:重置下标并且根据参数mode设置选项 - */ - public void setChoiceMode(boolean mode) { - mSelectedIndex.clear(); - mChoiceMode = mode; - } - - /* - * 函数功能:选择全部选项 - * 函数实现:如下注释 - */ - public void selectAll(boolean checked) { - Cursor cursor = getCursor(); - //获取光标位置 - for (int i = 0; i < getCount(); i++) { - if (cursor.moveToPosition(i)) { - if (NoteItemData.getNoteType(cursor) == Notes.TYPE_NOTE) { - setCheckedItem(i, checked); - } - } - } - //遍历所有光标可用的位置在判断为便签类型之后勾选单项框 - } - - /* - * 函数功能:建立选择项的下标列表 - * 函数实现:如下注释 - */ - public HashSet getSelectedItemIds() { - HashSet itemSet = new HashSet(); - //建立hash表 - for (Integer position : mSelectedIndex.keySet()) { - //遍历所有的关键 - if (mSelectedIndex.get(position) == true) { - //若光标位置可用 - Long id = getItemId(position); - if (id == Notes.ID_ROOT_FOLDER) { - //原文件不需要添加 - Log.d(TAG, "Wrong item id, should not happen"); - } else { - itemSet.add(id); - } - //则将id该下标假如选项集合中 - - } - } - - return itemSet; - } - - /* - * 函数功能:建立桌面Widget的选项表 - * 函数实现:如下注释 - */ - public HashSet getSelectedWidget() { - HashSet itemSet = new HashSet(); - for (Integer position : mSelectedIndex.keySet()) { - if (mSelectedIndex.get(position) == true) { - Cursor c = (Cursor) getItem(position); - //以上4句和getSelectedItemIds一样,不再重复 - if (c != null) { - //光标位置可用的话就建立新的Widget属性并编辑下标和类型,最后添加到选项集中 - AppWidgetAttribute widget = new AppWidgetAttribute(); - NoteItemData item = new NoteItemData(mContext, c); - widget.widgetId = item.getWidgetId(); - widget.widgetType = item.getWidgetType(); - itemSet.add(widget); - /** - * Don't close cursor here, only the adapter could close it - */ - } else { - Log.e(TAG, "Invalid cursor"); - return null; - } - } - } - return itemSet; - } - - /* - * 函数功能:获取选项个数 - * 函数实现:如下注释 - */ - public int getSelectedCount() { - Collection values = mSelectedIndex.values(); - //首先获取选项下标的值 - if (null == values) { - return 0; - } - Iterator iter = values.iterator(); - //初始化叠加器 - int count = 0; - while (iter.hasNext()) { - if (true == iter.next()) { - //若value值为真计数+1 - count++; - } - } - return count; - } - - /* - * 函数功能:判断是否全部选中 - * 函数实现:如下注释 - */ - public boolean isAllSelected() { - int checkedCount = getSelectedCount(); - return (checkedCount != 0 && checkedCount == mNotesCount); - //获取选项数看是否等于便签的个数 - } - - /* - * 函数功能:判断是否为选项表 - * 函数实现:通过传递的下标来确定 - */ - public boolean isSelectedItem(final int position) { - if (null == mSelectedIndex.get(position)) { - return false; - } - return mSelectedIndex.get(position); - } - - @Override - /* - * 函数功能:在activity内容发生局部变动的时候回调该函数计算便签的数量 - * 函数实现:如下注释 - */ - protected void onContentChanged() { - super.onContentChanged(); - //执行基类函数 - calcNotesCount(); - } - - @Override - /* - * 函数功能:在activity光标发生局部变动的时候回调该函数计算便签的数量 - */ - public void changeCursor(Cursor cursor) { - super.changeCursor(cursor); - //执行基类函数 - calcNotesCount(); - } - - /* - * 函数功能:计算便签数量 - * - */ - private void calcNotesCount() { - mNotesCount = 0; - for (int i = 0; i < getCount(); i++) { - //获取总数同时遍历 - Cursor c = (Cursor) getItem(i); - if (c != null) { - if (NoteItemData.getNoteType(c) == Notes.TYPE_NOTE) { - mNotesCount++; - //若该位置不为空并且文本类型为便签就+1 - } - } else { - Log.e(TAG, "Invalid cursor"); - return; - } - //否则报错 - } - } -} \ No newline at end of file diff --git a/other/06_刘峻豪_代码标注/ui/NotesListItem.java b/other/06_刘峻豪_代码标注/ui/NotesListItem.java deleted file mode 100644 index 02ff433..0000000 --- a/other/06_刘峻豪_代码标注/ui/NotesListItem.java +++ /dev/null @@ -1,148 +0,0 @@ -/* - * 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. - */ - -/* - * 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.ui; - -import android.content.Context; -import android.text.format.DateUtils; -import android.view.View; -import android.widget.CheckBox; -import android.widget.ImageView; -import android.widget.LinearLayout; -import android.widget.TextView; - -import net.micode.notes.R; -import net.micode.notes.data.Notes; -import net.micode.notes.tool.DataUtils; -import net.micode.notes.tool.ResourceParser.NoteItemBgResources; - - -//创建便签列表项目选项 -public class NotesListItem extends LinearLayout { - private ImageView mAlert;//闹钟图片 - private TextView mTitle; //标题 - private TextView mTime; //时间 - private TextView mCallName; // - private NoteItemData mItemData; //标签数据 - private CheckBox mCheckBox; //打钩框 - - /*初始化基本信息*/ - public NotesListItem(Context context) { - super(context); //super()它的主要作用是调整调用父类构造函数的顺序 - inflate(context, R.layout.note_item, this);//Inflate可用于将一个xml中定义的布局控件找出来,这里的xml是r.layout - //findViewById用于从contentView中查找指定ID的View,转换出来的形式根据需要而定 - mAlert = (ImageView) findViewById(R.id.iv_alert_icon); - mTitle = (TextView) findViewById(R.id.tv_title); - mTime = (TextView) findViewById(R.id.tv_time); - mCallName = (TextView) findViewById(R.id.tv_name); - mCheckBox = (CheckBox) findViewById(android.R.id.checkbox); - } - //根据data的属性对各个控件的属性的控制,主要是可见性Visibility,内容setText,格式setTextAppearance - public void bind(Context context, NoteItemData data, boolean choiceMode, boolean checked) { - if (choiceMode && data.getType() == Notes.TYPE_NOTE) { - mCheckBox.setVisibility(View.VISIBLE); ///设置可见行为可见 - mCheckBox.setChecked(checked); ///格子打钩 - } else { - mCheckBox.setVisibility(View.GONE); - } - - mItemData = data; - ///设置控件属性,一共三种情况,由data的id和父id是否与保存到文件夹的id一致来决定 - if (data.getId() == Notes.ID_CALL_RECORD_FOLDER) { - mCallName.setVisibility(View.GONE); - mAlert.setVisibility(View.VISIBLE); - //设置该textview的style - mTitle.setTextAppearance(context, R.style.TextAppearancePrimaryItem); - //setText为设置内容 - mTitle.setText(context.getString(R.string.call_record_folder_name) - + context.getString(R.string.format_folder_files_count, data.getNotesCount())); - mAlert.setImageResource(R.drawable.call_record); - } else if (data.getParentId() == Notes.ID_CALL_RECORD_FOLDER) { - mCallName.setVisibility(View.VISIBLE); - mCallName.setText(data.getCallName()); - mTitle.setTextAppearance(context,R.style.TextAppearanceSecondaryItem); - mTitle.setText(DataUtils.getFormattedSnippet(data.getSnippet())); - ///给闹钟进行相关设置 - if (data.hasAlert()) { - mAlert.setImageResource(R.drawable.clock);//图片来源的设置 - mAlert.setVisibility(View.VISIBLE); - } else { - mAlert.setVisibility(View.GONE); - } - } else { - mCallName.setVisibility(View.GONE); - mTitle.setTextAppearance(context, R.style.TextAppearancePrimaryItem); - ///设置title格式 - if (data.getType() == Notes.TYPE_FOLDER) { - mTitle.setText(data.getSnippet() - + context.getString(R.string.format_folder_files_count, - data.getNotesCount())); - mAlert.setVisibility(View.GONE); - } else { - mTitle.setText(DataUtils.getFormattedSnippet(data.getSnippet())); - if (data.hasAlert()) { - mAlert.setImageResource(R.drawable.clock);///设置图片来源 - mAlert.setVisibility(View.VISIBLE); - } else { - mAlert.setVisibility(View.GONE); - } - } - } - ///设置内容,获取相关时间,从data里编辑的日期中获取 - mTime. setText(DateUtils.getRelativeTimeSpanString(data.getModifiedDate())); - - setBackground(data); - } - //根据data的文件属性来设置背景 - private void setBackground(NoteItemData data) { - int id = data.getBgColorId(); - //,若是note型文件,则4种情况,对于4种不同情况的背景来源 - if (data.getType() == Notes.TYPE_NOTE) { - //单个数据并且只有一个子文件夹 - if (data.isSingle() || data.isOneFollowingFolder()) { - setBackgroundResource(NoteItemBgResources.getNoteBgSingleRes(id)); - } else if (data.isLast()) {//是最后一个数据 - setBackgroundResource(NoteItemBgResources.getNoteBgLastRes(id)); - } else if (data.isFirst() || data.isMultiFollowingFolder()) {//是一个数据并有多个子文件夹 - setBackgroundResource(NoteItemBgResources.getNoteBgFirstRes(id)); - } else { - setBackgroundResource(NoteItemBgResources.getNoteBgNormalRes(id)); - } - } else { - //若不是note直接调用文件夹的背景来源 - setBackgroundResource(NoteItemBgResources.getFolderBgRes()); - } - } - public NoteItemData getItemData() { - return mItemData; - } -} \ No newline at end of file diff --git a/other/06_刘峻豪_代码标注/ui/NotesPreferenceActivity.java b/other/06_刘峻豪_代码标注/ui/NotesPreferenceActivity.java deleted file mode 100644 index 982b261..0000000 --- a/other/06_刘峻豪_代码标注/ui/NotesPreferenceActivity.java +++ /dev/null @@ -1,546 +0,0 @@ -/* - * 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. - */ -/* - * 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.ui; - -import android.accounts.Account; -import android.accounts.AccountManager; -import android.app.ActionBar; -import android.app.AlertDialog; -import android.content.BroadcastReceiver; -import android.content.ContentValues; -import android.content.Context; -import android.content.DialogInterface; -import android.content.Intent; -import android.content.IntentFilter; -import android.content.SharedPreferences; -import android.os.Bundle; -import android.preference.Preference; -import android.preference.Preference.OnPreferenceClickListener; -import android.preference.PreferenceActivity; -import android.preference.PreferenceCategory; -import android.text.TextUtils; -import android.text.format.DateFormat; -import android.view.LayoutInflater; -import android.view.Menu; -import android.view.MenuItem; -import android.view.View; -import android.widget.Button; -import android.widget.TextView; -import android.widget.Toast; - -import net.micode.notes.R; -import net.micode.notes.data.Notes; -import net.micode.notes.data.Notes.NoteColumns; -import net.micode.notes.gtask.remote.GTaskSyncService; - -/* - *该类功能:在小米便签中主要实现的是对背景颜色和字体大小的数据储存。 - * 继承了PreferenceActivity主要功能为对系统信息和配置进行自动保存的Activity - */ -public class NotesPreferenceActivity extends PreferenceActivity { - public static final String PREFERENCE_NAME = "notes_preferences"; - //优先名 - public static final String PREFERENCE_SYNC_ACCOUNT_NAME = "pref_key_account_name"; - //同步账号 - public static final String PREFERENCE_LAST_SYNC_TIME = "pref_last_sync_time"; - //同步时间 - public static final String PREFERENCE_SET_BG_COLOR_KEY = "pref_key_bg_random_appear"; - - private static final String PREFERENCE_SYNC_ACCOUNT_KEY = "pref_sync_account_key"; - //同步密码 - private static final String AUTHORITIES_FILTER_KEY = "authorities"; - //本地密码 - private PreferenceCategory mAccountCategory; - //账户分组 - private GTaskReceiver mReceiver; - //同步任务接收器 - private Account[] mOriAccounts; - //账户 - private boolean mHasAddedAccount; - //账户的hash标记 - - @Override - /* - *函数功能:创建一个activity,在函数里要完成所有的正常静态设置 - *参数:Bundle icicle:存放了 activity 当前的状态 - *函数实现:如下注释 - */ - protected void onCreate(Bundle icicle) { - //先执行父类的创建函数 - super.onCreate(icicle); - - /* using the app icon for navigation */ - getActionBar().setDisplayHomeAsUpEnabled(true); - //给左上角图标的左边加上一个返回的图标 - - addPreferencesFromResource(R.xml.preferences); - //添加xml来源并显示 xml - mAccountCategory = (PreferenceCategory) findPreference(PREFERENCE_SYNC_ACCOUNT_KEY); - //根据同步账户关键码来初始化分组 - mReceiver = new GTaskReceiver(); - IntentFilter filter = new IntentFilter(); - filter.addAction(GTaskSyncService.GTASK_SERVICE_BROADCAST_NAME); - registerReceiver(mReceiver, filter); - //初始化同步组件 - - mOriAccounts = null; - View header = LayoutInflater.from(this).inflate(R.layout.settings_header, null); - //获取listView - // ListView的作用:用于列出所有选择 - getListView().addHeaderView(header, null, true); - //在listView组件上方添加其他组件 - } - - @Override - /* - * 函数功能:activity交互功能的实现,用于接受用户的输入 - * 函数实现:如下注释 - */ - protected void onResume() { - //先执行父类 的交互实现 - super.onResume(); - - // need to set sync account automatically if user has added a new - // account - if (mHasAddedAccount) { - //若用户新加了账户则自动设置同步账户 - Account[] accounts = getGoogleAccounts(); - //获取google同步账户 - if (mOriAccounts != null && accounts.length > mOriAccounts.length) { - //若原账户不为空且当前账户有增加 - for (Account accountNew : accounts) { - boolean found = false; - for (Account accountOld : mOriAccounts) { - if (TextUtils.equals(accountOld.name, accountNew.name)) { - //更新账户 - found = true; - break; - } - } - if (!found) { - setSyncAccount(accountNew.name); - //若是没有找到旧的账户,那么同步账号中就只添加新账户 - break; - } - } - } - } - - refreshUI(); - //刷新标签界面 - } - - @Override - /* - * 函数功能:销毁一个activity - * 函数实现:如下注释 - */ - protected void onDestroy() { - if (mReceiver != null) { - unregisterReceiver(mReceiver); - //注销接收器 - } - super.onDestroy(); - //执行父类的销毁动作 - } - - /* - * 函数功能:重新设置账户信息 - * 函数实现:如下注释 - */ - private void loadAccountPreference() { - mAccountCategory.removeAll(); - //销毁所有的分组 - Preference accountPref = new Preference(this); - //建立首选项 - final String defaultAccount = getSyncAccountName(this); - accountPref.setTitle(getString(R.string.preferences_account_title)); - accountPref.setSummary(getString(R.string.preferences_account_summary)); - //设置首选项的大标题和小标题 - accountPref.setOnPreferenceClickListener(new OnPreferenceClickListener() { - public boolean onPreferenceClick(Preference preference) { - //建立监听器 - if (!GTaskSyncService.isSyncing()) { - if (TextUtils.isEmpty(defaultAccount)) { - // the first time to set account - //若是第一次建立账户显示选择账户提示对话框 - showSelectAccountAlertDialog(); - } else { - // if the account has already been set, we need to promp - // user about the risk - //若是已经建立则显示修改对话框并进行修改操作 - showChangeAccountConfirmAlertDialog(); - } - } else { - //若在没有同步的情况下,则在toast中显示不能修改 - Toast.makeText(NotesPreferenceActivity.this, - R.string.preferences_toast_cannot_change_account, Toast.LENGTH_SHORT) - .show(); - } - return true; - } - }); - - //根据新建首选项编辑新的账户分组 - mAccountCategory.addPreference(accountPref); - } - - /* - *函数功能:设置按键的状态和最后同步的时间 - *函数实现:如下注释 - */ - private void loadSyncButton() { - Button syncButton = (Button) findViewById(R.id.preference_sync_button); - TextView lastSyncTimeView = (TextView) findViewById(R.id.prefenerece_sync_status_textview); - //获取同步按钮控件和最终同步时间的的窗口 - // set button state - //设置按钮的状态 - if (GTaskSyncService.isSyncing()) { - //若是在同步状态下 - syncButton.setText(getString(R.string.preferences_button_sync_cancel)); - syncButton.setOnClickListener(new View.OnClickListener() { - public void onClick(View v) { - GTaskSyncService.cancelSync(NotesPreferenceActivity.this); - } - }); - //设置按钮显示的文本为“取消同步”以及监听器 - } else { - syncButton.setText(getString(R.string.preferences_button_sync_immediately)); - syncButton.setOnClickListener(new View.OnClickListener() { - public void onClick(View v) { - GTaskSyncService.startSync(NotesPreferenceActivity.this); - } - }); - //若是不同步则设置按钮显示的文本为“立即同步”以及对应监听器 - } - syncButton.setEnabled(!TextUtils.isEmpty(getSyncAccountName(this))); - //设置按键可用还是不可用 - - // set last sync time - // 设置最终同步时间 - if (GTaskSyncService.isSyncing()) { - //若是在同步的情况下 - lastSyncTimeView.setText(GTaskSyncService.getProgressString()); - lastSyncTimeView.setVisibility(View.VISIBLE); - // 根据当前同步服务器设置时间显示框的文本以及可见性 - } else { - //若是非同步情况 - long lastSyncTime = getLastSyncTime(this); - if (lastSyncTime != 0) { - lastSyncTimeView.setText(getString(R.string.preferences_last_sync_time, - DateFormat.format(getString(R.string.preferences_last_sync_time_format), - lastSyncTime))); - lastSyncTimeView.setVisibility(View.VISIBLE); - //则根据最后同步时间的信息来编辑时间显示框的文本内容和可见性 - } else { - //若时间为空直接设置为不可见状态 - lastSyncTimeView.setVisibility(View.GONE); - } - } - } - /* - *函数功能:刷新标签界面 - *函数实现:调用上文设置账号和设置按键两个函数来实现 - */ - private void refreshUI() { - loadAccountPreference(); - loadSyncButton(); - } - - /* - * 函数功能:显示账户选择的对话框并进行账户的设置 - * 函数实现:如下注释 - */ - private void showSelectAccountAlertDialog() { - AlertDialog.Builder dialogBuilder = new AlertDialog.Builder(this); - //创建一个新的对话框 - - View titleView = LayoutInflater.from(this).inflate(R.layout.account_dialog_title, null); - TextView titleTextView = (TextView) titleView.findViewById(R.id.account_dialog_title); - titleTextView.setText(getString(R.string.preferences_dialog_select_account_title)); - TextView subtitleTextView = (TextView) titleView.findViewById(R.id.account_dialog_subtitle); - subtitleTextView.setText(getString(R.string.preferences_dialog_select_account_tips)); - //设置标题以及子标题的内容 - dialogBuilder.setCustomTitle(titleView); - dialogBuilder.setPositiveButton(null, null); - //设置对话框的自定义标题,建立一个YES的按钮 - Account[] accounts = getGoogleAccounts(); - String defAccount = getSyncAccountName(this); - //获取同步账户信息 - mOriAccounts = accounts; - mHasAddedAccount = false; - - if (accounts.length > 0) { - //若账户不为空 - CharSequence[] items = new CharSequence[accounts.length]; - final CharSequence[] itemMapping = items; - int checkedItem = -1; - int index = 0; - for (Account account : accounts) { - if (TextUtils.equals(account.name, defAccount)) { - checkedItem = index; - //在账户列表中查询到所需账户 - } - items[index++] = account.name; - } - dialogBuilder.setSingleChoiceItems(items, checkedItem, - //在对话框建立一个单选的复选框 - new DialogInterface.OnClickListener() { - public void onClick(DialogInterface dialog, int which) { - setSyncAccount(itemMapping[which].toString()); - dialog.dismiss(); - //取消对话框 - refreshUI(); - } - //设置点击后执行的事件,包括检录新同步账户和刷新标签界面 - }); - //建立对话框网络版的监听器 - } - - View addAccountView = LayoutInflater.from(this).inflate(R.layout.add_account_text, null); - dialogBuilder.setView(addAccountView); - //给新加账户对话框设置自定义样式 - - final AlertDialog dialog = dialogBuilder.show(); - //显示对话框 - addAccountView.setOnClickListener(new View.OnClickListener() { - public void onClick(View v) { - mHasAddedAccount = true; - //将新加账户的hash置true - Intent intent = new Intent("android.settings.ADD_ACCOUNT_SETTINGS"); - //建立网络建立组件 - intent.putExtra(AUTHORITIES_FILTER_KEY, new String[] { - "gmail-ls" - }); - startActivityForResult(intent, -1); - //跳回上一个选项 - dialog.dismiss(); - } - }); - //建立新加账户对话框的监听器 - } - - /* - * 函数功能:显示账户选择对话框和相关账户操作 - * 函数实现:如下注释 - */ - private void showChangeAccountConfirmAlertDialog() { - AlertDialog.Builder dialogBuilder = new AlertDialog.Builder(this); - //创建一个新的对话框 - View titleView = LayoutInflater.from(this).inflate(R.layout.account_dialog_title, null); - TextView titleTextView = (TextView) titleView.findViewById(R.id.account_dialog_title); - titleTextView.setText(getString(R.string.preferences_dialog_change_account_title, - getSyncAccountName(this))); - TextView subtitleTextView = (TextView) titleView.findViewById(R.id.account_dialog_subtitle); - subtitleTextView.setText(getString(R.string.preferences_dialog_change_account_warn_msg)); - //根据同步修改的账户信息设置标题以及子标题的内容 - dialogBuilder.setCustomTitle(titleView); - //设置对话框的自定义标题 - CharSequence[] menuItemArray = new CharSequence[] { - getString(R.string.preferences_menu_change_account), - getString(R.string.preferences_menu_remove_account), - getString(R.string.preferences_menu_cancel) - }; - //定义一些标记字符串 - dialogBuilder.setItems(menuItemArray, new DialogInterface.OnClickListener() { - //设置对话框要显示的一个list,用于显示几个命令时,即change,remove,cancel - 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; - } - } -} - diff --git a/other/06_江一阳_代码标注/gtask/data/MetaData.java b/other/06_江一阳_代码标注/gtask/data/MetaData.java deleted file mode 100644 index 01afbd2..0000000 --- a/other/06_江一阳_代码标注/gtask/data/MetaData.java +++ /dev/null @@ -1,97 +0,0 @@ -/* - * 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.data; - -// 导入所需的类和包 -import android.database.Cursor; -import android.util.Log; - -import net.micode.notes.tool.GTaskStringUtils; - -import org.json.JSONException; -import org.json.JSONObject; - -// MetaData类继承自Task类,用于处理与Google任务相关的元数据 -public class MetaData extends Task { - // 定义类的TAG,用于日志记录 - private final static String TAG = MetaData.class.getSimpleName(); - // 定义一个变量用于存储与Google任务相关的ID - private String mRelatedGid = null; - - // 设置元数据方法,接收一个gid和一个metaInfo JSONObject对象 - public void setMeta(String gid, JSONObject metaInfo) { - try { - // 将gid存储在metaInfo的JSON对象中 - metaInfo.put(GTaskStringUtils.META_HEAD_GTASK_ID, gid); - } catch (JSONException e) { - // 记录错误日志 - Log.e(TAG, "failed to put related gid"); - } - // 记录错误日志 - setNotes(metaInfo.toString()); - // 设置元数据任务的名称 - setName(GTaskStringUtils.META_NOTE_NAME); - } - - // 获取与Google任务相关的ID的方法 - public String getRelatedGid() { - return mRelatedGid; - } - - // 重写isWorthSaving方法,判断是否值得保存 - @Override - public boolean isWorthSaving() { - return getNotes() != null; - } - - // 从远程JSON设置内容 - @Override - public void setContentByRemoteJSON(JSONObject js) { - super.setContentByRemoteJSON(js); - if (getNotes() != null) { - try { - // 解析notes中的JSON字符串 - JSONObject metaInfo = new JSONObject(getNotes().trim()); - // 从JSON对象中获取与Google任务相关的ID - mRelatedGid = metaInfo.getString(GTaskStringUtils.META_HEAD_GTASK_ID); - } catch (JSONException e) { - // 记录警告日志 - Log.w(TAG, "failed to get related gid"); - mRelatedGid = null; - } - } - } - - // 从本地JSON设置内容,但是不应该被调用,因此抛出异常 - @Override - public void setContentByLocalJSON(JSONObject js) { - // this function should not be called - throw new IllegalAccessError("MetaData:setContentByLocalJSON should not be called"); - } - - // 从内容获取本地JSON,但是不应该被调用,因此抛出异常 - @Override - public JSONObject getLocalJSONFromContent() { - throw new IllegalAccessError("MetaData:getLocalJSONFromContent should not be called"); - } - // 获取同步操作,但是不应该被调用,因此抛出异常 - @Override - public int getSyncAction(Cursor c) { - throw new IllegalAccessError("MetaData:getSyncAction should not be called"); - } - -} diff --git a/other/06_江一阳_代码标注/gtask/data/Node.java b/other/06_江一阳_代码标注/gtask/data/Node.java deleted file mode 100644 index f205cdf..0000000 --- a/other/06_江一阳_代码标注/gtask/data/Node.java +++ /dev/null @@ -1,113 +0,0 @@ -/* - * 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.data; - -// 导入所需的类和包 -import android.database.Cursor; - -import org.json.JSONObject; - -// 定义一个抽象类Node,用于表示各种数据节点 -public abstract class Node { - // 定义同步操作的常量 - public static final int SYNC_ACTION_NONE = 0; - - public static final int SYNC_ACTION_ADD_REMOTE = 1; - - public static final int SYNC_ACTION_ADD_LOCAL = 2; - - public static final int SYNC_ACTION_DEL_REMOTE = 3; - - public static final int SYNC_ACTION_DEL_LOCAL = 4; - - public static final int SYNC_ACTION_UPDATE_REMOTE = 5; - - public static final int SYNC_ACTION_UPDATE_LOCAL = 6; - - public static final int SYNC_ACTION_UPDATE_CONFLICT = 7; - - public static final int SYNC_ACTION_ERROR = 8; - // 定义节点的基本属性 - private String mGid; - - private String mName; - - private long mLastModified; - - private boolean mDeleted; - - // 构造方法,初始化节点的属性 - public Node() { - mGid = null; - mName = ""; - mLastModified = 0; - mDeleted = false; - } - - // 定义抽象方法,用于获取创建操作的JSON对象 - public abstract JSONObject getCreateAction(int actionId); - - // 定义抽象方法,用于获取更新操作的JSON对象 - public abstract JSONObject getUpdateAction(int actionId); - - // 定义抽象方法,用于根据远程JSON设置节点的内容 - public abstract void setContentByRemoteJSON(JSONObject js); - - // 定义抽象方法,用于根据本地JSON设置节点的内容 - public abstract void setContentByLocalJSON(JSONObject js); - - // 定义抽象方法,用于根据内容获取本地JSON - public abstract JSONObject getLocalJSONFromContent(); - - // 定义抽象方法,用于获取同步操作类型 - public abstract int getSyncAction(Cursor c); - - // 定义setter方法,用于设置节点的属性 - public void setGid(String gid) { - this.mGid = gid; - } - - public void setName(String name) { - this.mName = name; - } - - public void setLastModified(long lastModified) { - this.mLastModified = lastModified; - } - - public void setDeleted(boolean deleted) { - this.mDeleted = deleted; - } - - // 定义getter方法,用于获取节点的属性 - public String getGid() { - return this.mGid; - } - - public String getName() { - return this.mName; - } - - public long getLastModified() { - return this.mLastModified; - } - - public boolean getDeleted() { - return this.mDeleted; - } - -} diff --git a/other/06_江一阳_代码标注/gtask/data/SqlData.java b/other/06_江一阳_代码标注/gtask/data/SqlData.java deleted file mode 100644 index 2685b8a..0000000 --- a/other/06_江一阳_代码标注/gtask/data/SqlData.java +++ /dev/null @@ -1,190 +0,0 @@ -/* - * 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.data; -// 导入所需的类和包 -import android.content.ContentResolver; -import android.content.ContentUris; -import android.content.ContentValues; -import android.content.Context; -import android.database.Cursor; -import android.net.Uri; -import android.util.Log; - -import net.micode.notes.data.Notes; -import net.micode.notes.data.Notes.DataColumns; -import net.micode.notes.data.Notes.DataConstants; -import net.micode.notes.data.Notes.NoteColumns; -import net.micode.notes.data.NotesDatabaseHelper.TABLE; -import net.micode.notes.gtask.exception.ActionFailureException; - -import org.json.JSONException; -import org.json.JSONObject; - -// 定义一个名为SqlData的类,用于表示SQL数据 -public class SqlData { - // 定义类的标签和常量 - private static final String TAG = SqlData.class.getSimpleName(); - - private static final int INVALID_ID = -99999; - // 定义查询的投影和列索引常量 - public static final String[] PROJECTION_DATA = new String[] { - DataColumns.ID, DataColumns.MIME_TYPE, DataColumns.CONTENT, DataColumns.DATA1, - DataColumns.DATA3 - }; - - public static final int DATA_ID_COLUMN = 0; - - public static final int DATA_MIME_TYPE_COLUMN = 1; - - public static final int DATA_CONTENT_COLUMN = 2; - - public static final int DATA_CONTENT_DATA_1_COLUMN = 3; - - public static final int DATA_CONTENT_DATA_3_COLUMN = 4; - // 定义类的属性 - private ContentResolver mContentResolver; - - private boolean mIsCreate; - - private long mDataId; - - private String mDataMimeType; - - private String mDataContent; - - private long mDataContentData1; - - private String mDataContentData3; - - private ContentValues mDiffDataValues; - // 构造方法,初始化类的属性 - public SqlData(Context context) { - mContentResolver = context.getContentResolver(); - mIsCreate = true; - mDataId = INVALID_ID; - mDataMimeType = DataConstants.NOTE; - mDataContent = ""; - mDataContentData1 = 0; - mDataContentData3 = ""; - mDiffDataValues = new ContentValues(); - } - // 基于现有的游标创建SqlData实例 - public SqlData(Context context, Cursor c) { - mContentResolver = context.getContentResolver(); - mIsCreate = false; - loadFromCursor(c); - mDiffDataValues = new ContentValues(); - } - // 从游标加载数据 - private void loadFromCursor(Cursor c) { - mDataId = c.getLong(DATA_ID_COLUMN); - mDataMimeType = c.getString(DATA_MIME_TYPE_COLUMN); - mDataContent = c.getString(DATA_CONTENT_COLUMN); - mDataContentData1 = c.getLong(DATA_CONTENT_DATA_1_COLUMN); - mDataContentData3 = c.getString(DATA_CONTENT_DATA_3_COLUMN); - } - // 从JSONObject设置内容 - public void setContent(JSONObject js) throws JSONException { - long dataId = js.has(DataColumns.ID) ? js.getLong(DataColumns.ID) : INVALID_ID; - if (mIsCreate || mDataId != dataId) { - mDiffDataValues.put(DataColumns.ID, dataId); - } - mDataId = dataId; - - String dataMimeType = js.has(DataColumns.MIME_TYPE) ? js.getString(DataColumns.MIME_TYPE) - : DataConstants.NOTE; - if (mIsCreate || !mDataMimeType.equals(dataMimeType)) { - mDiffDataValues.put(DataColumns.MIME_TYPE, dataMimeType); - } - mDataMimeType = dataMimeType; - - String dataContent = js.has(DataColumns.CONTENT) ? js.getString(DataColumns.CONTENT) : ""; - if (mIsCreate || !mDataContent.equals(dataContent)) { - mDiffDataValues.put(DataColumns.CONTENT, dataContent); - } - mDataContent = dataContent; - - long dataContentData1 = js.has(DataColumns.DATA1) ? js.getLong(DataColumns.DATA1) : 0; - if (mIsCreate || mDataContentData1 != dataContentData1) { - mDiffDataValues.put(DataColumns.DATA1, dataContentData1); - } - mDataContentData1 = dataContentData1; - - String dataContentData3 = js.has(DataColumns.DATA3) ? js.getString(DataColumns.DATA3) : ""; - if (mIsCreate || !mDataContentData3.equals(dataContentData3)) { - mDiffDataValues.put(DataColumns.DATA3, dataContentData3); - } - mDataContentData3 = dataContentData3; - } - // 获取内容并将其转换为JSONObject - public JSONObject getContent() throws JSONException { - if (mIsCreate) { - Log.e(TAG, "it seems that we haven't created this in database yet"); - return null; - } - JSONObject js = new JSONObject(); - js.put(DataColumns.ID, mDataId); - js.put(DataColumns.MIME_TYPE, mDataMimeType); - js.put(DataColumns.CONTENT, mDataContent); - js.put(DataColumns.DATA1, mDataContentData1); - js.put(DataColumns.DATA3, mDataContentData3); - return js; - } - // 提交更改到数据库 - public void commit(long noteId, boolean validateVersion, long version) { - - if (mIsCreate) { - if (mDataId == INVALID_ID && mDiffDataValues.containsKey(DataColumns.ID)) { - mDiffDataValues.remove(DataColumns.ID); - } - - mDiffDataValues.put(DataColumns.NOTE_ID, noteId); - Uri uri = mContentResolver.insert(Notes.CONTENT_DATA_URI, mDiffDataValues); - try { - mDataId = Long.valueOf(uri.getPathSegments().get(1)); - } catch (NumberFormatException e) { - Log.e(TAG, "Get note id error :" + e.toString()); - throw new ActionFailureException("create note failed"); - } - } else { - if (mDiffDataValues.size() > 0) { - int result = 0; - if (!validateVersion) { - result = mContentResolver.update(ContentUris.withAppendedId( - Notes.CONTENT_DATA_URI, mDataId), mDiffDataValues, null, null); - } else { - result = mContentResolver.update(ContentUris.withAppendedId( - Notes.CONTENT_DATA_URI, mDataId), mDiffDataValues, - " ? in (SELECT " + NoteColumns.ID + " FROM " + TABLE.NOTE - + " WHERE " + NoteColumns.VERSION + "=?)", new String[] { - String.valueOf(noteId), String.valueOf(version) - }); - } - if (result == 0) { - Log.w(TAG, "there is no update. maybe user updates note when syncing"); - } - } - } - - mDiffDataValues.clear(); - mIsCreate = false; - } - // 获取数据ID - public long getId() { - return mDataId; - } -} diff --git a/other/06_江一阳_代码标注/gtask/data/SqlNote.java b/other/06_江一阳_代码标注/gtask/data/SqlNote.java deleted file mode 100644 index ddcb8a1..0000000 --- a/other/06_江一阳_代码标注/gtask/data/SqlNote.java +++ /dev/null @@ -1,515 +0,0 @@ -/* - * 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.data; - -import android.appwidget.AppWidgetManager; -import android.content.ContentResolver; -import android.content.ContentValues; -import android.content.Context; -import android.database.Cursor; -import android.net.Uri; -import android.util.Log; - -import net.micode.notes.data.Notes; -import net.micode.notes.data.Notes.DataColumns; -import net.micode.notes.data.Notes.NoteColumns; -import net.micode.notes.gtask.exception.ActionFailureException; -import net.micode.notes.tool.GTaskStringUtils; -import net.micode.notes.tool.ResourceParser; - -import org.json.JSONArray; -import org.json.JSONException; -import org.json.JSONObject; - -import java.util.ArrayList; - -// 定义 SqlNote 类 -public class SqlNote { - // 定义日志标签,用于调试和输出日志信息 - private static final String TAG = SqlNote.class.getSimpleName(); - // 定义一个无效的 ID 常量 - private static final int INVALID_ID = -99999; - // 定义 PROJECTION_NOTE 数组,用于查询数据库时指定需要的列 - public static final String[] PROJECTION_NOTE = new String[] { - NoteColumns.ID, NoteColumns.ALERTED_DATE, NoteColumns.BG_COLOR_ID, - NoteColumns.CREATED_DATE, NoteColumns.HAS_ATTACHMENT, NoteColumns.MODIFIED_DATE, - NoteColumns.NOTES_COUNT, NoteColumns.PARENT_ID, NoteColumns.SNIPPET, NoteColumns.TYPE, - NoteColumns.WIDGET_ID, NoteColumns.WIDGET_TYPE, NoteColumns.SYNC_ID, - NoteColumns.LOCAL_MODIFIED, NoteColumns.ORIGIN_PARENT_ID, NoteColumns.GTASK_ID, - NoteColumns.VERSION - }; - - public static final int ID_COLUMN = 0; - - public static final int ALERTED_DATE_COLUMN = 1; - - public static final int BG_COLOR_ID_COLUMN = 2; - - public static final int CREATED_DATE_COLUMN = 3; - - public static final int HAS_ATTACHMENT_COLUMN = 4; - - public static final int MODIFIED_DATE_COLUMN = 5; - - public static final int NOTES_COUNT_COLUMN = 6; - - public static final int PARENT_ID_COLUMN = 7; - - public static final int SNIPPET_COLUMN = 8; - - public static final int TYPE_COLUMN = 9; - - public static final int WIDGET_ID_COLUMN = 10; - - public static final int WIDGET_TYPE_COLUMN = 11; - - public static final int SYNC_ID_COLUMN = 12; - - public static final int LOCAL_MODIFIED_COLUMN = 13; - - public static final int ORIGIN_PARENT_ID_COLUMN = 14; - - public static final int GTASK_ID_COLUMN = 15; - - public static final int VERSION_COLUMN = 16; - - private Context mContext; - - private ContentResolver mContentResolver; - - private boolean mIsCreate; - - private long mId; - - private long mAlertDate; - - private int mBgColorId; - - private long mCreatedDate; - - private int mHasAttachment; - - private long mModifiedDate; - - private long mParentId; - - private String mSnippet; - - private int mType; - - private int mWidgetId; - - private int mWidgetType; - - private long mOriginParent; - - private long mVersion; - - private ContentValues mDiffNoteValues; - - private ArrayList mDataList; - - // 构造方法:用于创建新的笔记对象 - public SqlNote(Context context) { - mContext = context; - mContentResolver = context.getContentResolver(); - mIsCreate = true; - mId = INVALID_ID; - mAlertDate = 0; - mBgColorId = ResourceParser.getDefaultBgId(context); - mCreatedDate = System.currentTimeMillis(); - mHasAttachment = 0; - mModifiedDate = System.currentTimeMillis(); - mParentId = 0; - mSnippet = ""; - mType = Notes.TYPE_NOTE; - mWidgetId = AppWidgetManager.INVALID_APPWIDGET_ID; - mWidgetType = Notes.TYPE_WIDGET_INVALIDE; - mOriginParent = 0; - mVersion = 0; - mDiffNoteValues = new ContentValues(); - mDataList = new ArrayList(); - } - - // 构造方法:用于从数据库的 Cursor 加载笔记对象 - public SqlNote(Context context, Cursor c) { - mContext = context; - mContentResolver = context.getContentResolver(); - mIsCreate = false; - loadFromCursor(c); - mDataList = new ArrayList(); - if (mType == Notes.TYPE_NOTE) - loadDataContent(); - mDiffNoteValues = new ContentValues(); - } - - // 构造方法:通过笔记 ID 从数据库加载笔记对象 - public SqlNote(Context context, long id) { - mContext = context; - mContentResolver = context.getContentResolver(); - mIsCreate = false; - loadFromCursor(id); - mDataList = new ArrayList(); - if (mType == Notes.TYPE_NOTE) - loadDataContent(); - mDiffNoteValues = new ContentValues(); - - } - - // 通过笔记 ID 从数据库加载笔记数据 - private void loadFromCursor(long id) { - Cursor c = null; - try { - c = mContentResolver.query(Notes.CONTENT_NOTE_URI, PROJECTION_NOTE, "(_id=?)", - new String[] { - String.valueOf(id) - }, null); - if (c != null) { - c.moveToNext(); - loadFromCursor(c); - } else { - Log.w(TAG, "loadFromCursor: cursor = null"); - } - } finally { - if (c != null) - c.close(); - } - } - - // 从 Cursor 加载 SqlNote 对象的数据 - private void loadFromCursor(Cursor c) { - mId = c.getLong(ID_COLUMN); - mAlertDate = c.getLong(ALERTED_DATE_COLUMN); - mBgColorId = c.getInt(BG_COLOR_ID_COLUMN); - mCreatedDate = c.getLong(CREATED_DATE_COLUMN); - mHasAttachment = c.getInt(HAS_ATTACHMENT_COLUMN); - mModifiedDate = c.getLong(MODIFIED_DATE_COLUMN); - mParentId = c.getLong(PARENT_ID_COLUMN); - mSnippet = c.getString(SNIPPET_COLUMN); - mType = c.getInt(TYPE_COLUMN); - mWidgetId = c.getInt(WIDGET_ID_COLUMN); - mWidgetType = c.getInt(WIDGET_TYPE_COLUMN); - mVersion = c.getLong(VERSION_COLUMN); - } - - // 加载笔记的数据内容 - private void loadDataContent() { - Cursor c = null; - mDataList.clear(); - try { - c = mContentResolver.query(Notes.CONTENT_DATA_URI, SqlData.PROJECTION_DATA, - "(note_id=?)", new String[] { - String.valueOf(mId) - }, null); - if (c != null) { - if (c.getCount() == 0) { - Log.w(TAG, "it seems that the note has not data"); - return; - } - while (c.moveToNext()) { - SqlData data = new SqlData(mContext, c); - mDataList.add(data); - } - } else { - Log.w(TAG, "loadDataContent: cursor = null"); - } - } finally { - if (c != null) - c.close(); - } - } - - public boolean setContent(JSONObject js) { - try { - // 获取包含笔记信息的 JSON 对象 - JSONObject note = js.getJSONObject(GTaskStringUtils.META_HEAD_NOTE); - if (note.getInt(NoteColumns.TYPE) == Notes.TYPE_SYSTEM) { - Log.w(TAG, "cannot set system folder"); - } else if (note.getInt(NoteColumns.TYPE) == Notes.TYPE_FOLDER) { - // for folder we can only update the snnipet and type - String snippet = note.has(NoteColumns.SNIPPET) ? note - .getString(NoteColumns.SNIPPET) : ""; - if (mIsCreate || !mSnippet.equals(snippet)) { - mDiffNoteValues.put(NoteColumns.SNIPPET, snippet); - } - mSnippet = snippet; - - int type = note.has(NoteColumns.TYPE) ? note.getInt(NoteColumns.TYPE) - : Notes.TYPE_NOTE; - if (mIsCreate || mType != type) { - mDiffNoteValues.put(NoteColumns.TYPE, type); - } - mType = type; - } else if (note.getInt(NoteColumns.TYPE) == Notes.TYPE_NOTE) { - JSONArray dataArray = js.getJSONArray(GTaskStringUtils.META_HEAD_DATA); - long id = note.has(NoteColumns.ID) ? note.getLong(NoteColumns.ID) : INVALID_ID; - if (mIsCreate || mId != id) { - mDiffNoteValues.put(NoteColumns.ID, id); - } - mId = id; - - long alertDate = note.has(NoteColumns.ALERTED_DATE) ? note - .getLong(NoteColumns.ALERTED_DATE) : 0; - if (mIsCreate || mAlertDate != alertDate) { - mDiffNoteValues.put(NoteColumns.ALERTED_DATE, alertDate); - } - mAlertDate = alertDate; - - int bgColorId = note.has(NoteColumns.BG_COLOR_ID) ? note - .getInt(NoteColumns.BG_COLOR_ID) : ResourceParser.getDefaultBgId(mContext); - if (mIsCreate || mBgColorId != bgColorId) { - mDiffNoteValues.put(NoteColumns.BG_COLOR_ID, bgColorId); - } - mBgColorId = bgColorId; - - long createDate = note.has(NoteColumns.CREATED_DATE) ? note - .getLong(NoteColumns.CREATED_DATE) : System.currentTimeMillis(); - if (mIsCreate || mCreatedDate != createDate) { - mDiffNoteValues.put(NoteColumns.CREATED_DATE, createDate); - } - mCreatedDate = createDate; - - int hasAttachment = note.has(NoteColumns.HAS_ATTACHMENT) ? note - .getInt(NoteColumns.HAS_ATTACHMENT) : 0; - if (mIsCreate || mHasAttachment != hasAttachment) { - mDiffNoteValues.put(NoteColumns.HAS_ATTACHMENT, hasAttachment); - } - mHasAttachment = hasAttachment; - - long modifiedDate = note.has(NoteColumns.MODIFIED_DATE) ? note - .getLong(NoteColumns.MODIFIED_DATE) : System.currentTimeMillis(); - if (mIsCreate || mModifiedDate != modifiedDate) { - mDiffNoteValues.put(NoteColumns.MODIFIED_DATE, modifiedDate); - } - mModifiedDate = modifiedDate; - - long parentId = note.has(NoteColumns.PARENT_ID) ? note - .getLong(NoteColumns.PARENT_ID) : 0; - if (mIsCreate || mParentId != parentId) { - mDiffNoteValues.put(NoteColumns.PARENT_ID, parentId); - } - mParentId = parentId; - - String snippet = note.has(NoteColumns.SNIPPET) ? note - .getString(NoteColumns.SNIPPET) : ""; - if (mIsCreate || !mSnippet.equals(snippet)) { - mDiffNoteValues.put(NoteColumns.SNIPPET, snippet); - } - mSnippet = snippet; - - int type = note.has(NoteColumns.TYPE) ? note.getInt(NoteColumns.TYPE) - : Notes.TYPE_NOTE; - if (mIsCreate || mType != type) { - mDiffNoteValues.put(NoteColumns.TYPE, type); - } - mType = type; - - int widgetId = note.has(NoteColumns.WIDGET_ID) ? note.getInt(NoteColumns.WIDGET_ID) - : AppWidgetManager.INVALID_APPWIDGET_ID; - if (mIsCreate || mWidgetId != widgetId) { - mDiffNoteValues.put(NoteColumns.WIDGET_ID, widgetId); - } - mWidgetId = widgetId; - - int widgetType = note.has(NoteColumns.WIDGET_TYPE) ? note - .getInt(NoteColumns.WIDGET_TYPE) : Notes.TYPE_WIDGET_INVALIDE; - if (mIsCreate || mWidgetType != widgetType) { - mDiffNoteValues.put(NoteColumns.WIDGET_TYPE, widgetType); - } - mWidgetType = widgetType; - - long originParent = note.has(NoteColumns.ORIGIN_PARENT_ID) ? note - .getLong(NoteColumns.ORIGIN_PARENT_ID) : 0; - if (mIsCreate || mOriginParent != originParent) { - mDiffNoteValues.put(NoteColumns.ORIGIN_PARENT_ID, originParent); - } - mOriginParent = originParent; - - for (int i = 0; i < dataArray.length(); i++) { - JSONObject data = dataArray.getJSONObject(i); - SqlData sqlData = null; - 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; - } - return true; - } - - public JSONObject getContent() { - try { - JSONObject js = new JSONObject(); - - if (mIsCreate) { - Log.e(TAG, "it seems that we haven't created this in database yet"); - return null; - } - - JSONObject note = new JSONObject(); - if (mType == Notes.TYPE_NOTE) { - note.put(NoteColumns.ID, mId); - note.put(NoteColumns.ALERTED_DATE, mAlertDate); - note.put(NoteColumns.BG_COLOR_ID, mBgColorId); - note.put(NoteColumns.CREATED_DATE, mCreatedDate); - note.put(NoteColumns.HAS_ATTACHMENT, mHasAttachment); - note.put(NoteColumns.MODIFIED_DATE, mModifiedDate); - note.put(NoteColumns.PARENT_ID, mParentId); - note.put(NoteColumns.SNIPPET, mSnippet); - note.put(NoteColumns.TYPE, mType); - note.put(NoteColumns.WIDGET_ID, mWidgetId); - note.put(NoteColumns.WIDGET_TYPE, mWidgetType); - note.put(NoteColumns.ORIGIN_PARENT_ID, mOriginParent); - js.put(GTaskStringUtils.META_HEAD_NOTE, note); - - JSONArray dataArray = new JSONArray(); - for (SqlData sqlData : mDataList) { - JSONObject data = sqlData.getContent(); - if (data != null) { - dataArray.put(data); - } - } - js.put(GTaskStringUtils.META_HEAD_DATA, dataArray); - } else if (mType == Notes.TYPE_FOLDER || mType == Notes.TYPE_SYSTEM) { - note.put(NoteColumns.ID, mId); - note.put(NoteColumns.TYPE, mType); - note.put(NoteColumns.SNIPPET, mSnippet); - js.put(GTaskStringUtils.META_HEAD_NOTE, note); - } - - return js; - } catch (JSONException e) { - Log.e(TAG, e.toString()); - e.printStackTrace(); - } - return null; - } - - public void setParentId(long id) { - mParentId = id; - mDiffNoteValues.put(NoteColumns.PARENT_ID, id); - } - - public void setGtaskId(String gid) { - mDiffNoteValues.put(NoteColumns.GTASK_ID, gid); - } - - public void setSyncId(long syncId) { - mDiffNoteValues.put(NoteColumns.SYNC_ID, syncId); - } - - public void resetLocalModified() { - mDiffNoteValues.put(NoteColumns.LOCAL_MODIFIED, 0); - } - - public long getId() { - return mId; - } - - public long getParentId() { - return mParentId; - } - - public String getSnippet() { - return mSnippet; - } - - public boolean isNoteType() { - return mType == Notes.TYPE_NOTE; - } - - public void commit(boolean validateVersion) { - if (mIsCreate) { - if (mId == INVALID_ID && mDiffNoteValues.containsKey(NoteColumns.ID)) { - mDiffNoteValues.remove(NoteColumns.ID); - } - - Uri uri = mContentResolver.insert(Notes.CONTENT_NOTE_URI, mDiffNoteValues); - try { - mId = Long.valueOf(uri.getPathSegments().get(1)); - } catch (NumberFormatException e) { - Log.e(TAG, "Get note id error :" + e.toString()); - throw new ActionFailureException("create note failed"); - } - if (mId == 0) { - throw new IllegalStateException("Create thread id failed"); - } - - if (mType == Notes.TYPE_NOTE) { - for (SqlData sqlData : mDataList) { - sqlData.commit(mId, false, -1); - } - } - } else { - if (mId <= 0 && mId != Notes.ID_ROOT_FOLDER && mId != Notes.ID_CALL_RECORD_FOLDER) { - Log.e(TAG, "No such note"); - throw new IllegalStateException("Try to update note with invalid id"); - } - if (mDiffNoteValues.size() > 0) { - mVersion++; - int result = 0; - if (!validateVersion) { - result = mContentResolver.update(Notes.CONTENT_NOTE_URI, mDiffNoteValues, "(" - + NoteColumns.ID + "=?)", - new String[] { - String.valueOf(mId) - }); - } else { - result = mContentResolver.update(Notes.CONTENT_NOTE_URI, mDiffNoteValues, "(" - + NoteColumns.ID + "=?) AND (" + NoteColumns.VERSION + "<=?)", - new String[] { - String.valueOf(mId), String.valueOf(mVersion) - }); - } - if (result == 0) { - Log.w(TAG, "there is no update. maybe user updates note when syncing"); - } - } - - if (mType == Notes.TYPE_NOTE) { - for (SqlData sqlData : mDataList) { - sqlData.commit(mId, validateVersion, mVersion); - } - } - } - - // refresh local info - loadFromCursor(mId); - if (mType == Notes.TYPE_NOTE) - loadDataContent(); - - mDiffNoteValues.clear(); - mIsCreate = false; - } -} diff --git a/other/07_何越_实践总结报告.docx b/other/07_何越_实践总结报告.docx deleted file mode 100644 index c879760..0000000 Binary files a/other/07_何越_实践总结报告.docx and /dev/null differ diff --git a/other/07_凌子杰_实践总结报告.docx b/other/07_凌子杰_实践总结报告.docx deleted file mode 100644 index ec99fa4..0000000 Binary files a/other/07_凌子杰_实践总结报告.docx and /dev/null differ diff --git a/other/07_刘峻豪_实践总结报告.docx b/other/07_刘峻豪_实践总结报告.docx deleted file mode 100644 index 2585b39..0000000 Binary files a/other/07_刘峻豪_实践总结报告.docx and /dev/null differ diff --git a/other/07_江一 阳_实践总结报告.docx b/other/07_江一 阳_实践总结报告.docx deleted file mode 100644 index 699c3a9..0000000 Binary files a/other/07_江一 阳_实践总结报告.docx and /dev/null differ diff --git a/other/07_陈七源_代码标注/model/Note.java b/other/07_陈七源_代码标注/model/Note.java deleted file mode 100644 index 25eba68..0000000 --- a/other/07_陈七源_代码标注/model/Note.java +++ /dev/null @@ -1,456 +0,0 @@ -/* - * 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.model; -import android.content.ContentProviderOperation; -import android.content.ContentProviderResult; -import android.content.ContentUris; -import android.content.ContentValues; -import android.content.Context; -import android.content.OperationApplicationException; -import android.net.Uri; -import android.os.RemoteException; -import android.util.Log; - -import net.micode.notes.data.Notes; -import net.micode.notes.data.Notes.CallNote; -import net.micode.notes.data.Notes.DataColumns; -import net.micode.notes.data.Notes.NoteColumns; -import net.micode.notes.data.Notes.TextNote; - -import java.util.ArrayList; - - -public class Note { - private ContentValues mNoteDiffValues; - private NoteData mNoteData; - private static final String TAG = "Note"; - /** - * 获取新笔记的唯一标识符 - * @param context 上下文对象 - * @param folderId 新建笔记所属文件夹的id - * @return 新建笔记的唯一标识符 - */ - public static synchronized long getNewNoteId(Context context, long folderId) { - // 创建一个新的ContentValues对象,并设置相关键值对 - ContentValues values = new ContentValues(); - long createdTime = System.currentTimeMillis(); // 获取当前时间作为创建时间 - values.put(NoteColumns.CREATED_DATE, createdTime); // 存入创建时间 - values.put(NoteColumns.MODIFIED_DATE, createdTime); // 存入修改时间(初始与创建时间相同) - values.put(NoteColumns.TYPE, Notes.TYPE_NOTE); // 存入笔记类型 - values.put(NoteColumns.LOCAL_MODIFIED, 1); // 存入本地修改状态 - values.put(NoteColumns.PARENT_ID, folderId); // 存入所属文件夹id - - // 向数据库中插入新笔记数据,并获取包含新建笔记Uri的对象uri - Uri uri = context.getContentResolver().insert(Notes.CONTENT_NOTE_URI, values); - - long noteId = 0; - try { - // 从uri中获得新建笔记的id,即路径segments的第二个元素,并将其转换为long类型返回 - noteId = Long.valueOf(uri.getPathSegments().get(1)); - } catch (NumberFormatException e) { - // 如果获取不到这个id或者它等于-1,则会抛出相应的异常 - Log.e(TAG, "Get note id error :" + e.toString()); - noteId = 0; - } - if (noteId == -1) { - throw new IllegalStateException("Wrong note id:" + noteId); - } - return noteId; - } - - /** - * Note类的构造函数,用于初始化对象 - * 该代码定义了一个名为"Note"的类的构造函数。这个类可能是一个笔记应用程序的一部分,用于表示一个笔记。 - */ - public Note() { - // 创建一个ContentValues对象,用于保存笔记中改变的值,即笔记已经被修改但还没同步到数据库 - mNoteDiffValues = new ContentValues(); - // 创建一个NoteData对象,用于保存笔记的所有数据,包括标题、正文、颜色等 - mNoteData = new NoteData(); - } - - - /** - * 设置笔记的key-value值 - * @param key 键名 - * @param value 键值 - * - * 在构造函数中,首先创建一个空的ContentValues对象mNoteDiffValues,用于保存笔记中改变的值,因为当我们修改笔记后并不会立即同步到数据库,而是需要等待一段时间或者手动进行同步。接着创建一个NoteData对象mNoteData,用于保存笔记的所有数据,包括标题、正文、颜色等。 - * - * - */ - public void setNoteValue(String key, String value) { - // 向ContentValues对象中添加键值对 - mNoteDiffValues.put(key, value); - // 将本地修改状态设置为1,表示笔记已经被修改但还没同步到数据库 - mNoteDiffValues.put(NoteColumns.LOCAL_MODIFIED, 1); - // 将修改时间设置为当前时间 - mNoteDiffValues.put(NoteColumns.MODIFIED_DATE, System.currentTimeMillis()); - } - - - /** - * 设置笔记文本数据 - * - * @param key 一个字符串类型的键,用于标识要设置的文本数据类型。例如:"title"、"content"等。 - * @param value 一个字符串类型的值,表示要设置的文本数据内容。例如:标题或正文内容等。 - * - * 在构造函数中,首先创建一个空的ContentValues对象mNoteDiffValues,用于保存笔记中改变的值。因为当我们修改笔记后并不会立即同步到数据库,而是需要等待一段时间或者手动进行同步。接着创建一个NoteData对象mNoteData,用于保存笔记的所有数据,包括标题、正文、颜色等。 - */ - public void setTextData(String key, String value) { - // 调用mNoteData对象的setTextData方法,将指定键(key)和值(value)添加到笔记的文本数据中 - mNoteData.setTextData(key, value); - } - - /** - * 设置笔记文本数据id - * - * @param id 一个长整型的值,表示要设置的笔记文本数据的id。 - * - * 该函数用于将指定的长整型数值作为笔记文本数据的id进行设置。 - * mNoteData是在类构造函数中创建的NoteData对象,通过调用它的setTextDataId方法,可以设置笔记的文本数据id。 - */ - public void setTextDataId(long id) { - // 调用mNoteData对象的setTextDataId方法,将指定的长整型数值作为笔记文本数据的id进行设置 - mNoteData.setTextDataId(id); - } - - - - /** - * 获取笔记文本数据id - * - * @return 一个长整型的值,表示当前笔记文本数据的id。 - * - * 该函数用于从当前Note对象中获取保存的笔记文本数据的id。 - * mNoteData是在类构造函数中创建的NoteData对象,其中mTextDataId是NoteData类成员变量,用于保存笔记文本数据的id。 - */ - public long getTextDataId() { - // 返回mNoteData对象中保存的笔记文本数据的id - return mNoteData.mTextDataId; - } - - - /** - * 设置笔记的通话记录id - * - * @param id 一个长整型的值,表示要设置的笔记通话记录的id。 - * - * 该函数用于将指定的长整型数值作为笔记的通话记录id进行设置。 - * mNoteData是在类构造函数中创建的NoteData对象,通过调用它的setCallDataId方法,可以设置笔记的通话记录id。 - */ - public void setCallDataId(long id) { - // 调用mNoteData对象的setCallDataId方法,将指定的长整型数值作为笔记的通话记录id进行设置 - mNoteData.setCallDataId(id); - } - - - /** - * 设置笔记通话记录数据 - * - * @param key 一个字符串类型的键,用于标识要设置的通话记录数据类型。例如:"name"、"number"等。 - * @param value 一个字符串类型的值,表示要设置的通话记录数据内容。例如:联系人姓名或电话号码等。 - * - * 该函数用于向当前Note对象中添加指定的通话记录数据。 - * mNoteData是在类构造函数中创建的NoteData对象,通过调用它的setCallData方法,可以将指定的键/值对添加到笔记的通话记录数据中。 - */ - public void setCallData(String key, String value) { - // 调用mNoteData对象的setCallData方法,将指定键(key)和值(value)添加到笔记的通话记录数据中 - mNoteData.setCallData(key, value); - } - - - /** - * 检查笔记是否已经被本地修改 - * - * @return 一个布尔值,表示当前笔记是否已经被本地修改。 - * - * 该函数用于检查当前Note对象是否已经被本地修改。它会检查mNoteDiffValues和mNoteData对象, - * 如果它们中的任意一个在本地被修改过,则返回true;否则返回false。 - */ - public boolean isLocalModified() { - // 判断mNoteDiffValues对象是否包含数据,或者mNoteData对象中是否有数据被本地修改。 - return mNoteDiffValues.size() > 0 || mNoteData.isLocalModified(); - } - - /** - * 同步笔记数据到本地数据库 - * - * @param context 一个Context对象,用于访问应用程序的内容提供器。 - * @param noteId 一个长整型的值,表示要同步的笔记ID。 - * - * @return 一个布尔值,表示笔记是否成功同步到本地数据库。 - * - * 该函数用于将当前Note对象保存到本地数据库中。如果当前Note对象没有被本地修改,则直接返回true; - * 否则,使用ContentResolver对象将mNoteDiffValues中保存的数值更新到NoteColumns.LOCAL_MODIFIED和NoteColumns.MODIFIED_DATE字段上。 - * 如果更新失败,则在日志中记录错误信息,并返回false;否则,清空mNoteDiffValues中的数据,并将mNoteData中的数据保存到本地数据库中。 - * 如果保存失败,则返回false;否则,返回true。 - */ - public boolean syncNote(Context context, long noteId) { - // 检查noteId参数是否合法,如果小于等于0,则抛出IllegalArgumentException异常 - if (noteId <= 0) { - throw new IllegalArgumentException("Wrong note id:" + noteId); - } - - // 如果当前Note对象没有被本地修改,则直接返回true - if (!isLocalModified()) { - return true; - } - - // 使用ContentResolver对象将mNoteDiffValues中保存的数值更新到NoteColumns.LOCAL_MODIFIED和NoteColumns.MODIFIED_DATE字段上 - if (context.getContentResolver().update( - ContentUris.withAppendedId(Notes.CONTENT_NOTE_URI, noteId), mNoteDiffValues, null, - null) == 0) { - // 如果更新失败,则在日志中记录错误信息 - Log.e(TAG, "Update note error, should not happen"); - // Do not return, fall through - } - - // 清空mNoteDiffValues中的数据 - mNoteDiffValues.clear(); - - // 将mNoteData中的数据保存到本地数据库中 - if (mNoteData.isLocalModified() - && (mNoteData.pushIntoContentResolver(context, noteId) == null)) { - // 如果保存失败,则返回false - return false; - } - - // 如果保存成功,则返回true - return true; - } - - - private class NoteData { - private long mTextDataId; - - private ContentValues mTextDataValues; - - private long mCallDataId; - - private ContentValues mCallDataValues; - - private static final String TAG = "NoteData"; - - /** - * 构造函数 - * - * 该函数用于创建一个新的NoteData对象,并初始化mTextDataValues、mCallDataValues、mTextDataId和mCallDataId成员变量。 - * mTextDataValues和mCallDataValues对象分别用于保存笔记的文本数据和通话记录数据。 - * mTextDataId和mCallDataId分别表示当前笔记的文本数据ID和通话记录数据ID,初始值为0。 - */ - public NoteData() { - // 创建一个新的ContentValues对象mTextDataValues,用于保存笔记的文本数据 - mTextDataValues = new ContentValues(); - // 创建一个新的ContentValues对象mCallDataValues,用于保存笔记的通话记录数据 - mCallDataValues = new ContentValues(); - // 将mTextDataId和mCallDataId的初始值设置为0 - mTextDataId = 0; - mCallDataId = 0; - } - - - /** - * 检查笔记数据是否已经被本地修改 - * - * @return 一个布尔值,表示当前笔记数据是否已经被本地修改。 - * - * 该函数用于检查当前NoteData对象是否已经被本地修改。它会检查mTextDataValues和mCallDataValues对象, - * 如果它们中的任意一个在本地被修改过,则返回true;否则返回false。 - */ - boolean isLocalModified() { - // 判断mTextDataValues和mCallDataValues对象是否包含数据,如果有数据被本地修改过,则返回true - return mTextDataValues.size() > 0 || mCallDataValues.size() > 0; - } - - /** - * 设置笔记的文本数据id - * - * @param id 一个长整型的值,表示要设置的笔记文本数据的id。 - * - * 该函数用于将指定的长整型数值作为笔记的文本数据id进行设置。如果指定的id小于等于0,则抛出IllegalArgumentException异常。 - * mTextDataId是NoteData类成员变量,用于保存笔记文本数据的ID。 - */ - void setTextDataId(long id) { - // 如果指定的id小于等于0,则抛出IllegalArgumentException异常 - if(id <= 0) { - throw new IllegalArgumentException("Text data id should larger than 0"); - } - // 将指定的长整型数值作为笔记的文本数据id进行设置 - mTextDataId = id; - } - - - /** - * 设置笔记的通话记录数据id - * - * @param id 一个长整型的值,表示要设置的笔记通话记录数据的id。 - * - * 该函数用于将指定的长整型数值作为笔记的通话记录数据id进行设置。如果指定的id小于等于0, - * 则抛出IllegalArgumentException异常。 - * mCallDataId是NoteData类成员变量,用于保存笔记的通话记录数据ID。 - */ - void setCallDataId(long id) { - // 如果指定的id小于等于0,则抛出IllegalArgumentException异常 - if (id <= 0) { - throw new IllegalArgumentException("Call data id should larger than 0"); - } - // 将指定的长整型数值作为笔记的通话记录数据id进行设置 - mCallDataId = id; - } - - - /** - * 设置笔记的通话记录数据 - * - * @param key 一个字符串,表示要设置的通话记录数据的键。 - * @param value 一个字符串,表示要设置的通话记录数据的值。 - * - * 该函数用于将指定的字符串作为笔记的通话记录数据进行设置。它会将键值对保存到mCallDataValues对象中,并将NoteColumns.LOCAL_MODIFIED和NoteColumns.MODIFIED_DATE字段的值更新为1和当前时间戳, - * 表示笔记已被本地修改过。 - */ - void setCallData(String key, String value) { - // 将键值对保存到mCallDataValues对象中 - mCallDataValues.put(key, value); - // 将NoteColumns.LOCAL_MODIFIED字段的值设置为1,表示笔记已被本地修改过 - mNoteDiffValues.put(NoteColumns.LOCAL_MODIFIED, 1); - // 将NoteColumns.MODIFIED_DATE字段的值更新为当前时间戳,表示笔记最近一次被修改的时间 - mNoteDiffValues.put(NoteColumns.MODIFIED_DATE, System.currentTimeMillis()); - } - - - /** - * 设置笔记的文本数据 - * - * @param key 一个字符串,表示要设置的文本数据的键。 - * @param value 一个字符串,表示要设置的文本数据的值。 - * - * 该函数用于将指定的字符串作为笔记的文本数据进行设置。它会将键值对保存到mTextDataValues对象中,并将NoteColumns.LOCAL_MODIFIED和NoteColumns.MODIFIED_DATE字段的值更新为1和当前时间戳, - * 表示笔记已被本地修改过。 - */ - void setTextData(String key, String value) { - // 将键值对保存到mTextDataValues对象中 - mTextDataValues.put(key, value); - // 将NoteColumns.LOCAL_MODIFIED字段的值设置为1,表示笔记已被本地修改过 - mNoteDiffValues.put(NoteColumns.LOCAL_MODIFIED, 1); - // 将NoteColumns.MODIFIED_DATE字段的值更新为当前时间戳,表示笔记最近一次被修改的时间 - mNoteDiffValues.put(NoteColumns.MODIFIED_DATE, System.currentTimeMillis()); - } - - - /** - * 将笔记数据插入ContentResolver中 - * - * @param context Context对象,表示当前应用程序的上下文环境。 - * @param noteId 一个长整型数值,表示要插入的笔记的id。 - * - * @return 返回插入成功的Uri对象,如果插入失败则返回null。 - * - * 该函数用于将当前NoteData对象保存到ContentResolver中。它会将mTextDataValues和mCallDataValues对象中的数据保存到ContentResolver中,并更新NoteColumns.LOCAL_MODIFIED和NoteColumns.MODIFIED_DATE字段的值为0和当前时间戳, - * 表示笔记已被同步到云端。 - */ - Uri pushIntoContentResolver(Context context, long noteId) { - /** - * 检查noteId是否合法 - */ - if (noteId <= 0) { - throw new IllegalArgumentException("Wrong note id:" + noteId); - } - - // 初始化operationList列表和builder对象 - ArrayList operationList = new ArrayList(); - ContentProviderOperation.Builder builder = null; - - // 如果mTextDataValues对象不为空,则将其中的数据保存到ContentResolver中 - if(mTextDataValues.size() > 0) { - // 为mTextDataValues对象添加笔记id - mTextDataValues.put(DataColumns.NOTE_ID, noteId); - if (mTextDataId == 0) { - // 如果mTextDataId为0,则表明当前笔记没有文本数据,需要进行插入操作 - mTextDataValues.put(DataColumns.MIME_TYPE, TextNote.CONTENT_ITEM_TYPE); - // 通过ContentResolver插入数据,并获取插入的Uri对象 - Uri uri = context.getContentResolver().insert(Notes.CONTENT_DATA_URI, - mTextDataValues); - try { - // 解析Uri中的id值,并将其设置为mTextDataId - setTextDataId(Long.valueOf(uri.getPathSegments().get(1))); - } catch (NumberFormatException e) { - Log.e(TAG, "Insert new text data fail with noteId" + noteId); - // 插入失败,清空mTextDataValues,并返回null - mTextDataValues.clear(); - return null; - } - } else { - // 如果mTextDataId不为0,则表明当前笔记已经有文本数据,需要进行更新操作 - builder = ContentProviderOperation.newUpdate(ContentUris.withAppendedId( - Notes.CONTENT_DATA_URI, mTextDataId)); - builder.withValues(mTextDataValues); - operationList.add(builder.build()); - } - // 清空mTextDataValues对象 - mTextDataValues.clear(); - } - - // 如果mCallDataValues对象不为空,则将其中的数据保存到ContentResolver中 - if(mCallDataValues.size() > 0) { - // 为mCallDataValues对象添加笔记id - mCallDataValues.put(DataColumns.NOTE_ID, noteId); - if (mCallDataId == 0) { - // 如果mCallDataId为0,则表明当前笔记没有通话记录数据,需要进行插入操作 - mCallDataValues.put(DataColumns.MIME_TYPE, CallNote.CONTENT_ITEM_TYPE); - // 通过ContentResolver插入数据,并获取插入的Uri对象 - Uri uri = context.getContentResolver().insert(Notes.CONTENT_DATA_URI, - mCallDataValues); - try { - // 解析Uri中的id值,并将其设置为mCallDataId - setCallDataId(Long.valueOf(uri.getPathSegments().get(1))); - } catch (NumberFormatException e) { - Log.e(TAG, "Insert new call data fail with noteId" + noteId); - // 插入失败,清空mCallDataValues,并返回null - mCallDataValues.clear(); - return null; - } - } else { - // 如果mCallDataId不为0,则表明当前笔记已经有通话记录数据,需要进行更新操作 - builder = ContentProviderOperation.newUpdate(ContentUris.withAppendedId( - Notes.CONTENT_DATA_URI, mCallDataId)); - builder.withValues(mCallDataValues); - operationList.add(builder.build()); - } - // 清空mCallDataValues对象 - mCallDataValues.clear(); - } - if (operationList.size() > 0) { // 如果操作列表中有操作 - try { - // 调用 ContentResolver 的 applyBatch 方法执行操作 - ContentProviderResult[] results = context.getContentResolver().applyBatch( - Notes.AUTHORITY, operationList); - // 判断操作是否成功,并返回对应的 Uri - return (results == null || results.length == 0 || results[0] == null) ? null - : ContentUris.withAppendedId(Notes.CONTENT_NOTE_URI, noteId); - } catch (RemoteException e) { // 捕获 RemoteException 异常 - Log.e(TAG, String.format("%s: %s", e.toString(), e.getMessage())); // 记录异常信息到日志 - return null; // 返回 null - } catch (OperationApplicationException e) { // 捕获 OperationApplicationException 异常 - Log.e(TAG, String.format("%s: %s", e.toString(), e.getMessage())); // 记录异常信息到日志 - return null; // 返回 null - } - } - return null; // 如果操作列表为空,则返回 null - } - } -} diff --git a/other/07_陈七源_代码标注/model/WorkingNote.java b/other/07_陈七源_代码标注/model/WorkingNote.java deleted file mode 100644 index 683df72..0000000 --- a/other/07_陈七源_代码标注/model/WorkingNote.java +++ /dev/null @@ -1,629 +0,0 @@ -/* - * 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.model; - -import android.appwidget.AppWidgetManager; -import android.content.ContentUris; -import android.content.Context; -import android.database.Cursor; -import android.text.TextUtils; -import android.util.Log; - -import net.micode.notes.data.Notes; -import net.micode.notes.data.Notes.CallNote; -import net.micode.notes.data.Notes.DataColumns; -import net.micode.notes.data.Notes.DataConstants; -import net.micode.notes.data.Notes.NoteColumns; -import net.micode.notes.data.Notes.TextNote; -import net.micode.notes.tool.ResourceParser.NoteBgResources; - - -public class WorkingNote { - // Note for the working note - private Note mNote; - // Note Id - private long mNoteId; - // Note content - private String mContent; - // Note mode - private int mMode; - - private long mAlertDate; - - private long mModifiedDate; - - private int mBgColorId; - - private int mWidgetId; - - private int mWidgetType; - - private long mFolderId; - - private Context mContext; - - private static final String TAG = "WorkingNote"; - - private boolean mIsDeleted; - - private NoteSettingChangedListener mNoteSettingStatusListener; - - public static final String[] DATA_PROJECTION = new String[] { - DataColumns.ID, // 表示数据行的 ID - DataColumns.CONTENT, // 表示数据行的内容 - DataColumns.MIME_TYPE, // 表示数据行的 MIME 类型 - DataColumns.DATA1, // 表示数据行的附加数据 - DataColumns.DATA2, // 表示数据行的附加数据 - DataColumns.DATA3, // 表示数据行的附加数据 - DataColumns.DATA4, // 表示数据行的附加数据 - }; - public static final String[] NOTE_PROJECTION = new String[] { - NoteColumns.PARENT_ID, // 表示笔记的父笔记 ID - NoteColumns.ALERTED_DATE, // 表示笔记的提醒时间 - NoteColumns.BG_COLOR_ID, // 表示笔记的背景颜色 ID - NoteColumns.WIDGET_ID, // 表示笔记的小部件 ID - NoteColumns.WIDGET_TYPE, // 表示笔记的小部件类型 - NoteColumns.MODIFIED_DATE // 表示笔记的修改时间 - }; - - private static final int DATA_ID_COLUMN = 0; - - private static final int DATA_CONTENT_COLUMN = 1; - - private static final int DATA_MIME_TYPE_COLUMN = 2; - - private static final int DATA_MODE_COLUMN = 3; - - private static final int NOTE_PARENT_ID_COLUMN = 0; - - private static final int NOTE_ALERTED_DATE_COLUMN = 1; - - private static final int NOTE_BG_COLOR_ID_COLUMN = 2; - - private static final int NOTE_WIDGET_ID_COLUMN = 3; - - private static final int NOTE_WIDGET_TYPE_COLUMN = 4; - - private static final int NOTE_MODIFIED_DATE_COLUMN = 5; - - // New note construct - /** - * 构造方法,创建一个新的 WorkingNote 对象。 - * @param context 上下文对象 - * @param folderId 笔记所属文件夹的 ID - */ - private WorkingNote(Context context, long folderId) { - // 初始化成员变量 - mContext = context; // 上下文对象 - mAlertDate = 0; // 提醒时间 - mModifiedDate = System.currentTimeMillis(); // 修改时间,初始化为当前时间戳 - mFolderId = folderId; // 笔记所属文件夹的 ID - mNote = new Note(); // Note 对象 - mNoteId = 0; // 笔记 ID,初始值为 0 - mIsDeleted = false; // 是否已删除,初始值为 false - mMode = 0; // 模式,初始值为 0 - mWidgetType = Notes.TYPE_WIDGET_INVALIDE; // 小部件类型,初始值为无效类型 - } - - - // Existing note construct - /** - * 构造方法,创建一个新的 WorkingNote 对象。 - * @param context 上下文对象 - * @param noteId 笔记的ID - * @param folderId 笔记所属文件夹的 ID - */ - private WorkingNote(Context context, long noteId, long folderId) { - // 初始化成员变量 - mContext = context; // 上下文对象 - mNoteId = noteId; // 笔记 ID - mFolderId = folderId; // 笔记所属文件夹的 ID - mIsDeleted = false; // 是否已删除,初始值为 false - mNote = new Note(); // Note 对象 - loadNote(); // 加载笔记数据 - } - - - /** - * 从内容提供程序中使用当前笔记 ID 加载笔记的详细信息。 - * 查询 CONTENT_NOTE_URI,检索与指定笔记 ID 相对应的记录。 - * 如果游标不为空并且可以将其移动到第一个结果,则结果将填充 mFolderId、mBgColorId、 - * mWidgetId、mWidgetType、mAlertDate 和 mModifiedDate 变量。 - * 然后关闭游标并调用 loadNoteData() 方法以加载笔记剩余部分的数据。 - * 如果没有检索到任何记录,则在打印错误日志后抛出 IllegalArgumentException 异常。 - */ - private void loadNote() { - Cursor cursor = mContext.getContentResolver().query( - ContentUris.withAppendedId(Notes.CONTENT_NOTE_URI, mNoteId), - NOTE_PROJECTION, - null, - null, - null); - - if (cursor != null) { - if (cursor.moveToFirst()) { - // 结果将填充以下变量:mFolderId、mBgColorId、mWidgetId、mWidgetType、mAlertDate 和 mModifiedDate - mFolderId = cursor.getLong(NOTE_PARENT_ID_COLUMN); - mBgColorId = cursor.getInt(NOTE_BG_COLOR_ID_COLUMN); - mWidgetId = cursor.getInt(NOTE_WIDGET_ID_COLUMN); - mWidgetType = cursor.getInt(NOTE_WIDGET_TYPE_COLUMN); - mAlertDate = cursor.getLong(NOTE_ALERTED_DATE_COLUMN); - mModifiedDate = cursor.getLong(NOTE_MODIFIED_DATE_COLUMN); - } - cursor.close(); - } else { - // 没有检索到任何记录,则在打印错误日志后抛出 IllegalArgumentException 异常 - Log.e(TAG, "No note with id:" + mNoteId); - throw new IllegalArgumentException("Unable to find note with id " + mNoteId); - } - // 加载笔记剩余部分的数据 - loadNoteData(); - } - - - /** - * 加载笔记数据。 - * - * 该方法用于从Content Provider中加载当前Note对象的数据。它会查询Notes.CONTENT_DATA_URI,条件为 - * DataColumns.NOTE_ID等于mNoteId,如果存在符合条件的数据,则将其加载到Note对象中。 - * 如果在查询时出现错误(例如Content Provider不存在或无法工作、指定的笔记ID无效等),则会抛出IllegalArgumentException异常。 - */ - private void loadNoteData() { - Cursor cursor = mContext.getContentResolver().query(Notes.CONTENT_DATA_URI, DATA_PROJECTION, - DataColumns.NOTE_ID + "=?", new String[] { String.valueOf(mNoteId) }, null); - - if (cursor != null) { - if (cursor.moveToFirst()) { - // 遍历所有数据并将它们加载到Note对象中。 - do { - String type = cursor.getString(DATA_MIME_TYPE_COLUMN); - if (DataConstants.NOTE.equals(type)) { - mContent = cursor.getString(DATA_CONTENT_COLUMN); - mMode = cursor.getInt(DATA_MODE_COLUMN); - mNote.setTextDataId(cursor.getLong(DATA_ID_COLUMN)); - } else if (DataConstants.CALL_NOTE.equals(type)) { - mNote.setCallDataId(cursor.getLong(DATA_ID_COLUMN)); - } else { - Log.d(TAG, "Wrong note type with type:" + type); - } - } while (cursor.moveToNext()); - } - cursor.close(); - } else { - // 如果未找到任何数据,则抛出异常。 - Log.e(TAG, "No data with id:" + mNoteId); - throw new IllegalArgumentException("Unable to find note's data with id " + mNoteId); - } - } - - - /** - * 创建一个空的笔记对象。 - * - * 该方法用于创建一个空白的WorkingNote对象,并设置其初始值。它接受context、folderId、widgetId、 - * widgetType和defaultBgColorId等参数作为输入,并返回一个新的WorkingNote对象。 - * - * @param context 上下文环境 - * @param folderId 笔记所在文件夹的ID - * @param widgetId 笔记所绑定的Widget的ID(如果有) - * @param widgetType 笔记所绑定的Widget的类型(例如便签、待办事项等) - * @param defaultBgColorId 笔记默认的背景颜色ID - * @return 返回一个新的WorkingNote对象 - */ - public static WorkingNote createEmptyNote(Context context, long folderId, int widgetId, - int widgetType, int defaultBgColorId) { - // 创建一个空白的WorkingNote对象 - WorkingNote note = new WorkingNote(context, folderId); - // 设置笔记的默认背景颜色 - note.setBgColorId(defaultBgColorId); - // 设置笔记所绑定的Widget的ID - note.setWidgetId(widgetId); - // 设置笔记所绑定的Widget的类型 - note.setWidgetType(widgetType); - // 返回一个新的WorkingNote对象 - return note; - } - - - /** - * 从数据库中加载一个WorkingNote对象。 - * - * 该方法用于从数据库中加载一个指定ID的WorkingNote对象。它接受context和id等参数作为输入,并返回 - * 一个新的WorkingNote对象。 - * - * @param context 上下文环境 - * @param id 待加载的WorkingNote对象的ID - * @return 返回一个新的WorkingNote对象 - */ - public static WorkingNote load(Context context, long id) { - // 创建一个新的WorkingNote对象,并将id和0作为参数传入构造函数中 - return new WorkingNote(context, id, 0); - } - - - /** - * 保存笔记对象。 - * - * 该方法用于将当前WorkingNote对象(如果有必要)保存到数据库中。如果该笔记尚未存在于数据库中, - * 则会先创建一个新的笔记记录。它还会更新绑定在该笔记上的Widget内容(如果该笔记绑定了Widget)。 - * - * @return 如果笔记成功保存,则返回true;否则返回false - */ - public synchronized boolean saveNote() { - // 检查该笔记是否值得被保存 - if (isWorthSaving()) { - // 如果该笔记尚未存在于数据库中,则需要先创建一个新的笔记记录。 - if (!existInDatabase()) { - // 获取一个新的笔记ID - if ((mNoteId = Note.getNewNoteId(mContext, mFolderId)) == 0) { - Log.e(TAG, "Create new note fail with id:" + mNoteId); - return false; - } - } - - // 将当前WorkingNote对象同步到Note对象中 - mNote.syncNote(mContext, mNoteId); - - /** - * 如果该笔记绑定了Widget,则需要更新Widget的内容。 - */ - if (mWidgetId != AppWidgetManager.INVALID_APPWIDGET_ID - && mWidgetType != Notes.TYPE_WIDGET_INVALIDE - && mNoteSettingStatusListener != null) { - mNoteSettingStatusListener.onWidgetChanged(); - } - - return true; - } else { - return false; - } - } - - - /** - * 检查当前WorkingNote对象是否存在于数据库中。 - * - * 该方法用于检查当前WorkingNote对象是否已经存在于数据库中。如果该笔记已经保存过,则existInDatabase() - * 方法会返回true;否则返回false。 - * - * @return 如果笔记已经存在于数据库中,则返回true;否则返回false - */ - public boolean existInDatabase() { - return mNoteId > 0; - } - - /** - * 判断笔记是否值得被保存。 - * - * 该方法用于判断当前WorkingNote对象是否值得被保存到数据库中。它会根据笔记内容、是否被删除以及 - * 是否已经保存到数据库等因素做出判断。如果该笔记不需要被保存,则isWorthSaving()方法会返回 - * false;否则返回true。 - * - * @return 如果笔记值得被保存到数据库中,则返回true;否则返回false - */ - private boolean isWorthSaving() { - if (mIsDeleted || (!existInDatabase() && TextUtils.isEmpty(mContent)) - || (existInDatabase() && !mNote.isLocalModified())) { - // 如果该笔记已经被标记为“已删除”,或者它既没有保存到数据库中,也没有任何数据,或者它已经存在于 - // 数据库中但没有被修改过,则认为该笔记不需要被保存。 - return false; - } else { - return true; - } - } - - - /** - * 设置笔记设置状态更改监听器。 - * - * 该方法用于设置一个NoteSettingChangedListener对象,以便能够在笔记设置状态发生更改时接收通知。 - * - * @param l 笔记设置状态更改监听器 - */ - public void setOnSettingStatusChangedListener(NoteSettingChangedListener l) { - mNoteSettingStatusListener = l; - } - - - /** - * 设置笔记提醒日期。 - * - * 该方法用于为当前WorkingNote对象设置一个提醒日期。它接受date和set两个参数作为输入,分别表示 - * 待设置的提醒日期和是否需要进行设置。如果待设置的提醒日期与当前笔记的提醒日期不同,则会将其更新到 - * Note对象中,并通知设置状态更改监听器。 - * - * @param date 待设置的提醒日期 - * @param set 是否需要进行设置 - */ - public void setAlertDate(long date, boolean set) { - if (date != mAlertDate) { - // 如果待设置的提醒日期与当前笔记的提醒日期不同,则需要更新Note对象中的数据。 - mAlertDate = date; - mNote.setNoteValue(NoteColumns.ALERTED_DATE, String.valueOf(mAlertDate)); - } - // 通知设置状态更改监听器。 - if (mNoteSettingStatusListener != null) { - mNoteSettingStatusListener.onClockAlertChanged(date, set); - } - } - - - /** - * 标记笔记是否被删除。 - * - * 该方法用于标记当前WorkingNote对象是否已经被删除。它接受一个mark参数作为输入,如果mark为true, - * 则表示该笔记已经被删除,否则表示该笔记未被删除。在标记笔记删除状态的同时,该方法还会检查 - * 是否存在与该笔记绑定的Widget,如果有,则需要更新Widget的内容。 - * - * @param mark 是否将当前WorkingNote对象标记为“已删除” - */ - public void markDeleted(boolean mark) { - // 标记当前WorkingNote对象是否已经被删除。 - mIsDeleted = mark; - // 检查是否存在与该笔记绑定的Widget,如果有,则需要更新Widget的内容。 - if (mWidgetId != AppWidgetManager.INVALID_APPWIDGET_ID - && mWidgetType != Notes.TYPE_WIDGET_INVALIDE - && mNoteSettingStatusListener != null) { - mNoteSettingStatusListener.onWidgetChanged(); - } - } - - - /** - * 设置笔记的背景颜色ID。 - * - * 该方法用于为当前WorkingNote对象设置一个新的背景颜色ID。它接受一个id参数作为输入,表示待设置 - * 的背景颜色ID。如果待设置的背景颜色ID与当前笔记的背景颜色ID不同,则会将其更新到Note对象中, - * 并通知设置状态更改监听器。 - * - * @param id 待设置的背景颜色ID - */ - public void setBgColorId(int id) { - if (id != mBgColorId) { - // 如果待设置的背景颜色ID与当前笔记的背景颜色ID不同,则需要更新Note对象中的数据。 - mBgColorId = id; - mNote.setNoteValue(NoteColumns.BG_COLOR_ID, String.valueOf(id)); - // 通知设置状态更改监听器。 - if (mNoteSettingStatusListener != null) { - mNoteSettingStatusListener.onBackgroundColorChanged(); - } - } - } - - /** - * 设置笔记的清单模式。 - * - * 该方法用于设置当前WorkingNote对象的清单模式。它接受一个mode参数作为输入,表示待设置的清单模式。 - * 如果当前笔记的清单模式与待设置的清单模式不同,则会将其更新到Note对象中,并通知设置状态更改监听器。 - * - * @param mode 待设置的清单模式 - */ - public void setCheckListMode(int mode) { - if (mMode != mode) { - // 如果当前笔记的清单模式与待设置的清单模式不同,则需要更新Note对象中的数据。 - mMode = mode; - mNote.setTextData(TextNote.MODE, String.valueOf(mMode)); - // 通知设置状态更改监听器。 - if (mNoteSettingStatusListener != null) { - mNoteSettingStatusListener.onCheckListModeChanged(mMode, mode); - } - } - } - - /** - * 设置笔记绑定的Widget类型。 - * - * 该方法用于设置当前WorkingNote对象绑定的Widget类型。它接受一个type参数作为输入,表示待设置的 - * Widget类型。如果当前笔记绑定的Widget类型与待设置的Widget类型不同,则会将其更新到Note对象中。 - * - * @param type 待设置的Widget类型 - */ - public void setWidgetType(int type) { - if (type != mWidgetType) { - // 如果当前笔记绑定的Widget类型与待设置的Widget类型不同,则需要更新Note对象中的数据。 - mWidgetType = type; - mNote.setNoteValue(NoteColumns.WIDGET_TYPE, String.valueOf(mWidgetType)); - } - } - - /** - * 设置笔记绑定的Widget ID。 - * - * 该方法用于设置当前WorkingNote对象绑定的Widget ID。它接受一个id参数作为输入,表示待设置的 - * Widget ID。如果当前笔记绑定的Widget ID与待设置的Widget ID不同,则会将其更新到Note对象中。 - * - * @param id 待设置的Widget ID - */ - public void setWidgetId(int id) { - if (id != mWidgetId) { - // 如果当前笔记绑定的Widget ID与待设置的Widget ID不同,则需要更新Note对象中的数据。 - mWidgetId = id; - mNote.setNoteValue(NoteColumns.WIDGET_ID, String.valueOf(mWidgetId)); - } - } - - - /** - * 设置笔记的文本内容。 - * - * 该方法用于设置当前WorkingNote对象的文本内容。它接受一个text参数作为输入,表示待设置的文本内容。 - * 如果当前笔记的文本内容与待设置的文本内容不同,则会将其更新到Note对象中。 - * - * @param text 待设置的文本内容 - */ - public void setWorkingText(String text) { - if (!TextUtils.equals(mContent, text)) { - // 如果当前笔记的文本内容与待设置的文本内容不同,则需要更新Note对象中的数据。 - mContent = text; - mNote.setTextData(DataColumns.CONTENT, mContent); - } - } - - /** - * 将笔记转换为通话记录。 - * - * 该方法用于将当前WorkingNote对象转换为通话记录。它接受一个phoneNumber和callDate两个参数作为输入, - * 分别表示通话记录的电话号码和通话日期。该方法会将笔记的类型设置为通话记录,并将相应的数据保存到Note对象中。 - * - * @param phoneNumber 通话记录的电话号码 - * @param callDate 通话记录的通话日期 - */ - public void convertToCallNote(String phoneNumber, long callDate) { - // 将笔记的类型设置为通话记录,并将相应的数据保存到Note对象中。 - mNote.setCallData(CallNote.CALL_DATE, String.valueOf(callDate)); - mNote.setCallData(CallNote.PHONE_NUMBER, phoneNumber); - mNote.setNoteValue(NoteColumns.PARENT_ID, String.valueOf(Notes.ID_CALL_RECORD_FOLDER)); - } - - - /** - * 检查笔记是否设置了提醒。 - * - * 该方法用于检查当前WorkingNote对象是否设置了提醒。如果已经设置了提醒,则返回true;否则返回false。 - * - * @return 如果笔记设置了提醒,则返回true;否则返回false - */ - public boolean hasClockAlert() { - return (mAlertDate > 0 ? true : false); - } - - /** - * 获取笔记的文本内容。 - * - * 该方法用于获取当前WorkingNote对象的文本内容。 - * - * @return 当前WorkingNote对象的文本内容 - */ - public String getContent() { - return mContent; - } - - /** - * 获取笔记的提醒日期。 - * - * 该方法用于获取当前WorkingNote对象的提醒日期。 - * - * @return 当前WorkingNote对象的提醒日期 - */ - public long getAlertDate() { - return mAlertDate; - } - - /** - * 获取笔记的修改日期。 - * - * 该方法用于获取当前WorkingNote对象的修改日期。 - * - * @return 当前WorkingNote对象的修改日期 - */ - public long getModifiedDate() { - return mModifiedDate; - } - - /** - * 获取笔记的背景颜色资源ID。 - * - * 该方法用于获取当前WorkingNote对象的背景颜色资源ID。它会根据当前笔记的背景颜色ID返回相应的颜色资源ID。 - * - * @return 当前WorkingNote对象的背景颜色资源ID - */ - public int getBgColorResId() { - return NoteBgResources.getNoteBgResource(mBgColorId); - } - - - /** - * 获取笔记的背景颜色ID。 - * - * 该方法用于获取当前WorkingNote对象的背景颜色ID。 - * - * @return 当前WorkingNote对象的背景颜色ID - */ - public int getBgColorId() { - return mBgColorId; - } - - /** - * 获取笔记标题的背景颜色资源ID。 - * - * 该方法用于获取当前WorkingNote对象的标题栏背景颜色资源ID。它会根据当前笔记的背景颜色ID返回相应的颜色资源ID。 - * - * @return 当前WorkingNote对象的标题栏背景颜色资源ID - */ - public int getTitleBgResId() { - return NoteBgResources.getNoteTitleBgResource(mBgColorId); - } - - /** - * 获取笔记的清单模式。 - * - * 该方法用于获取当前WorkingNote对象的清单模式。 - * - * @return 当前WorkingNote对象的清单模式 - */ - public int getCheckListMode() { - return mMode; - } - - /** - * 获取笔记的ID。 - * - * 该方法用于获取当前WorkingNote对象对应的Note对象的ID。 - * - * @return 当前WorkingNote对象对应的Note对象的ID - */ - public long getNoteId() { - return mNoteId; - } - - - public long getFolderId() { - return mFolderId; - } - - public int getWidgetId() { - return mWidgetId; - } - - public int getWidgetType() { - return mWidgetType; - } - - public interface NoteSettingChangedListener { - /** - * Called when the background color of current note has just changed - */ - void onBackgroundColorChanged(); - - /** - * Called when user set clock - */ - void onClockAlertChanged(long date, boolean set); - - /** - * Call when user create note from widget - */ - void onWidgetChanged(); - - /** - * Call when switch between check list mode and normal mode - * @param oldMode is previous mode before change - * @param newMode is new mode - */ - void onCheckListModeChanged(int oldMode, int newMode); - } -} diff --git a/other/07_陈七源_代码标注/model/test.txt b/other/07_陈七源_代码标注/model/test.txt deleted file mode 100644 index e69de29..0000000 diff --git a/other/07_陈七源_代码标注/tool/BackupUtils.java b/other/07_陈七源_代码标注/tool/BackupUtils.java deleted file mode 100644 index 9243427..0000000 --- a/other/07_陈七源_代码标注/tool/BackupUtils.java +++ /dev/null @@ -1,442 +0,0 @@ -/* - * 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.tool; - -import android.content.Context; -import android.database.Cursor; -import android.os.Environment; -import android.text.TextUtils; -import android.text.format.DateFormat; -import android.util.Log; - -import net.micode.notes.R; -import net.micode.notes.data.Notes; -import net.micode.notes.data.Notes.DataColumns; -import net.micode.notes.data.Notes.DataConstants; -import net.micode.notes.data.Notes.NoteColumns; - -import java.io.File; -import java.io.FileNotFoundException; -import java.io.FileOutputStream; -import java.io.IOException; -import java.io.PrintStream; - - -public class BackupUtils { - private static final String TAG = "BackupUtils"; - // Singleton stuff - private static BackupUtils sInstance; - - /** - * 返回 BackupUtils 类的单例实例,并确保线程安全。 - * @param context 应用程序的上下文。 - * @return BackupUtils 类的实例。 - */ - public static synchronized BackupUtils getInstance(Context context) { - // 如果实例不存在,创建一个新的实例 - if (sInstance == null) { - sInstance = new BackupUtils(context); - } - // 返回现有实例或新创建的实例 - return sInstance; - } - - - /** - * Following states are signs to represents backup or restore - * status - */ - // Currently, the sdcard is not mounted - // SD卡未挂载的状态 - public static final int STATE_SD_CARD_UNMOUONTED = 0; - // 备份文件不存在的状态 - public static final int STATE_BACKUP_FILE_NOT_EXIST = 1; - // 数据格式不正确,可能被其他程序更改的状态 - public static final int STATE_DATA_DESTROIED = 2; - // 恢复或备份失败的状态,由运行时异常引起 - public static final int STATE_SYSTEM_ERROR = 3; - // 备份或恢复成功的状态 - public static final int STATE_SUCCESS = 4; - - private TextExport mTextExport; // 用于导出文本数据的 TextExport 实例 - - // 构造函数,用于创建 BackupUtils 类的实例 - private BackupUtils(Context context) { - mTextExport = new TextExport(context); // 创建 TextExport 实例并保存到成员变量中 - } - - /** - * 检查外部存储是否可用。 - * @return 如果外部存储可用,则返回 true;否则返回 false。 - */ - private static boolean externalStorageAvailable() { - return Environment.MEDIA_MOUNTED.equals(Environment.getExternalStorageState()); - } - - /** - * 将数据导出到文本文件中。 - * @return 导出操作的结果状态。 - */ - public int exportToText() { - return mTextExport.exportToText(); - } - - /** - * 获取导出的文本文件名。 - * @return 导出的文本文件名。 - */ - public String getExportedTextFileName() { - return mTextExport.mFileName; - } - - /** - * 获取导出的文本文件所在的目录。 - * @return 导出的文本文件所在的目录。 - */ - public String getExportedTextFileDir() { - return mTextExport.mFileDirectory; - } - - private static class TextExport { - private static final String[] NOTE_PROJECTION = { - NoteColumns.ID, // 笔记的标识符 - NoteColumns.MODIFIED_DATE, // 笔记最近修改的日期 - NoteColumns.SNIPPET, // 笔记内容的片段,用于快速预览笔记 - NoteColumns.TYPE // 笔记的类型 - }; - - private static final int NOTE_COLUMN_ID = 0; // 列 ID - private static final int NOTE_COLUMN_MODIFIED_DATE = 1; // 列 MODIFIED_DATE - private static final int NOTE_COLUMN_SNIPPET = 2; // 列 SNIPPET - - private static final String[] DATA_PROJECTION = { - DataColumns.CONTENT, // 数据的主体内容 - DataColumns.MIME_TYPE, // 数据的 MIME 类型 - DataColumns.DATA1, // 此列可能包含电话号码、电子邮件地址等。 - DataColumns.DATA2, // 此列可能包含电话类型(家庭、工作等)、电子邮件类型等。 - DataColumns.DATA3, // 通常情况下,此列的使用与 DATA2 相同。 - DataColumns.DATA4 // 保留供将来使用。 - }; - - private static final int DATA_COLUMN_CONTENT = 0; // 列 CONTENT - private static final int DATA_COLUMN_MIME_TYPE = 1; // 列 MIME_TYPE - private static final int DATA_COLUMN_CALL_DATE = 2; // 列 CALL_DATE - - - private static final int DATA_COLUMN_PHONE_NUMBER = 4; // 列 PHONE_NUMBER - - private final String[] TEXT_FORMAT; // 存储导出文本的格式 - private static final int FORMAT_FOLDER_NAME = 0; // 文件夹名对应的格式 - private static final int FORMAT_NOTE_DATE = 1; // 笔记日期对应的格式 - private static final int FORMAT_NOTE_CONTENT = 2; // 笔记内容对应的格式 - - private Context mContext; // 应用程序上下文 - private String mFileName; // 导出文件的名称 - private String mFileDirectory; // 导出文件所在的目录 - - - /** - * 构造函数,用于创建 TextExport 类的实例。 - * - * @param context 应用程序上下文。 - */ - public TextExport(Context context) { - TEXT_FORMAT = context.getResources().getStringArray(R.array.format_for_exported_note); // 从资源中获取导出文本的格式 - mContext = context; // 保存应用程序上下文 - mFileName = ""; // 导出文件名称为空 - mFileDirectory = ""; // 导出文件所在目录为空 - } - - /** - * 获取指定索引处的导出文本格式。 - * - * @param id 索引值。 - * @return 指定索引处的导出文本格式。 - */ - private String getFormat(int id) { - return TEXT_FORMAT[id]; // 返回指定索引处的导出文本格式 - } - - /** - * Export the folder identified by folder id to text - */ - /** - * 导出指定笔记本中的所有笔记到文本文件。 - * - * @param folderId 笔记本的标识符。 - * @param ps PrintStream 对象,用于写入导出的文本数据。 - */ - private void exportFolderToText(String folderId, PrintStream ps) { - // 查询属于该笔记本的笔记 - Cursor notesCursor = mContext.getContentResolver().query(Notes.CONTENT_NOTE_URI, - NOTE_PROJECTION, NoteColumns.PARENT_ID + "=?", new String[]{folderId}, null); - - if (notesCursor != null) { - if (notesCursor.moveToFirst()) { - do { - // 打印笔记的最近修改日期 - ps.println(String.format(getFormat(FORMAT_NOTE_DATE), DateFormat.format( - mContext.getString(R.string.format_datetime_mdhm), - notesCursor.getLong(NOTE_COLUMN_MODIFIED_DATE)))); - // 查询属于该笔记的数据 - String noteId = notesCursor.getString(NOTE_COLUMN_ID); - exportNoteToText(noteId, ps); // 将笔记导出到文本文件 - } while (notesCursor.moveToNext()); - } - notesCursor.close(); - } - } - - /** - * 将指定 ID 的笔记导出到打印流中。 - * - * @param noteId 要导出的笔记 ID。 - * @param ps 要写入笔记的打印流对象。 - */ - private void exportNoteToText(String noteId, PrintStream ps) { - // 查询与该笔记关联的数据项 - Cursor dataCursor = mContext.getContentResolver().query( - Notes.CONTENT_DATA_URI, // 查询的 URI - DATA_PROJECTION, // 要查询的列名 - DataColumns.NOTE_ID + "=?", // WHERE 子句 - new String[]{noteId}, // WHERE 子句中占位符的值 - null // 排序方式 - ); - - if (dataCursor != null) { - if (dataCursor.moveToFirst()) { - do { - // 获取当前数据项的 MIME 类型 - String mimeType = dataCursor.getString(DATA_COLUMN_MIME_TYPE); - if (DataConstants.CALL_NOTE.equals(mimeType)) { - // 处理通话记录类型的笔记 - String phoneNumber = dataCursor.getString(DATA_COLUMN_PHONE_NUMBER); // 电话号码 - long callDate = dataCursor.getLong(DATA_COLUMN_CALL_DATE); // 通话日期 - String location = dataCursor.getString(DATA_COLUMN_CONTENT); // 通话位置 - - if (!TextUtils.isEmpty(phoneNumber)) { - // 如果电话号码不为空,就将其写入打印流中 - ps.println(String.format(getFormat(FORMAT_NOTE_CONTENT), phoneNumber)); - } - - // 将通话日期格式化为字符串,并写入打印流中 - ps.println(String.format(getFormat(FORMAT_NOTE_CONTENT), - DateFormat.format(mContext.getString(R.string.format_datetime_mdhm), callDate))); - - if (!TextUtils.isEmpty(location)) { - // 如果通话位置不为空,就将其写入打印流中 - ps.println(String.format(getFormat(FORMAT_NOTE_CONTENT), location)); - } - } else if (DataConstants.NOTE.equals(mimeType)) { - // 处理普通笔记类型 - String content = dataCursor.getString(DATA_COLUMN_CONTENT); // 笔记内容 - - if (!TextUtils.isEmpty(content)) { - // 如果笔记内容不为空,就将其写入打印流中 - ps.println(String.format(getFormat(FORMAT_NOTE_CONTENT), content)); - } - } - } while (dataCursor.moveToNext()); - } - - // 关闭游标 - dataCursor.close(); - } - - // 在每个笔记之间输出一个分隔符 - try { - ps.write(new byte[]{Character.LINE_SEPARATOR, Character.LETTER_NUMBER}); - } catch (IOException e) { - Log.e(TAG, e.toString()); - } - } - - /** - * 将笔记导出为纯文本文件。 - * - * @return 导出的状态码。 - */ - public int exportToText() { - // 检查外部存储是否可用 - if (!externalStorageAvailable()) { - Log.d(TAG, "Media was not mounted"); - return STATE_SD_CARD_UNMOUONTED; - } - - // 获取打印流对象,准备写入数据 - PrintStream ps = getExportToTextPrintStream(); - if (ps == null) { - Log.e(TAG, "get print stream error"); - return STATE_SYSTEM_ERROR; - } - - // 先导出所有的文件夹及其笔记 - Cursor folderCursor = mContext.getContentResolver().query( - Notes.CONTENT_NOTE_URI, // 查询的 URI - NOTE_PROJECTION, // 要查询的列名 - "(" + NoteColumns.TYPE + "=" + Notes.TYPE_FOLDER + " AND " - + NoteColumns.PARENT_ID + "<>" + Notes.ID_TRASH_FOLER + ") OR " - + NoteColumns.ID + "=" + Notes.ID_CALL_RECORD_FOLDER, // WHERE 子句 - null, // WHERE 子句中占位符的值 - null // 排序方式 - ); - - if (folderCursor != null) { - if (folderCursor.moveToFirst()) { - do { - // 打印文件夹名称 - String folderName = ""; - if (folderCursor.getLong(NOTE_COLUMN_ID) == Notes.ID_CALL_RECORD_FOLDER) { - folderName = mContext.getString(R.string.call_record_folder_name); - } else { - folderName = folderCursor.getString(NOTE_COLUMN_SNIPPET); - } - if (!TextUtils.isEmpty(folderName)) { - ps.println(String.format(getFormat(FORMAT_FOLDER_NAME), folderName)); - } - String folderId = folderCursor.getString(NOTE_COLUMN_ID); - // 导出当前文件夹下的所有笔记 - exportFolderToText(folderId, ps); - } while (folderCursor.moveToNext()); - } - - // 关闭游标 - folderCursor.close(); - } - - // 导出根目录下的笔记 - Cursor noteCursor = mContext.getContentResolver().query( - Notes.CONTENT_NOTE_URI, // 查询的 URI - NOTE_PROJECTION, // 要查询的列名 - NoteColumns.TYPE + "=" + +Notes.TYPE_NOTE + " AND " + NoteColumns.PARENT_ID + "=0", // WHERE 子句 - null, // WHERE 子句中占位符的值 - null // 排序方式 - ); - - if (noteCursor != null) { - if (noteCursor.moveToFirst()) { - do { - // 打印笔记的最后修改时间 - ps.println(String.format(getFormat(FORMAT_NOTE_DATE), DateFormat.format( - mContext.getString(R.string.format_datetime_mdhm), - noteCursor.getLong(NOTE_COLUMN_MODIFIED_DATE)))); - // 导出当前笔记的内容 - String noteId = noteCursor.getString(NOTE_COLUMN_ID); - exportNoteToText(noteId, ps); - } while (noteCursor.moveToNext()); - } - - // 关闭游标 - noteCursor.close(); - } - - // 关闭打印流 - ps.close(); - return STATE_SUCCESS; - } - /** - * Get a print stream pointed to the file {@generateExportedTextFile} - */ - /** - * 生成用于将数据输出到文本文件中的PrintStream对象。 - * - * @return 用于将数据输出到文本文件中的PrintStream对象。 - */ - private PrintStream getExportToTextPrintStream() { - // 生成指定路径和名称的文本文件,保存在SD卡上。 - File file = generateFileMountedOnSDcard(mContext, R.string.file_path, - R.string.file_name_txt_format); - if (file == null) { - // 如果创建文件失败,则记录错误日志并返回null。 - Log.e(TAG, "create file to exported failed"); - return null; - } - // 将文件名保存在成员变量中。 - mFileName = file.getName(); - // 将文件目录路径保存在成员变量中。 - mFileDirectory = mContext.getString(R.string.file_path); - PrintStream ps = null; - try { - // 创建一个新的FileOutputStream对象,并将其包装在PrintStream对象中。 - FileOutputStream fos = new FileOutputStream(file); - ps = new PrintStream(fos); - } catch (FileNotFoundException e) { - // 如果找不到文件,则打印堆栈跟踪并返回null。 - e.printStackTrace(); - return null; - } catch (NullPointerException e) { - // 如果发生空指针异常,则打印堆栈跟踪并返回null。 - e.printStackTrace(); - return null; - } - // 返回用于将数据输出到文本文件中的PrintStream对象。 - return ps; - } - } - - /** - * Generate the text file to store imported data - */ - /** - * 在SD卡上创建目录和指定格式的文件。 - * - * @param context 上下文对象。 - * @param filePathResId 文件路径的字符串资源ID。 - * @param fileNameFormatResId 文件名格式的字符串资源ID。 - * @return 生成的文件对象,如果创建失败则返回null。 - */ - private static File generateFileMountedOnSDcard(Context context, int filePathResId, int fileNameFormatResId) { - // 创建一个StringBuilder对象,用于拼接文件路径和名称。 - StringBuilder sb = new StringBuilder(); - // 将外部存储器目录添加到StringBuilder中。 - sb.append(Environment.getExternalStorageDirectory()); - // 将文件路径的字符串资源ID添加到StringBuilder中。 - sb.append(context.getString(filePathResId)); - // 创建一个包含文件路径的File对象。 - File filedir = new File(sb.toString()); - // 将文件名格式化并将其添加到StringBuilder末尾。 - sb.append(context.getString(fileNameFormatResId, - DateFormat.format(context.getString(R.string.format_date_ymd), System.currentTimeMillis()))); - // 创建一个包含完整文件路径的File对象。 - File file = new File(sb.toString()); - - try { - // 如果文件夹不存在,则创建它。 - if (!filedir.exists()) { - filedir.mkdir(); - } - // 如果文件不存在,则创建它。 - if (!file.exists()) { - file.createNewFile(); - } - // 返回创建的文件对象。 - return file; - } catch (SecurityException e) { - // 捕获安全异常,并打印错误日志。 - e.printStackTrace(); - } catch (IOException e) { - // 捕获I/O异常,并打印错误日志。 - e.printStackTrace(); - } - - // 如果创建文件失败,则返回null。 - return null; - } - -} - - diff --git a/other/07_陈七源_代码标注/tool/ResourceParser.java b/other/07_陈七源_代码标注/tool/ResourceParser.java deleted file mode 100644 index bc160a2..0000000 --- a/other/07_陈七源_代码标注/tool/ResourceParser.java +++ /dev/null @@ -1,284 +0,0 @@ -/* - * 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.tool; - -import android.content.Context; -import android.preference.PreferenceManager; - -import net.micode.notes.R; -import net.micode.notes.ui.NotesPreferenceActivity; - -public class ResourceParser { - - public static final int YELLOW = 0; - public static final int BLUE = 1; - public static final int WHITE = 2; - public static final int GREEN = 3; - public static final int RED = 4; - - public static final int BG_DEFAULT_COLOR = YELLOW; - - public static final int TEXT_SMALL = 0; - public static final int TEXT_MEDIUM = 1; - public static final int TEXT_LARGE = 2; - public static final int TEXT_SUPER = 3; - - public static final int BG_DEFAULT_FONT_SIZE = TEXT_MEDIUM; - - public static class NoteBgResources { - // 存储五种不同颜色的背景图片资源id - private final static int [] BG_EDIT_RESOURCES = new int [] { - R.drawable.edit_yellow, // 黄色背景图片 - R.drawable.edit_blue, // 蓝色背景图片 - R.drawable.edit_white, // 白色背景图片 - R.drawable.edit_green, // 绿色背景图片 - R.drawable.edit_red // 红色背景图片 - }; - - // 存储五种不同颜色的标题栏背景图片资源id - private final static int [] BG_EDIT_TITLE_RESOURCES = new int [] { - R.drawable.edit_title_yellow, // 黄色标题栏背景图片 - R.drawable.edit_title_blue, // 蓝色标题栏背景图片 - R.drawable.edit_title_white, // 白色标题栏背景图片 - R.drawable.edit_title_green, // 绿色标题栏背景图片 - R.drawable.edit_title_red // 红色标题栏背景图片 - }; - - /** - * 获取给定 id 对应的笔记项背景资源 ID - * - * @param id 笔记项背景色的 ID,从 0 到 4 分别表示黄、蓝、白、绿、红五种颜色 - * @return 笔记项背景资源的 ID - */ - public static int getNoteBgResource(int id) { - return BG_EDIT_RESOURCES[id]; - } - - /** - * 获取给定 id 对应的标题栏背景资源 ID - * - * @param id 标题栏背景色的 ID,从 0 到 2 分别表示橙色、蓝色和绿色三种颜色 - * @return 标题栏背景资源的 ID - */ - public static int getNoteTitleBgResource(int id) { - return BG_EDIT_TITLE_RESOURCES[id]; - } - - } - - /** - * 获取笔记项的默认背景 ID - * - * @param context 上下文对象 - * @return 笔记项的默认背景资源 ID - */ - public static int getDefaultBgId(Context context) { - // 检查应用程序的共享首选项中是否启用了自定义笔记项背景颜色 - if (PreferenceManager.getDefaultSharedPreferences(context).getBoolean( - NotesPreferenceActivity.PREFERENCE_SET_BG_COLOR_KEY, false)) { - // 如果已经启用,则生成一个随机数并返回对应的笔记项背景资源 ID - return (int) (Math.random() * NoteBgResources.BG_EDIT_RESOURCES.length); - } else { - // 如果未启用,则返回默认背景颜色(即 BG_DEFAULT_COLOR)所对应的资源 ID。 - return BG_DEFAULT_COLOR; - } - } - - - public static class NoteItemBgResources { - /** - * 笔记项在列表中作为第一个条目时的背景资源 ID - */ - private final static int [] BG_FIRST_RESOURCES = new int [] { - R.drawable.list_yellow_up, - R.drawable.list_blue_up, - R.drawable.list_white_up, - R.drawable.list_green_up, - R.drawable.list_red_up - }; - - /** - * 笔记项在列表中作为中间条目时的背景资源 ID - */ - private final static int [] BG_NORMAL_RESOURCES = new int [] { - R.drawable.list_yellow_middle, - R.drawable.list_blue_middle, - R.drawable.list_white_middle, - R.drawable.list_green_middle, - R.drawable.list_red_middle - }; - - /** - * 笔记项在列表中作为最后一个条目时的背景资源 ID - */ - private final static int [] BG_LAST_RESOURCES = new int [] { - R.drawable.list_yellow_down, - R.drawable.list_blue_down, - R.drawable.list_white_down, - R.drawable.list_green_down, - R.drawable.list_red_down, - }; - - - /** - * 笔记项在列表中作为唯一条目时的背景资源 ID - */ - private final static int [] BG_SINGLE_RESOURCES = new int [] { - R.drawable.list_yellow_single, - R.drawable.list_blue_single, - R.drawable.list_white_single, - R.drawable.list_green_single, - R.drawable.list_red_single - }; - - /** - * 获取给定 id 对应的笔记项在列表中作为第一个条目时的背景资源 ID - * - * @param id 背景颜色的 ID,从 0 到 4 分别表示黄、蓝、白、绿、红五种颜色 - * @return 笔记项在列表中作为第一个条目时的背景资源 ID - */ - public static int getNoteBgFirstRes(int id) { - return BG_FIRST_RESOURCES[id]; - } - - /** - * 获取给定 id 对应的笔记项在列表中作为最后一个条目时的背景资源 ID - * - * @param id 背景颜色的 ID,从 0 到 4 分别表示黄、蓝、白、绿、红五种颜色 - * @return 笔记项在列表中作为最后一个条目时的背景资源 ID - */ - public static int getNoteBgLastRes(int id) { - return BG_LAST_RESOURCES[id]; - } - - /** - * 获取给定 id 对应的笔记项在列表中作为唯一条目时的背景资源 ID - * - * @param id 背景颜色的 ID,从 0 到 4 分别表示黄、蓝、白、绿、红五种颜色 - * @return 笔记项在列表中作为唯一条目时的背景资源 ID - */ - public static int getNoteBgSingleRes(int id) { - return BG_SINGLE_RESOURCES[id]; - } - - - /** - * 获取给定 id 对应的笔记项在列表中作为中间条目时的背景资源 ID - * - * @param id 背景颜色的 ID,从 0 到 4 分别表示黄、蓝、白、绿、红五种颜色 - * @return 笔记项在列表中作为中间条目时的背景资源 ID - */ - public static int getNoteBgNormalRes(int id) { - return BG_NORMAL_RESOURCES[id]; - } - - /** - * 返回文件夹列表中文件夹的背景资源 ID,该资源是一个 drawable(R.drawable.list_folder) - * - * @return 文件夹列表中文件夹的背景资源 ID - */ - public static int getFolderBgRes() { - return R.drawable.list_folder; - } - - } - - public static class WidgetBgResources { - /** - * 小部件在 2x2 模式下的背景资源 ID 数组 - */ - private final static int [] BG_2X_RESOURCES = new int [] { - R.drawable.widget_2x_yellow, - R.drawable.widget_2x_blue, - R.drawable.widget_2x_white, - R.drawable.widget_2x_green, - R.drawable.widget_2x_red, - }; - - /** - * 获取给定 id 对应的小部件在 2x2 模式下的背景资源 ID - * - * @param id 背景颜色的 ID,从 0 到 4 分别表示黄、蓝、白、绿、红五种颜色 - * @return 给定 id 对应的小部件在 2x2 模式下的背景资源 ID - */ - public static int getWidget2xBgResource(int id) { - return BG_2X_RESOURCES[id]; - } - - /** - * 小部件在 4x4 模式下的背景资源 ID 数组 - */ - private final static int [] BG_4X_RESOURCES = new int [] { - R.drawable.widget_4x_yellow, - R.drawable.widget_4x_blue, - R.drawable.widget_4x_white, - R.drawable.widget_4x_green, - R.drawable.widget_4x_red - }; - - /** - * 获取给定 id 对应的小部件在 4x4 模式下的背景资源 ID - * - * @param id 背景颜色的 ID,从 0 到 4 分别表示黄、蓝、白、绿、红五种颜色 - * @return 给定 id 对应的小部件在 4x4 模式下的背景资源 ID - */ - public static int getWidget4xBgResource(int id) { - return BG_4X_RESOURCES[id]; - } - - } - - public static class TextAppearanceResources { - /** - * 文字外观资源 ID 数组 - */ - private final static int [] TEXTAPPEARANCE_RESOURCES = new int [] { - R.style.TextAppearanceNormal, - R.style.TextAppearanceMedium, - R.style.TextAppearanceLarge, - R.style.TextAppearanceSuper - }; - - /** - * 获取给定 id 对应的文字外观资源 ID - * - * @param id 文字外观的 ID,从 0 到 3 分别表示 Normal、Medium、Large、Super 四种大小 - * @return 给定 id 对应的文字外观资源 ID - */ - public static int getTexAppearanceResource(int id) { - /** - * HACKME: 修复在共享偏好中存储资源 ID 的 bug。 - * 在某些情况下,id 可能大于资源数组的长度,在这种情况下, - * 返回 {@link ResourceParser#BG_DEFAULT_FONT_SIZE} - */ - if (id >= TEXTAPPEARANCE_RESOURCES.length) { - return BG_DEFAULT_FONT_SIZE; - } - return TEXTAPPEARANCE_RESOURCES[id]; - } - - /** - * 获取文字外观资源 ID 数组的长度 - * - * @return 文字外观资源 ID 数组的长度 - */ - public static int getResourcesSize() { - return TEXTAPPEARANCE_RESOURCES.length; - } - - } -} diff --git a/other/07_陈七源_实践总结报告.docx b/other/07_陈七源_实践总结报告.docx deleted file mode 100644 index 9d881b3..0000000 Binary files a/other/07_陈七源_实践总结报告.docx and /dev/null differ diff --git a/other/08_演示录屏.mp4 b/other/08_演示录屏.mp4 deleted file mode 100644 index 655fa2c..0000000 Binary files a/other/08_演示录屏.mp4 and /dev/null differ diff --git a/src/src.zip b/src/src.zip deleted file mode 100644 index 21a12ad..0000000 Binary files a/src/src.zip and /dev/null differ