diff --git a/src/main/AndroidManifest.xml b/src/main/AndroidManifest.xml
index 2350512..cb43220 100644
--- a/src/main/AndroidManifest.xml
+++ b/src/main/AndroidManifest.xml
@@ -48,7 +48,7 @@
类型: INTEGER (long)
+ *0表示未被删除,其他值表示删除时间戳
+ */ + public static final String DELETED_DATE = "deleted_date"; } public interface DataColumns { diff --git a/src/main/java/net/micode/notes/data/NotesDatabaseHelper.java b/src/main/java/net/micode/notes/data/NotesDatabaseHelper.java index ce29b9f..4edf21d 100644 --- a/src/main/java/net/micode/notes/data/NotesDatabaseHelper.java +++ b/src/main/java/net/micode/notes/data/NotesDatabaseHelper.java @@ -35,7 +35,7 @@ public class NotesDatabaseHelper extends SQLiteOpenHelper { // 数据库文件名 private static final String DB_NAME = "note.db"; // 数据库版本号,用于升级控制 - private static final int DB_VERSION = 6; + private static final int DB_VERSION = 7; /** * 数据库表名常量定义 @@ -78,7 +78,8 @@ public class NotesDatabaseHelper extends SQLiteOpenHelper { NoteColumns.PINNED + " INTEGER NOT NULL DEFAULT 0," + NoteColumns.IS_LOCKED + " INTEGER NOT NULL DEFAULT 0," + NoteColumns.LOCK_PASSWORD + " TEXT NOT NULL DEFAULT ''," + - NoteColumns.LOCK_TYPE + " INTEGER NOT NULL DEFAULT 0" + + NoteColumns.LOCK_TYPE + " INTEGER NOT NULL DEFAULT 0," + + NoteColumns.DELETED_DATE + " INTEGER NOT NULL DEFAULT 0" + ")"; /** @@ -263,6 +264,8 @@ public class NotesDatabaseHelper extends SQLiteOpenHelper { 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("DROP TRIGGER IF EXISTS record_deleted_date_on_trash"); + db.execSQL("DROP TRIGGER IF EXISTS clear_deleted_date_on_restore"); db.execSQL(NOTE_INCREASE_FOLDER_COUNT_ON_UPDATE_TRIGGER); db.execSQL(NOTE_DECREASE_FOLDER_COUNT_ON_UPDATE_TRIGGER); @@ -271,6 +274,28 @@ public class NotesDatabaseHelper extends SQLiteOpenHelper { 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.execSQL("CREATE TRIGGER record_deleted_date_on_trash " + + " AFTER UPDATE OF " + NoteColumns.PARENT_ID + " ON " + TABLE.NOTE + + " WHEN new." + NoteColumns.PARENT_ID + "=" + Notes.ID_TRASH_FOLER + + " AND old." + NoteColumns.PARENT_ID + "!=" + Notes.ID_TRASH_FOLER + + " BEGIN " + + " UPDATE " + TABLE.NOTE + + " SET " + NoteColumns.DELETED_DATE + "=strftime('%s','now') * 1000" + + " WHERE " + NoteColumns.ID + "=new." + NoteColumns.ID + ";" + + " END"); + + // 创建清除删除时间触发器 + db.execSQL("CREATE TRIGGER clear_deleted_date_on_restore " + + " AFTER UPDATE OF " + NoteColumns.PARENT_ID + " ON " + TABLE.NOTE + + " WHEN new." + NoteColumns.PARENT_ID + "!=" + Notes.ID_TRASH_FOLER + + " AND old." + NoteColumns.PARENT_ID + "=" + Notes.ID_TRASH_FOLER + + " BEGIN " + + " UPDATE " + TABLE.NOTE + + " SET " + NoteColumns.DELETED_DATE + "=0" + + " WHERE " + NoteColumns.ID + "=new." + NoteColumns.ID + ";" + + " END"); } /** @@ -394,6 +419,12 @@ public class NotesDatabaseHelper extends SQLiteOpenHelper { oldVersion++; } + // 版本6升级到版本7:添加删除时间字段和相关触发器 + if (oldVersion == 6) { + upgradeToV7(db); + oldVersion++; + } + // 如果需要,重新创建触发器 if (reCreateTriggers) { reCreateNoteTableTriggers(db); @@ -403,7 +434,7 @@ public class NotesDatabaseHelper extends SQLiteOpenHelper { // 验证升级是否完成 if (oldVersion != newVersion) { throw new IllegalStateException("Upgrade notes database to version " + newVersion - + "fails"); + + " fails"); } } @@ -467,4 +498,36 @@ public class NotesDatabaseHelper extends SQLiteOpenHelper { db.execSQL("ALTER TABLE " + TABLE.NOTE + " ADD COLUMN " + NoteColumns.LOCK_TYPE + " INTEGER NOT NULL DEFAULT 0"); } + + /** + * 升级到版本7:添加删除时间字段和相关触发器,用于最近删除功能 + * @param db 数据库实例 + */ + private void upgradeToV7(SQLiteDatabase db) { + // 添加DELETED_DATE字段,用于记录便签被删除的时间 + db.execSQL("ALTER TABLE " + TABLE.NOTE + " ADD COLUMN " + NoteColumns.DELETED_DATE + + " INTEGER NOT NULL DEFAULT 0"); + + // 创建触发器:当便签被移动到回收站时,自动记录删除时间 + db.execSQL("CREATE TRIGGER record_deleted_date_on_trash " + + " AFTER UPDATE OF " + NoteColumns.PARENT_ID + " ON " + TABLE.NOTE + + " WHEN new." + NoteColumns.PARENT_ID + "=" + Notes.ID_TRASH_FOLER + + " AND old." + NoteColumns.PARENT_ID + "!=" + Notes.ID_TRASH_FOLER + + " BEGIN " + + " UPDATE " + TABLE.NOTE + + " SET " + NoteColumns.DELETED_DATE + "=strftime('%s','now') * 1000" + + " WHERE " + NoteColumns.ID + "=new." + NoteColumns.ID + ";" + + " END"); + + // 创建触发器:当便签从回收站恢复时,自动清除删除时间 + db.execSQL("CREATE TRIGGER clear_deleted_date_on_restore " + + " AFTER UPDATE OF " + NoteColumns.PARENT_ID + " ON " + TABLE.NOTE + + " WHEN new." + NoteColumns.PARENT_ID + "!=" + Notes.ID_TRASH_FOLER + + " AND old." + NoteColumns.PARENT_ID + "=" + Notes.ID_TRASH_FOLER + + " BEGIN " + + " UPDATE " + TABLE.NOTE + + " SET " + NoteColumns.DELETED_DATE + "=0" + + " WHERE " + NoteColumns.ID + "=new." + NoteColumns.ID + ";" + + " END"); + } } \ No newline at end of file diff --git a/src/main/java/net/micode/notes/data/NotesProvider.java b/src/main/java/net/micode/notes/data/NotesProvider.java index 678534f..ceb1020 100644 --- a/src/main/java/net/micode/notes/data/NotesProvider.java +++ b/src/main/java/net/micode/notes/data/NotesProvider.java @@ -169,7 +169,11 @@ public class NotesProvider extends ContentProvider { searchString = uri.getPathSegments().get(1); } } else { - searchString = uri.getQueryParameter("pattern"); + // 支持Android搜索框架的标准参数名"query"和当前实现使用的"pattern" + searchString = uri.getQueryParameter(SearchManager.QUERY); + if (TextUtils.isEmpty(searchString)) { + searchString = uri.getQueryParameter("pattern"); + } } // 如果搜索关键词为空,返回null diff --git a/src/main/java/net/micode/notes/model/NoteTemplate.java b/src/main/java/net/micode/notes/model/NoteTemplate.java deleted file mode 100644 index c956f7d..0000000 --- a/src/main/java/net/micode/notes/model/NoteTemplate.java +++ /dev/null @@ -1,119 +0,0 @@ -package net.micode.notes.model; - -import android.content.Context; -import android.content.SharedPreferences; -import android.preference.PreferenceManager; -import android.text.TextUtils; - -import org.json.JSONArray; -import org.json.JSONException; -import org.json.JSONObject; - -import java.util.ArrayList; -import java.util.List; - -/** - * 笔记模板数据模型类,用于存储和管理笔记模板 - */ -public class NoteTemplate { - public static final String TYPE_SYSTEM = "system"; - public static final String TYPE_CUSTOM = "custom"; - - private long id; - private String name; - private String content; - private String type; - private long createTime; - private long updateTime; - - public NoteTemplate() { - this.createTime = System.currentTimeMillis(); - this.updateTime = System.currentTimeMillis(); - } - - public NoteTemplate(long id, String name, String content, String type, long createTime, long updateTime) { - this.id = id; - this.name = name; - this.content = content; - this.type = type; - this.createTime = createTime; - this.updateTime = updateTime; - } - - // Getters and Setters - public long getId() { - return id; - } - - public void setId(long id) { - this.id = id; - } - - public String getName() { - return name; - } - - public void setName(String name) { - this.name = name; - } - - public String getContent() { - return content; - } - - public void setContent(String content) { - this.content = content; - } - - public String getType() { - return type; - } - - public void setType(String type) { - this.type = type; - } - - public long getCreateTime() { - return createTime; - } - - public void setCreateTime(long createTime) { - this.createTime = createTime; - } - - public long getUpdateTime() { - return updateTime; - } - - public void setUpdateTime(long updateTime) { - this.updateTime = updateTime; - } - - /** - * 将模板对象转换为JSON对象 - */ - public JSONObject toJson() throws JSONException { - JSONObject json = new JSONObject(); - json.put("id", id); - json.put("name", name); - json.put("content", content); - json.put("type", type); - json.put("createTime", createTime); - json.put("updateTime", updateTime); - return json; - } - - /** - * 从JSON对象创建模板对象 - */ - public static NoteTemplate fromJson(JSONObject json) throws JSONException { - NoteTemplate template = new NoteTemplate(); - template.setId(json.getLong("id")); - template.setName(json.getString("name")); - template.setContent(json.getString("content")); - template.setType(json.getString("type")); - template.setCreateTime(json.getLong("createTime")); - template.setUpdateTime(json.getLong("updateTime")); - return template; - } -} diff --git a/src/main/java/net/micode/notes/model/RecentlyDeletedManager.java b/src/main/java/net/micode/notes/model/RecentlyDeletedManager.java new file mode 100644 index 0000000..c025468 --- /dev/null +++ b/src/main/java/net/micode/notes/model/RecentlyDeletedManager.java @@ -0,0 +1,230 @@ +/* + * Copyright (c) 2010-2011, The MiCode Open Source Community (www.micode.net) + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package net.micode.notes.model; + +import android.content.ContentResolver; +import android.content.Context; +import android.database.Cursor; +import android.util.Log; + +import net.micode.notes.data.Notes; +import net.micode.notes.data.Notes.NoteColumns; +import net.micode.notes.tool.DataUtils; + +import java.util.HashSet; + +/** + * 最近删除管理器,负责管理最近删除的便签和文件夹 + * 采用单例模式设计,提供获取最近删除项、恢复项、永久删除项、清空回收站等功能 + */ +public class RecentlyDeletedManager { + // 默认保留天数,超过此天数的已删除项将被自动清理 + public static final int DEFAULT_RETENTION_DAYS = 30; + // 清理任务间隔(每天) + public static final long CLEANUP_INTERVAL = 24 * 60 * 60 * 1000; + + private Context mContext; + private ContentResolver mContentResolver; + + // 单例实例 + private static RecentlyDeletedManager sInstance; + // 日志标签 + private static final String TAG = "RecentlyDeletedManager"; + + /** + * 私有构造方法,防止外部实例化 + * @param context 上下文对象 + */ + private RecentlyDeletedManager(Context context) { + mContext = context.getApplicationContext(); + mContentResolver = mContext.getContentResolver(); + } + + /** + * 获取单例实例 + * @param context 上下文对象 + * @return 最近删除管理器实例 + */ + public static synchronized RecentlyDeletedManager getInstance(Context context) { + if (sInstance == null) { + sInstance = new RecentlyDeletedManager(context); + } + return sInstance; + } + + /** + * 获取最近删除的项列表 + * @return 最近删除项的游标,按删除时间倒序排列 + */ + public Cursor getRecentlyDeletedItems() { + // 查询条件:已删除(DELETED_DATE > 0)且位于回收站文件夹 + String selection = NoteColumns.DELETED_DATE + " > 0 AND " + NoteColumns.PARENT_ID + "=" + Notes.ID_TRASH_FOLER; + // 按删除时间倒序排序 + String sortOrder = NoteColumns.DELETED_DATE + " DESC"; + + try { + return mContentResolver.query( + Notes.CONTENT_NOTE_URI, + null, // 查询所有列 + selection, + null, + sortOrder + ); + } catch (Exception e) { + Log.e(TAG, "Failed to get recently deleted items", e); + return null; + } + } + + /** + * 恢复指定项 + * @param itemIds 要恢复的项ID数组 + * @return 成功恢复的项数量 + */ + public int restoreItems(long[] itemIds) { + if (itemIds == null || itemIds.length == 0) { + return 0; + } + + int restoredCount = 0; + + for (long itemId : itemIds) { + try { + // 获取原父文件夹ID + String[] projection = {NoteColumns.ORIGIN_PARENT_ID}; + String selection = NoteColumns.ID + "=" + itemId; + + Cursor cursor = mContentResolver.query( + Notes.CONTENT_NOTE_URI, + projection, + selection, + null, + null + ); + + if (cursor != null && cursor.moveToFirst()) { + long originParentId = cursor.getLong(0); + cursor.close(); + + // 如果原父文件夹不存在或已被删除,使用根文件夹 + long targetParentId = originParentId != Notes.ID_TRASH_FOLER ? originParentId : Notes.ID_ROOT_FOLDER; + + // 使用现有的batchMoveToFolder方法来恢复项目 + HashSet
diff --git a/src/main/java/net/micode/notes/ui/TemplateSelectActivity.java b/src/main/java/net/micode/notes/ui/TemplateSelectActivity.java
deleted file mode 100644
index 46999d5..0000000
--- a/src/main/java/net/micode/notes/ui/TemplateSelectActivity.java
+++ /dev/null
@@ -1,207 +0,0 @@
-package net.micode.notes.ui;
-
-import android.app.Activity;
-import android.app.AlertDialog;
-import android.content.DialogInterface;
-import android.content.Intent;
-import android.os.Bundle;
-import android.text.TextUtils;
-import android.view.LayoutInflater;
-import android.view.View;
-import android.view.ViewGroup;
-import android.widget.EditText;
-import android.widget.ImageView;
-import android.widget.TextView;
-import android.widget.Toast;
-
-import androidx.recyclerview.widget.LinearLayoutManager;
-import androidx.recyclerview.widget.RecyclerView;
-
-import net.micode.notes.R;
-import net.micode.notes.model.NoteTemplate;
-import net.micode.notes.model.TemplateManager;
-
-import java.util.List;
-
-/**
- * 模板选择Activity,用于展示和选择笔记模板
- */
-public class TemplateSelectActivity extends Activity {
- public static final String EXTRA_TEMPLATE_CONTENT = "template_content";
- public static final String EXTRA_CURRENT_NOTE_CONTENT = "current_note_content";
-
- private RecyclerView mRecyclerView;
- private TemplateAdapter mAdapter;
- private List