diff options
2 files changed, 116 insertions, 14 deletions
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/desktopmode/DesktopTasksTransitionObserver.kt b/libs/WindowManager/Shell/src/com/android/wm/shell/desktopmode/DesktopTasksTransitionObserver.kt index a4bc2fe9460b..0b1bb8f36fa8 100644 --- a/libs/WindowManager/Shell/src/com/android/wm/shell/desktopmode/DesktopTasksTransitionObserver.kt +++ b/libs/WindowManager/Shell/src/com/android/wm/shell/desktopmode/DesktopTasksTransitionObserver.kt @@ -21,6 +21,7 @@ import android.content.Context import android.os.IBinder import android.view.SurfaceControl import android.view.WindowManager +import android.view.WindowManager.TRANSIT_CLOSE import android.view.WindowManager.TRANSIT_TO_BACK import android.window.TransitionInfo import android.window.WindowContainerTransaction @@ -36,8 +37,8 @@ import com.android.wm.shell.transition.Transitions /** * A [Transitions.TransitionObserver] that observes shell transitions and updates the - * [DesktopRepository] state TODO: b/332682201 This observes transitions related to desktop - * mode and other transitions that originate both within and outside shell. + * [DesktopRepository] state TODO: b/332682201 This observes transitions related to desktop mode and + * other transitions that originate both within and outside shell. */ class DesktopTasksTransitionObserver( private val context: Context, @@ -47,6 +48,8 @@ class DesktopTasksTransitionObserver( shellInit: ShellInit ) : Transitions.TransitionObserver { + private var transitionToCloseWallpaper: IBinder? = null + init { if (DesktopModeStatus.canEnterDesktopMode(context)) { shellInit.addInitCallback(::onInit, this) @@ -70,6 +73,7 @@ class DesktopTasksTransitionObserver( handleBackNavigation(info) removeTaskIfNeeded(info) } + removeWallpaperOnLastTaskClosingIfNeeded(transition, info) } private fun removeTaskIfNeeded(info: TransitionInfo) { @@ -81,13 +85,9 @@ class DesktopTasksTransitionObserver( val taskInfo = change.taskInfo if (taskInfo == null || taskInfo.taskId == -1) continue - if (desktopRepository.isActiveTask(taskInfo.taskId) - && taskInfo.windowingMode != WINDOWING_MODE_FREEFORM - ) { - desktopRepository.removeFreeformTask( - taskInfo.displayId, - taskInfo.taskId - ) + if (desktopRepository.isActiveTask(taskInfo.taskId) && + taskInfo.windowingMode != WINDOWING_MODE_FREEFORM) { + desktopRepository.removeFreeformTask(taskInfo.displayId, taskInfo.taskId) } } } @@ -104,14 +104,32 @@ class DesktopTasksTransitionObserver( if (desktopRepository.getVisibleTaskCount(taskInfo.displayId) > 0 && change.mode == TRANSIT_TO_BACK && - taskInfo.windowingMode == WINDOWING_MODE_FREEFORM - ) { + taskInfo.windowingMode == WINDOWING_MODE_FREEFORM) { desktopRepository.minimizeTask(taskInfo.displayId, taskInfo.taskId) } } } } + private fun removeWallpaperOnLastTaskClosingIfNeeded( + transition: IBinder, + info: TransitionInfo + ) { + for (change in info.changes) { + val taskInfo = change.taskInfo + if (taskInfo == null || taskInfo.taskId == -1) { + continue + } + + if (desktopRepository.getVisibleTaskCount(taskInfo.displayId) == 1 && + change.mode == TRANSIT_CLOSE && + taskInfo.windowingMode == WINDOWING_MODE_FREEFORM && + desktopRepository.wallpaperActivityToken != null) { + transitionToCloseWallpaper = transition + } + } + } + override fun onTransitionStarting(transition: IBinder) { // TODO: b/332682201 Update repository state } @@ -122,6 +140,16 @@ class DesktopTasksTransitionObserver( override fun onTransitionFinished(transition: IBinder, aborted: Boolean) { // TODO: b/332682201 Update repository state + if (transitionToCloseWallpaper == transition) { + // TODO: b/362469671 - Handle merging the animation when desktop is also closing. + desktopRepository.wallpaperActivityToken?.let { wallpaperActivityToken -> + transitions.startTransition( + TRANSIT_CLOSE, + WindowContainerTransaction().removeTask(wallpaperActivityToken), + null) + } + transitionToCloseWallpaper = null + } } private fun updateWallpaperToken(info: TransitionInfo) { @@ -139,10 +167,9 @@ class DesktopTasksTransitionObserver( // task. shellTaskOrganizer.applyTransaction( WindowContainerTransaction() - .setTaskTrimmableFromRecents(taskInfo.token, false) - ) + .setTaskTrimmableFromRecents(taskInfo.token, false)) } - WindowManager.TRANSIT_CLOSE -> + TRANSIT_CLOSE -> desktopRepository.wallpaperActivityToken = null else -> {} } diff --git a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/desktopmode/DesktopTasksTransitionObserverTest.kt b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/desktopmode/DesktopTasksTransitionObserverTest.kt index 598df34a310d..fe87aa88a8db 100644 --- a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/desktopmode/DesktopTasksTransitionObserverTest.kt +++ b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/desktopmode/DesktopTasksTransitionObserverTest.kt @@ -22,26 +22,38 @@ import android.app.WindowConfiguration.WINDOWING_MODE_FULLSCREEN import android.content.ComponentName import android.content.Context import android.content.Intent +import android.os.IBinder import android.platform.test.annotations.EnableFlags import android.view.Display.DEFAULT_DISPLAY +import android.view.WindowManager +import android.view.WindowManager.TRANSIT_CLOSE import android.view.WindowManager.TRANSIT_OPEN import android.view.WindowManager.TRANSIT_TO_BACK import android.window.IWindowContainerToken import android.window.TransitionInfo import android.window.TransitionInfo.Change import android.window.WindowContainerToken +import android.window.WindowContainerTransaction +import android.window.WindowContainerTransaction.HierarchyOp.HIERARCHY_OP_TYPE_REMOVE_TASK import com.android.modules.utils.testing.ExtendedMockitoRule import com.android.window.flags.Flags +import com.android.wm.shell.MockToken import com.android.wm.shell.ShellTaskOrganizer import com.android.wm.shell.common.ShellExecutor import com.android.wm.shell.shared.desktopmode.DesktopModeStatus import com.android.wm.shell.sysui.ShellInit import com.android.wm.shell.transition.Transitions +import com.google.common.truth.Truth.assertThat +import com.google.common.truth.Truth.assertWithMessage import org.junit.Before import org.junit.Rule import org.junit.Test +import org.mockito.ArgumentCaptor +import org.mockito.ArgumentMatchers.eq +import org.mockito.ArgumentMatchers.isA import org.mockito.Mockito import org.mockito.kotlin.any +import org.mockito.kotlin.isNull import org.mockito.kotlin.mock import org.mockito.kotlin.never import org.mockito.kotlin.spy @@ -130,6 +142,27 @@ class DesktopTasksTransitionObserverTest { verify(taskRepository).removeFreeformTask(task.displayId, task.taskId) } + @Test + fun closeLastTask_wallpaperTokenExists_wallpaperIsRemoved() { + val mockTransition = Mockito.mock(IBinder::class.java) + val task = createTaskInfo(1, WINDOWING_MODE_FREEFORM) + val wallpaperToken = MockToken().token() + whenever(taskRepository.getVisibleTaskCount(task.displayId)).thenReturn(1) + whenever(taskRepository.wallpaperActivityToken).thenReturn(wallpaperToken) + + transitionObserver.onTransitionReady( + transition = mockTransition, + info = createCloseTransition(task), + startTransaction = mock(), + finishTransaction = mock(), + ) + transitionObserver.onTransitionFinished(mockTransition, false) + + val wct = getLatestWct(type = TRANSIT_CLOSE) + assertThat(wct.hierarchyOps).hasSize(1) + wct.assertRemoveAt(index = 0, wallpaperToken) + } + private fun createBackNavigationTransition( task: RunningTaskInfo? ): TransitionInfo { @@ -160,6 +193,48 @@ class DesktopTasksTransitionObserverTest { } } + private fun createCloseTransition( + task: RunningTaskInfo? + ): TransitionInfo { + return TransitionInfo(TRANSIT_CLOSE, 0 /* flags */).apply { + addChange( + Change(mock(), mock()).apply { + mode = TRANSIT_CLOSE + parent = null + taskInfo = task + flags = flags + } + ) + } + } + + private fun getLatestWct( + @WindowManager.TransitionType type: Int = TRANSIT_OPEN, + handlerClass: Class<out Transitions.TransitionHandler>? = null + ): WindowContainerTransaction { + val arg = ArgumentCaptor.forClass(WindowContainerTransaction::class.java) + if (handlerClass == null) { + Mockito.verify(transitions).startTransition(eq(type), arg.capture(), isNull()) + } else { + Mockito.verify(transitions) + .startTransition(eq(type), arg.capture(), isA(handlerClass)) + } + return arg.value + } + + private fun WindowContainerTransaction.assertRemoveAt(index: Int, token: WindowContainerToken) { + assertIndexInBounds(index) + val op = hierarchyOps[index] + assertThat(op.type).isEqualTo(HIERARCHY_OP_TYPE_REMOVE_TASK) + assertThat(op.container).isEqualTo(token.asBinder()) + } + + private fun WindowContainerTransaction.assertIndexInBounds(index: Int) { + assertWithMessage("WCT does not have a hierarchy operation at index $index") + .that(hierarchyOps.size) + .isGreaterThan(index) + } + private fun createTaskInfo(id: Int, windowingMode: Int = WINDOWING_MODE_FREEFORM) = RunningTaskInfo().apply { taskId = id |