|
|
/*
|
|
|
* 版权所有 (c) 2010-2011,The MiCode Open Source Community (www.micode.net)
|
|
|
*
|
|
|
* 本软件根据 Apache 许可证 2.0 版("许可证")发布;
|
|
|
* 除非符合许可证,否则不得使用此文件。
|
|
|
* 您可以在以下网址获取许可证副本:
|
|
|
*
|
|
|
* http://www.apache.org/licenses/LICENSE-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;
|
|
|
|
|
|
/**
|
|
|
* 笔记编辑活动(核心界面逻辑)
|
|
|
* 功能:
|
|
|
* 1. 支持新建/编辑文本笔记、待办事项、通话记录笔记
|
|
|
* 2. 实现笔记内容编辑、格式设置(背景颜色、字体大小)
|
|
|
* 3. 处理提醒设置、桌面小部件更新、快捷方式创建
|
|
|
* 4. 支持列表模式(待办事项)和普通文本模式切换
|
|
|
* 5. 集成搜索高亮、上下文菜单、多编辑项管理
|
|
|
*/
|
|
|
public class NoteEditActivity extends Activity implements OnClickListener,
|
|
|
NoteSettingChangedListener, OnTextViewChangeListener {
|
|
|
|
|
|
// 头部视图ViewHolder(优化UI组件访问)
|
|
|
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<>();
|
|
|
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<>();
|
|
|
static {
|
|
|
sBgSelectorSelectionMap.put(ResourceParser.YELLOW, R.id.iv_bg_yellow_select);
|
|
|
sBgSelectorSelectionMap.put(ResourceParser.RED, R.id.iv_bg_red_select);
|
|
|
sBgSelectorSelectionMap.put(ResourceParser.BLUE, R.id.iv_bg_blue_select);
|
|
|
sBgSelectorSelectionMap.put(ResourceParser.GREEN, R.id.iv_bg_green_select);
|
|
|
sBgSelectorSelectionMap.put(ResourceParser.WHITE, R.id.iv_bg_white_select);
|
|
|
}
|
|
|
|
|
|
// 字体大小按钮映射(视图ID -> 资源ID)
|
|
|
private static final Map<Integer, Integer> sFontSizeBtnsMap = new HashMap<>();
|
|
|
static {
|
|
|
sFontSizeBtnsMap.put(R.id.ll_font_large, ResourceParser.TEXT_LARGE);
|
|
|
sFontSizeBtnsMap.put(R.id.ll_font_small, ResourceParser.TEXT_SMALL);
|
|
|
sFontSizeBtnsMap.put(R.id.ll_font_normal, ResourceParser.TEXT_MEDIUM);
|
|
|
sFontSizeBtnsMap.put(R.id.ll_font_super, ResourceParser.TEXT_SUPER);
|
|
|
}
|
|
|
|
|
|
// 字体大小选中状态映射(资源ID -> 选中视图ID)
|
|
|
private static final Map<Integer, Integer> sFontSelectorSelectionMap = new HashMap<>();
|
|
|
static {
|
|
|
sFontSelectorSelectionMap.put(ResourceParser.TEXT_LARGE, R.id.iv_large_select);
|
|
|
sFontSelectorSelectionMap.put(ResourceParser.TEXT_SMALL, R.id.iv_small_select);
|
|
|
sFontSelectorSelectionMap.put(ResourceParser.TEXT_MEDIUM, R.id.iv_medium_select);
|
|
|
sFontSelectorSelectionMap.put(ResourceParser.TEXT_SUPER, R.id.iv_super_select);
|
|
|
}
|
|
|
|
|
|
private static final String TAG = "NoteEditActivity";
|
|
|
private HeadViewHolder mNoteHeaderHolder; // 头部视图持有者
|
|
|
private View mHeadViewPanel; // 头部布局
|
|
|
private View mNoteBgColorSelector; // 背景颜色选择面板
|
|
|
private View mFontSizeSelector; // 字体大小选择面板
|
|
|
private EditText mNoteEditor; // 普通文本编辑框
|
|
|
private View mNoteEditorPanel; // 文本编辑背景面板
|
|
|
private WorkingNote mWorkingNote; // 笔记数据模型
|
|
|
private SharedPreferences mSharedPrefs; // 共享偏好(存储字体大小等设置)
|
|
|
private int mFontSizeId; // 当前字体大小资源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 = "\u221A"; // 待办事项选中标记
|
|
|
public static final String TAG_UNCHECKED = "\u25A1"; // 待办事项未选中标记
|
|
|
private LinearLayout mEditTextList; // 列表模式下的编辑项容器
|
|
|
private String mUserQuery; // 搜索查询词(用于高亮显示)
|
|
|
private Pattern mPattern; // 搜索匹配模式
|
|
|
|
|
|
@Override
|
|
|
protected void onCreate(Bundle savedInstanceState) {
|
|
|
super.onCreate(savedInstanceState);
|
|
|
setContentView(R.layout.note_edit); // 加载编辑界面布局
|
|
|
|
|
|
// 恢复因内存不足被销毁的活动状态
|
|
|
if (savedInstanceState == null && !initActivityState(getIntent())) {
|
|
|
finish();
|
|
|
return;
|
|
|
}
|
|
|
initResources(); // 初始化UI组件和资源
|
|
|
}
|
|
|
|
|
|
@Override
|
|
|
protected void onRestoreInstanceState(Bundle savedInstanceState) {
|
|
|
super.onRestoreInstanceState(savedInstanceState);
|
|
|
if (savedInstanceState != null && savedInstanceState.containsKey(Intent.EXTRA_UID)) {
|
|
|
// 从已保存的状态中恢复笔记ID
|
|
|
Intent intent = new Intent(Intent.ACTION_VIEW);
|
|
|
intent.putExtra(Intent.EXTRA_UID, savedInstanceState.getLong(Intent.EXTRA_UID));
|
|
|
if (!initActivityState(intent)) {
|
|
|
finish();
|
|
|
}
|
|
|
}
|
|
|
}
|
|
|
|
|
|
/**
|
|
|
* 初始化活动状态(处理打开/新建笔记逻辑)
|
|
|
* @param intent 启动意图
|
|
|
* @return 是否初始化成功
|
|
|
*/
|
|
|
private boolean initActivityState(Intent intent) {
|
|
|
mWorkingNote = null;
|
|
|
if (TextUtils.equals(intent.getAction(), Intent.ACTION_VIEW)) {
|
|
|
// 打开已有笔记
|
|
|
long noteId = intent.getLongExtra(Intent.EXTRA_UID, 0);
|
|
|
mUserQuery = intent.getStringExtra(SearchManager.USER_QUERY); // 处理搜索结果跳转
|
|
|
|
|
|
// 检查笔记是否存在且可见
|
|
|
if (!DataUtils.visibleInNoteDatabase(getContentResolver(), noteId, Notes.TYPE_NOTE)) {
|
|
|
showToast(R.string.error_note_not_exist);
|
|
|
startActivity(new Intent(this, NotesListActivity.class));
|
|
|
finish();
|
|
|
return false;
|
|
|
}
|
|
|
mWorkingNote = WorkingNote.load(this, noteId); // 加载笔记数据
|
|
|
if (mWorkingNote == null) {
|
|
|
Log.e(TAG, "加载笔记失败:" + noteId);
|
|
|
finish();
|
|
|
return false;
|
|
|
}
|
|
|
// 隐藏软键盘(查看模式)
|
|
|
getWindow().setSoftInputMode(WindowManager.LayoutParams.SOFT_INPUT_STATE_HIDDEN);
|
|
|
} else if (TextUtils.equals(intent.getAction(), Intent.ACTION_INSERT_OR_EDIT)) {
|
|
|
// 新建笔记
|
|
|
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) {
|
|
|
long noteId = DataUtils.getNoteIdByPhoneNumberAndCallDate(getContentResolver(), phoneNumber, callDate);
|
|
|
if (noteId > 0) {
|
|
|
mWorkingNote = WorkingNote.load(this, noteId); // 加载已有通话记录笔记
|
|
|
} 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_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 bgId : sBgSelectorSelectionMap.keySet()) {
|
|
|
findViewById(sBgSelectorSelectionMap.get(bgId)).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 currentTime = System.currentTimeMillis();
|
|
|
if (currentTime > mWorkingNote.getAlertDate()) {
|
|
|
mNoteHeaderHolder.tvAlertDate.setText(R.string.note_alert_expired); // 提醒已过期
|
|
|
} else {
|
|
|
// 显示相对时间(如“10分钟后”)
|
|
|
mNoteHeaderHolder.tvAlertDate.setText(DateUtils.getRelativeTimeSpanString(
|
|
|
mWorkingNote.getAlertDate(), currentTime, 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); // 处理新的启动意图(如从搜索结果重新打开)
|
|
|
}
|
|
|
|
|
|
@Override
|
|
|
protected void onSaveInstanceState(Bundle outState) {
|
|
|
super.onSaveInstanceState(outState);
|
|
|
// 保存未存入数据库的新笔记(生成ID)
|
|
|
if (!mWorkingNote.existInDatabase()) {
|
|
|
saveNote();
|
|
|
}
|
|
|
outState.putLong(Intent.EXTRA_UID, mWorkingNote.getNoteId()); // 保存笔记ID
|
|
|
}
|
|
|
|
|
|
@Override
|
|
|
public boolean dispatchTouchEvent(MotionEvent ev) {
|
|
|
// 点击外部区域隐藏颜色/字体选择面板
|
|
|
if ((mNoteBgColorSelector.getVisibility() == View.VISIBLE || mFontSizeSelector.getVisibility() == View.VISIBLE) &&
|
|
|
!inRangeOfView(mNoteBgColorSelector, ev) && !inRangeOfView(mFontSizeSelector, ev)) {
|
|
|
mNoteBgColorSelector.setVisibility(View.GONE);
|
|
|
mFontSizeSelector.setVisibility(View.GONE);
|
|
|
return true;
|
|
|
}
|
|
|
return super.dispatchTouchEvent(ev);
|
|
|
}
|
|
|
|
|
|
/**
|
|
|
* 判断触摸点是否在指定视图范围内
|
|
|
* @param view 目标视图
|
|
|
* @param ev 触摸事件
|
|
|
* @return 是否在范围内
|
|
|
*/
|
|
|
private boolean inRangeOfView(View view, MotionEvent ev) {
|
|
|
int[] location = new int[2];
|
|
|
view.getLocationOnScreen(location);
|
|
|
int x = location[0], y = location[1];
|
|
|
return ev.getX() >= x && ev.getX() <= x + view.getWidth() &&
|
|
|
ev.getY() >= y && ev.getY() <= y + view.getHeight();
|
|
|
}
|
|
|
|
|
|
/**
|
|
|
* 初始化UI组件和资源
|
|
|
*/
|
|
|
private void initResources() {
|
|
|
// 头部视图组件初始化
|
|
|
mHeadViewPanel = findViewById(R.id.note_title);
|
|
|
mNoteHeaderHolder = new HeadViewHolder();
|
|
|
mNoteHeaderHolder.tvModified = findViewById(R.id.tv_modified_date);
|
|
|
mNoteHeaderHolder.ivAlertIcon = findViewById(R.id.iv_alert_icon);
|
|
|
mNoteHeaderHolder.tvAlertDate = findViewById(R.id.tv_alert_date);
|
|
|
mNoteHeaderHolder.ibSetBgColor = findViewById(R.id.btn_set_bg_color);
|
|
|
mNoteHeaderHolder.ibSetBgColor.setOnClickListener(this); // 设置背景颜色按钮点击监听
|
|
|
|
|
|
// 文本编辑组件初始化
|
|
|
mNoteEditor = findViewById(R.id.note_edit_view);
|
|
|
mNoteEditorPanel = findViewById(R.id.sv_note_edit);
|
|
|
mNoteBgColorSelector = findViewById(R.id.note_bg_color_selector);
|
|
|
// 注册背景颜色选择按钮点击监听
|
|
|
for (int btnId : sBgSelectorBtnsMap.keySet()) {
|
|
|
findViewById(btnId).setOnClickListener(this);
|
|
|
}
|
|
|
|
|
|
// 字体大小选择面板初始化
|
|
|
mFontSizeSelector = findViewById(R.id.font_size_selector);
|
|
|
for (int sizeId : sFontSizeBtnsMap.keySet()) {
|
|
|
findViewById(sizeId).setOnClickListener(this);
|
|
|
}
|
|
|
// 加载字体大小偏好设置
|
|
|
mSharedPrefs = PreferenceManager.getDefaultSharedPreferences(this);
|
|
|
mFontSizeId = mSharedPrefs.getInt(PREFERENCE_FONT_SIZE, ResourceParser.BG_DEFAULT_FONT_SIZE);
|
|
|
//
|