summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--libs/WindowManager/Shell/src/com/android/wm/shell/dagger/WMShellModule.java13
-rw-r--r--libs/WindowManager/Shell/src/com/android/wm/shell/desktopmode/DesktopTasksController.kt32
-rw-r--r--libs/WindowManager/Shell/src/com/android/wm/shell/desktopmode/DragToDisplayTransitionHandler.kt57
-rw-r--r--libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/desktopmode/DesktopTasksControllerTest.kt4
-rw-r--r--libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/desktopmode/DragToDisplayTransitionHandlerTest.kt101
5 files changed, 188 insertions, 19 deletions
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/dagger/WMShellModule.java b/libs/WindowManager/Shell/src/com/android/wm/shell/dagger/WMShellModule.java
index 59acdc574434..48fadc02ff1f 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/dagger/WMShellModule.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/dagger/WMShellModule.java
@@ -100,6 +100,7 @@ import com.android.wm.shell.desktopmode.DesktopTasksLimiter;
import com.android.wm.shell.desktopmode.DesktopTasksTransitionObserver;
import com.android.wm.shell.desktopmode.DesktopUserRepositories;
import com.android.wm.shell.desktopmode.DragToDesktopTransitionHandler;
+import com.android.wm.shell.desktopmode.DragToDisplayTransitionHandler;
import com.android.wm.shell.desktopmode.EnterDesktopTaskTransitionHandler;
import com.android.wm.shell.desktopmode.ExitDesktopTaskTransitionHandler;
import com.android.wm.shell.desktopmode.OverviewToDesktopTransitionObserver;
@@ -770,7 +771,8 @@ public abstract class WMShellModule {
DesksOrganizer desksOrganizer,
DesksTransitionObserver desksTransitionObserver,
UserProfileContexts userProfileContexts,
- DesktopModeCompatPolicy desktopModeCompatPolicy) {
+ DesktopModeCompatPolicy desktopModeCompatPolicy,
+ DragToDisplayTransitionHandler dragToDisplayTransitionHandler) {
return new DesktopTasksController(
context,
shellInit,
@@ -808,7 +810,8 @@ public abstract class WMShellModule {
desksOrganizer,
desksTransitionObserver,
userProfileContexts,
- desktopModeCompatPolicy);
+ desktopModeCompatPolicy,
+ dragToDisplayTransitionHandler);
}
@WMSingleton
@@ -934,6 +937,12 @@ public abstract class WMShellModule {
@WMSingleton
@Provides
+ static DragToDisplayTransitionHandler provideDragToDisplayTransitionHandler() {
+ return new DragToDisplayTransitionHandler();
+ }
+
+ @WMSingleton
+ @Provides
static Optional<DesktopModeKeyGestureHandler> provideDesktopModeKeyGestureHandler(
Context context,
Optional<DesktopModeWindowDecorViewModel> desktopModeWindowDecorViewModel,
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 e8d6fcf49eaa..fca5084b65bc 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
@@ -205,6 +205,7 @@ class DesktopTasksController(
private val desksTransitionObserver: DesksTransitionObserver,
private val userProfileContexts: UserProfileContexts,
private val desktopModeCompatPolicy: DesktopModeCompatPolicy,
+ private val dragToDisplayTransitionHandler: DragToDisplayTransitionHandler,
) :
RemoteCallable<DesktopTasksController>,
Transitions.TransitionHandler,
@@ -3188,25 +3189,24 @@ class DesktopTasksController(
val wct = WindowContainerTransaction()
wct.setBounds(taskInfo.token, destinationBounds)
- // TODO: b/362720497 - reparent to a specific desk within the target display.
- // Reparent task if it has been moved to a new display.
- if (Flags.enableConnectedDisplaysWindowDrag()) {
- val newDisplayId = motionEvent.getDisplayId()
- if (newDisplayId != taskInfo.getDisplayId()) {
- val displayAreaInfo =
- rootTaskDisplayAreaOrganizer.getDisplayAreaInfo(newDisplayId)
- if (displayAreaInfo == null) {
- logW(
- "Task reparent cannot find DisplayAreaInfo for displayId=%d",
- newDisplayId,
- )
- } else {
- wct.reparent(taskInfo.token, displayAreaInfo.token, /* onTop= */ true)
- }
+ val newDisplayId = motionEvent.getDisplayId()
+ val displayAreaInfo = rootTaskDisplayAreaOrganizer.getDisplayAreaInfo(newDisplayId)
+ val isCrossDisplayDrag =
+ Flags.enableConnectedDisplaysWindowDrag() &&
+ newDisplayId != taskInfo.getDisplayId() &&
+ displayAreaInfo != null
+ val handler =
+ if (isCrossDisplayDrag) {
+ 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)
}
- transitions.startTransition(TRANSIT_CHANGE, wct, null)
+ transitions.startTransition(TRANSIT_CHANGE, wct, handler)
releaseVisualIndicator()
}
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/desktopmode/DragToDisplayTransitionHandler.kt b/libs/WindowManager/Shell/src/com/android/wm/shell/desktopmode/DragToDisplayTransitionHandler.kt
new file mode 100644
index 000000000000..d51576a5148e
--- /dev/null
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/desktopmode/DragToDisplayTransitionHandler.kt
@@ -0,0 +1,57 @@
+/*
+ * Copyright (C) 2025 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package com.android.wm.shell.desktopmode
+
+import android.os.IBinder
+import android.view.SurfaceControl
+import android.window.TransitionInfo
+import android.window.TransitionRequestInfo
+import android.window.WindowContainerTransaction
+import com.android.wm.shell.transition.Transitions
+
+/** Handles the transition to drag a window to another display by dragging the caption. */
+class DragToDisplayTransitionHandler : Transitions.TransitionHandler {
+ override fun handleRequest(
+ transition: IBinder,
+ request: TransitionRequestInfo,
+ ): WindowContainerTransaction? {
+ return null
+ }
+
+ override fun startAnimation(
+ transition: IBinder,
+ info: TransitionInfo,
+ startTransaction: SurfaceControl.Transaction,
+ finishTransaction: SurfaceControl.Transaction,
+ finishCallback: Transitions.TransitionFinishCallback,
+ ): Boolean {
+ for (change in info.changes) {
+ val sc = change.leash
+ val endBounds = change.endAbsBounds
+ val endPosition = change.endRelOffset
+ startTransaction
+ .setWindowCrop(sc, endBounds.width(), endBounds.height())
+ .setPosition(sc, endPosition.x.toFloat(), endPosition.y.toFloat())
+ finishTransaction
+ .setWindowCrop(sc, endBounds.width(), endBounds.height())
+ .setPosition(sc, endPosition.x.toFloat(), endPosition.y.toFloat())
+ }
+
+ startTransaction.apply()
+ finishCallback.onTransitionFinished(null)
+ return true
+ }
+}
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 253740c8c3fd..04acaef344eb 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
@@ -261,6 +261,7 @@ class DesktopTasksControllerTest(flags: FlagsParameterization) : ShellTestCase()
@Mock private lateinit var desksTransitionsObserver: DesksTransitionObserver
@Mock private lateinit var packageManager: PackageManager
@Mock private lateinit var mockDisplayContext: Context
+ @Mock private lateinit var dragToDisplayTransitionHandler: DragToDisplayTransitionHandler
private lateinit var controller: DesktopTasksController
private lateinit var shellInit: ShellInit
@@ -431,6 +432,7 @@ class DesktopTasksControllerTest(flags: FlagsParameterization) : ShellTestCase()
desksTransitionsObserver,
userProfileContexts,
desktopModeCompatPolicy,
+ dragToDisplayTransitionHandler,
)
@After
@@ -5008,7 +5010,7 @@ class DesktopTasksControllerTest(flags: FlagsParameterization) : ShellTestCase()
Mockito.argThat { wct ->
return@argThat wct.hierarchyOps[0].isReparent
},
- eq(null),
+ eq(dragToDisplayTransitionHandler),
)
}
diff --git a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/desktopmode/DragToDisplayTransitionHandlerTest.kt b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/desktopmode/DragToDisplayTransitionHandlerTest.kt
new file mode 100644
index 000000000000..51c302983fd0
--- /dev/null
+++ b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/desktopmode/DragToDisplayTransitionHandlerTest.kt
@@ -0,0 +1,101 @@
+/*
+ * Copyright (C) 2025 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package com.android.wm.shell.desktopmode
+
+import android.graphics.Point
+import android.graphics.Rect
+import android.os.IBinder
+import android.view.SurfaceControl
+import android.window.TransitionInfo
+import android.window.TransitionRequestInfo
+import com.android.wm.shell.transition.Transitions
+import org.junit.Before
+import org.junit.Test
+import org.mockito.Mockito.verify
+import org.mockito.kotlin.any
+import org.mockito.kotlin.doReturn
+import org.mockito.kotlin.mock
+import org.mockito.kotlin.whenever
+
+/**
+ * Test class for {@link DragToDisplayTransitionHandler}
+ *
+ * Usage: atest WMShellUnitTests:DragToDisplayTransitionHandlerTest
+ */
+class DragToDisplayTransitionHandlerTest {
+ private lateinit var handler: DragToDisplayTransitionHandler
+ private val mockTransition: IBinder = mock()
+ private val mockRequestInfo: TransitionRequestInfo = mock()
+ private val mockTransitionInfo: TransitionInfo = mock()
+ private val mockStartTransaction: SurfaceControl.Transaction = mock()
+ private val mockFinishTransaction: SurfaceControl.Transaction = mock()
+ private val mockFinishCallback: Transitions.TransitionFinishCallback = mock()
+
+ @Before
+ fun setUp() {
+ handler = DragToDisplayTransitionHandler()
+ whenever(mockStartTransaction.setWindowCrop(any(), any(), any()))
+ .thenReturn(mockStartTransaction)
+ whenever(mockFinishTransaction.setWindowCrop(any(), any(), any()))
+ .thenReturn(mockFinishTransaction)
+ }
+
+ @Test
+ fun handleRequest_anyRequest_returnsNull() {
+ val result = handler.handleRequest(mockTransition, mockRequestInfo)
+ assert(result == null)
+ }
+
+ @Test
+ fun startAnimation_verifyTransformationsApplied() {
+ val mockChange1 = mock<TransitionInfo.Change>()
+ val leash1 = mock<SurfaceControl>()
+ val endBounds1 = Rect(0, 0, 50, 50)
+ val endPosition1 = Point(5, 5)
+
+ whenever(mockChange1.leash).doReturn(leash1)
+ whenever(mockChange1.endAbsBounds).doReturn(endBounds1)
+ whenever(mockChange1.endRelOffset).doReturn(endPosition1)
+
+ val mockChange2 = mock<TransitionInfo.Change>()
+ val leash2 = mock<SurfaceControl>()
+ val endBounds2 = Rect(100, 100, 200, 150)
+ val endPosition2 = Point(15, 25)
+
+ whenever(mockChange2.leash).doReturn(leash2)
+ whenever(mockChange2.endAbsBounds).doReturn(endBounds2)
+ whenever(mockChange2.endRelOffset).doReturn(endPosition2)
+
+ whenever(mockTransitionInfo.changes).doReturn(listOf(mockChange1, mockChange2))
+
+ handler.startAnimation(
+ mockTransition,
+ mockTransitionInfo,
+ mockStartTransaction,
+ mockFinishTransaction,
+ mockFinishCallback,
+ )
+
+ verify(mockStartTransaction).setWindowCrop(leash1, endBounds1.width(), endBounds1.height())
+ verify(mockStartTransaction)
+ .setPosition(leash1, endPosition1.x.toFloat(), endPosition1.y.toFloat())
+ verify(mockStartTransaction).setWindowCrop(leash2, endBounds2.width(), endBounds2.height())
+ verify(mockStartTransaction)
+ .setPosition(leash2, endPosition2.x.toFloat(), endPosition2.y.toFloat())
+ verify(mockStartTransaction).apply()
+ verify(mockFinishCallback).onTransitionFinished(null)
+ }
+}