|
|
|
@ -0,0 +1,269 @@
|
|
|
|
|
/*
|
|
|
|
|
* 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);
|
|
|
|
|
|
|
|
|
|
// 如果是首次创建该Activity(savedInstanceState为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);
|