From deacd8ddce8d720996fd265566048676ebafa0f5 Mon Sep 17 00:00:00 2001 From: STRIV1 <1476836209@qq.com> Date: Mon, 30 Dec 2024 19:26:52 +0800 Subject: [PATCH] =?UTF-8?q?ui=E4=BB=A3=E7=A0=81=E6=B3=A8=E9=87=8A?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- NoteEditActivity.java | 515 ++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 515 insertions(+) create mode 100644 NoteEditActivity.java diff --git a/NoteEditActivity.java b/NoteEditActivity.java new file mode 100644 index 0000000..075ccd6 --- /dev/null +++ b/NoteEditActivity.java @@ -0,0 +1,515 @@ +/* + * 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 sBgSelectorBtnsMap = new HashMap(); + 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 sBgSelectorSelectionMap = new HashMap(); + 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 sFontSizeBtnsMap = new HashMap(); + 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 sFontSelectorSelectionMap = new HashMap(); + 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 ids = new HashSet(); + 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 项的文本。 + */