| /* |
| * Copyright (C) 2010 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.content.Context; |
| import android.content.Intent; |
| import android.content.pm.PackageManager; |
| import android.content.res.Configuration; |
| import android.os.Bundle; |
| import android.support.v7.preference.Preference; |
| import android.support.v7.preference.Preference.OnPreferenceChangeListener; |
| import android.support.v7.preference.PreferenceCategory; |
| import android.support.v7.preference.PreferenceScreen; |
| import android.support.v7.preference.TwoStatePreference; |
| import android.text.TextUtils; |
| import android.view.inputmethod.InputMethodInfo; |
| import android.view.inputmethod.InputMethodManager; |
| import android.view.inputmethod.InputMethodSubtype; |
| |
| import com.android.internal.logging.MetricsProto.MetricsEvent; |
| import com.android.settings.R; |
| import com.android.settings.SettingsPreferenceFragment; |
| |
| import java.text.Collator; |
| import java.util.ArrayList; |
| import java.util.Collections; |
| import java.util.Comparator; |
| import java.util.HashMap; |
| import java.util.List; |
| |
| public class InputMethodAndSubtypeEnabler extends SettingsPreferenceFragment |
| implements OnPreferenceChangeListener { |
| private boolean mHaveHardKeyboard; |
| private final HashMap<String, List<Preference>> mInputMethodAndSubtypePrefsMap = |
| new HashMap<>(); |
| private final HashMap<String, TwoStatePreference> mAutoSelectionPrefsMap = new HashMap<>(); |
| private InputMethodManager mImm; |
| // TODO: Change mInputMethodInfoList to Map |
| private List<InputMethodInfo> mInputMethodInfoList; |
| private Collator mCollator; |
| |
| @Override |
| public int getMetricsCategory() { |
| return MetricsEvent.INPUTMETHOD_SUBTYPE_ENABLER; |
| } |
| |
| @Override |
| public void onCreate(final Bundle icicle) { |
| super.onCreate(icicle); |
| mImm = (InputMethodManager) getSystemService(Context.INPUT_METHOD_SERVICE); |
| final Configuration config = getResources().getConfiguration(); |
| mHaveHardKeyboard = (config.keyboard == Configuration.KEYBOARD_QWERTY); |
| |
| // Input method id should be available from an Intent when this preference is launched as a |
| // single Activity (see InputMethodAndSubtypeEnablerActivity). It should be available |
| // from a preference argument when the preference is launched as a part of the other |
| // Activity (like a right pane of 2-pane Settings app) |
| final String targetImi = getStringExtraFromIntentOrArguments( |
| android.provider.Settings.EXTRA_INPUT_METHOD_ID); |
| |
| mInputMethodInfoList = mImm.getInputMethodList(); |
| mCollator = Collator.getInstance(); |
| |
| final PreferenceScreen root = getPreferenceManager().createPreferenceScreen(getActivity()); |
| final int imiCount = mInputMethodInfoList.size(); |
| for (int index = 0; index < imiCount; ++index) { |
| final InputMethodInfo imi = mInputMethodInfoList.get(index); |
| // Add subtype preferences of this IME when it is specified or no IME is specified. |
| if (imi.getId().equals(targetImi) || TextUtils.isEmpty(targetImi)) { |
| addInputMethodSubtypePreferences(imi, root); |
| } |
| } |
| setPreferenceScreen(root); |
| } |
| |
| private String getStringExtraFromIntentOrArguments(final String name) { |
| final Intent intent = getActivity().getIntent(); |
| final String fromIntent = intent.getStringExtra(name); |
| if (fromIntent != null) { |
| return fromIntent; |
| } |
| final Bundle arguments = getArguments(); |
| return (arguments == null) ? null : arguments.getString(name); |
| } |
| |
| @Override |
| public void onActivityCreated(final Bundle icicle) { |
| super.onActivityCreated(icicle); |
| final String title = getStringExtraFromIntentOrArguments(Intent.EXTRA_TITLE); |
| if (!TextUtils.isEmpty(title)) { |
| getActivity().setTitle(title); |
| } |
| } |
| |
| @Override |
| public void onResume() { |
| super.onResume(); |
| // Refresh internal states in mInputMethodSettingValues to keep the latest |
| // "InputMethodInfo"s and "InputMethodSubtype"s |
| InputMethodSettingValuesWrapper |
| .getInstance(getActivity()).refreshAllInputMethodAndSubtypes(); |
| InputMethodAndSubtypeUtil.loadInputMethodSubtypeList( |
| this, getContentResolver(), mInputMethodInfoList, mInputMethodAndSubtypePrefsMap); |
| updateAutoSelectionPreferences(); |
| } |
| |
| @Override |
| public void onPause() { |
| super.onPause(); |
| InputMethodAndSubtypeUtil.saveInputMethodSubtypeList(this, getContentResolver(), |
| mInputMethodInfoList, mHaveHardKeyboard); |
| } |
| |
| @Override |
| public boolean onPreferenceChange(final Preference pref, final Object newValue) { |
| if (!(newValue instanceof Boolean)) { |
| return true; // Invoke default behavior. |
| } |
| final boolean isChecking = (Boolean) newValue; |
| for (final String imiId : mAutoSelectionPrefsMap.keySet()) { |
| // An auto select subtype preference is changing. |
| if (mAutoSelectionPrefsMap.get(imiId) == pref) { |
| final TwoStatePreference autoSelectionPref = (TwoStatePreference) pref; |
| autoSelectionPref.setChecked(isChecking); |
| // Enable or disable subtypes depending on the auto selection preference. |
| setAutoSelectionSubtypesEnabled(imiId, autoSelectionPref.isChecked()); |
| return false; |
| } |
| } |
| // A subtype preference is changing. |
| if (pref instanceof InputMethodSubtypePreference) { |
| final InputMethodSubtypePreference subtypePref = (InputMethodSubtypePreference) pref; |
| subtypePref.setChecked(isChecking); |
| if (!subtypePref.isChecked()) { |
| // It takes care of the case where no subtypes are explicitly enabled then the auto |
| // selection preference is going to be checked. |
| updateAutoSelectionPreferences(); |
| } |
| return false; |
| } |
| return true; // Invoke default behavior. |
| } |
| |
| private void addInputMethodSubtypePreferences(final InputMethodInfo imi, |
| final PreferenceScreen root) { |
| final Context context = getPrefContext(); |
| final int subtypeCount = imi.getSubtypeCount(); |
| if (subtypeCount <= 1) { |
| return; |
| } |
| final String imiId = imi.getId(); |
| final PreferenceCategory keyboardSettingsCategory = |
| new PreferenceCategory(getPrefContext()); |
| root.addPreference(keyboardSettingsCategory); |
| final PackageManager pm = getPackageManager(); |
| final CharSequence label = imi.loadLabel(pm); |
| |
| keyboardSettingsCategory.setTitle(label); |
| keyboardSettingsCategory.setKey(imiId); |
| // TODO: Use toggle Preference if images are ready. |
| final TwoStatePreference autoSelectionPref = |
| new SwitchWithNoTextPreference(getPrefContext()); |
| mAutoSelectionPrefsMap.put(imiId, autoSelectionPref); |
| keyboardSettingsCategory.addPreference(autoSelectionPref); |
| autoSelectionPref.setOnPreferenceChangeListener(this); |
| |
| final PreferenceCategory activeInputMethodsCategory = |
| new PreferenceCategory(getPrefContext()); |
| activeInputMethodsCategory.setTitle(R.string.active_input_method_subtypes); |
| root.addPreference(activeInputMethodsCategory); |
| |
| CharSequence autoSubtypeLabel = null; |
| final ArrayList<Preference> subtypePreferences = new ArrayList<>(); |
| for (int index = 0; index < subtypeCount; ++index) { |
| final InputMethodSubtype subtype = imi.getSubtypeAt(index); |
| if (subtype.overridesImplicitlyEnabledSubtype()) { |
| if (autoSubtypeLabel == null) { |
| autoSubtypeLabel = InputMethodAndSubtypeUtil.getSubtypeLocaleNameAsSentence( |
| subtype, context, imi); |
| } |
| } else { |
| final Preference subtypePref = new InputMethodSubtypePreference( |
| context, subtype, imi); |
| subtypePreferences.add(subtypePref); |
| } |
| } |
| Collections.sort(subtypePreferences, new Comparator<Preference>() { |
| @Override |
| public int compare(final Preference lhs, final Preference rhs) { |
| if (lhs instanceof InputMethodSubtypePreference) { |
| return ((InputMethodSubtypePreference) lhs).compareTo(rhs, mCollator); |
| } |
| return lhs.compareTo(rhs); |
| } |
| }); |
| final int prefCount = subtypePreferences.size(); |
| for (int index = 0; index < prefCount; ++index) { |
| final Preference pref = subtypePreferences.get(index); |
| activeInputMethodsCategory.addPreference(pref); |
| pref.setOnPreferenceChangeListener(this); |
| InputMethodAndSubtypeUtil.removeUnnecessaryNonPersistentPreference(pref); |
| } |
| mInputMethodAndSubtypePrefsMap.put(imiId, subtypePreferences); |
| if (TextUtils.isEmpty(autoSubtypeLabel)) { |
| autoSelectionPref.setTitle( |
| R.string.use_system_language_to_select_input_method_subtypes); |
| } else { |
| autoSelectionPref.setTitle(autoSubtypeLabel); |
| } |
| } |
| |
| private boolean isNoSubtypesExplicitlySelected(final String imiId) { |
| final List<Preference> subtypePrefs = mInputMethodAndSubtypePrefsMap.get(imiId); |
| for (final Preference pref : subtypePrefs) { |
| if (pref instanceof TwoStatePreference && ((TwoStatePreference)pref).isChecked()) { |
| return false; |
| } |
| } |
| return true; |
| } |
| |
| private void setAutoSelectionSubtypesEnabled(final String imiId, |
| final boolean autoSelectionEnabled) { |
| final TwoStatePreference autoSelectionPref = mAutoSelectionPrefsMap.get(imiId); |
| if (autoSelectionPref == null) { |
| return; |
| } |
| autoSelectionPref.setChecked(autoSelectionEnabled); |
| final List<Preference> subtypePrefs = mInputMethodAndSubtypePrefsMap.get(imiId); |
| for (final Preference pref : subtypePrefs) { |
| if (pref instanceof TwoStatePreference) { |
| // When autoSelectionEnabled is true, all subtype prefs need to be disabled with |
| // implicitly checked subtypes. In case of false, all subtype prefs need to be |
| // enabled. |
| pref.setEnabled(!autoSelectionEnabled); |
| if (autoSelectionEnabled) { |
| ((TwoStatePreference)pref).setChecked(false); |
| } |
| } |
| } |
| if (autoSelectionEnabled) { |
| InputMethodAndSubtypeUtil.saveInputMethodSubtypeList( |
| this, getContentResolver(), mInputMethodInfoList, mHaveHardKeyboard); |
| updateImplicitlyEnabledSubtypes(imiId, true /* check */); |
| } |
| } |
| |
| private void updateImplicitlyEnabledSubtypes(final String targetImiId, final boolean check) { |
| // When targetImiId is null, apply to all subtypes of all IMEs |
| for (final InputMethodInfo imi : mInputMethodInfoList) { |
| final String imiId = imi.getId(); |
| final TwoStatePreference autoSelectionPref = mAutoSelectionPrefsMap.get(imiId); |
| // No need to update implicitly enabled subtypes when the user has unchecked the |
| // "subtype auto selection". |
| if (autoSelectionPref == null || !autoSelectionPref.isChecked()) { |
| continue; |
| } |
| if (imiId.equals(targetImiId) || targetImiId == null) { |
| updateImplicitlyEnabledSubtypesOf(imi, check); |
| } |
| } |
| } |
| |
| private void updateImplicitlyEnabledSubtypesOf(final InputMethodInfo imi, final boolean check) { |
| final String imiId = imi.getId(); |
| final List<Preference> subtypePrefs = mInputMethodAndSubtypePrefsMap.get(imiId); |
| final List<InputMethodSubtype> implicitlyEnabledSubtypes = |
| mImm.getEnabledInputMethodSubtypeList(imi, true); |
| if (subtypePrefs == null || implicitlyEnabledSubtypes == null) { |
| return; |
| } |
| for (final Preference pref : subtypePrefs) { |
| if (!(pref instanceof TwoStatePreference)) { |
| continue; |
| } |
| final TwoStatePreference subtypePref = (TwoStatePreference)pref; |
| subtypePref.setChecked(false); |
| if (check) { |
| for (final InputMethodSubtype subtype : implicitlyEnabledSubtypes) { |
| final String implicitlyEnabledSubtypePrefKey = imiId + subtype.hashCode(); |
| if (subtypePref.getKey().equals(implicitlyEnabledSubtypePrefKey)) { |
| subtypePref.setChecked(true); |
| break; |
| } |
| } |
| } |
| } |
| } |
| |
| private void updateAutoSelectionPreferences() { |
| for (final String imiId : mInputMethodAndSubtypePrefsMap.keySet()) { |
| setAutoSelectionSubtypesEnabled(imiId, isNoSubtypesExplicitlySelected(imiId)); |
| } |
| updateImplicitlyEnabledSubtypes(null /* targetImiId */, true /* check */); |
| } |
| } |