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

Commit 38a5db4f authored by Gustav Sennton's avatar Gustav Sennton
Browse files

Drag-to-desktop transition: attach visual indicator to transition root

Attach the visual indicator to the drag-to-desktop transition root, so
the visual indicator is removed at the end of the transition regardless
of whether the indicator was released properly (we might not release the
indicator properly if the SysUI process crashes).

Note: attaching the visual indicator to the transition root only works
for the drag-to-desktop case. In the case where we drag a window inside
Desktop Mode we are not running a transition so we don't have a
transition root.

Test: used winscope to ensure indicator lives under transition root
Bug: 392826275
Flag: com.android.window.flags.enable_visual_indicator_in_transition_bugfix

Change-Id: I5d608fd11aad05addf14f06be1a2dd8d30bc8b9b
parent 6d466d3a
Loading
Loading
Loading
Loading
+22 −1
Original line number Diff line number Diff line
@@ -103,6 +103,10 @@ public class DesktopModeVisualIndicator {
                return null;
            }
        }

        private static boolean isDragToDesktopStartState(DragStartState startState) {
            return startState == FROM_FULLSCREEN || startState == FROM_SPLIT;
        }
    }

    private final VisualIndicatorViewContainer mVisualIndicatorViewContainer;
@@ -125,7 +129,12 @@ public class DesktopModeVisualIndicator {
            @Nullable BubbleDropTargetBoundsProvider bubbleBoundsProvider,
            SnapEventHandler snapEventHandler) {
        SurfaceControl.Builder builder = new SurfaceControl.Builder();
        if (!DragStartState.isDragToDesktopStartState(dragStartState)
                || !DesktopModeFlags.ENABLE_VISUAL_INDICATOR_IN_TRANSITION_BUGFIX.isTrue()) {
            // In the DragToDesktop transition we attach the indicator to the transition root once
            // that is available - for all other cases attach the indicator here.
            taskDisplayAreaOrganizer.attachToDisplayArea(taskInfo.displayId, builder);
        }
        mVisualIndicatorViewContainer = new VisualIndicatorViewContainer(
                DesktopModeFlags.ENABLE_DESKTOP_INDICATOR_IN_SEPARATE_THREAD_BUGFIX.isTrue()
                        ? desktopExecutor : mainExecutor,
@@ -159,6 +168,18 @@ public class DesktopModeVisualIndicator {
        mVisualIndicatorViewContainer.releaseVisualIndicator();
    }

    /** Reparent the visual indicator to {@code newParent}. */
    void reparentLeash(SurfaceControl.Transaction t, SurfaceControl newParent) {
        mVisualIndicatorViewContainer.reparentLeash(t, newParent);
    }

    /** Start the fade-in animation. */
    void fadeInIndicator() {
        mVisualIndicatorViewContainer.fadeInIndicator(
                mDisplayController.getDisplayLayout(mTaskInfo.displayId), mCurrentType,
                mTaskInfo.displayId);
    }

    /**
     * Based on the coordinates of the current drag event, determine which indicator type we should
     * display, including no visible indicator.
+1 −0
Original line number Diff line number Diff line
@@ -716,6 +716,7 @@ class DesktopTasksController(
        dragToDesktopTransitionHandler.startDragToDesktopTransition(
            taskInfo,
            dragToDesktopValueAnimator,
            visualIndicator,
        )
    }

+29 −2
Original line number Diff line number Diff line
@@ -25,6 +25,7 @@ import android.os.SystemProperties
import android.os.UserHandle
import android.view.SurfaceControl
import android.view.WindowManager.TRANSIT_CLOSE
import android.window.DesktopModeFlags
import android.window.TransitionInfo
import android.window.TransitionInfo.Change
import android.window.TransitionRequestInfo
@@ -118,6 +119,7 @@ sealed class DragToDesktopTransitionHandler(
    fun startDragToDesktopTransition(
        taskInfo: RunningTaskInfo,
        dragToDesktopAnimator: MoveToDesktopAnimator,
        visualIndicator: DesktopModeVisualIndicator?,
    ) {
        if (inProgress) {
            logV("Drag to desktop transition already in progress.")
@@ -163,12 +165,14 @@ sealed class DragToDesktopTransitionHandler(
                    dragAnimator = dragToDesktopAnimator,
                    startTransitionToken = startTransitionToken,
                    otherSplitTask = otherTask,
                    visualIndicator = visualIndicator,
                )
            } else {
                TransitionState.FromFullscreen(
                    draggedTaskId = taskInfo.taskId,
                    dragAnimator = dragToDesktopAnimator,
                    startTransitionToken = startTransitionToken,
                    visualIndicator = visualIndicator,
                )
            }
    }
@@ -457,6 +461,13 @@ sealed class DragToDesktopTransitionHandler(
        state.surfaceLayers = layers
        state.startTransitionFinishCb = finishCallback
        state.startTransitionFinishTransaction = finishTransaction

        val taskChange = state.draggedTaskChange ?: error("Expected non-null task change.")
        val taskInfo = taskChange.taskInfo ?: error("Expected non-null task info.")

        if (DesktopModeFlags.ENABLE_VISUAL_INDICATOR_IN_TRANSITION_BUGFIX.isTrue) {
            attachIndicatorToTransitionRoot(state, info, taskInfo, startTransaction)
        }
        startTransaction.apply()

        if (state.cancelState == CancelState.NO_CANCEL) {
@@ -485,8 +496,6 @@ sealed class DragToDesktopTransitionHandler(
                } else {
                    SPLIT_POSITION_BOTTOM_OR_RIGHT
                }
            val taskInfo =
                state.draggedTaskChange?.taskInfo ?: error("Expected non-null task info.")
            val wct = WindowContainerTransaction()
            restoreWindowOrder(wct)
            state.startTransitionFinishTransaction?.apply()
@@ -511,6 +520,21 @@ sealed class DragToDesktopTransitionHandler(
        return true
    }

    private fun attachIndicatorToTransitionRoot(
        state: TransitionState,
        info: TransitionInfo,
        taskInfo: RunningTaskInfo,
        t: SurfaceControl.Transaction,
    ) {
        val transitionRoot = info.getRoot(info.findRootIndex(taskInfo.displayId))
        state.visualIndicator?.let {
            // Attach the indicator to the transition root so that it's removed at the end of the
            // transition regardless of whether we managed to release the indicator.
            it.reparentLeash(t, transitionRoot.leash)
            it.fadeInIndicator()
        }
    }

    /**
     * Calculates start drag to desktop layers for transition [info]. The leash layer is calculated
     * based on its change position in the transition, e.g. `appLayer = appLayers - i`, where i is
@@ -901,6 +925,7 @@ sealed class DragToDesktopTransitionHandler(
        abstract var surfaceLayers: DragToDesktopLayers?
        abstract var cancelState: CancelState
        abstract var startAborted: Boolean
        abstract val visualIndicator: DesktopModeVisualIndicator?

        data class FromFullscreen(
            override val draggedTaskId: Int,
@@ -915,6 +940,7 @@ sealed class DragToDesktopTransitionHandler(
            override var surfaceLayers: DragToDesktopLayers? = null,
            override var cancelState: CancelState = CancelState.NO_CANCEL,
            override var startAborted: Boolean = false,
            override val visualIndicator: DesktopModeVisualIndicator?,
            var otherRootChanges: MutableList<Change> = mutableListOf(),
        ) : TransitionState()

@@ -931,6 +957,7 @@ sealed class DragToDesktopTransitionHandler(
            override var surfaceLayers: DragToDesktopLayers? = null,
            override var cancelState: CancelState = CancelState.NO_CANCEL,
            override var startAborted: Boolean = false,
            override val visualIndicator: DesktopModeVisualIndicator?,
            var splitRootChange: Change? = null,
            var otherSplitTask: Int,
        ) : TransitionState()
+20 −2
Original line number Diff line number Diff line
@@ -130,6 +130,12 @@ constructor(
        }
    }

    /** Reparent the indicator to {@code newParent}. */
    fun reparentLeash(t: SurfaceControl.Transaction, newParent: SurfaceControl) {
        val leash = indicatorLeash ?: return
        t.reparent(leash, newParent)
    }

    private fun showIndicator(taskSurface: SurfaceControl, leash: SurfaceControl) {
        mainExecutor.execute {
            indicatorLeash = leash
@@ -166,7 +172,7 @@ constructor(
                displayController.getDisplayLayout(taskInfo.displayId)
                    ?: error("Expected to find DisplayLayout for taskId${taskInfo.taskId}.")
            if (currentType == IndicatorType.NO_INDICATOR) {
                fadeInIndicator(layout, newType, taskInfo.displayId, snapEventHandler)
                fadeInIndicatorInternal(layout, newType, taskInfo.displayId, snapEventHandler)
            } else if (newType == IndicatorType.NO_INDICATOR) {
                fadeOutIndicator(
                    layout,
@@ -194,11 +200,23 @@ constructor(
        }
    }

    /**
     * Fade indicator in as provided type.
     *
     * Animator fades the indicator in while expanding the bounds outwards.
     */
    fun fadeInIndicator(layout: DisplayLayout, type: IndicatorType, displayId: Int) {
        if (isReleased) return
        desktopExecutor.execute {
            fadeInIndicatorInternal(layout, type, displayId, snapEventHandler)
        }
    }

    /**
     * Fade indicator in as provided type. Animator fades it in while expanding the bounds outwards.
     */
    @VisibleForTesting
    fun fadeInIndicator(
    fun fadeInIndicatorInternal(
        layout: DisplayLayout,
        type: IndicatorType,
        displayId: Int,
+36 −0
Original line number Diff line number Diff line
@@ -20,6 +20,7 @@ import android.animation.AnimatorTestRule
import android.app.ActivityManager.RunningTaskInfo
import android.graphics.PointF
import android.graphics.Rect
import android.platform.test.annotations.DisableFlags
import android.platform.test.annotations.EnableFlags
import android.testing.AndroidTestingRunner
import android.testing.TestableLooper.RunWithLooper
@@ -28,6 +29,7 @@ import android.view.SurfaceControl
import androidx.test.filters.SmallTest
import com.android.internal.policy.SystemBarUtils
import com.android.window.flags.Flags.FLAG_ENABLE_DESKTOP_WINDOWING_MODE
import com.android.window.flags.Flags.FLAG_ENABLE_VISUAL_INDICATOR_IN_TRANSITION_BUGFIX
import com.android.wm.shell.R
import com.android.wm.shell.RootTaskDisplayAreaOrganizer
import com.android.wm.shell.ShellTestCase
@@ -45,6 +47,8 @@ import org.junit.runner.RunWith
import org.mockito.ArgumentMatchers.anyInt
import org.mockito.Mock
import org.mockito.kotlin.any
import org.mockito.kotlin.never
import org.mockito.kotlin.verify
import org.mockito.kotlin.whenever

/**
@@ -345,6 +349,38 @@ class DesktopModeVisualIndicatorTest : ShellTestCase() {
        assertThat(visualIndicator.indicatorBounds).isEqualTo(dropTargetBounds)
    }

    @Test
    @DisableFlags(FLAG_ENABLE_VISUAL_INDICATOR_IN_TRANSITION_BUGFIX)
    fun createIndicator_inTransitionFlagDisabled_isAttachedToDisplayArea() {
        createVisualIndicator(DesktopModeVisualIndicator.DragStartState.FROM_FULLSCREEN)

        verify(taskDisplayAreaOrganizer).attachToDisplayArea(anyInt(), any())
    }

    @Test
    @EnableFlags(FLAG_ENABLE_VISUAL_INDICATOR_IN_TRANSITION_BUGFIX)
    fun createIndicator_fromFreeform_inTransitionFlagEnabled_isAttachedToDisplayArea() {
        createVisualIndicator(DesktopModeVisualIndicator.DragStartState.FROM_FREEFORM)

        verify(taskDisplayAreaOrganizer).attachToDisplayArea(anyInt(), any())
    }

    @Test
    @EnableFlags(FLAG_ENABLE_VISUAL_INDICATOR_IN_TRANSITION_BUGFIX)
    fun createIndicator_fromFullscreen_inTransitionFlagEnabled_notAttachedToDisplayArea() {
        createVisualIndicator(DesktopModeVisualIndicator.DragStartState.FROM_FULLSCREEN)

        verify(taskDisplayAreaOrganizer, never()).attachToDisplayArea(anyInt(), any())
    }

    @Test
    @EnableFlags(FLAG_ENABLE_VISUAL_INDICATOR_IN_TRANSITION_BUGFIX)
    fun createIndicator_fromSplit_inTransitionFlagEnabled_notAttachedToDisplayArea() {
        createVisualIndicator(DesktopModeVisualIndicator.DragStartState.FROM_SPLIT)

        verify(taskDisplayAreaOrganizer, never()).attachToDisplayArea(anyInt(), any())
    }

    private fun createVisualIndicator(dragStartState: DesktopModeVisualIndicator.DragStartState) {
        visualIndicator =
            DesktopModeVisualIndicator(
Loading