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

Commit d8cb90f0 authored by Mady Mellor's avatar Mady Mellor Committed by Android (Google) Code Review
Browse files

Merge changes Iaee9aeb0,I76587cf6 into udc-qpr-dev

* changes:
  Fade the bubble bar arrow in and out when the bubble bar expands and collapses.
  Animate the bubble bar width and the bubbles within it as it expands and collapses.
parents d2d1942b 3e94d527
Loading
Loading
Loading
Loading
+12 −3
Original line number Diff line number Diff line
@@ -48,6 +48,8 @@ class BubbleBarBackground(context: TaskbarActivityContext, private val backgroun
    private var showingArrow: Boolean = false
    private var arrowDrawable: ShapeDrawable

    var width: Float = 0f

    init {
        paint.color = context.getColor(R.color.taskbar_background)
        paint.flags = Paint.ANTI_ALIAS_FLAG
@@ -59,8 +61,11 @@ class BubbleBarBackground(context: TaskbarActivityContext, private val backgroun
        pointerSize = res.getDimension(R.dimen.bubblebar_pointer_size)

        shadowAlpha =
            if (Utilities.isDarkTheme(context)) DARK_THEME_SHADOW_ALPHA
            else LIGHT_THEME_SHADOW_ALPHA
            if (Utilities.isDarkTheme(context)) {
                DARK_THEME_SHADOW_ALPHA
            } else {
                LIGHT_THEME_SHADOW_ALPHA
            }

        arrowDrawable =
            ShapeDrawable(TriangleShape.create(pointerSize, pointerSize, /* pointUp= */ true))
@@ -102,7 +107,7 @@ class BubbleBarBackground(context: TaskbarActivityContext, private val backgroun
        // Draw background.
        val radius = backgroundHeight / 2f
        canvas.drawRoundRect(
            0f,
            canvas.width.toFloat() - width,
            0f,
            canvas.width.toFloat(),
            canvas.height.toFloat(),
@@ -132,4 +137,8 @@ class BubbleBarBackground(context: TaskbarActivityContext, private val backgroun
    override fun setColorFilter(colorFilter: ColorFilter?) {
        paint.colorFilter = colorFilter
    }

    fun setArrowAlpha(alpha: Int) {
        arrowDrawable.paint.alpha = alpha
    }
}
+116 −28
Original line number Diff line number Diff line
@@ -15,6 +15,7 @@
 */
package com.android.launcher3.taskbar.bubbles;

import android.animation.Animator;
import android.animation.ValueAnimator;
import android.annotation.Nullable;
import android.content.Context;
@@ -66,8 +67,8 @@ public class BubbleBarView extends FrameLayout {
    //  if it's smaller than 5.
    private static final int MAX_BUBBLES = 5;
    private static final int ARROW_POSITION_ANIMATION_DURATION_MS = 200;
    private static final int WIDTH_ANIMATION_DURATION_MS = 200;

    private final TaskbarActivityContext mActivityContext;
    private final BubbleBarBackground mBubbleBarBackground;

    // The current bounds of all the bubble bar.
@@ -90,6 +91,10 @@ public class BubbleBarView extends FrameLayout {

    private final Rect mTempRect = new Rect();

    // An animator that represents the expansion state of the bubble bar, where 0 corresponds to the
    // collapsed state and 1 to the fully expanded state.
    private final ValueAnimator mWidthAnimator = ValueAnimator.ofFloat(0, 1);

    // We don't reorder the bubbles when they are expanded as it could be jarring for the user
    // this runnable will be populated with any reordering of the bubbles that should be applied
    // once they are collapsed.
@@ -110,7 +115,7 @@ public class BubbleBarView extends FrameLayout {

    public BubbleBarView(Context context, AttributeSet attrs, int defStyleAttr, int defStyleRes) {
        super(context, attrs, defStyleAttr, defStyleRes);
        mActivityContext = ActivityContext.lookupContext(context);
        TaskbarActivityContext activityContext = ActivityContext.lookupContext(context);

        mIconOverlapAmount = getResources().getDimensionPixelSize(R.dimen.bubblebar_icon_overlap);
        mIconSpacing = getResources().getDimensionPixelSize(R.dimen.bubblebar_icon_spacing);
@@ -118,9 +123,39 @@ public class BubbleBarView extends FrameLayout {
        mBubbleElevation = getResources().getDimensionPixelSize(R.dimen.bubblebar_icon_elevation);
        setClipToPadding(false);

        mBubbleBarBackground = new BubbleBarBackground(mActivityContext,
        mBubbleBarBackground = new BubbleBarBackground(activityContext,
                getResources().getDimensionPixelSize(R.dimen.bubblebar_size));
        setBackgroundDrawable(mBubbleBarBackground);

        mWidthAnimator.setDuration(WIDTH_ANIMATION_DURATION_MS);
        mWidthAnimator.addUpdateListener(animation -> {
            updateChildrenRenderNodeProperties();
            invalidate();
        });
        mWidthAnimator.addListener(new Animator.AnimatorListener() {
            @Override
            public void onAnimationCancel(Animator animation) {
            }

            @Override
            public void onAnimationEnd(Animator animation) {
                mBubbleBarBackground.showArrow(mIsBarExpanded);
                if (!mIsBarExpanded && mReorderRunnable != null) {
                    mReorderRunnable.run();
                    mReorderRunnable = null;
                }
                updateWidth();
            }

            @Override
            public void onAnimationRepeat(Animator animation) {
            }

            @Override
            public void onAnimationStart(Animator animation) {
                mBubbleBarBackground.showArrow(true);
            }
        });
    }

    @Override
@@ -146,7 +181,7 @@ public class BubbleBarView extends FrameLayout {
        return mBubbleBarBounds;
    }

    // TODO: (b/273592694) animate it
    // TODO: (b/280605790) animate it
    @Override
    public void addView(View child, int index, ViewGroup.LayoutParams params) {
        if (getChildCount() + 1 > MAX_BUBBLES) {
@@ -155,27 +190,55 @@ public class BubbleBarView extends FrameLayout {
            removeViewInLayout(getChildAt(getChildCount() - 2));
        }
        super.addView(child, index, params);
        updateWidth();
    }

    // TODO: (b/283309949) animate it
    @Override
    public void removeView(View view) {
        super.removeView(view);
        updateWidth();
    }

    private void updateWidth() {
        LayoutParams lp = (FrameLayout.LayoutParams) getLayoutParams();
        lp.width = (int) (mIsBarExpanded ? expandedWidth() : collapsedWidth());
        setLayoutParams(lp);
    }

    /**
     * Updates the z order, positions, and badge visibility of the bubble views in the bar based
     * on the expanded state.
     */
    // TODO: (b/273592694) animate it
    private void updateChildrenRenderNodeProperties() {
        final float widthState = (float) mWidthAnimator.getAnimatedValue();
        final float currentWidth = getWidth();
        final float expandedWidth = expandedWidth();
        final float collapsedWidth = collapsedWidth();
        int bubbleCount = getChildCount();
        final float ty = (mBubbleBarBounds.height() - mIconSize) / 2f;
        for (int i = 0; i < bubbleCount; i++) {
            BubbleView bv = (BubbleView) getChildAt(i);
            bv.setTranslationY(ty);

            // the position of the bubble when the bar is fully expanded
            final float expandedX = i * (mIconSize + mIconSpacing);
            // the position of the bubble when the bar is fully collapsed
            final float collapsedX = i * mIconOverlapAmount;

            if (mIsBarExpanded) {
                final float tx = i * (mIconSize + mIconSpacing);
                bv.setTranslationX(tx);
                // where the bubble will end up when the animation ends
                final float targetX = currentWidth - expandedWidth + expandedX;
                bv.setTranslationX(widthState * (targetX - collapsedX) + collapsedX);
                // if we're fully expanded, set the z level to 0
                if (widthState == 1f) {
                    bv.setZ(0);
                }
                bv.showBadge();
            } else {
                final float targetX = currentWidth - collapsedWidth + collapsedX;
                bv.setTranslationX(widthState * (expandedX - targetX) + targetX);
                bv.setZ((MAX_BUBBLES * mBubbleElevation) - i);
                bv.setTranslationX(i * mIconOverlapAmount);
                if (i > 0) {
                    bv.hideBadge();
                } else {
@@ -183,13 +246,33 @@ public class BubbleBarView extends FrameLayout {
                }
            }
        }

        // update the arrow position
        final float collapsedArrowPosition = arrowPositionForSelectedWhenCollapsed();
        final float expandedArrowPosition = arrowPositionForSelectedWhenExpanded();
        final float interpolatedWidth =
                widthState * (expandedWidth - collapsedWidth) + collapsedWidth;
        if (mIsBarExpanded) {
            // when the bar is expanding, the selected bubble is always the first, so the arrow
            // always shifts with the interpolated width.
            final float arrowPosition = currentWidth - interpolatedWidth + collapsedArrowPosition;
            mBubbleBarBackground.setArrowPosition(arrowPosition);
        } else {
            final float targetPosition = currentWidth - collapsedWidth + collapsedArrowPosition;
            final float arrowPosition =
                    targetPosition + widthState * (expandedArrowPosition - targetPosition);
            mBubbleBarBackground.setArrowPosition(arrowPosition);
        }

        mBubbleBarBackground.setArrowAlpha((int) (255 * widthState));
        mBubbleBarBackground.setWidth(interpolatedWidth);
    }

    /**
     * Reorders the views to match the provided list.
     */
    public void reorder(List<BubbleView> viewOrder) {
        if (isExpanded()) {
        if (isExpanded() || mWidthAnimator.isRunning()) {
            mReorderRunnable = () -> doReorder(viewOrder);
        } else {
            doReorder(viewOrder);
@@ -249,6 +332,16 @@ public class BubbleBarView extends FrameLayout {
        }
    }

    private float arrowPositionForSelectedWhenExpanded() {
        final int index = indexOfChild(mSelectedBubbleView);
        return getPaddingStart() + index * (mIconSize + mIconSpacing) + mIconSize / 2f;
    }

    private float arrowPositionForSelectedWhenCollapsed() {
        final int index = indexOfChild(mSelectedBubbleView);
        return getPaddingStart() + index * (mIconOverlapAmount) + mIconSize / 2f;
    }

    @Override
    public void setOnClickListener(View.OnClickListener listener) {
        mOnClickListener = listener;
@@ -266,18 +359,16 @@ public class BubbleBarView extends FrameLayout {
    /**
     * Sets whether the bubble bar is expanded or collapsed.
     */
    // TODO: (b/273592694) animate it
    public void setExpanded(boolean isBarExpanded) {
        if (mIsBarExpanded != isBarExpanded) {
            mIsBarExpanded = isBarExpanded;
            updateArrowForSelected(/* shouldAnimate= */ false);
            setOrUnsetClickListener();
            if (!isBarExpanded && mReorderRunnable != null) {
                mReorderRunnable.run();
                mReorderRunnable = null;
            if (isBarExpanded) {
                mWidthAnimator.start();
            } else {
                mWidthAnimator.reverse();
            }
            mBubbleBarBackground.showArrow(mIsBarExpanded);
            requestLayout(); // trigger layout to reposition views & update size for expansion
        }
    }

@@ -288,19 +379,16 @@ public class BubbleBarView extends FrameLayout {
        return mIsBarExpanded;
    }

    @Override
    protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
    private float expandedWidth() {
        final int childCount = getChildCount();
        final float iconWidth = mIsBarExpanded
                ? (childCount * (mIconSize + mIconSpacing))
                : mIconSize + ((childCount - 1) * mIconOverlapAmount);
        final int totalWidth = (int) iconWidth + getPaddingStart() + getPaddingEnd();
        setMeasuredDimension(totalWidth, MeasureSpec.getSize(heightMeasureSpec));

        for (int i = 0; i < childCount; i++) {
            View child = getChildAt(i);
            measureChild(child, (int) mIconSize, (int) mIconSize);
        final int horizontalPadding = getPaddingStart() + getPaddingEnd();
        return childCount * (mIconSize + mIconSpacing) + horizontalPadding;
    }

    private float collapsedWidth() {
        final int childCount = getChildCount();
        final int horizontalPadding = getPaddingStart() + getPaddingEnd();
        return mIconSize + ((childCount - 1) * mIconOverlapAmount) + horizontalPadding;
    }

    /**