diff options
| author | 2025-03-03 10:47:09 -0800 | |
|---|---|---|
| committer | 2025-03-03 11:55:43 -0800 | |
| commit | 6d93334ff2cfcf2e63d1e105d32237029b6d4af3 (patch) | |
| tree | 2777dabb829e0d075f58ef4c748f886bfb3daca0 /libs | |
| parent | fd356738abe400daebc08bdc151d36e177c4bc8d (diff) | |
Add task scale to the convert to bubble animation
When a task is dragged to a bubble, the task is scaled down.
Add the initial scale to the size change animation and include another
animator in the set to scale the task up from the drag scale to 1x
scale.
Apply the dragged task scale to the snapshot while it is not under the
task container and while the task is being moved to the bubble task view.
Pass along full task bounds along with a scale to the size change animation.
This way the size change animation will be animating from start bounds
with intial scale to end bounds.
Update to emphasized interpolator for the size change animation.
Bug: 396449031
Test: atest BubbleTransitionsTest
Test: manual, drag a task to bubble
Flag: com.android.wm.shell.enable_bubble_to_fullscreen
Change-Id: I192c97aebe2632e6519d75ea9739e0037ca5484b
Diffstat (limited to 'libs')
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 677c21c96f4b..aeec423cabb4 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 @@ -348,13 +348,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 d396d8bff2b8..e2a3aa3dc517 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 @@ -277,6 +277,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) } @@ -288,7 +289,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(), @@ -321,22 +321,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 |