Merge "Adds an overflow bubble to the bubble bar." into udc-qpr-dev
diff --git a/quickstep/res/values/dimens.xml b/quickstep/res/values/dimens.xml
index 0926b19..e4f6555 100644
--- a/quickstep/res/values/dimens.xml
+++ b/quickstep/res/values/dimens.xml
@@ -363,6 +363,7 @@
     <dimen name="bubblebar_icon_size">50dp</dimen>
     <dimen name="bubblebar_badge_size">24dp</dimen>
     <dimen name="bubblebar_icon_overlap">12dp</dimen>
+    <dimen name="bubblebar_overflow_inset">24dp</dimen>
     <dimen name="bubblebar_icon_spacing">3dp</dimen>
     <dimen name="bubblebar_icon_elevation">1dp</dimen>
 
diff --git a/quickstep/src/com/android/launcher3/taskbar/bubbles/BubbleBarController.java b/quickstep/src/com/android/launcher3/taskbar/bubbles/BubbleBarController.java
index 6d19692..2bd6fcb 100644
--- a/quickstep/src/com/android/launcher3/taskbar/bubbles/BubbleBarController.java
+++ b/quickstep/src/com/android/launcher3/taskbar/bubbles/BubbleBarController.java
@@ -38,11 +38,15 @@
 import android.content.pm.LauncherApps;
 import android.content.pm.PackageManager;
 import android.content.pm.ShortcutInfo;
+import android.content.res.TypedArray;
 import android.graphics.Bitmap;
 import android.graphics.Color;
 import android.graphics.Matrix;
 import android.graphics.Path;
+import android.graphics.drawable.AdaptiveIconDrawable;
+import android.graphics.drawable.ColorDrawable;
 import android.graphics.drawable.Drawable;
+import android.graphics.drawable.InsetDrawable;
 import android.os.Bundle;
 import android.os.SystemProperties;
 import android.os.UserHandle;
@@ -51,6 +55,8 @@
 import android.util.PathParser;
 import android.view.LayoutInflater;
 
+import androidx.appcompat.content.res.AppCompatResources;
+
 import com.android.internal.graphics.ColorUtils;
 import com.android.launcher3.R;
 import com.android.launcher3.icons.BitmapInfo;
@@ -113,7 +119,8 @@
     private final LauncherApps mLauncherApps;
     private final BubbleIconFactory mIconFactory;
 
-    private BubbleBarBubble mSelectedBubble;
+    private BubbleBarItem mSelectedBubble;
+    private BubbleBarOverflow mOverflowBubble;
 
     private BubbleBarViewController mBubbleBarViewController;
     private BubbleStashController mBubbleStashController;
@@ -178,6 +185,16 @@
             mBubbleBarViewController.setHiddenForBubbles(!BUBBLE_BAR_ENABLED);
             mBubbleStashedHandleViewController.setHiddenForBubbles(!BUBBLE_BAR_ENABLED);
         });
+
+        BUBBLE_STATE_EXECUTOR.execute(() -> {
+            if (mOverflowBubble == null) {
+                BubbleBarOverflow overflow = createOverflow(mContext);
+                mMainExecutor.execute(() -> {
+                    mBubbleBarViewController.addBubble(overflow);
+                    mOverflowBubble = overflow;
+                });
+            }
+        });
     }
 
     /**
@@ -329,7 +346,7 @@
      * WMShell that the selection has changed, that should go through
      * {@link SystemUiProxy#showBubble}.
      */
-    public void setSelectedBubble(BubbleBarBubble b) {
+    public void setSelectedBubble(BubbleBarItem b) {
         if (!Objects.equals(b, mSelectedBubble)) {
             if (DEBUG) Log.w(TAG, "selectingBubble: " + b.getKey());
             mSelectedBubble = b;
@@ -424,7 +441,6 @@
         dotColor = ColorUtils.blendARGB(badgeBitmapInfo.color,
                 Color.WHITE, WHITE_SCRIM_ALPHA);
 
-
         LayoutInflater inflater = LayoutInflater.from(context);
         BubbleView bubbleView = (BubbleView) inflater.inflate(
                 R.layout.bubblebar_item_view, bbv, false /* attachToRoot */);
@@ -434,4 +450,37 @@
         bubbleView.setBubble(bubble);
         return bubble;
     }
+
+    private BubbleBarOverflow createOverflow(Context context) {
+        Bitmap bitmap = createOverflowBitmap(context);
+        LayoutInflater inflater = LayoutInflater.from(context);
+        BubbleView bubbleView = (BubbleView) inflater.inflate(
+                R.layout.bubblebar_item_view, mBarView, false /* attachToRoot */);
+        BubbleBarOverflow overflow = new BubbleBarOverflow(bubbleView);
+        bubbleView.setOverflow(overflow, bitmap);
+        return overflow;
+    }
+
+    private Bitmap createOverflowBitmap(Context context) {
+        Drawable iconDrawable = AppCompatResources.getDrawable(mContext,
+                R.drawable.bubble_ic_overflow_button);
+
+        final TypedArray ta = mContext.obtainStyledAttributes(
+                new int[]{
+                        com.android.internal.R.attr.materialColorOnPrimaryFixed,
+                        com.android.internal.R.attr.materialColorPrimaryFixed
+                });
+        int overflowIconColor = ta.getColor(0, Color.WHITE);
+        int overflowBackgroundColor = ta.getColor(1, Color.BLACK);
+        ta.recycle();
+
+        iconDrawable.setTint(overflowIconColor);
+
+        int inset = context.getResources().getDimensionPixelSize(R.dimen.bubblebar_overflow_inset);
+        Drawable foreground = new InsetDrawable(iconDrawable, inset);
+        Drawable drawable = new AdaptiveIconDrawable(new ColorDrawable(overflowBackgroundColor),
+                foreground);
+
+        return mIconFactory.createBadgedIconBitmap(drawable).icon;
+    }
 }
diff --git a/quickstep/src/com/android/launcher3/taskbar/bubbles/BubbleBarBubble.kt b/quickstep/src/com/android/launcher3/taskbar/bubbles/BubbleBarItem.kt
similarity index 75%
rename from quickstep/src/com/android/launcher3/taskbar/bubbles/BubbleBarBubble.kt
rename to quickstep/src/com/android/launcher3/taskbar/bubbles/BubbleBarItem.kt
index 3cd5f75..942a0a1 100644
--- a/quickstep/src/com/android/launcher3/taskbar/bubbles/BubbleBarBubble.kt
+++ b/quickstep/src/com/android/launcher3/taskbar/bubbles/BubbleBarItem.kt
@@ -19,16 +19,19 @@
 import android.graphics.Path
 import com.android.wm.shell.common.bubbles.BubbleInfo
 
+/** An entity in the bubble bar. */
+sealed class BubbleBarItem(open val key: String, open val view: BubbleView)
+
 /** Contains state info about a bubble in the bubble bar as well as presentation information. */
 data class BubbleBarBubble(
     val info: BubbleInfo,
-    val view: BubbleView,
+    override val view: BubbleView,
     val badge: Bitmap,
     val icon: Bitmap,
     val dotColor: Int,
     val dotPath: Path,
     val appName: String
-) {
+) : BubbleBarItem(info.key, view)
 
-    val key: String = info.key
-}
+/** Represents the overflow bubble in the bubble bar. */
+data class BubbleBarOverflow(override val view: BubbleView) : BubbleBarItem("overflow", view)
diff --git a/quickstep/src/com/android/launcher3/taskbar/bubbles/BubbleBarViewController.java b/quickstep/src/com/android/launcher3/taskbar/bubbles/BubbleBarViewController.java
index 3786189..52c144e 100644
--- a/quickstep/src/com/android/launcher3/taskbar/bubbles/BubbleBarViewController.java
+++ b/quickstep/src/com/android/launcher3/taskbar/bubbles/BubbleBarViewController.java
@@ -101,7 +101,7 @@
     }
 
     private void onBubbleClicked(View v) {
-        BubbleBarBubble bubble = ((BubbleView) v).getBubble();
+        BubbleBarItem bubble = ((BubbleView) v).getBubble();
         if (bubble == null) {
             Log.e(TAG, "bubble click listener, bubble was null");
         }
@@ -236,7 +236,7 @@
     /**
      * Removes the provided bubble from the bubble bar.
      */
-    public void removeBubble(BubbleBarBubble b) {
+    public void removeBubble(BubbleBarItem b) {
         if (b != null) {
             mBarView.removeView(b.getView());
         } else {
@@ -247,7 +247,7 @@
     /**
      * Adds the provided bubble to the bubble bar.
      */
-    public void addBubble(BubbleBarBubble b) {
+    public void addBubble(BubbleBarItem b) {
         if (b != null) {
             mBarView.addView(b.getView(), 0, new FrameLayout.LayoutParams(mIconSize, mIconSize));
             b.getView().setOnClickListener(mBubbleClickListener);
@@ -268,7 +268,7 @@
     /**
      * Updates the selected bubble.
      */
-    public void updateSelectedBubble(BubbleBarBubble newlySelected) {
+    public void updateSelectedBubble(BubbleBarItem newlySelected) {
         mBarView.setSelectedBubble(newlySelected.getView());
     }
 
diff --git a/quickstep/src/com/android/launcher3/taskbar/bubbles/BubbleView.java b/quickstep/src/com/android/launcher3/taskbar/bubbles/BubbleView.java
index e22e63a..92b76a6 100644
--- a/quickstep/src/com/android/launcher3/taskbar/bubbles/BubbleView.java
+++ b/quickstep/src/com/android/launcher3/taskbar/bubbles/BubbleView.java
@@ -32,6 +32,7 @@
 
 // TODO: (b/276978250) This is will be similar to WMShell's BadgedImageView, it'd be nice to share.
 // TODO: (b/269670235) currently this doesn't show the 'update dot'
+
 /**
  * View that displays a bubble icon, along with an app badge on either the left or
  * right side of the view.
@@ -48,7 +49,7 @@
     // TODO: (b/273310265) handle RTL
     private boolean mOnLeft = false;
 
-    private BubbleBarBubble mBubble;
+    private BubbleBarItem mBubble;
 
     public BubbleView(Context context) {
         this(context, null);
@@ -97,15 +98,27 @@
         mAppIcon.setImageBitmap(bubble.getBadge());
     }
 
+    void setOverflow(BubbleBarOverflow overflow, Bitmap bitmap) {
+        mBubble = overflow;
+        mBubbleIcon.setImageBitmap(bitmap);
+        hideBadge();
+    }
+
     /** Returns the bubble being rendered in this view. */
     @Nullable
-    BubbleBarBubble getBubble() {
+    BubbleBarItem getBubble() {
         return mBubble;
     }
 
     /** Shows the app badge on this bubble. */
     void showBadge() {
-        Bitmap appBadgeBitmap = mBubble.getBadge();
+        if (mBubble instanceof BubbleBarOverflow) {
+            // The overflow bubble does not have a badge, so just bail.
+            return;
+        }
+        BubbleBarBubble bubble = (BubbleBarBubble) mBubble;
+
+        Bitmap appBadgeBitmap = bubble.getBadge();
         if (appBadgeBitmap == null) {
             mAppIcon.setVisibility(GONE);
             return;
@@ -113,7 +126,7 @@
 
         int translationX;
         if (mOnLeft) {
-            translationX = -(mBubble.getIcon().getWidth() - appBadgeBitmap.getWidth());
+            translationX = -(bubble.getIcon().getWidth() - appBadgeBitmap.getWidth());
         } else {
             translationX = 0;
         }
diff --git a/res/drawable/bubble_ic_overflow_button.xml b/res/drawable/bubble_ic_overflow_button.xml
new file mode 100644
index 0000000..475639e
--- /dev/null
+++ b/res/drawable/bubble_ic_overflow_button.xml
@@ -0,0 +1,24 @@
+<!--
+Copyright (C) 2023 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.
+-->
+<vector xmlns:android="http://schemas.android.com/apk/res/android"
+  android:viewportWidth="24"
+  android:viewportHeight="24"
+  android:width="24dp"
+  android:height="24dp">
+  <path
+      android:fillColor="#1A73E8"
+      android:pathData="M19,13h-6v6h-2v-6H5v-2h6V5h2v6h6v2z"/>
+</vector>