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.
miNote2/java/net/micode/notes/ui/NoteEditActivity.java

820 lines
42 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.

/*
* 版权声明: MiCode 开源社区 版权所有
* 本文件遵循 Apache License, Version 2.0 许可协议
*/
package net.micode.notes.ui;
import android.app.Activity;
import android.app.AlarmManager;
import android.app.AlertDialog;
import android.app.PendingIntent;
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.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.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;
// 定义 NoteEditActivity 类,继承自 Activity用于编辑笔记
public class NoteEditActivity extends Activity implements OnClickListener,
NoteSettingChangedListener, OnTextViewChangeListener {
// 内部类,用于保存笔记头部视图的引用
private 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<Integer, Integer>();
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值为对应的选中状态布局文件 ID
private static final Map<Integer, Integer> sBgSelectorSelectionMap = new HashMap<Integer, Integer>();
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<Integer, Integer>();
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值为对应的选中状态布局文件 ID
private static final Map<Integer, Integer> sFontSelectorSelectionMap = new HashMap<Integer, Integer>();
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; // 字体大小 ID
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 = String.valueOf('\u221A'); // 表示已选中的标签
public static final String TAG_UNCHECKED = String.valueOf('\u25A1'); // 表示未选中的标签
private LinearLayout mEditTextList; // 编辑文本列表的线性布局
private String mUserQuery; // 用户查询字符串
private Pattern mPattern; // 正则表达式模式
// 在创建活动时调用,初始化界面和数据
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
this.setContentView(R.layout.note_edit); // 设置布局文件
// 如果没有保存实例状态且未能初始化活动状态,则销毁活动
if (savedInstanceState == null && !initActivityState(getIntent())) {
finish();
return;
}
initResources(); // 初始化资源
}
// 当活动被恢复时调用,用于恢复之前保存的状态
@Override
protected void onRestoreInstanceState(Bundle savedInstanceState) {
super.onRestoreInstanceState(savedInstanceState);
// 如果保存了实例状态且包含额外的 UID则恢复活动状态
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, "从已杀死的活动中恢复");
}
}
// 初始化活动状态,解析意图并加载笔记数据
private boolean initActivityState(Intent intent) {
mWorkingNote = null; // 初始化当前正在编辑的笔记为空
// 如果意图操作是查看且未提供 ID则跳转到笔记列表活动
if (TextUtils.equals(Intent.ACTION_VIEW, intent.getAction())) {
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);
}
// 检查笔记是否存在于数据库中
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, "加载笔记失败,笔记 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, intent.getAction())) {
// 创建新笔记
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, "通话记录号码为空");
}
long noteId = 0;
// 如果已存在通话记录笔记,则加载该笔记
if ((noteId = DataUtils.getNoteIdByPhoneNumberAndCallDate(getContentResolver(), phoneNumber, callDate)) > 0) {
mWorkingNote = WorkingNote.load(this, noteId);
if (mWorkingNote == null) {
Log.e(TAG, "加载通话笔记失败,笔记 ID" + noteId);
finish(); // 销毁活动
return false;
}
} else {
mWorkingNote = WorkingNote.createEmptyNote(this, folderId, widgetId, widgetType, bgResId);
mWorkingNote.convertToCallNote(phoneNumber, callDate); // 转换为通话笔记
}
} else {
mWorkingNote = WorkingNote.createEmptyNote(this, folderId, widgetId, widgetType, bgResId); // 创建空笔记
}
getWindow().setSoftInputMode(
WindowManager.LayoutParams.SOFT_INPUT_ADJUST_RESIZE | // 调整窗口大小以适应软键盘
WindowManager.LayoutParams.SOFT_INPUT_STATE_VISIBLE); // 软键盘初始状态为可见
} else {
Log.e(TAG, "未指定意图操作,不支持");
finish(); // 销毁活动
return false;
}
mWorkingNote.setOnSettingStatusChangedListener(this); // 设置笔记设置状态改变监听器
return true;
}
// 在活动恢复时调用,初始化笔记界面
@Override
protected void onResume() {
super.onResume();
initNoteScreen(); // 初始化笔记屏幕
}
// 初始化笔记屏幕,设置编辑框文本、背景颜色等
private void initNoteScreen() {
mNoteEditor.setTextAppearance(this, TextAppearanceResources.getTexAppearanceResource(mFontSizeId)); // 设置文本外观
if (mWorkingNote.getCheckListMode() == TextNote.MODE_CHECK_LIST) {
switchToListMode(mWorkingNote.getContent()); // 切换到列表模式
} else {
mNoteEditor.setText(getHighlightQueryResult(mWorkingNote.getContent(), mUserQuery)); // 设置文本并高亮查询结果
mNoteEditor.setSelection(mNoteEditor.getText().length()); // 设置编辑框光标位置
}
// 隐藏所有背景选择选中状态的视图
for (Integer id : sBgSelectorSelectionMap.keySet()) {
findViewById(sBgSelectorSelectionMap.get(id)).setVisibility(View.GONE);
}
mHeadViewPanel.setBackgroundResource(mWorkingNote.getTitleBgResId()); // 设置头部背景资源
mNoteEditorPanel.setBackgroundResource(mWorkingNote.getBgColorResId()); // 设置编辑面板背景资源
// 设置修改日期文本
mNoteHeaderHolder.tvModified.setText(DateUtils.formatDateTime(this, mWorkingNote.getModifiedDate(),
DateUtils.FORMAT_SHOW_DATE | DateUtils.FORMAT_NUMERIC_DATE | DateUtils.FORMAT_SHOW_TIME | DateUtils.FORMAT_SHOW_YEAR));
// 显示或隐藏提醒信息
showAlertHeader();
}
// 显示或隐藏提醒信息
private void showAlertHeader() {
if (mWorkingNote.hasClockAlert()) { // 如果有提醒
long time = System.currentTimeMillis();
if (time > mWorkingNote.getAlertDate()) { // 如果提醒时间已过期
mNoteHeaderHolder.tvAlertDate.setText(R.string.note_alert_expired); // 设置过期提示文本
} else {
mNoteHeaderHolder.tvAlertDate.setText(DateUtils.getRelativeTimeSpanString(
mWorkingNote.getAlertDate(), time, DateUtils.MINUTE_IN_MILLIS)); // 设置相对时间文本
}
mNoteHeaderHolder.tvAlertDate.setVisibility(View.VISIBLE); // 显示提醒日期文本视图
mNoteHeaderHolder.ivAlertIcon.setVisibility(View.VISIBLE); // 显示提醒图标图像视图
} else {
mNoteHeaderHolder.tvAlertDate.setVisibility(View.GONE); // 隐藏提醒日期文本视图
mNoteHeaderHolder.ivAlertIcon.setVisibility(View.GONE); // 隐藏提醒图标图像视图
}
}
// 当活动收到新的意图时调用
@Override
protected void onNewIntent(Intent intent) {
super.onNewIntent(intent);
initActivityState(intent); // 初始化活动状态
}
// 在保存实例状态时调用,保存笔记 ID
@Override
protected void onSaveInstanceState(Bundle outState) {
super.onSaveInstanceState(outState);
// 如果笔记不存在于数据库中,则先保存笔记
if (!mWorkingNote.existInDatabase()) {
saveNote();
}
outState.putLong(Intent.EXTRA_UID, mWorkingNote.getNoteId()); // 保存笔记 ID
Log.d(TAG, "保存工作笔记 ID" + mWorkingNote.getNoteId() + " onSaveInstanceState");
}
// 处理触摸事件,用于关闭背景颜色选择器和字体大小选择器
@Override
public boolean dispatchTouchEvent(MotionEvent ev) {
if (mNoteBgColorSelector.getVisibility() == View.VISIBLE && !inRangeOfView(mNoteBgColorSelector, ev)) {
mNoteBgColorSelector.setVisibility(View.GONE); // 隐藏背景颜色选择器
return true;
}
if (mFontSizeSelector.getVisibility() == View.VISIBLE && !inRangeOfView(mFontSizeSelector, ev)) {
mFontSizeSelector.setVisibility(View.GONE); // 隐藏字体大小选择器
return true;
}
return super.dispatchTouchEvent(ev); // 传递触摸事件给父类处理
}
// 检查触摸事件是否在指定视图范围内
private boolean inRangeOfView(View view, MotionEvent ev) {
int[] location = new int[2];
view.getLocationOnScreen(location); // 获取视图在屏幕上的位置
int x = location[0];
int y = location[1];
if (ev.getX() < x || ev.getX() > (x + view.getWidth()) || ev.getY() < y || ev.getY() > (y + view.getHeight())) {
return false; // 如果触摸位置不在视图范围内,返回 false
}
return true; // 否则返回 true
}
// 初始化界面资源,包括视图引用和偏好设置
private void initResources() {
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); // 获取笔记编辑面板
mNoteBgColorSelector = findViewById(R.id.note_bg_color_selector); // 获取背景颜色选择器视图
// 设置背景颜色选择按钮的点击监听器
for (int id : sBgSelectorBtnsMap.keySet()) {
ImageView iv = (ImageView) findViewById(id);
iv.setOnClickListener(this);
}
mFontSizeSelector = findViewById(R.id.font_size_selector); // 获取字体大小选择器视图
// 设置字体大小选择按钮的点击监听器
for (int id : sFontSizeBtnsMap.keySet()) {
View view = findViewById(id);
view.setOnClickListener(this);
}
mSharedPrefs = PreferenceManager.getDefaultSharedPreferences(this); // 获取默认共享偏好设置
mFontSizeId = mSharedPrefs.getInt(PREFERENCE_FONT_SIZE, ResourceParser.BG_DEFAULT_FONT_SIZE); // 获取字体大小 ID
// 如果字体大小 ID 超出资源范围,则使用默认字体大小 ID
if (mFontSizeId >= TextAppearanceResources.getResourcesSize()) {
mFontSizeId = ResourceParser.BG_DEFAULT_FONT_SIZE;
}
mEditTextList = (LinearLayout) findViewById(R.id.note_edit_list); // 获取编辑文本列表
}
// 在活动暂停时调用,保存笔记数据并更新小部件
@Override
protected void onPause() {
super.onPause();
if (saveNote()) { // 保存笔记
Log.d(TAG, "笔记数据已保存,长度:" + mWorkingNote.getContent().length());
}
clearSettingState(); // 清除设置状态
updateWidget(); // 更新小部件
}
// 更新小部件
private void updateWidget() {
Intent intent = new Intent(AppWidgetManager.ACTION_APPWIDGET_UPDATE); // 创建更新小部件的意图
if (mWorkingNote.getWidgetType() == Notes.TYPE_WIDGET_2X) {
intent.setClass(this, NoteWidgetProvider_2x.class); // 设置小部件提供者为 2x 类
} else if (mWorkingNote.getWidgetType() == Notes.TYPE_WIDGET_4X) {
intent.setClass(this, NoteWidgetProvider_4x.class); // 设置小部件提供者为 4x 类
} else {
Log.e(TAG, "不支持的小部件类型");
return;
}
intent.putExtra(AppWidgetManager.EXTRA_APPWIDGET_IDS, new int[]{mWorkingNote.getWidgetId()}); // 添加小部件 ID
sendBroadcast(intent); // 发送广播更新小部件
setResult(RESULT_OK, intent); // 设置结果为成功
}
// 处理视图点击事件
public void onClick(View v) {
int id = v.getId();
if (id == R.id.btn_set_bg_color) {
mNoteBgColorSelector.setVisibility(View.VISIBLE); // 显示背景颜色选择器
findViewById(sBgSelectorSelectionMap.get(mWorkingNote.getBgColorId())).setVisibility(View.VISIBLE); // 显示选中状态的背景颜色视图
} else if (sBgSelectorBtnsMap.containsKey(id)) { // 如果是背景颜色选择按钮
findViewById(sBgSelectorSelectionMap.get(mWorkingNote.getBgColorId())).setVisibility(View.GONE); // 隐藏之前选中状态的背景颜色视图
mWorkingNote.setBgColorId(sBgSelectorBtnsMap.get(id)); // 设置新的背景颜色 ID
mNoteBgColorSelector.setVisibility(View.GONE); // 隐藏背景颜色选择器
} else if (sFontSizeBtnsMap.containsKey(id)) { // 如果是字体大小选择按钮
findViewById(sFontSelectorSelectionMap.get(mFontSizeId)).setVisibility(View.GONE); // 隐藏之前选中状态的字体大小视图
mFontSizeId = sFontSizeBtnsMap.get(id); // 设置新的字体大小 ID
mSharedPrefs.edit().putInt(PREFERENCE_FONT_SIZE, mFontSizeId).commit(); // 保存字体大小 ID 到共享偏好设置
findViewById(sFontSelectorSelectionMap.get(mFontSizeId)).setVisibility(View.VISIBLE); // 显示新的选中状态的字体大小视图
if (mWorkingNote.getCheckListMode() == TextNote.MODE_CHECK_LIST) { // 如果是列表模式
getWorkingText(); // 获取工作文本
switchToListMode(mWorkingNote.getContent()); // 切换到列表模式
} else {
mNoteEditor.setTextAppearance(this, TextAppearanceResources.getTexAppearanceResource(mFontSizeId)); // 设置文本外观
}
mFontSizeSelector.setVisibility(View.GONE); // 隐藏字体大小选择器
}
}
// 处理返回键按下事件
@Override
public void onBackPressed() {
if (clearSettingState()) { // 如果清除设置状态成功,则返回
return;
}
saveNote(); // 保存笔记
super.onBackPressed(); // 调用父类的返回键处理方法
}
// 清除设置状态,包括隐藏背景颜色选择器和字体大小选择器
private boolean clearSettingState() {
if (mNoteBgColorSelector.getVisibility() == View.VISIBLE) {
mNoteBgColorSelector.setVisibility(View.GONE); // 隐藏背景颜色选择器
return true;
} else if (mFontSizeSelector.getVisibility() == View.VISIBLE) {
mFontSizeSelector.setVisibility(View.GONE); // 隐藏字体大小选择器
return true;
}
return false;
}
// 当笔记的背景颜色改变时调用
public void onBackgroundColorChanged() {
findViewById(sBgSelectorSelectionMap.get(mWorkingNote.getBgColorId())).setVisibility(View.VISIBLE); // 显示选中状态的背景颜色视图
mNoteEditorPanel.setBackgroundResource(mWorkingNote.getBgColorResId()); // 设置编辑面板背景资源
mHeadViewPanel.setBackgroundResource(mWorkingNote.getTitleBgResId()); // 设置头部背景资源
}
// 准备选项菜单,根据笔记状态设置菜单项的显示和隐藏
@Override
public boolean onPrepareOptionsMenu(Menu menu) {
if (isFinishing()) {
return true;
}
clearSettingState(); // 清除设置状态
menu.clear(); // 清空菜单
if (mWorkingNote.getFolderId() == Notes.ID_CALL_RECORD_FOLDER) {
getMenuInflater().inflate(R.menu.call_note_edit, menu); // 加载通话笔记编辑菜单
} else {
getMenuInflater().inflate(R.menu.note_edit, menu); // 加载普通笔记编辑菜单
}
if (mWorkingNote.getCheckListMode() == TextNote.MODE_CHECK_LIST) { // 如果是列表模式
menu.findItem(R.id.menu_list_mode).setTitle(R.string.menu_normal_mode); // 设置菜单项标题为普通模式
} else {
menu.findItem(R.id.menu_list_mode).setTitle(R.string.menu_list_mode); // 设置菜单项标题为列表模式
}
if (mWorkingNote.hasClockAlert()) { // 如果有提醒
menu.findItem(R.id.menu_alert).setVisible(false); // 隐藏提醒菜单项
} else {
menu.findItem(R.id.menu_delete_remind).setVisible(false); // 隐藏删除提醒菜单项
}
return true;
}
// 处理选项菜单项点击事件
@Override
public boolean onOptionsItemSelected(MenuItem item) {
switch (item.getItemId()) {
case R.id.menu_new_note:
createNewNote(); // 创建新笔记
break;
case 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();
break;
case R.id.menu_font_size:
mFontSizeSelector.setVisibility(View.VISIBLE); // 显示字体大小选择器
findViewById(sFontSelectorSelectionMap.get(mFontSizeId)).setVisibility(View.VISIBLE); // 显示当前选中状态的字体大小视图
break;
case R.id.menu_list_mode:
mWorkingNote.setCheckListMode(mWorkingNote.getCheckListMode() == 0 ? TextNote.MODE_CHECK_LIST : 0); // 切换列表模式状态
break;
case R.id.menu_share:
getWorkingText(); // 获取工作文本
sendTo(this, mWorkingNote.getContent()); // 分享笔记
break;
case R.id.menu_send_to_desktop:
sendToDesktop(); // 发送到桌面
break;
case R.id.menu_alert:
setReminder(); // 设置提醒
break;
case R.id.menu_delete_remind:
mWorkingNote.setAlertDate(0, false); // 删除提醒
break;
default:
break;
}
return true;
}
// 设置提醒
private void setReminder() {
DateTimePickerDialog d = new DateTimePickerDialog(this, System.currentTimeMillis()); // 创建日期时间选择对话框
d.setOnDateTimeSetListener(new OnDateTimeSetListener() {
public void OnDateTimeSet(AlertDialog dialog, long date) {
mWorkingNote.setAlertDate(date, true); // 设置提醒日期
}
});
d.show(); // 显示对话框
}
// 分享笔记到支持发送文本的应用
private void sendTo(Context context, String info) {
Intent intent = new Intent(Intent.ACTION_SEND);
intent.putExtra(Intent.EXTRA_TEXT, info); // 添加文本内容
intent.setType("text/plain"); // 设置数据类型为文本
context.startActivity(intent); // 启动活动
}
// 创建新笔记
private void createNewNote() {
saveNote(); // 保存当前笔记
finish(); // 销毁当前活动
Intent intent = new Intent(this, NoteEditActivity.class); // 创建新笔记编辑活动的意图
intent.setAction(Intent.ACTION_INSERT_OR_EDIT);
intent.putExtra(Notes.INTENT_EXTRA_FOLDER_ID, mWorkingNote.getFolderId()); // 添加文件夹 ID
startActivity(intent); // 启动活动
}
// 删除当前笔记
private void deleteCurrentNote() {
if (mWorkingNote.existInDatabase()) { // 如果笔记存在于数据库中
HashSet<Long> ids = new HashSet<Long>();
long id = mWorkingNote.getNoteId();
if (id != Notes.ID_ROOT_FOLDER) {
ids.add(id); // 添加笔记 ID 到集合
} else {
Log.d(TAG, "错误的笔记 ID不应该发生");
}
if (!isSyncMode()) { // 如果不是同步模式
if (!DataUtils.batchDeleteNotes(getContentResolver(), ids)) { // 批量删除笔记
Log.e(TAG, "删除笔记错误");
}
} else {
if (!DataUtils.batchMoveToFolder(getContentResolver(), ids, Notes.ID_TRASH_FOLER)) { // 批量移动笔记到回收站文件夹
Log.e(TAG, "移动笔记到回收站文件夹错误,不应该发生");
}
}
}
mWorkingNote.markDeleted(true); // 标记笔记为已删除
}
// 检查是否是同步模式
private boolean isSyncMode() {
return NotesPreferenceActivity.getSyncAccountName(this).trim().length() > 0; // 如果同步账户名不为空,则是同步模式
}
// 当时钟提醒改变时调用
public void onClockAlertChanged(long date, boolean set) {
if (!mWorkingNote.existInDatabase()) { // 如果笔记不存在于数据库中,则先保存笔记
saveNote();
}
if (mWorkingNote.getNoteId() > 0) { // 如果笔记 ID 大于 0
Intent intent = new Intent(this, AlarmReceiver.class); // 创建广播接收者意图
intent.setData(ContentUris.withAppendedId(Notes.CONTENT_NOTE_URI, mWorkingNote.getNoteId())); // 设置数据
PendingIntent pendingIntent = PendingIntent.getBroadcast(this, 0, intent, 0); // 创建挂起意图
AlarmManager alarmManager = ((AlarmManager) getSystemService(ALARM_SERVICE)); // 获取闹钟管理器服务
showAlertHeader(); // 显示提醒信息
if (!set) {
alarmManager.cancel(pendingIntent); // 取消闹钟
} else {
alarmManager.set(AlarmManager.RTC_WAKEUP, date, pendingIntent); // 设置闹钟
}
} else {
Log.e(TAG, "时钟提醒设置错误");
showToast(R.string.error_note_empty_for_clock); // 显示笔记为空不能设置提醒的提示信息
}
}
// 当小部件改变时调用,用于更新小部件
public void onWidgetChanged() {
updateWidget();
}
// 当编辑文本删除时调用
public void onEditTextDelete(int index, String text) {
int childCount = mEditTextList.getChildCount(); // 获取子视图数量
if (childCount == 1) { // 如果只有一个子视图,直接返回
return;
}
// 更新后续编辑文本的索引
for (int i = index + 1; i < childCount; i++) {
((NoteEditText) mEditTextList.getChildAt(i).findViewById(R.id.et_edit_text)).setIndex(i - 1);
}
mEditTextList.removeViewAt(index); // 移除指定索引的子视图
NoteEditText edit = null;
if (index == 0) { // 如果移除的是第一个子视图
edit = (NoteEditText) mEditTextList.getChildAt(0).findViewById(R.id.et_edit_text); // 获取新的第一个编辑文本
} else {
edit = (NoteEditText) mEditTextList.getChildAt(index - 1).findViewById(R.id.et_edit_text); // 获取上一个编辑文本
}
int length = edit.length(); // 获取编辑文本的长度
edit.append(text); // 追加文本
edit.requestFocus(); // 请求焦点
edit.setSelection(length); // 设置光标位置
}
// 当编辑文本按下回车键时调用
public void onEditTextEnter(int index, String text) {
if (index > mEditTextList.getChildCount()) { // 如果索引超出子视图数量,记录错误并返回
Log.e(TAG, "索引超出编辑文本列表边界,不应该发生");
return;
}
View view = getListItem(text, index); // 获取列表项视图
mEditTextList.addView(view, index); // 添加视图到编辑文本列表
NoteEditText edit = (NoteEditText) view.findViewById(R.id.et_edit_text); // 获取编辑文本
edit.requestFocus(); // 请求焦点
edit.setSelection(0); // 设置光标位置
// 更新后续编辑文本的索引
for (int i = index + 1; i < mEditTextList.getChildCount(); i++) {
((NoteEditText) mEditTextList.getChildAt(i).findViewById(R.id.et_edit_text)).setIndex(i);
}
}
// 切换到列表模式
private void switchToListMode(String text) {
mEditTextList.removeAllViews(); // 移除所有子视图
String[] items = text.split("\n"); // 按行分割文本
int index = 0;
for (String item : items) {
if (!TextUtils.isEmpty(item)) { // 如果行不为空
mEditTextList.addView(getListItem(item, index)); // 添加列表项视图到编辑文本列表
index++;
}
}
mEditTextList.addView(getListItem("", index)); // 添加一个空的列表项视图用于输入
mEditTextList.getChildAt(index).findViewById(R.id.et_edit_text).requestFocus(); // 请求焦点
mNoteEditor.setVisibility(View.GONE); // 隐藏编辑框
mEditTextList.setVisibility(View.VISIBLE); // 显示编辑文本列表
}
// 获取高亮查询结果的可编辑文本
private Spannable getHighlightQueryResult(String fullText, String userQuery) {
SpannableString spannable = new SpannableString(fullText == null ? "" : fullText); // 创建可编辑文本
if (!TextUtils.isEmpty(userQuery)) { // 如果用户查询不为空
mPattern = Pattern.compile(userQuery); // 编译正则表达式模式
Matcher m = mPattern.matcher(fullText); // 创建匹配器
int start = 0;
while (m.find(start)) { // 查找匹配项
spannable.setSpan(
new BackgroundColorSpan(this.getResources().getColor(R.color.user_query_highlight)), // 设置背景颜色跨度
m.start(),
m.end(),
Spannable.SPAN_INCLUSIVE_EXCLUSIVE);
start = m.end();
}
}
return spannable; // 返回可编辑文本
}
// 获取列表项视图
private View getListItem(String item, int index) {
View view = LayoutInflater.from(this).inflate(R.layout.note_edit_list_item, null); // 从布局文件创建视图
final NoteEditText edit = (NoteEditText) view.findViewById(R.id.et_edit_text); // 获取编辑文本
edit.setTextAppearance(this, TextAppearanceResources.getTexAppearanceResource(mFontSizeId)); // 设置文本外观
CheckBox cb = ((CheckBox) view.findViewById(R.id.cb_edit_item)); // 获取复选框
cb.setOnCheckedChangeListener(new OnCheckedChangeListener() {
public void onCheckedChanged(CompoundButton buttonView, boolean isChecked) {
if (isChecked) { // 如果复选框被选中
edit.setPaintFlags(edit.getPaintFlags() | Paint.STRIKE_THRU_TEXT_FLAG); // 设置文本为删除线样式
} else { // 如果复选框未被选中
edit.setPaintFlags(Paint.ANTI_ALIAS_FLAG | Paint.DEV_KERN_TEXT_FLAG); // 设置文本为正常样式
}
}
});
// 检查项是否以选中标签或未选中标签开头,并相应地设置复选框状态和文本样式
if (item.startsWith(TAG_CHECKED)) {
cb.setChecked(true);
edit.setPaintFlags(edit.getPaintFlags() | Paint.STRIKE_THRU_TEXT_FLAG);
item = item.substring(TAG_CHECKED.length(), item.length()).trim();
} else if (item.startsWith(TAG_UNCHECKED)) {
cb.setChecked(false);
edit.setPaintFlags(Paint.ANTI_ALIAS_FLAG | Paint.DEV_KERN_TEXT_FLAG);
item = item.substring(TAG_UNCHECKED.length(), item.length()).trim();
}
edit.setOnTextViewChangeListener(this); // 设置编辑文本变化监听器
edit.setIndex(index); // 设置编辑文本索引
edit.setText(getHighlightQueryResult(item, mUserQuery)); // 设置高亮查询结果的文本
return view; // 返回列表项视图
}
// 当文本变化时调用
public void onTextChange(int index, boolean hasText) {
if (index >= mEditTextList.getChildCount()) { // 如果索引超出子视图数量,记录错误并返回
Log.e(TAG, "错误的索引,不应该发生");
return;
}
if (hasText) { // 如果有文本
mEditTextList.getChildAt(index).findViewById(R.id.cb_edit_item).setVisibility(View.VISIBLE); // 显示复选框
} else {
mEditTextList.getChildAt(index).findViewById(R.id.cb_edit_item).setVisibility(View.GONE); // 隐藏复选框
}
}
// 当检查列表模式改变时调用
public void onCheckListModeChanged(int oldMode, int newMode) {
if (newMode == TextNote.MODE_CHECK_LIST) { // 如果切换到列表模式
switchToListMode(mNoteEditor.getText().toString()); // 切换到列表模式
} else { // 如果切换到普通模式
if (!getWorkingText()) { // 如果获取工作文本失败
mWorkingNote.setWorkingText(mWorkingNote.getContent().replace(TAG_UNCHECKED + " ", "")); // 清除未选中标签
}
mNoteEditor.setText(getHighlightQueryResult(mWorkingNote.getContent(), mUserQuery)); // 设置高亮查询结果的文本
mEditTextList.setVisibility(View.GONE); // 隐藏编辑文本列表
mNoteEditor.setVisibility(View.VISIBLE); // 显示编辑框
}
}
// 获取工作文本
private boolean getWorkingText() {
boolean hasChecked = false;
if (mWorkingNote.getCheckListMode() == TextNote.MODE_CHECK_LIST) { // 如果是列表模式
StringBuilder sb = new StringBuilder(); // 创建字符串构建器
for (int i = 0; i < mEditTextList.getChildCount(); i++) { // 遍历编辑文本列表的子视图
View view = mEditTextList.getChildAt(i);
NoteEditText edit = (NoteEditText) view.findViewById(R.id.et_edit_text); // 获取编辑文本
if (!TextUtils.isEmpty(edit.getText())) { // 如果编辑文本不为空
if (((CheckBox) view.findViewById(R.id.cb_edit_item)).isChecked()) { // 如果复选框被选中
sb.append(TAG_CHECKED).append(" ").append(edit.getText()).append("\n"); // 添加选中标签和文本
hasChecked = true; // 标记有选中项
} else { // 如果复选框未被选中
sb.append(TAG_UNCHECKED).append(" ").append(edit.getText()).append("\n"); // 添加未选中标签和文本
}
}
}
mWorkingNote.setWorkingText(sb.toString()); // 设置工作文本
} else { // 如果是普通模式
mWorkingNote.setWorkingText(mNoteEditor.getText().toString()); // 设置工作文本为编辑框的文本
}
return hasChecked; // 返回是否有选中项
}
// 保存笔记
private boolean saveNote() {
getWorkingText(); // 获取工作文本
boolean saved = mWorkingNote.saveNote(); // 保存笔记
if (saved) {
setResult(RESULT_OK); // 设置结果为成功
}
return saved;
}
// 发送到桌面
private void sendToDesktop() {
if (!mWorkingNote.existInDatabase()) { // 如果笔记不存在于数据库中,则先保存笔记
saveNote();
}
if (mWorkingNote.getNoteId() > 0) { // 如果笔记 ID 大于 0
Intent sender = new Intent(); // 创建发送意图
Intent shortcutIntent = new Intent(this, NoteEditActivity.class); // 创建快捷方式意图
shortcutIntent.setAction(Intent.ACTION_VIEW); // 设置操作为查看
shortcutIntent.putExtra(Intent.EXTRA_UID, mWorkingNote.getNoteId()); // 添加笔记 ID
sender.putExtra(Intent.EXTRA_SHORTCUT_INTENT, shortcutIntent); // 添加快捷方式意图
sender.putExtra(Intent.EXTRA_SHORTCUT_NAME, makeShortcutIconTitle(mWorkingNote.getContent())); // 添加快捷方式名称
sender.putExtra(Intent.EXTRA_SHORTCUT_ICON_RESOURCE, Intent.ShortcutIconResource.fromContext(this, R.drawable.icon_app)); // 添加快捷方式图标资源
sender.putExtra("duplicate", true); // 添加是否允许重复的快捷方式
sender.setAction("com.android.launcher.action.INSTALL_SHORTCUT"); // 设置操作为安装快捷方式
showToast(R.string.info_note_enter_desktop); // 显示笔记已发送到桌面的提示信息
sendBroadcast(sender); // 发送广播
} else {
Log.e(TAG, "发送到桌面错误");
showToast(R.string.error_note_empty_for_send_to_desktop); // 显示笔记为空不能发送到桌面的提示信息
}
}
// 创建快捷方式图标标题
private String makeShortcutIconTitle(String content) {
content = content.replace(TAG_CHECKED, ""); // 移除选中标签
content = content.replace(TAG_UNCHECKED, ""); // 移除未选中标签
return content.length() > SHORTCUT_ICON_TITLE_MAX_LEN ? content.substring(0, SHORTCUT_ICON_TITLE_MAX_LEN) : content; // 截取或返回内容作为标题
}
// 显示提示信息
private void showToast(int resId) {
showToast(resId, Toast.LENGTH_SHORT); // 显示短时长的提示信息
}
// 显示提示信息
private void showToast(int resId, int duration) {
Toast.makeText(this, resId, duration).show(); // 创建并显示提示信息
}
}