diff --git a/doc/小米便签开源代码的泛读报告.docx b/doc/小米便签开源代码的泛读报告.docx new file mode 100644 index 0000000..cdcd5c6 Binary files /dev/null and b/doc/小米便签开源代码的泛读报告.docx differ diff --git a/doc/小米便签开源代码的泛读报告最终版.docx b/doc/小米便签开源代码的泛读报告最终版.docx new file mode 100644 index 0000000..cb5f642 Binary files /dev/null and b/doc/小米便签开源代码的泛读报告最终版.docx differ diff --git a/src%2Fsrc%2Fnet%2Fmicode%2Fnotes%2Ftool/BackupUtils.java b/src%2Fsrc%2Fnet%2Fmicode%2Fnotes%2Ftool/BackupUtils.java new file mode 100644 index 0000000..0145eb0 --- /dev/null +++ b/src%2Fsrc%2Fnet%2Fmicode%2Fnotes%2Ftool/BackupUtils.java @@ -0,0 +1,347 @@ +/* + * 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 java.io.File; +import java.io.FileNotFoundException; +import java.io.FileOutputStream; +import java.io.IOException; +import java.io.PrintStream; +import java.text.DateFormat; + +import javax.naming.Context; + +import android.database.Cursor; +import android.os.Environment; +import android.text.TextUtils; +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; + +public class BackupUtils { + private static final String TAG = "BackupUtils"; + // Singleton stuff + private static BackupUtils sInstance; + + public static synchronized BackupUtils getInstance(Context context) { + // synchronized"同步锁",作用是保证在同一时刻, 被修饰的代码块或方法只会有一个线程执行,以保证并发安全。这里是把这个方法锁住了 + // 运行到这个方法时,要检查一下有没有其它线程正在用这个方法(或者该类的其他同步方法) + // (1)有:要等正在使用synchronized方法的线程运行完后再运行此线程(2)没有:锁定调用者,可以直接运行。 + + if (sInstance == null) {// 判断如果当前备份为null,就新声明一个 + sInstance = new BackupUtils(context); + } + return sInstance;// 返回备份 + } + + /** + * Following states are signs to represents backup or restore + * status + */ + // 表示此时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; + + 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 = { // 把NotesNolumn的一些拿作出为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 = { //// 把NotesNolumn的一些拿作出为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) { + // 通过查询PARENT_ID等于folderId的note来选出制定ID文件夹下的Note,默认输出为ID升序 + Cursor notesCursor = mContext.getContentResolver().query(Notes.CONTENT_NOTE_URI, + NOTE_PROJECTION, NoteColumns.PARENT_ID + "=?", new String[] { + folderId + }, null); + + if (notesCursor != null) { + if (notesCursor.moveToFirst()) {// 判断cursor是否移到了第一条上 + do { + // 打印note最后一次更新的日期,ps里面保存有这份note的日期 + 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);// 将文件导出到text + } while (notesCursor.moveToNext());// 使用moveToNext逐条读取 + } + notesCursor.close();// 关闭notesCursor且释放内存 + } + } + + /** + * 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, // 查询DataColumns的NOTE_ID等于传入的string型的noteId的note,默认为升序 + DATA_PROJECTION, DataColumns.NOTE_ID + "=?", new String[] { + noteId + }, null); + + if (dataCursor != null) {// 利用光标来扫描内容,区别为callnote和note两种,靠ps.printline输出 + 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)) { // 判断是否为空字符,不为空则打印phoneNumber + ps.println(String.format(getFormat(FORMAT_NOTE_CONTENT), // 将phoneNumber格式化为和FORMAT_NOTE_CONTENT一样的格式后再打印 + phoneNumber)); + } + // Print call date + ps.println(String.format(getFormat(FORMAT_NOTE_CONTENT), DateFormat// 格式化为和FORMAT_NOTE_CONTENT一样的格式后再打印 + .format(mContext.getString(R.string.format_datetime_mdhm), + callDate))); + // Print call attachment location + if (!TextUtils.isEmpty(location)) {// 判断location是否为空字符 + ps.println(String.format(getFormat(FORMAT_NOTE_CONTENT), // 不为空则转换格式后打印location + location)); + } + } else if (DataConstants.NOTE.equals(mimeType)) {// 判断是否mimeType和NOTE一致 + String content = dataCursor.getString(DATA_COLUMN_CONTENT); + if (!TextUtils.isEmpty(content)) {// 判断content是否为空字符 + ps.println(String.format(getFormat(FORMAT_NOTE_CONTENT), // 不为空则转换格式和FORMAT_NOTE_CONTENT一致后打印content + content)); + } + } + } while (dataCursor.moveToNext()); + } + dataCursor.close();// 关闭dataCursor且释放内存 + } + // note之间打印line seperator + 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() {// 通过调用上面的定义过的exportFolder和exportNote方法实现导出note为text + if (!externalStorageAvailable()) {// 判断是否外部存储是被允许的 + Log.d(TAG, "Media was not mounted");// 不被允许就log提醒 + return STATE_SD_CARD_UNMOUONTED;// 返回sd卡为被装入手机,也就是返回0 + } + + PrintStream ps = getExportToTextPrintStream(); + if (ps == null) {// 判断ps是否为空 + Log.e(TAG, "get print stream error");// 为空则log提醒并返回STATE_SYSTEM_ERROR代表的数值,即返回3 + return STATE_SYSTEM_ERROR; + } + // First export folder and its notes + Cursor folderCursor = mContext.getContentResolver().query(// 查询赋给folderCursor进行初始化 + 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 { + // 针对两种情况对folderName赋值 + 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)) {// 不为空则打印folderName + ps.println(String.format(getFormat(FORMAT_FOLDER_NAME), folderName)); + } + String folderId = folderCursor.getString(NOTE_COLUMN_ID); + exportFolderToText(folderId, ps);// 以id为标识导出ps + } while (folderCursor.moveToNext()); + } + folderCursor.close(); + } + + // 将根目录里的便签导出(由于不属于任何文件夹,因此无法通过文件夹导出来实现这一部分便签的导出) + Cursor noteCursor = mContext.getContentResolver().query(// 查询PARENT_ID等于0且NoteColumns.TYPE等于Notes.TYPE_NOTE的结果赋给noteCursor + 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);// 调用exportNoteToText进行导出 + } 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) {// file为空则产生file失败,log提醒后返回空 + Log.e(TAG, "create file to exported failed"); + return null; + } + mFileName = file.getName();// 调用则返回一个字符串值,该值是给定File对象的名称 + mFileDirectory = mContext.getString(R.string.file_path);// 得到文件地址,返回string + PrintStream ps = null; + try { + FileOutputStream fos = new FileOutputStream(file); + ps = new PrintStream(fos);// 给ps赋值 + } 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) {// 产生一个文件并导出到SD卡上 + 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/src%2Fsrc%2Fnet%2Fmicode%2Fnotes%2Ftool/GTaskStringUtils.java b/src%2Fsrc%2Fnet%2Fmicode%2Fnotes%2Ftool/GTaskStringUtils.java new file mode 100644 index 0000000..d685018 --- /dev/null +++ b/src%2Fsrc%2Fnet%2Fmicode%2Fnotes%2Ftool/GTaskStringUtils.java @@ -0,0 +1,114 @@ +/* + * 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. + */ +//只定义了很多的静态字符串,为jsonObject提供相应字符串的"key" + +package net.micode.notes.tool; + +public class GTaskStringUtils { + + public final static String GTASK_JSON_ACTION_ID = "action_id"; + + public final static String GTASK_JSON_ACTION_LIST = "action_list"; + + public final static String GTASK_JSON_ACTION_TYPE = "action_type"; + + public final static String GTASK_JSON_ACTION_TYPE_CREATE = "create"; + + public final static String GTASK_JSON_ACTION_TYPE_GETALL = "get_all"; + + public final static String GTASK_JSON_ACTION_TYPE_MOVE = "move"; + + public final static String GTASK_JSON_ACTION_TYPE_UPDATE = "update"; + + public final static String GTASK_JSON_CREATOR_ID = "creator_id"; + + public final static String GTASK_JSON_CHILD_ENTITY = "child_entity"; + + public final static String GTASK_JSON_CLIENT_VERSION = "client_version"; + + public final static String GTASK_JSON_COMPLETED = "completed"; + + public final static String GTASK_JSON_CURRENT_LIST_ID = "current_list_id"; + + public final static String GTASK_JSON_DEFAULT_LIST_ID = "default_list_id"; + + public final static String GTASK_JSON_DELETED = "deleted"; + + public final static String GTASK_JSON_DEST_LIST = "dest_list"; + + public final static String GTASK_JSON_DEST_PARENT = "dest_parent"; + + public final static String GTASK_JSON_DEST_PARENT_TYPE = "dest_parent_type"; + + public final static String GTASK_JSON_ENTITY_DELTA = "entity_delta"; + + public final static String GTASK_JSON_ENTITY_TYPE = "entity_type"; + + public final static String GTASK_JSON_GET_DELETED = "get_deleted"; + + public final static String GTASK_JSON_ID = "id"; + + public final static String GTASK_JSON_INDEX = "index"; + + public final static String GTASK_JSON_LAST_MODIFIED = "last_modified"; + + public final static String GTASK_JSON_LATEST_SYNC_POINT = "latest_sync_point"; + + public final static String GTASK_JSON_LIST_ID = "list_id"; + + public final static String GTASK_JSON_LISTS = "lists"; + + public final static String GTASK_JSON_NAME = "name"; + + public final static String GTASK_JSON_NEW_ID = "new_id"; + + public final static String GTASK_JSON_NOTES = "notes"; + + public final static String GTASK_JSON_PARENT_ID = "parent_id"; + + public final static String GTASK_JSON_PRIOR_SIBLING_ID = "prior_sibling_id"; + + public final static String GTASK_JSON_RESULTS = "results"; + + public final static String GTASK_JSON_SOURCE_LIST = "source_list"; + + public final static String GTASK_JSON_TASKS = "tasks"; + + public final static String GTASK_JSON_TYPE = "type"; + + public final static String GTASK_JSON_TYPE_GROUP = "GROUP"; + + public final static String GTASK_JSON_TYPE_TASK = "TASK"; + + public final static String GTASK_JSON_USER = "user"; + + public final static String MIUI_FOLDER_PREFFIX = "[MIUI_Notes]"; + + public final static String FOLDER_DEFAULT = "Default"; + + public final static String FOLDER_CALL_NOTE = "Call_Note"; + + public final static String FOLDER_META = "METADATA"; + + public final static String META_HEAD_GTASK_ID = "meta_gid"; + + public final static String META_HEAD_NOTE = "meta_note"; + + public final static String META_HEAD_DATA = "meta_data"; + + public final static String META_NOTE_NAME = "[META INFO] DON'T UPDATE AND DELETE"; + +} diff --git a/src/src/net/micode/notes/gtask/exception/ActionFailureException.java b/src/src/net/micode/notes/gtask/exception/ActionFailureException.java index 15504be..9f72c52 100644 --- a/src/src/net/micode/notes/gtask/exception/ActionFailureException.java +++ b/src/src/net/micode/notes/gtask/exception/ActionFailureException.java @@ -14,15 +14,26 @@ * limitations under the License. */ +/* + * Description:支持小米便签运行过程中的运行异常处理。 + */ + package net.micode.notes.gtask.exception; public class ActionFailureException extends RuntimeException { private static final long serialVersionUID = 4425249765923293627L; + /* + * serialVersionUID相当于java类的身份证。主要用于版本控制。 + * serialVersionUID作用是序列化时保持版本的兼容性,即在版本升级时反序列化仍保持对象的唯一性。 + */ public ActionFailureException() { super(); } - + /* + * 在JAVA类中使用super来引用父类的成分,用this来引用当前对象. + * 此处super()以及super (paramString)可认为是Exception ()和Exception (paramString) + */ public ActionFailureException(String paramString) { super(paramString); } diff --git a/src/src/net/micode/notes/tool/BackupUtils.java b/src/src/net/micode/notes/tool/BackupUtils.java index 39f6ec4..0145eb0 100644 --- a/src/src/net/micode/notes/tool/BackupUtils.java +++ b/src/src/net/micode/notes/tool/BackupUtils.java @@ -16,77 +16,80 @@ package net.micode.notes.tool; -import android.content.Context; +import java.io.File; +import java.io.FileNotFoundException; +import java.io.FileOutputStream; +import java.io.IOException; +import java.io.PrintStream; +import java.text.DateFormat; + +import javax.naming.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) { + // synchronized"同步锁",作用是保证在同一时刻, 被修饰的代码块或方法只会有一个线程执行,以保证并发安全。这里是把这个方法锁住了 + // 运行到这个方法时,要检查一下有没有其它线程正在用这个方法(或者该类的其他同步方法) + // (1)有:要等正在使用synchronized方法的线程运行完后再运行此线程(2)没有:锁定调用者,可以直接运行。 + + if (sInstance == null) {// 判断如果当前备份为null,就新声明一个 sInstance = new BackupUtils(context); } - return sInstance; + 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; + // 表示此时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; - private BackupUtils(Context context) { + private BackupUtils(Context context) {// 初始化函数 mTextExport = new TextExport(context); } - private static boolean externalStorageAvailable() { + private static boolean externalStorageAvailable() {// 检测外部存储功能是否可用 return Environment.MEDIA_MOUNTED.equals(Environment.getExternalStorageState()); } - public int exportToText() { + public int exportToText() {// 导出文本 return mTextExport.exportToText(); } - public String getExportedTextFileName() { + public String getExportedTextFileName() {// 获取导出的文本文件名称 return mTextExport.mFileName; } - public String getExportedTextFileDir() { + public String getExportedTextFileDir() {// 获取导出的文本文件的文件地址 return mTextExport.mFileDirectory; } - private static class TextExport { - private static final String[] NOTE_PROJECTION = { + private static class TextExport {// 导出工具类 + private static final String[] NOTE_PROJECTION = { // 把NotesNolumn的一些拿作出为NOTE_PROJECTION NoteColumns.ID, NoteColumns.MODIFIED_DATE, NoteColumns.SNIPPET, @@ -99,7 +102,7 @@ public class BackupUtils { private static final int NOTE_COLUMN_SNIPPET = 2; - private static final String[] DATA_PROJECTION = { + private static final String[] DATA_PROJECTION = { //// 把NotesNolumn的一些拿作出为DATA_PROJECTION DataColumns.CONTENT, DataColumns.MIME_TYPE, DataColumns.DATA1, @@ -116,23 +119,23 @@ public class BackupUtils { 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 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); + TEXT_FORMAT = context.getResources().getStringArray(R.array.format_for_exported_note);// 获取数据资源 mContext = context; - mFileName = ""; - mFileDirectory = ""; + mFileName = "";// 设置文件名称 + mFileDirectory = "";// 设置导出文件地址 } - private String getFormat(int id) { + private String getFormat(int id) {// 获取文本的组成部分 return TEXT_FORMAT[id]; } @@ -140,25 +143,25 @@ public class BackupUtils { * Export the folder identified by folder id to text */ private void exportFolderToText(String folderId, PrintStream ps) { - // Query notes belong to this folder + // 通过查询PARENT_ID等于folderId的note来选出制定ID文件夹下的Note,默认输出为ID升序 Cursor notesCursor = mContext.getContentResolver().query(Notes.CONTENT_NOTE_URI, NOTE_PROJECTION, NoteColumns.PARENT_ID + "=?", new String[] { - folderId + folderId }, null); if (notesCursor != null) { - if (notesCursor.moveToFirst()) { + if (notesCursor.moveToFirst()) {// 判断cursor是否移到了第一条上 do { - // Print note's last modified date + // 打印note最后一次更新的日期,ps里面保存有这份note的日期 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()); + exportNoteToText(noteId, ps);// 将文件导出到text + } while (notesCursor.moveToNext());// 使用moveToNext逐条读取 } - notesCursor.close(); + notesCursor.close();// 关闭notesCursor且释放内存 } } @@ -166,13 +169,13 @@ public class BackupUtils { * 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, + Cursor dataCursor = mContext.getContentResolver().query(Notes.CONTENT_DATA_URI, // 查询DataColumns的NOTE_ID等于传入的string型的noteId的note,默认为升序 DATA_PROJECTION, DataColumns.NOTE_ID + "=?", new String[] { - noteId + noteId }, null); - if (dataCursor != null) { - if (dataCursor.moveToFirst()) { + if (dataCursor != null) {// 利用光标来扫描内容,区别为callnote和note两种,靠ps.printline输出 + if (dataCursor.moveToFirst()) {// 从第一条开始遍历 do { String mimeType = dataCursor.getString(DATA_COLUMN_MIME_TYPE); if (DataConstants.CALL_NOTE.equals(mimeType)) { @@ -181,31 +184,31 @@ public class BackupUtils { 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), + if (!TextUtils.isEmpty(phoneNumber)) { // 判断是否为空字符,不为空则打印phoneNumber + ps.println(String.format(getFormat(FORMAT_NOTE_CONTENT), // 将phoneNumber格式化为和FORMAT_NOTE_CONTENT一样的格式后再打印 phoneNumber)); } // Print call date - ps.println(String.format(getFormat(FORMAT_NOTE_CONTENT), DateFormat + ps.println(String.format(getFormat(FORMAT_NOTE_CONTENT), DateFormat// 格式化为和FORMAT_NOTE_CONTENT一样的格式后再打印 .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), + if (!TextUtils.isEmpty(location)) {// 判断location是否为空字符 + ps.println(String.format(getFormat(FORMAT_NOTE_CONTENT), // 不为空则转换格式后打印location location)); } - } else if (DataConstants.NOTE.equals(mimeType)) { + } else if (DataConstants.NOTE.equals(mimeType)) {// 判断是否mimeType和NOTE一致 String content = dataCursor.getString(DATA_COLUMN_CONTENT); - if (!TextUtils.isEmpty(content)) { - ps.println(String.format(getFormat(FORMAT_NOTE_CONTENT), + if (!TextUtils.isEmpty(content)) {// 判断content是否为空字符 + ps.println(String.format(getFormat(FORMAT_NOTE_CONTENT), // 不为空则转换格式和FORMAT_NOTE_CONTENT一致后打印content content)); } } } while (dataCursor.moveToNext()); } - dataCursor.close(); + dataCursor.close();// 关闭dataCursor且释放内存 } - // print a line separator between note + // note之间打印line seperator try { ps.write(new byte[] { Character.LINE_SEPARATOR, Character.LETTER_NUMBER @@ -218,61 +221,63 @@ public class BackupUtils { /** * 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; + public int exportToText() {// 通过调用上面的定义过的exportFolder和exportNote方法实现导出note为text + if (!externalStorageAvailable()) {// 判断是否外部存储是被允许的 + Log.d(TAG, "Media was not mounted");// 不被允许就log提醒 + return STATE_SD_CARD_UNMOUONTED;// 返回sd卡为被装入手机,也就是返回0 } PrintStream ps = getExportToTextPrintStream(); - if (ps == null) { - Log.e(TAG, "get print stream error"); + if (ps == null) {// 判断ps是否为空 + Log.e(TAG, "get print stream error");// 为空则log提醒并返回STATE_SYSTEM_ERROR代表的数值,即返回3 return STATE_SYSTEM_ERROR; } // First export folder and its notes - Cursor folderCursor = mContext.getContentResolver().query( + Cursor folderCursor = mContext.getContentResolver().query(// 查询赋给folderCursor进行初始化 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); + + NoteColumns.ID + "=" + Notes.ID_CALL_RECORD_FOLDER, + null, null); - if (folderCursor != null) { + if (folderCursor != null) {// 赋值成功则从第一个开始遍历 if (folderCursor.moveToFirst()) { do { - // Print folder's name + // 针对两种情况对folderName赋值 String folderName = ""; - if(folderCursor.getLong(NOTE_COLUMN_ID) == Notes.ID_CALL_RECORD_FOLDER) { + 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)) { + if (!TextUtils.isEmpty(folderName)) {// 不为空则打印folderName ps.println(String.format(getFormat(FORMAT_FOLDER_NAME), folderName)); } String folderId = folderCursor.getString(NOTE_COLUMN_ID); - exportFolderToText(folderId, ps); + exportFolderToText(folderId, ps);// 以id为标识导出ps } while (folderCursor.moveToNext()); } folderCursor.close(); } - // Export notes in root's folder - Cursor noteCursor = mContext.getContentResolver().query( + // 将根目录里的便签导出(由于不属于任何文件夹,因此无法通过文件夹导出来实现这一部分便签的导出) + Cursor noteCursor = mContext.getContentResolver().query(// 查询PARENT_ID等于0且NoteColumns.TYPE等于Notes.TYPE_NOTE的结果赋给noteCursor Notes.CONTENT_NOTE_URI, NOTE_PROJECTION, NoteColumns.TYPE + "=" + +Notes.TYPE_NOTE + " AND " + NoteColumns.PARENT_ID - + "=0", null, null); + + "=0", + null, null);// 默认升序 if (noteCursor != null) { - if (noteCursor.moveToFirst()) { + 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); + exportNoteToText(noteId, ps);// 调用exportNoteToText进行导出 } while (noteCursor.moveToNext()); } noteCursor.close(); @@ -288,20 +293,20 @@ public class BackupUtils { private PrintStream getExportToTextPrintStream() { File file = generateFileMountedOnSDcard(mContext, R.string.file_path, R.string.file_name_txt_format); - if (file == null) { + if (file == null) {// file为空则产生file失败,log提醒后返回空 Log.e(TAG, "create file to exported failed"); return null; } - mFileName = file.getName(); - mFileDirectory = mContext.getString(R.string.file_path); + mFileName = file.getName();// 调用则返回一个字符串值,该值是给定File对象的名称 + mFileDirectory = mContext.getString(R.string.file_path);// 得到文件地址,返回string PrintStream ps = null; try { FileOutputStream fos = new FileOutputStream(file); - ps = new PrintStream(fos); - } catch (FileNotFoundException e) { + ps = new PrintStream(fos);// 给ps赋值 + } catch (FileNotFoundException e) {// 赋值失败,打开文件失败则将此可抛出对象及其回溯信息打印到标准错误流且返回空 e.printStackTrace(); return null; - } catch (NullPointerException e) { + } catch (NullPointerException e) {// 空指针异常则将此可抛出对象及其回溯信息打印到标准错误流且返回空 e.printStackTrace(); return null; } @@ -312,7 +317,7 @@ public class BackupUtils { /** * Generate the text file to store imported data */ - private static File generateFileMountedOnSDcard(Context context, int filePathResId, int fileNameFormatResId) { + private static File generateFileMountedOnSDcard(Context context, int filePathResId, int fileNameFormatResId) {// 产生一个文件并导出到SD卡上 StringBuilder sb = new StringBuilder(); sb.append(Environment.getExternalStorageDirectory()); sb.append(context.getString(filePathResId)); @@ -340,5 +345,3 @@ public class BackupUtils { return null; } } - - diff --git a/src/src/net/micode/notes/tool/DataUtils.java b/src/src/net/micode/notes/tool/DataUtils.java index 2a14982..faf01cf 100644 --- a/src/src/net/micode/notes/tool/DataUtils.java +++ b/src/src/net/micode/notes/tool/DataUtils.java @@ -16,6 +16,9 @@ package net.micode.notes.tool; +import java.util.ArrayList; +import java.util.HashSet; + import android.content.ContentProviderOperation; import android.content.ContentProviderResult; import android.content.ContentResolver; @@ -25,19 +28,16 @@ import android.content.OperationApplicationException; import android.database.Cursor; 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.NoteColumns; import net.micode.notes.ui.NotesListAdapter.AppWidgetAttribute; -import java.util.ArrayList; -import java.util.HashSet; - - public class DataUtils { public static final String TAG = "DataUtils"; - public static boolean batchDeleteNotes(ContentResolver resolver, HashSet ids) { + + public static boolean batchDeleteNotes(ContentResolver resolver, HashSet ids) {// 直接删除多个笔记 + if (ids == null) { Log.d(TAG, "the ids is null"); return true; @@ -47,36 +47,37 @@ public class DataUtils { return true; } - ArrayList operationList = new ArrayList(); + ArrayList operationList = new ArrayList();// 提供一个任务列表 + for (long id : ids) { - if(id == Notes.ID_ROOT_FOLDER) { + if (id == Notes.ID_ROOT_FOLDER) {// 如果发现是根文件夹,则不执行删除操作并且log提示 Log.e(TAG, "Don't delete system folder root"); continue; - } + } ContentProviderOperation.Builder builder = ContentProviderOperation - .newDelete(ContentUris.withAppendedId(Notes.CONTENT_NOTE_URI, id)); + .newDelete(ContentUris.withAppendedId(Notes.CONTENT_NOTE_URI, id)); // 调用newDelete方法进行删除 operationList.add(builder.build()); } try { - ContentProviderResult[] results = resolver.applyBatch(Notes.AUTHORITY, operationList); + ContentProviderResult[] results = resolver.applyBatch(Notes.AUTHORITY, operationList);// 主机名(或叫Authority)用于唯一标识这个ContentProvider,外部调用者可以根据这个标识来找到它。 if (results == null || results.length == 0 || results[0] == null) { - Log.d(TAG, "delete notes failed, ids:" + ids.toString()); - return false; + Log.d(TAG, "delete notes failed, ids:" + ids.toString());//log提醒ids为当下字符串的notes删除失败 + return false;//返回失败 } - return true; - } catch (RemoteException e) { + return true;//返回成功 + } catch (RemoteException e) {//出现远程异常 Log.e(TAG, String.format("%s: %s", e.toString(), e.getMessage())); - } catch (OperationApplicationException e) { + } catch (OperationApplicationException e) {//出现操作应用异常 Log.e(TAG, String.format("%s: %s", e.toString(), e.getMessage())); } return false; } - public static void moveNoteToFoler(ContentResolver resolver, long id, long srcFolderId, long desFolderId) { - ContentValues values = new ContentValues(); - values.put(NoteColumns.PARENT_ID, desFolderId); - values.put(NoteColumns.ORIGIN_PARENT_ID, srcFolderId); - values.put(NoteColumns.LOCAL_MODIFIED, 1); + public static void moveNoteToFoler(ContentResolver resolver, long id, long srcFolderId, long desFolderId) {// 对需要移动的便签进行数据更新,然后用update实现 + ContentValues values = new ContentValues();//new一个只能存储基本类型的数据的contentValues + values.put(NoteColumns.PARENT_ID, desFolderId);//使用put插入数据 + values.put(NoteColumns.ORIGIN_PARENT_ID, srcFolderId);//将移动前后的FolderId都插入 + values.put(NoteColumns.LOCAL_MODIFIED, 1);//标记此folder为已修改 resolver.update(ContentUris.withAppendedId(Notes.CONTENT_NOTE_URI, id), values, null, null); } @@ -90,22 +91,22 @@ public class DataUtils { ArrayList operationList = new ArrayList(); for (long id : ids) { ContentProviderOperation.Builder builder = ContentProviderOperation - .newUpdate(ContentUris.withAppendedId(Notes.CONTENT_NOTE_URI, id)); + .newUpdate(ContentUris.withAppendedId(Notes.CONTENT_NOTE_URI, id)); // 通过withAppendedId方法,为该Uri加上ID builder.withValue(NoteColumns.PARENT_ID, folderId); builder.withValue(NoteColumns.LOCAL_MODIFIED, 1); operationList.add(builder.build()); - } + } // 将ids里包含的每一列的数据逐次加入到operationList中,等待最后的批量处理 - try { + try { // applyBatch一次性处理一个操作列表 ContentProviderResult[] results = resolver.applyBatch(Notes.AUTHORITY, operationList); if (results == null || results.length == 0 || results[0] == null) { Log.d(TAG, "delete notes failed, ids:" + ids.toString()); return false; } return true; - } catch (RemoteException e) { + } catch (RemoteException e) {//RemoteException 由于系统断电,内存不足等原因导致dataNode丢失超过设置的丢失百分比,系统自动进入安全模式 Log.e(TAG, String.format("%s: %s", e.toString(), e.getMessage())); - } catch (OperationApplicationException e) { + } catch (OperationApplicationException e) {//操作应用异常 Log.e(TAG, String.format("%s: %s", e.toString(), e.getMessage())); } return false; @@ -115,18 +116,18 @@ public class DataUtils { * Get the all folder count except system folders {@link Notes#TYPE_SYSTEM}} */ public static int getUserFolderCount(ContentResolver resolver) { - Cursor cursor =resolver.query(Notes.CONTENT_NOTE_URI, + Cursor cursor = resolver.query(Notes.CONTENT_NOTE_URI, new String[] { "COUNT(*)" }, NoteColumns.TYPE + "=? AND " + NoteColumns.PARENT_ID + "<>?", - new String[] { String.valueOf(Notes.TYPE_FOLDER), String.valueOf(Notes.ID_TRASH_FOLER)}, - null); + new String[] { String.valueOf(Notes.TYPE_FOLDER), String.valueOf(Notes.ID_TRASH_FOLER) }, + null); // applyBatch一次性处理一个操作列表 int count = 0; - if(cursor != null) { - if(cursor.moveToFirst()) { + if (cursor != null) { + if (cursor.moveToFirst()) { try { count = cursor.getInt(0); - } catch (IndexOutOfBoundsException e) { + } catch (IndexOutOfBoundsException e) {//索引边界异常 Log.e(TAG, "get folder count failed:" + e.toString()); } finally { cursor.close(); @@ -137,11 +138,12 @@ public class DataUtils { } public static boolean visibleInNoteDatabase(ContentResolver resolver, long noteId, int type) { - Cursor cursor = resolver.query(ContentUris.withAppendedId(Notes.CONTENT_NOTE_URI, noteId), + Cursor cursor = resolver.query(ContentUris.withAppendedId(Notes.CONTENT_NOTE_URI, noteId), // 通过withAppendedId方法,为该Uri加上ID + null, NoteColumns.TYPE + "=? AND " + NoteColumns.PARENT_ID + "<>" + Notes.ID_TRASH_FOLER, - new String [] {String.valueOf(type)}, - null); + new String[] { String.valueOf(type) }, + null);// 查询条件:type符合,且不属于垃圾文件夹 boolean exist = false; if (cursor != null) { @@ -159,7 +161,7 @@ public class DataUtils { boolean exist = false; if (cursor != null) { - if (cursor.getCount() > 0) { + if (cursor.getCount() > 0) {// 用getcount函数判断cursor是否为空 exist = true; } cursor.close(); @@ -184,12 +186,13 @@ public class DataUtils { public static boolean checkVisibleFolderName(ContentResolver resolver, String name) { Cursor cursor = resolver.query(Notes.CONTENT_NOTE_URI, null, NoteColumns.TYPE + "=" + Notes.TYPE_FOLDER + - " AND " + NoteColumns.PARENT_ID + "<>" + Notes.ID_TRASH_FOLER + - " AND " + NoteColumns.SNIPPET + "=?", + " 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) { + // 通过名字查询文件是否存在 + if (cursor != null) { + if (cursor.getCount() > 0) { exist = true; } cursor.close(); @@ -202,7 +205,7 @@ public class DataUtils { new String[] { NoteColumns.WIDGET_ID, NoteColumns.WIDGET_TYPE }, NoteColumns.PARENT_ID + "=?", new String[] { String.valueOf(folderId) }, - null); + null);// 查询条件:父ID是传入的folderId; HashSet set = null; if (c != null) { @@ -211,7 +214,7 @@ public class DataUtils { do { try { AppWidgetAttribute widget = new AppWidgetAttribute(); - widget.widgetId = c.getInt(0); + widget.widgetId = c.getInt(0);// 0对应的NoteColumns.WIDGET_I,1对应的NoteColumns.WIDGET_TYPE widget.widgetType = c.getInt(1); set.add(widget); } catch (IndexOutOfBoundsException e) { @@ -226,9 +229,9 @@ public class DataUtils { public static String getCallNumberByNoteId(ContentResolver resolver, long noteId) { Cursor cursor = resolver.query(Notes.CONTENT_DATA_URI, - new String [] { CallNote.PHONE_NUMBER }, + new String[] { CallNote.PHONE_NUMBER }, CallNote.NOTE_ID + "=? AND " + CallNote.MIME_TYPE + "=?", - new String [] { String.valueOf(noteId), CallNote.CONTENT_ITEM_TYPE }, + new String[] { String.valueOf(noteId), CallNote.CONTENT_ITEM_TYPE }, null); if (cursor != null && cursor.moveToFirst()) { @@ -245,16 +248,16 @@ public class DataUtils { public static long getNoteIdByPhoneNumberAndCallDate(ContentResolver resolver, String phoneNumber, long callDate) { Cursor cursor = resolver.query(Notes.CONTENT_DATA_URI, - new String [] { CallNote.NOTE_ID }, + new String[] { CallNote.NOTE_ID }, CallNote.CALL_DATE + "=? AND " + CallNote.MIME_TYPE + "=? AND PHONE_NUMBERS_EQUAL(" - + CallNote.PHONE_NUMBER + ",?)", - new String [] { String.valueOf(callDate), CallNote.CONTENT_ITEM_TYPE, phoneNumber }, - null); + + CallNote.PHONE_NUMBER + ",?)", + new String[] { String.valueOf(callDate), CallNote.CONTENT_ITEM_TYPE, phoneNumber }, + null);// 通过数据库操作,查询条件是(callDate和phoneNumber匹配传入参数的值) if (cursor != null) { if (cursor.moveToFirst()) { try { - return cursor.getLong(0); + return cursor.getLong(0);// 0对应的CallNote.NOTE_ID } catch (IndexOutOfBoundsException e) { Log.e(TAG, "Get call note id fails " + e.toString()); } @@ -266,9 +269,9 @@ public class DataUtils { public static String getSnippetById(ContentResolver resolver, long noteId) { Cursor cursor = resolver.query(Notes.CONTENT_NOTE_URI, - new String [] { NoteColumns.SNIPPET }, + new String[] { NoteColumns.SNIPPET }, NoteColumns.ID + "=?", - new String [] { String.valueOf(noteId)}, + new String[] { String.valueOf(noteId) }, null); if (cursor != null) { @@ -282,9 +285,10 @@ public class DataUtils { throw new IllegalArgumentException("Note is not found with id: " + noteId); } - public static String getFormattedSnippet(String snippet) { + public static String getFormattedSnippet(String snippet) {// 对字符串进行格式处理,将字符串两头的空格去掉,同时将换行符去掉 if (snippet != null) { snippet = snippet.trim(); + int index = snippet.indexOf('\n'); if (index != -1) { snippet = snippet.substring(0, index); diff --git a/src/src/net/micode/notes/tool/GTaskStringUtils.java b/src/src/net/micode/notes/tool/GTaskStringUtils.java index 666b729..6e259f5 100644 --- a/src/src/net/micode/notes/tool/GTaskStringUtils.java +++ b/src/src/net/micode/notes/tool/GTaskStringUtils.java @@ -13,7 +13,7 @@ * See the License for the specific language governing permissions and * limitations under the License. */ - +//只定义了很多的静态字符串,为jsonObject提供相应字符串的"key" package net.micode.notes.tool; public class GTaskStringUtils { diff --git a/src/src/net/micode/notes/tool/ResourceParser.java b/src/src/net/micode/notes/tool/ResourceParser.java index 1ad3ad6..389a9ca 100644 --- a/src/src/net/micode/notes/tool/ResourceParser.java +++ b/src/src/net/micode/notes/tool/ResourceParser.java @@ -16,44 +16,44 @@ package net.micode.notes.tool; -import android.content.Context; -import android.preference.PreferenceManager; +import javax.naming.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 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 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_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 + 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) { @@ -65,6 +65,7 @@ public class ResourceParser { } } + // 直接获取默认的背景颜色。 public static int getDefaultBgId(Context context) { if (PreferenceManager.getDefaultSharedPreferences(context).getBoolean( NotesPreferenceActivity.PREFERENCE_SET_BG_COLOR_KEY, false)) { @@ -75,36 +76,36 @@ public class ResourceParser { } 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_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_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_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 + 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) { @@ -129,24 +130,24 @@ public class ResourceParser { } 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, + 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 + 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) { @@ -155,13 +156,14 @@ public class ResourceParser { } public static class TextAppearanceResources { - private final static int [] TEXTAPPEARANCE_RESOURCES = new int [] { - R.style.TextAppearanceNormal, - R.style.TextAppearanceMedium, - R.style.TextAppearanceLarge, - R.style.TextAppearanceSuper + private final static int[] TEXTAPPEARANCE_RESOURCES = new int[] { + R.style.TextAppearanceNormal, + R.style.TextAppearanceMedium, + R.style.TextAppearanceLarge, + R.style.TextAppearanceSuper }; + // 这里有一个容错的函数,防止输入的id大于资源总量,若如此,则自动返回默认的设置结果 public static int getTexAppearanceResource(int id) { /** * HACKME: Fix bug of store the resource id in shared preference. diff --git a/src/src/net/micode/notes/ui/NotesListAdapter.java b/src/src/net/micode/notes/ui/NotesListAdapter.java index 51c9cb9..19594d2 100644 --- a/src/src/net/micode/notes/ui/NotesListAdapter.java +++ b/src/src/net/micode/notes/ui/NotesListAdapter.java @@ -1,19 +1,3 @@ -/* - * 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; @@ -23,6 +7,7 @@ import android.view.View; import android.view.ViewGroup; import android.widget.CursorAdapter; + import net.micode.notes.data.Notes; import java.util.Collection; @@ -31,55 +16,93 @@ 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; + 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(); + 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); } + /* + * 函数功能:将已经存在的视图和鼠标指向的数据进行捆绑 + * 函数实现:如果视图是NotesListItem的实例,就创建一个NoteItemData对象,并使用bind方法将视图与鼠标、内容和便签数据绑定在一起。 + */ @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) { @@ -87,10 +110,16 @@ public class NotesListAdapter extends CursorAdapter { } } } + //遍历所有光标可用的位置在判断为便签类型之后勾选单项框 } + /* + * 函数功能:建立选择项的下标列表 + * 函数实现:通过遍历所有键,并判断其对应的值是否为true来确定选中项。然后获取每个选中项的下标对应的id,并将其加入到一个HashSet中。最后返回这个HashSet。 + */ public HashSet getSelectedItemIds() { HashSet itemSet = new HashSet(); + //建立hash表 for (Integer position : mSelectedIndex.keySet()) { if (mSelectedIndex.get(position) == true) { Long id = getItemId(position); @@ -101,16 +130,21 @@ public class NotesListAdapter extends CursorAdapter { } } } - 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(); @@ -128,27 +162,44 @@ public class NotesListAdapter extends CursorAdapter { 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) { + // 如果下标不存在,则返回false if (null == mSelectedIndex.get(position)) { return false; } @@ -156,24 +207,37 @@ public class NotesListAdapter extends CursorAdapter { } @Override + /* + * 函数功能:在activity内容发生局部变动的时候回调该函数计算便签的数量 + * 函数实现:函数首先调用父类的onContentChanged函数,然后调用calcNotesCount函数来执行计算。 + */ protected void onContentChanged() { super.onContentChanged(); calcNotesCount(); } @Override + /* + * 函数功能:在activity光标发生局部变动的时候回调该函数计算便签的数量 + */ + // 首先调用父类的changeCursor函数,然后调用自己的calcNotesCount函数进行计算。 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"); @@ -181,4 +245,4 @@ public class NotesListAdapter extends CursorAdapter { } } } -} +} \ No newline at end of file