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

Commit e7b43582 authored by Qijing Yao's avatar Qijing Yao
Browse files

Fix lingering mirrored surface after misinterpreted drag

When a window is dragged, a mirrored surface is created to show its
position while the original surface is hidden. This mirrored surface
must be cleaned up when the drag completes.

This cleanup is handled in two ways:
*   For a standard move where the window lands at the final drag
    position, a WindowDragTransitionHandler cleans up the surface in
    its finishTransaction.
*   In other cases where the final position is different (e.g., when a
    window is snapped), this handler is not used, and the caller of
    onDragPositioningEnd is responsible for the cleanup.

A bug occurred when a click on a window caption was misinterpreted as a
drag. This created a mirrored surface, but the drag ended immediately
with no change in bounds. This initiated a transition intended for the
WindowDragTransitionHandler, but because the bounds were unchanged, the
transition was empty. Shell aborts empty transitions, which prevented
the handler's finishTransaction from running and left the mirrored
surface on screen.

The fix prevents an empty transition from starting when the drag ends
without any actual movement. By avoiding the transition altogether in
this scenario, the cleanup responsibility correctly falls back to the
caller, making the behavior for a misinterpreted click consistent with
other non-handler cases.

Flag: EXEMPT - reverting a portion of a previous change
Bug: 442613884
Test: atest DesktopTasksControllerTest
Change-Id: Ib7393d109c12686cab6440b58ccfe91049f18a94
parent 1dfaa3ec
Loading
Loading
Loading
Loading
+10 −10
Original line number Original line Diff line number Diff line
@@ -5690,14 +5690,13 @@ class DesktopTasksController(
                    validDragArea,
                    validDragArea,
                )
                )


                if (
                if (destinationBounds == dragStartBounds) {
                    destinationBounds == dragStartBounds && destinationBounds != currentDragBounds
                ) {
                    // There's no actual difference between the start and end bounds, so while a
                    // There's no actual difference between the start and end bounds, so while a
                    // WCT change isn't needed, the dragged surface still needs to be snapped back
                    // WCT change isn't needed, the dragged surface still needs to be snapped back
                    // to its original location. This is as long as it moved some in the first
                    // to its original location. This is as long as it moved some in the first
                    // place, if it didn't and |currentDragBounds| is already at destination then
                    // place, if it didn't and |currentDragBounds| is already at destination then
                    // there's no need to animate.
                    // there's no need to animate.
                    if (currentDragBounds != dragStartBounds) {
                        releaseVisualIndicator()
                        releaseVisualIndicator()
                        returnToDragStartAnimator.start(
                        returnToDragStartAnimator.start(
                            taskInfo.taskId,
                            taskInfo.taskId,
@@ -5705,6 +5704,7 @@ class DesktopTasksController(
                            startBounds = currentDragBounds,
                            startBounds = currentDragBounds,
                            endBounds = dragStartBounds,
                            endBounds = dragStartBounds,
                        )
                        )
                    }
                    return true
                    return true
                }
                }


+41 −1
Original line number Original line Diff line number Diff line
@@ -9300,7 +9300,45 @@ class DesktopTasksControllerTest(flags: FlagsParameterization) : ShellTestCase()
    }
    }
    @Test
    @Test
    fun onDesktopDragEnd_noIndicator_noBoundsMovement_noReturnToStartAnimation() {
    fun onDesktopDragEnd_noIndicator_noBoundsMovement_outOfValidArea_startsReturnToStartAnimation_noWct() {
        val task = setUpFreeformTask(bounds = STABLE_BOUNDS)
        val spyController = spy(controller)
        val mockSurface = mock(SurfaceControl::class.java)
        whenever(spyController.getVisualIndicator()).thenReturn(desktopModeVisualIndicator)
        whenever(desktopModeVisualIndicator.updateIndicatorType(any(), anyOrNull()))
            .thenReturn(DesktopModeVisualIndicator.IndicatorType.NO_INDICATOR)
        whenever(motionEvent.displayId).thenReturn(DEFAULT_DISPLAY)
        val startBounds = Rect(0, 50, 500, 550)
        // Drag slightly outside of valid drag area
        val currentDragBounds = Rect(-10, 50, 490, 550)
        spyController.onDragPositioningEnd(
            taskInfo = task,
            taskSurface = mockSurface,
            displayId = DEFAULT_DISPLAY,
            inputCoordinate = PointF(250f, 300f),
            currentDragBounds = currentDragBounds,
            validDragArea = Rect(0, 50, 2000, 2000),
            dragStartBounds = startBounds,
            motionEvent = motionEvent,
        )
        // Verify animation to return to start is triggered.
        verify(mReturnToDragStartAnimator)
            .start(
                eq(task.taskId),
                eq(mockSurface),
                eq(currentDragBounds),
                eq(startBounds),
                anyOrNull(),
            )
        // Verify no WCT is started.
        verify(transitions, never()).startTransition(any(), any(), any())
    }
    @Test
    fun onDesktopDragEnd_noIndicator_noBoundsMovement_noReturnToStartAnimation_noWct() {
        val task = setUpFreeformTask(bounds = STABLE_BOUNDS)
        val task = setUpFreeformTask(bounds = STABLE_BOUNDS)
        val spyController = spy(controller)
        val spyController = spy(controller)
        val mockSurface = mock(SurfaceControl::class.java)
        val mockSurface = mock(SurfaceControl::class.java)
@@ -9341,6 +9379,8 @@ class DesktopTasksControllerTest(flags: FlagsParameterization) : ShellTestCase()
        // return because the current bounds are also the same as start/end.
        // return because the current bounds are also the same as start/end.
        verify(mReturnToDragStartAnimator, never())
        verify(mReturnToDragStartAnimator, never())
            .start(eq(task.taskId), eq(mockSurface), any(), any(), anyOrNull())
            .start(eq(task.taskId), eq(mockSurface), any(), any(), anyOrNull())
        // Verify no WCT is started.
        verify(transitions, never()).startTransition(any(), any(), any())
    }
    }
    @Test
    @Test