diff --git a/doc/黄志祥-小米便签编辑便签用例详细描述.docx b/doc/黄志祥-小米便签编辑便签用例详细描述.docx new file mode 100644 index 0000000..e5b70a8 Binary files /dev/null and b/doc/黄志祥-小米便签编辑便签用例详细描述.docx differ diff --git a/doc/黄志祥-羊涛-开源软件泛读、标注和维护报告文档.docx b/doc/黄志祥-羊涛-开源软件泛读、标注和维护报告文档.docx index e8af7f5..b147f93 100644 Binary files a/doc/黄志祥-羊涛-开源软件泛读、标注和维护报告文档.docx and b/doc/黄志祥-羊涛-开源软件泛读、标注和维护报告文档.docx differ diff --git a/doc/黄志祥-羊涛-开源软件的质量分析报告文档.docx b/doc/黄志祥-羊涛-开源软件的质量分析报告文档.docx index 52a92e0..57153e9 100644 Binary files a/doc/黄志祥-羊涛-开源软件的质量分析报告文档.docx and b/doc/黄志祥-羊涛-开源软件的质量分析报告文档.docx differ diff --git a/mi_note b/mi_note deleted file mode 160000 index d48acec..0000000 --- a/mi_note +++ /dev/null @@ -1 +0,0 @@ -Subproject commit d48acec0b0cae90d95d289c02545fcac5e7a1485 diff --git a/src/datauml.suml b/src/datauml.suml new file mode 100644 index 0000000..6273386 --- /dev/null +++ b/src/datauml.suml @@ -0,0 +1,2 @@ + + diff --git a/src/mi_note/app/src/main/java/net/micode/notes/data/Contact.java b/src/mi_note/app/src/main/java/net/micode/notes/data/Contact.java index d97ac5d..f7583b5 100644 --- a/src/mi_note/app/src/main/java/net/micode/notes/data/Contact.java +++ b/src/mi_note/app/src/main/java/net/micode/notes/data/Contact.java @@ -25,8 +25,14 @@ import android.util.Log; import java.util.HashMap; +/** + * @author hzx + * 版本:1.0 + * 创建日期:2023/10/28 + * 描述:Contact 联系人类 封装通过电话号码获取联系人姓名的接口 + */ public class Contact { - private static HashMap sContactCache; + private static HashMap sContactCache; // 缓存联系人号码和姓名的映射关系 private static final String TAG = "Contact"; private static final String CALLER_ID_SELECTION = "PHONE_NUMBERS_EQUAL(" + Phone.NUMBER @@ -34,19 +40,29 @@ public class Contact { + " AND " + Data.RAW_CONTACT_ID + " IN " + "(SELECT raw_contact_id " + " FROM phone_lookup" - + " WHERE min_match = '+')"; + + " WHERE min_match = '+')"; // 查询条件 + /** + * 根据电话号码获取联系人姓名 + * @param context 应用上下文 + * @param phoneNumber 电话号码 + * @return 联系人姓名 + */ public static String getContact(Context context, String phoneNumber) { if(sContactCache == null) { + // 初始化map缓存 sContactCache = new HashMap(); } if(sContactCache.containsKey(phoneNumber)) { + // 如果map缓存中的键包含要查询的电话号码,直接从缓存中读取 return sContactCache.get(phoneNumber); } + // 获取电话号码的min-match,然后替换原始查询条件中的“+” String selection = CALLER_ID_SELECTION.replace("+", PhoneNumberUtils.toCallerIDMinMatch(phoneNumber)); + // 从联系人内容提供者中查询联系人信息 Cursor cursor = context.getContentResolver().query( Data.CONTENT_URI, new String [] { Phone.DISPLAY_NAME }, @@ -54,18 +70,22 @@ public class Contact { new String[] { phoneNumber }, null); + // 读取查询结果 if (cursor != null && cursor.moveToFirst()) { try { String name = cursor.getString(0); + // 将这次电话号码和联系人姓名的匹配关系缓存起来 sContactCache.put(phoneNumber, name); return name; } catch (IndexOutOfBoundsException e) { + // 数组越界异常,从cursor取不到指定字段,输出日志信息 Log.e(TAG, " Cursor get string error " + e.toString()); return null; } finally { cursor.close(); } } else { + // 查询结果为空,输出日志信息 Log.d(TAG, "No contact matched with number:" + phoneNumber); return null; } diff --git a/src/mi_note/app/src/main/java/net/micode/notes/data/Notes.java b/src/mi_note/app/src/main/java/net/micode/notes/data/Notes.java index f240604..3d22f10 100644 --- a/src/mi_note/app/src/main/java/net/micode/notes/data/Notes.java +++ b/src/mi_note/app/src/main/java/net/micode/notes/data/Notes.java @@ -17,11 +17,21 @@ package net.micode.notes.data; import android.net.Uri; + +/** + * @author hzx + * 版本:1.0 + * 创建日期:2023/10/28 + * 描述:Notes 便签类,封装便签相关的属性 + */ public class Notes { public static final String AUTHORITY = "micode_notes"; public static final String TAG = "Notes"; + // 便签标识 public static final int TYPE_NOTE = 0; + // 文件夹标识 public static final int TYPE_FOLDER = 1; + // 系统文件夹标识 public static final int TYPE_SYSTEM = 2; /** @@ -30,9 +40,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; public static final String INTENT_EXTRA_ALERT_DATE = "net.micode.notes.alert_date"; @@ -42,46 +56,56 @@ 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"; + // 小部件类型 public static final int TYPE_WIDGET_INVALIDE = -1; public static final int TYPE_WIDGET_2X = 0; public static final int TYPE_WIDGET_4X = 1; + /** + * 封装一些数据常量 + */ public static class DataConstants { public static final String NOTE = TextNote.CONTENT_ITEM_TYPE; public static final String CALL_NOTE = CallNote.CONTENT_ITEM_TYPE; } /** - * Uri to query all notes and folders + * 查询所有的便签和文件夹的Uri */ public static final Uri CONTENT_NOTE_URI = Uri.parse("content://" + AUTHORITY + "/note"); /** - * Uri to query data + * 查询所有数据的Uri */ public static final Uri CONTENT_DATA_URI = Uri.parse("content://" + AUTHORITY + "/data"); + /** + * 便签字段接口,封装每一个字段实际存储的名字 + */ public interface NoteColumns { /** - * The unique ID for a row + * 每一行的唯一ID *

Type: INTEGER (long)

*/ public static final String ID = "_id"; /** * The parent's id for note or folder + * 便签或文件夹的父ID *

Type: INTEGER (long)

*/ public static final String PARENT_ID = "parent_id"; /** * Created data for note or folder + * 便签或文件夹的创建日期 *

Type: INTEGER (long)

*/ public static final String CREATED_DATE = "created_date"; /** * Latest modified date + * 最后一次修改的日期 *

Type: INTEGER (long)

*/ public static final String MODIFIED_DATE = "modified_date"; @@ -95,24 +119,28 @@ public class Notes { /** * Folder's name or text content of note + * 文件夹名或便签的文本内容 *

Type: TEXT

*/ public static final String SNIPPET = "snippet"; /** * Note's widget id + * 便签的小部件ID *

Type: INTEGER (long)

*/ public static final String WIDGET_ID = "widget_id"; /** * Note's widget type + * 便签的小部件类型 *

Type: INTEGER (long)

*/ public static final String WIDGET_TYPE = "widget_type"; /** * Note's background color's id + * 便签的背景颜色ID *

Type: INTEGER (long)

*/ public static final String BG_COLOR_ID = "bg_color_id"; @@ -120,127 +148,149 @@ public class Notes { /** * For text note, it doesn't has attachment, for multi-media * note, it has at least one attachment + * 是否有附件 *

Type: INTEGER

*/ public static final String HAS_ATTACHMENT = "has_attachment"; /** * Folder's count of notes + * 文件夹中便签的数量 *

Type: INTEGER (long)

*/ public static final String NOTES_COUNT = "notes_count"; /** * The file type: folder or note + * 文件类型:文件夹或便签 *

Type: INTEGER

*/ public static final String TYPE = "type"; /** * The last sync id + * 最后一次云同步的ID *

Type: INTEGER (long)

*/ public static final String SYNC_ID = "sync_id"; /** * Sign to indicate local modified or not + * 是否本地修改的标记 *

Type: INTEGER

*/ public static final String LOCAL_MODIFIED = "local_modified"; /** * Original parent id before moving into temporary folder + * 在移动到临时文件夹之前,文件夹或便签的父ID *

Type : INTEGER

*/ public static final String ORIGIN_PARENT_ID = "origin_parent_id"; /** * The gtask id + * 云同步ID *

Type : TEXT

*/ public static final String GTASK_ID = "gtask_id"; /** * The version code + * 版本号 *

Type : INTEGER (long)

*/ public static final String VERSION = "version"; } + /** + * 数据字段接口 + */ public interface DataColumns { /** * The unique ID for a row + * 行的唯一ID *

Type: INTEGER (long)

*/ public static final String ID = "_id"; /** * The MIME type of the item represented by this row. + * 这一行代表的项的MIME类型 *

Type: Text

*/ public static final String MIME_TYPE = "mime_type"; /** * The reference id to note that this data belongs to + * 这一行数据所属的便签的ID *

Type: INTEGER (long)

*/ public static final String NOTE_ID = "note_id"; /** * Created data for note or folder + * 创建日期 *

Type: INTEGER (long)

*/ public static final String CREATED_DATE = "created_date"; /** * Latest modified date + * 上一次修改的日期 *

Type: INTEGER (long)

*/ public static final String MODIFIED_DATE = "modified_date"; /** * Data's content + * 数据的内容 *

Type: TEXT

*/ public static final String CONTENT = "content"; /** - * Generic data column, the meaning is {@link #MIMETYPE} specific, used for + * Generic data column, the meaning is {@link #MIME_TYPE} specific, used for * integer data type + * 整型数据 *

Type: INTEGER

*/ public static final String DATA1 = "data1"; /** - * Generic data column, the meaning is {@link #MIMETYPE} specific, used for + * Generic data column, the meaning is {@link #MIME_TYPE} specific, used for * integer data type *

Type: INTEGER

*/ public static final String DATA2 = "data2"; /** - * Generic data column, the meaning is {@link #MIMETYPE} specific, used for + * Generic data column, the meaning is {@link #MIME_TYPE} specific, used for * TEXT data type + * 文本数据 *

Type: TEXT

*/ public static final String DATA3 = "data3"; /** - * Generic data column, the meaning is {@link #MIMETYPE} specific, used for + * Generic data column, the meaning is {@link #MIME_TYPE} specific, used for * TEXT data type *

Type: TEXT

*/ public static final String DATA4 = "data4"; /** - * Generic data column, the meaning is {@link #MIMETYPE} specific, used for + * Generic data column, the meaning is {@link #MIME_TYPE} specific, used for * TEXT data type *

Type: TEXT

*/ public static final String DATA5 = "data5"; } + /** + * 数据字段接口的实现,文本便签 + */ public static final class TextNote implements DataColumns { /** * Mode to indicate the text in check list mode or not @@ -250,16 +300,29 @@ public class Notes { public static final int MODE_CHECK_LIST = 1; + /** + * 内容类型 + */ public static final String CONTENT_TYPE = "vnd.android.cursor.dir/text_note"; + /** + * 内容项类型 + */ public static final String CONTENT_ITEM_TYPE = "vnd.android.cursor.item/text_note"; + /** + * 获取内容的Uri + */ public static final Uri CONTENT_URI = Uri.parse("content://" + AUTHORITY + "/text_note"); } + /** + * 数据字段接口的实现,电话内容 + */ public static final class CallNote implements DataColumns { /** * Call date for this record + * 通话日期 *

Type: INTEGER (long)

*/ public static final String CALL_DATE = DATA1; @@ -270,10 +333,19 @@ public class Notes { */ public static final String PHONE_NUMBER = DATA3; + /** + * 内容类型 + */ public static final String CONTENT_TYPE = "vnd.android.cursor.dir/call_note"; + /** + * 内容项类型 + */ public static final String CONTENT_ITEM_TYPE = "vnd.android.cursor.item/call_note"; + /** + * 获取内容的Uri + */ public static final Uri CONTENT_URI = Uri.parse("content://" + AUTHORITY + "/call_note"); } } diff --git a/src/mi_note/app/src/main/java/net/micode/notes/data/NotesDatabaseHelper.java b/src/mi_note/app/src/main/java/net/micode/notes/data/NotesDatabaseHelper.java index ffe5d57..78a62b9 100644 --- a/src/mi_note/app/src/main/java/net/micode/notes/data/NotesDatabaseHelper.java +++ b/src/mi_note/app/src/main/java/net/micode/notes/data/NotesDatabaseHelper.java @@ -26,22 +26,35 @@ import net.micode.notes.data.Notes.DataColumns; import net.micode.notes.data.Notes.DataConstants; import net.micode.notes.data.Notes.NoteColumns; - +/** + * @author hzx + * 版本:1.0 + * 创建日期:2023/10/28 + * 描述:NotesDatabaseHelper 便签数据库管理帮助类 + * 数据库创建与删除、触发器创建与删除、索引创建与删除、数据库升级 + */ public class NotesDatabaseHelper extends SQLiteOpenHelper { + // 数据库名称 private static final String DB_NAME = "note.db"; + // 数据库版本 private static final int DB_VERSION = 4; + // 数据库中的表名接口 public interface TABLE { + // 便签表 public static final String NOTE = "note"; + // 数据表 public static final String DATA = "data"; } private static final String TAG = "NotesDatabaseHelper"; + // 实例 private static NotesDatabaseHelper mInstance; + // 创建便签表的SQL语句字符串常量 private static final String CREATE_NOTE_TABLE_SQL = "CREATE TABLE " + TABLE.NOTE + "(" + NoteColumns.ID + " INTEGER PRIMARY KEY," + @@ -63,6 +76,7 @@ public class NotesDatabaseHelper extends SQLiteOpenHelper { 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," + @@ -78,6 +92,7 @@ public class NotesDatabaseHelper extends SQLiteOpenHelper { DataColumns.DATA5 + " TEXT NOT NULL DEFAULT ''" + ")"; + // 给数据表中的便签ID字段创建索引的SQL语句字符串常量 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 +100,7 @@ public class NotesDatabaseHelper extends SQLiteOpenHelper { /** * Increase folder's note count when move note to the folder */ + // 创建触发器的SQL语句,当便签的父ID改变时,也就是将便签移动到新的文件夹里,将该文件夹的便签数量字段值加1 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 +113,7 @@ public class NotesDatabaseHelper extends SQLiteOpenHelper { /** * Decrease folder's note count when move note from folder */ + // 创建触发器的SQL语句,把便签从文件夹里移走,该文件夹的便签数量字段值减1 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 +127,7 @@ public class NotesDatabaseHelper extends SQLiteOpenHelper { /** * Increase folder's note count when insert new note to the folder */ + // 创建触发器的SQL语句,该触发器处理当创建新的便签时,增加便签所处文件夹中便签的数量 private static final String NOTE_INCREASE_FOLDER_COUNT_ON_INSERT_TRIGGER = "CREATE TRIGGER increase_folder_count_on_insert " + " AFTER INSERT ON " + TABLE.NOTE + @@ -122,6 +140,7 @@ public class NotesDatabaseHelper extends SQLiteOpenHelper { /** * Decrease folder's note count when delete note from the folder */ + // 创建触发器的SQL语句,该触发器处理当删除便签时,减少对应文件夹便签数量 private static final String NOTE_DECREASE_FOLDER_COUNT_ON_DELETE_TRIGGER = "CREATE TRIGGER decrease_folder_count_on_delete " + " AFTER DELETE ON " + TABLE.NOTE + @@ -135,6 +154,7 @@ public class NotesDatabaseHelper extends SQLiteOpenHelper { /** * Update note's content when insert data with type {@link DataConstants#NOTE} */ + // 创建触发器的SQL语句,该触发器处理当插入新文本数据时,便签的文本内容进行更新 private static final String DATA_UPDATE_NOTE_CONTENT_ON_INSERT_TRIGGER = "CREATE TRIGGER update_note_content_on_insert " + " AFTER INSERT ON " + TABLE.DATA + @@ -148,6 +168,7 @@ public class NotesDatabaseHelper extends SQLiteOpenHelper { /** * Update note's content when data with {@link DataConstants#NOTE} type has changed */ + // 创建触发器的SQL语句,该触发器处理当更新数据表中的文本数据时,便签表中对应的便签内容进行更新 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 +182,7 @@ public class NotesDatabaseHelper extends SQLiteOpenHelper { /** * Update note's content when data with {@link DataConstants#NOTE} type has deleted */ + // 创建触发器的SQL语句,数据表中的记录被删除时触发便签表的更新 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 +196,7 @@ public class NotesDatabaseHelper extends SQLiteOpenHelper { /** * Delete datas belong to note which has been deleted */ + // 创建触发器的SQL语句,便签表的记录被删除时触发数据表对应记录的删除 private static final String NOTE_DELETE_DATA_ON_DELETE_TRIGGER = "CREATE TRIGGER delete_data_on_delete " + " AFTER DELETE ON " + TABLE.NOTE + @@ -185,6 +208,7 @@ public class NotesDatabaseHelper extends SQLiteOpenHelper { /** * Delete notes belong to folder which has been deleted */ + // 创建触发器的SQL语句,当文件夹被删除时,它所包含的所有便签也被删除 private static final String FOLDER_DELETE_NOTES_ON_DELETE_TRIGGER = "CREATE TRIGGER folder_delete_notes_on_delete " + " AFTER DELETE ON " + TABLE.NOTE + @@ -196,6 +220,7 @@ public class NotesDatabaseHelper extends SQLiteOpenHelper { /** * Move notes belong to folder which has been moved to trash folder */ + // 创建触发器的SQL语句,当某文件夹被移入垃圾文件夹中时,其包含的便签或文件夹也被移入垃圾文件夹中 private static final String FOLDER_MOVE_NOTES_ON_TRASH_TRIGGER = "CREATE TRIGGER folder_move_notes_on_trash " + " AFTER UPDATE ON " + TABLE.NOTE + @@ -207,17 +232,30 @@ public class NotesDatabaseHelper extends SQLiteOpenHelper { " END"; public NotesDatabaseHelper(Context context) { + // 初始化数据库 super(context, DB_NAME, null, DB_VERSION); } + /** + * 创建便签表的方法 + * @param db 数据库操作对象 + */ public void createNoteTable(SQLiteDatabase db) { + // 执行建表SQL语句,创建便签表 db.execSQL(CREATE_NOTE_TABLE_SQL); + // 初始化创建便签表触发器 reCreateNoteTableTriggers(db); + // 创建系统文件夹 createSystemFolder(db); Log.d(TAG, "note table has been created"); } + /** + * 重建便签表的所有触发器 + * @param db 数据库操作对象 + */ 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 +264,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); @@ -235,12 +274,17 @@ public class NotesDatabaseHelper extends SQLiteOpenHelper { db.execSQL(FOLDER_MOVE_NOTES_ON_TRASH_TRIGGER); } + /** + * 创建系统文件夹的方法,系统文件夹就是根文件夹 + * @param db 数据库操作对象 + */ private void createSystemFolder(SQLiteDatabase db) { ContentValues values = new ContentValues(); /** - * call record foler for call notes + * call record folder for call notes */ + // 创建调用记录文件夹 values.put(NoteColumns.ID, Notes.ID_CALL_RECORD_FOLDER); values.put(NoteColumns.TYPE, Notes.TYPE_SYSTEM); db.insert(TABLE.NOTE, null, values); @@ -248,6 +292,7 @@ public class NotesDatabaseHelper extends SQLiteOpenHelper { /** * root folder which is default folder */ + // 创建根文件夹 values.clear(); values.put(NoteColumns.ID, Notes.ID_ROOT_FOLDER); values.put(NoteColumns.TYPE, Notes.TYPE_SYSTEM); @@ -256,6 +301,7 @@ public class NotesDatabaseHelper extends SQLiteOpenHelper { /** * temporary folder which is used for moving note */ + // 创建临时文件夹 values.clear(); values.put(NoteColumns.ID, Notes.ID_TEMPARAY_FOLDER); values.put(NoteColumns.TYPE, Notes.TYPE_SYSTEM); @@ -264,29 +310,47 @@ public class NotesDatabaseHelper extends SQLiteOpenHelper { /** * create trash folder */ + // 创建垃圾文件夹(回收站) values.clear(); values.put(NoteColumns.ID, Notes.ID_TRASH_FOLER); values.put(NoteColumns.TYPE, Notes.TYPE_SYSTEM); db.insert(TABLE.NOTE, null, values); } + /** + * 创建数据表的方法 + * @param db 数据库操作对象 + */ public void createDataTable(SQLiteDatabase db) { + // 执行创建数据表的SQL语句 db.execSQL(CREATE_DATA_TABLE_SQL); + // 重建所有的数据表触发器 reCreateDataTableTriggers(db); db.execSQL(CREATE_DATA_NOTE_ID_INDEX_SQL); Log.d(TAG, "data table has been created"); } + /** + * 重建所有数据表触发器的方法 + * @param db 数据库操作对象 + */ private void reCreateDataTableTriggers(SQLiteDatabase db) { + // 删除已经存在的数据表触发器 db.execSQL("DROP TRIGGER IF EXISTS update_note_content_on_insert"); db.execSQL("DROP TRIGGER IF EXISTS update_note_content_on_update"); db.execSQL("DROP TRIGGER IF EXISTS update_note_content_on_delete"); + // 执行创建数据表触发器的SQL语句 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); } + /** + * 获取便签数据库帮助类实例(单例模式)的方法,这是线程安全的方法,同步进行 + * @param context 上下文 + * @return 实例 + */ static synchronized NotesDatabaseHelper getInstance(Context context) { if (mInstance == null) { mInstance = new NotesDatabaseHelper(context); @@ -296,66 +360,96 @@ public class NotesDatabaseHelper extends SQLiteOpenHelper { @Override public void onCreate(SQLiteDatabase db) { + // 初始化创建便签表和数据表 createNoteTable(db); createDataTable(db); } + /** + * 处理数据库升级的方法 + * @param db 数据库操作对象 + * @param oldVersion 旧版本号 + * @param newVersion 新版本号 + */ @Override public void onUpgrade(SQLiteDatabase db, int oldVersion, int newVersion) { boolean reCreateTriggers = false; boolean skipV2 = false; if (oldVersion == 1) { + // 升级到版本2,包含了升级到版本3 upgradeToV2(db); skipV2 = true; // this upgrade including the upgrade from v2 to v3 oldVersion++; } if (oldVersion == 2 && !skipV2) { + // 升级到版本3 upgradeToV3(db); reCreateTriggers = true; oldVersion++; } if (oldVersion == 3) { + // 升级到版本4 upgradeToV4(db); oldVersion++; } if (reCreateTriggers) { + // 重新创建所有的触发器 reCreateNoteTableTriggers(db); reCreateDataTableTriggers(db); } + // 升级失败 if (oldVersion != newVersion) { throw new IllegalStateException("Upgrade notes database to version " + newVersion + "fails"); } } + /** + * 数据库升级到版本2的方法 重新创建所有的表 + * @param db + */ 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); } + /** + * 数据库升级到版本3的方法 重新创建所有的触发器 + * @param 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); db.insert(TABLE.NOTE, null, values); } + /** + * 数据库升级到版本4的方法 + * @param db 数据库操作对象 + */ 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/mi_note/app/src/main/java/net/micode/notes/data/NotesProvider.java b/src/mi_note/app/src/main/java/net/micode/notes/data/NotesProvider.java index edb0a60..356f87d 100644 --- a/src/mi_note/app/src/main/java/net/micode/notes/data/NotesProvider.java +++ b/src/mi_note/app/src/main/java/net/micode/notes/data/NotesProvider.java @@ -34,23 +34,37 @@ import net.micode.notes.data.Notes.DataColumns; import net.micode.notes.data.Notes.NoteColumns; import net.micode.notes.data.NotesDatabaseHelper.TABLE; - +/** + * @author hzx + * 版本:1.0 + * 创建日期:2023/10/28 + * 描述:NotesProvider 便签内容提供者 向外提供便签增删改查的接口 + */ public class NotesProvider extends ContentProvider { + // Uri匹配器 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; + // 数据项 private static final int URI_DATA_ITEM = 4; + // 搜索 private static final int URI_SEARCH = 5; + // 搜索建议 private static final int URI_SEARCH_SUGGEST = 6; static { + // 初始化UriMatcher mMatcher = new UriMatcher(UriMatcher.NO_MATCH); mMatcher.addURI(Notes.AUTHORITY, "note", URI_NOTE); mMatcher.addURI(Notes.AUTHORITY, "note/#", URI_NOTE_ITEM); @@ -65,6 +79,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 + "," @@ -73,6 +88,7 @@ public class NotesProvider extends ContentProvider { + "'" + Intent.ACTION_VIEW + "' AS " + SearchManager.SUGGEST_COLUMN_INTENT_ACTION + "," + "'" + Notes.TextNote.CONTENT_TYPE + "' AS " + SearchManager.SUGGEST_COLUMN_INTENT_DATA; + // 查询便签内容的SQL语句,便签内容作为查询条件 private static String NOTES_SNIPPET_SEARCH_QUERY = "SELECT " + NOTES_SEARCH_PROJECTION + " FROM " + TABLE.NOTE + " WHERE " + NoteColumns.SNIPPET + " LIKE ?" @@ -81,37 +97,50 @@ public class NotesProvider extends ContentProvider { @Override public boolean onCreate() { + // 获取便签数据库帮助类的实例 mHelper = NotesDatabaseHelper.getInstance(getContext()); return true; } + /** + * 查询的方法 + * @param uri 内容Uri + * @param projection 查询映射 + * @param selection 查询条件 + * @param selectionArgs 要填充的参数 + * @param sortOrder 排序方式 asc:升序,desc:降序 + * @return cursor游标 + */ @Override public Cursor query(Uri uri, String[] projection, String selection, String[] selectionArgs, String sortOrder) { Cursor c = null; SQLiteDatabase db = mHelper.getReadableDatabase(); String id = null; + // 根据uri执行对应的操作 switch (mMatcher.match(uri)) { - case URI_NOTE: + case URI_NOTE: // 查询便签 c = db.query(TABLE.NOTE, projection, selection, selectionArgs, null, null, sortOrder); break; - case URI_NOTE_ITEM: + 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: + case URI_DATA: // 查询数据 c = db.query(TABLE.DATA, projection, selection, selectionArgs, null, null, sortOrder); break; - case URI_DATA_ITEM: + case URI_DATA_ITEM: // 查询数据项,指定了数据ID 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"); @@ -119,10 +148,13 @@ public class NotesProvider extends ContentProvider { String searchString = null; if (mMatcher.match(uri) == URI_SEARCH_SUGGEST) { + // 搜索建议 if (uri.getPathSegments().size() > 1) { searchString = uri.getPathSegments().get(1); } } else { + // 搜索 + // 获取查询参数pattern searchString = uri.getQueryParameter("pattern"); } @@ -131,68 +163,93 @@ public class NotesProvider extends ContentProvider { } try { + // 格式化查询字符串,即 xxx --> %xxx% searchString = String.format("%%%s%%", searchString); + // 执行查询SQL语句 c = db.rawQuery(NOTES_SNIPPET_SEARCH_QUERY, new String[] { searchString }); } catch (IllegalStateException ex) { Log.e(TAG, "got exception: " + ex.toString()); } break; - default: + default: // 未知的Uri,爬出异常 throw new IllegalArgumentException("Unknown URI " + uri); } - if (c != null) { + if (c != null) { // 查询结果不为空 + // 监视内容Uri的更改 c.setNotificationUri(getContext().getContentResolver(), uri); } return c; } + /** + * 插入数据或便签的方法 + * @param uri 内容Uri + * @param values 要插入的内容 + * @return uri + */ @Override public Uri insert(Uri uri, ContentValues values) { + // 获取可写数据库 SQLiteDatabase db = mHelper.getWritableDatabase(); long dataId = 0, noteId = 0, insertedId = 0; switch (mMatcher.match(uri)) { - case URI_NOTE: + case URI_NOTE: // 要插入的是便签 insertedId = noteId = db.insert(TABLE.NOTE, null, values); break; - case URI_DATA: + case URI_DATA: // 要插入的是数据 + // 插入数据时,values需要包含便签ID if (values.containsKey(DataColumns.NOTE_ID)) { + // 获取便签ID noteId = values.getAsLong(DataColumns.NOTE_ID); } else { Log.d(TAG, "Wrong data format without note id:" + values.toString()); } insertedId = dataId = db.insert(TABLE.DATA, null, values); break; - default: + default: // 未知的Uri throw new IllegalArgumentException("Unknown URI " + uri); } // Notify the note uri if (noteId > 0) { + // 通知内容解析器便签发生了更改 getContext().getContentResolver().notifyChange( ContentUris.withAppendedId(Notes.CONTENT_NOTE_URI, noteId), null); } // Notify the data uri if (dataId > 0) { + // 通知内容解析器数据发生了更改 getContext().getContentResolver().notifyChange( ContentUris.withAppendedId(Notes.CONTENT_DATA_URI, dataId), null); } + // 原来的uri添加id insertedId return ContentUris.withAppendedId(uri, insertedId); } + /** + * 删除数据或便签的方法 + * @param uri 内容Uri + * @param selection 删除的条件 + * @param selectionArgs 填充到删除条件的参数 + * @return 影响的数据库表行数 + */ @Override public int delete(Uri uri, String selection, String[] selectionArgs) { int count = 0; String id = null; + // 获取可写数据库 SQLiteDatabase db = mHelper.getWritableDatabase(); + // 记录是否删除了数据,没删除为false boolean deleteData = false; switch (mMatcher.match(uri)) { - case URI_NOTE: + case URI_NOTE: // 要删除便签 + // 保证便签ID大于0,防止删除特殊的记录,如根文件夹、临时文件夹、垃圾文件夹 selection = "(" + selection + ") AND " + NoteColumns.ID + ">0 "; count = db.delete(TABLE.NOTE, selection, selectionArgs); break; - case URI_NOTE_ITEM: + case URI_NOTE_ITEM: // 要删除指定ID的便签 id = uri.getPathSegments().get(1); /** * ID that smaller than 0 is system folder which is not allowed to @@ -200,26 +257,30 @@ public class NotesProvider extends ContentProvider { */ long noteId = Long.valueOf(id); if (noteId <= 0) { + // 便签ID小于0的系统文件夹不能被删除 break; } + // 执行删除SQL语句 count = db.delete(TABLE.NOTE, NoteColumns.ID + "=" + id + parseSelection(selection), selectionArgs); break; - case URI_DATA: + case URI_DATA: // 要删除数据 count = db.delete(TABLE.DATA, selection, selectionArgs); deleteData = true; break; - case URI_DATA_ITEM: + case URI_DATA_ITEM: // 要删除指定ID的数据 + // 获取数据ID id = uri.getPathSegments().get(1); count = db.delete(TABLE.DATA, DataColumns.ID + "=" + id + parseSelection(selection), selectionArgs); deleteData = true; break; - default: + default: // 未知的Uri throw new IllegalArgumentException("Unknown URI " + uri); } if (count > 0) { if (deleteData) { + // 删除了数据,通知内容解析器 便签内容发生了改变 getContext().getContentResolver().notifyChange(Notes.CONTENT_NOTE_URI, null); } getContext().getContentResolver().notifyChange(uri, null); @@ -227,58 +288,84 @@ public class NotesProvider extends ContentProvider { return count; } + /** + * 更新便签或数据的方法 + * @param uri 内容Uri + * @param values 要更新的内容 + * @param selection 更新条件 + * @param selectionArgs 填充更新条件的参数 + * @return 影响的数据库表行数 + */ @Override public int update(Uri uri, ContentValues values, String selection, String[] selectionArgs) { + // 记录影响的行数 int count = 0; String id = null; + // 获取可写的数据库 SQLiteDatabase db = mHelper.getWritableDatabase(); + // 记录数据表是否发生了变化 boolean updateData = false; switch (mMatcher.match(uri)) { - case URI_NOTE: + case URI_NOTE: // 要更新便签 + // 临时文件夹版本号加1 increaseNoteVersion(-1, selection, selectionArgs); count = db.update(TABLE.NOTE, values, selection, selectionArgs); break; - case URI_NOTE_ITEM: + case URI_NOTE_ITEM: // 要更新指定ID的便签项 + // 获取便签ID id = uri.getPathSegments().get(1); + // 便签的版本号加1 increaseNoteVersion(Long.valueOf(id), selection, selectionArgs); count = db.update(TABLE.NOTE, values, NoteColumns.ID + "=" + id + parseSelection(selection), selectionArgs); break; - case URI_DATA: + case URI_DATA: // 要更新数据 count = db.update(TABLE.DATA, values, selection, selectionArgs); updateData = true; break; - case URI_DATA_ITEM: + case URI_DATA_ITEM: // 要更新指定ID的数据项 id = uri.getPathSegments().get(1); count = db.update(TABLE.DATA, values, DataColumns.ID + "=" + id + parseSelection(selection), selectionArgs); updateData = true; break; - default: + default: // 未知的Uri throw new IllegalArgumentException("Unknown URI " + uri); } if (count > 0) { if (updateData) { + // 如果更新了data表,那么通知内容解析器note内容发生了改变 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 + ')' : ""); } + /** + * 增加指定便签项的版本号的方法 + * @param id 便签ID + * @param selection 更新条件 + * @param selectionArgs 填充条件的参数 + */ private void increaseNoteVersion(long id, String selection, String[] selectionArgs) { StringBuilder sql = new StringBuilder(120); sql.append("UPDATE "); sql.append(TABLE.NOTE); + // 将version字段加1 sql.append(" SET "); sql.append(NoteColumns.VERSION); sql.append("=" + NoteColumns.VERSION + "+1 "); + // 如果ID不为空或者条件不为空,sql语句增加更新条件where if (id > 0 || !TextUtils.isEmpty(selection)) { sql.append(" WHERE "); } @@ -286,13 +373,16 @@ public class NotesProvider extends ContentProvider { sql.append(NoteColumns.ID + "=" + String.valueOf(id)); } if (!TextUtils.isEmpty(selection)) { + // 处理更新条件,如果id>0,前面添加AND,否则不添加 String selectString = id > 0 ? parseSelection(selection) : selection; for (String args : selectionArgs) { + // 填充占位符? selectString = selectString.replaceFirst("\\?", args); } sql.append(selectString); } + // 执行SQL语句 mHelper.getWritableDatabase().execSQL(sql.toString()); } diff --git a/src/mi_note/app/src/main/java/net/micode/notes/data/data.plantuml b/src/mi_note/app/src/main/java/net/micode/notes/data/data.plantuml new file mode 100644 index 0000000..bca5fbb --- /dev/null +++ b/src/mi_note/app/src/main/java/net/micode/notes/data/data.plantuml @@ -0,0 +1,115 @@ +@startuml + +title __DATA's Class Diagram__\n + + namespace net.micode.notes { + namespace data { + class net.micode.notes.data.Contact { + + } + } + } + + + namespace net.micode.notes { + namespace data { + class net.micode.notes.data.Notes { + + } + } + } + + + namespace net.micode.notes { + namespace data { + class net.micode.notes.data.Notes.CallNote { + + } + } + } + + + namespace net.micode.notes { + namespace data { + interface net.micode.notes.data.Notes.DataColumns { + + } + } + } + + + namespace net.micode.notes { + namespace data { + class net.micode.notes.data.Notes.DataConstants { + + } + } + } + + + namespace net.micode.notes { + namespace data { + interface net.micode.notes.data.Notes.NoteColumns { + + } + } + } + + + namespace net.micode.notes { + namespace data { + class net.micode.notes.data.Notes.TextNote { + + } + } + } + + + namespace net.micode.notes { + namespace data { + class net.micode.notes.data.NotesDatabaseHelper { + + } + } + } + + + namespace net.micode.notes { + namespace data { + interface net.micode.notes.data.NotesDatabaseHelper.TABLE { + + } + } + } + + + namespace net.micode.notes { + namespace data { + class net.micode.notes.data.NotesProvider { + + } + } + } + + + net.micode.notes.data.Notes +-down- net.micode.notes.data.Notes.CallNote + net.micode.notes.data.Notes +-down- net.micode.notes.data.Notes.DataColumns + net.micode.notes.data.Notes +-down- net.micode.notes.data.Notes.DataConstants + net.micode.notes.data.Notes +-down- net.micode.notes.data.Notes.NoteColumns + net.micode.notes.data.Notes +-down- net.micode.notes.data.Notes.TextNote + net.micode.notes.data.Notes.CallNote .up.|> net.micode.notes.data.Notes.DataColumns + net.micode.notes.data.Notes.TextNote .up.|> net.micode.notes.data.Notes.DataColumns + net.micode.notes.data.NotesDatabaseHelper -up-|> android.database.sqlite.SQLiteOpenHelper + net.micode.notes.data.NotesDatabaseHelper +-down- net.micode.notes.data.NotesDatabaseHelper.TABLE + net.micode.notes.data.NotesProvider -up-|> android.content.ContentProvider + net.micode.notes.data.NotesProvider o-- net.micode.notes.data.NotesDatabaseHelper : mHelper + + +right footer + + +PlantUML diagram generated by SketchIt! (https://bitbucket.org/pmesmeur/sketch.it) +For more information about this tool, please contact philippe.mesmeur@gmail.com +endfooter + +@enduml diff --git a/src/mi_note/app/src/main/java/net/micode/notes/gtask/data/data.plantuml b/src/mi_note/app/src/main/java/net/micode/notes/gtask/data/data.plantuml new file mode 100644 index 0000000..8e5e8b6 --- /dev/null +++ b/src/mi_note/app/src/main/java/net/micode/notes/gtask/data/data.plantuml @@ -0,0 +1,73 @@ +@startuml + +title __DATA's Class Diagram__\n + + namespace net.micode.notes { + namespace gtask.data { + class net.micode.notes.gtask.data.MetaData { + + } + } + } + + + namespace net.micode.notes { + namespace gtask.data { + abstract class net.micode.notes.gtask.data.Node { + + } + } + } + + + namespace net.micode.notes { + namespace gtask.data { + class net.micode.notes.gtask.data.SqlData { + + } + } + } + + + namespace net.micode.notes { + namespace gtask.data { + class net.micode.notes.gtask.data.SqlNote { + + } + } + } + + + namespace net.micode.notes { + namespace gtask.data { + class net.micode.notes.gtask.data.Task { + + } + } + } + + + namespace net.micode.notes { + namespace gtask.data { + class net.micode.notes.gtask.data.TaskList { + + } + } + } + + + net.micode.notes.gtask.data.MetaData -up-|> net.micode.notes.gtask.data.Task + net.micode.notes.gtask.data.Task -up-|> net.micode.notes.gtask.data.Node + net.micode.notes.gtask.data.Task o-- net.micode.notes.gtask.data.TaskList : mParent + net.micode.notes.gtask.data.Task o-- net.micode.notes.gtask.data.Task : mPriorSibling + net.micode.notes.gtask.data.TaskList -up-|> net.micode.notes.gtask.data.Node + + +right footer + + +PlantUML diagram generated by SketchIt! (https://bitbucket.org/pmesmeur/sketch.it) +For more information about this tool, please contact philippe.mesmeur@gmail.com +endfooter + +@enduml diff --git a/src/mi_note/app/src/main/java/net/micode/notes/gtask/exception/exception.plantuml b/src/mi_note/app/src/main/java/net/micode/notes/gtask/exception/exception.plantuml new file mode 100644 index 0000000..5861db2 --- /dev/null +++ b/src/mi_note/app/src/main/java/net/micode/notes/gtask/exception/exception.plantuml @@ -0,0 +1,38 @@ +@startuml + +title __EXCEPTION's Class Diagram__\n + + namespace net.micode.notes { + namespace gtask.exception { + class net.micode.notes.gtask.exception.ActionFailureException { + {static} - serialVersionUID : long + + ActionFailureException() + + ActionFailureException() + + ActionFailureException() + } + } + } + + + namespace net.micode.notes { + namespace gtask.exception { + class net.micode.notes.gtask.exception.NetworkFailureException { + {static} - serialVersionUID : long + + NetworkFailureException() + + NetworkFailureException() + + NetworkFailureException() + } + } + } + + + + +right footer + + +PlantUML diagram generated by SketchIt! (https://bitbucket.org/pmesmeur/sketch.it) +For more information about this tool, please contact philippe.mesmeur@gmail.com +endfooter + +@enduml diff --git a/src/mi_note/app/src/main/java/net/micode/notes/gtask/remote/remote.plantuml b/src/mi_note/app/src/main/java/net/micode/notes/gtask/remote/remote.plantuml new file mode 100644 index 0000000..84ed90a --- /dev/null +++ b/src/mi_note/app/src/main/java/net/micode/notes/gtask/remote/remote.plantuml @@ -0,0 +1,65 @@ +@startuml + +title __REMOTE's Class Diagram__\n + + namespace net.micode.notes { + namespace gtask.remote { + class net.micode.notes.gtask.remote.GTaskASyncTask { + + } + } + } + + + namespace net.micode.notes { + namespace gtask.remote { + interface net.micode.notes.gtask.remote.GTaskASyncTask.OnCompleteListener { + + } + } + } + + + namespace net.micode.notes { + namespace gtask.remote { + class net.micode.notes.gtask.remote.GTaskClient { + + } + } + } + + + namespace net.micode.notes { + namespace gtask.remote { + class net.micode.notes.gtask.remote.GTaskManager { + + } + } + } + + + namespace net.micode.notes { + namespace gtask.remote { + class net.micode.notes.gtask.remote.GTaskSyncService { + + } + } + } + + + net.micode.notes.gtask.remote.GTaskASyncTask -up-|> android.os.AsyncTask + net.micode.notes.gtask.remote.GTaskASyncTask o-- net.micode.notes.gtask.remote.GTaskASyncTask.OnCompleteListener : mOnCompleteListener + net.micode.notes.gtask.remote.GTaskASyncTask o-- net.micode.notes.gtask.remote.GTaskManager : mTaskManager + net.micode.notes.gtask.remote.GTaskASyncTask +-down- net.micode.notes.gtask.remote.GTaskASyncTask.OnCompleteListener + net.micode.notes.gtask.remote.GTaskManager o-- net.micode.notes.gtask.data.TaskList : mMetaList + net.micode.notes.gtask.remote.GTaskSyncService -up-|> android.app.Service + + +right footer + + +PlantUML diagram generated by SketchIt! (https://bitbucket.org/pmesmeur/sketch.it) +For more information about this tool, please contact philippe.mesmeur@gmail.com +endfooter + +@enduml diff --git a/src/mi_note/app/src/main/java/net/micode/notes/model/Note.java b/src/mi_note/app/src/main/java/net/micode/notes/model/Note.java index 6706cf6..23714e3 100644 --- a/src/mi_note/app/src/main/java/net/micode/notes/model/Note.java +++ b/src/mi_note/app/src/main/java/net/micode/notes/model/Note.java @@ -34,12 +34,25 @@ import net.micode.notes.data.Notes.TextNote; import java.util.ArrayList; +/** + * @author hzx + * 版本:1.0 + * 创建日期:2023/10/28 + * 描述:Note 便签模型类 + */ 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 + * 创建一个新的便签ID,以便将新的便签添加到数据库中 线程安全 + * @param context 上下文 + * @param folderId 文件夹ID + * @return long 便签ID */ public static synchronized long getNewNoteId(Context context, long folderId) { // Create a new note in the database @@ -54,8 +67,10 @@ public class Note { long noteId = 0; try { + // 从uri中获取便签ID noteId = Long.valueOf(uri.getPathSegments().get(1)); } catch (NumberFormatException e) { + // 从uri中获取便签ID失败 Log.e(TAG, "Get note id error :" + e.toString()); noteId = 0; } @@ -70,42 +85,81 @@ public class Note { mNoteData = new NoteData(); } + /** + * 将键值对存入mNoteDiffValues中 + * @param key 键 + * @param value 值 + */ public void setNoteValue(String key, String value) { mNoteDiffValues.put(key, value); mNoteDiffValues.put(NoteColumns.LOCAL_MODIFIED, 1); mNoteDiffValues.put(NoteColumns.MODIFIED_DATE, System.currentTimeMillis()); } + /** + * 将文本数据存入到mNoteData中 + * @param key 键 + * @param value 值 + */ public void setTextData(String key, String value) { mNoteData.setTextData(key, value); } + /** + * 设置mNoteData的文本数据id + * @param id 文本数据id + */ public void setTextDataId(long id) { mNoteData.setTextDataId(id); } + /** + * 获取mNoteData的文本数据id + * @return 文本数据id + */ public long getTextDataId() { return mNoteData.mTextDataId; } + /** + * 设置mNoteData的电话号码数据id + * @param id 电话号码数据id + */ public void setCallDataId(long id) { mNoteData.setCallDataId(id); } + /** + * 设置mNoteData的电话号码数据 + * @param key 键 + * @param value 值 + */ public void setCallData(String key, String value) { mNoteData.setCallData(key, value); } + /** + * 是否便签被本地修改 便签本身(mNoteDiffValues)被修改或便签的内容数据(mNoteData)被修改 + * @return true or false + */ public boolean isLocalModified() { return mNoteDiffValues.size() > 0 || mNoteData.isLocalModified(); } + /** + * 同步便签 + * @param context 上下文 + * @param noteId 便签ID + * @return boolean 是否成功 + */ public boolean syncNote(Context context, long noteId) { if (noteId <= 0) { + // 便签ID不合法 throw new IllegalArgumentException("Wrong note id:" + noteId); } if (!isLocalModified()) { + // 如果便签没有被本地修改,直接返回true,不用同步了 return true; } @@ -117,41 +171,61 @@ public class Note { if (context.getContentResolver().update( ContentUris.withAppendedId(Notes.CONTENT_NOTE_URI, noteId), mNoteDiffValues, null, null) == 0) { + // 更新失败 Log.e(TAG, "Update note error, should not happen"); // Do not return, fall through } + // 清空便签更新内容 mNoteDiffValues.clear(); + // 将本地更新的便签数据更新到数据库 if (mNoteData.isLocalModified() && (mNoteData.pushIntoContentResolver(context, noteId) == null)) { + // 更新失败 return false; } return true; } + /** + * 便签数据类 + */ private class NoteData { + // 文本数据ID private long mTextDataId; + // 文本数据修改内容,修改了但没写入数据库 private ContentValues mTextDataValues; + // 电话号码数据ID private long mCallDataId; + // 电话号码数据修改内容 private ContentValues mCallDataValues; private static final String TAG = "NoteData"; public NoteData() { + // 初始化 mTextDataValues = new ContentValues(); mCallDataValues = new ContentValues(); mTextDataId = 0; mCallDataId = 0; } + /** + * 便签内容是否被本地修改 + * @return true or false + */ boolean isLocalModified() { return mTextDataValues.size() > 0 || mCallDataValues.size() > 0; } + /** + * 设置文本数据id + * @param id 文本数据id + */ void setTextDataId(long id) { if(id <= 0) { throw new IllegalArgumentException("Text data id should larger than 0"); @@ -159,6 +233,10 @@ public class Note { mTextDataId = id; } + /** + * 设置电话号码数据id + * @param id 电话号码数据id + */ void setCallDataId(long id) { if (id <= 0) { throw new IllegalArgumentException("Call data id should larger than 0"); @@ -166,75 +244,107 @@ 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()); } + /** + * 将更新的文本数据mTextDataValues和电话号码数据mCallDataValues插入或更新到数据库 + * @param context 上下文 + * @param noteId 便签ID + * @return 便签内容Uri + */ Uri pushIntoContentResolver(Context context, long noteId) { - /** - * Check for safety - */ + // 检查便签ID是否合法 if (noteId <= 0) { throw new IllegalArgumentException("Wrong note id:" + noteId); } + // 操作列表 ArrayList operationList = new ArrayList(); ContentProviderOperation.Builder builder = null; + // 维护文本数据 if(mTextDataValues.size() > 0) { mTextDataValues.put(DataColumns.NOTE_ID, noteId); - if (mTextDataId == 0) { + if (mTextDataId == 0) { // 是新创建的数据项,而不是更新数据 + // 设置文本数据的MIME类型 mTextDataValues.put(DataColumns.MIME_TYPE, TextNote.CONTENT_ITEM_TYPE); + // 使用内容解析器插入文本数据到data表中 Uri uri = context.getContentResolver().insert(Notes.CONTENT_DATA_URI, mTextDataValues); try { + // 设置文本数据ID为插入data表后返回ID,即新插入的那条记录的ID setTextDataId(Long.valueOf(uri.getPathSegments().get(1))); } catch (NumberFormatException e) { + // 插入数据失败 抛出异常 Log.e(TAG, "Insert new text data fail with noteId" + noteId); + // 清空要更新的文本数据内容 mTextDataValues.clear(); return null; } - } else { + } else { // 更新data表中ID为mTextDataId的记录 builder = ContentProviderOperation.newUpdate(ContentUris.withAppendedId( Notes.CONTENT_DATA_URI, mTextDataId)); builder.withValues(mTextDataValues); + // 向操作列表中添加一个更新操作 operationList.add(builder.build()); } + // 清空要更新的文本数据内容 mTextDataValues.clear(); } + // 维护电话号码数据 if(mCallDataValues.size() > 0) { + // 插入便签ID到要更新的电话号码数据内容中 mCallDataValues.put(DataColumns.NOTE_ID, noteId); - if (mCallDataId == 0) { + if (mCallDataId == 0) { // 插入新的数据 + // 设置MIME类型 mCallDataValues.put(DataColumns.MIME_TYPE, CallNote.CONTENT_ITEM_TYPE); + // 使用内容解析器插入电话号码数据到data表中 Uri uri = context.getContentResolver().insert(Notes.CONTENT_DATA_URI, mCallDataValues); try { + // 设置电话号码数据ID setCallDataId(Long.valueOf(uri.getPathSegments().get(1))); } catch (NumberFormatException e) { + // 插入失败 抛出异常 Log.e(TAG, "Insert new call data fail with noteId" + noteId); + // 清空要更新的电话号码数据内容 mCallDataValues.clear(); return null; } - } else { + } else { // 更新数据 builder = ContentProviderOperation.newUpdate(ContentUris.withAppendedId( Notes.CONTENT_DATA_URI, mCallDataId)); builder.withValues(mCallDataValues); operationList.add(builder.build()); } + // 清空要更新的电话号码数据内容 mCallDataValues.clear(); } if (operationList.size() > 0) { try { + // 批量处理操作 ContentProviderResult[] results = context.getContentResolver().applyBatch( Notes.AUTHORITY, operationList); return (results == null || results.length == 0 || results[0] == null) ? null @@ -243,6 +353,7 @@ public class Note { Log.e(TAG, String.format("%s: %s", e.toString(), e.getMessage())); return null; } catch (OperationApplicationException e) { + // 操作异常 Log.e(TAG, String.format("%s: %s", e.toString(), e.getMessage())); return null; } diff --git a/src/mi_note/app/src/main/java/net/micode/notes/model/WorkingNote.java b/src/mi_note/app/src/main/java/net/micode/notes/model/WorkingNote.java index be081e4..9236f2b 100644 --- a/src/mi_note/app/src/main/java/net/micode/notes/model/WorkingNote.java +++ b/src/mi_note/app/src/main/java/net/micode/notes/model/WorkingNote.java @@ -31,7 +31,12 @@ import net.micode.notes.data.Notes.NoteColumns; import net.micode.notes.data.Notes.TextNote; import net.micode.notes.tool.ResourceParser.NoteBgResources; - +/** + * @author hzx + * 版本:1.0 + * 创建日期:2023/10/28 + * 描述:WorkingNote 工作便签模型类,维护便签处于编辑模式时的状态 + */ public class WorkingNote { // Note for the working note private Note mNote; @@ -42,26 +47,37 @@ public class WorkingNote { // Note mode private int mMode; + // 便签的提醒日期 private long mAlertDate; + // 便签的上一次修改日期 private long mModifiedDate; + // 便签的背景颜色 private int mBgColorId; + // 便签的小部件ID private int mWidgetId; + // 便签的小部件类型 private int mWidgetType; + // 便签所处的文件夹ID private long mFolderId; + // 工作便签所处的上下文 private Context mContext; + // 日志标签 private static final String TAG = "WorkingNote"; + // 便签是否被删除 private boolean mIsDeleted; + // 便签设置状态改变监听器 private NoteSettingChangedListener mNoteSettingStatusListener; + // 便签数据表字段的投影 即select后面的字段 public static final String[] DATA_PROJECTION = new String[] { DataColumns.ID, DataColumns.CONTENT, @@ -72,6 +88,7 @@ public class WorkingNote { DataColumns.DATA4, }; + // 便签表字段的投影 public static final String[] NOTE_PROJECTION = new String[] { NoteColumns.PARENT_ID, NoteColumns.ALERTED_DATE, @@ -81,6 +98,9 @@ public class WorkingNote { NoteColumns.MODIFIED_DATE }; + /** + * data表和note表中某些字段的索引 + */ private static final int DATA_ID_COLUMN = 0; private static final int DATA_CONTENT_COLUMN = 1; @@ -101,12 +121,12 @@ public class WorkingNote { private static final int NOTE_MODIFIED_DATE_COLUMN = 5; - // New note construct + // 新便签的构造器 即当前编辑的便签是要新建的便签 private WorkingNote(Context context, long folderId) { mContext = context; mAlertDate = 0; mModifiedDate = System.currentTimeMillis(); - mFolderId = folderId; + mFolderId = folderId; // 设置所处文件夹的ID mNote = new Note(); mNoteId = 0; mIsDeleted = false; @@ -114,23 +134,28 @@ public class WorkingNote { mWidgetType = Notes.TYPE_WIDGET_INVALIDE; } - // Existing note construct + // 已经存在的便签构造器 即当前编辑的便签是已有的便签 private WorkingNote(Context context, long noteId, long folderId) { mContext = context; mNoteId = noteId; mFolderId = folderId; mIsDeleted = false; mNote = new Note(); - loadNote(); + loadNote(); // 加载便签的内容 } + /** + * 从数据库中获取便签内容并缓存 + */ private void loadNote() { + // 通过内容解析器查询便签内容 Cursor cursor = mContext.getContentResolver().query( ContentUris.withAppendedId(Notes.CONTENT_NOTE_URI, mNoteId), NOTE_PROJECTION, null, null, null); - if (cursor != null) { + if (cursor != null) { // 查询结果不为空 if (cursor.moveToFirst()) { + // 数据存入对应成员变量 mFolderId = cursor.getLong(NOTE_PARENT_ID_COLUMN); mBgColorId = cursor.getInt(NOTE_BG_COLOR_ID_COLUMN); mWidgetId = cursor.getInt(NOTE_WIDGET_ID_COLUMN); @@ -140,40 +165,60 @@ public class WorkingNote { } cursor.close(); } else { + // 查询结果为空 Log.e(TAG, "No note with id:" + mNoteId); + // 抛出异常 throw new IllegalArgumentException("Unable to find note with id " + mNoteId); } + // 加载便签的数据 loadNoteData(); } + /** + * 从数据库加载便签的数据并缓存 + */ private void loadNoteData() { + // 查询便签ID为mNoteId的便签数据 Cursor cursor = mContext.getContentResolver().query(Notes.CONTENT_DATA_URI, DATA_PROJECTION, DataColumns.NOTE_ID + "=?", new String[] { String.valueOf(mNoteId) }, null); - if (cursor != null) { + if (cursor != null) { // 查询结果不为空 if (cursor.moveToFirst()) { - do { + do { // 遍历所有的便签数据 String type = cursor.getString(DATA_MIME_TYPE_COLUMN); if (DataConstants.NOTE.equals(type)) { + // 提取便签文本数据 + // 存入成员变量 mContent = cursor.getString(DATA_CONTENT_COLUMN); mMode = cursor.getInt(DATA_MODE_COLUMN); mNote.setTextDataId(cursor.getLong(DATA_ID_COLUMN)); } else if (DataConstants.CALL_NOTE.equals(type)) { + // 提取便签电话号码数据ID mNote.setCallDataId(cursor.getLong(DATA_ID_COLUMN)); } else { + // 错误的数据类型 Log.d(TAG, "Wrong note type with type:" + type); } } while (cursor.moveToNext()); } cursor.close(); - } else { + } else { // 查询结果为空 Log.e(TAG, "No data with id:" + mNoteId); throw new IllegalArgumentException("Unable to find note's data with id " + mNoteId); } } + /** + * 创建空的便签 + * @param context 上下文 + * @param folderId 所处文件夹ID + * @param widgetId 小组件ID + * @param widgetType 小组件类型 + * @param defaultBgColorId 默认的背景颜色 + * @return 工作便签 + */ public static WorkingNote createEmptyNote(Context context, long folderId, int widgetId, int widgetType, int defaultBgColorId) { WorkingNote note = new WorkingNote(context, folderId); @@ -183,23 +228,38 @@ public class WorkingNote { return note; } + /** + * 通过便签ID来加载便签数据并创建一个工作便签 + * @param context 上下文 + * @param id 便签ID + * @return 工作便签 + */ public static WorkingNote load(Context context, long id) { return new WorkingNote(context, id, 0); } + /** + * 保存便签内容 线程安全 同步 + * @return 是否保存成功 + */ public synchronized boolean saveNote() { - if (isWorthSaving()) { - if (!existInDatabase()) { + // 判断是否应该保存 + if (isWorthSaving()) { // 应该保存 + if (!existInDatabase()) { // 该便签不存在于数据库,即新创建的便签 + // 在文件夹下创建一个新的便签,然后获取其ID if ((mNoteId = Note.getNewNoteId(mContext, mFolderId)) == 0) { + // 创建新便签失败 Log.e(TAG, "Create new note fail with id:" + mNoteId); return false; } } + // 将编辑的便签内容同步到数据库中 mNote.syncNote(mContext, mNoteId); /** * Update widget content if there exist any widget of this note + * 如果这个便签有小组件,更新小组件的内容 */ if (mWidgetId != AppWidgetManager.INVALID_APPWIDGET_ID && mWidgetType != Notes.TYPE_WIDGET_INVALIDE @@ -207,59 +267,99 @@ public class WorkingNote { mNoteSettingStatusListener.onWidgetChanged(); } return true; - } else { + } else { // 不应该保存 return false; } } + /** + * 判断当前便签是否存在于数据库 + * @return true or false + */ public boolean existInDatabase() { + // 因为mNoteId的初始值为0,如果是从数据库中加载的正常便签,那mNoteId应该大于0 return mNoteId > 0; } + /** + * 判断当前便签是否应该保存 + * @return true or false + */ private boolean isWorthSaving() { if (mIsDeleted || (!existInDatabase() && TextUtils.isEmpty(mContent)) || (existInDatabase() && !mNote.isLocalModified())) { + // 便签被删除 或 是新创建的便签但内容为空 或 数据库中已存在的便签但本地没有修改 + // 不应该保存 return false; } else { + // 应该保存 return true; } } + /** + * 设置 便签设置状态改变监听器 + * @param l 监听器 + */ public void setOnSettingStatusChangedListener(NoteSettingChangedListener l) { mNoteSettingStatusListener = l; } + /** + * 设置便签提醒日期 + * @param date 日期 + * @param set 是否设置提醒 + */ public void setAlertDate(long date, boolean set) { - if (date != mAlertDate) { + if (date != mAlertDate) { // 如果是新设置的提醒日期 + // 本地设置 mAlertDate = date; + // 设置到便签模型中 将会被保存到数据库中 mNote.setNoteValue(NoteColumns.ALERTED_DATE, String.valueOf(mAlertDate)); } if (mNoteSettingStatusListener != null) { + // 设置便签提醒监听 mNoteSettingStatusListener.onClockAlertChanged(date, set); } } + /** + * 修改便签的删除标记 + * @param mark 删除标记 + */ public void markDeleted(boolean mark) { mIsDeleted = mark; if (mWidgetId != AppWidgetManager.INVALID_APPWIDGET_ID && mWidgetType != Notes.TYPE_WIDGET_INVALIDE && mNoteSettingStatusListener != null) { - mNoteSettingStatusListener.onWidgetChanged(); + // 更新小组件内容 + mNoteSettingStatusListener.onWidgetChanged(); } } + /** + * 设置便签的背景颜色 + * @param id 背景颜色ID + */ public void setBgColorId(int id) { - if (id != mBgColorId) { + if (id != mBgColorId) { // 新的背景颜色 mBgColorId = id; if (mNoteSettingStatusListener != null) { + // 改变便签背景颜色 mNoteSettingStatusListener.onBackgroundColorChanged(); } + // 保存到数据库 mNote.setNoteValue(NoteColumns.BG_COLOR_ID, String.valueOf(id)); } } + /** + * 设置便签的查看模式 普通模式或者清单模式 + * @param mode 模式标识 + */ public void setCheckListMode(int mode) { - if (mMode != mode) { + if (mMode != mode) { // 新的模式 if (mNoteSettingStatusListener != null) { + // 实际改变便签的查看模式 mNoteSettingStatusListener.onCheckListModeChanged(mMode, mode); } mMode = mode; @@ -267,99 +367,135 @@ public class WorkingNote { } } + /** + * 设置小组件的类型 + * @param type 小组件类型标识 + */ public void setWidgetType(int type) { - if (type != mWidgetType) { + if (type != mWidgetType) { // 新的小组件类型 mWidgetType = type; mNote.setNoteValue(NoteColumns.WIDGET_TYPE, String.valueOf(mWidgetType)); } } + /** + * 设置小组件ID + * @param id 小组件ID + */ public void setWidgetId(int id) { - if (id != mWidgetId) { + if (id != mWidgetId) { // 新的小组件ID mWidgetId = id; mNote.setNoteValue(NoteColumns.WIDGET_ID, String.valueOf(mWidgetId)); } } + /** + * 设置工作便签的文本内容 + * @param text 文本 + */ public void setWorkingText(String text) { - if (!TextUtils.equals(mContent, text)) { + if (!TextUtils.equals(mContent, text)) { // 文本发生变化 mContent = text; mNote.setTextData(DataColumns.CONTENT, mContent); } } + /** + * 转换成通话记录便签 + * @param phoneNumber 电话号码 + * @param callDate 通话日期 + */ public void convertToCallNote(String phoneNumber, long callDate) { mNote.setCallData(CallNote.CALL_DATE, String.valueOf(callDate)); mNote.setCallData(CallNote.PHONE_NUMBER, phoneNumber); mNote.setNoteValue(NoteColumns.PARENT_ID, String.valueOf(Notes.ID_CALL_RECORD_FOLDER)); } + // 是否有闹钟提醒 public boolean hasClockAlert() { return (mAlertDate > 0 ? true : false); } + // 获取便签内容 public String getContent() { return mContent; } + // 获取便签提醒日期 public long getAlertDate() { return mAlertDate; } + // 获取上一次修改的日期 public long getModifiedDate() { return mModifiedDate; } + // 获取便签背景颜色资源ID public int getBgColorResId() { return NoteBgResources.getNoteBgResource(mBgColorId); } + // 获取背景颜色ID public int getBgColorId() { return mBgColorId; } + // 获取便签标题资源ID public int getTitleBgResId() { return NoteBgResources.getNoteTitleBgResource(mBgColorId); } + // 获取便签查看模式 public int getCheckListMode() { return mMode; } + // 获取便签ID public long getNoteId() { return mNoteId; } + // 获取便签所属文件夹ID public long getFolderId() { return mFolderId; } + // 获取小部件ID public int getWidgetId() { return mWidgetId; } + // 获取小部件类型 public int getWidgetType() { return mWidgetType; } + /** + * 便签设置改变监听器接口 + */ public interface NoteSettingChangedListener { /** * Called when the background color of current note has just changed + * 当前便签的背景颜色改变时被调用 */ void onBackgroundColorChanged(); /** * Called when user set clock + * 设置闹钟提醒时被调用 */ void onClockAlertChanged(long date, boolean set); /** * Call when user create note from widget + * 用户从小部件创建便签时被调用 */ void onWidgetChanged(); /** * Call when switch between check list mode and normal mode + * 在普通模式和清单模式之间切换时被调用 * @param oldMode is previous mode before change * @param newMode is new mode */ diff --git a/src/mi_note/app/src/main/java/net/micode/notes/model/model.plantuml b/src/mi_note/app/src/main/java/net/micode/notes/model/model.plantuml new file mode 100644 index 0000000..bda0387 --- /dev/null +++ b/src/mi_note/app/src/main/java/net/micode/notes/model/model.plantuml @@ -0,0 +1,55 @@ +@startuml + +title __MODEL's Class Diagram__\n + + namespace net.micode.notes { + namespace model { + class net.micode.notes.model.Note { + + } + } + } + + + namespace net.micode.notes { + namespace model { + class net.micode.notes.model.Note.NoteData { + + } + } + } + + + namespace net.micode.notes { + namespace model { + class net.micode.notes.model.WorkingNote { + + } + } + } + + + namespace net.micode.notes { + namespace model { + interface net.micode.notes.model.WorkingNote.NoteSettingChangedListener { + + } + } + } + + + net.micode.notes.model.Note o-- net.micode.notes.model.Note.NoteData : mNoteData + net.micode.notes.model.Note +-down- net.micode.notes.model.Note.NoteData + net.micode.notes.model.WorkingNote o-- net.micode.notes.model.Note : mNote + net.micode.notes.model.WorkingNote o-- net.micode.notes.model.WorkingNote.NoteSettingChangedListener : mNoteSettingStatusListener + net.micode.notes.model.WorkingNote +-down- net.micode.notes.model.WorkingNote.NoteSettingChangedListener + + +right footer + + +PlantUML diagram generated by SketchIt! (https://bitbucket.org/pmesmeur/sketch.it) +For more information about this tool, please contact philippe.mesmeur@gmail.com +endfooter + +@enduml diff --git a/src/mi_note/app/src/main/java/net/micode/notes/tool/DataUtils.java b/src/mi_note/app/src/main/java/net/micode/notes/tool/DataUtils.java index 03314bb..5492081 100644 --- a/src/mi_note/app/src/main/java/net/micode/notes/tool/DataUtils.java +++ b/src/mi_note/app/src/main/java/net/micode/notes/tool/DataUtils.java @@ -29,14 +29,27 @@ import android.util.Log; import net.micode.notes.data.Notes; import net.micode.notes.data.Notes.CallNote; import net.micode.notes.data.Notes.NoteColumns; +import net.micode.notes.data.NotesDatabaseHelper; import net.micode.notes.ui.NotesListAdapter.AppWidgetAttribute; import java.util.ArrayList; import java.util.HashSet; - +/** + * @author hzx + * 版本:1.0 + * 创建日期:2023/10/29 + * 描述:DataUtils 便签、文件夹、便签内容等数据处理工具类 + */ public class DataUtils { public static final String TAG = "DataUtils"; + + /** + * 批量删除便签 + * @param resolver 内容解析器 + * @param ids 便签ID集合 + * @return 是否成功 + */ public static boolean batchDeleteNotes(ContentResolver resolver, HashSet ids) { if (ids == null) { Log.d(TAG, "the ids is null"); @@ -47,19 +60,26 @@ public class DataUtils { return true; } + // 内容操作列表 ArrayList operationList = new ArrayList(); + // 遍历便签ID集合 for (long id : ids) { - if(id == Notes.ID_ROOT_FOLDER) { + if(id == Notes.ID_ROOT_FOLDER) { // 如果是根文件夹 + // 根文件夹不能被删除 Log.e(TAG, "Don't delete system folder root"); continue; } + // 创建一个删除操作 ContentProviderOperation.Builder builder = ContentProviderOperation .newDelete(ContentUris.withAppendedId(Notes.CONTENT_NOTE_URI, id)); + // 将删除操作加入到操作列表中 operationList.add(builder.build()); } try { + // 批量处理删除便签的操作 ContentProviderResult[] results = resolver.applyBatch(Notes.AUTHORITY, operationList); if (results == null || results.length == 0 || results[0] == null) { + // 删除便签失败 Log.d(TAG, "delete notes failed, ids:" + ids.toString()); return false; } @@ -67,19 +87,37 @@ public class DataUtils { } catch (RemoteException e) { Log.e(TAG, String.format("%s: %s", e.toString(), e.getMessage())); } catch (OperationApplicationException e) { + // 执行删除操作发生错误 Log.e(TAG, String.format("%s: %s", e.toString(), e.getMessage())); } return false; } + /** + * 将一个便签从原文件夹移动到另一个文件夹里 + * @param resolver 内容解析器 + * @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(); + // 将便签的父ID改为目标文件夹ID values.put(NoteColumns.PARENT_ID, desFolderId); + // 将便签的原父ID改为移动前的文件夹ID values.put(NoteColumns.ORIGIN_PARENT_ID, srcFolderId); values.put(NoteColumns.LOCAL_MODIFIED, 1); + // 将便签ID放入uri中然后通过内容解析器执行更新操作 resolver.update(ContentUris.withAppendedId(Notes.CONTENT_NOTE_URI, id), values, null, null); } + /** + * 批量移动便签到指定文件夹里 + * @param resolver 内容解析器 + * @param ids 便签ID集合 + * @param folderId 目标文件夹ID + * @return 是否成功 + */ public static boolean batchMoveToFolder(ContentResolver resolver, HashSet ids, long folderId) { if (ids == null) { @@ -87,34 +125,43 @@ public class DataUtils { return true; } + // 创建一个内容提供者操作列表 ArrayList operationList = new ArrayList(); for (long id : ids) { + // 创建更新操作 ContentProviderOperation.Builder builder = ContentProviderOperation .newUpdate(ContentUris.withAppendedId(Notes.CONTENT_NOTE_URI, id)); builder.withValue(NoteColumns.PARENT_ID, folderId); builder.withValue(NoteColumns.LOCAL_MODIFIED, 1); + // 将更新操作加入到操作列表中,以便后面批量处理 operationList.add(builder.build()); } try { + // 批量执行更新操作 ContentProviderResult[] results = resolver.applyBatch(Notes.AUTHORITY, operationList); if (results == null || results.length == 0 || results[0] == null) { - Log.d(TAG, "delete notes failed, ids:" + ids.toString()); + // 更新失败,没有行影响 + Log.d(TAG, "update notes failed, ids:" + ids.toString()); return false; } return true; } catch (RemoteException e) { Log.e(TAG, String.format("%s: %s", e.toString(), e.getMessage())); } catch (OperationApplicationException e) { + // 执行更新操作出错 Log.e(TAG, String.format("%s: %s", e.toString(), e.getMessage())); } return false; } /** - * Get the all folder count except system folders {@link Notes#TYPE_SYSTEM}} + * 获取除系统文件夹{@link Notes#TYPE_SYSTEM}(根文件夹、垃圾文件夹、临时文件夹)外的文件夹数量 + * @param resolver 内容解析器 + * @return int 普通文件夹数量 */ public static int getUserFolderCount(ContentResolver resolver) { + // 查询不在垃圾文件夹(回收站)中的普通文件夹数量 Cursor cursor =resolver.query(Notes.CONTENT_NOTE_URI, new String[] { "COUNT(*)" }, NoteColumns.TYPE + "=? AND " + NoteColumns.PARENT_ID + "<>?", @@ -125,6 +172,7 @@ public class DataUtils { if(cursor != null) { if(cursor.moveToFirst()) { try { + // 获取查询结果 count = cursor.getInt(0); } catch (IndexOutOfBoundsException e) { Log.e(TAG, "get folder count failed:" + e.toString()); @@ -136,9 +184,15 @@ public class DataUtils { return count; } - //通过ContentResolver查询判断便签是否存在 + /** + * 判断指定ID和类型的文件(便签或文件夹)是否在数据库可见 存在于数据库,且不在回收站中 + * @param resolver 内容解析器 + * @param noteId 文件ID + * @param type 文件类型 {@link NoteColumns#TYPE} 便签或文件夹 + * @return true or false + */ public static boolean visibleInNoteDatabase(ContentResolver resolver, long noteId, int type) { - //查询便签,返回查询结果 + // 通过resolver查询 Cursor cursor = resolver.query(ContentUris.withAppendedId(Notes.CONTENT_NOTE_URI, noteId), null, NoteColumns.TYPE + "=? AND " + NoteColumns.PARENT_ID + "<>" + Notes.ID_TRASH_FOLER, @@ -148,6 +202,7 @@ public class DataUtils { boolean exist = false; if (cursor != null) { if (cursor.getCount() > 0) { + // 有查询结果,说明指定便签在数据库可见 exist = true; } cursor.close(); @@ -155,13 +210,21 @@ public class DataUtils { return exist; } + /** + * 判断指定便签ID的便签是否存在于数据库中 + * @param resolver 内容解析器 + * @param noteId 便签ID + * @return true or false + */ public static boolean existInNoteDatabase(ContentResolver resolver, long noteId) { + // 查询 Cursor cursor = resolver.query(ContentUris.withAppendedId(Notes.CONTENT_NOTE_URI, noteId), null, null, null, null); boolean exist = false; if (cursor != null) { if (cursor.getCount() > 0) { + // 有结果 exist = true; } cursor.close(); @@ -169,13 +232,21 @@ public class DataUtils { return exist; } + /** + * 判断指定ID的数据记录{@link NotesDatabaseHelper.TABLE#DATA}是否存在于数据库中 + * @param resolver 内容解析器 + * @param dataId 数据项ID + * @return true or false + */ public static boolean existInDataDatabase(ContentResolver resolver, long dataId) { + // 查询 Cursor cursor = resolver.query(ContentUris.withAppendedId(Notes.CONTENT_DATA_URI, dataId), null, null, null, null); boolean exist = false; if (cursor != null) { if (cursor.getCount() > 0) { + // 有结果 exist = true; } cursor.close(); @@ -183,7 +254,14 @@ public class DataUtils { return exist; } + /** + * 检查指定名字的文件夹是否在数据库可见 存在于数据库且不在回收站中 + * @param resolver 内容解析器 + * @param name 文件夹名 + * @return true or false + */ 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 + @@ -192,6 +270,7 @@ public class DataUtils { boolean exist = false; if(cursor != null) { if(cursor.getCount() > 0) { + // 查询有结果 exist = true; } cursor.close(); @@ -199,7 +278,14 @@ public class DataUtils { return exist; } + /** + * 获取指定ID的文件夹的便签小部件 + * @param resolver 内容解析器 + * @param folderId 文件夹ID + * @return HashSet 小部件属性集合 + */ public static HashSet getFolderNoteWidget(ContentResolver resolver, long folderId) { + // 查询小部件ID和类型 Cursor c = resolver.query(Notes.CONTENT_NOTE_URI, new String[] { NoteColumns.WIDGET_ID, NoteColumns.WIDGET_TYPE }, NoteColumns.PARENT_ID + "=?", @@ -210,11 +296,13 @@ public class DataUtils { if (c != null) { if (c.moveToFirst()) { set = new HashSet(); + // 遍历查询到的所有小部件 do { try { AppWidgetAttribute widget = new AppWidgetAttribute(); widget.widgetId = c.getInt(0); widget.widgetType = c.getInt(1); + // 加入小部件属性集合 set.add(widget); } catch (IndexOutOfBoundsException e) { Log.e(TAG, e.toString()); @@ -226,7 +314,14 @@ public class DataUtils { return set; } + /** + * 通过便签ID获取通话号码 + * @param resolver 内容解析器 + * @param noteId 便签ID + * @return 电话号码 + */ public static String getCallNumberByNoteId(ContentResolver resolver, long noteId) { + // 通过内容解析器查询电话号码 Cursor cursor = resolver.query(Notes.CONTENT_DATA_URI, new String [] { CallNote.PHONE_NUMBER }, CallNote.NOTE_ID + "=? AND " + CallNote.MIME_TYPE + "=?", @@ -235,6 +330,7 @@ public class DataUtils { if (cursor != null && cursor.moveToFirst()) { try { + // 返回查询到的电话号码 return cursor.getString(0); } catch (IndexOutOfBoundsException e) { Log.e(TAG, "Get call number fails " + e.toString()); @@ -245,7 +341,15 @@ public class DataUtils { return ""; } + /** + * 通过电话号码和通话日期获取便签ID + * @param resolver 内容解析器 + * @param phoneNumber 电话号码 + * @param callDate 通话日期 + * @return 便签ID + */ public static long getNoteIdByPhoneNumberAndCallDate(ContentResolver resolver, String phoneNumber, long callDate) { + // 通过内容解析器查询 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,6 +360,7 @@ public class DataUtils { if (cursor != null) { if (cursor.moveToFirst()) { try { + // 返回查询到的便签ID return cursor.getLong(0); } catch (IndexOutOfBoundsException e) { Log.e(TAG, "Get call note id fails " + e.toString()); @@ -266,7 +371,14 @@ public class DataUtils { return 0; } + /** + * 通过文件ID查询文件夹名或便签文本内容 + * @param resolver 内容解析器 + * @param noteId 文件ID + * @return 文件夹名称或便签文本内容 + */ public static String getSnippetById(ContentResolver resolver, long noteId) { + // 通过内容解析器查询 Cursor cursor = resolver.query(Notes.CONTENT_NOTE_URI, new String [] { NoteColumns.SNIPPET }, NoteColumns.ID + "=?", @@ -276,19 +388,28 @@ public class DataUtils { if (cursor != null) { String snippet = ""; if (cursor.moveToFirst()) { + // 返回查询结果 snippet = cursor.getString(0); } cursor.close(); return snippet; } + // 查询不到,抛出异常 throw new IllegalArgumentException("Note is not found with id: " + noteId); } + /** + * 获取格式化后的snippet 去掉首尾的空白字符(换行符、空格等) + * 如果还有换行符,则截取第一个换行符之前的内容 + * @param snippet 文件夹名或便签文本内容 + * @return 格式化后的结果 + */ public static String getFormattedSnippet(String snippet) { if (snippet != null) { snippet = snippet.trim(); int index = snippet.indexOf('\n'); if (index != -1) { + // 截取第一个换行符之前的字符串 snippet = snippet.substring(0, index); } } diff --git a/src/mi_note/app/src/main/java/net/micode/notes/tool/GTaskStringUtils.java b/src/mi_note/app/src/main/java/net/micode/notes/tool/GTaskStringUtils.java index 666b729..2ee1a07 100644 --- a/src/mi_note/app/src/main/java/net/micode/notes/tool/GTaskStringUtils.java +++ b/src/mi_note/app/src/main/java/net/micode/notes/tool/GTaskStringUtils.java @@ -16,6 +16,12 @@ package net.micode.notes.tool; +/** + * @author hzx + * 版本:1.0 + * 创建日期:2023/10/29 + * 描述:GTaskStringUtils google账号同步的字符串工具类,封装一些字符串常量 + */ public class GTaskStringUtils { public final static String GTASK_JSON_ACTION_ID = "action_id"; diff --git a/src/mi_note/app/src/main/java/net/micode/notes/tool/ResourceParser.java b/src/mi_note/app/src/main/java/net/micode/notes/tool/ResourceParser.java index 1ad3ad6..b62f8eb 100644 --- a/src/mi_note/app/src/main/java/net/micode/notes/tool/ResourceParser.java +++ b/src/mi_note/app/src/main/java/net/micode/notes/tool/ResourceParser.java @@ -22,24 +22,43 @@ import android.preference.PreferenceManager; import net.micode.notes.R; import net.micode.notes.ui.NotesPreferenceActivity; +/** + * @author hzx + * 版本:1.0 + * 创建日期:2023/10/29 + * 描述:ResourceParser 资源解析器 + * 封装了一些颜色和字体大小常量,能够通过颜色或字体大小ID获取资源ID + */ 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; + // 默认的颜色 黄色 0 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; + // 默认的字体大小 中等 1 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, @@ -48,6 +67,7 @@ public class ResourceParser { R.drawable.edit_red }; + // 编辑标题背景资源数组 private final static int [] BG_EDIT_TITLE_RESOURCES = new int [] { R.drawable.edit_title_yellow, R.drawable.edit_title_blue, @@ -56,25 +76,39 @@ public class ResourceParser { R.drawable.edit_title_red }; + // 根据背景颜色ID获取便签背景资源ID public static int getNoteBgResource(int id) { return BG_EDIT_RESOURCES[id]; } + // 根据背景颜色ID获取便签标题背景资源ID public static int getNoteTitleBgResource(int id) { return BG_EDIT_TITLE_RESOURCES[id]; } } + /** + * 获取默认的背景颜色ID + * @param context 上下文 + * @return 背景颜色ID + */ public static int getDefaultBgId(Context context) { if (PreferenceManager.getDefaultSharedPreferences(context).getBoolean( NotesPreferenceActivity.PREFERENCE_SET_BG_COLOR_KEY, false)) { + // 设置了随机背景颜色 + // 返回一个随机背景颜色ID return (int) (Math.random() * NoteBgResources.BG_EDIT_RESOURCES.length); } else { + // 返回默认的背景颜色ID 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, @@ -83,6 +117,7 @@ public class ResourceParser { R.drawable.list_red_up }; + // 正常背景资源 private final static int [] BG_NORMAL_RESOURCES = new int [] { R.drawable.list_yellow_middle, R.drawable.list_blue_middle, @@ -91,6 +126,7 @@ public class ResourceParser { R.drawable.list_red_middle }; + // 结尾背景资源 private final static int [] BG_LAST_RESOURCES = new int [] { R.drawable.list_yellow_down, R.drawable.list_blue_down, @@ -99,6 +135,7 @@ public class ResourceParser { R.drawable.list_red_down, }; + // 单独的背景资源 private final static int [] BG_SINGLE_RESOURCES = new int [] { R.drawable.list_yellow_single, R.drawable.list_blue_single, @@ -107,28 +144,37 @@ public class ResourceParser { R.drawable.list_red_single }; + // 根据背景颜色ID获取开头背景资源ID public static int getNoteBgFirstRes(int id) { return BG_FIRST_RESOURCES[id]; } + // 根据背景颜色ID获取结尾背景资源ID public static int getNoteBgLastRes(int id) { return BG_LAST_RESOURCES[id]; } + // 根据背景颜色ID获取单独的背景资源ID public static int getNoteBgSingleRes(int id) { return BG_SINGLE_RESOURCES[id]; } + // 根据背景颜色ID获取普通背景资源ID public static int getNoteBgNormalRes(int id) { return BG_NORMAL_RESOURCES[id]; } + // 获取文件夹背景资源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, @@ -137,10 +183,12 @@ public class ResourceParser { R.drawable.widget_2x_red, }; + // 根据背景颜色ID获取小部件背景资源ID 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, @@ -149,12 +197,17 @@ public class ResourceParser { R.drawable.widget_4x_red }; + // 根据背景颜色ID获取小部件背景资源ID 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, @@ -162,18 +215,21 @@ public class ResourceParser { R.style.TextAppearanceSuper }; + // 根据字体大小ID获取字体资源ID 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/mi_note/app/src/main/java/net/micode/notes/tool/tool.plantuml b/src/mi_note/app/src/main/java/net/micode/notes/tool/tool.plantuml new file mode 100644 index 0000000..ce9643e --- /dev/null +++ b/src/mi_note/app/src/main/java/net/micode/notes/tool/tool.plantuml @@ -0,0 +1,101 @@ +@startuml + +title __TOOL's Class Diagram__\n + + namespace net.micode.notes { + namespace tool { + class net.micode.notes.tool.BackupUtils { + + } + } + } + + + namespace net.micode.notes { + namespace tool { + class net.micode.notes.tool.BackupUtils.TextExport { + + } + } + } + + + namespace net.micode.notes { + namespace tool { + class net.micode.notes.tool.DataUtils { + + } + } + } + + + namespace net.micode.notes { + namespace tool { + class net.micode.notes.tool.GTaskStringUtils { + + } + } + } + + + namespace net.micode.notes { + namespace tool { + class net.micode.notes.tool.ResourceParser { + + } + } + } + + + namespace net.micode.notes { + namespace tool { + class net.micode.notes.tool.ResourceParser.NoteBgResources { + + } + } + } + + + namespace net.micode.notes { + namespace tool { + class net.micode.notes.tool.ResourceParser.NoteItemBgResources { + + } + } + } + + + namespace net.micode.notes { + namespace tool { + class net.micode.notes.tool.ResourceParser.TextAppearanceResources { + + } + } + } + + + namespace net.micode.notes { + namespace tool { + class net.micode.notes.tool.ResourceParser.WidgetBgResources { + + } + } + } + + + net.micode.notes.tool.BackupUtils o-- net.micode.notes.tool.BackupUtils.TextExport : mTextExport + net.micode.notes.tool.BackupUtils +-down- net.micode.notes.tool.BackupUtils.TextExport + net.micode.notes.tool.ResourceParser +-down- net.micode.notes.tool.ResourceParser.NoteBgResources + net.micode.notes.tool.ResourceParser +-down- net.micode.notes.tool.ResourceParser.NoteItemBgResources + net.micode.notes.tool.ResourceParser +-down- net.micode.notes.tool.ResourceParser.TextAppearanceResources + net.micode.notes.tool.ResourceParser +-down- net.micode.notes.tool.ResourceParser.WidgetBgResources + + +right footer + + +PlantUML diagram generated by SketchIt! (https://bitbucket.org/pmesmeur/sketch.it) +For more information about this tool, please contact philippe.mesmeur@gmail.com +endfooter + +@enduml diff --git a/src/mi_note/app/src/main/java/net/micode/notes/ui/ui.plantuml b/src/mi_note/app/src/main/java/net/micode/notes/ui/ui.plantuml new file mode 100644 index 0000000..579f795 --- /dev/null +++ b/src/mi_note/app/src/main/java/net/micode/notes/ui/ui.plantuml @@ -0,0 +1,283 @@ +@startuml + +title __UI's Class Diagram__\n + + namespace net.micode.notes { + namespace ui { + class net.micode.notes.ui.AlarmAlertActivity { + + } + } + } + + + namespace net.micode.notes { + namespace ui { + class net.micode.notes.ui.AlarmInitReceiver { + + } + } + } + + + namespace net.micode.notes { + namespace ui { + class net.micode.notes.ui.AlarmReceiver { + + } + } + } + + + namespace net.micode.notes { + namespace ui { + class net.micode.notes.ui.DateTimePicker { + + } + } + } + + + namespace net.micode.notes { + namespace ui { + interface net.micode.notes.ui.DateTimePicker.OnDateTimeChangedListener { + {abstract} + onDateTimeChanged() + } + } + } + + + namespace net.micode.notes { + namespace ui { + class net.micode.notes.ui.DateTimePickerDialog { + + } + } + } + + + namespace net.micode.notes { + namespace ui { + interface net.micode.notes.ui.DateTimePickerDialog.OnDateTimeSetListener { + {abstract} + OnDateTimeSet() + } + } + } + + + namespace net.micode.notes { + namespace ui { + class net.micode.notes.ui.DropdownMenu { + + } + } + } + + + namespace net.micode.notes { + namespace ui { + class net.micode.notes.ui.FoldersListAdapter { + + } + } + } + + + namespace net.micode.notes { + namespace ui { + class net.micode.notes.ui.FoldersListAdapter.FolderListItem { + + } + } + } + + + namespace net.micode.notes { + namespace ui { + class net.micode.notes.ui.NoteEditActivity { + + } + } + } + + + namespace net.micode.notes { + namespace ui { + class net.micode.notes.ui.NoteEditActivity.HeadViewHolder { + + } + } + } + + + namespace net.micode.notes { + namespace ui { + class net.micode.notes.ui.NoteEditText { + + } + } + } + + + namespace net.micode.notes { + namespace ui { + interface net.micode.notes.ui.NoteEditText.OnTextViewChangeListener { + + } + } + } + + + namespace net.micode.notes { + namespace ui { + class net.micode.notes.ui.NoteItemData { + + } + } + } + + + namespace net.micode.notes { + namespace ui { + class net.micode.notes.ui.NotesListActivity { + + } + } + } + + + namespace net.micode.notes { + namespace ui { + class net.micode.notes.ui.NotesListActivity.BackgroundQueryHandler { + + } + } + } + + + namespace net.micode.notes { + namespace ui { + enum ListEditState { + + } + } + } + + + namespace net.micode.notes { + namespace ui { + class net.micode.notes.ui.NotesListActivity.ModeCallback { + + } + } + } + + + namespace net.micode.notes { + namespace ui { + class net.micode.notes.ui.NotesListActivity.NewNoteOnTouchListener { + + } + } + } + + + namespace net.micode.notes { + namespace ui { + class net.micode.notes.ui.NotesListActivity.OnListItemClickListener { + + } + } + } + + + namespace net.micode.notes { + namespace ui { + class net.micode.notes.ui.NotesListAdapter { + + } + } + } + + + namespace net.micode.notes { + namespace ui { + class net.micode.notes.ui.NotesListAdapter.AppWidgetAttribute { + + } + } + } + + + namespace net.micode.notes { + namespace ui { + class net.micode.notes.ui.NotesListItem { + + } + } + } + + + namespace net.micode.notes { + namespace ui { + class net.micode.notes.ui.NotesPreferenceActivity { + + } + } + } + + + namespace net.micode.notes { + namespace ui { + class net.micode.notes.ui.NotesPreferenceActivity.GTaskReceiver { + + } + } + } + + + + net.micode.notes.ui.DateTimePicker o-- net.micode.notes.ui.DateTimePicker.OnDateTimeChangedListener : mOnDateTimeChangedListener + net.micode.notes.ui.DateTimePicker +-down- net.micode.notes.ui.DateTimePicker.OnDateTimeChangedListener + + net.micode.notes.ui.DateTimePickerDialog o-- net.micode.notes.ui.DateTimePicker : mDateTimePicker + net.micode.notes.ui.DateTimePickerDialog o-- net.micode.notes.ui.DateTimePickerDialog.OnDateTimeSetListener : mOnDateTimeSetListener + net.micode.notes.ui.DateTimePickerDialog +-down- net.micode.notes.ui.DateTimePickerDialog.OnDateTimeSetListener + + net.micode.notes.ui.FoldersListAdapter +-down- net.micode.notes.ui.FoldersListAdapter.FolderListItem + + + net.micode.notes.ui.NoteEditActivity .up.|> net.micode.notes.ui.NoteEditText.OnTextViewChangeListener + + net.micode.notes.ui.NoteEditActivity o-- net.micode.notes.ui.NoteEditActivity.HeadViewHolder : mNoteHeaderHolder + net.micode.notes.ui.NoteEditActivity +-down- net.micode.notes.ui.NoteEditActivity.HeadViewHolder + net.micode.notes.ui.NoteEditText o-- net.micode.notes.ui.NoteEditText.OnTextViewChangeListener : mOnTextViewChangeListener + net.micode.notes.ui.NoteEditText +-down- net.micode.notes.ui.NoteEditText.OnTextViewChangeListener + + net.micode.notes.ui.NotesListActivity o-- net.micode.notes.ui.NotesListActivity.BackgroundQueryHandler : mBackgroundQueryHandler + net.micode.notes.ui.NotesListActivity o-- net.micode.notes.ui.NoteItemData : mFocusNoteDataItem + net.micode.notes.ui.NotesListActivity o-- net.micode.notes.ui.NotesListActivity.ModeCallback : mModeCallBack + net.micode.notes.ui.NotesListActivity o-- net.micode.notes.ui.NotesListAdapter : mNotesListAdapter + net.micode.notes.ui.NotesListActivity o-- net.micode.notes.ui.NotesListActivity.ListEditState : mState + net.micode.notes.ui.NotesListActivity +-down- net.micode.notes.ui.NotesListActivity.BackgroundQueryHandler + net.micode.notes.ui.NotesListActivity +-down- net.micode.notes.ui.NotesListActivity.ListEditState + net.micode.notes.ui.NotesListActivity +-down- net.micode.notes.ui.NotesListActivity.ModeCallback + net.micode.notes.ui.NotesListActivity +-down- net.micode.notes.ui.NotesListActivity.NewNoteOnTouchListener + net.micode.notes.ui.NotesListActivity +-down- net.micode.notes.ui.NotesListActivity.OnListItemClickListener + + + net.micode.notes.ui.NotesListActivity.ModeCallback o-- net.micode.notes.ui.DropdownMenu : mDropDownMenu + net.micode.notes.ui.NotesListAdapter +-down- net.micode.notes.ui.NotesListAdapter.AppWidgetAttribute + net.micode.notes.ui.NotesListItem o-- net.micode.notes.ui.NoteItemData : mItemData + net.micode.notes.ui.NotesPreferenceActivity o-- net.micode.notes.ui.NotesPreferenceActivity.GTaskReceiver : mReceiver + net.micode.notes.ui.NotesPreferenceActivity +-down- net.micode.notes.ui.NotesPreferenceActivity.GTaskReceiver + + +right footer + + +PlantUML diagram generated by SketchIt! (https://bitbucket.org/pmesmeur/sketch.it) +For more information about this tool, please contact philippe.mesmeur@gmail.com +endfooter + +@enduml diff --git a/src/mi_note/app/src/main/java/net/micode/notes/widget/widget.plantuml b/src/mi_note/app/src/main/java/net/micode/notes/widget/widget.plantuml new file mode 100644 index 0000000..74f43c6 --- /dev/null +++ b/src/mi_note/app/src/main/java/net/micode/notes/widget/widget.plantuml @@ -0,0 +1,61 @@ +@startuml + +title __WIDGET's Class Diagram__\n + + namespace net.micode.notes { + namespace widget { + abstract class net.micode.notes.widget.NoteWidgetProvider { + {static} + COLUMN_BG_COLOR_ID : int + {static} + COLUMN_ID : int + {static} + COLUMN_SNIPPET : int + {static} + PROJECTION : String[] + {static} - TAG : String + + onDeleted() + {abstract} # getBgResourceId() + {abstract} # getLayoutId() + {abstract} # getWidgetType() + # update() + - getNoteWidgetInfo() + - update() + } + } + } + + + namespace net.micode.notes { + namespace widget { + class net.micode.notes.widget.NoteWidgetProvider_2x { + + onUpdate() + # getBgResourceId() + # getLayoutId() + # getWidgetType() + } + } + } + + + namespace net.micode.notes { + namespace widget { + class net.micode.notes.widget.NoteWidgetProvider_4x { + + onUpdate() + # getBgResourceId() + # getLayoutId() + # getWidgetType() + } + } + } + + + net.micode.notes.widget.NoteWidgetProvider -up-|> android.appwidget.AppWidgetProvider + net.micode.notes.widget.NoteWidgetProvider_2x -up-|> net.micode.notes.widget.NoteWidgetProvider + net.micode.notes.widget.NoteWidgetProvider_4x -up-|> net.micode.notes.widget.NoteWidgetProvider + + +right footer + + +PlantUML diagram generated by SketchIt! (https://bitbucket.org/pmesmeur/sketch.it) +For more information about this tool, please contact philippe.mesmeur@gmail.com +endfooter + +@enduml diff --git a/src/mi_note/app/src/main/res/drawable-hdpi/edit_title_yellow.9.png b/src/mi_note/app/src/main/res/drawable-hdpi/edit_title_yellow.9.png index bf8f580..3fb224f 100644 Binary files a/src/mi_note/app/src/main/res/drawable-hdpi/edit_title_yellow.9.png and b/src/mi_note/app/src/main/res/drawable-hdpi/edit_title_yellow.9.png differ diff --git a/src/minote.suml b/src/minote.suml new file mode 100644 index 0000000..f485252 --- /dev/null +++ b/src/minote.suml @@ -0,0 +1,2 @@ + +