From 4a53c968d1d088dd36364b839d148a2e0bb44f8d Mon Sep 17 00:00:00 2001 From: pz96pqh8i <3326392379@qq.com> Date: Tue, 31 Dec 2024 02:07:01 +0800 Subject: [PATCH] ADD file via upload --- NoteEditActivity.java | 335 ++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 335 insertions(+) create mode 100644 NoteEditActivity.java diff --git a/NoteEditActivity.java b/NoteEditActivity.java new file mode 100644 index 0000000..95bc32e --- /dev/null +++ b/NoteEditActivity.java @@ -0,0 +1,335 @@ +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,实现了OnClickListener、NoteSettingChangedListener、OnTextViewChangeListener接口,用于编辑笔记相关的操作和界面交互 +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与对应的颜色资源ID等进行关联 + + // 将背景颜色选择按钮的ID映射到对应的颜色资源ID(如黄色、红色等颜色的资源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映射到对应的已选择该颜色时显示的标识视图的ID(比如选中黄色背景时对应的选中标识视图的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映射到对应的字体大小资源ID(如大字体、小字体等对应的资源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映射到对应的已选择该字体大小时显示的标识视图的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"; // 用于日志输出的标记字符串 + + private HeadViewHolder mNoteHeaderHolder; // 笔记头部视图相关控件的持有者实例 + private View mHeadViewPanel; // 笔记头部整体的视图 + private View mNoteBgColorSelector; // 背景颜色选择器的视图(通常是包含多个颜色选项的布局) + private View mFontSizeSelector; // 字体大小选择器的视图(包含不同字体大小选项的布局) + private EditText mNoteEditor; // 用于编辑笔记内容的EditText控件 + private View mNoteEditorPanel; // 包含笔记编辑区域的整体视图(可能用于设置背景等相关属性) + private WorkingNote mWorkingNote; // 代表正在编辑的笔记对象,包含笔记的各种属性和操作方法 + private SharedPreferences mSharedPrefs; // 用于获取和保存应用的共享偏好设置 + private int mFontSizeId; // 当前选中的字体大小对应的资源ID + + 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; // 用于显示笔记内容为列表模式时的列表布局(LinearLayout) + private String mUserQuery; // 用户输入的查询字符串(可能用于搜索笔记内容等场景) + private Pattern mPattern; // 用于对用户查询内容进行正则匹配的模式对象 + + // Activity创建时调用的方法 + @Override + protected void onCreate(Bundle savedInstanceState) { + super.onCreate(savedInstanceState); + // 设置该Activity对应的布局文件 + this.setContentView(R.layout.note_edit); + + // 如果savedInstanceState为null(即Activity首次创建,不是从内存恢复的情况),并且初始化Activity状态失败,则直接结束该Activity + if (savedInstanceState == null &&!initActivityState(getIntent())) { + finish(); + return; + } + // 初始化相关资源,如查找视图等操作 + initResources(); + } + + /** + * 当Activity因内存不足被系统销毁后,再次恢复时调用该方法,用于从保存的状态中恢复Activity的相关信息 + */ + @Override + protected void onRestoreInstanceState(Bundle savedInstanceState) { + super.onRestoreInstanceState(savedInstanceState); + // 如果保存的状态不为null且包含特定的额外数据(通过Intent.EXTRA_UID标识),则尝试重新初始化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"); + } + } + + // 根据传入的Intent初始化Activity的状态,比如确定是查看已有笔记还是新建笔记等情况,并加载相应的笔记数据 + private boolean initActivityState(Intent intent) { + /** + * 如果用户指定了Intent.ACTION_VIEW动作,但没有提供笔记的ID,那么跳转到NotesListActivity(可能是笔记列表页面) + */ + mWorkingNote = null; + if (TextUtils.equals(Intent.ACTION_VIEW, intent.getAction())) { + long noteId = intent.getLongExtra(Intent.EXTRA_UID, 0); + mUserQuery = ""; + + /** + * 如果Intent中包含搜索相关的额外数据(从搜索结果进入该Activity的情况),则获取对应的笔记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); + startActivity(jump); + showToast(R.string.error_note_not_exist); + finish(); + return false; + } else { + // 从数据库加载指定ID的笔记对象,如果加载失败则记录错误日志并结束该Activity + 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())) { + // 新建笔记的情况 + 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)); + + // 解析可能的通话记录笔记相关信息(如果有) + 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; + 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 { + 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; + } + // 设置笔记对象的设置状态改变监听器为当前Activity(实现了NoteSettingChangedListener接口) + mWorkingNote.setOnSettingStatusChangedListener(this); + return true; + } + + // Activity恢复可见时(如从暂停状态恢复)调用的方法,用于初始化笔记编辑界面的相关显示内容 + @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: 添加用于设置提醒的菜单选项。目前禁用该功能,因为DateTimePicker还未准备好 + */ + 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); + }; + } + + // 当Activity接收到新的Intent时调用该方法(比如通过启动模式设置可以接收新的Intent情况),重新初始化Activity状态 + @Override + protected void onNewIntent(Intent intent) { + super.onNewIntent(intent); + initActivityState(intent); + } + + // 在Activity可能被销毁前(如系统内存不足、用户切换应用等情况)保存当前Activity的相关状态信息(比如正在编辑的笔记的ID等) + @Override + protected void onSaveInstanceState(Bundle outState) { + super.onSaveInstanceState(outState); + /** + * 对于没有笔记ID的新笔记(可能还未保存),先保存笔记以生成一个ID。如果正在编辑的笔记不值得保存(可能内容为空等情况),没有ID就相当于新建笔记的情况 + */ + if (!mWorkingNote.existInDatabase()) { + saveNote(); + } + outState.putLong(Intent.EXTRA_UID, mWorkingNote.getNoteId()); + Log.d(TAG, "Save working note id: " + mWorkingNote.getNoteId() + " onSaveInstanceState"); + } + + // 用于分发触摸事件,当背景颜色选择器或字体大小选择器可见时,判断触摸事件是否在其区域外,如果在区域外则隐藏相应的选择器视图 + @Override + public boolean dispatchTouchEvent(MotionEvent ev) { + if (mNoteBgColorSelector.getVisibility() == View.VISIBLE + &&!inRangeOfView(mNoteBgColorSelector, ev)) { + mNoteBgColorSelector.setVisibility(View.GONE); + return true; + } + + if (mFontSizeSelector.getVisibility() == View.VISIBLE \ No newline at end of file