mTagDataIdList; // 标签数据ID列表
- /** 构造函数:初始化两个数据集与对应 ID */
+ /** 构造函数:初始化数据集与对应 ID */
public NoteData() {
mTextDataValues = new ContentValues();
mCallDataValues = new ContentValues();
mTextDataId = 0;
mCallDataId = 0;
+ // 初始化标签相关数据
+ mTagDataValuesList = new ArrayList<>();
+ mTagDataIdList = new ArrayList<>();
}
/**
@@ -219,7 +242,7 @@ public class Note {
* @return 如果笔记数据在本地被修改,返回true,否则返回false
*/
boolean isLocalModified() {
- return mTextDataValues.size() > 0 || mCallDataValues.size() > 0;
+ return mTextDataValues.size() > 0 || mCallDataValues.size() > 0 || mTagDataValuesList.size() > 0;
}
/**
@@ -267,6 +290,28 @@ public class Note {
mNoteDiffValues.put(NoteColumns.LOCAL_MODIFIED, 1);
mNoteDiffValues.put(NoteColumns.MODIFIED_DATE, System.currentTimeMillis());
}
+
+ /**
+ * 添加标签数据
+ * @param tagName 标签名称
+ */
+ void addTagData(String tagName) {
+ ContentValues tagValues = new ContentValues();
+ tagValues.put(TagNote.TAG_NAME, tagName);
+ mTagDataValuesList.add(tagValues);
+ mNoteDiffValues.put(NoteColumns.LOCAL_MODIFIED, 1);
+ mNoteDiffValues.put(NoteColumns.MODIFIED_DATE, System.currentTimeMillis());
+ }
+
+ /**
+ * 清空所有标签数据
+ */
+ void clearTagData() {
+ mTagDataValuesList.clear();
+ mTagDataIdList.clear();
+ mNoteDiffValues.put(NoteColumns.LOCAL_MODIFIED, 1);
+ mNoteDiffValues.put(NoteColumns.MODIFIED_DATE, System.currentTimeMillis());
+ }
/**
* 将笔记数据推入内容解析器
@@ -330,6 +375,29 @@ public class Note {
mCallDataValues.clear();
}
+ // 处理标签数据
+ if (mTagDataValuesList.size() > 0) {
+ // 先删除现有标签数据
+ String selection = DataColumns.NOTE_ID + "=? AND " + DataColumns.MIME_TYPE + "=?";
+ String[] selectionArgs = {String.valueOf(noteId), TagNote.CONTENT_ITEM_TYPE};
+ context.getContentResolver().delete(Notes.CONTENT_DATA_URI, selection, selectionArgs);
+
+ // 插入新标签数据
+ for (ContentValues tagValues : mTagDataValuesList) {
+ tagValues.put(DataColumns.NOTE_ID, noteId);
+ tagValues.put(DataColumns.MIME_TYPE, TagNote.CONTENT_ITEM_TYPE);
+ Uri uri = context.getContentResolver().insert(Notes.CONTENT_DATA_URI, tagValues);
+ try {
+ long tagDataId = Long.valueOf(uri.getPathSegments().get(1));
+ mTagDataIdList.add(tagDataId);
+ } catch (NumberFormatException e) {
+ Log.e(TAG, "Insert new tag data fail with noteId" + noteId);
+ return null;
+ }
+ }
+ mTagDataValuesList.clear();
+ }
+
if (operationList.size() > 0) {
try {
ContentProviderResult[] results = context.getContentResolver().applyBatch(
diff --git a/src/Notes-master/src/net/micode/notes/model/WorkingNote.java b/src/Notes-master/src/net/micode/notes/model/WorkingNote.java
index 1c54eab..471a0a7 100644
--- a/src/Notes-master/src/net/micode/notes/model/WorkingNote.java
+++ b/src/Notes-master/src/net/micode/notes/model/WorkingNote.java
@@ -162,7 +162,7 @@ public class WorkingNote {
/**
* 加载笔记的数据内容(data 表)。
- * 遍历该笔记的所有数据行:找到文本行(NOTE)记录内容与模式;找到通话行(CALL_NOTE)记录其数据ID。
+ * 遍历该笔记的所有数据行:找到文本行(NOTE)记录内容与模式;找到通话行(CALL_NOTE)记录其数据ID;找到标签行(TAG)记录标签。
*/
private void loadNoteData() {
Cursor cursor = mContext.getContentResolver().query(Notes.CONTENT_DATA_URI, DATA_PROJECTION,
@@ -180,6 +180,10 @@ public class WorkingNote {
mNote.setTextDataId(cursor.getLong(DATA_ID_COLUMN));
} else if (DataConstants.CALL_NOTE.equals(type)) {
mNote.setCallDataId(cursor.getLong(DATA_ID_COLUMN));
+ } else if (DataConstants.TAG.equals(type)) {
+ // 加载标签数据
+ String tagName = cursor.getString(DATA_CONTENT_COLUMN);
+ mNote.addTagData(tagName);
} else {
Log.d(TAG, "Wrong note type with type:" + type);
}
@@ -529,6 +533,21 @@ public class WorkingNote {
}
return encrypted;
}
+
+ /**
+ * 添加标签
+ * @param tagName 标签名称
+ */
+ public void addTag(String tagName) {
+ mNote.addTagData(tagName);
+ }
+
+ /**
+ * 清空所有标签
+ */
+ public void clearTags() {
+ mNote.clearTagData();
+ }
/**
* 笔记设置变化监听器
diff --git a/src/Notes-master/src/net/micode/notes/ui/AlarmAlertActivity.java b/src/Notes-master/src/net/micode/notes/ui/AlarmAlertActivity.java
index 608db9f..dbdd83c 100644
--- a/src/Notes-master/src/net/micode/notes/ui/AlarmAlertActivity.java
+++ b/src/Notes-master/src/net/micode/notes/ui/AlarmAlertActivity.java
@@ -36,6 +36,7 @@ import android.view.WindowManager;
import net.micode.notes.R;
import net.micode.notes.data.Notes;
import net.micode.notes.tool.DataUtils;
+import net.micode.notes.ui.NoteEditActivity;
import java.io.IOException;
diff --git a/src/Notes-master/src/net/micode/notes/ui/NoteEditActivity.java b/src/Notes-master/src/net/micode/notes/ui/NoteEditActivity.java
index e2c840c..4d0e7b6 100644
--- a/src/Notes-master/src/net/micode/notes/ui/NoteEditActivity.java
+++ b/src/Notes-master/src/net/micode/notes/ui/NoteEditActivity.java
@@ -28,6 +28,7 @@ import android.content.DialogInterface;
import android.content.Intent;
import android.content.SharedPreferences;
import android.content.pm.PackageManager;
+import android.database.Cursor;
import android.graphics.Paint;
import android.net.Uri;
import android.os.Build;
@@ -46,9 +47,14 @@ import android.view.LayoutInflater;
import android.view.Menu;
import android.view.MenuItem;
import android.view.MotionEvent;
+import android.view.Gravity;
import android.view.View;
import android.view.View.OnClickListener;
+import android.view.View.OnTouchListener;
+import android.view.ViewGroup;
import android.view.WindowManager;
+import android.util.DisplayMetrics;
+import android.widget.HorizontalScrollView;
import java.io.File;
import java.io.FileInputStream;
import java.io.IOException;
@@ -57,6 +63,7 @@ import android.util.Base64;
import android.widget.CheckBox;
import android.widget.CompoundButton;
import android.widget.CompoundButton.OnCheckedChangeListener;
+import android.widget.Button;
import android.widget.EditText;
import android.widget.ImageButton;
import android.widget.ImageView;
@@ -72,14 +79,21 @@ import android.content.res.Resources;
// import android.support.annotation.NonNull;
import android.Manifest;
import android.graphics.Color;
-import java.io.InputStream;
-import java.io.FileOutputStream;
-import java.io.OutputStream;
import android.os.Environment;
import android.text.format.DateUtils;
import android.view.ViewGroup;
import android.content.ActivityNotFoundException;
+import java.io.InputStream;
+import java.io.FileOutputStream;
+import java.io.OutputStream;
+import java.util.ArrayList;
+import java.util.HashMap;
+import java.util.HashSet;
+import java.util.Map;
+import java.util.regex.Matcher;
+import java.util.regex.Pattern;
+
import jp.wasabeef.richeditor.RichEditor;
import net.micode.notes.R;
@@ -96,12 +110,6 @@ 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;
-
/**
* 笔记编辑活动
@@ -181,6 +189,7 @@ public class NoteEditActivity extends Activity implements OnClickListener,
private View mFontSizeSelector; // 字体大小选择器
private RichEditor mNoteEditor; // 富文本编辑器
private View mNoteEditorPanel; // 笔记编辑器面板
+ private HorizontalScrollView mFloatingToolbar; // 浮动富文本工具栏
private WorkingNote mWorkingNote; // 工作笔记对象
private SharedPreferences mSharedPrefs; // 共享偏好设置
private int mFontSizeId; // 字体大小ID
@@ -198,6 +207,18 @@ public class NoteEditActivity extends Activity implements OnClickListener,
private String mUserQuery; // 用户查询字符串
private Pattern mPattern; // 正则表达式模式(用于高亮查询结果)
private ChecklistManager mChecklistManager; // 清单管理器(OMO)
+
+ // 标签管理相关控件
+ private LinearLayout mTagManagementSection; // 标签管理区域
+ private LinearLayout mExistingTagsContainer; // 现有标签容器
+ private LinearLayout mBottomTagsContainer; // 底部标签显示容器
+ private EditText mTagInput; // 标签输入框
+ private Button mAddTagButton; // 添加标签按钮
+ private ArrayList mTagsList; // 标签列表
+
+ // 搜索历史相关
+ private static final String PREFERENCE_SEARCH_HISTORY = "search_history"; // 搜索历史偏好设置键
+ private static final int MAX_SEARCH_HISTORY = 20; // 最大搜索历史记录数
/**
* 创建活动时调用
@@ -275,6 +296,11 @@ public class NoteEditActivity extends Activity implements OnClickListener,
return false;
}
}
+
+ // 保存搜索历史
+ if (!TextUtils.isEmpty(mUserQuery)) {
+ saveSearchHistory(mUserQuery);
+ }
getWindow().setSoftInputMode(
WindowManager.LayoutParams.SOFT_INPUT_STATE_HIDDEN
| WindowManager.LayoutParams.SOFT_INPUT_ADJUST_RESIZE);
@@ -503,6 +529,9 @@ public class NoteEditActivity extends Activity implements OnClickListener,
* is not ready
*/
showAlertHeader(); // 显示提醒头部
+
+ // 加载现有标签
+ loadExistingTags();
}
/**
@@ -736,6 +765,12 @@ public class NoteEditActivity extends Activity implements OnClickListener,
Log.e(TAG, "RichEditor is null! Check layout file.");
return;
}
+
+ // 初始化浮动富文本工具栏
+ mFloatingToolbar = findViewById(R.id.floating_editor_toolbar);
+ if (mFloatingToolbar != null) {
+ setupFloatingToolbarDrag();
+ }
// 初始化富文本编辑器配置
initRichEditor();
@@ -798,6 +833,9 @@ public class NoteEditActivity extends Activity implements OnClickListener,
mBtnInsertImage.setOnClickListener(this);
}
+ // 初始化标签管理相关控件
+ initTagManagement();
+
// 初始化富文本功能按钮
initRichEditorButtons();
}
@@ -1001,11 +1039,28 @@ public class NoteEditActivity extends Activity implements OnClickListener,
case R.id.menu_delete_remind:
mWorkingNote.setAlertDate(0, false); // 删除提醒
break;
+ case R.id.menu_tag_management:
+ // 切换标签管理界面显示
+ toggleTagManagement();
+ break;
default:
break;
}
return true;
}
+
+ /**
+ * 切换标签管理界面的显示/隐藏
+ */
+ private void toggleTagManagement() {
+ if (mTagManagementSection.getVisibility() == View.VISIBLE) {
+ // 隐藏标签管理界面
+ mTagManagementSection.setVisibility(View.GONE);
+ } else {
+ // 显示标签管理界面
+ mTagManagementSection.setVisibility(View.VISIBLE);
+ }
+ }
/**
* 设置提醒
@@ -1403,6 +1458,10 @@ public class NoteEditActivity extends Activity implements OnClickListener,
*/
private boolean saveNote() {
getWorkingText(); // 获取当前工作文本
+
+ // 保存标签数据
+ saveTagsToNote();
+
boolean saved = mWorkingNote.saveNote(); // 保存笔记到数据库
if (saved) {
/**
@@ -1416,6 +1475,69 @@ public class NoteEditActivity extends Activity implements OnClickListener,
}
return saved;
}
+
+ /**
+ * 将标签保存到笔记对象中
+ */
+ private void saveTagsToNote() {
+ // 清空现有标签
+ mWorkingNote.clearTags();
+
+ // 添加所有标签
+ for (String tag : mTagsList) {
+ mWorkingNote.addTag(tag);
+ }
+ }
+
+ /**
+ * 保存搜索历史记录
+ * @param query 搜索关键词
+ */
+ private void saveSearchHistory(String query) {
+ if (TextUtils.isEmpty(query)) {
+ return;
+ }
+
+ // 获取SharedPreferences
+ SharedPreferences sp = PreferenceManager.getDefaultSharedPreferences(this);
+
+ // 获取现有的搜索历史
+ String historyString = sp.getString(PREFERENCE_SEARCH_HISTORY, "");
+
+ // 将搜索历史拆分为列表
+ ArrayList historyList = new ArrayList<>();
+ if (!TextUtils.isEmpty(historyString)) {
+ String[] historyArray = historyString.split(",");
+ for (String item : historyArray) {
+ if (!TextUtils.isEmpty(item) && !historyList.contains(item)) {
+ historyList.add(item);
+ }
+ }
+ }
+
+ // 如果搜索关键词已存在,先移除
+ historyList.remove(query);
+
+ // 将新的搜索关键词添加到列表开头
+ historyList.add(0, query);
+
+ // 限制搜索历史记录数量
+ if (historyList.size() > MAX_SEARCH_HISTORY) {
+ historyList = new ArrayList<>(historyList.subList(0, MAX_SEARCH_HISTORY));
+ }
+
+ // 将搜索历史列表转换为字符串,用逗号分隔
+ StringBuilder sb = new StringBuilder();
+ for (int i = 0; i < historyList.size(); i++) {
+ if (i > 0) {
+ sb.append(",");
+ }
+ sb.append(historyList.get(i));
+ }
+
+ // 保存到SharedPreferences
+ sp.edit().putString(PREFERENCE_SEARCH_HISTORY, sb.toString()).apply();
+ }
/**
* 发送到桌面
@@ -1685,7 +1807,264 @@ public class NoteEditActivity extends Activity implements OnClickListener,
mNoteEditor.setEditorFontSize(18); // 默认值
}
}
-
+
+ /**
+ * 初始化标签管理相关控件和事件监听器
+ */
+ private void initTagManagement() {
+ // 初始化标签管理相关控件
+ mTagManagementSection = findViewById(R.id.tag_management_section);
+ mExistingTagsContainer = findViewById(R.id.existing_tags_container);
+ mTagInput = findViewById(R.id.et_tag_input);
+ mAddTagButton = findViewById(R.id.btn_add_tag);
+
+ // 初始化标签列表
+ mTagsList = new ArrayList<>();
+
+ // 设置添加标签按钮的点击事件
+ mAddTagButton.setOnClickListener(new OnClickListener() {
+ @Override
+ public void onClick(View v) {
+ addTag();
+ }
+ });
+
+ // 加载现有标签
+ loadExistingTags();
+ }
+
+ /**
+ * 添加标签
+ */
+ private void addTag() {
+ String tagName = mTagInput.getText().toString().trim();
+
+ // 检查标签是否有效
+ if (isValidTag(tagName)) {
+ // 添加标签到列表
+ mTagsList.add(tagName);
+
+ // 更新UI显示
+ updateTagsDisplay();
+
+ // 清空输入框
+ mTagInput.setText("");
+ }
+ }
+
+ /**
+ * 检查标签是否有效
+ * @param tagName 标签名称
+ * @return 标签是否有效
+ */
+ private boolean isValidTag(String tagName) {
+ // 检查标签是否为空
+ if (TextUtils.isEmpty(tagName)) {
+ Toast.makeText(this, "标签不能为空", Toast.LENGTH_SHORT).show();
+ return false;
+ }
+
+ // 检查标签长度
+ if (tagName.length() < 1 || tagName.length() > 15) {
+ Toast.makeText(this, "标签长度必须在1-15个字符之间", Toast.LENGTH_SHORT).show();
+ return false;
+ }
+
+ // 检查标签数量是否超过限制
+ if (mTagsList.size() >= 5) {
+ Toast.makeText(this, "每个笔记最多只能添加5个标签", Toast.LENGTH_SHORT).show();
+ return false;
+ }
+
+ // 检查标签是否已存在
+ if (mTagsList.contains(tagName)) {
+ Toast.makeText(this, "该标签已存在", Toast.LENGTH_SHORT).show();
+ return false;
+ }
+
+ return true;
+ }
+
+ /**
+ * 更新标签显示
+ */
+ private void updateTagsDisplay() {
+ // 清空现有标签容器
+ mExistingTagsContainer.removeAllViews();
+
+ // 添加所有标签到容器
+ for (final String tag : mTagsList) {
+ // 创建标签管理区域的标签视图
+ LinearLayout tagView = new LinearLayout(this);
+ LinearLayout.LayoutParams tagViewParams = new LinearLayout.LayoutParams(
+ LinearLayout.LayoutParams.WRAP_CONTENT,
+ 36);
+ tagViewParams.setMargins(0, 0, 8, 0);
+ tagView.setLayoutParams(tagViewParams);
+ tagView.setOrientation(LinearLayout.HORIZONTAL);
+ tagView.setGravity(Gravity.CENTER_VERTICAL);
+ tagView.setPadding(12, 0, 8, 0);
+ tagView.setBackgroundResource(R.drawable.bg_color_btn_mask);
+ tagView.setClickable(true);
+ tagView.setFocusable(true);
+
+ // 创建标签文本
+ TextView tagText = new TextView(this);
+ tagText.setText(tag);
+ tagText.setTextAppearance(this, R.style.TextAppearancePrimaryItem);
+ tagText.setTextColor(Color.WHITE);
+ tagText.setPadding(0, 0, 8, 0);
+ tagText.setSingleLine(true);
+ tagText.setTextSize(14);
+
+ // 创建删除按钮
+ ImageView deleteButton = new ImageView(this);
+ LinearLayout.LayoutParams deleteParams = new LinearLayout.LayoutParams(
+ 24, 24);
+ deleteButton.setLayoutParams(deleteParams);
+ deleteButton.setImageResource(android.R.drawable.ic_delete);
+ deleteButton.setScaleType(ImageView.ScaleType.CENTER_INSIDE);
+ deleteButton.setClickable(true);
+ deleteButton.setFocusable(true);
+ deleteButton.setTag(tag);
+
+ // 设置删除按钮点击事件
+ deleteButton.setOnClickListener(new OnClickListener() {
+ @Override
+ public void onClick(View v) {
+ String tagToRemove = (String) v.getTag();
+ removeTag(tagToRemove);
+ }
+ });
+
+ // 添加控件到标签视图
+ tagView.addView(tagText);
+ tagView.addView(deleteButton);
+
+ // 添加标签视图到容器
+ mExistingTagsContainer.addView(tagView);
+ }
+ }
+
+ /**
+ * 移除标签
+ * @param tagName 要移除的标签名称
+ */
+ private void removeTag(String tagName) {
+ mTagsList.remove(tagName);
+ updateTagsDisplay();
+ }
+
+ /**
+ * 设置浮动富文本工具栏的拖拽功能
+ */
+ private void setupFloatingToolbarDrag() {
+ // 记录拖拽状态
+ final boolean[] isDragging = {false};
+ final int[] lastX = {0};
+ final int[] lastY = {0};
+
+ // 设置触摸监听器
+ mFloatingToolbar.setOnTouchListener(new OnTouchListener() {
+ @Override
+ public boolean onTouch(View v, MotionEvent event) {
+ int x = (int) event.getRawX();
+ int y = (int) event.getRawY();
+
+ switch (event.getAction()) {
+ case MotionEvent.ACTION_DOWN:
+ // 记录初始触摸位置
+ isDragging[0] = false;
+ lastX[0] = x;
+ lastY[0] = y;
+ break;
+ case MotionEvent.ACTION_MOVE:
+ // 计算移动距离
+ int deltaX = x - lastX[0];
+ int deltaY = y - lastY[0];
+
+ // 判断是否开始拖拽
+ if (Math.abs(deltaX) > 5 || Math.abs(deltaY) > 5) {
+ isDragging[0] = true;
+ }
+
+ if (isDragging[0]) {
+ // 更新工具栏位置
+ ViewGroup.MarginLayoutParams params = (ViewGroup.MarginLayoutParams) v.getLayoutParams();
+
+ // 计算新的位置
+ int newLeftMargin = params.leftMargin + deltaX;
+ int newTopMargin = params.topMargin + deltaY;
+
+ // 限制位置在屏幕范围内
+ DisplayMetrics displayMetrics = new DisplayMetrics();
+ getWindowManager().getDefaultDisplay().getMetrics(displayMetrics);
+ int screenWidth = displayMetrics.widthPixels;
+ int screenHeight = displayMetrics.heightPixels;
+
+ // 计算工具栏宽度和高度
+ int toolbarWidth = v.getWidth();
+ int toolbarHeight = v.getHeight();
+
+ // 限制左边界
+ newLeftMargin = Math.max(0, Math.min(newLeftMargin, screenWidth - toolbarWidth));
+ // 限制上边界
+ newTopMargin = Math.max(0, Math.min(newTopMargin, screenHeight - toolbarHeight));
+
+ // 更新布局参数
+ params.leftMargin = newLeftMargin;
+ params.topMargin = newTopMargin;
+ v.setLayoutParams(params);
+
+ // 更新最后触摸位置
+ lastX[0] = x;
+ lastY[0] = y;
+
+ return true; // 消耗事件
+ }
+ break;
+ case MotionEvent.ACTION_UP:
+ case MotionEvent.ACTION_CANCEL:
+ // 结束拖拽
+ if (isDragging[0]) {
+ return true; // 消耗事件
+ }
+ break;
+ }
+
+ return false; // 不消耗事件,允许其他触摸事件处理
+ }
+ });
+ }
+
+ /**
+ * 加载现有标签
+ */
+ private void loadExistingTags() {
+ // 从数据库加载现有标签
+ if (mWorkingNote != null && mWorkingNote.getNoteId() > 0) {
+ Cursor cursor = getContentResolver().query(
+ Notes.CONTENT_DATA_URI,
+ new String[]{Notes.DataColumns.CONTENT},
+ Notes.DataColumns.NOTE_ID + "=? AND " + Notes.DataColumns.MIME_TYPE + "=?",
+ new String[]{String.valueOf(mWorkingNote.getNoteId()), Notes.DataConstants.TAG},
+ null);
+
+ if (cursor != null) {
+ while (cursor.moveToNext()) {
+ String tagName = cursor.getString(0);
+ if (!TextUtils.isEmpty(tagName) && !mTagsList.contains(tagName)) {
+ mTagsList.add(tagName);
+ }
+ }
+ cursor.close();
+ }
+
+ // 更新UI显示
+ updateTagsDisplay();
+ }
+ }
+
// 初始化富文本编辑器配置
private void initRichEditor() {
mNoteEditor.setEditorHeight(600); // 设置编辑器高度
@@ -1699,7 +2078,7 @@ public class NoteEditActivity extends Activity implements OnClickListener,
// 添加富文本功能按钮初始化方法
private void initRichEditorButtons() {
// 撤销功能
- findViewById(R.id.action_undo).setOnClickListener(new View.OnClickListener() {
+ findViewById(R.id.action_undo).setOnClickListener(new OnClickListener() {
@Override
public void onClick(View v) {
mNoteEditor.undo();
@@ -1707,7 +2086,7 @@ public class NoteEditActivity extends Activity implements OnClickListener,
});
// 加粗功能
- findViewById(R.id.action_bold).setOnClickListener(new View.OnClickListener() {
+ findViewById(R.id.action_bold).setOnClickListener(new OnClickListener() {
@Override
public void onClick(View v) {
mNoteEditor.setBold();
@@ -1715,7 +2094,7 @@ public class NoteEditActivity extends Activity implements OnClickListener,
});
// 斜体功能
- findViewById(R.id.action_italic).setOnClickListener(new View.OnClickListener() {
+ findViewById(R.id.action_italic).setOnClickListener(new OnClickListener() {
@Override
public void onClick(View v) {
mNoteEditor.setItalic();
@@ -1723,7 +2102,7 @@ public class NoteEditActivity extends Activity implements OnClickListener,
});
// 下划线功能
- findViewById(R.id.action_underline).setOnClickListener(new View.OnClickListener() {
+ findViewById(R.id.action_underline).setOnClickListener(new OnClickListener() {
@Override
public void onClick(View v) {
mNoteEditor.setUnderline();
diff --git a/src/Notes-master/src/net/micode/notes/ui/NoteItemData.java b/src/Notes-master/src/net/micode/notes/ui/NoteItemData.java
index ef52f3f..1d9185a 100644
--- a/src/Notes-master/src/net/micode/notes/ui/NoteItemData.java
+++ b/src/Notes-master/src/net/micode/notes/ui/NoteItemData.java
@@ -24,6 +24,7 @@ import net.micode.notes.data.Contact;
import net.micode.notes.data.Notes;
import net.micode.notes.data.Notes.NoteColumns;
import net.micode.notes.tool.DataUtils;
+import net.micode.notes.ui.NoteEditActivity;
/**
* 笔记项数据类
diff --git a/src/Notes-master/src/net/micode/notes/ui/NotesListActivity.java b/src/Notes-master/src/net/micode/notes/ui/NotesListActivity.java
index 1e14db4..be2f63a 100644
--- a/src/Notes-master/src/net/micode/notes/ui/NotesListActivity.java
+++ b/src/Notes-master/src/net/micode/notes/ui/NotesListActivity.java
@@ -20,6 +20,7 @@ import android.app.Activity;
import net.micode.notes.ui.PasswordInputActivity;
import android.app.AlertDialog;
import android.app.Dialog;
+import android.app.SearchManager;
import android.appwidget.AppWidgetManager;
import android.content.AsyncQueryHandler;
import android.content.ContentResolver;
@@ -41,6 +42,7 @@ import android.view.ContextMenu;
import android.view.ContextMenu.ContextMenuInfo;
import android.view.Display;
import android.view.HapticFeedbackConstants;
+import android.view.KeyEvent;
import android.view.LayoutInflater;
import android.view.Menu;
import android.view.MenuItem;
@@ -50,12 +52,16 @@ import android.view.View;
import android.view.View.OnClickListener;
import android.view.View.OnCreateContextMenuListener;
import android.view.View.OnTouchListener;
+import android.widget.TextView.OnEditorActionListener;
+import android.view.inputmethod.EditorInfo;
import android.view.inputmethod.InputMethodManager;
import android.widget.AdapterView;
import android.widget.AdapterView.OnItemClickListener;
import android.widget.AdapterView.OnItemLongClickListener;
import android.widget.Button;
import android.widget.EditText;
+import android.widget.ImageButton;
+import android.widget.LinearLayout;
import android.widget.ListView;
import android.widget.PopupMenu;
import android.widget.TextView;
@@ -65,6 +71,7 @@ import net.micode.notes.R;
import net.micode.notes.data.Notes;
import net.micode.notes.data.Notes.NoteColumns;
import net.micode.notes.data.Notes.TextNote;
+import net.micode.notes.data.NotesDatabaseHelper;
import net.micode.notes.gtask.remote.GTaskSyncService;
import net.micode.notes.model.ChecklistManager;
import net.micode.notes.model.WorkingNote;
@@ -80,9 +87,14 @@ import java.io.IOException;
import java.io.InputStream;
import java.io.InputStreamReader;
import java.util.HashSet;
+import java.util.ArrayList;
+import java.util.LinkedHashSet;
+import java.util.Set;
import android.os.Build;
+import android.view.Gravity;
import android.view.ViewConfiguration;
+import android.graphics.Color;
import java.lang.reflect.Field;
/**
@@ -99,6 +111,26 @@ public class NotesListActivity extends Activity implements OnClickListener, OnIt
private static final int MENU_FOLDER_VIEW = 1;
+ // 标签云相关控件
+ private LinearLayout mTagCloudSection; // 标签云区域
+ private LinearLayout mTagCloudContainer; // 标签云容器
+ private Button mAllNotesTag; // 全部笔记标签
+ private String mCurrentFilterTag; // 当前过滤标签
+
+ // 搜索历史相关
+ private static final String PREFERENCE_SEARCH_HISTORY = "search_history"; // 搜索历史偏好设置键
+ private static final int MAX_SEARCH_HISTORY = 20; // 最大搜索历史记录数
+ private String mSearchQuery; // 当前搜索查询字符串
+ private boolean mIsSearchMode; // 是否处于搜索模式
+
+ // 实时搜索相关
+ private LinearLayout mSearchBar; // 搜索栏容器
+ private EditText mSearchEditText; // 搜索输入框
+ private ImageButton mClearSearchButton; // 清除搜索按钮
+ private static final int SEARCH_DEBOUNCE_DELAY = 300; // 搜索防抖延迟时间(毫秒)
+ private Runnable mSearchRunnable; // 搜索任务
+ private long mLastSearchTime; // 上次搜索时间
+
private static final int MENU_FOLDER_CHANGE_NAME = 2;
private static final String PREFERENCE_ADD_INTRODUCTION = "net.micode.notes.introduction";
@@ -277,6 +309,103 @@ public class NotesListActivity extends Activity implements OnClickListener, OnIt
mIsChecklistMode = false;
updateModeButtons();
mChecklistManager = new ChecklistManager(mContentResolver);
+
+ // 初始化搜索相关控件
+ mSearchBar = (LinearLayout) findViewById(R.id.search_bar);
+ mSearchEditText = findViewById(R.id.et_search);
+ mClearSearchButton = (ImageButton) findViewById(R.id.btn_clear_search);
+
+ // 设置搜索监听器
+ initSearchListeners();
+
+ // 标签云功能已移除,相关代码已注释
+ // mTagCloudSection = findViewById(R.id.tag_cloud_section);
+ // mTagCloudContainer = findViewById(R.id.tag_cloud_container);
+ // mAllNotesTag = findViewById(R.id.tag_all_notes);
+ // mCurrentFilterTag = null;
+ //
+ // // 加载标签云
+ // loadTagCloud();
+ }
+
+ /**
+ * 初始化搜索相关监听器
+ */
+ private void initSearchListeners() {
+ // 初始化搜索任务
+ mSearchRunnable = new Runnable() {
+ @Override
+ public void run() {
+ // 执行搜索查询:如果已经处于搜索模式,只更新搜索结果,不退出搜索模式
+ // 这样可以确保搜索栏在用户输入时保持可见
+ if (mIsSearchMode) {
+ startAsyncNotesListQuery();
+ } else if (!TextUtils.isEmpty(mSearchQuery)) {
+ // 如果不在搜索模式但有搜索字符串,进入搜索模式
+ setSearchMode(true, mSearchQuery);
+ }
+ }
+ };
+
+ // 搜索输入框文本变化监听器
+ mSearchEditText.addTextChangedListener(new TextWatcher() {
+ @Override
+ public void beforeTextChanged(CharSequence s, int start, int count, int after) {
+ // 文本变化前的处理
+ }
+
+ @Override
+ public void onTextChanged(CharSequence s, int start, int before, int count) {
+ // 文本变化时的处理
+ mSearchQuery = s.toString();
+ Log.d(TAG, "Search text changed: '" + mSearchQuery + "'");
+ // 移除之前的搜索任务
+ mSearchEditText.removeCallbacks(mSearchRunnable);
+ // 延迟执行搜索任务
+ Log.d(TAG, "Posting search runnable with delay: " + SEARCH_DEBOUNCE_DELAY + "ms");
+ mSearchEditText.postDelayed(mSearchRunnable, SEARCH_DEBOUNCE_DELAY);
+ }
+
+ @Override
+ public void afterTextChanged(Editable s) {
+ // 文本变化后的处理
+ }
+ });
+
+ // 搜索输入框编辑器动作监听器(处理虚拟键盘上的搜索按钮)
+ mSearchEditText.setOnEditorActionListener(new TextView.OnEditorActionListener() {
+ @Override
+ public boolean onEditorAction(TextView v, int actionId, KeyEvent event) {
+ // 检查是否是搜索动作
+ if (actionId == EditorInfo.IME_ACTION_SEARCH) {
+ // 获取搜索查询
+ String query = mSearchEditText.getText().toString().trim();
+ if (!TextUtils.isEmpty(query)) {
+ // 设置搜索模式并执行搜索
+ setSearchMode(true, query);
+ // 保存搜索历史
+ saveSearchHistory(query);
+ // 隐藏软键盘
+ hideSoftInput(mSearchEditText);
+ }
+ return true;
+ }
+ return false;
+ }
+ });
+
+ // 清除搜索按钮点击监听器
+ mClearSearchButton.setOnClickListener(new OnClickListener() {
+ @Override
+ public void onClick(View v) {
+ // 清除搜索内容
+ mSearchEditText.setText("");
+ // 移除未执行的搜索任务
+ mSearchEditText.removeCallbacks(mSearchRunnable);
+ // 退出搜索模式
+ exitSearchMode();
+ }
+ });
}
/**
@@ -500,32 +629,189 @@ public class NotesListActivity extends Activity implements OnClickListener, OnIt
String selection;
String[] selectionArgs;
- if (mCurrentFolderId == Notes.ID_ROOT_FOLDER) {
- if (mIsChecklistMode) {
- // 根文件夹下的清单模式:只显示清单类型的笔记
- selection = "(" + NoteColumns.TYPE + "=" + Notes.TYPE_CHECKLIST + " AND " + NoteColumns.PARENT_ID + "=?)" +
+ // 基础查询条件
+ if (mIsSearchMode) {
+ // 搜索模式:忽略清单模式,搜索所有笔记和清单
+ if (mCurrentFolderId == Notes.ID_ROOT_FOLDER) {
+ // 根文件夹下的搜索:显示所有类型的笔记(普通笔记、清单、文件夹、通话记录文件夹)
+ selection = "(" + NoteColumns.TYPE + " IN (" + Notes.TYPE_NOTE + ", " + Notes.TYPE_CHECKLIST + ", " + Notes.TYPE_FOLDER + ") AND " + NoteColumns.PARENT_ID + "=?)" +
" OR (" + NoteColumns.ID + "=" + Notes.ID_CALL_RECORD_FOLDER + " AND " + NoteColumns.NOTES_COUNT + ">0)";
} else {
- // 根文件夹下的笔记模式:显示文件夹、普通笔记和非空通话记录文件夹
- selection = "(" + NoteColumns.TYPE + "<>" + Notes.TYPE_SYSTEM + " AND " + NoteColumns.PARENT_ID + "=?)" +
- " OR (" + NoteColumns.ID + "=" + Notes.ID_CALL_RECORD_FOLDER + " AND " + NoteColumns.NOTES_COUNT + ">0)";
+ // 普通文件夹下的搜索:显示所有类型的笔记(普通笔记和清单)
+ selection = NoteColumns.PARENT_ID + "=? AND " + NoteColumns.TYPE + " IN (" + Notes.TYPE_NOTE + ", " + Notes.TYPE_CHECKLIST + ")";
}
} else {
- if (mIsChecklistMode) {
- // 普通文件夹下的清单模式:只显示清单类型的笔记
- selection = NoteColumns.PARENT_ID + "=? AND " + NoteColumns.TYPE + "=" + Notes.TYPE_CHECKLIST;
+ // 非搜索模式:使用原来的逻辑
+ if (mCurrentFolderId == Notes.ID_ROOT_FOLDER) {
+ if (mIsChecklistMode) {
+ // 根文件夹下的清单模式:只显示清单类型的笔记
+ selection = "(" + NoteColumns.TYPE + "=" + Notes.TYPE_CHECKLIST + " AND " + NoteColumns.PARENT_ID + "=?)" +
+ " OR (" + NoteColumns.ID + "=" + Notes.ID_CALL_RECORD_FOLDER + " AND " + NoteColumns.NOTES_COUNT + ">0)";
+ } else {
+ // 根文件夹下的笔记模式:显示文件夹、普通笔记和非空通话记录文件夹
+ selection = "(" + NoteColumns.TYPE + "<>" + Notes.TYPE_SYSTEM + " AND " + NoteColumns.PARENT_ID + "=?)" +
+ " OR (" + NoteColumns.ID + "=" + Notes.ID_CALL_RECORD_FOLDER + " AND " + NoteColumns.NOTES_COUNT + ">0)";
+ }
} else {
- // 普通文件夹下的笔记模式:只显示普通笔记
- selection = NoteColumns.PARENT_ID + "=? AND " + NoteColumns.TYPE + "=" + Notes.TYPE_NOTE;
+ if (mIsChecklistMode) {
+ // 普通文件夹下的清单模式:只显示清单类型的笔记
+ selection = NoteColumns.PARENT_ID + "=? AND " + NoteColumns.TYPE + "=" + Notes.TYPE_CHECKLIST;
+ } else {
+ // 普通文件夹下的笔记模式:只显示普通笔记
+ selection = NoteColumns.PARENT_ID + "=? AND " + NoteColumns.TYPE + "=" + Notes.TYPE_NOTE;
+ }
}
}
+ // 添加搜索过滤条件
+ if (mIsSearchMode && !TextUtils.isEmpty(mSearchQuery)) {
+ // 搜索过滤需要使用子查询,因为内容存储在data表中
+ String searchFilter = " AND " + NoteColumns.ID + " IN (" +
+ "SELECT " + Notes.DataColumns.NOTE_ID +
+ " FROM " + NotesDatabaseHelper.TABLE.DATA +
+ " WHERE (" + Notes.DataColumns.MIME_TYPE + "=?" +
+ " AND " + Notes.DataColumns.CONTENT + " LIKE ?" +
+ ") OR (" + Notes.DataColumns.MIME_TYPE + "=?" +
+ " AND " + Notes.DataColumns.CONTENT + " LIKE ?" +
+ "))";
+
+ // 将整个基础查询条件用括号括起来,确保搜索条件应用到所有笔记
+ // 这是修复搜索功能的关键:解决SQL操作符优先级问题
+ selection = "(" + selection + ")";
+ selection += searchFilter;
+ }
+
+ // 添加标签过滤条件
+ if (mCurrentFilterTag != null) {
+ // 标签过滤需要使用子查询,因为标签存储在data表中
+ String tagFilter = " AND " + NoteColumns.ID + " IN (" +
+ "SELECT " + Notes.DataColumns.NOTE_ID +
+ " FROM " + NotesDatabaseHelper.TABLE.DATA +
+ " WHERE " + Notes.DataColumns.MIME_TYPE + "=?" +
+ " AND " + Notes.DataColumns.CONTENT + "=?" +
+ ")";
+ selection += tagFilter;
+ }
+
+ // 准备选择参数
+ ArrayList argsList = new ArrayList<>();
+ argsList.add(String.valueOf(mCurrentFolderId));
+
+ // 添加搜索参数
+ if (mIsSearchMode && !TextUtils.isEmpty(mSearchQuery)) {
+ String searchPattern = "%" + mSearchQuery + "%";
+ // 搜索普通文本内容
+ argsList.add(Notes.DataConstants.NOTE);
+ argsList.add(searchPattern);
+ // 搜索标签
+ argsList.add(Notes.DataConstants.TAG);
+ argsList.add(searchPattern);
+ }
+
+ // 添加标签参数
+ if (mCurrentFilterTag != null) {
+ argsList.add(Notes.DataConstants.TAG);
+ argsList.add(mCurrentFilterTag);
+ }
+
+ selectionArgs = argsList.toArray(new String[argsList.size()]);
+
mBackgroundQueryHandler.startQuery(FOLDER_NOTE_LIST_QUERY_TOKEN, null,
- Notes.CONTENT_NOTE_URI, NoteItemData.PROJECTION, selection, new String[] {
- String.valueOf(mCurrentFolderId)
- }, NoteColumns.TYPE + " DESC," + NoteColumns.MODIFIED_DATE + " DESC");
+ Notes.CONTENT_NOTE_URI, NoteItemData.PROJECTION, selection, selectionArgs,
+ NoteColumns.TYPE + " DESC," + NoteColumns.MODIFIED_DATE + " DESC");
+ }
+
+ /**
+ * 加载标签云
+ */
+ private void loadTagCloud() {
+ // 清空现有标签(保留"全部笔记"标签)
+ while (mTagCloudContainer.getChildCount() > 1) {
+ mTagCloudContainer.removeViewAt(1);
+ }
+
+ // 查询所有标签,按使用次数排序
+ String tagQuery = "SELECT " + Notes.DataColumns.CONTENT +
+ " FROM " + NotesDatabaseHelper.TABLE.DATA +
+ " WHERE " + Notes.DataColumns.MIME_TYPE + "=?" +
+ " GROUP BY " + Notes.DataColumns.CONTENT +
+ " ORDER BY COUNT(*) DESC";
+
+ Cursor cursor = mContentResolver.query(
+ Notes.CONTENT_DATA_URI,
+ new String[]{Notes.DataColumns.CONTENT},
+ Notes.DataColumns.MIME_TYPE + "=?",
+ new String[]{Notes.DataConstants.TAG},
+ Notes.DataColumns.CONTENT + " ASC");
+
+ if (cursor != null) {
+ while (cursor.moveToNext()) {
+ String tagName = cursor.getString(0);
+ if (!TextUtils.isEmpty(tagName)) {
+ // 创建标签按钮
+ Button tagButton = new Button(this);
+ tagButton.setText(tagName);
+ tagButton.setBackgroundResource(R.drawable.bg_btn_set_color);
+ tagButton.setTextColor(Color.WHITE);
+ tagButton.setTextSize(14);
+ tagButton.setSingleLine(true);
+ tagButton.setTag(tagName);
+ // tagButton.setOnClickListener(new OnClickListener() {
+ // @Override
+ // public void onClick(View v) {
+ // onTagClick(v);
+ // }
+ // });
+
+ // 设置标准化布局参数
+ LinearLayout.LayoutParams params = new LinearLayout.LayoutParams(
+ LinearLayout.LayoutParams.WRAP_CONTENT, 36);
+ params.setMargins(0, 0, 8, 0);
+ params.gravity = Gravity.CENTER_VERTICAL;
+ tagButton.setLayoutParams(params);
+
+ // 设置标准化内边距
+ tagButton.setPadding(12, 0, 12, 0);
+
+ // 添加到标签云容器
+ mTagCloudContainer.addView(tagButton);
+ }
+ }
+ cursor.close();
+ }
}
+
+ // 标签云功能已移除,onTagClick方法已删除
+ // public void onTagClick(View v) {
+ // if (v.getId() == R.id.tag_all_notes) {
+ // // 显示全部笔记
+ // mCurrentFilterTag = null;
+ // mAllNotesTag.setTextColor(Color.WHITE);
+ // mAllNotesTag.setBackgroundResource(R.drawable.bg_btn_set_color);
+ // } else {
+ // // 显示特定标签的笔记
+ // mCurrentFilterTag = (String) v.getTag();
+ // mAllNotesTag.setTextColor(Color.BLACK);
+ // mAllNotesTag.setBackgroundResource(android.R.drawable.btn_default);
+ // }
+ //
+ // // 更新其他标签的样式
+ // for (int i = 1; i < mTagCloudContainer.getChildCount(); i++) {
+ // Button tagButton = (Button) mTagCloudContainer.getChildAt(i);
+ // if (tagButton.getTag().equals(mCurrentFilterTag)) {
+ // tagButton.setTextColor(Color.WHITE);
+ // tagButton.setBackgroundResource(R.drawable.bg_btn_set_color);
+ // } else {
+ // tagButton.setTextColor(Color.BLACK);
+ // tagButton.setBackgroundResource(android.R.drawable.btn_default);
+ // }
+ // }
+ //
+ // // 重新查询笔记列表
+ // startAsyncNotesListQuery();
+ // }
+
/**
* 后台查询处理器 - 处理异步数据库查询
*/
@@ -540,9 +826,12 @@ public class NotesListActivity extends Activity implements OnClickListener, OnIt
*/
@Override
protected void onQueryComplete(int token, Object cookie, Cursor cursor) {
+ Log.d(TAG, "onQueryComplete called with token: " + token + ", cursor: " + cursor + ", count: " + (cursor != null ? cursor.getCount() : -1));
+
switch (token) {
case FOLDER_NOTE_LIST_QUERY_TOKEN:
// 查询笔记列表完成,更新适配器
+ Log.d(TAG, "Updating notes list adapter with cursor, count: " + (cursor != null ? cursor.getCount() : -1));
mNotesListAdapter.changeCursor(cursor);
break;
case FOLDER_LIST_QUERY_TOKEN:
@@ -615,7 +904,7 @@ public class NotesListActivity extends Activity implements OnClickListener, OnIt
View view = LayoutInflater.from(this).inflate(R.layout.dialog_edit_text, null);
final EditText etPassword = (EditText) view.findViewById(R.id.et_foler_name);
etPassword.setHint(R.string.password_hint);
- etPassword.setInputType(android.text.InputType.TYPE_CLASS_TEXT |
+ etPassword.setInputType(android.text.InputType.TYPE_CLASS_TEXT |
android.text.InputType.TYPE_TEXT_VARIATION_PASSWORD);
builder.setTitle(R.string.password_set_dialog_title);
builder.setView(view);
@@ -630,14 +919,14 @@ public class NotesListActivity extends Activity implements OnClickListener, OnIt
final Dialog dialog = builder.create();
dialog.show();
-
+
final Button positive = (Button) dialog.findViewById(android.R.id.button1);
positive.setOnClickListener(new OnClickListener() {
@Override
public void onClick(View v) {
String password = etPassword.getText().toString();
if (TextUtils.isEmpty(password)) {
- Toast.makeText(NotesListActivity.this,
+ Toast.makeText(NotesListActivity.this,
R.string.password_empty, Toast.LENGTH_SHORT).show();
return;
}
@@ -1175,12 +1464,71 @@ public class NotesListActivity extends Activity implements OnClickListener, OnIt
startActivity(intent);
break;
}
+ case R.id.menu_exit_search:
+ // 退出搜索模式
+ exitSearchMode();
+ return true;
default:
break;
}
return true;
}
+ @Override
+ protected void onNewIntent(Intent intent) {
+ super.onNewIntent(intent);
+
+ // 处理搜索意图
+ if (Intent.ACTION_SEARCH.equals(intent.getAction())) {
+ String query = intent.getStringExtra(SearchManager.QUERY);
+ if (!TextUtils.isEmpty(query)) {
+ setSearchMode(true, query);
+ // 添加到搜索历史
+ saveSearchHistory(query);
+ // 显示软键盘
+ InputMethodManager imm = (InputMethodManager) getSystemService(Context.INPUT_METHOD_SERVICE);
+ if (imm != null) {
+ imm.showSoftInput(mSearchEditText, InputMethodManager.SHOW_IMPLICIT);
+ }
+ }
+ }
+ }
+
+ /**
+ * 设置搜索模式
+ * @param isSearchMode 是否处于搜索模式
+ * @param searchQuery 搜索查询字符串
+ */
+ private void setSearchMode(boolean isSearchMode, String searchQuery) {
+ mIsSearchMode = isSearchMode;
+ mSearchQuery = searchQuery;
+
+ // 移除任何待执行的搜索任务,避免状态冲突
+ mSearchEditText.removeCallbacks(mSearchRunnable);
+
+ startAsyncNotesListQuery();
+
+ // 更新UI,显示或隐藏搜索相关控件
+ if (isSearchMode) {
+ // 搜索模式下显示搜索栏
+ mSearchBar.setVisibility(View.VISIBLE);
+ // 设置搜索查询文本
+ mSearchEditText.setText(searchQuery);
+ // 将光标定位到文本末尾
+ mSearchEditText.setSelection(searchQuery.length());
+ } else {
+ // 退出搜索模式,隐藏搜索栏
+ mSearchBar.setVisibility(View.GONE);
+ }
+ }
+
+ /**
+ * 退出搜索模式
+ */
+ public void exitSearchMode() {
+ setSearchMode(false, null);
+ }
+
@Override
public boolean onSearchRequested() {
startSearch(null, false, null /* appData */, false);
@@ -1192,8 +1540,87 @@ public class NotesListActivity extends Activity implements OnClickListener, OnIt
* OMO
*/
public void handleSearchClick(View view) {
- // 调用系统搜索方法
- onSearchRequested();
+ Log.d(TAG, "Search button clicked, showing search bar");
+ // 显示搜索栏并聚焦到搜索输入框
+ setSearchMode(true, mSearchQuery != null ? mSearchQuery : "");
+ mSearchEditText.requestFocus();
+ // 显示软键盘
+ InputMethodManager imm = (InputMethodManager) getSystemService(Context.INPUT_METHOD_SERVICE);
+ if (imm != null) {
+ imm.showSoftInput(mSearchEditText, InputMethodManager.SHOW_IMPLICIT);
+ }
+ Log.d(TAG, "Search bar displayed, search mode: " + mIsSearchMode);
+ }
+
+ /**
+ * 保存搜索历史
+ * @param query 搜索查询字符串
+ */
+ private void saveSearchHistory(String query) {
+ if (TextUtils.isEmpty(query)) {
+ return;
+ }
+
+ SharedPreferences prefs = PreferenceManager.getDefaultSharedPreferences(this);
+ Set historySet = prefs.getStringSet(PREFERENCE_SEARCH_HISTORY, new HashSet());
+
+ // 创建新的集合以支持修改
+ Set newHistorySet = new LinkedHashSet(historySet);
+
+ // 如果已存在,先移除,然后添加到开头
+ newHistorySet.remove(query);
+ // 创建有序集合以保持插入顺序
+ LinkedHashSet orderedHistorySet = new LinkedHashSet();
+ orderedHistorySet.add(query);
+ orderedHistorySet.addAll(newHistorySet);
+
+ // 限制历史记录数量
+ if (orderedHistorySet.size() > MAX_SEARCH_HISTORY) {
+ // 移除最旧的记录
+ String[] historyArray = orderedHistorySet.toArray(new String[0]);
+ for (int i = MAX_SEARCH_HISTORY; i < historyArray.length; i++) {
+ orderedHistorySet.remove(historyArray[i]);
+ }
+ }
+
+ // 保存到偏好设置
+ prefs.edit().putStringSet(PREFERENCE_SEARCH_HISTORY, orderedHistorySet).apply();
+ }
+
+ /**
+ * 获取搜索历史
+ * @return 搜索历史集合
+ */
+ private Set getSearchHistory() {
+ SharedPreferences prefs = PreferenceManager.getDefaultSharedPreferences(this);
+ return prefs.getStringSet(PREFERENCE_SEARCH_HISTORY, new HashSet());
+ }
+
+ /**
+ * 清除搜索历史
+ */
+ private void clearSearchHistory() {
+ SharedPreferences prefs = PreferenceManager.getDefaultSharedPreferences(this);
+ prefs.edit().remove(PREFERENCE_SEARCH_HISTORY).apply();
+ }
+
+ /**
+ * 添加退出搜索模式的菜单项
+ * @param menu 菜单对象
+ * @return 是否成功创建菜单
+ */
+ @Override
+ public boolean onCreateOptionsMenu(Menu menu) {
+ getMenuInflater().inflate(R.menu.note_list_options, menu);
+
+ // 在搜索模式下添加退出搜索菜单项
+ if (mIsSearchMode) {
+ MenuItem exitSearchItem = menu.add(Menu.NONE, R.id.menu_exit_search, Menu.NONE, "退出搜索");
+ exitSearchItem.setIcon(android.R.drawable.ic_menu_close_clear_cancel);
+ exitSearchItem.setShowAsAction(MenuItem.SHOW_AS_ACTION_IF_ROOM);
+ }
+
+ return true;
}
private void exportNoteToText() {
final BackupUtils backup = BackupUtils.getInstance(NotesListActivity.this);
diff --git a/src/Notes-master/src/net/micode/notes/ui/NotesListAdapter.java b/src/Notes-master/src/net/micode/notes/ui/NotesListAdapter.java
index 4fb993c..83e33b7 100644
--- a/src/Notes-master/src/net/micode/notes/ui/NotesListAdapter.java
+++ b/src/Notes-master/src/net/micode/notes/ui/NotesListAdapter.java
@@ -27,6 +27,7 @@ import android.widget.CursorAdapter;
import net.micode.notes.data.Notes;
import net.micode.notes.data.Notes.NoteColumns;
+import net.micode.notes.ui.NoteEditActivity;
import java.util.Collection;
import java.util.HashMap;
diff --git a/src/Notes-master/src/net/micode/notes/ui/PasswordInputActivity.java b/src/Notes-master/src/net/micode/notes/ui/PasswordInputActivity.java
index 90f30b2..db61a70 100644
--- a/src/Notes-master/src/net/micode/notes/ui/PasswordInputActivity.java
+++ b/src/Notes-master/src/net/micode/notes/ui/PasswordInputActivity.java
@@ -28,6 +28,7 @@ import android.widget.Toast;
import net.micode.notes.R;
import net.micode.notes.tool.PasswordUtils;
+import net.micode.notes.ui.NoteEditActivity;
/**
* 密码输入Activity