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