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

Commit 12c14878 authored by Orhan Uysal's avatar Orhan Uysal Committed by Android (Google) Code Review
Browse files

Merge "Remove wallpaper to not end up in empty desktop" into main

parents 787bc2c6 e71724cb
Loading
Loading
Loading
Loading
+41 −14
Original line number Diff line number Diff line
@@ -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 -> {}
                    }
+75 −0
Original line number Diff line number Diff line
@@ -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