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

Commit 7a506161 authored by Chris Göllner's avatar Chris Göllner
Browse files

Partial Screen Sharing: fix last item in app selector being empty

The bug was related to the override of shouldShowContentPreview() in
MediaProjectionAppSelectorActivity.
This method was being used internally in ChooserActivity not only to
determine whether the content preview should be shown, but also
whether a "service target" row should be shown or not. By default that
method checks whether the Intent action is ACTION_SEND.
MediaProjectionAppSelectorActivity overrides the method to always return
true. Then there is code in ChooserListAdapter that also checks for
the intent action to determine the service target count. This was
causing an inconsistency between ChooserActivity and ChooserListAdapter,
making it do a wrong calculation for the list item indexes.

Change-Id: I3dee38909035c44816d9ae1d06b17eec6266107e
Test:  atest FrameworksCoreTests:ChooserListAdapterTest
Test:  atest FrameworksCoreTests:ChooserActivityTest
Fixes: 295877430
Fixes: 299347412
parent dead0f9c
Loading
Loading
Loading
Loading
+14 −15
Original line number Diff line number Diff line
@@ -3023,28 +3023,31 @@ public class ChooserActivity extends ResolverActivity implements
        return shouldShowTabs()
                && (mMultiProfilePagerAdapter.getListAdapterForUserHandle(
                        UserHandle.of(UserHandle.myUserId())).getCount() > 0
                    || shouldShowContentPreviewWhenEmpty())
                    || shouldShowStickyContentPreviewWhenEmpty())
                && shouldShowContentPreview();
    }

    /**
     * This method could be used to override the default behavior when we hide the preview area
     * when the current tab doesn't have any items.
     * This method could be used to override the default behavior when we hide the sticky preview
     * area when the current tab doesn't have any items.
     *
     * @return true if we want to show the content preview area even if the tab for the current
     *         user is empty
     * @return {@code true} if we want to show the sticky content preview area even if the tab for
     *         the current user is empty
     */
    protected boolean shouldShowContentPreviewWhenEmpty() {
    protected boolean shouldShowStickyContentPreviewWhenEmpty() {
        return false;
    }

    /**
     * @return true if we want to show the content preview area
     */
    protected boolean shouldShowContentPreview() {
    @Override
    public boolean shouldShowContentPreview() {
        return isSendAction(getTargetIntent());
    }

    @Override
    public boolean shouldShowServiceTargets() {
        return shouldShowContentPreview() && !ActivityManager.isLowRamDeviceStatic();
    }

    private void updateStickyContentPreview() {
        if (shouldShowStickyContentPreviewNoOrientationCheck()) {
            // The sticky content preview is only shown when we show the work and personal tabs.
@@ -3406,11 +3409,7 @@ public class ChooserActivity extends ResolverActivity implements
        // There can be at most one row in the listview, that is internally
        // a ViewGroup with 2 rows
        public int getServiceTargetRowCount() {
            if (shouldShowContentPreview()
                    && !ActivityManager.isLowRamDeviceStatic()) {
                return 1;
            }
            return 0;
            return shouldShowServiceTargets() ? 1 : 0;
        }

        public int getAzLabelRowCount() {
+5 −4
Original line number Diff line number Diff line
@@ -19,7 +19,6 @@ package com.android.internal.app;
import static com.android.internal.app.ChooserActivity.TARGET_TYPE_SHORTCUTS_FROM_PREDICTION_SERVICE;
import static com.android.internal.app.ChooserActivity.TARGET_TYPE_SHORTCUTS_FROM_SHORTCUT_MANAGER;

import android.app.ActivityManager;
import android.app.prediction.AppPredictor;
import android.content.ComponentName;
import android.content.Context;
@@ -425,11 +424,9 @@ public class ChooserListAdapter extends ResolverListAdapter {
    }

    public int getServiceTargetCount() {
        if (mChooserListCommunicator.isSendAction(mChooserListCommunicator.getTargetIntent())
                && !ActivityManager.isLowRamDeviceStatic()) {
        if (mChooserListCommunicator.shouldShowServiceTargets()) {
            return Math.min(mServiceTargets.size(), mChooserListCommunicator.getMaxRankedTargets());
        }

        return 0;
    }

@@ -771,6 +768,10 @@ public class ChooserListAdapter extends ResolverListAdapter {
        void sendListViewUpdateMessage(UserHandle userHandle);

        boolean isSendAction(Intent targetIntent);

        boolean shouldShowContentPreview();

        boolean shouldShowServiceTargets();
    }

    /**
+97 −35
Original line number Diff line number Diff line
@@ -19,8 +19,12 @@ package com.android.internal.app
import android.content.ComponentName
import android.content.Context
import android.content.Intent
import android.content.pm.ActivityInfo
import android.content.pm.ApplicationInfo
import android.content.pm.PackageManager
import android.content.pm.ResolveInfo
import android.content.pm.ShortcutInfo
import android.graphics.drawable.Icon
import android.os.Bundle
import android.os.UserHandle
import android.service.chooser.ChooserTarget
@@ -32,12 +36,15 @@ import androidx.test.ext.junit.runners.AndroidJUnit4
import androidx.test.platform.app.InstrumentationRegistry
import com.android.internal.R
import com.android.internal.app.ChooserListAdapter.LoadDirectShareIconTask
import com.android.internal.app.chooser.DisplayResolveInfo
import com.android.internal.app.chooser.SelectableTargetInfo
import com.android.internal.app.chooser.SelectableTargetInfo.SelectableTargetInfoCommunicator
import com.android.internal.app.chooser.TargetInfo
import com.android.server.testutils.any
import com.android.server.testutils.mock
import com.android.server.testutils.whenever
import com.google.common.truth.Truth.assertThat
import org.junit.Before
import org.junit.Test
import org.junit.runner.RunWith
import org.mockito.Mockito.anyInt
@@ -46,22 +53,25 @@ import org.mockito.Mockito.verify

@RunWith(AndroidJUnit4::class)
class ChooserListAdapterTest {
    private val packageManager = mock<PackageManager> {
        whenever(resolveActivity(any(), anyInt())).thenReturn(mock())
    }
    private val packageManager =
        mock<PackageManager> { whenever(resolveActivity(any(), anyInt())).thenReturn(mock()) }
    private val context = InstrumentationRegistry.getInstrumentation().getContext()
    private val resolverListController = mock<ResolverListController>()
    private val chooserListCommunicator = mock<ChooserListAdapter.ChooserListCommunicator> {
    private val chooserListCommunicator =
        mock<ChooserListAdapter.ChooserListCommunicator> {
            whenever(maxRankedTargets).thenReturn(0)
        }
    private val selectableTargetInfoCommunicator =
        mock<SelectableTargetInfoCommunicator> {
            whenever(targetIntent).thenReturn(mock())
        }
        mock<SelectableTargetInfoCommunicator> { whenever(targetIntent).thenReturn(mock()) }
    private val chooserActivityLogger = mock<ChooserActivityLogger>()

    @Before
    fun setUp() {
        whenever(resolverListController.userHandle).thenReturn(UserHandle.CURRENT)
    }

    private fun createChooserListAdapter(
        taskProvider: (SelectableTargetInfo?) -> LoadDirectShareIconTask
        taskProvider: (SelectableTargetInfo?) -> LoadDirectShareIconTask = createTaskProvider()
    ) =
        ChooserListAdapterOverride(
            context,
@@ -98,9 +108,8 @@ class ChooserListAdapterTest {
        view.tag = viewHolderOne
        val targetInfo = createSelectableTargetInfo()
        val iconTaskOne = mock<LoadDirectShareIconTask>()
        val testTaskProvider = mock<() -> LoadDirectShareIconTask> {
            whenever(invoke()).thenReturn(iconTaskOne)
        }
        val testTaskProvider =
            mock<() -> LoadDirectShareIconTask> { whenever(invoke()).thenReturn(iconTaskOne) }
        val testSubject = createChooserListAdapter { testTaskProvider.invoke() }
        testSubject.testViewBind(view, targetInfo, 0)

@@ -114,6 +123,65 @@ class ChooserListAdapterTest {
        verify(testTaskProvider, times(1)).invoke()
    }

    @Test
    fun getServiceTargetCount_shouldNotShowServiceTargets_returnsZero() {
        whenever(chooserListCommunicator.shouldShowServiceTargets()).thenReturn(false)
        val adapter = createChooserListAdapter()
        whenever(chooserListCommunicator.maxRankedTargets).thenReturn(10)
        addServiceTargets(adapter, targetCount = 50)

        assertThat(adapter.serviceTargetCount).isEqualTo(0)
    }

    private fun createTaskProvider(): (SelectableTargetInfo?) -> LoadDirectShareIconTask {
        val iconTaskOne = mock<LoadDirectShareIconTask>()
        val testTaskProvider =
            mock<() -> LoadDirectShareIconTask> { whenever(invoke()).thenReturn(iconTaskOne) }
        return { testTaskProvider.invoke() }
    }

    private fun addServiceTargets(adapter: ChooserListAdapter, targetCount: Int) {
        val origTarget =
            DisplayResolveInfo(
                Intent(),
                createResolveInfo(),
                Intent(),
                ResolverListAdapter.ResolveInfoPresentationGetter(context, 200, createResolveInfo())
            )
        val targets = mutableListOf<ChooserTarget>()
        for (i in 1..targetCount) {
            val score = 1f
            val componentName = ComponentName("chooser.list.adapter", "Test$i")
            val extras = Bundle()
            val icon: Icon? = null
            targets += ChooserTarget("Title $i", icon, score, componentName, extras)
        }
        val directShareToShortcutInfos = mapOf<ChooserTarget, ShortcutInfo>()
        adapter.addServiceResults(
            origTarget,
            targets,
            ChooserActivity.TARGET_TYPE_DEFAULT,
            directShareToShortcutInfos
        )
    }

    private fun createResolveInfo(): ResolveInfo {
        val applicationInfo =
            ApplicationInfo().apply {
                packageName = "chooser.list.adapter"
                name = "ChooserListAdapterTestApplication"
            }
        val activityInfo =
            ActivityInfo().apply {
                packageName = applicationInfo.packageName
                name = "ChooserListAdapterTest"
            }
        activityInfo.applicationInfo = applicationInfo
        val resolveInfo = ResolveInfo()
        resolveInfo.activityInfo = activityInfo
        return resolveInfo
    }

    private fun createSelectableTargetInfo(): SelectableTargetInfo =
        SelectableTargetInfo(
            context,
@@ -125,13 +193,7 @@ class ChooserListAdapterTest {
        )

    private fun createChooserTarget(): ChooserTarget =
        ChooserTarget(
            "Title",
            null,
            1f,
            ComponentName("package", "package.Class"),
            Bundle()
        )
        ChooserTarget("Title", null, 1f, ComponentName("package", "package.Class"), Bundle())

    private fun createView(): View {
        val view = FrameLayout(context)
@@ -164,7 +226,8 @@ private class ChooserListAdapterOverride(
    chooserActivityLogger: ChooserActivityLogger?,
    initialIntentsUserHandle: UserHandle?,
    private val taskProvider: (SelectableTargetInfo?) -> LoadDirectShareIconTask
) : ChooserListAdapter(
) :
    ChooserListAdapter(
        context,
        payloadIntents,
        initialIntents,
@@ -179,8 +242,7 @@ private class ChooserListAdapterOverride(
    ) {
    override fun createLoadDirectShareIconTask(
        info: SelectableTargetInfo?
    ): LoadDirectShareIconTask =
        taskProvider.invoke(info)
    ): LoadDirectShareIconTask = taskProvider.invoke(info)

    fun testViewBind(view: View?, info: TargetInfo?, position: Int) {
        onBindView(view, info, position)
+3 −1
Original line number Diff line number Diff line
@@ -274,7 +274,9 @@ class MediaProjectionAppSelectorActivity(
            recentsViewController.hasRecentTasks
        }

    override fun shouldShowContentPreviewWhenEmpty() = shouldShowContentPreview()
    override fun shouldShowStickyContentPreviewWhenEmpty() = shouldShowContentPreview()

    override fun shouldShowServiceTargets() = false

    private fun hasWorkProfile() = mMultiProfilePagerAdapter.count > 1