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