|
|
|
|
@ -1,17 +1,6 @@
|
|
|
|
|
/*
|
|
|
|
|
* 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;
|
|
|
|
|
@ -70,20 +59,36 @@ import java.util.HashSet;
|
|
|
|
|
import java.util.Map;
|
|
|
|
|
import java.util.regex.Matcher;
|
|
|
|
|
import java.util.regex.Pattern;
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
import org.json.JSONArray;
|
|
|
|
|
import org.json.JSONObject;
|
|
|
|
|
import net.micode.notes.ai.AIService;
|
|
|
|
|
import android.app.ProgressDialog;
|
|
|
|
|
|
|
|
|
|
/**
|
|
|
|
|
* 便签编辑页面 (UI Layer - Core Activity)
|
|
|
|
|
* <p>
|
|
|
|
|
* 职责:
|
|
|
|
|
* 1. 提供便签内容的编辑界面,支持普通文本模式和清单模式 (CheckList)。
|
|
|
|
|
* 2. 处理背景色、字体大小、闹钟提醒等设置。
|
|
|
|
|
* 3. 负责 Activity 生命周期内的状态保存与恢复。
|
|
|
|
|
* 4. 【2025新特性】集成 AI 智能助手 (润色与分类)。
|
|
|
|
|
* <p>
|
|
|
|
|
* 架构关系:
|
|
|
|
|
* 持有 {@link WorkingNote} (ViewModel) 实例,通过它操作底层数据。
|
|
|
|
|
* 实现 {@link OnClickListener} 处理 UI 点击事件。
|
|
|
|
|
*/
|
|
|
|
|
public class NoteEditActivity extends Activity implements OnClickListener,
|
|
|
|
|
NoteSettingChangedListener, OnTextViewChangeListener {
|
|
|
|
|
private class HeadViewHolder {
|
|
|
|
|
public TextView tvModified;
|
|
|
|
|
|
|
|
|
|
public ImageView ivAlertIcon;
|
|
|
|
|
|
|
|
|
|
public TextView tvAlertDate;
|
|
|
|
|
|
|
|
|
|
public ImageView ibSetBgColor;
|
|
|
|
|
// 内部类:用于持有标题栏的 UI 控件引用 (ViewHolder模式)
|
|
|
|
|
private class HeadViewHolder {
|
|
|
|
|
public TextView tvModified; // 修改时间
|
|
|
|
|
public ImageView ivAlertIcon; // 闹钟图标
|
|
|
|
|
public TextView tvAlertDate; // 闹钟时间
|
|
|
|
|
public ImageView ibSetBgColor; // 设置背景色按钮
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// 静态映射表:将 UI 按钮 ID 映射到 逻辑颜色 ID
|
|
|
|
|
private static final Map<Integer, Integer> sBgSelectorBtnsMap = new HashMap<Integer, Integer>();
|
|
|
|
|
static {
|
|
|
|
|
sBgSelectorBtnsMap.put(R.id.iv_bg_yellow, ResourceParser.YELLOW);
|
|
|
|
|
@ -93,6 +98,7 @@ public class NoteEditActivity extends Activity implements OnClickListener,
|
|
|
|
|
sBgSelectorBtnsMap.put(R.id.iv_bg_white, ResourceParser.WHITE);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// 静态映射表:将 逻辑颜色 ID 映射到 选中状态图标 ID
|
|
|
|
|
private static final Map<Integer, Integer> sBgSelectorSelectionMap = new HashMap<Integer, Integer>();
|
|
|
|
|
static {
|
|
|
|
|
sBgSelectorSelectionMap.put(ResourceParser.YELLOW, R.id.iv_bg_yellow_select);
|
|
|
|
|
@ -102,6 +108,7 @@ public class NoteEditActivity extends Activity implements OnClickListener,
|
|
|
|
|
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);
|
|
|
|
|
@ -110,6 +117,10 @@ public class NoteEditActivity extends Activity implements OnClickListener,
|
|
|
|
|
sFontSizeBtnsMap.put(R.id.ll_font_super, ResourceParser.TEXT_SUPER);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// AI 菜单项的 ID (虽然主要逻辑已迁移至 XML 按钮,但保留此常量兼容旧代码)
|
|
|
|
|
private static final int MENU_AI_OPT_ID = 999;
|
|
|
|
|
|
|
|
|
|
// 静态映射表:字体选中状态映射
|
|
|
|
|
private static final Map<Integer, Integer> sFontSelectorSelectionMap = new HashMap<Integer, Integer>();
|
|
|
|
|
static {
|
|
|
|
|
sFontSelectorSelectionMap.put(ResourceParser.TEXT_LARGE, R.id.iv_large_select);
|
|
|
|
|
@ -120,32 +131,29 @@ public class NoteEditActivity extends Activity implements OnClickListener,
|
|
|
|
|
|
|
|
|
|
private static final String TAG = "NoteEditActivity";
|
|
|
|
|
|
|
|
|
|
// UI 控件引用
|
|
|
|
|
private HeadViewHolder mNoteHeaderHolder;
|
|
|
|
|
|
|
|
|
|
private View mHeadViewPanel;
|
|
|
|
|
private View mNoteBgColorSelector; // 背景色选择面板
|
|
|
|
|
private View mFontSizeSelector; // 字体大小选择面板
|
|
|
|
|
private EditText mNoteEditor; // 核心编辑框 (普通模式)
|
|
|
|
|
private View mNoteEditorPanel; // 编辑区域容器
|
|
|
|
|
private LinearLayout mEditTextList; // 清单模式下的容器 (CheckList)
|
|
|
|
|
|
|
|
|
|
private View mNoteBgColorSelector;
|
|
|
|
|
|
|
|
|
|
private View mFontSizeSelector;
|
|
|
|
|
|
|
|
|
|
private EditText mNoteEditor;
|
|
|
|
|
|
|
|
|
|
private View mNoteEditorPanel;
|
|
|
|
|
|
|
|
|
|
// 业务逻辑核心对象 (ViewModel)
|
|
|
|
|
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;
|
|
|
|
|
|
|
|
|
|
@ -154,16 +162,22 @@ public class NoteEditActivity extends Activity implements OnClickListener,
|
|
|
|
|
super.onCreate(savedInstanceState);
|
|
|
|
|
this.setContentView(R.layout.note_edit);
|
|
|
|
|
|
|
|
|
|
// 初始化 Activity 状态(加载数据或新建便签)
|
|
|
|
|
if (savedInstanceState == null && !initActivityState(getIntent())) {
|
|
|
|
|
finish();
|
|
|
|
|
return;
|
|
|
|
|
}
|
|
|
|
|
initResources();
|
|
|
|
|
initResources(); // 初始化 UI 控件
|
|
|
|
|
|
|
|
|
|
// === 【新增 AI 功能入口】 ===
|
|
|
|
|
// 在 XML 布局中我们植入了一个 ID 为 btn_ai_polish 的 ImageButton
|
|
|
|
|
// 这里手动绑定点击监听器,触发 AI 逻辑
|
|
|
|
|
findViewById(R.id.btn_ai_polish).setOnClickListener(this);
|
|
|
|
|
// ============================
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/**
|
|
|
|
|
* 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
|
|
|
|
|
* 处理 Activity 意外销毁后的状态恢复 (如内存不足时)
|
|
|
|
|
*/
|
|
|
|
|
@Override
|
|
|
|
|
protected void onRestoreInstanceState(Bundle savedInstanceState) {
|
|
|
|
|
@ -179,24 +193,25 @@ public class NoteEditActivity extends Activity implements OnClickListener,
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/**
|
|
|
|
|
* 初始化 Activity 状态的核心逻辑
|
|
|
|
|
* 根据 Intent 的 Action 判断是 "查看/编辑旧便签" 还是 "新建便签"
|
|
|
|
|
*/
|
|
|
|
|
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;
|
|
|
|
|
|
|
|
|
|
// Case 1: 查看或编辑已有便签
|
|
|
|
|
if (TextUtils.equals(Intent.ACTION_VIEW, intent.getAction())) {
|
|
|
|
|
long noteId = intent.getLongExtra(Intent.EXTRA_UID, 0);
|
|
|
|
|
mUserQuery = "";
|
|
|
|
|
|
|
|
|
|
/**
|
|
|
|
|
* Starting from the searched result
|
|
|
|
|
*/
|
|
|
|
|
// 处理来自搜索结果的跳转,可能会带有高亮关键词 (User Query)
|
|
|
|
|
if (intent.hasExtra(SearchManager.EXTRA_DATA_KEY)) {
|
|
|
|
|
noteId = Long.parseLong(intent.getStringExtra(SearchManager.EXTRA_DATA_KEY));
|
|
|
|
|
mUserQuery = intent.getStringExtra(SearchManager.USER_QUERY);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// 校验便签是否存在
|
|
|
|
|
if (!DataUtils.visibleInNoteDatabase(getContentResolver(), noteId, Notes.TYPE_NOTE)) {
|
|
|
|
|
Intent jump = new Intent(this, NotesListActivity.class);
|
|
|
|
|
startActivity(jump);
|
|
|
|
|
@ -204,6 +219,7 @@ public class NoteEditActivity extends Activity implements OnClickListener,
|
|
|
|
|
finish();
|
|
|
|
|
return false;
|
|
|
|
|
} else {
|
|
|
|
|
// 加载数据
|
|
|
|
|
mWorkingNote = WorkingNote.load(this, noteId);
|
|
|
|
|
if (mWorkingNote == null) {
|
|
|
|
|
Log.e(TAG, "load note failed with note id" + noteId);
|
|
|
|
|
@ -211,11 +227,13 @@ public class NoteEditActivity extends Activity implements OnClickListener,
|
|
|
|
|
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
|
|
|
|
|
}
|
|
|
|
|
// Case 2: 新建便签 (INSERT_OR_EDIT)
|
|
|
|
|
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);
|
|
|
|
|
@ -224,7 +242,7 @@ public class NoteEditActivity extends Activity implements OnClickListener,
|
|
|
|
|
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) {
|
|
|
|
|
@ -232,6 +250,7 @@ public class NoteEditActivity extends Activity implements OnClickListener,
|
|
|
|
|
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);
|
|
|
|
|
@ -241,15 +260,18 @@ public class NoteEditActivity extends Activity implements OnClickListener,
|
|
|
|
|
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);
|
|
|
|
|
@ -258,6 +280,7 @@ public class NoteEditActivity extends Activity implements OnClickListener,
|
|
|
|
|
finish();
|
|
|
|
|
return false;
|
|
|
|
|
}
|
|
|
|
|
// 注册监听器,当 WorkingNote 属性改变时回调本 Activity 刷新 UI
|
|
|
|
|
mWorkingNote.setOnSettingStatusChangedListener(this);
|
|
|
|
|
return true;
|
|
|
|
|
}
|
|
|
|
|
@ -265,36 +288,47 @@ public class NoteEditActivity extends Activity implements OnClickListener,
|
|
|
|
|
@Override
|
|
|
|
|
protected void onResume() {
|
|
|
|
|
super.onResume();
|
|
|
|
|
initNoteScreen();
|
|
|
|
|
initNoteScreen(); // 每次界面可见时刷新 UI 状态
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/**
|
|
|
|
|
* 刷新便签编辑界面
|
|
|
|
|
* 设置字体、背景、内容、修改时间等
|
|
|
|
|
*/
|
|
|
|
|
private void initNoteScreen() {
|
|
|
|
|
mNoteEditor.setTextAppearance(this, TextAppearanceResources
|
|
|
|
|
.getTexAppearanceResource(mFontSizeId));
|
|
|
|
|
|
|
|
|
|
// 根据模式决定显示 EditText 还是 ListLayout
|
|
|
|
|
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();
|
|
|
|
|
showAlertHeader(); // 刷新闹钟状态
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/**
|
|
|
|
|
* 显示或隐藏闹钟提示头
|
|
|
|
|
*/
|
|
|
|
|
private void showAlertHeader() {
|
|
|
|
|
if (mWorkingNote.hasClockAlert()) {
|
|
|
|
|
long time = System.currentTimeMillis();
|
|
|
|
|
@ -321,11 +355,7 @@ public class NoteEditActivity extends Activity implements OnClickListener,
|
|
|
|
|
@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
|
|
|
|
|
*/
|
|
|
|
|
// 如果是尚未保存的新便签,系统回收前先保存一次以生成 ID
|
|
|
|
|
if (!mWorkingNote.existInDatabase()) {
|
|
|
|
|
saveNote();
|
|
|
|
|
}
|
|
|
|
|
@ -333,6 +363,10 @@ public class NoteEditActivity extends Activity implements OnClickListener,
|
|
|
|
|
Log.d(TAG, "Save working note id: " + mWorkingNote.getNoteId() + " onSaveInstanceState");
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/**
|
|
|
|
|
* 处理点击事件分发
|
|
|
|
|
* 点击面板外部区域时自动关闭颜色选择器或字体选择器
|
|
|
|
|
*/
|
|
|
|
|
@Override
|
|
|
|
|
public boolean dispatchTouchEvent(MotionEvent ev) {
|
|
|
|
|
if (mNoteBgColorSelector.getVisibility() == View.VISIBLE
|
|
|
|
|
@ -358,8 +392,8 @@ public class NoteEditActivity extends Activity implements OnClickListener,
|
|
|
|
|
|| ev.getX() > (x + view.getWidth())
|
|
|
|
|
|| ev.getY() < y
|
|
|
|
|
|| ev.getY() > (y + view.getHeight())) {
|
|
|
|
|
return false;
|
|
|
|
|
}
|
|
|
|
|
return false;
|
|
|
|
|
}
|
|
|
|
|
return true;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
@ -386,26 +420,27 @@ public class NoteEditActivity extends Activity implements OnClickListener,
|
|
|
|
|
};
|
|
|
|
|
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);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/**
|
|
|
|
|
* 当 Activity 暂停时(如切到后台或锁屏),自动保存数据
|
|
|
|
|
*/
|
|
|
|
|
@Override
|
|
|
|
|
protected void onPause() {
|
|
|
|
|
super.onPause();
|
|
|
|
|
// saveNote() 方法已经过改造,内部使用线程池异步保存,不会阻塞主线程
|
|
|
|
|
if(saveNote()) {
|
|
|
|
|
Log.d(TAG, "Note data was saved with length:" + mWorkingNote.getContent().length());
|
|
|
|
|
}
|
|
|
|
|
clearSettingState();
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// 更新桌面 Widget 内容
|
|
|
|
|
private void updateWidget() {
|
|
|
|
|
Intent intent = new Intent(AppWidgetManager.ACTION_APPWIDGET_UPDATE);
|
|
|
|
|
if (mWorkingNote.getWidgetType() == Notes.TYPE_WIDGET_2X) {
|
|
|
|
|
@ -418,29 +453,61 @@ public class NoteEditActivity extends Activity implements OnClickListener,
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
intent.putExtra(AppWidgetManager.EXTRA_APPWIDGET_IDS, new int[] {
|
|
|
|
|
mWorkingNote.getWidgetId()
|
|
|
|
|
mWorkingNote.getWidgetId()
|
|
|
|
|
});
|
|
|
|
|
|
|
|
|
|
sendBroadcast(intent);
|
|
|
|
|
setResult(RESULT_OK, intent);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/**
|
|
|
|
|
* 全局点击事件处理
|
|
|
|
|
*/
|
|
|
|
|
public void onClick(View v) {
|
|
|
|
|
int id = v.getId();
|
|
|
|
|
|
|
|
|
|
// =============================================
|
|
|
|
|
// [新增] AI 智能助手入口
|
|
|
|
|
// 点击放大镜图标,弹出功能选择对话框
|
|
|
|
|
// =============================================
|
|
|
|
|
if (id == R.id.btn_ai_polish) {
|
|
|
|
|
new AlertDialog.Builder(this)
|
|
|
|
|
.setTitle("AI 助手")
|
|
|
|
|
.setItems(new String[]{"✨ 文本润色", "🏷️ 智能分类 & 打标签"}, new DialogInterface.OnClickListener() {
|
|
|
|
|
@Override
|
|
|
|
|
public void onClick(DialogInterface dialog, int which) {
|
|
|
|
|
if (which == 0) {
|
|
|
|
|
startDeepSeekOptimization(); // 功能1:润色
|
|
|
|
|
} else {
|
|
|
|
|
performAIClassification(); // 功能2:分类
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
})
|
|
|
|
|
.show();
|
|
|
|
|
return;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// 处理更改背景色按钮
|
|
|
|
|
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)) {
|
|
|
|
|
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)) {
|
|
|
|
|
}
|
|
|
|
|
// 处理具体字体大小点击
|
|
|
|
|
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());
|
|
|
|
|
@ -457,11 +524,11 @@ public class NoteEditActivity extends Activity implements OnClickListener,
|
|
|
|
|
if(clearSettingState()) {
|
|
|
|
|
return;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
saveNote();
|
|
|
|
|
super.onBackPressed();
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// 隐藏设置面板
|
|
|
|
|
private boolean clearSettingState() {
|
|
|
|
|
if (mNoteBgColorSelector.getVisibility() == View.VISIBLE) {
|
|
|
|
|
mNoteBgColorSelector.setVisibility(View.GONE);
|
|
|
|
|
@ -473,6 +540,7 @@ public class NoteEditActivity extends Activity implements OnClickListener,
|
|
|
|
|
return false;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// WorkingNote 回调:背景色变更时刷新界面
|
|
|
|
|
public void onBackgroundColorChanged() {
|
|
|
|
|
findViewById(sBgSelectorSelectionMap.get(mWorkingNote.getBgColorId())).setVisibility(
|
|
|
|
|
View.VISIBLE);
|
|
|
|
|
@ -487,11 +555,18 @@ public class NoteEditActivity extends Activity implements OnClickListener,
|
|
|
|
|
}
|
|
|
|
|
clearSettingState();
|
|
|
|
|
menu.clear();
|
|
|
|
|
// 根据文件夹类型加载不同菜单
|
|
|
|
|
if (mWorkingNote.getFolderId() == Notes.ID_CALL_RECORD_FOLDER) {
|
|
|
|
|
getMenuInflater().inflate(R.menu.call_note_edit, menu);
|
|
|
|
|
} else {
|
|
|
|
|
getMenuInflater().inflate(R.menu.note_edit, menu);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// 兼容性代码:虽然已通过 XML 添加按钮,但保留此代码以支持某些旧设备的 Menu 键
|
|
|
|
|
menu.add(0, MENU_AI_OPT_ID, 1, "AI 润色")
|
|
|
|
|
.setIcon(android.R.drawable.ic_menu_search)
|
|
|
|
|
.setShowAsAction(MenuItem.SHOW_AS_ACTION_ALWAYS);
|
|
|
|
|
|
|
|
|
|
if (mWorkingNote.getCheckListMode() == TextNote.MODE_CHECK_LIST) {
|
|
|
|
|
menu.findItem(R.id.menu_list_mode).setTitle(R.string.menu_normal_mode);
|
|
|
|
|
} else {
|
|
|
|
|
@ -508,6 +583,9 @@ public class NoteEditActivity extends Activity implements OnClickListener,
|
|
|
|
|
@Override
|
|
|
|
|
public boolean onOptionsItemSelected(MenuItem item) {
|
|
|
|
|
switch (item.getItemId()) {
|
|
|
|
|
case MENU_AI_OPT_ID: // 菜单栏的 AI 选项
|
|
|
|
|
startDeepSeekOptimization();
|
|
|
|
|
return true;
|
|
|
|
|
case R.id.menu_new_note:
|
|
|
|
|
createNewNote();
|
|
|
|
|
break;
|
|
|
|
|
@ -553,6 +631,7 @@ public class NoteEditActivity extends Activity implements OnClickListener,
|
|
|
|
|
return true;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// 弹出时间选择器设置提醒
|
|
|
|
|
private void setReminder() {
|
|
|
|
|
DateTimePickerDialog d = new DateTimePickerDialog(this, System.currentTimeMillis());
|
|
|
|
|
d.setOnDateTimeSetListener(new OnDateTimeSetListener() {
|
|
|
|
|
@ -563,10 +642,7 @@ public class NoteEditActivity extends Activity implements OnClickListener,
|
|
|
|
|
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.putExtra(Intent.EXTRA_TEXT, info);
|
|
|
|
|
@ -574,11 +650,9 @@ public class NoteEditActivity extends Activity implements OnClickListener,
|
|
|
|
|
context.startActivity(intent);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// 保存当前并创建新便签
|
|
|
|
|
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);
|
|
|
|
|
@ -586,6 +660,7 @@ public class NoteEditActivity extends Activity implements OnClickListener,
|
|
|
|
|
startActivity(intent);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// 删除便签
|
|
|
|
|
private void deleteCurrentNote() {
|
|
|
|
|
if (mWorkingNote.existInDatabase()) {
|
|
|
|
|
HashSet<Long> ids = new HashSet<Long>();
|
|
|
|
|
@ -613,10 +688,6 @@ public class NoteEditActivity extends Activity implements OnClickListener,
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
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();
|
|
|
|
|
}
|
|
|
|
|
@ -632,11 +703,6 @@ public class NoteEditActivity extends Activity implements OnClickListener,
|
|
|
|
|
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);
|
|
|
|
|
}
|
|
|
|
|
@ -646,6 +712,7 @@ public class NoteEditActivity extends Activity implements OnClickListener,
|
|
|
|
|
updateWidget();
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// 处理清单项删除
|
|
|
|
|
public void onEditTextDelete(int index, String text) {
|
|
|
|
|
int childCount = mEditTextList.getChildCount();
|
|
|
|
|
if (childCount == 1) {
|
|
|
|
|
@ -672,10 +739,8 @@ public class NoteEditActivity extends Activity implements OnClickListener,
|
|
|
|
|
edit.setSelection(length);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// 处理清单项回车(新增一行)
|
|
|
|
|
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");
|
|
|
|
|
}
|
|
|
|
|
@ -691,6 +756,7 @@ public class NoteEditActivity extends Activity implements OnClickListener,
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// 切换到清单模式:将文本拆分为列表项
|
|
|
|
|
private void switchToListMode(String text) {
|
|
|
|
|
mEditTextList.removeAllViews();
|
|
|
|
|
String[] items = text.split("\n");
|
|
|
|
|
@ -708,6 +774,7 @@ public class NoteEditActivity extends Activity implements OnClickListener,
|
|
|
|
|
mEditTextList.setVisibility(View.VISIBLE);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// 获取带有高亮的 Spannable 文本 (用于搜索结果高亮)
|
|
|
|
|
private Spannable getHighlightQueryResult(String fullText, String userQuery) {
|
|
|
|
|
SpannableString spannable = new SpannableString(fullText == null ? "" : fullText);
|
|
|
|
|
if (!TextUtils.isEmpty(userQuery)) {
|
|
|
|
|
@ -725,6 +792,7 @@ public class NoteEditActivity extends Activity implements OnClickListener,
|
|
|
|
|
return spannable;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// 生成清单列表项 View
|
|
|
|
|
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);
|
|
|
|
|
@ -805,28 +873,17 @@ public class NoteEditActivity extends Activity implements OnClickListener,
|
|
|
|
|
return hasChecked;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// 触发 WorkingNote 保存数据
|
|
|
|
|
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();
|
|
|
|
|
}
|
|
|
|
|
@ -846,11 +903,6 @@ public class NoteEditActivity extends Activity implements OnClickListener,
|
|
|
|
|
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);
|
|
|
|
|
}
|
|
|
|
|
@ -870,4 +922,119 @@ public class NoteEditActivity extends Activity implements OnClickListener,
|
|
|
|
|
private void showToast(int resId, int duration) {
|
|
|
|
|
Toast.makeText(this, resId, duration).show();
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// =================================================================================
|
|
|
|
|
// [新功能] 启动 DeepSeek 文本润色流程
|
|
|
|
|
// 逻辑:获取文本 -> 显示Loading -> 调用AIService -> 成功后弹窗询问是否替换
|
|
|
|
|
// =================================================================================
|
|
|
|
|
private void startDeepSeekOptimization() {
|
|
|
|
|
// 1. 获取当前编辑框里的文字
|
|
|
|
|
final String currentText = mNoteEditor.getText().toString();
|
|
|
|
|
|
|
|
|
|
if (currentText.trim().length() == 0) {
|
|
|
|
|
Toast.makeText(this, "请先写点内容再让 AI 润色哦~", Toast.LENGTH_SHORT).show();
|
|
|
|
|
return;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// 2. 弹出一个加载框 (Loading)
|
|
|
|
|
final ProgressDialog progressDialog = new ProgressDialog(this);
|
|
|
|
|
progressDialog.setMessage("DeepSeek 正在思考中...");
|
|
|
|
|
progressDialog.setCancelable(false);
|
|
|
|
|
progressDialog.show();
|
|
|
|
|
|
|
|
|
|
// 3. 呼叫后台的 AI 服务
|
|
|
|
|
AIService.callDeepSeek(currentText, new AIService.AIResultCallback() {
|
|
|
|
|
@Override
|
|
|
|
|
public void onSuccess(final String result) {
|
|
|
|
|
progressDialog.dismiss();
|
|
|
|
|
|
|
|
|
|
// 4. 弹出结果对话框
|
|
|
|
|
new AlertDialog.Builder(NoteEditActivity.this)
|
|
|
|
|
.setTitle("✨ DeepSeek 优化结果")
|
|
|
|
|
.setMessage(result)
|
|
|
|
|
.setPositiveButton("替换原文", new DialogInterface.OnClickListener() {
|
|
|
|
|
@Override
|
|
|
|
|
public void onClick(DialogInterface dialog, int which) {
|
|
|
|
|
mNoteEditor.setText(result);
|
|
|
|
|
mNoteEditor.setSelection(result.length());
|
|
|
|
|
mWorkingNote.setWorkingText(result); // 触发保存
|
|
|
|
|
}
|
|
|
|
|
})
|
|
|
|
|
.setNegativeButton("添加到末尾", new DialogInterface.OnClickListener() {
|
|
|
|
|
@Override
|
|
|
|
|
public void onClick(DialogInterface dialog, int which) {
|
|
|
|
|
String newText = currentText + "\n\n--- AI 建议 ---\n" + result;
|
|
|
|
|
mNoteEditor.setText(newText);
|
|
|
|
|
mNoteEditor.setSelection(newText.length());
|
|
|
|
|
mWorkingNote.setWorkingText(newText);
|
|
|
|
|
}
|
|
|
|
|
})
|
|
|
|
|
.setNeutralButton("取消", null)
|
|
|
|
|
.show();
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
@Override
|
|
|
|
|
public void onError(final String error) {
|
|
|
|
|
progressDialog.dismiss();
|
|
|
|
|
Toast.makeText(NoteEditActivity.this, "AI 罢工了: " + error, Toast.LENGTH_LONG).show();
|
|
|
|
|
}
|
|
|
|
|
});
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// =================================================================================
|
|
|
|
|
// [新功能] 启动 AI 智能分类流程
|
|
|
|
|
// 逻辑:获取文本 -> 调用AIService分类接口 -> 解析JSON -> 自动设置背景色和插入标签
|
|
|
|
|
// =================================================================================
|
|
|
|
|
private void performAIClassification() {
|
|
|
|
|
final String content = mNoteEditor.getText().toString();
|
|
|
|
|
if (content.trim().length() == 0) {
|
|
|
|
|
Toast.makeText(this, "内容为空,无法分类", Toast.LENGTH_SHORT).show();
|
|
|
|
|
return;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
final ProgressDialog pd = ProgressDialog.show(this, "DeepSeek", "正在分析内容并分类...", true, false);
|
|
|
|
|
|
|
|
|
|
AIService.classifyNote(content, new AIService.AIResultCallback() {
|
|
|
|
|
@Override
|
|
|
|
|
public void onSuccess(String result) {
|
|
|
|
|
pd.dismiss();
|
|
|
|
|
try {
|
|
|
|
|
// 1. 解析 DeepSeek 返回的 JSON 格式数据
|
|
|
|
|
JSONObject json = new JSONObject(result);
|
|
|
|
|
int colorId = json.optInt("color_id", 0); // 获取建议颜色
|
|
|
|
|
JSONArray tags = json.optJSONArray("tags"); // 获取建议标签
|
|
|
|
|
|
|
|
|
|
// 2. 自动改变便签背景颜色
|
|
|
|
|
mWorkingNote.setBgColorId(colorId);
|
|
|
|
|
mNoteBgColorSelector.setVisibility(View.GONE);
|
|
|
|
|
|
|
|
|
|
// 3. 将标签插入到正文头部
|
|
|
|
|
String tagString = "";
|
|
|
|
|
if (tags != null) {
|
|
|
|
|
for (int i = 0; i < tags.length(); i++) {
|
|
|
|
|
tagString += "【" + tags.getString(i) + "】";
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
String newText = tagString + "\n" + content;
|
|
|
|
|
mNoteEditor.setText(newText);
|
|
|
|
|
mWorkingNote.setWorkingText(newText); // 触发保存
|
|
|
|
|
|
|
|
|
|
// 4. Toast 提示用户分类结果
|
|
|
|
|
String[] categories = {"生活", "紧急", "工作", "旅行", "灵感"};
|
|
|
|
|
String catName = (colorId >= 0 && colorId < categories.length) ? categories[colorId] : "未知";
|
|
|
|
|
Toast.makeText(NoteEditActivity.this, "已分类为:" + catName, Toast.LENGTH_LONG).show();
|
|
|
|
|
|
|
|
|
|
} catch (Exception e) {
|
|
|
|
|
Toast.makeText(NoteEditActivity.this, "AI 返回格式错误: " + result, Toast.LENGTH_LONG).show();
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
@Override
|
|
|
|
|
public void onError(String error) {
|
|
|
|
|
pd.dismiss();
|
|
|
|
|
Toast.makeText(NoteEditActivity.this, error, Toast.LENGTH_SHORT).show();
|
|
|
|
|
}
|
|
|
|
|
});
|
|
|
|
|
}
|
|
|
|
|
}
|