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

269 lines
18 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.
*/
// 包声明表明该类所在的包名为net.micode.notes.ui
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它是用于编辑笔记的主要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;
// 用于编辑笔记文本内容的EditText控件用户在该控件中输入、修改笔记的具体文字内容同时也会根据其他设置如字体大小等更新显示样式。
private EditText mNoteEditor;
// 包含笔记编辑区域mNoteEditor的整体布局视图用于设置其背景等相关样式使其与笔记的整体风格和设置保持一致。
private View mNoteEditorPanel;
// 代表正在编辑的笔记对象,封装了笔记的各种属性(如文本内容、背景颜色、提醒设置等)以及相关的操作方法(如保存、加载等),是与笔记数据交互的核心对象。
private WorkingNote mWorkingNote;
// 用于获取和操作应用的共享偏好设置SharedPreferences可以保存和读取一些用户的个性化设置信息例如字体大小偏好设置等。
private SharedPreferences mSharedPrefs;
// 用于记录当前选中的字体大小资源ID通过读取共享偏好设置初始化并在字体大小切换时更新以确保文本编辑区域能正确应用对应的字体大小样式。
private int mFontSizeId;
// 用于在共享偏好设置中标识字体大小偏好设置项的键名,通过该键可以准确地存取对应的字体大小设置值。
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;
// Activity创建时调用的方法用于进行一些初始化操作如设置Activity的布局内容根据传入的Intent初始化活动状态等是整个Activity生命周期的起始方法。
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
// 设置该Activity对应的布局文件为R.layout.note_edit该布局文件定义了笔记编辑页面的整体UI结构包含各种视图控件的布局和样式等。
this.setContentView(R.layout.note_edit);
// 如果是首次创建该ActivitysavedInstanceState为null且无法成功初始化活动状态initActivityState方法返回false
// 则直接结束该Activity不再进行后续操作因为可能缺少必要的启动参数等导致无法正常展示编辑页面。
if (savedInstanceState == null &&!initActivityState(getIntent())) {
finish();
return;
}
// 调用初始化资源的方法,用于获取布局中的各个视图控件实例,并进行一些相关的初始设置,例如设置点击监听器等。
initResources();
}
/**
* 当前Activity在内存不足时可能会被系统销毁。当再次被用户启动时需要恢复之前的状态
* 该方法会在这种情况下被调用用于从保存的状态中恢复Activity的相关数据和设置。
*/
@Override
protected void onRestoreInstanceState(Bundle savedInstanceState) {
super.onRestoreInstanceState(savedInstanceState);
// 如果保存的状态不为空且包含特定的额外数据通过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的状态根据Intent的不同动作ACTION_VIEW或ACTION_INSERT_OR_EDIT等以及携带的额外数据
// 进行相应的操作如加载已有笔记、创建新笔记等并返回初始化是否成功的标志是决定Activity能否正常展示编辑功能的关键方法。
private boolean initActivityState(Intent intent) {
// 初始化为空表示尚未确定要编辑的笔记对象后续根据Intent的具体情况进行赋值和加载操作。
mWorkingNote = null;
// 判断Intent的动作是否为查看ACTION_VIEW如果是查看已有笔记的情况则进行以下操作。
if (TextUtils.equals(Intent.ACTION_VIEW, intent.getAction())) {
// 获取Intent中传递的笔记ID如果没有传递则默认为0后续会根据该ID来查找并加载对应的笔记数据。
long noteId = intent.getLongExtra(Intent.EXTRA_UID, 0);
mUserQuery = "";
// 如果Intent中包含搜索相关的额外数据SearchManager.EXTRA_DATA_KEY说明是从搜索结果进入编辑页面
// 则根据传递的搜索数据获取笔记ID并获取用户的搜索查询字符串用于后续在笔记中展示搜索匹配情况等操作。
if (intent.hasExtra(SearchManager.EXTRA_DATA_KEY)) {
noteId = Long.parseLong(intent.getStringExtra(SearchManager.EXTRA_DATA_KEY));
mUserQuery = intent.getStringExtra(SearchManager.USER_QUERY);
}
// 通过DataUtils工具类方法检查该笔记ID对应的笔记是否在笔记数据库中可见是否存在且符合类型要求等
// 如果不存在则跳转到笔记列表页面NotesListActivity并显示提示信息告知用户笔记不存在然后结束当前编辑Activity。
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 {
// 如果笔记存在则通过WorkingNote的加载方法根据笔记ID加载对应的笔记对象到mWorkingNote中
// 如果加载失败则记录错误日志并结束当前编辑Activity若加载成功则继续后续的初始化设置。
mWorkingNote = WorkingNote.load(this, noteId);
if (mWorkingNote == null) {
Log.e(TAG, "load note failed with note id" + noteId);
finish();
return false;
}
}
// 设置软键盘的显示模式使其初始状态为隐藏并且当软键盘弹出时调整Activity的布局以适应软键盘显示避免遮挡内容。
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())) {
// 处理创建新笔记或编辑已有笔记的情况以下是获取创建或编辑笔记所需的各种额外数据如文件夹ID、小部件相关信息、背景资源ID等。
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));
// 尝试解析通话记录相关的笔记内容如果传入的通话日期callDate和电话号码phoneNumber都不为空
// 则根据这两个信息查找是否已存在对应的笔记,如果存在则加载该笔记,若不存在则创建一个空的笔记并转换为通话记录笔记格式,填充相应的通话数据。
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);