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