Merge "Animations for new bubble while stack is collapsed"
This commit is contained in:
@@ -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());
|
||||
}
|
||||
|
||||
@@ -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);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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);
|
||||
}
|
||||
|
||||
/**
|
||||
|
||||
Reference in New Issue
Block a user