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

Commit 16d9a561 authored by Daichi Hirono's avatar Daichi Hirono
Browse files

Finish drag when cancel transaction is aborted

Without this CL, in a certain case,
TRANSIT_DESKTOP_MODE_START_DRAG_TO_DESKTOP never ends after a user
completes drag operations, and the app becomes unresponsible. This CL
is to add missing cleanup.

Currently when a user starts dragging the fullscreen window handle to
freeform, then drags back to the fullscreen, there are two window
transactions involving this.

- TRANSIT_DESKTOP_MODE_START_DRAG_TO_DESKTOP starts when a user starts
  drag and ends when a user completes drag.
- TRANSIT_DESKTOP_MODE_CANCEL_DRAG_TO_DESKTOP when a user ends the drag
  after they drag back to fullscreen.

Because the later starts when the former is being played, later is
either 1) merged to former, or 2) aborted when it's noop. For the merge
case, we finishes the former transaction in the `mergeAnimation`
callback. For the abort case, it did nothing in the
`onTransitionConsumed` callback before the CL.

Bug: 370596057
Flag: EXEMPT bugfix
Test: Follow the repro step in the above bug
Change-Id: Icfc40ba8f6193bed2ded8f7095fba95b9b60aad8
parent 68245f16
Loading
Loading
Loading
Loading
+9 −2
Original line number Diff line number Diff line
@@ -648,7 +648,13 @@ sealed class DragToDesktopTransitionHandler(
            state.startAborted = true
            // The start-transition (DRAG_HOLD) is aborted, cancel its jank interaction.
            interactionJankMonitor.cancel(CUJ_DESKTOP_MODE_ENTER_APP_HANDLE_DRAG_HOLD)
        } else if (state.cancelTransitionToken != transition) {
        } else if (state.cancelTransitionToken == transition) {
            state.draggedTaskChange?.leash?.let {
                state.startTransitionFinishTransaction?.show(it)
            }
            state.startTransitionFinishCb?.onTransitionFinished(null /* wct */)
            clearState()
        } else {
            // This transition being aborted is neither the start, nor the cancel transition, so
            // it must be the finish transition (DRAG_RELEASE); cancel its jank interaction.
            interactionJankMonitor.cancel(CUJ_DESKTOP_MODE_ENTER_APP_HANDLE_DRAG_RELEASE)
@@ -863,7 +869,8 @@ sealed class DragToDesktopTransitionHandler(

    companion object {
        /** The duration of the animation to commit or cancel the drag-to-desktop gesture. */
        internal const val DRAG_TO_DESKTOP_FINISH_ANIM_DURATION_MS = 336L
        @VisibleForTesting(visibility = VisibleForTesting.Visibility.PACKAGE)
        const val DRAG_TO_DESKTOP_FINISH_ANIM_DURATION_MS = 336L
    }
}

+48 −5
Original line number Diff line number Diff line
package com.android.wm.shell.desktopmode

import android.animation.AnimatorTestRule
import android.app.ActivityManager.RunningTaskInfo
import android.app.WindowConfiguration.ACTIVITY_TYPE_HOME
import android.app.WindowConfiguration.ACTIVITY_TYPE_STANDARD
@@ -24,6 +25,7 @@ import com.android.internal.jank.InteractionJankMonitor
import com.android.wm.shell.RootTaskDisplayAreaOrganizer
import com.android.wm.shell.ShellTestCase
import com.android.wm.shell.TestRunningTaskInfoBuilder
import com.android.wm.shell.desktopmode.DragToDesktopTransitionHandler.Companion.DRAG_TO_DESKTOP_FINISH_ANIM_DURATION_MS
import com.android.wm.shell.shared.split.SplitScreenConstants.SPLIT_POSITION_BOTTOM_OR_RIGHT
import com.android.wm.shell.shared.split.SplitScreenConstants.SPLIT_POSITION_TOP_OR_LEFT
import com.android.wm.shell.splitscreen.SplitScreenController
@@ -38,6 +40,7 @@ import junit.framework.Assert.assertFalse
import junit.framework.Assert.assertTrue
import org.junit.After
import org.junit.Before
import org.junit.Rule
import org.junit.Test
import org.junit.runner.RunWith
import org.mockito.ArgumentMatchers.any
@@ -58,6 +61,9 @@ import org.mockito.quality.Strictness
@RunWithLooper
@RunWith(AndroidTestingRunner::class)
class DragToDesktopTransitionHandlerTest : ShellTestCase() {
    @JvmField
    @Rule
    val mAnimatorTestRule = AnimatorTestRule(this)

    @Mock private lateinit var transitions: Transitions
    @Mock private lateinit var taskDisplayAreaOrganizer: RootTaskDisplayAreaOrganizer
@@ -267,16 +273,36 @@ class DragToDesktopTransitionHandlerTest : ShellTestCase() {
    }

    @Test
    fun cancelDragToDesktop_startWasReady_cancel() {
        startDrag(defaultHandler)
    fun cancelDragToDesktop_startWasReady_cancel_merged() {
        val startToken = startDrag(defaultHandler)

        // Then user cancelled after it had already started.
        defaultHandler.cancelDragToDesktopTransition(
            DragToDesktopTransitionHandler.CancelState.STANDARD_CANCEL
        )
        val cancelToken = cancelDragToDesktopTransition(
            defaultHandler, DragToDesktopTransitionHandler.CancelState.STANDARD_CANCEL)
        defaultHandler.mergeAnimation(
            cancelToken,
            TransitionInfo(TRANSIT_DESKTOP_MODE_CANCEL_DRAG_TO_DESKTOP, 0),
            mock<SurfaceControl.Transaction>(),
            startToken,
            mock<Transitions.TransitionFinishCallback>())

        // Cancel animation should run since it had already started.
        verify(dragAnimator).cancelAnimator()
        assertFalse("Drag should not be in progress after cancelling", defaultHandler.inProgress)
    }

    @Test
    fun cancelDragToDesktop_startWasReady_cancel_aborted() {
        val startToken = startDrag(defaultHandler)

        // Then user cancelled after it had already started.
        val cancelToken = cancelDragToDesktopTransition(
            defaultHandler, DragToDesktopTransitionHandler.CancelState.STANDARD_CANCEL)
        defaultHandler.onTransitionConsumed(cancelToken, aborted = true, null)

        // Cancel animation should run since it had already started.
        verify(dragAnimator).cancelAnimator()
        assertFalse("Drag should not be in progress after cancelling", defaultHandler.inProgress)
    }

    @Test
@@ -585,6 +611,23 @@ class DragToDesktopTransitionHandlerTest : ShellTestCase() {
        return token
    }

    private fun cancelDragToDesktopTransition(
        handler: DragToDesktopTransitionHandler,
        cancelState: DragToDesktopTransitionHandler.CancelState): IBinder {
        val token = mock<IBinder>()
        whenever(
                transitions.startTransition(
                    eq(TRANSIT_DESKTOP_MODE_CANCEL_DRAG_TO_DESKTOP),
                    any(),
                    eq(handler)
                )
            )
            .thenReturn(token)
        handler.cancelDragToDesktopTransition(cancelState)
        mAnimatorTestRule.advanceTimeBy(DRAG_TO_DESKTOP_FINISH_ANIM_DURATION_MS)
        return token
    }

    private fun performEarlyCancel(
        handler: DragToDesktopTransitionHandler,
        cancelState: DragToDesktopTransitionHandler.CancelState