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

Commit dd5890bf authored by mattsziklay's avatar mattsziklay
Browse files

Change drag to edge to requestSplit when not in freeform.

When in fullscreen or split, dragging a task using the handle would move
the task to freeform in split snap bounds. This change makes it so the
task instead requests splitscreen windowing mode.

Test: Manual
Test: atest DragToDesktopTransitionHandlerTest
Bug: 336310725
Change-Id: Idf232146e625a193960a8d9497f116eb3e4ac0e4
parent c1c4902b
Loading
Loading
Loading
Loading
+33 −17
Original line number Diff line number Diff line
@@ -459,7 +459,9 @@ class DesktopTasksController(
            "DesktopTasksController: cancelDragToDesktop taskId=%d",
            task.taskId
        )
        dragToDesktopTransitionHandler.cancelDragToDesktopTransition()
        dragToDesktopTransitionHandler.cancelDragToDesktopTransition(
            DragToDesktopTransitionHandler.CancelState.STANDARD_CANCEL
        )
    }

    private fun moveToFullscreenWithAnimation(task: RunningTaskInfo, position: Point) {
@@ -1078,12 +1080,22 @@ class DesktopTasksController(
    @JvmOverloads
    fun requestSplit(
        taskInfo: RunningTaskInfo,
        leftOrTop: Boolean = false,
    ) {
        val windowingMode = taskInfo.windowingMode
        if (
            windowingMode == WINDOWING_MODE_FULLSCREEN || windowingMode == WINDOWING_MODE_FREEFORM
        leftOrTop: Boolean = false
    ) {
        // If a drag to desktop is in progress, we want to enter split select
        // even if the requesting task is already in split.
        val isDragging = dragToDesktopTransitionHandler.inProgress
        val shouldRequestSplit = taskInfo.isFullscreen || taskInfo.isFreeform || isDragging
        if (shouldRequestSplit) {
            if (isDragging) {
                releaseVisualIndicator()
                val cancelState = if (leftOrTop) {
                    DragToDesktopTransitionHandler.CancelState.CANCEL_SPLIT_LEFT
                } else {
                    DragToDesktopTransitionHandler.CancelState.CANCEL_SPLIT_RIGHT
                }
                dragToDesktopTransitionHandler.cancelDragToDesktopTransition(cancelState)
            } else {
                val wct = WindowContainerTransaction()
                addMoveToSplitChanges(wct, taskInfo)
                splitScreenController.requestEnterSplitSelect(
@@ -1094,6 +1106,7 @@ class DesktopTasksController(
                )
            }
        }
    }

    private fun getDefaultDensityDpi(): Int {
        return context.resources.displayMetrics.densityDpi
@@ -1220,7 +1233,10 @@ class DesktopTasksController(
     * @param taskInfo the task being dragged.
     * @param y height of drag, to be checked against status bar height.
     */
    fun onDragPositioningEndThroughStatusBar(inputCoordinates: PointF, taskInfo: RunningTaskInfo) {
    fun onDragPositioningEndThroughStatusBar(
        inputCoordinates: PointF,
        taskInfo: RunningTaskInfo,
    ) {
        val indicator = getVisualIndicator() ?: return
        val indicatorType = indicator.updateIndicatorType(inputCoordinates, taskInfo.windowingMode)
        when (indicatorType) {
@@ -1237,10 +1253,10 @@ class DesktopTasksController(
                cancelDragToDesktop(taskInfo)
            }
            DesktopModeVisualIndicator.IndicatorType.TO_SPLIT_LEFT_INDICATOR -> {
                finalizeDragToDesktop(taskInfo, getSnapBounds(taskInfo, SnapPosition.LEFT))
                requestSplit(taskInfo, leftOrTop = true)
            }
            DesktopModeVisualIndicator.IndicatorType.TO_SPLIT_RIGHT_INDICATOR -> {
                finalizeDragToDesktop(taskInfo, getSnapBounds(taskInfo, SnapPosition.RIGHT))
                requestSplit(taskInfo, leftOrTop = false)
            }
        }
    }
+140 −13
Original line number Diff line number Diff line
@@ -4,6 +4,7 @@ import android.animation.Animator
import android.animation.AnimatorListenerAdapter
import android.animation.RectEvaluator
import android.animation.ValueAnimator
import android.app.ActivityManager.RunningTaskInfo
import android.app.ActivityOptions
import android.app.ActivityOptions.SourceInfo
import android.app.ActivityTaskManager.INVALID_TASK_ID
@@ -12,9 +13,11 @@ import android.app.PendingIntent.FLAG_ALLOW_UNSAFE_IMPLICIT_INTENT
import android.app.PendingIntent.FLAG_MUTABLE
import android.app.WindowConfiguration.ACTIVITY_TYPE_HOME
import android.app.WindowConfiguration.WINDOWING_MODE_FREEFORM
import android.app.WindowConfiguration.WINDOWING_MODE_MULTI_WINDOW
import android.content.Context
import android.content.Intent
import android.content.Intent.FILL_IN_COMPONENT
import android.graphics.PointF
import android.graphics.Rect
import android.os.Bundle
import android.os.IBinder
@@ -30,6 +33,7 @@ import com.android.wm.shell.RootTaskDisplayAreaOrganizer
import com.android.wm.shell.common.split.SplitScreenConstants.SPLIT_POSITION_BOTTOM_OR_RIGHT
import com.android.wm.shell.common.split.SplitScreenConstants.SPLIT_POSITION_TOP_OR_LEFT
import com.android.wm.shell.common.split.SplitScreenConstants.SPLIT_POSITION_UNDEFINED
import com.android.wm.shell.common.split.SplitScreenConstants.SplitPosition
import com.android.wm.shell.protolog.ShellProtoLogGroup
import com.android.wm.shell.shared.TransitionUtil
import com.android.wm.shell.splitscreen.SplitScreenController
@@ -186,7 +190,7 @@ class DragToDesktopTransitionHandler(
     * outside the desktop drop zone and is instead dropped back into the status bar region that
     * means the user wants to remain in their current windowing mode.
     */
    fun cancelDragToDesktopTransition() {
    fun cancelDragToDesktopTransition(cancelState: CancelState) {
        if (!inProgress) {
            // Don't attempt to cancel a drag to desktop transition since there is no transition in
            // progress which means that the drag to desktop transition was never successfully
@@ -200,13 +204,32 @@ class DragToDesktopTransitionHandler(
            clearState()
            return
        }
        state.cancelled = true
        if (state.draggedTaskChange != null) {
        state.cancelState = cancelState

        if (state.draggedTaskChange != null && cancelState == CancelState.STANDARD_CANCEL) {
            // Regular case, transient launch of Home happened as is waiting for the cancel
            // transient to start and merge. Animate the cancellation (scale back to original
            // bounds) first before actually starting the cancel transition so that the wallpaper
            // is visible behind the animating task.
            startCancelAnimation()
        } else if (
            state.draggedTaskChange != null &&
            (cancelState == CancelState.CANCEL_SPLIT_LEFT ||
                    cancelState == CancelState.CANCEL_SPLIT_RIGHT)
            ) {
            // We have a valid dragged task, but the animation will be handled by
            // SplitScreenController; request the transition here.
            @SplitPosition val splitPosition = if (cancelState == CancelState.CANCEL_SPLIT_LEFT) {
                SPLIT_POSITION_TOP_OR_LEFT
            } else {
                SPLIT_POSITION_BOTTOM_OR_RIGHT
            }
            val wct = WindowContainerTransaction()
            restoreWindowOrder(wct, state)
            state.startTransitionFinishTransaction?.apply()
            state.startTransitionFinishCb?.onTransitionFinished(null /* wct */)
            requestSplitFromScaledTask(splitPosition, wct)
            clearState()
        } else {
            // There's no dragged task, this can happen when the "cancel" happened too quickly
            // before the "start" transition is even ready (like on a fling gesture). The
@@ -217,6 +240,54 @@ class DragToDesktopTransitionHandler(
        }
    }

    /** Calculate the bounds of a scaled task, then use those bounds to request split select. */
    private fun requestSplitFromScaledTask(
        @SplitPosition splitPosition: Int,
        wct: WindowContainerTransaction
    ) {
        val state = requireTransitionState()
        val taskInfo = state.draggedTaskChange?.taskInfo
            ?: error("Expected non-null taskInfo")
        val taskBounds = Rect(taskInfo.configuration.windowConfiguration.bounds)
        val taskScale = state.dragAnimator.scale
        val scaledWidth = taskBounds.width() * taskScale
        val scaledHeight = taskBounds.height() * taskScale
        val dragPosition = PointF(state.dragAnimator.position)
        state.dragAnimator.cancelAnimator()
        val animatedTaskBounds = Rect(
            dragPosition.x.toInt(),
            dragPosition.y.toInt(),
            (dragPosition.x + scaledWidth).toInt(),
            (dragPosition.y + scaledHeight).toInt()
        )
        requestSplitSelect(wct, taskInfo, splitPosition, animatedTaskBounds)
    }

    private fun requestSplitSelect(
        wct: WindowContainerTransaction,
        taskInfo: RunningTaskInfo,
        @SplitPosition splitPosition: Int,
        taskBounds: Rect = Rect(taskInfo.configuration.windowConfiguration.bounds)
    ) {
        // Prepare to exit split in order to enter split select.
        if (taskInfo.windowingMode == WINDOWING_MODE_MULTI_WINDOW) {
            splitScreenController.prepareExitSplitScreen(
                wct,
                splitScreenController.getStageOfTask(taskInfo.taskId),
                SplitScreenController.EXIT_REASON_DESKTOP_MODE
            )
            splitScreenController.transitionHandler.onSplitToDesktop()
        }
        wct.setWindowingMode(taskInfo.token, WINDOWING_MODE_MULTI_WINDOW)
        wct.setDensityDpi(taskInfo.token, context.resources.displayMetrics.densityDpi)
        splitScreenController.requestEnterSplitSelect(
            taskInfo,
            wct,
            splitPosition,
            taskBounds
        )
    }

    override fun startAnimation(
        transition: IBinder,
        info: TransitionInfo,
@@ -261,7 +332,7 @@ class DragToDesktopTransitionHandler(
                    is TransitionState.FromSplit -> {
                        state.splitRootChange = change
                        val layer =
                            if (!state.cancelled) {
                            if (state.cancelState == CancelState.NO_CANCEL) {
                                // Normal case, split root goes to the bottom behind everything
                                // else.
                                appLayers - i
@@ -311,8 +382,18 @@ class DragToDesktopTransitionHandler(
                // Do not do this in the cancel-early case though, since in that case nothing should
                // happen on screen so the layering will remain the same as if no transition
                // occurred.
                if (change.taskInfo?.taskId == state.draggedTaskId && !state.cancelled) {
                if (
                    change.taskInfo?.taskId == state.draggedTaskId &&
                    state.cancelState != CancelState.STANDARD_CANCEL
                ) {
                    // We need access to the dragged task's change in both non-cancel and split
                    // cancel cases.
                    state.draggedTaskChange = change
                }
                if (
                    change.taskInfo?.taskId == state.draggedTaskId &&
                    state.cancelState == CancelState.NO_CANCEL
                    ) {
                    taskDisplayAreaOrganizer.reparentToDisplayArea(
                        change.endDisplayId,
                        change.leash,
@@ -331,11 +412,11 @@ class DragToDesktopTransitionHandler(
        state.startTransitionFinishTransaction = finishTransaction
        startTransaction.apply()

        if (!state.cancelled) {
        if (state.cancelState == CancelState.NO_CANCEL) {
            // Normal case, start animation to scale down the dragged task. It'll also be moved to
            // follow the finger and when released we'll start the next phase/transition.
            state.dragAnimator.startAnimation()
        } else {
        } else if (state.cancelState == CancelState.STANDARD_CANCEL) {
            // Cancel-early case, the state was flagged was cancelled already, which means the
            // gesture ended in the cancel region. This can happen even before the start transition
            // is ready/animate here when cancelling quickly like with a fling. There's no point
@@ -343,6 +424,26 @@ class DragToDesktopTransitionHandler(
            // directly into starting the cancel transition to restore WM order. Surfaces should
            // not move as if no transition happened.
            startCancelDragToDesktopTransition()
        } else if (
            state.cancelState == CancelState.CANCEL_SPLIT_LEFT ||
            state.cancelState == CancelState.CANCEL_SPLIT_RIGHT
            ){
            // Cancel-early case for split-cancel. The state was flagged already as a cancel for
            // requesting split select. Similar to the above, this can happen due to quick fling
            // gestures. We can simply request split here without needing to calculate animated
            // task bounds as the task has not shrunk at all.
            val splitPosition = if (state.cancelState == CancelState.CANCEL_SPLIT_LEFT) {
                SPLIT_POSITION_TOP_OR_LEFT
            } else {
                SPLIT_POSITION_BOTTOM_OR_RIGHT
            }
            val taskInfo = state.draggedTaskChange?.taskInfo
                ?: error("Expected non-null task info.")
            val wct = WindowContainerTransaction()
            restoreWindowOrder(wct)
            state.startTransitionFinishTransaction?.apply()
            state.startTransitionFinishCb?.onTransitionFinished(null /* wct */)
            requestSplitSelect(wct, taskInfo, splitPosition)
        }
        return true
    }
@@ -355,6 +456,12 @@ class DragToDesktopTransitionHandler(
        finishCallback: Transitions.TransitionFinishCallback
    ) {
        val state = requireTransitionState()
        // We don't want to merge the split select animation if that's what we requested.
        if (state.cancelState == CancelState.CANCEL_SPLIT_LEFT ||
            state.cancelState == CancelState.CANCEL_SPLIT_RIGHT) {
            clearState()
            return
        }
        val isCancelTransition =
            info.type == TRANSIT_DESKTOP_MODE_CANCEL_DRAG_TO_DESKTOP &&
                transition == state.cancelTransitionToken &&
@@ -552,6 +659,17 @@ class DragToDesktopTransitionHandler(
    private fun startCancelDragToDesktopTransition() {
        val state = requireTransitionState()
        val wct = WindowContainerTransaction()
        restoreWindowOrder(wct, state)
        state.cancelTransitionToken =
            transitions.startTransition(
                TRANSIT_DESKTOP_MODE_CANCEL_DRAG_TO_DESKTOP, wct, this
            )
    }

    private fun restoreWindowOrder(
        wct: WindowContainerTransaction,
        state: TransitionState = requireTransitionState()
    ) {
        when (state) {
            is TransitionState.FromFullscreen -> {
                // There may have been tasks sent behind home that are not the dragged task (like
@@ -580,9 +698,6 @@ class DragToDesktopTransitionHandler(
        }
        val homeWc = state.homeToken ?: error("Home task should be non-null before cancelling")
        wct.restoreTransientOrder(homeWc)

        state.cancelTransitionToken =
            transitions.startTransition(TRANSIT_DESKTOP_MODE_CANCEL_DRAG_TO_DESKTOP, wct, this)
    }

    private fun clearState() {
@@ -624,7 +739,7 @@ class DragToDesktopTransitionHandler(
        abstract var cancelTransitionToken: IBinder?
        abstract var homeToken: WindowContainerToken?
        abstract var draggedTaskChange: Change?
        abstract var cancelled: Boolean
        abstract var cancelState: CancelState
        abstract var startAborted: Boolean

        data class FromFullscreen(
@@ -636,7 +751,7 @@ class DragToDesktopTransitionHandler(
            override var cancelTransitionToken: IBinder? = null,
            override var homeToken: WindowContainerToken? = null,
            override var draggedTaskChange: Change? = null,
            override var cancelled: Boolean = false,
            override var cancelState: CancelState = CancelState.NO_CANCEL,
            override var startAborted: Boolean = false,
            var otherRootChanges: MutableList<Change> = mutableListOf()
        ) : TransitionState()
@@ -650,13 +765,25 @@ class DragToDesktopTransitionHandler(
            override var cancelTransitionToken: IBinder? = null,
            override var homeToken: WindowContainerToken? = null,
            override var draggedTaskChange: Change? = null,
            override var cancelled: Boolean = false,
            override var cancelState: CancelState = CancelState.NO_CANCEL,
            override var startAborted: Boolean = false,
            var splitRootChange: Change? = null,
            var otherSplitTask: Int
        ) : TransitionState()
    }

    /** Enum to provide context on cancelling a drag to desktop event. */
    enum class CancelState {
        /** No cancel case; this drag is not flagged for a cancel event. */
        NO_CANCEL,
        /** A standard cancel event; should restore task to previous windowing mode. */
        STANDARD_CANCEL,
        /** A cancel event where the task will request to enter split on the left side. */
        CANCEL_SPLIT_LEFT,
        /** A cancel event where the task will request to enter split on the right side. */
        CANCEL_SPLIT_RIGHT
    }

    companion object {
        /** The duration of the animation to commit or cancel the drag-to-desktop gesture. */
        private const val DRAG_TO_DESKTOP_FINISH_ANIM_DURATION_MS = 336L
+115 −46

File changed.

Preview size limit exceeded, changes collapsed.