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

1223 lines
47 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.
*/
// NoteEditActivity.java - 便签编辑Activity
// 主要功能:提供便签的创建、编辑、查看、设置等完整功能
package net.micode.notes.ui;
// ======================= 导入区域 =======================
// Android基础
import android.app.Activity; // 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; // URI工具
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; // 偏好设置管理器
// Android文本处理
import android.text.Editable; // 可编辑文本
import android.text.Spannable; // 可设置样式的文本
import android.text.SpannableString; // 可设置样式的字符串
import android.text.TextUtils; // 文本工具
import android.text.TextWatcher; // 文本变化监听
import android.text.format.DateUtils; // 日期工具
import android.text.style.BackgroundColorSpan; // 背景色样式
// Android网络
import android.net.Uri; // URI工具
import android.util.Log; // 日志工具
// Android视图
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; // 窗口管理器
// Android控件
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; // 资源文件R类
// 应用数据模型
import net.micode.notes.data.Notes; // 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.tool.SearchHistoryManager; // 搜索历史管理器
// 应用对话框
import net.micode.notes.ui.DateTimePickerDialog.OnDateTimeSetListener; // 日期时间设置监听
// 应用自定义控件
import net.micode.notes.ui.NoteEditText.OnTextViewChangeListener; // 文本变化监听
// 应用小部件
import net.micode.notes.widget.NoteWidgetProvider_2x; // 2x小部件
import net.micode.notes.widget.NoteWidgetProvider_4x; // 4x小部件
// Java集合
import java.util.HashMap; // 哈希映射
import java.util.HashSet; // 哈希集合
import java.util.List; // 列表接口
import java.util.Map; // 映射接口
// Java正则表达式
import java.util.regex.Matcher; // 正则匹配器
import java.util.regex.Pattern; // 正则模式
// ======================= 便签编辑Activity =======================
/**
* NoteEditActivity - 便签编辑Activity
* 继承自Activity实现多个监听器接口
* 功能:便签的完整编辑功能,包括文本编辑、样式设置、提醒设置、分享等
* 实现接口:
* OnClickListener: 处理视图点击
* NoteSettingChangedListener: 监听便签设置变化
* OnTextViewChangeListener: 监听文本编辑变化
*/
public class NoteEditActivity extends Activity implements OnClickListener,
NoteSettingChangedListener, OnTextViewChangeListener {
// ======================= 头部视图持有者内部类 =======================
/**
* HeadViewHolder - 头部视图持有者
* 视图持有者模式,缓存头部控件引用
*/
private class HeadViewHolder {
public TextView tvModified; // 修改时间文本
public ImageView ivAlertIcon; // 提醒图标
public TextView tvAlertDate; // 提醒日期文本
public ImageView ibSetBgColor; // 设置背景颜色按钮
}
// ======================= 背景颜色选择器映射 =======================
/**
* 背景颜色选择器按钮映射
* 键按钮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
* 用于显示当前选中的颜色
*/
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字体大小常量索引
*/
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
*/
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 LinearLayout mEditTextList; // 列表模式编辑容器
private TextView mWordCountView; // 字数统计视图
// 业务模型
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 static final int REQUEST_CODE_IMAGE_SELECTION = 1;
// 搜索高亮相关
private String mUserQuery; // 用户搜索关键词
private Pattern mPattern; // 正则模式,用于高亮搜索
// ======================= 生命周期方法 =======================
/**
* onCreate - Activity创建
* 初始化Activity状态和资源
* @param savedInstanceState 保存的状态
*/
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
// 设置布局
this.setContentView(R.layout.note_edit);
// 初始化Activity状态
if (savedInstanceState == null && !initActivityState(getIntent())) {
// 初始化失败结束Activity
finish();
return;
}
// 初始化资源
initResources();
}
/**
* 恢复实例状态
* 当Activity被系统杀死后恢复时调用
* @param savedInstanceState 保存的状态
*/
@Override
protected void onRestoreInstanceState(Bundle savedInstanceState) {
super.onRestoreInstanceState(savedInstanceState);
// 从保存的状态中恢复便签ID
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));
// 重新初始化Activity状态
if (!initActivityState(intent)) {
finish();
return;
}
Log.d(TAG, "Restoring from killed activity");
}
}
/**
* 初始化Activity状态
* 根据Intent判断是查看、编辑还是创建便签
* @param intent 启动Activity的Intent
* @return true: 初始化成功; false: 初始化失败
*/
private boolean initActivityState(Intent intent) {
mWorkingNote = null; // 重置工作便签
// 处理查看便签的ACTION_VIEW
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);
// 保存搜索关键词到历史记录
SearchHistoryManager.getInstance(this).saveSearchKeyword(mUserQuery);
}
// 检查便签是否存在
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);
}
// 处理创建/编辑便签的ACTION_INSERT_OR_EDIT
else if(TextUtils.equals(Intent.ACTION_INSERT_OR_EDIT, intent.getAction())) {
// 从Intent获取参数
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 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 {
// 不支持的Action
Log.e(TAG, "Intent not specified action, should not support");
finish();
return false;
}
// 设置便签变化监听器
mWorkingNote.setOnSettingStatusChangedListener(this);
return true;
}
/**
* onResume - Activity恢复可见
* 初始化便签屏幕显示
*/
@Override
protected void onResume() {
super.onResume();
initNoteScreen();
}
/**
* 初始化便签屏幕
* 更新所有UI控件显示当前便签状态
*/
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();
// 更新字数统计
updateWordCount();
}
/**
* 显示提醒头部
* 根据便签是否有提醒设置显示相应控件
*/
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);
};
}
/**
* onNewIntent - 处理新的Intent
* 当Activity已存在时被再次启动
*/
@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());
Log.d(TAG, "Save working note 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;
}
return 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);
// 字数统计视图
mWordCountView = (TextView) findViewById(R.id.tv_word_count);
// 设置文本变化监听
mNoteEditor.addTextChangedListener(new TextWatcher() {
@Override
public void beforeTextChanged(CharSequence s, int start, int count, int after) {
}
@Override
public void onTextChanged(CharSequence s, int start, int before, int count) {
updateWordCount();
}
@Override
public void afterTextChanged(Editable s) {
}
});
// 背景选择器
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 = getSharedPreferences(NotesPreferenceActivity.PREFERENCE_NAME, Context.MODE_PRIVATE);
mFontSizeId = mSharedPrefs.getInt(PREFERENCE_FONT_SIZE, ResourceParser.BG_DEFAULT_FONT_SIZE);
// 修复字体大小ID可能越界的bug
if(mFontSizeId >= TextAppearanceResources.getResourcesSize()) {
mFontSizeId = ResourceParser.BG_DEFAULT_FONT_SIZE;
}
// 列表模式容器
mEditTextList = (LinearLayout) findViewById(R.id.note_edit_list);
}
// ======================= 生命周期暂停 =======================
/**
* onPause - Activity暂停
* 保存便签数据
*/
@Override
protected void onPause() {
super.onPause();
if(saveNote()) {
Log.d(TAG, "Note data was saved with length:" + mWorkingNote.getContent().length());
}
clearSettingState();
}
@Override
protected void onActivityResult(int requestCode, int resultCode, Intent data) {
if (requestCode == REQUEST_CODE_IMAGE_SELECTION && resultCode == RESULT_OK) {
if (data != null && data.getData() != null) {
// 获取选中图片的Uri
Uri imageUri = data.getData();
// 将图片Uri插入到当前编辑位置
String imageTag = "[IMAGE:" + imageUri.toString() + "]";
int currentPosition = mNoteEditor.getSelectionStart();
Editable text = mNoteEditor.getText();
text.insert(currentPosition, imageTag);
// 保存便签内容
getWorkingText();
}
} else {
super.onActivityResult(requestCode, resultCode, data);
}
}
// ======================= 小部件更新 =======================
/**
* 更新小部件
* 根据便签关联的小部件类型发送更新广播
*/
private void updateWidget() {
Intent intent = new Intent(AppWidgetManager.ACTION_APPWIDGET_UPDATE);
if (mWorkingNote.getWidgetType() == Notes.TYPE_WIDGET_2X) {
intent.setClass(this, NoteWidgetProvider_2x.class);
} else if (mWorkingNote.getWidgetType() == Notes.TYPE_WIDGET_4X) {
intent.setClass(this, NoteWidgetProvider_4x.class);
} else {
Log.e(TAG, "Unspported widget type");
return;
}
intent.putExtra(AppWidgetManager.EXTRA_APPWIDGET_IDS, new int[] {
mWorkingNote.getWidgetId()
});
sendBroadcast(intent);
setResult(RESULT_OK, intent);
}
// ======================= 点击事件处理 =======================
/**
* onClick - 视图点击处理
* 实现OnClickListener接口
*/
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));
mNoteBgColorSelector.setVisibility(View.GONE);
}
// 字体大小选择
else if (sFontSizeBtnsMap.containsKey(id)) {
// 隐藏之前的选中状态
findViewById(sFontSelectorSelectionMap.get(mFontSizeId)).setVisibility(View.GONE);
// 设置新字体大小
mFontSizeId = sFontSizeBtnsMap.get(id);
mSharedPrefs.edit().putInt(PREFERENCE_FONT_SIZE, mFontSizeId).commit();
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();
}
/**
* 清除设置状态
* 关闭打开的选择器
* @return true: 有关闭的选择器; false: 无选择器打开
*/
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;
}
// ======================= 便签设置变化回调 =======================
/**
* 背景颜色变化回调
* 实现NoteSettingChangedListener接口
*/
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) {
int itemId = item.getItemId(); // 获取菜单ID并缓存
if (itemId == R.id.menu_new_note) {
createNewNote();
} else if (itemId == 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 (itemId == R.id.menu_font_size) {
mFontSizeSelector.setVisibility(View.VISIBLE);
findViewById(sFontSelectorSelectionMap.get(mFontSizeId)).setVisibility(View.VISIBLE);
} else if (itemId == R.id.menu_list_mode) {
mWorkingNote.setCheckListMode(mWorkingNote.getCheckListMode() == 0 ?
TextNote.MODE_CHECK_LIST : 0);
} else if (itemId == R.id.menu_share) {
getWorkingText();
sendTo(this, mWorkingNote.getContent());
} else if (itemId == R.id.menu_send_to_desktop) {
sendToDesktop();
} else if (itemId == R.id.menu_alert) {
setReminder();
} else if (itemId == R.id.menu_delete_remind) {
mWorkingNote.setAlertDate(0, false);
} else if (itemId == R.id.menu_insert_image) {
// 启动图片选择器
Intent intent = new Intent(Intent.ACTION_GET_CONTENT);
intent.setType("image/*");
startActivityForResult(intent, REQUEST_CODE_IMAGE_SELECTION);
} else {
// 默认分支原default
}
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();
// 启动新的编辑Activity
finish();
Intent intent = new Intent(this, NoteEditActivity.class);
intent.setAction(Intent.ACTION_INSERT_OR_EDIT);
intent.putExtra(Notes.INTENT_EXTRA_FOLDER_ID, mWorkingNote.getFolderId());
startActivity(intent);
}
/**
* 显示Toast消息
* @param resId 字符串资源ID
*/
// 已删除重复的showToast方法使用第1383行定义的更灵活的方法
/**
* 删除当前便签
*/
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);
} else {
Log.d(TAG, "Wrong note id, should not happen");
}
// 统一使用永久删除方式
if (!DataUtils.batchDeleteNotes(getContentResolver(), ids)) {
Log.e(TAG, "Delete Note error");
}
}
mWorkingNote.markDeleted(true);
}
/**
* 判断是否为同步模式
*/
private boolean isSyncMode() {
return NotesPreferenceActivity.getSyncAccountName(this).trim().length() > 0;
}
// ======================= 提醒变化回调 =======================
/**
* 闹钟提醒变化回调
* 实现NoteSettingChangedListener接口
*/
public void onClockAlertChanged(long date, boolean set) {
// 如果便签未保存,先保存
if (!mWorkingNote.existInDatabase()) {
saveNote();
}
if (mWorkingNote.getNoteId() > 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, "Clock alert setting error");
showToast(R.string.error_note_empty_for_clock);
}
}
/**
* 小部件变化回调
*/
public void onWidgetChanged() {
updateWidget();
}
// ======================= 文本编辑变化回调 =======================
/**
* 文本编辑删除回调
* 实现OnTextViewChangeListener接口
*/
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, "Index out of mEditTextList boundrary, should not happen");
}
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, "Wrong index, should not happen");
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);
}
// 更新字数统计
updateWordCount();
}
// ======================= 清单模式变化回调 =======================
/**
* 清单模式变化回调
*/
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);
}
// 更新字数统计
updateWordCount();
}
// ======================= 获取工作文本 =======================
/**
* 获取工作文本
* 从当前UI获取文本内容保存到模型
* @return 是否包含已勾选项
*/
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 void updateWordCount() {
int wordCount = 0;
if (mWorkingNote.getCheckListMode() == TextNote.MODE_CHECK_LIST) {
// 清单模式:计算所有列表项的字数
StringBuilder content = 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);
content.append(edit.getText().toString());
}
wordCount = content.length();
} else {
// 普通模式:直接计算文本长度
wordCount = mNoteEditor.getText().length();
}
mWordCountView.setText(String.valueOf(wordCount));
}
/**
* 保存便签
* @return true: 保存成功; false: 保存失败
*/
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) {
Intent sender = new Intent();
Intent shortcutIntent = new Intent(this, NoteEditActivity.class);
shortcutIntent.setAction(Intent.ACTION_VIEW);
shortcutIntent.putExtra(Intent.EXTRA_UID, mWorkingNote.getNoteId());
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, "Send to desktop error");
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;
}
// ======================= 工具方法 =======================
/**
* 显示Toast提示
*/
private void showToast(int resId) {
showToast(resId, Toast.LENGTH_SHORT);
}
private void showToast(int resId, int duration) {
Toast.makeText(this, resId, duration).show();
}
}