diff options
2 files changed, 80 insertions, 11 deletions
diff --git a/services/core/java/com/android/server/inputmethod/InputMethodMenuControllerNew.java b/services/core/java/com/android/server/inputmethod/InputMethodMenuControllerNew.java index 5e2a6ac31a0a..1d0e3c639c3f 100644 --- a/services/core/java/com/android/server/inputmethod/InputMethodMenuControllerNew.java +++ b/services/core/java/com/android/server/inputmethod/InputMethodMenuControllerNew.java @@ -199,8 +199,10 @@ final class InputMethodMenuControllerNew { * handles adding headers and dividers between groups of items from different input methods * as follows: * + * <li>If there is only one group, no divider or header will be added.</li> * <li>A divider is added before each group, except the first one.</li> - * <li>A header is added before each group (after the divider, if it exists).</li> + * <li>A header is added before each group (after the divider, if it exists) if the group has + * at least two items, or a single item with a subtype name.</li> * * @param items the list of input method and subtype items. */ @@ -212,23 +214,32 @@ final class InputMethodMenuControllerNew { return menuItems; } - String prevImeId = null; - for (int i = 0; i < items.size(); i++) { - final var item = items.get(i); + final var itemsArray = (ArrayList<ImeSubtypeListItem>) items; + final int numItems = itemsArray.size(); + // Initialize to the last IME id to avoid headers if there is only a single IME. + String prevImeId = itemsArray.getLast().mImi.getId(); + boolean firstGroup = true; + for (int i = 0; i < numItems; i++) { + final var item = itemsArray.get(i); final var imeId = item.mImi.getId(); final boolean groupChange = !imeId.equals(prevImeId); if (groupChange) { - final boolean firstGroup = prevImeId == null; if (!firstGroup) { menuItems.add(DividerItem.getInstance()); } - menuItems.add(new HeaderItem(item.mImeName)); + // Add a header if we have at least two items, or a single item with a subtype name. + final var nextItemId = i + 1 < numItems ? itemsArray.get(i + 1).mImi.getId() : null; + final boolean addHeader = item.mSubtypeName != null || imeId.equals(nextItemId); + if (addHeader) { + menuItems.add(new HeaderItem(item.mImeName)); + } + firstGroup = false; + prevImeId = imeId; } menuItems.add(new SubtypeItem(item.mImeName, item.mSubtypeName, item.mImi, item.mSubtypeIndex)); - prevImeId = imeId; } return menuItems; diff --git a/services/tests/InputMethodSystemServerTests/src/com/android/server/inputmethod/InputMethodMenuControllerTest.java b/services/tests/InputMethodSystemServerTests/src/com/android/server/inputmethod/InputMethodMenuControllerTest.java index 02071695505c..02dc86bffe2d 100644 --- a/services/tests/InputMethodSystemServerTests/src/com/android/server/inputmethod/InputMethodMenuControllerTest.java +++ b/services/tests/InputMethodSystemServerTests/src/com/android/server/inputmethod/InputMethodMenuControllerTest.java @@ -81,6 +81,63 @@ public class InputMethodMenuControllerTest { .isEqualTo(items.size()); } + /** + * Verifies that getMenuItems does not add a header or divider if all the items belong to + * a single input method. + */ + @Test + public void testGetMenuItemsNoHeaderOrDividerForSingleInputMethod() { + final var items = new ArrayList<ImeSubtypeListItem>(); + addTestImeSubtypeListItems(items, "LatinIme", "LatinIme", + List.of("en", "fr"), true /* supportsSwitchingToNextInputMethod */); + + final var menuItems = getMenuItems(items); + + assertThat(menuItems.stream() + .filter(item -> item instanceof HeaderItem || item instanceof DividerItem).toList()) + .isEmpty(); + } + + /** + * Verifies that getMenuItems only adds headers for item groups with at least two items, + * or with a single item with a subtype name. + */ + @Test + public void testGetMenuItemsHeaders() { + final var items = new ArrayList<ImeSubtypeListItem>(); + addTestImeSubtypeListItems(items, "DefaultIme", "DefaultIme", + null, true /* supportsSwitchingToNextInputMethod */); + addTestImeSubtypeListItems(items, "LatinIme", "LatinIme", + List.of("en", "fr"), true /* supportsSwitchingToNextInputMethod */); + addTestImeSubtypeListItems(items, "ItalianIme", "ItalianIme", + List.of("it"), true /* supportsSwitchingToNextInputMethod */); + addTestImeSubtypeListItems(items, "SimpleIme", "SimpleIme", + null, true /* supportsSwitchingToNextInputMethod */); + + final var menuItems = getMenuItems(items); + + assertWithMessage("Must have menu items").that(menuItems).isNotEmpty(); + + final var headersAndDividers = menuItems.stream() + .filter(item -> item instanceof HeaderItem || item instanceof DividerItem) + .toList(); + + assertWithMessage("Must have header and divider items").that(headersAndDividers).hasSize(5); + + assertWithMessage("First group has no header") + .that(menuItems.getFirst()).isInstanceOf(SubtypeItem.class); + assertWithMessage("Group with multiple items has divider") + .that(headersAndDividers.get(0)).isInstanceOf(DividerItem.class); + assertWithMessage("Group with multiple items has header") + .that(headersAndDividers.get(1)).isInstanceOf(HeaderItem.class); + assertWithMessage("Group with single item with subtype name has divider") + .that(headersAndDividers.get(2)).isInstanceOf(DividerItem.class); + assertWithMessage("Group with single item with subtype name has header") + .that(headersAndDividers.get(3)).isInstanceOf(HeaderItem.class); + assertWithMessage("Group with single item without subtype name has divider only") + .that(headersAndDividers.get(4)).isInstanceOf(DividerItem.class); + } + /** Verifies that getMenuItems adds a divider before every header except the first one. */ @Test public void testGetMenuItemsDivider() { @@ -107,8 +164,9 @@ public class InputMethodMenuControllerTest { .that(prevItem).isInstanceOf(DividerItem.class); } else if (item instanceof DividerItem && i < menuItems.size() - 1) { final var nextItem = menuItems.get(i + 1); - assertWithMessage("The item after a divider should be a header") - .that(nextItem).isInstanceOf(HeaderItem.class); + assertWithMessage("The item after a divider should be a header or subtype") + .that(nextItem instanceof HeaderItem || nextItem instanceof SubtypeItem) + .isTrue(); } } } @@ -170,7 +228,7 @@ public class InputMethodMenuControllerTest { final int selectedIndex = getSelectedIndex(menuItems, simpleImeId, 1); - // Two headers + one divider + two items - assertThat(selectedIndex).isEqualTo(5); + // One header + one divider + two items + assertThat(selectedIndex).isEqualTo(4); } } |