Merge "Scale bubble bar to the width of the handle" into main
diff --git a/quickstep/src/com/android/launcher3/taskbar/bubbles/BubbleBarView.java b/quickstep/src/com/android/launcher3/taskbar/bubbles/BubbleBarView.java
index 71867fe..dc7600a 100644
--- a/quickstep/src/com/android/launcher3/taskbar/bubbles/BubbleBarView.java
+++ b/quickstep/src/com/android/launcher3/taskbar/bubbles/BubbleBarView.java
@@ -1303,7 +1303,10 @@
         return totalIconSize + totalSpace + horizontalPadding;
     }
 
-    private float collapsedWidth() {
+    /**
+     * Get width of the bubble bar if it is collapsed
+     */
+    float collapsedWidth() {
         final int bubbleChildCount = getBubbleChildCount();
         final float horizontalPadding = 2 * mBubbleBarPadding;
         // If there are more than 2 bubbles, the first 2 should be visible when collapsed,
diff --git a/quickstep/src/com/android/launcher3/taskbar/bubbles/BubbleBarViewController.java b/quickstep/src/com/android/launcher3/taskbar/bubbles/BubbleBarViewController.java
index 2502e4a..c55faa6 100644
--- a/quickstep/src/com/android/launcher3/taskbar/bubbles/BubbleBarViewController.java
+++ b/quickstep/src/com/android/launcher3/taskbar/bubbles/BubbleBarViewController.java
@@ -80,6 +80,7 @@
 
     // These are exposed to {@link BubbleStashController} to animate for stashing/un-stashing
     private final MultiValueAlpha mBubbleBarAlpha;
+    private final AnimatedFloat mBubbleBarScaleX = new AnimatedFloat(this::updateScaleX);
     private final AnimatedFloat mBubbleBarScaleY = new AnimatedFloat(this::updateScaleY);
     private final AnimatedFloat mBubbleBarTranslationY = new AnimatedFloat(
             this::updateTranslationY);
@@ -257,6 +258,10 @@
         return mBubbleBarAlpha;
     }
 
+    public AnimatedFloat getBubbleBarScaleX() {
+        return mBubbleBarScaleX;
+    }
+
     public AnimatedFloat getBubbleBarScaleY() {
         return mBubbleBarScaleY;
     }
@@ -265,6 +270,10 @@
         return mBubbleBarTranslationY;
     }
 
+    public float getBubbleBarCollapsedWidth() {
+        return mBarView.collapsedWidth();
+    }
+
     public float getBubbleBarCollapsedHeight() {
         return mBarView.getBubbleBarCollapsedHeight();
     }
@@ -310,6 +319,14 @@
     }
 
     /**
+     * @return {@code true} if bubble bar is on the left edge of the screen, {@code false} if on
+     * the right
+     */
+    public boolean isBubbleBarOnLeft() {
+        return mBarView.getBubbleBarLocation().isOnLeft(mBarView.isLayoutRtl());
+    }
+
+    /**
      * Update bar {@link BubbleBarLocation}
      */
     public void setBubbleBarLocation(BubbleBarLocation bubbleBarLocation) {
@@ -510,6 +527,10 @@
                 + mBubbleBarStashTranslationY);
     }
 
+    private void updateScaleX(float scale) {
+        mBarView.setScaleX(scale);
+    }
+
     private void updateScaleY(float scale) {
         mBarView.setScaleY(scale);
     }
diff --git a/quickstep/src/com/android/launcher3/taskbar/bubbles/BubbleStashedHandleViewController.java b/quickstep/src/com/android/launcher3/taskbar/bubbles/BubbleStashedHandleViewController.java
index 16af248..73984ea 100644
--- a/quickstep/src/com/android/launcher3/taskbar/bubbles/BubbleStashedHandleViewController.java
+++ b/quickstep/src/com/android/launcher3/taskbar/bubbles/BubbleStashedHandleViewController.java
@@ -173,6 +173,13 @@
     }
 
     /**
+     * Returns the width of the stashed handle.
+     */
+    public int getStashedWidth() {
+        return mStashedHandleWidth;
+    }
+
+    /**
      * Returns the height of the stashed handle.
      */
     public int getStashedHeight() {
diff --git a/quickstep/src/com/android/launcher3/taskbar/bubbles/stashing/TransientBubbleStashController.kt b/quickstep/src/com/android/launcher3/taskbar/bubbles/stashing/TransientBubbleStashController.kt
index b8fd1cb..ab27eb2 100644
--- a/quickstep/src/com/android/launcher3/taskbar/bubbles/stashing/TransientBubbleStashController.kt
+++ b/quickstep/src/com/android/launcher3/taskbar/bubbles/stashing/TransientBubbleStashController.kt
@@ -67,7 +67,8 @@
     // bubble bar properties
     private lateinit var bubbleBarAlpha: MultiPropertyFactory<View>.MultiProperty
     private lateinit var bubbleBarTranslationYAnimator: AnimatedFloat
-    private lateinit var bubbleBarScale: AnimatedFloat
+    private lateinit var bubbleBarScaleX: AnimatedFloat
+    private lateinit var bubbleBarScaleY: AnimatedFloat
     private val handleCenterFromScreenBottom =
         context.resources.getDimensionPixelSize(R.dimen.bubblebar_stashed_size) / 2f
 
@@ -148,7 +149,8 @@
         bubbleBarTranslationYAnimator = bubbleBarViewController.bubbleBarTranslationY
         // bubble bar has only alpha property, getting it at index 0
         bubbleBarAlpha = bubbleBarViewController.bubbleBarAlpha.get(/* index= */ 0)
-        bubbleBarScale = bubbleBarViewController.bubbleBarScaleY
+        bubbleBarScaleX = bubbleBarViewController.bubbleBarScaleX
+        bubbleBarScaleY = bubbleBarViewController.bubbleBarScaleY
         stashedHeight = bubbleStashedHandleViewController?.stashedHeight ?: 0
         stashHandleViewAlpha = bubbleStashedHandleViewController?.stashedHandleAlpha?.get(0)
     }
@@ -158,7 +160,8 @@
         if (isBubblesShowingOnHome || isBubblesShowingOnOverview) {
             isStashed = false
             animatorSet.playTogether(
-                bubbleBarScale.animateToValue(1f),
+                bubbleBarScaleX.animateToValue(1f),
+                bubbleBarScaleY.animateToValue(1f),
                 bubbleBarTranslationYAnimator.animateToValue(bubbleBarTranslationY),
                 bubbleBarAlpha.animateToValue(1f)
             )
@@ -178,7 +181,8 @@
         stashHandleViewAlpha?.value = 0f
         this.bubbleBarTranslationYAnimator.updateValue(bubbleBarTranslationY)
         bubbleBarAlpha.setValue(1f)
-        bubbleBarScale.updateValue(1f)
+        bubbleBarScaleX.updateValue(1f)
+        bubbleBarScaleY.updateValue(1f)
         isStashed = false
         onIsStashedChanged()
     }
@@ -188,7 +192,8 @@
         stashHandleViewAlpha?.value = 1f
         this.bubbleBarTranslationYAnimator.updateValue(getStashTranslation())
         bubbleBarAlpha.setValue(0f)
-        bubbleBarScale.updateValue(getStashScale())
+        bubbleBarScaleX.updateValue(getStashScaleX())
+        bubbleBarScaleY.updateValue(getStashScaleY())
         isStashed = true
         onIsStashedChanged()
     }
@@ -258,7 +263,13 @@
     }
 
     @VisibleForTesting
-    fun getStashScale(): Float {
+    fun getStashScaleX(): Float {
+        val handleWidth = bubbleStashedHandleViewController?.stashedWidth ?: 0
+        return handleWidth / bubbleBarViewController.bubbleBarCollapsedWidth
+    }
+
+    @VisibleForTesting
+    fun getStashScaleY(): Float {
         val handleHeight = bubbleStashedHandleViewController?.stashedHeight ?: 0
         return handleHeight / bubbleBarViewController.bubbleBarCollapsedHeight
     }
@@ -298,12 +309,12 @@
             }
         )
 
-        val scaleTarget = if (isStashed) getStashScale() else 1f
+        val pivotX = if (bubbleBarViewController.isBubbleBarOnLeft) 0f else 1f
         animatorSet.play(
-            bubbleBarScale.animateToValue(scaleTarget).apply {
+            createScaleAnimator(isStashed).apply {
                 this.duration = duration
                 this.interpolator = EMPHASIZED
-                this.setBubbleBarPivotDuringAnim(0.5f, 1f)
+                this.setBubbleBarPivotDuringAnim(pivotX, 1f)
             }
         )
 
@@ -351,6 +362,15 @@
             .build(translationYDuringStash, AnimatedFloat.VALUE)
     }
 
+    private fun createScaleAnimator(isStashed: Boolean): AnimatorSet {
+        val scaleXTarget = if (isStashed) getStashScaleX() else 1f
+        val scaleYTarget = if (isStashed) getStashScaleY() else 1f
+        return AnimatorSet().apply {
+            play(bubbleBarScaleX.animateToValue(scaleXTarget))
+            play(bubbleBarScaleY.animateToValue(scaleYTarget))
+        }
+    }
+
     private fun onIsStashedChanged() {
         controllersAfterInitAction.runAfterInit {
             taskbarInsetsController.onTaskbarOrBubblebarWindowHeightOrInsetsChanged()
diff --git a/quickstep/tests/multivalentTests/src/com/android/launcher3/taskbar/bubbles/stashing/TransientBubbleStashControllerTest.kt b/quickstep/tests/multivalentTests/src/com/android/launcher3/taskbar/bubbles/stashing/TransientBubbleStashControllerTest.kt
index 63c2197..262d8da 100644
--- a/quickstep/tests/multivalentTests/src/com/android/launcher3/taskbar/bubbles/stashing/TransientBubbleStashControllerTest.kt
+++ b/quickstep/tests/multivalentTests/src/com/android/launcher3/taskbar/bubbles/stashing/TransientBubbleStashControllerTest.kt
@@ -54,9 +54,11 @@
 
     companion object {
         const val TASKBAR_BOTTOM_SPACE = 5
+        const val BUBBLE_BAR_WIDTH = 200f
         const val BUBBLE_BAR_HEIGHT = 100f
         const val HOTSEAT_TRANSLATION_Y = -45f
         const val TASK_BAR_TRANSLATION_Y = -TASKBAR_BOTTOM_SPACE
+        const val HANDLE_VIEW_WIDTH = 150
         const val HANDLE_VIEW_HEIGHT = 4
         const val BUBBLE_BAR_STASHED_TRANSLATION_Y = -2.5f
     }
@@ -75,7 +77,8 @@
     private lateinit var bubbleBarView: BubbleBarView
     private lateinit var stashedHandleView: StashedHandleView
     private lateinit var barTranslationY: AnimatedFloat
-    private lateinit var barScale: AnimatedFloat
+    private lateinit var barScaleX: AnimatedFloat
+    private lateinit var barScaleY: AnimatedFloat
     private lateinit var barAlpha: MultiValueAlpha
     private lateinit var stashedHandleAlpha: MultiValueAlpha
     private lateinit var stashedHandleScale: AnimatedFloat
@@ -173,8 +176,8 @@
         assertThat(mTransientBubbleStashController.isStashed).isTrue()
         assertThat(bubbleBarView.translationY).isEqualTo(BUBBLE_BAR_STASHED_TRANSLATION_Y)
         assertThat(bubbleBarView.alpha).isEqualTo(0f)
-        assertThat(bubbleBarView.scaleX).isEqualTo(mTransientBubbleStashController.getStashScale())
-        assertThat(bubbleBarView.scaleY).isEqualTo(mTransientBubbleStashController.getStashScale())
+        assertThat(bubbleBarView.scaleX).isEqualTo(mTransientBubbleStashController.getStashScaleX())
+        assertThat(bubbleBarView.scaleY).isEqualTo(mTransientBubbleStashController.getStashScaleY())
         // Handle view is visible
         assertThat(stashedHandleView.translationY).isEqualTo(0)
         assertThat(stashedHandleView.alpha).isEqualTo(1)
@@ -195,7 +198,7 @@
 
         // Then
         assertThat(barTranslationY.isAnimating).isTrue()
-        assertThat(barScale.isAnimating).isTrue()
+        assertThat(barScaleX.isAnimating).isTrue()
         // Wait until animation ends
         advanceTimeBy(BubbleStashController.BAR_STASH_DURATION)
 
@@ -242,8 +245,8 @@
         // Then all property values are updated
         assertThat(bubbleBarView.translationY).isEqualTo(BUBBLE_BAR_STASHED_TRANSLATION_Y)
         assertThat(bubbleBarView.alpha).isEqualTo(0)
-        assertThat(bubbleBarView.scaleX).isEqualTo(mTransientBubbleStashController.getStashScale())
-        assertThat(bubbleBarView.scaleY).isEqualTo(mTransientBubbleStashController.getStashScale())
+        assertThat(bubbleBarView.scaleX).isEqualTo(mTransientBubbleStashController.getStashScaleX())
+        assertThat(bubbleBarView.scaleY).isEqualTo(mTransientBubbleStashController.getStashScaleY())
         // Handle is visible at correct Y position
         assertThat(stashedHandleView.alpha).isEqualTo(1)
         assertThat(stashedHandleView.translationY).isEqualTo(0)
@@ -293,20 +296,16 @@
     private fun setUpBubbleBarController() {
         barTranslationY =
             AnimatedFloat(Runnable { bubbleBarView.translationY = barTranslationY.value })
-        barScale =
-            AnimatedFloat(
-                Runnable {
-                    val scale: Float = barScale.value
-                    bubbleBarView.scaleX = scale
-                    bubbleBarView.scaleY = scale
-                }
-            )
+        barScaleX = AnimatedFloat { value -> bubbleBarView.scaleX = value }
+        barScaleY = AnimatedFloat { value -> bubbleBarView.scaleY = value }
         barAlpha = MultiValueAlpha(bubbleBarView, 1 /* num alpha channels */)
 
         whenever(bubbleBarViewController.hasBubbles()).thenReturn(true)
         whenever(bubbleBarViewController.bubbleBarTranslationY).thenReturn(barTranslationY)
-        whenever(bubbleBarViewController.bubbleBarScaleY).thenReturn(barScale)
+        whenever(bubbleBarViewController.bubbleBarScaleX).thenReturn(barScaleX)
+        whenever(bubbleBarViewController.bubbleBarScaleY).thenReturn(barScaleY)
         whenever(bubbleBarViewController.bubbleBarAlpha).thenReturn(barAlpha)
+        whenever(bubbleBarViewController.bubbleBarCollapsedWidth).thenReturn(BUBBLE_BAR_WIDTH)
         whenever(bubbleBarViewController.bubbleBarCollapsedHeight).thenReturn(BUBBLE_BAR_HEIGHT)
     }
 
@@ -316,7 +315,7 @@
         stashedHandleScale =
             AnimatedFloat(
                 Runnable {
-                    val scale: Float = barScale.value
+                    val scale: Float = barScaleX.value
                     bubbleBarView.scaleX = scale
                     bubbleBarView.scaleY = scale
                 }
@@ -326,6 +325,7 @@
         whenever(bubbleStashedHandleViewController.stashedHandleAlpha)
             .thenReturn(stashedHandleAlpha)
         whenever(bubbleStashedHandleViewController.physicsAnimator).thenReturn(stashPhysicsAnimator)
+        whenever(bubbleStashedHandleViewController.stashedWidth).thenReturn(HANDLE_VIEW_WIDTH)
         whenever(bubbleStashedHandleViewController.stashedHeight).thenReturn(HANDLE_VIEW_HEIGHT)
         whenever(bubbleStashedHandleViewController.setTranslationYForSwipe(any())).thenAnswer {
             invocation ->