summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--libs/WindowManager/Shell/src/com/android/wm/shell/windowdecor/tiling/DesktopTilingWindowDecoration.kt107
-rw-r--r--libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/windowdecor/tiling/DesktopTilingWindowDecorationTest.kt82
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)