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.
GPL_Project/doc/标注/210340062_刘瑞轩/NoteEditActivity.java

874 lines
43 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 {
sBgSelectorBtnsMap.put(R.id.iv_bg_yellow, ResourceParser.YELLOW);
sBgSelectorBtnsMap.put(R.id.iv_bg_red, ResourceParser.RED);
sBgSelectorBtnsMap.put(R.id.iv_bg_blue, ResourceParser.BLUE);
sBgSelectorBtnsMap.put(R.id.iv_bg_green, ResourceParser.GREEN);
sBgSelectorBtnsMap.put(R.id.iv_bg_white, ResourceParser.WHITE);
}
private static final Map<Integer, Integer> sBgSelectorSelectionMap = new HashMap<Integer, Integer>();//将映射的颜色在反射为小米便签对应颜色的一个值
static {
sBgSelectorSelectionMap.put(ResourceParser.YELLOW, R.id.iv_bg_yellow_select);
sBgSelectorSelectionMap.put(ResourceParser.RED, R.id.iv_bg_red_select);
sBgSelectorSelectionMap.put(ResourceParser.BLUE, R.id.iv_bg_blue_select);
sBgSelectorSelectionMap.put(ResourceParser.GREEN, R.id.iv_bg_green_select);
sBgSelectorSelectionMap.put(ResourceParser.WHITE, R.id.iv_bg_white_select);
}
private static final Map<Integer, Integer> sFontSizeBtnsMap = new HashMap<Integer, Integer>();//将字体大小映射为一个值
static {
sFontSizeBtnsMap.put(R.id.ll_font_large, ResourceParser.TEXT_LARGE);
sFontSizeBtnsMap.put(R.id.ll_font_small, ResourceParser.TEXT_SMALL);
sFontSizeBtnsMap.put(R.id.ll_font_normal, ResourceParser.TEXT_MEDIUM);
sFontSizeBtnsMap.put(R.id.ll_font_super, ResourceParser.TEXT_SUPER);
}
private static final Map<Integer, Integer> sFontSelectorSelectionMap = new HashMap<Integer, Integer>();//将映射的值重新反射为字体大小对应的值
static {
sFontSelectorSelectionMap.put(ResourceParser.TEXT_LARGE, R.id.iv_large_select);
sFontSelectorSelectionMap.put(ResourceParser.TEXT_SMALL, R.id.iv_small_select);
sFontSelectorSelectionMap.put(ResourceParser.TEXT_MEDIUM, R.id.iv_medium_select);
sFontSelectorSelectionMap.put(ResourceParser.TEXT_SUPER, R.id.iv_super_select);
}
private static final String TAG = "NoteEditActivity";
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);//将布局文件node_edit加载到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) {//回复Activities的状态
super.onRestoreInstanceState(savedInstanceState);
if (savedInstanceState != null && savedInstanceState.containsKey(Intent.EXTRA_UID)) {//是否包含Intent.EXTRA_UID如果包含则重新恢复
Intent intent = new Intent(Intent.ACTION_VIEW);//创建新的对象并显示出来
intent.putExtra(Intent.EXTRA_UID, savedInstanceState.getLong(Intent.EXTRA_UID));//把之前的数据加载到该intent上面来
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())) {
long noteId = intent.getLongExtra(Intent.EXTRA_UID, 0);//获取intent所携带的附加参数
mUserQuery = "";
/**
* Starting from the searched result
*/
if (intent.hasExtra(SearchManager.EXTRA_DATA_KEY)) {//是否有额外的数据,有的话代表用户想要查看这一个便签
noteId = Long.parseLong(intent.getStringExtra(SearchManager.EXTRA_DATA_KEY));//取出来对应的id以便于之后数据库搜索
mUserQuery = intent.getStringExtra(SearchManager.USER_QUERY);//将mUserQuery设置为用户输入的查询的字符串
}
if (!DataUtils.visibleInNoteDatabase(getContentResolver(), noteId, Notes.TYPE_NOTE)) {//这个便签不能在数据库中找到
Intent jump = new Intent(this, NotesListActivity.class);//跳转到对应NoteListActivities上
startActivity(jump);//跳转
showToast(R.string.error_note_not_exist);//输出这个便签不存在
finish();//关闭页面
return false;
} else {
mWorkingNote = WorkingNote.load(this, noteId);//加载对应的便签
if (mWorkingNote == null) {//加载失败会记录当前的Activity
Log.e(TAG, "load note failed with note id" + noteId);//输出打开失败
finish();
return false;
}
}
getWindow().setSoftInputMode(//设置窗口为软件盘模式,窗口会自动调整布局大小以适应软键盘的显示或隐藏
WindowManager.LayoutParams.SOFT_INPUT_STATE_HIDDEN//此标志表示启动该 Activity 时软键盘不会自动弹出
| WindowManager.LayoutParams.SOFT_INPUT_ADJUST_RESIZE);//Activity 中的窗口大小会根据软键盘的状态自动调整
} else if(TextUtils.equals(Intent.ACTION_INSERT_OR_EDIT, intent.getAction())) {//如果是增添或者编辑
// New note
long folderId = intent.getLongExtra(Notes.INTENT_EXTRA_FOLDER_ID, 0);//获取FolderId
int widgetId = intent.getIntExtra(Notes.INTENT_EXTRA_WIDGET_ID,//获取传入的组件Id如果没有找到对应的组件Id那么就会返回无效的ID
AppWidgetManager.INVALID_APPWIDGET_ID);
int widgetType = intent.getIntExtra(Notes.INTENT_EXTRA_WIDGET_TYPE,//获取组件的类型
Notes.TYPE_WIDGET_INVALIDE);
int bgResId = intent.getIntExtra(Notes.INTENT_EXTRA_BACKGROUND_ID,//获取组件的背景Id如果没有就默认的背景颜色
ResourceParser.getDefaultBgId(this));
// 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);
}
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);//找到当前键的值也就是id并隐藏背景选择器选中的背景图
}
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();
}
outState.putLong(Intent.EXTRA_UID, mWorkingNote.getNoteId());//初始化
Log.d(TAG, "Save working note id: " + mWorkingNote.getNoteId() + " onSaveInstanceState");//显示对应id信息和保存成功
}
@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];//获取位置
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;
}//ev.getX获取了触摸事件的横坐标getY则是获取纵坐标该判断语句就是判断触摸事件的横纵坐标在该视图的外部
return true;//在管辖范畴内
}
private void initResources() {
mHeadViewPanel = findViewById(R.id.note_title);//获取对应id的视图
mNoteHeaderHolder = new HeadViewHolder();//实例化头部信息
mNoteHeaderHolder.tvModified = (TextView) findViewById(R.id.tv_modified_date);//把修改日期视图封装在mNoteHeaderHolder中
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);//将背景颜色选择器捆绑给mNoteBgColorSelector中
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()) {//如果设置的字体大小的id超过了限制那么就重新调回默认的字体大小
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());
}//提示已保存对应文本
clearSettingState();
}
private void updateWidget() {//更新widget
Intent intent = new Intent(AppWidgetManager.ACTION_APPWIDGET_UPDATE);
if (mWorkingNote.getWidgetType() == Notes.TYPE_WIDGET_2X) {//更新到2x大小
intent.setClass(this, NoteWidgetProvider_2x.class);
} else if (mWorkingNote.getWidgetType() == Notes.TYPE_WIDGET_4X) {//更新到4x大小
intent.setClass(this, NoteWidgetProvider_4x.class);
} else {
Log.e(TAG, "Unspported widget type");
return;
}
intent.putExtra(AppWidgetManager.EXTRA_APPWIDGET_IDS, new int[] {//把对应id附加上去
mWorkingNote.getWidgetId()
});
sendBroadcast(intent);//让widget进行刷新操作从而实现数据同步
setResult(RESULT_OK, intent);
}
public void onClick(View v) {
int id = v.getId();//获取id号
if (id == R.id.btn_set_bg_color) {//如果设置背景颜色
mNoteBgColorSelector.setVisibility(View.VISIBLE);//将改变颜色的视图显示出来
findViewById(sBgSelectorSelectionMap.get(mWorkingNote.getBgColorId())).setVisibility(
View.VISIBLE);
} else if (sBgSelectorBtnsMap.containsKey(id)) {//如果是背景选择按钮包含此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);//找到对应颜色的id并进行视图显示
mNoteEditorPanel.setBackgroundResource(mWorkingNote.getBgColorResId());//编辑面板进行显示
mHeadViewPanel.setBackgroundResource(mWorkingNote.getTitleBgResId());//试图面板显示
}
@Override
public boolean onPrepareOptionsMenu(Menu menu) {
if (isFinishing()) {//页面完成
return true;
}
clearSettingState();//关闭设置的状态
menu.clear();//清空菜单
if (mWorkingNote.getFolderId() == Notes.ID_CALL_RECORD_FOLDER) {
getMenuInflater().inflate(R.menu.call_note_edit, menu);//进行菜单的填充
} else {
getMenuInflater().inflate(R.menu.note_edit, menu);
}
if (mWorkingNote.getCheckListMode() == TextNote.MODE_CHECK_LIST) {
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));//提醒是否要删除
builder.setIcon(android.R.drawable.ic_dialog_alert);//设置删除的图标
builder.setMessage(getString(R.string.alert_message_delete_note));//提示要不要删除
builder.setPositiveButton(android.R.string.ok,
new DialogInterface.OnClickListener() {//确定按钮
public void onClick(DialogInterface dialog, int which) {
deleteCurrentNote();//删除当前的便签
finish();
}
});
builder.setNegativeButton(android.R.string.cancel, null);//不确定
builder.show();//继续展示对应界面
break;
case R.id.menu_font_size://字体大小菜单
mFontSizeSelector.setVisibility(View.VISIBLE);//显示字体选择器
findViewById(sFontSelectorSelectionMap.get(mFontSizeId)).setVisibility(View.VISIBLE);//将字体大小选择器显示出来
break;
case R.id.menu_list_mode:
mWorkingNote.setCheckListMode(mWorkingNote.getCheckListMode() == 0 ?
TextNote.MODE_CHECK_LIST : 0);//设置列表模式
break;
case R.id.menu_share://进行菜单分享
getWorkingText();//获取便签的内容
sendTo(this, mWorkingNote.getContent());
break;
case R.id.menu_send_to_desktop:
sendToDesktop();//发送到桌面上
break;
case R.id.menu_alert:
setReminder();//设置提醒器
break;
case R.id.menu_delete_remind:
mWorkingNote.setAlertDate(0, false);//设置日期提醒器
break;
default:
break;
}
return true;
}
private void setReminder() {//设置提醒器
DateTimePickerDialog d = new DateTimePickerDialog(this, System.currentTimeMillis());//设置新的对话框
d.setOnDateTimeSetListener(new OnDateTimeSetListener() {//设置监听器
public void OnDateTimeSet(AlertDialog dialog, long date) {
mWorkingNote.setAlertDate(date , true);//设置信息
}
});
d.show();
}
/**
* Share note to apps that support {@link Intent#ACTION_SEND} action
* and {@text/plain} type
*/
private void sendTo(Context context, String info) {
Intent intent = new Intent(Intent.ACTION_SEND);//代表发送一个文本信息
intent.putExtra(Intent.EXTRA_TEXT, info);//向intend中加入文本信息
intent.setType("text/plain");//文本类型
context.startActivity(intent);//启动这个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
intent.setAction(Intent.ACTION_INSERT_OR_EDIT);//设置为编辑或者插入
intent.putExtra(Notes.INTENT_EXTRA_FOLDER_ID, mWorkingNote.getFolderId());//给id
startActivity(intent);//进行相应的编辑活动
}
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);//加入
} else {
Log.d(TAG, "Wrong note id, should not happen");//不存在这样的便签id
}
if (!isSyncMode()) {//不是在同步模式下
if (!DataUtils.batchDeleteNotes(getContentResolver(), ids)) {//批量删除失败
Log.e(TAG, "Delete Note error");
}
} else {
if (!DataUtils.batchMoveToFolder(getContentResolver(), ids, Notes.ID_TRASH_FOLER)) {//无法移动到垃圾箱
Log.e(TAG, "Move notes to trash folder error, should not happens");
}
}
}
mWorkingNote.markDeleted(true);//标记为已经删除
}
private boolean isSyncMode() {
return NotesPreferenceActivity.getSyncAccountName(this).trim().length() > 0;//获取账号名称,如果不是空则代表是同步模式下
}
public void onClockAlertChanged(long date, boolean set) {
/**
* User could set clock to an unsaved note, so before setting the
* alert clock, we should save the note first
*/
if (!mWorkingNote.existInDatabase()) {//如果不在数据库中
saveNote();//保存
}
if (mWorkingNote.getNoteId() > 0) {//如果id为有效id
Intent intent = new Intent(this, AlarmReceiver.class);//新建intent
intent.setData(ContentUris.withAppendedId(Notes.CONTENT_NOTE_URI, mWorkingNote.getNoteId()));//设置对应的日期,和对应的信息
PendingIntent pendingIntent = PendingIntent.getBroadcast(this, 0, intent, 0);//设置触发intent
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) {
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);//将index后的文本往前移动
}
mEditTextList.removeViewAt(index);//把对应下标的列表内容删除
NoteEditText edit = null;
if(index == 0) {//如果下标是0那么就获取第一个标签
edit = (NoteEditText) mEditTextList.getChildAt(0).findViewById(
R.id.et_edit_text);
} else {//否则就是下标减一,因为删除了一个元素
edit = (NoteEditText) mEditTextList.getChildAt(index - 1).findViewById(
R.id.et_edit_text);
}
int length = edit.length();
edit.append(text);//把对应的文本内容加入
edit.requestFocus();//获取焦点
edit.setSelection(length);//设置光标到对应的长度
}
public void onEditTextEnter(int index, String text) {
/**
* 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的索引值和列表元素的实际位置一致
((NoteEditText) mEditTextList.getChildAt(i).findViewById(R.id.et_edit_text))
.setIndex(i);
}
}
private void switchToListMode(String text) {
mEditTextList.removeAllViews();//关掉所有的视图
String[] items = text.split("\n");//将对应的文本据回车分开
int index = 0;
for (String item : items) {//遍历里面的元素
if(!TextUtils.isEmpty(item)) {//如果对应文本不是空
mEditTextList.addView(getListItem(item, index));//增加一个视图,并标上下标
index++;//到达下一个
}
}
mEditTextList.addView(getListItem("", index));//用于添加新的列表项目
mEditTextList.getChildAt(index).findViewById(R.id.et_edit_text).requestFocus();//请求焦点
mNoteEditor.setVisibility(View.GONE);//将便签编辑器取消
mEditTextList.setVisibility(View.VISIBLE);//将便文本列表显示出来
}
private Spannable getHighlightQueryResult(String fullText, String userQuery) {//将搜索到的关键字通过高亮显示出来
SpannableString spannable = new SpannableString(fullText == null ? "" : fullText);
if (!TextUtils.isEmpty(userQuery)) {//判断用户查询的内容是否为空
mPattern = Pattern.compile(userQuery);//将对应得文本转化为匹配模式
Matcher m = mPattern.matcher(fullText);//把对应得文本匹配出来
int start = 0;//定义开始
while (m.find(start)) {//到达下一个位置
spannable.setSpan(
new BackgroundColorSpan(this.getResources().getColor(
R.color.user_query_highlight)), m.start(), m.end(),
Spannable.SPAN_INCLUSIVE_EXCLUSIVE);//设置对应得高亮模式
start = m.end();//更新扫描得开始位置
}
}
return spannable;
}
private View getListItem(String item, int index) {
View view = LayoutInflater.from(this).inflate(R.layout.note_edit_list_item, null);//获取布局解析器对象
final NoteEditText edit = (NoteEditText) view.findViewById(R.id.et_edit_text);//找到对应id得控件对象
edit.setTextAppearance(this, TextAppearanceResources.getTexAppearanceResource(mFontSizeId));//设置文本形式
CheckBox cb = ((CheckBox) view.findViewById(R.id.cb_edit_item));//根据id找到对应的多选框
cb.setOnCheckedChangeListener(new OnCheckedChangeListener() {//监听复选框的变化
public void onCheckedChanged(CompoundButton buttonView, boolean isChecked) {
if (isChecked) {//勾选了对应的复选框
edit.setPaintFlags(edit.getPaintFlags() | Paint.STRIKE_THRU_TEXT_FLAG);//加上对应的删除线
} else {//取消了勾选
edit.setPaintFlags(Paint.ANTI_ALIAS_FLAG | Paint.DEV_KERN_TEXT_FLAG);//删除对应的删除线
}
}
});
if (item.startsWith(TAG_CHECKED)) {//如果该列表项已经被勾选了
cb.setChecked(true);//设置为选中状态
edit.setPaintFlags(edit.getPaintFlags() | Paint.STRIKE_THRU_TEXT_FLAG);//
item = item.substring(TAG_CHECKED.length(), item.length()).trim();//修剪掉对应的空格
} else if (item.startsWith(TAG_UNCHECKED)) {
cb.setChecked(false);
edit.setPaintFlags(Paint.ANTI_ALIAS_FLAG | Paint.DEV_KERN_TEXT_FLAG);//未勾选状态
item = item.substring(TAG_UNCHECKED.length(), item.length()).trim();//去除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;
if (mWorkingNote.getCheckListMode() == TextNote.MODE_CHECK_LIST) {//如果是勾选列表模式
StringBuilder sb = new StringBuilder();
for (int i = 0; i < mEditTextList.getChildCount(); i++) {
View view = mEditTextList.getChildAt(i);//获取对应构件
NoteEditText edit = (NoteEditText) view.findViewById(R.id.et_edit_text);
if (!TextUtils.isEmpty(edit.getText())) {//获取输入框的内容
if (((CheckBox) view.findViewById(R.id.cb_edit_item)).isChecked()) {//如果当前的遍历的多选框被勾选了
sb.append(TAG_CHECKED).append(" ").append(edit.getText()).append("\n");//将该信息加入到sb中
hasChecked = true;
} else {
sb.append(TAG_UNCHECKED).append(" ").append(edit.getText()).append("\n");//没有勾选
}
}
}
mWorkingNote.setWorkingText(sb.toString());//将其转化为String类
} 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);//表示点击桌面快捷方式需要启动的activity
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();//在指定时间内显示对应的信息
}
}