UserDictionarySettings.java revision f58090d5224fa03e8f4d8a980306952686a152f0
1/** 2 * Copyright (C) 2009 Google Inc. 3 * 4 * Licensed under the Apache License, Version 2.0 (the "License"); you may not 5 * use this file except in compliance with the License. You may obtain a copy 6 * of the License at 7 * 8 * http://www.apache.org/licenses/LICENSE-2.0 9 * 10 * Unless required by applicable law or agreed to in writing, software 11 * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT 12 * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the 13 * License for the specific language governing permissions and limitations 14 * under the License. 15 */ 16 17package com.android.settings; 18 19import com.android.settings.SettingsPreferenceFragment.SettingsDialogFragment; 20 21import android.app.Activity; 22import android.app.AlertDialog; 23import android.app.Dialog; 24import android.app.ListFragment; 25import android.content.Context; 26import android.content.DialogInterface; 27import android.content.Intent; 28import android.database.Cursor; 29import android.os.Bundle; 30import android.provider.UserDictionary; 31import android.text.InputType; 32import android.util.Log; 33import android.view.ContextMenu; 34import android.view.ContextMenu.ContextMenuInfo; 35import android.view.LayoutInflater; 36import android.view.Menu; 37import android.view.MenuInflater; 38import android.view.MenuItem; 39import android.view.View; 40import android.view.ViewGroup; 41import android.view.WindowManager; 42import android.widget.AdapterView.AdapterContextMenuInfo; 43import android.widget.AlphabetIndexer; 44import android.widget.EditText; 45import android.widget.ListAdapter; 46import android.widget.ListView; 47import android.widget.SectionIndexer; 48import android.widget.SimpleCursorAdapter; 49import android.widget.TextView; 50 51import java.util.Locale; 52 53public class UserDictionarySettings extends ListFragment implements DialogCreatable { 54 private static final String TAG = "UserDictionarySettings"; 55 56 private static final String INSTANCE_KEY_DIALOG_EDITING_WORD = "DIALOG_EDITING_WORD"; 57 private static final String INSTANCE_KEY_ADDED_WORD = "DIALOG_ADDED_WORD"; 58 59 private static final String[] QUERY_PROJECTION = { 60 UserDictionary.Words._ID, UserDictionary.Words.WORD 61 }; 62 63 // Either the locale is empty (means the word is applicable to all locales) 64 // or the word equals our current locale 65 private static final String QUERY_SELECTION = UserDictionary.Words.LOCALE + "=? OR " 66 + UserDictionary.Words.LOCALE + " is null"; 67 68 private static final String DELETE_SELECTION = UserDictionary.Words.WORD + "=?"; 69 70 private static final String EXTRA_WORD = "word"; 71 72 private static final int CONTEXT_MENU_EDIT = Menu.FIRST; 73 private static final int CONTEXT_MENU_DELETE = Menu.FIRST + 1; 74 75 private static final int OPTIONS_MENU_ADD = Menu.FIRST; 76 77 private static final int DIALOG_ADD_OR_EDIT = 0; 78 79 /** The word being edited in the dialog (null means the user is adding a word). */ 80 private String mDialogEditingWord; 81 82 private View mView; 83 private Cursor mCursor; 84 85 private boolean mAddedWordAlready; 86 private boolean mAutoReturn; 87 88 private SettingsDialogFragment mDialogFragment; 89 90 @Override 91 public void onCreate(Bundle savedInstanceState) { 92 super.onCreate(savedInstanceState); 93 } 94 95 @Override 96 public View onCreateView(LayoutInflater inflater, ViewGroup container, 97 Bundle savedInstanceState) { 98 mView = inflater.inflate(R.layout.list_content_with_empty_view, container, false); 99 return mView; 100 } 101 102 @Override 103 public void onActivityCreated(Bundle savedInstanceState) { 104 super.onActivityCreated(savedInstanceState); 105 106 mCursor = createCursor(); 107 TextView emptyView = (TextView)mView.findViewById(R.id.empty); 108 emptyView.setText(R.string.user_dict_settings_empty_text); 109 110 final ListView listView = getListView(); 111 listView.setAdapter(createAdapter()); 112 listView.setFastScrollEnabled(true); 113 listView.setEmptyView(emptyView); 114 115 registerForContextMenu(listView); 116 setHasOptionsMenu(true); 117 118 if (savedInstanceState != null) { 119 mDialogEditingWord = savedInstanceState.getString(INSTANCE_KEY_DIALOG_EDITING_WORD); 120 mAddedWordAlready = savedInstanceState.getBoolean(INSTANCE_KEY_ADDED_WORD, false); 121 } 122 } 123 124 @Override 125 public void onResume() { 126 super.onResume(); 127 final Intent intent = getActivity().getIntent(); 128 if (!mAddedWordAlready 129 && intent.getAction().equals("com.android.settings.USER_DICTIONARY_INSERT")) { 130 final String word = intent.getStringExtra(EXTRA_WORD); 131 mAutoReturn = true; 132 if (word != null) { 133 showAddOrEditDialog(word); 134 } 135 } 136 } 137 138 @Override 139 public void onSaveInstanceState(Bundle outState) { 140 super.onSaveInstanceState(outState); 141 outState.putString(INSTANCE_KEY_DIALOG_EDITING_WORD, mDialogEditingWord); 142 outState.putBoolean(INSTANCE_KEY_ADDED_WORD, mAddedWordAlready); 143 } 144 145 private Cursor createCursor() { 146 String currentLocale = Locale.getDefault().toString(); 147 // Case-insensitive sort 148 return getActivity().managedQuery(UserDictionary.Words.CONTENT_URI, QUERY_PROJECTION, 149 QUERY_SELECTION, new String[] { currentLocale }, 150 "UPPER(" + UserDictionary.Words.WORD + ")"); 151 } 152 153 private ListAdapter createAdapter() { 154 return new MyAdapter(getActivity(), 155 android.R.layout.simple_list_item_1, mCursor, 156 new String[] { UserDictionary.Words.WORD }, 157 new int[] { android.R.id.text1 }); 158 } 159 160 @Override 161 public void onListItemClick(ListView l, View v, int position, long id) { 162 getActivity().openContextMenu(v); 163 } 164 165 @Override 166 public void onCreateContextMenu(ContextMenu menu, View v, ContextMenuInfo menuInfo) { 167 if (!(menuInfo instanceof AdapterContextMenuInfo)) return; 168 169 AdapterContextMenuInfo adapterMenuInfo = (AdapterContextMenuInfo) menuInfo; 170 menu.setHeaderTitle(getWord(adapterMenuInfo.position)); 171 menu.add(0, CONTEXT_MENU_EDIT, 0, 172 R.string.user_dict_settings_context_menu_edit_title); 173 menu.add(0, CONTEXT_MENU_DELETE, 0, 174 R.string.user_dict_settings_context_menu_delete_title); 175 } 176 177 @Override 178 public boolean onContextItemSelected(MenuItem item) { 179 ContextMenuInfo menuInfo = item.getMenuInfo(); 180 if (!(menuInfo instanceof AdapterContextMenuInfo)) return false; 181 182 AdapterContextMenuInfo adapterMenuInfo = (AdapterContextMenuInfo) menuInfo; 183 String word = getWord(adapterMenuInfo.position); 184 if (word == null) return true; 185 186 switch (item.getItemId()) { 187 case CONTEXT_MENU_DELETE: 188 deleteWord(word); 189 return true; 190 191 case CONTEXT_MENU_EDIT: 192 showAddOrEditDialog(word); 193 return true; 194 } 195 196 return false; 197 } 198 199 @Override 200 public void onCreateOptionsMenu(Menu menu, MenuInflater inflater) { 201 menu.add(0, OPTIONS_MENU_ADD, 0, R.string.user_dict_settings_add_menu_title) 202 .setIcon(R.drawable.ic_menu_add); 203 } 204 205 @Override 206 public boolean onOptionsItemSelected(MenuItem item) { 207 showAddOrEditDialog(null); 208 return true; 209 } 210 211 private void showAddOrEditDialog(String editingWord) { 212 mDialogEditingWord = editingWord; 213 showDialog(DIALOG_ADD_OR_EDIT); 214 } 215 216 private String getWord(int position) { 217 mCursor.moveToPosition(position); 218 // Handle a possible race-condition 219 if (mCursor.isAfterLast()) return null; 220 221 return mCursor.getString( 222 mCursor.getColumnIndexOrThrow(UserDictionary.Words.WORD)); 223 } 224 225 @Override 226 public Dialog onCreateDialog(int id) { 227 final Activity activity = getActivity(); 228 final View content = activity.getLayoutInflater().inflate(R.layout.dialog_edittext, null); 229 final EditText editText = (EditText) content.findViewById(R.id.edittext); 230 editText.setText(mDialogEditingWord); 231 // No prediction in soft keyboard mode. TODO: Create a better way to disable prediction 232 editText.setInputType(InputType.TYPE_CLASS_TEXT 233 | InputType.TYPE_TEXT_FLAG_AUTO_COMPLETE); 234 235 AlertDialog dialog = new AlertDialog.Builder(activity) 236 .setTitle(mDialogEditingWord != null 237 ? R.string.user_dict_settings_edit_dialog_title 238 : R.string.user_dict_settings_add_dialog_title) 239 .setView(content) 240 .setPositiveButton(android.R.string.ok, new DialogInterface.OnClickListener() { 241 public void onClick(DialogInterface dialog, int which) { 242 onAddOrEditFinished(editText.getText().toString()); 243 if (mAutoReturn) activity.onBackPressed(); 244 }}) 245 .setNegativeButton(android.R.string.cancel, new DialogInterface.OnClickListener() { 246 public void onClick(DialogInterface dialog, int which) { 247 if (mAutoReturn) activity.onBackPressed(); 248 }}) 249 .create(); 250 dialog.getWindow().setSoftInputMode(WindowManager.LayoutParams.SOFT_INPUT_ADJUST_RESIZE | 251 WindowManager.LayoutParams.SOFT_INPUT_STATE_VISIBLE); 252 return dialog; 253 } 254 255 private void showDialog(int dialogId) { 256 if (mDialogFragment != null) { 257 Log.e(TAG, "Old dialog fragment not null!"); 258 } 259 mDialogFragment = new SettingsDialogFragment(this, dialogId); 260 mDialogFragment.show(getActivity().getFragmentManager(), Integer.toString(dialogId)); 261 } 262 263 private void onAddOrEditFinished(String word) { 264 if (mDialogEditingWord != null) { 265 // The user was editing a word, so do a delete/add 266 deleteWord(mDialogEditingWord); 267 } 268 269 // Disallow duplicates 270 deleteWord(word); 271 272 // TODO: present UI for picking whether to add word to all locales, or current. 273 UserDictionary.Words.addWord(getActivity(), word.toString(), 274 250, UserDictionary.Words.LOCALE_TYPE_ALL); 275 if (!mCursor.requery()) { 276 throw new IllegalStateException("can't requery on already-closed cursor."); 277 } 278 mAddedWordAlready = true; 279 } 280 281 private void deleteWord(String word) { 282 getActivity().getContentResolver().delete( 283 UserDictionary.Words.CONTENT_URI, DELETE_SELECTION, new String[] { word }); 284 } 285 286 private static class MyAdapter extends SimpleCursorAdapter implements SectionIndexer { 287 private AlphabetIndexer mIndexer; 288 289 public MyAdapter(Context context, int layout, Cursor c, String[] from, int[] to) { 290 super(context, layout, c, from, to); 291 292 int wordColIndex = c.getColumnIndexOrThrow(UserDictionary.Words.WORD); 293 String alphabet = context.getString(com.android.internal.R.string.fast_scroll_alphabet); 294 mIndexer = new AlphabetIndexer(c, wordColIndex, alphabet); 295 } 296 297 public int getPositionForSection(int section) { 298 return mIndexer.getPositionForSection(section); 299 } 300 301 public int getSectionForPosition(int position) { 302 return mIndexer.getSectionForPosition(position); 303 } 304 305 public Object[] getSections() { 306 return mIndexer.getSections(); 307 } 308 } 309} 310