diff options
Diffstat (limited to 'libs')
3 files changed, 180 insertions, 5 deletions
diff --git a/libs/WindowManager/Shell/shared/src/com/android/wm/shell/shared/desktopmode/DesktopModeStatus.java b/libs/WindowManager/Shell/shared/src/com/android/wm/shell/shared/desktopmode/DesktopModeStatus.java index 0150bcdbd412..6bc995f14d44 100644 --- a/libs/WindowManager/Shell/shared/src/com/android/wm/shell/shared/desktopmode/DesktopModeStatus.java +++ b/libs/WindowManager/Shell/shared/src/com/android/wm/shell/shared/desktopmode/DesktopModeStatus.java @@ -102,6 +102,15 @@ public class DesktopModeStatus { "persist.wm.debug.enter_desktop_by_default_on_freeform_display"; /** + * Sysprop declaring whether to enable drag-to-maximize for desktop windows. + * + * <p>If it is not defined, then {@code R.integer.config_dragToMaximizeInDesktopMode} + * is used. + */ + public static final String ENABLE_DRAG_TO_MAXIMIZE_SYS_PROP = + "persist.wm.debug.enable_drag_to_maximize"; + + /** * Sysprop declaring the maximum number of Tasks to show in Desktop Mode at any one time. * * <p>If it is not defined, then {@code R.integer.config_maxDesktopWindowingActiveTasks} is @@ -230,6 +239,18 @@ public class DesktopModeStatus { R.bool.config_enterDesktopByDefaultOnFreeformDisplay)); } + /** + * Return {@code true} if a window should be maximized when it's dragged to the top edge of the + * screen. + */ + public static boolean shouldMaximizeWhenDragToTopEdge(@NonNull Context context) { + if (!Flags.enableDragToMaximize()) { + return false; + } + return SystemProperties.getBoolean(ENABLE_DRAG_TO_MAXIMIZE_SYS_PROP, + context.getResources().getBoolean(R.bool.config_dragToMaximizeInDesktopMode)); + } + /** Dumps DesktopModeStatus flags and configs. */ public static void dump(PrintWriter pw, String prefix, Context context) { String innerPrefix = prefix + " "; 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 3d3c072727be..577c18cfa5e4 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 @@ -857,6 +857,39 @@ class DesktopTasksController( toggleResizeDesktopTaskTransitionHandler.startTransition(wct) } + private fun dragToMaximizeDesktopTask( + taskInfo: RunningTaskInfo, + taskSurface: SurfaceControl, + currentDragBounds: Rect, + motionEvent: MotionEvent + ) { + val displayLayout = displayController.getDisplayLayout(taskInfo.displayId) ?: return + val stableBounds = Rect() + displayLayout.getStableBounds(stableBounds) + if (isTaskMaximized(taskInfo, stableBounds)) { + // Handle the case where we attempt to drag-to-maximize when already maximized: the task + // position won't need to change but we want to animate the surface going back to the + // maximized position. + val containerBounds = taskInfo.configuration.windowConfiguration.bounds + if (containerBounds != currentDragBounds) { + returnToDragStartAnimator.start( + taskInfo.taskId, + taskSurface, + startBounds = currentDragBounds, + endBounds = containerBounds, + isResizable = taskInfo.isResizeable + ) + } + return + } + + // TODO(b/375356605): Introduce a new ResizeTrigger for drag-to-top. + desktopModeEventLogger.logTaskResizingStarted( + ResizeTrigger.UNKNOWN_RESIZE_TRIGGER, motionEvent, taskInfo, displayController + ) + toggleDesktopTaskSize(taskInfo, ResizeTrigger.UNKNOWN_RESIZE_TRIGGER, motionEvent) + } + private fun getMaximizeBounds(taskInfo: RunningTaskInfo, stableBounds: Rect): Rect { if (taskInfo.isResizeable) { // if resizable then expand to entire stable bounds (full display minus insets) @@ -1918,11 +1951,15 @@ class DesktopTasksController( ) when (indicatorType) { IndicatorType.TO_FULLSCREEN_INDICATOR -> { - moveToFullscreenWithAnimation( - taskInfo, - position, - DesktopModeTransitionSource.TASK_DRAG - ) + if (DesktopModeStatus.shouldMaximizeWhenDragToTopEdge(context)) { + dragToMaximizeDesktopTask(taskInfo, taskSurface, currentDragBounds, motionEvent) + } else { + moveToFullscreenWithAnimation( + taskInfo, + position, + DesktopModeTransitionSource.TASK_DRAG + ) + } } IndicatorType.TO_SPLIT_LEFT_INDICATOR -> { handleSnapResizingTask( 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 62d2c987ac5d..051079c9d519 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 @@ -2914,6 +2914,123 @@ class DesktopTasksControllerTest : ShellTestCase() { } @Test + fun onDesktopDragEnd_fullscreenIndicator_dragToExitDesktop() { + val task = setUpFreeformTask(bounds = Rect(0, 0, 100, 100)) + val spyController = spy(controller) + val mockSurface = mock(SurfaceControl::class.java) + val mockDisplayLayout = mock(DisplayLayout::class.java) + val tda = rootTaskDisplayAreaOrganizer.getDisplayAreaInfo(DEFAULT_DISPLAY)!! + tda.configuration.windowConfiguration.windowingMode = WINDOWING_MODE_FULLSCREEN + whenever(displayController.getDisplayLayout(task.displayId)).thenReturn(mockDisplayLayout) + whenever(mockDisplayLayout.stableInsets()).thenReturn(Rect(0, 100, 2000, 2000)) + whenever(mockDisplayLayout.getStableBounds(any())).thenAnswer { i -> + (i.arguments.first() as Rect).set(STABLE_BOUNDS) + } + whenever(DesktopModeStatus.shouldMaximizeWhenDragToTopEdge(context)).thenReturn(false) + whenever(spyController.getVisualIndicator()).thenReturn(desktopModeVisualIndicator) + whenever(desktopModeVisualIndicator.updateIndicatorType(anyOrNull())) + .thenReturn(DesktopModeVisualIndicator.IndicatorType.TO_FULLSCREEN_INDICATOR) + + // Drag move the task to the top edge + spyController.onDragPositioningMove(task, mockSurface, 200f, Rect(100, 200, 500, 1000)) + spyController.onDragPositioningEnd( + task, + mockSurface, + Point(100, 50), /* position */ + PointF(200f, 300f), /* inputCoordinate */ + Rect(100, 50, 500, 1000), /* currentDragBounds */ + Rect(0, 50, 2000, 2000) /* validDragArea */, + Rect() /* dragStartBounds */, + motionEvent, + desktopWindowDecoration) + + // Assert the task exits desktop mode + val wct = getLatestExitDesktopWct() + assertThat(wct.changes[task.token.asBinder()]?.windowingMode) + .isEqualTo(WINDOWING_MODE_UNDEFINED) + } + + @Test + fun onDesktopDragEnd_fullscreenIndicator_dragToMaximize() { + val task = setUpFreeformTask(bounds = Rect(0, 0, 100, 100)) + val spyController = spy(controller) + val mockSurface = mock(SurfaceControl::class.java) + val mockDisplayLayout = mock(DisplayLayout::class.java) + val tda = rootTaskDisplayAreaOrganizer.getDisplayAreaInfo(DEFAULT_DISPLAY)!! + tda.configuration.windowConfiguration.windowingMode = WINDOWING_MODE_FULLSCREEN + whenever(displayController.getDisplayLayout(task.displayId)).thenReturn(mockDisplayLayout) + whenever(mockDisplayLayout.stableInsets()).thenReturn(Rect(0, 100, 2000, 2000)) + whenever(mockDisplayLayout.getStableBounds(any())).thenAnswer { i -> + (i.arguments.first() as Rect).set(STABLE_BOUNDS) + } + whenever(DesktopModeStatus.shouldMaximizeWhenDragToTopEdge(context)).thenReturn(true) + whenever(spyController.getVisualIndicator()).thenReturn(desktopModeVisualIndicator) + whenever(desktopModeVisualIndicator.updateIndicatorType(anyOrNull())) + .thenReturn(DesktopModeVisualIndicator.IndicatorType.TO_FULLSCREEN_INDICATOR) + + // Drag move the task to the top edge + spyController.onDragPositioningMove(task, mockSurface, 200f, Rect(100, 200, 500, 1000)) + spyController.onDragPositioningEnd( + task, + mockSurface, + Point(100, 50), /* position */ + PointF(200f, 300f), /* inputCoordinate */ + Rect(100, 50, 500, 1000), /* currentDragBounds */ + Rect(0, 50, 2000, 2000) /* validDragArea */, + Rect() /* dragStartBounds */, + motionEvent, + desktopWindowDecoration) + + // Assert bounds set to stable bounds + val wct = getLatestToggleResizeDesktopTaskWct() + assertThat(findBoundsChange(wct, task)).isEqualTo(STABLE_BOUNDS) + } + + @Test + fun onDesktopDragEnd_fullscreenIndicator_dragToMaximize_noBoundsChange() { + val task = setUpFreeformTask(bounds = STABLE_BOUNDS) + val spyController = spy(controller) + val mockSurface = mock(SurfaceControl::class.java) + val mockDisplayLayout = mock(DisplayLayout::class.java) + val tda = rootTaskDisplayAreaOrganizer.getDisplayAreaInfo(DEFAULT_DISPLAY)!! + tda.configuration.windowConfiguration.windowingMode = WINDOWING_MODE_FULLSCREEN + whenever(displayController.getDisplayLayout(task.displayId)).thenReturn(mockDisplayLayout) + whenever(mockDisplayLayout.stableInsets()).thenReturn(Rect(0, 100, 2000, 2000)) + whenever(mockDisplayLayout.getStableBounds(any())).thenAnswer { i -> + (i.arguments.first() as Rect).set(STABLE_BOUNDS) + } + whenever(DesktopModeStatus.shouldMaximizeWhenDragToTopEdge(context)).thenReturn(true) + whenever(spyController.getVisualIndicator()).thenReturn(desktopModeVisualIndicator) + whenever(desktopModeVisualIndicator.updateIndicatorType(anyOrNull())) + .thenReturn(DesktopModeVisualIndicator.IndicatorType.TO_FULLSCREEN_INDICATOR) + + // Drag move the task to the top edge + val currentDragBounds = Rect(100, 50, 500, 1000) + spyController.onDragPositioningMove(task, mockSurface, 200f, Rect(100, 200, 500, 1000)) + spyController.onDragPositioningEnd( + task, + mockSurface, + Point(100, 50), /* position */ + PointF(200f, 300f), /* inputCoordinate */ + currentDragBounds, /* currentDragBounds */ + Rect(0, 50, 2000, 2000) /* validDragArea */, + Rect() /* dragStartBounds */, + motionEvent, + desktopWindowDecoration) + + // 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(STABLE_BOUNDS), + eq(true) + ) + } + + @Test fun enterSplit_freeformTaskIsMovedToSplit() { val task1 = setUpFreeformTask() val task2 = setUpFreeformTask() |