| /* |
| * 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 com.android.settings.R; |
| import com.android.settings.Settings.KeyboardLayoutPickerActivity; |
| import com.android.settings.Settings.SpellCheckersSettingsActivity; |
| import com.android.settings.SettingsPreferenceFragment; |
| import com.android.settings.Utils; |
| import com.android.settings.VoiceInputOutputSettings; |
| |
| import android.app.Activity; |
| import android.content.ContentResolver; |
| import android.content.Context; |
| import android.content.Intent; |
| import android.content.pm.PackageManager; |
| import android.content.res.Configuration; |
| import android.content.res.Resources; |
| import android.database.ContentObserver; |
| import android.hardware.input.InputManager; |
| import android.hardware.input.KeyboardLayout; |
| import android.os.Bundle; |
| import android.os.Handler; |
| import android.preference.CheckBoxPreference; |
| import android.preference.ListPreference; |
| import android.preference.Preference; |
| import android.preference.PreferenceCategory; |
| import android.preference.PreferenceScreen; |
| import android.provider.Settings; |
| import android.provider.Settings.System; |
| import android.text.TextUtils; |
| import android.view.InputDevice; |
| import android.view.inputmethod.InputMethodInfo; |
| import android.view.inputmethod.InputMethodManager; |
| |
| import java.util.ArrayList; |
| import java.util.Collections; |
| import java.util.List; |
| import java.util.TreeSet; |
| |
| public class InputMethodAndLanguageSettings extends SettingsPreferenceFragment |
| implements Preference.OnPreferenceChangeListener, InputManager.InputDeviceListener, |
| KeyboardLayoutDialogFragment.OnSetupKeyboardLayoutsListener { |
| |
| 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"; |
| // 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 PreferenceCategory mKeyboardSettingsCategory; |
| private PreferenceCategory mHardKeyboardCategory; |
| private PreferenceCategory mGameControllerCategory; |
| private Preference mLanguagePref; |
| private final ArrayList<InputMethodPreference> mInputMethodPreferenceList = |
| new ArrayList<InputMethodPreference>(); |
| private final ArrayList<PreferenceScreen> mHardKeyboardPreferenceList = |
| new ArrayList<PreferenceScreen>(); |
| private InputManager mIm; |
| private InputMethodManager mImm; |
| private List<InputMethodInfo> mImis; |
| private boolean mIsOnlyImeSettings; |
| private Handler mHandler; |
| @SuppressWarnings("unused") |
| private SettingsObserver mSettingsObserver; |
| private Intent mIntentWaitingForResult; |
| |
| @Override |
| public void onCreate(Bundle icicle) { |
| super.onCreate(icicle); |
| |
| addPreferencesFromResource(R.xml.language_settings); |
| |
| try { |
| mDefaultInputMethodSelectorVisibility = Integer.valueOf( |
| getString(R.string.input_method_selector_visibility_default_value)); |
| } catch (NumberFormatException e) { |
| } |
| |
| if (getActivity().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"); |
| |
| // Filter out irrelevant features if invoked from IME settings button. |
| mIsOnlyImeSettings = Settings.ACTION_INPUT_METHOD_SETTINGS.equals( |
| getActivity().getIntent().getAction()); |
| getActivity().getIntent().setAction(null); |
| if (mIsOnlyImeSettings) { |
| getPreferenceScreen().removeAll(); |
| getPreferenceScreen().addPreference(mHardKeyboardCategory); |
| if (SHOW_INPUT_METHOD_SWITCHER_SETTINGS) { |
| getPreferenceScreen().addPreference(mShowInputMethodSelectorPref); |
| } |
| getPreferenceScreen().addPreference(mKeyboardSettingsCategory); |
| } |
| |
| // Build IME preference category. |
| mImm = (InputMethodManager) getSystemService(Context.INPUT_METHOD_SERVICE); |
| mImis = mImm.getInputMethodList(); |
| |
| mKeyboardSettingsCategory.removeAll(); |
| if (!mIsOnlyImeSettings) { |
| final PreferenceScreen currentIme = new PreferenceScreen(getActivity(), null); |
| currentIme.setKey(KEY_CURRENT_INPUT_METHOD); |
| currentIme.setTitle(getResources().getString(R.string.current_input_method)); |
| mKeyboardSettingsCategory.addPreference(currentIme); |
| } |
| |
| mInputMethodPreferenceList.clear(); |
| final int N = (mImis == null ? 0 : mImis.size()); |
| for (int i = 0; i < N; ++i) { |
| final InputMethodInfo imi = mImis.get(i); |
| final InputMethodPreference pref = getInputMethodPreference(imi, N); |
| mInputMethodPreferenceList.add(pref); |
| } |
| |
| if (!mInputMethodPreferenceList.isEmpty()) { |
| Collections.sort(mInputMethodPreferenceList); |
| for (int i = 0; i < N; ++i) { |
| mKeyboardSettingsCategory.addPreference(mInputMethodPreferenceList.get(i)); |
| } |
| } |
| |
| // Build hard keyboard and game controller preference categories. |
| mIm = (InputManager)getActivity().getSystemService(Context.INPUT_SERVICE); |
| updateInputDevices(); |
| |
| // Spell Checker |
| final Intent intent = new Intent(Intent.ACTION_MAIN); |
| intent.setClass(getActivity(), SpellCheckersSettingsActivity.class); |
| final SpellCheckersPreference scp = ((SpellCheckersPreference)findPreference( |
| "spellcheckers_settings")); |
| if (scp != null) { |
| scp.setFragmentIntent(this, intent); |
| } |
| |
| mHandler = new Handler(); |
| mSettingsObserver = new SettingsObserver(mHandler, getActivity()); |
| } |
| |
| 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<String> localeList = UserDictionaryList.getUserDictionaryLocalesSet(activity); |
| if (null == localeList) { |
| // 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 if (localeList.size() <= 1) { |
| final Intent intent = |
| new Intent(UserDictionaryList.USER_DICTIONARY_SETTINGS_INTENT_ACTION); |
| userDictionaryPreference.setTitle(R.string.user_dict_single_settings_title); |
| userDictionaryPreference.setIntent(intent); |
| userDictionaryPreference.setFragment( |
| com.android.settings.UserDictionarySettings.class.getName()); |
| // 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(). |
| if (localeList.size() == 1) { |
| final String locale = (String)localeList.toArray()[0]; |
| userDictionaryPreference.getExtras().putString("locale", locale); |
| } |
| } else { |
| userDictionaryPreference.setTitle(R.string.user_dict_multiple_settings_title); |
| userDictionaryPreference.setFragment(UserDictionaryList.class.getName()); |
| } |
| } |
| |
| @Override |
| public void onResume() { |
| super.onResume(); |
| |
| mSettingsObserver.resume(); |
| mIm.registerInputDeviceListener(this, null); |
| |
| if (!mIsOnlyImeSettings) { |
| if (mLanguagePref != null) { |
| Configuration conf = getResources().getConfiguration(); |
| String language = conf.locale.getLanguage(); |
| String localeString; |
| // TODO: This is not an accurate way to display the locale, as it is |
| // just working around the fact that we support limited dialects |
| // and want to pretend that the language is valid for all locales. |
| // We need a way to support languages that aren't tied to a particular |
| // locale instead of hiding the locale qualifier. |
| if (language.equals("zz")) { |
| String country = conf.locale.getCountry(); |
| if (country.equals("ZZ")) { |
| localeString = "[Developer] Accented English (zz_ZZ)"; |
| } else if (country.equals("ZY")) { |
| localeString = "[Developer] Fake Bi-Directional (zz_ZY)"; |
| } else { |
| localeString = ""; |
| } |
| } else if (hasOnlyOneLanguageInstance(language, |
| Resources.getSystem().getAssets().getLocales())) { |
| localeString = conf.locale.getDisplayLanguage(conf.locale); |
| } else { |
| localeString = conf.locale.getDisplayName(conf.locale); |
| } |
| if (localeString.length() > 1) { |
| localeString = Character.toUpperCase(localeString.charAt(0)) |
| + localeString.substring(1); |
| mLanguagePref.setSummary(localeString); |
| } |
| } |
| |
| 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) { |
| CheckBoxPreference chkPref = (CheckBoxPreference) |
| mHardKeyboardCategory.findPreference(sHardKeyboardKeys[i]); |
| chkPref.setChecked( |
| System.getInt(getContentResolver(), sSystemSettingNames[i], 1) > 0); |
| } |
| } |
| |
| updateInputDevices(); |
| |
| // IME |
| InputMethodAndSubtypeUtil.loadInputMethodSubtypeList( |
| this, getContentResolver(), mImis, null); |
| updateActiveInputMethodsSummary(); |
| } |
| |
| @Override |
| public void onPause() { |
| super.onPause(); |
| |
| mIm.unregisterInputDeviceListener(this); |
| mSettingsObserver.pause(); |
| |
| if (SHOW_INPUT_METHOD_SWITCHER_SETTINGS) { |
| mShowInputMethodSelectorPref.setOnPreferenceChangeListener(null); |
| } |
| InputMethodAndSubtypeUtil.saveInputMethodSubtypeList( |
| this, getContentResolver(), mImis, !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 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(); |
| } |
| } else if (preference instanceof CheckBoxPreference) { |
| final CheckBoxPreference chkPref = (CheckBoxPreference) preference; |
| if (!mHardKeyboardPreferenceList.isEmpty()) { |
| for (int i = 0; i < sHardKeyboardKeys.length; ++i) { |
| if (chkPref == mHardKeyboardCategory.findPreference(sHardKeyboardKeys[i])) { |
| System.putInt(getContentResolver(), sSystemSettingNames[i], |
| chkPref.isChecked() ? 1 : 0); |
| return true; |
| } |
| } |
| } |
| if (chkPref == mGameControllerCategory.findPreference("vibrate_input_devices")) { |
| System.putInt(getContentResolver(), Settings.System.VIBRATE_INPUT_DEVICES, |
| chkPref.isChecked() ? 1 : 0); |
| return true; |
| } |
| } |
| return super.onPreferenceTreeClick(preferenceScreen, preference); |
| } |
| |
| private boolean hasOnlyOneLanguageInstance(String languageCode, String[] locales) { |
| int count = 0; |
| for (String localeCode : locales) { |
| if (localeCode.length() > 2 |
| && localeCode.startsWith(languageCode)) { |
| count++; |
| if (count > 1) { |
| return false; |
| } |
| } |
| } |
| return count == 1; |
| } |
| |
| 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 updateActiveInputMethodsSummary() { |
| for (Preference pref : mInputMethodPreferenceList) { |
| if (pref instanceof InputMethodPreference) { |
| ((InputMethodPreference)pref).updateSummary(); |
| } |
| } |
| updateCurrentImeName(); |
| } |
| |
| 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 = InputMethodAndSubtypeUtil.getCurrentInputMethodName( |
| context, getContentResolver(), mImm, mImis, getPackageManager()); |
| if (!TextUtils.isEmpty(curIme)) { |
| synchronized(this) { |
| curPref.setSummary(curIme); |
| } |
| } |
| } |
| } |
| |
| private InputMethodPreference getInputMethodPreference(InputMethodInfo imi, int imiSize) { |
| final PackageManager pm = getPackageManager(); |
| final CharSequence label = imi.loadLabel(pm); |
| // IME settings |
| final Intent intent; |
| final String settingsActivity = imi.getSettingsActivity(); |
| if (!TextUtils.isEmpty(settingsActivity)) { |
| intent = new Intent(Intent.ACTION_MAIN); |
| intent.setClassName(imi.getPackageName(), settingsActivity); |
| } else { |
| intent = null; |
| } |
| |
| // Add a check box for enabling/disabling IME |
| InputMethodPreference pref = new InputMethodPreference(this, intent, mImm, imi, imiSize); |
| pref.setKey(imi.getId()); |
| pref.setTitle(label); |
| return pref; |
| } |
| |
| private void updateInputDevices() { |
| updateHardKeyboards(); |
| updateGameControllers(); |
| } |
| |
| private void updateHardKeyboards() { |
| mHardKeyboardPreferenceList.clear(); |
| if (getResources().getConfiguration().keyboard == Configuration.KEYBOARD_QWERTY) { |
| 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 String inputDeviceDescriptor = device.getDescriptor(); |
| final String keyboardLayoutDescriptor = |
| mIm.getCurrentKeyboardLayoutForInputDevice(inputDeviceDescriptor); |
| 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(inputDeviceDescriptor); |
| 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(String inputDeviceDescriptor) { |
| KeyboardLayoutDialogFragment fragment = |
| new KeyboardLayoutDialogFragment(inputDeviceDescriptor); |
| fragment.setTargetFragment(this, 0); |
| fragment.show(getActivity().getFragmentManager(), "keyboardLayout"); |
| } |
| |
| @Override |
| public void onSetupKeyboardLayouts(String inputDeviceDescriptor) { |
| final Intent intent = new Intent(Intent.ACTION_MAIN); |
| intent.setClass(getActivity(), KeyboardLayoutPickerActivity.class); |
| intent.putExtra(KeyboardLayoutPickerFragment.EXTRA_INPUT_DEVICE_DESCRIPTOR, |
| inputDeviceDescriptor); |
| mIntentWaitingForResult = intent; |
| startActivityForResult(intent, 0); |
| } |
| |
| @Override |
| public void onActivityResult(int requestCode, int resultCode, Intent data) { |
| super.onActivityResult(requestCode, resultCode, data); |
| |
| if (mIntentWaitingForResult != null) { |
| String inputDeviceDescriptor = mIntentWaitingForResult.getStringExtra( |
| KeyboardLayoutPickerFragment.EXTRA_INPUT_DEVICE_DESCRIPTOR); |
| mIntentWaitingForResult = null; |
| showKeyboardLayoutDialog(inputDeviceDescriptor); |
| } |
| } |
| |
| private void updateGameControllers() { |
| if (haveInputDeviceWithVibrator()) { |
| getPreferenceScreen().addPreference(mGameControllerCategory); |
| |
| CheckBoxPreference chkPref = (CheckBoxPreference) |
| mGameControllerCategory.findPreference("vibrate_input_devices"); |
| chkPref.setChecked(System.getInt(getContentResolver(), |
| Settings.System.VIBRATE_INPUT_DEVICES, 1) > 0); |
| } else { |
| getPreferenceScreen().removePreference(mGameControllerCategory); |
| } |
| } |
| |
| private 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); |
| } |
| } |
| } |