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
This commit is contained in:
Maryam Dehaini
2023-06-13 13:22:09 -07:00
parent 6ee04ea76f
commit ee7406faea
5 changed files with 175 additions and 57 deletions

View File

@@ -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

View File

@@ -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();

View File

@@ -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()) {

View File

@@ -0,0 +1,72 @@
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()
}
}

View File

@@ -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);