Merge "Set position during TRANSIT_ENTER_DESKTOP_MODE transition" into udc-qpr-dev

This commit is contained in:
Maryam Dehaini
2023-07-06 21:57:20 +00:00
committed by Android (Google) Code Review
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);