Animate overflow button

Add overflow button to mBubbleContainer
Update animation math to account for 6th child

Bug: 138116789
Fixes: 148233216
Test: manual: expand stack - button animates like normal bubbles
Test: manual: collapse stack - button animates like normal bubbles
Test: atest SystemUITests
Change-Id: I8bd575a116ab4a7443c2d682debc5c5207df1808
diff --git a/packages/SystemUI/src/com/android/systemui/bubbles/BubbleController.java b/packages/SystemUI/src/com/android/systemui/bubbles/BubbleController.java
index ac06f95..e954163 100644
--- a/packages/SystemUI/src/com/android/systemui/bubbles/BubbleController.java
+++ b/packages/SystemUI/src/com/android/systemui/bubbles/BubbleController.java
@@ -866,10 +866,6 @@
                 mOverflowCallback.run();
             }
 
-            if (update.addedBubble != null) {
-                mStackView.addBubble(update.addedBubble);
-            }
-
             // Collapsing? Do this first before remaining steps.
             if (update.expandedChanged && !update.expanded) {
                 mStackView.setExpanded(false);
@@ -916,6 +912,10 @@
                 }
             }
 
+            if (update.addedBubble != null) {
+                mStackView.addBubble(update.addedBubble);
+            }
+
             if (update.updatedBubble != null) {
                 mStackView.updateBubble(update.updatedBubble);
             }
diff --git a/packages/SystemUI/src/com/android/systemui/bubbles/BubbleStackView.java b/packages/SystemUI/src/com/android/systemui/bubbles/BubbleStackView.java
index 685bb94..6062a3d 100644
--- a/packages/SystemUI/src/com/android/systemui/bubbles/BubbleStackView.java
+++ b/packages/SystemUI/src/com/android/systemui/bubbles/BubbleStackView.java
@@ -526,8 +526,13 @@
                 R.layout.bubble_expanded_view, this /* root */, false /* attachToRoot */);
         mOverflowExpandedView.setOverflow(true);
 
-        mInflater.inflate(R.layout.bubble_overflow_button, this);
-        mOverflowBtn = findViewById(R.id.bubble_overflow_button);
+        mOverflowBtn = (ImageView) mInflater.inflate(R.layout.bubble_overflow_button,
+                this /* root */,
+                false /* attachToRoot */);
+
+        mBubbleContainer.addView(mOverflowBtn, 0,
+                new FrameLayout.LayoutParams(WRAP_CONTENT, WRAP_CONTENT));
+
         mOverflowBtn.setOnClickListener(v -> {
             setSelectedBubble(null);
         });
@@ -541,11 +546,13 @@
         ColorDrawable bg = new ColorDrawable(bgColor);
         AdaptiveIconDrawable adaptiveIcon = new AdaptiveIconDrawable(bg, fg);
         mOverflowBtn.setImageDrawable(adaptiveIcon);
+
         mOverflowBtn.setVisibility(GONE);
     }
 
     void showExpandedViewContents(int displayId) {
-        if (mOverflowExpandedView.getVirtualDisplayId() == displayId) {
+        if (mOverflowExpandedView != null
+                && mOverflowExpandedView.getVirtualDisplayId() == displayId) {
             mOverflowExpandedView.setContentVisibility(true);
         } else if (mExpandedBubble != null
                 && mExpandedBubble.getExpandedView().getVirtualDisplayId() == displayId) {
@@ -714,7 +721,7 @@
     private void updateSystemGestureExcludeRects() {
         // Exclude the region occupied by the first BubbleView in the stack
         Rect excludeZone = mSystemGestureExclusionRects.get(0);
-        if (mBubbleContainer.getChildCount() > 0) {
+        if (getBubbleCount() > 0) {
             View firstBubble = mBubbleContainer.getChildAt(0);
             excludeZone.set(firstBubble.getLeft(), firstBubble.getTop(), firstBubble.getRight(),
                     firstBubble.getBottom());
@@ -775,7 +782,7 @@
             Log.d(TAG, "addBubble: " + bubble);
         }
 
-        if (mBubbleContainer.getChildCount() == 0) {
+        if (getBubbleCount() == 0) {
             mStackOnLeftOrWillBe = mStackAnimationController.isStackOnLeftSide();
         }
 
@@ -817,16 +824,13 @@
         }
         if (mIsExpanded) {
             if (DEBUG_BUBBLE_STACK_VIEW) {
-                Log.d(TAG, "Expanded && overflow > 0. Show overflow button at");
-                Log.d(TAG, "x: " + mExpandedAnimationController.getOverflowBtnLeft());
-                Log.d(TAG, "y: " + mExpandedAnimationController.getExpandedY());
+                Log.d(TAG, "Show overflow button.");
             }
-            mOverflowBtn.setX(mExpandedAnimationController.getOverflowBtnLeft());
-            mOverflowBtn.setY(mExpandedAnimationController.getExpandedY());
             mOverflowBtn.setVisibility(VISIBLE);
-            mExpandedAnimationController.setShowOverflowBtn(true);
             if (apply) {
-                mExpandedAnimationController.expandFromStack(null /* after */);
+                mExpandedAnimationController.expandFromStack(() -> {
+                    updatePointerPosition();
+                } /* after */);
             }
         } else {
             if (DEBUG_BUBBLE_STACK_VIEW) {
@@ -947,7 +951,7 @@
         if (mIsExpanded) {
             if (isIntersecting(mBubbleContainer, x, y)) {
                 // Could be tapping or dragging a bubble while expanded
-                for (int i = 0; i < mBubbleContainer.getChildCount(); i++) {
+                for (int i = 0; i < getBubbleCount(); i++) {
                     BadgedImageView view = (BadgedImageView) mBubbleContainer.getChildAt(i);
                     if (isIntersecting(view, x, y)) {
                         return view;
@@ -1103,7 +1107,7 @@
 
     /** Return the BubbleView at the given index from the bubble container. */
     public BadgedImageView getBubbleAt(int i) {
-        return mBubbleContainer.getChildCount() > i
+        return getBubbleCount() > i
                 ? (BadgedImageView) mBubbleContainer.getChildAt(i)
                 : null;
     }
@@ -1567,7 +1571,7 @@
             return;
         }
         if (!mIsExpanded) {
-            if (mBubbleContainer.getChildCount() > 0) {
+            if (getBubbleCount() > 0) {
                 mBubbleContainer.getChildAt(0).getBoundsOnScreen(outRect);
             }
             // Increase the touch target size of the bubble
@@ -1661,7 +1665,7 @@
 
     /** Sets the appropriate Z-order and dot position for each bubble in the stack. */
     private void updateBubbleZOrdersAndDotPosition(boolean animate) {
-        int bubbleCount = mBubbleContainer.getChildCount();
+        int bubbleCount = getBubbleCount();
         for (int i = 0; i < bubbleCount; i++) {
             BadgedImageView bv = (BadgedImageView) mBubbleContainer.getChildAt(i);
             bv.setZ((mMaxBubbles * mBubbleElevation) - i);
@@ -1677,30 +1681,23 @@
         if (expandedBubble == null) {
             return;
         }
-
         int index = getBubbleIndex(expandedBubble);
-        if (index >= mMaxBubbles) {
-            // In between state, where extra bubble will be overflowed, and new bubble added
-            index = 0;
-        }
         float bubbleLeftFromScreenLeft = mExpandedAnimationController.getBubbleLeft(index);
         float halfBubble = mBubbleSize / 2f;
         float bubbleCenter = bubbleLeftFromScreenLeft + halfBubble;
         // Padding might be adjusted for insets, so get it directly from the view
         bubbleCenter -= mExpandedViewContainer.getPaddingLeft();
-
-        if (index >= mMaxBubbles) {
-            Bubble first = mBubbleData.getBubbles().get(0);
-            first.getExpandedView().setPointerPosition(bubbleCenter);
-        } else {
-            expandedBubble.getExpandedView().setPointerPosition(bubbleCenter);
-        }
+        expandedBubble.getExpandedView().setPointerPosition(bubbleCenter);
     }
 
     /**
      * @return the number of bubbles in the stack view.
      */
     public int getBubbleCount() {
+        if (BubbleExperimentConfig.allowBubbleOverflow(mContext)) {
+            // Subtract 1 for the overflow button which is always in the bubble container.
+            return mBubbleContainer.getChildCount() - 1;
+        }
         return mBubbleContainer.getChildCount();
     }
 
@@ -1797,7 +1794,7 @@
     /** For debugging only */
     List<Bubble> getBubblesOnScreen() {
         List<Bubble> bubbles = new ArrayList<>();
-        for (int i = 0; i < mBubbleContainer.getChildCount(); i++) {
+        for (int i = 0; i < getBubbleCount(); i++) {
             View child = mBubbleContainer.getChildAt(i);
             if (child instanceof BadgedImageView) {
                 String key = ((BadgedImageView) child).getKey();
diff --git a/packages/SystemUI/src/com/android/systemui/bubbles/animation/ExpandedAnimationController.java b/packages/SystemUI/src/com/android/systemui/bubbles/animation/ExpandedAnimationController.java
index 6d6969d..aa549dc 100644
--- a/packages/SystemUI/src/com/android/systemui/bubbles/animation/ExpandedAnimationController.java
+++ b/packages/SystemUI/src/com/android/systemui/bubbles/animation/ExpandedAnimationController.java
@@ -31,6 +31,7 @@
 
 import com.android.systemui.Interpolators;
 import com.android.systemui.R;
+import com.android.systemui.bubbles.BubbleExperimentConfig;
 
 import com.google.android.collect.Sets;
 
@@ -67,13 +68,13 @@
     private float mBubblePaddingTop;
     /** Size of each bubble. */
     private float mBubbleSizePx;
-    /** Width of the overflow button. */
-    private float mOverflowBtnWidth;
+    /** Space between bubbles in row above expanded view. */
+    private float mSpaceBetweenBubbles;
     /** Height of the status bar. */
     private float mStatusBarHeight;
     /** Size of display. */
     private Point mDisplaySize;
-    /** Max number of bubbles shown in row above expanded view.*/
+    /** Max number of bubbles shown in row above expanded view. */
     private int mBubblesMaxRendered;
     /** What the current screen orientation is. */
     private int mScreenOrientation;
@@ -99,7 +100,6 @@
     private boolean mSpringingBubbleToTouch = false;
 
     private int mExpandedViewPadding;
-    private boolean mShowOverflowBtn;
 
     public ExpandedAnimationController(Point displaySize, int expandedViewPadding,
             int orientation) {
@@ -153,14 +153,6 @@
         }
     }
 
-    public void setShowOverflowBtn(boolean showBtn) {
-        mShowOverflowBtn = showBtn;
-    }
-
-    public boolean getShowOverflowBtn() {
-        return mShowOverflowBtn;
-    }
-
     /**
      * Animates the bubbles along a curved path, either to expand them along the top or collapse
      * them back into a stack.
@@ -369,10 +361,10 @@
         }
         final WindowInsets insets = mLayout.getRootWindowInsets();
         return mBubblePaddingTop + Math.max(
-            mStatusBarHeight,
-            insets.getDisplayCutout() != null
-                ? insets.getDisplayCutout().getSafeInsetTop()
-                : 0);
+                mStatusBarHeight,
+                insets.getDisplayCutout() != null
+                        ? insets.getDisplayCutout().getSafeInsetTop()
+                        : 0);
     }
 
     /** Description of current animation controller state. */
@@ -391,11 +383,15 @@
         mStackOffsetPx = res.getDimensionPixelSize(R.dimen.bubble_stack_offset);
         mBubblePaddingTop = res.getDimensionPixelSize(R.dimen.bubble_padding_top);
         mBubbleSizePx = res.getDimensionPixelSize(R.dimen.individual_bubble_size);
-        mOverflowBtnWidth = mBubbleSizePx;
         mStatusBarHeight =
                 res.getDimensionPixelSize(com.android.internal.R.dimen.status_bar_height);
         mBubblesMaxRendered = res.getInteger(R.integer.bubbles_max_rendered);
 
+        // Includes overflow button.
+        float totalGapWidth = getWidthForDisplayingBubbles() - (mExpandedViewPadding * 2)
+                - (mBubblesMaxRendered + 1) * mBubbleSizePx;
+        mSpaceBetweenBubbles = totalGapWidth / mBubblesMaxRendered;
+
         // Ensure that all child views are at 1x scale, and visible, in case they were animating
         // in.
         mLayout.setVisibility(View.VISIBLE);
@@ -506,18 +502,10 @@
      * @return Bubble left x from left edge of screen.
      */
     public float getBubbleLeft(int index) {
-        final float bubbleFromRowLeft = index * (mBubbleSizePx + getSpaceBetweenBubbles());
+        final float bubbleFromRowLeft = index * (mBubbleSizePx + mSpaceBetweenBubbles);
         return getRowLeft() + bubbleFromRowLeft;
     }
 
-    public float getOverflowBtnLeft() {
-        if (mLayout == null || mLayout.getChildCount() == 0) {
-            return 0;
-        }
-        return getBubbleLeft(mLayout.getChildCount() - 1) + mBubbleSizePx
-                + getSpaceBetweenBubbles();
-    }
-
     /**
      * When expanded, the bubbles are centered in the screen. In portrait, all available space is
      * used. In landscape we have too much space so the value is restricted. This method accounts
@@ -539,7 +527,7 @@
      * Determines the available screen width without the cutout.
      *
      * @param subtractStableInsets Whether or not stable insets should also be removed from the
-     *                            returned width.
+     *                             returned width.
      * @return the total screen width available accounting for cutouts and insets,
      * iff {@param includeStableInsets} is true.
      */
@@ -566,38 +554,13 @@
         if (mLayout == null) {
             return 0;
         }
-
-        int bubbleCount = mLayout.getChildCount();
-
-        final float totalBubbleWidth = bubbleCount * mBubbleSizePx;
-        final float totalGapWidth = (bubbleCount - 1) * getSpaceBetweenBubbles();
-        float rowWidth = totalGapWidth + totalBubbleWidth;
-        if (mShowOverflowBtn) {
-            rowWidth += getSpaceBetweenBubbles();
-            rowWidth += mOverflowBtnWidth;
-        }
+        float rowWidth = (mLayout.getChildCount() * mBubbleSizePx)
+                + ((mLayout.getChildCount() - 1) * mSpaceBetweenBubbles);
 
         // This display size we're using includes the size of the insets, we want the true
         // center of the display minus the notch here, which means we should include the
         // stable insets (e.g. status bar, nav bar) in this calculation.
         final float trueCenter = getAvailableScreenWidth(false /* subtractStableInsets */) / 2f;
-        final float halfRow = rowWidth / 2f;
-        final float rowLeft = trueCenter - halfRow;
-        return rowLeft;
-    }
-
-    /**
-     * @return Space between bubbles in row above expanded view.
-     */
-    private float getSpaceBetweenBubbles() {
-        final float totalBubbleWidth = mBubblesMaxRendered * mBubbleSizePx;
-        final float rowMargins = mExpandedViewPadding * 2;
-        float totalGapWidth = getWidthForDisplayingBubbles() - rowMargins - totalBubbleWidth;
-        if (mShowOverflowBtn) {
-            totalGapWidth -= mBubbleSizePx;
-        }
-        final int gapCount = mBubblesMaxRendered - 1;
-        final float gapWidth = totalGapWidth / gapCount;
-        return gapWidth;
+        return trueCenter - (rowWidth / 2f);
     }
 }