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

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

Merge "Minimize tasks moving back with back nav." into main

parents 32658daa d1eccd27
Loading
Loading
Loading
Loading
+12 −4
Original line number Diff line number Diff line
@@ -43,6 +43,7 @@ import android.view.Display.DEFAULT_DISPLAY
import android.view.DragEvent
import android.view.SurfaceControl
import android.view.WindowManager.TRANSIT_CHANGE
import android.view.WindowManager.TRANSIT_CLOSE
import android.view.WindowManager.TRANSIT_NONE
import android.view.WindowManager.TRANSIT_OPEN
import android.view.WindowManager.TRANSIT_TO_FRONT
@@ -1061,7 +1062,10 @@ class DesktopTasksController(
                    // Check if freeform task launch during recents should be handled
                    shouldHandleMidRecentsFreeformLaunch -> handleMidRecentsFreeformTaskLaunch(task)
                    // Check if the closing task needs to be handled
                    TransitionUtil.isClosingType(request.type) -> handleTaskClosing(task)
                    TransitionUtil.isClosingType(request.type) -> handleTaskClosing(
                        task,
                        request.type
                    )
                    // Check if the top task shouldn't be allowed to enter desktop mode
                    isIncompatibleTask(task) -> handleIncompatibleTaskLaunch(task)
                    // Check if fullscreen task should be updated
@@ -1288,7 +1292,10 @@ class DesktopTasksController(
    }

    /** Handle task closing by removing wallpaper activity if it's the last active task */
    private fun handleTaskClosing(task: RunningTaskInfo): WindowContainerTransaction? {
    private fun handleTaskClosing(
        task: RunningTaskInfo,
        transitionType: Int
    ): WindowContainerTransaction? {
        logV("handleTaskClosing")
        if (!isDesktopModeShowing(task.displayId))
            return null
@@ -1301,9 +1308,10 @@ class DesktopTasksController(
            removeWallpaperActivity(wct)
        }
        taskRepository.addClosingTask(task.displayId, task.taskId)
        // If a CLOSE or TO_BACK is triggered on a desktop task, remove the task.
        // If a CLOSE is triggered on a desktop task, remove the task.
        if (DesktopModeFlags.ENABLE_DESKTOP_WINDOWING_BACK_NAVIGATION.isTrue() &&
            taskRepository.isVisibleTask(task.taskId)
            taskRepository.isVisibleTask(task.taskId) &&
            transitionType == TRANSIT_CLOSE
        ) {
            wct.removeTask(task.token)
        }
+28 −1
Original line number Diff line number Diff line
@@ -16,16 +16,19 @@

package com.android.wm.shell.desktopmode

import android.app.WindowConfiguration.WINDOWING_MODE_FREEFORM
import android.content.Context
import android.os.IBinder
import android.view.SurfaceControl
import android.view.WindowManager
import android.view.WindowManager.TRANSIT_TO_BACK
import android.window.TransitionInfo
import android.window.WindowContainerTransaction
import android.window.flags.DesktopModeFlags
import android.window.flags.DesktopModeFlags.ENABLE_DESKTOP_WINDOWING_WALLPAPER_ACTIVITY
import com.android.internal.protolog.ProtoLog
import com.android.wm.shell.ShellTaskOrganizer
import com.android.wm.shell.protolog.ShellProtoLogGroup.WM_SHELL_DESKTOP_MODE
import android.window.flags.DesktopModeFlags.ENABLE_DESKTOP_WINDOWING_WALLPAPER_ACTIVITY
import com.android.wm.shell.shared.desktopmode.DesktopModeStatus
import com.android.wm.shell.sysui.ShellInit
import com.android.wm.shell.transition.Transitions
@@ -64,6 +67,30 @@ class DesktopTasksTransitionObserver(
    ) {
        // TODO: b/332682201 Update repository state
        updateWallpaperToken(info)

        if (DesktopModeFlags.ENABLE_DESKTOP_WINDOWING_BACK_NAVIGATION.isTrue()) {
            handleBackNavigation(info)
        }
    }

    private fun handleBackNavigation(info: TransitionInfo) {
        // When default back navigation happens, transition type is TO_BACK and the change is
        // TO_BACK. Mark the task going to back as minimized.
        if (info.type == TRANSIT_TO_BACK) {
            for (change in info.changes) {
                val taskInfo = change.taskInfo
                if (taskInfo == null || taskInfo.taskId == -1) {
                    continue
                }

                if (desktopModeTaskRepository.getVisibleTaskCount(taskInfo.displayId) > 0 &&
                    change.mode == TRANSIT_TO_BACK &&
                    taskInfo.windowingMode == WINDOWING_MODE_FREEFORM
                ) {
                    desktopModeTaskRepository.minimizeTask(taskInfo.displayId, taskInfo.taskId)
                }
            }
        }
    }

    override fun onTransitionStarting(transition: IBinder) {
+4 −76
Original line number Diff line number Diff line
@@ -2086,16 +2086,13 @@ class DesktopTasksControllerTest : ShellTestCase() {
  }

  @Test
  @EnableFlags(
    Flags.FLAG_ENABLE_DESKTOP_WINDOWING_WALLPAPER_ACTIVITY,
    Flags.FLAG_ENABLE_DESKTOP_WINDOWING_BACK_NAVIGATION
  )
  fun handleRequest_backTransition_singleTaskNoToken_withWallpaper_withBackNav_removesTask() {
  @EnableFlags(Flags.FLAG_ENABLE_DESKTOP_WINDOWING_WALLPAPER_ACTIVITY,)
  fun handleRequest_backTransition_singleTaskNoToken_withWallpaper_removesTask() {
    val task = setUpFreeformTask()

    val result = controller.handleRequest(Binder(), createTransition(task, type = TRANSIT_TO_BACK))

    assertNotNull(result, "Should handle request").assertRemoveAt(0, task.token)
    assertNull(result, "Should not handle request")
  }

  @Test
@@ -2136,27 +2133,9 @@ class DesktopTasksControllerTest : ShellTestCase() {
    assertNull(result, "Should not handle request")
  }

  @Test
  @EnableFlags(
    Flags.FLAG_ENABLE_DESKTOP_WINDOWING_WALLPAPER_ACTIVITY,
    Flags.FLAG_ENABLE_DESKTOP_WINDOWING_BACK_NAVIGATION
  )
  fun handleRequest_backTransition_singleTask_withWallpaper_withBackNav_removesWallpaperAndTask() {
    val task = setUpFreeformTask()
    val wallpaperToken = MockToken().token()

    taskRepository.wallpaperActivityToken = wallpaperToken
    val result = controller.handleRequest(Binder(), createTransition(task, type = TRANSIT_TO_BACK))

    // Should create remove wallpaper transaction
    assertNotNull(result, "Should handle request").assertRemoveAt(index = 0, wallpaperToken)
    result.assertRemoveAt(index = 1, task.token)
  }

  @Test
  @EnableFlags(Flags.FLAG_ENABLE_DESKTOP_WINDOWING_WALLPAPER_ACTIVITY)
  @DisableFlags(Flags.FLAG_ENABLE_DESKTOP_WINDOWING_BACK_NAVIGATION)
  fun handleRequest_backTransition_singleTaskWithToken_noBackNav_removesWallpaper() {
  fun handleRequest_backTransition_singleTaskWithToken_removesWallpaper() {
    val task = setUpFreeformTask()
    val wallpaperToken = MockToken().token()

@@ -2182,24 +2161,8 @@ class DesktopTasksControllerTest : ShellTestCase() {
    assertNull(result, "Should not handle request")
  }

  @Test
  @EnableFlags(
    Flags.FLAG_ENABLE_DESKTOP_WINDOWING_WALLPAPER_ACTIVITY,
    Flags.FLAG_ENABLE_DESKTOP_WINDOWING_BACK_NAVIGATION
  )
  fun handleRequest_backTransition_multipleTasks_withWallpaper_withBackNav_removesTask() {
    val task1 = setUpFreeformTask()
    setUpFreeformTask()

    taskRepository.wallpaperActivityToken = MockToken().token()
    val result = controller.handleRequest(Binder(), createTransition(task1, type = TRANSIT_TO_BACK))

    assertNotNull(result, "Should handle request").assertRemoveAt(index = 0, task1.token)
  }

  @Test
  @EnableFlags(Flags.FLAG_ENABLE_DESKTOP_WINDOWING_WALLPAPER_ACTIVITY)
  @DisableFlags(Flags.FLAG_ENABLE_DESKTOP_WINDOWING_BACK_NAVIGATION)
  fun handleRequest_backTransition_multipleTasks_noBackNav_doesNotHandle() {
    val task1 = setUpFreeformTask()
    setUpFreeformTask()
@@ -2224,23 +2187,6 @@ class DesktopTasksControllerTest : ShellTestCase() {
    taskRepository.addClosingTask(displayId = DEFAULT_DISPLAY, taskId = task2.taskId)
    val result = controller.handleRequest(Binder(), createTransition(task1, type = TRANSIT_TO_BACK))

    // Should create remove wallpaper transaction
    assertNotNull(result, "Should handle request").assertRemoveAt(index = 0, wallpaperToken)
    result.assertRemoveAt(index = 1, task1.token)
  }

  @Test
  @EnableFlags(Flags.FLAG_ENABLE_DESKTOP_WINDOWING_WALLPAPER_ACTIVITY)
  @DisableFlags(Flags.FLAG_ENABLE_DESKTOP_WINDOWING_BACK_NAVIGATION)
  fun handleRequest_backTransition_multipleTasksSingleNonClosing_noBackNav_removesWallpaper() {
    val task1 = setUpFreeformTask(displayId = DEFAULT_DISPLAY)
    val task2 = setUpFreeformTask(displayId = DEFAULT_DISPLAY)
    val wallpaperToken = MockToken().token()

    taskRepository.wallpaperActivityToken = wallpaperToken
    taskRepository.addClosingTask(displayId = DEFAULT_DISPLAY, taskId = task2.taskId)
    val result = controller.handleRequest(Binder(), createTransition(task1, type = TRANSIT_TO_BACK))

    // Should create remove wallpaper transaction
    assertNotNull(result, "Should handle request").assertRemoveAt(index = 0, wallpaperToken)
  }
@@ -2248,7 +2194,6 @@ class DesktopTasksControllerTest : ShellTestCase() {
  @Test
  @EnableFlags(
    Flags.FLAG_ENABLE_DESKTOP_WINDOWING_WALLPAPER_ACTIVITY,
    Flags.FLAG_ENABLE_DESKTOP_WINDOWING_BACK_NAVIGATION
  )
  fun handleRequest_backTransition_multipleTasksSingleNonMinimized_removesWallpaperAndTask() {
    val task1 = setUpFreeformTask(displayId = DEFAULT_DISPLAY)
@@ -2259,23 +2204,6 @@ class DesktopTasksControllerTest : ShellTestCase() {
    taskRepository.minimizeTask(displayId = DEFAULT_DISPLAY, taskId = task2.taskId)
    val result = controller.handleRequest(Binder(), createTransition(task1, type = TRANSIT_TO_BACK))

    // Should create remove wallpaper transaction
    assertNotNull(result, "Should handle request").assertRemoveAt(index = 0, wallpaperToken)
    result.assertRemoveAt(index = 1, task1.token)
  }

  @Test
  @EnableFlags(Flags.FLAG_ENABLE_DESKTOP_WINDOWING_WALLPAPER_ACTIVITY)
  @DisableFlags(Flags.FLAG_ENABLE_DESKTOP_WINDOWING_BACK_NAVIGATION)
  fun handleRequest_backTransition_multipleTasksSingleNonMinimized_noBackNav_removesWallpaper() {
    val task1 = setUpFreeformTask(displayId = DEFAULT_DISPLAY)
    val task2 = setUpFreeformTask(displayId = DEFAULT_DISPLAY)
    val wallpaperToken = MockToken().token()

    taskRepository.wallpaperActivityToken = wallpaperToken
    taskRepository.minimizeTask(displayId = DEFAULT_DISPLAY, taskId = task2.taskId)
    val result = controller.handleRequest(Binder(), createTransition(task1, type = TRANSIT_TO_BACK))

    // Should create remove wallpaper transaction
    assertNotNull(result, "Should handle request").assertRemoveAt(index = 0, wallpaperToken)
  }
+138 −0
Original line number Diff line number Diff line
/*
 * Copyright (C) 2024 The Android Open Source Project
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *      http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */

package com.android.wm.shell.desktopmode

import android.app.ActivityManager.RunningTaskInfo
import android.app.WindowConfiguration.WINDOWING_MODE_FREEFORM
import android.content.ComponentName
import android.content.Context
import android.content.Intent
import android.platform.test.annotations.EnableFlags
import android.view.Display.DEFAULT_DISPLAY
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 com.android.modules.utils.testing.ExtendedMockitoRule
import com.android.window.flags.Flags
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 org.junit.Before
import org.junit.Rule
import org.junit.Test
import org.mockito.Mockito
import org.mockito.kotlin.any
import org.mockito.kotlin.mock
import org.mockito.kotlin.never
import org.mockito.kotlin.spy
import org.mockito.kotlin.verify
import org.mockito.kotlin.whenever

class DesktopTasksTransitionObserverTest {

    @JvmField
    @Rule
    val extendedMockitoRule =
        ExtendedMockitoRule.Builder(this)
            .mockStatic(DesktopModeStatus::class.java)
            .build()!!

    private val testExecutor = mock<ShellExecutor>()
    private val mockShellInit = mock<ShellInit>()
    private val transitions = mock<Transitions>()
    private val context = mock<Context>()
    private val shellTaskOrganizer = mock<ShellTaskOrganizer>()
    private val taskRepository = mock<DesktopModeTaskRepository>()

    private lateinit var transitionObserver: DesktopTasksTransitionObserver
    private lateinit var shellInit: ShellInit

    @Before
    fun setup() {
        whenever(DesktopModeStatus.canEnterDesktopMode(any())).thenReturn(true)
        shellInit = spy(ShellInit(testExecutor))

        transitionObserver =
            DesktopTasksTransitionObserver(
                context, taskRepository, transitions, shellTaskOrganizer, shellInit
            )
    }

    @Test
    @EnableFlags(Flags.FLAG_ENABLE_DESKTOP_WINDOWING_BACK_NAVIGATION)
    fun backNavigation_taskMinimized() {
        val task = createTaskInfo(1)
        whenever(taskRepository.getVisibleTaskCount(any())).thenReturn(1)

        transitionObserver.onTransitionReady(
            transition = mock(),
            info =
            createBackNavigationTransition(task),
            startTransaction = mock(),
            finishTransaction = mock(),
        )

        verify(taskRepository).minimizeTask(task.displayId, task.taskId)
    }

    @Test
    @EnableFlags(Flags.FLAG_ENABLE_DESKTOP_WINDOWING_BACK_NAVIGATION)
    fun backNavigation_nullTaskInfo_taskNotMinimized() {
        val task = createTaskInfo(1)
        whenever(taskRepository.getVisibleTaskCount(any())).thenReturn(1)

        transitionObserver.onTransitionReady(
            transition = mock(),
            info =
            createBackNavigationTransition(null),
            startTransaction = mock(),
            finishTransaction = mock(),
        )

        verify(taskRepository, never()).minimizeTask(task.displayId, task.taskId)
    }

    private fun createBackNavigationTransition(
        task: RunningTaskInfo?
    ): TransitionInfo {
        return TransitionInfo(TRANSIT_TO_BACK, 0 /* flags */).apply {
            addChange(
                Change(mock(), mock()).apply {
                    mode = TRANSIT_TO_BACK
                    parent = null
                    taskInfo = task
                    flags = flags
                }
            )
        }
    }

    private fun createTaskInfo(id: Int) =
        RunningTaskInfo().apply {
            taskId = id
            displayId = DEFAULT_DISPLAY
            configuration.windowConfiguration.windowingMode = WINDOWING_MODE_FREEFORM
            token = WindowContainerToken(Mockito.mock(IWindowContainerToken::class.java))
            baseIntent = Intent().apply {
                component = ComponentName("package", "component.name")
            }
        }
}