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

Commit 40e979e6 authored by Sergey Troshin's avatar Sergey Troshin
Browse files

Improve ResolverActivity UX on TV

The following problems were fixed:

1. It's not possible to select "Just once" / "Always" buttons when the list doesn't fit all the items;

2. The list doesn't automatically scroll when selecting an item that's out of the view;

3. When there's a default action assigned, the dialog has an interactive "Use a different app" header, that's selected by default and does nothing.

Bug: 119841729
Test: atest ChooserActivityTest ResolverActivityTest
Change-Id: Ib143636ea009f9f155942af68c3a3a6250ec8261
parent e1dfcf69
Loading
Loading
Loading
Loading
+11 −3
Original line number Diff line number Diff line
@@ -353,7 +353,11 @@ public class ResolverActivity extends Activity implements
                    finish();
                }
            });
            if (isVoiceInteraction()) {

            boolean hasTouchScreen = getPackageManager()
                    .hasSystemFeature(PackageManager.FEATURE_TOUCHSCREEN);

            if (isVoiceInteraction() || !hasTouchScreen) {
                rdl.setCollapsed(false);
            }

@@ -1283,10 +1287,11 @@ public class ResolverActivity extends Activity implements

        // In case this method is called again (due to activity recreation), avoid adding a new
        // header if one is already present.
        if (useHeader && listView != null && listView.getHeaderViewsCount() == 0) {
        if (useHeader && listView.getHeaderViewsCount() == 0) {
            listView.setHeaderDividersEnabled(true);
            listView.addHeaderView(LayoutInflater.from(this).inflate(
                    R.layout.resolver_different_item_header, listView, false));
                    R.layout.resolver_different_item_header, listView, false),
                    null, false);
        }
    }

@@ -1349,6 +1354,8 @@ public class ResolverActivity extends Activity implements
        if (useLayoutWithDefault() && filteredPosition != ListView.INVALID_POSITION) {
            setAlwaysButtonEnabled(true, filteredPosition, false);
            mOnceButton.setEnabled(true);
            // Focus the button if we already have the default option
            mOnceButton.requestFocus();
            return;
        }

@@ -1481,6 +1488,7 @@ public class ResolverActivity extends Activity implements
                mOnceButton.setEnabled(hasValidSelection);
                if (hasValidSelection) {
                    currentAdapterView.smoothScrollToPosition(checkedPos);
                    mOnceButton.requestFocus();
                }
                mLastSelected = checkedPos;
            } else {
+1 −1
Original line number Diff line number Diff line
@@ -187,7 +187,7 @@ public class ResolverDrawerLayout extends ViewGroup {

    public void setCollapsed(boolean collapsed) {
        if (!isLaidOut()) {
            mOpenOnLayout = collapsed;
            mOpenOnLayout = !collapsed;
        } else {
            smoothScrollTo(collapsed ? mCollapsibleHeight : 0, 0);
        }
+29 −3
Original line number Diff line number Diff line
@@ -54,6 +54,7 @@ import android.content.pm.PackageManager;
import android.content.pm.ResolveInfo;
import android.content.pm.ShortcutInfo;
import android.content.pm.ShortcutManager.ShareShortcutInfo;
import android.content.res.Configuration;
import android.database.Cursor;
import android.graphics.Bitmap;
import android.graphics.Canvas;
@@ -977,6 +978,7 @@ public class ChooserActivityTest {
                        serviceTargets,
                        TARGET_TYPE_CHOOSER_TARGET)
        );

        // Thread.sleep shouldn't be a thing in an integration test but it's
        // necessary here because of the way the code is structured
        // TODO: restructure the tests b/129870719
@@ -1075,7 +1077,29 @@ public class ChooserActivityTest {

    // This test is too long and too slow and should not be taken as an example for future tests.
    @Test
    public void testDirectTargetLoggingWithAppTargetNotRanked() throws InterruptedException {
    public void testDirectTargetLoggingWithAppTargetNotRankedPortrait()
            throws InterruptedException {
        testDirectTargetLoggingWithAppTargetNotRanked(Configuration.ORIENTATION_PORTRAIT, 4);
    }

    @Test
    public void testDirectTargetLoggingWithAppTargetNotRankedLandscape()
            throws InterruptedException {
        testDirectTargetLoggingWithAppTargetNotRanked(Configuration.ORIENTATION_LANDSCAPE, 8);
    }

    private void testDirectTargetLoggingWithAppTargetNotRanked(
            int orientation, int appTargetsExpected
    ) throws InterruptedException {
        Configuration configuration =
                new Configuration(InstrumentationRegistry.getInstrumentation().getContext()
                        .getResources().getConfiguration());
        configuration.orientation = orientation;

        sOverrides.resources = Mockito.spy(
                InstrumentationRegistry.getInstrumentation().getContext().getResources());
        when(sOverrides.resources.getConfiguration()).thenReturn(configuration);

        Intent sendIntent = createSendTextIntent();
        // We need app targets for direct targets to get displayed
        List<ResolvedComponentInfo> resolvedComponentInfos = createResolvedComponentsForTest(15);
@@ -1111,8 +1135,10 @@ public class ChooserActivityTest {
        // TODO: restructure the tests b/129870719
        Thread.sleep(ChooserActivity.LIST_VIEW_UPDATE_INTERVAL_IN_MILLIS);

        assertThat("Chooser should have 20 targets (4 apps, 1 direct, 15 A-Z)",
                activity.getAdapter().getCount(), is(20));
        assertThat(
                String.format("Chooser should have %d targets (%d apps, 1 direct, 15 A-Z)",
                        appTargetsExpected + 16, appTargetsExpected),
                activity.getAdapter().getCount(), is(appTargetsExpected + 16));
        assertThat("Chooser should have exactly one selectable direct target",
                activity.getAdapter().getSelectableServiceTargetCount(), is(1));
        assertThat("The resolver info must match the resolver info used to create the target",