diff --git a/src/ui/NotesListActivity.java b/src/ui/NotesListActivity.java index 42bac1d..969594f 100644 --- a/src/ui/NotesListActivity.java +++ b/src/ui/NotesListActivity.java @@ -54,6 +54,7 @@ import android.widget.AdapterView; import android.widget.AdapterView.OnItemClickListener; import android.widget.AdapterView.OnItemLongClickListener; import android.widget.Button; +import android.widget.ImageButton; import android.widget.EditText; import android.widget.ListView; import android.widget.PopupMenu; @@ -78,72 +79,73 @@ import java.io.InputStream; import java.io.InputStreamReader; import java.util.HashSet; -/** - * 小米笔记的主界面类,负责管理笔记列表、文件夹、搜索和批量操作 - * 实现了笔记的创建、编辑、删除、移动等核心功能 - */ public class NotesListActivity extends Activity implements OnClickListener, OnItemLongClickListener { - /** - * 异步查询的Token常量,用于区分不同类型的查询请求 - */ - 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; // 重命名文件夹 - - /** - * SharedPreferences键名,用于标记是否显示过添加笔记的介绍 - */ + 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 // 通话记录文件夹列表 + NOTE_LIST, SUB_FOLDER, CALL_RECORD_FOLDER, TRASH_FOLDER }; - private ListEditState mState; // 当前列表状态 - private BackgroundQueryHandler mBackgroundQueryHandler; // 异步查询处理器,用于后台加载数据 - - private NotesListAdapter mNotesListAdapter; // 笔记列表适配器,负责数据与UI的绑定 - private ListView mNotesListView; // 显示笔记列表的ListView组件 - 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; // ListView滚动速率 - private NoteItemData mFocusNoteDataItem; // 当前聚焦的笔记数据项 - - /** - * 数据库查询条件 - */ - private static final String NORMAL_SELECTION = NoteColumns.PARENT_ID + "=?"; // 普通文件夹查询条件 + private ListEditState mState; + + private BackgroundQueryHandler mBackgroundQueryHandler; + + private NotesListAdapter mNotesListAdapter; + + private ListView mNotesListView; + + private Button mAddNewNote; + private Button mMemoryBottle; + private ImageButton mTrashButton; + private MemoryBottleDialog mMemoryBottleDialog; + + private boolean mDispatch; + + private int mOriginY; + + private int mDispatchY; + + private TextView mTitleBar; + + private long mCurrentFolderId; + + private ContentResolver mContentResolver; + + private ModeCallback mModeCallBack; + private MenuItem mRestoreMenu; + private TrashManager mTrashManager; + private EncryptedFolderManager mEncryptedFolderManager; + + 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)"; // 根文件夹查询条件 + + NoteColumns.NOTES_COUNT + ">0)"; + + private static final String PREF_MEMORY_FOLDER_ID = "pref_memory_bottle_folder_id"; - /** - * startActivityForResult的请求码 - */ - private final static int REQUEST_CODE_OPEN_NODE = 102; // 打开笔记的请求码 - private final static int REQUEST_CODE_NEW_NODE = 103; // 新建笔记的请求码 + private final static int REQUEST_CODE_OPEN_NODE = 102; + private final static int REQUEST_CODE_NEW_NODE = 103; @Override - protected void onCreate(Bundle savedInstanceState) {// 初始化活动,设置布局和资源 + protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.note_list); initResources(); @@ -155,7 +157,7 @@ public class NotesListActivity extends Activity implements OnClickListener, OnIt } @Override - protected void onActivityResult(int requestCode, int resultCode, Intent data) {// 处理子活动返回结果 + 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); @@ -164,23 +166,20 @@ public class NotesListActivity extends Activity implements OnClickListener, OnIt } } - /** - * 首次使用应用时插入介绍笔记 - */ private void setAppInfoFromRawRes() { - SharedPreferences sp = PreferenceManager.getDefaultSharedPreferences(this);// 亮点:SharedPreferences 是Android平台上一个轻量级的存储类,主要用于保存应用的一些常用配置。它通过键值对的形式将数据保存在XML文件中 - if (!sp.getBoolean(PREFERENCE_ADD_INTRODUCTION, false)) {// 检查是否已添加介绍笔记 + 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); + in = getResources().openRawResource(R.raw.introduction); if (in != null) { - InputStreamReader isr = new InputStreamReader(in, "UTF-8");// 亮点:指定字符编码为UTF-8,确保能够正确读取包含特殊字符的文本文件 - BufferedReader br = new BufferedReader(isr);// 亮点:BufferedReader 是Java IO库中的一个字符流类,用于读取字符数据。它提供了缓冲区的功能,能够一次读取多个字符,提高了读取效率。 + 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);//将读取到的字符数组buf中的0到len-1位置的字符添加到StringBuilder中 + sb.append(buf, 0, len); } } else { Log.e(TAG, "Read introduction file error"); @@ -213,18 +212,12 @@ public class NotesListActivity extends Activity implements OnClickListener, OnIt } } - /** - * 活动启动时执行,开始异步查询笔记列表 - */ @Override protected void onStart() { super.onStart(); startAsyncNotesListQuery(); } - /** - * 初始化资源和UI组件 - */ private void initResources() { mContentResolver = this.getContentResolver(); mBackgroundQueryHandler = new BackgroundQueryHandler(this.getContentResolver()); @@ -239,46 +232,98 @@ public class NotesListActivity extends Activity implements OnClickListener, OnIt mAddNewNote = (Button) findViewById(R.id.btn_new_note); mAddNewNote.setOnClickListener(this); mAddNewNote.setOnTouchListener(new NewNoteOnTouchListener()); + mMemoryBottle = (Button) findViewById(R.id.btn_memory_bottle); + mMemoryBottle.setOnClickListener(this); + mTrashButton = (ImageButton) findViewById(R.id.btn_trash); + mTrashButton.setOnClickListener(this); mDispatch = false; mDispatchY = 0; mOriginY = 0; mTitleBar = (TextView) findViewById(R.id.tv_title_bar); mState = ListEditState.NOTE_LIST; mModeCallBack = new ModeCallback(); + mTrashManager = new TrashManager(this, mContentResolver, new TrashManager.Callback() { + @Override + public void onWidgetsNeedUpdate(HashSet 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); + } + } + } + } + + @Override + public void onListChanged() { + startAsyncNotesListQuery(); + } + + @Override + public void onActionModeFinished() { + mModeCallBack.finishActionMode(); + } + + @Override + public void onRestoreInvalid() { + Toast.makeText(NotesListActivity.this, R.string.trash_restore_invalid, + Toast.LENGTH_SHORT).show(); + } + }); + mEncryptedFolderManager = new EncryptedFolderManager(this, mContentResolver, + new EncryptedFolderManager.Callback() { + @Override + public void onEncryptedFolderCreated() { + startAsyncNotesListQuery(); + } + + @Override + public void onEncryptedFolderUnlocked(NoteItemData data) { + openFolderInternal(data); + } + }); + updateMemoryButtonVisibility(); + updateTrashButtonVisibility(); } - /** - * 多选模式回调类,处理批量操作和菜单项点击事件 - */ private class ModeCallback implements ListView.MultiChoiceModeListener, OnMenuItemClickListener { - /** 下拉菜单,用于显示选择操作选项 */ private DropdownMenu mDropDownMenu; - /** ActionMode实例,用于管理多选模式的生命周期 */ private ActionMode mActionMode; - /** 移动菜单项,用于批量移动笔记 */ private MenuItem mMoveMenu; - /** - * 创建多选模式时的初始化操作 - * @param mode ActionMode实例 - * @param menu 菜单对象 - * @return 是否成功创建多选模式 - */ public boolean onCreateActionMode(ActionMode mode, Menu menu) { getMenuInflater().inflate(R.menu.note_list_options, menu); - menu.findItem(R.id.delete).setOnMenuItemClickListener(this); + MenuItem deleteMenu = menu.findItem(R.id.delete); + deleteMenu.setOnMenuItemClickListener(this); mMoveMenu = menu.findItem(R.id.move); - if (mFocusNoteDataItem.getParentId() == Notes.ID_CALL_RECORD_FOLDER - || DataUtils.getUserFolderCount(mContentResolver) == 0) { + mRestoreMenu = menu.findItem(R.id.restore); + if (mState == ListEditState.TRASH_FOLDER) { mMoveMenu.setVisible(false); + mRestoreMenu.setVisible(true); + mRestoreMenu.setOnMenuItemClickListener(this); + deleteMenu.setTitle(R.string.menu_delete_permanent); } else { - mMoveMenu.setVisible(true); - mMoveMenu.setOnMenuItemClickListener(this); + mRestoreMenu.setVisible(false); + if (mFocusNoteDataItem.getParentId() == Notes.ID_CALL_RECORD_FOLDER + || DataUtils.getUserFolderCount(mContentResolver) == 0) { + mMoveMenu.setVisible(false); + } else { + mMoveMenu.setVisible(true); + mMoveMenu.setOnMenuItemClickListener(this); + } + deleteMenu.setTitle(R.string.menu_delete); } mActionMode = mode; - mNotesListAdapter.setChoiceMode(true); + mNotesListAdapter.setChoiceMode(true, mState == ListEditState.TRASH_FOLDER); mNotesListView.setLongClickable(false); mAddNewNote.setVisibility(View.GONE); + if (mMemoryBottle != null) { + mMemoryBottle.setVisibility(View.GONE); + } + if (mTrashButton != null) { + mTrashButton.setVisibility(View.GONE); + } View customView = LayoutInflater.from(NotesListActivity.this).inflate( R.layout.note_list_dropdown_menu, null); @@ -297,12 +342,12 @@ public class NotesListActivity extends Activity implements OnClickListener, OnIt return true; } - private void updateMenu() {// 更新下拉菜单显示的选中项数量 + private void updateMenu() { int selectedCount = mNotesListAdapter.getSelectedCount(); // Update dropdown menu String format = getResources().getString(R.string.menu_select_title, selectedCount); mDropDownMenu.setTitle(format); - MenuItem item = mDropDownMenu.findItem(R.id.action_select_all);// 查找选择所有项的菜单项 + MenuItem item = mDropDownMenu.findItem(R.id.action_select_all); if (item != null) { if (mNotesListAdapter.isAllSelected()) { item.setChecked(true); @@ -314,33 +359,39 @@ public class NotesListActivity extends Activity implements OnClickListener, OnIt } } - public boolean onPrepareActionMode(ActionMode mode, Menu menu) {// 准备多选模式时的操作 + public boolean onPrepareActionMode(ActionMode mode, Menu menu) { // TODO Auto-generated method stub return false; } - public boolean onActionItemClicked(ActionMode mode, MenuItem item) {// 处理下拉菜单项点击事件 + public boolean onActionItemClicked(ActionMode mode, MenuItem item) { // TODO Auto-generated method stub return false; } - public void onDestroyActionMode(ActionMode mode) {// 多选模式结束时的操作 + public void onDestroyActionMode(ActionMode mode) { mNotesListAdapter.setChoiceMode(false); mNotesListView.setLongClickable(true); - mAddNewNote.setVisibility(View.VISIBLE); + if (mState == ListEditState.CALL_RECORD_FOLDER) { + mAddNewNote.setVisibility(View.GONE); + } else { + mAddNewNote.setVisibility(View.VISIBLE); + } + updateMemoryButtonVisibility(); + updateTrashButtonVisibility(); } - public void finishActionMode() {// 结束多选模式 + public void finishActionMode() { mActionMode.finish(); } public void onItemCheckedStateChanged(ActionMode mode, int position, long id, - boolean checked) {// 处理列表项选中状态改变事件,注意actionmode的生命周期 + boolean checked) { mNotesListAdapter.setCheckedItem(position, checked); updateMenu(); } - public boolean onMenuItemClick(MenuItem item) {// 处理下拉菜单项点击事件 + public boolean onMenuItemClick(MenuItem item) { if (mNotesListAdapter.getSelectedCount() == 0) { Toast.makeText(NotesListActivity.this, getString(R.string.menu_select_none), Toast.LENGTH_SHORT).show(); @@ -352,21 +403,29 @@ public class NotesListActivity extends Activity implements OnClickListener, OnIt 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())); + if (mState == ListEditState.TRASH_FOLDER) { + builder.setMessage(getString(R.string.alert_message_delete_notes, + mNotesListAdapter.getSelectedCount())); + } else { + 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(); - } - }); + 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; + case R.id.restore: + restoreSelected(); + break; default: return false; } @@ -374,7 +433,8 @@ public class NotesListActivity extends Activity implements OnClickListener, OnIt } } - private class NewNoteOnTouchListener implements OnTouchListener {// 处理"新建笔记"按钮的触摸事件 + private class NewNoteOnTouchListener implements OnTouchListener { + public boolean onTouch(View v, MotionEvent event) { switch (event.getAction()) { case MotionEvent.ACTION_DOWN: { @@ -386,7 +446,7 @@ public class NotesListActivity extends Activity implements OnClickListener, OnIt /** * Minus TitleBar's height */ - if (mState == ListEditState.SUB_FOLDER) {// 如果当前状态是子文件夹 + if (mState == ListEditState.SUB_FOLDER) { eventY -= mTitleBar.getHeight(); start -= mTitleBar.getHeight(); } @@ -399,11 +459,11 @@ public class NotesListActivity extends Activity implements OnClickListener, OnIt * Notice that, if the background of the button changes, the formula should * also change. This is very bad, just for the UI designer's strong requirement. */ - if (event.getY() < (event.getX() * (-0.12) + 94)) {// 如果点击位置在"新建笔记"按钮的透明部分 + if (event.getY() < (event.getX() * (-0.12) + 94)) { View view = mNotesListView.getChildAt(mNotesListView.getChildCount() - 1 - - mNotesListView.getFooterViewsCount());// 获取列表项的底部视图 + - mNotesListView.getFooterViewsCount()); if (view != null && view.getBottom() > start - && (view.getTop() < (start + 94))) {// 如果点击位置在"新建笔记"按钮的透明部分且在列表项范围内 + && (view.getTop() < (start + 94))) { mOriginY = (int) event.getY(); mDispatchY = eventY; event.setLocation(event.getX(), mDispatchY); @@ -413,7 +473,7 @@ public class NotesListActivity extends Activity implements OnClickListener, OnIt } break; } - case MotionEvent.ACTION_MOVE: {// 处理"新建笔记"按钮的移动事件 + case MotionEvent.ACTION_MOVE: { if (mDispatch) { mDispatchY += (int) event.getY() - mOriginY; event.setLocation(event.getX(), mDispatchY); @@ -421,7 +481,7 @@ public class NotesListActivity extends Activity implements OnClickListener, OnIt } break; } - default: {// 处理"新建笔记"按钮的其他事件 + default: { if (mDispatch) { event.setLocation(event.getX(), mDispatchY); mDispatch = false; @@ -435,44 +495,41 @@ public class NotesListActivity extends Activity implements OnClickListener, OnIt }; - /** - * 启动异步查询笔记列表的方法 - * 根据当前文件夹ID构建查询条件,使用异步查询处理器加载数据 - */ private void startAsyncNotesListQuery() { String selection = (mCurrentFolderId == Notes.ID_ROOT_FOLDER) ? ROOT_FOLDER_SELECTION : NORMAL_SELECTION; + String[] selectionArgs; + if (mCurrentFolderId == Notes.ID_ROOT_FOLDER) { + long memoryFolderId = getMemoryBottleFolderId(); + if (memoryFolderId > 0) { + selection = "(" + selection + ") AND " + NoteColumns.ID + "<> ?"; + selectionArgs = new String[] { + String.valueOf(mCurrentFolderId), + String.valueOf(memoryFolderId) + }; + } else { + selectionArgs = new String[] { String.valueOf(mCurrentFolderId) }; + } + } else { + selectionArgs = new String[] { String.valueOf(mCurrentFolderId) }; + } mBackgroundQueryHandler.startQuery(FOLDER_NOTE_LIST_QUERY_TOKEN, null, - Notes.CONTENT_NOTE_URI, NoteItemData.PROJECTION, selection, new String[] { - String.valueOf(mCurrentFolderId) - }, NoteColumns.TYPE + " DESC," + NoteColumns.MODIFIED_DATE + " DESC" );// 按类型和修改日期排序 + Notes.CONTENT_NOTE_URI, NoteItemData.PROJECTION, selection, selectionArgs, + NoteColumns.TYPE + " DESC," + NoteColumns.MODIFIED_DATE + " DESC"); } - /** - * 异步查询处理器,用于在后台执行数据库查询操作 - */ private final class BackgroundQueryHandler extends AsyncQueryHandler { - /** - * 构造函数 - * @param contentResolver 内容解析器,用于访问数据库 - */ public BackgroundQueryHandler(ContentResolver contentResolver) { super(contentResolver); } - /** - * 查询完成后的回调处理 - * @param token 查询标识,区分不同类型的查询 - * @param cookie 额外数据 - * @param cursor 查询结果游标 - */ @Override protected void onQueryComplete(int token, Object cookie, Cursor cursor) { switch (token) { - case FOLDER_NOTE_LIST_QUERY_TOKEN:// 查询笔记列表 + case FOLDER_NOTE_LIST_QUERY_TOKEN: mNotesListAdapter.changeCursor(cursor); break; - case FOLDER_LIST_QUERY_TOKEN:// 查询文件夹列表 + case FOLDER_LIST_QUERY_TOKEN: if (cursor != null && cursor.getCount() > 0) { showFolderListMenu(cursor); } else { @@ -485,7 +542,7 @@ public class NotesListActivity extends Activity implements OnClickListener, OnIt } } - private void showFolderListMenu(Cursor cursor) {// 显示文件夹列表菜单 + 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); @@ -506,67 +563,33 @@ public class NotesListActivity extends Activity implements OnClickListener, OnIt builder.show(); } - private void createNewNote() {// 创建新笔记 + 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>() { - protected HashSet doInBackground(Void... unused) { - HashSet widgets = mNotesListAdapter.getSelectedWidget(); - if (!isSyncMode()) { - // if not synced, delete notes directly - if (DataUtils.batchDeleteNotes(mContentResolver, mNotesListAdapter - .getSelectedItemIds())) { - } else { - Log.e(TAG, "Delete notes error, should not happens"); - } - } else { - // in sync mode, we'll move the deleted note into the trash - // folder - if (!DataUtils.batchMoveToFolder(mContentResolver, mNotesListAdapter - .getSelectedItemIds(), Notes.ID_TRASH_FOLER)) { - Log.e(TAG, "Move notes to trash folder error, should not happens"); - } - } - return widgets; - } + private void batchDelete() { + HashSet widgets = mNotesListAdapter.getSelectedWidget(); + HashSet ids = mNotesListAdapter.getSelectedItemIds(); + boolean inTrash = mState == ListEditState.TRASH_FOLDER; + mTrashManager.batchDelete(inTrash, ids, widgets, mCurrentFolderId); + } - @Override - protected void onPostExecute(HashSet 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 restoreSelected() { + HashSet ids = mNotesListAdapter.getSelectedItemIds(); + HashSet widgets = mNotesListAdapter.getSelectedWidget(); + mTrashManager.restoreSelected(ids, widgets); } - private void deleteFolder(long folderId) {// 删除文件夹 + private void deleteFolder(long folderId) { if (folderId == Notes.ID_ROOT_FOLDER) { Log.e(TAG, "Wrong folder id, should not happen " + folderId); return; } - HashSet ids = new HashSet(); - ids.add(folderId); - HashSet widgets = DataUtils.getFolderNoteWidget(mContentResolver, - folderId); - if (!isSyncMode()) { - // if not synced, delete folder directly - DataUtils.batchDeleteNotes(mContentResolver, ids); - } else { - // in sync mode, we'll move the deleted folder into the trash folder - DataUtils.batchMoveToFolder(mContentResolver, ids, Notes.ID_TRASH_FOLER); - } + HashSet widgets = mTrashManager.moveFolderToTrash(folderId, mCurrentFolderId); if (widgets != null) { for (AppWidgetAttribute widget : widgets) { if (widget.widgetId != AppWidgetManager.INVALID_APPWIDGET_ID @@ -577,14 +600,75 @@ public class NotesListActivity extends Activity implements OnClickListener, OnIt } } - private void openNode(NoteItemData 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); } - private void openFolder(NoteItemData data) {// 打开文件夹 + private void openFolder(NoteItemData data) { + if (data.getId() == Notes.ID_TRASH_FOLER) { + openTrashFolder(); + return; + } + EncryptedFolderManager.EncryptedFolderInfo encryptedInfo = + mEncryptedFolderManager.getEncryptedFolderInfo(data.getId()); + if (encryptedInfo != null) { + mEncryptedFolderManager.showEncryptedUnlockDialog(encryptedInfo, data); + return; + } + openFolderInternal(data); + } + + public void onClick(View v) { + switch (v.getId()) { + case R.id.btn_new_note: + createNewNote(); + break; + case R.id.btn_memory_bottle: + openMemoryBottle(); + break; + case R.id.btn_trash: + openTrashFolder(); + break; + default: + break; + } + } + + private void updateMemoryButtonVisibility() { + if (mMemoryBottle == null) { + return; + } + if (mState == ListEditState.NOTE_LIST) { + mMemoryBottle.setVisibility(View.VISIBLE); + } else { + mMemoryBottle.setVisibility(View.GONE); + } + } + + private void updateTrashButtonVisibility() { + if (mTrashButton == null) { + return; + } + if (mState == ListEditState.NOTE_LIST) { + mTrashButton.setVisibility(View.VISIBLE); + } else { + mTrashButton.setVisibility(View.GONE); + } + } + + private long getMemoryBottleFolderId() { + SharedPreferences sp = PreferenceManager.getDefaultSharedPreferences(this); + long folderId = sp.getLong(PREF_MEMORY_FOLDER_ID, -1); + if (folderId > 0 && DataUtils.visibleInNoteDatabase(mContentResolver, folderId, + Notes.TYPE_FOLDER)) { + return folderId; + } + return -1; + } + private void openFolderInternal(NoteItemData data) { mCurrentFolderId = data.getId(); startAsyncNotesListQuery(); if (data.getId() == Notes.ID_CALL_RECORD_FOLDER) { @@ -599,31 +683,51 @@ public class NotesListActivity extends Activity implements OnClickListener, OnIt mTitleBar.setText(data.getSnippet()); } mTitleBar.setVisibility(View.VISIBLE); + updateMemoryButtonVisibility(); + updateTrashButtonVisibility(); } - public void onClick(View v) { - switch (v.getId()) { - case R.id.btn_new_note: - createNewNote(); - break; - default: - break; + private void openMemoryBottle() { + if (mMemoryBottleDialog == null) { + mMemoryBottleDialog = new MemoryBottleDialog(this); } + mMemoryBottleDialog.show(); } - private void showSoftInput() {// 显示软键盘 + private void openTrashFolder() { + mCurrentFolderId = Notes.ID_TRASH_FOLER; + mState = ListEditState.TRASH_FOLDER; + mTrashManager.cleanupExpiredTrash(); + startAsyncNotesListQuery(); + mTitleBar.setText(R.string.trash_folder_name); + mTitleBar.setVisibility(View.VISIBLE); + mAddNewNote.setVisibility(View.GONE); + updateMemoryButtonVisibility(); + updateTrashButtonVisibility(); + } + + @Override + protected void onDestroy() { + if (mMemoryBottleDialog != null && mMemoryBottleDialog.isShowing()) { + mMemoryBottleDialog.dismiss(); + } + mMemoryBottleDialog = null; + super.onDestroy(); + } + + 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) {// 隐藏软键盘 + private void hideSoftInput(View view) { InputMethodManager inputMethodManager = (InputMethodManager) getSystemService(Context.INPUT_METHOD_SERVICE); inputMethodManager.hideSoftInputFromWindow(view.getWindowToken(), 0); } - private void showCreateOrModifyFolderDialog(final boolean create) {// 显示创建或修改文件夹对话框 + 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); @@ -650,7 +754,7 @@ public class NotesListActivity extends Activity implements OnClickListener, OnIt final Dialog dialog = builder.setView(view).show(); final Button positive = (Button)dialog.findViewById(android.R.id.button1); - positive.setOnClickListener(new OnClickListener() {// 确认创建或修改文件夹 + positive.setOnClickListener(new OnClickListener() { public void onClick(View v) { hideSoftInput(etName); String name = etName.getText().toString(); @@ -661,17 +765,17 @@ public class NotesListActivity extends Activity implements OnClickListener, OnIt return; } if (!create) { - if (!TextUtils.isEmpty(name)) {// 修改文件夹名称 + 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()) + String.valueOf(mFocusNoteDataItem.getId()) }); } - } else if (!TextUtils.isEmpty(name)) {// 创建新文件夹 + } else if (!TextUtils.isEmpty(name)) { ContentValues values = new ContentValues(); values.put(NoteColumns.SNIPPET, name); values.put(NoteColumns.TYPE, Notes.TYPE_FOLDER); @@ -681,19 +785,19 @@ public class NotesListActivity extends Activity implements OnClickListener, OnIt } }); - if (TextUtils.isEmpty(etName.getText())) {// 输入框为空时禁用确认按钮 + if (TextUtils.isEmpty(etName.getText())) { positive.setEnabled(false); } /** * When the name edit text is null, disable the positive button */ - etName.addTextChangedListener(new TextWatcher() {// 监听输入框文本变化 + 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) {// 输入框文本变化时更新确认按钮状态 + public void onTextChanged(CharSequence s, int start, int before, int count) { if (TextUtils.isEmpty(etName.getText())) { positive.setEnabled(false); } else { @@ -701,7 +805,7 @@ public class NotesListActivity extends Activity implements OnClickListener, OnIt } } - public void afterTextChanged(Editable s) {// 输入框文本变化后更新确认按钮状态 + public void afterTextChanged(Editable s) { // TODO Auto-generated method stub } @@ -709,22 +813,35 @@ public class NotesListActivity extends Activity implements OnClickListener, OnIt } @Override - public void onBackPressed() {// 处理返回键事件 + public void onBackPressed() { switch (mState) { - case SUB_FOLDER:// 返回上一级文件夹 + case SUB_FOLDER: mCurrentFolderId = Notes.ID_ROOT_FOLDER; mState = ListEditState.NOTE_LIST; startAsyncNotesListQuery(); mTitleBar.setVisibility(View.GONE); + updateMemoryButtonVisibility(); + updateTrashButtonVisibility(); break; - case CALL_RECORD_FOLDER:// 返回上一级通话记录文件夹 + case CALL_RECORD_FOLDER: mCurrentFolderId = Notes.ID_ROOT_FOLDER; mState = ListEditState.NOTE_LIST; mAddNewNote.setVisibility(View.VISIBLE); mTitleBar.setVisibility(View.GONE); startAsyncNotesListQuery(); + updateMemoryButtonVisibility(); + updateTrashButtonVisibility(); + break; + case TRASH_FOLDER: + mCurrentFolderId = Notes.ID_ROOT_FOLDER; + mState = ListEditState.NOTE_LIST; + startAsyncNotesListQuery(); + mTitleBar.setVisibility(View.GONE); + mAddNewNote.setVisibility(View.VISIBLE); + updateMemoryButtonVisibility(); + updateTrashButtonVisibility(); break; - case NOTE_LIST:// 返回主界面 + case NOTE_LIST: super.onBackPressed(); break; default: @@ -732,7 +849,7 @@ public class NotesListActivity extends Activity implements OnClickListener, OnIt } } - private void updateWidget(int appWidgetId, int appWidgetType) {// 更新指定应用小部件 + 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); @@ -744,14 +861,14 @@ public class NotesListActivity extends Activity implements OnClickListener, OnIt } intent.putExtra(AppWidgetManager.EXTRA_APPWIDGET_IDS, new int[] { - appWidgetId + appWidgetId }); sendBroadcast(intent); setResult(RESULT_OK, intent); } - private final OnCreateContextMenuListener mFolderOnCreateContextMenuListener = new OnCreateContextMenuListener() {// 文件夹上下文菜单创建监听 + private final OnCreateContextMenuListener mFolderOnCreateContextMenuListener = new OnCreateContextMenuListener() { public void onCreateContextMenu(ContextMenu menu, View v, ContextMenuInfo menuInfo) { if (mFocusNoteDataItem != null) { menu.setHeaderTitle(mFocusNoteDataItem.getSnippet()); @@ -763,7 +880,7 @@ public class NotesListActivity extends Activity implements OnClickListener, OnIt }; @Override - public void onContextMenuClosed(Menu menu) {// 上下文菜单关闭时移除监听 + public void onContextMenuClosed(Menu menu) { if (mNotesListView != null) { mNotesListView.setOnCreateContextMenuListener(null); } @@ -777,10 +894,10 @@ public class NotesListActivity extends Activity implements OnClickListener, OnIt return false; } switch (item.getItemId()) { - case MENU_FOLDER_VIEW:// 查看文件夹 + case MENU_FOLDER_VIEW: openFolder(mFocusNoteDataItem); break; - case MENU_FOLDER_DELETE:// 删除文件夹 + 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); @@ -794,7 +911,7 @@ public class NotesListActivity extends Activity implements OnClickListener, OnIt builder.setNegativeButton(android.R.string.cancel, null); builder.show(); break; - case MENU_FOLDER_CHANGE_NAME:// 重命名文件夹 + case MENU_FOLDER_CHANGE_NAME: showCreateOrModifyFolderDialog(false); break; default: @@ -805,7 +922,7 @@ public class NotesListActivity extends Activity implements OnClickListener, OnIt } @Override - public boolean onPrepareOptionsMenu(Menu menu) {// 准备选项菜单 + public boolean onPrepareOptionsMenu(Menu menu) { menu.clear(); if (mState == ListEditState.NOTE_LIST) { getMenuInflater().inflate(R.menu.note_list, menu); @@ -816,6 +933,12 @@ public class NotesListActivity extends Activity implements OnClickListener, OnIt getMenuInflater().inflate(R.menu.sub_folder, menu); } else if (mState == ListEditState.CALL_RECORD_FOLDER) { getMenuInflater().inflate(R.menu.call_record_folder, menu); + } else if (mState == ListEditState.TRASH_FOLDER) { + getMenuInflater().inflate(R.menu.sub_folder, menu); + MenuItem newNote = menu.findItem(R.id.menu_new_note); + if (newNote != null) { + newNote.setVisible(false); + } } else { Log.e(TAG, "Wrong state:" + mState); } @@ -823,19 +946,23 @@ public class NotesListActivity extends Activity implements OnClickListener, OnIt } @Override - public boolean onOptionsItemSelected(MenuItem item) {// 选项菜单项点击事件处理 + public boolean onOptionsItemSelected(MenuItem item) { switch (item.getItemId()) { - case R.id.menu_new_folder: {// 新建文件夹 + case R.id.menu_new_folder: { showCreateOrModifyFolderDialog(true); break; } - case R.id.menu_export_text: {// 导出笔记为文本文件 + case R.id.menu_new_encrypted_folder: { + mEncryptedFolderManager.showCreateEncryptedFolderDialog(); + break; + } + case R.id.menu_export_text: { exportNoteToText(); break; } - case R.id.menu_sync: {// 同步笔记 + case R.id.menu_sync: { if (isSyncMode()) { - if (TextUtils.equals(item.getTitle(), getString(R.string.menu_sync))) {// 同步笔记 + if (TextUtils.equals(item.getTitle(), getString(R.string.menu_sync))) { GTaskSyncService.startSync(this); } else { GTaskSyncService.cancelSync(this); @@ -845,23 +972,17 @@ public class NotesListActivity extends Activity implements OnClickListener, OnIt } break; } - case R.id.menu_setting: {// 打开偏好设置 + case R.id.menu_setting: { startPreferenceActivity(); break; } - case R.id.menu_new_note: {// 新建笔记 + case R.id.menu_new_note: { createNewNote(); break; } - case R.id.menu_search: {// 搜索笔记 - onSearchRequested(); - break; - } - - case R.id.menu_search:{ + case R.id.menu_search: onSearchRequested(); break; - } default: break; } @@ -869,24 +990,24 @@ public class NotesListActivity extends Activity implements OnClickListener, OnIt } @Override - public boolean onSearchRequested() {// 搜索笔记 + public boolean onSearchRequested() { startSearch(null, false, null /* appData */, false); return true; } - private void exportNoteToText() {// 导出笔记为文本文件 + private void exportNoteToText() { final BackupUtils backup = BackupUtils.getInstance(NotesListActivity.this); new AsyncTask() { @Override - protected Integer doInBackground(Void... unused) {// 后台导出笔记为文本文件 + protected Integer doInBackground(Void... unused) { return backup.exportToText(); } @Override - protected void onPostExecute(Integer result) {// 导出笔记为文本文件完成后的回调 + protected void onPostExecute(Integer result) { if (result == BackupUtils.STATE_SD_CARD_UNMOUONTED) { - AlertDialog.Builder builder = new AlertDialog.Builder(NotesListActivity.this);// 导出笔记为文本文件失败对话框 + AlertDialog.Builder builder = new AlertDialog.Builder(NotesListActivity.this); builder.setTitle(NotesListActivity.this .getString(R.string.failed_sdcard_export)); builder.setMessage(NotesListActivity.this @@ -894,7 +1015,7 @@ public class NotesListActivity extends Activity implements OnClickListener, OnIt builder.setPositiveButton(android.R.string.ok, null); builder.show(); } else if (result == BackupUtils.STATE_SUCCESS) { - AlertDialog.Builder builder = new AlertDialog.Builder(NotesListActivity.this);// 导出笔记为文本文件成功对话框 + AlertDialog.Builder builder = new AlertDialog.Builder(NotesListActivity.this); builder.setTitle(NotesListActivity.this .getString(R.string.success_sdcard_export)); builder.setMessage(NotesListActivity.this.getString( @@ -902,7 +1023,7 @@ public class NotesListActivity extends Activity implements OnClickListener, OnIt .getExportedTextFileName(), backup.getExportedTextFileDir())); builder.setPositiveButton(android.R.string.ok, null); builder.show(); - } else if (result == BackupUtils.STATE_SYSTEM_ERROR) {// 导出笔记为文本文件系统错误对话框 + } 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)); @@ -916,19 +1037,19 @@ public class NotesListActivity extends Activity implements OnClickListener, OnIt }.execute(); } - private boolean isSyncMode() {// 判断是否为同步模式 + private boolean isSyncMode() { return NotesPreferenceActivity.getSyncAccountName(this).trim().length() > 0; } - private void startPreferenceActivity() {// 打开偏好设置活动 + 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 {// 列表项点击监听器 + private class OnListItemClickListener implements OnItemClickListener { - public void onItemClick(AdapterView parent, View view, int position, long id) {// 列表项点击事件 + public void onItemClick(AdapterView parent, View view, int position, long id) { if (view instanceof NotesListItem) { NoteItemData item = ((NotesListItem) view).getItemData(); if (mNotesListAdapter.isInChoiceMode()) { @@ -940,9 +1061,9 @@ public class NotesListActivity extends Activity implements OnClickListener, OnIt return; } - switch (mState) {// 根据当前状态处理点击事件 + switch (mState) { case NOTE_LIST: - if (item.getType() == Notes.TYPE_FOLDER// 点击文件夹项 + if (item.getType() == Notes.TYPE_FOLDER || item.getType() == Notes.TYPE_SYSTEM) { openFolder(item); } else if (item.getType() == Notes.TYPE_NOTE) { @@ -959,6 +1080,14 @@ public class NotesListActivity extends Activity implements OnClickListener, OnIt Log.e(TAG, "Wrong note type in SUB_FOLDER"); } break; + case TRASH_FOLDER: + if (item.getType() == Notes.TYPE_NOTE) { + openNode(item); + } else { + Toast.makeText(NotesListActivity.this, + R.string.menu_restore, Toast.LENGTH_SHORT).show(); + } + break; default: break; } @@ -967,10 +1096,10 @@ public class NotesListActivity extends Activity implements OnClickListener, OnIt } - private void startQueryDestinationFolders() {// 查询目标文件夹 + 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 + ")"; + "(" + selection + ") OR (" + NoteColumns.ID + "=" + Notes.ID_ROOT_FOLDER + ")"; mBackgroundQueryHandler.startQuery(FOLDER_LIST_QUERY_TOKEN, null, @@ -985,10 +1114,17 @@ public class NotesListActivity extends Activity implements OnClickListener, OnIt NoteColumns.MODIFIED_DATE + " DESC"); } - public boolean onItemLongClick(AdapterView parent, View view, int position, long id) {// 列表项长按事件 + 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 (mState == ListEditState.TRASH_FOLDER && !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_NOTE && !mNotesListAdapter.isInChoiceMode()) { if (mNotesListView.startActionMode(mModeCallBack) != null) { mModeCallBack.onItemCheckedStateChanged(null, position, id, true); mNotesListView.performHapticFeedback(HapticFeedbackConstants.LONG_PRESS); @@ -1001,4 +1137,4 @@ public class NotesListActivity extends Activity implements OnClickListener, OnIt } return false; } -} +} \ No newline at end of file diff --git a/src/ui/NotesListItem.java b/src/ui/NotesListItem.java index 60c91cd..694f325 100644 --- a/src/ui/NotesListItem.java +++ b/src/ui/NotesListItem.java @@ -75,6 +75,15 @@ public class NotesListItem extends LinearLayout { + context.getString(R.string.format_folder_files_count, data.getNotesCount())); mAlert.setImageResource(R.drawable.call_record); } + // 处理回收站文件夹 + else if (data.getId() == Notes.ID_TRASH_FOLER) { + mCallName.setVisibility(View.GONE); + mAlert.setVisibility(View.VISIBLE); + mTitle.setTextAppearance(context, R.style.TextAppearancePrimaryItem); + mTitle.setText(context.getString(R.string.trash_folder_name) + + context.getString(R.string.format_folder_files_count, data.getNotesCount())); + mAlert.setImageResource(R.drawable.trash); + } // 处理通话记录笔记 else if (data.getParentId() == Notes.ID_CALL_RECORD_FOLDER) { mCallName.setVisibility(View.VISIBLE);