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

Commit 95a538f7 authored by mattsziklay's avatar mattsziklay
Browse files

Fix visual issues with splitscreen to desktop transition.

Fixes three minor visual issues with the split to desktop transition:

1 - Modifies getFocusedDecor to always prioritize a currently dragged decor,
fixing an issue where some split -> desktop transitions would hide the
dragged task if they ended on the opposite side of the screen.

2 - Adds onSplitToDesktopAnimation to StageCoordinator to
set stages as not visible once the to desktop animation is complete,
preventing the split divider from briefly showing if we then navigate to
overview.

3- When requesting split, sets the task to WINDOWING_MODE_MULTI_WINDOW
to avoid a task flicker. Then, when the stage listener detects a change,
it will set the windowing mode to UNDEFINED to inherit the mode from
split stage, ensuring the task doesn't use freeform bounds in split
stage.

Bug: 336312668
Bug: 336311310
Bug: 323378048
Test: atest DragToDesktopTransitionHandlerTest
Test: manual
Change-Id: I55634fbe9687cd9a8f433818c8bafe983763356c
parent 2e086e36
Loading
Loading
Loading
Loading
+19 −8
Original line number Original line Diff line number Diff line
@@ -158,6 +158,10 @@ class DesktopTasksController(
            com.android.wm.shell.R.dimen.desktop_mode_transition_area_width
            com.android.wm.shell.R.dimen.desktop_mode_transition_area_width
        )
        )


    /** Task id of the task currently being dragged from fullscreen/split. */
    val draggingTaskId
        get() = dragToDesktopTransitionHandler.draggingTaskId

    private var recentsAnimationRunning = false
    private var recentsAnimationRunning = false
    private lateinit var splitScreenController: SplitScreenController
    private lateinit var splitScreenController: SplitScreenController


@@ -406,6 +410,7 @@ class DesktopTasksController(
    fun onDesktopSplitSelectAnimComplete(taskInfo: RunningTaskInfo) {
    fun onDesktopSplitSelectAnimComplete(taskInfo: RunningTaskInfo) {
        val wct = WindowContainerTransaction()
        val wct = WindowContainerTransaction()
        wct.setBounds(taskInfo.token, Rect())
        wct.setBounds(taskInfo.token, Rect())
        wct.setWindowingMode(taskInfo.token, WINDOWING_MODE_UNDEFINED)
        shellTaskOrganizer.applyTransaction(wct)
        shellTaskOrganizer.applyTransaction(wct)
    }
    }


@@ -447,7 +452,9 @@ class DesktopTasksController(
        )
        )
        val wct = WindowContainerTransaction()
        val wct = WindowContainerTransaction()
        wct.setBounds(task.token, Rect())
        wct.setBounds(task.token, Rect())
        addMoveToSplitChanges(wct, task)
        // Rather than set windowing mode to multi-window at task level, set it to
        // undefined and inherit from split stage.
        wct.setWindowingMode(task.token, WINDOWING_MODE_UNDEFINED)
        if (Transitions.ENABLE_SHELL_TRANSITIONS) {
        if (Transitions.ENABLE_SHELL_TRANSITIONS) {
            transitions.startTransition(TRANSIT_CHANGE, wct, null /* handler */)
            transitions.startTransition(TRANSIT_CHANGE, wct, null /* handler */)
        } else {
        } else {
@@ -462,6 +469,8 @@ class DesktopTasksController(
                splitScreenController.getStageOfTask(taskInfo.taskId),
                splitScreenController.getStageOfTask(taskInfo.taskId),
                EXIT_REASON_DESKTOP_MODE
                EXIT_REASON_DESKTOP_MODE
            )
            )
            splitScreenController.transitionHandler
                ?.onSplitToDesktop()
        }
        }
    }
    }


@@ -1044,9 +1053,11 @@ class DesktopTasksController(
        wct: WindowContainerTransaction,
        wct: WindowContainerTransaction,
        taskInfo: RunningTaskInfo
        taskInfo: RunningTaskInfo
    ) {
    ) {
        // Explicitly setting multi-window at task level interferes with animations.
        // This windowing mode is to get the transition animation started; once we complete
        // Let task inherit windowing mode once transition is complete instead.
        // split select, we will change windowing mode to undefined and inherit from split stage.
        wct.setWindowingMode(taskInfo.token, WINDOWING_MODE_UNDEFINED)
        // Going to undefined here causes task to flicker to the top left.
        // Cancelling the split select flow will revert it to fullscreen.
        wct.setWindowingMode(taskInfo.token, WINDOWING_MODE_MULTI_WINDOW)
        // The task's density may have been overridden in freeform; revert it here as we don't
        // The task's density may have been overridden in freeform; revert it here as we don't
        // want it overridden in multi-window.
        // want it overridden in multi-window.
        wct.setDensityDpi(taskInfo.token, getDefaultDensityDpi())
        wct.setDensityDpi(taskInfo.token, getDefaultDensityDpi())
+32 −4
Original line number Original line Diff line number Diff line
@@ -6,6 +6,7 @@ import android.animation.RectEvaluator
import android.animation.ValueAnimator
import android.animation.ValueAnimator
import android.app.ActivityOptions
import android.app.ActivityOptions
import android.app.ActivityOptions.SourceInfo
import android.app.ActivityOptions.SourceInfo
import android.app.ActivityTaskManager.INVALID_TASK_ID
import android.app.PendingIntent
import android.app.PendingIntent
import android.app.PendingIntent.FLAG_ALLOW_UNSAFE_IMPLICIT_INTENT
import android.app.PendingIntent.FLAG_ALLOW_UNSAFE_IMPLICIT_INTENT
import android.app.PendingIntent.FLAG_MUTABLE
import android.app.PendingIntent.FLAG_MUTABLE
@@ -26,6 +27,9 @@ import android.window.TransitionRequestInfo
import android.window.WindowContainerToken
import android.window.WindowContainerToken
import android.window.WindowContainerTransaction
import android.window.WindowContainerTransaction
import com.android.wm.shell.RootTaskDisplayAreaOrganizer
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.protolog.ShellProtoLogGroup
import com.android.wm.shell.protolog.ShellProtoLogGroup
import com.android.wm.shell.shared.TransitionUtil
import com.android.wm.shell.shared.TransitionUtil
import com.android.wm.shell.splitscreen.SplitScreenController
import com.android.wm.shell.splitscreen.SplitScreenController
@@ -68,7 +72,7 @@ class DragToDesktopTransitionHandler(
            .addCategory(Intent.CATEGORY_HOME)
            .addCategory(Intent.CATEGORY_HOME)


    private var dragToDesktopStateListener: DragToDesktopStateListener? = null
    private var dragToDesktopStateListener: DragToDesktopStateListener? = null
    private var splitScreenController: SplitScreenController? = null
    private lateinit var splitScreenController: SplitScreenController
    private var transitionState: TransitionState? = null
    private var transitionState: TransitionState? = null
    private lateinit var onTaskResizeAnimationListener: OnTaskResizeAnimationListener
    private lateinit var onTaskResizeAnimationListener: OnTaskResizeAnimationListener


@@ -76,6 +80,9 @@ class DragToDesktopTransitionHandler(
    val inProgress: Boolean
    val inProgress: Boolean
        get() = transitionState != null
        get() = transitionState != null


    /** The task id of the task currently being dragged from fullscreen/split. */
    val draggingTaskId: Int
        get() = transitionState?.draggedTaskId ?: INVALID_TASK_ID
    /** Sets a listener to receive callback about events during the transition animation. */
    /** Sets a listener to receive callback about events during the transition animation. */
    fun setDragToDesktopStateListener(listener: DragToDesktopStateListener) {
    fun setDragToDesktopStateListener(listener: DragToDesktopStateListener) {
        dragToDesktopStateListener = listener
        dragToDesktopStateListener = listener
@@ -130,10 +137,14 @@ class DragToDesktopTransitionHandler(
                .startTransition(TRANSIT_DESKTOP_MODE_START_DRAG_TO_DESKTOP, wct, this)
                .startTransition(TRANSIT_DESKTOP_MODE_START_DRAG_TO_DESKTOP, wct, this)


        transitionState = if (isSplitTask(taskId)) {
        transitionState = if (isSplitTask(taskId)) {
            val otherTask = getOtherSplitTask(taskId) ?: throw IllegalStateException(
                "Expected split task to have a counterpart."
            )
            TransitionState.FromSplit(
            TransitionState.FromSplit(
                    draggedTaskId = taskId,
                    draggedTaskId = taskId,
                    dragAnimator = dragToDesktopAnimator,
                    dragAnimator = dragToDesktopAnimator,
                    startTransitionToken = startTransitionToken
                    startTransitionToken = startTransitionToken,
                    otherSplitTask = otherTask
            )
            )
        } else {
        } else {
            TransitionState.FromFullscreen(
            TransitionState.FromFullscreen(
@@ -347,6 +358,12 @@ class DragToDesktopTransitionHandler(
                ?: error("Start transition expected to be waiting for merge but wasn't")
                ?: error("Start transition expected to be waiting for merge but wasn't")
        if (isEndTransition) {
        if (isEndTransition) {
            info.changes.withIndex().forEach { (i, change) ->
            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) {
                if (change.mode == TRANSIT_CLOSE) {
                    t.hide(change.leash)
                    t.hide(change.leash)
                    startTransactionFinishT.hide(change.leash)
                    startTransactionFinishT.hide(change.leash)
@@ -392,7 +409,6 @@ class DragToDesktopTransitionHandler(
            onTaskResizeAnimationListener.onAnimationStart(state.draggedTaskId, t,
            onTaskResizeAnimationListener.onAnimationStart(state.draggedTaskId, t,
                unscaledStartBounds)
                unscaledStartBounds)
            finishCallback.onTransitionFinished(null /* wct */)
            finishCallback.onTransitionFinished(null /* wct */)

            val tx: SurfaceControl.Transaction = transactionSupplier.get()
            val tx: SurfaceControl.Transaction = transactionSupplier.get()
            ValueAnimator.ofObject(rectEvaluator, unscaledStartBounds, endBounds)
            ValueAnimator.ofObject(rectEvaluator, unscaledStartBounds, endBounds)
                    .setDuration(DRAG_TO_DESKTOP_FINISH_ANIM_DURATION_MS)
                    .setDuration(DRAG_TO_DESKTOP_FINISH_ANIM_DURATION_MS)
@@ -549,7 +565,18 @@ class DragToDesktopTransitionHandler(
    }
    }


    private fun isSplitTask(taskId: Int): Boolean {
    private fun isSplitTask(taskId: Int): Boolean {
        return splitScreenController?.isTaskInSplitScreen(taskId) ?: false
        return splitScreenController.isTaskInSplitScreen(taskId)
    }

    private fun getOtherSplitTask(taskId: Int): Int? {
        val splitPos = splitScreenController.getSplitPosition(taskId)
        if (splitPos == SPLIT_POSITION_UNDEFINED) return null
        val otherTaskPos = if (splitPos == SPLIT_POSITION_BOTTOM_OR_RIGHT) {
            SPLIT_POSITION_TOP_OR_LEFT
        } else {
            SPLIT_POSITION_BOTTOM_OR_RIGHT
        }
        return splitScreenController.getTaskInfo(otherTaskPos)?.taskId
    }
    }


    private fun requireTransitionState(): TransitionState {
    private fun requireTransitionState(): TransitionState {
@@ -598,6 +625,7 @@ class DragToDesktopTransitionHandler(
                override var cancelled: Boolean = false,
                override var cancelled: Boolean = false,
                override var startAborted: Boolean = false,
                override var startAborted: Boolean = false,
                var splitRootChange: Change? = null,
                var splitRootChange: Change? = null,
                var otherSplitTask: Int
        ) : TransitionState()
        ) : TransitionState()
    }
    }


+5 −0
Original line number Original line Diff line number Diff line
@@ -3353,6 +3353,11 @@ public class StageCoordinator implements SplitLayout.SplitLayoutHandler,
                true /* reparentLeafTaskIfRelaunch */);
                true /* reparentLeafTaskIfRelaunch */);
    }
    }


    /** Call this when the animation from split screen to desktop is started. */
    public void onSplitToDesktop() {
        setSplitsVisible(false);
    }

    /** Call this when the recents animation finishes by doing pair-to-pair switch. */
    /** Call this when the recents animation finishes by doing pair-to-pair switch. */
    public void onRecentsPairToPairAnimationFinish(WindowContainerTransaction finishWct) {
    public void onRecentsPairToPairAnimationFinish(WindowContainerTransaction finishWct) {
        ProtoLog.d(WM_SHELL_SPLIT_SCREEN, "onRecentsPairToPairAnimationFinish");
        ProtoLog.d(WM_SHELL_SPLIT_SCREEN, "onRecentsPairToPairAnimationFinish");
+9 −3
Original line number Original line Diff line number Diff line
@@ -16,6 +16,7 @@


package com.android.wm.shell.windowdecor;
package com.android.wm.shell.windowdecor;


import static android.app.ActivityTaskManager.INVALID_TASK_ID;
import static android.app.WindowConfiguration.ACTIVITY_TYPE_STANDARD;
import static android.app.WindowConfiguration.ACTIVITY_TYPE_STANDARD;
import static android.app.WindowConfiguration.WINDOWING_MODE_FREEFORM;
import static android.app.WindowConfiguration.WINDOWING_MODE_FREEFORM;
import static android.app.WindowConfiguration.WINDOWING_MODE_FULLSCREEN;
import static android.app.WindowConfiguration.WINDOWING_MODE_FULLSCREEN;
@@ -34,6 +35,7 @@ import static com.android.wm.shell.common.split.SplitScreenConstants.SPLIT_POSIT
import static com.android.wm.shell.common.split.SplitScreenConstants.SPLIT_POSITION_TOP_OR_LEFT;
import static com.android.wm.shell.common.split.SplitScreenConstants.SPLIT_POSITION_TOP_OR_LEFT;
import static com.android.wm.shell.compatui.AppCompatUtils.isSingleTopActivityTranslucent;
import static com.android.wm.shell.compatui.AppCompatUtils.isSingleTopActivityTranslucent;
import static com.android.wm.shell.desktopmode.DesktopModeVisualIndicator.IndicatorType.TO_FULLSCREEN_INDICATOR;
import static com.android.wm.shell.desktopmode.DesktopModeVisualIndicator.IndicatorType.TO_FULLSCREEN_INDICATOR;
import static com.android.wm.shell.splitscreen.SplitScreen.STAGE_TYPE_UNDEFINED;


import android.annotation.NonNull;
import android.annotation.NonNull;
import android.app.ActivityManager;
import android.app.ActivityManager;
@@ -272,10 +274,9 @@ public class DesktopModeWindowDecorViewModel implements WindowDecorViewModel {
        mSplitScreenController.registerSplitScreenListener(new SplitScreen.SplitScreenListener() {
        mSplitScreenController.registerSplitScreenListener(new SplitScreen.SplitScreenListener() {
            @Override
            @Override
            public void onTaskStageChanged(int taskId, @StageType int stage, boolean visible) {
            public void onTaskStageChanged(int taskId, @StageType int stage, boolean visible) {
                if (visible) {
                if (visible && stage != STAGE_TYPE_UNDEFINED) {
                    DesktopModeWindowDecoration decor = mWindowDecorByTaskId.get(taskId);
                    DesktopModeWindowDecoration decor = mWindowDecorByTaskId.get(taskId);
                    if (decor != null && DesktopModeStatus.isEnabled()
                    if (decor != null && DesktopModeStatus.isEnabled()) {
                            && decor.mTaskInfo.getWindowingMode() == WINDOWING_MODE_FREEFORM) {
                        mDesktopTasksController.moveToSplit(decor.mTaskInfo);
                        mDesktopTasksController.moveToSplit(decor.mTaskInfo);
                    }
                    }
                }
                }
@@ -915,6 +916,11 @@ public class DesktopModeWindowDecorViewModel implements WindowDecorViewModel {


    @Nullable
    @Nullable
    private DesktopModeWindowDecoration getRelevantWindowDecor(MotionEvent ev) {
    private DesktopModeWindowDecoration getRelevantWindowDecor(MotionEvent ev) {
        // If we are mid-transition, dragged task's decor is always relevant.
        final int draggedTaskId = mDesktopTasksController.getDraggingTaskId();
        if (draggedTaskId != INVALID_TASK_ID) {
            return mWindowDecorByTaskId.get(draggedTaskId);
        }
        final DesktopModeWindowDecoration focusedDecor = getFocusedDecor();
        final DesktopModeWindowDecoration focusedDecor = getFocusedDecor();
        if (focusedDecor == null) {
        if (focusedDecor == null) {
            return null;
            return null;