Loading packages/SystemUI/src/com/android/systemui/bubbles/BubbleFlyoutView.java +14 −22 Original line number Diff line number Diff line Loading @@ -39,8 +39,7 @@ import android.view.ViewOutlineProvider; import android.widget.FrameLayout; import android.widget.TextView; import androidx.dynamicanimation.animation.DynamicAnimation; import androidx.dynamicanimation.animation.SpringAnimation; import androidx.annotation.Nullable; import com.android.systemui.R; import com.android.systemui.recents.TriangleShape; Loading @@ -67,9 +66,6 @@ public class BubbleFlyoutView extends FrameLayout { private final ViewGroup mFlyoutTextContainer; private final TextView mFlyoutText; /** Spring animation for the flyout. */ private final SpringAnimation mFlyoutSpring = new SpringAnimation(this, DynamicAnimation.TRANSLATION_X); /** Values related to the 'new' dot which we use to figure out where to collapse the flyout. */ private final float mNewDotRadius; Loading Loading @@ -141,7 +137,7 @@ public class BubbleFlyoutView extends FrameLayout { private static final float DOT_SCALE = 0.8f; /** Callback to run when the flyout is hidden. */ private Runnable mOnHide; @Nullable private Runnable mOnHide; public BubbleFlyoutView(Context context) { super(context); Loading Loading @@ -209,17 +205,16 @@ public class BubbleFlyoutView extends FrameLayout { super.onDraw(canvas); } /** Configures the flyout and animates it in. */ void showFlyout( /** Configures the flyout, collapsed into to dot form. */ void setupFlyoutStartingAsDot( CharSequence updateMessage, PointF stackPos, float parentWidth, boolean arrowPointingLeft, int dotColor, Runnable onHide) { boolean arrowPointingLeft, int dotColor, @Nullable Runnable onLayoutComplete, @Nullable Runnable onHide) { mArrowPointingLeft = arrowPointingLeft; mDotColor = dotColor; mOnHide = onHide; setCollapsePercent(0f); setAlpha(0f); setVisibility(VISIBLE); setCollapsePercent(1f); // Set the flyout TextView's max width in terms of percent, and then subtract out the // padding so that the entire flyout view will be the desired width (rather than the Loading @@ -245,14 +240,6 @@ public class BubbleFlyoutView extends FrameLayout { ? stackPos.x + mBubbleSize + mFlyoutSpaceFromBubble : stackPos.x - getWidth() - mFlyoutSpaceFromBubble; // Translate towards the stack slightly. setTranslationX( mRestingTranslationX + (arrowPointingLeft ? -mBubbleSize : mBubbleSize)); // Fade in the entire flyout and spring it to its normal position. animate().alpha(1f); mFlyoutSpring.animateToFinalPosition(mRestingTranslationX); // Calculate the difference in size between the flyout and the 'dot' so that we can // transform into the dot later. mFlyoutToDotWidthDelta = getWidth() - mNewDotSize; Loading @@ -271,12 +258,17 @@ public class BubbleFlyoutView extends FrameLayout { getHeight() / 2f - mBubbleSize / 2f + mOriginalDotSize / 2; if (onLayoutComplete != null) { onLayoutComplete.run(); } }); } /** * Hides the flyout and runs the optional callback passed into showFlyout. The flyout has been * animated into the 'new' dot by the time we call this, so no animations are needed. * Hides the flyout and runs the optional callback passed into setupFlyoutStartingAsDot. * The flyout has been animated into the 'new' dot by the time we call this, so no animations * are needed. */ void hideFlyout() { if (mOnHide != null) { Loading packages/SystemUI/src/com/android/systemui/bubbles/BubbleStackView.java +60 −10 Original line number Diff line number Diff line Loading @@ -287,6 +287,11 @@ public class BubbleStackView extends FrameLayout { /** Distance the flyout has been dragged in the X axis. */ private float mFlyoutDragDeltaX = 0f; /** * Runnable that animates in the flyout. This reference is needed to cancel delayed postings. */ private Runnable mAnimateInFlyout; /** * End listener for the flyout spring that either posts a runnable to hide the flyout, or hides * it immediately. Loading Loading @@ -370,7 +375,7 @@ public class BubbleStackView extends FrameLayout { addView(mFlyout, new FrameLayout.LayoutParams(WRAP_CONTENT, WRAP_CONTENT)); mFlyoutTransitionSpring.setSpring(new SpringForce() .setStiffness(SpringForce.STIFFNESS_MEDIUM) .setStiffness(SpringForce.STIFFNESS_LOW) .setDampingRatio(SpringForce.DAMPING_RATIO_LOW_BOUNCY)); mFlyoutTransitionSpring.addEndListener(mAfterFlyoutTransitionSpring); Loading Loading @@ -1091,6 +1096,19 @@ public class BubbleStackView extends FrameLayout { mFlyout.setTranslationX(mFlyout.getRestingTranslationX() + overscrollTranslation); } /** * Set when the flyout is tapped, so that we can expand the bubble associated with the flyout * once it collapses. */ @Nullable private Bubble mBubbleToExpandAfterFlyoutCollapse = null; void onFlyoutTapped() { mBubbleToExpandAfterFlyoutCollapse = mBubbleData.getSelectedBubble(); mFlyout.removeCallbacks(mHideFlyout); mHideFlyout.run(); } /** * Called when the flyout drag has finished, and returns true if the gesture successfully * dismissed the flyout. Loading Loading @@ -1288,6 +1306,12 @@ public class BubbleStackView extends FrameLayout { /** Animates the flyout collapsed (to dot), or the reverse, starting with the given velocity. */ private void animateFlyoutCollapsed(boolean collapsed, float velX) { final boolean onLeft = mStackAnimationController.isStackOnLeftSide(); // If the flyout was tapped, we want a higher stiffness for the collapse animation so it's // faster. mFlyoutTransitionSpring.getSpring().setStiffness( (mBubbleToExpandAfterFlyoutCollapse != null) ? SpringForce.STIFFNESS_MEDIUM : SpringForce.STIFFNESS_LOW); mFlyoutTransitionSpring .setStartValue(mFlyoutDragDeltaX) .setStartVelocity(velX) Loading Loading @@ -1329,8 +1353,10 @@ public class BubbleStackView extends FrameLayout { if (updateMessage == null || isExpanded() || mIsExpansionAnimating || mIsGestureInProgress) { // Skip the message if none exists, we're expanded or animating expansion. || mIsGestureInProgress || mBubbleToExpandAfterFlyoutCollapse != null) { // Skip the message if none exists, we're expanded or animating expansion, or we're // about to expand a bubble from the previous tapped flyout. return; } Loading @@ -1339,18 +1365,14 @@ public class BubbleStackView extends FrameLayout { bubble.getIconView().setSuppressDot( true /* suppressDot */, false /* animate */); mFlyout.removeCallbacks(mAnimateInFlyout); mFlyoutDragDeltaX = 0f; mFlyout.setAlpha(0f); if (mAfterFlyoutHides != null) { mAfterFlyoutHides.run(); } mAfterFlyoutHides = () -> { if (bubble.getIconView() == null) { return; } final boolean suppressDot = !bubble.showBubbleDot(); // If we're going to suppress the dot, make it visible first so it'll // visibly animate away. Loading @@ -1365,8 +1387,16 @@ public class BubbleStackView extends FrameLayout { bubble.getIconView().setSuppressDot( suppressDot /* suppressDot */, suppressDot /* animate */); if (mBubbleToExpandAfterFlyoutCollapse != null) { mBubbleData.setSelectedBubble(mBubbleToExpandAfterFlyoutCollapse); mBubbleData.setExpanded(true); mBubbleToExpandAfterFlyoutCollapse = null; } }; mFlyout.setVisibility(INVISIBLE); // Post in case layout isn't complete and getWidth returns 0. post(() -> { // An auto-expanding bubble could have been posted during the time it takes to Loading @@ -1375,10 +1405,29 @@ public class BubbleStackView extends FrameLayout { return; } mFlyout.showFlyout( final Runnable afterShow = () -> { mAnimateInFlyout = () -> { mFlyout.setVisibility(VISIBLE); bubble.getIconView().setSuppressDot( true /* suppressDot */, false /* animate */); mFlyoutDragDeltaX = mStackAnimationController.isStackOnLeftSide() ? -mFlyout.getWidth() : mFlyout.getWidth(); animateFlyoutCollapsed(false /* collapsed */, 0 /* velX */); mFlyout.postDelayed(mHideFlyout, FLYOUT_HIDE_AFTER); }; mFlyout.postDelayed(mAnimateInFlyout, 200); }; mFlyout.setupFlyoutStartingAsDot( updateMessage, mStackAnimationController.getStackPosition(), getWidth(), mStackAnimationController.isStackOnLeftSide(), bubble.getIconView().getBadgeColor(), mAfterFlyoutHides); bubble.getIconView().getBadgeColor(), afterShow, mAfterFlyoutHides); mFlyout.bringToFront(); }); } Loading @@ -1393,6 +1442,7 @@ public class BubbleStackView extends FrameLayout { mAfterFlyoutHides.run(); } mFlyout.removeCallbacks(mAnimateInFlyout); mFlyout.removeCallbacks(mHideFlyout); mFlyout.hideFlyout(); } Loading packages/SystemUI/src/com/android/systemui/bubbles/BubbleTouchHandler.java +1 −2 Original line number Diff line number Diff line Loading @@ -192,9 +192,8 @@ class BubbleTouchHandler implements View.OnTouchListener { } }); } else if (isFlyout) { // TODO(b/129768381): Expand if tapped, dismiss if swiped away. if (!mBubbleData.isExpanded() && !mMovedEnough) { mBubbleData.setExpanded(true); mStack.onFlyoutTapped(); } } else if (mMovedEnough) { if (isStack) { Loading packages/SystemUI/src/com/android/systemui/bubbles/animation/StackAnimationController.java +27 −10 Original line number Diff line number Diff line Loading @@ -56,6 +56,10 @@ public class StackAnimationController extends /** Translation factor (multiplied by stack offset) to use for bubbles being animated in/out. */ private static final int ANIMATE_TRANSLATION_FACTOR = 4; /** Values to use for animating bubbles in. */ private static final float ANIMATE_IN_STIFFNESS = 1000f; private static final int ANIMATE_IN_START_DELAY = 25; /** * Values to use for the default {@link SpringForce} provided to the physics animation layout. */ Loading Loading @@ -643,7 +647,7 @@ public class StackAnimationController extends } else if (isStackPositionSet() && mLayout.indexOfChild(child) == 0) { // Otherwise, animate the bubble in if it's the newest bubble. If we're adding a bubble // to the back of the stack, it'll be largely invisible so don't bother animating it in. animateInBubble(child); animateInBubble(child, index); } } Loading Loading @@ -697,7 +701,7 @@ public class StackAnimationController extends // Animate in the top bubble now that we're visible. if (mLayout.getChildCount() > 0) { animateInBubble(mLayout.getChildAt(0)); animateInBubble(mLayout.getChildAt(0), 0 /* index */); } }); } Loading Loading @@ -760,21 +764,34 @@ public class StackAnimationController extends } /** Animates in the given bubble. */ private void animateInBubble(View child) { private void animateInBubble(View child, int index) { if (!isActiveController()) { return; } final float xOffset = getOffsetForChainedPropertyAnimation(DynamicAnimation.TRANSLATION_X); // Position the new bubble in the correct position, scaled down completely. child.setTranslationX(mStackPosition.x + xOffset * index); child.setTranslationY(mStackPosition.y); child.setScaleX(0f); child.setScaleY(0f); // Push the subsequent views out of the way, if there are subsequent views. if (index + 1 < mLayout.getChildCount()) { animationForChildAtIndex(index + 1) .translationX(mStackPosition.x + xOffset * (index + 1)) .withStiffness(SpringForce.STIFFNESS_LOW) .start(); } float xOffset = getOffsetForChainedPropertyAnimation(DynamicAnimation.TRANSLATION_X); // Scale in the new bubble, slightly delayed. animationForChild(child) .scaleX(ANIMATE_IN_STARTING_SCALE /* from */, 1f /* to */) .scaleY(ANIMATE_IN_STARTING_SCALE /* from */, 1f /* to */) .alpha(0f /* from */, 1f /* to */) .translationX( mStackPosition.x - ANIMATE_TRANSLATION_FACTOR * xOffset /* from */, mStackPosition.x /* to */) .scaleX(1f) .scaleY(1f) .withStiffness(ANIMATE_IN_STIFFNESS) .withStartDelay(mLayout.getChildCount() > 1 ? ANIMATE_IN_START_DELAY : 0) .start(); } Loading packages/SystemUI/tests/src/com/android/systemui/bubbles/BubbleFlyoutViewTest.java +9 −10 Original line number Diff line number Diff line Loading @@ -18,7 +18,6 @@ package com.android.systemui.bubbles; import static junit.framework.Assert.assertEquals; import static junit.framework.Assert.assertNotSame; import static junit.framework.Assert.assertTrue; import static org.mockito.Mockito.verify; Loading Loading @@ -57,16 +56,19 @@ public class BubbleFlyoutViewTest extends SysuiTestCase { @Test public void testShowFlyout_isVisible() { mFlyout.showFlyout("Hello", new PointF(100, 100), 500, true, Color.WHITE, null); mFlyout.setupFlyoutStartingAsDot( "Hello", new PointF(100, 100), 500, true, Color.WHITE, null, null); mFlyout.setVisibility(View.VISIBLE); assertEquals("Hello", mFlyoutText.getText()); assertEquals(View.VISIBLE, mFlyout.getVisibility()); assertEquals(1f, mFlyoutText.getAlpha(), .01f); } @Test public void testFlyoutHide_runsCallback() { Runnable after = Mockito.mock(Runnable.class); mFlyout.showFlyout("Hello", new PointF(100, 100), 500, true, Color.WHITE, after); mFlyout.setupFlyoutStartingAsDot( "Hello", new PointF(100, 100), 500, true, Color.WHITE, null, after); mFlyout.hideFlyout(); verify(after).run(); Loading @@ -74,19 +76,16 @@ public class BubbleFlyoutViewTest extends SysuiTestCase { @Test public void testSetCollapsePercent() { mFlyout.showFlyout("Hello", new PointF(100, 100), 500, true, Color.WHITE, null); float initialTranslationZ = mFlyout.getTranslationZ(); mFlyout.setupFlyoutStartingAsDot( "Hello", new PointF(100, 100), 500, true, Color.WHITE, null, null); mFlyout.setVisibility(View.VISIBLE); mFlyout.setCollapsePercent(1f); assertEquals(0f, mFlyoutText.getAlpha(), 0.01f); assertNotSame(0f, mFlyoutText.getTranslationX()); // Should have moved to collapse. assertTrue(mFlyout.getTranslationZ() < initialTranslationZ); // Should be descending. mFlyout.setCollapsePercent(0f); assertEquals(1f, mFlyoutText.getAlpha(), 0.01f); assertEquals(0f, mFlyoutText.getTranslationX()); assertEquals(initialTranslationZ, mFlyout.getTranslationZ()); } } Loading
packages/SystemUI/src/com/android/systemui/bubbles/BubbleFlyoutView.java +14 −22 Original line number Diff line number Diff line Loading @@ -39,8 +39,7 @@ import android.view.ViewOutlineProvider; import android.widget.FrameLayout; import android.widget.TextView; import androidx.dynamicanimation.animation.DynamicAnimation; import androidx.dynamicanimation.animation.SpringAnimation; import androidx.annotation.Nullable; import com.android.systemui.R; import com.android.systemui.recents.TriangleShape; Loading @@ -67,9 +66,6 @@ public class BubbleFlyoutView extends FrameLayout { private final ViewGroup mFlyoutTextContainer; private final TextView mFlyoutText; /** Spring animation for the flyout. */ private final SpringAnimation mFlyoutSpring = new SpringAnimation(this, DynamicAnimation.TRANSLATION_X); /** Values related to the 'new' dot which we use to figure out where to collapse the flyout. */ private final float mNewDotRadius; Loading Loading @@ -141,7 +137,7 @@ public class BubbleFlyoutView extends FrameLayout { private static final float DOT_SCALE = 0.8f; /** Callback to run when the flyout is hidden. */ private Runnable mOnHide; @Nullable private Runnable mOnHide; public BubbleFlyoutView(Context context) { super(context); Loading Loading @@ -209,17 +205,16 @@ public class BubbleFlyoutView extends FrameLayout { super.onDraw(canvas); } /** Configures the flyout and animates it in. */ void showFlyout( /** Configures the flyout, collapsed into to dot form. */ void setupFlyoutStartingAsDot( CharSequence updateMessage, PointF stackPos, float parentWidth, boolean arrowPointingLeft, int dotColor, Runnable onHide) { boolean arrowPointingLeft, int dotColor, @Nullable Runnable onLayoutComplete, @Nullable Runnable onHide) { mArrowPointingLeft = arrowPointingLeft; mDotColor = dotColor; mOnHide = onHide; setCollapsePercent(0f); setAlpha(0f); setVisibility(VISIBLE); setCollapsePercent(1f); // Set the flyout TextView's max width in terms of percent, and then subtract out the // padding so that the entire flyout view will be the desired width (rather than the Loading @@ -245,14 +240,6 @@ public class BubbleFlyoutView extends FrameLayout { ? stackPos.x + mBubbleSize + mFlyoutSpaceFromBubble : stackPos.x - getWidth() - mFlyoutSpaceFromBubble; // Translate towards the stack slightly. setTranslationX( mRestingTranslationX + (arrowPointingLeft ? -mBubbleSize : mBubbleSize)); // Fade in the entire flyout and spring it to its normal position. animate().alpha(1f); mFlyoutSpring.animateToFinalPosition(mRestingTranslationX); // Calculate the difference in size between the flyout and the 'dot' so that we can // transform into the dot later. mFlyoutToDotWidthDelta = getWidth() - mNewDotSize; Loading @@ -271,12 +258,17 @@ public class BubbleFlyoutView extends FrameLayout { getHeight() / 2f - mBubbleSize / 2f + mOriginalDotSize / 2; if (onLayoutComplete != null) { onLayoutComplete.run(); } }); } /** * Hides the flyout and runs the optional callback passed into showFlyout. The flyout has been * animated into the 'new' dot by the time we call this, so no animations are needed. * Hides the flyout and runs the optional callback passed into setupFlyoutStartingAsDot. * The flyout has been animated into the 'new' dot by the time we call this, so no animations * are needed. */ void hideFlyout() { if (mOnHide != null) { Loading
packages/SystemUI/src/com/android/systemui/bubbles/BubbleStackView.java +60 −10 Original line number Diff line number Diff line Loading @@ -287,6 +287,11 @@ public class BubbleStackView extends FrameLayout { /** Distance the flyout has been dragged in the X axis. */ private float mFlyoutDragDeltaX = 0f; /** * Runnable that animates in the flyout. This reference is needed to cancel delayed postings. */ private Runnable mAnimateInFlyout; /** * End listener for the flyout spring that either posts a runnable to hide the flyout, or hides * it immediately. Loading Loading @@ -370,7 +375,7 @@ public class BubbleStackView extends FrameLayout { addView(mFlyout, new FrameLayout.LayoutParams(WRAP_CONTENT, WRAP_CONTENT)); mFlyoutTransitionSpring.setSpring(new SpringForce() .setStiffness(SpringForce.STIFFNESS_MEDIUM) .setStiffness(SpringForce.STIFFNESS_LOW) .setDampingRatio(SpringForce.DAMPING_RATIO_LOW_BOUNCY)); mFlyoutTransitionSpring.addEndListener(mAfterFlyoutTransitionSpring); Loading Loading @@ -1091,6 +1096,19 @@ public class BubbleStackView extends FrameLayout { mFlyout.setTranslationX(mFlyout.getRestingTranslationX() + overscrollTranslation); } /** * Set when the flyout is tapped, so that we can expand the bubble associated with the flyout * once it collapses. */ @Nullable private Bubble mBubbleToExpandAfterFlyoutCollapse = null; void onFlyoutTapped() { mBubbleToExpandAfterFlyoutCollapse = mBubbleData.getSelectedBubble(); mFlyout.removeCallbacks(mHideFlyout); mHideFlyout.run(); } /** * Called when the flyout drag has finished, and returns true if the gesture successfully * dismissed the flyout. Loading Loading @@ -1288,6 +1306,12 @@ public class BubbleStackView extends FrameLayout { /** Animates the flyout collapsed (to dot), or the reverse, starting with the given velocity. */ private void animateFlyoutCollapsed(boolean collapsed, float velX) { final boolean onLeft = mStackAnimationController.isStackOnLeftSide(); // If the flyout was tapped, we want a higher stiffness for the collapse animation so it's // faster. mFlyoutTransitionSpring.getSpring().setStiffness( (mBubbleToExpandAfterFlyoutCollapse != null) ? SpringForce.STIFFNESS_MEDIUM : SpringForce.STIFFNESS_LOW); mFlyoutTransitionSpring .setStartValue(mFlyoutDragDeltaX) .setStartVelocity(velX) Loading Loading @@ -1329,8 +1353,10 @@ public class BubbleStackView extends FrameLayout { if (updateMessage == null || isExpanded() || mIsExpansionAnimating || mIsGestureInProgress) { // Skip the message if none exists, we're expanded or animating expansion. || mIsGestureInProgress || mBubbleToExpandAfterFlyoutCollapse != null) { // Skip the message if none exists, we're expanded or animating expansion, or we're // about to expand a bubble from the previous tapped flyout. return; } Loading @@ -1339,18 +1365,14 @@ public class BubbleStackView extends FrameLayout { bubble.getIconView().setSuppressDot( true /* suppressDot */, false /* animate */); mFlyout.removeCallbacks(mAnimateInFlyout); mFlyoutDragDeltaX = 0f; mFlyout.setAlpha(0f); if (mAfterFlyoutHides != null) { mAfterFlyoutHides.run(); } mAfterFlyoutHides = () -> { if (bubble.getIconView() == null) { return; } final boolean suppressDot = !bubble.showBubbleDot(); // If we're going to suppress the dot, make it visible first so it'll // visibly animate away. Loading @@ -1365,8 +1387,16 @@ public class BubbleStackView extends FrameLayout { bubble.getIconView().setSuppressDot( suppressDot /* suppressDot */, suppressDot /* animate */); if (mBubbleToExpandAfterFlyoutCollapse != null) { mBubbleData.setSelectedBubble(mBubbleToExpandAfterFlyoutCollapse); mBubbleData.setExpanded(true); mBubbleToExpandAfterFlyoutCollapse = null; } }; mFlyout.setVisibility(INVISIBLE); // Post in case layout isn't complete and getWidth returns 0. post(() -> { // An auto-expanding bubble could have been posted during the time it takes to Loading @@ -1375,10 +1405,29 @@ public class BubbleStackView extends FrameLayout { return; } mFlyout.showFlyout( final Runnable afterShow = () -> { mAnimateInFlyout = () -> { mFlyout.setVisibility(VISIBLE); bubble.getIconView().setSuppressDot( true /* suppressDot */, false /* animate */); mFlyoutDragDeltaX = mStackAnimationController.isStackOnLeftSide() ? -mFlyout.getWidth() : mFlyout.getWidth(); animateFlyoutCollapsed(false /* collapsed */, 0 /* velX */); mFlyout.postDelayed(mHideFlyout, FLYOUT_HIDE_AFTER); }; mFlyout.postDelayed(mAnimateInFlyout, 200); }; mFlyout.setupFlyoutStartingAsDot( updateMessage, mStackAnimationController.getStackPosition(), getWidth(), mStackAnimationController.isStackOnLeftSide(), bubble.getIconView().getBadgeColor(), mAfterFlyoutHides); bubble.getIconView().getBadgeColor(), afterShow, mAfterFlyoutHides); mFlyout.bringToFront(); }); } Loading @@ -1393,6 +1442,7 @@ public class BubbleStackView extends FrameLayout { mAfterFlyoutHides.run(); } mFlyout.removeCallbacks(mAnimateInFlyout); mFlyout.removeCallbacks(mHideFlyout); mFlyout.hideFlyout(); } Loading
packages/SystemUI/src/com/android/systemui/bubbles/BubbleTouchHandler.java +1 −2 Original line number Diff line number Diff line Loading @@ -192,9 +192,8 @@ class BubbleTouchHandler implements View.OnTouchListener { } }); } else if (isFlyout) { // TODO(b/129768381): Expand if tapped, dismiss if swiped away. if (!mBubbleData.isExpanded() && !mMovedEnough) { mBubbleData.setExpanded(true); mStack.onFlyoutTapped(); } } else if (mMovedEnough) { if (isStack) { Loading
packages/SystemUI/src/com/android/systemui/bubbles/animation/StackAnimationController.java +27 −10 Original line number Diff line number Diff line Loading @@ -56,6 +56,10 @@ public class StackAnimationController extends /** Translation factor (multiplied by stack offset) to use for bubbles being animated in/out. */ private static final int ANIMATE_TRANSLATION_FACTOR = 4; /** Values to use for animating bubbles in. */ private static final float ANIMATE_IN_STIFFNESS = 1000f; private static final int ANIMATE_IN_START_DELAY = 25; /** * Values to use for the default {@link SpringForce} provided to the physics animation layout. */ Loading Loading @@ -643,7 +647,7 @@ public class StackAnimationController extends } else if (isStackPositionSet() && mLayout.indexOfChild(child) == 0) { // Otherwise, animate the bubble in if it's the newest bubble. If we're adding a bubble // to the back of the stack, it'll be largely invisible so don't bother animating it in. animateInBubble(child); animateInBubble(child, index); } } Loading Loading @@ -697,7 +701,7 @@ public class StackAnimationController extends // Animate in the top bubble now that we're visible. if (mLayout.getChildCount() > 0) { animateInBubble(mLayout.getChildAt(0)); animateInBubble(mLayout.getChildAt(0), 0 /* index */); } }); } Loading Loading @@ -760,21 +764,34 @@ public class StackAnimationController extends } /** Animates in the given bubble. */ private void animateInBubble(View child) { private void animateInBubble(View child, int index) { if (!isActiveController()) { return; } final float xOffset = getOffsetForChainedPropertyAnimation(DynamicAnimation.TRANSLATION_X); // Position the new bubble in the correct position, scaled down completely. child.setTranslationX(mStackPosition.x + xOffset * index); child.setTranslationY(mStackPosition.y); child.setScaleX(0f); child.setScaleY(0f); // Push the subsequent views out of the way, if there are subsequent views. if (index + 1 < mLayout.getChildCount()) { animationForChildAtIndex(index + 1) .translationX(mStackPosition.x + xOffset * (index + 1)) .withStiffness(SpringForce.STIFFNESS_LOW) .start(); } float xOffset = getOffsetForChainedPropertyAnimation(DynamicAnimation.TRANSLATION_X); // Scale in the new bubble, slightly delayed. animationForChild(child) .scaleX(ANIMATE_IN_STARTING_SCALE /* from */, 1f /* to */) .scaleY(ANIMATE_IN_STARTING_SCALE /* from */, 1f /* to */) .alpha(0f /* from */, 1f /* to */) .translationX( mStackPosition.x - ANIMATE_TRANSLATION_FACTOR * xOffset /* from */, mStackPosition.x /* to */) .scaleX(1f) .scaleY(1f) .withStiffness(ANIMATE_IN_STIFFNESS) .withStartDelay(mLayout.getChildCount() > 1 ? ANIMATE_IN_START_DELAY : 0) .start(); } Loading
packages/SystemUI/tests/src/com/android/systemui/bubbles/BubbleFlyoutViewTest.java +9 −10 Original line number Diff line number Diff line Loading @@ -18,7 +18,6 @@ package com.android.systemui.bubbles; import static junit.framework.Assert.assertEquals; import static junit.framework.Assert.assertNotSame; import static junit.framework.Assert.assertTrue; import static org.mockito.Mockito.verify; Loading Loading @@ -57,16 +56,19 @@ public class BubbleFlyoutViewTest extends SysuiTestCase { @Test public void testShowFlyout_isVisible() { mFlyout.showFlyout("Hello", new PointF(100, 100), 500, true, Color.WHITE, null); mFlyout.setupFlyoutStartingAsDot( "Hello", new PointF(100, 100), 500, true, Color.WHITE, null, null); mFlyout.setVisibility(View.VISIBLE); assertEquals("Hello", mFlyoutText.getText()); assertEquals(View.VISIBLE, mFlyout.getVisibility()); assertEquals(1f, mFlyoutText.getAlpha(), .01f); } @Test public void testFlyoutHide_runsCallback() { Runnable after = Mockito.mock(Runnable.class); mFlyout.showFlyout("Hello", new PointF(100, 100), 500, true, Color.WHITE, after); mFlyout.setupFlyoutStartingAsDot( "Hello", new PointF(100, 100), 500, true, Color.WHITE, null, after); mFlyout.hideFlyout(); verify(after).run(); Loading @@ -74,19 +76,16 @@ public class BubbleFlyoutViewTest extends SysuiTestCase { @Test public void testSetCollapsePercent() { mFlyout.showFlyout("Hello", new PointF(100, 100), 500, true, Color.WHITE, null); float initialTranslationZ = mFlyout.getTranslationZ(); mFlyout.setupFlyoutStartingAsDot( "Hello", new PointF(100, 100), 500, true, Color.WHITE, null, null); mFlyout.setVisibility(View.VISIBLE); mFlyout.setCollapsePercent(1f); assertEquals(0f, mFlyoutText.getAlpha(), 0.01f); assertNotSame(0f, mFlyoutText.getTranslationX()); // Should have moved to collapse. assertTrue(mFlyout.getTranslationZ() < initialTranslationZ); // Should be descending. mFlyout.setCollapsePercent(0f); assertEquals(1f, mFlyoutText.getAlpha(), 0.01f); assertEquals(0f, mFlyoutText.getTranslationX()); assertEquals(initialTranslationZ, mFlyout.getTranslationZ()); } }