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-master/NoteEditActivity.java

516 lines
18 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;
// NoteEditActivity 是一个用于编辑便签的 Activity。
public class NoteEditActivity extends Activity implements OnClickListener,
NoteSettingChangedListener, OnTextViewChangeListener {
// 内部类,用于持有头部视图的控件引用。
private class HeadViewHolder {
public TextView tvModified; // 修改日期的 TextView。
public ImageView ivAlertIcon; // 提醒图标的 ImageView。
public TextView tvAlertDate; // 提醒日期的 TextView。
public ImageView ibSetBgColor; // 设置背景颜色的 ImageView。
}
// 背景颜色选择按钮与颜色 ID 的映射。
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 的映射。
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);
}
// 字体大小选择按钮与大小 ID 的映射。
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);
}
// 字体大小选择结果与按钮 ID 的映射。
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";
// 头部视图的 Holder。
private HeadViewHolder mNoteHeaderHolder;
// 头部视图面板、背景颜色选择器和字体大小选择器。
private View mHeadViewPanel;
private View mNoteBgColorSelector;
private View mFontSizeSelector;
// 编辑器和编辑器面板。
private EditText mNoteEditor;
private View mNoteEditorPanel;
// 正在编辑的便签。
private WorkingNote mWorkingNote;
// SharedPreferences 和字体大小 ID。
private SharedPreferences mSharedPrefs;
private int mFontSizeId;
// SharedPreferences 中字体大小的键。
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;
// onCreate 方法,初始化 Activity。
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
this.setContentView(R.layout.note_edit);
if (savedInstanceState == null && !initActivityState(getIntent())) {
finish();
return;
}
initResources();
}
// onRestoreInstanceState 方法,恢复 Activity 状态。
@Override
protected void onRestoreInstanceState(Bundle savedInstanceState) {
super.onRestoreInstanceState(savedInstanceState);
if (savedInstanceState != null && savedInstanceState.containsKey(Intent.EXTRA_UID)) {
Intent intent = new Intent(Intent.ACTION_VIEW);
intent.putExtra(Intent.EXTRA_UID, savedInstanceState.getLong(Intent.EXTRA_UID));
if (!initActivityState(intent)) {
finish();
return;
}
Log.d(TAG, "Restoring from killed activity");
}
}
// initActivityState 方法,初始化 Activity 状态。
private boolean initActivityState(Intent intent) {
// 省略部分代码...
}
// onResume 方法,恢复 Activity 时调用。
@Override
protected void onResume() {
super.onResume();
initNoteScreen();
}
// initNoteScreen 方法,初始化便签屏幕。
private void initNoteScreen() {
// 省略部分代码...
}
// onNewIntent 方法,处理新的 Intent。
@Override
protected void onNewIntent(Intent intent) {
super.onNewIntent(intent);
initActivityState(intent);
}
// onSaveInstanceState 方法,保存 Activity 状态。
@Override
protected void onSaveInstanceState(Bundle outState) {
super.onSaveInstanceState(outState);
// 省略部分代码...
}
// dispatchTouchEvent 方法,分发触摸事件。
@Override
public boolean dispatchTouchEvent(MotionEvent ev) {
// 省略部分代码...
}
// inRangeOfView 方法,判断触摸事件是否在视图范围内。
private boolean inRangeOfView(View view, MotionEvent ev) {
// 省略部分代码...
}
// initResources 方法,初始化资源。
private void initResources() {
// 省略部分代码...
}
// onPause 方法,暂停 Activity 时调用。
@Override
protected void onPause() {
super.onPause();
if(saveNote()) {
Log.d(TAG, "Note data was saved with length:" + mWorkingNote.getContent().length());
}
clearSettingState();
}
// updateWidget 方法,更新小部件。
private void updateWidget() {
// 省略部分代码...
}
}
/**
* 当点击某个视图时调用此方法。
* @param v 被点击的视图。
*/
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)) { // 如果被点击的是背景颜色按钮。
findViewById(sBgSelectorSelectionMap.get(mWorkingNote.getBgColorId())).setVisibility(View.GONE);
mWorkingNote.setBgColorId(sBgSelectorBtnsMap.get(id)); // 更改背景颜色ID。
mNoteBgColorSelector.setVisibility(View.GONE);
} else if (sFontSizeBtnsMap.containsKey(id)) { // 如果被点击的是字体大小按钮。
findViewById(sFontSelectorSelectionMap.get(mFontSizeId)).setVisibility(View.GONE);
mFontSizeId = sFontSizeBtnsMap.get(id); // 更改字体大小ID。
mSharedPrefs.edit().putInt(PREFERENCE_FONT_SIZE, mFontSizeId).commit(); // 保存新的字体大小偏好。
findViewById(sFontSelectorSelectionMap.get(mFontSizeId)).setVisibility(View.VISIBLE);
// 如果便签处于清单模式,更新清单视图;否则,更新文本外观。
if (mWorkingNote.getCheckListMode() == TextNote.MODE_CHECK_LIST) {
getWorkingText();
switchToListMode(mWorkingNote.getContent());
} else {
mNoteEditor.setTextAppearance(this, TextAppearanceResources.getTexAppearanceResource(mFontSizeId));
}
mFontSizeSelector.setVisibility(View.GONE);
}
}
/**
* 当按下返回键时调用。
*/
@Override
public void onBackPressed() {
if(clearSettingState()) {
return;
}
saveNote();
super.onBackPressed();
}
/**
* 清除设置状态,例如隐藏背景颜色选择器和字体大小选择器。
* @return 如果设置状态被清除则返回true否则返回false。
*/
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());
}
/**
* 准备选项菜单。
* @param menu 选项菜单。
* @return 如果菜单已准备则返回true否则返回false。
*/
@Override
public boolean onPrepareOptionsMenu(Menu menu) {
if (isFinishing()) {
return true;
}
clearSettingState();
menu.clear();
// 根据便签的文件夹ID决定加载哪个菜单。
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;
}
/**
* 处理选项菜单项的选择。
* @param item 被选择的菜单项。
* @return 如果项被处理则返回true否则返回false。
*/
@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();
}
/**
* 分享便签到支持{@link Intent#ACTION_SEND}动作和{text/plain}类型的应用。
* @param context 上下文。
* @param info 要分享的便签内容。
*/
private void sendTo(Context context, String info) {
Intent intent = new Intent(Intent.ACTION_SEND);
intent.putExtra(Intent.EXTRA_TEXT, info);
intent.setType("text/plain");
context.startActivity(intent);
}
/**
* 创建一个新的便签。
*/
private void createNewNote() {
// 在创建新便签之前保存当前正在编辑的便签。
saveNote();
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);
} else {
Log.d(TAG, "错误的便签ID不应该发生");
}
if (!isSyncMode()) {
if (!DataUtils.batchDeleteNotes(getContentResolver(), ids)) {
Log.e(TAG, "删除便签错误");
}
} else {
if (!DataUtils.batchMoveToFolder(getContentResolver(), ids, Notes.ID_TRASH_FOLER)) {
Log.e(TAG, "将便签移动到废纸篓文件夹错误,不应该发生");
}
}
}
mWorkingNote.markDeleted(true);
}
/**
* 检查应用是否处于同步模式。
* @return 如果处于同步模式则返回true否则返回false。
*/
private boolean isSyncMode() {
return NotesPreferenceActivity.getSyncAccountName(this).trim().length() > 0;
}
/**
* 当时钟提醒改变时调用。
* @param date 新的提醒日期。
* @param set 如果设置了提醒则为true如果取消了提醒则为false。
*/
public void onClockAlertChanged(long date, boolean set) {
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 {
Log.e(TAG, "时钟提醒设置错误");
showToast(R.string.error_note_empty_for_clock);
}
}
/**
* 当小部件改变时调用。
*/
public void onWidgetChanged() {
updateWidget();
}
/**
* 处理在清单中删除文本项。
* @param index 要删除项的索引。
* @param text 项的文本。
*/