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

Commit 455c6651 authored by Ats Jenk's avatar Ats Jenk Committed by Android (Google) Code Review
Browse files

Merge "Move task to fullscreen or freeeform on transition" into tm-qpr-dev

parents e2ccdcbe a1f8da1f
Loading
Loading
Loading
Loading
+91 −8
Original line number Diff line number Diff line
@@ -17,12 +17,20 @@
package com.android.wm.shell.desktopmode

import android.app.ActivityManager
import android.app.WindowConfiguration
import android.app.WindowConfiguration.ACTIVITY_TYPE_HOME
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_UNDEFINED
import android.app.WindowConfiguration.WindowingMode
import android.content.Context
import android.view.WindowManager
import android.os.IBinder
import android.view.SurfaceControl
import android.view.WindowManager.TRANSIT_CHANGE
import android.view.WindowManager.TRANSIT_OPEN
import android.view.WindowManager.TRANSIT_TO_FRONT
import android.window.TransitionInfo
import android.window.TransitionRequestInfo
import android.window.WindowContainerTransaction
import androidx.annotation.BinderThread
import com.android.internal.protolog.common.ProtoLog
@@ -51,7 +59,7 @@ class DesktopTasksController(
    private val transitions: Transitions,
    private val desktopModeTaskRepository: DesktopModeTaskRepository,
    @ShellMainThread private val mainExecutor: ShellExecutor
) : RemoteCallable<DesktopTasksController> {
) : RemoteCallable<DesktopTasksController>, Transitions.TransitionHandler {

    private val desktopMode: DesktopModeImpl

@@ -69,6 +77,7 @@ class DesktopTasksController(
            { createExternalInterface() },
            this
        )
        transitions.addHandler(this)
    }

    /** Show all tasks, that are part of the desktop, on top of launcher */
@@ -81,7 +90,7 @@ class DesktopTasksController(
        // Execute transaction if there are pending operations
        if (!wct.isEmpty) {
            if (Transitions.ENABLE_SHELL_TRANSITIONS) {
                transitions.startTransition(WindowManager.TRANSIT_TO_FRONT, wct, null /* handler */)
                transitions.startTransition(TRANSIT_TO_FRONT, wct, null /* handler */)
            } else {
                shellTaskOrganizer.applyTransaction(wct)
            }
@@ -101,11 +110,11 @@ class DesktopTasksController(
        // Bring other apps to front first
        bringDesktopAppsToFront(wct)

        wct.setWindowingMode(task.getToken(), WindowConfiguration.WINDOWING_MODE_FREEFORM)
        wct.setWindowingMode(task.getToken(), WINDOWING_MODE_FREEFORM)
        wct.reorder(task.getToken(), true /* onTop */)

        if (Transitions.ENABLE_SHELL_TRANSITIONS) {
            transitions.startTransition(WindowManager.TRANSIT_CHANGE, wct, null /* handler */)
            transitions.startTransition(TRANSIT_CHANGE, wct, null /* handler */)
        } else {
            shellTaskOrganizer.applyTransaction(wct)
        }
@@ -121,10 +130,10 @@ class DesktopTasksController(
        ProtoLog.v(WM_SHELL_DESKTOP_MODE, "moveToFullscreen: %d", task.taskId)

        val wct = WindowContainerTransaction()
        wct.setWindowingMode(task.getToken(), WindowConfiguration.WINDOWING_MODE_FULLSCREEN)
        wct.setWindowingMode(task.getToken(), WINDOWING_MODE_FULLSCREEN)
        wct.setBounds(task.getToken(), null)
        if (Transitions.ENABLE_SHELL_TRANSITIONS) {
            transitions.startTransition(WindowManager.TRANSIT_CHANGE, wct, null /* handler */)
            transitions.startTransition(TRANSIT_CHANGE, wct, null /* handler */)
        } else {
            shellTaskOrganizer.applyTransaction(wct)
        }
@@ -181,6 +190,80 @@ class DesktopTasksController(
        return mainExecutor
    }

    override fun startAnimation(
        transition: IBinder,
        info: TransitionInfo,
        startTransaction: SurfaceControl.Transaction,
        finishTransaction: SurfaceControl.Transaction,
        finishCallback: Transitions.TransitionFinishCallback
    ): Boolean {
        // This handler should never be the sole handler, so should not animate anything.
        return false
    }

    override fun handleRequest(
        transition: IBinder,
        request: TransitionRequestInfo
    ): WindowContainerTransaction? {
        // Check if we should skip handling this transition
        val task: ActivityManager.RunningTaskInfo? = request.triggerTask
        val shouldHandleRequest =
            when {
                // Only handle open or to front transitions
                request.type != TRANSIT_OPEN && request.type != TRANSIT_TO_FRONT -> false
                // Only handle when it is a task transition
                task == null -> false
                // Only handle standard type tasks
                task.activityType != ACTIVITY_TYPE_STANDARD -> false
                // Only handle fullscreen or freeform tasks
                task.windowingMode != WINDOWING_MODE_FULLSCREEN &&
                    task.windowingMode != WINDOWING_MODE_FREEFORM -> false
                // Otherwise process it
                else -> true
            }

        if (!shouldHandleRequest) {
            return null
        }

        val activeTasks = desktopModeTaskRepository.getActiveTasks()

        // Check if we should switch a fullscreen task to freeform
        if (task?.windowingMode == WINDOWING_MODE_FULLSCREEN) {
            // If there are any visible desktop tasks, switch the task to freeform
            if (activeTasks.any { desktopModeTaskRepository.isVisibleTask(it) }) {
                ProtoLog.d(
                    WM_SHELL_DESKTOP_MODE,
                    "DesktopTasksController#handleRequest: switch fullscreen task to freeform," +
                        " taskId=%d",
                    task.taskId
                )
                return WindowContainerTransaction().apply {
                    setWindowingMode(task.token, WINDOWING_MODE_FREEFORM)
                }
            }
        }

        // CHeck if we should switch a freeform task to fullscreen
        if (task?.windowingMode == WINDOWING_MODE_FREEFORM) {
            // If no visible desktop tasks, switch this task to freeform as the transition came
            // outside of this controller
            if (activeTasks.none { desktopModeTaskRepository.isVisibleTask(it) }) {
                ProtoLog.d(
                    WM_SHELL_DESKTOP_MODE,
                    "DesktopTasksController#handleRequest: switch freeform task to fullscreen," +
                        " taskId=%d",
                    task.taskId
                )
                return WindowContainerTransaction().apply {
                    setWindowingMode(task.token, WINDOWING_MODE_FULLSCREEN)
                    setBounds(task.token, null)
                }
            }
        }
        return null
    }

    /** Creates a new instance of the external interface to pass to another process. */
    private fun createExternalInterface(): ExternalInterfaceBinder {
        return IDesktopModeImpl(this)
+129 −2
Original line number Diff line number Diff line
@@ -17,10 +17,18 @@
package com.android.wm.shell.desktopmode

import android.app.ActivityManager.RunningTaskInfo
import android.app.WindowConfiguration.ACTIVITY_TYPE_HOME
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.app.WindowConfiguration.WINDOWING_MODE_UNDEFINED
import android.os.Binder
import android.testing.AndroidTestingRunner
import android.view.WindowManager
import android.view.WindowManager.TRANSIT_OPEN
import android.view.WindowManager.TRANSIT_TO_FRONT
import android.window.TransitionRequestInfo
import android.window.WindowContainerTransaction
import android.window.WindowContainerTransaction.HierarchyOp.HIERARCHY_OP_TYPE_REORDER
import androidx.test.filters.SmallTest
@@ -29,6 +37,7 @@ import com.android.dx.mockito.inline.extended.ExtendedMockito.never
import com.android.dx.mockito.inline.extended.StaticMockitoSession
import com.android.wm.shell.ShellTaskOrganizer
import com.android.wm.shell.ShellTestCase
import com.android.wm.shell.TestRunningTaskInfoBuilder
import com.android.wm.shell.TestShellExecutor
import com.android.wm.shell.common.ShellExecutor
import com.android.wm.shell.desktopmode.DesktopTestHelpers.Companion.createFreeformTask
@@ -37,9 +46,11 @@ import com.android.wm.shell.desktopmode.DesktopTestHelpers.Companion.createHomeT
import com.android.wm.shell.sysui.ShellController
import com.android.wm.shell.sysui.ShellInit
import com.android.wm.shell.transition.Transitions
import com.android.wm.shell.transition.Transitions.ENABLE_SHELL_TRANSITIONS
import com.google.common.truth.Truth.assertThat
import com.google.common.truth.Truth.assertWithMessage
import org.junit.After
import org.junit.Assume.assumeTrue
import org.junit.Before
import org.junit.Test
import org.junit.runner.RunWith
@@ -79,6 +90,7 @@ class DesktopTasksControllerTest : ShellTestCase() {
        desktopModeTaskRepository = DesktopModeTaskRepository()

        whenever(shellTaskOrganizer.getRunningTasks(anyInt())).thenAnswer { runningTasks }
        whenever(transitions.startTransition(anyInt(), any(), isNull())).thenAnswer { Binder() }

        controller = createController()

@@ -221,6 +233,114 @@ class DesktopTasksControllerTest : ShellTestCase() {
        assertThat(controller.getTaskWindowingMode(999)).isEqualTo(WINDOWING_MODE_UNDEFINED)
    }

    @Test
    fun handleRequest_fullscreenTask_freeformVisible_returnSwitchToFreeformWCT() {
        assumeTrue(ENABLE_SHELL_TRANSITIONS)

        val freeformTask = setUpFreeformTask()
        markTaskVisible(freeformTask)
        val fullscreenTask = createFullscreenTask()

        val result = controller.handleRequest(Binder(), createTransition(fullscreenTask))
        assertThat(result?.changes?.get(fullscreenTask.token.asBinder())?.windowingMode)
            .isEqualTo(WINDOWING_MODE_FREEFORM)
    }

    @Test
    fun handleRequest_fullscreenTask_freeformNotVisible_returnNull() {
        assumeTrue(ENABLE_SHELL_TRANSITIONS)

        val freeformTask = setUpFreeformTask()
        markTaskHidden(freeformTask)
        val fullscreenTask = createFullscreenTask()
        assertThat(controller.handleRequest(Binder(), createTransition(fullscreenTask))).isNull()
    }

    @Test
    fun handleRequest_fullscreenTask_noOtherTasks_returnNull() {
        assumeTrue(ENABLE_SHELL_TRANSITIONS)

        val fullscreenTask = createFullscreenTask()
        assertThat(controller.handleRequest(Binder(), createTransition(fullscreenTask))).isNull()
    }

    @Test
    fun handleRequest_freeformTask_freeformVisible_returnNull() {
        assumeTrue(ENABLE_SHELL_TRANSITIONS)

        val freeformTask1 = setUpFreeformTask()
        markTaskVisible(freeformTask1)

        val freeformTask2 = createFreeformTask()
        assertThat(controller.handleRequest(Binder(), createTransition(freeformTask2))).isNull()
    }

    @Test
    fun handleRequest_freeformTask_freeformNotVisible_returnSwitchToFullscreenWCT() {
        assumeTrue(ENABLE_SHELL_TRANSITIONS)

        val freeformTask1 = setUpFreeformTask()
        markTaskHidden(freeformTask1)

        val freeformTask2 = createFreeformTask()
        val result =
            controller.handleRequest(
                Binder(),
                createTransition(freeformTask2, type = TRANSIT_TO_FRONT)
            )
        assertThat(result?.changes?.get(freeformTask2.token.asBinder())?.windowingMode)
            .isEqualTo(WINDOWING_MODE_FULLSCREEN)
    }

    @Test
    fun handleRequest_freeformTask_noOtherTasks_returnSwitchToFullscreenWCT() {
        assumeTrue(ENABLE_SHELL_TRANSITIONS)

        val task = createFreeformTask()
        val result = controller.handleRequest(Binder(), createTransition(task))
        assertThat(result?.changes?.get(task.token.asBinder())?.windowingMode)
            .isEqualTo(WINDOWING_MODE_FULLSCREEN)
    }

    @Test
    fun handleRequest_notOpenOrToFrontTransition_returnNull() {
        assumeTrue(ENABLE_SHELL_TRANSITIONS)

        val task =
            TestRunningTaskInfoBuilder()
                .setActivityType(ACTIVITY_TYPE_STANDARD)
                .setWindowingMode(WINDOWING_MODE_FULLSCREEN)
                .build()
        val transition = createTransition(task = task, type = WindowManager.TRANSIT_CLOSE)
        val result = controller.handleRequest(Binder(), transition)
        assertThat(result).isNull()
    }

    @Test
    fun handleRequest_noTriggerTask_returnNull() {
        assumeTrue(ENABLE_SHELL_TRANSITIONS)
        assertThat(controller.handleRequest(Binder(), createTransition(task = null))).isNull()
    }

    @Test
    fun handleRequest_triggerTaskNotStandard_returnNull() {
        assumeTrue(ENABLE_SHELL_TRANSITIONS)
        val task = TestRunningTaskInfoBuilder().setActivityType(ACTIVITY_TYPE_HOME).build()
        assertThat(controller.handleRequest(Binder(), createTransition(task))).isNull()
    }

    @Test
    fun handleRequest_triggerTaskNotFullscreenOrFreeform_returnNull() {
        assumeTrue(ENABLE_SHELL_TRANSITIONS)

        val task =
            TestRunningTaskInfoBuilder()
                .setActivityType(ACTIVITY_TYPE_STANDARD)
                .setWindowingMode(WINDOWING_MODE_MULTI_WINDOW)
                .build()
        assertThat(controller.handleRequest(Binder(), createTransition(task))).isNull()
    }

    private fun setUpFreeformTask(): RunningTaskInfo {
        val task = createFreeformTask()
        whenever(shellTaskOrganizer.getRunningTaskInfo(task.taskId)).thenReturn(task)
@@ -254,7 +374,7 @@ class DesktopTasksControllerTest : ShellTestCase() {

    private fun getLatestWct(): WindowContainerTransaction {
        val arg = ArgumentCaptor.forClass(WindowContainerTransaction::class.java)
        if (Transitions.ENABLE_SHELL_TRANSITIONS) {
        if (ENABLE_SHELL_TRANSITIONS) {
            verify(transitions).startTransition(anyInt(), arg.capture(), isNull())
        } else {
            verify(shellTaskOrganizer).applyTransaction(arg.capture())
@@ -263,12 +383,19 @@ class DesktopTasksControllerTest : ShellTestCase() {
    }

    private fun verifyWCTNotExecuted() {
        if (Transitions.ENABLE_SHELL_TRANSITIONS) {
        if (ENABLE_SHELL_TRANSITIONS) {
            verify(transitions, never()).startTransition(anyInt(), any(), isNull())
        } else {
            verify(shellTaskOrganizer, never()).applyTransaction(any())
        }
    }

    private fun createTransition(
        task: RunningTaskInfo?,
        @WindowManager.TransitionType type: Int = TRANSIT_OPEN
    ): TransitionRequestInfo {
        return TransitionRequestInfo(type, task, null /* remoteTransition */)
    }
}

private fun WindowContainerTransaction.assertReorderAt(index: Int, task: RunningTaskInfo) {