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

Commit b74d1d59 authored by Winson Chung's avatar Winson Chung Committed by Android (Google) Code Review
Browse files

Merge "Enforce that app & launchable intent drags are mututally exclusive" into main

parents 2a029127 9bf5601b
Loading
Loading
Loading
Loading
+6 −2
Original line number Diff line number Diff line
@@ -133,7 +133,11 @@ public class DragSession {
        // TODO: This should technically check & respect config_supportsNonResizableMultiWindow
        dragItemSupportsSplitscreen = activityInfo == null
                || ActivityInfo.isResizeableMode(activityInfo.resizeMode);
        appData = mInitialDragData.getItemAt(0).getIntent();
        launchableIntent = DragUtils.getLaunchIntent(mInitialDragData, mInitialDragFlags);
        appData = DragUtils.isAppDrag(getClipDescription())
                ? mInitialDragData.getItemAt(0).getIntent()
                : null;
        launchableIntent = appData != null
                ? null
                : DragUtils.getLaunchIntent(mInitialDragData, mInitialDragFlags);
    }
}
+125 −25
Original line number Diff line number Diff line
@@ -20,23 +20,23 @@ import android.app.WindowConfiguration.ACTIVITY_TYPE_STANDARD
import android.app.WindowConfiguration.WINDOWING_MODE_FULLSCREEN
import android.app.WindowConfiguration.WINDOWING_MODE_MULTI_WINDOW
import android.app.WindowConfiguration.WINDOWING_MODE_PINNED
import android.content.ClipDescription
import android.content.ClipDescription.EXTRA_HIDE_DRAG_SOURCE_TASK_ID
import android.content.ClipDescription.MIMETYPE_APPLICATION_ACTIVITY
import android.content.ClipDescription.MIMETYPE_APPLICATION_SHORTCUT
import android.os.PersistableBundle
import android.os.RemoteException
import android.testing.AndroidTestingRunner
import android.view.View.DRAG_FLAG_START_INTENT_SENDER_ON_UNHANDLED_DRAG
import androidx.test.filters.SmallTest
import com.android.wm.shell.ShellTestCase
import com.android.wm.shell.common.DisplayLayout
import com.android.wm.shell.draganddrop.DragTestUtils.createTaskInfo
import com.google.common.truth.Truth.assertThat
import org.junit.Before
import org.junit.Test
import org.junit.runner.RunWith
import org.mockito.Mock
import org.mockito.MockitoAnnotations
import org.mockito.kotlin.any
import org.mockito.kotlin.whenever
import org.mockito.kotlin.doReturn
import org.mockito.kotlin.mock
import org.mockito.kotlin.stub

/**
 * Tests for DragSession.
@@ -46,22 +46,15 @@ import org.mockito.kotlin.whenever
@SmallTest
@RunWith(AndroidTestingRunner::class)
class DragSessionTest : ShellTestCase() {
    @Mock
    private lateinit var activityTaskManager: ActivityTaskManager

    @Mock
    private lateinit var displayLayout: DisplayLayout

    @Before
    @Throws(RemoteException::class)
    fun setUp() {
        MockitoAnnotations.initMocks(this)
    }
    private val activityTaskManager = mock<ActivityTaskManager>()
    private val displayLayout = mock<DisplayLayout>()

    @Test
    fun testNullClipData() {
        // Start a new drag session with null data
        val session = DragSession(activityTaskManager, displayLayout, null, 0)

        assertThat(session.hideDragSourceTaskId).isEqualTo(-1)
    }

@@ -71,10 +64,12 @@ class DragSessionTest : ShellTestCase() {
        val runningTasks = listOf(
            createTaskInfo(WINDOWING_MODE_FULLSCREEN, ACTIVITY_TYPE_STANDARD),
        )
        whenever(activityTaskManager.getTasks(any(), any())).thenReturn(runningTasks)
        activityTaskManager.stub {
            on { getTasks(any(), any()) } doReturn runningTasks
        }

        // Simulate dragging an app
        val data = DragTestUtils.createAppClipData(ClipDescription.MIMETYPE_APPLICATION_SHORTCUT)
        // Set up for dragging an app
        val data = DragTestUtils.createAppClipData(MIMETYPE_APPLICATION_SHORTCUT)

        // Start a new drag session
        val session = DragSession(activityTaskManager, displayLayout, data, 0)
@@ -93,10 +88,12 @@ class DragSessionTest : ShellTestCase() {
            createTaskInfo(WINDOWING_MODE_PINNED, ACTIVITY_TYPE_STANDARD),
            createTaskInfo(WINDOWING_MODE_MULTI_WINDOW, ACTIVITY_TYPE_STANDARD, alwaysOnTop=true),
        )
        whenever(activityTaskManager.getTasks(any(), any())).thenReturn(runningTasks)
        activityTaskManager.stub {
            on { getTasks(any(), any()) } doReturn runningTasks
        }

        // Simulate dragging an app
        val data = DragTestUtils.createAppClipData(ClipDescription.MIMETYPE_APPLICATION_SHORTCUT)
        // Set up for dragging an app
        val data = DragTestUtils.createAppClipData(MIMETYPE_APPLICATION_SHORTCUT)

        // Start a new drag session
        val session = DragSession(activityTaskManager, displayLayout, data, 0)
@@ -115,10 +112,12 @@ class DragSessionTest : ShellTestCase() {
            createTaskInfo(WINDOWING_MODE_FULLSCREEN, ACTIVITY_TYPE_STANDARD),
            createTaskInfo(WINDOWING_MODE_FULLSCREEN, ACTIVITY_TYPE_STANDARD),
        )
        whenever(activityTaskManager.getTasks(any(), any())).thenReturn(runningTasks)
        activityTaskManager.stub {
            on { getTasks(any(), any()) } doReturn runningTasks
        }

        // Simulate dragging an app with hide-drag-source set for the second (top most) app
        val data = DragTestUtils.createAppClipData(ClipDescription.MIMETYPE_APPLICATION_SHORTCUT)
        // Set up for dragging an app
        val data = DragTestUtils.createAppClipData(MIMETYPE_APPLICATION_SHORTCUT)
        data.description.extras =
            PersistableBundle().apply {
                putInt(
@@ -133,4 +132,105 @@ class DragSessionTest : ShellTestCase() {

        assertThat(session.hideDragSourceTaskId).isEqualTo(runningTasks.last().taskId)
    }

    @Test
    fun testAppData() {
        // Set up running tasks
        val runningTasks = listOf(
            createTaskInfo(WINDOWING_MODE_FULLSCREEN, ACTIVITY_TYPE_STANDARD),
            createTaskInfo(WINDOWING_MODE_FULLSCREEN, ACTIVITY_TYPE_STANDARD),
        )
        activityTaskManager.stub {
            on { getTasks(any(), any()) } doReturn runningTasks
        }

        // Set up for dragging with app data
        val data = DragTestUtils.createAppClipData(MIMETYPE_APPLICATION_ACTIVITY)

        // Start a new drag session
        val session = DragSession(activityTaskManager, displayLayout, data, 0)
        session.initialize(true /* skipUpdateRunningTask */)

        assertThat(session.appData).isNotNull()
        assertThat(session.launchableIntent).isNull()
    }

    @Test
    fun testLaunchableIntent() {
        // Set up running tasks
        val runningTasks = listOf(
            createTaskInfo(WINDOWING_MODE_FULLSCREEN, ACTIVITY_TYPE_STANDARD),
            createTaskInfo(WINDOWING_MODE_FULLSCREEN, ACTIVITY_TYPE_STANDARD),
        )
        activityTaskManager.stub {
            on { getTasks(any(), any()) } doReturn runningTasks
        }
        val pendingIntent = DragTestUtils.createLaunchableIntent(mContext)

        // Set up for dragging with a launchable intent
        val data = DragTestUtils.createIntentClipData(pendingIntent)

        // Start a new drag session
        val session = DragSession(activityTaskManager, displayLayout, data,
            DRAG_FLAG_START_INTENT_SENDER_ON_UNHANDLED_DRAG)
        session.initialize(true /* skipUpdateRunningTask */)

        assertThat(session.appData).isNull()
        assertThat(session.launchableIntent).isNotNull()
    }

    @Test
    fun testBothValidAppDataAndLaunchableIntent_launchableIntentIsNull() {
        // Set up running tasks
        val runningTasks = listOf(
            createTaskInfo(WINDOWING_MODE_FULLSCREEN, ACTIVITY_TYPE_STANDARD),
            createTaskInfo(WINDOWING_MODE_FULLSCREEN, ACTIVITY_TYPE_STANDARD),
        )
        activityTaskManager.stub {
            on { getTasks(any(), any()) } doReturn runningTasks
        }
        val pendingIntent = DragTestUtils.createLaunchableIntent(mContext)

        // Set up for dragging data with both and app-data intent and a laucnhable intent
        val launchData = DragTestUtils.createIntentClipData(pendingIntent)
        val data = DragTestUtils.createAppClipData(MIMETYPE_APPLICATION_ACTIVITY)
        data.addItem(launchData.getItemAt(0))

        // Start a new drag session
        val session = DragSession(activityTaskManager, displayLayout, data,
            DRAG_FLAG_START_INTENT_SENDER_ON_UNHANDLED_DRAG)
        session.initialize(true /* skipUpdateRunningTask */)

        // Since the app data is valid, we prioritize using that
        assertThat(session.appData).isNotNull()
        assertThat(session.launchableIntent).isNull()
    }

    @Test
    fun testInvalidAppDataWithValidLaunchableIntent_appDataIsNull() {
        // Set up running tasks
        val runningTasks = listOf(
            createTaskInfo(WINDOWING_MODE_FULLSCREEN, ACTIVITY_TYPE_STANDARD),
            createTaskInfo(WINDOWING_MODE_FULLSCREEN, ACTIVITY_TYPE_STANDARD),
        )
        activityTaskManager.stub {
            on { getTasks(any(), any()) } doReturn runningTasks
        }
        val pendingIntent = DragTestUtils.createLaunchableIntent(mContext)

        // Set up for dragging data with an unknown mime type, this should not be treated as a valid
        // app drag
        val launchData = DragTestUtils.createIntentClipData(pendingIntent)
        val data = DragTestUtils.createAppClipData("unknown_mime_type")
        data.addItem(launchData.getItemAt(0))

        // Start a new drag session
        val session = DragSession(activityTaskManager, displayLayout, data,
            DRAG_FLAG_START_INTENT_SENDER_ON_UNHANDLED_DRAG)
        session.initialize(true /* skipUpdateRunningTask */)

        // Since the app data is invalid, we use the launchable intent
        assertThat(session.appData).isNull()
        assertThat(session.launchableIntent).isNotNull()
    }
}
+14 −1
Original line number Diff line number Diff line
@@ -17,15 +17,17 @@ package com.android.wm.shell.draganddrop

import android.app.ActivityManager
import android.app.PendingIntent
import android.app.PendingIntent.FLAG_IMMUTABLE
import android.content.ClipData
import android.content.ClipDescription
import android.content.ComponentName
import android.content.Context
import android.content.Intent
import android.content.pm.ActivityInfo
import android.content.pm.ApplicationInfo
import android.os.Process
import java.util.Random
import org.mockito.Mockito
import java.util.Random

/**
 * Convenience methods for drag tests.
@@ -79,6 +81,17 @@ object DragTestUtils {
        return data
    }

    /**
     * Creates a new launchable intent.
     */
    @JvmStatic
    fun createLaunchableIntent(context: Context) : PendingIntent {
        val fakeActivityIntent = Intent()
        fakeActivityIntent.component = ComponentName("com.android.wm.shell.draganddrop",
            "FakeActivity")
        return PendingIntent.getActivity(context, 0, fakeActivityIntent, FLAG_IMMUTABLE)
    }

    /**
     * Sets the given clip data to be resizeable.
     */
+24 −51
Original line number Diff line number Diff line
@@ -20,15 +20,11 @@ import static android.app.WindowConfiguration.ACTIVITY_TYPE_HOME;
import static android.app.WindowConfiguration.ACTIVITY_TYPE_STANDARD;
import static android.app.WindowConfiguration.WINDOWING_MODE_FULLSCREEN;
import static android.content.ClipDescription.MIMETYPE_APPLICATION_ACTIVITY;
import static android.content.ClipDescription.MIMETYPE_APPLICATION_SHORTCUT;
import static android.content.ClipDescription.MIMETYPE_APPLICATION_TASK;
import static android.view.View.DRAG_FLAG_START_INTENT_SENDER_ON_UNHANDLED_DRAG;

import static com.android.dx.mockito.inline.extended.ExtendedMockito.mockitoSession;
import static com.android.wm.shell.shared.split.SplitScreenConstants.SPLIT_INDEX_UNDEFINED;
import static com.android.wm.shell.draganddrop.DragTestUtils.createAppClipData;
import static com.android.wm.shell.draganddrop.DragTestUtils.createIntentClipData;
import static com.android.wm.shell.draganddrop.DragTestUtils.createTaskInfo;
import static com.android.wm.shell.draganddrop.DragTestUtils.setClipDataResizeable;
import static com.android.wm.shell.shared.split.SplitScreenConstants.SPLIT_POSITION_BOTTOM_OR_RIGHT;
import static com.android.wm.shell.shared.split.SplitScreenConstants.SPLIT_POSITION_TOP_OR_LEFT;
import static com.android.wm.shell.shared.split.SplitScreenConstants.SPLIT_POSITION_UNDEFINED;
@@ -50,8 +46,6 @@ import static org.mockito.Mockito.mock;
import static org.mockito.Mockito.reset;
import static org.mockito.Mockito.spy;
import static org.mockito.Mockito.verify;
import static org.mockito.Mockito.when;
import static org.mockito.quality.Strictness.LENIENT;

import android.app.ActivityManager;
import android.app.ActivityTaskManager;
@@ -73,7 +67,6 @@ import com.android.wm.shell.common.DisplayLayout;
import com.android.wm.shell.draganddrop.SplitDragPolicy.Target;
import com.android.wm.shell.splitscreen.SplitScreenController;

import org.junit.After;
import org.junit.Before;
import org.junit.Test;
import org.junit.runner.RunWith;
@@ -112,13 +105,6 @@ public class SplitDragPolicyTest extends ShellTestCase {
    private Insets mInsets;
    private SplitDragPolicy mPolicy;

    private ClipData mActivityClipData;
    private PendingIntent mLaunchableIntentPendingIntent;
    private ClipData mLaunchableIntentClipData;
    private ClipData mNonResizeableActivityClipData;
    private ClipData mTaskClipData;
    private ClipData mShortcutClipData;

    private ActivityManager.RunningTaskInfo mHomeTask;
    private ActivityManager.RunningTaskInfo mFullscreenAppTask;
    private ActivityManager.RunningTaskInfo mNonResizeableFullscreenAppTask;
@@ -128,11 +114,6 @@ public class SplitDragPolicyTest extends ShellTestCase {
    @Before
    public void setUp() throws RemoteException {
        MockitoAnnotations.initMocks(this);
        mMockitoSession = mockitoSession()
                .strictness(LENIENT)
                .mockStatic(DragUtils.class)
                .startMocking();
        when(DragUtils.canHandleDrag(any())).thenReturn(true);

        Resources res = mock(Resources.class);
        Configuration config = new Configuration();
@@ -150,15 +131,6 @@ public class SplitDragPolicyTest extends ShellTestCase {

        mPolicy = spy(new SplitDragPolicy(mContext, mSplitScreenStarter, mFullscreenStarter,
                mock(DragZoneAnimator.class)));
        mActivityClipData = createAppClipData(MIMETYPE_APPLICATION_ACTIVITY);
        mLaunchableIntentPendingIntent = mock(PendingIntent.class);
        when(mLaunchableIntentPendingIntent.getCreatorUserHandle())
                .thenReturn(android.os.Process.myUserHandle());
        mLaunchableIntentClipData = createIntentClipData(mLaunchableIntentPendingIntent);
        mNonResizeableActivityClipData = createAppClipData(MIMETYPE_APPLICATION_ACTIVITY);
        setClipDataResizeable(mNonResizeableActivityClipData, false);
        mTaskClipData = createAppClipData(MIMETYPE_APPLICATION_TASK);
        mShortcutClipData = createAppClipData(MIMETYPE_APPLICATION_SHORTCUT);

        mHomeTask = createTaskInfo(WINDOWING_MODE_FULLSCREEN, ACTIVITY_TYPE_HOME);
        mFullscreenAppTask = createTaskInfo(WINDOWING_MODE_FULLSCREEN, ACTIVITY_TYPE_STANDARD);
@@ -169,11 +141,6 @@ public class SplitDragPolicyTest extends ShellTestCase {
        setRunningTask(mFullscreenAppTask);
    }

    @After
    public void tearDown() {
        mMockitoSession.finishMocking();
    }

    private void setRunningTask(ActivityManager.RunningTaskInfo task) {
        doReturn(Collections.singletonList(task)).when(mActivityTaskManager)
                .getTasks(anyInt(), anyBoolean());
@@ -181,45 +148,48 @@ public class SplitDragPolicyTest extends ShellTestCase {

    @Test
    public void testDragAppOverFullscreenHome_expectOnlyFullscreenTarget() {
        dragOverFullscreenHome_expectOnlyFullscreenTarget(mActivityClipData);
        final ClipData data = createAppClipData(MIMETYPE_APPLICATION_ACTIVITY);
        dragOverFullscreenHome_expectOnlyFullscreenTarget(data);
    }

    @Test
    public void testDragAppOverFullscreenApp_expectSplitScreenTargets() {
        dragOverFullscreenApp_expectSplitScreenTargets(mActivityClipData);
        final ClipData data = createAppClipData(MIMETYPE_APPLICATION_ACTIVITY);
        dragOverFullscreenApp_expectSplitScreenTargets(data);
    }

    @Test
    public void testDragAppOverFullscreenAppPhone_expectVerticalSplitScreenTargets() {
        dragOverFullscreenAppPhone_expectVerticalSplitScreenTargets(mActivityClipData);
        final ClipData data = createAppClipData(MIMETYPE_APPLICATION_ACTIVITY);
        dragOverFullscreenAppPhone_expectVerticalSplitScreenTargets(data);
    }

    @Test
    public void testDragIntentOverFullscreenHome_expectOnlyFullscreenTarget() {
        when(DragUtils.getLaunchIntent((ClipData) any(), anyInt())).thenReturn(
                mLaunchableIntentPendingIntent);
        dragOverFullscreenHome_expectOnlyFullscreenTarget(mLaunchableIntentClipData);
        final PendingIntent pendingIntent = DragTestUtils.createLaunchableIntent(super.mContext);
        final ClipData data = DragTestUtils.createIntentClipData(pendingIntent);
        dragOverFullscreenHome_expectOnlyFullscreenTarget(data);
    }

    @Test
    public void testDragIntentOverFullscreenApp_expectSplitScreenTargets() {
        when(DragUtils.getLaunchIntent((ClipData) any(), anyInt())).thenReturn(
                mLaunchableIntentPendingIntent);
        dragOverFullscreenApp_expectSplitScreenTargets(mLaunchableIntentClipData);
        final PendingIntent pendingIntent = DragTestUtils.createLaunchableIntent(super.mContext);
        final ClipData data = DragTestUtils.createIntentClipData(pendingIntent);
        dragOverFullscreenApp_expectSplitScreenTargets(data);
    }

    @Test
    public void testDragIntentOverFullscreenAppPhone_expectVerticalSplitScreenTargets() {
        when(DragUtils.getLaunchIntent((ClipData) any(), anyInt())).thenReturn(
                mLaunchableIntentPendingIntent);
        dragOverFullscreenAppPhone_expectVerticalSplitScreenTargets(mLaunchableIntentClipData);
        final PendingIntent pendingIntent = DragTestUtils.createLaunchableIntent(super.mContext);
        final ClipData data = DragTestUtils.createIntentClipData(pendingIntent);
        dragOverFullscreenAppPhone_expectVerticalSplitScreenTargets(data);
    }

    private void dragOverFullscreenHome_expectOnlyFullscreenTarget(ClipData data) {
        doReturn(true).when(mSplitScreenStarter).isLeftRightSplit();
        setRunningTask(mHomeTask);
        DragSession dragSession = new DragSession(mActivityTaskManager,
                mLandscapeDisplayLayout, data, 0 /* dragFlags */);
                mLandscapeDisplayLayout, data, DRAG_FLAG_START_INTENT_SENDER_ON_UNHANDLED_DRAG);
        dragSession.initialize(false /* skipUpdateRunningTask */);
        mPolicy.start(dragSession, mLoggerSessionId);
        ArrayList<Target> targets = assertExactTargetTypes(
@@ -234,7 +204,7 @@ public class SplitDragPolicyTest extends ShellTestCase {
        doReturn(true).when(mSplitScreenStarter).isLeftRightSplit();
        setRunningTask(mFullscreenAppTask);
        DragSession dragSession = new DragSession(mActivityTaskManager,
                mLandscapeDisplayLayout, data, 0 /* dragFlags */);
                mLandscapeDisplayLayout, data, DRAG_FLAG_START_INTENT_SENDER_ON_UNHANDLED_DRAG);
        dragSession.initialize(false /* skipUpdateRunningTask */);
        mPolicy.start(dragSession, mLoggerSessionId);
        ArrayList<Target> targets = assertExactTargetTypes(
@@ -254,7 +224,7 @@ public class SplitDragPolicyTest extends ShellTestCase {
        doReturn(false).when(mSplitScreenStarter).isLeftRightSplit();
        setRunningTask(mFullscreenAppTask);
        DragSession dragSession = new DragSession(mActivityTaskManager,
                mPortraitDisplayLayout, data, 0 /* dragFlags */);
                mPortraitDisplayLayout, data, DRAG_FLAG_START_INTENT_SENDER_ON_UNHANDLED_DRAG);
        dragSession.initialize(false /* skipUpdateRunningTask */);
        mPolicy.start(dragSession, mLoggerSessionId);
        ArrayList<Target> targets = assertExactTargetTypes(
@@ -273,9 +243,10 @@ public class SplitDragPolicyTest extends ShellTestCase {

    @Test
    public void testTargetHitRects() {
        final ClipData data = createAppClipData(MIMETYPE_APPLICATION_ACTIVITY);
        setRunningTask(mFullscreenAppTask);
        DragSession dragSession = new DragSession(mActivityTaskManager,
                mLandscapeDisplayLayout, mActivityClipData, 0 /* dragFlags */);
                mLandscapeDisplayLayout, data, 0 /* dragFlags */);
        dragSession.initialize(false /* skipUpdateRunningTask */);
        mPolicy.start(dragSession, mLoggerSessionId);
        ArrayList<Target> targets = mPolicy.getTargets(mInsets);
@@ -291,7 +262,9 @@ public class SplitDragPolicyTest extends ShellTestCase {

    @Test
    public void testDisallowLaunchIntentWithoutDelegationFlag() {
        assertTrue(DragUtils.getLaunchIntent(mLaunchableIntentClipData, 0) == null);
        final PendingIntent pendingIntent = DragTestUtils.createLaunchableIntent(super.mContext);
        final ClipData data = DragTestUtils.createIntentClipData(pendingIntent);
        assertTrue(DragUtils.getLaunchIntent(data, 0) == null);
    }

    private Target filterTargetByType(ArrayList<Target> targets, int type) {