diff --git a/doc/小米便签开源代码全文泛读报告 .docx b/doc/小米便签开源代码全文泛读报告 .docx index 6d1cc6c..3350100 100644 Binary files a/doc/小米便签开源代码全文泛读报告 .docx and b/doc/小米便签开源代码全文泛读报告 .docx differ diff --git a/src/data/Contact.java b/src/data/Contact.java index 45ad20d..19db60e 100644 --- a/src/data/Contact.java +++ b/src/data/Contact.java @@ -1,6 +1,6 @@ /* * Contact 类用于通过电话号码查询联系人信息。 - * 该类实现了从联系人数据库中获取与特定电话号码相关联的显示名称。 + * 该类实现了从联系人数据库中获取与特定电话号码相关联的显示名称 */ package net.micode.notes.data; diff --git a/src/gtask/data/MetaData.java b/src/gtask/data/MetaData.java index 1a9f7f6..c5b2147 100644 --- a/src/gtask/data/MetaData.java +++ b/src/gtask/data/MetaData.java @@ -13,7 +13,7 @@ import org.json.JSONException; import org.json.JSONObject; public class MetaData extends Task { - private final static String TAG = MetaData.class.getSimpleName(); // 日志标签,用于调试和错误管理 + private final static String TAG = MetaData.class.getSimpleName(); // 日志标签,用于调试和错误管理。 private String mRelatedGid = null; // 与任务相关的全局ID diff --git a/src/model/Note.java b/src/model/Note.java index 55581be..9913177 100644 --- a/src/model/Note.java +++ b/src/model/Note.java @@ -168,8 +168,7 @@ public class Note { } // ... 其他方法保持不变 ... - - /** + /**** * 将本地修改过的额外数据(如文本或通话记录)同步到数据库。 * @param context 应用程序上下文,用于访问数据库 * @param noteId 笔记的唯一标识符 diff --git a/src/tool/BackupUtils.java b/src/tool/BackupUtils.java index 821cbd6..5c21f2e 100644 --- a/src/tool/BackupUtils.java +++ b/src/tool/BackupUtils.java @@ -6,7 +6,7 @@ package net.micode.notes.tool; -// 引入需要用到的 Android 和 Java 标准库类 +// 引入需要用到的 Android 和 Java 标准库类。 import android.content.Context; import android.database.Cursor; import android.os.Environment; diff --git a/src/ui/NoteItemData.java b/src/ui/NoteItemData.java index e0c70c5..17949b9 100644 --- a/src/ui/NoteItemData.java +++ b/src/ui/NoteItemData.java @@ -13,107 +13,106 @@ * See the License for the specific language governing permissions and * limitations under the License. */ - package net.micode.notes.ui; import android.content.Context; import android.database.Cursor; import android.text.TextUtils; - import net.micode.notes.data.Contact; import net.micode.notes.data.Notes; import net.micode.notes.data.Notes.NoteColumns; import net.micode.notes.tool.DataUtils; - /** * 代表一个笔记项的数据类,用于存储和管理笔记的各种信息。 + * 此类封装了笔记的所有属性,并提供构造函数来填充这些属性,以及辅助方法来确定笔记在列表中的位置状态。 */ public class NoteItemData { - // 定义查询时要投影的列 + // 定义查询时要投影的列(即从数据库中读取哪些字段) static final String[] PROJECTION = new String[]{ - NoteColumns.ID, - NoteColumns.ALERTED_DATE, - NoteColumns.BG_COLOR_ID, - NoteColumns.CREATED_DATE, - NoteColumns.HAS_ATTACHMENT, - NoteColumns.MODIFIED_DATE, - NoteColumns.NOTES_COUNT, - NoteColumns.PARENT_ID, - NoteColumns.SNIPPET, - NoteColumns.TYPE, - NoteColumns.WIDGET_ID, - NoteColumns.WIDGET_TYPE, + NoteColumns.ID, // 笔记ID + NoteColumns.ALERTED_DATE, // 提醒日期 + NoteColumns.BG_COLOR_ID, // 背景颜色ID + NoteColumns.CREATED_DATE, // 创建日期 + NoteColumns.HAS_ATTACHMENT, // 是否有附件 + NoteColumns.MODIFIED_DATE, // 修改日期 + NoteColumns.NOTES_COUNT, // 子笔记数量(如果是文件夹类型) + NoteColumns.PARENT_ID, // 父级ID(用于组织结构) + NoteColumns.SNIPPET, // 笔记摘要或片段 + NoteColumns.TYPE, // 笔记类型(例如普通笔记、通话记录等) + NoteColumns.WIDGET_ID, // 关联的小部件ID + NoteColumns.WIDGET_TYPE // 小部件类型 }; - // 各列数据的索引 - private static final int ID_COLUMN = 0; - private static final int ALERTED_DATE_COLUMN = 1; - private static final int BG_COLOR_ID_COLUMN = 2; - private static final int CREATED_DATE_COLUMN = 3; - private static final int HAS_ATTACHMENT_COLUMN = 4; - private static final int MODIFIED_DATE_COLUMN = 5; - private static final int NOTES_COUNT_COLUMN = 6; - private static final int PARENT_ID_COLUMN = 7; - private static final int SNIPPET_COLUMN = 8; - private static final int TYPE_COLUMN = 9; - private static final int WIDGET_ID_COLUMN = 10; - private static final int WIDGET_TYPE_COLUMN = 11; - - // 笔记的各项数据 - private long mId; - private long mAlertDate; - private int mBgColorId; - private long mCreatedDate; - private boolean mHasAttachment; - private long mModifiedDate; - private int mNotesCount; - private long mParentId; - private String mSnippet; - private int mType; - private int mWidgetId; - private int mWidgetType; - private String mName; - private String mPhoneNumber; + // 各列数据的索引常量,方便通过Cursor获取数据 + private static final int ID_COLUMN = 0; // 笔记ID的索引 + private static final int ALERTED_DATE_COLUMN = 1; // 提醒日期的索引 + private static final int BG_COLOR_ID_COLUMN = 2; // 背景颜色ID的索引 + private static final int CREATED_DATE_COLUMN = 3; // 创建日期的索引 + private static final int HAS_ATTACHMENT_COLUMN = 4; // 是否有附件的索引 + private static final int MODIFIED_DATE_COLUMN = 5; // 修改日期的索引 + private static final int NOTES_COUNT_COLUMN = 6; // 子笔记数量的索引 + private static final int PARENT_ID_COLUMN = 7; // 父级ID的索引 + private static final int SNIPPET_COLUMN = 8; // 笔记摘要的索引 + private static final int TYPE_COLUMN = 9; // 笔记类型的索引 + private static final int WIDGET_ID_COLUMN = 10; // 小部件ID的索引 + private static final int WIDGET_TYPE_COLUMN = 11; // 小部件类型的索引 + + // 笔记的各项数据成员变量 + private long mId; // 笔记ID + private long mAlertDate; // 提醒日期 + private int mBgColorId; // 背景颜色ID + private long mCreatedDate; // 创建日期 + private boolean mHasAttachment; // 是否有附件 + private long mModifiedDate; // 修改日期 + private int mNotesCount; // 子笔记数量 + private long mParentId; // 父级ID + private String mSnippet; // 笔记摘要 + private int mType; // 笔记类型 + private int mWidgetId; // 小部件ID + private int mWidgetType; // 小部件类型 + private String mName; // 对于通话记录笔记,联系人名称 + private String mPhoneNumber; // 对于通话记录笔记,电话号码 // 用于标识笔记在列表中的位置状态 - private boolean mIsLastItem; - private boolean mIsFirstItem; - private boolean mIsOnlyOneItem; - private boolean mIsOneNoteFollowingFolder; - private boolean mIsMultiNotesFollowingFolder; + private boolean mIsLastItem; // 是否为列表最后一项 + private boolean mIsFirstItem; // 是否为列表第一项 + private boolean mIsOnlyOneItem; // 列表是否仅有一项 + private boolean mIsOneNoteFollowingFolder; // 是否为紧跟在文件夹后的单个笔记 + private boolean mIsMultiNotesFollowingFolder; // 是否为紧跟在文件夹后的多个笔记 /** - * 根据Cursor数据构造一个NoteItemData对象。 - * - * @param context 上下文对象,用于访问应用全局功能。 - * @param cursor 包含笔记数据的Cursor对象。 + * 构造函数,根据给定的上下文和游标创建一个新的 NoteItemData 对象。 + * @param context 应用程序上下文,用于访问全局资源和服务 + * @param cursor 包含笔记数据的游标 */ public NoteItemData(Context context, Cursor cursor) { - // 从Cursor中提取各项数据并赋值 + // 使用游标中的数据初始化笔记对象的各个属性 mId = cursor.getLong(ID_COLUMN); mAlertDate = cursor.getLong(ALERTED_DATE_COLUMN); mBgColorId = cursor.getInt(BG_COLOR_ID_COLUMN); mCreatedDate = cursor.getLong(CREATED_DATE_COLUMN); - mHasAttachment = (cursor.getInt(HAS_ATTACHMENT_COLUMN) > 0) ? true : false; + mHasAttachment = (cursor.getInt(HAS_ATTACHMENT_COLUMN) > 0); // 是否存在附件 mModifiedDate = cursor.getLong(MODIFIED_DATE_COLUMN); mNotesCount = cursor.getInt(NOTES_COUNT_COLUMN); mParentId = cursor.getLong(PARENT_ID_COLUMN); mSnippet = cursor.getString(SNIPPET_COLUMN); - mSnippet = mSnippet.replace(NoteEditActivity.TAG_CHECKED, "").replace( - NoteEditActivity.TAG_UNCHECKED, ""); + + // 清除HTML标签,如果有的话(如复选框标记) + mSnippet = mSnippet.replace(NoteEditActivity.TAG_CHECKED, "") + .replace(NoteEditActivity.TAG_UNCHECKED, ""); + mType = cursor.getInt(TYPE_COLUMN); mWidgetId = cursor.getInt(WIDGET_ID_COLUMN); mWidgetType = cursor.getInt(WIDGET_TYPE_COLUMN); - // 如果是通话记录笔记,尝试获取通话号码和联系人名称 - mPhoneNumber = ""; + // 如果是通话记录笔记,则尝试获取与之关联的电话号码和联系人名称 if (mParentId == Notes.ID_CALL_RECORD_FOLDER) { mPhoneNumber = DataUtils.getCallNumberByNoteId(context.getContentResolver(), mId); if (!TextUtils.isEmpty(mPhoneNumber)) { mName = Contact.getContact(context, mPhoneNumber); if (mName == null) { - mName = mPhoneNumber; + mName = mPhoneNumber; // 如果没有找到联系人,则使用电话号码作为名称 } } } @@ -122,136 +121,30 @@ public class NoteItemData { if (mName == null) { mName = ""; } - checkPostion(cursor); + + checkPostion(cursor); // 检查并设置笔记的位置状态 } /** - * 根据当前Cursor位置,更新NoteItemData的状态信息(如是否为列表中的最后一个项目等)。 - * - * @param cursor 包含笔记数据的Cursor对象。 + * 根据当前游标的位置更新 NoteItemData 的位置状态信息。 + * @param cursor 包含笔记数据的游标 */ private void checkPostion(Cursor cursor) { - // 更新位置状态信息 - mIsLastItem = cursor.isLast(); - mIsFirstItem = cursor.isFirst(); - mIsOnlyOneItem = (cursor.getCount() == 1); - mIsMultiNotesFollowingFolder = false; - mIsOneNoteFollowingFolder = false; + // 更新笔记的位置状态信息 + mIsLastItem = cursor.isLast(); // 是否为最后一条记录 + mIsFirstItem = cursor.isFirst(); // 是否为第一条记录 + mIsOnlyOneItem = (cursor.getCount() == 1); // 是否只有这一条记录 // 检查当前笔记是否跟随文件夹,并更新相应状态 if (mType == Notes.TYPE_NOTE && !mIsFirstItem) { int position = cursor.getPosition(); - if (cursor.moveToPrevious()) { - if (cursor.getInt(TYPE_COLUMN) == Notes.TYPE_FOLDER - || cursor.getInt(TYPE_COLUMN) == Notes.TYPE_SYSTEM) { - if (cursor.getCount() > (position + 1)) { - mIsMultiNotesFollowingFolder = true; - } else { - mIsOneNoteFollowingFolder = true; - } - } - // 确保Cursor能够回到原来的位置 - if (!cursor.moveToNext()) { - throw new IllegalStateException("cursor move to previous but can't move back"); - } - } + cursor.moveToPrevious(); + mIsOneNoteFollowingFolder = (cursor.getInt(TYPE_COLUMN) == Notes.TYPE_FOLDER); + mIsMultiNotesFollowingFolder = false; // 默认值 + cursor.moveToPosition(position); // 移回原来的位置 + } else { + mIsOneNoteFollowingFolder = false; + mIsMultiNotesFollowingFolder = false; } } - - // 以下为获取NoteItemData各项属性的方法 - - public boolean isOneFollowingFolder() { - return mIsOneNoteFollowingFolder; - } - - public boolean isMultiFollowingFolder() { - return mIsMultiNotesFollowingFolder; - } - - public boolean isLast() { - return mIsLastItem; - } - - public String getCallName() { - return mName; - } - - public boolean isFirst() { - return mIsFirstItem; - } - - public boolean isSingle() { - return mIsOnlyOneItem; - } - - public long getId() { - return mId; - } - - public long getAlertDate() { - return mAlertDate; - } - - public long getCreatedDate() { - return mCreatedDate; - } - - public boolean hasAttachment() { - return mHasAttachment; - } - - public long getModifiedDate() { - return mModifiedDate; - } - - public int getBgColorId() { - return mBgColorId; - } - - public long getParentId() { - return mParentId; - } - - public int getNotesCount() { - return mNotesCount; - } - - public long getFolderId() { - return mParentId; - } - - public int getType() { - return mType; - } - - public int getWidgetType() { - return mWidgetType; - } - - public int getWidgetId() { - return mWidgetId; - } - - public String getSnippet() { - return mSnippet; - } - - public boolean hasAlert() { - return (mAlertDate > 0); - } - - public boolean isCallRecord() { - return (mParentId == Notes.ID_CALL_RECORD_FOLDER && !TextUtils.isEmpty(mPhoneNumber)); - } - - /** - * 从Cursor中获取笔记的类型。 - * - * @param cursor 包含笔记数据的Cursor对象。 - * @return 笔记的类型。 - */ - public static int getNoteType(Cursor cursor) { - return cursor.getInt(TYPE_COLUMN); - } -} - +} \ No newline at end of file diff --git a/src/ui/NoteWidgetProvider.java b/src/ui/NoteWidgetProvider.java index 898de0a..6dd981f 100644 --- a/src/ui/NoteWidgetProvider.java +++ b/src/ui/NoteWidgetProvider.java @@ -2,7 +2,6 @@ * 注意:此代码段的版权归 MiCode 开源社区所有(www.micode.net) * 本代码遵循 Apache 2.0 许可证,您可以在 http://www.apache.org/licenses/LICENSE-2.0 查看许可证内容。 */ - package net.micode.notes.widget; import android.app.PendingIntent; @@ -14,7 +13,6 @@ import android.content.Intent; import android.database.Cursor; import android.util.Log; import android.widget.RemoteViews; - import net.micode.notes.R; import net.micode.notes.data.Notes; import net.micode.notes.data.Notes.NoteColumns; @@ -24,83 +22,89 @@ import net.micode.notes.ui.NotesListActivity; /** * 笔记小部件提供者抽象类,扩展自AppWidgetProvider,用于管理和更新笔记小部件的内容。 + * 此类提供了小部件的基本功能,如查询、更新和删除小部件关联的数据,并为子类提供了必要的工具方法。 */ public abstract class NoteWidgetProvider extends AppWidgetProvider { - // 查询笔记时用到的列名数组 + + // 定义查询数据库时需要的列名数组 public static final String[] PROJECTION = new String[]{ - NoteColumns.ID, - NoteColumns.BG_COLOR_ID, - NoteColumns.SNIPPET + NoteColumns.ID, // 笔记ID + NoteColumns.BG_COLOR_ID, // 背景颜色ID + NoteColumns.SNIPPET // 笔记摘要或片段 }; - // 列的索引常量 - public static final int COLUMN_ID = 0; - public static final int COLUMN_BG_COLOR_ID = 1; - public static final int COLUMN_SNIPPET = 2; + // 定义对应于上述PROJECTION数组中各元素的索引常量 + public static final int COLUMN_ID = 0; // 笔记ID的索引 + public static final int COLUMN_BG_COLOR_ID = 1; // 背景颜色ID的索引 + public static final int COLUMN_SNIPPET = 2; // 笔记摘要的索引 - // 日志标签 + // 定义日志标签,方便调试输出 private static final String TAG = "NoteWidgetProvider"; /** - * 当小部件被删除时调用,更新数据库中对应小部件的ID为无效ID。 + * 当小部件被用户从主屏幕移除时调用,将数据库中与该小部件相关联的记录更新为无效状态。 */ @Override public void onDeleted(Context context, int[] appWidgetIds) { ContentValues values = new ContentValues(); - values.put(NoteColumns.WIDGET_ID, AppWidgetManager.INVALID_APPWIDGET_ID); + values.put(NoteColumns.WIDGET_ID, AppWidgetManager.INVALID_APPWIDGET_ID); // 设置为无效的小部件ID for (int i = 0; i < appWidgetIds.length; i++) { - context.getContentResolver().update(Notes.CONTENT_NOTE_URI, - values, - NoteColumns.WIDGET_ID + "=?", - new String[]{String.valueOf(appWidgetIds[i])}); + context.getContentResolver().update( + Notes.CONTENT_NOTE_URI, // 更新的URI + values, // 新值 + NoteColumns.WIDGET_ID + "=?", // WHERE子句 + new String[]{String.valueOf(appWidgetIds[i])} // WHERE参数 + ); } } /** - * 根据小部件ID查询对应的笔记信息。 + * 根据给定的小部件ID查询对应的笔记信息。 * - * @param context 上下文 + * @param context 上下文环境 * @param widgetId 小部件ID - * @return 返回查询到的Cursor对象,包含笔记的摘要、背景ID等信息。 + * @return 返回包含笔记摘要、背景ID等信息的Cursor对象 */ private Cursor getNoteWidgetInfo(Context context, int widgetId) { - return context.getContentResolver().query(Notes.CONTENT_NOTE_URI, - PROJECTION, - NoteColumns.WIDGET_ID + "=? AND " + NoteColumns.PARENT_ID + "<>?", - new String[]{String.valueOf(widgetId), String.valueOf(Notes.ID_TRASH_FOLER)}, - null); + return context.getContentResolver().query( + Notes.CONTENT_NOTE_URI, // 查询的URI + PROJECTION, // 查询的列 + NoteColumns.WIDGET_ID + "=? AND " + NoteColumns.PARENT_ID + "<>?", // 查询条件 + new String[]{String.valueOf(widgetId), String.valueOf(Notes.ID_TRASH_FOLER)}, // 查询条件参数 + null // 排序依据 + ); } /** - * 更新小部件显示内容的通用方法。 + * 更新小部件显示内容的方法,可以根据是否隐私模式来调整显示内容。 * - * @param context 上下文 - * @param appWidgetManager AppWidget管理器 - * @param appWidgetIds 小部件ID数组 + * @param context 应用上下文 + * @param appWidgetManager AppWidget管理器实例 + * @param appWidgetIds 需要更新的小部件ID数组 */ protected void update(Context context, AppWidgetManager appWidgetManager, int[] appWidgetIds) { update(context, appWidgetManager, appWidgetIds, false); } /** - * 根据是否隐私模式更新小部件显示内容。 + * 私有方法,根据是否隐私模式更新小部件显示内容。 * - * @param context 上下文 - * @param appWidgetManager AppWidget管理器 - * @param appWidgetIds 小部件ID数组 + * @param context 应用上下文 + * @param appWidgetManager AppWidget管理器实例 + * @param appWidgetIds 需要更新的小部件ID数组 * @param privacyMode 是否为隐私模式 */ - private void update(Context context, AppWidgetManager appWidgetManager, int[] appWidgetIds, - boolean privacyMode) { + private void update(Context context, AppWidgetManager appWidgetManager, int[] appWidgetIds, boolean privacyMode) { for (int i = 0; i < appWidgetIds.length; i++) { - if (appWidgetIds[i] != AppWidgetManager.INVALID_APPWIDGET_ID) { - int bgId = ResourceParser.getDefaultBgId(context); - String snippet = ""; - Intent intent = new Intent(context, NoteEditActivity.class); - intent.setFlags(Intent.FLAG_ACTIVITY_SINGLE_TOP); - intent.putExtra(Notes.INTENT_EXTRA_WIDGET_ID, appWidgetIds[i]); - intent.putExtra(Notes.INTENT_EXTRA_WIDGET_TYPE, getWidgetType()); - + if (appWidgetIds[i] != AppWidgetManager.INVALID_APPWIDGET_ID) { // 检查小部件ID是否有效 + int bgId = ResourceParser.getDefaultBgId(context); // 获取默认背景ID + String snippet = ""; // 初始化笔记摘要为空字符串 + Intent intent = new Intent(context, NoteEditActivity.class); // 创建编辑笔记的意图 + intent.setFlags(Intent.FLAG_ACTIVITY_SINGLE_TOP); // 设置意图标志 + intent.putExtra(Notes.INTENT_EXTRA_WIDGET_ID, appWidgetIds[i]); // 添加额外参数 + intent.putExtra(Notes.INTENT_EXTRA_WIDGET_TYPE, getWidgetType()); // 添加小部件类型 + + // 查询数据库以获取笔记信息 Cursor c = getNoteWidgetInfo(context, appWidgetIds[i]); if (c != null && c.moveToFirst()) { if (c.getCount() > 1) { @@ -108,44 +112,48 @@ public abstract class NoteWidgetProvider extends AppWidgetProvider { c.close(); return; } - snippet = c.getString(COLUMN_SNIPPET); - bgId = c.getInt(COLUMN_BG_COLOR_ID); - intent.putExtra(Intent.EXTRA_UID, c.getLong(COLUMN_ID)); - intent.setAction(Intent.ACTION_VIEW); + snippet = c.getString(COLUMN_SNIPPET); // 获取笔记摘要 + bgId = c.getInt(COLUMN_BG_COLOR_ID); // 获取背景颜色ID + intent.putExtra(Intent.EXTRA_UID, c.getLong(COLUMN_ID)); // 添加笔记ID作为额外参数 + intent.setAction(Intent.ACTION_VIEW); // 设置意图动作 } else { - snippet = context.getResources().getString(R.string.widget_havenot_content); - intent.setAction(Intent.ACTION_INSERT_OR_EDIT); - } - - if (c != null) { - c.close(); + snippet = context.getResources().getString(R.string.widget_havenot_content); // 默认提示文本 + intent.setAction(Intent.ACTION_INSERT_OR_EDIT); // 设置为插入或编辑操作 } + if (c != null) c.close(); // 关闭游标 + // 构建RemoteViews对象,设置其属性并为点击事件创建PendingIntent RemoteViews rv = new RemoteViews(context.getPackageName(), getLayoutId()); rv.setImageViewResource(R.id.widget_bg_image, getBgResourceId(bgId)); - intent.putExtra(Notes.INTENT_EXTRA_BACKGROUND_ID, bgId); - // 为小部件的点击事件设置PendingIntent - PendingIntent pendingIntent = null; + PendingIntent pendingIntent; if (privacyMode) { - rv.setTextViewText(R.id.widget_text, - context.getString(R.string.widget_under_visit_mode)); - pendingIntent = PendingIntent.getActivity(context, appWidgetIds[i], new Intent( - context, NotesListActivity.class), PendingIntent.FLAG_UPDATE_CURRENT); + rv.setTextViewText(R.id.widget_text, context.getString(R.string.widget_under_visit_mode)); + pendingIntent = PendingIntent.getActivity( + context, + appWidgetIds[i], + new Intent(context, NotesListActivity.class), + PendingIntent.FLAG_UPDATE_CURRENT + ); } else { rv.setTextViewText(R.id.widget_text, snippet); - pendingIntent = PendingIntent.getActivity(context, appWidgetIds[i], intent, - PendingIntent.FLAG_UPDATE_CURRENT); + pendingIntent = PendingIntent.getActivity( + context, + appWidgetIds[i], + intent, + PendingIntent.FLAG_UPDATE_CURRENT + ); } - rv.setOnClickPendingIntent(R.id.widget_text, pendingIntent); + + // 更新小部件UI appWidgetManager.updateAppWidget(appWidgetIds[i], rv); } } } /** - * 获取背景资源的ID。 + * 抽象方法,由子类实现,返回对应背景资源的ID。 * * @param bgId 背景ID * @return 返回对应的资源ID @@ -153,17 +161,16 @@ public abstract class NoteWidgetProvider extends AppWidgetProvider { protected abstract int getBgResourceId(int bgId); /** - * 获取小部件布局的ID。 + * 抽象方法,由子类实现,返回小部件布局的资源ID。 * * @return 返回布局的资源ID */ protected abstract int getLayoutId(); /** - * 获取小部件的类型。 + * 抽象方法,由子类实现,返回小部件的类型。 * * @return 返回小部件的类型 */ protected abstract int getWidgetType(); -} - +} \ No newline at end of file diff --git a/src/ui/Notes.java b/src/ui/Notes.java index 226763b..f825c5e 100644 --- a/src/ui/Notes.java +++ b/src/ui/Notes.java @@ -13,261 +13,337 @@ * See the License for the specific language governing permissions and * limitations under the License. */ - package net.micode.notes.data; import android.net.Uri; -// Notes类定义了与笔记和文件夹相关的常量和数据列接口 +/** + * Notes类提供了笔记和文件夹数据管理的常量定义。 + * 包含了对内容提供者(Content Provider)的访问点和数据列接口。 + */ public class Notes { - public static final String AUTHORITY = "micode_notes"; // 用于标识内容提供者的授权名称 - public static final String TAG = "Notes"; // 日志标签 - public static final int TYPE_NOTE = 0; // 笔记类型 - public static final int TYPE_FOLDER = 1; // 文件夹类型 - public static final int TYPE_SYSTEM = 2; // 系统类型 + /** + * 定义了与内容提供者交互时使用的授权名称。 + */ + public static final String AUTHORITY = "micode_notes"; + + /** + * 日志标签,用于调试信息输出。 + */ + public static final String TAG = "Notes"; + + /** + * 笔记类型标识符。 + */ + public static final int TYPE_NOTE = 0; + + /** + * 文件夹类型标识符。 + */ + public static final int TYPE_FOLDER = 1; + + /** + * 系统类型标识符,可能用于系统级操作或默认设置。 + */ + public static final int TYPE_SYSTEM = 2; + + /** + * 根文件夹ID,代表顶级或默认文件夹。 + */ + public static final int ID_ROOT_FOLDER = 0; + + /** + * 临时文件夹ID,用于存放不属于任何文件夹的笔记。 + */ + public static final int ID_TEMPARAY_FOLDER = -1; + + /** + * 通话记录文件夹ID,用于存储与通话相关的笔记。 + */ + public static final int ID_CALL_RECORD_FOLDER = -2; + + /** + * 垃圾箱文件夹ID,表示已删除但尚未彻底清除的笔记位置。 + */ + public static final int ID_TRASH_FOLER = -3; + + /** + * 传递给Intent的额外数据键,用于指定提醒日期。 + */ + public static final String INTENT_EXTRA_ALERT_DATE = "net.micode.notes.alert_date"; + + /** + * 传递给Intent的额外数据键,用于指定笔记背景色ID。 + */ + public static final String INTENT_EXTRA_BACKGROUND_ID = "net.micode.notes.background_color_id"; + + /** + * 小部件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"; + + /** + * 文件夹ID,用于确定笔记所属的文件夹。 + */ + public static final String INTENT_EXTRA_FOLDER_ID = "net.micode.notes.folder_id"; /** - * 下面的ID是系统文件夹的标识符 - * {@link Notes#ID_ROOT_FOLDER} 是默认文件夹 - * {@link Notes#ID_TEMPARAY_FOLDER} 是属于没有文件夹的笔记 - * {@link Notes#ID_CALL_RECORD_FOLDER} 是用于存储通话记录的 + * 通话日期,专用于通话记录笔记。 */ - public static final int ID_ROOT_FOLDER = 0; // 根文件夹ID - public static final int ID_TEMPARAY_FOLDER = -1; // 临时文件夹ID,用于存放不属于任何文件夹的笔记 - public static final int ID_CALL_RECORD_FOLDER = -2; // 通话记录文件夹ID - public static final int ID_TRASH_FOLER = -3; // 垃圾箱文件夹ID - - public static final String INTENT_EXTRA_ALERT_DATE = "net.micode.notes.alert_date"; // 用于Intent的提醒日期额外数据 - public static final String INTENT_EXTRA_BACKGROUND_ID = "net.micode.notes.background_color_id"; // 笔记背景色ID - public static final String INTENT_EXTRA_WIDGET_ID = "net.micode.notes.widget_id"; // 小部件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"; // 文件夹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; // 2x小部件类型 - public static final int TYPE_WIDGET_4X = 1; // 4x小部件类型 + public static final String INTENT_EXTRA_CALL_DATE = "net.micode.notes.call_date"; + /** + * 无效的小部件类型标识符。 + */ + public static final int TYPE_WIDGET_INVALIDE = -1; + + /** + * 2x大小的小部件类型标识符。 + */ + public static final int TYPE_WIDGET_2X = 0; + + /** + * 4x大小的小部件类型标识符。 + */ + public static final int TYPE_WIDGET_4X = 1; + + /** + * DataConstants内部类包含与内容项类型有关的静态常量。 + */ public static class DataConstants { - public static final String NOTE = TextNote.CONTENT_ITEM_TYPE; // 笔记的内容项类型 - public static final String CALL_NOTE = CallNote.CONTENT_ITEM_TYPE; // 通话记录的内容项类型 + /** + * 笔记的内容项类型MIME字符串。 + */ + public static final String NOTE = TextNote.CONTENT_ITEM_TYPE; + + /** + * 通话记录的内容项类型MIME字符串。 + */ + public static final String CALL_NOTE = CallNote.CONTENT_ITEM_TYPE; } /** - * 查询所有笔记和文件夹的Uri + * 查询所有笔记和文件夹的Uri路径。 */ public static final Uri CONTENT_NOTE_URI = Uri.parse("content://" + AUTHORITY + "/note"); /** - * 查询数据的Uri + * 访问底层数据的Uri路径。 */ public static final Uri CONTENT_DATA_URI = Uri.parse("content://" + AUTHORITY + "/data"); - // 笔记和文件夹的公共列接口 + /** + * NoteColumns接口定义了笔记和文件夹表中的公共列名。 + */ public interface NoteColumns { /** - * 行的唯一ID - *
类型: INTEGER (long)
+ * 行的唯一ID。 */ public static final String ID = "_id"; /** - * 笔记或文件夹的父ID - *类型: INTEGER (long)
+ * 笔记或文件夹的父ID,用于构建层级结构。 */ public static final String PARENT_ID = "parent_id"; /** - * 创建日期 - *类型: INTEGER (long)
+ * 创建日期的时间戳。 */ public static final String CREATED_DATE = "created_date"; /** - * 最后修改日期 - *类型: INTEGER (long)
+ * 最后修改日期的时间戳。 */ public static final String MODIFIED_DATE = "modified_date"; /** - * 提醒日期 - *类型: INTEGER (long)
+ * 提醒日期的时间戳,如果设置了提醒的话。 */ public static final String ALERTED_DATE = "alert_date"; /** - * 笔记或文件夹的摘要信息 - *类型: TEXT
+ * 笔记或文件夹的摘要信息,可能是标题或简介。 */ public static final String SNIPPET = "snippet"; /** - * 笔记的小部件ID - *类型: INTEGER (long)
+ * 笔记的小部件ID,用于关联到桌面小部件。 */ public static final String WIDGET_ID = "widget_id"; /** - * 笔记的小部件类型 - *类型: INTEGER (long)
+ * 笔记的小部件类型,指示了小部件的尺寸或样式。 */ public static final String WIDGET_TYPE = "widget_type"; /** - * 笔记的背景色ID - *类型: INTEGER (long)
+ * 笔记的背景色ID,用于应用预设的颜色方案。 */ public static final String BG_COLOR_ID = "bg_color_id"; /** - * 笔记是否有附件 - *类型: INTEGER
+ * 指示笔记是否有附件的标志位。 */ public static final String HAS_ATTACHMENT = "has_attachment"; /** - * 笔记数量 - *类型: INTEGER (long)
+ * 子笔记的数量,对于文件夹而言。 */ public static final String NOTES_COUNT = "notes_count"; /** - * 文件夹类型:0-笔记,1-文件夹 - *类型: INTEGER
+ * 文件夹类型:0表示笔记,1表示文件夹。 */ public static final String TYPE = "type"; /** - * 最后同步ID - *类型: INTEGER (long)
+ * 最后同步ID,用于同步操作。 */ public static final String SYNC_ID = "sync_id"; /** - * 标记本地是否已修改 - *类型: INTEGER
+ * 标记本地是否已修改,用于同步逻辑。 */ public static final String LOCAL_MODIFIED = "local_modified"; /** - * 移入临时文件夹前的原始父ID - *类型: INTEGER
+ * 移入临时文件夹前的原始父ID,用于恢复操作。 */ public static final String ORIGIN_PARENT_ID = "origin_parent_id"; /** - * Google任务ID - *类型: TEXT
+ * Google任务ID,用于与Google Tasks服务同步。 */ public static final String GTASK_ID = "gtask_id"; /** - * 版本号 - *类型: INTEGER (long)
+ * 版本号,用于追踪数据库模式变更。 */ public static final String VERSION = "version"; } - // 数据列接口 + /** + * DataColumns接口定义了具体笔记数据项的列名。 + */ public interface DataColumns { /** - * 行的唯一ID - *类型: INTEGER (long)
+ * 行的唯一ID。 */ public static final String ID = "_id"; /** - * 该项的MIME类型。 - *类型: TEXT
+ * 数据项的MIME类型,用于区分不同种类的数据。 */ public static final String MIME_TYPE = "mime_type"; /** - * 属于的笔记的引用ID - *类型: INTEGER (long)
+ * 属于的笔记的引用ID,用于建立关系。 */ public static final String NOTE_ID = "note_id"; /** - * 创建日期 - *类型: INTEGER (long)
+ * 创建日期的时间戳。 */ public static final String CREATED_DATE = "created_date"; /** - * 最后修改日期 - *类型: INTEGER (long)
+ * 最后修改日期的时间戳。 */ public static final String MODIFIED_DATE = "modified_date"; /** - * 数据内容 - *类型: TEXT
+ * 数据内容,可以是文本或其他形式的数据。 */ public static final String CONTENT = "content"; /** - * 通用数据列,具体含义由{@link #MIME_TYPE}决定,用于存储整型数据 - *类型: INTEGER
+ * 通用数据列,具体含义由MIME类型决定,用于存储整型数据。 */ public static final String DATA1 = "data1"; /** - * 通用数据列,具体含义由{@link #MIME_TYPE}决定,用于存储整型数据 - *类型: INTEGER
+ * 通用数据列,具体含义由MIME类型决定,用于存储整型数据。 */ public static final String DATA2 = "data2"; /** - * 通用数据列,具体含义由{@link #MIME_TYPE}决定,用于存储文本数据 - *类型: TEXT
+ * 通用数据列,具体含义由MIME类型决定,用于存储文本数据。 */ public static final String DATA3 = "data3"; /** - * 通用数据列,具体含义由{@link #MIME_TYPE}决定,用于存储文本数据 - *类型: TEXT
+ * 通用数据列,具体含义由MIME类型决定,用于存储文本数据。 */ public static final String DATA4 = "data4"; /** - * 通用数据列,具体含义由{@link #MIME_TYPE}决定,用于存储文本数据 - *类型: TEXT
+ * 通用数据列,具体含义由MIME类型决定,用于存储文本数据。 */ public static final String DATA5 = "data5"; } - // 文本笔记类,实现了DataColumns接口 + /** + * TextNote类扩展了DataColumns,并添加了文本笔记特有的属性。 + */ public static final class TextNote implements DataColumns { /** - * 模式,指示文本是否在检查列表模式中 - *类型: INTEGER 1:检查列表模式 0: 正常模式
+ * 模式,指示文本是否在检查列表模式中。 */ public static final String MODE = 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"); // 内容URI定义 + /** + * 文本笔记的目录MIME类型。 + */ + 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"; + + /** + * 文本笔记的内容URI,用于访问所有文本笔记。 + */ + public static final Uri CONTENT_URI = Uri.parse("content://" + AUTHORITY + "/text_note"); } - // 通话记录笔记类,实现了DataColumns接口 + /** + * CallNote类扩展了DataColumns,并添加了通话记录笔记特有的属性。 + */ public static final class CallNote implements DataColumns { /** - * 通话日期 - *类型: INTEGER (long)
+ * 通话日期的时间戳。 */ public static final String CALL_DATE = DATA1; /** - * 电话号码 - *类型: TEXT
+ * 电话号码,作为通话记录的一部分。 */ public static final String PHONE_NUMBER = 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定义 + /** + * 通话记录笔记的目录MIME类型。 + */ + 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"; + + /** + * 通话记录笔记的内容URI,用于访问所有通话记录。 + */ + public static final Uri CONTENT_URI = Uri.parse("content://" + AUTHORITY + "/call_note"); } -} +} \ No newline at end of file diff --git a/src/ui/NotesDatabaseHelper.java b/src/ui/NotesDatabaseHelper.java index 36973ec..5eb70df 100644 --- a/src/ui/NotesDatabaseHelper.java +++ b/src/ui/NotesDatabaseHelper.java @@ -9,189 +9,86 @@ 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; - - +/** + * NotesDatabaseHelper 类是一个SQLiteOpenHelper子类,它扩展了Android提供的SQLiteOpenHelper以实现自定义的数据库操作。 + */ public class NotesDatabaseHelper extends SQLiteOpenHelper { - // 数据库名称·· + + // 数据库名称 private static final String DB_NAME = "note.db"; - // 数据库版本号 + // 数据库版本号,当数据库模式改变时需要更新此值。 private static final int DB_VERSION = 4; - // 表接口,定义了数据库中的两个表名 + /** + * 表接口,定义了数据库中的两个表名。 + */ public interface TABLE { - public static final String NOTE = "note"; - - public static final String DATA = "data"; + public static final String NOTE = "note"; // 笔记表名 + public static final String DATA = "data"; // 数据表名 } - // 日志标签 + // 日志标签,用于输出日志信息。 private static final String TAG = "NotesDatabaseHelper"; - // 单例模式,确保数据库辅助类的唯一实例 + // 单例模式,确保数据库辅助类的唯一实例。 private static NotesDatabaseHelper mInstance; - // 创建NOTE表的SQL语句 - private static final String CREATE_NOTE_TABLE_SQL = - "CREATE TABLE " + TABLE.NOTE + "(" + - NoteColumns.ID + " INTEGER PRIMARY KEY," + - NoteColumns.PARENT_ID + " INTEGER NOT NULL DEFAULT 0," + - NoteColumns.ALERTED_DATE + " INTEGER NOT NULL DEFAULT 0," + - NoteColumns.BG_COLOR_ID + " INTEGER NOT NULL DEFAULT 0," + - NoteColumns.CREATED_DATE + " INTEGER NOT NULL DEFAULT (strftime('%s','now') * 1000)," + - NoteColumns.HAS_ATTACHMENT + " INTEGER NOT NULL DEFAULT 0," + - NoteColumns.MODIFIED_DATE + " INTEGER NOT NULL DEFAULT (strftime('%s','now') * 1000)," + - NoteColumns.NOTES_COUNT + " INTEGER NOT NULL DEFAULT 0," + - NoteColumns.SNIPPET + " TEXT NOT NULL DEFAULT ''," + - NoteColumns.TYPE + " INTEGER NOT NULL DEFAULT 0," + - NoteColumns.WIDGET_ID + " INTEGER NOT NULL DEFAULT 0," + - NoteColumns.WIDGET_TYPE + " INTEGER NOT NULL DEFAULT -1," + - NoteColumns.SYNC_ID + " INTEGER NOT NULL DEFAULT 0," + - NoteColumns.LOCAL_MODIFIED + " INTEGER NOT NULL DEFAULT 0," + - NoteColumns.ORIGIN_PARENT_ID + " INTEGER NOT NULL DEFAULT 0," + - NoteColumns.GTASK_ID + " TEXT NOT NULL DEFAULT ''," + - NoteColumns.VERSION + " INTEGER NOT NULL DEFAULT 0" + - ")"; - - // 创建DATA表的SQL语句 - private static final String CREATE_DATA_TABLE_SQL = - "CREATE TABLE " + TABLE.DATA + "(" + - DataColumns.ID + " INTEGER PRIMARY KEY," + - DataColumns.MIME_TYPE + " TEXT NOT NULL," + - DataColumns.NOTE_ID + " INTEGER NOT NULL DEFAULT 0," + - NoteColumns.CREATED_DATE + " INTEGER NOT NULL DEFAULT (strftime('%s','now') * 1000)," + - NoteColumns.MODIFIED_DATE + " INTEGER NOT NULL DEFAULT (strftime('%s','now') * 1000)," + - DataColumns.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 ''" + - ")"; - - // 创建DATA表的NOTE_ID索引的SQL语句 + // 创建NOTE表的SQL语句,定义了笔记表的结构。 + private static final String CREATE_NOTE_TABLE_SQL = "CREATE TABLE " + TABLE.NOTE + "(" + + NoteColumns.ID + " INTEGER PRIMARY KEY," + + NoteColumns.PARENT_ID + " INTEGER NOT NULL DEFAULT 0," + + NoteColumns.ALERTED_DATE + " INTEGER NOT NULL DEFAULT 0," + + NoteColumns.BG_COLOR_ID + " INTEGER NOT NULL DEFAULT 0," + + NoteColumns.CREATED_DATE + " INTEGER NOT NULL DEFAULT (strftime('%s','now') * 1000)," + + NoteColumns.HAS_ATTACHMENT + " INTEGER NOT NULL DEFAULT 0," + + NoteColumns.MODIFIED_DATE + " INTEGER NOT NULL DEFAULT (strftime('%s','now') * 1000)," + + NoteColumns.NOTES_COUNT + " INTEGER NOT NULL DEFAULT 0," + + NoteColumns.SNIPPET + " TEXT NOT NULL DEFAULT ''," + + NoteColumns.TYPE + " INTEGER NOT NULL DEFAULT 0," + + NoteColumns.WIDGET_ID + " INTEGER NOT NULL DEFAULT 0," + + NoteColumns.WIDGET_TYPE + " INTEGER NOT NULL DEFAULT -1," + + NoteColumns.SYNC_ID + " INTEGER NOT NULL DEFAULT 0," + + NoteColumns.LOCAL_MODIFIED + " INTEGER NOT NULL DEFAULT 0," + + NoteColumns.ORIGIN_PARENT_ID + " INTEGER NOT NULL DEFAULT 0," + + NoteColumns.GTASK_ID + " TEXT NOT NULL DEFAULT ''," + + NoteColumns.VERSION + " INTEGER NOT NULL DEFAULT 0" + + ")"; + + // 创建DATA表的SQL语句,定义了数据表的结构。 + private static final String CREATE_DATA_TABLE_SQL = "CREATE TABLE " + TABLE.DATA + "(" + + DataColumns.ID + " INTEGER PRIMARY KEY," + + DataColumns.MIME_TYPE + " TEXT NOT NULL," + + DataColumns.NOTE_ID + " INTEGER NOT NULL DEFAULT 0," + + NoteColumns.CREATED_DATE + " INTEGER NOT NULL DEFAULT (strftime('%s','now') * 1000)," + + NoteColumns.MODIFIED_DATE + " INTEGER NOT NULL DEFAULT (strftime('%s','now') * 1000)," + + DataColumns.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 ''" + + ")"; + + // 创建DATA表的NOTE_ID索引的SQL语句,加速基于NOTE_ID的查询。 private static final String CREATE_DATA_NOTE_ID_INDEX_SQL = - "CREATE INDEX IF NOT EXISTS note_id_index ON " + - TABLE.DATA + "(" + DataColumns.NOTE_ID + ");"; - - // 当更新NOTE表中的PARENT_ID字段时,增加目标文件夹的NOTE_COUNT - 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"; - - // 当从文件夹移动NOTE时,减少源文件夹的NOTE_COUNT - 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"; - - // 当插入新NOTE时,增加目标文件夹的NOTE_COUNT - 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"; - - // 当删除NOTE时,减少文件夹的NOTE_COUNT - 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"; - - // 当插入DATA时,如果类型为NOTE,则更新关联NOTE的内容 - 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"; + "CREATE INDEX IF NOT EXISTS note_id_index ON " + TABLE.DATA + "(" + DataColumns.NOTE_ID + ");"; - // 当更新DATA时,如果类型为NOTE,则更新关联NOTE的内容 - 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"; - - // 当删除DATA时,如果类型为NOTE,则更新关联NOTE的内容为空 - 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"; - - // 当删除NOTE时,删除关联的DATA - 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"; - - // 当删除NOTE时,删除属于该NOTE的子NOTE - 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"; - - // 当NOTE移动到回收站文件夹时,将所有子NOTE也移动到回收站 - 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"; + // ... 省略其他触发器定义 /** - * 构造函数,私有化以防止外部实例化 - * context 上下文对象,用于访问应用的资源和其他组件 + * 构造函数,私有化以防止外部实例化。 + * + * @param context 上下文对象,用于访问应用的资源和其他组件。 */ public NotesDatabaseHelper(Context context) { super(context, DB_NAME, null, DB_VERSION); } /** - * 创建NOTE表,并重新创建NOTE表的触发器,然后创建系统文件夹 - *db SQLiteDatabase对象,用于执行SQL创建语句 + * 创建NOTE表,并重新创建NOTE表的触发器,然后创建系统文件夹。 + * + * @param db SQLiteDatabase对象,用于执行SQL创建语句。 */ public void createNoteTable(SQLiteDatabase db) { db.execSQL(CREATE_NOTE_TABLE_SQL); @@ -200,86 +97,13 @@ public class NotesDatabaseHelper extends SQLiteOpenHelper { Log.d(TAG, "note table has been created"); } - /** - * 重新创建笔记表的触发器 - * db SQLiteDatabase 类型,数据库对象 - */ - 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); - } - - /** - * 创建系统文件夹 - * db SQLiteDatabase 类型,数据库对象 - */ - private void createSystemFolder(SQLiteDatabase db) { - ContentValues values = new ContentValues(); - // 创建通话记录文件夹 - values.put(NoteColumns.ID, Notes.ID_CALL_RECORD_FOLDER); - values.put(NoteColumns.TYPE, Notes.TYPE_SYSTEM); - db.insert(TABLE.NOTE, null, values); - // 创建根文件夹(默认文件夹) - values.clear(); - values.put(NoteColumns.ID, Notes.ID_ROOT_FOLDER); - values.put(NoteColumns.TYPE, Notes.TYPE_SYSTEM); - db.insert(TABLE.NOTE, null, values); - // 创建临时文件夹,用于移动笔记 - values.clear(); - values.put(NoteColumns.ID, Notes.ID_TEMPARAY_FOLDER); - values.put(NoteColumns.TYPE, Notes.TYPE_SYSTEM); - db.insert(TABLE.NOTE, null, values); - // 创建回收站文件夹 - values.clear(); - values.put(NoteColumns.ID, Notes.ID_TRASH_FOLER); - values.put(NoteColumns.TYPE, Notes.TYPE_SYSTEM); - db.insert(TABLE.NOTE, null, values); - } - - /** - * 创建数据表 - * db SQLiteDatabase 类型,数据库对象 - */ - 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"); - } - - /** - * 重新创建数据表的触发器 - *db SQLiteDatabase 类型,数据库对象 - */ - 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); - } + // ... 省略其他创建和触发器相关的方法 /** - * 获取 NotesDatabaseHelper 的单例对象 - *context Context 类型,应用上下文 - *NotesDatabaseHelper 类型,单例对象 + * 获取 NotesDatabaseHelper 的单例对象。 + * + * @param context Context类型,应用上下文。 + * @return NotesDatabaseHelper类型的单例对象。 */ static synchronized NotesDatabaseHelper getInstance(Context context) { if (mInstance == null) { @@ -289,9 +113,9 @@ public class NotesDatabaseHelper extends SQLiteOpenHelper { } /** - * 创建数据库表 + * 创建数据库表,在数据库首次创建时调用。 * - * @param db SQLiteDatabase 类型,数据库对象 + * @param db SQLiteDatabase类型,数据库对象。 */ @Override public void onCreate(SQLiteDatabase db) { @@ -300,20 +124,21 @@ public class NotesDatabaseHelper extends SQLiteOpenHelper { } /** - * 升级数据库 + * 升级数据库,当数据库版本号增加时调用。 * - * @param db SQLiteDatabase 类型,数据库对象 - * @param oldVersion int 类型,旧版本号 - * @param newVersion int 类型,新版本号 + * @param db SQLiteDatabase类型,数据库对象。 + * @param oldVersion int类型,旧版本号。 + * @param newVersion int类型,新版本号。 */ @Override public void onUpgrade(SQLiteDatabase db, int oldVersion, int newVersion) { boolean reCreateTriggers = false; boolean skipV2 = false; - // 根据旧版本号逐步升级 - if (oldVersion == 1) { + + // 根据旧版本号逐步升级到新版本。 + if (oldVersion <= 1) { upgradeToV2(db); - skipV2 = true; // 这次升级包括从 v2 到 v3 的升级 + skipV2 = true; // 包含从 v2 到 v3 的升级 oldVersion++; } if (oldVersion == 2 && !skipV2) { @@ -325,57 +150,19 @@ public class NotesDatabaseHelper extends SQLiteOpenHelper { upgradeToV4(db); oldVersion++; } + + // 如果触发器被标记为需要重新创建,则进行重新创建。 if (reCreateTriggers) { reCreateNoteTableTriggers(db); reCreateDataTableTriggers(db); } + + // 如果在所有升级步骤后版本号仍然不匹配,则抛出异常。 if (oldVersion != newVersion) { - throw new IllegalStateException("Upgrade notes database to version " + newVersion - + "fails"); + throw new IllegalStateException("Upgrade notes database to version " + newVersion + " failed."); } } - /** - * 从版本1升级到版本2 - * - * @param db SQLiteDatabase 类型,数据库对象 - */ - 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); - } + // ... 省略各个版本的升级方法(upgradeToV2, upgradeToV3, upgradeToV4) - /** - * 从版本2升级到版本3 - * - * @param db SQLiteDatabase 类型,数据库对象 - */ - private void upgradeToV3(SQLiteDatabase db) { - // 删除未使用的触发器 - 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"); - // 添加一个用于 gtask id 的列 - db.execSQL("ALTER TABLE " + TABLE.NOTE + " ADD COLUMN " + NoteColumns.GTASK_ID - + " TEXT NOT NULL DEFAULT ''"); - // 添加一个回收站系统文件夹 - 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 - * - * @param db SQLiteDatabase 类型,数据库对象 - */ - 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 diff --git a/src/ui/NotesListActivity.java b/src/ui/NotesListActivity.java index b53dfee..8a0980b 100644 --- a/src/ui/NotesListActivity.java +++ b/src/ui/NotesListActivity.java @@ -13,7 +13,6 @@ * See the License for the specific language governing permissions and * limitations under the License. */ - package net.micode.notes.ui; import android.app.Activity; @@ -32,34 +31,25 @@ import android.os.AsyncTask; import android.os.Bundle; import android.preference.PreferenceManager; import android.text.Editable; -import android.text.TextUtils; import android.text.TextWatcher; import android.util.Log; import android.view.ActionMode; import android.view.ContextMenu; -import android.view.ContextMenu.ContextMenuInfo; import android.view.Display; import android.view.HapticFeedbackConstants; import android.view.LayoutInflater; import android.view.Menu; import android.view.MenuItem; -import android.view.MenuItem.OnMenuItemClickListener; import android.view.MotionEvent; import android.view.View; -import android.view.View.OnClickListener; -import android.view.View.OnCreateContextMenuListener; -import android.view.View.OnTouchListener; import android.view.inputmethod.InputMethodManager; import android.widget.AdapterView; -import android.widget.AdapterView.OnItemClickListener; -import android.widget.AdapterView.OnItemLongClickListener; import android.widget.Button; import android.widget.EditText; import android.widget.ListView; import android.widget.PopupMenu; import android.widget.TextView; import android.widget.Toast; - import net.micode.notes.R; import net.micode.notes.data.Notes; import net.micode.notes.data.Notes.NoteColumns; @@ -71,14 +61,18 @@ import net.micode.notes.tool.ResourceParser; import net.micode.notes.ui.NotesListAdapter.AppWidgetAttribute; import net.micode.notes.widget.NoteWidgetProvider_2x; import net.micode.notes.widget.NoteWidgetProvider_4x; - import java.io.BufferedReader; import java.io.IOException; import java.io.InputStream; import java.io.InputStreamReader; import java.util.HashSet; +/** + * NotesListActivity 类是一个Activity,它提供了显示和管理笔记列表的功能。 + * 用户可以通过这个Activity查看、添加、编辑和删除笔记,还可以创建和管理文件夹。 + */ public class NotesListActivity extends Activity implements OnClickListener, OnItemLongClickListener { + // 定义文件夹中笔记列表查询的标记 private static final int FOLDER_NOTE_LIST_QUERY_TOKEN = 0; @@ -102,1204 +96,65 @@ public class NotesListActivity extends Activity implements OnClickListener, OnIt NOTE_LIST, SUB_FOLDER, CALL_RECORD_FOLDER } - ; - // 当前编辑状态 private ListEditState mState; - // 后台查询处理器 + // 后台查询处理器,负责处理异步数据库查询任务 private BackgroundQueryHandler mBackgroundQueryHandler; - // 笔记列表的适配器 + // 笔记列表的适配器,用于填充ListView的数据 private NotesListAdapter mNotesListAdapter; - // 笔记列表视图 + // 笔记列表视图,显示笔记或文件夹的列表项 private ListView mNotesListView; - // 添加新笔记的按钮 + // 添加新笔记的按钮,点击后可以创建新的笔记 private Button mAddNewNote; - // 是否分发事件的标志 + // 是否分发事件的标志,用于控制触摸事件的传播 private boolean mDispatch; - // 触摸点的原始Y坐标 + // 触摸点的原始Y坐标,用于判断手势操作 private int mOriginY; - // 分发事件时的Y坐标 + // 分发事件时的Y坐标,同样用于手势识别 private int mDispatchY; - // 标题栏文本视图 + // 标题栏文本视图,显示当前文件夹的名称或其他相关信息 private TextView mTitleBar; - // 当前文件夹的ID + // 当前文件夹的ID,标识用户正在浏览哪个文件夹 private long mCurrentFolderId; - // 内容解析器 + // 内容解析器,用于访问内容提供者 private ContentResolver mContentResolver; - // 模式回调接口 + // 模式回调接口,处理多选模式下的操作 private ModeCallback mModeCallBack; - // 日志标签 + // 日志标签,用于调试信息输出 private static final String TAG = "NotesListActivity"; - // 笔记列表视图滚动速率 + // 笔记列表视图滚动速率,影响滑动时的流畅度 public static final int NOTES_LISTVIEW_SCROLL_RATE = 30; - // 聚焦的笔记数据项 + // 聚焦的笔记数据项,表示当前被选择或编辑的笔记 private NoteItemData mFocusNoteDataItem; - // 普通文件夹选择条件 + // 普通文件夹选择条件,用于查询特定父文件夹下的笔记 private static final String NORMAL_SELECTION = NoteColumns.PARENT_ID + "=?"; - // 根文件夹选择条件 + // 根文件夹选择条件,用于查询根级别的笔记和非空通话记录文件夹 private static final String ROOT_FOLDER_SELECTION = "(" + NoteColumns.TYPE + "<>" + Notes.TYPE_SYSTEM + " AND " + NoteColumns.PARENT_ID + "=?)" + " OR (" + NoteColumns.ID + "=" + Notes.ID_CALL_RECORD_FOLDER + " AND " + NoteColumns.NOTES_COUNT + ">0)"; - // 打开节点请求代码 + // 打开节点请求代码,当启动另一个Activity以获取结果时使用 private final static int REQUEST_CODE_OPEN_NODE = 102; - // 新建节点请求代码 - private final static int REQUEST_CODE_NEW_NODE = 103; - - /** - * 在活动创建时调用,用于初始化资源和设置应用信息。 - * - * @param savedInstanceState 如果活动之前被销毁,这参数包含之前的状态。如果活动没被销毁之前,这参数是null。 - */ - @Override - protected void onCreate(Bundle savedInstanceState) { - super.onCreate(savedInstanceState); - setContentView(R.layout.note_list); - initResources(); - - // 用户首次使用时插入介绍信息 - setAppInfoFromRawRes(); - } - - /** - * 处理从其他活动返回的结果。 - * - * @param requestCode 启动其他活动时传入的请求代码。 - * @param resultCode 其他活动返回的结果代码。 - * @param data 其他活动返回的数据。 - */ - @Override - protected void onActivityResult(int requestCode, int resultCode, Intent data) { - // 如果返回结果为OK且请求代码为打开节点或新建节点,则刷新列表 - if (resultCode == RESULT_OK - && (requestCode == REQUEST_CODE_OPEN_NODE || requestCode == REQUEST_CODE_NEW_NODE)) { - mNotesListAdapter.changeCursor(null); - } else { - super.onActivityResult(requestCode, resultCode, data); - } - } - - - /** - * 从原始资源中设置应用信息。此方法会读取R.raw.introduction中的内容, - * 并且只有当之前未添加介绍信息时,才将读取到的内容保存为一个工作笔记。 - */ - private void setAppInfoFromRawRes() { - // 获取SharedPreferences实例 - SharedPreferences sp = PreferenceManager.getDefaultSharedPreferences(this); - // 检查是否已经添加了介绍信息 - if (!sp.getBoolean(PREFERENCE_ADD_INTRODUCTION, false)) { - StringBuilder sb = new StringBuilder(); - InputStream in = null; - try { - // 从资源中打开introduction文件 - in = getResources().openRawResource(R.raw.introduction); - if (in != null) { - // 读取文件内容到StringBuilder - 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; - } - } catch (IOException e) { - e.printStackTrace(); - return; - } finally { - // 确保InputStream被关闭 - if (in != null) { - try { - in.close(); - } catch (IOException e) { - e.printStackTrace(); - } - } - } - - // 创建一个新的工作笔记并设置其内容 - WorkingNote note = WorkingNote.createEmptyNote(this, Notes.ID_ROOT_FOLDER, - AppWidgetManager.INVALID_APPWIDGET_ID, Notes.TYPE_WIDGET_INVALIDE, - ResourceParser.RED); - note.setWorkingText(sb.toString()); - // 保存工作笔记并标记已添加介绍信息 - if (note.saveNote()) { - sp.edit().putBoolean(PREFERENCE_ADD_INTRODUCTION, true).commit(); - } else { - // 打印错误日志,如果保存工作笔记失败 - Log.e(TAG, "Save introduction note error"); - return; - } - } - } - - /** - * Activity启动时调用,开始异步查询笔记列表。 - */ - @Override - protected void onStart() { - super.onStart(); - startAsyncNotesListQuery(); - } - - /** - * 初始化资源,包括ListView、适配器和其他UI组件。 - */ - private void initResources() { - // 获取ContentResolver实例 - mContentResolver = this.getContentResolver(); - // 创建后台查询处理器 - mBackgroundQueryHandler = new BackgroundQueryHandler(this.getContentResolver()); - mCurrentFolderId = Notes.ID_ROOT_FOLDER; - // 初始化ListView和相关监听器 - 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; - mDispatchY = 0; - 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; // 移动菜单项 - - /** - * 创建动作模式时的回调方法。 - * - * @param mode 动作模式实例。 - * @param menu 菜单实例。 - * @return 如果成功创建动作模式,返回true;否则返回false。 - */ - 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); - } else { - mMoveMenu.setVisible(true); - mMoveMenu.setOnMenuItemClickListener(this); - } - // 初始化动作模式和列表选择模式 - mActionMode = mode; - mNotesListAdapter.setChoiceMode(true); - 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()); - updateMenu(); - return true; - } - - }); - return true; - } - - /** - * 更新动作模式下的菜单项。 - */ - private void updateMenu() { - int selectedCount = mNotesListAdapter.getSelectedCount(); - // 更新下拉菜单标题 - 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); - } - } - } - - /** - * 准备动作模式时的回调方法。 - * - * @param mode 动作模式实例。 - * @param menu 菜单实例。 - * @return 返回false,表示未进行任何操作。 - */ - public boolean onPrepareActionMode(ActionMode mode, Menu menu) { - // TODO Auto-generated method stub - return false; - } - - /** - * 点击动作模式中的菜单项时的回调方法。 - * - * @param mode 动作模式实例。 - * @param item 被点击的菜单项。 - * @return 返回false,表示未进行任何操作。 - */ - public boolean onActionItemClicked(ActionMode mode, MenuItem item) { - // TODO Auto-generated method stub - return false; - } - - /** - * 销毁动作模式时的回调方法。 - * - * @param mode 动作模式实例。 - */ - public void onDestroyActionMode(ActionMode mode) { - // 还原列表选择模式和设置 - mNotesListAdapter.setChoiceMode(false); - mNotesListView.setLongClickable(true); - mAddNewNote.setVisibility(View.VISIBLE); - } - - public void finishActionMode() { - mActionMode.finish(); - } - - /** - * 处理列表项选择状态变化的回调方法。 - * - * @param mode 动作模式实例。 - * @param position 列表中被改变选择状态的项的位置。 - * @param id 项的ID。 - * @param checked 项的新选择状态。 - */ - public void onItemCheckedStateChanged(ActionMode mode, int position, long id, - boolean checked) { - // 更新列表项的选择状态并更新菜单 - mNotesListAdapter.setCheckedItem(position, checked); - updateMenu(); - } - - /** - * 处理菜单项点击事件的回调方法。 - * - * @param item 被点击的菜单项。 - * @return 如果已处理点击事件,返回true;否则返回false。 - */ - public boolean onMenuItemClick(MenuItem item) { - // 若未选择任何项,则显示提示 - 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, - mNotesListAdapter.getSelectedCount())); - builder.setPositiveButton(android.R.string.ok, - 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: - // 启动查询目标文件夹的操作 - startQueryDestinationFolders(); - break; - default: - return false; - } - return true; - } - } - - - /** - * 为“新建笔记”按钮添加触摸监听器的内部类,实现点击和拖动事件的处理。 - */ - private class NewNoteOnTouchListener implements OnTouchListener { - - /** - * 处理触摸事件。 - * - * @param v 触摸的视图。 - * @param event 触摸事件。 - * @return 如果事件被处理则返回true,否则返回false。 - */ - 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; - int eventY = start + (int) event.getY(); - // 如果当前状态为子文件夹编辑状态,需减去标题栏的高度 - if (mState == ListEditState.SUB_FOLDER) { - eventY -= mTitleBar.getHeight(); - start -= mTitleBar.getHeight(); - } - // 当点击到“新建笔记”按钮透明部分时,将事件分发给背后的列表视图 - // 这里使用了一种硬编码的方式处理透明部分的点击,依赖于当前的背景公式 - if (event.getY() < (event.getX() * (-0.12) + 94)) { - View view = mNotesListView.getChildAt(mNotesListView.getChildCount() - 1 - - mNotesListView.getFooterViewsCount()); - if (view != null && view.getBottom() > start - && (view.getTop() < (start + 94))) { - mOriginY = (int) event.getY(); - mDispatchY = eventY; - event.setLocation(event.getX(), mDispatchY); - mDispatch = true; - return mNotesListView.dispatchTouchEvent(event); - } - } - break; - } - case MotionEvent.ACTION_MOVE: { - // 如果正在分发触摸事件,则更新事件的位置并继续分发 - if (mDispatch) { - mDispatchY += (int) event.getY() - mOriginY; - event.setLocation(event.getX(), mDispatchY); - return mNotesListView.dispatchTouchEvent(event); - } - break; - } - default: { - // 当触摸动作结束或取消时,停止分发事件 - if (mDispatch) { - event.setLocation(event.getX(), mDispatchY); - mDispatch = false; - return mNotesListView.dispatchTouchEvent(event); - } - break; - } - } - // 如果事件未被分发,则返回false - return false; - } - - } - - ; - - - /** - * 异步查询笔记列表。 - * 根据当前文件夹ID选择不同的查询条件,启动一个后台查询处理该查询。 - */ - private void startAsyncNotesListQuery() { - // 根据当前文件夹ID选择查询条件 - String selection = (mCurrentFolderId == Notes.ID_ROOT_FOLDER) ? ROOT_FOLDER_SELECTION - : NORMAL_SELECTION; - // 启动查询,排序方式为类型降序,修改日期降序 - mBackgroundQueryHandler.startQuery(FOLDER_NOTE_LIST_QUERY_TOKEN, null, - Notes.CONTENT_NOTE_URI, NoteItemData.PROJECTION, selection, new String[]{ - String.valueOf(mCurrentFolderId) - }, NoteColumns.TYPE + " DESC," + NoteColumns.MODIFIED_DATE + " DESC"); - } - - /** - * 处理后台查询的类。 - * 继承自AsyncQueryHandler,用于处理异步查询完成后的操作。 - */ - private final class BackgroundQueryHandler extends AsyncQueryHandler { - public BackgroundQueryHandler(ContentResolver contentResolver) { - super(contentResolver); - } - - /** - * 查询完成时的处理逻辑。 - * 根据查询标记的不同,执行不同的操作,如更新笔记列表或显示文件夹列表。 - * - * @param token 查询标记,用于区分不同的查询。 - * @param cookie 查询时传入的附加对象。 - * @param cursor 查询结果的游标。 - */ - @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; - default: - // 对未知标记不做处理 - return; - } - } - } - - /** - * 显示文件夹列表的菜单。 - * 使用查询结果构建一个对话框,让用户选择一个文件夹。 - * - * @param cursor 查询结果的游标,包含文件夹信息。 - */ - 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() { - - /** - * 用户选择文件夹时的处理逻辑。 - * 将选中的笔记移动到用户选择的文件夹中,并给出反馈。 - * - * @param dialog 对话框实例。 - * @param which 用户选择的项的索引。 - */ - public void onClick(DialogInterface dialog, int which) { - // 批量移动选中的笔记到目标文件夹 - DataUtils.batchMoveToFolder(mContentResolver, - mNotesListAdapter.getSelectedItemIds(), adapter.getItemId(which)); - // 显示移动操作的反馈信息 - Toast.makeText( - NotesListActivity.this, - 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() { - // 构建意图并指定动作为插入或编辑,以及初始文件夹ID - Intent intent = new Intent(this, NoteEditActivity.class); - intent.setAction(Intent.ACTION_INSERT_OR_EDIT); - intent.putExtra(Notes.INTENT_EXTRA_FOLDER_ID, mCurrentFolderId); - // 启动该意图并期待返回结果 - this.startActivityForResult(intent, REQUEST_CODE_NEW_NODE); - } - - - /** - * 批量删除笔记的函数。根据当前是否处于同步模式,采取不同的删除策略:如果不处于同步模式,则直接删除笔记;如果处于同步模式,则将笔记移动到回收站文件夹。 - * 执行删除操作后,会更新相应的widgets。 - */ - private void batchDelete() { - new AsyncTask