diff --git a/src/main/AndroidManifest.xml b/src/main/AndroidManifest.xml index cb43220..2350512 100644 --- a/src/main/AndroidManifest.xml +++ b/src/main/AndroidManifest.xml @@ -48,7 +48,7 @@ - > + @@ -67,6 +67,13 @@ android:resource="@xml/searchable" /> + + + 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..2777247 100644 --- a/src/main/java/net/micode/notes/ui/NoteEditActivity.java +++ b/src/main/java/net/micode/notes/ui/NoteEditActivity.java @@ -141,6 +141,8 @@ public class NoteEditActivity extends Activity implements OnClickListener, /** 日志标签 */ private static final String TAG = "NoteEditActivity"; + /** 请求选择模板的请求码 */ + private static final int REQUEST_SELECT_TEMPLATE = 100; /** 头部视图持有者 */ private HeadViewHolder mNoteHeaderHolder; @@ -764,12 +766,43 @@ public class NoteEditActivity extends Activity implements OnClickListener, case R.id.menu_unlock: showPasswordDialogForUnlock(); break; + case R.id.menu_template: + // 启动模板选择Activity + Intent intent = new Intent(this, TemplateSelectActivity.class); + getWorkingText(); + intent.putExtra(TemplateSelectActivity.EXTRA_CURRENT_NOTE_CONTENT, mWorkingNote.getContent()); + startActivityForResult(intent, REQUEST_SELECT_TEMPLATE); + break; default: break; } return true; } + /** + * 处理从其他Activity返回的结果 + */ + @Override + protected void onActivityResult(int requestCode, int resultCode, Intent data) { + super.onActivityResult(requestCode, resultCode, data); + if (requestCode == REQUEST_SELECT_TEMPLATE && resultCode == RESULT_OK && data != null) { + // 获取选择的模板内容 + String templateContent = data.getStringExtra(TemplateSelectActivity.EXTRA_TEMPLATE_CONTENT); + if (templateContent != null) { + // 应用模板内容到当前笔记 + mWorkingNote.setWorkingText(templateContent); + if (mWorkingNote.getCheckListMode() == TextNote.MODE_CHECK_LIST) { + switchToListMode(templateContent); + } else { + // 解析HTML格式的富文本内容,确保粗体和回车能正确显示 + CharSequence htmlContent = Html.fromHtml(templateContent); + mNoteEditor.setText(getHighlightQueryResult(htmlContent, mUserQuery)); + mNoteEditor.setSelection(mNoteEditor.getText().length()); + } + } + } + } + /** * 设置笔记的提醒时间 *

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