Donate to e Foundation | Murena handsets with /e/OS | Own a part of Murena! Learn more

Commit 03622914 authored by Lyn Han's avatar Lyn Han Committed by Android (Google) Code Review
Browse files

Merge "Animations for new bubble while stack is collapsed"

parents f8fcda56 f609cbd8
Loading
Loading
Loading
Loading
+5 −1
Original line number Diff line number Diff line
@@ -131,7 +131,11 @@ public class BadgedImageView extends ImageView {
     */
    public void setRenderedBubble(BubbleViewProvider bubble) {
        mBubble = bubble;
        if (mDotSuppressionFlags.contains(SuppressionFlag.BEHIND_STACK)) {
            hideBadge();
        } else {
            showBadge();
        }
        mDotColor = bubble.getDotColor();
        drawDot(bubble.getDotPath());
    }
+12 −9
Original line number Diff line number Diff line
@@ -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,30 +2618,31 @@ 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) {
            } else if (setBadgeForCollapsedStack) {
                if (i == 0) {
                    bv.showDotAndBadge(!mStackOnLeftOrWillBe);
                } else {
                    bv.hideDotAndBadge(!mStackOnLeftOrWillBe);
                }
            }
        }
    }

    private void updatePointerPosition() {
        if (mExpandedBubble == null || mExpandedBubble.getExpandedView() == null) {
+67 −57
Original line number Diff line number Diff line
@@ -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);
    }

    /**