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 import org.cyanogenmod.hardware.HighTouchSensitivity;
79 public class InputMethodAndLanguageSettings extends SettingsPreferenceFragment
80 implements Preference.OnPreferenceChangeListener, InputManager.InputDeviceListener,
81 KeyboardLayoutDialogFragment.OnSetupKeyboardLayoutsListener, Indexable,
82 InputMethodPreference.OnSavePreferenceListener {
83 private static final String KEY_SPELL_CHECKERS = "spellcheckers_settings";
84 private static final String KEY_PHONE_LANGUAGE = "phone_language";
85 private static final String KEY_CURRENT_INPUT_METHOD = "current_input_method";
86 private static final String KEY_INPUT_METHOD_SELECTOR = "input_method_selector";
87 private static final String KEY_USER_DICTIONARY_SETTINGS = "key_user_dictionary_settings";
88 private static final String KEY_POINTER_SETTINGS_CATEGORY = "pointer_settings_category";
89 private static final String KEY_PREVIOUSLY_ENABLED_SUBTYPES = "previously_enabled_subtypes";
90 private static final String KEY_TRACKPAD_SETTINGS = "gesture_pad_settings";
91 private static final String KEY_HIGH_TOUCH_SENSITIVITY = "high_touch_sensitivity";
92 // false: on ICS or later
93 private static final boolean SHOW_INPUT_METHOD_SWITCHER_SETTINGS = false;
95 private int mDefaultInputMethodSelectorVisibility = 0;
96 private ListPreference mShowInputMethodSelectorPref;
97 private SwitchPreference mHighTouchSensitivity;
98 private PreferenceCategory mKeyboardSettingsCategory;
99 private PreferenceCategory mHardKeyboardCategory;
100 private PreferenceCategory mGameControllerCategory;
101 private Preference mLanguagePref;
102 private final ArrayList<InputMethodPreference> mInputMethodPreferenceList = new ArrayList<>();
103 private final ArrayList<PreferenceScreen> mHardKeyboardPreferenceList = new ArrayList<>();
104 private InputManager mIm;
105 private InputMethodManager mImm;
106 private boolean mShowsOnlyFullImeAndKeyboardList;
107 private Handler mHandler;
108 private SettingsObserver mSettingsObserver;
109 private Intent mIntentWaitingForResult;
110 private InputMethodSettingValuesWrapper mInputMethodSettingValues;
111 private DevicePolicyManager mDpm;
114 protected int getMetricsCategory() {
115 return MetricsLogger.INPUTMETHOD_LANGUAGE;
119 public void onCreate(Bundle icicle) {
120 super.onCreate(icicle);
122 addPreferencesFromResource(R.xml.language_settings);
124 final Activity activity = getActivity();
125 mImm = (InputMethodManager) getSystemService(Context.INPUT_METHOD_SERVICE);
126 mInputMethodSettingValues = InputMethodSettingValuesWrapper.getInstance(activity);
129 mDefaultInputMethodSelectorVisibility = Integer.valueOf(
130 getString(R.string.input_method_selector_visibility_default_value));
131 } catch (NumberFormatException e) {
134 if (activity.getAssets().getLocales().length == 1) {
135 // No "Select language" pref if there's only one system locale available.
136 getPreferenceScreen().removePreference(findPreference(KEY_PHONE_LANGUAGE));
138 mLanguagePref = findPreference(KEY_PHONE_LANGUAGE);
140 if (SHOW_INPUT_METHOD_SWITCHER_SETTINGS) {
141 mShowInputMethodSelectorPref = (ListPreference)findPreference(
142 KEY_INPUT_METHOD_SELECTOR);
143 mShowInputMethodSelectorPref.setOnPreferenceChangeListener(this);
144 // TODO: Update current input method name on summary
145 updateInputMethodSelectorSummary(loadInputMethodSelectorVisibility());
148 new VoiceInputOutputSettings(this).onCreate();
150 // Get references to dynamically constructed categories.
151 mHardKeyboardCategory = (PreferenceCategory)findPreference("hard_keyboard");
152 mKeyboardSettingsCategory = (PreferenceCategory)findPreference(
153 "keyboard_settings_category");
154 mGameControllerCategory = (PreferenceCategory)findPreference(
155 "game_controller_settings_category");
157 final Intent startingIntent = activity.getIntent();
158 // Filter out irrelevant features if invoked from IME settings button.
159 mShowsOnlyFullImeAndKeyboardList = Settings.ACTION_INPUT_METHOD_SETTINGS.equals(
160 startingIntent.getAction());
161 if (mShowsOnlyFullImeAndKeyboardList) {
162 getPreferenceScreen().removeAll();
163 getPreferenceScreen().addPreference(mHardKeyboardCategory);
164 if (SHOW_INPUT_METHOD_SWITCHER_SETTINGS) {
165 getPreferenceScreen().addPreference(mShowInputMethodSelectorPref);
167 mKeyboardSettingsCategory.removeAll();
168 getPreferenceScreen().addPreference(mKeyboardSettingsCategory);
171 // Build hard keyboard and game controller preference categories.
172 mIm = (InputManager)activity.getSystemService(Context.INPUT_SERVICE);
173 updateInputDevices();
175 PreferenceCategory pointerSettingsCategory = (PreferenceCategory)
176 findPreference(KEY_POINTER_SETTINGS_CATEGORY);
178 mHighTouchSensitivity = (SwitchPreference) findPreference(KEY_HIGH_TOUCH_SENSITIVITY);
179 if (!isHighTouchSensitivitySupported()) {
180 pointerSettingsCategory.removePreference(mHighTouchSensitivity);
181 mHighTouchSensitivity = null;
183 mHighTouchSensitivity.setChecked(HighTouchSensitivity.isEnabled());
186 // Enable or disable mStatusBarImeSwitcher based on boolean: config_show_cmIMESwitcher
187 boolean showCmImeSwitcher = getResources().getBoolean(
188 com.android.internal.R.bool.config_show_cmIMESwitcher);
189 if (!showCmImeSwitcher) {
190 getPreferenceScreen().removePreference(
191 findPreference(Settings.System.STATUS_BAR_IME_SWITCHER));
195 final Preference spellChecker = findPreference(KEY_SPELL_CHECKERS);
196 if (spellChecker != null) {
197 // Note: KEY_SPELL_CHECKERS preference is marked as persistent="false" in XML.
198 InputMethodAndSubtypeUtil.removeUnnecessaryNonPersistentPreference(spellChecker);
199 final Intent intent = new Intent(Intent.ACTION_MAIN);
200 intent.setClass(activity, SubSettings.class);
201 intent.putExtra(SettingsActivity.EXTRA_SHOW_FRAGMENT,
202 SpellCheckersSettings.class.getName());
203 intent.putExtra(SettingsActivity.EXTRA_SHOW_FRAGMENT_TITLE_RESID,
204 R.string.spellcheckers_settings_title);
205 spellChecker.setIntent(intent);
208 mHandler = new Handler();
209 mSettingsObserver = new SettingsObserver(mHandler, activity);
210 mDpm = (DevicePolicyManager) (getActivity().
211 getSystemService(Context.DEVICE_POLICY_SERVICE));
213 // If we've launched from the keyboard layout notification, go ahead and just show the
214 // keyboard layout dialog.
215 final InputDeviceIdentifier identifier =
216 startingIntent.getParcelableExtra(Settings.EXTRA_INPUT_DEVICE_IDENTIFIER);
217 if (mShowsOnlyFullImeAndKeyboardList && identifier != null) {
218 showKeyboardLayoutDialog(identifier);
222 private void updateInputMethodSelectorSummary(int value) {
223 String[] inputMethodSelectorTitles = getResources().getStringArray(
224 R.array.input_method_selector_titles);
225 if (inputMethodSelectorTitles.length > value) {
226 mShowInputMethodSelectorPref.setSummary(inputMethodSelectorTitles[value]);
227 mShowInputMethodSelectorPref.setValue(String.valueOf(value));
231 private void updateUserDictionaryPreference(Preference userDictionaryPreference) {
232 final Activity activity = getActivity();
233 final TreeSet<String> localeSet = UserDictionaryList.getUserDictionaryLocalesSet(activity);
234 if (null == localeSet) {
235 // The locale list is null if and only if the user dictionary service is
236 // not present or disabled. In this case we need to remove the preference.
237 getPreferenceScreen().removePreference(userDictionaryPreference);
239 userDictionaryPreference.setOnPreferenceClickListener(
240 new OnPreferenceClickListener() {
242 public boolean onPreferenceClick(Preference arg0) {
243 // Redirect to UserDictionarySettings if the user needs only one
245 final Bundle extras = new Bundle();
246 final Class<? extends Fragment> targetFragment;
247 if (localeSet.size() <= 1) {
248 if (!localeSet.isEmpty()) {
249 // If the size of localeList is 0, we don't set the locale
250 // parameter in the extras. This will be interpreted by the
251 // UserDictionarySettings class as meaning
252 // "the current locale". Note that with the current code for
253 // UserDictionaryList#getUserDictionaryLocalesSet()
254 // the locale list always has at least one element, since it
255 // always includes the current locale explicitly.
256 // @see UserDictionaryList.getUserDictionaryLocalesSet().
257 extras.putString("locale", localeSet.first());
259 targetFragment = UserDictionarySettings.class;
261 targetFragment = UserDictionaryList.class;
263 startFragment(InputMethodAndLanguageSettings.this,
264 targetFragment.getCanonicalName(), -1, -1, extras);
272 public void onResume() {
275 mSettingsObserver.resume();
276 mIm.registerInputDeviceListener(this, null);
278 final Preference spellChecker = findPreference(KEY_SPELL_CHECKERS);
279 if (spellChecker != null) {
280 final TextServicesManager tsm = (TextServicesManager) getSystemService(
281 Context.TEXT_SERVICES_MANAGER_SERVICE);
282 final SpellCheckerInfo sci = tsm.getCurrentSpellChecker();
283 spellChecker.setEnabled(sci != null);
284 if (tsm.isSpellCheckerEnabled() && sci != null) {
285 spellChecker.setSummary(sci.loadLabel(getPackageManager()));
287 spellChecker.setSummary(R.string.switch_off_text);
291 if (!mShowsOnlyFullImeAndKeyboardList) {
292 if (mLanguagePref != null) {
293 String localeName = getLocaleName(getActivity());
294 mLanguagePref.setSummary(localeName);
297 updateUserDictionaryPreference(findPreference(KEY_USER_DICTIONARY_SETTINGS));
298 if (SHOW_INPUT_METHOD_SWITCHER_SETTINGS) {
299 mShowInputMethodSelectorPref.setOnPreferenceChangeListener(this);
303 updateInputDevices();
305 // Refresh internal states in mInputMethodSettingValues to keep the latest
306 // "InputMethodInfo"s and "InputMethodSubtype"s
307 mInputMethodSettingValues.refreshAllInputMethodAndSubtypes();
308 updateInputMethodPreferenceViews();
312 public void onPause() {
315 mIm.unregisterInputDeviceListener(this);
316 mSettingsObserver.pause();
318 if (SHOW_INPUT_METHOD_SWITCHER_SETTINGS) {
319 mShowInputMethodSelectorPref.setOnPreferenceChangeListener(null);
321 // TODO: Consolidate the logic to InputMethodSettingsWrapper
322 InputMethodAndSubtypeUtil.saveInputMethodSubtypeList(
323 this, getContentResolver(), mInputMethodSettingValues.getInputMethodList(),
324 !mHardKeyboardPreferenceList.isEmpty());
328 public void onInputDeviceAdded(int deviceId) {
329 updateInputDevices();
333 public void onInputDeviceChanged(int deviceId) {
334 updateInputDevices();
338 public void onInputDeviceRemoved(int deviceId) {
339 updateInputDevices();
343 public boolean onPreferenceTreeClick(PreferenceScreen preferenceScreen, Preference preference) {
344 // Input Method stuff
345 if (Utils.isMonkeyRunning()) {
348 if (preference instanceof PreferenceScreen) {
349 if (preference.getFragment() != null) {
350 // Fragment will be handled correctly by the super class.
351 } else if (KEY_CURRENT_INPUT_METHOD.equals(preference.getKey())) {
352 final InputMethodManager imm = (InputMethodManager)
353 getSystemService(Context.INPUT_METHOD_SERVICE);
354 imm.showInputMethodPicker(false /* showAuxiliarySubtypes */);
356 } else if (preference instanceof SwitchPreference) {
357 final SwitchPreference pref = (SwitchPreference) preference;
358 if (pref == mGameControllerCategory.findPreference("vibrate_input_devices")) {
359 System.putInt(getContentResolver(), Settings.System.VIBRATE_INPUT_DEVICES,
360 pref.isChecked() ? 1 : 0);
363 } else if (preference == mHighTouchSensitivity) {
364 return HighTouchSensitivity.setEnabled(mHighTouchSensitivity.isChecked());
366 return super.onPreferenceTreeClick(preferenceScreen, preference);
369 private static String getLocaleName(Context context) {
370 // We want to show the same string that the LocalePicker used.
371 // TODO: should this method be in LocalePicker instead?
372 Locale currentLocale = context.getResources().getConfiguration().locale;
373 List<LocalePicker.LocaleInfo> locales = LocalePicker.getAllAssetLocales(context, true);
374 for (LocalePicker.LocaleInfo locale : locales) {
375 if (locale.getLocale().equals(currentLocale)) {
376 return locale.getLabel();
379 // This can't happen as long as the locale was one set by Settings.
380 // Fall back in case a developer is testing an unsupported locale.
381 return currentLocale.getDisplayName(currentLocale);
384 private void saveInputMethodSelectorVisibility(String value) {
386 int intValue = Integer.valueOf(value);
387 Settings.Secure.putInt(getContentResolver(),
388 Settings.Secure.INPUT_METHOD_SELECTOR_VISIBILITY, intValue);
389 updateInputMethodSelectorSummary(intValue);
390 } catch(NumberFormatException e) {
394 private int loadInputMethodSelectorVisibility() {
395 return Settings.Secure.getInt(getContentResolver(),
396 Settings.Secure.INPUT_METHOD_SELECTOR_VISIBILITY,
397 mDefaultInputMethodSelectorVisibility);
401 public boolean onPreferenceChange(Preference preference, Object value) {
402 if (SHOW_INPUT_METHOD_SWITCHER_SETTINGS) {
403 if (preference == mShowInputMethodSelectorPref) {
404 if (value instanceof String) {
405 saveInputMethodSelectorVisibility((String)value);
412 private void updateInputMethodPreferenceViews() {
413 synchronized (mInputMethodPreferenceList) {
414 // Clear existing "InputMethodPreference"s
415 for (final InputMethodPreference pref : mInputMethodPreferenceList) {
416 mKeyboardSettingsCategory.removePreference(pref);
418 mInputMethodPreferenceList.clear();
419 List<String> permittedList = mDpm.getPermittedInputMethodsForCurrentUser();
420 final Context context = getActivity();
421 final List<InputMethodInfo> imis = mShowsOnlyFullImeAndKeyboardList
422 ? mInputMethodSettingValues.getInputMethodList()
423 : mImm.getEnabledInputMethodList();
424 final int N = (imis == null ? 0 : imis.size());
425 for (int i = 0; i < N; ++i) {
426 final InputMethodInfo imi = imis.get(i);
427 final boolean isAllowedByOrganization = permittedList == null
428 || permittedList.contains(imi.getPackageName());
429 final InputMethodPreference pref = new InputMethodPreference(
430 context, imi, mShowsOnlyFullImeAndKeyboardList /* hasSwitch */,
431 isAllowedByOrganization, this);
432 mInputMethodPreferenceList.add(pref);
434 final Collator collator = Collator.getInstance();
435 Collections.sort(mInputMethodPreferenceList, new Comparator<InputMethodPreference>() {
437 public int compare(InputMethodPreference lhs, InputMethodPreference rhs) {
438 return lhs.compareTo(rhs, collator);
441 for (int i = 0; i < N; ++i) {
442 final InputMethodPreference pref = mInputMethodPreferenceList.get(i);
443 mKeyboardSettingsCategory.addPreference(pref);
444 InputMethodAndSubtypeUtil.removeUnnecessaryNonPersistentPreference(pref);
445 pref.updatePreferenceViews();
448 updateCurrentImeName();
449 // TODO: Consolidate the logic with InputMethodSettingsWrapper
450 // CAVEAT: The preference class here does not know about the default value - that is
451 // managed by the Input Method Manager Service, so in this case it could save the wrong
452 // value. Hence we must update the checkboxes here.
453 InputMethodAndSubtypeUtil.loadInputMethodSubtypeList(
454 this, getContentResolver(),
455 mInputMethodSettingValues.getInputMethodList(), null);
459 public void onSaveInputMethodPreference(final InputMethodPreference pref) {
460 final InputMethodInfo imi = pref.getInputMethodInfo();
461 if (!pref.isChecked()) {
462 // An IME is being disabled. Save enabled subtypes of the IME to shared preference to be
463 // able to re-enable these subtypes when the IME gets re-enabled.
464 saveEnabledSubtypesOf(imi);
466 final boolean hasHardwareKeyboard = getResources().getConfiguration().keyboard
467 == Configuration.KEYBOARD_QWERTY;
468 InputMethodAndSubtypeUtil.saveInputMethodSubtypeList(this, getContentResolver(),
469 mImm.getInputMethodList(), hasHardwareKeyboard);
470 // Update input method settings and preference list.
471 mInputMethodSettingValues.refreshAllInputMethodAndSubtypes();
472 if (pref.isChecked()) {
473 // An IME is being enabled. Load the previously enabled subtypes from shared preference
474 // and enable these subtypes.
475 restorePreviouslyEnabledSubtypesOf(imi);
477 for (final InputMethodPreference p : mInputMethodPreferenceList) {
478 p.updatePreferenceViews();
482 private void saveEnabledSubtypesOf(final InputMethodInfo imi) {
483 final HashSet<String> enabledSubtypeIdSet = new HashSet<>();
484 final List<InputMethodSubtype> enabledSubtypes = mImm.getEnabledInputMethodSubtypeList(
485 imi, true /* allowsImplicitlySelectedSubtypes */);
486 for (final InputMethodSubtype subtype : enabledSubtypes) {
487 final String subtypeId = Integer.toString(subtype.hashCode());
488 enabledSubtypeIdSet.add(subtypeId);
490 final HashMap<String, HashSet<String>> imeToEnabledSubtypeIdsMap =
491 loadPreviouslyEnabledSubtypeIdsMap();
492 final String imiId = imi.getId();
493 imeToEnabledSubtypeIdsMap.put(imiId, enabledSubtypeIdSet);
494 savePreviouslyEnabledSubtypeIdsMap(imeToEnabledSubtypeIdsMap);
497 private void restorePreviouslyEnabledSubtypesOf(final InputMethodInfo imi) {
498 final HashMap<String, HashSet<String>> imeToEnabledSubtypeIdsMap =
499 loadPreviouslyEnabledSubtypeIdsMap();
500 final String imiId = imi.getId();
501 final HashSet<String> enabledSubtypeIdSet = imeToEnabledSubtypeIdsMap.remove(imiId);
502 if (enabledSubtypeIdSet == null) {
505 savePreviouslyEnabledSubtypeIdsMap(imeToEnabledSubtypeIdsMap);
506 InputMethodAndSubtypeUtil.enableInputMethodSubtypesOf(
507 getContentResolver(), imiId, enabledSubtypeIdSet);
510 private HashMap<String, HashSet<String>> loadPreviouslyEnabledSubtypeIdsMap() {
511 final Context context = getActivity();
512 final SharedPreferences prefs = PreferenceManager.getDefaultSharedPreferences(context);
513 final String imesAndSubtypesString = prefs.getString(KEY_PREVIOUSLY_ENABLED_SUBTYPES, null);
514 return InputMethodAndSubtypeUtil.parseInputMethodsAndSubtypesString(imesAndSubtypesString);
517 private void savePreviouslyEnabledSubtypeIdsMap(
518 final HashMap<String, HashSet<String>> subtypesMap) {
519 final Context context = getActivity();
520 final SharedPreferences prefs = PreferenceManager.getDefaultSharedPreferences(context);
521 final String imesAndSubtypesString = InputMethodAndSubtypeUtil
522 .buildInputMethodsAndSubtypesString(subtypesMap);
523 prefs.edit().putString(KEY_PREVIOUSLY_ENABLED_SUBTYPES, imesAndSubtypesString).apply();
526 private void updateCurrentImeName() {
527 final Context context = getActivity();
528 if (context == null || mImm == null) return;
529 final Preference curPref = getPreferenceScreen().findPreference(KEY_CURRENT_INPUT_METHOD);
530 if (curPref != null) {
531 final CharSequence curIme =
532 mInputMethodSettingValues.getCurrentInputMethodName(context);
533 if (!TextUtils.isEmpty(curIme)) {
534 synchronized (this) {
535 curPref.setSummary(curIme);
541 private void updateInputDevices() {
542 updateHardKeyboards();
543 updateGameControllers();
546 private void updateHardKeyboards() {
547 mHardKeyboardPreferenceList.clear();
548 final int[] devices = InputDevice.getDeviceIds();
549 for (int i = 0; i < devices.length; i++) {
550 InputDevice device = InputDevice.getDevice(devices[i]);
552 && !device.isVirtual()
553 && device.isFullKeyboard()) {
554 final InputDeviceIdentifier identifier = device.getIdentifier();
555 final String keyboardLayoutDescriptor =
556 mIm.getCurrentKeyboardLayoutForInputDevice(identifier);
557 final KeyboardLayout keyboardLayout = keyboardLayoutDescriptor != null ?
558 mIm.getKeyboardLayout(keyboardLayoutDescriptor) : null;
560 final PreferenceScreen pref = new PreferenceScreen(getActivity(), null);
561 pref.setTitle(device.getName());
562 if (keyboardLayout != null) {
563 pref.setSummary(keyboardLayout.toString());
565 pref.setSummary(R.string.keyboard_layout_default_label);
567 pref.setOnPreferenceClickListener(new Preference.OnPreferenceClickListener() {
569 public boolean onPreferenceClick(Preference preference) {
570 showKeyboardLayoutDialog(identifier);
574 mHardKeyboardPreferenceList.add(pref);
578 if (!mHardKeyboardPreferenceList.isEmpty()) {
579 for (int i = mHardKeyboardCategory.getPreferenceCount(); i-- > 0; ) {
580 final Preference pref = mHardKeyboardCategory.getPreference(i);
581 if (pref.getOrder() < 1000) {
582 mHardKeyboardCategory.removePreference(pref);
586 Collections.sort(mHardKeyboardPreferenceList);
587 final int count = mHardKeyboardPreferenceList.size();
588 for (int i = 0; i < count; i++) {
589 final Preference pref = mHardKeyboardPreferenceList.get(i);
591 mHardKeyboardCategory.addPreference(pref);
594 getPreferenceScreen().addPreference(mHardKeyboardCategory);
596 getPreferenceScreen().removePreference(mHardKeyboardCategory);
600 private void showKeyboardLayoutDialog(InputDeviceIdentifier inputDeviceIdentifier) {
601 KeyboardLayoutDialogFragment fragment = new KeyboardLayoutDialogFragment(
602 inputDeviceIdentifier);
603 fragment.setTargetFragment(this, 0);
604 fragment.show(getActivity().getFragmentManager(), "keyboardLayout");
608 public void onSetupKeyboardLayouts(InputDeviceIdentifier inputDeviceIdentifier) {
609 final Intent intent = new Intent(Intent.ACTION_MAIN);
610 intent.setClass(getActivity(), KeyboardLayoutPickerActivity.class);
611 intent.putExtra(KeyboardLayoutPickerFragment.EXTRA_INPUT_DEVICE_IDENTIFIER,
612 inputDeviceIdentifier);
613 mIntentWaitingForResult = intent;
614 startActivityForResult(intent, 0);
618 public void onActivityResult(int requestCode, int resultCode, Intent data) {
619 super.onActivityResult(requestCode, resultCode, data);
621 if (mIntentWaitingForResult != null) {
622 InputDeviceIdentifier inputDeviceIdentifier = mIntentWaitingForResult
623 .getParcelableExtra(KeyboardLayoutPickerFragment.EXTRA_INPUT_DEVICE_IDENTIFIER);
624 mIntentWaitingForResult = null;
625 showKeyboardLayoutDialog(inputDeviceIdentifier);
629 private void updateGameControllers() {
630 if (haveInputDeviceWithVibrator()) {
631 getPreferenceScreen().addPreference(mGameControllerCategory);
633 SwitchPreference pref = (SwitchPreference)
634 mGameControllerCategory.findPreference("vibrate_input_devices");
635 pref.setChecked(System.getInt(getContentResolver(),
636 Settings.System.VIBRATE_INPUT_DEVICES, 1) > 0);
638 getPreferenceScreen().removePreference(mGameControllerCategory);
642 private static boolean isHighTouchSensitivitySupported() {
644 return HighTouchSensitivity.isSupported();
645 } catch (NoClassDefFoundError e) {
646 // Hardware abstraction framework not installed
651 private static boolean haveInputDeviceWithVibrator() {
652 final int[] devices = InputDevice.getDeviceIds();
653 for (int i = 0; i < devices.length; i++) {
654 InputDevice device = InputDevice.getDevice(devices[i]);
655 if (device != null && !device.isVirtual() && device.getVibrator().hasVibrator()) {
662 private class SettingsObserver extends ContentObserver {
663 private Context mContext;
665 public SettingsObserver(Handler handler, Context context) {
670 @Override public void onChange(boolean selfChange) {
671 updateCurrentImeName();
674 public void resume() {
675 final ContentResolver cr = mContext.getContentResolver();
676 cr.registerContentObserver(
677 Settings.Secure.getUriFor(Settings.Secure.DEFAULT_INPUT_METHOD), false, this);
678 cr.registerContentObserver(Settings.Secure.getUriFor(
679 Settings.Secure.SELECTED_INPUT_METHOD_SUBTYPE), false, this);
682 public void pause() {
683 mContext.getContentResolver().unregisterContentObserver(this);
687 public static final Indexable.SearchIndexProvider SEARCH_INDEX_DATA_PROVIDER =
688 new BaseSearchIndexProvider() {
690 public List<SearchIndexableRaw> getRawDataToIndex(Context context, boolean enabled) {
691 List<SearchIndexableRaw> indexables = new ArrayList<>();
693 final String screenTitle = context.getString(R.string.language_keyboard_settings_title);
696 if (context.getAssets().getLocales().length > 1) {
697 String localeName = getLocaleName(context);
698 SearchIndexableRaw indexable = new SearchIndexableRaw(context);
699 indexable.key = KEY_PHONE_LANGUAGE;
700 indexable.title = context.getString(R.string.phone_language);
701 indexable.summaryOn = localeName;
702 indexable.summaryOff = localeName;
703 indexable.screenTitle = screenTitle;
704 indexables.add(indexable);
708 SearchIndexableRaw indexable = new SearchIndexableRaw(context);
709 indexable.key = KEY_SPELL_CHECKERS;
710 indexable.title = context.getString(R.string.spellcheckers_settings_title);
711 indexable.screenTitle = screenTitle;
712 indexable.keywords = context.getString(R.string.keywords_spell_checker);
713 indexables.add(indexable);
716 if (UserDictionaryList.getUserDictionaryLocalesSet(context) != null) {
717 indexable = new SearchIndexableRaw(context);
718 indexable.key = "user_dict_settings";
719 indexable.title = context.getString(R.string.user_dict_settings_title);
720 indexable.screenTitle = screenTitle;
721 indexables.add(indexable);
724 // Keyboard settings.
725 indexable = new SearchIndexableRaw(context);
726 indexable.key = "keyboard_settings";
727 indexable.title = context.getString(R.string.keyboard_settings_category);
728 indexable.screenTitle = screenTitle;
729 indexable.keywords = context.getString(R.string.keywords_keyboard_and_ime);
730 indexables.add(indexable);
732 InputMethodSettingValuesWrapper immValues = InputMethodSettingValuesWrapper
733 .getInstance(context);
734 immValues.refreshAllInputMethodAndSubtypes();
737 String currImeName = immValues.getCurrentInputMethodName(context).toString();
738 indexable = new SearchIndexableRaw(context);
739 indexable.key = KEY_CURRENT_INPUT_METHOD;
740 indexable.title = context.getString(R.string.current_input_method);
741 indexable.summaryOn = currImeName;
742 indexable.summaryOff = currImeName;
743 indexable.screenTitle = screenTitle;
744 indexables.add(indexable);
746 InputMethodManager inputMethodManager = (InputMethodManager) context.getSystemService(
747 Context.INPUT_METHOD_SERVICE);
750 List<InputMethodInfo> inputMethods = immValues.getInputMethodList();
751 final int inputMethodCount = (inputMethods == null ? 0 : inputMethods.size());
752 for (int i = 0; i < inputMethodCount; ++i) {
753 InputMethodInfo inputMethod = inputMethods.get(i);
755 StringBuilder builder = new StringBuilder();
756 List<InputMethodSubtype> subtypes = inputMethodManager
757 .getEnabledInputMethodSubtypeList(inputMethod, true);
758 final int subtypeCount = subtypes.size();
759 for (int j = 0; j < subtypeCount; j++) {
760 InputMethodSubtype subtype = subtypes.get(j);
761 if (builder.length() > 0) {
764 CharSequence subtypeLabel = subtype.getDisplayName(context,
765 inputMethod.getPackageName(), inputMethod.getServiceInfo()
767 builder.append(subtypeLabel);
769 String summary = builder.toString();
771 ServiceInfo serviceInfo = inputMethod.getServiceInfo();
772 ComponentName componentName = new ComponentName(serviceInfo.packageName,
775 indexable = new SearchIndexableRaw(context);
776 indexable.key = componentName.flattenToString();
777 indexable.title = inputMethod.loadLabel(context.getPackageManager()).toString();
778 indexable.summaryOn = summary;
779 indexable.summaryOff = summary;
780 indexable.screenTitle = screenTitle;
781 indexables.add(indexable);
785 InputManager inputManager = (InputManager) context.getSystemService(
786 Context.INPUT_SERVICE);
787 boolean hasHardKeyboards = false;
789 final int[] devices = InputDevice.getDeviceIds();
790 for (int i = 0; i < devices.length; i++) {
791 InputDevice device = InputDevice.getDevice(devices[i]);
792 if (device == null || device.isVirtual() || !device.isFullKeyboard()) {
796 hasHardKeyboards = true;
798 InputDeviceIdentifier identifier = device.getIdentifier();
799 String keyboardLayoutDescriptor =
800 inputManager.getCurrentKeyboardLayoutForInputDevice(identifier);
801 KeyboardLayout keyboardLayout = keyboardLayoutDescriptor != null ?
802 inputManager.getKeyboardLayout(keyboardLayoutDescriptor) : null;
805 if (keyboardLayout != null) {
806 summary = keyboardLayout.toString();
808 summary = context.getString(R.string.keyboard_layout_default_label);
811 indexable = new SearchIndexableRaw(context);
812 indexable.key = device.getName();
813 indexable.title = device.getName();
814 indexable.summaryOn = summary;
815 indexable.summaryOff = summary;
816 indexable.screenTitle = screenTitle;
817 indexables.add(indexable);
820 if (hasHardKeyboards) {
821 // Hard keyboard category.
822 indexable = new SearchIndexableRaw(context);
823 indexable.key = "builtin_keyboard_settings";
824 indexable.title = context.getString(
825 R.string.builtin_keyboard_settings_title);
826 indexable.screenTitle = screenTitle;
827 indexables.add(indexable);
831 TtsEngines ttsEngines = new TtsEngines(context);
832 if (!ttsEngines.getEngines().isEmpty()) {
833 indexable = new SearchIndexableRaw(context);
834 indexable.key = "tts_settings";
835 indexable.title = context.getString(R.string.tts_settings_title);
836 indexable.screenTitle = screenTitle;
837 indexable.keywords = context.getString(R.string.keywords_text_to_speech_output);
838 indexables.add(indexable);
842 indexable = new SearchIndexableRaw(context);
843 indexable.key = "pointer_settings_category";
844 indexable.title = context.getString(R.string.pointer_settings_category);
845 indexable.screenTitle = screenTitle;
846 indexables.add(indexable);
848 indexable = new SearchIndexableRaw(context);
849 indexable.key = "pointer_speed";
850 indexable.title = context.getString(R.string.pointer_speed);
851 indexable.screenTitle = screenTitle;
852 indexables.add(indexable);
855 if (haveInputDeviceWithVibrator()) {
856 indexable = new SearchIndexableRaw(context);
857 indexable.key = "vibrate_input_devices";
858 indexable.title = context.getString(R.string.vibrate_input_devices);
859 indexable.summaryOn = context.getString(R.string.vibrate_input_devices_summary);
860 indexable.summaryOff = context.getString(R.string.vibrate_input_devices_summary);
861 indexable.screenTitle = screenTitle;
862 indexables.add(indexable);