From fffe5ba19379e35e96b5efff53ed30e09e24eb14 Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?=E9=99=B6=E4=BF=8A=E5=AE=87?= <2643473564@qq.com>
Date: Tue, 27 Jan 2026 00:18:46 +0800
Subject: [PATCH] =?UTF-8?q?=E4=BF=AE=E5=A4=8D=E5=AF=86=E7=A0=81=E9=94=81?=
=?UTF-8?q?=E5=AE=89=E5=85=A8=E6=BC=8F=E6=B4=9E?=
MIME-Version: 1.0
Content-Type: text/plain; charset=UTF-8
Content-Transfer-Encoding: 8bit
---
src/main/AndroidManifest.xml | 9 +-
.../java/net/micode/notes/data/Notes.java | 7 +
.../notes/data/NotesDatabaseHelper.java | 19 +-
.../net/micode/notes/model/NoteTemplate.java | 119 ++++++++
.../micode/notes/model/TemplateManager.java | 271 ++++++++++++++++++
.../net/micode/notes/ui/NoteEditActivity.java | 223 +++++++++++++-
.../net/micode/notes/ui/NoteItemData.java | 30 +-
.../micode/notes/ui/NotesListActivity.java | 139 ++++++---
.../net/micode/notes/ui/NotesListItem.java | 34 ++-
.../notes/ui/TemplateSelectActivity.java | 207 +++++++++++++
src/main/res/drawable/template_item_bg.xml | 21 ++
src/main/res/drawable/template_type_bg.xml | 9 +
src/main/res/layout/template_item.xml | 43 +++
src/main/res/layout/template_select.xml | 59 ++++
src/main/res/menu/note_edit.xml | 8 +
src/main/res/values/strings.xml | 23 +-
16 files changed, 1168 insertions(+), 53 deletions(-)
create mode 100644 src/main/java/net/micode/notes/model/NoteTemplate.java
create mode 100644 src/main/java/net/micode/notes/model/TemplateManager.java
create mode 100644 src/main/java/net/micode/notes/ui/TemplateSelectActivity.java
create mode 100644 src/main/res/drawable/template_item_bg.xml
create mode 100644 src/main/res/drawable/template_type_bg.xml
create mode 100644 src/main/res/layout/template_item.xml
create mode 100644 src/main/res/layout/template_select.xml
diff --git a/src/main/AndroidManifest.xml b/src/main/AndroidManifest.xml
index cb43220..243ffcc 100644
--- a/src/main/AndroidManifest.xml
+++ b/src/main/AndroidManifest.xml
@@ -125,7 +125,7 @@
+
+
+
diff --git a/src/main/java/net/micode/notes/data/Notes.java b/src/main/java/net/micode/notes/data/Notes.java
index 297b7dc..33f8d3a 100644
--- a/src/main/java/net/micode/notes/data/Notes.java
+++ b/src/main/java/net/micode/notes/data/Notes.java
@@ -265,6 +265,13 @@ public class Notes {
*/
public static final String LOCK_TYPE = "lock_type";
+ /**
+ * 密码提示
+ * 类型: TEXT
+ * 用于帮助用户记忆密码的提示信息
+ */
+ public static final String LOCK_HINT = "lock_hint";
+
/**
* 便签或文件夹被删除的时间
* 类型: INTEGER (long)
diff --git a/src/main/java/net/micode/notes/data/NotesDatabaseHelper.java b/src/main/java/net/micode/notes/data/NotesDatabaseHelper.java
index 4edf21d..d31c1c5 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 = 7;
+ private static final int DB_VERSION = 8;
/**
* 数据库表名常量定义
@@ -79,6 +79,7 @@ public class NotesDatabaseHelper extends SQLiteOpenHelper {
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_HINT + " TEXT NOT NULL DEFAULT ''," +
NoteColumns.DELETED_DATE + " INTEGER NOT NULL DEFAULT 0" +
")";
@@ -425,6 +426,12 @@ public class NotesDatabaseHelper extends SQLiteOpenHelper {
oldVersion++;
}
+ // 版本7升级到版本8:添加密码提示字段
+ if (oldVersion == 7) {
+ upgradeToV8(db);
+ oldVersion++;
+ }
+
// 如果需要,重新创建触发器
if (reCreateTriggers) {
reCreateNoteTableTriggers(db);
@@ -530,4 +537,14 @@ public class NotesDatabaseHelper extends SQLiteOpenHelper {
" WHERE " + NoteColumns.ID + "=new." + NoteColumns.ID + ";" +
" END");
}
+
+ /**
+ * 升级到版本8:添加密码提示字段,用于密码记忆辅助
+ * @param db 数据库实例
+ */
+ private void upgradeToV8(SQLiteDatabase db) {
+ // 添加LOCK_HINT字段,用于存储密码提示
+ db.execSQL("ALTER TABLE " + TABLE.NOTE + " ADD COLUMN " + NoteColumns.LOCK_HINT
+ + " TEXT NOT NULL DEFAULT ''");
+ }
}
\ No newline at end of file
diff --git a/src/main/java/net/micode/notes/model/NoteTemplate.java b/src/main/java/net/micode/notes/model/NoteTemplate.java
new file mode 100644
index 0000000..c956f7d
--- /dev/null
+++ b/src/main/java/net/micode/notes/model/NoteTemplate.java
@@ -0,0 +1,119 @@
+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/TemplateManager.java b/src/main/java/net/micode/notes/model/TemplateManager.java
new file mode 100644
index 0000000..be93fc8
--- /dev/null
+++ b/src/main/java/net/micode/notes/model/TemplateManager.java
@@ -0,0 +1,271 @@
+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 TemplateManager {
+ private static final String PREF_TEMPLATES = "note_templates";
+ private static final String PREF_TEMPLATE_ID_COUNTER = "template_id_counter";
+ private static TemplateManager sInstance;
+ private Context mContext;
+ private List mTemplates;
+
+ private TemplateManager(Context context) {
+ mContext = context.getApplicationContext();
+ loadTemplates();
+ }
+
+ public static synchronized TemplateManager getInstance(Context context) {
+ if (sInstance == null) {
+ sInstance = new TemplateManager(context);
+ }
+ return sInstance;
+ }
+
+ /**
+ * 加载所有模板,包括系统模板和自定义模板
+ */
+ private void loadTemplates() {
+ mTemplates = new ArrayList<>();
+
+ // 先添加系统模板
+ addSystemTemplates();
+
+ // 然后加载自定义模板
+ loadCustomTemplates();
+ }
+
+ /**
+ * 添加系统模板
+ */
+ private void addSystemTemplates() {
+ // 会议记录模板
+ NoteTemplate meetingTemplate = new NoteTemplate();
+ meetingTemplate.setId(1);
+ meetingTemplate.setName("会议记录");
+ meetingTemplate.setContent("会议记录
" +
+ "会议主题:
" +
+ "会议时间:
" +
+ "会议地点:
" +
+ "参会人员:
" +
+ "主持人:
" +
+ "会议议程
" +
+ "1.
" +
+ "2.
" +
+ "3.
" +
+ "会议内容
" +
+ "决议事项
" +
+ "1.
" +
+ "2.
" +
+ "3.
" +
+ "行动项
" +
+ "任务:负责人:截止日期:状态:
" +
+ "
" +
+ "下次会议
" +
+ "时间:
" +
+ "主题:
");
+ meetingTemplate.setType(NoteTemplate.TYPE_SYSTEM);
+ mTemplates.add(meetingTemplate);
+
+ // 待办事项模板
+ NoteTemplate todoTemplate = new NoteTemplate();
+ todoTemplate.setId(2);
+ todoTemplate.setName("待办事项");
+ todoTemplate.setContent("待办事项
" +
+ "日期:
" +
+ "今日待办
" +
+ "☐
" +
+ "☐
" +
+ "☐
" +
+ "☐
" +
+ "☐
" +
+ "重要事项
" +
+ "⭐
" +
+ "⭐
" +
+ "已完成
" +
+ "✓
" +
+ "✓
" +
+ "✓
");
+ todoTemplate.setType(NoteTemplate.TYPE_SYSTEM);
+ mTemplates.add(todoTemplate);
+
+ // 购物清单模板
+ NoteTemplate shoppingTemplate = new NoteTemplate();
+ shoppingTemplate.setId(3);
+ shoppingTemplate.setName("购物清单");
+ shoppingTemplate.setContent("购物清单
" +
+ "日期:
" +
+ "商店:
" +
+ "预算:
" +
+ "清单
" +
+ "物品:数量:单价:备注:状态:
" +
+ " ☐
" +
+ " ☐
" +
+ " ☐
" +
+ " ☐
" +
+ " ☐
" +
+ "总计
" +
+ "实际花费:
" +
+ "节省/超支:
");
+ shoppingTemplate.setType(NoteTemplate.TYPE_SYSTEM);
+ mTemplates.add(shoppingTemplate);
+ }
+
+ /**
+ * 加载自定义模板
+ */
+ private void loadCustomTemplates() {
+ SharedPreferences prefs = PreferenceManager.getDefaultSharedPreferences(mContext);
+ String templatesJson = prefs.getString(PREF_TEMPLATES, "[]");
+
+ try {
+ JSONArray jsonArray = new JSONArray(templatesJson);
+ for (int i = 0; i < jsonArray.length(); i++) {
+ JSONObject json = jsonArray.getJSONObject(i);
+ NoteTemplate template = NoteTemplate.fromJson(json);
+ mTemplates.add(template);
+ }
+ } catch (JSONException e) {
+ e.printStackTrace();
+ }
+ }
+
+ /**
+ * 保存自定义模板到SharedPreferences
+ */
+ private void saveCustomTemplates() {
+ SharedPreferences prefs = PreferenceManager.getDefaultSharedPreferences(mContext);
+ SharedPreferences.Editor editor = prefs.edit();
+
+ JSONArray jsonArray = new JSONArray();
+ for (NoteTemplate template : mTemplates) {
+ if (NoteTemplate.TYPE_CUSTOM.equals(template.getType())) {
+ try {
+ jsonArray.put(template.toJson());
+ } catch (JSONException e) {
+ e.printStackTrace();
+ }
+ }
+ }
+
+ editor.putString(PREF_TEMPLATES, jsonArray.toString());
+ editor.apply();
+ }
+
+ /**
+ * 获取所有模板
+ */
+ public List getAllTemplates() {
+ return new ArrayList<>(mTemplates);
+ }
+
+ /**
+ * 获取系统模板
+ */
+ public List getSystemTemplates() {
+ List systemTemplates = new ArrayList<>();
+ for (NoteTemplate template : mTemplates) {
+ if (NoteTemplate.TYPE_SYSTEM.equals(template.getType())) {
+ systemTemplates.add(template);
+ }
+ }
+ return systemTemplates;
+ }
+
+ /**
+ * 获取自定义模板
+ */
+ public List getCustomTemplates() {
+ List customTemplates = new ArrayList<>();
+ for (NoteTemplate template : mTemplates) {
+ if (NoteTemplate.TYPE_CUSTOM.equals(template.getType())) {
+ customTemplates.add(template);
+ }
+ }
+ return customTemplates;
+ }
+
+ /**
+ * 根据ID获取模板
+ */
+ public NoteTemplate getTemplateById(long id) {
+ for (NoteTemplate template : mTemplates) {
+ if (template.getId() == id) {
+ return template;
+ }
+ }
+ return null;
+ }
+
+ /**
+ * 添加自定义模板
+ */
+ public void addCustomTemplate(NoteTemplate template) {
+ // 生成唯一ID
+ SharedPreferences prefs = PreferenceManager.getDefaultSharedPreferences(mContext);
+ long id = prefs.getLong(PREF_TEMPLATE_ID_COUNTER, 1000);
+
+ template.setId(id);
+ template.setType(NoteTemplate.TYPE_CUSTOM);
+ template.setCreateTime(System.currentTimeMillis());
+ template.setUpdateTime(System.currentTimeMillis());
+
+ mTemplates.add(template);
+
+ // 更新ID计数器
+ prefs.edit().putLong(PREF_TEMPLATE_ID_COUNTER, id + 1).apply();
+
+ // 保存自定义模板
+ saveCustomTemplates();
+ }
+
+ /**
+ * 删除自定义模板
+ */
+ public boolean deleteCustomTemplate(long id) {
+ NoteTemplate templateToRemove = null;
+ for (NoteTemplate template : mTemplates) {
+ if (template.getId() == id && NoteTemplate.TYPE_CUSTOM.equals(template.getType())) {
+ templateToRemove = template;
+ break;
+ }
+ }
+
+ if (templateToRemove != null) {
+ mTemplates.remove(templateToRemove);
+ saveCustomTemplates();
+ return true;
+ }
+ return false;
+ }
+
+ /**
+ * 更新自定义模板
+ */
+ public boolean updateCustomTemplate(NoteTemplate updatedTemplate) {
+ for (int i = 0; i < mTemplates.size(); i++) {
+ NoteTemplate template = mTemplates.get(i);
+ if (template.getId() == updatedTemplate.getId() &&
+ NoteTemplate.TYPE_CUSTOM.equals(template.getType())) {
+
+ updatedTemplate.setUpdateTime(System.currentTimeMillis());
+ mTemplates.set(i, updatedTemplate);
+ saveCustomTemplates();
+ return true;
+ }
+ }
+ return false;
+ }
+}
diff --git a/src/main/java/net/micode/notes/ui/NoteEditActivity.java b/src/main/java/net/micode/notes/ui/NoteEditActivity.java
index 184fe3b..a6d70ed 100644
--- a/src/main/java/net/micode/notes/ui/NoteEditActivity.java
+++ b/src/main/java/net/micode/notes/ui/NoteEditActivity.java
@@ -46,6 +46,9 @@ import android.view.View.OnClickListener;
import android.view.WindowManager;
import android.widget.Button;
import android.widget.CheckBox;
+import android.widget.LinearLayout;
+import android.widget.TextView;
+import android.util.TypedValue;
import android.widget.CompoundButton;
import android.widget.CompoundButton.OnCheckedChangeListener;
import android.widget.EditText;
@@ -69,6 +72,7 @@ import net.micode.notes.tool.ResourceParser;
import net.micode.notes.tool.ResourceParser.TextAppearanceResources;
import net.micode.notes.ui.DateTimePickerDialog.OnDateTimeSetListener;
import net.micode.notes.ui.NoteEditText.OnTextViewChangeListener;
+import net.micode.notes.ui.TemplateSelectActivity;
import net.micode.notes.widget.NoteWidgetProvider_2x;
import net.micode.notes.widget.NoteWidgetProvider_4x;
@@ -190,6 +194,9 @@ public class NoteEditActivity extends Activity implements OnClickListener,
/** 未选中标记 */
public static final String TAG_UNCHECKED = String.valueOf('\u25A1');
+ /** 模板选择请求码 */
+ private static final int REQUEST_CODE_TEMPLATE = 1001;
+
/** 清单模式下的编辑文本列表 */
private LinearLayout mEditTextList;
@@ -764,6 +771,12 @@ public class NoteEditActivity extends Activity implements OnClickListener,
case R.id.menu_unlock:
showPasswordDialogForUnlock();
break;
+ case R.id.menu_change_password:
+ showChangePasswordDialog();
+ break;
+ case R.id.menu_note_template:
+ openTemplateSelector();
+ break;
default:
break;
}
@@ -805,23 +818,41 @@ public class NoteEditActivity extends Activity implements OnClickListener,
/**
* 显示密码输入对话框用于锁定笔记
*
- * 该方法用于显示一个密码输入对话框,让用户输入密码来锁定当前编辑的笔记
+ * 该方法用于显示一个密码输入对话框,让用户输入密码和密码提示来锁定当前编辑的笔记
*
*/
private void showPasswordDialogForLock() {
+ // 创建布局并添加密码输入框
+ LinearLayout layout = new LinearLayout(this);
+ layout.setOrientation(LinearLayout.VERTICAL);
+ layout.setPadding(60, 40, 60, 40);
+ layout.setPadding(60, 40, 60, 40);
+ // 设置子视图间距
+ LinearLayout.LayoutParams params = new LinearLayout.LayoutParams(
+ LinearLayout.LayoutParams.MATCH_PARENT,
+ LinearLayout.LayoutParams.WRAP_CONTENT);
+ params.setMargins(0, 0, 0, 20); // 底部外边距作为间距
+
final EditText passwordEditText = new EditText(this);
passwordEditText.setHint(R.string.hint_enter_password);
passwordEditText.setInputType(android.text.InputType.TYPE_CLASS_TEXT | android.text.InputType.TYPE_TEXT_VARIATION_PASSWORD);
+ layout.addView(passwordEditText);
+
+ // 添加密码提示输入框
+ final EditText hintEditText = new EditText(this);
+ hintEditText.setHint(R.string.hint_enter_password_hint);
+ layout.addView(hintEditText);
new AlertDialog.Builder(this)
.setTitle(R.string.dialog_enter_password)
- .setView(passwordEditText)
+ .setView(layout)
.setPositiveButton(android.R.string.ok, new DialogInterface.OnClickListener() {
@Override
public void onClick(DialogInterface dialog, int which) {
String password = passwordEditText.getText().toString();
+ String hint = hintEditText.getText().toString();
if (!TextUtils.isEmpty(password)) {
- lockCurrentNote(password);
+ lockCurrentNote(password, hint);
}
}
})
@@ -836,13 +867,50 @@ public class NoteEditActivity extends Activity implements OnClickListener,
*
*/
private void showPasswordDialogForUnlock() {
+ // 查询当前笔记的密码提示
+ String[] projection = {NoteColumns.LOCK_HINT};
+ String selection = NoteColumns.ID + "=?";
+ String[] selectionArgs = {String.valueOf(mWorkingNote.getNoteId())};
+
+ Cursor cursor = getContentResolver().query(Notes.CONTENT_NOTE_URI, projection, selection, selectionArgs, null);
+ String lockHint = null;
+ if (cursor != null && cursor.moveToFirst()) {
+ lockHint = cursor.getString(0);
+ cursor.close();
+ }
+
+ // 创建布局并添加密码输入框
+ LinearLayout layout = new LinearLayout(this);
+ layout.setOrientation(LinearLayout.VERTICAL);
+ layout.setPadding(60, 40, 60, 40);
+
+ // 创建布局参数,用于设置子视图间距
+ LinearLayout.LayoutParams params = new LinearLayout.LayoutParams(
+ LinearLayout.LayoutParams.MATCH_PARENT,
+ LinearLayout.LayoutParams.WRAP_CONTENT);
+ params.setMargins(0, 0, 0, 20); // 底部外边距作为间距
+
final EditText passwordEditText = new EditText(this);
passwordEditText.setHint(R.string.hint_enter_password);
passwordEditText.setInputType(android.text.InputType.TYPE_CLASS_TEXT | android.text.InputType.TYPE_TEXT_VARIATION_PASSWORD);
+ layout.addView(passwordEditText, params);
+
+ // 如果有密码提示,显示提示信息
+ if (!TextUtils.isEmpty(lockHint)) {
+ TextView hintTextView = new TextView(this);
+ hintTextView.setText(getString(R.string.password_hint_prefix) + lockHint);
+ hintTextView.setTextColor(getResources().getColor(R.color.secondary_text_dark));
+ hintTextView.setTextSize(TypedValue.COMPLEX_UNIT_SP, 14);
+ // 提示文本不需要底部间距
+ LinearLayout.LayoutParams hintParams = new LinearLayout.LayoutParams(
+ LinearLayout.LayoutParams.MATCH_PARENT,
+ LinearLayout.LayoutParams.WRAP_CONTENT);
+ layout.addView(hintTextView, hintParams);
+ }
new AlertDialog.Builder(this)
.setTitle(R.string.dialog_enter_password)
- .setView(passwordEditText)
+ .setView(layout)
.setPositiveButton(android.R.string.ok, new DialogInterface.OnClickListener() {
@Override
public void onClick(DialogInterface dialog, int which) {
@@ -859,17 +927,20 @@ public class NoteEditActivity extends Activity implements OnClickListener,
/**
* 锁定当前笔记
*
- * 该方法用于锁定当前编辑的笔记,更新其锁定状态、密码和锁定类型
+ * 该方法用于锁定当前编辑的笔记,更新其锁定状态、密码、密码提示和锁定类型
*
* @param password 用于锁定的密码
+ * @param hint 用于帮助记忆密码的提示
*/
- private void lockCurrentNote(String password) {
+ private void lockCurrentNote(String password, String hint) {
// 设置锁定状态为1(已锁定)
mWorkingNote.setNoteValue(NoteColumns.IS_LOCKED, "1");
// 设置加密后的密码
mWorkingNote.setNoteValue(NoteColumns.LOCK_PASSWORD, encryptPassword(password));
// 设置锁定类型为笔记类型
mWorkingNote.setNoteValue(NoteColumns.LOCK_TYPE, String.valueOf(Notes.LOCK_TYPE_NOTE));
+ // 设置密码提示
+ mWorkingNote.setNoteValue(NoteColumns.LOCK_HINT, hint);
// 保存笔记
saveNote();
// 显示提示信息
@@ -934,6 +1005,97 @@ public class NoteEditActivity extends Activity implements OnClickListener,
return password; // 加密失败时返回原始密码
}
}
+
+ /**
+ * 显示修改密码对话框
+ *
+ * 该方法用于显示一个修改密码对话框,让用户输入旧密码、新密码和新的密码提示
+ *
+ */
+ private void showChangePasswordDialog() {
+ // 创建布局并添加输入框
+ LinearLayout layout = new LinearLayout(this);
+ layout.setOrientation(LinearLayout.VERTICAL);
+ layout.setPadding(60, 40, 60, 40);
+
+ // 创建布局参数,用于设置子视图间距
+ LinearLayout.LayoutParams params = new LinearLayout.LayoutParams(
+ LinearLayout.LayoutParams.MATCH_PARENT,
+ LinearLayout.LayoutParams.WRAP_CONTENT);
+ params.setMargins(0, 0, 0, 20); // 底部外边距作为间距
+
+ // 旧密码输入框
+ final EditText oldPasswordEditText = new EditText(this);
+ oldPasswordEditText.setHint(R.string.hint_enter_password);
+ oldPasswordEditText.setInputType(android.text.InputType.TYPE_CLASS_TEXT | android.text.InputType.TYPE_TEXT_VARIATION_PASSWORD);
+ layout.addView(oldPasswordEditText, params);
+
+ // 新密码输入框
+ final EditText newPasswordEditText = new EditText(this);
+ newPasswordEditText.setHint(R.string.hint_enter_password);
+ newPasswordEditText.setInputType(android.text.InputType.TYPE_CLASS_TEXT | android.text.InputType.TYPE_TEXT_VARIATION_PASSWORD);
+ layout.addView(newPasswordEditText, params);
+
+ // 新密码提示输入框
+ final EditText newHintEditText = new EditText(this);
+ newHintEditText.setHint(R.string.hint_enter_password_hint);
+ layout.addView(newHintEditText);
+
+ new AlertDialog.Builder(this)
+ .setTitle(R.string.menu_change_password)
+ .setView(layout)
+ .setPositiveButton(android.R.string.ok, new DialogInterface.OnClickListener() {
+ @Override
+ public void onClick(DialogInterface dialog, int which) {
+ String oldPassword = oldPasswordEditText.getText().toString();
+ String newPassword = newPasswordEditText.getText().toString();
+ String newHint = newHintEditText.getText().toString();
+ if (!TextUtils.isEmpty(oldPassword) && !TextUtils.isEmpty(newPassword)) {
+ changePassword(oldPassword, newPassword, newHint);
+ }
+ }
+ })
+ .setNegativeButton(android.R.string.cancel, null)
+ .show();
+ }
+
+ /**
+ * 修改密码
+ *
+ * 该方法用于修改当前笔记的密码,需要验证旧密码,验证通过后更新新密码和密码提示
+ *
+ * @param oldPassword 旧密码
+ * @param newPassword 新密码
+ * @param newHint 新的密码提示
+ */
+ private void changePassword(String oldPassword, String newPassword, String newHint) {
+ // 使用ContentResolver直接查询数据库获取加密密码
+ String[] projection = {NoteColumns.LOCK_PASSWORD};
+ String selection = NoteColumns.ID + "=?";
+ String[] selectionArgs = {String.valueOf(mWorkingNote.getNoteId())};
+
+ Cursor cursor = getContentResolver().query(Notes.CONTENT_NOTE_URI, projection, selection, selectionArgs, null);
+ String encryptedPassword = null;
+
+ if (cursor != null && cursor.moveToFirst()) {
+ encryptedPassword = cursor.getString(0);
+ cursor.close();
+ }
+
+ // 验证旧密码是否正确
+ if (encryptedPassword != null && encryptedPassword.equals(encryptPassword(oldPassword))) {
+ // 旧密码正确,更新新密码和密码提示
+ mWorkingNote.setNoteValue(NoteColumns.LOCK_PASSWORD, encryptPassword(newPassword));
+ mWorkingNote.setNoteValue(NoteColumns.LOCK_HINT, newHint);
+ // 保存笔记
+ saveNote();
+ // 显示提示信息
+ Toast.makeText(this, getString(R.string.message_password_changed), Toast.LENGTH_SHORT).show();
+ } else {
+ // 旧密码错误,显示错误信息
+ Toast.makeText(this, getString(R.string.error_wrong_password), Toast.LENGTH_SHORT).show();
+ }
+ }
/**
* 创建新笔记
@@ -1375,6 +1537,55 @@ public class NoteEditActivity extends Activity implements OnClickListener,
showToast(resId, Toast.LENGTH_SHORT);
}
+ /**
+ * 打开模板选择页面
+ *
+ * 该方法启动TemplateSelectActivity,让用户选择便签模板
+ *
+ */
+ private void openTemplateSelector() {
+ // 获取当前笔记内容
+ getWorkingText();
+ String currentContent = mWorkingNote.getContent();
+
+ Intent intent = new Intent(this, TemplateSelectActivity.class);
+ intent.putExtra(TemplateSelectActivity.EXTRA_CURRENT_NOTE_CONTENT, currentContent);
+ startActivityForResult(intent, REQUEST_CODE_TEMPLATE);
+ }
+
+ /**
+ * 处理从模板选择页面返回的结果
+ *
+ * 该方法在从TemplateSelectActivity返回时被调用,
+ * 如果用户选择了模板,则更新当前笔记的内容
+ *
+ * @param requestCode 请求码
+ * @param resultCode 结果码
+ * @param data 返回的Intent数据
+ */
+ @Override
+ protected void onActivityResult(int requestCode, int resultCode, Intent data) {
+ super.onActivityResult(requestCode, resultCode, data);
+ if (requestCode == REQUEST_CODE_TEMPLATE && resultCode == RESULT_OK) {
+ if (data != null) {
+ String templateContent = data.getStringExtra(TemplateSelectActivity.EXTRA_TEMPLATE_CONTENT);
+ if (!TextUtils.isEmpty(templateContent)) {
+ // 保存模板内容到WorkingNote,防止onResume时被覆盖
+ mWorkingNote.setWorkingText(templateContent);
+
+ // 更新笔记内容
+ if (mWorkingNote.getCheckListMode() == TextNote.MODE_CHECK_LIST) {
+ switchToListMode(templateContent);
+ } else {
+ mNoteEditor.setText(Html.fromHtml(templateContent));
+ mNoteEditor.setSelection(mNoteEditor.getText().length());
+ }
+ Toast.makeText(this, R.string.notealert_enter, Toast.LENGTH_SHORT).show();
+ }
+ }
+ }
+ }
+
/**
* 显示Toast提示信息
*
diff --git a/src/main/java/net/micode/notes/ui/NoteItemData.java b/src/main/java/net/micode/notes/ui/NoteItemData.java
index 2dcdedf..9a51882 100644
--- a/src/main/java/net/micode/notes/ui/NoteItemData.java
+++ b/src/main/java/net/micode/notes/ui/NoteItemData.java
@@ -57,6 +57,7 @@ public class NoteItemData {
NoteColumns.IS_LOCKED,
NoteColumns.LOCK_PASSWORD,
NoteColumns.LOCK_TYPE,
+ NoteColumns.LOCK_HINT,
NoteColumns.DELETED_DATE,
};
@@ -140,10 +141,15 @@ public class NoteItemData {
*/
private static final int LOCK_TYPE_COLUMN = 15;
+ /**
+ * 查询结果中密码提示列的索引
+ */
+ private static final int LOCK_HINT_COLUMN = 16;
+
/**
* 查询结果中删除日期列的索引
*/
- private static final int DELETED_DATE_COLUMN = 16;
+ private static final int DELETED_DATE_COLUMN = 17;
/**
* 笔记ID
@@ -170,6 +176,11 @@ public class NoteItemData {
*/
private int mLockType;
+ /**
+ * 密码提示
+ */
+ private String mLockHint;
+
/**
* 提醒日期
*/
@@ -290,6 +301,7 @@ public class NoteItemData {
mIsLocked = (cursor.getInt(IS_LOCKED_COLUMN) > 0) ? true : false;
mLockPassword = cursor.getString(LOCK_PASSWORD_COLUMN);
mLockType = cursor.getInt(LOCK_TYPE_COLUMN);
+ mLockHint = cursor.getString(LOCK_HINT_COLUMN);
mDeletedDate = cursor.getLong(DELETED_DATE_COLUMN);
mPhoneNumber = "";
@@ -565,6 +577,22 @@ public class NoteItemData {
return mLockType;
}
+ /**
+ * 获取密码提示
+ * @return 密码提示字符串
+ */
+ public String getLockHint() {
+ return mLockHint;
+ }
+
+ /**
+ * 设置密码提示
+ * @param hint 密码提示字符串
+ */
+ public void setLockHint(String hint) {
+ mLockHint = hint;
+ }
+
/**
* 设置锁定类型
* @param type 锁定类型,0表示笔记,1表示文件夹
diff --git a/src/main/java/net/micode/notes/ui/NotesListActivity.java b/src/main/java/net/micode/notes/ui/NotesListActivity.java
index ae6ab3f..7eb7a8a 100644
--- a/src/main/java/net/micode/notes/ui/NotesListActivity.java
+++ b/src/main/java/net/micode/notes/ui/NotesListActivity.java
@@ -57,8 +57,10 @@ import android.widget.Button;
import android.widget.EditText;
import android.widget.ListView;
import android.widget.PopupMenu;
+import android.widget.LinearLayout;
import android.widget.TextView;
import android.widget.Toast;
+import android.util.TypedValue;
import net.micode.notes.R;
import net.micode.notes.data.Notes;
@@ -124,19 +126,37 @@ public class NotesListActivity extends Activity implements OnClickListener, OnIt
*
*/
private void showPasswordDialog() {
+ // 创建布局并添加输入框
+ LinearLayout layout = new LinearLayout(this);
+ layout.setOrientation(LinearLayout.VERTICAL);
+ layout.setPadding(60, 40, 60, 40);
+
+ // 创建布局参数,用于设置子视图间距
+ LinearLayout.LayoutParams params = new LinearLayout.LayoutParams(
+ LinearLayout.LayoutParams.MATCH_PARENT,
+ LinearLayout.LayoutParams.WRAP_CONTENT);
+ params.setMargins(0, 0, 0, 20); // 底部外边距作为间距
+
final EditText passwordEditText = new EditText(this);
passwordEditText.setHint(R.string.hint_enter_password);
passwordEditText.setInputType(android.text.InputType.TYPE_CLASS_TEXT | android.text.InputType.TYPE_TEXT_VARIATION_PASSWORD);
+ layout.addView(passwordEditText, params);
+
+ // 添加密码提示输入框
+ final EditText hintEditText = new EditText(this);
+ hintEditText.setHint(R.string.hint_enter_password_hint);
+ layout.addView(hintEditText);
new AlertDialog.Builder(this)
.setTitle(R.string.dialog_enter_password)
- .setView(passwordEditText)
+ .setView(layout)
.setPositiveButton(android.R.string.ok, new DialogInterface.OnClickListener() {
@Override
public void onClick(DialogInterface dialog, int which) {
String password = passwordEditText.getText().toString();
+ String hint = hintEditText.getText().toString();
if (!TextUtils.isEmpty(password)) {
- toggleLockedStatus(password);
+ toggleLockedStatus(password, hint);
}
}
})
@@ -150,8 +170,9 @@ public class NotesListActivity extends Activity implements OnClickListener, OnIt
* 该方法用于切换选中笔记或文件夹的锁定状态,使用异步任务处理数据库操作
*
* @param password 用于锁定的密码
+ * @param hint 用于帮助记忆密码的提示
*/
- private void toggleLockedStatus(final String password) {
+ private void toggleLockedStatus(final String password, final String hint) {
final HashSet selectedIds = mNotesListAdapter.getSelectedItemIds();
final int selectedCount = mNotesListAdapter.getSelectedCount();
@@ -159,45 +180,61 @@ public class NotesListActivity extends Activity implements OnClickListener, OnIt
protected Integer doInBackground(Void... unused) {
int finalLockedStatus = -1; // 默认为-1,表示未处理
for (Long noteId : selectedIds) {
- // 查询当前便签的锁定状态
+ // 查询当前便签的锁定状态和密码
Cursor cursor = mContentResolver.query(Notes.CONTENT_NOTE_URI,
- new String[]{NoteColumns.IS_LOCKED},
+ new String[]{NoteColumns.IS_LOCKED, NoteColumns.LOCK_PASSWORD},
NoteColumns.ID + "=?",
new String[]{String.valueOf(noteId)},
null);
if (cursor != null && cursor.moveToFirst()) {
int currentLocked = cursor.getInt(0);
- // 切换锁定状态
- int newLocked = currentLocked == 1 ? 0 : 1;
- finalLockedStatus = newLocked; // 保存最终锁定状态
-
- ContentValues values = new ContentValues();
- values.put(NoteColumns.IS_LOCKED, newLocked);
-
- if (newLocked == 1) {
- // 如果是锁定操作,设置密码和锁定类型
- values.put(NoteColumns.LOCK_PASSWORD, encryptPassword(password));
- // 判断是笔记还是文件夹
- Cursor typeCursor = mContentResolver.query(Notes.CONTENT_NOTE_URI,
- new String[]{NoteColumns.TYPE},
- NoteColumns.ID + "=?",
- new String[]{String.valueOf(noteId)},
- null);
- if (typeCursor != null && typeCursor.moveToFirst()) {
- int type = typeCursor.getInt(0);
- values.put(NoteColumns.LOCK_TYPE, type == Notes.TYPE_FOLDER ? Notes.LOCK_TYPE_FOLDER : Notes.LOCK_TYPE_NOTE);
- typeCursor.close();
+ String encryptedPassword = cursor.getString(1);
+ int newLocked = currentLocked;
+
+ if (currentLocked == 1) {
+ // 如果当前是锁定状态,需要验证密码才能解锁
+ if (encryptedPassword != null && encryptedPassword.equals(encryptPassword(password))) {
+ // 密码正确,解锁
+ newLocked = 0;
+ finalLockedStatus = newLocked;
}
} else {
- // 如果是解锁操作,清空密码
- values.put(NoteColumns.LOCK_PASSWORD, "");
+ // 如果当前是未锁定状态,直接锁定
+ newLocked = 1;
+ finalLockedStatus = newLocked;
}
- mContentResolver.update(Notes.CONTENT_NOTE_URI,
- values,
- NoteColumns.ID + "=?",
- new String[]{String.valueOf(noteId)});
+ if (newLocked != currentLocked) {
+ ContentValues values = new ContentValues();
+ values.put(NoteColumns.IS_LOCKED, newLocked);
+
+ if (newLocked == 1) {
+ // 如果是锁定操作,设置密码、密码提示和锁定类型
+ values.put(NoteColumns.LOCK_PASSWORD, encryptPassword(password));
+ values.put(NoteColumns.LOCK_HINT, hint);
+ // 判断是笔记还是文件夹
+ Cursor typeCursor = mContentResolver.query(Notes.CONTENT_NOTE_URI,
+ new String[]{NoteColumns.TYPE},
+ NoteColumns.ID + "=?",
+ new String[]{String.valueOf(noteId)},
+ null);
+ if (typeCursor != null && typeCursor.moveToFirst()) {
+ int type = typeCursor.getInt(0);
+ values.put(NoteColumns.LOCK_TYPE, type == Notes.TYPE_FOLDER ? Notes.LOCK_TYPE_FOLDER : Notes.LOCK_TYPE_NOTE);
+ typeCursor.close();
+ }
+ } else {
+ // 如果是解锁操作,清空密码和密码提示
+ values.put(NoteColumns.LOCK_PASSWORD, "");
+ values.put(NoteColumns.LOCK_HINT, "");
+ }
+
+ mContentResolver.update(Notes.CONTENT_NOTE_URI,
+ values,
+ NoteColumns.ID + "=?",
+ new String[]{String.valueOf(noteId)});
+ }
cursor.close();
}
@@ -207,11 +244,17 @@ public class NotesListActivity extends Activity implements OnClickListener, OnIt
@Override
protected void onPostExecute(Integer finalLockedStatus) {
- String message = finalLockedStatus == 1 ? getString(R.string.message_note_locked) : getString(R.string.message_note_unlocked);
- Toast.makeText(NotesListActivity.this, message, Toast.LENGTH_SHORT).show();
- // 重新查询数据,更新列表
- startAsyncNotesListQuery();
- mModeCallBack.finishActionMode();
+ if (finalLockedStatus == -1) {
+ // 密码错误,显示错误信息
+ Toast.makeText(NotesListActivity.this, getString(R.string.error_wrong_password), Toast.LENGTH_SHORT).show();
+ } else {
+ // 操作成功,显示结果
+ String message = finalLockedStatus == 1 ? getString(R.string.message_note_locked) : getString(R.string.message_note_unlocked);
+ Toast.makeText(NotesListActivity.this, message, Toast.LENGTH_SHORT).show();
+ // 重新查询数据,更新列表
+ startAsyncNotesListQuery();
+ mModeCallBack.finishActionMode();
+ }
}
}.execute();
}
@@ -1193,13 +1236,35 @@ public class NotesListActivity extends Activity implements OnClickListener, OnIt
private void openNode(final NoteItemData data) {
// 检查笔记是否被锁定
if (data.isLocked()) {
+ // 创建布局并添加密码输入框
+ LinearLayout layout = new LinearLayout(this);
+ layout.setOrientation(LinearLayout.VERTICAL);
+ layout.setPadding(60, 40, 60, 40);
+
+ // 创建布局参数,用于设置子视图间距
+ LinearLayout.LayoutParams params = new LinearLayout.LayoutParams(
+ LinearLayout.LayoutParams.MATCH_PARENT,
+ LinearLayout.LayoutParams.WRAP_CONTENT);
+ params.setMargins(0, 0, 0, 20); // 底部外边距作为间距
+
final EditText passwordEditText = new EditText(this);
passwordEditText.setHint(R.string.hint_enter_password);
passwordEditText.setInputType(android.text.InputType.TYPE_CLASS_TEXT | android.text.InputType.TYPE_TEXT_VARIATION_PASSWORD);
+ layout.addView(passwordEditText, params);
+
+ // 获取密码提示并显示
+ String lockHint = data.getLockHint();
+ if (!TextUtils.isEmpty(lockHint)) {
+ TextView hintTextView = new TextView(this);
+ hintTextView.setText(getString(R.string.password_hint_prefix) + lockHint);
+ hintTextView.setTextColor(getResources().getColor(R.color.secondary_text_dark));
+ hintTextView.setTextSize(TypedValue.COMPLEX_UNIT_SP, 14);
+ layout.addView(hintTextView);
+ }
new AlertDialog.Builder(this)
.setTitle(R.string.dialog_enter_password)
- .setView(passwordEditText)
+ .setView(layout)
.setPositiveButton(android.R.string.ok, new DialogInterface.OnClickListener() {
@Override
public void onClick(DialogInterface dialog, int which) {
diff --git a/src/main/java/net/micode/notes/ui/NotesListItem.java b/src/main/java/net/micode/notes/ui/NotesListItem.java
index 069771a..32dfbcd 100644
--- a/src/main/java/net/micode/notes/ui/NotesListItem.java
+++ b/src/main/java/net/micode/notes/ui/NotesListItem.java
@@ -99,20 +99,34 @@ public class NotesListItem extends LinearLayout {
mTitle.setTextAppearance(context, R.style.TextAppearancePrimaryItem);
mTitle.setText(context.getString(R.string.call_record_folder_name)
+ context.getString(R.string.format_folder_files_count, data.getNotesCount()));
- mAlert.setImageResource(R.drawable.call_record);
+ // 检查是否被锁定
+ if (data.isLocked()) {
+ mAlert.setImageResource(android.R.drawable.ic_lock_lock);
+ } else {
+ mAlert.setImageResource(R.drawable.call_record);
+ }
} else if (data.getId() == Notes.ID_TRASH_FOLER) {
mCallName.setVisibility(View.GONE);
mAlert.setVisibility(View.VISIBLE);
mTitle.setTextAppearance(context, R.style.TextAppearancePrimaryItem);
mTitle.setText("回收站"
+ context.getString(R.string.format_folder_files_count, data.getNotesCount()));
- mAlert.setImageResource(R.drawable.delete);
+ // 检查是否被锁定
+ if (data.isLocked()) {
+ mAlert.setImageResource(android.R.drawable.ic_lock_lock);
+ } else {
+ mAlert.setImageResource(R.drawable.delete);
+ }
} else if (data.getParentId() == Notes.ID_CALL_RECORD_FOLDER) {
mCallName.setVisibility(View.VISIBLE);
mCallName.setText(data.getCallName());
mTitle.setTextAppearance(context,R.style.TextAppearanceSecondaryItem);
mTitle.setText(Html.fromHtml(DataUtils.getFormattedSnippet(data.getSnippet())));
- if (data.hasAlert()) {
+ // 检查是否被锁定
+ if (data.isLocked()) {
+ mAlert.setImageResource(android.R.drawable.ic_lock_lock);
+ mAlert.setVisibility(View.VISIBLE);
+ } else if (data.hasAlert()) {
mAlert.setImageResource(R.drawable.clock);
mAlert.setVisibility(View.VISIBLE);
} else {
@@ -126,10 +140,20 @@ public class NotesListItem extends LinearLayout {
mTitle.setText(data.getSnippet()
+ context.getString(R.string.format_folder_files_count,
data.getNotesCount()));
- mAlert.setVisibility(View.GONE);
+ // 检查文件夹是否被锁定
+ if (data.isLocked()) {
+ mAlert.setImageResource(android.R.drawable.ic_lock_lock);
+ mAlert.setVisibility(View.VISIBLE);
+ } else {
+ mAlert.setVisibility(View.GONE);
+ }
} else {
mTitle.setText(Html.fromHtml(DataUtils.getFormattedSnippet(data.getSnippet())));
- if (data.hasAlert()) {
+ // 检查笔记是否被锁定
+ if (data.isLocked()) {
+ mAlert.setImageResource(android.R.drawable.ic_lock_lock);
+ mAlert.setVisibility(View.VISIBLE);
+ } else if (data.hasAlert()) {
mAlert.setImageResource(R.drawable.clock);
mAlert.setVisibility(View.VISIBLE);
} else {
diff --git a/src/main/java/net/micode/notes/ui/TemplateSelectActivity.java b/src/main/java/net/micode/notes/ui/TemplateSelectActivity.java
new file mode 100644
index 0000000..46999d5
--- /dev/null
+++ b/src/main/java/net/micode/notes/ui/TemplateSelectActivity.java
@@ -0,0 +1,207 @@
+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 mTemplates;
+ private TemplateManager mTemplateManager;
+ private String mCurrentNoteContent;
+
+ @Override
+ protected void onCreate(Bundle savedInstanceState) {
+ super.onCreate(savedInstanceState);
+ setContentView(R.layout.template_select);
+
+ // 获取当前笔记内容
+ mCurrentNoteContent = getIntent().getStringExtra(EXTRA_CURRENT_NOTE_CONTENT);
+
+ // 初始化模板管理器
+ mTemplateManager = TemplateManager.getInstance(this);
+ mTemplates = mTemplateManager.getAllTemplates();
+
+ // 初始化RecyclerView
+ mRecyclerView = findViewById(R.id.rv_templates);
+ mRecyclerView.setLayoutManager(new LinearLayoutManager(this));
+ mAdapter = new TemplateAdapter(mTemplates);
+ mRecyclerView.setAdapter(mAdapter);
+
+ // 初始化按钮点击事件
+ findViewById(R.id.btn_close).setOnClickListener(new View.OnClickListener() {
+ @Override
+ public void onClick(View v) {
+ finish();
+ }
+ });
+
+ findViewById(R.id.btn_save_as_template).setOnClickListener(new View.OnClickListener() {
+ @Override
+ public void onClick(View v) {
+ saveAsTemplate();
+ }
+ });
+ }
+
+ /**
+ * 保存当前笔记作为自定义模板
+ */
+ private void saveAsTemplate() {
+ if (TextUtils.isEmpty(mCurrentNoteContent)) {
+ Toast.makeText(this, R.string.error_note_empty, Toast.LENGTH_SHORT).show();
+ return;
+ }
+
+ // 弹出对话框输入模板名称
+ final EditText editText = new EditText(this);
+ editText.setHint(R.string.enter_template_name);
+
+ new AlertDialog.Builder(this)
+ .setTitle(R.string.save_as_template)
+ .setView(editText)
+ .setPositiveButton(android.R.string.ok, new DialogInterface.OnClickListener() {
+ @Override
+ public void onClick(DialogInterface dialog, int which) {
+ String templateName = editText.getText().toString().trim();
+ if (TextUtils.isEmpty(templateName)) {
+ Toast.makeText(TemplateSelectActivity.this, R.string.error_template_name_empty, Toast.LENGTH_SHORT).show();
+ return;
+ }
+
+ // 创建并保存自定义模板
+ NoteTemplate template = new NoteTemplate();
+ template.setName(templateName);
+ template.setContent(mCurrentNoteContent);
+ template.setType(NoteTemplate.TYPE_CUSTOM);
+
+ mTemplateManager.addCustomTemplate(template);
+
+ // 更新模板列表
+ mTemplates = mTemplateManager.getAllTemplates();
+ mAdapter = new TemplateAdapter(mTemplates);
+ mRecyclerView.setAdapter(mAdapter);
+
+ Toast.makeText(TemplateSelectActivity.this, R.string.message_template_saved, Toast.LENGTH_SHORT).show();
+ }
+ })
+ .setNegativeButton(android.R.string.cancel, null)
+ .show();
+ }
+
+ /**
+ * 模板适配器,用于在RecyclerView中展示模板列表
+ */
+ private class TemplateAdapter extends RecyclerView.Adapter {
+ private List mTemplates;
+
+ public TemplateAdapter(List templates) {
+ mTemplates = templates;
+ }
+
+ @Override
+ public ViewHolder onCreateViewHolder(ViewGroup parent, int viewType) {
+ View view = LayoutInflater.from(parent.getContext())
+ .inflate(R.layout.template_item, parent, false);
+ return new ViewHolder(view);
+ }
+
+ @Override
+ public void onBindViewHolder(ViewHolder holder, int position) {
+ final NoteTemplate template = mTemplates.get(position);
+
+ holder.tvTemplateName.setText(template.getName());
+
+ // 设置模板类型标识
+ if (NoteTemplate.TYPE_SYSTEM.equals(template.getType())) {
+ holder.tvTemplateType.setText(R.string.template_type_system);
+ holder.ivDelete.setVisibility(View.GONE);
+ } else {
+ holder.tvTemplateType.setText(R.string.template_type_custom);
+ holder.ivDelete.setVisibility(View.VISIBLE);
+ }
+
+ // 设置点击事件
+ holder.itemView.setOnClickListener(new View.OnClickListener() {
+ @Override
+ public void onClick(View v) {
+ // 返回选择的模板内容
+ Intent intent = new Intent();
+ intent.putExtra(EXTRA_TEMPLATE_CONTENT, template.getContent());
+ setResult(RESULT_OK, intent);
+ finish();
+ }
+ });
+
+ // 设置删除按钮点击事件
+ holder.ivDelete.setOnClickListener(new View.OnClickListener() {
+ @Override
+ public void onClick(View v) {
+ new AlertDialog.Builder(TemplateSelectActivity.this)
+ .setTitle(R.string.alert_title_delete)
+ .setMessage(R.string.alert_message_delete_template)
+ .setPositiveButton(android.R.string.ok, new DialogInterface.OnClickListener() {
+ @Override
+ public void onClick(DialogInterface dialog, int which) {
+ // 删除自定义模板
+ mTemplateManager.deleteCustomTemplate(template.getId());
+
+ // 更新模板列表
+ mTemplates.clear();
+ mTemplates.addAll(mTemplateManager.getAllTemplates());
+ notifyDataSetChanged();
+
+ Toast.makeText(TemplateSelectActivity.this, R.string.message_template_deleted, Toast.LENGTH_SHORT).show();
+ }
+ })
+ .setNegativeButton(android.R.string.cancel, null)
+ .show();
+ }
+ });
+ }
+
+ @Override
+ public int getItemCount() {
+ return mTemplates.size();
+ }
+
+ public class ViewHolder extends RecyclerView.ViewHolder {
+ public TextView tvTemplateName;
+ public TextView tvTemplateType;
+ public ImageView ivDelete;
+
+ public ViewHolder(View itemView) {
+ super(itemView);
+ tvTemplateName = itemView.findViewById(R.id.tv_template_name);
+ tvTemplateType = itemView.findViewById(R.id.tv_template_type);
+ ivDelete = itemView.findViewById(R.id.iv_delete);
+ }
+ }
+ }
+}
diff --git a/src/main/res/drawable/template_item_bg.xml b/src/main/res/drawable/template_item_bg.xml
new file mode 100644
index 0000000..15117c7
--- /dev/null
+++ b/src/main/res/drawable/template_item_bg.xml
@@ -0,0 +1,21 @@
+
+
+ -
+
+
+
+
+
+
+ -
+
+
+
+
+
+
+
diff --git a/src/main/res/drawable/template_type_bg.xml b/src/main/res/drawable/template_type_bg.xml
new file mode 100644
index 0000000..94af0a3
--- /dev/null
+++ b/src/main/res/drawable/template_type_bg.xml
@@ -0,0 +1,9 @@
+
+
+
+
+
+
diff --git a/src/main/res/layout/template_item.xml b/src/main/res/layout/template_item.xml
new file mode 100644
index 0000000..0834bb4
--- /dev/null
+++ b/src/main/res/layout/template_item.xml
@@ -0,0 +1,43 @@
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/src/main/res/layout/template_select.xml b/src/main/res/layout/template_select.xml
new file mode 100644
index 0000000..e444f96
--- /dev/null
+++ b/src/main/res/layout/template_select.xml
@@ -0,0 +1,59 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/src/main/res/menu/note_edit.xml b/src/main/res/menu/note_edit.xml
index caee172..a1f9976 100644
--- a/src/main/res/menu/note_edit.xml
+++ b/src/main/res/menu/note_edit.xml
@@ -57,4 +57,12 @@
+
+
+
+
\ No newline at end of file
diff --git a/src/main/res/values/strings.xml b/src/main/res/values/strings.xml
index 7cc869a..4a02bd1 100644
--- a/src/main/res/values/strings.xml
+++ b/src/main/res/values/strings.xml
@@ -52,11 +52,17 @@
Unpin
Lock
Unlock
+ Change Password
Enter Password
+ Enter Old Password
+ Enter New Password
Please enter password
+ Please enter password hint
+ Hint:
Incorrect password
Note locked successfully
Note unlocked successfully
+ Password changed successfully
%d selected
Nothing selected, the operation is invalid
Select all
@@ -141,9 +147,22 @@
set
cancel
- - %1$s result for \"%2$s\"
+ - %1$s result for "%2$s"
- - %1$s results for \"%2$s\"
+ - %1$s results for "%2$s"
+
+ System
+ Custom
+ Templates
+ Save as Template
+ Enter template name
+ Note is empty
+ Template name cannot be empty
+ Template saved successfully
+ Template deleted successfully
+ Are you sure you want to delete this template?
+ Note Template
+