1 /* 2 * Copyright (C) 2013 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 com.android.internal.inputmethod; 18 19 import android.content.Context; 20 import android.content.pm.ApplicationInfo; 21 import android.content.pm.ResolveInfo; 22 import android.content.pm.ServiceInfo; 23 import android.content.res.Configuration; 24 import android.content.res.Resources; 25 import android.os.LocaleList; 26 import android.os.Parcel; 27 import android.test.InstrumentationTestCase; 28 import android.test.suitebuilder.annotation.SmallTest; 29 import android.util.ArrayMap; 30 import android.util.ArraySet; 31 import android.view.inputmethod.InputMethodInfo; 32 import android.view.inputmethod.InputMethodSubtype.InputMethodSubtypeBuilder; 33 import android.view.inputmethod.InputMethodSubtype; 34 35 import java.util.ArrayList; 36 import java.util.List; 37 import java.util.Locale; 38 import java.util.Objects; 39 40 import static org.hamcrest.MatcherAssert.assertThat; 41 import static org.hamcrest.Matchers.isIn; 42 import static org.hamcrest.Matchers.not; 43 44 public class InputMethodUtilsTest extends InstrumentationTestCase { 45 private static final boolean IS_AUX = true; 46 private static final boolean IS_DEFAULT = true; 47 private static final boolean IS_OVERRIDES_IMPLICITLY_ENABLED_SUBTYPE = true; 48 private static final boolean IS_ASCII_CAPABLE = true; 49 private static final boolean IS_ENABLED_WHEN_DEFAULT_IS_NOT_ASCII_CAPABLE = true; 50 private static final boolean IS_SYSTEM_READY = true; 51 private static final Locale LOCALE_EN = new Locale("en"); 52 private static final Locale LOCALE_EN_US = new Locale("en", "US"); 53 private static final Locale LOCALE_EN_GB = new Locale("en", "GB"); 54 private static final Locale LOCALE_EN_IN = new Locale("en", "IN"); 55 private static final Locale LOCALE_FI = new Locale("fi"); 56 private static final Locale LOCALE_FI_FI = new Locale("fi", "FI"); 57 private static final Locale LOCALE_FIL = new Locale("fil"); 58 private static final Locale LOCALE_FIL_PH = new Locale("fil", "PH"); 59 private static final Locale LOCALE_FR = new Locale("fr"); 60 private static final Locale LOCALE_FR_CA = new Locale("fr", "CA"); 61 private static final Locale LOCALE_HI = new Locale("hi"); 62 private static final Locale LOCALE_JA = new Locale("ja"); 63 private static final Locale LOCALE_JA_JP = new Locale("ja", "JP"); 64 private static final Locale LOCALE_ZH_CN = new Locale("zh", "CN"); 65 private static final Locale LOCALE_ZH_TW = new Locale("zh", "TW"); 66 private static final Locale LOCALE_IN = new Locale("in"); 67 private static final Locale LOCALE_ID = new Locale("id"); 68 private static final Locale LOCALE_TH = new Locale("ht"); 69 private static final Locale LOCALE_TH_TH = new Locale("ht", "TH"); 70 private static final Locale LOCALE_TH_TH_TH = new Locale("ht", "TH", "TH"); 71 private static final String SUBTYPE_MODE_KEYBOARD = "keyboard"; 72 private static final String SUBTYPE_MODE_VOICE = "voice"; 73 private static final String SUBTYPE_MODE_HANDWRITING = "handwriting"; 74 private static final String SUBTYPE_MODE_ANY = null; 75 private static final String EXTRA_VALUE_PAIR_SEPARATOR = ","; 76 private static final String EXTRA_VALUE_ASCII_CAPABLE = "AsciiCapable"; 77 private static final String EXTRA_VALUE_ENABLED_WHEN_DEFAULT_IS_NOT_ASCII_CAPABLE = 78 "EnabledWhenDefaultIsNotAsciiCapable"; 79 80 @SmallTest testVoiceImes()81 public void testVoiceImes() throws Exception { 82 // locale: en_US 83 assertDefaultEnabledImes(getImesWithDefaultVoiceIme(), LOCALE_EN_US, 84 "DummyDefaultEnKeyboardIme", "DummyDefaultAutoVoiceIme"); 85 assertDefaultEnabledImes(getImesWithoutDefaultVoiceIme(), LOCALE_EN_US, 86 "DummyDefaultEnKeyboardIme", "DummyNonDefaultAutoVoiceIme0", 87 "DummyNonDefaultAutoVoiceIme1"); 88 89 // locale: en_GB 90 assertDefaultEnabledImes(getImesWithDefaultVoiceIme(), LOCALE_EN_GB, 91 "DummyDefaultEnKeyboardIme", "DummyDefaultAutoVoiceIme"); 92 assertDefaultEnabledImes(getImesWithoutDefaultVoiceIme(), LOCALE_EN_GB, 93 "DummyDefaultEnKeyboardIme", "DummyNonDefaultAutoVoiceIme0", 94 "DummyNonDefaultAutoVoiceIme1"); 95 96 // locale: ja_JP 97 assertDefaultEnabledImes(getImesWithDefaultVoiceIme(), LOCALE_JA_JP, 98 "DummyDefaultEnKeyboardIme", "DummyDefaultAutoVoiceIme"); 99 assertDefaultEnabledImes(getImesWithoutDefaultVoiceIme(), LOCALE_JA_JP, 100 "DummyDefaultEnKeyboardIme", "DummyNonDefaultAutoVoiceIme0", 101 "DummyNonDefaultAutoVoiceIme1"); 102 } 103 104 @SmallTest testKeyboardImes()105 public void testKeyboardImes() throws Exception { 106 // locale: en_US 107 assertDefaultEnabledImes(getSamplePreinstalledImes("en-rUS"), LOCALE_EN_US, 108 "com.android.apps.inputmethod.latin", "com.android.apps.inputmethod.voice"); 109 110 // locale: en_GB 111 assertDefaultEnabledImes(getSamplePreinstalledImes("en-rGB"), LOCALE_EN_GB, 112 "com.android.apps.inputmethod.latin", "com.android.apps.inputmethod.voice"); 113 114 // locale: en_IN 115 assertDefaultEnabledImes(getSamplePreinstalledImes("en-rIN"), LOCALE_EN_IN, 116 "com.android.apps.inputmethod.hindi", 117 "com.android.apps.inputmethod.latin", "com.android.apps.inputmethod.voice"); 118 119 // locale: hi 120 assertDefaultEnabledImes(getSamplePreinstalledImes("hi"), LOCALE_HI, 121 "com.android.apps.inputmethod.hindi", "com.android.apps.inputmethod.latin", 122 "com.android.apps.inputmethod.voice"); 123 124 // locale: ja_JP 125 assertDefaultEnabledImes(getSamplePreinstalledImes("ja-rJP"), LOCALE_JA_JP, 126 "com.android.apps.inputmethod.japanese", "com.android.apps.inputmethod.voice"); 127 128 // locale: zh_CN 129 assertDefaultEnabledImes(getSamplePreinstalledImes("zh-rCN"), LOCALE_ZH_CN, 130 "com.android.apps.inputmethod.pinyin", "com.android.apps.inputmethod.voice"); 131 132 // locale: zh_TW 133 // Note: In this case, no IME is suitable for the system locale. Hence we will pick up a 134 // fallback IME regardless of the "default" attribute. 135 assertDefaultEnabledImes(getSamplePreinstalledImes("zh-rTW"), LOCALE_ZH_TW, 136 "com.android.apps.inputmethod.latin", "com.android.apps.inputmethod.voice"); 137 } 138 139 @SmallTest testParcelable()140 public void testParcelable() throws Exception { 141 final ArrayList<InputMethodInfo> originalList = getSamplePreinstalledImes("en-rUS"); 142 final List<InputMethodInfo> clonedList = cloneViaParcel(originalList); 143 assertNotNull(clonedList); 144 final List<InputMethodInfo> clonedClonedList = cloneViaParcel(clonedList); 145 assertNotNull(clonedClonedList); 146 assertEquals(originalList, clonedList); 147 assertEquals(clonedList, clonedClonedList); 148 assertEquals(originalList.size(), clonedList.size()); 149 assertEquals(clonedList.size(), clonedClonedList.size()); 150 for (int imeIndex = 0; imeIndex < originalList.size(); ++imeIndex) { 151 verifyEquality(originalList.get(imeIndex), clonedList.get(imeIndex)); 152 verifyEquality(clonedList.get(imeIndex), clonedClonedList.get(imeIndex)); 153 } 154 } 155 156 @SmallTest testGetImplicitlyApplicableSubtypesLocked()157 public void testGetImplicitlyApplicableSubtypesLocked() throws Exception { 158 final InputMethodSubtype nonAutoEnUS = createDummyInputMethodSubtype("en_US", 159 SUBTYPE_MODE_KEYBOARD, !IS_AUX, !IS_OVERRIDES_IMPLICITLY_ENABLED_SUBTYPE, 160 IS_ASCII_CAPABLE, !IS_ENABLED_WHEN_DEFAULT_IS_NOT_ASCII_CAPABLE); 161 final InputMethodSubtype nonAutoEnGB = createDummyInputMethodSubtype("en_GB", 162 SUBTYPE_MODE_KEYBOARD, !IS_AUX, !IS_OVERRIDES_IMPLICITLY_ENABLED_SUBTYPE, 163 IS_ASCII_CAPABLE, IS_ENABLED_WHEN_DEFAULT_IS_NOT_ASCII_CAPABLE); 164 final InputMethodSubtype nonAutoEnIN = createDummyInputMethodSubtype("en_IN", 165 SUBTYPE_MODE_KEYBOARD, !IS_AUX, !IS_OVERRIDES_IMPLICITLY_ENABLED_SUBTYPE, 166 IS_ASCII_CAPABLE, IS_ENABLED_WHEN_DEFAULT_IS_NOT_ASCII_CAPABLE); 167 final InputMethodSubtype nonAutoFrCA = createDummyInputMethodSubtype("fr_CA", 168 SUBTYPE_MODE_KEYBOARD, !IS_AUX, !IS_OVERRIDES_IMPLICITLY_ENABLED_SUBTYPE, 169 IS_ASCII_CAPABLE, IS_ENABLED_WHEN_DEFAULT_IS_NOT_ASCII_CAPABLE); 170 final InputMethodSubtype nonAutoFr = createDummyInputMethodSubtype("fr_CA", 171 SUBTYPE_MODE_KEYBOARD, !IS_AUX, !IS_OVERRIDES_IMPLICITLY_ENABLED_SUBTYPE, 172 IS_ASCII_CAPABLE, IS_ENABLED_WHEN_DEFAULT_IS_NOT_ASCII_CAPABLE); 173 final InputMethodSubtype nonAutoFil = createDummyInputMethodSubtype("fil", 174 SUBTYPE_MODE_KEYBOARD, !IS_AUX, !IS_OVERRIDES_IMPLICITLY_ENABLED_SUBTYPE, 175 IS_ASCII_CAPABLE, !IS_ENABLED_WHEN_DEFAULT_IS_NOT_ASCII_CAPABLE); 176 final InputMethodSubtype nonAutoIn = createDummyInputMethodSubtype("in", 177 SUBTYPE_MODE_KEYBOARD, !IS_AUX, !IS_OVERRIDES_IMPLICITLY_ENABLED_SUBTYPE, 178 IS_ASCII_CAPABLE, IS_ENABLED_WHEN_DEFAULT_IS_NOT_ASCII_CAPABLE); 179 final InputMethodSubtype nonAutoId = createDummyInputMethodSubtype("id", 180 SUBTYPE_MODE_KEYBOARD, !IS_AUX, !IS_OVERRIDES_IMPLICITLY_ENABLED_SUBTYPE, 181 IS_ASCII_CAPABLE, IS_ENABLED_WHEN_DEFAULT_IS_NOT_ASCII_CAPABLE); 182 final InputMethodSubtype autoSubtype = createDummyInputMethodSubtype("auto", 183 SUBTYPE_MODE_KEYBOARD, !IS_AUX, IS_OVERRIDES_IMPLICITLY_ENABLED_SUBTYPE, 184 IS_ASCII_CAPABLE, !IS_ENABLED_WHEN_DEFAULT_IS_NOT_ASCII_CAPABLE); 185 final InputMethodSubtype nonAutoJa = createDummyInputMethodSubtype("ja", 186 SUBTYPE_MODE_KEYBOARD, !IS_AUX, !IS_OVERRIDES_IMPLICITLY_ENABLED_SUBTYPE, 187 !IS_ASCII_CAPABLE, !IS_ENABLED_WHEN_DEFAULT_IS_NOT_ASCII_CAPABLE); 188 final InputMethodSubtype nonAutoHi = createDummyInputMethodSubtype("hi", 189 SUBTYPE_MODE_KEYBOARD, !IS_AUX, !IS_OVERRIDES_IMPLICITLY_ENABLED_SUBTYPE, 190 !IS_ASCII_CAPABLE, !IS_ENABLED_WHEN_DEFAULT_IS_NOT_ASCII_CAPABLE); 191 final InputMethodSubtype nonAutoSrCyrl = createDummyInputMethodSubtype("sr", 192 "sr-Cyrl", SUBTYPE_MODE_KEYBOARD, !IS_AUX, 193 !IS_OVERRIDES_IMPLICITLY_ENABLED_SUBTYPE, !IS_ASCII_CAPABLE, 194 !IS_ENABLED_WHEN_DEFAULT_IS_NOT_ASCII_CAPABLE); 195 final InputMethodSubtype nonAutoSrLatn = createDummyInputMethodSubtype("sr_ZZ", 196 "sr-Latn", SUBTYPE_MODE_KEYBOARD, !IS_AUX, 197 !IS_OVERRIDES_IMPLICITLY_ENABLED_SUBTYPE, IS_ASCII_CAPABLE, 198 !IS_ENABLED_WHEN_DEFAULT_IS_NOT_ASCII_CAPABLE); 199 final InputMethodSubtype nonAutoHandwritingEn = createDummyInputMethodSubtype("en", 200 SUBTYPE_MODE_HANDWRITING, !IS_AUX, !IS_OVERRIDES_IMPLICITLY_ENABLED_SUBTYPE, 201 !IS_ASCII_CAPABLE, !IS_ENABLED_WHEN_DEFAULT_IS_NOT_ASCII_CAPABLE); 202 final InputMethodSubtype nonAutoHandwritingFr = createDummyInputMethodSubtype("fr", 203 SUBTYPE_MODE_HANDWRITING, !IS_AUX, !IS_OVERRIDES_IMPLICITLY_ENABLED_SUBTYPE, 204 !IS_ASCII_CAPABLE, !IS_ENABLED_WHEN_DEFAULT_IS_NOT_ASCII_CAPABLE); 205 final InputMethodSubtype nonAutoHandwritingSrCyrl = createDummyInputMethodSubtype("sr", 206 "sr-Cyrl", SUBTYPE_MODE_HANDWRITING, !IS_AUX, 207 !IS_OVERRIDES_IMPLICITLY_ENABLED_SUBTYPE, !IS_ASCII_CAPABLE, 208 !IS_ENABLED_WHEN_DEFAULT_IS_NOT_ASCII_CAPABLE); 209 final InputMethodSubtype nonAutoHandwritingSrLatn = createDummyInputMethodSubtype("sr_ZZ", 210 "sr-Latn", SUBTYPE_MODE_HANDWRITING, !IS_AUX, 211 !IS_OVERRIDES_IMPLICITLY_ENABLED_SUBTYPE, !IS_ASCII_CAPABLE, 212 !IS_ENABLED_WHEN_DEFAULT_IS_NOT_ASCII_CAPABLE); 213 final InputMethodSubtype nonAutoEnabledWhenDefaultIsNotAsciiCalableSubtype = 214 createDummyInputMethodSubtype("zz", SUBTYPE_MODE_KEYBOARD, !IS_AUX, 215 !IS_OVERRIDES_IMPLICITLY_ENABLED_SUBTYPE, !IS_ASCII_CAPABLE, 216 IS_ENABLED_WHEN_DEFAULT_IS_NOT_ASCII_CAPABLE); 217 final InputMethodSubtype nonAutoEnabledWhenDefaultIsNotAsciiCalableSubtype2 = 218 createDummyInputMethodSubtype("zz", SUBTYPE_MODE_KEYBOARD, !IS_AUX, 219 !IS_OVERRIDES_IMPLICITLY_ENABLED_SUBTYPE, !IS_ASCII_CAPABLE, 220 IS_ENABLED_WHEN_DEFAULT_IS_NOT_ASCII_CAPABLE); 221 222 // Make sure that an automatic subtype (overridesImplicitlyEnabledSubtype:true) is 223 // selected no matter what locale is specified. 224 { 225 final ArrayList<InputMethodSubtype> subtypes = new ArrayList<>(); 226 subtypes.add(nonAutoEnUS); 227 subtypes.add(nonAutoEnGB); 228 subtypes.add(nonAutoJa); 229 subtypes.add(nonAutoFil); 230 subtypes.add(autoSubtype); // overridesImplicitlyEnabledSubtype == true 231 subtypes.add(nonAutoEnabledWhenDefaultIsNotAsciiCalableSubtype); 232 subtypes.add(nonAutoEnabledWhenDefaultIsNotAsciiCalableSubtype2); 233 subtypes.add(nonAutoHandwritingEn); 234 subtypes.add(nonAutoHandwritingFr); 235 final InputMethodInfo imi = createDummyInputMethodInfo( 236 "com.android.apps.inputmethod.latin", 237 "com.android.apps.inputmethod.latin", "DummyLatinIme", !IS_AUX, IS_DEFAULT, 238 subtypes); 239 final ArrayList<InputMethodSubtype> result = 240 InputMethodUtils.getImplicitlyApplicableSubtypesLocked( 241 getResourcesForLocales(LOCALE_EN_US), imi); 242 assertEquals(1, result.size()); 243 verifyEquality(autoSubtype, result.get(0)); 244 } 245 246 // Make sure that a subtype whose locale is exactly equal to the specified locale is 247 // selected as long as there is no no automatic subtype 248 // (overridesImplicitlyEnabledSubtype:true) in the given list. 249 { 250 final ArrayList<InputMethodSubtype> subtypes = new ArrayList<>(); 251 subtypes.add(nonAutoEnUS); // locale == "en_US" 252 subtypes.add(nonAutoEnGB); 253 subtypes.add(nonAutoJa); 254 subtypes.add(nonAutoFil); 255 subtypes.add(nonAutoEnabledWhenDefaultIsNotAsciiCalableSubtype); 256 subtypes.add(nonAutoEnabledWhenDefaultIsNotAsciiCalableSubtype2); 257 subtypes.add(nonAutoHandwritingEn); 258 subtypes.add(nonAutoHandwritingFr); 259 final InputMethodInfo imi = createDummyInputMethodInfo( 260 "com.android.apps.inputmethod.latin", 261 "com.android.apps.inputmethod.latin", "DummyLatinIme", !IS_AUX, IS_DEFAULT, 262 subtypes); 263 final ArrayList<InputMethodSubtype> result = 264 InputMethodUtils.getImplicitlyApplicableSubtypesLocked( 265 getResourcesForLocales(LOCALE_EN_US), imi); 266 assertEquals(2, result.size()); 267 verifyEquality(nonAutoEnUS, result.get(0)); 268 verifyEquality(nonAutoHandwritingEn, result.get(1)); 269 } 270 271 // Make sure that a subtype whose locale is exactly equal to the specified locale is 272 // selected as long as there is no automatic subtype 273 // (overridesImplicitlyEnabledSubtype:true) in the given list. 274 { 275 final ArrayList<InputMethodSubtype> subtypes = new ArrayList<>(); 276 subtypes.add(nonAutoEnUS); 277 subtypes.add(nonAutoEnGB); // locale == "en_GB" 278 subtypes.add(nonAutoJa); 279 subtypes.add(nonAutoFil); 280 subtypes.add(nonAutoEnabledWhenDefaultIsNotAsciiCalableSubtype); 281 subtypes.add(nonAutoHandwritingEn); 282 subtypes.add(nonAutoHandwritingFr); 283 final InputMethodInfo imi = createDummyInputMethodInfo( 284 "com.android.apps.inputmethod.latin", 285 "com.android.apps.inputmethod.latin", "DummyLatinIme", !IS_AUX, IS_DEFAULT, 286 subtypes); 287 final ArrayList<InputMethodSubtype> result = 288 InputMethodUtils.getImplicitlyApplicableSubtypesLocked( 289 getResourcesForLocales(LOCALE_EN_GB), imi); 290 assertEquals(2, result.size()); 291 verifyEquality(nonAutoEnGB, result.get(0)); 292 verifyEquality(nonAutoHandwritingEn, result.get(1)); 293 } 294 295 // If there is no automatic subtype (overridesImplicitlyEnabledSubtype:true) and 296 // any subtype whose locale is exactly equal to the specified locale in the given list, 297 // try to find a subtype whose language is equal to the language part of the given locale. 298 // Here make sure that a subtype (locale: "fr_CA") can be found with locale: "fr". 299 { 300 final ArrayList<InputMethodSubtype> subtypes = new ArrayList<>(); 301 subtypes.add(nonAutoFrCA); // locale == "fr_CA" 302 subtypes.add(nonAutoJa); 303 subtypes.add(nonAutoFil); 304 subtypes.add(nonAutoEnabledWhenDefaultIsNotAsciiCalableSubtype); 305 subtypes.add(nonAutoEnabledWhenDefaultIsNotAsciiCalableSubtype2); 306 subtypes.add(nonAutoHandwritingEn); 307 subtypes.add(nonAutoHandwritingFr); 308 final InputMethodInfo imi = createDummyInputMethodInfo( 309 "com.android.apps.inputmethod.latin", 310 "com.android.apps.inputmethod.latin", "DummyLatinIme", !IS_AUX, IS_DEFAULT, 311 subtypes); 312 final ArrayList<InputMethodSubtype> result = 313 InputMethodUtils.getImplicitlyApplicableSubtypesLocked( 314 getResourcesForLocales(LOCALE_FR), imi); 315 assertEquals(2, result.size()); 316 verifyEquality(nonAutoFrCA, result.get(0)); 317 verifyEquality(nonAutoHandwritingFr, result.get(1)); 318 } 319 // Then make sure that a subtype (locale: "fr") can be found with locale: "fr_CA". 320 { 321 final ArrayList<InputMethodSubtype> subtypes = new ArrayList<>(); 322 subtypes.add(nonAutoFr); // locale == "fr" 323 subtypes.add(nonAutoJa); 324 subtypes.add(nonAutoFil); 325 subtypes.add(nonAutoEnabledWhenDefaultIsNotAsciiCalableSubtype); 326 subtypes.add(nonAutoEnabledWhenDefaultIsNotAsciiCalableSubtype2); 327 subtypes.add(nonAutoHandwritingEn); 328 subtypes.add(nonAutoHandwritingFr); 329 final InputMethodInfo imi = createDummyInputMethodInfo( 330 "com.android.apps.inputmethod.latin", 331 "com.android.apps.inputmethod.latin", "DummyLatinIme", !IS_AUX, IS_DEFAULT, 332 subtypes); 333 final ArrayList<InputMethodSubtype> result = 334 InputMethodUtils.getImplicitlyApplicableSubtypesLocked( 335 getResourcesForLocales(LOCALE_FR_CA), imi); 336 assertEquals(2, result.size()); 337 verifyEquality(nonAutoFrCA, result.get(0)); 338 verifyEquality(nonAutoHandwritingFr, result.get(1)); 339 } 340 341 // Make sure that subtypes which have "EnabledWhenDefaultIsNotAsciiCapable" in its 342 // extra value is selected if and only if all other selected IMEs are not AsciiCapable. 343 { 344 final ArrayList<InputMethodSubtype> subtypes = new ArrayList<>(); 345 subtypes.add(nonAutoEnUS); 346 subtypes.add(nonAutoJa); // not ASCII capable 347 subtypes.add(nonAutoEnabledWhenDefaultIsNotAsciiCalableSubtype); 348 subtypes.add(nonAutoEnabledWhenDefaultIsNotAsciiCalableSubtype2); 349 subtypes.add(nonAutoHandwritingEn); 350 subtypes.add(nonAutoHandwritingFr); 351 final InputMethodInfo imi = createDummyInputMethodInfo( 352 "com.android.apps.inputmethod.latin", 353 "com.android.apps.inputmethod.latin", "DummyLatinIme", !IS_AUX, IS_DEFAULT, 354 subtypes); 355 final ArrayList<InputMethodSubtype> result = 356 InputMethodUtils.getImplicitlyApplicableSubtypesLocked( 357 getResourcesForLocales(LOCALE_JA_JP), imi); 358 assertEquals(3, result.size()); 359 verifyEquality(nonAutoJa, result.get(0)); 360 verifyEquality(nonAutoEnabledWhenDefaultIsNotAsciiCalableSubtype, result.get(1)); 361 verifyEquality(nonAutoEnabledWhenDefaultIsNotAsciiCalableSubtype2, result.get(2)); 362 } 363 364 // Make sure that if there is no subtype that matches the language requested, then we just 365 // use the first keyboard subtype. 366 { 367 final ArrayList<InputMethodSubtype> subtypes = new ArrayList<>(); 368 subtypes.add(nonAutoHi); 369 subtypes.add(nonAutoEnUS); 370 subtypes.add(nonAutoHandwritingEn); 371 subtypes.add(nonAutoHandwritingFr); 372 subtypes.add(nonAutoEnabledWhenDefaultIsNotAsciiCalableSubtype); 373 final InputMethodInfo imi = createDummyInputMethodInfo( 374 "com.android.apps.inputmethod.latin", 375 "com.android.apps.inputmethod.latin", "DummyLatinIme", !IS_AUX, IS_DEFAULT, 376 subtypes); 377 final ArrayList<InputMethodSubtype> result = 378 InputMethodUtils.getImplicitlyApplicableSubtypesLocked( 379 getResourcesForLocales(LOCALE_JA_JP), imi); 380 assertEquals(1, result.size()); 381 verifyEquality(nonAutoHi, result.get(0)); 382 } 383 { 384 final ArrayList<InputMethodSubtype> subtypes = new ArrayList<>(); 385 subtypes.add(nonAutoEnUS); 386 subtypes.add(nonAutoHi); 387 subtypes.add(nonAutoHandwritingEn); 388 subtypes.add(nonAutoHandwritingFr); 389 subtypes.add(nonAutoEnabledWhenDefaultIsNotAsciiCalableSubtype); 390 final InputMethodInfo imi = createDummyInputMethodInfo( 391 "com.android.apps.inputmethod.latin", 392 "com.android.apps.inputmethod.latin", "DummyLatinIme", !IS_AUX, IS_DEFAULT, 393 subtypes); 394 final ArrayList<InputMethodSubtype> result = 395 InputMethodUtils.getImplicitlyApplicableSubtypesLocked( 396 getResourcesForLocales(LOCALE_JA_JP), imi); 397 assertEquals(1, result.size()); 398 verifyEquality(nonAutoEnUS, result.get(0)); 399 } 400 { 401 final ArrayList<InputMethodSubtype> subtypes = new ArrayList<>(); 402 subtypes.add(nonAutoHandwritingEn); 403 subtypes.add(nonAutoHandwritingFr); 404 subtypes.add(nonAutoEnUS); 405 subtypes.add(nonAutoHi); 406 subtypes.add(nonAutoEnabledWhenDefaultIsNotAsciiCalableSubtype); 407 final InputMethodInfo imi = createDummyInputMethodInfo( 408 "com.android.apps.inputmethod.latin", 409 "com.android.apps.inputmethod.latin", "DummyLatinIme", !IS_AUX, IS_DEFAULT, 410 subtypes); 411 final ArrayList<InputMethodSubtype> result = 412 InputMethodUtils.getImplicitlyApplicableSubtypesLocked( 413 getResourcesForLocales(LOCALE_JA_JP), imi); 414 assertEquals(1, result.size()); 415 verifyEquality(nonAutoEnUS, result.get(0)); 416 } 417 418 // Make sure that both language and script are taken into account to find the best matching 419 // subtype. 420 { 421 final ArrayList<InputMethodSubtype> subtypes = new ArrayList<>(); 422 subtypes.add(nonAutoEnUS); 423 subtypes.add(nonAutoSrCyrl); 424 subtypes.add(nonAutoSrLatn); 425 subtypes.add(nonAutoHandwritingEn); 426 subtypes.add(nonAutoHandwritingFr); 427 subtypes.add(nonAutoHandwritingSrCyrl); 428 subtypes.add(nonAutoHandwritingSrLatn); 429 final InputMethodInfo imi = createDummyInputMethodInfo( 430 "com.android.apps.inputmethod.latin", 431 "com.android.apps.inputmethod.latin", "DummyLatinIme", !IS_AUX, IS_DEFAULT, 432 subtypes); 433 final ArrayList<InputMethodSubtype> result = 434 InputMethodUtils.getImplicitlyApplicableSubtypesLocked( 435 getResourcesForLocales(Locale.forLanguageTag("sr-Latn-RS")), imi); 436 assertEquals(2, result.size()); 437 assertThat(nonAutoSrLatn, isIn(result)); 438 assertThat(nonAutoHandwritingSrLatn, isIn(result)); 439 } 440 { 441 final ArrayList<InputMethodSubtype> subtypes = new ArrayList<>(); 442 subtypes.add(nonAutoEnUS); 443 subtypes.add(nonAutoSrCyrl); 444 subtypes.add(nonAutoSrLatn); 445 subtypes.add(nonAutoHandwritingEn); 446 subtypes.add(nonAutoHandwritingFr); 447 subtypes.add(nonAutoHandwritingSrCyrl); 448 subtypes.add(nonAutoHandwritingSrLatn); 449 final InputMethodInfo imi = createDummyInputMethodInfo( 450 "com.android.apps.inputmethod.latin", 451 "com.android.apps.inputmethod.latin", "DummyLatinIme", !IS_AUX, IS_DEFAULT, 452 subtypes); 453 final ArrayList<InputMethodSubtype> result = 454 InputMethodUtils.getImplicitlyApplicableSubtypesLocked( 455 getResourcesForLocales(Locale.forLanguageTag("sr-Cyrl-RS")), imi); 456 assertEquals(2, result.size()); 457 assertThat(nonAutoSrCyrl, isIn(result)); 458 assertThat(nonAutoHandwritingSrCyrl, isIn(result)); 459 } 460 461 // Make sure that secondary locales are taken into account to find the best matching 462 // subtype. 463 { 464 final ArrayList<InputMethodSubtype> subtypes = new ArrayList<>(); 465 subtypes.add(nonAutoEnUS); 466 subtypes.add(nonAutoEnGB); 467 subtypes.add(nonAutoSrCyrl); 468 subtypes.add(nonAutoSrLatn); 469 subtypes.add(nonAutoFr); 470 subtypes.add(nonAutoFrCA); 471 subtypes.add(nonAutoHandwritingEn); 472 subtypes.add(nonAutoHandwritingFr); 473 subtypes.add(nonAutoHandwritingSrCyrl); 474 subtypes.add(nonAutoHandwritingSrLatn); 475 final InputMethodInfo imi = createDummyInputMethodInfo( 476 "com.android.apps.inputmethod.latin", 477 "com.android.apps.inputmethod.latin", "DummyLatinIme", !IS_AUX, IS_DEFAULT, 478 subtypes); 479 final ArrayList<InputMethodSubtype> result = 480 InputMethodUtils.getImplicitlyApplicableSubtypesLocked( 481 getResourcesForLocales( 482 Locale.forLanguageTag("sr-Latn-RS-x-android"), 483 Locale.forLanguageTag("ja-JP"), 484 Locale.forLanguageTag("fr-FR"), 485 Locale.forLanguageTag("en-GB"), 486 Locale.forLanguageTag("en-US")), 487 imi); 488 assertEquals(6, result.size()); 489 assertThat(nonAutoEnGB, isIn(result)); 490 assertThat(nonAutoFr, isIn(result)); 491 assertThat(nonAutoSrLatn, isIn(result)); 492 assertThat(nonAutoHandwritingEn, isIn(result)); 493 assertThat(nonAutoHandwritingFr, isIn(result)); 494 assertThat(nonAutoHandwritingSrLatn, isIn(result)); 495 } 496 497 // Make sure that 3-letter language code can be handled. 498 { 499 final ArrayList<InputMethodSubtype> subtypes = new ArrayList<>(); 500 subtypes.add(nonAutoEnUS); 501 subtypes.add(nonAutoFil); 502 final InputMethodInfo imi = createDummyInputMethodInfo( 503 "com.android.apps.inputmethod.latin", 504 "com.android.apps.inputmethod.latin", "DummyLatinIme", !IS_AUX, IS_DEFAULT, 505 subtypes); 506 final ArrayList<InputMethodSubtype> result = 507 InputMethodUtils.getImplicitlyApplicableSubtypesLocked( 508 getResourcesForLocales(LOCALE_FIL_PH), imi); 509 assertEquals(1, result.size()); 510 verifyEquality(nonAutoFil, result.get(0)); 511 } 512 513 // Make sure that we never end up matching "fi" (finnish) with "fil" (filipino). 514 // Also make sure that the first subtype will be used as the last-resort candidate. 515 { 516 final ArrayList<InputMethodSubtype> subtypes = new ArrayList<>(); 517 subtypes.add(nonAutoJa); 518 subtypes.add(nonAutoEnUS); 519 subtypes.add(nonAutoFil); 520 final InputMethodInfo imi = createDummyInputMethodInfo( 521 "com.android.apps.inputmethod.latin", 522 "com.android.apps.inputmethod.latin", "DummyLatinIme", !IS_AUX, IS_DEFAULT, 523 subtypes); 524 final ArrayList<InputMethodSubtype> result = 525 InputMethodUtils.getImplicitlyApplicableSubtypesLocked( 526 getResourcesForLocales(LOCALE_FI), imi); 527 assertEquals(1, result.size()); 528 verifyEquality(nonAutoJa, result.get(0)); 529 } 530 531 // Make sure that "in" and "id" conversion is taken into account. 532 { 533 final ArrayList<InputMethodSubtype> subtypes = new ArrayList<>(); 534 subtypes.add(nonAutoIn); 535 subtypes.add(nonAutoEnUS); 536 final InputMethodInfo imi = createDummyInputMethodInfo( 537 "com.android.apps.inputmethod.latin", 538 "com.android.apps.inputmethod.latin", "DummyLatinIme", !IS_AUX, IS_DEFAULT, 539 subtypes); 540 final ArrayList<InputMethodSubtype> result = 541 InputMethodUtils.getImplicitlyApplicableSubtypesLocked( 542 getResourcesForLocales(LOCALE_IN), imi); 543 assertEquals(1, result.size()); 544 verifyEquality(nonAutoIn, result.get(0)); 545 } 546 { 547 final ArrayList<InputMethodSubtype> subtypes = new ArrayList<>(); 548 subtypes.add(nonAutoIn); 549 subtypes.add(nonAutoEnUS); 550 final InputMethodInfo imi = createDummyInputMethodInfo( 551 "com.android.apps.inputmethod.latin", 552 "com.android.apps.inputmethod.latin", "DummyLatinIme", !IS_AUX, IS_DEFAULT, 553 subtypes); 554 final ArrayList<InputMethodSubtype> result = 555 InputMethodUtils.getImplicitlyApplicableSubtypesLocked( 556 getResourcesForLocales(LOCALE_ID), imi); 557 assertEquals(1, result.size()); 558 verifyEquality(nonAutoIn, result.get(0)); 559 } 560 { 561 final ArrayList<InputMethodSubtype> subtypes = new ArrayList<>(); 562 subtypes.add(nonAutoId); 563 subtypes.add(nonAutoEnUS); 564 final InputMethodInfo imi = createDummyInputMethodInfo( 565 "com.android.apps.inputmethod.latin", 566 "com.android.apps.inputmethod.latin", "DummyLatinIme", !IS_AUX, IS_DEFAULT, 567 subtypes); 568 final ArrayList<InputMethodSubtype> result = 569 InputMethodUtils.getImplicitlyApplicableSubtypesLocked( 570 getResourcesForLocales(LOCALE_IN), imi); 571 assertEquals(1, result.size()); 572 verifyEquality(nonAutoId, result.get(0)); 573 } 574 { 575 final ArrayList<InputMethodSubtype> subtypes = new ArrayList<>(); 576 subtypes.add(nonAutoId); 577 subtypes.add(nonAutoEnUS); 578 final InputMethodInfo imi = createDummyInputMethodInfo( 579 "com.android.apps.inputmethod.latin", 580 "com.android.apps.inputmethod.latin", "DummyLatinIme", !IS_AUX, IS_DEFAULT, 581 subtypes); 582 final ArrayList<InputMethodSubtype> result = 583 InputMethodUtils.getImplicitlyApplicableSubtypesLocked( 584 getResourcesForLocales(LOCALE_ID), imi); 585 assertEquals(1, result.size()); 586 verifyEquality(nonAutoId, result.get(0)); 587 } 588 589 // If there is no automatic subtype (overridesImplicitlyEnabledSubtype:true) and the system 590 // provides multiple locales, we try to enable multiple subtypes. 591 { 592 final ArrayList<InputMethodSubtype> subtypes = new ArrayList<>(); 593 subtypes.add(nonAutoEnUS); 594 subtypes.add(nonAutoFrCA); 595 subtypes.add(nonAutoIn); 596 subtypes.add(nonAutoJa); 597 subtypes.add(nonAutoFil); 598 subtypes.add(nonAutoEnabledWhenDefaultIsNotAsciiCalableSubtype); 599 subtypes.add(nonAutoEnabledWhenDefaultIsNotAsciiCalableSubtype2); 600 final InputMethodInfo imi = createDummyInputMethodInfo( 601 "com.android.apps.inputmethod.latin", 602 "com.android.apps.inputmethod.latin", "DummyLatinIme", !IS_AUX, IS_DEFAULT, 603 subtypes); 604 final ArrayList<InputMethodSubtype> result = 605 InputMethodUtils.getImplicitlyApplicableSubtypesLocked( 606 getResourcesForLocales(LOCALE_FR, LOCALE_EN_US, LOCALE_JA_JP), imi); 607 assertThat(nonAutoFrCA, isIn(result)); 608 assertThat(nonAutoEnUS, isIn(result)); 609 assertThat(nonAutoJa, isIn(result)); 610 assertThat(nonAutoIn, not(isIn(result))); 611 assertThat(nonAutoEnabledWhenDefaultIsNotAsciiCalableSubtype, not(isIn(result))); 612 assertThat(nonAutoEnabledWhenDefaultIsNotAsciiCalableSubtype, not(isIn(result))); 613 } 614 } 615 616 @SmallTest testContainsSubtypeOf()617 public void testContainsSubtypeOf() throws Exception { 618 final InputMethodSubtype nonAutoEnUS = createDummyInputMethodSubtype("en_US", 619 SUBTYPE_MODE_KEYBOARD, !IS_AUX, !IS_OVERRIDES_IMPLICITLY_ENABLED_SUBTYPE, 620 IS_ASCII_CAPABLE, !IS_ENABLED_WHEN_DEFAULT_IS_NOT_ASCII_CAPABLE); 621 final InputMethodSubtype nonAutoEnGB = createDummyInputMethodSubtype("en_GB", 622 SUBTYPE_MODE_KEYBOARD, !IS_AUX, !IS_OVERRIDES_IMPLICITLY_ENABLED_SUBTYPE, 623 IS_ASCII_CAPABLE, IS_ENABLED_WHEN_DEFAULT_IS_NOT_ASCII_CAPABLE); 624 final InputMethodSubtype nonAutoFil = createDummyInputMethodSubtype("fil", 625 SUBTYPE_MODE_KEYBOARD, !IS_AUX, !IS_OVERRIDES_IMPLICITLY_ENABLED_SUBTYPE, 626 IS_ASCII_CAPABLE, !IS_ENABLED_WHEN_DEFAULT_IS_NOT_ASCII_CAPABLE); 627 final InputMethodSubtype nonAutoFilPH = createDummyInputMethodSubtype("fil_PH", 628 SUBTYPE_MODE_KEYBOARD, !IS_AUX, !IS_OVERRIDES_IMPLICITLY_ENABLED_SUBTYPE, 629 IS_ASCII_CAPABLE, !IS_ENABLED_WHEN_DEFAULT_IS_NOT_ASCII_CAPABLE); 630 final InputMethodSubtype nonAutoIn = createDummyInputMethodSubtype("in", 631 SUBTYPE_MODE_KEYBOARD, !IS_AUX, !IS_OVERRIDES_IMPLICITLY_ENABLED_SUBTYPE, 632 IS_ASCII_CAPABLE, IS_ENABLED_WHEN_DEFAULT_IS_NOT_ASCII_CAPABLE); 633 final InputMethodSubtype nonAutoId = createDummyInputMethodSubtype("id", 634 SUBTYPE_MODE_KEYBOARD, !IS_AUX, !IS_OVERRIDES_IMPLICITLY_ENABLED_SUBTYPE, 635 IS_ASCII_CAPABLE, IS_ENABLED_WHEN_DEFAULT_IS_NOT_ASCII_CAPABLE); 636 637 final boolean CHECK_COUNTRY = true; 638 639 { 640 final ArrayList<InputMethodSubtype> subtypes = new ArrayList<>(); 641 subtypes.add(nonAutoEnUS); 642 final InputMethodInfo imi = createDummyInputMethodInfo( 643 "com.android.apps.inputmethod.latin", 644 "com.android.apps.inputmethod.latin", "DummyLatinIme", !IS_AUX, IS_DEFAULT, 645 subtypes); 646 647 assertTrue(InputMethodUtils.containsSubtypeOf(imi, LOCALE_EN, !CHECK_COUNTRY, 648 SUBTYPE_MODE_KEYBOARD)); 649 assertFalse(InputMethodUtils.containsSubtypeOf(imi, LOCALE_EN, CHECK_COUNTRY, 650 SUBTYPE_MODE_KEYBOARD)); 651 assertTrue(InputMethodUtils.containsSubtypeOf(imi, LOCALE_EN_US, !CHECK_COUNTRY, 652 SUBTYPE_MODE_KEYBOARD)); 653 assertTrue(InputMethodUtils.containsSubtypeOf(imi, LOCALE_EN_US, CHECK_COUNTRY, 654 SUBTYPE_MODE_KEYBOARD)); 655 assertFalse(InputMethodUtils.containsSubtypeOf(imi, LOCALE_EN_US, !CHECK_COUNTRY, 656 SUBTYPE_MODE_VOICE)); 657 assertFalse(InputMethodUtils.containsSubtypeOf(imi, LOCALE_EN_US, CHECK_COUNTRY, 658 SUBTYPE_MODE_VOICE)); 659 assertTrue(InputMethodUtils.containsSubtypeOf(imi, LOCALE_EN_US, !CHECK_COUNTRY, 660 SUBTYPE_MODE_ANY)); 661 assertTrue(InputMethodUtils.containsSubtypeOf(imi, LOCALE_EN_US, CHECK_COUNTRY, 662 SUBTYPE_MODE_ANY)); 663 664 assertTrue(InputMethodUtils.containsSubtypeOf(imi, LOCALE_EN_GB, !CHECK_COUNTRY, 665 SUBTYPE_MODE_KEYBOARD)); 666 assertFalse(InputMethodUtils.containsSubtypeOf(imi, LOCALE_EN_GB, CHECK_COUNTRY, 667 SUBTYPE_MODE_KEYBOARD)); 668 } 669 670 // Make sure that 3-letter language code ("fil") can be handled. 671 { 672 final ArrayList<InputMethodSubtype> subtypes = new ArrayList<>(); 673 subtypes.add(nonAutoFil); 674 final InputMethodInfo imi = createDummyInputMethodInfo( 675 "com.android.apps.inputmethod.latin", 676 "com.android.apps.inputmethod.latin", "DummyLatinIme", !IS_AUX, IS_DEFAULT, 677 subtypes); 678 assertTrue(InputMethodUtils.containsSubtypeOf(imi, LOCALE_FIL, !CHECK_COUNTRY, 679 SUBTYPE_MODE_KEYBOARD)); 680 assertTrue(InputMethodUtils.containsSubtypeOf(imi, LOCALE_FIL, CHECK_COUNTRY, 681 SUBTYPE_MODE_KEYBOARD)); 682 assertTrue(InputMethodUtils.containsSubtypeOf(imi, LOCALE_FIL_PH, !CHECK_COUNTRY, 683 SUBTYPE_MODE_KEYBOARD)); 684 assertFalse(InputMethodUtils.containsSubtypeOf(imi, LOCALE_FIL_PH, CHECK_COUNTRY, 685 SUBTYPE_MODE_KEYBOARD)); 686 687 assertFalse(InputMethodUtils.containsSubtypeOf(imi, LOCALE_FI, !CHECK_COUNTRY, 688 SUBTYPE_MODE_KEYBOARD)); 689 assertFalse(InputMethodUtils.containsSubtypeOf(imi, LOCALE_FI, CHECK_COUNTRY, 690 SUBTYPE_MODE_KEYBOARD)); 691 assertFalse(InputMethodUtils.containsSubtypeOf(imi, LOCALE_FI_FI, !CHECK_COUNTRY, 692 SUBTYPE_MODE_KEYBOARD)); 693 assertFalse(InputMethodUtils.containsSubtypeOf(imi, LOCALE_FI_FI, CHECK_COUNTRY, 694 SUBTYPE_MODE_KEYBOARD)); 695 } 696 697 // Make sure that 3-letter language code ("fil_PH") can be handled. 698 { 699 final ArrayList<InputMethodSubtype> subtypes = new ArrayList<>(); 700 subtypes.add(nonAutoFilPH); 701 final InputMethodInfo imi = createDummyInputMethodInfo( 702 "com.android.apps.inputmethod.latin", 703 "com.android.apps.inputmethod.latin", "DummyLatinIme", !IS_AUX, IS_DEFAULT, 704 subtypes); 705 assertTrue(InputMethodUtils.containsSubtypeOf(imi, LOCALE_FIL, !CHECK_COUNTRY, 706 SUBTYPE_MODE_KEYBOARD)); 707 assertFalse(InputMethodUtils.containsSubtypeOf(imi, LOCALE_FIL, CHECK_COUNTRY, 708 SUBTYPE_MODE_KEYBOARD)); 709 assertTrue(InputMethodUtils.containsSubtypeOf(imi, LOCALE_FIL_PH, !CHECK_COUNTRY, 710 SUBTYPE_MODE_KEYBOARD)); 711 assertTrue(InputMethodUtils.containsSubtypeOf(imi, LOCALE_FIL_PH, CHECK_COUNTRY, 712 SUBTYPE_MODE_KEYBOARD)); 713 714 assertFalse(InputMethodUtils.containsSubtypeOf(imi, LOCALE_FI, !CHECK_COUNTRY, 715 SUBTYPE_MODE_KEYBOARD)); 716 assertFalse(InputMethodUtils.containsSubtypeOf(imi, LOCALE_FI, CHECK_COUNTRY, 717 SUBTYPE_MODE_KEYBOARD)); 718 assertFalse(InputMethodUtils.containsSubtypeOf(imi, LOCALE_FI_FI, !CHECK_COUNTRY, 719 SUBTYPE_MODE_KEYBOARD)); 720 assertFalse(InputMethodUtils.containsSubtypeOf(imi, LOCALE_FI_FI, CHECK_COUNTRY, 721 SUBTYPE_MODE_KEYBOARD)); 722 } 723 724 // Make sure that a subtype whose locale is "in" can be queried with "id". 725 { 726 final ArrayList<InputMethodSubtype> subtypes = new ArrayList<>(); 727 subtypes.add(nonAutoIn); 728 subtypes.add(nonAutoEnUS); 729 final InputMethodInfo imi = createDummyInputMethodInfo( 730 "com.android.apps.inputmethod.latin", 731 "com.android.apps.inputmethod.latin", "DummyLatinIme", !IS_AUX, IS_DEFAULT, 732 subtypes); 733 assertTrue(InputMethodUtils.containsSubtypeOf(imi, LOCALE_IN, !CHECK_COUNTRY, 734 SUBTYPE_MODE_KEYBOARD)); 735 assertTrue(InputMethodUtils.containsSubtypeOf(imi, LOCALE_IN, CHECK_COUNTRY, 736 SUBTYPE_MODE_KEYBOARD)); 737 assertTrue(InputMethodUtils.containsSubtypeOf(imi, LOCALE_ID, !CHECK_COUNTRY, 738 SUBTYPE_MODE_KEYBOARD)); 739 assertTrue(InputMethodUtils.containsSubtypeOf(imi, LOCALE_ID, CHECK_COUNTRY, 740 SUBTYPE_MODE_KEYBOARD)); 741 } 742 743 // Make sure that a subtype whose locale is "id" can be queried with "in". 744 { 745 final ArrayList<InputMethodSubtype> subtypes = new ArrayList<>(); 746 subtypes.add(nonAutoId); 747 subtypes.add(nonAutoEnUS); 748 final InputMethodInfo imi = createDummyInputMethodInfo( 749 "com.android.apps.inputmethod.latin", 750 "com.android.apps.inputmethod.latin", "DummyLatinIme", !IS_AUX, IS_DEFAULT, 751 subtypes); 752 assertTrue(InputMethodUtils.containsSubtypeOf(imi, LOCALE_IN, !CHECK_COUNTRY, 753 SUBTYPE_MODE_KEYBOARD)); 754 assertTrue(InputMethodUtils.containsSubtypeOf(imi, LOCALE_IN, CHECK_COUNTRY, 755 SUBTYPE_MODE_KEYBOARD)); 756 assertTrue(InputMethodUtils.containsSubtypeOf(imi, LOCALE_ID, !CHECK_COUNTRY, 757 SUBTYPE_MODE_KEYBOARD)); 758 assertTrue(InputMethodUtils.containsSubtypeOf(imi, LOCALE_ID, CHECK_COUNTRY, 759 SUBTYPE_MODE_KEYBOARD)); 760 } 761 } 762 assertDefaultEnabledImes(final ArrayList<InputMethodInfo> preinstalledImes, final Locale systemLocale, String... expectedImeNames)763 private void assertDefaultEnabledImes(final ArrayList<InputMethodInfo> preinstalledImes, 764 final Locale systemLocale, String... expectedImeNames) { 765 final Context context = createTargetContextWithLocales(new LocaleList(systemLocale)); 766 final String[] actualImeNames = getPackageNames( 767 InputMethodUtils.getDefaultEnabledImes(context, preinstalledImes)); 768 assertEquals(expectedImeNames.length, actualImeNames.length); 769 for (int i = 0; i < expectedImeNames.length; ++i) { 770 assertEquals(expectedImeNames[i], actualImeNames[i]); 771 } 772 } 773 cloneViaParcel(final List<InputMethodInfo> list)774 private static List<InputMethodInfo> cloneViaParcel(final List<InputMethodInfo> list) { 775 Parcel p = null; 776 try { 777 p = Parcel.obtain(); 778 p.writeTypedList(list); 779 p.setDataPosition(0); 780 return p.createTypedArrayList(InputMethodInfo.CREATOR); 781 } finally { 782 if (p != null) { 783 p.recycle(); 784 } 785 } 786 } 787 createTargetContextWithLocales(final LocaleList locales)788 private Context createTargetContextWithLocales(final LocaleList locales) { 789 final Configuration resourceConfiguration = new Configuration(); 790 resourceConfiguration.setLocales(locales); 791 return getInstrumentation() 792 .getTargetContext() 793 .createConfigurationContext(resourceConfiguration); 794 } 795 getResourcesForLocales(Locale... locales)796 private Resources getResourcesForLocales(Locale... locales) { 797 return createTargetContextWithLocales(new LocaleList(locales)).getResources(); 798 } 799 getPackageNames(final ArrayList<InputMethodInfo> imis)800 private String[] getPackageNames(final ArrayList<InputMethodInfo> imis) { 801 final String[] packageNames = new String[imis.size()]; 802 for (int i = 0; i < imis.size(); ++i) { 803 packageNames[i] = imis.get(i).getPackageName(); 804 } 805 return packageNames; 806 } 807 verifyEquality(InputMethodInfo expected, InputMethodInfo actual)808 private static void verifyEquality(InputMethodInfo expected, InputMethodInfo actual) { 809 assertEquals(expected, actual); 810 assertEquals(expected.getSubtypeCount(), actual.getSubtypeCount()); 811 for (int subtypeIndex = 0; subtypeIndex < expected.getSubtypeCount(); ++subtypeIndex) { 812 final InputMethodSubtype expectedSubtype = expected.getSubtypeAt(subtypeIndex); 813 final InputMethodSubtype actualSubtype = actual.getSubtypeAt(subtypeIndex); 814 verifyEquality(expectedSubtype, actualSubtype); 815 } 816 } 817 verifyEquality(InputMethodSubtype expected, InputMethodSubtype actual)818 private static void verifyEquality(InputMethodSubtype expected, InputMethodSubtype actual) { 819 assertEquals(expected, actual); 820 assertEquals(expected.hashCode(), actual.hashCode()); 821 } 822 createDummyInputMethodInfo(String packageName, String name, CharSequence label, boolean isAuxIme, boolean isDefault, List<InputMethodSubtype> subtypes)823 private static InputMethodInfo createDummyInputMethodInfo(String packageName, String name, 824 CharSequence label, boolean isAuxIme, boolean isDefault, 825 List<InputMethodSubtype> subtypes) { 826 final ResolveInfo ri = new ResolveInfo(); 827 final ServiceInfo si = new ServiceInfo(); 828 final ApplicationInfo ai = new ApplicationInfo(); 829 ai.packageName = packageName; 830 ai.enabled = true; 831 ai.flags |= ApplicationInfo.FLAG_SYSTEM; 832 si.applicationInfo = ai; 833 si.enabled = true; 834 si.packageName = packageName; 835 si.name = name; 836 si.exported = true; 837 si.nonLocalizedLabel = label; 838 ri.serviceInfo = si; 839 return new InputMethodInfo(ri, isAuxIme, "", subtypes, 1, isDefault); 840 } 841 createDummyInputMethodSubtype(String locale, String mode, boolean isAuxiliary, boolean overridesImplicitlyEnabledSubtype, boolean isAsciiCapable, boolean isEnabledWhenDefaultIsNotAsciiCapable)842 private static InputMethodSubtype createDummyInputMethodSubtype(String locale, String mode, 843 boolean isAuxiliary, boolean overridesImplicitlyEnabledSubtype, 844 boolean isAsciiCapable, boolean isEnabledWhenDefaultIsNotAsciiCapable) { 845 return createDummyInputMethodSubtype(locale, null /* languageTag */, mode, isAuxiliary, 846 overridesImplicitlyEnabledSubtype, isAsciiCapable, 847 isEnabledWhenDefaultIsNotAsciiCapable); 848 } 849 createDummyInputMethodSubtype(String locale, String languageTag, String mode, boolean isAuxiliary, boolean overridesImplicitlyEnabledSubtype, boolean isAsciiCapable, boolean isEnabledWhenDefaultIsNotAsciiCapable)850 private static InputMethodSubtype createDummyInputMethodSubtype(String locale, 851 String languageTag, String mode, boolean isAuxiliary, 852 boolean overridesImplicitlyEnabledSubtype, boolean isAsciiCapable, 853 boolean isEnabledWhenDefaultIsNotAsciiCapable) { 854 final StringBuilder subtypeExtraValue = new StringBuilder(); 855 if (isEnabledWhenDefaultIsNotAsciiCapable) { 856 subtypeExtraValue.append(EXTRA_VALUE_PAIR_SEPARATOR); 857 subtypeExtraValue.append(EXTRA_VALUE_ENABLED_WHEN_DEFAULT_IS_NOT_ASCII_CAPABLE); 858 } 859 860 // TODO: Remove following code. InputMethodSubtype#isAsciiCapable() has been publicly 861 // available since API level 19 (KitKat). We no longer need to rely on extra value. 862 if (isAsciiCapable) { 863 subtypeExtraValue.append(EXTRA_VALUE_PAIR_SEPARATOR); 864 subtypeExtraValue.append(EXTRA_VALUE_ASCII_CAPABLE); 865 } 866 867 return new InputMethodSubtypeBuilder() 868 .setSubtypeNameResId(0) 869 .setSubtypeIconResId(0) 870 .setSubtypeLocale(locale) 871 .setLanguageTag(languageTag) 872 .setSubtypeMode(mode) 873 .setSubtypeExtraValue(subtypeExtraValue.toString()) 874 .setIsAuxiliary(isAuxiliary) 875 .setOverridesImplicitlyEnabledSubtype(overridesImplicitlyEnabledSubtype) 876 .setIsAsciiCapable(isAsciiCapable) 877 .build(); 878 } 879 getImesWithDefaultVoiceIme()880 private static ArrayList<InputMethodInfo> getImesWithDefaultVoiceIme() { 881 ArrayList<InputMethodInfo> preinstalledImes = new ArrayList<>(); 882 { 883 final ArrayList<InputMethodSubtype> subtypes = new ArrayList<>(); 884 subtypes.add(createDummyInputMethodSubtype("auto", SUBTYPE_MODE_VOICE, IS_AUX, 885 IS_OVERRIDES_IMPLICITLY_ENABLED_SUBTYPE, !IS_ASCII_CAPABLE, 886 !IS_ENABLED_WHEN_DEFAULT_IS_NOT_ASCII_CAPABLE)); 887 subtypes.add(createDummyInputMethodSubtype("en_US", SUBTYPE_MODE_VOICE, IS_AUX, 888 !IS_OVERRIDES_IMPLICITLY_ENABLED_SUBTYPE, !IS_ASCII_CAPABLE, 889 !IS_ENABLED_WHEN_DEFAULT_IS_NOT_ASCII_CAPABLE)); 890 preinstalledImes.add(createDummyInputMethodInfo("DummyDefaultAutoVoiceIme", 891 "dummy.voice0", "DummyVoice0", IS_AUX, IS_DEFAULT, subtypes)); 892 } 893 preinstalledImes.addAll(getImesWithoutDefaultVoiceIme()); 894 return preinstalledImes; 895 } 896 getImesWithoutDefaultVoiceIme()897 private static ArrayList<InputMethodInfo> getImesWithoutDefaultVoiceIme() { 898 ArrayList<InputMethodInfo> preinstalledImes = new ArrayList<>(); 899 { 900 final ArrayList<InputMethodSubtype> subtypes = new ArrayList<>(); 901 subtypes.add(createDummyInputMethodSubtype("auto", SUBTYPE_MODE_VOICE, IS_AUX, 902 IS_OVERRIDES_IMPLICITLY_ENABLED_SUBTYPE, !IS_ASCII_CAPABLE, 903 !IS_ENABLED_WHEN_DEFAULT_IS_NOT_ASCII_CAPABLE)); 904 subtypes.add(createDummyInputMethodSubtype("en_US", SUBTYPE_MODE_VOICE, IS_AUX, 905 !IS_OVERRIDES_IMPLICITLY_ENABLED_SUBTYPE, !IS_ASCII_CAPABLE, 906 !IS_ENABLED_WHEN_DEFAULT_IS_NOT_ASCII_CAPABLE)); 907 preinstalledImes.add(createDummyInputMethodInfo("DummyNonDefaultAutoVoiceIme0", 908 "dummy.voice1", "DummyVoice1", IS_AUX, !IS_DEFAULT, subtypes)); 909 } 910 { 911 final ArrayList<InputMethodSubtype> subtypes = new ArrayList<>(); 912 subtypes.add(createDummyInputMethodSubtype("auto", SUBTYPE_MODE_VOICE, IS_AUX, 913 IS_OVERRIDES_IMPLICITLY_ENABLED_SUBTYPE, !IS_ASCII_CAPABLE, 914 !IS_ENABLED_WHEN_DEFAULT_IS_NOT_ASCII_CAPABLE)); 915 subtypes.add(createDummyInputMethodSubtype("en_US", SUBTYPE_MODE_VOICE, IS_AUX, 916 !IS_OVERRIDES_IMPLICITLY_ENABLED_SUBTYPE, !IS_ASCII_CAPABLE, 917 !IS_ENABLED_WHEN_DEFAULT_IS_NOT_ASCII_CAPABLE)); 918 preinstalledImes.add(createDummyInputMethodInfo("DummyNonDefaultAutoVoiceIme1", 919 "dummy.voice2", "DummyVoice2", IS_AUX, !IS_DEFAULT, subtypes)); 920 } 921 { 922 final ArrayList<InputMethodSubtype> subtypes = new ArrayList<>(); 923 subtypes.add(createDummyInputMethodSubtype("en_US", SUBTYPE_MODE_VOICE, IS_AUX, 924 !IS_OVERRIDES_IMPLICITLY_ENABLED_SUBTYPE, !IS_ASCII_CAPABLE, 925 !IS_ENABLED_WHEN_DEFAULT_IS_NOT_ASCII_CAPABLE)); 926 preinstalledImes.add(createDummyInputMethodInfo("DummyNonDefaultVoiceIme2", 927 "dummy.voice3", "DummyVoice3", IS_AUX, !IS_DEFAULT, subtypes)); 928 } 929 { 930 final ArrayList<InputMethodSubtype> subtypes = new ArrayList<>(); 931 subtypes.add(createDummyInputMethodSubtype("en_US", SUBTYPE_MODE_KEYBOARD, !IS_AUX, 932 !IS_OVERRIDES_IMPLICITLY_ENABLED_SUBTYPE, IS_ASCII_CAPABLE, 933 !IS_ENABLED_WHEN_DEFAULT_IS_NOT_ASCII_CAPABLE)); 934 preinstalledImes.add(createDummyInputMethodInfo("DummyDefaultEnKeyboardIme", 935 "dummy.keyboard0", "DummyKeyboard0", !IS_AUX, IS_DEFAULT, subtypes)); 936 } 937 return preinstalledImes; 938 } 939 contains(final String[] textList, final String textToBeChecked)940 private static boolean contains(final String[] textList, final String textToBeChecked) { 941 if (textList == null) { 942 return false; 943 } 944 for (final String text : textList) { 945 if (Objects.equals(textToBeChecked, text)) { 946 return true; 947 } 948 } 949 return false; 950 } 951 getSamplePreinstalledImes(final String localeString)952 private static ArrayList<InputMethodInfo> getSamplePreinstalledImes(final String localeString) { 953 ArrayList<InputMethodInfo> preinstalledImes = new ArrayList<>(); 954 955 // a dummy Voice IME 956 { 957 final boolean isDefaultIme = false; 958 final ArrayList<InputMethodSubtype> subtypes = new ArrayList<>(); 959 subtypes.add(createDummyInputMethodSubtype("", SUBTYPE_MODE_VOICE, IS_AUX, 960 IS_OVERRIDES_IMPLICITLY_ENABLED_SUBTYPE, !IS_ASCII_CAPABLE, 961 !IS_ENABLED_WHEN_DEFAULT_IS_NOT_ASCII_CAPABLE)); 962 preinstalledImes.add(createDummyInputMethodInfo("com.android.apps.inputmethod.voice", 963 "com.android.inputmethod.voice", "DummyVoiceIme", IS_AUX, isDefaultIme, 964 subtypes)); 965 } 966 // a dummy Hindi IME 967 { 968 final boolean isDefaultIme = contains(new String[]{ "hi", "en-rIN" }, localeString); 969 final ArrayList<InputMethodSubtype> subtypes = new ArrayList<>(); 970 // TODO: This subtype should be marked as IS_ASCII_CAPABLE 971 subtypes.add(createDummyInputMethodSubtype("en_IN", SUBTYPE_MODE_KEYBOARD, !IS_AUX, 972 !IS_OVERRIDES_IMPLICITLY_ENABLED_SUBTYPE, !IS_ASCII_CAPABLE, 973 !IS_ENABLED_WHEN_DEFAULT_IS_NOT_ASCII_CAPABLE)); 974 subtypes.add(createDummyInputMethodSubtype("hi", SUBTYPE_MODE_KEYBOARD, !IS_AUX, 975 !IS_OVERRIDES_IMPLICITLY_ENABLED_SUBTYPE, !IS_ASCII_CAPABLE, 976 !IS_ENABLED_WHEN_DEFAULT_IS_NOT_ASCII_CAPABLE)); 977 preinstalledImes.add(createDummyInputMethodInfo("com.android.apps.inputmethod.hindi", 978 "com.android.inputmethod.hindi", "DummyHindiIme", !IS_AUX, isDefaultIme, 979 subtypes)); 980 } 981 982 // a dummy Pinyin IME 983 { 984 final boolean isDefaultIme = contains(new String[]{ "zh-rCN" }, localeString); 985 final ArrayList<InputMethodSubtype> subtypes = new ArrayList<>(); 986 subtypes.add(createDummyInputMethodSubtype("zh_CN", SUBTYPE_MODE_KEYBOARD, !IS_AUX, 987 !IS_OVERRIDES_IMPLICITLY_ENABLED_SUBTYPE, !IS_ASCII_CAPABLE, 988 !IS_ENABLED_WHEN_DEFAULT_IS_NOT_ASCII_CAPABLE)); 989 preinstalledImes.add(createDummyInputMethodInfo("com.android.apps.inputmethod.pinyin", 990 "com.android.apps.inputmethod.pinyin", "DummyPinyinIme", !IS_AUX, isDefaultIme, 991 subtypes)); 992 } 993 994 // a dummy Korean IME 995 { 996 final boolean isDefaultIme = contains(new String[]{ "ko" }, localeString); 997 final ArrayList<InputMethodSubtype> subtypes = new ArrayList<>(); 998 subtypes.add(createDummyInputMethodSubtype("ko", SUBTYPE_MODE_KEYBOARD, !IS_AUX, 999 !IS_OVERRIDES_IMPLICITLY_ENABLED_SUBTYPE, !IS_ASCII_CAPABLE, 1000 !IS_ENABLED_WHEN_DEFAULT_IS_NOT_ASCII_CAPABLE)); 1001 preinstalledImes.add(createDummyInputMethodInfo("com.android.apps.inputmethod.korean", 1002 "com.android.apps.inputmethod.korean", "DummyKoreanIme", !IS_AUX, isDefaultIme, 1003 subtypes)); 1004 } 1005 1006 // a dummy Latin IME 1007 { 1008 final boolean isDefaultIme = contains( 1009 new String[]{ "en-rUS", "en-rGB", "en-rIN", "en", "hi" }, localeString); 1010 final ArrayList<InputMethodSubtype> subtypes = new ArrayList<>(); 1011 subtypes.add(createDummyInputMethodSubtype("en_US", SUBTYPE_MODE_KEYBOARD, !IS_AUX, 1012 !IS_OVERRIDES_IMPLICITLY_ENABLED_SUBTYPE, IS_ASCII_CAPABLE, 1013 !IS_ENABLED_WHEN_DEFAULT_IS_NOT_ASCII_CAPABLE)); 1014 subtypes.add(createDummyInputMethodSubtype("en_GB", SUBTYPE_MODE_KEYBOARD, !IS_AUX, 1015 !IS_OVERRIDES_IMPLICITLY_ENABLED_SUBTYPE, IS_ASCII_CAPABLE, 1016 !IS_ENABLED_WHEN_DEFAULT_IS_NOT_ASCII_CAPABLE)); 1017 subtypes.add(createDummyInputMethodSubtype("en_IN", SUBTYPE_MODE_KEYBOARD, !IS_AUX, 1018 !IS_OVERRIDES_IMPLICITLY_ENABLED_SUBTYPE, IS_ASCII_CAPABLE, 1019 !IS_ENABLED_WHEN_DEFAULT_IS_NOT_ASCII_CAPABLE)); 1020 subtypes.add(createDummyInputMethodSubtype("hi", SUBTYPE_MODE_KEYBOARD, !IS_AUX, 1021 !IS_OVERRIDES_IMPLICITLY_ENABLED_SUBTYPE, IS_ASCII_CAPABLE, 1022 !IS_ENABLED_WHEN_DEFAULT_IS_NOT_ASCII_CAPABLE)); 1023 preinstalledImes.add(createDummyInputMethodInfo("com.android.apps.inputmethod.latin", 1024 "com.android.apps.inputmethod.latin", "DummyLatinIme", !IS_AUX, isDefaultIme, 1025 subtypes)); 1026 } 1027 1028 // a dummy Japanese IME 1029 { 1030 final boolean isDefaultIme = contains(new String[]{ "ja", "ja-rJP" }, localeString); 1031 final ArrayList<InputMethodSubtype> subtypes = new ArrayList<>(); 1032 subtypes.add(createDummyInputMethodSubtype("ja", SUBTYPE_MODE_KEYBOARD, !IS_AUX, 1033 !IS_OVERRIDES_IMPLICITLY_ENABLED_SUBTYPE, !IS_ASCII_CAPABLE, 1034 !IS_ENABLED_WHEN_DEFAULT_IS_NOT_ASCII_CAPABLE)); 1035 subtypes.add(createDummyInputMethodSubtype("emoji", SUBTYPE_MODE_KEYBOARD, !IS_AUX, 1036 !IS_OVERRIDES_IMPLICITLY_ENABLED_SUBTYPE, !IS_ASCII_CAPABLE, 1037 !IS_ENABLED_WHEN_DEFAULT_IS_NOT_ASCII_CAPABLE)); 1038 preinstalledImes.add(createDummyInputMethodInfo("com.android.apps.inputmethod.japanese", 1039 "com.android.apps.inputmethod.japanese", "DummyJapaneseIme", !IS_AUX, 1040 isDefaultIme, subtypes)); 1041 } 1042 1043 return preinstalledImes; 1044 } 1045 1046 @SmallTest testGetSuitableLocalesForSpellChecker()1047 public void testGetSuitableLocalesForSpellChecker() throws Exception { 1048 { 1049 final ArrayList<Locale> locales = 1050 InputMethodUtils.getSuitableLocalesForSpellChecker(LOCALE_EN_US); 1051 assertEquals(3, locales.size()); 1052 assertEquals(LOCALE_EN_US, locales.get(0)); 1053 assertEquals(LOCALE_EN_GB, locales.get(1)); 1054 assertEquals(LOCALE_EN, locales.get(2)); 1055 } 1056 1057 { 1058 final ArrayList<Locale> locales = 1059 InputMethodUtils.getSuitableLocalesForSpellChecker(LOCALE_EN_GB); 1060 assertEquals(3, locales.size()); 1061 assertEquals(LOCALE_EN_GB, locales.get(0)); 1062 assertEquals(LOCALE_EN_US, locales.get(1)); 1063 assertEquals(LOCALE_EN, locales.get(2)); 1064 } 1065 1066 { 1067 final ArrayList<Locale> locales = 1068 InputMethodUtils.getSuitableLocalesForSpellChecker(LOCALE_EN); 1069 assertEquals(3, locales.size()); 1070 assertEquals(LOCALE_EN, locales.get(0)); 1071 assertEquals(LOCALE_EN_US, locales.get(1)); 1072 assertEquals(LOCALE_EN_GB, locales.get(2)); 1073 } 1074 1075 { 1076 final ArrayList<Locale> locales = 1077 InputMethodUtils.getSuitableLocalesForSpellChecker(LOCALE_EN_IN); 1078 assertEquals(4, locales.size()); 1079 assertEquals(LOCALE_EN_IN, locales.get(0)); 1080 assertEquals(LOCALE_EN_US, locales.get(1)); 1081 assertEquals(LOCALE_EN_GB, locales.get(2)); 1082 assertEquals(LOCALE_EN, locales.get(3)); 1083 } 1084 1085 { 1086 final ArrayList<Locale> locales = 1087 InputMethodUtils.getSuitableLocalesForSpellChecker(LOCALE_JA_JP); 1088 assertEquals(5, locales.size()); 1089 assertEquals(LOCALE_JA_JP, locales.get(0)); 1090 assertEquals(LOCALE_JA, locales.get(1)); 1091 assertEquals(LOCALE_EN_US, locales.get(2)); 1092 assertEquals(LOCALE_EN_GB, locales.get(3)); 1093 assertEquals(Locale.ENGLISH, locales.get(4)); 1094 } 1095 1096 // Test 3-letter language code. 1097 { 1098 final ArrayList<Locale> locales = 1099 InputMethodUtils.getSuitableLocalesForSpellChecker(LOCALE_FIL_PH); 1100 assertEquals(5, locales.size()); 1101 assertEquals(LOCALE_FIL_PH, locales.get(0)); 1102 assertEquals(LOCALE_FIL, locales.get(1)); 1103 assertEquals(LOCALE_EN_US, locales.get(2)); 1104 assertEquals(LOCALE_EN_GB, locales.get(3)); 1105 assertEquals(Locale.ENGLISH, locales.get(4)); 1106 } 1107 1108 // Test variant. 1109 { 1110 final ArrayList<Locale> locales = 1111 InputMethodUtils.getSuitableLocalesForSpellChecker(LOCALE_TH_TH_TH); 1112 assertEquals(6, locales.size()); 1113 assertEquals(LOCALE_TH_TH_TH, locales.get(0)); 1114 assertEquals(LOCALE_TH_TH, locales.get(1)); 1115 assertEquals(LOCALE_TH, locales.get(2)); 1116 assertEquals(LOCALE_EN_US, locales.get(3)); 1117 assertEquals(LOCALE_EN_GB, locales.get(4)); 1118 assertEquals(Locale.ENGLISH, locales.get(5)); 1119 } 1120 1121 // Test Locale extension. 1122 { 1123 final Locale localeWithoutVariant = LOCALE_JA_JP; 1124 final Locale localeWithVariant = new Locale.Builder() 1125 .setLocale(LOCALE_JA_JP) 1126 .setExtension('x', "android") 1127 .build(); 1128 assertFalse(localeWithoutVariant.equals(localeWithVariant)); 1129 1130 final ArrayList<Locale> locales = 1131 InputMethodUtils.getSuitableLocalesForSpellChecker(localeWithVariant); 1132 assertEquals(5, locales.size()); 1133 assertEquals(LOCALE_JA_JP, locales.get(0)); 1134 assertEquals(LOCALE_JA, locales.get(1)); 1135 assertEquals(LOCALE_EN_US, locales.get(2)); 1136 assertEquals(LOCALE_EN_GB, locales.get(3)); 1137 assertEquals(Locale.ENGLISH, locales.get(4)); 1138 } 1139 } 1140 1141 @SmallTest testParseInputMethodsAndSubtypesString()1142 public void testParseInputMethodsAndSubtypesString() { 1143 // Trivial cases. 1144 { 1145 assertTrue(InputMethodUtils.parseInputMethodsAndSubtypesString("").isEmpty()); 1146 assertTrue(InputMethodUtils.parseInputMethodsAndSubtypesString(null).isEmpty()); 1147 } 1148 1149 // No subtype cases. 1150 { 1151 ArrayMap<String, ArraySet<String>> r = 1152 InputMethodUtils.parseInputMethodsAndSubtypesString("ime0"); 1153 assertEquals(1, r.size()); 1154 assertTrue(r.containsKey("ime0")); 1155 assertTrue(r.get("ime0").isEmpty()); 1156 } 1157 { 1158 ArrayMap<String, ArraySet<String>> r = 1159 InputMethodUtils.parseInputMethodsAndSubtypesString("ime0:ime1"); 1160 assertEquals(2, r.size()); 1161 assertTrue(r.containsKey("ime0")); 1162 assertTrue(r.get("ime0").isEmpty()); 1163 assertTrue(r.containsKey("ime1")); 1164 assertTrue(r.get("ime1").isEmpty()); 1165 } 1166 1167 // Input metho IDs and their subtypes. 1168 { 1169 ArrayMap<String, ArraySet<String>> r = 1170 InputMethodUtils.parseInputMethodsAndSubtypesString("ime0;subtype0"); 1171 assertEquals(1, r.size()); 1172 assertTrue(r.containsKey("ime0")); 1173 ArraySet<String> subtypes = r.get("ime0"); 1174 assertEquals(1, subtypes.size()); 1175 assertTrue(subtypes.contains("subtype0")); 1176 } 1177 { 1178 ArrayMap<String, ArraySet<String>> r = 1179 InputMethodUtils.parseInputMethodsAndSubtypesString("ime0;subtype0;subtype0"); 1180 assertEquals(1, r.size()); 1181 assertTrue(r.containsKey("ime0")); 1182 ArraySet<String> subtypes = r.get("ime0"); 1183 assertEquals(1, subtypes.size()); 1184 assertTrue(subtypes.contains("subtype0")); 1185 } 1186 { 1187 ArrayMap<String, ArraySet<String>> r = 1188 InputMethodUtils.parseInputMethodsAndSubtypesString("ime0;subtype0;subtype1"); 1189 assertEquals(1, r.size()); 1190 assertTrue(r.containsKey("ime0")); 1191 ArraySet<String> subtypes = r.get("ime0"); 1192 assertEquals(2, subtypes.size()); 1193 assertTrue(subtypes.contains("subtype0")); 1194 assertTrue(subtypes.contains("subtype1")); 1195 } 1196 { 1197 ArrayMap<String, ArraySet<String>> r = 1198 InputMethodUtils.parseInputMethodsAndSubtypesString( 1199 "ime0;subtype0:ime1;subtype1"); 1200 assertEquals(2, r.size()); 1201 assertTrue(r.containsKey("ime0")); 1202 assertTrue(r.containsKey("ime1")); 1203 ArraySet<String> subtypes0 = r.get("ime0"); 1204 assertEquals(1, subtypes0.size()); 1205 assertTrue(subtypes0.contains("subtype0")); 1206 1207 ArraySet<String> subtypes1 = r.get("ime1"); 1208 assertEquals(1, subtypes1.size()); 1209 assertTrue(subtypes1.contains("subtype1")); 1210 } 1211 { 1212 ArrayMap<String, ArraySet<String>> r = 1213 InputMethodUtils.parseInputMethodsAndSubtypesString( 1214 "ime0;subtype0;subtype1:ime1;subtype2"); 1215 assertEquals(2, r.size()); 1216 assertTrue(r.containsKey("ime0")); 1217 assertTrue(r.containsKey("ime1")); 1218 ArraySet<String> subtypes0 = r.get("ime0"); 1219 assertEquals(2, subtypes0.size()); 1220 assertTrue(subtypes0.contains("subtype0")); 1221 assertTrue(subtypes0.contains("subtype1")); 1222 1223 ArraySet<String> subtypes1 = r.get("ime1"); 1224 assertEquals(1, subtypes1.size()); 1225 assertTrue(subtypes1.contains("subtype2")); 1226 } 1227 { 1228 ArrayMap<String, ArraySet<String>> r = 1229 InputMethodUtils.parseInputMethodsAndSubtypesString( 1230 "ime0;subtype0;subtype1:ime1;subtype1;subtype2"); 1231 assertEquals(2, r.size()); 1232 assertTrue(r.containsKey("ime0")); 1233 assertTrue(r.containsKey("ime1")); 1234 ArraySet<String> subtypes0 = r.get("ime0"); 1235 assertEquals(2, subtypes0.size()); 1236 assertTrue(subtypes0.contains("subtype0")); 1237 assertTrue(subtypes0.contains("subtype1")); 1238 1239 ArraySet<String> subtypes1 = r.get("ime1"); 1240 assertEquals(2, subtypes1.size()); 1241 assertTrue(subtypes0.contains("subtype1")); 1242 assertTrue(subtypes1.contains("subtype2")); 1243 } 1244 { 1245 ArrayMap<String, ArraySet<String>> r = 1246 InputMethodUtils.parseInputMethodsAndSubtypesString( 1247 "ime0;subtype0;subtype1:ime1;subtype1;subtype2:ime2"); 1248 assertEquals(3, r.size()); 1249 assertTrue(r.containsKey("ime0")); 1250 assertTrue(r.containsKey("ime1")); 1251 assertTrue(r.containsKey("ime2")); 1252 ArraySet<String> subtypes0 = r.get("ime0"); 1253 assertEquals(2, subtypes0.size()); 1254 assertTrue(subtypes0.contains("subtype0")); 1255 assertTrue(subtypes0.contains("subtype1")); 1256 1257 ArraySet<String> subtypes1 = r.get("ime1"); 1258 assertEquals(2, subtypes1.size()); 1259 assertTrue(subtypes0.contains("subtype1")); 1260 assertTrue(subtypes1.contains("subtype2")); 1261 1262 ArraySet<String> subtypes2 = r.get("ime2"); 1263 assertTrue(subtypes2.isEmpty()); 1264 } 1265 } 1266 1267 @SmallTest testbuildInputMethodsAndSubtypesString()1268 public void testbuildInputMethodsAndSubtypesString() { 1269 { 1270 ArrayMap<String, ArraySet<String>> map = new ArrayMap<>(); 1271 assertEquals("", InputMethodUtils.buildInputMethodsAndSubtypesString(map)); 1272 } 1273 { 1274 ArrayMap<String, ArraySet<String>> map = new ArrayMap<>(); 1275 map.put("ime0", new ArraySet<String>()); 1276 assertEquals("ime0", InputMethodUtils.buildInputMethodsAndSubtypesString(map)); 1277 } 1278 { 1279 ArrayMap<String, ArraySet<String>> map = new ArrayMap<>(); 1280 ArraySet<String> subtypes1 = new ArraySet<>(); 1281 subtypes1.add("subtype0"); 1282 map.put("ime0", subtypes1); 1283 assertEquals("ime0;subtype0", InputMethodUtils.buildInputMethodsAndSubtypesString(map)); 1284 } 1285 { 1286 ArrayMap<String, ArraySet<String>> map = new ArrayMap<>(); 1287 ArraySet<String> subtypes1 = new ArraySet<>(); 1288 subtypes1.add("subtype0"); 1289 subtypes1.add("subtype1"); 1290 map.put("ime0", subtypes1); 1291 1292 // We do not expect what order will be used to concatenate items in 1293 // InputMethodUtils.buildInputMethodsAndSubtypesString() hence enumerate all possible 1294 // permutations here. 1295 ArraySet<String> validSequences = new ArraySet<>(); 1296 validSequences.add("ime0;subtype0;subtype1"); 1297 validSequences.add("ime0;subtype1;subtype0"); 1298 assertTrue(validSequences.contains( 1299 InputMethodUtils.buildInputMethodsAndSubtypesString(map))); 1300 } 1301 { 1302 ArrayMap<String, ArraySet<String>> map = new ArrayMap<>(); 1303 map.put("ime0", new ArraySet<String>()); 1304 map.put("ime1", new ArraySet<String>()); 1305 1306 ArraySet<String> validSequences = new ArraySet<>(); 1307 validSequences.add("ime0:ime1"); 1308 validSequences.add("ime1:ime0"); 1309 assertTrue(validSequences.contains( 1310 InputMethodUtils.buildInputMethodsAndSubtypesString(map))); 1311 } 1312 { 1313 ArrayMap<String, ArraySet<String>> map = new ArrayMap<>(); 1314 ArraySet<String> subtypes1 = new ArraySet<>(); 1315 subtypes1.add("subtype0"); 1316 map.put("ime0", subtypes1); 1317 map.put("ime1", new ArraySet<String>()); 1318 1319 ArraySet<String> validSequences = new ArraySet<>(); 1320 validSequences.add("ime0;subtype0:ime1"); 1321 validSequences.add("ime1;ime0;subtype0"); 1322 assertTrue(validSequences.contains( 1323 InputMethodUtils.buildInputMethodsAndSubtypesString(map))); 1324 } 1325 { 1326 ArrayMap<String, ArraySet<String>> map = new ArrayMap<>(); 1327 ArraySet<String> subtypes1 = new ArraySet<>(); 1328 subtypes1.add("subtype0"); 1329 subtypes1.add("subtype1"); 1330 map.put("ime0", subtypes1); 1331 map.put("ime1", new ArraySet<String>()); 1332 1333 ArraySet<String> validSequences = new ArraySet<>(); 1334 validSequences.add("ime0;subtype0;subtype1:ime1"); 1335 validSequences.add("ime0;subtype1;subtype0:ime1"); 1336 validSequences.add("ime1:ime0;subtype0;subtype1"); 1337 validSequences.add("ime1:ime0;subtype1;subtype0"); 1338 assertTrue(validSequences.contains( 1339 InputMethodUtils.buildInputMethodsAndSubtypesString(map))); 1340 } 1341 { 1342 ArrayMap<String, ArraySet<String>> map = new ArrayMap<>(); 1343 ArraySet<String> subtypes1 = new ArraySet<>(); 1344 subtypes1.add("subtype0"); 1345 map.put("ime0", subtypes1); 1346 1347 ArraySet<String> subtypes2 = new ArraySet<>(); 1348 subtypes2.add("subtype1"); 1349 map.put("ime1", subtypes2); 1350 1351 ArraySet<String> validSequences = new ArraySet<>(); 1352 validSequences.add("ime0;subtype0:ime1;subtype1"); 1353 validSequences.add("ime1;subtype1:ime0;subtype0"); 1354 assertTrue(validSequences.contains( 1355 InputMethodUtils.buildInputMethodsAndSubtypesString(map))); 1356 } 1357 { 1358 ArrayMap<String, ArraySet<String>> map = new ArrayMap<>(); 1359 ArraySet<String> subtypes1 = new ArraySet<>(); 1360 subtypes1.add("subtype0"); 1361 subtypes1.add("subtype1"); 1362 map.put("ime0", subtypes1); 1363 1364 ArraySet<String> subtypes2 = new ArraySet<>(); 1365 subtypes2.add("subtype2"); 1366 subtypes2.add("subtype3"); 1367 map.put("ime1", subtypes2); 1368 1369 ArraySet<String> validSequences = new ArraySet<>(); 1370 validSequences.add("ime0;subtype0;subtype1:ime1;subtype2;subtype3"); 1371 validSequences.add("ime0;subtype1;subtype0:ime1;subtype2;subtype3"); 1372 validSequences.add("ime0;subtype0;subtype1:ime1;subtype3;subtype2"); 1373 validSequences.add("ime0;subtype1;subtype0:ime1;subtype3;subtype2"); 1374 validSequences.add("ime1;subtype2;subtype3:ime0;subtype0;subtype1"); 1375 validSequences.add("ime2;subtype3;subtype2:ime0;subtype0;subtype1"); 1376 validSequences.add("ime3;subtype2;subtype3:ime0;subtype1;subtype0"); 1377 validSequences.add("ime4;subtype3;subtype2:ime0;subtype1;subtype0"); 1378 assertTrue(validSequences.contains( 1379 InputMethodUtils.buildInputMethodsAndSubtypesString(map))); 1380 } 1381 } 1382 1383 @SmallTest testConstructLocaleFromString()1384 public void testConstructLocaleFromString() throws Exception { 1385 assertEquals(new Locale("en"), InputMethodUtils.constructLocaleFromString("en")); 1386 assertEquals(new Locale("en", "US"), InputMethodUtils.constructLocaleFromString("en_US")); 1387 assertEquals(new Locale("en", "US", "POSIX"), 1388 InputMethodUtils.constructLocaleFromString("en_US_POSIX")); 1389 1390 // Special rewrite rule for "tl" for versions of Android earlier than Lollipop that did not 1391 // support three letter language codes, and used "tl" (Tagalog) as the language string for 1392 // "fil" (Filipino). 1393 assertEquals(new Locale("fil"), InputMethodUtils.constructLocaleFromString("tl")); 1394 assertEquals(new Locale("fil", "PH"), InputMethodUtils.constructLocaleFromString("tl_PH")); 1395 assertEquals(new Locale("fil", "PH", "POSIX"), 1396 InputMethodUtils.constructLocaleFromString("tl_PH_POSIX")); 1397 1398 // So far rejecting an invalid/unexpected locale string is out of the scope of this method. 1399 assertEquals(new Locale("a"), InputMethodUtils.constructLocaleFromString("a")); 1400 assertEquals(new Locale("a b c"), InputMethodUtils.constructLocaleFromString("a b c")); 1401 assertEquals(new Locale("en-US"), InputMethodUtils.constructLocaleFromString("en-US")); 1402 } 1403 } 1404