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 
17 package com.android.internal.inputmethod;
18 
19 import android.content.pm.ApplicationInfo;
20 import android.content.pm.ResolveInfo;
21 import android.content.pm.ServiceInfo;
22 import android.test.InstrumentationTestCase;
23 import android.test.suitebuilder.annotation.SmallTest;
24 import android.view.inputmethod.InputMethodInfo;
25 import android.view.inputmethod.InputMethodSubtype;
26 import android.view.inputmethod.InputMethodSubtype.InputMethodSubtypeBuilder;
27 
28 import com.android.internal.inputmethod.InputMethodSubtypeSwitchingController.ControllerImpl;
29 import com.android.internal.inputmethod.InputMethodSubtypeSwitchingController.ImeSubtypeListItem;
30 
31 import java.util.ArrayList;
32 import java.util.Arrays;
33 import java.util.List;
34 
35 public class InputMethodSubtypeSwitchingControllerTest extends InstrumentationTestCase {
36     private static final String DUMMY_PACKAGE_NAME = "dummy package name";
37     private static final String DUMMY_IME_LABEL = "dummy ime label";
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 
createDummySubtype(final String locale)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 
addDummyImeSubtypeListItems(List<ImeSubtypeListItem> items, String imeName, String imeLabel, List<String> subtypeLocales, boolean supportsSwitchingToNextInputMethod)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<>();
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 
createDummyItem(String imeName, String subtypeName, String subtypeLocale, int subtypeIndex, String systemLocale)91     private static ImeSubtypeListItem createDummyItem(String imeName,
92             String subtypeName, String subtypeLocale, int subtypeIndex, String systemLocale) {
93         final ResolveInfo ri = new ResolveInfo();
94         final ServiceInfo si = new ServiceInfo();
95         final ApplicationInfo ai = new ApplicationInfo();
96         ai.packageName = DUMMY_PACKAGE_NAME;
97         ai.enabled = true;
98         si.applicationInfo = ai;
99         si.enabled = true;
100         si.packageName = DUMMY_PACKAGE_NAME;
101         si.name = imeName;
102         si.exported = true;
103         si.nonLocalizedLabel = DUMMY_IME_LABEL;
104         ri.serviceInfo = si;
105         ArrayList<InputMethodSubtype> subtypes = new ArrayList<>();
106         subtypes.add(new InputMethodSubtypeBuilder()
107                 .setSubtypeNameResId(0)
108                 .setSubtypeIconResId(0)
109                 .setSubtypeLocale(subtypeLocale)
110                 .setIsAsciiCapable(true)
111                 .build());
112         final InputMethodInfo imi = new InputMethodInfo(ri, DUMMY_IS_AUX_IME,
113                 DUMMY_SETTING_ACTIVITY_NAME, subtypes, DUMMY_IS_DEFAULT_RES_ID,
114                 DUMMY_FORCE_DEFAULT, true /* supportsSwitchingToNextInputMethod */);
115         return new ImeSubtypeListItem(imeName, subtypeName, imi, subtypeIndex, subtypeLocale,
116                 systemLocale);
117     }
118 
createEnabledImeSubtypes()119     private static List<ImeSubtypeListItem> createEnabledImeSubtypes() {
120         final List<ImeSubtypeListItem> items = new ArrayList<>();
121         addDummyImeSubtypeListItems(items, "LatinIme", "LatinIme", Arrays.asList("en_US", "fr"),
122                 true /* supportsSwitchingToNextInputMethod*/);
123         addDummyImeSubtypeListItems(items, "switchUnawareLatinIme", "switchUnawareLatinIme",
124                 Arrays.asList("en_UK", "hi"),
125                 false /* supportsSwitchingToNextInputMethod*/);
126         addDummyImeSubtypeListItems(items, "subtypeUnawareIme", "subtypeUnawareIme", null,
127                 false /* supportsSwitchingToNextInputMethod*/);
128         addDummyImeSubtypeListItems(items, "JapaneseIme", "JapaneseIme", Arrays.asList("ja_JP"),
129                 true /* supportsSwitchingToNextInputMethod*/);
130         addDummyImeSubtypeListItems(items, "switchUnawareJapaneseIme", "switchUnawareJapaneseIme",
131                 Arrays.asList("ja_JP"), false /* supportsSwitchingToNextInputMethod*/);
132         return items;
133     }
134 
createDisabledImeSubtypes()135     private static List<ImeSubtypeListItem> createDisabledImeSubtypes() {
136         final List<ImeSubtypeListItem> items = new ArrayList<>();
137         addDummyImeSubtypeListItems(items,
138                 "UnknownIme", "UnknownIme",
139                 Arrays.asList("en_US", "hi"),
140                 true /* supportsSwitchingToNextInputMethod*/);
141         addDummyImeSubtypeListItems(items,
142                 "UnknownSwitchingUnawareIme", "UnknownSwitchingUnawareIme",
143                 Arrays.asList("en_US"),
144                 false /* supportsSwitchingToNextInputMethod*/);
145         addDummyImeSubtypeListItems(items, "UnknownSubtypeUnawareIme",
146                 "UnknownSubtypeUnawareIme", null,
147                 false /* supportsSwitchingToNextInputMethod*/);
148         return items;
149     }
150 
assertNextInputMethod(final ControllerImpl controller, final boolean onlyCurrentIme, final ImeSubtypeListItem currentItem, final ImeSubtypeListItem nextItem, final ImeSubtypeListItem prevItem)151     private void assertNextInputMethod(final ControllerImpl controller,
152             final boolean onlyCurrentIme, final ImeSubtypeListItem currentItem,
153             final ImeSubtypeListItem nextItem, final ImeSubtypeListItem prevItem) {
154         InputMethodSubtype subtype = null;
155         if (currentItem.mSubtypeName != null) {
156             subtype = createDummySubtype(currentItem.mSubtypeName.toString());
157         }
158         final ImeSubtypeListItem nextIme = controller.getNextInputMethod(onlyCurrentIme,
159                 currentItem.mImi, subtype, true /* forward */);
160         assertEquals(nextItem, nextIme);
161         final ImeSubtypeListItem prevIme = controller.getNextInputMethod(onlyCurrentIme,
162                 currentItem.mImi, subtype, false /* forward */);
163         assertEquals(prevItem, prevIme);
164     }
165 
assertRotationOrder(final ControllerImpl controller, final boolean onlyCurrentIme, final ImeSubtypeListItem... expectedRotationOrderOfImeSubtypeList)166     private void assertRotationOrder(final ControllerImpl controller,
167             final boolean onlyCurrentIme,
168             final ImeSubtypeListItem... expectedRotationOrderOfImeSubtypeList) {
169         final int N = expectedRotationOrderOfImeSubtypeList.length;
170         for (int i = 0; i < N; i++) {
171             final int currentIndex = i;
172             final int prevIndex = (currentIndex + N - 1) % N;
173             final int nextIndex = (currentIndex + 1) % N;
174             final ImeSubtypeListItem currentItem =
175                     expectedRotationOrderOfImeSubtypeList[currentIndex];
176             final ImeSubtypeListItem nextItem = expectedRotationOrderOfImeSubtypeList[nextIndex];
177             final ImeSubtypeListItem prevItem = expectedRotationOrderOfImeSubtypeList[prevIndex];
178             assertNextInputMethod(controller, onlyCurrentIme, currentItem, nextItem, prevItem);
179         }
180     }
181 
onUserAction(final ControllerImpl controller, final ImeSubtypeListItem subtypeListItem)182     private void onUserAction(final ControllerImpl controller,
183             final ImeSubtypeListItem subtypeListItem) {
184         InputMethodSubtype subtype = null;
185         if (subtypeListItem.mSubtypeName != null) {
186             subtype = createDummySubtype(subtypeListItem.mSubtypeName.toString());
187         }
188         controller.onUserActionLocked(subtypeListItem.mImi, subtype);
189     }
190 
191     @SmallTest
testControllerImpl()192     public void testControllerImpl() throws Exception {
193         final List<ImeSubtypeListItem> disabledItems = createDisabledImeSubtypes();
194         final ImeSubtypeListItem disabledIme_en_US = disabledItems.get(0);
195         final ImeSubtypeListItem disabledIme_hi = disabledItems.get(1);
196         final ImeSubtypeListItem disabledSwitchingUnawareIme = disabledItems.get(2);
197         final ImeSubtypeListItem disabledSubtypeUnawareIme = disabledItems.get(3);
198 
199         final List<ImeSubtypeListItem> enabledItems = createEnabledImeSubtypes();
200         final ImeSubtypeListItem latinIme_en_US = enabledItems.get(0);
201         final ImeSubtypeListItem latinIme_fr = enabledItems.get(1);
202         final ImeSubtypeListItem switchingUnawarelatinIme_en_UK = enabledItems.get(2);
203         final ImeSubtypeListItem switchingUnawarelatinIme_hi = enabledItems.get(3);
204         final ImeSubtypeListItem subtypeUnawareIme = enabledItems.get(4);
205         final ImeSubtypeListItem japaneseIme_ja_JP = enabledItems.get(5);
206         final ImeSubtypeListItem switchUnawareJapaneseIme_ja_JP = enabledItems.get(6);
207 
208         final ControllerImpl controller = ControllerImpl.createFrom(
209                 null /* currentInstance */, enabledItems);
210 
211         // switching-aware loop
212         assertRotationOrder(controller, false /* onlyCurrentIme */,
213                 latinIme_en_US, latinIme_fr, japaneseIme_ja_JP);
214 
215         // switching-unaware loop
216         assertRotationOrder(controller, false /* onlyCurrentIme */,
217                 switchingUnawarelatinIme_en_UK, switchingUnawarelatinIme_hi, subtypeUnawareIme,
218                 switchUnawareJapaneseIme_ja_JP);
219 
220         // test onlyCurrentIme == true
221         assertRotationOrder(controller, true /* onlyCurrentIme */,
222                 latinIme_en_US, latinIme_fr);
223         assertRotationOrder(controller, true /* onlyCurrentIme */,
224                 switchingUnawarelatinIme_en_UK, switchingUnawarelatinIme_hi);
225         assertNextInputMethod(controller, true /* onlyCurrentIme */,
226                 subtypeUnawareIme, null, null);
227         assertNextInputMethod(controller, true /* onlyCurrentIme */,
228                 japaneseIme_ja_JP, null, null);
229         assertNextInputMethod(controller, true /* onlyCurrentIme */,
230                 switchUnawareJapaneseIme_ja_JP, null, null);
231 
232         // Make sure that disabled IMEs are not accepted.
233         assertNextInputMethod(controller, false /* onlyCurrentIme */,
234                 disabledIme_en_US, null, null);
235         assertNextInputMethod(controller, false /* onlyCurrentIme */,
236                 disabledIme_hi, null, null);
237         assertNextInputMethod(controller, false /* onlyCurrentIme */,
238                 disabledSwitchingUnawareIme, null, null);
239         assertNextInputMethod(controller, false /* onlyCurrentIme */,
240                 disabledSubtypeUnawareIme, null, null);
241         assertNextInputMethod(controller, true /* onlyCurrentIme */,
242                 disabledIme_en_US, null, null);
243         assertNextInputMethod(controller, true /* onlyCurrentIme */,
244                 disabledIme_hi, null, null);
245         assertNextInputMethod(controller, true /* onlyCurrentIme */,
246                 disabledSwitchingUnawareIme, null, null);
247         assertNextInputMethod(controller, true /* onlyCurrentIme */,
248                 disabledSubtypeUnawareIme, null, null);
249     }
250 
251     @SmallTest
testControllerImplWithUserAction()252     public void testControllerImplWithUserAction() throws Exception {
253         final List<ImeSubtypeListItem> enabledItems = createEnabledImeSubtypes();
254         final ImeSubtypeListItem latinIme_en_US = enabledItems.get(0);
255         final ImeSubtypeListItem latinIme_fr = enabledItems.get(1);
256         final ImeSubtypeListItem switchingUnawarelatinIme_en_UK = enabledItems.get(2);
257         final ImeSubtypeListItem switchingUnawarelatinIme_hi = enabledItems.get(3);
258         final ImeSubtypeListItem subtypeUnawareIme = enabledItems.get(4);
259         final ImeSubtypeListItem japaneseIme_ja_JP = enabledItems.get(5);
260         final ImeSubtypeListItem switchUnawareJapaneseIme_ja_JP = enabledItems.get(6);
261 
262         final ControllerImpl controller = ControllerImpl.createFrom(
263                 null /* currentInstance */, enabledItems);
264 
265         // === switching-aware loop ===
266         assertRotationOrder(controller, false /* onlyCurrentIme */,
267                 latinIme_en_US, latinIme_fr, japaneseIme_ja_JP);
268         // Then notify that a user did something for latinIme_fr.
269         onUserAction(controller, latinIme_fr);
270         assertRotationOrder(controller, false /* onlyCurrentIme */,
271                 latinIme_fr, latinIme_en_US, japaneseIme_ja_JP);
272         // Then notify that a user did something for latinIme_fr again.
273         onUserAction(controller, latinIme_fr);
274         assertRotationOrder(controller, false /* onlyCurrentIme */,
275                 latinIme_fr, latinIme_en_US, japaneseIme_ja_JP);
276         // Then notify that a user did something for japaneseIme_ja_JP.
277         onUserAction(controller, latinIme_fr);
278         assertRotationOrder(controller, false /* onlyCurrentIme */,
279                 japaneseIme_ja_JP, latinIme_fr, latinIme_en_US);
280         // Check onlyCurrentIme == true.
281         assertNextInputMethod(controller, true /* onlyCurrentIme */,
282                 japaneseIme_ja_JP, null, null);
283         assertRotationOrder(controller, true /* onlyCurrentIme */,
284                 latinIme_fr, latinIme_en_US);
285         assertRotationOrder(controller, true /* onlyCurrentIme */,
286                 latinIme_en_US, latinIme_fr);
287 
288         // === switching-unaware loop ===
289         assertRotationOrder(controller, false /* onlyCurrentIme */,
290                 switchingUnawarelatinIme_en_UK, switchingUnawarelatinIme_hi, subtypeUnawareIme,
291                 switchUnawareJapaneseIme_ja_JP);
292         // User action should be ignored for switching unaware IMEs.
293         onUserAction(controller, switchingUnawarelatinIme_hi);
294         assertRotationOrder(controller, false /* onlyCurrentIme */,
295                 switchingUnawarelatinIme_en_UK, switchingUnawarelatinIme_hi, subtypeUnawareIme,
296                 switchUnawareJapaneseIme_ja_JP);
297         // User action should be ignored for switching unaware IMEs.
298         onUserAction(controller, switchUnawareJapaneseIme_ja_JP);
299         assertRotationOrder(controller, false /* onlyCurrentIme */,
300                 switchingUnawarelatinIme_en_UK, switchingUnawarelatinIme_hi, subtypeUnawareIme,
301                 switchUnawareJapaneseIme_ja_JP);
302         // Check onlyCurrentIme == true.
303         assertRotationOrder(controller, true /* onlyCurrentIme */,
304                 switchingUnawarelatinIme_en_UK, switchingUnawarelatinIme_hi);
305         assertNextInputMethod(controller, true /* onlyCurrentIme */,
306                 subtypeUnawareIme, null, null);
307         assertNextInputMethod(controller, true /* onlyCurrentIme */,
308                 switchUnawareJapaneseIme_ja_JP, null, null);
309 
310         // Rotation order should be preserved when created with the same subtype list.
311         final List<ImeSubtypeListItem> sameEnabledItems = createEnabledImeSubtypes();
312         final ControllerImpl newController = ControllerImpl.createFrom(controller,
313                 sameEnabledItems);
314         assertRotationOrder(newController, false /* onlyCurrentIme */,
315                 japaneseIme_ja_JP, latinIme_fr, latinIme_en_US);
316         assertRotationOrder(newController, false /* onlyCurrentIme */,
317                 switchingUnawarelatinIme_en_UK, switchingUnawarelatinIme_hi, subtypeUnawareIme,
318                 switchUnawareJapaneseIme_ja_JP);
319 
320         // Rotation order should be initialized when created with a different subtype list.
321         final List<ImeSubtypeListItem> differentEnabledItems = Arrays.asList(
322                 latinIme_en_US, latinIme_fr, switchingUnawarelatinIme_en_UK,
323                 switchUnawareJapaneseIme_ja_JP);
324         final ControllerImpl anotherController = ControllerImpl.createFrom(controller,
325                 differentEnabledItems);
326         assertRotationOrder(anotherController, false /* onlyCurrentIme */,
327                 latinIme_en_US, latinIme_fr);
328         assertRotationOrder(anotherController, false /* onlyCurrentIme */,
329                 switchingUnawarelatinIme_en_UK, switchUnawareJapaneseIme_ja_JP);
330     }
331 
332     @SmallTest
testImeSubtypeListItem()333     public void testImeSubtypeListItem() throws Exception {
334         final List<ImeSubtypeListItem> items = new ArrayList<>();
335         addDummyImeSubtypeListItems(items, "LatinIme", "LatinIme",
336                 Arrays.asList("en_US", "fr", "en", "en_uk", "enn", "e", "EN_US"),
337                 true /* supportsSwitchingToNextInputMethod*/);
338         final ImeSubtypeListItem item_en_US = items.get(0);
339         final ImeSubtypeListItem item_fr = items.get(1);
340         final ImeSubtypeListItem item_en = items.get(2);
341         final ImeSubtypeListItem item_enn = items.get(3);
342         final ImeSubtypeListItem item_e = items.get(4);
343         final ImeSubtypeListItem item_EN_US = items.get(5);
344 
345         assertTrue(item_en_US.mIsSystemLocale);
346         assertFalse(item_fr.mIsSystemLocale);
347         assertFalse(item_en.mIsSystemLocale);
348         assertFalse(item_en.mIsSystemLocale);
349         assertFalse(item_enn.mIsSystemLocale);
350         assertFalse(item_e.mIsSystemLocale);
351         assertFalse(item_EN_US.mIsSystemLocale);
352 
353         assertTrue(item_en_US.mIsSystemLanguage);
354         assertFalse(item_fr.mIsSystemLanguage);
355         assertTrue(item_en.mIsSystemLanguage);
356         assertFalse(item_enn.mIsSystemLocale);
357         assertFalse(item_e.mIsSystemLocale);
358         assertFalse(item_EN_US.mIsSystemLocale);
359     }
360 
361     @SmallTest
testImeSubtypeListComparator()362     public void testImeSubtypeListComparator() throws Exception {
363         {
364             final List<ImeSubtypeListItem> items = Arrays.asList(
365                     // Subtypes of IME "X".
366                     // Subtypes that has the same locale of the system's.
367                     createDummyItem("X", "E", "en_US", 0, "en_US"),
368                     createDummyItem("X", "Z", "en_US", 3, "en_US"),
369                     createDummyItem("X", "", "en_US", 6, "en_US"),
370                     // Subtypes that has the same language of the system's.
371                     createDummyItem("X", "E", "en", 1, "en_US"),
372                     createDummyItem("X", "Z", "en", 4, "en_US"),
373                     createDummyItem("X", "", "en", 7, "en_US"),
374                     // Subtypes that has different language than the system's.
375                     createDummyItem("X", "A", "hi_IN", 27, "en_US"),
376                     createDummyItem("X", "E", "ja", 2, "en_US"),
377                     createDummyItem("X", "Z", "ja", 5, "en_US"),
378                     createDummyItem("X", "", "ja", 8, "en_US"),
379 
380                     // Subtypes of IME "Y".
381                     // Subtypes that has the same locale of the system's.
382                     createDummyItem("Y", "E", "en_US", 9, "en_US"),
383                     createDummyItem("Y", "Z", "en_US", 12, "en_US"),
384                     createDummyItem("Y", "", "en_US", 15, "en_US"),
385                     // Subtypes that has the same language of the system's.
386                     createDummyItem("Y", "E", "en", 10, "en_US"),
387                     createDummyItem("Y", "Z", "en", 13, "en_US"),
388                     createDummyItem("Y", "", "en", 16, "en_US"),
389                     // Subtypes that has different language than the system's.
390                     createDummyItem("Y", "A", "hi_IN", 28, "en_US"),
391                     createDummyItem("Y", "E", "ja", 11, "en_US"),
392                     createDummyItem("Y", "Z", "ja", 14, "en_US"),
393                     createDummyItem("Y", "", "ja", 17, "en_US"),
394 
395                     // Subtypes of IME "".
396                     // Subtypes that has the same locale of the system's.
397                     createDummyItem("", "E", "en_US", 18, "en_US"),
398                     createDummyItem("", "Z", "en_US", 21, "en_US"),
399                     createDummyItem("", "", "en_US", 24, "en_US"),
400                     // Subtypes that has the same language of the system's.
401                     createDummyItem("", "E", "en", 19, "en_US"),
402                     createDummyItem("", "Z", "en", 22, "en_US"),
403                     createDummyItem("", "", "en", 25, "en_US"),
404                     // Subtypes that has different language than the system's.
405                     createDummyItem("", "A", "hi_IN", 29, "en_US"),
406                     createDummyItem("", "E", "ja", 20, "en_US"),
407                     createDummyItem("", "Z", "ja", 23, "en_US"),
408                     createDummyItem("", "", "ja", 26, "en_US"));
409 
410             // Ensure {@link java.lang.Comparable#compareTo} contracts are satisfied.
411             for (int i = 0; i < items.size(); ++i) {
412                 final ImeSubtypeListItem item1 = items.get(i);
413                 // Ensures sgn(x.compareTo(y)) == -sgn(y.compareTo(x)).
414                 assertTrue(item1 + " has the same order of itself", item1.compareTo(item1) == 0);
415                 // Ensures (x.compareTo(y) > 0 && y.compareTo(z) > 0) implies x.compareTo(z) > 0.
416                 for (int j = i + 1; j < items.size(); ++j) {
417                     final ImeSubtypeListItem item2 = items.get(j);
418                     // Ensures sgn(x.compareTo(y)) == -sgn(y.compareTo(x)).
419                     assertTrue(item1 + " is less than " + item2, item1.compareTo(item2) < 0);
420                     assertTrue(item2 + " is greater than " + item1, item2.compareTo(item1) > 0);
421                 }
422             }
423         }
424 
425         {
426             // Following two items have the same priority.
427             final ImeSubtypeListItem nonSystemLocale1 =
428                     createDummyItem("X", "A", "ja_JP", 0, "en_US");
429             final ImeSubtypeListItem nonSystemLocale2 =
430                     createDummyItem("X", "A", "hi_IN", 1, "en_US");
431             assertTrue(nonSystemLocale1.compareTo(nonSystemLocale2) == 0);
432             assertTrue(nonSystemLocale2.compareTo(nonSystemLocale1) == 0);
433             // But those aren't equal to each other.
434             assertFalse(nonSystemLocale1.equals(nonSystemLocale2));
435             assertFalse(nonSystemLocale2.equals(nonSystemLocale1));
436         }
437     }
438 }
439