summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
author Ats Jenk <atsjenk@google.com> 2025-03-04 00:02:54 -0800
committer Android (Google) Code Review <android-gerrit@google.com> 2025-03-04 00:02:54 -0800
commit1c413e4dbb280fc1f5b82d5b07d3425182001702 (patch)
tree3b6bdb3d2f24d998ece5666341e5365c371100b8
parentc8f5b457106caba889775478e7f3417c43e1e504 (diff)
parent6d93334ff2cfcf2e63d1e105d32237029b6d4af3 (diff)
Merge "Add task scale to the convert to bubble animation" into main
-rw-r--r--libs/WindowManager/Shell/src/com/android/wm/shell/animation/SizeChangeAnimation.java23
-rw-r--r--libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/BubbleTransitions.java65
-rw-r--r--libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/bar/BubbleBarAnimationHelper.java4
-rw-r--r--libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/bar/BubbleBarLayerView.java8
-rw-r--r--libs/WindowManager/Shell/src/com/android/wm/shell/desktopmode/DragToDesktopTransitionHandler.kt12
-rw-r--r--libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/bubbles/BubbleTransitionsTest.java36
6 files changed, 98 insertions, 50 deletions
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/animation/SizeChangeAnimation.java b/libs/WindowManager/Shell/src/com/android/wm/shell/animation/SizeChangeAnimation.java
index 5018fdb615da..8e78686ac13d 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/animation/SizeChangeAnimation.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/animation/SizeChangeAnimation.java
@@ -83,7 +83,11 @@ public class SizeChangeAnimation {
private static final int ANIMATION_RESOLUTION = 1000;
public SizeChangeAnimation(Rect startBounds, Rect endBounds) {
- mAnimation = buildContainerAnimation(startBounds, endBounds);
+ this(startBounds, endBounds, 1f);
+ }
+
+ public SizeChangeAnimation(Rect startBounds, Rect endBounds, float initialScale) {
+ mAnimation = buildContainerAnimation(startBounds, endBounds, initialScale);
mSnapshotAnim = buildSnapshotAnimation(startBounds, endBounds);
}
@@ -167,7 +171,8 @@ public class SizeChangeAnimation {
}
/** Animation for the whole container (snapshot is inside this container). */
- private static AnimationSet buildContainerAnimation(Rect startBounds, Rect endBounds) {
+ private static AnimationSet buildContainerAnimation(Rect startBounds, Rect endBounds,
+ float initialScale) {
final long duration = ANIMATION_RESOLUTION;
boolean growing = endBounds.width() - startBounds.width()
+ endBounds.height() - startBounds.height() >= 0;
@@ -180,15 +185,27 @@ public class SizeChangeAnimation {
final Animation scaleAnim = new ScaleAnimation(startScaleX, 1, startScaleY, 1);
scaleAnim.setDuration(scalePeriod);
+ long scaleStartOffset = 0;
if (!growing) {
- scaleAnim.setStartOffset(duration - scalePeriod);
+ scaleStartOffset = duration - scalePeriod;
}
+ scaleAnim.setStartOffset(scaleStartOffset);
animSet.addAnimation(scaleAnim);
+
+ if (initialScale != 1f) {
+ final Animation initialScaleAnim = new ScaleAnimation(initialScale, 1f, initialScale,
+ 1f);
+ initialScaleAnim.setDuration(scalePeriod);
+ initialScaleAnim.setStartOffset(scaleStartOffset);
+ animSet.addAnimation(initialScaleAnim);
+ }
+
final Animation translateAnim = new TranslateAnimation(startBounds.left,
endBounds.left, startBounds.top, endBounds.top);
translateAnim.setDuration(duration);
animSet.addAnimation(translateAnim);
Rect startClip = new Rect(startBounds);
+ startClip.scale(initialScale);
Rect endClip = new Rect(endBounds);
startClip.offsetTo(0, 0);
endClip.offsetTo(0, 0);
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/BubbleTransitions.java b/libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/BubbleTransitions.java
index 338ffe76e6ea..e7e7be9cdf6b 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/BubbleTransitions.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/BubbleTransitions.java
@@ -30,6 +30,7 @@ import android.annotation.Nullable;
import android.app.ActivityManager;
import android.app.TaskInfo;
import android.content.Context;
+import android.graphics.PointF;
import android.graphics.Rect;
import android.os.IBinder;
import android.util.Slog;
@@ -155,28 +156,23 @@ public class BubbleTransitions {
* Information about the task when it is being dragged to a bubble
*/
public static class DragData {
- private final Rect mBounds;
private final WindowContainerTransaction mPendingWct;
private final boolean mReleasedOnLeft;
+ private final float mTaskScale;
+ private final PointF mDragPosition;
/**
- * @param bounds bounds of the dragged task when the drag was released
- * @param wct pending operations to be applied when finishing the drag
* @param releasedOnLeft true if the bubble was released in the left drop target
+ * @param taskScale the scale of the task when it was dragged to bubble
+ * @param dragPosition the position of the task when it was dragged to bubble
+ * @param wct pending operations to be applied when finishing the drag
*/
- public DragData(@Nullable Rect bounds, @Nullable WindowContainerTransaction wct,
- boolean releasedOnLeft) {
- mBounds = bounds;
+ public DragData(boolean releasedOnLeft, float taskScale, @Nullable PointF dragPosition,
+ @Nullable WindowContainerTransaction wct) {
mPendingWct = wct;
mReleasedOnLeft = releasedOnLeft;
- }
-
- /**
- * @return bounds of the dragged task when the drag was released
- */
- @Nullable
- public Rect getBounds() {
- return mBounds;
+ mTaskScale = taskScale;
+ mDragPosition = dragPosition != null ? dragPosition : new PointF(0, 0);
}
/**
@@ -193,6 +189,20 @@ public class BubbleTransitions {
public boolean isReleasedOnLeft() {
return mReleasedOnLeft;
}
+
+ /**
+ * @return the scale of the task when it was dragged to bubble
+ */
+ public float getTaskScale() {
+ return mTaskScale;
+ }
+
+ /**
+ * @return position of the task when it was dragged to bubble
+ */
+ public PointF getDragPosition() {
+ return mDragPosition;
+ }
}
/**
@@ -347,21 +357,24 @@ public class BubbleTransitions {
}
mFinishCb = finishCallback;
- if (mDragData != null && mDragData.getBounds() != null) {
- // Override start bounds with the dragged task bounds
- mStartBounds.set(mDragData.getBounds());
+ if (mDragData != null) {
+ mStartBounds.offsetTo((int) mDragData.getDragPosition().x,
+ (int) mDragData.getDragPosition().y);
+ startTransaction.setScale(mSnapshot, mDragData.getTaskScale(),
+ mDragData.getTaskScale());
}
// Now update state (and talk to launcher) in parallel with snapshot stuff
mBubbleData.notificationEntryUpdated(mBubble, /* suppressFlyout= */ true,
/* showInShade= */ false);
+ final int left = mStartBounds.left - info.getRoot(0).getOffset().x;
+ final int top = mStartBounds.top - info.getRoot(0).getOffset().y;
+ startTransaction.setPosition(mTaskLeash, left, top);
startTransaction.show(mSnapshot);
// Move snapshot to root so that it remains visible while task is moved to taskview
startTransaction.reparent(mSnapshot, info.getRoot(0).getLeash());
- startTransaction.setPosition(mSnapshot,
- mStartBounds.left - info.getRoot(0).getOffset().x,
- mStartBounds.top - info.getRoot(0).getOffset().y);
+ startTransaction.setPosition(mSnapshot, left, top);
startTransaction.setLayer(mSnapshot, Integer.MAX_VALUE);
BubbleBarExpandedView bbev = mBubble.getBubbleBarExpandedView();
@@ -416,6 +429,8 @@ public class BubbleTransitions {
private void playAnimation(boolean animate) {
final TaskViewTaskController tv = mBubble.getTaskView().getController();
final SurfaceControl.Transaction startT = new SurfaceControl.Transaction();
+ // Set task position to 0,0 as it will be placed inside the TaskView
+ startT.setPosition(mTaskLeash, 0, 0);
mTaskViewTransitions.prepareOpenAnimation(tv, true /* new */, startT, mFinishT,
(ActivityManager.RunningTaskInfo) mTaskInfo, mTaskLeash, mFinishWct);
@@ -424,10 +439,12 @@ public class BubbleTransitions {
}
if (animate) {
- mLayerView.animateConvert(startT, mStartBounds, mSnapshot, mTaskLeash, () -> {
- mFinishCb.onTransitionFinished(mFinishWct);
- mFinishCb = null;
- });
+ float startScale = mDragData != null ? mDragData.getTaskScale() : 1f;
+ mLayerView.animateConvert(startT, mStartBounds, startScale, mSnapshot, mTaskLeash,
+ () -> {
+ mFinishCb.onTransitionFinished(mFinishWct);
+ mFinishCb = null;
+ });
} else {
startT.apply();
mFinishCb.onTransitionFinished(mFinishWct);
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/bar/BubbleBarAnimationHelper.java b/libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/bar/BubbleBarAnimationHelper.java
index 52f20646fb4a..fa22a3961002 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/bar/BubbleBarAnimationHelper.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/bar/BubbleBarAnimationHelper.java
@@ -580,6 +580,7 @@ public class BubbleBarAnimationHelper {
public void animateConvert(BubbleViewProvider expandedBubble,
@NonNull SurfaceControl.Transaction startT,
@NonNull Rect origBounds,
+ float origScale,
@NonNull SurfaceControl snapshot,
@NonNull SurfaceControl taskLeash,
@Nullable Runnable afterAnimation) {
@@ -599,7 +600,7 @@ public class BubbleBarAnimationHelper {
new SizeChangeAnimation(
new Rect(origBounds.left - position.x, origBounds.top - position.y,
origBounds.right - position.x, origBounds.bottom - position.y),
- new Rect(0, 0, size.getWidth(), size.getHeight()));
+ new Rect(0, 0, size.getWidth(), size.getHeight()), origScale);
sca.initialize(bbev, taskLeash, snapshot, startT);
Animator a = sca.buildViewAnimator(bbev, tvSf, snapshot, /* onFinish */ (va) -> {
@@ -614,6 +615,7 @@ public class BubbleBarAnimationHelper {
bbev.setSurfaceZOrderedOnTop(true);
a.setDuration(EXPANDED_VIEW_ANIMATE_TO_REST_DURATION);
+ a.setInterpolator(Interpolators.EMPHASIZED);
a.start();
}
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/bar/BubbleBarLayerView.java b/libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/bar/BubbleBarLayerView.java
index 99082f18492c..6c840f020f90 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/bar/BubbleBarLayerView.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/bar/BubbleBarLayerView.java
@@ -423,13 +423,13 @@ public class BubbleBarLayerView extends FrameLayout
* @param startT A transaction with first-frame work. this *will* be applied here!
*/
public void animateConvert(@NonNull SurfaceControl.Transaction startT,
- @NonNull Rect startBounds, @NonNull SurfaceControl snapshot, SurfaceControl taskLeash,
- Runnable animFinish) {
+ @NonNull Rect startBounds, float startScale, @NonNull SurfaceControl snapshot,
+ SurfaceControl taskLeash, Runnable animFinish) {
if (!mIsExpanded || mExpandedBubble == null) {
throw new IllegalStateException("Can't animateExpand without expanded state");
}
- mAnimationHelper.animateConvert(mExpandedBubble, startT, startBounds, snapshot, taskLeash,
- animFinish);
+ mAnimationHelper.animateConvert(mExpandedBubble, startT, startBounds, startScale, snapshot,
+ taskLeash, animFinish);
}
/**
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/desktopmode/DragToDesktopTransitionHandler.kt b/libs/WindowManager/Shell/src/com/android/wm/shell/desktopmode/DragToDesktopTransitionHandler.kt
index c8f7ea9f885f..e943c42dcdfc 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/desktopmode/DragToDesktopTransitionHandler.kt
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/desktopmode/DragToDesktopTransitionHandler.kt
@@ -281,6 +281,7 @@ sealed class DragToDesktopTransitionHandler(
val state = requireTransitionState()
val taskInfo = state.draggedTaskChange?.taskInfo ?: error("Expected non-null taskInfo")
val animatedTaskBounds = getAnimatedTaskBounds()
+ state.dragAnimator.cancelAnimator()
requestSplitSelect(wct, taskInfo, splitPosition, animatedTaskBounds)
}
@@ -292,7 +293,6 @@ sealed class DragToDesktopTransitionHandler(
val scaledWidth = taskBounds.width() * taskScale
val scaledHeight = taskBounds.height() * taskScale
val dragPosition = PointF(state.dragAnimator.position)
- state.dragAnimator.cancelAnimator()
return Rect(
dragPosition.x.toInt(),
dragPosition.y.toInt(),
@@ -325,22 +325,24 @@ sealed class DragToDesktopTransitionHandler(
// TODO(b/391928049): update density once we can drag from desktop to bubble
val state = requireTransitionState()
val taskInfo = state.draggedTaskChange?.taskInfo ?: error("Expected non-null taskInfo")
- val taskBounds = getAnimatedTaskBounds()
+ val dragPosition = PointF(state.dragAnimator.position)
+ val scale = state.dragAnimator.scale
state.dragAnimator.cancelAnimator()
- requestBubble(wct, taskInfo, onLeft, taskBounds)
+ requestBubble(wct, taskInfo, onLeft, scale, dragPosition)
}
private fun requestBubble(
wct: WindowContainerTransaction,
taskInfo: RunningTaskInfo,
onLeft: Boolean,
- taskBounds: Rect = Rect(taskInfo.configuration.windowConfiguration.bounds),
+ taskScale: Float = 1f,
+ dragPosition: PointF = PointF(0f, 0f),
) {
val controller =
bubbleController.orElseThrow { IllegalStateException("BubbleController not set") }
controller.expandStackAndSelectBubble(
taskInfo,
- BubbleTransitions.DragData(taskBounds, wct, onLeft),
+ BubbleTransitions.DragData(onLeft, taskScale, dragPosition, wct),
)
}
diff --git a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/bubbles/BubbleTransitionsTest.java b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/bubbles/BubbleTransitionsTest.java
index 42310caba1c6..3c79ea7be39f 100644
--- a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/bubbles/BubbleTransitionsTest.java
+++ b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/bubbles/BubbleTransitionsTest.java
@@ -25,6 +25,7 @@ import static org.junit.Assert.assertFalse;
import static org.junit.Assert.assertTrue;
import static org.mockito.ArgumentMatchers.any;
import static org.mockito.ArgumentMatchers.anyBoolean;
+import static org.mockito.ArgumentMatchers.anyFloat;
import static org.mockito.ArgumentMatchers.anyInt;
import static org.mockito.ArgumentMatchers.eq;
import static org.mockito.ArgumentMatchers.isNull;
@@ -35,6 +36,7 @@ import static org.mockito.Mockito.verify;
import static org.mockito.Mockito.when;
import android.app.ActivityManager;
+import android.graphics.PointF;
import android.graphics.Rect;
import android.os.IBinder;
import android.view.SurfaceControl;
@@ -138,13 +140,14 @@ public class BubbleTransitionsTest extends ShellTestCase {
return taskInfo;
}
- private TransitionInfo setupFullscreenTaskTransition(ActivityManager.RunningTaskInfo taskInfo) {
+ private TransitionInfo setupFullscreenTaskTransition(ActivityManager.RunningTaskInfo taskInfo,
+ SurfaceControl taskLeash, SurfaceControl snapshot) {
final TransitionInfo info = new TransitionInfo(TRANSIT_CONVERT_TO_BUBBLE, 0);
- final TransitionInfo.Change chg = new TransitionInfo.Change(taskInfo.token,
- mock(SurfaceControl.class));
+ final TransitionInfo.Change chg = new TransitionInfo.Change(taskInfo.token, taskLeash);
chg.setTaskInfo(taskInfo);
chg.setMode(TRANSIT_CHANGE);
chg.setStartAbsBounds(new Rect(0, 0, FULLSCREEN_TASK_WIDTH, FULLSCREEN_TASK_HEIGHT));
+ chg.setSnapshot(snapshot, /* luma= */ 0f);
info.addChange(chg);
info.addRoot(new TransitionInfo.Root(0, mock(SurfaceControl.class), 0, 0));
return info;
@@ -172,7 +175,9 @@ public class BubbleTransitionsTest extends ShellTestCase {
// Ensure we are communicating with the taskviewtransitions queue
assertTrue(mTaskViewTransitions.hasPending());
- final TransitionInfo info = setupFullscreenTaskTransition(taskInfo);
+ SurfaceControl taskLeash = new SurfaceControl.Builder().setName("taskLeash").build();
+ SurfaceControl snapshot = new SurfaceControl.Builder().setName("snapshot").build();
+ final TransitionInfo info = setupFullscreenTaskTransition(taskInfo, taskLeash, snapshot);
SurfaceControl.Transaction startT = mock(SurfaceControl.Transaction.class);
SurfaceControl.Transaction finishT = mock(SurfaceControl.Transaction.class);
final boolean[] finishCalled = new boolean[]{false};
@@ -183,7 +188,8 @@ public class BubbleTransitionsTest extends ShellTestCase {
ctb.startAnimation(ctb.mTransition, info, startT, finishT, finishCb);
assertFalse(mTaskViewTransitions.hasPending());
- verify(startT).setPosition(any(), eq(0f), eq(0f));
+ verify(startT).setPosition(taskLeash, 0, 0);
+ verify(startT).setPosition(snapshot, 0, 0);
verify(mBubbleData).notificationEntryUpdated(eq(mBubble), anyBoolean(), anyBoolean());
@@ -194,7 +200,7 @@ public class BubbleTransitionsTest extends ShellTestCase {
// Check that preparing transition is not reset before continueExpand is called
verify(mBubble, never()).setPreparingTransition(any());
ArgumentCaptor<Runnable> animCb = ArgumentCaptor.forClass(Runnable.class);
- verify(mLayerView).animateConvert(any(), any(), any(), any(), animCb.capture());
+ verify(mLayerView).animateConvert(any(), any(), anyFloat(), any(), any(), animCb.capture());
// continueExpand is now called, check that preparing transition is cleared
ctb.continueExpand();
@@ -209,14 +215,14 @@ public class BubbleTransitionsTest extends ShellTestCase {
public void testConvertToBubble_drag() {
ActivityManager.RunningTaskInfo taskInfo = setupBubble();
- Rect draggedTaskBounds = new Rect(10, 20, 30, 40);
WindowContainerTransaction pendingWct = new WindowContainerTransaction();
WindowContainerToken pendingDragOpToken = createMockToken();
pendingWct.reorder(pendingDragOpToken, /* onTop= */ false);
+ PointF dragPosition = new PointF(10f, 20f);
BubbleTransitions.DragData dragData = new BubbleTransitions.DragData(
- draggedTaskBounds, pendingWct, /* releasedOnLeft= */ false
- );
+ /* releasedOnLeft= */ false, /* taskScale= */ 0.5f, dragPosition,
+ pendingWct);
final BubbleTransitions.BubbleTransition bt = mBubbleTransitions.startConvertToBubble(
mBubble, taskInfo, mExpandedViewManager, mTaskViewFactory, mBubblePositioner,
@@ -234,15 +240,19 @@ public class BubbleTransitionsTest extends ShellTestCase {
== WindowContainerTransaction.HierarchyOp.HIERARCHY_OP_TYPE_REORDER
&& op.getContainer() == pendingDragOpToken.asBinder())).isTrue();
- final TransitionInfo info = setupFullscreenTaskTransition(taskInfo);
+ SurfaceControl taskLeash = new SurfaceControl.Builder().setName("taskLeash").build();
+ SurfaceControl snapshot = new SurfaceControl.Builder().setName("snapshot").build();
+ final TransitionInfo info = setupFullscreenTaskTransition(taskInfo, taskLeash, snapshot);
SurfaceControl.Transaction startT = mock(SurfaceControl.Transaction.class);
SurfaceControl.Transaction finishT = mock(SurfaceControl.Transaction.class);
Transitions.TransitionFinishCallback finishCb = wct -> {};
ctb.startAnimation(ctb.mTransition, info, startT, finishT, finishCb);
- // Verify that dragged task bounds are used for the position
- verify(startT).setPosition(any(), eq((float) draggedTaskBounds.left),
- eq((float) draggedTaskBounds.top));
+ // Verify that snapshot and task are placed at where the drag ended
+ verify(startT).setPosition(taskLeash, dragPosition.x, dragPosition.y);
+ verify(startT).setPosition(snapshot, dragPosition.x, dragPosition.y);
+ // Snapshot has the scale of the dragged task
+ verify(startT).setScale(snapshot, dragData.getTaskScale(), dragData.getTaskScale());
}
@Test