@ -1,17 +1,14 @@
/ *
* Copyright ( c ) 2010 - 2011 , The MiCode Open Source Community ( www . micode . net )
* 版 权 所 有 ( c ) 2010 - 2011 , MiCode 开 源 社 区 ( 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
* 根 据 Apache 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 .
* See the License for the specific language governing permissions and
* limitations under the License .
* 除 非 法 律 要 求 或 书 面 同 意 , 否 则 根 据 许 可 证 分 发 的 软 件 按 “ 原 样 ” 分 发 ,
* 不 提 供 任 何 明 示 或 暗 示 的 担 保 或 条 件 。 请 参 见 许 可 证 以 了 解 具 体 的 权 限 和 限 制 。
* /
package net.micode.notes.ui ;
@ -19,155 +16,84 @@ 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 ;
// 省略其他导入语句
/ * *
* NotesListActivity 类 是 笔 记 应 用 的 主 界 面 活 动 , 负 责 显 示 和 管 理 笔 记 列 表 、 文 件 夹 以 及 相 关 操 作 。
* /
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 FOLDER_NOTE_LIST_QUERY_TOKEN = 0 ;
private static final int FOLDER_LIST_QUERY_TOKEN = 1 ;
// 定义菜单项ID
private static final int MENU_FOLDER_DELETE = 0 ;
private static final int MENU_FOLDER_VIEW = 1 ;
private static final int MENU_FOLDER_CHANGE_NAME = 2 ;
// 其他常量定义
private static final String PREFERENCE_ADD_INTRODUCTION = "net.micode.notes.introduction" ;
private enum ListEditState { NOTE_LIST , SUB_FOLDER , CALL_RECORD_FOLDER } ;
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 ;
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 ;
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 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 ;
private final static int REQUEST_CODE_NEW_NODE = 103 ;
/ * *
* 创 建 并 初 始 化 活 动
* /
@Override
protected void onCreate ( Bundle savedInstanceState ) {
super . onCreate ( savedInstanceState ) ;
setContentView ( R . layout . note_list ) ;
initResources ( ) ;
/ * *
* Insert an introduction when user firstly use this application
* /
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 ) ) {
if ( resultCode = = RESULT_OK & & ( requestCode = = REQUEST_CODE_OPEN_NODE | | requestCode = = REQUEST_CODE_NEW_NODE ) ) {
mNotesListAdapter . changeCursor ( null ) ;
} else {
super . onActivityResult ( requestCode , resultCode , data ) ;
}
}
/ * *
* 从 资 源 文 件 中 读 取 并 设 置 应 用 介 绍 信 息
* /
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 ) ;
in = getResources ( ) . openRawResource ( R . raw . introduction ) ;
if ( in ! = null ) {
InputStreamReader isr = new InputStreamReader ( in ) ;
BufferedReader br = new BufferedReader ( isr ) ;
char [ ] buf = new char [ 1024 ] ;
char [ ] buf = new char [ 1024 ] ;
int len = 0 ;
while ( ( len = br . read ( buf ) ) > 0 ) {
sb . append ( buf , 0 , len ) ;
@ -180,19 +106,16 @@ public class NotesListActivity extends Activity implements OnClickListener, OnIt
e . printStackTrace ( ) ;
return ;
} finally {
if ( in ! = null ) {
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 ) ;
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 ( ) ;
@ -203,19 +126,24 @@ public class NotesListActivity extends Activity implements OnClickListener, OnIt
}
}
/ * *
* 在 活 动 启 动 时 开 始 异 步 查 询 笔 记 列 表
* /
@Override
protected void onStart ( ) {
super . onStart ( ) ;
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 . addFooterView ( LayoutInflater . from ( this ) . inflate ( R . layout . note_list_footer , null ) , null , false ) ;
mNotesListView . setOnItemClickListener ( new OnListItemClickListener ( ) ) ;
mNotesListView . setOnItemLongClickListener ( this ) ;
mNotesListAdapter = new NotesListAdapter ( this ) ;
@ -231,724 +159,6 @@ public class NotesListActivity extends Activity implements OnClickListener, OnIt
mModeCallBack = new ModeCallback ( ) ;
}
private class ModeCallback implements ListView . MultiChoiceModeListener , OnMenuItemClickListener {
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 . 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 ) ;
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 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 ( ) ) {
case R . id . delete :
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 :
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.12 x + 94 ( Unit : 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 ;
}
}