You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
git-xiaomibianqian/src/ui/NotesListActivity.java

955 lines
43 KiB

This file contains ambiguous Unicode characters!

This file contains ambiguous Unicode characters that may be confused with others in your current locale. If your use case is intentional and legitimate, you can safely ignore this warning. Use the Escape button to highlight these characters.

/*
* Copyright (c) 2010-2011, The MiCode Open Source Community (www.micode.net)
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package net.micode.notes.ui;
import android.app.Activity;
import android.app.AlertDialog;
import android.app.Dialog;
import android.appwidget.AppWidgetManager;
import android.content.AsyncQueryHandler;
import android.content.ContentResolver;
import android.content.ContentValues;
import android.content.Context;
import android.content.DialogInterface;
import android.content.Intent;
import android.content.SharedPreferences;
import android.database.Cursor;
import android.os.AsyncTask;
import android.os.Bundle;
import android.preference.PreferenceManager;
import android.text.Editable;
import android.text.TextUtils;
import android.text.TextWatcher;
import android.util.Log;
import android.view.ActionMode;
import android.view.ContextMenu;
import android.view.ContextMenu.ContextMenuInfo;
import android.view.Display;
import android.view.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;
public class NotesListActivity extends Activity implements OnClickListener, OnItemLongClickListener {//在类的声明中通过关键字extends来创建一个类的子类。一个类通过关键字implements声明自己使用一个或者多个接口。
private static final int FOLDER_NOTE_LIST_QUERY_TOKEN = 0;//声明并赋值一些不可更改的私有属性
private static final int FOLDER_LIST_QUERY_TOKEN = 1;//查询记号
private static final int MENU_FOLDER_DELETE = 0;//删除菜单文件
private static final int MENU_FOLDER_VIEW = 1;//菜单中的查看文件夹对应值
private static final int MENU_FOLDER_CHANGE_NAME = 2;//修改菜单文件名称功能
private static final String PREFERENCE_ADD_INTRODUCTION = "net.micode.notes.introduction";//第一次使用小米便签时的介绍语句
private enum ListEditState {//列表编辑状态类
NOTE_LIST, SUB_FOLDER, CALL_RECORD_FOLDER
};
private ListEditState mState;
private BackgroundQueryHandler mBackgroundQueryHandler;//后台疑问处理功能
private NotesListAdapter mNotesListAdapter;//便签列表配适器
private ListView mNotesListView;//主界面的视图
private Button mAddNewNote;//最下方添加便签的按钮
private boolean mDispatch;//是否调度的判断变量
private int mOriginY;//首次触摸时屏幕上的垂直距离y值
private int mDispatchY;//重新调度时的触摸的在屏幕上的垂直距离
private TextView mTitleBar;//子文件夹下标头
private long mCurrentFolderId;//当前文件夹的ID
private ContentResolver mContentResolver;//提供内容分析
private ModeCallback mModeCallBack;//返回调用方法
private static final String TAG = "NotesListActivity";//名称(可用于日志文件调试)
public static final int NOTES_LISTVIEW_SCROLL_RATE = 30;//列表滚动速度
private NoteItemData mFocusNoteDataItem;//光标指向的物件的数据内容
private static final String NORMAL_SELECTION = NoteColumns.PARENT_ID + "=?";//用于表明处于非父文件夹的其他文件夹下
private static final String ROOT_FOLDER_SELECTION = "(" + NoteColumns.TYPE + "<>"//用于表明处于父文件夹下(主列表)
+ Notes.TYPE_SYSTEM + " AND " + NoteColumns.PARENT_ID + "=?)" + " OR ("
+ NoteColumns.ID + "=" + Notes.ID_CALL_RECORD_FOLDER + " AND "
+ NoteColumns.NOTES_COUNT + ">0)";
private final static int REQUEST_CODE_OPEN_NODE = 102;//打开某个便签时,创建活动的请求码
private final static int REQUEST_CODE_NEW_NODE = 103;//打开某个便签时,创建活动的请求码
@Override//功能为Activity生命周期开始时调用的函数用来保存读取状态设置界面
protected void onCreate(Bundle savedInstanceState) {//创建类
super.onCreate(savedInstanceState);//引用父类对象
setContentView(R.layout.note_list);//设置内容的视图
initResources();//初始化类的资源
/**
* Insert an introduction when user firstly use this application
*/
setAppInfoFromRawRes();//当用户第一次访问APP时提供相关信息
}
@Override// 代表执行这个方法时,重写并调用了父类的方法
protected void onActivityResult(int requestCode, int resultCode, Intent data) {//返回一些子模块完成的数据交给主Activity处理
if (resultCode == RESULT_OK//当被销毁的活动是打开便签或者新建便签且结果码匹配时,把从编辑界面的光标数据删除
&& (requestCode == REQUEST_CODE_OPEN_NODE || requestCode == REQUEST_CODE_NEW_NODE)) {
mNotesListAdapter.changeCursor(null);
} else {//如果条件不满足则将数据返回给父类即调用父类的onActivityResult()。
super.onActivityResult(requestCode, resultCode, data);//调用父类Activity的onActivityResult方法 将数据返回给父类处理
}
}
private void setAppInfoFromRawRes() {//利用原始资源文件设置APP的相关信息
SharedPreferences sp = PreferenceManager.getDefaultSharedPreferences(this);//保存配置参数
if (!sp.getBoolean(PREFERENCE_ADD_INTRODUCTION, false)) {//判断偏好,增加说明
StringBuilder sb = new StringBuilder();//读取原生资源信息
InputStream in = null;//输入流初始设置为空
try {//使用getResources获取资源后,以openRawResource方法不带后缀的资源文件名打开这个文件。
in = getResources().openRawResource(R.raw.introduction);//加载Welcome to use MIUI notes
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) {//不断在buf中读取数据放入sb里
sb.append(buf, 0, len);//使用append函数在指定元素的结尾插入内容
}
} else {
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) {
// 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();//更新保存note的信息
} else {
Log.e(TAG, "Save introduction note error");
return;
}//若便签保存不成功,则把错误信息打印到日志里
}
}
@Override
protected void onStart() {//代表activity生命周期开始
super.onStart();//调用父类,启动活动
startAsyncNotesListQuery();//同步列表中的便签信息
}
private void initResources() {//初始化资源
mContentResolver = this.getContentResolver();//获取应用程序的数据
mBackgroundQueryHandler = new BackgroundQueryHandler(this.getContentResolver());//动态创建后台请求处理器的实例
mCurrentFolderId = Notes.ID_ROOT_FOLDER;//设置当前文件夹ID是根目录ID
mNotesListView = (ListView) findViewById(R.id.notes_list);//根据R文件中的id值查询到相应的View,然后返回
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);//在activity中要获取该按钮
mAddNewNote.setOnClickListener(this);//点击监听
mAddNewNote.setOnTouchListener(new NewNoteOnTouchListener());//屏幕监听
mDispatch = false;//用于新建便签模块
mDispatchY = 0;//初始y值设置为0
mOriginY = 0;//加载文件夹下的标头资源
mTitleBar = (TextView) findViewById(R.id.tv_title_bar);//加载文件夹下的标头资源
mState = ListEditState.NOTE_LIST;//设置状态为主界面
mModeCallBack = new ModeCallback();//对便签的方法调用,包括删除和移动
}
private class ModeCallback implements ListView.MultiChoiceModeListener, OnMenuItemClickListener {//implements声明自己使用一个或多个接口
private DropdownMenu mDropDownMenu;//下拉菜单
private ActionMode mActionMode;//动作方式
private MenuItem mMoveMenu;//移动菜单
public boolean onCreateActionMode(ActionMode mode, Menu menu) {//根据菜单和动作创建新动作
getMenuInflater().inflate(R.menu.note_list_options, menu);//配置menu的布局
menu.findItem(R.id.delete).setOnMenuItemClickListener(this);//为便签删除模块设定监听器
mMoveMenu = menu.findItem(R.id.move);//获取菜单项目
if (mFocusNoteDataItem.getParentId() == Notes.ID_CALL_RECORD_FOLDER//若父类id在文件夹中保存或者用户文件数量为零设置移动菜单为不可见否者设为可见
|| 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);//隐藏了新增便签按钮
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);//为view添加dropDownMenu
mDropDownMenu.setOnDropdownMenuItemClickListener(new PopupMenu.OnMenuItemClickListener(){
public boolean onMenuItemClick(MenuItem item) {//点击菜单时,设置为全选并更新菜单
mNotesListAdapter.selectAll(!mNotesListAdapter.isAllSelected());//对应下拉菜单里的全选按键
updateMenu();//更新菜单
return true;
}
});
return true;
}
private void updateMenu() {//用于用户在进行了勾选之后,更新列表
int selectedCount = mNotesListAdapter.getSelectedCount();//获取被勾选的条目数量
// Update dropdown menu
String format = getResources().getString(R.string.menu_select_title, selectedCount);//从原始资源中读取信息更改下拉菜单内容
mDropDownMenu.setTitle(format);//更改标题
MenuItem item = mDropDownMenu.findItem(R.id.action_select_all);//全选操作
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);
}
}
}
public boolean onPrepareActionMode(ActionMode mode, Menu menu) {//准备活动模式
// TODO Auto-generated method stub
return false;
}
public boolean onActionItemClicked(ActionMode mode, MenuItem item) {//菜单动作触发标记
// TODO Auto-generated method stub
return false;
}
public void onDestroyActionMode(ActionMode mode) {//销毁动作模式,设置便签可见
mNotesListAdapter.setChoiceMode(false);//设置笔记列表适配器选择方式
mNotesListView.setLongClickable(true);//长按操作
mAddNewNote.setVisibility(View.VISIBLE);//设置新建的笔记为可见
}
public void finishActionMode() {
mActionMode.finish();
}//动作模式结束,菜单勾选状态改变
public void onItemCheckedStateChanged(ActionMode mode, int position, long id,//勾选状态改变时,更改勾选标志,更新菜单
boolean checked) {//点击菜单选项触发操作
mNotesListAdapter.setCheckedItem(position, checked);//改变勾选的状态
updateMenu();//更新菜单
}
public boolean onMenuItemClick(MenuItem item) {//判断菜单是否被点击
if (mNotesListAdapter.getSelectedCount() == 0) {// 当勾选数为零时(即未点击),创建文本并显示
Toast.makeText(NotesListActivity.this, getString(R.string.menu_select_none),
Toast.LENGTH_SHORT).show();
return true;
}
switch (item.getItemId()) {//根据id号判断是删除还是移动
case R.id.delete://删除菜单项
AlertDialog.Builder builder = new AlertDialog.Builder(NotesListActivity.this);//警告对话框
builder.setTitle(getString(R.string.alert_title_delete));//设置“删除选中的便签”的title
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:
return false;
}
return true;
}
}
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();
/**
* Minus TitleBar's height
*/
if (mState == ListEditState.SUB_FOLDER) {
eventY -= mTitleBar.getHeight();
start -= mTitleBar.getHeight();
}
/**
* HACKME:When click the transparent part of "New Note" button, dispatch
* the event to the list view behind this button. The transparent part of
* "New Note" button could be expressed by formula y=-0.12x+94Unit:pixel
* and the line top of the button. The coordinate based on left of the "New
* Note" button. The 94 represents maximum height of the transparent part.
* Notice that, if the background of the button changes, the formula should
* also change. This is very bad, just for the UI designer's strong requirement.
*/
if (event.getY() < (event.getX() * (-0.12) + 94)) {
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 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 final class BackgroundQueryHandler extends AsyncQueryHandler {
public BackgroundQueryHandler(ContentResolver contentResolver) {
super(contentResolver);
}
@Override
protected void onQueryComplete(int token, Object cookie, Cursor cursor) {
switch (token) {
case FOLDER_NOTE_LIST_QUERY_TOKEN:
mNotesListAdapter.changeCursor(cursor);
break;
case FOLDER_LIST_QUERY_TOKEN:
if (cursor != null && cursor.getCount() > 0) {
showFolderListMenu(cursor);
} else {
Log.e(TAG, "Query folder failed");
}
break;
default:
return;
}
}
}
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();
}
});
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<Void, Void, HashSet<AppWidgetAttribute>>() {
protected HashSet<AppWidgetAttribute> doInBackground(Void... unused) {
HashSet<AppWidgetAttribute> widgets = mNotesListAdapter.getSelectedWidget();
if (!isSyncMode()) {
// if not synced, delete notes directly
if (DataUtils.batchDeleteNotes(mContentResolver, mNotesListAdapter
.getSelectedItemIds())) {
} else {
Log.e(TAG, "Delete notes error, should not happens");
}
} else {
// in sync mode, we'll move the deleted note into the trash
// folder
if (!DataUtils.batchMoveToFolder(mContentResolver, mNotesListAdapter
.getSelectedItemIds(), Notes.ID_TRASH_FOLER)) {
Log.e(TAG, "Move notes to trash folder error, should not happens");
}
}
return widgets;
}
@Override
protected void onPostExecute(HashSet<AppWidgetAttribute> widgets) {
if (widgets != null) {
for (AppWidgetAttribute widget : widgets) {
if (widget.widgetId != AppWidgetManager.INVALID_APPWIDGET_ID
&& widget.widgetType != Notes.TYPE_WIDGET_INVALIDE) {
updateWidget(widget.widgetId, widget.widgetType);
}
}
}
mModeCallBack.finishActionMode();
}
}.execute();
}
private void deleteFolder(long folderId) {
if (folderId == Notes.ID_ROOT_FOLDER) {
Log.e(TAG, "Wrong folder id, should not happen " + folderId);
return;
}
HashSet<Long> ids = new HashSet<Long>();
ids.add(folderId);
HashSet<AppWidgetAttribute> widgets = DataUtils.getFolderNoteWidget(mContentResolver,
folderId);
if (!isSyncMode()) {
// if not synced, delete folder directly
DataUtils.batchDeleteNotes(mContentResolver, ids);
} else {
// in sync mode, we'll move the deleted folder into the trash folder
DataUtils.batchMoveToFolder(mContentResolver, ids, Notes.ID_TRASH_FOLER);
}
if (widgets != null) {
for (AppWidgetAttribute widget : widgets) {
if (widget.widgetId != AppWidgetManager.INVALID_APPWIDGET_ID
&& widget.widgetType != Notes.TYPE_WIDGET_INVALIDE) {
updateWidget(widget.widgetId, widget.widgetType);
}
}
}
}
private void openNode(NoteItemData data) {
Intent intent = new Intent(this, NoteEditActivity.class);
intent.setAction(Intent.ACTION_VIEW);
intent.putExtra(Intent.EXTRA_UID, data.getId());
this.startActivityForResult(intent, REQUEST_CODE_OPEN_NODE);
}
private void openFolder(NoteItemData data) {
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;
}
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 void onClick(View v) {
switch (v.getId()) {
case R.id.btn_new_note:
createNewNote();
break;
default:
break;
}
}
private void showSoftInput() {
InputMethodManager inputMethodManager = (InputMethodManager) getSystemService(Context.INPUT_METHOD_SERVICE);
if (inputMethodManager != null) {
inputMethodManager.toggleSoftInput(InputMethodManager.SHOW_FORCED, 0);
}
}
private void hideSoftInput(View view) {
InputMethodManager inputMethodManager = (InputMethodManager) getSystemService(Context.INPUT_METHOD_SERVICE);
inputMethodManager.hideSoftInputFromWindow(view.getWindowToken(), 0);
}
private void showCreateOrModifyFolderDialog(final boolean create) {
final AlertDialog.Builder builder = new AlertDialog.Builder(this);
View view = LayoutInflater.from(this).inflate(R.layout.dialog_edit_text, null);
final EditText etName = (EditText) view.findViewById(R.id.et_foler_name);
showSoftInput();
if (!create) {
if (mFocusNoteDataItem != null) {
etName.setText(mFocusNoteDataItem.getSnippet());
builder.setTitle(getString(R.string.menu_folder_change_name));
} else {
Log.e(TAG, "The long click data item is null");
return;
}
} else {
etName.setText("");
builder.setTitle(this.getString(R.string.menu_create_folder));
}
builder.setPositiveButton(android.R.string.ok, null);
builder.setNegativeButton(android.R.string.cancel, new DialogInterface.OnClickListener() {
public void onClick(DialogInterface dialog, int which) {
hideSoftInput(etName);
}
});
final Dialog dialog = builder.setView(view).show();
final Button positive = (Button)dialog.findViewById(android.R.id.button1);
positive.setOnClickListener(new OnClickListener() {
public void onClick(View v) {
hideSoftInput(etName);
String name = etName.getText().toString();
if (DataUtils.checkVisibleFolderName(mContentResolver, name)) {
Toast.makeText(NotesListActivity.this, getString(R.string.folder_exist, name),
Toast.LENGTH_LONG).show();
etName.setSelection(0, etName.length());
return;
}
if (!create) {
if (!TextUtils.isEmpty(name)) {
ContentValues values = new ContentValues();
values.put(NoteColumns.SNIPPET, name);
values.put(NoteColumns.TYPE, Notes.TYPE_FOLDER);
values.put(NoteColumns.LOCAL_MODIFIED, 1);
mContentResolver.update(Notes.CONTENT_NOTE_URI, values, NoteColumns.ID
+ "=?", new String[] {
String.valueOf(mFocusNoteDataItem.getId())
});
}
} else if (!TextUtils.isEmpty(name)) {
ContentValues values = new ContentValues();
values.put(NoteColumns.SNIPPET, name);
values.put(NoteColumns.TYPE, Notes.TYPE_FOLDER);
mContentResolver.insert(Notes.CONTENT_NOTE_URI, values);
}
dialog.dismiss();
}
});
if (TextUtils.isEmpty(etName.getText())) {
positive.setEnabled(false);
}
/**
* When the name edit text is null, disable the positive button
*/
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);
}
}
public void afterTextChanged(Editable s) {
// TODO Auto-generated method stub
}
});
}
@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;
}
}
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;
}
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);
}
}
};
@Override
public void onContextMenuClosed(Menu menu) {
if (mNotesListView != null) {
mNotesListView.setOnCreateContextMenuListener(null);
}
super.onContextMenuClosed(menu);
}
@Override
public boolean onContextItemSelected(MenuItem item) {
if (mFocusNoteDataItem == null) {
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 true;
}
@Override
public boolean onPrepareOptionsMenu(Menu menu) {
menu.clear();
if (mState == ListEditState.NOTE_LIST) {
getMenuInflater().inflate(R.menu.note_list, menu);
// set sync or sync_cancel
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);
}
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;
}
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;
}
return true;
}
@Override
public boolean onSearchRequested() {
startSearch(null, false, null /* appData */, false);
return true;
}
private void exportNoteToText() {
final BackupUtils backup = BackupUtils.getInstance(NotesListActivity.this);
new AsyncTask<Void, Void, Integer>() {
@Override
protected Integer doInBackground(Void... unused) {
return backup.exportToText();
}
@Override
protected void onPostExecute(Integer result) {
if (result == BackupUtils.STATE_SD_CARD_UNMOUONTED) {
AlertDialog.Builder builder = new AlertDialog.Builder(NotesListActivity.this);
builder.setTitle(NotesListActivity.this
.getString(R.string.failed_sdcard_export));
builder.setMessage(NotesListActivity.this
.getString(R.string.error_sdcard_unmounted));
builder.setPositiveButton(android.R.string.ok, null);
builder.show();
} else if (result == BackupUtils.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 boolean isSyncMode() {
return NotesPreferenceActivity.getSyncAccountName(this).trim().length() > 0;
}
private void startPreferenceActivity() {
Activity from = getParent() != null ? getParent() : this;
Intent intent = new Intent(from, NotesPreferenceActivity.class);
from.startActivityIfNeeded(intent, -1);
}
private class OnListItemClickListener implements OnItemClickListener {
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;
}
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;
}
}
}
}
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);
}
}
return false;
}
}