InputMethodTest.java revision dc489241cfb3691a87942344cf55efd3d98c1107
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 17package android.os; 18 19import android.content.Context; 20import android.content.pm.ApplicationInfo; 21import android.content.pm.ResolveInfo; 22import android.content.pm.ServiceInfo; 23import android.test.InstrumentationTestCase; 24import android.test.suitebuilder.annotation.SmallTest; 25import android.view.inputmethod.InputMethodInfo; 26import android.view.inputmethod.InputMethodSubtype; 27import android.view.inputmethod.InputMethodSubtype.InputMethodSubtypeBuilder; 28 29import com.android.internal.inputmethod.InputMethodUtils; 30 31import java.util.ArrayList; 32import java.util.Arrays; 33import java.util.HashSet; 34import java.util.List; 35import java.util.Locale; 36 37public class InputMethodTest extends InstrumentationTestCase { 38 private static final boolean IS_AUX = true; 39 private static final boolean IS_DEFAULT = true; 40 private static final boolean IS_AUTO = true; 41 private static final boolean IS_ASCII_CAPABLE = true; 42 private static final boolean IS_SYSTEM_READY = true; 43 private static final ArrayList<InputMethodSubtype> NO_SUBTYPE = null; 44 private static final Locale LOCALE_EN_US = new Locale("en", "US"); 45 private static final Locale LOCALE_EN_GB = new Locale("en", "GB"); 46 private static final Locale LOCALE_EN_IN = new Locale("en", "IN"); 47 private static final Locale LOCALE_HI = new Locale("hi"); 48 private static final Locale LOCALE_JA_JP = new Locale("ja", "JP"); 49 private static final String SUBTYPE_MODE_KEYBOARD = "keyboard"; 50 private static final String SUBTYPE_MODE_VOICE = "voice"; 51 52 @SmallTest 53 public void testVoiceImes() throws Exception { 54 // locale: en_US 55 assertDefaultEnabledImes(getImesWithDefaultVoiceIme(), LOCALE_EN_US, !IS_SYSTEM_READY, 56 "DummyDefaultEnKeyboardIme"); 57 assertDefaultEnabledImes(getImesWithoutDefaultVoiceIme(), LOCALE_EN_US, !IS_SYSTEM_READY, 58 "DummyDefaultEnKeyboardIme"); 59 assertDefaultEnabledImes(getImesWithDefaultVoiceIme(), LOCALE_EN_US, IS_SYSTEM_READY, 60 "DummyDefaultAutoVoiceIme", "DummyDefaultEnKeyboardIme"); 61 assertDefaultEnabledImes(getImesWithoutDefaultVoiceIme(), LOCALE_EN_US, IS_SYSTEM_READY, 62 "DummyNonDefaultAutoVoiceIme0", "DummyNonDefaultAutoVoiceIme1", 63 "DummyDefaultEnKeyboardIme"); 64 65 // locale: en_GB 66 assertDefaultEnabledImes(getImesWithDefaultVoiceIme(), LOCALE_EN_GB, !IS_SYSTEM_READY, 67 "DummyDefaultEnKeyboardIme"); 68 assertDefaultEnabledImes(getImesWithoutDefaultVoiceIme(), LOCALE_EN_GB, !IS_SYSTEM_READY, 69 "DummyDefaultEnKeyboardIme"); 70 assertDefaultEnabledImes(getImesWithDefaultVoiceIme(), LOCALE_EN_GB, IS_SYSTEM_READY, 71 "DummyDefaultEnKeyboardIme", "DummyDefaultAutoVoiceIme"); 72 assertDefaultEnabledImes(getImesWithoutDefaultVoiceIme(), LOCALE_EN_GB, IS_SYSTEM_READY, 73 "DummyNonDefaultAutoVoiceIme0", "DummyNonDefaultAutoVoiceIme1", 74 "DummyDefaultEnKeyboardIme"); 75 76 // locale: ja_JP 77 assertDefaultEnabledImes(getImesWithDefaultVoiceIme(), LOCALE_JA_JP, !IS_SYSTEM_READY, 78 "DummyDefaultEnKeyboardIme"); 79 assertDefaultEnabledImes(getImesWithoutDefaultVoiceIme(), LOCALE_JA_JP, !IS_SYSTEM_READY, 80 "DummyDefaultEnKeyboardIme"); 81 assertDefaultEnabledImes(getImesWithDefaultVoiceIme(), LOCALE_JA_JP, IS_SYSTEM_READY, 82 "DummyDefaultEnKeyboardIme", "DummyDefaultAutoVoiceIme"); 83 assertDefaultEnabledImes(getImesWithoutDefaultVoiceIme(), LOCALE_JA_JP, IS_SYSTEM_READY, 84 "DummyNonDefaultAutoVoiceIme0", "DummyNonDefaultAutoVoiceIme1", 85 "DummyDefaultEnKeyboardIme"); 86 } 87 88 @SmallTest 89 public void testKeyboardImes() throws Exception { 90 // locale: en_US 91 assertDefaultEnabledImes(getSamplePreinstalledImes(), LOCALE_EN_US, !IS_SYSTEM_READY, 92 "com.android.apps.inputmethod.latin"); 93 assertDefaultEnabledImes(getSamplePreinstalledImes(), LOCALE_EN_US, IS_SYSTEM_READY, 94 "com.android.apps.inputmethod.voice", "com.android.apps.inputmethod.latin"); 95 96 // locale: en_GB 97 assertDefaultEnabledImes(getSamplePreinstalledImes(), LOCALE_EN_GB, !IS_SYSTEM_READY, 98 "com.android.apps.inputmethod.latin"); 99 assertDefaultEnabledImes(getSamplePreinstalledImes(), LOCALE_EN_GB, IS_SYSTEM_READY, 100 "com.android.apps.inputmethod.voice", "com.android.apps.inputmethod.latin"); 101 102 // locale: en_IN 103 assertDefaultEnabledImes(getSamplePreinstalledImes(), LOCALE_EN_IN, !IS_SYSTEM_READY, 104 "com.android.apps.inputmethod.latin"); 105 assertDefaultEnabledImes(getSamplePreinstalledImes(), LOCALE_EN_IN, IS_SYSTEM_READY, 106 "com.android.apps.inputmethod.voice", "com.android.apps.inputmethod.latin", 107 "com.android.apps.inputmethod.hindi"); 108 109 // locale: hi 110 assertDefaultEnabledImes(getSamplePreinstalledImes(), LOCALE_HI, !IS_SYSTEM_READY, 111 "com.android.apps.inputmethod.latin"); 112 assertDefaultEnabledImes(getSamplePreinstalledImes(), LOCALE_HI, IS_SYSTEM_READY, 113 "com.android.apps.inputmethod.voice", "com.android.apps.inputmethod.latin", 114 "com.android.apps.inputmethod.hindi"); 115 116 // locale: ja_JP 117 assertDefaultEnabledImes(getSamplePreinstalledImes(), LOCALE_JA_JP, !IS_SYSTEM_READY, 118 "com.android.apps.inputmethod.latin"); 119 assertDefaultEnabledImes(getSamplePreinstalledImes(), LOCALE_JA_JP, IS_SYSTEM_READY, 120 "com.android.apps.inputmethod.voice", "com.android.apps.inputmethod.latin", 121 "com.android.apps.inputmethod.japanese"); 122 } 123 124 @SmallTest 125 public void testParcelable() throws Exception { 126 final ArrayList<InputMethodInfo> originalList = getSamplePreinstalledImes(); 127 final List<InputMethodInfo> clonedList = cloneViaParcel(originalList); 128 assertNotNull(clonedList); 129 final List<InputMethodInfo> clonedClonedList = cloneViaParcel(clonedList); 130 assertNotNull(clonedClonedList); 131 assertEquals(originalList, clonedList); 132 assertEquals(clonedList, clonedClonedList); 133 assertEquals(originalList.size(), clonedList.size()); 134 assertEquals(clonedList.size(), clonedClonedList.size()); 135 for (int imeIndex = 0; imeIndex < originalList.size(); ++imeIndex) { 136 verifyEquality(originalList.get(imeIndex), clonedList.get(imeIndex)); 137 verifyEquality(clonedList.get(imeIndex), clonedClonedList.get(imeIndex)); 138 } 139 } 140 141 private void assertDefaultEnabledImes(final ArrayList<InputMethodInfo> preinstalledImes, 142 final Locale systemLocale, final boolean isSystemReady, String... imeNames) { 143 final Context context = getInstrumentation().getTargetContext(); 144 assertEquals(new HashSet<String>(Arrays.asList(imeNames)), 145 getPackageNames(callGetDefaultEnabledImesUnderWithLocale(context, 146 isSystemReady, preinstalledImes, systemLocale))); 147 } 148 149 private static List<InputMethodInfo> cloneViaParcel(final List<InputMethodInfo> list) { 150 Parcel p = null; 151 try { 152 p = Parcel.obtain(); 153 p.writeTypedList(list); 154 p.setDataPosition(0); 155 return p.createTypedArrayList(InputMethodInfo.CREATOR); 156 } finally { 157 if (p != null) { 158 p.recycle(); 159 } 160 } 161 } 162 163 private static ArrayList<InputMethodInfo> callGetDefaultEnabledImesUnderWithLocale( 164 final Context context, final boolean isSystemReady, 165 final ArrayList<InputMethodInfo> imis, final Locale locale) { 166 final Locale initialLocale = context.getResources().getConfiguration().locale; 167 try { 168 context.getResources().getConfiguration().setLocale(locale); 169 return InputMethodUtils.getDefaultEnabledImes(context, isSystemReady, imis); 170 } finally { 171 context.getResources().getConfiguration().setLocale(initialLocale); 172 } 173 } 174 175 private HashSet<String> getPackageNames(final ArrayList<InputMethodInfo> imis) { 176 final HashSet<String> packageNames = new HashSet<>(); 177 for (final InputMethodInfo imi : imis) { 178 final String actualPackageName = imi.getPackageName(); 179 packageNames.add(actualPackageName); 180 } 181 return packageNames; 182 } 183 184 private static void verifyEquality(InputMethodInfo expected, InputMethodInfo actual) { 185 assertEquals(expected, actual); 186 assertEquals(expected.getSubtypeCount(), actual.getSubtypeCount()); 187 for (int subtypeIndex = 0; subtypeIndex < expected.getSubtypeCount(); ++subtypeIndex) { 188 final InputMethodSubtype expectedSubtype = expected.getSubtypeAt(subtypeIndex); 189 final InputMethodSubtype actualSubtype = actual.getSubtypeAt(subtypeIndex); 190 assertEquals(expectedSubtype, actualSubtype); 191 assertEquals(expectedSubtype.hashCode(), actualSubtype.hashCode()); 192 } 193 } 194 195 private static InputMethodInfo createDummyInputMethodInfo(String packageName, String name, 196 CharSequence label, boolean isAuxIme, boolean isDefault, 197 List<InputMethodSubtype> subtypes) { 198 final ResolveInfo ri = new ResolveInfo(); 199 final ServiceInfo si = new ServiceInfo(); 200 final ApplicationInfo ai = new ApplicationInfo(); 201 ai.packageName = packageName; 202 ai.enabled = true; 203 ai.flags |= ApplicationInfo.FLAG_SYSTEM; 204 si.applicationInfo = ai; 205 si.enabled = true; 206 si.packageName = packageName; 207 si.name = name; 208 si.exported = true; 209 si.nonLocalizedLabel = label; 210 ri.serviceInfo = si; 211 return new InputMethodInfo(ri, isAuxIme, "", subtypes, 1, isDefault); 212 } 213 214 private static InputMethodSubtype createDummyInputMethodSubtype(String locale, String mode, 215 boolean isAuxiliary, boolean overridesImplicitlyEnabledSubtype, 216 boolean isAsciiCapable) { 217 return new InputMethodSubtypeBuilder() 218 .setSubtypeNameResId(0) 219 .setSubtypeIconResId(0) 220 .setSubtypeLocale(locale) 221 .setSubtypeMode(mode) 222 .setSubtypeExtraValue("") 223 .setIsAuxiliary(isAuxiliary) 224 .setOverridesImplicitlyEnabledSubtype(overridesImplicitlyEnabledSubtype) 225 .setIsAsciiCapable(isAsciiCapable) 226 .build(); 227 } 228 229 private static ArrayList<InputMethodInfo> getImesWithDefaultVoiceIme() { 230 ArrayList<InputMethodInfo> preinstalledImes = new ArrayList<>(); 231 { 232 final ArrayList<InputMethodSubtype> subtypes = new ArrayList<InputMethodSubtype>(); 233 subtypes.add(createDummyInputMethodSubtype("auto", SUBTYPE_MODE_VOICE, IS_AUX, IS_AUTO, 234 !IS_ASCII_CAPABLE)); 235 subtypes.add(createDummyInputMethodSubtype("en_US", SUBTYPE_MODE_VOICE, IS_AUX, 236 !IS_AUTO, !IS_ASCII_CAPABLE)); 237 preinstalledImes.add(createDummyInputMethodInfo("DummyDefaultAutoVoiceIme", 238 "dummy.voice0", "DummyVoice0", IS_AUX, IS_DEFAULT, subtypes)); 239 } 240 preinstalledImes.addAll(getImesWithoutDefaultVoiceIme()); 241 return preinstalledImes; 242 } 243 244 private static ArrayList<InputMethodInfo> getImesWithoutDefaultVoiceIme() { 245 ArrayList<InputMethodInfo> preinstalledImes = new ArrayList<>(); 246 { 247 final ArrayList<InputMethodSubtype> subtypes = new ArrayList<InputMethodSubtype>(); 248 subtypes.add(createDummyInputMethodSubtype("auto", SUBTYPE_MODE_VOICE, IS_AUX, IS_AUTO, 249 !IS_ASCII_CAPABLE)); 250 subtypes.add(createDummyInputMethodSubtype("en_US", SUBTYPE_MODE_VOICE, IS_AUX, 251 !IS_AUTO, !IS_ASCII_CAPABLE)); 252 preinstalledImes.add(createDummyInputMethodInfo("DummyNonDefaultAutoVoiceIme0", 253 "dummy.voice1", "DummyVoice1", IS_AUX, !IS_DEFAULT, subtypes)); 254 } 255 { 256 final ArrayList<InputMethodSubtype> subtypes = new ArrayList<InputMethodSubtype>(); 257 subtypes.add(createDummyInputMethodSubtype("auto", SUBTYPE_MODE_VOICE, IS_AUX, IS_AUTO, 258 !IS_ASCII_CAPABLE)); 259 subtypes.add(createDummyInputMethodSubtype("en_US", SUBTYPE_MODE_VOICE, IS_AUX, 260 !IS_AUTO, !IS_ASCII_CAPABLE)); 261 preinstalledImes.add(createDummyInputMethodInfo("DummyNonDefaultAutoVoiceIme1", 262 "dummy.voice2", "DummyVoice2", IS_AUX, !IS_DEFAULT, subtypes)); 263 } 264 { 265 final ArrayList<InputMethodSubtype> subtypes = new ArrayList<InputMethodSubtype>(); 266 subtypes.add(createDummyInputMethodSubtype("en_US", SUBTYPE_MODE_VOICE, IS_AUX, 267 !IS_AUTO, !IS_ASCII_CAPABLE)); 268 preinstalledImes.add(createDummyInputMethodInfo("DummyNonDefaultVoiceIme2", 269 "dummy.voice3", "DummyVoice3", IS_AUX, !IS_DEFAULT, subtypes)); 270 } 271 { 272 final ArrayList<InputMethodSubtype> subtypes = new ArrayList<InputMethodSubtype>(); 273 subtypes.add(createDummyInputMethodSubtype("en_US", SUBTYPE_MODE_KEYBOARD, !IS_AUX, 274 !IS_AUTO, IS_ASCII_CAPABLE)); 275 preinstalledImes.add(createDummyInputMethodInfo("DummyDefaultEnKeyboardIme", 276 "dummy.keyboard0", "DummyKeyboard0", !IS_AUX, IS_DEFAULT, subtypes)); 277 } 278 return preinstalledImes; 279 } 280 281 private static ArrayList<InputMethodInfo> getSamplePreinstalledImes() { 282 ArrayList<InputMethodInfo> preinstalledImes = new ArrayList<>(); 283 284 // a dummy Voice IME 285 { 286 final ArrayList<InputMethodSubtype> subtypes = new ArrayList<InputMethodSubtype>(); 287 subtypes.add(createDummyInputMethodSubtype("", SUBTYPE_MODE_VOICE, IS_AUX, 288 IS_AUTO, !IS_ASCII_CAPABLE)); 289 preinstalledImes.add(createDummyInputMethodInfo("com.android.apps.inputmethod.voice", 290 "com.android.inputmethod.voice", "DummyVoiceIme", IS_AUX, IS_DEFAULT, 291 subtypes)); 292 } 293 294 // a dummy Hindi IME 295 { 296 final ArrayList<InputMethodSubtype> subtypes = new ArrayList<InputMethodSubtype>(); 297 // TODO: This subtype should be marked as IS_ASCII_CAPABLE 298 subtypes.add(createDummyInputMethodSubtype("en_IN", SUBTYPE_MODE_KEYBOARD, !IS_AUX, 299 !IS_AUTO, !IS_ASCII_CAPABLE)); 300 subtypes.add(createDummyInputMethodSubtype("hi", SUBTYPE_MODE_KEYBOARD, !IS_AUX, 301 !IS_AUTO, !IS_ASCII_CAPABLE)); 302 preinstalledImes.add(createDummyInputMethodInfo("com.android.apps.inputmethod.hindi", 303 "com.android.inputmethod.hindi", "DummyHindiIme", !IS_AUX, IS_DEFAULT, 304 subtypes)); 305 } 306 307 // a dummy Pinyin IME 308 { 309 final ArrayList<InputMethodSubtype> subtypes = new ArrayList<InputMethodSubtype>(); 310 subtypes.add(createDummyInputMethodSubtype("zh_CN", SUBTYPE_MODE_KEYBOARD, !IS_AUX, 311 !IS_AUTO, !IS_ASCII_CAPABLE)); 312 preinstalledImes.add(createDummyInputMethodInfo("ccom.android.apps.inputmethod.pinyin", 313 "com.android.apps.inputmethod.pinyin", "DummyPinyinIme", !IS_AUX, IS_DEFAULT, 314 subtypes)); 315 } 316 317 // a dummy Korian IME 318 { 319 final ArrayList<InputMethodSubtype> subtypes = new ArrayList<InputMethodSubtype>(); 320 subtypes.add(createDummyInputMethodSubtype("ko", SUBTYPE_MODE_KEYBOARD, !IS_AUX, 321 !IS_AUTO, !IS_ASCII_CAPABLE)); 322 preinstalledImes.add(createDummyInputMethodInfo("com.android.apps.inputmethod.korean", 323 "com.android.apps.inputmethod.korean", "DummyKorianIme", !IS_AUX, IS_DEFAULT, 324 subtypes)); 325 } 326 327 // a dummy Latin IME 328 { 329 final ArrayList<InputMethodSubtype> subtypes = new ArrayList<InputMethodSubtype>(); 330 subtypes.add(createDummyInputMethodSubtype("en_US", SUBTYPE_MODE_KEYBOARD, !IS_AUX, 331 !IS_AUTO, IS_ASCII_CAPABLE)); 332 subtypes.add(createDummyInputMethodSubtype("en_GB", SUBTYPE_MODE_KEYBOARD, !IS_AUX, 333 !IS_AUTO, IS_ASCII_CAPABLE)); 334 subtypes.add(createDummyInputMethodSubtype("en_IN", SUBTYPE_MODE_KEYBOARD, !IS_AUX, 335 !IS_AUTO, IS_ASCII_CAPABLE)); 336 subtypes.add(createDummyInputMethodSubtype("hi", SUBTYPE_MODE_KEYBOARD, !IS_AUX, 337 !IS_AUTO, IS_ASCII_CAPABLE)); 338 preinstalledImes.add(createDummyInputMethodInfo("com.android.apps.inputmethod.latin", 339 "com.android.apps.inputmethod.latin", "DummyLatinIme", !IS_AUX, IS_DEFAULT, 340 subtypes)); 341 } 342 343 // a dummy Japanese IME 344 { 345 final ArrayList<InputMethodSubtype> subtypes = new ArrayList<InputMethodSubtype>(); 346 subtypes.add(createDummyInputMethodSubtype("ja", SUBTYPE_MODE_KEYBOARD, !IS_AUX, 347 !IS_AUTO, !IS_ASCII_CAPABLE)); 348 subtypes.add(createDummyInputMethodSubtype("emoji", SUBTYPE_MODE_KEYBOARD, !IS_AUX, 349 !IS_AUTO, !IS_ASCII_CAPABLE)); 350 preinstalledImes.add(createDummyInputMethodInfo("com.android.apps.inputmethod.japanese", 351 "com.android.apps.inputmethod.japanese", "DummyJapaneseIme", !IS_AUX, 352 IS_DEFAULT, subtypes)); 353 } 354 355 return preinstalledImes; 356 } 357} 358