/* * Copyright (C) 2008 The Android Open Source Project * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package com.android.settings.inputmethod; import android.app.Activity; import android.app.Fragment; import android.app.admin.DevicePolicyManager; import android.content.ComponentName; import android.content.ContentResolver; import android.content.Context; import android.content.Intent; import android.content.SharedPreferences; import android.content.pm.ServiceInfo; import android.content.res.Configuration; import android.database.ContentObserver; import android.hardware.input.InputDeviceIdentifier; import android.hardware.input.InputManager; import android.hardware.input.KeyboardLayout; import android.os.Bundle; import android.os.Handler; import android.preference.ListPreference; import android.preference.Preference; import android.preference.Preference.OnPreferenceClickListener; import android.preference.PreferenceCategory; import android.preference.PreferenceManager; import android.preference.PreferenceScreen; import android.preference.SwitchPreference; import android.provider.Settings; import android.provider.Settings.System; import android.speech.tts.TtsEngines; import android.text.TextUtils; import android.util.Log; import android.view.InputDevice; import android.view.inputmethod.InputMethodInfo; import android.view.inputmethod.InputMethodManager; import android.view.inputmethod.InputMethodSubtype; import android.view.textservice.SpellCheckerInfo; import android.view.textservice.TextServicesManager; import com.android.internal.app.LocalePicker; import com.android.internal.logging.MetricsLogger; import com.android.settings.R; import com.android.settings.Settings.KeyboardLayoutPickerActivity; import com.android.settings.SettingsActivity; import com.android.settings.SettingsPreferenceFragment; import com.android.settings.SubSettings; import com.android.settings.UserDictionarySettings; import com.android.settings.Utils; import com.android.settings.VoiceInputOutputSettings; import com.android.settings.search.BaseSearchIndexProvider; import com.android.settings.search.Indexable; import com.android.settings.search.SearchIndexableRaw; import com.android.settings.voicewakeup.VoiceWakeupSettings; import cyanogenmod.hardware.CMHardwareManager; import cyanogenmod.providers.CMSettings; import java.text.Collator; import java.util.ArrayList; import java.util.Collections; import java.util.Comparator; import java.util.HashMap; import java.util.HashSet; import java.util.List; import java.util.Locale; import java.util.TreeSet; public class InputMethodAndLanguageSettings extends SettingsPreferenceFragment implements Preference.OnPreferenceChangeListener, InputManager.InputDeviceListener, KeyboardLayoutDialogFragment.OnSetupKeyboardLayoutsListener, Indexable, InputMethodPreference.OnSavePreferenceListener { private static final String TAG = "InputMethodAndLanguageSettings"; private static final String KEY_SPELL_CHECKERS = "spellcheckers_settings"; private static final String KEY_PHONE_LANGUAGE = "phone_language"; private static final String KEY_CURRENT_INPUT_METHOD = "current_input_method"; private static final String KEY_INPUT_METHOD_SELECTOR = "input_method_selector"; private static final String KEY_USER_DICTIONARY_SETTINGS = "key_user_dictionary_settings"; private static final String KEY_POINTER_SETTINGS_CATEGORY = "pointer_settings_category"; private static final String KEY_PREVIOUSLY_ENABLED_SUBTYPES = "previously_enabled_subtypes"; private static final String KEY_TOUCHSCREEN_HOVERING = "touchscreen_hovering"; private static final String KEY_TRACKPAD_SETTINGS = "gesture_pad_settings"; private static final String KEY_STYLUS_GESTURES = "stylus_gestures"; private static final String KEY_STYLUS_ICON_ENABLED = "stylus_icon_enabled"; private static final String KEY_VOICE_CATEGORY = "voice_category"; private static final String KEY_VOICE_WAKEUP = "voice_wakeup"; // false: on ICS or later private static final boolean SHOW_INPUT_METHOD_SWITCHER_SETTINGS = false; private static final String[] sSystemSettingNames = { System.TEXT_AUTO_REPLACE, System.TEXT_AUTO_CAPS, System.TEXT_AUTO_PUNCTUATE, }; private static final String[] sHardKeyboardKeys = { "auto_replace", "auto_caps", "auto_punctuate", }; private int mDefaultInputMethodSelectorVisibility = 0; private ListPreference mShowInputMethodSelectorPref; private SwitchPreference mStylusIconEnabled; private SwitchPreference mTouchscreenHovering; private PreferenceCategory mKeyboardSettingsCategory; private PreferenceCategory mHardKeyboardCategory; private PreferenceCategory mGameControllerCategory; private Preference mLanguagePref; private PreferenceScreen mStylusGestures; private final ArrayList mInputMethodPreferenceList = new ArrayList<>(); private final ArrayList mHardKeyboardPreferenceList = new ArrayList<>(); private InputManager mIm; private InputMethodManager mImm; private boolean mShowsOnlyFullImeAndKeyboardList; private Handler mHandler; private SettingsObserver mSettingsObserver; private Intent mIntentWaitingForResult; private InputMethodSettingValuesWrapper mInputMethodSettingValues; private DevicePolicyManager mDpm; private CMHardwareManager mHardware; @Override protected int getMetricsCategory() { return MetricsLogger.INPUTMETHOD_LANGUAGE; } @Override public void onCreate(Bundle icicle) { super.onCreate(icicle); addPreferencesFromResource(R.xml.language_settings); final Activity activity = getActivity(); mImm = (InputMethodManager) getSystemService(Context.INPUT_METHOD_SERVICE); mInputMethodSettingValues = InputMethodSettingValuesWrapper.getInstance(activity); mHardware = CMHardwareManager.getInstance(activity); try { mDefaultInputMethodSelectorVisibility = Integer.valueOf( getString(R.string.input_method_selector_visibility_default_value)); } catch (NumberFormatException e) { } if (activity.getAssets().getLocales().length == 1) { // No "Select language" pref if there's only one system locale available. getPreferenceScreen().removePreference(findPreference(KEY_PHONE_LANGUAGE)); } else { mLanguagePref = findPreference(KEY_PHONE_LANGUAGE); } if (SHOW_INPUT_METHOD_SWITCHER_SETTINGS) { mShowInputMethodSelectorPref = (ListPreference)findPreference( KEY_INPUT_METHOD_SELECTOR); mShowInputMethodSelectorPref.setOnPreferenceChangeListener(this); // TODO: Update current input method name on summary updateInputMethodSelectorSummary(loadInputMethodSelectorVisibility()); } new VoiceInputOutputSettings(this).onCreate(); // Get references to dynamically constructed categories. mHardKeyboardCategory = (PreferenceCategory)findPreference("hard_keyboard"); mKeyboardSettingsCategory = (PreferenceCategory)findPreference( "keyboard_settings_category"); mGameControllerCategory = (PreferenceCategory)findPreference( "game_controller_settings_category"); final Intent startingIntent = activity.getIntent(); // Filter out irrelevant features if invoked from IME settings button. mShowsOnlyFullImeAndKeyboardList = Settings.ACTION_INPUT_METHOD_SETTINGS.equals( startingIntent.getAction()); if (mShowsOnlyFullImeAndKeyboardList) { getPreferenceScreen().removeAll(); getPreferenceScreen().addPreference(mHardKeyboardCategory); if (SHOW_INPUT_METHOD_SWITCHER_SETTINGS) { getPreferenceScreen().addPreference(mShowInputMethodSelectorPref); } mKeyboardSettingsCategory.removeAll(); getPreferenceScreen().addPreference(mKeyboardSettingsCategory); } // Build hard keyboard and game controller preference categories. mIm = (InputManager)activity.getSystemService(Context.INPUT_SERVICE); updateInputDevices(); PreferenceCategory pointerSettingsCategory = (PreferenceCategory) findPreference(KEY_POINTER_SETTINGS_CATEGORY); mStylusGestures = (PreferenceScreen) findPreference(KEY_STYLUS_GESTURES); mStylusIconEnabled = (SwitchPreference) findPreference(KEY_STYLUS_ICON_ENABLED); mTouchscreenHovering = (SwitchPreference) findPreference(KEY_TOUCHSCREEN_HOVERING); if (pointerSettingsCategory != null) { if (!getResources().getBoolean(com.android.internal.R.bool.config_stylusGestures)) { pointerSettingsCategory.removePreference(mStylusGestures); pointerSettingsCategory.removePreference(mStylusIconEnabled); } if (!mHardware.isSupported(CMHardwareManager.FEATURE_TOUCH_HOVERING)) { pointerSettingsCategory.removePreference(mTouchscreenHovering); mTouchscreenHovering = null; } else { mTouchscreenHovering.setChecked( mHardware.get(CMHardwareManager.FEATURE_TOUCH_HOVERING)); } Utils.updatePreferenceToSpecificActivityFromMetaDataOrRemove(getActivity(), pointerSettingsCategory, KEY_TRACKPAD_SETTINGS); if (pointerSettingsCategory.getPreferenceCount() == 0) { getPreferenceScreen().removePreference(pointerSettingsCategory); } } // Enable or disable mStatusBarImeSwitcher based on boolean: config_show_cmIMESwitcher boolean showCmImeSwitcher = getResources().getBoolean( com.android.internal.R.bool.config_show_cmIMESwitcher); if (!showCmImeSwitcher) { Preference pref = findPreference(CMSettings.System.STATUS_BAR_IME_SWITCHER); if (pref != null) { getPreferenceScreen().removePreference(pref); } } // Spell Checker final Preference spellChecker = findPreference(KEY_SPELL_CHECKERS); if (spellChecker != null) { // Note: KEY_SPELL_CHECKERS preference is marked as persistent="false" in XML. InputMethodAndSubtypeUtil.removeUnnecessaryNonPersistentPreference(spellChecker); final Intent intent = new Intent(Intent.ACTION_MAIN); intent.setClass(activity, SubSettings.class); intent.putExtra(SettingsActivity.EXTRA_SHOW_FRAGMENT, SpellCheckersSettings.class.getName()); intent.putExtra(SettingsActivity.EXTRA_SHOW_FRAGMENT_TITLE_RESID, R.string.spellcheckers_settings_title); spellChecker.setIntent(intent); } mHandler = new Handler(); mSettingsObserver = new SettingsObserver(mHandler, activity); mDpm = (DevicePolicyManager) (getActivity(). getSystemService(Context.DEVICE_POLICY_SERVICE)); // If we've launched from the keyboard layout notification, go ahead and just show the // keyboard layout dialog. final InputDeviceIdentifier identifier = startingIntent.getParcelableExtra(Settings.EXTRA_INPUT_DEVICE_IDENTIFIER); if (mShowsOnlyFullImeAndKeyboardList && identifier != null) { showKeyboardLayoutDialog(identifier); } if (!Utils.isUserOwner() || !Utils.isPackageInstalled(getActivity(), VoiceWakeupSettings.VOICE_WAKEUP_PACKAGE, false)) { PreferenceCategory voiceCategory = (PreferenceCategory) findPreference(KEY_VOICE_CATEGORY); if (voiceCategory != null) { Preference wakeup = voiceCategory.findPreference(KEY_VOICE_WAKEUP); if (wakeup != null) { voiceCategory.removePreference(wakeup); } } } } private void updateInputMethodSelectorSummary(int value) { String[] inputMethodSelectorTitles = getResources().getStringArray( R.array.input_method_selector_titles); if (inputMethodSelectorTitles.length > value) { mShowInputMethodSelectorPref.setSummary(inputMethodSelectorTitles[value]); mShowInputMethodSelectorPref.setValue(String.valueOf(value)); } } private void updateUserDictionaryPreference(Preference userDictionaryPreference) { final Activity activity = getActivity(); final TreeSet localeSet = UserDictionaryList.getUserDictionaryLocalesSet(activity); if (null == localeSet) { // The locale list is null if and only if the user dictionary service is // not present or disabled. In this case we need to remove the preference. getPreferenceScreen().removePreference(userDictionaryPreference); } else { userDictionaryPreference.setOnPreferenceClickListener( new OnPreferenceClickListener() { @Override public boolean onPreferenceClick(Preference arg0) { // Redirect to UserDictionarySettings if the user needs only one // language. final Bundle extras = new Bundle(); final Class targetFragment; if (localeSet.size() <= 1) { if (!localeSet.isEmpty()) { // If the size of localeList is 0, we don't set the locale // parameter in the extras. This will be interpreted by the // UserDictionarySettings class as meaning // "the current locale". Note that with the current code for // UserDictionaryList#getUserDictionaryLocalesSet() // the locale list always has at least one element, since it // always includes the current locale explicitly. // @see UserDictionaryList.getUserDictionaryLocalesSet(). extras.putString("locale", localeSet.first()); } targetFragment = UserDictionarySettings.class; } else { targetFragment = UserDictionaryList.class; } startFragment(InputMethodAndLanguageSettings.this, targetFragment.getCanonicalName(), -1, -1, extras); return true; } }); } } @Override public void onResume() { super.onResume(); mSettingsObserver.resume(); mIm.registerInputDeviceListener(this, null); final Preference spellChecker = findPreference(KEY_SPELL_CHECKERS); if (spellChecker != null) { final TextServicesManager tsm = (TextServicesManager) getSystemService( Context.TEXT_SERVICES_MANAGER_SERVICE); final SpellCheckerInfo sci = tsm.getCurrentSpellChecker(); spellChecker.setEnabled(sci != null); if (tsm.isSpellCheckerEnabled() && sci != null) { spellChecker.setSummary(sci.loadLabel(getPackageManager())); } else { spellChecker.setSummary(R.string.switch_off_text); } } if (mStylusIconEnabled != null) { mStylusIconEnabled.setChecked(Settings.System.getInt(getActivity().getContentResolver(), Settings.System.STYLUS_ICON_ENABLED, 0) == 1); } if (!mShowsOnlyFullImeAndKeyboardList) { if (mLanguagePref != null) { String localeName = getLocaleName(getActivity()); mLanguagePref.setSummary(localeName); } updateUserDictionaryPreference(findPreference(KEY_USER_DICTIONARY_SETTINGS)); if (SHOW_INPUT_METHOD_SWITCHER_SETTINGS) { mShowInputMethodSelectorPref.setOnPreferenceChangeListener(this); } } // Hard keyboard if (!mHardKeyboardPreferenceList.isEmpty()) { for (int i = 0; i < sHardKeyboardKeys.length; ++i) { SwitchPreference swPref = (SwitchPreference) mHardKeyboardCategory.findPreference(sHardKeyboardKeys[i]); swPref.setChecked( System.getInt(getContentResolver(), sSystemSettingNames[i], 1) > 0); } } updateInputDevices(); // Refresh internal states in mInputMethodSettingValues to keep the latest // "InputMethodInfo"s and "InputMethodSubtype"s mInputMethodSettingValues.refreshAllInputMethodAndSubtypes(); updateInputMethodPreferenceViews(); } @Override public void onPause() { super.onPause(); mIm.unregisterInputDeviceListener(this); mSettingsObserver.pause(); if (SHOW_INPUT_METHOD_SWITCHER_SETTINGS) { mShowInputMethodSelectorPref.setOnPreferenceChangeListener(null); } // TODO: Consolidate the logic to InputMethodSettingsWrapper InputMethodAndSubtypeUtil.saveInputMethodSubtypeList( this, getContentResolver(), mInputMethodSettingValues.getInputMethodList(), !mHardKeyboardPreferenceList.isEmpty()); } @Override public void onInputDeviceAdded(int deviceId) { updateInputDevices(); } @Override public void onInputDeviceChanged(int deviceId) { updateInputDevices(); } @Override public void onInputDeviceRemoved(int deviceId) { updateInputDevices(); } @Override public boolean onPreferenceTreeClick(PreferenceScreen preferenceScreen, Preference preference) { // Input Method stuff if (Utils.isMonkeyRunning()) { return false; } if (preference == mStylusIconEnabled) { Settings.System.putInt(getActivity().getContentResolver(), Settings.System.STYLUS_ICON_ENABLED, mStylusIconEnabled.isChecked() ? 1 : 0); } else if (preference == mTouchscreenHovering) { boolean touchHoveringEnable = mTouchscreenHovering.isChecked(); CMSettings.Secure.putInt(getActivity().getContentResolver(), CMSettings.Secure.FEATURE_TOUCH_HOVERING, touchHoveringEnable ? 1 : 0); return true; } else if (preference instanceof PreferenceScreen) { if (preference.getFragment() != null) { // Fragment will be handled correctly by the super class. } else if (KEY_CURRENT_INPUT_METHOD.equals(preference.getKey())) { final InputMethodManager imm = (InputMethodManager) getSystemService(Context.INPUT_METHOD_SERVICE); imm.showInputMethodPicker(false /* showAuxiliarySubtypes */); } } else if (preference instanceof SwitchPreference) { final SwitchPreference pref = (SwitchPreference) preference; if (pref == mGameControllerCategory.findPreference("vibrate_input_devices")) { System.putInt(getContentResolver(), Settings.System.VIBRATE_INPUT_DEVICES, pref.isChecked() ? 1 : 0); return true; } if (!mHardKeyboardPreferenceList.isEmpty()) { for (int i = 0; i < sHardKeyboardKeys.length; ++i) { if (pref == mHardKeyboardCategory.findPreference(sHardKeyboardKeys[i])) { System.putInt(getContentResolver(), sSystemSettingNames[i], pref.isChecked() ? 1 : 0); return true; } } } } return super.onPreferenceTreeClick(preferenceScreen, preference); } private static String getLocaleName(Context context) { // We want to show the same string that the LocalePicker used. // TODO: should this method be in LocalePicker instead? Locale currentLocale = context.getResources().getConfiguration().locale; List locales = LocalePicker.getAllAssetLocales(context, true); for (LocalePicker.LocaleInfo locale : locales) { if (locale.getLocale().equals(currentLocale)) { return locale.getLabel(); } } // This can't happen as long as the locale was one set by Settings. // Fall back in case a developer is testing an unsupported locale. return currentLocale.getDisplayName(currentLocale); } private void saveInputMethodSelectorVisibility(String value) { try { int intValue = Integer.valueOf(value); Settings.Secure.putInt(getContentResolver(), Settings.Secure.INPUT_METHOD_SELECTOR_VISIBILITY, intValue); updateInputMethodSelectorSummary(intValue); } catch(NumberFormatException e) { } } private int loadInputMethodSelectorVisibility() { return Settings.Secure.getInt(getContentResolver(), Settings.Secure.INPUT_METHOD_SELECTOR_VISIBILITY, mDefaultInputMethodSelectorVisibility); } @Override public boolean onPreferenceChange(Preference preference, Object value) { if (SHOW_INPUT_METHOD_SWITCHER_SETTINGS) { if (preference == mShowInputMethodSelectorPref) { if (value instanceof String) { saveInputMethodSelectorVisibility((String)value); } } } return false; } private void updateInputMethodPreferenceViews() { synchronized (mInputMethodPreferenceList) { // Clear existing "InputMethodPreference"s for (final InputMethodPreference pref : mInputMethodPreferenceList) { mKeyboardSettingsCategory.removePreference(pref); } mInputMethodPreferenceList.clear(); List permittedList = mDpm.getPermittedInputMethodsForCurrentUser(); final Context context = getActivity(); final List imis = mShowsOnlyFullImeAndKeyboardList ? mInputMethodSettingValues.getInputMethodList() : mImm.getEnabledInputMethodList(); final int N = (imis == null ? 0 : imis.size()); for (int i = 0; i < N; ++i) { final InputMethodInfo imi = imis.get(i); final boolean isAllowedByOrganization = permittedList == null || permittedList.contains(imi.getPackageName()); final InputMethodPreference pref = new InputMethodPreference( context, imi, mShowsOnlyFullImeAndKeyboardList /* hasSwitch */, isAllowedByOrganization, this); mInputMethodPreferenceList.add(pref); } final Collator collator = Collator.getInstance(); Collections.sort(mInputMethodPreferenceList, new Comparator() { @Override public int compare(InputMethodPreference lhs, InputMethodPreference rhs) { return lhs.compareTo(rhs, collator); } }); for (int i = 0; i < N; ++i) { final InputMethodPreference pref = mInputMethodPreferenceList.get(i); mKeyboardSettingsCategory.addPreference(pref); InputMethodAndSubtypeUtil.removeUnnecessaryNonPersistentPreference(pref); pref.updatePreferenceViews(); } } updateCurrentImeName(); // TODO: Consolidate the logic with InputMethodSettingsWrapper // CAVEAT: The preference class here does not know about the default value - that is // managed by the Input Method Manager Service, so in this case it could save the wrong // value. Hence we must update the checkboxes here. InputMethodAndSubtypeUtil.loadInputMethodSubtypeList( this, getContentResolver(), mInputMethodSettingValues.getInputMethodList(), null); } @Override public void onSaveInputMethodPreference(final InputMethodPreference pref) { final InputMethodInfo imi = pref.getInputMethodInfo(); if (!pref.isChecked()) { // An IME is being disabled. Save enabled subtypes of the IME to shared preference to be // able to re-enable these subtypes when the IME gets re-enabled. saveEnabledSubtypesOf(imi); } final boolean hasHardwareKeyboard = getResources().getConfiguration().keyboard == Configuration.KEYBOARD_QWERTY; InputMethodAndSubtypeUtil.saveInputMethodSubtypeList(this, getContentResolver(), mImm.getInputMethodList(), hasHardwareKeyboard); // Update input method settings and preference list. mInputMethodSettingValues.refreshAllInputMethodAndSubtypes(); if (pref.isChecked()) { // An IME is being enabled. Load the previously enabled subtypes from shared preference // and enable these subtypes. restorePreviouslyEnabledSubtypesOf(imi); } for (final InputMethodPreference p : mInputMethodPreferenceList) { p.updatePreferenceViews(); } } private void saveEnabledSubtypesOf(final InputMethodInfo imi) { final HashSet enabledSubtypeIdSet = new HashSet<>(); final List enabledSubtypes = mImm.getEnabledInputMethodSubtypeList( imi, true /* allowsImplicitlySelectedSubtypes */); for (final InputMethodSubtype subtype : enabledSubtypes) { final String subtypeId = Integer.toString(subtype.hashCode()); enabledSubtypeIdSet.add(subtypeId); } final HashMap> imeToEnabledSubtypeIdsMap = loadPreviouslyEnabledSubtypeIdsMap(); final String imiId = imi.getId(); imeToEnabledSubtypeIdsMap.put(imiId, enabledSubtypeIdSet); savePreviouslyEnabledSubtypeIdsMap(imeToEnabledSubtypeIdsMap); } private void restorePreviouslyEnabledSubtypesOf(final InputMethodInfo imi) { final HashMap> imeToEnabledSubtypeIdsMap = loadPreviouslyEnabledSubtypeIdsMap(); final String imiId = imi.getId(); final HashSet enabledSubtypeIdSet = imeToEnabledSubtypeIdsMap.remove(imiId); if (enabledSubtypeIdSet == null) { return; } savePreviouslyEnabledSubtypeIdsMap(imeToEnabledSubtypeIdsMap); InputMethodAndSubtypeUtil.enableInputMethodSubtypesOf( getContentResolver(), imiId, enabledSubtypeIdSet); } private HashMap> loadPreviouslyEnabledSubtypeIdsMap() { final Context context = getActivity(); final SharedPreferences prefs = PreferenceManager.getDefaultSharedPreferences(context); final String imesAndSubtypesString = prefs.getString(KEY_PREVIOUSLY_ENABLED_SUBTYPES, null); return InputMethodAndSubtypeUtil.parseInputMethodsAndSubtypesString(imesAndSubtypesString); } private void savePreviouslyEnabledSubtypeIdsMap( final HashMap> subtypesMap) { final Context context = getActivity(); final SharedPreferences prefs = PreferenceManager.getDefaultSharedPreferences(context); final String imesAndSubtypesString = InputMethodAndSubtypeUtil .buildInputMethodsAndSubtypesString(subtypesMap); prefs.edit().putString(KEY_PREVIOUSLY_ENABLED_SUBTYPES, imesAndSubtypesString).apply(); } private void updateCurrentImeName() { final Context context = getActivity(); if (context == null || mImm == null) return; final Preference curPref = getPreferenceScreen().findPreference(KEY_CURRENT_INPUT_METHOD); if (curPref != null) { final CharSequence curIme = mInputMethodSettingValues.getCurrentInputMethodName(context); if (!TextUtils.isEmpty(curIme)) { synchronized (this) { curPref.setSummary(curIme); } } } } private void updateInputDevices() { updateHardKeyboards(); updateGameControllers(); } private void updateHardKeyboards() { mHardKeyboardPreferenceList.clear(); final int[] devices = InputDevice.getDeviceIds(); for (int i = 0; i < devices.length; i++) { InputDevice device = InputDevice.getDevice(devices[i]); if (device != null && !device.isVirtual() && device.isFullKeyboard()) { final InputDeviceIdentifier identifier = device.getIdentifier(); final String keyboardLayoutDescriptor = mIm.getCurrentKeyboardLayoutForInputDevice(identifier); final KeyboardLayout keyboardLayout = keyboardLayoutDescriptor != null ? mIm.getKeyboardLayout(keyboardLayoutDescriptor) : null; final PreferenceScreen pref = new PreferenceScreen(getActivity(), null); pref.setTitle(device.getName()); if (keyboardLayout != null) { pref.setSummary(keyboardLayout.toString()); } else { pref.setSummary(R.string.keyboard_layout_default_label); } pref.setOnPreferenceClickListener(new Preference.OnPreferenceClickListener() { @Override public boolean onPreferenceClick(Preference preference) { showKeyboardLayoutDialog(identifier); return true; } }); mHardKeyboardPreferenceList.add(pref); } } if (!mHardKeyboardPreferenceList.isEmpty()) { for (int i = mHardKeyboardCategory.getPreferenceCount(); i-- > 0; ) { final Preference pref = mHardKeyboardCategory.getPreference(i); if (pref.getOrder() < 1000) { mHardKeyboardCategory.removePreference(pref); } } Collections.sort(mHardKeyboardPreferenceList); final int count = mHardKeyboardPreferenceList.size(); for (int i = 0; i < count; i++) { final Preference pref = mHardKeyboardPreferenceList.get(i); pref.setOrder(i); mHardKeyboardCategory.addPreference(pref); } getPreferenceScreen().addPreference(mHardKeyboardCategory); } else { getPreferenceScreen().removePreference(mHardKeyboardCategory); } } private void showKeyboardLayoutDialog(InputDeviceIdentifier inputDeviceIdentifier) { KeyboardLayoutDialogFragment fragment = new KeyboardLayoutDialogFragment( inputDeviceIdentifier); fragment.setTargetFragment(this, 0); fragment.show(getActivity().getFragmentManager(), "keyboardLayout"); } @Override public void onSetupKeyboardLayouts(InputDeviceIdentifier inputDeviceIdentifier) { final Intent intent = new Intent(Intent.ACTION_MAIN); intent.setClass(getActivity(), KeyboardLayoutPickerActivity.class); intent.putExtra(KeyboardLayoutPickerFragment.EXTRA_INPUT_DEVICE_IDENTIFIER, inputDeviceIdentifier); mIntentWaitingForResult = intent; startActivityForResult(intent, 0); } @Override public void onActivityResult(int requestCode, int resultCode, Intent data) { super.onActivityResult(requestCode, resultCode, data); if (mIntentWaitingForResult != null) { InputDeviceIdentifier inputDeviceIdentifier = mIntentWaitingForResult .getParcelableExtra(KeyboardLayoutPickerFragment.EXTRA_INPUT_DEVICE_IDENTIFIER); mIntentWaitingForResult = null; showKeyboardLayoutDialog(inputDeviceIdentifier); } } private void updateGameControllers() { if (haveInputDeviceWithVibrator()) { getPreferenceScreen().addPreference(mGameControllerCategory); SwitchPreference pref = (SwitchPreference) mGameControllerCategory.findPreference("vibrate_input_devices"); pref.setChecked(System.getInt(getContentResolver(), Settings.System.VIBRATE_INPUT_DEVICES, 1) > 0); } else { getPreferenceScreen().removePreference(mGameControllerCategory); } } private static boolean haveInputDeviceWithVibrator() { final int[] devices = InputDevice.getDeviceIds(); for (int i = 0; i < devices.length; i++) { InputDevice device = InputDevice.getDevice(devices[i]); if (device != null && !device.isVirtual() && device.getVibrator().hasVibrator()) { return true; } } return false; } private class SettingsObserver extends ContentObserver { private Context mContext; public SettingsObserver(Handler handler, Context context) { super(handler); mContext = context; } @Override public void onChange(boolean selfChange) { updateCurrentImeName(); } public void resume() { final ContentResolver cr = mContext.getContentResolver(); cr.registerContentObserver( Settings.Secure.getUriFor(Settings.Secure.DEFAULT_INPUT_METHOD), false, this); cr.registerContentObserver(Settings.Secure.getUriFor( Settings.Secure.SELECTED_INPUT_METHOD_SUBTYPE), false, this); } public void pause() { mContext.getContentResolver().unregisterContentObserver(this); } } public static void restore(Context context) { final SharedPreferences prefs = PreferenceManager.getDefaultSharedPreferences(context); final CMHardwareManager hardware = CMHardwareManager.getInstance(context); if (hardware.isSupported(CMHardwareManager.FEATURE_TOUCH_HOVERING)) { final boolean enabled = prefs.getBoolean(KEY_TOUCHSCREEN_HOVERING, hardware.get(CMHardwareManager.FEATURE_TOUCH_HOVERING)); CMSettings.Secure.putInt(context.getContentResolver(), CMSettings.Secure.FEATURE_TOUCH_HOVERING, enabled ? 1 : 0); } } public static final Indexable.SearchIndexProvider SEARCH_INDEX_DATA_PROVIDER = new BaseSearchIndexProvider() { @Override public List getRawDataToIndex(Context context, boolean enabled) { List indexables = new ArrayList<>(); final String screenTitle = context.getString(R.string.language_keyboard_settings_title); // Locale picker. if (context.getAssets().getLocales().length > 1) { String localeName = getLocaleName(context); SearchIndexableRaw indexable = new SearchIndexableRaw(context); indexable.key = KEY_PHONE_LANGUAGE; indexable.title = context.getString(R.string.phone_language); indexable.summaryOn = localeName; indexable.summaryOff = localeName; indexable.screenTitle = screenTitle; indexables.add(indexable); } // Spell checker. SearchIndexableRaw indexable = new SearchIndexableRaw(context); indexable.key = KEY_SPELL_CHECKERS; indexable.title = context.getString(R.string.spellcheckers_settings_title); indexable.screenTitle = screenTitle; indexable.keywords = context.getString(R.string.keywords_spell_checker); indexables.add(indexable); // User dictionary. if (UserDictionaryList.getUserDictionaryLocalesSet(context) != null) { indexable = new SearchIndexableRaw(context); indexable.key = "user_dict_settings"; indexable.title = context.getString(R.string.user_dict_settings_title); indexable.screenTitle = screenTitle; indexables.add(indexable); } // Keyboard settings. indexable = new SearchIndexableRaw(context); indexable.key = "keyboard_settings"; indexable.title = context.getString(R.string.keyboard_settings_category); indexable.screenTitle = screenTitle; indexable.keywords = context.getString(R.string.keywords_keyboard_and_ime); indexables.add(indexable); InputMethodSettingValuesWrapper immValues = InputMethodSettingValuesWrapper .getInstance(context); immValues.refreshAllInputMethodAndSubtypes(); // Current IME. String currImeName = immValues.getCurrentInputMethodName(context).toString(); indexable = new SearchIndexableRaw(context); indexable.key = KEY_CURRENT_INPUT_METHOD; indexable.title = context.getString(R.string.current_input_method); indexable.summaryOn = currImeName; indexable.summaryOff = currImeName; indexable.screenTitle = screenTitle; indexables.add(indexable); InputMethodManager inputMethodManager = (InputMethodManager) context.getSystemService( Context.INPUT_METHOD_SERVICE); // All other IMEs. List inputMethods = immValues.getInputMethodList(); final int inputMethodCount = (inputMethods == null ? 0 : inputMethods.size()); for (int i = 0; i < inputMethodCount; ++i) { InputMethodInfo inputMethod = inputMethods.get(i); StringBuilder builder = new StringBuilder(); List subtypes = inputMethodManager .getEnabledInputMethodSubtypeList(inputMethod, true); final int subtypeCount = subtypes.size(); for (int j = 0; j < subtypeCount; j++) { InputMethodSubtype subtype = subtypes.get(j); if (builder.length() > 0) { builder.append(','); } CharSequence subtypeLabel = subtype.getDisplayName(context, inputMethod.getPackageName(), inputMethod.getServiceInfo() .applicationInfo); builder.append(subtypeLabel); } String summary = builder.toString(); ServiceInfo serviceInfo = inputMethod.getServiceInfo(); ComponentName componentName = new ComponentName(serviceInfo.packageName, serviceInfo.name); indexable = new SearchIndexableRaw(context); indexable.key = componentName.flattenToString(); indexable.title = inputMethod.loadLabel(context.getPackageManager()).toString(); indexable.summaryOn = summary; indexable.summaryOff = summary; indexable.screenTitle = screenTitle; indexables.add(indexable); } // Hard keyboards InputManager inputManager = (InputManager) context.getSystemService( Context.INPUT_SERVICE); boolean hasHardKeyboards = false; final int[] devices = InputDevice.getDeviceIds(); for (int i = 0; i < devices.length; i++) { InputDevice device = InputDevice.getDevice(devices[i]); if (device == null || device.isVirtual() || !device.isFullKeyboard()) { continue; } hasHardKeyboards = true; InputDeviceIdentifier identifier = device.getIdentifier(); String keyboardLayoutDescriptor = inputManager.getCurrentKeyboardLayoutForInputDevice(identifier); KeyboardLayout keyboardLayout = keyboardLayoutDescriptor != null ? inputManager.getKeyboardLayout(keyboardLayoutDescriptor) : null; String summary; if (keyboardLayout != null) { summary = keyboardLayout.toString(); } else { summary = context.getString(R.string.keyboard_layout_default_label); } indexable = new SearchIndexableRaw(context); indexable.key = device.getName(); indexable.title = device.getName(); indexable.summaryOn = summary; indexable.summaryOff = summary; indexable.screenTitle = screenTitle; indexables.add(indexable); } if (hasHardKeyboards) { // Hard keyboard category. indexable = new SearchIndexableRaw(context); indexable.key = "builtin_keyboard_settings"; indexable.title = context.getString( R.string.builtin_keyboard_settings_title); indexable.screenTitle = screenTitle; indexables.add(indexable); // Auto replace. indexable = new SearchIndexableRaw(context); indexable.key = "auto_replace"; indexable.title = context.getString(R.string.auto_replace); indexable.summaryOn = context.getString(R.string.auto_replace_summary); indexable.summaryOff = context.getString(R.string.auto_replace_summary); indexable.screenTitle = screenTitle; indexables.add(indexable); // Auto caps. indexable = new SearchIndexableRaw(context); indexable.key = "auto_caps"; indexable.title = context.getString(R.string.auto_caps); indexable.summaryOn = context.getString(R.string.auto_caps_summary); indexable.summaryOff = context.getString(R.string.auto_caps_summary); indexable.screenTitle = screenTitle; indexables.add(indexable); // Auto punctuate. indexable = new SearchIndexableRaw(context); indexable.key = "auto_punctuate"; indexable.title = context.getString(R.string.auto_punctuate); indexable.summaryOn = context.getString(R.string.auto_punctuate_summary); indexable.summaryOff = context.getString(R.string.auto_punctuate_summary); indexable.screenTitle = screenTitle; indexables.add(indexable); } // Text-to-speech. TtsEngines ttsEngines = new TtsEngines(context); if (!ttsEngines.getEngines().isEmpty()) { indexable = new SearchIndexableRaw(context); indexable.key = "tts_settings"; indexable.title = context.getString(R.string.tts_settings_title); indexable.screenTitle = screenTitle; indexable.keywords = context.getString(R.string.keywords_text_to_speech_output); indexables.add(indexable); } // Pointer settings. indexable = new SearchIndexableRaw(context); indexable.key = "pointer_settings_category"; indexable.title = context.getString(R.string.pointer_settings_category); indexable.screenTitle = screenTitle; indexables.add(indexable); indexable = new SearchIndexableRaw(context); indexable.key = "pointer_speed"; indexable.title = context.getString(R.string.pointer_speed); indexable.screenTitle = screenTitle; indexables.add(indexable); // Game controllers. if (haveInputDeviceWithVibrator()) { indexable = new SearchIndexableRaw(context); indexable.key = "vibrate_input_devices"; indexable.title = context.getString(R.string.vibrate_input_devices); indexable.summaryOn = context.getString(R.string.vibrate_input_devices_summary); indexable.summaryOff = context.getString(R.string.vibrate_input_devices_summary); indexable.screenTitle = screenTitle; indexables.add(indexable); } return indexables; } }; }