UserDictionarySettings.java revision 432fafe200a84ac519b1f23484cab55007baa62a
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
17package com.android.settings;
18
19import android.app.Activity;
20import android.app.AlertDialog;
21import android.app.Dialog;
22import android.app.ListFragment;
23import android.content.ContentResolver;
24import android.content.Context;
25import android.content.DialogInterface;
26import android.content.Intent;
27import android.database.Cursor;
28import android.os.Bundle;
29import android.provider.UserDictionary;
30import android.text.InputType;
31import android.util.Log;
32import android.view.LayoutInflater;
33import android.view.Menu;
34import android.view.MenuInflater;
35import android.view.MenuItem;
36import android.view.View;
37import android.view.ViewGroup;
38import android.view.WindowManager;
39import android.widget.AlphabetIndexer;
40import android.widget.EditText;
41import android.widget.ImageView;
42import android.widget.ListAdapter;
43import android.widget.ListView;
44import android.widget.SectionIndexer;
45import android.widget.SimpleCursorAdapter;
46import android.widget.TextView;
47
48import com.android.settings.inputmethod.UserDictionaryAddWordActivity;
49
50import java.util.Locale;
51
52public class UserDictionarySettings extends ListFragment {
53    private static final String TAG = "UserDictionarySettings";
54
55    private static final String[] QUERY_PROJECTION = {
56        UserDictionary.Words._ID, UserDictionary.Words.WORD
57    };
58
59    private static final int INDEX_ID = 0;
60    private static final int INDEX_WORD = 1;
61
62    // Either the locale is empty (means the word is applicable to all locales)
63    // or the word equals our current locale
64    private static final String QUERY_SELECTION =
65            UserDictionary.Words.LOCALE + "=?";
66    private static final String QUERY_SELECTION_ALL_LOCALES =
67            UserDictionary.Words.LOCALE + " is null";
68
69    private static final String DELETE_SELECTION = UserDictionary.Words.WORD + "=?";
70
71    private static final int OPTIONS_MENU_ADD = Menu.FIRST;
72
73    private Cursor mCursor;
74
75    protected String mLocale;
76
77    @Override
78    public void onCreate(Bundle savedInstanceState) {
79        super.onCreate(savedInstanceState);
80    }
81
82    @Override
83    public View onCreateView(
84            LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) {
85        return inflater.inflate(
86                com.android.internal.R.layout.preference_list_fragment, container, false);
87    }
88
89    @Override
90    public void onActivityCreated(Bundle savedInstanceState) {
91        super.onActivityCreated(savedInstanceState);
92
93        final Intent intent = getActivity().getIntent();
94        final String localeFromIntent =
95                null == intent ? null : intent.getStringExtra("locale");
96
97        final Bundle arguments = getArguments();
98        final String localeFromArguments =
99                null == arguments ? null : arguments.getString("locale");
100
101        final String locale;
102        if (null != localeFromArguments) {
103            locale = localeFromArguments;
104        } else if (null != localeFromIntent) {
105            locale = localeFromIntent;
106        } else {
107            locale = null;
108        }
109
110        mLocale = locale;
111        mCursor = createCursor(locale);
112        TextView emptyView = (TextView) getView().findViewById(android.R.id.empty);
113        emptyView.setText(R.string.user_dict_settings_empty_text);
114
115        final ListView listView = getListView();
116        listView.setAdapter(createAdapter());
117        listView.setFastScrollEnabled(true);
118        listView.setEmptyView(emptyView);
119
120        setHasOptionsMenu(true);
121
122    }
123
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
148    private ListAdapter createAdapter() {
149        return new MyAdapter(getActivity(),
150                R.layout.user_dictionary_item, mCursor,
151                new String[] { UserDictionary.Words.WORD, UserDictionary.Words._ID },
152                new int[] { android.R.id.text1, R.id.delete_button }, this);
153    }
154
155    @Override
156    public void onListItemClick(ListView l, View v, int position, long id) {
157        String word = getWord(position);
158        if (word != null) {
159            showAddOrEditDialog(word);
160        }
161    }
162
163    @Override
164    public void onCreateOptionsMenu(Menu menu, MenuInflater inflater) {
165        MenuItem actionItem =
166                menu.add(0, OPTIONS_MENU_ADD, 0, R.string.user_dict_settings_add_menu_title)
167                .setIcon(R.drawable.ic_menu_add);
168        actionItem.setShowAsAction(MenuItem.SHOW_AS_ACTION_IF_ROOM |
169                MenuItem.SHOW_AS_ACTION_WITH_TEXT);
170    }
171
172    @Override
173    public boolean onOptionsItemSelected(MenuItem item) {
174        if (item.getItemId() == OPTIONS_MENU_ADD) {
175            showAddOrEditDialog(null);
176            return true;
177        }
178        return false;
179    }
180
181    /**
182     * Add or edit a word. If editingWord is null, it's an add; otherwise, it's an edit.
183     * @param editingWord the word to edit, or null if it's an add.
184     */
185    private void showAddOrEditDialog(final String editingWord) {
186        final Intent intent = new Intent(null == editingWord
187                ? UserDictionaryAddWordActivity.MODE_INSERT_ACTION
188                : UserDictionaryAddWordActivity.MODE_EDIT_ACTION);
189        // The following are fine if they are null
190        intent.putExtra(UserDictionaryAddWordActivity.EXTRA_WORD, editingWord);
191        intent.putExtra(UserDictionaryAddWordActivity.EXTRA_LOCALE, mLocale);
192        startActivity(intent);
193    }
194
195    private String getWord(int position) {
196        if (null == mCursor) return null;
197        mCursor.moveToPosition(position);
198        // Handle a possible race-condition
199        if (mCursor.isAfterLast()) return null;
200
201        return mCursor.getString(
202                mCursor.getColumnIndexOrThrow(UserDictionary.Words.WORD));
203    }
204
205    public static void deleteWord(final String word, final ContentResolver resolver) {
206        resolver.delete(
207                UserDictionary.Words.CONTENT_URI, DELETE_SELECTION, new String[] { word });
208    }
209
210    private static class MyAdapter extends SimpleCursorAdapter implements SectionIndexer,
211            View.OnClickListener {
212
213        private AlphabetIndexer mIndexer;
214        private UserDictionarySettings mSettings;
215
216        private ViewBinder mViewBinder = new ViewBinder() {
217
218            public boolean setViewValue(View v, Cursor c, int columnIndex) {
219                if (v instanceof ImageView && columnIndex == INDEX_ID) {
220                    v.setOnClickListener(MyAdapter.this);
221                    v.setTag(c.getString(INDEX_WORD));
222                    return true;
223                }
224
225                return false;
226            }
227        };
228
229        public MyAdapter(Context context, int layout, Cursor c, String[] from, int[] to,
230                UserDictionarySettings settings) {
231            super(context, layout, c, from, to);
232
233            mSettings = settings;
234            if (null != c) {
235                final String alphabet = context.getString(
236                        com.android.internal.R.string.fast_scroll_alphabet);
237                final int wordColIndex = c.getColumnIndexOrThrow(UserDictionary.Words.WORD);
238                mIndexer = new AlphabetIndexer(c, wordColIndex, alphabet);
239            }
240            setViewBinder(mViewBinder);
241        }
242
243        public int getPositionForSection(int section) {
244            return null == mIndexer ? 0 : mIndexer.getPositionForSection(section);
245        }
246
247        public int getSectionForPosition(int position) {
248            return null == mIndexer ? 0 : mIndexer.getSectionForPosition(position);
249        }
250
251        public Object[] getSections() {
252            return null == mIndexer ? null : mIndexer.getSections();
253        }
254
255        public void onClick(View v) {
256            UserDictionarySettings.deleteWord((String) v.getTag(),
257                    mSettings.getActivity().getContentResolver());
258        }
259    }
260}
261