Merge "Animations for new bubble while stack is collapsed"

This commit is contained in:
Lyn Han
2021-01-23 00:52:43 +00:00
committed by Android (Google) Code Review
3 changed files with 84 additions and 67 deletions

View File

@@ -131,7 +131,11 @@ public class BadgedImageView extends ImageView {
*/
public void setRenderedBubble(BubbleViewProvider bubble) {
mBubble = bubble;
showBadge();
if (mDotSuppressionFlags.contains(SuppressionFlag.BEHIND_STACK)) {
hideBadge();
} else {
showBadge();
}
mDotColor = bubble.getDotColor();
drawDot(bubble.getDotPath());
}

View File

@@ -647,10 +647,12 @@ public class BubbleStackView extends FrameLayout
} else {
// Fling the stack to the edge, and save whether or not it's going to end up on
// the left side of the screen.
final boolean oldOnLeft = mStackOnLeftOrWillBe;
mStackOnLeftOrWillBe =
mStackAnimationController.flingStackThenSpringToEdge(
viewInitialX + dx, velX, velY) <= 0;
updateBubbleIcons();
final boolean updateForCollapsedStack = oldOnLeft != mStackOnLeftOrWillBe;
updateBadgesAndZOrder(updateForCollapsedStack);
logBubbleEvent(null /* no bubble associated with bubble stack move */,
FrameworkStatsLog.BUBBLE_UICHANGED__ACTION__STACK_MOVED);
}
@@ -1481,7 +1483,7 @@ public class BubbleStackView extends FrameLayout
};
if (mIsExpanded) {
reorder.run();
updateBubbleIcons();
updateBadgesAndZOrder(false /* setBadgeForCollapsedStack */);
} else {
List<View> bubbleViews = bubbles.stream()
.map(b -> b.getIconView()).collect(Collectors.toList());
@@ -2616,27 +2618,28 @@ public class BubbleStackView extends FrameLayout
}
mStackOnLeftOrWillBe = mStackAnimationController.isStackOnLeftSide();
updateBubbleIcons();
updateBadgesAndZOrder(false /* setBadgeForCollapsedStack */);
}
/**
* Sets the appropriate Z-order, badge, and dot position for each bubble in the stack.
* Animate dot and badge changes.
*/
private void updateBubbleIcons() {
private void updateBadgesAndZOrder(boolean setBadgeForCollapsedStack) {
int bubbleCount = getBubbleCount();
for (int i = 0; i < bubbleCount; i++) {
BadgedImageView bv = (BadgedImageView) mBubbleContainer.getChildAt(i);
bv.setZ((mMaxBubbles * mBubbleElevation) - i);
if (mIsExpanded) {
// If we're not displaying vertically, we always show the badge on the left.
boolean onLeft = mPositioner.showBubblesVertically() && !mStackOnLeftOrWillBe;
bv.showDotAndBadge(onLeft);
} else if (i == 0) {
bv.showDotAndBadge(!mStackOnLeftOrWillBe);
} else {
bv.hideDotAndBadge(!mStackOnLeftOrWillBe);
} else if (setBadgeForCollapsedStack) {
if (i == 0) {
bv.showDotAndBadge(!mStackOnLeftOrWillBe);
} else {
bv.hideDotAndBadge(!mStackOnLeftOrWillBe);
}
}
}
}

View File

@@ -64,10 +64,10 @@ public class StackAnimationController extends
/** 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 animating updated bubble to top of stack. */
private static final float BUBBLE_SWAP_SCALE = 0.8f;
private static final float NEW_BUBBLE_START_SCALE = 0.5f;
private static final float NEW_BUBBLE_START_Y = 100f;
private static final long BUBBLE_SWAP_DURATION = 300L;
/**
@@ -779,48 +779,58 @@ public class StackAnimationController extends
}
public void animateReorder(List<View> bubbleViews, Runnable after) {
// After the bubble going to index 0 springs above stack, update all icons
// at the same time, to avoid visibly changing bubble order before the animation completes.
Runnable updateAllIcons = () -> {
for (int newIndex = 0; newIndex < bubbleViews.size(); newIndex++) {
View view = bubbleViews.get(newIndex);
updateBadgesAndZOrder(view, newIndex);
}
};
for (int newIndex = 0; newIndex < bubbleViews.size(); newIndex++) {
View view = bubbleViews.get(newIndex);
final int oldIndex = mLayout.indexOfChild(view);
animateSwap(view, oldIndex, newIndex, after);
animateSwap(view, oldIndex, newIndex, updateAllIcons, after);
}
}
private void animateSwap(View view, int oldIndex, int newIndex, Runnable finishReorder) {
final float newY = getStackPosition().y + newIndex * mSwapAnimationOffset;
final float swapY = newIndex == 0
? newY - mSwapAnimationOffset // Above top of stack
: newY + mSwapAnimationOffset; // Below where bubble will be
final ViewPropertyAnimator animator = view.animate()
.scaleX(BUBBLE_SWAP_SCALE)
.scaleY(BUBBLE_SWAP_SCALE)
.translationY(swapY)
private void animateSwap(View view, int oldIndex, int newIndex,
Runnable updateAllIcons, Runnable finishReorder) {
if (newIndex == oldIndex) {
// Add new bubble to index 0; move existing bubbles down
updateBadgesAndZOrder(view, newIndex);
if (newIndex == 0) {
animateInBubble(view, newIndex);
} else {
moveToFinalIndex(view, newIndex, finishReorder);
}
} else {
// Reorder existing bubbles
if (newIndex == 0) {
animateToFrontThenUpdateIcons(view, updateAllIcons, finishReorder);
} else {
moveToFinalIndex(view, newIndex, finishReorder);
}
}
}
private void animateToFrontThenUpdateIcons(View v, Runnable updateAllIcons,
Runnable finishReorder) {
final ViewPropertyAnimator animator = v.animate()
.translationY(getStackPosition().y - mSwapAnimationOffset)
.setDuration(BUBBLE_SWAP_DURATION)
.withEndAction(() -> {
finishSwapAnimation(view, oldIndex, newIndex, finishReorder);
updateAllIcons.run();
moveToFinalIndex(v, 0 /* index */, finishReorder);
});
view.setTag(R.id.reorder_animator_tag, animator);
v.setTag(R.id.reorder_animator_tag, animator);
}
private void finishSwapAnimation(View view, int oldIndex, int newIndex,
private void moveToFinalIndex(View view, int newIndex,
Runnable finishReorder) {
// At this point, swapping bubbles have the least overlap.
// Update z-index and badge visibility here for least jarring transition.
view.setZ((mMaxBubbles * mElevation) - newIndex);
BadgedImageView bv = (BadgedImageView) view;
if (oldIndex == 0 && newIndex > 0) {
bv.hideDotAndBadge(!isStackOnLeftSide());
} else if (oldIndex > 0 && newIndex == 0) {
bv.showDotAndBadge(!isStackOnLeftSide());
}
// Animate bubble back into stack, at new index and original size.
final float newY = getStackPosition().y + newIndex * mStackOffset;
final ViewPropertyAnimator animator = view.animate()
.scaleX(1f)
.scaleY(1f)
.translationY(newY)
.translationY(getStackPosition().y + newIndex * mStackOffset)
.setDuration(BUBBLE_SWAP_DURATION)
.withEndAction(() -> {
view.setTag(R.id.reorder_animator_tag, null);
@@ -829,13 +839,19 @@ public class StackAnimationController extends
view.setTag(R.id.reorder_animator_tag, animator);
}
@Override
void onChildReordered(View child, int oldIndex, int newIndex) {
if (isStackPositionSet()) {
setStackPosition(mStackPosition);
private void updateBadgesAndZOrder(View v, int index) {
v.setZ((mMaxBubbles * mElevation) - index);
BadgedImageView bv = (BadgedImageView) v;
if (index == 0) {
bv.showDotAndBadge(!isStackOnLeftSide());
} else {
bv.hideDotAndBadge(!isStackOnLeftSide());
}
}
@Override
void onChildReordered(View child, int oldIndex, int newIndex) {}
@Override
void onActiveControllerForLayout(PhysicsAnimationLayout layout) {
Resources res = layout.getResources();
@@ -943,35 +959,29 @@ public class StackAnimationController extends
}
/** Animates in the given bubble. */
private void animateInBubble(View child, int index) {
private void animateInBubble(View v, int index) {
if (!isActiveController()) {
return;
}
v.setTranslationX(mStackPosition.x);
final float yOffset =
getOffsetForChainedPropertyAnimation(DynamicAnimation.TRANSLATION_Y);
// Position the new bubble in the correct position, scaled down completely.
child.setTranslationX(mStackPosition.x);
child.setTranslationY(mStackPosition.y + yOffset * index);
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)
.translationY(mStackPosition.y + yOffset * (index + 1))
.withStiffness(SpringForce.STIFFNESS_LOW)
.start();
}
// Scale in the new bubble, slightly delayed.
animationForChild(child)
final float endY = mStackPosition.y + yOffset * index;
final float startY = endY + NEW_BUBBLE_START_Y;
v.setTranslationY(startY);
v.setScaleX(NEW_BUBBLE_START_SCALE);
v.setScaleY(NEW_BUBBLE_START_SCALE);
v.setAlpha(0f);
final ViewPropertyAnimator animator = v.animate()
.translationY(endY)
.scaleX(1f)
.scaleY(1f)
.withStiffness(ANIMATE_IN_STIFFNESS)
.withStartDelay(mLayout.getChildCount() > 1 ? ANIMATE_IN_START_DELAY : 0)
.start();
.alpha(1f)
.setDuration(BUBBLE_SWAP_DURATION)
.withEndAction(() -> {
v.setTag(R.id.reorder_animator_tag, null);
});
v.setTag(R.id.reorder_animator_tag, animator);
}
/**