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

Commit ee7406fa authored by Maryam Dehaini's avatar Maryam Dehaini
Browse files

Set position during TRANSIT_ENTER_DESKTOP_MODE transition

Currently, relayout runs during the TRANSIT_ENTER_DESKTOP_MODE
transition and sets the surface's position in startT and finishT. This
change overrides that by calculating and setting the surface control's
position to the correct value.

We also introduced the MoveToDesktop animator since we previously had an
animator running at the same time as the enter desktop animation that
was also positioning the task which meant we had to pass in a lot of
information over to the transition handler. Now, we only pass in the
MoveToDesktop animator which holds all the information we need.

Test: Drag to freeform and check that there is no flash
Bug: 277780635
Change-Id: Iaed5191f9d56375c42c32e9afce3ffc54ca5f351
parent 6ee04ea7
Loading
Loading
Loading
Loading
+11 −6
Original line number Diff line number Diff line
@@ -63,6 +63,7 @@ import com.android.wm.shell.sysui.ShellSharedConstants
import com.android.wm.shell.transition.Transitions
import com.android.wm.shell.util.KtProtoLog
import com.android.wm.shell.windowdecor.DesktopModeWindowDecoration
import com.android.wm.shell.windowdecor.MoveToDesktopAnimator
import java.io.PrintWriter
import java.util.concurrent.Executor
import java.util.function.Consumer
@@ -204,7 +205,11 @@ class DesktopTasksController(
     * Moves a single task to freeform and sets the taskBounds to the passed in bounds,
     * startBounds
     */
    fun moveToFreeform(taskInfo: RunningTaskInfo, startBounds: Rect) {
    fun moveToFreeform(
            taskInfo: RunningTaskInfo,
            startBounds: Rect,
            dragToDesktopValueAnimator: MoveToDesktopAnimator
    ) {
        KtProtoLog.v(
            WM_SHELL_DESKTOP_MODE,
            "DesktopTasksController: moveToFreeform with bounds taskId=%d",
@@ -216,8 +221,8 @@ class DesktopTasksController(
        wct.setBounds(taskInfo.token, startBounds)

        if (Transitions.ENABLE_SHELL_TRANSITIONS) {
            enterDesktopTaskTransitionHandler.startTransition(
                    Transitions.TRANSIT_ENTER_FREEFORM, wct, mOnAnimationFinishedCallback)
            enterDesktopTaskTransitionHandler.startMoveToFreeformAnimation(wct,
                    dragToDesktopValueAnimator, mOnAnimationFinishedCallback)
        } else {
            shellTaskOrganizer.applyTransaction(wct)
        }
@@ -270,7 +275,7 @@ class DesktopTasksController(
     * Move a task to fullscreen after being dragged from fullscreen and released back into
     * status bar area
     */
    fun cancelMoveToFreeform(task: RunningTaskInfo, position: Point) {
    fun cancelMoveToFreeform(task: RunningTaskInfo, moveToDesktopAnimator: MoveToDesktopAnimator) {
        KtProtoLog.v(
            WM_SHELL_DESKTOP_MODE,
            "DesktopTasksController: cancelMoveToFreeform taskId=%d",
@@ -280,8 +285,8 @@ class DesktopTasksController(
        wct.setBounds(task.token, null)

        if (Transitions.ENABLE_SHELL_TRANSITIONS) {
            enterDesktopTaskTransitionHandler.startCancelMoveToDesktopMode(
                wct, position) { t ->
            enterDesktopTaskTransitionHandler.startCancelMoveToDesktopMode(wct,
                    moveToDesktopAnimator) { t ->
                val callbackWCT = WindowContainerTransaction()
                visualIndicator?.releaseVisualIndicator(t)
                visualIndicator = null
+61 −12
Original line number Diff line number Diff line
@@ -22,9 +22,10 @@ import android.animation.Animator;
import android.animation.AnimatorListenerAdapter;
import android.animation.ValueAnimator;
import android.app.ActivityManager;
import android.graphics.Point;
import android.graphics.PointF;
import android.graphics.Rect;
import android.os.IBinder;
import android.util.Slog;
import android.view.SurfaceControl;
import android.view.WindowManager;
import android.window.TransitionInfo;
@@ -35,6 +36,7 @@ import androidx.annotation.NonNull;
import androidx.annotation.Nullable;

import com.android.wm.shell.transition.Transitions;
import com.android.wm.shell.windowdecor.MoveToDesktopAnimator;

import java.util.ArrayList;
import java.util.List;
@@ -47,18 +49,17 @@ import java.util.function.Supplier;
 */
public class EnterDesktopTaskTransitionHandler implements Transitions.TransitionHandler {

    private static final String TAG = "EnterDesktopTaskTransitionHandler";
    private final Transitions mTransitions;
    private final Supplier<SurfaceControl.Transaction> mTransactionSupplier;

    // The size of the screen during drag relative to the fullscreen size
    public static final float DRAG_FREEFORM_SCALE = 0.4f;
    // The size of the screen after drag relative to the fullscreen size
    public static final float FINAL_FREEFORM_SCALE = 0.6f;
    public static final int FREEFORM_ANIMATION_DURATION = 336;

    private final List<IBinder> mPendingTransitionTokens = new ArrayList<>();
    private Point mPosition;
    private Consumer<SurfaceControl.Transaction> mOnAnimationFinishedCallback;
    private MoveToDesktopAnimator mMoveToDesktopAnimator;

    public EnterDesktopTaskTransitionHandler(
            Transitions transitions) {
@@ -86,16 +87,31 @@ public class EnterDesktopTaskTransitionHandler implements Transitions.Transition
        mPendingTransitionTokens.add(token);
    }

    /**
     * Starts Transition of type TRANSIT_ENTER_FREEFORM
     * @param wct WindowContainerTransaction for transition
     * @param moveToDesktopAnimator Animator that shrinks and positions task during two part move
     *                              to desktop animation
     * @param onAnimationEndCallback to be called after animation
     */
    public void startMoveToFreeformAnimation(@NonNull WindowContainerTransaction wct,
            @NonNull MoveToDesktopAnimator moveToDesktopAnimator,
            Consumer<SurfaceControl.Transaction> onAnimationEndCallback) {
        mMoveToDesktopAnimator = moveToDesktopAnimator;
        startTransition(Transitions.TRANSIT_ENTER_FREEFORM, wct, onAnimationEndCallback);
    }

    /**
     * Starts Transition of type TRANSIT_CANCEL_ENTERING_DESKTOP_MODE
     * @param wct WindowContainerTransaction for transition
     * @param position Position of task when transition is triggered
     * @param moveToDesktopAnimator Animator that shrinks and positions task during two part move
     *                              to desktop animation
     * @param onAnimationEndCallback to be called after animation
     */
    public void startCancelMoveToDesktopMode(@NonNull WindowContainerTransaction wct,
            Point position,
            MoveToDesktopAnimator moveToDesktopAnimator,
            Consumer<SurfaceControl.Transaction> onAnimationEndCallback) {
        mPosition = position;
        mMoveToDesktopAnimator = moveToDesktopAnimator;
        startTransition(Transitions.TRANSIT_CANCEL_ENTERING_DESKTOP_MODE, wct,
                onAnimationEndCallback);
    }
@@ -145,9 +161,23 @@ public class EnterDesktopTaskTransitionHandler implements Transitions.Transition
            // to null and we don't require an animation
            final SurfaceControl sc = change.getLeash();
            startT.setWindowCrop(sc, null);

            if (mMoveToDesktopAnimator == null
                    || mMoveToDesktopAnimator.getTaskId() != change.getTaskInfo().taskId) {
                Slog.e(TAG, "No animator available for this transition");
                return false;
            }

            // Calculate and set position of the task
            final PointF position = mMoveToDesktopAnimator.getPosition();
            startT.setPosition(sc, position.x, position.y);
            finishT.setPosition(sc, position.x, position.y);

            startT.apply();

            mTransitions.getMainExecutor().execute(
                    () -> finishCallback.onTransitionFinished(null, null));

            return true;
        }

@@ -162,12 +192,18 @@ public class EnterDesktopTaskTransitionHandler implements Transitions.Transition
                    endBounds.height());
            startT.apply();

            // End the animation that shrinks the window when task is first dragged from fullscreen
            if (mMoveToDesktopAnimator != null) {
                mMoveToDesktopAnimator.endAnimator();
            }

            // We want to find the scale of the current bounds relative to the end bounds. The
            // task is currently scaled to DRAG_FREEFORM_SCALE and the final bounds will be
            // scaled to FINAL_FREEFORM_SCALE. So, it is scaled to
            // DRAG_FREEFORM_SCALE / FINAL_FREEFORM_SCALE relative to the freeform bounds
            final ValueAnimator animator =
                    ValueAnimator.ofFloat(DRAG_FREEFORM_SCALE / FINAL_FREEFORM_SCALE, 1f);
                    ValueAnimator.ofFloat(
                            MoveToDesktopAnimator.DRAG_FREEFORM_SCALE / FINAL_FREEFORM_SCALE, 1f);
            animator.setDuration(FREEFORM_ANIMATION_DURATION);
            final SurfaceControl.Transaction t = mTransactionSupplier.get();
            animator.addUpdateListener(animation -> {
@@ -199,8 +235,7 @@ public class EnterDesktopTaskTransitionHandler implements Transitions.Transition
        }

        if (type == Transitions.TRANSIT_CANCEL_ENTERING_DESKTOP_MODE
                && taskInfo.getWindowingMode() == WINDOWING_MODE_FREEFORM
                && mPosition != null) {
                && taskInfo.getWindowingMode() == WINDOWING_MODE_FREEFORM) {
            // This Transition animates a task to fullscreen after being dragged from the status
            // bar and then released back into the status bar area
            final SurfaceControl sc = change.getLeash();
@@ -210,13 +245,27 @@ public class EnterDesktopTaskTransitionHandler implements Transitions.Transition
                    .setWindowCrop(sc, endBounds.width(), endBounds.height())
                    .apply();

            if (mMoveToDesktopAnimator == null
                    || mMoveToDesktopAnimator.getTaskId() != change.getTaskInfo().taskId) {
                Slog.e(TAG, "No animator available for this transition");
                return false;
            }

            // End the animation that shrinks the window when task is first dragged from fullscreen
            mMoveToDesktopAnimator.endAnimator();

            final ValueAnimator animator = new ValueAnimator();
            animator.setFloatValues(DRAG_FREEFORM_SCALE, 1f);
            animator.setFloatValues(MoveToDesktopAnimator.DRAG_FREEFORM_SCALE, 1f);
            animator.setDuration(FREEFORM_ANIMATION_DURATION);
            final SurfaceControl.Transaction t = mTransactionSupplier.get();

            // Get position of the task
            final float x = mMoveToDesktopAnimator.getPosition().x;
            final float y = mMoveToDesktopAnimator.getPosition().y;

            animator.addUpdateListener(animation -> {
                final float scale = (float) animation.getAnimatedValue();
                t.setPosition(sc, mPosition.x * (1 - scale), mPosition.y * (1 - scale))
                t.setPosition(sc, x * (1 - scale), y * (1 - scale))
                        .setScale(sc, scale, scale)
                        .show(sc)
                        .apply();
+18 −36
Original line number Diff line number Diff line
@@ -24,9 +24,9 @@ import static android.app.WindowConfiguration.WINDOWING_MODE_PINNED;

import static com.android.wm.shell.common.split.SplitScreenConstants.SPLIT_POSITION_BOTTOM_OR_RIGHT;
import static com.android.wm.shell.common.split.SplitScreenConstants.SPLIT_POSITION_TOP_OR_LEFT;
import static com.android.wm.shell.desktopmode.EnterDesktopTaskTransitionHandler.DRAG_FREEFORM_SCALE;
import static com.android.wm.shell.desktopmode.EnterDesktopTaskTransitionHandler.FINAL_FREEFORM_SCALE;
import static com.android.wm.shell.desktopmode.EnterDesktopTaskTransitionHandler.FREEFORM_ANIMATION_DURATION;
import static com.android.wm.shell.windowdecor.MoveToDesktopAnimator.DRAG_FREEFORM_SCALE;

import android.animation.Animator;
import android.animation.AnimatorListenerAdapter;
@@ -112,9 +112,8 @@ public class DesktopModeWindowDecorViewModel implements WindowDecorViewModel {

    private SplitScreenController mSplitScreenController;

    private ValueAnimator mDragToDesktopValueAnimator;
    private MoveToDesktopAnimator mMoveToDesktopAnimator;
    private final Rect mDragToDesktopAnimationStartBounds = new Rect();
    private boolean mDragToDesktopAnimationStarted;

    public DesktopModeWindowDecorViewModel(
            Context context,
@@ -233,7 +232,6 @@ public class DesktopModeWindowDecorViewModel implements WindowDecorViewModel {
            removeTaskFromEventReceiver(oldTaskInfo.displayId);
            incrementEventReceiverTasks(taskInfo.displayId);
        }

        decoration.relayout(taskInfo);
    }

@@ -599,7 +597,7 @@ public class DesktopModeWindowDecorViewModel implements WindowDecorViewModel {
            }
            case MotionEvent.ACTION_UP: {
                if (relevantDecor == null) {
                    mDragToDesktopAnimationStarted = false;
                    mMoveToDesktopAnimator = null;
                    mTransitionDragActive = false;
                    return;
                }
@@ -613,14 +611,14 @@ public class DesktopModeWindowDecorViewModel implements WindowDecorViewModel {
                        } else if (DesktopModeStatus.isProto1Enabled()) {
                            mDesktopModeController.ifPresent(c -> c.setDesktopModeActive(true));
                        }
                        mDragToDesktopAnimationStarted = false;
                        mMoveToDesktopAnimator = null;
                        return;
                    } else if (mDragToDesktopAnimationStarted) {
                        Point position = new Point((int) ev.getX(), (int) ev.getY());
                    } else if (mMoveToDesktopAnimator != null) {
                        relevantDecor.incrementRelayoutBlock();
                        mDesktopTasksController.ifPresent(
                                c -> c.cancelMoveToFreeform(relevantDecor.mTaskInfo, position));
                        mDragToDesktopAnimationStarted = false;
                                c -> c.cancelMoveToFreeform(relevantDecor.mTaskInfo,
                                        mMoveToDesktopAnimator));
                        mMoveToDesktopAnimator = null;
                        return;
                    }
                }
@@ -640,21 +638,19 @@ public class DesktopModeWindowDecorViewModel implements WindowDecorViewModel {
                    final int statusBarHeight = getStatusBarHeight(
                            relevantDecor.mTaskInfo.displayId);
                    if (ev.getY() > statusBarHeight) {
                        if (!mDragToDesktopAnimationStarted) {
                            mDragToDesktopAnimationStarted = true;
                        if (mMoveToDesktopAnimator == null) {
                            mMoveToDesktopAnimator = new MoveToDesktopAnimator(
                                    mDragToDesktopAnimationStartBounds, relevantDecor.mTaskInfo,
                                    relevantDecor.mTaskSurface);
                            mDesktopTasksController.ifPresent(
                                    c -> c.moveToFreeform(relevantDecor.mTaskInfo,
                                            mDragToDesktopAnimationStartBounds));
                            startAnimation(relevantDecor);
                                            mDragToDesktopAnimationStartBounds,
                                            mMoveToDesktopAnimator));
                            mMoveToDesktopAnimator.startAnimation();
                        }
                    }
                    if (mDragToDesktopAnimationStarted) {
                        Transaction t = mTransactionFactory.get();
                        float width = (float) mDragToDesktopValueAnimator.getAnimatedValue()
                                * mDragToDesktopAnimationStartBounds.width();
                        float x = ev.getX() - (width / 2);
                        t.setPosition(relevantDecor.mTaskSurface, x, ev.getY());
                        t.apply();
                    if (mMoveToDesktopAnimator != null) {
                        mMoveToDesktopAnimator.updatePosition(ev);
                    }
                }
                break;
@@ -662,7 +658,7 @@ public class DesktopModeWindowDecorViewModel implements WindowDecorViewModel {

            case MotionEvent.ACTION_CANCEL: {
                mTransitionDragActive = false;
                mDragToDesktopAnimationStarted = false;
                mMoveToDesktopAnimator = null;
            }
        }
    }
@@ -729,20 +725,6 @@ public class DesktopModeWindowDecorViewModel implements WindowDecorViewModel {
        animator.start();
    }

    private void startAnimation(@NonNull DesktopModeWindowDecoration focusedDecor) {
        mDragToDesktopValueAnimator = ValueAnimator.ofFloat(1f, DRAG_FREEFORM_SCALE);
        mDragToDesktopValueAnimator.setDuration(FREEFORM_ANIMATION_DURATION);
        final Transaction t = mTransactionFactory.get();
        mDragToDesktopValueAnimator.addUpdateListener(animation -> {
            final float animatorValue = (float) animation.getAnimatedValue();
            SurfaceControl sc = focusedDecor.mTaskSurface;
            t.setScale(sc, animatorValue, animatorValue);
            t.apply();
        });

        mDragToDesktopValueAnimator.start();
    }

    @Nullable
    private DesktopModeWindowDecoration getRelevantWindowDecor(MotionEvent ev) {
        if (mSplitScreenController != null && mSplitScreenController.isSplitScreenVisible()) {
+72 −0
Original line number Diff line number Diff line
package com.android.wm.shell.windowdecor

import android.animation.ValueAnimator
import android.app.ActivityManager.RunningTaskInfo
import android.graphics.PointF
import android.graphics.Rect
import android.view.MotionEvent
import android.view.SurfaceControl

/**
 * Creates an animator to shrink and position task after a user drags a fullscreen task from
 * the top of the screen to transition it into freeform and before the user releases the task. The
 * MoveToDesktopAnimator object also holds information about the state of the task that are
 * accessed by the EnterDesktopTaskTransitionHandler.
 */
class MoveToDesktopAnimator @JvmOverloads constructor(
        private val startBounds: Rect,
        private val taskInfo: RunningTaskInfo,
        private val taskSurface: SurfaceControl,
        private val transactionFactory: () -> SurfaceControl.Transaction =
                SurfaceControl::Transaction
) {
    companion object {
        // The size of the screen during drag relative to the fullscreen size
        const val DRAG_FREEFORM_SCALE: Float = 0.4f
        const val ANIMATION_DURATION = 336
    }

    private val animatedTaskWidth
        get() = dragToDesktopAnimator.animatedValue as Float * startBounds.width()
    private val dragToDesktopAnimator: ValueAnimator = ValueAnimator.ofFloat(1f,
            DRAG_FREEFORM_SCALE)
            .setDuration(ANIMATION_DURATION.toLong())
            .apply {
                val t = SurfaceControl.Transaction()
                addUpdateListener { animation ->
                    val animatorValue = animation.animatedValue as Float
                    t.setScale(taskSurface, animatorValue, animatorValue)
                            .apply()
                }
            }

    val taskId get() = taskInfo.taskId
    val position: PointF = PointF(0.0f, 0.0f)

    /**
     * Starts the animation that scales the task down.
     */
    fun startAnimation() {
        dragToDesktopAnimator.start()
    }

    /**
     * Uses the position of the motion event and the current scale of the task as defined by the
     * ValueAnimator to update the local position variable and set the task surface's position
     */
    fun updatePosition(ev: MotionEvent) {
        position.x = ev.x - animatedTaskWidth / 2
        position.y = ev.y

        val t = transactionFactory()
        t.setPosition(taskSurface, position.x, position.y)
        t.apply()
    }

    /**
     * Ends the animation, setting the scale and position to the final animation value
     */
    fun endAnimator() {
        dragToDesktopAnimator.end()
    }
}
 No newline at end of file
+13 −3
Original line number Diff line number Diff line
@@ -28,6 +28,7 @@ import static org.mockito.Mockito.verify;
import android.annotation.NonNull;
import android.app.ActivityManager;
import android.app.WindowConfiguration;
import android.graphics.PointF;
import android.graphics.Rect;
import android.os.IBinder;
import android.view.SurfaceControl;
@@ -41,6 +42,7 @@ import androidx.test.filters.SmallTest;

import com.android.wm.shell.common.ShellExecutor;
import com.android.wm.shell.transition.Transitions;
import com.android.wm.shell.windowdecor.MoveToDesktopAnimator;

import junit.framework.AssertionFailedError;

@@ -73,6 +75,10 @@ public class EnterDesktopTaskTransitionHandlerTest {
    ShellExecutor mExecutor;
    @Mock
    SurfaceControl mSurfaceControl;
    @Mock
    MoveToDesktopAnimator mMoveToDesktopAnimator;
    @Mock
    PointF mPosition;

    private EnterDesktopTaskTransitionHandler mEnterDesktopTaskTransitionHandler;

@@ -82,6 +88,7 @@ public class EnterDesktopTaskTransitionHandlerTest {

        doReturn(mExecutor).when(mTransitions).getMainExecutor();
        doReturn(mAnimationT).when(mTransactionFactory).get();
        doReturn(mPosition).when(mMoveToDesktopAnimator).getPosition();

        mEnterDesktopTaskTransitionHandler = new EnterDesktopTaskTransitionHandler(mTransitions,
                mTransactionFactory);
@@ -89,12 +96,15 @@ public class EnterDesktopTaskTransitionHandlerTest {

    @Test
    public void testEnterFreeformAnimation() {
        final int transitionType = Transitions.TRANSIT_ENTER_FREEFORM;
        final int taskId = 1;
        WindowContainerTransaction wct = new WindowContainerTransaction();
        doReturn(mToken).when(mTransitions)
                .startTransition(transitionType, wct, mEnterDesktopTaskTransitionHandler);
        mEnterDesktopTaskTransitionHandler.startTransition(transitionType, wct, null);
                .startTransition(Transitions.TRANSIT_ENTER_FREEFORM, wct,
                        mEnterDesktopTaskTransitionHandler);
        doReturn(taskId).when(mMoveToDesktopAnimator).getTaskId();

        mEnterDesktopTaskTransitionHandler.startMoveToFreeformAnimation(wct,
                mMoveToDesktopAnimator, null);

        TransitionInfo.Change change =
                createChange(WindowManager.TRANSIT_CHANGE, taskId, WINDOWING_MODE_FREEFORM);