1ab751aa085433e9f735d2e7603459c6c7e9d2fb0satok/*
2ab751aa085433e9f735d2e7603459c6c7e9d2fb0satok * Copyright (C) 2010 The Android Open Source Project
3ab751aa085433e9f735d2e7603459c6c7e9d2fb0satok *
4ab751aa085433e9f735d2e7603459c6c7e9d2fb0satok * Licensed under the Apache License, Version 2.0 (the "License");
5ab751aa085433e9f735d2e7603459c6c7e9d2fb0satok * you may not use this file except in compliance with the License.
6ab751aa085433e9f735d2e7603459c6c7e9d2fb0satok * You may obtain a copy of the License at
7ab751aa085433e9f735d2e7603459c6c7e9d2fb0satok *
8ab751aa085433e9f735d2e7603459c6c7e9d2fb0satok *      http://www.apache.org/licenses/LICENSE-2.0
9ab751aa085433e9f735d2e7603459c6c7e9d2fb0satok *
10ab751aa085433e9f735d2e7603459c6c7e9d2fb0satok * Unless required by applicable law or agreed to in writing, software
11ab751aa085433e9f735d2e7603459c6c7e9d2fb0satok * distributed under the License is distributed on an "AS IS" BASIS,
12ab751aa085433e9f735d2e7603459c6c7e9d2fb0satok * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13ab751aa085433e9f735d2e7603459c6c7e9d2fb0satok * See the License for the specific language governing permissions and
14ab751aa085433e9f735d2e7603459c6c7e9d2fb0satok * limitations under the License.
15ab751aa085433e9f735d2e7603459c6c7e9d2fb0satok */
16ab751aa085433e9f735d2e7603459c6c7e9d2fb0satok
17ab751aa085433e9f735d2e7603459c6c7e9d2fb0satokpackage android.view.inputmethod;
18ab751aa085433e9f735d2e7603459c6c7e9d2fb0satok
197265d9bd6d80c5bedaa6de2b80f6619a301a07c8satokimport android.content.Context;
204f31353cb3b00c77c9420ef27ec949fd570ede3bsatokimport android.content.pm.ApplicationInfo;
21ab751aa085433e9f735d2e7603459c6c7e9d2fb0satokimport android.os.Parcel;
22ab751aa085433e9f735d2e7603459c6c7e9d2fb0satokimport android.os.Parcelable;
234f31353cb3b00c77c9420ef27ec949fd570ede3bsatokimport android.text.TextUtils;
249c4cc03a354922df08efacfc486ef0e80144d3easatokimport android.util.Slog;
25ab751aa085433e9f735d2e7603459c6c7e9d2fb0satok
267265d9bd6d80c5bedaa6de2b80f6619a301a07c8satokimport java.util.ArrayList;
27ab751aa085433e9f735d2e7603459c6c7e9d2fb0satokimport java.util.Arrays;
289c4cc03a354922df08efacfc486ef0e80144d3easatokimport java.util.HashMap;
297265d9bd6d80c5bedaa6de2b80f6619a301a07c8satokimport java.util.HashSet;
3083e675f5ecf9f5615f3179ac102176faa3ae2596satokimport java.util.IllegalFormatException;
317265d9bd6d80c5bedaa6de2b80f6619a301a07c8satokimport java.util.List;
32a9778d4d442db65344e32318b1fd43ab54898389satokimport java.util.Locale;
33ab751aa085433e9f735d2e7603459c6c7e9d2fb0satok
34ab751aa085433e9f735d2e7603459c6c7e9d2fb0satok/**
359a74476c0ab1aaa59e9aed6ecdeba43d5b4d3495Ken Wakasa * This class is used to specify meta information of a subtype contained in an input method editor
369a74476c0ab1aaa59e9aed6ecdeba43d5b4d3495Ken Wakasa * (IME). Subtype can describe locale (e.g. en_US, fr_FR...) and mode (e.g. voice, keyboard...),
379a74476c0ab1aaa59e9aed6ecdeba43d5b4d3495Ken Wakasa * and is used for IME switch and settings. The input method subtype allows the system to bring up
389a74476c0ab1aaa59e9aed6ecdeba43d5b4d3495Ken Wakasa * the specified subtype of the designated IME directly.
3944b75030931d9c65c9e495a86d11d71da59b4429satok *
409a74476c0ab1aaa59e9aed6ecdeba43d5b4d3495Ken Wakasa * <p>It should be defined in an XML resource file of the input method with the
419a74476c0ab1aaa59e9aed6ecdeba43d5b4d3495Ken Wakasa * <code>&lt;subtype&gt;</code> element. For more information, see the guide to
4244b75030931d9c65c9e495a86d11d71da59b4429satok * <a href="{@docRoot}resources/articles/creating-input-method.html">
4344b75030931d9c65c9e495a86d11d71da59b4429satok * Creating an Input Method</a>.</p>
44ab751aa085433e9f735d2e7603459c6c7e9d2fb0satok */
45ab751aa085433e9f735d2e7603459c6c7e9d2fb0satokpublic final class InputMethodSubtype implements Parcelable {
469c4cc03a354922df08efacfc486ef0e80144d3easatok    private static final String TAG = InputMethodSubtype.class.getSimpleName();
479c4cc03a354922df08efacfc486ef0e80144d3easatok    private static final String EXTRA_VALUE_PAIR_SEPARATOR = ",";
489c4cc03a354922df08efacfc486ef0e80144d3easatok    private static final String EXTRA_VALUE_KEY_VALUE_SEPARATOR = "=";
4983e675f5ecf9f5615f3179ac102176faa3ae2596satok    // TODO: remove this
5083e675f5ecf9f5615f3179ac102176faa3ae2596satok    private static final String EXTRA_KEY_UNTRANSLATABLE_STRING_IN_SUBTYPE_NAME =
5183e675f5ecf9f5615f3179ac102176faa3ae2596satok            "UntranslatableReplacementStringInSubtypeName";
529c4cc03a354922df08efacfc486ef0e80144d3easatok
539aabb95781bee6a44684a6f6feb155e115d24983satok    private final boolean mIsAuxiliary;
54a86f5e448cd6d29340ca6cbe509bc6384bc0d711satok    private final boolean mOverridesImplicitlyEnabledSubtype;
559aabb95781bee6a44684a6f6feb155e115d24983satok    private final int mSubtypeHashCode;
56ab751aa085433e9f735d2e7603459c6c7e9d2fb0satok    private final int mSubtypeIconResId;
579aabb95781bee6a44684a6f6feb155e115d24983satok    private final int mSubtypeNameResId;
58e62e6d8731ab1e02c1632ebc011792d07b902af8Satoshi Kataoka    private final int mSubtypeId;
59ab751aa085433e9f735d2e7603459c6c7e9d2fb0satok    private final String mSubtypeLocale;
609ef0283bdcd9534cc09ae37eb2b78771b95247b5satok    private final String mSubtypeMode;
61ab751aa085433e9f735d2e7603459c6c7e9d2fb0satok    private final String mSubtypeExtraValue;
62e52eb4e289bf8d7b04582803b0d0f9ab7399c1afsatok    private volatile HashMap<String, String> mExtraValueHashMapCache;
63ab751aa085433e9f735d2e7603459c6c7e9d2fb0satok
64ab751aa085433e9f735d2e7603459c6c7e9d2fb0satok    /**
65e62e6d8731ab1e02c1632ebc011792d07b902af8Satoshi Kataoka     * Constructor with no subtype ID specified, overridesImplicitlyEnabledSubtype not specified.
669a74476c0ab1aaa59e9aed6ecdeba43d5b4d3495Ken Wakasa     * @param nameId Resource ID of the subtype name string. The string resource may have exactly
679a74476c0ab1aaa59e9aed6ecdeba43d5b4d3495Ken Wakasa     * one %s in it. If there is, the %s part will be replaced with the locale's display name by
689a74476c0ab1aaa59e9aed6ecdeba43d5b4d3495Ken Wakasa     * the formatter. Please refer to {@link #getDisplayName} for details.
699a74476c0ab1aaa59e9aed6ecdeba43d5b4d3495Ken Wakasa     * @param iconId Resource ID of the subtype icon drawable.
70ab751aa085433e9f735d2e7603459c6c7e9d2fb0satok     * @param locale The locale supported by the subtype
71a9778d4d442db65344e32318b1fd43ab54898389satok     * @param mode The mode supported by the subtype
729a74476c0ab1aaa59e9aed6ecdeba43d5b4d3495Ken Wakasa     * @param extraValue The extra value of the subtype. This string is free-form, but the API
739a74476c0ab1aaa59e9aed6ecdeba43d5b4d3495Ken Wakasa     * supplies tools to deal with a key-value comma-separated list; see
749a74476c0ab1aaa59e9aed6ecdeba43d5b4d3495Ken Wakasa     * {@link #containsExtraValueKey} and {@link #getExtraValueOf}.
759a74476c0ab1aaa59e9aed6ecdeba43d5b4d3495Ken Wakasa     * @param isAuxiliary true when this subtype is auxiliary, false otherwise. An auxiliary
769a74476c0ab1aaa59e9aed6ecdeba43d5b4d3495Ken Wakasa     * subtype will not be shown in the list of enabled IMEs for choosing the current IME in
779a74476c0ab1aaa59e9aed6ecdeba43d5b4d3495Ken Wakasa     * the Settings even when this subtype is enabled. Please note that this subtype will still
789a74476c0ab1aaa59e9aed6ecdeba43d5b4d3495Ken Wakasa     * be shown in the list of IMEs in the IME switcher to allow the user to tentatively switch
799a74476c0ab1aaa59e9aed6ecdeba43d5b4d3495Ken Wakasa     * to this subtype while an IME is shown. The framework will never switch the current IME to
809a74476c0ab1aaa59e9aed6ecdeba43d5b4d3495Ken Wakasa     * this subtype by {@link android.view.inputmethod.InputMethodManager#switchToLastInputMethod}.
819a74476c0ab1aaa59e9aed6ecdeba43d5b4d3495Ken Wakasa     * The intent of having this flag is to allow for IMEs that are invoked in a one-shot way as
829a74476c0ab1aaa59e9aed6ecdeba43d5b4d3495Ken Wakasa     * auxiliary input mode, and return to the previous IME once it is finished (e.g. voice input).
833889e492bcad3c6f565d30da4e02d0dcde09d084satok     * @hide
84ab751aa085433e9f735d2e7603459c6c7e9d2fb0satok     */
85a86f5e448cd6d29340ca6cbe509bc6384bc0d711satok    public InputMethodSubtype(int nameId, int iconId, String locale, String mode, String extraValue,
86a86f5e448cd6d29340ca6cbe509bc6384bc0d711satok            boolean isAuxiliary) {
878b83a7297680911d666546ee0b3a464514dc2c27Ken Wakasa        this(nameId, iconId, locale, mode, extraValue, isAuxiliary, false);
889aabb95781bee6a44684a6f6feb155e115d24983satok    }
899aabb95781bee6a44684a6f6feb155e115d24983satok
909aabb95781bee6a44684a6f6feb155e115d24983satok    /**
91e62e6d8731ab1e02c1632ebc011792d07b902af8Satoshi Kataoka     * Constructor with no subtype ID specified.
929a74476c0ab1aaa59e9aed6ecdeba43d5b4d3495Ken Wakasa     * @param nameId Resource ID of the subtype name string. The string resource may have exactly
939a74476c0ab1aaa59e9aed6ecdeba43d5b4d3495Ken Wakasa     * one %s in it. If there is, the %s part will be replaced with the locale's display name by
949a74476c0ab1aaa59e9aed6ecdeba43d5b4d3495Ken Wakasa     * the formatter. Please refer to {@link #getDisplayName} for details.
959a74476c0ab1aaa59e9aed6ecdeba43d5b4d3495Ken Wakasa     * @param iconId Resource ID of the subtype icon drawable.
969aabb95781bee6a44684a6f6feb155e115d24983satok     * @param locale The locale supported by the subtype
97a9778d4d442db65344e32318b1fd43ab54898389satok     * @param mode The mode supported by the subtype
989a74476c0ab1aaa59e9aed6ecdeba43d5b4d3495Ken Wakasa     * @param extraValue The extra value of the subtype. This string is free-form, but the API
999a74476c0ab1aaa59e9aed6ecdeba43d5b4d3495Ken Wakasa     * supplies tools to deal with a key-value comma-separated list; see
1009a74476c0ab1aaa59e9aed6ecdeba43d5b4d3495Ken Wakasa     * {@link #containsExtraValueKey} and {@link #getExtraValueOf}.
1019a74476c0ab1aaa59e9aed6ecdeba43d5b4d3495Ken Wakasa     * @param isAuxiliary true when this subtype is auxiliary, false otherwise. An auxiliary
1029a74476c0ab1aaa59e9aed6ecdeba43d5b4d3495Ken Wakasa     * subtype will not be shown in the list of enabled IMEs for choosing the current IME in
1039a74476c0ab1aaa59e9aed6ecdeba43d5b4d3495Ken Wakasa     * the Settings even when this subtype is enabled. Please note that this subtype will still
1049a74476c0ab1aaa59e9aed6ecdeba43d5b4d3495Ken Wakasa     * be shown in the list of IMEs in the IME switcher to allow the user to tentatively switch
1059a74476c0ab1aaa59e9aed6ecdeba43d5b4d3495Ken Wakasa     * to this subtype while an IME is shown. The framework will never switch the current IME to
1069a74476c0ab1aaa59e9aed6ecdeba43d5b4d3495Ken Wakasa     * this subtype by {@link android.view.inputmethod.InputMethodManager#switchToLastInputMethod}.
1079a74476c0ab1aaa59e9aed6ecdeba43d5b4d3495Ken Wakasa     * The intent of having this flag is to allow for IMEs that are invoked in a one-shot way as
1089a74476c0ab1aaa59e9aed6ecdeba43d5b4d3495Ken Wakasa     * auxiliary input mode, and return to the previous IME once it is finished (e.g. voice input).
1099a74476c0ab1aaa59e9aed6ecdeba43d5b4d3495Ken Wakasa     * @param overridesImplicitlyEnabledSubtype true when this subtype should be enabled by default
1109a74476c0ab1aaa59e9aed6ecdeba43d5b4d3495Ken Wakasa     * if no other subtypes in the IME are enabled explicitly. Note that a subtype with this
1119a74476c0ab1aaa59e9aed6ecdeba43d5b4d3495Ken Wakasa     * parameter being true will not be shown in the list of subtypes in each IME's subtype enabler.
1129a74476c0ab1aaa59e9aed6ecdeba43d5b4d3495Ken Wakasa     * Having an "automatic" subtype is an example use of this flag.
1139aabb95781bee6a44684a6f6feb155e115d24983satok     */
114a9778d4d442db65344e32318b1fd43ab54898389satok    public InputMethodSubtype(int nameId, int iconId, String locale, String mode, String extraValue,
115a86f5e448cd6d29340ca6cbe509bc6384bc0d711satok            boolean isAuxiliary, boolean overridesImplicitlyEnabledSubtype) {
116e62e6d8731ab1e02c1632ebc011792d07b902af8Satoshi Kataoka        this(nameId, iconId, locale, mode, extraValue, isAuxiliary,
117e62e6d8731ab1e02c1632ebc011792d07b902af8Satoshi Kataoka                overridesImplicitlyEnabledSubtype, 0);
118e62e6d8731ab1e02c1632ebc011792d07b902af8Satoshi Kataoka    }
119e62e6d8731ab1e02c1632ebc011792d07b902af8Satoshi Kataoka
120e62e6d8731ab1e02c1632ebc011792d07b902af8Satoshi Kataoka    /**
121e62e6d8731ab1e02c1632ebc011792d07b902af8Satoshi Kataoka     * Constructor.
122e62e6d8731ab1e02c1632ebc011792d07b902af8Satoshi Kataoka     * @param nameId Resource ID of the subtype name string. The string resource may have exactly
123e62e6d8731ab1e02c1632ebc011792d07b902af8Satoshi Kataoka     * one %s in it. If there is, the %s part will be replaced with the locale's display name by
124e62e6d8731ab1e02c1632ebc011792d07b902af8Satoshi Kataoka     * the formatter. Please refer to {@link #getDisplayName} for details.
125e62e6d8731ab1e02c1632ebc011792d07b902af8Satoshi Kataoka     * @param iconId Resource ID of the subtype icon drawable.
126e62e6d8731ab1e02c1632ebc011792d07b902af8Satoshi Kataoka     * @param locale The locale supported by the subtype
127e62e6d8731ab1e02c1632ebc011792d07b902af8Satoshi Kataoka     * @param mode The mode supported by the subtype
128e62e6d8731ab1e02c1632ebc011792d07b902af8Satoshi Kataoka     * @param extraValue The extra value of the subtype. This string is free-form, but the API
129e62e6d8731ab1e02c1632ebc011792d07b902af8Satoshi Kataoka     * supplies tools to deal with a key-value comma-separated list; see
130e62e6d8731ab1e02c1632ebc011792d07b902af8Satoshi Kataoka     * {@link #containsExtraValueKey} and {@link #getExtraValueOf}.
131e62e6d8731ab1e02c1632ebc011792d07b902af8Satoshi Kataoka     * @param isAuxiliary true when this subtype is auxiliary, false otherwise. An auxiliary
132e62e6d8731ab1e02c1632ebc011792d07b902af8Satoshi Kataoka     * subtype will not be shown in the list of enabled IMEs for choosing the current IME in
133e62e6d8731ab1e02c1632ebc011792d07b902af8Satoshi Kataoka     * the Settings even when this subtype is enabled. Please note that this subtype will still
134e62e6d8731ab1e02c1632ebc011792d07b902af8Satoshi Kataoka     * be shown in the list of IMEs in the IME switcher to allow the user to tentatively switch
135e62e6d8731ab1e02c1632ebc011792d07b902af8Satoshi Kataoka     * to this subtype while an IME is shown. The framework will never switch the current IME to
136e62e6d8731ab1e02c1632ebc011792d07b902af8Satoshi Kataoka     * this subtype by {@link android.view.inputmethod.InputMethodManager#switchToLastInputMethod}.
137e62e6d8731ab1e02c1632ebc011792d07b902af8Satoshi Kataoka     * The intent of having this flag is to allow for IMEs that are invoked in a one-shot way as
138e62e6d8731ab1e02c1632ebc011792d07b902af8Satoshi Kataoka     * auxiliary input mode, and return to the previous IME once it is finished (e.g. voice input).
139e62e6d8731ab1e02c1632ebc011792d07b902af8Satoshi Kataoka     * @param overridesImplicitlyEnabledSubtype true when this subtype should be enabled by default
140e62e6d8731ab1e02c1632ebc011792d07b902af8Satoshi Kataoka     * if no other subtypes in the IME are enabled explicitly. Note that a subtype with this
141e62e6d8731ab1e02c1632ebc011792d07b902af8Satoshi Kataoka     * parameter being true will not be shown in the list of subtypes in each IME's subtype enabler.
142e62e6d8731ab1e02c1632ebc011792d07b902af8Satoshi Kataoka     * Having an "automatic" subtype is an example use of this flag.
143e62e6d8731ab1e02c1632ebc011792d07b902af8Satoshi Kataoka     * @param id The unique ID for the subtype. The input method framework keeps track of enabled
144e62e6d8731ab1e02c1632ebc011792d07b902af8Satoshi Kataoka     * subtypes by ID. When the IME package gets upgraded, enabled IDs will stay enabled even if
145e62e6d8731ab1e02c1632ebc011792d07b902af8Satoshi Kataoka     * other attributes are different. If the ID is unspecified or 0,
146e62e6d8731ab1e02c1632ebc011792d07b902af8Satoshi Kataoka     * Arrays.hashCode(new Object[] {locale, mode, extraValue,
147e62e6d8731ab1e02c1632ebc011792d07b902af8Satoshi Kataoka     * isAuxiliary, overridesImplicitlyEnabledSubtype}) will be used instead.
148e62e6d8731ab1e02c1632ebc011792d07b902af8Satoshi Kataoka     */
149e62e6d8731ab1e02c1632ebc011792d07b902af8Satoshi Kataoka    public InputMethodSubtype(int nameId, int iconId, String locale, String mode, String extraValue,
150e62e6d8731ab1e02c1632ebc011792d07b902af8Satoshi Kataoka            boolean isAuxiliary, boolean overridesImplicitlyEnabledSubtype, int id) {
151ab751aa085433e9f735d2e7603459c6c7e9d2fb0satok        mSubtypeNameResId = nameId;
152ab751aa085433e9f735d2e7603459c6c7e9d2fb0satok        mSubtypeIconResId = iconId;
153af4bf400abab86baee44dacbcdf13444d06ee46esatok        mSubtypeLocale = locale != null ? locale : "";
154af4bf400abab86baee44dacbcdf13444d06ee46esatok        mSubtypeMode = mode != null ? mode : "";
155af4bf400abab86baee44dacbcdf13444d06ee46esatok        mSubtypeExtraValue = extraValue != null ? extraValue : "";
1569aabb95781bee6a44684a6f6feb155e115d24983satok        mIsAuxiliary = isAuxiliary;
157a86f5e448cd6d29340ca6cbe509bc6384bc0d711satok        mOverridesImplicitlyEnabledSubtype = overridesImplicitlyEnabledSubtype;
158e62e6d8731ab1e02c1632ebc011792d07b902af8Satoshi Kataoka        // If hashCode() of this subtype is 0 and you want to specify it as an id of this subtype,
159e62e6d8731ab1e02c1632ebc011792d07b902af8Satoshi Kataoka        // just specify 0 as this subtype's id. Then, this subtype's id is treated as 0.
160e62e6d8731ab1e02c1632ebc011792d07b902af8Satoshi Kataoka        mSubtypeHashCode = id != 0 ? id : hashCodeInternal(mSubtypeLocale, mSubtypeMode,
161e62e6d8731ab1e02c1632ebc011792d07b902af8Satoshi Kataoka                mSubtypeExtraValue, mIsAuxiliary, mOverridesImplicitlyEnabledSubtype);
162e62e6d8731ab1e02c1632ebc011792d07b902af8Satoshi Kataoka        mSubtypeId = id;
163ab751aa085433e9f735d2e7603459c6c7e9d2fb0satok    }
164ab751aa085433e9f735d2e7603459c6c7e9d2fb0satok
165ab751aa085433e9f735d2e7603459c6c7e9d2fb0satok    InputMethodSubtype(Parcel source) {
166af4bf400abab86baee44dacbcdf13444d06ee46esatok        String s;
167ab751aa085433e9f735d2e7603459c6c7e9d2fb0satok        mSubtypeNameResId = source.readInt();
168ab751aa085433e9f735d2e7603459c6c7e9d2fb0satok        mSubtypeIconResId = source.readInt();
169af4bf400abab86baee44dacbcdf13444d06ee46esatok        s = source.readString();
170af4bf400abab86baee44dacbcdf13444d06ee46esatok        mSubtypeLocale = s != null ? s : "";
171af4bf400abab86baee44dacbcdf13444d06ee46esatok        s = source.readString();
172af4bf400abab86baee44dacbcdf13444d06ee46esatok        mSubtypeMode = s != null ? s : "";
173af4bf400abab86baee44dacbcdf13444d06ee46esatok        s = source.readString();
174af4bf400abab86baee44dacbcdf13444d06ee46esatok        mSubtypeExtraValue = s != null ? s : "";
1759aabb95781bee6a44684a6f6feb155e115d24983satok        mIsAuxiliary = (source.readInt() == 1);
176a86f5e448cd6d29340ca6cbe509bc6384bc0d711satok        mOverridesImplicitlyEnabledSubtype = (source.readInt() == 1);
177e62e6d8731ab1e02c1632ebc011792d07b902af8Satoshi Kataoka        mSubtypeHashCode = source.readInt();
178e62e6d8731ab1e02c1632ebc011792d07b902af8Satoshi Kataoka        mSubtypeId = source.readInt();
179ab751aa085433e9f735d2e7603459c6c7e9d2fb0satok    }
180ab751aa085433e9f735d2e7603459c6c7e9d2fb0satok
181ab751aa085433e9f735d2e7603459c6c7e9d2fb0satok    /**
1829a74476c0ab1aaa59e9aed6ecdeba43d5b4d3495Ken Wakasa     * @return Resource ID of the subtype name string.
183ab751aa085433e9f735d2e7603459c6c7e9d2fb0satok     */
184ab751aa085433e9f735d2e7603459c6c7e9d2fb0satok    public int getNameResId() {
185ab751aa085433e9f735d2e7603459c6c7e9d2fb0satok        return mSubtypeNameResId;
186ab751aa085433e9f735d2e7603459c6c7e9d2fb0satok    }
187ab751aa085433e9f735d2e7603459c6c7e9d2fb0satok
188ab751aa085433e9f735d2e7603459c6c7e9d2fb0satok    /**
1899a74476c0ab1aaa59e9aed6ecdeba43d5b4d3495Ken Wakasa     * @return Resource ID of the subtype icon drawable.
190ab751aa085433e9f735d2e7603459c6c7e9d2fb0satok     */
191ab751aa085433e9f735d2e7603459c6c7e9d2fb0satok    public int getIconResId() {
192ab751aa085433e9f735d2e7603459c6c7e9d2fb0satok        return mSubtypeIconResId;
193ab751aa085433e9f735d2e7603459c6c7e9d2fb0satok    }
194ab751aa085433e9f735d2e7603459c6c7e9d2fb0satok
195ab751aa085433e9f735d2e7603459c6c7e9d2fb0satok    /**
1969a74476c0ab1aaa59e9aed6ecdeba43d5b4d3495Ken Wakasa     * @return The locale of the subtype. This method returns the "locale" string parameter passed
1979a74476c0ab1aaa59e9aed6ecdeba43d5b4d3495Ken Wakasa     * to the constructor.
198ab751aa085433e9f735d2e7603459c6c7e9d2fb0satok     */
199ab751aa085433e9f735d2e7603459c6c7e9d2fb0satok    public String getLocale() {
200ab751aa085433e9f735d2e7603459c6c7e9d2fb0satok        return mSubtypeLocale;
201ab751aa085433e9f735d2e7603459c6c7e9d2fb0satok    }
202ab751aa085433e9f735d2e7603459c6c7e9d2fb0satok
203ab751aa085433e9f735d2e7603459c6c7e9d2fb0satok    /**
2049a74476c0ab1aaa59e9aed6ecdeba43d5b4d3495Ken Wakasa     * @return The mode of the subtype.
205ab751aa085433e9f735d2e7603459c6c7e9d2fb0satok     */
2069ef0283bdcd9534cc09ae37eb2b78771b95247b5satok    public String getMode() {
2079ef0283bdcd9534cc09ae37eb2b78771b95247b5satok        return mSubtypeMode;
208ab751aa085433e9f735d2e7603459c6c7e9d2fb0satok    }
209ab751aa085433e9f735d2e7603459c6c7e9d2fb0satok
210ab751aa085433e9f735d2e7603459c6c7e9d2fb0satok    /**
2119a74476c0ab1aaa59e9aed6ecdeba43d5b4d3495Ken Wakasa     * @return The extra value of the subtype.
212ab751aa085433e9f735d2e7603459c6c7e9d2fb0satok     */
213ab751aa085433e9f735d2e7603459c6c7e9d2fb0satok    public String getExtraValue() {
214ab751aa085433e9f735d2e7603459c6c7e9d2fb0satok        return mSubtypeExtraValue;
215ab751aa085433e9f735d2e7603459c6c7e9d2fb0satok    }
216ab751aa085433e9f735d2e7603459c6c7e9d2fb0satok
2179aabb95781bee6a44684a6f6feb155e115d24983satok    /**
2189a74476c0ab1aaa59e9aed6ecdeba43d5b4d3495Ken Wakasa     * @return true if this subtype is auxiliary, false otherwise. An auxiliary subtype will not be
2199a74476c0ab1aaa59e9aed6ecdeba43d5b4d3495Ken Wakasa     * shown in the list of enabled IMEs for choosing the current IME in the Settings even when this
2209a74476c0ab1aaa59e9aed6ecdeba43d5b4d3495Ken Wakasa     * subtype is enabled. Please note that this subtype will still be shown in the list of IMEs in
2219a74476c0ab1aaa59e9aed6ecdeba43d5b4d3495Ken Wakasa     * the IME switcher to allow the user to tentatively switch to this subtype while an IME is
2229a74476c0ab1aaa59e9aed6ecdeba43d5b4d3495Ken Wakasa     * shown. The framework will never switch the current IME to this subtype by
2239a74476c0ab1aaa59e9aed6ecdeba43d5b4d3495Ken Wakasa     * {@link android.view.inputmethod.InputMethodManager#switchToLastInputMethod}.
2249a74476c0ab1aaa59e9aed6ecdeba43d5b4d3495Ken Wakasa     * The intent of having this flag is to allow for IMEs that are invoked in a one-shot way as
2259a74476c0ab1aaa59e9aed6ecdeba43d5b4d3495Ken Wakasa     * auxiliary input mode, and return to the previous IME once it is finished (e.g. voice input).
2269aabb95781bee6a44684a6f6feb155e115d24983satok     */
2279aabb95781bee6a44684a6f6feb155e115d24983satok    public boolean isAuxiliary() {
2289aabb95781bee6a44684a6f6feb155e115d24983satok        return mIsAuxiliary;
2299aabb95781bee6a44684a6f6feb155e115d24983satok    }
2309aabb95781bee6a44684a6f6feb155e115d24983satok
2314f31353cb3b00c77c9420ef27ec949fd570ede3bsatok    /**
2329a74476c0ab1aaa59e9aed6ecdeba43d5b4d3495Ken Wakasa     * @return true when this subtype will be enabled by default if no other subtypes in the IME
2339a74476c0ab1aaa59e9aed6ecdeba43d5b4d3495Ken Wakasa     * are enabled explicitly, false otherwise. Note that a subtype with this method returning true
2349a74476c0ab1aaa59e9aed6ecdeba43d5b4d3495Ken Wakasa     * will not be shown in the list of subtypes in each IME's subtype enabler. Having an
2359a74476c0ab1aaa59e9aed6ecdeba43d5b4d3495Ken Wakasa     * "automatic" subtype is an example use of this flag.
236a86f5e448cd6d29340ca6cbe509bc6384bc0d711satok     */
237a86f5e448cd6d29340ca6cbe509bc6384bc0d711satok    public boolean overridesImplicitlyEnabledSubtype() {
238a86f5e448cd6d29340ca6cbe509bc6384bc0d711satok        return mOverridesImplicitlyEnabledSubtype;
239a86f5e448cd6d29340ca6cbe509bc6384bc0d711satok    }
240a86f5e448cd6d29340ca6cbe509bc6384bc0d711satok
241a86f5e448cd6d29340ca6cbe509bc6384bc0d711satok    /**
2424f31353cb3b00c77c9420ef27ec949fd570ede3bsatok     * @param context Context will be used for getting Locale and PackageManager.
2434f31353cb3b00c77c9420ef27ec949fd570ede3bsatok     * @param packageName The package name of the IME
2444f31353cb3b00c77c9420ef27ec949fd570ede3bsatok     * @param appInfo The application info of the IME
2454f31353cb3b00c77c9420ef27ec949fd570ede3bsatok     * @return a display name for this subtype. The string resource of the label (mSubtypeNameResId)
2469a74476c0ab1aaa59e9aed6ecdeba43d5b4d3495Ken Wakasa     * may have exactly one %s in it. If there is, the %s part will be replaced with the locale's
2479a74476c0ab1aaa59e9aed6ecdeba43d5b4d3495Ken Wakasa     * display name by the formatter. If there is not, this method returns the string specified by
2489a74476c0ab1aaa59e9aed6ecdeba43d5b4d3495Ken Wakasa     * mSubtypeNameResId. If mSubtypeNameResId is not specified (== 0), it's up to the framework to
2499a74476c0ab1aaa59e9aed6ecdeba43d5b4d3495Ken Wakasa     * generate an appropriate display name.
2504f31353cb3b00c77c9420ef27ec949fd570ede3bsatok     */
2514f31353cb3b00c77c9420ef27ec949fd570ede3bsatok    public CharSequence getDisplayName(
2524f31353cb3b00c77c9420ef27ec949fd570ede3bsatok            Context context, String packageName, ApplicationInfo appInfo) {
253a9778d4d442db65344e32318b1fd43ab54898389satok        final Locale locale = constructLocaleFromString(mSubtypeLocale);
254a9778d4d442db65344e32318b1fd43ab54898389satok        final String localeStr = locale != null ? locale.getDisplayName() : mSubtypeLocale;
2554f31353cb3b00c77c9420ef27ec949fd570ede3bsatok        if (mSubtypeNameResId == 0) {
256a9778d4d442db65344e32318b1fd43ab54898389satok            return localeStr;
2574f31353cb3b00c77c9420ef27ec949fd570ede3bsatok        }
25835412d63ee59b99a43725e466c5deef52b2324dfsatok        final CharSequence subtypeName = context.getPackageManager().getText(
25935412d63ee59b99a43725e466c5deef52b2324dfsatok                packageName, mSubtypeNameResId, appInfo);
2604f31353cb3b00c77c9420ef27ec949fd570ede3bsatok        if (!TextUtils.isEmpty(subtypeName)) {
26183e675f5ecf9f5615f3179ac102176faa3ae2596satok            final String replacementString =
26283e675f5ecf9f5615f3179ac102176faa3ae2596satok                    containsExtraValueKey(EXTRA_KEY_UNTRANSLATABLE_STRING_IN_SUBTYPE_NAME)
26383e675f5ecf9f5615f3179ac102176faa3ae2596satok                            ? getExtraValueOf(EXTRA_KEY_UNTRANSLATABLE_STRING_IN_SUBTYPE_NAME)
26483e675f5ecf9f5615f3179ac102176faa3ae2596satok                            : localeStr;
26583e675f5ecf9f5615f3179ac102176faa3ae2596satok            try {
26683e675f5ecf9f5615f3179ac102176faa3ae2596satok                return String.format(
26783e675f5ecf9f5615f3179ac102176faa3ae2596satok                        subtypeName.toString(), replacementString != null ? replacementString : "");
26883e675f5ecf9f5615f3179ac102176faa3ae2596satok            } catch (IllegalFormatException e) {
26983e675f5ecf9f5615f3179ac102176faa3ae2596satok                Slog.w(TAG, "Found illegal format in subtype name("+ subtypeName + "): " + e);
27083e675f5ecf9f5615f3179ac102176faa3ae2596satok                return "";
27183e675f5ecf9f5615f3179ac102176faa3ae2596satok            }
2724f31353cb3b00c77c9420ef27ec949fd570ede3bsatok        } else {
273a9778d4d442db65344e32318b1fd43ab54898389satok            return localeStr;
2744f31353cb3b00c77c9420ef27ec949fd570ede3bsatok        }
2754f31353cb3b00c77c9420ef27ec949fd570ede3bsatok    }
2764f31353cb3b00c77c9420ef27ec949fd570ede3bsatok
2779c4cc03a354922df08efacfc486ef0e80144d3easatok    private HashMap<String, String> getExtraValueHashMap() {
2789c4cc03a354922df08efacfc486ef0e80144d3easatok        if (mExtraValueHashMapCache == null) {
279e52eb4e289bf8d7b04582803b0d0f9ab7399c1afsatok            synchronized(this) {
280e52eb4e289bf8d7b04582803b0d0f9ab7399c1afsatok                if (mExtraValueHashMapCache == null) {
281e52eb4e289bf8d7b04582803b0d0f9ab7399c1afsatok                    mExtraValueHashMapCache = new HashMap<String, String>();
282e52eb4e289bf8d7b04582803b0d0f9ab7399c1afsatok                    final String[] pairs = mSubtypeExtraValue.split(EXTRA_VALUE_PAIR_SEPARATOR);
283e52eb4e289bf8d7b04582803b0d0f9ab7399c1afsatok                    final int N = pairs.length;
284e52eb4e289bf8d7b04582803b0d0f9ab7399c1afsatok                    for (int i = 0; i < N; ++i) {
285e52eb4e289bf8d7b04582803b0d0f9ab7399c1afsatok                        final String[] pair = pairs[i].split(EXTRA_VALUE_KEY_VALUE_SEPARATOR);
286e52eb4e289bf8d7b04582803b0d0f9ab7399c1afsatok                        if (pair.length == 1) {
287e52eb4e289bf8d7b04582803b0d0f9ab7399c1afsatok                            mExtraValueHashMapCache.put(pair[0], null);
288e52eb4e289bf8d7b04582803b0d0f9ab7399c1afsatok                        } else if (pair.length > 1) {
289e52eb4e289bf8d7b04582803b0d0f9ab7399c1afsatok                            if (pair.length > 2) {
290e52eb4e289bf8d7b04582803b0d0f9ab7399c1afsatok                                Slog.w(TAG, "ExtraValue has two or more '='s");
291e52eb4e289bf8d7b04582803b0d0f9ab7399c1afsatok                            }
292e52eb4e289bf8d7b04582803b0d0f9ab7399c1afsatok                            mExtraValueHashMapCache.put(pair[0], pair[1]);
293e52eb4e289bf8d7b04582803b0d0f9ab7399c1afsatok                        }
2949c4cc03a354922df08efacfc486ef0e80144d3easatok                    }
2959c4cc03a354922df08efacfc486ef0e80144d3easatok                }
2969c4cc03a354922df08efacfc486ef0e80144d3easatok            }
2979c4cc03a354922df08efacfc486ef0e80144d3easatok        }
2989c4cc03a354922df08efacfc486ef0e80144d3easatok        return mExtraValueHashMapCache;
2999c4cc03a354922df08efacfc486ef0e80144d3easatok    }
3009c4cc03a354922df08efacfc486ef0e80144d3easatok
3019c4cc03a354922df08efacfc486ef0e80144d3easatok    /**
3029c4cc03a354922df08efacfc486ef0e80144d3easatok     * The string of ExtraValue in subtype should be defined as follows:
3039c4cc03a354922df08efacfc486ef0e80144d3easatok     * example: key0,key1=value1,key2,key3,key4=value4
3049a74476c0ab1aaa59e9aed6ecdeba43d5b4d3495Ken Wakasa     * @param key The key of extra value
3059a74476c0ab1aaa59e9aed6ecdeba43d5b4d3495Ken Wakasa     * @return The subtype contains specified the extra value
3069c4cc03a354922df08efacfc486ef0e80144d3easatok     */
3079c4cc03a354922df08efacfc486ef0e80144d3easatok    public boolean containsExtraValueKey(String key) {
3089c4cc03a354922df08efacfc486ef0e80144d3easatok        return getExtraValueHashMap().containsKey(key);
3099c4cc03a354922df08efacfc486ef0e80144d3easatok    }
3109c4cc03a354922df08efacfc486ef0e80144d3easatok
3119c4cc03a354922df08efacfc486ef0e80144d3easatok    /**
3129c4cc03a354922df08efacfc486ef0e80144d3easatok     * The string of ExtraValue in subtype should be defined as follows:
3139c4cc03a354922df08efacfc486ef0e80144d3easatok     * example: key0,key1=value1,key2,key3,key4=value4
3149a74476c0ab1aaa59e9aed6ecdeba43d5b4d3495Ken Wakasa     * @param key The key of extra value
3159a74476c0ab1aaa59e9aed6ecdeba43d5b4d3495Ken Wakasa     * @return The value of the specified key
3169c4cc03a354922df08efacfc486ef0e80144d3easatok     */
3179c4cc03a354922df08efacfc486ef0e80144d3easatok    public String getExtraValueOf(String key) {
3189c4cc03a354922df08efacfc486ef0e80144d3easatok        return getExtraValueHashMap().get(key);
3199c4cc03a354922df08efacfc486ef0e80144d3easatok    }
3209c4cc03a354922df08efacfc486ef0e80144d3easatok
321ab751aa085433e9f735d2e7603459c6c7e9d2fb0satok    @Override
322ab751aa085433e9f735d2e7603459c6c7e9d2fb0satok    public int hashCode() {
323ab751aa085433e9f735d2e7603459c6c7e9d2fb0satok        return mSubtypeHashCode;
324ab751aa085433e9f735d2e7603459c6c7e9d2fb0satok    }
325ab751aa085433e9f735d2e7603459c6c7e9d2fb0satok
326ab751aa085433e9f735d2e7603459c6c7e9d2fb0satok    @Override
327ab751aa085433e9f735d2e7603459c6c7e9d2fb0satok    public boolean equals(Object o) {
328ab751aa085433e9f735d2e7603459c6c7e9d2fb0satok        if (o instanceof InputMethodSubtype) {
329ab751aa085433e9f735d2e7603459c6c7e9d2fb0satok            InputMethodSubtype subtype = (InputMethodSubtype) o;
330e62e6d8731ab1e02c1632ebc011792d07b902af8Satoshi Kataoka            if (subtype.mSubtypeId != 0 || mSubtypeId != 0) {
331e62e6d8731ab1e02c1632ebc011792d07b902af8Satoshi Kataoka                return (subtype.hashCode() == hashCode());
332e62e6d8731ab1e02c1632ebc011792d07b902af8Satoshi Kataoka            }
333af4bf400abab86baee44dacbcdf13444d06ee46esatok            return (subtype.hashCode() == hashCode())
334af4bf400abab86baee44dacbcdf13444d06ee46esatok                && (subtype.getNameResId() == getNameResId())
335af4bf400abab86baee44dacbcdf13444d06ee46esatok                && (subtype.getMode().equals(getMode()))
336ab751aa085433e9f735d2e7603459c6c7e9d2fb0satok                && (subtype.getIconResId() == getIconResId())
337ab751aa085433e9f735d2e7603459c6c7e9d2fb0satok                && (subtype.getLocale().equals(getLocale()))
338a9778d4d442db65344e32318b1fd43ab54898389satok                && (subtype.getExtraValue().equals(getExtraValue()))
339a9778d4d442db65344e32318b1fd43ab54898389satok                && (subtype.isAuxiliary() == isAuxiliary());
340ab751aa085433e9f735d2e7603459c6c7e9d2fb0satok        }
341ab751aa085433e9f735d2e7603459c6c7e9d2fb0satok        return false;
342ab751aa085433e9f735d2e7603459c6c7e9d2fb0satok    }
343ab751aa085433e9f735d2e7603459c6c7e9d2fb0satok
3449aabb95781bee6a44684a6f6feb155e115d24983satok    @Override
345ab751aa085433e9f735d2e7603459c6c7e9d2fb0satok    public int describeContents() {
346ab751aa085433e9f735d2e7603459c6c7e9d2fb0satok        return 0;
347ab751aa085433e9f735d2e7603459c6c7e9d2fb0satok    }
348ab751aa085433e9f735d2e7603459c6c7e9d2fb0satok
3499aabb95781bee6a44684a6f6feb155e115d24983satok    @Override
350ab751aa085433e9f735d2e7603459c6c7e9d2fb0satok    public void writeToParcel(Parcel dest, int parcelableFlags) {
351ab751aa085433e9f735d2e7603459c6c7e9d2fb0satok        dest.writeInt(mSubtypeNameResId);
352ab751aa085433e9f735d2e7603459c6c7e9d2fb0satok        dest.writeInt(mSubtypeIconResId);
353ab751aa085433e9f735d2e7603459c6c7e9d2fb0satok        dest.writeString(mSubtypeLocale);
3549ef0283bdcd9534cc09ae37eb2b78771b95247b5satok        dest.writeString(mSubtypeMode);
355ab751aa085433e9f735d2e7603459c6c7e9d2fb0satok        dest.writeString(mSubtypeExtraValue);
3569aabb95781bee6a44684a6f6feb155e115d24983satok        dest.writeInt(mIsAuxiliary ? 1 : 0);
357a86f5e448cd6d29340ca6cbe509bc6384bc0d711satok        dest.writeInt(mOverridesImplicitlyEnabledSubtype ? 1 : 0);
358e62e6d8731ab1e02c1632ebc011792d07b902af8Satoshi Kataoka        dest.writeInt(mSubtypeHashCode);
359e62e6d8731ab1e02c1632ebc011792d07b902af8Satoshi Kataoka        dest.writeInt(mSubtypeId);
360ab751aa085433e9f735d2e7603459c6c7e9d2fb0satok    }
361ab751aa085433e9f735d2e7603459c6c7e9d2fb0satok
362ab751aa085433e9f735d2e7603459c6c7e9d2fb0satok    public static final Parcelable.Creator<InputMethodSubtype> CREATOR
363ab751aa085433e9f735d2e7603459c6c7e9d2fb0satok            = new Parcelable.Creator<InputMethodSubtype>() {
3649aabb95781bee6a44684a6f6feb155e115d24983satok        @Override
365ab751aa085433e9f735d2e7603459c6c7e9d2fb0satok        public InputMethodSubtype createFromParcel(Parcel source) {
366ab751aa085433e9f735d2e7603459c6c7e9d2fb0satok            return new InputMethodSubtype(source);
367ab751aa085433e9f735d2e7603459c6c7e9d2fb0satok        }
368ab751aa085433e9f735d2e7603459c6c7e9d2fb0satok
3699aabb95781bee6a44684a6f6feb155e115d24983satok        @Override
370ab751aa085433e9f735d2e7603459c6c7e9d2fb0satok        public InputMethodSubtype[] newArray(int size) {
371ab751aa085433e9f735d2e7603459c6c7e9d2fb0satok            return new InputMethodSubtype[size];
372ab751aa085433e9f735d2e7603459c6c7e9d2fb0satok        }
373ab751aa085433e9f735d2e7603459c6c7e9d2fb0satok    };
374ab751aa085433e9f735d2e7603459c6c7e9d2fb0satok
375a9778d4d442db65344e32318b1fd43ab54898389satok    private static Locale constructLocaleFromString(String localeStr) {
376a9778d4d442db65344e32318b1fd43ab54898389satok        if (TextUtils.isEmpty(localeStr))
377a9778d4d442db65344e32318b1fd43ab54898389satok            return null;
378a9778d4d442db65344e32318b1fd43ab54898389satok        String[] localeParams = localeStr.split("_", 3);
379a9778d4d442db65344e32318b1fd43ab54898389satok        // The length of localeStr is guaranteed to always return a 1 <= value <= 3
380a9778d4d442db65344e32318b1fd43ab54898389satok        // because localeStr is not empty.
381a9778d4d442db65344e32318b1fd43ab54898389satok        if (localeParams.length == 1) {
382a9778d4d442db65344e32318b1fd43ab54898389satok            return new Locale(localeParams[0]);
383a9778d4d442db65344e32318b1fd43ab54898389satok        } else if (localeParams.length == 2) {
384a9778d4d442db65344e32318b1fd43ab54898389satok            return new Locale(localeParams[0], localeParams[1]);
385a9778d4d442db65344e32318b1fd43ab54898389satok        } else if (localeParams.length == 3) {
386a9778d4d442db65344e32318b1fd43ab54898389satok            return new Locale(localeParams[0], localeParams[1], localeParams[2]);
387a9778d4d442db65344e32318b1fd43ab54898389satok        }
388a9778d4d442db65344e32318b1fd43ab54898389satok        return null;
389a9778d4d442db65344e32318b1fd43ab54898389satok    }
390a9778d4d442db65344e32318b1fd43ab54898389satok
391a9778d4d442db65344e32318b1fd43ab54898389satok    private static int hashCodeInternal(String locale, String mode, String extraValue,
392a86f5e448cd6d29340ca6cbe509bc6384bc0d711satok            boolean isAuxiliary, boolean overridesImplicitlyEnabledSubtype) {
393a86f5e448cd6d29340ca6cbe509bc6384bc0d711satok        return Arrays.hashCode(new Object[] {locale, mode, extraValue, isAuxiliary,
394a86f5e448cd6d29340ca6cbe509bc6384bc0d711satok                overridesImplicitlyEnabledSubtype});
395ab751aa085433e9f735d2e7603459c6c7e9d2fb0satok    }
3967265d9bd6d80c5bedaa6de2b80f6619a301a07c8satok
3977265d9bd6d80c5bedaa6de2b80f6619a301a07c8satok    /**
3987265d9bd6d80c5bedaa6de2b80f6619a301a07c8satok     * Sort the list of InputMethodSubtype
3997265d9bd6d80c5bedaa6de2b80f6619a301a07c8satok     * @param context Context will be used for getting localized strings from IME
4007265d9bd6d80c5bedaa6de2b80f6619a301a07c8satok     * @param flags Flags for the sort order
4017265d9bd6d80c5bedaa6de2b80f6619a301a07c8satok     * @param imi InputMethodInfo of which subtypes are subject to be sorted
4027265d9bd6d80c5bedaa6de2b80f6619a301a07c8satok     * @param subtypeList List of InputMethodSubtype which will be sorted
4037265d9bd6d80c5bedaa6de2b80f6619a301a07c8satok     * @return Sorted list of subtypes
4047265d9bd6d80c5bedaa6de2b80f6619a301a07c8satok     * @hide
4057265d9bd6d80c5bedaa6de2b80f6619a301a07c8satok     */
4067265d9bd6d80c5bedaa6de2b80f6619a301a07c8satok    public static List<InputMethodSubtype> sort(Context context, int flags, InputMethodInfo imi,
4077265d9bd6d80c5bedaa6de2b80f6619a301a07c8satok            List<InputMethodSubtype> subtypeList) {
4087265d9bd6d80c5bedaa6de2b80f6619a301a07c8satok        if (imi == null) return subtypeList;
4097265d9bd6d80c5bedaa6de2b80f6619a301a07c8satok        final HashSet<InputMethodSubtype> inputSubtypesSet = new HashSet<InputMethodSubtype>(
4107265d9bd6d80c5bedaa6de2b80f6619a301a07c8satok                subtypeList);
4117265d9bd6d80c5bedaa6de2b80f6619a301a07c8satok        final ArrayList<InputMethodSubtype> sortedList = new ArrayList<InputMethodSubtype>();
4127265d9bd6d80c5bedaa6de2b80f6619a301a07c8satok        int N = imi.getSubtypeCount();
4137265d9bd6d80c5bedaa6de2b80f6619a301a07c8satok        for (int i = 0; i < N; ++i) {
4147265d9bd6d80c5bedaa6de2b80f6619a301a07c8satok            InputMethodSubtype subtype = imi.getSubtypeAt(i);
4157265d9bd6d80c5bedaa6de2b80f6619a301a07c8satok            if (inputSubtypesSet.contains(subtype)) {
4167265d9bd6d80c5bedaa6de2b80f6619a301a07c8satok                sortedList.add(subtype);
4177265d9bd6d80c5bedaa6de2b80f6619a301a07c8satok                inputSubtypesSet.remove(subtype);
4187265d9bd6d80c5bedaa6de2b80f6619a301a07c8satok            }
4197265d9bd6d80c5bedaa6de2b80f6619a301a07c8satok        }
4207265d9bd6d80c5bedaa6de2b80f6619a301a07c8satok        // If subtypes in inputSubtypesSet remain, that means these subtypes are not
4217265d9bd6d80c5bedaa6de2b80f6619a301a07c8satok        // contained in imi, so the remaining subtypes will be appended.
4227265d9bd6d80c5bedaa6de2b80f6619a301a07c8satok        for (InputMethodSubtype subtype: inputSubtypesSet) {
4237265d9bd6d80c5bedaa6de2b80f6619a301a07c8satok            sortedList.add(subtype);
4247265d9bd6d80c5bedaa6de2b80f6619a301a07c8satok        }
4257265d9bd6d80c5bedaa6de2b80f6619a301a07c8satok        return sortedList;
4267265d9bd6d80c5bedaa6de2b80f6619a301a07c8satok    }
427af4bf400abab86baee44dacbcdf13444d06ee46esatok}
428