diff options
2 files changed, 188 insertions, 1 deletions
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/windowdecor/tiling/DesktopTilingWindowDecoration.kt b/libs/WindowManager/Shell/src/com/android/wm/shell/windowdecor/tiling/DesktopTilingWindowDecoration.kt index ee3cb72b8cc9..42b8f17927e2 100644 --- a/libs/WindowManager/Shell/src/com/android/wm/shell/windowdecor/tiling/DesktopTilingWindowDecoration.kt +++ b/libs/WindowManager/Shell/src/com/android/wm/shell/windowdecor/tiling/DesktopTilingWindowDecoration.kt @@ -22,10 +22,13 @@ import android.content.res.Configuration import android.content.res.Resources import android.graphics.Bitmap import android.graphics.Rect +import android.os.IBinder import android.util.Slog import android.view.SurfaceControl import android.view.SurfaceControl.Transaction +import android.view.WindowManager.TRANSIT_CHANGE import android.view.WindowManager.TRANSIT_TO_FRONT +import android.window.TransitionRequestInfo import android.window.WindowContainerTransaction import com.android.launcher3.icons.BaseIconFactory import com.android.launcher3.icons.BaseIconFactory.MODE_DEFAULT @@ -196,6 +199,110 @@ class DesktopTilingWindowDecoration( return tilingManager } + fun onDividerHandleMoved(dividerBounds: Rect, t: SurfaceControl.Transaction): Boolean { + val leftTiledTask = leftTaskResizingHelper ?: return false + val rightTiledTask = rightTaskResizingHelper ?: return false + val stableBounds = Rect() + val displayLayout = displayController.getDisplayLayout(displayId) + displayLayout?.getStableBounds(stableBounds) + + if (stableBounds.isEmpty) return false + + val leftBounds = leftTiledTask.bounds + val rightBounds = rightTiledTask.bounds + val newLeftBounds = + Rect(leftBounds.left, leftBounds.top, dividerBounds.left, leftBounds.bottom) + val newRightBounds = + Rect(dividerBounds.right, rightBounds.top, rightBounds.right, rightBounds.bottom) + + // If one of the apps is getting smaller or bigger than size constraint, ignore finger move. + if ( + isResizeWithinSizeConstraints( + newLeftBounds, + newRightBounds, + leftBounds, + rightBounds, + stableBounds, + ) + ) { + return false + } + + // The final new bounds for each app has to be registered to make sure a startAnimate + // when the new bounds are different from old bounds, otherwise hide the veil without + // waiting for an animation as no animation will run when no bounds are changed. + leftTiledTask.newBounds.set(newLeftBounds) + rightTiledTask.newBounds.set(newRightBounds) + if (!isResizing) { + leftTiledTask.showVeil(t) + rightTiledTask.showVeil(t) + isResizing = true + } else { + leftTiledTask.updateVeil(t) + rightTiledTask.updateVeil(t) + } + + // Applies showing/updating veil for both apps and moving the divider into its new position. + t.apply() + return true + } + + fun onDividerHandleDragEnd(dividerBounds: Rect, t: SurfaceControl.Transaction) { + val leftTiledTask = leftTaskResizingHelper ?: return + val rightTiledTask = rightTaskResizingHelper ?: return + + if (leftTiledTask.newBounds == leftTiledTask.bounds) { + leftTiledTask.hideVeil() + rightTiledTask.hideVeil() + isResizing = false + return + } + leftTiledTask.bounds.set(leftTiledTask.newBounds) + rightTiledTask.bounds.set(rightTiledTask.newBounds) + onDividerHandleMoved(dividerBounds, t) + isResizing = false + val wct = WindowContainerTransaction() + wct.setBounds(leftTiledTask.taskInfo.token, leftTiledTask.bounds) + wct.setBounds(rightTiledTask.taskInfo.token, rightTiledTask.bounds) + transitions.startTransition(TRANSIT_CHANGE, wct, this) + } + + override fun startAnimation( + transition: IBinder, + info: TransitionInfo, + startTransaction: Transaction, + finishTransaction: Transaction, + finishCallback: Transitions.TransitionFinishCallback, + ): Boolean { + val leftTiledTask = leftTaskResizingHelper ?: return false + val rightTiledTask = rightTaskResizingHelper ?: return false + for (change in info.getChanges()) { + val sc: SurfaceControl = change.getLeash() + val endBounds = + if (change.taskInfo?.taskId == leftTiledTask.taskInfo.taskId) { + leftTiledTask.bounds + } else { + rightTiledTask.bounds + } + startTransaction.setWindowCrop(sc, endBounds.width(), endBounds.height()) + finishTransaction.setWindowCrop(sc, endBounds.width(), endBounds.height()) + } + + startTransaction.apply() + leftTiledTask.hideVeil() + rightTiledTask.hideVeil() + finishCallback.onTransitionFinished(null) + return true + } + + // TODO(b/361505243) bring tasks to front here when the empty request info bug is fixed. + override fun handleRequest( + transition: IBinder, + request: TransitionRequestInfo, + ): WindowContainerTransaction? { + return null + } + class AppResizingHelper( val taskInfo: RunningTaskInfo, val desktopModeWindowDecoration: DesktopModeWindowDecoration, diff --git a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/windowdecor/tiling/DesktopTilingWindowDecorationTest.kt b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/windowdecor/tiling/DesktopTilingWindowDecorationTest.kt index 61c7a7a063cc..609c81e18b69 100644 --- a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/windowdecor/tiling/DesktopTilingWindowDecorationTest.kt +++ b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/windowdecor/tiling/DesktopTilingWindowDecorationTest.kt @@ -15,12 +15,16 @@ */ package com.android.wm.shell.windowdecor.tiling +import android.app.ActivityManager import android.content.Context import android.content.res.Resources import android.graphics.Rect +import android.os.IBinder import android.testing.AndroidTestingRunner import android.view.SurfaceControl +import android.view.WindowManager.TRANSIT_CHANGE import android.view.WindowManager.TRANSIT_TO_FRONT +import android.window.TransitionInfo import android.window.WindowContainerTransaction import androidx.test.filters.SmallTest import com.android.wm.shell.RootTaskDisplayAreaOrganizer @@ -80,7 +84,9 @@ class DesktopTilingWindowDecorationTest : ShellTestCase() { private val surfaceControlMock: SurfaceControl = mock() private val transaction: SurfaceControl.Transaction = mock() private val tiledTaskHelper: DesktopTilingWindowDecoration.AppResizingHelper = mock() - + private val transition: IBinder = mock() + private val info: TransitionInfo = mock() + private val finishCallback: Transitions.TransitionFinishCallback = mock() private lateinit var tilingDecoration: DesktopTilingWindowDecoration private val split_divider_width = 10 @@ -308,6 +314,80 @@ class DesktopTilingWindowDecorationTest : ShellTestCase() { verify(transitions, times(1)).startTransition(eq(TRANSIT_TO_FRONT), any(), eq(null)) } + @Test + fun taskTiledTasks_NotResized_BeforeTouchEndArrival() { + // Setup + val task1 = createFreeformTask() + val task2 = createFreeformTask() + val stableBounds = STABLE_BOUNDS_MOCK + whenever(displayController.getDisplayLayout(any())).thenReturn(displayLayout) + whenever(displayLayout.getStableBounds(any())).thenAnswer { i -> + (i.arguments.first() as Rect).set(stableBounds) + } + whenever(context.resources).thenReturn(resources) + whenever(resources.getDimensionPixelSize(any())).thenReturn(split_divider_width) + desktopWindowDecoration.mTaskInfo = task1 + task1.minWidth = 0 + task1.minHeight = 0 + initTiledTaskHelperMock(task1) + desktopWindowDecoration.mDecorWindowContext = context + whenever(resources.getBoolean(any())).thenReturn(true) + + // Act + tilingDecoration.onAppTiled( + task1, + desktopWindowDecoration, + DesktopTasksController.SnapPosition.RIGHT, + BOUNDS, + ) + tilingDecoration.onAppTiled( + task2, + desktopWindowDecoration, + DesktopTasksController.SnapPosition.LEFT, + BOUNDS, + ) + + tilingDecoration.leftTaskResizingHelper = tiledTaskHelper + tilingDecoration.rightTaskResizingHelper = tiledTaskHelper + tilingDecoration.onDividerHandleMoved(BOUNDS, transaction) + + // Assert + verify(transaction, times(1)).apply() + // Show should be called twice for each tiled app, to show the veil and the icon for each + // of them. + verify(tiledTaskHelper, times(2)).showVeil(any()) + + // Move again + tilingDecoration.onDividerHandleMoved(BOUNDS, transaction) + verify(tiledTaskHelper, times(2)).updateVeil(any()) + verify(transitions, never()).startTransition(any(), any(), any()) + + // End moving, no startTransition because bounds did not change. + tiledTaskHelper.newBounds.set(BOUNDS) + tilingDecoration.onDividerHandleDragEnd(BOUNDS, transaction) + verify(tiledTaskHelper, times(2)).hideVeil() + verify(transitions, never()).startTransition(any(), any(), any()) + + // Move then end again with bounds changing to ensure startTransition is called. + tilingDecoration.onDividerHandleMoved(BOUNDS, transaction) + tilingDecoration.onDividerHandleDragEnd(BOUNDS, transaction) + verify(transitions, times(1)) + .startTransition(eq(TRANSIT_CHANGE), any(), eq(tilingDecoration)) + // No hide veil until start animation is called. + verify(tiledTaskHelper, times(2)).hideVeil() + + tilingDecoration.startAnimation(transition, info, transaction, transaction, finishCallback) + // the startAnimation function should hide the veils. + verify(tiledTaskHelper, times(4)).hideVeil() + } + + private fun initTiledTaskHelperMock(taskInfo: ActivityManager.RunningTaskInfo) { + whenever(tiledTaskHelper.bounds).thenReturn(BOUNDS) + whenever(tiledTaskHelper.taskInfo).thenReturn(taskInfo) + whenever(tiledTaskHelper.newBounds).thenReturn(Rect(BOUNDS)) + whenever(tiledTaskHelper.desktopModeWindowDecoration).thenReturn(desktopWindowDecoration) + } + private fun assertRectEqual(rect1: Rect, rect2: Rect) { assertThat(rect1.left).isEqualTo(rect2.left) assertThat(rect1.right).isEqualTo(rect2.right) |