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