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.
xiaomi/src/ui/NotesListActivity.java

982 lines
46 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;
import android.app.AlertDialog;
import android.app.Dialog;
import android.appwidget.AppWidgetManager;
import android.content.AsyncQueryHandler;
import android.content.ContentResolver;
import android.content.ContentValues;
import android.content.Context;
import android.content.DialogInterface;
import android.content.Intent;
import android.content.SharedPreferences;
import android.database.Cursor;
import android.os.AsyncTask;
import android.os.Bundle;
import android.preference.PreferenceManager;
import android.text.Editable;
import android.text.TextUtils;
import android.text.TextWatcher;
import android.util.Log;
import android.view.ActionMode;
import android.view.ContextMenu;
import android.view.ContextMenu.ContextMenuInfo;
import android.view.Display;
import android.view.HapticFeedbackConstants;
import android.view.LayoutInflater;
import android.view.Menu;
import android.view.MenuItem;
import android.view.MenuItem.OnMenuItemClickListener;
import android.view.MotionEvent;
import android.view.View;
import android.view.View.OnClickListener;
import android.view.View.OnCreateContextMenuListener;
import android.view.View.OnTouchListener;
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.ListView;
import android.widget.PopupMenu;
import android.widget.TextView;
import android.widget.Toast;
import net.micode.notes.R;
import net.micode.notes.data.Notes;
import net.micode.notes.data.Notes.NoteColumns;
import net.micode.notes.gtask.remote.GTaskSyncService;
import net.micode.notes.model.WorkingNote;
import net.micode.notes.tool.BackupUtils;
import net.micode.notes.tool.DataUtils;
import net.micode.notes.tool.ResourceParser;
import net.micode.notes.ui.NotesListAdapter.AppWidgetAttribute;
import net.micode.notes.widget.NoteWidgetProvider_2x;
import net.micode.notes.widget.NoteWidgetProvider_4x;
import java.io.BufferedReader;
import java.io.IOException;
import java.io.InputStream;
import java.io.InputStreamReader;
import java.util.HashSet;
```java
// 便签列表活动,负责显示和管理便签/文件夹列表
// 便签列表活动,负责显示和管理便签/文件夹列表
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; // 文件夹列表查询
// 上下文菜单项ID
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; // 当前焦点便签数据项
// SQL查询条件
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);
setContentView(R.layout.note_list); // 设置布局
initResources(); // 初始化资源
// 首次使用时添加介绍便签
setAppInfoFromRawRes();
}
// 活动结果回调
@Override
protected void onActivityResult(int requestCode, int resultCode, Intent data) {
if (resultCode == RESULT_OK
&& (requestCode == REQUEST_CODE_OPEN_NODE || requestCode == REQUEST_CODE_NEW_NODE)) {
mNotesListAdapter.changeCursor(null); // 清空游标,触发重新查询
} else {
super.onActivityResult(requestCode, resultCode, data);
}
}
// 从资源文件设置应用介绍便签
private void setAppInfoFromRawRes() {
SharedPreferences sp = PreferenceManager.getDefaultSharedPreferences(this);
// 检查是否已添加介绍便签
if (!sp.getBoolean(PREFERENCE_ADD_INTRODUCTION, false)) {
StringBuilder sb = new StringBuilder();
InputStream in = null;
try {
in = getResources().openRawResource(R.raw.introduction); // 读取原始资源
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) {
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();
startAsyncNotesListQuery(); // 开始异步查询便签列表
}
// 初始化资源
private void initResources() {
mContentResolver = this.getContentResolver(); // 获取内容解析器
mBackgroundQueryHandler = new BackgroundQueryHandler(this.getContentResolver()); // 创建后台查询处理器
mCurrentFolderId = Notes.ID_ROOT_FOLDER; // 初始化为根文件夹
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; // 初始化事件分发标志
mDispatchY = 0; // 初始化分发Y坐标
mOriginY = 0; // 初始化原始Y坐标
mTitleBar = (TextView) findViewById(R.id.tv_title_bar); // 获取标题栏
mState = ListEditState.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); // 设置适配器为选择模式
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) {
return false; // 不准备菜单
}
public boolean onActionItemClicked(ActionMode mode, MenuItem item) {
return false; // 不处理操作项点击
}
// 销毁操作模式
public void onDestroyActionMode(ActionMode mode) {
mNotesListAdapter.setChoiceMode(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(); // 事件绝对Y坐标
// 减去标题栏高度(子文件夹状态)
if (mState == ListEditState.SUB_FOLDER) {
eventY -= mTitleBar.getHeight();
start -= mTitleBar.getHeight();
}
/**
* 特殊处理:点击按钮透明区域时将事件分发给底层列表视图
* 透明区域由公式 y=-0.12x+94 定义(单位:像素)
* 这是一个UI设计的特殊需求
*/
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(); // 保存原始Y坐标
mDispatchY = eventY; // 设置分发Y坐标
event.setLocation(event.getX(), mDispatchY); // 修改事件位置
mDispatch = true; // 标记为分发状态
return mNotesListView.dispatchTouchEvent(event); // 分发给列表视图
}
}
break;
}
case MotionEvent.ACTION_MOVE: { // 移动事件
if (mDispatch) {
mDispatchY += (int) event.getY() - mOriginY; // 更新分发Y坐标
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; // 不处理事件
}
};
// 开始异步查询便签列表
private void startAsyncNotesListQuery() {
String selection = (mCurrentFolderId == Notes.ID_ROOT_FOLDER) ? ROOT_FOLDER_SELECTION
: NORMAL_SELECTION; // 根据文件夹ID选择查询条件
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) {
if (folderId == Notes.ID_ROOT_FOLDER) {
Log.e(TAG, "Wrong folder id, should not happen " + folderId);
return;
}
HashSet<Long> ids = new HashSet<Long>();
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(); // 查询子文件夹内容
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();
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();
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) {
}
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) {
}
});
}
// 返回键处理
@Override
public void onBackPressed() {
switch (mState) {
case SUB_FOLDER: // 子文件夹状态:返回到根目录
mCurrentFolderId = Notes.ID_ROOT_FOLDER;
mState = ListEditState.NOTE_LIST;
startAsyncNotesListQuery(); // 重新查询根目录
mTitleBar.setVisibility(View.GONE); // 隐藏标题栏
break;
case CALL_RECORD_FOLDER: // 通话记录文件夹:返回到根目录
mCurrentFolderId = Notes.ID_ROOT_FOLDER;
mState = ListEditState.NOTE_LIST;
mAddNewNote.setVisibility(View.VISIBLE); // 显示添加按钮
mTitleBar.setVisibility(View.GONE); // 隐藏标题栏
startAsyncNotesListQuery(); // 重新查询根目录
break;
case NOTE_LIST: // 根目录状态:退出应用
super.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;
}
intent.putExtra(AppWidgetManager.EXTRA_APPWIDGET_IDS, new int[] {
appWidgetId // 小部件ID
});
sendBroadcast(intent); // 发送广播更新小部件
setResult(RESULT_OK, intent); // 设置结果
}
// 文件夹上下文菜单创建监听器
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);
}
// 上下文菜单项选择处理
@Override
public boolean onContextItemSelected(MenuItem item) {
if (mFocusNoteDataItem == null) {
Log.e(TAG, "The long click data item is null");
return false;
}
switch (item.getItemId()) {
case MENU_FOLDER_VIEW: // 查看文件夹
openFolder(mFocusNoteDataItem);
break;
case MENU_FOLDER_DELETE: // 删除文件夹
AlertDialog.Builder builder = new AlertDialog.Builder(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;
}
// 准备选项菜单(根据状态加载不同的菜单)
@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;
}
// 选项菜单项选择处理
@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;
}
// 搜索请求
@Override
public boolean onSearchRequested() {
startSearch(null, false, null /* appData */, false); // 启动搜索
return 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;
}
}