UserDictionarySettings.java revision e98256646c0c1a8d79af5d23bf5330daafb5cf14
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.Context;
24import android.content.DialogInterface;
25import android.content.Intent;
26import android.database.Cursor;
27import android.os.Bundle;
28import android.provider.UserDictionary;
29import android.text.InputType;
30import android.util.Log;
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.view.WindowManager;
38import android.widget.AlphabetIndexer;
39import android.widget.EditText;
40import android.widget.ImageView;
41import android.widget.ListAdapter;
42import android.widget.ListView;
43import android.widget.SectionIndexer;
44import android.widget.SimpleCursorAdapter;
45import android.widget.TextView;
46
47import com.android.settings.SettingsPreferenceFragment.SettingsDialogFragment;
48
49import java.util.Locale;
50
51public class UserDictionarySettings extends ListFragment implements DialogCreatable {
52    private static final String TAG = "UserDictionarySettings";
53
54    private static final String INSTANCE_KEY_DIALOG_EDITING_WORD = "DIALOG_EDITING_WORD";
55    private static final String INSTANCE_KEY_ADDED_WORD = "DIALOG_ADDED_WORD";
56
57    private static final String[] QUERY_PROJECTION = {
58        UserDictionary.Words._ID, UserDictionary.Words.WORD
59    };
60
61    private static final int INDEX_ID = 0;
62    private static final int INDEX_WORD = 1;
63
64    // Either the locale is empty (means the word is applicable to all locales)
65    // or the word equals our current locale
66    private static final String QUERY_SELECTION = UserDictionary.Words.LOCALE + "=? OR "
67            + UserDictionary.Words.LOCALE + " is null";
68
69    private static final String DELETE_SELECTION = UserDictionary.Words.WORD + "=?";
70
71    private static final String EXTRA_WORD = "word";
72
73    private static final int OPTIONS_MENU_ADD = Menu.FIRST;
74
75    private static final int DIALOG_ADD_OR_EDIT = 0;
76
77    /** The word being edited in the dialog (null means the user is adding a word). */
78    private String mDialogEditingWord;
79
80    private View mView;
81    private Cursor mCursor;
82
83    private boolean mAddedWordAlready;
84    private boolean mAutoReturn;
85
86    private SettingsDialogFragment mDialogFragment;
87
88    @Override
89    public void onCreate(Bundle savedInstanceState) {
90        super.onCreate(savedInstanceState);
91    }
92
93    @Override
94    public View onCreateView(LayoutInflater inflater, ViewGroup container,
95            Bundle savedInstanceState) {
96        mView = inflater.inflate(R.layout.list_content_with_empty_view, container, false);
97        return mView;
98    }
99
100    @Override
101    public void onActivityCreated(Bundle savedInstanceState) {
102        super.onActivityCreated(savedInstanceState);
103
104        mCursor = createCursor();
105        TextView emptyView = (TextView)mView.findViewById(R.id.empty);
106        emptyView.setText(R.string.user_dict_settings_empty_text);
107
108        final ListView listView = getListView();
109        listView.setAdapter(createAdapter());
110        listView.setFastScrollEnabled(true);
111        listView.setEmptyView(emptyView);
112
113        setHasOptionsMenu(true);
114
115        if (savedInstanceState != null) {
116            mDialogEditingWord = savedInstanceState.getString(INSTANCE_KEY_DIALOG_EDITING_WORD);
117            mAddedWordAlready = savedInstanceState.getBoolean(INSTANCE_KEY_ADDED_WORD, false);
118        }
119    }
120
121    @Override
122    public void onResume() {
123        super.onResume();
124        final Intent intent = getActivity().getIntent();
125        if (!mAddedWordAlready
126                && intent.getAction().equals("com.android.settings.USER_DICTIONARY_INSERT")) {
127            final String word = intent.getStringExtra(EXTRA_WORD);
128            mAutoReturn = true;
129            if (word != null) {
130                showAddOrEditDialog(word);
131            }
132        }
133    }
134
135    @Override
136    public void onSaveInstanceState(Bundle outState) {
137        super.onSaveInstanceState(outState);
138        outState.putString(INSTANCE_KEY_DIALOG_EDITING_WORD, mDialogEditingWord);
139        outState.putBoolean(INSTANCE_KEY_ADDED_WORD, mAddedWordAlready);
140    }
141
142    private Cursor createCursor() {
143        String currentLocale = Locale.getDefault().toString();
144        // Case-insensitive sort
145        return getActivity().managedQuery(UserDictionary.Words.CONTENT_URI, QUERY_PROJECTION,
146                QUERY_SELECTION, new String[] { currentLocale },
147                "UPPER(" + UserDictionary.Words.WORD + ")");
148    }
149
150    private ListAdapter createAdapter() {
151        return new MyAdapter(getActivity(),
152                R.layout.user_dictionary_item, mCursor,
153                new String[] { UserDictionary.Words.WORD, UserDictionary.Words._ID },
154                new int[] { android.R.id.text1, R.id.delete_button }, this);
155    }
156
157    @Override
158    public void onListItemClick(ListView l, View v, int position, long id) {
159        String word = getWord(position);
160        if (word != null) {
161            showAddOrEditDialog(word);
162        }
163    }
164
165    @Override
166    public void onCreateOptionsMenu(Menu menu, MenuInflater inflater) {
167        MenuItem actionItem =
168                menu.add(0, OPTIONS_MENU_ADD, 0, R.string.user_dict_settings_add_menu_title)
169                .setIcon(R.drawable.ic_menu_add);
170        actionItem.setShowAsAction(MenuItem.SHOW_AS_ACTION_IF_ROOM);
171    }
172
173    @Override
174    public boolean onOptionsItemSelected(MenuItem item) {
175        showAddOrEditDialog(null);
176        return true;
177    }
178
179    private void showAddOrEditDialog(String editingWord) {
180        mDialogEditingWord = editingWord;
181        showDialog(DIALOG_ADD_OR_EDIT);
182    }
183
184    private String getWord(int position) {
185        mCursor.moveToPosition(position);
186        // Handle a possible race-condition
187        if (mCursor.isAfterLast()) return null;
188
189        return mCursor.getString(
190                mCursor.getColumnIndexOrThrow(UserDictionary.Words.WORD));
191    }
192
193    @Override
194    public Dialog onCreateDialog(int id) {
195        final Activity activity = getActivity();
196        final AlertDialog.Builder dialogBuilder = new AlertDialog.Builder(activity);
197        final LayoutInflater inflater = LayoutInflater.from(dialogBuilder.getContext());
198        final View content = inflater.inflate(R.layout.dialog_edittext, null);
199        final EditText editText = (EditText) content.findViewById(R.id.edittext);
200        editText.setText(mDialogEditingWord);
201        // No prediction in soft keyboard mode. TODO: Create a better way to disable prediction
202        editText.setInputType(InputType.TYPE_CLASS_TEXT
203                | InputType.TYPE_TEXT_FLAG_AUTO_COMPLETE);
204
205        AlertDialog dialog = dialogBuilder
206                .setTitle(mDialogEditingWord != null
207                        ? R.string.user_dict_settings_edit_dialog_title
208                        : R.string.user_dict_settings_add_dialog_title)
209                .setView(content)
210                .setPositiveButton(android.R.string.ok, new DialogInterface.OnClickListener() {
211                    public void onClick(DialogInterface dialog, int which) {
212                        onAddOrEditFinished(editText.getText().toString());
213                        if (mAutoReturn) activity.onBackPressed();
214                    }})
215                .setNegativeButton(android.R.string.cancel, new DialogInterface.OnClickListener() {
216                    public void onClick(DialogInterface dialog, int which) {
217                        if (mAutoReturn) activity.onBackPressed();
218                    }})
219                .create();
220        dialog.getWindow().setSoftInputMode(WindowManager.LayoutParams.SOFT_INPUT_ADJUST_RESIZE |
221                WindowManager.LayoutParams.SOFT_INPUT_STATE_VISIBLE);
222        return dialog;
223    }
224
225    private void showDialog(int dialogId) {
226        if (mDialogFragment != null) {
227            Log.e(TAG, "Old dialog fragment not null!");
228        }
229        mDialogFragment = new SettingsDialogFragment(this, dialogId);
230        mDialogFragment.show(getActivity().getFragmentManager(), Integer.toString(dialogId));
231    }
232
233    private void onAddOrEditFinished(String word) {
234        if (mDialogEditingWord != null) {
235            // The user was editing a word, so do a delete/add
236            deleteWord(mDialogEditingWord);
237        }
238
239        // Disallow duplicates
240        deleteWord(word);
241
242        // TODO: present UI for picking whether to add word to all locales, or current.
243        UserDictionary.Words.addWord(getActivity(), word.toString(),
244                250, UserDictionary.Words.LOCALE_TYPE_ALL);
245        if (!mCursor.requery()) {
246            throw new IllegalStateException("can't requery on already-closed cursor.");
247        }
248        mAddedWordAlready = true;
249    }
250
251    private void deleteWord(String word) {
252        getActivity().getContentResolver().delete(
253                UserDictionary.Words.CONTENT_URI, DELETE_SELECTION, new String[] { word });
254    }
255
256    private static class MyAdapter extends SimpleCursorAdapter implements SectionIndexer,
257            View.OnClickListener {
258
259        private AlphabetIndexer mIndexer;
260        private UserDictionarySettings mSettings;
261
262        private ViewBinder mViewBinder = new ViewBinder() {
263
264            public boolean setViewValue(View v, Cursor c, int columnIndex) {
265                if (v instanceof ImageView && columnIndex == INDEX_ID) {
266                    v.setOnClickListener(MyAdapter.this);
267                    v.setTag(c.getString(INDEX_WORD));
268                    return true;
269                }
270
271                return false;
272            }
273        };
274
275        public MyAdapter(Context context, int layout, Cursor c, String[] from, int[] to,
276                UserDictionarySettings settings) {
277            super(context, layout, c, from, to);
278
279            mSettings = settings;
280            int wordColIndex = c.getColumnIndexOrThrow(UserDictionary.Words.WORD);
281            String alphabet = context.getString(
282                    com.android.internal.R.string.fast_scroll_alphabet);
283            mIndexer = new AlphabetIndexer(c, wordColIndex, alphabet);
284            setViewBinder(mViewBinder);
285        }
286
287        public int getPositionForSection(int section) {
288            return mIndexer.getPositionForSection(section);
289        }
290
291        public int getSectionForPosition(int position) {
292            return mIndexer.getSectionForPosition(position);
293        }
294
295        public Object[] getSections() {
296            return mIndexer.getSections();
297        }
298
299        public void onClick(View v) {
300            mSettings.deleteWord((String) v.getTag());
301        }
302    }
303}
304