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.server.inputmethod; 18 19 import static org.hamcrest.MatcherAssert.assertThat; 20 import static org.hamcrest.Matchers.in; 21 import static org.hamcrest.Matchers.not; 22 import static org.hamcrest.core.Is.is; 23 import static org.junit.Assert.assertEquals; 24 import static org.junit.Assert.assertFalse; 25 import static org.junit.Assert.assertNotNull; 26 import static org.junit.Assert.assertTrue; 27 28 import android.content.Context; 29 import android.content.pm.ApplicationInfo; 30 import android.content.pm.ResolveInfo; 31 import android.content.pm.ServiceInfo; 32 import android.content.res.Configuration; 33 import android.content.res.Resources; 34 import android.os.Build; 35 import android.os.LocaleList; 36 import android.os.Parcel; 37 import android.view.inputmethod.InputMethodInfo; 38 import android.view.inputmethod.InputMethodSubtype; 39 import android.view.inputmethod.InputMethodSubtype.InputMethodSubtypeBuilder; 40 41 import androidx.test.InstrumentationRegistry; 42 import androidx.test.filters.SmallTest; 43 import androidx.test.runner.AndroidJUnit4; 44 45 import com.android.internal.inputmethod.StartInputFlags; 46 47 import org.junit.Test; 48 import org.junit.runner.RunWith; 49 50 import java.util.ArrayList; 51 import java.util.List; 52 import java.util.Locale; 53 import java.util.Objects; 54 55 @SmallTest 56 @RunWith(AndroidJUnit4.class) 57 public class InputMethodUtilsTest { 58 private static final boolean IS_AUX = true; 59 private static final boolean IS_DEFAULT = true; 60 private static final boolean IS_OVERRIDES_IMPLICITLY_ENABLED_SUBTYPE = true; 61 private static final boolean IS_ASCII_CAPABLE = true; 62 private static final boolean IS_ENABLED_WHEN_DEFAULT_IS_NOT_ASCII_CAPABLE = true; 63 private static final Locale LOCALE_EN = new Locale("en"); 64 private static final Locale LOCALE_EN_US = new Locale("en", "US"); 65 private static final Locale LOCALE_EN_GB = new Locale("en", "GB"); 66 private static final Locale LOCALE_EN_IN = new Locale("en", "IN"); 67 private static final Locale LOCALE_FI = new Locale("fi"); 68 private static final Locale LOCALE_FI_FI = new Locale("fi", "FI"); 69 private static final Locale LOCALE_FIL = new Locale("fil"); 70 private static final Locale LOCALE_FIL_PH = new Locale("fil", "PH"); 71 private static final Locale LOCALE_FR = new Locale("fr"); 72 private static final Locale LOCALE_FR_CA = new Locale("fr", "CA"); 73 private static final Locale LOCALE_HI = new Locale("hi"); 74 private static final Locale LOCALE_JA_JP = new Locale("ja", "JP"); 75 private static final Locale LOCALE_ZH_CN = new Locale("zh", "CN"); 76 private static final Locale LOCALE_ZH_TW = new Locale("zh", "TW"); 77 private static final Locale LOCALE_IN = new Locale("in"); 78 private static final Locale LOCALE_ID = new Locale("id"); 79 private static final String SUBTYPE_MODE_KEYBOARD = "keyboard"; 80 private static final String SUBTYPE_MODE_VOICE = "voice"; 81 private static final String SUBTYPE_MODE_HANDWRITING = "handwriting"; 82 private static final String SUBTYPE_MODE_ANY = null; 83 private static final String EXTRA_VALUE_PAIR_SEPARATOR = ","; 84 private static final String EXTRA_VALUE_ENABLED_WHEN_DEFAULT_IS_NOT_ASCII_CAPABLE = 85 "EnabledWhenDefaultIsNotAsciiCapable"; 86 87 @Test testVoiceImes()88 public void testVoiceImes() throws Exception { 89 // locale: en_US 90 assertDefaultEnabledImes(getImesWithDefaultVoiceIme(), LOCALE_EN_US, 91 "DummyDefaultEnKeyboardIme", "DummyDefaultAutoVoiceIme"); 92 assertDefaultEnabledImes(getImesWithoutDefaultVoiceIme(), LOCALE_EN_US, 93 "DummyDefaultEnKeyboardIme", "DummyNonDefaultAutoVoiceIme0", 94 "DummyNonDefaultAutoVoiceIme1"); 95 assertDefaultEnabledMinimumImes(getImesWithDefaultVoiceIme(), LOCALE_EN_US, 96 "DummyDefaultEnKeyboardIme"); 97 assertDefaultEnabledMinimumImes(getImesWithoutDefaultVoiceIme(), LOCALE_EN_US, 98 "DummyDefaultEnKeyboardIme"); 99 100 // locale: en_GB 101 assertDefaultEnabledImes(getImesWithDefaultVoiceIme(), LOCALE_EN_GB, 102 "DummyDefaultEnKeyboardIme", "DummyDefaultAutoVoiceIme"); 103 assertDefaultEnabledImes(getImesWithoutDefaultVoiceIme(), LOCALE_EN_GB, 104 "DummyDefaultEnKeyboardIme", "DummyNonDefaultAutoVoiceIme0", 105 "DummyNonDefaultAutoVoiceIme1"); 106 assertDefaultEnabledMinimumImes(getImesWithDefaultVoiceIme(), LOCALE_EN_GB, 107 "DummyDefaultEnKeyboardIme"); 108 assertDefaultEnabledMinimumImes(getImesWithoutDefaultVoiceIme(), LOCALE_EN_GB, 109 "DummyDefaultEnKeyboardIme"); 110 111 // locale: ja_JP 112 assertDefaultEnabledImes(getImesWithDefaultVoiceIme(), LOCALE_JA_JP, 113 "DummyDefaultEnKeyboardIme", "DummyDefaultAutoVoiceIme"); 114 assertDefaultEnabledImes(getImesWithoutDefaultVoiceIme(), LOCALE_JA_JP, 115 "DummyDefaultEnKeyboardIme", "DummyNonDefaultAutoVoiceIme0", 116 "DummyNonDefaultAutoVoiceIme1"); 117 assertDefaultEnabledMinimumImes(getImesWithDefaultVoiceIme(), LOCALE_JA_JP, 118 "DummyDefaultEnKeyboardIme"); 119 assertDefaultEnabledMinimumImes(getImesWithoutDefaultVoiceIme(), LOCALE_JA_JP, 120 "DummyDefaultEnKeyboardIme"); 121 } 122 123 @Test testKeyboardImes()124 public void testKeyboardImes() throws Exception { 125 // locale: en_US 126 assertDefaultEnabledImes(getSamplePreinstalledImes("en-rUS"), LOCALE_EN_US, 127 "com.android.apps.inputmethod.latin", "com.android.apps.inputmethod.voice"); 128 assertDefaultEnabledMinimumImes(getSamplePreinstalledImes("en-rUS"), LOCALE_EN_US, 129 "com.android.apps.inputmethod.latin"); 130 131 // locale: en_GB 132 assertDefaultEnabledImes(getSamplePreinstalledImes("en-rGB"), LOCALE_EN_GB, 133 "com.android.apps.inputmethod.latin", "com.android.apps.inputmethod.voice"); 134 assertDefaultEnabledMinimumImes(getSamplePreinstalledImes("en-rGB"), LOCALE_EN_GB, 135 "com.android.apps.inputmethod.latin"); 136 137 // locale: en_IN 138 assertDefaultEnabledImes(getSamplePreinstalledImes("en-rIN"), LOCALE_EN_IN, 139 "com.android.apps.inputmethod.hindi", 140 "com.android.apps.inputmethod.latin", "com.android.apps.inputmethod.voice"); 141 assertDefaultEnabledMinimumImes(getSamplePreinstalledImes("en-rIN"), LOCALE_EN_IN, 142 "com.android.apps.inputmethod.hindi", 143 "com.android.apps.inputmethod.latin"); 144 145 // locale: hi 146 assertDefaultEnabledImes(getSamplePreinstalledImes("hi"), LOCALE_HI, 147 "com.android.apps.inputmethod.hindi", "com.android.apps.inputmethod.latin", 148 "com.android.apps.inputmethod.voice"); 149 assertDefaultEnabledMinimumImes(getSamplePreinstalledImes("hi"), LOCALE_HI, 150 "com.android.apps.inputmethod.hindi", "com.android.apps.inputmethod.latin"); 151 152 // locale: ja_JP 153 assertDefaultEnabledImes(getSamplePreinstalledImes("ja-rJP"), LOCALE_JA_JP, 154 "com.android.apps.inputmethod.japanese", "com.android.apps.inputmethod.voice"); 155 assertDefaultEnabledMinimumImes(getSamplePreinstalledImes("ja-rJP"), LOCALE_JA_JP, 156 "com.android.apps.inputmethod.japanese"); 157 158 // locale: zh_CN 159 assertDefaultEnabledImes(getSamplePreinstalledImes("zh-rCN"), LOCALE_ZH_CN, 160 "com.android.apps.inputmethod.pinyin", "com.android.apps.inputmethod.voice"); 161 assertDefaultEnabledMinimumImes(getSamplePreinstalledImes("zh-rCN"), LOCALE_ZH_CN, 162 "com.android.apps.inputmethod.pinyin"); 163 164 // locale: zh_TW 165 // Note: In this case, no IME is suitable for the system locale. Hence we will pick up a 166 // fallback IME regardless of the "default" attribute. 167 assertDefaultEnabledImes(getSamplePreinstalledImes("zh-rTW"), LOCALE_ZH_TW, 168 "com.android.apps.inputmethod.latin", "com.android.apps.inputmethod.voice"); 169 assertDefaultEnabledMinimumImes(getSamplePreinstalledImes("zh-rTW"), LOCALE_ZH_TW, 170 "com.android.apps.inputmethod.latin"); 171 } 172 173 @Test testParcelable()174 public void testParcelable() throws Exception { 175 final ArrayList<InputMethodInfo> originalList = getSamplePreinstalledImes("en-rUS"); 176 final List<InputMethodInfo> clonedList = cloneViaParcel(originalList); 177 assertNotNull(clonedList); 178 final List<InputMethodInfo> clonedClonedList = cloneViaParcel(clonedList); 179 assertNotNull(clonedClonedList); 180 assertEquals(originalList, clonedList); 181 assertEquals(clonedList, clonedClonedList); 182 assertEquals(originalList.size(), clonedList.size()); 183 assertEquals(clonedList.size(), clonedClonedList.size()); 184 for (int imeIndex = 0; imeIndex < originalList.size(); ++imeIndex) { 185 verifyEquality(originalList.get(imeIndex), clonedList.get(imeIndex)); 186 verifyEquality(clonedList.get(imeIndex), clonedClonedList.get(imeIndex)); 187 } 188 } 189 190 @Test testGetImplicitlyApplicableSubtypesLocked()191 public void testGetImplicitlyApplicableSubtypesLocked() throws Exception { 192 final InputMethodSubtype nonAutoEnUS = createDummyInputMethodSubtype("en_US", 193 SUBTYPE_MODE_KEYBOARD, !IS_AUX, !IS_OVERRIDES_IMPLICITLY_ENABLED_SUBTYPE, 194 IS_ASCII_CAPABLE, !IS_ENABLED_WHEN_DEFAULT_IS_NOT_ASCII_CAPABLE); 195 final InputMethodSubtype nonAutoEnGB = createDummyInputMethodSubtype("en_GB", 196 SUBTYPE_MODE_KEYBOARD, !IS_AUX, !IS_OVERRIDES_IMPLICITLY_ENABLED_SUBTYPE, 197 IS_ASCII_CAPABLE, IS_ENABLED_WHEN_DEFAULT_IS_NOT_ASCII_CAPABLE); 198 final InputMethodSubtype nonAutoEnIN = createDummyInputMethodSubtype("en_IN", 199 SUBTYPE_MODE_KEYBOARD, !IS_AUX, !IS_OVERRIDES_IMPLICITLY_ENABLED_SUBTYPE, 200 IS_ASCII_CAPABLE, IS_ENABLED_WHEN_DEFAULT_IS_NOT_ASCII_CAPABLE); 201 final InputMethodSubtype nonAutoFrCA = createDummyInputMethodSubtype("fr_CA", 202 SUBTYPE_MODE_KEYBOARD, !IS_AUX, !IS_OVERRIDES_IMPLICITLY_ENABLED_SUBTYPE, 203 IS_ASCII_CAPABLE, IS_ENABLED_WHEN_DEFAULT_IS_NOT_ASCII_CAPABLE); 204 final InputMethodSubtype nonAutoFr = createDummyInputMethodSubtype("fr_CA", 205 SUBTYPE_MODE_KEYBOARD, !IS_AUX, !IS_OVERRIDES_IMPLICITLY_ENABLED_SUBTYPE, 206 IS_ASCII_CAPABLE, IS_ENABLED_WHEN_DEFAULT_IS_NOT_ASCII_CAPABLE); 207 final InputMethodSubtype nonAutoFil = createDummyInputMethodSubtype("fil", 208 SUBTYPE_MODE_KEYBOARD, !IS_AUX, !IS_OVERRIDES_IMPLICITLY_ENABLED_SUBTYPE, 209 IS_ASCII_CAPABLE, !IS_ENABLED_WHEN_DEFAULT_IS_NOT_ASCII_CAPABLE); 210 final InputMethodSubtype nonAutoIn = createDummyInputMethodSubtype("in", 211 SUBTYPE_MODE_KEYBOARD, !IS_AUX, !IS_OVERRIDES_IMPLICITLY_ENABLED_SUBTYPE, 212 IS_ASCII_CAPABLE, IS_ENABLED_WHEN_DEFAULT_IS_NOT_ASCII_CAPABLE); 213 final InputMethodSubtype nonAutoId = createDummyInputMethodSubtype("id", 214 SUBTYPE_MODE_KEYBOARD, !IS_AUX, !IS_OVERRIDES_IMPLICITLY_ENABLED_SUBTYPE, 215 IS_ASCII_CAPABLE, IS_ENABLED_WHEN_DEFAULT_IS_NOT_ASCII_CAPABLE); 216 final InputMethodSubtype autoSubtype = createDummyInputMethodSubtype("auto", 217 SUBTYPE_MODE_KEYBOARD, !IS_AUX, IS_OVERRIDES_IMPLICITLY_ENABLED_SUBTYPE, 218 IS_ASCII_CAPABLE, !IS_ENABLED_WHEN_DEFAULT_IS_NOT_ASCII_CAPABLE); 219 final InputMethodSubtype nonAutoJa = createDummyInputMethodSubtype("ja", 220 SUBTYPE_MODE_KEYBOARD, !IS_AUX, !IS_OVERRIDES_IMPLICITLY_ENABLED_SUBTYPE, 221 !IS_ASCII_CAPABLE, !IS_ENABLED_WHEN_DEFAULT_IS_NOT_ASCII_CAPABLE); 222 final InputMethodSubtype nonAutoHi = createDummyInputMethodSubtype("hi", 223 SUBTYPE_MODE_KEYBOARD, !IS_AUX, !IS_OVERRIDES_IMPLICITLY_ENABLED_SUBTYPE, 224 !IS_ASCII_CAPABLE, !IS_ENABLED_WHEN_DEFAULT_IS_NOT_ASCII_CAPABLE); 225 final InputMethodSubtype nonAutoSrCyrl = createDummyInputMethodSubtype("sr", 226 "sr-Cyrl", SUBTYPE_MODE_KEYBOARD, !IS_AUX, 227 !IS_OVERRIDES_IMPLICITLY_ENABLED_SUBTYPE, !IS_ASCII_CAPABLE, 228 !IS_ENABLED_WHEN_DEFAULT_IS_NOT_ASCII_CAPABLE); 229 final InputMethodSubtype nonAutoSrLatn = createDummyInputMethodSubtype("sr_ZZ", 230 "sr-Latn", SUBTYPE_MODE_KEYBOARD, !IS_AUX, 231 !IS_OVERRIDES_IMPLICITLY_ENABLED_SUBTYPE, IS_ASCII_CAPABLE, 232 !IS_ENABLED_WHEN_DEFAULT_IS_NOT_ASCII_CAPABLE); 233 final InputMethodSubtype nonAutoHandwritingEn = createDummyInputMethodSubtype("en", 234 SUBTYPE_MODE_HANDWRITING, !IS_AUX, !IS_OVERRIDES_IMPLICITLY_ENABLED_SUBTYPE, 235 !IS_ASCII_CAPABLE, !IS_ENABLED_WHEN_DEFAULT_IS_NOT_ASCII_CAPABLE); 236 final InputMethodSubtype nonAutoHandwritingFr = createDummyInputMethodSubtype("fr", 237 SUBTYPE_MODE_HANDWRITING, !IS_AUX, !IS_OVERRIDES_IMPLICITLY_ENABLED_SUBTYPE, 238 !IS_ASCII_CAPABLE, !IS_ENABLED_WHEN_DEFAULT_IS_NOT_ASCII_CAPABLE); 239 final InputMethodSubtype nonAutoHandwritingSrCyrl = createDummyInputMethodSubtype("sr", 240 "sr-Cyrl", SUBTYPE_MODE_HANDWRITING, !IS_AUX, 241 !IS_OVERRIDES_IMPLICITLY_ENABLED_SUBTYPE, !IS_ASCII_CAPABLE, 242 !IS_ENABLED_WHEN_DEFAULT_IS_NOT_ASCII_CAPABLE); 243 final InputMethodSubtype nonAutoHandwritingSrLatn = createDummyInputMethodSubtype("sr_ZZ", 244 "sr-Latn", SUBTYPE_MODE_HANDWRITING, !IS_AUX, 245 !IS_OVERRIDES_IMPLICITLY_ENABLED_SUBTYPE, !IS_ASCII_CAPABLE, 246 !IS_ENABLED_WHEN_DEFAULT_IS_NOT_ASCII_CAPABLE); 247 final InputMethodSubtype nonAutoEnabledWhenDefaultIsNotAsciiCalableSubtype = 248 createDummyInputMethodSubtype("zz", SUBTYPE_MODE_KEYBOARD, !IS_AUX, 249 !IS_OVERRIDES_IMPLICITLY_ENABLED_SUBTYPE, !IS_ASCII_CAPABLE, 250 IS_ENABLED_WHEN_DEFAULT_IS_NOT_ASCII_CAPABLE); 251 final InputMethodSubtype nonAutoEnabledWhenDefaultIsNotAsciiCalableSubtype2 = 252 createDummyInputMethodSubtype("zz", SUBTYPE_MODE_KEYBOARD, !IS_AUX, 253 !IS_OVERRIDES_IMPLICITLY_ENABLED_SUBTYPE, !IS_ASCII_CAPABLE, 254 IS_ENABLED_WHEN_DEFAULT_IS_NOT_ASCII_CAPABLE); 255 256 // Make sure that an automatic subtype (overridesImplicitlyEnabledSubtype:true) is 257 // selected no matter what locale is specified. 258 { 259 final ArrayList<InputMethodSubtype> subtypes = new ArrayList<>(); 260 subtypes.add(nonAutoEnUS); 261 subtypes.add(nonAutoEnGB); 262 subtypes.add(nonAutoJa); 263 subtypes.add(nonAutoFil); 264 subtypes.add(autoSubtype); // overridesImplicitlyEnabledSubtype == true 265 subtypes.add(nonAutoEnabledWhenDefaultIsNotAsciiCalableSubtype); 266 subtypes.add(nonAutoEnabledWhenDefaultIsNotAsciiCalableSubtype2); 267 subtypes.add(nonAutoHandwritingEn); 268 subtypes.add(nonAutoHandwritingFr); 269 final InputMethodInfo imi = createDummyInputMethodInfo( 270 "com.android.apps.inputmethod.latin", 271 "com.android.apps.inputmethod.latin", "DummyLatinIme", !IS_AUX, IS_DEFAULT, 272 subtypes); 273 final ArrayList<InputMethodSubtype> result = 274 InputMethodUtils.getImplicitlyApplicableSubtypesLocked( 275 getResourcesForLocales(LOCALE_EN_US), imi); 276 assertEquals(1, result.size()); 277 verifyEquality(autoSubtype, result.get(0)); 278 } 279 280 // Make sure that a subtype whose locale is exactly equal to the specified locale is 281 // selected as long as there is no no automatic subtype 282 // (overridesImplicitlyEnabledSubtype:true) in the given list. 283 { 284 final ArrayList<InputMethodSubtype> subtypes = new ArrayList<>(); 285 subtypes.add(nonAutoEnUS); // locale == "en_US" 286 subtypes.add(nonAutoEnGB); 287 subtypes.add(nonAutoJa); 288 subtypes.add(nonAutoFil); 289 subtypes.add(nonAutoEnabledWhenDefaultIsNotAsciiCalableSubtype); 290 subtypes.add(nonAutoEnabledWhenDefaultIsNotAsciiCalableSubtype2); 291 subtypes.add(nonAutoHandwritingEn); 292 subtypes.add(nonAutoHandwritingFr); 293 final InputMethodInfo imi = createDummyInputMethodInfo( 294 "com.android.apps.inputmethod.latin", 295 "com.android.apps.inputmethod.latin", "DummyLatinIme", !IS_AUX, IS_DEFAULT, 296 subtypes); 297 final ArrayList<InputMethodSubtype> result = 298 InputMethodUtils.getImplicitlyApplicableSubtypesLocked( 299 getResourcesForLocales(LOCALE_EN_US), imi); 300 assertEquals(2, result.size()); 301 verifyEquality(nonAutoEnUS, result.get(0)); 302 verifyEquality(nonAutoHandwritingEn, result.get(1)); 303 } 304 305 // Make sure that a subtype whose locale is exactly equal to the specified locale is 306 // selected as long as there is no automatic subtype 307 // (overridesImplicitlyEnabledSubtype:true) in the given list. 308 { 309 final ArrayList<InputMethodSubtype> subtypes = new ArrayList<>(); 310 subtypes.add(nonAutoEnUS); 311 subtypes.add(nonAutoEnGB); // locale == "en_GB" 312 subtypes.add(nonAutoJa); 313 subtypes.add(nonAutoFil); 314 subtypes.add(nonAutoEnabledWhenDefaultIsNotAsciiCalableSubtype); 315 subtypes.add(nonAutoHandwritingEn); 316 subtypes.add(nonAutoHandwritingFr); 317 final InputMethodInfo imi = createDummyInputMethodInfo( 318 "com.android.apps.inputmethod.latin", 319 "com.android.apps.inputmethod.latin", "DummyLatinIme", !IS_AUX, IS_DEFAULT, 320 subtypes); 321 final ArrayList<InputMethodSubtype> result = 322 InputMethodUtils.getImplicitlyApplicableSubtypesLocked( 323 getResourcesForLocales(LOCALE_EN_GB), imi); 324 assertEquals(2, result.size()); 325 verifyEquality(nonAutoEnGB, result.get(0)); 326 verifyEquality(nonAutoHandwritingEn, result.get(1)); 327 } 328 329 // If there is no automatic subtype (overridesImplicitlyEnabledSubtype:true) and 330 // any subtype whose locale is exactly equal to the specified locale in the given list, 331 // try to find a subtype whose language is equal to the language part of the given locale. 332 // Here make sure that a subtype (locale: "fr_CA") can be found with locale: "fr". 333 { 334 final ArrayList<InputMethodSubtype> subtypes = new ArrayList<>(); 335 subtypes.add(nonAutoFrCA); // locale == "fr_CA" 336 subtypes.add(nonAutoJa); 337 subtypes.add(nonAutoFil); 338 subtypes.add(nonAutoEnabledWhenDefaultIsNotAsciiCalableSubtype); 339 subtypes.add(nonAutoEnabledWhenDefaultIsNotAsciiCalableSubtype2); 340 subtypes.add(nonAutoHandwritingEn); 341 subtypes.add(nonAutoHandwritingFr); 342 final InputMethodInfo imi = createDummyInputMethodInfo( 343 "com.android.apps.inputmethod.latin", 344 "com.android.apps.inputmethod.latin", "DummyLatinIme", !IS_AUX, IS_DEFAULT, 345 subtypes); 346 final ArrayList<InputMethodSubtype> result = 347 InputMethodUtils.getImplicitlyApplicableSubtypesLocked( 348 getResourcesForLocales(LOCALE_FR), imi); 349 assertEquals(2, result.size()); 350 verifyEquality(nonAutoFrCA, result.get(0)); 351 verifyEquality(nonAutoHandwritingFr, result.get(1)); 352 } 353 // Then make sure that a subtype (locale: "fr") can be found with locale: "fr_CA". 354 { 355 final ArrayList<InputMethodSubtype> subtypes = new ArrayList<>(); 356 subtypes.add(nonAutoFr); // locale == "fr" 357 subtypes.add(nonAutoJa); 358 subtypes.add(nonAutoFil); 359 subtypes.add(nonAutoEnabledWhenDefaultIsNotAsciiCalableSubtype); 360 subtypes.add(nonAutoEnabledWhenDefaultIsNotAsciiCalableSubtype2); 361 subtypes.add(nonAutoHandwritingEn); 362 subtypes.add(nonAutoHandwritingFr); 363 final InputMethodInfo imi = createDummyInputMethodInfo( 364 "com.android.apps.inputmethod.latin", 365 "com.android.apps.inputmethod.latin", "DummyLatinIme", !IS_AUX, IS_DEFAULT, 366 subtypes); 367 final ArrayList<InputMethodSubtype> result = 368 InputMethodUtils.getImplicitlyApplicableSubtypesLocked( 369 getResourcesForLocales(LOCALE_FR_CA), imi); 370 assertEquals(2, result.size()); 371 verifyEquality(nonAutoFrCA, result.get(0)); 372 verifyEquality(nonAutoHandwritingFr, result.get(1)); 373 } 374 375 // Make sure that subtypes which have "EnabledWhenDefaultIsNotAsciiCapable" in its 376 // extra value is selected if and only if all other selected IMEs are not AsciiCapable. 377 { 378 final ArrayList<InputMethodSubtype> subtypes = new ArrayList<>(); 379 subtypes.add(nonAutoEnUS); 380 subtypes.add(nonAutoJa); // not ASCII capable 381 subtypes.add(nonAutoEnabledWhenDefaultIsNotAsciiCalableSubtype); 382 subtypes.add(nonAutoEnabledWhenDefaultIsNotAsciiCalableSubtype2); 383 subtypes.add(nonAutoHandwritingEn); 384 subtypes.add(nonAutoHandwritingFr); 385 final InputMethodInfo imi = createDummyInputMethodInfo( 386 "com.android.apps.inputmethod.latin", 387 "com.android.apps.inputmethod.latin", "DummyLatinIme", !IS_AUX, IS_DEFAULT, 388 subtypes); 389 final ArrayList<InputMethodSubtype> result = 390 InputMethodUtils.getImplicitlyApplicableSubtypesLocked( 391 getResourcesForLocales(LOCALE_JA_JP), imi); 392 assertEquals(3, result.size()); 393 verifyEquality(nonAutoJa, result.get(0)); 394 verifyEquality(nonAutoEnabledWhenDefaultIsNotAsciiCalableSubtype, result.get(1)); 395 verifyEquality(nonAutoEnabledWhenDefaultIsNotAsciiCalableSubtype2, result.get(2)); 396 } 397 398 // Make sure that if there is no subtype that matches the language requested, then we just 399 // use the first keyboard subtype. 400 { 401 final ArrayList<InputMethodSubtype> subtypes = new ArrayList<>(); 402 subtypes.add(nonAutoHi); 403 subtypes.add(nonAutoEnUS); 404 subtypes.add(nonAutoHandwritingEn); 405 subtypes.add(nonAutoHandwritingFr); 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(nonAutoHi, result.get(0)); 416 } 417 { 418 final ArrayList<InputMethodSubtype> subtypes = new ArrayList<>(); 419 subtypes.add(nonAutoEnUS); 420 subtypes.add(nonAutoHi); 421 subtypes.add(nonAutoHandwritingEn); 422 subtypes.add(nonAutoHandwritingFr); 423 subtypes.add(nonAutoEnabledWhenDefaultIsNotAsciiCalableSubtype); 424 final InputMethodInfo imi = createDummyInputMethodInfo( 425 "com.android.apps.inputmethod.latin", 426 "com.android.apps.inputmethod.latin", "DummyLatinIme", !IS_AUX, IS_DEFAULT, 427 subtypes); 428 final ArrayList<InputMethodSubtype> result = 429 InputMethodUtils.getImplicitlyApplicableSubtypesLocked( 430 getResourcesForLocales(LOCALE_JA_JP), imi); 431 assertEquals(1, result.size()); 432 verifyEquality(nonAutoEnUS, result.get(0)); 433 } 434 { 435 final ArrayList<InputMethodSubtype> subtypes = new ArrayList<>(); 436 subtypes.add(nonAutoHandwritingEn); 437 subtypes.add(nonAutoHandwritingFr); 438 subtypes.add(nonAutoEnUS); 439 subtypes.add(nonAutoHi); 440 subtypes.add(nonAutoEnabledWhenDefaultIsNotAsciiCalableSubtype); 441 final InputMethodInfo imi = createDummyInputMethodInfo( 442 "com.android.apps.inputmethod.latin", 443 "com.android.apps.inputmethod.latin", "DummyLatinIme", !IS_AUX, IS_DEFAULT, 444 subtypes); 445 final ArrayList<InputMethodSubtype> result = 446 InputMethodUtils.getImplicitlyApplicableSubtypesLocked( 447 getResourcesForLocales(LOCALE_JA_JP), imi); 448 assertEquals(1, result.size()); 449 verifyEquality(nonAutoEnUS, result.get(0)); 450 } 451 452 // Make sure that both language and script are taken into account to find the best matching 453 // subtype. 454 { 455 final ArrayList<InputMethodSubtype> subtypes = new ArrayList<>(); 456 subtypes.add(nonAutoEnUS); 457 subtypes.add(nonAutoSrCyrl); 458 subtypes.add(nonAutoSrLatn); 459 subtypes.add(nonAutoHandwritingEn); 460 subtypes.add(nonAutoHandwritingFr); 461 subtypes.add(nonAutoHandwritingSrCyrl); 462 subtypes.add(nonAutoHandwritingSrLatn); 463 final InputMethodInfo imi = createDummyInputMethodInfo( 464 "com.android.apps.inputmethod.latin", 465 "com.android.apps.inputmethod.latin", "DummyLatinIme", !IS_AUX, IS_DEFAULT, 466 subtypes); 467 final ArrayList<InputMethodSubtype> result = 468 InputMethodUtils.getImplicitlyApplicableSubtypesLocked( 469 getResourcesForLocales(Locale.forLanguageTag("sr-Latn-RS")), imi); 470 assertEquals(2, result.size()); 471 assertThat(nonAutoSrLatn, is(in(result))); 472 assertThat(nonAutoHandwritingSrLatn, is(in(result))); 473 } 474 { 475 final ArrayList<InputMethodSubtype> subtypes = new ArrayList<>(); 476 subtypes.add(nonAutoEnUS); 477 subtypes.add(nonAutoSrCyrl); 478 subtypes.add(nonAutoSrLatn); 479 subtypes.add(nonAutoHandwritingEn); 480 subtypes.add(nonAutoHandwritingFr); 481 subtypes.add(nonAutoHandwritingSrCyrl); 482 subtypes.add(nonAutoHandwritingSrLatn); 483 final InputMethodInfo imi = createDummyInputMethodInfo( 484 "com.android.apps.inputmethod.latin", 485 "com.android.apps.inputmethod.latin", "DummyLatinIme", !IS_AUX, IS_DEFAULT, 486 subtypes); 487 final ArrayList<InputMethodSubtype> result = 488 InputMethodUtils.getImplicitlyApplicableSubtypesLocked( 489 getResourcesForLocales(Locale.forLanguageTag("sr-Cyrl-RS")), imi); 490 assertEquals(2, result.size()); 491 assertThat(nonAutoSrCyrl, is(in(result))); 492 assertThat(nonAutoHandwritingSrCyrl, is(in(result))); 493 } 494 495 // Make sure that secondary locales are taken into account to find the best matching 496 // subtype. 497 { 498 final ArrayList<InputMethodSubtype> subtypes = new ArrayList<>(); 499 subtypes.add(nonAutoEnUS); 500 subtypes.add(nonAutoEnGB); 501 subtypes.add(nonAutoSrCyrl); 502 subtypes.add(nonAutoSrLatn); 503 subtypes.add(nonAutoFr); 504 subtypes.add(nonAutoFrCA); 505 subtypes.add(nonAutoHandwritingEn); 506 subtypes.add(nonAutoHandwritingFr); 507 subtypes.add(nonAutoHandwritingSrCyrl); 508 subtypes.add(nonAutoHandwritingSrLatn); 509 final InputMethodInfo imi = createDummyInputMethodInfo( 510 "com.android.apps.inputmethod.latin", 511 "com.android.apps.inputmethod.latin", "DummyLatinIme", !IS_AUX, IS_DEFAULT, 512 subtypes); 513 final ArrayList<InputMethodSubtype> result = 514 InputMethodUtils.getImplicitlyApplicableSubtypesLocked( 515 getResourcesForLocales( 516 Locale.forLanguageTag("sr-Latn-RS-x-android"), 517 Locale.forLanguageTag("ja-JP"), 518 Locale.forLanguageTag("fr-FR"), 519 Locale.forLanguageTag("en-GB"), 520 Locale.forLanguageTag("en-US")), 521 imi); 522 assertEquals(6, result.size()); 523 assertThat(nonAutoEnGB, is(in(result))); 524 assertThat(nonAutoFr, is(in(result))); 525 assertThat(nonAutoSrLatn, is(in(result))); 526 assertThat(nonAutoHandwritingEn, is(in(result))); 527 assertThat(nonAutoHandwritingFr, is(in(result))); 528 assertThat(nonAutoHandwritingSrLatn, is(in(result))); 529 } 530 531 // Make sure that 3-letter language code can be handled. 532 { 533 final ArrayList<InputMethodSubtype> subtypes = new ArrayList<>(); 534 subtypes.add(nonAutoEnUS); 535 subtypes.add(nonAutoFil); 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_FIL_PH), imi); 543 assertEquals(1, result.size()); 544 verifyEquality(nonAutoFil, result.get(0)); 545 } 546 547 // Make sure that we never end up matching "fi" (finnish) with "fil" (filipino). 548 // Also make sure that the first subtype will be used as the last-resort candidate. 549 { 550 final ArrayList<InputMethodSubtype> subtypes = new ArrayList<>(); 551 subtypes.add(nonAutoJa); 552 subtypes.add(nonAutoEnUS); 553 subtypes.add(nonAutoFil); 554 final InputMethodInfo imi = createDummyInputMethodInfo( 555 "com.android.apps.inputmethod.latin", 556 "com.android.apps.inputmethod.latin", "DummyLatinIme", !IS_AUX, IS_DEFAULT, 557 subtypes); 558 final ArrayList<InputMethodSubtype> result = 559 InputMethodUtils.getImplicitlyApplicableSubtypesLocked( 560 getResourcesForLocales(LOCALE_FI), imi); 561 assertEquals(1, result.size()); 562 verifyEquality(nonAutoJa, result.get(0)); 563 } 564 565 // Make sure that "in" and "id" conversion is taken into account. 566 { 567 final ArrayList<InputMethodSubtype> subtypes = new ArrayList<>(); 568 subtypes.add(nonAutoIn); 569 subtypes.add(nonAutoEnUS); 570 final InputMethodInfo imi = createDummyInputMethodInfo( 571 "com.android.apps.inputmethod.latin", 572 "com.android.apps.inputmethod.latin", "DummyLatinIme", !IS_AUX, IS_DEFAULT, 573 subtypes); 574 final ArrayList<InputMethodSubtype> result = 575 InputMethodUtils.getImplicitlyApplicableSubtypesLocked( 576 getResourcesForLocales(LOCALE_IN), imi); 577 assertEquals(1, result.size()); 578 verifyEquality(nonAutoIn, result.get(0)); 579 } 580 { 581 final ArrayList<InputMethodSubtype> subtypes = new ArrayList<>(); 582 subtypes.add(nonAutoIn); 583 subtypes.add(nonAutoEnUS); 584 final InputMethodInfo imi = createDummyInputMethodInfo( 585 "com.android.apps.inputmethod.latin", 586 "com.android.apps.inputmethod.latin", "DummyLatinIme", !IS_AUX, IS_DEFAULT, 587 subtypes); 588 final ArrayList<InputMethodSubtype> result = 589 InputMethodUtils.getImplicitlyApplicableSubtypesLocked( 590 getResourcesForLocales(LOCALE_ID), imi); 591 assertEquals(1, result.size()); 592 verifyEquality(nonAutoIn, result.get(0)); 593 } 594 { 595 final ArrayList<InputMethodSubtype> subtypes = new ArrayList<>(); 596 subtypes.add(nonAutoId); 597 subtypes.add(nonAutoEnUS); 598 final InputMethodInfo imi = createDummyInputMethodInfo( 599 "com.android.apps.inputmethod.latin", 600 "com.android.apps.inputmethod.latin", "DummyLatinIme", !IS_AUX, IS_DEFAULT, 601 subtypes); 602 final ArrayList<InputMethodSubtype> result = 603 InputMethodUtils.getImplicitlyApplicableSubtypesLocked( 604 getResourcesForLocales(LOCALE_IN), imi); 605 assertEquals(1, result.size()); 606 verifyEquality(nonAutoId, result.get(0)); 607 } 608 { 609 final ArrayList<InputMethodSubtype> subtypes = new ArrayList<>(); 610 subtypes.add(nonAutoId); 611 subtypes.add(nonAutoEnUS); 612 final InputMethodInfo imi = createDummyInputMethodInfo( 613 "com.android.apps.inputmethod.latin", 614 "com.android.apps.inputmethod.latin", "DummyLatinIme", !IS_AUX, IS_DEFAULT, 615 subtypes); 616 final ArrayList<InputMethodSubtype> result = 617 InputMethodUtils.getImplicitlyApplicableSubtypesLocked( 618 getResourcesForLocales(LOCALE_ID), imi); 619 assertEquals(1, result.size()); 620 verifyEquality(nonAutoId, result.get(0)); 621 } 622 623 // If there is no automatic subtype (overridesImplicitlyEnabledSubtype:true) and the system 624 // provides multiple locales, we try to enable multiple subtypes. 625 { 626 final ArrayList<InputMethodSubtype> subtypes = new ArrayList<>(); 627 subtypes.add(nonAutoEnUS); 628 subtypes.add(nonAutoFrCA); 629 subtypes.add(nonAutoIn); 630 subtypes.add(nonAutoJa); 631 subtypes.add(nonAutoFil); 632 subtypes.add(nonAutoEnabledWhenDefaultIsNotAsciiCalableSubtype); 633 subtypes.add(nonAutoEnabledWhenDefaultIsNotAsciiCalableSubtype2); 634 final InputMethodInfo imi = createDummyInputMethodInfo( 635 "com.android.apps.inputmethod.latin", 636 "com.android.apps.inputmethod.latin", "DummyLatinIme", !IS_AUX, IS_DEFAULT, 637 subtypes); 638 final ArrayList<InputMethodSubtype> result = 639 InputMethodUtils.getImplicitlyApplicableSubtypesLocked( 640 getResourcesForLocales(LOCALE_FR, LOCALE_EN_US, LOCALE_JA_JP), imi); 641 assertThat(nonAutoFrCA, is(in(result))); 642 assertThat(nonAutoEnUS, is(in(result))); 643 assertThat(nonAutoJa, is(in(result))); 644 assertThat(nonAutoIn, not(is(in(result)))); 645 assertThat(nonAutoEnabledWhenDefaultIsNotAsciiCalableSubtype, not(is(in(result)))); 646 assertThat(nonAutoEnabledWhenDefaultIsNotAsciiCalableSubtype, not(is(in(result)))); 647 } 648 } 649 650 @Test testContainsSubtypeOf()651 public void testContainsSubtypeOf() throws Exception { 652 final InputMethodSubtype nonAutoEnUS = createDummyInputMethodSubtype("en_US", 653 SUBTYPE_MODE_KEYBOARD, !IS_AUX, !IS_OVERRIDES_IMPLICITLY_ENABLED_SUBTYPE, 654 IS_ASCII_CAPABLE, !IS_ENABLED_WHEN_DEFAULT_IS_NOT_ASCII_CAPABLE); 655 final InputMethodSubtype nonAutoEnGB = createDummyInputMethodSubtype("en_GB", 656 SUBTYPE_MODE_KEYBOARD, !IS_AUX, !IS_OVERRIDES_IMPLICITLY_ENABLED_SUBTYPE, 657 IS_ASCII_CAPABLE, IS_ENABLED_WHEN_DEFAULT_IS_NOT_ASCII_CAPABLE); 658 final InputMethodSubtype nonAutoFil = createDummyInputMethodSubtype("fil", 659 SUBTYPE_MODE_KEYBOARD, !IS_AUX, !IS_OVERRIDES_IMPLICITLY_ENABLED_SUBTYPE, 660 IS_ASCII_CAPABLE, !IS_ENABLED_WHEN_DEFAULT_IS_NOT_ASCII_CAPABLE); 661 final InputMethodSubtype nonAutoFilPH = createDummyInputMethodSubtype("fil_PH", 662 SUBTYPE_MODE_KEYBOARD, !IS_AUX, !IS_OVERRIDES_IMPLICITLY_ENABLED_SUBTYPE, 663 IS_ASCII_CAPABLE, !IS_ENABLED_WHEN_DEFAULT_IS_NOT_ASCII_CAPABLE); 664 final InputMethodSubtype nonAutoIn = createDummyInputMethodSubtype("in", 665 SUBTYPE_MODE_KEYBOARD, !IS_AUX, !IS_OVERRIDES_IMPLICITLY_ENABLED_SUBTYPE, 666 IS_ASCII_CAPABLE, IS_ENABLED_WHEN_DEFAULT_IS_NOT_ASCII_CAPABLE); 667 final InputMethodSubtype nonAutoId = createDummyInputMethodSubtype("id", 668 SUBTYPE_MODE_KEYBOARD, !IS_AUX, !IS_OVERRIDES_IMPLICITLY_ENABLED_SUBTYPE, 669 IS_ASCII_CAPABLE, IS_ENABLED_WHEN_DEFAULT_IS_NOT_ASCII_CAPABLE); 670 671 final boolean CHECK_COUNTRY = true; 672 673 { 674 final ArrayList<InputMethodSubtype> subtypes = new ArrayList<>(); 675 subtypes.add(nonAutoEnUS); 676 final InputMethodInfo imi = createDummyInputMethodInfo( 677 "com.android.apps.inputmethod.latin", 678 "com.android.apps.inputmethod.latin", "DummyLatinIme", !IS_AUX, IS_DEFAULT, 679 subtypes); 680 681 assertTrue(InputMethodUtils.containsSubtypeOf(imi, LOCALE_EN, !CHECK_COUNTRY, 682 SUBTYPE_MODE_KEYBOARD)); 683 assertFalse(InputMethodUtils.containsSubtypeOf(imi, LOCALE_EN, CHECK_COUNTRY, 684 SUBTYPE_MODE_KEYBOARD)); 685 assertTrue(InputMethodUtils.containsSubtypeOf(imi, LOCALE_EN_US, !CHECK_COUNTRY, 686 SUBTYPE_MODE_KEYBOARD)); 687 assertTrue(InputMethodUtils.containsSubtypeOf(imi, LOCALE_EN_US, CHECK_COUNTRY, 688 SUBTYPE_MODE_KEYBOARD)); 689 assertFalse(InputMethodUtils.containsSubtypeOf(imi, LOCALE_EN_US, !CHECK_COUNTRY, 690 SUBTYPE_MODE_VOICE)); 691 assertFalse(InputMethodUtils.containsSubtypeOf(imi, LOCALE_EN_US, CHECK_COUNTRY, 692 SUBTYPE_MODE_VOICE)); 693 assertTrue(InputMethodUtils.containsSubtypeOf(imi, LOCALE_EN_US, !CHECK_COUNTRY, 694 SUBTYPE_MODE_ANY)); 695 assertTrue(InputMethodUtils.containsSubtypeOf(imi, LOCALE_EN_US, CHECK_COUNTRY, 696 SUBTYPE_MODE_ANY)); 697 698 assertTrue(InputMethodUtils.containsSubtypeOf(imi, LOCALE_EN_GB, !CHECK_COUNTRY, 699 SUBTYPE_MODE_KEYBOARD)); 700 assertFalse(InputMethodUtils.containsSubtypeOf(imi, LOCALE_EN_GB, CHECK_COUNTRY, 701 SUBTYPE_MODE_KEYBOARD)); 702 } 703 704 // Make sure that 3-letter language code ("fil") can be handled. 705 { 706 final ArrayList<InputMethodSubtype> subtypes = new ArrayList<>(); 707 subtypes.add(nonAutoFil); 708 final InputMethodInfo imi = createDummyInputMethodInfo( 709 "com.android.apps.inputmethod.latin", 710 "com.android.apps.inputmethod.latin", "DummyLatinIme", !IS_AUX, IS_DEFAULT, 711 subtypes); 712 assertTrue(InputMethodUtils.containsSubtypeOf(imi, LOCALE_FIL, !CHECK_COUNTRY, 713 SUBTYPE_MODE_KEYBOARD)); 714 assertTrue(InputMethodUtils.containsSubtypeOf(imi, LOCALE_FIL, CHECK_COUNTRY, 715 SUBTYPE_MODE_KEYBOARD)); 716 assertTrue(InputMethodUtils.containsSubtypeOf(imi, LOCALE_FIL_PH, !CHECK_COUNTRY, 717 SUBTYPE_MODE_KEYBOARD)); 718 assertFalse(InputMethodUtils.containsSubtypeOf(imi, LOCALE_FIL_PH, CHECK_COUNTRY, 719 SUBTYPE_MODE_KEYBOARD)); 720 721 assertFalse(InputMethodUtils.containsSubtypeOf(imi, LOCALE_FI, !CHECK_COUNTRY, 722 SUBTYPE_MODE_KEYBOARD)); 723 assertFalse(InputMethodUtils.containsSubtypeOf(imi, LOCALE_FI, CHECK_COUNTRY, 724 SUBTYPE_MODE_KEYBOARD)); 725 assertFalse(InputMethodUtils.containsSubtypeOf(imi, LOCALE_FI_FI, !CHECK_COUNTRY, 726 SUBTYPE_MODE_KEYBOARD)); 727 assertFalse(InputMethodUtils.containsSubtypeOf(imi, LOCALE_FI_FI, CHECK_COUNTRY, 728 SUBTYPE_MODE_KEYBOARD)); 729 } 730 731 // Make sure that 3-letter language code ("fil_PH") can be handled. 732 { 733 final ArrayList<InputMethodSubtype> subtypes = new ArrayList<>(); 734 subtypes.add(nonAutoFilPH); 735 final InputMethodInfo imi = createDummyInputMethodInfo( 736 "com.android.apps.inputmethod.latin", 737 "com.android.apps.inputmethod.latin", "DummyLatinIme", !IS_AUX, IS_DEFAULT, 738 subtypes); 739 assertTrue(InputMethodUtils.containsSubtypeOf(imi, LOCALE_FIL, !CHECK_COUNTRY, 740 SUBTYPE_MODE_KEYBOARD)); 741 assertFalse(InputMethodUtils.containsSubtypeOf(imi, LOCALE_FIL, CHECK_COUNTRY, 742 SUBTYPE_MODE_KEYBOARD)); 743 assertTrue(InputMethodUtils.containsSubtypeOf(imi, LOCALE_FIL_PH, !CHECK_COUNTRY, 744 SUBTYPE_MODE_KEYBOARD)); 745 assertTrue(InputMethodUtils.containsSubtypeOf(imi, LOCALE_FIL_PH, CHECK_COUNTRY, 746 SUBTYPE_MODE_KEYBOARD)); 747 748 assertFalse(InputMethodUtils.containsSubtypeOf(imi, LOCALE_FI, !CHECK_COUNTRY, 749 SUBTYPE_MODE_KEYBOARD)); 750 assertFalse(InputMethodUtils.containsSubtypeOf(imi, LOCALE_FI, CHECK_COUNTRY, 751 SUBTYPE_MODE_KEYBOARD)); 752 assertFalse(InputMethodUtils.containsSubtypeOf(imi, LOCALE_FI_FI, !CHECK_COUNTRY, 753 SUBTYPE_MODE_KEYBOARD)); 754 assertFalse(InputMethodUtils.containsSubtypeOf(imi, LOCALE_FI_FI, CHECK_COUNTRY, 755 SUBTYPE_MODE_KEYBOARD)); 756 } 757 758 // Make sure that a subtype whose locale is "in" can be queried with "id". 759 { 760 final ArrayList<InputMethodSubtype> subtypes = new ArrayList<>(); 761 subtypes.add(nonAutoIn); 762 subtypes.add(nonAutoEnUS); 763 final InputMethodInfo imi = createDummyInputMethodInfo( 764 "com.android.apps.inputmethod.latin", 765 "com.android.apps.inputmethod.latin", "DummyLatinIme", !IS_AUX, IS_DEFAULT, 766 subtypes); 767 assertTrue(InputMethodUtils.containsSubtypeOf(imi, LOCALE_IN, !CHECK_COUNTRY, 768 SUBTYPE_MODE_KEYBOARD)); 769 assertTrue(InputMethodUtils.containsSubtypeOf(imi, LOCALE_IN, CHECK_COUNTRY, 770 SUBTYPE_MODE_KEYBOARD)); 771 assertTrue(InputMethodUtils.containsSubtypeOf(imi, LOCALE_ID, !CHECK_COUNTRY, 772 SUBTYPE_MODE_KEYBOARD)); 773 assertTrue(InputMethodUtils.containsSubtypeOf(imi, LOCALE_ID, CHECK_COUNTRY, 774 SUBTYPE_MODE_KEYBOARD)); 775 } 776 777 // Make sure that a subtype whose locale is "id" can be queried with "in". 778 { 779 final ArrayList<InputMethodSubtype> subtypes = new ArrayList<>(); 780 subtypes.add(nonAutoId); 781 subtypes.add(nonAutoEnUS); 782 final InputMethodInfo imi = createDummyInputMethodInfo( 783 "com.android.apps.inputmethod.latin", 784 "com.android.apps.inputmethod.latin", "DummyLatinIme", !IS_AUX, IS_DEFAULT, 785 subtypes); 786 assertTrue(InputMethodUtils.containsSubtypeOf(imi, LOCALE_IN, !CHECK_COUNTRY, 787 SUBTYPE_MODE_KEYBOARD)); 788 assertTrue(InputMethodUtils.containsSubtypeOf(imi, LOCALE_IN, CHECK_COUNTRY, 789 SUBTYPE_MODE_KEYBOARD)); 790 assertTrue(InputMethodUtils.containsSubtypeOf(imi, LOCALE_ID, !CHECK_COUNTRY, 791 SUBTYPE_MODE_KEYBOARD)); 792 assertTrue(InputMethodUtils.containsSubtypeOf(imi, LOCALE_ID, CHECK_COUNTRY, 793 SUBTYPE_MODE_KEYBOARD)); 794 } 795 } 796 assertDefaultEnabledImes(final ArrayList<InputMethodInfo> preinstalledImes, final Locale systemLocale, String... expectedImeNames)797 private void assertDefaultEnabledImes(final ArrayList<InputMethodInfo> preinstalledImes, 798 final Locale systemLocale, String... expectedImeNames) { 799 final Context context = createTargetContextWithLocales(new LocaleList(systemLocale)); 800 final String[] actualImeNames = getPackageNames( 801 InputMethodUtils.getDefaultEnabledImes(context, preinstalledImes)); 802 assertEquals(expectedImeNames.length, actualImeNames.length); 803 for (int i = 0; i < expectedImeNames.length; ++i) { 804 assertEquals(expectedImeNames[i], actualImeNames[i]); 805 } 806 } 807 assertDefaultEnabledMinimumImes(final ArrayList<InputMethodInfo> preinstalledImes, final Locale systemLocale, String... expectedImeNames)808 private void assertDefaultEnabledMinimumImes(final ArrayList<InputMethodInfo> preinstalledImes, 809 final Locale systemLocale, String... expectedImeNames) { 810 final Context context = createTargetContextWithLocales(new LocaleList(systemLocale)); 811 final String[] actualImeNames = getPackageNames( 812 InputMethodUtils.getDefaultEnabledImes(context, preinstalledImes, 813 true /* onlyMinimum */)); 814 assertEquals(expectedImeNames.length, actualImeNames.length); 815 for (int i = 0; i < expectedImeNames.length; ++i) { 816 assertEquals(expectedImeNames[i], actualImeNames[i]); 817 } 818 } 819 cloneViaParcel(final List<InputMethodInfo> list)820 private static List<InputMethodInfo> cloneViaParcel(final List<InputMethodInfo> list) { 821 Parcel p = null; 822 try { 823 p = Parcel.obtain(); 824 p.writeTypedList(list); 825 p.setDataPosition(0); 826 return p.createTypedArrayList(InputMethodInfo.CREATOR); 827 } finally { 828 if (p != null) { 829 p.recycle(); 830 } 831 } 832 } 833 createTargetContextWithLocales(final LocaleList locales)834 private Context createTargetContextWithLocales(final LocaleList locales) { 835 final Configuration resourceConfiguration = new Configuration(); 836 resourceConfiguration.setLocales(locales); 837 return InstrumentationRegistry.getInstrumentation() 838 .getTargetContext() 839 .createConfigurationContext(resourceConfiguration); 840 } 841 getResourcesForLocales(Locale... locales)842 private Resources getResourcesForLocales(Locale... locales) { 843 return createTargetContextWithLocales(new LocaleList(locales)).getResources(); 844 } 845 getPackageNames(final ArrayList<InputMethodInfo> imis)846 private String[] getPackageNames(final ArrayList<InputMethodInfo> imis) { 847 final String[] packageNames = new String[imis.size()]; 848 for (int i = 0; i < imis.size(); ++i) { 849 packageNames[i] = imis.get(i).getPackageName(); 850 } 851 return packageNames; 852 } 853 verifyEquality(InputMethodInfo expected, InputMethodInfo actual)854 private static void verifyEquality(InputMethodInfo expected, InputMethodInfo actual) { 855 assertEquals(expected, actual); 856 assertEquals(expected.getSubtypeCount(), actual.getSubtypeCount()); 857 for (int subtypeIndex = 0; subtypeIndex < expected.getSubtypeCount(); ++subtypeIndex) { 858 final InputMethodSubtype expectedSubtype = expected.getSubtypeAt(subtypeIndex); 859 final InputMethodSubtype actualSubtype = actual.getSubtypeAt(subtypeIndex); 860 verifyEquality(expectedSubtype, actualSubtype); 861 } 862 } 863 verifyEquality(InputMethodSubtype expected, InputMethodSubtype actual)864 private static void verifyEquality(InputMethodSubtype expected, InputMethodSubtype actual) { 865 assertEquals(expected, actual); 866 assertEquals(expected.hashCode(), actual.hashCode()); 867 } 868 createDummyInputMethodInfo(String packageName, String name, CharSequence label, boolean isAuxIme, boolean isDefault, List<InputMethodSubtype> subtypes)869 private static InputMethodInfo createDummyInputMethodInfo(String packageName, String name, 870 CharSequence label, boolean isAuxIme, boolean isDefault, 871 List<InputMethodSubtype> subtypes) { 872 final ResolveInfo ri = new ResolveInfo(); 873 final ServiceInfo si = new ServiceInfo(); 874 final ApplicationInfo ai = new ApplicationInfo(); 875 ai.packageName = packageName; 876 ai.enabled = true; 877 ai.flags |= ApplicationInfo.FLAG_SYSTEM; 878 si.applicationInfo = ai; 879 si.enabled = true; 880 si.packageName = packageName; 881 si.name = name; 882 si.exported = true; 883 si.nonLocalizedLabel = label; 884 ri.serviceInfo = si; 885 return new InputMethodInfo(ri, isAuxIme, "", subtypes, 1, isDefault); 886 } 887 createDummyInputMethodSubtype(String locale, String mode, boolean isAuxiliary, boolean overridesImplicitlyEnabledSubtype, boolean isAsciiCapable, boolean isEnabledWhenDefaultIsNotAsciiCapable)888 private static InputMethodSubtype createDummyInputMethodSubtype(String locale, String mode, 889 boolean isAuxiliary, boolean overridesImplicitlyEnabledSubtype, 890 boolean isAsciiCapable, boolean isEnabledWhenDefaultIsNotAsciiCapable) { 891 return createDummyInputMethodSubtype(locale, null /* languageTag */, mode, isAuxiliary, 892 overridesImplicitlyEnabledSubtype, isAsciiCapable, 893 isEnabledWhenDefaultIsNotAsciiCapable); 894 } 895 createDummyInputMethodSubtype(String locale, String languageTag, String mode, boolean isAuxiliary, boolean overridesImplicitlyEnabledSubtype, boolean isAsciiCapable, boolean isEnabledWhenDefaultIsNotAsciiCapable)896 private static InputMethodSubtype createDummyInputMethodSubtype(String locale, 897 String languageTag, String mode, boolean isAuxiliary, 898 boolean overridesImplicitlyEnabledSubtype, boolean isAsciiCapable, 899 boolean isEnabledWhenDefaultIsNotAsciiCapable) { 900 final StringBuilder subtypeExtraValue = new StringBuilder(); 901 if (isEnabledWhenDefaultIsNotAsciiCapable) { 902 subtypeExtraValue.append(EXTRA_VALUE_PAIR_SEPARATOR); 903 subtypeExtraValue.append(EXTRA_VALUE_ENABLED_WHEN_DEFAULT_IS_NOT_ASCII_CAPABLE); 904 } 905 906 return new InputMethodSubtypeBuilder() 907 .setSubtypeNameResId(0) 908 .setSubtypeIconResId(0) 909 .setSubtypeLocale(locale) 910 .setLanguageTag(languageTag) 911 .setSubtypeMode(mode) 912 .setSubtypeExtraValue(subtypeExtraValue.toString()) 913 .setIsAuxiliary(isAuxiliary) 914 .setOverridesImplicitlyEnabledSubtype(overridesImplicitlyEnabledSubtype) 915 .setIsAsciiCapable(isAsciiCapable) 916 .build(); 917 } 918 getImesWithDefaultVoiceIme()919 private static ArrayList<InputMethodInfo> getImesWithDefaultVoiceIme() { 920 ArrayList<InputMethodInfo> preinstalledImes = new ArrayList<>(); 921 { 922 final ArrayList<InputMethodSubtype> subtypes = new ArrayList<>(); 923 subtypes.add(createDummyInputMethodSubtype("auto", SUBTYPE_MODE_VOICE, IS_AUX, 924 IS_OVERRIDES_IMPLICITLY_ENABLED_SUBTYPE, !IS_ASCII_CAPABLE, 925 !IS_ENABLED_WHEN_DEFAULT_IS_NOT_ASCII_CAPABLE)); 926 subtypes.add(createDummyInputMethodSubtype("en_US", SUBTYPE_MODE_VOICE, IS_AUX, 927 !IS_OVERRIDES_IMPLICITLY_ENABLED_SUBTYPE, !IS_ASCII_CAPABLE, 928 !IS_ENABLED_WHEN_DEFAULT_IS_NOT_ASCII_CAPABLE)); 929 preinstalledImes.add(createDummyInputMethodInfo("DummyDefaultAutoVoiceIme", 930 "dummy.voice0", "DummyVoice0", IS_AUX, IS_DEFAULT, subtypes)); 931 } 932 preinstalledImes.addAll(getImesWithoutDefaultVoiceIme()); 933 return preinstalledImes; 934 } 935 getImesWithoutDefaultVoiceIme()936 private static ArrayList<InputMethodInfo> getImesWithoutDefaultVoiceIme() { 937 ArrayList<InputMethodInfo> preinstalledImes = new ArrayList<>(); 938 { 939 final ArrayList<InputMethodSubtype> subtypes = new ArrayList<>(); 940 subtypes.add(createDummyInputMethodSubtype("auto", SUBTYPE_MODE_VOICE, IS_AUX, 941 IS_OVERRIDES_IMPLICITLY_ENABLED_SUBTYPE, !IS_ASCII_CAPABLE, 942 !IS_ENABLED_WHEN_DEFAULT_IS_NOT_ASCII_CAPABLE)); 943 subtypes.add(createDummyInputMethodSubtype("en_US", SUBTYPE_MODE_VOICE, IS_AUX, 944 !IS_OVERRIDES_IMPLICITLY_ENABLED_SUBTYPE, !IS_ASCII_CAPABLE, 945 !IS_ENABLED_WHEN_DEFAULT_IS_NOT_ASCII_CAPABLE)); 946 preinstalledImes.add(createDummyInputMethodInfo("DummyNonDefaultAutoVoiceIme0", 947 "dummy.voice1", "DummyVoice1", IS_AUX, !IS_DEFAULT, subtypes)); 948 } 949 { 950 final ArrayList<InputMethodSubtype> subtypes = new ArrayList<>(); 951 subtypes.add(createDummyInputMethodSubtype("auto", SUBTYPE_MODE_VOICE, IS_AUX, 952 IS_OVERRIDES_IMPLICITLY_ENABLED_SUBTYPE, !IS_ASCII_CAPABLE, 953 !IS_ENABLED_WHEN_DEFAULT_IS_NOT_ASCII_CAPABLE)); 954 subtypes.add(createDummyInputMethodSubtype("en_US", SUBTYPE_MODE_VOICE, IS_AUX, 955 !IS_OVERRIDES_IMPLICITLY_ENABLED_SUBTYPE, !IS_ASCII_CAPABLE, 956 !IS_ENABLED_WHEN_DEFAULT_IS_NOT_ASCII_CAPABLE)); 957 preinstalledImes.add(createDummyInputMethodInfo("DummyNonDefaultAutoVoiceIme1", 958 "dummy.voice2", "DummyVoice2", IS_AUX, !IS_DEFAULT, subtypes)); 959 } 960 { 961 final ArrayList<InputMethodSubtype> subtypes = new ArrayList<>(); 962 subtypes.add(createDummyInputMethodSubtype("en_US", SUBTYPE_MODE_VOICE, IS_AUX, 963 !IS_OVERRIDES_IMPLICITLY_ENABLED_SUBTYPE, !IS_ASCII_CAPABLE, 964 !IS_ENABLED_WHEN_DEFAULT_IS_NOT_ASCII_CAPABLE)); 965 preinstalledImes.add(createDummyInputMethodInfo("DummyNonDefaultVoiceIme2", 966 "dummy.voice3", "DummyVoice3", IS_AUX, !IS_DEFAULT, subtypes)); 967 } 968 { 969 final ArrayList<InputMethodSubtype> subtypes = new ArrayList<>(); 970 subtypes.add(createDummyInputMethodSubtype("en_US", SUBTYPE_MODE_KEYBOARD, !IS_AUX, 971 !IS_OVERRIDES_IMPLICITLY_ENABLED_SUBTYPE, IS_ASCII_CAPABLE, 972 !IS_ENABLED_WHEN_DEFAULT_IS_NOT_ASCII_CAPABLE)); 973 preinstalledImes.add(createDummyInputMethodInfo("DummyDefaultEnKeyboardIme", 974 "dummy.keyboard0", "DummyKeyboard0", !IS_AUX, IS_DEFAULT, subtypes)); 975 } 976 return preinstalledImes; 977 } 978 contains(final String[] textList, final String textToBeChecked)979 private static boolean contains(final String[] textList, final String textToBeChecked) { 980 if (textList == null) { 981 return false; 982 } 983 for (final String text : textList) { 984 if (Objects.equals(textToBeChecked, text)) { 985 return true; 986 } 987 } 988 return false; 989 } 990 getSamplePreinstalledImes(final String localeString)991 private static ArrayList<InputMethodInfo> getSamplePreinstalledImes(final String localeString) { 992 ArrayList<InputMethodInfo> preinstalledImes = new ArrayList<>(); 993 994 // a dummy Voice IME 995 { 996 final boolean isDefaultIme = false; 997 final ArrayList<InputMethodSubtype> subtypes = new ArrayList<>(); 998 subtypes.add(createDummyInputMethodSubtype("", SUBTYPE_MODE_VOICE, 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.voice", 1002 "com.android.inputmethod.voice", "DummyVoiceIme", IS_AUX, isDefaultIme, 1003 subtypes)); 1004 } 1005 // a dummy Hindi IME 1006 { 1007 final boolean isDefaultIme = contains(new String[]{ "hi", "en-rIN" }, localeString); 1008 final ArrayList<InputMethodSubtype> subtypes = new ArrayList<>(); 1009 // TODO: This subtype should be marked as IS_ASCII_CAPABLE 1010 subtypes.add(createDummyInputMethodSubtype("en_IN", SUBTYPE_MODE_KEYBOARD, !IS_AUX, 1011 !IS_OVERRIDES_IMPLICITLY_ENABLED_SUBTYPE, !IS_ASCII_CAPABLE, 1012 !IS_ENABLED_WHEN_DEFAULT_IS_NOT_ASCII_CAPABLE)); 1013 subtypes.add(createDummyInputMethodSubtype("hi", SUBTYPE_MODE_KEYBOARD, !IS_AUX, 1014 !IS_OVERRIDES_IMPLICITLY_ENABLED_SUBTYPE, !IS_ASCII_CAPABLE, 1015 !IS_ENABLED_WHEN_DEFAULT_IS_NOT_ASCII_CAPABLE)); 1016 preinstalledImes.add(createDummyInputMethodInfo("com.android.apps.inputmethod.hindi", 1017 "com.android.inputmethod.hindi", "DummyHindiIme", !IS_AUX, isDefaultIme, 1018 subtypes)); 1019 } 1020 1021 // a dummy Pinyin IME 1022 { 1023 final boolean isDefaultIme = contains(new String[]{ "zh-rCN" }, localeString); 1024 final ArrayList<InputMethodSubtype> subtypes = new ArrayList<>(); 1025 subtypes.add(createDummyInputMethodSubtype("zh_CN", SUBTYPE_MODE_KEYBOARD, !IS_AUX, 1026 !IS_OVERRIDES_IMPLICITLY_ENABLED_SUBTYPE, !IS_ASCII_CAPABLE, 1027 !IS_ENABLED_WHEN_DEFAULT_IS_NOT_ASCII_CAPABLE)); 1028 preinstalledImes.add(createDummyInputMethodInfo("com.android.apps.inputmethod.pinyin", 1029 "com.android.apps.inputmethod.pinyin", "DummyPinyinIme", !IS_AUX, isDefaultIme, 1030 subtypes)); 1031 } 1032 1033 // a dummy Korean IME 1034 { 1035 final boolean isDefaultIme = contains(new String[]{ "ko" }, localeString); 1036 final ArrayList<InputMethodSubtype> subtypes = new ArrayList<>(); 1037 subtypes.add(createDummyInputMethodSubtype("ko", SUBTYPE_MODE_KEYBOARD, !IS_AUX, 1038 !IS_OVERRIDES_IMPLICITLY_ENABLED_SUBTYPE, !IS_ASCII_CAPABLE, 1039 !IS_ENABLED_WHEN_DEFAULT_IS_NOT_ASCII_CAPABLE)); 1040 preinstalledImes.add(createDummyInputMethodInfo("com.android.apps.inputmethod.korean", 1041 "com.android.apps.inputmethod.korean", "DummyKoreanIme", !IS_AUX, isDefaultIme, 1042 subtypes)); 1043 } 1044 1045 // a dummy Latin IME 1046 { 1047 final boolean isDefaultIme = contains( 1048 new String[]{ "en-rUS", "en-rGB", "en-rIN", "en", "hi" }, localeString); 1049 final ArrayList<InputMethodSubtype> subtypes = new ArrayList<>(); 1050 subtypes.add(createDummyInputMethodSubtype("en_US", SUBTYPE_MODE_KEYBOARD, !IS_AUX, 1051 !IS_OVERRIDES_IMPLICITLY_ENABLED_SUBTYPE, IS_ASCII_CAPABLE, 1052 !IS_ENABLED_WHEN_DEFAULT_IS_NOT_ASCII_CAPABLE)); 1053 subtypes.add(createDummyInputMethodSubtype("en_GB", SUBTYPE_MODE_KEYBOARD, !IS_AUX, 1054 !IS_OVERRIDES_IMPLICITLY_ENABLED_SUBTYPE, IS_ASCII_CAPABLE, 1055 !IS_ENABLED_WHEN_DEFAULT_IS_NOT_ASCII_CAPABLE)); 1056 subtypes.add(createDummyInputMethodSubtype("en_IN", SUBTYPE_MODE_KEYBOARD, !IS_AUX, 1057 !IS_OVERRIDES_IMPLICITLY_ENABLED_SUBTYPE, IS_ASCII_CAPABLE, 1058 !IS_ENABLED_WHEN_DEFAULT_IS_NOT_ASCII_CAPABLE)); 1059 subtypes.add(createDummyInputMethodSubtype("hi", SUBTYPE_MODE_KEYBOARD, !IS_AUX, 1060 !IS_OVERRIDES_IMPLICITLY_ENABLED_SUBTYPE, IS_ASCII_CAPABLE, 1061 !IS_ENABLED_WHEN_DEFAULT_IS_NOT_ASCII_CAPABLE)); 1062 preinstalledImes.add(createDummyInputMethodInfo("com.android.apps.inputmethod.latin", 1063 "com.android.apps.inputmethod.latin", "DummyLatinIme", !IS_AUX, isDefaultIme, 1064 subtypes)); 1065 } 1066 1067 // a dummy Japanese IME 1068 { 1069 final boolean isDefaultIme = contains(new String[]{ "ja", "ja-rJP" }, localeString); 1070 final ArrayList<InputMethodSubtype> subtypes = new ArrayList<>(); 1071 subtypes.add(createDummyInputMethodSubtype("ja", SUBTYPE_MODE_KEYBOARD, !IS_AUX, 1072 !IS_OVERRIDES_IMPLICITLY_ENABLED_SUBTYPE, !IS_ASCII_CAPABLE, 1073 !IS_ENABLED_WHEN_DEFAULT_IS_NOT_ASCII_CAPABLE)); 1074 subtypes.add(createDummyInputMethodSubtype("emoji", SUBTYPE_MODE_KEYBOARD, !IS_AUX, 1075 !IS_OVERRIDES_IMPLICITLY_ENABLED_SUBTYPE, !IS_ASCII_CAPABLE, 1076 !IS_ENABLED_WHEN_DEFAULT_IS_NOT_ASCII_CAPABLE)); 1077 preinstalledImes.add(createDummyInputMethodInfo("com.android.apps.inputmethod.japanese", 1078 "com.android.apps.inputmethod.japanese", "DummyJapaneseIme", !IS_AUX, 1079 isDefaultIme, subtypes)); 1080 } 1081 1082 return preinstalledImes; 1083 } 1084 1085 @Test testIsSoftInputModeStateVisibleAllowed()1086 public void testIsSoftInputModeStateVisibleAllowed() { 1087 // On pre-P devices, SOFT_INPUT_STATE_VISIBLE/SOFT_INPUT_STATE_ALWAYS_VISIBLE are always 1088 // allowed, regardless of the focused view state. 1089 assertTrue(InputMethodUtils.isSoftInputModeStateVisibleAllowed( 1090 Build.VERSION_CODES.O_MR1, 0)); 1091 assertTrue(InputMethodUtils.isSoftInputModeStateVisibleAllowed( 1092 Build.VERSION_CODES.O_MR1, StartInputFlags.VIEW_HAS_FOCUS)); 1093 assertTrue(InputMethodUtils.isSoftInputModeStateVisibleAllowed( 1094 Build.VERSION_CODES.O_MR1, 1095 StartInputFlags.VIEW_HAS_FOCUS | StartInputFlags.IS_TEXT_EDITOR)); 1096 1097 // On P+ devices, SOFT_INPUT_STATE_VISIBLE/SOFT_INPUT_STATE_ALWAYS_VISIBLE are allowed only 1098 // when there is a focused View and its View#onCheckIsTextEditor() returns true. 1099 assertFalse(InputMethodUtils.isSoftInputModeStateVisibleAllowed( 1100 Build.VERSION_CODES.P, 0)); 1101 assertFalse(InputMethodUtils.isSoftInputModeStateVisibleAllowed( 1102 Build.VERSION_CODES.P, StartInputFlags.VIEW_HAS_FOCUS)); 1103 assertTrue(InputMethodUtils.isSoftInputModeStateVisibleAllowed( 1104 Build.VERSION_CODES.P, 1105 StartInputFlags.VIEW_HAS_FOCUS | StartInputFlags.IS_TEXT_EDITOR)); 1106 } 1107 } 1108