Donate to e Foundation | Murena handsets with /e/OS | Own a part of Murena! Learn more

Commit ceaa8ce1 authored by Cosmin Băieș's avatar Cosmin Băieș
Browse files

Enable omitting IME Switcher Menu header

This updates the IME Switcher Menu header logic to omit the title for
groups of items with only a single item without a subtype name.
Additionally, if all the items belong to the same input method, no
header will be shown.

Flag: android.view.inputmethod.ime_switcher_revamp
Bug: 369120217
Test: atest InputMethodMenuControllerTest#testGetMenuItemsNoHeaderForSingleInputMethod
  InputMethodMenuControllerTest#testGetMenuItemsHeaderTitles
Change-Id: I977831b2020fb9c92b4a20a8e8fb86cb512a8a43
parent 6c4e087c
Loading
Loading
Loading
Loading
+18 −7
Original line number Diff line number Diff line
@@ -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());
                }
                // 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;
+62 −4
Original line number Diff line number Diff line
@@ -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);
    }
}