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 17 package com.android.settings; 18 19 import android.app.Activity; 20 import android.app.AlertDialog; 21 import android.app.Dialog; 22 import android.app.ListFragment; 23 import android.content.ContentResolver; 24 import android.content.Context; 25 import android.content.DialogInterface; 26 import android.content.Intent; 27 import android.database.Cursor; 28 import android.os.Bundle; 29 import android.provider.UserDictionary; 30 import android.text.InputType; 31 import android.text.TextUtils; 32 import android.util.Log; 33 import android.view.LayoutInflater; 34 import android.view.Menu; 35 import android.view.MenuInflater; 36 import android.view.MenuItem; 37 import android.view.View; 38 import android.view.ViewGroup; 39 import android.view.WindowManager; 40 import android.widget.AlphabetIndexer; 41 import android.widget.EditText; 42 import android.widget.ImageView; 43 import android.widget.ListAdapter; 44 import android.widget.ListView; 45 import android.widget.SectionIndexer; 46 import android.widget.SimpleCursorAdapter; 47 import android.widget.TextView; 48 49 import com.android.settings.inputmethod.UserDictionaryAddWordContents; 50 51 import java.util.Locale; 52 53 public class UserDictionarySettings extends ListFragment { 54 private static final String TAG = "UserDictionarySettings"; 55 56 private static final String[] QUERY_PROJECTION = { 57 UserDictionary.Words._ID, UserDictionary.Words.WORD, UserDictionary.Words.SHORTCUT 58 }; 59 60 // The index of the shortcut in the above array. 61 private static final int INDEX_SHORTCUT = 2; 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 = 66 UserDictionary.Words.LOCALE + "=?"; 67 private static final String QUERY_SELECTION_ALL_LOCALES = 68 UserDictionary.Words.LOCALE + " is null"; 69 70 private static final String DELETE_SELECTION_WITH_SHORTCUT = UserDictionary.Words.WORD 71 + "=? AND " + UserDictionary.Words.SHORTCUT + "=?"; 72 private static final String DELETE_SELECTION_WITHOUT_SHORTCUT = UserDictionary.Words.WORD 73 + "=? AND " + UserDictionary.Words.SHORTCUT + " is null OR " 74 + UserDictionary.Words.SHORTCUT + "=''"; 75 76 private static final int OPTIONS_MENU_ADD = Menu.FIRST; 77 78 private Cursor mCursor; 79 80 protected String mLocale; 81 82 @Override onCreate(Bundle savedInstanceState)83 public void onCreate(Bundle savedInstanceState) { 84 super.onCreate(savedInstanceState); 85 } 86 87 @Override onCreateView( LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState)88 public View onCreateView( 89 LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) { 90 return inflater.inflate( 91 com.android.internal.R.layout.preference_list_fragment, container, false); 92 } 93 94 @Override onActivityCreated(Bundle savedInstanceState)95 public void onActivityCreated(Bundle savedInstanceState) { 96 super.onActivityCreated(savedInstanceState); 97 98 final Intent intent = getActivity().getIntent(); 99 final String localeFromIntent = 100 null == intent ? null : intent.getStringExtra("locale"); 101 102 final Bundle arguments = getArguments(); 103 final String localeFromArguments = 104 null == arguments ? null : arguments.getString("locale"); 105 106 final String locale; 107 if (null != localeFromArguments) { 108 locale = localeFromArguments; 109 } else if (null != localeFromIntent) { 110 locale = localeFromIntent; 111 } else { 112 locale = null; 113 } 114 115 mLocale = locale; 116 mCursor = createCursor(locale); 117 TextView emptyView = (TextView) getView().findViewById(android.R.id.empty); 118 emptyView.setText(R.string.user_dict_settings_empty_text); 119 120 final ListView listView = getListView(); 121 listView.setAdapter(createAdapter()); 122 listView.setFastScrollEnabled(true); 123 listView.setEmptyView(emptyView); 124 125 setHasOptionsMenu(true); 126 127 } 128 createCursor(final String locale)129 private Cursor createCursor(final String locale) { 130 // Locale can be any of: 131 // - The string representation of a locale, as returned by Locale#toString() 132 // - The empty string. This means we want a cursor returning words valid for all locales. 133 // - null. This means we want a cursor for the current locale, whatever this is. 134 // Note that this contrasts with the data inside the database, where NULL means "all 135 // locales" and there should never be an empty string. The confusion is called by the 136 // historical use of null for "all locales". 137 // TODO: it should be easy to make this more readable by making the special values 138 // human-readable, like "all_locales" and "current_locales" strings, provided they 139 // can be guaranteed not to match locales that may exist. 140 if ("".equals(locale)) { 141 // Case-insensitive sort 142 return getActivity().managedQuery(UserDictionary.Words.CONTENT_URI, QUERY_PROJECTION, 143 QUERY_SELECTION_ALL_LOCALES, null, 144 "UPPER(" + UserDictionary.Words.WORD + ")"); 145 } else { 146 final String queryLocale = null != locale ? locale : Locale.getDefault().toString(); 147 return getActivity().managedQuery(UserDictionary.Words.CONTENT_URI, QUERY_PROJECTION, 148 QUERY_SELECTION, new String[] { queryLocale }, 149 "UPPER(" + UserDictionary.Words.WORD + ")"); 150 } 151 } 152 createAdapter()153 private ListAdapter createAdapter() { 154 return new MyAdapter(getActivity(), 155 R.layout.user_dictionary_item, mCursor, 156 new String[] { UserDictionary.Words.WORD, UserDictionary.Words.SHORTCUT }, 157 new int[] { android.R.id.text1, android.R.id.text2 }, this); 158 } 159 160 @Override onListItemClick(ListView l, View v, int position, long id)161 public void onListItemClick(ListView l, View v, int position, long id) { 162 final String word = getWord(position); 163 final String shortcut = getShortcut(position); 164 if (word != null) { 165 showAddOrEditDialog(word, shortcut); 166 } 167 } 168 169 @Override onCreateOptionsMenu(Menu menu, MenuInflater inflater)170 public void onCreateOptionsMenu(Menu menu, MenuInflater inflater) { 171 MenuItem actionItem = 172 menu.add(0, OPTIONS_MENU_ADD, 0, R.string.user_dict_settings_add_menu_title) 173 .setIcon(R.drawable.ic_menu_add); 174 actionItem.setShowAsAction(MenuItem.SHOW_AS_ACTION_IF_ROOM | 175 MenuItem.SHOW_AS_ACTION_WITH_TEXT); 176 } 177 178 @Override onOptionsItemSelected(MenuItem item)179 public boolean onOptionsItemSelected(MenuItem item) { 180 if (item.getItemId() == OPTIONS_MENU_ADD) { 181 showAddOrEditDialog(null, null); 182 return true; 183 } 184 return false; 185 } 186 187 /** 188 * Add or edit a word. If editingWord is null, it's an add; otherwise, it's an edit. 189 * @param editingWord the word to edit, or null if it's an add. 190 * @param editingShortcut the shortcut for this entry, or null if none. 191 */ showAddOrEditDialog(final String editingWord, final String editingShortcut)192 private void showAddOrEditDialog(final String editingWord, final String editingShortcut) { 193 final Bundle args = new Bundle(); 194 args.putInt(UserDictionaryAddWordContents.EXTRA_MODE, null == editingWord 195 ? UserDictionaryAddWordContents.MODE_INSERT 196 : UserDictionaryAddWordContents.MODE_EDIT); 197 args.putString(UserDictionaryAddWordContents.EXTRA_WORD, editingWord); 198 args.putString(UserDictionaryAddWordContents.EXTRA_SHORTCUT, editingShortcut); 199 args.putString(UserDictionaryAddWordContents.EXTRA_LOCALE, mLocale); 200 android.preference.PreferenceActivity pa = 201 (android.preference.PreferenceActivity)getActivity(); 202 pa.startPreferencePanel( 203 com.android.settings.inputmethod.UserDictionaryAddWordFragment.class.getName(), 204 args, R.string.user_dict_settings_add_dialog_title, null, null, 0); 205 } 206 getWord(final int position)207 private String getWord(final int position) { 208 if (null == mCursor) return null; 209 mCursor.moveToPosition(position); 210 // Handle a possible race-condition 211 if (mCursor.isAfterLast()) return null; 212 213 return mCursor.getString( 214 mCursor.getColumnIndexOrThrow(UserDictionary.Words.WORD)); 215 } 216 getShortcut(final int position)217 private String getShortcut(final int position) { 218 if (null == mCursor) return null; 219 mCursor.moveToPosition(position); 220 // Handle a possible race-condition 221 if (mCursor.isAfterLast()) return null; 222 223 return mCursor.getString( 224 mCursor.getColumnIndexOrThrow(UserDictionary.Words.SHORTCUT)); 225 } 226 deleteWord(final String word, final String shortcut, final ContentResolver resolver)227 public static void deleteWord(final String word, final String shortcut, 228 final ContentResolver resolver) { 229 if (TextUtils.isEmpty(shortcut)) { 230 resolver.delete( 231 UserDictionary.Words.CONTENT_URI, DELETE_SELECTION_WITHOUT_SHORTCUT, 232 new String[] { word }); 233 } else { 234 resolver.delete( 235 UserDictionary.Words.CONTENT_URI, DELETE_SELECTION_WITH_SHORTCUT, 236 new String[] { word, shortcut }); 237 } 238 } 239 240 private static class MyAdapter extends SimpleCursorAdapter implements SectionIndexer { 241 242 private AlphabetIndexer mIndexer; 243 244 private ViewBinder mViewBinder = new ViewBinder() { 245 246 public boolean setViewValue(View v, Cursor c, int columnIndex) { 247 if (columnIndex == INDEX_SHORTCUT) { 248 final String shortcut = c.getString(INDEX_SHORTCUT); 249 if (TextUtils.isEmpty(shortcut)) { 250 v.setVisibility(View.GONE); 251 } else { 252 ((TextView)v).setText(shortcut); 253 v.setVisibility(View.VISIBLE); 254 } 255 v.invalidate(); 256 return true; 257 } 258 259 return false; 260 } 261 }; 262 MyAdapter(Context context, int layout, Cursor c, String[] from, int[] to, UserDictionarySettings settings)263 public MyAdapter(Context context, int layout, Cursor c, String[] from, int[] to, 264 UserDictionarySettings settings) { 265 super(context, layout, c, from, to); 266 267 if (null != c) { 268 final String alphabet = context.getString( 269 com.android.internal.R.string.fast_scroll_alphabet); 270 final int wordColIndex = c.getColumnIndexOrThrow(UserDictionary.Words.WORD); 271 mIndexer = new AlphabetIndexer(c, wordColIndex, alphabet); 272 } 273 setViewBinder(mViewBinder); 274 } 275 getPositionForSection(int section)276 public int getPositionForSection(int section) { 277 return null == mIndexer ? 0 : mIndexer.getPositionForSection(section); 278 } 279 getSectionForPosition(int position)280 public int getSectionForPosition(int position) { 281 return null == mIndexer ? 0 : mIndexer.getSectionForPosition(position); 282 } 283 getSections()284 public Object[] getSections() { 285 return null == mIndexer ? null : mIndexer.getSections(); 286 } 287 } 288 } 289