Home | History | Annotate | Download | only in inputmethod
      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 
     17 package com.android.settings.inputmethod;
     18 
     19 import com.android.settings.R;
     20 import com.android.settings.Settings.KeyboardLayoutPickerActivity;
     21 import com.android.settings.Settings.SpellCheckersSettingsActivity;
     22 import com.android.settings.SettingsPreferenceFragment;
     23 import com.android.settings.UserDictionarySettings;
     24 import com.android.settings.Utils;
     25 import com.android.settings.VoiceInputOutputSettings;
     26 
     27 import android.app.Activity;
     28 import android.app.Fragment;
     29 import android.content.ContentResolver;
     30 import android.content.Context;
     31 import android.content.Intent;
     32 import android.content.pm.PackageManager;
     33 import android.content.res.Configuration;
     34 import android.content.res.Resources;
     35 import android.database.ContentObserver;
     36 import android.hardware.input.InputManager;
     37 import android.hardware.input.KeyboardLayout;
     38 import android.os.Bundle;
     39 import android.os.Handler;
     40 import android.preference.CheckBoxPreference;
     41 import android.preference.ListPreference;
     42 import android.preference.Preference;
     43 import android.preference.Preference.OnPreferenceChangeListener;
     44 import android.preference.Preference.OnPreferenceClickListener;
     45 import android.preference.PreferenceCategory;
     46 import android.preference.PreferenceScreen;
     47 import android.provider.Settings;
     48 import android.provider.Settings.System;
     49 import android.text.TextUtils;
     50 import android.view.InputDevice;
     51 import android.view.inputmethod.InputMethodInfo;
     52 import android.view.inputmethod.InputMethodManager;
     53 import android.widget.BaseAdapter;
     54 
     55 import java.util.ArrayList;
     56 import java.util.Collections;
     57 import java.util.List;
     58 import java.util.TreeSet;
     59 
     60 public class InputMethodAndLanguageSettings extends SettingsPreferenceFragment
     61         implements Preference.OnPreferenceChangeListener, InputManager.InputDeviceListener,
     62         KeyboardLayoutDialogFragment.OnSetupKeyboardLayoutsListener {
     63 
     64     private static final String KEY_PHONE_LANGUAGE = "phone_language";
     65     private static final String KEY_CURRENT_INPUT_METHOD = "current_input_method";
     66     private static final String KEY_INPUT_METHOD_SELECTOR = "input_method_selector";
     67     private static final String KEY_USER_DICTIONARY_SETTINGS = "key_user_dictionary_settings";
     68     // false: on ICS or later
     69     private static final boolean SHOW_INPUT_METHOD_SWITCHER_SETTINGS = false;
     70 
     71     private static final String[] sSystemSettingNames = {
     72         System.TEXT_AUTO_REPLACE, System.TEXT_AUTO_CAPS, System.TEXT_AUTO_PUNCTUATE,
     73     };
     74 
     75     private static final String[] sHardKeyboardKeys = {
     76         "auto_replace", "auto_caps", "auto_punctuate",
     77     };
     78 
     79     private int mDefaultInputMethodSelectorVisibility = 0;
     80     private ListPreference mShowInputMethodSelectorPref;
     81     private PreferenceCategory mKeyboardSettingsCategory;
     82     private PreferenceCategory mHardKeyboardCategory;
     83     private PreferenceCategory mGameControllerCategory;
     84     private Preference mLanguagePref;
     85     private final ArrayList<InputMethodPreference> mInputMethodPreferenceList =
     86             new ArrayList<InputMethodPreference>();
     87     private final ArrayList<PreferenceScreen> mHardKeyboardPreferenceList =
     88             new ArrayList<PreferenceScreen>();
     89     private InputManager mIm;
     90     private InputMethodManager mImm;
     91     private boolean mIsOnlyImeSettings;
     92     private Handler mHandler;
     93     private SettingsObserver mSettingsObserver;
     94     private Intent mIntentWaitingForResult;
     95     private InputMethodSettingValuesWrapper mInputMethodSettingValues;
     96 
     97     private final OnPreferenceChangeListener mOnImePreferenceChangedListener =
     98             new OnPreferenceChangeListener() {
     99                 @Override
    100                 public boolean onPreferenceChange(Preference arg0, Object arg1) {
    101                     InputMethodSettingValuesWrapper.getInstance(
    102                             arg0.getContext()).refreshAllInputMethodAndSubtypes();
    103                     ((BaseAdapter)getPreferenceScreen().getRootAdapter()).notifyDataSetChanged();
    104                     updateInputMethodPreferenceViews();
    105                     return true;
    106                 }
    107             };
    108 
    109     @Override
    110     public void onCreate(Bundle icicle) {
    111         super.onCreate(icicle);
    112 
    113         addPreferencesFromResource(R.xml.language_settings);
    114 
    115         try {
    116             mDefaultInputMethodSelectorVisibility = Integer.valueOf(
    117                     getString(R.string.input_method_selector_visibility_default_value));
    118         } catch (NumberFormatException e) {
    119         }
    120 
    121         if (getActivity().getAssets().getLocales().length == 1) {
    122             // No "Select language" pref if there's only one system locale available.
    123             getPreferenceScreen().removePreference(findPreference(KEY_PHONE_LANGUAGE));
    124         } else {
    125             mLanguagePref = findPreference(KEY_PHONE_LANGUAGE);
    126         }
    127         if (SHOW_INPUT_METHOD_SWITCHER_SETTINGS) {
    128             mShowInputMethodSelectorPref = (ListPreference)findPreference(
    129                     KEY_INPUT_METHOD_SELECTOR);
    130             mShowInputMethodSelectorPref.setOnPreferenceChangeListener(this);
    131             // TODO: Update current input method name on summary
    132             updateInputMethodSelectorSummary(loadInputMethodSelectorVisibility());
    133         }
    134 
    135         new VoiceInputOutputSettings(this).onCreate();
    136 
    137         // Get references to dynamically constructed categories.
    138         mHardKeyboardCategory = (PreferenceCategory)findPreference("hard_keyboard");
    139         mKeyboardSettingsCategory = (PreferenceCategory)findPreference(
    140                 "keyboard_settings_category");
    141         mGameControllerCategory = (PreferenceCategory)findPreference(
    142                 "game_controller_settings_category");
    143 
    144         // Filter out irrelevant features if invoked from IME settings button.
    145         mIsOnlyImeSettings = Settings.ACTION_INPUT_METHOD_SETTINGS.equals(
    146                 getActivity().getIntent().getAction());
    147         getActivity().getIntent().setAction(null);
    148         if (mIsOnlyImeSettings) {
    149             getPreferenceScreen().removeAll();
    150             getPreferenceScreen().addPreference(mHardKeyboardCategory);
    151             if (SHOW_INPUT_METHOD_SWITCHER_SETTINGS) {
    152                 getPreferenceScreen().addPreference(mShowInputMethodSelectorPref);
    153             }
    154             getPreferenceScreen().addPreference(mKeyboardSettingsCategory);
    155         }
    156 
    157         // Build IME preference category.
    158         mImm = (InputMethodManager) getSystemService(Context.INPUT_METHOD_SERVICE);
    159         mInputMethodSettingValues = InputMethodSettingValuesWrapper.getInstance(getActivity());
    160 
    161         mKeyboardSettingsCategory.removeAll();
    162         if (!mIsOnlyImeSettings) {
    163             final PreferenceScreen currentIme = new PreferenceScreen(getActivity(), null);
    164             currentIme.setKey(KEY_CURRENT_INPUT_METHOD);
    165             currentIme.setTitle(getResources().getString(R.string.current_input_method));
    166             mKeyboardSettingsCategory.addPreference(currentIme);
    167         }
    168 
    169         // Build hard keyboard and game controller preference categories.
    170         mIm = (InputManager)getActivity().getSystemService(Context.INPUT_SERVICE);
    171         updateInputDevices();
    172 
    173         // Spell Checker
    174         final Intent intent = new Intent(Intent.ACTION_MAIN);
    175         intent.setClass(getActivity(), SpellCheckersSettingsActivity.class);
    176         final SpellCheckersPreference scp = ((SpellCheckersPreference)findPreference(
    177                 "spellcheckers_settings"));
    178         if (scp != null) {
    179             scp.setFragmentIntent(this, intent);
    180         }
    181 
    182         mHandler = new Handler();
    183         mSettingsObserver = new SettingsObserver(mHandler, getActivity());
    184     }
    185 
    186     private void updateInputMethodSelectorSummary(int value) {
    187         String[] inputMethodSelectorTitles = getResources().getStringArray(
    188                 R.array.input_method_selector_titles);
    189         if (inputMethodSelectorTitles.length > value) {
    190             mShowInputMethodSelectorPref.setSummary(inputMethodSelectorTitles[value]);
    191             mShowInputMethodSelectorPref.setValue(String.valueOf(value));
    192         }
    193     }
    194 
    195     private void updateUserDictionaryPreference(Preference userDictionaryPreference) {
    196         final Activity activity = getActivity();
    197         final TreeSet<String> localeSet = UserDictionaryList.getUserDictionaryLocalesSet(activity);
    198         if (null == localeSet) {
    199             // The locale list is null if and only if the user dictionary service is
    200             // not present or disabled. In this case we need to remove the preference.
    201             getPreferenceScreen().removePreference(userDictionaryPreference);
    202         } else {
    203             userDictionaryPreference.setOnPreferenceClickListener(
    204                     new OnPreferenceClickListener() {
    205                         @Override
    206                         public boolean onPreferenceClick(Preference arg0) {
    207                             // Redirect to UserDictionarySettings if the user needs only one
    208                             // language.
    209                             final Bundle extras = new Bundle();
    210                             final Class<? extends Fragment> targetFragment;
    211                             if (localeSet.size() <= 1) {
    212                                 if (!localeSet.isEmpty()) {
    213                                     // If the size of localeList is 0, we don't set the locale
    214                                     // parameter in the extras. This will be interpreted by the
    215                                     // UserDictionarySettings class as meaning
    216                                     // "the current locale". Note that with the current code for
    217                                     // UserDictionaryList#getUserDictionaryLocalesSet()
    218                                     // the locale list always has at least one element, since it
    219                                     // always includes the current locale explicitly.
    220                                     // @see UserDictionaryList.getUserDictionaryLocalesSet().
    221                                     extras.putString("locale", localeSet.first());
    222                                 }
    223                                 targetFragment = UserDictionarySettings.class;
    224                             } else {
    225                                 targetFragment = UserDictionaryList.class;
    226                             }
    227                             startFragment(InputMethodAndLanguageSettings.this,
    228                                     targetFragment.getCanonicalName(), -1, extras);
    229                             return true;
    230                         }
    231                     });
    232         }
    233     }
    234 
    235     @Override
    236     public void onResume() {
    237         super.onResume();
    238 
    239         mSettingsObserver.resume();
    240         mIm.registerInputDeviceListener(this, null);
    241 
    242         if (!mIsOnlyImeSettings) {
    243             if (mLanguagePref != null) {
    244                 Configuration conf = getResources().getConfiguration();
    245                 String language = conf.locale.getLanguage();
    246                 String localeString;
    247                 // TODO: This is not an accurate way to display the locale, as it is
    248                 // just working around the fact that we support limited dialects
    249                 // and want to pretend that the language is valid for all locales.
    250                 // We need a way to support languages that aren't tied to a particular
    251                 // locale instead of hiding the locale qualifier.
    252                 if (language.equals("zz")) {
    253                     String country = conf.locale.getCountry();
    254                     if (country.equals("ZZ")) {
    255                         localeString = "[Developer] Accented English (zz_ZZ)";
    256                     } else if (country.equals("ZY")) {
    257                         localeString = "[Developer] Fake Bi-Directional (zz_ZY)";
    258                     } else {
    259                         localeString = "";
    260                     }
    261                 } else if (hasOnlyOneLanguageInstance(language,
    262                         Resources.getSystem().getAssets().getLocales())) {
    263                     localeString = conf.locale.getDisplayLanguage(conf.locale);
    264                 } else {
    265                     localeString = conf.locale.getDisplayName(conf.locale);
    266                 }
    267                 if (localeString.length() > 1) {
    268                     localeString = Character.toUpperCase(localeString.charAt(0))
    269                             + localeString.substring(1);
    270                     mLanguagePref.setSummary(localeString);
    271                 }
    272             }
    273 
    274             updateUserDictionaryPreference(findPreference(KEY_USER_DICTIONARY_SETTINGS));
    275             if (SHOW_INPUT_METHOD_SWITCHER_SETTINGS) {
    276                 mShowInputMethodSelectorPref.setOnPreferenceChangeListener(this);
    277             }
    278         }
    279 
    280         // Hard keyboard
    281         if (!mHardKeyboardPreferenceList.isEmpty()) {
    282             for (int i = 0; i < sHardKeyboardKeys.length; ++i) {
    283                 CheckBoxPreference chkPref = (CheckBoxPreference)
    284                         mHardKeyboardCategory.findPreference(sHardKeyboardKeys[i]);
    285                 chkPref.setChecked(
    286                         System.getInt(getContentResolver(), sSystemSettingNames[i], 1) > 0);
    287             }
    288         }
    289 
    290         updateInputDevices();
    291 
    292         // Refresh internal states in mInputMethodSettingValues to keep the latest
    293         // "InputMethodInfo"s and "InputMethodSubtype"s
    294         mInputMethodSettingValues.refreshAllInputMethodAndSubtypes();
    295         updateInputMethodPreferenceViews();
    296     }
    297 
    298     @Override
    299     public void onPause() {
    300         super.onPause();
    301 
    302         mIm.unregisterInputDeviceListener(this);
    303         mSettingsObserver.pause();
    304 
    305         if (SHOW_INPUT_METHOD_SWITCHER_SETTINGS) {
    306             mShowInputMethodSelectorPref.setOnPreferenceChangeListener(null);
    307         }
    308         // TODO: Consolidate the logic to InputMethodSettingsWrapper
    309         InputMethodAndSubtypeUtil.saveInputMethodSubtypeList(
    310                 this, getContentResolver(), mInputMethodSettingValues.getInputMethodList(),
    311                 !mHardKeyboardPreferenceList.isEmpty());
    312     }
    313 
    314     @Override
    315     public void onInputDeviceAdded(int deviceId) {
    316         updateInputDevices();
    317     }
    318 
    319     @Override
    320     public void onInputDeviceChanged(int deviceId) {
    321         updateInputDevices();
    322     }
    323 
    324     @Override
    325     public void onInputDeviceRemoved(int deviceId) {
    326         updateInputDevices();
    327     }
    328 
    329     @Override
    330     public boolean onPreferenceTreeClick(PreferenceScreen preferenceScreen, Preference preference) {
    331         // Input Method stuff
    332         if (Utils.isMonkeyRunning()) {
    333             return false;
    334         }
    335         if (preference instanceof PreferenceScreen) {
    336             if (preference.getFragment() != null) {
    337                 // Fragment will be handled correctly by the super class.
    338             } else if (KEY_CURRENT_INPUT_METHOD.equals(preference.getKey())) {
    339                 final InputMethodManager imm = (InputMethodManager)
    340                         getSystemService(Context.INPUT_METHOD_SERVICE);
    341                 imm.showInputMethodPicker();
    342             }
    343         } else if (preference instanceof CheckBoxPreference) {
    344             final CheckBoxPreference chkPref = (CheckBoxPreference) preference;
    345             if (!mHardKeyboardPreferenceList.isEmpty()) {
    346                 for (int i = 0; i < sHardKeyboardKeys.length; ++i) {
    347                     if (chkPref == mHardKeyboardCategory.findPreference(sHardKeyboardKeys[i])) {
    348                         System.putInt(getContentResolver(), sSystemSettingNames[i],
    349                                 chkPref.isChecked() ? 1 : 0);
    350                         return true;
    351                     }
    352                 }
    353             }
    354             if (chkPref == mGameControllerCategory.findPreference("vibrate_input_devices")) {
    355                 System.putInt(getContentResolver(), Settings.System.VIBRATE_INPUT_DEVICES,
    356                         chkPref.isChecked() ? 1 : 0);
    357                 return true;
    358             }
    359         }
    360         return super.onPreferenceTreeClick(preferenceScreen, preference);
    361     }
    362 
    363     private boolean hasOnlyOneLanguageInstance(String languageCode, String[] locales) {
    364         int count = 0;
    365         for (String localeCode : locales) {
    366             if (localeCode.length() > 2
    367                     && localeCode.startsWith(languageCode)) {
    368                 count++;
    369                 if (count > 1) {
    370                     return false;
    371                 }
    372             }
    373         }
    374         return count == 1;
    375     }
    376 
    377     private void saveInputMethodSelectorVisibility(String value) {
    378         try {
    379             int intValue = Integer.valueOf(value);
    380             Settings.Secure.putInt(getContentResolver(),
    381                     Settings.Secure.INPUT_METHOD_SELECTOR_VISIBILITY, intValue);
    382             updateInputMethodSelectorSummary(intValue);
    383         } catch(NumberFormatException e) {
    384         }
    385     }
    386 
    387     private int loadInputMethodSelectorVisibility() {
    388         return Settings.Secure.getInt(getContentResolver(),
    389                 Settings.Secure.INPUT_METHOD_SELECTOR_VISIBILITY,
    390                 mDefaultInputMethodSelectorVisibility);
    391     }
    392 
    393     @Override
    394     public boolean onPreferenceChange(Preference preference, Object value) {
    395         if (SHOW_INPUT_METHOD_SWITCHER_SETTINGS) {
    396             if (preference == mShowInputMethodSelectorPref) {
    397                 if (value instanceof String) {
    398                     saveInputMethodSelectorVisibility((String)value);
    399                 }
    400             }
    401         }
    402         return false;
    403     }
    404 
    405     private void updateInputMethodPreferenceViews() {
    406         synchronized (mInputMethodPreferenceList) {
    407             // Clear existing "InputMethodPreference"s
    408             for (final InputMethodPreference imp : mInputMethodPreferenceList) {
    409                 mKeyboardSettingsCategory.removePreference(imp);
    410             }
    411             mInputMethodPreferenceList.clear();
    412             final List<InputMethodInfo> imis = mInputMethodSettingValues.getInputMethodList();
    413             final int N = (imis == null ? 0 : imis.size());
    414             for (int i = 0; i < N; ++i) {
    415                 final InputMethodInfo imi = imis.get(i);
    416                 final InputMethodPreference pref = getInputMethodPreference(imi);
    417                 pref.setOnImePreferenceChangeListener(mOnImePreferenceChangedListener);
    418                 mInputMethodPreferenceList.add(pref);
    419             }
    420 
    421             if (!mInputMethodPreferenceList.isEmpty()) {
    422                 Collections.sort(mInputMethodPreferenceList);
    423                 for (int i = 0; i < N; ++i) {
    424                     mKeyboardSettingsCategory.addPreference(mInputMethodPreferenceList.get(i));
    425                 }
    426             }
    427 
    428             // update views status
    429             for (Preference pref : mInputMethodPreferenceList) {
    430                 if (pref instanceof InputMethodPreference) {
    431                     ((InputMethodPreference) pref).updatePreferenceViews();
    432                 }
    433             }
    434         }
    435         updateCurrentImeName();
    436         // TODO: Consolidate the logic with InputMethodSettingsWrapper
    437         // CAVEAT: The preference class here does not know about the default value - that is
    438         // managed by the Input Method Manager Service, so in this case it could save the wrong
    439         // value. Hence we must update the checkboxes here.
    440         InputMethodAndSubtypeUtil.loadInputMethodSubtypeList(
    441                 this, getContentResolver(),
    442                 mInputMethodSettingValues.getInputMethodList(), null);
    443     }
    444 
    445     private void updateCurrentImeName() {
    446         final Context context = getActivity();
    447         if (context == null || mImm == null) return;
    448         final Preference curPref = getPreferenceScreen().findPreference(KEY_CURRENT_INPUT_METHOD);
    449         if (curPref != null) {
    450             final CharSequence curIme =
    451                     mInputMethodSettingValues.getCurrentInputMethodName(context);
    452             if (!TextUtils.isEmpty(curIme)) {
    453                 synchronized(this) {
    454                     curPref.setSummary(curIme);
    455                 }
    456             }
    457         }
    458     }
    459 
    460     private InputMethodPreference getInputMethodPreference(InputMethodInfo imi) {
    461         final PackageManager pm = getPackageManager();
    462         final CharSequence label = imi.loadLabel(pm);
    463         // IME settings
    464         final Intent intent;
    465         final String settingsActivity = imi.getSettingsActivity();
    466         if (!TextUtils.isEmpty(settingsActivity)) {
    467             intent = new Intent(Intent.ACTION_MAIN);
    468             intent.setClassName(imi.getPackageName(), settingsActivity);
    469         } else {
    470             intent = null;
    471         }
    472 
    473         // Add a check box for enabling/disabling IME
    474         final InputMethodPreference pref =
    475                 new InputMethodPreference(this, intent, mImm, imi);
    476         pref.setKey(imi.getId());
    477         pref.setTitle(label);
    478         return pref;
    479     }
    480 
    481     private void updateInputDevices() {
    482         updateHardKeyboards();
    483         updateGameControllers();
    484     }
    485 
    486     private void updateHardKeyboards() {
    487         mHardKeyboardPreferenceList.clear();
    488         if (getResources().getConfiguration().keyboard == Configuration.KEYBOARD_QWERTY) {
    489             final int[] devices = InputDevice.getDeviceIds();
    490             for (int i = 0; i < devices.length; i++) {
    491                 InputDevice device = InputDevice.getDevice(devices[i]);
    492                 if (device != null
    493                         && !device.isVirtual()
    494                         && device.isFullKeyboard()) {
    495                     final String inputDeviceDescriptor = device.getDescriptor();
    496                     final String keyboardLayoutDescriptor =
    497                             mIm.getCurrentKeyboardLayoutForInputDevice(inputDeviceDescriptor);
    498                     final KeyboardLayout keyboardLayout = keyboardLayoutDescriptor != null ?
    499                             mIm.getKeyboardLayout(keyboardLayoutDescriptor) : null;
    500 
    501                     final PreferenceScreen pref = new PreferenceScreen(getActivity(), null);
    502                     pref.setTitle(device.getName());
    503                     if (keyboardLayout != null) {
    504                         pref.setSummary(keyboardLayout.toString());
    505                     } else {
    506                         pref.setSummary(R.string.keyboard_layout_default_label);
    507                     }
    508                     pref.setOnPreferenceClickListener(new Preference.OnPreferenceClickListener() {
    509                         @Override
    510                         public boolean onPreferenceClick(Preference preference) {
    511                             showKeyboardLayoutDialog(inputDeviceDescriptor);
    512                             return true;
    513                         }
    514                     });
    515                     mHardKeyboardPreferenceList.add(pref);
    516                 }
    517             }
    518         }
    519 
    520         if (!mHardKeyboardPreferenceList.isEmpty()) {
    521             for (int i = mHardKeyboardCategory.getPreferenceCount(); i-- > 0; ) {
    522                 final Preference pref = mHardKeyboardCategory.getPreference(i);
    523                 if (pref.getOrder() < 1000) {
    524                     mHardKeyboardCategory.removePreference(pref);
    525                 }
    526             }
    527 
    528             Collections.sort(mHardKeyboardPreferenceList);
    529             final int count = mHardKeyboardPreferenceList.size();
    530             for (int i = 0; i < count; i++) {
    531                 final Preference pref = mHardKeyboardPreferenceList.get(i);
    532                 pref.setOrder(i);
    533                 mHardKeyboardCategory.addPreference(pref);
    534             }
    535 
    536             getPreferenceScreen().addPreference(mHardKeyboardCategory);
    537         } else {
    538             getPreferenceScreen().removePreference(mHardKeyboardCategory);
    539         }
    540     }
    541 
    542     private void showKeyboardLayoutDialog(String inputDeviceDescriptor) {
    543         KeyboardLayoutDialogFragment fragment =
    544                 new KeyboardLayoutDialogFragment(inputDeviceDescriptor);
    545         fragment.setTargetFragment(this, 0);
    546         fragment.show(getActivity().getFragmentManager(), "keyboardLayout");
    547     }
    548 
    549     @Override
    550     public void onSetupKeyboardLayouts(String inputDeviceDescriptor) {
    551         final Intent intent = new Intent(Intent.ACTION_MAIN);
    552         intent.setClass(getActivity(), KeyboardLayoutPickerActivity.class);
    553         intent.putExtra(KeyboardLayoutPickerFragment.EXTRA_INPUT_DEVICE_DESCRIPTOR,
    554                 inputDeviceDescriptor);
    555         mIntentWaitingForResult = intent;
    556         startActivityForResult(intent, 0);
    557     }
    558 
    559     @Override
    560     public void onActivityResult(int requestCode, int resultCode, Intent data) {
    561         super.onActivityResult(requestCode, resultCode, data);
    562 
    563         if (mIntentWaitingForResult != null) {
    564             String inputDeviceDescriptor = mIntentWaitingForResult.getStringExtra(
    565                     KeyboardLayoutPickerFragment.EXTRA_INPUT_DEVICE_DESCRIPTOR);
    566             mIntentWaitingForResult = null;
    567             showKeyboardLayoutDialog(inputDeviceDescriptor);
    568         }
    569     }
    570 
    571     private void updateGameControllers() {
    572         if (haveInputDeviceWithVibrator()) {
    573             getPreferenceScreen().addPreference(mGameControllerCategory);
    574 
    575             CheckBoxPreference chkPref = (CheckBoxPreference)
    576                     mGameControllerCategory.findPreference("vibrate_input_devices");
    577             chkPref.setChecked(System.getInt(getContentResolver(),
    578                     Settings.System.VIBRATE_INPUT_DEVICES, 1) > 0);
    579         } else {
    580             getPreferenceScreen().removePreference(mGameControllerCategory);
    581         }
    582     }
    583 
    584     private boolean haveInputDeviceWithVibrator() {
    585         final int[] devices = InputDevice.getDeviceIds();
    586         for (int i = 0; i < devices.length; i++) {
    587             InputDevice device = InputDevice.getDevice(devices[i]);
    588             if (device != null && !device.isVirtual() && device.getVibrator().hasVibrator()) {
    589                 return true;
    590             }
    591         }
    592         return false;
    593     }
    594 
    595     private class SettingsObserver extends ContentObserver {
    596         private Context mContext;
    597 
    598         public SettingsObserver(Handler handler, Context context) {
    599             super(handler);
    600             mContext = context;
    601         }
    602 
    603         @Override public void onChange(boolean selfChange) {
    604             updateCurrentImeName();
    605         }
    606 
    607         public void resume() {
    608             final ContentResolver cr = mContext.getContentResolver();
    609             cr.registerContentObserver(
    610                     Settings.Secure.getUriFor(Settings.Secure.DEFAULT_INPUT_METHOD), false, this);
    611             cr.registerContentObserver(Settings.Secure.getUriFor(
    612                     Settings.Secure.SELECTED_INPUT_METHOD_SUBTYPE), false, this);
    613         }
    614 
    615         public void pause() {
    616             mContext.getContentResolver().unregisterContentObserver(this);
    617         }
    618     }
    619 }
    620