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.
This commit is contained in:
Mady Mellor
2023-06-27 17:35:58 +00:00
committed by Android (Google) Code Review
2 changed files with 127 additions and 30 deletions
@@ -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
}
}
@@ -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);
bv.setZ(0);
// 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));
final int horizontalPadding = getPaddingStart() + getPaddingEnd();
return childCount * (mIconSize + mIconSpacing) + horizontalPadding;
}
for (int i = 0; i < childCount; i++) {
View child = getChildAt(i);
measureChild(child, (int) mIconSize, (int) mIconSize);
}
private float collapsedWidth() {
final int childCount = getChildCount();
final int horizontalPadding = getPaddingStart() + getPaddingEnd();
return mIconSize + ((childCount - 1) * mIconOverlapAmount) + horizontalPadding;
}
/**