InputMethodTest.java revision f487e0e395a4e49f28c6448339d57357667756c6
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.List;
33import java.util.Locale;
34import java.util.Objects;
35
36public class InputMethodTest extends InstrumentationTestCase {
37    private static final boolean IS_AUX = true;
38    private static final boolean IS_DEFAULT = true;
39    private static final boolean IS_OVERRIDES_IMPLICITLY_ENABLED_SUBTYPE = true;
40    private static final boolean IS_ASCII_CAPABLE = true;
41    private static final boolean IS_ENABLED_WHEN_DEFAULT_IS_NOT_ASCII_CAPABLE = true;
42    private static final boolean IS_SYSTEM_READY = true;
43    private static final Locale LOCALE_EN = new Locale("en");
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_FI = new Locale("fi");
48    private static final Locale LOCALE_FI_FI = new Locale("fi", "FI");
49    private static final Locale LOCALE_FIL = new Locale("fil");
50    private static final Locale LOCALE_FIL_PH = new Locale("fil", "PH");
51    private static final Locale LOCALE_FR = new Locale("fr");
52    private static final Locale LOCALE_FR_CA = new Locale("fr", "CA");
53    private static final Locale LOCALE_HI = new Locale("hi");
54    private static final Locale LOCALE_JA_JP = new Locale("ja", "JP");
55    private static final Locale LOCALE_ZH_CN = new Locale("zh", "CN");
56    private static final Locale LOCALE_ZH_TW = new Locale("zh", "TW");
57    private static final Locale LOCALE_IN = new Locale("in");
58    private static final Locale LOCALE_ID = new Locale("id");
59    private static final String SUBTYPE_MODE_KEYBOARD = "keyboard";
60    private static final String SUBTYPE_MODE_VOICE = "voice";
61    private static final String SUBTYPE_MODE_ANY = null;
62    private static final String EXTRA_VALUE_PAIR_SEPARATOR = ",";
63    private static final String EXTRA_VALUE_ASCII_CAPABLE = "AsciiCapable";
64    private static final String EXTRA_VALUE_ENABLED_WHEN_DEFAULT_IS_NOT_ASCII_CAPABLE =
65            "EnabledWhenDefaultIsNotAsciiCapable";
66
67    @SmallTest
68    public void testVoiceImes() throws Exception {
69        // locale: en_US
70        assertDefaultEnabledImes(getImesWithDefaultVoiceIme(), LOCALE_EN_US,
71                !IS_SYSTEM_READY, "DummyDefaultEnKeyboardIme", "DummyDefaultAutoVoiceIme");
72        assertDefaultEnabledImes(getImesWithoutDefaultVoiceIme(), LOCALE_EN_US,
73                !IS_SYSTEM_READY, "DummyDefaultEnKeyboardIme");
74        assertDefaultEnabledImes(getImesWithDefaultVoiceIme(), LOCALE_EN_US,
75                IS_SYSTEM_READY, "DummyDefaultEnKeyboardIme", "DummyDefaultAutoVoiceIme");
76        assertDefaultEnabledImes(getImesWithoutDefaultVoiceIme(), LOCALE_EN_US,
77                IS_SYSTEM_READY, "DummyDefaultEnKeyboardIme", "DummyNonDefaultAutoVoiceIme0",
78                "DummyNonDefaultAutoVoiceIme1");
79
80        // locale: en_GB
81        assertDefaultEnabledImes(getImesWithDefaultVoiceIme(), LOCALE_EN_GB,
82                !IS_SYSTEM_READY, "DummyDefaultEnKeyboardIme", "DummyDefaultAutoVoiceIme");
83        assertDefaultEnabledImes(getImesWithoutDefaultVoiceIme(), LOCALE_EN_GB,
84                !IS_SYSTEM_READY, "DummyDefaultEnKeyboardIme");
85        assertDefaultEnabledImes(getImesWithDefaultVoiceIme(), LOCALE_EN_GB,
86                IS_SYSTEM_READY, "DummyDefaultEnKeyboardIme", "DummyDefaultAutoVoiceIme");
87        assertDefaultEnabledImes(getImesWithoutDefaultVoiceIme(), LOCALE_EN_GB,
88                IS_SYSTEM_READY, "DummyDefaultEnKeyboardIme", "DummyNonDefaultAutoVoiceIme0",
89                "DummyNonDefaultAutoVoiceIme1");
90
91        // locale: ja_JP
92        assertDefaultEnabledImes(getImesWithDefaultVoiceIme(), LOCALE_JA_JP,
93                !IS_SYSTEM_READY, "DummyDefaultEnKeyboardIme", "DummyDefaultAutoVoiceIme");
94        assertDefaultEnabledImes(getImesWithoutDefaultVoiceIme(), LOCALE_JA_JP,
95                !IS_SYSTEM_READY, "DummyDefaultEnKeyboardIme");
96        assertDefaultEnabledImes(getImesWithDefaultVoiceIme(), LOCALE_JA_JP,
97                IS_SYSTEM_READY, "DummyDefaultEnKeyboardIme", "DummyDefaultAutoVoiceIme");
98        assertDefaultEnabledImes(getImesWithoutDefaultVoiceIme(), LOCALE_JA_JP,
99                IS_SYSTEM_READY, "DummyDefaultEnKeyboardIme", "DummyNonDefaultAutoVoiceIme0",
100                "DummyNonDefaultAutoVoiceIme1");
101    }
102
103    @SmallTest
104    public void testKeyboardImes() throws Exception {
105        // locale: en_US
106        assertDefaultEnabledImes(getSamplePreinstalledImes("en-rUS"), LOCALE_EN_US,
107                !IS_SYSTEM_READY, "com.android.apps.inputmethod.latin");
108        assertDefaultEnabledImes(getSamplePreinstalledImes("en-rUS"), LOCALE_EN_US,
109                IS_SYSTEM_READY, "com.android.apps.inputmethod.latin",
110                "com.android.apps.inputmethod.voice");
111
112        // locale: en_GB
113        assertDefaultEnabledImes(getSamplePreinstalledImes("en-rGB"), LOCALE_EN_GB,
114                !IS_SYSTEM_READY, "com.android.apps.inputmethod.latin");
115        assertDefaultEnabledImes(getSamplePreinstalledImes("en-rGB"), LOCALE_EN_GB,
116                IS_SYSTEM_READY, "com.android.apps.inputmethod.latin",
117                "com.android.apps.inputmethod.voice");
118
119        // locale: en_IN
120        assertDefaultEnabledImes(getSamplePreinstalledImes("en-rIN"), LOCALE_EN_IN,
121                !IS_SYSTEM_READY, "com.android.apps.inputmethod.latin");
122        assertDefaultEnabledImes(getSamplePreinstalledImes("en-rIN"), LOCALE_EN_IN,
123                IS_SYSTEM_READY, "com.android.apps.inputmethod.hindi",
124                "com.android.apps.inputmethod.latin", "com.android.apps.inputmethod.voice");
125
126        // locale: hi
127        assertDefaultEnabledImes(getSamplePreinstalledImes("hi"), LOCALE_HI,
128                !IS_SYSTEM_READY, "com.android.apps.inputmethod.latin");
129        assertDefaultEnabledImes(getSamplePreinstalledImes("hi"), LOCALE_HI,
130                IS_SYSTEM_READY, "com.android.apps.inputmethod.hindi",
131                "com.android.apps.inputmethod.latin", "com.android.apps.inputmethod.voice");
132
133        // locale: ja_JP
134        assertDefaultEnabledImes(getSamplePreinstalledImes("ja-rJP"), LOCALE_JA_JP,
135                !IS_SYSTEM_READY, "com.android.apps.inputmethod.latin");
136        assertDefaultEnabledImes(getSamplePreinstalledImes("ja-rJP"), LOCALE_JA_JP,
137                IS_SYSTEM_READY, "com.android.apps.inputmethod.japanese",
138                "com.android.apps.inputmethod.voice");
139
140        // locale: zh_CN
141        assertDefaultEnabledImes(getSamplePreinstalledImes("zh-rCN"), LOCALE_ZH_CN,
142                !IS_SYSTEM_READY, "com.android.apps.inputmethod.latin");
143        assertDefaultEnabledImes(getSamplePreinstalledImes("zh-rCN"), LOCALE_ZH_CN,
144                IS_SYSTEM_READY, "com.android.apps.inputmethod.pinyin",
145                "com.android.apps.inputmethod.voice");
146
147        // locale: zh_TW
148        // Note: In this case, no IME is suitable for the system locale. Hence we will pick up a
149        // fallback IME regardless of the "default" attribute.
150        assertDefaultEnabledImes(getSamplePreinstalledImes("zh-rTW"), LOCALE_ZH_TW,
151                !IS_SYSTEM_READY, "com.android.apps.inputmethod.latin");
152        assertDefaultEnabledImes(getSamplePreinstalledImes("zh-rTW"), LOCALE_ZH_TW,
153                IS_SYSTEM_READY, "com.android.apps.inputmethod.latin",
154                "com.android.apps.inputmethod.voice");
155    }
156
157    @SmallTest
158    public void testParcelable() throws Exception {
159        final ArrayList<InputMethodInfo> originalList = getSamplePreinstalledImes("en-rUS");
160        final List<InputMethodInfo> clonedList = cloneViaParcel(originalList);
161        assertNotNull(clonedList);
162        final List<InputMethodInfo> clonedClonedList = cloneViaParcel(clonedList);
163        assertNotNull(clonedClonedList);
164        assertEquals(originalList, clonedList);
165        assertEquals(clonedList, clonedClonedList);
166        assertEquals(originalList.size(), clonedList.size());
167        assertEquals(clonedList.size(), clonedClonedList.size());
168        for (int imeIndex = 0; imeIndex < originalList.size(); ++imeIndex) {
169            verifyEquality(originalList.get(imeIndex), clonedList.get(imeIndex));
170            verifyEquality(clonedList.get(imeIndex), clonedClonedList.get(imeIndex));
171        }
172    }
173
174    @SmallTest
175    public void testGetImplicitlyApplicableSubtypesLocked() throws Exception {
176        final InputMethodSubtype nonAutoEnUS = createDummyInputMethodSubtype("en_US",
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 nonAutoEnGB = createDummyInputMethodSubtype("en_GB",
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 nonAutoFrCA = createDummyInputMethodSubtype("fr_CA",
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 nonAutoFr = createDummyInputMethodSubtype("fr_CA",
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 nonAutoFil = createDummyInputMethodSubtype("fil",
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 nonAutoIn = createDummyInputMethodSubtype("in",
192                SUBTYPE_MODE_KEYBOARD, !IS_AUX, !IS_OVERRIDES_IMPLICITLY_ENABLED_SUBTYPE,
193                IS_ASCII_CAPABLE, IS_ENABLED_WHEN_DEFAULT_IS_NOT_ASCII_CAPABLE);
194        final InputMethodSubtype nonAutoId = createDummyInputMethodSubtype("id",
195                SUBTYPE_MODE_KEYBOARD, !IS_AUX, !IS_OVERRIDES_IMPLICITLY_ENABLED_SUBTYPE,
196                IS_ASCII_CAPABLE, IS_ENABLED_WHEN_DEFAULT_IS_NOT_ASCII_CAPABLE);
197        final InputMethodSubtype autoSubtype = createDummyInputMethodSubtype("auto",
198                SUBTYPE_MODE_KEYBOARD, !IS_AUX, IS_OVERRIDES_IMPLICITLY_ENABLED_SUBTYPE,
199                IS_ASCII_CAPABLE, !IS_ENABLED_WHEN_DEFAULT_IS_NOT_ASCII_CAPABLE);
200        final InputMethodSubtype nonAutoJa = createDummyInputMethodSubtype("ja",
201                SUBTYPE_MODE_KEYBOARD, !IS_AUX, !IS_OVERRIDES_IMPLICITLY_ENABLED_SUBTYPE,
202                !IS_ASCII_CAPABLE, !IS_ENABLED_WHEN_DEFAULT_IS_NOT_ASCII_CAPABLE);
203        final InputMethodSubtype nonAutoEnabledWhenDefaultIsNotAsciiCalableSubtype =
204                createDummyInputMethodSubtype("zz", SUBTYPE_MODE_KEYBOARD, !IS_AUX,
205                        !IS_OVERRIDES_IMPLICITLY_ENABLED_SUBTYPE, !IS_ASCII_CAPABLE,
206                        IS_ENABLED_WHEN_DEFAULT_IS_NOT_ASCII_CAPABLE);
207        final InputMethodSubtype nonAutoEnabledWhenDefaultIsNotAsciiCalableSubtype2 =
208                createDummyInputMethodSubtype("zz", SUBTYPE_MODE_KEYBOARD, !IS_AUX,
209                        !IS_OVERRIDES_IMPLICITLY_ENABLED_SUBTYPE, !IS_ASCII_CAPABLE,
210                        IS_ENABLED_WHEN_DEFAULT_IS_NOT_ASCII_CAPABLE);
211
212        // Make sure that an automatic subtype (overridesImplicitlyEnabledSubtype:true) is
213        // selected no matter what locale is specified.
214        {
215            final ArrayList<InputMethodSubtype> subtypes = new ArrayList<InputMethodSubtype>();
216            subtypes.add(nonAutoEnUS);
217            subtypes.add(nonAutoEnGB);
218            subtypes.add(nonAutoJa);
219            subtypes.add(nonAutoFil);
220            subtypes.add(autoSubtype);  // overridesImplicitlyEnabledSubtype == true
221            subtypes.add(nonAutoEnabledWhenDefaultIsNotAsciiCalableSubtype);
222            subtypes.add(nonAutoEnabledWhenDefaultIsNotAsciiCalableSubtype2);
223            final InputMethodInfo imi = createDummyInputMethodInfo(
224                    "com.android.apps.inputmethod.latin",
225                    "com.android.apps.inputmethod.latin", "DummyLatinIme", !IS_AUX, IS_DEFAULT,
226                    subtypes);
227            final ArrayList<InputMethodSubtype> result =
228                    callGetImplicitlyApplicableSubtypesLockedWithLocale(LOCALE_EN_US, imi);
229            assertEquals(1, result.size());
230            verifyEquality(autoSubtype, result.get(0));
231        }
232
233        // Make sure that a subtype whose locale is exactly equal to the specified locale is
234        // selected as long as there is no no automatic subtype
235        // (overridesImplicitlyEnabledSubtype:true) in the given list.
236        {
237            final ArrayList<InputMethodSubtype> subtypes = new ArrayList<InputMethodSubtype>();
238            subtypes.add(nonAutoEnUS);  // locale == "en_US"
239            subtypes.add(nonAutoEnGB);
240            subtypes.add(nonAutoJa);
241            subtypes.add(nonAutoFil);
242            subtypes.add(nonAutoEnabledWhenDefaultIsNotAsciiCalableSubtype);
243            subtypes.add(nonAutoEnabledWhenDefaultIsNotAsciiCalableSubtype2);
244            final InputMethodInfo imi = createDummyInputMethodInfo(
245                    "com.android.apps.inputmethod.latin",
246                    "com.android.apps.inputmethod.latin", "DummyLatinIme", !IS_AUX, IS_DEFAULT,
247                    subtypes);
248            final ArrayList<InputMethodSubtype> result =
249                    callGetImplicitlyApplicableSubtypesLockedWithLocale(LOCALE_EN_US, imi);
250            assertEquals(1, result.size());
251            verifyEquality(nonAutoEnUS, result.get(0));
252        }
253
254        // Make sure that a subtype whose locale is exactly equal to the specified locale is
255        // selected as long as there is no automatic subtype
256        // (overridesImplicitlyEnabledSubtype:true) in the given list.
257        {
258            final ArrayList<InputMethodSubtype> subtypes = new ArrayList<InputMethodSubtype>();
259            subtypes.add(nonAutoEnUS);
260            subtypes.add(nonAutoEnGB); // locale == "en_GB"
261            subtypes.add(nonAutoJa);
262            subtypes.add(nonAutoFil);
263            subtypes.add(nonAutoEnabledWhenDefaultIsNotAsciiCalableSubtype);
264            final InputMethodInfo imi = createDummyInputMethodInfo(
265                    "com.android.apps.inputmethod.latin",
266                    "com.android.apps.inputmethod.latin", "DummyLatinIme", !IS_AUX, IS_DEFAULT,
267                    subtypes);
268            final ArrayList<InputMethodSubtype> result =
269                    callGetImplicitlyApplicableSubtypesLockedWithLocale(LOCALE_EN_GB, imi);
270            assertEquals(1, result.size());
271            verifyEquality(nonAutoEnGB, result.get(0));
272        }
273
274        // If there is no automatic subtype (overridesImplicitlyEnabledSubtype:true) and
275        // any subtype whose locale is exactly equal to the specified locale in the given list,
276        // try to find a subtype whose language is equal to the language part of the given locale.
277        // Here make sure that a subtype (locale: "fr_CA") can be found with locale: "fr".
278        {
279            final ArrayList<InputMethodSubtype> subtypes = new ArrayList<InputMethodSubtype>();
280            subtypes.add(nonAutoFrCA);  // locale == "fr_CA"
281            subtypes.add(nonAutoJa);
282            subtypes.add(nonAutoFil);
283            subtypes.add(nonAutoEnabledWhenDefaultIsNotAsciiCalableSubtype);
284            subtypes.add(nonAutoEnabledWhenDefaultIsNotAsciiCalableSubtype2);
285            final InputMethodInfo imi = createDummyInputMethodInfo(
286                    "com.android.apps.inputmethod.latin",
287                    "com.android.apps.inputmethod.latin", "DummyLatinIme", !IS_AUX, IS_DEFAULT,
288                    subtypes);
289            final ArrayList<InputMethodSubtype> result =
290                    callGetImplicitlyApplicableSubtypesLockedWithLocale(LOCALE_FR, imi);
291            assertEquals(1, result.size());
292            verifyEquality(nonAutoFrCA, result.get(0));
293        }
294        // Then make sure that a subtype (locale: "fr") can be found with locale: "fr_CA".
295        {
296            final ArrayList<InputMethodSubtype> subtypes = new ArrayList<InputMethodSubtype>();
297            subtypes.add(nonAutoFr);  // locale == "fr"
298            subtypes.add(nonAutoJa);
299            subtypes.add(nonAutoFil);
300            subtypes.add(nonAutoEnabledWhenDefaultIsNotAsciiCalableSubtype);
301            subtypes.add(nonAutoEnabledWhenDefaultIsNotAsciiCalableSubtype2);
302            final InputMethodInfo imi = createDummyInputMethodInfo(
303                    "com.android.apps.inputmethod.latin",
304                    "com.android.apps.inputmethod.latin", "DummyLatinIme", !IS_AUX, IS_DEFAULT,
305                    subtypes);
306            final ArrayList<InputMethodSubtype> result =
307                    callGetImplicitlyApplicableSubtypesLockedWithLocale(LOCALE_FR_CA, imi);
308            assertEquals(1, result.size());
309            verifyEquality(nonAutoFrCA, result.get(0));
310        }
311
312        // Make sure that subtypes which have "EnabledWhenDefaultIsNotAsciiCapable" in its
313        // extra value is selected if and only if all other selected IMEs are not AsciiCapable.
314        {
315            final ArrayList<InputMethodSubtype> subtypes = new ArrayList<InputMethodSubtype>();
316            subtypes.add(nonAutoEnUS);
317            subtypes.add(nonAutoJa);    // not ASCII capable
318            subtypes.add(nonAutoEnabledWhenDefaultIsNotAsciiCalableSubtype);
319            subtypes.add(nonAutoEnabledWhenDefaultIsNotAsciiCalableSubtype2);
320            final InputMethodInfo imi = createDummyInputMethodInfo(
321                    "com.android.apps.inputmethod.latin",
322                    "com.android.apps.inputmethod.latin", "DummyLatinIme", !IS_AUX, IS_DEFAULT,
323                    subtypes);
324            final ArrayList<InputMethodSubtype> result =
325                    callGetImplicitlyApplicableSubtypesLockedWithLocale(LOCALE_JA_JP, imi);
326            assertEquals(3, result.size());
327            verifyEquality(nonAutoJa, result.get(0));
328            verifyEquality(nonAutoEnabledWhenDefaultIsNotAsciiCalableSubtype, result.get(1));
329            verifyEquality(nonAutoEnabledWhenDefaultIsNotAsciiCalableSubtype2, result.get(2));
330        }
331
332        // Make sure that 3-letter language code can be handled.
333        {
334            final ArrayList<InputMethodSubtype> subtypes = new ArrayList<InputMethodSubtype>();
335            subtypes.add(nonAutoEnUS);
336            subtypes.add(nonAutoFil);
337            final InputMethodInfo imi = createDummyInputMethodInfo(
338                    "com.android.apps.inputmethod.latin",
339                    "com.android.apps.inputmethod.latin", "DummyLatinIme", !IS_AUX, IS_DEFAULT,
340                    subtypes);
341            final ArrayList<InputMethodSubtype> result =
342                    callGetImplicitlyApplicableSubtypesLockedWithLocale(LOCALE_FIL_PH, imi);
343            assertEquals(1, result.size());
344            verifyEquality(nonAutoFil, result.get(0));
345        }
346
347        // Make sure that we never end up matching "fi" (finnish) with "fil" (filipino).
348        // Also make sure that the first subtype will be used as the last-resort candidate.
349        {
350            final ArrayList<InputMethodSubtype> subtypes = new ArrayList<InputMethodSubtype>();
351            subtypes.add(nonAutoJa);
352            subtypes.add(nonAutoEnUS);
353            subtypes.add(nonAutoFil);
354            final InputMethodInfo imi = createDummyInputMethodInfo(
355                    "com.android.apps.inputmethod.latin",
356                    "com.android.apps.inputmethod.latin", "DummyLatinIme", !IS_AUX, IS_DEFAULT,
357                    subtypes);
358            final ArrayList<InputMethodSubtype> result =
359                    callGetImplicitlyApplicableSubtypesLockedWithLocale(LOCALE_FI, imi);
360            assertEquals(1, result.size());
361            verifyEquality(nonAutoJa, result.get(0));
362        }
363
364        // Make sure that "in" and "id" conversion is taken into account.
365        {
366            final ArrayList<InputMethodSubtype> subtypes = new ArrayList<InputMethodSubtype>();
367            subtypes.add(nonAutoIn);
368            subtypes.add(nonAutoEnUS);
369            final InputMethodInfo imi = createDummyInputMethodInfo(
370                    "com.android.apps.inputmethod.latin",
371                    "com.android.apps.inputmethod.latin", "DummyLatinIme", !IS_AUX, IS_DEFAULT,
372                    subtypes);
373            final ArrayList<InputMethodSubtype> result =
374                    callGetImplicitlyApplicableSubtypesLockedWithLocale(LOCALE_IN, imi);
375            assertEquals(1, result.size());
376            verifyEquality(nonAutoIn, result.get(0));
377        }
378        {
379            final ArrayList<InputMethodSubtype> subtypes = new ArrayList<InputMethodSubtype>();
380            subtypes.add(nonAutoIn);
381            subtypes.add(nonAutoEnUS);
382            final InputMethodInfo imi = createDummyInputMethodInfo(
383                    "com.android.apps.inputmethod.latin",
384                    "com.android.apps.inputmethod.latin", "DummyLatinIme", !IS_AUX, IS_DEFAULT,
385                    subtypes);
386            final ArrayList<InputMethodSubtype> result =
387                    callGetImplicitlyApplicableSubtypesLockedWithLocale(LOCALE_ID, imi);
388            assertEquals(1, result.size());
389            verifyEquality(nonAutoIn, result.get(0));
390        }
391        {
392            final ArrayList<InputMethodSubtype> subtypes = new ArrayList<InputMethodSubtype>();
393            subtypes.add(nonAutoId);
394            subtypes.add(nonAutoEnUS);
395            final InputMethodInfo imi = createDummyInputMethodInfo(
396                    "com.android.apps.inputmethod.latin",
397                    "com.android.apps.inputmethod.latin", "DummyLatinIme", !IS_AUX, IS_DEFAULT,
398                    subtypes);
399            final ArrayList<InputMethodSubtype> result =
400                    callGetImplicitlyApplicableSubtypesLockedWithLocale(LOCALE_IN, imi);
401            assertEquals(1, result.size());
402            verifyEquality(nonAutoId, result.get(0));
403        }
404        {
405            final ArrayList<InputMethodSubtype> subtypes = new ArrayList<InputMethodSubtype>();
406            subtypes.add(nonAutoId);
407            subtypes.add(nonAutoEnUS);
408            final InputMethodInfo imi = createDummyInputMethodInfo(
409                    "com.android.apps.inputmethod.latin",
410                    "com.android.apps.inputmethod.latin", "DummyLatinIme", !IS_AUX, IS_DEFAULT,
411                    subtypes);
412            final ArrayList<InputMethodSubtype> result =
413                    callGetImplicitlyApplicableSubtypesLockedWithLocale(LOCALE_ID, imi);
414            assertEquals(1, result.size());
415            verifyEquality(nonAutoId, result.get(0));
416        }
417    }
418
419    @SmallTest
420    public void testContainsSubtypeOf() throws Exception {
421        final InputMethodSubtype nonAutoEnUS = createDummyInputMethodSubtype("en_US",
422                SUBTYPE_MODE_KEYBOARD, !IS_AUX, !IS_OVERRIDES_IMPLICITLY_ENABLED_SUBTYPE,
423                IS_ASCII_CAPABLE, !IS_ENABLED_WHEN_DEFAULT_IS_NOT_ASCII_CAPABLE);
424        final InputMethodSubtype nonAutoEnGB = createDummyInputMethodSubtype("en_GB",
425                SUBTYPE_MODE_KEYBOARD, !IS_AUX, !IS_OVERRIDES_IMPLICITLY_ENABLED_SUBTYPE,
426                IS_ASCII_CAPABLE, IS_ENABLED_WHEN_DEFAULT_IS_NOT_ASCII_CAPABLE);
427        final InputMethodSubtype nonAutoFil = createDummyInputMethodSubtype("fil",
428                SUBTYPE_MODE_KEYBOARD, !IS_AUX, !IS_OVERRIDES_IMPLICITLY_ENABLED_SUBTYPE,
429                IS_ASCII_CAPABLE, !IS_ENABLED_WHEN_DEFAULT_IS_NOT_ASCII_CAPABLE);
430        final InputMethodSubtype nonAutoFilPH = createDummyInputMethodSubtype("fil_PH",
431                SUBTYPE_MODE_KEYBOARD, !IS_AUX, !IS_OVERRIDES_IMPLICITLY_ENABLED_SUBTYPE,
432                IS_ASCII_CAPABLE, !IS_ENABLED_WHEN_DEFAULT_IS_NOT_ASCII_CAPABLE);
433        final InputMethodSubtype nonAutoIn = createDummyInputMethodSubtype("in",
434                SUBTYPE_MODE_KEYBOARD, !IS_AUX, !IS_OVERRIDES_IMPLICITLY_ENABLED_SUBTYPE,
435                IS_ASCII_CAPABLE, IS_ENABLED_WHEN_DEFAULT_IS_NOT_ASCII_CAPABLE);
436        final InputMethodSubtype nonAutoId = createDummyInputMethodSubtype("id",
437                SUBTYPE_MODE_KEYBOARD, !IS_AUX, !IS_OVERRIDES_IMPLICITLY_ENABLED_SUBTYPE,
438                IS_ASCII_CAPABLE, IS_ENABLED_WHEN_DEFAULT_IS_NOT_ASCII_CAPABLE);
439
440        final boolean CHECK_COUNTRY = true;
441
442        {
443            final ArrayList<InputMethodSubtype> subtypes = new ArrayList<InputMethodSubtype>();
444            subtypes.add(nonAutoEnUS);
445            final InputMethodInfo imi = createDummyInputMethodInfo(
446                    "com.android.apps.inputmethod.latin",
447                    "com.android.apps.inputmethod.latin", "DummyLatinIme", !IS_AUX, IS_DEFAULT,
448                    subtypes);
449
450            assertTrue(InputMethodUtils.containsSubtypeOf(imi, LOCALE_EN, !CHECK_COUNTRY,
451                    SUBTYPE_MODE_KEYBOARD));
452            assertFalse(InputMethodUtils.containsSubtypeOf(imi, LOCALE_EN, CHECK_COUNTRY,
453                    SUBTYPE_MODE_KEYBOARD));
454            assertTrue(InputMethodUtils.containsSubtypeOf(imi, LOCALE_EN_US, !CHECK_COUNTRY,
455                    SUBTYPE_MODE_KEYBOARD));
456            assertTrue(InputMethodUtils.containsSubtypeOf(imi, LOCALE_EN_US, CHECK_COUNTRY,
457                    SUBTYPE_MODE_KEYBOARD));
458            assertFalse(InputMethodUtils.containsSubtypeOf(imi, LOCALE_EN_US, !CHECK_COUNTRY,
459                    SUBTYPE_MODE_VOICE));
460            assertFalse(InputMethodUtils.containsSubtypeOf(imi, LOCALE_EN_US, CHECK_COUNTRY,
461                    SUBTYPE_MODE_VOICE));
462            assertTrue(InputMethodUtils.containsSubtypeOf(imi, LOCALE_EN_US, !CHECK_COUNTRY,
463                    SUBTYPE_MODE_ANY));
464            assertTrue(InputMethodUtils.containsSubtypeOf(imi, LOCALE_EN_US, CHECK_COUNTRY,
465                    SUBTYPE_MODE_ANY));
466
467            assertTrue(InputMethodUtils.containsSubtypeOf(imi, LOCALE_EN_GB, !CHECK_COUNTRY,
468                    SUBTYPE_MODE_KEYBOARD));
469            assertFalse(InputMethodUtils.containsSubtypeOf(imi, LOCALE_EN_GB, CHECK_COUNTRY,
470                    SUBTYPE_MODE_KEYBOARD));
471        }
472
473        // Make sure that 3-letter language code ("fil") can be handled.
474        {
475            final ArrayList<InputMethodSubtype> subtypes = new ArrayList<InputMethodSubtype>();
476            subtypes.add(nonAutoFil);
477            final InputMethodInfo imi = createDummyInputMethodInfo(
478                    "com.android.apps.inputmethod.latin",
479                    "com.android.apps.inputmethod.latin", "DummyLatinIme", !IS_AUX, IS_DEFAULT,
480                    subtypes);
481            assertTrue(InputMethodUtils.containsSubtypeOf(imi, LOCALE_FIL, !CHECK_COUNTRY,
482                    SUBTYPE_MODE_KEYBOARD));
483            assertTrue(InputMethodUtils.containsSubtypeOf(imi, LOCALE_FIL, CHECK_COUNTRY,
484                    SUBTYPE_MODE_KEYBOARD));
485            assertTrue(InputMethodUtils.containsSubtypeOf(imi, LOCALE_FIL_PH, !CHECK_COUNTRY,
486                    SUBTYPE_MODE_KEYBOARD));
487            assertFalse(InputMethodUtils.containsSubtypeOf(imi, LOCALE_FIL_PH, CHECK_COUNTRY,
488                    SUBTYPE_MODE_KEYBOARD));
489
490            assertFalse(InputMethodUtils.containsSubtypeOf(imi, LOCALE_FI, !CHECK_COUNTRY,
491                    SUBTYPE_MODE_KEYBOARD));
492            assertFalse(InputMethodUtils.containsSubtypeOf(imi, LOCALE_FI, CHECK_COUNTRY,
493                    SUBTYPE_MODE_KEYBOARD));
494            assertFalse(InputMethodUtils.containsSubtypeOf(imi, LOCALE_FI_FI, !CHECK_COUNTRY,
495                    SUBTYPE_MODE_KEYBOARD));
496            assertFalse(InputMethodUtils.containsSubtypeOf(imi, LOCALE_FI_FI, CHECK_COUNTRY,
497                    SUBTYPE_MODE_KEYBOARD));
498        }
499
500        // Make sure that 3-letter language code ("fil_PH") can be handled.
501        {
502            final ArrayList<InputMethodSubtype> subtypes = new ArrayList<InputMethodSubtype>();
503            subtypes.add(nonAutoFilPH);
504            final InputMethodInfo imi = createDummyInputMethodInfo(
505                    "com.android.apps.inputmethod.latin",
506                    "com.android.apps.inputmethod.latin", "DummyLatinIme", !IS_AUX, IS_DEFAULT,
507                    subtypes);
508            assertTrue(InputMethodUtils.containsSubtypeOf(imi, LOCALE_FIL, !CHECK_COUNTRY,
509                    SUBTYPE_MODE_KEYBOARD));
510            assertFalse(InputMethodUtils.containsSubtypeOf(imi, LOCALE_FIL, CHECK_COUNTRY,
511                    SUBTYPE_MODE_KEYBOARD));
512            assertTrue(InputMethodUtils.containsSubtypeOf(imi, LOCALE_FIL_PH, !CHECK_COUNTRY,
513                    SUBTYPE_MODE_KEYBOARD));
514            assertTrue(InputMethodUtils.containsSubtypeOf(imi, LOCALE_FIL_PH, CHECK_COUNTRY,
515                    SUBTYPE_MODE_KEYBOARD));
516
517            assertFalse(InputMethodUtils.containsSubtypeOf(imi, LOCALE_FI, !CHECK_COUNTRY,
518                    SUBTYPE_MODE_KEYBOARD));
519            assertFalse(InputMethodUtils.containsSubtypeOf(imi, LOCALE_FI, CHECK_COUNTRY,
520                    SUBTYPE_MODE_KEYBOARD));
521            assertFalse(InputMethodUtils.containsSubtypeOf(imi, LOCALE_FI_FI, !CHECK_COUNTRY,
522                    SUBTYPE_MODE_KEYBOARD));
523            assertFalse(InputMethodUtils.containsSubtypeOf(imi, LOCALE_FI_FI, CHECK_COUNTRY,
524                    SUBTYPE_MODE_KEYBOARD));
525        }
526
527        // Make sure that a subtype whose locale is "in" can be queried with "id".
528        {
529            final ArrayList<InputMethodSubtype> subtypes = new ArrayList<InputMethodSubtype>();
530            subtypes.add(nonAutoIn);
531            subtypes.add(nonAutoEnUS);
532            final InputMethodInfo imi = createDummyInputMethodInfo(
533                    "com.android.apps.inputmethod.latin",
534                    "com.android.apps.inputmethod.latin", "DummyLatinIme", !IS_AUX, IS_DEFAULT,
535                    subtypes);
536            assertTrue(InputMethodUtils.containsSubtypeOf(imi, LOCALE_IN, !CHECK_COUNTRY,
537                    SUBTYPE_MODE_KEYBOARD));
538            assertTrue(InputMethodUtils.containsSubtypeOf(imi, LOCALE_IN, CHECK_COUNTRY,
539                    SUBTYPE_MODE_KEYBOARD));
540            assertTrue(InputMethodUtils.containsSubtypeOf(imi, LOCALE_ID, !CHECK_COUNTRY,
541                    SUBTYPE_MODE_KEYBOARD));
542            assertTrue(InputMethodUtils.containsSubtypeOf(imi, LOCALE_ID, CHECK_COUNTRY,
543                    SUBTYPE_MODE_KEYBOARD));
544        }
545
546        // Make sure that a subtype whose locale is "id" can be queried with "in".
547        {
548            final ArrayList<InputMethodSubtype> subtypes = new ArrayList<InputMethodSubtype>();
549            subtypes.add(nonAutoId);
550            subtypes.add(nonAutoEnUS);
551            final InputMethodInfo imi = createDummyInputMethodInfo(
552                    "com.android.apps.inputmethod.latin",
553                    "com.android.apps.inputmethod.latin", "DummyLatinIme", !IS_AUX, IS_DEFAULT,
554                    subtypes);
555            assertTrue(InputMethodUtils.containsSubtypeOf(imi, LOCALE_IN, !CHECK_COUNTRY,
556                    SUBTYPE_MODE_KEYBOARD));
557            assertTrue(InputMethodUtils.containsSubtypeOf(imi, LOCALE_IN, CHECK_COUNTRY,
558                    SUBTYPE_MODE_KEYBOARD));
559            assertTrue(InputMethodUtils.containsSubtypeOf(imi, LOCALE_ID, !CHECK_COUNTRY,
560                    SUBTYPE_MODE_KEYBOARD));
561            assertTrue(InputMethodUtils.containsSubtypeOf(imi, LOCALE_ID, CHECK_COUNTRY,
562                    SUBTYPE_MODE_KEYBOARD));
563        }
564    }
565
566    private ArrayList<InputMethodSubtype> callGetImplicitlyApplicableSubtypesLockedWithLocale(
567            final Locale locale, final InputMethodInfo imi) {
568        final Context context = getInstrumentation().getTargetContext();
569        final Locale initialLocale = context.getResources().getConfiguration().locale;
570        try {
571            context.getResources().getConfiguration().setLocale(locale);
572            return InputMethodUtils.getImplicitlyApplicableSubtypesLocked(context.getResources(),
573                    imi);
574        } finally {
575            context.getResources().getConfiguration().setLocale(initialLocale);
576        }
577    }
578
579    private void assertDefaultEnabledImes(final ArrayList<InputMethodInfo> preinstalledImes,
580            final Locale systemLocale, final boolean isSystemReady, String... expectedImeNames) {
581        final Context context = getInstrumentation().getTargetContext();
582        final String[] actualImeNames = getPackageNames(callGetDefaultEnabledImesWithLocale(
583                context, isSystemReady, preinstalledImes, systemLocale));
584        assertEquals(expectedImeNames.length, actualImeNames.length);
585        for (int i = 0; i < expectedImeNames.length; ++i) {
586            assertEquals(expectedImeNames[i], actualImeNames[i]);
587        }
588    }
589
590    private static List<InputMethodInfo> cloneViaParcel(final List<InputMethodInfo> list) {
591        Parcel p = null;
592        try {
593            p = Parcel.obtain();
594            p.writeTypedList(list);
595            p.setDataPosition(0);
596            return p.createTypedArrayList(InputMethodInfo.CREATOR);
597        } finally {
598            if (p != null) {
599                p.recycle();
600            }
601        }
602    }
603
604    private static ArrayList<InputMethodInfo> callGetDefaultEnabledImesWithLocale(
605            final Context context, final boolean isSystemReady,
606            final ArrayList<InputMethodInfo> imis, final Locale locale) {
607        final Locale initialLocale = context.getResources().getConfiguration().locale;
608        try {
609            context.getResources().getConfiguration().setLocale(locale);
610            return InputMethodUtils.getDefaultEnabledImes(context, isSystemReady, imis);
611        } finally {
612            context.getResources().getConfiguration().setLocale(initialLocale);
613        }
614    }
615
616    private String[] getPackageNames(final ArrayList<InputMethodInfo> imis) {
617        final String[] packageNames = new String[imis.size()];
618        for (int i = 0; i < imis.size(); ++i) {
619            packageNames[i] = imis.get(i).getPackageName();
620        }
621        return packageNames;
622    }
623
624    private static void verifyEquality(InputMethodInfo expected, InputMethodInfo actual) {
625        assertEquals(expected, actual);
626        assertEquals(expected.getSubtypeCount(), actual.getSubtypeCount());
627        for (int subtypeIndex = 0; subtypeIndex < expected.getSubtypeCount(); ++subtypeIndex) {
628            final InputMethodSubtype expectedSubtype = expected.getSubtypeAt(subtypeIndex);
629            final InputMethodSubtype actualSubtype = actual.getSubtypeAt(subtypeIndex);
630            verifyEquality(expectedSubtype, actualSubtype);
631        }
632    }
633
634    private static void verifyEquality(InputMethodSubtype expected, InputMethodSubtype actual) {
635        assertEquals(expected, actual);
636        assertEquals(expected.hashCode(), actual.hashCode());
637    }
638
639    private static InputMethodInfo createDummyInputMethodInfo(String packageName, String name,
640            CharSequence label, boolean isAuxIme, boolean isDefault,
641            List<InputMethodSubtype> subtypes) {
642        final ResolveInfo ri = new ResolveInfo();
643        final ServiceInfo si = new ServiceInfo();
644        final ApplicationInfo ai = new ApplicationInfo();
645        ai.packageName = packageName;
646        ai.enabled = true;
647        ai.flags |= ApplicationInfo.FLAG_SYSTEM;
648        si.applicationInfo = ai;
649        si.enabled = true;
650        si.packageName = packageName;
651        si.name = name;
652        si.exported = true;
653        si.nonLocalizedLabel = label;
654        ri.serviceInfo = si;
655        return new InputMethodInfo(ri, isAuxIme, "", subtypes, 1, isDefault);
656    }
657
658    private static InputMethodSubtype createDummyInputMethodSubtype(String locale, String mode,
659            boolean isAuxiliary, boolean overridesImplicitlyEnabledSubtype,
660            boolean isAsciiCapable, boolean isEnabledWhenDefaultIsNotAsciiCapable) {
661
662        final StringBuilder subtypeExtraValue = new StringBuilder();
663        if (isEnabledWhenDefaultIsNotAsciiCapable) {
664            subtypeExtraValue.append(EXTRA_VALUE_PAIR_SEPARATOR);
665            subtypeExtraValue.append(EXTRA_VALUE_ENABLED_WHEN_DEFAULT_IS_NOT_ASCII_CAPABLE);
666        }
667
668        // TODO: Remove following code. InputMethodSubtype#isAsciiCapable() has been publicly
669        // available since API level 19 (KitKat). We no longer need to rely on extra value.
670        if (isAsciiCapable) {
671            subtypeExtraValue.append(EXTRA_VALUE_PAIR_SEPARATOR);
672            subtypeExtraValue.append(EXTRA_VALUE_ASCII_CAPABLE);
673        }
674
675        return new InputMethodSubtypeBuilder()
676                .setSubtypeNameResId(0)
677                .setSubtypeIconResId(0)
678                .setSubtypeLocale(locale)
679                .setSubtypeMode(mode)
680                .setSubtypeExtraValue(subtypeExtraValue.toString())
681                .setIsAuxiliary(isAuxiliary)
682                .setOverridesImplicitlyEnabledSubtype(overridesImplicitlyEnabledSubtype)
683                .setIsAsciiCapable(isAsciiCapable)
684                .build();
685    }
686
687    private static ArrayList<InputMethodInfo> getImesWithDefaultVoiceIme() {
688        ArrayList<InputMethodInfo> preinstalledImes = new ArrayList<>();
689        {
690            final ArrayList<InputMethodSubtype> subtypes = new ArrayList<InputMethodSubtype>();
691            subtypes.add(createDummyInputMethodSubtype("auto", SUBTYPE_MODE_VOICE, IS_AUX,
692                    IS_OVERRIDES_IMPLICITLY_ENABLED_SUBTYPE, !IS_ASCII_CAPABLE,
693                    !IS_ENABLED_WHEN_DEFAULT_IS_NOT_ASCII_CAPABLE));
694            subtypes.add(createDummyInputMethodSubtype("en_US", SUBTYPE_MODE_VOICE, IS_AUX,
695                    !IS_OVERRIDES_IMPLICITLY_ENABLED_SUBTYPE, !IS_ASCII_CAPABLE,
696                    !IS_ENABLED_WHEN_DEFAULT_IS_NOT_ASCII_CAPABLE));
697            preinstalledImes.add(createDummyInputMethodInfo("DummyDefaultAutoVoiceIme",
698                    "dummy.voice0", "DummyVoice0", IS_AUX, IS_DEFAULT, subtypes));
699        }
700        preinstalledImes.addAll(getImesWithoutDefaultVoiceIme());
701        return preinstalledImes;
702    }
703
704    private static ArrayList<InputMethodInfo> getImesWithoutDefaultVoiceIme() {
705        ArrayList<InputMethodInfo> preinstalledImes = new ArrayList<>();
706        {
707            final ArrayList<InputMethodSubtype> subtypes = new ArrayList<InputMethodSubtype>();
708            subtypes.add(createDummyInputMethodSubtype("auto", SUBTYPE_MODE_VOICE, IS_AUX,
709                    IS_OVERRIDES_IMPLICITLY_ENABLED_SUBTYPE, !IS_ASCII_CAPABLE,
710                    !IS_ENABLED_WHEN_DEFAULT_IS_NOT_ASCII_CAPABLE));
711            subtypes.add(createDummyInputMethodSubtype("en_US", SUBTYPE_MODE_VOICE, IS_AUX,
712                    !IS_OVERRIDES_IMPLICITLY_ENABLED_SUBTYPE, !IS_ASCII_CAPABLE,
713                    !IS_ENABLED_WHEN_DEFAULT_IS_NOT_ASCII_CAPABLE));
714            preinstalledImes.add(createDummyInputMethodInfo("DummyNonDefaultAutoVoiceIme0",
715                    "dummy.voice1", "DummyVoice1", IS_AUX, !IS_DEFAULT, subtypes));
716        }
717        {
718            final ArrayList<InputMethodSubtype> subtypes = new ArrayList<InputMethodSubtype>();
719            subtypes.add(createDummyInputMethodSubtype("auto", SUBTYPE_MODE_VOICE, IS_AUX,
720                    IS_OVERRIDES_IMPLICITLY_ENABLED_SUBTYPE, !IS_ASCII_CAPABLE,
721                    !IS_ENABLED_WHEN_DEFAULT_IS_NOT_ASCII_CAPABLE));
722            subtypes.add(createDummyInputMethodSubtype("en_US", SUBTYPE_MODE_VOICE, IS_AUX,
723                    !IS_OVERRIDES_IMPLICITLY_ENABLED_SUBTYPE, !IS_ASCII_CAPABLE,
724                    !IS_ENABLED_WHEN_DEFAULT_IS_NOT_ASCII_CAPABLE));
725            preinstalledImes.add(createDummyInputMethodInfo("DummyNonDefaultAutoVoiceIme1",
726                    "dummy.voice2", "DummyVoice2", IS_AUX, !IS_DEFAULT, subtypes));
727        }
728        {
729            final ArrayList<InputMethodSubtype> subtypes = new ArrayList<InputMethodSubtype>();
730            subtypes.add(createDummyInputMethodSubtype("en_US", SUBTYPE_MODE_VOICE, IS_AUX,
731                    !IS_OVERRIDES_IMPLICITLY_ENABLED_SUBTYPE, !IS_ASCII_CAPABLE,
732                    !IS_ENABLED_WHEN_DEFAULT_IS_NOT_ASCII_CAPABLE));
733            preinstalledImes.add(createDummyInputMethodInfo("DummyNonDefaultVoiceIme2",
734                    "dummy.voice3", "DummyVoice3", IS_AUX, !IS_DEFAULT, subtypes));
735        }
736        {
737            final ArrayList<InputMethodSubtype> subtypes = new ArrayList<InputMethodSubtype>();
738            subtypes.add(createDummyInputMethodSubtype("en_US", SUBTYPE_MODE_KEYBOARD, !IS_AUX,
739                    !IS_OVERRIDES_IMPLICITLY_ENABLED_SUBTYPE, IS_ASCII_CAPABLE,
740                    !IS_ENABLED_WHEN_DEFAULT_IS_NOT_ASCII_CAPABLE));
741            preinstalledImes.add(createDummyInputMethodInfo("DummyDefaultEnKeyboardIme",
742                    "dummy.keyboard0", "DummyKeyboard0", !IS_AUX, IS_DEFAULT, subtypes));
743        }
744        return preinstalledImes;
745    }
746
747    private static boolean contains(final String[] textList, final String textToBeChecked) {
748        if (textList == null) {
749            return false;
750        }
751        for (final String text : textList) {
752            if (Objects.equals(textToBeChecked, text)) {
753                return true;
754            }
755        }
756        return false;
757    }
758
759    private static ArrayList<InputMethodInfo> getSamplePreinstalledImes(final String localeString) {
760        ArrayList<InputMethodInfo> preinstalledImes = new ArrayList<>();
761
762        // a dummy Voice IME
763        {
764            final boolean isDefaultIme = false;
765            final ArrayList<InputMethodSubtype> subtypes = new ArrayList<InputMethodSubtype>();
766            subtypes.add(createDummyInputMethodSubtype("", SUBTYPE_MODE_VOICE, IS_AUX,
767                    IS_OVERRIDES_IMPLICITLY_ENABLED_SUBTYPE, !IS_ASCII_CAPABLE,
768                    !IS_ENABLED_WHEN_DEFAULT_IS_NOT_ASCII_CAPABLE));
769            preinstalledImes.add(createDummyInputMethodInfo("com.android.apps.inputmethod.voice",
770                    "com.android.inputmethod.voice", "DummyVoiceIme", IS_AUX, isDefaultIme,
771                    subtypes));
772        }
773        // a dummy Hindi IME
774        {
775            final boolean isDefaultIme = contains(new String[]{ "hi", "en-rIN" }, localeString);
776            final ArrayList<InputMethodSubtype> subtypes = new ArrayList<InputMethodSubtype>();
777            // TODO: This subtype should be marked as IS_ASCII_CAPABLE
778            subtypes.add(createDummyInputMethodSubtype("en_IN", SUBTYPE_MODE_KEYBOARD, !IS_AUX,
779                    !IS_OVERRIDES_IMPLICITLY_ENABLED_SUBTYPE, !IS_ASCII_CAPABLE,
780                    !IS_ENABLED_WHEN_DEFAULT_IS_NOT_ASCII_CAPABLE));
781            subtypes.add(createDummyInputMethodSubtype("hi", SUBTYPE_MODE_KEYBOARD, !IS_AUX,
782                    !IS_OVERRIDES_IMPLICITLY_ENABLED_SUBTYPE, !IS_ASCII_CAPABLE,
783                    !IS_ENABLED_WHEN_DEFAULT_IS_NOT_ASCII_CAPABLE));
784            preinstalledImes.add(createDummyInputMethodInfo("com.android.apps.inputmethod.hindi",
785                    "com.android.inputmethod.hindi", "DummyHindiIme", !IS_AUX, isDefaultIme,
786                    subtypes));
787        }
788
789        // a dummy Pinyin IME
790        {
791            final boolean isDefaultIme = contains(new String[]{ "zh-rCN" }, localeString);
792            final ArrayList<InputMethodSubtype> subtypes = new ArrayList<InputMethodSubtype>();
793            subtypes.add(createDummyInputMethodSubtype("zh_CN", SUBTYPE_MODE_KEYBOARD, !IS_AUX,
794                    !IS_OVERRIDES_IMPLICITLY_ENABLED_SUBTYPE, !IS_ASCII_CAPABLE,
795                    !IS_ENABLED_WHEN_DEFAULT_IS_NOT_ASCII_CAPABLE));
796            preinstalledImes.add(createDummyInputMethodInfo("com.android.apps.inputmethod.pinyin",
797                    "com.android.apps.inputmethod.pinyin", "DummyPinyinIme", !IS_AUX, isDefaultIme,
798                    subtypes));
799        }
800
801        // a dummy Korean IME
802        {
803            final boolean isDefaultIme = contains(new String[]{ "ko" }, localeString);
804            final ArrayList<InputMethodSubtype> subtypes = new ArrayList<InputMethodSubtype>();
805            subtypes.add(createDummyInputMethodSubtype("ko", SUBTYPE_MODE_KEYBOARD, !IS_AUX,
806                    !IS_OVERRIDES_IMPLICITLY_ENABLED_SUBTYPE, !IS_ASCII_CAPABLE,
807                    !IS_ENABLED_WHEN_DEFAULT_IS_NOT_ASCII_CAPABLE));
808            preinstalledImes.add(createDummyInputMethodInfo("com.android.apps.inputmethod.korean",
809                    "com.android.apps.inputmethod.korean", "DummyKoreanIme", !IS_AUX, isDefaultIme,
810                    subtypes));
811        }
812
813        // a dummy Latin IME
814        {
815            final boolean isDefaultIme = contains(
816                    new String[]{ "en-rUS", "en-rGB", "en-rIN", "en", "hi" }, localeString);
817            final ArrayList<InputMethodSubtype> subtypes = new ArrayList<InputMethodSubtype>();
818            subtypes.add(createDummyInputMethodSubtype("en_US", SUBTYPE_MODE_KEYBOARD, !IS_AUX,
819                    !IS_OVERRIDES_IMPLICITLY_ENABLED_SUBTYPE, IS_ASCII_CAPABLE,
820                    !IS_ENABLED_WHEN_DEFAULT_IS_NOT_ASCII_CAPABLE));
821            subtypes.add(createDummyInputMethodSubtype("en_GB", SUBTYPE_MODE_KEYBOARD, !IS_AUX,
822                    !IS_OVERRIDES_IMPLICITLY_ENABLED_SUBTYPE, IS_ASCII_CAPABLE,
823                    !IS_ENABLED_WHEN_DEFAULT_IS_NOT_ASCII_CAPABLE));
824            subtypes.add(createDummyInputMethodSubtype("en_IN", SUBTYPE_MODE_KEYBOARD, !IS_AUX,
825                    !IS_OVERRIDES_IMPLICITLY_ENABLED_SUBTYPE, IS_ASCII_CAPABLE,
826                    !IS_ENABLED_WHEN_DEFAULT_IS_NOT_ASCII_CAPABLE));
827            subtypes.add(createDummyInputMethodSubtype("hi", SUBTYPE_MODE_KEYBOARD, !IS_AUX,
828                    !IS_OVERRIDES_IMPLICITLY_ENABLED_SUBTYPE, IS_ASCII_CAPABLE,
829                    !IS_ENABLED_WHEN_DEFAULT_IS_NOT_ASCII_CAPABLE));
830            preinstalledImes.add(createDummyInputMethodInfo("com.android.apps.inputmethod.latin",
831                    "com.android.apps.inputmethod.latin", "DummyLatinIme", !IS_AUX, isDefaultIme,
832                    subtypes));
833        }
834
835        // a dummy Japanese IME
836        {
837            final boolean isDefaultIme = contains(new String[]{ "ja", "ja-rJP" }, localeString);
838            final ArrayList<InputMethodSubtype> subtypes = new ArrayList<InputMethodSubtype>();
839            subtypes.add(createDummyInputMethodSubtype("ja", SUBTYPE_MODE_KEYBOARD, !IS_AUX,
840                    !IS_OVERRIDES_IMPLICITLY_ENABLED_SUBTYPE, !IS_ASCII_CAPABLE,
841                    !IS_ENABLED_WHEN_DEFAULT_IS_NOT_ASCII_CAPABLE));
842            subtypes.add(createDummyInputMethodSubtype("emoji", SUBTYPE_MODE_KEYBOARD, !IS_AUX,
843                    !IS_OVERRIDES_IMPLICITLY_ENABLED_SUBTYPE, !IS_ASCII_CAPABLE,
844                    !IS_ENABLED_WHEN_DEFAULT_IS_NOT_ASCII_CAPABLE));
845            preinstalledImes.add(createDummyInputMethodInfo("com.android.apps.inputmethod.japanese",
846                    "com.android.apps.inputmethod.japanese", "DummyJapaneseIme", !IS_AUX,
847                    isDefaultIme, subtypes));
848        }
849
850        return preinstalledImes;
851    }
852}
853