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

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

Add back nav animation with minimizing.

- Introduce `DesktopBackNavigationTransitionHandler` for animating back navigation
  transitions.
- Use `DesktopMixedTransitionHandler` to delegate pending minimize
  transitions caused by back navigation to this handler.
- Update `DesktopTasksController` and `DesktopTasksTransitionObserver`
  to create pendingMinimizeTransitions on back navigation cases.

Bug: 323384177
Test: atest DesktopBackNavigationTransitionHandlerTest
Test: atest DesktopMixedTransitionHandlerTest
Flag: com.android.window.flags.enable_desktop_windowing_back_navigation
Change-Id: I44da90ab0c4a2c1500faca30c0123f9c989cdf5c
parent 340d9400
Loading
Loading
Loading
Loading
+15 −0
Original line number Diff line number Diff line
@@ -67,6 +67,7 @@ import com.android.wm.shell.dagger.pip.PipModule;
import com.android.wm.shell.desktopmode.CloseDesktopTaskTransitionHandler;
import com.android.wm.shell.desktopmode.DefaultDragToDesktopTransitionHandler;
import com.android.wm.shell.desktopmode.DesktopActivityOrientationChangeHandler;
import com.android.wm.shell.desktopmode.DesktopBackNavigationTransitionHandler;
import com.android.wm.shell.desktopmode.DesktopDisplayEventHandler;
import com.android.wm.shell.desktopmode.DesktopImmersiveController;
import com.android.wm.shell.desktopmode.DesktopMixedTransitionHandler;
@@ -906,6 +907,16 @@ public abstract class WMShellModule {
        return new CloseDesktopTaskTransitionHandler(context, mainExecutor, animExecutor);
    }

    @WMSingleton
    @Provides
    static DesktopBackNavigationTransitionHandler provideDesktopBackNavigationTransitionHandler(
            @ShellMainThread ShellExecutor mainExecutor,
            @ShellAnimationThread ShellExecutor animExecutor,
            DisplayController displayController) {
        return new DesktopBackNavigationTransitionHandler(mainExecutor, animExecutor,
                displayController);
    }

    @WMSingleton
    @Provides
    static DesktopModeDragAndDropTransitionHandler provideDesktopModeDragAndDropTransitionHandler(
@@ -957,6 +968,7 @@ public abstract class WMShellModule {
            Optional<DesktopRepository> desktopRepository,
            Transitions transitions,
            ShellTaskOrganizer shellTaskOrganizer,
            Optional<DesktopMixedTransitionHandler> desktopMixedTransitionHandler,
            ShellInit shellInit) {
        return desktopRepository.flatMap(
                repository ->
@@ -966,6 +978,7 @@ public abstract class WMShellModule {
                                        repository,
                                        transitions,
                                        shellTaskOrganizer,
                                        desktopMixedTransitionHandler.get(),
                                        shellInit)));
    }

@@ -978,6 +991,7 @@ public abstract class WMShellModule {
            FreeformTaskTransitionHandler freeformTaskTransitionHandler,
            CloseDesktopTaskTransitionHandler closeDesktopTaskTransitionHandler,
            Optional<DesktopImmersiveController> desktopImmersiveController,
            DesktopBackNavigationTransitionHandler desktopBackNavigationTransitionHandler,
            InteractionJankMonitor interactionJankMonitor,
            @ShellMainThread Handler handler,
            ShellInit shellInit,
@@ -994,6 +1008,7 @@ public abstract class WMShellModule {
                        freeformTaskTransitionHandler,
                        closeDesktopTaskTransitionHandler,
                        desktopImmersiveController.get(),
                        desktopBackNavigationTransitionHandler,
                        interactionJankMonitor,
                        handler,
                        shellInit,
+97 −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.animation.Animator
import android.app.WindowConfiguration.WINDOWING_MODE_FREEFORM
import android.os.IBinder
import android.util.DisplayMetrics
import android.view.SurfaceControl.Transaction
import android.window.TransitionInfo
import android.window.TransitionRequestInfo
import android.window.WindowContainerTransaction
import com.android.wm.shell.common.DisplayController
import com.android.wm.shell.common.ShellExecutor
import com.android.wm.shell.shared.TransitionUtil
import com.android.wm.shell.shared.animation.MinimizeAnimator.create
import com.android.wm.shell.transition.Transitions

/**
 * The [Transitions.TransitionHandler] that handles transitions for tasks that are closing or going
 * to back as part of back navigation. This handler is used only for animating transitions.
 */
class DesktopBackNavigationTransitionHandler(
    private val mainExecutor: ShellExecutor,
    private val animExecutor: ShellExecutor,
    private val displayController: DisplayController,
) : Transitions.TransitionHandler {

    /** Shouldn't handle anything */
    override fun handleRequest(
        transition: IBinder,
        request: TransitionRequestInfo,
    ): WindowContainerTransaction? = null

    /** Animates a transition with minimizing tasks */
    override fun startAnimation(
        transition: IBinder,
        info: TransitionInfo,
        startTransaction: Transaction,
        finishTransaction: Transaction,
        finishCallback: Transitions.TransitionFinishCallback,
    ): Boolean {
        if (!TransitionUtil.isClosingType(info.type)) return false

        val animations = mutableListOf<Animator>()
        val onAnimFinish: (Animator) -> Unit = { animator ->
            mainExecutor.execute {
                // Animation completed
                animations.remove(animator)
                if (animations.isEmpty()) {
                    // All animations completed, finish the transition
                    finishCallback.onTransitionFinished(/* wct= */ null)
                }
            }
        }

        animations +=
            info.changes
                .filter {
                    it.mode == info.type &&
                            it.taskInfo?.windowingMode == WINDOWING_MODE_FREEFORM
                }
                .mapNotNull { createMinimizeAnimation(it, finishTransaction, onAnimFinish) }
        if (animations.isEmpty()) return false
        animExecutor.execute { animations.forEach(Animator::start) }
        return true
    }

    private fun createMinimizeAnimation(
        change: TransitionInfo.Change,
        finishTransaction: Transaction,
        onAnimFinish: (Animator) -> Unit
    ): Animator? {
        val t = Transaction()
        val sc = change.leash
        finishTransaction.hide(sc)
        val displayMetrics: DisplayMetrics? =
            change.taskInfo?.let {
                displayController.getDisplayContext(it.displayId)?.getResources()?.displayMetrics
            }
        return displayMetrics?.let { create(it, change, t, onAnimFinish) }
    }
}
+53 −0
Original line number Diff line number Diff line
@@ -52,6 +52,7 @@ class DesktopMixedTransitionHandler(
    private val freeformTaskTransitionHandler: FreeformTaskTransitionHandler,
    private val closeDesktopTaskTransitionHandler: CloseDesktopTaskTransitionHandler,
    private val desktopImmersiveController: DesktopImmersiveController,
    private val desktopBackNavigationTransitionHandler: DesktopBackNavigationTransitionHandler,
    private val interactionJankMonitor: InteractionJankMonitor,
    @ShellMainThread private val handler: Handler,
    shellInit: ShellInit,
@@ -161,6 +162,14 @@ class DesktopMixedTransitionHandler(
                finishTransaction,
                finishCallback
            )
            is PendingMixedTransition.Minimize -> animateMinimizeTransition(
                pending,
                transition,
                info,
                startTransaction,
                finishTransaction,
                finishCallback
            )
        }
    }

@@ -267,6 +276,42 @@ class DesktopMixedTransitionHandler(
        )
    }

    private fun animateMinimizeTransition(
        pending: PendingMixedTransition.Minimize,
        transition: IBinder,
        info: TransitionInfo,
        startTransaction: SurfaceControl.Transaction,
        finishTransaction: SurfaceControl.Transaction,
        finishCallback: TransitionFinishCallback,
    ): Boolean {
        if (!DesktopModeFlags.ENABLE_DESKTOP_WINDOWING_BACK_NAVIGATION.isTrue) return false

        val minimizeChange = findDesktopTaskChange(info, pending.minimizingTask)
        if (minimizeChange == null) {
            logW("Should have minimizing desktop task")
            return false
        }
        if (pending.isLastTask) {
            // Dispatch close desktop task animation to the default transition handlers.
            return dispatchToLeftoverHandler(
                transition,
                info,
                startTransaction,
                finishTransaction,
                finishCallback
            )
        }

        // Animate minimizing desktop task transition with [DesktopBackNavigationTransitionHandler].
        return desktopBackNavigationTransitionHandler.startAnimation(
            transition,
            info,
            startTransaction,
            finishTransaction,
            finishCallback,
        )
    }

    override fun onTransitionConsumed(
        transition: IBinder,
        aborted: Boolean,
@@ -395,6 +440,14 @@ class DesktopMixedTransitionHandler(
            val minimizingTask: Int?,
            val exitingImmersiveTask: Int?,
        ) : PendingMixedTransition()

        /** A task is minimizing. This should be used for task going to back and some closing cases
         * with back navigation. */
        data class Minimize(
            override val transition: IBinder,
            val minimizingTask: Int,
            val isLastTask: Boolean,
        ) : PendingMixedTransition()
    }

    private fun logV(msg: String, vararg arguments: Any?) {
+14 −3
Original line number Diff line number Diff line
@@ -1291,7 +1291,11 @@ 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,
                        transition,
                        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
@@ -1621,7 +1625,7 @@ 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, transition: IBinder, requestType: Int): WindowContainerTransaction? {
        logV("handleTaskClosing")
        if (!isDesktopModeShowing(task.displayId))
            return null
@@ -1637,8 +1641,15 @@ 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(
            doesAnyTaskRequireTaskbarRounding(
                task.displayId,
+8 −3
Original line number Diff line number Diff line
@@ -46,6 +46,7 @@ class DesktopTasksTransitionObserver(
    private val desktopRepository: DesktopRepository,
    private val transitions: Transitions,
    private val shellTaskOrganizer: ShellTaskOrganizer,
    private val desktopMixedTransitionHandler: DesktopMixedTransitionHandler,
    shellInit: ShellInit
) : Transitions.TransitionObserver {

@@ -71,7 +72,7 @@ class DesktopTasksTransitionObserver(
        // TODO: b/332682201 Update repository state
        updateWallpaperToken(info)
        if (DesktopModeFlags.ENABLE_DESKTOP_WINDOWING_BACK_NAVIGATION.isTrue()) {
            handleBackNavigation(info)
            handleBackNavigation(transition, info)
            removeTaskIfNeeded(info)
        }
        removeWallpaperOnLastTaskClosingIfNeeded(transition, info)
@@ -95,7 +96,7 @@ class DesktopTasksTransitionObserver(
        }
    }

    private fun handleBackNavigation(info: TransitionInfo) {
    private fun handleBackNavigation(transition: IBinder, 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) {
@@ -105,10 +106,14 @@ class DesktopTasksTransitionObserver(
                    continue
                }

                if (desktopRepository.getVisibleTaskCount(taskInfo.displayId) > 0 &&
                val visibleTaskCount = desktopRepository.getVisibleTaskCount(taskInfo.displayId)
                if (visibleTaskCount > 0 &&
                    change.mode == TRANSIT_TO_BACK &&
                    taskInfo.windowingMode == WINDOWING_MODE_FREEFORM) {
                    desktopRepository.minimizeTask(taskInfo.displayId, taskInfo.taskId)
                    desktopMixedTransitionHandler.addPendingMixedTransition(
                        DesktopMixedTransitionHandler.PendingMixedTransition.Minimize(
                            transition, taskInfo.taskId, visibleTaskCount == 1))
                }
            }
        }
Loading