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

866 lines
48 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.
*/
package net.micode.notes.ui; // 定义包名
import android.app.Activity; // 导入Activity类
import android.app.AlarmManager; // 导入AlarmManager类
import android.app.AlertDialog; // 导入AlertDialog类
import android.app.PendingIntent; // 导入PendingIntent类
import android.app.SearchManager; // 导入SearchManager类
import android.appwidget.AppWidgetManager; // 导入AppWidgetManager类
import android.content.ContentUris; // 导入ContentUris类
import android.content.Context; // 导入Context类
import android.content.DialogInterface; // 导入DialogInterface类
import android.content.Intent; // 导入Intent类
import android.content.SharedPreferences; // 导入SharedPreferences类
import android.graphics.Paint; // 导入Paint类
import android.os.Bundle; // 导入Bundle类
import android.preference.PreferenceManager; // 导入PreferenceManager类
import android.text.Spannable; // 导入Spannable类
import android.text.SpannableString; // 导入SpannableString类
import android.text.TextUtils; // 导入TextUtils类
import android.text.format.DateUtils; // 导入DateUtils类
import android.text.style.BackgroundColorSpan; // 导入BackgroundColorSpan类
import android.util.Log; // 导入Log类
import android.view.LayoutInflater; // 导入LayoutInflater类
import android.view.Menu; // 导入Menu类
import android.view.MenuItem; // 导入MenuItem类
import android.view.MotionEvent; // 导入MotionEvent类
import android.view.View; // 导入View类
import android.view.ViewGroup; // 导入ViewGroup类
import android.view.WindowManager; // 导入WindowManager类
import android.widget.CheckBox; // 导入CheckBox类
import android.widget.CompoundButton; // 导入CompoundButton类
import android.widget.EditText; // 导入EditText类
import android.widget.ImageView; // 导入ImageView类
import android.widget.LinearLayout; // 导入LinearLayout类
import android.widget.TextView; // 导入TextView类
import android.widget.Toast; // 导入Toast类
import net.micode.notes.R; // 导入资源类
import net.micode.notes.data.Notes; // 导入Notes数据类
import net.micode.notes.data.Notes.TextNote; // 导入笔记文本类
import net.micode.notes.model.WorkingNote; // 导入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; // 导入 2x 笔记小部件提供者
import net.micode.notes.widget.NoteWidgetProvider_4x; // 导入 4x 笔记小部件提供者
import java.util.HashMap; // 导入HashMap类
import java.util.HashSet; // 导入HashSet类
import java.util.Map; // 导入Map接口
import java.util.regex.Matcher; // 导入Matcher类
import java.util.regex.Pattern; // 导入Pattern类
public class NoteEditActivity extends Activity implements OnClickListener, // 定义NoteEditActivity类继承Activity并实现点击监听和其他接口
NoteSettingChangedListener, OnTextViewChangeListener {
private class HeadViewHolder { // 内部类用于存储头部视图的信息
public TextView tvModified; // 修改日期文本视图
public ImageView ivAlertIcon; // 警告图标
public TextView tvAlertDate; // 警告日期文本视图
public ImageView ibSetBgColor; // 设置背景颜色的图标按钮
}
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);
}
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);
}
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);
}
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"; // 定义日志 TAG
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 = String.valueOf('\u221A'); // 完成标记
public static final String TAG_UNCHECKED = String.valueOf('\u25A1'); // 未完成标记
private LinearLayout mEditTextList; // 编辑文本列表
private String mUserQuery; // 用户查询字符串
private Pattern mPattern; // 正则表达式模式
@Override
protected void onCreate(Bundle savedInstanceState) { // Activity 创建时调用
super.onCreate(savedInstanceState);
this.setContentView(R.layout.note_edit); // 设置内容视图为笔记编辑布局
if (savedInstanceState == null && !initActivityState(getIntent())) { // 如果没有保存的状态且初始化失败
finish(); // 结束Activity
return; // 返回
}
initResources(); // 初始化资源
}
/**
* 当前Activity可能在内存不足时被系统杀死。当再次加载此Activity时应该恢复之前的状态
*/
@Override
protected void onRestoreInstanceState(Bundle savedInstanceState) { // 当Activity状态恢复时调用
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)); // 添加ID到意图中
if (!initActivityState(intent)) { // 初始化Activity状态若失败
finish(); // 结束Activity
return;
}
Log.d(TAG, "Restoring from killed activity"); // 日志输出
}
}
private boolean initActivityState(Intent intent) { // 初始化Activity状态的方法
/**
* 如果用户指定了{@link Intent#ACTION_VIEW}但未提供ID
* 则跳转到NotesListActivity
*/
mWorkingNote = null; // 清空笔记对象
if (TextUtils.equals(Intent.ACTION_VIEW, intent.getAction())) { // 如果Intent的操作是查看
long noteId = intent.getLongExtra(Intent.EXTRA_UID, 0); // 获取笔记ID
mUserQuery = ""; // 初始化用户查询字符串
/**
* 从搜索结果开始
*/
if (intent.hasExtra(SearchManager.EXTRA_DATA_KEY)) { // 如果Intent包含搜索数据
noteId = Long.parseLong(intent.getStringExtra(SearchManager.EXTRA_DATA_KEY)); // 获取数据关键字
mUserQuery = intent.getStringExtra(SearchManager.USER_QUERY); // 获取用户查询字符串
}
if (!DataUtils.visibleInNoteDatabase(getContentResolver(), noteId, Notes.TYPE_NOTE)) { // 检查笔记是否可见
Intent jump = new Intent(this, NotesListActivity.class); // 创建跳转Intent
startActivity(jump); // 启动跳转Activity
showToast(R.string.error_note_not_exist); // 显示笔记不存在的提示
finish(); // 结束此Activity
return false; // 返回false
} else {
mWorkingNote = WorkingNote.load(this, noteId); // 加载正在编辑的笔记
if (mWorkingNote == null) { // 如果加载失败
Log.e(TAG, "load note failed with note id" + noteId); // 日志错误输出
finish(); // 结束此Activity
return false; // 返回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())) { // 如果Intent的操作是插入或编辑
// 新建笔记
long folderId = intent.getLongExtra(Notes.INTENT_EXTRA_FOLDER_ID, 0); // 获取文件夹ID
int widgetId = intent.getIntExtra(Notes.INTENT_EXTRA_WIDGET_ID, // 获取小部件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, // 获取背景资源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; // 初始化笔记ID
if ((noteId = DataUtils.getNoteIdByPhoneNumberAndCallDate(getContentResolver(), // 根据电话号码和日期获取笔记ID
phoneNumber, callDate)) > 0) {
mWorkingNote = WorkingNote.load(this, noteId); // 加载已存在通话记录的笔记
if (mWorkingNote == null) { // 如果加载失败
Log.e(TAG, "load call note failed with note id" + noteId); // 日志错误输出
finish(); // 结束此Activity
return false; // 返回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(); // 结束Activity
return false; // 返回false
}
mWorkingNote.setOnSettingStatusChangedListener(this); // 设置修改状态监听器
return true; // 返回true
}
@Override
protected void onResume() { // 当Activity恢复时调用
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()) { // 如果笔记有 clock 警报
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); // 隐藏警报图标
}
}
@Override
protected void onNewIntent(Intent intent) { // 当有新的Intent传入时调用
super.onNewIntent(intent);
initActivityState(intent); // 初始化Activity状态
}
@Override
protected void onSaveInstanceState(Bundle outState) { // 当保存Instance状态时调用
super.onSaveInstanceState(outState);
/**
* 对于没有笔记ID的新增笔记我们需要先保存它以生成ID。
* 如果当前编辑的笔记不值得保存则没有ID等于创建新笔记
*/
if (!mWorkingNote.existInDatabase()) {
saveNote(); // 保存笔记
}
outState.putLong(Intent.EXTRA_UID, mWorkingNote.getNoteId()); // 保存笔记ID
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; // 返回true表示事件已处理
}
if (mFontSizeSelector.getVisibility() == View.VISIBLE // 检查字体大小选择器的可见性
&& !inRangeOfView(mFontSizeSelector, ev)) { // 如果触摸事件不在该视图范围内
mFontSizeSelector.setVisibility(View.GONE); // 隐藏字体选择器
return true; // 返回true表示事件已处理
}
return super.dispatchTouchEvent(ev); // 调用父类方法处理其他触摸事件
}
private boolean inRangeOfView(View view, MotionEvent ev) { // 检查触摸事件是否在视图范围内
int []location = new int[2];
view.getLocationOnScreen(location); // 获取视图在屏幕上的位置
int x = location[0]; // 获取x坐标
int y = location[1]; // 获取y坐标
if (ev.getX() < x // 如果触摸点x坐标小于视图左上角x坐标
|| ev.getX() > (x + view.getWidth()) // 或者触摸点x坐标大于视图右下角x坐标
|| ev.getY() < y // 或者触摸点y坐标小于视图左上角y坐标
|| ev.getY() > (y + view.getHeight())) { // 或者触摸点y坐标大于视图右下角y坐标
return false; // 返回false表示不在范围内
}
return true; // 返回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); // 查找笔记编辑器面板
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 = PreferenceManager.getDefaultSharedPreferences(this); // 获取默认共享偏好设置
mFontSizeId = mSharedPrefs.getInt(PREFERENCE_FONT_SIZE, ResourceParser.BG_DEFAULT_FONT_SIZE); // 获取字体大小
/**
* HACKME: 修复共享偏好中存储资源ID的错误。
* ID可能大于资源长度这种情况下
* 返回{@link ResourceParser#BG_DEFAULT_FONT_SIZE}
*/
if(mFontSizeId >= TextAppearanceResources.getResourcesSize()) { // 如果字体大小超出范围
mFontSizeId = ResourceParser.BG_DEFAULT_FONT_SIZE; // 设置为默认字体大小
}
mEditTextList = (LinearLayout) findViewById(R.id.note_edit_list); // 查找笔记编辑列表
}
@Override
protected void onPause() { // 当Activity暂停时调用
super.onPause();
if(saveNote()) { // 保存笔记
Log.d(TAG, "Note data was saved with length:" + mWorkingNote.getContent().length()); // 日志输出
}
clearSettingState(); // 清空设置状态
}
private void updateWidget() { // 更新小部件的方法
Intent intent = new Intent(AppWidgetManager.ACTION_APPWIDGET_UPDATE); // 创建更新小部件的Intent
if (mWorkingNote.getWidgetType() == Notes.TYPE_WIDGET_2X) { // 如果小部件是2x类型
intent.setClass(this, NoteWidgetProvider_2x.class); // 设置类为2x小部件提供者
} else if (mWorkingNote.getWidgetType() == Notes.TYPE_WIDGET_4X) { // 如果小部件是4x类型
intent.setClass(this, NoteWidgetProvider_4x.class); // 设置类为4x小部件提供者
} else {
Log.e(TAG, "Unspported widget type"); // 日志错误输出,表示不支持的小部件类型
return; // 返回
}
intent.putExtra(AppWidgetManager.EXTRA_APPWIDGET_IDS, new int[] { // 将小部件ID附加到Intent中
mWorkingNote.getWidgetId()
});
sendBroadcast(intent); // 发送广播以更新小部件
setResult(RESULT_OK, intent); // 设置返回结果为确定
}
public void onClick(View v) { // 点击事件处理
int id = v.getId(); // 获取点击视图的ID
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)); // 设置所选的背景颜色ID
mNoteBgColorSelector.setVisibility(View.GONE); // 隐藏背景颜色选择器
} else if (sFontSizeBtnsMap.containsKey(id)) { // 如果点击的是字体大小选择按钮
findViewById(sFontSelectorSelectionMap.get(mFontSizeId)).setVisibility(View.GONE); // 隐藏当前选中的字体大小
mFontSizeId = sFontSizeBtnsMap.get(id); // 设置所选的字体大小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(); // 调用父类返回方法
}
private boolean clearSettingState() { // 清空设置状态
if (mNoteBgColorSelector.getVisibility() == View.VISIBLE) { // 如果背景颜色选择器可见
mNoteBgColorSelector.setVisibility(View.GONE); // 隐藏它
return true; // 返回true表示有状态被处理
} else if (mFontSizeSelector.getVisibility() == View.VISIBLE) { // 如果字体大小选择器可见
mFontSizeSelector.setVisibility(View.GONE); // 隐藏它
return true; // 返回true表示有状态被处理
}
return false; // 返回false表示没有状态被处理
}
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()) { // 如果Activity正在结束
return true; // 返回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; // 返回true表示菜单准备成功
}
@Override
public boolean onOptionsItemSelected(MenuItem item) { // 处理菜单项选择
switch (item.getItemId()) { // 根据菜单项的ID执行相应操作
case R.id.menu_new_note: // 新建笔记菜单项
createNewNote(); // 创建新笔记
break;
case 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(); // 结束Activity
}
});
builder.setNegativeButton(android.R.string.cancel, null); // 设置取消按钮
builder.show(); // 显示对话框
break;
case R.id.menu_font_size: // 字体大小菜单项
mFontSizeSelector.setVisibility(View.VISIBLE); // 显示字体大小选择器
findViewById(sFontSelectorSelectionMap.get(mFontSizeId)).setVisibility(View.VISIBLE); // 显示当前选中的字体大小
break;
case R.id.menu_list_mode: // 切换到列表模式的菜单项
mWorkingNote.setCheckListMode(mWorkingNote.getCheckListMode() == 0 ?
TextNote.MODE_CHECK_LIST : 0); // 切换检查列表模式
break;
case R.id.menu_share: // 分享菜单项
getWorkingText(); // 获取当前工作文本
sendTo(this, mWorkingNote.getContent()); // 发送文本到其他应用
break;
case R.id.menu_send_to_desktop: // 发送到桌面菜单项
sendToDesktop(); // 发送到桌面
break;
case R.id.menu_alert: // 设置提醒菜单项
setReminder(); // 设置提醒
break;
case R.id.menu_delete_remind: // 删除提醒菜单项
mWorkingNote.setAlertDate(0, false); // 清除警报日期
break;
default: // 默认情况
break;
}
return true; // 返回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(); // 显示对话框
}
/**
* 分享笔记到支持{@link Intent#ACTION_SEND}动作和{@text/plain}类型的应用
*/
private void sendTo(Context context, String info) { // 发送文本到其他应用的方法
Intent intent = new Intent(Intent.ACTION_SEND); // 创建发送意图
intent.putExtra(Intent.EXTRA_TEXT, info); // 添加文本内容
intent.setType("text/plain"); // 设置Mime类型为纯文本
context.startActivity(intent); // 启动分享Activity
}
private void createNewNote() { // 创建新笔记的方法
// 首先保存当前正在编辑的笔记
saveNote(); // 保存笔记
// 为安全起见启动新的NoteEditActivity
finish(); // 结束当前Activity
Intent intent = new Intent(this, NoteEditActivity.class); // 创建新的Intent
intent.setAction(Intent.ACTION_INSERT_OR_EDIT); // 设置动作为插入或编辑
intent.putExtra(Notes.INTENT_EXTRA_FOLDER_ID, mWorkingNote.getFolderId()); // 传递当前文件夹ID
startActivity(intent); // 启动新Activity
}
private void deleteCurrentNote() { // 删除当前笔记的方法
if (mWorkingNote.existInDatabase()) { // 如果笔记在数据库中存在
HashSet<Long> ids = new HashSet<Long>(); // 创建ID集合
long id = mWorkingNote.getNoteId(); // 获取当前笔记ID
if (id != Notes.ID_ROOT_FOLDER) { // 如果ID不是根文件夹ID
ids.add(id); // 将ID添加到集合
} else {
Log.d(TAG, "Wrong note id, should not happen"); // 日志输出错误信息
}
if (!isSyncMode()) { // 如果不是同步模式
if (!DataUtils.batchDeleteNotes(getContentResolver(), ids)) { // 批量删除笔记
Log.e(TAG, "Delete Note error"); // 日志输出错误信息
}
} else { // 如果是同步模式
if (!DataUtils.batchMoveToFolder(getContentResolver(), ids, Notes.ID_TRASH_FOLER)) { // 批量移动到回收站
Log.e(TAG, "Move notes to trash folder error, should not happens"); // 日志输出错误信息
}
}
}
mWorkingNote.markDeleted(true); // 标记笔记为已删除
}
private boolean isSyncMode() { // 判断是否是同步模式的方法
return NotesPreferenceActivity.getSyncAccountName(this).trim().length() > 0; // 如果同步账户名称不为空则返回true
}
public void onClockAlertChanged(long date, boolean set) { // 时钟提醒变化时调用的方法
/**
* 用户可能会将时钟设置到未保存的笔记上,因此在设置警报时
* 我们应该首先保存笔记
*/
if (!mWorkingNote.existInDatabase()) { // 如果笔记不存在于数据库中
saveNote(); // 保存笔记
}
if (mWorkingNote.getNoteId() > 0) { // 如果笔记ID有效
Intent intent = new Intent(this, AlarmReceiver.class); // 创建闹钟接收器的Intent
intent.setData(ContentUris.withAppendedId(Notes.CONTENT_NOTE_URI, mWorkingNote.getNoteId())); // 设置数据URI
PendingIntent pendingIntent = PendingIntent.getBroadcast(this, 0, intent, 0); // 创建PendingIntent
AlarmManager alarmManager = ((AlarmManager) getSystemService(ALARM_SERVICE)); // 获取AlarmManager实例
showAlertHeader(); // 显示警报头
if (!set) { // 如果不设置警报
alarmManager.cancel(pendingIntent); // 取消警报
} else {
alarmManager.set(AlarmManager.RTC_WAKEUP, date, pendingIntent); // 设置警报
}
} else { // 如果当前笔记没有ID
/**
* 用户输入为空笔记不值得保存没有笔记ID提示用户输入内容
*/
Log.e(TAG, "Clock alert setting error"); // 日志输出错误信息
showToast(R.string.error_note_empty_for_clock); // 显示错误提示
}
}
public void onWidgetChanged() { // 当小部件变化时调用
updateWidget(); // 更新小部件
}
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)) // 更新List中的索引
.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); // 查找新的NoteEditText
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); // 将新的项目添加到列表
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); // 隐藏复选框
}
}
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); // 显示笔记编辑器
}
}
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 boolean saveNote() { // 保存笔记的方法
getWorkingText(); // 获取当前工作文本
boolean saved = mWorkingNote.saveNote(); // 尝试保存笔记
if (saved) { // 如果保存成功
/**
* 当从列表视图到编辑视图时,有两种模式,一个是打开笔记
* 另一个是创建/编辑节点。打开节点需要在返回编辑视图时
* 记录原位置,而创建新节点则需要在列表顶部。
* 这段代码RESULT_OK用于标识创建/编辑状态
*/
setResult(RESULT_OK); // 设置结果为成功
}
return saved; // 返回保存状态
}
private void sendToDesktop() { // 发送到桌面的方法
/**
* 在发送消息到主屏幕前,我们应确保当前
* 编辑的笔记存在于数据库中。因此,对于新笔记,先保存它
*/
if (!mWorkingNote.existInDatabase()) { // 如果笔记在数据库中不存在
saveNote(); // 保存笔记
}
if (mWorkingNote.getNoteId() > 0) { // 如果笔记ID有效
Intent sender = new Intent(); // 创建发送意图
Intent shortcutIntent = new Intent(this, NoteEditActivity.class); // 创建快捷方式意图
shortcutIntent.setAction(Intent.ACTION_VIEW); // 设置快捷方式的操作为查看
shortcutIntent.putExtra(Intent.EXTRA_UID, mWorkingNote.getNoteId()); // 添加笔记ID到意图
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); // 设置重复属性为true
sender.setAction("com.android.launcher.action.INSTALL_SHORTCUT"); // 设置操作为安装快捷方式
showToast(R.string.info_note_enter_desktop); // 显示信息提示
sendBroadcast(sender); // 发送广播以安装快捷方式
} else { // 如果笔记不存在(例如,新笔记没有内容)
/**
* 如果用户没有输入任何内容此笔记不值得保存则没有笔记ID
* 提醒用户应该输入内容
*/
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;
}
private void showToast(int resId) { // 显示提示消息的方法
showToast(resId, Toast.LENGTH_SHORT); // 默认短时间显示
}
private void showToast(int resId, int duration) { // 自定义显示提示消息的方法
Toast.makeText(this, resId, duration).show(); // 显示Toast消息
}
}