2 * Copyright (C) 2008 The Android Open Source Project
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
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,
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.
17 package com.android.settings.inputmethod;
19 import android.app.Activity;
20 import android.app.Fragment;
21 import android.app.admin.DevicePolicyManager;
22 import android.content.ComponentName;
23 import android.content.ContentResolver;
24 import android.content.Context;
25 import android.content.Intent;
26 import android.content.SharedPreferences;
27 import android.content.pm.ServiceInfo;
28 import android.content.res.Configuration;
29 import android.database.ContentObserver;
30 import android.hardware.input.InputDeviceIdentifier;
31 import android.hardware.input.InputManager;
32 import android.hardware.input.KeyboardLayout;
33 import android.os.Bundle;
34 import android.os.Handler;
35 import android.preference.ListPreference;
36 import android.preference.Preference;
37 import android.preference.Preference.OnPreferenceClickListener;
38 import android.preference.PreferenceCategory;
39 import android.preference.PreferenceManager;
40 import android.preference.PreferenceScreen;
41 import android.preference.SwitchPreference;
42 import android.provider.Settings;
43 import android.provider.Settings.System;
44 import android.speech.tts.TtsEngines;
45 import android.text.TextUtils;
46 import android.view.InputDevice;
47 import android.view.inputmethod.InputMethodInfo;
48 import android.view.inputmethod.InputMethodManager;
49 import android.view.inputmethod.InputMethodSubtype;
50 import android.view.textservice.SpellCheckerInfo;
51 import android.view.textservice.TextServicesManager;
53 import com.android.internal.app.LocalePicker;
54 import com.android.internal.logging.MetricsLogger;
55 import com.android.settings.R;
56 import com.android.settings.Settings.KeyboardLayoutPickerActivity;
57 import com.android.settings.SettingsActivity;
58 import com.android.settings.SettingsPreferenceFragment;
59 import com.android.settings.SubSettings;
60 import com.android.settings.UserDictionarySettings;
61 import com.android.settings.Utils;
62 import com.android.settings.VoiceInputOutputSettings;
63 import com.android.settings.search.BaseSearchIndexProvider;
64 import com.android.settings.search.Indexable;
65 import com.android.settings.search.SearchIndexableRaw;
67 import java.text.Collator;
68 import java.util.ArrayList;
69 import java.util.Collections;
70 import java.util.Comparator;
71 import java.util.HashMap;
72 import java.util.HashSet;
73 import java.util.List;
74 import java.util.Locale;
75 import java.util.TreeSet;
77 public class InputMethodAndLanguageSettings extends SettingsPreferenceFragment
78 implements Preference.OnPreferenceChangeListener, InputManager.InputDeviceListener,
79 KeyboardLayoutDialogFragment.OnSetupKeyboardLayoutsListener, Indexable,
80 InputMethodPreference.OnSavePreferenceListener {
81 private static final String KEY_SPELL_CHECKERS = "spellcheckers_settings";
82 private static final String KEY_PHONE_LANGUAGE = "phone_language";
83 private static final String KEY_CURRENT_INPUT_METHOD = "current_input_method";
84 private static final String KEY_INPUT_METHOD_SELECTOR = "input_method_selector";
85 private static final String KEY_USER_DICTIONARY_SETTINGS = "key_user_dictionary_settings";
86 private static final String KEY_PREVIOUSLY_ENABLED_SUBTYPES = "previously_enabled_subtypes";
87 // false: on ICS or later
88 private static final boolean SHOW_INPUT_METHOD_SWITCHER_SETTINGS = false;
90 private int mDefaultInputMethodSelectorVisibility = 0;
91 private ListPreference mShowInputMethodSelectorPref;
92 private PreferenceCategory mKeyboardSettingsCategory;
93 private PreferenceCategory mHardKeyboardCategory;
94 private PreferenceCategory mGameControllerCategory;
95 private Preference mLanguagePref;
96 private final ArrayList<InputMethodPreference> mInputMethodPreferenceList = new ArrayList<>();
97 private final ArrayList<PreferenceScreen> mHardKeyboardPreferenceList = new ArrayList<>();
98 private InputManager mIm;
99 private InputMethodManager mImm;
100 private boolean mShowsOnlyFullImeAndKeyboardList;
101 private Handler mHandler;
102 private SettingsObserver mSettingsObserver;
103 private Intent mIntentWaitingForResult;
104 private InputMethodSettingValuesWrapper mInputMethodSettingValues;
105 private DevicePolicyManager mDpm;
108 protected int getMetricsCategory() {
109 return MetricsLogger.INPUTMETHOD_LANGUAGE;
113 public void onCreate(Bundle icicle) {
114 super.onCreate(icicle);
116 addPreferencesFromResource(R.xml.language_settings);
118 final Activity activity = getActivity();
119 mImm = (InputMethodManager) getSystemService(Context.INPUT_METHOD_SERVICE);
120 mInputMethodSettingValues = InputMethodSettingValuesWrapper.getInstance(activity);
123 mDefaultInputMethodSelectorVisibility = Integer.valueOf(
124 getString(R.string.input_method_selector_visibility_default_value));
125 } catch (NumberFormatException e) {
128 if (activity.getAssets().getLocales().length == 1) {
129 // No "Select language" pref if there's only one system locale available.
130 getPreferenceScreen().removePreference(findPreference(KEY_PHONE_LANGUAGE));
132 mLanguagePref = findPreference(KEY_PHONE_LANGUAGE);
134 if (SHOW_INPUT_METHOD_SWITCHER_SETTINGS) {
135 mShowInputMethodSelectorPref = (ListPreference)findPreference(
136 KEY_INPUT_METHOD_SELECTOR);
137 mShowInputMethodSelectorPref.setOnPreferenceChangeListener(this);
138 // TODO: Update current input method name on summary
139 updateInputMethodSelectorSummary(loadInputMethodSelectorVisibility());
142 new VoiceInputOutputSettings(this).onCreate();
144 // Get references to dynamically constructed categories.
145 mHardKeyboardCategory = (PreferenceCategory)findPreference("hard_keyboard");
146 mKeyboardSettingsCategory = (PreferenceCategory)findPreference(
147 "keyboard_settings_category");
148 mGameControllerCategory = (PreferenceCategory)findPreference(
149 "game_controller_settings_category");
151 final Intent startingIntent = activity.getIntent();
152 // Filter out irrelevant features if invoked from IME settings button.
153 mShowsOnlyFullImeAndKeyboardList = Settings.ACTION_INPUT_METHOD_SETTINGS.equals(
154 startingIntent.getAction());
155 if (mShowsOnlyFullImeAndKeyboardList) {
156 getPreferenceScreen().removeAll();
157 getPreferenceScreen().addPreference(mHardKeyboardCategory);
158 if (SHOW_INPUT_METHOD_SWITCHER_SETTINGS) {
159 getPreferenceScreen().addPreference(mShowInputMethodSelectorPref);
161 mKeyboardSettingsCategory.removeAll();
162 getPreferenceScreen().addPreference(mKeyboardSettingsCategory);
165 // Build hard keyboard and game controller preference categories.
166 mIm = (InputManager)activity.getSystemService(Context.INPUT_SERVICE);
167 updateInputDevices();
170 final Preference spellChecker = findPreference(KEY_SPELL_CHECKERS);
171 if (spellChecker != null) {
172 // Note: KEY_SPELL_CHECKERS preference is marked as persistent="false" in XML.
173 InputMethodAndSubtypeUtil.removeUnnecessaryNonPersistentPreference(spellChecker);
174 final Intent intent = new Intent(Intent.ACTION_MAIN);
175 intent.setClass(activity, SubSettings.class);
176 intent.putExtra(SettingsActivity.EXTRA_SHOW_FRAGMENT,
177 SpellCheckersSettings.class.getName());
178 intent.putExtra(SettingsActivity.EXTRA_SHOW_FRAGMENT_TITLE_RESID,
179 R.string.spellcheckers_settings_title);
180 spellChecker.setIntent(intent);
183 mHandler = new Handler();
184 mSettingsObserver = new SettingsObserver(mHandler, activity);
185 mDpm = (DevicePolicyManager) (getActivity().
186 getSystemService(Context.DEVICE_POLICY_SERVICE));
188 // If we've launched from the keyboard layout notification, go ahead and just show the
189 // keyboard layout dialog.
190 final InputDeviceIdentifier identifier =
191 startingIntent.getParcelableExtra(Settings.EXTRA_INPUT_DEVICE_IDENTIFIER);
192 if (mShowsOnlyFullImeAndKeyboardList && identifier != null) {
193 showKeyboardLayoutDialog(identifier);
197 private void updateInputMethodSelectorSummary(int value) {
198 String[] inputMethodSelectorTitles = getResources().getStringArray(
199 R.array.input_method_selector_titles);
200 if (inputMethodSelectorTitles.length > value) {
201 mShowInputMethodSelectorPref.setSummary(inputMethodSelectorTitles[value]);
202 mShowInputMethodSelectorPref.setValue(String.valueOf(value));
206 private void updateUserDictionaryPreference(Preference userDictionaryPreference) {
207 final Activity activity = getActivity();
208 final TreeSet<String> localeSet = UserDictionaryList.getUserDictionaryLocalesSet(activity);
209 if (null == localeSet) {
210 // The locale list is null if and only if the user dictionary service is
211 // not present or disabled. In this case we need to remove the preference.
212 getPreferenceScreen().removePreference(userDictionaryPreference);
214 userDictionaryPreference.setOnPreferenceClickListener(
215 new OnPreferenceClickListener() {
217 public boolean onPreferenceClick(Preference arg0) {
218 // Redirect to UserDictionarySettings if the user needs only one
220 final Bundle extras = new Bundle();
221 final Class<? extends Fragment> targetFragment;
222 if (localeSet.size() <= 1) {
223 if (!localeSet.isEmpty()) {
224 // If the size of localeList is 0, we don't set the locale
225 // parameter in the extras. This will be interpreted by the
226 // UserDictionarySettings class as meaning
227 // "the current locale". Note that with the current code for
228 // UserDictionaryList#getUserDictionaryLocalesSet()
229 // the locale list always has at least one element, since it
230 // always includes the current locale explicitly.
231 // @see UserDictionaryList.getUserDictionaryLocalesSet().
232 extras.putString("locale", localeSet.first());
234 targetFragment = UserDictionarySettings.class;
236 targetFragment = UserDictionaryList.class;
238 startFragment(InputMethodAndLanguageSettings.this,
239 targetFragment.getCanonicalName(), -1, -1, extras);
247 public void onResume() {
250 mSettingsObserver.resume();
251 mIm.registerInputDeviceListener(this, null);
253 final Preference spellChecker = findPreference(KEY_SPELL_CHECKERS);
254 if (spellChecker != null) {
255 final TextServicesManager tsm = (TextServicesManager) getSystemService(
256 Context.TEXT_SERVICES_MANAGER_SERVICE);
257 if (tsm.isSpellCheckerEnabled()) {
258 final SpellCheckerInfo sci = tsm.getCurrentSpellChecker();
259 spellChecker.setSummary(sci.loadLabel(getPackageManager()));
261 spellChecker.setSummary(R.string.switch_off_text);
265 if (!mShowsOnlyFullImeAndKeyboardList) {
266 if (mLanguagePref != null) {
267 String localeName = getLocaleName(getActivity());
268 mLanguagePref.setSummary(localeName);
271 updateUserDictionaryPreference(findPreference(KEY_USER_DICTIONARY_SETTINGS));
272 if (SHOW_INPUT_METHOD_SWITCHER_SETTINGS) {
273 mShowInputMethodSelectorPref.setOnPreferenceChangeListener(this);
277 updateInputDevices();
279 // Refresh internal states in mInputMethodSettingValues to keep the latest
280 // "InputMethodInfo"s and "InputMethodSubtype"s
281 mInputMethodSettingValues.refreshAllInputMethodAndSubtypes();
282 updateInputMethodPreferenceViews();
286 public void onPause() {
289 mIm.unregisterInputDeviceListener(this);
290 mSettingsObserver.pause();
292 if (SHOW_INPUT_METHOD_SWITCHER_SETTINGS) {
293 mShowInputMethodSelectorPref.setOnPreferenceChangeListener(null);
295 // TODO: Consolidate the logic to InputMethodSettingsWrapper
296 InputMethodAndSubtypeUtil.saveInputMethodSubtypeList(
297 this, getContentResolver(), mInputMethodSettingValues.getInputMethodList(),
298 !mHardKeyboardPreferenceList.isEmpty());
302 public void onInputDeviceAdded(int deviceId) {
303 updateInputDevices();
307 public void onInputDeviceChanged(int deviceId) {
308 updateInputDevices();
312 public void onInputDeviceRemoved(int deviceId) {
313 updateInputDevices();
317 public boolean onPreferenceTreeClick(PreferenceScreen preferenceScreen, Preference preference) {
318 // Input Method stuff
319 if (Utils.isMonkeyRunning()) {
322 if (preference instanceof PreferenceScreen) {
323 if (preference.getFragment() != null) {
324 // Fragment will be handled correctly by the super class.
325 } else if (KEY_CURRENT_INPUT_METHOD.equals(preference.getKey())) {
326 final InputMethodManager imm = (InputMethodManager)
327 getSystemService(Context.INPUT_METHOD_SERVICE);
328 imm.showInputMethodPicker(false /* showAuxiliarySubtypes */);
330 } else if (preference instanceof SwitchPreference) {
331 final SwitchPreference pref = (SwitchPreference) preference;
332 if (pref == mGameControllerCategory.findPreference("vibrate_input_devices")) {
333 System.putInt(getContentResolver(), Settings.System.VIBRATE_INPUT_DEVICES,
334 pref.isChecked() ? 1 : 0);
338 return super.onPreferenceTreeClick(preferenceScreen, preference);
341 private static String getLocaleName(Context context) {
342 // We want to show the same string that the LocalePicker used.
343 // TODO: should this method be in LocalePicker instead?
344 Locale currentLocale = context.getResources().getConfiguration().locale;
345 List<LocalePicker.LocaleInfo> locales = LocalePicker.getAllAssetLocales(context, true);
346 for (LocalePicker.LocaleInfo locale : locales) {
347 if (locale.getLocale().equals(currentLocale)) {
348 return locale.getLabel();
351 // This can't happen as long as the locale was one set by Settings.
352 // Fall back in case a developer is testing an unsupported locale.
353 return currentLocale.getDisplayName(currentLocale);
356 private void saveInputMethodSelectorVisibility(String value) {
358 int intValue = Integer.valueOf(value);
359 Settings.Secure.putInt(getContentResolver(),
360 Settings.Secure.INPUT_METHOD_SELECTOR_VISIBILITY, intValue);
361 updateInputMethodSelectorSummary(intValue);
362 } catch(NumberFormatException e) {
366 private int loadInputMethodSelectorVisibility() {
367 return Settings.Secure.getInt(getContentResolver(),
368 Settings.Secure.INPUT_METHOD_SELECTOR_VISIBILITY,
369 mDefaultInputMethodSelectorVisibility);
373 public boolean onPreferenceChange(Preference preference, Object value) {
374 if (SHOW_INPUT_METHOD_SWITCHER_SETTINGS) {
375 if (preference == mShowInputMethodSelectorPref) {
376 if (value instanceof String) {
377 saveInputMethodSelectorVisibility((String)value);
384 private void updateInputMethodPreferenceViews() {
385 synchronized (mInputMethodPreferenceList) {
386 // Clear existing "InputMethodPreference"s
387 for (final InputMethodPreference pref : mInputMethodPreferenceList) {
388 mKeyboardSettingsCategory.removePreference(pref);
390 mInputMethodPreferenceList.clear();
391 List<String> permittedList = mDpm.getPermittedInputMethodsForCurrentUser();
392 final Context context = getActivity();
393 final List<InputMethodInfo> imis = mShowsOnlyFullImeAndKeyboardList
394 ? mInputMethodSettingValues.getInputMethodList()
395 : mImm.getEnabledInputMethodList();
396 final int N = (imis == null ? 0 : imis.size());
397 for (int i = 0; i < N; ++i) {
398 final InputMethodInfo imi = imis.get(i);
399 final boolean isAllowedByOrganization = permittedList == null
400 || permittedList.contains(imi.getPackageName());
401 final InputMethodPreference pref = new InputMethodPreference(
402 context, imi, mShowsOnlyFullImeAndKeyboardList /* hasSwitch */,
403 isAllowedByOrganization, this);
404 mInputMethodPreferenceList.add(pref);
406 final Collator collator = Collator.getInstance();
407 Collections.sort(mInputMethodPreferenceList, new Comparator<InputMethodPreference>() {
409 public int compare(InputMethodPreference lhs, InputMethodPreference rhs) {
410 return lhs.compareTo(rhs, collator);
413 for (int i = 0; i < N; ++i) {
414 final InputMethodPreference pref = mInputMethodPreferenceList.get(i);
415 mKeyboardSettingsCategory.addPreference(pref);
416 InputMethodAndSubtypeUtil.removeUnnecessaryNonPersistentPreference(pref);
417 pref.updatePreferenceViews();
420 updateCurrentImeName();
421 // TODO: Consolidate the logic with InputMethodSettingsWrapper
422 // CAVEAT: The preference class here does not know about the default value - that is
423 // managed by the Input Method Manager Service, so in this case it could save the wrong
424 // value. Hence we must update the checkboxes here.
425 InputMethodAndSubtypeUtil.loadInputMethodSubtypeList(
426 this, getContentResolver(),
427 mInputMethodSettingValues.getInputMethodList(), null);
431 public void onSaveInputMethodPreference(final InputMethodPreference pref) {
432 final InputMethodInfo imi = pref.getInputMethodInfo();
433 if (!pref.isChecked()) {
434 // An IME is being disabled. Save enabled subtypes of the IME to shared preference to be
435 // able to re-enable these subtypes when the IME gets re-enabled.
436 saveEnabledSubtypesOf(imi);
438 final boolean hasHardwareKeyboard = getResources().getConfiguration().keyboard
439 == Configuration.KEYBOARD_QWERTY;
440 InputMethodAndSubtypeUtil.saveInputMethodSubtypeList(this, getContentResolver(),
441 mImm.getInputMethodList(), hasHardwareKeyboard);
442 // Update input method settings and preference list.
443 mInputMethodSettingValues.refreshAllInputMethodAndSubtypes();
444 if (pref.isChecked()) {
445 // An IME is being enabled. Load the previously enabled subtypes from shared preference
446 // and enable these subtypes.
447 restorePreviouslyEnabledSubtypesOf(imi);
449 for (final InputMethodPreference p : mInputMethodPreferenceList) {
450 p.updatePreferenceViews();
454 private void saveEnabledSubtypesOf(final InputMethodInfo imi) {
455 final HashSet<String> enabledSubtypeIdSet = new HashSet<>();
456 final List<InputMethodSubtype> enabledSubtypes = mImm.getEnabledInputMethodSubtypeList(
457 imi, true /* allowsImplicitlySelectedSubtypes */);
458 for (final InputMethodSubtype subtype : enabledSubtypes) {
459 final String subtypeId = Integer.toString(subtype.hashCode());
460 enabledSubtypeIdSet.add(subtypeId);
462 final HashMap<String, HashSet<String>> imeToEnabledSubtypeIdsMap =
463 loadPreviouslyEnabledSubtypeIdsMap();
464 final String imiId = imi.getId();
465 imeToEnabledSubtypeIdsMap.put(imiId, enabledSubtypeIdSet);
466 savePreviouslyEnabledSubtypeIdsMap(imeToEnabledSubtypeIdsMap);
469 private void restorePreviouslyEnabledSubtypesOf(final InputMethodInfo imi) {
470 final HashMap<String, HashSet<String>> imeToEnabledSubtypeIdsMap =
471 loadPreviouslyEnabledSubtypeIdsMap();
472 final String imiId = imi.getId();
473 final HashSet<String> enabledSubtypeIdSet = imeToEnabledSubtypeIdsMap.remove(imiId);
474 if (enabledSubtypeIdSet == null) {
477 savePreviouslyEnabledSubtypeIdsMap(imeToEnabledSubtypeIdsMap);
478 InputMethodAndSubtypeUtil.enableInputMethodSubtypesOf(
479 getContentResolver(), imiId, enabledSubtypeIdSet);
482 private HashMap<String, HashSet<String>> loadPreviouslyEnabledSubtypeIdsMap() {
483 final Context context = getActivity();
484 final SharedPreferences prefs = PreferenceManager.getDefaultSharedPreferences(context);
485 final String imesAndSubtypesString = prefs.getString(KEY_PREVIOUSLY_ENABLED_SUBTYPES, null);
486 return InputMethodAndSubtypeUtil.parseInputMethodsAndSubtypesString(imesAndSubtypesString);
489 private void savePreviouslyEnabledSubtypeIdsMap(
490 final HashMap<String, HashSet<String>> subtypesMap) {
491 final Context context = getActivity();
492 final SharedPreferences prefs = PreferenceManager.getDefaultSharedPreferences(context);
493 final String imesAndSubtypesString = InputMethodAndSubtypeUtil
494 .buildInputMethodsAndSubtypesString(subtypesMap);
495 prefs.edit().putString(KEY_PREVIOUSLY_ENABLED_SUBTYPES, imesAndSubtypesString).apply();
498 private void updateCurrentImeName() {
499 final Context context = getActivity();
500 if (context == null || mImm == null) return;
501 final Preference curPref = getPreferenceScreen().findPreference(KEY_CURRENT_INPUT_METHOD);
502 if (curPref != null) {
503 final CharSequence curIme =
504 mInputMethodSettingValues.getCurrentInputMethodName(context);
505 if (!TextUtils.isEmpty(curIme)) {
506 synchronized (this) {
507 curPref.setSummary(curIme);
513 private void updateInputDevices() {
514 updateHardKeyboards();
515 updateGameControllers();
518 private void updateHardKeyboards() {
519 mHardKeyboardPreferenceList.clear();
520 final int[] devices = InputDevice.getDeviceIds();
521 for (int i = 0; i < devices.length; i++) {
522 InputDevice device = InputDevice.getDevice(devices[i]);
524 && !device.isVirtual()
525 && device.isFullKeyboard()) {
526 final InputDeviceIdentifier identifier = device.getIdentifier();
527 final String keyboardLayoutDescriptor =
528 mIm.getCurrentKeyboardLayoutForInputDevice(identifier);
529 final KeyboardLayout keyboardLayout = keyboardLayoutDescriptor != null ?
530 mIm.getKeyboardLayout(keyboardLayoutDescriptor) : null;
532 final PreferenceScreen pref = new PreferenceScreen(getActivity(), null);
533 pref.setTitle(device.getName());
534 if (keyboardLayout != null) {
535 pref.setSummary(keyboardLayout.toString());
537 pref.setSummary(R.string.keyboard_layout_default_label);
539 pref.setOnPreferenceClickListener(new Preference.OnPreferenceClickListener() {
541 public boolean onPreferenceClick(Preference preference) {
542 showKeyboardLayoutDialog(identifier);
546 mHardKeyboardPreferenceList.add(pref);
550 if (!mHardKeyboardPreferenceList.isEmpty()) {
551 for (int i = mHardKeyboardCategory.getPreferenceCount(); i-- > 0; ) {
552 final Preference pref = mHardKeyboardCategory.getPreference(i);
553 if (pref.getOrder() < 1000) {
554 mHardKeyboardCategory.removePreference(pref);
558 Collections.sort(mHardKeyboardPreferenceList);
559 final int count = mHardKeyboardPreferenceList.size();
560 for (int i = 0; i < count; i++) {
561 final Preference pref = mHardKeyboardPreferenceList.get(i);
563 mHardKeyboardCategory.addPreference(pref);
566 getPreferenceScreen().addPreference(mHardKeyboardCategory);
568 getPreferenceScreen().removePreference(mHardKeyboardCategory);
572 private void showKeyboardLayoutDialog(InputDeviceIdentifier inputDeviceIdentifier) {
573 KeyboardLayoutDialogFragment fragment = new KeyboardLayoutDialogFragment(
574 inputDeviceIdentifier);
575 fragment.setTargetFragment(this, 0);
576 fragment.show(getActivity().getFragmentManager(), "keyboardLayout");
580 public void onSetupKeyboardLayouts(InputDeviceIdentifier inputDeviceIdentifier) {
581 final Intent intent = new Intent(Intent.ACTION_MAIN);
582 intent.setClass(getActivity(), KeyboardLayoutPickerActivity.class);
583 intent.putExtra(KeyboardLayoutPickerFragment.EXTRA_INPUT_DEVICE_IDENTIFIER,
584 inputDeviceIdentifier);
585 mIntentWaitingForResult = intent;
586 startActivityForResult(intent, 0);
590 public void onActivityResult(int requestCode, int resultCode, Intent data) {
591 super.onActivityResult(requestCode, resultCode, data);
593 if (mIntentWaitingForResult != null) {
594 InputDeviceIdentifier inputDeviceIdentifier = mIntentWaitingForResult
595 .getParcelableExtra(KeyboardLayoutPickerFragment.EXTRA_INPUT_DEVICE_IDENTIFIER);
596 mIntentWaitingForResult = null;
597 showKeyboardLayoutDialog(inputDeviceIdentifier);
601 private void updateGameControllers() {
602 if (haveInputDeviceWithVibrator()) {
603 getPreferenceScreen().addPreference(mGameControllerCategory);
605 SwitchPreference pref = (SwitchPreference)
606 mGameControllerCategory.findPreference("vibrate_input_devices");
607 pref.setChecked(System.getInt(getContentResolver(),
608 Settings.System.VIBRATE_INPUT_DEVICES, 1) > 0);
610 getPreferenceScreen().removePreference(mGameControllerCategory);
614 private static boolean haveInputDeviceWithVibrator() {
615 final int[] devices = InputDevice.getDeviceIds();
616 for (int i = 0; i < devices.length; i++) {
617 InputDevice device = InputDevice.getDevice(devices[i]);
618 if (device != null && !device.isVirtual() && device.getVibrator().hasVibrator()) {
625 private class SettingsObserver extends ContentObserver {
626 private Context mContext;
628 public SettingsObserver(Handler handler, Context context) {
633 @Override public void onChange(boolean selfChange) {
634 updateCurrentImeName();
637 public void resume() {
638 final ContentResolver cr = mContext.getContentResolver();
639 cr.registerContentObserver(
640 Settings.Secure.getUriFor(Settings.Secure.DEFAULT_INPUT_METHOD), false, this);
641 cr.registerContentObserver(Settings.Secure.getUriFor(
642 Settings.Secure.SELECTED_INPUT_METHOD_SUBTYPE), false, this);
645 public void pause() {
646 mContext.getContentResolver().unregisterContentObserver(this);
650 public static final Indexable.SearchIndexProvider SEARCH_INDEX_DATA_PROVIDER =
651 new BaseSearchIndexProvider() {
653 public List<SearchIndexableRaw> getRawDataToIndex(Context context, boolean enabled) {
654 List<SearchIndexableRaw> indexables = new ArrayList<>();
656 final String screenTitle = context.getString(R.string.language_keyboard_settings_title);
659 if (context.getAssets().getLocales().length > 1) {
660 String localeName = getLocaleName(context);
661 SearchIndexableRaw indexable = new SearchIndexableRaw(context);
662 indexable.key = KEY_PHONE_LANGUAGE;
663 indexable.title = context.getString(R.string.phone_language);
664 indexable.summaryOn = localeName;
665 indexable.summaryOff = localeName;
666 indexable.screenTitle = screenTitle;
667 indexables.add(indexable);
671 SearchIndexableRaw indexable = new SearchIndexableRaw(context);
672 indexable.key = KEY_SPELL_CHECKERS;
673 indexable.title = context.getString(R.string.spellcheckers_settings_title);
674 indexable.screenTitle = screenTitle;
675 indexable.keywords = context.getString(R.string.keywords_spell_checker);
676 indexables.add(indexable);
679 if (UserDictionaryList.getUserDictionaryLocalesSet(context) != null) {
680 indexable = new SearchIndexableRaw(context);
681 indexable.key = "user_dict_settings";
682 indexable.title = context.getString(R.string.user_dict_settings_title);
683 indexable.screenTitle = screenTitle;
684 indexables.add(indexable);
687 // Keyboard settings.
688 indexable = new SearchIndexableRaw(context);
689 indexable.key = "keyboard_settings";
690 indexable.title = context.getString(R.string.keyboard_settings_category);
691 indexable.screenTitle = screenTitle;
692 indexable.keywords = context.getString(R.string.keywords_keyboard_and_ime);
693 indexables.add(indexable);
695 InputMethodSettingValuesWrapper immValues = InputMethodSettingValuesWrapper
696 .getInstance(context);
697 immValues.refreshAllInputMethodAndSubtypes();
700 String currImeName = immValues.getCurrentInputMethodName(context).toString();
701 indexable = new SearchIndexableRaw(context);
702 indexable.key = KEY_CURRENT_INPUT_METHOD;
703 indexable.title = context.getString(R.string.current_input_method);
704 indexable.summaryOn = currImeName;
705 indexable.summaryOff = currImeName;
706 indexable.screenTitle = screenTitle;
707 indexables.add(indexable);
709 InputMethodManager inputMethodManager = (InputMethodManager) context.getSystemService(
710 Context.INPUT_METHOD_SERVICE);
713 List<InputMethodInfo> inputMethods = immValues.getInputMethodList();
714 final int inputMethodCount = (inputMethods == null ? 0 : inputMethods.size());
715 for (int i = 0; i < inputMethodCount; ++i) {
716 InputMethodInfo inputMethod = inputMethods.get(i);
718 StringBuilder builder = new StringBuilder();
719 List<InputMethodSubtype> subtypes = inputMethodManager
720 .getEnabledInputMethodSubtypeList(inputMethod, true);
721 final int subtypeCount = subtypes.size();
722 for (int j = 0; j < subtypeCount; j++) {
723 InputMethodSubtype subtype = subtypes.get(j);
724 if (builder.length() > 0) {
727 CharSequence subtypeLabel = subtype.getDisplayName(context,
728 inputMethod.getPackageName(), inputMethod.getServiceInfo()
730 builder.append(subtypeLabel);
732 String summary = builder.toString();
734 ServiceInfo serviceInfo = inputMethod.getServiceInfo();
735 ComponentName componentName = new ComponentName(serviceInfo.packageName,
738 indexable = new SearchIndexableRaw(context);
739 indexable.key = componentName.flattenToString();
740 indexable.title = inputMethod.loadLabel(context.getPackageManager()).toString();
741 indexable.summaryOn = summary;
742 indexable.summaryOff = summary;
743 indexable.screenTitle = screenTitle;
744 indexables.add(indexable);
748 InputManager inputManager = (InputManager) context.getSystemService(
749 Context.INPUT_SERVICE);
750 boolean hasHardKeyboards = false;
752 final int[] devices = InputDevice.getDeviceIds();
753 for (int i = 0; i < devices.length; i++) {
754 InputDevice device = InputDevice.getDevice(devices[i]);
755 if (device == null || device.isVirtual() || !device.isFullKeyboard()) {
759 hasHardKeyboards = true;
761 InputDeviceIdentifier identifier = device.getIdentifier();
762 String keyboardLayoutDescriptor =
763 inputManager.getCurrentKeyboardLayoutForInputDevice(identifier);
764 KeyboardLayout keyboardLayout = keyboardLayoutDescriptor != null ?
765 inputManager.getKeyboardLayout(keyboardLayoutDescriptor) : null;
768 if (keyboardLayout != null) {
769 summary = keyboardLayout.toString();
771 summary = context.getString(R.string.keyboard_layout_default_label);
774 indexable = new SearchIndexableRaw(context);
775 indexable.key = device.getName();
776 indexable.title = device.getName();
777 indexable.summaryOn = summary;
778 indexable.summaryOff = summary;
779 indexable.screenTitle = screenTitle;
780 indexables.add(indexable);
783 if (hasHardKeyboards) {
784 // Hard keyboard category.
785 indexable = new SearchIndexableRaw(context);
786 indexable.key = "builtin_keyboard_settings";
787 indexable.title = context.getString(
788 R.string.builtin_keyboard_settings_title);
789 indexable.screenTitle = screenTitle;
790 indexables.add(indexable);
794 indexable = new SearchIndexableRaw(context);
795 indexable.key = "voice_input_settings";
796 indexable.title = context.getString(R.string.voice_input_settings);
797 indexable.screenTitle = screenTitle;
798 indexable.keywords = context.getString(R.string.keywords_voice_input);
799 indexables.add(indexable);
802 TtsEngines ttsEngines = new TtsEngines(context);
803 if (!ttsEngines.getEngines().isEmpty()) {
804 indexable = new SearchIndexableRaw(context);
805 indexable.key = "tts_settings";
806 indexable.title = context.getString(R.string.tts_settings_title);
807 indexable.screenTitle = screenTitle;
808 indexable.keywords = context.getString(R.string.keywords_text_to_speech_output);
809 indexables.add(indexable);
813 indexable = new SearchIndexableRaw(context);
814 indexable.key = "pointer_settings_category";
815 indexable.title = context.getString(R.string.pointer_settings_category);
816 indexable.screenTitle = screenTitle;
817 indexables.add(indexable);
819 indexable = new SearchIndexableRaw(context);
820 indexable.key = "pointer_speed";
821 indexable.title = context.getString(R.string.pointer_speed);
822 indexable.screenTitle = screenTitle;
823 indexables.add(indexable);
826 if (haveInputDeviceWithVibrator()) {
827 indexable = new SearchIndexableRaw(context);
828 indexable.key = "vibrate_input_devices";
829 indexable.title = context.getString(R.string.vibrate_input_devices);
830 indexable.summaryOn = context.getString(R.string.vibrate_input_devices_summary);
831 indexable.summaryOff = context.getString(R.string.vibrate_input_devices_summary);
832 indexable.screenTitle = screenTitle;
833 indexables.add(indexable);