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.
gitProject/other/07_210340164温雍敬_代码标注/ui/NoteEditActivity.java

915 lines
45 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;
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;
public class NoteEditActivity extends Activity implements OnClickListener,
NoteSettingChangedListener, OnTextViewChangeListener {
private class HeadViewHolder {//创建一个私有类HeadViewHolder用于对界面进行设置包括文本内容图片内容等
public TextView tvModified;
public ImageView ivAlertIcon;
public TextView tvAlertDate;
public ImageView ibSetBgColor;
}//该类实现了对当前便签界面进行编辑功能继承了父类Activity并实现了接口封装有方法39个
//声明Hashmap类将按钮和对应的资源解析器中的各个属性关联。
private static final Map<Integer, Integer> sBgSelectorBtnsMap = new HashMap<Integer, Integer>();
static {//对HashMap进行初始化主要是颜色的对应
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>();
//代码块运用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);
}
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>();
//常量实现资源解析器中字号ID与字体大小按钮已选择对应
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";//为本模块进行标签设置“NoteEditActivity"
private HeadViewHolder mNoteHeaderHolder;//.头部布局
private View mHeadViewPanel;//操作表头
private View mNoteBgColorSelector;//背景颜色
private View mFontSizeSelector;//私有化一个界面操作mFontSizeSelector对标签字体的操作
private EditText mNoteEditor;//文本操作
private View mNoteEditorPanel;//.文本编辑控制板
private WorkingNote mWorkingNote;//对模板WorkingNote的初始化
private SharedPreferences mSharedPrefs;//私有化SharedPreferences的数据存储方式
private int mFontSizeId;//.操作字体大小
private static final String PREFERENCE_FONT_SIZE = "pref_font_size";//定义常量 字体大小设置
private static final int SHORTCUT_ICON_TITLE_MAX_LEN = 10;//设置标题最大长度为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//重写Activity类的onCreate方法
//@param savedInstanceState 已保存的便签实例状态
protected void onCreate(Bundle savedInstanceState) {//创建该activity然后对content进行设置。通过对State的相关信息判断是否返回
super.onCreate(savedInstanceState);
this.setContentView(R.layout.note_edit);
if (savedInstanceState == null && !initActivityState(getIntent())) {
finish();
return;
}
initResources();
}
/**
* Current activity may be killed when the memory is low. Once it is killed, for another time
* user load this activity, we should restore the former state
*/
@Override //覆盖父类的onRestoreInstanceState函数
protected void onRestoreInstanceState(Bundle savedInstanceState) { //在恢复Activity状态时调用传入上一个活动销毁时保存的Bundle对象
super.onRestoreInstanceState(savedInstanceState); //调用父类的onRestoreInstanceState方法来恢复视图层次结构中每个View的状态
if (savedInstanceState != null && savedInstanceState.containsKey(Intent.EXTRA_UID)) { //如果保存的Bundle对象不为空且包含EXTRA_UID键
Intent intent = new Intent(Intent.ACTION_VIEW); //创建一个Intent对象指定操作为ACTION_VIEW
intent.putExtra(Intent.EXTRA_UID, savedInstanceState.getLong(Intent.EXTRA_UID)); //向Intent中添加EXTRA_UID键和值
if (!initActivityState(intent)) { //调用initActivityState方法初始化Activity状态并判断是否成功
finish(); //调用finish方法销毁当前Activity
return; //结束方法的执行
}
Log.d(TAG, "Restoring from killed activity"); //调用Log.d方法向LogCat输出一条调试信息
}
}
private boolean initActivityState(Intent intent) {//初始化Activity的状态
/**
* If the user specified the {@link Intent#ACTION_VIEW} but not provided with id,
* then jump to the NotesListActivity
*/
mWorkingNote = null;
if (TextUtils.equals(Intent.ACTION_VIEW, intent.getAction())) {//判断初始化该Activity的Intent所包含的action是ACTION_VIEW还是ACTION_INSERT_OR_EDIT然后进行不同的操作
long noteId = intent.getLongExtra(Intent.EXTRA_UID, 0);
mUserQuery = "";
/**
* Starting from the searched result
*/if (intent.hasExtra(SearchManager.EXTRA_DATA_KEY)) { //判断Intent是否包含EXTRA_DATA_KEY键
noteId = Long.parseLong(intent.getStringExtra(SearchManager.EXTRA_DATA_KEY)); //获取EXTRA_DATA_KEY键所对应的Long类型值
mUserQuery = intent.getStringExtra(SearchManager.USER_QUERY); //获取USER_QUERY键所对应的字符串类型值
}
if (!DataUtils.visibleInNoteDatabase(getContentResolver(), noteId, Notes.TYPE_NOTE)) { //判断指定noteId和Notes.TYPE_NOTE类型的笔记是否存在于NoteDatabase中
Intent jump = new Intent(this, NotesListActivity.class); //创建一个Intent对象跳转到NotesListActivity活动
startActivity(jump); //启动Intent指定的活动
showToast(R.string.error_note_not_exist); //显示一个Toast提示“笔记不存在”
finish(); //结束当前Activity
return false; //直接返回false
} else { //如果NoteDatabase中存在指定的笔记则加载该笔记
mWorkingNote = WorkingNote.load(this, noteId); //通过WorkingNote类的load方法加载指定的笔记
if (mWorkingNote == null) { //如果加载失败则记录日志并结束Activity
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())) {//判断Intent的Action是否为ACTION_INSERT_OR_EDIT
// New note
long folderId = intent.getLongExtra(Notes.INTENT_EXTRA_FOLDER_ID, 0); //获取Intent的附加数据中名为INTENT_EXTRA_FOLDER_ID的Long类型值如果不存在则返回默认值0
int widgetId = intent.getIntExtra(Notes.INTENT_EXTRA_WIDGET_ID,
AppWidgetManager.INVALID_APPWIDGET_ID); //获取Intent的附加数据中名为INTENT_EXTRA_WIDGET_ID的整型值如果不存在则返回无效的AppWidgetManager ID
int widgetType = intent.getIntExtra(Notes.INTENT_EXTRA_WIDGET_TYPE,
Notes.TYPE_WIDGET_INVALIDE); //获取Intent的附加数据中名为INTENT_EXTRA_WIDGET_TYPE的整型值如果不存在则返回无效的widget类型
int bgResId = intent.getIntExtra(Notes.INTENT_EXTRA_BACKGROUND_ID,
ResourceParser.getDefaultBgId(this)); //获取Intent的附加数据中名为INTENT_EXTRA_BACKGROUND_ID的整型值如果不存在则返回当前主题的默认背景色ID
// Parse call-record note
String phoneNumber = intent.getStringExtra(Intent.EXTRA_PHONE_NUMBER); //获取Intent的附加数据中名为EXTRA_PHONE_NUMBER的字符串类型值
long callDate = intent.getLongExtra(Notes.INTENT_EXTRA_CALL_DATE, 0); //获取Intent的附加数据中名为INTENT_EXTRA_CALL_DATE的long类型值如果该附加数据不存在则返回默认值0
if (callDate != 0 && phoneNumber != null) { //如果存在通话记录
if (TextUtils.isEmpty(phoneNumber)) { //如果通话记录号码为空
Log.w(TAG, "The call record number is null");
}
long noteId = 0; //初始化noteId为0
if ((noteId = DataUtils.getNoteIdByPhoneNumberAndCallDate(getContentResolver(),
phoneNumber, callDate)) > 0) { //通过电话号码和通话时间获取笔记的noteId
mWorkingNote = WorkingNote.load(this, noteId); //加载对应的笔记
if (mWorkingNote == null) { //如果加载失败则记录日志并结束Activity
Log.e(TAG, "load call note failed with note id" + noteId);
finish();
return false;
}
} else { //如果不存在对应的笔记,则创建新笔记
mWorkingNote = WorkingNote.createEmptyNote(this, folderId, widgetId,
widgetType, bgResId); //调用WorkingNote类的静态createEmptyNote方法创建新的笔记
mWorkingNote.convertToCallNote(phoneNumber, callDate); //将笔记转换为通话记录类型
}
} else { //如果不存在通话记录,则创建新笔记
mWorkingNote = WorkingNote.createEmptyNote(this, folderId, widgetId, widgetType,
bgResId); //调用WorkingNote类的静态createEmptyNote方法创建新的笔记
}
getWindow().setSoftInputMode( //设置软键盘状态
WindowManager.LayoutParams.SOFT_INPUT_ADJUST_RESIZE //窗口大小会自动调整以适应新的软键盘状态
| WindowManager.LayoutParams.SOFT_INPUT_STATE_VISIBLE); //同时显示软键盘
} else { //如果Intent的Action既不是ACTION_SEARCH也不是ACTION_INSERT_OR_EDIT则记录日志并结束Activity
Log.e(TAG, "Intent not specified action, should not support");
finish();
return false;
}
mWorkingNote.setOnSettingStatusChangedListener(this); //为WorkingNote设置状态变化的监听器
return true; //返回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 id : sBgSelectorSelectionMap.keySet()) {//循环遍历所有sBgSelectorSelectionMap中id对应的资源文件使其不可见且不占用空间
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: Add the menu for setting alert. Currently disable it because the DateTimePicker
* is not ready
*/
showAlertHeader();
}
private void showAlertHeader() { //定义一个名为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); //设置图标不可见
};
}
@Override
protected void onNewIntent(Intent intent) {//新建一个传值对象
super.onNewIntent(intent);
initActivityState(intent);
}
@Override
protected void onSaveInstanceState(Bundle outState) {//用于保存数据
super.onSaveInstanceState(outState);
/**
* For new note without note id, we should firstly save it to
* generate a id. If the editing note is not worth saving, there
* is no id which is equivalent to create new note
*/
if (!mWorkingNote.existInDatabase()) {//如果该workingnote实例无法在数据库中找到即为新建的便签则先将其存储
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)) {//且当前触摸事件不在其范围内则隐藏选择器并返回true
mNoteBgColorSelector.setVisibility(View.GONE);
return true;
}
if (mFontSizeSelector.getVisibility() == View.VISIBLE//如果字体大小选择器可见
&& !inRangeOfView(mFontSizeSelector, ev)) {//且当前触摸事件不在其范围内则隐藏选择器并返回true
mFontSizeSelector.setVisibility(View.GONE);
return true;
}
return super.dispatchTouchEvent(ev);//否则调用父类的dispatchTouchEvent方法处理事件
}
private boolean inRangeOfView(View view, MotionEvent ev) {//判断给定的触摸事件是否在给定视图的范围内
int []location = new int[2];//获取视图在屏幕中的坐标
view.getLocationOnScreen(location);
int x = location[0];//获取视图左侧在屏幕中的横向坐标
int y = location[1];//获取视图顶部在屏幕中的纵向坐标
if (ev.getX() < x
|| ev.getX() > (x + view.getWidth())
|| ev.getY() < y
|| ev.getY() > (y + view.getHeight())) {//如果触摸事件坐标不在该视图的范围内则返回false
return 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);//从偏好设置中获取字体大小的ID默认值为1
/**
* HACKME: Fix bug of store the resource id in shared preference.
* The id may larger than the length of resources, in this case,
* return the {@link ResourceParser#BG_DEFAULT_FONT_SIZE}
*/
if(mFontSizeId >= TextAppearanceResources.getResourcesSize()) {//如果获取到的字体大小ID大于资源文件应有长度则将其设置为默认长度缺省值为1.
mFontSizeId = ResourceParser.BG_DEFAULT_FONT_SIZE;
}
mEditTextList = (LinearLayout) findViewById(R.id.note_edit_list);//获取笔记编辑列表对象
}
@Override
protected void onPause() {//onPause()在activity退出前想保存用户重要数据的必须在onPause中处理调用父类的界面暂停操作并输出对应信息。
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);//新建一个包含改变widget动作的intent实例
if (mWorkingNote.getWidgetType() == Notes.TYPE_WIDGET_2X) {//根据workingnote的widget类型大小设置intent中映射的类
intent.setClass(this, NoteWidgetProvider_2x.class);
} else if (mWorkingNote.getWidgetType() == Notes.TYPE_WIDGET_4X) {//如果是4倍大小
intent.setClass(this, NoteWidgetProvider_4x.class);
} else {
Log.e(TAG, "Unspported widget type");//Log输出error信息不支持的widget类型
return;
}
intent.putExtra(AppWidgetManager.EXTRA_APPWIDGET_IDS, new int[] {
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); //显示背景颜色选择器
//根据当前活动笔记的背景颜色ID显示相应的选项
findViewById(sBgSelectorSelectionMap.get(mWorkingNote.getBgColorId())).setVisibility(View.VISIBLE);
}
//如果点击的是背景颜色选择器中的背景颜色选项按钮,则执行下面的函数:
else if (sBgSelectorBtnsMap.containsKey(id)) {
//隐藏之前选中的颜色选项
findViewById(sBgSelectorSelectionMap.get(mWorkingNote.getBgColorId())).setVisibility(View.GONE);
//将活动笔记的背景颜色ID设置为该按钮所代表的ID
mWorkingNote.setBgColorId(sBgSelectorBtnsMap.get(id));
//隐藏背景颜色选择器
mNoteBgColorSelector.setVisibility(View.GONE);
}
//如果点击的是字体大小选择器中的文字大小选项按钮,则执行下面的函数:
else if (sFontSizeBtnsMap.containsKey(id)) {
//隐藏之前选中的大小选项
findViewById(sFontSelectorSelectionMap.get(mFontSizeId)).setVisibility(View.GONE);
//将当前选择的字体大小ID保存到共享偏好设置中
mFontSizeId = sFontSizeBtnsMap.get(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;
} else if (mFontSizeSelector.getVisibility() == View.VISIBLE) {//如果字体选择菜单可见,设置为不可见
mFontSizeSelector.setVisibility(View.GONE);
return true;
}
return 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()) {//如果窗口正在关闭,则不做处理
return 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) {//如果workingNote模式为核对列表模式则执行下面函数
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()) {//如果workingNote对象中有时钟提醒事项则执行下面函数。
menu.findItem(R.id.menu_alert).setVisible(false);
} else {
menu.findItem(R.id.menu_delete_remind).setVisible(false);
}
return true;
}
@Override
public boolean onOptionsItemSelected(MenuItem item) {//动态改变菜单选项内容
switch (item.getItemId()) {//判断选择项
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();
}
});
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;
}
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();
}
/**
* Share note to apps that support {@link Intent#ACTION_SEND} action
* and {@text/plain} type
*/
private void sendTo(Context context, String info) {//共享便签
Intent intent = new Intent(Intent.ACTION_SEND);
intent.putExtra(Intent.EXTRA_TEXT, info);
intent.setType("text/plain");
context.startActivity(intent);
}
private void createNewNote() {//创建新的便签
// Firstly, save current editing notes
saveNote();
// For safety, start a new NoteEditActivity
finish();
Intent intent = new Intent(this, NoteEditActivity.class);
intent.setAction(Intent.ACTION_INSERT_OR_EDIT);
intent.putExtra(Notes.INTENT_EXTRA_FOLDER_ID, mWorkingNote.getFolderId());
startActivity(intent);
}
private void deleteCurrentNote() {//删除当前便签
if (mWorkingNote.existInDatabase()) {//先判断便签是否在数据库中
HashSet<Long> ids = new HashSet<Long>();
long id = mWorkingNote.getNoteId();
if (id != Notes.ID_ROOT_FOLDER) {//如果不是头文件夹建立一个hash表把便签id存起来
ids.add(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;
}
public void onClockAlertChanged(long date, boolean set) {//监听闹钟变化事件函数
/**
* User could set clock to an unsaved note, so before setting the
* alert clock, we should save the note first
*/
if (!mWorkingNote.existInDatabase()) {//如果设置提醒的便签未保存,则先保存
saveNote();
}
if (mWorkingNote.getNoteId() > 0) {//数据库中有该提醒的id,执行下面操作。否则,报错。
Intent intent = new Intent(this, AlarmReceiver.class);
intent.setData(ContentUris.withAppendedId(Notes.CONTENT_NOTE_URI, mWorkingNote.getNoteId()));
PendingIntent pendingIntent = PendingIntent.getBroadcast(this, 0, intent, 0);
AlarmManager alarmManager = ((AlarmManager) getSystemService(ALARM_SERVICE));
showAlertHeader();
if(!set) {
alarmManager.cancel(pendingIntent);
} else {
alarmManager.set(AlarmManager.RTC_WAKEUP, date, pendingIntent);
}
} else {
/**
* There is the condition that user has input nothing (the note is
* not worthy saving), we have no note id, remind the user that he
* should input something
*/
Log.e(TAG, "Clock alert setting error");
showToast(R.string.error_note_empty_for_clock);
}
}
public void onWidgetChanged() {
updateWidget();
}
//当widget变化时执行更新widget这个函数
public void onEditTextDelete(int index, String text) {//当删除编辑文本时,执行这个函数
int childCount = mEditTextList.getChildCount();
if (childCount == 1) {//当删除编辑文本时,执行这个函数
return;
}
for (int i = index + 1; i < childCount; i++) {//将index后面的每一个项覆盖前面的项即将index的项删除后面补上去
((NoteEditText) mEditTextList.getChildAt(i).findViewById(R.id.et_edit_text))
.setIndex(i - 1);
}
mEditTextList.removeViewAt(index);
NoteEditText edit = null;
if(index == 0) {//如果index为0则将edit定位到第一个EditText项
edit = (NoteEditText) mEditTextList.getChildAt(0).findViewById(
R.id.et_edit_text);
} else {//index不为0则将edit定位到index-1对应的EditText项
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) {//当编辑便签中输入enter时执行该函数
/**
* Should not happen, check for debug
*/
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);
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++;
}
}
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) {
//从note_edit_list_item布局文件中获取视图并对其进行初始化操作。
View view = LayoutInflater.from(this).inflate(R.layout.note_edit_list_item, null);
//从视图中查找 NoteEditText 控件并设置文本样式。
final NoteEditText edit = (NoteEditText) view.findViewById(R.id.et_edit_text);
edit.setTextAppearance(this, TextAppearanceResources.getTexAppearanceResource(mFontSizeId));
//从视图中查找 CheckBox 控件,并对其设置勾选状态改变监听器。
CheckBox cb = ((CheckBox) view.findViewById(R.id.cb_edit_item));
cb.setOnCheckedChangeListener(new OnCheckedChangeListener() {
public void onCheckedChanged(CompoundButton buttonView, boolean isChecked) {
//如果CheckBox已勾选则给NoteEditText添加文本样式显示为删除线。
if (isChecked) {
edit.setPaintFlags(edit.getPaintFlags() | Paint.STRIKE_THRU_TEXT_FLAG);
}
//否则将NoteEditText的文本样式重置为默认样式。
else {
edit.setPaintFlags(Paint.ANTI_ALIAS_FLAG | Paint.DEV_KERN_TEXT_FLAG);
}
}
});
//返回经过初始化、设置文本样式和勾选状态监听器的视图。
return view;
}
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++) {//for循环遍历EditTextList的项
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) {//如果workingNote已经保存则将结果设置为ok。
/**
* There are two modes from List view to edit view, open one note,
* create/edit a node. Opening node requires to the original
* position in the list when back from edit view, while creating a
* new node requires to the top of the list. This code
* {@link #RESULT_OK} is used to identify the create/edit state
*/
setResult(RESULT_OK);
}
return saved;
}
private void sendToDesktop() {//将便签发送到桌面
/**
* Before send message to home, we should make sure that current
* editing note is exists in databases. So, for new note, firstly
* save it
*/
if (!mWorkingNote.existInDatabase()) {//若不存在数据也就是新的标签就先保存
saveNote();
}
if (mWorkingNote.getNoteId() > 0) {//若便签的id大于0则执行以下程序
Intent sender = new Intent();
Intent shortcutIntent = new Intent(this, NoteEditActivity.class);
shortcutIntent.setAction(Intent.ACTION_VIEW);
shortcutIntent.putExtra(Intent.EXTRA_UID, mWorkingNote.getNoteId());
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);
sender.setAction("com.android.launcher.action.INSTALL_SHORTCUT");
showToast(R.string.info_note_enter_desktop);
sendBroadcast(sender);
} else {//如果便签的id错误则保存提醒用户
/**
* There is the condition that user has input nothing (the note is
* not worthy saving), we have no note id, remind the user that he
* should input something
*/
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;//直接设置为content中的内容并返回有勾选和未勾选2种
}
private void showToast(int resId) {//显示提示的视图 函数实现:根据下标显示对应的提示
showToast(resId, Toast.LENGTH_SHORT);
}
private void showToast(int resId, int duration) {
//持续显示提示的视图 函数实现根据下标和持续的时间duration编辑提示视图并显示
Toast.makeText(this, resId, duration).show();
}
}