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

1159 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;
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 {
// 定义一个内部类HeadViewHolder用于保存头部视图的控件
private class HeadViewHolder {
// 保存修改日期的TextView
public TextView tvModified;
// 保存提醒图标的ImageView
public ImageView ivAlertIcon;
// 保存提醒日期的TextView
public TextView tvAlertDate;
// 保存设置背景颜色的ImageView
public ImageView ibSetBgColor;
}
// 定义一个静态的Map用于存储背景选择按钮的ID和对应的颜色值
private static final Map<Integer, Integer> sBgSelectorBtnsMap = new HashMap<Integer, Integer>();
// 静态代码块用于初始化Map
static {
// 将背景选择按钮的ID和对应的颜色值放入Map中
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>();
// 初始化静态变量sBgSelectorSelectionMap用于存储背景选择器的资源ID
static {
// 将ResourceParser.YELLOW对应的资源ID存入sBgSelectorSelectionMap
sBgSelectorSelectionMap.put(ResourceParser.YELLOW, R.id.iv_bg_yellow_select);
// 将ResourceParser.RED对应的资源ID存入sBgSelectorSelectionMap
sBgSelectorSelectionMap.put(ResourceParser.RED, R.id.iv_bg_red_select);
// 将ResourceParser.BLUE对应的资源ID存入sBgSelectorSelectionMap
sBgSelectorSelectionMap.put(ResourceParser.BLUE, R.id.iv_bg_blue_select);
// 将ResourceParser.GREEN对应的资源ID存入sBgSelectorSelectionMap
sBgSelectorSelectionMap.put(ResourceParser.GREEN, R.id.iv_bg_green_select);
// 将ResourceParser.WHITE对应的资源ID存入sBgSelectorSelectionMap
sBgSelectorSelectionMap.put(ResourceParser.WHITE, R.id.iv_bg_white_select);
}
// 定义一个静态常量用于存储字体大小按钮的ID和对应的字体大小
private static final Map<Integer, Integer> sFontSizeBtnsMap = new HashMap<Integer, Integer>();
static {
// 将字体大小按钮的ID和对应的字体大小放入Map中
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";
// 头部视图持有者
private HeadViewHolder mNoteHeaderHolder;
// 头部视图面板
private View mHeadViewPanel;
// 笔记背景颜色选择器
private View mNoteBgColorSelector;
// 字体大小选择器
private View mFontSizeSelector;
// 笔记编辑器
private EditText mNoteEditor;
// 笔记编辑器面板
private View mNoteEditorPanel;
// 工作中的笔记
private WorkingNote mWorkingNote;
// 共享偏好设置
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;
@Override
// 重写onCreate方法
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
// 调用父类的onCreate方法
this.setContentView(R.layout.note_edit);
// 设置当前Activity的布局文件为note_edit.xml
if (savedInstanceState == null && !initActivityState(getIntent())) {
// 如果savedInstanceState为空且initActivityState方法返回false
finish();
// 结束当前Activity
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) {
super.onRestoreInstanceState(savedInstanceState);
// 调用父类的onRestoreInstanceState方法
if (savedInstanceState != null && savedInstanceState.containsKey(Intent.EXTRA_UID)) {
// 如果savedInstanceState不为空且包含Intent.EXTRA_UID键
Intent intent = new Intent(Intent.ACTION_VIEW);
// 创建一个Intent对象并设置Action为Intent.ACTION_VIEW
intent.putExtra(Intent.EXTRA_UID, savedInstanceState.getLong(Intent.EXTRA_UID));
// 将savedInstanceState中的Intent.EXTRA_UID键对应的值添加到Intent对象中
if (!initActivityState(intent)) {
// 如果initActivityState方法返回false
finish();
// 结束当前Activity
return;
}
// 如果initActivityState方法返回true
Log.d(TAG, "Restoring from killed activity");
// 打印日志表示从被杀掉的Activity中恢复
}
}
private boolean initActivityState(Intent intent) {
/**
* 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())) {
long noteId = intent.getLongExtra(Intent.EXTRA_UID, 0);
mUserQuery = "";
/**
* Starting from the searched result
*/
if (intent.hasExtra(SearchManager.EXTRA_DATA_KEY)) {
noteId = Long.parseLong(intent.getStringExtra(SearchManager.EXTRA_DATA_KEY));
mUserQuery = intent.getStringExtra(SearchManager.USER_QUERY);
}
// Check if the note exists
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 {
// Load the note
mWorkingNote = WorkingNote.load(this, noteId);
if (mWorkingNote == null) {
Log.e(TAG, "load note failed with note id" + noteId);
finish();
return false;
}
}
// Set the soft input mode
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())) {
// New note
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));
// Parse call-record note
String phoneNumber = intent.getStringExtra(Intent.EXTRA_PHONE_NUMBER);
long callDate = intent.getLongExtra(Notes.INTENT_EXTRA_CALL_DATE, 0);
if (callDate != 0 && phoneNumber != null) {
if (TextUtils.isEmpty(phoneNumber)) {
Log.w(TAG, "The call record number is null");
}
long noteId = 0;
if ((noteId = DataUtils.getNoteIdByPhoneNumberAndCallDate(getContentResolver(),
phoneNumber, callDate)) > 0) {
mWorkingNote = WorkingNote.load(this, noteId);
if (mWorkingNote == null) {
Log.e(TAG, "load call note failed with note id" + noteId);
finish();
return false;
}
} else {
mWorkingNote = WorkingNote.createEmptyNote(this, folderId, widgetId,
widgetType, bgResId);
mWorkingNote.convertToCallNote(phoneNumber, callDate);
}
} else {
mWorkingNote = WorkingNote.createEmptyNote(this, folderId, widgetId, widgetType,
bgResId);
}
// Set the soft input mode
getWindow().setSoftInputMode(
WindowManager.LayoutParams.SOFT_INPUT_ADJUST_RESIZE
| WindowManager.LayoutParams.SOFT_INPUT_STATE_VISIBLE);
} else {
Log.e(TAG, "Intent not specified action, should not support");
finish();
return false;
}
// Set the listener for the note
mWorkingNote.setOnSettingStatusChangedListener(this);
return true;
}
@Override
protected void onResume() {
super.onResume();
// Initialize the note screen
initNoteScreen();
}
private void initNoteScreen() {
// Set the text appearance of the note editor
mNoteEditor.setTextAppearance(this, TextAppearanceResources
.getTexAppearanceResource(mFontSizeId));
// Check if the note is in check list mode
if (mWorkingNote.getCheckListMode() == TextNote.MODE_CHECK_LIST) {
// Switch to list mode
switchToListMode(mWorkingNote.getContent());
} else {
// Set the text of the note editor
mNoteEditor.setText(getHighlightQueryResult(mWorkingNote.getContent(), mUserQuery));
// Set the selection of the note editor
mNoteEditor.setSelection(mNoteEditor.getText().length());
}
// Set the visibility of the background selector
for (Integer id : sBgSelectorSelectionMap.keySet()) {
findViewById(sBgSelectorSelectionMap.get(id)).setVisibility(View.GONE);
}
// Set the background of the header and note editor panel
mHeadViewPanel.setBackgroundResource(mWorkingNote.getTitleBgResId());
mNoteEditorPanel.setBackgroundResource(mWorkingNote.getBgColorResId());
// Set the modified date of the note
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
*/
// Show the alert header
showAlertHeader();
}
private void showAlertHeader() {
// 如果当前笔记设置了提醒
if (mWorkingNote.hasClockAlert()) {
// 获取当前时间
long time = System.currentTimeMillis();
// 如果当前时间大于提醒时间
if (time > mWorkingNote.getAlertDate()) {
// 设置提醒日期为已过期
mNoteHeaderHolder.tvAlertDate.setText(R.string.note_alert_expired);
} else {
// 设置提醒日期为相对时间
mNoteHeaderHolder.tvAlertDate.setText(DateUtils.getRelativeTimeSpanString(
mWorkingNote.getAlertDate(), time, DateUtils.MINUTE_IN_MILLIS));
}
// 显示提醒日期
mNoteHeaderHolder.tvAlertDate.setVisibility(View.VISIBLE);
// 显示提醒图标
mNoteHeaderHolder.ivAlertIcon.setVisibility(View.VISIBLE);
} else {
// 隐藏提醒日期
mNoteHeaderHolder.tvAlertDate.setVisibility(View.GONE);
// 隐藏提醒图标
mNoteHeaderHolder.ivAlertIcon.setVisibility(View.GONE);
};
}
// 重写onNewIntent方法
@Override
protected void onNewIntent(Intent intent) {
// 调用父类的onNewIntent方法
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
*/
// 如果当前编辑的笔记没有note id我们首先应该保存它以生成一个id。如果编辑的笔记不值得保存那么没有id相当于创建新笔记
if (!mWorkingNote.existInDatabase()) {
saveNote();
}
// 将当前编辑的笔记的id保存到Bundle中
outState.putLong(Intent.EXTRA_UID, mWorkingNote.getNoteId());
// 打印日志记录当前编辑的笔记的id
Log.d(TAG, "Save working note id: " + mWorkingNote.getNoteId() + " onSaveInstanceState");
}
@Override
public boolean dispatchTouchEvent(MotionEvent ev) {
// 如果mNoteBgColorSelector可见且不在mNoteBgColorSelector范围内则隐藏mNoteBgColorSelector
if (mNoteBgColorSelector.getVisibility() == View.VISIBLE
&& !inRangeOfView(mNoteBgColorSelector, ev)) {
mNoteBgColorSelector.setVisibility(View.GONE);
return true;
}
// 如果mFontSizeSelector可见且不在mFontSizeSelector范围内则隐藏mFontSizeSelector
if (mFontSizeSelector.getVisibility() == View.VISIBLE
&& !inRangeOfView(mFontSizeSelector, ev)) {
mFontSizeSelector.setVisibility(View.GONE);
return true;
}
// 否则调用父类的dispatchTouchEvent方法
return super.dispatchTouchEvent(ev);
}
private boolean inRangeOfView(View view, MotionEvent ev) {
// 获取view在屏幕上的位置
int []location = new int[2];
view.getLocationOnScreen(location);
// 获取view的x坐标
int x = location[0];
// 获取view的y坐标
int y = location[1];
// 判断ev的x坐标是否在view的范围内
if (ev.getX() < x
// 判断ev的x坐标是否在view的范围内
|| ev.getX() > (x + view.getWidth())
// 判断ev的y坐标是否在view的范围内
|| ev.getY() < y
// 判断ev的y坐标是否在view的范围内
|| ev.getY() > (y + view.getHeight())) {
// 如果不在范围内返回false
return false;
}
// 如果在范围内返回true
return true;
}
private void initResources() {
// 初始化标题栏
mHeadViewPanel = findViewById(R.id.note_title);
// 初始化标题栏的ViewHolder
mNoteHeaderHolder = new HeadViewHolder();
// 初始化标题栏中的修改日期TextView
mNoteHeaderHolder.tvModified = (TextView) findViewById(R.id.tv_modified_date);
// 初始化标题栏中的提醒图标ImageView
mNoteHeaderHolder.ivAlertIcon = (ImageView) findViewById(R.id.iv_alert_icon);
// 初始化标题栏中的提醒日期TextView
mNoteHeaderHolder.tvAlertDate = (TextView) findViewById(R.id.tv_alert_date);
// 初始化标题栏中的设置背景颜色按钮ImageView
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);
};
// 获取默认的SharedPreferences
mSharedPrefs = PreferenceManager.getDefaultSharedPreferences(this);
mFontSizeId = mSharedPrefs.getInt(PREFERENCE_FONT_SIZE, ResourceParser.BG_DEFAULT_FONT_SIZE);
/**
* 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()) {
mFontSizeId = ResourceParser.BG_DEFAULT_FONT_SIZE;
}
mEditTextList = (LinearLayout) findViewById(R.id.note_edit_list);
}
// 重写onPause方法
@Override
protected void onPause() {
// 调用父类的onPause方法
super.onPause();
// 如果保存笔记数据成功
if(saveNote()) {
// 打印日志,显示笔记数据的长度
Log.d(TAG, "Note data was saved with length:" + mWorkingNote.getContent().length());
}
// 清除设置状态
clearSettingState();
}
private void updateWidget() {
// 创建一个Intent用于更新AppWidget
Intent intent = new Intent(AppWidgetManager.ACTION_APPWIDGET_UPDATE);
// 判断当前笔记的Widget类型
if (mWorkingNote.getWidgetType() == Notes.TYPE_WIDGET_2X) {
// 如果是2x类型的Widget设置Intent的Class为NoteWidgetProvider_2x
intent.setClass(this, NoteWidgetProvider_2x.class);
} else if (mWorkingNote.getWidgetType() == Notes.TYPE_WIDGET_4X) {
// 如果是4x类型的Widget设置Intent的Class为NoteWidgetProvider_4x
intent.setClass(this, NoteWidgetProvider_4x.class);
} else {
// 如果是其他类型的Widget输出错误日志并返回
Log.e(TAG, "Unspported widget type");
return;
}
// 将当前笔记的WidgetId添加到Intent中
intent.putExtra(AppWidgetManager.EXTRA_APPWIDGET_IDS, new int[] {
mWorkingNote.getWidgetId()
});
// 发送广播更新AppWidget
sendBroadcast(intent);
// 设置结果为RESULT_OK
setResult(RESULT_OK, intent);
}
public void onClick(View v) {
// 获取点击的View的id
int id = v.getId();
// 如果点击的是设置背景颜色的按钮
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));
// 隐藏背景颜色选择器
mNoteBgColorSelector.setVisibility(View.GONE);
// 如果点击的是字体大小选择器中的按钮
} else if (sFontSizeBtnsMap.containsKey(id)) {
// 隐藏当前字体大小选择器
findViewById(sFontSelectorSelectionMap.get(mFontSizeId)).setVisibility(View.GONE);
// 设置当前字体大小
mFontSizeId = sFontSizeBtnsMap.get(id);
// 保存当前字体大小到SharedPreferences
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() {
// 如果mNoteBgColorSelector可见则将其设置为不可见并返回true
if (mNoteBgColorSelector.getVisibility() == View.VISIBLE) {
mNoteBgColorSelector.setVisibility(View.GONE);
return true;
// 如果mFontSizeSelector可见则将其设置为不可见并返回true
} else if (mFontSizeSelector.getVisibility() == View.VISIBLE) {
mFontSizeSelector.setVisibility(View.GONE);
return true;
}
// 如果以上两种情况都不满足则返回false
return false;
}
// 当背景颜色改变时调用
public void onBackgroundColorChanged() {
// 根据当前笔记的背景颜色ID找到对应的背景颜色选择器并将其可见性设置为可见
findViewById(sBgSelectorSelectionMap.get(mWorkingNote.getBgColorId())).setVisibility(
View.VISIBLE);
// 设置笔记编辑面板的背景颜色为当前笔记的背景颜色资源ID
mNoteEditorPanel.setBackgroundResource(mWorkingNote.getBgColorResId());
// 设置标题面板的背景颜色为当前笔记的标题背景颜色资源ID
mHeadViewPanel.setBackgroundResource(mWorkingNote.getTitleBgResId());
}
@Override
public boolean onPrepareOptionsMenu(Menu menu) {
// 如果Activity正在结束则返回true
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) {
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);
}
// 返回true
return true;
}
@Override
public boolean onOptionsItemSelected(MenuItem item) {
// 根据菜单项的id执行相应的操作
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对象参数为当前上下文和当前时间
DateTimePickerDialog d = new DateTimePickerDialog(this, System.currentTimeMillis());
// 设置DateTimePickerDialog的监听器
d.setOnDateTimeSetListener(new OnDateTimeSetListener() {
// 当日期时间被设置时,调用此方法
public void OnDateTimeSet(AlertDialog dialog, long date) {
// 设置工作笔记的提醒日期
mWorkingNote.setAlertDate(date , true);
}
});
// 显示DateTimePickerDialog
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来存储要删除的笔记的id
HashSet<Long> ids = new HashSet<Long>();
// 获取当前笔记的id
long id = mWorkingNote.getNoteId();
// 如果当前笔记的id不是根文件夹的id
if (id != Notes.ID_ROOT_FOLDER) {
// 将当前笔记的id添加到HashSet中
ids.add(id);
} else {
// 如果当前笔记的id是根文件夹的id则打印错误日志
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) {
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();
}
public void onEditTextDelete(int index, String text) {
// 获取EditTextList的子视图数量
int childCount = mEditTextList.getChildCount();
// 如果子视图数量为1则直接返回
if (childCount == 1) {
return;
}
// 从被删除的EditText的下一个开始依次将每个EditText的索引减1
for (int i = index + 1; i < childCount; i++) {
((NoteEditText) mEditTextList.getChildAt(i).findViewById(R.id.et_edit_text))
.setIndex(i - 1);
}
// 从EditTextList中移除被删除的EditText
mEditTextList.removeViewAt(index);
// 获取被删除的EditText的前一个EditText
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);
}
// 获取被删除的EditText的长度
int length = edit.length();
// 将被删除的EditText的内容追加到前一个EditText中
edit.append(text);
// 将焦点设置到前一个EditText中
edit.requestFocus();
// 将光标设置到前一个EditText的末尾
edit.setSelection(length);
}
public void onEditTextEnter(int index, String text) {
/**
* Should not happen, check for debug
*/
// 如果index大于mEditTextList的子元素数量则输出错误日志
if(index > mEditTextList.getChildCount()) {
Log.e(TAG, "Index out of mEditTextList boundrary, should not happen");
}
// 获取列表项
View view = getListItem(text, index);
// 将列表项添加到mEditTextList中
mEditTextList.addView(view, index);
// 获取NoteEditText
NoteEditText edit = (NoteEditText) view.findViewById(R.id.et_edit_text);
// 设置焦点
edit.requestFocus();
// 设置光标位置
edit.setSelection(0);
// 遍历mEditTextList中的子元素从index+1开始
for (int i = index + 1; i < mEditTextList.getChildCount(); i++) {
// 设置NoteEditText的索引
((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));
// 获取最后一个列表项的EditText并请求焦点
mEditTextList.getChildAt(index).findViewById(R.id.et_edit_text).requestFocus();
// 隐藏NoteEditor
mNoteEditor.setVisibility(View.GONE);
// 显示EditTextList
mEditTextList.setVisibility(View.VISIBLE);
}
// 根据用户查询结果高亮显示全文
private Spannable getHighlightQueryResult(String fullText, String userQuery) {
// 创建一个SpannableString对象用于存储高亮显示的文本
SpannableString spannable = new SpannableString(fullText == null ? "" : fullText);
// 如果用户查询结果不为空
if (!TextUtils.isEmpty(userQuery)) {
// 创建一个正则表达式对象,用于匹配用户查询结果
mPattern = Pattern.compile(userQuery);
// 创建一个Matcher对象用于匹配全文
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;
}
// 根据传入的item和index获取一个View
private View getListItem(String item, int index) {
// 通过LayoutInflater加载布局文件
View view = LayoutInflater.from(this).inflate(R.layout.note_edit_list_item, null);
// 获取EditText
final NoteEditText edit = (NoteEditText) view.findViewById(R.id.et_edit_text);
// 设置EditText的文本样式
edit.setTextAppearance(this, TextAppearanceResources.getTexAppearanceResource(mFontSizeId));
// 获取CheckBox
CheckBox cb = ((CheckBox) view.findViewById(R.id.cb_edit_item));
// 设置CheckBox的监听器
cb.setOnCheckedChangeListener(new OnCheckedChangeListener() {
public void onCheckedChanged(CompoundButton buttonView, boolean isChecked) {
// 如果CheckBox被选中
if (isChecked) {
// 设置EditText的画笔标志添加删除线
edit.setPaintFlags(edit.getPaintFlags() | Paint.STRIKE_THRU_TEXT_FLAG);
} else {
// 设置EditText的画笔标志取消删除线
edit.setPaintFlags(Paint.ANTI_ALIAS_FLAG | Paint.DEV_KERN_TEXT_FLAG);
}
}
});
// 如果item以TAG_CHECKED开头
if (item.startsWith(TAG_CHECKED)) {
// 设置CheckBox为选中状态
cb.setChecked(true);
// 设置EditText的画笔标志添加删除线
edit.setPaintFlags(edit.getPaintFlags() | Paint.STRIKE_THRU_TEXT_FLAG);
// 去掉TAG_CHECKED
item = item.substring(TAG_CHECKED.length(), item.length()).trim();
// 如果item以TAG_UNCHECKED开头
} else if (item.startsWith(TAG_UNCHECKED)) {
// 设置CheckBox为未选中状态
cb.setChecked(false);
// 设置EditText的画笔标志取消删除线
edit.setPaintFlags(Paint.ANTI_ALIAS_FLAG | Paint.DEV_KERN_TEXT_FLAG);
// 去掉TAG_UNCHECKED
item = item.substring(TAG_UNCHECKED.length(), item.length()).trim();
}
// 设置EditText的监听器
edit.setOnTextViewChangeListener(this);
// 设置EditText的索引
edit.setIndex(index);
// 设置EditText的文本
edit.setText(getHighlightQueryResult(item, mUserQuery));
return view;
}
// 当EditText的文本发生变化时调用
public void onTextChange(int index, boolean hasText) {
// 如果索引超出范围
if (index >= mEditTextList.getChildCount()) {
Log.e(TAG, "Wrong index, should not happen");
return;
}
// 如果有文本
if(hasText) {
// 设置CheckBox为可见
mEditTextList.getChildAt(index).findViewById(R.id.cb_edit_item).setVisibility(View.VISIBLE);
// 如果没有文本
} else {
// 设置CheckBox为不可见
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对象
StringBuilder sb = new StringBuilder();
// 遍历EditTextList的子视图
for (int i = 0; i < mEditTextList.getChildCount(); i++) {
// 获取子视图
View view = mEditTextList.getChildAt(i);
// 获取NoteEditText对象
NoteEditText edit = (NoteEditText) view.findViewById(R.id.et_edit_text);
// 如果EditText的内容不为空
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) {
/**
* 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) {
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 {
/**
* 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);
}
}
// 根据传入的content去除TAG_CHECKED和TAG_UNCHECKED标签并返回处理后的字符串
private String makeShortcutIconTitle(String content) {
// 去除TAG_CHECKED标签
content = content.replace(TAG_CHECKED, "");
// 去除TAG_UNCHECKED标签
content = content.replace(TAG_UNCHECKED, "");
// 如果处理后的字符串长度大于SHORTCUT_ICON_TITLE_MAX_LEN则截取前SHORTCUT_ICON_TITLE_MAX_LEN个字符
return content.length() > SHORTCUT_ICON_TITLE_MAX_LEN ? content.substring(0,
SHORTCUT_ICON_TITLE_MAX_LEN) : content;
}
// 显示Toast使用默认时长
private void showToast(int resId) {
// 调用showToast方法传入resId和默认时长
showToast(resId, Toast.LENGTH_SHORT);
}
// 显示Toast使用指定时长
private void showToast(int resId, int duration) {
// 使用Toast.makeText方法创建Toast对象并显示
Toast.makeText(this, resId, duration).show();
}
}