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.
note/NoteEditActivity.java

335 lines
17 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.

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实现了OnClickListener、NoteSettingChangedListener、OnTextViewChangeListener接口用于编辑笔记相关的操作和界面交互
public class NoteEditActivity extends Activity implements OnClickListener,
NoteSettingChangedListener, OnTextViewChangeListener {
// 内部类,用于方便地持有笔记头部视图相关的控件引用
private class HeadViewHolder {
public TextView tvModified; // 用于显示笔记修改时间的TextView
public ImageView ivAlertIcon; // 用于显示提醒图标比如闹钟图标等的ImageView
public TextView tvAlertDate; // 用于显示提醒时间相关文本的TextView
public ImageView ibSetBgColor; // 用于设置背景颜色的按钮ImageView
}
// 以下是一些静态的映射关系用于将界面上的按钮ID与对应的颜色资源ID等进行关联
// 将背景颜色选择按钮的ID映射到对应的颜色资源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比如选中黄色背景时对应的选中标识视图的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如大字体、小字体等对应的资源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; // 用于编辑笔记内容的EditText控件
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; // 用于显示笔记内容为列表模式时的列表布局LinearLayout
private String mUserQuery; // 用户输入的查询字符串(可能用于搜索笔记内容等场景)
private Pattern mPattern; // 用于对用户查询内容进行正则匹配的模式对象
// Activity创建时调用的方法
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
// 设置该Activity对应的布局文件
this.setContentView(R.layout.note_edit);
// 如果savedInstanceState为null即Activity首次创建不是从内存恢复的情况并且初始化Activity状态失败则直接结束该Activity
if (savedInstanceState == null &&!initActivityState(getIntent())) {
finish();
return;
}
// 初始化相关资源,如查找视图等操作
initResources();
}
/**
* 当Activity因内存不足被系统销毁后再次恢复时调用该方法用于从保存的状态中恢复Activity的相关信息
*/
@Override
protected void onRestoreInstanceState(Bundle savedInstanceState) {
super.onRestoreInstanceState(savedInstanceState);
// 如果保存的状态不为null且包含特定的额外数据通过Intent.EXTRA_UID标识则尝试重新初始化Activity状态
if (savedInstanceState!= null && savedInstanceState.containsKey(Intent.EXTRA_UID)) {
Intent intent = new Intent(Intent.ACTION_VIEW);
intent.putExtra(Intent.EXTRA_UID, savedInstanceState.getLong(Intent.EXTRA_UID));
if (!initActivityState(intent)) {
finish();
return;
}
Log.d(TAG, "Restoring from killed activity");
}
}
// 根据传入的Intent初始化Activity的状态比如确定是查看已有笔记还是新建笔记等情况并加载相应的笔记数据
private boolean initActivityState(Intent intent) {
/**
* 如果用户指定了Intent.ACTION_VIEW动作但没有提供笔记的ID那么跳转到NotesListActivity可能是笔记列表页面
*/
mWorkingNote = null;
if (TextUtils.equals(Intent.ACTION_VIEW, intent.getAction())) {
long noteId = intent.getLongExtra(Intent.EXTRA_UID, 0);
mUserQuery = "";
/**
* 如果Intent中包含搜索相关的额外数据从搜索结果进入该Activity的情况则获取对应的笔记ID和用户查询字符串
*/
if (intent.hasExtra(SearchManager.EXTRA_DATA_KEY)) {
noteId = Long.parseLong(intent.getStringExtra(SearchManager.EXTRA_DATA_KEY));
mUserQuery = intent.getStringExtra(SearchManager.USER_QUERY);
}
// 检查指定的笔记ID对应的笔记在数据库中是否可见是否存在且满足可见条件如果不存在则跳转到笔记列表页面并提示错误信息
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 {
// 从数据库加载指定ID的笔记对象如果加载失败则记录错误日志并结束该Activity
mWorkingNote = WorkingNote.load(this, noteId);
if (mWorkingNote == null) {
Log.e(TAG, "load note failed with note id" + noteId);
finish();
return false;
}
}
// 设置软键盘的显示模式,初始为隐藏且根据布局调整大小
getWindow().setSoftInputMode(
WindowManager.LayoutParams.SOFT_INPUT_STATE_HIDDEN
| WindowManager.LayoutParams.SOFT_INPUT_ADJUST_RESIZE);
} else if (TextUtils.equals(Intent.ACTION_INSERT_OR_EDIT, 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, "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 {
Log.e(TAG, "Intent not specified action, should not support");
finish();
return false;
}
// 设置笔记对象的设置状态改变监听器为当前Activity实现了NoteSettingChangedListener接口
mWorkingNote.setOnSettingStatusChangedListener(this);
return true;
}
// Activity恢复可见时如从暂停状态恢复调用的方法用于初始化笔记编辑界面的相关显示内容
@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));
/**
* TODO: 添加用于设置提醒的菜单选项。目前禁用该功能因为DateTimePicker还未准备好
*/
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);
};
}
// 当Activity接收到新的Intent时调用该方法比如通过启动模式设置可以接收新的Intent情况重新初始化Activity状态
@Override
protected void onNewIntent(Intent intent) {
super.onNewIntent(intent);
initActivityState(intent);
}
// 在Activity可能被销毁前如系统内存不足、用户切换应用等情况保存当前Activity的相关状态信息比如正在编辑的笔记的ID等
@Override
protected void onSaveInstanceState(Bundle outState) {
super.onSaveInstanceState(outState);
/**
* 对于没有笔记ID的新笔记可能还未保存先保存笔记以生成一个ID。如果正在编辑的笔记不值得保存可能内容为空等情况没有ID就相当于新建笔记的情况
*/
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