Donate to e Foundation | Murena handsets with /e/OS | Own a part of Murena! Learn more

Commit 63efc310 authored by Maryam Dehaini's avatar Maryam Dehaini
Browse files

Restore bounds when returning from maximized window bounds

Saves the tasks bounds before toggling it to stable bounds when window
is maximized. When restoring from maximized state, restores to the saved
bounds.

Bug: 323387092
Test: Manual testing
Change-Id: I237a3ffca4b80538eff9ced8d2abedce3e1cccb1
parent f1ec23e3
Loading
Loading
Loading
Loading
+18 −0
Original line number Diff line number Diff line
@@ -16,6 +16,7 @@

package com.android.wm.shell.desktopmode

import android.graphics.Rect
import android.graphics.Region
import android.util.ArrayMap
import android.util.ArraySet
@@ -55,6 +56,8 @@ class DesktopModeTaskRepository {
    private val visibleTasksListeners = ArrayMap<VisibleTasksListener, Executor>()
    // Track corner/caption regions of desktop tasks, used to determine gesture exclusion
    private val desktopExclusionRegions = SparseArray<Region>()
    // Track last bounds of task before toggled to stable bounds
    private val boundsBeforeMaximizeByTaskId = SparseArray<Rect>()
    private var desktopGestureExclusionListener: Consumer<Region>? = null
    private var desktopGestureExclusionExecutor: Executor? = null

@@ -307,6 +310,7 @@ class DesktopModeTaskRepository {
            taskId
        )
        freeformTasksInZOrder.remove(taskId)
        boundsBeforeMaximizeByTaskId.remove(taskId)
        KtProtoLog.d(
            WM_SHELL_DESKTOP_MODE,
            "DesktopTaskRepo: remaining freeform tasks: " + freeformTasksInZOrder.toDumpString()
@@ -357,6 +361,20 @@ class DesktopModeTaskRepository {
        }
    }

    /**
     * Removes and returns the bounds saved before maximizing the given task.
     */
    fun removeBoundsBeforeMaximize(taskId: Int): Rect? {
        return boundsBeforeMaximizeByTaskId.removeReturnOld(taskId)
    }

    /**
     * Saves the bounds of the given task before maximizing.
     */
    fun saveBoundsBeforeMaximize(taskId: Int, bounds: Rect) {
        boundsBeforeMaximizeByTaskId.set(taskId, Rect(bounds))
    }

    /**
     * Check if display with id [displayId] has desktop tasks stashed
     */
+18 −6
Original line number Diff line number Diff line
@@ -120,7 +120,6 @@ class DesktopTasksController(
    private var visualIndicator: DesktopModeVisualIndicator? = null
    private val desktopModeShellCommandHandler: DesktopModeShellCommandHandler =
        DesktopModeShellCommandHandler(this)

    private val mOnAnimationFinishedCallback = Consumer<SurfaceControl.Transaction> {
        t: SurfaceControl.Transaction ->
        visualIndicator?.releaseVisualIndicator(t)
@@ -570,7 +569,10 @@ class DesktopTasksController(
        }
    }

    /** Quick-resizes a desktop task, toggling between the stable bounds and the default bounds. */
    /**
     * Quick-resizes a desktop task, toggling between the stable bounds and the last saved bounds
     * if available or the default bounds otherwise.
     */
    fun toggleDesktopTaskSize(taskInfo: RunningTaskInfo) {
        val displayLayout = displayController.getDisplayLayout(taskInfo.displayId) ?: return

@@ -578,11 +580,21 @@ class DesktopTasksController(
        displayLayout.getStableBounds(stableBounds)
        val destinationBounds = Rect()
        if (taskInfo.configuration.windowConfiguration.bounds == stableBounds) {
            // The desktop task is currently occupying the whole stable bounds, toggle to the
            // default bounds.
            // The desktop task is currently occupying the whole stable bounds. If the bounds
            // before the task was toggled to stable bounds were saved, toggle the task to those
            // bounds. Otherwise, toggle to the default bounds.
            val taskBoundsBeforeMaximize =
                    desktopModeTaskRepository.removeBoundsBeforeMaximize(taskInfo.taskId)
            if (taskBoundsBeforeMaximize != null) {
                destinationBounds.set(taskBoundsBeforeMaximize)
            } else {
                getDefaultDesktopTaskBounds(displayLayout, destinationBounds)
            }
        } else {
            // Toggle to the stable bounds.
            // Save current bounds so that task can be restored back to original bounds if necessary
            // and toggle to the stable bounds.
            val taskBounds = taskInfo.configuration.windowConfiguration.bounds
            desktopModeTaskRepository.saveBoundsBeforeMaximize(taskInfo.taskId, taskBounds)
            destinationBounds.set(stableBounds)
        }

+26 −0
Original line number Diff line number Diff line
@@ -16,6 +16,7 @@

package com.android.wm.shell.desktopmode

import android.graphics.Rect
import android.testing.AndroidTestingRunner
import android.view.Display.DEFAULT_DISPLAY
import android.view.Display.INVALID_DISPLAY
@@ -406,6 +407,31 @@ class DesktopModeTaskRepositoryTest : ShellTestCase() {
        assertThat(listener.stashedOnSecondaryDisplay).isTrue()
    }

    @Test
    fun removeFreeformTask_removesTaskBoundsBeforeMaximize() {
        val taskId = 1
        repo.saveBoundsBeforeMaximize(taskId, Rect(0, 0, 200, 200))
        repo.removeFreeformTask(taskId)
        assertThat(repo.removeBoundsBeforeMaximize(taskId)).isNull()
    }

    @Test
    fun saveBoundsBeforeMaximize_boundsSavedByTaskId() {
        val taskId = 1
        val bounds = Rect(0, 0, 200, 200)
        repo.saveBoundsBeforeMaximize(taskId, bounds)
        assertThat(repo.removeBoundsBeforeMaximize(taskId)).isEqualTo(bounds)
    }

    @Test
    fun removeBoundsBeforeMaximize_returnsNullAfterBoundsRemoved() {
        val taskId = 1
        val bounds = Rect(0, 0, 200, 200)
        repo.saveBoundsBeforeMaximize(taskId, bounds)
        repo.removeBoundsBeforeMaximize(taskId)
        assertThat(repo.removeBoundsBeforeMaximize(taskId)).isNull()
    }

    class TestListener : DesktopModeTaskRepository.ActiveTasksListener {
        var activeChangesOnDefaultDisplay = 0
        var activeChangesOnSecondaryDisplay = 0
+88 −10
Original line number Diff line number Diff line
@@ -95,9 +95,10 @@ import org.mockito.Mockito.anyInt
import org.mockito.Mockito.clearInvocations
import org.mockito.Mockito.mock
import org.mockito.Mockito.verify
import org.mockito.kotlin.times
import org.mockito.Mockito.`when` as whenever
import org.mockito.kotlin.atLeastOnce
import org.mockito.kotlin.capture
import org.mockito.quality.Strictness
import org.mockito.Mockito.`when` as whenever

/**
 * Test class for {@link DesktopTasksController}
@@ -116,13 +117,14 @@ class DesktopTasksControllerTest : ShellTestCase() {
    @Mock lateinit var shellCommandHandler: ShellCommandHandler
    @Mock lateinit var shellController: ShellController
    @Mock lateinit var displayController: DisplayController
    @Mock lateinit var displayLayout: DisplayLayout
    @Mock lateinit var shellTaskOrganizer: ShellTaskOrganizer
    @Mock lateinit var syncQueue: SyncTransactionQueue
    @Mock lateinit var rootTaskDisplayAreaOrganizer: RootTaskDisplayAreaOrganizer
    @Mock lateinit var transitions: Transitions
    @Mock lateinit var exitDesktopTransitionHandler: ExitDesktopTaskTransitionHandler
    @Mock lateinit var enterDesktopTransitionHandler: EnterDesktopTaskTransitionHandler
    @Mock lateinit var mToggleResizeDesktopTaskTransitionHandler:
    @Mock lateinit var toggleResizeDesktopTaskTransitionHandler:
            ToggleResizeDesktopTaskTransitionHandler
    @Mock lateinit var dragToDesktopTransitionHandler: DragToDesktopTransitionHandler
    @Mock lateinit var launchAdjacentController: LaunchAdjacentController
@@ -154,6 +156,10 @@ class DesktopTasksControllerTest : ShellTestCase() {

        whenever(shellTaskOrganizer.getRunningTasks(anyInt())).thenAnswer { runningTasks }
        whenever(transitions.startTransition(anyInt(), any(), isNull())).thenAnswer { Binder() }
        whenever(displayController.getDisplayLayout(anyInt())).thenReturn(displayLayout)
        whenever(displayLayout.getStableBounds(any())).thenAnswer { i ->
                (i.arguments.first() as Rect).set(STABLE_BOUNDS)
            }

        controller = createController()
        controller.setSplitScreenController(splitScreenController)
@@ -179,7 +185,7 @@ class DesktopTasksControllerTest : ShellTestCase() {
            transitions,
            enterDesktopTransitionHandler,
            exitDesktopTransitionHandler,
            mToggleResizeDesktopTaskTransitionHandler,
            toggleResizeDesktopTaskTransitionHandler,
            dragToDesktopTransitionHandler,
            desktopModeTaskRepository,
            desktopModeLoggerTransitionObserver,
@@ -936,8 +942,67 @@ class DesktopTasksControllerTest : ShellTestCase() {
        )
    }

    private fun setUpFreeformTask(displayId: Int = DEFAULT_DISPLAY): RunningTaskInfo {
        val task = createFreeformTask(displayId)
    @Test
    fun toggleBounds_togglesToStableBounds() {
        val bounds = Rect(0, 0, 100, 100)
        val task = setUpFreeformTask(DEFAULT_DISPLAY, bounds)

        controller.toggleDesktopTaskSize(task)
        // Assert bounds set to stable bounds
        val wct = getLatestToggleResizeDesktopTaskWct()
        assertThat(wct.changes[task.token.asBinder()]?.configuration?.windowConfiguration?.bounds)
                .isEqualTo(STABLE_BOUNDS)
    }

    @Test
    fun toggleBounds_lastBoundsBeforeMaximizeSaved() {
        val bounds = Rect(0, 0, 100, 100)
        val task = setUpFreeformTask(DEFAULT_DISPLAY, bounds)

        controller.toggleDesktopTaskSize(task)
        assertThat(desktopModeTaskRepository.removeBoundsBeforeMaximize(task.taskId))
                .isEqualTo(bounds)
    }

    @Test
    fun toggleBounds_togglesFromStableBoundsToLastBoundsBeforeMaximize() {
        val boundsBeforeMaximize = Rect(0, 0, 100, 100)
        val task = setUpFreeformTask(DEFAULT_DISPLAY, boundsBeforeMaximize)

        // Maximize
        controller.toggleDesktopTaskSize(task)
        task.configuration.windowConfiguration.bounds.set(STABLE_BOUNDS)

        // Restore
        controller.toggleDesktopTaskSize(task)

        // Assert bounds set to last bounds before maximize
        val wct = getLatestToggleResizeDesktopTaskWct()
        assertThat(wct.changes[task.token.asBinder()]?.configuration?.windowConfiguration?.bounds)
                .isEqualTo(boundsBeforeMaximize)
    }

    @Test
    fun toggleBounds_removesLastBoundsBeforeMaximizeAfterRestoringBounds() {
        val boundsBeforeMaximize = Rect(0, 0, 100, 100)
        val task = setUpFreeformTask(DEFAULT_DISPLAY, boundsBeforeMaximize)

        // Maximize
        controller.toggleDesktopTaskSize(task)
        task.configuration.windowConfiguration.bounds.set(STABLE_BOUNDS)

        // Restore
        controller.toggleDesktopTaskSize(task)

        // Assert last bounds before maximize removed after use
        assertThat(desktopModeTaskRepository.removeBoundsBeforeMaximize(task.taskId)).isNull()
    }

    private fun setUpFreeformTask(
            displayId: Int = DEFAULT_DISPLAY,
            bounds: Rect? = null
    ): RunningTaskInfo {
        val task = createFreeformTask(displayId, bounds)
        whenever(shellTaskOrganizer.getRunningTaskInfo(task.taskId)).thenReturn(task)
        desktopModeTaskRepository.addActiveTask(displayId, task.taskId)
        desktopModeTaskRepository.addOrMoveFreeformTaskToTop(task.taskId)
@@ -1004,6 +1069,18 @@ class DesktopTasksControllerTest : ShellTestCase() {
        return arg.value
    }

    private fun getLatestToggleResizeDesktopTaskWct(): WindowContainerTransaction {
        val arg: ArgumentCaptor<WindowContainerTransaction> =
                ArgumentCaptor.forClass(WindowContainerTransaction::class.java)
        if (ENABLE_SHELL_TRANSITIONS) {
            verify(toggleResizeDesktopTaskTransitionHandler, atLeastOnce())
                    .startTransition(capture(arg))
        } else {
            verify(shellTaskOrganizer).applyTransaction(capture(arg))
        }
        return arg.value
    }

    private fun getLatestMoveToDesktopWct(): WindowContainerTransaction {
        val arg = ArgumentCaptor.forClass(WindowContainerTransaction::class.java)
        if (ENABLE_SHELL_TRANSITIONS) {
@@ -1042,6 +1119,7 @@ class DesktopTasksControllerTest : ShellTestCase() {

    companion object {
        const val SECOND_DISPLAY = 2
        private val STABLE_BOUNDS = Rect(0, 0, 1000, 1000)
    }
}

+6 −1
Original line number Diff line number Diff line
@@ -22,6 +22,7 @@ import android.app.WindowConfiguration.ACTIVITY_TYPE_STANDARD
import android.app.WindowConfiguration.WINDOWING_MODE_FREEFORM
import android.app.WindowConfiguration.WINDOWING_MODE_FULLSCREEN
import android.app.WindowConfiguration.WINDOWING_MODE_MULTI_WINDOW
import android.graphics.Rect
import android.view.Display.DEFAULT_DISPLAY
import com.android.wm.shell.MockToken
import com.android.wm.shell.TestRunningTaskInfoBuilder
@@ -31,13 +32,17 @@ class DesktopTestHelpers {
        /** Create a task that has windowing mode set to [WINDOWING_MODE_FREEFORM] */
        @JvmStatic
        @JvmOverloads
        fun createFreeformTask(displayId: Int = DEFAULT_DISPLAY): RunningTaskInfo {
        fun createFreeformTask(
                displayId: Int = DEFAULT_DISPLAY,
                bounds: Rect? = null
        ): RunningTaskInfo {
            return TestRunningTaskInfoBuilder()
                    .setDisplayId(displayId)
                    .setToken(MockToken().token())
                    .setActivityType(ACTIVITY_TYPE_STANDARD)
                    .setWindowingMode(WINDOWING_MODE_FREEFORM)
                    .setLastActiveTime(100)
                    .apply { bounds?.let { setBounds(it) }}
                    .build()
        }