Loading libs/WindowManager/Shell/src/com/android/wm/shell/desktopmode/DesktopTasksController.kt +31 −18 Original line number Diff line number Diff line Loading @@ -150,6 +150,7 @@ import com.android.wm.shell.transition.FocusTransitionObserver import com.android.wm.shell.transition.OneShotRemoteHandler import com.android.wm.shell.transition.Transitions import com.android.wm.shell.transition.Transitions.TransitionFinishCallback import com.android.wm.shell.transition.Transitions.TransitionHandler import com.android.wm.shell.windowdecor.DragPositioningCallbackUtility import com.android.wm.shell.windowdecor.MoveToDesktopAnimator import com.android.wm.shell.windowdecor.OnTaskRepositionAnimationListener Loading Loading @@ -1283,13 +1284,20 @@ class DesktopTasksController( } /** * Move [task] to display with [displayId]. * Move [task] to display with [displayId]. When [bounds] is not null, it will be used as the * bounds on the new display. When [transitionHandler] is not null, it will be used instead of * the default [DesktopModeMoveToDisplayTransitionHandler]. * * No-op if task is already on that display per [RunningTaskInfo.displayId]. * * TODO: b/399411604 - split this up into smaller functions. */ private fun moveToDisplay(task: RunningTaskInfo, displayId: Int) { private fun moveToDisplay( task: RunningTaskInfo, displayId: Int, bounds: Rect? = null, transitionHandler: TransitionHandler? = null, ) { logV("moveToDisplay: taskId=%d displayId=%d", task.taskId, displayId) if (task.displayId == displayId) { logD("moveToDisplay: task already on display %d", displayId) Loading Loading @@ -1321,7 +1329,9 @@ class DesktopTasksController( if (DesktopExperienceFlags.ENABLE_MULTIPLE_DESKTOPS_BACKEND.isTrue) { desksOrganizer.moveTaskToDesk(wct, destinationDeskId, task) } if (Flags.enableMoveToNextDisplayShortcut()) { if (bounds != null) { wct.setBounds(task.token, bounds) } else if (Flags.enableMoveToNextDisplayShortcut()) { applyFreeformDisplayChange(wct, task, displayId) } } Loading Loading @@ -1362,7 +1372,11 @@ class DesktopTasksController( null } val transition = transitions.startTransition(TRANSIT_CHANGE, wct, moveToDisplayTransitionHandler) transitions.startTransition( TRANSIT_CHANGE, wct, transitionHandler ?: moveToDisplayTransitionHandler, ) deactivationRunnable?.invoke(transition) activationRunnable?.invoke(transition) } Loading Loading @@ -3326,29 +3340,28 @@ class DesktopTasksController( return } // Update task bounds so that the task position will match the position of its leash val wct = WindowContainerTransaction() wct.setBounds(taskInfo.token, destinationBounds) val newDisplayId = motionEvent.getDisplayId() val displayAreaInfo = rootTaskDisplayAreaOrganizer.getDisplayAreaInfo(newDisplayId) val isCrossDisplayDrag = Flags.enableConnectedDisplaysWindowDrag() && newDisplayId != taskInfo.getDisplayId() && displayAreaInfo != null val handler = if (isCrossDisplayDrag) { dragToDisplayTransitionHandler moveToDisplay( taskInfo, newDisplayId, destinationBounds, dragToDisplayTransitionHandler, ) } else { null } if (isCrossDisplayDrag) { // TODO: b/362720497 - reparent to a specific desk within the target display. wct.reparent(taskInfo.token, displayAreaInfo.token, /* onTop= */ true) // Update task bounds so that the task position will match the position of its // leash val wct = WindowContainerTransaction() wct.setBounds(taskInfo.token, destinationBounds) transitions.startTransition(TRANSIT_CHANGE, wct, /* handler= */ null) } transitions.startTransition(TRANSIT_CHANGE, wct, handler) releaseVisualIndicator() } IndicatorType.TO_DESKTOP_INDICATOR -> { Loading libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/desktopmode/DesktopTasksControllerTest.kt +36 −0 Original line number Diff line number Diff line Loading @@ -6057,6 +6057,7 @@ class DesktopTasksControllerTest(flags: FlagsParameterization) : ShellTestCase() @Test @EnableFlags(Flags.FLAG_ENABLE_CONNECTED_DISPLAYS_WINDOW_DRAG) @DisableFlags(Flags.FLAG_ENABLE_MULTIPLE_DESKTOPS_BACKEND) fun onDesktopDragEnd_noIndicatorAndMoveToNewDisplay_reparent() { val task = setUpFreeformTask() val spyController = spy(controller) Loading Loading @@ -6092,6 +6093,41 @@ class DesktopTasksControllerTest(flags: FlagsParameterization) : ShellTestCase() ) } @Test @EnableFlags( Flags.FLAG_ENABLE_CONNECTED_DISPLAYS_WINDOW_DRAG, Flags.FLAG_ENABLE_MULTIPLE_DESKTOPS_BACKEND, ) fun onDesktopDragEnd_noIndicatorAndMoveToNewDisplay_reparentToDesk() { val deskId = 5 val task = setUpFreeformTask() val spyController = spy(controller) val mockSurface = mock(SurfaceControl::class.java) val mockDisplayLayout = mock(DisplayLayout::class.java) taskRepository.addDesk(displayId = SECONDARY_DISPLAY_ID, deskId = deskId) whenever(displayController.getDisplayLayout(task.displayId)).thenReturn(mockDisplayLayout) whenever(mockDisplayLayout.stableInsets()).thenReturn(Rect(0, 100, 2000, 2000)) spyController.onDragPositioningMove(task, mockSurface, 200f, Rect(100, 200, 500, 1000)) val currentDragBounds = Rect(100, 200, 500, 1000) whenever(spyController.getVisualIndicator()).thenReturn(desktopModeVisualIndicator) whenever(desktopModeVisualIndicator.updateIndicatorType(anyOrNull())) .thenReturn(DesktopModeVisualIndicator.IndicatorType.NO_INDICATOR) whenever(motionEvent.displayId).thenReturn(SECONDARY_DISPLAY_ID) spyController.onDragPositioningEnd( task, mockSurface, inputCoordinate = PointF(200f, 300f), currentDragBounds, validDragArea = Rect(0, 50, 2000, 2000), dragStartBounds = Rect(), motionEvent, ) verify(desksOrganizer).moveTaskToDesk(any(), eq(deskId), eq(task)) } @Test fun onDesktopDragEnd_fullscreenIndicator_dragToExitDesktop() { val task = setUpFreeformTask(bounds = Rect(0, 0, 100, 100)) Loading Loading
libs/WindowManager/Shell/src/com/android/wm/shell/desktopmode/DesktopTasksController.kt +31 −18 Original line number Diff line number Diff line Loading @@ -150,6 +150,7 @@ import com.android.wm.shell.transition.FocusTransitionObserver import com.android.wm.shell.transition.OneShotRemoteHandler import com.android.wm.shell.transition.Transitions import com.android.wm.shell.transition.Transitions.TransitionFinishCallback import com.android.wm.shell.transition.Transitions.TransitionHandler import com.android.wm.shell.windowdecor.DragPositioningCallbackUtility import com.android.wm.shell.windowdecor.MoveToDesktopAnimator import com.android.wm.shell.windowdecor.OnTaskRepositionAnimationListener Loading Loading @@ -1283,13 +1284,20 @@ class DesktopTasksController( } /** * Move [task] to display with [displayId]. * Move [task] to display with [displayId]. When [bounds] is not null, it will be used as the * bounds on the new display. When [transitionHandler] is not null, it will be used instead of * the default [DesktopModeMoveToDisplayTransitionHandler]. * * No-op if task is already on that display per [RunningTaskInfo.displayId]. * * TODO: b/399411604 - split this up into smaller functions. */ private fun moveToDisplay(task: RunningTaskInfo, displayId: Int) { private fun moveToDisplay( task: RunningTaskInfo, displayId: Int, bounds: Rect? = null, transitionHandler: TransitionHandler? = null, ) { logV("moveToDisplay: taskId=%d displayId=%d", task.taskId, displayId) if (task.displayId == displayId) { logD("moveToDisplay: task already on display %d", displayId) Loading Loading @@ -1321,7 +1329,9 @@ class DesktopTasksController( if (DesktopExperienceFlags.ENABLE_MULTIPLE_DESKTOPS_BACKEND.isTrue) { desksOrganizer.moveTaskToDesk(wct, destinationDeskId, task) } if (Flags.enableMoveToNextDisplayShortcut()) { if (bounds != null) { wct.setBounds(task.token, bounds) } else if (Flags.enableMoveToNextDisplayShortcut()) { applyFreeformDisplayChange(wct, task, displayId) } } Loading Loading @@ -1362,7 +1372,11 @@ class DesktopTasksController( null } val transition = transitions.startTransition(TRANSIT_CHANGE, wct, moveToDisplayTransitionHandler) transitions.startTransition( TRANSIT_CHANGE, wct, transitionHandler ?: moveToDisplayTransitionHandler, ) deactivationRunnable?.invoke(transition) activationRunnable?.invoke(transition) } Loading Loading @@ -3326,29 +3340,28 @@ class DesktopTasksController( return } // Update task bounds so that the task position will match the position of its leash val wct = WindowContainerTransaction() wct.setBounds(taskInfo.token, destinationBounds) val newDisplayId = motionEvent.getDisplayId() val displayAreaInfo = rootTaskDisplayAreaOrganizer.getDisplayAreaInfo(newDisplayId) val isCrossDisplayDrag = Flags.enableConnectedDisplaysWindowDrag() && newDisplayId != taskInfo.getDisplayId() && displayAreaInfo != null val handler = if (isCrossDisplayDrag) { dragToDisplayTransitionHandler moveToDisplay( taskInfo, newDisplayId, destinationBounds, dragToDisplayTransitionHandler, ) } else { null } if (isCrossDisplayDrag) { // TODO: b/362720497 - reparent to a specific desk within the target display. wct.reparent(taskInfo.token, displayAreaInfo.token, /* onTop= */ true) // Update task bounds so that the task position will match the position of its // leash val wct = WindowContainerTransaction() wct.setBounds(taskInfo.token, destinationBounds) transitions.startTransition(TRANSIT_CHANGE, wct, /* handler= */ null) } transitions.startTransition(TRANSIT_CHANGE, wct, handler) releaseVisualIndicator() } IndicatorType.TO_DESKTOP_INDICATOR -> { Loading
libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/desktopmode/DesktopTasksControllerTest.kt +36 −0 Original line number Diff line number Diff line Loading @@ -6057,6 +6057,7 @@ class DesktopTasksControllerTest(flags: FlagsParameterization) : ShellTestCase() @Test @EnableFlags(Flags.FLAG_ENABLE_CONNECTED_DISPLAYS_WINDOW_DRAG) @DisableFlags(Flags.FLAG_ENABLE_MULTIPLE_DESKTOPS_BACKEND) fun onDesktopDragEnd_noIndicatorAndMoveToNewDisplay_reparent() { val task = setUpFreeformTask() val spyController = spy(controller) Loading Loading @@ -6092,6 +6093,41 @@ class DesktopTasksControllerTest(flags: FlagsParameterization) : ShellTestCase() ) } @Test @EnableFlags( Flags.FLAG_ENABLE_CONNECTED_DISPLAYS_WINDOW_DRAG, Flags.FLAG_ENABLE_MULTIPLE_DESKTOPS_BACKEND, ) fun onDesktopDragEnd_noIndicatorAndMoveToNewDisplay_reparentToDesk() { val deskId = 5 val task = setUpFreeformTask() val spyController = spy(controller) val mockSurface = mock(SurfaceControl::class.java) val mockDisplayLayout = mock(DisplayLayout::class.java) taskRepository.addDesk(displayId = SECONDARY_DISPLAY_ID, deskId = deskId) whenever(displayController.getDisplayLayout(task.displayId)).thenReturn(mockDisplayLayout) whenever(mockDisplayLayout.stableInsets()).thenReturn(Rect(0, 100, 2000, 2000)) spyController.onDragPositioningMove(task, mockSurface, 200f, Rect(100, 200, 500, 1000)) val currentDragBounds = Rect(100, 200, 500, 1000) whenever(spyController.getVisualIndicator()).thenReturn(desktopModeVisualIndicator) whenever(desktopModeVisualIndicator.updateIndicatorType(anyOrNull())) .thenReturn(DesktopModeVisualIndicator.IndicatorType.NO_INDICATOR) whenever(motionEvent.displayId).thenReturn(SECONDARY_DISPLAY_ID) spyController.onDragPositioningEnd( task, mockSurface, inputCoordinate = PointF(200f, 300f), currentDragBounds, validDragArea = Rect(0, 50, 2000, 2000), dragStartBounds = Rect(), motionEvent, ) verify(desksOrganizer).moveTaskToDesk(any(), eq(deskId), eq(task)) } @Test fun onDesktopDragEnd_fullscreenIndicator_dragToExitDesktop() { val task = setUpFreeformTask(bounds = Rect(0, 0, 100, 100)) Loading