|
|
|
@ -0,0 +1,368 @@
|
|
|
|
|
/*
|
|
|
|
|
* 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) {
|
|
|
|
|
// 省略部分代码...
|
|
|
|
|
}
|
|
|
|
|
}
|