/* * 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 sBgSelectorBtnsMap = new HashMap(); static { // 将R.id.iv_bg_yellow对应的ResourceParser.YELLOW添加到sBgSelectorBtnsMap中 sBgSelectorBtnsMap.put(R.id.iv_bg_yellow, ResourceParser.YELLOW); // 将R.id.iv_bg_red对应的ResourceParser.RED添加到sBgSelectorBtnsMap中 sBgSelectorBtnsMap.put(R.id.iv_bg_red, ResourceParser.RED); // 将R.id.iv_bg_blue对应的ResourceParser.BLUE添加到sBgSelectorBtnsMap中 sBgSelectorBtnsMap.put(R.id.iv_bg_blue, ResourceParser.BLUE); // 将R.id.iv_bg_green对应的ResourceParser.GREEN添加到sBgSelectorBtnsMap中 sBgSelectorBtnsMap.put(R.id.iv_bg_green, ResourceParser.GREEN); // 将R.id.iv_bg_white对应的ResourceParser.WHITE添加到sBgSelectorBtnsMap中 sBgSelectorBtnsMap.put(R.id.iv_bg_white, ResourceParser.WHITE); } private static final Map sBgSelectorSelectionMap = new HashMap(); static { // 将YELLOW放入sBgSelectorSelectionMap中 sBgSelectorSelectionMap.put(ResourceParser.YELLOW, R.id.iv_bg_yellow_select); // 将RED放入sBgSelectorSelectionMap中 sBgSelectorSelectionMap.put(ResourceParser.RED, R.id.iv_bg_red_select); // 将BLUE放入sBgSelectorSelectionMap中 sBgSelectorSelectionMap.put(ResourceParser.BLUE, R.id.iv_bg_blue_select); // 将GREEN放入sBgSelectorSelectionMap中 sBgSelectorSelectionMap.put(ResourceParser.GREEN, R.id.iv_bg_green_select); // 将WHITE放入sBgSelectorSelectionMap中 sBgSelectorSelectionMap.put(ResourceParser.WHITE, R.id.iv_bg_white_select); } private static final Map sFontSizeBtnsMap = new HashMap(); static { // 将R.id.ll_font_large和ResourceParser.TEXT_LARGE放入sFontSizeBtnsMap中 sFontSizeBtnsMap.put(R.id.ll_font_large, ResourceParser.TEXT_LARGE); // 将R.id.ll_font_small和ResourceParser.TEXT_SMALL放入sFontSizeBtnsMap中 sFontSizeBtnsMap.put(R.id.ll_font_small, ResourceParser.TEXT_SMALL); // 将R.id.ll_font_normal和ResourceParser.TEXT_MEDIUM放入sFontSizeBtnsMap中 sFontSizeBtnsMap.put(R.id.ll_font_normal, ResourceParser.TEXT_MEDIUM); // 将R.id.ll_font_super和ResourceParser.TEXT_SUPER放入sFontSizeBtnsMap中 sFontSizeBtnsMap.put(R.id.ll_font_super, ResourceParser.TEXT_SUPER); } private static final Map sFontSelectorSelectionMap = new HashMap(); static { // 将ResourceParser.TEXT_LARGE和R.id.iv_large_select放入sFontSelectorSelectionMap中 sFontSelectorSelectionMap.put(ResourceParser.TEXT_LARGE, R.id.iv_large_select); // 将ResourceParser.TEXT_SMALL和R.id.iv_small_select放入sFontSelectorSelectionMap中 sFontSelectorSelectionMap.put(ResourceParser.TEXT_SMALL, R.id.iv_small_select); // 将ResourceParser.TEXT_MEDIUM和R.id.iv_medium_select放入sFontSelectorSelectionMap中 sFontSelectorSelectionMap.put(ResourceParser.TEXT_MEDIUM, R.id.iv_medium_select); // 将ResourceParser.TEXT_SUPER和R.id.iv_super_select放入sFontSelectorSelectionMap中 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); // 如果savedInstanceState为空,且initActivityState(getIntent())返回false,则结束当前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) { 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"); } } 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())) { // 获取传入的noteId long noteId = intent.getLongExtra(Intent.EXTRA_UID, 0); // 设置查询条件 mUserQuery = ""; /** * Starting from the searched result */ if (intent.hasExtra(SearchManager.EXTRA_DATA_KEY)) { noteId = Long.parseLong(intent.getStringExtra(SearchManager.EXTRA_DATA_KEY)); mUserQuery = intent.getStringExtra(SearchManager.USER_QUERY); } // 检查note是否可见 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 { // 加载note 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 // 获取传入的文件夹id long folderId = intent.getLongExtra(Notes.INTENT_EXTRA_FOLDER_ID, 0); // 获取传入的小部件id 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); // 获取传入的小部件背景资源id int bgResId = intent.getIntExtra(Notes.INTENT_EXTRA_BACKGROUND_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; // 获取电话号码和通话日期的noteId if ((noteId = DataUtils.getNoteIdByPhoneNumberAndCallDate(getContentResolver(), phoneNumber, callDate)) > 0) { // 加载note mWorkingNote = WorkingNote.load(this, noteId); if (mWorkingNote == null) { Log.e(TAG, "load call note failed with note id" + noteId); finish(); return false; } } else { // 创建一个新的call note 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); } // 设置标题背景 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(); } // 将当前笔记的ID存储到Bundle中 outState.putLong(Intent.EXTRA_UID, mWorkingNote.getNoteId()); Log.d(TAG, "Save working note id: " + mWorkingNote.getNoteId() + " onSaveInstanceState"); } @Override public boolean dispatchTouchEvent(MotionEvent ev) { // 如果mNoteBgColorSelector可见,且ev不在mNoteBgColorSelector范围内,则将mNoteBgColorSelector设置为不可见 if (mNoteBgColorSelector.getVisibility() == View.VISIBLE && !inRangeOfView(mNoteBgColorSelector, ev)) { mNoteBgColorSelector.setVisibility(View.GONE); return true; } // 如果mFontSizeSelector可见,且ev不在mFontSizeSelector范围内,则将mFontSizeSelector设置为不可见 if (mFontSizeSelector.getVisibility() == View.VISIBLE && !inRangeOfView(mFontSizeSelector, ev)) { mFontSizeSelector.setVisibility(View.GONE); return true; } // 调用父类的dispatchTouchEvent方法 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]; //判断触摸点是否在View的范围内 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); // 初始化头部视图的holder 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); //遍历sFontSizeBtnsMap中的键,找到对应的View for (int id : sFontSizeBtnsMap.keySet()) { View view = findViewById(id); //为View设置点击事件 view.setOnClickListener(this); }; //获取SharedPreferences 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; } //获取布局中的LinearLayout 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() { // 创建一个Intent,用于更新小部件 Intent intent = new Intent(AppWidgetManager.ACTION_APPWIDGET_UPDATE); // 根据小部件类型,设置Intent的class 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; } // 将小部件ID放入Intent中 intent.putExtra(AppWidgetManager.EXTRA_APPWIDGET_IDS, new int[] { mWorkingNote.getWidgetId() }); // 发送Intent 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); // 设置当前笔记的背景颜色id mWorkingNote.setBgColorId(sBgSelectorBtnsMap.get(id)); // 设置背景颜色选择器不可见 mNoteBgColorSelector.setVisibility(View.GONE); // 如果点击的是字体大小选择器中的按钮 } else if (sFontSizeBtnsMap.containsKey(id)) { // 设置字体大小选择器中的按钮不可见 findViewById(sFontSelectorSelectionMap.get(mFontSizeId)).setVisibility(View.GONE); // 设置当前笔记的字体大小id 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(); //调用父类的onBackPressed()方法 super.onBackPressed(); } private boolean clearSettingState() { //检查mNoteBgColorSelector的可见性 if (mNoteBgColorSelector.getVisibility() == View.VISIBLE) { //将mNoteBgColorSelector设置为不可见 mNoteBgColorSelector.setVisibility(View.GONE); //返回true return true; //检查mFontSizeSelector的可见性 } else if (mFontSizeSelector.getVisibility() == View.VISIBLE) { //将mFontSizeSelector设置为不可见 mFontSizeSelector.setVisibility(View.GONE); //返回true return true; } //返回false 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) { // 如果正在关闭,则返回true if (isFinishing()) { return true; } // 清除设置状态 clearSettingState(); // 清除菜单 menu.clear(); // 如果文件夹ID等于Notes.ID_CALL_RECORD_FOLDER,则使用R.menu.call_note_edit,否则使用R.menu.note_edit if (mWorkingNote.getFolderId() == Notes.ID_CALL_RECORD_FOLDER) { getMenuInflater().inflate(R.menu.call_note_edit, menu); } else { getMenuInflater().inflate(R.menu.note_edit, menu); } // 如果检查列表模式为TextNote.MODE_CHECK_LIST,则设置菜单标题为R.string.menu_normal_mode,否则设置菜单标题为R.string.menu_list_mode 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); } // 如果有闹钟,则隐藏菜单中的menu_alert,否则隐藏菜单中的menu_delete_remind 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对象,传入当前时间 DateTimePickerDialog d = new DateTimePickerDialog(this, System.currentTimeMillis()); // 为DateTimePickerDialog设置OnDateTimeSetListener,当设置时间时调用 d.setOnDateTimeSetListener(new OnDateTimeSetListener() { public void OnDateTimeSet(AlertDialog dialog, long date) { // 设置mWorkingNote的alertDate为传入的date,并设置为true mWorkingNote.setAlertDate(date , true); } }); // 显示DateTimePickerDialog d.show(); } /** * Share note to apps that support {@link Intent#ACTION_SEND} action * and {@text/plain} type */ private void sendTo(Context context, String info) { Intent intent = new Intent(Intent.ACTION_SEND); // 将要发送的信息放入Intent中 intent.putExtra(Intent.EXTRA_TEXT, info); // 设置要发送的类型 intent.setType("text/plain"); // 启动发送 context.startActivity(intent); } private void createNewNote() { // 首先,保存当前编辑笔记 saveNote(); // 为了安全起见,启动新的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 HashSet ids = new HashSet(); // 获取当前笔记的id long id = mWorkingNote.getNoteId(); // 如果当前笔记的id不是根文件夹的id if (id != Notes.ID_ROOT_FOLDER) { // 将当前笔记的id添加到HashSet中 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"); } } } // 标记当前笔记已删除 mWorkingNote.markDeleted(true); } //获取NotesPreferenceActivity中的同步账户名,并判断是否大于0 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) { // 获取EditText列表的子视图的数量 int childCount = mEditTextList.getChildCount(); // 如果只有一个子视图,则直接返回 if (childCount == 1) { return; } // 从index+1开始,将EditText列表中的每一个子视图的index属性设置为i-1 for (int i = index + 1; i < childCount; i++) { ((NoteEditText) mEditTextList.getChildAt(i).findViewById(R.id.et_edit_text)) .setIndex(i - 1); } // 从EditText列表中移除指定位置的子视图 mEditTextList.removeViewAt(index); NoteEditText edit = null; // 如果移除的是第一个子视图,则获取EditText列表的第一个子视图 if(index == 0) { edit = (NoteEditText) mEditTextList.getChildAt(0).findViewById( R.id.et_edit_text); // 否则,获取EditText列表中指定位置的子视图 } else { edit = (NoteEditText) mEditTextList.getChildAt(index - 1).findViewById( R.id.et_edit_text); } // 获取EditText中的文本长度 int length = edit.length(); // 将文本添加到EditText中 edit.append(text); // 设置EditText获取焦点 edit.requestFocus(); // 设置EditText中的光标位置 edit.setSelection(length); } public void onEditTextEnter(int index, String text) { /** * Should not happen, check for debug */ // 检查索引是否超出mEditTextList的范围 if(index > mEditTextList.getChildCount()) { Log.e(TAG, "Index out of mEditTextList boundrary, should not happen"); } // 获取指定索引的View View view = getListItem(text, index); // 将View添加到mEditTextList中 mEditTextList.addView(view, index); // 获取EditText NoteEditText edit = (NoteEditText) view.findViewById(R.id.et_edit_text); // 设置焦点 edit.requestFocus(); // 设置光标位置 edit.setSelection(0); // 遍历mEditTextList,设置每个EditText的索引 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中的所有视图 mEditTextList.removeAllViews(); // 将文本按换行符分割成字符串数组 String[] items = text.split("\n"); int index = 0; // 遍历字符串数组,将每一个字符串添加到mEditTextList中 for (String item : items) { if(!TextUtils.isEmpty(item)) { mEditTextList.addView(getListItem(item, index)); index++; } } // 将一个空字符串添加到mEditTextList中 mEditTextList.addView(getListItem("", index)); // 将焦点设置到mEditTextList中最后一个添加的视图中 mEditTextList.getChildAt(index).findViewById(R.id.et_edit_text).requestFocus(); // 将mNoteEditor设置为不可见,mEditTextList设置为可见 mNoteEditor.setVisibility(View.GONE); mEditTextList.setVisibility(View.VISIBLE); } private Spannable getHighlightQueryResult(String fullText, String userQuery) { //创建一个SpannableString对象,用于存储高亮后的文本 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); // 获取edit文本框 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); } } }); // 判断item是否以TAG_CHECKED开头 if (item.startsWith(TAG_CHECKED)) { // 设置复选框为选中状态 cb.setChecked(true); // 设置文本框的画笔标志 edit.setPaintFlags(edit.getPaintFlags() | Paint.STRIKE_THRU_TEXT_FLAG); // 截取item 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 = 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) { //如果索引大于等于EditText列表的子元素数量,则打印错误信息 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(); // 遍历EditTextList中的每一个View for (int i = 0; i < mEditTextList.getChildCount(); i++) { View view = mEditTextList.getChildAt(i); // 获取EditText NoteEditText edit = (NoteEditText) view.findViewById(R.id.et_edit_text); // 判断EditText是否为空 if (!TextUtils.isEmpty(edit.getText())) { // 获取复选框 if (((CheckBox) view.findViewById(R.id.cb_edit_item)).isChecked()) { // 将复选框中的文本添加到StringBuilder中 sb.append(TAG_CHECKED).append(" ").append(edit.getText()).append("\n"); hasChecked = true; } else { // 将未复选框中的文本添加到StringBuilder中 sb.append(TAG_UNCHECKED).append(" ").append(edit.getText()).append("\n"); } } } // 将StringBuilder中的文本设置为当前工作文本 mWorkingNote.setWorkingText(sb.toString()); } else { // 将NoteEditor中的文本设置为当前工作文本 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 Intent sender = new Intent(); // 创建一个Intent,用于跳转到NoteEditActivity Intent shortcutIntent = new Intent(this, NoteEditActivity.class); // 设置Intent的Action shortcutIntent.setAction(Intent.ACTION_VIEW); // 设置Intent的参数,用于跳转到NoteEditActivity shortcutIntent.putExtra(Intent.EXTRA_UID, mWorkingNote.getNoteId()); // 将Intent添加到sender中 sender.putExtra(Intent.EXTRA_SHORTCUT_INTENT, shortcutIntent); // 设置Intent的名称 sender.putExtra(Intent.EXTRA_SHORTCUT_NAME, makeShortcutIconTitle(mWorkingNote.getContent())); // 设置Intent的图标 sender.putExtra(Intent.EXTRA_SHORTCUT_ICON_RESOURCE, Intent.ShortcutIconResource.fromContext(this, R.drawable.icon_app)); // 设置Intent的参数,用于标记是否是重复创建 sender.putExtra("duplicate", true); // 设置Intent的Action sender.setAction("com.android.launcher.action.INSTALL_SHORTCUT"); // 显示提示信息 showToast(R.string.info_note_enter_desktop); // 发送Intent 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) { // 移除TAG_CHECKED和TAG_UNCHECKED content = content.replace(TAG_CHECKED, ""); content = content.replace(TAG_UNCHECKED, ""); // 如果内容长度大于SHORTCUT_ICON_TITLE_MAX_LEN,则截取前SHORTCUT_ICON_TITLE_MAX_LEN个字符 return content.length() > SHORTCUT_ICON_TITLE_MAX_LEN ? content.substring(0, SHORTCUT_ICON_TITLE_MAX_LEN) : content; } private void showToast(int resId) { // 显示Toast,持续时间为Toast.LENGTH_SHORT showToast(resId, Toast.LENGTH_SHORT); } private void showToast(int resId, int duration) { // 创建Toast,显示resId,持续时间为duration Toast.makeText(this, resId, duration).show(); } }