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.
Notes/src/ui/NoteEditActivity.java

876 lines
41 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);
}
//上下是反的为啥构建双向印射关系方便根据颜色值获取对应的选择按钮ID比如当前背景是红的红的按钮就是按下去的或者根据选择按钮ID获取对应的颜色值
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);
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) {//存储uid便于恢复活动状态时调用
super.onRestoreInstanceState(savedInstanceState); //使用 Intent Extra 在组件之间传递uid
if (savedInstanceState != null && savedInstanceState.containsKey(Intent.EXTRA_UID)) {
Intent intent = new Intent(Intent.ACTION_VIEW); //创建一个意图,用于显示笔记详情,大小是action_view显示数据功能
intent.putExtra(Intent.EXTRA_UID, savedInstanceState.getLong(Intent.EXTRA_UID));//将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())) {//如果意图是查看操作
long noteId = intent.getLongExtra(Intent.EXTRA_UID, 0);//获取uid
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);//解析出用户查询
}
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 {
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
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);//获取小部件类型
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);//获取电话号码
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;//初始化笔记id
if ((noteId = DataUtils.getNoteIdByPhoneNumberAndCallDate(getContentResolver(),
phoneNumber, callDate)) > 0) {//如果记录中根据电话号码和调用日期查询到笔记id
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);//将笔记转换为call笔记
}
} 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() {//已经oncreate后切出再次调用时初始化笔记界面
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) {//处理新的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");
}
@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];//获取视图在屏幕上的坐标长度为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;
}
return true;
}
private void initResources() {//初始化资源
mHeadViewPanel = findViewById(R.id.note_title);
mNoteHeaderHolder = new HeadViewHolder();
mNoteHeaderHolder.tvModified = (TextView) findViewById(R.id.tv_modified_date);
mNoteHeaderHolder.ivAlertIcon = (ImageView) findViewById(R.id.iv_alert_icon);
mNoteHeaderHolder.tvAlertDate = (TextView) findViewById(R.id.tv_alert_date);
mNoteHeaderHolder.ibSetBgColor = (ImageView) findViewById(R.id.btn_set_bg_color);
mNoteHeaderHolder.ibSetBgColor.setOnClickListener(this);
mNoteEditor = (EditText) findViewById(R.id.note_edit_view);
mNoteEditorPanel = findViewById(R.id.sv_note_edit);
mNoteBgColorSelector = findViewById(R.id.note_bg_color_selector);
for (int id : sBgSelectorBtnsMap.keySet()) {
ImageView iv = (ImageView) findViewById(id);
iv.setOnClickListener(this);
}
mFontSizeSelector = findViewById(R.id.font_size_selector);
for (int id : sFontSizeBtnsMap.keySet()) {
View view = findViewById(id);
view.setOnClickListener(this);
};
mSharedPrefs = PreferenceManager.getDefaultSharedPreferences(this);
mFontSizeId = mSharedPrefs.getInt(PREFERENCE_FONT_SIZE, ResourceParser.BG_DEFAULT_FONT_SIZE);
/**
* 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();//调用父类的onPause方法
if(saveNote()) {
Log.d(TAG, "Note data was saved with length:" + mWorkingNote.getContent().length());
}
clearSettingState();//清除设置状态
}
private void updateWidget() {//更新应用小部件
Intent intent = new Intent(AppWidgetManager.ACTION_APPWIDGET_UPDATE);//接受意图
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.putExtra(AppWidgetManager.EXTRA_APPWIDGET_IDS, new int[] {//添加应用小部件id
mWorkingNote.getWidgetId()
});
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);
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);//如果是通话记录文件夹, 通话记录转化为菜单
} 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()) {
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);//再intetn中添加标签为EXTRA_TEXT 值为info
intent.setType("text/plain");
context.startActivity(intent);
}
private void createNewNote() {
// Firstly, save current editing notes
saveNote();
// For safety, start a new NoteEditActivity
finish();
Intent intent = new Intent(this, NoteEditActivity.class);
intent.setAction(Intent.ACTION_INSERT_OR_EDIT);
intent.putExtra(Notes.INTENT_EXTRA_FOLDER_ID, mWorkingNote.getFolderId());
startActivity(intent);
}
private void deleteCurrentNote() {
if (mWorkingNote.existInDatabase()) {
HashSet<Long> ids = new HashSet<Long>();
long id = mWorkingNote.getNoteId();
if (id != Notes.ID_ROOT_FOLDER) {//如果不是根文件夹
ids.add(id);//将笔记id添加到集合中
} else {
Log.d(TAG, "Wrong note id, should not happen");
}
if (!isSyncMode()) {//如果不是同步模式
if (!DataUtils.batchDeleteNotes(getContentResolver(), ids)) {//批量删除笔记
Log.e(TAG, "Delete Note error");
}
} else {
if (!DataUtils.batchMoveToFolder(getContentResolver(), ids, Notes.ID_TRASH_FOLER)) {//将笔记移动到回收站文件夹
Log.e(TAG, "Move notes to trash folder error, should not happens");
}
}
}
mWorkingNote.markDeleted(true);
}
private boolean isSyncMode() {
return NotesPreferenceActivity.getSyncAccountName(this).trim().length() > 0;
}
public void onClockAlertChanged(long date, boolean set) {//设置提醒时间
/**
* User could set clock to an unsaved note, so before setting the
* alert clock, we should save the note first
*/
if (!mWorkingNote.existInDatabase()) {
saveNote();
}
if (mWorkingNote.getNoteId() > 0) {
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) {//处理清单模式下删除列表项 的操作
int childCount = mEditTextList.getChildCount();//获取编辑框的子视图数量
if (childCount == 1) {
return;
}
for (int i = index + 1; i < childCount; i++) {//遍历删除项index处之后的所有列表项 将它们的索引减1不会影响到后续的列表项的索引从而避免了索引错乱的问题
((NoteEditText) mEditTextList.getChildAt(i).findViewById(R.id.et_edit_text))
.setIndex(i - 1);
}
mEditTextList.removeViewAt(index);//删除index处的子图
//亮点:小米笔记清单模式下的特殊设计 ,属于防误删的安全机制 ,主要为了保护用户内容,这是啥意思?
NoteEditText edit = null;
if(index == 0) {
edit = (NoteEditText) mEditTextList.getChildAt(0).findViewById(
R.id.et_edit_text);
} else {
edit = (NoteEditText) mEditTextList.getChildAt(index - 1).findViewById(
R.id.et_edit_text);//
}
int length = edit.length();//获取删除项index处之前的列表项的文本长度
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);//将新的列表项视图添加到编辑框中位置为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
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) {//高亮显示用户查询的文本这里用了spannable是比较好的视图管理器
SpannableString spannable = new SpannableString(fullText == null ? "" : fullText);//存储高亮显示的文本
if (!TextUtils.isEmpty(userQuery)) {
mPattern = Pattern.compile(userQuery, Pattern.CASE_INSENSITIVE);//编译正则表达式
Matcher m = mPattern.matcher(fullText);//创建一个 Matcher 对象,用于对输入字符串进行匹配操作
int start = 0;
while (m.find(start)) { //构建span字符串
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)); //复选框用于让用户从有限数量的选项中选择一个或多个选项。
cb.setOnCheckedChangeListener(new OnCheckedChangeListener() {//复选框变化监听
public void onCheckedChanged(CompoundButton buttonView, boolean isChecked) {//
if (isChecked) {//PaintFlags是Android中用于控制文本绘制样式的位掩码 ,通过位运算组合多个绘制标志
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)) {//如果列表项以(TAG_CHECKED)开头,则将复选框设置为选中状态
cb.setChecked(true);
edit.setPaintFlags(edit.getPaintFlags() | Paint.STRIKE_THRU_TEXT_FLAG);//
item = item.substring(TAG_CHECKED.length(), item.length()).trim();//
} else if (item.startsWith(TAG_UNCHECKED)) {
cb.setChecked(false);
edit.setPaintFlags(Paint.ANTI_ALIAS_FLAG | Paint.DEV_KERN_TEXT_FLAG);//
item = item.substring(TAG_UNCHECKED.length(), item.length()).trim();
}
edit.setOnTextViewChangeListener(this);
edit.setIndex(index);
edit.setText(getHighlightQueryResult(item, mUserQuery));
return view;
}
public void onTextChange(int index, boolean hasText) {// 列表项文本变化事件
if (index >= mEditTextList.getChildCount()) {//检查索引是否超出范围
Log.e(TAG, "Wrong index, should not happen");
return;
}
if(hasText) {//如果列表项有文本
mEditTextList.getChildAt(index).findViewById(R.id.cb_edit_item).setVisibility(View.VISIBLE);
} else {
mEditTextList.getChildAt(index).findViewById(R.id.cb_edit_item).setVisibility(View.GONE);
}
}
public void onCheckListModeChanged(int oldMode, int newMode) {// 检查列表模式变化事件
if (newMode == TextNote.MODE_CHECK_LIST) {//如果切换到检查列表模式
switchToListMode(mNoteEditor.getText().toString());
} else {
if (!getWorkingText()) {
mWorkingNote.setWorkingText(mWorkingNote.getContent().replace(TAG_UNCHECKED + " ",
""));//如果没有选中项,则将所有未选中项的文本删除
}
mNoteEditor.setText(getHighlightQueryResult(mWorkingNote.getContent(), mUserQuery));
mEditTextList.setVisibility(View.GONE);
mNoteEditor.setVisibility(View.VISIBLE);
}
}
private boolean getWorkingText() {//获取选中项的文本
boolean hasChecked = false;
if (mWorkingNote.getCheckListMode() == TextNote.MODE_CHECK_LIST) {//如果是检查列表模式
StringBuilder sb = new StringBuilder();
for (int i = 0; i < mEditTextList.getChildCount(); i++) {//遍历所有列表项
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");//如果列表项选中,则将其文本添加到 StringBuilder 中
hasChecked = true;
} else {
sb.append(TAG_UNCHECKED).append(" ").append(edit.getText()).append("\n");//如果列表项未选中,则将其文本添加到 StringBuilder 中
}
}
}
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 消息
Toast.makeText(this, resId, duration).show();
}
}