diff --git a/quickstep/src/com/android/launcher3/taskbar/bubbles/BubbleBarBackground.kt b/quickstep/src/com/android/launcher3/taskbar/bubbles/BubbleBarBackground.kt index 1e3f4f13f2..aa2b29de39 100644 --- a/quickstep/src/com/android/launcher3/taskbar/bubbles/BubbleBarBackground.kt +++ b/quickstep/src/com/android/launcher3/taskbar/bubbles/BubbleBarBackground.kt @@ -50,6 +50,22 @@ class BubbleBarBackground(context: TaskbarActivityContext, private val backgroun 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 { paint.color = context.getColor(R.color.taskbar_background) paint.flags = Paint.ANTI_ALIAS_FLAG @@ -106,15 +122,9 @@ class BubbleBarBackground(context: TaskbarActivityContext, private val backgroun // Draw background. val radius = backgroundHeight / 2f - canvas.drawRoundRect( - canvas.width.toFloat() - width, - 0f, - canvas.width.toFloat(), - canvas.height.toFloat(), - radius, - radius, - paint - ) + val left = if (anchorLeft) 0f else canvas.width.toFloat() - width + val right = if (anchorLeft) width else canvas.width.toFloat() + canvas.drawRoundRect(left, 0f, right, canvas.height.toFloat(), radius, radius, paint) if (showingArrow) { // Draw arrow. diff --git a/quickstep/src/com/android/launcher3/taskbar/bubbles/BubbleBarView.java b/quickstep/src/com/android/launcher3/taskbar/bubbles/BubbleBarView.java index ec9f4e5567..5ca2991d6b 100644 --- a/quickstep/src/com/android/launcher3/taskbar/bubbles/BubbleBarView.java +++ b/quickstep/src/com/android/launcher3/taskbar/bubbles/BubbleBarView.java @@ -197,6 +197,16 @@ public class BubbleBarView extends FrameLayout { 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. */ @@ -275,18 +285,31 @@ public class BubbleBarView extends FrameLayout { int bubbleCount = getChildCount(); final float ty = (mBubbleBarBounds.height() - mIconSize) / 2f; final boolean animate = getVisibility() == VISIBLE; + final boolean onLeft = isOnLeft(); 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); + final float expandedX; // 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 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 - final float targetX = currentWidth - expandedWidth + expandedX; + final float targetX = expandedX + expandedBarShift; bv.setTranslationX(widthState * (targetX - collapsedX) + collapsedX); // if we're fully expanded, set the z level to 0 or to bubble elevation if dragged if (widthState == 1f) { @@ -296,7 +319,9 @@ public class BubbleBarView extends FrameLayout { bv.setBehindStack(false, animate); bv.setAlpha(1); } 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.setZ((MAX_BUBBLES * mBubbleElevation) - i); // 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 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); + final float arrowPosition; + if (onLeft) { + float interpolatedShift = (expandedArrowPosition - collapsedArrowPosition) * widthState; + arrowPosition = collapsedArrowPosition + interpolatedShift; } else { - final float targetPosition = currentWidth - collapsedWidth + collapsedArrowPosition; - final float arrowPosition = - targetPosition + widthState * (expandedArrowPosition - targetPosition); - mBubbleBarBackground.setArrowPosition(arrowPosition); + if (mIsBarExpanded) { + // when the bar is expanding, the selected bubble is always the first, so the arrow + // always shifts with the interpolated width. + arrowPosition = currentWidth - interpolatedWidth + collapsedArrowPosition; + } else { + final float targetPosition = currentWidth - collapsedWidth + collapsedArrowPosition; + arrowPosition = + targetPosition + widthState * (expandedArrowPosition - targetPosition); + } } - + mBubbleBarBackground.setArrowPosition(arrowPosition); mBubbleBarBackground.setArrowAlpha((int) (255 * widthState)); mBubbleBarBackground.setWidth(interpolatedWidth); } @@ -394,9 +423,8 @@ public class BubbleBarView extends FrameLayout { Log.w(TAG, "trying to update selection arrow without a selected view!"); return; } - final int index = indexOfChild(mSelectedBubbleView); // 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) { final float currentArrowPosition = mBubbleBarBackground.getArrowPositionX(); @@ -416,12 +444,27 @@ public class BubbleBarView extends FrameLayout { private float arrowPositionForSelectedWhenExpanded() { 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() { 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 diff --git a/quickstep/src/com/android/launcher3/taskbar/bubbles/BubbleBarViewController.java b/quickstep/src/com/android/launcher3/taskbar/bubbles/BubbleBarViewController.java index 065dd58f16..6bb7b04c98 100644 --- a/quickstep/src/com/android/launcher3/taskbar/bubbles/BubbleBarViewController.java +++ b/quickstep/src/com/android/launcher3/taskbar/bubbles/BubbleBarViewController.java @@ -21,6 +21,7 @@ import static android.view.View.VISIBLE; import android.graphics.Point; import android.graphics.Rect; import android.util.Log; +import android.view.Gravity; import android.view.MotionEvent; import android.view.View; import android.widget.FrameLayout; @@ -289,7 +290,8 @@ public class BubbleBarViewController { */ public void addBubble(BubbleBarItem b) { 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); mBubbleDragController.setupBubbleView(b.getView()); } else {