diff --git a/src/main/java/net/micode/notes/data/Contact.java b/src/main/java/net/micode/notes/data/Contact.java index d97ac5d..dee453c 100644 --- a/src/main/java/net/micode/notes/data/Contact.java +++ b/src/main/java/net/micode/notes/data/Contact.java @@ -26,27 +26,34 @@ import android.util.Log; import java.util.HashMap; public class Contact { + // 声明一个名为sContactCache的静态HashMap,用于缓存电话号码对应的联系人名称 private static HashMap sContactCache; private static final String TAG = "Contact"; + // 声明一个名为CALLER_ID_SELECTION的常量字符串,用于构建查询选择器 private static final String CALLER_ID_SELECTION = "PHONE_NUMBERS_EQUAL(" + Phone.NUMBER - + ",?) AND " + Data.MIMETYPE + "='" + Phone.CONTENT_ITEM_TYPE + "'" - + " AND " + Data.RAW_CONTACT_ID + " IN " + + ",?) AND " + Data.MIMETYPE + "='" + Phone.CONTENT_ITEM_TYPE + "'" + + " AND " + Data.RAW_CONTACT_ID + " IN " + "(SELECT raw_contact_id " + " FROM phone_lookup" + " WHERE min_match = '+')"; + // 声明一个名为getContact的静态方法,用于查询与电话号码匹配的联系人名称 public static String getContact(Context context, String phoneNumber) { + // 如果sContactCache为空,则创建一个新的HashMap if(sContactCache == null) { sContactCache = new HashMap(); } + // 如果sContactCache中已经缓存了该电话号码的联系人名称,则直接返回缓存的名称 if(sContactCache.containsKey(phoneNumber)) { return sContactCache.get(phoneNumber); } + // 使用PhoneNumberUtils.toCallerIDMinMatch方法将电话号码转换为最小匹配模式,并使用该模式构建一个查询选择器 String selection = CALLER_ID_SELECTION.replace("+", PhoneNumberUtils.toCallerIDMinMatch(phoneNumber)); + // 使用ContentResolver查询联系人数据,并返回与电话号码匹配的第一个联系人的名称 Cursor cursor = context.getContentResolver().query( Data.CONTENT_URI, new String [] { Phone.DISPLAY_NAME }, @@ -54,6 +61,7 @@ public class Contact { new String[] { phoneNumber }, null); + // 如果找到匹配的联系人,则将其名称缓存到sContactCache中,并返回该名称 if (cursor != null && cursor.moveToFirst()) { try { String name = cursor.getString(0); @@ -66,6 +74,7 @@ public class Contact { cursor.close(); } } else { + // 如果没有找到匹配的联系人,则返回null Log.d(TAG, "No contact matched with number:" + phoneNumber); return null; } diff --git a/src/main/java/net/micode/notes/data/Notes.java b/src/main/java/net/micode/notes/data/Notes.java index f240604..e81a8b6 100644 --- a/src/main/java/net/micode/notes/data/Notes.java +++ b/src/main/java/net/micode/notes/data/Notes.java @@ -18,6 +18,7 @@ package net.micode.notes.data; import android.net.Uri; public class Notes { + // 定义笔记类型的常量 public static final String AUTHORITY = "micode_notes"; public static final String TAG = "Notes"; public static final int TYPE_NOTE = 0; @@ -30,11 +31,13 @@ public class Notes { * {@link Notes#ID_TEMPARAY_FOLDER } is for notes belonging no folder * {@link Notes#ID_CALL_RECORD_FOLDER} is to store call records */ + // 定义系统文件夹的常量 public static final int ID_ROOT_FOLDER = 0; public static final int ID_TEMPARAY_FOLDER = -1; public static final int ID_CALL_RECORD_FOLDER = -2; public static final int ID_TRASH_FOLER = -3; + // 定义 Intent extras 的常量 public static final String INTENT_EXTRA_ALERT_DATE = "net.micode.notes.alert_date"; public static final String INTENT_EXTRA_BACKGROUND_ID = "net.micode.notes.background_color_id"; public static final String INTENT_EXTRA_WIDGET_ID = "net.micode.notes.widget_id"; @@ -42,6 +45,7 @@ public class Notes { public static final String INTENT_EXTRA_FOLDER_ID = "net.micode.notes.folder_id"; public static final String INTENT_EXTRA_CALL_DATE = "net.micode.notes.call_date"; + // 定义 widget 类型的常量 public static final int TYPE_WIDGET_INVALIDE = -1; public static final int TYPE_WIDGET_2X = 0; public static final int TYPE_WIDGET_4X = 1; @@ -54,6 +58,7 @@ public class Notes { /** * Uri to query all notes and folders */ + // 定义查询所有笔记和文件夹的 Uri 常量 public static final Uri CONTENT_NOTE_URI = Uri.parse("content://" + AUTHORITY + "/note"); /** @@ -61,6 +66,7 @@ public class Notes { */ public static final Uri CONTENT_DATA_URI = Uri.parse("content://" + AUTHORITY + "/data"); + // 定义笔记表的列名 public interface NoteColumns { /** * The unique ID for a row @@ -167,6 +173,7 @@ public class Notes { public static final String VERSION = "version"; } + // 定义数据表的列名 public interface DataColumns { /** * The unique ID for a row @@ -241,6 +248,7 @@ public class Notes { public static final String DATA5 = "data5"; } + // 定义文本笔记的内部类 public static final class TextNote implements DataColumns { /** * Mode to indicate the text in check list mode or not @@ -257,6 +265,7 @@ public class Notes { public static final Uri CONTENT_URI = Uri.parse("content://" + AUTHORITY + "/text_note"); } + // 定义电话笔记的内部类 public static final class CallNote implements DataColumns { /** * Call date for this record diff --git a/src/main/java/net/micode/notes/data/NotesDatabaseHelper.java b/src/main/java/net/micode/notes/data/NotesDatabaseHelper.java index ffe5d57..01bc8e6 100644 --- a/src/main/java/net/micode/notes/data/NotesDatabaseHelper.java +++ b/src/main/java/net/micode/notes/data/NotesDatabaseHelper.java @@ -28,10 +28,12 @@ import net.micode.notes.data.Notes.NoteColumns; public class NotesDatabaseHelper extends SQLiteOpenHelper { + // 定义数据库名称和版本号常量 private static final String DB_NAME = "note.db"; private static final int DB_VERSION = 4; + // 定义笔记表和数据表的名称常量 public interface TABLE { public static final String NOTE = "note"; @@ -42,34 +44,36 @@ public class NotesDatabaseHelper extends SQLiteOpenHelper { private static NotesDatabaseHelper mInstance; + // 定义创建笔记表和数据表的 SQL 语句常量 private static final String CREATE_NOTE_TABLE_SQL = "CREATE TABLE " + TABLE.NOTE + "(" + NoteColumns.ID + " INTEGER PRIMARY KEY," + - NoteColumns.PARENT_ID + " INTEGER NOT NULL DEFAULT 0," + - NoteColumns.ALERTED_DATE + " INTEGER NOT NULL DEFAULT 0," + - NoteColumns.BG_COLOR_ID + " INTEGER NOT NULL DEFAULT 0," + - NoteColumns.CREATED_DATE + " INTEGER NOT NULL DEFAULT (strftime('%s','now') * 1000)," + - NoteColumns.HAS_ATTACHMENT + " INTEGER NOT NULL DEFAULT 0," + - NoteColumns.MODIFIED_DATE + " INTEGER NOT NULL DEFAULT (strftime('%s','now') * 1000)," + - NoteColumns.NOTES_COUNT + " INTEGER NOT NULL DEFAULT 0," + - NoteColumns.SNIPPET + " TEXT NOT NULL DEFAULT ''," + - NoteColumns.TYPE + " INTEGER NOT NULL DEFAULT 0," + - NoteColumns.WIDGET_ID + " INTEGER NOT NULL DEFAULT 0," + - NoteColumns.WIDGET_TYPE + " INTEGER NOT NULL DEFAULT -1," + - NoteColumns.SYNC_ID + " INTEGER NOT NULL DEFAULT 0," + - NoteColumns.LOCAL_MODIFIED + " INTEGER NOT NULL DEFAULT 0," + - NoteColumns.ORIGIN_PARENT_ID + " INTEGER NOT NULL DEFAULT 0," + - NoteColumns.GTASK_ID + " TEXT NOT NULL DEFAULT ''," + - NoteColumns.VERSION + " INTEGER NOT NULL DEFAULT 0" + + NoteColumns.PARENT_ID + " INTEGER NOT NULL DEFAULT 0," + // 父笔记的 ID + NoteColumns.ALERTED_DATE + " INTEGER NOT NULL DEFAULT 0," + // 提醒时间 + NoteColumns.BG_COLOR_ID + " INTEGER NOT NULL DEFAULT 0," + // 背景颜色 ID + NoteColumns.CREATED_DATE + " INTEGER NOT NULL DEFAULT (strftime('%s','now') * 1000)," + // 创建时间 + NoteColumns.HAS_ATTACHMENT + " INTEGER NOT NULL DEFAULT 0," + // 是否有附件 + NoteColumns.MODIFIED_DATE + " INTEGER NOT NULL DEFAULT (strftime('%s','now') * 1000)," + // 修改时间 + NoteColumns.NOTES_COUNT + " INTEGER NOT NULL DEFAULT 0," + // 子笔记数量 + NoteColumns.SNIPPET + " TEXT NOT NULL DEFAULT ''," + // 摘要信息 + NoteColumns.TYPE + " INTEGER NOT NULL DEFAULT 0," + // 笔记类型 + NoteColumns.WIDGET_ID + " INTEGER NOT NULL DEFAULT 0," + // 小组件 ID + NoteColumns.WIDGET_TYPE + " INTEGER NOT NULL DEFAULT -1," + // 小组件类型 + NoteColumns.SYNC_ID + " INTEGER NOT NULL DEFAULT 0," + // 同步 ID + NoteColumns.LOCAL_MODIFIED + " INTEGER NOT NULL DEFAULT 0," + // 本地修改 + NoteColumns.ORIGIN_PARENT_ID + " INTEGER NOT NULL DEFAULT 0," + // 原始父笔记的 ID + NoteColumns.GTASK_ID + " TEXT NOT NULL DEFAULT ''," + // Google 任务 ID + NoteColumns.VERSION + " INTEGER NOT NULL DEFAULT 0" + // 版本号 ")"; + // 定义创建数据表的 SQL 语句常量 private static final String CREATE_DATA_TABLE_SQL = "CREATE TABLE " + TABLE.DATA + "(" + DataColumns.ID + " INTEGER PRIMARY KEY," + - DataColumns.MIME_TYPE + " TEXT NOT NULL," + - DataColumns.NOTE_ID + " INTEGER NOT NULL DEFAULT 0," + - NoteColumns.CREATED_DATE + " INTEGER NOT NULL DEFAULT (strftime('%s','now') * 1000)," + - NoteColumns.MODIFIED_DATE + " INTEGER NOT NULL DEFAULT (strftime('%s','now') * 1000)," + + DataColumns.MIME_TYPE + " TEXT NOT NULL," + // MIME 类型 + DataColumns.NOTE_ID + " INTEGER NOT NULL DEFAULT 0," + // 所属笔记的 ID + NoteColumns.CREATED_DATE + " INTEGER NOT NULL DEFAULT (strftime('%s','now') * 1000)," + // 创建时间 + NoteColumns.MODIFIED_DATE + " INTEGER NOT NULL DEFAULT (strftime('%s','now') * 1000)," + // 修改时间 DataColumns.CONTENT + " TEXT NOT NULL DEFAULT ''," + DataColumns.DATA1 + " INTEGER," + DataColumns.DATA2 + " INTEGER," + @@ -78,6 +82,7 @@ public class NotesDatabaseHelper extends SQLiteOpenHelper { DataColumns.DATA5 + " TEXT NOT NULL DEFAULT ''" + ")"; + // 创建数据表中 note_id 列的索引 private static final String CREATE_DATA_NOTE_ID_INDEX_SQL = "CREATE INDEX IF NOT EXISTS note_id_index ON " + TABLE.DATA + "(" + DataColumns.NOTE_ID + ");"; @@ -85,6 +90,7 @@ public class NotesDatabaseHelper extends SQLiteOpenHelper { /** * Increase folder's note count when move note to the folder */ + // 当将笔记移到文件夹中时,增加文件夹的笔记计数 private static final String NOTE_INCREASE_FOLDER_COUNT_ON_UPDATE_TRIGGER = "CREATE TRIGGER increase_folder_count_on_update "+ " AFTER UPDATE OF " + NoteColumns.PARENT_ID + " ON " + TABLE.NOTE + @@ -97,6 +103,7 @@ public class NotesDatabaseHelper extends SQLiteOpenHelper { /** * Decrease folder's note count when move note from folder */ + // 当将笔记从文件夹中移出时,减少文件夹的笔记计数 private static final String NOTE_DECREASE_FOLDER_COUNT_ON_UPDATE_TRIGGER = "CREATE TRIGGER decrease_folder_count_on_update " + " AFTER UPDATE OF " + NoteColumns.PARENT_ID + " ON " + TABLE.NOTE + @@ -110,6 +117,7 @@ public class NotesDatabaseHelper extends SQLiteOpenHelper { /** * Increase folder's note count when insert new note to the folder */ + // 当向文件夹中插入新笔记时,增加文件夹的笔记计数 private static final String NOTE_INCREASE_FOLDER_COUNT_ON_INSERT_TRIGGER = "CREATE TRIGGER increase_folder_count_on_insert " + " AFTER INSERT ON " + TABLE.NOTE + @@ -124,12 +132,12 @@ public class NotesDatabaseHelper extends SQLiteOpenHelper { */ private static final String NOTE_DECREASE_FOLDER_COUNT_ON_DELETE_TRIGGER = "CREATE TRIGGER decrease_folder_count_on_delete " + - " AFTER DELETE ON " + TABLE.NOTE + + " AFTER DELETE ON " + TABLE.NOTE + // 在Note表中删除一条记录后触发 " BEGIN " + - " UPDATE " + TABLE.NOTE + - " SET " + NoteColumns.NOTES_COUNT + "=" + NoteColumns.NOTES_COUNT + "-1" + - " WHERE " + NoteColumns.ID + "=old." + NoteColumns.PARENT_ID + - " AND " + NoteColumns.NOTES_COUNT + ">0;" + + " UPDATE " + TABLE.NOTE + // 更新Note表中的一条记录 + " SET " + NoteColumns.NOTES_COUNT + "=" + NoteColumns.NOTES_COUNT + "-1" + // 将记录的NOTES_COUNT字段减1 + " WHERE " + NoteColumns.ID + "=old." + NoteColumns.PARENT_ID + // 更新父ID为指定ID的记录 + " AND " + NoteColumns.NOTES_COUNT + ">0;" + // 仅在记录的NOTES_COUNT字段大于0时更新 " END"; /** @@ -137,17 +145,20 @@ public class NotesDatabaseHelper extends SQLiteOpenHelper { */ private static final String DATA_UPDATE_NOTE_CONTENT_ON_INSERT_TRIGGER = "CREATE TRIGGER update_note_content_on_insert " + - " AFTER INSERT ON " + TABLE.DATA + - " WHEN new." + DataColumns.MIME_TYPE + "='" + DataConstants.NOTE + "'" + + " AFTER INSERT ON " + TABLE.DATA + // 在Data表中插入一条记录后触发 + " WHEN new." + DataColumns.MIME_TYPE + "='" + DataConstants.NOTE + "'" + // 仅在新记录的MIME_TYPE字段为"note"时触发 " BEGIN" + - " UPDATE " + TABLE.NOTE + - " SET " + NoteColumns.SNIPPET + "=new." + DataColumns.CONTENT + - " WHERE " + NoteColumns.ID + "=new." + DataColumns.NOTE_ID + ";" + + " UPDATE " + TABLE.NOTE + // 更新Note表中的一条记录 + " SET " + NoteColumns.SNIPPET + "=new." + DataColumns.CONTENT + // 将记录的CONTENT字段设置为新记录的CONTENT字段 + " WHERE " + NoteColumns.ID + "=new." + DataColumns.NOTE_ID + ";" + // 更新指定ID的记录 " END"; /** * Update note's content when data with {@link DataConstants#NOTE} type has changed */ + /** + * 当类型为 {@link DataConstants#NOTE} 的数据被更改时,更新笔记的内容 + */ private static final String DATA_UPDATE_NOTE_CONTENT_ON_UPDATE_TRIGGER = "CREATE TRIGGER update_note_content_on_update " + " AFTER UPDATE ON " + TABLE.DATA + @@ -161,6 +172,9 @@ public class NotesDatabaseHelper extends SQLiteOpenHelper { /** * Update note's content when data with {@link DataConstants#NOTE} type has deleted */ + /** + * 当类型为 {@link DataConstants#NOTE} 的数据被删除时,更新笔记的内容 + */ private static final String DATA_UPDATE_NOTE_CONTENT_ON_DELETE_TRIGGER = "CREATE TRIGGER update_note_content_on_delete " + " AFTER delete ON " + TABLE.DATA + @@ -174,6 +188,9 @@ public class NotesDatabaseHelper extends SQLiteOpenHelper { /** * Delete datas belong to note which has been deleted */ + /** + * 当笔记被删除时,删除属于该笔记的数据 + */ private static final String NOTE_DELETE_DATA_ON_DELETE_TRIGGER = "CREATE TRIGGER delete_data_on_delete " + " AFTER DELETE ON " + TABLE.NOTE + @@ -185,6 +202,9 @@ public class NotesDatabaseHelper extends SQLiteOpenHelper { /** * Delete notes belong to folder which has been deleted */ + /** + * 当文件夹被删除时,删除属于该文件夹的笔记 + */ private static final String FOLDER_DELETE_NOTES_ON_DELETE_TRIGGER = "CREATE TRIGGER folder_delete_notes_on_delete " + " AFTER DELETE ON " + TABLE.NOTE + @@ -196,6 +216,9 @@ public class NotesDatabaseHelper extends SQLiteOpenHelper { /** * Move notes belong to folder which has been moved to trash folder */ + /** + * 当文件夹被移动到回收站时,将属于该文件夹的笔记一同移动到回收站 + */ private static final String FOLDER_MOVE_NOTES_ON_TRASH_TRIGGER = "CREATE TRIGGER folder_move_notes_on_trash " + " AFTER UPDATE ON " + TABLE.NOTE + @@ -206,6 +229,7 @@ public class NotesDatabaseHelper extends SQLiteOpenHelper { " WHERE " + NoteColumns.PARENT_ID + "=old." + NoteColumns.ID + ";" + " END"; + // NotesDatabaseHelper的构造函数 public NotesDatabaseHelper(Context context) { super(context, DB_NAME, null, DB_VERSION); } @@ -217,7 +241,9 @@ public class NotesDatabaseHelper extends SQLiteOpenHelper { Log.d(TAG, "note table has been created"); } + // 创建笔记表和数据表 private void reCreateNoteTableTriggers(SQLiteDatabase db) { + // 删除旧的触发器 db.execSQL("DROP TRIGGER IF EXISTS increase_folder_count_on_update"); db.execSQL("DROP TRIGGER IF EXISTS decrease_folder_count_on_update"); db.execSQL("DROP TRIGGER IF EXISTS decrease_folder_count_on_delete"); @@ -226,6 +252,7 @@ public class NotesDatabaseHelper extends SQLiteOpenHelper { db.execSQL("DROP TRIGGER IF EXISTS folder_delete_notes_on_delete"); db.execSQL("DROP TRIGGER IF EXISTS folder_move_notes_on_trash"); + // 创建新的触发器 db.execSQL(NOTE_INCREASE_FOLDER_COUNT_ON_UPDATE_TRIGGER); db.execSQL(NOTE_DECREASE_FOLDER_COUNT_ON_UPDATE_TRIGGER); db.execSQL(NOTE_DECREASE_FOLDER_COUNT_ON_DELETE_TRIGGER); @@ -241,6 +268,7 @@ public class NotesDatabaseHelper extends SQLiteOpenHelper { /** * call record foler for call notes */ + // 创建一个名为 "call record" 的文件夹,用于存放电话记录 values.put(NoteColumns.ID, Notes.ID_CALL_RECORD_FOLDER); values.put(NoteColumns.TYPE, Notes.TYPE_SYSTEM); db.insert(TABLE.NOTE, null, values); @@ -248,6 +276,7 @@ public class NotesDatabaseHelper extends SQLiteOpenHelper { /** * root folder which is default folder */ + // 创建一个名为 "root" 的文件夹,作为默认文件夹 values.clear(); values.put(NoteColumns.ID, Notes.ID_ROOT_FOLDER); values.put(NoteColumns.TYPE, Notes.TYPE_SYSTEM); @@ -256,6 +285,7 @@ public class NotesDatabaseHelper extends SQLiteOpenHelper { /** * temporary folder which is used for moving note */ + // 创建一个名为 "temp" 的文件夹,用于临时存放笔记 values.clear(); values.put(NoteColumns.ID, Notes.ID_TEMPARAY_FOLDER); values.put(NoteColumns.TYPE, Notes.TYPE_SYSTEM); @@ -264,6 +294,7 @@ public class NotesDatabaseHelper extends SQLiteOpenHelper { /** * create trash folder */ + // 创建一个名为 "trash" 的文件夹,用于存放被删除的笔记 values.clear(); values.put(NoteColumns.ID, Notes.ID_TRASH_FOLER); values.put(NoteColumns.TYPE, Notes.TYPE_SYSTEM); @@ -271,22 +302,28 @@ public class NotesDatabaseHelper extends SQLiteOpenHelper { } public void createDataTable(SQLiteDatabase db) { + // 创建名为 "data" 的表格 db.execSQL(CREATE_DATA_TABLE_SQL); + // 重新创建触发器 reCreateDataTableTriggers(db); + // 在 note_id 字段上创建索引 db.execSQL(CREATE_DATA_NOTE_ID_INDEX_SQL); Log.d(TAG, "data table has been created"); } private void reCreateDataTableTriggers(SQLiteDatabase db) { + // 删除旧的触发器 db.execSQL("DROP TRIGGER IF EXISTS update_note_content_on_insert"); db.execSQL("DROP TRIGGER IF EXISTS update_note_content_on_update"); db.execSQL("DROP TRIGGER IF EXISTS update_note_content_on_delete"); + // 创建新的触发器 db.execSQL(DATA_UPDATE_NOTE_CONTENT_ON_INSERT_TRIGGER); db.execSQL(DATA_UPDATE_NOTE_CONTENT_ON_UPDATE_TRIGGER); db.execSQL(DATA_UPDATE_NOTE_CONTENT_ON_DELETE_TRIGGER); } + // 定义创建数据库帮助类的构造函数 static synchronized NotesDatabaseHelper getInstance(Context context) { if (mInstance == null) { mInstance = new NotesDatabaseHelper(context); @@ -305,28 +342,34 @@ public class NotesDatabaseHelper extends SQLiteOpenHelper { boolean reCreateTriggers = false; boolean skipV2 = false; + // 如果当前版本是 1,则执行将版本升级到 2 的操作 if (oldVersion == 1) { upgradeToV2(db); skipV2 = true; // this upgrade including the upgrade from v2 to v3 + // 这个升级包含了从版本 2 升级到版本 3 的操作 oldVersion++; } + // 如果当前版本是 2 且之前没有跳过版本 2 的升级,则执行将版本升级到 3 的操作 if (oldVersion == 2 && !skipV2) { upgradeToV3(db); reCreateTriggers = true; oldVersion++; } + // 如果当前版本是 3,则执行将版本升级到 4 的操作 if (oldVersion == 3) { upgradeToV4(db); oldVersion++; } + // 如果需要重新创建触发器,则执行重新创建笔记表和数据表的触发器的操作 if (reCreateTriggers) { reCreateNoteTableTriggers(db); reCreateDataTableTriggers(db); } + // 如果当前版本与目标版本不一致,则抛出异常 if (oldVersion != newVersion) { throw new IllegalStateException("Upgrade notes database to version " + newVersion + "fails"); @@ -334,21 +377,26 @@ public class NotesDatabaseHelper extends SQLiteOpenHelper { } private void upgradeToV2(SQLiteDatabase db) { + // 删除旧的笔记表和数据表 db.execSQL("DROP TABLE IF EXISTS " + TABLE.NOTE); db.execSQL("DROP TABLE IF EXISTS " + TABLE.DATA); + // 创建新的笔记表和数据表 createNoteTable(db); createDataTable(db); } private void upgradeToV3(SQLiteDatabase db) { // drop unused triggers + // 删除未使用的触发器 db.execSQL("DROP TRIGGER IF EXISTS update_note_modified_date_on_insert"); db.execSQL("DROP TRIGGER IF EXISTS update_note_modified_date_on_delete"); db.execSQL("DROP TRIGGER IF EXISTS update_note_modified_date_on_update"); // add a column for gtask id + // 添加一个名为 gtask_id 的列 db.execSQL("ALTER TABLE " + TABLE.NOTE + " ADD COLUMN " + NoteColumns.GTASK_ID + " TEXT NOT NULL DEFAULT ''"); // add a trash system folder + // 添加一个回收站系统文件夹 ContentValues values = new ContentValues(); values.put(NoteColumns.ID, Notes.ID_TRASH_FOLER); values.put(NoteColumns.TYPE, Notes.TYPE_SYSTEM); @@ -356,6 +404,7 @@ public class NotesDatabaseHelper extends SQLiteOpenHelper { } private void upgradeToV4(SQLiteDatabase db) { + // 添加一个名为 version 的列 db.execSQL("ALTER TABLE " + TABLE.NOTE + " ADD COLUMN " + NoteColumns.VERSION + " INTEGER NOT NULL DEFAULT 0"); } diff --git a/src/main/java/net/micode/notes/data/NotesProvider.java b/src/main/java/net/micode/notes/data/NotesProvider.java index edb0a60..e248215 100644 --- a/src/main/java/net/micode/notes/data/NotesProvider.java +++ b/src/main/java/net/micode/notes/data/NotesProvider.java @@ -36,12 +36,14 @@ import net.micode.notes.data.NotesDatabaseHelper.TABLE; public class NotesProvider extends ContentProvider { +// 定义类并声明 mMatcher 和 mHelper 变量 private static final UriMatcher mMatcher; private NotesDatabaseHelper mHelper; private static final String TAG = "NotesProvider"; + // 定义不同类型的查询的常量 private static final int URI_NOTE = 1; private static final int URI_NOTE_ITEM = 2; private static final int URI_DATA = 3; @@ -50,6 +52,7 @@ public class NotesProvider extends ContentProvider { private static final int URI_SEARCH = 5; private static final int URI_SEARCH_SUGGEST = 6; + // 定义 UriMatcher 用于匹配不同类型的查询 static { mMatcher = new UriMatcher(UriMatcher.NO_MATCH); mMatcher.addURI(Notes.AUTHORITY, "note", URI_NOTE); @@ -65,6 +68,7 @@ public class NotesProvider extends ContentProvider { * x'0A' represents the '\n' character in sqlite. For title and content in the search result, * we will trim '\n' and white space in order to show more information. */ +// 定义搜索结果查询语句 private static final String NOTES_SEARCH_PROJECTION = NoteColumns.ID + "," + NoteColumns.ID + " AS " + SearchManager.SUGGEST_COLUMN_INTENT_EXTRA_DATA + "," + "TRIM(REPLACE(" + NoteColumns.SNIPPET + ", x'0A','')) AS " + SearchManager.SUGGEST_COLUMN_TEXT_1 + "," @@ -79,39 +83,51 @@ public class NotesProvider extends ContentProvider { + " AND " + NoteColumns.PARENT_ID + "<>" + Notes.ID_TRASH_FOLER + " AND " + NoteColumns.TYPE + "=" + Notes.TYPE_NOTE; + // onCreate() 方法会在 ContentProvider 被创建时调用 @Override public boolean onCreate() { + // 初始化数据库操作类 mHelper = NotesDatabaseHelper.getInstance(getContext()); return true; } + // 处理查询请求 @Override public Cursor query(Uri uri, String[] projection, String selection, String[] selectionArgs, String sortOrder) { Cursor c = null; SQLiteDatabase db = mHelper.getReadableDatabase(); String id = null; + // 根据 UriMatcher 匹配到对应的查询类型 switch (mMatcher.match(uri)) { case URI_NOTE: + // 查询所有笔记,使用默认的排序方式 c = db.query(TABLE.NOTE, projection, selection, selectionArgs, null, null, sortOrder); break; case URI_NOTE_ITEM: + // 查询指定 ID 的笔记 id = uri.getPathSegments().get(1); c = db.query(TABLE.NOTE, projection, NoteColumns.ID + "=" + id + parseSelection(selection), selectionArgs, null, null, sortOrder); break; case URI_DATA: + // 查询所有的 data,使用默认的排序方式 c = db.query(TABLE.DATA, projection, selection, selectionArgs, null, null, sortOrder); break; case URI_DATA_ITEM: + // 根据 id 查询单个 data id = uri.getPathSegments().get(1); c = db.query(TABLE.DATA, projection, DataColumns.ID + "=" + id + parseSelection(selection), selectionArgs, null, null, sortOrder); break; + // 查询搜索结果 case URI_SEARCH: + // 执行全文搜索 + // 查询搜索建议结果 case URI_SEARCH_SUGGEST: + // 提供搜索建议 if (sortOrder != null || projection != null) { throw new IllegalArgumentException( "do not specify sortOrder, selection, selectionArgs, or projection" + "with this query"); @@ -142,20 +158,28 @@ public class NotesProvider extends ContentProvider { throw new IllegalArgumentException("Unknown URI " + uri); } if (c != null) { + // 通知 ContentResolver 数据变更 c.setNotificationUri(getContext().getContentResolver(), uri); } return c; } + // 处理插入请求 @Override public Uri insert(Uri uri, ContentValues values) { + // 获取可写数据库实例 + // 根据 UriMatcher 匹配到对应的表,执行插入操作 SQLiteDatabase db = mHelper.getWritableDatabase(); + // 初始化变量 long dataId = 0, noteId = 0, insertedId = 0; + // 根据 URI 匹配不同的操作 switch (mMatcher.match(uri)) { case URI_NOTE: + // 插入笔记 insertedId = noteId = db.insert(TABLE.NOTE, null, values); break; case URI_DATA: + // 插入数据 if (values.containsKey(DataColumns.NOTE_ID)) { noteId = values.getAsLong(DataColumns.NOTE_ID); } else { @@ -164,35 +188,45 @@ public class NotesProvider extends ContentProvider { insertedId = dataId = db.insert(TABLE.DATA, null, values); break; default: + // 如果 URI 不匹配,则抛出异常 throw new IllegalArgumentException("Unknown URI " + uri); } // Notify the note uri + // 通知笔记 URI 已更改 if (noteId > 0) { getContext().getContentResolver().notifyChange( ContentUris.withAppendedId(Notes.CONTENT_NOTE_URI, noteId), null); } // Notify the data uri + // 通知数据 URI 已更改 if (dataId > 0) { getContext().getContentResolver().notifyChange( ContentUris.withAppendedId(Notes.CONTENT_DATA_URI, dataId), null); } + // 返回新插入数据的 URI return ContentUris.withAppendedId(uri, insertedId); } + // 处理删除请求 @Override public int delete(Uri uri, String selection, String[] selectionArgs) { + // 根据 UriMatcher 匹配到对应的表,执行删除操作 + // 初始化变量 int count = 0; String id = null; + // 获取可写数据库实例 SQLiteDatabase db = mHelper.getWritableDatabase(); boolean deleteData = false; switch (mMatcher.match(uri)) { case URI_NOTE: + // 删除所有笔记 selection = "(" + selection + ") AND " + NoteColumns.ID + ">0 "; count = db.delete(TABLE.NOTE, selection, selectionArgs); break; case URI_NOTE_ITEM: + // 删除指定 ID 的笔记 id = uri.getPathSegments().get(1); /** * ID that smaller than 0 is system folder which is not allowed to @@ -206,6 +240,7 @@ public class NotesProvider extends ContentProvider { NoteColumns.ID + "=" + id + parseSelection(selection), selectionArgs); break; case URI_DATA: + // 删除数据 count = db.delete(TABLE.DATA, selection, selectionArgs); deleteData = true; break; @@ -216,35 +251,46 @@ public class NotesProvider extends ContentProvider { deleteData = true; break; default: + // 如果 URI 不匹配,则抛出异常 throw new IllegalArgumentException("Unknown URI " + uri); } + // 通知笔记 URI 已更改 if (count > 0) { if (deleteData) { getContext().getContentResolver().notifyChange(Notes.CONTENT_NOTE_URI, null); } + // 通知数据 URI 已更改 getContext().getContentResolver().notifyChange(uri, null); } + // 返回删除的记录数 return count; } + // 处理更新请求 @Override public int update(Uri uri, ContentValues values, String selection, String[] selectionArgs) { + // 根据 UriMatcher 匹配到对应的表,执行更新操作 + // 初始化变量 int count = 0; String id = null; + // 获取可写数据库实例 SQLiteDatabase db = mHelper.getWritableDatabase(); boolean updateData = false; switch (mMatcher.match(uri)) { case URI_NOTE: + // 更新所有笔记 increaseNoteVersion(-1, selection, selectionArgs); count = db.update(TABLE.NOTE, values, selection, selectionArgs); break; case URI_NOTE_ITEM: + // 更新指定 ID 的笔记 id = uri.getPathSegments().get(1); increaseNoteVersion(Long.valueOf(id), selection, selectionArgs); count = db.update(TABLE.NOTE, values, NoteColumns.ID + "=" + id + parseSelection(selection), selectionArgs); break; case URI_DATA: + // 更新数据 count = db.update(TABLE.DATA, values, selection, selectionArgs); updateData = true; break; @@ -255,36 +301,46 @@ public class NotesProvider extends ContentProvider { updateData = true; break; default: + // 如果 URI 不匹配,则抛出异常 throw new IllegalArgumentException("Unknown URI " + uri); } if (count > 0) { + // 通知笔记 URI 已更改 if (updateData) { getContext().getContentResolver().notifyChange(Notes.CONTENT_NOTE_URI, null); } + // 通知数据 URI 已更改 getContext().getContentResolver().notifyChange(uri, null); } + // 返回更新的记录数 return count; } + // 解析选择条件,如果有的话 private String parseSelection(String selection) { return (!TextUtils.isEmpty(selection) ? " AND (" + selection + ')' : ""); } + // 增加笔记版本号 private void increaseNoteVersion(long id, String selection, String[] selectionArgs) { +// 定义了一个私有方法 increaseNoteVersion,接受三个参数:id,selection 和 selectionArgs。 StringBuilder sql = new StringBuilder(120); sql.append("UPDATE "); sql.append(TABLE.NOTE); sql.append(" SET "); sql.append(NoteColumns.VERSION); sql.append("=" + NoteColumns.VERSION + "+1 "); +// 创建一个 StringBuilder 对象 sql,并向其添加一些字符串来构建 SQL 查询语句,用于更新笔记版本。具体来说,它将 TABLE.NOTE 表中的 NoteColumns.VERSION 字段的值增加 1。 if (id > 0 || !TextUtils.isEmpty(selection)) { sql.append(" WHERE "); } +// 如果 id 大于 0 或者 selection 不为空,则向 sql 中添加字符串 " WHERE "。 if (id > 0) { sql.append(NoteColumns.ID + "=" + String.valueOf(id)); } +// 如果 id 大于 0,则向 sql 中添加一个条件,即 NoteColumns.ID 等于 id。 if (!TextUtils.isEmpty(selection)) { String selectString = id > 0 ? parseSelection(selection) : selection; for (String args : selectionArgs) { @@ -293,9 +349,13 @@ public class NotesProvider extends ContentProvider { sql.append(selectString); } +// 如果 selection 不为空,则向 sql 中添加一个条件,即根据 selection 中的内容来构建查询语句。 +// 如果 id 大于 0,则先使用 parseSelection 方法对 selection 进行解析,否则直接使用 selection。然后使用 selectionArgs 中的参数值来替换 selectString 中的占位符,最后将结果添加到 sql 中。 mHelper.getWritableDatabase().execSQL(sql.toString()); +// 调用 mHelper 对象的 getWritableDatabase 方法获取可写的数据库,并使用 execSQL 方法执行 sql 中的查询语句。 } + // 获取 URI 对应数据的 MIME 类型 @Override public String getType(Uri uri) { // TODO Auto-generated method stub diff --git a/src/main/java/net/micode/notes/model/Note.java b/src/main/java/net/micode/notes/model/Note.java index 6706cf6..26c3c4b 100644 --- a/src/main/java/net/micode/notes/model/Note.java +++ b/src/main/java/net/micode/notes/model/Note.java @@ -35,71 +35,129 @@ import java.util.ArrayList; public class Note { + //声明私有成员变量 private ContentValues mNoteDiffValues; + //用于存储笔记的当前状态和先前状态之间的差异 private NoteData mNoteData; + //用于存储笔记的实际数据 private static final String TAG = "Note"; + //日志标签 /** * Create a new note id for adding a new note to databases */ public static synchronized long getNewNoteId(Context context, long folderId) { // Create a new note in the database + //创建一个系的呢ContentValues对象 ContentValues values = new ContentValues(); long createdTime = System.currentTimeMillis(); + //设置笔记的创建时间、修改时间、类型、本地修改状态和父文件夹ID等属性 values.put(NoteColumns.CREATED_DATE, createdTime); values.put(NoteColumns.MODIFIED_DATE, createdTime); values.put(NoteColumns.TYPE, Notes.TYPE_NOTE); values.put(NoteColumns.LOCAL_MODIFIED, 1); values.put(NoteColumns.PARENT_ID, folderId); + //将新笔记插入到数据库中 Uri uri = context.getContentResolver().insert(Notes.CONTENT_NOTE_URI, values); long noteId = 0; try { + //从返回的Uri对象中提取生成的笔记ID noteId = Long.valueOf(uri.getPathSegments().get(1)); } catch (NumberFormatException e) { + //如果提取笔记ID时发生 Log.e(TAG, "Get note id error :" + e.toString()); noteId = 0; } if (noteId == -1) { + //如果提取笔记ID后发现其值为-1,则抛出异常 throw new IllegalStateException("Wrong note id:" + noteId); } + //返回新笔记的ID return noteId; } public Note() { + //初始化私有成员变量 mNoteDiffValues = new ContentValues(); + //用于存储笔记的当前状态和先前状态之间的差异 mNoteData = new NoteData(); + //用于存储笔记的实际数据 } + /** + *设置笔记的某个属性值 + * @param key 属性名 + * @param value 属性值 + */ public void setNoteValue(String key, String value) { mNoteDiffValues.put(key, value); + //更新笔记的当前状态 mNoteDiffValues.put(NoteColumns.LOCAL_MODIFIED, 1); + //将笔记本的本地修改状态设为1 mNoteDiffValues.put(NoteColumns.MODIFIED_DATE, System.currentTimeMillis()); + //更新笔记的最后修改时间 } + /** + * 设置笔记的文本数据 + * @param key 文本数据的键 + * @param value 文本数据的值 + */ public void setTextData(String key, String value) { mNoteData.setTextData(key, value); + //设置笔记的文本数据 } + /** + * 设置笔记的文本数据ID + * @param id 文本数据ID + */ public void setTextDataId(long id) { mNoteData.setTextDataId(id); + //设置笔记的文本数据ID } + /** + * 获取笔记的文本数据ID + * @return 文本数据ID + */ public long getTextDataId() { return mNoteData.mTextDataId; + //返回笔记的文本数据ID } + /** + * 设置笔记的通话数据ID + * @param id 通话数据ID + */ public void setCallDataId(long id) { mNoteData.setCallDataId(id); + //设置笔记的通话数据ID } + /** + * 设置笔记的通话数据 + * @param key 通话数据的键 + * @param value 通话数据的值 + */ public void setCallData(String key, String value) { mNoteData.setCallData(key, value); + //设置笔记的通话数据 } - + /** + * 判断笔记是否被本地修改过 + * @return 若笔记被本地修改过,则返回true;否则返回false + */ public boolean isLocalModified() { return mNoteDiffValues.size() > 0 || mNoteData.isLocalModified(); + // 判断笔记的当前状态和实际数据是否被本地修改过 } - + /** + * 将本地修改的笔记同步到云端 + * @param context 上下文对象 + * @param noteId 笔记的ID + * @return 若同步成功,则返回true;否则返回false + */ public boolean syncNote(Context context, long noteId) { if (noteId <= 0) { throw new IllegalArgumentException("Wrong note id:" + noteId); @@ -114,33 +172,45 @@ public class Note { * {@link NoteColumns#MODIFIED_DATE}. For data safety, though update note fails, we also update the * note data info */ + // 更新数据库中的笔记 if (context.getContentResolver().update( ContentUris.withAppendedId(Notes.CONTENT_NOTE_URI, noteId), mNoteDiffValues, null, null) == 0) { Log.e(TAG, "Update note error, should not happen"); + // 如果更新笔记失败,则记录错误日志但不返回 // Do not return, fall through } mNoteDiffValues.clear(); + // 清空笔记的当前状态 + // 将本地修改的笔记数据同步到云端 if (mNoteData.isLocalModified() && (mNoteData.pushIntoContentResolver(context, noteId) == null)) { return false; + // 如果同步笔记数据失败,则返回false } return true; + // 返回true表示同步成功 } + /** + * NoteData类用于存储笔记的数据,在将笔记保存到ContentProvider之前,将笔记数据保存在这个类中, + * 并在需要时通过pushIntoContentResolver方法将数据保存到ContentProvider中。 + */ private class NoteData { private long mTextDataId; - + // 用于存储文本笔记数据在ContentProvider中的ID private ContentValues mTextDataValues; - + // 用于存储文本笔记数据的ContentValues对象 private long mCallDataId; - + // 用于存储通话笔记数据在ContentProvider中的ID private ContentValues mCallDataValues; - + // 用于存储通话笔记数据的ContentValues对象 private static final String TAG = "NoteData"; + // 用于调试的标签 + // NoteData类的构造函数,初始化成员变量 public NoteData() { mTextDataValues = new ContentValues(); mCallDataValues = new ContentValues(); @@ -148,10 +218,19 @@ public class Note { mCallDataId = 0; } + /** + * 判断是否有本地修改 + * @return 如果有本地修改,则返回true,否则返回false + */ boolean isLocalModified() { return mTextDataValues.size() > 0 || mCallDataValues.size() > 0; } + /** + * 设置文本笔记数据在ContentProvider中的ID + * @param id 文本笔记数据在ContentProvider中的ID + * @throws IllegalArgumentException 如果id小于等于0,则抛出该异常 + */ void setTextDataId(long id) { if(id <= 0) { throw new IllegalArgumentException("Text data id should larger than 0"); @@ -159,6 +238,11 @@ public class Note { mTextDataId = id; } + /** + * 设置通话笔记数据在ContentProvider中的ID + * @param id 通话笔记数据在ContentProvider中的ID + * @throws IllegalArgumentException 如果id小于等于0,则抛出该异常 + */ void setCallDataId(long id) { if (id <= 0) { throw new IllegalArgumentException("Call data id should larger than 0"); @@ -166,21 +250,38 @@ public class Note { mCallDataId = id; } + /** + * 设置通话笔记数据 + * @param key 通话笔记数据的键 + * @param value 通话笔记数据的值 + */ void setCallData(String key, String value) { mCallDataValues.put(key, value); mNoteDiffValues.put(NoteColumns.LOCAL_MODIFIED, 1); mNoteDiffValues.put(NoteColumns.MODIFIED_DATE, System.currentTimeMillis()); } + /** + * 设置文本笔记数据 + * @param key 文本笔记数据的键 + * @param value 文本笔记数据的值 + */ void setTextData(String key, String value) { mTextDataValues.put(key, value); mNoteDiffValues.put(NoteColumns.LOCAL_MODIFIED, 1); mNoteDiffValues.put(NoteColumns.MODIFIED_DATE, System.currentTimeMillis()); } + /** + * 将数据保存到ContentProvider中 + * @param context 上下文对象 + * @param noteId 笔记的ID + * @return 如果保存成功,则返回保存后的笔记的Uri对象,否则返回null + * @throws IllegalArgumentException 如果noteId小于等于0,则抛出该异常 + */ Uri pushIntoContentResolver(Context context, long noteId) { /** - * Check for safety + * 检查参数的合法性 */ if (noteId <= 0) { throw new IllegalArgumentException("Wrong note id:" + noteId); @@ -188,11 +289,15 @@ public class Note { ArrayList operationList = new ArrayList(); ContentProviderOperation.Builder builder = null; + /** + * 如果有文本笔记数据,则将文本笔记数据保存到ContentProvider中 + */ if(mTextDataValues.size() > 0) { mTextDataValues.put(DataColumns.NOTE_ID, noteId); if (mTextDataId == 0) { mTextDataValues.put(DataColumns.MIME_TYPE, TextNote.CONTENT_ITEM_TYPE); + // 将文本笔记数据插入到 ContentProvider 中 Uri uri = context.getContentResolver().insert(Notes.CONTENT_DATA_URI, mTextDataValues); try { @@ -203,6 +308,7 @@ public class Note { return null; } } else { + // 更新已有的文本笔记数据 builder = ContentProviderOperation.newUpdate(ContentUris.withAppendedId( Notes.CONTENT_DATA_URI, mTextDataId)); builder.withValues(mTextDataValues); @@ -211,10 +317,12 @@ public class Note { mTextDataValues.clear(); } + // 如果有通话笔记数据,则将其保存到 ContentProvider 中。 if(mCallDataValues.size() > 0) { mCallDataValues.put(DataColumns.NOTE_ID, noteId); if (mCallDataId == 0) { mCallDataValues.put(DataColumns.MIME_TYPE, CallNote.CONTENT_ITEM_TYPE); + // 将通话笔记数据插入到 ContentProvider 中 Uri uri = context.getContentResolver().insert(Notes.CONTENT_DATA_URI, mCallDataValues); try { @@ -225,6 +333,7 @@ public class Note { return null; } } else { + // 更新已有的通话笔记数据 builder = ContentProviderOperation.newUpdate(ContentUris.withAppendedId( Notes.CONTENT_DATA_URI, mCallDataId)); builder.withValues(mCallDataValues); @@ -232,11 +341,13 @@ public class Note { } mCallDataValues.clear(); } + // 如果有待执行的操作,则使用 applyBatch() 方法将其应用于 ContentProvider。 if (operationList.size() > 0) { try { ContentProviderResult[] results = context.getContentResolver().applyBatch( Notes.AUTHORITY, operationList); + // 返回保存的笔记的 ContentUri return (results == null || results.length == 0 || results[0] == null) ? null : ContentUris.withAppendedId(Notes.CONTENT_NOTE_URI, noteId); } catch (RemoteException e) { diff --git a/src/main/java/net/micode/notes/ui/AlarmAlertActivity.java b/src/main/java/net/micode/notes/ui/AlarmAlertActivity.java index 85723be..6dbfd93 100644 --- a/src/main/java/net/micode/notes/ui/AlarmAlertActivity.java +++ b/src/main/java/net/micode/notes/ui/AlarmAlertActivity.java @@ -1,20 +1,20 @@ /* - * Copyright (c) 2010-2011, The MiCode Open Source Community (www.micode.net) - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package net.micode.notes.ui; + * 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; @@ -39,26 +39,33 @@ import net.micode.notes.tool.DataUtils; import java.io.IOException; - +//主要用于提醒用户处理备忘录、日程等事务,以便用户可以更好地管理自己的时间和事务 public class AlarmAlertActivity extends Activity implements OnClickListener, OnDismissListener { private long mNoteId; + //对应文本在数据库中存储的id号 private String mSnippet; + //闹钟提示时出现的文本片段 private static final int SNIPPET_PREW_MAX_LEN = 60; + //限制提示文本片段的最大长度 MediaPlayer mPlayer; @Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); + //Bundle类型的数据与Map类型的数据类似,均以Key-Value的方式进行数据存储 + //设置该活动的特性(无标题栏),并获取窗口对象 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(); @@ -66,9 +73,13 @@ public class AlarmAlertActivity extends Activity implements OnClickListener, OnD try { mNoteId = Long.valueOf(intent.getData().getPathSegments().get(1)); mSnippet = DataUtils.getSnippetById(this.getContentResolver(), mNoteId); + //根据ID从数据库中获取标签的内容 + //getContentResolver()实现数据共享+实例存储 mSnippet = mSnippet.length() > SNIPPET_PREW_MAX_LEN ? mSnippet.substring(0, SNIPPET_PREW_MAX_LEN) + getResources().getString(R.string.notelist_string_info) : mSnippet; + //判断获取的标签片段是否达到符合长度 + //如果代码异常,则进行catch内的异常处理 } catch (IllegalArgumentException e) { e.printStackTrace(); return; @@ -76,21 +87,27 @@ public class AlarmAlertActivity extends Activity implements OnClickListener, OnD 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); @@ -102,11 +119,15 @@ public class AlarmAlertActivity extends Activity implements OnClickListener, OnD 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(); @@ -121,38 +142,52 @@ public class AlarmAlertActivity extends Activity implements OnClickListener, OnD private void showActionDialog() { AlertDialog.Builder dialog = new AlertDialog.Builder(this); + //新建dialog dialog.setTitle(R.string.app_name); + //设置标题 dialog.setMessage(mSnippet); + //设置对话框的内容 dialog.setPositiveButton(R.string.notealert_ok, this); + //为对话框添加“Yes”按钮 if (isScreenOn()) { dialog.setNegativeButton(R.string.notealert_enter, this); - } + }//为对话框添加“No"按钮 dialog.show().setOnDismissListener(this); } public void onClick(DialogInterface dialog, int which) { switch (which) { + //用which选择click后下一步的操作 case DialogInterface.BUTTON_NEGATIVE: + //取消操作 Intent intent = new Intent(this, NoteEditActivity.class); + //实现Intent类和NoteEditActivity类之间的数据传输 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(); + //释放MediaPlayer对象 mPlayer = null; } } -} +} \ No newline at end of file diff --git a/src/main/java/net/micode/notes/ui/AlarmInitReceiver.java b/src/main/java/net/micode/notes/ui/AlarmInitReceiver.java index f221202..c1e2d29 100644 --- a/src/main/java/net/micode/notes/ui/AlarmInitReceiver.java +++ b/src/main/java/net/micode/notes/ui/AlarmInitReceiver.java @@ -31,22 +31,23 @@ import net.micode.notes.data.Notes.NoteColumns; public class AlarmInitReceiver extends BroadcastReceiver { private static final String [] PROJECTION = new String [] { - NoteColumns.ID, - NoteColumns.ALERTED_DATE + NoteColumns.ID, + NoteColumns.ALERTED_DATE }; - + //从数据库中调用标签ID和闹钟日期 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(); + long currentDate = System.currentTimeMillis(); //产生一个当前时间的毫秒 Cursor c = context.getContentResolver().query(Notes.CONTENT_NOTE_URI, PROJECTION, NoteColumns.ALERTED_DATE + ">? AND " + NoteColumns.TYPE + "=" + Notes.TYPE_NOTE, new String[] { String.valueOf(currentDate) }, null); - + //将long变量currentDate转化为字符串 + //Cursor通过查找数据库中的标签内容,从而找到和当前系统时间相等的标签 if (c != null) { if (c.moveToFirst()) { do { @@ -61,5 +62,8 @@ public class AlarmInitReceiver extends BroadcastReceiver { } c.close(); } + //闹钟的启动需要以下步骤 + //新建Intent、PendingIntent以及AlarmManager等 + //这里就是根据数据库中的闹钟时间创建一个闹钟 } -} +} \ No newline at end of file diff --git a/src/main/java/net/micode/notes/ui/AlarmReceiver.java b/src/main/java/net/micode/notes/ui/AlarmReceiver.java index 54e503b..c1e2d29 100644 --- a/src/main/java/net/micode/notes/ui/AlarmReceiver.java +++ b/src/main/java/net/micode/notes/ui/AlarmReceiver.java @@ -16,15 +16,54 @@ package net.micode.notes.ui; +import android.app.AlarmManager; +import android.app.PendingIntent; import android.content.BroadcastReceiver; +import android.content.ContentUris; import android.content.Context; import android.content.Intent; +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 + }; + //从数据库中调用标签ID和闹钟日期 + private static final int COLUMN_ID = 0; + private static final int COLUMN_ALERTED_DATE = 1; -public class AlarmReceiver extends BroadcastReceiver { @Override public void onReceive(Context context, Intent intent) { - intent.setClass(context, AlarmAlertActivity.class); - intent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK); - context.startActivity(intent); + long currentDate = System.currentTimeMillis(); //产生一个当前时间的毫秒 + Cursor c = context.getContentResolver().query(Notes.CONTENT_NOTE_URI, + PROJECTION, + NoteColumns.ALERTED_DATE + ">? AND " + NoteColumns.TYPE + "=" + Notes.TYPE_NOTE, + new String[] { String.valueOf(currentDate) }, + null); + //将long变量currentDate转化为字符串 + //Cursor通过查找数据库中的标签内容,从而找到和当前系统时间相等的标签 + if (c != null) { + if (c.moveToFirst()) { + do { + long alertDate = c.getLong(COLUMN_ALERTED_DATE); + Intent sender = new Intent(context, AlarmReceiver.class); + sender.setData(ContentUris.withAppendedId(Notes.CONTENT_NOTE_URI, c.getLong(COLUMN_ID))); + PendingIntent pendingIntent = PendingIntent.getBroadcast(context, 0, sender, 0); + AlarmManager alermManager = (AlarmManager) context + .getSystemService(Context.ALARM_SERVICE); + alermManager.set(AlarmManager.RTC_WAKEUP, alertDate, pendingIntent); + } while (c.moveToNext()); + } + c.close(); + } + //闹钟的启动需要以下步骤 + //新建Intent、PendingIntent以及AlarmManager等 + //这里就是根据数据库中的闹钟时间创建一个闹钟 } -} +} \ No newline at end of file diff --git a/src/main/java/net/micode/notes/ui/DateTimePicker.java b/src/main/java/net/micode/notes/ui/DateTimePicker.java index 496b0cd..ca6a4dc 100644 --- a/src/main/java/net/micode/notes/ui/DateTimePicker.java +++ b/src/main/java/net/micode/notes/ui/DateTimePicker.java @@ -29,7 +29,7 @@ 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; @@ -45,13 +45,15 @@ public class DateTimePicker extends FrameLayout { private static final int MINUT_SPINNER_MAX_VAL = 59; private static final int AMPM_SPINNER_MIN_VAL = 0; private static final int AMPM_SPINNER_MAX_VAL = 1; - + //对一些构建进行初始化 private final NumberPicker mDateSpinner; private final NumberPicker mHourSpinner; private final NumberPicker mMinuteSpinner; private final NumberPicker mAmPmSpinner; + //定义数值选择器变量 + //均为设置闹钟时涉及到的变量(日期、小时、分钟、上午、下午) private Calendar mDate; - + //定义Calendar类型的变量mDate,用于操作时间 private String[] mDateDisplayValues = new String[DAYS_IN_ALL_WEEK]; private boolean mIsAm; @@ -67,44 +69,50 @@ public class DateTimePicker extends FrameLayout { 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); + mDate.add(Calendar.DAY_OF_YEAR, newVal - oldVal); //传日期值 updateDateControl(); onDateTimeChanged(); } - }; + };//监听日期,updateDateControl()是同步日期数据 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; + //对十二小时制,晚上11点和12点更替对日期的更改 } else if (mIsAm && oldVal == HOURS_IN_HALF_DAY && newVal == HOURS_IN_HALF_DAY - 1) { cal.setTimeInMillis(mDate.getTimeInMillis()); cal.add(Calendar.DAY_OF_YEAR, -1); isDateChanged = true; } + //对十二小时制,凌晨11点和12点更替对日期的更改 if (oldVal == HOURS_IN_HALF_DAY - 1 && newVal == HOURS_IN_HALF_DAY || oldVal == HOURS_IN_HALF_DAY && newVal == HOURS_IN_HALF_DAY - 1) { mIsAm = !mIsAm; updateAmPmControl(); - } + }//对十二小时制,中午11点和12点更替对AM和PM的更改 } else { if (oldVal == HOURS_IN_ALL_DAY - 1 && newVal == 0) { cal.setTimeInMillis(mDate.getTimeInMillis()); cal.add(Calendar.DAY_OF_YEAR, 1); isDateChanged = true; + //这里是对于24小时制时,晚上11点和12点交替时对日期的更改 } else if (oldVal == 0 && newVal == HOURS_IN_ALL_DAY - 1) { cal.setTimeInMillis(mDate.getTimeInMillis()); cal.add(Calendar.DAY_OF_YEAR, -1); isDateChanged = true; } - } + }//这里是对于12小时制时,凌晨11点和12点交替时对日期的更改 int newHour = mHourSpinner.getValue() % HOURS_IN_HALF_DAY + (mIsAm ? 0 : HOURS_IN_HALF_DAY); + //通过数字选择器对newHour赋值 mDate.set(Calendar.HOUR_OF_DAY, newHour); onDateTimeChanged(); if (isDateChanged) { @@ -117,15 +125,18 @@ public class DateTimePicker extends FrameLayout { private NumberPicker.OnValueChangeListener mOnMinuteChangedListener = new NumberPicker.OnValueChangeListener() { @Override + //对分钟(Minute)改变的监听 public void onValueChange(NumberPicker picker, int oldVal, int newVal) { int minValue = mMinuteSpinner.getMinValue(); int maxValue = mMinuteSpinner.getMaxValue(); - int offset = 0; + int offset = 0;//设置offset,作为小时改变的一个记录数据 if (oldVal == maxValue && newVal == minValue) { offset += 1; } else if (oldVal == minValue && newVal == maxValue) { offset -= 1; } + //如果原值为59,新值为0,则offset加1 + //如果原值为0,新值为59,则offset减1 if (offset != 0) { mDate.add(Calendar.HOUR_OF_DAY, offset); mHourSpinner.setValue(getCurrentHour()); @@ -145,6 +156,7 @@ public class DateTimePicker extends FrameLayout { }; private NumberPicker.OnValueChangeListener mOnAmPmChangedListener = new NumberPicker.OnValueChangeListener() { + //对AM和PM的监听 @Override public void onValueChange(NumberPicker picker, int oldVal, int newVal) { mIsAm = !mIsAm; @@ -160,24 +172,26 @@ public class DateTimePicker extends FrameLayout { public interface OnDateTimeChangedListener { void onDateTimeChanged(DateTimePicker view, int year, int month, - int dayOfMonth, int hourOfDay, int minute); + 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); - + //inflate找出不属于当前组件的内容 + //再用findViewById()找到它上面的其它组件 mDateSpinner = (NumberPicker) findViewById(R.id.date); mDateSpinner.setMinValue(DATE_SPINNER_MIN_VAL); mDateSpinner.setMaxValue(DATE_SPINNER_MAX_VAL); @@ -226,7 +240,7 @@ public class DateTimePicker extends FrameLayout { mAmPmSpinner.setEnabled(enabled); mIsEnabled = enabled; } - + //下面的各函数主要是对上面代码引用到的各函数功能的实现 @Override public boolean isEnabled() { return mIsEnabled; @@ -240,7 +254,7 @@ public class DateTimePicker extends FrameLayout { public long getCurrentDateInTimeMillis() { return mDate.getTimeInMillis(); } - + //得到当前的秒数 /** * Set the current date * @@ -251,7 +265,7 @@ public class DateTimePicker extends FrameLayout { 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 @@ -263,19 +277,20 @@ public class DateTimePicker extends FrameLayout { * @param minute The current minute */ public void setCurrentDate(int year, int month, - int dayOfMonth, int hourOfDay, int minute) { + int dayOfMonth, int hourOfDay, int minute) { setCurrentYear(year); setCurrentMonth(month); setCurrentDay(dayOfMonth); setCurrentHour(hourOfDay); setCurrentMinute(minute); - } + }//实现函数功能——设置当前的时间,参数是具体的变量 /** * Get current year * * @return The current year */ + //下面是得到year、month、day等值 public int getCurrentYear() { return mDate.get(Calendar.YEAR); } @@ -447,7 +462,7 @@ public class DateTimePicker extends FrameLayout { mDateSpinner.setValue(DAYS_IN_ALL_WEEK / 2); mDateSpinner.invalidate(); } - + //求出星期几的算法 private void updateAmPmControl() { if (mIs24HourView) { mAmPmSpinner.setVisibility(View.GONE); @@ -457,7 +472,7 @@ public class DateTimePicker extends FrameLayout { mAmPmSpinner.setVisibility(View.VISIBLE); } } - + //对于上下午操作的算法 private void updateHourControl() { if (mIs24HourView) { mHourSpinner.setMinValue(HOUR_SPINNER_MIN_VAL_24_HOUR_VIEW); @@ -467,7 +482,7 @@ public class DateTimePicker extends FrameLayout { 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 @@ -482,4 +497,4 @@ public class DateTimePicker extends FrameLayout { getCurrentMonth(), getCurrentDay(), getCurrentHourOfDay(), getCurrentMinute()); } } -} +} \ No newline at end of file diff --git a/src/main/java/net/micode/notes/ui/DateTimePickerDialog.java b/src/main/java/net/micode/notes/ui/DateTimePickerDialog.java index 2c47ba4..f415e5f 100644 --- a/src/main/java/net/micode/notes/ui/DateTimePickerDialog.java +++ b/src/main/java/net/micode/notes/ui/DateTimePickerDialog.java @@ -35,32 +35,41 @@ public class DateTimePickerDialog extends AlertDialog implements OnClickListener private boolean mIs24HourView; private OnDateTimeSetListener mOnDateTimeSetListener; private DateTimePicker mDateTimePicker; - + //DateTimePicker控件,控件一般用于让用户可以从日期列表中选择单个值。 + //运行时,单击控件边上的下拉箭头,会显示为两个部分:一个下拉列表,一个用于选择日期的 public interface OnDateTimeSetListener { void OnDateTimeSet(AlertDialog dialog, long date); } public DateTimePickerDialog(Context context, long date) { + //对该界面对话框的实例化 super(context); + //对数据库的操作 mDateTimePicker = new DateTimePicker(context); setView(mDateTimePicker); + //添加子视图 mDateTimePicker.setOnDateTimeChangedListener(new OnDateTimeChangedListener() { public void onDateTimeChanged(DateTimePicker view, int year, int month, - int dayOfMonth, int hourOfDay, int minute) { + int dayOfMonth, int hourOfDay, int minute) { mDate.set(Calendar.YEAR, year); mDate.set(Calendar.MONTH, month); mDate.set(Calendar.DAY_OF_MONTH, dayOfMonth); mDate.set(Calendar.HOUR_OF_DAY, hourOfDay); mDate.set(Calendar.MINUTE, minute); + //将视图中的各选项设置为系统当前时间 updateTitle(mDate.getTimeInMillis()); } }); mDate.setTimeInMillis(date); + //得到系统时间 mDate.set(Calendar.SECOND, 0); + //秒数置零 mDateTimePicker.setCurrentDate(mDate.getTimeInMillis()); setButton(context.getString(R.string.datetime_dialog_ok), this); setButton2(context.getString(R.string.datetime_dialog_cancel), (OnClickListener)null); + //设置两个按钮 set24HourView(DateFormat.is24HourFormat(this.getContext())); + //时间标准化打印 updateTitle(mDate.getTimeInMillis()); } @@ -70,21 +79,22 @@ public class DateTimePickerDialog extends AlertDialog implements OnClickListener public void setOnDateTimeSetListener(OnDateTimeSetListener callBack) { mOnDateTimeSetListener = callBack; - } + }//实例化时间日期滚动操作 private void updateTitle(long date) { int flag = - DateUtils.FORMAT_SHOW_YEAR | - DateUtils.FORMAT_SHOW_DATE | - DateUtils.FORMAT_SHOW_TIME; + DateUtils.FORMAT_SHOW_YEAR | + DateUtils.FORMAT_SHOW_DATE | + DateUtils.FORMAT_SHOW_TIME; flag |= mIs24HourView ? DateUtils.FORMAT_24HOUR : DateUtils.FORMAT_24HOUR; setTitle(DateUtils.formatDateTime(this.getContext(), date, flag)); - } + }//按照上下午显示时间 public void onClick(DialogInterface arg0, int arg1) { if (mOnDateTimeSetListener != null) { mOnDateTimeSetListener.OnDateTimeSet(this, mDate.getTimeInMillis()); } - } + }//arg0是接收到点击事件的对话框 + //arg1是该对话框上的按钮 } \ No newline at end of file diff --git a/src/main/java/net/micode/notes/ui/DropdownMenu.java b/src/main/java/net/micode/notes/ui/DropdownMenu.java index 613dc74..1d2c635 100644 --- a/src/main/java/net/micode/notes/ui/DropdownMenu.java +++ b/src/main/java/net/micode/notes/ui/DropdownMenu.java @@ -30,14 +30,18 @@ import net.micode.notes.R; public class DropdownMenu { private Button mButton; private PopupMenu mPopupMenu; + //声明一个下拉菜单 private Menu mMenu; public DropdownMenu(Context context, Button button, int menuId) { mButton = button; mButton.setBackgroundResource(R.drawable.dropdown_icon); + //视图背景设置 mPopupMenu = new PopupMenu(context, mButton); mMenu = mPopupMenu.getMenu(); mPopupMenu.getMenuInflater().inflate(menuId, mMenu); + //MenuInflater是用来实例化Menu目录下的Menu布局文件 + //根据ID来确认menu的内容选项 mButton.setOnClickListener(new OnClickListener() { public void onClick(View v) { mPopupMenu.show(); @@ -48,14 +52,15 @@ public class DropdownMenu { public void setOnDropdownMenuItemClickListener(OnMenuItemClickListener listener) { if (mPopupMenu != null) { mPopupMenu.setOnMenuItemClickListener(listener); - } + }//设置菜单的监听 } public MenuItem findItem(int id) { return mMenu.findItem(id); } - + //对于菜单选项的初始化,根据索引搜索菜单需要的选项 public void setTitle(CharSequence title) { mButton.setText(title); } -} + //布局文件,设置标题 +} \ No newline at end of file diff --git a/src/main/java/net/micode/notes/widget/NoteWidgetProvider.java b/src/main/java/net/micode/notes/widget/NoteWidgetProvider.java index ec6f819..8aa5ccb 100644 --- a/src/main/java/net/micode/notes/widget/NoteWidgetProvider.java +++ b/src/main/java/net/micode/notes/widget/NoteWidgetProvider.java @@ -33,20 +33,24 @@ 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) { + // 在数据库中更新 widget ID 为无效值 ContentValues values = new ContentValues(); values.put(NoteColumns.WIDGET_ID, AppWidgetManager.INVALID_APPWIDGET_ID); for (int i = 0; i < appWidgetIds.length; i++) { @@ -57,6 +61,7 @@ public abstract class NoteWidgetProvider extends AppWidgetProvider { } } + // 从数据库查询给定 widget ID 对应的笔记信息 private Cursor getNoteWidgetInfo(Context context, int widgetId) { return context.getContentResolver().query(Notes.CONTENT_NOTE_URI, PROJECTION, @@ -65,21 +70,27 @@ public abstract class NoteWidgetProvider extends AppWidgetProvider { null); } + // 更新所有的 note widget protected void update(Context context, AppWidgetManager appWidgetManager, int[] appWidgetIds) { update(context, appWidgetManager, appWidgetIds, false); } + // 更新所有的 note widget + // privacyMode 为 true 时,更新为隐私模式,不显示具体信息 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 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) { @@ -87,19 +98,24 @@ public abstract class NoteWidgetProvider extends AppWidgetProvider { c.close(); return; } + // 更新内容摘要和背景颜色 snippet = c.getString(COLUMN_SNIPPET); bgId = c.getInt(COLUMN_BG_COLOR_ID); + // 设置 intent 的参数 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); } + // 关闭 cursor if (c != null) { c.close(); } + // 创建 RemoteViews 对象,更新 UI RemoteViews rv = new RemoteViews(context.getPackageName(), getLayoutId()); rv.setImageViewResource(R.id.widget_bg_image, getBgResourceId(bgId)); intent.putExtra(Notes.INTENT_EXTRA_BACKGROUND_ID, bgId); @@ -108,25 +124,31 @@ public abstract class NoteWidgetProvider extends AppWidgetProvider { */ 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); + // 更新 widget appWidgetManager.updateAppWidget(appWidgetIds[i], rv); } } } + // 获取 widget 的类型 protected abstract int getBgResourceId(int bgId); + // 获取对应的布局 ID protected abstract int getLayoutId(); + // 获取对应的背景资源 ID protected abstract int getWidgetType(); } diff --git a/src/main/java/net/micode/notes/widget/NoteWidgetProvider_2x.java b/src/main/java/net/micode/notes/widget/NoteWidgetProvider_2x.java index adcb2f7..0d00581 100644 --- a/src/main/java/net/micode/notes/widget/NoteWidgetProvider_2x.java +++ b/src/main/java/net/micode/notes/widget/NoteWidgetProvider_2x.java @@ -25,23 +25,34 @@ import net.micode.notes.tool.ResourceParser; public class NoteWidgetProvider_2x extends NoteWidgetProvider { + // 声明一个名为NoteWidgetProvider_2x的Java类,它继承自NoteWidgetProvider类 + // NoteWidgetProvider_2x类将重写NoteWidgetProvider类的一些方法,以自定义部件的行为 + @Override public void onUpdate(Context context, AppWidgetManager appWidgetManager, int[] appWidgetIds) { + // 覆盖父类的onUpdate方法,当系统需要更新小部件时将调用该方法 + // 在这个实现中,它调用了父类的update方法,该方法实际上执行了更新小部件的工作 super.update(context, appWidgetManager, appWidgetIds); } @Override protected int getLayoutId() { + // 覆盖父类的getLayoutId方法,返回应该用于显示小部件的布局文件的资源ID + // 在这种情况下,它返回R.layout.widget_2x,这是一个占据两个单元格的主屏幕网格的小部件的布局文件 return R.layout.widget_2x; } @Override protected int getBgResourceId(int bgId) { + // 覆盖父类的getBgResourceId方法,根据背景ID返回应该用于小部件的背景图像的资源ID + // 它在名为ResourceParser.WidgetBgResources的帮助类中查找适当的资源ID,该类包含了2x小部件背景ID到资源ID的映射 return ResourceParser.WidgetBgResources.getWidget2xBgResource(bgId); } @Override protected int getWidgetType() { + // 覆盖父类的getWidgetType方法,返回一个整数常量,用于标识小部件的类型 + // 在这种情况下,它返回Notes.TYPE_WIDGET_2X,这可能表示这是一个用于显示笔记的小部件,并且它是2x大小的 return Notes.TYPE_WIDGET_2X; } } diff --git a/src/main/java/net/micode/notes/widget/NoteWidgetProvider_4x.java b/src/main/java/net/micode/notes/widget/NoteWidgetProvider_4x.java index c12a02e..ce8f1b4 100644 --- a/src/main/java/net/micode/notes/widget/NoteWidgetProvider_4x.java +++ b/src/main/java/net/micode/notes/widget/NoteWidgetProvider_4x.java @@ -25,22 +25,33 @@ import net.micode.notes.tool.ResourceParser; public class NoteWidgetProvider_4x extends NoteWidgetProvider { + // 声明一个名为NoteWidgetProvider_4x的Java类,它继承自NoteWidgetProvider类 + // NoteWidgetProvider_4x类将重写NoteWidgetProvider类的一些方法,以自定义部件的行为 + @Override public void onUpdate(Context context, AppWidgetManager appWidgetManager, int[] appWidgetIds) { + // 覆盖父类的onUpdate方法,当系统需要更新小部件时将调用该方法 + // 在这个实现中,它调用了父类的update方法,该方法实际上执行了更新小部件的工作 super.update(context, appWidgetManager, appWidgetIds); } protected int getLayoutId() { + // 覆盖父类的getLayoutId方法,返回应该用于显示小部件的布局文件的资源ID + // 在这种情况下,它返回R.layout.widget_4x,这是一个占据四个单元格的主屏幕网格的小部件的布局文件 return R.layout.widget_4x; } @Override protected int getBgResourceId(int bgId) { + // 覆盖父类的getBgResourceId方法,根据背景ID返回应该用于小部件的背景图像的资源ID + // 它在名为ResourceParser.WidgetBgResources的帮助类中查找适当的资源ID,该类包含了4x小部件背景ID到资源ID的映射 return ResourceParser.WidgetBgResources.getWidget4xBgResource(bgId); } @Override protected int getWidgetType() { + // 覆盖父类的getWidgetType方法,返回一个整数常量,用于标识小部件的类型 + // 在这种情况下,它返回Notes.TYPE_WIDGET_4X,这可能表示这是一个用于显示笔记的小部件,并且它是4x大小的 return Notes.TYPE_WIDGET_4X; } -} +} \ No newline at end of file