InputMethodAndLanguageSettings.java revision 9bcc60704991879a0f8c9098eef26233942d6fe2
1/*
2 * Copyright (C) 2008 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 com.android.settings.R;
20import com.android.settings.Settings.SpellCheckersSettingsActivity;
21import com.android.settings.SettingsPreferenceFragment;
22import com.android.settings.Utils;
23import com.android.settings.VoiceInputOutputSettings;
24
25import android.app.Activity;
26import android.content.ContentResolver;
27import android.content.Context;
28import android.content.Intent;
29import android.content.pm.PackageManager;
30import android.content.res.Configuration;
31import android.database.ContentObserver;
32import android.os.Bundle;
33import android.os.Handler;
34import android.preference.CheckBoxPreference;
35import android.preference.ListPreference;
36import android.preference.Preference;
37import android.preference.PreferenceCategory;
38import android.preference.PreferenceGroup;
39import android.preference.PreferenceScreen;
40import android.provider.Settings;
41import android.provider.Settings.System;
42import android.text.TextUtils;
43import android.view.inputmethod.InputMethodInfo;
44import android.view.inputmethod.InputMethodManager;
45
46import java.util.ArrayList;
47import java.util.Collections;
48import java.util.List;
49import java.util.Set;
50
51public class InputMethodAndLanguageSettings extends SettingsPreferenceFragment
52        implements Preference.OnPreferenceChangeListener{
53
54    private static final String KEY_PHONE_LANGUAGE = "phone_language";
55    private static final String KEY_CURRENT_INPUT_METHOD = "current_input_method";
56    private static final String KEY_INPUT_METHOD_SELECTOR = "input_method_selector";
57    private static final String KEY_USER_DICTIONARY_SETTINGS = "key_user_dictionary_settings";
58    // false: on ICS or later
59    private static final boolean SHOW_INPUT_METHOD_SWITCHER_SETTINGS = false;
60
61    private static final String[] sSystemSettingNames = {
62        System.TEXT_AUTO_REPLACE, System.TEXT_AUTO_CAPS, System.TEXT_AUTO_PUNCTUATE,
63    };
64
65    private static final String[] sHardKeyboardKeys = {
66        "auto_replace", "auto_caps", "auto_punctuate",
67    };
68
69    private int mDefaultInputMethodSelectorVisibility = 0;
70    private ListPreference mShowInputMethodSelectorPref;
71    private Preference mLanguagePref;
72    private ArrayList<InputMethodPreference> mInputMethodPreferenceList =
73            new ArrayList<InputMethodPreference>();
74    private boolean mHaveHardKeyboard;
75    private PreferenceCategory mHardKeyboardCategory;
76    private InputMethodManager mImm;
77    private List<InputMethodInfo> mImis;
78    private boolean mIsOnlyImeSettings;
79    private Handler mHandler;
80    @SuppressWarnings("unused")
81    private SettingsObserver mSettingsObserver;
82
83    @Override
84    public void onCreate(Bundle icicle) {
85        super.onCreate(icicle);
86
87        addPreferencesFromResource(R.xml.language_settings);
88
89        try {
90            mDefaultInputMethodSelectorVisibility = Integer.valueOf(
91                    getString(R.string.input_method_selector_visibility_default_value));
92        } catch (NumberFormatException e) {
93        }
94
95        if (getActivity().getAssets().getLocales().length == 1) {
96            // No "Select language" pref if there's only one system locale available.
97            getPreferenceScreen().removePreference(findPreference(KEY_PHONE_LANGUAGE));
98        } else {
99            mLanguagePref = findPreference(KEY_PHONE_LANGUAGE);
100        }
101        if (SHOW_INPUT_METHOD_SWITCHER_SETTINGS) {
102            mShowInputMethodSelectorPref = (ListPreference)findPreference(
103                    KEY_INPUT_METHOD_SELECTOR);
104            mShowInputMethodSelectorPref.setOnPreferenceChangeListener(this);
105            // TODO: Update current input method name on summary
106            updateInputMethodSelectorSummary(loadInputMethodSelectorVisibility());
107        }
108
109        new VoiceInputOutputSettings(this).onCreate();
110
111        // Hard keyboard
112        final Configuration config = getResources().getConfiguration();
113        mHaveHardKeyboard = (config.keyboard == Configuration.KEYBOARD_QWERTY);
114
115        // IME
116        mIsOnlyImeSettings = Settings.ACTION_INPUT_METHOD_SETTINGS.equals(
117                getActivity().getIntent().getAction());
118        getActivity().getIntent().setAction(null);
119        mImm = (InputMethodManager) getSystemService(Context.INPUT_METHOD_SERVICE);
120        mImis = mImm.getInputMethodList();
121        createImePreferenceHierarchy((PreferenceGroup)findPreference("keyboard_settings_category"));
122
123        final Intent intent = new Intent(Intent.ACTION_MAIN);
124        intent.setClass(getActivity(), SpellCheckersSettingsActivity.class);
125        final SpellCheckersPreference scp = ((SpellCheckersPreference)findPreference(
126                "spellcheckers_settings"));
127        if (scp != null) {
128            scp.setFragmentIntent(this, intent);
129        }
130
131        mHandler = new Handler();
132        mSettingsObserver = new SettingsObserver(mHandler, getActivity());
133    }
134
135    private void updateInputMethodSelectorSummary(int value) {
136        String[] inputMethodSelectorTitles = getResources().getStringArray(
137                R.array.input_method_selector_titles);
138        if (inputMethodSelectorTitles.length > value) {
139            mShowInputMethodSelectorPref.setSummary(inputMethodSelectorTitles[value]);
140            mShowInputMethodSelectorPref.setValue(String.valueOf(value));
141        }
142    }
143
144    private void updateUserDictionaryPreference(Preference userDictionaryPreference) {
145        final Activity activity = getActivity();
146        final Set<String> localeList = UserDictionaryList.getUserDictionaryLocalesList(activity);
147        if (null == localeList) {
148            // The locale list is null if and only if the user dictionary service is
149            // not present or disabled. In this case we need to remove the preference.
150            getPreferenceScreen().removePreference(userDictionaryPreference);
151        } else if (localeList.size() <= 1) {
152            final Intent intent =
153                    new Intent(UserDictionaryList.USER_DICTIONARY_SETTINGS_INTENT_ACTION);
154            userDictionaryPreference.setTitle(R.string.user_dict_single_settings_title);
155            userDictionaryPreference.setIntent(intent);
156            // If the size of localeList is 0, we don't set the locale parameter in the
157            // extras. This will be interpreted by the UserDictionarySettings class as
158            // meaning "the current locale".
159            // Note that with the current code for UserDictionaryList#getUserDictionaryLocalesList()
160            // the locale list always has at least one element, since it always includes the current
161            // locale explicitly. @see UserDictionaryList.getUserDictionaryLocalesList().
162            if (localeList.size() == 1) {
163                final String locale = (String)localeList.toArray()[0];
164                userDictionaryPreference.getExtras().putString("locale", locale);
165            }
166        } else {
167            userDictionaryPreference.setTitle(R.string.user_dict_multiple_settings_title);
168            userDictionaryPreference.setFragment(UserDictionaryList.class.getName());
169        }
170    }
171
172    @Override
173    public void onResume() {
174        super.onResume();
175        mSettingsObserver.resume();
176        if (!mIsOnlyImeSettings) {
177            if (mLanguagePref != null) {
178                Configuration conf = getResources().getConfiguration();
179                String locale = conf.locale.getDisplayName(conf.locale);
180                if (locale != null && locale.length() > 1) {
181                    locale = Character.toUpperCase(locale.charAt(0)) + locale.substring(1);
182                    mLanguagePref.setSummary(locale);
183                }
184            }
185
186            updateUserDictionaryPreference(findPreference(KEY_USER_DICTIONARY_SETTINGS));
187            if (SHOW_INPUT_METHOD_SWITCHER_SETTINGS) {
188                mShowInputMethodSelectorPref.setOnPreferenceChangeListener(this);
189            }
190        }
191
192        // Hard keyboard
193        if (mHaveHardKeyboard) {
194            for (int i = 0; i < sHardKeyboardKeys.length; ++i) {
195                CheckBoxPreference chkPref = (CheckBoxPreference)
196                        mHardKeyboardCategory.findPreference(sHardKeyboardKeys[i]);
197                chkPref.setChecked(
198                        System.getInt(getContentResolver(), sSystemSettingNames[i], 1) > 0);
199            }
200        }
201
202        // IME
203        InputMethodAndSubtypeUtil.loadInputMethodSubtypeList(
204                this, getContentResolver(), mImis, null);
205        updateActiveInputMethodsSummary();
206    }
207
208    @Override
209    public void onPause() {
210        super.onPause();
211        mSettingsObserver.pause();
212        if (SHOW_INPUT_METHOD_SWITCHER_SETTINGS) {
213            mShowInputMethodSelectorPref.setOnPreferenceChangeListener(null);
214        }
215        InputMethodAndSubtypeUtil.saveInputMethodSubtypeList(
216                this, getContentResolver(), mImis, mHaveHardKeyboard);
217    }
218
219    @Override
220    public boolean onPreferenceTreeClick(PreferenceScreen preferenceScreen, Preference preference) {
221        // Input Method stuff
222        if (Utils.isMonkeyRunning()) {
223            return false;
224        }
225        if (preference instanceof PreferenceScreen) {
226            if (preference.getFragment() != null) {
227                // Fragment will be handled correctly by the super class.
228            } else if (KEY_CURRENT_INPUT_METHOD.equals(preference.getKey())) {
229                final InputMethodManager imm = (InputMethodManager)
230                        getSystemService(Context.INPUT_METHOD_SERVICE);
231                imm.showInputMethodPicker();
232            }
233        } else if (preference instanceof CheckBoxPreference) {
234            final CheckBoxPreference chkPref = (CheckBoxPreference) preference;
235            if (mHaveHardKeyboard) {
236                for (int i = 0; i < sHardKeyboardKeys.length; ++i) {
237                    if (chkPref == mHardKeyboardCategory.findPreference(sHardKeyboardKeys[i])) {
238                        System.putInt(getContentResolver(), sSystemSettingNames[i],
239                                chkPref.isChecked() ? 1 : 0);
240                        return true;
241                    }
242                }
243            }
244        }
245        return super.onPreferenceTreeClick(preferenceScreen, preference);
246    }
247
248    private void saveInputMethodSelectorVisibility(String value) {
249        try {
250            int intValue = Integer.valueOf(value);
251            Settings.Secure.putInt(getContentResolver(),
252                    Settings.Secure.INPUT_METHOD_SELECTOR_VISIBILITY, intValue);
253            updateInputMethodSelectorSummary(intValue);
254        } catch(NumberFormatException e) {
255        }
256    }
257
258    private int loadInputMethodSelectorVisibility() {
259        return Settings.Secure.getInt(getContentResolver(),
260                Settings.Secure.INPUT_METHOD_SELECTOR_VISIBILITY,
261                mDefaultInputMethodSelectorVisibility);
262    }
263
264    @Override
265    public boolean onPreferenceChange(Preference preference, Object value) {
266        if (SHOW_INPUT_METHOD_SWITCHER_SETTINGS) {
267            if (preference == mShowInputMethodSelectorPref) {
268                if (value instanceof String) {
269                    saveInputMethodSelectorVisibility((String)value);
270                }
271            }
272        }
273        return false;
274    }
275
276    private void updateActiveInputMethodsSummary() {
277        for (Preference pref : mInputMethodPreferenceList) {
278            if (pref instanceof InputMethodPreference) {
279                ((InputMethodPreference)pref).updateSummary();
280            }
281        }
282        updateCurrentImeName();
283    }
284
285    private void updateCurrentImeName() {
286        final Context context = getActivity();
287        if (context == null || mImm == null) return;
288        final Preference curPref = getPreferenceScreen().findPreference(KEY_CURRENT_INPUT_METHOD);
289        if (curPref != null) {
290            final CharSequence curIme = InputMethodAndSubtypeUtil.getCurrentInputMethodName(
291                    context, getContentResolver(), mImm, mImis, getPackageManager());
292            if (!TextUtils.isEmpty(curIme)) {
293                synchronized(this) {
294                    curPref.setSummary(curIme);
295                }
296            }
297        }
298    }
299
300    private InputMethodPreference getInputMethodPreference(InputMethodInfo imi, int imiSize) {
301        final PackageManager pm = getPackageManager();
302        final CharSequence label = imi.loadLabel(pm);
303        // IME settings
304        final Intent intent;
305        final String settingsActivity = imi.getSettingsActivity();
306        if (!TextUtils.isEmpty(settingsActivity)) {
307            intent = new Intent(Intent.ACTION_MAIN);
308            intent.setClassName(imi.getPackageName(), settingsActivity);
309        } else {
310            intent = null;
311        }
312
313        // Add a check box for enabling/disabling IME
314        InputMethodPreference pref = new InputMethodPreference(this, intent, mImm, imi, imiSize);
315        pref.setKey(imi.getId());
316        pref.setTitle(label);
317        return pref;
318    }
319
320    private void createImePreferenceHierarchy(PreferenceGroup root) {
321        final Preference hardKeyPref = findPreference("hard_keyboard");
322        if (mIsOnlyImeSettings) {
323            getPreferenceScreen().removeAll();
324            if (hardKeyPref != null && mHaveHardKeyboard) {
325                getPreferenceScreen().addPreference(hardKeyPref);
326            }
327            if (SHOW_INPUT_METHOD_SWITCHER_SETTINGS) {
328                getPreferenceScreen().addPreference(mShowInputMethodSelectorPref);
329            }
330            getPreferenceScreen().addPreference(root);
331        }
332        if (hardKeyPref != null) {
333            if (mHaveHardKeyboard) {
334                mHardKeyboardCategory = (PreferenceCategory) hardKeyPref;
335            } else {
336                getPreferenceScreen().removePreference(hardKeyPref);
337            }
338        }
339        root.removeAll();
340        mInputMethodPreferenceList.clear();
341
342        if (!mIsOnlyImeSettings) {
343            // Current IME selection
344            final PreferenceScreen currentIme = new PreferenceScreen(getActivity(), null);
345            currentIme.setKey(KEY_CURRENT_INPUT_METHOD);
346            currentIme.setTitle(getResources().getString(R.string.current_input_method));
347            root.addPreference(currentIme);
348        }
349
350        final int N = (mImis == null ? 0 : mImis.size());
351        for (int i = 0; i < N; ++i) {
352            final InputMethodInfo imi = mImis.get(i);
353            final InputMethodPreference pref = getInputMethodPreference(imi, N);
354            mInputMethodPreferenceList.add(pref);
355        }
356
357        Collections.sort(mInputMethodPreferenceList);
358        for (int i = 0; i < N; ++i) {
359            root.addPreference(mInputMethodPreferenceList.get(i));
360        }
361    }
362
363    private class SettingsObserver extends ContentObserver {
364        private Context mContext;
365
366        public SettingsObserver(Handler handler, Context context) {
367            super(handler);
368            mContext = context;
369        }
370
371        @Override public void onChange(boolean selfChange) {
372            updateCurrentImeName();
373        }
374
375        public void resume() {
376            final ContentResolver cr = mContext.getContentResolver();
377            cr.registerContentObserver(
378                    Settings.Secure.getUriFor(Settings.Secure.DEFAULT_INPUT_METHOD), false, this);
379            cr.registerContentObserver(Settings.Secure.getUriFor(
380                    Settings.Secure.SELECTED_INPUT_METHOD_SUBTYPE), false, this);
381        }
382
383        public void pause() {
384            mContext.getContentResolver().unregisterContentObserver(this);
385        }
386    }
387}
388