1b8592357c37e681ba709b524a7202b7787f35e5aTony Mantler/*
2b8592357c37e681ba709b524a7202b7787f35e5aTony Mantler * Copyright (C) 2017 The Android Open Source Project
3b8592357c37e681ba709b524a7202b7787f35e5aTony Mantler *
4b8592357c37e681ba709b524a7202b7787f35e5aTony Mantler * Licensed under the Apache License, Version 2.0 (the "License");
5b8592357c37e681ba709b524a7202b7787f35e5aTony Mantler * you may not use this file except in compliance with the License.
6b8592357c37e681ba709b524a7202b7787f35e5aTony Mantler * You may obtain a copy of the License at
7b8592357c37e681ba709b524a7202b7787f35e5aTony Mantler *
8b8592357c37e681ba709b524a7202b7787f35e5aTony Mantler *      http://www.apache.org/licenses/LICENSE-2.0
9b8592357c37e681ba709b524a7202b7787f35e5aTony Mantler *
10b8592357c37e681ba709b524a7202b7787f35e5aTony Mantler * Unless required by applicable law or agreed to in writing, software
11b8592357c37e681ba709b524a7202b7787f35e5aTony Mantler * distributed under the License is distributed on an "AS IS" BASIS,
12b8592357c37e681ba709b524a7202b7787f35e5aTony Mantler * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13b8592357c37e681ba709b524a7202b7787f35e5aTony Mantler * See the License for the specific language governing permissions and
14b8592357c37e681ba709b524a7202b7787f35e5aTony Mantler * limitations under the License
15b8592357c37e681ba709b524a7202b7787f35e5aTony Mantler */
16b8592357c37e681ba709b524a7202b7787f35e5aTony Mantler
17b8592357c37e681ba709b524a7202b7787f35e5aTony Mantlerpackage com.android.settingslib.inputmethod;
18b8592357c37e681ba709b524a7202b7787f35e5aTony Mantler
19b8592357c37e681ba709b524a7202b7787f35e5aTony Mantlerimport android.annotation.NonNull;
20b8592357c37e681ba709b524a7202b7787f35e5aTony Mantlerimport android.annotation.Nullable;
21b8592357c37e681ba709b524a7202b7787f35e5aTony Mantlerimport android.content.ContentResolver;
22b8592357c37e681ba709b524a7202b7787f35e5aTony Mantlerimport android.content.Context;
23b8592357c37e681ba709b524a7202b7787f35e5aTony Mantlerimport android.content.SharedPreferences;
24b8592357c37e681ba709b524a7202b7787f35e5aTony Mantlerimport android.content.res.Configuration;
25b8592357c37e681ba709b524a7202b7787f35e5aTony Mantlerimport android.icu.text.ListFormatter;
26b8592357c37e681ba709b524a7202b7787f35e5aTony Mantlerimport android.provider.Settings;
27b8592357c37e681ba709b524a7202b7787f35e5aTony Mantlerimport android.provider.Settings.SettingNotFoundException;
28b8592357c37e681ba709b524a7202b7787f35e5aTony Mantlerimport android.support.v14.preference.PreferenceFragment;
29b8592357c37e681ba709b524a7202b7787f35e5aTony Mantlerimport android.support.v7.preference.Preference;
30b8592357c37e681ba709b524a7202b7787f35e5aTony Mantlerimport android.support.v7.preference.PreferenceScreen;
31b8592357c37e681ba709b524a7202b7787f35e5aTony Mantlerimport android.support.v7.preference.TwoStatePreference;
32b8592357c37e681ba709b524a7202b7787f35e5aTony Mantlerimport android.text.TextUtils;
33b8592357c37e681ba709b524a7202b7787f35e5aTony Mantlerimport android.util.Log;
34b8592357c37e681ba709b524a7202b7787f35e5aTony Mantlerimport android.view.inputmethod.InputMethodInfo;
35b8592357c37e681ba709b524a7202b7787f35e5aTony Mantlerimport android.view.inputmethod.InputMethodSubtype;
36b8592357c37e681ba709b524a7202b7787f35e5aTony Mantler
37b8592357c37e681ba709b524a7202b7787f35e5aTony Mantlerimport com.android.internal.app.LocaleHelper;
38b8592357c37e681ba709b524a7202b7787f35e5aTony Mantlerimport com.android.internal.inputmethod.InputMethodUtils;
39b8592357c37e681ba709b524a7202b7787f35e5aTony Mantler
40b8592357c37e681ba709b524a7202b7787f35e5aTony Mantlerimport java.util.HashMap;
41b8592357c37e681ba709b524a7202b7787f35e5aTony Mantlerimport java.util.HashSet;
42b8592357c37e681ba709b524a7202b7787f35e5aTony Mantlerimport java.util.List;
43b8592357c37e681ba709b524a7202b7787f35e5aTony Mantlerimport java.util.Locale;
44b8592357c37e681ba709b524a7202b7787f35e5aTony Mantlerimport java.util.Map;
45b8592357c37e681ba709b524a7202b7787f35e5aTony Mantler
46b8592357c37e681ba709b524a7202b7787f35e5aTony Mantler// TODO: Consolidate this with {@link InputMethodSettingValuesWrapper}.
47b8592357c37e681ba709b524a7202b7787f35e5aTony Mantlerpublic class InputMethodAndSubtypeUtil {
48b8592357c37e681ba709b524a7202b7787f35e5aTony Mantler
49b8592357c37e681ba709b524a7202b7787f35e5aTony Mantler    private static final boolean DEBUG = false;
50b8592357c37e681ba709b524a7202b7787f35e5aTony Mantler    private static final String TAG = "InputMethdAndSubtypeUtl";
51b8592357c37e681ba709b524a7202b7787f35e5aTony Mantler
52b8592357c37e681ba709b524a7202b7787f35e5aTony Mantler    private static final char INPUT_METHOD_SEPARATER = ':';
53b8592357c37e681ba709b524a7202b7787f35e5aTony Mantler    private static final char INPUT_METHOD_SUBTYPE_SEPARATER = ';';
54b8592357c37e681ba709b524a7202b7787f35e5aTony Mantler    private static final int NOT_A_SUBTYPE_ID = -1;
55b8592357c37e681ba709b524a7202b7787f35e5aTony Mantler
56b8592357c37e681ba709b524a7202b7787f35e5aTony Mantler    private static final TextUtils.SimpleStringSplitter sStringInputMethodSplitter
57b8592357c37e681ba709b524a7202b7787f35e5aTony Mantler            = new TextUtils.SimpleStringSplitter(INPUT_METHOD_SEPARATER);
58b8592357c37e681ba709b524a7202b7787f35e5aTony Mantler
59b8592357c37e681ba709b524a7202b7787f35e5aTony Mantler    private static final TextUtils.SimpleStringSplitter sStringInputMethodSubtypeSplitter
60b8592357c37e681ba709b524a7202b7787f35e5aTony Mantler            = new TextUtils.SimpleStringSplitter(INPUT_METHOD_SUBTYPE_SEPARATER);
61b8592357c37e681ba709b524a7202b7787f35e5aTony Mantler
62b8592357c37e681ba709b524a7202b7787f35e5aTony Mantler    // InputMethods and subtypes are saved in the settings as follows:
63b8592357c37e681ba709b524a7202b7787f35e5aTony Mantler    // ime0;subtype0;subtype1:ime1;subtype0:ime2:ime3;subtype0;subtype1
64b8592357c37e681ba709b524a7202b7787f35e5aTony Mantler    private static String buildInputMethodsAndSubtypesString(
65b8592357c37e681ba709b524a7202b7787f35e5aTony Mantler            final HashMap<String, HashSet<String>> imeToSubtypesMap) {
66b8592357c37e681ba709b524a7202b7787f35e5aTony Mantler        final StringBuilder builder = new StringBuilder();
67b8592357c37e681ba709b524a7202b7787f35e5aTony Mantler        for (final String imi : imeToSubtypesMap.keySet()) {
68b8592357c37e681ba709b524a7202b7787f35e5aTony Mantler            if (builder.length() > 0) {
69b8592357c37e681ba709b524a7202b7787f35e5aTony Mantler                builder.append(INPUT_METHOD_SEPARATER);
70b8592357c37e681ba709b524a7202b7787f35e5aTony Mantler            }
71b8592357c37e681ba709b524a7202b7787f35e5aTony Mantler            final HashSet<String> subtypeIdSet = imeToSubtypesMap.get(imi);
72b8592357c37e681ba709b524a7202b7787f35e5aTony Mantler            builder.append(imi);
73b8592357c37e681ba709b524a7202b7787f35e5aTony Mantler            for (final String subtypeId : subtypeIdSet) {
74b8592357c37e681ba709b524a7202b7787f35e5aTony Mantler                builder.append(INPUT_METHOD_SUBTYPE_SEPARATER).append(subtypeId);
75b8592357c37e681ba709b524a7202b7787f35e5aTony Mantler            }
76b8592357c37e681ba709b524a7202b7787f35e5aTony Mantler        }
77b8592357c37e681ba709b524a7202b7787f35e5aTony Mantler        return builder.toString();
78b8592357c37e681ba709b524a7202b7787f35e5aTony Mantler    }
79b8592357c37e681ba709b524a7202b7787f35e5aTony Mantler
80b8592357c37e681ba709b524a7202b7787f35e5aTony Mantler    private static String buildInputMethodsString(final HashSet<String> imiList) {
81b8592357c37e681ba709b524a7202b7787f35e5aTony Mantler        final StringBuilder builder = new StringBuilder();
82b8592357c37e681ba709b524a7202b7787f35e5aTony Mantler        for (final String imi : imiList) {
83b8592357c37e681ba709b524a7202b7787f35e5aTony Mantler            if (builder.length() > 0) {
84b8592357c37e681ba709b524a7202b7787f35e5aTony Mantler                builder.append(INPUT_METHOD_SEPARATER);
85b8592357c37e681ba709b524a7202b7787f35e5aTony Mantler            }
86b8592357c37e681ba709b524a7202b7787f35e5aTony Mantler            builder.append(imi);
87b8592357c37e681ba709b524a7202b7787f35e5aTony Mantler        }
88b8592357c37e681ba709b524a7202b7787f35e5aTony Mantler        return builder.toString();
89b8592357c37e681ba709b524a7202b7787f35e5aTony Mantler    }
90b8592357c37e681ba709b524a7202b7787f35e5aTony Mantler
91b8592357c37e681ba709b524a7202b7787f35e5aTony Mantler    private static int getInputMethodSubtypeSelected(ContentResolver resolver) {
92b8592357c37e681ba709b524a7202b7787f35e5aTony Mantler        try {
93b8592357c37e681ba709b524a7202b7787f35e5aTony Mantler            return Settings.Secure.getInt(resolver,
94b8592357c37e681ba709b524a7202b7787f35e5aTony Mantler                    Settings.Secure.SELECTED_INPUT_METHOD_SUBTYPE);
95b8592357c37e681ba709b524a7202b7787f35e5aTony Mantler        } catch (SettingNotFoundException e) {
96b8592357c37e681ba709b524a7202b7787f35e5aTony Mantler            return NOT_A_SUBTYPE_ID;
97b8592357c37e681ba709b524a7202b7787f35e5aTony Mantler        }
98b8592357c37e681ba709b524a7202b7787f35e5aTony Mantler    }
99b8592357c37e681ba709b524a7202b7787f35e5aTony Mantler
100b8592357c37e681ba709b524a7202b7787f35e5aTony Mantler    private static boolean isInputMethodSubtypeSelected(ContentResolver resolver) {
101b8592357c37e681ba709b524a7202b7787f35e5aTony Mantler        return getInputMethodSubtypeSelected(resolver) != NOT_A_SUBTYPE_ID;
102b8592357c37e681ba709b524a7202b7787f35e5aTony Mantler    }
103b8592357c37e681ba709b524a7202b7787f35e5aTony Mantler
104b8592357c37e681ba709b524a7202b7787f35e5aTony Mantler    private static void putSelectedInputMethodSubtype(ContentResolver resolver, int hashCode) {
105b8592357c37e681ba709b524a7202b7787f35e5aTony Mantler        Settings.Secure.putInt(resolver, Settings.Secure.SELECTED_INPUT_METHOD_SUBTYPE, hashCode);
106b8592357c37e681ba709b524a7202b7787f35e5aTony Mantler    }
107b8592357c37e681ba709b524a7202b7787f35e5aTony Mantler
108b8592357c37e681ba709b524a7202b7787f35e5aTony Mantler    // Needs to modify InputMethodManageService if you want to change the format of saved string.
109b8592357c37e681ba709b524a7202b7787f35e5aTony Mantler    private static HashMap<String, HashSet<String>> getEnabledInputMethodsAndSubtypeList(
110b8592357c37e681ba709b524a7202b7787f35e5aTony Mantler            ContentResolver resolver) {
111b8592357c37e681ba709b524a7202b7787f35e5aTony Mantler        final String enabledInputMethodsStr = Settings.Secure.getString(
112b8592357c37e681ba709b524a7202b7787f35e5aTony Mantler                resolver, Settings.Secure.ENABLED_INPUT_METHODS);
113b8592357c37e681ba709b524a7202b7787f35e5aTony Mantler        if (DEBUG) {
114b8592357c37e681ba709b524a7202b7787f35e5aTony Mantler            Log.d(TAG, "--- Load enabled input methods: " + enabledInputMethodsStr);
115b8592357c37e681ba709b524a7202b7787f35e5aTony Mantler        }
116b8592357c37e681ba709b524a7202b7787f35e5aTony Mantler        return parseInputMethodsAndSubtypesString(enabledInputMethodsStr);
117b8592357c37e681ba709b524a7202b7787f35e5aTony Mantler    }
118b8592357c37e681ba709b524a7202b7787f35e5aTony Mantler
119b8592357c37e681ba709b524a7202b7787f35e5aTony Mantler    private static HashMap<String, HashSet<String>> parseInputMethodsAndSubtypesString(
120b8592357c37e681ba709b524a7202b7787f35e5aTony Mantler            final String inputMethodsAndSubtypesString) {
121b8592357c37e681ba709b524a7202b7787f35e5aTony Mantler        final HashMap<String, HashSet<String>> subtypesMap = new HashMap<>();
122b8592357c37e681ba709b524a7202b7787f35e5aTony Mantler        if (TextUtils.isEmpty(inputMethodsAndSubtypesString)) {
123b8592357c37e681ba709b524a7202b7787f35e5aTony Mantler            return subtypesMap;
124b8592357c37e681ba709b524a7202b7787f35e5aTony Mantler        }
125b8592357c37e681ba709b524a7202b7787f35e5aTony Mantler        sStringInputMethodSplitter.setString(inputMethodsAndSubtypesString);
126b8592357c37e681ba709b524a7202b7787f35e5aTony Mantler        while (sStringInputMethodSplitter.hasNext()) {
127b8592357c37e681ba709b524a7202b7787f35e5aTony Mantler            final String nextImsStr = sStringInputMethodSplitter.next();
128b8592357c37e681ba709b524a7202b7787f35e5aTony Mantler            sStringInputMethodSubtypeSplitter.setString(nextImsStr);
129b8592357c37e681ba709b524a7202b7787f35e5aTony Mantler            if (sStringInputMethodSubtypeSplitter.hasNext()) {
130b8592357c37e681ba709b524a7202b7787f35e5aTony Mantler                final HashSet<String> subtypeIdSet = new HashSet<>();
131b8592357c37e681ba709b524a7202b7787f35e5aTony Mantler                // The first element is {@link InputMethodInfoId}.
132b8592357c37e681ba709b524a7202b7787f35e5aTony Mantler                final String imiId = sStringInputMethodSubtypeSplitter.next();
133b8592357c37e681ba709b524a7202b7787f35e5aTony Mantler                while (sStringInputMethodSubtypeSplitter.hasNext()) {
134b8592357c37e681ba709b524a7202b7787f35e5aTony Mantler                    subtypeIdSet.add(sStringInputMethodSubtypeSplitter.next());
135b8592357c37e681ba709b524a7202b7787f35e5aTony Mantler                }
136b8592357c37e681ba709b524a7202b7787f35e5aTony Mantler                subtypesMap.put(imiId, subtypeIdSet);
137b8592357c37e681ba709b524a7202b7787f35e5aTony Mantler            }
138b8592357c37e681ba709b524a7202b7787f35e5aTony Mantler        }
139b8592357c37e681ba709b524a7202b7787f35e5aTony Mantler        return subtypesMap;
140b8592357c37e681ba709b524a7202b7787f35e5aTony Mantler    }
141b8592357c37e681ba709b524a7202b7787f35e5aTony Mantler
142b8592357c37e681ba709b524a7202b7787f35e5aTony Mantler    private static HashSet<String> getDisabledSystemIMEs(ContentResolver resolver) {
143b8592357c37e681ba709b524a7202b7787f35e5aTony Mantler        HashSet<String> set = new HashSet<>();
144b8592357c37e681ba709b524a7202b7787f35e5aTony Mantler        String disabledIMEsStr = Settings.Secure.getString(
145b8592357c37e681ba709b524a7202b7787f35e5aTony Mantler                resolver, Settings.Secure.DISABLED_SYSTEM_INPUT_METHODS);
146b8592357c37e681ba709b524a7202b7787f35e5aTony Mantler        if (TextUtils.isEmpty(disabledIMEsStr)) {
147b8592357c37e681ba709b524a7202b7787f35e5aTony Mantler            return set;
148b8592357c37e681ba709b524a7202b7787f35e5aTony Mantler        }
149b8592357c37e681ba709b524a7202b7787f35e5aTony Mantler        sStringInputMethodSplitter.setString(disabledIMEsStr);
150b8592357c37e681ba709b524a7202b7787f35e5aTony Mantler        while(sStringInputMethodSplitter.hasNext()) {
151b8592357c37e681ba709b524a7202b7787f35e5aTony Mantler            set.add(sStringInputMethodSplitter.next());
152b8592357c37e681ba709b524a7202b7787f35e5aTony Mantler        }
153b8592357c37e681ba709b524a7202b7787f35e5aTony Mantler        return set;
154b8592357c37e681ba709b524a7202b7787f35e5aTony Mantler    }
155b8592357c37e681ba709b524a7202b7787f35e5aTony Mantler
156b8592357c37e681ba709b524a7202b7787f35e5aTony Mantler    public static void saveInputMethodSubtypeList(PreferenceFragment context,
157b8592357c37e681ba709b524a7202b7787f35e5aTony Mantler            ContentResolver resolver, List<InputMethodInfo> inputMethodInfos,
158b8592357c37e681ba709b524a7202b7787f35e5aTony Mantler            boolean hasHardKeyboard) {
159b8592357c37e681ba709b524a7202b7787f35e5aTony Mantler        String currentInputMethodId = Settings.Secure.getString(resolver,
160b8592357c37e681ba709b524a7202b7787f35e5aTony Mantler                Settings.Secure.DEFAULT_INPUT_METHOD);
161b8592357c37e681ba709b524a7202b7787f35e5aTony Mantler        final int selectedInputMethodSubtype = getInputMethodSubtypeSelected(resolver);
162b8592357c37e681ba709b524a7202b7787f35e5aTony Mantler        final HashMap<String, HashSet<String>> enabledIMEsAndSubtypesMap =
163b8592357c37e681ba709b524a7202b7787f35e5aTony Mantler                getEnabledInputMethodsAndSubtypeList(resolver);
164b8592357c37e681ba709b524a7202b7787f35e5aTony Mantler        final HashSet<String> disabledSystemIMEs = getDisabledSystemIMEs(resolver);
165b8592357c37e681ba709b524a7202b7787f35e5aTony Mantler
166b8592357c37e681ba709b524a7202b7787f35e5aTony Mantler        boolean needsToResetSelectedSubtype = false;
167b8592357c37e681ba709b524a7202b7787f35e5aTony Mantler        for (final InputMethodInfo imi : inputMethodInfos) {
168b8592357c37e681ba709b524a7202b7787f35e5aTony Mantler            final String imiId = imi.getId();
169b8592357c37e681ba709b524a7202b7787f35e5aTony Mantler            final Preference pref = context.findPreference(imiId);
170b8592357c37e681ba709b524a7202b7787f35e5aTony Mantler            if (pref == null) {
171b8592357c37e681ba709b524a7202b7787f35e5aTony Mantler                continue;
172b8592357c37e681ba709b524a7202b7787f35e5aTony Mantler            }
173b8592357c37e681ba709b524a7202b7787f35e5aTony Mantler            // In the choose input method screen or in the subtype enabler screen,
174b8592357c37e681ba709b524a7202b7787f35e5aTony Mantler            // <code>pref</code> is an instance of TwoStatePreference.
175b8592357c37e681ba709b524a7202b7787f35e5aTony Mantler            final boolean isImeChecked = (pref instanceof TwoStatePreference) ?
176b8592357c37e681ba709b524a7202b7787f35e5aTony Mantler                    ((TwoStatePreference) pref).isChecked()
177b8592357c37e681ba709b524a7202b7787f35e5aTony Mantler                    : enabledIMEsAndSubtypesMap.containsKey(imiId);
178b8592357c37e681ba709b524a7202b7787f35e5aTony Mantler            final boolean isCurrentInputMethod = imiId.equals(currentInputMethodId);
179b8592357c37e681ba709b524a7202b7787f35e5aTony Mantler            final boolean systemIme = InputMethodUtils.isSystemIme(imi);
180b8592357c37e681ba709b524a7202b7787f35e5aTony Mantler            if ((!hasHardKeyboard && InputMethodSettingValuesWrapper.getInstance(
181b8592357c37e681ba709b524a7202b7787f35e5aTony Mantler                    context.getActivity()).isAlwaysCheckedIme(imi, context.getActivity()))
182b8592357c37e681ba709b524a7202b7787f35e5aTony Mantler                    || isImeChecked) {
183b8592357c37e681ba709b524a7202b7787f35e5aTony Mantler                if (!enabledIMEsAndSubtypesMap.containsKey(imiId)) {
184b8592357c37e681ba709b524a7202b7787f35e5aTony Mantler                    // imiId has just been enabled
185b8592357c37e681ba709b524a7202b7787f35e5aTony Mantler                    enabledIMEsAndSubtypesMap.put(imiId, new HashSet<>());
186b8592357c37e681ba709b524a7202b7787f35e5aTony Mantler                }
187b8592357c37e681ba709b524a7202b7787f35e5aTony Mantler                final HashSet<String> subtypesSet = enabledIMEsAndSubtypesMap.get(imiId);
188b8592357c37e681ba709b524a7202b7787f35e5aTony Mantler
189b8592357c37e681ba709b524a7202b7787f35e5aTony Mantler                boolean subtypePrefFound = false;
190b8592357c37e681ba709b524a7202b7787f35e5aTony Mantler                final int subtypeCount = imi.getSubtypeCount();
191b8592357c37e681ba709b524a7202b7787f35e5aTony Mantler                for (int i = 0; i < subtypeCount; ++i) {
192b8592357c37e681ba709b524a7202b7787f35e5aTony Mantler                    final InputMethodSubtype subtype = imi.getSubtypeAt(i);
193b8592357c37e681ba709b524a7202b7787f35e5aTony Mantler                    final String subtypeHashCodeStr = String.valueOf(subtype.hashCode());
194b8592357c37e681ba709b524a7202b7787f35e5aTony Mantler                    final TwoStatePreference subtypePref = (TwoStatePreference) context
195b8592357c37e681ba709b524a7202b7787f35e5aTony Mantler                            .findPreference(imiId + subtypeHashCodeStr);
196b8592357c37e681ba709b524a7202b7787f35e5aTony Mantler                    // In the Configure input method screen which does not have subtype preferences.
197b8592357c37e681ba709b524a7202b7787f35e5aTony Mantler                    if (subtypePref == null) {
198b8592357c37e681ba709b524a7202b7787f35e5aTony Mantler                        continue;
199b8592357c37e681ba709b524a7202b7787f35e5aTony Mantler                    }
200b8592357c37e681ba709b524a7202b7787f35e5aTony Mantler                    if (!subtypePrefFound) {
201b8592357c37e681ba709b524a7202b7787f35e5aTony Mantler                        // Once subtype preference is found, subtypeSet needs to be cleared.
202b8592357c37e681ba709b524a7202b7787f35e5aTony Mantler                        // Because of system change, hashCode value could have been changed.
203b8592357c37e681ba709b524a7202b7787f35e5aTony Mantler                        subtypesSet.clear();
204b8592357c37e681ba709b524a7202b7787f35e5aTony Mantler                        // If selected subtype preference is disabled, needs to reset.
205b8592357c37e681ba709b524a7202b7787f35e5aTony Mantler                        needsToResetSelectedSubtype = true;
206b8592357c37e681ba709b524a7202b7787f35e5aTony Mantler                        subtypePrefFound = true;
207b8592357c37e681ba709b524a7202b7787f35e5aTony Mantler                    }
208b8592357c37e681ba709b524a7202b7787f35e5aTony Mantler                    // Checking <code>subtypePref.isEnabled()</code> is insufficient to determine
209b8592357c37e681ba709b524a7202b7787f35e5aTony Mantler                    // whether the user manually enabled this subtype or not.  Implicitly-enabled
210b8592357c37e681ba709b524a7202b7787f35e5aTony Mantler                    // subtypes are also checked just as an indicator to users.  We also need to
211b8592357c37e681ba709b524a7202b7787f35e5aTony Mantler                    // check <code>subtypePref.isEnabled()</code> so that only manually enabled
212b8592357c37e681ba709b524a7202b7787f35e5aTony Mantler                    // subtypes can be saved here.
213b8592357c37e681ba709b524a7202b7787f35e5aTony Mantler                    if (subtypePref.isEnabled() && subtypePref.isChecked()) {
214b8592357c37e681ba709b524a7202b7787f35e5aTony Mantler                        subtypesSet.add(subtypeHashCodeStr);
215b8592357c37e681ba709b524a7202b7787f35e5aTony Mantler                        if (isCurrentInputMethod) {
216b8592357c37e681ba709b524a7202b7787f35e5aTony Mantler                            if (selectedInputMethodSubtype == subtype.hashCode()) {
217b8592357c37e681ba709b524a7202b7787f35e5aTony Mantler                                // Selected subtype is still enabled, there is no need to reset
218b8592357c37e681ba709b524a7202b7787f35e5aTony Mantler                                // selected subtype.
219b8592357c37e681ba709b524a7202b7787f35e5aTony Mantler                                needsToResetSelectedSubtype = false;
220b8592357c37e681ba709b524a7202b7787f35e5aTony Mantler                            }
221b8592357c37e681ba709b524a7202b7787f35e5aTony Mantler                        }
222b8592357c37e681ba709b524a7202b7787f35e5aTony Mantler                    } else {
223b8592357c37e681ba709b524a7202b7787f35e5aTony Mantler                        subtypesSet.remove(subtypeHashCodeStr);
224b8592357c37e681ba709b524a7202b7787f35e5aTony Mantler                    }
225b8592357c37e681ba709b524a7202b7787f35e5aTony Mantler                }
226b8592357c37e681ba709b524a7202b7787f35e5aTony Mantler            } else {
227b8592357c37e681ba709b524a7202b7787f35e5aTony Mantler                enabledIMEsAndSubtypesMap.remove(imiId);
228b8592357c37e681ba709b524a7202b7787f35e5aTony Mantler                if (isCurrentInputMethod) {
229b8592357c37e681ba709b524a7202b7787f35e5aTony Mantler                    // We are processing the current input method, but found that it's not enabled.
230b8592357c37e681ba709b524a7202b7787f35e5aTony Mantler                    // This means that the current input method has been uninstalled.
231b8592357c37e681ba709b524a7202b7787f35e5aTony Mantler                    // If currentInputMethod is already uninstalled, InputMethodManagerService will
232b8592357c37e681ba709b524a7202b7787f35e5aTony Mantler                    // find the applicable IME from the history and the system locale.
233b8592357c37e681ba709b524a7202b7787f35e5aTony Mantler                    if (DEBUG) {
234b8592357c37e681ba709b524a7202b7787f35e5aTony Mantler                        Log.d(TAG, "Current IME was uninstalled or disabled.");
235b8592357c37e681ba709b524a7202b7787f35e5aTony Mantler                    }
236b8592357c37e681ba709b524a7202b7787f35e5aTony Mantler                    currentInputMethodId = null;
237b8592357c37e681ba709b524a7202b7787f35e5aTony Mantler                }
238b8592357c37e681ba709b524a7202b7787f35e5aTony Mantler            }
239b8592357c37e681ba709b524a7202b7787f35e5aTony Mantler            // If it's a disabled system ime, add it to the disabled list so that it
240b8592357c37e681ba709b524a7202b7787f35e5aTony Mantler            // doesn't get enabled automatically on any changes to the package list
241b8592357c37e681ba709b524a7202b7787f35e5aTony Mantler            if (systemIme && hasHardKeyboard) {
242b8592357c37e681ba709b524a7202b7787f35e5aTony Mantler                if (disabledSystemIMEs.contains(imiId)) {
243b8592357c37e681ba709b524a7202b7787f35e5aTony Mantler                    if (isImeChecked) {
244b8592357c37e681ba709b524a7202b7787f35e5aTony Mantler                        disabledSystemIMEs.remove(imiId);
245b8592357c37e681ba709b524a7202b7787f35e5aTony Mantler                    }
246b8592357c37e681ba709b524a7202b7787f35e5aTony Mantler                } else {
247b8592357c37e681ba709b524a7202b7787f35e5aTony Mantler                    if (!isImeChecked) {
248b8592357c37e681ba709b524a7202b7787f35e5aTony Mantler                        disabledSystemIMEs.add(imiId);
249b8592357c37e681ba709b524a7202b7787f35e5aTony Mantler                    }
250b8592357c37e681ba709b524a7202b7787f35e5aTony Mantler                }
251b8592357c37e681ba709b524a7202b7787f35e5aTony Mantler            }
252b8592357c37e681ba709b524a7202b7787f35e5aTony Mantler        }
253b8592357c37e681ba709b524a7202b7787f35e5aTony Mantler
254b8592357c37e681ba709b524a7202b7787f35e5aTony Mantler        final String enabledIMEsAndSubtypesString = buildInputMethodsAndSubtypesString(
255b8592357c37e681ba709b524a7202b7787f35e5aTony Mantler                enabledIMEsAndSubtypesMap);
256b8592357c37e681ba709b524a7202b7787f35e5aTony Mantler        final String disabledSystemIMEsString = buildInputMethodsString(disabledSystemIMEs);
257b8592357c37e681ba709b524a7202b7787f35e5aTony Mantler        if (DEBUG) {
258b8592357c37e681ba709b524a7202b7787f35e5aTony Mantler            Log.d(TAG, "--- Save enabled inputmethod settings. :" + enabledIMEsAndSubtypesString);
259b8592357c37e681ba709b524a7202b7787f35e5aTony Mantler            Log.d(TAG, "--- Save disabled system inputmethod settings. :"
260b8592357c37e681ba709b524a7202b7787f35e5aTony Mantler                    + disabledSystemIMEsString);
261b8592357c37e681ba709b524a7202b7787f35e5aTony Mantler            Log.d(TAG, "--- Save default inputmethod settings. :" + currentInputMethodId);
262b8592357c37e681ba709b524a7202b7787f35e5aTony Mantler            Log.d(TAG, "--- Needs to reset the selected subtype :" + needsToResetSelectedSubtype);
263b8592357c37e681ba709b524a7202b7787f35e5aTony Mantler            Log.d(TAG, "--- Subtype is selected :" + isInputMethodSubtypeSelected(resolver));
264b8592357c37e681ba709b524a7202b7787f35e5aTony Mantler        }
265b8592357c37e681ba709b524a7202b7787f35e5aTony Mantler
266b8592357c37e681ba709b524a7202b7787f35e5aTony Mantler        // Redefines SelectedSubtype when all subtypes are unchecked or there is no subtype
267b8592357c37e681ba709b524a7202b7787f35e5aTony Mantler        // selected. And if the selected subtype of the current input method was disabled,
268b8592357c37e681ba709b524a7202b7787f35e5aTony Mantler        // We should reset the selected input method's subtype.
269b8592357c37e681ba709b524a7202b7787f35e5aTony Mantler        if (needsToResetSelectedSubtype || !isInputMethodSubtypeSelected(resolver)) {
270b8592357c37e681ba709b524a7202b7787f35e5aTony Mantler            if (DEBUG) {
271b8592357c37e681ba709b524a7202b7787f35e5aTony Mantler                Log.d(TAG, "--- Reset inputmethod subtype because it's not defined.");
272b8592357c37e681ba709b524a7202b7787f35e5aTony Mantler            }
273b8592357c37e681ba709b524a7202b7787f35e5aTony Mantler            putSelectedInputMethodSubtype(resolver, NOT_A_SUBTYPE_ID);
274b8592357c37e681ba709b524a7202b7787f35e5aTony Mantler        }
275b8592357c37e681ba709b524a7202b7787f35e5aTony Mantler
276b8592357c37e681ba709b524a7202b7787f35e5aTony Mantler        Settings.Secure.putString(resolver,
277b8592357c37e681ba709b524a7202b7787f35e5aTony Mantler                Settings.Secure.ENABLED_INPUT_METHODS, enabledIMEsAndSubtypesString);
278b8592357c37e681ba709b524a7202b7787f35e5aTony Mantler        if (disabledSystemIMEsString.length() > 0) {
279b8592357c37e681ba709b524a7202b7787f35e5aTony Mantler            Settings.Secure.putString(resolver, Settings.Secure.DISABLED_SYSTEM_INPUT_METHODS,
280b8592357c37e681ba709b524a7202b7787f35e5aTony Mantler                    disabledSystemIMEsString);
281b8592357c37e681ba709b524a7202b7787f35e5aTony Mantler        }
282b8592357c37e681ba709b524a7202b7787f35e5aTony Mantler        // If the current input method is unset, InputMethodManagerService will find the applicable
283b8592357c37e681ba709b524a7202b7787f35e5aTony Mantler        // IME from the history and the system locale.
284b8592357c37e681ba709b524a7202b7787f35e5aTony Mantler        Settings.Secure.putString(resolver, Settings.Secure.DEFAULT_INPUT_METHOD,
285b8592357c37e681ba709b524a7202b7787f35e5aTony Mantler                currentInputMethodId != null ? currentInputMethodId : "");
286b8592357c37e681ba709b524a7202b7787f35e5aTony Mantler    }
287b8592357c37e681ba709b524a7202b7787f35e5aTony Mantler
288b8592357c37e681ba709b524a7202b7787f35e5aTony Mantler    public static void loadInputMethodSubtypeList(final PreferenceFragment context,
289b8592357c37e681ba709b524a7202b7787f35e5aTony Mantler            final ContentResolver resolver, final List<InputMethodInfo> inputMethodInfos,
290b8592357c37e681ba709b524a7202b7787f35e5aTony Mantler            final Map<String, List<Preference>> inputMethodPrefsMap) {
291b8592357c37e681ba709b524a7202b7787f35e5aTony Mantler        final HashMap<String, HashSet<String>> enabledSubtypes =
292b8592357c37e681ba709b524a7202b7787f35e5aTony Mantler                getEnabledInputMethodsAndSubtypeList(resolver);
293b8592357c37e681ba709b524a7202b7787f35e5aTony Mantler
294b8592357c37e681ba709b524a7202b7787f35e5aTony Mantler        for (final InputMethodInfo imi : inputMethodInfos) {
295b8592357c37e681ba709b524a7202b7787f35e5aTony Mantler            final String imiId = imi.getId();
296b8592357c37e681ba709b524a7202b7787f35e5aTony Mantler            final Preference pref = context.findPreference(imiId);
297b8592357c37e681ba709b524a7202b7787f35e5aTony Mantler            if (pref instanceof TwoStatePreference) {
298b8592357c37e681ba709b524a7202b7787f35e5aTony Mantler                final TwoStatePreference subtypePref = (TwoStatePreference) pref;
299b8592357c37e681ba709b524a7202b7787f35e5aTony Mantler                final boolean isEnabled = enabledSubtypes.containsKey(imiId);
300b8592357c37e681ba709b524a7202b7787f35e5aTony Mantler                subtypePref.setChecked(isEnabled);
301b8592357c37e681ba709b524a7202b7787f35e5aTony Mantler                if (inputMethodPrefsMap != null) {
302b8592357c37e681ba709b524a7202b7787f35e5aTony Mantler                    for (final Preference childPref: inputMethodPrefsMap.get(imiId)) {
303b8592357c37e681ba709b524a7202b7787f35e5aTony Mantler                        childPref.setEnabled(isEnabled);
304b8592357c37e681ba709b524a7202b7787f35e5aTony Mantler                    }
305b8592357c37e681ba709b524a7202b7787f35e5aTony Mantler                }
306b8592357c37e681ba709b524a7202b7787f35e5aTony Mantler                setSubtypesPreferenceEnabled(context, inputMethodInfos, imiId, isEnabled);
307b8592357c37e681ba709b524a7202b7787f35e5aTony Mantler            }
308b8592357c37e681ba709b524a7202b7787f35e5aTony Mantler        }
309b8592357c37e681ba709b524a7202b7787f35e5aTony Mantler        updateSubtypesPreferenceChecked(context, inputMethodInfos, enabledSubtypes);
310b8592357c37e681ba709b524a7202b7787f35e5aTony Mantler    }
311b8592357c37e681ba709b524a7202b7787f35e5aTony Mantler
312b8592357c37e681ba709b524a7202b7787f35e5aTony Mantler    private static void setSubtypesPreferenceEnabled(final PreferenceFragment context,
313b8592357c37e681ba709b524a7202b7787f35e5aTony Mantler            final List<InputMethodInfo> inputMethodProperties, final String id,
314b8592357c37e681ba709b524a7202b7787f35e5aTony Mantler            final boolean enabled) {
315b8592357c37e681ba709b524a7202b7787f35e5aTony Mantler        final PreferenceScreen preferenceScreen = context.getPreferenceScreen();
316b8592357c37e681ba709b524a7202b7787f35e5aTony Mantler        for (final InputMethodInfo imi : inputMethodProperties) {
317b8592357c37e681ba709b524a7202b7787f35e5aTony Mantler            if (id.equals(imi.getId())) {
318b8592357c37e681ba709b524a7202b7787f35e5aTony Mantler                final int subtypeCount = imi.getSubtypeCount();
319b8592357c37e681ba709b524a7202b7787f35e5aTony Mantler                for (int i = 0; i < subtypeCount; ++i) {
320b8592357c37e681ba709b524a7202b7787f35e5aTony Mantler                    final InputMethodSubtype subtype = imi.getSubtypeAt(i);
321b8592357c37e681ba709b524a7202b7787f35e5aTony Mantler                    final TwoStatePreference pref = (TwoStatePreference) preferenceScreen
322b8592357c37e681ba709b524a7202b7787f35e5aTony Mantler                            .findPreference(id + subtype.hashCode());
323b8592357c37e681ba709b524a7202b7787f35e5aTony Mantler                    if (pref != null) {
324b8592357c37e681ba709b524a7202b7787f35e5aTony Mantler                        pref.setEnabled(enabled);
325b8592357c37e681ba709b524a7202b7787f35e5aTony Mantler                    }
326b8592357c37e681ba709b524a7202b7787f35e5aTony Mantler                }
327b8592357c37e681ba709b524a7202b7787f35e5aTony Mantler            }
328b8592357c37e681ba709b524a7202b7787f35e5aTony Mantler        }
329b8592357c37e681ba709b524a7202b7787f35e5aTony Mantler    }
330b8592357c37e681ba709b524a7202b7787f35e5aTony Mantler
331b8592357c37e681ba709b524a7202b7787f35e5aTony Mantler    private static void updateSubtypesPreferenceChecked(final PreferenceFragment context,
332b8592357c37e681ba709b524a7202b7787f35e5aTony Mantler            final List<InputMethodInfo> inputMethodProperties,
333b8592357c37e681ba709b524a7202b7787f35e5aTony Mantler            final HashMap<String, HashSet<String>> enabledSubtypes) {
334b8592357c37e681ba709b524a7202b7787f35e5aTony Mantler        final PreferenceScreen preferenceScreen = context.getPreferenceScreen();
335b8592357c37e681ba709b524a7202b7787f35e5aTony Mantler        for (final InputMethodInfo imi : inputMethodProperties) {
336b8592357c37e681ba709b524a7202b7787f35e5aTony Mantler            final String id = imi.getId();
337b8592357c37e681ba709b524a7202b7787f35e5aTony Mantler            if (!enabledSubtypes.containsKey(id)) {
338b8592357c37e681ba709b524a7202b7787f35e5aTony Mantler                // There is no need to enable/disable subtypes of disabled IMEs.
339b8592357c37e681ba709b524a7202b7787f35e5aTony Mantler                continue;
340b8592357c37e681ba709b524a7202b7787f35e5aTony Mantler            }
341b8592357c37e681ba709b524a7202b7787f35e5aTony Mantler            final HashSet<String> enabledSubtypesSet = enabledSubtypes.get(id);
342b8592357c37e681ba709b524a7202b7787f35e5aTony Mantler            final int subtypeCount = imi.getSubtypeCount();
343b8592357c37e681ba709b524a7202b7787f35e5aTony Mantler            for (int i = 0; i < subtypeCount; ++i) {
344b8592357c37e681ba709b524a7202b7787f35e5aTony Mantler                final InputMethodSubtype subtype = imi.getSubtypeAt(i);
345b8592357c37e681ba709b524a7202b7787f35e5aTony Mantler                final String hashCode = String.valueOf(subtype.hashCode());
346b8592357c37e681ba709b524a7202b7787f35e5aTony Mantler                if (DEBUG) {
347b8592357c37e681ba709b524a7202b7787f35e5aTony Mantler                    Log.d(TAG, "--- Set checked state: " + "id" + ", " + hashCode + ", "
348b8592357c37e681ba709b524a7202b7787f35e5aTony Mantler                            + enabledSubtypesSet.contains(hashCode));
349b8592357c37e681ba709b524a7202b7787f35e5aTony Mantler                }
350b8592357c37e681ba709b524a7202b7787f35e5aTony Mantler                final TwoStatePreference pref = (TwoStatePreference) preferenceScreen
351b8592357c37e681ba709b524a7202b7787f35e5aTony Mantler                        .findPreference(id + hashCode);
352b8592357c37e681ba709b524a7202b7787f35e5aTony Mantler                if (pref != null) {
353b8592357c37e681ba709b524a7202b7787f35e5aTony Mantler                    pref.setChecked(enabledSubtypesSet.contains(hashCode));
354b8592357c37e681ba709b524a7202b7787f35e5aTony Mantler                }
355b8592357c37e681ba709b524a7202b7787f35e5aTony Mantler            }
356b8592357c37e681ba709b524a7202b7787f35e5aTony Mantler        }
357b8592357c37e681ba709b524a7202b7787f35e5aTony Mantler    }
358b8592357c37e681ba709b524a7202b7787f35e5aTony Mantler
359b8592357c37e681ba709b524a7202b7787f35e5aTony Mantler    public static void removeUnnecessaryNonPersistentPreference(final Preference pref) {
360b8592357c37e681ba709b524a7202b7787f35e5aTony Mantler        final String key = pref.getKey();
361b8592357c37e681ba709b524a7202b7787f35e5aTony Mantler        if (pref.isPersistent() || key == null) {
362b8592357c37e681ba709b524a7202b7787f35e5aTony Mantler            return;
363b8592357c37e681ba709b524a7202b7787f35e5aTony Mantler        }
364b8592357c37e681ba709b524a7202b7787f35e5aTony Mantler        final SharedPreferences prefs = pref.getSharedPreferences();
365b8592357c37e681ba709b524a7202b7787f35e5aTony Mantler        if (prefs != null && prefs.contains(key)) {
366b8592357c37e681ba709b524a7202b7787f35e5aTony Mantler            prefs.edit().remove(key).apply();
367b8592357c37e681ba709b524a7202b7787f35e5aTony Mantler        }
368b8592357c37e681ba709b524a7202b7787f35e5aTony Mantler    }
369b8592357c37e681ba709b524a7202b7787f35e5aTony Mantler
370b8592357c37e681ba709b524a7202b7787f35e5aTony Mantler    @NonNull
371b8592357c37e681ba709b524a7202b7787f35e5aTony Mantler    public static String getSubtypeLocaleNameAsSentence(@Nullable InputMethodSubtype subtype,
372b8592357c37e681ba709b524a7202b7787f35e5aTony Mantler            @NonNull final Context context, @NonNull final InputMethodInfo inputMethodInfo) {
373b8592357c37e681ba709b524a7202b7787f35e5aTony Mantler        if (subtype == null) {
374b8592357c37e681ba709b524a7202b7787f35e5aTony Mantler            return "";
375b8592357c37e681ba709b524a7202b7787f35e5aTony Mantler        }
376b8592357c37e681ba709b524a7202b7787f35e5aTony Mantler        final Locale locale = getDisplayLocale(context);
377b8592357c37e681ba709b524a7202b7787f35e5aTony Mantler        final CharSequence subtypeName = subtype.getDisplayName(context,
378b8592357c37e681ba709b524a7202b7787f35e5aTony Mantler                inputMethodInfo.getPackageName(), inputMethodInfo.getServiceInfo()
379b8592357c37e681ba709b524a7202b7787f35e5aTony Mantler                        .applicationInfo);
380b8592357c37e681ba709b524a7202b7787f35e5aTony Mantler        return LocaleHelper.toSentenceCase(subtypeName.toString(), locale);
381b8592357c37e681ba709b524a7202b7787f35e5aTony Mantler    }
382b8592357c37e681ba709b524a7202b7787f35e5aTony Mantler
383b8592357c37e681ba709b524a7202b7787f35e5aTony Mantler    @NonNull
384b8592357c37e681ba709b524a7202b7787f35e5aTony Mantler    public static String getSubtypeLocaleNameListAsSentence(
385b8592357c37e681ba709b524a7202b7787f35e5aTony Mantler            @NonNull final List<InputMethodSubtype> subtypes, @NonNull final Context context,
386b8592357c37e681ba709b524a7202b7787f35e5aTony Mantler            @NonNull final InputMethodInfo inputMethodInfo) {
387b8592357c37e681ba709b524a7202b7787f35e5aTony Mantler        if (subtypes.isEmpty()) {
388b8592357c37e681ba709b524a7202b7787f35e5aTony Mantler            return "";
389b8592357c37e681ba709b524a7202b7787f35e5aTony Mantler        }
390b8592357c37e681ba709b524a7202b7787f35e5aTony Mantler        final Locale locale = getDisplayLocale(context);
391b8592357c37e681ba709b524a7202b7787f35e5aTony Mantler        final int subtypeCount = subtypes.size();
392b8592357c37e681ba709b524a7202b7787f35e5aTony Mantler        final CharSequence[] subtypeNames = new CharSequence[subtypeCount];
393b8592357c37e681ba709b524a7202b7787f35e5aTony Mantler        for (int i = 0; i < subtypeCount; i++) {
394b8592357c37e681ba709b524a7202b7787f35e5aTony Mantler            subtypeNames[i] = subtypes.get(i).getDisplayName(context,
395b8592357c37e681ba709b524a7202b7787f35e5aTony Mantler                    inputMethodInfo.getPackageName(), inputMethodInfo.getServiceInfo()
396b8592357c37e681ba709b524a7202b7787f35e5aTony Mantler                            .applicationInfo);
397b8592357c37e681ba709b524a7202b7787f35e5aTony Mantler        }
398b8592357c37e681ba709b524a7202b7787f35e5aTony Mantler        return LocaleHelper.toSentenceCase(
3999c1310e8648230c6ec3999b1f941cdafbe1aa816Maurice Lam                ListFormatter.getInstance(locale).format((Object[]) subtypeNames), locale);
400b8592357c37e681ba709b524a7202b7787f35e5aTony Mantler    }
401b8592357c37e681ba709b524a7202b7787f35e5aTony Mantler
402b8592357c37e681ba709b524a7202b7787f35e5aTony Mantler    @NonNull
403b8592357c37e681ba709b524a7202b7787f35e5aTony Mantler    private static Locale getDisplayLocale(@Nullable final Context context) {
404b8592357c37e681ba709b524a7202b7787f35e5aTony Mantler        if (context == null) {
405b8592357c37e681ba709b524a7202b7787f35e5aTony Mantler            return Locale.getDefault();
406b8592357c37e681ba709b524a7202b7787f35e5aTony Mantler        }
407b8592357c37e681ba709b524a7202b7787f35e5aTony Mantler        if (context.getResources() == null) {
408b8592357c37e681ba709b524a7202b7787f35e5aTony Mantler            return Locale.getDefault();
409b8592357c37e681ba709b524a7202b7787f35e5aTony Mantler        }
410b8592357c37e681ba709b524a7202b7787f35e5aTony Mantler        final Configuration configuration = context.getResources().getConfiguration();
411b8592357c37e681ba709b524a7202b7787f35e5aTony Mantler        if (configuration == null) {
412b8592357c37e681ba709b524a7202b7787f35e5aTony Mantler            return Locale.getDefault();
413b8592357c37e681ba709b524a7202b7787f35e5aTony Mantler        }
414b8592357c37e681ba709b524a7202b7787f35e5aTony Mantler        final Locale configurationLocale = configuration.getLocales().get(0);
415b8592357c37e681ba709b524a7202b7787f35e5aTony Mantler        if (configurationLocale == null) {
416b8592357c37e681ba709b524a7202b7787f35e5aTony Mantler            return Locale.getDefault();
417b8592357c37e681ba709b524a7202b7787f35e5aTony Mantler        }
418b8592357c37e681ba709b524a7202b7787f35e5aTony Mantler        return configurationLocale;
419b8592357c37e681ba709b524a7202b7787f35e5aTony Mantler    }
420b8592357c37e681ba709b524a7202b7787f35e5aTony Mantler}
421