Merge "Extend DesktopTaskListener Functionality" into main
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/desktopmode/DesktopModeUtils.kt b/libs/WindowManager/Shell/src/com/android/wm/shell/desktopmode/DesktopModeUtils.kt
index b68b436..c8ffe28 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/desktopmode/DesktopModeUtils.kt
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/desktopmode/DesktopModeUtils.kt
@@ -171,6 +171,18 @@
minOf(appBounds.height(), appBounds.width()).toFloat()
}
+/** Returns true if task's width or height is maximized else returns false. */
+fun isTaskWidthOrHeightEqual(taskBounds: Rect, stableBounds: Rect): Boolean {
+ return taskBounds.width() == stableBounds.width() ||
+ taskBounds.height() == stableBounds.height()
+}
+
+/** Returns true if task bound is equal to stable bounds else returns false. */
+fun isTaskBoundsEqual(taskBounds: Rect, stableBounds: Rect): Boolean {
+ return taskBounds.width() == stableBounds.width() &&
+ taskBounds.height() == stableBounds.height()
+}
+
/**
* Calculates the desired initial bounds for applications in desktop windowing. This is done as a
* scale of the screen bounds.
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 2852631..90f8276 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
@@ -169,6 +169,9 @@
}
}
+ @VisibleForTesting
+ var taskbarDesktopTaskListener: TaskbarDesktopTaskListener? = null
+
/** Task id of the task currently being dragged from fullscreen/split. */
val draggingTaskId
get() = dragToDesktopTransitionHandler.draggingTaskId
@@ -610,13 +613,10 @@
val currentTaskBounds = taskInfo.configuration.windowConfiguration.bounds
val destinationBounds = Rect()
- val isMaximized = if (taskInfo.isResizeable) {
- currentTaskBounds == stableBounds
- } else {
- currentTaskBounds.width() == stableBounds.width()
- || currentTaskBounds.height() == stableBounds.height()
- }
-
+ val isMaximized = isTaskMaximized(taskInfo, stableBounds)
+ // If the task is currently maximized, we will toggle it not to be and vice versa. This is
+ // helpful to eliminate the current task from logic to calculate taskbar corner rounding.
+ val willMaximize = !isMaximized
if (isMaximized) {
// The desktop task is at the maximized width and/or height of the stable bounds.
// If the task's pre-maximize stable bounds were saved, toggle the task to those bounds.
@@ -651,6 +651,18 @@
}
}
+
+
+ val shouldRestoreToSnap =
+ isMaximized && isTaskSnappedToHalfScreen(taskInfo, destinationBounds)
+
+ logD("willMaximize = %s", willMaximize)
+ logD("shouldRestoreToSnap = %s", shouldRestoreToSnap)
+
+ val doesAnyTaskRequireTaskbarRounding = willMaximize || shouldRestoreToSnap ||
+ doesAnyTaskRequireTaskbarRounding(taskInfo.displayId, taskInfo.taskId)
+
+ taskbarDesktopTaskListener?.onTaskbarCornerRoundingUpdate(doesAnyTaskRequireTaskbarRounding)
val wct = WindowContainerTransaction().setBounds(taskInfo.token, destinationBounds)
if (Transitions.ENABLE_SHELL_TRANSITIONS) {
toggleResizeDesktopTaskTransitionHandler.startTransition(wct)
@@ -659,6 +671,65 @@
}
}
+ private fun isTaskMaximized(
+ taskInfo: RunningTaskInfo,
+ stableBounds: Rect
+ ): Boolean {
+ val currentTaskBounds = taskInfo.configuration.windowConfiguration.bounds
+
+ return if (taskInfo.isResizeable) {
+ isTaskBoundsEqual(currentTaskBounds, stableBounds)
+ } else {
+ isTaskWidthOrHeightEqual(currentTaskBounds, stableBounds)
+ }
+ }
+
+ private fun isMaximizedToStableBoundsEdges(
+ taskInfo: RunningTaskInfo,
+ stableBounds: Rect
+ ): Boolean {
+ val currentTaskBounds = taskInfo.configuration.windowConfiguration.bounds
+ return isTaskBoundsEqual(currentTaskBounds, stableBounds)
+ }
+
+ /** Returns if current task bound is snapped to half screen */
+ private fun isTaskSnappedToHalfScreen(
+ taskInfo: RunningTaskInfo,
+ taskBounds: Rect = taskInfo.configuration.windowConfiguration.bounds
+ ): Boolean =
+ getSnapBounds(taskInfo, SnapPosition.LEFT) == taskBounds ||
+ getSnapBounds(taskInfo, SnapPosition.RIGHT) == taskBounds
+
+ @VisibleForTesting
+ fun doesAnyTaskRequireTaskbarRounding(
+ displayId: Int,
+ excludeTaskId: Int? = null,
+ ): Boolean {
+ val doesAnyTaskRequireTaskbarRounding =
+ taskRepository.getActiveNonMinimizedOrderedTasks(displayId)
+ // exclude current task since maximize/restore transition has not taken place yet.
+ .filterNot { taskId -> taskId == excludeTaskId }
+ .any { taskId ->
+ val taskInfo = shellTaskOrganizer.getRunningTaskInfo(taskId)!!
+ val displayLayout = displayController.getDisplayLayout(taskInfo.displayId)
+ val stableBounds = Rect().apply { displayLayout?.getStableBounds(this) }
+ logD("taskInfo = %s", taskInfo)
+ logD(
+ "isTaskSnappedToHalfScreen(taskInfo) = %s",
+ isTaskSnappedToHalfScreen(taskInfo)
+ )
+ logD(
+ "isMaximizedToStableBoundsEdges(taskInfo, stableBounds) = %s",
+ isMaximizedToStableBoundsEdges(taskInfo, stableBounds)
+ )
+ isTaskSnappedToHalfScreen(taskInfo)
+ || isMaximizedToStableBoundsEdges(taskInfo, stableBounds)
+ }
+
+ logD("doesAnyTaskRequireTaskbarRounding = %s", doesAnyTaskRequireTaskbarRounding)
+ return doesAnyTaskRequireTaskbarRounding
+ }
+
/**
* Quick-resize to the right or left half of the stable bounds.
*
@@ -673,9 +744,9 @@
position: SnapPosition
) {
val destinationBounds = getSnapBounds(taskInfo, position)
-
if (destinationBounds == taskInfo.configuration.windowConfiguration.bounds) return
+ taskbarDesktopTaskListener?.onTaskbarCornerRoundingUpdate(true)
val wct = WindowContainerTransaction().setBounds(taskInfo.token, destinationBounds)
if (Transitions.ENABLE_SHELL_TRANSITIONS) {
toggleResizeDesktopTaskTransitionHandler.startTransition(wct, currentDragBounds)
@@ -806,6 +877,10 @@
.mapNotNull { taskId -> shellTaskOrganizer.getRunningTaskInfo(taskId) }
.reversed() // Start from the back so the front task is brought forward last
.forEach { task -> wct.reorder(task.token, /* onTop= */ true) }
+
+ taskbarDesktopTaskListener?.
+ onTaskbarCornerRoundingUpdate(doesAnyTaskRequireTaskbarRounding(displayId))
+
return taskToMinimize
}
@@ -1156,6 +1231,12 @@
) {
wct.removeTask(task.token)
}
+ taskbarDesktopTaskListener?.onTaskbarCornerRoundingUpdate(
+ doesAnyTaskRequireTaskbarRounding(
+ task.displayId,
+ task.id
+ )
+ )
return if (wct.isEmpty) null else wct
}
@@ -1450,6 +1531,8 @@
}
// A freeform drag-move ended, remove the indicator immediately.
releaseVisualIndicator()
+ taskbarDesktopTaskListener
+ ?.onTaskbarCornerRoundingUpdate(doesAnyTaskRequireTaskbarRounding(taskInfo.displayId))
}
/**
@@ -1686,17 +1769,39 @@
}
}
+ private val mTaskbarDesktopTaskListener: TaskbarDesktopTaskListener =
+ object : TaskbarDesktopTaskListener {
+ override fun onTaskbarCornerRoundingUpdate(
+ hasTasksRequiringTaskbarRounding: Boolean) {
+ ProtoLog.v(
+ WM_SHELL_DESKTOP_MODE,
+ "IDesktopModeImpl: onTaskbarCornerRoundingUpdate " +
+ "doesAnyTaskRequireTaskbarRounding=%s",
+ hasTasksRequiringTaskbarRounding
+ )
+
+ remoteListener.call { l ->
+ l.onTaskbarCornerRoundingUpdate(hasTasksRequiringTaskbarRounding)
+ }
+ }
+ }
+
init {
remoteListener =
SingleInstanceRemoteListener<DesktopTasksController, IDesktopTaskListener>(
controller,
{ c ->
- c.taskRepository.addVisibleTasksListener(
- listener,
- c.mainExecutor
- )
+ run {
+ c.taskRepository.addVisibleTasksListener(listener, c.mainExecutor)
+ c.taskbarDesktopTaskListener = mTaskbarDesktopTaskListener
+ }
},
- { c -> c.taskRepository.removeVisibleTasksListener(listener) }
+ { c ->
+ run {
+ c.taskRepository.removeVisibleTasksListener(listener)
+ c.taskbarDesktopTaskListener = null
+ }
+ }
)
}
@@ -1779,6 +1884,16 @@
private const val TAG = "DesktopTasksController"
}
+ /** Defines interface for classes that can listen to changes for task resize. */
+ // TODO(b/343931111): Migrate to using TransitionObservers when ready
+ interface TaskbarDesktopTaskListener {
+ /**
+ * [hasTasksRequiringTaskbarRounding] is true when a task is either maximized or snapped
+ * left/right and rounded corners are enabled.
+ */
+ fun onTaskbarCornerRoundingUpdate(hasTasksRequiringTaskbarRounding: Boolean)
+ }
+
/** The positions on a screen that a task can snap to. */
enum class SnapPosition {
RIGHT,
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/desktopmode/IDesktopTaskListener.aidl b/libs/WindowManager/Shell/src/com/android/wm/shell/desktopmode/IDesktopTaskListener.aidl
index 8ebdfdc..c2acb87 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/desktopmode/IDesktopTaskListener.aidl
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/desktopmode/IDesktopTaskListener.aidl
@@ -27,4 +27,10 @@
/** @deprecated this is no longer supported. */
oneway void onStashedChanged(int displayId, boolean stashed);
+
+ /**
+ * Shows taskbar corner radius when running desktop tasks are updated if
+ * [hasTasksRequiringTaskbarRounding] is true.
+ */
+ oneway void onTaskbarCornerRoundingUpdate(boolean hasTasksRequiringTaskbarRounding);
}
\ No newline at end of file
diff --git a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/desktopmode/DesktopModeUtilsTest.kt b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/desktopmode/DesktopModeUtilsTest.kt
new file mode 100644
index 0000000..8fab410
--- /dev/null
+++ b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/desktopmode/DesktopModeUtilsTest.kt
@@ -0,0 +1,66 @@
+/*
+ * Copyright (C) 2024 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.Rect
+import android.testing.AndroidTestingRunner
+import androidx.test.filters.SmallTest
+import com.google.common.truth.Truth.assertThat
+import org.junit.Test
+import org.junit.runner.RunWith
+
+@SmallTest
+@RunWith(AndroidTestingRunner::class)
+class DesktopModeUtilsTest {
+ @Test
+ fun isTaskBoundsEqual_stableBoundsAreEqual_returnTrue() {
+ assertThat(isTaskBoundsEqual(task2Bounds, stableBounds)).isTrue()
+ }
+
+ @Test
+ fun isTaskBoundsEqual_stableBoundsAreNotEqual_returnFalse() {
+ assertThat(isTaskBoundsEqual(task4Bounds, stableBounds)).isFalse()
+ }
+
+ @Test
+ fun isTaskWidthOrHeightEqual_stableBoundsAreEqual_returnTrue() {
+ assertThat(isTaskWidthOrHeightEqual(task2Bounds, stableBounds)).isTrue()
+ }
+
+ @Test
+ fun isTaskWidthOrHeightEqual_stableBoundWidthIsEquals_returnTrue() {
+ assertThat(isTaskWidthOrHeightEqual(task3Bounds, stableBounds)).isTrue()
+ }
+
+ @Test
+ fun isTaskWidthOrHeightEqual_stableBoundHeightIsEquals_returnTrue() {
+ assertThat(isTaskWidthOrHeightEqual(task3Bounds, stableBounds)).isTrue()
+ }
+
+ @Test
+ fun isTaskWidthOrHeightEqual_stableBoundsWidthOrHeightAreNotEquals_returnFalse() {
+ assertThat(isTaskWidthOrHeightEqual(task1Bounds, stableBounds)).isTrue()
+ }
+
+ private companion object {
+ val task1Bounds = Rect(0, 0, 0, 0)
+ val task2Bounds = Rect(1, 1, 1, 1)
+ val task3Bounds = Rect(0, 1, 0, 1)
+ val task4Bounds = Rect(1, 2, 2, 1)
+ val stableBounds = Rect(1, 1, 1, 1)
+ }
+}
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 d248720..5474e53 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
@@ -84,6 +84,7 @@
import com.android.wm.shell.common.ShellExecutor
import com.android.wm.shell.common.SyncTransactionQueue
import com.android.wm.shell.desktopmode.DesktopTasksController.SnapPosition
+import com.android.wm.shell.desktopmode.DesktopTasksController.TaskbarDesktopTaskListener
import com.android.wm.shell.desktopmode.DesktopTestHelpers.Companion.createFreeformTask
import com.android.wm.shell.desktopmode.DesktopTestHelpers.Companion.createFullscreenTask
import com.android.wm.shell.desktopmode.DesktopTestHelpers.Companion.createHomeTask
@@ -175,6 +176,7 @@
@Mock
private lateinit var mockInteractionJankMonitor: InteractionJankMonitor
@Mock private lateinit var mockSurface: SurfaceControl
+ @Mock private lateinit var taskbarDesktopTaskListener: TaskbarDesktopTaskListener
private lateinit var mockitoSession: StaticMockitoSession
private lateinit var controller: DesktopTasksController
@@ -237,6 +239,8 @@
val captor = ArgumentCaptor.forClass(RecentsTransitionStateListener::class.java)
verify(recentsTransitionHandler).addTransitionStateListener(captor.capture())
recentsTransitionStateListener = captor.value
+
+ controller.taskbarDesktopTaskListener = taskbarDesktopTaskListener
}
private fun createController(): DesktopTasksController {
@@ -282,6 +286,52 @@
}
@Test
+ fun doesAnyTaskRequireTaskbarRounding_onlyFreeFormTaskIsRunning_returnFalse() {
+ setUpFreeformTask()
+
+ assertThat(controller.doesAnyTaskRequireTaskbarRounding(DEFAULT_DISPLAY)).isFalse()
+ }
+
+ @Test
+ fun doesAnyTaskRequireTaskbarRounding_toggleResizeOfFreeFormTask_returnTrue() {
+ val task1 = setUpFreeformTask()
+
+ val argumentCaptor = ArgumentCaptor.forClass(Boolean::class.java)
+ controller.toggleDesktopTaskSize(task1)
+ verify(taskbarDesktopTaskListener).onTaskbarCornerRoundingUpdate(argumentCaptor.capture())
+
+ assertThat(argumentCaptor.value).isTrue()
+ }
+
+ @Test
+ fun doesAnyTaskRequireTaskbarRounding_fullScreenTaskIsRunning_returnTrue() {
+ val stableBounds = Rect().apply { displayLayout.getStableBounds(this) }
+ setUpFreeformTask(bounds = stableBounds, active = true)
+ assertThat(controller.doesAnyTaskRequireTaskbarRounding(DEFAULT_DISPLAY)).isTrue()
+ }
+
+ @Test
+ fun doesAnyTaskRequireTaskbarRounding_toggleResizeOfFullScreenTask_returnFalse() {
+ val stableBounds = Rect().apply { displayLayout.getStableBounds(this) }
+ val task1 = setUpFreeformTask(bounds = stableBounds, active = true)
+
+ val argumentCaptor = ArgumentCaptor.forClass(Boolean::class.java)
+ controller.toggleDesktopTaskSize(task1)
+ verify(taskbarDesktopTaskListener).onTaskbarCornerRoundingUpdate(argumentCaptor.capture())
+
+ assertThat(argumentCaptor.value).isFalse()
+ }
+
+ @Test
+ fun doesAnyTaskRequireTaskbarRounding_splitScreenTaskIsRunning_returnTrue() {
+ val stableBounds = Rect().apply { displayLayout.getStableBounds(this) }
+ setUpFreeformTask(bounds = Rect(stableBounds.left, stableBounds.top, 500, stableBounds.bottom))
+
+ assertThat(controller.doesAnyTaskRequireTaskbarRounding(DEFAULT_DISPLAY)).isTrue()
+ }
+
+
+ @Test
fun instantiate_cannotEnterDesktopMode_doNotAddInitCallback() {
whenever(DesktopModeStatus.canEnterDesktopMode(context)).thenReturn(false)
clearInvocations(shellInit)