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.
MiNote/micode/notes/ui/NoteEditActivity.java

1283 lines
47 KiB

This file contains ambiguous Unicode characters!

This file contains ambiguous Unicode characters that may be confused with others in your current locale. If your use case is intentional and legitimate, you can safely ignore this warning. Use the Escape button to highlight these characters.

/*
* Copyright (c) 2010-2011, The MiCode Open Source Community (www.micode.net)
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package net.micode.notes.ui;
import android.app.Activity;
import android.app.AlarmManager;
import android.app.AlertDialog;
import android.app.PendingIntent;
import android.app.SearchManager;
import android.appwidget.AppWidgetManager;
import android.content.ContentUris;
import android.content.Context;
import android.content.DialogInterface;
import android.content.Intent;
import android.content.SharedPreferences;
import android.graphics.Paint;
import android.os.Bundle;
import android.preference.PreferenceManager;
import android.text.Spannable;
import android.text.SpannableString;
import android.text.TextUtils;
import android.text.format.DateUtils;
import android.text.style.BackgroundColorSpan;
import android.util.Log;
import android.view.LayoutInflater;
import android.view.Menu;
import android.view.MenuItem;
import android.view.MotionEvent;
import android.view.View;
import android.view.View.OnClickListener;
import android.view.WindowManager;
import android.widget.CheckBox;
import android.widget.CompoundButton;
import android.widget.CompoundButton.OnCheckedChangeListener;
import android.widget.EditText;
import android.widget.ImageView;
import android.widget.LinearLayout;
import android.widget.TextView;
import android.widget.Toast;
import net.micode.notes.R;
import net.micode.notes.data.Notes;
import net.micode.notes.data.Notes.TextNote;
import net.micode.notes.model.WorkingNote;
import net.micode.notes.model.WorkingNote.NoteSettingChangedListener;
import net.micode.notes.tool.DataUtils;
import net.micode.notes.tool.ResourceParser;
import net.micode.notes.tool.ResourceParser.TextAppearanceResources;
import net.micode.notes.ui.DateTimePickerDialog.OnDateTimeSetListener;
import net.micode.notes.ui.NoteEditText.OnTextViewChangeListener;
import net.micode.notes.widget.NoteWidgetProvider_2x;
import net.micode.notes.widget.NoteWidgetProvider_4x;
import java.util.HashMap;
import java.util.HashSet;
import java.util.Map;
import java.util.regex.Matcher;
import java.util.regex.Pattern;
/**
* @projectName项目名称: xiaomi label
* @package: ui
* @className类名称: NoteEditActivity
* @description类描述: 该类主要是针对标签的编辑继承了系统内部许多和监听有关的类
* @author创建人: zhangchaoqun
* @createDate创建时间: datetime
* @updateUser修改人: user
* @updateDate修改时间: datetime
* @updateRemark修改备注: 说明本次修改内容
* @version版本: v1.0
*/
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 {
//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 {
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;
//私有化一个界面操作mHeadViewPanel对表头的操作
private View mNoteBgColorSelector;
//私有化一个界面操作mNoteBgColorSelector对背景颜色的操作
private View mFontSizeSelector;
//私有化一个界面操作mFontSizeSelector对标签字体的操作
private EditText mNoteEditor;
//声明编辑控件,对文本操作
private View mNoteEditorPanel;
//私有化一个界面操作mNoteEditorPanel文本编辑的控制板
private WorkingNote mWorkingNote;
//对模板WorkingNote初始化
private SharedPreferences mSharedPrefs;
//私有化SharedPreferences的数据存储方式本质是存储key-value键值对数据
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
*/
/**
* @description 描述:保存现场的函数,如果程序中止可以从恢复现场
* @param 参数1:savedInstanceState
* @param 参数2:
* @param 参数3:
* @return 返回值:void
* @author zhangchaoqun
*/
@Override
protected void onRestoreInstanceState(Bundle savedInstanceState) {
super.onRestoreInstanceState(savedInstanceState);
//当Bundle不为空时通过onRestoreInstanceState是恢复Activity的状态
if (savedInstanceState != null && savedInstanceState.containsKey(Intent.EXTRA_UID)) {
Intent intent = new Intent(Intent.ACTION_VIEW);
intent.putExtra(Intent.EXTRA_UID, savedInstanceState.getLong(Intent.EXTRA_UID));
if (!initActivityState(intent)) {
finish();
return;
}
Log.d(TAG, "Restoring from killed activity");
}
}
/**
* @description 描述:从Intent中通过initActivityState来初始化相关的数据
* @param 参数1:intent
* @param 参数2:
* @param 参数3:
* @return 返回值:bool是否初始化成功
* @author zhangchaoqun
*/
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;
/*
*intent.getAction()
*用于broadcast发送广播时给机制intent设置一个action就是一个字符串
* 用户可以通过receive接受intent通过 getAction得到的字符串来决定下一步
*/
if (TextUtils.equals(Intent.ACTION_VIEW, intent.getAction())) {
long noteId = intent.getLongExtra(Intent.EXTRA_UID, 0);
mUserQuery = "";
//用户实例化标签时未给出标签id
/**
* Starting from the searched result
*/
//根据键值查找ID
if (intent.hasExtra(SearchManager.EXTRA_DATA_KEY)) {
noteId = Long.parseLong(intent.getStringExtra(SearchManager.EXTRA_DATA_KEY));
mUserQuery = intent.getStringExtra(SearchManager.USER_QUERY);
}
//如果ID在数据库中未找到
if (!DataUtils.visibleInNoteDatabase(getContentResolver(), noteId, Notes.TYPE_NOTE)) {
Intent jump = new Intent(this, NotesListActivity.class);
//程序将跳转到上面声明的jump
startActivity(jump);
showToast(R.string.error_note_not_exist);
finish();
return false;
}
//ID在数据库中找到
else {
mWorkingNote = WorkingNote.load(this, noteId);
if (mWorkingNote == null) {
Log.e(TAG, "load note failed with note id" + noteId);
finish();
return false;
}
}
//setSoftInputMode(软键盘输入模式)
getWindow().setSoftInputMode(
WindowManager.LayoutParams.SOFT_INPUT_STATE_HIDDEN
| WindowManager.LayoutParams.SOFT_INPUT_ADJUST_RESIZE);
} else if(TextUtils.equals(Intent.ACTION_INSERT_OR_EDIT, intent.getAction())) {
// New note
long folderId = intent.getLongExtra(Notes.INTENT_EXTRA_FOLDER_ID, 0);
int widgetId = intent.getIntExtra(Notes.INTENT_EXTRA_WIDGET_ID,
AppWidgetManager.INVALID_APPWIDGET_ID);
int widgetType = intent.getIntExtra(Notes.INTENT_EXTRA_WIDGET_TYPE,
Notes.TYPE_WIDGET_INVALIDE);
int bgResId = intent.getIntExtra(Notes.INTENT_EXTRA_BACKGROUND_ID,
ResourceParser.getDefaultBgId(this));
//intent.getTypeExtra()对各个变量进行语法分析
// Parse call-record note
String phoneNumber = intent.getStringExtra(Intent.EXTRA_PHONE_NUMBER);
long callDate = intent.getLongExtra(Notes.INTENT_EXTRA_CALL_DATE, 0);
if (callDate != 0 && phoneNumber != null) {
if (TextUtils.isEmpty(phoneNumber)) {
Log.w(TAG, "The call record number is null");
}
long noteId = 0;
//通过电话号码和号码数据来查找noteId
if ((noteId = DataUtils.getNoteIdByPhoneNumberAndCallDate(getContentResolver(),
phoneNumber, callDate)) > 0) {
mWorkingNote = WorkingNote.load(this, noteId);
if (mWorkingNote == null) {
Log.e(TAG, "load call note failed with note id" + noteId);
finish();
return false;
}
} else {
//创建一个新的WorkingNote
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();
}
/**
* @description 描述:对界面的初始化操作
* @param 参数1:
* @param 参数2:
* @param 参数3:
* @return 返回值:void
* @author zhangchaoqun
*/
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();
}
/**
* @description 描述:设置闹钟显示
* @param 参数1:
* @param 参数2:
* @param 参数3:
* @return 返回值:void
* @author zhangchaoqun
*/
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");
}
@Override
public boolean dispatchTouchEvent(MotionEvent ev) {
//MotionEvent是触屏传递机制
//颜色选择器在屏幕上可见
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);
}
/**
* @description 描述:对屏幕触控的坐标进行操作
* @param 参数1:view
* @param 参数2:ev
* @param 参数3:
* @return 返回值:bool触控坐标是否合法
* @author zhangchaoqun
*/
private boolean inRangeOfView(View view, MotionEvent ev) {
int []location = new int[2];
view.getLocationOnScreen(location);
int x = location[0];
int y = location[1];
//如果触控的位置超出了给定的范围返回false
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();
if(saveNote()) {
Log.d(TAG, "Note data was saved with length:" + mWorkingNote.getContent().length());
}
clearSettingState();
}
/**
* @description 描述:更新同步桌面小挂件Widget
* @param 参数1:
* @param 参数2:
* @param 参数3:
* @return 返回值:void
* @author zhangchaoqun
*/
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[] {
mWorkingNote.getWidgetId()
});
sendBroadcast(intent);
setResult(RESULT_OK, intent);
}
/**
* @description 描述:处理点击
* @param 参数1:
* @param 参数2:
* @param 参数3:
* @return 返回值:void
* @author zhangchaoqun
*/
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());
}
/**
* @description 描述:准备选择菜单
* @param 参数1:menu
* @param 参数2:
* @param 参数3:
* @return 返回值:bool菜单是否准备好
* @author zhangchaoqun
*/
@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;
}
/**
* @description 描述:动态改变菜单选项内容
* @param 参数1:item
* @param 参数2:
* @param 参数3:
* @return 返回值:bool改变的结果
* @author zhangchaoqun
*/
@Override
public boolean onOptionsItemSelected(MenuItem item) {
//根据菜单的id来编辑相关项目
int itemId = item.getItemId();
//创建一个新的便签
if (itemId == R.id.menu_new_note) {
createNewNote();
}
//删除一个便签
else if (itemId == 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();
}
//字体大小的设置
else if (itemId == R.id.menu_font_size) {
//字体选择器设置为可见并通过id找字体大小
mFontSizeSelector.setVisibility(View.VISIBLE);
findViewById(sFontSelectorSelectionMap.get(mFontSizeId)).setVisibility(View.VISIBLE);
}
//选择列表模式
else if (itemId == R.id.menu_list_mode) {
mWorkingNote.setCheckListMode(mWorkingNote.getCheckListMode() == 0 ?
TextNote.MODE_CHECK_LIST : 0);
}
//菜单共享
else if (itemId == R.id.menu_share) {
getWorkingText();
//sendto函数将运行文本发送到遍历的本文内
sendTo(this, mWorkingNote.getContent());
}
//发送到桌面
else if (itemId == R.id.menu_send_to_desktop) {
sendToDesktop();
}
//创建提醒
else if (itemId == R.id.menu_alert) {
setReminder();
}
//删除提醒
else if (itemId == R.id.menu_delete_remind) {
mWorkingNote.setAlertDate(0, false);
}
return true;
}
/**
* @description 描述:建立时间提醒
* @param 参数1:
* @param 参数2:
* @param 参数3:
* @return 返回值:void
* @author zhangchaoqun
*/
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
*/
/**
* @description 描述:共享便签
* @param 参数1:context
* @param 参数2:info
* @param 参数3:
* @return 返回值:void
* @author zhangchaoqun
*/
private void sendTo(Context context, String info) {
//建立intent链接选项
Intent intent = new Intent(Intent.ACTION_SEND);
//将需要传递的便签信息放入text文件中
intent.putExtra(Intent.EXTRA_TEXT, info);
//设置连接器的类型
intent.setType("text/plain");
//在activity中进行链接
context.startActivity(intent);
}
/**
* @description 描述:创建一个新的便签
* @param 参数1:
* @param 参数2:
* @param 参数3:
* @return 返回值:void
* @author zhangchaoqun
*/
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);
//将运行便签的id添加到INTENT_EXTRA_FOLDER_ID标记中
intent.putExtra(Notes.INTENT_EXTRA_FOLDER_ID, mWorkingNote.getFolderId());
//开始activity并链接
startActivity(intent);
}
/**
* @description 描述:删除当前便签
* @param 参数1:
* @param 参数2:
* @param 参数3:
* @return 返回值:void
* @author zhangchaoqun
*/
private void deleteCurrentNote() {
//若当前便签中有数据
if (mWorkingNote.existInDatabase()) {
HashSet<Long> ids = new HashSet<Long>();
long id = mWorkingNote.getNoteId();
//如果不是根文件则建立一个hash表把便签id存起来
if (id != Notes.ID_ROOT_FOLDER) {
ids.add(id);
}
//否则报错
else {
Log.d(TAG, "Wrong note id, should not happen");
}
//如果是在非同步模式下
if (!isSyncMode()) {
//删除
if (!DataUtils.batchDeleteNotes(getContentResolver(), ids)) {
Log.e(TAG, "Delete Note error");
}
}
//在同步模式下
else {
//移动至垃圾文件夹中
if (!DataUtils.batchMoveToFolder(getContentResolver(), ids, Notes.ID_TRASH_FOLER)) {
Log.e(TAG, "Move notes to trash folder error, should not happens");
}
}
}
//便签的删除标记位置为true
mWorkingNote.markDeleted(true);
}
/**
* @description 描述:判断是否为同步模式看NotesPreferenceActivity的同步名称是否为空
* @param 参数1:
* @param 参数2:
* @param 参数3:
* @return 返回值:bool返回判断结果
* @author zhangchaoqun
*/
private boolean isSyncMode() {
return NotesPreferenceActivity.getSyncAccountName(this).trim().length() > 0;
}
/**
* @description 描述:设置提醒时间
* @param 参数1:date
* @param 参数2:set
* @param 参数3:
* @return 返回值:void
* @author zhangchaoqun
*/
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();
}
/*
*若有有运行的便签就是建立一个链接器将标签id都存在uri中
* 设置提醒管理器
* 如果用户设置了时间,就通过提醒管理器设置一个监听事项
*/
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);
}
}
//Widget发生改变时就更新
public void onWidgetChanged() {
updateWidget();
}
/**
* @description 描述:删除编辑文本框时所发生的动作
* @param 参数1:index
* @param 参数2:text
* @param 参数3:
* @return 返回值:void
* @author zhangchaoqun
*/
public void onEditTextDelete(int index, String text) {
int childCount = mEditTextList.getChildCount();
//没有编辑框的话直接返回
if (childCount == 1) {
return;
}
//通过id把编辑框存在便签编辑框中
for (int i = index + 1; i < childCount; i++) {
((NoteEditText) mEditTextList.getChildAt(i).findViewById(R.id.et_edit_text))
.setIndex(i - 1);
}
//删除特定位置的视图
mEditTextList.removeViewAt(index);
NoteEditText edit = null;
//通过id把编辑框存在空的NoteEditText中
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();
edit.append(text);
edit.requestFocus();
edit.setSelection(length);
}
/**
* @description 描述:进入编辑文本框时的动作
* @param 参数1:index
* @param 参数2:text
* @param 参数3:
* @return 返回值:void
* @author zhangchaoqun
*/
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);
}
}
/**
* @description 描述:切换至列表清单模式
* @param 参数1:text
* @param 参数2:
* @param 参数3:
* @return 返回值:void
* @author zhangchaoqun
*/
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);
}
/**
* @description 描述:获取高亮效果的反馈情况
* @param 参数1:fullText
* @param 参数2:userQuery
* @param 参数3:
* @return 返回值:Spannable返回设置好的效果
* @author zhangchaoqun
*/
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;
}
/**
* @description 描述:获取列表项
* @param 参数1:item
* @param 参数2:index
* @param 参数3:
* @return 返回值:View返回列表视图
* @author zhangchaoqun
*/
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) {
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();
}
//运行编辑框的监听器对该行为作出反应,并设置下标及文本内容
edit.setOnTextViewChangeListener(this);
edit.setIndex(index);
edit.setText(getHighlightQueryResult(item, mUserQuery));
return view;
}
/**
* @description 描述:便签内容发生改变时需要做的事情
* @param 参数1:index
* @param 参数2:hasText
* @param 参数3:
* @return 返回值:void
* @author zhangchaoqun
*/
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);
}
}
/**
* @description 描述:检查模式和列表模式的切换
* @param 参数1:oldMode
* @param 参数2:newMode
* @param 参数3:
* @return 返回值:void
* @author zhangchaoqun
*/
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);
}
}
/**
* @description 描述:设置勾选选项表并返回是否勾选的标记
* @param 参数1:
* @param 参数2:
* @param 参数3:
* @return 返回值:bool是否勾选
* @author zhangchaoqun
*/
private boolean getWorkingText() {
//初始化check标记
boolean hasChecked = false;
// 若模式为CHECK_LIST
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()) {
//扩展字符串为已打钩并把标记置true
sb.append(TAG_CHECKED).append(" ").append(edit.getText()).append("\n");
hasChecked = true;
} else {
//扩展字符串添加未打钩
sb.append(TAG_UNCHECKED).append(" ").append(edit.getText()).append("\n");
}
}
}
//利用编辑好的字符串设置运行便签的内容
mWorkingNote.setWorkingText(sb.toString());
} else {
// 若不是list模式直接用编辑器中的内容设置运行中标签的内容
mWorkingNote.setWorkingText(mNoteEditor.getText().toString());
}
return hasChecked;
}
/**
* @description 描述:保存便签
* @param 参数1:
* @param 参数2:
* @param 参数3:
* @return 返回值:bool是否保存
* @author zhangchaoqun
*/
private boolean saveNote() {
//运行 getWorkingText()之后保存
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
*/
//链接RESULT_OK是为了识别保存的2种情况一是创建后保存二是修改后保存
setResult(RESULT_OK);
}
return saved;
}
/**
* @description 描述:将便签发送至桌面
* @param 参数1:
* @param 参数2:
* @param 参数3:
* @return 返回值:void
* @author zhangchaoqun
*/
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);
//设置sneder的行为是发送
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);
}
}
/**
* @description 描述:编辑小图标的标题
* @param 参数1:content
* @param 参数2:
* @param 参数3:
* @return 返回值:String标题
* @author zhangchaoqun
*/
private String makeShortcutIconTitle(String content) {
//直接设置为content中的内容并返回有勾选和未勾选2种
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;
}
/**
* @description 描述:显示提示的视图,根据下标显示对应的提示
* @param 参数1:resId
* @param 参数2:
* @param 参数3:
* @return 返回值:void
* @author zhangchaoqun
*/
private void showToast(int resId) {
showToast(resId, Toast.LENGTH_SHORT);
}
/**
* @description 描述:持续显示提示的视图根据下标和持续的时间duration编辑提示视图并显示
* @param 参数1:resId
* @param 参数2:duration
* @param 参数3:
* @return 返回值:void
* @author zhangchaoqun
*/
private void showToast(int resId, int duration) {
Toast.makeText(this, resId, duration).show();
}
}