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

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

Merge "Consecutive flyout animation"

parents 527796c2 048094f6
Loading
Loading
Loading
Loading
+63 −29
Original line number Diff line number Diff line
@@ -18,6 +18,8 @@ package com.android.systemui.bubbles;

import static android.graphics.Paint.ANTI_ALIAS_FLAG;
import static android.graphics.Paint.FILTER_BITMAP_FLAG;
import static com.android.systemui.Interpolators.ALPHA_IN;
import static com.android.systemui.Interpolators.ALPHA_OUT;

import android.animation.ArgbEvaluator;
import android.content.Context;
@@ -56,6 +58,11 @@ public class BubbleFlyoutView extends FrameLayout {
    /** Max width of the flyout, in terms of percent of the screen width. */
    private static final float FLYOUT_MAX_WIDTH_PERCENT = .6f;

    /** Translation Y of fade animation. */
    private static final float FLYOUT_FADE_Y = 40f;

    private static final long FLYOUT_FADE_DURATION = 200L;

    private final int mFlyoutPadding;
    private final int mFlyoutSpaceFromBubble;
    private final int mPointerSize;
@@ -104,6 +111,9 @@ public class BubbleFlyoutView extends FrameLayout {
    /** The bounds of the flyout background, kept up to date as it transitions to the 'new' dot. */
    private final RectF mBgRect = new RectF();

    /** The y position of the flyout, relative to the top of the screen. */
    private float mFlyoutY = 0f;

    /**
     * Percent progress in the transition from flyout to 'new' dot. These two values are the inverse
     * of each other (if we're 40% transitioned to the dot, we're 60% flyout), but it makes the code
@@ -221,18 +231,33 @@ public class BubbleFlyoutView extends FrameLayout {
        mSenderText.setTextSize(TypedValue.COMPLEX_UNIT_PX, newFontSize);
    }

    /** Configures the flyout, collapsed into to dot form. */
    void setupFlyoutStartingAsDot(
            Bubble.FlyoutMessage flyoutMessage,
            PointF stackPos,
            float parentWidth,
            boolean arrowPointingLeft,
            int dotColor,
            @Nullable Runnable onLayoutComplete,
            @Nullable Runnable onHide,
            float[] dotCenter,
            boolean hideDot) {
    /*
     * Fade animation for consecutive flyouts.
     */
    void animateUpdate(Bubble.FlyoutMessage flyoutMessage, float parentWidth, float stackY) {
        fade(false /* in */);
        updateFlyoutMessage(flyoutMessage, parentWidth);
        // Wait for TextViews to layout with updated height.
        post(() -> {
            mFlyoutY = stackY + (mBubbleSize - mFlyoutTextContainer.getHeight()) / 2f;
            fade(true /* in */);
        });
    }

    private void fade(boolean in) {
        setAlpha(in ? 0f : 1f);
        setTranslationY(in ? mFlyoutY : mFlyoutY + FLYOUT_FADE_Y);
        animate()
                .alpha(in ? 1f : 0f)
                .setDuration(FLYOUT_FADE_DURATION)
                .setInterpolator(in ? ALPHA_IN : ALPHA_OUT);
        animate()
                .translationY(in ? mFlyoutY : mFlyoutY - FLYOUT_FADE_Y)
                .setDuration(FLYOUT_FADE_DURATION)
                .setInterpolator(in ? ALPHA_IN : ALPHA_OUT);
    }

    private void updateFlyoutMessage(Bubble.FlyoutMessage flyoutMessage, float parentWidth) {
        final Drawable senderAvatar = flyoutMessage.senderAvatar;
        if (senderAvatar != null && flyoutMessage.isGroupChat) {
            mSenderAvatar.setVisibility(VISIBLE);
@@ -256,6 +281,27 @@ public class BubbleFlyoutView extends FrameLayout {
            mSenderText.setVisibility(GONE);
        }

        // 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
        // TextView being the desired width + extra padding).
        mMessageText.setMaxWidth(maxTextViewWidth);
        mMessageText.setText(flyoutMessage.message);
    }

    /** Configures the flyout, collapsed into dot form. */
    void setupFlyoutStartingAsDot(
            Bubble.FlyoutMessage flyoutMessage,
            PointF stackPos,
            float parentWidth,
            boolean arrowPointingLeft,
            int dotColor,
            @Nullable Runnable onLayoutComplete,
            @Nullable Runnable onHide,
            float[] dotCenter,
            boolean hideDot)  {

        updateFlyoutMessage(flyoutMessage, parentWidth);

        mArrowPointingLeft = arrowPointingLeft;
        mDotColor = dotColor;
        mOnHide = onHide;
@@ -263,24 +309,12 @@ public class BubbleFlyoutView extends FrameLayout {

        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
        // TextView being the desired width + extra padding).
        mMessageText.setMaxWidth(maxTextViewWidth);
        mMessageText.setText(flyoutMessage.message);

        // Wait for the TextView to lay out so we know its line count.
        // Wait for TextViews to layout with updated height.
        post(() -> {
            float restingTranslationY;
            // Multi line flyouts get top-aligned to the bubble.
            if (mMessageText.getLineCount() > 1) {
                restingTranslationY = stackPos.y + mBubbleIconTopPadding;
            } else {
                // Single line flyouts are vertically centered with respect to the bubble.
                restingTranslationY =
            // Flyout is vertically centered with respect to the bubble.
            mFlyoutY =
                    stackPos.y + (mBubbleSize - mFlyoutTextContainer.getHeight()) / 2f;
            }
            setTranslationY(restingTranslationY);
            setTranslationY(mFlyoutY);

            // Calculate the translation required to position the flyout next to the bubble stack,
            // with the desired padding.
@@ -300,7 +334,7 @@ public class BubbleFlyoutView extends FrameLayout {
            final float dotPositionY = stackPos.y + mDotCenter[1] - adjustmentForScaleAway;

            final float distanceFromFlyoutLeftToDotCenterX = mRestingTranslationX - dotPositionX;
            final float distanceFromLayoutTopToDotCenterY = restingTranslationY - dotPositionY;
            final float distanceFromLayoutTopToDotCenterY = mFlyoutY - dotPositionY;

            mTranslationXWhenDot = -distanceFromFlyoutLeftToDotCenterX;
            mTranslationYWhenDot = -distanceFromLayoutTopToDotCenterY;
+30 −23
Original line number Diff line number Diff line
@@ -2171,11 +2171,7 @@ public class BubbleStackView extends FrameLayout
        return getStatusBarHeight() + mBubbleSize + mBubblePaddingTop;
    }

    /**
     * Animates in the flyout for the given bubble, if available, and then hides it after some time.
     */
    @VisibleForTesting
    void animateInFlyoutForBubble(Bubble bubble) {
    private boolean shouldShowFlyout(Bubble bubble) {
        Bubble.FlyoutMessage flyoutMessage = bubble.getFlyoutMessage();
        final BadgedImageView bubbleView = bubble.getIconView();
        if (flyoutMessage == null
@@ -2187,11 +2183,22 @@ public class BubbleStackView extends FrameLayout
                || mIsGestureInProgress
                || mBubbleToExpandAfterFlyoutCollapse != null
                || bubbleView == null) {
            if (bubbleView != null) {
            if (bubbleView != null && mFlyout.getVisibility() != VISIBLE) {
                bubbleView.removeDotSuppressionFlag(BadgedImageView.SuppressionFlag.FLYOUT_VISIBLE);
            }
            // 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, or if bubble view is null.
            return false;
        }
        return true;
    }

    /**
     * Animates in the flyout for the given bubble, if available, and then hides it after some time.
     */
    @VisibleForTesting
    void animateInFlyoutForBubble(Bubble bubble) {
        if (!shouldShowFlyout(bubble)) {
            return;
        }

@@ -2209,25 +2216,22 @@ public class BubbleStackView extends FrameLayout
            }

            // Stop suppressing the dot now that the flyout has morphed into the dot.
            bubbleView.removeDotSuppressionFlag(
            bubble.getIconView().removeDotSuppressionFlag(
                    BadgedImageView.SuppressionFlag.FLYOUT_VISIBLE);

            mFlyout.setVisibility(INVISIBLE);

            // Hide the stack after a delay, if needed.
            updateTemporarilyInvisibleAnimation(false /* hideImmediately */);
        };
        mFlyout.setVisibility(INVISIBLE);

        // Suppress the dot when we are animating the flyout.
        bubbleView.addDotSuppressionFlag(
        bubble.getIconView().addDotSuppressionFlag(
                BadgedImageView.SuppressionFlag.FLYOUT_VISIBLE);

        // Start flyout expansion. 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
            // layout.
            if (isExpanded()) {
            if (isExpanded() || bubble.getIconView() == null) {
                return;
            }
            final Runnable expandFlyoutAfterDelay = () -> {
@@ -2244,11 +2248,13 @@ public class BubbleStackView extends FrameLayout
                mFlyout.postDelayed(mAnimateInFlyout, 200);
            };

            if (bubble.getIconView() == null) {
                return;
            }

            mFlyout.setupFlyoutStartingAsDot(flyoutMessage,
            if (mFlyout.getVisibility() == View.VISIBLE) {
                mFlyout.animateUpdate(bubble.getFlyoutMessage(), getWidth(),
                        mStackAnimationController.getStackPosition().y);
            } else {
                mFlyout.setVisibility(INVISIBLE);
                mFlyout.setupFlyoutStartingAsDot(bubble.getFlyoutMessage(),
                        mStackAnimationController.getStackPosition(), getWidth(),
                        mStackAnimationController.isStackOnLeftSide(),
                        bubble.getIconView().getDotColor() /* dotColor */,
@@ -2256,6 +2262,7 @@ public class BubbleStackView extends FrameLayout
                        mAfterFlyoutHidden,
                        bubble.getIconView().getDotCenter(),
                        !bubble.showDot());
            }
            mFlyout.bringToFront();
        });
        mFlyout.removeCallbacks(mHideFlyout);