/* * 版权声明: 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 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,值为对应的选中状态布局文件 ID private static final Map 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 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,值为对应的选中状态布局文件 ID private static final Map 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; // 字体大小 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 ids = new HashSet(); 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(); // 创建并显示提示信息 } }