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