1/* 2 * Copyright (C) 2009 The Android Open Source Project 3 * 4 * Licensed under the Apache License, Version 2.0 (the "License"); 5 * you may not use this file except in compliance with the License. 6 * You may obtain a copy 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, 12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 * See the License for the specific language governing permissions and 14 * limitations under the License. 15 */ 16 17package com.android.settings.inputmethod; 18 19import android.annotation.Nullable; 20import android.app.ActionBar; 21import android.app.ListFragment; 22import android.app.LoaderManager; 23import android.content.ContentResolver; 24import android.content.Context; 25import android.content.Intent; 26import android.content.Loader; 27import android.database.Cursor; 28import android.os.Bundle; 29import android.provider.UserDictionary; 30import android.text.TextUtils; 31import android.view.LayoutInflater; 32import android.view.Menu; 33import android.view.MenuInflater; 34import android.view.MenuItem; 35import android.view.View; 36import android.view.ViewGroup; 37import android.widget.AlphabetIndexer; 38import android.widget.ListAdapter; 39import android.widget.ListView; 40import android.widget.SectionIndexer; 41import android.widget.SimpleCursorAdapter; 42import android.widget.TextView; 43 44import com.android.internal.logging.nano.MetricsProto; 45import com.android.settings.R; 46import com.android.settings.core.SubSettingLauncher; 47import com.android.settings.overlay.FeatureFactory; 48import com.android.settingslib.core.instrumentation.Instrumentable; 49import com.android.settingslib.core.instrumentation.VisibilityLoggerMixin; 50 51public class UserDictionarySettings extends ListFragment implements Instrumentable, 52 LoaderManager.LoaderCallbacks<Cursor> { 53 54 private static final String DELETE_SELECTION_WITH_SHORTCUT = UserDictionary.Words.WORD 55 + "=? AND " + UserDictionary.Words.SHORTCUT + "=?"; 56 private static final String DELETE_SELECTION_WITHOUT_SHORTCUT = UserDictionary.Words.WORD 57 + "=? AND " + UserDictionary.Words.SHORTCUT + " is null OR " 58 + UserDictionary.Words.SHORTCUT + "=''"; 59 60 private static final int OPTIONS_MENU_ADD = Menu.FIRST; 61 private static final int LOADER_ID = 1; 62 63 private VisibilityLoggerMixin mVisibilityLoggerMixin; 64 65 private Cursor mCursor; 66 private String mLocale; 67 68 @Override 69 public int getMetricsCategory() { 70 return MetricsProto.MetricsEvent.USER_DICTIONARY_SETTINGS; 71 } 72 73 @Override 74 public void onCreate(@Nullable Bundle savedInstanceState) { 75 super.onCreate(savedInstanceState); 76 77 mVisibilityLoggerMixin = new VisibilityLoggerMixin(getMetricsCategory(), 78 FeatureFactory.getFactory(getContext()).getMetricsFeatureProvider()); 79 80 final Intent intent = getActivity().getIntent(); 81 final String localeFromIntent = 82 null == intent ? null : intent.getStringExtra("locale"); 83 84 final Bundle arguments = getArguments(); 85 final String localeFromArguments = 86 null == arguments ? null : arguments.getString("locale"); 87 88 final String locale; 89 if (null != localeFromArguments) { 90 locale = localeFromArguments; 91 } else if (null != localeFromIntent) { 92 locale = localeFromIntent; 93 } else { 94 locale = null; 95 } 96 97 mLocale = locale; 98 99 setHasOptionsMenu(true); 100 getLoaderManager().initLoader(LOADER_ID, null, this /* callback */); 101 } 102 103 @Override 104 public View onCreateView( 105 LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) { 106 // Show the language as a subtitle of the action bar 107 final ActionBar actionBar = getActivity().getActionBar(); 108 if (actionBar != null) { 109 actionBar.setTitle(R.string.user_dict_settings_title); 110 actionBar.setSubtitle( 111 UserDictionarySettingsUtils.getLocaleDisplayName(getActivity(), mLocale)); 112 } 113 114 return inflater.inflate( 115 com.android.internal.R.layout.preference_list_fragment, container, false); 116 } 117 118 @Override 119 public void onViewCreated(View view, Bundle savedInstanceState) { 120 super.onViewCreated(view, savedInstanceState); 121 TextView emptyView = getView().findViewById(android.R.id.empty); 122 emptyView.setText(R.string.user_dict_settings_empty_text); 123 124 final ListView listView = getListView(); 125 listView.setFastScrollEnabled(true); 126 listView.setEmptyView(emptyView); 127 } 128 129 @Override 130 public void onResume() { 131 super.onResume(); 132 mVisibilityLoggerMixin.onResume(); 133 getLoaderManager().restartLoader(LOADER_ID, null, this /* callback */); 134 } 135 136 private ListAdapter createAdapter() { 137 return new MyAdapter(getActivity(), 138 R.layout.user_dictionary_item, mCursor, 139 new String[]{UserDictionary.Words.WORD, UserDictionary.Words.SHORTCUT}, 140 new int[]{android.R.id.text1, android.R.id.text2}); 141 } 142 143 @Override 144 public void onListItemClick(ListView l, View v, int position, long id) { 145 final String word = getWord(position); 146 final String shortcut = getShortcut(position); 147 if (word != null) { 148 showAddOrEditDialog(word, shortcut); 149 } 150 } 151 152 @Override 153 public void onCreateOptionsMenu(Menu menu, MenuInflater inflater) { 154 MenuItem actionItem = 155 menu.add(0, OPTIONS_MENU_ADD, 0, R.string.user_dict_settings_add_menu_title) 156 .setIcon(R.drawable.ic_menu_add_white); 157 actionItem.setShowAsAction(MenuItem.SHOW_AS_ACTION_IF_ROOM | 158 MenuItem.SHOW_AS_ACTION_WITH_TEXT); 159 } 160 161 @Override 162 public boolean onOptionsItemSelected(MenuItem item) { 163 if (item.getItemId() == OPTIONS_MENU_ADD) { 164 showAddOrEditDialog(null, null); 165 return true; 166 } 167 return false; 168 } 169 170 @Override 171 public void onPause() { 172 super.onPause(); 173 mVisibilityLoggerMixin.onPause(); 174 } 175 176 /** 177 * Add or edit a word. If editingWord is null, it's an add; otherwise, it's an edit. 178 * 179 * @param editingWord the word to edit, or null if it's an add. 180 * @param editingShortcut the shortcut for this entry, or null if none. 181 */ 182 private void showAddOrEditDialog(final String editingWord, final String editingShortcut) { 183 final Bundle args = new Bundle(); 184 args.putInt(UserDictionaryAddWordContents.EXTRA_MODE, null == editingWord 185 ? UserDictionaryAddWordContents.MODE_INSERT 186 : UserDictionaryAddWordContents.MODE_EDIT); 187 args.putString(UserDictionaryAddWordContents.EXTRA_WORD, editingWord); 188 args.putString(UserDictionaryAddWordContents.EXTRA_SHORTCUT, editingShortcut); 189 args.putString(UserDictionaryAddWordContents.EXTRA_LOCALE, mLocale); 190 191 new SubSettingLauncher(getContext()) 192 .setDestination(UserDictionaryAddWordFragment.class.getName()) 193 .setArguments(args) 194 .setTitle(R.string.user_dict_settings_add_dialog_title) 195 .setSourceMetricsCategory(getMetricsCategory()) 196 .launch(); 197 198 } 199 200 private String getWord(final int position) { 201 if (null == mCursor) return null; 202 mCursor.moveToPosition(position); 203 // Handle a possible race-condition 204 if (mCursor.isAfterLast()) return null; 205 206 return mCursor.getString( 207 mCursor.getColumnIndexOrThrow(UserDictionary.Words.WORD)); 208 } 209 210 private String getShortcut(final int position) { 211 if (null == mCursor) return null; 212 mCursor.moveToPosition(position); 213 // Handle a possible race-condition 214 if (mCursor.isAfterLast()) return null; 215 216 return mCursor.getString( 217 mCursor.getColumnIndexOrThrow(UserDictionary.Words.SHORTCUT)); 218 } 219 220 public static void deleteWord(final String word, final String shortcut, 221 final ContentResolver resolver) { 222 if (TextUtils.isEmpty(shortcut)) { 223 resolver.delete( 224 UserDictionary.Words.CONTENT_URI, DELETE_SELECTION_WITHOUT_SHORTCUT, 225 new String[]{word}); 226 } else { 227 resolver.delete( 228 UserDictionary.Words.CONTENT_URI, DELETE_SELECTION_WITH_SHORTCUT, 229 new String[]{word, shortcut}); 230 } 231 } 232 233 @Override 234 public Loader<Cursor> onCreateLoader(int id, Bundle args) { 235 return new UserDictionaryCursorLoader(getContext(), mLocale); 236 } 237 238 @Override 239 public void onLoadFinished(Loader<Cursor> loader, Cursor data) { 240 mCursor = data; 241 getListView().setAdapter(createAdapter()); 242 } 243 244 @Override 245 public void onLoaderReset(Loader<Cursor> loader) { 246 247 } 248 249 private static class MyAdapter extends SimpleCursorAdapter implements SectionIndexer { 250 251 private AlphabetIndexer mIndexer; 252 253 private final ViewBinder mViewBinder = new ViewBinder() { 254 255 @Override 256 public boolean setViewValue(View v, Cursor c, int columnIndex) { 257 if (columnIndex == UserDictionaryCursorLoader.INDEX_SHORTCUT) { 258 final String shortcut = c.getString(UserDictionaryCursorLoader.INDEX_SHORTCUT); 259 if (TextUtils.isEmpty(shortcut)) { 260 v.setVisibility(View.GONE); 261 } else { 262 ((TextView) v).setText(shortcut); 263 v.setVisibility(View.VISIBLE); 264 } 265 v.invalidate(); 266 return true; 267 } 268 269 return false; 270 } 271 }; 272 273 public MyAdapter(Context context, int layout, Cursor c, String[] from, int[] to) { 274 super(context, layout, c, from, to); 275 276 if (null != c) { 277 final String alphabet = context.getString( 278 com.android.internal.R.string.fast_scroll_alphabet); 279 final int wordColIndex = c.getColumnIndexOrThrow(UserDictionary.Words.WORD); 280 mIndexer = new AlphabetIndexer(c, wordColIndex, alphabet); 281 } 282 setViewBinder(mViewBinder); 283 } 284 285 @Override 286 public int getPositionForSection(int section) { 287 return null == mIndexer ? 0 : mIndexer.getPositionForSection(section); 288 } 289 290 @Override 291 public int getSectionForPosition(int position) { 292 return null == mIndexer ? 0 : mIndexer.getSectionForPosition(position); 293 } 294 295 @Override 296 public Object[] getSections() { 297 return null == mIndexer ? null : mIndexer.getSections(); 298 } 299 } 300} 301