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

954 lines
38 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.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.ui.NotesListItem.NoteItemData;
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;
/**
* 笔记列表活动 - 应用的主界面
*
* 【功能说明】
* 1. 显示所有笔记和文件夹的列表视图
* 2. 支持创建、编辑、删除笔记
* 3. 支持创建和管理文件夹(重命名/删除)
* 4. 支持多选批量操作(删除/移动)
* 5. 支持长按上下文菜单操作
* 6. 支持搜索和导出文本功能
* 7. 支持Google任务同步
* 8. 首次启动时显示使用说明便签
* 9. 支持通话记录便签显示
* 10. 支持桌面小部件更新
*
* 【界面组成】
* - ListView: 显示笔记和文件夹列表
* - 底部按钮: 新建笔记
* - 标题栏: 显示当前文件夹名称
* - 多选模式: ActionMode + 下拉菜单
*
* @author MiCode Open Source Community
* @modified 2026-05-07 (Complete Chinese Annotation)
*/
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;
// SharedPreferences键名是否已添加介绍便签
private static final String PREFERENCE_ADD_INTRODUCTION = "net.micode.notes.introduction";
/**
* 列表编辑状态枚举
* NOTE_LIST: 根目录笔记列表
* SUB_FOLDER: 用户自定义文件夹内
* CALL_RECORD_FOLDER: 通话记录文件夹
*/
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;
private int mDispatchY;
private TextView mTitleBar;
private long mCurrentFolderId;
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)";
// Activity请求码
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);
}
}
/**
* 首次启动时从raw资源加载使用说明并创建介绍便签
* 仅执行一次通过SharedPreferences标记
*/
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();
}
/**
* 初始化UI组件和资源
* 包括ListView、适配器、按钮等
*/
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;
mOriginY = 0;
mTitleBar = (TextView) findViewById(R.id.tv_title_bar);
mState = ListEditState.NOTE_LIST;
mModeCallBack = new ModeCallback();
}
/**
* 多选模式回调类
* 实现ListView.MultiChoiceModeListener接口
* 处理批量选择和操作相关的逻辑
*/
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 != null && 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(ListView.CHOICE_MODE_MULTIPLE);
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();
mDropDownMenu.setTitle(String.valueOf(selectedCount));
if (selectedCount == 0) {
mMoveMenu.setVisible(false);
} else {
mMoveMenu.setVisible(true);
}
}
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(ListView.CHOICE_MODE_NONE);
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 (item.getItemId() == 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_note));
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();
} else if (item.getItemId() == R.id.move) {
startQueryDestinationFolders();
}
return false;
}
}
/**
* 开始查询目标文件夹列表(用于移动笔记)
* 根据当前状态构建不同的查询条件
*/
private void startQueryDestinationFolders() {
String selection = NoteColumns.TYPE + "=? AND " + NoteColumns.ID + "<>? AND " + NoteColumns.ID + "<>?";
selection = (mState == ListEditState.NOTE_LIST) ? selection :
"(" + selection + ") OR (" + NoteColumns.ID + "=?)";
String[] selectionArgs = (mState == ListEditState.NOTE_LIST) ?
new String[] { String.valueOf(Notes.TYPE_FOLDER), String.valueOf(Notes.ID_ROOT_FOLDER),
String.valueOf(Notes.ID_TEMPARAY_FOLDER) } :
new String[] { String.valueOf(Notes.TYPE_FOLDER), String.valueOf(Notes.ID_ROOT_FOLDER),
String.valueOf(Notes.ID_TEMPARAY_FOLDER), String.valueOf(mCurrentFolderId) };
mBackgroundQueryHandler.startQuery(FOLDER_LIST_QUERY_TOKEN, null,
Notes.CONTENT_NOTE_URI, FoldersListAdapter.PROJECTION, selection, selectionArgs, null);
}
/**
* 后台查询处理器
* 异步处理数据库查询操作避免阻塞UI线程
*/
private 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) {
showFolderListDialog(cursor);
} else {
Log.e(TAG, "Query folder list failed");
}
cursor.close();
break;
default:
break;
}
}
}
/**
* 显示文件夹列表对话框(用于选择移动目标)
* @param cursor 文件夹数据游标
*/
private void showFolderListDialog(Cursor cursor) {
final Cursor foldersCursor = cursor;
final AlertDialog.Builder builder = new AlertDialog.Builder(this);
View view = LayoutInflater.from(this).inflate(R.layout.folder_list_item, null);
ListView foldersList = (ListView) view.findViewById(R.id.folders_list);
FoldersListAdapter adapter = new FoldersListAdapter(this, foldersCursor);
foldersList.setAdapter(adapter);
foldersList.setOnItemClickListener(new OnItemClickListener() {
@Override
public void onItemClick(AdapterView<?> parent, View view, int position, long id) {
if (mFocusNoteDataItem != null) {
ContentValues values = new ContentValues();
values.put(NoteColumns.PARENT_ID, id);
values.put(NoteColumns.LOCAL_MODIFIED, 1);
mContentResolver.update(Notes.CONTENT_NOTE_URI, values, NoteColumns.ID + "=?",
new String[] { String.valueOf(mFocusNoteDataItem.getId()) });
}
mModeCallBack.finishActionMode();
}
});
builder.setTitle("Move to folder");
builder.setView(view);
builder.setNegativeButton(android.R.string.cancel, null);
builder.show();
}
/**
* 开始异步查询笔记列表
* 根据当前文件夹ID和状态构建不同的查询条件
*/
private void startAsyncNotesListQuery() {
String selection = (mCurrentFolderId == Notes.ID_ROOT_FOLDER) ? ROOT_FOLDER_SELECTION : NORMAL_SELECTION;
String[] selectionArgs = (mCurrentFolderId == Notes.ID_ROOT_FOLDER) ?
new String[] { String.valueOf(Notes.ID_ROOT_FOLDER) } :
new String[] { String.valueOf(mCurrentFolderId) };
mBackgroundQueryHandler.startQuery(FOLDER_NOTE_LIST_QUERY_TOKEN, null,
Notes.CONTENT_NOTE_URI, NotesListAdapter.PROJECTION, selection, selectionArgs,
NoteColumns.TYPE + " DESC," + NoteColumns.MODIFIED_DATE + " DESC");
}
/**
* 列表项点击监听器
* 处理笔记和文件夹的点击事件
*/
private final class OnListItemClickListener implements OnItemClickListener {
public void onItemClick(AdapterView<?> parent, View view, int position, long id) {
// 忽略底部按钮的点击
if (position >= mNotesListAdapter.getCount()) {
return;
}
NoteItemData item = (NoteItemData) mNotesListAdapter.getItem(position);
if (item == null) return;
Log.d(TAG, "Item clicked: type=" + item.getType() + ", id=" + item.getId() + ", snippet=" + item.getSnippet());
if (item.getType() == Notes.TYPE_NOTE) {
openNode(item);
} else if (item.getType() == Notes.TYPE_FOLDER) {
Log.d(TAG, "Opening folder: " + item.getSnippet());
openFolder(item);
}
}
}
/**
* 打开笔记编辑页面
* @param data 笔记项数据
*/
private void openNode(NoteItemData data) {
Intent intent = new Intent(this, NoteEditActivity.class);
intent.setAction(Intent.ACTION_VIEW);
intent.putExtra(Intent.EXTRA_UID, data.getId());
this.startActivityForResult(intent, REQUEST_CODE_OPEN_NODE);
}
/**
* 进入文件夹
* @param data 文件夹数据
*/
private void openFolder(NoteItemData data) {
mCurrentFolderId = data.getId();
Log.d(TAG, "Entered folder ID: " + mCurrentFolderId);
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);
}
@Override
public boolean onItemLongClick(AdapterView<?> parent, View view, int position, long id) {
if (position >= mNotesListAdapter.getCount()) {
mNotesListView.setSelection(position);
return false;
}
mFocusNoteDataItem = (NoteItemData) mNotesListAdapter.getItem(position);
if (mFocusNoteDataItem == null) {
Log.e(TAG, "Long click failed: mFocusNoteDataItem is null");
return false;
}
// 长按普通笔记:直接删除
if (mFocusNoteDataItem.getType() == Notes.TYPE_NOTE) {
AlertDialog.Builder builder = new AlertDialog.Builder(this);
builder.setTitle(getString(R.string.alert_title_delete));
builder.setMessage(getString(R.string.alert_message_delete_note));
builder.setPositiveButton(android.R.string.ok,
new DialogInterface.OnClickListener() {
public void onClick(DialogInterface dialog, int which) {
HashSet<Long> ids = new HashSet<>();
ids.add(mFocusNoteDataItem.getId());
DataUtils.batchDeleteNotes(mContentResolver, ids);
mNotesListAdapter.changeCursor(null);
startAsyncNotesListQuery();
}
});
builder.setNegativeButton(android.R.string.cancel, null);
builder.show();
return true;
}
// 长按文件夹:弹出菜单(重命名/删除)
if (mFocusNoteDataItem.getType() == Notes.TYPE_FOLDER) {
showCreateOrModifyFolderDialog(false);
return true;
}
return false;
}
public void onClick(View v) {
if (v.getId() == R.id.btn_new_note) {
createNewNote();
}
}
/**
* 创建新笔记
* 传递当前文件夹ID
*/
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);
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();
}
/**
* 删除文件夹及其所有内容
* @param folderId 文件夹ID
*/
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<>();
ids.add(folderId);
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 showSoftInput() {
InputMethodManager inputMethodManager = (InputMethodManager) getSystemService(Context.INPUT_METHOD_SERVICE);
if (inputMethodManager != null) {
inputMethodManager.toggleSoftInput(InputMethodManager.SHOW_FORCED, 0);
}
}
/**
* 隐藏软键盘
* @param view 获取窗口令牌的视图
*/
private void hideSoftInput(View view) {
InputMethodManager inputMethodManager = (InputMethodManager) getSystemService(Context.INPUT_METHOD_SERVICE);
inputMethodManager.hideSoftInputFromWindow(view.getWindowToken(), 0);
}
/**
* 显示创建或修改文件夹的对话框
* @param create true表示创建新文件夹false表示修改现有文件夹
*/
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() {
// 在子文件夹或通话记录文件夹中按返回键,返回根目录
if (mState == ListEditState.SUB_FOLDER) {
mCurrentFolderId = Notes.ID_ROOT_FOLDER;
mState = ListEditState.NOTE_LIST;
startAsyncNotesListQuery();
mTitleBar.setVisibility(View.GONE);
mAddNewNote.setVisibility(View.VISIBLE);
} else if (mState == ListEditState.CALL_RECORD_FOLDER) {
mCurrentFolderId = Notes.ID_ROOT_FOLDER;
mState = ListEditState.NOTE_LIST;
startAsyncNotesListQuery();
mTitleBar.setVisibility(View.GONE);
mAddNewNote.setVisibility(View.VISIBLE);
} else {
super.onBackPressed();
}
}
/**
* 更新桌面小部件
* @param widgetId 小部件ID
* @param widgetType 小部件类型
*/
private void updateWidget(int widgetId, int widgetType) {
Intent intent = new Intent(AppWidgetManager.ACTION_APPWIDGET_UPDATE);
if (widgetType == Notes.TYPE_WIDGET_2X) {
intent.setClass(this, NoteWidgetProvider_2x.class);
} else if (widgetType == 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[] { widgetId });
sendBroadcast(intent);
setResult(RESULT_OK, intent);
}
/**
* 文件夹上下文菜单创建监听器
*/
private final OnCreateContextMenuListener mFolderOnCreateContextMenuListener = new OnCreateContextMenuListener() {
@Override
public void onCreateContextMenu(ContextMenu menu, View v, ContextMenuInfo menuInfo) {
menu.setHeaderTitle(R.string.menu_folder_option);
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 onCreateContextMenu(ContextMenu menu, View v, ContextMenuInfo menuInfo) {
if (mState == ListEditState.NOTE_LIST) {
AdapterView.AdapterContextMenuInfo info = (AdapterView.AdapterContextMenuInfo) menuInfo;
if (info.position >= mNotesListAdapter.getCount()) {
return;
}
NoteItemData item = (NoteItemData) mNotesListAdapter.getItem(info.position);
// 只为文件夹显示上下文菜单
if (item.getType() == Notes.TYPE_FOLDER) {
menu.setHeaderTitle(R.string.menu_folder_option);
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 boolean onContextItemSelected(MenuItem item) {
if (item.getItemId() == 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();
} else if (item.getItemId() == MENU_FOLDER_CHANGE_NAME) {
showCreateOrModifyFolderDialog(false);
}
return true;
}
@Override
public boolean onCreateOptionsMenu(Menu menu) {
getMenuInflater().inflate(R.menu.note_list, menu);
return true;
}
@Override
public boolean onOptionsItemSelected(MenuItem item) {
if (item.getItemId() == R.id.menu_new_folder) {
showCreateOrModifyFolderDialog(true);
} else if (item.getItemId() == R.id.menu_export_text) {
exportNoteToText();
} else if (item.getItemId() == R.id.menu_sync) {
if (GTaskSyncService.isSyncing()) {
GTaskSyncService.cancelSync(this);
} else {
GTaskSyncService.startSync(this);
}
} else if (item.getItemId() == R.id.menu_setting) {
Intent intent = new Intent(this, NotesPreferenceActivity.class);
startActivity(intent);
} else if (item.getItemId() == R.id.menu_search) {
onSearchRequested();
}
return true;
}
/**
* 导出笔记为文本文件
* 异步执行导出操作,并根据结果显示相应的提示
*/
private void exportNoteToText() {
final BackupUtils backup = BackupUtils.getInstance(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) {
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.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.success_sdcard_exported, backup.getExportedTextFile()));
builder.setPositiveButton(android.R.string.ok, null);
builder.show();
} else if (result == BackupUtils.EXPORT_FILE_UNEXIST) {
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();
}
/**
* 判断当前是否为同步模式
* @return true表示已设置同步账号
*/
private boolean isSyncMode() {
return NotesPreferenceActivity.isSyncMode(this);
}
/**
* 新建笔记按钮触摸监听器
* 处理滑动事件,防止误触
*/
private class NewNoteOnTouchListener implements OnTouchListener {
@Override
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;
int eventY = start + (int) event.getY();
eventY -= 38;
if (mDispatchY == 0) {
mDispatchY = (int) event.getY();
mOriginY = mDispatchY;
}
if (eventY > mOriginY + 50) {
mDispatch = true;
mOriginY = mDispatchY;
}
break;
case MotionEvent.ACTION_MOVE:
if (mDispatch) {
mDispatchY = (int) event.getY();
}
break;
case MotionEvent.ACTION_UP:
if (mDispatch) {
mDispatch = false;
if (mDispatchY - mOriginY > 50) {
return true;
}
}
mDispatchY = 0;
mOriginY = 0;
break;
default:
break;
}
return false;
}
}
}