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.
mi-note-project/doc/康江龙注释的代码/NoteEditActivity.java

1117 lines
46 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>();
//使用Map实现数据的储存
static {
//对HashMap的设置颜色相对应链接
//put函数用来将指定的值与指定键相连
sBgSelectorBtnsMap.put(R.id.iv_bg_yellow, ResourceParser.YELLOW);
sBgSelectorBtnsMap.put(R.id.iv_bg_red, ResourceParser.RED);
sBgSelectorBtnsMap.put(R.id.iv_bg_blue, ResourceParser.BLUE);
sBgSelectorBtnsMap.put(R.id.iv_bg_green, ResourceParser.GREEN);
sBgSelectorBtnsMap.put(R.id.iv_bg_white, ResourceParser.WHITE);
}
private static final Map<Integer, Integer> sBgSelectorSelectionMap = new HashMap<Integer, Integer>();
//按钮与设置背景颜色的对应
static {
//put函数用来将指定的值与指定键相连
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 {
//put函数用来将指定的值与指定键相连
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 {
//put函数用来将指定的值与指定键相连
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;
//字体设置
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);//视图的设置显示
if (savedInstanceState == null && !initActivityState(getIntent())) {
finish();//结束activity
return;
}
//判断传入参数是否为空且对activity进行初始化是否成功
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
intent.putExtra(Intent.EXTRA_UID, savedInstanceState.getLong(Intent.EXTRA_UID));
//利用此前的extra_uid初始化一个intent
if (!initActivityState(intent)) {
finish();
return;
}
//初始化失败,就退出结束
Log.d(TAG, "Restoring from killed activity");
}
//如果保存了关闭前的实例状态且其中包含着便签标识符则将该标识符添加到初始化Activity的Intent中
}
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 = "";
//如果用户实例化标签时系统并未给出标签ID
/**
* Starting from the searched result
*/
if (intent.hasExtra(SearchManager.EXTRA_DATA_KEY)) {
//根据键值查找ID
noteId = Long.parseLong(intent.getStringExtra(SearchManager.EXTRA_DATA_KEY));
//获取便签ID
mUserQuery = intent.getStringExtra(SearchManager.USER_QUERY);
}
if (!DataUtils.visibleInNoteDatabase(getContentResolver(), noteId, Notes.TYPE_NOTE)) {
//如果ID数据库中没有找到
Intent jump = new Intent(this, NotesListActivity.class);
startActivity(jump);
showToast(R.string.error_note_not_exist);
finish();
return false;
} else {
//找到了
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);
//使Edit界面适应软键盘弹出
} else if(TextUtils.equals(Intent.ACTION_INSERT_OR_EDIT, intent.getAction())) {
//getAction获得字符串判断intent是否需要新建便签
// New note
long folderId = intent.getLongExtra(Notes.INTENT_EXTRA_FOLDER_ID, 0);
//文件夹ID
int widgetId = intent.getIntExtra(Notes.INTENT_EXTRA_WIDGET_ID,
AppWidgetManager.INVALID_APPWIDGET_ID);
//窗口ID
int widgetType = intent.getIntExtra(Notes.INTENT_EXTRA_WIDGET_TYPE,
Notes.TYPE_WIDGET_INVALIDE);
//widget的类型
int bgResId = intent.getIntExtra(Notes.INTENT_EXTRA_BACKGROUND_ID,
ResourceParser.getDefaultBgId(this));
//背景ID
// Parse call-record note
String phoneNumber = intent.getStringExtra(Intent.EXTRA_PHONE_NUMBER);
//实现的功能是解析通话记录便签首先获取intent里的电话号码
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) {
//根据电话号码和日期找到对应标识符赋给noteId
mWorkingNote = WorkingNote.load(this, noteId);
//导入便签
if (mWorkingNote == null) {
//mWorkingNote中空报错警告
Log.e(TAG, "load call note failed with note id" + noteId);
//警告
finish();
return false;
}
//若根据电话号码和通话时间未获取到便签id则执行下面程序
} else {
mWorkingNote = WorkingNote.createEmptyNote(this, folderId, widgetId,
widgetType, bgResId);
//没有找到noteId里的信息创建一个新的便签并且初始化
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() {
//重写Activity的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()) {
//for循环遍历
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);
};
//此时WorkingNote对象中不含提醒则设置标题中相关提醒的控件为不可见
}
@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();//保存
}
outState.putLong(Intent.EXTRA_UID, mWorkingNote.getNoteId());
//将便签的ID信息写入到intent的EXTRA_UID项中便于之后恢复的时候调用
Log.d(TAG, "Save working note id: " + mWorkingNote.getNoteId()+"onSavelnstanceState");
//使用Log来保存信息
}
@Override
public boolean dispatchTouchEvent(MotionEvent ev) {
//屏幕触摸反应
if (mNoteBgColorSelector.getVisibility() == View.VISIBLE
&& !inRangeOfView(mNoteBgColorSelector, ev)) {
//颜色背景选择界面可见且触摸范围在颜色选择范围外
mNoteBgColorSelector.setVisibility(View.GONE);
//颜色选择器
return true;
}
if (mFontSizeSelector.getVisibility() == View.VISIBLE
&& !inRangeOfView(mFontSizeSelector, ev)) {
//字体大小选择界面可见且触摸范围在字体大小选择范围外
mFontSizeSelector.setVisibility(View.GONE);
return true;
}
return super.dispatchTouchEvent(ev);
//调用父类
}
private boolean inRangeOfView(View view, MotionEvent ev) {
//对触控屏幕操作
int []location = new int[2];
//获取x-y位置
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())) {
return false;
}
//超出范围时返回false
return true;
}
private void initResources() {
//初始化过程
mHeadViewPanel = findViewById(R.id.note_title);
//找到标题栏
mNoteHeaderHolder = new HeadViewHolder();
//实例化HeadViewHolder,初始化
mNoteHeaderHolder.tvModified = (TextView) findViewById(R.id.tv_modified_date);
//日期
mNoteHeaderHolder.ivAlertIcon = (ImageView) findViewById(R.id.iv_alert_icon);
//闹钟提醒图标
mNoteHeaderHolder.tvAlertDate = (TextView) findViewById(R.id.tv_alert_date);
//提醒日期
mNoteHeaderHolder.ibSetBgColor = (ImageView) findViewById(R.id.btn_set_bg_color);
//设置背景颜色按钮
mNoteHeaderHolder.ibSetBgColor.setOnClickListener(this);
//设置监听器
mNoteEditor = (EditText) findViewById(R.id.note_edit_view);
//文本编辑初始化
mNoteEditorPanel = findViewById(R.id.sv_note_edit);
//便签编辑器
mNoteBgColorSelector = findViewById(R.id.note_bg_color_selector);
//便签背景选择
for (int id : sBgSelectorBtnsMap.keySet()) {
ImageView iv = (ImageView) findViewById(id);
iv.setOnClickListener(this);
}
//为背景颜色选择界面的每一个按钮设置相对应的资源文件,并且设置了点击事件监听器
mFontSizeSelector = findViewById(R.id.font_size_selector);
//文字大小选择器
for (int id : sFontSizeBtnsMap.keySet()) {
View view = findViewById(id);
view.setOnClickListener(this);
};
//为文字大小选择界面的每一个按钮设置相对应的资源文件,并且设置了点击事件监听器
mSharedPrefs = PreferenceManager.getDefaultSharedPreferences(this);
mFontSizeId = mSharedPrefs.getInt(PREFERENCE_FONT_SIZE, ResourceParser.BG_DEFAULT_FONT_SIZE);
//默认大小字体
/**
* HACKME: 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);
////便签文本布局列表
}
@Override
protected void onPause() {
super.onPause();
if(saveNote()) {
//保存标签
Log.d(TAG, "Note data was saved with length:" + mWorkingNote.getContent().length());
//暂停时即进行便签的存储,记录log文件
}
clearSettingState();
//清除状态设置
}
private void updateWidget() {
//同步桌面挂件
Intent intent = new Intent(AppWidgetManager.ACTION_APPWIDGET_UPDATE);
//实例化Intent用来AppWidgetManager进行message传输
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;
}
//判断当前工作中的便签的桌面挂件的类型,并根据不同类型的桌面挂件指定Intent不同的目的组件
intent.putExtra(AppWidgetManager.EXTRA_APPWIDGET_IDS, new int[] {
//将桌面挂件的标识作为附加信息添加到Intent中
mWorkingNote.getWidgetId()
//广播Intent
});
sendBroadcast(intent);
//发送出去
setResult(RESULT_OK, intent);
//设置当前Activity结束后将结束信息发送给其父活动回复相关有用的信息
}
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);
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);
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);
//MenuInflater是用来实例化Menu目录下的Menu布局文件的
} else {
getMenuInflater().inflate(R.menu.note_edit, menu);
}
if (mWorkingNote.getCheckListMode() == TextNote.MODE_CHECK_LIST) {
menu.findItem(R.id.menu_list_mode).setTitle(R.string.menu_normal_mode);
} else {
menu.findItem(R.id.menu_list_mode).setTitle(R.string.menu_list_mode);
}
if (mWorkingNote.hasClockAlert()) {
menu.findItem(R.id.menu_alert).setVisible(false);
} else {
menu.findItem(R.id.menu_delete_remind).setVisible(false);
}
return true;
}
@Override
/*
* 函数功能:动态改变菜单选项内容
* 函数实现:如下注释
*/
public boolean onOptionsItemSelected(MenuItem item) {
switch (item.getItemId()) {
//根据菜单的id来编剧相关项目
case R.id.menu_new_note:
//创建一个新的便签
createNewNote();
break;
case R.id.menu_delete:
//删除便签
AlertDialog.Builder builder = new AlertDialog.Builder(this);
//创建关于删除操作的对话框
builder.setTitle(getString(R.string.alert_title_delete));
// 设置标签的标题为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();
}
});
//添加“YES”按钮
builder.setNegativeButton(android.R.string.cancel, null);
//添加“NO”的按钮
builder.show();
//显示对话框
break;
case R.id.menu_font_size:
//字体大小的编辑
mFontSizeSelector.setVisibility(View.VISIBLE);
// 将字体选择器置为可见
findViewById(sFontSelectorSelectionMap.get(mFontSizeId)).setVisibility(View.VISIBLE);
// 通过id找到相应的大小
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());
// 用sendto函数将运行文本发送到遍历的本文内
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链接选项
intent.putExtra(Intent.EXTRA_TEXT, info);
//将需要传递的便签信息放入text文件中
intent.setType("text/plain");
//编辑连接器的类型
context.startActivity(intent);
//在action中进行链接
}
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());
//将运行便签的id添加到INTENT_EXTRA_FOLDER_ID标记中
startActivity(intent);
//开始activity并链接
}
private void deleteCurrentNote() {
if (mWorkingNote.existInDatabase()) {
//假如当前运行的便签内存有数据
HashSet<Long> ids = new HashSet<Long>();
long id = mWorkingNote.getNoteId();
if (id != Notes.ID_ROOT_FOLDER) {
ids.add(id);
//如果不是头文件夹建立一个hash表把便签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);
//将这些标签的删除标记置为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()));
//若有有运行的便签就是建立一个链接器将标签id都存在uri中
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发生改变触发事件
public void onEditTextDelete(int index, String text) {
int childCount = mEditTextList.getChildCount();
if (childCount == 1) {
return;
}
//是否有编辑框出现,没有就返回
for (int i = index + 1; i < childCount; i++) {
((NoteEditText) mEditTextList.getChildAt(i).findViewById(R.id.et_edit_text))
.setIndex(i - 1);
}
//用ID把编辑框存在便签编辑框中
mEditTextList.removeViewAt(index);
//删除视图
NoteEditText edit = null;
if(index == 0) {
edit = (NoteEditText) mEditTextList.getChildAt(0).findViewById(
R.id.et_edit_text);
//如果原来编辑的索引值为0则编辑当前行否则编辑上一行
} else {
edit = (NoteEditText) mEditTextList.getChildAt(index - 1).findViewById(
R.id.et_edit_text);
//index不为0则将edit定位到index-1对应的EditText项
}
int length = edit.length();
//获取文本编辑长度
edit.append(text);
//添加
edit.requestFocus();
//优先
edit.setSelection(length);
//定位
}
public void onEditTextEnter(int index, String text) {
/**
* 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);
//建立一个状态机检查Pattern并进行匹配
int start = 0;
while (m.find(start)) {
spannable.setSpan(
new BackgroundColorSpan(this.getResources().getColor(
R.color.user_query_highlight)), m.start(), m.end(),
Spannable.SPAN_INCLUSIVE_EXCLUSIVE);
//设置背景颜色
start = m.end();
//跟新起始位置
}
}
return spannable;
}
//获取列表
private View getListItem(String item, int index) {
View view = LayoutInflater.from(this).inflate(R.layout.note_edit_list_item, null);
//创建视图
final NoteEditText edit = (NoteEditText) view.findViewById(R.id.et_edit_text);
edit.setTextAppearance(this, TextAppearanceResources.getTexAppearanceResource(mFontSizeId));
//创建文本框,设置可见性
CheckBox cb = ((CheckBox) view.findViewById(R.id.cb_edit_item));
//实例化CheckBox组件打勾
cb.setOnCheckedChangeListener(new OnCheckedChangeListener() {
public void onCheckedChanged(CompoundButton buttonView, boolean isChecked) {
if (isChecked) {
edit.setPaintFlags(edit.getPaintFlags() | Paint.STRIKE_THRU_TEXT_FLAG);
} else {
edit.setPaintFlags(Paint.ANTI_ALIAS_FLAG | Paint.DEV_KERN_TEXT_FLAG);
}
}
//建立一个打勾框,设置监听器
});
if (item.startsWith(TAG_CHECKED)) {
//勾选
cb.setChecked(true);
//true
edit.setPaintFlags(edit.getPaintFlags() | Paint.STRIKE_THRU_TEXT_FLAG);
item = item.substring(TAG_CHECKED.length(), item.length()).trim();
//去掉TAG_CHECKED和空格
} else if (item.startsWith(TAG_UNCHECKED)) {
//不勾选
cb.setChecked(false);
//false
edit.setPaintFlags(Paint.ANTI_ALIAS_FLAG | Paint.DEV_KERN_TEXT_FLAG);
item = item.substring(TAG_UNCHECKED.length(), item.length()).trim();
//去掉TAG_UNCHECKED和空格
}
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;
//初始化check标记
if (mWorkingNote.getCheckListMode() == TextNote.MODE_CHECK_LIST) {
// 若模式为CHECK_LIST
StringBuilder sb = new StringBuilder();
//创建可变字符串
for (int i = 0; i < mEditTextList.getChildCount(); i++) {
View view = mEditTextList.getChildAt(i);
//遍历所有子编辑框的视图
NoteEditText edit = (NoteEditText) view.findViewById(R.id.et_edit_text);
if (!TextUtils.isEmpty(edit.getText())) {
//若文本不为空
if (((CheckBox) view.findViewById(R.id.cb_edit_item)).isChecked()) {
//该选项框已打钩
sb.append(TAG_CHECKED).append(" ").append(edit.getText()).append("\n");
hasChecked = true;
//扩展字符串为已打钩并把标记置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);
}
//为空报错
}
private String makeShortcutIconTitle(String content) {
content = content.replace(TAG_CHECKED, "");
content = content.replace(TAG_UNCHECKED, "");
return content.length() > SHORTCUT_ICON_TITLE_MAX_LEN ? content.substring(0,
SHORTCUT_ICON_TITLE_MAX_LEN) : content;
}//编辑小图标标题
private void showToast(int resId) {
showToast(resId, Toast.LENGTH_SHORT);
}//显示提示视图
private void showToast(int resId, int duration) {
Toast.makeText(this, resId, duration).show();
}//持续显示
}