diff --git a/src/src/net/micode/notes/tool/BackupUtils.java b/src/src/net/micode/notes/tool/BackupUtils.java deleted file mode 100644 index 39f6ec4..0000000 --- a/src/src/net/micode/notes/tool/BackupUtils.java +++ /dev/null @@ -1,344 +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; - - 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/src/src/net/micode/notes/tool/ResourceParser.java b/src/src/net/micode/notes/tool/ResourceParser.java deleted file mode 100644 index 1ad3ad6..0000000 --- a/src/src/net/micode/notes/tool/ResourceParser.java +++ /dev/null @@ -1,181 +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 { - 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/src/src/net/micode/notes/tool/tool/BackupUtils.java b/src/src/net/micode/notes/tool/tool/BackupUtils.java new file mode 100644 index 0000000..72bb8d8 --- /dev/null +++ b/src/src/net/micode/notes/tool/tool/BackupUtils.java @@ -0,0 +1,348 @@ +/* + * 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"; + // 单例相关 + private static BackupUtils sInstance; + + public static synchronized BackupUtils getInstance(Context context) { + if (sInstance == null) { + sInstance = new BackupUtils(context); + } + return sInstance; + } + + /** + * 以下状态用于表示备份或还原的状态 + */ + // 当前SD卡未挂载 + public static final int STATE_SD_CARD_UNMOUNTED = 0; + // 备份文件不存在 + public static final int STATE_BACKUP_FILE_NOT_EXIST = 1; + // 数据格式不正确,可能被其他程序更改 + public static final int STATE_DATA_DESTROYED = 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 = { + 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]; + } + + /** + * 导出特定文件夹的笔记到文本 + */ + public int exportToText() { + // 导出文件的逻辑 + } + } + + 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标识的笔记导出到打印流中 + */ + 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)) { + // 打印电话号码 + 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()); + } + } + + /** + * 将笔记导出为用户可读的文本 + */ + public int exportToText() { + if (!externalStorageAvailable()) { + Log.d(TAG, "Media was not mounted"); + return STATE_SD_CARD_UNMOUNTED; + } + + 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, + NOTE_PROJECTION, + "(" + NoteColumns.TYPE + "=" + Notes.TYPE_FOLDER + " AND " + + NoteColumns.PARENT_ID + "<>" + Notes.ID_TRASH_FOLDER + ") OR " + + NoteColumns.ID + "=" + Notes.ID_CALL_RECORD_FOLDER, null, 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, + 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)))); + // 查询属于该笔记的数据 + String noteId = noteCursor.getString(NOTE_COLUMN_ID); + exportNoteToText(noteId, ps); + } while (noteCursor.moveToNext()); + } + noteCursor.close(); + } + ps.close(); + + return STATE_SUCCESS; + } + + /** + * 获取指向文件{@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 export 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; + } + + /** + * 生成用于存储导出数据的文本文件 + */ + 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/src/src/net/micode/notes/tool/DataUtils.java b/src/src/net/micode/notes/tool/tool/DataUtils.java similarity index 88% rename from src/src/net/micode/notes/tool/DataUtils.java rename to src/src/net/micode/notes/tool/tool/DataUtils.java index 2a14982..085e268 100644 --- a/src/src/net/micode/notes/tool/DataUtils.java +++ b/src/src/net/micode/notes/tool/tool/DataUtils.java @@ -37,6 +37,13 @@ import java.util.HashSet; public class DataUtils { public static final String TAG = "DataUtils"; + + /** + * 批量删除笔记 + * @param resolver ContentResolver对象 + * @param ids 要删除的笔记ID集合 + * @return 是否删除成功 + */ public static boolean batchDeleteNotes(ContentResolver resolver, HashSet ids) { if (ids == null) { Log.d(TAG, "the ids is null"); @@ -72,6 +79,13 @@ public class DataUtils { 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 values = new ContentValues(); values.put(NoteColumns.PARENT_ID, desFolderId); @@ -80,8 +94,14 @@ public class DataUtils { resolver.update(ContentUris.withAppendedId(Notes.CONTENT_NOTE_URI, id), values, null, null); } - public static boolean batchMoveToFolder(ContentResolver resolver, HashSet ids, - long folderId) { + /** + * 批量将笔记移动到文件夹 + * @param resolver ContentResolver对象 + * @param ids 要移动的笔记ID集合 + * @param folderId 目标文件夹ID + * @return 是否移动成功 + */ + public static boolean batchMoveToFolder(ContentResolver resolver, HashSet ids, long folderId) { if (ids == null) { Log.d(TAG, "the ids is null"); return true; @@ -112,10 +132,13 @@ public class DataUtils { } /** - * Get the all folder count except system folders {@link Notes#TYPE_SYSTEM}} + * 获取除系统文件夹外的所有文件夹数量 + * @param resolver ContentResolver对象 + * @return 文件夹数量 */ 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)}, @@ -127,7 +150,7 @@ public class DataUtils { try { count = cursor.getInt(0); } catch (IndexOutOfBoundsException e) { - Log.e(TAG, "get folder count failed:" + e.toString()); + Log.e(TAG, "获取文件夹数量失败:" + e.toString()); } finally { cursor.close(); } @@ -137,6 +160,7 @@ public class DataUtils { } public static boolean visibleInNoteDatabase(ContentResolver resolver, long noteId, int type) { + // 检查笔记是否在数据库中可见 Cursor cursor = resolver.query(ContentUris.withAppendedId(Notes.CONTENT_NOTE_URI, noteId), null, NoteColumns.TYPE + "=? AND " + NoteColumns.PARENT_ID + "<>" + Notes.ID_TRASH_FOLER, @@ -154,6 +178,7 @@ public class DataUtils { } public static boolean existInNoteDatabase(ContentResolver resolver, long noteId) { + // 检查笔记是否存在于数据库中 Cursor cursor = resolver.query(ContentUris.withAppendedId(Notes.CONTENT_NOTE_URI, noteId), null, null, null, null); @@ -168,6 +193,7 @@ public class DataUtils { } public static boolean existInDataDatabase(ContentResolver resolver, long dataId) { + // 检查数据是否存在于数据库中 Cursor cursor = resolver.query(ContentUris.withAppendedId(Notes.CONTENT_DATA_URI, dataId), null, null, null, null); @@ -182,6 +208,7 @@ 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 + @@ -198,6 +225,7 @@ public class DataUtils { } public static HashSet getFolderNoteWidget(ContentResolver resolver, long folderId) { + // 获取文件夹笔记的小部件 Cursor c = resolver.query(Notes.CONTENT_NOTE_URI, new String[] { NoteColumns.WIDGET_ID, NoteColumns.WIDGET_TYPE }, NoteColumns.PARENT_ID + "=?", @@ -225,6 +253,7 @@ public class DataUtils { } public static String getCallNumberByNoteId(ContentResolver resolver, long noteId) { + // 根据笔记ID获取通话号码 Cursor cursor = resolver.query(Notes.CONTENT_DATA_URI, new String [] { CallNote.PHONE_NUMBER }, CallNote.NOTE_ID + "=? AND " + CallNote.MIME_TYPE + "=?", @@ -235,15 +264,16 @@ public class DataUtils { try { return cursor.getString(0); } catch (IndexOutOfBoundsException e) { - Log.e(TAG, "Get call number fails " + e.toString()); + Log.e(TAG, "获取通话号码失败:" + e.toString()); } finally { cursor.close(); } } - return ""; + return ""; } public static long getNoteIdByPhoneNumberAndCallDate(ContentResolver resolver, String phoneNumber, long callDate) { + // 根据电话号码和通话日期获取笔记ID Cursor cursor = resolver.query(Notes.CONTENT_DATA_URI, new String [] { CallNote.NOTE_ID }, CallNote.CALL_DATE + "=? AND " + CallNote.MIME_TYPE + "=? AND PHONE_NUMBERS_EQUAL(" @@ -256,7 +286,7 @@ public class DataUtils { try { return cursor.getLong(0); } catch (IndexOutOfBoundsException e) { - Log.e(TAG, "Get call note id fails " + e.toString()); + Log.e(TAG, "获取通话笔记ID失败:" + e.toString()); } } cursor.close(); @@ -265,6 +295,7 @@ public class DataUtils { } public static String getSnippetById(ContentResolver resolver, long noteId) { + // 根据笔记ID获取摘要 Cursor cursor = resolver.query(Notes.CONTENT_NOTE_URI, new String [] { NoteColumns.SNIPPET }, NoteColumns.ID + "=?", @@ -279,10 +310,11 @@ public class DataUtils { cursor.close(); return snippet; } - throw new IllegalArgumentException("Note is not found with id: " + noteId); + throw new IllegalArgumentException("找不到ID为 " + noteId + " 的笔记"); } public static String getFormattedSnippet(String snippet) { + // 获取格式化后的摘要 if (snippet != null) { snippet = snippet.trim(); int index = snippet.indexOf('\n'); diff --git a/src/src/net/micode/notes/tool/GTaskStringUtils.java b/src/src/net/micode/notes/tool/tool/GTaskStringUtils.java similarity index 53% rename from src/src/net/micode/notes/tool/GTaskStringUtils.java rename to src/src/net/micode/notes/tool/tool/GTaskStringUtils.java index 666b729..dedea87 100644 --- a/src/src/net/micode/notes/tool/GTaskStringUtils.java +++ b/src/src/net/micode/notes/tool/tool/GTaskStringUtils.java @@ -18,96 +18,51 @@ 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"; + public final static String GTASK_JSON_ACTION_ID = "action_id"; // 动作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"; // 创建者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"; // 当前列表ID + public final static String GTASK_JSON_DEFAULT_LIST_ID = "default_list_id"; // 默认列表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"; // 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"; // 列表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"; // 新ID + public final static String GTASK_JSON_NOTES = "notes"; // 笔记 + public final static String GTASK_JSON_PARENT_ID = "parent_id"; // 父级ID + public final static String GTASK_JSON_PRIOR_SIBLING_ID = "prior_sibling_id"; // 上一个同级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]"; // MIUI文件夹前缀 + 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"; // 元数据GTask ID + 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/tool/tool/ResourceParser.java b/src/src/net/micode/notes/tool/tool/ResourceParser.java new file mode 100644 index 0000000..7926586 --- /dev/null +++ b/src/src/net/micode/notes/tool/tool/ResourceParser.java @@ -0,0 +1,209 @@ +/* + * 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]; + } + } + + // 获取默认背景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 + }; + + // 普通背景资源数组 + privatefinal 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 { + // 2x背景资源数组 + 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, + }; + + // 获取2x小部件背景资源 + public static int getWidget2xBgResource(int id) { + return BG_2X_RESOURCES[id]; + } + + // 4x背景资源数组 + 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 + }; + + // 获取4x小部件背景资源 + 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: 修复共享首选项中存储资源ID的错误。 + * ID 可能大于资源数组的长度,在这种情况下,返回 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/src/src/net/micode/notes/ui/AlarmAlertActivity.java b/src/src/net/micode/notes/ui/AlarmAlertActivity.java deleted file mode 100644 index 85723be..0000000 --- a/src/src/net/micode/notes/ui/AlarmAlertActivity.java +++ /dev/null @@ -1,158 +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.content.Context; -import android.content.DialogInterface; -import android.content.DialogInterface.OnClickListener; -import android.content.DialogInterface.OnDismissListener; -import android.content.Intent; -import android.media.AudioManager; -import android.media.MediaPlayer; -import android.media.RingtoneManager; -import android.net.Uri; -import android.os.Bundle; -import android.os.PowerManager; -import android.provider.Settings; -import android.view.Window; -import android.view.WindowManager; - -import net.micode.notes.R; -import net.micode.notes.data.Notes; -import net.micode.notes.tool.DataUtils; - -import java.io.IOException; - - -public class AlarmAlertActivity extends Activity implements OnClickListener, OnDismissListener { - private long mNoteId; - private String mSnippet; - private static final int SNIPPET_PREW_MAX_LEN = 60; - MediaPlayer mPlayer; - - @Override - protected void onCreate(Bundle savedInstanceState) { - super.onCreate(savedInstanceState); - requestWindowFeature(Window.FEATURE_NO_TITLE); - - 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 - | WindowManager.LayoutParams.FLAG_ALLOW_LOCK_WHILE_SCREEN_ON - | WindowManager.LayoutParams.FLAG_LAYOUT_INSET_DECOR); - } - - Intent intent = getIntent(); - - try { - mNoteId = Long.valueOf(intent.getData().getPathSegments().get(1)); - mSnippet = DataUtils.getSnippetById(this.getContentResolver(), mNoteId); - mSnippet = mSnippet.length() > SNIPPET_PREW_MAX_LEN ? mSnippet.substring(0, - SNIPPET_PREW_MAX_LEN) + getResources().getString(R.string.notelist_string_info) - : mSnippet; - } catch (IllegalArgumentException e) { - e.printStackTrace(); - return; - } - - mPlayer = new MediaPlayer(); - if (DataUtils.visibleInNoteDatabase(getContentResolver(), mNoteId, Notes.TYPE_NOTE)) { - showActionDialog(); - playAlarmSound(); - } else { - finish(); - } - } - - private boolean isScreenOn() { - PowerManager pm = (PowerManager) getSystemService(Context.POWER_SERVICE); - return pm.isScreenOn(); - } - - private void playAlarmSound() { - Uri url = RingtoneManager.getActualDefaultRingtoneUri(this, RingtoneManager.TYPE_ALARM); - - int silentModeStreams = Settings.System.getInt(getContentResolver(), - Settings.System.MODE_RINGER_STREAMS_AFFECTED, 0); - - 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); - } - - 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); - startActivity(intent); - break; - default: - break; - } - } - - public void onDismiss(DialogInterface dialog) { - stopAlarmSound(); - finish(); - } - - private void stopAlarmSound() { - if (mPlayer != null) { - mPlayer.stop(); - mPlayer.release(); - mPlayer = null; - } - } -} diff --git a/src/src/net/micode/notes/ui/DateTimePicker.java b/src/src/net/micode/notes/ui/DateTimePicker.java deleted file mode 100644 index 496b0cd..0000000 --- a/src/src/net/micode/notes/ui/DateTimePicker.java +++ /dev/null @@ -1,485 +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.text.DateFormatSymbols; -import java.util.Calendar; - -import net.micode.notes.R; - - -import android.content.Context; -import android.text.format.DateFormat; -import android.view.View; -import android.widget.FrameLayout; -import android.widget.NumberPicker; - -public class DateTimePicker extends 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; - private 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(); - } - }; - - private NumberPicker.OnValueChangeListener mOnHourChangedListener = new NumberPicker.OnValueChangeListener() { - @Override - public void onValueChange(NumberPicker picker, int oldVal, int newVal) { - boolean isDateChanged = false; - Calendar cal = Calendar.getInstance(); - 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; - } 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; - } - 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(); - } - } else { - if (oldVal == HOURS_IN_ALL_DAY - 1 && newVal == 0) { - cal.setTimeInMillis(mDate.getTimeInMillis()); - cal.add(Calendar.DAY_OF_YEAR, 1); - isDateChanged = true; - } else if (oldVal == 0 && newVal == HOURS_IN_ALL_DAY - 1) { - cal.setTimeInMillis(mDate.getTimeInMillis()); - cal.add(Calendar.DAY_OF_YEAR, -1); - isDateChanged = true; - } - } - int newHour = mHourSpinner.getValue() % HOURS_IN_HALF_DAY + (mIsAm ? 0 : HOURS_IN_HALF_DAY); - mDate.set(Calendar.HOUR_OF_DAY, newHour); - 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 - public void onValueChange(NumberPicker picker, int oldVal, int newVal) { - int minValue = mMinuteSpinner.getMinValue(); - int maxValue = mMinuteSpinner.getMaxValue(); - int offset = 0; - if (oldVal == maxValue && newVal == minValue) { - offset += 1; - } else if (oldVal == minValue && newVal == maxValue) { - 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() { - @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)); - } - - 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); - - 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; - } - - @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)); - } - - /** - * 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 - */ - 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/src/src/net/micode/notes/ui/ui/AlarmAlertActivity.java b/src/src/net/micode/notes/ui/ui/AlarmAlertActivity.java new file mode 100644 index 0000000..e6f3b3f --- /dev/null +++ b/src/src/net/micode/notes/ui/ui/AlarmAlertActivity.java @@ -0,0 +1,185 @@ +/* + * 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.content.Context; +import android.content.DialogInterface; +import android.content.DialogInterface.OnClickListener; +import android.content.DialogInterface.OnDismissListener; +import android.content.Intent; +import android.media.AudioManager; +import android.media.MediaPlayer; +import android.media.RingtoneManager; +import android.net.Uri; +import android.os.Bundle; +import android.os.PowerManager; +import android.provider.Settings; +import android.view.Window; +import android.view.WindowManager; + +import net.micode.notes.R; +import net.micode.notes.data.Notes; +import net.micode.notes.tool.DataUtils; + +import java.io.IOException; + +//定义一个名为AlarmAlertActivity的公开类,该类继承自Activity类。在Android中,Activity是应用组件,用于与用户交互。 +public class AlarmAlertActivity extends Activity implements OnClickListener, OnDismissListener { + + // 定义一个私有长整型变量mNoteId,可能用于存储与提醒相关的笔记ID。 + private long mNoteId; + // 定义一个私有字符串变量mSnippet,可能用于存储与提醒相关的小片段信息(如文本、音频等)。 + private String mSnippet; + // 定义一个静态常量SNIPPET_PREW_MAX_LEN,其值为60,可能用于限制mSnippet的最大长度。 + private static final int SNIPPET_PREW_MAX_LEN = 60; + // 定义一个私有MediaPlayer对象mPlayer,可能用于播放音频/音乐。 + MediaPlayer mPlayer; + + @Override + protected void onCreate(Bundle savedInstanceState) { + super.onCreate(savedInstanceState); + requestWindowFeature(Window.FEATURE_NO_TITLE); + + 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 + | WindowManager.LayoutParams.FLAG_ALLOW_LOCK_WHILE_SCREEN_ON + | WindowManager.LayoutParams.FLAG_LAYOUT_INSET_DECOR); + } + + Intent intent = getIntent(); + + try { + // 从Intent中获取与提醒相关的笔记ID,并存储到mNoteId变量中。 + mNoteId = Long.valueOf(intent.getData().getPathSegments().get(1)); + // 从数据工具类中获取与mNoteId对应的提醒片段,并存储到mSnippet变量中。 + mSnippet = DataUtils.getSnippetById(this.getContentResolver(), mNoteId); + // 如果mSnippet的长度超过SNIPPET_PREW_MAX_LEN,则截取前SNIPPET_PREW_MAX_LEN个字符,并附加字符串"..."。 + mSnippet = mSnippet.length() > SNIPPET_PREW_MAX_LEN ? mSnippet.substring(0, + SNIPPET_PREW_MAX_LEN) + getResources().getString(R.string.notelist_string_info) + : mSnippet; + } catch (IllegalArgumentException e) { + e.printStackTrace(); + return; + } + + mPlayer = new MediaPlayer(); + if (DataUtils.visibleInNoteDatabase(getContentResolver(), mNoteId, Notes.TYPE_NOTE)) { + // 显示带有提醒片段的对话框。 + showActionDialog(); + // 播放提醒音频。 + playAlarmSound(); + } else { + finish(); + } + } + + // 检查屏幕是否亮着。 + 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) { + // 如果闹钟铃声受铃声模式影响,则设置MediaPlayer的音频流为silentModeStreams。 + mPlayer.setAudioStreamType(silentModeStreams); + } else { + // 否则,设置MediaPlayer的音频流为闹钟铃声流。 + mPlayer.setAudioStreamType(AudioManager.STREAM_ALARM); + } + try { + // 设置MediaPlayer的数据源为闹钟铃声的URI。 + mPlayer.setDataSource(this, url); + // 准备MediaPlayer的播放器。 + mPlayer.prepare(); + // 设置MediaPlayer循环播放。 + mPlayer.setLooping(true); + // 开始播放音频。 + mPlayer.start(); + } catch (IllegalArgumentException e) { + e.printStackTrace(); + } catch (SecurityException e) { + e.printStackTrace(); + } catch (IllegalStateException e) { + 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); + } + + // 对话框按钮点击事件的处理。 + 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); + startActivity(intent); + break; + default: + break; + } + } + + // 对话框关闭事件的处理。 + public void onDismiss(DialogInterface dialog) { + // 停止播放音频。 + stopAlarmSound(); + // 结束当前Activity。 + finish(); + } + + // 停止播放音频。 + private void stopAlarmSound() { + if (mPlayer != null) { + // 停止MediaPlayer的播放。 + mPlayer.stop(); + // 释放MediaPlayer的资源。 + mPlayer.release(); + mPlayer = null; + } + } +} diff --git a/src/src/net/micode/notes/ui/AlarmInitReceiver.java b/src/src/net/micode/notes/ui/ui/AlarmInitReceiver.java similarity index 65% rename from src/src/net/micode/notes/ui/AlarmInitReceiver.java rename to src/src/net/micode/notes/ui/ui/AlarmInitReceiver.java index f221202..89fbd55 100644 --- a/src/src/net/micode/notes/ui/AlarmInitReceiver.java +++ b/src/src/net/micode/notes/ui/ui/AlarmInitReceiver.java @@ -27,36 +27,44 @@ import android.database.Cursor; import net.micode.notes.data.Notes; import net.micode.notes.data.Notes.NoteColumns; - public class AlarmInitReceiver extends BroadcastReceiver { - private static final String [] PROJECTION = new String [] { - NoteColumns.ID, - NoteColumns.ALERTED_DATE + // 定义一个字符串数组PROJECTION,用于指定查询提醒相关信息时返回的列。 + private static final String[] PROJECTION = new String[]{ + NoteColumns.ID, + NoteColumns.ALERTED_DATE }; - private static final int COLUMN_ID = 0; - private static final int COLUMN_ALERTED_DATE = 1; + // 定义一些常量,用于指定PROJECTION数组中的列索引。 + private static final int COLUMN_ID = 0; + private static final int COLUMN_ALERTED_DATE = 1; @Override public void onReceive(Context context, Intent intent) { + // 获取当前时间的毫秒值。 long currentDate = System.currentTimeMillis(); + // 使用ContentResolver查询数据库,获取所有提醒日期大于当前时间的提醒记录。 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 { + // 获取提醒日期的毫秒值。 long alertDate = c.getLong(COLUMN_ALERTED_DATE); + // 创建一个新的Intent,指向AlarmReceiver类。 Intent sender = new Intent(context, AlarmReceiver.class); + // 设置Intent的数据为提醒记录的URI。 sender.setData(ContentUris.withAppendedId(Notes.CONTENT_NOTE_URI, c.getLong(COLUMN_ID))); + // 创建一个PendingIntent,用于发送广播。 PendingIntent pendingIntent = PendingIntent.getBroadcast(context, 0, sender, 0); - AlarmManager alermManager = (AlarmManager) context - .getSystemService(Context.ALARM_SERVICE); - alermManager.set(AlarmManager.RTC_WAKEUP, alertDate, pendingIntent); + // 获取系统的闹钟管理器。 + AlarmManager alarmManager = (AlarmManager) context.getSystemService(Context.ALARM_SERVICE); + // 设置闹钟,当到达提醒日期时触发PendingIntent发送广播。 + alarmManager.set(AlarmManager.RTC_WAKEUP, alertDate, pendingIntent); } while (c.moveToNext()); } c.close(); diff --git a/src/src/net/micode/notes/ui/AlarmReceiver.java b/src/src/net/micode/notes/ui/ui/AlarmReceiver.java similarity index 87% rename from src/src/net/micode/notes/ui/AlarmReceiver.java rename to src/src/net/micode/notes/ui/ui/AlarmReceiver.java index 54e503b..7ac37b2 100644 --- a/src/src/net/micode/notes/ui/AlarmReceiver.java +++ b/src/src/net/micode/notes/ui/ui/AlarmReceiver.java @@ -23,8 +23,11 @@ import android.content.Intent; public class AlarmReceiver extends BroadcastReceiver { @Override public void onReceive(Context context, Intent intent) { + // 创建一个新的Intent,指向AlarmAlertActivity类。 intent.setClass(context, AlarmAlertActivity.class); + // 添加FLAG_ACTIVITY_NEW_TASK标志,以在一个新的任务中启动活动。 intent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK); + // 启动活动。 context.startActivity(intent); } -} +} \ No newline at end of file diff --git a/src/src/net/micode/notes/ui/ui/DateTimePicker.java b/src/src/net/micode/notes/ui/ui/DateTimePicker.java new file mode 100644 index 0000000..2f287a0 --- /dev/null +++ b/src/src/net/micode/notes/ui/ui/DateTimePicker.java @@ -0,0 +1,635 @@ +/* + * 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.text.DateFormatSymbols; +import java.util.Calendar; + +import net.micode.notes.R; + + +import android.content.Context; +import android.text.format.DateFormat; +import android.view.View; +import android.widget.FrameLayout; +import android.widget.NumberPicker; + +public class DateTimePicker extends 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; // 24小时制小时选择器的最小值 + private static final int HOUR_SPINNER_MAX_VAL_24_HOUR_VIEW = 23; // 24小时制小时选择器的最大值 + private static final int HOUR_SPINNER_MIN_VAL_12_HOUR_VIEW = 1; // 12小时制小时选择器的最小值 + private static final int HOUR_SPINNER_MAX_VAL_12_HOUR_VIEW = 12; // 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; // 上午/下午选择器 + private Calendar mDate; // 当前日期 + + private String[] mDateDisplayValues = new String[DAYS_IN_ALL_WEEK]; // 日期显示值数组 + + private boolean mIsAm; // 是否为上午 + + private boolean mIs24HourView; // 是否为24小时制 + + 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(); // 通知日期时间发生变化 + } + }; + + private NumberPicker.OnValueChangeListener mOnHourChangedListener = new NumberPicker.OnValueChangeListener() { + @Override + public void onValueChange(NumberPicker picker, int oldVal, int newVal) { + // 当小时选择器的值发生变化时调用 + boolean isDateChanged = false; + Calendar cal = Calendar.getInstance(); + if (!mIs24HourView) { // 如果是12小时制 + 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; + } 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; + } + 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(); // 更新上午/下午控件 + } + } else { // 如果是24小时制 + if (oldVal == HOURS_IN_ALL_DAY - 1 && newVal == 0) { + // 从最后一小时切换到第一小时 + cal.setTimeInMillis(mDate.getTimeInMillis()); + cal.add(Calendar.DAY_OF_YEAR, 1); + isDateChanged = true; + } else if (oldVal == 0 && newVal == HOURS_IN_ALL_DAY - 1) { + // 从第一小时切换到最后一小时 + cal.setTimeInMillis(mDate.getTimeInMillis()); + cal.add(Calendar.DAY_OF_YEAR, -1); + isDateChanged = true; + } + } + int newHour = mHourSpinner.getValue() % HOURS_IN_HALF_DAY + (mIsAm ? 0 : HOURS_IN_HALF_DAY); + mDate.set(Calendar.HOUR_OF_DAY, newHour); // 更新小时 + 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 + public void onValueChange(NumberPicker picker, int oldVal, int newVal) { + // 当分钟选择器的值发生变化时调用 + int minValue = mMinuteSpinner.getMinValue(); + int maxValue = mMinuteSpinner.getMaxValue(); + int offset = 0; + if (oldVal == maxValue && newVal == minValue) { + offset += 1; + } else if (oldVal == minValue && newVal == maxValue) { + 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() { + @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) { + // 使用当前时间初始化 DateTimePicker + this(context, System.currentTimeMillis()); +} + +public DateTimePicker(Context context, long date) { + // 使用指定的时间初始化 DateTimePicker,并根据上下文的时间格式确定是否为24小时制 + this(context, date, DateFormat.is24HourFormat(context)); +} + +public DateTimePicker(Context context, long date, boolean is24HourView) { + super(context); + // 初始化日期、时间和 AM/PM 的选择器 + mDate = Calendar.getInstance(); + mInitialising = true; + mIsAm = getCurrentHourOfDay() >= HOURS_IN_HALF_DAY; + inflate(context, R.layout.datetime_picker, this); + + 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); + + // 更新控件的初始状态 + updateDateControl(); + updateHourControl(); + updateAmPmControl(); + + // 设置是否为24小时制 + set24HourView(is24HourView); + + // 设置为当前时间 + setCurrentDate(date); + + setEnabled(isEnabled()); + + // 设置内容描述 + 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; +} + +@Override +public boolean isEnabled() { + return mIsEnabled; +} + +/** + * 获取当前时间的毫秒表示 + * + * @return 当前时间的毫秒表示 + */ +public long getCurrentDateInTimeMillis() { + return mDate.getTimeInMillis(); +} + +/** + * 设置当前时间 + * + * @param date 当前时间的毫秒表示 + */ +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)); +} + +/** + * 设置当前时间 + * + * @param year 当前年份 + * @param month 当前月份 + * @param dayOfMonth 当前日期 + * @param hourOfDay 当前小时数 + * @param minute 当前分钟数 + */ +public void setCurrentDate(int year, int month, + int dayOfMonth, int hourOfDay, int minute) { + setCurrentYear(year); + setCurrentMonth(month); + setCurrentDay(dayOfMonth); + setCurrentHour(hourOfDay); + setCurrentMinute(minute); +} + +/** + * 获取当前年份 + * + * @return 当前年份 + */ +public int getCurrentYear() { + return mDate.get(Calendar.YEAR); +} + +/** + * 设置当前年份 + * + * @param year 当前年份 + */ +public void setCurrentYear(int year) { + mDate.set(Calendar.YEAR, year); + updateDateControl(); +} + +/** + * 获取当前月份 + * + * @return 当前月份 + */ +public int getCurrentMonth() { + return mDate.get(Calendar.MONTH); +} + +/** + * 设置当前月份 + * + * @param month 当前月份 + */ +public void setCurrentMonth(int month) { + mDate.set(Calendar.MONTH, month); + updateDateControl(); +} + +/** + * 获取当前日期 + * + * @return 当前日期 + */ +public int getCurrentDay() { + return mDate.get(Calendar.DAY_OF_MONTH); +} + +/** + * 设置当前日期 + * + *@param dayOfMonth 当前日期 + */ +public void setCurrentDay(int dayOfMonth) { + mDate.set(Calendar.DAY_OF_MONTH, dayOfMonth); + updateDateControl(); +} + +/** + * 获取当前小时数 + * + * @return 当前小时数 + */ +public int getCurrentHour() { + int hourOfDay = mDate.get(Calendar.HOUR_OF_DAY); + if (mIsAm && hourOfDay >= HOURS_IN_HALF_DAY) { + hourOfDay -= HOURS_IN_HALF_DAY; + } else if (!mIsAm && hourOfDay < HOURS_IN_HALF_DAY) { + hourOfDay += HOURS_IN_HALF_DAY; + } + return hourOfDay; +} + +/** + * 设置当前小时数 + * + * @param hour 当前小时数 + */ +public void setCurrentHour(int hour) { + if (hour >= HOURS_IN_HALF_DAY) { + mIsAm = false; + hour -= HOURS_IN_HALF_DAY; + } else { + mIsAm = true; + } + mDate.set(Calendar.HOUR_OF_DAY, hour); + updateHourControl(); + updateAmPmControl(); +} + +/** + * 获取当前分钟数 + * + * @return 当前分钟数 + */ +public int getCurrentMinute() { + return mDate.get(Calendar.MINUTE); +} + +/** + * 设置当前分钟数 + * + * @param minute 当前分钟数 + */ +public void setCurrentMinute(int minute) { + mDate.set(Calendar.MINUTE, minute); + updateMinuteControl(); +} + +/** + * 设置当前年份 + * + * @param year 当前年份 + */ +public void setCurrentYear(int year) { + // 如果不是初始化状态并且传入的年份与当前年份相同,则不做任何操作 + if (!mInitialising && year == getCurrentYear()) { + return; + } + mDate.set(Calendar.YEAR, year); + updateDateControl(); + onDateTimeChanged(); +} + +/** + * 获取当前年份 + * + * @return 当前年份 + */ +public int getCurrentYear() { + return mDate.get(Calendar.YEAR); +} + +/** + * 获取当前月份(从0开始,即0表示一月) + * + * @return 当前月份 + */ +public int getCurrentMonth() { + return mDate.get(Calendar.MONTH); +} + +/** + * 设置当前月份(从0开始,即0表示一月) + * + * @param month 月份 + */ +public void setCurrentMonth(int month) { + // 如果不是初始化状态并且传入的月份与当前月份相同,则不做任何操作 + if (!mInitialising && month == getCurrentMonth()) { + return; + } + mDate.set(Calendar.MONTH, month); + updateDateControl(); + onDateTimeChanged(); +} + +/** + * 获取当前月份中的日期(即某个月的第几天) + * + * @return 当前日期 + */ +public int getCurrentDay() { + return mDate.get(Calendar.DAY_OF_MONTH); +} + +/** + * 设置当前月份中的日期(即某个月的第几天) + * + * @param dayOfMonth 日期 + */ +public void setCurrentDay(int dayOfMonth) { + // 如果不是初始化状态并且传入的日期与当前日期相同,则不做任何操作 + if (!mInitialising && dayOfMonth == getCurrentDay()) { + return; + } + mDate.set(Calendar.DAY_OF_MONTH, dayOfMonth); + updateDateControl(); + onDateTimeChanged(); +} + +/** + * 获取当前小时(24小时制,范围为0~23) + * + * @return 当前小时 + */ +public int getCurrentHourOfDay() { + return mDate.get(Calendar.HOUR_OF_DAY); +} + +/** + * 获取当前小时(12小时制) + * + * @return 当前小时 + */ +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; + } + } +} + +/** + * 设置当前小时(24小时制,范围为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(); +} + +/** + * 获取当前分钟 + * + * @return 当前分钟 + */ +public int getCurrentMinute() { + return mDate.get(Calendar.MINUTE); +} + +/** + * 设置当前分钟 + * + * @param minute 分钟 + */ +public void setCurrentMinute(int minute) { + // 如果不是初始化状态并且传入的分钟与当前分钟相同,则不做任何操作 + if (!mInitialising && minute == getCurrentMinute()) { + return; + } + mMinuteSpinner.setValue(minute); + mDate.set(Calendar.MINUTE, minute); + onDateTimeChanged(); +} + +/** + * 获取是否为24小时制 + * + * @return 如果为24小时制,则返回true;否则返回false + */ +public boolean is24HourView() { + return mIs24HourView; +} + +/** + * 设置是否为24小时制 + * + * @param is24HourView true表示为24小时制,false表示为AM/PM制 + */ +public void set24HourView(boolean is24HourView) { + //如果当前的显示模式(24小时制或AM/PM制)与要设置的模式相同,则不做任何操作 + if (mIs24HourView == is24HourView) { + return; + } + mIs24HourView = is24HourView; + mAmPmSpinner.setVisibility(is24HourView ? View.GONE : View.VISIBLE); + int hour = getCurrentHourOfDay(); + updateHourControl(); + setCurrentHour(hour); + updateAmPmControl(); +} + +/** + * 设置日期时间改变的回调接口 + * + * @param callback 回调接口 + */ +public void setOnDateTimeChangedListener(OnDateTimeChangedListener callback) { + mOnDateTimeChangedListener = callback; +} + +/** + * 当日期时间改变时触发回调 + */ +private void onDateTimeChanged() { + if (mOnDateTimeChangedListener != null) { + mOnDateTimeChangedListener.onDateTimeChanged(this, getCurrentYear(), + getCurrentMonth(), getCurrentDay(), getCurrentHourOfDay(), getCurrentMinute()); + } +} + +/** + * 更新日期控件的显示 + */ +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(); +} + +/** + * 更新AM/PM控件的显示 + */ +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); + } +} \ No newline at end of file diff --git a/src/src/net/micode/notes/ui/DateTimePickerDialog.java b/src/src/net/micode/notes/ui/ui/DateTimePickerDialog.java similarity index 95% rename from src/src/net/micode/notes/ui/DateTimePickerDialog.java rename to src/src/net/micode/notes/ui/ui/DateTimePickerDialog.java index 2c47ba4..c4c164c 100644 --- a/src/src/net/micode/notes/ui/DateTimePickerDialog.java +++ b/src/src/net/micode/notes/ui/ui/DateTimePickerDialog.java @@ -35,7 +35,7 @@ public class DateTimePickerDialog extends AlertDialog implements OnClickListener private boolean mIs24HourView; private OnDateTimeSetListener mOnDateTimeSetListener; private DateTimePicker mDateTimePicker; - + // 回调接口,用于在日期时间设置完成后执行一些操作 public interface OnDateTimeSetListener { void OnDateTimeSet(AlertDialog dialog, long date); } @@ -63,15 +63,15 @@ public class DateTimePickerDialog extends AlertDialog implements OnClickListener set24HourView(DateFormat.is24HourFormat(this.getContext())); updateTitle(mDate.getTimeInMillis()); } - + // 设置是否使用24小时制显示 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 | @@ -80,7 +80,7 @@ public class DateTimePickerDialog extends AlertDialog implements OnClickListener flag |= mIs24HourView ? DateUtils.FORMAT_24HOUR : DateUtils.FORMAT_24HOUR; setTitle(DateUtils.formatDateTime(this.getContext(), date, flag)); } - + // 确定按钮点击事件 public void onClick(DialogInterface arg0, int arg1) { if (mOnDateTimeSetListener != null) { mOnDateTimeSetListener.OnDateTimeSet(this, mDate.getTimeInMillis()); diff --git a/src/src/net/micode/notes/ui/DropdownMenu.java b/src/src/net/micode/notes/ui/ui/DropdownMenu.java similarity index 92% rename from src/src/net/micode/notes/ui/DropdownMenu.java rename to src/src/net/micode/notes/ui/ui/DropdownMenu.java index 613dc74..ca4e500 100644 --- a/src/src/net/micode/notes/ui/DropdownMenu.java +++ b/src/src/net/micode/notes/ui/ui/DropdownMenu.java @@ -35,9 +35,13 @@ public class DropdownMenu { public DropdownMenu(Context context, Button button, int menuId) { mButton = button; mButton.setBackgroundResource(R.drawable.dropdown_icon); + + // 创建弹出菜单 mPopupMenu = new PopupMenu(context, mButton); mMenu = mPopupMenu.getMenu(); mPopupMenu.getMenuInflater().inflate(menuId, mMenu); + + // 设置按钮点击事件,显示弹出菜单 mButton.setOnClickListener(new OnClickListener() { public void onClick(View v) { mPopupMenu.show(); @@ -45,16 +49,19 @@ public class DropdownMenu { }); } + // 设置菜单项点击监听器 public void setOnDropdownMenuItemClickListener(OnMenuItemClickListener listener) { if (mPopupMenu != null) { mPopupMenu.setOnMenuItemClickListener(listener); } } + // 根据ID查找菜单项 public MenuItem findItem(int id) { return mMenu.findItem(id); } + // 设置按钮标题 public void setTitle(CharSequence title) { mButton.setText(title); } diff --git a/src/src/net/micode/notes/ui/FoldersListAdapter.java b/src/src/net/micode/notes/ui/ui/FoldersListAdapter.java similarity index 89% rename from src/src/net/micode/notes/ui/FoldersListAdapter.java rename to src/src/net/micode/notes/ui/ui/FoldersListAdapter.java index 96b77da..f245964 100644 --- a/src/src/net/micode/notes/ui/FoldersListAdapter.java +++ b/src/src/net/micode/notes/ui/ui/FoldersListAdapter.java @@ -28,14 +28,13 @@ import net.micode.notes.R; import net.micode.notes.data.Notes; import net.micode.notes.data.Notes.NoteColumns; - public class FoldersListAdapter extends CursorAdapter { - public static final String [] PROJECTION = { + public static final String[] PROJECTION = { NoteColumns.ID, NoteColumns.SNIPPET }; - public static final int ID_COLUMN = 0; + public static final int ID_COLUMN = 0; public static final int NAME_COLUMN = 1; public FoldersListAdapter(Context context, Cursor c) { @@ -45,19 +44,23 @@ public class FoldersListAdapter extends CursorAdapter { @Override public View newView(Context context, Cursor cursor, ViewGroup parent) { + // 创建新的视图对象 return new FolderListItem(context); } @Override public void bindView(View view, Context context, Cursor cursor) { if (view instanceof FolderListItem) { + // 获取文件夹名称 String folderName = (cursor.getLong(ID_COLUMN) == Notes.ID_ROOT_FOLDER) ? context .getString(R.string.menu_move_parent_folder) : cursor.getString(NAME_COLUMN); + // 绑定文件夹名称到视图 ((FolderListItem) view).bind(folderName); } } 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); @@ -68,13 +71,14 @@ public class FoldersListAdapter extends CursorAdapter { 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/src/src/net/micode/notes/ui/NoteEditActivity.java b/src/src/net/micode/notes/ui/ui/NoteEditActivity.java similarity index 78% rename from src/src/net/micode/notes/ui/NoteEditActivity.java rename to src/src/net/micode/notes/ui/ui/NoteEditActivity.java index 96a9ff8..bcea75d 100644 --- a/src/src/net/micode/notes/ui/NoteEditActivity.java +++ b/src/src/net/micode/notes/ui/ui/NoteEditActivity.java @@ -71,7 +71,7 @@ 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 { @@ -83,7 +83,7 @@ public class NoteEditActivity extends Activity implements OnClickListener, public ImageView ibSetBgColor; } - + // 背景颜色选择按钮与对应颜色值的映射关系 private static final Map sBgSelectorBtnsMap = new HashMap(); static { sBgSelectorBtnsMap.put(R.id.iv_bg_yellow, ResourceParser.YELLOW); @@ -92,7 +92,7 @@ public class NoteEditActivity extends Activity implements OnClickListener, sBgSelectorBtnsMap.put(R.id.iv_bg_green, ResourceParser.GREEN); sBgSelectorBtnsMap.put(R.id.iv_bg_white, ResourceParser.WHITE); } - + // 颜色值与背景颜色选择按钮的选中状态对应的映射关系 private static final Map sBgSelectorSelectionMap = new HashMap(); static { sBgSelectorSelectionMap.put(ResourceParser.YELLOW, R.id.iv_bg_yellow_select); @@ -101,7 +101,7 @@ public class NoteEditActivity extends Activity implements OnClickListener, sBgSelectorSelectionMap.put(ResourceParser.GREEN, R.id.iv_bg_green_select); sBgSelectorSelectionMap.put(ResourceParser.WHITE, R.id.iv_bg_white_select); } - + // 字体大小选择按钮与对应字体大小值的映射关系 private static final Map sFontSizeBtnsMap = new HashMap(); static { sFontSizeBtnsMap.put(R.id.ll_font_large, ResourceParser.TEXT_LARGE); @@ -109,7 +109,7 @@ public class NoteEditActivity extends Activity implements OnClickListener, sFontSizeBtnsMap.put(R.id.ll_font_normal, ResourceParser.TEXT_MEDIUM); sFontSizeBtnsMap.put(R.id.ll_font_super, ResourceParser.TEXT_SUPER); } - + // 字体大小值与字体大小选择按钮的选中状态对应的映射关系 private static final Map sFontSelectorSelectionMap = new HashMap(); static { sFontSelectorSelectionMap.put(ResourceParser.TEXT_LARGE, R.id.iv_large_select); @@ -118,36 +118,23 @@ public class NoteEditActivity extends Activity implements OnClickListener, sFontSelectorSelectionMap.put(ResourceParser.TEXT_SUPER, R.id.iv_super_select); } - private static final String TAG = "NoteEditActivity"; - - private HeadViewHolder mNoteHeaderHolder; - - private View mHeadViewPanel; - - private View mNoteBgColorSelector; - - private View mFontSizeSelector; - - private EditText mNoteEditor; - - private View mNoteEditorPanel; - - private WorkingNote mWorkingNote; - - private SharedPreferences mSharedPrefs; - 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; + private HeadViewHolder mNoteHeaderHolder; // 笔记标题视图持有者 + private View mHeadViewPanel; // 笔记标题面板 + private View mNoteBgColorSelector; // 笔记背景颜色选择器 + private View mFontSizeSelector; // 字体大小选择器 + private EditText mNoteEditor; // 笔记编辑器 + private View mNoteEditorPanel; // 笔记编辑面板 + private WorkingNote mWorkingNote; // 当前编辑的笔记 + private SharedPreferences mSharedPrefs; // SharedPreferences 对象,用于存储应用程序的设置信息 + private int mFontSizeId; // 字体大小ID + 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) { @@ -162,15 +149,16 @@ public class NoteEditActivity extends Activity implements OnClickListener, } /** - * 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 intent = new Intent(Intent.ACTION_VIEW); intent.putExtra(Intent.EXTRA_UID, savedInstanceState.getLong(Intent.EXTRA_UID)); + // 初始化活动状态 if (!initActivityState(intent)) { finish(); return; @@ -180,30 +168,29 @@ public class NoteEditActivity extends Activity implements OnClickListener, } private boolean initActivityState(Intent intent) { - /** - * If the user specified the {@link Intent#ACTION_VIEW} but not provided with id, - * then jump to the NotesListActivity - */ + // 根据提供的Intent初始化活动状态 + + // 检查Intent的操作是否为ACTION_VIEW mWorkingNote = null; if (TextUtils.equals(Intent.ACTION_VIEW, intent.getAction())) { long noteId = intent.getLongExtra(Intent.EXTRA_UID, 0); mUserQuery = ""; - /** - * Starting from the searched result - */ + // 检查Intent是否包含EXTRA_DATA_KEY(搜索结果) if (intent.hasExtra(SearchManager.EXTRA_DATA_KEY)) { noteId = Long.parseLong(intent.getStringExtra(SearchManager.EXTRA_DATA_KEY)); mUserQuery = intent.getStringExtra(SearchManager.USER_QUERY); } - + // 检查笔记是否存在于笔记数据库中 if (!DataUtils.visibleInNoteDatabase(getContentResolver(), noteId, Notes.TYPE_NOTE)) { - Intent jump = new Intent(this, NotesListActivity.class); + // 如果笔记不存在,跳转到NotesListActivity并结束当前活动 + Intent jump = new Intent(this, NotesListActivity.class); startActivity(jump); showToast(R.string.error_note_not_exist); finish(); return false; } else { + // 使用指定的noteId加载工作笔记 mWorkingNote = WorkingNote.load(this, noteId); if (mWorkingNote == null) { Log.e(TAG, "load note failed with note id" + noteId); @@ -211,11 +198,14 @@ public class NoteEditActivity extends Activity implements OnClickListener, return false; } } + // 设置软键盘模式(隐藏和调整大小) 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 + // 新建笔记 + + // 从Intent的额外数据中获取文件夹ID、小部件ID、小部件类型和背景资源ID long folderId = intent.getLongExtra(Notes.INTENT_EXTRA_FOLDER_ID, 0); int widgetId = intent.getIntExtra(Notes.INTENT_EXTRA_WIDGET_ID, AppWidgetManager.INVALID_APPWIDGET_ID); @@ -224,7 +214,7 @@ public class NoteEditActivity extends Activity implements OnClickListener, int bgResId = intent.getIntExtra(Notes.INTENT_EXTRA_BACKGROUND_ID, ResourceParser.getDefaultBgId(this)); - // 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) { @@ -232,8 +222,10 @@ public class NoteEditActivity extends Activity implements OnClickListener, Log.w(TAG, "The call record number is null"); } long noteId = 0; + // 检查具有指定的phoneNumber和callDate的笔记是否已经存在 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); @@ -241,19 +233,22 @@ public class NoteEditActivity extends Activity implements OnClickListener, return false; } } else { + // 如果笔记不存在,创建空白笔记,并转换为电话记录笔记 mWorkingNote = WorkingNote.createEmptyNote(this, folderId, widgetId, widgetType, bgResId); mWorkingNote.convertToCallNote(phoneNumber, callDate); } } else { + // 创建空白笔记 mWorkingNote = WorkingNote.createEmptyNote(this, folderId, widgetId, widgetType, bgResId); } - + // 设置软键盘模式(调整大小和可见) 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; @@ -261,28 +256,41 @@ public class NoteEditActivity extends Activity implements OnClickListener, mWorkingNote.setOnSettingStatusChangedListener(this); return true; } + /** + * 在活动恢复其状态时调用的方法。它用于恢复之前被销毁的活动的状态。该方法通过检查保存的实例状态中是否包含特定键来确定是否需要执行恢复操作。如果需要恢复操作,它会创建一个新的意图(Intent),设置必要的参数,并调用initActivityState()方法来初始化活动的状态。 + * + * @param savedInstanceState 保存的实例状态 + */ @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 @@ -292,21 +300,28 @@ public class NoteEditActivity extends Activity implements OnClickListener, * 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 { + // 如果未超过提醒时间,将提醒日期文本设置为相对时间字符串(如"2分钟前") 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); }; @@ -315,6 +330,7 @@ public class NoteEditActivity extends Activity implements OnClickListener, @Override protected void onNewIntent(Intent intent) { super.onNewIntent(intent); + // 初始化活动状态 initActivityState(intent); } @@ -322,27 +338,30 @@ public class NoteEditActivity extends Activity implements OnClickListener, 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 + * 对于没有笔记ID的新笔记,我们应该首先保存它以生成一个ID。 + * 如果正在编辑的笔记没有值得保存的内容,则没有ID,这相当于创建新笔记。 */ if (!mWorkingNote.existInDatabase()) { saveNote(); } + // 将工作笔记的ID保存到状态Bundle中 outState.putLong(Intent.EXTRA_UID, mWorkingNote.getNoteId()); Log.d(TAG, "Save working note id: " + mWorkingNote.getNoteId() + " onSaveInstanceState"); } @Override 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; } @@ -350,10 +369,12 @@ public class NoteEditActivity extends Activity implements OnClickListener, } 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 @@ -364,6 +385,7 @@ public class NoteEditActivity extends Activity implements OnClickListener, } private void initResources() { + // 初始化资源对象和视图控件 mHeadViewPanel = findViewById(R.id.note_title); mNoteHeaderHolder = new HeadViewHolder(); mNoteHeaderHolder.tvModified = (TextView) findViewById(R.id.tv_modified_date); @@ -374,12 +396,14 @@ public class NoteEditActivity extends Activity implements OnClickListener, 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); @@ -387,9 +411,8 @@ public class NoteEditActivity extends Activity implements OnClickListener, 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} + * HACKME: 修复将资源ID存储在共享首选项中的错误。 + * ID 可能大于资源的长度,在这种情况下,返回 BG_DEFAULT_FONT_SIZE */ if(mFontSizeId >= TextAppearanceResources.getResourcesSize()) { mFontSizeId = ResourceParser.BG_DEFAULT_FONT_SIZE; @@ -400,14 +423,17 @@ public class NoteEditActivity extends Activity implements OnClickListener, @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) { @@ -416,101 +442,141 @@ public class NoteEditActivity extends Activity implements OnClickListener, Log.e(TAG, "Unspported widget type"); return; } - + // 将工作笔记的小部件ID放入意图中 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); + // 设置工作笔记的背景颜色ID mWorkingNote.setBgColorId(sBgSelectorBtnsMap.get(id)); + // 隐藏背景颜色选择器 mNoteBgColorSelector.setVisibility(View.GONE); + // 点击字体大小选择器的选项按钮 } else if (sFontSizeBtnsMap.containsKey(id)) { + // 隐藏当前字体大小选择项 findViewById(sFontSelectorSelectionMap.get(mFontSizeId)).setVisibility(View.GONE); + // 设置字体大小ID mFontSizeId = sFontSizeBtnsMap.get(id); + // 将字体大小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 { + } // 否则,设置编辑器的文本外观为选定的字体大小 + else { mNoteEditor.setTextAppearance(this, TextAppearanceResources.getTexAppearanceResource(mFontSizeId)); } + // 隐藏字体大小选择器 mFontSizeSelector.setVisibility(View.GONE); } } @Override public void onBackPressed() { - if(clearSettingState()) { + // 清除设置状态 + 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) { + } + // 如果字体大小选择器可见,隐藏它 + 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); + // 显示当前工作笔记的背景颜色选择项 + findViewById(sBgSelectorSelectionMap.get(mWorkingNote.getBgColorId())).setVisibility(View.VISIBLE); + + // 设置笔记编辑区域的背景颜色 mNoteEditorPanel.setBackgroundResource(mWorkingNote.getBgColorResId()); + + // 设置标题栏的背景颜色 mHeadViewPanel.setBackgroundResource(mWorkingNote.getTitleBgResId()); } @Override public boolean onPrepareOptionsMenu(Menu menu) { + // 如果Activity已经被销毁,直接返回 if (isFinishing()) { return true; } + + // 清除设置状态 clearSettingState(); + + // 清空菜单 menu.clear(); + + // 根据工作笔记所属文件夹的不同,加载不同的菜单布局文件 if (mWorkingNote.getFolderId() == Notes.ID_CALL_RECORD_FOLDER) { getMenuInflater().inflate(R.menu.call_note_edit, 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()) { + // 点击新建笔记菜单项 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)); @@ -526,24 +592,35 @@ public class NoteEditActivity extends Activity implements OnClickListener, builder.setNegativeButton(android.R.string.cancel, null); builder.show(); break; + // 点击字体大小菜单项 case R.id.menu_font_size: + // 显示字体大小选择器 mFontSizeSelector.setVisibility(View.VISIBLE); + // 显示当前字体大小选择项 findViewById(sFontSelectorSelectionMap.get(mFontSizeId)).setVisibility(View.VISIBLE); 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()); 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; @@ -554,20 +631,22 @@ public class NoteEditActivity extends Activity implements OnClickListener, } 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); + // 设置工作笔记的提醒日期 + mWorkingNote.setAlertDate(date, true); } }); d.show(); } /** - * Share note to apps that support {@link Intent#ACTION_SEND} action - * and {@text/plain} type + * 分享笔记给支持{@link Intent#ACTION_SEND}操作和{@text/plain}类型的应用程序 */ private void sendTo(Context context, String info) { + // 创建发送文本的意图 Intent intent = new Intent(Intent.ACTION_SEND); intent.putExtra(Intent.EXTRA_TEXT, info); intent.setType("text/plain"); @@ -575,10 +654,10 @@ public class NoteEditActivity extends Activity implements OnClickListener, } private void createNewNote() { - // Firstly, save current editing notes + // 首先保存当前编辑的笔记 saveNote(); - // For safety, start a new NoteEditActivity + // 为了安全起见,启动一个新的NoteEditActivity finish(); Intent intent = new Intent(this, NoteEditActivity.class); intent.setAction(Intent.ACTION_INSERT_OR_EDIT); @@ -611,11 +690,9 @@ public class NoteEditActivity extends Activity implements OnClickListener, 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(); @@ -626,23 +703,23 @@ public class NoteEditActivity extends Activity implements OnClickListener, PendingIntent pendingIntent = PendingIntent.getBroadcast(this, 0, intent, 0); AlarmManager alarmManager = ((AlarmManager) getSystemService(ALARM_SERVICE)); showAlertHeader(); - if(!set) { + 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 + * 有一种情况是用户没有输入任何内容(笔记不值得保存),我们没有笔记ID,提醒用户输入内容 */ Log.e(TAG, "Clock alert setting error"); showToast(R.string.error_note_empty_for_clock); } } - public void onWidgetChanged() { + // 更新小部件 updateWidget(); } @@ -653,13 +730,15 @@ public class NoteEditActivity extends Activity implements OnClickListener, } for (int i = index + 1; i < childCount; i++) { + // 更新索引 ((NoteEditText) mEditTextList.getChildAt(i).findViewById(R.id.et_edit_text)) .setIndex(i - 1); } + // 移除指定位置的EditText mEditTextList.removeViewAt(index); NoteEditText edit = null; - if(index == 0) { + if (index == 0) { edit = (NoteEditText) mEditTextList.getChildAt(0).findViewById( R.id.et_edit_text); } else { @@ -674,18 +753,20 @@ public class NoteEditActivity extends Activity implements OnClickListener, 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"); + if (index > mEditTextList.getChildCount()) { + Log.e(TAG, "Index out of mEditTextList boundary, should not happen"); } + // 创建新的EditText并插入到指定位置 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); } @@ -696,14 +777,17 @@ public class NoteEditActivity extends Activity implements OnClickListener, String[] items = text.split("\n"); int index = 0; for (String item : items) { - if(!TextUtils.isEmpty(item)) { + if (!TextUtils.isEmpty(item)) { + // 创建列表项并添加到EditText列表 mEditTextList.addView(getListItem(item, index)); index++; } } + // 添加一个空的列表项 mEditTextList.addView(getListItem("", index)); mEditTextList.getChildAt(index).findViewById(R.id.et_edit_text).requestFocus(); + // 切换到列表模式,隐藏编辑器,显示EditText列表 mNoteEditor.setVisibility(View.GONE); mEditTextList.setVisibility(View.VISIBLE); } @@ -715,6 +799,7 @@ public class NoteEditActivity extends Activity implements OnClickListener, Matcher m = mPattern.matcher(fullText); int start = 0; while (m.find(start)) { + // 设置匹配到的文本背景色高亮 spannable.setSpan( new BackgroundColorSpan(this.getResources().getColor( R.color.user_query_highlight)), m.start(), m.end(), @@ -724,22 +809,29 @@ public class NoteEditActivity extends Activity implements OnClickListener, } return spannable; } - private View getListItem(String item, int index) { + // 创建列表项视图 View view = LayoutInflater.from(this).inflate(R.layout.note_edit_list_item, null); + + // 获取列表项中的NoteEditText final NoteEditText edit = (NoteEditText) view.findViewById(R.id.et_edit_text); edit.setTextAppearance(this, TextAppearanceResources.getTexAppearanceResource(mFontSizeId)); + + // 获取列表项中的CheckBox CheckBox cb = ((CheckBox) view.findViewById(R.id.cb_edit_item)); cb.setOnCheckedChangeListener(new OnCheckedChangeListener() { public void onCheckedChanged(CompoundButton buttonView, boolean isChecked) { if (isChecked) { + // 设置NoteEditText的文本为删除线效果 edit.setPaintFlags(edit.getPaintFlags() | Paint.STRIKE_THRU_TEXT_FLAG); } else { + // 移除NoteEditText的删除线效果 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); @@ -750,9 +842,11 @@ public class NoteEditActivity extends Activity implements OnClickListener, item = item.substring(TAG_UNCHECKED.length(), item.length()).trim(); } + // 设置NoteEditText的监听器、索引和文本内容 edit.setOnTextViewChangeListener(this); edit.setIndex(index); edit.setText(getHighlightQueryResult(item, mUserQuery)); + return view; } @@ -761,7 +855,8 @@ public class NoteEditActivity extends Activity implements OnClickListener, Log.e(TAG, "Wrong index, should not happen"); return; } - if(hasText) { + // 根据文本内容的有无显示或隐藏CheckBox + 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); @@ -770,18 +865,19 @@ public class NoteEditActivity extends Activity implements OnClickListener, 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 + " ", - "")); + // 从工作笔记中移除未选中标记 + 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; if (mWorkingNote.getCheckListMode() == TextNote.MODE_CHECK_LIST) { @@ -791,47 +887,44 @@ public class NoteEditActivity extends Activity implements OnClickListener, 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()) { + // 将选中的列表项添加到StringBuilder中,并添加已选中标记 sb.append(TAG_CHECKED).append(" ").append(edit.getText()).append("\n"); hasChecked = true; } else { + // 将未选中的列表项添加到StringBuilder中,并添加未选中标记 sb.append(TAG_UNCHECKED).append(" ").append(edit.getText()).append("\n"); } } } + // 将StringBuilder中的文本设置为工作笔记的内容 mWorkingNote.setWorkingText(sb.toString()); } else { + // 将编辑器中的文本设置为工作笔记的内容 mWorkingNote.setWorkingText(mNoteEditor.getText().toString()); } return hasChecked; } private boolean saveNote() { + // 获取工作笔记的内容 getWorkingText(); + // 保存工作笔记 boolean saved = mWorkingNote.saveNote(); 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,用于标识创建/编辑状态 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 Intent sender = new Intent(); Intent shortcutIntent = new Intent(this, NoteEditActivity.class); shortcutIntent.setAction(Intent.ACTION_VIEW); @@ -843,22 +936,22 @@ public class NoteEditActivity extends Activity implements OnClickListener, Intent.ShortcutIconResource.fromContext(this, R.drawable.icon_app)); sender.putExtra("duplicate", true); sender.setAction("com.android.launcher.action.INSTALL_SHORTCUT"); + // 显示发送到桌面成功的提示 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; } @@ -868,6 +961,7 @@ public class NoteEditActivity extends Activity implements OnClickListener, } private void showToast(int resId, int duration) { + // 显示Toast提示 Toast.makeText(this, resId, duration).show(); } } diff --git a/src/src/net/micode/notes/ui/NoteEditText.java b/src/src/net/micode/notes/ui/ui/NoteEditText.java similarity index 85% rename from src/src/net/micode/notes/ui/NoteEditText.java rename to src/src/net/micode/notes/ui/ui/NoteEditText.java index 2afe2a8..ce0be9d 100644 --- a/src/src/net/micode/notes/ui/NoteEditText.java +++ b/src/src/net/micode/notes/ui/ui/NoteEditText.java @@ -47,30 +47,30 @@ public class NoteEditText extends EditText { private static final String SCHEME_EMAIL = "mailto:" ; private static final Map sSchemaActionResMap = new HashMap(); + static { + // 将不同的链接协议和对应的操作资源ID存储在映射表中 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 + * 当前编辑文本视图的变化监听器接口 */ public interface OnTextViewChangeListener { /** - * Delete current edit text when {@link KeyEvent#KEYCODE_DEL} happens - * and the text is null + * 当发生{@link KeyEvent#KEYCODE_DEL}且文本为空时,删除当前编辑文本视图 */ void onEditTextDelete(int index, String text); /** - * Add edit text after current edit text when {@link KeyEvent#KEYCODE_ENTER} - * happen + * 当发生{@link KeyEvent#KEYCODE_ENTER}时,在当前编辑文本视图之后添加新的编辑文本视图 */ void onEditTextEnter(int index, String text); /** - * Hide or show item option when text change + * 当文本发生变化时,隐藏或显示项目选项 */ void onTextChange(int index, boolean hasText); } @@ -96,24 +96,28 @@ public class NoteEditText extends EditText { public NoteEditText(Context context, AttributeSet attrs, int defStyle) { super(context, attrs, defStyle); - // TODO Auto-generated constructor stub } @Override public boolean onTouchEvent(MotionEvent event) { switch (event.getAction()) { case MotionEvent.ACTION_DOWN: - int x = (int) event.getX(); + ```java int y = (int) event.getY(); + + // 通过偏移量计算出点击位置的文本偏移量 x -= getTotalPaddingLeft(); y -= getTotalPaddingTop(); x += getScrollX(); y += getScrollY(); + // 根据点击位置的偏移量获取对应的文本偏移量 Layout layout = getLayout(); int line = layout.getLineForVertical(y); int off = layout.getOffsetForHorizontal(line, x); + + // 设置选中文本的偏移量,即设置光标位置 Selection.setSelection(getText(), off); break; } @@ -126,6 +130,7 @@ public class NoteEditText extends EditText { switch (keyCode) { case KeyEvent.KEYCODE_ENTER: if (mOnTextViewChangeListener != null) { + // 如果监听器存在,则回调onEditTextEnter方法 return false; } break; @@ -144,21 +149,23 @@ public class NoteEditText extends EditText { case KeyEvent.KEYCODE_DEL: if (mOnTextViewChangeListener != null) { if (0 == mSelectionStartBeforeDelete && mIndex != 0) { + // 当文本为空且按下删除键时,回调onEditTextDelete方法 mOnTextViewChangeListener.onEditTextDelete(mIndex, getText().toString()); return true; } } else { - Log.d(TAG, "OnTextViewChangeListener was not seted"); + Log.d(TAG, "OnTextViewChangeListener未设置"); } break; case KeyEvent.KEYCODE_ENTER: if (mOnTextViewChangeListener != null) { + // 当按下回车键时,获取光标位置之后的文本,并回调onEditTextEnter方法 int selectionStart = getSelectionStart(); String text = getText().subSequence(selectionStart, length()).toString(); setText(getText().subSequence(0, selectionStart)); mOnTextViewChangeListener.onEditTextEnter(mIndex + 1, text); } else { - Log.d(TAG, "OnTextViewChangeListener was not seted"); + Log.d(TAG, "OnTextViewChangeListener未设置"); } break; default: @@ -171,8 +178,10 @@ public class NoteEditText extends EditText { protected void onFocusChanged(boolean focused, int direction, Rect previouslyFocusedRect) { if (mOnTextViewChangeListener != null) { if (!focused && TextUtils.isEmpty(getText())) { + // 当失去焦点且文本为空时,回调onTextChange方法,参数为false mOnTextViewChangeListener.onTextChange(mIndex, false); } else { + // 当获得焦点或文本不为空时,回调onTextChange方法,参数为true mOnTextViewChangeListener.onTextChange(mIndex, true); } } @@ -193,6 +202,7 @@ public class NoteEditText extends EditText { int defaultResId = 0; for(String schema: sSchemaActionResMap.keySet()) { if(urls[0].getURL().indexOf(schema) >= 0) { + // 根据链接协议获取对应的操作资源ID defaultResId = sSchemaActionResMap.get(schema); break; } @@ -202,10 +212,11 @@ public class NoteEditText extends EditText { 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; } diff --git a/src/src/net/micode/notes/ui/NoteItemData.java b/src/src/net/micode/notes/ui/ui/NoteItemData.java similarity index 55% rename from src/src/net/micode/notes/ui/NoteItemData.java rename to src/src/net/micode/notes/ui/ui/NoteItemData.java index 0f5a878..84f8bc7 100644 --- a/src/src/net/micode/notes/ui/NoteItemData.java +++ b/src/src/net/micode/notes/ui/ui/NoteItemData.java @@ -28,18 +28,18 @@ 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, + NoteColumns.ID, // 笔记的ID + NoteColumns.ALERTED_DATE, // 提醒日期 + NoteColumns.BG_COLOR_ID, // 背景颜色ID + NoteColumns.CREATED_DATE, // 创建日期 + NoteColumns.HAS_ATTACHMENT, // 是否有附件 + NoteColumns.MODIFIED_DATE, // 修改日期 + NoteColumns.NOTES_COUNT, // 子笔记数量 + NoteColumns.PARENT_ID, // 父笔记ID + NoteColumns.SNIPPET, // 摘要 + NoteColumns.TYPE, // 笔记类型 + NoteColumns.WIDGET_ID, // 小组件ID + NoteColumns.WIDGET_TYPE, // 小组件类型 }; private static final int ID_COLUMN = 0; @@ -55,45 +55,47 @@ public class NoteItemData { 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; + private long mId; // 笔记ID + private long mAlertDate; // 提醒日期 + private int mBgColorId; // 背景颜色ID + private long mCreatedDate; // 创建日期 + private boolean mHasAttachment; // 是否有附件 + private long mModifiedDate; // 修改日期 + private int mNotesCount; // 子笔记数量 + private long mParentId; // 父笔记ID + private String mSnippet; // 摘要 + private int mType; // 笔记类型 + private int mWidgetId; // 小组件ID + private int mWidgetType; // 小组件类型 + private String mName; // 笔记的名称 + private String mPhoneNumber; // 笔记关联的电话号码 + + private boolean mIsLastItem; // 是否为最后一个笔记项 + private boolean mIsFirstItem; // 是否为第一个笔记项 + private boolean mIsOnlyOneItem; // 是否只有一个笔记项 + private boolean mIsOneNoteFollowingFolder; // 是否有一个笔记项跟随在文件夹后面 + private boolean mIsMultiNotesFollowingFolder; // 是否有多个笔记项跟随在文件夹后面 public NoteItemData(Context context, Cursor cursor) { - 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); + mId = cursor.getLong(ID_COLUMN); // 获取笔记ID + mAlertDate = cursor.getLong(ALERTED_DATE_COLUMN); // 获取提醒日期 + mBgColorId = cursor.getInt(BG_COLOR_ID_COLUMN); // 获取背景颜色ID + 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); // 获取父笔记ID + 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); + NoteEditActivity.TAG_UNCHECKED, ""); // 处理摘要中的特殊标记 + mType = cursor.getInt(TYPE_COLUMN); // 获取笔记类型 + mWidgetId = cursor.getInt(WIDGET_ID_COLUMN); // 获取小组件ID + mWidgetType = cursor.getInt(WIDGET_TYPE_COLUMN); // 获取小组件类型 mPhoneNumber = ""; if (mParentId == Notes.ID_CALL_RECORD_FOLDER) { + //根据父笔记ID判断是否为通话记录类型的笔记,并获取关联的电话号码。 + mPhoneNumber = DataUtils.getCallNumberByNoteId(context.getContentResolver(), mId); if (!TextUtils.isEmpty(mPhoneNumber)) { mName = Contact.getContact(context, mPhoneNumber); @@ -109,12 +111,17 @@ public class NoteItemData { checkPostion(cursor); } + /** + * 检查笔记项在光标中的位置 + * + * @param cursor 光标对象 + */ private void checkPostion(Cursor cursor) { - mIsLastItem = cursor.isLast() ? true : false; - mIsFirstItem = cursor.isFirst() ? true : false; - mIsOnlyOneItem = (cursor.getCount() == 1); - mIsMultiNotesFollowingFolder = false; - mIsOneNoteFollowingFolder = false; + mIsLastItem = cursor.isLast() ? true : false; // 是否为最后一个笔记项 + mIsFirstItem = cursor.isFirst() ? true : false; // 是否为第一个笔记项 + mIsOnlyOneItem = (cursor.getCount() == 1); // 是否只有一个笔记项 + mIsMultiNotesFollowingFolder = false; // 是否有多个笔记项跟随在文件夹后面 + mIsOneNoteFollowingFolder = false; // 是否有一个笔记项跟随在文件夹后面 if (mType == Notes.TYPE_NOTE && !mIsFirstItem) { int position = cursor.getPosition(); @@ -122,9 +129,9 @@ public class NoteItemData { if (cursor.getInt(TYPE_COLUMN) == Notes.TYPE_FOLDER || cursor.getInt(TYPE_COLUMN) == Notes.TYPE_SYSTEM) { if (cursor.getCount() > (position + 1)) { - mIsMultiNotesFollowingFolder = true; + mIsMultiNotesFollowingFolder = true; // 有多个笔记项跟随在文件夹后面 } else { - mIsOneNoteFollowingFolder = true; + mIsOneNoteFollowingFolder = true; // 有一个笔记项跟随在文件夹后面 } } if (!cursor.moveToNext()) { @@ -218,7 +225,13 @@ public class NoteItemData { return (mParentId == Notes.ID_CALL_RECORD_FOLDER && !TextUtils.isEmpty(mPhoneNumber)); } + /** + * 从光标中获取笔记类型 + * + * @param cursor 光标对象 + * @return 笔记类型 + */ public static int getNoteType(Cursor cursor) { return cursor.getInt(TYPE_COLUMN); } -} +} \ No newline at end of file diff --git a/src/src/net/micode/notes/ui/NotesListActivity.java b/src/src/net/micode/notes/ui/ui/NotesListActivity.java similarity index 80% rename from src/src/net/micode/notes/ui/NotesListActivity.java rename to src/src/net/micode/notes/ui/ui/NotesListActivity.java index e843aec..777ce88 100644 --- a/src/src/net/micode/notes/ui/NotesListActivity.java +++ b/src/src/net/micode/notes/ui/ui/NotesListActivity.java @@ -1,21 +1,10 @@ /* - * 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. + Edit by yanjingyu + 杩欎釜绫讳富瑕佺敤浜庢樉绀哄拰绠$悊绗旇鍒楄〃锛岃礋璐e鐞嗙瑪璁板垪琛ㄧ殑鏄剧ず鍜岀敤鎴蜂氦浜掞紝鍖呮嫭鎵撳紑绗旇銆佹枃浠跺す銆佸鐞嗕笂涓嬫枃鑿滃崟銆佸鍑虹瑪璁扮瓑鎿嶄綔銆 + */ package net.micode.notes.ui; - import android.app.Activity; import android.app.AlertDialog; import android.app.Dialog; @@ -79,62 +68,63 @@ import java.io.InputStreamReader; import java.util.HashSet; 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; - + //SharedPreferences 涓殑閿紝鐢ㄤ簬鏍囪瘑鏄惁娣诲姞浜嗕粙缁 private static final String PREFERENCE_ADD_INTRODUCTION = "net.micode.notes.introduction"; - + //鏋氫妇绫诲瀷锛岃〃绀哄垪琛ㄧ殑缂栬緫鐘舵 private enum ListEditState { NOTE_LIST, SUB_FOLDER, CALL_RECORD_FOLDER }; - + //褰撳墠鐨勫垪琛ㄧ紪杈戠姸鎬併 private ListEditState mState; - + //鍚庡彴鏌ヨ澶勭悊绋嬪簭锛岀敤浜庡湪鍚庡彴鎵ц鏁版嵁搴撴煡璇 private BackgroundQueryHandler mBackgroundQueryHandler; - + //绗旇鍒楄〃鐨勯傞厤鍣 private NotesListAdapter mNotesListAdapter; - + //鏄剧ず绗旇鍒楄〃鐨 ListView private ListView mNotesListView; - + //娣诲姞鏂扮瑪璁扮殑鎸夐挳 private Button mAddNewNote; - + //鏍囧織浣嶏紝鐢ㄤ簬鍒ゆ柇鏄惁杩涜婊戝姩鐨勫垎鍙 private boolean mDispatch; - + //Y 杞寸殑鍧愭爣锛岀敤浜庤绠楁粦鍔ㄨ窛绂 private int mOriginY; private int mDispatchY; - + //鏄剧ず鏍囬鐨 TextView銆 private TextView mTitleBar; - + //褰撳墠鏂囦欢澶圭殑 ID private long mCurrentFolderId; - + //鐢ㄤ簬璁块棶搴旂敤鐨勬暟鎹 private ContentResolver mContentResolver; - + //涓婁笅鏂囨搷浣滄爮鐨勫洖璋冩帴鍙 private ModeCallback mModeCallBack; - + //鐢ㄤ簬鍦ㄦ棩蹇椾腑鏍囪瘑璇 Activity 鐨勫瓧绗︿覆 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; - + //Activity 鍒涘缓鏃惰皟鐢ㄧ殑鏂规硶锛岀敤浜庡垵濮嬪寲鐣岄潰鍜岃祫婧 @Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); @@ -146,7 +136,7 @@ public class NotesListActivity extends Activity implements OnClickListener, OnIt */ setAppInfoFromRawRes(); } - + //澶勭悊浠庡叾浠 Activity 杩斿洖鐨勭粨鏋滐紝涓昏澶勭悊鎵撳紑鎴栨柊寤鸿妭鐐瑰悗鏇存柊绗旇鍒楄〃 @Override protected void onActivityResult(int requestCode, int resultCode, Intent data) { if (resultCode == RESULT_OK @@ -156,7 +146,7 @@ public class NotesListActivity extends Activity implements OnClickListener, OnIt super.onActivityResult(requestCode, resultCode, data); } } - + //浠庡簲鐢ㄧ殑 res/raw 鐩綍涓嬬殑 R.raw.introduction 鏂囦欢涓鍙栦粙缁嶄俊鎭紝骞跺皢鍏朵繚瀛樹负涓涓瑪璁 private void setAppInfoFromRawRes() { SharedPreferences sp = PreferenceManager.getDefaultSharedPreferences(this); if (!sp.getBoolean(PREFERENCE_ADD_INTRODUCTION, false)) { @@ -202,13 +192,13 @@ public class NotesListActivity extends Activity implements OnClickListener, OnIt } } } - + //鍦 Activity 鍙鏃惰皟鐢紝鍚姩寮傛鏌ヨ绗旇鍒楄〃鐨勪换鍔 @Override protected void onStart() { super.onStart(); startAsyncNotesListQuery(); } - + //鍒濆鍖栬祫婧愶紝鍖呮嫭鑾峰彇 ContentResolver銆佸垵濮嬪寲绗旇鍒楄〃瑙嗗浘绛 private void initResources() { mContentResolver = this.getContentResolver(); mBackgroundQueryHandler = new BackgroundQueryHandler(this.getContentResolver()); @@ -230,12 +220,14 @@ public class NotesListActivity extends Activity implements OnClickListener, OnIt 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; - + //鍦ㄥ垱寤轰笂涓嬫枃鎿嶄綔鏍忔椂璋冪敤,閫氳繃 getMenuInflater().inflate(R.menu.note_list_options, menu) 鍔犺浇涓婁笅鏂囨搷浣滄爮鐨勮彍鍗曞竷灞; + //璁剧疆鑿滃崟椤 R.id.delete 鐨勭偣鍑荤洃鍚櫒涓 this锛屽嵆褰撳墠绫荤殑瀹炰緥;鏍规嵁褰撳墠鐒︾偣绗旇椤圭殑鐖舵枃浠跺す ID 鍜岀敤鎴峰垱寤虹殑鏂囦欢澶规暟閲忥紝鍐冲畾鏄惁鏄剧ず绉诲姩鑿滃崟椤癸紝 + //骞惰缃Щ鍔ㄨ彍鍗曢」鐨勭偣鍑荤洃鍚櫒涓 this; public boolean onCreateActionMode(ActionMode mode, Menu menu) { getMenuInflater().inflate(R.menu.note_list_options, menu); menu.findItem(R.id.delete).setOnMenuItemClickListener(this); @@ -247,6 +239,7 @@ public class NotesListActivity extends Activity implements OnClickListener, OnIt mMoveMenu.setVisible(true); mMoveMenu.setOnMenuItemClickListener(this); } + //鍒濆鍖 mActionMode 涓哄綋鍓嶇殑涓婁笅鏂囨搷浣滄爮锛岃缃瑪璁板垪琛ㄩ傞厤鍣ㄧ殑閫夋嫨妯″紡涓 true锛岀鐢ㄧ瑪璁板垪琛ㄧ殑闀挎寜浜嬩欢锛岄殣钘忔柊寤虹瑪璁版寜閽 mActionMode = mode; mNotesListAdapter.setChoiceMode(true); mNotesListView.setLongClickable(false); @@ -255,6 +248,7 @@ public class NotesListActivity extends Activity implements OnClickListener, OnIt View customView = LayoutInflater.from(NotesListActivity.this).inflate( R.layout.note_list_dropdown_menu, null); mode.setCustomView(customView); + //鍒涘缓涓涓嚜瀹氫箟瑙嗗浘 mDropDownMenu锛岀敤浜庢樉绀轰笅鎷夎彍鍗曪紝骞惰缃笅鎷夎彍鍗曠殑鐐瑰嚮鐩戝惉鍣 mDropDownMenu = new DropdownMenu(NotesListActivity.this, (Button) customView.findViewById(R.id.selection_menu), R.menu.note_list_dropdown); @@ -268,7 +262,8 @@ public class NotesListActivity extends Activity implements OnClickListener, OnIt }); return true; } - + //鏇存柊涓嬫媺鑿滃崟鐨勬樉绀烘枃鏈拰閫夋嫨鐘舵 + //鑾峰彇宸查夋嫨鐨勭瑪璁版暟閲忥紝骞舵牴鎹暟閲忔洿鏂颁笅鎷夎彍鍗曠殑鏍囬;鏍规嵁鏄惁鍏ㄩ儴閫夋嫨鏉ヨ缃笅鎷夎彍鍗曚腑鍏ㄩ夐」鐨勭姸鎬佸拰鏂囨湰 private void updateMenu() { int selectedCount = mNotesListAdapter.getSelectedCount(); // Update dropdown menu @@ -285,23 +280,23 @@ public class NotesListActivity extends Activity implements OnClickListener, OnIt } } } - + //鍦ㄤ笂涓嬫枃鎿嶄綔鏍忓噯澶囧氨缁椂璋冪敤銆傝繖涓柟娉曞湪鍒涘缓涓婁笅鏂囨搷浣滄爮鍚庤璋冪敤锛岄氬父鐢ㄤ簬璁剧疆涓浜涙搷浣滃墠鐨勫噯澶囧伐浣溿傚湪杩欓噷锛岃繑鍥 false 琛ㄧず娌℃湁棰濆鐨勫噯澶囧伐浣滈渶瑕佹墽琛屻 public boolean onPrepareActionMode(ActionMode mode, Menu menu) { // TODO Auto-generated method stub return false; } - + //鍦ㄧ偣鍑讳笂涓嬫枃鎿嶄綔鏍忕殑鑿滃崟椤规椂璋冪敤銆傝繖涓柟娉曠敤浜庡鐞嗕笂涓嬫枃鎿嶄綔鏍忚彍鍗曢」鐨勭偣鍑讳簨浠躲傚湪杩欓噷锛岃繑鍥 false 琛ㄧず鏈鐞嗚鐐瑰嚮浜嬩欢 public boolean onActionItemClicked(ActionMode mode, MenuItem item) { // TODO Auto-generated method stub return false; } - + //鍦ㄩ攢姣佷笂涓嬫枃鎿嶄綔鏍忔椂璋冪敤銆傝繖涓柟娉曠敤浜庢墽琛屼竴浜涙竻鐞嗗伐浣溿傚湪杩欓噷锛屽畠灏嗙瑪璁板垪琛ㄩ傞厤鍣ㄧ殑閫夋嫨妯″紡璁剧疆涓 false锛屽惎鐢ㄧ瑪璁板垪琛ㄧ殑闀挎寜浜嬩欢锛屾樉绀烘柊寤虹瑪璁版寜閽 public void onDestroyActionMode(ActionMode mode) { mNotesListAdapter.setChoiceMode(false); mNotesListView.setLongClickable(true); mAddNewNote.setVisibility(View.VISIBLE); } - + //缁撴潫涓婁笅鏂囨搷浣滄爮鐨勬樉绀恒傝繖涓柟娉曠敤浜庢墜鍔ㄧ粨鏉熶笂涓嬫枃鎿嶄綔鏍忥紝閫氬父鍦ㄥ畬鎴愪竴绯诲垪鎿嶄綔鍚庤皟鐢 public void finishActionMode() { mActionMode.finish(); } @@ -311,7 +306,7 @@ public class NotesListActivity extends Activity implements OnClickListener, OnIt mNotesListAdapter.setCheckedItem(position, checked); updateMenu(); } - + //鍦ㄧ偣鍑讳笅鎷夎彍鍗曢」鏃惰皟鐢ㄣ傝繖涓柟娉曠敤浜庡鐞嗕笅鎷夎彍鍗曢」鐨勭偣鍑讳簨浠躲傚叿浣撴搷浣滃寘鎷垹闄ら夊畾鐨勭瑪璁版垨绉诲姩閫夊畾鐨勭瑪璁板埌鍏朵粬鏂囦欢澶广傚鏋滄湭閫夋嫨浠讳綍绗旇锛屽垯鏄剧ず鐩稿簲鐨勬彁绀轰俊鎭 public boolean onMenuItemClick(MenuItem item) { if (mNotesListAdapter.getSelectedCount() == 0) { Toast.makeText(NotesListActivity.this, getString(R.string.menu_select_none), @@ -347,7 +342,8 @@ public class NotesListActivity extends Activity implements OnClickListener, OnIt } private class NewNoteOnTouchListener implements OnTouchListener { - + //璇ユ柟娉曞鐞嗘柊寤虹瑪璁版寜閽殑瑙︽懜浜嬩欢銆傚綋鎸変笅锛圓CTION_DOWN锛夋椂锛岄氳繃璁$畻鍧愭爣浣嶇疆鍒ゆ柇鏄惁鐐瑰嚮浜嗘寜閽殑閫忔槑閮ㄥ垎锛屽鏋滄槸锛屽垯灏嗕簨浠朵紶閫掔粰鍒楄〃瑙嗗浘銆 + //鍦ㄧЩ鍔紙ACTION_MOVE锛夋椂锛屽鏋滃凡缁忓紑濮嬪垎娲句簨浠讹紝鍒欏皢浜嬩欢浼犻掔粰鍒楄〃瑙嗗浘銆傚湪鍏朵粬鎯呭喌涓嬶紝杩斿洖 false public boolean onTouch(View v, MotionEvent event) { switch (event.getAction()) { case MotionEvent.ACTION_DOWN: { @@ -407,7 +403,7 @@ public class NotesListActivity extends Activity implements OnClickListener, OnIt } }; - + //璇ユ柟娉曞惎鍔ㄥ紓姝ユ煡璇互鑾峰彇绗旇鍒楄〃銆傛牴鎹綋鍓嶆枃浠跺す鐨勪笉鍚岋紝浣跨敤涓嶅悓鐨勬煡璇㈡潯浠躲傛煡璇㈢粨鏋滃皢閫氳繃 BackgroundQueryHandler 澶勭悊 private void startAsyncNotesListQuery() { String selection = (mCurrentFolderId == Notes.ID_ROOT_FOLDER) ? ROOT_FOLDER_SELECTION : NORMAL_SELECTION; @@ -416,7 +412,7 @@ public class NotesListActivity extends Activity implements OnClickListener, OnIt String.valueOf(mCurrentFolderId) }, NoteColumns.TYPE + " DESC," + NoteColumns.MODIFIED_DATE + " DESC"); } - + //璇ョ被鏄 AsyncQueryHandler 鐨勫瓙绫伙紝鐢ㄤ簬鍦ㄥ悗鍙版墽琛屽紓姝ユ煡璇€傚湪鏌ヨ瀹屾垚鍚庯紝鏍规嵁鏌ヨ鐨勭洰鐨勶紙閫氳繃 token 鍖哄垎锛夛紝鏇存柊鐩稿簲鐨 UI private final class BackgroundQueryHandler extends AsyncQueryHandler { public BackgroundQueryHandler(ContentResolver contentResolver) { super(contentResolver); @@ -440,7 +436,7 @@ public class NotesListActivity extends Activity implements OnClickListener, OnIt } } } - + //璇ユ柟娉曟樉绀轰竴涓璇濇锛屽厑璁哥敤鎴烽夋嫨鐩爣鏂囦欢澶逛互杩涜绉诲姩鎿嶄綔銆備粠鏁版嵁搴撴煡璇㈢殑鏂囦欢澶瑰垪琛ㄩ氳繃閫傞厤鍣紙FoldersListAdapter锛変紶閫掔粰瀵硅瘽妗嗐 private void showFolderListMenu(Cursor cursor) { AlertDialog.Builder builder = new AlertDialog.Builder(NotesListActivity.this); builder.setTitle(R.string.menu_title_select_folder); @@ -461,14 +457,14 @@ public class NotesListActivity extends Activity implements OnClickListener, OnIt }); builder.show(); } - + //鍚姩鏂板缓绗旇鐨勭紪杈戠晫闈€傞氳繃 Intent 璁剧疆鍔ㄤ綔涓烘彃鍏ユ垨缂栬緫锛屽苟浼犻掑綋鍓嶆枃浠跺す鐨 ID銆傛鏂规硶鐢ㄤ簬澶勭悊鏂板缓绗旇鎸夐挳鐨勭偣鍑讳簨浠 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) { @@ -505,7 +501,7 @@ public class NotesListActivity extends Activity implements OnClickListener, OnIt } }.execute(); } - + //鍒犻櫎鎸囧畾鏂囦欢澶瑰強鍏跺唴瀹广傚鏋滀笉鏄悓姝ユā寮忥紝鐩存帴鍒犻櫎鏂囦欢澶瑰強鍏跺寘鍚殑绗旇銆傚湪鍚屾妯″紡涓嬶紝灏嗘枃浠跺す鍙婂叾绗旇绉诲姩鍒板瀮鍦炬枃浠跺す銆傚垹闄ゅ畬鎴愬悗锛屽悓鏍蜂細鏇存柊鐩稿叧鐨勫皬閮ㄤ欢銆 private void deleteFolder(long folderId) { if (folderId == Notes.ID_ROOT_FOLDER) { Log.e(TAG, "Wrong folder id, should not happen " + folderId); @@ -532,14 +528,15 @@ public class NotesListActivity extends Activity implements OnClickListener, OnIt } } } - + //鎵撳紑鎸囧畾绗旇鐨勭紪杈戠晫闈€傚垱寤轰竴涓 Intent 骞朵紶閫掔瑪璁扮殑 ID锛屽惎鍔ㄧ瑪璁扮紪杈戠晫闈€ 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); } - + //鎵撳紑鎸囧畾鏂囦欢澶广傛牴鎹紶鍏ョ殑 NoteItemData 瀵硅薄锛岃缃綋鍓嶆枃浠跺す ID锛岀劧鍚庡紓姝ユ煡璇㈣鏂囦欢澶逛笅鐨勭瑪璁板垪琛ㄣ傛牴鎹枃浠跺す绫诲瀷锛堟槸鍚︿负閫氳瘽璁板綍鏂囦欢澶癸級锛 + //鏇存柊鐣岄潰鐘舵侊紝鍖呮嫭鏍囬鏍忕殑鏄剧ず鍜屾柊寤虹瑪璁版寜閽殑鍙鎬с private void openFolder(NoteItemData data) { mCurrentFolderId = data.getId(); startAsyncNotesListQuery(); @@ -556,7 +553,7 @@ public class NotesListActivity extends Activity implements OnClickListener, OnIt } mTitleBar.setVisibility(View.VISIBLE); } - + //澶勭悊鎸夐挳鐐瑰嚮浜嬩欢鐨勫洖璋冩柟娉曘傜洰鍓嶄粎澶勭悊鏂板缓绗旇鎸夐挳鐨勭偣鍑讳簨浠讹紝璋冪敤 createNewNote() 鏂规硶銆 public void onClick(View v) { switch (v.getId()) { case R.id.btn_new_note: @@ -566,19 +563,20 @@ public class NotesListActivity extends Activity implements OnClickListener, OnIt break; } } - + //鏄剧ず杞敭鐩樸傞氳繃鑾峰彇 InputMethodManager 鐨勫疄渚嬶紝寮哄埗鏄剧ず杞敭鐩 private void showSoftInput() { InputMethodManager inputMethodManager = (InputMethodManager) getSystemService(Context.INPUT_METHOD_SERVICE); if (inputMethodManager != null) { inputMethodManager.toggleSoftInput(InputMethodManager.SHOW_FORCED, 0); } } - + //闅愯棌杞敭鐩樸傞氳繃鑾峰彇 InputMethodManager 鐨勫疄渚嬶紝闅愯棌杞敭鐩橈紝浼犲叆鐨 view 鍙傛暟鐢ㄤ簬纭畾褰撳墠鐒︾偣銆 private void hideSoftInput(View view) { InputMethodManager inputMethodManager = (InputMethodManager) getSystemService(Context.INPUT_METHOD_SERVICE); inputMethodManager.hideSoftInputFromWindow(view.getWindowToken(), 0); } - + //鏄剧ず鍒涘缓鎴栦慨鏀规枃浠跺す鐨勫璇濇銆傛牴鎹弬鏁 create 鍐冲畾鏄垱寤鸿繕鏄慨鏀规枃浠跺す銆傚璇濇涓寘鍚竴涓紪杈戞枃鏈锛岀敤鎴峰彲浠ヨ緭鍏ユ枃浠跺す鍚嶇О銆 + //鐐瑰嚮纭畾鎸夐挳鍚庯紝鏍规嵁杈撳叆鎵ц鍒涘缓鎴栦慨鏀规枃浠跺す鐨勬搷浣溿傚湪瀵硅瘽妗嗕腑瀹炵幇浜嗘枃鏈緭鍏ュ彉鍖栫殑鐩戝惉锛屼互瀹炴椂鏇存柊纭畾鎸夐挳鐨勭姸鎬 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); @@ -648,7 +646,7 @@ public class NotesListActivity extends Activity implements OnClickListener, OnIt // TODO Auto-generated method stub } - + //褰撴枃鏈唴瀹瑰彂鐢熷彉鍖栨椂璋冪敤銆傚湪杩欓噷锛屽鏋滆緭鍏ユ涓殑鏂囨湰鍐呭涓虹┖锛屽氨绂佺敤纭畾鎸夐挳锛坧ositive锛夛紝鍚﹀垯鍚敤纭畾鎸夐挳銆傝繖鏄负浜嗗湪鐢ㄦ埛杈撳叆鍐呭鏃跺疄鏃舵帶鍒剁‘瀹氭寜閽殑鍙偣鍑荤姸鎬 public void onTextChanged(CharSequence s, int start, int before, int count) { if (TextUtils.isEmpty(etName.getText())) { positive.setEnabled(false); @@ -656,14 +654,16 @@ public class NotesListActivity extends Activity implements OnClickListener, OnIt positive.setEnabled(true); } } - + //鍦ㄦ枃鏈唴瀹瑰彂鐢熷彉鍖栦箣鍚庤皟鐢紝杩欎釜鏂规硶鍦 onTextChanged 涔嬪悗琚皟鐢ㄣ + afterTextChanged(Editable s): public void afterTextChanged(Editable s) { // TODO Auto-generated method stub } }); } - + //澶勭悊杩斿洖鎸夐挳鐨勭偣鍑讳簨浠躲傛牴鎹綋鍓嶇殑鐘舵侊紙mState锛夛紝鎵ц涓嶅悓鐨勬搷浣溿傚鏋滃綋鍓嶅湪瀛愭枃浠跺す锛圫UB_FOLDER锛夋垨閫氳瘽璁板綍鏂囦欢澶癸紙CALL_RECORD_FOLDER锛夛紝 + //鍒欒繑鍥炲埌绗旇鍒楄〃銆傚鏋滃綋鍓嶅湪绗旇鍒楄〃锛圢OTE_LIST锛夛紝鍒欐墽琛岄粯璁ょ殑杩斿洖鎿嶄綔 @Override public void onBackPressed() { switch (mState) { @@ -687,7 +687,7 @@ public class NotesListActivity extends Activity implements OnClickListener, OnIt break; } } - + //鏇存柊灏忛儴浠讹紝鏍规嵁灏忛儴浠剁殑绫诲瀷閫夋嫨涓嶅悓鐨 NoteWidgetProvider銆 private void updateWidget(int appWidgetId, int appWidgetType) { Intent intent = new Intent(AppWidgetManager.ACTION_APPWIDGET_UPDATE); if (appWidgetType == Notes.TYPE_WIDGET_2X) { @@ -706,7 +706,7 @@ public class NotesListActivity extends Activity implements OnClickListener, OnIt sendBroadcast(intent); setResult(RESULT_OK, intent); } - + //闀挎寜鏂囦欢澶规椂鐨勪笂涓嬫枃鑿滃崟鍒涘缓鐩戝惉鍣紝鐢ㄤ簬鍦ㄤ笂涓嬫枃鑿滃崟涓樉绀轰笌鏂囦欢澶圭浉鍏崇殑閫夐」锛屽鏌ョ湅銆佸垹闄ゅ拰淇敼鍚嶇О銆 private final OnCreateContextMenuListener mFolderOnCreateContextMenuListener = new OnCreateContextMenuListener() { public void onCreateContextMenu(ContextMenu menu, View v, ContextMenuInfo menuInfo) { if (mFocusNoteDataItem != null) { @@ -717,7 +717,7 @@ public class NotesListActivity extends Activity implements OnClickListener, OnIt } } }; - + //涓婁笅鏂囪彍鍗曞叧闂椂璋冪敤锛岀敤浜庢竻闄や笂涓嬫枃鑿滃崟鐨勫垱寤虹洃鍚櫒銆 @Override public void onContextMenuClosed(Menu menu) { if (mNotesListView != null) { @@ -725,7 +725,7 @@ public class NotesListActivity extends Activity implements OnClickListener, OnIt } super.onContextMenuClosed(menu); } - + //澶勭悊涓婁笅鏂囪彍鍗曢」鐨勯夋嫨浜嬩欢锛屾牴鎹夋嫨鐨勮彍鍗曢」鎵ц鐩稿簲鐨勬搷浣滐紝濡傛煡鐪嬫枃浠跺す銆佸垹闄ゆ枃浠跺す鎴栦慨鏀规枃浠跺す鍚嶇О銆 @Override public boolean onContextItemSelected(MenuItem item) { if (mFocusNoteDataItem == null) { @@ -759,7 +759,7 @@ public class NotesListActivity extends Activity implements OnClickListener, OnIt return true; } - + //鍦ㄨ彍鍗曟樉绀轰箣鍓嶈皟鐢紝鐢ㄤ簬鍔ㄦ佽缃彍鍗曢」銆傛牴鎹綋鍓嶇殑鐘舵侊紙mState锛夛紝鏄剧ず鐩稿簲鐨勮彍鍗曢」銆 @Override public boolean onPrepareOptionsMenu(Menu menu) { menu.clear(); @@ -777,7 +777,7 @@ public class NotesListActivity extends Activity implements OnClickListener, OnIt } return true; } - + // 澶勭悊鑿滃崟椤圭殑閫夋嫨浜嬩欢锛屾牴鎹夋嫨鐨勮彍鍗曢」鎵ц鐩稿簲鐨勬搷浣滐紝濡傚垱寤烘柊鏂囦欢澶广佸鍑虹瑪璁板埌鏂囨湰銆佸悓姝ユ垨鍙栨秷鍚屾銆佹墦寮璁剧疆銆佸垱寤烘柊绗旇绛 @Override public boolean onOptionsItemSelected(MenuItem item) { switch (item.getItemId()) { @@ -817,7 +817,7 @@ public class NotesListActivity extends Activity implements OnClickListener, OnIt } return true; } - + //澶勭悊鎼滅储璇锋眰锛屽惎鍔ㄦ悳绱㈡椿鍔 @Override public boolean onSearchRequested() { startSearch(null, false, null /* appData */, false); @@ -832,7 +832,7 @@ public class NotesListActivity extends Activity implements OnClickListener, OnIt protected Integer doInBackground(Void... unused) { return backup.exportToText(); } - + //瀵煎嚭绗旇鍒版枃鏈枃浠躲傞氳繃鍚庡彴浠诲姟寮傛鎵ц瀵煎嚭鎿嶄綔锛屾牴鎹鍑虹殑缁撴灉鏄剧ず鐩稿簲鐨勬彁绀哄璇濇 @Override protected void onPostExecute(Integer result) { if (result == BackupUtils.STATE_SD_CARD_UNMOUONTED) { @@ -865,19 +865,19 @@ public class NotesListActivity extends Activity implements OnClickListener, OnIt }.execute(); } - + //妫鏌ユ槸鍚﹀浜庡悓姝ユā寮忋傞氳繃妫鏌ュ亸濂借缃腑鍚屾璐︽埛鐨勫悕绉版潵鍒ゆ柇鏄惁鍚敤浜嗗悓姝 private boolean isSyncMode() { return NotesPreferenceActivity.getSyncAccountName(this).trim().length() > 0; } - + //鍚姩鍋忓ソ璁剧疆娲诲姩銆傚鏋滄湁鐖舵椿鍔紝鍒欎娇鐢ㄧ埗娲诲姩锛屽惁鍒欎娇鐢ㄥ綋鍓嶆椿鍔ㄥ惎鍔ㄥ亸濂借缃椿鍔 private void startPreferenceActivity() { Activity from = getParent() != null ? getParent() : this; Intent intent = new Intent(from, NotesPreferenceActivity.class); from.startActivityIfNeeded(intent, -1); } - + //鍒楄〃椤圭偣鍑荤洃鍚櫒锛屽鐞嗗垪琛ㄩ」鐨勭偣鍑讳簨浠躲傛牴鎹綋鍓嶇殑鐘舵侊紙mState锛夊拰鐐瑰嚮鐨勯」鐨勭被鍨嬫墽琛屼笉鍚岀殑鎿嶄綔锛屽鎵撳紑鏂囦欢澶规垨绗旇 private class OnListItemClickListener implements OnItemClickListener { - + //澶勭悊鍒楄〃椤圭殑鐐瑰嚮浜嬩欢銆傛牴鎹綋鍓嶇殑鐘舵侊紙mState锛夊拰鐐瑰嚮鐨勯」鐨勭被鍨嬫墽琛屼笉鍚岀殑鎿嶄綔锛屽鎵撳紑鏂囦欢澶规垨绗旇銆 public void onItemClick(AdapterView parent, View view, int position, long id) { if (view instanceof NotesListItem) { NoteItemData item = ((NotesListItem) view).getItemData(); @@ -916,7 +916,7 @@ public class NotesListActivity extends Activity implements OnClickListener, OnIt } } - + //鍚姩鏌ヨ鐩爣鏂囦欢澶圭殑寮傛鏌ヨ銆傛牴鎹綋鍓嶇殑鐘舵侊紙mState锛夋瀯寤烘煡璇紝鏌ヨ闄や簡鍨冨溇绠卞拰褰撳墠鏂囦欢澶逛箣澶栫殑鎵鏈夋枃浠跺す锛屽苟鎸変慨鏀规棩鏈熼檷搴忔帓鍒 private void startQueryDestinationFolders() { String selection = NoteColumns.TYPE + "=? AND " + NoteColumns.PARENT_ID + "<>? AND " + NoteColumns.ID + "<>?"; selection = (mState == ListEditState.NOTE_LIST) ? selection: @@ -934,7 +934,8 @@ public class NotesListActivity extends Activity implements OnClickListener, OnIt }, NoteColumns.MODIFIED_DATE + " DESC"); } - + //澶勭悊鍒楄〃椤圭殑闀挎寜浜嬩欢銆傚鏋滈暱鎸夌殑鏄瑪璁伴」涓斾笉鍦ㄩ夋嫨妯″紡涓嬶紝鍒欏惎鍔ㄤ笂涓嬫枃鎿嶄綔鏍忥紙ActionMode锛夛紝骞堕氳繃 mModeCallBack 澶勭悊椤圭殑閫夋嫨鐘舵併 + //濡傛灉闀挎寜鐨勬槸鏂囦欢澶归」锛屽垯璁剧疆涓婁笅鏂囪彍鍗曠洃鍚櫒锛坢FolderOnCreateContextMenuListener锛 public boolean onItemLongClick(AdapterView parent, View view, int position, long id) { if (view instanceof NotesListItem) { mFocusNoteDataItem = ((NotesListItem) view).getItemData(); diff --git a/src/src/net/micode/notes/ui/NotesListAdapter.java b/src/src/net/micode/notes/ui/ui/NotesListAdapter.java similarity index 84% rename from src/src/net/micode/notes/ui/NotesListAdapter.java rename to src/src/net/micode/notes/ui/ui/NotesListAdapter.java index 51c9cb9..4f8af07 100644 --- a/src/src/net/micode/notes/ui/NotesListAdapter.java +++ b/src/src/net/micode/notes/ui/ui/NotesListAdapter.java @@ -1,17 +1,6 @@ /* - * 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. + Edit by yanjingyu + 该类用于显示笔记列表的自定义适配器类,主要用于将数据库中的笔记数据与 UI 控件进行关联。使得用户可以在笔记列表中进行选择操作,并能够获取选中项的相关信息。 */ package net.micode.notes.ui; @@ -30,7 +19,7 @@ import java.util.HashMap; import java.util.HashSet; import java.util.Iterator; - +//构造方法初始化适配器,初始化上下文(mContext),选择模式标志(mChoiceMode),以及用于存储选中项索引的哈希映射(mSelectedIndex)。 public class NotesListAdapter extends CursorAdapter { private static final String TAG = "NotesListAdapter"; private Context mContext; @@ -49,12 +38,12 @@ public class NotesListAdapter extends CursorAdapter { mContext = context; mNotesCount = 0; } - +//用于创建新的笔记列表项视图。 @Override 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) { @@ -63,21 +52,21 @@ public class NotesListAdapter extends CursorAdapter { isSelectedItem(cursor.getPosition())); } } - +//设置指定位置的笔记项的选中状态,并通知适配器数据集发生变化。 public void setCheckedItem(final int position, final boolean checked) { mSelectedIndex.put(position, checked); notifyDataSetChanged(); } - +//检查是否处于选择模式。 public boolean isInChoiceMode() { return mChoiceMode; } - +//设置选择模式,清空选中项索引。 public void setChoiceMode(boolean mode) { mSelectedIndex.clear(); mChoiceMode = mode; } - +//选择或取消选择所有笔记项。 public void selectAll(boolean checked) { Cursor cursor = getCursor(); for (int i = 0; i < getCount(); i++) { @@ -88,7 +77,7 @@ public class NotesListAdapter extends CursorAdapter { } } } - +//获取选中的笔记项的ID集合。 public HashSet getSelectedItemIds() { HashSet itemSet = new HashSet(); for (Integer position : mSelectedIndex.keySet()) { @@ -104,7 +93,8 @@ public class NotesListAdapter extends CursorAdapter { return itemSet; } - +//获取选中的笔记项中的小部件属性集合。对于每个选中的笔记项,它创建一个 AppWidgetAttribute 对象,其中包含小部件的ID和类型。 +//返回一个包含所有选中笔记项的小部件属性的 HashSet。 public HashSet getSelectedWidget() { HashSet itemSet = new HashSet(); for (Integer position : mSelectedIndex.keySet()) { @@ -127,7 +117,7 @@ public class NotesListAdapter extends CursorAdapter { } return itemSet; } - +//获取选中的笔记项的数量。 public int getSelectedCount() { Collection values = mSelectedIndex.values(); if (null == values) { @@ -142,31 +132,31 @@ public class NotesListAdapter extends CursorAdapter { } 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 protected void onContentChanged() { super.onContentChanged(); calcNotesCount(); } - +//在更改光标时调用,用于重新计算笔记项的总数。 @Override public void changeCursor(Cursor cursor) { super.changeCursor(cursor); calcNotesCount(); } - +//计算笔记项的总数。 private void calcNotesCount() { mNotesCount = 0; for (int i = 0; i < getCount(); i++) { diff --git a/src/src/net/micode/notes/ui/NotesListItem.java b/src/src/net/micode/notes/ui/ui/NotesListItem.java similarity index 86% rename from src/src/net/micode/notes/ui/NotesListItem.java rename to src/src/net/micode/notes/ui/ui/NotesListItem.java index 1221e80..de93434 100644 --- a/src/src/net/micode/notes/ui/NotesListItem.java +++ b/src/src/net/micode/notes/ui/ui/NotesListItem.java @@ -1,19 +1,7 @@ /* - * 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. + Edit by yanjingyu + 该类是一个自定义的笔记列表项视图类,用于显示笔记列表中的每一项。负责管理笔记列表中每个列表项的布局和显示,以及与之关联的数据。 */ - package net.micode.notes.ui; import android.content.Context; @@ -37,7 +25,7 @@ public class NotesListItem extends LinearLayout { private TextView mCallName; private NoteItemData mItemData; private CheckBox mCheckBox; - +//构造函数初始化视图,并通过 inflate 方法将布局文件 note_item.xml 载入该自定义视图。 public NotesListItem(Context context) { super(context); inflate(context, R.layout.note_item, this); @@ -47,7 +35,7 @@ public class NotesListItem extends LinearLayout { mCallName = (TextView) findViewById(R.id.tv_name); mCheckBox = (CheckBox) findViewById(android.R.id.checkbox); } - +//该方法用于将数据绑定到视图上。它根据传入的数据项以及选择模式和选中状态来更新视图元素,例如标题、时间、提醒图标等。 public void bind(Context context, NoteItemData data, boolean choiceMode, boolean checked) { if (choiceMode && data.getType() == Notes.TYPE_NOTE) { mCheckBox.setVisibility(View.VISIBLE); @@ -98,7 +86,7 @@ public class NotesListItem extends LinearLayout { setBackground(data); } - +//该方法根据笔记项的类型和位置来设置背景。根据笔记项是文件夹还是笔记,以及其在列表中的位置,选择相应的背景资源。 private void setBackground(NoteItemData data) { int id = data.getBgColorId(); if (data.getType() == Notes.TYPE_NOTE) { @@ -115,7 +103,7 @@ public class NotesListItem extends LinearLayout { setBackgroundResource(NoteItemBgResources.getFolderBgRes()); } } - +//获取当前 NotesListItem 对象的关联 NoteItemData。 public NoteItemData getItemData() { return mItemData; } diff --git a/src/src/net/micode/notes/ui/NotesPreferenceActivity.java b/src/src/net/micode/notes/ui/ui/NotesPreferenceActivity.java similarity index 88% rename from src/src/net/micode/notes/ui/NotesPreferenceActivity.java rename to src/src/net/micode/notes/ui/ui/NotesPreferenceActivity.java index 07c5f7e..8b6403d 100644 --- a/src/src/net/micode/notes/ui/NotesPreferenceActivity.java +++ b/src/src/net/micode/notes/ui/ui/NotesPreferenceActivity.java @@ -1,17 +1,7 @@ /* - * 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. + Edit by yanjingyu + 该类用于显示和管理笔记应用偏好设置,包括同步账户、同步按钮、上次同步时间等。 + */ package net.micode.notes.ui; @@ -68,7 +58,7 @@ public class NotesPreferenceActivity extends PreferenceActivity { private Account[] mOriAccounts; private boolean mHasAddedAccount; - +//该类从资源文件 R.xml.preferences 中加载首选项,并设置了一些必要的界面元素,如显示返回按钮、注册广播接收器等。 @Override protected void onCreate(Bundle icicle) { super.onCreate(icicle); @@ -87,7 +77,7 @@ public class NotesPreferenceActivity extends PreferenceActivity { View header = LayoutInflater.from(this).inflate(R.layout.settings_header, null); getListView().addHeaderView(header, null, true); } - +//通过调用 refreshUI 方法刷新用户界面,根据同步状态、同步按钮等动态更新设置界面。 @Override protected void onResume() { super.onResume(); @@ -115,7 +105,7 @@ public class NotesPreferenceActivity extends PreferenceActivity { refreshUI(); } - +//注销了之前注册的广播接收器,确保在销毁活动时不再接收广播。 @Override protected void onDestroy() { if (mReceiver != null) { @@ -123,7 +113,7 @@ public class NotesPreferenceActivity extends PreferenceActivity { } super.onDestroy(); } - +//负责加载并显示与帐户相关的首选项。它创建一个 Preference 对象,用于显示帐户的标题和摘要,并设置点击监听器以便在用户点击时触发相应的操作。 private void loadAccountPreference() { mAccountCategory.removeAll(); @@ -153,7 +143,7 @@ public class NotesPreferenceActivity extends PreferenceActivity { 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); @@ -192,12 +182,13 @@ public class NotesPreferenceActivity extends PreferenceActivity { } } } - +//用于刷新用户界面,它调用 loadAccountPreference 和 loadSyncButton 方法,以便动态更新设置界面的内容。 private void refreshUI() { loadAccountPreference(); loadSyncButton(); } - +//显示选择账户的对话框。创建一个 AlertDialog 对象,包含了用户当前的账户列表以供选择,以及一个用于添加新账户的选项。 +//用户可以从列表中选择现有账户,或点击添加账户以添加新账户。 private void showSelectAccountAlertDialog() { AlertDialog.Builder dialogBuilder = new AlertDialog.Builder(this); @@ -253,7 +244,8 @@ public class NotesPreferenceActivity extends PreferenceActivity { } }); } - +//显示更改账户的确认对话框。该方法创建一个 AlertDialog 对象,显示当前账户信息,并提供更改账户、移除账户和取消操作的选项。 +//用户可以选择更改账户,移除账户,或取消操作。 private void showChangeAccountConfirmAlertDialog() { AlertDialog.Builder dialogBuilder = new AlertDialog.Builder(this); @@ -282,12 +274,12 @@ public class NotesPreferenceActivity extends PreferenceActivity { }); dialogBuilder.show(); } - +//获取设备上的 Google 账户列表。通过调用 AccountManager.getAccountsByType("com.google") 获取。 private Account[] getGoogleAccounts() { AccountManager accountManager = AccountManager.get(this); return accountManager.getAccountsByType("com.google"); } - +//设置同步账户,接受一个账户名作为参数,将其保存到 SharedPreferences 中,并清除与同步相关的本地数据 private void setSyncAccount(String account) { if (!getSyncAccountName(this).equals(account)) { SharedPreferences settings = getSharedPreferences(PREFERENCE_NAME, Context.MODE_PRIVATE); @@ -317,7 +309,7 @@ public class NotesPreferenceActivity extends PreferenceActivity { Toast.LENGTH_SHORT).show(); } } - +//移除同步账户,从 SharedPreferences 中移除同步账户信息,并清除与同步相关的本地数据。 private void removeSyncAccount() { SharedPreferences settings = getSharedPreferences(PREFERENCE_NAME, Context.MODE_PRIVATE); SharedPreferences.Editor editor = settings.edit(); @@ -339,13 +331,13 @@ public class NotesPreferenceActivity extends PreferenceActivity { } }).start(); } - +//获取当前同步账户的名称,从 SharedPreferences 中读取保存的同步账户信息。 public static String getSyncAccountName(Context context) { SharedPreferences settings = context.getSharedPreferences(PREFERENCE_NAME, Context.MODE_PRIVATE); return settings.getString(PREFERENCE_SYNC_ACCOUNT_NAME, ""); } - +//设置上次同步时间,将上次同步时间保存到 SharedPreferences 中。 public static void setLastSyncTime(Context context, long time) { SharedPreferences settings = context.getSharedPreferences(PREFERENCE_NAME, Context.MODE_PRIVATE); @@ -353,15 +345,15 @@ public class NotesPreferenceActivity extends PreferenceActivity { editor.putLong(PREFERENCE_LAST_SYNC_TIME, time); editor.commit(); } - +//获取上次同步时间,从 SharedPreferences 中读取保存的上次同步时间信息。 public static long getLastSyncTime(Context context) { SharedPreferences settings = context.getSharedPreferences(PREFERENCE_NAME, Context.MODE_PRIVATE); return settings.getLong(PREFERENCE_LAST_SYNC_TIME, 0); } - +//用于接收与 GTask 同步服务相关的广播。 private class GTaskReceiver extends BroadcastReceiver { - +//它调用了 refreshUI 方法以刷新用户界面,同时根据广播中的同步状态更新同步状态的文本信息。 @Override public void onReceive(Context context, Intent intent) { refreshUI(); @@ -373,7 +365,8 @@ public class NotesPreferenceActivity extends PreferenceActivity { } } - +//一个处理选项菜单项选择事件的方法。当选择的菜单项的 ID 为 android.R.id.home(即返回按钮),它创建一个 Intent 对象,用于导航到 NotesListActivity 类, +//并清除了任务栈上的所有活动,确保返回到 NotesListActivity 时,它是任务栈的根活动。最后,通过调用 startActivity(intent) 启动该活动。 public boolean onOptionsItemSelected(MenuItem item) { switch (item.getItemId()) { case android.R.id.home: diff --git a/src/src/net/micode/notes/widget/NoteWidgetProvider.java b/src/src/net/micode/notes/widget/NoteWidgetProvider.java deleted file mode 100644 index ec6f819..0000000 --- a/src/src/net/micode/notes/widget/NoteWidgetProvider.java +++ /dev/null @@ -1,132 +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.widget; -import android.app.PendingIntent; -import android.appwidget.AppWidgetManager; -import android.appwidget.AppWidgetProvider; -import android.content.ContentValues; -import android.content.Context; -import android.content.Intent; -import android.database.Cursor; -import android.util.Log; -import android.widget.RemoteViews; - -import net.micode.notes.R; -import net.micode.notes.data.Notes; -import net.micode.notes.data.Notes.NoteColumns; -import net.micode.notes.tool.ResourceParser; -import net.micode.notes.ui.NoteEditActivity; -import net.micode.notes.ui.NotesListActivity; - -public abstract class NoteWidgetProvider extends AppWidgetProvider { - public static final String [] PROJECTION = new String [] { - NoteColumns.ID, - NoteColumns.BG_COLOR_ID, - NoteColumns.SNIPPET - }; - - public static final int COLUMN_ID = 0; - public static final int COLUMN_BG_COLOR_ID = 1; - public static final int COLUMN_SNIPPET = 2; - - private static final String TAG = "NoteWidgetProvider"; - - @Override - public void onDeleted(Context context, int[] appWidgetIds) { - ContentValues values = new ContentValues(); - values.put(NoteColumns.WIDGET_ID, AppWidgetManager.INVALID_APPWIDGET_ID); - for (int i = 0; i < appWidgetIds.length; i++) { - context.getContentResolver().update(Notes.CONTENT_NOTE_URI, - values, - NoteColumns.WIDGET_ID + "=?", - new String[] { String.valueOf(appWidgetIds[i])}); - } - } - - private Cursor getNoteWidgetInfo(Context context, int widgetId) { - return context.getContentResolver().query(Notes.CONTENT_NOTE_URI, - PROJECTION, - NoteColumns.WIDGET_ID + "=? AND " + NoteColumns.PARENT_ID + "<>?", - new String[] { String.valueOf(widgetId), String.valueOf(Notes.ID_TRASH_FOLER) }, - null); - } - - protected void update(Context context, AppWidgetManager appWidgetManager, int[] appWidgetIds) { - update(context, appWidgetManager, appWidgetIds, false); - } - - private void update(Context context, AppWidgetManager appWidgetManager, int[] appWidgetIds, - boolean privacyMode) { - for (int i = 0; i < appWidgetIds.length; i++) { - if (appWidgetIds[i] != AppWidgetManager.INVALID_APPWIDGET_ID) { - int bgId = ResourceParser.getDefaultBgId(context); - String snippet = ""; - Intent intent = new Intent(context, NoteEditActivity.class); - intent.setFlags(Intent.FLAG_ACTIVITY_SINGLE_TOP); - intent.putExtra(Notes.INTENT_EXTRA_WIDGET_ID, appWidgetIds[i]); - intent.putExtra(Notes.INTENT_EXTRA_WIDGET_TYPE, getWidgetType()); - - Cursor c = getNoteWidgetInfo(context, appWidgetIds[i]); - if (c != null && c.moveToFirst()) { - if (c.getCount() > 1) { - Log.e(TAG, "Multiple message with same widget id:" + appWidgetIds[i]); - c.close(); - return; - } - snippet = c.getString(COLUMN_SNIPPET); - bgId = c.getInt(COLUMN_BG_COLOR_ID); - intent.putExtra(Intent.EXTRA_UID, c.getLong(COLUMN_ID)); - intent.setAction(Intent.ACTION_VIEW); - } else { - snippet = context.getResources().getString(R.string.widget_havenot_content); - intent.setAction(Intent.ACTION_INSERT_OR_EDIT); - } - - if (c != null) { - c.close(); - } - - RemoteViews rv = new RemoteViews(context.getPackageName(), getLayoutId()); - 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) { - rv.setTextViewText(R.id.widget_text, - context.getString(R.string.widget_under_visit_mode)); - pendingIntent = PendingIntent.getActivity(context, appWidgetIds[i], new Intent( - context, NotesListActivity.class), PendingIntent.FLAG_UPDATE_CURRENT); - } else { - rv.setTextViewText(R.id.widget_text, snippet); - pendingIntent = PendingIntent.getActivity(context, appWidgetIds[i], intent, - PendingIntent.FLAG_UPDATE_CURRENT); - } - - rv.setOnClickPendingIntent(R.id.widget_text, pendingIntent); - appWidgetManager.updateAppWidget(appWidgetIds[i], rv); - } - } - } - - protected abstract int getBgResourceId(int bgId); - - protected abstract int getLayoutId(); - - protected abstract int getWidgetType(); -} diff --git a/src/src/net/micode/notes/widget/NoteWidgetProvider_2x.java b/src/src/net/micode/notes/widget/NoteWidgetProvider_2x.java deleted file mode 100644 index adcb2f7..0000000 --- a/src/src/net/micode/notes/widget/NoteWidgetProvider_2x.java +++ /dev/null @@ -1,47 +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.widget; - -import android.appwidget.AppWidgetManager; -import android.content.Context; - -import net.micode.notes.R; -import net.micode.notes.data.Notes; -import net.micode.notes.tool.ResourceParser; - - -public class NoteWidgetProvider_2x extends NoteWidgetProvider { - @Override - public void onUpdate(Context context, AppWidgetManager appWidgetManager, int[] appWidgetIds) { - super.update(context, appWidgetManager, appWidgetIds); - } - - @Override - protected int getLayoutId() { - return R.layout.widget_2x; - } - - @Override - protected int getBgResourceId(int bgId) { - return ResourceParser.WidgetBgResources.getWidget2xBgResource(bgId); - } - - @Override - protected int getWidgetType() { - return Notes.TYPE_WIDGET_2X; - } -} diff --git a/src/src/net/micode/notes/widget/NoteWidgetProvider_4x.java b/src/src/net/micode/notes/widget/NoteWidgetProvider_4x.java deleted file mode 100644 index c12a02e..0000000 --- a/src/src/net/micode/notes/widget/NoteWidgetProvider_4x.java +++ /dev/null @@ -1,46 +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.widget; - -import android.appwidget.AppWidgetManager; -import android.content.Context; - -import net.micode.notes.R; -import net.micode.notes.data.Notes; -import net.micode.notes.tool.ResourceParser; - - -public class NoteWidgetProvider_4x extends NoteWidgetProvider { - @Override - public void onUpdate(Context context, AppWidgetManager appWidgetManager, int[] appWidgetIds) { - super.update(context, appWidgetManager, appWidgetIds); - } - - protected int getLayoutId() { - return R.layout.widget_4x; - } - - @Override - protected int getBgResourceId(int bgId) { - return ResourceParser.WidgetBgResources.getWidget4xBgResource(bgId); - } - - @Override - protected int getWidgetType() { - return Notes.TYPE_WIDGET_4X; - } -} diff --git a/src/src/net/micode/notes/widget/widget/NoteWidgetProvider.java b/src/src/net/micode/notes/widget/widget/NoteWidgetProvider.java new file mode 100644 index 0000000..dfb1a7e --- /dev/null +++ b/src/src/net/micode/notes/widget/widget/NoteWidgetProvider.java @@ -0,0 +1,171 @@ +/* + * 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.widget; + + +//导入需要的Android库 +import android.app.PendingIntent; +import android.appwidget.AppWidgetManager; +import android.appwidget.AppWidgetProvider; +import android.content.ContentValues; +import android.content.Context; +import android.content.Intent; +import android.database.Cursor; +import android.util.Log; +import android.widget.RemoteViews; + +import net.micode.notes.R; // 导入自定义资源类,该类包含应用程序中使用的所有资源,例如字符串,图像,布局文件等 +import net.micode.notes.data.Notes; // 导入Notes数据类,该类包含所有关于笔记的数据结构和操作 +import net.micode.notes.data.NoteColumns; // 导入NoteColumns数据类,该类包含Note表的所有列名和类型 +import net.micode.notes.tool.ResourceParser; // 导入ResourceParser工具类,该类用于解析资源文件中的数据 +import net.micode.notes.ui.NoteEditActivity; // 导入NoteEditActivity类,该类是用于编辑笔记的Activity +import net.micode.notes.ui.NotesListActivity; // 导入NotesListActivity类,该类是用于显示所有笔记的Activity + +//声明一个抽象类NoteWidgetProvider,继承自AppWidgetProvider +public abstract class NoteWidgetProvider extends AppWidgetProvider { + // 定义一个字符串数组PROJECTION,包含NoteColumns的三个字段:ID,背景颜色ID和片段 + public static final String [] PROJECTION = new String [] { + NoteColumns.ID, + NoteColumns.BG_COLOR_ID, + NoteColumns.SNIPPET + }; + // 定义三个对应的整型常量,用于后续方便使用 + public static final int COLUMN_ID = 0; + public static final int COLUMN_BG_COLOR_ID = 1; + public static final int COLUMN_SNIPPET = 2; + + // 定义一个私有的静态常量TAG,用于记录日志时标识来源 + private static final String TAG = "NoteWidgetProvider"; + + // 重写AppWidgetProvider的onDeleted方法,当widget被删除时调用 + @Override + public void onDeleted(Context context, int[] appWidgetIds) { + ContentValues values = new ContentValues(); // 创建一个ContentValues对象,用于存储数据变更的键值对 + values.put(NoteColumns.WIDGET_ID, AppWidgetManager.INVALID_APPWIDGET_ID); // 将数据中的WIDGET_ID字段设置为无效值 + for (int i = 0; i < appWidgetIds.length; i++) { // 遍历所有被删除的widget的id + context.getContentResolver().update(Notes.CONTENT_NOTE_URI, // 更新Note表中的数据,将对应widget的数据的WIDGET_ID字段设置为无效值 + values, + NoteColumns.WIDGET_ID + "=?", // 构建更新条件的SQL语句,条件是WIDGET_ID等于当前遍历到的widget id + new String[] { String.valueOf(appWidgetIds[i])}); // 将当前遍历到的widget id转换为字符串,作为SQL语句的参数 + } + } + +/// 定义一个私有的名为getNoteWidgetInfo的方法,该方法接收两个参数:一个上下文对象context和一个widgetId。这个方法可能用于从数据存储中获取特定widgetId的相关信息。 + private Cursor getNoteWidgetInfo(Context context, int widgetId) { + // 从上下文对象获取内容解析器,它是一个用于访问底层数据存储(如SQLite数据库)的对象。 + return context.getContentResolver().query(Notes.CONTENT_NOTE_URI, + // 定义一个名为PROJECTION的变量,这可能是一个用于过滤查询结果的字符串数组。 + PROJECTION, + // 在查询中设置过滤条件。这里,我们查找widgetId等于给定值并且parentId不等于特定值(Notes.ID_TRASH_FOLER)的记录。 + NoteColumns.WIDGET_ID + "=? AND " + NoteColumns.PARENT_ID + "<>?", + // 创建一个字符串数组,其中包含两个通过String.valueOf方法转换的参数值。第一个是widgetId,第二个是Notes.ID_TRASH_FOLER。 + new String[] { String.valueOf(widgetId), String.valueOf(Notes.ID_TRASH_FOLER) }, + // 在查询中指定排序方式。这里没有给出排序方式,所以为null。 + null); + } + + // 定义一个受保护的名为update的方法,该方法接收三个参数:一个上下文对象context,一个AppWidgetManager对象appWidgetManager,和一个int数组appWidgetIds。这个方法可能用于更新应用小部件的信息。 + protected void update(Context context, AppWidgetManager appWidgetManager, int[] appWidgetIds) { + // 调用update方法,传入相同的参数,但增加一个额外的参数,即布尔值false。此方法可能用于执行特定条件下的更新操作。 + update(context, appWidgetManager, appWidgetIds, false); +} + + // 定义一个私有方法update,该方法接收四个参数:上下文对象context,AppWidgetManager对象appWidgetManager,int数组appWidgetIds以及一个布尔值privacyMode。此方法可能用于更新应用小部件的信息。 + private void update(Context context, AppWidgetManager appWidgetManager, int[] appWidgetIds, + boolean privacyMode) { + // 遍历所有的appWidgetIds + for (int i = 0; i < appWidgetIds.length; i++) { + // 检查appWidgetIds[i]是否不为AppWidgetManager.INVALID_APPWIDGET_ID,如果不是,则跳过后续操作。AppWidgetManager.INVALID_APPWIDGET_ID表示无效的app部件ID。 + if (appWidgetIds[i] != AppWidgetManager.INVALID_APPWIDGET_ID) { + // 调用ResourceParser的getDefaultBgId方法,传入上下文对象context,以获取默认的背景ID,并将其赋值给变量bgId。 + int bgId = ResourceParser.getDefaultBgId(context); + // 初始化一个空字符串变量snippet。 + String snippet = ""; + // 创建一个新的意图(Intent)对象,该意图将启动NoteEditActivity。设置FLAG_ACTIVITY_SINGLE_TOP标志位,表示如果NoteEditActivity已经存在在堆栈顶,则不会重新创建它。 + Intent intent = new Intent(context, NoteEditActivity.class); + intent.setFlags(Intent.FLAG_ACTIVITY_SINGLE_TOP); + // 将appWidgetIds[i]作为extra数据传递给意图,键为Notes.INTENT_EXTRA_WIDGET_ID。 + intent.putExtra(Notes.INTENT_EXTRA_WIDGET_ID, appWidgetIds[i]); + // 调用getWidgetType方法获取widget类型并将其作为extra数据传递给意图,键为Notes.INTENT_EXTRA_WIDGET_TYPE。 + intent.putExtra(Notes.INTENT_EXTRA_WIDGET_TYPE, getWidgetType()); + + // 调用getNoteWidgetInfo方法,传入上下文对象context和appWidgetIds[i],以获取与指定widget ID关联的数据。查询结果存储在Cursor对象c中。 + Cursor c = getNoteWidgetInfo(context, appWidgetIds[i]); + // 检查c是否不为null并且是否可以移动到第一条记录。如果满足条件,则继续执行下面的代码块。 + if (c != null && c.moveToFirst()) { + if (c != null && c.moveToFirst()) { + // 如果c中包含多于一条记录,则打印一条错误日志信息并关闭cursor对象c,然后返回。 + if (c.getCount() > 1) { + Log.e(TAG, "Multiple message with same widget id:" + appWidgetIds[i]); + c.close(); + return; + } + // 从c中获取指定列的字符串值并赋值给snippet。这里使用的是COLUMN_SNIPPET常量。 + snippet = c.getString(COLUMN_SNIPPET); + // 从c中获取指定列的整数值并赋值给bgId。这里使用的是COLUMN_BG_COLOR_ID常量。 + bgId = c.getInt(COLUMN_BG_COLOR_ID); + // 将id作为extra数据传递给意图,键为Intent.EXTRA_UID。 + intent.putExtra(Intent.EXTRA_UID, c.getLong(COLUMN_ID)); + // 设置意图的动作为Intent.ACTION_VIEW。这表示当这个意图被触发时,系统会查看或提供用户界面来查看或编辑一条信息。 + intent.setAction(Intent.ACTION_VIEW); + } else { + // 如果c为null或者不能移动到第一条记录,则设置snippet为指定字符串资源ID对应的字符串值。这里使用的是R.string.widget_havenot_content,表示widget还没有内容。 + snippet = context.getResources().getString(R.string.widget_havenot_content); + // 设置意图的动作为Intent.ACTION_INSERT_OR_EDIT。这表示当这个意图被触发时,系统会插入或编辑一条信息 + intent.setAction(Intent.ACTION_INSERT_OR_EDIT); + } + + // 如果变量c不为空(即c存在),则关闭c。这是一种良好的资源管理实践,可以确保不再需要的资源被及时释放。 + if (c != null) { + if (c != null) { + c.close(); + } + + // 创建一个新的RemoteViews对象,它用于定义在appWidgetIds[i]位置的小部件的布局和行为。 + // RemoteViews构造函数接收两个参数,第一个是包名,第二个是布局ID。 + RemoteViews rv = new RemoteViews(context.getPackageName(), getLayoutId()); + + // 使用getImageViewResource方法设置widget_bg_image的图像资源ID。这是从getBgResourceId方法获取的,该方法是一个抽象方法,需要由子类实现以返回背景图像的资源ID。 + rv.setImageViewResource(R.id.widget_bg_image, getBgResourceId(bgId)); + rv.setImageViewResource(R.id.widget_bg_image, getBgResourceId(bgId)); + + // 将背景ID作为额外数据添加到intent中,这样它就可以在启动的Activity或Service中使用 + intent.putExtra(Notes.INTENT_EXTRA_BACKGROUND_ID, bgId); + + // 这是一段注释,描述了下面的代码将生成一个用于启动宿主Activity的PendingIntent。 +/** + * Generate the pending intent to start host for the widget + */ + PendingIntent pendingIntent = null; + + // 如果隐私模式为真,则设置widget_text的文本为"widget_under_visit_mode",这是一个字符串资源ID,它可能表示"正在访问模式"。同时创建一个PendingIntent,这个PendingIntent将在稍后被设置到RemoteViews上,以便在用户点击widget时启动NotesListActivity。 + if (privacyMode) { + rv.setTextViewText(R.id.widget_text, context.getString(R.string.widget_under_visit_mode)); + pendingIntent = PendingIntent.getActivity(context, appWidgetIds[i], new Intent(context, NotesListActivity.class), PendingIntent.FLAG_UPDATE_CURRENT); +} else { + // 如果隐私模式为假,则设置widget_text的文本为"snippet",这是一个变量,可能表示一段可读的内容。同时创建一个PendingIntent,这个PendingIntent将在稍后被设置到RemoteViews上,以便在用户点击widget时启动由intent定义的Activity。 + rv.setTextViewText(R.id.widget_text, snippet); + pendingIntent = PendingIntent.getActivity(context, appWidgetIds[i], intent, PendingIntent.FLAG_UPDATE_CURRENT); +} + + // 使用setOnClickPendingIntent方法设置当用户点击widget_text时应该启动的PendingIntent。这个PendingIntent之前已经被创建并赋值给了pendingIntent变量。 +rv.setOnClickPendingIntent(R.id.widget_text, pendingIntent); + +//使用appWidgetManager对象的updateAppWidget方法更新指定位置的widget。这会导致小部件的布局被更新以反映在RemoteViews对象中做的更改。 +appWidgetManager.updateAppWidget(appWidgetIds[i], rv); \ No newline at end of file diff --git a/src/src/net/micode/notes/widget/widget/NoteWidgetProvider_2x.java b/src/src/net/micode/notes/widget/widget/NoteWidgetProvider_2x.java new file mode 100644 index 0000000..beddb46 --- /dev/null +++ b/src/src/net/micode/notes/widget/widget/NoteWidgetProvider_2x.java @@ -0,0 +1,58 @@ +/* + * 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. + */ + +// 定义一个位于net.micode.notes.widget包下的NoteWidgetProvider_2x类,这个类继承自NoteWidgetProvider类 +package net.micode.notes.widget; + +// 导入必要的Android库 +import android.appwidget.AppWidgetManager; // 管理App小部件的类 +import android.content.Context; // 提供应用环境信息的接口 + +// 导入项目内部的库 +import net.micode.notes.R; // 项目的资源文件 +import net.micode.notes.data.Notes; // 项目的数据模型类,可能包含笔记的数据结构和方法 +import net.micode.notes.tool.ResourceParser; // 项目的工具类,用于解析资源 + +// NoteWidgetProvider_2x类的定义开始 +public class NoteWidgetProvider_2x extends NoteWidgetProvider { + // 重写父类的onUpdate方法,这是在更新小部件时被调用的方法 + @Override + public void onUpdate(Context context, AppWidgetManager appWidgetManager, int[] appWidgetIds) { + // 调用父类的update方法,传递相同的参数,进行实际的更新操作 + super.update(context, appWidgetManager, appWidgetIds); + } + + // 重写父类的getLayoutId方法,这个方法用于获取小部件的布局资源ID + @Override + protected int getLayoutId() { + // 返回预定义的2x小部件布局资源ID + return R.layout.widget_2x; + } + + // 重写父类的getBgResourceId方法,这个方法用于获取小部件的背景资源ID + @Override + protected int getBgResourceId(int bgId) { + // 使用ResourceParser工具的getWidget2xBgResource方法,根据传入的bgId参数获取对应的2x小部件背景资源ID,并返回 + return ResourceParser.WidgetBgResources.getWidget2xBgResource(bgId); + } + + // 重写父类的getWidgetType方法,这个方法用于获取小部件的类型 + @Override + protected int getWidgetType() { + // 返回预定义的2x小部件类型ID,这个ID可能在Notes类中有定义 + return Notes.TYPE_WIDGET_2X; + } +} // NoteWidgetProvider_2x类的定义结束 \ No newline at end of file diff --git a/src/src/net/micode/notes/widget/widget/NoteWidgetProvider_4x.java b/src/src/net/micode/notes/widget/widget/NoteWidgetProvider_4x.java new file mode 100644 index 0000000..99660df --- /dev/null +++ b/src/src/net/micode/notes/widget/widget/NoteWidgetProvider_4x.java @@ -0,0 +1,57 @@ +/* + * 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. + */ + +// 声明一个名为NoteWidgetProvider_4x的公开类,该类继承自NoteWidgetProvider类。这个类看起来是用于提供Android小部件(Widget)的功能。 +package net.micode.notes.widget; + +// 导入必要的Android库,用于处理小部件和应用程序上下文。 +import android.appwidget.AppWidgetManager; +import android.content.Context; + +// 导入项目内部的库,其中包含小部件相关的类和方法。 +import net.micode.notes.R; // 导入项目资源类,该类包含与应用相关的资源(如ID,颜色,尺寸等)。 +import net.micode.notes.data.Notes; // 导入数据模型类,可能包含笔记的数据结构和相关方法。 +import net.micode.notes.tool.ResourceParser; // 导入项目工具类,可能用于解析和应用资源。 + +// NoteWidgetProvider_4x类的定义开始。 +public class NoteWidgetProvider_4x extends NoteWidgetProvider { + // 重写父类的onUpdate方法。这个方法通常在小部件需要更新时被系统调用。 + @Override + public void onUpdate(Context context, AppWidgetManager appWidgetManager, int[] appWidgetIds) { + // 调用父类的update方法,使用相同的参数进行更新操作。 + super.update(context, appWidgetManager, appWidgetIds); + } + + // 重写父类的getLayoutId方法。这个方法用于获取小部件的布局资源ID。 + protected int getLayoutId() { + // 返回预定义的4x小部件布局资源ID。 + return R.layout.widget_4x; + } + + // 重写父类的getBgResourceId方法。这个方法用于获取小部件的背景资源ID。 + @Override + protected int getBgResourceId(int bgId) { + // 使用ResourceParser工具类的getWidget4xBgResource方法,根据传入的bgId参数获取对应的4x小部件背景资源ID,并返回。 + return ResourceParser.WidgetBgResources.getWidget4xBgResource(bgId); + } + + // 重写父类的getWidgetType方法。这个方法用于获取小部件的类型。 + @Override + protected int getWidgetType() { + // 返回预定义的4x小部件类型ID。这个ID可能在Notes类中有定义。 + return Notes.TYPE_WIDGET_4X; + } +} // NoteWidgetProvider_4x类的定义结束