Home | History | Annotate | Download | only in inputmethod
      1 /*
      2  * Copyright (C) 2010 The Android Open Source Project
      3  *
      4  * Licensed under the Apache License, Version 2.0 (the "License");
      5  * you may not use this file except in compliance with the License.
      6  * You may obtain a copy of the License at
      7  *
      8  *      http://www.apache.org/licenses/LICENSE-2.0
      9  *
     10  * Unless required by applicable law or agreed to in writing, software
     11  * distributed under the License is distributed on an "AS IS" BASIS,
     12  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
     13  * See the License for the specific language governing permissions and
     14  * limitations under the License.
     15  */
     16 
     17 package android.view.inputmethod;
     18 
     19 import android.content.Context;
     20 import android.content.pm.ApplicationInfo;
     21 import android.os.Parcel;
     22 import android.os.Parcelable;
     23 import android.text.TextUtils;
     24 import android.util.Slog;
     25 
     26 import java.util.ArrayList;
     27 import java.util.Arrays;
     28 import java.util.HashMap;
     29 import java.util.HashSet;
     30 import java.util.IllegalFormatException;
     31 import java.util.List;
     32 import java.util.Locale;
     33 
     34 /**
     35  * This class is used to specify meta information of a subtype contained in an input method editor
     36  * (IME). Subtype can describe locale (e.g. en_US, fr_FR...) and mode (e.g. voice, keyboard...),
     37  * and is used for IME switch and settings. The input method subtype allows the system to bring up
     38  * the specified subtype of the designated IME directly.
     39  *
     40  * <p>It should be defined in an XML resource file of the input method with the
     41  * <code>&lt;subtype&gt;</code> element, which resides within an {@code &lt;input-method>} element.
     42  * For more information, see the guide to
     43  * <a href="{@docRoot}guide/topics/text/creating-input-method.html">
     44  * Creating an Input Method</a>.</p>
     45  *
     46  * @see InputMethodInfo
     47  *
     48  * @attr ref android.R.styleable#InputMethod_Subtype_label
     49  * @attr ref android.R.styleable#InputMethod_Subtype_icon
     50  * @attr ref android.R.styleable#InputMethod_Subtype_imeSubtypeLocale
     51  * @attr ref android.R.styleable#InputMethod_Subtype_imeSubtypeMode
     52  * @attr ref android.R.styleable#InputMethod_Subtype_imeSubtypeExtraValue
     53  * @attr ref android.R.styleable#InputMethod_Subtype_isAuxiliary
     54  * @attr ref android.R.styleable#InputMethod_Subtype_overridesImplicitlyEnabledSubtype
     55  * @attr ref android.R.styleable#InputMethod_Subtype_subtypeId
     56  * @attr ref android.R.styleable#InputMethod_Subtype_isAsciiCapable
     57  */
     58 public final class InputMethodSubtype implements Parcelable {
     59     private static final String TAG = InputMethodSubtype.class.getSimpleName();
     60     private static final String EXTRA_VALUE_PAIR_SEPARATOR = ",";
     61     private static final String EXTRA_VALUE_KEY_VALUE_SEPARATOR = "=";
     62     // TODO: remove this
     63     private static final String EXTRA_KEY_UNTRANSLATABLE_STRING_IN_SUBTYPE_NAME =
     64             "UntranslatableReplacementStringInSubtypeName";
     65 
     66     private final boolean mIsAuxiliary;
     67     private final boolean mOverridesImplicitlyEnabledSubtype;
     68     private final boolean mIsAsciiCapable;
     69     private final int mSubtypeHashCode;
     70     private final int mSubtypeIconResId;
     71     private final int mSubtypeNameResId;
     72     private final int mSubtypeId;
     73     private final String mSubtypeLocale;
     74     private final String mSubtypeMode;
     75     private final String mSubtypeExtraValue;
     76     private volatile HashMap<String, String> mExtraValueHashMapCache;
     77 
     78     /**
     79      * InputMethodSubtypeBuilder is a builder class of InputMethodSubtype.
     80      * This class is designed to be used with
     81      * {@link android.view.inputmethod.InputMethodManager#setAdditionalInputMethodSubtypes}.
     82      * The developer needs to be aware of what each parameter means.
     83      */
     84     public static class InputMethodSubtypeBuilder {
     85         /**
     86          * @param isAuxiliary should true when this subtype is auxiliary, false otherwise.
     87          * An auxiliary subtype has the following differences with a regular subtype:
     88          * - An auxiliary subtype cannot be chosen as the default IME in Settings.
     89          * - The framework will never switch to this subtype through
     90          *   {@link android.view.inputmethod.InputMethodManager#switchToLastInputMethod}.
     91          * Note that the subtype will still be available in the IME switcher.
     92          * The intent is to allow for IMEs to specify they are meant to be invoked temporarily
     93          * in a one-shot way, and to return to the previous IME once finished (e.g. voice input).
     94          */
     95         public InputMethodSubtypeBuilder setIsAuxiliary(boolean isAuxiliary) {
     96             mIsAuxiliary = isAuxiliary;
     97             return this;
     98         }
     99         private boolean mIsAuxiliary = false;
    100 
    101         /**
    102          * @param overridesImplicitlyEnabledSubtype should be true if this subtype should be
    103          * enabled by default if no other subtypes in the IME are enabled explicitly. Note that a
    104          * subtype with this parameter set will not be shown in the list of subtypes in each IME's
    105          * subtype enabler. A canonical use of this would be for an IME to supply an "automatic"
    106          * subtype that adapts to the current system language.
    107          */
    108         public InputMethodSubtypeBuilder setOverridesImplicitlyEnabledSubtype(
    109                 boolean overridesImplicitlyEnabledSubtype) {
    110             mOverridesImplicitlyEnabledSubtype = overridesImplicitlyEnabledSubtype;
    111             return this;
    112         }
    113         private boolean mOverridesImplicitlyEnabledSubtype = false;
    114 
    115         /**
    116          * @param isAsciiCapable should be true if this subtype is ASCII capable. If the subtype
    117          * is ASCII capable, it should guarantee that the user can input ASCII characters with
    118          * this subtype. This is important because many password fields only allow
    119          * ASCII-characters.
    120          */
    121         public InputMethodSubtypeBuilder setIsAsciiCapable(boolean isAsciiCapable) {
    122             mIsAsciiCapable = isAsciiCapable;
    123             return this;
    124         }
    125         private boolean mIsAsciiCapable = false;
    126 
    127         /**
    128          * @param subtypeIconResId is a resource ID of the subtype icon drawable.
    129          */
    130         public InputMethodSubtypeBuilder setSubtypeIconResId(int subtypeIconResId) {
    131             mSubtypeIconResId = subtypeIconResId;
    132             return this;
    133         }
    134         private int mSubtypeIconResId = 0;
    135 
    136         /**
    137          * @param subtypeNameResId is the resource ID of the subtype name string.
    138          * The string resource may have exactly one %s in it. If present,
    139          * the %s part will be replaced with the locale's display name by
    140          * the formatter. Please refer to {@link #getDisplayName} for details.
    141          */
    142         public InputMethodSubtypeBuilder setSubtypeNameResId(int subtypeNameResId) {
    143             mSubtypeNameResId = subtypeNameResId;
    144             return this;
    145         }
    146         private int mSubtypeNameResId = 0;
    147 
    148         /**
    149          * @param subtypeId is the unique ID for this subtype. The input method framework keeps
    150          * track of enabled subtypes by ID. When the IME package gets upgraded, enabled IDs will
    151          * stay enabled even if other attributes are different. If the ID is unspecified or 0,
    152          * Arrays.hashCode(new Object[] {locale, mode, extraValue,
    153          * isAuxiliary, overridesImplicitlyEnabledSubtype}) will be used instead.
    154          */
    155         public InputMethodSubtypeBuilder setSubtypeId(int subtypeId) {
    156             mSubtypeId = subtypeId;
    157             return this;
    158         }
    159         private int mSubtypeId = 0;
    160 
    161         /**
    162          * @param subtypeLocale is the locale supported by this subtype.
    163          */
    164         public InputMethodSubtypeBuilder setSubtypeLocale(String subtypeLocale) {
    165             mSubtypeLocale = subtypeLocale == null ? "" : subtypeLocale;
    166             return this;
    167         }
    168         private String mSubtypeLocale = "";
    169 
    170         /**
    171          * @param subtypeMode is the mode supported by this subtype.
    172          */
    173         public InputMethodSubtypeBuilder setSubtypeMode(String subtypeMode) {
    174             mSubtypeMode = subtypeMode == null ? "" : subtypeMode;
    175             return this;
    176         }
    177         private String mSubtypeMode = "";
    178         /**
    179          * @param subtypeExtraValue is the extra value of the subtype. This string is free-form,
    180          * but the API supplies tools to deal with a key-value comma-separated list; see
    181          * {@link #containsExtraValueKey} and {@link #getExtraValueOf}.
    182          */
    183         public InputMethodSubtypeBuilder setSubtypeExtraValue(String subtypeExtraValue) {
    184             mSubtypeExtraValue = subtypeExtraValue == null ? "" : subtypeExtraValue;
    185             return this;
    186         }
    187         private String mSubtypeExtraValue = "";
    188 
    189         /**
    190          * @return InputMethodSubtype using parameters in this InputMethodSubtypeBuilder.
    191          */
    192         public InputMethodSubtype build() {
    193             return new InputMethodSubtype(this);
    194         }
    195      }
    196 
    197      private static InputMethodSubtypeBuilder getBuilder(int nameId, int iconId, String locale,
    198              String mode, String extraValue, boolean isAuxiliary,
    199              boolean overridesImplicitlyEnabledSubtype, int id, boolean isAsciiCapable) {
    200          final InputMethodSubtypeBuilder builder = new InputMethodSubtypeBuilder();
    201          builder.mSubtypeNameResId = nameId;
    202          builder.mSubtypeIconResId = iconId;
    203          builder.mSubtypeLocale = locale;
    204          builder.mSubtypeMode = mode;
    205          builder.mSubtypeExtraValue = extraValue;
    206          builder.mIsAuxiliary = isAuxiliary;
    207          builder.mOverridesImplicitlyEnabledSubtype = overridesImplicitlyEnabledSubtype;
    208          builder.mSubtypeId = id;
    209          builder.mIsAsciiCapable = isAsciiCapable;
    210          return builder;
    211      }
    212 
    213     /**
    214      * Constructor with no subtype ID specified, overridesImplicitlyEnabledSubtype not specified.
    215      * Arguments for this constructor have the same meanings as
    216      * {@link InputMethodSubtype#InputMethodSubtype(int, int, String, String, String, boolean,
    217      * boolean, int)} except "id" and "overridesImplicitlyEnabledSubtype".
    218      * @hide
    219      */
    220     public InputMethodSubtype(int nameId, int iconId, String locale, String mode, String extraValue,
    221             boolean isAuxiliary) {
    222         this(nameId, iconId, locale, mode, extraValue, isAuxiliary, false);
    223     }
    224 
    225     /**
    226      * Constructor with no subtype ID specified.
    227      * @deprecated use {@link InputMethodSubtypeBuilder} instead.
    228      * Arguments for this constructor have the same meanings as
    229      * {@link InputMethodSubtype#InputMethodSubtype(int, int, String, String, String, boolean,
    230      * boolean, int)} except "id".
    231      */
    232     public InputMethodSubtype(int nameId, int iconId, String locale, String mode, String extraValue,
    233             boolean isAuxiliary, boolean overridesImplicitlyEnabledSubtype) {
    234         this(nameId, iconId, locale, mode, extraValue, isAuxiliary,
    235                 overridesImplicitlyEnabledSubtype, 0);
    236     }
    237 
    238     /**
    239      * Constructor.
    240      * @deprecated use {@link InputMethodSubtypeBuilder} instead.
    241      * "isAsciiCapable" is "false" in this constructor.
    242      * @param nameId Resource ID of the subtype name string. The string resource may have exactly
    243      * one %s in it. If there is, the %s part will be replaced with the locale's display name by
    244      * the formatter. Please refer to {@link #getDisplayName} for details.
    245      * @param iconId Resource ID of the subtype icon drawable.
    246      * @param locale The locale supported by the subtype
    247      * @param mode The mode supported by the subtype
    248      * @param extraValue The extra value of the subtype. This string is free-form, but the API
    249      * supplies tools to deal with a key-value comma-separated list; see
    250      * {@link #containsExtraValueKey} and {@link #getExtraValueOf}.
    251      * @param isAuxiliary true when this subtype is auxiliary, false otherwise. An auxiliary
    252      * subtype will not be shown in the list of enabled IMEs for choosing the current IME in
    253      * the Settings even when this subtype is enabled. Please note that this subtype will still
    254      * be shown in the list of IMEs in the IME switcher to allow the user to tentatively switch
    255      * to this subtype while an IME is shown. The framework will never switch the current IME to
    256      * this subtype by {@link android.view.inputmethod.InputMethodManager#switchToLastInputMethod}.
    257      * The intent of having this flag is to allow for IMEs that are invoked in a one-shot way as
    258      * auxiliary input mode, and return to the previous IME once it is finished (e.g. voice input).
    259      * @param overridesImplicitlyEnabledSubtype true when this subtype should be enabled by default
    260      * if no other subtypes in the IME are enabled explicitly. Note that a subtype with this
    261      * parameter being true will not be shown in the list of subtypes in each IME's subtype enabler.
    262      * Having an "automatic" subtype is an example use of this flag.
    263      * @param id The unique ID for the subtype. The input method framework keeps track of enabled
    264      * subtypes by ID. When the IME package gets upgraded, enabled IDs will stay enabled even if
    265      * other attributes are different. If the ID is unspecified or 0,
    266      * Arrays.hashCode(new Object[] {locale, mode, extraValue,
    267      * isAuxiliary, overridesImplicitlyEnabledSubtype}) will be used instead.
    268      */
    269     public InputMethodSubtype(int nameId, int iconId, String locale, String mode, String extraValue,
    270             boolean isAuxiliary, boolean overridesImplicitlyEnabledSubtype, int id) {
    271         this(getBuilder(nameId, iconId, locale, mode, extraValue, isAuxiliary,
    272                 overridesImplicitlyEnabledSubtype, id, false));
    273     }
    274 
    275     /**
    276      * Constructor.
    277      * @param builder Builder for InputMethodSubtype
    278      */
    279     private InputMethodSubtype(InputMethodSubtypeBuilder builder) {
    280         mSubtypeNameResId = builder.mSubtypeNameResId;
    281         mSubtypeIconResId = builder.mSubtypeIconResId;
    282         mSubtypeLocale = builder.mSubtypeLocale;
    283         mSubtypeMode = builder.mSubtypeMode;
    284         mSubtypeExtraValue = builder.mSubtypeExtraValue;
    285         mIsAuxiliary = builder.mIsAuxiliary;
    286         mOverridesImplicitlyEnabledSubtype = builder.mOverridesImplicitlyEnabledSubtype;
    287         mSubtypeId = builder.mSubtypeId;
    288         mIsAsciiCapable = builder.mIsAsciiCapable;
    289         // If hashCode() of this subtype is 0 and you want to specify it as an id of this subtype,
    290         // just specify 0 as this subtype's id. Then, this subtype's id is treated as 0.
    291         mSubtypeHashCode = mSubtypeId != 0 ? mSubtypeId : hashCodeInternal(mSubtypeLocale,
    292                 mSubtypeMode, mSubtypeExtraValue, mIsAuxiliary, mOverridesImplicitlyEnabledSubtype,
    293                 mIsAsciiCapable);
    294     }
    295 
    296     InputMethodSubtype(Parcel source) {
    297         String s;
    298         mSubtypeNameResId = source.readInt();
    299         mSubtypeIconResId = source.readInt();
    300         s = source.readString();
    301         mSubtypeLocale = s != null ? s : "";
    302         s = source.readString();
    303         mSubtypeMode = s != null ? s : "";
    304         s = source.readString();
    305         mSubtypeExtraValue = s != null ? s : "";
    306         mIsAuxiliary = (source.readInt() == 1);
    307         mOverridesImplicitlyEnabledSubtype = (source.readInt() == 1);
    308         mSubtypeHashCode = source.readInt();
    309         mSubtypeId = source.readInt();
    310         mIsAsciiCapable = (source.readInt() == 1);
    311     }
    312 
    313     /**
    314      * @return Resource ID of the subtype name string.
    315      */
    316     public int getNameResId() {
    317         return mSubtypeNameResId;
    318     }
    319 
    320     /**
    321      * @return Resource ID of the subtype icon drawable.
    322      */
    323     public int getIconResId() {
    324         return mSubtypeIconResId;
    325     }
    326 
    327     /**
    328      * @return The locale of the subtype. This method returns the "locale" string parameter passed
    329      * to the constructor.
    330      */
    331     public String getLocale() {
    332         return mSubtypeLocale;
    333     }
    334 
    335     /**
    336      * @return The mode of the subtype.
    337      */
    338     public String getMode() {
    339         return mSubtypeMode;
    340     }
    341 
    342     /**
    343      * @return The extra value of the subtype.
    344      */
    345     public String getExtraValue() {
    346         return mSubtypeExtraValue;
    347     }
    348 
    349     /**
    350      * @return true if this subtype is auxiliary, false otherwise. An auxiliary subtype will not be
    351      * shown in the list of enabled IMEs for choosing the current IME in the Settings even when this
    352      * subtype is enabled. Please note that this subtype will still be shown in the list of IMEs in
    353      * the IME switcher to allow the user to tentatively switch to this subtype while an IME is
    354      * shown. The framework will never switch the current IME to this subtype by
    355      * {@link android.view.inputmethod.InputMethodManager#switchToLastInputMethod}.
    356      * The intent of having this flag is to allow for IMEs that are invoked in a one-shot way as
    357      * auxiliary input mode, and return to the previous IME once it is finished (e.g. voice input).
    358      */
    359     public boolean isAuxiliary() {
    360         return mIsAuxiliary;
    361     }
    362 
    363     /**
    364      * @return true when this subtype will be enabled by default if no other subtypes in the IME
    365      * are enabled explicitly, false otherwise. Note that a subtype with this method returning true
    366      * will not be shown in the list of subtypes in each IME's subtype enabler. Having an
    367      * "automatic" subtype is an example use of this flag.
    368      */
    369     public boolean overridesImplicitlyEnabledSubtype() {
    370         return mOverridesImplicitlyEnabledSubtype;
    371     }
    372 
    373     /**
    374      * @return true if this subtype is Ascii capable, false otherwise. If the subtype is ASCII
    375      * capable, it should guarantee that the user can input ASCII characters with this subtype.
    376      * This is important because many password fields only allow ASCII-characters.
    377      */
    378     public boolean isAsciiCapable() {
    379         return mIsAsciiCapable;
    380     }
    381 
    382     /**
    383      * @param context Context will be used for getting Locale and PackageManager.
    384      * @param packageName The package name of the IME
    385      * @param appInfo The application info of the IME
    386      * @return a display name for this subtype. The string resource of the label (mSubtypeNameResId)
    387      * may have exactly one %s in it. If there is, the %s part will be replaced with the locale's
    388      * display name by the formatter. If there is not, this method returns the string specified by
    389      * mSubtypeNameResId. If mSubtypeNameResId is not specified (== 0), it's up to the framework to
    390      * generate an appropriate display name.
    391      */
    392     public CharSequence getDisplayName(
    393             Context context, String packageName, ApplicationInfo appInfo) {
    394         final Locale locale = constructLocaleFromString(mSubtypeLocale);
    395         final String localeStr = locale != null ? locale.getDisplayName() : mSubtypeLocale;
    396         if (mSubtypeNameResId == 0) {
    397             return localeStr;
    398         }
    399         final CharSequence subtypeName = context.getPackageManager().getText(
    400                 packageName, mSubtypeNameResId, appInfo);
    401         if (!TextUtils.isEmpty(subtypeName)) {
    402             final String replacementString =
    403                     containsExtraValueKey(EXTRA_KEY_UNTRANSLATABLE_STRING_IN_SUBTYPE_NAME)
    404                             ? getExtraValueOf(EXTRA_KEY_UNTRANSLATABLE_STRING_IN_SUBTYPE_NAME)
    405                             : localeStr;
    406             try {
    407                 return String.format(
    408                         subtypeName.toString(), replacementString != null ? replacementString : "");
    409             } catch (IllegalFormatException e) {
    410                 Slog.w(TAG, "Found illegal format in subtype name("+ subtypeName + "): " + e);
    411                 return "";
    412             }
    413         } else {
    414             return localeStr;
    415         }
    416     }
    417 
    418     private HashMap<String, String> getExtraValueHashMap() {
    419         if (mExtraValueHashMapCache == null) {
    420             synchronized(this) {
    421                 if (mExtraValueHashMapCache == null) {
    422                     mExtraValueHashMapCache = new HashMap<String, String>();
    423                     final String[] pairs = mSubtypeExtraValue.split(EXTRA_VALUE_PAIR_SEPARATOR);
    424                     final int N = pairs.length;
    425                     for (int i = 0; i < N; ++i) {
    426                         final String[] pair = pairs[i].split(EXTRA_VALUE_KEY_VALUE_SEPARATOR);
    427                         if (pair.length == 1) {
    428                             mExtraValueHashMapCache.put(pair[0], null);
    429                         } else if (pair.length > 1) {
    430                             if (pair.length > 2) {
    431                                 Slog.w(TAG, "ExtraValue has two or more '='s");
    432                             }
    433                             mExtraValueHashMapCache.put(pair[0], pair[1]);
    434                         }
    435                     }
    436                 }
    437             }
    438         }
    439         return mExtraValueHashMapCache;
    440     }
    441 
    442     /**
    443      * The string of ExtraValue in subtype should be defined as follows:
    444      * example: key0,key1=value1,key2,key3,key4=value4
    445      * @param key The key of extra value
    446      * @return The subtype contains specified the extra value
    447      */
    448     public boolean containsExtraValueKey(String key) {
    449         return getExtraValueHashMap().containsKey(key);
    450     }
    451 
    452     /**
    453      * The string of ExtraValue in subtype should be defined as follows:
    454      * example: key0,key1=value1,key2,key3,key4=value4
    455      * @param key The key of extra value
    456      * @return The value of the specified key
    457      */
    458     public String getExtraValueOf(String key) {
    459         return getExtraValueHashMap().get(key);
    460     }
    461 
    462     @Override
    463     public int hashCode() {
    464         return mSubtypeHashCode;
    465     }
    466 
    467     @Override
    468     public boolean equals(Object o) {
    469         if (o instanceof InputMethodSubtype) {
    470             InputMethodSubtype subtype = (InputMethodSubtype) o;
    471             if (subtype.mSubtypeId != 0 || mSubtypeId != 0) {
    472                 return (subtype.hashCode() == hashCode());
    473             }
    474             return (subtype.hashCode() == hashCode())
    475                 && (subtype.getNameResId() == getNameResId())
    476                 && (subtype.getMode().equals(getMode()))
    477                 && (subtype.getIconResId() == getIconResId())
    478                 && (subtype.getLocale().equals(getLocale()))
    479                 && (subtype.getExtraValue().equals(getExtraValue()))
    480                 && (subtype.isAuxiliary() == isAuxiliary())
    481                 && (subtype.isAsciiCapable() == isAsciiCapable());
    482         }
    483         return false;
    484     }
    485 
    486     @Override
    487     public int describeContents() {
    488         return 0;
    489     }
    490 
    491     @Override
    492     public void writeToParcel(Parcel dest, int parcelableFlags) {
    493         dest.writeInt(mSubtypeNameResId);
    494         dest.writeInt(mSubtypeIconResId);
    495         dest.writeString(mSubtypeLocale);
    496         dest.writeString(mSubtypeMode);
    497         dest.writeString(mSubtypeExtraValue);
    498         dest.writeInt(mIsAuxiliary ? 1 : 0);
    499         dest.writeInt(mOverridesImplicitlyEnabledSubtype ? 1 : 0);
    500         dest.writeInt(mSubtypeHashCode);
    501         dest.writeInt(mSubtypeId);
    502         dest.writeInt(mIsAsciiCapable ? 1 : 0);
    503     }
    504 
    505     public static final Parcelable.Creator<InputMethodSubtype> CREATOR
    506             = new Parcelable.Creator<InputMethodSubtype>() {
    507         @Override
    508         public InputMethodSubtype createFromParcel(Parcel source) {
    509             return new InputMethodSubtype(source);
    510         }
    511 
    512         @Override
    513         public InputMethodSubtype[] newArray(int size) {
    514             return new InputMethodSubtype[size];
    515         }
    516     };
    517 
    518     private static Locale constructLocaleFromString(String localeStr) {
    519         if (TextUtils.isEmpty(localeStr))
    520             return null;
    521         String[] localeParams = localeStr.split("_", 3);
    522         // The length of localeStr is guaranteed to always return a 1 <= value <= 3
    523         // because localeStr is not empty.
    524         if (localeParams.length == 1) {
    525             return new Locale(localeParams[0]);
    526         } else if (localeParams.length == 2) {
    527             return new Locale(localeParams[0], localeParams[1]);
    528         } else if (localeParams.length == 3) {
    529             return new Locale(localeParams[0], localeParams[1], localeParams[2]);
    530         }
    531         return null;
    532     }
    533 
    534     private static int hashCodeInternal(String locale, String mode, String extraValue,
    535             boolean isAuxiliary, boolean overridesImplicitlyEnabledSubtype,
    536             boolean isAsciiCapable) {
    537         // CAVEAT: Must revisit how to compute needsToCalculateCompatibleHashCode when a new
    538         // attribute is added in order to avoid enabled subtypes being unexpectedly disabled.
    539         final boolean needsToCalculateCompatibleHashCode = !isAsciiCapable;
    540         if (needsToCalculateCompatibleHashCode) {
    541             return Arrays.hashCode(new Object[] {locale, mode, extraValue, isAuxiliary,
    542                     overridesImplicitlyEnabledSubtype});
    543         }
    544         return Arrays.hashCode(new Object[] {locale, mode, extraValue, isAuxiliary,
    545                 overridesImplicitlyEnabledSubtype, isAsciiCapable});
    546     }
    547 
    548     /**
    549      * Sort the list of InputMethodSubtype
    550      * @param context Context will be used for getting localized strings from IME
    551      * @param flags Flags for the sort order
    552      * @param imi InputMethodInfo of which subtypes are subject to be sorted
    553      * @param subtypeList List of InputMethodSubtype which will be sorted
    554      * @return Sorted list of subtypes
    555      * @hide
    556      */
    557     public static List<InputMethodSubtype> sort(Context context, int flags, InputMethodInfo imi,
    558             List<InputMethodSubtype> subtypeList) {
    559         if (imi == null) return subtypeList;
    560         final HashSet<InputMethodSubtype> inputSubtypesSet = new HashSet<InputMethodSubtype>(
    561                 subtypeList);
    562         final ArrayList<InputMethodSubtype> sortedList = new ArrayList<InputMethodSubtype>();
    563         int N = imi.getSubtypeCount();
    564         for (int i = 0; i < N; ++i) {
    565             InputMethodSubtype subtype = imi.getSubtypeAt(i);
    566             if (inputSubtypesSet.contains(subtype)) {
    567                 sortedList.add(subtype);
    568                 inputSubtypesSet.remove(subtype);
    569             }
    570         }
    571         // If subtypes in inputSubtypesSet remain, that means these subtypes are not
    572         // contained in imi, so the remaining subtypes will be appended.
    573         for (InputMethodSubtype subtype: inputSubtypesSet) {
    574             sortedList.add(subtype);
    575         }
    576         return sortedList;
    577     }
    578 }
    579