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

Commit feb3a68d authored by Ats Jenk's avatar Ats Jenk
Browse files

Bubble bar expland/collapse animation when on left

Fix bubble bar collapse and expand animations when bubble bar is pinned
to the left.
When system language is set to an RTL language, bubble bar gets pinned
to the left edge of the screen.
Bubble bar should expand to the right and collapse to the left. Bubbles
in the bubble bar should be ordered from right to left. The most recent
bubble should be at the right in the bubble bar.

Known issue:
  - when the most recent bubble is removed, the arrow animates to a new
    position, but the background is not animated, resulting in arrow
    getting detached from the container. Will be fixed by animating the
    background.

Flag: LEGACY persist.wm.debug.bubble_bar DEVELOPMENT
Bug: 273310265
Test: with LTR language set as the system language
  - expand bubble bar with 1 bubble
  - expand bubble bar with multiple bubbles
  - select second bubble, observe after collapse it is set as first
  - dismiss a bubble from bubble bar by dragging
Test: with RTL language set as the system language
  - expand bubble bar with 1 bubble
  - expand bubble bar with multiple bubbles
  - select second bubble, observe after collapse it is set as first
  - dismiss a bubble from bubble bar by dragging

Change-Id: Ic46a5b1a6e45ad225ba509a61147cc6a8cdd0397
parent 10ac692f
Loading
Loading
Loading
Loading
+19 −9
Original line number Original line Diff line number Diff line
@@ -50,6 +50,22 @@ class BubbleBarBackground(context: TaskbarActivityContext, private val backgroun


    var width: Float = 0f
    var width: Float = 0f


    /**
     * Set whether the drawable is anchored to the left or right edge of the container.
     *
     * When `anchorLeft` is set to `true`, drawable left edge aligns up with the container left
     * edge. Drawable can be drawn outside container bounds on the right edge. When it is set to
     * `false` (the default), drawable right edge aligns up with the container right edge. Drawable
     * can be drawn outside container bounds on the left edge.
     */
    var anchorLeft: Boolean = false
        set(value) {
            if (field != value) {
                field = value
                invalidateSelf()
            }
        }

    init {
    init {
        paint.color = context.getColor(R.color.taskbar_background)
        paint.color = context.getColor(R.color.taskbar_background)
        paint.flags = Paint.ANTI_ALIAS_FLAG
        paint.flags = Paint.ANTI_ALIAS_FLAG
@@ -106,15 +122,9 @@ class BubbleBarBackground(context: TaskbarActivityContext, private val backgroun


        // Draw background.
        // Draw background.
        val radius = backgroundHeight / 2f
        val radius = backgroundHeight / 2f
        canvas.drawRoundRect(
        val left = if (anchorLeft) 0f else canvas.width.toFloat() - width
            canvas.width.toFloat() - width,
        val right = if (anchorLeft) width else canvas.width.toFloat()
            0f,
        canvas.drawRoundRect(left, 0f, right, canvas.height.toFloat(), radius, radius, paint)
            canvas.width.toFloat(),
            canvas.height.toFloat(),
            radius,
            radius,
            paint
        )


        if (showingArrow) {
        if (showingArrow) {
            // Draw arrow.
            // Draw arrow.
+61 −18
Original line number Original line Diff line number Diff line
@@ -197,6 +197,16 @@ public class BubbleBarView extends FrameLayout {
        updateChildrenRenderNodeProperties();
        updateChildrenRenderNodeProperties();
    }
    }


    @Override
    public void onRtlPropertiesChanged(int layoutDirection) {
        // TODO(b/273310265): set this based on bubble bar position and not LTR or RTL
        mBubbleBarBackground.setAnchorLeft(layoutDirection == LAYOUT_DIRECTION_RTL);
    }

    private boolean isOnLeft() {
        return getLayoutDirection() == LAYOUT_DIRECTION_RTL;
    }

    /**
    /**
     * Updates the bounds with translation that may have been applied and returns the result.
     * Updates the bounds with translation that may have been applied and returns the result.
     */
     */
@@ -275,18 +285,31 @@ public class BubbleBarView extends FrameLayout {
        int bubbleCount = getChildCount();
        int bubbleCount = getChildCount();
        final float ty = (mBubbleBarBounds.height() - mIconSize) / 2f;
        final float ty = (mBubbleBarBounds.height() - mIconSize) / 2f;
        final boolean animate = getVisibility() == VISIBLE;
        final boolean animate = getVisibility() == VISIBLE;
        final boolean onLeft = isOnLeft();
        for (int i = 0; i < bubbleCount; i++) {
        for (int i = 0; i < bubbleCount; i++) {
            BubbleView bv = (BubbleView) getChildAt(i);
            BubbleView bv = (BubbleView) getChildAt(i);
            bv.setTranslationY(ty);
            bv.setTranslationY(ty);


            // the position of the bubble when the bar is fully expanded
            // the position of the bubble when the bar is fully expanded
            final float expandedX = i * (mIconSize + mIconSpacing);
            final float expandedX;
            // the position of the bubble when the bar is fully collapsed
            // the position of the bubble when the bar is fully collapsed
            final float collapsedX = i == 0 ? 0 : mIconOverlapAmount;
            final float collapsedX;
            if (onLeft) {
                // If bar is on the left, bubbles are ordered right to left
                expandedX = (bubbleCount - i - 1) * (mIconSize + mIconSpacing);
                // Shift the first bubble only if there are more bubbles in addition to overflow
                collapsedX = i == 0 && bubbleCount > 2 ? mIconOverlapAmount : 0;
            } else {
                // Bubbles ordered left to right, don't move the first bubble
                expandedX = i * (mIconSize + mIconSpacing);
                collapsedX = i == 0 ? 0 : mIconOverlapAmount;
            }


            if (mIsBarExpanded) {
            if (mIsBarExpanded) {
                // If bar is on the right, account for bubble bar expanding and shifting left
                final float expandedBarShift = onLeft ? 0 : currentWidth - expandedWidth;
                // where the bubble will end up when the animation ends
                // where the bubble will end up when the animation ends
                final float targetX = currentWidth - expandedWidth + expandedX;
                final float targetX = expandedX + expandedBarShift;
                bv.setTranslationX(widthState * (targetX - collapsedX) + collapsedX);
                bv.setTranslationX(widthState * (targetX - collapsedX) + collapsedX);
                // if we're fully expanded, set the z level to 0 or to bubble elevation if dragged
                // if we're fully expanded, set the z level to 0 or to bubble elevation if dragged
                if (widthState == 1f) {
                if (widthState == 1f) {
@@ -296,7 +319,9 @@ public class BubbleBarView extends FrameLayout {
                bv.setBehindStack(false, animate);
                bv.setBehindStack(false, animate);
                bv.setAlpha(1);
                bv.setAlpha(1);
            } else {
            } else {
                final float targetX = currentWidth - collapsedWidth + collapsedX;
                // If bar is on the right, account for bubble bar expanding and shifting left
                final float collapsedBarShift = onLeft ? 0 : currentWidth - collapsedWidth;
                final float targetX = collapsedX + collapsedBarShift;
                bv.setTranslationX(widthState * (expandedX - targetX) + targetX);
                bv.setTranslationX(widthState * (expandedX - targetX) + targetX);
                bv.setZ((MAX_BUBBLES * mBubbleElevation) - i);
                bv.setZ((MAX_BUBBLES * mBubbleElevation) - i);
                // If we're not the first bubble we're behind the stack
                // If we're not the first bubble we're behind the stack
@@ -318,18 +343,22 @@ public class BubbleBarView extends FrameLayout {
        final float expandedArrowPosition = arrowPositionForSelectedWhenExpanded();
        final float expandedArrowPosition = arrowPositionForSelectedWhenExpanded();
        final float interpolatedWidth =
        final float interpolatedWidth =
                widthState * (expandedWidth - collapsedWidth) + collapsedWidth;
                widthState * (expandedWidth - collapsedWidth) + collapsedWidth;
        final float arrowPosition;
        if (onLeft) {
            float interpolatedShift = (expandedArrowPosition - collapsedArrowPosition) * widthState;
            arrowPosition = collapsedArrowPosition + interpolatedShift;
        } else {
            if (mIsBarExpanded) {
            if (mIsBarExpanded) {
                // when the bar is expanding, the selected bubble is always the first, so the arrow
                // when the bar is expanding, the selected bubble is always the first, so the arrow
                // always shifts with the interpolated width.
                // always shifts with the interpolated width.
            final float arrowPosition = currentWidth - interpolatedWidth + collapsedArrowPosition;
                arrowPosition = currentWidth - interpolatedWidth + collapsedArrowPosition;
            mBubbleBarBackground.setArrowPosition(arrowPosition);
            } else {
            } else {
                final float targetPosition = currentWidth - collapsedWidth + collapsedArrowPosition;
                final float targetPosition = currentWidth - collapsedWidth + collapsedArrowPosition;
            final float arrowPosition =
                arrowPosition =
                        targetPosition + widthState * (expandedArrowPosition - targetPosition);
                        targetPosition + widthState * (expandedArrowPosition - targetPosition);
            mBubbleBarBackground.setArrowPosition(arrowPosition);
            }
            }

        }
        mBubbleBarBackground.setArrowPosition(arrowPosition);
        mBubbleBarBackground.setArrowAlpha((int) (255 * widthState));
        mBubbleBarBackground.setArrowAlpha((int) (255 * widthState));
        mBubbleBarBackground.setWidth(interpolatedWidth);
        mBubbleBarBackground.setWidth(interpolatedWidth);
    }
    }
@@ -394,9 +423,8 @@ public class BubbleBarView extends FrameLayout {
            Log.w(TAG, "trying to update selection arrow without a selected view!");
            Log.w(TAG, "trying to update selection arrow without a selected view!");
            return;
            return;
        }
        }
        final int index = indexOfChild(mSelectedBubbleView);
        // Find the center of the bubble when it's expanded, set the arrow position to it.
        // Find the center of the bubble when it's expanded, set the arrow position to it.
        final float tx = getPaddingStart() + index * (mIconSize + mIconSpacing) + mIconSize / 2f;
        final float tx = arrowPositionForSelectedWhenExpanded();


        if (shouldAnimate) {
        if (shouldAnimate) {
            final float currentArrowPosition = mBubbleBarBackground.getArrowPositionX();
            final float currentArrowPosition = mBubbleBarBackground.getArrowPositionX();
@@ -416,12 +444,27 @@ public class BubbleBarView extends FrameLayout {


    private float arrowPositionForSelectedWhenExpanded() {
    private float arrowPositionForSelectedWhenExpanded() {
        final int index = indexOfChild(mSelectedBubbleView);
        final int index = indexOfChild(mSelectedBubbleView);
        return getPaddingStart() + index * (mIconSize + mIconSpacing) + mIconSize / 2f;
        final int bubblePosition;
        if (isOnLeft()) {
            // Bubble positions are reversed. First bubble is on the right.
            bubblePosition = getChildCount() - index - 1;
        } else {
            bubblePosition = index;
        }
        return getPaddingStart() + bubblePosition * (mIconSize + mIconSpacing) + mIconSize / 2f;
    }
    }


    private float arrowPositionForSelectedWhenCollapsed() {
    private float arrowPositionForSelectedWhenCollapsed() {
        final int index = indexOfChild(mSelectedBubbleView);
        final int index = indexOfChild(mSelectedBubbleView);
        return getPaddingStart() + index * (mIconOverlapAmount) + mIconSize / 2f;
        final int bubblePosition;
        if (isOnLeft()) {
            // Bubble positions are reversed. First bubble may be shifted, if there are more
            // bubbles than the current bubble and overflow.
            bubblePosition = index == 0 && getChildCount() > 2 ? 1 : 0;
        } else {
            bubblePosition = index;
        }
        return getPaddingStart() + bubblePosition * (mIconOverlapAmount) + mIconSize / 2f;
    }
    }


    @Override
    @Override
+3 −1
Original line number Original line Diff line number Diff line
@@ -21,6 +21,7 @@ import static android.view.View.VISIBLE;
import android.graphics.Point;
import android.graphics.Point;
import android.graphics.Rect;
import android.graphics.Rect;
import android.util.Log;
import android.util.Log;
import android.view.Gravity;
import android.view.MotionEvent;
import android.view.MotionEvent;
import android.view.View;
import android.view.View;
import android.widget.FrameLayout;
import android.widget.FrameLayout;
@@ -289,7 +290,8 @@ public class BubbleBarViewController {
     */
     */
    public void addBubble(BubbleBarItem b) {
    public void addBubble(BubbleBarItem b) {
        if (b != null) {
        if (b != null) {
            mBarView.addView(b.getView(), 0, new FrameLayout.LayoutParams(mIconSize, mIconSize));
            mBarView.addView(b.getView(), 0,
                    new FrameLayout.LayoutParams(mIconSize, mIconSize, Gravity.LEFT));
            b.getView().setOnClickListener(mBubbleClickListener);
            b.getView().setOnClickListener(mBubbleClickListener);
            mBubbleDragController.setupBubbleView(b.getView());
            mBubbleDragController.setupBubbleView(b.getView());
        } else {
        } else {