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.settings.R;
55 import com.android.settings.Settings.KeyboardLayoutPickerActivity;
56 import com.android.settings.SettingsActivity;
57 import com.android.settings.SettingsPreferenceFragment;
58 import com.android.settings.SubSettings;
59 import com.android.settings.UserDictionarySettings;
60 import com.android.settings.Utils;
61 import com.android.settings.VoiceInputOutputSettings;
62 import com.android.settings.search.BaseSearchIndexProvider;
63 import com.android.settings.search.Indexable;
64 import com.android.settings.search.SearchIndexableRaw;
66 import java.text.Collator;
67 import java.util.ArrayList;
68 import java.util.Collections;
69 import java.util.Comparator;
70 import java.util.HashMap;
71 import java.util.HashSet;
72 import java.util.List;
73 import java.util.Locale;
74 import java.util.TreeSet;
76 public class InputMethodAndLanguageSettings extends SettingsPreferenceFragment
77 implements Preference.OnPreferenceChangeListener, InputManager.InputDeviceListener,
78 KeyboardLayoutDialogFragment.OnSetupKeyboardLayoutsListener, Indexable,
79 InputMethodPreference.OnSavePreferenceListener {
80 private static final String KEY_SPELL_CHECKERS = "spellcheckers_settings";
81 private static final String KEY_PHONE_LANGUAGE = "phone_language";
82 private static final String KEY_CURRENT_INPUT_METHOD = "current_input_method";
83 private static final String KEY_INPUT_METHOD_SELECTOR = "input_method_selector";
84 private static final String KEY_USER_DICTIONARY_SETTINGS = "key_user_dictionary_settings";
85 private static final String KEY_PREVIOUSLY_ENABLED_SUBTYPES = "previously_enabled_subtypes";
86 // false: on ICS or later
87 private static final boolean SHOW_INPUT_METHOD_SWITCHER_SETTINGS = false;
89 private int mDefaultInputMethodSelectorVisibility = 0;
90 private ListPreference mShowInputMethodSelectorPref;
91 private PreferenceCategory mKeyboardSettingsCategory;
92 private PreferenceCategory mHardKeyboardCategory;
93 private PreferenceCategory mGameControllerCategory;
94 private Preference mLanguagePref;
95 private final ArrayList<InputMethodPreference> mInputMethodPreferenceList = new ArrayList<>();
96 private final ArrayList<PreferenceScreen> mHardKeyboardPreferenceList = new ArrayList<>();
97 private InputManager mIm;
98 private InputMethodManager mImm;
99 private boolean mShowsOnlyFullImeAndKeyboardList;
100 private Handler mHandler;
101 private SettingsObserver mSettingsObserver;
102 private Intent mIntentWaitingForResult;
103 private InputMethodSettingValuesWrapper mInputMethodSettingValues;
104 private DevicePolicyManager mDpm;
107 public void onCreate(Bundle icicle) {
108 super.onCreate(icicle);
110 addPreferencesFromResource(R.xml.language_settings);
112 final Activity activity = getActivity();
113 mImm = (InputMethodManager) getSystemService(Context.INPUT_METHOD_SERVICE);
114 mInputMethodSettingValues = InputMethodSettingValuesWrapper.getInstance(activity);
117 mDefaultInputMethodSelectorVisibility = Integer.valueOf(
118 getString(R.string.input_method_selector_visibility_default_value));
119 } catch (NumberFormatException e) {
122 if (activity.getAssets().getLocales().length == 1) {
123 // No "Select language" pref if there's only one system locale available.
124 getPreferenceScreen().removePreference(findPreference(KEY_PHONE_LANGUAGE));
126 mLanguagePref = findPreference(KEY_PHONE_LANGUAGE);
128 if (SHOW_INPUT_METHOD_SWITCHER_SETTINGS) {
129 mShowInputMethodSelectorPref = (ListPreference)findPreference(
130 KEY_INPUT_METHOD_SELECTOR);
131 mShowInputMethodSelectorPref.setOnPreferenceChangeListener(this);
132 // TODO: Update current input method name on summary
133 updateInputMethodSelectorSummary(loadInputMethodSelectorVisibility());
136 new VoiceInputOutputSettings(this).onCreate();
138 // Get references to dynamically constructed categories.
139 mHardKeyboardCategory = (PreferenceCategory)findPreference("hard_keyboard");
140 mKeyboardSettingsCategory = (PreferenceCategory)findPreference(
141 "keyboard_settings_category");
142 mGameControllerCategory = (PreferenceCategory)findPreference(
143 "game_controller_settings_category");
145 final Intent startingIntent = activity.getIntent();
146 // Filter out irrelevant features if invoked from IME settings button.
147 mShowsOnlyFullImeAndKeyboardList = Settings.ACTION_INPUT_METHOD_SETTINGS.equals(
148 startingIntent.getAction());
149 if (mShowsOnlyFullImeAndKeyboardList) {
150 getPreferenceScreen().removeAll();
151 getPreferenceScreen().addPreference(mHardKeyboardCategory);
152 if (SHOW_INPUT_METHOD_SWITCHER_SETTINGS) {
153 getPreferenceScreen().addPreference(mShowInputMethodSelectorPref);
155 mKeyboardSettingsCategory.removeAll();
156 getPreferenceScreen().addPreference(mKeyboardSettingsCategory);
159 // Build hard keyboard and game controller preference categories.
160 mIm = (InputManager)activity.getSystemService(Context.INPUT_SERVICE);
161 updateInputDevices();
164 final Preference spellChecker = findPreference(KEY_SPELL_CHECKERS);
165 if (spellChecker != null) {
166 // Note: KEY_SPELL_CHECKERS preference is marked as persistent="false" in XML.
167 InputMethodAndSubtypeUtil.removeUnnecessaryNonPersistentPreference(spellChecker);
168 final Intent intent = new Intent(Intent.ACTION_MAIN);
169 intent.setClass(activity, SubSettings.class);
170 intent.putExtra(SettingsActivity.EXTRA_SHOW_FRAGMENT,
171 SpellCheckersSettings.class.getName());
172 intent.putExtra(SettingsActivity.EXTRA_SHOW_FRAGMENT_TITLE_RESID,
173 R.string.spellcheckers_settings_title);
174 spellChecker.setIntent(intent);
177 mHandler = new Handler();
178 mSettingsObserver = new SettingsObserver(mHandler, activity);
179 mDpm = (DevicePolicyManager) (getActivity().
180 getSystemService(Context.DEVICE_POLICY_SERVICE));
182 // If we've launched from the keyboard layout notification, go ahead and just show the
183 // keyboard layout dialog.
184 final InputDeviceIdentifier identifier =
185 startingIntent.getParcelableExtra(Settings.EXTRA_INPUT_DEVICE_IDENTIFIER);
186 if (mShowsOnlyFullImeAndKeyboardList && identifier != null) {
187 showKeyboardLayoutDialog(identifier);
191 private void updateInputMethodSelectorSummary(int value) {
192 String[] inputMethodSelectorTitles = getResources().getStringArray(
193 R.array.input_method_selector_titles);
194 if (inputMethodSelectorTitles.length > value) {
195 mShowInputMethodSelectorPref.setSummary(inputMethodSelectorTitles[value]);
196 mShowInputMethodSelectorPref.setValue(String.valueOf(value));
200 private void updateUserDictionaryPreference(Preference userDictionaryPreference) {
201 final Activity activity = getActivity();
202 final TreeSet<String> localeSet = UserDictionaryList.getUserDictionaryLocalesSet(activity);
203 if (null == localeSet) {
204 // The locale list is null if and only if the user dictionary service is
205 // not present or disabled. In this case we need to remove the preference.
206 getPreferenceScreen().removePreference(userDictionaryPreference);
208 userDictionaryPreference.setOnPreferenceClickListener(
209 new OnPreferenceClickListener() {
211 public boolean onPreferenceClick(Preference arg0) {
212 // Redirect to UserDictionarySettings if the user needs only one
214 final Bundle extras = new Bundle();
215 final Class<? extends Fragment> targetFragment;
216 if (localeSet.size() <= 1) {
217 if (!localeSet.isEmpty()) {
218 // If the size of localeList is 0, we don't set the locale
219 // parameter in the extras. This will be interpreted by the
220 // UserDictionarySettings class as meaning
221 // "the current locale". Note that with the current code for
222 // UserDictionaryList#getUserDictionaryLocalesSet()
223 // the locale list always has at least one element, since it
224 // always includes the current locale explicitly.
225 // @see UserDictionaryList.getUserDictionaryLocalesSet().
226 extras.putString("locale", localeSet.first());
228 targetFragment = UserDictionarySettings.class;
230 targetFragment = UserDictionaryList.class;
232 startFragment(InputMethodAndLanguageSettings.this,
233 targetFragment.getCanonicalName(), -1, -1, extras);
241 public void onResume() {
244 mSettingsObserver.resume();
245 mIm.registerInputDeviceListener(this, null);
247 final Preference spellChecker = findPreference(KEY_SPELL_CHECKERS);
248 if (spellChecker != null) {
249 final TextServicesManager tsm = (TextServicesManager) getSystemService(
250 Context.TEXT_SERVICES_MANAGER_SERVICE);
251 if (tsm.isSpellCheckerEnabled()) {
252 final SpellCheckerInfo sci = tsm.getCurrentSpellChecker();
253 spellChecker.setSummary(sci.loadLabel(getPackageManager()));
255 spellChecker.setSummary(R.string.switch_off_text);
259 if (!mShowsOnlyFullImeAndKeyboardList) {
260 if (mLanguagePref != null) {
261 String localeName = getLocaleName(getActivity());
262 mLanguagePref.setSummary(localeName);
265 updateUserDictionaryPreference(findPreference(KEY_USER_DICTIONARY_SETTINGS));
266 if (SHOW_INPUT_METHOD_SWITCHER_SETTINGS) {
267 mShowInputMethodSelectorPref.setOnPreferenceChangeListener(this);
271 updateInputDevices();
273 // Refresh internal states in mInputMethodSettingValues to keep the latest
274 // "InputMethodInfo"s and "InputMethodSubtype"s
275 mInputMethodSettingValues.refreshAllInputMethodAndSubtypes();
276 updateInputMethodPreferenceViews();
280 public void onPause() {
283 mIm.unregisterInputDeviceListener(this);
284 mSettingsObserver.pause();
286 if (SHOW_INPUT_METHOD_SWITCHER_SETTINGS) {
287 mShowInputMethodSelectorPref.setOnPreferenceChangeListener(null);
289 // TODO: Consolidate the logic to InputMethodSettingsWrapper
290 InputMethodAndSubtypeUtil.saveInputMethodSubtypeList(
291 this, getContentResolver(), mInputMethodSettingValues.getInputMethodList(),
292 !mHardKeyboardPreferenceList.isEmpty());
296 public void onInputDeviceAdded(int deviceId) {
297 updateInputDevices();
301 public void onInputDeviceChanged(int deviceId) {
302 updateInputDevices();
306 public void onInputDeviceRemoved(int deviceId) {
307 updateInputDevices();
311 public boolean onPreferenceTreeClick(PreferenceScreen preferenceScreen, Preference preference) {
312 // Input Method stuff
313 if (Utils.isMonkeyRunning()) {
316 if (preference instanceof PreferenceScreen) {
317 if (preference.getFragment() != null) {
318 // Fragment will be handled correctly by the super class.
319 } else if (KEY_CURRENT_INPUT_METHOD.equals(preference.getKey())) {
320 final InputMethodManager imm = (InputMethodManager)
321 getSystemService(Context.INPUT_METHOD_SERVICE);
322 imm.showInputMethodPicker();
324 } else if (preference instanceof SwitchPreference) {
325 final SwitchPreference pref = (SwitchPreference) preference;
326 if (pref == mGameControllerCategory.findPreference("vibrate_input_devices")) {
327 System.putInt(getContentResolver(), Settings.System.VIBRATE_INPUT_DEVICES,
328 pref.isChecked() ? 1 : 0);
332 return super.onPreferenceTreeClick(preferenceScreen, preference);
335 private static String getLocaleName(Context context) {
336 // We want to show the same string that the LocalePicker used.
337 // TODO: should this method be in LocalePicker instead?
338 Locale currentLocale = context.getResources().getConfiguration().locale;
339 List<LocalePicker.LocaleInfo> locales = LocalePicker.getAllAssetLocales(context, true);
340 for (LocalePicker.LocaleInfo locale : locales) {
341 if (locale.getLocale().equals(currentLocale)) {
342 return locale.getLabel();
345 // This can't happen as long as the locale was one set by Settings.
346 // Fall back in case a developer is testing an unsupported locale.
347 return currentLocale.getDisplayName(currentLocale);
350 private void saveInputMethodSelectorVisibility(String value) {
352 int intValue = Integer.valueOf(value);
353 Settings.Secure.putInt(getContentResolver(),
354 Settings.Secure.INPUT_METHOD_SELECTOR_VISIBILITY, intValue);
355 updateInputMethodSelectorSummary(intValue);
356 } catch(NumberFormatException e) {
360 private int loadInputMethodSelectorVisibility() {
361 return Settings.Secure.getInt(getContentResolver(),
362 Settings.Secure.INPUT_METHOD_SELECTOR_VISIBILITY,
363 mDefaultInputMethodSelectorVisibility);
367 public boolean onPreferenceChange(Preference preference, Object value) {
368 if (SHOW_INPUT_METHOD_SWITCHER_SETTINGS) {
369 if (preference == mShowInputMethodSelectorPref) {
370 if (value instanceof String) {
371 saveInputMethodSelectorVisibility((String)value);
378 private void updateInputMethodPreferenceViews() {
379 synchronized (mInputMethodPreferenceList) {
380 // Clear existing "InputMethodPreference"s
381 for (final InputMethodPreference pref : mInputMethodPreferenceList) {
382 mKeyboardSettingsCategory.removePreference(pref);
384 mInputMethodPreferenceList.clear();
385 List<String> permittedList = mDpm.getPermittedInputMethodsForCurrentUser();
386 final Context context = getActivity();
387 final List<InputMethodInfo> imis = mShowsOnlyFullImeAndKeyboardList
388 ? mInputMethodSettingValues.getInputMethodList()
389 : mImm.getEnabledInputMethodList();
390 final int N = (imis == null ? 0 : imis.size());
391 for (int i = 0; i < N; ++i) {
392 final InputMethodInfo imi = imis.get(i);
393 final boolean isAllowedByOrganization = permittedList == null
394 || permittedList.contains(imi.getPackageName());
395 final InputMethodPreference pref = new InputMethodPreference(
396 context, imi, mShowsOnlyFullImeAndKeyboardList /* hasSwitch */,
397 isAllowedByOrganization, this);
398 mInputMethodPreferenceList.add(pref);
400 final Collator collator = Collator.getInstance();
401 Collections.sort(mInputMethodPreferenceList, new Comparator<InputMethodPreference>() {
403 public int compare(InputMethodPreference lhs, InputMethodPreference rhs) {
404 return lhs.compareTo(rhs, collator);
407 for (int i = 0; i < N; ++i) {
408 final InputMethodPreference pref = mInputMethodPreferenceList.get(i);
409 mKeyboardSettingsCategory.addPreference(pref);
410 InputMethodAndSubtypeUtil.removeUnnecessaryNonPersistentPreference(pref);
411 pref.updatePreferenceViews();
414 updateCurrentImeName();
415 // TODO: Consolidate the logic with InputMethodSettingsWrapper
416 // CAVEAT: The preference class here does not know about the default value - that is
417 // managed by the Input Method Manager Service, so in this case it could save the wrong
418 // value. Hence we must update the checkboxes here.
419 InputMethodAndSubtypeUtil.loadInputMethodSubtypeList(
420 this, getContentResolver(),
421 mInputMethodSettingValues.getInputMethodList(), null);
425 public void onSaveInputMethodPreference(final InputMethodPreference pref) {
426 final InputMethodInfo imi = pref.getInputMethodInfo();
427 if (!pref.isChecked()) {
428 // An IME is being disabled. Save enabled subtypes of the IME to shared preference to be
429 // able to re-enable these subtypes when the IME gets re-enabled.
430 saveEnabledSubtypesOf(imi);
432 final boolean hasHardwareKeyboard = getResources().getConfiguration().keyboard
433 == Configuration.KEYBOARD_QWERTY;
434 InputMethodAndSubtypeUtil.saveInputMethodSubtypeList(this, getContentResolver(),
435 mImm.getInputMethodList(), hasHardwareKeyboard);
436 // Update input method settings and preference list.
437 mInputMethodSettingValues.refreshAllInputMethodAndSubtypes();
438 if (pref.isChecked()) {
439 // An IME is being enabled. Load the previously enabled subtypes from shared preference
440 // and enable these subtypes.
441 restorePreviouslyEnabledSubtypesOf(imi);
443 for (final InputMethodPreference p : mInputMethodPreferenceList) {
444 p.updatePreferenceViews();
448 private void saveEnabledSubtypesOf(final InputMethodInfo imi) {
449 final HashSet<String> enabledSubtypeIdSet = new HashSet<>();
450 final List<InputMethodSubtype> enabledSubtypes = mImm.getEnabledInputMethodSubtypeList(
451 imi, true /* allowsImplicitlySelectedSubtypes */);
452 for (final InputMethodSubtype subtype : enabledSubtypes) {
453 final String subtypeId = Integer.toString(subtype.hashCode());
454 enabledSubtypeIdSet.add(subtypeId);
456 final HashMap<String, HashSet<String>> imeToEnabledSubtypeIdsMap =
457 loadPreviouslyEnabledSubtypeIdsMap();
458 final String imiId = imi.getId();
459 imeToEnabledSubtypeIdsMap.put(imiId, enabledSubtypeIdSet);
460 savePreviouslyEnabledSubtypeIdsMap(imeToEnabledSubtypeIdsMap);
463 private void restorePreviouslyEnabledSubtypesOf(final InputMethodInfo imi) {
464 final HashMap<String, HashSet<String>> imeToEnabledSubtypeIdsMap =
465 loadPreviouslyEnabledSubtypeIdsMap();
466 final String imiId = imi.getId();
467 final HashSet<String> enabledSubtypeIdSet = imeToEnabledSubtypeIdsMap.remove(imiId);
468 if (enabledSubtypeIdSet == null) {
471 savePreviouslyEnabledSubtypeIdsMap(imeToEnabledSubtypeIdsMap);
472 InputMethodAndSubtypeUtil.enableInputMethodSubtypesOf(
473 getContentResolver(), imiId, enabledSubtypeIdSet);
476 private HashMap<String, HashSet<String>> loadPreviouslyEnabledSubtypeIdsMap() {
477 final Context context = getActivity();
478 final SharedPreferences prefs = PreferenceManager.getDefaultSharedPreferences(context);
479 final String imesAndSubtypesString = prefs.getString(KEY_PREVIOUSLY_ENABLED_SUBTYPES, null);
480 return InputMethodAndSubtypeUtil.parseInputMethodsAndSubtypesString(imesAndSubtypesString);
483 private void savePreviouslyEnabledSubtypeIdsMap(
484 final HashMap<String, HashSet<String>> subtypesMap) {
485 final Context context = getActivity();
486 final SharedPreferences prefs = PreferenceManager.getDefaultSharedPreferences(context);
487 final String imesAndSubtypesString = InputMethodAndSubtypeUtil
488 .buildInputMethodsAndSubtypesString(subtypesMap);
489 prefs.edit().putString(KEY_PREVIOUSLY_ENABLED_SUBTYPES, imesAndSubtypesString).apply();
492 private void updateCurrentImeName() {
493 final Context context = getActivity();
494 if (context == null || mImm == null) return;
495 final Preference curPref = getPreferenceScreen().findPreference(KEY_CURRENT_INPUT_METHOD);
496 if (curPref != null) {
497 final CharSequence curIme =
498 mInputMethodSettingValues.getCurrentInputMethodName(context);
499 if (!TextUtils.isEmpty(curIme)) {
500 synchronized (this) {
501 curPref.setSummary(curIme);
507 private void updateInputDevices() {
508 updateHardKeyboards();
509 updateGameControllers();
512 private void updateHardKeyboards() {
513 mHardKeyboardPreferenceList.clear();
514 final int[] devices = InputDevice.getDeviceIds();
515 for (int i = 0; i < devices.length; i++) {
516 InputDevice device = InputDevice.getDevice(devices[i]);
518 && !device.isVirtual()
519 && device.isFullKeyboard()) {
520 final InputDeviceIdentifier identifier = device.getIdentifier();
521 final String keyboardLayoutDescriptor =
522 mIm.getCurrentKeyboardLayoutForInputDevice(identifier);
523 final KeyboardLayout keyboardLayout = keyboardLayoutDescriptor != null ?
524 mIm.getKeyboardLayout(keyboardLayoutDescriptor) : null;
526 final PreferenceScreen pref = new PreferenceScreen(getActivity(), null);
527 pref.setTitle(device.getName());
528 if (keyboardLayout != null) {
529 pref.setSummary(keyboardLayout.toString());
531 pref.setSummary(R.string.keyboard_layout_default_label);
533 pref.setOnPreferenceClickListener(new Preference.OnPreferenceClickListener() {
535 public boolean onPreferenceClick(Preference preference) {
536 showKeyboardLayoutDialog(identifier);
540 mHardKeyboardPreferenceList.add(pref);
544 if (!mHardKeyboardPreferenceList.isEmpty()) {
545 for (int i = mHardKeyboardCategory.getPreferenceCount(); i-- > 0; ) {
546 final Preference pref = mHardKeyboardCategory.getPreference(i);
547 if (pref.getOrder() < 1000) {
548 mHardKeyboardCategory.removePreference(pref);
552 Collections.sort(mHardKeyboardPreferenceList);
553 final int count = mHardKeyboardPreferenceList.size();
554 for (int i = 0; i < count; i++) {
555 final Preference pref = mHardKeyboardPreferenceList.get(i);
557 mHardKeyboardCategory.addPreference(pref);
560 getPreferenceScreen().addPreference(mHardKeyboardCategory);
562 getPreferenceScreen().removePreference(mHardKeyboardCategory);
566 private void showKeyboardLayoutDialog(InputDeviceIdentifier inputDeviceIdentifier) {
567 KeyboardLayoutDialogFragment fragment = new KeyboardLayoutDialogFragment(
568 inputDeviceIdentifier);
569 fragment.setTargetFragment(this, 0);
570 fragment.show(getActivity().getFragmentManager(), "keyboardLayout");
574 public void onSetupKeyboardLayouts(InputDeviceIdentifier inputDeviceIdentifier) {
575 final Intent intent = new Intent(Intent.ACTION_MAIN);
576 intent.setClass(getActivity(), KeyboardLayoutPickerActivity.class);
577 intent.putExtra(KeyboardLayoutPickerFragment.EXTRA_INPUT_DEVICE_IDENTIFIER,
578 inputDeviceIdentifier);
579 mIntentWaitingForResult = intent;
580 startActivityForResult(intent, 0);
584 public void onActivityResult(int requestCode, int resultCode, Intent data) {
585 super.onActivityResult(requestCode, resultCode, data);
587 if (mIntentWaitingForResult != null) {
588 InputDeviceIdentifier inputDeviceIdentifier = mIntentWaitingForResult
589 .getParcelableExtra(KeyboardLayoutPickerFragment.EXTRA_INPUT_DEVICE_IDENTIFIER);
590 mIntentWaitingForResult = null;
591 showKeyboardLayoutDialog(inputDeviceIdentifier);
595 private void updateGameControllers() {
596 if (haveInputDeviceWithVibrator()) {
597 getPreferenceScreen().addPreference(mGameControllerCategory);
599 SwitchPreference pref = (SwitchPreference)
600 mGameControllerCategory.findPreference("vibrate_input_devices");
601 pref.setChecked(System.getInt(getContentResolver(),
602 Settings.System.VIBRATE_INPUT_DEVICES, 1) > 0);
604 getPreferenceScreen().removePreference(mGameControllerCategory);
608 private static boolean haveInputDeviceWithVibrator() {
609 final int[] devices = InputDevice.getDeviceIds();
610 for (int i = 0; i < devices.length; i++) {
611 InputDevice device = InputDevice.getDevice(devices[i]);
612 if (device != null && !device.isVirtual() && device.getVibrator().hasVibrator()) {
619 private class SettingsObserver extends ContentObserver {
620 private Context mContext;
622 public SettingsObserver(Handler handler, Context context) {
627 @Override public void onChange(boolean selfChange) {
628 updateCurrentImeName();
631 public void resume() {
632 final ContentResolver cr = mContext.getContentResolver();
633 cr.registerContentObserver(
634 Settings.Secure.getUriFor(Settings.Secure.DEFAULT_INPUT_METHOD), false, this);
635 cr.registerContentObserver(Settings.Secure.getUriFor(
636 Settings.Secure.SELECTED_INPUT_METHOD_SUBTYPE), false, this);
639 public void pause() {
640 mContext.getContentResolver().unregisterContentObserver(this);
644 public static final Indexable.SearchIndexProvider SEARCH_INDEX_DATA_PROVIDER =
645 new BaseSearchIndexProvider() {
647 public List<SearchIndexableRaw> getRawDataToIndex(Context context, boolean enabled) {
648 List<SearchIndexableRaw> indexables = new ArrayList<>();
650 final String screenTitle = context.getString(R.string.language_keyboard_settings_title);
653 if (context.getAssets().getLocales().length > 1) {
654 String localeName = getLocaleName(context);
655 SearchIndexableRaw indexable = new SearchIndexableRaw(context);
656 indexable.key = KEY_PHONE_LANGUAGE;
657 indexable.title = context.getString(R.string.phone_language);
658 indexable.summaryOn = localeName;
659 indexable.summaryOff = localeName;
660 indexable.screenTitle = screenTitle;
661 indexables.add(indexable);
665 SearchIndexableRaw indexable = new SearchIndexableRaw(context);
666 indexable.key = KEY_SPELL_CHECKERS;
667 indexable.title = context.getString(R.string.spellcheckers_settings_title);
668 indexable.screenTitle = screenTitle;
669 indexable.keywords = context.getString(R.string.keywords_spell_checker);
670 indexables.add(indexable);
673 if (UserDictionaryList.getUserDictionaryLocalesSet(context) != null) {
674 indexable = new SearchIndexableRaw(context);
675 indexable.key = "user_dict_settings";
676 indexable.title = context.getString(R.string.user_dict_settings_title);
677 indexable.screenTitle = screenTitle;
678 indexables.add(indexable);
681 // Keyboard settings.
682 indexable = new SearchIndexableRaw(context);
683 indexable.key = "keyboard_settings";
684 indexable.title = context.getString(R.string.keyboard_settings_category);
685 indexable.screenTitle = screenTitle;
686 indexable.keywords = context.getString(R.string.keywords_keyboard_and_ime);
687 indexables.add(indexable);
689 InputMethodSettingValuesWrapper immValues = InputMethodSettingValuesWrapper
690 .getInstance(context);
691 immValues.refreshAllInputMethodAndSubtypes();
694 String currImeName = immValues.getCurrentInputMethodName(context).toString();
695 indexable = new SearchIndexableRaw(context);
696 indexable.key = KEY_CURRENT_INPUT_METHOD;
697 indexable.title = context.getString(R.string.current_input_method);
698 indexable.summaryOn = currImeName;
699 indexable.summaryOff = currImeName;
700 indexable.screenTitle = screenTitle;
701 indexables.add(indexable);
703 InputMethodManager inputMethodManager = (InputMethodManager) context.getSystemService(
704 Context.INPUT_METHOD_SERVICE);
707 List<InputMethodInfo> inputMethods = immValues.getInputMethodList();
708 final int inputMethodCount = (inputMethods == null ? 0 : inputMethods.size());
709 for (int i = 0; i < inputMethodCount; ++i) {
710 InputMethodInfo inputMethod = inputMethods.get(i);
712 StringBuilder builder = new StringBuilder();
713 List<InputMethodSubtype> subtypes = inputMethodManager
714 .getEnabledInputMethodSubtypeList(inputMethod, true);
715 final int subtypeCount = subtypes.size();
716 for (int j = 0; j < subtypeCount; j++) {
717 InputMethodSubtype subtype = subtypes.get(j);
718 if (builder.length() > 0) {
721 CharSequence subtypeLabel = subtype.getDisplayName(context,
722 inputMethod.getPackageName(), inputMethod.getServiceInfo()
724 builder.append(subtypeLabel);
726 String summary = builder.toString();
728 ServiceInfo serviceInfo = inputMethod.getServiceInfo();
729 ComponentName componentName = new ComponentName(serviceInfo.packageName,
732 indexable = new SearchIndexableRaw(context);
733 indexable.key = componentName.flattenToString();
734 indexable.title = inputMethod.loadLabel(context.getPackageManager()).toString();
735 indexable.summaryOn = summary;
736 indexable.summaryOff = summary;
737 indexable.screenTitle = screenTitle;
738 indexables.add(indexable);
742 InputManager inputManager = (InputManager) context.getSystemService(
743 Context.INPUT_SERVICE);
744 boolean hasHardKeyboards = false;
746 final int[] devices = InputDevice.getDeviceIds();
747 for (int i = 0; i < devices.length; i++) {
748 InputDevice device = InputDevice.getDevice(devices[i]);
749 if (device == null || device.isVirtual() || !device.isFullKeyboard()) {
753 hasHardKeyboards = true;
755 InputDeviceIdentifier identifier = device.getIdentifier();
756 String keyboardLayoutDescriptor =
757 inputManager.getCurrentKeyboardLayoutForInputDevice(identifier);
758 KeyboardLayout keyboardLayout = keyboardLayoutDescriptor != null ?
759 inputManager.getKeyboardLayout(keyboardLayoutDescriptor) : null;
762 if (keyboardLayout != null) {
763 summary = keyboardLayout.toString();
765 summary = context.getString(R.string.keyboard_layout_default_label);
768 indexable = new SearchIndexableRaw(context);
769 indexable.key = device.getName();
770 indexable.title = device.getName();
771 indexable.summaryOn = summary;
772 indexable.summaryOff = summary;
773 indexable.screenTitle = screenTitle;
774 indexables.add(indexable);
777 if (hasHardKeyboards) {
778 // Hard keyboard category.
779 indexable = new SearchIndexableRaw(context);
780 indexable.key = "builtin_keyboard_settings";
781 indexable.title = context.getString(
782 R.string.builtin_keyboard_settings_title);
783 indexable.screenTitle = screenTitle;
784 indexables.add(indexable);
788 indexable = new SearchIndexableRaw(context);
789 indexable.key = "voice_input_settings";
790 indexable.title = context.getString(R.string.voice_input_settings);
791 indexable.screenTitle = screenTitle;
792 indexable.keywords = context.getString(R.string.keywords_voice_input);
793 indexables.add(indexable);
796 TtsEngines ttsEngines = new TtsEngines(context);
797 if (!ttsEngines.getEngines().isEmpty()) {
798 indexable = new SearchIndexableRaw(context);
799 indexable.key = "tts_settings";
800 indexable.title = context.getString(R.string.tts_settings_title);
801 indexable.screenTitle = screenTitle;
802 indexable.keywords = context.getString(R.string.keywords_text_to_speech_output);
803 indexables.add(indexable);
807 indexable = new SearchIndexableRaw(context);
808 indexable.key = "pointer_settings_category";
809 indexable.title = context.getString(R.string.pointer_settings_category);
810 indexable.screenTitle = screenTitle;
811 indexables.add(indexable);
813 indexable = new SearchIndexableRaw(context);
814 indexable.key = "pointer_speed";
815 indexable.title = context.getString(R.string.pointer_speed);
816 indexable.screenTitle = screenTitle;
817 indexables.add(indexable);
820 if (haveInputDeviceWithVibrator()) {
821 indexable = new SearchIndexableRaw(context);
822 indexable.key = "vibrate_input_devices";
823 indexable.title = context.getString(R.string.vibrate_input_devices);
824 indexable.summaryOn = context.getString(R.string.vibrate_input_devices_summary);
825 indexable.summaryOff = context.getString(R.string.vibrate_input_devices_summary);
826 indexable.screenTitle = screenTitle;
827 indexables.add(indexable);