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

Commit ae9506b4 authored by Orhan Uysal's avatar Orhan Uysal
Browse files

Change back navigation impl to rely on observer

This cl changes how back navigation manipulates the repository by
removing the dependency on FreeformTaskListener.

1. Move all back navigation observing logic to
  DesktopTasksTransitionObserver. Now we solely rely on the observer to
decide whether a back navigation has happened.
2. For back navigation cases where an app is closing, verify that it's
   not marked as closing task(which only happens on app header exit) and
 that the last back gesture was triggered on this task.
3. Check whether for these closing cases, we should animate the
   transition in shell or animate it in launcher. This is done by
identfying whether we are removing wallpaper or not in the transition.

Fix: 379783329
Test: atest DesktopTasksControllerTest | atest
DesktopTasksTransitionObserverTest | atest FreeformTasksListenerTest
Flag: com.android.window.flags.enable_desktop_windowing_back_navigation

Change-Id: I916cd6b777a455d9ffc733d0108e3f88ad7a1b9e
parent 31b1e9e7
Loading
Loading
Loading
Loading
+3 −1
Original line number Diff line number Diff line
@@ -50,6 +50,7 @@ import com.android.wm.shell.WindowManagerShellWrapper;
import com.android.wm.shell.activityembedding.ActivityEmbeddingController;
import com.android.wm.shell.apptoweb.AppToWebGenericLinksParser;
import com.android.wm.shell.apptoweb.AssistContentRequester;
import com.android.wm.shell.back.BackAnimationController;
import com.android.wm.shell.bubbles.BubbleController;
import com.android.wm.shell.bubbles.BubbleData;
import com.android.wm.shell.bubbles.BubbleDataRepository;
@@ -87,7 +88,6 @@ import com.android.wm.shell.desktopmode.DesktopModeEventLogger;
import com.android.wm.shell.desktopmode.DesktopModeKeyGestureHandler;
import com.android.wm.shell.desktopmode.DesktopModeLoggerTransitionObserver;
import com.android.wm.shell.desktopmode.DesktopModeUiEventLogger;
import com.android.wm.shell.desktopmode.DesktopRepository;
import com.android.wm.shell.desktopmode.DesktopTaskChangeListener;
import com.android.wm.shell.desktopmode.DesktopTasksController;
import com.android.wm.shell.desktopmode.DesktopTasksLimiter;
@@ -1049,6 +1049,7 @@ public abstract class WMShellModule {
            Transitions transitions,
            ShellTaskOrganizer shellTaskOrganizer,
            Optional<DesktopMixedTransitionHandler> desktopMixedTransitionHandler,
            Optional<BackAnimationController> backAnimationController,
            ShellInit shellInit) {
        return desktopUserRepositories.flatMap(
                repository ->
@@ -1059,6 +1060,7 @@ public abstract class WMShellModule {
                                        transitions,
                                        shellTaskOrganizer,
                                        desktopMixedTransitionHandler.get(),
                                        backAnimationController.get(),
                                        shellInit)));
    }

+0 −10
Original line number Diff line number Diff line
@@ -1777,16 +1777,6 @@ class DesktopTasksController(
        if (!DesktopModeFlags.ENABLE_DESKTOP_WINDOWING_BACK_NAVIGATION.isTrue()) {
            taskRepository.addClosingTask(task.displayId, task.taskId)
            desktopTilingDecorViewModel.removeTaskIfTiled(task.displayId, task.taskId)
        } else if (requestType == TRANSIT_CLOSE) {
            // Handle closing tasks, tasks that are going to back are handled in
            // [DesktopTasksTransitionObserver].
            desktopMixedTransitionHandler.addPendingMixedTransition(
                DesktopMixedTransitionHandler.PendingMixedTransition.Minimize(
                    transition,
                    task.taskId,
                    taskRepository.getVisibleTaskCount(task.displayId) == 1,
                )
            )
        }

        taskbarDesktopTaskListener?.onTaskbarCornerRoundingUpdate(
+62 −1
Original line number Diff line number Diff line
@@ -30,6 +30,7 @@ import android.window.TransitionInfo
import android.window.WindowContainerTransaction
import com.android.internal.protolog.ProtoLog
import com.android.wm.shell.ShellTaskOrganizer
import com.android.wm.shell.back.BackAnimationController
import com.android.wm.shell.desktopmode.DesktopModeTransitionTypes.isExitDesktopModeTransition
import com.android.wm.shell.protolog.ShellProtoLogGroup.WM_SHELL_DESKTOP_MODE
import com.android.wm.shell.shared.TransitionUtil
@@ -48,6 +49,7 @@ class DesktopTasksTransitionObserver(
    private val transitions: Transitions,
    private val shellTaskOrganizer: ShellTaskOrganizer,
    private val desktopMixedTransitionHandler: DesktopMixedTransitionHandler,
    private val backAnimationController: BackAnimationController,
    shellInit: ShellInit,
) : Transitions.TransitionObserver {

@@ -128,21 +130,80 @@ class DesktopTasksTransitionObserver(
                    )
                }
            }
        } else if (info.type == TRANSIT_CLOSE) {
            // In some cases app will be closing as a result of back navigation but we would like
            // to minimize. Mark the task closing as minimized.
            var hasWallpaperClosing = false
            var minimizingTask: Int? = null
            for (change in info.changes) {
                val taskInfo = change.taskInfo
                if (taskInfo == null || taskInfo.taskId == -1) continue
                if (change.mode != TRANSIT_CLOSE) continue

                if (minimizingTask == null) {
                    minimizingTask = getMinimizingTaskForClosingTransition(taskInfo)
                }

                if (DesktopWallpaperActivity.isWallpaperTask(taskInfo)) {
                    hasWallpaperClosing = true
                }
            }

            if (minimizingTask == null) return
            // If the transition has wallpaper closing, it means we are moving out of desktop.
            desktopMixedTransitionHandler.addPendingMixedTransition(
                DesktopMixedTransitionHandler.PendingMixedTransition.Minimize(
                    transition,
                    minimizingTask,
                    isLastTask = hasWallpaperClosing,
                )
            )
        }
    }

    /**
     * Given this a closing task in a closing transition, a task is assumed to be closed by back
     * navigation if:
     * 1) Desktop mode is visible.
     * 2) Task is in freeform.
     * 3) Task is the latest task that the back gesture is triggered on.
     * 4) It's not marked as a closing task as a result of closing it by the app header.
     *
     * This doesn't necessarily mean all the cases are because of back navigation but those cases
     * will be rare. E.g. triggering back navigation on an app that pops up a close dialog, and
     * closing it will minimize it here.
     */
    private fun getMinimizingTaskForClosingTransition(
        taskInfo: ActivityManager.RunningTaskInfo
    ): Int? {
        val desktopRepository = desktopUserRepositories.getProfile(taskInfo.userId)
        val visibleTaskCount = desktopRepository.getVisibleTaskCount(taskInfo.displayId)
        if (
            visibleTaskCount > 0 &&
                taskInfo.windowingMode == WINDOWING_MODE_FREEFORM &&
                backAnimationController.latestTriggerBackTask == taskInfo.taskId &&
                !desktopRepository.isClosingTask(taskInfo.taskId)
        ) {
            desktopRepository.minimizeTask(taskInfo.displayId, taskInfo.taskId)
            return taskInfo.taskId
        }
        return null
    }

    private fun removeWallpaperOnLastTaskClosingIfNeeded(
        transition: IBinder,
        info: TransitionInfo,
    ) {
        // TODO: 380868195 - Smooth animation for wallpaper activity closing just by itself
        for (change in info.changes) {
            val taskInfo = change.taskInfo
            if (taskInfo == null || taskInfo.taskId == -1) {
                continue
            }

            val desktopRepository = desktopUserRepositories.getProfile(taskInfo.userId)
            if (
                desktopRepository.getVisibleTaskCount(taskInfo.displayId) == 1 &&
                desktopRepository.getVisibleTaskCount(taskInfo.displayId) == 0 &&
                    change.mode == TRANSIT_CLOSE &&
                    taskInfo.windowingMode == WINDOWING_MODE_FREEFORM &&
                    desktopRepository.wallpaperActivityToken != null
+6 −8
Original line number Diff line number Diff line
@@ -30,8 +30,8 @@ import com.android.internal.protolog.ProtoLog;
import com.android.wm.shell.ShellTaskOrganizer;
import com.android.wm.shell.common.LaunchAdjacentController;
import com.android.wm.shell.desktopmode.DesktopRepository;
import com.android.wm.shell.desktopmode.DesktopUserRepositories;
import com.android.wm.shell.desktopmode.DesktopTasksController;
import com.android.wm.shell.desktopmode.DesktopUserRepositories;
import com.android.wm.shell.protolog.ShellProtoLogGroup;
import com.android.wm.shell.shared.desktopmode.DesktopModeStatus;
import com.android.wm.shell.sysui.ShellInit;
@@ -121,15 +121,13 @@ public class FreeformTaskListener implements ShellTaskOrganizer.TaskListener,
                    mDesktopUserRepositories.get().getProfile(taskInfo.userId);
            // TODO: b/370038902 - Handle Activity#finishAndRemoveTask.
            if (!DesktopModeFlags.ENABLE_DESKTOP_WINDOWING_BACK_NAVIGATION.isTrue()
                    || repository.isClosingTask(taskInfo.taskId)) {
                    || !repository.isMinimizedTask(taskInfo.taskId)) {
                // A task that's vanishing should be removed:
                // - If it's closed by the X button which means it's marked as a closing task.
                // - If it's not yet minimized. It can be minimized when a back navigation is
                // triggered on a task and the task is closing. It will be marked as minimized in
                // [DesktopTasksTransitionObserver] before it gets here.
                repository.removeClosingTask(taskInfo.taskId);
                repository.removeFreeformTask(taskInfo.displayId, taskInfo.taskId);
            } else {
                repository.updateTask(taskInfo.displayId, taskInfo.taskId, /* isVisible= */
                        false);
                repository.minimizeTask(taskInfo.displayId, taskInfo.taskId);
            }
        }
        mWindowDecorationViewModel.onTaskVanished(taskInfo);
+88 −26
Original line number Diff line number Diff line
@@ -39,6 +39,7 @@ 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.back.BackAnimationController
import com.android.wm.shell.common.ShellExecutor
import com.android.wm.shell.desktopmode.DesktopModeTransitionTypes.TRANSIT_EXIT_DESKTOP_MODE_TASK_DRAG
import com.android.wm.shell.shared.desktopmode.DesktopModeStatus
@@ -67,9 +68,7 @@ class DesktopTasksTransitionObserverTest {
    @JvmField
    @Rule
    val extendedMockitoRule =
        ExtendedMockitoRule.Builder(this)
            .mockStatic(DesktopModeStatus::class.java)
            .build()!!
        ExtendedMockitoRule.Builder(this).mockStatic(DesktopModeStatus::class.java).build()!!

    private val testExecutor = mock<ShellExecutor>()
    private val mockShellInit = mock<ShellInit>()
@@ -79,6 +78,7 @@ class DesktopTasksTransitionObserverTest {
    private val userRepositories = mock<DesktopUserRepositories>()
    private val taskRepository = mock<DesktopRepository>()
    private val mixedHandler = mock<DesktopMixedTransitionHandler>()
    private val backAnimationController = mock<BackAnimationController>()

    private lateinit var transitionObserver: DesktopTasksTransitionObserver
    private lateinit var shellInit: ShellInit
@@ -93,7 +93,13 @@ class DesktopTasksTransitionObserverTest {

        transitionObserver =
            DesktopTasksTransitionObserver(
                context, userRepositories, transitions, shellTaskOrganizer, mixedHandler, shellInit
                context,
                userRepositories,
                transitions,
                shellTaskOrganizer,
                mixedHandler,
                backAnimationController,
                shellInit
            )
    }

@@ -105,8 +111,7 @@ class DesktopTasksTransitionObserverTest {

        transitionObserver.onTransitionReady(
            transition = mock(),
            info =
            createBackNavigationTransition(task),
            info = createBackNavigationTransition(task),
            startTransaction = mock(),
            finishTransaction = mock(),
        )
@@ -115,6 +120,52 @@ class DesktopTasksTransitionObserverTest {
        verify(mixedHandler).addPendingMixedTransition(any())
    }

    @Test
    @EnableFlags(Flags.FLAG_ENABLE_DESKTOP_WINDOWING_BACK_NAVIGATION)
    fun backNavigation_withCloseTransitionNotLastTask_taskMinimized() {
        val task = createTaskInfo(1)
        val transition = mock<IBinder>()
        whenever(taskRepository.getVisibleTaskCount(any())).thenReturn(2)
        whenever(taskRepository.isClosingTask(task.taskId)).thenReturn(false)
        whenever(backAnimationController.latestTriggerBackTask).thenReturn(task.taskId)

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

        verify(taskRepository).minimizeTask(task.displayId, task.taskId)
        val pendingTransition =
            DesktopMixedTransitionHandler.PendingMixedTransition.Minimize(
                transition, task.taskId, isLastTask = false)
        verify(mixedHandler).addPendingMixedTransition(pendingTransition)
    }

    @Test
    @EnableFlags(Flags.FLAG_ENABLE_DESKTOP_WINDOWING_BACK_NAVIGATION)
    fun backNavigation_withCloseTransitionLastTask_taskMinimized() {
        val task = createTaskInfo(1)
        val transition = mock<IBinder>()
        whenever(taskRepository.getVisibleTaskCount(any())).thenReturn(1)
        whenever(taskRepository.isClosingTask(task.taskId)).thenReturn(false)
        whenever(backAnimationController.latestTriggerBackTask).thenReturn(task.taskId)

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

        verify(taskRepository).minimizeTask(task.displayId, task.taskId)
        val pendingTransition =
            DesktopMixedTransitionHandler.PendingMixedTransition.Minimize(
                transition, task.taskId, isLastTask = true)
        verify(mixedHandler).addPendingMixedTransition(pendingTransition)
    }

    @Test
    @EnableFlags(Flags.FLAG_ENABLE_DESKTOP_WINDOWING_BACK_NAVIGATION)
    fun backNavigation_nullTaskInfo_taskNotMinimized() {
@@ -123,8 +174,7 @@ class DesktopTasksTransitionObserverTest {

        transitionObserver.onTransitionReady(
            transition = mock(),
            info =
            createBackNavigationTransition(null),
            info = createBackNavigationTransition(null),
            startTransaction = mock(),
            finishTransaction = mock(),
        )
@@ -173,7 +223,7 @@ class DesktopTasksTransitionObserverTest {
        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.getVisibleTaskCount(task.displayId)).thenReturn(0)
        whenever(taskRepository.wallpaperActivityToken).thenReturn(wallpaperToken)

        transitionObserver.onTransitionReady(
@@ -190,17 +240,27 @@ class DesktopTasksTransitionObserverTest {
    }

    private fun createBackNavigationTransition(
        task: RunningTaskInfo?
        task: RunningTaskInfo?,
        type: Int = TRANSIT_TO_BACK,
        withWallpaper: Boolean = false,
    ): TransitionInfo {
        return TransitionInfo(TRANSIT_TO_BACK, 0 /* flags */).apply {
        return TransitionInfo(type, 0 /* flags */).apply {
            addChange(
                Change(mock(), mock()).apply {
                    mode = TRANSIT_TO_BACK
                    mode = type
                    parent = null
                    taskInfo = task
                    flags = flags
                })
            if (withWallpaper) {
                addChange(
                    Change(mock(), mock()).apply {
                        mode = TRANSIT_CLOSE
                        parent = null
                        taskInfo = createWallpaperTaskInfo()
                        flags = flags
                    })
            }
            )
        }
    }

@@ -215,14 +275,11 @@ class DesktopTasksTransitionObserverTest {
                    parent = null
                    taskInfo = task
                    flags = flags
                }
            )
                })
        }
    }

    private fun createCloseTransition(
        task: RunningTaskInfo?
    ): TransitionInfo {
    private fun createCloseTransition(task: RunningTaskInfo?): TransitionInfo {
        return TransitionInfo(TRANSIT_CLOSE, 0 /* flags */).apply {
            addChange(
                Change(mock(), mock()).apply {
@@ -230,8 +287,7 @@ class DesktopTasksTransitionObserverTest {
                    parent = null
                    taskInfo = task
                    flags = flags
                }
            )
                })
        }
    }

@@ -243,8 +299,7 @@ class DesktopTasksTransitionObserverTest {
        if (handlerClass == null) {
            Mockito.verify(transitions).startTransition(eq(type), arg.capture(), isNull())
        } else {
            Mockito.verify(transitions)
                .startTransition(eq(type), arg.capture(), isA(handlerClass))
            Mockito.verify(transitions).startTransition(eq(type), arg.capture(), isA(handlerClass))
        }
        return arg.value
    }
@@ -268,8 +323,15 @@ class DesktopTasksTransitionObserverTest {
            displayId = DEFAULT_DISPLAY
            configuration.windowConfiguration.windowingMode = windowingMode
            token = WindowContainerToken(Mockito.mock(IWindowContainerToken::class.java))
            baseIntent = Intent().apply {
                component = ComponentName("package", "component.name")
            baseIntent = Intent().apply { component = ComponentName("package", "component.name") }
        }

    private fun createWallpaperTaskInfo() =
        RunningTaskInfo().apply {
            token = mock<WindowContainerToken>()
            baseIntent =
                Intent().apply {
                    component = DesktopWallpaperActivity.wallpaperActivityComponent
                }
        }
}
Loading