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_WAKEUP = "voice_wakeup";
102 private static final String VOICE_WAKEUP_PACKAGE = "com.cyanogenmod.voicewakeup";
104 // false: on ICS or later
105 private static final boolean SHOW_INPUT_METHOD_SWITCHER_SETTINGS = false;
107 private static final String[] sSystemSettingNames = {
108 System.TEXT_AUTO_REPLACE, System.TEXT_AUTO_CAPS, System.TEXT_AUTO_PUNCTUATE,
111 private static final String[] sHardKeyboardKeys = {
112 "auto_replace", "auto_caps", "auto_punctuate",
115 private int mDefaultInputMethodSelectorVisibility = 0;
116 private ListPreference mShowInputMethodSelectorPref;
117 private SwitchPreference mStylusIconEnabled;
118 private SwitchPreference mHighTouchSensitivity;
119 private SwitchPreference mTouchscreenHovering;
120 private PreferenceCategory mKeyboardSettingsCategory;
121 private PreferenceCategory mHardKeyboardCategory;
122 private PreferenceCategory mGameControllerCategory;
123 private Preference mLanguagePref;
124 private PreferenceScreen mStylusGestures;
125 private final ArrayList<InputMethodPreference> mInputMethodPreferenceList = new ArrayList<>();
126 private final ArrayList<PreferenceScreen> mHardKeyboardPreferenceList = new ArrayList<>();
127 private InputManager mIm;
128 private InputMethodManager mImm;
129 private boolean mShowsOnlyFullImeAndKeyboardList;
130 private Handler mHandler;
131 private SettingsObserver mSettingsObserver;
132 private Intent mIntentWaitingForResult;
133 private InputMethodSettingValuesWrapper mInputMethodSettingValues;
134 private DevicePolicyManager mDpm;
135 private CMHardwareManager mHardware;
138 protected int getMetricsCategory() {
139 return MetricsLogger.INPUTMETHOD_LANGUAGE;
143 public void onCreate(Bundle icicle) {
144 super.onCreate(icicle);
146 addPreferencesFromResource(R.xml.language_settings);
148 final Activity activity = getActivity();
149 mImm = (InputMethodManager) getSystemService(Context.INPUT_METHOD_SERVICE);
150 mInputMethodSettingValues = InputMethodSettingValuesWrapper.getInstance(activity);
152 mHardware = CMHardwareManager.getInstance(activity);
155 mDefaultInputMethodSelectorVisibility = Integer.valueOf(
156 getString(R.string.input_method_selector_visibility_default_value));
157 } catch (NumberFormatException e) {
160 if (activity.getAssets().getLocales().length == 1) {
161 // No "Select language" pref if there's only one system locale available.
162 getPreferenceScreen().removePreference(findPreference(KEY_PHONE_LANGUAGE));
164 mLanguagePref = findPreference(KEY_PHONE_LANGUAGE);
166 if (SHOW_INPUT_METHOD_SWITCHER_SETTINGS) {
167 mShowInputMethodSelectorPref = (ListPreference)findPreference(
168 KEY_INPUT_METHOD_SELECTOR);
169 mShowInputMethodSelectorPref.setOnPreferenceChangeListener(this);
170 // TODO: Update current input method name on summary
171 updateInputMethodSelectorSummary(loadInputMethodSelectorVisibility());
174 new VoiceInputOutputSettings(this).onCreate();
176 // Get references to dynamically constructed categories.
177 mHardKeyboardCategory = (PreferenceCategory)findPreference("hard_keyboard");
178 mKeyboardSettingsCategory = (PreferenceCategory)findPreference(
179 "keyboard_settings_category");
180 mGameControllerCategory = (PreferenceCategory)findPreference(
181 "game_controller_settings_category");
183 final Intent startingIntent = activity.getIntent();
184 // Filter out irrelevant features if invoked from IME settings button.
185 mShowsOnlyFullImeAndKeyboardList = Settings.ACTION_INPUT_METHOD_SETTINGS.equals(
186 startingIntent.getAction());
187 if (mShowsOnlyFullImeAndKeyboardList) {
188 getPreferenceScreen().removeAll();
189 getPreferenceScreen().addPreference(mHardKeyboardCategory);
190 if (SHOW_INPUT_METHOD_SWITCHER_SETTINGS) {
191 getPreferenceScreen().addPreference(mShowInputMethodSelectorPref);
193 mKeyboardSettingsCategory.removeAll();
194 getPreferenceScreen().addPreference(mKeyboardSettingsCategory);
197 // Build hard keyboard and game controller preference categories.
198 mIm = (InputManager)activity.getSystemService(Context.INPUT_SERVICE);
199 updateInputDevices();
201 PreferenceCategory pointerSettingsCategory = (PreferenceCategory)
202 findPreference(KEY_POINTER_SETTINGS_CATEGORY);
204 mStylusGestures = (PreferenceScreen) findPreference(KEY_STYLUS_GESTURES);
205 mStylusIconEnabled = (SwitchPreference) findPreference(KEY_STYLUS_ICON_ENABLED);
206 mHighTouchSensitivity = (SwitchPreference) findPreference(KEY_HIGH_TOUCH_SENSITIVITY);
208 mTouchscreenHovering = (SwitchPreference) findPreference(KEY_TOUCHSCREEN_HOVERING);
210 if (pointerSettingsCategory != null) {
211 if (!getResources().getBoolean(com.android.internal.R.bool.config_stylusGestures)) {
212 pointerSettingsCategory.removePreference(mStylusGestures);
213 pointerSettingsCategory.removePreference(mStylusIconEnabled);
216 if (!mHardware.isSupported(
217 CMHardwareManager.FEATURE_HIGH_TOUCH_SENSITIVITY)) {
218 pointerSettingsCategory.removePreference(mHighTouchSensitivity);
219 mHighTouchSensitivity = null;
221 mHighTouchSensitivity.setChecked(
222 mHardware.get(CMHardwareManager.FEATURE_HIGH_TOUCH_SENSITIVITY));
225 if (!mHardware.isSupported(CMHardwareManager.FEATURE_TOUCH_HOVERING)) {
226 pointerSettingsCategory.removePreference(mTouchscreenHovering);
227 mTouchscreenHovering = null;
229 mTouchscreenHovering.setChecked(
230 mHardware.get(CMHardwareManager.FEATURE_TOUCH_HOVERING));
233 Utils.updatePreferenceToSpecificActivityFromMetaDataOrRemove(getActivity(),
234 pointerSettingsCategory, KEY_TRACKPAD_SETTINGS);
235 if (pointerSettingsCategory.getPreferenceCount() == 0) {
236 getPreferenceScreen().removePreference(pointerSettingsCategory);
240 // Enable or disable mStatusBarImeSwitcher based on boolean: config_show_cmIMESwitcher
241 boolean showCmImeSwitcher = getResources().getBoolean(
242 com.android.internal.R.bool.config_show_cmIMESwitcher);
243 if (!showCmImeSwitcher) {
244 Preference pref = findPreference(CMSettings.System.STATUS_BAR_IME_SWITCHER);
246 getPreferenceScreen().removePreference(pref);
251 final Preference spellChecker = findPreference(KEY_SPELL_CHECKERS);
252 if (spellChecker != null) {
253 // Note: KEY_SPELL_CHECKERS preference is marked as persistent="false" in XML.
254 InputMethodAndSubtypeUtil.removeUnnecessaryNonPersistentPreference(spellChecker);
255 final Intent intent = new Intent(Intent.ACTION_MAIN);
256 intent.setClass(activity, SubSettings.class);
257 intent.putExtra(SettingsActivity.EXTRA_SHOW_FRAGMENT,
258 SpellCheckersSettings.class.getName());
259 intent.putExtra(SettingsActivity.EXTRA_SHOW_FRAGMENT_TITLE_RESID,
260 R.string.spellcheckers_settings_title);
261 spellChecker.setIntent(intent);
264 mHandler = new Handler();
265 mSettingsObserver = new SettingsObserver(mHandler, activity);
266 mDpm = (DevicePolicyManager) (getActivity().
267 getSystemService(Context.DEVICE_POLICY_SERVICE));
269 // If we've launched from the keyboard layout notification, go ahead and just show the
270 // keyboard layout dialog.
271 final InputDeviceIdentifier identifier =
272 startingIntent.getParcelableExtra(Settings.EXTRA_INPUT_DEVICE_IDENTIFIER);
273 if (mShowsOnlyFullImeAndKeyboardList && identifier != null) {
274 showKeyboardLayoutDialog(identifier);
277 if (!Utils.isPackageInstalled(getActivity(), KEY_VOICE_WAKEUP, false)) {
278 Preference wakeup = findPreference(KEY_VOICE_WAKEUP);
279 if (wakeup != null) {
280 getPreferenceScreen().removePreference(wakeup);
286 private void updateInputMethodSelectorSummary(int value) {
287 String[] inputMethodSelectorTitles = getResources().getStringArray(
288 R.array.input_method_selector_titles);
289 if (inputMethodSelectorTitles.length > value) {
290 mShowInputMethodSelectorPref.setSummary(inputMethodSelectorTitles[value]);
291 mShowInputMethodSelectorPref.setValue(String.valueOf(value));
295 private void updateUserDictionaryPreference(Preference userDictionaryPreference) {
296 final Activity activity = getActivity();
297 final TreeSet<String> localeSet = UserDictionaryList.getUserDictionaryLocalesSet(activity);
298 if (null == localeSet) {
299 // The locale list is null if and only if the user dictionary service is
300 // not present or disabled. In this case we need to remove the preference.
301 getPreferenceScreen().removePreference(userDictionaryPreference);
303 userDictionaryPreference.setOnPreferenceClickListener(
304 new OnPreferenceClickListener() {
306 public boolean onPreferenceClick(Preference arg0) {
307 // Redirect to UserDictionarySettings if the user needs only one
309 final Bundle extras = new Bundle();
310 final Class<? extends Fragment> targetFragment;
311 if (localeSet.size() <= 1) {
312 if (!localeSet.isEmpty()) {
313 // If the size of localeList is 0, we don't set the locale
314 // parameter in the extras. This will be interpreted by the
315 // UserDictionarySettings class as meaning
316 // "the current locale". Note that with the current code for
317 // UserDictionaryList#getUserDictionaryLocalesSet()
318 // the locale list always has at least one element, since it
319 // always includes the current locale explicitly.
320 // @see UserDictionaryList.getUserDictionaryLocalesSet().
321 extras.putString("locale", localeSet.first());
323 targetFragment = UserDictionarySettings.class;
325 targetFragment = UserDictionaryList.class;
327 startFragment(InputMethodAndLanguageSettings.this,
328 targetFragment.getCanonicalName(), -1, -1, extras);
336 public void onResume() {
339 mSettingsObserver.resume();
340 mIm.registerInputDeviceListener(this, null);
342 final Preference spellChecker = findPreference(KEY_SPELL_CHECKERS);
343 if (spellChecker != null) {
344 final TextServicesManager tsm = (TextServicesManager) getSystemService(
345 Context.TEXT_SERVICES_MANAGER_SERVICE);
346 final SpellCheckerInfo sci = tsm.getCurrentSpellChecker();
347 spellChecker.setEnabled(sci != null);
348 if (tsm.isSpellCheckerEnabled() && sci != null) {
349 spellChecker.setSummary(sci.loadLabel(getPackageManager()));
351 spellChecker.setSummary(R.string.switch_off_text);
355 if (mStylusIconEnabled != null) {
356 mStylusIconEnabled.setChecked(Settings.System.getInt(getActivity().getContentResolver(),
357 Settings.System.STYLUS_ICON_ENABLED, 0) == 1);
360 if (!mShowsOnlyFullImeAndKeyboardList) {
361 if (mLanguagePref != null) {
362 String localeName = getLocaleName(getActivity());
363 mLanguagePref.setSummary(localeName);
366 updateUserDictionaryPreference(findPreference(KEY_USER_DICTIONARY_SETTINGS));
367 if (SHOW_INPUT_METHOD_SWITCHER_SETTINGS) {
368 mShowInputMethodSelectorPref.setOnPreferenceChangeListener(this);
373 if (!mHardKeyboardPreferenceList.isEmpty()) {
374 for (int i = 0; i < sHardKeyboardKeys.length; ++i) {
375 SwitchPreference swPref = (SwitchPreference)
376 mHardKeyboardCategory.findPreference(sHardKeyboardKeys[i]);
378 System.getInt(getContentResolver(), sSystemSettingNames[i], 1) > 0);
382 updateInputDevices();
384 // Refresh internal states in mInputMethodSettingValues to keep the latest
385 // "InputMethodInfo"s and "InputMethodSubtype"s
386 mInputMethodSettingValues.refreshAllInputMethodAndSubtypes();
387 updateInputMethodPreferenceViews();
391 public void onPause() {
394 mIm.unregisterInputDeviceListener(this);
395 mSettingsObserver.pause();
397 if (SHOW_INPUT_METHOD_SWITCHER_SETTINGS) {
398 mShowInputMethodSelectorPref.setOnPreferenceChangeListener(null);
400 // TODO: Consolidate the logic to InputMethodSettingsWrapper
401 InputMethodAndSubtypeUtil.saveInputMethodSubtypeList(
402 this, getContentResolver(), mInputMethodSettingValues.getInputMethodList(),
403 !mHardKeyboardPreferenceList.isEmpty());
407 public void onInputDeviceAdded(int deviceId) {
408 updateInputDevices();
412 public void onInputDeviceChanged(int deviceId) {
413 updateInputDevices();
417 public void onInputDeviceRemoved(int deviceId) {
418 updateInputDevices();
422 public boolean onPreferenceTreeClick(PreferenceScreen preferenceScreen, Preference preference) {
423 // Input Method stuff
424 if (Utils.isMonkeyRunning()) {
427 if (preference == mStylusIconEnabled) {
428 Settings.System.putInt(getActivity().getContentResolver(),
429 Settings.System.STYLUS_ICON_ENABLED, mStylusIconEnabled.isChecked() ? 1 : 0);
430 } else if (preference == mHighTouchSensitivity) {
431 boolean mHighTouchSensitivityEnable = mHighTouchSensitivity.isChecked();
432 CMSettings.System.putInt(getActivity().getContentResolver(),
433 CMSettings.System.HIGH_TOUCH_SENSITIVITY_ENABLE,
434 mHighTouchSensitivityEnable ? 1 : 0);
435 return mHardware.set(CMHardwareManager.FEATURE_HIGH_TOUCH_SENSITIVITY,
436 mHighTouchSensitivityEnable);
437 } else if (preference == mTouchscreenHovering) {
438 return mHardware.set(CMHardwareManager.FEATURE_TOUCH_HOVERING,
439 mTouchscreenHovering.isChecked());
440 } else if (preference instanceof PreferenceScreen) {
441 if (preference.getFragment() != null) {
442 // Fragment will be handled correctly by the super class.
443 } else if (KEY_CURRENT_INPUT_METHOD.equals(preference.getKey())) {
444 final InputMethodManager imm = (InputMethodManager)
445 getSystemService(Context.INPUT_METHOD_SERVICE);
446 imm.showInputMethodPicker(false /* showAuxiliarySubtypes */);
448 } else if (preference instanceof SwitchPreference) {
449 final SwitchPreference pref = (SwitchPreference) preference;
450 if (pref == mGameControllerCategory.findPreference("vibrate_input_devices")) {
451 System.putInt(getContentResolver(), Settings.System.VIBRATE_INPUT_DEVICES,
452 pref.isChecked() ? 1 : 0);
455 if (!mHardKeyboardPreferenceList.isEmpty()) {
456 for (int i = 0; i < sHardKeyboardKeys.length; ++i) {
457 if (pref == mHardKeyboardCategory.findPreference(sHardKeyboardKeys[i])) {
458 System.putInt(getContentResolver(), sSystemSettingNames[i],
459 pref.isChecked() ? 1 : 0);
465 return super.onPreferenceTreeClick(preferenceScreen, preference);
468 private static String getLocaleName(Context context) {
469 // We want to show the same string that the LocalePicker used.
470 // TODO: should this method be in LocalePicker instead?
471 Locale currentLocale = context.getResources().getConfiguration().locale;
472 List<LocalePicker.LocaleInfo> locales = LocalePicker.getAllAssetLocales(context, true);
473 for (LocalePicker.LocaleInfo locale : locales) {
474 if (locale.getLocale().equals(currentLocale)) {
475 return locale.getLabel();
478 // This can't happen as long as the locale was one set by Settings.
479 // Fall back in case a developer is testing an unsupported locale.
480 return currentLocale.getDisplayName(currentLocale);
483 private void saveInputMethodSelectorVisibility(String value) {
485 int intValue = Integer.valueOf(value);
486 Settings.Secure.putInt(getContentResolver(),
487 Settings.Secure.INPUT_METHOD_SELECTOR_VISIBILITY, intValue);
488 updateInputMethodSelectorSummary(intValue);
489 } catch(NumberFormatException e) {
493 private int loadInputMethodSelectorVisibility() {
494 return Settings.Secure.getInt(getContentResolver(),
495 Settings.Secure.INPUT_METHOD_SELECTOR_VISIBILITY,
496 mDefaultInputMethodSelectorVisibility);
500 public boolean onPreferenceChange(Preference preference, Object value) {
501 if (SHOW_INPUT_METHOD_SWITCHER_SETTINGS) {
502 if (preference == mShowInputMethodSelectorPref) {
503 if (value instanceof String) {
504 saveInputMethodSelectorVisibility((String)value);
511 private void updateInputMethodPreferenceViews() {
512 synchronized (mInputMethodPreferenceList) {
513 // Clear existing "InputMethodPreference"s
514 for (final InputMethodPreference pref : mInputMethodPreferenceList) {
515 mKeyboardSettingsCategory.removePreference(pref);
517 mInputMethodPreferenceList.clear();
518 List<String> permittedList = mDpm.getPermittedInputMethodsForCurrentUser();
519 final Context context = getActivity();
520 final List<InputMethodInfo> imis = mShowsOnlyFullImeAndKeyboardList
521 ? mInputMethodSettingValues.getInputMethodList()
522 : mImm.getEnabledInputMethodList();
523 final int N = (imis == null ? 0 : imis.size());
524 for (int i = 0; i < N; ++i) {
525 final InputMethodInfo imi = imis.get(i);
526 final boolean isAllowedByOrganization = permittedList == null
527 || permittedList.contains(imi.getPackageName());
528 final InputMethodPreference pref = new InputMethodPreference(
529 context, imi, mShowsOnlyFullImeAndKeyboardList /* hasSwitch */,
530 isAllowedByOrganization, this);
531 mInputMethodPreferenceList.add(pref);
533 final Collator collator = Collator.getInstance();
534 Collections.sort(mInputMethodPreferenceList, new Comparator<InputMethodPreference>() {
536 public int compare(InputMethodPreference lhs, InputMethodPreference rhs) {
537 return lhs.compareTo(rhs, collator);
540 for (int i = 0; i < N; ++i) {
541 final InputMethodPreference pref = mInputMethodPreferenceList.get(i);
542 mKeyboardSettingsCategory.addPreference(pref);
543 InputMethodAndSubtypeUtil.removeUnnecessaryNonPersistentPreference(pref);
544 pref.updatePreferenceViews();
547 updateCurrentImeName();
548 // TODO: Consolidate the logic with InputMethodSettingsWrapper
549 // CAVEAT: The preference class here does not know about the default value - that is
550 // managed by the Input Method Manager Service, so in this case it could save the wrong
551 // value. Hence we must update the checkboxes here.
552 InputMethodAndSubtypeUtil.loadInputMethodSubtypeList(
553 this, getContentResolver(),
554 mInputMethodSettingValues.getInputMethodList(), null);
558 public void onSaveInputMethodPreference(final InputMethodPreference pref) {
559 final InputMethodInfo imi = pref.getInputMethodInfo();
560 if (!pref.isChecked()) {
561 // An IME is being disabled. Save enabled subtypes of the IME to shared preference to be
562 // able to re-enable these subtypes when the IME gets re-enabled.
563 saveEnabledSubtypesOf(imi);
565 final boolean hasHardwareKeyboard = getResources().getConfiguration().keyboard
566 == Configuration.KEYBOARD_QWERTY;
567 InputMethodAndSubtypeUtil.saveInputMethodSubtypeList(this, getContentResolver(),
568 mImm.getInputMethodList(), hasHardwareKeyboard);
569 // Update input method settings and preference list.
570 mInputMethodSettingValues.refreshAllInputMethodAndSubtypes();
571 if (pref.isChecked()) {
572 // An IME is being enabled. Load the previously enabled subtypes from shared preference
573 // and enable these subtypes.
574 restorePreviouslyEnabledSubtypesOf(imi);
576 for (final InputMethodPreference p : mInputMethodPreferenceList) {
577 p.updatePreferenceViews();
581 private void saveEnabledSubtypesOf(final InputMethodInfo imi) {
582 final HashSet<String> enabledSubtypeIdSet = new HashSet<>();
583 final List<InputMethodSubtype> enabledSubtypes = mImm.getEnabledInputMethodSubtypeList(
584 imi, true /* allowsImplicitlySelectedSubtypes */);
585 for (final InputMethodSubtype subtype : enabledSubtypes) {
586 final String subtypeId = Integer.toString(subtype.hashCode());
587 enabledSubtypeIdSet.add(subtypeId);
589 final HashMap<String, HashSet<String>> imeToEnabledSubtypeIdsMap =
590 loadPreviouslyEnabledSubtypeIdsMap();
591 final String imiId = imi.getId();
592 imeToEnabledSubtypeIdsMap.put(imiId, enabledSubtypeIdSet);
593 savePreviouslyEnabledSubtypeIdsMap(imeToEnabledSubtypeIdsMap);
596 private void restorePreviouslyEnabledSubtypesOf(final InputMethodInfo imi) {
597 final HashMap<String, HashSet<String>> imeToEnabledSubtypeIdsMap =
598 loadPreviouslyEnabledSubtypeIdsMap();
599 final String imiId = imi.getId();
600 final HashSet<String> enabledSubtypeIdSet = imeToEnabledSubtypeIdsMap.remove(imiId);
601 if (enabledSubtypeIdSet == null) {
604 savePreviouslyEnabledSubtypeIdsMap(imeToEnabledSubtypeIdsMap);
605 InputMethodAndSubtypeUtil.enableInputMethodSubtypesOf(
606 getContentResolver(), imiId, enabledSubtypeIdSet);
609 private HashMap<String, HashSet<String>> loadPreviouslyEnabledSubtypeIdsMap() {
610 final Context context = getActivity();
611 final SharedPreferences prefs = PreferenceManager.getDefaultSharedPreferences(context);
612 final String imesAndSubtypesString = prefs.getString(KEY_PREVIOUSLY_ENABLED_SUBTYPES, null);
613 return InputMethodAndSubtypeUtil.parseInputMethodsAndSubtypesString(imesAndSubtypesString);
616 private void savePreviouslyEnabledSubtypeIdsMap(
617 final HashMap<String, HashSet<String>> subtypesMap) {
618 final Context context = getActivity();
619 final SharedPreferences prefs = PreferenceManager.getDefaultSharedPreferences(context);
620 final String imesAndSubtypesString = InputMethodAndSubtypeUtil
621 .buildInputMethodsAndSubtypesString(subtypesMap);
622 prefs.edit().putString(KEY_PREVIOUSLY_ENABLED_SUBTYPES, imesAndSubtypesString).apply();
625 private void updateCurrentImeName() {
626 final Context context = getActivity();
627 if (context == null || mImm == null) return;
628 final Preference curPref = getPreferenceScreen().findPreference(KEY_CURRENT_INPUT_METHOD);
629 if (curPref != null) {
630 final CharSequence curIme =
631 mInputMethodSettingValues.getCurrentInputMethodName(context);
632 if (!TextUtils.isEmpty(curIme)) {
633 synchronized (this) {
634 curPref.setSummary(curIme);
640 private void updateInputDevices() {
641 updateHardKeyboards();
642 updateGameControllers();
645 private void updateHardKeyboards() {
646 mHardKeyboardPreferenceList.clear();
647 final int[] devices = InputDevice.getDeviceIds();
648 for (int i = 0; i < devices.length; i++) {
649 InputDevice device = InputDevice.getDevice(devices[i]);
651 && !device.isVirtual()
652 && device.isFullKeyboard()) {
653 final InputDeviceIdentifier identifier = device.getIdentifier();
654 final String keyboardLayoutDescriptor =
655 mIm.getCurrentKeyboardLayoutForInputDevice(identifier);
656 final KeyboardLayout keyboardLayout = keyboardLayoutDescriptor != null ?
657 mIm.getKeyboardLayout(keyboardLayoutDescriptor) : null;
659 final PreferenceScreen pref = new PreferenceScreen(getActivity(), null);
660 pref.setTitle(device.getName());
661 if (keyboardLayout != null) {
662 pref.setSummary(keyboardLayout.toString());
664 pref.setSummary(R.string.keyboard_layout_default_label);
666 pref.setOnPreferenceClickListener(new Preference.OnPreferenceClickListener() {
668 public boolean onPreferenceClick(Preference preference) {
669 showKeyboardLayoutDialog(identifier);
673 mHardKeyboardPreferenceList.add(pref);
677 if (!mHardKeyboardPreferenceList.isEmpty()) {
678 for (int i = mHardKeyboardCategory.getPreferenceCount(); i-- > 0; ) {
679 final Preference pref = mHardKeyboardCategory.getPreference(i);
680 if (pref.getOrder() < 1000) {
681 mHardKeyboardCategory.removePreference(pref);
685 Collections.sort(mHardKeyboardPreferenceList);
686 final int count = mHardKeyboardPreferenceList.size();
687 for (int i = 0; i < count; i++) {
688 final Preference pref = mHardKeyboardPreferenceList.get(i);
690 mHardKeyboardCategory.addPreference(pref);
693 getPreferenceScreen().addPreference(mHardKeyboardCategory);
695 getPreferenceScreen().removePreference(mHardKeyboardCategory);
699 private void showKeyboardLayoutDialog(InputDeviceIdentifier inputDeviceIdentifier) {
700 KeyboardLayoutDialogFragment fragment = new KeyboardLayoutDialogFragment(
701 inputDeviceIdentifier);
702 fragment.setTargetFragment(this, 0);
703 fragment.show(getActivity().getFragmentManager(), "keyboardLayout");
707 public void onSetupKeyboardLayouts(InputDeviceIdentifier inputDeviceIdentifier) {
708 final Intent intent = new Intent(Intent.ACTION_MAIN);
709 intent.setClass(getActivity(), KeyboardLayoutPickerActivity.class);
710 intent.putExtra(KeyboardLayoutPickerFragment.EXTRA_INPUT_DEVICE_IDENTIFIER,
711 inputDeviceIdentifier);
712 mIntentWaitingForResult = intent;
713 startActivityForResult(intent, 0);
717 public void onActivityResult(int requestCode, int resultCode, Intent data) {
718 super.onActivityResult(requestCode, resultCode, data);
720 if (mIntentWaitingForResult != null) {
721 InputDeviceIdentifier inputDeviceIdentifier = mIntentWaitingForResult
722 .getParcelableExtra(KeyboardLayoutPickerFragment.EXTRA_INPUT_DEVICE_IDENTIFIER);
723 mIntentWaitingForResult = null;
724 showKeyboardLayoutDialog(inputDeviceIdentifier);
728 private void updateGameControllers() {
729 if (haveInputDeviceWithVibrator()) {
730 getPreferenceScreen().addPreference(mGameControllerCategory);
732 SwitchPreference pref = (SwitchPreference)
733 mGameControllerCategory.findPreference("vibrate_input_devices");
734 pref.setChecked(System.getInt(getContentResolver(),
735 Settings.System.VIBRATE_INPUT_DEVICES, 1) > 0);
737 getPreferenceScreen().removePreference(mGameControllerCategory);
741 private static boolean haveInputDeviceWithVibrator() {
742 final int[] devices = InputDevice.getDeviceIds();
743 for (int i = 0; i < devices.length; i++) {
744 InputDevice device = InputDevice.getDevice(devices[i]);
745 if (device != null && !device.isVirtual() && device.getVibrator().hasVibrator()) {
752 private class SettingsObserver extends ContentObserver {
753 private Context mContext;
755 public SettingsObserver(Handler handler, Context context) {
760 @Override public void onChange(boolean selfChange) {
761 updateCurrentImeName();
764 public void resume() {
765 final ContentResolver cr = mContext.getContentResolver();
766 cr.registerContentObserver(
767 Settings.Secure.getUriFor(Settings.Secure.DEFAULT_INPUT_METHOD), false, this);
768 cr.registerContentObserver(Settings.Secure.getUriFor(
769 Settings.Secure.SELECTED_INPUT_METHOD_SUBTYPE), false, this);
772 public void pause() {
773 mContext.getContentResolver().unregisterContentObserver(this);
777 public static void restore(Context context) {
778 final SharedPreferences prefs = PreferenceManager.getDefaultSharedPreferences(context);
779 final CMHardwareManager hardware = CMHardwareManager.getInstance(context);
780 if (hardware.isSupported(CMHardwareManager.FEATURE_HIGH_TOUCH_SENSITIVITY)) {
781 final boolean enabled = prefs.getBoolean(KEY_HIGH_TOUCH_SENSITIVITY,
782 hardware.get(CMHardwareManager.FEATURE_HIGH_TOUCH_SENSITIVITY));
783 if (!hardware.set(CMHardwareManager.FEATURE_HIGH_TOUCH_SENSITIVITY,
785 Log.e(TAG, "Failed to restore high touch sensitivity settings.");
787 Log.d(TAG, "High touch sensitivity settings restored.");
790 if (hardware.isSupported(CMHardwareManager.FEATURE_TOUCH_HOVERING)) {
791 final boolean enabled = prefs.getBoolean(KEY_TOUCHSCREEN_HOVERING,
792 hardware.get(CMHardwareManager.FEATURE_TOUCH_HOVERING));
793 if (!hardware.set(CMHardwareManager.FEATURE_TOUCH_HOVERING, enabled)) {
794 Log.e(TAG, "Failed to restore touch hovering settings.");
796 Log.d(TAG, "Touch hovering settings restored.");
801 public static final Indexable.SearchIndexProvider SEARCH_INDEX_DATA_PROVIDER =
802 new BaseSearchIndexProvider() {
804 public List<SearchIndexableRaw> getRawDataToIndex(Context context, boolean enabled) {
805 List<SearchIndexableRaw> indexables = new ArrayList<>();
807 final String screenTitle = context.getString(R.string.language_keyboard_settings_title);
810 if (context.getAssets().getLocales().length > 1) {
811 String localeName = getLocaleName(context);
812 SearchIndexableRaw indexable = new SearchIndexableRaw(context);
813 indexable.key = KEY_PHONE_LANGUAGE;
814 indexable.title = context.getString(R.string.phone_language);
815 indexable.summaryOn = localeName;
816 indexable.summaryOff = localeName;
817 indexable.screenTitle = screenTitle;
818 indexables.add(indexable);
822 SearchIndexableRaw indexable = new SearchIndexableRaw(context);
823 indexable.key = KEY_SPELL_CHECKERS;
824 indexable.title = context.getString(R.string.spellcheckers_settings_title);
825 indexable.screenTitle = screenTitle;
826 indexable.keywords = context.getString(R.string.keywords_spell_checker);
827 indexables.add(indexable);
830 if (UserDictionaryList.getUserDictionaryLocalesSet(context) != null) {
831 indexable = new SearchIndexableRaw(context);
832 indexable.key = "user_dict_settings";
833 indexable.title = context.getString(R.string.user_dict_settings_title);
834 indexable.screenTitle = screenTitle;
835 indexables.add(indexable);
838 // Keyboard settings.
839 indexable = new SearchIndexableRaw(context);
840 indexable.key = "keyboard_settings";
841 indexable.title = context.getString(R.string.keyboard_settings_category);
842 indexable.screenTitle = screenTitle;
843 indexable.keywords = context.getString(R.string.keywords_keyboard_and_ime);
844 indexables.add(indexable);
846 InputMethodSettingValuesWrapper immValues = InputMethodSettingValuesWrapper
847 .getInstance(context);
848 immValues.refreshAllInputMethodAndSubtypes();
851 String currImeName = immValues.getCurrentInputMethodName(context).toString();
852 indexable = new SearchIndexableRaw(context);
853 indexable.key = KEY_CURRENT_INPUT_METHOD;
854 indexable.title = context.getString(R.string.current_input_method);
855 indexable.summaryOn = currImeName;
856 indexable.summaryOff = currImeName;
857 indexable.screenTitle = screenTitle;
858 indexables.add(indexable);
860 InputMethodManager inputMethodManager = (InputMethodManager) context.getSystemService(
861 Context.INPUT_METHOD_SERVICE);
864 List<InputMethodInfo> inputMethods = immValues.getInputMethodList();
865 final int inputMethodCount = (inputMethods == null ? 0 : inputMethods.size());
866 for (int i = 0; i < inputMethodCount; ++i) {
867 InputMethodInfo inputMethod = inputMethods.get(i);
869 StringBuilder builder = new StringBuilder();
870 List<InputMethodSubtype> subtypes = inputMethodManager
871 .getEnabledInputMethodSubtypeList(inputMethod, true);
872 final int subtypeCount = subtypes.size();
873 for (int j = 0; j < subtypeCount; j++) {
874 InputMethodSubtype subtype = subtypes.get(j);
875 if (builder.length() > 0) {
878 CharSequence subtypeLabel = subtype.getDisplayName(context,
879 inputMethod.getPackageName(), inputMethod.getServiceInfo()
881 builder.append(subtypeLabel);
883 String summary = builder.toString();
885 ServiceInfo serviceInfo = inputMethod.getServiceInfo();
886 ComponentName componentName = new ComponentName(serviceInfo.packageName,
889 indexable = new SearchIndexableRaw(context);
890 indexable.key = componentName.flattenToString();
891 indexable.title = inputMethod.loadLabel(context.getPackageManager()).toString();
892 indexable.summaryOn = summary;
893 indexable.summaryOff = summary;
894 indexable.screenTitle = screenTitle;
895 indexables.add(indexable);
899 InputManager inputManager = (InputManager) context.getSystemService(
900 Context.INPUT_SERVICE);
901 boolean hasHardKeyboards = false;
903 final int[] devices = InputDevice.getDeviceIds();
904 for (int i = 0; i < devices.length; i++) {
905 InputDevice device = InputDevice.getDevice(devices[i]);
906 if (device == null || device.isVirtual() || !device.isFullKeyboard()) {
910 hasHardKeyboards = true;
912 InputDeviceIdentifier identifier = device.getIdentifier();
913 String keyboardLayoutDescriptor =
914 inputManager.getCurrentKeyboardLayoutForInputDevice(identifier);
915 KeyboardLayout keyboardLayout = keyboardLayoutDescriptor != null ?
916 inputManager.getKeyboardLayout(keyboardLayoutDescriptor) : null;
919 if (keyboardLayout != null) {
920 summary = keyboardLayout.toString();
922 summary = context.getString(R.string.keyboard_layout_default_label);
925 indexable = new SearchIndexableRaw(context);
926 indexable.key = device.getName();
927 indexable.title = device.getName();
928 indexable.summaryOn = summary;
929 indexable.summaryOff = summary;
930 indexable.screenTitle = screenTitle;
931 indexables.add(indexable);
934 if (hasHardKeyboards) {
935 // Hard keyboard category.
936 indexable = new SearchIndexableRaw(context);
937 indexable.key = "builtin_keyboard_settings";
938 indexable.title = context.getString(
939 R.string.builtin_keyboard_settings_title);
940 indexable.screenTitle = screenTitle;
941 indexables.add(indexable);
944 indexable = new SearchIndexableRaw(context);
945 indexable.key = "auto_replace";
946 indexable.title = context.getString(R.string.auto_replace);
947 indexable.summaryOn = context.getString(R.string.auto_replace_summary);
948 indexable.summaryOff = context.getString(R.string.auto_replace_summary);
949 indexable.screenTitle = screenTitle;
950 indexables.add(indexable);
953 indexable = new SearchIndexableRaw(context);
954 indexable.key = "auto_caps";
955 indexable.title = context.getString(R.string.auto_caps);
956 indexable.summaryOn = context.getString(R.string.auto_caps_summary);
957 indexable.summaryOff = context.getString(R.string.auto_caps_summary);
958 indexable.screenTitle = screenTitle;
959 indexables.add(indexable);
962 indexable = new SearchIndexableRaw(context);
963 indexable.key = "auto_punctuate";
964 indexable.title = context.getString(R.string.auto_punctuate);
965 indexable.summaryOn = context.getString(R.string.auto_punctuate_summary);
966 indexable.summaryOff = context.getString(R.string.auto_punctuate_summary);
967 indexable.screenTitle = screenTitle;
968 indexables.add(indexable);
972 TtsEngines ttsEngines = new TtsEngines(context);
973 if (!ttsEngines.getEngines().isEmpty()) {
974 indexable = new SearchIndexableRaw(context);
975 indexable.key = "tts_settings";
976 indexable.title = context.getString(R.string.tts_settings_title);
977 indexable.screenTitle = screenTitle;
978 indexable.keywords = context.getString(R.string.keywords_text_to_speech_output);
979 indexables.add(indexable);
983 indexable = new SearchIndexableRaw(context);
984 indexable.key = "pointer_settings_category";
985 indexable.title = context.getString(R.string.pointer_settings_category);
986 indexable.screenTitle = screenTitle;
987 indexables.add(indexable);
989 indexable = new SearchIndexableRaw(context);
990 indexable.key = "pointer_speed";
991 indexable.title = context.getString(R.string.pointer_speed);
992 indexable.screenTitle = screenTitle;
993 indexables.add(indexable);
996 if (haveInputDeviceWithVibrator()) {
997 indexable = new SearchIndexableRaw(context);
998 indexable.key = "vibrate_input_devices";
999 indexable.title = context.getString(R.string.vibrate_input_devices);
1000 indexable.summaryOn = context.getString(R.string.vibrate_input_devices_summary);
1001 indexable.summaryOff = context.getString(R.string.vibrate_input_devices_summary);
1002 indexable.screenTitle = screenTitle;
1003 indexables.add(indexable);