| /* |
| * Copyright (C) 2017 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.settingslib.inputmethod; |
| |
| import android.content.Context; |
| import android.content.pm.PackageManager; |
| import android.content.res.Configuration; |
| import androidx.preference.PreferenceFragment; |
| import androidx.preference.Preference; |
| import androidx.preference.PreferenceCategory; |
| import androidx.preference.PreferenceScreen; |
| import androidx.preference.TwoStatePreference; |
| import android.text.TextUtils; |
| import android.view.inputmethod.InputMethodInfo; |
| import android.view.inputmethod.InputMethodManager; |
| import android.view.inputmethod.InputMethodSubtype; |
| |
| import com.android.settingslib.R; |
| |
| import java.text.Collator; |
| import java.util.ArrayList; |
| import java.util.HashMap; |
| import java.util.List; |
| |
| public class InputMethodAndSubtypeEnablerManager implements Preference.OnPreferenceChangeListener { |
| |
| private final PreferenceFragment mFragment; |
| |
| 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 final Collator mCollator = Collator.getInstance(); |
| |
| public InputMethodAndSubtypeEnablerManager(PreferenceFragment fragment) { |
| mFragment = fragment; |
| mImm = fragment.getContext().getSystemService(InputMethodManager.class); |
| |
| mInputMethodInfoList = mImm.getInputMethodList(); |
| } |
| |
| public void init(PreferenceFragment fragment, String targetImi, PreferenceScreen root) { |
| final Configuration config = fragment.getResources().getConfiguration(); |
| mHaveHardKeyboard = (config.keyboard == Configuration.KEYBOARD_QWERTY); |
| |
| for (final InputMethodInfo imi : mInputMethodInfoList) { |
| // Add subtype preferences of this IME when it is specified or no IME is specified. |
| if (imi.getId().equals(targetImi) || TextUtils.isEmpty(targetImi)) { |
| addInputMethodSubtypePreferences(fragment, imi, root); |
| } |
| } |
| } |
| |
| public void refresh(Context context, PreferenceFragment fragment) { |
| // Refresh internal states in mInputMethodSettingValues to keep the latest |
| // "InputMethodInfo"s and "InputMethodSubtype"s |
| InputMethodSettingValuesWrapper |
| .getInstance(context).refreshAllInputMethodAndSubtypes(); |
| InputMethodAndSubtypeUtil.loadInputMethodSubtypeList(fragment, context.getContentResolver(), |
| mInputMethodInfoList, mInputMethodAndSubtypePrefsMap); |
| updateAutoSelectionPreferences(); |
| } |
| |
| public void save(Context context, PreferenceFragment fragment) { |
| InputMethodAndSubtypeUtil.saveInputMethodSubtypeList(fragment, context.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(PreferenceFragment fragment, InputMethodInfo imi, |
| final PreferenceScreen root) { |
| Context prefContext = fragment.getPreferenceManager().getContext(); |
| |
| final int subtypeCount = imi.getSubtypeCount(); |
| if (subtypeCount <= 1) { |
| return; |
| } |
| final String imiId = imi.getId(); |
| final PreferenceCategory keyboardSettingsCategory = |
| new PreferenceCategory(prefContext); |
| root.addPreference(keyboardSettingsCategory); |
| final PackageManager pm = prefContext.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(prefContext); |
| mAutoSelectionPrefsMap.put(imiId, autoSelectionPref); |
| keyboardSettingsCategory.addPreference(autoSelectionPref); |
| autoSelectionPref.setOnPreferenceChangeListener(this); |
| |
| final PreferenceCategory activeInputMethodsCategory = |
| new PreferenceCategory(prefContext); |
| 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, prefContext, imi); |
| } |
| } else { |
| final Preference subtypePref = new InputMethodSubtypePreference( |
| prefContext, subtype, imi); |
| subtypePreferences.add(subtypePref); |
| } |
| } |
| subtypePreferences.sort((lhs, rhs) -> { |
| if (lhs instanceof InputMethodSubtypePreference) { |
| return ((InputMethodSubtypePreference) lhs).compareTo(rhs, mCollator); |
| } |
| return lhs.compareTo(rhs); |
| }); |
| for (final Preference pref : subtypePreferences) { |
| 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( |
| mFragment, mFragment.getContext().getContentResolver(), |
| mInputMethodInfoList, mHaveHardKeyboard); |
| updateImplicitlyEnabledSubtypes(imiId); |
| } |
| } |
| |
| private void updateImplicitlyEnabledSubtypes(final String targetImiId) { |
| // 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); |
| } |
| } |
| } |
| |
| private void updateImplicitlyEnabledSubtypesOf(final InputMethodInfo imi) { |
| 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); |
| 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 */ /* check */); |
| } |
| } |