From a7b3aa33f4f06e30a2c068012671ee4b54ae80f9 Mon Sep 17 00:00:00 2001
From: pkagifq93 <2740594397@qq.com>
Date: Mon, 16 Dec 2024 23:00:39 +0800
Subject: [PATCH 01/13] ADD file via upload
---
.../zhushi1.txt | 467 ++++++++++++++++++
1 file changed, 467 insertions(+)
create mode 100644 src%2FNotes-master%2Fsrc%2Fnet%2Fmicode%2Fnotes/zhushi1.txt
diff --git a/src%2FNotes-master%2Fsrc%2Fnet%2Fmicode%2Fnotes/zhushi1.txt b/src%2FNotes-master%2Fsrc%2Fnet%2Fmicode%2Fnotes/zhushi1.txt
new file mode 100644
index 0000000..c0c0575
--- /dev/null
+++ b/src%2FNotes-master%2Fsrc%2Fnet%2Fmicode%2Fnotes/zhushi1.txt
@@ -0,0 +1,467 @@
+package net.micode.notes.data;
+
+import android.content.ContentValues;
+import android.content.Context;
+import android.database.sqlite.SQLiteDatabase;
+import android.database.sqlite.SQLiteOpenHelper;
+import android.util.Log;
+
+import net.micode.notes.data.Notes.DataColumns;
+import net.micode.notes.data.Notes.DataConstants;
+import net.micode.notes.data.Notes.NoteColumns;
+
+
+public class NotesDatabaseHelper extends SQLiteOpenHelper {
+// 数据库帮助类,用于管理名为 note.db 的 SQLite 数据库。
+// 它继承自 SQLiteOpenHelper 类,这是 Android提供的一个方便的工具类,用于管理数据库的创建和版本更新.
+ // 数据库的基本信息;数据库名称和版本信息(在创建实例对象时会用到)
+ private static final String DB_NAME = "note.db";
+
+ private static final int DB_VERSION = 4;
+
+ //内部接口:个人理解为两个表名,一个note,一个data
+ 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语句,辅助我们来对数据库进行操作 */
+ //创建note表的语句,这里的NoteColumns就是我们刚刚在Notes中定义的一个接口,里面定义了一系列静态的数据库表中的列名
+ 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" +
+ ")";
+
+ //同上,创建data表的语句,这里的DataColumns就是我们刚刚在Notes中定义的一个接口,里面定义了一系列静态的数据库表中的列名
+ 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.CONTENT + " TEXT NOT NULL DEFAULT ''," +
+ DataColumns.DATA1 + " INTEGER," +
+ DataColumns.DATA2 + " INTEGER," +
+ DataColumns.DATA3 + " TEXT NOT NULL DEFAULT ''," +
+ DataColumns.DATA4 + " TEXT NOT NULL DEFAULT ''," +
+ DataColumns.DATA5 + " TEXT NOT NULL DEFAULT ''" +
+ ")";
+
+ // 功能简介:
+ // 创建一个以note的ID为索引
+ // 解读:
+ // 用于在TABLE.DATA表上创建一个名为note_id_index的索引。
+ // 这个索引是基于DataColumns.NOTE_ID列的。IF NOT EXISTS确保了如果索引已经存在,那么就不会尝试重新创建它,避免了可能的错误。
+ // 索引通常用于提高查询性能,特别是在对某个字段进行频繁查询时。
+ private static final String CREATE_DATA_NOTE_ID_INDEX_SQL =
+ "CREATE INDEX IF NOT EXISTS note_id_index ON " +
+ TABLE.DATA + "(" + DataColumns.NOTE_ID + ");";
+
+ /* 以下是一些对便签增删改定义的触发器 */
+ /* 总结
+ * 这些触发器都是用来维护NOTE表和与之相关联的DATA表之间数据一致性的。
+ * 当在NOTE表中发生删除或更新操作时,这些触发器会自动执行相应的数据清理或更新操作,确保数据库中的数据保持正确和一致。
+ * 特别是在处理文件夹和回收站等逻辑时,这些触发器起到了非常重要的作用,可以自动管理数据的移动和删除。*/
+ /**
+ * Increase folder's note count when move note to the folder
+ */
+ // 功能简介:
+ // 添加触发器:增加文件夹的便签个数记录(因为我们会移动便签进入文件夹,这时候文件夹的计数要进行更新)
+ // 解读:
+ // 定义了一个SQL触发器increase_folder_count_on_update。
+ // 触发器是一种特殊的存储过程,它会在指定表上的指定事件(如INSERT、UPDATE、DELETE)发生时自动执行。
+ // 这个触发器会在TABLE.NOTE表的NoteColumns.PARENT_ID字段更新后执行。
+ // 触发器的逻辑是:当某个笔记的PARENT_ID(即父文件夹ID)被更新时,它会找到对应的文件夹(通过新的PARENT_ID),并将该文件夹的NOTES_COUNT(即笔记数)增加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 +
+ " BEGIN " +
+ " UPDATE " + TABLE.NOTE +
+ " SET " + NoteColumns.NOTES_COUNT + "=" + NoteColumns.NOTES_COUNT + " + 1" +
+ " WHERE " + NoteColumns.ID + "=new." + NoteColumns.PARENT_ID + ";" +
+ " END";
+
+ /**
+ * 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 +
+ " BEGIN " +
+ " UPDATE " + TABLE.NOTE +
+ " SET " + NoteColumns.NOTES_COUNT + "=" + NoteColumns.NOTES_COUNT + "-1" +
+ " WHERE " + NoteColumns.ID + "=old." + NoteColumns.PARENT_ID +
+ " AND " + NoteColumns.NOTES_COUNT + ">0" + ";" +
+ " END";
+
+ /**
+ * 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 +
+ " BEGIN " +
+ " UPDATE " + TABLE.NOTE +
+ " SET " + NoteColumns.NOTES_COUNT + "=" + NoteColumns.NOTES_COUNT + " + 1" +
+ " WHERE " + NoteColumns.ID + "=new." + NoteColumns.PARENT_ID + ";" +
+ " END";
+
+ /**
+ * Decrease folder's note count when delete note from the folder
+ */
+ // 功能简介:(触发器原理和上面的 “增加文件夹的便签个数记录” 同理,就不细节解读了)
+ // 添加触发器:当我们在文件夹删除便签时,减少文件夹的便签个数记录
+ private static final String NOTE_DECREASE_FOLDER_COUNT_ON_DELETE_TRIGGER =
+ "CREATE TRIGGER decrease_folder_count_on_delete " +
+ " AFTER DELETE ON " + TABLE.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;" +
+ " END";
+
+ /**
+ * Update note's content when insert data with type {@link DataConstants#NOTE}
+ */
+ // 功能简介:
+ // 添加触发器:当向DATA表中插入类型为NOTE(便签)的数据时,更新note表对应的笔记内容。
+ // 解读:
+ // 在DATA表上进行INSERT操作后,如果新插入的数据的MIME_TYPE为NOTE,则触发此操作。
+ // 它会更新NOTE表,将与新插入数据相关联的标签的SNIPPET(摘要)字段设置为新插入数据的CONTENT字段的值
+ 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 + "'" +
+ " BEGIN" +
+ " UPDATE " + TABLE.NOTE +
+ " SET " + NoteColumns.SNIPPET + "=new." + DataColumns.CONTENT +
+ " WHERE " + NoteColumns.ID + "=new." + DataColumns.NOTE_ID + ";" +
+ " END";
+
+ /**
+ * Update note's content when data with {@link DataConstants#NOTE} type has changed
+ */
+ // 功能简介:
+ // 添加触发器:当DATA表中,类型为NOTE(便签)的数据更改时,更新note表对应的笔记内容。
+ // 解读:
+ // 在DATA表上进行UPDATE操作后,如果更新前的数据的MIME_TYPE为NOTE,则触发此操作。
+ // 它会更新NOTE表,将与更新后的数据相关联的笔记的SNIPPET字段设置为新数据的CONTENT字段的值
+ private static final String DATA_UPDATE_NOTE_CONTENT_ON_UPDATE_TRIGGER =
+ "CREATE TRIGGER update_note_content_on_update " +
+ " AFTER UPDATE ON " + TABLE.DATA +
+ " WHEN old." + DataColumns.MIME_TYPE + "='" + DataConstants.NOTE + "'" +
+ " BEGIN" +
+ " UPDATE " + TABLE.NOTE +
+ " SET " + NoteColumns.SNIPPET + "=new." + DataColumns.CONTENT +
+ " WHERE " + NoteColumns.ID + "=new." + DataColumns.NOTE_ID + ";" +
+ " END";
+
+ /**
+ * Update note's content when data with {@link DataConstants#NOTE} type has deleted
+ */
+ // 功能简介:
+ // 添加触发器:当DATA表中,类型为NOTE(便签)的数据删除时,更新note表对应的笔记内容(置空)。
+ // 解读:
+ // 在DATA表上进行DELETE操作后,如果删除的数据的MIME_TYPE为NOTE,则触发此操作。
+ // 它会更新NOTE表,将与删除的数据相关联的笔记的SNIPPET字段设置为空字符串。
+ private static final String DATA_UPDATE_NOTE_CONTENT_ON_DELETE_TRIGGER =
+ "CREATE TRIGGER update_note_content_on_delete " +
+ " AFTER delete ON " + TABLE.DATA +
+ " WHEN old." + DataColumns.MIME_TYPE + "='" + DataConstants.NOTE + "'" +
+ " BEGIN" +
+ " UPDATE " + TABLE.NOTE +
+ " SET " + NoteColumns.SNIPPET + "=''" +
+ " WHERE " + NoteColumns.ID + "=old." + DataColumns.NOTE_ID + ";" +
+ " END";
+
+ /**
+ * Delete datas belong to note which has been deleted
+ */
+ // 功能简介:
+ // 添加触发器:当从NOTE表中删除笔记时,删除与该笔记相关联的数据(就是删除data表中为该note的数据)
+ // 解读:
+ // 在NOTE表上进行DELETE操作后,此触发器被激活。
+ // 它会从DATA表中删除所有与已删除的笔记(由old.ID表示)相关联的数据行(通过比较DATA表中的NOTE_ID字段与已删除笔记的ID来实现)
+ private static final String NOTE_DELETE_DATA_ON_DELETE_TRIGGER =
+ "CREATE TRIGGER delete_data_on_delete " +
+ " AFTER DELETE ON " + TABLE.NOTE +
+ " BEGIN" +
+ " DELETE FROM " + TABLE.DATA +
+ " WHERE " + DataColumns.NOTE_ID + "=old." + NoteColumns.ID + ";" +
+ " END";
+
+ /**
+ * Delete notes belong to folder which has been deleted
+ */
+ // 功能简介:
+ // 添加触发器:当从NOTE表中删除一个文件夹时,删除该文件夹下的所有笔记。
+ // 解读:
+ // 在NOTE表上进行DELETE操作后,如果删除的是一个文件夹(由old.ID表示)
+ // 触发器会删除所有以该文件夹为父级(PARENT_ID)的笔记(通过比较NOTE表中的PARENT_ID字段与已删除文件夹的ID来实现)
+ private static final String FOLDER_DELETE_NOTES_ON_DELETE_TRIGGER =
+ "CREATE TRIGGER folder_delete_notes_on_delete " +
+ " AFTER DELETE ON " + TABLE.NOTE +
+ " BEGIN" +
+ " DELETE FROM " + TABLE.NOTE +
+ " WHERE " + NoteColumns.PARENT_ID + "=old." + NoteColumns.ID + ";" +
+ " END";
+
+ /**
+ * Move notes belong to folder which has been moved to trash folder
+ */
+ // 功能简介:
+ // 添加触发器:当某个文件夹被移动到回收站时,移动该文件夹下的所有笔记到回收站
+ // 解读:
+ // 在NOTE表上进行UPDATE操作后,如果某个文件夹的新PARENT_ID字段值等于回收站的ID(Notes.ID_TRASH_FOLER)
+ // 触发器会更新所有以该文件夹为父级(PARENT_ID)的笔记,将它们也移动到回收站。
+ private static final String FOLDER_MOVE_NOTES_ON_TRASH_TRIGGER =
+ "CREATE TRIGGER folder_move_notes_on_trash " +
+ " AFTER UPDATE ON " + TABLE.NOTE +
+ " WHEN new." + NoteColumns.PARENT_ID + "=" + Notes.ID_TRASH_FOLER +
+ " BEGIN" +
+ " UPDATE " + TABLE.NOTE +
+ " SET " + NoteColumns.PARENT_ID + "=" + Notes.ID_TRASH_FOLER +
+ " WHERE " + NoteColumns.PARENT_ID + "=old." + NoteColumns.ID + ";" +
+ " END";
+
+ // 构造器
+ public NotesDatabaseHelper(Context context) {
+ super(context, DB_NAME, null, DB_VERSION);
+ }
+
+ // 创建note(标签)表
+ public void createNoteTable(SQLiteDatabase db) {
+ db.execSQL(CREATE_NOTE_TABLE_SQL);
+ reCreateNoteTableTriggers(db);
+ createSystemFolder(db);
+ Log.d(TAG, "note table has been created");
+ }
+
+ // 重新创建或更新与笔记表相关的触发器。
+ // 首先,使用DROP TRIGGER IF EXISTS语句删除已存在的触发器。确保在重新创建触发器之前,不存在同名的触发器。
+ // 然后,使用db.execSQL()方法执行预定义的SQL语句,这些语句用于创建新的触发器。
+ 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");
+ db.execSQL("DROP TRIGGER IF EXISTS delete_data_on_delete");
+ db.execSQL("DROP TRIGGER IF EXISTS increase_folder_count_on_insert");
+ 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);
+ db.execSQL(NOTE_DELETE_DATA_ON_DELETE_TRIGGER);
+ db.execSQL(NOTE_INCREASE_FOLDER_COUNT_ON_INSERT_TRIGGER);
+ db.execSQL(FOLDER_DELETE_NOTES_ON_DELETE_TRIGGER);
+ db.execSQL(FOLDER_MOVE_NOTES_ON_TRASH_TRIGGER);
+ }
+
+ /* 以下部分是操作SQLite数据库部分 */
+ // 功能简介:
+ // 创建通话记录文件夹、默认文件夹、临时文件夹和回收站,并插入相关数据
+ // 具体解读:
+ // ContentValues是一个用于存储键值对的类,常用于SQLite数据库的插入操作
+ // values.put方法可以向ContentValues对象中添加数据。
+ // NoteColumns.ID是存储文件夹ID的列名,Notes.ID_CALL_RECORD_FOLDER是通话记录文件夹的ID。
+ // NoteColumns.TYPE是存储文件夹类型的列名,Notes.TYPE_SYSTEM表示这是一个系统文件夹。
+ // 使用db.insert方法将values中的数据插入到TABLE.NOTE(即标签表)中。
+ // 每次插入新数据前,都使用values.clear()方法清除ContentValues对象中的旧数据,确保不会重复插入旧数据。
+ // 然后分别创建默认文件夹、临时文件夹和回收站,并以同样的方法插入数据。
+ private void createSystemFolder(SQLiteDatabase db) {
+ ContentValues values = new ContentValues();
+
+ /**
+ * call record foler 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);
+
+ /**
+ * root folder which is default folder
+ */
+ // 创建默认文件夹:重复上述步骤,但这次是为根文件夹插入数据。
+ values.clear();
+ values.put(NoteColumns.ID, Notes.ID_ROOT_FOLDER);
+ values.put(NoteColumns.TYPE, Notes.TYPE_SYSTEM);
+ db.insert(TABLE.NOTE, null, values);
+
+ /**
+ * 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);
+ db.insert(TABLE.NOTE, null, values);
+
+ /**
+ * 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);
+ }
+
+ //功能简介:
+ //创建data(数据)表
+ //解读:
+ //这个方法用于创建数据表,以及与之相关的触发器。
+ //创建数据表:使用db.execSQL方法执行预定义的SQL语句CREATE_DATA_TABLE_SQL,用于创建数据表。
+ //重新创建数据表触发器:调用reCreateDataTableTriggers方法,用于删除并重新创建与数据表相关的触发器。
+ //创建索引:使用db.execSQL方法执行CREATE_DATA_NOTE_ID_INDEX_SQL语句,为数据表创建索引。
+ //记录日志:使用Log.d方法记录一条调试级别的日志,表示数据表已经创建。
+ public void createDataTable(SQLiteDatabase db) {
+ db.execSQL(CREATE_DATA_TABLE_SQL);
+ reCreateDataTableTriggers(db);
+ db.execSQL(CREATE_DATA_NOTE_ID_INDEX_SQL);
+ Log.d(TAG, "data table has been created");
+ }
+
+ //和上面的note表的reCreate...同理
+ //重新创建或更新与笔记表相关的触发器。
+ //首先,使用DROP TRIGGER IF EXISTS语句删除已存在的触发器。确保在重新创建触发器之前,不存在同名的触发器。
+ //然后,使用db.execSQL()方法执行预定义的SQL语句,这些语句用于创建新的触发器。
+ 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);
+ }
+
+ //解读:
+ //synchronized关键字确保在多线程环境下,只有一个线程能够进入这个方法,防止了同时创建多个实例的情况
+ //getInstance(Context context)方法使用了单例模式来确保整个应用程序中只有一个NotesDatabaseHelper实例。
+ //它首先检查mInstance(类的静态成员变量,没有在代码片段中显示)是否为null。
+ //如果是null,则创建一个新的NotesDatabaseHelper实例,并将其赋值给mInstance。最后返回mInstance。
+ static synchronized NotesDatabaseHelper getInstance(Context context) {
+ if (mInstance == null) {
+ mInstance = new NotesDatabaseHelper(context);
+ }
+ return mInstance;
+ }
+
+ //功能简介:
+ //当数据库首次创建时,onCreate方法会被调用。
+ //这里重写onCreate方法,它调用了上述createNoteTable(db)和createDataTable(db)两个方法
+ //这样首次创建数据库时就多出了两张表。
+ @Override
+ public void onCreate(SQLiteDatabase db) {
+ createNoteTable(db);
+ createDataTable(db);
+ }
+
+ //功能简介:
+ //当数据库需要升级时(即数据库的版本号改变),onUpgrade方法会被调用。
+ //该方法会根据当前的oldVersion和新的newVersion来执行相应的升级操作
+ @Override
+ public void onUpgrade(SQLiteDatabase db, int oldVersion, int newVersion) {
+ boolean reCreateTriggers = false;
+ boolean skipV2 = false;
+
+ if (oldVersion == 1) {
+ upgradeToV2(db);
+ skipV2 = true; // this upgrade including the upgrade from v2 to v3
+ oldVersion++;
+ }
+
+ if (oldVersion == 2 && !skipV2) {
+ upgradeToV3(db);
+ reCreateTriggers = true;
+ oldVersion++;
+ }
+
+ 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");
+ }
+ }
+
+ //功能简介:
+ // 将数据库从版本1升级到版本2。
+ //解读:
+ // 首先,它删除了已经存在的NOTE和DATA表(如果存在的话)。DROP TABLE IF EXISTS语句确保了即使这些表不存在,也不会抛出错误。
+ // 然后,它调用了createNoteTable(db)和createDataTable(db)方法来重新创建这两个表。这意味着在升级到版本2时,这两个表的内容会被完全清除,并重新创建新的空表。
+ 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);
+ }
+
+ //功能简介:
+ // 将数据库从版本2(或可能是跳过版本2的某个状态)升级到版本3。
+ //解读:
+ // 首先,删除了三个不再使用的触发器(如果存在的话)。触发器是数据库中的一种对象,可以在插入、更新或删除记录时自动执行某些操作。
+ // 然后,使用ALTER TABLE语句修改表结构,向NOTE表中添加了一个名为GTASK_ID的新列,并设置默认值为空字符串。
+ // 最后,向NOTE表中插入了一条新的系统文件夹记录,表示一个名为“trash folder”的系统文件夹。这可能是用于存储已删除笔记的回收站功能。
+ 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
+ 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);
+ }
+
+ //功能简介:
+ // 这个方法负责将数据库从版本3升级到版本4。
+ //解读:
+ // 它向NOTE表中添加了一个名为VERSION的新列,并设置了默认值为0。这个新列用于记录标签版本信息。
+ private void upgradeToV4(SQLiteDatabase db) {
+ db.execSQL("ALTER TABLE " + TABLE.NOTE + " ADD COLUMN " + NoteColumns.VERSION
+ + " INTEGER NOT NULL DEFAULT 0");
+ }
+}
\ No newline at end of file
From f394794af307cd25d1bff3f9391f685785478876 Mon Sep 17 00:00:00 2001
From: pkagifq93 <2740594397@qq.com>
Date: Mon, 16 Dec 2024 23:01:49 +0800
Subject: [PATCH 02/13] ADD file via upload
---
src%2FNotes-master/zhushi2.txt | 302 +++++++++++++++++++++++++++++++++
1 file changed, 302 insertions(+)
create mode 100644 src%2FNotes-master/zhushi2.txt
diff --git a/src%2FNotes-master/zhushi2.txt b/src%2FNotes-master/zhushi2.txt
new file mode 100644
index 0000000..e6b5b4a
--- /dev/null
+++ b/src%2FNotes-master/zhushi2.txt
@@ -0,0 +1,302 @@
+package net.micode.notes.data;
+
+import android.net.Uri;
+public class Notes {
+// 用于表示笔记应用中的各种类型、标识符以及Intent的额外数据
+ public static final String AUTHORITY = "micode_notes";
+ public static final String TAG = "Notes";
+
+ //对NoteColumns.TYPE的值进行设置时使用:
+ //即不同种类:笔记、文件夹和系统文件夹
+ public static final int TYPE_NOTE = 0;
+ public static final int TYPE_FOLDER = 1;
+ public static final int TYPE_SYSTEM = 2;
+
+ /**
+ * Following IDs are system folders' identifiers
+ * {@link Notes#ID_ROOT_FOLDER } is default folder
+ * {@link Notes#ID_TEMPARAY_FOLDER } is for notes belonging no folder
+ * {@link Notes#ID_CALL_RECORD_FOLDER} is to store call records
+ */
+ //以下id是系统文件夹的标识符(即系统文件夹的分类)
+ //ID_ROOT_FOLDER:默认文件夹
+ //ID_TEMPARAY_FOLDER:不属于文件夹的笔记
+ //ID_CALL_RECORD_FOLDER:用于存储通话记录,以便返回
+ //ID_TRASH_FOLER:垃圾回收站
+ 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;
+
+
+ // 额外的数据键,个人理解为就是定义一些布局的ID
+ // 这部分就是用于设置UI界面的一些布局或小组件的id,给它定义成常量了。
+ // (这样的封装性可能比较好?因为如果有部分要修改,则直接来这边修改即可,不用在activity部分一个一个修改。)
+ public static final String INTENT_EXTRA_ALERT_DATE = "net.micode.notes.alert_date";
+ public static final String INTENT_EXTRA_BACKGROUND_ID = "net.micode.notes.background_color_id";
+ public static final String INTENT_EXTRA_WIDGET_ID = "net.micode.notes.widget_id";
+ public static final String INTENT_EXTRA_WIDGET_TYPE = "net.micode.notes.widget_type";
+ public static final String INTENT_EXTRA_FOLDER_ID = "net.micode.notes.folder_id";
+ public static final String INTENT_EXTRA_CALL_DATE = "net.micode.notes.call_date";
+
+ public static final int TYPE_WIDGET_INVALIDE = -1;
+ public static final int TYPE_WIDGET_2X = 0;
+ public static final int TYPE_WIDGET_4X = 1;
+
+ // 数据常量:里面定义了两种类型:文本便签和通话记录
+ public static class DataConstants {
+ public static final String NOTE = TextNote.CONTENT_ITEM_TYPE;
+ public static final String CALL_NOTE = CallNote.CONTENT_ITEM_TYPE;
+ }
+
+ //下面这些有类似指针的效果? 其实就是定义一堆访问笔记和文件的uri
+ //GPT:Android开发中常见的用于定义内容提供者(Content Provider)URI
+ //内容提供者是一种Android组件,它允许应用程序共享和存储数据。这里定义了一个URI来查询数据
+ /**
+ * Uri to query all notes and folders
+ */
+ public static final Uri CONTENT_NOTE_URI = Uri.parse("content://" + AUTHORITY + "/note");
+
+ /**
+ * Uri to query data
+ */
+ public static final Uri CONTENT_DATA_URI = Uri.parse("content://" + AUTHORITY + "/data");
+
+ public interface NoteColumns {
+ // 雨:这个接口定义了一系列静态的、最终的字符串常量,这些常量代表数据库表中的列名。
+ // 作用:用于后面创建数据库的表头
+ // 总的属性有:ID、父级ID、创建日期、修改日期、提醒日期、文件(标签)名(摘要?)、小部件ID、小部件类型、背景颜色ID、附件、文件中的标签数量、
+ // 文件(标签)类型、最后一个同步ID、本地修改标签、移动前的ID、谷歌任务ID、代码版本信息。
+ // GPT提示:在Android开发中,当使用SQLite数据库时,通常会为表中的每一列定义一个常量,以便在代码中引用。
+ // 这样做的好处是,如果以后需要更改列名,只需要在一个地方修改,而不需要在整个代码中搜索和替换。
+
+ /**
+ * The unique ID for a row
+ *
Type: INTEGER (long)
+ */
+ public static final String ID = "_id";
+
+ /**
+ * The parent's id for note or folder
+ * 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";
+
+
+ /**
+ * Alert date
+ * Type: INTEGER (long)
+ */
+ public static final String ALERTED_DATE = "alert_date";
+
+ /**
+ * Folder's name or text content of note
+ * Type: TEXT
+ */
+ // 摘要?
+ public static final String SNIPPET = "snippet";
+
+ /**
+ * Note's widget 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
+ * Type: INTEGER (long)
+ */
+ public static final String BG_COLOR_ID = "bg_color_id";
+
+ /**
+ * 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
+ * Type: INTEGER (long)
+ */
+ //雨:在数据同步过程中,这个ID可能用来跟踪和识别每次同步操作的唯一性,确保数据的一致性。
+ 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
+ * Type : INTEGER
+ */
+ public static final String ORIGIN_PARENT_ID = "origin_parent_id";
+
+ /**
+ * The gtask 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 {
+ // DataColumns的接口,这个接口包含了一系列静态常量,这些常量代表了数据库表中用于存储数据的列名。
+ // 每个常量都有相应的注释,说明该列的作用和数据类型。
+
+ /**
+ * The unique ID for a row
+ * Type: INTEGER (long)
+ */
+ public static final String ID = "_id";
+
+ /**
+ * The MIME type of the item represented by this row.
+ * Type: Text
+ */
+ //MIME类型是一种标准,用于标识文档、文件或字节流的性质和格式。在数据库中,这个字段可以用来识别不同类型的数据,例如文本、图片、音频或视频等。
+ public static final String MIME_TYPE = "mime_type";
+
+ /**
+ * The reference id to note that this data belongs to
+ * Type: INTEGER (long)
+ */
+ //归属的Note的ID
+ 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";
+
+
+ // 以下5个是通用数据列,它们的具体意义取决于MIME类型(由MIME_TYPE字段指定)。
+ // 不同的MIME类型可能需要存储不同类型的数据,这五个字段提供了灵活性,允许根据MIME类型来存储相应的数据。
+ // 读后面的代码感觉这部分是在表示内容的不同状态?
+ /**
+ * Generic data column, the meaning is {@link #MIMETYPE} 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
+ * integer data type
+ * Type: INTEGER
+ */
+ public static final String DATA2 = "data2";
+
+ /**
+ * Generic data column, the meaning is {@link #MIMETYPE} 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
+ * TEXT data type
+ * Type: TEXT
+ */
+ public static final String DATA4 = "data4";
+
+ /**
+ * Generic data column, the meaning is {@link #MIMETYPE} 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
+ * Type: Integer 1:check list mode 0: normal mode
+ */
+ public static final String MODE = DATA1; //模式?这个被存在DATA1列中
+
+ public static final int MODE_CHECK_LIST = 1; //所处检查列表模式?
+
+ public static final String CONTENT_TYPE = "vnd.android.cursor.dir/text_note"; // 定义了MIME类型,用于标识文本标签的目录
+
+ public static final String CONTENT_ITEM_TYPE = "vnd.android.cursor.item/text_note";// 定义了MIME类型,用于标识文本标签的单个项
+
+ public static final Uri CONTENT_URI = Uri.parse("content://" + AUTHORITY + "/text_note");//文本标签内容提供者(Content Provider)的URI,用于访问文本标签数据
+ }
+
+ // 通话记录的定义?
+ public static final class CallNote implements DataColumns {
+ /**
+ * Call date for this record
+ * Type: INTEGER (long)
+ */
+ public static final String CALL_DATE = DATA1; //一个字符串常量,表示通话记录的日期
+
+ /**
+ * Phone number for this record
+ * Type: TEXT
+ */
+ public static final String PHONE_NUMBER = DATA3; //意味着在数据库表中,这个电话号码信息将被存储在DATA3列中
+
+ public static final String CONTENT_TYPE = "vnd.android.cursor.dir/call_note";// 同样定义了MIME类型,是用于标识通话记录的目录。
+
+ public static final String CONTENT_ITEM_TYPE = "vnd.android.cursor.item/call_note";// 同样定义了MIME类型,是用于标识通话记录的单个项。
+
+ public static final Uri CONTENT_URI = Uri.parse("content://" + AUTHORITY + "/call_note");//定义了通话记录内容提供者的URI,用于访问通话记录数据。
+ }
+}
\ No newline at end of file
From bfee96b1ff5cf51daccc9b80d96b0a2a6e5d6c24 Mon Sep 17 00:00:00 2001
From: pkagifq93 <2740594397@qq.com>
Date: Mon, 16 Dec 2024 23:07:48 +0800
Subject: [PATCH 03/13] ADD file via upload
---
doc/minode.docx | Bin 0 -> 23648 bytes
1 file changed, 0 insertions(+), 0 deletions(-)
create mode 100644 doc/minode.docx
diff --git a/doc/minode.docx b/doc/minode.docx
new file mode 100644
index 0000000000000000000000000000000000000000..6e006c98325890661f8bd68b7db47b75d217e25b
GIT binary patch
literal 23648
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