InputMethodSubtypeSwitchingControllerTest.java revision 71cf0a32d0d9a45653704a671154f26a8bc3132b
1/*
2 * Copyright (C) 2014 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.pm.ApplicationInfo;
20import android.content.pm.ResolveInfo;
21import android.content.pm.ServiceInfo;
22import android.test.InstrumentationTestCase;
23import android.test.suitebuilder.annotation.SmallTest;
24import android.view.inputmethod.InputMethodInfo;
25import android.view.inputmethod.InputMethodSubtype;
26import android.view.inputmethod.InputMethodSubtype.InputMethodSubtypeBuilder;
27
28import com.android.internal.inputmethod.InputMethodSubtypeSwitchingController.ControllerImpl;
29import com.android.internal.inputmethod.InputMethodSubtypeSwitchingController.ImeSubtypeListItem;
30import com.android.internal.inputmethod.InputMethodUtils;
31
32import java.util.ArrayList;
33import java.util.Arrays;
34import java.util.List;
35
36public class InputMethodSubtypeSwitchingControllerTest extends InstrumentationTestCase {
37    private static final String DUMMY_PACKAGE_NAME = "dymmy package name";
38    private static final String DUMMY_SETTING_ACTIVITY_NAME = "";
39    private static final boolean DUMMY_IS_AUX_IME = false;
40    private static final boolean DUMMY_FORCE_DEFAULT = false;
41    private static final int DUMMY_IS_DEFAULT_RES_ID = 0;
42    private static final String SYSTEM_LOCALE = "en_US";
43    private static final int NOT_A_SUBTYPE_ID = InputMethodUtils.NOT_A_SUBTYPE_ID;
44
45    private static InputMethodSubtype createDummySubtype(final String locale) {
46        final InputMethodSubtypeBuilder builder = new InputMethodSubtypeBuilder();
47        return builder.setSubtypeNameResId(0)
48                .setSubtypeIconResId(0)
49                .setSubtypeLocale(locale)
50                .setIsAsciiCapable(true)
51                .build();
52    }
53
54    private static void addDummyImeSubtypeListItems(List<ImeSubtypeListItem> items,
55            String imeName, String imeLabel, List<String> subtypeLocales,
56            boolean supportsSwitchingToNextInputMethod) {
57        final ResolveInfo ri = new ResolveInfo();
58        final ServiceInfo si = new ServiceInfo();
59        final ApplicationInfo ai = new ApplicationInfo();
60        ai.packageName = DUMMY_PACKAGE_NAME;
61        ai.enabled = true;
62        si.applicationInfo = ai;
63        si.enabled = true;
64        si.packageName = DUMMY_PACKAGE_NAME;
65        si.name = imeName;
66        si.exported = true;
67        si.nonLocalizedLabel = imeLabel;
68        ri.serviceInfo = si;
69        List<InputMethodSubtype> subtypes = null;
70        if (subtypeLocales != null) {
71            subtypes = new ArrayList<InputMethodSubtype>();
72            for (String subtypeLocale : subtypeLocales) {
73                subtypes.add(createDummySubtype(subtypeLocale));
74            }
75        }
76        final InputMethodInfo imi = new InputMethodInfo(ri, DUMMY_IS_AUX_IME,
77                DUMMY_SETTING_ACTIVITY_NAME, subtypes, DUMMY_IS_DEFAULT_RES_ID,
78                DUMMY_FORCE_DEFAULT, supportsSwitchingToNextInputMethod);
79        if (subtypes == null) {
80            items.add(new ImeSubtypeListItem(imeName, null /* variableName */, imi,
81                    NOT_A_SUBTYPE_ID, null, SYSTEM_LOCALE));
82        } else {
83            for (int i = 0; i < subtypes.size(); ++i) {
84                final String subtypeLocale = subtypeLocales.get(i);
85                items.add(new ImeSubtypeListItem(imeName, subtypeLocale, imi, i, subtypeLocale,
86                        SYSTEM_LOCALE));
87            }
88        }
89    }
90
91    private static List<ImeSubtypeListItem> createEnabledImeSubtypes() {
92        final List<ImeSubtypeListItem> items = new ArrayList<ImeSubtypeListItem>();
93        addDummyImeSubtypeListItems(items, "LatinIme", "LatinIme", Arrays.asList("en_US", "fr"),
94                true /* supportsSwitchingToNextInputMethod*/);
95        addDummyImeSubtypeListItems(items, "switchUnawareLatinIme", "switchUnawareLatinIme",
96                Arrays.asList("en_UK", "hi"),
97                false /* supportsSwitchingToNextInputMethod*/);
98        addDummyImeSubtypeListItems(items, "subtypeUnawareIme", "subtypeUnawareIme", null,
99                false /* supportsSwitchingToNextInputMethod*/);
100        addDummyImeSubtypeListItems(items, "JapaneseIme", "JapaneseIme", Arrays.asList("ja_JP"),
101                true /* supportsSwitchingToNextInputMethod*/);
102        addDummyImeSubtypeListItems(items, "switchUnawareJapaneseIme", "switchUnawareJapaneseIme",
103                Arrays.asList("ja_JP"), false /* supportsSwitchingToNextInputMethod*/);
104        return items;
105    }
106
107    private static List<ImeSubtypeListItem> createDisabledImeSubtypes() {
108        final List<ImeSubtypeListItem> items = new ArrayList<ImeSubtypeListItem>();
109        addDummyImeSubtypeListItems(items,
110                "UnknownIme", "UnknownIme",
111                Arrays.asList("en_US", "hi"),
112                true /* supportsSwitchingToNextInputMethod*/);
113        addDummyImeSubtypeListItems(items,
114                "UnknownSwitchingUnawareIme", "UnknownSwitchingUnawareIme",
115                Arrays.asList("en_US"),
116                false /* supportsSwitchingToNextInputMethod*/);
117        addDummyImeSubtypeListItems(items, "UnknownSubtypeUnawareIme",
118                "UnknownSubtypeUnawareIme", null,
119                false /* supportsSwitchingToNextInputMethod*/);
120        return items;
121    }
122
123    private void assertNextInputMethod(final ControllerImpl controller,
124            final boolean onlyCurrentIme,
125            final ImeSubtypeListItem currentItem, final ImeSubtypeListItem nextItem) {
126        InputMethodSubtype subtype = null;
127        if (currentItem.mSubtypeName != null) {
128            subtype = createDummySubtype(currentItem.mSubtypeName.toString());
129        }
130        final ImeSubtypeListItem nextIme = controller.getNextInputMethod(onlyCurrentIme,
131                currentItem.mImi, subtype);
132        assertEquals(nextItem, nextIme);
133    }
134
135    private void assertRotationOrder(final ControllerImpl controller,
136            final boolean onlyCurrentIme,
137            final ImeSubtypeListItem... expectedRotationOrderOfImeSubtypeList) {
138        final int N = expectedRotationOrderOfImeSubtypeList.length;
139        for (int i = 0; i < N; i++) {
140            final int currentIndex = i;
141            final int nextIndex = (currentIndex + 1) % N;
142            final ImeSubtypeListItem currentItem =
143                    expectedRotationOrderOfImeSubtypeList[currentIndex];
144            final ImeSubtypeListItem nextItem = expectedRotationOrderOfImeSubtypeList[nextIndex];
145            assertNextInputMethod(controller, onlyCurrentIme, currentItem, nextItem);
146        }
147    }
148
149    private void onUserAction(final ControllerImpl controller,
150            final ImeSubtypeListItem subtypeListItem) {
151        InputMethodSubtype subtype = null;
152        if (subtypeListItem.mSubtypeName != null) {
153            subtype = createDummySubtype(subtypeListItem.mSubtypeName.toString());
154        }
155        controller.onUserActionLocked(subtypeListItem.mImi, subtype);
156    }
157
158    @SmallTest
159    public void testControllerImpl() throws Exception {
160        final List<ImeSubtypeListItem> disabledItems = createDisabledImeSubtypes();
161        final ImeSubtypeListItem disabledIme_en_US = disabledItems.get(0);
162        final ImeSubtypeListItem disabledIme_hi = disabledItems.get(1);
163        final ImeSubtypeListItem disabledSwitchingUnawareIme = disabledItems.get(2);
164        final ImeSubtypeListItem disabledSubtypeUnawareIme = disabledItems.get(3);
165
166        final List<ImeSubtypeListItem> enabledItems = createEnabledImeSubtypes();
167        final ImeSubtypeListItem latinIme_en_US = enabledItems.get(0);
168        final ImeSubtypeListItem latinIme_fr = enabledItems.get(1);
169        final ImeSubtypeListItem switchingUnawarelatinIme_en_UK = enabledItems.get(2);
170        final ImeSubtypeListItem switchingUnawarelatinIme_hi = enabledItems.get(3);
171        final ImeSubtypeListItem subtypeUnawareIme = enabledItems.get(4);
172        final ImeSubtypeListItem japaneseIme_ja_JP = enabledItems.get(5);
173        final ImeSubtypeListItem switchUnawareJapaneseIme_ja_JP = enabledItems.get(6);
174
175        final ControllerImpl controller = ControllerImpl.createFrom(
176                null /* currentInstance */, enabledItems);
177
178        // switching-aware loop
179        assertRotationOrder(controller, false /* onlyCurrentIme */,
180                latinIme_en_US, latinIme_fr, japaneseIme_ja_JP);
181
182        // switching-unaware loop
183        assertRotationOrder(controller, false /* onlyCurrentIme */,
184                switchingUnawarelatinIme_en_UK, switchingUnawarelatinIme_hi, subtypeUnawareIme,
185                switchUnawareJapaneseIme_ja_JP);
186
187        // test onlyCurrentIme == true
188        assertRotationOrder(controller, true /* onlyCurrentIme */,
189                latinIme_en_US, latinIme_fr);
190        assertRotationOrder(controller, true /* onlyCurrentIme */,
191                switchingUnawarelatinIme_en_UK, switchingUnawarelatinIme_hi);
192        assertNextInputMethod(controller, true /* onlyCurrentIme */,
193                subtypeUnawareIme, null);
194        assertNextInputMethod(controller, true /* onlyCurrentIme */,
195                japaneseIme_ja_JP, null);
196        assertNextInputMethod(controller, true /* onlyCurrentIme */,
197                switchUnawareJapaneseIme_ja_JP, null);
198
199        // Make sure that disabled IMEs are not accepted.
200        assertNextInputMethod(controller, false /* onlyCurrentIme */,
201                disabledIme_en_US, null);
202        assertNextInputMethod(controller, false /* onlyCurrentIme */,
203                disabledIme_hi, null);
204        assertNextInputMethod(controller, false /* onlyCurrentIme */,
205                disabledSwitchingUnawareIme, null);
206        assertNextInputMethod(controller, false /* onlyCurrentIme */,
207                disabledSubtypeUnawareIme, null);
208        assertNextInputMethod(controller, true /* onlyCurrentIme */,
209                disabledIme_en_US, null);
210        assertNextInputMethod(controller, true /* onlyCurrentIme */,
211                disabledIme_hi, null);
212        assertNextInputMethod(controller, true /* onlyCurrentIme */,
213                disabledSwitchingUnawareIme, null);
214        assertNextInputMethod(controller, true /* onlyCurrentIme */,
215                disabledSubtypeUnawareIme, null);
216    }
217
218    @SmallTest
219    public void testControllerImplWithUserAction() throws Exception {
220        final List<ImeSubtypeListItem> enabledItems = createEnabledImeSubtypes();
221        final ImeSubtypeListItem latinIme_en_US = enabledItems.get(0);
222        final ImeSubtypeListItem latinIme_fr = enabledItems.get(1);
223        final ImeSubtypeListItem switchingUnawarelatinIme_en_UK = enabledItems.get(2);
224        final ImeSubtypeListItem switchingUnawarelatinIme_hi = enabledItems.get(3);
225        final ImeSubtypeListItem subtypeUnawareIme = enabledItems.get(4);
226        final ImeSubtypeListItem japaneseIme_ja_JP = enabledItems.get(5);
227        final ImeSubtypeListItem switchUnawareJapaneseIme_ja_JP = enabledItems.get(6);
228
229        final ControllerImpl controller = ControllerImpl.createFrom(
230                null /* currentInstance */, enabledItems);
231
232        // === switching-aware loop ===
233        assertRotationOrder(controller, false /* onlyCurrentIme */,
234                latinIme_en_US, latinIme_fr, japaneseIme_ja_JP);
235        // Then notify that a user did something for latinIme_fr.
236        onUserAction(controller, latinIme_fr);
237        assertRotationOrder(controller, false /* onlyCurrentIme */,
238                latinIme_fr, latinIme_en_US, japaneseIme_ja_JP);
239        // Then notify that a user did something for latinIme_fr again.
240        onUserAction(controller, latinIme_fr);
241        assertRotationOrder(controller, false /* onlyCurrentIme */,
242                latinIme_fr, latinIme_en_US, japaneseIme_ja_JP);
243        // Then notify that a user did something for japaneseIme_ja_JP.
244        onUserAction(controller, latinIme_fr);
245        assertRotationOrder(controller, false /* onlyCurrentIme */,
246                japaneseIme_ja_JP, latinIme_fr, latinIme_en_US);
247        // Check onlyCurrentIme == true.
248        assertNextInputMethod(controller, true /* onlyCurrentIme */,
249                japaneseIme_ja_JP, null);
250        assertRotationOrder(controller, true /* onlyCurrentIme */,
251                latinIme_fr, latinIme_en_US);
252        assertRotationOrder(controller, true /* onlyCurrentIme */,
253                latinIme_en_US, latinIme_fr);
254
255        // === switching-unaware loop ===
256        assertRotationOrder(controller, false /* onlyCurrentIme */,
257                switchingUnawarelatinIme_en_UK, switchingUnawarelatinIme_hi, subtypeUnawareIme,
258                switchUnawareJapaneseIme_ja_JP);
259        // User action should be ignored for switching unaware IMEs.
260        onUserAction(controller, switchingUnawarelatinIme_hi);
261        assertRotationOrder(controller, false /* onlyCurrentIme */,
262                switchingUnawarelatinIme_en_UK, switchingUnawarelatinIme_hi, subtypeUnawareIme,
263                switchUnawareJapaneseIme_ja_JP);
264        // User action should be ignored for switching unaware IMEs.
265        onUserAction(controller, switchUnawareJapaneseIme_ja_JP);
266        assertRotationOrder(controller, false /* onlyCurrentIme */,
267                switchingUnawarelatinIme_en_UK, switchingUnawarelatinIme_hi, subtypeUnawareIme,
268                switchUnawareJapaneseIme_ja_JP);
269        // Check onlyCurrentIme == true.
270        assertRotationOrder(controller, true /* onlyCurrentIme */,
271                switchingUnawarelatinIme_en_UK, switchingUnawarelatinIme_hi);
272        assertNextInputMethod(controller, true /* onlyCurrentIme */,
273                subtypeUnawareIme, null);
274        assertNextInputMethod(controller, true /* onlyCurrentIme */,
275                switchUnawareJapaneseIme_ja_JP, null);
276
277        // Rotation order should be preserved when created with the same subtype list.
278        final List<ImeSubtypeListItem> sameEnabledItems = createEnabledImeSubtypes();
279        final ControllerImpl newController = ControllerImpl.createFrom(controller,
280                sameEnabledItems);
281        assertRotationOrder(newController, false /* onlyCurrentIme */,
282                japaneseIme_ja_JP, latinIme_fr, latinIme_en_US);
283        assertRotationOrder(newController, false /* onlyCurrentIme */,
284                switchingUnawarelatinIme_en_UK, switchingUnawarelatinIme_hi, subtypeUnawareIme,
285                switchUnawareJapaneseIme_ja_JP);
286
287        // Rotation order should be initialized when created with a different subtype list.
288        final List<ImeSubtypeListItem> differentEnabledItems = Arrays.asList(
289                latinIme_en_US, latinIme_fr, switchingUnawarelatinIme_en_UK,
290                switchUnawareJapaneseIme_ja_JP);
291        final ControllerImpl anotherController = ControllerImpl.createFrom(controller,
292                differentEnabledItems);
293        assertRotationOrder(anotherController, false /* onlyCurrentIme */,
294                latinIme_en_US, latinIme_fr);
295        assertRotationOrder(anotherController, false /* onlyCurrentIme */,
296                switchingUnawarelatinIme_en_UK, switchUnawareJapaneseIme_ja_JP);
297    }
298
299    @SmallTest
300    public void testImeSubtypeListItem() throws Exception {
301        final List<ImeSubtypeListItem> items = new ArrayList<ImeSubtypeListItem>();
302        addDummyImeSubtypeListItems(items, "LatinIme", "LatinIme",
303                Arrays.asList("en_US", "fr", "en", "en_uk", "enn", "e", "EN_US"),
304                true /* supportsSwitchingToNextInputMethod*/);
305        final ImeSubtypeListItem item_en_US = items.get(0);
306        final ImeSubtypeListItem item_fr = items.get(1);
307        final ImeSubtypeListItem item_en = items.get(2);
308        final ImeSubtypeListItem item_enn = items.get(3);
309        final ImeSubtypeListItem item_e = items.get(4);
310        final ImeSubtypeListItem item_EN_US = items.get(5);
311
312        assertTrue(item_en_US.mIsSystemLocale);
313        assertFalse(item_fr.mIsSystemLocale);
314        assertFalse(item_en.mIsSystemLocale);
315        assertFalse(item_en.mIsSystemLocale);
316        assertFalse(item_enn.mIsSystemLocale);
317        assertFalse(item_e.mIsSystemLocale);
318        assertFalse(item_EN_US.mIsSystemLocale);
319
320        assertTrue(item_en_US.mIsSystemLanguage);
321        assertFalse(item_fr.mIsSystemLanguage);
322        assertTrue(item_en.mIsSystemLanguage);
323        assertFalse(item_enn.mIsSystemLocale);
324        assertFalse(item_e.mIsSystemLocale);
325        assertFalse(item_EN_US.mIsSystemLocale);
326    }
327}
328