Merge "Move bubble icon / content loading into its own controller class" into main
diff --git a/quickstep/src/com/android/launcher3/taskbar/TaskbarActivityContext.java b/quickstep/src/com/android/launcher3/taskbar/TaskbarActivityContext.java
index e4f7262..2cb37ff 100644
--- a/quickstep/src/com/android/launcher3/taskbar/TaskbarActivityContext.java
+++ b/quickstep/src/com/android/launcher3/taskbar/TaskbarActivityContext.java
@@ -108,6 +108,7 @@
import com.android.launcher3.taskbar.bubbles.BubbleBarView;
import com.android.launcher3.taskbar.bubbles.BubbleBarViewController;
import com.android.launcher3.taskbar.bubbles.BubbleControllers;
+import com.android.launcher3.taskbar.bubbles.BubbleCreator;
import com.android.launcher3.taskbar.bubbles.BubbleDismissController;
import com.android.launcher3.taskbar.bubbles.BubbleDragController;
import com.android.launcher3.taskbar.bubbles.BubblePinController;
@@ -296,7 +297,8 @@
new BubbleBarPinController(this, mDragLayer,
() -> DisplayController.INSTANCE.get(this).getInfo().currentSize),
new BubblePinController(this, mDragLayer,
- () -> DisplayController.INSTANCE.get(this).getInfo().currentSize)
+ () -> DisplayController.INSTANCE.get(this).getInfo().currentSize),
+ new BubbleCreator(this)
));
}
diff --git a/quickstep/src/com/android/launcher3/taskbar/bubbles/BubbleBarController.java b/quickstep/src/com/android/launcher3/taskbar/bubbles/BubbleBarController.java
index 58cd042..fbee080 100644
--- a/quickstep/src/com/android/launcher3/taskbar/bubbles/BubbleBarController.java
+++ b/quickstep/src/com/android/launcher3/taskbar/bubbles/BubbleBarController.java
@@ -15,13 +15,8 @@
*/
package com.android.launcher3.taskbar.bubbles;
-import static android.content.pm.LauncherApps.ShortcutQuery.FLAG_GET_PERSONS_DATA;
-import static android.content.pm.LauncherApps.ShortcutQuery.FLAG_MATCH_CACHED;
-import static android.content.pm.LauncherApps.ShortcutQuery.FLAG_MATCH_DYNAMIC;
-import static android.content.pm.LauncherApps.ShortcutQuery.FLAG_MATCH_PINNED_BY_ANY_LAUNCHER;
import static android.os.Process.THREAD_PRIORITY_BACKGROUND;
-import static com.android.launcher3.icons.FastBitmapDrawable.WHITE_SCRIM_ALPHA;
import static com.android.launcher3.util.Executors.MAIN_EXECUTOR;
import static com.android.systemui.shared.system.QuickStepContract.SYSUI_STATE_BOUNCER_SHOWING;
import static com.android.systemui.shared.system.QuickStepContract.SYSUI_STATE_IME_SHOWING;
@@ -34,35 +29,12 @@
import android.annotation.BinderThread;
import android.annotation.Nullable;
import android.content.Context;
-import android.content.pm.ApplicationInfo;
-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.Point;
-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;
import android.util.ArrayMap;
import android.util.Log;
-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;
-import com.android.launcher3.icons.BubbleIconFactory;
-import com.android.launcher3.shortcuts.ShortcutRequest;
import com.android.launcher3.taskbar.bubbles.stashing.BubbleStashController;
import com.android.launcher3.util.Executors.SimpleThreadFactory;
import com.android.quickstep.SystemUiProxy;
@@ -136,18 +108,16 @@
private static final Executor BUBBLE_STATE_EXECUTOR = Executors.newSingleThreadExecutor(
new SimpleThreadFactory("BubbleStateUpdates-", THREAD_PRIORITY_BACKGROUND));
- private final LauncherApps mLauncherApps;
- private final BubbleIconFactory mIconFactory;
private final SystemUiProxy mSystemUiProxy;
private BubbleBarItem mSelectedBubble;
- private BubbleBarOverflow mOverflowBubble;
private ImeVisibilityChecker mImeVisibilityChecker;
private BubbleBarViewController mBubbleBarViewController;
private BubbleStashController mBubbleStashController;
private Optional<BubbleStashedHandleViewController> mBubbleStashedHandleViewController;
private BubblePinController mBubblePinController;
+ private BubbleCreator mBubbleCreator;
// Cache last sent top coordinate to avoid sending duplicate updates to shell
private int mLastSentBubbleBarTop;
@@ -198,13 +168,6 @@
if (sBubbleBarEnabled) {
mSystemUiProxy.setBubblesListener(this);
}
- mLauncherApps = context.getSystemService(LauncherApps.class);
- mIconFactory = new BubbleIconFactory(context,
- context.getResources().getDimensionPixelSize(R.dimen.bubblebar_icon_size),
- context.getResources().getDimensionPixelSize(R.dimen.bubblebar_badge_size),
- context.getResources().getColor(R.color.important_conversation),
- context.getResources().getDimensionPixelSize(
- com.android.internal.R.dimen.importance_ring_stroke_width));
}
public void onDestroy() {
@@ -219,6 +182,7 @@
mBubbleStashController = bubbleControllers.bubbleStashController;
mBubbleStashedHandleViewController = bubbleControllers.bubbleStashedHandleViewController;
mBubblePinController = bubbleControllers.bubblePinController;
+ mBubbleCreator = bubbleControllers.bubbleCreator;
bubbleControllers.runAfterInit(() -> {
mBubbleBarViewController.setHiddenForBubbles(
@@ -233,27 +197,6 @@
}
/**
- * Creates and adds the overflow bubble to the bubble bar if it hasn't been created yet.
- *
- * <p>This should be called on the {@link #BUBBLE_STATE_EXECUTOR} executor to avoid inflating
- * the overflow multiple times.
- */
- private void createAndAddOverflowIfNeeded() {
- if (mOverflowBubble == null) {
- BubbleBarOverflow overflow = createOverflow(mContext);
- MAIN_EXECUTOR.execute(() -> {
- // we're on the main executor now, so check that the overflow hasn't been created
- // again to avoid races.
- if (mOverflowBubble == null) {
- mBubbleBarViewController.addBubble(
- overflow, /* isExpanding= */ false, /* suppressAnimation= */ true);
- mOverflowBubble = overflow;
- }
- });
- }
- }
-
- /**
* Updates the bubble bar, handle bar, and stash controllers based on sysui state flags.
*/
public void updateStateForSysuiFlags(@SystemUiStateFlags long flags) {
@@ -283,23 +226,25 @@
|| !update.currentBubbleList.isEmpty()) {
// We have bubbles to load
BUBBLE_STATE_EXECUTOR.execute(() -> {
- createAndAddOverflowIfNeeded();
if (update.addedBubble != null) {
- viewUpdate.addedBubble = populateBubble(mContext, update.addedBubble, mBarView,
+ viewUpdate.addedBubble = mBubbleCreator.populateBubble(mContext,
+ update.addedBubble,
+ mBarView,
null /* existingBubble */);
}
if (update.updatedBubble != null) {
BubbleBarBubble existingBubble = mBubbles.get(update.updatedBubble.getKey());
viewUpdate.updatedBubble =
- populateBubble(mContext, update.updatedBubble, mBarView,
+ mBubbleCreator.populateBubble(mContext, update.updatedBubble,
+ mBarView,
existingBubble);
}
if (update.currentBubbleList != null && !update.currentBubbleList.isEmpty()) {
List<BubbleBarBubble> currentBubbles = new ArrayList<>();
for (int i = 0; i < update.currentBubbleList.size(); i++) {
- BubbleBarBubble b =
- populateBubble(mContext, update.currentBubbleList.get(i), mBarView,
- null /* existingBubble */);
+ BubbleBarBubble b = mBubbleCreator.populateBubble(mContext,
+ update.currentBubbleList.get(i), mBarView,
+ null /* existingBubble */);
currentBubbles.add(b);
}
viewUpdate.currentBubbles = currentBubbles;
@@ -530,133 +475,6 @@
// Loading data for the bubbles
//
- @Nullable
- private BubbleBarBubble populateBubble(Context context, BubbleInfo b, BubbleBarView bbv,
- @Nullable BubbleBarBubble existingBubble) {
- String appName;
- Bitmap badgeBitmap;
- Bitmap bubbleBitmap;
- Path dotPath;
- int dotColor;
-
- boolean isImportantConvo = b.isImportantConversation();
-
- ShortcutRequest.QueryResult result = new ShortcutRequest(context,
- new UserHandle(b.getUserId()))
- .forPackage(b.getPackageName(), b.getShortcutId())
- .query(FLAG_MATCH_DYNAMIC
- | FLAG_MATCH_PINNED_BY_ANY_LAUNCHER
- | FLAG_MATCH_CACHED
- | FLAG_GET_PERSONS_DATA);
-
- ShortcutInfo shortcutInfo = result.size() > 0 ? result.get(0) : null;
- if (shortcutInfo == null) {
- Log.w(TAG, "No shortcutInfo found for bubble: " + b.getKey()
- + " with shortcutId: " + b.getShortcutId());
- }
-
- ApplicationInfo appInfo;
- try {
- appInfo = mLauncherApps.getApplicationInfo(
- b.getPackageName(),
- 0,
- new UserHandle(b.getUserId()));
- } catch (PackageManager.NameNotFoundException e) {
- // If we can't find package... don't think we should show the bubble.
- Log.w(TAG, "Unable to find packageName: " + b.getPackageName());
- return null;
- }
- if (appInfo == null) {
- Log.w(TAG, "Unable to find appInfo: " + b.getPackageName());
- return null;
- }
- PackageManager pm = context.getPackageManager();
- appName = String.valueOf(appInfo.loadLabel(pm));
- Drawable appIcon = appInfo.loadUnbadgedIcon(pm);
- Drawable badgedIcon = pm.getUserBadgedIcon(appIcon, new UserHandle(b.getUserId()));
-
- // Badged bubble image
- Drawable bubbleDrawable = mIconFactory.getBubbleDrawable(context, shortcutInfo,
- b.getIcon());
- if (bubbleDrawable == null) {
- // Default to app icon
- bubbleDrawable = appIcon;
- }
-
- BitmapInfo badgeBitmapInfo = mIconFactory.getBadgeBitmap(badgedIcon, isImportantConvo);
- badgeBitmap = badgeBitmapInfo.icon;
-
- float[] bubbleBitmapScale = new float[1];
- bubbleBitmap = mIconFactory.getBubbleBitmap(bubbleDrawable, bubbleBitmapScale);
-
- // Dot color & placement
- Path iconPath = PathParser.createPathFromPathData(
- context.getResources().getString(
- com.android.internal.R.string.config_icon_mask));
- Matrix matrix = new Matrix();
- float scale = bubbleBitmapScale[0];
- float radius = BubbleView.DEFAULT_PATH_SIZE / 2f;
- matrix.setScale(scale /* x scale */, scale /* y scale */, radius /* pivot x */,
- radius /* pivot y */);
- iconPath.transform(matrix);
- dotPath = iconPath;
- dotColor = ColorUtils.blendARGB(badgeBitmapInfo.color,
- Color.WHITE, WHITE_SCRIM_ALPHA / 255f);
-
- if (existingBubble == null) {
- LayoutInflater inflater = LayoutInflater.from(context);
- BubbleView bubbleView = (BubbleView) inflater.inflate(
- R.layout.bubblebar_item_view, bbv, false /* attachToRoot */);
-
- BubbleBarBubble bubble = new BubbleBarBubble(b, bubbleView,
- badgeBitmap, bubbleBitmap, dotColor, dotPath, appName);
- bubbleView.setBubble(bubble);
- return bubble;
- } else {
- // If we already have a bubble (so it already has an inflated view), update it.
- existingBubble.setInfo(b);
- existingBubble.setBadge(badgeBitmap);
- existingBubble.setIcon(bubbleBitmap);
- existingBubble.setDotColor(dotColor);
- existingBubble.setDotPath(dotPath);
- existingBubble.setAppName(appName);
- return existingBubble;
- }
- }
-
- private BubbleBarOverflow createOverflow(Context context) {
- Bitmap bitmap = createOverflowBitmap(context);
- LayoutInflater inflater = LayoutInflater.from(context);
- BubbleView bubbleView = (BubbleView) inflater.inflate(
- R.layout.bubble_bar_overflow_button, 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[]{
- R.attr.materialColorOnPrimaryFixed,
- 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;
- }
-
private void onBubbleBarBoundsChanged() {
int newTop = mBarView.getRestingTopPositionOnScreen();
if (newTop != mLastSentBubbleBarTop) {
diff --git a/quickstep/src/com/android/launcher3/taskbar/bubbles/BubbleBarViewController.java b/quickstep/src/com/android/launcher3/taskbar/bubbles/BubbleBarViewController.java
index 916b7b1..590916e 100644
--- a/quickstep/src/com/android/launcher3/taskbar/bubbles/BubbleBarViewController.java
+++ b/quickstep/src/com/android/launcher3/taskbar/bubbles/BubbleBarViewController.java
@@ -75,6 +75,7 @@
private View.OnClickListener mBubbleClickListener;
private View.OnClickListener mBubbleBarClickListener;
private BubbleView.Controller mBubbleViewController;
+ private BubbleBarOverflow mOverflowBubble;
// These are exposed to {@link BubbleStashController} to animate for stashing/un-stashing
private final MultiValueAlpha mBubbleBarAlpha;
@@ -121,6 +122,8 @@
mBubbleClickListener = v -> onBubbleClicked((BubbleView) v);
mBubbleBarClickListener = v -> expandBubbleBar();
mBubbleDragController.setupBubbleBarView(mBarView);
+ mOverflowBubble = bubbleControllers.bubbleCreator.createOverflow(mBarView);
+ addOverflow();
mBarView.setOnClickListener(mBubbleBarClickListener);
mBarView.addOnLayoutChangeListener(
(v, left, top, right, bottom, oldLeft, oldTop, oldRight, oldBottom) -> {
@@ -492,6 +495,15 @@
}
/**
+ * Adds the overflow view to the bubble bar.
+ */
+ public void addOverflow() {
+ mBarView.addBubble(mOverflowBubble.getView());
+ mOverflowBubble.getView().setOnClickListener(mBubbleClickListener);
+ mOverflowBubble.getView().setController(mBubbleViewController);
+ }
+
+ /**
* Adds the provided bubble to the bubble bar.
*/
public void addBubble(BubbleBarItem b, boolean isExpanding, boolean suppressAnimation) {
@@ -501,10 +513,6 @@
mBubbleDragController.setupBubbleView(b.getView());
b.getView().setController(mBubbleViewController);
- if (b instanceof BubbleBarOverflow) {
- return;
- }
-
if (suppressAnimation || !(b instanceof BubbleBarBubble bubble)) {
// the bubble bar and handle are initialized as part of the first bubble animation.
// if the animation is suppressed, immediately stash or show the bubble bar to
diff --git a/quickstep/src/com/android/launcher3/taskbar/bubbles/BubbleControllers.java b/quickstep/src/com/android/launcher3/taskbar/bubbles/BubbleControllers.java
index a5243fa..8478dc2 100644
--- a/quickstep/src/com/android/launcher3/taskbar/bubbles/BubbleControllers.java
+++ b/quickstep/src/com/android/launcher3/taskbar/bubbles/BubbleControllers.java
@@ -33,6 +33,7 @@
public final BubbleDismissController bubbleDismissController;
public final BubbleBarPinController bubbleBarPinController;
public final BubblePinController bubblePinController;
+ public final BubbleCreator bubbleCreator;
private final RunnableList mPostInitRunnables = new RunnableList();
@@ -49,7 +50,8 @@
BubbleDragController bubbleDragController,
BubbleDismissController bubbleDismissController,
BubbleBarPinController bubbleBarPinController,
- BubblePinController bubblePinController) {
+ BubblePinController bubblePinController,
+ BubbleCreator bubbleCreator) {
this.bubbleBarController = bubbleBarController;
this.bubbleBarViewController = bubbleBarViewController;
this.bubbleStashController = bubbleStashController;
@@ -58,6 +60,7 @@
this.bubbleDismissController = bubbleDismissController;
this.bubbleBarPinController = bubbleBarPinController;
this.bubblePinController = bubblePinController;
+ this.bubbleCreator = bubbleCreator;
}
/**
diff --git a/quickstep/src/com/android/launcher3/taskbar/bubbles/BubbleCreator.java b/quickstep/src/com/android/launcher3/taskbar/bubbles/BubbleCreator.java
new file mode 100644
index 0000000..8e9a2f6
--- /dev/null
+++ b/quickstep/src/com/android/launcher3/taskbar/bubbles/BubbleCreator.java
@@ -0,0 +1,221 @@
+/*
+ * Copyright (C) 2024 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.launcher3.taskbar.bubbles;
+
+import static android.content.pm.LauncherApps.ShortcutQuery.FLAG_GET_PERSONS_DATA;
+import static android.content.pm.LauncherApps.ShortcutQuery.FLAG_MATCH_CACHED;
+import static android.content.pm.LauncherApps.ShortcutQuery.FLAG_MATCH_DYNAMIC;
+import static android.content.pm.LauncherApps.ShortcutQuery.FLAG_MATCH_PINNED_BY_ANY_LAUNCHER;
+
+import static com.android.launcher3.icons.FastBitmapDrawable.WHITE_SCRIM_ALPHA;
+
+import android.annotation.Nullable;
+import android.content.Context;
+import android.content.pm.ApplicationInfo;
+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.UserHandle;
+import android.util.Log;
+import android.util.PathParser;
+import android.view.LayoutInflater;
+import android.view.ViewGroup;
+
+import androidx.appcompat.content.res.AppCompatResources;
+
+import com.android.internal.graphics.ColorUtils;
+import com.android.launcher3.R;
+import com.android.launcher3.icons.BitmapInfo;
+import com.android.launcher3.icons.BubbleIconFactory;
+import com.android.launcher3.shortcuts.ShortcutRequest;
+import com.android.wm.shell.common.bubbles.BubbleInfo;
+
+/**
+ * Loads the necessary info to populate / present a bubble (name, icon, shortcut).
+ */
+public class BubbleCreator {
+
+ private static final String TAG = BubbleCreator.class.getSimpleName();
+
+ private final Context mContext;
+ private final LauncherApps mLauncherApps;
+ private final BubbleIconFactory mIconFactory;
+
+ public BubbleCreator(Context context) {
+ mContext = context;
+ mLauncherApps = mContext.getSystemService(LauncherApps.class);
+ mIconFactory = new BubbleIconFactory(context,
+ context.getResources().getDimensionPixelSize(R.dimen.bubblebar_icon_size),
+ context.getResources().getDimensionPixelSize(R.dimen.bubblebar_badge_size),
+ context.getResources().getColor(R.color.important_conversation),
+ context.getResources().getDimensionPixelSize(
+ com.android.internal.R.dimen.importance_ring_stroke_width));
+ }
+
+ /**
+ * Creates a BubbleBarBubble object, including the view if needed, and populates it with
+ * the info needed for presentation.
+ *
+ * @param context the context to use for inflation.
+ * @param info the info to use to populate the bubble.
+ * @param barView the parent view for the bubble (bubble is not added to the view).
+ * @param existingBubble if a bubble exists already, this object gets updated with the new
+ * info & returned (& any existing views are reused instead of inflating
+ * new ones.
+ */
+ @Nullable
+ public BubbleBarBubble populateBubble(Context context, BubbleInfo info, ViewGroup barView,
+ @Nullable BubbleBarBubble existingBubble) {
+ String appName;
+ Bitmap badgeBitmap;
+ Bitmap bubbleBitmap;
+ Path dotPath;
+ int dotColor;
+
+ boolean isImportantConvo = info.isImportantConversation();
+
+ ShortcutRequest.QueryResult result = new ShortcutRequest(context,
+ new UserHandle(info.getUserId()))
+ .forPackage(info.getPackageName(), info.getShortcutId())
+ .query(FLAG_MATCH_DYNAMIC
+ | FLAG_MATCH_PINNED_BY_ANY_LAUNCHER
+ | FLAG_MATCH_CACHED
+ | FLAG_GET_PERSONS_DATA);
+
+ ShortcutInfo shortcutInfo = result.size() > 0 ? result.get(0) : null;
+ if (shortcutInfo == null) {
+ Log.w(TAG, "No shortcutInfo found for bubble: " + info.getKey()
+ + " with shortcutId: " + info.getShortcutId());
+ }
+
+ ApplicationInfo appInfo;
+ try {
+ appInfo = mLauncherApps.getApplicationInfo(
+ info.getPackageName(),
+ 0,
+ new UserHandle(info.getUserId()));
+ } catch (PackageManager.NameNotFoundException e) {
+ // If we can't find package... don't think we should show the bubble.
+ Log.w(TAG, "Unable to find packageName: " + info.getPackageName());
+ return null;
+ }
+ if (appInfo == null) {
+ Log.w(TAG, "Unable to find appInfo: " + info.getPackageName());
+ return null;
+ }
+ PackageManager pm = context.getPackageManager();
+ appName = String.valueOf(appInfo.loadLabel(pm));
+ Drawable appIcon = appInfo.loadUnbadgedIcon(pm);
+ Drawable badgedIcon = pm.getUserBadgedIcon(appIcon, new UserHandle(info.getUserId()));
+
+ // Badged bubble image
+ Drawable bubbleDrawable = mIconFactory.getBubbleDrawable(context, shortcutInfo,
+ info.getIcon());
+ if (bubbleDrawable == null) {
+ // Default to app icon
+ bubbleDrawable = appIcon;
+ }
+
+ BitmapInfo badgeBitmapInfo = mIconFactory.getBadgeBitmap(badgedIcon, isImportantConvo);
+ badgeBitmap = badgeBitmapInfo.icon;
+
+ float[] bubbleBitmapScale = new float[1];
+ bubbleBitmap = mIconFactory.getBubbleBitmap(bubbleDrawable, bubbleBitmapScale);
+
+ // Dot color & placement
+ Path iconPath = PathParser.createPathFromPathData(
+ context.getResources().getString(
+ com.android.internal.R.string.config_icon_mask));
+ Matrix matrix = new Matrix();
+ float scale = bubbleBitmapScale[0];
+ float radius = BubbleView.DEFAULT_PATH_SIZE / 2f;
+ matrix.setScale(scale /* x scale */, scale /* y scale */, radius /* pivot x */,
+ radius /* pivot y */);
+ iconPath.transform(matrix);
+ dotPath = iconPath;
+ dotColor = ColorUtils.blendARGB(badgeBitmapInfo.color,
+ Color.WHITE, WHITE_SCRIM_ALPHA / 255f);
+
+ if (existingBubble == null) {
+ LayoutInflater inflater = LayoutInflater.from(context);
+ BubbleView bubbleView = (BubbleView) inflater.inflate(
+ R.layout.bubblebar_item_view, barView, false /* attachToRoot */);
+
+ BubbleBarBubble bubble = new BubbleBarBubble(info, bubbleView,
+ badgeBitmap, bubbleBitmap, dotColor, dotPath, appName);
+ bubbleView.setBubble(bubble);
+ return bubble;
+ } else {
+ // If we already have a bubble (so it already has an inflated view), update it.
+ existingBubble.setInfo(info);
+ existingBubble.setBadge(badgeBitmap);
+ existingBubble.setIcon(bubbleBitmap);
+ existingBubble.setDotColor(dotColor);
+ existingBubble.setDotPath(dotPath);
+ existingBubble.setAppName(appName);
+ return existingBubble;
+ }
+ }
+
+ /**
+ * Creates the overflow view shown in the bubble bar.
+ *
+ * @param barView the parent view for the bubble (bubble is not added to the view).
+ */
+ public BubbleBarOverflow createOverflow(ViewGroup barView) {
+ Bitmap bitmap = createOverflowBitmap();
+ LayoutInflater inflater = LayoutInflater.from(mContext);
+ BubbleView bubbleView = (BubbleView) inflater.inflate(
+ R.layout.bubble_bar_overflow_button, barView, false /* attachToRoot */);
+ BubbleBarOverflow overflow = new BubbleBarOverflow(bubbleView);
+ bubbleView.setOverflow(overflow, bitmap);
+ return overflow;
+ }
+
+ private Bitmap createOverflowBitmap() {
+ Drawable iconDrawable = AppCompatResources.getDrawable(mContext,
+ R.drawable.bubble_ic_overflow_button);
+
+ final TypedArray ta = mContext.obtainStyledAttributes(
+ new int[]{
+ R.attr.materialColorOnPrimaryFixed,
+ R.attr.materialColorPrimaryFixed
+ });
+ int overflowIconColor = ta.getColor(0, Color.WHITE);
+ int overflowBackgroundColor = ta.getColor(1, Color.BLACK);
+ ta.recycle();
+
+ iconDrawable.setTint(overflowIconColor);
+
+ int inset = mContext.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;
+ }
+
+}