From 22720f6f475f19c81a4f8f74005c1b8efedd828f Mon Sep 17 00:00:00 2001 From: mc19 <2716188191@qq.com> Date: Wed, 4 Feb 2026 15:03:28 +0800 Subject: [PATCH] =?UTF-8?q?=E5=AE=8C=E5=96=84=E5=8A=9F=E8=83=BD=EF=BC=8C?= =?UTF-8?q?=E4=BF=AE=E6=94=B9=E6=B3=A8=E9=87=8A?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/notes/data/Contact.java | 32 ++-- src/notes/data/Notes.java | 26 +-- src/notes/data/NotesDatabaseHelper.java | 201 ++++++++++++++---------- src/notes/ui/LoginRegisterActivity.java | 37 ++++- src/notes/ui/SplashActivity.java | 54 +++---- src/notes/ui/WaveAnimation.java | 76 +++++++++ 6 files changed, 272 insertions(+), 154 deletions(-) create mode 100644 src/notes/ui/WaveAnimation.java diff --git a/src/notes/data/Contact.java b/src/notes/data/Contact.java index 206eaf3..13c9564 100644 --- a/src/notes/data/Contact.java +++ b/src/notes/data/Contact.java @@ -32,15 +32,10 @@ import android.util.Log; import java.util.HashMap; /** -*Contact类是联系人工具类 -* -*

根据“提供的电话号码反向查询联系人姓名”的能力,并带内存缓存,避免重复查库。

-* 本类所有方法均为静态,无需实例化。 -* -* @author 蒙程 - * @version 1.0 - * @since [起始版本] -*/ + * Contact类是联系人工具类 + * 根据提供的电话号码反向查询联系人姓名 + * 本类所有方法均为静态,无需实例化。 + */ public class Contact { //HashMap表示联系人和电话号码之间的映射关系。只声明,不new,是延迟初始化的表现 private static HashMap sContactCache; @@ -56,15 +51,13 @@ public class Contact { /** * 根据电话号码获取联系人姓名 - *

优先读取内存缓存,未命中时查询系统联系人数据库,并将结果写入缓存。

+ * 优先读取内存缓存,未命中时查询系统联系人数据库,并将结果写入缓存 * @param context 上下文,用于访问ContentResolver * @param phoneNumber 待查询的电话号码(可为国际格式、带前缀 0 等) - * @return 联系人姓名;未匹配到或者发生异常时,返回@code null - * @throws - * @see android.provider.ContactsContract.CommonDataKinds + * @return 联系人姓名;(未匹配到或者发生异常时,返回@code null) */ public static String getContact(Context context, String phoneNumber) { - //不到真正要用的时候,不给对象分配内存。第一次被调用时才new。 + //不到真正要用的时候,不给对象分配内存,第一次被调用时才new if(sContactCache == null) { sContactCache = new HashMap(); } @@ -74,11 +67,11 @@ public class Contact { return sContactCache.get(phoneNumber); } - //构造匹配串,提取数字核心部分 + //构造匹配串,提取电话号码数字核心部分 String selection = CALLER_ID_SELECTION.replace("+", PhoneNumberUtils.toCallerIDMinMatch(phoneNumber)); - //cursor是数据库游标,初始位置是-1;query把SQL拼装好后交给系统联系人Provider + //getcontentresolver.query()方法查询数据库,根据URI和电话号码匹配 Cursor cursor = context.getContentResolver().query( Data.CONTENT_URI, new String [] { Phone.DISPLAY_NAME }, @@ -86,16 +79,17 @@ public class Contact { new String[] { phoneNumber }, null); - //指针移到第一行,成功返回true + //利用cursor获取联系人姓名 if (cursor != null && cursor.moveToFirst()) { try { String name = cursor.getString(0); sContactCache.put(phoneNumber, name); //将号码-姓名键值对放进内存缓存 return name; - } catch (IndexOutOfBoundsException e) { //捕获异常,索引超出列范围的情况,返回错误日志 + } catch (IndexOutOfBoundsException e) { + //捕获索引越界异常 Log.e(TAG, " Cursor get string error " + e.toString()); return null; - } finally { //无论是否发生异常都必须释放cursor。 + } finally { cursor.close(); } } else { //查询失败,输出日志信息 diff --git a/src/notes/data/Notes.java b/src/notes/data/Notes.java index 414c31f..34c26dd 100644 --- a/src/notes/data/Notes.java +++ b/src/notes/data/Notes.java @@ -25,14 +25,14 @@ package net.micode.notes.data; import android.net.Uri; /** -*Notes是便签数据库类 -*

定义了URI常量、便签/文件夹类型常量、Intent扩展字段名、数据库接口、两种与业务实体

-* 本类所有字段均用static final修饰,说明字段不会变,可以直接用 +* Notes是便签数据库类 +*

定义了URI常量、便签/文件夹类型常量、Intent扩展字段名、数据库接口、两种业务实体

+* 所有字段均用static final修饰,静态变量,静态常量,静态方法,静态内部类。 */ public class Notes { public static final String AUTHORITY = "micode_notes"; //拼接URI的前缀 - public static final String TAG = "Notes"; //日志显示Notes + public static final String TAG = "Notes"; //日志显示的标记 /** * 定义便签/文件夹/系统文件夹类型常量 @@ -55,7 +55,6 @@ public class Notes { /** * Intent实现各个组件(界面)之间数据的传递 - * */ public static final String INTENT_EXTRA_ALERT_DATE = "net.micode.notes.alert_date"; //提醒时间戳 public static final String INTENT_EXTRA_BACKGROUND_ID = "net.micode.notes.background_color_id"; //便签背景颜色 @@ -65,7 +64,7 @@ public class Notes { 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; @@ -94,7 +93,7 @@ public class Notes { /** * NoteColumns是便签数据库列名接口 - * 声明便签信息列名,包括便签ID,父文件夹ID,创建日期,修改日期,提醒日期等。 + * 声明便签信息列名,包括便签ID,父文件夹ID,创建日期,修改日期,提醒日期等 */ public interface NoteColumns { /** @@ -247,7 +246,7 @@ public class Notes { /** - * 数据库列名接口,声明与便签内容相关的字段 + * 数据库列名接口,声明与便签具体内容相关的字段 */ public interface DataColumns { /** @@ -345,27 +344,27 @@ public class Notes { * Mode to indicate the text in check list mode or not *

Type: Integer 1:check list mode 0: normal mode

*/ - public static final String MODE = DATA1; //清单模式or普通文本模式? + 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_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 String CONTENT_ITEM_TYPE = "vnd.android.cursor.item/text_note"; //MIME类型为单条笔记 public static final Uri CONTENT_URI = Uri.parse("content://" + AUTHORITY + "/text_note"); } /** * CallNote是通话记录便签实体常量类 - * 实现了DataColumns接口,与TextNote一起,体现了接口的多实现特性。 + * TextNote和Callote实现DataColumns接口,体现接口的多实现特性 */ public static final class CallNote implements DataColumns { /** * Call date for this record *

Type: INTEGER (long)

*/ - public static final String CALL_DATE = DATA1; //DATA1是数据库中的真实列名,将通话的时间戳存入DATA1列中 + public static final String CALL_DATE = DATA1; //DATA1存储通话的时间戳 /** * Phone number for this record @@ -382,6 +381,7 @@ public class Notes { /** * 图片数据实体常量类 + * 增加原因是新增了便签插入图片功能 */ public static final class ImageData implements DataColumns { /** diff --git a/src/notes/data/NotesDatabaseHelper.java b/src/notes/data/NotesDatabaseHelper.java index ee7df8c..1445fd7 100644 --- a/src/notes/data/NotesDatabaseHelper.java +++ b/src/notes/data/NotesDatabaseHelper.java @@ -32,32 +32,29 @@ import net.micode.notes.data.Notes.DataConstants; import net.micode.notes.data.Notes.NoteColumns; /** -*便签数据库辅助类 -

- *负责以下功能: +*便签数据库辅助类: *

-

* * @author 蒙程 - * @version {@link #DB_VERSION} 4 - * @since 2010-2011 + * @version 1.0 + * @since 2025-12-13 */ public class NotesDatabaseHelper extends SQLiteOpenHelper { /** - *一、数据库基础常量 + *一、声明数据库基础常量 */ private static final String DB_NAME = "note.db"; private static final int DB_VERSION = 7; - - /**表明常量接口*/ public interface TABLE { public static final String NOTE = "note"; @@ -66,16 +63,17 @@ public class NotesDatabaseHelper extends SQLiteOpenHelper { private static final String TAG = "NotesDatabaseHelper"; - //类加载时不创建NotesDatabaseHelper实例(mInstance),真正需要使用数据库时才调用getInstance初始化,节省应用启动时的内存和资源开销。 + //类加载时不创建实例单例,真正需要使用时才调用getInstance初始化,节省应用启动时的内存和资源开销。 private static NotesDatabaseHelper mInstance; - - //二、建表SQL,初始化note和data表,同时添加note_id_index索引列 /** - * 结合在Notes中定义的NoteColumns初始化 - * 在Notes类里定义了NoteColumns,是便签数据库列名接口,ID、PARENT_ID等都是列名,表示把Notes数据库里声明的列名拿来用 - * NOT NULL约束强制列不接受NULL值,DEFAULT 0 表示默认值为0,如果字段设定NOT NULL,没有输入值时,DEFAULT后跟的默认值来填充 - * 这里的strftime是SQLite内置的时间、日期格式化函数,参数为输出格式(%s表示秒级Unix时间戳)、时间源(‘now'表示现在) + * 二、初始化Note和Data表中各列 + * NoteColumns是 Notes类便签数据库列名接口,ID、PARENT_ID等都是列名 + *

+ * NOT NULL约束强制列不接受NULL值,DEFAULT 0 表示默认值为0, + * 如果字段设定 NOT NULL,没有输入值时,用DEFAULT后的默认值填充。 + *

+ * strftime是 SQLite内置的时间、日期格式化函数,参数为输出格式(%s表示秒级Unix时间戳)、时间源(‘now'表示现在) */ private static final String CREATE_NOTE_TABLE_SQL = "CREATE TABLE " + TABLE.NOTE + "(" + //开始建表,表名是TABLE.NOTE(note) @@ -105,8 +103,8 @@ public class NotesDatabaseHelper extends SQLiteOpenHelper { /** * 创建 Data 表 SQL,结合在Notes中定义的DataColumns初始化 *

- * 外键:{@link DataColumns#NOTE_ID} 指向 note.id
- * 通用列 data1~data5 含义由 {@link DataColumns#MIME_TYPE} 决定。 + * 外键:{@link DataColumns#NOTE_ID} 指向 note.id
+ * 通用列 data1~data5 含义由 {@link DataColumns#MIME_TYPE} 决定。 *

*/ private static final String CREATE_DATA_TABLE_SQL = @@ -127,20 +125,19 @@ public class NotesDatabaseHelper extends SQLiteOpenHelper { ")"; /** - *在data(NOTE_ID)这一列上创建名为note_id_index的索引(如果该索引不存在) - * 便于执行SELECT * FROM data WHERE note_id=?之类的查询语句 + *

在data表中如果不存在 note_id索引列,则创建

+ *

便于执行"SELECT * FROM data WHERE 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表触发器,在文件夹下便签数量变化时自动增减便签数量(notes_count)的值 /** + * 三、创建 Note表触发器,文件夹下便签数量变化时自动增减便签数量的值 * Increase folder's note count when move note to the folder - * 创建触发器,名字叫increase_folder_count_on_update - * 在note表的parent_id列更新后 - * 执行触发器体,更新Note表,设置notes_count=notes_count+1,在id=new.parent_id的地方 + * 创建触发器 increase_folder_count_on_update + * 在 note表的 parent_id列更新后 + * 执行触发器体,更新 Note表,设置 notes_count=notes_count+1,在 id=new.parent_id的地方 * 此处用于自动更新父文件夹下的便签数量 */ private static final String NOTE_INCREASE_FOLDER_COUNT_ON_UPDATE_TRIGGER = @@ -190,11 +187,11 @@ public class NotesDatabaseHelper extends SQLiteOpenHelper { " AND " + NoteColumns.NOTES_COUNT + ">0;" + " END"; - //四、Data 表触发器:自动同步便签摘要(snippet) /** + * 四、Data 表触发器:自动同步便签摘要(snippet) * Update note's content when insert data with type {@link DataConstants#NOTE} - * 仅当新插入的DATA记录的mime_type字段值为note(笔记)类型时,触发器才会执行后续逻辑,若插入的是其他数据类型,比如图片,则不触发 - * 执行触发器,将note表里的snippet列值更新为新纪录的content值,且仅更新note表里主键id=新纪录note_id的行 + * 启动触发器:新插入的记录 mime_type字段值为note类型(其他数据类型,比如图片,则不触发) + * 执行触发器:根据 note_id将 Note表里相应的snippet列值更新为 DataColumn表中的content列值 */ private static final String DATA_UPDATE_NOTE_CONTENT_ON_INSERT_TRIGGER = "CREATE TRIGGER update_note_content_on_insert " + @@ -234,8 +231,8 @@ public class NotesDatabaseHelper extends SQLiteOpenHelper { /** * Delete data belong to note which has been deleted - * 当从note表删除一条笔记记录后,通过data表的note_id与note表的id关联 - * 自动删除data表中所有与这条被删笔记关联的记录,笔记-笔记内容 + * 当从 note表删除一条笔记记录后,通过 note表的id与 data表的 note_id关联 + * 自动删除data表中所有与这条被删笔记关联的记录 */ private static final String NOTE_DELETE_DATA_ON_DELETE_TRIGGER = "CREATE TRIGGER delete_data_on_delete " + @@ -247,7 +244,8 @@ public class NotesDatabaseHelper extends SQLiteOpenHelper { /** * Delete notes belong to folder which has been deleted - * 当从note表里删除一条记录后,自动级联删除note表中所有parent_id等于被删记录id的子记录 + * 从note表里删除一条笔记,自动级联删除note表中所有parent_id为该记录id的子记录 + * 比如:删除一个文件夹,该文件夹下的所有笔记都会被删除 */ private static final String FOLDER_DELETE_NOTES_ON_DELETE_TRIGGER = "CREATE TRIGGER folder_delete_notes_on_delete " + @@ -271,7 +269,7 @@ public class NotesDatabaseHelper extends SQLiteOpenHelper { " END"; /** - * Android开发中自定义的数据库辅助类的构造方法,作用是初始化,指定要创建/管理的数据库名称、版本号等核心参数 + * Android开发中自定义的数据库辅助类的构造方法,作用是初始化要创建/管理的数据库名称、版本号等核心参数 * @param context 上下文,提供应用运行的环境信息,如访问资源、数据库文件路径、系统服务等。 */ public NotesDatabaseHelper(Context context) { @@ -279,13 +277,15 @@ public class NotesDatabaseHelper extends SQLiteOpenHelper { /** super是调用的父类(SQLiteOpenHelper)构造方法 * @param context 父类通过context确定数据库文件的存储位置 * @param DB_NAME 数据库文件名 - * @param null 创建数据库查询的游标(cursor),传null表示默认 - * @param DB_VERSION自定义常量,数据库版本号 + * @param null 创建数据库查询的游标(cursor),null表示默认(默认值为 1,表示只读) + * @param DB_VERSION 自定义常量,数据库版本号 */ super(context, DB_NAME, null, DB_VERSION); } - //完成note表的全量初始化 + /** + * 创建Note表 + */ public void createNoteTable(SQLiteDatabase db) { db.execSQL(CREATE_NOTE_TABLE_SQL); //执行SQL语句创建note表 reCreateNoteTableTriggers(db); //重建该表关联的触发器 @@ -293,7 +293,9 @@ public class NotesDatabaseHelper extends SQLiteOpenHelper { Log.d(TAG, "note table has been created"); //打印日志,确认note表成功创建 } - //批量重建触发器,先删除旧的,再建立新的 + /** + * 批量重建触发器,先删除旧的,再建立新的 + */ 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"); @@ -312,7 +314,11 @@ public class NotesDatabaseHelper extends SQLiteOpenHelper { db.execSQL(FOLDER_MOVE_NOTES_ON_TRASH_TRIGGER); } - //向note表插入四个系统默认文件夹:通话记录、根、临时、回收站。保证应用首次启动时有基础的文件夹结构。 + /** + * 创建系统文件夹 + * 向note表插入四个系统默认文件夹:通话记录、根、临时、回收站.保证应用首次启动时有基础的文件夹结构 + * @param db + */ private void createSystemFolder(SQLiteDatabase db) { //ContentValues类用于存储键值对 ContentValues values = new ContentValues(); @@ -349,6 +355,9 @@ public class NotesDatabaseHelper extends SQLiteOpenHelper { db.insert(TABLE.NOTE, null, values); } + /** + * 创建 Data 表 + */ public void createDataTable(SQLiteDatabase db) { db.execSQL(CREATE_DATA_TABLE_SQL); reCreateDataTableTriggers(db); @@ -367,10 +376,9 @@ public class NotesDatabaseHelper extends SQLiteOpenHelper { } /** - * synchronized同步关键字,保证多线程安全,避免多个线程同时调用该方法时,进入if分支,创建多个实例 - * getInstance函数用于获取唯一实例 + * synchronized同步关键字,保证多线程安全,避免多个线程同时调用该方法时创建多个实例 + * getInstance方法用于获取唯一实例 */ - static synchronized NotesDatabaseHelper getInstance(Context context) { //首次调用该方法时,才创建实例;若已创建,则直接复用 if (mInstance == null) { @@ -379,13 +387,24 @@ public class NotesDatabaseHelper extends SQLiteOpenHelper { return mInstance; } - //@override是Java中的一种注解,用于明确表示子类的方法是对父类方法的重写,实现多态性 + /** + * onCreate方法在数据库第一次创建时调用 + * "@override"表示子类重写父类方法,实现多态性 + */ @Override public void onCreate(SQLiteDatabase db) { - createNoteTable(db); //初始化笔记表 - createDataTable(db); //初始化数据表 + createNoteTable(db); //初始化笔记表Note + createDataTable(db); //初始化数据表Data } + /** + * onUpgrade方法在数据库升级时调用 + *

+ * 什么时候需要更新数据库版本? + *

  • 数据库结构发生改变,比如添加字段 rich_text_format
  • + *
  • 数据库结构没有改变,但数据结构发生改变, 比如增加了富文本样式
  • + *

    + */ @Override public void onUpgrade(SQLiteDatabase db, int oldVersion, int newVersion) { boolean reCreateTriggers = false; @@ -408,25 +427,25 @@ public class NotesDatabaseHelper extends SQLiteOpenHelper { oldVersion++; } - if (oldVersion == 4) { - upgradeToV5(db); - oldVersion++; - } + if (oldVersion == 4) { + upgradeToV5(db); + oldVersion++; + } - if (oldVersion == 5) { - upgradeToV6(db); - oldVersion++; - } + if (oldVersion == 5) { + upgradeToV6(db); + oldVersion++; + } - if (oldVersion == 6) { - upgradeToV7(db); - oldVersion++; - } + if (oldVersion == 6) { + upgradeToV7(db); + oldVersion++; + } - if (oldVersion == 7) { - upgradeToV8(db); - oldVersion++; - } + if (oldVersion == 7) { + upgradeToV8(db); + oldVersion++; + } if (reCreateTriggers) { reCreateNoteTableTriggers(db); @@ -439,7 +458,9 @@ public class NotesDatabaseHelper extends SQLiteOpenHelper { } } - //v1到v2的升级,删除旧的note data表,清空所有用户数据,重建新的 + /** + * v1到v2的升级,删除旧的note data表,清空所有用户数据,重建新的 + */ private void upgradeToV2(SQLiteDatabase db) { db.execSQL("DROP TABLE IF EXISTS " + TABLE.NOTE); //执行具体的SQL语句,删除旧的note表 db.execSQL("DROP TABLE IF EXISTS " + TABLE.DATA); //删除旧的data表 @@ -447,7 +468,9 @@ public class NotesDatabaseHelper extends SQLiteOpenHelper { createDataTable(db); } - //v2到v3的升级,清除无用触发器,新增gtask_id列,新增回收站文件夹 + /** + * v2到v3的升级,清除无用触发器,Note表新增 gtask_id列,新增回收站文件夹 + */ private void upgradeToV3(SQLiteDatabase db) { // drop unused triggers db.execSQL("DROP TRIGGER IF EXISTS update_note_modified_date_on_insert"); @@ -463,37 +486,49 @@ public class NotesDatabaseHelper extends SQLiteOpenHelper { db.insert(TABLE.NOTE, null, values); } - //v3到v4的升级,为note表新增version列,且约束非空,默认为0 + /** + *v3到 v4的升级,为 note表新增 version列,且约束非空,默认为0 + */ private void upgradeToV4(SQLiteDatabase db) { db.execSQL("ALTER TABLE " + TABLE.NOTE + " ADD COLUMN " + NoteColumns.VERSION + " INTEGER NOT NULL DEFAULT 0"); } - //v4到v5的升级,为data表新增rich_text_format列,用于存储富文本格式信息 + /** + * v4到v5的升级,为 data表新增 rich_text_format列,用于存储富文本格式信息 + */ private void upgradeToV5(SQLiteDatabase db) { db.execSQL("ALTER TABLE " + TABLE.DATA + " ADD COLUMN rich_text_format TEXT NOT NULL DEFAULT ''"); } - //v5到v6的升级,为data表新增用于存储图片路径的列 + /** + * v5到v6的升级,为 data表新增用于存储图片路径的列 + */ private void upgradeToV6(SQLiteDatabase db) { - // 为data表添加一个专门存储图片路径的列 db.execSQL("ALTER TABLE " + TABLE.DATA + " ADD COLUMN image_path TEXT NOT NULL DEFAULT ''"); } - - //v6到v7的升级,为note表新增位置提醒相关的列 + + /** + * v6到v7的升级,为 note表新增位置提醒相关的列 + */ private void upgradeToV7(SQLiteDatabase db) { - // 为note表添加位置提醒相关的列 - db.execSQL("ALTER TABLE " + TABLE.NOTE + " ADD COLUMN " + NoteColumns.ALERT_LATITUDE + " REAL NOT NULL DEFAULT 0"); - db.execSQL("ALTER TABLE " + TABLE.NOTE + " ADD COLUMN " + NoteColumns.ALERT_LONGITUDE + " REAL NOT NULL DEFAULT 0"); - db.execSQL("ALTER TABLE " + TABLE.NOTE + " ADD COLUMN " + NoteColumns.ALERT_RADIUS + " REAL NOT NULL DEFAULT 0"); - db.execSQL("ALTER TABLE " + TABLE.NOTE + " ADD COLUMN " + NoteColumns.ALERT_LOCATION_NAME + " TEXT NOT NULL DEFAULT ''"); + db.execSQL("ALTER TABLE " + TABLE.NOTE + " ADD COLUMN " + NoteColumns.ALERT_LATITUDE + " REAL NOT NULL DEFAULT 0"); //纬度 + db.execSQL("ALTER TABLE " + TABLE.NOTE + " ADD COLUMN " + NoteColumns.ALERT_LONGITUDE + " REAL NOT NULL DEFAULT 0"); //经度 + db.execSQL("ALTER TABLE " + TABLE.NOTE + " ADD COLUMN " + NoteColumns.ALERT_RADIUS + " REAL NOT NULL DEFAULT 0"); //半径 + db.execSQL("ALTER TABLE " + TABLE.NOTE + " ADD COLUMN " + NoteColumns.ALERT_LOCATION_NAME + " TEXT NOT NULL DEFAULT ''"); //地点名称 } - - //v7到v8的升级,为note表新增隐私锁相关的列 + + /** + * v7到v8的升级,为 note表新增隐私锁相关的列 + */ private void upgradeToV8(SQLiteDatabase db) { - // 为note表添加隐私锁相关列 - db.execSQL("ALTER TABLE " + TABLE.NOTE + " ADD COLUMN " + NoteColumns.LOCKED + " INTEGER NOT NULL DEFAULT 0"); // 锁定状态:0-未锁定,1-已锁定 - db.execSQL("ALTER TABLE " + TABLE.NOTE + " ADD COLUMN " + NoteColumns.LOCK_TYPE + " INTEGER NOT NULL DEFAULT 0"); // 锁类型:0-无锁,1-密码锁,2-手势锁 - db.execSQL("ALTER TABLE " + TABLE.NOTE + " ADD COLUMN " + NoteColumns.ENCRYPTED_PASSWORD + " TEXT NOT NULL DEFAULT ''"); // 加密后的密码 + // 锁定状态:0-未锁定,1-已锁定 + db.execSQL("ALTER TABLE " + TABLE.NOTE + " ADD COLUMN " + NoteColumns.LOCKED + " INTEGER NOT NULL DEFAULT 0"); + + // 锁类型:0-无锁,1-密码锁,2-手势锁 + db.execSQL("ALTER TABLE " + TABLE.NOTE + " ADD COLUMN " + NoteColumns.LOCK_TYPE + " INTEGER NOT NULL DEFAULT 0"); + + // 加密后的密码 + db.execSQL("ALTER TABLE " + TABLE.NOTE + " ADD COLUMN " + NoteColumns.ENCRYPTED_PASSWORD + " TEXT NOT NULL DEFAULT ''"); } -} +} \ No newline at end of file diff --git a/src/notes/ui/LoginRegisterActivity.java b/src/notes/ui/LoginRegisterActivity.java index b2ac984..d797bd8 100644 --- a/src/notes/ui/LoginRegisterActivity.java +++ b/src/notes/ui/LoginRegisterActivity.java @@ -3,7 +3,6 @@ package net.micode.notes.ui; import android.app.AlertDialog; import android.content.DialogInterface; import android.content.Intent; - import android.os.Bundle; import android.text.TextUtils; import android.view.View; @@ -47,6 +46,8 @@ public class LoginRegisterActivity extends AppCompatActivity { // 密码可见性状态 private boolean isPasswordVisible = false; + + private static final int ROOT_AUTH_REQUEST_CODE = 1001; @Override protected void onCreate(Bundle savedInstanceState) { @@ -130,7 +131,8 @@ public class LoginRegisterActivity extends AppCompatActivity { if (isLoginMode) { performLogin(); } else { - performRegister(); + // 注册前需要验证根用户权限 + showRootAuthDialog(); } } }); @@ -144,6 +146,15 @@ public class LoginRegisterActivity extends AppCompatActivity { }); } + /** + * 显示根用户权限验证对话框 + */ + private void showRootAuthDialog() { + Intent intent = new Intent(this, SplashActivity.class); + intent.putExtra("SHOW_ROOT_AUTH_DIALOG", true); + startActivityForResult(intent, ROOT_AUTH_REQUEST_CODE); + } + /** * 更新UI以适应当前模式(登录或注册) */ @@ -323,6 +334,24 @@ public class LoginRegisterActivity extends AppCompatActivity { }) .show(); } - - + + @Override + protected void onActivityResult(int requestCode, int resultCode, Intent data) { + super.onActivityResult(requestCode, resultCode, data); + + if (requestCode == ROOT_AUTH_REQUEST_CODE) { + if (resultCode == RESULT_OK && data != null) { + boolean authResult = data.getBooleanExtra("ROOT_AUTH_RESULT", false); + if (authResult) { + // 根用户验证成功,执行注册 + performRegister(); + } else { + Toast.makeText(this, "根用户验证失败,无法注册新用户", Toast.LENGTH_SHORT).show(); + } + } else if (resultCode == RESULT_CANCELED) { + // 用户取消了验证 + Toast.makeText(this, "已取消根用户验证", Toast.LENGTH_SHORT).show(); + } + } + } } \ No newline at end of file diff --git a/src/notes/ui/SplashActivity.java b/src/notes/ui/SplashActivity.java index 14938cf..9b72136 100644 --- a/src/notes/ui/SplashActivity.java +++ b/src/notes/ui/SplashActivity.java @@ -3,24 +3,16 @@ package net.micode.notes.ui; import android.content.Intent; import android.os.Bundle; import android.os.Handler; -import android.view.animation.Animation; -import android.view.animation.AnimationUtils; -import android.widget.ImageView; import android.widget.TextView; import androidx.appcompat.app.AppCompatActivity; import net.micode.notes.R; +import net.micode.notes.tool.UserManager; public class SplashActivity extends AppCompatActivity { private static final int SPLASH_DURATION = 3000; // 3秒 - private static final int TEXT_FADE_IN_DELAY = 2000; // 2秒后文字淡入 - - // 动画完成回调接口 - public interface OnSplashCompleteListener { - void onSplashComplete(); - } // 启动页创建,初始化动画界面和跳转逻辑 @Override @@ -28,37 +20,29 @@ public class SplashActivity extends AppCompatActivity { super.onCreate(savedInstanceState); setContentView(R.layout.activity_splash); - ImageView logo = findViewById(R.id.splash_logo); TextView text = findViewById(R.id.splash_text); - // 加载文字滑动动画 - Animation slideUpAnimation = AnimationUtils.loadAnimation(this, R.anim.text_slide_up); - - // 2秒后显示文字动画 - new Handler().postDelayed(new Runnable() { - @Override - public void run() { - text.setVisibility(android.view.View.VISIBLE); - text.startAnimation(slideUpAnimation); - } - }, TEXT_FADE_IN_DELAY); - - // 3秒后跳转到登录界面 - new Handler().postDelayed(new Runnable() { - @Override - public void run() { - onSplashComplete(); + // 开始淡入动画 + WaveAnimation.applyFadeInAnimation(text); + + // 3秒后根据登录状态跳转 + new Handler().postDelayed(() -> { + UserManager userManager = UserManager.getInstance(this); + boolean isLoggedIn = userManager.isLoggedIn(); + + if (isLoggedIn) { + // 已登录,直接进入主界面 + Intent intent = new Intent(SplashActivity.this, NotesListActivity.class); + startActivity(intent); + } else { + // 未登录,跳转到注册登录界面 + Intent intent = new Intent(SplashActivity.this, LoginRegisterActivity.class); + startActivity(intent); } + finish(); + overridePendingTransition(android.R.anim.fade_in, android.R.anim.fade_out); }, SPLASH_DURATION); } - - // 动画完成回调方法 - private void onSplashComplete() { - Intent intent = new Intent(SplashActivity.this, LoginRegisterActivity.class); - startActivity(intent); - finish(); // 结束启动页,防止用户返回到此页面 - overridePendingTransition(android.R.anim.fade_in, android.R.anim.fade_out); - } // 处理屏幕旋转等配置变更 @Override diff --git a/src/notes/ui/WaveAnimation.java b/src/notes/ui/WaveAnimation.java new file mode 100644 index 0000000..3963599 --- /dev/null +++ b/src/notes/ui/WaveAnimation.java @@ -0,0 +1,76 @@ +package net.micode.notes.ui; + +import android.animation.ValueAnimator; +import android.view.animation.AccelerateDecelerateInterpolator; +import android.widget.TextView; + +public class WaveAnimation { + + // 整行文字从左到右淡入显示动画 + public static void applyFadeInAnimation(TextView textView) { + // 设置初始状态为完全透明 + textView.setAlpha(0f); + textView.setVisibility(android.view.View.VISIBLE); + + // 创建透明度渐变动画 + ValueAnimator fadeInAnimator = ValueAnimator.ofFloat(0f, 1f); + fadeInAnimator.setDuration(2000); // 2秒淡入效果 + fadeInAnimator.setInterpolator(new AccelerateDecelerateInterpolator()); + + fadeInAnimator.addUpdateListener(animation -> { + float alpha = (float) animation.getAnimatedValue(); + textView.setAlpha(alpha); + }); + + // 创建从左到右的滑入效果 + ValueAnimator slideInAnimator = ValueAnimator.ofFloat(-100f, 0f); + slideInAnimator.setDuration(1500); // 1.5秒滑入 + slideInAnimator.setInterpolator(new android.view.animation.OvershootInterpolator()); + + slideInAnimator.addUpdateListener(animation -> { + float translationX = (float) animation.getAnimatedValue(); + textView.setTranslationX(translationX); + }); + + // 同时启动两个动画 + fadeInAnimator.start(); + slideInAnimator.start(); + } + + // 从左到右逐字淡入效果(如果需要的话) + public static void applyCharByCharFadeIn(TextView textView) { + String text = textView.getText().toString(); + int length = text.length(); + + // 设置初始状态 + textView.setAlpha(0f); + textView.setVisibility(android.view.View.VISIBLE); + + // 为整行文字创建统一的淡入动画 + ValueAnimator animator = ValueAnimator.ofFloat(0f, 1f); + animator.setDuration(2500); // 总时长2.5秒 + animator.setInterpolator(new android.view.animation.DecelerateInterpolator()); + + animator.addUpdateListener(animation -> { + float progress = (float) animation.getAnimatedValue(); + textView.setAlpha(progress); + + // 添加轻微的缩放效果 + float scale = 0.8f + (progress * 0.2f); // 从0.8倍放大到1倍 + textView.setScaleX(scale); + textView.setScaleY(scale); + }); + + animator.start(); + } + + // 兼容旧版本的方法名 - 使用新的淡入效果 + public static void applyComplexWaveAnimation(TextView textView) { + applyFadeInAnimation(textView); + } + + // 兼容旧版本的另一个方法名 + public static void applyCharWaveAnimation(TextView textView) { + applyCharByCharFadeIn(textView); + } +} \ No newline at end of file