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.
ReadMiNotes/代码标注/NoteEditActivity.java

1117 lines
47 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 {
// 修改时间
public TextView tvModified;
// 警报图标
public ImageView ivAlertIcon;
// 警报日期
public TextView tvAlertDate;
// 设置背景颜色
public ImageView ibSetBgColor;
}
private static final Map<Integer, Integer> sBgSelectorBtnsMap = new HashMap<Integer, Integer>();
static {
// 将R.id.iv_bg_yellow对应的ResourceParser.YELLOW添加到sBgSelectorBtnsMap中
sBgSelectorBtnsMap.put(R.id.iv_bg_yellow, ResourceParser.YELLOW);
// 将R.id.iv_bg_red对应的ResourceParser.RED添加到sBgSelectorBtnsMap中
sBgSelectorBtnsMap.put(R.id.iv_bg_red, ResourceParser.RED);
// 将R.id.iv_bg_blue对应的ResourceParser.BLUE添加到sBgSelectorBtnsMap中
sBgSelectorBtnsMap.put(R.id.iv_bg_blue, ResourceParser.BLUE);
// 将R.id.iv_bg_green对应的ResourceParser.GREEN添加到sBgSelectorBtnsMap中
sBgSelectorBtnsMap.put(R.id.iv_bg_green, ResourceParser.GREEN);
// 将R.id.iv_bg_white对应的ResourceParser.WHITE添加到sBgSelectorBtnsMap中
sBgSelectorBtnsMap.put(R.id.iv_bg_white, ResourceParser.WHITE);
}
private static final Map<Integer, Integer> sBgSelectorSelectionMap = new HashMap<Integer, Integer>();
static {
// 将YELLOW放入sBgSelectorSelectionMap中
sBgSelectorSelectionMap.put(ResourceParser.YELLOW, R.id.iv_bg_yellow_select);
// 将RED放入sBgSelectorSelectionMap中
sBgSelectorSelectionMap.put(ResourceParser.RED, R.id.iv_bg_red_select);
// 将BLUE放入sBgSelectorSelectionMap中
sBgSelectorSelectionMap.put(ResourceParser.BLUE, R.id.iv_bg_blue_select);
// 将GREEN放入sBgSelectorSelectionMap中
sBgSelectorSelectionMap.put(ResourceParser.GREEN, R.id.iv_bg_green_select);
// 将WHITE放入sBgSelectorSelectionMap中
sBgSelectorSelectionMap.put(ResourceParser.WHITE, R.id.iv_bg_white_select);
}
private static final Map<Integer, Integer> sFontSizeBtnsMap = new HashMap<Integer, Integer>();
static {
// 将R.id.ll_font_large和ResourceParser.TEXT_LARGE放入sFontSizeBtnsMap中
sFontSizeBtnsMap.put(R.id.ll_font_large, ResourceParser.TEXT_LARGE);
// 将R.id.ll_font_small和ResourceParser.TEXT_SMALL放入sFontSizeBtnsMap中
sFontSizeBtnsMap.put(R.id.ll_font_small, ResourceParser.TEXT_SMALL);
// 将R.id.ll_font_normal和ResourceParser.TEXT_MEDIUM放入sFontSizeBtnsMap中
sFontSizeBtnsMap.put(R.id.ll_font_normal, ResourceParser.TEXT_MEDIUM);
// 将R.id.ll_font_super和ResourceParser.TEXT_SUPER放入sFontSizeBtnsMap中
sFontSizeBtnsMap.put(R.id.ll_font_super, ResourceParser.TEXT_SUPER);
}
private static final Map<Integer, Integer> sFontSelectorSelectionMap = new HashMap<Integer, Integer>();
static {
// 将ResourceParser.TEXT_LARGE和R.id.iv_large_select放入sFontSelectorSelectionMap中
sFontSelectorSelectionMap.put(ResourceParser.TEXT_LARGE, R.id.iv_large_select);
// 将ResourceParser.TEXT_SMALL和R.id.iv_small_select放入sFontSelectorSelectionMap中
sFontSelectorSelectionMap.put(ResourceParser.TEXT_SMALL, R.id.iv_small_select);
// 将ResourceParser.TEXT_MEDIUM和R.id.iv_medium_select放入sFontSelectorSelectionMap中
sFontSelectorSelectionMap.put(ResourceParser.TEXT_MEDIUM, R.id.iv_medium_select);
// 将ResourceParser.TEXT_SUPER和R.id.iv_super_select放入sFontSelectorSelectionMap中
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;
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
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
// 设置当前页面的布局
this.setContentView(R.layout.note_edit);
// 如果savedInstanceState为空且initActivityState(getIntent())返回false则结束当前Activity
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
protected void onRestoreInstanceState(Bundle savedInstanceState) {
super.onRestoreInstanceState(savedInstanceState);
// 从保存的实例状态中恢复
if (savedInstanceState != null && savedInstanceState.containsKey(Intent.EXTRA_UID)) {
// 从保存的实例状态中恢复
Intent intent = new Intent(Intent.ACTION_VIEW);
intent.putExtra(Intent.EXTRA_UID, savedInstanceState.getLong(Intent.EXTRA_UID));
if (!initActivityState(intent)) {
finish();
return;
}
Log.d(TAG, "Restoring from killed activity");
}
}
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())) {
// 获取传入的noteId
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);
}
// 检查note是否可见
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 {
// 加载note
mWorkingNote = WorkingNote.load(this, noteId);
if (mWorkingNote == null) {
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())) {
// New note
// 获取传入的文件夹id
long folderId = intent.getLongExtra(Notes.INTENT_EXTRA_FOLDER_ID, 0);
// 获取传入的小部件id
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);
// 获取传入的小部件背景资源id
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;
// 获取电话号码和通话日期的noteId
if ((noteId = DataUtils.getNoteIdByPhoneNumberAndCallDate(getContentResolver(),
phoneNumber, callDate)) > 0) {
// 加载note
mWorkingNote = WorkingNote.load(this, noteId);
if (mWorkingNote == null) {
Log.e(TAG, "load call note failed with note id" + noteId);
finish();
return false;
}
} else {
// 创建一个新的call note
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();
return false;
}
mWorkingNote.setOnSettingStatusChangedListener(this);
return 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()) {
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() {
// 检查工作笔记是否有闹钟
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()) {
saveNote();
}
// 将当前笔记的ID存储到Bundle中
outState.putLong(Intent.EXTRA_UID, mWorkingNote.getNoteId());
Log.d(TAG, "Save working note id: " + mWorkingNote.getNoteId() + " onSaveInstanceState");
}
@Override
public boolean dispatchTouchEvent(MotionEvent ev) {
// 如果mNoteBgColorSelector可见且ev不在mNoteBgColorSelector范围内则将mNoteBgColorSelector设置为不可见
if (mNoteBgColorSelector.getVisibility() == View.VISIBLE
&& !inRangeOfView(mNoteBgColorSelector, ev)) {
mNoteBgColorSelector.setVisibility(View.GONE);
return true;
}
// 如果mFontSizeSelector可见且ev不在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) {
int []location = new int[2];
view.getLocationOnScreen(location);
int x = location[0];
int y = location[1];
//判断触摸点是否在View的范围内
if (ev.getX() < x
|| ev.getX() > (x + view.getWidth())
|| ev.getY() < y
|| ev.getY() > (y + view.getHeight())) {
return false;
}
return true;
}
private void initResources() {
// 初始化头部视图
mHeadViewPanel = findViewById(R.id.note_title);
// 初始化头部视图的holder
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);
//遍历sFontSizeBtnsMap中的键找到对应的View
for (int id : sFontSizeBtnsMap.keySet()) {
View view = findViewById(id);
//为View设置点击事件
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;
}
//获取布局中的LinearLayout
mEditTextList = (LinearLayout) findViewById(R.id.note_edit_list);
}
@Override
protected void onPause() {
super.onPause();
// 保存笔记
if(saveNote()) {
Log.d(TAG, "Note data was saved with length:" + mWorkingNote.getContent().length());
}
// 清除设置状态
clearSettingState();
}
private void updateWidget() {
// 创建一个Intent用于更新小部件
Intent intent = new Intent(AppWidgetManager.ACTION_APPWIDGET_UPDATE);
// 根据小部件类型设置Intent的class
if (mWorkingNote.getWidgetType() == Notes.TYPE_WIDGET_2X) {
intent.setClass(this, NoteWidgetProvider_2x.class);
} else if (mWorkingNote.getWidgetType() == Notes.TYPE_WIDGET_4X) {
intent.setClass(this, NoteWidgetProvider_4x.class);
} else {
Log.e(TAG, "Unspported widget type");
return;
}
// 将小部件ID放入Intent中
intent.putExtra(AppWidgetManager.EXTRA_APPWIDGET_IDS, new int[] {
mWorkingNote.getWidgetId()
});
// 发送Intent
sendBroadcast(intent);
// 设置更新结果
setResult(RESULT_OK, intent);
}
public void onClick(View v) {
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);
// 设置当前笔记的背景颜色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);
// 保存当前笔记的字体大小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();
//调用父类的onBackPressed()方法
super.onBackPressed();
}
private boolean clearSettingState() {
//检查mNoteBgColorSelector的可见性
if (mNoteBgColorSelector.getVisibility() == View.VISIBLE) {
//将mNoteBgColorSelector设置为不可见
mNoteBgColorSelector.setVisibility(View.GONE);
//返回true
return true;
//检查mFontSizeSelector的可见性
} else if (mFontSizeSelector.getVisibility() == View.VISIBLE) {
//将mFontSizeSelector设置为不可见
mFontSizeSelector.setVisibility(View.GONE);
//返回true
return true;
}
//返回false
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) {
// 如果正在关闭则返回true
if (isFinishing()) {
return true;
}
// 清除设置状态
clearSettingState();
// 清除菜单
menu.clear();
// 如果文件夹ID等于Notes.ID_CALL_RECORD_FOLDER则使用R.menu.call_note_edit否则使用R.menu.note_edit
if (mWorkingNote.getFolderId() == Notes.ID_CALL_RECORD_FOLDER) {
getMenuInflater().inflate(R.menu.call_note_edit, menu);
} else {
getMenuInflater().inflate(R.menu.note_edit, menu);
}
// 如果检查列表模式为TextNote.MODE_CHECK_LIST则设置菜单标题为R.string.menu_normal_mode否则设置菜单标题为R.string.menu_list_mode
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);
}
// 如果有闹钟则隐藏菜单中的menu_alert否则隐藏菜单中的menu_delete_remind
if (mWorkingNote.hasClockAlert()) {
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对象传入当前时间
DateTimePickerDialog d = new DateTimePickerDialog(this, System.currentTimeMillis());
// 为DateTimePickerDialog设置OnDateTimeSetListener当设置时间时调用
d.setOnDateTimeSetListener(new OnDateTimeSetListener() {
public void OnDateTimeSet(AlertDialog dialog, long date) {
// 设置mWorkingNote的alertDate为传入的date并设置为true
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中
intent.putExtra(Intent.EXTRA_TEXT, info);
// 设置要发送的类型
intent.setType("text/plain");
// 启动发送
context.startActivity(intent);
}
private void createNewNote() {
// 首先,保存当前编辑笔记
saveNote();
// 为了安全起见启动新的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
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 {
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);
}
//获取NotesPreferenceActivity中的同步账户名并判断是否大于0
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) {
// 获取EditText列表的子视图的数量
int childCount = mEditTextList.getChildCount();
// 如果只有一个子视图,则直接返回
if (childCount == 1) {
return;
}
// 从index+1开始将EditText列表中的每一个子视图的index属性设置为i-1
for (int i = index + 1; i < childCount; i++) {
((NoteEditText) mEditTextList.getChildAt(i).findViewById(R.id.et_edit_text))
.setIndex(i - 1);
}
// 从EditText列表中移除指定位置的子视图
mEditTextList.removeViewAt(index);
NoteEditText edit = null;
// 如果移除的是第一个子视图则获取EditText列表的第一个子视图
if(index == 0) {
edit = (NoteEditText) mEditTextList.getChildAt(0).findViewById(
R.id.et_edit_text);
// 否则获取EditText列表中指定位置的子视图
} else {
edit = (NoteEditText) mEditTextList.getChildAt(index - 1).findViewById(
R.id.et_edit_text);
}
// 获取EditText中的文本长度
int length = edit.length();
// 将文本添加到EditText中
edit.append(text);
// 设置EditText获取焦点
edit.requestFocus();
// 设置EditText中的光标位置
edit.setSelection(length);
}
public void onEditTextEnter(int index, String text) {
/**
* Should not happen, check for debug
*/
// 检查索引是否超出mEditTextList的范围
if(index > mEditTextList.getChildCount()) {
Log.e(TAG, "Index out of mEditTextList boundrary, should not happen");
}
// 获取指定索引的View
View view = getListItem(text, index);
// 将View添加到mEditTextList中
mEditTextList.addView(view, index);
// 获取EditText
NoteEditText edit = (NoteEditText) view.findViewById(R.id.et_edit_text);
// 设置焦点
edit.requestFocus();
// 设置光标位置
edit.setSelection(0);
// 遍历mEditTextList设置每个EditText的索引
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中的所有视图
mEditTextList.removeAllViews();
// 将文本按换行符分割成字符串数组
String[] items = text.split("\n");
int index = 0;
// 遍历字符串数组将每一个字符串添加到mEditTextList中
for (String item : items) {
if(!TextUtils.isEmpty(item)) {
mEditTextList.addView(getListItem(item, index));
index++;
}
}
// 将一个空字符串添加到mEditTextList中
mEditTextList.addView(getListItem("", index));
// 将焦点设置到mEditTextList中最后一个添加的视图中
mEditTextList.getChildAt(index).findViewById(R.id.et_edit_text).requestFocus();
// 将mNoteEditor设置为不可见mEditTextList设置为可见
mNoteEditor.setVisibility(View.GONE);
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 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);
// 获取edit文本框
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);
}
}
});
// 判断item是否以TAG_CHECKED开头
if (item.startsWith(TAG_CHECKED)) {
// 设置复选框为选中状态
cb.setChecked(true);
// 设置文本框的画笔标志
edit.setPaintFlags(edit.getPaintFlags() | Paint.STRIKE_THRU_TEXT_FLAG);
// 截取item
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 = 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) {
//如果索引大于等于EditText列表的子元素数量则打印错误信息
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();
// 遍历EditTextList中的每一个View
for (int i = 0; i < mEditTextList.getChildCount(); i++) {
View view = mEditTextList.getChildAt(i);
// 获取EditText
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()) {
// 将复选框中的文本添加到StringBuilder中
sb.append(TAG_CHECKED).append(" ").append(edit.getText()).append("\n");
hasChecked = true;
} else {
// 将未复选框中的文本添加到StringBuilder中
sb.append(TAG_UNCHECKED).append(" ").append(edit.getText()).append("\n");
}
}
}
// 将StringBuilder中的文本设置为当前工作文本
mWorkingNote.setWorkingText(sb.toString());
} else {
// 将NoteEditor中的文本设置为当前工作文本
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
Intent sender = new Intent();
// 创建一个Intent用于跳转到NoteEditActivity
Intent shortcutIntent = new Intent(this, NoteEditActivity.class);
// 设置Intent的Action
shortcutIntent.setAction(Intent.ACTION_VIEW);
// 设置Intent的参数用于跳转到NoteEditActivity
shortcutIntent.putExtra(Intent.EXTRA_UID, mWorkingNote.getNoteId());
// 将Intent添加到sender中
sender.putExtra(Intent.EXTRA_SHORTCUT_INTENT, shortcutIntent);
// 设置Intent的名称
sender.putExtra(Intent.EXTRA_SHORTCUT_NAME,
makeShortcutIconTitle(mWorkingNote.getContent()));
// 设置Intent的图标
sender.putExtra(Intent.EXTRA_SHORTCUT_ICON_RESOURCE,
Intent.ShortcutIconResource.fromContext(this, R.drawable.icon_app));
// 设置Intent的参数用于标记是否是重复创建
sender.putExtra("duplicate", true);
// 设置Intent的Action
sender.setAction("com.android.launcher.action.INSTALL_SHORTCUT");
// 显示提示信息
showToast(R.string.info_note_enter_desktop);
// 发送Intent
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);
}
}
private String makeShortcutIconTitle(String content) {
// 移除TAG_CHECKED和TAG_UNCHECKED
content = content.replace(TAG_CHECKED, "");
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;
}
private void showToast(int resId) {
// 显示Toast持续时间为Toast.LENGTH_SHORT
showToast(resId, Toast.LENGTH_SHORT);
}
private void showToast(int resId, int duration) {
// 创建Toast显示resId持续时间为duration
Toast.makeText(this, resId, duration).show();
}
}