19cd11a9aa5ac74ca89432655d019f68d789bc405satok/*
29cd11a9aa5ac74ca89432655d019f68d789bc405satok * Copyright (C) 2010 The Android Open Source Project
39cd11a9aa5ac74ca89432655d019f68d789bc405satok *
49cd11a9aa5ac74ca89432655d019f68d789bc405satok * Licensed under the Apache License, Version 2.0 (the "License");
59cd11a9aa5ac74ca89432655d019f68d789bc405satok * you may not use this file except in compliance with the License.
69cd11a9aa5ac74ca89432655d019f68d789bc405satok * You may obtain a copy of the License at
79cd11a9aa5ac74ca89432655d019f68d789bc405satok *
89cd11a9aa5ac74ca89432655d019f68d789bc405satok *      http://www.apache.org/licenses/LICENSE-2.0
99cd11a9aa5ac74ca89432655d019f68d789bc405satok *
109cd11a9aa5ac74ca89432655d019f68d789bc405satok * Unless required by applicable law or agreed to in writing, software
119cd11a9aa5ac74ca89432655d019f68d789bc405satok * distributed under the License is distributed on an "AS IS" BASIS,
129cd11a9aa5ac74ca89432655d019f68d789bc405satok * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
139cd11a9aa5ac74ca89432655d019f68d789bc405satok * See the License for the specific language governing permissions and
149cd11a9aa5ac74ca89432655d019f68d789bc405satok * limitations under the License.
159cd11a9aa5ac74ca89432655d019f68d789bc405satok */
169cd11a9aa5ac74ca89432655d019f68d789bc405satok
170417e4094713c5f4dac700b645000d0959bf62fasatokpackage com.android.settings.inputmethod;
189cd11a9aa5ac74ca89432655d019f68d789bc405satok
1991c23ca03f9ffa9ac9f2644e50e51e481a423662Yohei Yukawaimport android.annotation.NonNull;
2091c23ca03f9ffa9ac9f2644e50e51e481a423662Yohei Yukawaimport android.annotation.Nullable;
210417e4094713c5f4dac700b645000d0959bf62fasatokimport android.content.ContentResolver;
2291c23ca03f9ffa9ac9f2644e50e51e481a423662Yohei Yukawaimport android.content.Context;
2374e7c3e3601f4808854cf12bffe11ebf4c6ea8abTadashi G. Takaokaimport android.content.SharedPreferences;
2491c23ca03f9ffa9ac9f2644e50e51e481a423662Yohei Yukawaimport android.content.res.Configuration;
2591c23ca03f9ffa9ac9f2644e50e51e481a423662Yohei Yukawaimport android.icu.text.ListFormatter;
269cd11a9aa5ac74ca89432655d019f68d789bc405satokimport android.provider.Settings;
273de7021756efd7fc9082b8768bc9a38fa310e7d8satokimport android.provider.Settings.SettingNotFoundException;
2839b467482d1bf256a111c757e9b7621c6f523271Jason Monkimport android.support.v7.preference.Preference;
2939b467482d1bf256a111c757e9b7621c6f523271Jason Monkimport android.support.v7.preference.PreferenceScreen;
3039b467482d1bf256a111c757e9b7621c6f523271Jason Monkimport android.support.v7.preference.TwoStatePreference;
319cd11a9aa5ac74ca89432655d019f68d789bc405satokimport android.text.TextUtils;
323de7021756efd7fc9082b8768bc9a38fa310e7d8satokimport android.util.Log;
339cd11a9aa5ac74ca89432655d019f68d789bc405satokimport android.view.inputmethod.InputMethodInfo;
349cd11a9aa5ac74ca89432655d019f68d789bc405satokimport android.view.inputmethod.InputMethodSubtype;
359cd11a9aa5ac74ca89432655d019f68d789bc405satok
3691c23ca03f9ffa9ac9f2644e50e51e481a423662Yohei Yukawaimport com.android.internal.app.LocaleHelper;
3774e7c3e3601f4808854cf12bffe11ebf4c6ea8abTadashi G. Takaokaimport com.android.internal.inputmethod.InputMethodUtils;
3874e7c3e3601f4808854cf12bffe11ebf4c6ea8abTadashi G. Takaokaimport com.android.settings.SettingsPreferenceFragment;
3974e7c3e3601f4808854cf12bffe11ebf4c6ea8abTadashi G. Takaoka
403de7021756efd7fc9082b8768bc9a38fa310e7d8satokimport java.util.HashMap;
419cd11a9aa5ac74ca89432655d019f68d789bc405satokimport java.util.HashSet;
429cd11a9aa5ac74ca89432655d019f68d789bc405satokimport java.util.List;
4391c23ca03f9ffa9ac9f2644e50e51e481a423662Yohei Yukawaimport java.util.Locale;
44c88a7ff1efd10374974e45768bde1658cc1d8483satokimport java.util.Map;
459cd11a9aa5ac74ca89432655d019f68d789bc405satok
46d2dd7d33c9206f7aa29abbff48119d0f86ddfc36Tadashi G. Takaoka// TODO: Consolidate this with {@link InputMethodSettingValuesWrapper}.
47d2dd7d33c9206f7aa29abbff48119d0f86ddfc36Tadashi G. Takaokaclass InputMethodAndSubtypeUtil {
489cd11a9aa5ac74ca89432655d019f68d789bc405satok
493de7021756efd7fc9082b8768bc9a38fa310e7d8satok    private static final boolean DEBUG = false;
503de7021756efd7fc9082b8768bc9a38fa310e7d8satok    static final String TAG = "InputMethdAndSubtypeUtil";
513de7021756efd7fc9082b8768bc9a38fa310e7d8satok
523de7021756efd7fc9082b8768bc9a38fa310e7d8satok    private static final char INPUT_METHOD_SEPARATER = ':';
533de7021756efd7fc9082b8768bc9a38fa310e7d8satok    private static final char INPUT_METHOD_SUBTYPE_SEPARATER = ';';
543de7021756efd7fc9082b8768bc9a38fa310e7d8satok    private static final int NOT_A_SUBTYPE_ID = -1;
553de7021756efd7fc9082b8768bc9a38fa310e7d8satok
563de7021756efd7fc9082b8768bc9a38fa310e7d8satok    private static final TextUtils.SimpleStringSplitter sStringInputMethodSplitter
573de7021756efd7fc9082b8768bc9a38fa310e7d8satok            = new TextUtils.SimpleStringSplitter(INPUT_METHOD_SEPARATER);
583de7021756efd7fc9082b8768bc9a38fa310e7d8satok
593de7021756efd7fc9082b8768bc9a38fa310e7d8satok    private static final TextUtils.SimpleStringSplitter sStringInputMethodSubtypeSplitter
603de7021756efd7fc9082b8768bc9a38fa310e7d8satok            = new TextUtils.SimpleStringSplitter(INPUT_METHOD_SUBTYPE_SEPARATER);
613de7021756efd7fc9082b8768bc9a38fa310e7d8satok
629a8b4f239f219bb2cd2d08470b8c858fd74341e7Tadashi G. Takaoka    // InputMethods and subtypes are saved in the settings as follows:
639a8b4f239f219bb2cd2d08470b8c858fd74341e7Tadashi G. Takaoka    // ime0;subtype0;subtype1:ime1;subtype0:ime2:ime3;subtype0;subtype1
649a8b4f239f219bb2cd2d08470b8c858fd74341e7Tadashi G. Takaoka    static String buildInputMethodsAndSubtypesString(
659a8b4f239f219bb2cd2d08470b8c858fd74341e7Tadashi G. Takaoka            final HashMap<String, HashSet<String>> imeToSubtypesMap) {
669a8b4f239f219bb2cd2d08470b8c858fd74341e7Tadashi G. Takaoka        final StringBuilder builder = new StringBuilder();
679a8b4f239f219bb2cd2d08470b8c858fd74341e7Tadashi G. Takaoka        for (final String imi : imeToSubtypesMap.keySet()) {
689a8b4f239f219bb2cd2d08470b8c858fd74341e7Tadashi G. Takaoka            if (builder.length() > 0) {
69c88a7ff1efd10374974e45768bde1658cc1d8483satok                builder.append(INPUT_METHOD_SEPARATER);
70c88a7ff1efd10374974e45768bde1658cc1d8483satok            }
719a8b4f239f219bb2cd2d08470b8c858fd74341e7Tadashi G. Takaoka            final HashSet<String> subtypeIdSet = imeToSubtypesMap.get(imi);
729a8b4f239f219bb2cd2d08470b8c858fd74341e7Tadashi G. Takaoka            builder.append(imi);
739a8b4f239f219bb2cd2d08470b8c858fd74341e7Tadashi G. Takaoka            for (final String subtypeId : subtypeIdSet) {
749a8b4f239f219bb2cd2d08470b8c858fd74341e7Tadashi G. Takaoka                builder.append(INPUT_METHOD_SUBTYPE_SEPARATER).append(subtypeId);
759a8b4f239f219bb2cd2d08470b8c858fd74341e7Tadashi G. Takaoka            }
76c88a7ff1efd10374974e45768bde1658cc1d8483satok        }
779a8b4f239f219bb2cd2d08470b8c858fd74341e7Tadashi G. Takaoka        return builder.toString();
78c88a7ff1efd10374974e45768bde1658cc1d8483satok    }
79c88a7ff1efd10374974e45768bde1658cc1d8483satok
809a8b4f239f219bb2cd2d08470b8c858fd74341e7Tadashi G. Takaoka    private static String buildInputMethodsString(final HashSet<String> imiList) {
819a8b4f239f219bb2cd2d08470b8c858fd74341e7Tadashi G. Takaoka        final StringBuilder builder = new StringBuilder();
829a8b4f239f219bb2cd2d08470b8c858fd74341e7Tadashi G. Takaoka        for (final String imi : imiList) {
839a8b4f239f219bb2cd2d08470b8c858fd74341e7Tadashi G. Takaoka            if (builder.length() > 0) {
84c88a7ff1efd10374974e45768bde1658cc1d8483satok                builder.append(INPUT_METHOD_SEPARATER);
85c88a7ff1efd10374974e45768bde1658cc1d8483satok            }
869a8b4f239f219bb2cd2d08470b8c858fd74341e7Tadashi G. Takaoka            builder.append(imi);
87c88a7ff1efd10374974e45768bde1658cc1d8483satok        }
889a8b4f239f219bb2cd2d08470b8c858fd74341e7Tadashi G. Takaoka        return builder.toString();
89c88a7ff1efd10374974e45768bde1658cc1d8483satok    }
90c88a7ff1efd10374974e45768bde1658cc1d8483satok
9123bdc188438e28958c97ac252f59f5beefb38238satok    private static int getInputMethodSubtypeSelected(ContentResolver resolver) {
923de7021756efd7fc9082b8768bc9a38fa310e7d8satok        try {
933de7021756efd7fc9082b8768bc9a38fa310e7d8satok            return Settings.Secure.getInt(resolver,
9423bdc188438e28958c97ac252f59f5beefb38238satok                    Settings.Secure.SELECTED_INPUT_METHOD_SUBTYPE);
953de7021756efd7fc9082b8768bc9a38fa310e7d8satok        } catch (SettingNotFoundException e) {
9623bdc188438e28958c97ac252f59f5beefb38238satok            return NOT_A_SUBTYPE_ID;
973de7021756efd7fc9082b8768bc9a38fa310e7d8satok        }
983de7021756efd7fc9082b8768bc9a38fa310e7d8satok    }
993de7021756efd7fc9082b8768bc9a38fa310e7d8satok
10023bdc188438e28958c97ac252f59f5beefb38238satok    private static boolean isInputMethodSubtypeSelected(ContentResolver resolver) {
10123bdc188438e28958c97ac252f59f5beefb38238satok        return getInputMethodSubtypeSelected(resolver) != NOT_A_SUBTYPE_ID;
10223bdc188438e28958c97ac252f59f5beefb38238satok    }
10323bdc188438e28958c97ac252f59f5beefb38238satok
1043de7021756efd7fc9082b8768bc9a38fa310e7d8satok    private static void putSelectedInputMethodSubtype(ContentResolver resolver, int hashCode) {
1053de7021756efd7fc9082b8768bc9a38fa310e7d8satok        Settings.Secure.putInt(resolver, Settings.Secure.SELECTED_INPUT_METHOD_SUBTYPE, hashCode);
1063de7021756efd7fc9082b8768bc9a38fa310e7d8satok    }
1073de7021756efd7fc9082b8768bc9a38fa310e7d8satok
1083de7021756efd7fc9082b8768bc9a38fa310e7d8satok    // Needs to modify InputMethodManageService if you want to change the format of saved string.
1093de7021756efd7fc9082b8768bc9a38fa310e7d8satok    private static HashMap<String, HashSet<String>> getEnabledInputMethodsAndSubtypeList(
1103de7021756efd7fc9082b8768bc9a38fa310e7d8satok            ContentResolver resolver) {
1113de7021756efd7fc9082b8768bc9a38fa310e7d8satok        final String enabledInputMethodsStr = Settings.Secure.getString(
1123de7021756efd7fc9082b8768bc9a38fa310e7d8satok                resolver, Settings.Secure.ENABLED_INPUT_METHODS);
1133de7021756efd7fc9082b8768bc9a38fa310e7d8satok        if (DEBUG) {
1143de7021756efd7fc9082b8768bc9a38fa310e7d8satok            Log.d(TAG, "--- Load enabled input methods: " + enabledInputMethodsStr);
1153de7021756efd7fc9082b8768bc9a38fa310e7d8satok        }
1169a8b4f239f219bb2cd2d08470b8c858fd74341e7Tadashi G. Takaoka        return parseInputMethodsAndSubtypesString(enabledInputMethodsStr);
1179a8b4f239f219bb2cd2d08470b8c858fd74341e7Tadashi G. Takaoka    }
1183de7021756efd7fc9082b8768bc9a38fa310e7d8satok
1199a8b4f239f219bb2cd2d08470b8c858fd74341e7Tadashi G. Takaoka    static HashMap<String, HashSet<String>> parseInputMethodsAndSubtypesString(
1209a8b4f239f219bb2cd2d08470b8c858fd74341e7Tadashi G. Takaoka            final String inputMethodsAndSubtypesString) {
1219a8b4f239f219bb2cd2d08470b8c858fd74341e7Tadashi G. Takaoka        final HashMap<String, HashSet<String>> subtypesMap = new HashMap<>();
1229a8b4f239f219bb2cd2d08470b8c858fd74341e7Tadashi G. Takaoka        if (TextUtils.isEmpty(inputMethodsAndSubtypesString)) {
1239a8b4f239f219bb2cd2d08470b8c858fd74341e7Tadashi G. Takaoka            return subtypesMap;
1243de7021756efd7fc9082b8768bc9a38fa310e7d8satok        }
1259a8b4f239f219bb2cd2d08470b8c858fd74341e7Tadashi G. Takaoka        sStringInputMethodSplitter.setString(inputMethodsAndSubtypesString);
1263de7021756efd7fc9082b8768bc9a38fa310e7d8satok        while (sStringInputMethodSplitter.hasNext()) {
1279a8b4f239f219bb2cd2d08470b8c858fd74341e7Tadashi G. Takaoka            final String nextImsStr = sStringInputMethodSplitter.next();
1283de7021756efd7fc9082b8768bc9a38fa310e7d8satok            sStringInputMethodSubtypeSplitter.setString(nextImsStr);
1293de7021756efd7fc9082b8768bc9a38fa310e7d8satok            if (sStringInputMethodSubtypeSplitter.hasNext()) {
1309a8b4f239f219bb2cd2d08470b8c858fd74341e7Tadashi G. Takaoka                final HashSet<String> subtypeIdSet = new HashSet<>();
1319a8b4f239f219bb2cd2d08470b8c858fd74341e7Tadashi G. Takaoka                // The first element is {@link InputMethodInfoId}.
1329a8b4f239f219bb2cd2d08470b8c858fd74341e7Tadashi G. Takaoka                final String imiId = sStringInputMethodSubtypeSplitter.next();
1333de7021756efd7fc9082b8768bc9a38fa310e7d8satok                while (sStringInputMethodSubtypeSplitter.hasNext()) {
1349a8b4f239f219bb2cd2d08470b8c858fd74341e7Tadashi G. Takaoka                    subtypeIdSet.add(sStringInputMethodSubtypeSplitter.next());
1353de7021756efd7fc9082b8768bc9a38fa310e7d8satok                }
1369a8b4f239f219bb2cd2d08470b8c858fd74341e7Tadashi G. Takaoka                subtypesMap.put(imiId, subtypeIdSet);
1373de7021756efd7fc9082b8768bc9a38fa310e7d8satok            }
1383de7021756efd7fc9082b8768bc9a38fa310e7d8satok        }
1399a8b4f239f219bb2cd2d08470b8c858fd74341e7Tadashi G. Takaoka        return subtypesMap;
1409a8b4f239f219bb2cd2d08470b8c858fd74341e7Tadashi G. Takaoka    }
1419a8b4f239f219bb2cd2d08470b8c858fd74341e7Tadashi G. Takaoka
1429a8b4f239f219bb2cd2d08470b8c858fd74341e7Tadashi G. Takaoka    static void enableInputMethodSubtypesOf(final ContentResolver resolver, final String imiId,
1439a8b4f239f219bb2cd2d08470b8c858fd74341e7Tadashi G. Takaoka            final HashSet<String> enabledSubtypeIdSet) {
1449a8b4f239f219bb2cd2d08470b8c858fd74341e7Tadashi G. Takaoka        final HashMap<String, HashSet<String>> enabledImeAndSubtypeIdsMap =
1459a8b4f239f219bb2cd2d08470b8c858fd74341e7Tadashi G. Takaoka                getEnabledInputMethodsAndSubtypeList(resolver);
1469a8b4f239f219bb2cd2d08470b8c858fd74341e7Tadashi G. Takaoka        enabledImeAndSubtypeIdsMap.put(imiId, enabledSubtypeIdSet);
1479a8b4f239f219bb2cd2d08470b8c858fd74341e7Tadashi G. Takaoka        final String enabledImesAndSubtypesString = buildInputMethodsAndSubtypesString(
1489a8b4f239f219bb2cd2d08470b8c858fd74341e7Tadashi G. Takaoka                enabledImeAndSubtypeIdsMap);
1499a8b4f239f219bb2cd2d08470b8c858fd74341e7Tadashi G. Takaoka        Settings.Secure.putString(resolver,
1509a8b4f239f219bb2cd2d08470b8c858fd74341e7Tadashi G. Takaoka                Settings.Secure.ENABLED_INPUT_METHODS, enabledImesAndSubtypesString);
1513de7021756efd7fc9082b8768bc9a38fa310e7d8satok    }
1529cd11a9aa5ac74ca89432655d019f68d789bc405satok
153c88a7ff1efd10374974e45768bde1658cc1d8483satok    private static HashSet<String> getDisabledSystemIMEs(ContentResolver resolver) {
154d2dd7d33c9206f7aa29abbff48119d0f86ddfc36Tadashi G. Takaoka        HashSet<String> set = new HashSet<>();
155c88a7ff1efd10374974e45768bde1658cc1d8483satok        String disabledIMEsStr = Settings.Secure.getString(
156c88a7ff1efd10374974e45768bde1658cc1d8483satok                resolver, Settings.Secure.DISABLED_SYSTEM_INPUT_METHODS);
15710d2e1bc2f43fa3f39ea819e2eaf14c713c91a27satok        if (TextUtils.isEmpty(disabledIMEsStr)) {
15810d2e1bc2f43fa3f39ea819e2eaf14c713c91a27satok            return set;
15910d2e1bc2f43fa3f39ea819e2eaf14c713c91a27satok        }
160c88a7ff1efd10374974e45768bde1658cc1d8483satok        sStringInputMethodSplitter.setString(disabledIMEsStr);
161c88a7ff1efd10374974e45768bde1658cc1d8483satok        while(sStringInputMethodSplitter.hasNext()) {
162c88a7ff1efd10374974e45768bde1658cc1d8483satok            set.add(sStringInputMethodSplitter.next());
163c88a7ff1efd10374974e45768bde1658cc1d8483satok        }
164c88a7ff1efd10374974e45768bde1658cc1d8483satok        return set;
165c88a7ff1efd10374974e45768bde1658cc1d8483satok    }
166c88a7ff1efd10374974e45768bde1658cc1d8483satok
167d2dd7d33c9206f7aa29abbff48119d0f86ddfc36Tadashi G. Takaoka    static void saveInputMethodSubtypeList(SettingsPreferenceFragment context,
168c88a7ff1efd10374974e45768bde1658cc1d8483satok            ContentResolver resolver, List<InputMethodInfo> inputMethodInfos,
169c88a7ff1efd10374974e45768bde1658cc1d8483satok            boolean hasHardKeyboard) {
1703de7021756efd7fc9082b8768bc9a38fa310e7d8satok        String currentInputMethodId = Settings.Secure.getString(resolver,
1719cd11a9aa5ac74ca89432655d019f68d789bc405satok                Settings.Secure.DEFAULT_INPUT_METHOD);
17223bdc188438e28958c97ac252f59f5beefb38238satok        final int selectedInputMethodSubtype = getInputMethodSubtypeSelected(resolver);
1739a8b4f239f219bb2cd2d08470b8c858fd74341e7Tadashi G. Takaoka        final HashMap<String, HashSet<String>> enabledIMEsAndSubtypesMap =
174c88a7ff1efd10374974e45768bde1658cc1d8483satok                getEnabledInputMethodsAndSubtypeList(resolver);
1759a8b4f239f219bb2cd2d08470b8c858fd74341e7Tadashi G. Takaoka        final HashSet<String> disabledSystemIMEs = getDisabledSystemIMEs(resolver);
1769cd11a9aa5ac74ca89432655d019f68d789bc405satok
177d7973d1c0ae6ad6c676f19d5e0e030152c50784asatok        boolean needsToResetSelectedSubtype = false;
1789a8b4f239f219bb2cd2d08470b8c858fd74341e7Tadashi G. Takaoka        for (final InputMethodInfo imi : inputMethodInfos) {
179c88a7ff1efd10374974e45768bde1658cc1d8483satok            final String imiId = imi.getId();
1809a8b4f239f219bb2cd2d08470b8c858fd74341e7Tadashi G. Takaoka            final Preference pref = context.findPreference(imiId);
1819a8b4f239f219bb2cd2d08470b8c858fd74341e7Tadashi G. Takaoka            if (pref == null) {
1829a8b4f239f219bb2cd2d08470b8c858fd74341e7Tadashi G. Takaoka                continue;
1839a8b4f239f219bb2cd2d08470b8c858fd74341e7Tadashi G. Takaoka            }
18474e7c3e3601f4808854cf12bffe11ebf4c6ea8abTadashi G. Takaoka            // In the choose input method screen or in the subtype enabler screen,
18574e7c3e3601f4808854cf12bffe11ebf4c6ea8abTadashi G. Takaoka            // <code>pref</code> is an instance of TwoStatePreference.
18674e7c3e3601f4808854cf12bffe11ebf4c6ea8abTadashi G. Takaoka            final boolean isImeChecked = (pref instanceof TwoStatePreference) ?
18774e7c3e3601f4808854cf12bffe11ebf4c6ea8abTadashi G. Takaoka                    ((TwoStatePreference) pref).isChecked()
1889a8b4f239f219bb2cd2d08470b8c858fd74341e7Tadashi G. Takaoka                    : enabledIMEsAndSubtypesMap.containsKey(imiId);
1894c5fdcc3f70ac5496b21d7db57f62ee9db4e6a19satok            final boolean isCurrentInputMethod = imiId.equals(currentInputMethodId);
190993f6ecf4065501433271b5fd5daf21a6f3ae586Satoshi Kataoka            final boolean systemIme = InputMethodUtils.isSystemIme(imi);
191993f6ecf4065501433271b5fd5daf21a6f3ae586Satoshi Kataoka            if ((!hasHardKeyboard && InputMethodSettingValuesWrapper.getInstance(
192993f6ecf4065501433271b5fd5daf21a6f3ae586Satoshi Kataoka                    context.getActivity()).isAlwaysCheckedIme(imi, context.getActivity()))
193472c3c93fc61b797b3ee22352a876ccae3eeb153satok                    || isImeChecked) {
1949a8b4f239f219bb2cd2d08470b8c858fd74341e7Tadashi G. Takaoka                if (!enabledIMEsAndSubtypesMap.containsKey(imiId)) {
195c88a7ff1efd10374974e45768bde1658cc1d8483satok                    // imiId has just been enabled
1969a8b4f239f219bb2cd2d08470b8c858fd74341e7Tadashi G. Takaoka                    enabledIMEsAndSubtypesMap.put(imiId, new HashSet<String>());
197c88a7ff1efd10374974e45768bde1658cc1d8483satok                }
1989a8b4f239f219bb2cd2d08470b8c858fd74341e7Tadashi G. Takaoka                final HashSet<String> subtypesSet = enabledIMEsAndSubtypesMap.get(imiId);
199517c10dd9cc8f1b52cbe818595fe124e9c0af813satok
200d7973d1c0ae6ad6c676f19d5e0e030152c50784asatok                boolean subtypePrefFound = false;
2015a813ba7f99f1c797ead4a58fdf05f91296158a4Ken Wakasa                final int subtypeCount = imi.getSubtypeCount();
2025a813ba7f99f1c797ead4a58fdf05f91296158a4Ken Wakasa                for (int i = 0; i < subtypeCount; ++i) {
2039a8b4f239f219bb2cd2d08470b8c858fd74341e7Tadashi G. Takaoka                    final InputMethodSubtype subtype = imi.getSubtypeAt(i);
204c88a7ff1efd10374974e45768bde1658cc1d8483satok                    final String subtypeHashCodeStr = String.valueOf(subtype.hashCode());
2051f53937469095f268d4c7a21d93f821a341a151dTadashi G. Takaoka                    final TwoStatePreference subtypePref = (TwoStatePreference) context
2061f53937469095f268d4c7a21d93f821a341a151dTadashi G. Takaoka                            .findPreference(imiId + subtypeHashCodeStr);
207c88a7ff1efd10374974e45768bde1658cc1d8483satok                    // In the Configure input method screen which does not have subtype preferences.
2089a8b4f239f219bb2cd2d08470b8c858fd74341e7Tadashi G. Takaoka                    if (subtypePref == null) {
2099a8b4f239f219bb2cd2d08470b8c858fd74341e7Tadashi G. Takaoka                        continue;
2109a8b4f239f219bb2cd2d08470b8c858fd74341e7Tadashi G. Takaoka                    }
211d7973d1c0ae6ad6c676f19d5e0e030152c50784asatok                    if (!subtypePrefFound) {
2121f53937469095f268d4c7a21d93f821a341a151dTadashi G. Takaoka                        // Once subtype preference is found, subtypeSet needs to be cleared.
213d7973d1c0ae6ad6c676f19d5e0e030152c50784asatok                        // Because of system change, hashCode value could have been changed.
214517c10dd9cc8f1b52cbe818595fe124e9c0af813satok                        subtypesSet.clear();
215d7973d1c0ae6ad6c676f19d5e0e030152c50784asatok                        // If selected subtype preference is disabled, needs to reset.
216d7973d1c0ae6ad6c676f19d5e0e030152c50784asatok                        needsToResetSelectedSubtype = true;
217d7973d1c0ae6ad6c676f19d5e0e030152c50784asatok                        subtypePrefFound = true;
218517c10dd9cc8f1b52cbe818595fe124e9c0af813satok                    }
2196452b84763f864139a3744bd3406f72a0014301aYohei Yukawa                    // Checking <code>subtypePref.isEnabled()</code> is insufficient to determine
2206452b84763f864139a3744bd3406f72a0014301aYohei Yukawa                    // whether the user manually enabled this subtype or not.  Implicitly-enabled
2216452b84763f864139a3744bd3406f72a0014301aYohei Yukawa                    // subtypes are also checked just as an indicator to users.  We also need to
2226452b84763f864139a3744bd3406f72a0014301aYohei Yukawa                    // check <code>subtypePref.isEnabled()</code> so that only manually enabled
2236452b84763f864139a3744bd3406f72a0014301aYohei Yukawa                    // subtypes can be saved here.
2246452b84763f864139a3744bd3406f72a0014301aYohei Yukawa                    if (subtypePref.isEnabled() && subtypePref.isChecked()) {
225c88a7ff1efd10374974e45768bde1658cc1d8483satok                        subtypesSet.add(subtypeHashCodeStr);
22623bdc188438e28958c97ac252f59f5beefb38238satok                        if (isCurrentInputMethod) {
22723bdc188438e28958c97ac252f59f5beefb38238satok                            if (selectedInputMethodSubtype == subtype.hashCode()) {
228d7973d1c0ae6ad6c676f19d5e0e030152c50784asatok                                // Selected subtype is still enabled, there is no need to reset
229d7973d1c0ae6ad6c676f19d5e0e030152c50784asatok                                // selected subtype.
230d7973d1c0ae6ad6c676f19d5e0e030152c50784asatok                                needsToResetSelectedSubtype = false;
23123bdc188438e28958c97ac252f59f5beefb38238satok                            }
2323de7021756efd7fc9082b8768bc9a38fa310e7d8satok                        }
233c88a7ff1efd10374974e45768bde1658cc1d8483satok                    } else {
234c88a7ff1efd10374974e45768bde1658cc1d8483satok                        subtypesSet.remove(subtypeHashCodeStr);
2353de7021756efd7fc9082b8768bc9a38fa310e7d8satok                    }
2363de7021756efd7fc9082b8768bc9a38fa310e7d8satok                }
237c88a7ff1efd10374974e45768bde1658cc1d8483satok            } else {
2389a8b4f239f219bb2cd2d08470b8c858fd74341e7Tadashi G. Takaoka                enabledIMEsAndSubtypesMap.remove(imiId);
239c88a7ff1efd10374974e45768bde1658cc1d8483satok                if (isCurrentInputMethod) {
240c88a7ff1efd10374974e45768bde1658cc1d8483satok                    // We are processing the current input method, but found that it's not enabled.
241c88a7ff1efd10374974e45768bde1658cc1d8483satok                    // This means that the current input method has been uninstalled.
242c88a7ff1efd10374974e45768bde1658cc1d8483satok                    // If currentInputMethod is already uninstalled, InputMethodManagerService will
243c88a7ff1efd10374974e45768bde1658cc1d8483satok                    // find the applicable IME from the history and the system locale.
244c88a7ff1efd10374974e45768bde1658cc1d8483satok                    if (DEBUG) {
245c88a7ff1efd10374974e45768bde1658cc1d8483satok                        Log.d(TAG, "Current IME was uninstalled or disabled.");
246c88a7ff1efd10374974e45768bde1658cc1d8483satok                    }
247062c3c3c3d5890e85bff61ca067860641aa8f8f8satok                    currentInputMethodId = null;
2485fd39cafe1de792300e9e3dd60258a1ce5079f73satok                }
2499cd11a9aa5ac74ca89432655d019f68d789bc405satok            }
2509cd11a9aa5ac74ca89432655d019f68d789bc405satok            // If it's a disabled system ime, add it to the disabled list so that it
2519cd11a9aa5ac74ca89432655d019f68d789bc405satok            // doesn't get enabled automatically on any changes to the package list
252c88a7ff1efd10374974e45768bde1658cc1d8483satok            if (systemIme && hasHardKeyboard) {
253c88a7ff1efd10374974e45768bde1658cc1d8483satok                if (disabledSystemIMEs.contains(imiId)) {
254c88a7ff1efd10374974e45768bde1658cc1d8483satok                    if (isImeChecked) {
255c88a7ff1efd10374974e45768bde1658cc1d8483satok                        disabledSystemIMEs.remove(imiId);
256c88a7ff1efd10374974e45768bde1658cc1d8483satok                    }
257c88a7ff1efd10374974e45768bde1658cc1d8483satok                } else {
258c88a7ff1efd10374974e45768bde1658cc1d8483satok                    if (!isImeChecked) {
259c88a7ff1efd10374974e45768bde1658cc1d8483satok                        disabledSystemIMEs.add(imiId);
260c88a7ff1efd10374974e45768bde1658cc1d8483satok                    }
261c88a7ff1efd10374974e45768bde1658cc1d8483satok                }
2629cd11a9aa5ac74ca89432655d019f68d789bc405satok            }
2639cd11a9aa5ac74ca89432655d019f68d789bc405satok        }
2649cd11a9aa5ac74ca89432655d019f68d789bc405satok
2659a8b4f239f219bb2cd2d08470b8c858fd74341e7Tadashi G. Takaoka        final String enabledIMEsAndSubtypesString = buildInputMethodsAndSubtypesString(
2669a8b4f239f219bb2cd2d08470b8c858fd74341e7Tadashi G. Takaoka                enabledIMEsAndSubtypesMap);
2679a8b4f239f219bb2cd2d08470b8c858fd74341e7Tadashi G. Takaoka        final String disabledSystemIMEsString = buildInputMethodsString(disabledSystemIMEs);
2683de7021756efd7fc9082b8768bc9a38fa310e7d8satok        if (DEBUG) {
2699a8b4f239f219bb2cd2d08470b8c858fd74341e7Tadashi G. Takaoka            Log.d(TAG, "--- Save enabled inputmethod settings. :" + enabledIMEsAndSubtypesString);
2709a8b4f239f219bb2cd2d08470b8c858fd74341e7Tadashi G. Takaoka            Log.d(TAG, "--- Save disabled system inputmethod settings. :"
2719a8b4f239f219bb2cd2d08470b8c858fd74341e7Tadashi G. Takaoka                    + disabledSystemIMEsString);
2723de7021756efd7fc9082b8768bc9a38fa310e7d8satok            Log.d(TAG, "--- Save default inputmethod settings. :" + currentInputMethodId);
273d7973d1c0ae6ad6c676f19d5e0e030152c50784asatok            Log.d(TAG, "--- Needs to reset the selected subtype :" + needsToResetSelectedSubtype);
274d7973d1c0ae6ad6c676f19d5e0e030152c50784asatok            Log.d(TAG, "--- Subtype is selected :" + isInputMethodSubtypeSelected(resolver));
2753de7021756efd7fc9082b8768bc9a38fa310e7d8satok        }
2763de7021756efd7fc9082b8768bc9a38fa310e7d8satok
27723bdc188438e28958c97ac252f59f5beefb38238satok        // Redefines SelectedSubtype when all subtypes are unchecked or there is no subtype
27823bdc188438e28958c97ac252f59f5beefb38238satok        // selected. And if the selected subtype of the current input method was disabled,
27923bdc188438e28958c97ac252f59f5beefb38238satok        // We should reset the selected input method's subtype.
280d7973d1c0ae6ad6c676f19d5e0e030152c50784asatok        if (needsToResetSelectedSubtype || !isInputMethodSubtypeSelected(resolver)) {
2813de7021756efd7fc9082b8768bc9a38fa310e7d8satok            if (DEBUG) {
282c88a7ff1efd10374974e45768bde1658cc1d8483satok                Log.d(TAG, "--- Reset inputmethod subtype because it's not defined.");
2833de7021756efd7fc9082b8768bc9a38fa310e7d8satok            }
284c88a7ff1efd10374974e45768bde1658cc1d8483satok            putSelectedInputMethodSubtype(resolver, NOT_A_SUBTYPE_ID);
2853de7021756efd7fc9082b8768bc9a38fa310e7d8satok        }
2863de7021756efd7fc9082b8768bc9a38fa310e7d8satok
2870417e4094713c5f4dac700b645000d0959bf62fasatok        Settings.Secure.putString(resolver,
2889a8b4f239f219bb2cd2d08470b8c858fd74341e7Tadashi G. Takaoka                Settings.Secure.ENABLED_INPUT_METHODS, enabledIMEsAndSubtypesString);
2899a8b4f239f219bb2cd2d08470b8c858fd74341e7Tadashi G. Takaoka        if (disabledSystemIMEsString.length() > 0) {
290c88a7ff1efd10374974e45768bde1658cc1d8483satok            Settings.Secure.putString(resolver, Settings.Secure.DISABLED_SYSTEM_INPUT_METHODS,
2919a8b4f239f219bb2cd2d08470b8c858fd74341e7Tadashi G. Takaoka                    disabledSystemIMEsString);
292c88a7ff1efd10374974e45768bde1658cc1d8483satok        }
2935fd39cafe1de792300e9e3dd60258a1ce5079f73satok        // If the current input method is unset, InputMethodManagerService will find the applicable
2945fd39cafe1de792300e9e3dd60258a1ce5079f73satok        // IME from the history and the system locale.
2950417e4094713c5f4dac700b645000d0959bf62fasatok        Settings.Secure.putString(resolver, Settings.Secure.DEFAULT_INPUT_METHOD,
2963de7021756efd7fc9082b8768bc9a38fa310e7d8satok                currentInputMethodId != null ? currentInputMethodId : "");
2979cd11a9aa5ac74ca89432655d019f68d789bc405satok    }
2989cd11a9aa5ac74ca89432655d019f68d789bc405satok
2993460a2683ca820d8d7d184bc1e98241ea3986a4aTadashi G. Takaoka    static void loadInputMethodSubtypeList(final SettingsPreferenceFragment context,
3003460a2683ca820d8d7d184bc1e98241ea3986a4aTadashi G. Takaoka            final ContentResolver resolver, final List<InputMethodInfo> inputMethodInfos,
301c88a7ff1efd10374974e45768bde1658cc1d8483satok            final Map<String, List<Preference>> inputMethodPrefsMap) {
3023460a2683ca820d8d7d184bc1e98241ea3986a4aTadashi G. Takaoka        final HashMap<String, HashSet<String>> enabledSubtypes =
3034c5fdcc3f70ac5496b21d7db57f62ee9db4e6a19satok                getEnabledInputMethodsAndSubtypeList(resolver);
3049cd11a9aa5ac74ca89432655d019f68d789bc405satok
3053460a2683ca820d8d7d184bc1e98241ea3986a4aTadashi G. Takaoka        for (final InputMethodInfo imi : inputMethodInfos) {
306c88a7ff1efd10374974e45768bde1658cc1d8483satok            final String imiId = imi.getId();
3073460a2683ca820d8d7d184bc1e98241ea3986a4aTadashi G. Takaoka            final Preference pref = context.findPreference(imiId);
3081f53937469095f268d4c7a21d93f821a341a151dTadashi G. Takaoka            if (pref instanceof TwoStatePreference) {
3091f53937469095f268d4c7a21d93f821a341a151dTadashi G. Takaoka                final TwoStatePreference subtypePref = (TwoStatePreference) pref;
3103460a2683ca820d8d7d184bc1e98241ea3986a4aTadashi G. Takaoka                final boolean isEnabled = enabledSubtypes.containsKey(imiId);
3111f53937469095f268d4c7a21d93f821a341a151dTadashi G. Takaoka                subtypePref.setChecked(isEnabled);
312c88a7ff1efd10374974e45768bde1658cc1d8483satok                if (inputMethodPrefsMap != null) {
3133460a2683ca820d8d7d184bc1e98241ea3986a4aTadashi G. Takaoka                    for (final Preference childPref: inputMethodPrefsMap.get(imiId)) {
314c88a7ff1efd10374974e45768bde1658cc1d8483satok                        childPref.setEnabled(isEnabled);
315c88a7ff1efd10374974e45768bde1658cc1d8483satok                    }
316c88a7ff1efd10374974e45768bde1658cc1d8483satok                }
317c88a7ff1efd10374974e45768bde1658cc1d8483satok                setSubtypesPreferenceEnabled(context, inputMethodInfos, imiId, isEnabled);
3189cd11a9aa5ac74ca89432655d019f68d789bc405satok            }
3199cd11a9aa5ac74ca89432655d019f68d789bc405satok        }
3207ea58d31646bcfbcdd9ddd0bfdc685ed7ab3f1d8satok        updateSubtypesPreferenceChecked(context, inputMethodInfos, enabledSubtypes);
3219cd11a9aa5ac74ca89432655d019f68d789bc405satok    }
3229cd11a9aa5ac74ca89432655d019f68d789bc405satok
3233460a2683ca820d8d7d184bc1e98241ea3986a4aTadashi G. Takaoka    static void setSubtypesPreferenceEnabled(final SettingsPreferenceFragment context,
3243460a2683ca820d8d7d184bc1e98241ea3986a4aTadashi G. Takaoka            final List<InputMethodInfo> inputMethodProperties, final String id,
3253460a2683ca820d8d7d184bc1e98241ea3986a4aTadashi G. Takaoka            final boolean enabled) {
3263460a2683ca820d8d7d184bc1e98241ea3986a4aTadashi G. Takaoka        final PreferenceScreen preferenceScreen = context.getPreferenceScreen();
3273460a2683ca820d8d7d184bc1e98241ea3986a4aTadashi G. Takaoka        for (final InputMethodInfo imi : inputMethodProperties) {
3289cd11a9aa5ac74ca89432655d019f68d789bc405satok            if (id.equals(imi.getId())) {
3295a813ba7f99f1c797ead4a58fdf05f91296158a4Ken Wakasa                final int subtypeCount = imi.getSubtypeCount();
3305a813ba7f99f1c797ead4a58fdf05f91296158a4Ken Wakasa                for (int i = 0; i < subtypeCount; ++i) {
3313460a2683ca820d8d7d184bc1e98241ea3986a4aTadashi G. Takaoka                    final InputMethodSubtype subtype = imi.getSubtypeAt(i);
3321f53937469095f268d4c7a21d93f821a341a151dTadashi G. Takaoka                    final TwoStatePreference pref = (TwoStatePreference) preferenceScreen
3333460a2683ca820d8d7d184bc1e98241ea3986a4aTadashi G. Takaoka                            .findPreference(id + subtype.hashCode());
3343de7021756efd7fc9082b8768bc9a38fa310e7d8satok                    if (pref != null) {
3353de7021756efd7fc9082b8768bc9a38fa310e7d8satok                        pref.setEnabled(enabled);
3363de7021756efd7fc9082b8768bc9a38fa310e7d8satok                    }
3379cd11a9aa5ac74ca89432655d019f68d789bc405satok                }
3389cd11a9aa5ac74ca89432655d019f68d789bc405satok            }
3399cd11a9aa5ac74ca89432655d019f68d789bc405satok        }
3409cd11a9aa5ac74ca89432655d019f68d789bc405satok    }
3413de7021756efd7fc9082b8768bc9a38fa310e7d8satok
3423460a2683ca820d8d7d184bc1e98241ea3986a4aTadashi G. Takaoka    private static void updateSubtypesPreferenceChecked(final SettingsPreferenceFragment context,
3433460a2683ca820d8d7d184bc1e98241ea3986a4aTadashi G. Takaoka            final List<InputMethodInfo> inputMethodProperties,
3443460a2683ca820d8d7d184bc1e98241ea3986a4aTadashi G. Takaoka            final HashMap<String, HashSet<String>> enabledSubtypes) {
3453460a2683ca820d8d7d184bc1e98241ea3986a4aTadashi G. Takaoka        final PreferenceScreen preferenceScreen = context.getPreferenceScreen();
3463460a2683ca820d8d7d184bc1e98241ea3986a4aTadashi G. Takaoka        for (final InputMethodInfo imi : inputMethodProperties) {
3473460a2683ca820d8d7d184bc1e98241ea3986a4aTadashi G. Takaoka            final String id = imi.getId();
3483460a2683ca820d8d7d184bc1e98241ea3986a4aTadashi G. Takaoka            if (!enabledSubtypes.containsKey(id)) {
3493460a2683ca820d8d7d184bc1e98241ea3986a4aTadashi G. Takaoka                // There is no need to enable/disable subtypes of disabled IMEs.
3503460a2683ca820d8d7d184bc1e98241ea3986a4aTadashi G. Takaoka                continue;
3513460a2683ca820d8d7d184bc1e98241ea3986a4aTadashi G. Takaoka            }
3527ea58d31646bcfbcdd9ddd0bfdc685ed7ab3f1d8satok            final HashSet<String> enabledSubtypesSet = enabledSubtypes.get(id);
3535a813ba7f99f1c797ead4a58fdf05f91296158a4Ken Wakasa            final int subtypeCount = imi.getSubtypeCount();
3545a813ba7f99f1c797ead4a58fdf05f91296158a4Ken Wakasa            for (int i = 0; i < subtypeCount; ++i) {
3553460a2683ca820d8d7d184bc1e98241ea3986a4aTadashi G. Takaoka                final InputMethodSubtype subtype = imi.getSubtypeAt(i);
3563460a2683ca820d8d7d184bc1e98241ea3986a4aTadashi G. Takaoka                final String hashCode = String.valueOf(subtype.hashCode());
3573de7021756efd7fc9082b8768bc9a38fa310e7d8satok                if (DEBUG) {
3583de7021756efd7fc9082b8768bc9a38fa310e7d8satok                    Log.d(TAG, "--- Set checked state: " + "id" + ", " + hashCode + ", "
3593de7021756efd7fc9082b8768bc9a38fa310e7d8satok                            + enabledSubtypesSet.contains(hashCode));
3603de7021756efd7fc9082b8768bc9a38fa310e7d8satok                }
3611f53937469095f268d4c7a21d93f821a341a151dTadashi G. Takaoka                final TwoStatePreference pref = (TwoStatePreference) preferenceScreen
3623460a2683ca820d8d7d184bc1e98241ea3986a4aTadashi G. Takaoka                        .findPreference(id + hashCode);
3633de7021756efd7fc9082b8768bc9a38fa310e7d8satok                if (pref != null) {
3643de7021756efd7fc9082b8768bc9a38fa310e7d8satok                    pref.setChecked(enabledSubtypesSet.contains(hashCode));
3653de7021756efd7fc9082b8768bc9a38fa310e7d8satok                }
3663de7021756efd7fc9082b8768bc9a38fa310e7d8satok            }
3673de7021756efd7fc9082b8768bc9a38fa310e7d8satok        }
3683de7021756efd7fc9082b8768bc9a38fa310e7d8satok    }
36974e7c3e3601f4808854cf12bffe11ebf4c6ea8abTadashi G. Takaoka
37074e7c3e3601f4808854cf12bffe11ebf4c6ea8abTadashi G. Takaoka    static void removeUnnecessaryNonPersistentPreference(final Preference pref) {
37174e7c3e3601f4808854cf12bffe11ebf4c6ea8abTadashi G. Takaoka        final String key = pref.getKey();
37274e7c3e3601f4808854cf12bffe11ebf4c6ea8abTadashi G. Takaoka        if (pref.isPersistent() || key == null) {
37374e7c3e3601f4808854cf12bffe11ebf4c6ea8abTadashi G. Takaoka            return;
37474e7c3e3601f4808854cf12bffe11ebf4c6ea8abTadashi G. Takaoka        }
37574e7c3e3601f4808854cf12bffe11ebf4c6ea8abTadashi G. Takaoka        final SharedPreferences prefs = pref.getSharedPreferences();
37674e7c3e3601f4808854cf12bffe11ebf4c6ea8abTadashi G. Takaoka        if (prefs != null && prefs.contains(key)) {
37774e7c3e3601f4808854cf12bffe11ebf4c6ea8abTadashi G. Takaoka            prefs.edit().remove(key).apply();
37874e7c3e3601f4808854cf12bffe11ebf4c6ea8abTadashi G. Takaoka        }
37974e7c3e3601f4808854cf12bffe11ebf4c6ea8abTadashi G. Takaoka    }
38091c23ca03f9ffa9ac9f2644e50e51e481a423662Yohei Yukawa
38191c23ca03f9ffa9ac9f2644e50e51e481a423662Yohei Yukawa    @NonNull
38291c23ca03f9ffa9ac9f2644e50e51e481a423662Yohei Yukawa    static String getSubtypeLocaleNameAsSentence(@Nullable InputMethodSubtype subtype,
38391c23ca03f9ffa9ac9f2644e50e51e481a423662Yohei Yukawa            @NonNull final Context context, @NonNull final InputMethodInfo inputMethodInfo) {
38491c23ca03f9ffa9ac9f2644e50e51e481a423662Yohei Yukawa        if (subtype == null) {
38591c23ca03f9ffa9ac9f2644e50e51e481a423662Yohei Yukawa            return "";
38691c23ca03f9ffa9ac9f2644e50e51e481a423662Yohei Yukawa        }
38791c23ca03f9ffa9ac9f2644e50e51e481a423662Yohei Yukawa        final Locale locale = getDisplayLocale(context);
38891c23ca03f9ffa9ac9f2644e50e51e481a423662Yohei Yukawa        final CharSequence subtypeName = subtype.getDisplayName(context,
38991c23ca03f9ffa9ac9f2644e50e51e481a423662Yohei Yukawa                inputMethodInfo.getPackageName(), inputMethodInfo.getServiceInfo()
39091c23ca03f9ffa9ac9f2644e50e51e481a423662Yohei Yukawa                        .applicationInfo);
39191c23ca03f9ffa9ac9f2644e50e51e481a423662Yohei Yukawa        return LocaleHelper.toSentenceCase(subtypeName.toString(), locale);
39291c23ca03f9ffa9ac9f2644e50e51e481a423662Yohei Yukawa    }
39391c23ca03f9ffa9ac9f2644e50e51e481a423662Yohei Yukawa
39491c23ca03f9ffa9ac9f2644e50e51e481a423662Yohei Yukawa    @NonNull
39591c23ca03f9ffa9ac9f2644e50e51e481a423662Yohei Yukawa    static String getSubtypeLocaleNameListAsSentence(
39691c23ca03f9ffa9ac9f2644e50e51e481a423662Yohei Yukawa            @NonNull final List<InputMethodSubtype> subtypes, @NonNull final Context context,
39791c23ca03f9ffa9ac9f2644e50e51e481a423662Yohei Yukawa            @NonNull final InputMethodInfo inputMethodInfo) {
39891c23ca03f9ffa9ac9f2644e50e51e481a423662Yohei Yukawa        if (subtypes.isEmpty()) {
39991c23ca03f9ffa9ac9f2644e50e51e481a423662Yohei Yukawa            return "";
40091c23ca03f9ffa9ac9f2644e50e51e481a423662Yohei Yukawa        }
40191c23ca03f9ffa9ac9f2644e50e51e481a423662Yohei Yukawa        final Locale locale = getDisplayLocale(context);
40291c23ca03f9ffa9ac9f2644e50e51e481a423662Yohei Yukawa        final int subtypeCount = subtypes.size();
40391c23ca03f9ffa9ac9f2644e50e51e481a423662Yohei Yukawa        final CharSequence[] subtypeNames = new CharSequence[subtypeCount];
40491c23ca03f9ffa9ac9f2644e50e51e481a423662Yohei Yukawa        for (int i = 0; i < subtypeCount; i++) {
40591c23ca03f9ffa9ac9f2644e50e51e481a423662Yohei Yukawa            subtypeNames[i] = subtypes.get(i).getDisplayName(context,
40691c23ca03f9ffa9ac9f2644e50e51e481a423662Yohei Yukawa                    inputMethodInfo.getPackageName(), inputMethodInfo.getServiceInfo()
40791c23ca03f9ffa9ac9f2644e50e51e481a423662Yohei Yukawa                            .applicationInfo);
40891c23ca03f9ffa9ac9f2644e50e51e481a423662Yohei Yukawa        }
40991c23ca03f9ffa9ac9f2644e50e51e481a423662Yohei Yukawa        return LocaleHelper.toSentenceCase(
41091c23ca03f9ffa9ac9f2644e50e51e481a423662Yohei Yukawa                ListFormatter.getInstance(locale).format(subtypeNames), locale);
41191c23ca03f9ffa9ac9f2644e50e51e481a423662Yohei Yukawa    }
41291c23ca03f9ffa9ac9f2644e50e51e481a423662Yohei Yukawa
41391c23ca03f9ffa9ac9f2644e50e51e481a423662Yohei Yukawa    @NonNull
41491c23ca03f9ffa9ac9f2644e50e51e481a423662Yohei Yukawa    private static Locale getDisplayLocale(@Nullable final Context context) {
41591c23ca03f9ffa9ac9f2644e50e51e481a423662Yohei Yukawa        if (context == null) {
41691c23ca03f9ffa9ac9f2644e50e51e481a423662Yohei Yukawa            return Locale.getDefault();
41791c23ca03f9ffa9ac9f2644e50e51e481a423662Yohei Yukawa        }
41891c23ca03f9ffa9ac9f2644e50e51e481a423662Yohei Yukawa        if (context.getResources() == null) {
41991c23ca03f9ffa9ac9f2644e50e51e481a423662Yohei Yukawa            return Locale.getDefault();
42091c23ca03f9ffa9ac9f2644e50e51e481a423662Yohei Yukawa        }
42191c23ca03f9ffa9ac9f2644e50e51e481a423662Yohei Yukawa        final Configuration configuration = context.getResources().getConfiguration();
42291c23ca03f9ffa9ac9f2644e50e51e481a423662Yohei Yukawa        if (configuration == null) {
42391c23ca03f9ffa9ac9f2644e50e51e481a423662Yohei Yukawa            return Locale.getDefault();
42491c23ca03f9ffa9ac9f2644e50e51e481a423662Yohei Yukawa        }
42591c23ca03f9ffa9ac9f2644e50e51e481a423662Yohei Yukawa        final Locale configurationLocale = configuration.getLocales().get(0);
42691c23ca03f9ffa9ac9f2644e50e51e481a423662Yohei Yukawa        if (configurationLocale == null) {
42791c23ca03f9ffa9ac9f2644e50e51e481a423662Yohei Yukawa            return Locale.getDefault();
42891c23ca03f9ffa9ac9f2644e50e51e481a423662Yohei Yukawa        }
42991c23ca03f9ffa9ac9f2644e50e51e481a423662Yohei Yukawa        return configurationLocale;
43091c23ca03f9ffa9ac9f2644e50e51e481a423662Yohei Yukawa    }
4319cd11a9aa5ac74ca89432655d019f68d789bc405satok}
432