diff options
12 files changed, 243 insertions, 88 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 e8add56619c4..408160d30467 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 @@ -1154,9 +1154,12 @@ public abstract class WMShellModule { Context context, ShellInit shellInit, Transitions transitions, - DesktopModeEventLogger desktopModeEventLogger) { + DesktopModeEventLogger desktopModeEventLogger, + Optional<DesktopTasksLimiter> desktopTasksLimiter, + ShellTaskOrganizer shellTaskOrganizer) { return new DesktopModeLoggerTransitionObserver( - context, shellInit, transitions, desktopModeEventLogger); + context, shellInit, transitions, desktopModeEventLogger, + desktopTasksLimiter, shellTaskOrganizer); } @WMSingleton diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/desktopmode/DesktopModeEventLogger.kt b/libs/WindowManager/Shell/src/com/android/wm/shell/desktopmode/DesktopModeEventLogger.kt index e8f9a789bb98..68bdbd1758b3 100644 --- a/libs/WindowManager/Shell/src/com/android/wm/shell/desktopmode/DesktopModeEventLogger.kt +++ b/libs/WindowManager/Shell/src/com/android/wm/shell/desktopmode/DesktopModeEventLogger.kt @@ -467,9 +467,13 @@ class DesktopModeEventLogger { FrameworkStatsLog .DESKTOP_MODE_SESSION_TASK_UPDATE__MINIMIZE_REASON__MINIMIZE_TASK_LIMIT ), - MINIMIZE_BUTTON( // TODO(b/356843241): use this enum value + MINIMIZE_BUTTON( FrameworkStatsLog.DESKTOP_MODE_SESSION_TASK_UPDATE__MINIMIZE_REASON__MINIMIZE_BUTTON ), + KEY_GESTURE( + FrameworkStatsLog + .DESKTOP_MODE_SESSION_TASK_UPDATE__MINIMIZE_REASON__MINIMIZE_KEY_GESTURE + ), } // Default value used when the task was not unminimized. diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/desktopmode/DesktopModeKeyGestureHandler.kt b/libs/WindowManager/Shell/src/com/android/wm/shell/desktopmode/DesktopModeKeyGestureHandler.kt index 1ddb834399cb..9334898fdb93 100644 --- a/libs/WindowManager/Shell/src/com/android/wm/shell/desktopmode/DesktopModeKeyGestureHandler.kt +++ b/libs/WindowManager/Shell/src/com/android/wm/shell/desktopmode/DesktopModeKeyGestureHandler.kt @@ -30,6 +30,7 @@ import com.android.window.flags.Flags.enableTaskResizingKeyboardShortcuts import com.android.wm.shell.ShellTaskOrganizer import com.android.wm.shell.common.DisplayController import com.android.wm.shell.common.ShellExecutor +import com.android.wm.shell.desktopmode.DesktopModeEventLogger.Companion.MinimizeReason import com.android.wm.shell.desktopmode.common.ToggleTaskSizeInteraction import com.android.wm.shell.protolog.ShellProtoLogGroup.WM_SHELL_DESKTOP_MODE import com.android.wm.shell.shared.annotations.ShellMainThread @@ -125,7 +126,9 @@ class DesktopModeKeyGestureHandler( KeyGestureEvent.KEY_GESTURE_TYPE_MINIMIZE_FREEFORM_WINDOW -> { logV("Key gesture MINIMIZE_FREEFORM_WINDOW is handled") getGloballyFocusedFreeformTask()?.let { - mainExecutor.execute { desktopTasksController.get().minimizeTask(it) } + mainExecutor.execute { + desktopTasksController.get().minimizeTask(it, MinimizeReason.KEY_GESTURE) + } } return true } diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/desktopmode/DesktopModeLoggerTransitionObserver.kt b/libs/WindowManager/Shell/src/com/android/wm/shell/desktopmode/DesktopModeLoggerTransitionObserver.kt index c09504ee3725..2dd89c790b58 100644 --- a/libs/WindowManager/Shell/src/com/android/wm/shell/desktopmode/DesktopModeLoggerTransitionObserver.kt +++ b/libs/WindowManager/Shell/src/com/android/wm/shell/desktopmode/DesktopModeLoggerTransitionObserver.kt @@ -36,6 +36,7 @@ import androidx.core.util.isNotEmpty import androidx.core.util.plus import androidx.core.util.putAll import com.android.internal.protolog.ProtoLog +import com.android.wm.shell.ShellTaskOrganizer import com.android.wm.shell.desktopmode.DesktopModeEventLogger.Companion.EnterReason import com.android.wm.shell.desktopmode.DesktopModeEventLogger.Companion.ExitReason import com.android.wm.shell.desktopmode.DesktopModeEventLogger.Companion.MinimizeReason @@ -52,6 +53,8 @@ import com.android.wm.shell.shared.TransitionUtil import com.android.wm.shell.shared.desktopmode.DesktopModeStatus import com.android.wm.shell.sysui.ShellInit import com.android.wm.shell.transition.Transitions +import java.util.Optional +import kotlin.jvm.optionals.getOrNull /** * A [Transitions.TransitionObserver] that observes transitions and the proposed changes to log @@ -63,6 +66,8 @@ class DesktopModeLoggerTransitionObserver( shellInit: ShellInit, private val transitions: Transitions, private val desktopModeEventLogger: DesktopModeEventLogger, + private val desktopTasksLimiter: Optional<DesktopTasksLimiter>, + private val shellTaskOrganizer: ShellTaskOrganizer, ) : Transitions.TransitionObserver { init { @@ -141,6 +146,7 @@ class DesktopModeLoggerTransitionObserver( // identify if we need to log any changes and update the state of visible freeform tasks identifyLogEventAndUpdateState( + transition = transition, transitionInfo = info, preTransitionVisibleFreeformTasks = visibleFreeformTaskInfos, postTransitionVisibleFreeformTasks = postTransitionVisibleFreeformTasks, @@ -227,6 +233,7 @@ class DesktopModeLoggerTransitionObserver( * state and update it */ private fun identifyLogEventAndUpdateState( + transition: IBinder, transitionInfo: TransitionInfo, preTransitionVisibleFreeformTasks: SparseArray<TaskInfo>, postTransitionVisibleFreeformTasks: SparseArray<TaskInfo>, @@ -238,6 +245,7 @@ class DesktopModeLoggerTransitionObserver( ) { // Sessions is finishing, log task updates followed by an exit event identifyAndLogTaskUpdates( + transition, transitionInfo, preTransitionVisibleFreeformTasks, postTransitionVisibleFreeformTasks, @@ -255,6 +263,7 @@ class DesktopModeLoggerTransitionObserver( desktopModeEventLogger.logSessionEnter(getEnterReason(transitionInfo)) identifyAndLogTaskUpdates( + transition, transitionInfo, preTransitionVisibleFreeformTasks, postTransitionVisibleFreeformTasks, @@ -262,6 +271,7 @@ class DesktopModeLoggerTransitionObserver( } else if (isSessionActive) { // Session is neither starting, nor finishing, log task updates if there are any identifyAndLogTaskUpdates( + transition, transitionInfo, preTransitionVisibleFreeformTasks, postTransitionVisibleFreeformTasks, @@ -275,6 +285,7 @@ class DesktopModeLoggerTransitionObserver( /** Compare the old and new state of taskInfos and identify and log the changes */ private fun identifyAndLogTaskUpdates( + transition: IBinder, transitionInfo: TransitionInfo, preTransitionVisibleFreeformTasks: SparseArray<TaskInfo>, postTransitionVisibleFreeformTasks: SparseArray<TaskInfo>, @@ -310,12 +321,9 @@ class DesktopModeLoggerTransitionObserver( // find old tasks that were removed preTransitionVisibleFreeformTasks.forEach { taskId, taskInfo -> if (!postTransitionVisibleFreeformTasks.containsKey(taskId)) { - val minimizeReason = - if (transitionInfo.type == Transitions.TRANSIT_MINIMIZE) { - MinimizeReason.MINIMIZE_BUTTON - } else { - null - } + // The task is no longer visible, it might have been minimized, get the minimize + // reason (if any) + val minimizeReason = getMinimizeReason(transition, transitionInfo, taskInfo) val taskUpdate = buildTaskUpdateForTask( taskInfo, @@ -336,6 +344,21 @@ class DesktopModeLoggerTransitionObserver( } } + private fun getMinimizeReason( + transition: IBinder, + transitionInfo: TransitionInfo, + taskInfo: TaskInfo, + ): MinimizeReason? { + if (transitionInfo.type == Transitions.TRANSIT_MINIMIZE) { + return MinimizeReason.MINIMIZE_BUTTON + } + val minimizingTask = desktopTasksLimiter.getOrNull()?.getMinimizingTask(transition) + if (minimizingTask?.taskId == taskInfo.taskId) { + return minimizingTask.minimizeReason + } + return null + } + private fun buildTaskUpdateForTask( taskInfo: TaskInfo, visibleTasks: Int, 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 91e06300f1b0..c0a66fde3005 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 @@ -87,6 +87,7 @@ import com.android.wm.shell.common.SyncTransactionQueue import com.android.wm.shell.compatui.isTopActivityExemptFromDesktopWindowing import com.android.wm.shell.compatui.isTransparentTask import com.android.wm.shell.desktopmode.DesktopModeEventLogger.Companion.InputMethod +import com.android.wm.shell.desktopmode.DesktopModeEventLogger.Companion.MinimizeReason import com.android.wm.shell.desktopmode.DesktopModeEventLogger.Companion.ResizeTrigger import com.android.wm.shell.desktopmode.DesktopModeUiEventLogger.DesktopUiEventEnum import com.android.wm.shell.desktopmode.DesktopModeVisualIndicator.DragStartState @@ -466,7 +467,9 @@ class DesktopTasksController( desktopModeEnterExitTransitionListener?.onEnterDesktopModeTransitionStarted( FREEFORM_ANIMATION_DURATION ) - taskIdToMinimize?.let { addPendingMinimizeTransition(transition, it) } + taskIdToMinimize?.let { + addPendingMinimizeTransition(transition, it, MinimizeReason.TASK_LIMIT) + } exitResult.asExit()?.runOnTransitionStart?.invoke(transition) return true } @@ -512,7 +515,9 @@ class DesktopTasksController( desktopModeEnterExitTransitionListener?.onEnterDesktopModeTransitionStarted( FREEFORM_ANIMATION_DURATION ) - taskIdToMinimize?.let { addPendingMinimizeTransition(transition, it) } + taskIdToMinimize?.let { + addPendingMinimizeTransition(transition, it, MinimizeReason.TASK_LIMIT) + } exitResult.asExit()?.runOnTransitionStart?.invoke(transition) } @@ -573,7 +578,9 @@ class DesktopTasksController( DRAG_TO_DESKTOP_FINISH_ANIM_DURATION_MS.toInt() ) transition?.let { - taskIdToMinimize?.let { taskId -> addPendingMinimizeTransition(it, taskId) } + taskIdToMinimize?.let { taskId -> + addPendingMinimizeTransition(it, taskId, MinimizeReason.TASK_LIMIT) + } exitResult.asExit()?.runOnTransitionStart?.invoke(transition) } } @@ -622,7 +629,7 @@ class DesktopTasksController( ?.runOnTransitionStart } - fun minimizeTask(taskInfo: RunningTaskInfo) { + fun minimizeTask(taskInfo: RunningTaskInfo, minimizeReason: MinimizeReason) { val wct = WindowContainerTransaction() val isMinimizingToPip = taskInfo.pictureInPictureParams?.isAutoEnterEnabled() ?: false @@ -642,16 +649,16 @@ class DesktopTasksController( freeformTaskTransitionStarter.startPipTransition(wct) taskRepository.setTaskInPip(taskInfo.displayId, taskInfo.taskId, enterPip = true) taskRepository.setOnPipAbortedCallback { displayId, taskId -> - minimizeTaskInner(shellTaskOrganizer.getRunningTaskInfo(taskId)!!) + minimizeTaskInner(shellTaskOrganizer.getRunningTaskInfo(taskId)!!, minimizeReason) taskRepository.setTaskInPip(displayId, taskId, enterPip = false) } return } - minimizeTaskInner(taskInfo) + minimizeTaskInner(taskInfo, minimizeReason) } - private fun minimizeTaskInner(taskInfo: RunningTaskInfo) { + private fun minimizeTaskInner(taskInfo: RunningTaskInfo, minimizeReason: MinimizeReason) { val taskId = taskInfo.taskId val displayId = taskInfo.displayId val wct = WindowContainerTransaction() @@ -671,6 +678,7 @@ class DesktopTasksController( transition = transition, displayId = displayId, taskId = taskId, + minimizeReason = minimizeReason, ) } exitResult.asExit()?.runOnTransitionStart?.invoke(transition) @@ -826,7 +834,7 @@ class DesktopTasksController( minimizingTaskId = taskIdToMinimize, exitingImmersiveTask = exitImmersiveResult.asExit()?.exitingTask, ) - taskIdToMinimize?.let { addPendingMinimizeTransition(t, it) } + taskIdToMinimize?.let { addPendingMinimizeTransition(t, it, MinimizeReason.TASK_LIMIT) } exitImmersiveResult.asExit()?.runOnTransitionStart?.invoke(t) return t } @@ -846,7 +854,7 @@ class DesktopTasksController( ) val t = transitions.startTransition(transitionType, wct, remoteTransitionHandler) remoteTransitionHandler.setTransition(t) - taskIdToMinimize.let { addPendingMinimizeTransition(t, it) } + taskIdToMinimize.let { addPendingMinimizeTransition(t, it, MinimizeReason.TASK_LIMIT) } exitImmersiveResult.asExit()?.runOnTransitionStart?.invoke(t) return t } @@ -1898,7 +1906,7 @@ class DesktopTasksController( val taskIdToMinimize = addAndGetMinimizeChanges(task.displayId, wct, task.taskId) addPendingAppLaunchTransition(transition, task.taskId, taskIdToMinimize) if (taskIdToMinimize != null) { - addPendingMinimizeTransition(transition, taskIdToMinimize) + addPendingMinimizeTransition(transition, taskIdToMinimize, MinimizeReason.TASK_LIMIT) return wct } if (!wct.isEmpty) { @@ -1932,7 +1940,9 @@ class DesktopTasksController( // Desktop Mode is already showing and we're launching a new Task - we might need to // minimize another Task. val taskIdToMinimize = addAndGetMinimizeChanges(task.displayId, wct, task.taskId) - taskIdToMinimize?.let { addPendingMinimizeTransition(transition, it) } + taskIdToMinimize?.let { + addPendingMinimizeTransition(transition, it, MinimizeReason.TASK_LIMIT) + } addPendingAppLaunchTransition(transition, task.taskId, taskIdToMinimize) desktopImmersiveController.exitImmersiveIfApplicable( transition, @@ -2180,13 +2190,18 @@ class DesktopTasksController( .addAndGetMinimizeTaskChanges(displayId, wct, newTaskId, launchingNewIntent) } - private fun addPendingMinimizeTransition(transition: IBinder, taskIdToMinimize: Int) { + private fun addPendingMinimizeTransition( + transition: IBinder, + taskIdToMinimize: Int, + minimizeReason: MinimizeReason, + ) { val taskToMinimize = shellTaskOrganizer.getRunningTaskInfo(taskIdToMinimize) desktopTasksLimiter.ifPresent { it.addPendingMinimizeChange( transition = transition, displayId = taskToMinimize?.displayId ?: DEFAULT_DISPLAY, taskId = taskIdToMinimize, + minimizeReason = minimizeReason, ) } } diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/desktopmode/DesktopTasksLimiter.kt b/libs/WindowManager/Shell/src/com/android/wm/shell/desktopmode/DesktopTasksLimiter.kt index e4a28e9efe60..204b39645248 100644 --- a/libs/WindowManager/Shell/src/com/android/wm/shell/desktopmode/DesktopTasksLimiter.kt +++ b/libs/WindowManager/Shell/src/com/android/wm/shell/desktopmode/DesktopTasksLimiter.kt @@ -30,6 +30,7 @@ import com.android.internal.jank.Cuj.CUJ_DESKTOP_MODE_MINIMIZE_WINDOW import com.android.internal.jank.InteractionJankMonitor import com.android.internal.protolog.ProtoLog import com.android.wm.shell.ShellTaskOrganizer +import com.android.wm.shell.desktopmode.DesktopModeEventLogger.Companion.MinimizeReason import com.android.wm.shell.protolog.ShellProtoLogGroup.WM_SHELL_DESKTOP_MODE import com.android.wm.shell.shared.annotations.ShellMainThread import com.android.wm.shell.sysui.UserChangeListener @@ -67,12 +68,21 @@ class DesktopTasksLimiter( logV("Starting limiter with a maximum of %d tasks", maxTasksLimit) } - private data class TaskDetails( + data class TaskDetails( val displayId: Int, val taskId: Int, - var transitionInfo: TransitionInfo?, + var transitionInfo: TransitionInfo? = null, + val minimizeReason: MinimizeReason? = null, ) + /** + * Returns the task being minimized in the given transition if that transition is a pending or + * active minimize transition. + */ + fun getMinimizingTask(transition: IBinder): TaskDetails? { + return minimizeTransitionObserver.getMinimizingTask(transition) + } + // TODO(b/333018485): replace this observer when implementing the minimize-animation private inner class MinimizeTransitionObserver : TransitionObserver { private val pendingTransitionTokensAndTasks = mutableMapOf<IBinder, TaskDetails>() @@ -82,6 +92,11 @@ class DesktopTasksLimiter( pendingTransitionTokensAndTasks[transition] = taskDetails } + fun getMinimizingTask(transition: IBinder): TaskDetails? { + return pendingTransitionTokensAndTasks[transition] + ?: activeTransitionTokensAndTasks[transition] + } + override fun onTransitionReady( transition: IBinder, info: TransitionInfo, @@ -89,6 +104,14 @@ class DesktopTasksLimiter( finishTransaction: SurfaceControl.Transaction, ) { val taskRepository = desktopUserRepositories.current + handleMinimizeTransition(taskRepository, transition, info) + } + + private fun handleMinimizeTransition( + taskRepository: DesktopRepository, + transition: IBinder, + info: TransitionInfo, + ) { val taskToMinimize = pendingTransitionTokensAndTasks.remove(transition) ?: return if (!taskRepository.isActiveTask(taskToMinimize.taskId)) return if (!isTaskReadyForMinimize(info, taskToMinimize)) { @@ -241,10 +264,15 @@ class DesktopTasksLimiter( * Add a pending minimize transition change to update the list of minimized apps once the * transition goes through. */ - fun addPendingMinimizeChange(transition: IBinder, displayId: Int, taskId: Int) { + fun addPendingMinimizeChange( + transition: IBinder, + displayId: Int, + taskId: Int, + minimizeReason: MinimizeReason, + ) { minimizeTransitionObserver.addPendingTransitionToken( transition, - TaskDetails(displayId, taskId, transitionInfo = null), + TaskDetails(displayId, taskId, transitionInfo = null, minimizeReason = minimizeReason), ) } diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/windowdecor/DesktopModeWindowDecorViewModel.java b/libs/WindowManager/Shell/src/com/android/wm/shell/windowdecor/DesktopModeWindowDecorViewModel.java index d6d393f2500c..eb3a698fb58e 100644 --- a/libs/WindowManager/Shell/src/com/android/wm/shell/windowdecor/DesktopModeWindowDecorViewModel.java +++ b/libs/WindowManager/Shell/src/com/android/wm/shell/windowdecor/DesktopModeWindowDecorViewModel.java @@ -34,6 +34,7 @@ import static com.android.internal.jank.Cuj.CUJ_DESKTOP_MODE_ENTER_MODE_APP_HAND import static com.android.window.flags.Flags.enableDisplayFocusInShellTransitions; import static com.android.wm.shell.compatui.AppCompatUtils.isTopActivityExemptFromDesktopWindowing; import static com.android.wm.shell.desktopmode.DesktopModeEventLogger.Companion.InputMethod; +import static com.android.wm.shell.desktopmode.DesktopModeEventLogger.Companion.MinimizeReason; import static com.android.wm.shell.desktopmode.DesktopModeEventLogger.Companion.ResizeTrigger; import static com.android.wm.shell.desktopmode.DesktopModeVisualIndicator.IndicatorType.TO_FULLSCREEN_INDICATOR; import static com.android.wm.shell.desktopmode.DesktopModeVisualIndicator.IndicatorType.TO_SPLIT_LEFT_INDICATOR; @@ -980,7 +981,8 @@ public class DesktopModeWindowDecorViewModel implements WindowDecorViewModel, ToggleTaskSizeInteraction.AmbiguousSource.HEADER_BUTTON, mMotionEvent); } } else if (id == R.id.minimize_window) { - mDesktopTasksController.minimizeTask(decoration.mTaskInfo); + mDesktopTasksController.minimizeTask( + decoration.mTaskInfo, MinimizeReason.MINIMIZE_BUTTON); } } diff --git a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/desktopmode/DesktopModeKeyGestureHandlerTest.kt b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/desktopmode/DesktopModeKeyGestureHandlerTest.kt index 413e7bc5d1d6..016e04039b12 100644 --- a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/desktopmode/DesktopModeKeyGestureHandlerTest.kt +++ b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/desktopmode/DesktopModeKeyGestureHandlerTest.kt @@ -46,6 +46,7 @@ import com.android.wm.shell.ShellTestCase import com.android.wm.shell.TestShellExecutor import com.android.wm.shell.common.DisplayController import com.android.wm.shell.common.DisplayLayout +import com.android.wm.shell.desktopmode.DesktopModeEventLogger.Companion.MinimizeReason import com.android.wm.shell.desktopmode.DesktopTestHelpers.createFreeformTask import com.android.wm.shell.desktopmode.common.ToggleTaskSizeInteraction import com.android.wm.shell.shared.desktopmode.DesktopModeStatus @@ -294,7 +295,7 @@ class DesktopModeKeyGestureHandlerTest : ShellTestCase() { testExecutor.flushAll() assertThat(result).isTrue() - verify(desktopTasksController).minimizeTask(task) + verify(desktopTasksController).minimizeTask(task, MinimizeReason.KEY_GESTURE) } private fun setUpFreeformTask( diff --git a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/desktopmode/DesktopModeLoggerTransitionObserverTest.kt b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/desktopmode/DesktopModeLoggerTransitionObserverTest.kt index 4317143aebfe..a9ebcef9bd98 100644 --- a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/desktopmode/DesktopModeLoggerTransitionObserverTest.kt +++ b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/desktopmode/DesktopModeLoggerTransitionObserverTest.kt @@ -42,6 +42,7 @@ import android.window.WindowContainerToken import androidx.test.filters.SmallTest import com.android.dx.mockito.inline.extended.ExtendedMockito import com.android.modules.utils.testing.ExtendedMockitoRule +import com.android.wm.shell.ShellTaskOrganizer import com.android.wm.shell.ShellTestCase import com.android.wm.shell.common.ShellExecutor import com.android.wm.shell.desktopmode.DesktopModeEventLogger.Companion.EnterReason @@ -62,6 +63,7 @@ import com.android.wm.shell.sysui.ShellInit import com.android.wm.shell.transition.TransitionInfoBuilder import com.android.wm.shell.transition.Transitions import com.android.wm.shell.transition.Transitions.TRANSIT_MINIMIZE +import java.util.Optional import kotlin.test.assertFalse import kotlin.test.assertTrue import org.junit.Before @@ -69,6 +71,7 @@ import org.junit.Rule import org.junit.Test import org.junit.runner.RunWith import org.mockito.ArgumentCaptor +import org.mockito.Mockito.`when` import org.mockito.kotlin.any import org.mockito.kotlin.eq import org.mockito.kotlin.mock @@ -102,6 +105,8 @@ class DesktopModeLoggerTransitionObserverTest : ShellTestCase() { private val mockShellInit = mock<ShellInit>() private val transitions = mock<Transitions>() private val context = mock<Context>() + private val shellTaskOrganizer = mock<ShellTaskOrganizer>() + private val desktopTasksLimiter = mock<DesktopTasksLimiter>() private lateinit var transitionObserver: DesktopModeLoggerTransitionObserver private lateinit var shellInit: ShellInit @@ -119,6 +124,8 @@ class DesktopModeLoggerTransitionObserverTest : ShellTestCase() { mockShellInit, transitions, desktopModeEventLogger, + Optional.of(desktopTasksLimiter), + shellTaskOrganizer, ) val initRunnableCaptor = ArgumentCaptor.forClass(Runnable::class.java) verify(mockShellInit) @@ -755,6 +762,39 @@ class DesktopModeLoggerTransitionObserverTest : ShellTestCase() { verify(desktopModeEventLogger, never()).logSessionExit(any()) } + @Test + fun onTransitionReady_taskIsBeingMinimized_logsTaskMinimized() { + transitionObserver.isSessionActive = true + transitionObserver.addTaskInfosToCachedMap(createTaskInfo(WINDOWING_MODE_FREEFORM, id = 1)) + val taskInfo2 = createTaskInfo(WINDOWING_MODE_FREEFORM, id = 2) + transitionObserver.addTaskInfosToCachedMap(taskInfo2) + val transitionInfo = + TransitionInfoBuilder(TRANSIT_TO_BACK, 0) + .addChange(createChange(TRANSIT_TO_BACK, taskInfo2)) + .build() + `when`(desktopTasksLimiter.getMinimizingTask(any())) + .thenReturn( + DesktopTasksLimiter.TaskDetails( + taskInfo2.displayId, + taskInfo2.taskId, + minimizeReason = MinimizeReason.TASK_LIMIT, + ) + ) + + callOnTransitionReady(transitionInfo) + + verify(desktopModeEventLogger, times(1)) + .logTaskRemoved( + eq( + DEFAULT_TASK_UPDATE.copy( + instanceId = 2, + visibleTaskCount = 1, + minimizeReason = MinimizeReason.TASK_LIMIT, + ) + ) + ) + } + /** Simulate calling the onTransitionReady() method */ private fun callOnTransitionReady(transitionInfo: TransitionInfo) { val transition = mock<IBinder>() 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 d13ff79b9518..6c4f043a4f39 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 @@ -102,6 +102,7 @@ import com.android.wm.shell.common.ShellExecutor import com.android.wm.shell.common.SyncTransactionQueue import com.android.wm.shell.desktopmode.DesktopImmersiveController.ExitResult import com.android.wm.shell.desktopmode.DesktopModeEventLogger.Companion.InputMethod +import com.android.wm.shell.desktopmode.DesktopModeEventLogger.Companion.MinimizeReason import com.android.wm.shell.desktopmode.DesktopModeEventLogger.Companion.ResizeTrigger import com.android.wm.shell.desktopmode.DesktopTasksController.DesktopModeEntryExitTransitionListener import com.android.wm.shell.desktopmode.DesktopTasksController.SnapPosition @@ -2162,7 +2163,7 @@ class DesktopTasksControllerTest : ShellTestCase() { whenever(transitions.dispatchRequest(any(), any(), anyOrNull())) .thenReturn(android.util.Pair(handler, WindowContainerTransaction())) - controller.minimizeTask(pipTask) + controller.minimizeTask(pipTask, MinimizeReason.MINIMIZE_BUTTON) verifyExitDesktopWCTNotExecuted() taskRepository.setTaskInPip(DEFAULT_DISPLAY, pipTask.taskId, enterPip = false) @@ -2182,7 +2183,7 @@ class DesktopTasksControllerTest : ShellTestCase() { whenever(freeformTaskTransitionStarter.startMinimizedModeTransition(any())) .thenReturn(transition) - controller.minimizeTask(task) + controller.minimizeTask(task, MinimizeReason.MINIMIZE_BUTTON) val captor = ArgumentCaptor.forClass(WindowContainerTransaction::class.java) verify(freeformTaskTransitionStarter).startMinimizedModeTransition(captor.capture()) @@ -2198,7 +2199,7 @@ class DesktopTasksControllerTest : ShellTestCase() { whenever(transitions.dispatchRequest(any(), any(), anyOrNull())) .thenReturn(android.util.Pair(handler, WindowContainerTransaction())) - controller.minimizeTask(task) + controller.minimizeTask(task, MinimizeReason.MINIMIZE_BUTTON) verify(freeformTaskTransitionStarter).startPipTransition(any()) verify(freeformTaskTransitionStarter, never()).startMinimizedModeTransition(any()) @@ -2210,7 +2211,7 @@ class DesktopTasksControllerTest : ShellTestCase() { whenever(freeformTaskTransitionStarter.startMinimizedModeTransition(any())) .thenReturn(Binder()) - controller.minimizeTask(task) + controller.minimizeTask(task, MinimizeReason.MINIMIZE_BUTTON) verify(freeformTaskTransitionStarter).startMinimizedModeTransition(any()) verify(freeformTaskTransitionStarter, never()).startPipTransition(any()) @@ -2223,7 +2224,7 @@ class DesktopTasksControllerTest : ShellTestCase() { whenever(transitions.dispatchRequest(any(), any(), anyOrNull())) .thenReturn(android.util.Pair(handler, WindowContainerTransaction())) - controller.minimizeTask(task) + controller.minimizeTask(task, MinimizeReason.MINIMIZE_BUTTON) val captor = ArgumentCaptor.forClass(WindowContainerTransaction::class.java) verify(freeformTaskTransitionStarter).startPipTransition(captor.capture()) @@ -2239,7 +2240,7 @@ class DesktopTasksControllerTest : ShellTestCase() { whenever(freeformTaskTransitionStarter.startMinimizedModeTransition(any())) .thenReturn(transition) - controller.minimizeTask(task) + controller.minimizeTask(task, MinimizeReason.MINIMIZE_BUTTON) val captor = ArgumentCaptor.forClass(WindowContainerTransaction::class.java) verify(freeformTaskTransitionStarter).startMinimizedModeTransition(captor.capture()) @@ -2255,7 +2256,7 @@ class DesktopTasksControllerTest : ShellTestCase() { .thenReturn(transition) // The only active task is being minimized. - controller.minimizeTask(task) + controller.minimizeTask(task, MinimizeReason.MINIMIZE_BUTTON) val captor = ArgumentCaptor.forClass(WindowContainerTransaction::class.java) verify(freeformTaskTransitionStarter).startMinimizedModeTransition(captor.capture()) @@ -2272,7 +2273,7 @@ class DesktopTasksControllerTest : ShellTestCase() { taskRepository.minimizeTask(DEFAULT_DISPLAY, task.taskId) // The only active task is already minimized. - controller.minimizeTask(task) + controller.minimizeTask(task, MinimizeReason.MINIMIZE_BUTTON) val captor = ArgumentCaptor.forClass(WindowContainerTransaction::class.java) verify(freeformTaskTransitionStarter).startMinimizedModeTransition(captor.capture()) @@ -2289,7 +2290,7 @@ class DesktopTasksControllerTest : ShellTestCase() { whenever(freeformTaskTransitionStarter.startMinimizedModeTransition(any())) .thenReturn(transition) - controller.minimizeTask(task1) + controller.minimizeTask(task1, MinimizeReason.MINIMIZE_BUTTON) val captor = ArgumentCaptor.forClass(WindowContainerTransaction::class.java) verify(freeformTaskTransitionStarter).startMinimizedModeTransition(captor.capture()) @@ -2309,7 +2310,7 @@ class DesktopTasksControllerTest : ShellTestCase() { taskRepository.minimizeTask(DEFAULT_DISPLAY, task2.taskId) // task1 is the only visible task as task2 is minimized. - controller.minimizeTask(task1) + controller.minimizeTask(task1, MinimizeReason.MINIMIZE_BUTTON) // Adds remove wallpaper operation val captor = ArgumentCaptor.forClass(WindowContainerTransaction::class.java) verify(freeformTaskTransitionStarter).startMinimizedModeTransition(captor.capture()) @@ -2324,7 +2325,7 @@ class DesktopTasksControllerTest : ShellTestCase() { whenever(freeformTaskTransitionStarter.startMinimizedModeTransition(any())) .thenReturn(transition) - controller.minimizeTask(task) + controller.minimizeTask(task, MinimizeReason.MINIMIZE_BUTTON) verify(mMockDesktopImmersiveController).exitImmersiveIfApplicable(any(), eq(task), any()) } @@ -2341,7 +2342,7 @@ class DesktopTasksControllerTest : ShellTestCase() { ExitResult.Exit(exitingTask = task.taskId, runOnTransitionStart = runOnTransit) ) - controller.minimizeTask(task) + controller.minimizeTask(task, MinimizeReason.MINIMIZE_BUTTON) assertThat(runOnTransit.invocations).isEqualTo(1) assertThat(runOnTransit.lastInvoked).isEqualTo(transition) @@ -3285,7 +3286,7 @@ class DesktopTasksControllerTest : ShellTestCase() { whenever(transitions.dispatchRequest(any(), any(), anyOrNull())) .thenReturn(android.util.Pair(handler, WindowContainerTransaction())) - controller.minimizeTask(pipTask) + controller.minimizeTask(pipTask, MinimizeReason.MINIMIZE_BUTTON) verifyExitDesktopWCTNotExecuted() freeformTask.isFocused = true diff --git a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/desktopmode/DesktopTasksLimiterTest.kt b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/desktopmode/DesktopTasksLimiterTest.kt index c8214b3838e2..acfe1e9fd5a2 100644 --- a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/desktopmode/DesktopTasksLimiterTest.kt +++ b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/desktopmode/DesktopTasksLimiterTest.kt @@ -20,6 +20,7 @@ import android.app.ActivityManager.RunningTaskInfo import android.graphics.Rect import android.os.Binder import android.os.Handler +import android.os.IBinder import android.os.UserManager import android.platform.test.annotations.DisableFlags import android.platform.test.annotations.EnableFlags @@ -43,6 +44,7 @@ import com.android.window.flags.Flags.FLAG_ENABLE_DESKTOP_WINDOWING_BACK_NAVIGAT import com.android.wm.shell.ShellTaskOrganizer import com.android.wm.shell.ShellTestCase import com.android.wm.shell.common.ShellExecutor +import com.android.wm.shell.desktopmode.DesktopModeEventLogger.Companion.MinimizeReason import com.android.wm.shell.desktopmode.DesktopTestHelpers.createFreeformTask import com.android.wm.shell.desktopmode.persistence.DesktopPersistentRepository import com.android.wm.shell.desktopmode.persistence.DesktopRepositoryInitializer @@ -180,7 +182,7 @@ class DesktopTasksLimiterTest : ShellTestCase() { val task = setUpFreeformTask() markTaskHidden(task) - desktopTasksLimiter.addPendingMinimizeChange(Binder(), displayId = 1, taskId = task.taskId) + addPendingMinimizeChange(Binder(), displayId = 1, taskId = task.taskId) assertThat(desktopTaskRepo.isMinimizedTask(taskId = task.taskId)).isFalse() } @@ -208,11 +210,7 @@ class DesktopTasksLimiterTest : ShellTestCase() { val taskTransition = Binder() val task = setUpFreeformTask() markTaskHidden(task) - desktopTasksLimiter.addPendingMinimizeChange( - pendingTransition, - displayId = DEFAULT_DISPLAY, - taskId = task.taskId, - ) + addPendingMinimizeChange(pendingTransition, taskId = task.taskId) desktopTasksLimiter .getTransitionObserver() @@ -231,11 +229,7 @@ class DesktopTasksLimiterTest : ShellTestCase() { val transition = Binder() val task = setUpFreeformTask() markTaskVisible(task) - desktopTasksLimiter.addPendingMinimizeChange( - transition, - displayId = DEFAULT_DISPLAY, - taskId = task.taskId, - ) + addPendingMinimizeChange(transition, taskId = task.taskId) desktopTasksLimiter .getTransitionObserver() @@ -254,11 +248,7 @@ class DesktopTasksLimiterTest : ShellTestCase() { val transition = Binder() val task = setUpFreeformTask() markTaskHidden(task) - desktopTasksLimiter.addPendingMinimizeChange( - transition, - displayId = DEFAULT_DISPLAY, - taskId = task.taskId, - ) + addPendingMinimizeChange(transition, taskId = task.taskId) desktopTasksLimiter .getTransitionObserver() @@ -276,11 +266,7 @@ class DesktopTasksLimiterTest : ShellTestCase() { fun onTransitionReady_pendingTransition_changeTaskToBack_taskIsMinimized() { val transition = Binder() val task = setUpFreeformTask() - desktopTasksLimiter.addPendingMinimizeChange( - transition, - displayId = DEFAULT_DISPLAY, - taskId = task.taskId, - ) + addPendingMinimizeChange(transition, taskId = task.taskId) desktopTasksLimiter .getTransitionObserver() @@ -299,11 +285,7 @@ class DesktopTasksLimiterTest : ShellTestCase() { val bounds = Rect(0, 0, 200, 200) val transition = Binder() val task = setUpFreeformTask() - desktopTasksLimiter.addPendingMinimizeChange( - transition, - displayId = DEFAULT_DISPLAY, - taskId = task.taskId, - ) + addPendingMinimizeChange(transition, taskId = task.taskId) val change = TransitionInfo.Change(task.token, mock(SurfaceControl::class.java)).apply { @@ -330,11 +312,7 @@ class DesktopTasksLimiterTest : ShellTestCase() { val mergedTransition = Binder() val newTransition = Binder() val task = setUpFreeformTask() - desktopTasksLimiter.addPendingMinimizeChange( - mergedTransition, - displayId = DEFAULT_DISPLAY, - taskId = task.taskId, - ) + addPendingMinimizeChange(mergedTransition, taskId = task.taskId) desktopTasksLimiter .getTransitionObserver() .onTransitionMerged(mergedTransition, newTransition) @@ -541,11 +519,7 @@ class DesktopTasksLimiterTest : ShellTestCase() { (1..<MAX_TASK_LIMIT).forEach { _ -> setUpFreeformTask() } val transition = Binder() val task = setUpFreeformTask() - desktopTasksLimiter.addPendingMinimizeChange( - transition, - displayId = DEFAULT_DISPLAY, - taskId = task.taskId, - ) + addPendingMinimizeChange(transition, taskId = task.taskId) desktopTasksLimiter .getTransitionObserver() @@ -573,11 +547,7 @@ class DesktopTasksLimiterTest : ShellTestCase() { (1..<MAX_TASK_LIMIT).forEach { _ -> setUpFreeformTask() } val transition = Binder() val task = setUpFreeformTask() - desktopTasksLimiter.addPendingMinimizeChange( - transition, - displayId = DEFAULT_DISPLAY, - taskId = task.taskId, - ) + addPendingMinimizeChange(transition, taskId = task.taskId) desktopTasksLimiter .getTransitionObserver() @@ -606,11 +576,7 @@ class DesktopTasksLimiterTest : ShellTestCase() { val mergedTransition = Binder() val newTransition = Binder() val task = setUpFreeformTask() - desktopTasksLimiter.addPendingMinimizeChange( - mergedTransition, - displayId = DEFAULT_DISPLAY, - taskId = task.taskId, - ) + addPendingMinimizeChange(mergedTransition, taskId = task.taskId) desktopTasksLimiter .getTransitionObserver() @@ -633,6 +599,60 @@ class DesktopTasksLimiterTest : ShellTestCase() { verify(interactionJankMonitor).end(eq(CUJ_DESKTOP_MODE_MINIMIZE_WINDOW)) } + @Test + fun getMinimizingTask_noPendingTransition_returnsNull() { + val transition = Binder() + + assertThat(desktopTasksLimiter.getMinimizingTask(transition)).isNull() + } + + @Test + fun getMinimizingTask_pendingTaskTransition_returnsTask() { + val transition = Binder() + val task = setUpFreeformTask() + addPendingMinimizeChange( + transition, + taskId = task.taskId, + minimizeReason = MinimizeReason.TASK_LIMIT, + ) + + assertThat(desktopTasksLimiter.getMinimizingTask(transition)) + .isEqualTo( + createTaskDetails(taskId = task.taskId, minimizeReason = MinimizeReason.TASK_LIMIT) + ) + } + + @Test + fun getMinimizingTask_activeTaskTransition_returnsTask() { + val transition = Binder() + val task = setUpFreeformTask() + addPendingMinimizeChange( + transition, + taskId = task.taskId, + minimizeReason = MinimizeReason.TASK_LIMIT, + ) + val transitionInfo = + TransitionInfoBuilder(TRANSIT_OPEN).addChange(TRANSIT_TO_BACK, task).build() + + desktopTasksLimiter + .getTransitionObserver() + .onTransitionReady( + transition, + transitionInfo, + /* startTransaction= */ StubTransaction(), + /* finishTransaction= */ StubTransaction(), + ) + + assertThat(desktopTasksLimiter.getMinimizingTask(transition)) + .isEqualTo( + createTaskDetails( + taskId = task.taskId, + transitionInfo = transitionInfo, + minimizeReason = MinimizeReason.TASK_LIMIT, + ) + ) + } + private fun setUpFreeformTask(displayId: Int = DEFAULT_DISPLAY): RunningTaskInfo { val task = createFreeformTask(displayId) `when`(shellTaskOrganizer.getRunningTaskInfo(task.taskId)).thenReturn(task) @@ -640,6 +660,20 @@ class DesktopTasksLimiterTest : ShellTestCase() { return task } + private fun createTaskDetails( + displayId: Int = DEFAULT_DISPLAY, + taskId: Int, + transitionInfo: TransitionInfo? = null, + minimizeReason: MinimizeReason? = null, + ) = DesktopTasksLimiter.TaskDetails(displayId, taskId, transitionInfo, minimizeReason) + + fun addPendingMinimizeChange( + transition: IBinder, + displayId: Int = DEFAULT_DISPLAY, + taskId: Int, + minimizeReason: MinimizeReason = MinimizeReason.TASK_LIMIT, + ) = desktopTasksLimiter.addPendingMinimizeChange(transition, displayId, taskId, minimizeReason) + private fun markTaskVisible(task: RunningTaskInfo) { desktopTaskRepo.updateTask(task.displayId, task.taskId, isVisible = true) } diff --git a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/windowdecor/DesktopModeWindowDecorViewModelTests.kt b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/windowdecor/DesktopModeWindowDecorViewModelTests.kt index 79e9b9c8cd77..b4791642663a 100644 --- a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/windowdecor/DesktopModeWindowDecorViewModelTests.kt +++ b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/windowdecor/DesktopModeWindowDecorViewModelTests.kt @@ -61,6 +61,7 @@ import com.android.window.flags.Flags import com.android.wm.shell.R import com.android.wm.shell.desktopmode.DesktopImmersiveController import com.android.wm.shell.desktopmode.DesktopModeEventLogger.Companion.InputMethod +import com.android.wm.shell.desktopmode.DesktopModeEventLogger.Companion.MinimizeReason import com.android.wm.shell.desktopmode.DesktopModeEventLogger.Companion.ResizeTrigger import com.android.wm.shell.desktopmode.DesktopTasksController.SnapPosition import com.android.wm.shell.desktopmode.common.ToggleTaskSizeInteraction @@ -272,7 +273,7 @@ class DesktopModeWindowDecorViewModelTests : DesktopModeWindowDecorViewModelTest onClickListenerCaptor.value.onClick(view) - verify(mockDesktopTasksController).minimizeTask(decor.mTaskInfo) + verify(mockDesktopTasksController).minimizeTask(decor.mTaskInfo, MinimizeReason.MINIMIZE_BUTTON) } @Test |