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

Commit 498ed6c6 authored by Ivan Tkachenko's avatar Ivan Tkachenko Committed by Android (Google) Code Review
Browse files

Merge "Add flagged drag to desktop transition handler" into main

parents 90b0f2fe f2873f4b
Loading
Loading
Loading
Loading
+8 −3
Original line number Diff line number Diff line
@@ -32,6 +32,7 @@ import com.android.internal.jank.InteractionJankMonitor;
import com.android.internal.logging.UiEventLogger;
import com.android.internal.statusbar.IStatusBarService;
import com.android.launcher3.icons.IconProvider;
import com.android.window.flags.Flags;
import com.android.wm.shell.RootTaskDisplayAreaOrganizer;
import com.android.wm.shell.ShellTaskOrganizer;
import com.android.wm.shell.WindowManagerShellWrapper;
@@ -58,6 +59,7 @@ import com.android.wm.shell.common.TaskStackListenerImpl;
import com.android.wm.shell.common.TransactionPool;
import com.android.wm.shell.dagger.back.ShellBackAnimationModule;
import com.android.wm.shell.dagger.pip.PipModule;
import com.android.wm.shell.desktopmode.DefaultDragToDesktopTransitionHandler;
import com.android.wm.shell.desktopmode.DesktopModeEventLogger;
import com.android.wm.shell.desktopmode.DesktopModeLoggerTransitionObserver;
import com.android.wm.shell.desktopmode.DesktopModeTaskRepository;
@@ -68,6 +70,7 @@ import com.android.wm.shell.desktopmode.DragToDesktopTransitionHandler;
import com.android.wm.shell.desktopmode.EnterDesktopTaskTransitionHandler;
import com.android.wm.shell.desktopmode.ExitDesktopTaskTransitionHandler;
import com.android.wm.shell.desktopmode.ReturnToDragStartAnimator;
import com.android.wm.shell.desktopmode.SpringDragToDesktopTransitionHandler;
import com.android.wm.shell.desktopmode.ToggleResizeDesktopTaskTransitionHandler;
import com.android.wm.shell.draganddrop.DragAndDropController;
import com.android.wm.shell.draganddrop.GlobalDragListener;
@@ -603,9 +606,11 @@ public abstract class WMShellModule {
            Context context,
            Transitions transitions,
            RootTaskDisplayAreaOrganizer rootTaskDisplayAreaOrganizer,
            Optional<DesktopTasksLimiter> desktopTasksLimiter,
            InteractionJankMonitor interactionJankMonitor) {
        return new DragToDesktopTransitionHandler(context, transitions,
        return Flags.enableDesktopWindowingTransitions()
                ? new SpringDragToDesktopTransitionHandler(context, transitions,
                        rootTaskDisplayAreaOrganizer, interactionJankMonitor)
                : new DefaultDragToDesktopTransitionHandler(context, transitions,
                        rootTaskDisplayAreaOrganizer, interactionJankMonitor);
    }

+240 −127
Original line number Diff line number Diff line
@@ -27,7 +27,6 @@ import android.view.WindowManager.TRANSIT_CLOSE
import android.window.TransitionInfo
import android.window.TransitionInfo.Change
import android.window.TransitionRequestInfo
import android.window.WindowContainerToken
import android.window.WindowContainerTransaction
import com.android.internal.jank.Cuj.CUJ_DESKTOP_MODE_ENTER_APP_HANDLE_DRAG_HOLD
import com.android.internal.jank.Cuj.CUJ_DESKTOP_MODE_ENTER_APP_HANDLE_DRAG_RELEASE
@@ -55,8 +54,13 @@ import java.util.function.Supplier
 * Handles the transition to enter desktop from fullscreen by dragging on the handle bar. It also
 * handles the cancellation case where the task is dragged back to the status bar area in the same
 * gesture.
 *
 * It's a base sealed class that delegates flag dependant logic to its subclasses:
 * [DefaultDragToDesktopTransitionHandler] and [SpringDragToDesktopTransitionHandler]
 *
 * TODO(b/356764679): Clean up after the full flag rollout
 */
class DragToDesktopTransitionHandler(
sealed class DragToDesktopTransitionHandler(
    private val context: Context,
    private val transitions: Transitions,
    private val taskDisplayAreaOrganizer: RootTaskDisplayAreaOrganizer,
@@ -64,19 +68,6 @@ class DragToDesktopTransitionHandler(
    private val transactionSupplier: Supplier<SurfaceControl.Transaction>,
) : TransitionHandler {

    constructor(
        context: Context,
        transitions: Transitions,
        rootTaskDisplayAreaOrganizer: RootTaskDisplayAreaOrganizer,
        interactionJankMonitor: InteractionJankMonitor
    ) : this(
        context,
        transitions,
        rootTaskDisplayAreaOrganizer,
        interactionJankMonitor,
        Supplier { SurfaceControl.Transaction() }
    )

    private val rectEvaluator = RectEvaluator(Rect())
    private val launchHomeIntent = Intent(Intent.ACTION_MAIN).addCategory(Intent.CATEGORY_HOME)

@@ -307,24 +298,18 @@ class DragToDesktopTransitionHandler(
            return false
        }

        // Layering: non-wallpaper, non-home tasks excluding the dragged task go at the bottom,
        // then Home on top of that, wallpaper on top of that and finally the dragged task on top
        // of everything.
        val appLayers = info.changes.size
        val homeLayers = info.changes.size * 2
        val wallpaperLayers = info.changes.size * 3
        val dragLayer = wallpaperLayers
        val layers = calculateStartDragToDesktopLayers(info)
        val leafTaskFilter = TransitionUtil.LeafTaskFilter()
        info.changes.withIndex().forEach { (i, change) ->
            if (TransitionUtil.isWallpaper(change)) {
                val layer = wallpaperLayers - i
                val layer = layers.wallpaperLayers - i
                startTransaction.apply {
                    setLayer(change.leash, layer)
                    show(change.leash)
                }
            } else if (isHomeChange(change)) {
                state.homeToken = change.container
                val layer = homeLayers - i
                state.homeChange = change
                val layer = layers.homeLayers - i
                startTransaction.apply {
                    setLayer(change.leash, layer)
                    show(change.leash)
@@ -338,11 +323,11 @@ class DragToDesktopTransitionHandler(
                            if (state.cancelState == CancelState.NO_CANCEL) {
                                // Normal case, split root goes to the bottom behind everything
                                // else.
                                appLayers - i
                                layers.appLayers - i
                            } else {
                                // Cancel-early case, pretend nothing happened so split root stays
                                // top.
                                dragLayer
                                layers.dragLayer
                            }
                        startTransaction.apply {
                            setLayer(change.leash, layer)
@@ -357,7 +342,7 @@ class DragToDesktopTransitionHandler(
                            state.draggedTaskChange = change
                            val bounds = change.endAbsBounds
                            startTransaction.apply {
                                setLayer(change.leash, dragLayer)
                                setLayer(change.leash, layers.dragLayer)
                                setWindowCrop(change.leash, bounds.width(), bounds.height())
                                show(change.leash)
                            }
@@ -370,7 +355,7 @@ class DragToDesktopTransitionHandler(
                            state.otherRootChanges.add(change)
                            val bounds = change.endAbsBounds
                            startTransaction.apply {
                                setLayer(change.leash, appLayers - i)
                                setLayer(change.leash, layers.appLayers - i)
                                setWindowCrop(change.leash, bounds.width(), bounds.height())
                                show(change.leash)
                            }
@@ -404,7 +389,7 @@ class DragToDesktopTransitionHandler(
                    )
                    val bounds = change.endAbsBounds
                    startTransaction.apply {
                        setLayer(change.leash, dragLayer)
                        setLayer(change.leash, layers.dragLayer)
                        setWindowCrop(change.leash, bounds.width(), bounds.height())
                        show(change.leash)
                    }
@@ -452,6 +437,15 @@ class DragToDesktopTransitionHandler(
        return true
    }

    /**
     * Calculates start drag to desktop layers for transition [info]. The leash layer is calculated
     * based on its change position in the transition, e.g. `appLayer = appLayers - i`, where i is
     * the change index.
     */
    protected abstract fun calculateStartDragToDesktopLayers(
        info: TransitionInfo
    ): DragToDesktopLayers

    override fun mergeAnimation(
        transition: IBinder,
        info: TransitionInfo,
@@ -483,40 +477,73 @@ class DragToDesktopTransitionHandler(
            state.startTransitionFinishCb
                ?: error("Start transition expected to be waiting for merge but wasn't")
        if (isEndTransition) {
            info.changes.withIndex().forEach { (i, change) ->
                // If we're exiting split, hide the remaining split task.
                if (
                    state is TransitionState.FromSplit &&
                        change.taskInfo?.taskId == state.otherSplitTask
                ) {
                    t.hide(change.leash)
                    startTransactionFinishT.hide(change.leash)
                }
                if (change.mode == TRANSIT_CLOSE) {
                    t.hide(change.leash)
                    startTransactionFinishT.hide(change.leash)
                } else if (change.taskInfo?.taskId == state.draggedTaskId) {
            setupEndDragToDesktop(
                info,
                startTransaction = t,
                finishTransaction = startTransactionFinishT
            )
            // Call finishCallback to merge animation before startTransitionFinishCb is called
            finishCallback.onTransitionFinished(null /* wct */)
            animateEndDragToDesktop(startTransaction = t, startTransitionFinishCb)
        } else if (isCancelTransition) {
            info.changes.forEach { change ->
                t.show(change.leash)
                startTransactionFinishT.show(change.leash)
            }
            t.apply()
            finishCallback.onTransitionFinished(null /* wct */)
            startTransitionFinishCb.onTransitionFinished(null /* wct */)
            clearState()
        }
    }

    protected open fun setupEndDragToDesktop(
        info: TransitionInfo,
        startTransaction: SurfaceControl.Transaction,
        finishTransaction: SurfaceControl.Transaction
    ) {
        val state = requireTransitionState()
        info.changes.forEachIndexed { i, change ->
            when {
                state is TransitionState.FromSplit &&
                    change.taskInfo?.taskId == state.otherSplitTask -> {
                    // If we're exiting split, hide the remaining split task.
                    startTransaction.hide(change.leash)
                    finishTransaction.hide(change.leash)
                }
                change.mode == TRANSIT_CLOSE -> {
                    startTransaction.hide(change.leash)
                    finishTransaction.hide(change.leash)
                }
                change.taskInfo?.taskId == state.draggedTaskId -> {
                    startTransaction.show(change.leash)
                    finishTransaction.show(change.leash)
                    state.draggedTaskChange = change
                } else if (change.taskInfo?.windowingMode == WINDOWING_MODE_FREEFORM) {
                }
                change.taskInfo?.windowingMode == WINDOWING_MODE_FREEFORM -> {
                    // Other freeform tasks that are being restored go behind the dragged task.
                    val draggedTaskLeash =
                        state.draggedTaskChange?.leash
                            ?: error("Expected dragged leash to be non-null")
                    t.setRelativeLayer(change.leash, draggedTaskLeash, -i)
                    startTransactionFinishT.setRelativeLayer(change.leash, draggedTaskLeash, -i)
                    startTransaction.setRelativeLayer(change.leash, draggedTaskLeash, -i)
                    finishTransaction.setRelativeLayer(change.leash, draggedTaskLeash, -i)
                }
            }
        }
    }

    private fun animateEndDragToDesktop(
        startTransaction: SurfaceControl.Transaction,
        startTransitionFinishCb: Transitions.TransitionFinishCallback
    ) {
        val state = requireTransitionState()
        val draggedTaskChange =
                state.draggedTaskChange
                    ?: throw IllegalStateException("Expected non-null change of dragged task")
            state.draggedTaskChange ?: error("Expected non-null change of dragged task")
        val draggedTaskLeash = draggedTaskChange.leash
        val startBounds = draggedTaskChange.startAbsBounds
        val endBounds = draggedTaskChange.endAbsBounds

            // Pause any animation that may be currently playing; we will use the relevant
        // Cancel any animation that may be currently playing; we will use the relevant
        // details of that animation here.
        state.dragAnimator.cancelAnimator()
        // We still apply scale to task bounds; as we animate the bounds to their
@@ -533,16 +560,15 @@ class DragToDesktopTransitionHandler(
                startPosition.y.toInt() + unscaledStartHeight
            )

            dragToDesktopStateListener?.onCommitToDesktopAnimationStart(t)
        dragToDesktopStateListener?.onCommitToDesktopAnimationStart(startTransaction)
        // Accept the merge by applying the merging transaction (applied by #showResizeVeil)
        // and finish callback. Show the veil and position the task at the first frame before
        // starting the final animation.
        onTaskResizeAnimationListener.onAnimationStart(
            state.draggedTaskId,
                t,
            startTransaction,
            unscaledStartBounds
        )
            finishCallback.onTransitionFinished(null /* wct */)
        val tx: SurfaceControl.Transaction = transactionSupplier.get()
        ValueAnimator.ofObject(rectEvaluator, unscaledStartBounds, endBounds)
            .setDuration(DRAG_TO_DESKTOP_FINISH_ANIM_DURATION_MS)
@@ -581,16 +607,6 @@ class DragToDesktopTransitionHandler(
                )
                start()
            }
        } else if (isCancelTransition) {
            info.changes.forEach { change ->
                t.show(change.leash)
                startTransactionFinishT.show(change.leash)
            }
            t.apply()
            finishCallback.onTransitionFinished(null /* wct */)
            startTransitionFinishCb.onTransitionFinished(null /* wct */)
            clearState()
        }
    }

    override fun handleRequest(
@@ -707,7 +723,8 @@ class DragToDesktopTransitionHandler(
                wct.reorder(wc, true /* toTop */)
            }
        }
        val homeWc = state.homeToken ?: error("Home task should be non-null before cancelling")
        val homeWc =
            state.homeChange?.container ?: error("Home task should be non-null before cancelling")
        wct.restoreTransientOrder(homeWc)
    }

@@ -731,10 +748,21 @@ class DragToDesktopTransitionHandler(
        return splitScreenController.getTaskInfo(otherTaskPos)?.taskId
    }

    private fun requireTransitionState(): TransitionState {
    protected fun requireTransitionState(): TransitionState {
        return transitionState ?: error("Expected non-null transition state")
    }

    /**
     * Represents the layering (Z order) that will be given to any window based on its type during
     * the "start" transition of the drag-to-desktop transition
     */
    protected data class DragToDesktopLayers(
        val appLayers: Int,
        val homeLayers: Int,
        val wallpaperLayers: Int,
        val dragLayer: Int,
    )

    interface DragToDesktopStateListener {
        fun onCommitToDesktopAnimationStart(tx: SurfaceControl.Transaction)

@@ -748,7 +776,7 @@ class DragToDesktopTransitionHandler(
        abstract var startTransitionFinishCb: Transitions.TransitionFinishCallback?
        abstract var startTransitionFinishTransaction: SurfaceControl.Transaction?
        abstract var cancelTransitionToken: IBinder?
        abstract var homeToken: WindowContainerToken?
        abstract var homeChange: Change?
        abstract var draggedTaskChange: Change?
        abstract var cancelState: CancelState
        abstract var startAborted: Boolean
@@ -760,7 +788,7 @@ class DragToDesktopTransitionHandler(
            override var startTransitionFinishCb: Transitions.TransitionFinishCallback? = null,
            override var startTransitionFinishTransaction: SurfaceControl.Transaction? = null,
            override var cancelTransitionToken: IBinder? = null,
            override var homeToken: WindowContainerToken? = null,
            override var homeChange: Change? = null,
            override var draggedTaskChange: Change? = null,
            override var cancelState: CancelState = CancelState.NO_CANCEL,
            override var startAborted: Boolean = false,
@@ -774,7 +802,7 @@ class DragToDesktopTransitionHandler(
            override var startTransitionFinishCb: Transitions.TransitionFinishCallback? = null,
            override var startTransitionFinishTransaction: SurfaceControl.Transaction? = null,
            override var cancelTransitionToken: IBinder? = null,
            override var homeToken: WindowContainerToken? = null,
            override var homeChange: Change? = null,
            override var draggedTaskChange: Change? = null,
            override var cancelState: CancelState = CancelState.NO_CANCEL,
            override var startAborted: Boolean = false,
@@ -800,3 +828,88 @@ class DragToDesktopTransitionHandler(
        private const val DRAG_TO_DESKTOP_FINISH_ANIM_DURATION_MS = 336L
    }
}

/** Enables flagged rollout of the [SpringDragToDesktopTransitionHandler] */
class DefaultDragToDesktopTransitionHandler
@JvmOverloads
constructor(
    context: Context,
    transitions: Transitions,
    taskDisplayAreaOrganizer: RootTaskDisplayAreaOrganizer,
    interactionJankMonitor: InteractionJankMonitor,
    transactionSupplier: Supplier<SurfaceControl.Transaction> = Supplier {
        SurfaceControl.Transaction()
    },
) :
    DragToDesktopTransitionHandler(
        context,
        transitions,
        taskDisplayAreaOrganizer,
        interactionJankMonitor,
        transactionSupplier
    ) {

    /**
     * @return layers in order:
     * - appLayers - non-wallpaper, non-home tasks excluding the dragged task go at the bottom
     * - homeLayers - home task on top of apps
     * - wallpaperLayers - wallpaper on top of home
     * - dragLayer - the dragged task on top of everything, there's only 1 dragged task
     */
    override fun calculateStartDragToDesktopLayers(info: TransitionInfo): DragToDesktopLayers =
        DragToDesktopLayers(
            appLayers = info.changes.size,
            homeLayers = info.changes.size * 2,
            wallpaperLayers = info.changes.size * 3,
            dragLayer = info.changes.size * 3
        )
}

/** Desktop transition handler with spring based animation for the end drag to desktop transition */
class SpringDragToDesktopTransitionHandler
@JvmOverloads
constructor(
    context: Context,
    transitions: Transitions,
    taskDisplayAreaOrganizer: RootTaskDisplayAreaOrganizer,
    interactionJankMonitor: InteractionJankMonitor,
    transactionSupplier: Supplier<SurfaceControl.Transaction> = Supplier {
        SurfaceControl.Transaction()
    },
) :
    DragToDesktopTransitionHandler(
        context,
        transitions,
        taskDisplayAreaOrganizer,
        interactionJankMonitor,
        transactionSupplier
    ) {

    /**
     * @return layers in order:
     * - appLayers - below everything z < 0, effectively hides the leash
     * - homeLayers - home task on top of apps, z in 0..<size
     * - wallpaperLayers - wallpaper on top of home, z in size..<size*2
     * - dragLayer - the dragged task on top of everything, z == size*2
     */
    override fun calculateStartDragToDesktopLayers(info: TransitionInfo): DragToDesktopLayers =
        DragToDesktopLayers(
            appLayers = -1,
            homeLayers = info.changes.size - 1,
            wallpaperLayers = info.changes.size * 2 - 1,
            dragLayer = info.changes.size * 2
        )

    override fun setupEndDragToDesktop(
        info: TransitionInfo,
        startTransaction: SurfaceControl.Transaction,
        finishTransaction: SurfaceControl.Transaction
    ) {
        super.setupEndDragToDesktop(info, startTransaction, finishTransaction)

        val state = requireTransitionState()
        val homeLeash = state.homeChange?.leash ?: error("Expects home leash to be non-null")
        // Hide home on finish to prevent flickering when wallpaper activity flag is enabled
        finishTransaction.hide(homeLeash)
    }
}
+177 −78

File changed.

Preview size limit exceeded, changes collapsed.