diff --git a/src/ui(m)/NotesListActivity.java b/src/ui(m)/NotesListActivity.java index b5291ec..37bc24b 100644 --- a/src/ui(m)/NotesListActivity.java +++ b/src/ui(m)/NotesListActivity.java @@ -1,10 +1,12 @@ /* * 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 + * 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. @@ -12,12 +14,70 @@ * limitations under the License. */ +// 导入 Android 相关的类库 package net.micode.notes.ui; import android.app.Activity; import android.app.AlertDialog; import android.app.Dialog; -// ...(省略其他import) +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; // NotesListActivity类继承自Activity,用于展示笔记列表 public class NotesListActivity extends Activity implements OnClickListener, OnItemLongClickListener { @@ -34,6 +94,7 @@ public class NotesListActivity extends Activity implements OnClickListener, OnIt private enum ListEditState { NOTE_LIST, SUB_FOLDER, CALL_RECORD_FOLDER }; + // 成员变量声明 private ListEditState mState; private BackgroundQueryHandler mBackgroundQueryHandler; @@ -52,12 +113,13 @@ public class NotesListActivity extends Activity implements OnClickListener, OnIt 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 " + + 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; + // Activity 创建时调用的方法 @Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); @@ -69,722 +131,1041 @@ public class NotesListActivity extends Activity implements OnClickListener, OnIt */ 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 { + // 调用父类的onActivityResult方法处理其他情况 + super.onActivityResult(requestCode, resultCode, data); + } +} - /** - * 从raw资源文件中读取介绍信息,并将其保存为笔记 - */ - 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; +private void setAppInfoFromRawRes() { + // 获取默认的SharedPreferences实例 + SharedPreferences sp = PreferenceManager.getDefaultSharedPreferences(this); + // 检查是否已经添加了介绍信息 + if (!sp.getBoolean(PREFERENCE_ADD_INTRODUCTION, false)) { + StringBuilder sb = new StringBuilder(); // 用于构建介绍信息的字符串 + InputStream in = null; // 用于读取raw资源文件的输入流 + try { + // 打开raw资源文件 + 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; + // 逐行读取文件内容并追加到StringBuilder中 + while ((len = br.read(buf)) > 0) { + sb.append(buf, 0, len); } - } catch (IOException e) { - e.printStackTrace(); - return; - } finally { - if(in != null) { - try { - in.close(); - } catch (IOException e) { - // TODO Auto-generated catch block - e.printStackTrace(); - } - } - } - - WorkingNote note = WorkingNote.createEmptyNote(this, Notes.ID_ROOT_FOLDER, - AppWidgetManager.INVALID_APPWIDGET_ID, Notes.TYPE_WIDGET_INVALIDE, - ResourceParser.RED); - note.setWorkingText(sb.toString()); - if (note.saveNote()) { - sp.edit().putBoolean(PREFERENCE_ADD_INTRODUCTION, true).commit(); } else { - Log.e(TAG, "Save introduction note error"); + // 如果输入流为空,记录错误信息 + Log.e(TAG, "Read introduction file error"); return; } + } catch (IOException e) { + // 捕获并记录IO异常 + e.printStackTrace(); + return; + } finally { + // 确保输入流在使用后关闭 + if (in != null) { + try { + in.close(); + } catch (IOException e) { + // 捕获并记录关闭输入流时的IO异常 + e.printStackTrace(); + } + } } - } - @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); + // 创建一个新的笔记对象 + 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()) { + // 如果保存成功,更新SharedPreferences,标记已经添加了介绍信息 + sp.edit().putBoolean(PREFERENCE_ADD_INTRODUCTION, true).commit(); } else { - super.onActivityResult(requestCode, resultCode, data); + // 如果保存失败,记录错误信息 + Log.e(TAG, "Save introduction note error"); + return; } } +} +@Override +protected void onStart() { + super.onStart(); + // 当Activity启动时,开始异步查询笔记列表 + 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; - mOriginY = 0; - mTitleBar = (TextView) findViewById(R.id.tv_title_bar); - mState = ListEditState.NOTE_LIST; - mModeCallBack = new ModeCallback(); - } - // 内部类,用于处理多点触控事件,当用户触摸“新建笔记”按钮时的特殊处理 - 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; - int eventY = start + (int) event.getY(); - // 根据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(); - mDispatchY = eventY; - event.setLocation(event.getX(), mDispatchY); - mDispatch = true; - return mNotesListView.dispatchTouchEvent(event); - } - } - break; - } - case MotionEvent.ACTION_MOVE: { - if (mDispatch) { - mDispatchY += (int) event.getY() - mOriginY; - event.setLocation(event.getX(), mDispatchY); - return mNotesListView.dispatchTouchEvent(event); - } - break; - } - default: { - if (mDispatch) { - event.setLocation(event.getX(), mDispatchY); - mDispatch = false; - return mNotesListView.dispatchTouchEvent(event); - } - break; - } - } - return false; - } - }; +private void initResources() { + // 初始化内容解析器 + mContentResolver = this.getContentResolver(); + // 创建异步查询处理器 + mBackgroundQueryHandler = new BackgroundQueryHandler(this.getContentResolver()); + // 设置当前文件夹ID为根文件夹ID + 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(); +} - /** - * 启动异步查询,获取笔记列表 - */ - private void startAsyncNotesListQuery() { - String selection = (mCurrentFolderId == Notes.ID_ROOT_FOLDER) ? ROOT_FOLDER_SELECTION - : NORMAL_SELECTION; - mBackgroundQueryHandler.startQuery(FOLDER_NOTE_LIST_QUERY_TOKEN, null, - Notes.CONTENT_NOTE_URI, NoteItemData.PROJECTION, selection, new String[] { - String.valueOf(mCurrentFolderId) - }, NoteColumns.TYPE + " DESC," + NoteColumns.MODIFIED_DATE + " DESC"); - } +private class ModeCallback implements ListView.MultiChoiceModeListener, OnMenuItemClickListener { + private DropdownMenu mDropDownMenu; // 下拉菜单 + private ActionMode mActionMode; // 多选模式 + private MenuItem mMoveMenu; // 移动菜单项 - // 异步查询处理器,处理查询完成事件 - private final class BackgroundQueryHandler extends AsyncQueryHandler { - public BackgroundQueryHandler(ContentResolver contentResolver) { - super(contentResolver); + // 创建多选模式时调用 + 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); - @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; + // 加载自定义视图 + 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 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(); + // 更新菜单状态 + 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); } - }); - 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); - this.startActivityForResult(intent, REQUEST_CODE_NEW_NODE); + } } - /** - * 批量删除笔记的函数 - */ - private void batchDelete() { - new AsyncTask>() { - protected HashSet doInBackground(Void... unused) { - HashSet 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; - } + // 准备多选模式时调用 + public boolean onPrepareActionMode(ActionMode mode, Menu menu) { + // TODO Auto-generated method stub + return false; + } - @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(); + // 点击多选模式菜单项时调用 + public boolean onActionItemClicked(ActionMode mode, MenuItem item) { + // TODO Auto-generated method stub + return false; } - /** - * 删除文件夹的函数 - */ - private void deleteFolder(long folderId) { - if (folderId == Notes.ID_ROOT_FOLDER) { - Log.e(TAG, "Wrong folder id, should not happen " + folderId); - return; - } + // 多选模式销毁时调用 + public void onDestroyActionMode(ActionMode mode) { + // 重置适配器为非多选模式 + mNotesListAdapter.setChoiceMode(false); + // 启用列表项的长按事件 + mNotesListView.setLongClickable(true); + // 显示新建笔记按钮 + mAddNewNote.setVisibility(View.VISIBLE); + } - HashSet ids = new HashSet(); - ids.add(folderId); - HashSet 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); - } - } - } + // 结束多选模式 + public void finishActionMode() { + mActionMode.finish(); } - /** - * 打开笔记项的函数 - */ - 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); + // 列表项选中状态改变时调用 + public void onItemCheckedStateChanged(ActionMode mode, int position, long id, + boolean checked) { + // 更新适配器的选中状态 + mNotesListAdapter.setCheckedItem(position, checked); + // 更新菜单状态 + updateMenu(); } - /** - * 打开文件夹的函数 - */ - private void openFolder(NoteItemData data) { - mCurrentFolderId = data.getId(); - startAsyncNotesListQuery(); - if (data.getId() == Notes.ID_CALL_RECORD_FOLDER) { - mState = ListEditState.CALL_RECORD_FOLDER; - mAddNewNote.setVisibility(View.GONE); - } else { - mState = ListEditState.SUB_FOLDER; + // 菜单项点击事件 + 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; } - 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 boolean onMenuItemClick(MenuItem item) { + // 根据菜单项的ID进行处理 + switch (item.getItemId()) { + case R.id.delete: + // 创建一个AlertDialog.Builder实例 + 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: + // 如果不是已知的菜单项,返回false + return false; + } + // 处理成功,返回true + return true; +} - @Override - public void onClick(View v) { - switch (v.getId()) { - case R.id.btn_new_note: - createNewNote(); +// 内部类,用于处理多点触控事件,当用户触摸“新建笔记”按钮时的特殊处理 +private class NewNoteOnTouchListener implements OnTouchListener { + public boolean onTouch(View v, MotionEvent event) { + // 根据事件类型进行处理 + switch (event.getAction()) { + case MotionEvent.ACTION_DOWN: { + // 获取屏幕高度 + Display display = getWindowManager().getDefaultDisplay(); + int screenHeight = display.getHeight(); + // 获取“新建笔记”按钮的高度 + int newNoteViewHeight = mAddNewNote.getHeight(); + // 计算“新建笔记”按钮的起始位置 + int start = screenHeight - newNoteViewHeight; + // 计算事件的Y坐标 + int eventY = start + (int) event.getY(); + /** + * 如果当前状态是子文件夹状态,减去标题栏的高度 + */ + if (mState == ListEditState.SUB_FOLDER) { + eventY -= mTitleBar.getHeight(); + start -= mTitleBar.getHeight(); + } + /** + * HACKME: 当点击“新建笔记”按钮的透明部分时,将事件分发给背后的列表视图。 + * “新建笔记”按钮的透明部分可以用公式 y=-0.12x+94(单位:像素)和按钮的顶部线表示。 + * 坐标基于“新建笔记”按钮的左侧。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))) { + // 记录初始Y坐标 + mOriginY = (int) event.getY(); + mDispatchY = eventY; + // 重新设置事件的Y坐标 + event.setLocation(event.getX(), mDispatchY); + // 标记为需要分发 + mDispatch = true; + // 分发事件到列表视图 + return mNotesListView.dispatchTouchEvent(event); + } + } break; - default: + } + case MotionEvent.ACTION_MOVE: { + // 如果需要分发 + if (mDispatch) { + // 更新事件的Y坐标 + mDispatchY += (int) event.getY() - mOriginY; + event.setLocation(event.getX(), mDispatchY); + // 分发事件到列表视图 + return mNotesListView.dispatchTouchEvent(event); + } + break; + } + default: { + // 如果需要分发 + if (mDispatch) { + // 重新设置事件的Y坐标 + event.setLocation(event.getX(), mDispatchY); + // 重置分发标志 + mDispatch = false; + // 分发事件到列表视图 + return mNotesListView.dispatchTouchEvent(event); + } break; + } } + // 事件未处理,返回false + return false; } +} - /** - * 显示软键盘的函数 - */ - private void showSoftInput() { - InputMethodManager inputMethodManager = (InputMethodManager) getSystemService(Context.INPUT_METHOD_SERVICE); - if (inputMethodManager != null) { - inputMethodManager.toggleSoftInput(InputMethodManager.SHOW_FORCED, 0); - } - } +// 启动异步查询,获取笔记列表 +private void startAsyncNotesListQuery() { + // 根据当前文件夹ID选择查询条件 + String selection = (mCurrentFolderId == Notes.ID_ROOT_FOLDER) ? ROOT_FOLDER_SELECTION + : NORMAL_SELECTION; + // 开始异步查询 + mBackgroundQueryHandler.startQuery(FOLDER_NOTE_LIST_QUERY_TOKEN, null, + Notes.CONTENT_NOTE_URI, NoteItemData.PROJECTION, selection, new String[] { + String.valueOf(mCurrentFolderId) + }, NoteColumns.TYPE + " DESC," + NoteColumns.MODIFIED_DATE + " DESC"); +} - /** - * 隐藏软键盘的函数 - */ - private void hideSoftInput(View view) { - InputMethodManager inputMethodManager = (InputMethodManager) getSystemService(Context.INPUT_METHOD_SERVICE); - inputMethodManager.hideSoftInputFromWindow(view.getWindowToken(), 0); +// 异步查询处理器,处理查询完成事件 +private final class BackgroundQueryHandler extends AsyncQueryHandler { + public BackgroundQueryHandler(ContentResolver contentResolver) { + super(contentResolver); } - - /** - * 显示创建或修改文件夹对话框的函数 - */ - 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)); +} +@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, "The long click data item is null"); - return; + // 记录查询失败的错误信息 + Log.e(TAG, "Query folder failed"); } - } else { - etName.setText(""); - builder.setTitle(this.getString(R.string.menu_create_folder)); + break; + default: + // 未知令牌,直接返回 + return; + } +} + +// 显示文件夹列表菜单 +private void showFolderListMenu(Cursor cursor) { + // 创建AlertDialog.Builder实例 + 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(); +} - 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); - } - }); +// 创建新笔记 +private void createNewNote() { + // 创建Intent,启动NoteEditActivity + Intent intent = new Intent(this, NoteEditActivity.class); + // 设置Action为插入或编辑 + intent.setAction(Intent.ACTION_INSERT_OR_EDIT); + // 传递当前文件夹ID + intent.putExtra(Notes.INTENT_EXTRA_FOLDER_ID, mCurrentFolderId); + // 启动Activity并等待结果 + this.startActivityForResult(intent, REQUEST_CODE_NEW_NODE); +} - 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; +// 批量删除笔记 +private void batchDelete() { + // 创建异步任务 + new AsyncTask>() { + protected HashSet doInBackground(Void... unused) { + // 获取选中的小部件属性 + HashSet widgets = mNotesListAdapter.getSelectedWidget(); + if (!isSyncMode()) { + // 如果不是同步模式,直接删除笔记 + if (DataUtils.batchDeleteNotes(mContentResolver, mNotesListAdapter.getSelectedItemIds())) { + } else { + // 记录删除失败的错误信息 + Log.e(TAG, "Delete notes error, should not happens"); } - 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); + } else { + // 如果是同步模式,将删除的笔记移动到回收站文件夹 + if (!DataUtils.batchMoveToFolder(mContentResolver, mNotesListAdapter.getSelectedItemIds(), Notes.ID_TRASH_FOLER)) { + // 记录移动失败的错误信息 + Log.e(TAG, "Move notes to trash folder error, should not happens"); } - dialog.dismiss(); } - }); - - if (TextUtils.isEmpty(etName.getText())) { - positive.setEnabled(false); + return widgets; } - etName.addTextChangedListener(new TextWatcher() { - public void beforeTextChanged(CharSequence s, int start, int count, int after) { - // TODO Auto-generated method stub - } - public void onTextChanged(CharSequence s, int start, int before, int count) { - if (TextUtils.isEmpty(etName.getText())) { - positive.setEnabled(false); - } else { - positive.setEnabled(true); + @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(); +} - public void afterTextChanged(Editable s) { - // TODO Auto-generated method stub - } - }); +// 删除文件夹 +private void deleteFolder(long folderId) { + if (folderId == Notes.ID_ROOT_FOLDER) { + // 如果尝试删除根文件夹,记录错误信息 + Log.e(TAG, "Wrong folder id, should not happen " + folderId); + return; } - @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; - } + // 创建文件夹ID集合 + HashSet ids = new HashSet(); + ids.add(folderId); + // 获取文件夹的小部件属性 + HashSet widgets = DataUtils.getFolderNoteWidget(mContentResolver, folderId); + if (!isSyncMode()) { + // 如果不是同步模式,直接删除文件夹 + DataUtils.batchDeleteNotes(mContentResolver, ids); + } else { + // 如果是同步模式,将删除的文件夹移动到回收站文件夹 + DataUtils.batchMoveToFolder(mContentResolver, ids, Notes.ID_TRASH_FOLER); } - - /** - * 更新Widget的函数 - */ - 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; + // 更新小部件 + 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); + } } + } +} - intent.putExtra(AppWidgetManager.EXTRA_APPWIDGET_IDS, new int[] { - appWidgetId - }); +// 打开笔记项 +private void openNode(NoteItemData data) { + // 创建Intent,启动NoteEditActivity + Intent intent = new Intent(this, NoteEditActivity.class); + // 设置Action为查看 + intent.setAction(Intent.ACTION_VIEW); + // 传递笔记ID + intent.putExtra(Intent.EXTRA_UID, data.getId()); + // 启动Activity并等待结果 + this.startActivityForResult(intent, REQUEST_CODE_OPEN_NODE); +} - sendBroadcast(intent); - setResult(RESULT_OK, intent); +// 打开文件夹 +private void openFolder(NoteItemData data) { + // 更新当前文件夹ID + mCurrentFolderId = data.getId(); + // 重新查询笔记列表 + startAsyncNotesListQuery(); + if (data.getId() == Notes.ID_CALL_RECORD_FOLDER) { + // 如果是通话记录文件夹,更新状态和UI + mState = ListEditState.CALL_RECORD_FOLDER; + mAddNewNote.setVisibility(View.GONE); + } else { + // 如果是普通文件夹,更新状态 + mState = ListEditState.SUB_FOLDER; } - /** - * 为文件夹创建上下文菜单的监听器 - */ - 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); - } - } - }; + // 更新标题栏文本 + 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 void onContextMenuClosed(Menu menu) { - if (mNotesListView != null) { - mNotesListView.setOnCreateContextMenuListener(null); - } - super.onContextMenuClosed(menu); +// 点击事件处理 +public void onClick(View v) { + // 根据点击的视图ID进行处理 + switch (v.getId()) { + case R.id.btn_new_note: + // 创建新笔记 + createNewNote(); + break; + default: + // 其他按钮不做处理 + break; } +} +// 显示软键盘 +private void showSoftInput() { + // 获取InputMethodManager服务 + InputMethodManager inputMethodManager = (InputMethodManager) getSystemService(Context.INPUT_METHOD_SERVICE); + if (inputMethodManager != null) { + // 强制显示软键盘 + inputMethodManager.toggleSoftInput(InputMethodManager.SHOW_FORCED, 0); + } +} - @Override - public boolean onContextItemSelected(MenuItem item) { - if (mFocusNoteDataItem == null) { +// 隐藏软键盘 +private void hideSoftInput(View view) { + // 获取InputMethodManager服务 + InputMethodManager inputMethodManager = (InputMethodManager) getSystemService(Context.INPUT_METHOD_SERVICE); + // 隐藏软键盘 + inputMethodManager.hideSoftInputFromWindow(view.getWindowToken(), 0); +} + +// 显示创建或修改文件夹对话框 +private void showCreateOrModifyFolderDialog(final boolean create) { + // 创建AlertDialog.Builder实例 + 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 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; } - - return true; + } else { + // 如果是创建文件夹,清空编辑文本框 + etName.setText(""); + // 设置对话框标题为“创建文件夹” + builder.setTitle(this.getString(R.string.menu_create_folder)); } - @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); + // 设置确认按钮,但不立即设置点击事件监听器 + 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); } - 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; + // 显示对话框 + 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; } - 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(); + // 如果是修改文件夹 + if (!create) { + if (!TextUtils.isEmpty(name)) { + // 创建ContentValues对象,设置文件夹名称和类型 + 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()) + }); } - break; - } - case R.id.menu_setting: { - startPreferenceActivity(); - break; + } 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); } - case R.id.menu_new_note: { - createNewNote(); - break; - } - case R.id.menu_search: - onSearchRequested(); - break; - default: - break; + // 关闭对话框 + dialog.dismiss(); } - return true; - } + }); - @Override - public boolean onSearchRequested() { - startSearch(null, false, null /* appData */, false); - return true; + // 如果编辑文本框为空,禁用确认按钮 + if (TextUtils.isEmpty(etName.getText())) { + positive.setEnabled(false); } - /** - * 导出笔记到文本文件的函数 + * 当编辑文本框为空时,禁用确认按钮 */ - private void exportNoteToText() { - final BackupUtils backup = BackupUtils.getInstance(NotesListActivity.this); - new AsyncTask() { + etName.addTextChangedListener(new TextWatcher() { + public void beforeTextChanged(CharSequence s, int start, int count, int after) { + // 文本变化前的处理 + } - @Override - protected Integer doInBackground(Void... unused) { - return backup.exportToText(); + public void onTextChanged(CharSequence s, int start, int before, int count) { + // 文本变化时的处理 + if (TextUtils.isEmpty(etName.getText())) { + // 如果编辑文本框为空,禁用确认按钮 + positive.setEnabled(false); + } else { + // 如果编辑文本框不为空,启用确认按钮 + positive.setEnabled(true); } + } - @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.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(); - } - } + 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: + // 如果当前在笔记列表状态,调用父类的onBackPressed方法 + super.onBackPressed(); + break; + default: + // 其他状态不做处理 + break; + } +} - }.execute(); +// 更新小部件 +private void updateWidget(int appWidgetId, int appWidgetType) { + // 创建更新小部件的Intent + Intent intent = new Intent(AppWidgetManager.ACTION_APPWIDGET_UPDATE); + // 根据小部件类型设置对应的Provider + 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, "Unsupported widget type"); + return; } - /** - * 检查是否处于同步模式的函数 - */ - private boolean isSyncMode() { - return NotesPreferenceActivity.getSyncAccountName(this).trim().length() > 0; + // 传递小部件ID + intent.putExtra(AppWidgetManager.EXTRA_APPWIDGET_IDS, new int[] { + appWidgetId + }); + + // 发送广播更新小部件 + 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); + } } +}; - /** - * 启动偏好设置活动的函数 - */ - private void startPreferenceActivity() { - Activity from = getParent() != null ? getParent() : this; - Intent intent = new Intent(from, NotesPreferenceActivity.class); - from.startActivityIfNeeded(intent, -1); +// 上下文菜单关闭时调用 +@Override +public void onContextMenuClosed(Menu menu) { + // 移除列表视图的上下文菜单创建监听器 + if (mNotesListView != null) { + mNotesListView.setOnCreateContextMenuListener(null); } + // 调用父类的onContextMenuClosed方法 + super.onContextMenuClosed(menu); +} - // 列表项点击事件的监听器 - private class OnListItemClickListener implements OnItemClickListener { +// 上下文菜单项点击事件处理 +@Override +public boolean onContextItemSelected(MenuItem item) { + // 如果当前焦点笔记项为空,记录错误信息并返回false + if (mFocusNoteDataItem == null) { + Log.e(TAG, "The long click data item is null"); + return false; + } + // 根据菜单项ID进行处理 + 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; + } + // 处理成功,返回true + return true; +} - 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; - } +// 准备选项菜单时调用 +@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); + } + // 返回true表示菜单已准备 + return true; +} - 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; +// 选项菜单项点击事件处理 +@Override +public boolean onOptionsItemSelected(MenuItem item) { + // 根据菜单项ID进行处理 + 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; + } + // 处理成功,返回true + return true; +} +// 搜索请求处理 +@Override +public boolean onSearchRequested() { + // 启动搜索,不传递额外数据 + startSearch(null, false, null /* appData */, false); + // 返回true表示已处理搜索请求 + return true; +} + +// 导出笔记到文本文件 +private void exportNoteToText() { + // 获取BackupUtils实例 + final BackupUtils backup = BackupUtils.getInstance(NotesListActivity.this); + // 创建异步任务 + new AsyncTask() { + @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 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 + ")"; +// 检查是否处于同步模式 +private boolean isSyncMode() { + // 获取同步账户名称,如果非空则表示处于同步模式 + return NotesPreferenceActivity.getSyncAccountName(this).trim().length() > 0; +} - 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"); - } +// 启动偏好设置活动 +private void startPreferenceActivity() { + // 获取当前Activity或其父Activity + Activity from = getParent() != null ? getParent() : this; + // 创建启动偏好设置活动的Intent + Intent intent = new Intent(from, NotesPreferenceActivity.class); + // 启动活动 + from.startActivityIfNeeded(intent, -1); +} - @Override - public boolean onItemLongClick(AdapterView parent, View view, int position, long id) { +// 列表项点击事件监听器 +private class OnListItemClickListener implements OnItemClickListener { + public void onItemClick(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"); + // 获取点击的笔记项数据 + 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)); } - } else if (mFocusNoteDataItem.getType() == Notes.TYPE_FOLDER) { - mNotesListView.setOnCreateContextMenuListener(mFolderOnCreateContextMenuListener); + 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; } } - return false; } } - \ No newline at end of file +// 启动查询目标文件夹 +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); + } + } + // 返回false表示事件已处理 + return false; +} \ No newline at end of file diff --git a/src/ui(m)/NotesPreferenceActivity.java b/src/ui(m)/NotesPreferenceActivity.java index 7a59d05..a594b98 100644 --- a/src/ui(m)/NotesPreferenceActivity.java +++ b/src/ui(m)/NotesPreferenceActivity.java @@ -5,7 +5,7 @@ * 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 + * 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, @@ -14,6 +14,7 @@ * limitations under the License. */ +// 导入所需的包,这些包提供了各种 Android 开发中常用的类和接口 package net.micode.notes.ui; import android.accounts.Account; @@ -47,74 +48,90 @@ import net.micode.notes.data.Notes; import net.micode.notes.data.Notes.NoteColumns; import net.micode.notes.gtask.remote.GTaskSyncService; -/** - * 设置和偏好活动,用于配置应用程序的偏好设置。 - */ +// 定义一个名为 NotesPreferenceActivity 的类,继承自 PreferenceActivity,用于处理应用的偏好设置相关操作 public class NotesPreferenceActivity extends PreferenceActivity { - // 偏好设置文件的名称 + // 定义一个常量,表示偏好设置的名称 public static final String PREFERENCE_NAME = "notes_preferences"; - // 同步账户名称的键 + + // 定义常量,表示同步账户名称的偏好设置键 public static final String PREFERENCE_SYNC_ACCOUNT_NAME = "pref_key_account_name"; - // 最后一次同步时间的键 + + // 定义常量,表示最后一次同步时间的偏好设置键 public static final String PREFERENCE_LAST_SYNC_TIME = "pref_last_sync_time"; - // 设置背景颜色的键 + + // 定义常量,表示设置背景颜色的偏好设置键 public static final String PREFERENCE_SET_BG_COLOR_KEY = "pref_key_bg_random_appear"; - // 同步账户的键 + + // 定义常量,表示同步账户的偏好设置键 private static final String PREFERENCE_SYNC_ACCOUNT_KEY = "pref_sync_account_key"; - // 权限过滤键 + + // 定义常量,表示过滤器的键 private static final String AUTHORITIES_FILTER_KEY = "authorities"; - // 账户类别 + + // 声明一个 PreferenceCategory 类型的成员变量,用于存储账户相关的偏好设置分类 private PreferenceCategory mAccountCategory; - // GTask接收器 + + // 声明一个 GTaskReceiver 类型的成员变量,用于接收与 GTask 相关的广播 private GTaskReceiver mReceiver; - // 原始账户数组 + + // 声明一个 Account 数组类型的成员变量,用于存储原始账户信息 private Account[] mOriAccounts; - // 是否添加了账户 + + // 声明一个布尔类型的成员变量,用于标记是否添加了新账户 private boolean mHasAddedAccount; + // 重写 onCreate 方法,用于初始化活动 @Override protected void onCreate(Bundle icicle) { super.onCreate(icicle); - /* 使用应用程序图标进行导航 */ + // 使用应用图标作为导航图标 getActionBar().setDisplayHomeAsUpEnabled(true); // 从资源文件中添加偏好设置 addPreferencesFromResource(R.xml.preferences); - // 获取账户类别 + // 获取同步账户的偏好设置分类 mAccountCategory = (PreferenceCategory) findPreference(PREFERENCE_SYNC_ACCOUNT_KEY); - // 创建GTask接收器 + // 初始化 GTaskReceiver 实例 mReceiver = new GTaskReceiver(); - // 创建意图过滤器 + // 创建 IntentFilter 实例,用于过滤与 GTask 服务相关的广播 IntentFilter filter = new IntentFilter(); filter.addAction(GTaskSyncService.GTASK_SERVICE_BROADCAST_NAME); - // 注册接收器 + // 注册广播接收器 registerReceiver(mReceiver, filter); - // 初始化原始账户数组 + // 初始化原始账户信息为 null mOriAccounts = null; - // 从XML布局文件中加载设置头部 + // 使用布局填充器从布局文件中加载设置头部视图 View header = LayoutInflater.from(this).inflate(R.layout.settings_header, null); - // 将头部添加到偏好列表中 + // 将头部视图添加到列表视图中 getListView().addHeaderView(header, null, true); } + // 重写 onResume 方法,用于在活动恢复时执行相关操作 @Override protected void onResume() { super.onResume(); - // 自动设置同步账户,如果用户添加了新账户 + // 如果添加了新账户,则需要自动设置同步账户 if (mHasAddedAccount) { + // 获取谷歌账户信息 Account[] accounts = getGoogleAccounts(); + // 如果原始账户信息不为空且新获取的账户数量大于原始账户数量 if (mOriAccounts != null && accounts.length > mOriAccounts.length) { + // 遍历新获取的账户信息 for (Account accountNew : accounts) { + // 标记是否找到匹配的账户 boolean found = false; + // 遍历原始账户信息 for (Account accountOld : mOriAccounts) { + // 如果新旧账户名称相同,则标记为找到 if (TextUtils.equals(accountOld.name, accountNew.name)) { found = true; break; } } + // 如果未找到匹配的账户,则设置同步账户为新账户 if (!found) { setSyncAccount(accountNew.name); break; @@ -123,172 +140,173 @@ public class NotesPreferenceActivity extends PreferenceActivity { } } - // 刷新UI + // 刷新 UI 界面 refreshUI(); } - - @Override - protected void onDestroy() { - // 如果接收器不为空,注销接收器 - if (mReceiver != null) { - unregisterReceiver(mReceiver); - } - super.onDestroy(); +// 重写 onDestroy 方法,用于在活动销毁时执行相关操作 +@Override +protected void onDestroy() { + // 如果广播接收器不为空,则注销广播接收器 + if (mReceiver != null) { + unregisterReceiver(mReceiver); } + // 调用父类的 onDestroy 方法 + super.onDestroy(); +} - /** - * 加载账户偏好设置。 - */ - private void loadAccountPreference() { - // 移除所有账户偏好设置 - mAccountCategory.removeAll(); - - // 创建一个新的偏好设置 - Preference accountPref = new Preference(this); - // 获取默认账户 - final String defaultAccount = getSyncAccountName(this); - accountPref.setTitle(getString(R.string.preferences_account_title)); - accountPref.setSummary(getString(R.string.preferences_account_summary)); - accountPref.setOnPreferenceClickListener(new OnPreferenceClickListener() { - public boolean onPreferenceClick(Preference preference) { - // 如果GTask服务没有正在同步 - if (!GTaskSyncService.isSyncing()) { - // 如果默认账户为空 - if (TextUtils.isEmpty(defaultAccount)) { - // 第一次设置账户 - showSelectAccountAlertDialog(); - } else { - // 如果账户已经设置,提示用户风险 - showChangeAccountConfirmAlertDialog(); - } +// 定义一个私有方法 loadAccountPreference,用于加载账户相关的偏好设置 +private void loadAccountPreference() { + // 清除账户分类中的所有偏好设置 + mAccountCategory.removeAll(); + + // 创建一个新的 Preference 实例 + Preference accountPref = new Preference(this); + // 获取默认的同步账户名称 + final String defaultAccount = getSyncAccountName(this); + // 设置偏好设置的标题 + accountPref.setTitle(getString(R.string.preferences_account_title)); + // 设置偏好设置的摘要 + accountPref.setSummary(getString(R.string.preferences_account_summary)); + // 设置偏好设置的点击监听器 + accountPref.setOnPreferenceClickListener(new OnPreferenceClickListener() { + public boolean onPreferenceClick(Preference preference) { + // 如果没有正在同步 + if (!GTaskSyncService.isSyncing()) { + // 如果默认账户名称为空 + if (TextUtils.isEmpty(defaultAccount)) { + // 显示选择账户的对话框 + showSelectAccountAlertDialog(); } else { - // 提示用户不能更改账户 - Toast.makeText(NotesPreferenceActivity.this, - R.string.preferences_toast_cannot_change_account, Toast.LENGTH_SHORT) - .show(); + // 如果账户已经设置,提示用户更改账户的风险 + showChangeAccountConfirmAlertDialog(); } - return true; + } else { + // 显示提示信息,表示不能更改账户 + Toast.makeText(NotesPreferenceActivity.this, + R.string.preferences_toast_cannot_change_account, Toast.LENGTH_SHORT) + .show(); } - }); - - // 将账户偏好设置添加到账户类别中 - mAccountCategory.addPreference(accountPref); - } - - /** - * 加载同步按钮。 - */ - private void loadSyncButton() { - // 获取同步按钮和最后同步时间视图 - Button syncButton = (Button) findViewById(R.id.preference_sync_button); - TextView lastSyncTimeView = (TextView) findViewById(R.id.prefenerece_sync_status_textview); - - // 设置按钮状态 - if (GTaskSyncService.isSyncing()) { - syncButton.setText(getString(R.string.preferences_button_sync_cancel)); - syncButton.setOnClickListener(new View.OnClickListener() { - public void onClick(View v) { - // 取消同步 - GTaskSyncService.cancelSync(NotesPreferenceActivity.this); - } - }); - } else { - syncButton.setText(getString(R.string.preferences_button_sync_immediately)); - syncButton.setOnClickListener(new View.OnClickListener() { - public void onClick(View v) { - // 立即同步 - GTaskSyncService.startSync(NotesPreferenceActivity.this); - } - }); + return true; } - syncButton.setEnabled(!TextUtils.isEmpty(getSyncAccountName(this))); + }); + + // 将账户偏好设置添加到账户分类中 + mAccountCategory.addPreference(accountPref); +} - // 设置最后同步时间 - if (GTaskSyncService.isSyncing()) { - lastSyncTimeView.setText(GTaskSyncService.getProgressString()); +// 定义一个私有方法 loadSyncButton,用于加载同步按钮和显示最后一次同步时间 +private void loadSyncButton() { + // 获取同步按钮和显示最后一次同步时间的文本视图 + Button syncButton = (Button) findViewById(R.id.preference_sync_button); + TextView lastSyncTimeView = (TextView) findViewById(R.id.prefenerece_sync_status_textview); + + // 设置按钮状态 + if (GTaskSyncService.isSyncing()) { + // 如果正在同步,设置按钮文本为取消同步,并设置点击监听器 + syncButton.setText(getString(R.string.preferences_button_sync_cancel)); + syncButton.setOnClickListener(new View.OnClickListener() { + public void onClick(View v) { + GTaskSyncService.cancelSync(NotesPreferenceActivity.this); + } + }); + } else { + // 如果没有正在同步,设置按钮文本为立即同步,并设置点击监听器 + syncButton.setText(getString(R.string.preferences_button_sync_immediately)); + syncButton.setOnClickListener(new View.OnClickListener() { + public void onClick(View v) { + GTaskSyncService.startSync(NotesPreferenceActivity.this); + } + }); + } + // 如果同步账户名称不为空,启用按钮 + syncButton.setEnabled(!TextUtils.isEmpty(getSyncAccountName(this))); + + // 设置最后一次同步时间 + if (GTaskSyncService.isSyncing()) { + // 如果正在同步,显示同步进度 + lastSyncTimeView.setText(GTaskSyncService.getProgressString()); + lastSyncTimeView.setVisibility(View.VISIBLE); + } else { + // 如果没有正在同步,显示最后一次同步时间 + long lastSyncTime = getLastSyncTime(this); + if (lastSyncTime != 0) { + lastSyncTimeView.setText(getString(R.string.preferences_last_sync_time, + DateFormat.format(getString(R.string.preferences_last_sync_time_format), + lastSyncTime))); lastSyncTimeView.setVisibility(View.VISIBLE); } else { - long lastSyncTime = getLastSyncTime(this); - if (lastSyncTime != 0) { - lastSyncTimeView.setText(getString(R.string.preferences_last_sync_time, - DateFormat.format(getString(R.string.preferences_last_sync_time_format), - lastSyncTime))); - lastSyncTimeView.setVisibility(View.VISIBLE); - } else { - lastSyncTimeView.setVisibility(View.GONE); - } + lastSyncTimeView.setVisibility(View.GONE); } } +} - /** - * 刷新UI。 - */ - private void refreshUI() { - // 加载账户偏好设置和同步按钮 - loadAccountPreference(); - loadSyncButton(); - } +// 定义一个私有方法 refreshUI,用于刷新 UI 界面 +private void refreshUI() { + // 加载账户偏好设置和同步按钮 + loadAccountPreference(); + loadSyncButton(); +} - /** - * 显示选择账户的警告对话框。 - */ - private void showSelectAccountAlertDialog() { - // 创建警告对话框的构建器 - AlertDialog.Builder dialogBuilder = new AlertDialog.Builder(this); - - // 从XML布局文件中加载标题视图 - View titleView = LayoutInflater.from(this).inflate(R.layout.account_dialog_title, null); - TextView titleTextView = (TextView) titleView.findViewById(R.id.account_dialog_title); - titleTextView.setText(getString(R.string.preferences_dialog_select_account_title)); - TextView subtitleTextView = (TextView) titleView.findViewById(R.id.account_dialog_subtitle); - subtitleTextView.setText(getString(R.string.preferences_dialog_select_account_tips)); - - // 设置自定义标题 - dialogBuilder.setCustomTitle(titleView); - dialogBuilder.setPositiveButton(null, null); - - // 获取Google账户 - Account[] accounts = getGoogleAccounts()); - String defAccount = getSyncAccountName(this); - - // 初始化原始账户数组和添加账户标志 - mOriAccounts = accounts; - mHasAddedAccount = false; - - // 如果账户数量大于0 - if (accounts.length > 0) { - // 创建账户项 - CharSequence[] items = new CharSequence[accounts.length]; - final CharSequence[] itemMapping = items; - int checkedItem = -1; - int index = 0; - for (Account account : accounts) { - if (TextUtils.equals(account.name, defAccount)) { - checkedItem = index; - } - items[index++] = account.name; +// 定义一个私有方法 showSelectAccountAlertDialog,用于显示选择账户的对话框 +private void showSelectAccountAlertDialog() { + // 创建一个对话框构建器 + AlertDialog.Builder dialogBuilder = new AlertDialog.Builder(this); + + // 从布局文件中加载自定义标题视图 + View titleView = LayoutInflater.from(this).inflate(R.layout.account_dialog_title, null); + TextView titleTextView = (TextView) titleView.findViewById(R.id.account_dialog_title); + titleTextView.setText(getString(R.string.preferences_dialog_select_account_title)); + TextView subtitleTextView = (TextView) titleView.findViewById(R.id.account_dialog_subtitle); + subtitleTextView.setText(getString(R.string.preferences_dialog_select_account_tips)); + + // 设置自定义标题 + dialogBuilder.setCustomTitle(titleView); + dialogBuilder.setPositiveButton(null, null); + + // 获取谷歌账户信息 + Account[] accounts = getGoogleAccounts(); + String defAccount = getSyncAccountName(this); + + // 保存原始账户信息 + mOriAccounts = accounts; + mHasAddedAccount = false; + + // 如果有账户信息 + if (accounts.length > 0) { + // 创建账户名称数组 + CharSequence[] items = new CharSequence[accounts.length]; + final CharSequence[] itemMapping = items; + int checkedItem = -1; + int index = 0; + for (Account account : accounts) { + if (TextUtils.equals(account.name, defAccount)) { + checkedItem = index; } - // 设置单选项目 - dialogBuilder.setSingleChoiceItems(items, checkedItem, - new DialogInterface.OnClickListener() { - public void onClick(DialogInterface dialog, int which) { - //设置同步账户 - setSyncAccount(itemMapping[which].toString()); - dialog.dismiss(); - refreshUI(); - } - }); - } - // 从XML布局文件中加载添加账户视图 + items[index++] = account.name; + } + // 设置单选列表项 + dialogBuilder.setSingleChoiceItems(items, checkedItem, + new DialogInterface.OnClickListener() { + public void onClick(DialogInterface dialog, int which) { + // 设置同步账户 + setSyncAccount(itemMapping[which].toString()); + dialog.dismiss(); + refreshUI(); + } + }); + } + + // 从布局文件中加载添加账户视图 View addAccountView = LayoutInflater.from(this).inflate(R.layout.add_account_text, null); dialogBuilder.setView(addAccountView); - // 显示警告对话框 + // 显示对话框 final AlertDialog dialog = dialogBuilder.show(); addAccountView.setOnClickListener(new View.OnClickListener() { public void onClick(View v) { - // 添加账户 + // 标记添加了新账户 mHasAddedAccount = true; + // 启动添加账户的设置活动 Intent intent = new Intent("android.settings.ADD_ACCOUNT_SETTINGS"); intent.putExtra(AUTHORITIES_FILTER_KEY, new String[] { "gmail-ls" @@ -299,14 +317,12 @@ public class NotesPreferenceActivity extends PreferenceActivity { }); } -/** - * 显示更改账户的确认警告对话框。 - */ +// 定义一个私有方法 showChangeAccountConfirmAlertDialog,用于显示更改账户确认对话框 private void showChangeAccountConfirmAlertDialog() { - // 创建警告对话框的构建器 + // 创建一个对话框构建器 AlertDialog.Builder dialogBuilder = new AlertDialog.Builder(this); - // 从XML布局文件中加载标题视图 + // 从布局文件中加载自定义标题视图 View titleView = LayoutInflater.from(this).inflate(R.layout.account_dialog_title, null); TextView titleTextView = (TextView) titleView.findViewById(R.id.account_dialog_title); titleTextView.setText(getString(R.string.preferences_dialog_change_account_title, @@ -315,56 +331,57 @@ private void showChangeAccountConfirmAlertDialog() { subtitleTextView.setText(getString(R.string.preferences_dialog_change_account_warn_msg)); dialogBuilder.setCustomTitle(titleView); - // 创建菜单项 + // 创建菜单项数组 CharSequence[] menuItemArray = new CharSequence[] { getString(R.string.preferences_menu_change_account), getString(R.string.preferences_menu_remove_account), getString(R.string.preferences_menu_cancel) }; + // 设置菜单项点击监听器 dialogBuilder.setItems(menuItemArray, new DialogInterface.OnClickListener() { public void onClick(DialogInterface dialog, int which) { - // 处理菜单项点击事件 if (which == 0) { + // 显示选择账户的对话框 showSelectAccountAlertDialog(); } else if (which == 1) { + // 移除同步账户 removeSyncAccount(); refreshUI(); } } }); + // 显示对话框 dialogBuilder.show(); } - -/** - * 获取Google账户。 - */ +// 定义一个私有方法 getGoogleAccounts,用于获取谷歌账户信息 private Account[] getGoogleAccounts() { - // 获取账户管理器 + // 获取 AccountManager 实例 AccountManager accountManager = AccountManager.get(this); - // 获取Google账户 + // 返回类型为 "com.google" 的账户数组 return accountManager.getAccountsByType("com.google"); } -/** - * 设置同步账户。 - */ +// 定义一个私有方法 setSyncAccount,用于设置同步账户 private void setSyncAccount(String account) { - // 如果同步账户不为空且与当前账户不同 + // 如果新账户与当前设置的同步账户不同 if (!getSyncAccountName(this).equals(account)) { - // 获取共享首选项 + // 获取共享偏好设置 SharedPreferences settings = getSharedPreferences(PREFERENCE_NAME, Context.MODE_PRIVATE); + // 获取编辑器 SharedPreferences.Editor editor = settings.edit(); + // 如果账户不为空,则设置同步账户名称,否则设置为空字符串 if (account != null) { editor.putString(PREFERENCE_SYNC_ACCOUNT_NAME, account); } else { editor.putString(PREFERENCE_SYNC_ACCOUNT_NAME, ""); } + // 提交编辑 editor.commit(); - // 清除最后同步时间 + // 清除最后一次同步时间 setLastSyncTime(this, 0); - // 清除本地gtask相关信息 + // 清除本地与 GTask 相关的信息 new Thread(new Runnable() { public void run() { ContentValues values = new ContentValues(); @@ -374,29 +391,31 @@ private void setSyncAccount(String account) { } }).start(); - // 提示用户设置账户成功 + // 显示设置账户成功的提示信息 Toast.makeText(NotesPreferenceActivity.this, getString(R.string.preferences_toast_success_set_accout, account), Toast.LENGTH_SHORT).show(); } } -/** - * 移除同步账户。 - */ +// 定义一个私有方法 removeSyncAccount,用于移除同步账户 private void removeSyncAccount() { - // 获取共享首选项 + // 获取共享偏好设置 SharedPreferences settings = getSharedPreferences(PREFERENCE_NAME, Context.MODE_PRIVATE); + // 获取编辑器 SharedPreferences.Editor editor = settings.edit(); + // 如果存在同步账户名称,则移除 if (settings.contains(PREFERENCE_SYNC_ACCOUNT_NAME)) { editor.remove(PREFERENCE_SYNC_ACCOUNT_NAME); } + // 如果存在最后一次同步时间,则移除 if (settings.contains(PREFERENCE_LAST_SYNC_TIME)) { editor.remove(PREFERENCE_LAST_SYNC_TIME); } + // 提交编辑 editor.commit(); - // 清除本地gtask相关信息 + // 清除本地与 GTask 相关的信息 new Thread(new Runnable() { public void run() { ContentValues values = new ContentValues(); @@ -407,65 +426,69 @@ private void removeSyncAccount() { }).start(); } -/** - * 获取同步账户名称。 - */ +// 定义一个公共静态方法 getSyncAccountName,用于获取同步账户名称 public static String getSyncAccountName(Context context) { + // 获取共享偏好设置 SharedPreferences settings = context.getSharedPreferences(PREFERENCE_NAME, Context.MODE_PRIVATE); + // 返回同步账户名称,如果不存在则返回空字符串 return settings.getString(PREFERENCE_SYNC_ACCOUNT_NAME, ""); } -/** - * 设置最后同步时间。 - */ +// 定义一个公共静态方法 setLastSyncTime,用于设置最后一次同步时间 public static void setLastSyncTime(Context context, long time) { + // 获取共享偏好设置 SharedPreferences settings = context.getSharedPreferences(PREFERENCE_NAME, Context.MODE_PRIVATE); + // 获取编辑器 SharedPreferences.Editor editor = settings.edit(); + // 设置最后一次同步时间 editor.putLong(PREFERENCE_LAST_SYNC_TIME, time); + // 提交编辑 editor.commit(); } -/** - * 获取最后同步时间。 - */ +// 定义一个公共静态方法 getLastSyncTime,用于获取最后一次同步时间 public static long getLastSyncTime(Context context) { + // 获取共享偏好设置 SharedPreferences settings = context.getSharedPreferences(PREFERENCE_NAME, Context.MODE_PRIVATE); + // 返回最后一次同步时间,如果不存在则返回 0 return settings.getLong(PREFERENCE_LAST_SYNC_TIME, 0); } -/** - * GTask接收器,用于接收GTask服务的广播。 - */ +// 定义一个内部类 GTaskReceiver,继承自 BroadcastReceiver,用于接收 GTask 相关的广播 private class GTaskReceiver extends BroadcastReceiver { @Override public void onReceive(Context context, Intent intent) { - // 刷新UI + // 刷新 UI 界面 refreshUI(); + // 如果正在同步 if (intent.getBooleanExtra(GTaskSyncService.GTASK_SERVICE_BROADCAST_IS_SYNCING, false)) { - // 获取同步状态视图 + // 获取显示同步状态的文本视图 TextView syncStatus = (TextView) findViewById(R.id.prefenerece_sync_status_textview); + // 设置同步状态文本 syncStatus.setText(intent .getStringExtra(GTaskSyncService.GTASK_SERVICE_BROADCAST_PROGRESS_MSG)); } - } } -/** - * 处理选项菜单项点击事件。 - */ +// 重写 onOptionsItemSelected 方法,用于处理菜单项点击事件 public boolean onOptionsItemSelected(MenuItem item) { + // 根据菜单项的 ID 进行处理 switch (item.getItemId()) { case android.R.id.home: + // 创建一个 Intent,用于启动 NotesListActivity Intent intent = new Intent(this, NotesListActivity.class); + // 添加标志,清除任务栈顶部的活动 intent.addFlags(Intent.FLAG_ACTIVITY_CLEAR_TOP); + // 启动活动 startActivity(intent); return true; default: return false; } } +