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

Commit 82b6fdcc authored by Jorge Gil's avatar Jorge Gil
Browse files

Opt-in desktop mode's drag PendingIntent for BAL

Otherwise the transient launch of Home is not executed which prevents
the drag-to-desktop transition from being usable.

Also adds handling of aborted transitions to recover to a usable state
instead of crashing the system.

Bug: 312507707
Bug: 301106941
Test: atest DragToDesktopTransitionHandlerTest
Test: manual - drag from fullscreen, see animation and transition work
Change-Id: I59799510350d89556a83f108a12dd3485bffade6
parent 685e3896
Loading
Loading
Loading
Loading
+41 −2
Original line number Diff line number Diff line
@@ -25,12 +25,14 @@ import android.window.TransitionRequestInfo
import android.window.WindowContainerToken
import android.window.WindowContainerTransaction
import com.android.wm.shell.RootTaskDisplayAreaOrganizer
import com.android.wm.shell.protolog.ShellProtoLogGroup
import com.android.wm.shell.splitscreen.SplitScreenController
import com.android.wm.shell.transition.Transitions
import com.android.wm.shell.transition.Transitions.TRANSIT_DESKTOP_MODE_CANCEL_DRAG_TO_DESKTOP
import com.android.wm.shell.transition.Transitions.TRANSIT_DESKTOP_MODE_END_DRAG_TO_DESKTOP
import com.android.wm.shell.transition.Transitions.TRANSIT_DESKTOP_MODE_START_DRAG_TO_DESKTOP
import com.android.wm.shell.transition.Transitions.TransitionHandler
import com.android.wm.shell.util.KtProtoLog
import com.android.wm.shell.util.TransitionUtil
import com.android.wm.shell.windowdecor.DesktopModeWindowDecoration
import com.android.wm.shell.windowdecor.MoveToDesktopAnimator
@@ -68,6 +70,10 @@ class DragToDesktopTransitionHandler(
    private var splitScreenController: SplitScreenController? = null
    private var transitionState: TransitionState? = null

    /** Whether a drag-to-desktop transition is in progress. */
    val inProgress: Boolean
        get() = transitionState != null

    /** Sets a listener to receive callback about events during the transition animation. */
    fun setDragToDesktopStateListener(listener: DragToDesktopStateListener) {
        dragToDesktopStateListener = listener
@@ -92,19 +98,22 @@ class DragToDesktopTransitionHandler(
            dragToDesktopAnimator: MoveToDesktopAnimator,
            windowDecoration: DesktopModeWindowDecoration
    ) {
        if (transitionState != null) {
        if (inProgress) {
            error("A drag to desktop is already in progress")
        }

        val options = ActivityOptions.makeBasic().apply {
            setTransientLaunch()
            setSourceInfo(SourceInfo.TYPE_DESKTOP_ANIMATION, SystemClock.uptimeMillis())
            pendingIntentCreatorBackgroundActivityStartMode =
                ActivityOptions.MODE_BACKGROUND_ACTIVITY_START_ALLOWED
        }
        val pendingIntent = PendingIntent.getActivity(
                context,
                0 /* requestCode */,
                launchHomeIntent,
                FLAG_MUTABLE or FLAG_ALLOW_UNSAFE_IMPLICIT_INTENT or FILL_IN_COMPONENT
                FLAG_MUTABLE or FLAG_ALLOW_UNSAFE_IMPLICIT_INTENT or FILL_IN_COMPONENT,
                options.toBundle()
        )
        val wct = WindowContainerTransaction()
        wct.sendPendingIntent(pendingIntent, launchHomeIntent, options.toBundle())
@@ -135,6 +144,12 @@ class DragToDesktopTransitionHandler(
     * inside the desktop drop zone.
     */
    fun finishDragToDesktopTransition(wct: WindowContainerTransaction) {
        if (requireTransitionState().startAborted) {
            // Don't attempt to complete the drag-to-desktop since the start transition didn't
            // succeed as expected. Just reset the state as if nothing happened.
            clearState()
            return
        }
        transitions.startTransition(TRANSIT_DESKTOP_MODE_END_DRAG_TO_DESKTOP, wct, this)
    }

@@ -147,6 +162,12 @@ class DragToDesktopTransitionHandler(
     */
    fun cancelDragToDesktopTransition() {
        val state = requireTransitionState()
        if (state.startAborted) {
            // Don't attempt to cancel the drag-to-desktop since the start transition didn't
            // succeed as expected. Just reset the state as if nothing happened.
            clearState()
            return
        }
        state.cancelled = true
        if (state.draggedTaskChange != null) {
            // Regular case, transient launch of Home happened as is waiting for the cancel
@@ -409,6 +430,21 @@ class DragToDesktopTransitionHandler(
        return null
    }

    override fun onTransitionConsumed(
        transition: IBinder,
        aborted: Boolean,
        finishTransaction: SurfaceControl.Transaction?
    ) {
        val state = transitionState ?: return
        if (aborted && state.startTransitionToken == transition) {
            KtProtoLog.v(
                ShellProtoLogGroup.WM_SHELL_DESKTOP_MODE,
                "DragToDesktop: onTransitionConsumed() start transition aborted"
            )
            state.startAborted = true
        }
    }

    private fun isHomeChange(change: Change): Boolean {
        return change.taskInfo?.activityType == ACTIVITY_TYPE_HOME
    }
@@ -508,6 +544,7 @@ class DragToDesktopTransitionHandler(
        abstract var homeToken: WindowContainerToken?
        abstract var draggedTaskChange: Change?
        abstract var cancelled: Boolean
        abstract var startAborted: Boolean

        data class FromFullscreen(
                override val draggedTaskId: Int,
@@ -520,6 +557,7 @@ class DragToDesktopTransitionHandler(
                override var homeToken: WindowContainerToken? = null,
                override var draggedTaskChange: Change? = null,
                override var cancelled: Boolean = false,
                override var startAborted: Boolean = false,
        ) : TransitionState()
        data class FromSplit(
                override val draggedTaskId: Int,
@@ -532,6 +570,7 @@ class DragToDesktopTransitionHandler(
                override var homeToken: WindowContainerToken? = null,
                override var draggedTaskChange: Change? = null,
                override var cancelled: Boolean = false,
                override var startAborted: Boolean = false,
                var splitRootChange: Change? = null,
        ) : TransitionState()
    }
+37 −0
Original line number Diff line number Diff line
@@ -13,6 +13,7 @@ import android.testing.TestableLooper.RunWithLooper
import android.view.SurfaceControl
import android.window.TransitionInfo
import android.window.TransitionInfo.FLAG_IS_WALLPAPER
import android.window.WindowContainerTransaction
import androidx.test.filters.SmallTest
import com.android.server.testutils.any
import com.android.server.testutils.mock
@@ -22,9 +23,11 @@ import com.android.wm.shell.TestRunningTaskInfoBuilder
import com.android.wm.shell.splitscreen.SplitScreenController
import com.android.wm.shell.transition.Transitions
import com.android.wm.shell.transition.Transitions.TRANSIT_DESKTOP_MODE_CANCEL_DRAG_TO_DESKTOP
import com.android.wm.shell.transition.Transitions.TRANSIT_DESKTOP_MODE_END_DRAG_TO_DESKTOP
import com.android.wm.shell.transition.Transitions.TRANSIT_DESKTOP_MODE_START_DRAG_TO_DESKTOP
import com.android.wm.shell.windowdecor.MoveToDesktopAnimator
import java.util.function.Supplier
import junit.framework.Assert.assertFalse
import org.junit.Before
import org.junit.Test
import org.junit.runner.RunWith
@@ -112,6 +115,40 @@ class DragToDesktopTransitionHandlerTest : ShellTestCase() {
            .startTransition(eq(TRANSIT_DESKTOP_MODE_CANCEL_DRAG_TO_DESKTOP), any(), eq(handler))
    }

    @Test
    fun startDragToDesktop_aborted_finishDropped() {
        val task = createTask()
        val dragAnimator = mock<MoveToDesktopAnimator>()
        // Simulate transition is started.
        val transition = startDragToDesktopTransition(task, dragAnimator)
        // But the transition was aborted.
        handler.onTransitionConsumed(transition, aborted = true, mock())

        // Attempt to finish the failed drag start.
        handler.finishDragToDesktopTransition(WindowContainerTransaction())

        // Should not be attempted and state should be reset.
        verify(transitions, never())
                .startTransition(eq(TRANSIT_DESKTOP_MODE_END_DRAG_TO_DESKTOP), any(), any())
        assertFalse(handler.inProgress)
    }

    @Test
    fun startDragToDesktop_aborted_cancelDropped() {
        val task = createTask()
        val dragAnimator = mock<MoveToDesktopAnimator>()
        // Simulate transition is started.
        val transition = startDragToDesktopTransition(task, dragAnimator)
        // But the transition was aborted.
        handler.onTransitionConsumed(transition, aborted = true, mock())

        // Attempt to finish the failed drag start.
        handler.cancelDragToDesktopTransition()

        // Should not be attempted and state should be reset.
        assertFalse(handler.inProgress)
    }

    @Test
    fun cancelDragToDesktop_startWasReady_cancel() {
        val task = createTask()