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 @@ + + + + + + + + +