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.util.Log;
47 import android.view.InputDevice;
48 import android.view.inputmethod.InputMethodInfo;
49 import android.view.inputmethod.InputMethodManager;
50 import android.view.inputmethod.InputMethodSubtype;
51 import android.view.textservice.SpellCheckerInfo;
52 import android.view.textservice.TextServicesManager;
54 import com.android.internal.app.LocalePicker;
55 import com.android.internal.logging.MetricsLogger;
56 import com.android.settings.R;
57 import com.android.settings.Settings.KeyboardLayoutPickerActivity;
58 import com.android.settings.SettingsActivity;
59 import com.android.settings.SettingsPreferenceFragment;
60 import com.android.settings.SubSettings;
61 import com.android.settings.UserDictionarySettings;
62 import com.android.settings.Utils;
63 import com.android.settings.VoiceInputOutputSettings;
64 import com.android.settings.search.BaseSearchIndexProvider;
65 import com.android.settings.search.Indexable;
66 import com.android.settings.search.SearchIndexableRaw;
68 import cyanogenmod.hardware.CMHardwareManager;
69 import cyanogenmod.providers.CMSettings;
71 import java.text.Collator;
72 import java.util.ArrayList;
73 import java.util.Collections;
74 import java.util.Comparator;
75 import java.util.HashMap;
76 import java.util.HashSet;
77 import java.util.List;
78 import java.util.Locale;
79 import java.util.TreeSet;
81 public class InputMethodAndLanguageSettings extends SettingsPreferenceFragment
82 implements Preference.OnPreferenceChangeListener, InputManager.InputDeviceListener,
83 KeyboardLayoutDialogFragment.OnSetupKeyboardLayoutsListener, Indexable,
84 InputMethodPreference.OnSavePreferenceListener {
86 private static final String TAG = "InputMethodAndLanguageSettings";
88 private static final String KEY_SPELL_CHECKERS = "spellcheckers_settings";
89 private static final String KEY_PHONE_LANGUAGE = "phone_language";
90 private static final String KEY_CURRENT_INPUT_METHOD = "current_input_method";
91 private static final String KEY_INPUT_METHOD_SELECTOR = "input_method_selector";
92 private static final String KEY_USER_DICTIONARY_SETTINGS = "key_user_dictionary_settings";
93 private static final String KEY_POINTER_SETTINGS_CATEGORY = "pointer_settings_category";
94 private static final String KEY_PREVIOUSLY_ENABLED_SUBTYPES = "previously_enabled_subtypes";
95 private static final String KEY_HIGH_TOUCH_SENSITIVITY = "high_touch_sensitivity";
96 private static final String KEY_TOUCHSCREEN_HOVERING = "touchscreen_hovering";
97 private static final String KEY_TRACKPAD_SETTINGS = "gesture_pad_settings";
98 private static final String KEY_STYLUS_GESTURES = "stylus_gestures";
99 private static final String KEY_STYLUS_ICON_ENABLED = "stylus_icon_enabled";
100 private static final String KEY_VOICE_CATEGORY = "voice_category";
101 private static final String KEY_VOICE_WAKEUP = "voice_wakeup";
103 private static final String VOICE_WAKEUP_PACKAGE = "com.cyanogenmod.voicewakeup";
105 // false: on ICS or later
106 private static final boolean SHOW_INPUT_METHOD_SWITCHER_SETTINGS = false;
108 private static final String[] sSystemSettingNames = {
109 System.TEXT_AUTO_REPLACE, System.TEXT_AUTO_CAPS, System.TEXT_AUTO_PUNCTUATE,
112 private static final String[] sHardKeyboardKeys = {
113 "auto_replace", "auto_caps", "auto_punctuate",
116 private int mDefaultInputMethodSelectorVisibility = 0;
117 private ListPreference mShowInputMethodSelectorPref;
118 private SwitchPreference mStylusIconEnabled;
119 private SwitchPreference mHighTouchSensitivity;
120 private SwitchPreference mTouchscreenHovering;
121 private PreferenceCategory mKeyboardSettingsCategory;
122 private PreferenceCategory mHardKeyboardCategory;
123 private PreferenceCategory mGameControllerCategory;
124 private Preference mLanguagePref;
125 private PreferenceScreen mStylusGestures;
126 private final ArrayList<InputMethodPreference> mInputMethodPreferenceList = new ArrayList<>();
127 private final ArrayList<PreferenceScreen> mHardKeyboardPreferenceList = new ArrayList<>();
128 private InputManager mIm;
129 private InputMethodManager mImm;
130 private boolean mShowsOnlyFullImeAndKeyboardList;
131 private Handler mHandler;
132 private SettingsObserver mSettingsObserver;
133 private Intent mIntentWaitingForResult;
134 private InputMethodSettingValuesWrapper mInputMethodSettingValues;
135 private DevicePolicyManager mDpm;
136 private CMHardwareManager mHardware;
139 protected int getMetricsCategory() {
140 return MetricsLogger.INPUTMETHOD_LANGUAGE;
144 public void onCreate(Bundle icicle) {
145 super.onCreate(icicle);
147 addPreferencesFromResource(R.xml.language_settings);
149 final Activity activity = getActivity();
150 mImm = (InputMethodManager) getSystemService(Context.INPUT_METHOD_SERVICE);
151 mInputMethodSettingValues = InputMethodSettingValuesWrapper.getInstance(activity);
153 mHardware = CMHardwareManager.getInstance(activity);
156 mDefaultInputMethodSelectorVisibility = Integer.valueOf(
157 getString(R.string.input_method_selector_visibility_default_value));
158 } catch (NumberFormatException e) {
161 if (activity.getAssets().getLocales().length == 1) {
162 // No "Select language" pref if there's only one system locale available.
163 getPreferenceScreen().removePreference(findPreference(KEY_PHONE_LANGUAGE));
165 mLanguagePref = findPreference(KEY_PHONE_LANGUAGE);
167 if (SHOW_INPUT_METHOD_SWITCHER_SETTINGS) {
168 mShowInputMethodSelectorPref = (ListPreference)findPreference(
169 KEY_INPUT_METHOD_SELECTOR);
170 mShowInputMethodSelectorPref.setOnPreferenceChangeListener(this);
171 // TODO: Update current input method name on summary
172 updateInputMethodSelectorSummary(loadInputMethodSelectorVisibility());
175 new VoiceInputOutputSettings(this).onCreate();
177 // Get references to dynamically constructed categories.
178 mHardKeyboardCategory = (PreferenceCategory)findPreference("hard_keyboard");
179 mKeyboardSettingsCategory = (PreferenceCategory)findPreference(
180 "keyboard_settings_category");
181 mGameControllerCategory = (PreferenceCategory)findPreference(
182 "game_controller_settings_category");
184 final Intent startingIntent = activity.getIntent();
185 // Filter out irrelevant features if invoked from IME settings button.
186 mShowsOnlyFullImeAndKeyboardList = Settings.ACTION_INPUT_METHOD_SETTINGS.equals(
187 startingIntent.getAction());
188 if (mShowsOnlyFullImeAndKeyboardList) {
189 getPreferenceScreen().removeAll();
190 getPreferenceScreen().addPreference(mHardKeyboardCategory);
191 if (SHOW_INPUT_METHOD_SWITCHER_SETTINGS) {
192 getPreferenceScreen().addPreference(mShowInputMethodSelectorPref);
194 mKeyboardSettingsCategory.removeAll();
195 getPreferenceScreen().addPreference(mKeyboardSettingsCategory);
198 // Build hard keyboard and game controller preference categories.
199 mIm = (InputManager)activity.getSystemService(Context.INPUT_SERVICE);
200 updateInputDevices();
202 PreferenceCategory pointerSettingsCategory = (PreferenceCategory)
203 findPreference(KEY_POINTER_SETTINGS_CATEGORY);
205 mStylusGestures = (PreferenceScreen) findPreference(KEY_STYLUS_GESTURES);
206 mStylusIconEnabled = (SwitchPreference) findPreference(KEY_STYLUS_ICON_ENABLED);
207 mHighTouchSensitivity = (SwitchPreference) findPreference(KEY_HIGH_TOUCH_SENSITIVITY);
209 mTouchscreenHovering = (SwitchPreference) findPreference(KEY_TOUCHSCREEN_HOVERING);
211 if (pointerSettingsCategory != null) {
212 if (!getResources().getBoolean(com.android.internal.R.bool.config_stylusGestures)) {
213 pointerSettingsCategory.removePreference(mStylusGestures);
214 pointerSettingsCategory.removePreference(mStylusIconEnabled);
217 if (!mHardware.isSupported(
218 CMHardwareManager.FEATURE_HIGH_TOUCH_SENSITIVITY)) {
219 pointerSettingsCategory.removePreference(mHighTouchSensitivity);
220 mHighTouchSensitivity = null;
222 mHighTouchSensitivity.setChecked(
223 mHardware.get(CMHardwareManager.FEATURE_HIGH_TOUCH_SENSITIVITY));
226 if (!mHardware.isSupported(CMHardwareManager.FEATURE_TOUCH_HOVERING)) {
227 pointerSettingsCategory.removePreference(mTouchscreenHovering);
228 mTouchscreenHovering = null;
230 mTouchscreenHovering.setChecked(
231 mHardware.get(CMHardwareManager.FEATURE_TOUCH_HOVERING));
234 Utils.updatePreferenceToSpecificActivityFromMetaDataOrRemove(getActivity(),
235 pointerSettingsCategory, KEY_TRACKPAD_SETTINGS);
236 if (pointerSettingsCategory.getPreferenceCount() == 0) {
237 getPreferenceScreen().removePreference(pointerSettingsCategory);
241 // Enable or disable mStatusBarImeSwitcher based on boolean: config_show_cmIMESwitcher
242 boolean showCmImeSwitcher = getResources().getBoolean(
243 com.android.internal.R.bool.config_show_cmIMESwitcher);
244 if (!showCmImeSwitcher) {
245 Preference pref = findPreference(CMSettings.System.STATUS_BAR_IME_SWITCHER);
247 getPreferenceScreen().removePreference(pref);
252 final Preference spellChecker = findPreference(KEY_SPELL_CHECKERS);
253 if (spellChecker != null) {
254 // Note: KEY_SPELL_CHECKERS preference is marked as persistent="false" in XML.
255 InputMethodAndSubtypeUtil.removeUnnecessaryNonPersistentPreference(spellChecker);
256 final Intent intent = new Intent(Intent.ACTION_MAIN);
257 intent.setClass(activity, SubSettings.class);
258 intent.putExtra(SettingsActivity.EXTRA_SHOW_FRAGMENT,
259 SpellCheckersSettings.class.getName());
260 intent.putExtra(SettingsActivity.EXTRA_SHOW_FRAGMENT_TITLE_RESID,
261 R.string.spellcheckers_settings_title);
262 spellChecker.setIntent(intent);
265 mHandler = new Handler();
266 mSettingsObserver = new SettingsObserver(mHandler, activity);
267 mDpm = (DevicePolicyManager) (getActivity().
268 getSystemService(Context.DEVICE_POLICY_SERVICE));
270 // If we've launched from the keyboard layout notification, go ahead and just show the
271 // keyboard layout dialog.
272 final InputDeviceIdentifier identifier =
273 startingIntent.getParcelableExtra(Settings.EXTRA_INPUT_DEVICE_IDENTIFIER);
274 if (mShowsOnlyFullImeAndKeyboardList && identifier != null) {
275 showKeyboardLayoutDialog(identifier);
278 if (!Utils.isUserOwner() ||
279 !Utils.isPackageInstalled(getActivity(), VOICE_WAKEUP_PACKAGE, false)) {
280 PreferenceCategory voiceCategory = (PreferenceCategory)
281 findPreference(KEY_VOICE_CATEGORY);
282 if (voiceCategory != null) {
283 Preference wakeup = voiceCategory.findPreference(KEY_VOICE_WAKEUP);
284 if (wakeup != null) {
285 voiceCategory.removePreference(wakeup);
292 private void updateInputMethodSelectorSummary(int value) {
293 String[] inputMethodSelectorTitles = getResources().getStringArray(
294 R.array.input_method_selector_titles);
295 if (inputMethodSelectorTitles.length > value) {
296 mShowInputMethodSelectorPref.setSummary(inputMethodSelectorTitles[value]);
297 mShowInputMethodSelectorPref.setValue(String.valueOf(value));
301 private void updateUserDictionaryPreference(Preference userDictionaryPreference) {
302 final Activity activity = getActivity();
303 final TreeSet<String> localeSet = UserDictionaryList.getUserDictionaryLocalesSet(activity);
304 if (null == localeSet) {
305 // The locale list is null if and only if the user dictionary service is
306 // not present or disabled. In this case we need to remove the preference.
307 getPreferenceScreen().removePreference(userDictionaryPreference);
309 userDictionaryPreference.setOnPreferenceClickListener(
310 new OnPreferenceClickListener() {
312 public boolean onPreferenceClick(Preference arg0) {
313 // Redirect to UserDictionarySettings if the user needs only one
315 final Bundle extras = new Bundle();
316 final Class<? extends Fragment> targetFragment;
317 if (localeSet.size() <= 1) {
318 if (!localeSet.isEmpty()) {
319 // If the size of localeList is 0, we don't set the locale
320 // parameter in the extras. This will be interpreted by the
321 // UserDictionarySettings class as meaning
322 // "the current locale". Note that with the current code for
323 // UserDictionaryList#getUserDictionaryLocalesSet()
324 // the locale list always has at least one element, since it
325 // always includes the current locale explicitly.
326 // @see UserDictionaryList.getUserDictionaryLocalesSet().
327 extras.putString("locale", localeSet.first());
329 targetFragment = UserDictionarySettings.class;
331 targetFragment = UserDictionaryList.class;
333 startFragment(InputMethodAndLanguageSettings.this,
334 targetFragment.getCanonicalName(), -1, -1, extras);
342 public void onResume() {
345 mSettingsObserver.resume();
346 mIm.registerInputDeviceListener(this, null);
348 final Preference spellChecker = findPreference(KEY_SPELL_CHECKERS);
349 if (spellChecker != null) {
350 final TextServicesManager tsm = (TextServicesManager) getSystemService(
351 Context.TEXT_SERVICES_MANAGER_SERVICE);
352 final SpellCheckerInfo sci = tsm.getCurrentSpellChecker();
353 spellChecker.setEnabled(sci != null);
354 if (tsm.isSpellCheckerEnabled() && sci != null) {
355 spellChecker.setSummary(sci.loadLabel(getPackageManager()));
357 spellChecker.setSummary(R.string.switch_off_text);
361 if (mStylusIconEnabled != null) {
362 mStylusIconEnabled.setChecked(Settings.System.getInt(getActivity().getContentResolver(),
363 Settings.System.STYLUS_ICON_ENABLED, 0) == 1);
366 if (!mShowsOnlyFullImeAndKeyboardList) {
367 if (mLanguagePref != null) {
368 String localeName = getLocaleName(getActivity());
369 mLanguagePref.setSummary(localeName);
372 updateUserDictionaryPreference(findPreference(KEY_USER_DICTIONARY_SETTINGS));
373 if (SHOW_INPUT_METHOD_SWITCHER_SETTINGS) {
374 mShowInputMethodSelectorPref.setOnPreferenceChangeListener(this);
379 if (!mHardKeyboardPreferenceList.isEmpty()) {
380 for (int i = 0; i < sHardKeyboardKeys.length; ++i) {
381 SwitchPreference swPref = (SwitchPreference)
382 mHardKeyboardCategory.findPreference(sHardKeyboardKeys[i]);
384 System.getInt(getContentResolver(), sSystemSettingNames[i], 1) > 0);
388 updateInputDevices();
390 // Refresh internal states in mInputMethodSettingValues to keep the latest
391 // "InputMethodInfo"s and "InputMethodSubtype"s
392 mInputMethodSettingValues.refreshAllInputMethodAndSubtypes();
393 updateInputMethodPreferenceViews();
397 public void onPause() {
400 mIm.unregisterInputDeviceListener(this);
401 mSettingsObserver.pause();
403 if (SHOW_INPUT_METHOD_SWITCHER_SETTINGS) {
404 mShowInputMethodSelectorPref.setOnPreferenceChangeListener(null);
406 // TODO: Consolidate the logic to InputMethodSettingsWrapper
407 InputMethodAndSubtypeUtil.saveInputMethodSubtypeList(
408 this, getContentResolver(), mInputMethodSettingValues.getInputMethodList(),
409 !mHardKeyboardPreferenceList.isEmpty());
413 public void onInputDeviceAdded(int deviceId) {
414 updateInputDevices();
418 public void onInputDeviceChanged(int deviceId) {
419 updateInputDevices();
423 public void onInputDeviceRemoved(int deviceId) {
424 updateInputDevices();
428 public boolean onPreferenceTreeClick(PreferenceScreen preferenceScreen, Preference preference) {
429 // Input Method stuff
430 if (Utils.isMonkeyRunning()) {
433 if (preference == mStylusIconEnabled) {
434 Settings.System.putInt(getActivity().getContentResolver(),
435 Settings.System.STYLUS_ICON_ENABLED, mStylusIconEnabled.isChecked() ? 1 : 0);
436 } else if (preference == mHighTouchSensitivity) {
437 boolean mHighTouchSensitivityEnable = mHighTouchSensitivity.isChecked();
438 CMSettings.System.putInt(getActivity().getContentResolver(),
439 CMSettings.System.HIGH_TOUCH_SENSITIVITY_ENABLE,
440 mHighTouchSensitivityEnable ? 1 : 0);
441 return mHardware.set(CMHardwareManager.FEATURE_HIGH_TOUCH_SENSITIVITY,
442 mHighTouchSensitivityEnable);
443 } else if (preference == mTouchscreenHovering) {
444 return mHardware.set(CMHardwareManager.FEATURE_TOUCH_HOVERING,
445 mTouchscreenHovering.isChecked());
446 } else if (preference instanceof PreferenceScreen) {
447 if (preference.getFragment() != null) {
448 // Fragment will be handled correctly by the super class.
449 } else if (KEY_CURRENT_INPUT_METHOD.equals(preference.getKey())) {
450 final InputMethodManager imm = (InputMethodManager)
451 getSystemService(Context.INPUT_METHOD_SERVICE);
452 imm.showInputMethodPicker(false /* showAuxiliarySubtypes */);
454 } else if (preference instanceof SwitchPreference) {
455 final SwitchPreference pref = (SwitchPreference) preference;
456 if (pref == mGameControllerCategory.findPreference("vibrate_input_devices")) {
457 System.putInt(getContentResolver(), Settings.System.VIBRATE_INPUT_DEVICES,
458 pref.isChecked() ? 1 : 0);
461 if (!mHardKeyboardPreferenceList.isEmpty()) {
462 for (int i = 0; i < sHardKeyboardKeys.length; ++i) {
463 if (pref == mHardKeyboardCategory.findPreference(sHardKeyboardKeys[i])) {
464 System.putInt(getContentResolver(), sSystemSettingNames[i],
465 pref.isChecked() ? 1 : 0);
471 return super.onPreferenceTreeClick(preferenceScreen, preference);
474 private static String getLocaleName(Context context) {
475 // We want to show the same string that the LocalePicker used.
476 // TODO: should this method be in LocalePicker instead?
477 Locale currentLocale = context.getResources().getConfiguration().locale;
478 List<LocalePicker.LocaleInfo> locales = LocalePicker.getAllAssetLocales(context, true);
479 for (LocalePicker.LocaleInfo locale : locales) {
480 if (locale.getLocale().equals(currentLocale)) {
481 return locale.getLabel();
484 // This can't happen as long as the locale was one set by Settings.
485 // Fall back in case a developer is testing an unsupported locale.
486 return currentLocale.getDisplayName(currentLocale);
489 private void saveInputMethodSelectorVisibility(String value) {
491 int intValue = Integer.valueOf(value);
492 Settings.Secure.putInt(getContentResolver(),
493 Settings.Secure.INPUT_METHOD_SELECTOR_VISIBILITY, intValue);
494 updateInputMethodSelectorSummary(intValue);
495 } catch(NumberFormatException e) {
499 private int loadInputMethodSelectorVisibility() {
500 return Settings.Secure.getInt(getContentResolver(),
501 Settings.Secure.INPUT_METHOD_SELECTOR_VISIBILITY,
502 mDefaultInputMethodSelectorVisibility);
506 public boolean onPreferenceChange(Preference preference, Object value) {
507 if (SHOW_INPUT_METHOD_SWITCHER_SETTINGS) {
508 if (preference == mShowInputMethodSelectorPref) {
509 if (value instanceof String) {
510 saveInputMethodSelectorVisibility((String)value);
517 private void updateInputMethodPreferenceViews() {
518 synchronized (mInputMethodPreferenceList) {
519 // Clear existing "InputMethodPreference"s
520 for (final InputMethodPreference pref : mInputMethodPreferenceList) {
521 mKeyboardSettingsCategory.removePreference(pref);
523 mInputMethodPreferenceList.clear();
524 List<String> permittedList = mDpm.getPermittedInputMethodsForCurrentUser();
525 final Context context = getActivity();
526 final List<InputMethodInfo> imis = mShowsOnlyFullImeAndKeyboardList
527 ? mInputMethodSettingValues.getInputMethodList()
528 : mImm.getEnabledInputMethodList();
529 final int N = (imis == null ? 0 : imis.size());
530 for (int i = 0; i < N; ++i) {
531 final InputMethodInfo imi = imis.get(i);
532 final boolean isAllowedByOrganization = permittedList == null
533 || permittedList.contains(imi.getPackageName());
534 final InputMethodPreference pref = new InputMethodPreference(
535 context, imi, mShowsOnlyFullImeAndKeyboardList /* hasSwitch */,
536 isAllowedByOrganization, this);
537 mInputMethodPreferenceList.add(pref);
539 final Collator collator = Collator.getInstance();
540 Collections.sort(mInputMethodPreferenceList, new Comparator<InputMethodPreference>() {
542 public int compare(InputMethodPreference lhs, InputMethodPreference rhs) {
543 return lhs.compareTo(rhs, collator);
546 for (int i = 0; i < N; ++i) {
547 final InputMethodPreference pref = mInputMethodPreferenceList.get(i);
548 mKeyboardSettingsCategory.addPreference(pref);
549 InputMethodAndSubtypeUtil.removeUnnecessaryNonPersistentPreference(pref);
550 pref.updatePreferenceViews();
553 updateCurrentImeName();
554 // TODO: Consolidate the logic with InputMethodSettingsWrapper
555 // CAVEAT: The preference class here does not know about the default value - that is
556 // managed by the Input Method Manager Service, so in this case it could save the wrong
557 // value. Hence we must update the checkboxes here.
558 InputMethodAndSubtypeUtil.loadInputMethodSubtypeList(
559 this, getContentResolver(),
560 mInputMethodSettingValues.getInputMethodList(), null);
564 public void onSaveInputMethodPreference(final InputMethodPreference pref) {
565 final InputMethodInfo imi = pref.getInputMethodInfo();
566 if (!pref.isChecked()) {
567 // An IME is being disabled. Save enabled subtypes of the IME to shared preference to be
568 // able to re-enable these subtypes when the IME gets re-enabled.
569 saveEnabledSubtypesOf(imi);
571 final boolean hasHardwareKeyboard = getResources().getConfiguration().keyboard
572 == Configuration.KEYBOARD_QWERTY;
573 InputMethodAndSubtypeUtil.saveInputMethodSubtypeList(this, getContentResolver(),
574 mImm.getInputMethodList(), hasHardwareKeyboard);
575 // Update input method settings and preference list.
576 mInputMethodSettingValues.refreshAllInputMethodAndSubtypes();
577 if (pref.isChecked()) {
578 // An IME is being enabled. Load the previously enabled subtypes from shared preference
579 // and enable these subtypes.
580 restorePreviouslyEnabledSubtypesOf(imi);
582 for (final InputMethodPreference p : mInputMethodPreferenceList) {
583 p.updatePreferenceViews();
587 private void saveEnabledSubtypesOf(final InputMethodInfo imi) {
588 final HashSet<String> enabledSubtypeIdSet = new HashSet<>();
589 final List<InputMethodSubtype> enabledSubtypes = mImm.getEnabledInputMethodSubtypeList(
590 imi, true /* allowsImplicitlySelectedSubtypes */);
591 for (final InputMethodSubtype subtype : enabledSubtypes) {
592 final String subtypeId = Integer.toString(subtype.hashCode());
593 enabledSubtypeIdSet.add(subtypeId);
595 final HashMap<String, HashSet<String>> imeToEnabledSubtypeIdsMap =
596 loadPreviouslyEnabledSubtypeIdsMap();
597 final String imiId = imi.getId();
598 imeToEnabledSubtypeIdsMap.put(imiId, enabledSubtypeIdSet);
599 savePreviouslyEnabledSubtypeIdsMap(imeToEnabledSubtypeIdsMap);
602 private void restorePreviouslyEnabledSubtypesOf(final InputMethodInfo imi) {
603 final HashMap<String, HashSet<String>> imeToEnabledSubtypeIdsMap =
604 loadPreviouslyEnabledSubtypeIdsMap();
605 final String imiId = imi.getId();
606 final HashSet<String> enabledSubtypeIdSet = imeToEnabledSubtypeIdsMap.remove(imiId);
607 if (enabledSubtypeIdSet == null) {
610 savePreviouslyEnabledSubtypeIdsMap(imeToEnabledSubtypeIdsMap);
611 InputMethodAndSubtypeUtil.enableInputMethodSubtypesOf(
612 getContentResolver(), imiId, enabledSubtypeIdSet);
615 private HashMap<String, HashSet<String>> loadPreviouslyEnabledSubtypeIdsMap() {
616 final Context context = getActivity();
617 final SharedPreferences prefs = PreferenceManager.getDefaultSharedPreferences(context);
618 final String imesAndSubtypesString = prefs.getString(KEY_PREVIOUSLY_ENABLED_SUBTYPES, null);
619 return InputMethodAndSubtypeUtil.parseInputMethodsAndSubtypesString(imesAndSubtypesString);
622 private void savePreviouslyEnabledSubtypeIdsMap(
623 final HashMap<String, HashSet<String>> subtypesMap) {
624 final Context context = getActivity();
625 final SharedPreferences prefs = PreferenceManager.getDefaultSharedPreferences(context);
626 final String imesAndSubtypesString = InputMethodAndSubtypeUtil
627 .buildInputMethodsAndSubtypesString(subtypesMap);
628 prefs.edit().putString(KEY_PREVIOUSLY_ENABLED_SUBTYPES, imesAndSubtypesString).apply();
631 private void updateCurrentImeName() {
632 final Context context = getActivity();
633 if (context == null || mImm == null) return;
634 final Preference curPref = getPreferenceScreen().findPreference(KEY_CURRENT_INPUT_METHOD);
635 if (curPref != null) {
636 final CharSequence curIme =
637 mInputMethodSettingValues.getCurrentInputMethodName(context);
638 if (!TextUtils.isEmpty(curIme)) {
639 synchronized (this) {
640 curPref.setSummary(curIme);
646 private void updateInputDevices() {
647 updateHardKeyboards();
648 updateGameControllers();
651 private void updateHardKeyboards() {
652 mHardKeyboardPreferenceList.clear();
653 final int[] devices = InputDevice.getDeviceIds();
654 for (int i = 0; i < devices.length; i++) {
655 InputDevice device = InputDevice.getDevice(devices[i]);
657 && !device.isVirtual()
658 && device.isFullKeyboard()) {
659 final InputDeviceIdentifier identifier = device.getIdentifier();
660 final String keyboardLayoutDescriptor =
661 mIm.getCurrentKeyboardLayoutForInputDevice(identifier);
662 final KeyboardLayout keyboardLayout = keyboardLayoutDescriptor != null ?
663 mIm.getKeyboardLayout(keyboardLayoutDescriptor) : null;
665 final PreferenceScreen pref = new PreferenceScreen(getActivity(), null);
666 pref.setTitle(device.getName());
667 if (keyboardLayout != null) {
668 pref.setSummary(keyboardLayout.toString());
670 pref.setSummary(R.string.keyboard_layout_default_label);
672 pref.setOnPreferenceClickListener(new Preference.OnPreferenceClickListener() {
674 public boolean onPreferenceClick(Preference preference) {
675 showKeyboardLayoutDialog(identifier);
679 mHardKeyboardPreferenceList.add(pref);
683 if (!mHardKeyboardPreferenceList.isEmpty()) {
684 for (int i = mHardKeyboardCategory.getPreferenceCount(); i-- > 0; ) {
685 final Preference pref = mHardKeyboardCategory.getPreference(i);
686 if (pref.getOrder() < 1000) {
687 mHardKeyboardCategory.removePreference(pref);
691 Collections.sort(mHardKeyboardPreferenceList);
692 final int count = mHardKeyboardPreferenceList.size();
693 for (int i = 0; i < count; i++) {
694 final Preference pref = mHardKeyboardPreferenceList.get(i);
696 mHardKeyboardCategory.addPreference(pref);
699 getPreferenceScreen().addPreference(mHardKeyboardCategory);
701 getPreferenceScreen().removePreference(mHardKeyboardCategory);
705 private void showKeyboardLayoutDialog(InputDeviceIdentifier inputDeviceIdentifier) {
706 KeyboardLayoutDialogFragment fragment = new KeyboardLayoutDialogFragment(
707 inputDeviceIdentifier);
708 fragment.setTargetFragment(this, 0);
709 fragment.show(getActivity().getFragmentManager(), "keyboardLayout");
713 public void onSetupKeyboardLayouts(InputDeviceIdentifier inputDeviceIdentifier) {
714 final Intent intent = new Intent(Intent.ACTION_MAIN);
715 intent.setClass(getActivity(), KeyboardLayoutPickerActivity.class);
716 intent.putExtra(KeyboardLayoutPickerFragment.EXTRA_INPUT_DEVICE_IDENTIFIER,
717 inputDeviceIdentifier);
718 mIntentWaitingForResult = intent;
719 startActivityForResult(intent, 0);
723 public void onActivityResult(int requestCode, int resultCode, Intent data) {
724 super.onActivityResult(requestCode, resultCode, data);
726 if (mIntentWaitingForResult != null) {
727 InputDeviceIdentifier inputDeviceIdentifier = mIntentWaitingForResult
728 .getParcelableExtra(KeyboardLayoutPickerFragment.EXTRA_INPUT_DEVICE_IDENTIFIER);
729 mIntentWaitingForResult = null;
730 showKeyboardLayoutDialog(inputDeviceIdentifier);
734 private void updateGameControllers() {
735 if (haveInputDeviceWithVibrator()) {
736 getPreferenceScreen().addPreference(mGameControllerCategory);
738 SwitchPreference pref = (SwitchPreference)
739 mGameControllerCategory.findPreference("vibrate_input_devices");
740 pref.setChecked(System.getInt(getContentResolver(),
741 Settings.System.VIBRATE_INPUT_DEVICES, 1) > 0);
743 getPreferenceScreen().removePreference(mGameControllerCategory);
747 private static boolean haveInputDeviceWithVibrator() {
748 final int[] devices = InputDevice.getDeviceIds();
749 for (int i = 0; i < devices.length; i++) {
750 InputDevice device = InputDevice.getDevice(devices[i]);
751 if (device != null && !device.isVirtual() && device.getVibrator().hasVibrator()) {
758 private class SettingsObserver extends ContentObserver {
759 private Context mContext;
761 public SettingsObserver(Handler handler, Context context) {
766 @Override public void onChange(boolean selfChange) {
767 updateCurrentImeName();
770 public void resume() {
771 final ContentResolver cr = mContext.getContentResolver();
772 cr.registerContentObserver(
773 Settings.Secure.getUriFor(Settings.Secure.DEFAULT_INPUT_METHOD), false, this);
774 cr.registerContentObserver(Settings.Secure.getUriFor(
775 Settings.Secure.SELECTED_INPUT_METHOD_SUBTYPE), false, this);
778 public void pause() {
779 mContext.getContentResolver().unregisterContentObserver(this);
783 public static void restore(Context context) {
784 final SharedPreferences prefs = PreferenceManager.getDefaultSharedPreferences(context);
785 final CMHardwareManager hardware = CMHardwareManager.getInstance(context);
786 if (hardware.isSupported(CMHardwareManager.FEATURE_HIGH_TOUCH_SENSITIVITY)) {
787 final boolean enabled = prefs.getBoolean(KEY_HIGH_TOUCH_SENSITIVITY,
788 hardware.get(CMHardwareManager.FEATURE_HIGH_TOUCH_SENSITIVITY));
789 if (!hardware.set(CMHardwareManager.FEATURE_HIGH_TOUCH_SENSITIVITY,
791 Log.e(TAG, "Failed to restore high touch sensitivity settings.");
793 Log.d(TAG, "High touch sensitivity settings restored.");
796 if (hardware.isSupported(CMHardwareManager.FEATURE_TOUCH_HOVERING)) {
797 final boolean enabled = prefs.getBoolean(KEY_TOUCHSCREEN_HOVERING,
798 hardware.get(CMHardwareManager.FEATURE_TOUCH_HOVERING));
799 if (!hardware.set(CMHardwareManager.FEATURE_TOUCH_HOVERING, enabled)) {
800 Log.e(TAG, "Failed to restore touch hovering settings.");
802 Log.d(TAG, "Touch hovering settings restored.");
807 public static final Indexable.SearchIndexProvider SEARCH_INDEX_DATA_PROVIDER =
808 new BaseSearchIndexProvider() {
810 public List<SearchIndexableRaw> getRawDataToIndex(Context context, boolean enabled) {
811 List<SearchIndexableRaw> indexables = new ArrayList<>();
813 final String screenTitle = context.getString(R.string.language_keyboard_settings_title);
816 if (context.getAssets().getLocales().length > 1) {
817 String localeName = getLocaleName(context);
818 SearchIndexableRaw indexable = new SearchIndexableRaw(context);
819 indexable.key = KEY_PHONE_LANGUAGE;
820 indexable.title = context.getString(R.string.phone_language);
821 indexable.summaryOn = localeName;
822 indexable.summaryOff = localeName;
823 indexable.screenTitle = screenTitle;
824 indexables.add(indexable);
828 SearchIndexableRaw indexable = new SearchIndexableRaw(context);
829 indexable.key = KEY_SPELL_CHECKERS;
830 indexable.title = context.getString(R.string.spellcheckers_settings_title);
831 indexable.screenTitle = screenTitle;
832 indexable.keywords = context.getString(R.string.keywords_spell_checker);
833 indexables.add(indexable);
836 if (UserDictionaryList.getUserDictionaryLocalesSet(context) != null) {
837 indexable = new SearchIndexableRaw(context);
838 indexable.key = "user_dict_settings";
839 indexable.title = context.getString(R.string.user_dict_settings_title);
840 indexable.screenTitle = screenTitle;
841 indexables.add(indexable);
844 // Keyboard settings.
845 indexable = new SearchIndexableRaw(context);
846 indexable.key = "keyboard_settings";
847 indexable.title = context.getString(R.string.keyboard_settings_category);
848 indexable.screenTitle = screenTitle;
849 indexable.keywords = context.getString(R.string.keywords_keyboard_and_ime);
850 indexables.add(indexable);
852 InputMethodSettingValuesWrapper immValues = InputMethodSettingValuesWrapper
853 .getInstance(context);
854 immValues.refreshAllInputMethodAndSubtypes();
857 String currImeName = immValues.getCurrentInputMethodName(context).toString();
858 indexable = new SearchIndexableRaw(context);
859 indexable.key = KEY_CURRENT_INPUT_METHOD;
860 indexable.title = context.getString(R.string.current_input_method);
861 indexable.summaryOn = currImeName;
862 indexable.summaryOff = currImeName;
863 indexable.screenTitle = screenTitle;
864 indexables.add(indexable);
866 InputMethodManager inputMethodManager = (InputMethodManager) context.getSystemService(
867 Context.INPUT_METHOD_SERVICE);
870 List<InputMethodInfo> inputMethods = immValues.getInputMethodList();
871 final int inputMethodCount = (inputMethods == null ? 0 : inputMethods.size());
872 for (int i = 0; i < inputMethodCount; ++i) {
873 InputMethodInfo inputMethod = inputMethods.get(i);
875 StringBuilder builder = new StringBuilder();
876 List<InputMethodSubtype> subtypes = inputMethodManager
877 .getEnabledInputMethodSubtypeList(inputMethod, true);
878 final int subtypeCount = subtypes.size();
879 for (int j = 0; j < subtypeCount; j++) {
880 InputMethodSubtype subtype = subtypes.get(j);
881 if (builder.length() > 0) {
884 CharSequence subtypeLabel = subtype.getDisplayName(context,
885 inputMethod.getPackageName(), inputMethod.getServiceInfo()
887 builder.append(subtypeLabel);
889 String summary = builder.toString();
891 ServiceInfo serviceInfo = inputMethod.getServiceInfo();
892 ComponentName componentName = new ComponentName(serviceInfo.packageName,
895 indexable = new SearchIndexableRaw(context);
896 indexable.key = componentName.flattenToString();
897 indexable.title = inputMethod.loadLabel(context.getPackageManager()).toString();
898 indexable.summaryOn = summary;
899 indexable.summaryOff = summary;
900 indexable.screenTitle = screenTitle;
901 indexables.add(indexable);
905 InputManager inputManager = (InputManager) context.getSystemService(
906 Context.INPUT_SERVICE);
907 boolean hasHardKeyboards = false;
909 final int[] devices = InputDevice.getDeviceIds();
910 for (int i = 0; i < devices.length; i++) {
911 InputDevice device = InputDevice.getDevice(devices[i]);
912 if (device == null || device.isVirtual() || !device.isFullKeyboard()) {
916 hasHardKeyboards = true;
918 InputDeviceIdentifier identifier = device.getIdentifier();
919 String keyboardLayoutDescriptor =
920 inputManager.getCurrentKeyboardLayoutForInputDevice(identifier);
921 KeyboardLayout keyboardLayout = keyboardLayoutDescriptor != null ?
922 inputManager.getKeyboardLayout(keyboardLayoutDescriptor) : null;
925 if (keyboardLayout != null) {
926 summary = keyboardLayout.toString();
928 summary = context.getString(R.string.keyboard_layout_default_label);
931 indexable = new SearchIndexableRaw(context);
932 indexable.key = device.getName();
933 indexable.title = device.getName();
934 indexable.summaryOn = summary;
935 indexable.summaryOff = summary;
936 indexable.screenTitle = screenTitle;
937 indexables.add(indexable);
940 if (hasHardKeyboards) {
941 // Hard keyboard category.
942 indexable = new SearchIndexableRaw(context);
943 indexable.key = "builtin_keyboard_settings";
944 indexable.title = context.getString(
945 R.string.builtin_keyboard_settings_title);
946 indexable.screenTitle = screenTitle;
947 indexables.add(indexable);
950 indexable = new SearchIndexableRaw(context);
951 indexable.key = "auto_replace";
952 indexable.title = context.getString(R.string.auto_replace);
953 indexable.summaryOn = context.getString(R.string.auto_replace_summary);
954 indexable.summaryOff = context.getString(R.string.auto_replace_summary);
955 indexable.screenTitle = screenTitle;
956 indexables.add(indexable);
959 indexable = new SearchIndexableRaw(context);
960 indexable.key = "auto_caps";
961 indexable.title = context.getString(R.string.auto_caps);
962 indexable.summaryOn = context.getString(R.string.auto_caps_summary);
963 indexable.summaryOff = context.getString(R.string.auto_caps_summary);
964 indexable.screenTitle = screenTitle;
965 indexables.add(indexable);
968 indexable = new SearchIndexableRaw(context);
969 indexable.key = "auto_punctuate";
970 indexable.title = context.getString(R.string.auto_punctuate);
971 indexable.summaryOn = context.getString(R.string.auto_punctuate_summary);
972 indexable.summaryOff = context.getString(R.string.auto_punctuate_summary);
973 indexable.screenTitle = screenTitle;
974 indexables.add(indexable);
978 TtsEngines ttsEngines = new TtsEngines(context);
979 if (!ttsEngines.getEngines().isEmpty()) {
980 indexable = new SearchIndexableRaw(context);
981 indexable.key = "tts_settings";
982 indexable.title = context.getString(R.string.tts_settings_title);
983 indexable.screenTitle = screenTitle;
984 indexable.keywords = context.getString(R.string.keywords_text_to_speech_output);
985 indexables.add(indexable);
989 indexable = new SearchIndexableRaw(context);
990 indexable.key = "pointer_settings_category";
991 indexable.title = context.getString(R.string.pointer_settings_category);
992 indexable.screenTitle = screenTitle;
993 indexables.add(indexable);
995 indexable = new SearchIndexableRaw(context);
996 indexable.key = "pointer_speed";
997 indexable.title = context.getString(R.string.pointer_speed);
998 indexable.screenTitle = screenTitle;
999 indexables.add(indexable);
1001 // Game controllers.
1002 if (haveInputDeviceWithVibrator()) {
1003 indexable = new SearchIndexableRaw(context);
1004 indexable.key = "vibrate_input_devices";
1005 indexable.title = context.getString(R.string.vibrate_input_devices);
1006 indexable.summaryOn = context.getString(R.string.vibrate_input_devices_summary);
1007 indexable.summaryOff = context.getString(R.string.vibrate_input_devices_summary);
1008 indexable.screenTitle = screenTitle;
1009 indexables.add(indexable);