Reorder overflow button with kotlin refactor

Add overflow button when overflow is created
Reorder overflow button instead of remove+add
Convert overflow view provider to kotlin

Bug: 161396059
Test: toggle all themes, display size; dismiss single bubble /
entire stack; promote bubble from overflow: no regressions

Change-Id: Iee68c982d1591de4f010aac6c620c7353737bc11
diff --git a/packages/SystemUI/src/com/android/systemui/bubbles/BubbleOverflow.java b/packages/SystemUI/src/com/android/systemui/bubbles/BubbleOverflow.java
deleted file mode 100644
index bb9d109..0000000
--- a/packages/SystemUI/src/com/android/systemui/bubbles/BubbleOverflow.java
+++ /dev/null
@@ -1,179 +0,0 @@
-/*
- * Copyright (C) 2020 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- *      http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package com.android.systemui.bubbles;
-
-import static android.view.Display.INVALID_DISPLAY;
-import static android.view.View.GONE;
-
-import static com.android.systemui.bubbles.BadgedImageView.DEFAULT_PATH_SIZE;
-
-import android.content.Context;
-import android.content.res.Configuration;
-import android.content.res.Resources;
-import android.graphics.Bitmap;
-import android.graphics.Matrix;
-import android.graphics.Path;
-import android.graphics.drawable.AdaptiveIconDrawable;
-import android.graphics.drawable.ColorDrawable;
-import android.graphics.drawable.InsetDrawable;
-import android.util.PathParser;
-import android.util.TypedValue;
-import android.view.LayoutInflater;
-import android.view.View;
-import android.view.ViewGroup;
-import android.widget.ImageView;
-
-import com.android.systemui.R;
-
-/**
- * Class for showing aged out bubbles.
- */
-public class BubbleOverflow implements BubbleViewProvider {
-    public static final String KEY = "Overflow";
-
-    private BadgedImageView mOverflowBtn;
-    private BubbleExpandedView mExpandedView;
-    private LayoutInflater mInflater;
-    private Context mContext;
-    private Bitmap mIcon;
-    private Path mPath;
-    private int mBitmapSize;
-    private int mIconBitmapSize;
-    private int mDotColor;
-
-    public BubbleOverflow(Context context) {
-        mContext = context;
-        mInflater = LayoutInflater.from(context);
-    }
-
-    void setUpOverflow(ViewGroup parentViewGroup, BubbleStackView stackView) {
-        updateDimensions();
-        mExpandedView = (BubbleExpandedView) mInflater.inflate(
-                R.layout.bubble_expanded_view, parentViewGroup /* root */,
-                false /* attachToRoot */);
-        mExpandedView.setOverflow(true);
-        mExpandedView.setStackView(stackView);
-        mExpandedView.applyThemeAttrs();
-        updateIcon(mContext, parentViewGroup);
-    }
-
-    void updateDimensions() {
-        mBitmapSize = mContext.getResources().getDimensionPixelSize(R.dimen.bubble_bitmap_size);
-        mIconBitmapSize = mContext.getResources().getDimensionPixelSize(
-                R.dimen.bubble_overflow_icon_bitmap_size);
-        if (mExpandedView != null) {
-            mExpandedView.updateDimensions();
-        }
-    }
-
-    void updateIcon(Context context, ViewGroup parentViewGroup) {
-        mContext = context;
-        mInflater = LayoutInflater.from(context);
-        mOverflowBtn = (BadgedImageView) mInflater.inflate(R.layout.bubble_overflow_button,
-                parentViewGroup /* root */,
-                false /* attachToRoot */);
-        mOverflowBtn.setContentDescription(mContext.getResources().getString(
-                R.string.bubble_overflow_button_content_description));
-        Resources res = mContext.getResources();
-
-        // Set color for button icon and dot
-        TypedValue typedValue = new TypedValue();
-        mContext.getTheme().resolveAttribute(android.R.attr.colorAccent, typedValue, true);
-        int colorAccent = mContext.getColor(typedValue.resourceId);
-        mOverflowBtn.getDrawable().setTint(colorAccent);
-        mDotColor = colorAccent;
-
-        // Set color for button and activity background
-        ColorDrawable bg = new ColorDrawable(res.getColor(R.color.bubbles_light));
-        final int mode = res.getConfiguration().uiMode & Configuration.UI_MODE_NIGHT_MASK;
-        if (mode == Configuration.UI_MODE_NIGHT_YES) {
-            bg = new ColorDrawable(res.getColor(R.color.bubbles_dark));
-        }
-
-        // Apply icon inset
-        InsetDrawable fg = new InsetDrawable(mOverflowBtn.getDrawable(),
-                mBitmapSize - mIconBitmapSize /* inset */);
-        AdaptiveIconDrawable adaptiveIconDrawable = new AdaptiveIconDrawable(bg, fg);
-
-        BubbleIconFactory iconFactory = new BubbleIconFactory(mContext);
-        mIcon = iconFactory.createBadgedIconBitmap(adaptiveIconDrawable,
-                null /* user */,
-                true /* shrinkNonAdaptiveIcons */).icon;
-
-        // Get path with dot location
-        float scale = iconFactory.getNormalizer().getScale(mOverflowBtn.getDrawable(),
-                null /* outBounds */, null /* path */, null /* outMaskShape */);
-        float radius = DEFAULT_PATH_SIZE / 2f;
-        mPath = PathParser.createPathFromPathData(
-                mContext.getResources().getString(com.android.internal.R.string.config_icon_mask));
-        Matrix matrix = new Matrix();
-        matrix.setScale(scale /* x scale */, scale /* y scale */, radius /* pivot x */,
-                radius /* pivot y */);
-        mPath.transform(matrix);
-
-        mOverflowBtn.setRenderedBubble(this);
-    }
-
-    void setVisible(int visible) {
-        mOverflowBtn.setVisibility(visible);
-    }
-
-    @Override
-    public BubbleExpandedView getExpandedView() {
-        return mExpandedView;
-    }
-
-    @Override
-    public int getDotColor() {
-        return mDotColor;
-    }
-
-    @Override
-    public Bitmap getBadgedImage() {
-        return mIcon;
-    }
-
-    @Override
-    public boolean showDot() {
-        return false;
-    }
-
-    @Override
-    public Path getDotPath() {
-        return mPath;
-    }
-
-    @Override
-    public void setContentVisibility(boolean visible) {
-        mExpandedView.setContentVisibility(visible);
-    }
-
-    @Override
-    public View getIconView() {
-        return mOverflowBtn;
-    }
-
-    @Override
-    public String getKey() {
-        return BubbleOverflow.KEY;
-    }
-
-    @Override
-    public int getDisplayId() {
-        return mExpandedView != null ? mExpandedView.getVirtualDisplayId() : INVALID_DISPLAY;
-    }
-}
diff --git a/packages/SystemUI/src/com/android/systemui/bubbles/BubbleOverflow.kt b/packages/SystemUI/src/com/android/systemui/bubbles/BubbleOverflow.kt
new file mode 100644
index 0000000..155b71b
--- /dev/null
+++ b/packages/SystemUI/src/com/android/systemui/bubbles/BubbleOverflow.kt
@@ -0,0 +1,163 @@
+/*
+ * Copyright (C) 2020 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.systemui.bubbles
+
+import android.content.Context
+import android.content.res.Configuration
+import android.graphics.Bitmap
+import android.graphics.Matrix
+import android.graphics.Path
+import android.graphics.drawable.AdaptiveIconDrawable
+import android.graphics.drawable.ColorDrawable
+import android.graphics.drawable.InsetDrawable
+import android.util.PathParser
+import android.util.TypedValue
+import android.view.LayoutInflater
+import android.view.View
+import android.widget.FrameLayout
+import com.android.systemui.R
+
+class BubbleOverflow(
+    private val context: Context,
+    private val stack: BubbleStackView
+) : BubbleViewProvider {
+
+    private var bitmap: Bitmap? = null
+    private var dotPath: Path? = null
+    private var bitmapSize = 0
+    private var iconBitmapSize = 0
+    private var dotColor = 0
+
+    private val inflater: LayoutInflater = LayoutInflater.from(context)
+    private val expandedView: BubbleExpandedView = inflater
+        .inflate(R.layout.bubble_expanded_view, null /* root */, false /* attachToRoot */)
+            as BubbleExpandedView
+    private val overflowBtn: BadgedImageView = inflater
+        .inflate(R.layout.bubble_overflow_button, null /* root */, false /* attachToRoot */)
+            as BadgedImageView
+    init {
+        updateResources()
+        with(expandedView) {
+            setOverflow(true)
+            setStackView(stack)
+            applyThemeAttrs()
+        }
+        with(overflowBtn) {
+            setContentDescription(context.resources.getString(
+                R.string.bubble_overflow_button_content_description))
+            updateBtnTheme()
+        }
+    }
+
+    fun update() {
+        updateResources()
+        expandedView.applyThemeAttrs()
+        // Apply inset and new style to fresh icon drawable.
+        overflowBtn.setImageResource(R.drawable.ic_bubble_overflow_button)
+        updateBtnTheme()
+    }
+
+    fun updateResources() {
+        bitmapSize = context.resources.getDimensionPixelSize(R.dimen.bubble_bitmap_size)
+        iconBitmapSize = context.resources.getDimensionPixelSize(
+                R.dimen.bubble_overflow_icon_bitmap_size)
+        val bubbleSize = context.resources.getDimensionPixelSize(R.dimen.individual_bubble_size)
+        overflowBtn.setLayoutParams(FrameLayout.LayoutParams(bubbleSize, bubbleSize))
+        expandedView.updateDimensions()
+    }
+
+    fun updateBtnTheme() {
+        val res = context.resources
+
+        // Set overflow button accent color, dot color
+        val typedValue = TypedValue()
+        context.theme.resolveAttribute(android.R.attr.colorAccent, typedValue, true)
+
+        val colorAccent = res.getColor(typedValue.resourceId)
+        overflowBtn.getDrawable()?.setTint(colorAccent)
+        dotColor = colorAccent
+
+        // Set button and activity background color
+        val nightMode = (res.configuration.uiMode and Configuration.UI_MODE_NIGHT_MASK
+                == Configuration.UI_MODE_NIGHT_YES)
+        val bg = ColorDrawable(res.getColor(
+                if (nightMode) R.color.bubbles_dark else R.color.bubbles_light))
+
+        // Set button icon
+        val iconFactory = BubbleIconFactory(context)
+        val fg = InsetDrawable(overflowBtn.getDrawable(),
+                bitmapSize - iconBitmapSize /* inset */)
+        bitmap = iconFactory.createBadgedIconBitmap(AdaptiveIconDrawable(bg, fg),
+                null /* user */, true /* shrinkNonAdaptiveIcons */).icon
+
+        // Set dot path
+        dotPath = PathParser.createPathFromPathData(
+                res.getString(com.android.internal.R.string.config_icon_mask))
+        val scale = iconFactory.normalizer.getScale(overflowBtn.getDrawable(),
+                null /* outBounds */, null /* path */, null /* outMaskShape */)
+        val radius = BadgedImageView.DEFAULT_PATH_SIZE / 2f
+        val matrix = Matrix()
+        matrix.setScale(scale /* x scale */, scale /* y scale */, radius /* pivot x */,
+                radius /* pivot y */)
+        dotPath?.transform(matrix)
+        overflowBtn.setRenderedBubble(this)
+    }
+
+    fun setVisible(visible: Int) {
+        overflowBtn.visibility = visible
+    }
+
+    override fun getExpandedView(): BubbleExpandedView? {
+        return expandedView
+    }
+
+    override fun getDotColor(): Int {
+        return dotColor
+    }
+
+    override fun getBadgedImage(): Bitmap? {
+        return bitmap
+    }
+
+    override fun showDot(): Boolean {
+        return false
+    }
+
+    override fun getDotPath(): Path? {
+        return dotPath
+    }
+
+    override fun setContentVisibility(visible: Boolean) {
+        expandedView.setContentVisibility(visible)
+    }
+
+    override fun getIconView(): View? {
+        return overflowBtn
+    }
+
+    override fun getKey(): String {
+        return KEY
+    }
+
+    override fun getDisplayId(): Int {
+        return expandedView.virtualDisplayId
+    }
+
+    companion object {
+        @JvmField val KEY = "Overflow"
+    }
+}
\ No newline at end of file
diff --git a/packages/SystemUI/src/com/android/systemui/bubbles/BubbleStackView.java b/packages/SystemUI/src/com/android/systemui/bubbles/BubbleStackView.java
index f02945e..75c6385 100644
--- a/packages/SystemUI/src/com/android/systemui/bubbles/BubbleStackView.java
+++ b/packages/SystemUI/src/com/android/systemui/bubbles/BubbleStackView.java
@@ -907,7 +907,16 @@
         setFocusable(true);
         mBubbleContainer.bringToFront();
 
-        setUpOverflow();
+        mBubbleOverflow = new BubbleOverflow(getContext(), this);
+        mBubbleContainer.addView(mBubbleOverflow.getIconView(),
+                mBubbleContainer.getChildCount() /* index */,
+                new FrameLayout.LayoutParams(ViewGroup.LayoutParams.WRAP_CONTENT,
+                        ViewGroup.LayoutParams.WRAP_CONTENT));
+        updateOverflow();
+        mBubbleOverflow.getIconView().setOnClickListener((View v) -> {
+            setSelectedBubble(mBubbleOverflow);
+            showManageMenu(false);
+        });
 
         mOnImeVisibilityChanged = onImeVisibilityChanged;
         mHideCurrentInputMethodCallback = hideCurrentInputMethodCallback;
@@ -933,7 +942,7 @@
                 (v, left, top, right, bottom, oldLeft, oldTop, oldRight, oldBottom) -> {
                     mExpandedAnimationController.updateResources(mOrientation, mDisplaySize);
                     mStackAnimationController.updateResources(mOrientation);
-                    mBubbleOverflow.updateDimensions();
+                    mBubbleOverflow.updateResources();
 
                     // Need to update the padding around the view
                     WindowInsets insets = getRootWindowInsets();
@@ -1187,32 +1196,21 @@
         addView(mFlyout, new FrameLayout.LayoutParams(WRAP_CONTENT, WRAP_CONTENT));
     }
 
-    private void setUpOverflow() {
-        int overflowBtnIndex = 0;
-        if (mBubbleOverflow == null) {
-            mBubbleOverflow = new BubbleOverflow(getContext());
-            mBubbleOverflow.setUpOverflow(mBubbleContainer, this);
-        } else {
-            mBubbleContainer.removeView(mBubbleOverflow.getIconView());
-            mBubbleOverflow.setUpOverflow(mBubbleContainer, this);
-            overflowBtnIndex = mBubbleContainer.getChildCount();
-        }
-        mBubbleContainer.addView(mBubbleOverflow.getIconView(), overflowBtnIndex,
-                new FrameLayout.LayoutParams(WRAP_CONTENT, WRAP_CONTENT));
-        mBubbleOverflow.getIconView().setOnClickListener(v -> {
-            setSelectedBubble(mBubbleOverflow);
-            showManageMenu(false);
-        });
+    private void updateOverflow() {
+        mBubbleOverflow.update();
+        mBubbleContainer.reorderView(mBubbleOverflow.getIconView(),
+                mBubbleContainer.getChildCount() - 1 /* index */);
         updateOverflowVisibility();
     }
+
     /**
      * Handle theme changes.
      */
     public void onThemeChanged() {
         setUpFlyout();
-        setUpOverflow();
         setUpUserEducation();
         setUpManageMenu();
+        updateOverflow();
         updateExpandedViewTheme();
     }
 
@@ -1261,7 +1259,7 @@
 
     /** Respond to the display size change by recalculating view size and location. */
     public void onDisplaySizeChanged() {
-        setUpOverflow();
+        updateOverflow();
 
         WindowManager wm = (WindowManager) getContext().getSystemService(Context.WINDOW_SERVICE);
         wm.getDefaultDisplay().getRealSize(mDisplaySize);