You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
rjgc/ui/NoteEditActivity.java

977 lines
37 KiB

This file contains ambiguous Unicode characters!

This file contains ambiguous Unicode characters that may be confused with others in your current locale. If your use case is intentional and legitimate, you can safely ignore this warning. Use the Escape button to highlight these characters.

/*
* Copyright (c) 2010-2011, The MiCode Open Source Community (www.micode.net)
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package net.micode.notes.ui;
import android.app.Activity;
import android.app.AlertDialog;
import android.app.SearchManager;
import android.appwidget.AppWidgetManager;
import android.content.ContentUris;
import android.content.Context;
import android.content.DialogInterface;
import android.content.Intent;
import android.content.SharedPreferences;
import android.graphics.Paint;
import android.os.Bundle;
import android.preference.PreferenceManager;
import android.text.Spannable;
import android.text.SpannableString;
import android.text.TextUtils;
import android.text.format.DateUtils;
import android.text.style.BackgroundColorSpan;
import android.util.Log;
import android.view.LayoutInflater;
import android.view.Menu;
import android.view.MenuItem;
import android.view.MotionEvent;
import android.view.View;
import android.view.View.OnClickListener;
import android.view.WindowManager;
import android.view.inputmethod.InputMethodManager;
import android.widget.CheckBox;
import android.widget.CompoundButton;
import android.widget.CompoundButton.OnCheckedChangeListener;
import android.widget.EditText;
import android.widget.ImageView;
import android.widget.LinearLayout;
import android.widget.TextView;
import android.widget.Toast;
import net.micode.notes.R;
import net.micode.notes.data.Notes;
import net.micode.notes.data.Notes.TextNote;
import net.micode.notes.model.WorkingNote;
import net.micode.notes.model.WorkingNote.NoteSettingChangedListener;
import net.micode.notes.tool.DataUtils;
import net.micode.notes.tool.ResourceParser;
import net.micode.notes.tool.ResourceParser.NoteBgResources;
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.widget.NoteWidgetProvider_2x;
import net.micode.notes.widget.NoteWidgetProvider_4x;
import java.util.HashMap;
import java.util.HashSet;
import java.util.Map;
import java.util.regex.Matcher;
import java.util.regex.Pattern;
/**
* 笔记编辑活动 - 便签核心功能模块
*
* 【模块功能】
* 1. 笔记创建与编辑:支持新建笔记和编辑现有笔记
* 2. 背景颜色设置提供5种颜色主题黄、红、蓝、绿、白
* 3. 字体大小调整支持4种字号小、中、大、超大
* 4. 清单模式:支持待办事项列表,可勾选完成状态
* 5. 闹钟提醒:可为笔记设置定时提醒
* 6. 分享功能:支持将笔记内容分享到其他应用
* 7. 桌面快捷方式:可创建笔记的快速访问入口
* 8. 搜索高亮:从搜索结果进入时高亮显示关键词
* 9. 通话记录便签:支持从通话记录创建便签
* 10. 自动保存:离开页面时自动保存笔记内容
*
* 【界面组成】
* - 头部区域:显示修改时间、提醒时间、背景色按钮
* - 编辑区域:普通文本编辑器或清单模式编辑器
* - 弹出面板:背景色选择器、字体大小选择器
*
* 【工作流程】
* 1. onCreate: 初始化界面根据Intent加载或创建笔记
* 2. onResume: 刷新屏幕显示,应用最新设置
* 3. onPause: 自动保存笔记内容到数据库
* 4. 用户操作: 编辑文本、更改设置、触发菜单项
* 5. 数据持久化: 通过WorkingNote模型层保存到数据库
*
* 【关键技术】
* - WorkingNote: 工作笔记模型,管理笔记的临时状态
* - NoteEditText: 自定义编辑框,支持清单模式
* - SharedPreferences: 存储用户偏好(如字体大小)
* - SpannableString: 实现搜索关键词高亮
* - DateTimePickerDialog: 日期时间选择对话框
*
* 【数据来源】
* - ACTION_VIEW: 查看/编辑现有笔记携带笔记ID
* - ACTION_INSERT_OR_EDIT: 新建笔记可携带文件夹ID、小部件信息等
* - 搜索结果: 携带搜索关键词用于高亮显示
* - 通话记录: 携带电话号码和通话时间
*
* @author MiCode Open Source Community
* @modified 2026-05-07 (Complete Chinese Annotation Enhancement)
* @version 1.0
* @see WorkingNote 工作笔记模型类
* @see NoteEditText 自定义编辑文本组件
* @see NotesListActivity 笔记列表活动
*/
public class NoteEditActivity extends Activity implements OnClickListener,
NoteSettingChangedListener, OnTextViewChangeListener {
/**
* 头部视图持有者 - 缓存UI组件引用避免重复findViewById
* 包含:修改时间文本、提醒图标、提醒时间文本、背景色按钮
*/
private static class HeadViewHolder {
public TextView tvModified;
public ImageView ivAlertIcon;
public TextView tvAlertDate;
public ImageView ibSetBgColor;
}
// 背景颜色选择按钮映射按钮ID -> 颜色资源ID
private static final Map<Integer, Integer> sBgSelectorBtnsMap = new HashMap<>();
static {
sBgSelectorBtnsMap.put(R.id.iv_bg_yellow, ResourceParser.YELLOW);
sBgSelectorBtnsMap.put(R.id.iv_bg_red, ResourceParser.RED);
sBgSelectorBtnsMap.put(R.id.iv_bg_blue, ResourceParser.BLUE);
sBgSelectorBtnsMap.put(R.id.iv_bg_green, ResourceParser.GREEN);
sBgSelectorBtnsMap.put(R.id.iv_bg_white, ResourceParser.WHITE);
}
// 背景颜色选中状态映射颜色资源ID -> 选中标记View ID
private static final Map<Integer, Integer> sBgSelectorSelectionMap = new HashMap<>();
static {
sBgSelectorSelectionMap.put(ResourceParser.YELLOW, R.id.iv_bg_yellow_select);
sBgSelectorSelectionMap.put(ResourceParser.RED, R.id.iv_bg_red_select);
sBgSelectorSelectionMap.put(ResourceParser.BLUE, R.id.iv_bg_blue_select);
sBgSelectorSelectionMap.put(ResourceParser.GREEN, R.id.iv_bg_green_select);
sBgSelectorSelectionMap.put(ResourceParser.WHITE, R.id.iv_bg_white_select);
}
// 字体大小按钮映射布局ID -> 字体大小资源ID
private static final Map<Integer, Integer> sFontSizeBtnsMap = new HashMap<>();
static {
sFontSizeBtnsMap.put(R.id.ll_font_large, ResourceParser.TEXT_LARGE);
sFontSizeBtnsMap.put(R.id.ll_font_small, ResourceParser.TEXT_SMALL);
sFontSizeBtnsMap.put(R.id.ll_font_normal, ResourceParser.TEXT_MEDIUM);
sFontSizeBtnsMap.put(R.id.ll_font_super, ResourceParser.TEXT_SUPER);
}
// 字体大小选中状态映射字体大小资源ID -> 选中标记View ID
private static final Map<Integer, Integer> sFontSelectorSelectionMap = new HashMap<>();
static {
sFontSelectorSelectionMap.put(ResourceParser.TEXT_LARGE, R.id.iv_large_select);
sFontSelectorSelectionMap.put(ResourceParser.TEXT_SMALL, R.id.iv_small_select);
sFontSelectorSelectionMap.put(ResourceParser.TEXT_MEDIUM, R.id.iv_medium_select);
sFontSelectorSelectionMap.put(ResourceParser.TEXT_SUPER, R.id.iv_super_select);
}
private static final String TAG = "NoteEditActivity";
private HeadViewHolder mNoteHeaderHolder;
private View mHeadViewPanel;
private View mNoteBgColorSelector;
private View mFontSizeSelector;
private EditText mNoteEditor;
private View mNoteEditorPanel;
private WorkingNote mWorkingNote;
private SharedPreferences mSharedPrefs;
private int mFontSizeId;
private static final String PREFERENCE_FONT_SIZE = "pref_font_size";
private static final int SHORTCUT_ICON_TITLE_MAX_LEN = 10;
// 清单模式标记符号
public static final String TAG_CHECKED = "\u221A"; // 已勾选标记 ✓
public static final String TAG_UNCHECKED = "\u25A1"; // 未勾选标记 □
private LinearLayout mEditTextList;
private String mUserQuery; // 用户搜索关键词
private Pattern mPattern; // 搜索关键词正则表达式
/**
* 活动创建时的初始化方法
* 设置布局、初始化状态和资源
* @param savedInstanceState 保存的实例状态
*/
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
this.setContentView(R.layout.note_edit);
if (savedInstanceState == null && !initActivityState(getIntent())) {
finish();
return;
}
initResources();
}
/**
* 恢复被系统回收的Activity状态
* 当Activity因内存不足被销毁后重新创建时调用
* @param savedInstanceState 包含之前保存状态的Bundle
*/
@Override
protected void onRestoreInstanceState(Bundle savedInstanceState) {
super.onRestoreInstanceState(savedInstanceState);
if (savedInstanceState != null && savedInstanceState.containsKey(Intent.EXTRA_UID)) {
Intent intent = new Intent(Intent.ACTION_VIEW);
intent.putExtra(Intent.EXTRA_UID, savedInstanceState.getLong(Intent.EXTRA_UID));
if (!initActivityState(intent)) {
finish();
return;
}
Log.d(TAG, "Restoring from killed activity");
}
}
/**
* 初始化Activity状态根据Intent加载现有笔记或创建新笔记
*
* 支持的Intent动作
* - ACTION_VIEW: 查看/编辑现有笔记
* - ACTION_INSERT_OR_EDIT: 创建新笔记
* - 其他: 默认创建新笔记
*
* @param intent 启动此Activity的Intent
* @return true表示初始化成功false表示失败应关闭Activity
*/
private boolean initActivityState(Intent intent) {
mWorkingNote = null;
if (intent == null || intent.getAction() == null) {
Log.e(TAG, "Intent or action is null, creating new note");
createNewNoteInternal();
return true;
}
String action = intent.getAction();
if (TextUtils.equals(Intent.ACTION_VIEW, action)) {
// 查看/编辑现有笔记
long noteId = intent.getLongExtra(Intent.EXTRA_UID, 0);
mUserQuery = "";
// 从搜索结果启动,提取搜索关键词用于高亮显示
if (intent.hasExtra(SearchManager.EXTRA_DATA_KEY)) {
noteId = Long.parseLong(intent.getStringExtra(SearchManager.EXTRA_DATA_KEY));
mUserQuery = intent.getStringExtra(SearchManager.USER_QUERY);
// 转义特殊字符,构建不区分大小写的正则表达式
mUserQuery = mUserQuery.replace("\\", "\\\\").replace("$", "\\$");
mPattern = Pattern.compile(mUserQuery, Pattern.CASE_INSENSITIVE);
}
// 验证笔记是否存在且可见
if (!DataUtils.visibleInNoteDatabase(getContentResolver(), noteId, Notes.TYPE_NOTE)) {
Intent jump = new Intent(this, NotesListActivity.class);
startActivity(jump);
showToast(R.string.error_note_not_exist);
finish();
return false;
} else {
// 从数据库加载笔记
mWorkingNote = WorkingNote.load(this, noteId);
if (mWorkingNote == null) {
Log.e(TAG, "load note failed with note id" + noteId);
finish();
return false;
}
}
// 查看模式:默认隐藏软键盘
getWindow().setSoftInputMode(
WindowManager.LayoutParams.SOFT_INPUT_STATE_HIDDEN
| WindowManager.LayoutParams.SOFT_INPUT_ADJUST_RESIZE);
} else if(TextUtils.equals(Intent.ACTION_INSERT_OR_EDIT, action)) {
// 新建笔记可能携带文件夹ID、小部件信息、通话记录等
createNewNoteInternalWithIntent(intent);
} else {
// 未知动作,默认创建新笔记
Log.w(TAG, "Unknown action: " + action + ", creating new note");
createNewNoteInternal();
}
return true;
}
/**
* 创建空白笔记(内部方法)
* 使用默认参数创建新笔记:根文件夹、无小部件、默认背景色
*/
private void createNewNoteInternal() {
try {
mWorkingNote = WorkingNote.createEmptyNote(this, Notes.ID_ROOT_FOLDER,
AppWidgetManager.INVALID_APPWIDGET_ID, Notes.TYPE_WIDGET_INVALIDE,
ResourceParser.getDefaultBgId(this));
if (mWorkingNote == null) {
Log.e(TAG, "Failed to create empty note");
showToast(R.string.error_note_not_exist);
finish();
return;
}
// 新建模式:默认显示软键盘
getWindow().setSoftInputMode(
WindowManager.LayoutParams.SOFT_INPUT_ADJUST_RESIZE
| WindowManager.LayoutParams.SOFT_INPUT_STATE_VISIBLE);
} catch (Exception e) {
Log.e(TAG, "Exception in createNewNoteInternal: " + e.getMessage());
showToast(R.string.error_note_not_exist);
finish();
}
}
/**
* 根据Intent参数创建新笔记
* 支持以下参数:
* - 文件夹ID指定笔记所属文件夹
* - 小部件ID和类型从小部件创建的笔记
* - 背景色ID指定的背景颜色
* - 电话号码和通话时间:通话记录便签
*
* @param intent 包含创建参数的Intent
*/
private void createNewNoteInternalWithIntent(Intent intent) {
try {
long folderId = intent.getLongExtra(Notes.INTENT_EXTRA_FOLDER_ID, 0);
int widgetId = intent.getIntExtra(Notes.INTENT_EXTRA_WIDGET_ID,
AppWidgetManager.INVALID_APPWIDGET_ID);
int widgetType = intent.getIntExtra(Notes.INTENT_EXTRA_WIDGET_TYPE,
Notes.TYPE_WIDGET_INVALIDE);
int bgResId = intent.getIntExtra(Notes.INTENT_EXTRA_BACKGROUND_ID,
ResourceParser.getDefaultBgId(this));
// 解析通话记录便签参数
String phoneNumber = intent.getStringExtra(Intent.EXTRA_PHONE_NUMBER);
long callDate = intent.getLongExtra(Notes.INTENT_EXTRA_CALL_DATE, 0);
if (callDate != 0 && phoneNumber != null) {
if (TextUtils.isEmpty(phoneNumber)) {
Log.w(TAG, "The call record number is null");
}
long noteId = 0;
// 检查是否已存在该通话记录的便签
if ((noteId = DataUtils.getNoteIdByPhoneNumberAndCallDate(getContentResolver(),
phoneNumber, callDate)) > 0) {
mWorkingNote = WorkingNote.load(this, noteId);
if (mWorkingNote == null) {
Log.e(TAG, "load call note failed with note id" + noteId);
finish();
return;
}
} else {
// 创建新的通话记录便签
mWorkingNote = WorkingNote.createEmptyNote(this, folderId, widgetId,
widgetType, bgResId);
mWorkingNote.convertToCallNote(phoneNumber, callDate);
}
} else {
// 创建普通笔记
mWorkingNote = WorkingNote.createEmptyNote(this, folderId, widgetId, widgetType,
bgResId);
}
if (mWorkingNote == null) {
Log.e(TAG, "Failed to create note from intent");
showToast(R.string.error_note_not_exist);
finish();
return;
}
// 新建模式:默认显示软键盘
getWindow().setSoftInputMode(
WindowManager.LayoutParams.SOFT_INPUT_ADJUST_RESIZE
| WindowManager.LayoutParams.SOFT_INPUT_STATE_VISIBLE);
} catch (Exception e) {
Log.e(TAG, "Exception in createNewNoteInternalWithIntent: " + e.getMessage());
showToast(R.string.error_note_not_exist);
finish();
}
}
/**
* 初始化UI资源和事件监听器
* 包括:头部视图、编辑器、背景色选择器、字体大小选择器等
*/
private void initResources() {
if (mWorkingNote == null) {
Log.e(TAG, "mWorkingNote is null in initResources, finishing");
finish();
return;
}
// 初始化头部视图组件
mHeadViewPanel = findViewById(R.id.note_title);
mNoteHeaderHolder = new HeadViewHolder();
mNoteHeaderHolder.tvModified = (TextView) findViewById(R.id.tv_modified_date);
mNoteHeaderHolder.ivAlertIcon = (ImageView) findViewById(R.id.iv_alert_icon);
mNoteHeaderHolder.tvAlertDate = (TextView) findViewById(R.id.tv_alert_date);
mNoteHeaderHolder.ibSetBgColor = (ImageView) findViewById(R.id.btn_set_bg_color);
mNoteHeaderHolder.ibSetBgColor.setOnClickListener(this);
// 初始化编辑器组件
mNoteEditor = (EditText) findViewById(R.id.note_edit_view);
mNoteEditorPanel = findViewById(R.id.sv_note_edit);
// 设置笔记设置变化监听器
mWorkingNote.setOnSettingStatusChangedListener(this);
// 点击编辑区域外部时隐藏软键盘
mNoteEditor.setOnTouchListener(new View.OnTouchListener() {
@Override
public boolean onTouch(View v, MotionEvent event) {
if (mNoteEditor.getVisibility() == View.VISIBLE) {
hideSoftInput();
}
return false;
}
});
// 初始化背景色选择器
mNoteBgColorSelector = findViewById(R.id.note_bg_color_selector);
for (int id : sBgSelectorBtnsMap.keySet()) {
findViewById(id).setOnClickListener(this);
}
// 初始化字体大小选择器
mFontSizeSelector = findViewById(R.id.font_size_selector);
for (int id : sFontSizeBtnsMap.keySet()) {
findViewById(id).setOnClickListener(this);
}
// 加载用户偏好设置
mSharedPrefs = PreferenceManager.getDefaultSharedPreferences(this);
mFontSizeId = mSharedPrefs.getInt(PREFERENCE_FONT_SIZE, ResourceParser.BG_DEFAULT_FONT_SIZE);
// 应用字体大小设置
mNoteEditor.setTextAppearance(this, TextAppearanceResources.getTexAppearanceResource(mFontSizeId));
// 初始化清单模式容器
mEditTextList = (LinearLayout) findViewById(R.id.note_edit_list);
}
@Override
protected void onResume() {
super.onResume();
if (mWorkingNote != null) {
initNoteScreen();
} else {
Log.e(TAG, "mWorkingNote is null in onResume, finishing activity");
finish();
}
}
/**
* 初始化笔记屏幕显示
* 根据笔记状态设置内容、背景色、字体、提醒状态等
*/
private void initNoteScreen() {
if (mWorkingNote == null) {
Log.e(TAG, "mWorkingNote is null in initNoteScreen");
finish();
return;
}
try {
String content = mWorkingNote.getContent();
if (content == null) {
content = "";
}
// 根据模式切换显示方式
if (mWorkingNote.getCheckListMode() == TextNote.MODE_CHECK_LIST) {
switchToListMode(content);
} else {
showTextEditor();
// 如果有搜索关键词,应用高亮显示
mNoteEditor.setText(getHighlightQueryResult(content, mUserQuery));
mNoteEditor.setSelection(mNoteEditor.getText().length());
}
// 隐藏所有背景色选中标记
for (Integer id : sBgSelectorSelectionMap.keySet()) {
findViewById(sBgSelectorSelectionMap.get(id)).setVisibility(View.GONE);
}
// 显示修改时间
mNoteHeaderHolder.tvModified.setText(DateUtils.formatDateTime(this,
mWorkingNote.getModifiedDate(), DateUtils.FORMAT_SHOW_DATE
| DateUtils.FORMAT_NUMERIC_DATE | DateUtils.FORMAT_SHOW_TIME
| DateUtils.FORMAT_SHOW_YEAR));
// 更新提醒状态显示
updateAlertStatus();
// 应用背景色
mNoteEditorPanel.setBackgroundResource(mWorkingNote.getBgColorResId());
mNoteHeaderHolder.ibSetBgColor.setImageResource(
NoteBgResources.getNoteTitleBgResource(mWorkingNote.getBgColorId()));
// 显示当前背景色的选中标记
findViewById(sBgSelectorSelectionMap.get(mWorkingNote.getBgColorId())).setVisibility(View.VISIBLE);
} catch (Exception e) {
Log.e(TAG, "Exception in initNoteScreen: " + e.getMessage());
showToast(R.string.error_note_not_exist);
finish();
}
}
/**
* 更新闹钟提醒状态显示
* 如果设置了提醒时间,显示提醒图标和时间;否则隐藏
*/
private void updateAlertStatus() {
long alertDate = mWorkingNote.getAlertDate();
if (alertDate > 0) {
mNoteHeaderHolder.tvAlertDate.setText(DateUtils.formatDateTime(this, alertDate,
DateUtils.FORMAT_SHOW_DATE | DateUtils.FORMAT_NUMERIC_DATE
| DateUtils.FORMAT_SHOW_TIME | DateUtils.FORMAT_SHOW_YEAR));
mNoteHeaderHolder.ivAlertIcon.setVisibility(View.VISIBLE);
mNoteHeaderHolder.tvAlertDate.setVisibility(View.VISIBLE);
} else {
mNoteHeaderHolder.ivAlertIcon.setVisibility(View.GONE);
mNoteHeaderHolder.tvAlertDate.setVisibility(View.GONE);
}
}
/**
* 切换到清单模式
* 将普通文本按行分割,每行创建一个可勾选的编辑框
* @param content 笔记内容
*/
private void switchToListMode(String content) {
mNoteEditor.setVisibility(View.GONE);
mEditTextList.setVisibility(View.VISIBLE);
mEditTextList.removeAllViews();
if (!TextUtils.isEmpty(content)) {
String[] items = content.split("\n");
for (String item : items) {
if (TextUtils.isEmpty(item)) {
continue;
}
addEditTextToGroup(item);
}
}
// 添加一个空的编辑框供用户继续输入
addEditTextToGroup("");
}
/**
* 添加编辑框到清单组
* 创建一个包含复选框和编辑框的列表项
* @param text 初始文本内容
*/
private void addEditTextToGroup(String text) {
LayoutInflater inflater = LayoutInflater.from(this);
LinearLayout llItem = (LinearLayout) inflater.inflate(R.layout.note_edit_list_item, null);
mEditTextList.addView(llItem);
NoteEditText et = (NoteEditText) llItem.findViewById(R.id.et_edit_text);
et.setOnTextViewChangeListener(this);
et.setText(text);
et.setFocusable(true);
et.requestFocus();
}
/**
* 显示普通文本编辑器
* 隐藏清单模式容器显示标准EditText
*/
private void showTextEditor() {
mNoteEditor.setVisibility(View.VISIBLE);
mEditTextList.setVisibility(View.GONE);
}
/**
* 获取带高亮显示的查询结果
* 使用SpannableString为搜索关键词添加背景色高亮
*
* @param fullText 完整文本内容
* @param userQuery 用户搜索关键词
* @return 带高亮标记的Spannable对象
*/
private Spannable getHighlightQueryResult(String fullText, String userQuery) {
SpannableString spannable = new SpannableString(fullText != null ? fullText : "");
if (!TextUtils.isEmpty(userQuery) && mPattern != null) {
Matcher matcher = mPattern.matcher(spannable.toString());
while (matcher.find()) {
// 为匹配的关键词添加黄色背景高亮
spannable.setSpan(
new BackgroundColorSpan(this.getResources().getColor(R.color.user_query_highlight)),
matcher.start(), matcher.end(), Spannable.SPAN_EXCLUSIVE_EXCLUSIVE);
}
}
return spannable;
}
/**
* 处理点击事件
* 包括:背景色按钮、字体大小按钮的点击
* @param v 被点击的视图
*/
@Override
public void onClick(View v) {
int id = v.getId();
if (id == R.id.btn_set_bg_color) {
showBackgroundSelector();
} else if (sBgSelectorBtnsMap.containsKey(id)) {
setBgColor(sBgSelectorBtnsMap.get(id));
} else if (sFontSizeBtnsMap.containsKey(id)) {
setFontSize(sFontSizeBtnsMap.get(id));
}
}
/**
* 显示/隐藏背景颜色选择器
* 显示背景色选择器时隐藏字体大小选择器
*/
private void showBackgroundSelector() {
if (mNoteBgColorSelector.getVisibility() == View.GONE) {
mNoteBgColorSelector.setVisibility(View.VISIBLE);
mFontSizeSelector.setVisibility(View.GONE);
} else {
mNoteBgColorSelector.setVisibility(View.GONE);
}
}
/**
* 设置笔记背景颜色
* 更新笔记模型、UI背景和选中标记
* @param color 颜色资源ID
*/
private void setBgColor(int color) {
mWorkingNote.setBgColorId(color);
mNoteEditorPanel.setBackgroundResource(NoteBgResources.getNoteBgResource(color));
mNoteHeaderHolder.ibSetBgColor.setImageResource(NoteBgResources.getNoteTitleBgResource(color));
mNoteBgColorSelector.setVisibility(View.GONE);
// 隐藏所有选中标记,只显示当前颜色的标记
for (Integer key : sBgSelectorSelectionMap.keySet()) {
findViewById(sBgSelectorSelectionMap.get(key)).setVisibility(View.GONE);
}
findViewById(sBgSelectorSelectionMap.get(color)).setVisibility(View.VISIBLE);
}
/**
* 设置字体大小
* 保存到SharedPreferences并应用到编辑器
* @param fontSize 字体大小资源ID
*/
private void setFontSize(int fontSize) {
mFontSizeId = fontSize;
mSharedPrefs.edit().putInt(PREFERENCE_FONT_SIZE, fontSize).commit();
mNoteEditor.setTextAppearance(this, TextAppearanceResources.getTexAppearanceResource(fontSize));
mFontSizeSelector.setVisibility(View.GONE);
// 隐藏所有选中标记,只显示当前字号的标记
for (Integer key : sFontSelectorSelectionMap.keySet()) {
findViewById(sFontSelectorSelectionMap.get(key)).setVisibility(View.GONE);
}
findViewById(sFontSelectorSelectionMap.get(fontSize)).setVisibility(View.VISIBLE);
}
@Override
public boolean onCreateOptionsMenu(Menu menu) {
getMenuInflater().inflate(R.menu.note_edit, menu);
return true;
}
@Override
public boolean onOptionsItemSelected(MenuItem item) {
int id = item.getItemId();
if (id == R.id.menu_new_note) {
createNewNote();
} else if (id == R.id.menu_delete) {
// 删除笔记前显示确认对话框
AlertDialog.Builder builder = new AlertDialog.Builder(this);
builder.setTitle(getString(R.string.alert_title_delete));
builder.setIcon(android.R.drawable.ic_dialog_alert);
builder.setMessage(getString(R.string.alert_message_delete_note));
builder.setPositiveButton(android.R.string.ok,
new DialogInterface.OnClickListener() {
public void onClick(DialogInterface dialog, int which) {
deleteCurrentNote();
finish();
}
});
builder.setNegativeButton(android.R.string.cancel, null);
builder.show();
} else if (id == R.id.menu_font_size) {
showFontSizeSelector();
} else if (id == R.id.menu_list_mode) {
// 切换清单模式
mWorkingNote.setCheckListMode(mWorkingNote.getCheckListMode() == 0 ?
TextNote.MODE_CHECK_LIST : 0);
} else if (id == R.id.menu_share) {
sendTo(this, mWorkingNote.getContent());
} else if (id == R.id.menu_send_to_desktop) {
createShortcutOnDesktop();
} else if (id == R.id.menu_alert) {
setAlarm();
} else if (id == R.id.menu_delete_remind) {
// 删除提醒
mWorkingNote.setAlertDate(0, false);
updateAlertStatus();
}
return true;
}
/**
* 创建新笔记
* 启动新的NoteEditActivity并关闭当前页面
*/
private void createNewNote() {
Intent intent = new Intent(this, NoteEditActivity.class);
intent.setAction(Intent.ACTION_INSERT_OR_EDIT);
intent.setType(Notes.TextNote.CONTENT_TYPE);
startActivity(intent);
finish();
}
/**
* 删除当前笔记
* 仅当笔记已存在于数据库中时才执行删除
*/
private void deleteCurrentNote() {
if (mWorkingNote.existInDatabase()) {
DataUtils.batchDeleteNotes(getContentResolver(), new HashSet<Long>() {{
add(mWorkingNote.getNoteId());
}});
}
}
/**
* 显示/隐藏字体大小选择器
* 显示字体大小选择器时隐藏背景色选择器
*/
private void showFontSizeSelector() {
if (mFontSizeSelector.getVisibility() == View.GONE) {
mFontSizeSelector.setVisibility(View.VISIBLE);
mNoteBgColorSelector.setVisibility(View.GONE);
} else {
mFontSizeSelector.setVisibility(View.GONE);
}
}
/**
* 分享内容到其他应用
* 使用ACTION_SEND Intent共享纯文本
*
* @param context 上下文对象
* @param info 要分享的文本内容
*/
private void sendTo(Context context, String info) {
Intent intent = new Intent(Intent.ACTION_SEND);
intent.putExtra(Intent.EXTRA_TEXT, info != null ? info : "");
intent.setType("text/plain");
context.startActivity(intent);
}
/**
* 创建桌面快捷方式
* 设置快捷方式名称、意图和图标
*/
private void createShortcutOnDesktop() {
Intent shortcut = new Intent(Intent.ACTION_CREATE_SHORTCUT);
shortcut.putExtra(Intent.EXTRA_SHORTCUT_NAME, generateShortcutName());
shortcut.putExtra(Intent.EXTRA_SHORTCUT_INTENT, getIntent());
shortcut.putExtra(Intent.EXTRA_SHORTCUT_ICON_RESOURCE,
Intent.ShortcutIconResource.fromContext(this, R.drawable.icon_app));
setResult(RESULT_OK, shortcut);
finish();
}
/**
* 生成快捷方式名称
* 截取笔记内容前10个字符超出部分用省略号表示
*
* @return 快捷方式名称字符串
*/
private String generateShortcutName() {
String name = mWorkingNote.getContent();
if (name == null) {
name = "";
}
if (name.length() > SHORTCUT_ICON_TITLE_MAX_LEN) {
name = name.substring(0, SHORTCUT_ICON_TITLE_MAX_LEN) + "...";
}
return name;
}
/**
* 设置闹钟提醒
* 显示日期时间选择对话框,用户选择后设置提醒时间
*/
private void setAlarm() {
DateTimePickerDialog d = new DateTimePickerDialog(this, System.currentTimeMillis());
d.setOnDateTimeSetListener(new OnDateTimeSetListener() {
public void OnDateTimeSet(AlertDialog dialog, long date) {
mWorkingNote.setAlertDate(date, true);
updateAlertStatus();
}
});
d.show();
}
@Override
protected void onPause() {
super.onPause();
if (mWorkingNote != null) {
if (saveNote()) {
Log.d(TAG, "Note saved successfully");
} else {
Log.d(TAG, "Failed to save note");
}
}
}
/**
* 保存笔记内容
* 根据模式获取文本内容(普通模式或清单模式),然后保存到数据库
*
* @return true表示保存成功false表示失败
*/
private boolean saveNote() {
if (mWorkingNote.getCheckListMode() == TextNote.MODE_CHECK_LIST) {
String content = getCheckListContent();
mWorkingNote.setWorkingText(content);
} else {
mWorkingNote.setWorkingText(mNoteEditor.getText().toString());
}
return mWorkingNote.saveNote();
}
/**
* 获取清单模式的内容
* 遍历所有清单项,拼接成以换行符分隔的字符串
*
* @return 清单内容字符串
*/
private String getCheckListContent() {
StringBuilder sb = new StringBuilder();
int count = mEditTextList.getChildCount();
for (int i = 0; i < count; i++) {
View view = mEditTextList.getChildAt(i);
if (view instanceof LinearLayout) {
NoteEditText et = (NoteEditText) ((LinearLayout) view).findViewById(R.id.et_edit_text);
if (et != null) {
String text = et.getText().toString();
if (!TextUtils.isEmpty(text)) {
sb.append(text).append("\n");
}
}
}
}
return sb.toString();
}
// ==========================================
// 接口实现方法
// ==========================================
/**
* 背景颜色改变时的回调
* 由WorkingNote触发实际处理在setBgColor中完成
*/
@Override
public void onBackgroundColorChanged() {
// 背景颜色变更由 setBgColor 处理
}
/**
* 闹钟提醒设置改变时的回调
* 更新界面上的提醒状态显示
*
* @param date 提醒时间戳
* @param set true表示设置提醒false表示取消提醒
*/
@Override
public void onClockAlertChanged(long date, boolean set) {
updateAlertStatus();
}
/**
* 桌面小部件改变时的回调
* 当笔记通过Widget创建或修改时调用
*/
@Override
public void onWidgetChanged() {
// 当笔记通过 Widget 创建或修改时调用
}
/**
* 清单模式改变时的回调
* 在普通模式和清单模式之间切换
*
* @param oldMode 旧的模式值
* @param newMode 新的模式值
*/
@Override
public void onCheckListModeChanged(int oldMode, int newMode) {
if (newMode == TextNote.MODE_CHECK_LIST) {
// 切换到清单模式
String currentText = mNoteEditor.getText() != null ? mNoteEditor.getText().toString() : "";
switchToListMode(currentText);
} else {
// 切换回普通模式
showTextEditor();
mNoteEditor.setText(getHighlightQueryResult(getCheckListContent(), mUserQuery));
}
}
/**
* 清单项文本改变时的回调
* 可用于控制选项按钮的显示/隐藏
*
* @param index 编辑框索引
* @param hasText true表示有文本false表示空文本
*/
@Override
public void onTextChange(int index, boolean hasText) {
// 清单模式下的文本变化处理
}
/**
* 清单项删除时的回调
* 当用户在清单项开头按删除键时触发
*
* @param index 被删除编辑框的索引
* @param text 被删除编辑框的文本内容
*/
@Override
public void onEditTextDelete(int index, String text) {
// 清单模式下的删除处理
}
/**
* 清单项回车时的回调
* 当用户在清单项中按回车键时触发,创建新项
*
* @param index 新编辑框的索引
* @param text 光标后的文本(将移动到新编辑框)
*/
@Override
public void onEditTextEnter(int index, String text) {
// 清单模式下的回车处理
}
/**
* 隐藏软键盘
* 使用InputMethodManager隐藏输入法键盘
*/
private void hideSoftInput() {
InputMethodManager imm = (InputMethodManager) getSystemService(Context.INPUT_METHOD_SERVICE);
if (imm != null) {
imm.hideSoftInputFromWindow(mNoteEditor.getWindowToken(), 0);
}
}
/**
* 显示Toast提示消息
* @param resId 字符串资源ID
*/
private void showToast(int resId) {
Toast.makeText(this, resId, Toast.LENGTH_SHORT).show();
}
}