Loading packages/SystemUI/src/com/android/systemui/bubbles/BubbleStackView.java +28 −19 Original line number Diff line number Diff line Loading @@ -330,9 +330,7 @@ public class BubbleStackView extends FrameLayout { mSurfaceSynchronizer = synchronizer != null ? synchronizer : DEFAULT_SURFACE_SYNCHRONIZER; mBubbleContainer = new PhysicsAnimationLayout(context); mBubbleContainer.setMaxRenderedChildren( getResources().getInteger(R.integer.bubbles_max_rendered)); mBubbleContainer.setController(mStackAnimationController); mBubbleContainer.setActiveController(mStackAnimationController); mBubbleContainer.setElevation(elevation); mBubbleContainer.setClipChildren(false); addView(mBubbleContainer, new FrameLayout.LayoutParams(MATCH_PARENT, MATCH_PARENT)); Loading Loading @@ -728,7 +726,7 @@ public class BubbleStackView extends FrameLayout { public void updateBubbleOrder(List<Bubble> bubbles) { for (int i = 0; i < bubbles.size(); i++) { Bubble bubble = bubbles.get(i); mBubbleContainer.moveViewTo(bubble.iconView, i); mBubbleContainer.reorderView(bubble.iconView, i); } } Loading Loading @@ -920,19 +918,17 @@ public class BubbleStackView extends FrameLayout { }; if (shouldExpand) { mBubbleContainer.setController(mExpandedAnimationController); mExpandedAnimationController.expandFromStack( mStackAnimationController.getStackPositionAlongNearestHorizontalEdge() /* collapseTo */, () -> { mBubbleContainer.setActiveController(mExpandedAnimationController); mExpandedAnimationController.expandFromStack(() -> { updatePointerPosition(); updateAfter.run(); } /* after */); } else { mBubbleContainer.cancelAllAnimations(); mExpandedAnimationController.collapseBackToStack( mStackAnimationController.getStackPositionAlongNearestHorizontalEdge(), () -> { mBubbleContainer.setController(mStackAnimationController); mBubbleContainer.setActiveController(mStackAnimationController); updateAfter.run(); }); } Loading Loading @@ -1021,7 +1017,7 @@ public class BubbleStackView extends FrameLayout { } mStackAnimationController.cancelStackPositionAnimations(); mBubbleContainer.setController(mStackAnimationController); mBubbleContainer.setActiveController(mStackAnimationController); hideFlyoutImmediate(); mDraggingInDismissTarget = false; Loading Loading @@ -1119,6 +1115,10 @@ public class BubbleStackView extends FrameLayout { /** Called when a gesture is completed or cancelled. */ void onGestureFinished() { mIsGestureInProgress = false; if (mIsExpanded) { mExpandedAnimationController.onGestureFinished(); } } /** Prepares and starts the desaturate/darken animation on the bubble stack. */ Loading Loading @@ -1209,6 +1209,7 @@ public class BubbleStackView extends FrameLayout { */ void magnetToStackIfNeededThenAnimateDismissal( View touchedView, float velX, float velY, Runnable after) { final View draggedOutBubble = mExpandedAnimationController.getDraggedOutBubble(); final Runnable animateDismissal = () -> { mAfterMagnet = null; Loading @@ -1226,7 +1227,7 @@ public class BubbleStackView extends FrameLayout { resetDesaturationAndDarken(); }); } else { mExpandedAnimationController.dismissDraggedOutBubble(() -> { mExpandedAnimationController.dismissDraggedOutBubble(draggedOutBubble, () -> { mAnimatingMagnet = false; mShowingDismiss = false; mDraggingInDismissTarget = false; Loading Loading @@ -1393,10 +1394,18 @@ public class BubbleStackView extends FrameLayout { }; // Post in case layout isn't complete and getWidth returns 0. post(() -> mFlyout.showFlyout( post(() -> { // An auto-expanding bubble could have been posted during the time it takes to // layout. if (isExpanded()) { return; } mFlyout.showFlyout( updateMessage, mStackAnimationController.getStackPosition(), getWidth(), mStackAnimationController.isStackOnLeftSide(), bubble.iconView.getBadgeColor(), mAfterFlyoutHides)); bubble.iconView.getBadgeColor(), mAfterFlyoutHides); }); } mFlyout.removeCallbacks(mHideFlyout); Loading packages/SystemUI/src/com/android/systemui/bubbles/animation/ExpandedAnimationController.java +98 −74 Original line number Diff line number Diff line Loading @@ -22,6 +22,7 @@ import android.graphics.PointF; import android.view.View; import android.view.WindowInsets; import androidx.annotation.Nullable; import androidx.dynamicanimation.animation.DynamicAnimation; import androidx.dynamicanimation.animation.SpringForce; Loading Loading @@ -63,12 +64,16 @@ public class ExpandedAnimationController private Point mDisplaySize; /** Size of dismiss target at bottom of screen. */ private float mPipDismissHeight; /** Max number of bubbles shown in row above expanded view.*/ private int mBubblesMaxRendered; /** Whether the dragged-out bubble is in the dismiss target. */ private boolean mIndividualBubbleWithinDismissTarget = false; private boolean mAnimatingExpand = false; private boolean mAnimatingCollapse = false; private Runnable mAfterExpand; private Runnable mAfterCollapse; private PointF mCollapsePoint; /** * Whether the dragged out bubble is springing towards the touch point, rather than using the * default behavior of moving directly to the touch point. Loading Loading @@ -97,56 +102,60 @@ public class ExpandedAnimationController private View mBubbleDraggingOut; /** * Drag velocities for the dragging-out bubble when the drag finished. These are used by * {@link #onChildRemoved} to animate out the bubble while respecting touch velocity. * Animates expanding the bubbles into a row along the top of the screen. */ private float mBubbleDraggingOutVelX; private float mBubbleDraggingOutVelY; public void expandFromStack(Runnable after) { mAnimatingCollapse = false; mAnimatingExpand = true; mAfterExpand = after; @Override protected void setLayout(PhysicsAnimationLayout layout) { super.setLayout(layout); startOrUpdateExpandAnimation(); } final Resources res = layout.getResources(); mStackOffsetPx = res.getDimensionPixelSize(R.dimen.bubble_stack_offset); mBubblePaddingPx = res.getDimensionPixelSize(R.dimen.bubble_padding); mBubbleSizePx = res.getDimensionPixelSize(R.dimen.individual_bubble_size); mStatusBarHeight = res.getDimensionPixelSize(com.android.internal.R.dimen.status_bar_height); mPipDismissHeight = res.getDimensionPixelSize(R.dimen.pip_dismiss_gradient_height); mBubblesMaxRendered = res.getInteger(R.integer.bubbles_max_rendered); /** Animate collapsing the bubbles back to their stacked position. */ public void collapseBackToStack(PointF collapsePoint, Runnable after) { mAnimatingExpand = false; mAnimatingCollapse = true; mAfterCollapse = after; mCollapsePoint = collapsePoint; startOrUpdateCollapseAnimation(); } /** * Animates expanding the bubbles into a row along the top of the screen. */ public void expandFromStack(PointF collapseTo, Runnable after) { private void startOrUpdateExpandAnimation() { animationsForChildrenFromIndex( 0, /* startIndex */ new ChildAnimationConfigurator() { @Override public void configureAnimationForChildAtIndex( int index, PhysicsAnimationLayout.PhysicsPropertyAnimator animation) { animation.position(getBubbleLeft(index), getExpandedY()); (index, animation) -> animation.position(getBubbleLeft(index), getExpandedY())) .startAll(() -> { mAnimatingExpand = false; if (mAfterExpand != null) { mAfterExpand.run(); } }) .startAll(after); mCollapseToPoint = collapseTo; mAfterExpand = null; }); } /** Animate collapsing the bubbles back to their stacked position. */ public void collapseBackToStack(Runnable after) { private void startOrUpdateCollapseAnimation() { // Stack to the left if we're going to the left, or right if not. final float sideMultiplier = mLayout.isFirstChildXLeftOfCenter(mCollapseToPoint.x) ? -1 : 1; final float sideMultiplier = mLayout.isFirstChildXLeftOfCenter(mCollapsePoint.x) ? -1 : 1; animationsForChildrenFromIndex( 0, /* startIndex */ (index, animation) -> (index, animation) -> { animation.position( mCollapseToPoint.x + (sideMultiplier * index * mStackOffsetPx), mCollapseToPoint.y)) .startAll(after /* endAction */); mCollapsePoint.x + (sideMultiplier * index * mStackOffsetPx), mCollapsePoint.y); }) .startAll(() -> { mAnimatingCollapse = false; if (mAfterCollapse != null) { mAfterCollapse.run(); } mAfterCollapse = null; }); } /** Prepares the given bubble to be dragged out. */ Loading Loading @@ -190,10 +199,10 @@ public class ExpandedAnimationController } /** Plays a dismiss animation on the dragged out bubble. */ public void dismissDraggedOutBubble(Runnable after) { public void dismissDraggedOutBubble(View bubble, Runnable after) { mIndividualBubbleWithinDismissTarget = false; animationForChild(mBubbleDraggingOut) animationForChild(bubble) .withStiffness(SpringForce.STIFFNESS_HIGH) .scaleX(1.1f) .scaleY(1.1f) Loading @@ -203,6 +212,10 @@ public class ExpandedAnimationController updateBubblePositions(); } @Nullable public View getDraggedOutBubble() { return mBubbleDraggingOut; } /** Magnets the given bubble to the dismiss target. */ public void magnetBubbleToDismiss( View bubbleView, float velX, float velY, float destY, Runnable after) { Loading Loading @@ -245,20 +258,13 @@ public class ExpandedAnimationController .withPositionStartVelocities(velX, velY) .start(() -> bubbleView.setTranslationZ(0f) /* after */); mBubbleDraggingOut = null; mBubbleDraggedOutEnough = false; updateBubblePositions(); } /** * Sets configuration variables so that when the given bubble is removed, the animations are * started with the given velocities. */ public void prepareForDismissalWithVelocity(View bubbleView, float velX, float velY) { mBubbleDraggingOut = bubbleView; mBubbleDraggingOutVelX = velX; mBubbleDraggingOutVelY = velY; /** Resets bubble drag out gesture flags. */ public void onGestureFinished() { mBubbleDraggedOutEnough = false; mBubbleDraggingOut = null; } /** Loading Loading @@ -296,6 +302,23 @@ public class ExpandedAnimationController : 0); } @Override void onActiveControllerForLayout(PhysicsAnimationLayout layout) { final Resources res = layout.getResources(); mStackOffsetPx = res.getDimensionPixelSize(R.dimen.bubble_stack_offset); mBubblePaddingPx = res.getDimensionPixelSize(R.dimen.bubble_padding); mBubbleSizePx = res.getDimensionPixelSize(R.dimen.individual_bubble_size); mStatusBarHeight = res.getDimensionPixelSize(com.android.internal.R.dimen.status_bar_height); mPipDismissHeight = res.getDimensionPixelSize(R.dimen.pip_dismiss_gradient_height); // Ensure that all child views are at 1x scale, and visible, in case they were animating // in. mLayout.setVisibility(View.VISIBLE); animationsForChildrenFromIndex(0 /* startIndex */, (index, animation) -> animation.scaleX(1f).scaleY(1f).alpha(1f)).startAll(); } @Override Set<DynamicAnimation.ViewProperty> getAnimatedProperties() { return Sets.newHashSet( Loading Loading @@ -325,8 +348,14 @@ public class ExpandedAnimationController @Override void onChildAdded(View child, int index) { // If a bubble is added while the expand/collapse animations are playing, update the // animation to include the new bubble. if (mAnimatingExpand) { startOrUpdateExpandAnimation(); } else if (mAnimatingCollapse) { startOrUpdateCollapseAnimation(); } else { child.setTranslationX(getXForChildAtIndex(index)); animationForChild(child) .translationY( getExpandedY() - mBubbleSizePx * ANIMATE_TRANSLATION_FACTOR, /* from */ Loading @@ -334,6 +363,7 @@ public class ExpandedAnimationController .start(); updateBubblePositions(); } } @Override void onChildRemoved(View child, int index, Runnable finishRemoval) { Loading @@ -357,19 +387,15 @@ public class ExpandedAnimationController } @Override protected void setChildVisibility(View child, int index, int visibility) { if (visibility == View.VISIBLE) { // Set alpha to 0 but then become visible immediately so the animation is visible. child.setAlpha(0f); child.setVisibility(View.VISIBLE); void onChildReordered(View child, int oldIndex, int newIndex) { updateBubblePositions(); } animationForChild(child) .alpha(visibility == View.GONE ? 0f : 1f) .start(() -> super.setChildVisibility(child, index, visibility) /* after */); private void updateBubblePositions() { if (mAnimatingExpand || mAnimatingCollapse) { return; } private void updateBubblePositions() { for (int i = 0; i < mLayout.getChildCount(); i++) { final View bubble = mLayout.getChildAt(i); Loading @@ -378,6 +404,7 @@ public class ExpandedAnimationController if (bubble.equals(mBubbleDraggingOut)) { return; } animationForChild(bubble) .translationX(getBubbleLeft(i)) .start(); Loading @@ -403,10 +430,7 @@ public class ExpandedAnimationController return 0; } int bubbleCount = mLayout.getChildCount(); if (bubbleCount > mBubblesMaxRendered) { // Only shown bubbles are relevant for calculating position. bubbleCount = mBubblesMaxRendered; } // Width calculations. double bubble = bubbleCount * mBubbleSizePx; float gap = (bubbleCount - 1) * mBubblePaddingPx; Loading packages/SystemUI/src/com/android/systemui/bubbles/animation/PhysicsAnimationLayout.java +136 −124 File changed.Preview size limit exceeded, changes collapsed. Show changes packages/SystemUI/src/com/android/systemui/bubbles/animation/StackAnimationController.java +43 −28 Original line number Diff line number Diff line Loading @@ -154,21 +154,6 @@ public class StackAnimationController extends /** Height of the status bar. */ private float mStatusBarHeight; @Override protected void setLayout(PhysicsAnimationLayout layout) { super.setLayout(layout); Resources res = layout.getResources(); mStackOffset = res.getDimensionPixelSize(R.dimen.bubble_stack_offset); mIndividualBubbleSize = res.getDimensionPixelSize(R.dimen.individual_bubble_size); mBubblePadding = res.getDimensionPixelSize(R.dimen.bubble_padding); mBubbleOffscreen = res.getDimensionPixelSize(R.dimen.bubble_stack_offscreen); mStackStartingVerticalOffset = res.getDimensionPixelSize(R.dimen.bubble_stack_starting_offset_y); mStatusBarHeight = res.getDimensionPixelSize(com.android.internal.R.dimen.status_bar_height); } /** * Instantly move the first bubble to the given point, and animate the rest of the stack behind * it with the 'following' effect. Loading Loading @@ -286,6 +271,8 @@ public class StackAnimationController extends }, DynamicAnimation.TRANSLATION_X, DynamicAnimation.TRANSLATION_Y); // If we're flinging now, there's no more touch event to catch up to. mFirstBubbleSpringingToTouch = false; mIsMovingFromFlinging = true; return destinationRelativeX; } Loading Loading @@ -656,7 +643,27 @@ public class StackAnimationController extends if (mLayout.getChildCount() > 0) { animationForChildAtIndex(0).translationX(mStackPosition.x).start(); } else { // Set the start position back to the default since we're out of bubbles. New bubbles // will then animate in from the start position. mStackPosition = getDefaultStartPosition(); } } @Override void onChildReordered(View child, int oldIndex, int newIndex) {} @Override void onActiveControllerForLayout(PhysicsAnimationLayout layout) { Resources res = layout.getResources(); mStackOffset = res.getDimensionPixelSize(R.dimen.bubble_stack_offset); mIndividualBubbleSize = res.getDimensionPixelSize(R.dimen.individual_bubble_size); mBubblePadding = res.getDimensionPixelSize(R.dimen.bubble_padding); mBubbleOffscreen = res.getDimensionPixelSize(R.dimen.bubble_stack_offscreen); mStackStartingVerticalOffset = res.getDimensionPixelSize(R.dimen.bubble_stack_starting_offset_y); mStatusBarHeight = res.getDimensionPixelSize(com.android.internal.R.dimen.status_bar_height); } /** Moves the stack, without any animation, to the starting position. */ Loading @@ -664,11 +671,10 @@ public class StackAnimationController extends // Post to ensure that the layout's width and height have been calculated. mLayout.setVisibility(View.INVISIBLE); mLayout.post(() -> { mStackMovedToStartPosition = true; setStackPosition( mRestingStackPosition == null setStackPosition(mRestingStackPosition == null ? getDefaultStartPosition() : mRestingStackPosition); mStackMovedToStartPosition = true; mLayout.setVisibility(View.VISIBLE); // Animate in the top bubble now that we're visible. Loading Loading @@ -707,17 +713,22 @@ public class StackAnimationController extends Log.d(TAG, String.format("Setting position to (%f, %f).", pos.x, pos.y)); mStackPosition.set(pos.x, pos.y); // If we're not the active controller, we don't want to physically move the bubble views. if (isActiveController()) { mLayout.cancelAllAnimations(); cancelStackPositionAnimations(); // Since we're not using the chained animations, apply the offsets manually. final float xOffset = getOffsetForChainedPropertyAnimation(DynamicAnimation.TRANSLATION_X); final float yOffset = getOffsetForChainedPropertyAnimation(DynamicAnimation.TRANSLATION_Y); final float xOffset = getOffsetForChainedPropertyAnimation( DynamicAnimation.TRANSLATION_X); final float yOffset = getOffsetForChainedPropertyAnimation( DynamicAnimation.TRANSLATION_Y); for (int i = 0; i < mLayout.getChildCount(); i++) { mLayout.getChildAt(i).setTranslationX(pos.x + (i * xOffset)); mLayout.getChildAt(i).setTranslationY(pos.y + (i * yOffset)); } } } /** Returns the default stack position, which is on the top right. */ private PointF getDefaultStartPosition() { Loading @@ -732,6 +743,10 @@ public class StackAnimationController extends /** Animates in the given bubble. */ private void animateInBubble(View child) { if (!isActiveController()) { return; } child.setTranslationY(mStackPosition.y); float xOffset = getOffsetForChainedPropertyAnimation(DynamicAnimation.TRANSLATION_X); Loading packages/SystemUI/tests/src/com/android/systemui/bubbles/animation/ExpandedAnimationControllerTest.java +9 −19 Original line number Diff line number Diff line Loading @@ -60,8 +60,8 @@ public class ExpandedAnimationControllerTest extends PhysicsAnimationLayoutTestC @Before public void setUp() throws Exception { super.setUp(); addOneMoreThanRenderLimitBubbles(); mLayout.setController(mExpandedController); addOneMoreThanBubbleLimitBubbles(); mLayout.setActiveController(mExpandedController); Resources res = mLayout.getResources(); mStackOffset = res.getDimensionPixelSize(R.dimen.bubble_stack_offset); Loading @@ -73,14 +73,14 @@ public class ExpandedAnimationControllerTest extends PhysicsAnimationLayoutTestC @Test public void testExpansionAndCollapse() throws InterruptedException { Runnable afterExpand = Mockito.mock(Runnable.class); mExpandedController.expandFromStack(mExpansionPoint, afterExpand); mExpandedController.expandFromStack(afterExpand); waitForPropertyAnimations(DynamicAnimation.TRANSLATION_X, DynamicAnimation.TRANSLATION_Y); testBubblesInCorrectExpandedPositions(); verify(afterExpand).run(); Runnable afterCollapse = Mockito.mock(Runnable.class); mExpandedController.collapseBackToStack(afterCollapse); mExpandedController.collapseBackToStack(mExpansionPoint, afterCollapse); waitForPropertyAnimations(DynamicAnimation.TRANSLATION_X, DynamicAnimation.TRANSLATION_Y); testStackedAtPosition(mExpansionPoint.x, mExpansionPoint.y, -1); Loading Loading @@ -139,7 +139,6 @@ public class ExpandedAnimationControllerTest extends PhysicsAnimationLayoutTestC assertEquals(500f, draggedBubble.getTranslationY(), 1f); // Snap it back and make sure it made it back correctly. mExpandedController.prepareForDismissalWithVelocity(draggedBubble, 0f, 0f); mLayout.removeView(draggedBubble); waitForLayoutMessageQueue(); waitForPropertyAnimations(DynamicAnimation.TRANSLATION_X, DynamicAnimation.TRANSLATION_Y); Loading Loading @@ -169,7 +168,7 @@ public class ExpandedAnimationControllerTest extends PhysicsAnimationLayoutTestC // Dismiss the now-magneted bubble, verify that the callback was called. final Runnable afterDismiss = Mockito.mock(Runnable.class); mExpandedController.dismissDraggedOutBubble(afterDismiss); mExpandedController.dismissDraggedOutBubble(draggedOutView, afterDismiss); waitForPropertyAnimations(DynamicAnimation.ALPHA); verify(after).run(); Loading Loading @@ -224,7 +223,7 @@ public class ExpandedAnimationControllerTest extends PhysicsAnimationLayoutTestC /** Expand the stack and wait for animations to finish. */ private void expand() throws InterruptedException { mExpandedController.expandFromStack(mExpansionPoint, Mockito.mock(Runnable.class)); mExpandedController.expandFromStack(Mockito.mock(Runnable.class)); waitForPropertyAnimations(DynamicAnimation.TRANSLATION_X, DynamicAnimation.TRANSLATION_Y); } Loading @@ -236,26 +235,19 @@ public class ExpandedAnimationControllerTest extends PhysicsAnimationLayoutTestC assertEquals(x + i * offsetMultiplier * mStackOffset, mLayout.getChildAt(i).getTranslationX(), 2f); assertEquals(y, mLayout.getChildAt(i).getTranslationY(), 2f); if (i < mMaxRenderedBubbles) { assertEquals(1f, mLayout.getChildAt(i).getAlpha(), .01f); } } } /** Check that children are in the correct positions for being expanded. */ private void testBubblesInCorrectExpandedPositions() { // Check all the visible bubbles to see if they're in the right place. for (int i = 0; i < Math.min(mLayout.getChildCount(), mMaxRenderedBubbles); i++) { for (int i = 0; i < mLayout.getChildCount(); i++) { assertEquals(getBubbleLeft(i), mLayout.getChildAt(i).getTranslationX(), 2f); assertEquals(mExpandedController.getExpandedY(), mLayout.getChildAt(i).getTranslationY(), 2f); if (i < mMaxRenderedBubbles) { assertEquals(1f, mLayout.getChildAt(i).getAlpha(), .01f); } } } Loading @@ -273,9 +265,7 @@ public class ExpandedAnimationControllerTest extends PhysicsAnimationLayoutTestC return 0; } int bubbleCount = mLayout.getChildCount(); if (bubbleCount > mMaxRenderedBubbles) { bubbleCount = mMaxRenderedBubbles; } // Width calculations. double bubble = bubbleCount * mBubbleSize; float gap = (bubbleCount - 1) * mBubblePadding; Loading Loading
packages/SystemUI/src/com/android/systemui/bubbles/BubbleStackView.java +28 −19 Original line number Diff line number Diff line Loading @@ -330,9 +330,7 @@ public class BubbleStackView extends FrameLayout { mSurfaceSynchronizer = synchronizer != null ? synchronizer : DEFAULT_SURFACE_SYNCHRONIZER; mBubbleContainer = new PhysicsAnimationLayout(context); mBubbleContainer.setMaxRenderedChildren( getResources().getInteger(R.integer.bubbles_max_rendered)); mBubbleContainer.setController(mStackAnimationController); mBubbleContainer.setActiveController(mStackAnimationController); mBubbleContainer.setElevation(elevation); mBubbleContainer.setClipChildren(false); addView(mBubbleContainer, new FrameLayout.LayoutParams(MATCH_PARENT, MATCH_PARENT)); Loading Loading @@ -728,7 +726,7 @@ public class BubbleStackView extends FrameLayout { public void updateBubbleOrder(List<Bubble> bubbles) { for (int i = 0; i < bubbles.size(); i++) { Bubble bubble = bubbles.get(i); mBubbleContainer.moveViewTo(bubble.iconView, i); mBubbleContainer.reorderView(bubble.iconView, i); } } Loading Loading @@ -920,19 +918,17 @@ public class BubbleStackView extends FrameLayout { }; if (shouldExpand) { mBubbleContainer.setController(mExpandedAnimationController); mExpandedAnimationController.expandFromStack( mStackAnimationController.getStackPositionAlongNearestHorizontalEdge() /* collapseTo */, () -> { mBubbleContainer.setActiveController(mExpandedAnimationController); mExpandedAnimationController.expandFromStack(() -> { updatePointerPosition(); updateAfter.run(); } /* after */); } else { mBubbleContainer.cancelAllAnimations(); mExpandedAnimationController.collapseBackToStack( mStackAnimationController.getStackPositionAlongNearestHorizontalEdge(), () -> { mBubbleContainer.setController(mStackAnimationController); mBubbleContainer.setActiveController(mStackAnimationController); updateAfter.run(); }); } Loading Loading @@ -1021,7 +1017,7 @@ public class BubbleStackView extends FrameLayout { } mStackAnimationController.cancelStackPositionAnimations(); mBubbleContainer.setController(mStackAnimationController); mBubbleContainer.setActiveController(mStackAnimationController); hideFlyoutImmediate(); mDraggingInDismissTarget = false; Loading Loading @@ -1119,6 +1115,10 @@ public class BubbleStackView extends FrameLayout { /** Called when a gesture is completed or cancelled. */ void onGestureFinished() { mIsGestureInProgress = false; if (mIsExpanded) { mExpandedAnimationController.onGestureFinished(); } } /** Prepares and starts the desaturate/darken animation on the bubble stack. */ Loading Loading @@ -1209,6 +1209,7 @@ public class BubbleStackView extends FrameLayout { */ void magnetToStackIfNeededThenAnimateDismissal( View touchedView, float velX, float velY, Runnable after) { final View draggedOutBubble = mExpandedAnimationController.getDraggedOutBubble(); final Runnable animateDismissal = () -> { mAfterMagnet = null; Loading @@ -1226,7 +1227,7 @@ public class BubbleStackView extends FrameLayout { resetDesaturationAndDarken(); }); } else { mExpandedAnimationController.dismissDraggedOutBubble(() -> { mExpandedAnimationController.dismissDraggedOutBubble(draggedOutBubble, () -> { mAnimatingMagnet = false; mShowingDismiss = false; mDraggingInDismissTarget = false; Loading Loading @@ -1393,10 +1394,18 @@ public class BubbleStackView extends FrameLayout { }; // Post in case layout isn't complete and getWidth returns 0. post(() -> mFlyout.showFlyout( post(() -> { // An auto-expanding bubble could have been posted during the time it takes to // layout. if (isExpanded()) { return; } mFlyout.showFlyout( updateMessage, mStackAnimationController.getStackPosition(), getWidth(), mStackAnimationController.isStackOnLeftSide(), bubble.iconView.getBadgeColor(), mAfterFlyoutHides)); bubble.iconView.getBadgeColor(), mAfterFlyoutHides); }); } mFlyout.removeCallbacks(mHideFlyout); Loading
packages/SystemUI/src/com/android/systemui/bubbles/animation/ExpandedAnimationController.java +98 −74 Original line number Diff line number Diff line Loading @@ -22,6 +22,7 @@ import android.graphics.PointF; import android.view.View; import android.view.WindowInsets; import androidx.annotation.Nullable; import androidx.dynamicanimation.animation.DynamicAnimation; import androidx.dynamicanimation.animation.SpringForce; Loading Loading @@ -63,12 +64,16 @@ public class ExpandedAnimationController private Point mDisplaySize; /** Size of dismiss target at bottom of screen. */ private float mPipDismissHeight; /** Max number of bubbles shown in row above expanded view.*/ private int mBubblesMaxRendered; /** Whether the dragged-out bubble is in the dismiss target. */ private boolean mIndividualBubbleWithinDismissTarget = false; private boolean mAnimatingExpand = false; private boolean mAnimatingCollapse = false; private Runnable mAfterExpand; private Runnable mAfterCollapse; private PointF mCollapsePoint; /** * Whether the dragged out bubble is springing towards the touch point, rather than using the * default behavior of moving directly to the touch point. Loading Loading @@ -97,56 +102,60 @@ public class ExpandedAnimationController private View mBubbleDraggingOut; /** * Drag velocities for the dragging-out bubble when the drag finished. These are used by * {@link #onChildRemoved} to animate out the bubble while respecting touch velocity. * Animates expanding the bubbles into a row along the top of the screen. */ private float mBubbleDraggingOutVelX; private float mBubbleDraggingOutVelY; public void expandFromStack(Runnable after) { mAnimatingCollapse = false; mAnimatingExpand = true; mAfterExpand = after; @Override protected void setLayout(PhysicsAnimationLayout layout) { super.setLayout(layout); startOrUpdateExpandAnimation(); } final Resources res = layout.getResources(); mStackOffsetPx = res.getDimensionPixelSize(R.dimen.bubble_stack_offset); mBubblePaddingPx = res.getDimensionPixelSize(R.dimen.bubble_padding); mBubbleSizePx = res.getDimensionPixelSize(R.dimen.individual_bubble_size); mStatusBarHeight = res.getDimensionPixelSize(com.android.internal.R.dimen.status_bar_height); mPipDismissHeight = res.getDimensionPixelSize(R.dimen.pip_dismiss_gradient_height); mBubblesMaxRendered = res.getInteger(R.integer.bubbles_max_rendered); /** Animate collapsing the bubbles back to their stacked position. */ public void collapseBackToStack(PointF collapsePoint, Runnable after) { mAnimatingExpand = false; mAnimatingCollapse = true; mAfterCollapse = after; mCollapsePoint = collapsePoint; startOrUpdateCollapseAnimation(); } /** * Animates expanding the bubbles into a row along the top of the screen. */ public void expandFromStack(PointF collapseTo, Runnable after) { private void startOrUpdateExpandAnimation() { animationsForChildrenFromIndex( 0, /* startIndex */ new ChildAnimationConfigurator() { @Override public void configureAnimationForChildAtIndex( int index, PhysicsAnimationLayout.PhysicsPropertyAnimator animation) { animation.position(getBubbleLeft(index), getExpandedY()); (index, animation) -> animation.position(getBubbleLeft(index), getExpandedY())) .startAll(() -> { mAnimatingExpand = false; if (mAfterExpand != null) { mAfterExpand.run(); } }) .startAll(after); mCollapseToPoint = collapseTo; mAfterExpand = null; }); } /** Animate collapsing the bubbles back to their stacked position. */ public void collapseBackToStack(Runnable after) { private void startOrUpdateCollapseAnimation() { // Stack to the left if we're going to the left, or right if not. final float sideMultiplier = mLayout.isFirstChildXLeftOfCenter(mCollapseToPoint.x) ? -1 : 1; final float sideMultiplier = mLayout.isFirstChildXLeftOfCenter(mCollapsePoint.x) ? -1 : 1; animationsForChildrenFromIndex( 0, /* startIndex */ (index, animation) -> (index, animation) -> { animation.position( mCollapseToPoint.x + (sideMultiplier * index * mStackOffsetPx), mCollapseToPoint.y)) .startAll(after /* endAction */); mCollapsePoint.x + (sideMultiplier * index * mStackOffsetPx), mCollapsePoint.y); }) .startAll(() -> { mAnimatingCollapse = false; if (mAfterCollapse != null) { mAfterCollapse.run(); } mAfterCollapse = null; }); } /** Prepares the given bubble to be dragged out. */ Loading Loading @@ -190,10 +199,10 @@ public class ExpandedAnimationController } /** Plays a dismiss animation on the dragged out bubble. */ public void dismissDraggedOutBubble(Runnable after) { public void dismissDraggedOutBubble(View bubble, Runnable after) { mIndividualBubbleWithinDismissTarget = false; animationForChild(mBubbleDraggingOut) animationForChild(bubble) .withStiffness(SpringForce.STIFFNESS_HIGH) .scaleX(1.1f) .scaleY(1.1f) Loading @@ -203,6 +212,10 @@ public class ExpandedAnimationController updateBubblePositions(); } @Nullable public View getDraggedOutBubble() { return mBubbleDraggingOut; } /** Magnets the given bubble to the dismiss target. */ public void magnetBubbleToDismiss( View bubbleView, float velX, float velY, float destY, Runnable after) { Loading Loading @@ -245,20 +258,13 @@ public class ExpandedAnimationController .withPositionStartVelocities(velX, velY) .start(() -> bubbleView.setTranslationZ(0f) /* after */); mBubbleDraggingOut = null; mBubbleDraggedOutEnough = false; updateBubblePositions(); } /** * Sets configuration variables so that when the given bubble is removed, the animations are * started with the given velocities. */ public void prepareForDismissalWithVelocity(View bubbleView, float velX, float velY) { mBubbleDraggingOut = bubbleView; mBubbleDraggingOutVelX = velX; mBubbleDraggingOutVelY = velY; /** Resets bubble drag out gesture flags. */ public void onGestureFinished() { mBubbleDraggedOutEnough = false; mBubbleDraggingOut = null; } /** Loading Loading @@ -296,6 +302,23 @@ public class ExpandedAnimationController : 0); } @Override void onActiveControllerForLayout(PhysicsAnimationLayout layout) { final Resources res = layout.getResources(); mStackOffsetPx = res.getDimensionPixelSize(R.dimen.bubble_stack_offset); mBubblePaddingPx = res.getDimensionPixelSize(R.dimen.bubble_padding); mBubbleSizePx = res.getDimensionPixelSize(R.dimen.individual_bubble_size); mStatusBarHeight = res.getDimensionPixelSize(com.android.internal.R.dimen.status_bar_height); mPipDismissHeight = res.getDimensionPixelSize(R.dimen.pip_dismiss_gradient_height); // Ensure that all child views are at 1x scale, and visible, in case they were animating // in. mLayout.setVisibility(View.VISIBLE); animationsForChildrenFromIndex(0 /* startIndex */, (index, animation) -> animation.scaleX(1f).scaleY(1f).alpha(1f)).startAll(); } @Override Set<DynamicAnimation.ViewProperty> getAnimatedProperties() { return Sets.newHashSet( Loading Loading @@ -325,8 +348,14 @@ public class ExpandedAnimationController @Override void onChildAdded(View child, int index) { // If a bubble is added while the expand/collapse animations are playing, update the // animation to include the new bubble. if (mAnimatingExpand) { startOrUpdateExpandAnimation(); } else if (mAnimatingCollapse) { startOrUpdateCollapseAnimation(); } else { child.setTranslationX(getXForChildAtIndex(index)); animationForChild(child) .translationY( getExpandedY() - mBubbleSizePx * ANIMATE_TRANSLATION_FACTOR, /* from */ Loading @@ -334,6 +363,7 @@ public class ExpandedAnimationController .start(); updateBubblePositions(); } } @Override void onChildRemoved(View child, int index, Runnable finishRemoval) { Loading @@ -357,19 +387,15 @@ public class ExpandedAnimationController } @Override protected void setChildVisibility(View child, int index, int visibility) { if (visibility == View.VISIBLE) { // Set alpha to 0 but then become visible immediately so the animation is visible. child.setAlpha(0f); child.setVisibility(View.VISIBLE); void onChildReordered(View child, int oldIndex, int newIndex) { updateBubblePositions(); } animationForChild(child) .alpha(visibility == View.GONE ? 0f : 1f) .start(() -> super.setChildVisibility(child, index, visibility) /* after */); private void updateBubblePositions() { if (mAnimatingExpand || mAnimatingCollapse) { return; } private void updateBubblePositions() { for (int i = 0; i < mLayout.getChildCount(); i++) { final View bubble = mLayout.getChildAt(i); Loading @@ -378,6 +404,7 @@ public class ExpandedAnimationController if (bubble.equals(mBubbleDraggingOut)) { return; } animationForChild(bubble) .translationX(getBubbleLeft(i)) .start(); Loading @@ -403,10 +430,7 @@ public class ExpandedAnimationController return 0; } int bubbleCount = mLayout.getChildCount(); if (bubbleCount > mBubblesMaxRendered) { // Only shown bubbles are relevant for calculating position. bubbleCount = mBubblesMaxRendered; } // Width calculations. double bubble = bubbleCount * mBubbleSizePx; float gap = (bubbleCount - 1) * mBubblePaddingPx; Loading
packages/SystemUI/src/com/android/systemui/bubbles/animation/PhysicsAnimationLayout.java +136 −124 File changed.Preview size limit exceeded, changes collapsed. Show changes
packages/SystemUI/src/com/android/systemui/bubbles/animation/StackAnimationController.java +43 −28 Original line number Diff line number Diff line Loading @@ -154,21 +154,6 @@ public class StackAnimationController extends /** Height of the status bar. */ private float mStatusBarHeight; @Override protected void setLayout(PhysicsAnimationLayout layout) { super.setLayout(layout); Resources res = layout.getResources(); mStackOffset = res.getDimensionPixelSize(R.dimen.bubble_stack_offset); mIndividualBubbleSize = res.getDimensionPixelSize(R.dimen.individual_bubble_size); mBubblePadding = res.getDimensionPixelSize(R.dimen.bubble_padding); mBubbleOffscreen = res.getDimensionPixelSize(R.dimen.bubble_stack_offscreen); mStackStartingVerticalOffset = res.getDimensionPixelSize(R.dimen.bubble_stack_starting_offset_y); mStatusBarHeight = res.getDimensionPixelSize(com.android.internal.R.dimen.status_bar_height); } /** * Instantly move the first bubble to the given point, and animate the rest of the stack behind * it with the 'following' effect. Loading Loading @@ -286,6 +271,8 @@ public class StackAnimationController extends }, DynamicAnimation.TRANSLATION_X, DynamicAnimation.TRANSLATION_Y); // If we're flinging now, there's no more touch event to catch up to. mFirstBubbleSpringingToTouch = false; mIsMovingFromFlinging = true; return destinationRelativeX; } Loading Loading @@ -656,7 +643,27 @@ public class StackAnimationController extends if (mLayout.getChildCount() > 0) { animationForChildAtIndex(0).translationX(mStackPosition.x).start(); } else { // Set the start position back to the default since we're out of bubbles. New bubbles // will then animate in from the start position. mStackPosition = getDefaultStartPosition(); } } @Override void onChildReordered(View child, int oldIndex, int newIndex) {} @Override void onActiveControllerForLayout(PhysicsAnimationLayout layout) { Resources res = layout.getResources(); mStackOffset = res.getDimensionPixelSize(R.dimen.bubble_stack_offset); mIndividualBubbleSize = res.getDimensionPixelSize(R.dimen.individual_bubble_size); mBubblePadding = res.getDimensionPixelSize(R.dimen.bubble_padding); mBubbleOffscreen = res.getDimensionPixelSize(R.dimen.bubble_stack_offscreen); mStackStartingVerticalOffset = res.getDimensionPixelSize(R.dimen.bubble_stack_starting_offset_y); mStatusBarHeight = res.getDimensionPixelSize(com.android.internal.R.dimen.status_bar_height); } /** Moves the stack, without any animation, to the starting position. */ Loading @@ -664,11 +671,10 @@ public class StackAnimationController extends // Post to ensure that the layout's width and height have been calculated. mLayout.setVisibility(View.INVISIBLE); mLayout.post(() -> { mStackMovedToStartPosition = true; setStackPosition( mRestingStackPosition == null setStackPosition(mRestingStackPosition == null ? getDefaultStartPosition() : mRestingStackPosition); mStackMovedToStartPosition = true; mLayout.setVisibility(View.VISIBLE); // Animate in the top bubble now that we're visible. Loading Loading @@ -707,17 +713,22 @@ public class StackAnimationController extends Log.d(TAG, String.format("Setting position to (%f, %f).", pos.x, pos.y)); mStackPosition.set(pos.x, pos.y); // If we're not the active controller, we don't want to physically move the bubble views. if (isActiveController()) { mLayout.cancelAllAnimations(); cancelStackPositionAnimations(); // Since we're not using the chained animations, apply the offsets manually. final float xOffset = getOffsetForChainedPropertyAnimation(DynamicAnimation.TRANSLATION_X); final float yOffset = getOffsetForChainedPropertyAnimation(DynamicAnimation.TRANSLATION_Y); final float xOffset = getOffsetForChainedPropertyAnimation( DynamicAnimation.TRANSLATION_X); final float yOffset = getOffsetForChainedPropertyAnimation( DynamicAnimation.TRANSLATION_Y); for (int i = 0; i < mLayout.getChildCount(); i++) { mLayout.getChildAt(i).setTranslationX(pos.x + (i * xOffset)); mLayout.getChildAt(i).setTranslationY(pos.y + (i * yOffset)); } } } /** Returns the default stack position, which is on the top right. */ private PointF getDefaultStartPosition() { Loading @@ -732,6 +743,10 @@ public class StackAnimationController extends /** Animates in the given bubble. */ private void animateInBubble(View child) { if (!isActiveController()) { return; } child.setTranslationY(mStackPosition.y); float xOffset = getOffsetForChainedPropertyAnimation(DynamicAnimation.TRANSLATION_X); Loading
packages/SystemUI/tests/src/com/android/systemui/bubbles/animation/ExpandedAnimationControllerTest.java +9 −19 Original line number Diff line number Diff line Loading @@ -60,8 +60,8 @@ public class ExpandedAnimationControllerTest extends PhysicsAnimationLayoutTestC @Before public void setUp() throws Exception { super.setUp(); addOneMoreThanRenderLimitBubbles(); mLayout.setController(mExpandedController); addOneMoreThanBubbleLimitBubbles(); mLayout.setActiveController(mExpandedController); Resources res = mLayout.getResources(); mStackOffset = res.getDimensionPixelSize(R.dimen.bubble_stack_offset); Loading @@ -73,14 +73,14 @@ public class ExpandedAnimationControllerTest extends PhysicsAnimationLayoutTestC @Test public void testExpansionAndCollapse() throws InterruptedException { Runnable afterExpand = Mockito.mock(Runnable.class); mExpandedController.expandFromStack(mExpansionPoint, afterExpand); mExpandedController.expandFromStack(afterExpand); waitForPropertyAnimations(DynamicAnimation.TRANSLATION_X, DynamicAnimation.TRANSLATION_Y); testBubblesInCorrectExpandedPositions(); verify(afterExpand).run(); Runnable afterCollapse = Mockito.mock(Runnable.class); mExpandedController.collapseBackToStack(afterCollapse); mExpandedController.collapseBackToStack(mExpansionPoint, afterCollapse); waitForPropertyAnimations(DynamicAnimation.TRANSLATION_X, DynamicAnimation.TRANSLATION_Y); testStackedAtPosition(mExpansionPoint.x, mExpansionPoint.y, -1); Loading Loading @@ -139,7 +139,6 @@ public class ExpandedAnimationControllerTest extends PhysicsAnimationLayoutTestC assertEquals(500f, draggedBubble.getTranslationY(), 1f); // Snap it back and make sure it made it back correctly. mExpandedController.prepareForDismissalWithVelocity(draggedBubble, 0f, 0f); mLayout.removeView(draggedBubble); waitForLayoutMessageQueue(); waitForPropertyAnimations(DynamicAnimation.TRANSLATION_X, DynamicAnimation.TRANSLATION_Y); Loading Loading @@ -169,7 +168,7 @@ public class ExpandedAnimationControllerTest extends PhysicsAnimationLayoutTestC // Dismiss the now-magneted bubble, verify that the callback was called. final Runnable afterDismiss = Mockito.mock(Runnable.class); mExpandedController.dismissDraggedOutBubble(afterDismiss); mExpandedController.dismissDraggedOutBubble(draggedOutView, afterDismiss); waitForPropertyAnimations(DynamicAnimation.ALPHA); verify(after).run(); Loading Loading @@ -224,7 +223,7 @@ public class ExpandedAnimationControllerTest extends PhysicsAnimationLayoutTestC /** Expand the stack and wait for animations to finish. */ private void expand() throws InterruptedException { mExpandedController.expandFromStack(mExpansionPoint, Mockito.mock(Runnable.class)); mExpandedController.expandFromStack(Mockito.mock(Runnable.class)); waitForPropertyAnimations(DynamicAnimation.TRANSLATION_X, DynamicAnimation.TRANSLATION_Y); } Loading @@ -236,26 +235,19 @@ public class ExpandedAnimationControllerTest extends PhysicsAnimationLayoutTestC assertEquals(x + i * offsetMultiplier * mStackOffset, mLayout.getChildAt(i).getTranslationX(), 2f); assertEquals(y, mLayout.getChildAt(i).getTranslationY(), 2f); if (i < mMaxRenderedBubbles) { assertEquals(1f, mLayout.getChildAt(i).getAlpha(), .01f); } } } /** Check that children are in the correct positions for being expanded. */ private void testBubblesInCorrectExpandedPositions() { // Check all the visible bubbles to see if they're in the right place. for (int i = 0; i < Math.min(mLayout.getChildCount(), mMaxRenderedBubbles); i++) { for (int i = 0; i < mLayout.getChildCount(); i++) { assertEquals(getBubbleLeft(i), mLayout.getChildAt(i).getTranslationX(), 2f); assertEquals(mExpandedController.getExpandedY(), mLayout.getChildAt(i).getTranslationY(), 2f); if (i < mMaxRenderedBubbles) { assertEquals(1f, mLayout.getChildAt(i).getAlpha(), .01f); } } } Loading @@ -273,9 +265,7 @@ public class ExpandedAnimationControllerTest extends PhysicsAnimationLayoutTestC return 0; } int bubbleCount = mLayout.getChildCount(); if (bubbleCount > mMaxRenderedBubbles) { bubbleCount = mMaxRenderedBubbles; } // Width calculations. double bubble = bubbleCount * mBubbleSize; float gap = (bubbleCount - 1) * mBubblePadding; Loading