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><subtype></code> element, which resides within an {@code <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