diff --git a/src/data/Contact.java b/src/data/Contact.java index e49331c..75b2b87 100644 --- a/src/data/Contact.java +++ b/src/data/Contact.java @@ -25,15 +25,69 @@ import android.util.Log; import java.util.HashMap; +<<<<<<< HEAD +<<<<<<< HEAD +<<<<<<< HEAD:app/src/main/java/net/micode/notes/data/Contact.java +public class Contact { + private static HashMap sContactCache;//建立哈希表 + private static final String TAG = "Contact";//设置TAG值为'联系' +======= +public class Contact {//连接类 + private static HashMap sContactCache;//建立哈希表 + private static final String TAG = "Contact";//设置TAG的值为'连接' +>>>>>>> 0444a65d4e44c509f57ad301a5a2b59200d62177:src/data/Contact.java +======= +public class Contact { //连接类 + private static HashMap sContactCache; //定义连接哈希表 + private static final String TAG = "Contact"; //设置TAG的值为'连接' +>>>>>>> b12b9a8449498593360310c554053628b31cc8c9 +======= public class Contact { // 定义连接类 private static HashMap sContactCache; // 定义连接哈希表 private static final String TAG = "Contact"; // 设置TAG的值为'连接' +>>>>>>> 17cc1b00f633e1a6fec3efd17e03a80bcf9060a1 private static final String CALLER_ID_SELECTION = "PHONE_NUMBERS_EQUAL(" + Phone.NUMBER + ",?) AND " + Data.MIMETYPE + "='" + Phone.CONTENT_ITEM_TYPE + "'" + " AND " + Data.RAW_CONTACT_ID + " IN " + "(SELECT raw_contact_id " + " FROM phone_lookup" +<<<<<<< HEAD + + " WHERE min_match = '+')"; //SQL语句 + + public static String getContact(Context context, String phoneNumber) { //获取连接 + if(sContactCache == null) { + sContactCache = new HashMap(); //连接哈希表为空时创建连接哈希表 + } + + if(sContactCache.containsKey(phoneNumber)) { + return sContactCache.get(phoneNumber); //连接哈希表存在电话号码时获取值 + } + + String selection = CALLER_ID_SELECTION.replace("+", + PhoneNumberUtils.toCallerIDMinMatch(phoneNumber)); //将电话号码进行函数处理并代替'+'符号 + Cursor cursor = context.getContentResolver().query( //用于查询的结果集 + Data.CONTENT_URI, //通话记录uri + new String [] { Phone.DISPLAY_NAME }, + selection, + new String[] { phoneNumber }, + null); //查询指定号码相关联系人名字 + + if (cursor != null && cursor.moveToFirst()) { + try { //有结果时 + String name = cursor.getString(0); //获取联系人名字 + sContactCache.put(phoneNumber, name); //缓存电话号码与联系人名字 + return name; + } catch (IndexOutOfBoundsException e) { + 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; //没找到,返回NULL +======= + " WHERE min_match = '+')"; // SQL语句 public static String getContact(Context context, String phoneNumber) { // 获取连接 @@ -69,6 +123,7 @@ public class Contact { // 定义连接类 } else { Log.d(TAG, "No contact matched with number:" + phoneNumber); // 日志记录信息 return null; // 没找到,返回NULL +>>>>>>> 17cc1b00f633e1a6fec3efd17e03a80bcf9060a1 } } } diff --git a/src/data/NotesDatabaseHelper.java b/src/data/NotesDatabaseHelper.java index 1552f98..a29749a 100644 --- a/src/data/NotesDatabaseHelper.java +++ b/src/data/NotesDatabaseHelper.java @@ -26,6 +26,63 @@ import net.micode.notes.data.Notes.DataColumns; import net.micode.notes.data.Notes.DataConstants; import net.micode.notes.data.Notes.NoteColumns; +<<<<<<< HEAD + //引入Notes中接口 +public class NotesDatabaseHelper extends SQLiteOpenHelper { //该类为便签SQL表的定义以及操作,便于进行文件夹与便签的各种操作,例如添加或删除的行为 + 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; //创建NotesDatabaseHelper类对象 + + private static final String CREATE_NOTE_TABLE_SQL = //SQL语句,创建便签表,包含ID、最后修改日期、背景颜色、便签数等在Notes类中的内容 + "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" + + ")"; + + private static final String CREATE_DATA_TABLE_SQL = //创建数据表的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 ''" + + ")"; + + private static final String CREATE_DATA_NOTE_ID_INDEX_SQL = + "CREATE INDEX IF NOT EXISTS note_id_index ON " + + TABLE.DATA + "(" + DataColumns.NOTE_ID + ");"; //创建查询操作表索引,用于查找 +======= //引入Notes中接口 public class NotesDatabaseHelper extends SQLiteOpenHelper { // 该类为便签SQL表的定义以及操作,易于进行文件夹与便签的各种操作,例如添加或删除的行为 private static final String DB_NAME = "note.db"; // 数据库名称 @@ -80,10 +137,21 @@ public class NotesDatabaseHelper extends SQLiteOpenHelper { // 该类为便签SQ private static final String CREATE_DATA_NOTE_ID_INDEX_SQL = "CREATE INDEX IF NOT EXISTS note_id_index ON " + TABLE.DATA + "(" + DataColumns.NOTE_ID + ");"; // 创建查询操作表索引,用于查找 +>>>>>>> 17cc1b00f633e1a6fec3efd17e03a80bcf9060a1 /** * Increase folder's note count when move note to the folder */ +<<<<<<< HEAD + 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"; //创建触发器,方便进行操作,当移动便签时统计新的父文件夹内便签数量 +======= 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 + @@ -92,10 +160,22 @@ public class NotesDatabaseHelper extends SQLiteOpenHelper { // 该类为便签SQ " SET " + NoteColumns.NOTES_COUNT + "=" + NoteColumns.NOTES_COUNT + " + 1" + " WHERE " + NoteColumns.ID + "=new." + NoteColumns.PARENT_ID + ";" + " END"; // 创建触发器,方便进行操作,当移动便签时统计新的父文件夹内便签数量 +>>>>>>> 17cc1b00f633e1a6fec3efd17e03a80bcf9060a1 /** * Decrease folder's note count when move note from folder */ +<<<<<<< HEAD + 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"; //与上一个方法作用相反,统计旧的父文件夹便签数量 +======= 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 + @@ -105,10 +185,21 @@ public class NotesDatabaseHelper extends SQLiteOpenHelper { // 该类为便签SQ " WHERE " + NoteColumns.ID + "=old." + NoteColumns.PARENT_ID + " AND " + NoteColumns.NOTES_COUNT + ">0" + ";" + " END"; // 与上一个方法作用相反,统计旧的父文件夹便签数量 +>>>>>>> 17cc1b00f633e1a6fec3efd17e03a80bcf9060a1 /** * Increase folder's note count when insert new note to the folder */ +<<<<<<< HEAD + 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"; //创建便签时增加统计数量 +======= private static final String NOTE_INCREASE_FOLDER_COUNT_ON_INSERT_TRIGGER = "CREATE TRIGGER increase_folder_count_on_insert " + " AFTER INSERT ON " + TABLE.NOTE + @@ -117,10 +208,22 @@ public class NotesDatabaseHelper extends SQLiteOpenHelper { // 该类为便签SQ " SET " + NoteColumns.NOTES_COUNT + "=" + NoteColumns.NOTES_COUNT + " + 1" + " WHERE " + NoteColumns.ID + "=new." + NoteColumns.PARENT_ID + ";" + " END"; // 创建便签时增加统计数量 +>>>>>>> 17cc1b00f633e1a6fec3efd17e03a80bcf9060a1 /** * Decrease folder's note count when delete note from the folder */ +<<<<<<< HEAD + 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"; //删除时减少数量 +======= private static final String NOTE_DECREASE_FOLDER_COUNT_ON_DELETE_TRIGGER = "CREATE TRIGGER decrease_folder_count_on_delete " + " AFTER DELETE ON " + TABLE.NOTE + @@ -130,10 +233,22 @@ public class NotesDatabaseHelper extends SQLiteOpenHelper { // 该类为便签SQ " WHERE " + NoteColumns.ID + "=old." + NoteColumns.PARENT_ID + " AND " + NoteColumns.NOTES_COUNT + ">0;" + " END"; // 删除时减少数量 +>>>>>>> 17cc1b00f633e1a6fec3efd17e03a80bcf9060a1 /** * Update note's content when insert data with type {@link DataConstants#NOTE} */ +<<<<<<< HEAD + 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"; //更新便签数据时更改table表内容 +======= private static final String DATA_UPDATE_NOTE_CONTENT_ON_INSERT_TRIGGER = "CREATE TRIGGER update_note_content_on_insert " + " AFTER INSERT ON " + TABLE.DATA + @@ -143,11 +258,23 @@ public class NotesDatabaseHelper extends SQLiteOpenHelper { // 该类为便签SQ " SET " + NoteColumns.SNIPPET + "=new." + DataColumns.CONTENT + " WHERE " + NoteColumns.ID + "=new." + DataColumns.NOTE_ID + ";" + " END"; // 更新便签数据时更改table表内容 +>>>>>>> 17cc1b00f633e1a6fec3efd17e03a80bcf9060a1 /** * Update note's content when data with {@link DataConstants#NOTE} type has * changed */ +<<<<<<< HEAD + 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"; //改变便签数据时更改table表内容 +======= private static final String DATA_UPDATE_NOTE_CONTENT_ON_UPDATE_TRIGGER = "CREATE TRIGGER update_note_content_on_update " + " AFTER UPDATE ON " + TABLE.DATA + @@ -157,11 +284,23 @@ public class NotesDatabaseHelper extends SQLiteOpenHelper { // 该类为便签SQ " SET " + NoteColumns.SNIPPET + "=new." + DataColumns.CONTENT + " WHERE " + NoteColumns.ID + "=new." + DataColumns.NOTE_ID + ";" + " END"; // 改变便签数据时更改table表内容 +>>>>>>> 17cc1b00f633e1a6fec3efd17e03a80bcf9060a1 /** * Update note's content when data with {@link DataConstants#NOTE} type has * deleted */ +<<<<<<< HEAD + 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"; //删除便签数据时改变table表内容 +======= private static final String DATA_UPDATE_NOTE_CONTENT_ON_DELETE_TRIGGER = "CREATE TRIGGER update_note_content_on_delete " + " AFTER delete ON " + TABLE.DATA + @@ -171,20 +310,40 @@ public class NotesDatabaseHelper extends SQLiteOpenHelper { // 该类为便签SQ " SET " + NoteColumns.SNIPPET + "=''" + " WHERE " + NoteColumns.ID + "=old." + DataColumns.NOTE_ID + ";" + " END"; // 删除便签数据时改变table表内容 +>>>>>>> 17cc1b00f633e1a6fec3efd17e03a80bcf9060a1 /** * Delete datas belong to note which has been deleted */ +<<<<<<< HEAD + 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"; //删除便签时删除便签内容 +======= 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"; // 删除便签时删除便签内容 +>>>>>>> 17cc1b00f633e1a6fec3efd17e03a80bcf9060a1 /** * Delete notes belong to folder which has been deleted */ +<<<<<<< HEAD + 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"; //删除文件夹时删除内部的便签 +======= private static final String FOLDER_DELETE_NOTES_ON_DELETE_TRIGGER = "CREATE TRIGGER folder_delete_notes_on_delete " + " AFTER DELETE ON " + TABLE.NOTE + @@ -192,10 +351,35 @@ public class NotesDatabaseHelper extends SQLiteOpenHelper { // 该类为便签SQ " DELETE FROM " + TABLE.NOTE + " WHERE " + NoteColumns.PARENT_ID + "=old." + NoteColumns.ID + ";" + " END"; // 删除文件夹时删除内部的便签 +>>>>>>> 17cc1b00f633e1a6fec3efd17e03a80bcf9060a1 /** * Move notes belong to folder which has been moved to trash folder */ +<<<<<<< HEAD + 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); + } //创建一个数据库 + + public void createNoteTable(SQLiteDatabase db) { + db.execSQL(CREATE_NOTE_TABLE_SQL); + reCreateNoteTableTriggers(db); //重新创建触发器 + createSystemFolder(db); //创建系统文件夹 + Log.d(TAG, "note table has been created"); //创建一个新的便签表,返回日志 + } + + private void reCreateNoteTableTriggers(SQLiteDatabase db) { //数据库对象DB +======= 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 + @@ -217,6 +401,7 @@ public class NotesDatabaseHelper extends SQLiteOpenHelper { // 该类为便签SQ } private void reCreateNoteTableTriggers(SQLiteDatabase db) { // 数据库对象DB +>>>>>>> 17cc1b00f633e1a6fec3efd17e03a80bcf9060a1 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"); @@ -232,17 +417,28 @@ public class NotesDatabaseHelper extends SQLiteOpenHelper { // 该类为便签SQ 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); +<<<<<<< HEAD + } //用于执行SQL语句,运行重新创建便签表触发器函数 + + private void createSystemFolder(SQLiteDatabase db) { //创建系统级文件夹 + ContentValues values = new ContentValues(); //为字典类创建对象 +======= } // 用于执行SQL语句,运行重新创建便签表触发器函数 private void createSystemFolder(SQLiteDatabase db) { // 创建系统级文件夹 ContentValues values = new ContentValues(); // 为字典类创建对象 +>>>>>>> 17cc1b00f633e1a6fec3efd17e03a80bcf9060a1 /** * call record foler for call notes */ values.put(NoteColumns.ID, Notes.ID_CALL_RECORD_FOLDER); values.put(NoteColumns.TYPE, Notes.TYPE_SYSTEM); +<<<<<<< HEAD + db.insert(TABLE.NOTE, null, values); //为通讯便签创建记录文件夹,并添加键值对 +======= db.insert(TABLE.NOTE, null, values); // 为通讯便签创建记录文件夹,并添加键值对 +>>>>>>> 17cc1b00f633e1a6fec3efd17e03a80bcf9060a1 /** * root folder which is default folder @@ -250,7 +446,11 @@ public class NotesDatabaseHelper extends SQLiteOpenHelper { // 该类为便签SQ values.clear(); values.put(NoteColumns.ID, Notes.ID_ROOT_FOLDER); values.put(NoteColumns.TYPE, Notes.TYPE_SYSTEM); +<<<<<<< HEAD + db.insert(TABLE.NOTE, null, values); //创建根文件夹,是默认的、最终的父文件夹 +======= db.insert(TABLE.NOTE, null, values); // 创建根文件夹,是默认的、最终的父文件夹 +>>>>>>> 17cc1b00f633e1a6fec3efd17e03a80bcf9060a1 /** * temporary folder which is used for moving note @@ -258,7 +458,11 @@ public class NotesDatabaseHelper extends SQLiteOpenHelper { // 该类为便签SQ values.clear(); values.put(NoteColumns.ID, Notes.ID_TEMPARAY_FOLDER); values.put(NoteColumns.TYPE, Notes.TYPE_SYSTEM); +<<<<<<< HEAD + db.insert(TABLE.NOTE, null, values); //为移动中的便签创建的临时文件夹 +======= db.insert(TABLE.NOTE, null, values); // 为移动中的便签创建的临时文件夹 +>>>>>>> 17cc1b00f633e1a6fec3efd17e03a80bcf9060a1 /** * create trash folder @@ -266,6 +470,26 @@ public class NotesDatabaseHelper extends SQLiteOpenHelper { // 该类为便签SQ values.clear(); values.put(NoteColumns.ID, Notes.ID_TRASH_FOLER); values.put(NoteColumns.TYPE, Notes.TYPE_SYSTEM); +<<<<<<< HEAD + db.insert(TABLE.NOTE, null, values); //创建垃圾桶 + } + + public void createDataTable(SQLiteDatabase db) { //创建数据表 + db.execSQL(CREATE_DATA_TABLE_SQL); //SQL输入保存的语句 + reCreateDataTableTriggers(db); + db.execSQL(CREATE_DATA_NOTE_ID_INDEX_SQL); + Log.d(TAG, "data table has been created"); //日志记录 + } + + private void reCreateDataTableTriggers(SQLiteDatabase db) { //为数据表重新创建触发器 + db.execSQL("DROP TRIGGER IF EXISTS update_note_content_on_insert"); + db.execSQL("DROP TRIGGER IF EXISTS update_note_content_on_update"); + db.execSQL("DROP TRIGGER IF EXISTS update_note_content_on_delete"); //删除触发器 + + db.execSQL(DATA_UPDATE_NOTE_CONTENT_ON_INSERT_TRIGGER); + db.execSQL(DATA_UPDATE_NOTE_CONTENT_ON_UPDATE_TRIGGER); + db.execSQL(DATA_UPDATE_NOTE_CONTENT_ON_DELETE_TRIGGER); //重新创建被删除的触发器 +======= db.insert(TABLE.NOTE, null, values); // 创建垃圾桶 } @@ -284,6 +508,7 @@ public class NotesDatabaseHelper extends SQLiteOpenHelper { // 该类为便签SQ 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); // 重新创建被删除的触发器 +>>>>>>> 17cc1b00f633e1a6fec3efd17e03a80bcf9060a1 } static synchronized NotesDatabaseHelper getInstance(Context context) { @@ -291,6 +516,18 @@ public class NotesDatabaseHelper extends SQLiteOpenHelper { // 该类为便签SQ mInstance = new NotesDatabaseHelper(context); } return mInstance; +<<<<<<< HEAD + } //静态同步方法,用于返回NotesDatabaseHelper的对象实例mInstance + + @Override + public void onCreate(SQLiteDatabase db) { + createNoteTable(db); //创建便签表 + createDataTable(db); //创建数据表 + } //用于初始化表 + + @Override + public void onUpgrade(SQLiteDatabase db, int oldVersion, int newVersion) { //用于检查并升级数据库版本 +======= } // 静态同步方法,用于返回NotesDatabaseHelper的对象实例mInstance @Override @@ -301,6 +538,7 @@ public class NotesDatabaseHelper extends SQLiteOpenHelper { // 该类为便签SQ @Override public void onUpgrade(SQLiteDatabase db, int oldVersion, int newVersion) { // 用于检查并升级数据库版本 +>>>>>>> 17cc1b00f633e1a6fec3efd17e03a80bcf9060a1 boolean reCreateTriggers = false; boolean skipV2 = false; @@ -308,22 +546,46 @@ public class NotesDatabaseHelper extends SQLiteOpenHelper { // 该类为便签SQ upgradeToV2(db); skipV2 = true; // this upgrade including the upgrade from v2 to v3 oldVersion++; +<<<<<<< HEAD + } //检测到版本为1,升级版本 + + if (oldVersion == 2 && !skipV2) { + upgradeToV3(db); + reCreateTriggers = true; //重建触发器标志 + oldVersion++; //检测到版本为2,升级版本 +======= } // 检测到版本为1,升级版本 if (oldVersion == 2 && !skipV2) { upgradeToV3(db); reCreateTriggers = true; // 重建触发器标志 oldVersion++; // 检测到版本为2,升级版本 +>>>>>>> 17cc1b00f633e1a6fec3efd17e03a80bcf9060a1 } if (oldVersion == 3) { upgradeToV4(db); oldVersion++; +<<<<<<< HEAD + } //检测到版本为3,升级版本 +======= } // 检测到版本为3,升级版本 +>>>>>>> 17cc1b00f633e1a6fec3efd17e03a80bcf9060a1 if (reCreateTriggers) { reCreateNoteTableTriggers(db); reCreateDataTableTriggers(db); +<<<<<<< HEAD + } //进行触发器的重建 + + if (oldVersion != newVersion) { + throw new IllegalStateException("Upgrade notes database to version " + newVersion + + "fails"); //版本不匹配则抛出异常 + } + } + + private void upgradeToV2(SQLiteDatabase db) { //升级版本并重建表 +======= } // 进行触发器的重建 if (oldVersion != newVersion) { @@ -333,6 +595,7 @@ public class NotesDatabaseHelper extends SQLiteOpenHelper { // 该类为便签SQ } private void upgradeToV2(SQLiteDatabase db) { // 升级版本并重建表 +>>>>>>> 17cc1b00f633e1a6fec3efd17e03a80bcf9060a1 db.execSQL("DROP TABLE IF EXISTS " + TABLE.NOTE); db.execSQL("DROP TABLE IF EXISTS " + TABLE.DATA); createNoteTable(db); @@ -353,10 +616,18 @@ public class NotesDatabaseHelper extends SQLiteOpenHelper { // 该类为便签SQ values.put(NoteColumns.ID, Notes.ID_TRASH_FOLER); values.put(NoteColumns.TYPE, Notes.TYPE_SYSTEM); db.insert(TABLE.NOTE, null, values); +<<<<<<< HEAD + } //删除不用的触发器,并添加不为空的新列,添加键值对 +======= } // 删除不用的触发器,并添加不为空的新列,添加键值对 +>>>>>>> 17cc1b00f633e1a6fec3efd17e03a80bcf9060a1 private void upgradeToV4(SQLiteDatabase db) { db.execSQL("ALTER TABLE " + TABLE.NOTE + " ADD COLUMN " + NoteColumns.VERSION + " INTEGER NOT NULL DEFAULT 0"); +<<<<<<< HEAD + } //添加新的不为空的列 +======= } // 添加新的不为空的列 +>>>>>>> 17cc1b00f633e1a6fec3efd17e03a80bcf9060a1 } diff --git a/src/data/NotesProvider.java b/src/data/NotesProvider.java index 719d1c8..69f29f6 100644 --- a/src/data/NotesProvider.java +++ b/src/data/NotesProvider.java @@ -36,6 +36,25 @@ import net.micode.notes.data.NotesDatabaseHelper.TABLE; public class NotesProvider extends ContentProvider {// uri操作相关类,用于对数据库进行操作 private static final UriMatcher mMatcher; // UriMatcher类用于分辨数据操作类型,用于匹配不同的uri +<<<<<<< HEAD +public class NotesProvider extends ContentProvider { + private static final UriMatcher mMatcher; //UriMatcher类用于分辨数据表 + + private NotesDatabaseHelper mHelper; //建立NotesDatabaseHelper类对象 + + private static final String TAG = "NotesProvider"; //设置类名值 + + private static final int URI_NOTE = 1; //设置便签uri + private static final int URI_NOTE_ITEM = 2; + private static final int URI_DATA = 3; //设置数据uri + private static final int URI_DATA_ITEM = 4; + + private static final int URI_SEARCH = 5; //设置搜索uri + private static final int URI_SEARCH_SUGGEST = 6; + + static { + mMatcher = new UriMatcher(UriMatcher.NO_MATCH); //进行初始化 +======= private NotesDatabaseHelper mHelper; // 建立NotesDatabaseHelper类对象,用于数据库操作 private static final String TAG = "NotesProvider"; // 设置类名标签,用于调试 @@ -50,6 +69,7 @@ public class NotesProvider extends ContentProvider {// uri操作相关类,用 static { mMatcher = new UriMatcher(UriMatcher.NO_MATCH); // 进行初始化 +>>>>>>> 17cc1b00f633e1a6fec3efd17e03a80bcf9060a1 mMatcher.addURI(Notes.AUTHORITY, "note", URI_NOTE); mMatcher.addURI(Notes.AUTHORITY, "note/#", URI_NOTE_ITEM); mMatcher.addURI(Notes.AUTHORITY, "data", URI_DATA); @@ -57,12 +77,32 @@ public class NotesProvider extends ContentProvider {// uri操作相关类,用 mMatcher.addURI(Notes.AUTHORITY, "search", URI_SEARCH); mMatcher.addURI(Notes.AUTHORITY, SearchManager.SUGGEST_URI_PATH_QUERY, URI_SEARCH_SUGGEST); mMatcher.addURI(Notes.AUTHORITY, SearchManager.SUGGEST_URI_PATH_QUERY + "/*", URI_SEARCH_SUGGEST); +<<<<<<< HEAD + } //将不同的uri与数值相匹配 +======= } // 将不同的uri与数值相匹配 +>>>>>>> 17cc1b00f633e1a6fec3efd17e03a80bcf9060a1 /** * 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. +<<<<<<< HEAD + */ //用0A代表\n,为搜索结果换行 + 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 + "," + + "TRIM(REPLACE(" + NoteColumns.SNIPPET + ", x'0A','')) AS " + SearchManager.SUGGEST_COLUMN_TEXT_2 + "," + + R.drawable.search_result + " AS " + SearchManager.SUGGEST_COLUMN_ICON_1 + "," + + "'" + Intent.ACTION_VIEW + "' AS " + SearchManager.SUGGEST_COLUMN_INTENT_ACTION + "," + + "'" + Notes.TextNote.CONTENT_TYPE + "' AS " + SearchManager.SUGGEST_COLUMN_INTENT_DATA; //... + + private static String NOTES_SNIPPET_SEARCH_QUERY = "SELECT " + NOTES_SEARCH_PROJECTION + + " FROM " + TABLE.NOTE + + " WHERE " + NoteColumns.SNIPPET + " LIKE ?" + + " AND " + NoteColumns.PARENT_ID + "<>" + Notes.ID_TRASH_FOLER + + " AND " + NoteColumns.TYPE + "=" + Notes.TYPE_NOTE; //检索便签的sql语句,在列表中搜寻。使用Notes类中的接口与变量 +======= */ // 用0A代表\n,为搜索结果换行 private static final String NOTES_SEARCH_PROJECTION = NoteColumns.ID + "," + NoteColumns.ID + " AS " + SearchManager.SUGGEST_COLUMN_INTENT_EXTRA_DATA + "," @@ -77,15 +117,30 @@ public class NotesProvider extends ContentProvider {// uri操作相关类,用 + " WHERE " + NoteColumns.SNIPPET + " LIKE ?" + " AND " + NoteColumns.PARENT_ID + "<>" + Notes.ID_TRASH_FOLER + " AND " + NoteColumns.TYPE + "=" + Notes.TYPE_NOTE; // 检索便签的sql语句,在列表中搜寻。使用Notes类中的接口与变量 +>>>>>>> 17cc1b00f633e1a6fec3efd17e03a80bcf9060a1 @Override public boolean onCreate() { mHelper = NotesDatabaseHelper.getInstance(getContext()); +<<<<<<< HEAD + return true; //创建NotesDatabaseHelper类并返回成果标识 +======= return true; // 创建NotesDatabaseHelper类并返回成果标识 +>>>>>>> 17cc1b00f633e1a6fec3efd17e03a80bcf9060a1 } @Override public Cursor query(Uri uri, String[] projection, String selection, String[] selectionArgs, +<<<<<<< HEAD + String sortOrder) { //负责查询数据,接受uri、返回烈、选择、排列方式 + Cursor c = null; //创建空游标,表示数据库操作结果集合 + SQLiteDatabase db = mHelper.getReadableDatabase(); //获取数据库对象 + String id = null; + switch (mMatcher.match(uri)) { //确定uri类型 + case URI_NOTE: + c = db.query(TABLE.NOTE, projection, selection, selectionArgs, null, null, + sortOrder); //传参,进行标准查询 +======= String sortOrder) { // 负责查询数据,接受uri、返回烈、选择、排列方式 Cursor c = null; // 创建空游标,表示数据库操作结果集合 SQLiteDatabase db = mHelper.getReadableDatabase(); // 获取数据库对象 @@ -94,12 +149,17 @@ public class NotesProvider extends ContentProvider {// uri操作相关类,用 case URI_NOTE: c = db.query(TABLE.NOTE, projection, selection, selectionArgs, null, null, sortOrder); // 传参,进行标准查询 +>>>>>>> 17cc1b00f633e1a6fec3efd17e03a80bcf9060a1 break; case URI_NOTE_ITEM: id = uri.getPathSegments().get(1); c = db.query(TABLE.NOTE, projection, NoteColumns.ID + "=" + id + parseSelection(selection), selectionArgs, null, null, sortOrder); +<<<<<<< HEAD + break; //传参,对id进行查询 +======= break; // 传参,对id进行查询 +>>>>>>> 17cc1b00f633e1a6fec3efd17e03a80bcf9060a1 case URI_DATA: c = db.query(TABLE.DATA, projection, selection, selectionArgs, null, null, sortOrder); @@ -111,13 +171,21 @@ public class NotesProvider extends ContentProvider {// uri操作相关类,用 break; case URI_SEARCH: case URI_SEARCH_SUGGEST: +<<<<<<< HEAD + if (sortOrder != null || projection != null) { //检查是否指定排序方式 +======= if (sortOrder != null || projection != null) { // 检查是否指定排序方式 +>>>>>>> 17cc1b00f633e1a6fec3efd17e03a80bcf9060a1 throw new IllegalArgumentException( "do not specify sortOrder, selection, selectionArgs, or projection" + "with this query"); } String searchString = null; +<<<<<<< HEAD + if (mMatcher.match(uri) == URI_SEARCH_SUGGEST) { //提取uri中搜索模式 +======= if (mMatcher.match(uri) == URI_SEARCH_SUGGEST) { // 提取uri中搜索模式 +>>>>>>> 17cc1b00f633e1a6fec3efd17e03a80bcf9060a1 if (uri.getPathSegments().size() > 1) { searchString = uri.getPathSegments().get(1); } @@ -132,6 +200,19 @@ public class NotesProvider extends ContentProvider {// uri操作相关类,用 try { searchString = String.format("%%%s%%", searchString); c = db.rawQuery(NOTES_SNIPPET_SEARCH_QUERY, +<<<<<<< HEAD + new String[] { searchString }); //在Notes表进行搜索 + } catch (IllegalStateException ex) { + Log.e(TAG, "got exception: " + ex.toString()); //日志 + } + break; + default: + throw new IllegalArgumentException("Unknown URI " + uri); //抛出异常 + } + if (c != null) { + c.setNotificationUri(getContext().getContentResolver(), uri); //在游标查询的数据变化时进行通知 + } //通知uri是标识查询结果的uri,setNotificationUri方法可注册通知uri,与对象c相关联 +======= new String[] { searchString }); // 在Notes表进行搜索 } catch (IllegalStateException ex) { Log.e(TAG, "got exception: " + ex.toString()); // 日志 @@ -143,10 +224,27 @@ public class NotesProvider extends ContentProvider {// uri操作相关类,用 if (c != null) { c.setNotificationUri(getContext().getContentResolver(), uri); // 在游标查询的数据变化时进行通知 } // 通知uri是标识查询结果的uri,setNotificationUri方法可注册通知uri,与对象c相关联 +>>>>>>> 17cc1b00f633e1a6fec3efd17e03a80bcf9060a1 return c; } @Override +<<<<<<< HEAD + public Uri insert(Uri uri, ContentValues values) { //uri表示要插入的uri,values表示要插入的数据 + SQLiteDatabase db = mHelper.getWritableDatabase(); //打开或创建一个数据库 + long dataId = 0, noteId = 0, insertedId = 0; + switch (mMatcher.match(uri)) { //匹配uri + case URI_NOTE: + insertedId = noteId = db.insert(TABLE.NOTE, null, values); //将数据插入到Notes表中 + break; + case URI_DATA: + if (values.containsKey(DataColumns.NOTE_ID)) { //检查values是否含有NOTE_ID内容,返回bool值 + noteId = values.getAsLong(DataColumns.NOTE_ID); //关联noteId + } else { + Log.d(TAG, "Wrong data format without note id:" + values.toString()); //日志记录 + } + insertedId = dataId = db.insert(TABLE.DATA, null, values); //数据库插入新行的id返回到两个变量中 +======= public Uri insert(Uri uri, ContentValues values) { // uri表示要插入的uri,values表示要插入的数据 SQLiteDatabase db = mHelper.getWritableDatabase(); // 打开或创建一个数据库 long dataId = 0, noteId = 0, insertedId = 0; @@ -161,14 +259,21 @@ public class NotesProvider extends ContentProvider {// uri操作相关类,用 Log.d(TAG, "Wrong data format without note id:" + values.toString()); // 日志记录 } insertedId = dataId = db.insert(TABLE.DATA, null, values); // 数据库插入新行的id返回到两个变量中 +>>>>>>> 17cc1b00f633e1a6fec3efd17e03a80bcf9060a1 break; default: throw new IllegalArgumentException("Unknown URI " + uri); } // Notify the note uri +<<<<<<< HEAD + if (noteId > 0) { //不为零表示插入成功, + getContext().getContentResolver().notifyChange( + ContentUris.withAppendedId(Notes.CONTENT_NOTE_URI, noteId), null); //通知uri发生变化 +======= if (noteId > 0) { // 不为零表示插入成功, getContext().getContentResolver().notifyChange( ContentUris.withAppendedId(Notes.CONTENT_NOTE_URI, noteId), null); // 通知uri发生变化 +>>>>>>> 17cc1b00f633e1a6fec3efd17e03a80bcf9060a1 } // Notify the data uri @@ -177,6 +282,24 @@ public class NotesProvider extends ContentProvider {// uri操作相关类,用 ContentUris.withAppendedId(Notes.CONTENT_DATA_URI, dataId), null); } +<<<<<<< HEAD + return ContentUris.withAppendedId(uri, insertedId); //返回新的uri,表示插入位置 + } + + @Override + public int delete(Uri uri, String selection, String[] selectionArgs) { //表示要删除的uri、SQL中的WHERE与参数 + int count = 0; //表示删除记录数 + String id = null; + SQLiteDatabase db = mHelper.getWritableDatabase(); //打开数据库 + boolean deleteData = false; + switch (mMatcher.match(uri)) { //uri匹配 + case URI_NOTE: + selection = "(" + selection + ") AND " + NoteColumns.ID + ">0 "; //设置WHERE + count = db.delete(TABLE.NOTE, selection, selectionArgs); //删除,并计数 + break; + case URI_NOTE_ITEM: + id = uri.getPathSegments().get(1); //获取id +======= return ContentUris.withAppendedId(uri, insertedId); // 返回新的uri,表示插入位置 } @@ -194,6 +317,7 @@ public class NotesProvider extends ContentProvider {// uri操作相关类,用 break; case URI_NOTE_ITEM: id = uri.getPathSegments().get(1); // 获取id +>>>>>>> 17cc1b00f633e1a6fec3efd17e03a80bcf9060a1 /** * ID that smaller than 0 is system folder which is not allowed to * trash @@ -204,6 +328,23 @@ public class NotesProvider extends ContentProvider {// uri操作相关类,用 } count = db.delete(TABLE.NOTE, NoteColumns.ID + "=" + id + parseSelection(selection), selectionArgs); +<<<<<<< HEAD + break; //删除指定id,并计数 + case URI_DATA: + count = db.delete(TABLE.DATA, selection, selectionArgs); //删除,并计数 + deleteData = true; + break; + case URI_DATA_ITEM: + id = uri.getPathSegments().get(1); //获取id + count = db.delete(TABLE.DATA, + DataColumns.ID + "=" + id + parseSelection(selection), selectionArgs); + deleteData = true; //删除指定id,并计数 + break; + default: + throw new IllegalArgumentException("Unknown URI " + uri); //报错 + } + if (count > 0) { //表示有删除操作,通知uri发生变化 +======= break; // 删除指定id,并计数 case URI_DATA: count = db.delete(TABLE.DATA, selection, selectionArgs); // 删除,并计数 @@ -220,11 +361,33 @@ public class NotesProvider extends ContentProvider {// uri操作相关类,用 } if (count > 0) { // 表示有删除操作,通知uri发生变化 +>>>>>>> 17cc1b00f633e1a6fec3efd17e03a80bcf9060a1 if (deleteData) { getContext().getContentResolver().notifyChange(Notes.CONTENT_NOTE_URI, null); } getContext().getContentResolver().notifyChange(uri, null); } +<<<<<<< HEAD + return count; //返回删除的记录数 + } + + @Override + public int update(Uri uri, ContentValues values, String selection, String[] selectionArgs) { //表示更新的uri、values为新的列值、where以及参数 + int count = 0; //更新条数 + String id = null; + SQLiteDatabase db = mHelper.getWritableDatabase(); //打开数据库 + boolean updateData = false; //设置数据表更新表示 + switch (mMatcher.match(uri)) { //匹配uri + case URI_NOTE: + increaseNoteVersion(-1, selection, selectionArgs); //跟新便签 + count = db.update(TABLE.NOTE, values, selection, selectionArgs); //更新指定表的行,并计数 + break; + case URI_NOTE_ITEM: + id = uri.getPathSegments().get(1); //提取id + increaseNoteVersion(Long.valueOf(id), selection, selectionArgs); + count = db.update(TABLE.NOTE, values, NoteColumns.ID + "=" + id + + parseSelection(selection), selectionArgs); //增加指定的id条件 +======= return count; // 返回删除的记录数 } @@ -244,6 +407,7 @@ public class NotesProvider extends ContentProvider {// uri操作相关类,用 increaseNoteVersion(Long.valueOf(id), selection, selectionArgs); count = db.update(TABLE.NOTE, values, NoteColumns.ID + "=" + id + parseSelection(selection), selectionArgs); // 增加指定的id条件 +>>>>>>> 17cc1b00f633e1a6fec3efd17e03a80bcf9060a1 break; case URI_DATA: count = db.update(TABLE.DATA, values, selection, selectionArgs); @@ -256,50 +420,89 @@ public class NotesProvider extends ContentProvider {// uri操作相关类,用 updateData = true; break; default: +<<<<<<< HEAD + throw new IllegalArgumentException("Unknown URI " + uri); //抛出异常 +======= throw new IllegalArgumentException("Unknown URI " + uri); // 抛出异常 +>>>>>>> 17cc1b00f633e1a6fec3efd17e03a80bcf9060a1 } if (count > 0) { if (updateData) { +<<<<<<< HEAD + getContext().getContentResolver().notifyChange(Notes.CONTENT_NOTE_URI, null);//通知指定uri数据更改 + } + getContext().getContentResolver().notifyChange(uri, null); //通知uri数据更改 +======= getContext().getContentResolver().notifyChange(Notes.CONTENT_NOTE_URI, null);// 通知指定uri数据更改 } getContext().getContentResolver().notifyChange(uri, null); // 通知uri数据更改 +>>>>>>> 17cc1b00f633e1a6fec3efd17e03a80bcf9060a1 } return count; } +<<<<<<< HEAD + private String parseSelection(String selection) { //检查selection是否为空 + return (!TextUtils.isEmpty(selection) ? " AND (" + selection + ')' : ""); //为空则返回“” + } + + private void increaseNoteVersion(long id, String selection, String[] selectionArgs) { //使用数据库更新便签版本 +======= private String parseSelection(String selection) { // 检查selection是否为空 return (!TextUtils.isEmpty(selection) ? " AND (" + selection + ')' : ""); // 为空则返回“” } private void increaseNoteVersion(long id, String selection, String[] selectionArgs) { // 使用数据库更新便签版本 +>>>>>>> 17cc1b00f633e1a6fec3efd17e03a80bcf9060a1 StringBuilder sql = new StringBuilder(120); sql.append("UPDATE "); sql.append(TABLE.NOTE); sql.append(" SET "); sql.append(NoteColumns.VERSION); +<<<<<<< HEAD + sql.append("=" + NoteColumns.VERSION + "+1 "); //升级便签版本号 + + if (id > 0 || !TextUtils.isEmpty(selection)) { //selection不为空或存在id则增加where条件 +======= sql.append("=" + NoteColumns.VERSION + "+1 "); // 升级便签版本号 if (id > 0 || !TextUtils.isEmpty(selection)) { // selection不为空或存在id则增加where条件 +>>>>>>> 17cc1b00f633e1a6fec3efd17e03a80bcf9060a1 sql.append(" WHERE "); } if (id > 0) { sql.append(NoteColumns.ID + "=" + String.valueOf(id)); } if (!TextUtils.isEmpty(selection)) { +<<<<<<< HEAD + String selectString = id > 0 ? parseSelection(selection) : selection; //parseSelection将传入的参数中的占位符替换为实际的值 + for (String args : selectionArgs) { // + selectString = selectString.replaceFirst("\\?", args); //replaceFirst选择参数数组中的值替换语句中的占位符 +======= String selectString = id > 0 ? parseSelection(selection) : selection; // parseSelection将传入的参数中的占位符替换为实际的值 for (String args : selectionArgs) { // selectString = selectString.replaceFirst("\\?", args); // replaceFirst选择参数数组中的值替换语句中的占位符 +>>>>>>> 17cc1b00f633e1a6fec3efd17e03a80bcf9060a1 } sql.append(selectString); } +<<<<<<< HEAD + mHelper.getWritableDatabase().execSQL(sql.toString()); //执行SQL语句 + } + + @Override + public String getType(Uri uri) { //获取指定uri的MIME类型 + // TODO Auto-generated method stub //表示该方法没有实现 +======= mHelper.getWritableDatabase().execSQL(sql.toString()); // 执行SQL语句 } @Override public String getType(Uri uri) { // 获取指定uri的MIME类型 // TODO Auto-generated method stub //表示该方法没有实现 +>>>>>>> 17cc1b00f633e1a6fec3efd17e03a80bcf9060a1 return null; } } diff --git a/src/ui/NotesListActivity.java b/src/ui/NotesListActivity.java index e843aec..bda637d 100644 --- a/src/ui/NotesListActivity.java +++ b/src/ui/NotesListActivity.java @@ -135,9 +135,11 @@ public class NotesListActivity extends Activity implements OnClickListener, OnIt private final static int REQUEST_CODE_OPEN_NODE = 102; private final static int REQUEST_CODE_NEW_NODE = 103; + // 在Activity创建时调用 @Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); + // 设置Activity的布局文件为note_list.xml setContentView(R.layout.note_list); initResources(); @@ -147,32 +149,45 @@ public class NotesListActivity extends Activity implements OnClickListener, OnIt setAppInfoFromRawRes(); } + // 在Activity接收到其他Activity返回的结果时调用 @Override protected void onActivityResult(int requestCode, int resultCode, Intent data) { + // 如果结果是成功的,并且请求码是打开或新建笔记,那么更新笔记列表的游标 if (resultCode == RESULT_OK && (requestCode == REQUEST_CODE_OPEN_NODE || requestCode == REQUEST_CODE_NEW_NODE)) { mNotesListAdapter.changeCursor(null); } else { + // 否则,调用父类的方法处理结果 super.onActivityResult(requestCode, resultCode, data); } } + // 从raw资源文件中读取应用的介绍信息,并保存为一个笔记 private void setAppInfoFromRawRes() { + // 获取SharedPreferences对象,用于存储设置信息 SharedPreferences sp = PreferenceManager.getDefaultSharedPreferences(this); + // 如果没有添加过介绍信息,那么执行以下操作 if (!sp.getBoolean(PREFERENCE_ADD_INTRODUCTION, false)) { + // 用于拼接介绍信息的文本 StringBuilder sb = new StringBuilder(); InputStream in = null; try { + // 从raw资源文件中打开介绍信息的输入流 in = getResources().openRawResource(R.raw.introduction); if (in != null) { + // 用于读取输入流 InputStreamReader isr = new InputStreamReader(in); + // 用于缓冲输入流 BufferedReader br = new BufferedReader(isr); + // 用于存储读取到的字符 char [] buf = new char[1024]; int len = 0; + // 循环读取输入流中的字符,直到结束 while ((len = br.read(buf)) > 0) { sb.append(buf, 0, len); } } else { + // 如果输入流为空,那么打印错误日志并返回 Log.e(TAG, "Read introduction file error"); return; } @@ -182,6 +197,7 @@ public class NotesListActivity extends Activity implements OnClickListener, OnIt } finally { if(in != null) { try { + // 关闭输入流 in.close(); } catch (IOException e) { // TODO Auto-generated catch block @@ -190,10 +206,13 @@ public class NotesListActivity extends Activity implements OnClickListener, OnIt } } + // 创建一个空的工作笔记对象,设置其所属文件夹,小部件ID,类型和颜色为默认值 WorkingNote note = WorkingNote.createEmptyNote(this, Notes.ID_ROOT_FOLDER, AppWidgetManager.INVALID_APPWIDGET_ID, Notes.TYPE_WIDGET_INVALIDE, ResourceParser.RED); + // 设置工作笔记的文本为StringBuilder对象中的内容 note.setWorkingText(sb.toString()); + // 保存工作笔记到数据库中 if (note.saveNote()) { sp.edit().putBoolean(PREFERENCE_ADD_INTRODUCTION, true).commit(); } else { @@ -203,43 +222,71 @@ public class NotesListActivity extends Activity implements OnClickListener, OnIt } } + // 在Activity的生命周期中开始时调用 @Override protected void onStart() { super.onStart(); + // 开始异步查询笔记列表 startAsyncNotesListQuery(); } + // 用来初始化一些资源 private void initResources() { + // 获取内容解析器,用来操作数据库 mContentResolver = this.getContentResolver(); + // 创建一个后台查询处理器,用来异步查询数据 mBackgroundQueryHandler = new BackgroundQueryHandler(this.getContentResolver()); + // 设置当前文件夹的ID为根文件夹的ID mCurrentFolderId = Notes.ID_ROOT_FOLDER; + // 获取笔记列表视图,用来显示笔记 mNotesListView = (ListView) findViewById(R.id.notes_list); + // 给笔记列表视图添加一个脚部视图,用来显示更多选项 mNotesListView.addFooterView(LayoutInflater.from(this).inflate(R.layout.note_list_footer, null), null, false); + // 给笔记列表视图设置一个点击监听器,用来响应点击事件 mNotesListView.setOnItemClickListener(new OnListItemClickListener()); + // 给笔记列表视图设置一个长按监听器,用来响应长按事件 mNotesListView.setOnItemLongClickListener(this); + // 创建一个笔记列表适配器,用来绑定数据和视图 mNotesListAdapter = new NotesListAdapter(this); + // 给笔记列表视图设置适配器 mNotesListView.setAdapter(mNotesListAdapter); + // 获取添加新笔记的按钮,用来创建新笔记 mAddNewNote = (Button) findViewById(R.id.btn_new_note); + // 给添加新笔记的按钮设置一个点击监听器,用来响应点击事件 mAddNewNote.setOnClickListener(this); + // 给添加新笔记的按钮设置一个触摸监听器,用来响应触摸事件 mAddNewNote.setOnTouchListener(new NewNoteOnTouchListener()); + // 初始化一个标志位,用来判断是否需要分发触摸事件 mDispatch = false; + // 用来记录分发触摸事件时的Y坐标 mDispatchY = 0; + // 用来记录触摸事件开始时的Y坐标 mOriginY = 0; + // 获取标题栏视图,用来显示标题 mTitleBar = (TextView) findViewById(R.id.tv_title_bar); + // 设置当前的列表编辑状态为普通状态 mState = ListEditState.NOTE_LIST; + // 创建一个模式回调对象,用来处理上下文菜单的操作 mModeCallBack = new ModeCallback(); } + // 用户长按列表视图的某一项时,弹出一个上下文菜单,让用户可以对选中的笔记进行一些操作 private class ModeCallback implements ListView.MultiChoiceModeListener, OnMenuItemClickListener { private DropdownMenu mDropDownMenu; private ActionMode mActionMode; private MenuItem mMoveMenu; - + // 用户长按一个笔记时触发的,用于创建一个操作模式(ActionMode), + // 在这个模式下,用户可以对笔记进行一些操作,比如删除或移动 public boolean onCreateActionMode(ActionMode mode, Menu menu) { + //从资源文件中加载菜单项 getMenuInflater().inflate(R.menu.note_list_options, menu); + //找到删除菜单项,并设置点击监听器 menu.findItem(R.id.delete).setOnMenuItemClickListener(this); + //找到移动菜单项,并保存为一个成员变量 mMoveMenu = menu.findItem(R.id.move); + //判断当前笔记是否属于通话记录文件夹,或者用户没有创建任何自定义文件夹, + //如果是,则隐藏移动菜单项,否则显示移动菜单项,并设置点击监听器 if (mFocusNoteDataItem.getParentId() == Notes.ID_CALL_RECORD_FOLDER || DataUtils.getUserFolderCount(mContentResolver) == 0) { mMoveMenu.setVisible(false); @@ -247,17 +294,26 @@ public class NotesListActivity extends Activity implements OnClickListener, OnIt mMoveMenu.setVisible(true); mMoveMenu.setOnMenuItemClickListener(this); } + //保存操作模式为一个成员变量 mActionMode = mode; + //设置笔记列表适配器的选择模式为true,表示可以多选笔记 mNotesListAdapter.setChoiceMode(true); + //设置笔记列表视图的长按可点击属性为false,表示不再响应长按事件 mNotesListView.setLongClickable(false); + //隐藏添加新笔记的按钮 mAddNewNote.setVisibility(View.GONE); + //从布局文件中加载一个自定义视图,用于显示一个下拉菜单按钮 View customView = LayoutInflater.from(NotesListActivity.this).inflate( R.layout.note_list_dropdown_menu, null); + //将自定义视图设置为操作模式的自定义视图 mode.setCustomView(customView); + //创建一个下拉菜单对象,绑定到自定义视图中的按钮,并从资源文件中加载下拉菜单项 mDropDownMenu = new DropdownMenu(NotesListActivity.this, (Button) customView.findViewById(R.id.selection_menu), R.menu.note_list_dropdown); + //设置下拉菜单的点击监听器,当用户点击下拉菜单项时, + // 根据当前的选择状态,全选或取消全选所有笔记,并更新菜单项的状态 mDropDownMenu.setOnDropdownMenuItemClickListener(new PopupMenu.OnMenuItemClickListener(){ public boolean onMenuItemClick(MenuItem item) { mNotesListAdapter.selectAll(!mNotesListAdapter.isAllSelected()); @@ -266,62 +322,85 @@ public class NotesListActivity extends Activity implements OnClickListener, OnIt } }); + //返回true表示创建操作模式成功 return true; } + // 用于更新菜单项的状态和标题,根据当前选择的笔记的数量和是否全选 private void updateMenu() { + //获取当前选择的笔记的数量 int selectedCount = mNotesListAdapter.getSelectedCount(); // Update dropdown menu String format = getResources().getString(R.string.menu_select_title, selectedCount); mDropDownMenu.setTitle(format); + //找到下拉菜单中的全选菜单项 MenuItem item = mDropDownMenu.findItem(R.id.action_select_all); + //如果当前已经全选了所有笔记,则设置全选菜单项为选中状态,并修改标题为取消全选 if (item != null) { if (mNotesListAdapter.isAllSelected()) { item.setChecked(true); item.setTitle(R.string.menu_deselect_all); } else { + //否则,设置全选菜单项为未选中状态,并修改标题为全选 item.setChecked(false); item.setTitle(R.string.menu_select_all); } } } + // 在操作模式准备显示之前调用,用于修改菜单项的状态或可见性, + // 返回true表示修改了菜单项,返回false表示没有修改 public boolean onPrepareActionMode(ActionMode mode, Menu menu) { // TODO Auto-generated method stub return false; } + // 在用户点击操作模式中的菜单项时调用,用于处理相应的逻辑, + // 返回true表示处理了点击事件,返回false表示没有处理 public boolean onActionItemClicked(ActionMode mode, MenuItem item) { // TODO Auto-generated method stub return false; } + // 在操作模式结束时调用,用于恢复笔记列表的正常状态 public void onDestroyActionMode(ActionMode mode) { + //设置笔记列表适配器的选择模式为false,表示不再可以多选笔记 mNotesListAdapter.setChoiceMode(false); + //设置笔记列表视图的长按可点击属性为true,表示可以响应长按事件 mNotesListView.setLongClickable(true); + //显示添加新笔记的按钮 mAddNewNote.setVisibility(View.VISIBLE); } + // 用于结束操作模式,调用操作模式的finish方法 public void finishActionMode() { mActionMode.finish(); } + // 在用户改变某个笔记的选择状态时调用,用于更新笔记列表适配器和菜单项的状态 public void onItemCheckedStateChanged(ActionMode mode, int position, long id, boolean checked) { + //设置笔记列表适配器中指定位置的笔记的选择状态 mNotesListAdapter.setCheckedItem(position, checked); + //更新菜单项的状态和标题 updateMenu(); } + // 在用户点击操作模式中的菜单项时调用,用于处理相应的逻辑 public boolean onMenuItemClick(MenuItem item) { + //如果当前没有选择任何笔记,则弹出一个提示信息,并返回true表示处理了点击事件 if (mNotesListAdapter.getSelectedCount() == 0) { Toast.makeText(NotesListActivity.this, getString(R.string.menu_select_none), Toast.LENGTH_SHORT).show(); return true; } + //根据菜单项的id,执行不同的操作 switch (item.getItemId()) { case R.id.delete: + //创建一个对话框对象,用于提示用户是否确定删除所选笔记 AlertDialog.Builder builder = new AlertDialog.Builder(NotesListActivity.this); + //设置对话框的标题、图标、消息和按钮 builder.setTitle(getString(R.string.alert_title_delete)); builder.setIcon(android.R.drawable.ic_dialog_alert); builder.setMessage(getString(R.string.alert_message_delete_notes, @@ -330,10 +409,12 @@ public class NotesListActivity extends Activity implements OnClickListener, OnIt new DialogInterface.OnClickListener() { public void onClick(DialogInterface dialog, int which) { + //如果用户点击确定按钮,则调用批量删除的方法 batchDelete(); } }); builder.setNegativeButton(android.R.string.cancel, null); + //显示对话框 builder.show(); break; case R.id.move: @@ -346,19 +427,27 @@ public class NotesListActivity extends Activity implements OnClickListener, OnIt } } + // 用于处理添加新笔记按钮的触摸事件 private class NewNoteOnTouchListener implements OnTouchListener { - + // 用于处理触摸事件,参数v是被触摸的视图,参数event是触摸事件对象 public boolean onTouch(View v, MotionEvent event) { + //根据触摸事件的类型,执行不同的操作 switch (event.getAction()) { case MotionEvent.ACTION_DOWN: { + //获取屏幕的显示对象 Display display = getWindowManager().getDefaultDisplay(); + //获取屏幕的高度 int screenHeight = display.getHeight(); + //获取添加新笔记按钮的高度 int newNoteViewHeight = mAddNewNote.getHeight(); + //计算添加新笔记按钮的起始位置,即屏幕高度减去按钮高度 int start = screenHeight - newNoteViewHeight; + //计算触摸事件的y坐标,即按钮起始位置加上触摸事件相对于按钮的y坐标 int eventY = start + (int) event.getY(); /** * Minus TitleBar's height */ + //如果当前是子文件夹模式,则需要减去标题栏的高度,因为标题栏会遮挡部分按钮 if (mState == ListEditState.SUB_FOLDER) { eventY -= mTitleBar.getHeight(); start -= mTitleBar.getHeight(); @@ -372,43 +461,63 @@ public class NotesListActivity extends Activity implements OnClickListener, OnIt * Notice that, if the background of the button changes, the formula should * also change. This is very bad, just for the UI designer's strong requirement. */ + // 如果触摸事件发生在屏幕的左上角 if (event.getY() < (event.getX() * (-0.12) + 94)) { + // 获取列表视图中最后一个可见的项目 View view = mNotesListView.getChildAt(mNotesListView.getChildCount() - 1 - mNotesListView.getFooterViewsCount()); + // 如果项目不为空,并且它的底部在起始位置下方,它的顶部在起始位置加上94的上方 if (view != null && view.getBottom() > start && (view.getTop() < (start + 94))) { + // 保存触摸事件的原始y坐标 mOriginY = (int) event.getY(); + // 设置分发y坐标为当前y坐标 mDispatchY = eventY; + // 设置触摸事件的位置为分发坐标 event.setLocation(event.getX(), mDispatchY); + // 设置一个标志,表示触摸事件应该被分发给列表视图 mDispatch = true; + // 将触摸事件分发给列表视图,并返回结果 return mNotesListView.dispatchTouchEvent(event); } } break; } + // 如果触摸事件是一个移动动作 case MotionEvent.ACTION_MOVE: { + // 如果标志设置为将触摸事件分发给列表视图 if (mDispatch) { + // 更新分发y坐标,加上当前y坐标和原始y坐标的差值 mDispatchY += (int) event.getY() - mOriginY; + // 设置触摸事件的位置为分发坐标 event.setLocation(event.getX(), mDispatchY); + // 将触摸事件分发给列表视图,并返回结果 return mNotesListView.dispatchTouchEvent(event); } break; } default: { + // 如果标志设置为将触摸事件分发给列表视图 if (mDispatch) { + // 设置触摸事件的位置为分发坐标 event.setLocation(event.getX(), mDispatchY); + // 重置标志为false mDispatch = false; + // 将触摸事件分发给列表视图,并返回结果 return mNotesListView.dispatchTouchEvent(event); } break; } } + // 如果没有满足以上任何条件,返回false return false; } }; + // 用来异步查询笔记列表 private void startAsyncNotesListQuery() { + // 设置查询条件,如果当前文件夹的id是根文件夹的id,就用根文件夹的选择条件,否则用普通的选择条件 String selection = (mCurrentFolderId == Notes.ID_ROOT_FOLDER) ? ROOT_FOLDER_SELECTION : NORMAL_SELECTION; mBackgroundQueryHandler.startQuery(FOLDER_NOTE_LIST_QUERY_TOKEN, null, @@ -417,21 +526,31 @@ public class NotesListActivity extends Activity implements OnClickListener, OnIt }, NoteColumns.TYPE + " DESC," + NoteColumns.MODIFIED_DATE + " DESC"); } + // 用来处理异步查询的结果 private final class BackgroundQueryHandler extends AsyncQueryHandler { public BackgroundQueryHandler(ContentResolver contentResolver) { + // 调用父类的构造方法,传入内容解析器 super(contentResolver); } + // 当查询完成时,这个方法会被调用,传入查询标识符,对象和游标 @Override protected void onQueryComplete(int token, Object cookie, Cursor cursor) { + // 根据查询标识符进行不同的处理 switch (token) { + // 如果是查询笔记列表的标识符 case FOLDER_NOTE_LIST_QUERY_TOKEN: + // 改变笔记列表适配器的游标,更新数据 mNotesListAdapter.changeCursor(cursor); break; + // 如果是查询文件夹列表的标识符 case FOLDER_LIST_QUERY_TOKEN: + // 如果游标不为空,并且有数据 if (cursor != null && cursor.getCount() > 0) { + // 显示文件夹列表菜单,传入游标 showFolderListMenu(cursor); } else { + // 否则,打印错误日志,表示查询文件夹 Log.e(TAG, "Query folder failed"); } break; @@ -441,13 +560,21 @@ public class NotesListActivity extends Activity implements OnClickListener, OnIt } } + // 用来显示文件夹列表菜单 private void showFolderListMenu(Cursor cursor) { + // 创建一个对话框构建器,传入当前活动的上下文 AlertDialog.Builder builder = new AlertDialog.Builder(NotesListActivity.this); + // 设置对话框的标题为选择文件夹 builder.setTitle(R.string.menu_title_select_folder); + // 创建一个文件夹列表适配器,传入当前活动和游标 final FoldersListAdapter adapter = new FoldersListAdapter(this, cursor); + // 设置对话框的适配器和点击监听器 builder.setAdapter(adapter, new DialogInterface.OnClickListener() { + // 当点击某个项目时,这个方法会被调用,传入对话框和项目的位置 public void onClick(DialogInterface dialog, int which) { + // 调用数据工具类的方法,批量将选中的笔记移动到点击的文件夹中, + // 传入内容解析器,选中的笔记的id和点击的文件夹的id DataUtils.batchMoveToFolder(mContentResolver, mNotesListAdapter.getSelectedItemIds(), adapter.getItemId(which)); Toast.makeText( @@ -455,58 +582,85 @@ public class NotesListActivity extends Activity implements OnClickListener, OnIt getString(R.string.format_move_notes_to_folder, mNotesListAdapter.getSelectedCount(), adapter.getFolderName(NotesListActivity.this, which)), + // 显示一个短暂的提示信息,表示移动了多少个笔记到哪个文件夹 Toast.LENGTH_SHORT).show(); + // 结束操作模式,取消选择 mModeCallBack.finishActionMode(); } }); + // 显示对话框 builder.show(); } + // 用来创建一个新的笔记 private void createNewNote() { + // 创建一个意图,传入当前活动和笔记编辑活动的类对象 Intent intent = new Intent(this, NoteEditActivity.class); + // 设置意图的动作为插入或编辑 intent.setAction(Intent.ACTION_INSERT_OR_EDIT); + // 将当前文件夹的id作为额外数据放入意图中 intent.putExtra(Notes.INTENT_EXTRA_FOLDER_ID, mCurrentFolderId); + // 启动一个新的活动,并期望返回结果,传入意图和请求码 this.startActivityForResult(intent, REQUEST_CODE_NEW_NODE); } + // 用来批量删除笔记 private void batchDelete() { + // 创建一个异步任务,传入空参数,空进度和小部件属性的集合 new AsyncTask>() { + // 在后台线程执行的方法,传入空参数 protected HashSet doInBackground(Void... unused) { + // 获取选中的小部件的属性集合 HashSet widgets = mNotesListAdapter.getSelectedWidget(); + // 如果不是同步模式 if (!isSyncMode()) { // if not synced, delete notes directly + // 调用数据工具类的方法,直接删除选中的笔记,传入内容解析器和选中的笔记的id if (DataUtils.batchDeleteNotes(mContentResolver, mNotesListAdapter .getSelectedItemIds())) { } else { + // 否则,打印错误日志,表示删除笔记出错,不应该发生 Log.e(TAG, "Delete notes error, should not happens"); } } else { // in sync mode, we'll move the deleted note into the trash // folder + // 如果是同步模式,调用数据工具类的方法,将选中的笔记移动到回收站文件夹中, + // 传入内容解析器,选中的笔记的id和回收站文件夹的id if (!DataUtils.batchMoveToFolder(mContentResolver, mNotesListAdapter .getSelectedItemIds(), Notes.ID_TRASH_FOLER)) { + // 如果失败,打印错误日志,表示移动笔记到回收站文件夹出错,不应该发生 Log.e(TAG, "Move notes to trash folder error, should not happens"); } } + // 返回小部件属性集合 return widgets; } + // 在主线程执行的方法,传入小部件属性集合 @Override protected void onPostExecute(HashSet widgets) { + // 如果小部件属性集合不为空 if (widgets != null) { + // 遍历每个小部件属性 for (AppWidgetAttribute widget : widgets) { + // 如果小部件的id和类型都有效 if (widget.widgetId != AppWidgetManager.INVALID_APPWIDGET_ID && widget.widgetType != Notes.TYPE_WIDGET_INVALIDE) { + // 更新小部件,传入小部件的id和类型 updateWidget(widget.widgetId, widget.widgetType); } } } + // 结束操作模式,取消选择 mModeCallBack.finishActionMode(); } - }.execute(); + }.execute(); // 执行异步任务 } + // 用于删除文件夹 private void deleteFolder(long folderId) { + // 如果文件夹id是根文件夹的id,打印错误信息并返回 if (folderId == Notes.ID_ROOT_FOLDER) { Log.e(TAG, "Wrong folder id, should not happen " + folderId); return; @@ -514,6 +668,7 @@ public class NotesListActivity extends Activity implements OnClickListener, OnIt HashSet ids = new HashSet(); ids.add(folderId); + // 获取文件夹中的笔记小部件 HashSet widgets = DataUtils.getFolderNoteWidget(mContentResolver, folderId); if (!isSyncMode()) { @@ -525,6 +680,7 @@ public class NotesListActivity extends Activity implements OnClickListener, OnIt } if (widgets != null) { for (AppWidgetAttribute widget : widgets) { + // 如果小部件id和类型有效,更新小部件 if (widget.widgetId != AppWidgetManager.INVALID_APPWIDGET_ID && widget.widgetType != Notes.TYPE_WIDGET_INVALIDE) { updateWidget(widget.widgetId, widget.widgetType); @@ -533,32 +689,44 @@ public class NotesListActivity extends Activity implements OnClickListener, OnIt } } + // 打开一个笔记 private void openNode(NoteItemData data) { Intent intent = new Intent(this, NoteEditActivity.class); intent.setAction(Intent.ACTION_VIEW); + // 传递笔记的id作为额外参数 intent.putExtra(Intent.EXTRA_UID, data.getId()); + // 启动笔记编辑活动,并期待返回结果 this.startActivityForResult(intent, REQUEST_CODE_OPEN_NODE); } + // 打开一个文件夹,并显示其中的笔记列表 private void openFolder(NoteItemData data) { + // 设置当前文件夹的id mCurrentFolderId = data.getId(); + // 异步查询笔记列表 startAsyncNotesListQuery(); + // 判断文件夹的类型,设置状态和视图 if (data.getId() == Notes.ID_CALL_RECORD_FOLDER) { + // 如果是通话记录文件夹,设置状态为CALL_RECORD_FOLDER,并隐藏新建笔记按钮 mState = ListEditState.CALL_RECORD_FOLDER; mAddNewNote.setVisibility(View.GONE); } else { + // 如果是其他文件夹,设置状态为SUB_FOLDER mState = ListEditState.SUB_FOLDER; } + // 设置标题栏的文本,如果是通话记录文件夹,显示固定的字符串,否则显示文件夹的名称 if (data.getId() == Notes.ID_CALL_RECORD_FOLDER) { mTitleBar.setText(R.string.call_record_folder_name); } else { mTitleBar.setText(data.getSnippet()); } + // 显示标题栏 mTitleBar.setVisibility(View.VISIBLE); } - + // 处理点击事件 public void onClick(View v) { switch (v.getId()) { + // 如果点击的是新建笔记按钮,调用createNewNote方法 case R.id.btn_new_note: createNewNote(); break; @@ -567,89 +735,134 @@ public class NotesListActivity extends Activity implements OnClickListener, OnIt } } + // 显示软键盘 private void showSoftInput() { + // 获取输入法管理器对象 InputMethodManager inputMethodManager = (InputMethodManager) getSystemService(Context.INPUT_METHOD_SERVICE); if (inputMethodManager != null) { + // 强制显示软键盘 inputMethodManager.toggleSoftInput(InputMethodManager.SHOW_FORCED, 0); } } + // 隐藏软键盘 private void hideSoftInput(View view) { InputMethodManager inputMethodManager = (InputMethodManager) getSystemService(Context.INPUT_METHOD_SERVICE); inputMethodManager.hideSoftInputFromWindow(view.getWindowToken(), 0); } + //定义一个方法,根据参数create来显示创建或修改文件夹的对话框 private void showCreateOrModifyFolderDialog(final boolean create) { + //创建一个AlertDialog.Builder对象,用于构建对话框 final AlertDialog.Builder builder = new AlertDialog.Builder(this); + // 从布局文件中加载一个View对象,用于显示对话框的内容 View view = LayoutInflater.from(this).inflate(R.layout.dialog_edit_text, null); + //从View对象中获取一个EditText对象,用于输入文件夹的名称 final EditText etName = (EditText) view.findViewById(R.id.et_foler_name); showSoftInput(); + //判断create参数是否为false,如果是,表示要修改文件夹的名称 if (!create) { + //判断mFocusNoteDataItem是否不为空,如果是,表示有选中的文件夹 if (mFocusNoteDataItem != null) { + //设置EditText对象的文本为选中文件夹的名称 etName.setText(mFocusNoteDataItem.getSnippet()); + //设置对话框的标题为“修改文件夹名称” builder.setTitle(getString(R.string.menu_folder_change_name)); } else { + //否则,打印一条错误日志,并返回 Log.e(TAG, "The long click data item is null"); return; } } else { + //否则,表示要创建文件夹 + //设置EditText对象的文本为空 etName.setText(""); + //设置对话框的标题为“新文件夹” builder.setTitle(this.getString(R.string.menu_create_folder)); } + //设置对话框的确定按钮,点击后不做任何操作(需要在后面重写点击事件) builder.setPositiveButton(android.R.string.ok, null); + //设置对话框的取消按钮,点击后调用一个方法,隐藏软键盘,并传入EditText对象作为参数 builder.setNegativeButton(android.R.string.cancel, new DialogInterface.OnClickListener() { public void onClick(DialogInterface dialog, int which) { hideSoftInput(etName); } }); + // 设置对话框的视图,并显示 final Dialog dialog = builder.setView(view).show(); + // 根据ID找到正面按钮,然后将其转换为Button类型 final Button positive = (Button)dialog.findViewById(android.R.id.button1); + // 设置positive按钮的点击事件 positive.setOnClickListener(new OnClickListener() { public void onClick(View v) { + // 隐藏软键盘 hideSoftInput(etName); + // 获取输入框中的文件夹名 String name = etName.getText().toString(); + // 检查文件夹名是否已存在 if (DataUtils.checkVisibleFolderName(mContentResolver, name)) { + // 如果已存在,弹出提示信息 Toast.makeText(NotesListActivity.this, getString(R.string.folder_exist, name), Toast.LENGTH_LONG).show(); + // 选中输入框中的所有文本 etName.setSelection(0, etName.length()); + // 返回,不执行后续操作 return; } + // 如果不是创建模式,而是修改模式 if (!create) { + // 如果输入框中有内容 if (!TextUtils.isEmpty(name)) { + // 创建一个ContentValues对象,用于存放更新的数据 ContentValues values = new ContentValues(); + // 将文件夹名作为便签片段存入values values.put(NoteColumns.SNIPPET, name); + // 将便签类型设为文件夹类型存入values values.put(NoteColumns.TYPE, Notes.TYPE_FOLDER); + // 将本地修改标志设为1存入values values.put(NoteColumns.LOCAL_MODIFIED, 1); + // 根据便签ID更新数据库中的数据 mContentResolver.update(Notes.CONTENT_NOTE_URI, values, NoteColumns.ID + "=?", new String[] { String.valueOf(mFocusNoteDataItem.getId()) }); } } else if (!TextUtils.isEmpty(name)) { + // 如果是创建模式,并且输入框中有内容 + // 创建一个ContentValues对象,用于存放插入的数据 ContentValues values = new ContentValues(); + // 将文件夹名作为便签片段存入values values.put(NoteColumns.SNIPPET, name); + // 将便签类型设为文件夹类型存入values values.put(NoteColumns.TYPE, Notes.TYPE_FOLDER); + // 插入一条新的数据到数据库中 mContentResolver.insert(Notes.CONTENT_NOTE_URI, values); } + // 关闭对话框 dialog.dismiss(); } }); + // 如果输入框中没有内容,禁用positive按钮 if (TextUtils.isEmpty(etName.getText())) { positive.setEnabled(false); } /** * When the name edit text is null, disable the positive button */ + // 为输入框添加文本变化的监听器 etName.addTextChangedListener(new TextWatcher() { + // 在文本变化之前的回调方法 public void beforeTextChanged(CharSequence s, int start, int count, int after) { // TODO Auto-generated method stub } + // 在文本变化之前的回调方法 public void onTextChanged(CharSequence s, int start, int before, int count) { + // 如果输入框中没有内容,禁用positive按钮 if (TextUtils.isEmpty(etName.getText())) { positive.setEnabled(false); } else { @@ -657,6 +870,7 @@ public class NotesListActivity extends Activity implements OnClickListener, OnIt } } + // 在文本变化之后的回调方法 public void afterTextChanged(Editable s) { // TODO Auto-generated method stub @@ -664,23 +878,32 @@ public class NotesListActivity extends Activity implements OnClickListener, OnIt }); } + // 当用户按下返回键时执行 @Override public void onBackPressed() { switch (mState) { case SUB_FOLDER: + //如果当前状态是子文件夹,那么返回到根文件夹 mCurrentFolderId = Notes.ID_ROOT_FOLDER; mState = ListEditState.NOTE_LIST; + //开始异步查询笔记列表 startAsyncNotesListQuery(); + //隐藏标题栏 mTitleBar.setVisibility(View.GONE); break; case CALL_RECORD_FOLDER: + //如果当前状态是通话记录文件夹,那么返回到根文件夹 mCurrentFolderId = Notes.ID_ROOT_FOLDER; mState = ListEditState.NOTE_LIST; + //显示添加新笔记的按钮 mAddNewNote.setVisibility(View.VISIBLE); + //隐藏标题栏 mTitleBar.setVisibility(View.GONE); + //开始异步查询笔记列表 startAsyncNotesListQuery(); break; case NOTE_LIST: + //如果当前状态是笔记列表,那么直接调用父类的方法 super.onBackPressed(); break; default: @@ -688,29 +911,42 @@ public class NotesListActivity extends Activity implements OnClickListener, OnIt } } + // 用来更新小部件 private void updateWidget(int appWidgetId, int appWidgetType) { + //创建一个更新小部件的意图 Intent intent = new Intent(AppWidgetManager.ACTION_APPWIDGET_UPDATE); if (appWidgetType == Notes.TYPE_WIDGET_2X) { + //如果小部件类型是2x,那么设置意图的类为NoteWidgetProvider_2x intent.setClass(this, NoteWidgetProvider_2x.class); } else if (appWidgetType == Notes.TYPE_WIDGET_4X) { + //如果小部件类型是4x,那么设置意图的类为NoteWidgetProvider_4x intent.setClass(this, NoteWidgetProvider_4x.class); } else { + //如果小部件类型不支持,那么打印错误日志并返回 Log.e(TAG, "Unspported widget type"); return; } + //将小部件的id放入意图中 intent.putExtra(AppWidgetManager.EXTRA_APPWIDGET_IDS, new int[] { appWidgetId }); + //发送广播更新小部件 sendBroadcast(intent); + //设置结果为成功,并将意图返回给调用者 setResult(RESULT_OK, intent); } + // 定义一个私有的最终变量,用于创建上下文菜单的监听器 private final OnCreateContextMenuListener mFolderOnCreateContextMenuListener = new OnCreateContextMenuListener() { + // 重写onCreateContextMenu方法,用于在长按文件夹时弹出菜单 public void onCreateContextMenu(ContextMenu menu, View v, ContextMenuInfo menuInfo) { + // 如果当前聚焦的笔记数据项不为空 if (mFocusNoteDataItem != null) { + // 设置菜单的标题为笔记的摘要 menu.setHeaderTitle(mFocusNoteDataItem.getSnippet()); + // 添加菜单项,分别为查看文件夹、删除文件夹和修改文件夹名称,指定id和顺序 menu.add(0, MENU_FOLDER_VIEW, 0, R.string.menu_folder_view); menu.add(0, MENU_FOLDER_DELETE, 0, R.string.menu_folder_delete); menu.add(0, MENU_FOLDER_CHANGE_NAME, 0, R.string.menu_folder_change_name); @@ -718,38 +954,53 @@ public class NotesListActivity extends Activity implements OnClickListener, OnIt } }; + //当上下文菜单关闭时,执行以下操作 @Override public void onContextMenuClosed(Menu menu) { + //如果笔记列表视图不为空,就取消设置上下文菜单监听器 if (mNotesListView != null) { mNotesListView.setOnCreateContextMenuListener(null); } + //调用父类的方法 super.onContextMenuClosed(menu); } + //当上下文菜单中的某一项被选择时,执行以下操作 @Override public boolean onContextItemSelected(MenuItem item) { + //如果长按的数据项为空,就打印错误日志并返回false if (mFocusNoteDataItem == null) { Log.e(TAG, "The long click data item is null"); return false; } + //根据选择的菜单项的id,执行不同的操作 switch (item.getItemId()) { + //如果选择了查看文件夹,就打开对应的文件夹 case MENU_FOLDER_VIEW: openFolder(mFocusNoteDataItem); break; + //如果选择了删除文件夹,就弹出一个对话框,询问用户是否确定删除 case MENU_FOLDER_DELETE: AlertDialog.Builder builder = new AlertDialog.Builder(this); + //设置对话框的标题 builder.setTitle(getString(R.string.alert_title_delete)); + //设置对话框的图标 builder.setIcon(android.R.drawable.ic_dialog_alert); + //设置对话框的内容 builder.setMessage(getString(R.string.alert_message_delete_folder)); + //设置对话框的确定按钮和点击事件 builder.setPositiveButton(android.R.string.ok, new DialogInterface.OnClickListener() { public void onClick(DialogInterface dialog, int which) { + //删除文件夹 deleteFolder(mFocusNoteDataItem.getId()); } }); + //设置对话框的取消按钮和点击事件 builder.setNegativeButton(android.R.string.cancel, null); - builder.show(); + builder.show();//显示对话框 break; + //如果选择了修改文件夹名称,就显示一个创建或修改文件夹的对话框 case MENU_FOLDER_CHANGE_NAME: showCreateOrModifyFolderDialog(false); break; @@ -757,103 +1008,158 @@ public class NotesListActivity extends Activity implements OnClickListener, OnIt break; } + //返回true表示处理了上下文菜单事件 return true; } + // 重写onPrepareOptionsMenu方法,用于在准备选项菜单时根据不同的状态显示不同的菜单 @Override public boolean onPrepareOptionsMenu(Menu menu) { + // 清空菜单 menu.clear(); + // 如果当前状态是笔记列表 if (mState == ListEditState.NOTE_LIST) { + // 从note_list.xml文件中加载菜单项 getMenuInflater().inflate(R.menu.note_list, menu); // set sync or sync_cancel menu.findItem(R.id.menu_sync).setTitle( GTaskSyncService.isSyncing() ? R.string.menu_sync_cancel : R.string.menu_sync); } else if (mState == ListEditState.SUB_FOLDER) { + // 如果当前状态是子文件夹 + // 从sub_folder.xml文件中加载菜单项 getMenuInflater().inflate(R.menu.sub_folder, menu); } else if (mState == ListEditState.CALL_RECORD_FOLDER) { + // 如果当前状态是通话记录文件夹 + // 从call_record_folder.xml文件中加载菜单项 getMenuInflater().inflate(R.menu.call_record_folder, menu); } else { + // 如果当前状态不属于以上任何一种 + // 打印错误日志,显示错误的状态 Log.e(TAG, "Wrong state:" + mState); } + // 返回true表示显示菜单 return true; } + // 重写onOptionsItemSelected方法,用于处理选项菜单的点击事件 @Override public boolean onOptionsItemSelected(MenuItem item) { + // 根据菜单项的id进行分支判断 switch (item.getItemId()) { + // 如果点击了新建文件夹菜单项 case R.id.menu_new_folder: { + // 显示创建或修改文件夹的对话框,传入true表示是创建模式 showCreateOrModifyFolderDialog(true); break; } + // 如果点击了导出文本菜单项 case R.id.menu_export_text: { + // 调用导出笔记到文本的方法 exportNoteToText(); break; } + // 如果点击了同步菜单项 case R.id.menu_sync: { + // 如果当前是同步模式 if (isSyncMode()) { + // 如果菜单项的标题是同步 if (TextUtils.equals(item.getTitle(), getString(R.string.menu_sync))) { + // 调用GTaskSyncService的startSync方法,开始同步 GTaskSyncService.startSync(this); } else { + // 如果菜单项的标题是取消同步 + // 调用GTaskSyncService的cancelSync方法,取消同步 GTaskSyncService.cancelSync(this); } } else { + // 如果当前不是同步模式 + // 调用启动偏好设置活动的方法 startPreferenceActivity(); } break; } case R.id.menu_setting: { + // 如果点击了设置菜单项 + // 调用启动偏好设置活动的方法 startPreferenceActivity(); break; } case R.id.menu_new_note: { + // 如果点击了新建笔记菜单项 + // 调用创建新笔记的方法 createNewNote(); break; } case R.id.menu_search: + // 如果点击了搜索菜单项 + // 调用onSearchRequested方法,启动搜索界面 onSearchRequested(); break; default: break; } + // 返回true表示处理了菜单项的点击事件 return true; } + // 用于启动搜索界面 @Override public boolean onSearchRequested() { + // 调用startSearch方法,传入null表示不指定搜索的初始值, + // false表示不显示搜索提示,null表示不传递额外的数据,false表示不使用全局搜索 startSearch(null, false, null /* appData */, false); + // 返回true表示处理了搜索请求 return true; } + // 用于到处笔记到文本文件 private void exportNoteToText() { + // 获取一个BackupUtils的实例,传入当前活动的上下文 final BackupUtils backup = BackupUtils.getInstance(NotesListActivity.this); + // 创建一个异步任务,用于在后台线程执行导出操作 new AsyncTask() { + // 重写doInBackground方法,用于执行导出操作,并返回一个整数表示导出的结果 @Override protected Integer doInBackground(Void... unused) { return backup.exportToText(); } + // 重写onPostExecute方法,用于在主线程处理导出的结果 @Override protected void onPostExecute(Integer result) { + // 如果导出的结果是SD卡未挂载 if (result == BackupUtils.STATE_SD_CARD_UNMOUONTED) { + // 创建一个对话框构造器,传入当前活动的上下文 AlertDialog.Builder builder = new AlertDialog.Builder(NotesListActivity.this); + // 设置对话框的标题为导出失败 builder.setTitle(NotesListActivity.this .getString(R.string.failed_sdcard_export)); + // 设置对话框的内容为SD卡未挂载的错误信息 builder.setMessage(NotesListActivity.this .getString(R.string.error_sdcard_unmounted)); + // 设置对话框的确定按钮,不指定点击事件 builder.setPositiveButton(android.R.string.ok, null); - builder.show(); + builder.show();// 显示对话框 } else if (result == BackupUtils.STATE_SUCCESS) { + // 如果导出的结果是成功 + // 创建一个对话框构造器,传入当前活动的上下文 AlertDialog.Builder builder = new AlertDialog.Builder(NotesListActivity.this); + // 设置对话框的标题为导出成功 builder.setTitle(NotesListActivity.this .getString(R.string.success_sdcard_export)); + // 设置对话框的内容为导出文件的名称和位置 builder.setMessage(NotesListActivity.this.getString( R.string.format_exported_file_location, backup .getExportedTextFileName(), backup.getExportedTextFileDir())); + // 设置对话框的确定按钮,不指定点击事件 builder.setPositiveButton(android.R.string.ok, null); builder.show(); } else if (result == BackupUtils.STATE_SYSTEM_ERROR) { + // 如果导出的结果是系统错误 + // 创建一个对话框构造器,传入当前活动的上下文 AlertDialog.Builder builder = new AlertDialog.Builder(NotesListActivity.this); + // 设置对话框的标题为导出失败 builder.setTitle(NotesListActivity.this .getString(R.string.failed_sdcard_export)); builder.setMessage(NotesListActivity.this @@ -863,49 +1169,79 @@ public class NotesListActivity extends Activity implements OnClickListener, OnIt } } - }.execute(); + }.execute();// 调用execute方法,启动异步任务 } + // 用于判断是否是同步模式 private boolean isSyncMode() { + // 调用NotesPreferenceActivity的getSyncAccountName方法,获取同步账户的名称,并去除两端的空格, + // 如果长度大于0,说明有同步账户,返回true,否则返回false return NotesPreferenceActivity.getSyncAccountName(this).trim().length() > 0; } + // 用于启动偏好设置活动 private void startPreferenceActivity() { + // 获取当前活动的父活动,如果没有父活动,则使用当前活动 Activity from = getParent() != null ? getParent() : this; + // 创建一个意图对象,指定从当前活动或父活动跳转到NotesPreferenceActivity Intent intent = new Intent(from, NotesPreferenceActivity.class); + // 传入意图对象和-1表示不指定请求码,如果需要则启动活动 from.startActivityIfNeeded(intent, -1); } + // 用于处理列表项的点击事件 private class OnListItemClickListener implements OnItemClickListener { + // 重写onItemClick方法,传入父视图,点击的视图,点击的位置和点击的id public void onItemClick(AdapterView parent, View view, int position, long id) { if (view instanceof NotesListItem) { + // 获取该视图对应的笔记数据项 NoteItemData item = ((NotesListItem) view).getItemData(); + // 如果笔记列表适配器处于选择模式 if (mNotesListAdapter.isInChoiceMode()) { + // 如果笔记数据项的类型是普通笔记 if (item.getType() == Notes.TYPE_NOTE) { + // 计算点击的位置,减去列表视图的头部视图数 position = position - mNotesListView.getHeaderViewsCount(); + // 调用模式回调的onItemCheckedStateChanged方法, + // 传入null表示不指定操作模式,传入位置和id,以及取反后的选择状态, + // 用于改变列表项的选择状态 mModeCallBack.onItemCheckedStateChanged(null, position, id, !mNotesListAdapter.isSelectedItem(position)); } return; } + // 根据当前状态进行分支判断 switch (mState) { + // 如果当前状态是笔记列表 case NOTE_LIST: + // 如果笔记数据项的类型是文件夹或系统文件夹 if (item.getType() == Notes.TYPE_FOLDER || item.getType() == Notes.TYPE_SYSTEM) { + // 调用打开文件夹的方法,传入笔记数据项 openFolder(item); } else if (item.getType() == Notes.TYPE_NOTE) { + // 如果笔记数据项的类型是普通笔记 + // 调用打开笔记的方法,传入笔记数据项 openNode(item); } else { + // 如果笔记数据项的类型不属于以上任何一种 + // 打印错误日志,显示错误的笔记类型 Log.e(TAG, "Wrong note type in NOTE_LIST"); } break; + // 如果当前状态是子文件夹 case SUB_FOLDER: + // 或者当前状态是通话记录文件夹 case CALL_RECORD_FOLDER: if (item.getType() == Notes.TYPE_NOTE) { + // 如果笔记数据项的类型是普通笔记 + // 调用打开笔记的方法,传入笔记数据项 openNode(item); } else { + // 如果笔记数据项的类型不是普通笔记 + // 打印错误日志,显示错误的笔记类型 Log.e(TAG, "Wrong note type in SUB_FOLDER"); } break; @@ -917,11 +1253,18 @@ public class NotesListActivity extends Activity implements OnClickListener, OnIt } + // 用于启动查询目标文件夹的操作 private void startQueryDestinationFolders() { + // 用于存储查询条件,表示笔记的类型是文件夹,且父id不是回收站文件夹,且id不是当前文件夹 String selection = NoteColumns.TYPE + "=? AND " + NoteColumns.PARENT_ID + "<>? AND " + NoteColumns.ID + "<>?"; + + // 如果当前状态是笔记列表,不改变查询条件, + // 否则在查询条件的基础上增加一个或条件,表示id等于根文件夹 selection = (mState == ListEditState.NOTE_LIST) ? selection: "(" + selection + ") OR (" + NoteColumns.ID + "=" + Notes.ID_ROOT_FOLDER + ")"; + // 调用后台查询处理器的startQuery方法,传入文件夹列表查询标记, + // null表示不指定cookie对象,笔记内容的uri,文件夹列表适配器的投影数组,查询条件和参数数组,以及按修改日期降序排序 mBackgroundQueryHandler.startQuery(FOLDER_LIST_QUERY_TOKEN, null, Notes.CONTENT_NOTE_URI, @@ -935,20 +1278,33 @@ public class NotesListActivity extends Activity implements OnClickListener, OnIt NoteColumns.MODIFIED_DATE + " DESC"); } + // 用于处理列表项的长按事件,传入父视图,长按的视图,长按的位置和长按的id public boolean onItemLongClick(AdapterView parent, View view, int position, long id) { + // 如果长按的视图是一个NotesListItem if (view instanceof NotesListItem) { + // 获取该视图对应的笔记数据项,并赋值给mFocusNoteDataItem变量 mFocusNoteDataItem = ((NotesListItem) view).getItemData(); + // 如果笔记数据项的类型是普通笔记,并且笔记列表适配器不处于选择模式 if (mFocusNoteDataItem.getType() == Notes.TYPE_NOTE && !mNotesListAdapter.isInChoiceMode()) { + // 如果调用列表视图的startActionMode方法,并传入模式回调对象,返回不为空 if (mNotesListView.startActionMode(mModeCallBack) != null) { + // 传入null表示不指定操作模式,传入位置和id,以及true表示选中该列表项 mModeCallBack.onItemCheckedStateChanged(null, position, id, true); + // 传入长按常量,表示触发触觉反馈 mNotesListView.performHapticFeedback(HapticFeedbackConstants.LONG_PRESS); } else { + // 如果调用列表视图的startActionMode方法失败 + // 打印错误日志,显示启动操作模式失败 Log.e(TAG, "startActionMode fails"); } } else if (mFocusNoteDataItem.getType() == Notes.TYPE_FOLDER) { + // 如果笔记数据项的类型是文件夹 + // 调用列表视图的setOnCreateContextMenuListener方法, + // 并传入文件夹创建上下文菜单监听器对象,表示设置该监听器为创建上下文菜单的监听器 mNotesListView.setOnCreateContextMenuListener(mFolderOnCreateContextMenuListener); } } + // 返回false表示不消费长按事件 return false; } }