2 * Copyright (C) 2009 Google Inc.
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
8 * http://www.apache.org/licenses/LICENSE-2.0
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
17 package com.android.settings;
19 import com.android.settings.SettingsPreferenceFragment.SettingsDialogFragment;
21 import android.app.Activity;
22 import android.app.AlertDialog;
23 import android.app.Dialog;
24 import android.app.ListFragment;
25 import android.content.Context;
26 import android.content.DialogInterface;
27 import android.content.Intent;
28 import android.database.Cursor;
29 import android.os.Bundle;
30 import android.provider.UserDictionary;
31 import android.text.InputType;
32 import android.util.Log;
33 import android.view.ContextMenu;
34 import android.view.LayoutInflater;
35 import android.view.Menu;
36 import android.view.MenuInflater;
37 import android.view.MenuItem;
38 import android.view.View;
39 import android.view.ViewGroup;
40 import android.view.WindowManager;
41 import android.view.ContextMenu.ContextMenuInfo;
42 import android.widget.AlphabetIndexer;
43 import android.widget.EditText;
44 import android.widget.ImageView;
45 import android.widget.ListAdapter;
46 import android.widget.ListView;
47 import android.widget.SectionIndexer;
48 import android.widget.SimpleCursorAdapter;
49 import android.widget.TextView;
50 import android.widget.AdapterView.AdapterContextMenuInfo;
52 import java.util.Locale;
54 public class UserDictionarySettings extends ListFragment implements DialogCreatable {
55 private static final String TAG = "UserDictionarySettings";
57 private static final String INSTANCE_KEY_DIALOG_EDITING_WORD = "DIALOG_EDITING_WORD";
58 private static final String INSTANCE_KEY_ADDED_WORD = "DIALOG_ADDED_WORD";
60 private static final String[] QUERY_PROJECTION = {
61 UserDictionary.Words._ID, UserDictionary.Words.WORD
64 private static final int INDEX_ID = 0;
65 private static final int INDEX_WORD = 1;
67 // Either the locale is empty (means the word is applicable to all locales)
68 // or the word equals our current locale
69 private static final String QUERY_SELECTION = UserDictionary.Words.LOCALE + "=? OR "
70 + UserDictionary.Words.LOCALE + " is null";
72 private static final String DELETE_SELECTION = UserDictionary.Words.WORD + "=?";
74 private static final String EXTRA_WORD = "word";
76 private static final int CONTEXT_MENU_EDIT = Menu.FIRST;
77 private static final int CONTEXT_MENU_DELETE = Menu.FIRST + 1;
79 private static final int OPTIONS_MENU_ADD = Menu.FIRST;
81 private static final int DIALOG_ADD_OR_EDIT = 0;
83 /** The word being edited in the dialog (null means the user is adding a word). */
84 private String mDialogEditingWord;
87 private Cursor mCursor;
89 private boolean mAddedWordAlready;
90 private boolean mAutoReturn;
92 private SettingsDialogFragment mDialogFragment;
95 public void onCreate(Bundle savedInstanceState) {
96 super.onCreate(savedInstanceState);
100 public View onCreateView(LayoutInflater inflater, ViewGroup container,
101 Bundle savedInstanceState) {
102 mView = inflater.inflate(R.layout.list_content_with_empty_view, container, false);
107 public void onActivityCreated(Bundle savedInstanceState) {
108 super.onActivityCreated(savedInstanceState);
110 mCursor = createCursor();
111 TextView emptyView = (TextView)mView.findViewById(R.id.empty);
112 emptyView.setText(R.string.user_dict_settings_empty_text);
114 final ListView listView = getListView();
115 listView.setAdapter(createAdapter());
116 listView.setFastScrollEnabled(true);
117 listView.setEmptyView(emptyView);
119 registerForContextMenu(listView);
120 setHasOptionsMenu(true);
122 if (savedInstanceState != null) {
123 mDialogEditingWord = savedInstanceState.getString(INSTANCE_KEY_DIALOG_EDITING_WORD);
124 mAddedWordAlready = savedInstanceState.getBoolean(INSTANCE_KEY_ADDED_WORD, false);
129 public void onResume() {
131 final Intent intent = getActivity().getIntent();
132 if (!mAddedWordAlready
133 && intent.getAction().equals("com.android.settings.USER_DICTIONARY_INSERT")) {
134 final String word = intent.getStringExtra(EXTRA_WORD);
137 showAddOrEditDialog(word);
143 public void onSaveInstanceState(Bundle outState) {
144 super.onSaveInstanceState(outState);
145 outState.putString(INSTANCE_KEY_DIALOG_EDITING_WORD, mDialogEditingWord);
146 outState.putBoolean(INSTANCE_KEY_ADDED_WORD, mAddedWordAlready);
149 private Cursor createCursor() {
150 String currentLocale = Locale.getDefault().toString();
151 // Case-insensitive sort
152 return getActivity().managedQuery(UserDictionary.Words.CONTENT_URI, QUERY_PROJECTION,
153 QUERY_SELECTION, new String[] { currentLocale },
154 "UPPER(" + UserDictionary.Words.WORD + ")");
157 private ListAdapter createAdapter() {
158 return new MyAdapter(getActivity(),
159 R.layout.user_dictionary_item, mCursor,
160 new String[] { UserDictionary.Words.WORD, UserDictionary.Words._ID },
161 new int[] { android.R.id.text1, R.id.delete_button }, this);
165 public void onListItemClick(ListView l, View v, int position, long id) {
166 getActivity().openContextMenu(v);
170 public void onCreateContextMenu(ContextMenu menu, View v, ContextMenuInfo menuInfo) {
171 if (!(menuInfo instanceof AdapterContextMenuInfo)) return;
173 AdapterContextMenuInfo adapterMenuInfo = (AdapterContextMenuInfo) menuInfo;
174 menu.setHeaderTitle(getWord(adapterMenuInfo.position));
175 menu.add(0, CONTEXT_MENU_EDIT, 0,
176 R.string.user_dict_settings_context_menu_edit_title);
177 menu.add(0, CONTEXT_MENU_DELETE, 0,
178 R.string.user_dict_settings_context_menu_delete_title);
182 public boolean onContextItemSelected(MenuItem item) {
183 ContextMenuInfo menuInfo = item.getMenuInfo();
184 if (!(menuInfo instanceof AdapterContextMenuInfo)) return false;
186 AdapterContextMenuInfo adapterMenuInfo = (AdapterContextMenuInfo) menuInfo;
187 String word = getWord(adapterMenuInfo.position);
188 if (word == null) return true;
190 switch (item.getItemId()) {
191 case CONTEXT_MENU_DELETE:
195 case CONTEXT_MENU_EDIT:
196 showAddOrEditDialog(word);
204 public void onCreateOptionsMenu(Menu menu, MenuInflater inflater) {
205 MenuItem actionItem =
206 menu.add(0, OPTIONS_MENU_ADD, 0, R.string.user_dict_settings_add_menu_title)
207 .setIcon(R.drawable.ic_menu_add);
208 actionItem.setShowAsAction(MenuItem.SHOW_AS_ACTION_IF_ROOM);
212 public boolean onOptionsItemSelected(MenuItem item) {
213 showAddOrEditDialog(null);
217 private void showAddOrEditDialog(String editingWord) {
218 mDialogEditingWord = editingWord;
219 showDialog(DIALOG_ADD_OR_EDIT);
222 private String getWord(int position) {
223 mCursor.moveToPosition(position);
224 // Handle a possible race-condition
225 if (mCursor.isAfterLast()) return null;
227 return mCursor.getString(
228 mCursor.getColumnIndexOrThrow(UserDictionary.Words.WORD));
232 public Dialog onCreateDialog(int id) {
233 final Activity activity = getActivity();
234 final View content = activity.getLayoutInflater().inflate(R.layout.dialog_edittext, null);
235 final EditText editText = (EditText) content.findViewById(R.id.edittext);
236 editText.setText(mDialogEditingWord);
237 // No prediction in soft keyboard mode. TODO: Create a better way to disable prediction
238 editText.setInputType(InputType.TYPE_CLASS_TEXT
239 | InputType.TYPE_TEXT_FLAG_AUTO_COMPLETE);
241 AlertDialog dialog = new AlertDialog.Builder(activity)
242 .setTitle(mDialogEditingWord != null
243 ? R.string.user_dict_settings_edit_dialog_title
244 : R.string.user_dict_settings_add_dialog_title)
246 .setPositiveButton(android.R.string.ok, new DialogInterface.OnClickListener() {
247 public void onClick(DialogInterface dialog, int which) {
248 onAddOrEditFinished(editText.getText().toString());
249 if (mAutoReturn) activity.onBackPressed();
251 .setNegativeButton(android.R.string.cancel, new DialogInterface.OnClickListener() {
252 public void onClick(DialogInterface dialog, int which) {
253 if (mAutoReturn) activity.onBackPressed();
256 dialog.getWindow().setSoftInputMode(WindowManager.LayoutParams.SOFT_INPUT_ADJUST_RESIZE |
257 WindowManager.LayoutParams.SOFT_INPUT_STATE_VISIBLE);
261 private void showDialog(int dialogId) {
262 if (mDialogFragment != null) {
263 Log.e(TAG, "Old dialog fragment not null!");
265 mDialogFragment = new SettingsDialogFragment(this, dialogId);
266 mDialogFragment.show(getActivity().getFragmentManager(), Integer.toString(dialogId));
269 private void onAddOrEditFinished(String word) {
270 if (mDialogEditingWord != null) {
271 // The user was editing a word, so do a delete/add
272 deleteWord(mDialogEditingWord);
275 // Disallow duplicates
278 // TODO: present UI for picking whether to add word to all locales, or current.
279 UserDictionary.Words.addWord(getActivity(), word.toString(),
280 250, UserDictionary.Words.LOCALE_TYPE_ALL);
281 if (!mCursor.requery()) {
282 throw new IllegalStateException("can't requery on already-closed cursor.");
284 mAddedWordAlready = true;
287 private void deleteWord(String word) {
288 getActivity().getContentResolver().delete(
289 UserDictionary.Words.CONTENT_URI, DELETE_SELECTION, new String[] { word });
292 private static class MyAdapter extends SimpleCursorAdapter implements SectionIndexer,
293 View.OnClickListener {
295 private AlphabetIndexer mIndexer;
296 private UserDictionarySettings mSettings;
298 private ViewBinder mViewBinder = new ViewBinder() {
300 public boolean setViewValue(View v, Cursor c, int columnIndex) {
301 if (v instanceof ImageView && columnIndex == INDEX_ID) {
302 v.setOnClickListener(MyAdapter.this);
303 v.setTag(c.getString(INDEX_WORD));
311 public MyAdapter(Context context, int layout, Cursor c, String[] from, int[] to,
312 UserDictionarySettings settings) {
313 super(context, layout, c, from, to);
315 mSettings = settings;
316 int wordColIndex = c.getColumnIndexOrThrow(UserDictionary.Words.WORD);
317 String alphabet = context.getString(
318 com.android.internal.R.string.fast_scroll_alphabet);
319 mIndexer = new AlphabetIndexer(c, wordColIndex, alphabet);
320 setViewBinder(mViewBinder);
323 public int getPositionForSection(int section) {
324 return mIndexer.getPositionForSection(section);
327 public int getSectionForPosition(int position) {
328 return mIndexer.getSectionForPosition(position);
331 public Object[] getSections() {
332 return mIndexer.getSections();
335 public void onClick(View v) {
336 mSettings.deleteWord((String) v.getTag());