You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
note/src/NotesListActivity.java

962 lines
44 KiB

This file contains ambiguous Unicode characters!

This file contains ambiguous Unicode characters that may be confused with others in your current locale. If your use case is intentional and legitimate, you can safely ignore this warning. Use the Escape button to highlight these characters.

/*
* Copyright (c) 2010-2011, The MiCode Open Source Community (www.micode.net)
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package net.micode.notes.ui; // 定义类的包名
import android.app.Activity; // 导入Activity类
import android.app.AlertDialog; // 导入AlertDialog类
import android.app.Dialog; // 导入Dialog类
import android.appwidget.AppWidgetManager; // 导入AppWidgetManager类
import android.content.AsyncQueryHandler; // 导入AsyncQueryHandler类
import android.content.ContentResolver; // 导入ContentResolver类
import android.content.ContentValues; // 导入ContentValues类
import android.content.Context; // 导入Context类
import android.content.DialogInterface; // 导入DialogInterface类
import android.content.Intent; // 导入Intent类
import android.content.SharedPreferences; // 导入SharedPreferences类
import android.content.res.Resources; // 导入Resources类
import android.database.Cursor; // 导入Cursor类
import android.os.AsyncTask; // 导入AsyncTask类
import android.os.Bundle; // 导入Bundle类
import android.preference.PreferenceManager; // 导入PreferenceManager类
import android.text.Editable; // 导入Editable类
import android.text.TextUtils; // 导入TextUtils类
import android.text.TextWatcher; // 导入TextWatcher类
import android.util.Log; // 导入Log类
import android.view.ActionMode; // 导入ActionMode类
import android.view.ContextMenu; // 导入ContextMenu类
import android.view.Display; // 导入Display类
import android.view.HapticFeedbackConstants; // 导入HapticFeedbackConstants类
import android.view.LayoutInflater; // 导入LayoutInflater类
import android.view.Menu; // 导入Menu类
import android.view.MenuItem; // 导入MenuItem类
import android.view.MotionEvent; // 导入MotionEvent类
import android.view.View; // 导入View类
import android.view.inputmethod.InputMethodManager; // 导入InputMethodManager类
import android.widget.AdapterView; // 导入AdapterView类
import android.widget.Button; // 导入Button类
import android.widget.EditText; // 导入EditText类
import android.widget.ListView; // 导入ListView类
import android.widget.PopupMenu; // 导入PopupMenu类
import android.widget.TextView; // 导入TextView类
import android.widget.Toast; // 导入Toast类
import net.micode.notes.R; // 导入项目的资源文件
import net.micode.notes.data.Notes; // 导入Notes类
import net.micode.notes.data.Notes.NoteColumns; // 导入笔记列定义
import net.micode.notes.gtask.remote.GTaskSyncService; // 导入GTaskSyncService类
import net.micode.notes.model.WorkingNote; // 导入WorkingNote类
import net.micode.notes.tool.BackupUtils; // 导入BackupUtils类
import net.micode.notes.tool.DataUtils; // 导入DataUtils类
import net.micode.notes.tool.ResourceParser; // 导入ResourceParser类
import net.micode.notes.ui.NotesListAdapter.AppWidgetAttribute; // 导入AppWidgetAttribute类
import net.micode.notes.widget.NoteWidgetProvider_2x; // 导入NoteWidgetProvider_2x类
import net.micode.notes.widget.NoteWidgetProvider_4x; // 导入NoteWidgetProvider_4x类
import java.io.BufferedReader; // 导入BufferedReader类
import java.io.IOException; // 导入IOException类
import java.io.InputStream; // 导入InputStream类
import java.io.InputStreamReader; // 导入InputStreamReader类
import java.util.HashSet; // 导入HashSet类
public class NotesListActivity extends Activity implements OnClickListener, OnItemLongClickListener {
// 定义查询令牌
private static final int FOLDER_NOTE_LIST_QUERY_TOKEN = 0;
private static final int FOLDER_LIST_QUERY_TOKEN = 1;
// 定义菜单项
private static final int MENU_FOLDER_DELETE = 0;
private static final int MENU_FOLDER_VIEW = 1;
private static final int MENU_FOLDER_CHANGE_NAME = 2;
// 定义偏好设置键
private static final String PREFERENCE_ADD_INTRODUCTION = "net.micode.notes.introduction";
// 定义枚举类型,表示列表编辑状态
private enum ListEditState {
NOTE_LIST, SUB_FOLDER, CALL_RECORD_FOLDER
};
// 初始化成员变量
private ListEditState mState; // 列表编辑状态
private BackgroundQueryHandler mBackgroundQueryHandler; // 后台查询处理器
private NotesListAdapter mNotesListAdapter; // 笔记列表适配器
private ListView mNotesListView; // 笔记列表视图
private Button mAddNewNote; // 添加新笔记按钮
private boolean mDispatch; // 是否分发事件
private int mOriginY; // 原始Y坐标
private int mDispatchY; // 分发Y坐标
private TextView mTitleBar; // 标题栏
private long mCurrentFolderId; // 当前文件夹ID
private ContentResolver mContentResolver; // 内容解析器
private ModeCallback mModeCallBack; // 模式回调
private static final String TAG = "NotesListActivity"; // 日志标签
// 定义常量
public static final int NOTES_LISTVIEW_SCROLL_RATE = 30;
private NoteItemData mFocusNoteDataItem; // 焦点笔记数据项
private static final String NORMAL_SELECTION = NoteColumns.PARENT_ID + "=?";
private static final String ROOT_FOLDER_SELECTION = "(" + NoteColumns.TYPE + "<>"
+ Notes.TYPE_SYSTEM + " AND " + NoteColumns.PARENT_ID + "=?)" + " OR ("
+ NoteColumns.ID + "=" + Notes.ID_CALL_RECORD_FOLDER + " AND "
+ NoteColumns.NOTES_COUNT + ">0)";
// 定义请求码
private final static int REQUEST_CODE_OPEN_NODE = 102;
private final static int REQUEST_CODE_NEW_NODE = 103;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState); // 调用父类的onCreate方法
setContentView(R.layout.note_list); // 设置内容视图为note_list布局
initResources(); // 初始化资源
/**
* 当用户首次使用此应用程序时插入介绍
*/
setAppInfoFromRawRes(); // 从raw资源文件中设置应用信息
}
@Override
protected void onActivityResult(int requestCode, int resultCode, Intent data) {
if (resultCode == RESULT_OK // 如果结果码为OK
&& (requestCode == REQUEST_CODE_OPEN_NODE || requestCode == REQUEST_CODE_NEW_NODE)) { // 如果请求码为打开节点或新建节点
mNotesListAdapter.changeCursor(null); // 更改适配器的游标
} else {
super.onActivityResult(requestCode, resultCode, data); // 调用父类的onActivityResult方法
}
}
private void setAppInfoFromRawRes() {
SharedPreferences sp = PreferenceManager.getDefaultSharedPreferences(this); // 获取默认的SharedPreferences
if (!sp.getBoolean(PREFERENCE_ADD_INTRODUCTION, false)) { // 如果没有添加过介绍
StringBuilder sb = new StringBuilder(); // 创建字符串构建器
InputStream in = null; // 创建输入流
try {
in = getResources().openRawResource(R.raw.introduction); // 打开raw资源文件
if (in != null) { // 如果输入流不为空
InputStreamReader isr = new InputStreamReader(in); // 创建输入流读取器
BufferedReader br = new BufferedReader(isr); // 创建缓冲读取器
char [] buf = new char[1024]; // 创建缓冲区
int len = 0; // 初始化长度
while ((len = br.read(buf)) > 0) { // 读取数据
sb.append(buf, 0, len); // 追加到字符串构建器
}
} else {
Log.e(TAG, "Read introduction file error"); // 日志记录错误
return;
}
} catch (IOException e) {
e.printStackTrace(); // 打印异常堆栈
return;
} finally {
if(in != null) { // 如果输入流不为空
try {
in.close(); // 关闭输入流
} catch (IOException e) {
// TODO Auto-generated catch block
e.printStackTrace(); // 打印异常堆栈
}
}
}
WorkingNote note = WorkingNote.createEmptyNote(this, Notes.ID_ROOT_FOLDER, // 创建空笔记
AppWidgetManager.INVALID_APPWIDGET_ID, Notes.TYPE_WIDGET_INVALIDE,
ResourceParser.RED);
note.setWorkingText(sb.toString()); // 设置笔记文本
if (note.saveNote()) { // 如果保存笔记成功
sp.edit().putBoolean(PREFERENCE_ADD_INTRODUCTION, true).commit(); // 设置已添加介绍
} else {
Log.e(TAG, "Save introduction note error"); // 日志记录错误
return;
}
}
}
}
@Override
protected void onStart() {
super.onStart(); // 调用父类的onStart方法
startAsyncNotesListQuery(); // 启动异步笔记列表查询
}
private void initResources() {
mContentResolver = this.getContentResolver(); // 获取内容解析器
mBackgroundQueryHandler = new BackgroundQueryHandler(this.getContentResolver()); // 创建后台查询处理器
mCurrentFolderId = Notes.ID_ROOT_FOLDER; // 设置当前文件夹ID为根文件夹ID
mNotesListView = (ListView) findViewById(R.id.notes_list); // 获取笔记列表视图
mNotesListView.addFooterView(LayoutInflater.from(this).inflate(R.layout.note_list_footer, null), null, false); // 添加列表底部视图
mNotesListView.setOnItemClickListener(new OnListItemClickListener()); // 设置列表项点击监听器
mNotesListView.setOnItemLongClickListener(this); // 设置列表项长按监听器
mNotesListAdapter = new NotesListAdapter(this); // 创建笔记列表适配器
mNotesListView.setAdapter(mNotesListAdapter); // 设置列表适配器
mAddNewNote = (Button) findViewById(R.id.btn_new_note); // 获取添加新笔记按钮
mAddNewNote.setOnClickListener(this); // 设置按钮点击监听器
mAddNewNote.setOnTouchListener(new NewNoteOnTouchListener()); // 设置按钮触摸监听器
mDispatch = false; // 初始化分发标志为false
mDispatchY = 0; // 初始化分发Y坐标为0
mOriginY = 0; // 初始化原始Y坐标为0
mTitleBar = (TextView) findViewById(R.id.tv_title_bar); // 获取标题栏
mState = ListEditState.NOTE_LIST; // 初始化列表编辑状态为NOTE_LIST
mModeCallBack = new ModeCallback(); // 创建模式回调
}
private class ModeCallback implements ListView.MultiChoiceModeListener, OnMenuItemClickListener {
private DropdownMenu mDropDownMenu; // 下拉菜单
private ActionMode mActionMode; // 动作模式
private MenuItem mMoveMenu; // 移动菜单项
public boolean onCreateActionMode(ActionMode mode, Menu menu) {
// 填充动作模式菜单
getMenuInflater().inflate(R.menu.note_list_options, menu);
menu.findItem(R.id.delete).setOnMenuItemClickListener(this);
mMoveMenu = menu.findItem(R.id.move);
// 根据是否有文件夹和是否为通话记录文件夹设置移动菜单项的可见性
if (mFocusNoteDataItem.getParentId() == Notes.ID_CALL_RECORD_FOLDER
|| DataUtils.getUserFolderCount(mContentResolver) == 0) {
mMoveMenu.setVisible(false);
} else {
mMoveMenu.setVisible(true);
mMoveMenu.setOnMenuItemClickListener(this);
}
mActionMode = mode; // 设置动作模式
mNotesListAdapter.setChoiceMode(true); // 设置列表选择模式为true
mNotesListView.setLongClickable(false); // 设置列表不可长按
mAddNewNote.setVisibility(View.GONE); // 隐藏添加新笔记按钮
// 填充自定义视图
View customView = LayoutInflater.from(NotesListActivity.this).inflate(
R.layout.note_list_dropdown_menu, null);
mode.setCustomView(customView);
mDropDownMenu = new DropdownMenu(NotesListActivity.this,
(Button) customView.findViewById(R.id.selection_menu),
R.menu.note_list_dropdown);
mDropDownMenu.setOnDropdownMenuItemClickListener(new PopupMenu.OnMenuItemClickListener(){
public boolean onMenuItemClick(MenuItem item) {
// 切换全选状态
mNotesListAdapter.selectAll(!mNotesListAdapter.isAllSelected());
updateMenu();
return true;
}
});
return true;
}
private void updateMenu() {
// 更新菜单项
int selectedCount = mNotesListAdapter.getSelectedCount();
String format = getResources().getString(R.string.menu_select_title, selectedCount);
mDropDownMenu.setTitle(format);
MenuItem item = mDropDownMenu.findItem(R.id.action_select_all);
if (item != null) {
if (mNotesListAdapter.isAllSelected()) {
item.setChecked(true);
item.setTitle(R.string.menu_deselect_all);
} else {
item.setChecked(false);
item.setTitle(R.string.menu_select_all);
}
}
}
public boolean onPrepareActionMode(ActionMode mode, Menu menu) {
// TODO Auto-generated method stub
return false;
}
public boolean onActionItemClicked(ActionMode mode, MenuItem item) {
// TODO Auto-generated method stub
return false;
}
public void onDestroyActionMode(ActionMode mode) {
mNotesListAdapter.setChoiceMode(false); // 设置列表选择模式为false
mNotesListView.setLongClickable(true); // 设置列表可长按
mAddNewNote.setVisibility(View.VISIBLE); // 显示添加新笔记按钮
}
public void finishActionMode() {
mActionMode.finish(); // 结束动作模式
}
public void onItemCheckedStateChanged(ActionMode mode, int position, long id,
boolean checked) {
mNotesListAdapter.setCheckedItem(position, checked); // 设置列表项的选中状态
updateMenu(); // 更新菜单
}
public boolean onMenuItemClick(MenuItem item) {
if (mNotesListAdapter.getSelectedCount() == 0) {
Toast.makeText(NotesListActivity.this, getString(R.string.menu_select_none),
Toast.LENGTH_SHORT).show(); // 提示未选择任何项
return true;
}
switch (item.getItemId()) {
case R.id.delete:
// 显示删除确认对话框
AlertDialog.Builder builder = new AlertDialog.Builder(NotesListActivity.this);
builder.setTitle(getString(R.string.alert_title_delete));
builder.setIcon(android.R.drawable.ic_dialog_alert);
builder.setMessage(getString(R.string.alert_message_delete_notes,
mNotesListAdapter.getSelectedCount()));
builder.setPositiveButton(android.R.string.ok,
new DialogInterface.OnClickListener() {
public void onClick(DialogInterface dialog,
int which) {
batchDelete(); // 批量删除
}
});
builder.setNegativeButton(android.R.string.cancel, null);
builder.show();
break;
case R.id.move:
startQueryDestinationFolders(); // 查询目标文件夹
break;
default:
return false;
}
return true;
}
}
private class NewNoteOnTouchListener implements OnTouchListener {
// 实现触摸监听器,处理“添加新笔记”按钮的触摸事件
public boolean onTouch(View v, MotionEvent event) {
switch (event.getAction()) {
case MotionEvent.ACTION_DOWN: {
// 获取屏幕高度和“添加新笔记”按钮的高度
Display display = getWindowManager().getDefaultDisplay();
int screenHeight = display.getHeight();
int newNoteViewHeight = mAddNewNote.getHeight();
int start = screenHeight - newNoteViewHeight;
// 计算触摸事件的Y坐标
int eventY = start + (int) event.getY();
// 如果处于子文件夹状态,则减去标题栏的高度
if (mState == ListEditState.SUB_FOLDER) {
eventY -= mTitleBar.getHeight();
start -= mTitleBar.getHeight();
}
// 如果触摸事件在“添加新笔记”按钮的透明部分,则将事件分发到列表视图
if (event.getY() < (event.getX() * (-0.12) + 94)) {
View view = mNotesListView.getChildAt(mNotesListView.getChildCount() - 1
- mNotesListView.getFooterViewsCount());
if (view != null && view.getBottom() > start
&& (view.getTop() < (start + 94))) {
mOriginY = (int) event.getY();
mDispatchY = eventY;
event.setLocation(event.getX(), mDispatchY);
mDispatch = true;
return mNotesListView.dispatchTouchEvent(event); // 分发触摸事件
}
}
break;
}
case MotionEvent.ACTION_MOVE: {
// 在移动事件中更新分发的Y坐标
if (mDispatch) {
mDispatchY += (int) event.getY() - mOriginY;
event.setLocation(event.getX(), mDispatchY);
return mNotesListView.dispatchTouchEvent(event); // 分发触摸事件
}
break;
}
default: {
// 在其他事件中,重置分发标志
if (mDispatch) {
event.setLocation(event.getX(), mDispatchY);
mDispatch = false;
return mNotesListView.dispatchTouchEvent(event); // 分发触摸事件
}
break;
}
}
return false; // 返回false表示未处理事件
}
}
private void startAsyncNotesListQuery() {
// 根据当前文件夹ID选择查询条件
String selection = (mCurrentFolderId == Notes.ID_ROOT_FOLDER) ? ROOT_FOLDER_SELECTION
: NORMAL_SELECTION;
// 启动异步查询,查询笔记列表
mBackgroundQueryHandler.startQuery(FOLDER_NOTE_LIST_QUERY_TOKEN, null,
Notes.CONTENT_NOTE_URI, NoteItemData.PROJECTION, selection, new String[] {
String.valueOf(mCurrentFolderId) // 添加当前文件夹ID作为查询参数
}, NoteColumns.TYPE + " DESC," + NoteColumns.MODIFIED_DATE + " DESC"); // 按类型和修改日期降序排列
}
private final class BackgroundQueryHandler extends AsyncQueryHandler {
public BackgroundQueryHandler(ContentResolver contentResolver) {
super(contentResolver); // 调用父类构造函数
}
@Override
protected void onQueryComplete(int token, Object cookie, Cursor cursor) {
switch (token) {
case FOLDER_NOTE_LIST_QUERY_TOKEN:
mNotesListAdapter.changeCursor(cursor); // 更新笔记列表适配器的游标
break;
case FOLDER_LIST_QUERY_TOKEN:
if (cursor != null && cursor.getCount() > 0) {
showFolderListMenu(cursor); // 显示文件夹列表菜单
} else {
Log.e(TAG, "Query folder failed"); // 日志记录查询文件夹失败
}
break;
default:
return; // 处理其他令牌
}
}
}
private void showFolderListMenu(Cursor cursor) {
AlertDialog.Builder builder = new AlertDialog.Builder(NotesListActivity.this); // 创建对话框构建器
builder.setTitle(R.string.menu_title_select_folder); // 设置对话框标题
final FoldersListAdapter adapter = new FoldersListAdapter(this, cursor); // 创建文件夹列表适配器
builder.setAdapter(adapter, new DialogInterface.OnClickListener() {
public void onClick(DialogInterface dialog, int which) {
// 移动选中的笔记到所选文件夹
DataUtils.batchMoveToFolder(mContentResolver,
mNotesListAdapter.getSelectedItemIds(), adapter.getItemId(which));
// 显示移动成功的提示
Toast.makeText(
NotesListActivity.this,
getString(R.string.format_move_notes_to_folder,
mNotesListAdapter.getSelectedCount(),
adapter.getFolderName(NotesListActivity.this, which)),
Toast.LENGTH_SHORT).show();
mModeCallBack.finishActionMode(); // 结束动作模式
}
});
builder.show(); // 显示对话框
}
private void createNewNote() {
// 创建新的笔记编辑意图
Intent intent = new Intent(this, NoteEditActivity.class);
intent.setAction(Intent.ACTION_INSERT_OR_EDIT); // 设置动作为插入或编辑
intent.putExtra(Notes.INTENT_EXTRA_FOLDER_ID, mCurrentFolderId); // 添加当前文件夹ID
this.startActivityForResult(intent, REQUEST_CODE_NEW_NODE); // 启动新笔记编辑活动
}
private void batchDelete() {
// 创建异步任务以批量删除笔记
new AsyncTask<Void, Void, HashSet<AppWidgetAttribute>>() {
protected HashSet<AppWidgetAttribute> doInBackground(Void... unused) {
HashSet<AppWidgetAttribute> widgets = mNotesListAdapter.getSelectedWidget(); // 获取选中小部件
if (!isSyncMode()) { // 如果不是同步模式
// 直接删除笔记
if (DataUtils.batchDeleteNotes(mContentResolver, mNotesListAdapter
.getSelectedItemIds())) {
// 删除成功
} else {
Log.e(TAG, "Delete notes error, should not happens"); // 日志记录删除错误
}
} else {
// 在同步模式下,将删除的笔记移动到垃圾文件夹
if (!DataUtils.batchMoveToFolder(mContentResolver, mNotesListAdapter
.getSelectedItemIds(), Notes.ID_TRASH_FOLER)) {
Log.e(TAG, "Move notes to trash folder error, should not happens"); // 日志记录移动错误
}
}
return widgets; // 返回小部件集合
}
@Override
protected void onPostExecute(HashSet<AppWidgetAttribute> widgets) {
if (widgets != null) { // 如果小部件集合不为空
for (AppWidgetAttribute widget : widgets) { // 遍历小部件
if (widget.widgetId != AppWidgetManager.INVALID_APPWIDGET_ID
&& widget.widgetType != Notes.TYPE_WIDGET_INVALIDE) {
updateWidget(widget.widgetId, widget.widgetType); // 更新小部件
}
}
}
mModeCallBack.finishActionMode(); // 结束动作模式
}
}.execute(); // 执行异步任务
}
private void deleteFolder(long folderId) {
// 如果文件夹ID是根文件夹ID记录错误并返回
if (folderId == Notes.ID_ROOT_FOLDER) {
Log.e(TAG, "Wrong folder id, should not happen " + folderId);
return;
}
HashSet<Long> ids = new HashSet<Long>(); // 创建文件夹ID集合
ids.add(folderId); // 添加文件夹ID
HashSet<AppWidgetAttribute> widgets = DataUtils.getFolderNoteWidget(mContentResolver,
folderId); // 获取与文件夹相关的小部件
if (!isSyncMode()) { // 如果不是同步模式
// 直接删除文件夹
DataUtils.batchDeleteNotes(mContentResolver, ids);
} else {
// 在同步模式下,将删除的文件夹移动到垃圾文件夹
DataUtils.batchMoveToFolder(mContentResolver, ids, Notes.ID_TRASH_FOLER);
}
if (widgets != null) { // 如果小部件集合不为空
for (AppWidgetAttribute widget : widgets) { // 遍历小部件
if (widget.widgetId != AppWidgetManager.INVALID_APPWIDGET_ID
&& widget.widgetType != Notes.TYPE_WIDGET_INVALIDE) {
updateWidget(widget.widgetId, widget.widgetType); // 更新小部件
}
}
}
}
private void openNode(NoteItemData data) {
// 创建打开笔记的意图
Intent intent = new Intent(this, NoteEditActivity.class);
intent.setAction(Intent.ACTION_VIEW); // 设置动作为查看
intent.putExtra(Intent.EXTRA_UID, data.getId()); // 添加笔记ID到意图
this.startActivityForResult(intent, REQUEST_CODE_OPEN_NODE); // 启动笔记编辑活动
}
private void openFolder(NoteItemData data) {
mCurrentFolderId = data.getId(); // 更新当前文件夹ID
startAsyncNotesListQuery(); // 启动异步笔记列表查询
// 根据文件夹ID设置状态
if (data.getId() == Notes.ID_CALL_RECORD_FOLDER) {
mState = ListEditState.CALL_RECORD_FOLDER; // 设置状态为通话记录文件夹
mAddNewNote.setVisibility(View.GONE); // 隐藏添加新笔记按钮
} else {
mState = ListEditState.SUB_FOLDER; // 设置状态为子文件夹
}
// 更新标题栏文本
if (data.getId() == Notes.ID_CALL_RECORD_FOLDER) {
mTitleBar.setText(R.string.call_record_folder_name); // 设置标题为通话记录文件夹名称
} else {
mTitleBar.setText(data.getSnippet()); // 设置标题为文件夹摘要
}
mTitleBar.setVisibility(View.VISIBLE); // 显示标题栏
}
public void onClick(View v) {
switch (v.getId()) {
case R.id.btn_new_note: // 如果点击的是添加新笔记按钮
createNewNote(); // 创建新笔记
break;
default:
break; // 其他情况不处理
}
}
private void showSoftInput() {
// 显示软键盘
InputMethodManager inputMethodManager = (InputMethodManager) getSystemService(Context.INPUT_METHOD_SERVICE);
if (inputMethodManager != null) {
inputMethodManager.toggleSoftInput(InputMethodManager.SHOW_FORCED, 0); // 强制显示软键盘
}
}
private void hideSoftInput(View view) {
// 隐藏软键盘
InputMethodManager inputMethodManager = (InputMethodManager) getSystemService(Context.INPUT_METHOD_SERVICE);
inputMethodManager.hideSoftInputFromWindow(view.getWindowToken(), 0);
}
private void showCreateOrModifyFolderDialog(final boolean create) {
// 显示创建或修改文件夹的对话框
final AlertDialog.Builder builder = new AlertDialog.Builder(this);
View view = LayoutInflater.from(this).inflate(R.layout.dialog_edit_text, null);
final EditText etName = (EditText) view.findViewById(R.id.et_foler_name);
showSoftInput(); // 显示软键盘
if (!create) {
if (mFocusNoteDataItem != null) {
etName.setText(mFocusNoteDataItem.getSnippet()); // 设置对话框中编辑框的文本
builder.setTitle(getString(R.string.menu_folder_change_name)); // 设置对话框标题为“修改文件夹名称”
} else {
Log.e(TAG, "The long click data item is null"); // 日志记录错误
return;
}
} else {
etName.setText(""); // 清空编辑框文本
builder.setTitle(this.getString(R.string.menu_create_folder)); // 设置对话框标题为“创建文件夹”
}
builder.setPositiveButton(android.R.string.ok, null); // 设置对话框的“确定”按钮
builder.setNegativeButton(android.R.string.cancel, new DialogInterface.OnClickListener() {
public void onClick(DialogInterface dialog, int which) {
hideSoftInput(etName); // 隐藏软键盘
}
});
final Dialog dialog = builder.setView(view).show(); // 显示对话框
final Button positive = (Button)dialog.findViewById(android.R.id.button1); // 获取“确定”按钮
positive.setOnClickListener(new OnClickListener() { // 设置“确定”按钮的点击事件
public void onClick(View v) {
hideSoftInput(etName); // 隐藏软键盘
String name = etName.getText().toString(); // 获取编辑框的文本
if (DataUtils.checkVisibleFolderName(mContentResolver, name)) { // 检查文件夹名称是否已存在
Toast.makeText(NotesListActivity.this, getString(R.string.folder_exist, name),
Toast.LENGTH_LONG).show(); // 显示提示
etName.setSelection(0, etName.length()); // 将光标移动到文本末尾
return;
}
if (!create) {
if (!TextUtils.isEmpty(name)) { // 如果不是创建新文件夹且名称不为空
ContentValues values = new ContentValues(); // 创建ContentValues对象
values.put(NoteColumns.SNIPPET, name); // 更新文件夹名称
values.put(NoteColumns.TYPE, Notes.TYPE_FOLDER); // 更新文件夹类型
values.put(NoteColumns.LOCAL_MODIFIED, 1); // 更新本地修改标志
mContentResolver.update(Notes.CONTENT_NOTE_URI, values, NoteColumns.ID
+ "=?", new String[] { String.valueOf(mFocusNoteDataItem.getId()) }); // 更新数据库
}
} else if (!TextUtils.isEmpty(name)) { // 如果是创建新文件夹且名称不为空
ContentValues values = new ContentValues(); // 创建ContentValues对象
values.put(NoteColumns.SNIPPET, name); // 设置文件夹名称
values.put(NoteColumns.TYPE, Notes.TYPE_FOLDER); // 设置文件夹类型
mContentResolver.insert(Notes.CONTENT_NOTE_URI, values); // 插入数据库
}
dialog.dismiss(); // 关闭对话框
}
});
if (TextUtils.isEmpty(etName.getText())) { // 如果编辑框文本为空
positive.setEnabled(false); // 禁用“确定”按钮
}
// 当编辑框文本变化时,启用或禁用“确定”按钮
etName.addTextChangedListener(new TextWatcher() {
public void beforeTextChanged(CharSequence s, int start, int count, int after) {
// TODO Auto-generated method stub
}
public void onTextChanged(CharSequence s, int start, int before, int count) {
if (TextUtils.isEmpty(etName.getText())) {
positive.setEnabled(false); // 禁用“确定”按钮
} else {
positive.setEnabled(true); // 启用“确定”按钮
}
}
public void afterTextChanged(Editable s) {
// TODO Auto-generated method stub
}
});
}
@Override
public void onBackPressed() {
// 处理返回键事件
switch (mState) {
case SUB_FOLDER:
mCurrentFolderId = Notes.ID_ROOT_FOLDER; // 设置当前文件夹ID为根文件夹ID
mState = ListEditState.NOTE_LIST; // 设置状态为NOTE_LIST
startAsyncNotesListQuery(); // 启动异步笔记列表查询
mTitleBar.setVisibility(View.GONE); // 隐藏标题栏
break;
case CALL_RECORD_FOLDER:
mCurrentFolderId = Notes.ID_ROOT_FOLDER; // 设置当前文件夹ID为根文件夹ID
mState = ListEditState.NOTE_LIST; // 设置状态为NOTE_LIST
mAddNewNote.setVisibility(View.VISIBLE); // 显示添加新笔记按钮
mTitleBar.setVisibility(View.GONE); // 隐藏标题栏
startAsyncNotesListQuery(); // 启动异步笔记列表查询
break;
case NOTE_LIST:
super.onBackPressed(); // 调用父类的onBackPressed方法
break;
default:
break;
}
}
private void updateWidget(int appWidgetId, int appWidgetType) {
// 创建更新小部件的意图
Intent intent = new Intent(AppWidgetManager.ACTION_APPWIDGET_UPDATE);
// 根据小部件类型设置小部件提供者类
if (appWidgetType == Notes.TYPE_WIDGET_2X) {
intent.setClass(this, NoteWidgetProvider_2x.class);
} else if (appWidgetType == Notes.TYPE_WIDGET_4X) {
intent.setClass(this, NoteWidgetProvider_4x.class);
} else {
Log.e(TAG, "Unspported widget type"); // 日志记录不支持的小部件类型
return; // 返回
}
// 添加小部件ID到意图
intent.putExtra(AppWidgetManager.EXTRA_APPWIDGET_IDS, new int[] {
appWidgetId
});
sendBroadcast(intent); // 发送广播以更新小部件
setResult(RESULT_OK, intent); // 设置结果为OK
}
private final OnCreateContextMenuListener mFolderOnCreateContextMenuListener = new OnCreateContextMenuListener() {
public void onCreateContextMenu(ContextMenu menu, View v, ContextMenuInfo menuInfo) {
// 创建上下文菜单
if (mFocusNoteDataItem != null) { // 如果焦点笔记数据项不为空
menu.setHeaderTitle(mFocusNoteDataItem.getSnippet()); // 设置菜单标题为笔记摘要
menu.add(0, MENU_FOLDER_VIEW, 0, R.string.menu_folder_view); // 添加查看菜单项
menu.add(0, MENU_FOLDER_DELETE, 0, R.string.menu_folder_delete); // 添加删除菜单项
menu.add(0, MENU_FOLDER_CHANGE_NAME, 0, R.string.menu_folder_change_name); // 添加修改名称菜单项
}
}
};
@Override
public void onContextMenuClosed(Menu menu) {
// 当上下文菜单关闭时
if (mNotesListView != null) {
mNotesListView.setOnCreateContextMenuListener(null); // 清除列表视图的上下文菜单监听器
}
super.onContextMenuClosed(menu); // 调用父类的onContextMenuClosed方法
}
@Override
public boolean onContextItemSelected(MenuItem item) {
// 处理上下文菜单项的选择
if (mFocusNoteDataItem == null) { // 如果焦点笔记数据项为空
Log.e(TAG, "The long click data item is null"); // 日志记录错误
return false; // 返回false
}
switch (item.getItemId()) { // 根据菜单项ID执行相应操作
case MENU_FOLDER_VIEW:
openFolder(mFocusNoteDataItem); // 打开文件夹
break;
case MENU_FOLDER_DELETE:
// 显示删除确认对话框
AlertDialog.Builder builder = new AlertDialog.Builder(NotesListActivity.this);
builder.setTitle(getString(R.string.alert_title_delete)); // 设置对话框标题
builder.setIcon(android.R.drawable.ic_dialog_alert); // 设置对话框图标
builder.setMessage(getString(R.string.alert_message_delete_folder)); // 设置对话框消息
builder.setPositiveButton(android.R.string.ok,
new DialogInterface.OnClickListener() {
public void onClick(DialogInterface dialog, int which) {
deleteFolder(mFocusNoteDataItem.getId()); // 删除文件夹
}
});
builder.setNegativeButton(android.R.string.cancel, null); // 设置取消按钮
builder.show(); // 显示对话框
break;
case MENU_FOLDER_CHANGE_NAME:
showCreateOrModifyFolderDialog(false); // 显示修改文件夹名称对话框
break;
default:
break; // 其他情况不处理
}
return true; // 返回true表示菜单项已处理
}
@Override
public boolean onPrepareOptionsMenu(Menu menu) {
// 准备选项菜单
menu.clear(); // 清除菜单项
if (mState == ListEditState.NOTE_LIST) { // 如果当前状态是笔记列表
getMenuInflater().inflate(R.menu.note_list, menu); // 加载笔记列表菜单
// 设置同步或取消同步的菜单项标题
menu.findItem(R.id.menu_sync).setTitle(
GTaskSyncService.isSyncing() ? R.string.menu_sync_cancel : R.string.menu_sync);
} else if (mState == ListEditState.SUB_FOLDER) { // 如果当前状态是子文件夹
getMenuInflater().inflate(R.menu.sub_folder, menu); // 加载子文件夹菜单
} else if (mState == ListEditState.CALL_RECORD_FOLDER) { // 如果当前状态是通话记录文件夹
getMenuInflater().inflate(R.menu.call_record_folder, menu); // 加载通话记录文件夹菜单
} else {
Log.e(TAG, "Wrong state:" + mState); // 日志记录错误状态
}
return true; // 返回true表示菜单已准备好
}
@Override
public boolean onOptionsItemSelected(MenuItem item) {
// 处理选项菜单项的选择
switch (item.getItemId()) {
case R.id.menu_new_folder: { // 如果点击的是新建文件夹菜单项
showCreateOrModifyFolderDialog(true); // 显示创建文件夹对话框
break;
}
case R.id.menu_export_text: { // 如果点击的是导出文本菜单项
exportNoteToText(); // 导出笔记为文本
break;
}
case R.id.menu_sync: { // 如果点击的是同步菜单项
if (isSyncMode()) { // 如果处于同步模式
if (TextUtils.equals(item.getTitle(), getString(R.string.menu_sync))) {
GTaskSyncService.startSync(this); // 开始同步
} else {
GTaskSyncService.cancelSync(this); // 取消同步
}
} else {
startPreferenceActivity(); // 启动偏好设置活动
}
break;
}
case R.id.menu_setting: { // 如果点击的是设置菜单项
startPreferenceActivity(); // 启动偏好设置活动
break;
}
case R.id.menu_new_note: { // 如果点击的是新建笔记菜单项
createNewNote(); // 创建新笔记
break;
}
case R.id.menu_search: // 如果点击的是搜索菜单项
onSearchRequested(); // 请求搜索
break;
default:
break; // 其他情况不处理
}
return true; // 返回true表示菜单项已处理
}
@Override
public boolean onSearchRequested() {
// 请求搜索
startSearch(null, false, null /* appData */, false); // 启动搜索
return true; // 返回true表示搜索请求已处理
}
private void exportNoteToText() {
// 使用备份工具实例导出笔记到文本
final BackupUtils backup = BackupUtils.getInstance(NotesListActivity.this);
new AsyncTask<Void, Void, Integer>() {
@Override
protected Integer doInBackground(Void... unused) {
// 后台任务:调用备份工具的导出方法
return backup.exportToText();
}
@Override
protected void onPostExecute(Integer result) {
// 根据导出结果弹出不同的对话框
if (result == BackupUtils.STATE_SD_CARD_UNMOUONTED) {
// SD卡未挂载
AlertDialog.Builder builder = new AlertDialog.Builder(NotesListActivity.this);
builder.setTitle(NotesListActivity.this
.getString(R.string.failed_sdcard_export));
builder.setMessage(NotesListActivity.this
.getString(R.string.error_sdcard_unmounted));
builder.setPositiveButton(android.R.string.ok, null);
builder.show();
} else if (result == BackupUtils.STATE_SUCCESS) {
// 导出成功
AlertDialog.Builder builder = new AlertDialog.Builder(NotesListActivity.this);
builder.setTitle(NotesListActivity.this
.getString(R.string.success_sdcard_export));
builder.setMessage(NotesListActivity.this.getString(
R.string.format_exported_file_location, backup
.getExportedTextFileName(), backup.getExportedTextFileDir()));
builder.setPositiveButton(android.R.string.ok, null);
builder.show();
} else if (result == BackupUtils.STATE_SYSTEM_ERROR) {
// 系统错误
AlertDialog.Builder builder = new AlertDialog.Builder(NotesListActivity.this);
builder.setTitle(NotesListActivity.this
.getString(R.string.failed_sdcard_export));
builder.setMessage(NotesListActivity.this
.getString(R.string.error_sdcard_export));
builder.setPositiveButton(android.R.string.ok, null);
builder.show();
}
}
}.execute(); // 执行异步任务
}
private boolean isSyncMode() {
// 检查是否处于同步模式
return NotesPreferenceActivity.getSyncAccountName(this).trim().length() > 0;
}
private void startPreferenceActivity() {
// 启动偏好设置活动
Activity from = getParent() != null ? getParent() : this;
Intent intent = new Intent(from, NotesPreferenceActivity.class);
from.startActivityIfNeeded(intent, -1);
}
private class OnListItemClickListener implements OnItemClickListener {
public void onItemClick(AdapterView<?> parent, View view, int position, long id) {
// 处理列表项点击事件
if (view instanceof NotesListItem) {
NoteItemData item = ((NotesListItem) view).getItemData();
if (mNotesListAdapter.isInChoiceMode()) {
// 处理多选模式下的点击事件
if (item.getType() == Notes.TYPE_NOTE) {
position = position - mNotesListView.getHeaderViewsCount();
mModeCallBack.onItemCheckedStateChanged(null, position, id,
!mNotesListAdapter.isSelectedItem(position));
}
return;
}
// 根据当前状态处理点击事件
switch (mState) {
case NOTE_LIST:
if (item.getType() == Notes.TYPE_FOLDER
|| item.getType() == Notes.TYPE_SYSTEM) {
openFolder(item); // 打开文件夹
} else if (item.getType() == Notes.TYPE_NOTE) {
openNode(item); // 打开笔记
} else {
Log.e(TAG, "Wrong note type in NOTE_LIST");
}
break;
case SUB_FOLDER:
case CALL_RECORD_FOLDER:
if (item.getType() == Notes.TYPE_NOTE) {
openNode(item); // 打开笔记
} else {
Log.e(TAG, "Wrong note type in SUB_FOLDER");
}
break;
default:
break;
}
}
}
}
private void startQueryDestinationFolders() {
// 查询目标文件夹
String selection = NoteColumns.TYPE + "=? AND " + NoteColumns.PARENT_ID + "<>? AND " + NoteColumns.ID + "<>?";
selection = (mState == ListEditState.NOTE_LIST) ? selection:
"(" + selection + ") OR (" + NoteColumns.ID + "=" + Notes.ID_ROOT_FOLDER + ")";
mBackgroundQueryHandler.startQuery(FOLDER_LIST_QUERY_TOKEN,
null,
Notes.CONTENT_NOTE_URI,
FoldersListAdapter.PROJECTION,
selection,
new String[] {
String.valueOf(Notes.TYPE_FOLDER),
String.valueOf(Notes.ID_TRASH_FOLER),
String.valueOf(mCurrentFolderId)
},
NoteColumns.MODIFIED_DATE + " DESC");
}
public boolean onItemLongClick(AdapterView<?> parent, View view, int position, long id) {
// 处理列表项长按事件
if (view instanceof NotesListItem) {
mFocusNoteDataItem = ((NotesListItem) view).getItemData();
if (mFocusNoteDataItem.getType() == Notes.TYPE_NOTE && !mNotesListAdapter.isInChoiceMode()) {
// 启动动作模式
if (mNotesListView.startActionMode(mModeCallBack) != null) {
mModeCallBack.onItemCheckedStateChanged(null, position, id, true);
mNotesListView.performHapticFeedback(HapticFeedbackConstants.LONG_PRESS);
} else {
Log.e(TAG, "startActionMode fails");
}
} else if (mFocusNoteDataItem.getType() == Notes.TYPE_FOLDER) {
mNotesListView.setOnCreateContextMenuListener(mFolderOnCreateContextMenuListener);
}
}
return false;
}