Animate bubble bar icon sizes and padding

Added animation for the updates of bubble bar icon sizes and padding

Fixes: 335575529
Flag: com.android.wm.shell.enable_bubble_bar DEVELOPMENT
Test: BubbleBarViewAnimatorTest
Change-Id: I07d66b3a9d565f4fa6040c3c4bf4866e4d81a3a6
diff --git a/quickstep/res/layout/transient_taskbar.xml b/quickstep/res/layout/transient_taskbar.xml
index 7c55bf8..f3c3383 100644
--- a/quickstep/res/layout/transient_taskbar.xml
+++ b/quickstep/res/layout/transient_taskbar.xml
@@ -45,8 +45,6 @@
         android:layout_gravity="bottom|end"
         android:layout_marginHorizontal="@dimen/transient_taskbar_bottom_margin"
         android:paddingTop="@dimen/bubblebar_pointer_visible_size"
-        android:paddingEnd="@dimen/taskbar_icon_spacing"
-        android:paddingStart="@dimen/taskbar_icon_spacing"
         android:visibility="gone"
         android:gravity="center"
         android:clipChildren="false"
diff --git a/quickstep/src/com/android/launcher3/taskbar/bubbles/BubbleBarBackground.kt b/quickstep/src/com/android/launcher3/taskbar/bubbles/BubbleBarBackground.kt
index bc13c89..25939e1 100644
--- a/quickstep/src/com/android/launcher3/taskbar/bubbles/BubbleBarBackground.kt
+++ b/quickstep/src/com/android/launcher3/taskbar/bubbles/BubbleBarBackground.kt
@@ -123,22 +123,23 @@
         )
         // Create background path
         val backgroundPath = Path()
+        val topOffset = backgroundHeight - bounds.height().toFloat()
         val radius = backgroundHeight / 2f
         val left = bounds.left + (if (anchorLeft) 0f else bounds.width().toFloat() - width)
         val right = bounds.left + (if (anchorLeft) width else bounds.width().toFloat())
-        val top = bounds.top + arrowVisibleHeight
+        val top = bounds.top - topOffset + arrowVisibleHeight
+
         val bottom = bounds.top + bounds.height().toFloat()
         backgroundPath.addRoundRect(left, top, right, bottom, radius, radius, Path.Direction.CW)
-        addArrowPathIfNeeded(backgroundPath)
+        addArrowPathIfNeeded(backgroundPath, topOffset)
 
         // Draw background.
         canvas.drawPath(backgroundPath, fillPaint)
         canvas.drawPath(backgroundPath, strokePaint)
-
         canvas.restore()
     }
 
-    private fun addArrowPathIfNeeded(sourcePath: Path) {
+    private fun addArrowPathIfNeeded(sourcePath: Path, topOffset: Float) {
         if (!showingArrow || arrowHeightFraction <= 0) return
         val arrowPath = Path()
         RoundedArrowDrawable.addDownPointingRoundedTriangleToPath(
@@ -153,7 +154,7 @@
         arrowPath.transform(pathTransform)
         // shift to arrow position
         val arrowStart = bounds.left + arrowPositionX - (arrowWidth / 2f)
-        val arrowTop = (1 - arrowHeightFraction) * arrowVisibleHeight
+        val arrowTop = (1 - arrowHeightFraction) * arrowVisibleHeight - topOffset
         arrowPath.offset(arrowStart, arrowTop)
         // union with rectangle
         sourcePath.op(arrowPath, Path.Op.UNION)
@@ -180,7 +181,7 @@
         fillPaint.colorFilter = colorFilter
     }
 
-    fun setHeight(newHeight: Float) {
+    fun setBackgroundHeight(newHeight: Float) {
         backgroundHeight = newHeight
     }
 
diff --git a/quickstep/src/com/android/launcher3/taskbar/bubbles/BubbleBarView.java b/quickstep/src/com/android/launcher3/taskbar/bubbles/BubbleBarView.java
index 2efecfb..c7c63e8 100644
--- a/quickstep/src/com/android/launcher3/taskbar/bubbles/BubbleBarView.java
+++ b/quickstep/src/com/android/launcher3/taskbar/bubbles/BubbleBarView.java
@@ -24,6 +24,7 @@
 import android.animation.AnimatorSet;
 import android.animation.ObjectAnimator;
 import android.animation.ValueAnimator;
+import android.annotation.NonNull;
 import android.annotation.Nullable;
 import android.annotation.SuppressLint;
 import android.content.Context;
@@ -44,6 +45,7 @@
 import com.android.launcher3.R;
 import com.android.launcher3.anim.SpringAnimationBuilder;
 import com.android.launcher3.util.DisplayController;
+import com.android.wm.shell.Flags;
 import com.android.wm.shell.common.bubbles.BubbleBarLocation;
 
 import java.util.List;
@@ -85,6 +87,7 @@
     private static final int MAX_VISIBLE_BUBBLES_COLLAPSED = 2;
     private static final int ARROW_POSITION_ANIMATION_DURATION_MS = 200;
     private static final int WIDTH_ANIMATION_DURATION_MS = 200;
+    private static final int SCALE_ANIMATION_DURATION_MS = 200;
 
     private static final long FADE_OUT_ANIM_ALPHA_DURATION_MS = 50L;
     private static final long FADE_OUT_ANIM_ALPHA_DELAY_MS = 50L;
@@ -135,13 +138,12 @@
     private float mBubbleBarPadding;
     // The size of a bubble in the bar
     private float mIconSize;
+    // The scale of bubble icons
+    private float mIconScale = 1f;
     // The elevation of the bubbles within the bar
     private final float mBubbleElevation;
     private final float mDragElevation;
     private final int mPointerSize;
-
-    private final Rect mTempBackgroundBounds = new Rect();
-
     // Whether the bar is expanded (i.e. the bubble activity is being displayed).
     private boolean mIsBarExpanded = false;
     // The currently selected bubble view.
@@ -162,7 +164,8 @@
     /** An animator used for scaling in a new bubble to the bubble bar while expanded. */
     @Nullable
     private ValueAnimator mNewBubbleScaleInAnimator = null;
-
+    @Nullable
+    private ValueAnimator mScalePaddingAnimator;
     @Nullable
     private Animator mBubbleBarLocationAnimator = null;
 
@@ -217,42 +220,74 @@
         setBackgroundDrawable(mBubbleBarBackground);
 
         mWidthAnimator.setDuration(WIDTH_ANIMATION_DURATION_MS);
-        mWidthAnimator.addUpdateListener(animation -> {
-            updateBubblesLayoutProperties(mBubbleBarLocation);
-            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;
-                }
-                // If the bar was just collapsed and the overflow was the last bubble that was
-                // selected, set the first bubble as selected.
-                if (!mIsBarExpanded && mUpdateSelectedBubbleAfterCollapse != null
-                        && mSelectedBubbleView != null
-                        && mSelectedBubbleView.getBubble() instanceof BubbleBarOverflow) {
-                    BubbleView firstBubble = (BubbleView) getChildAt(0);
-                    mUpdateSelectedBubbleAfterCollapse.accept(firstBubble.getBubble().getKey());
-                }
-                updateWidth();
-            }
+        addAnimationCallBacks(mWidthAnimator,
+                /* onStart= */ () -> mBubbleBarBackground.showArrow(true),
+                /* onEnd= */ () -> {
+                    mBubbleBarBackground.showArrow(mIsBarExpanded);
+                    if (!mIsBarExpanded && mReorderRunnable != null) {
+                        mReorderRunnable.run();
+                        mReorderRunnable = null;
+                    }
+                    // If the bar was just collapsed and the overflow was the last bubble that was
+                    // selected, set the first bubble as selected.
+                    if (!mIsBarExpanded && mUpdateSelectedBubbleAfterCollapse != null
+                            && mSelectedBubbleView != null
+                            && mSelectedBubbleView.getBubble() instanceof BubbleBarOverflow) {
+                        BubbleView firstBubble = (BubbleView) getChildAt(0);
+                        mUpdateSelectedBubbleAfterCollapse.accept(firstBubble.getBubble().getKey());
+                    }
+                    updateWidth();
+                },
+                /* onUpdate= */ animator -> {
+                    updateBubblesLayoutProperties(mBubbleBarLocation);
+                    invalidate();
+                });
+    }
 
-            @Override
-            public void onAnimationRepeat(Animator animation) {
-            }
 
-            @Override
-            public void onAnimationStart(Animator animation) {
-                mBubbleBarBackground.showArrow(true);
-            }
-        });
+    /**
+     * Animates icon sizes and spacing between icons and bubble bar borders.
+     *
+     * @param newIconSize         new icon size
+     * @param newBubbleBarPadding spacing between icons and bubble bar borders.
+     */
+    public void animateBubbleBarIconSize(float newIconSize, float newBubbleBarPadding) {
+        if (!isIconSizeOrPaddingUpdated(newIconSize, newBubbleBarPadding)) {
+            return;
+        }
+        if (!Flags.animateBubbleSizeChange()) {
+            setIconSizeAndPadding(newIconSize, newBubbleBarPadding);
+        }
+        if (mScalePaddingAnimator != null && mScalePaddingAnimator.isRunning()) {
+            mScalePaddingAnimator.cancel();
+        }
+        ValueAnimator scalePaddingAnimator = ValueAnimator.ofFloat(0f, 1f);
+        scalePaddingAnimator.setDuration(SCALE_ANIMATION_DURATION_MS);
+        boolean isPaddingUpdated = isPaddingUpdated(newBubbleBarPadding);
+        boolean isIconSizeUpdated = isIconSizeUpdated(newIconSize);
+        float initialScale = mIconScale;
+        float initialPadding = mBubbleBarPadding;
+        float targetScale = newIconSize / getScaledIconSize();
+
+        addAnimationCallBacks(scalePaddingAnimator,
+                /* onStart= */ null,
+                /* onEnd= */ () -> setIconSizeAndPadding(newIconSize, newBubbleBarPadding),
+                /* onUpdate= */ animator -> {
+                    float transitionProgress = (float) animator.getAnimatedValue();
+                    if (isIconSizeUpdated) {
+                        mIconScale =
+                                initialScale + (targetScale - initialScale) * transitionProgress;
+                    }
+                    if (isPaddingUpdated) {
+                        mBubbleBarPadding = initialPadding
+                                + (newBubbleBarPadding - initialPadding) * transitionProgress;
+                    }
+                    updateBubblesLayoutProperties(mBubbleBarLocation);
+                    invalidate();
+                });
+        scalePaddingAnimator.start();
+        mScalePaddingAnimator = scalePaddingAnimator;
     }
 
     @Override
@@ -267,28 +302,37 @@
     }
 
     /**
-     * Sets new icon size and spacing between icons and bubble bar borders.
+     * Sets new icon sizes and newBubbleBarPadding between icons and bubble bar borders.
      *
-     * @param newIconSize new icon size
-     * @param spacing     spacing between icons and bubble bar borders.
+     * @param newIconSize         new icon size
+     * @param newBubbleBarPadding newBubbleBarPadding between icons and bubble bar borders.
      */
-    // TODO(b/335575529): animate bubble bar icons size change
-    public void setIconSizeAndPadding(float newIconSize, float spacing) {
+    public void setIconSizeAndPadding(float newIconSize, float newBubbleBarPadding) {
         // TODO(b/335457839): handle new bubble animation during the size change
-        mBubbleBarPadding = spacing;
+        if (!isIconSizeOrPaddingUpdated(newIconSize, newBubbleBarPadding)) {
+            return;
+        }
+        mIconScale = 1f;
+        mBubbleBarPadding = newBubbleBarPadding;
         mIconSize = newIconSize;
         int childCount = getChildCount();
         for (int i = 0; i < childCount; i++) {
             View childView = getChildAt(i);
+            childView.setScaleY(mIconScale);
+            childView.setScaleY(mIconScale);
             FrameLayout.LayoutParams params = (LayoutParams) childView.getLayoutParams();
             params.height = (int) mIconSize;
             params.width = (int) mIconSize;
             childView.setLayoutParams(params);
         }
-        mBubbleBarBackground.setHeight(getBubbleBarExpandedHeight());
+        mBubbleBarBackground.setBackgroundHeight(getBubbleBarHeight());
         updateLayoutParams();
     }
 
+    private float getScaledIconSize() {
+        return mIconSize * mIconScale;
+    }
+
     @Override
     protected void onLayout(boolean changed, int left, int top, int right, int bottom) {
         super.onLayout(changed, left, top, right, bottom);
@@ -698,6 +742,11 @@
         setLayoutParams(lp);
     }
 
+    private float getBubbleBarHeight() {
+        return mIsBarExpanded ? getBubbleBarExpandedHeight()
+                : getBubbleBarCollapsedHeight();
+    }
+
     /** @return the horizontal margin between the bubble bar and the edge of the screen. */
     int getHorizontalMargin() {
         LayoutParams lp = (FrameLayout.LayoutParams) getLayoutParams();
@@ -714,7 +763,10 @@
         final float expandedWidth = expandedWidth();
         final float collapsedWidth = collapsedWidth();
         int bubbleCount = getChildCount();
-        final float ty = (mBubbleBarBounds.height() - mIconSize) / 2f;
+        float viewBottom = mBubbleBarBounds.height() + (isExpanded() ? mPointerSize : 0);
+        float bubbleBarAnimatedTop = viewBottom - getBubbleBarHeight();
+        // When translating X & Y the scale is ignored, so need to deduct it from the translations
+        final float ty = bubbleBarAnimatedTop + mBubbleBarPadding - getScaleIconShift();
         final boolean animate = getVisibility() == VISIBLE;
         final boolean onLeft = bubbleBarLocation.isOnLeft(isLayoutRtl());
         // elevation state is opposite to widthState - when expanded all icons are flat
@@ -729,8 +781,9 @@
             bv.setDragTranslationX(0f);
             bv.setOffsetX(0f);
 
+            bv.setScaleX(mIconScale);
+            bv.setScaleY(mIconScale);
             bv.setTranslationY(ty);
-
             // the position of the bubble when the bar is fully expanded
             final float expandedX = getExpandedBubbleTranslationX(i, bubbleCount, onLeft);
             // the position of the bubble when the bar is fully collapsed
@@ -795,21 +848,28 @@
         mBubbleBarBackground.setArrowPosition(arrowPosition);
         mBubbleBarBackground.setArrowHeightFraction(widthState);
         mBubbleBarBackground.setWidth(interpolatedWidth);
+        mBubbleBarBackground.setBackgroundHeight(getBubbleBarExpandedHeight());
+    }
+
+    private float getScaleIconShift() {
+        return (mIconSize - getScaledIconSize()) / 2;
     }
 
     private float getExpandedBubbleTranslationX(int bubbleIndex, int bubbleCount, boolean onLeft) {
         if (bubbleIndex < 0 || bubbleIndex >= bubbleCount) {
             return 0;
         }
-        final float iconAndSpacing = mIconSize + mExpandedBarIconsSpacing;
+        final float iconAndSpacing = getScaledIconSize() + mExpandedBarIconsSpacing;
+        float translationX;
         if (mNewBubbleScaleInAnimator != null && mNewBubbleScaleInAnimator.isRunning()) {
-            return getExpandedBubbleTranslationXDuringScaleAnimation(
+            translationX = getExpandedBubbleTranslationXDuringScaleAnimation(
                     bubbleIndex, bubbleCount, onLeft);
         } else if (onLeft) {
-            return (bubbleCount - bubbleIndex - 1) * iconAndSpacing;
+            translationX = mBubbleBarPadding + (bubbleCount - bubbleIndex - 1) * iconAndSpacing;
         } else {
-            return bubbleIndex * iconAndSpacing;
+            translationX = mBubbleBarPadding + bubbleIndex * iconAndSpacing;
         }
+        return translationX - getScaleIconShift();
     }
 
     /**
@@ -830,17 +890,17 @@
             // compiler doesn't know that.
             return 0;
         }
-        final float iconAndSpacing = mIconSize + mExpandedBarIconsSpacing;
+        final float iconAndSpacing = getScaledIconSize() + mExpandedBarIconsSpacing;
         final float newBubbleScale = mNewBubbleScaleInAnimator.getAnimatedFraction();
         // the new bubble is scaling in from the center, so we need to adjust its translation so
         // that the distance to the adjacent bubble scales at the same rate.
-        final float pivotAdjustment = -(1 - newBubbleScale) * mIconSize / 2f;
+        final float pivotAdjustment = -(1 - newBubbleScale) * getScaledIconSize() / 2f;
 
         if (onLeft) {
             if (bubbleIndex == 0) {
                 // this is the animating bubble. use scaled spacing between it and the bubble to
                 // its left
-                return (bubbleCount - 1) * mIconSize
+                return (bubbleCount - 1) * getScaledIconSize()
                         + (bubbleCount - 2) * mExpandedBarIconsSpacing
                         + newBubbleScale * mExpandedBarIconsSpacing
                         + pivotAdjustment;
@@ -862,13 +922,16 @@
         if (bubbleIndex < 0 || bubbleIndex >= bubbleCount) {
             return 0;
         }
+        float translationX;
         if (onLeft) {
             // Shift the first bubble only if there are more bubbles in addition to overflow
-            return bubbleIndex == 0 && bubbleCount > MAX_VISIBLE_BUBBLES_COLLAPSED
-                    ? mIconOverlapAmount : 0;
+            translationX = mBubbleBarPadding + (
+                    bubbleIndex == 0 && bubbleCount > MAX_VISIBLE_BUBBLES_COLLAPSED
+                            ? mIconOverlapAmount : 0);
         } else {
-            return bubbleIndex == 0 ? 0 : mIconOverlapAmount;
+            translationX = mBubbleBarPadding + (bubbleIndex == 0 ? 0 : mIconOverlapAmount);
         }
+        return translationX - getScaleIconShift();
     }
 
     /**
@@ -976,7 +1039,7 @@
         final int index = indexOfChild(mSelectedBubbleView);
         final float selectedBubbleTranslationX = getExpandedBubbleTranslationX(
                 index, getChildCount(), bubbleBarLocation.isOnLeft(isLayoutRtl()));
-        return getPaddingStart() + selectedBubbleTranslationX + mIconSize / 2f;
+        return selectedBubbleTranslationX + mIconSize / 2f;
     }
 
     private float arrowPositionForSelectedWhenCollapsed(BubbleBarLocation bubbleBarLocation) {
@@ -990,7 +1053,7 @@
             bubblePosition = index >= MAX_VISIBLE_BUBBLES_COLLAPSED
                     ? MAX_VISIBLE_BUBBLES_COLLAPSED - 1 : index;
         }
-        return getPaddingStart() + bubblePosition * (mIconOverlapAmount) + mIconSize / 2f;
+        return mBubbleBarPadding + bubblePosition * (mIconOverlapAmount) + getScaledIconSize() / 2f;
     }
 
     @Override
@@ -1038,7 +1101,6 @@
      */
     public float expandedWidth() {
         final int childCount = getChildCount();
-        final int horizontalPadding = getPaddingStart() + getPaddingEnd();
         // spaces amount is less than child count by 1, or 0 if no child views
         final float totalSpace;
         final float totalIconSize;
@@ -1047,22 +1109,22 @@
             // expanded, so we have at least 2 bubbles in the bubble bar.
             final float newBubbleScale = mNewBubbleScaleInAnimator.getAnimatedFraction();
             totalSpace = (childCount - 2 + newBubbleScale) * mExpandedBarIconsSpacing;
-            totalIconSize = (childCount - 1 + newBubbleScale) * mIconSize;
+            totalIconSize = (childCount - 1 + newBubbleScale) * getScaledIconSize();
         } else {
             totalSpace = Math.max(childCount - 1, 0) * mExpandedBarIconsSpacing;
-            totalIconSize = childCount * mIconSize;
+            totalIconSize = childCount * getScaledIconSize();
         }
-        return totalIconSize + totalSpace + horizontalPadding;
+        return totalIconSize + totalSpace + 2 * mBubbleBarPadding;
     }
 
     private float collapsedWidth() {
         final int childCount = getChildCount();
-        final int horizontalPadding = getPaddingStart() + getPaddingEnd();
+        final float horizontalPadding = 2 * mBubbleBarPadding;
         // If there are more than 2 bubbles, the first 2 should be visible when collapsed.
         // Otherwise just the first bubble should be visible because we don't show the overflow.
         return childCount > MAX_VISIBLE_BUBBLES_COLLAPSED
-                ? mIconSize + mIconOverlapAmount + horizontalPadding
-                : mIconSize + horizontalPadding;
+                ? getScaledIconSize() + mIconOverlapAmount + horizontalPadding
+                : getScaledIconSize() + horizontalPadding;
     }
 
     private float getBubbleBarExpandedHeight() {
@@ -1071,7 +1133,7 @@
 
     float getBubbleBarCollapsedHeight() {
         // the pointer is invisible when collapsed
-        return mIconSize + mBubbleBarPadding * 2;
+        return getScaledIconSize() + mBubbleBarPadding * 2;
     }
 
     /**
@@ -1103,6 +1165,7 @@
         return mIsAnimatingNewBubble;
     }
 
+
     private boolean hasOverview() {
         // Overview is always the last bubble
         View lastChild = getChildAt(getChildCount() - 1);
@@ -1144,6 +1207,46 @@
         setContentDescription(contentDesc);
     }
 
+    private boolean isIconSizeOrPaddingUpdated(float newIconSize, float newBubbleBarPadding) {
+        return isIconSizeUpdated(newIconSize) || isPaddingUpdated(newBubbleBarPadding);
+    }
+
+    private boolean isIconSizeUpdated(float newIconSize) {
+        return Float.compare(mIconSize, newIconSize) != 0;
+    }
+
+    private boolean isPaddingUpdated(float newBubbleBarPadding) {
+        return Float.compare(mBubbleBarPadding, newBubbleBarPadding) != 0;
+    }
+
+    private void addAnimationCallBacks(@NonNull ValueAnimator animator,
+            @Nullable Runnable onStart,
+            @Nullable Runnable onEnd,
+            @Nullable ValueAnimator.AnimatorUpdateListener onUpdate) {
+        if (onUpdate != null) animator.addUpdateListener(onUpdate);
+        animator.addListener(new Animator.AnimatorListener() {
+            @Override
+            public void onAnimationCancel(Animator animator) {
+
+            }
+
+            @Override
+            public void onAnimationStart(Animator animator) {
+                if (onStart != null) onStart.run();
+            }
+
+            @Override
+            public void onAnimationEnd(Animator animator) {
+                if (onEnd != null) onEnd.run();
+            }
+
+            @Override
+            public void onAnimationRepeat(Animator animator) {
+
+            }
+        });
+    }
+
     /** Interface for BubbleBarView to communicate with its controller. */
     interface Controller {
 
diff --git a/quickstep/src/com/android/launcher3/taskbar/bubbles/BubbleBarViewController.java b/quickstep/src/com/android/launcher3/taskbar/bubbles/BubbleBarViewController.java
index 0bfd1d7..eec095d 100644
--- a/quickstep/src/com/android/launcher3/taskbar/bubbles/BubbleBarViewController.java
+++ b/quickstep/src/com/android/launcher3/taskbar/bubbles/BubbleBarViewController.java
@@ -108,9 +108,11 @@
         mBubbleDragController = bubbleControllers.bubbleDragController;
         mTaskbarStashController = controllers.taskbarStashController;
         mTaskbarInsetsController = controllers.taskbarInsetsController;
+        mBubbleBarViewAnimator = new BubbleBarViewAnimator(mBarView, mBubbleStashController);
 
-        mActivity.addOnDeviceProfileChangeListener(dp -> setBubbleBarIconSize(dp.taskbarIconSize));
-        setBubbleBarIconSize(mActivity.getDeviceProfile().taskbarIconSize);
+        mActivity.addOnDeviceProfileChangeListener(
+                dp -> updateBubbleBarIconSize(dp.taskbarIconSize, /* animate= */ true));
+        updateBubbleBarIconSize(mActivity.getDeviceProfile().taskbarIconSize, /* animate= */ false);
         mBubbleBarScale.updateValue(1f);
         mBubbleClickListener = v -> onBubbleClicked(v);
         mBubbleBarClickListener = v -> onBubbleBarClicked();
@@ -123,8 +125,6 @@
                         mBoundsChangeListener.onBoundsChanged();
                     }
                 });
-
-        mBubbleBarViewAnimator = new BubbleBarViewAnimator(mBarView, mBubbleStashController);
         mBarView.setController(new BubbleBarView.Controller() {
             @Override
             public float getBubbleBarTranslationY() {
@@ -299,33 +299,6 @@
         }
     }
 
-    private void setBubbleBarIconSize(int newIconSize) {
-        if (newIconSize == mIconSize) {
-            return;
-        }
-        Resources res = mActivity.getResources();
-        DisplayMetrics dm = res.getDisplayMetrics();
-        float smallIconSize = TypedValue.applyDimension(TypedValue.COMPLEX_UNIT_DIP,
-                APP_ICON_SMALL_DP, dm);
-        float mediumIconSize = TypedValue.applyDimension(TypedValue.COMPLEX_UNIT_DIP,
-                APP_ICON_MEDIUM_DP, dm);
-        float largeIconSize = TypedValue.applyDimension(TypedValue.COMPLEX_UNIT_DIP,
-                APP_ICON_LARGE_DP, dm);
-        float smallMediumThreshold = (smallIconSize + mediumIconSize) / 2f;
-        float mediumLargeThreshold = (mediumIconSize + largeIconSize) / 2f;
-        mIconSize = newIconSize <= smallMediumThreshold
-                ? res.getDimensionPixelSize(R.dimen.bubblebar_icon_size_small) :
-                res.getDimensionPixelSize(R.dimen.bubblebar_icon_size);
-        float bubbleBarPadding = newIconSize >= mediumLargeThreshold
-                ? res.getDimensionPixelSize(R.dimen.bubblebar_icon_spacing_large) :
-                res.getDimensionPixelSize(R.dimen.bubblebar_icon_spacing);
-
-        mBarView.setIconSizeAndPadding(mIconSize, bubbleBarPadding);
-        mBarView.setPadding((int) bubbleBarPadding, mBarView.getPaddingTop(),
-                (int) bubbleBarPadding,
-                mBarView.getPaddingBottom());
-    }
-
     /** Sets a callback that updates the selected bubble after the bubble bar collapses. */
     public void setUpdateSelectedBubbleAfterCollapse(
             Consumer<String> updateSelectedBubbleAfterCollapse) {
@@ -360,6 +333,30 @@
     // Modifying view related properties.
     //
 
+    private void updateBubbleBarIconSize(int newIconSize, boolean animate) {
+        Resources res = mActivity.getResources();
+        DisplayMetrics dm = res.getDisplayMetrics();
+        float smallIconSize = TypedValue.applyDimension(TypedValue.COMPLEX_UNIT_DIP,
+                APP_ICON_SMALL_DP, dm);
+        float mediumIconSize = TypedValue.applyDimension(TypedValue.COMPLEX_UNIT_DIP,
+                APP_ICON_MEDIUM_DP, dm);
+        float largeIconSize = TypedValue.applyDimension(TypedValue.COMPLEX_UNIT_DIP,
+                APP_ICON_LARGE_DP, dm);
+        float smallMediumThreshold = (smallIconSize + mediumIconSize) / 2f;
+        float mediumLargeThreshold = (mediumIconSize + largeIconSize) / 2f;
+        mIconSize = newIconSize <= smallMediumThreshold
+                ? res.getDimensionPixelSize(R.dimen.bubblebar_icon_size_small) :
+                res.getDimensionPixelSize(R.dimen.bubblebar_icon_size);
+        float bubbleBarPadding = newIconSize >= mediumLargeThreshold
+                ? res.getDimensionPixelSize(R.dimen.bubblebar_icon_spacing_large) :
+                res.getDimensionPixelSize(R.dimen.bubblebar_icon_spacing);
+        if (animate) {
+            mBarView.animateBubbleBarIconSize(mIconSize, bubbleBarPadding);
+        } else {
+            mBarView.setIconSizeAndPadding(mIconSize, bubbleBarPadding);
+        }
+    }
+
     /**
      * Sets the translation of the bubble bar during the swipe up gesture.
      */