InputMethodTest.java revision cfcbddaff269cbb35192c21e557d34aece2f3d6c
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 com.android.internal.inputmethod.InputMethodUtils;
20
21import android.content.Context;
22import android.content.pm.ApplicationInfo;
23import android.content.pm.ResolveInfo;
24import android.content.pm.ServiceInfo;
25import android.test.InstrumentationTestCase;
26import android.test.suitebuilder.annotation.SmallTest;
27import android.view.inputmethod.InputMethodInfo;
28import android.view.inputmethod.InputMethodSubtype;
29import android.view.inputmethod.InputMethodSubtype.InputMethodSubtypeBuilder;
30
31import java.util.ArrayList;
32import java.util.List;
33
34public class InputMethodTest extends InstrumentationTestCase {
35    private static final boolean IS_AUX = true;
36    private static final boolean IS_DEFAULT = true;
37    private static final boolean IS_AUTO = true;
38    private static final boolean IS_ASCII_CAPABLE = true;
39    private static final ArrayList<InputMethodSubtype> NO_SUBTYPE = null;
40
41    @SmallTest
42    public void testDefaultEnabledImesWithDefaultVoiceIme() throws Exception {
43        final Context context = getInstrumentation().getTargetContext();
44        final ArrayList<InputMethodInfo> imis = new ArrayList<InputMethodInfo>();
45        imis.add(createDefaultAutoDummyVoiceIme());
46        imis.add(createNonDefaultAutoDummyVoiceIme0());
47        imis.add(createNonDefaultAutoDummyVoiceIme1());
48        imis.add(createNonDefaultDummyVoiceIme2());
49        imis.add(createDefaultDummyLatinKeyboardIme());
50        imis.add(createNonDefaultDummyJaJPKeyboardIme());
51        imis.add(createNonDefaultDummyJaJPKeyboardImeWithoutSubtypes());
52        final ArrayList<InputMethodInfo> enabledImis = InputMethodUtils.getDefaultEnabledImes(
53                context, true, imis);
54        assertEquals(2, enabledImis.size());
55        for (int i = 0; i < enabledImis.size(); ++i) {
56            final InputMethodInfo imi = enabledImis.get(0);
57            // "DummyDefaultAutoVoiceIme" and "DummyDefaultEnKeyboardIme"
58            if (imi.getPackageName().equals("DummyDefaultAutoVoiceIme")
59                    || imi.getPackageName().equals("DummyDefaultEnKeyboardIme")) {
60                continue;
61            } else {
62                fail("Invalid enabled subtype.");
63            }
64        }
65    }
66
67    @SmallTest
68    public void testDefaultEnabledImesWithOutDefaultVoiceIme() throws Exception {
69        final Context context = getInstrumentation().getTargetContext();
70        final ArrayList<InputMethodInfo> imis = new ArrayList<InputMethodInfo>();
71        imis.add(createNonDefaultAutoDummyVoiceIme0());
72        imis.add(createNonDefaultAutoDummyVoiceIme1());
73        imis.add(createNonDefaultDummyVoiceIme2());
74        imis.add(createDefaultDummyLatinKeyboardIme());
75        imis.add(createNonDefaultDummyJaJPKeyboardIme());
76        imis.add(createNonDefaultDummyJaJPKeyboardImeWithoutSubtypes());
77        final ArrayList<InputMethodInfo> enabledImis = InputMethodUtils.getDefaultEnabledImes(
78                context, true, imis);
79        assertEquals(3, enabledImis.size());
80        for (int i = 0; i < enabledImis.size(); ++i) {
81            final InputMethodInfo imi = enabledImis.get(0);
82            // "DummyNonDefaultAutoVoiceIme0", "DummyNonDefaultAutoVoiceIme1" and
83            // "DummyDefaultEnKeyboardIme"
84            if (imi.getPackageName().equals("DummyNonDefaultAutoVoiceIme0")
85                    || imi.getPackageName().equals("DummyNonDefaultAutoVoiceIme1")
86                    || imi.getPackageName().equals("DummyDefaultEnKeyboardIme")) {
87                continue;
88            } else {
89                fail("Invalid enabled subtype.");
90            }
91        }
92    }
93
94    @SmallTest
95    public void testParcelable() throws Exception {
96        final ArrayList<InputMethodInfo> originalList = new ArrayList<InputMethodInfo>();
97        originalList.add(createNonDefaultAutoDummyVoiceIme0());
98        originalList.add(createNonDefaultAutoDummyVoiceIme1());
99        originalList.add(createNonDefaultDummyVoiceIme2());
100        originalList.add(createDefaultDummyLatinKeyboardIme());
101        originalList.add(createNonDefaultDummyJaJPKeyboardIme());
102        originalList.add(createNonDefaultDummyJaJPKeyboardImeWithoutSubtypes());
103
104        final List<InputMethodInfo> clonedList = cloneViaParcel(originalList);
105        assertNotNull(clonedList);
106        final List<InputMethodInfo> clonedClonedList = cloneViaParcel(clonedList);
107        assertNotNull(clonedClonedList);
108        assertEquals(originalList, clonedList);
109        assertEquals(clonedList, clonedClonedList);
110        assertEquals(originalList.size(), clonedList.size());
111        assertEquals(clonedList.size(), clonedClonedList.size());
112        for (int imeIndex = 0; imeIndex < originalList.size(); ++imeIndex) {
113            verifyEquality(originalList.get(imeIndex), clonedList.get(imeIndex));
114            verifyEquality(clonedList.get(imeIndex), clonedClonedList.get(imeIndex));
115        }
116    }
117
118    private static List<InputMethodInfo> cloneViaParcel(final List<InputMethodInfo> list) {
119        Parcel p = null;
120        try {
121            p = Parcel.obtain();
122            p.writeTypedList(list);
123            p.setDataPosition(0);
124            return p.createTypedArrayList(InputMethodInfo.CREATOR);
125        } finally {
126            if (p != null) {
127                p.recycle();
128            }
129        }
130    }
131
132    private static void verifyEquality(InputMethodInfo expected, InputMethodInfo actual) {
133        assertEquals(expected, actual);
134        assertEquals(expected.getSubtypeCount(), actual.getSubtypeCount());
135        for (int subtypeIndex = 0; subtypeIndex < expected.getSubtypeCount(); ++subtypeIndex) {
136            final InputMethodSubtype expectedSubtype = expected.getSubtypeAt(subtypeIndex);
137            final InputMethodSubtype actualSubtype = actual.getSubtypeAt(subtypeIndex);
138            assertEquals(expectedSubtype, actualSubtype);
139            assertEquals(expectedSubtype.hashCode(), actualSubtype.hashCode());
140        }
141    }
142
143    private static InputMethodInfo createDummyInputMethodInfo(String packageName, String name,
144            CharSequence label, boolean isAuxIme, boolean isDefault,
145            List<InputMethodSubtype> subtypes) {
146        final ResolveInfo ri = new ResolveInfo();
147        final ServiceInfo si = new ServiceInfo();
148        final ApplicationInfo ai = new ApplicationInfo();
149        ai.packageName = packageName;
150        ai.enabled = true;
151        ai.flags |= ApplicationInfo.FLAG_SYSTEM;
152        si.applicationInfo = ai;
153        si.enabled = true;
154        si.packageName = packageName;
155        si.name = name;
156        si.exported = true;
157        si.nonLocalizedLabel = label;
158        ri.serviceInfo = si;
159        return new InputMethodInfo(ri, isAuxIme, "", subtypes, 1, isDefault);
160    }
161
162    private static InputMethodSubtype createDummyInputMethodSubtype(String locale, String mode,
163            boolean isAuxiliary, boolean overridesImplicitlyEnabledSubtype,
164            boolean isAsciiCapable) {
165        return new InputMethodSubtypeBuilder()
166                .setSubtypeNameResId(0)
167                .setSubtypeIconResId(0)
168                .setSubtypeLocale(locale)
169                .setSubtypeMode(mode)
170                .setSubtypeExtraValue("")
171                .setIsAuxiliary(isAuxiliary)
172                .setOverridesImplicitlyEnabledSubtype(overridesImplicitlyEnabledSubtype)
173                .setIsAsciiCapable(isAsciiCapable)
174                .build();
175    }
176
177    private static InputMethodInfo createDefaultAutoDummyVoiceIme() {
178        final ArrayList<InputMethodSubtype> subtypes = new ArrayList<InputMethodSubtype>();
179        subtypes.add(createDummyInputMethodSubtype("auto", "voice", IS_AUX, IS_AUTO,
180                !IS_ASCII_CAPABLE));
181        subtypes.add(createDummyInputMethodSubtype("en_US", "voice", IS_AUX, !IS_AUTO,
182                !IS_ASCII_CAPABLE));
183        return createDummyInputMethodInfo("DummyDefaultAutoVoiceIme", "dummy.voice0",
184                "DummyVoice0", IS_AUX, IS_DEFAULT, subtypes);
185    }
186
187    private static InputMethodInfo createNonDefaultAutoDummyVoiceIme0() {
188        final ArrayList<InputMethodSubtype> subtypes = new ArrayList<InputMethodSubtype>();
189        subtypes.add(createDummyInputMethodSubtype("auto", "voice", IS_AUX, IS_AUTO,
190                !IS_ASCII_CAPABLE));
191        subtypes.add(createDummyInputMethodSubtype("en_US", "voice", IS_AUX, !IS_AUTO,
192                !IS_ASCII_CAPABLE));
193        return createDummyInputMethodInfo("DummyNonDefaultAutoVoiceIme0", "dummy.voice1",
194                "DummyVoice1", IS_AUX, !IS_DEFAULT, subtypes);
195    }
196
197    private static InputMethodInfo createNonDefaultAutoDummyVoiceIme1() {
198        final ArrayList<InputMethodSubtype> subtypes = new ArrayList<InputMethodSubtype>();
199        subtypes.add(createDummyInputMethodSubtype("auto", "voice", IS_AUX, IS_AUTO,
200                !IS_ASCII_CAPABLE));
201        subtypes.add(createDummyInputMethodSubtype("en_US", "voice", IS_AUX, !IS_AUTO,
202                !IS_ASCII_CAPABLE));
203        return createDummyInputMethodInfo("DummyNonDefaultAutoVoiceIme1", "dummy.voice2",
204                "DummyVoice2", IS_AUX, !IS_DEFAULT, subtypes);
205    }
206
207    private static InputMethodInfo createNonDefaultDummyVoiceIme2() {
208        final ArrayList<InputMethodSubtype> subtypes = new ArrayList<InputMethodSubtype>();
209        subtypes.add(createDummyInputMethodSubtype("en_US", "voice", IS_AUX, !IS_AUTO,
210                !IS_ASCII_CAPABLE));
211        return createDummyInputMethodInfo("DummyNonDefaultVoiceIme2", "dummy.voice3",
212                "DummyVoice3", IS_AUX, !IS_DEFAULT, subtypes);
213    }
214
215    private static InputMethodInfo createDefaultDummyLatinKeyboardIme() {
216        final ArrayList<InputMethodSubtype> subtypes = new ArrayList<InputMethodSubtype>();
217        subtypes.add(createDummyInputMethodSubtype("en_US", "keyboard", !IS_AUX, !IS_AUTO,
218                IS_ASCII_CAPABLE));
219        subtypes.add(createDummyInputMethodSubtype("en_GB", "keyboard", !IS_AUX, !IS_AUTO,
220                IS_ASCII_CAPABLE));
221        subtypes.add(createDummyInputMethodSubtype("en_IN", "keyboard", !IS_AUX, !IS_AUTO,
222                IS_ASCII_CAPABLE));
223        subtypes.add(createDummyInputMethodSubtype("hi", "keyboard", !IS_AUX, !IS_AUTO,
224                !IS_ASCII_CAPABLE));  // not AsciiCapable!
225        subtypes.add(createDummyInputMethodSubtype("hi_ZZ", "keyboard", !IS_AUX, !IS_AUTO,
226                IS_ASCII_CAPABLE));
227        return createDummyInputMethodInfo("DummyDefaultEnKeyboardIme", "dummy.keyboard0",
228                "DummyKeyboard0", !IS_AUX, IS_DEFAULT, subtypes);
229    }
230
231    private static InputMethodInfo createNonDefaultDummyJaJPKeyboardIme() {
232        final ArrayList<InputMethodSubtype> subtypes = new ArrayList<InputMethodSubtype>();
233        subtypes.add(createDummyInputMethodSubtype("ja_JP", "keyboard", !IS_AUX, !IS_AUTO,
234                IS_ASCII_CAPABLE));
235        return createDummyInputMethodInfo("DummyNonDefaultJaJPKeyboardIme", "dummy.keyboard1",
236                "DummyKeyboard1", !IS_AUX, !IS_DEFAULT, subtypes);
237    }
238
239    // Although IMEs that have no subtype are considered to be deprecated, the Android framework
240    // must still be able to handle such IMEs as well as IMEs that have at least one subtype.
241    private static InputMethodInfo createNonDefaultDummyJaJPKeyboardImeWithoutSubtypes() {
242        final ArrayList<InputMethodSubtype> subtypes = new ArrayList<InputMethodSubtype>();
243        return createDummyInputMethodInfo("DummyNonDefaultJaJPKeyboardImeWithoutSubtypes",
244                "dummy.keyboard2", "DummyKeyboard2", !IS_AUX, !IS_DEFAULT, NO_SUBTYPE);
245    }
246}
247