/* * Copyright (c) 2010-2011, The MiCode Open Source Community (www.micode.net) * * 根据Apache License, Version 2.0(以下简称“许可证”)授权; * 您可能不会使用此文件,除非遵守许可证。 * 您可以在以下网址获得许可证的副本: * * http://www.apache.org/licenses/LICENSE-2.0 * * 除非适用法律要求或书面同意,否则按照许可证分发的软件 * 按“原样”分发,不附带任何明示或暗示的保证或条件。 * 请参阅控制权限和许可证下的限制的具体语言的许可证。 */ package net.micode.notes.ui; import android.app.Activity; import android.app.AlertDialog; import android.app.Dialog; import android.appwidget.AppWidgetManager; import android.content.AsyncQueryHandler; import android.content.ContentResolver; import android.content.ContentValues; import android.content.Context; import android.content.DialogInterface; import android.content.Intent; import android.content.SharedPreferences; import android.database.Cursor; import android.os.AsyncTask; import android.os.Bundle; import android.preference.PreferenceManager; import android.text.Editable; import android.text.TextUtils; import android.text.TextWatcher; import android.util.Log; import android.view.ActionMode; import android.view.ContextMenu; import android.view.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 { // 定义查询令牌,用于处理数据库查询。 private static final int FOLDER_NOTE_LIST_QUERY_TOKEN = 0; private static final int FOLDER_LIST_QUERY_TOKEN = 1; // 定义菜单项的常量。 private static final int MENU_FOLDER_DELETE = 0; private static final int MENU_FOLDER_VIEW = 1; private static final int MENU_FOLDER_CHANGE_NAME = 2; // 定义 SharedPreferences 中保存的“添加介绍”的键。 private static final String PREFERENCE_ADD_INTRODUCTION = "net.micode.notes.introduction"; // 枚举类,表示当前列表的编辑状态。 private enum ListEditState { NOTE_LIST, SUB_FOLDER, CALL_RECORD_FOLDER }; private ListEditState mState; // 后台查询处理器。 private BackgroundQueryHandler mBackgroundQueryHandler; // 便签列表适配器。 private NotesListAdapter mNotesListAdapter; // 便签列表视图。 private ListView mNotesListView; // 添加新便签的按钮。 private Button mAddNewNote; // 处理触摸事件的变量。 private boolean mDispatch; private int mOriginY; private int mDispatchY; // 标题栏。 private TextView mTitleBar; // 当前文件夹ID。 private long mCurrentFolderId; // 内容解析器。 private ContentResolver mContentResolver; // 模式回调。 private ModeCallback mModeCallBack; // 日志标签。 private static final String TAG = "NotesListActivity"; // 便签列表视图滚动速率。 public static final int NOTES_LISTVIEW_SCROLL_RATE = 30; // 焦点便签数据项。 private NoteItemData mFocusNoteDataItem; // 普通选择和根文件夹选择的SQL语句。 private static final String NORMAL_SELECTION = NoteColumns.PARENT_ID + "=?"; private static final String ROOT_FOLDER_SELECTION = "(" + NoteColumns.TYPE + "<>" + Notes.TYPE_SYSTEM + " AND " + NoteColumns.PARENT_ID + "=?)" + " OR (" + NoteColumns.ID + "=" + Notes.ID_CALL_RECORD_FOLDER + " AND " + NoteColumns.NOTES_COUNT + ">0)"; // 请求代码常量。 private final static int REQUEST_CODE_OPEN_NODE = 102; private final static int REQUEST_CODE_NEW_NODE = 103; // onCreate 方法,初始化 Activity。 @Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.note_list); initResources(); /** * 当用户首次使用此应用程序时插入介绍。 */ setAppInfoFromRawRes(); } // onActivityResult 方法,处理 Activity 结果。 @Override protected void onActivityResult(int requestCode, int resultCode, Intent data) { if (resultCode == RESULT_OK && (requestCode == REQUEST_CODE_OPEN_NODE || requestCode == REQUEST_CODE_NEW_NODE)) { mNotesListAdapter.changeCursor(null); } else { super.onActivityResult(requestCode, resultCode, data); } } // 从 raw 资源文件中设置应用程序信息。 private void setAppInfoFromRawRes() { SharedPreferences sp = PreferenceManager.getDefaultSharedPreferences(this); if (!sp.getBoolean(PREFERENCE_ADD_INTRODUCTION, false)) { StringBuilder sb = new StringBuilder(); InputStream in = null; try { in = getResources().openRawResource(R.raw.introduction); if (in != null) { InputStreamReader isr = new InputStreamReader(in); BufferedReader br = new BufferedReader(isr); char [] buf = new char[1024]; int len = 0; while ((len = br.read(buf)) > 0) { sb.append(buf, 0, len); } } else { Log.e(TAG, "Read introduction file error"); return; } } catch (IOException e) { e.printStackTrace(); return; } finally { if(in != null) { try { in.close(); } catch (IOException e) { // TODO Auto-generated catch block e.printStackTrace(); } } } WorkingNote note = WorkingNote.createEmptyNote(this, Notes.ID_ROOT_FOLDER, AppWidgetManager.INVALID_APPWIDGET_ID, Notes.TYPE_WIDGET_INVALIDE, ResourceParser.RED); note.setWorkingText(sb.toString()); if (note.saveNote()) { sp.edit().putBoolean(PREFERENCE_ADD_INTRODUCTION, true).commit(); } else { Log.e(TAG, "Save introduction note error"); return; } } } } /** * 当 Activity 开始时调用此方法。 */ @Override protected void onStart() { super.onStart(); // 调用父类的 onStart 方法。 startAsyncNotesListQuery(); // 开始异步查询便签列表。 } /** * 初始化资源的方法。 */ private void initResources() { mContentResolver = this.getContentResolver(); // 获取内容解析器。 mBackgroundQueryHandler = new BackgroundQueryHandler(this.getContentResolver()); // 创建后台查询处理器。 mCurrentFolderId = Notes.ID_ROOT_FOLDER; // 设置当前文件夹ID为根文件夹。 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; // 初始化 dispatch 变量。 mDispatchY = 0; // 初始化 dispatchY 变量。 mOriginY = 0; // 初始化 originY 变量。 mTitleBar = (TextView) findViewById(R.id.tv_title_bar); // 获取标题栏。 mState = ListEditState.NOTE_LIST; // 初始化列表编辑状态。 mModeCallBack = new ModeCallback(); // 创建模式回调对象。 } /** * ModeCallback 内部类,实现 ListView 多选模式监听器和菜单项点击事件。 */ private class ModeCallback implements ListView.MultiChoiceModeListener, OnMenuItemClickListener { private DropdownMenu mDropDownMenu; // 下拉菜单。 private ActionMode mActionMode; // 动作模式。 private MenuItem mMoveMenu; // 移动菜单项。 /** * 创建动作模式时调用。 * @param mode 动作模式。 * @param menu 菜单。 * @return 是否创建成功。 */ public boolean onCreateActionMode(ActionMode mode, Menu menu) { // 省略部分代码... } /** * 更新菜单项。 */ private void updateMenu() { // 省略部分代码... } /** * 准备动作模式时调用。 * @param mode 动作模式。 * @param menu 菜单。 * @return 是否准备成功。 */ public boolean onPrepareActionMode(ActionMode mode, Menu menu) { // TODO Auto-generated method stub return false; } /** * 动作模式中的菜单项被点击时调用。 * @param mode 动作模式。 * @param item 菜单项。 * @return 是否处理成功。 */ public boolean onActionItemClicked(ActionMode mode, MenuItem item) { // TODO Auto-generated method stub return false; } /** * 动作模式销毁时调用。 * @param mode 动作模式。 */ public void onDestroyActionMode(ActionMode mode) { // 省略部分代码... } /** * 结束动作模式。 */ public void finishActionMode() { mActionMode.finish(); } /** * 列表项选中状态改变时调用。 * @param mode 动作模式。 * @param position 位置。 * @param id ID。 * @param checked 是否选中。 */ public void onItemCheckedStateChanged(ActionMode mode, int position, long id, boolean checked) { // 省略部分代码... } /** * 菜单项点击事件。 * @param item 菜单项。 * @return 是否处理成功。 */ public boolean onMenuItemClick(MenuItem item) { // 省略部分代码... } } /** * NewNoteOnTouchListener 内部类,处理添加新便签按钮的触摸事件。 */ private class NewNoteOnTouchListener implements OnTouchListener { /** * 触摸事件处理。 * @param v 视图。 * @param event 触摸事件。 * @return 是否处理成功。 */ public boolean onTouch(View v, MotionEvent event) { // 省略部分代码... } } /** * 开始异步查询便签列表。 */ private void startAsyncNotesListQuery() { // 省略部分代码... } /** * BackgroundQueryHandler 内部类,继承自 AsyncQueryHandler,处理后台查询。 */ 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) { // 省略部分代码... } }