修改了部分代码文件 #32

Merged
p5vjqsb6e merged 21 commits from luogang_branch into master 2 months ago

@ -0,0 +1,2 @@
#Wed Jan 28 09:46:21 CST 2026
gradle.version=9.2.0

File diff suppressed because one or more lines are too long

@ -1,5 +1,6 @@
<?xml version="1.0" encoding="utf-8"?>
<!-- Copyright (c) 2010-2011, The MiCode Open Source Community (www.micode.net)
<!--
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
@ -9,44 +10,83 @@
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.
AndroidManifest.xml - 应用清单文件
定义了应用的基本信息、权限、组件Activity、Service、Receiver、Provider等配置
-->
<!--
Manifest根元素
package: 应用的包名,用于唯一标识应用
versionCode: 版本代码,用于版本比较(内部版本号)
versionName: 版本名称,显示给用户的版本号
-->
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
package="net.micode.notes"
android:versionCode="1"
android:versionName="0.1" >
<!-- 已移除package属性已注释minSdkVersion可选删除 -->
<!-- <uses-sdk android:minSdkVersion="14" /> -->
<!-- ==================== 权限声明区域 ==================== -->
<!-- 写入外部存储权限用于导出笔记到SD卡、保存图片等 -->
<uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE" />
<!-- 读取外部存储权限:用于读取图片、导入文件等 -->
<uses-permission android:name="android.permission.READ_EXTERNAL_STORAGE" />
<!-- 安装快捷方式权限:用于在桌面创建笔记快捷方式 -->
<uses-permission android:name="com.android.launcher.permission.INSTALL_SHORTCUT" />
<!-- 网络访问权限用于Google Tasks同步、在线语音识别等功能 -->
<uses-permission android:name="android.permission.INTERNET" />
<!-- 读取联系人权限:用于在笔记中识别电话号码并显示联系人名称 -->
<uses-permission android:name="android.permission.READ_CONTACTS" />
<!-- 管理账户权限用于管理Google账户进行Google Tasks同步 -->
<uses-permission android:name="android.permission.MANAGE_ACCOUNTS" />
<!-- 账户认证权限用于Google账户认证 -->
<uses-permission android:name="android.permission.AUTHENTICATE_ACCOUNTS" />
<!-- 获取账户列表权限用于获取系统中的Google账户列表 -->
<uses-permission android:name="android.permission.GET_ACCOUNTS" />
<!-- 使用凭证权限用于使用Google账户凭证进行同步 -->
<uses-permission android:name="android.permission.USE_CREDENTIALS" />
<!-- 接收开机完成广播权限:用于在系统启动后重新设置闹钟提醒 -->
<uses-permission android:name="android.permission.RECEIVE_BOOT_COMPLETED" />
<!-- 录音权限:用于语音识别功能,记录音频输入 -->
<uses-permission android:name="android.permission.RECORD_AUDIO" />
<!-- 语音识别权限 -->
<uses-permission android:name="android.permission.RECORD_AUDIO" />
<!-- 网络权限(用于在线语音识别) -->
<uses-permission android:name="android.permission.INTERNET" />
<!-- 存储权限(用于保存语音识别结果) -->
<uses-permission android:name="android.permission.RECORD_AUDIO" />
<!-- Android 6.0+ 需要的权限 -->
<!-- 访问网络状态权限用于检查网络连接状态Android 6.0+需要) -->
<uses-permission android:name="android.permission.ACCESS_NETWORK_STATE" />
<!-- 从相册选择图片需要 -->
<uses-permission android:name="android.permission.READ_EXTERNAL_STORAGE"/>
<!-- 如果Android 13+,可能需要读取媒体文件 -->
<!-- 读取媒体图片权限Android 13+API 33+)需要,用于从相册选择图片 -->
<uses-permission android:name="android.permission.READ_MEDIA_IMAGES" />
<!-- ==================== 应用配置区域 ==================== -->
<!--
Application元素定义应用的全局配置
icon: 应用图标
label: 应用名称(显示在应用列表中)
-->
<application
android:icon="@drawable/icon_app"
android:label="@string/app_name" >
<!-- ==================== Activity组件 ==================== -->
<!--
笔记列表Activity - 应用主界面
功能:显示笔记列表、文件夹管理、搜索等
configChanges: 配置变化时不重启Activity键盘、屏幕方向、屏幕尺寸
launchMode: singleTop - 如果Activity已在栈顶则复用实例
uiOptions: 窄屏时分割操作栏
windowSoftInputMode: adjustPan - 软键盘弹出时平移窗口
exported: true - 允许其他应用启动此Activity
-->
<activity
android:name=".ui.NotesListActivity"
android:configChanges="keyboardHidden|orientation|screenSize"
@ -55,35 +95,44 @@
android:theme="@style/NoteTheme"
android:uiOptions="splitActionBarWhenNarrow"
android:windowSoftInputMode="adjustPan"
android:exported="true" > <!-- 新增 -->
android:exported="true" >
<!-- 主启动器Intent过滤器应用入口显示在应用列表中 -->
<intent-filter>
<action android:name="android.intent.action.MAIN" />
<category android:name="android.intent.category.LAUNCHER" />
</intent-filter>
<!-- 搜索Intent过滤器支持系统搜索功能 -->
<intent-filter>
<action android:name="android.intent.action.SEARCH" />
<category android:name="android.intent.category.DEFAULT" />
</intent-filter>
<!-- 搜索配置元数据:指定搜索配置文件 -->
<meta-data
android:name="android.app.searchable"
android:resource="@xml/searchable" />
</activity>
<!--
笔记编辑Activity
功能:创建和编辑笔记内容
configChanges: 配置变化时不重启Activity
launchMode: singleTop - 如果Activity已在栈顶则复用实例
exported: true - 允许其他应用启动此Activity
-->
<activity
android:name=".ui.NoteEditActivity"
android:configChanges="keyboardHidden|orientation|screenSize"
android:launchMode="singleTop"
android:theme="@style/NoteTheme"
android:exported="true">
<!-- 查看笔记Intent过滤器支持查看文本笔记和通话记录笔记 -->
<intent-filter >
<action android:name="android.intent.action.VIEW" />
<category android:name="android.intent.category.DEFAULT" />
<data android:mimeType="vnd.android.cursor.item/text_note" />
<data android:mimeType="vnd.android.cursor.item/call_note" />
</intent-filter >
<!-- 插入或编辑笔记Intent过滤器支持创建或编辑笔记 -->
<intent-filter >
<action android:name="android.intent.action.INSERT_OR_EDIT" />
<category android:name="android.intent.category.DEFAULT" />
@ -93,55 +142,98 @@
</activity>
<!-- ==================== ContentProvider组件 ==================== -->
<!--
笔记数据提供者
功能:提供笔记数据的访问接口,支持其他应用访问笔记数据
authorities: 内容提供者的唯一标识符
multiprocess: true - 允许在多个进程中运行
-->
<provider
android:name="net.micode.notes.data.NotesProvider"
android:authorities="micode_notes"
android:multiprocess="true" />
<!-- ==================== BroadcastReceiver组件 ==================== -->
<!--
2x2尺寸桌面小部件提供者
功能提供2x2尺寸的桌面小部件显示笔记内容
exported: true - 允许系统启动此Receiver
-->
<receiver
android:name=".widget.NoteWidgetProvider_2x"
android:label="@string/app_widget2x2"
android:exported="true">
<intent-filter>
<!-- 小部件更新动作:当需要更新小部件时触发 -->
<action android:name="android.appwidget.action.APPWIDGET_UPDATE" />
<!-- 小部件删除动作:当小部件被删除时触发 -->
<action android:name="android.appwidget.action.APPWIDGET_DELETED" />
<!-- 隐私模式变化动作:当隐私模式改变时触发 -->
<action android:name="android.intent.action.PRIVACY_MODE_CHANGED" />
</intent-filter>
<!-- 小部件配置元数据:指定小部件的配置文件 -->
<meta-data
android:name="android.appwidget.provider"
android:resource="@xml/widget_2x_info" />
</receiver>
<!--
4x4尺寸桌面小部件提供者
功能提供4x4尺寸的桌面小部件显示更多笔记内容
exported: true - 允许系统启动此Receiver
-->
<receiver
android:name=".widget.NoteWidgetProvider_4x"
android:label="@string/app_widget4x4"
android:exported="true">
<intent-filter>
<!-- 小部件更新动作 -->
<action android:name="android.appwidget.action.APPWIDGET_UPDATE" />
<!-- 小部件删除动作 -->
<action android:name="android.appwidget.action.APPWIDGET_DELETED" />
<!-- 隐私模式变化动作 -->
<action android:name="android.intent.action.PRIVACY_MODE_CHANGED" />
</intent-filter>
<!-- 小部件配置元数据 -->
<meta-data
android:name="android.appwidget.provider"
android:resource="@xml/widget_4x_info" />
</receiver>
<!--
闹钟初始化接收器
功能:在系统启动完成后重新设置所有笔记的闹钟提醒
exported: true - 允许系统启动此Receiver
-->
<receiver android:name=".ui.AlarmInitReceiver"
android:exported="true">
<intent-filter>
<!-- 开机完成广播:系统启动完成后触发 -->
<action android:name="android.intent.action.BOOT_COMPLETED" />
</intent-filter>
</receiver>
<!--
闹钟提醒接收器
功能:接收闹钟提醒广播,启动提醒界面
process: :remote - 在独立进程中运行,避免影响主进程
-->
<receiver
android:name="net.micode.notes.ui.AlarmReceiver"
android:process=":remote" >
</receiver>
<!-- ==================== 其他Activity组件 ==================== -->
<!--
闹钟提醒Activity
功能:显示笔记提醒对话框,播放提醒声音
launchMode: singleInstance - 独立任务栈,确保只有一个实例
theme: 使用壁纸主题,无标题栏
-->
<activity
android:name=".ui.AlarmAlertActivity"
android:label="@string/app_name"
@ -149,14 +241,25 @@
android:theme="@android:style/Theme.Holo.Wallpaper.NoTitleBar" >
</activity>
<!--
设置Activity
功能:应用设置界面,管理同步账户、背景颜色等偏好设置
launchMode: singleTop - 如果Activity已在栈顶则复用实例
theme: 使用浅色主题
-->
<activity
android:name="net.micode.notes.ui.NotesPreferenceActivity"
android:label="@string/preferences_title"
android:launchMode="singleTop"
android:theme="@android:style/Theme.Holo.Light" >
</activity>
<!--
回收站Activity
功能:显示和管理已删除的便签,支持恢复和彻底删除
launchMode: singleTop - 如果Activity已在栈顶则复用实例
exported: true - 允许其他应用启动此Activity
-->
<activity
android:name=".ui.TrashActivity"
android:label="@string/menu_trash"
@ -164,6 +267,12 @@
android:theme="@style/NoteTheme"
android:exported="true" >
</activity>
<!--
密码输入Activity
功能:输入加密便签的密码,验证后跳转到编辑界面
exported: false - 不允许其他应用启动,仅内部使用
-->
<activity
android:name=".ui.PasswordInputActivity"
android:label="@string/password_input_title"
@ -171,11 +280,24 @@
android:exported="false" >
</activity>
<!-- ==================== Service组件 ==================== -->
<!--
Google Tasks同步服务
功能在后台执行Google Tasks同步操作
exported: false - 不允许其他应用启动,仅内部使用
-->
<service
android:name="net.micode.notes.gtask.remote.GTaskSyncService"
android:exported="false" >
</service>
<!-- ==================== 应用元数据 ==================== -->
<!--
默认搜索Activity配置
功能指定应用的默认搜索Activity为NotesListActivity
-->
<meta-data
android:name="android.app.default_searchable"
android:value=".ui.NotesListActivity" />

Binary file not shown.

Before

Width:  |  Height:  |  Size: 304 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 1.5 MiB

@ -1,5 +1,6 @@
<?xml version="1.0" encoding="utf-8"?>
<!-- Copyright (c) 2010-2011, The MiCode Open Source Community (www.micode.net)
<!--
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.
@ -12,7 +13,11 @@
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.
密码输入界面布局文件
用于显示密码输入界面,包含标题、密码输入框和操作按钮
-->
<!-- 根布局:垂直方向的线性布局,内容居中显示 -->
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="match_parent"
android:layout_height="match_parent"
@ -20,6 +25,7 @@
android:padding="16dp"
android:gravity="center">
<!-- 标题文本视图:显示"请输入密码"标题 -->
<TextView
android:layout_width="wrap_content"
android:layout_height="wrap_content"
@ -28,6 +34,7 @@
android:textStyle="bold"
android:layout_marginBottom="24dp" />
<!-- 密码输入框:用于输入加密便签的密码,输入类型为密码模式(隐藏输入内容) -->
<EditText
android:id="@+id/et_password"
android:layout_width="match_parent"
@ -36,12 +43,14 @@
android:inputType="textPassword"
android:layout_marginBottom="16dp" />
<!-- 按钮容器:水平方向的线性布局,按钮右对齐 -->
<LinearLayout
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:orientation="horizontal"
android:gravity="end">
<!-- 返回按钮:点击后关闭当前界面,返回上一界面 -->
<Button
android:id="@+id/btn_return"
android:layout_width="wrap_content"
@ -49,6 +58,7 @@
android:text="@string/return_button"
android:layout_marginEnd="8dp" />
<!-- 确认按钮:点击后验证密码,密码正确则跳转到便签编辑界面 -->
<Button
android:id="@+id/btn_confirm"
android:layout_width="wrap_content"

@ -118,9 +118,6 @@ public class ResourceParser {
* @return ID
*/
public static int getNoteBgResource(int id) {
if (id < 0 || id >= BG_EDIT_RESOURCES.length) {
return BG_EDIT_RESOURCES[BG_DEFAULT_COLOR];
}
return BG_EDIT_RESOURCES[id];
}
@ -130,9 +127,6 @@ public class ResourceParser {
* @return ID
*/
public static int getNoteTitleBgResource(int id) {
if (id < 0 || id >= BG_EDIT_TITLE_RESOURCES.length) {
return BG_EDIT_TITLE_RESOURCES[BG_DEFAULT_COLOR];
}
return BG_EDIT_TITLE_RESOURCES[id];
}
}
@ -211,9 +205,6 @@ public class ResourceParser {
* @return ID
*/
public static int getNoteBgFirstRes(int id) {
if (id < 0 || id >= BG_FIRST_RESOURCES.length) {
return BG_FIRST_RESOURCES[BG_DEFAULT_COLOR];
}
return BG_FIRST_RESOURCES[id];
}
@ -223,9 +214,6 @@ public class ResourceParser {
* @return ID
*/
public static int getNoteBgLastRes(int id) {
if (id < 0 || id >= BG_LAST_RESOURCES.length) {
return BG_LAST_RESOURCES[BG_DEFAULT_COLOR];
}
return BG_LAST_RESOURCES[id];
}
@ -235,9 +223,6 @@ public class ResourceParser {
* @return ID
*/
public static int getNoteBgSingleRes(int id) {
if (id < 0 || id >= BG_SINGLE_RESOURCES.length) {
return BG_SINGLE_RESOURCES[BG_DEFAULT_COLOR];
}
return BG_SINGLE_RESOURCES[id];
}
@ -247,9 +232,6 @@ public class ResourceParser {
* @return ID
*/
public static int getNoteBgNormalRes(int id) {
if (id < 0 || id >= BG_NORMAL_RESOURCES.length) {
return BG_NORMAL_RESOURCES[BG_DEFAULT_COLOR];
}
return BG_NORMAL_RESOURCES[id];
}
@ -285,9 +267,6 @@ public class ResourceParser {
* @return 2xID
*/
public static int getWidget2xBgResource(int id) {
if (id < 0 || id >= BG_2X_RESOURCES.length) {
return BG_2X_RESOURCES[BG_DEFAULT_COLOR];
}
return BG_2X_RESOURCES[id];
}
@ -309,9 +288,6 @@ public class ResourceParser {
* @return 4xID
*/
public static int getWidget4xBgResource(int id) {
if (id < 0 || id >= BG_4X_RESOURCES.length) {
return BG_4X_RESOURCES[BG_DEFAULT_COLOR];
}
return BG_4X_RESOURCES[id];
}
}

@ -234,14 +234,6 @@ public class NoteEditActivity extends Activity implements OnClickListener,
private EditText mTagInput; // 标签输入框
private Button mAddTagButton; // 添加标签按钮
private ArrayList<String> mTagsList; // 标签列表
// 视图缓存,减少对象创建
private ArrayList<View> mViewCache = new ArrayList<>();
private static final int MAX_CACHE_SIZE = 10; // 最大缓存大小
// 延迟保存相关
private android.os.Handler mSaveNoteHandler = new android.os.Handler();
private TextView mSaveHint; // 保存成功提示视图
// 搜索历史相关
private static final String PREFERENCE_SEARCH_HISTORY = "search_history"; // 搜索历史偏好设置键
@ -1345,114 +1337,58 @@ public class NoteEditActivity extends Activity implements OnClickListener,
long startTime = System.currentTimeMillis();
Log.d(TAG, "switchToListMode started, text length: " + (text != null ? text.length() : 0));
// 处理null文本避免空指针异常
// 处理null文本避免空指针异常OMO
final String processedText = text != null ? text : "";
// 优化:直接在主线程处理,减少线程切换开销
// 对于大多数情况,文本分割和视图创建的开销很小
// 只有在文本特别长时才需要考虑后台线程
if (processedText.length() > 10000) {
// 对于长文本,使用后台线程
new Thread(new Runnable() {
@Override
public void run() {
final String[] items = processTextForListMode(processedText);
Log.d(TAG, "switchToListMode: split into " + items.length + " items");
runOnUiThread(new Runnable() {
@Override
public void run() {
createListModeViews(items);
long endTime = System.currentTimeMillis();
Log.d(TAG, "switchToListMode completed in " + (endTime - startTime) + "ms");
// 预处理:在后台线程中分割文本和准备数据
new Thread(new Runnable() {
@Override
public void run() {
final String[] items = processedText.split("\n"); // 按行分割文本
Log.d(TAG, "switchToListMode: split into " + items.length + " items");
// 切换回主线程更新UI
runOnUiThread(new Runnable() {
@Override
public void run() {
// 批量创建和添加视图,减少布局重绘
mEditTextList.removeAllViews(); // 清空编辑文本列表
int index = 0;
View lastItem = null;
// 先创建所有视图
ArrayList<View> viewsToAdd = new ArrayList<>();
for (String item : items) {
if(!TextUtils.isEmpty(item)) {
View listItem = getListItem(item, index);
viewsToAdd.add(listItem);
index++;
}
}
});
}
}).start();
} else {
// 对于短文本,直接在主线程处理
final String[] items = processTextForListMode(processedText);
Log.d(TAG, "switchToListMode: split into " + items.length + " items");
createListModeViews(items);
long endTime = System.currentTimeMillis();
Log.d(TAG, "switchToListMode completed in " + (endTime - startTime) + "ms");
}
}
/**
*
* @param text
* @return
*/
private String[] processTextForListMode(String text) {
// 移除HTML标签只保留纯文本
String plainText = removeHtmlTags(text);
// 按行分割
return plainText.split("\n");
}
// 添加最后一个空列表项
lastItem = getListItem("", index);
viewsToAdd.add(lastItem);
/**
* HTML
* @param html HTML
* @return
*/
private String removeHtmlTags(String html) {
// 简单的HTML标签移除适合大多数情况
if (TextUtils.isEmpty(html)) {
return "";
}
// 移除常见HTML标签
String plainText = html.replaceAll("<[^>]+>", "");
// 处理HTML实体
plainText = plainText.replaceAll("&nbsp;", " ");
plainText = plainText.replaceAll("&lt;", "<");
plainText = plainText.replaceAll("&gt;", ">");
plainText = plainText.replaceAll("&amp;", "&");
return plainText;
}
// 一次性添加所有视图到容器
for (View view : viewsToAdd) {
mEditTextList.addView(view);
}
/**
*
* @param items
*/
private void createListModeViews(String[] items) {
// 回收现有视图到缓存,减少内存占用
int childCount = mEditTextList.getChildCount();
for (int i = childCount - 1; i >= 0; i--) {
View view = mEditTextList.getChildAt(i);
recycleView(view);
}
// 清空编辑文本列表
mEditTextList.removeAllViews();
// 批量创建视图
ArrayList<View> viewsToAdd = new ArrayList<>();
int index = 0;
for (String item : items) {
if (!TextUtils.isEmpty(item)) {
View listItem = getListItem(item, index);
viewsToAdd.add(listItem);
index++;
// 设置焦点
ListItemViewHolder holder = (ListItemViewHolder) lastItem.getTag();
holder.etEditText.requestFocus();
mNoteEditor.setVisibility(View.GONE); // 隐藏普通编辑器
mEditTextList.setVisibility(View.VISIBLE); // 显示清单编辑器
long endTime = System.currentTimeMillis();
Log.d(TAG, "switchToListMode completed in " + (endTime - startTime) + "ms");
}
});
}
}
// 添加最后一个空列表项
View lastItem = getListItem("", index);
viewsToAdd.add(lastItem);
// 批量添加视图,减少布局重绘
for (View view : viewsToAdd) {
mEditTextList.addView(view);
}
// 设置焦点
ListItemViewHolder holder = (ListItemViewHolder) lastItem.getTag();
holder.etEditText.requestFocus();
// 显示/隐藏编辑器
mNoteEditor.setVisibility(View.GONE);
mEditTextList.setVisibility(View.VISIBLE);
}).start();
}
/**
@ -1510,22 +1446,13 @@ public class NoteEditActivity extends Activity implements OnClickListener,
* @return
*/
private View getListItem(String item, int index) {
// 优先从缓存中获取视图减少布局inflate开销
View view = null;
if (!mViewCache.isEmpty()) {
view = mViewCache.remove(0);
} else {
view = LayoutInflater.from(this).inflate(R.layout.note_edit_list_item, null);
}
View view = LayoutInflater.from(this).inflate(R.layout.note_edit_list_item, null);
// 创建并缓存ViewHolder
ListItemViewHolder holder = (ListItemViewHolder) view.getTag();
if (holder == null) {
holder = new ListItemViewHolder();
holder.etEditText = (NoteEditText) view.findViewById(R.id.et_edit_text);
holder.cbEditItem = (CheckBox) view.findViewById(R.id.cb_edit_item);
view.setTag(holder);
}
ListItemViewHolder holder = new ListItemViewHolder();
holder.etEditText = (NoteEditText) view.findViewById(R.id.et_edit_text);
holder.cbEditItem = (CheckBox) view.findViewById(R.id.cb_edit_item);
view.setTag(holder);
final NoteEditText edit = holder.etEditText;
edit.setTextAppearance(this, TextAppearanceResources.getTexAppearanceResource(mFontSizeId));
@ -1535,20 +1462,10 @@ public class NoteEditActivity extends Activity implements OnClickListener,
cb.setOnCheckedChangeListener(new OnCheckedChangeListener() {
public void onCheckedChanged(CompoundButton buttonView, boolean isChecked) {
if (isChecked) {
// 添加删除线和灰色效果
edit.setPaintFlags(edit.getPaintFlags() | Paint.STRIKE_THRU_TEXT_FLAG);
edit.setTextColor(Color.GRAY);
// 将已完成项目移到列表底部
moveCompletedItemToBottom(edit);
} else {
// 移除删除线和恢复颜色
edit.setPaintFlags(Paint.ANTI_ALIAS_FLAG | Paint.DEV_KERN_TEXT_FLAG);
edit.setTextColor(Color.BLACK);
// 将未完成项目移到列表顶部
moveUncompletedItemToTop(edit);
}
// 延迟保存,避免频繁操作导致卡顿
saveNoteDelayed();
}
});
@ -1556,120 +1473,13 @@ public class NoteEditActivity extends Activity implements OnClickListener,
if (item.startsWith(TAG_CHECKED)) {
cb.setChecked(true);
edit.setPaintFlags(edit.getPaintFlags() | Paint.STRIKE_THRU_TEXT_FLAG);
edit.setTextColor(Color.GRAY);
item = item.substring(TAG_CHECKED.length(), item.length()).trim();
} else if (item.startsWith(TAG_UNCHECKED)) {
cb.setChecked(false);
edit.setPaintFlags(Paint.ANTI_ALIAS_FLAG | Paint.DEV_KERN_TEXT_FLAG);
edit.setTextColor(Color.BLACK);
item = item.substring(TAG_UNCHECKED.length(), item.length()).trim();
} else {
// 重置为默认状态
cb.setChecked(false);
edit.setPaintFlags(Paint.ANTI_ALIAS_FLAG | Paint.DEV_KERN_TEXT_FLAG);
edit.setTextColor(Color.BLACK);
}
// 设置列表项点击事件监听器,用于标记项目为已完成状态
view.setOnClickListener(new OnClickListener() {
@Override
public void onClick(View v) {
// 添加点击反馈效果
v.setBackgroundColor(Color.parseColor("#E0E0E0"));
v.postDelayed(new Runnable() {
@Override
public void run() {
v.setBackgroundColor(Color.TRANSPARENT);
}
}, 100);
// 处理列表项点击事件,标记项目为已完成状态
CheckBox checkBox = (CheckBox) v.findViewById(R.id.cb_edit_item);
checkBox.setChecked(!checkBox.isChecked());
}
});
// 设置复选框点击事件监听器
cb.setOnClickListener(new OnClickListener() {
@Override
public void onClick(View v) {
// 直接切换选中状态,避免与列表项点击事件冲突
cb.setChecked(!cb.isChecked());
}
});
// 设置编辑文本点击事件监听器,用于进入详细编辑模式
edit.setOnClickListener(new OnClickListener() {
@Override
public void onClick(View v) {
// 阻止事件冒泡,避免触发列表项的点击事件
v.setClickable(true);
v.setFocusable(true);
v.setFocusableInTouchMode(true);
// 添加点击反馈效果
edit.setBackgroundColor(Color.parseColor("#F0F0F0"));
edit.postDelayed(new Runnable() {
@Override
public void run() {
edit.setBackgroundColor(Color.TRANSPARENT);
}
}, 100);
// 处理编辑文本点击事件,进入详细编辑模式
edit.requestFocus();
edit.setSelection(edit.length());
// 显示软键盘
getWindow().setSoftInputMode(WindowManager.LayoutParams.SOFT_INPUT_STATE_VISIBLE | WindowManager.LayoutParams.SOFT_INPUT_ADJUST_RESIZE);
}
});
// 为编辑文本添加文本变化监听器,确保修改能够被保存
edit.addTextChangedListener(new android.text.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) {
}
@Override
public void afterTextChanged(android.text.Editable s) {
// 文本变化后延迟保存笔记,避免频繁操作导致卡顿
saveNoteDelayed();
}
});
// 设置列表项长按事件监听器
edit.setOnLongClickListener(new View.OnLongClickListener() {
@Override
public boolean onLongClick(View v) {
try {
// 添加长按反馈效果
edit.setBackgroundColor(Color.parseColor("#D0D0D0"));
edit.postDelayed(new Runnable() {
@Override
public void run() {
edit.setBackgroundColor(Color.TRANSPARENT);
}
}, 100);
// 处理长按事件,将清单标记为已完成状态
View parentView = (View) v.getParent();
if (parentView != null) {
CheckBox checkBox = (CheckBox) parentView.findViewById(R.id.cb_edit_item);
if (checkBox != null) {
checkBox.setChecked(!checkBox.isChecked());
}
}
} catch (Exception e) {
Log.e(TAG, "Error in long click listener: " + e.getMessage());
}
return true;
}
});
edit.setOnTextViewChangeListener(this);
edit.setIndex(index);
edit.setText(getHighlightQueryResult(item, mUserQuery));
@ -1698,161 +1508,6 @@ public class NoteEditActivity extends Activity implements OnClickListener,
}
}
/**
*
* @param edit
*/
private void moveCompletedItemToBottom(NoteEditText edit) {
try {
int childCount = mEditTextList.getChildCount();
if (childCount <= 1 || edit == null) {
return;
}
// 找到当前项目的索引
int currentIndex = -1;
for (int i = 0; i < childCount; i++) {
View view = mEditTextList.getChildAt(i);
if (view != null) {
ListItemViewHolder holder = (ListItemViewHolder) view.getTag();
if (holder != null && holder.etEditText == edit) {
currentIndex = i;
break;
}
}
}
if (currentIndex == -1 || currentIndex == childCount - 1) {
return;
}
// 找到第一个未完成的项目索引
int firstUncompletedIndex = -1;
for (int i = currentIndex + 1; i < childCount; i++) {
View view = mEditTextList.getChildAt(i);
if (view != null) {
ListItemViewHolder holder = (ListItemViewHolder) view.getTag();
if (holder != null && holder.cbEditItem != null && !holder.cbEditItem.isChecked()) {
firstUncompletedIndex = i;
break;
}
}
}
if (firstUncompletedIndex != -1) {
// 保存当前项目
View currentView = mEditTextList.getChildAt(currentIndex);
if (currentView != null) {
mEditTextList.removeViewAt(currentIndex);
// 插入到第一个未完成项目之前
mEditTextList.addView(currentView, firstUncompletedIndex - 1);
// 更新索引
updateItemIndices();
}
}
} catch (Exception e) {
Log.e(TAG, "Error in moveCompletedItemToBottom: " + e.getMessage());
}
}
/**
*
* @param edit
*/
private void moveUncompletedItemToTop(NoteEditText edit) {
try {
int childCount = mEditTextList.getChildCount();
if (childCount <= 1 || edit == null) {
return;
}
// 找到当前项目的索引
int currentIndex = -1;
for (int i = 0; i < childCount; i++) {
View view = mEditTextList.getChildAt(i);
if (view != null) {
ListItemViewHolder holder = (ListItemViewHolder) view.getTag();
if (holder != null && holder.etEditText == edit) {
currentIndex = i;
break;
}
}
}
if (currentIndex == -1 || currentIndex == 0) {
return;
}
// 找到最后一个已完成的项目索引
int lastCompletedIndex = -1;
for (int i = 0; i < currentIndex; i++) {
View view = mEditTextList.getChildAt(i);
if (view != null) {
ListItemViewHolder holder = (ListItemViewHolder) view.getTag();
if (holder != null && holder.cbEditItem != null && holder.cbEditItem.isChecked()) {
lastCompletedIndex = i;
}
}
}
// 保存当前项目
View currentView = mEditTextList.getChildAt(currentIndex);
if (currentView != null) {
mEditTextList.removeViewAt(currentIndex);
// 插入到最后一个已完成项目之后
mEditTextList.addView(currentView, lastCompletedIndex + 1);
// 更新索引
updateItemIndices();
}
} catch (Exception e) {
Log.e(TAG, "Error in moveUncompletedItemToTop: " + e.getMessage());
}
}
/**
*
*/
private void updateItemIndices() {
try {
int childCount = mEditTextList.getChildCount();
for (int i = 0; i < childCount; i++) {
View view = mEditTextList.getChildAt(i);
if (view != null) {
ListItemViewHolder holder = (ListItemViewHolder) view.getTag();
if (holder != null && holder.etEditText != null) {
holder.etEditText.setIndex(i);
}
}
}
} catch (Exception e) {
Log.e(TAG, "Error in updateItemIndices: " + e.getMessage());
}
}
/**
*
* @param view
*/
private void recycleView(View view) {
if (view != null && mViewCache.size() < MAX_CACHE_SIZE) {
// 清除视图的内容和监听器,避免内存泄漏
ListItemViewHolder holder = (ListItemViewHolder) view.getTag();
if (holder != null) {
holder.etEditText.setText("");
holder.etEditText.setOnTextViewChangeListener(null);
holder.cbEditItem.setOnCheckedChangeListener(null);
}
mViewCache.add(view);
}
}
/**
*
*/
private void clearViewCache() {
mViewCache.clear();
}
/**
*
* NoteSettingChangedListener
@ -1925,35 +1580,26 @@ public class NoteEditActivity extends Activity implements OnClickListener,
private boolean getWorkingText() {
boolean hasChecked = false;
if (mWorkingNote.getCheckListMode() == TextNote.MODE_CHECK_LIST) {
// 优化使用StringBuilder的初始容量减少扩容开销
StringBuilder sb = new StringBuilder(mEditTextList.getChildCount() * 50); // 预估每个项目平均50字符
StringBuilder sb = new StringBuilder();
for (int i = 0; i < mEditTextList.getChildCount(); i++) {
View view = mEditTextList.getChildAt(i);
if (view != null) {
ListItemViewHolder holder = (ListItemViewHolder) view.getTag();
if (holder != null && holder.etEditText != null && holder.cbEditItem != null) {
NoteEditText edit = holder.etEditText;
if (!TextUtils.isEmpty(edit.getText())) {
if (holder.cbEditItem.isChecked()) {
sb.append(TAG_CHECKED).append(" ").append(edit.getText()).append("\n");
hasChecked = true;
} else {
sb.append(TAG_UNCHECKED).append(" ").append(edit.getText()).append("\n");
}
}
ListItemViewHolder holder = (ListItemViewHolder) view.getTag();
NoteEditText edit = holder.etEditText;
if (!TextUtils.isEmpty(edit.getText())) {
if (holder.cbEditItem.isChecked()) {
sb.append(TAG_CHECKED).append(" ").append(edit.getText()).append("\n");
hasChecked = true;
} else {
sb.append(TAG_UNCHECKED).append(" ").append(edit.getText()).append("\n");
}
}
}
mWorkingNote.setWorkingText(sb.toString());
} else {
// 对于富文本模式保存当前的HTML内容包括Base64图片
// 优化避免不必要的日志输出减少IO开销
if (mNoteEditor != null) {
String currentHtml = mNoteEditor.getHtml();
if (currentHtml != null) {
mWorkingNote.setWorkingText(currentHtml);
}
}
String currentHtml = mNoteEditor.getHtml();
Log.d(TAG, "getWorkingText called for rich text mode, saving HTML content: " + currentHtml);
mWorkingNote.setWorkingText(currentHtml);
}
return hasChecked;
}
@ -2202,71 +1848,6 @@ public class NoteEditActivity extends Activity implements OnClickListener,
Toast.makeText(this, resId, duration).show();
}
/**
*
*/
private void saveNoteDelayed() {
// 移除之前的延迟保存任务
if (mSaveNoteHandler != null) {
mSaveNoteHandler.removeCallbacksAndMessages(null);
}
// 延迟500毫秒保存避免频繁操作导致卡顿
mSaveNoteHandler.postDelayed(new Runnable() {
@Override
public void run() {
if (saveNote()) {
// 保存成功的简短提示
showSaveSuccessHint();
}
}
}, 500);
}
/**
*
*/
private void showSaveSuccessHint() {
if (mSaveHint == null) {
mSaveHint = new TextView(this);
mSaveHint.setText("已保存");
mSaveHint.setTextColor(Color.GRAY);
mSaveHint.setTextSize(12);
mSaveHint.setPadding(10, 5, 10, 5);
mSaveHint.setGravity(Gravity.CENTER);
mSaveHint.setBackgroundColor(Color.parseColor("#E0E0E0"));
mSaveHint.setAlpha(0.0f);
LinearLayout.LayoutParams params = new LinearLayout.LayoutParams(
LinearLayout.LayoutParams.WRAP_CONTENT,
LinearLayout.LayoutParams.WRAP_CONTENT
);
params.gravity = Gravity.CENTER;
params.topMargin = 10;
mEditTextList.addView(mSaveHint, params);
}
// 显示动画
mSaveHint.animate()
.alpha(1.0f)
.setDuration(300)
.withEndAction(new Runnable() {
@Override
public void run() {
// 延迟后隐藏
mSaveHint.postDelayed(new Runnable() {
@Override
public void run() {
mSaveHint.animate()
.alpha(0.0f)
.setDuration(300)
.start();
}
}, 1000);
}
})
.start();
}
/**
* OMO
*/

@ -33,67 +33,107 @@ import net.micode.notes.ui.NoteEditActivity;
/**
* Activity
* 便
*
*
* 1. Intent便ID
* 2.
* 3.
* 4. 便
*/
public class PasswordInputActivity extends Activity {
/** 日志标签,用于调试和日志输出 */
private static final String TAG = "PasswordInputActivity";
/** Intent Extra键名用于传递便签ID */
public static final String EXTRA_NOTE_ID = "note_id";
/** 密码输入框控件 */
private EditText mPasswordEditText;
/** 返回按钮控件 */
private Button mReturnButton;
/** 确认按钮控件 */
private Button mConfirmButton;
/** 当前要访问的便签ID */
private long mNoteId;
/**
* Activity
* Intent便ID
*
* @param savedInstanceState Activity
*/
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
// 设置布局文件
setContentView(R.layout.activity_password_input);
// 从Intent中获取便签ID
mNoteId = getIntent().getLongExtra(EXTRA_NOTE_ID, 0);
// 验证便签ID是否有效
if (mNoteId <= 0) {
// 便签ID无效显示错误提示并关闭Activity
Toast.makeText(this, R.string.error_note_not_exist, Toast.LENGTH_SHORT).show();
finish();
return;
}
// 初始化界面控件
initViews();
}
/**
*
*
*/
private void initViews() {
// 获取密码输入框控件
mPasswordEditText = (EditText) findViewById(R.id.et_password);
// 获取返回按钮控件
mReturnButton = (Button) findViewById(R.id.btn_return);
// 获取确认按钮控件
mConfirmButton = (Button) findViewById(R.id.btn_confirm);
// 设置返回按钮的点击事件监听器
mReturnButton.setOnClickListener(new OnClickListener() {
@Override
public void onClick(View v) {
// 返回主界面
// 返回主界面关闭当前Activity
finish();
}
});
// 设置确认按钮的点击事件监听器
mConfirmButton.setOnClickListener(new OnClickListener() {
@Override
public void onClick(View v) {
// 获取用户输入的密码
String password = mPasswordEditText.getText().toString();
// 检查密码是否为空
if (TextUtils.isEmpty(password)) {
// 密码为空,显示提示信息
Toast.makeText(PasswordInputActivity.this,
R.string.password_empty, Toast.LENGTH_SHORT).show();
return;
}
// 验证密码
// 验证密码是否正确
if (PasswordUtils.verifyPassword(PasswordInputActivity.this, mNoteId, password)) {
// 密码正确,跳转到便签编辑界面
// 密码正确,创建Intent跳转到便签编辑界面
Intent intent = new Intent(PasswordInputActivity.this, NoteEditActivity.class);
// 设置Intent动作为查看
intent.setAction(Intent.ACTION_VIEW);
// 传递便签ID
intent.putExtra(Intent.EXTRA_UID, mNoteId);
// 启动便签编辑Activity
startActivity(intent);
// 关闭当前Activity
finish();
} else {
// 密码错误
// 密码错误,显示错误提示
Toast.makeText(PasswordInputActivity.this,
R.string.password_error, Toast.LENGTH_SHORT).show();
// 清空密码输入框,方便用户重新输入
mPasswordEditText.setText("");
}
}

@ -37,68 +37,132 @@ import net.micode.notes.data.Notes;
import net.micode.notes.data.Notes.NoteColumns;
import net.micode.notes.tool.DataUtils;
/**
* Activity
* 便
*
*
* 1. 便
* 2. 便
* 3. 便
* 4. 便
*/
public class TrashActivity extends Activity implements OnClickListener {
/** 日志标签,用于调试和日志输出 */
private static final String TAG = "TrashActivity";
/** 回收站列表查询的Token标识 */
private static final int TRASH_LIST_QUERY_TOKEN = 0;
/** 返回按钮控件 */
private Button mReturnButton;
/** 清空所有按钮控件 */
private Button mCleanAllButton;
/** 回收站列表视图 */
private ListView mTrashListView;
/** 回收站列表适配器 */
private NotesListAdapter mTrashListAdapter;
/** 内容解析器,用于访问数据库 */
private ContentResolver mContentResolver;
/** 后台查询处理器,用于异步查询回收站数据 */
private BackgroundQueryHandler mBackgroundQueryHandler;
/**
* Activity
*
*
* @param savedInstanceState
*/
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
// 设置布局文件
setContentView(R.layout.activity_trash);
// 初始化界面控件和资源
initResources();
}
/**
* Activity
* Activity
*/
@Override
protected void onStart() {
super.onStart();
// 启动异步查询回收站列表
startAsyncTrashListQuery();
}
/**
*
*
*/
private void initResources() {
// 获取内容解析器
mContentResolver = this.getContentResolver();
// 创建后台查询处理器
mBackgroundQueryHandler = new BackgroundQueryHandler(mContentResolver);
// 绑定返回按钮
mReturnButton = (Button) findViewById(R.id.btn_return);
if (mReturnButton != null) {
mReturnButton.setOnClickListener(this);
}
// 绑定清空所有按钮
mCleanAllButton = (Button) findViewById(R.id.btn_clean_all);
if (mCleanAllButton != null) {
mCleanAllButton.setOnClickListener(this);
}
// 绑定回收站列表视图
mTrashListView = (ListView) findViewById(R.id.trash_list);
if (mTrashListView != null) {
// 创建列表适配器
mTrashListAdapter = new NotesListAdapter(this);
// 设置适配器
mTrashListView.setAdapter(mTrashListAdapter);
// 设置列表项点击监听器
mTrashListView.setOnItemClickListener(new OnTrashItemClickListener());
}
}
/**
*
* 使
*/
private void startAsyncTrashListQuery() {
mBackgroundQueryHandler.startQuery(TRASH_LIST_QUERY_TOKEN, null,
Notes.CONTENT_TRASH_URI, NoteItemData.PROJECTION, null, null,
NoteColumns.MODIFIED_DATE + " DESC");
}
/**
*
* AsyncQueryHandler线
*/
private final class BackgroundQueryHandler extends AsyncQueryHandler {
/**
*
* @param contentResolver
*/
public BackgroundQueryHandler(ContentResolver contentResolver) {
super(contentResolver);
}
/**
*
* 线UI
*
* @param token Token
* @param cookie Cookie
* @param cursor
*/
@Override
protected void onQueryComplete(int token, Object cookie, Cursor cursor) {
switch (token) {
case TRASH_LIST_QUERY_TOKEN:
// 更新列表适配器的游标,刷新列表显示
mTrashListAdapter.changeCursor(cursor);
break;
default:
@ -107,19 +171,42 @@ public class TrashActivity extends Activity implements OnClickListener {
}
}
/**
*
*
*/
private class OnTrashItemClickListener implements OnItemClickListener {
/**
*
*
* @param parent
* @param view
* @param position
* @param id ID
*/
@Override
public void onItemClick(AdapterView<?> parent, View view, int position, long id) {
// 检查视图是否为NotesListItem类型
if (view instanceof NotesListItem) {
// 获取列表项数据
NoteItemData item = ((NotesListItem) view).getItemData();
// 显示操作对话框
showTrashItemDialog(item);
}
}
}
/**
* 便
*
*
* @param item 便
*/
private void showTrashItemDialog(final NoteItemData item) {
AlertDialog.Builder builder = new AlertDialog.Builder(this);
// 设置对话框标题为便签摘要
builder.setTitle(item.getSnippet());
// 设置操作选项:删除和恢复
builder.setItems(new String[] {
getString(R.string.menu_delete),
getString(R.string.button_recovery)
@ -127,73 +214,120 @@ public class TrashActivity extends Activity implements OnClickListener {
@Override
public void onClick(DialogInterface dialog, int which) {
switch (which) {
case 0: // Delete - 彻底删除
case 0: // 彻底删除选项
permanentlyDeleteNote(item.getId());
break;
case 1: // Recovery - 恢复
case 1: // 恢复选项
recoverNote(item.getId());
break;
}
}
});
// 显示对话框
builder.show();
}
/**
* 便
* 便
*
* @param noteId 便ID
*/
private void permanentlyDeleteNote(long noteId) {
// 调用工具类方法彻底删除便签
if (DataUtils.permanentlyDeleteFromTrash(mContentResolver, noteId, this)) {
// 删除成功,显示提示信息
Toast.makeText(this, getString(R.string.menu_delete) + " " + getString(R.string.menu_success),
Toast.LENGTH_SHORT).show();
startAsyncTrashListQuery(); // Refresh list
// 刷新列表显示
startAsyncTrashListQuery();
} else {
// 删除失败,显示错误提示
Toast.makeText(this, getString(R.string.menu_delete) + " " + getString(R.string.failed_sdcard_export),
Toast.LENGTH_SHORT).show();
}
}
/**
* 便
* 便
*
* @param noteId 便ID
*/
private void recoverNote(long noteId) {
// 调用工具类方法恢复便签
if (DataUtils.restoreNoteFromTrash(mContentResolver, noteId, this)) {
// 恢复成功,显示提示信息
Toast.makeText(this, getString(R.string.button_recovery) + " " + getString(R.string.menu_success),
Toast.LENGTH_SHORT).show();
startAsyncTrashListQuery(); // Refresh list
// 刷新列表显示
startAsyncTrashListQuery();
} else {
// 恢复失败,显示错误提示
Toast.makeText(this, getString(R.string.button_recovery) + " " + getString(R.string.failed_sdcard_export),
Toast.LENGTH_SHORT).show();
}
}
/**
*
*
*/
private void showClearAllTrashDialog() {
AlertDialog.Builder builder = new AlertDialog.Builder(this);
// 设置对话框标题
builder.setTitle(getString(R.string.button_clean_all));
// 设置对话框消息,提醒用户此操作不可恢复
builder.setMessage(getString(R.string.alert_message_clear_all_trash));
// 设置确认按钮
builder.setPositiveButton(android.R.string.ok,
new DialogInterface.OnClickListener() {
@Override
public void onClick(DialogInterface dialog, int which) {
// 用户确认后执行清空操作
clearAllTrash();
}
});
// 设置取消按钮
builder.setNegativeButton(android.R.string.cancel, null);
// 显示对话框
builder.show();
}
/**
* 便
* 便
*/
private void clearAllTrash() {
// 调用工具类方法清空所有回收站便签
if (DataUtils.clearAllTrash(mContentResolver, this)) {
// 清空成功,显示提示信息
Toast.makeText(this, getString(R.string.button_clean_all) + " " + getString(R.string.menu_success),
Toast.LENGTH_SHORT).show();
startAsyncTrashListQuery(); // Refresh list to clear UI
// 刷新列表显示清空UI
startAsyncTrashListQuery();
} else {
// 清空失败,显示错误提示
Toast.makeText(this, getString(R.string.button_clean_all) + " " + getString(R.string.failed_sdcard_export),
Toast.LENGTH_SHORT).show();
}
}
/**
*
*
*
* @param v
*/
@Override
public void onClick(View v) {
switch (v.getId()) {
case R.id.btn_return:
// 返回按钮关闭当前Activity
finish();
break;
case R.id.btn_clean_all:
// 清空所有按钮:显示确认对话框
showClearAllTrashDialog();
break;
default:

Loading…
Cancel
Save