diff options
5 files changed, 111 insertions, 26 deletions
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/desktopmode/DesktopTasksController.kt b/libs/WindowManager/Shell/src/com/android/wm/shell/desktopmode/DesktopTasksController.kt index 90f8276240a7..4b30ed0dfa7c 100644 --- a/libs/WindowManager/Shell/src/com/android/wm/shell/desktopmode/DesktopTasksController.kt +++ b/libs/WindowManager/Shell/src/com/android/wm/shell/desktopmode/DesktopTasksController.kt @@ -734,17 +734,33 @@ class DesktopTasksController(       * Quick-resize to the right or left half of the stable bounds.       *       * @param taskInfo current task that is being snap-resized via dragging or maximize menu button +     * @param taskSurface the leash of the task being dragged       * @param currentDragBounds current position of the task leash being dragged (or current task       *                          bounds if being snapped resize via maximize menu button)       * @param position the portion of the screen (RIGHT or LEFT) we want to snap the task to.       */      fun snapToHalfScreen(          taskInfo: RunningTaskInfo, +        taskSurface: SurfaceControl,          currentDragBounds: Rect,          position: SnapPosition      ) {          val destinationBounds = getSnapBounds(taskInfo, position) -        if (destinationBounds == taskInfo.configuration.windowConfiguration.bounds) return +        if (destinationBounds == taskInfo.configuration.windowConfiguration.bounds) { +            // Handle the case where we attempt to snap resize when already snap resized: the task +            // position won't need to change but we want to animate the surface going back to the +            // snapped position from the "dragged-to-the-edge" position. +            if (destinationBounds != currentDragBounds) { +                returnToDragStartAnimator.start( +                    taskInfo.taskId, +                    taskSurface, +                    startBounds = currentDragBounds, +                    endBounds = destinationBounds, +                    isResizable = taskInfo.isResizeable +                ) +            } +            return +        }          taskbarDesktopTaskListener?.onTaskbarCornerRoundingUpdate(true)          val wct = WindowContainerTransaction().setBounds(taskInfo.token, destinationBounds) @@ -774,13 +790,14 @@ class DesktopTasksController(                  taskInfo.taskId,                  taskSurface,                  startBounds = currentDragBounds, -                endBounds = dragStartBounds +                endBounds = dragStartBounds, +                isResizable = taskInfo.isResizeable,              )          } else {              interactionJankMonitor.begin(                  taskSurface, context, CUJ_DESKTOP_MODE_SNAP_RESIZE, "drag_resizable"              ) -            snapToHalfScreen(taskInfo, currentDragBounds, position) +            snapToHalfScreen(taskInfo, taskSurface, currentDragBounds, position)          }      } diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/desktopmode/ReturnToDragStartAnimator.kt b/libs/WindowManager/Shell/src/com/android/wm/shell/desktopmode/ReturnToDragStartAnimator.kt index 4c5258f2bfcd..f4df42cde10f 100644 --- a/libs/WindowManager/Shell/src/com/android/wm/shell/desktopmode/ReturnToDragStartAnimator.kt +++ b/libs/WindowManager/Shell/src/com/android/wm/shell/desktopmode/ReturnToDragStartAnimator.kt @@ -48,7 +48,13 @@ class ReturnToDragStartAnimator(      }      /** Builds new animator and starts animation of task leash reposition. */ -    fun start(taskId: Int, taskSurface: SurfaceControl, startBounds: Rect, endBounds: Rect) { +    fun start( +        taskId: Int, +        taskSurface: SurfaceControl, +        startBounds: Rect, +        endBounds: Rect, +        isResizable: Boolean +    ) {          val tx = transactionSupplier.get()          boundsAnimator?.cancel() @@ -81,11 +87,13 @@ class ReturnToDragStartAnimator(                                  .apply()                              taskRepositionAnimationListener.onAnimationEnd(taskId)                              boundsAnimator = null -                            Toast.makeText( -                                context, -                                R.string.desktop_mode_non_resizable_snap_text, -                                Toast.LENGTH_SHORT -                            ).show() +                            if (!isResizable) { +                                Toast.makeText( +                                    context, +                                    R.string.desktop_mode_non_resizable_snap_text, +                                    Toast.LENGTH_SHORT +                                ).show() +                            }                              interactionJankMonitor.end(Cuj.CUJ_DESKTOP_MODE_SNAP_RESIZE)                          }                      ) diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/windowdecor/DesktopModeWindowDecorViewModel.java b/libs/WindowManager/Shell/src/com/android/wm/shell/windowdecor/DesktopModeWindowDecorViewModel.java index ac35459347c6..c88c1e28b011 100644 --- a/libs/WindowManager/Shell/src/com/android/wm/shell/windowdecor/DesktopModeWindowDecorViewModel.java +++ b/libs/WindowManager/Shell/src/com/android/wm/shell/windowdecor/DesktopModeWindowDecorViewModel.java @@ -491,7 +491,9 @@ public class DesktopModeWindowDecorViewModel implements WindowDecorViewModel {          } else {              mInteractionJankMonitor.begin(decoration.mTaskSurface, mContext,                      Cuj.CUJ_DESKTOP_MODE_SNAP_RESIZE, "maximize_menu_resizable"); -            mDesktopTasksController.snapToHalfScreen(decoration.mTaskInfo, +            mDesktopTasksController.snapToHalfScreen( +                    decoration.mTaskInfo, +                    decoration.mTaskSurface,                      decoration.mTaskInfo.configuration.windowConfiguration.getBounds(),                      left ? SnapPosition.LEFT : SnapPosition.RIGHT);          } diff --git a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/desktopmode/DesktopTasksControllerTest.kt b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/desktopmode/DesktopTasksControllerTest.kt index 5474e539f286..10557dd9b439 100644 --- a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/desktopmode/DesktopTasksControllerTest.kt +++ b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/desktopmode/DesktopTasksControllerTest.kt @@ -123,12 +123,12 @@ import org.mockito.ArgumentMatchers.isA  import org.mockito.ArgumentMatchers.isNull  import org.mockito.Mock  import org.mockito.Mockito -import org.mockito.Mockito.any  import org.mockito.Mockito.anyInt  import org.mockito.Mockito.clearInvocations  import org.mockito.Mockito.mock  import org.mockito.Mockito.spy  import org.mockito.Mockito.verify +import org.mockito.kotlin.any  import org.mockito.kotlin.anyOrNull  import org.mockito.kotlin.atLeastOnce  import org.mockito.kotlin.eq @@ -2859,7 +2859,7 @@ class DesktopTasksControllerTest : ShellTestCase() {    }    @Test -  fun getSnapBounds_calculatesBoundsForResizable() { +  fun snapToHalfScreen_getSnapBounds_calculatesBoundsForResizable() {      val bounds = Rect(100, 100, 300, 300)      val task = setUpFreeformTask(DEFAULT_DISPLAY, bounds).apply {        topActivityInfo = ActivityInfo().apply { @@ -2874,13 +2874,45 @@ class DesktopTasksControllerTest : ShellTestCase() {        STABLE_BOUNDS.left, STABLE_BOUNDS.top, STABLE_BOUNDS.right / 2, STABLE_BOUNDS.bottom      ) -    controller.snapToHalfScreen(task, currentDragBounds, SnapPosition.LEFT) +    controller.snapToHalfScreen(task, mockSurface, currentDragBounds, SnapPosition.LEFT)      // Assert bounds set to stable bounds      val wct = getLatestToggleResizeDesktopTaskWct(currentDragBounds)      assertThat(findBoundsChange(wct, task)).isEqualTo(expectedBounds)    }    @Test +  fun snapToHalfScreen_snapBoundsWhenAlreadySnapped_animatesSurfaceWithoutWCT() { +    assumeTrue(ENABLE_SHELL_TRANSITIONS) +    // Set up task to already be in snapped-left bounds +    val bounds = Rect( +      STABLE_BOUNDS.left, STABLE_BOUNDS.top, STABLE_BOUNDS.right / 2, STABLE_BOUNDS.bottom +    ) +    val task = setUpFreeformTask(DEFAULT_DISPLAY, bounds).apply { +      topActivityInfo = ActivityInfo().apply { +        screenOrientation = SCREEN_ORIENTATION_LANDSCAPE +        configuration.windowConfiguration.appBounds = bounds +      } +      isResizeable = true +    } + +    // Attempt to snap left again +    val currentDragBounds = Rect(bounds).apply { offset(-100, 0) } +    controller.snapToHalfScreen(task, mockSurface, currentDragBounds, SnapPosition.LEFT) + +    // Assert that task is NOT updated via WCT +    verify(toggleResizeDesktopTaskTransitionHandler, never()).startTransition(any(), any()) + +    // Assert that task leash is updated via Surface Animations +    verify(mReturnToDragStartAnimator).start( +      eq(task.taskId), +      eq(mockSurface), +      eq(currentDragBounds), +      eq(bounds), +      eq(true) +    ) +  } + +  @Test    @DisableFlags(Flags.FLAG_DISABLE_NON_RESIZABLE_APP_SNAP_RESIZING)    fun handleSnapResizingTask_nonResizable_snapsToHalfScreen() {      val task = setUpFreeformTask(DEFAULT_DISPLAY, Rect(0, 0, 200, 100)).apply { @@ -2911,7 +2943,8 @@ class DesktopTasksControllerTest : ShellTestCase() {        eq(task.taskId),        eq(mockSurface),        eq(currentDragBounds), -      eq(preDragBounds) +      eq(preDragBounds), +      eq(false)      )    } diff --git a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/windowdecor/DesktopModeWindowDecorViewModelTests.kt b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/windowdecor/DesktopModeWindowDecorViewModelTests.kt index 0b5c6784b73d..be0549b6655d 100644 --- a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/windowdecor/DesktopModeWindowDecorViewModelTests.kt +++ b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/windowdecor/DesktopModeWindowDecorViewModelTests.kt @@ -113,7 +113,7 @@ import org.mockito.Mockito.anyInt  import org.mockito.Mockito.mock  import org.mockito.Mockito.never  import org.mockito.Mockito.times -import org.mockito.Mockito.verify +import org.mockito.kotlin.verify  import org.mockito.kotlin.any  import org.mockito.kotlin.anyOrNull  import org.mockito.kotlin.argThat @@ -600,6 +600,7 @@ class DesktopModeWindowDecorViewModelTests : ShellTestCase() {      @Test      fun testOnDecorSnappedLeft_snapResizes() { +        val taskSurfaceCaptor = argumentCaptor<SurfaceControl>()          val onLeftSnapClickListenerCaptor = forClass(Function0::class.java)                  as ArgumentCaptor<Function0<Unit>>          val decor = createOpenTaskDecoration( @@ -610,8 +611,13 @@ class DesktopModeWindowDecorViewModelTests : ShellTestCase() {          val currentBounds = decor.mTaskInfo.configuration.windowConfiguration.bounds          onLeftSnapClickListenerCaptor.value.invoke() -        verify(mockDesktopTasksController) -            .snapToHalfScreen(decor.mTaskInfo, currentBounds, SnapPosition.LEFT) +        verify(mockDesktopTasksController).snapToHalfScreen( +            eq(decor.mTaskInfo), +            taskSurfaceCaptor.capture(), +            eq(currentBounds), +            eq(SnapPosition.LEFT) +        ) +        assertEquals(taskSurfaceCaptor.firstValue, decor.mTaskSurface)      }      @Test @@ -632,6 +638,7 @@ class DesktopModeWindowDecorViewModelTests : ShellTestCase() {      @Test      @DisableFlags(Flags.FLAG_DISABLE_NON_RESIZABLE_APP_SNAP_RESIZING)      fun testOnSnapResizeLeft_nonResizable_decorSnappedLeft() { +        val taskSurfaceCaptor = argumentCaptor<SurfaceControl>()          val onLeftSnapClickListenerCaptor = forClass(Function0::class.java)                  as ArgumentCaptor<Function0<Unit>>          val decor = createOpenTaskDecoration( @@ -642,8 +649,13 @@ class DesktopModeWindowDecorViewModelTests : ShellTestCase() {          val currentBounds = decor.mTaskInfo.configuration.windowConfiguration.bounds          onLeftSnapClickListenerCaptor.value.invoke() -        verify(mockDesktopTasksController) -            .snapToHalfScreen(decor.mTaskInfo, currentBounds, SnapPosition.LEFT) +        verify(mockDesktopTasksController).snapToHalfScreen( +            eq(decor.mTaskInfo), +            taskSurfaceCaptor.capture(), +            eq(currentBounds), +            eq(SnapPosition.LEFT) +        ) +        assertEquals(decor.mTaskSurface, taskSurfaceCaptor.firstValue)      }      @Test @@ -660,12 +672,13 @@ class DesktopModeWindowDecorViewModelTests : ShellTestCase() {          onLeftSnapClickListenerCaptor.value.invoke()          verify(mockDesktopTasksController, never()) -            .snapToHalfScreen(decor.mTaskInfo, currentBounds, SnapPosition.LEFT) +            .snapToHalfScreen(eq(decor.mTaskInfo), any(), eq(currentBounds), eq(SnapPosition.LEFT))          verify(mockToast).show()      }      @Test      fun testOnDecorSnappedRight_snapResizes() { +        val taskSurfaceCaptor = argumentCaptor<SurfaceControl>()          val onRightSnapClickListenerCaptor = forClass(Function0::class.java)                  as ArgumentCaptor<Function0<Unit>>          val decor = createOpenTaskDecoration( @@ -676,8 +689,13 @@ class DesktopModeWindowDecorViewModelTests : ShellTestCase() {          val currentBounds = decor.mTaskInfo.configuration.windowConfiguration.bounds          onRightSnapClickListenerCaptor.value.invoke() -        verify(mockDesktopTasksController) -            .snapToHalfScreen(decor.mTaskInfo, currentBounds, SnapPosition.RIGHT) +        verify(mockDesktopTasksController).snapToHalfScreen( +            eq(decor.mTaskInfo), +            taskSurfaceCaptor.capture(), +            eq(currentBounds), +            eq(SnapPosition.RIGHT) +        ) +        assertEquals(decor.mTaskSurface, taskSurfaceCaptor.firstValue)      }      @Test @@ -698,6 +716,7 @@ class DesktopModeWindowDecorViewModelTests : ShellTestCase() {      @Test      @DisableFlags(Flags.FLAG_DISABLE_NON_RESIZABLE_APP_SNAP_RESIZING)      fun testOnSnapResizeRight_nonResizable_decorSnappedRight() { +        val taskSurfaceCaptor = argumentCaptor<SurfaceControl>()          val onRightSnapClickListenerCaptor = forClass(Function0::class.java)                  as ArgumentCaptor<Function0<Unit>>          val decor = createOpenTaskDecoration( @@ -708,8 +727,13 @@ class DesktopModeWindowDecorViewModelTests : ShellTestCase() {          val currentBounds = decor.mTaskInfo.configuration.windowConfiguration.bounds          onRightSnapClickListenerCaptor.value.invoke() -        verify(mockDesktopTasksController) -            .snapToHalfScreen(decor.mTaskInfo, currentBounds, SnapPosition.RIGHT) +        verify(mockDesktopTasksController).snapToHalfScreen( +            eq(decor.mTaskInfo), +            taskSurfaceCaptor.capture(), +            eq(currentBounds), +            eq(SnapPosition.RIGHT) +        ) +        assertEquals(decor.mTaskSurface, taskSurfaceCaptor.firstValue)      }      @Test @@ -726,7 +750,7 @@ class DesktopModeWindowDecorViewModelTests : ShellTestCase() {          onRightSnapClickListenerCaptor.value.invoke()          verify(mockDesktopTasksController, never()) -            .snapToHalfScreen(decor.mTaskInfo, currentBounds, SnapPosition.RIGHT) +            .snapToHalfScreen(eq(decor.mTaskInfo), any(), eq(currentBounds), eq(SnapPosition.RIGHT))          verify(mockToast).show()      } @@ -1033,6 +1057,7 @@ class DesktopModeWindowDecorViewModelTests : ShellTestCase() {      private fun createOpenTaskDecoration(          @WindowingMode windowingMode: Int, +        taskSurface: SurfaceControl = SurfaceControl(),          onMaxOrRestoreListenerCaptor: ArgumentCaptor<Function0<Unit>> =              forClass(Function0::class.java) as ArgumentCaptor<Function0<Unit>>,          onLeftSnapClickListenerCaptor: ArgumentCaptor<Function0<Unit>> = @@ -1051,7 +1076,7 @@ class DesktopModeWindowDecorViewModelTests : ShellTestCase() {              forClass(View.OnClickListener::class.java) as ArgumentCaptor<View.OnClickListener>      ): DesktopModeWindowDecoration {          val decor = setUpMockDecorationForTask(createTask(windowingMode = windowingMode)) -        onTaskOpening(decor.mTaskInfo) +        onTaskOpening(decor.mTaskInfo, taskSurface)          verify(decor).setOnMaximizeOrRestoreClickListener(onMaxOrRestoreListenerCaptor.capture())          verify(decor).setOnLeftSnapClickListener(onLeftSnapClickListenerCaptor.capture())          verify(decor).setOnRightSnapClickListener(onRightSnapClickListenerCaptor.capture())  |