Enable left badge, hidden badge
Manually composit badge on icon bitmap
Follow up cls to render dot and badge in separate views
Fixes: 161168537
Test: drag collapsed stack left and right
- top bubble moves badge right and left
- no badge for bubbles below
Test: expand stack from left and right
- stack and overflow bubbles have right-side badge
Change-Id: I7da824ef120033c65c9a0faee23ef2c2e5dc89a7
diff --git a/packages/SystemUI/src/com/android/systemui/bubbles/BadgedImageView.java b/packages/SystemUI/src/com/android/systemui/bubbles/BadgedImageView.java
index a86a469..9f7358b 100644
--- a/packages/SystemUI/src/com/android/systemui/bubbles/BadgedImageView.java
+++ b/packages/SystemUI/src/com/android/systemui/bubbles/BadgedImageView.java
@@ -17,9 +17,12 @@
import android.annotation.Nullable;
import android.content.Context;
+import android.graphics.Bitmap;
import android.graphics.Canvas;
+import android.graphics.PaintFlagsDrawFilter;
import android.graphics.Path;
import android.graphics.Rect;
+import android.graphics.drawable.Drawable;
import android.util.AttributeSet;
import android.util.PathParser;
import android.widget.ImageView;
@@ -30,6 +33,9 @@
import java.util.EnumSet;
+import static android.graphics.Paint.DITHER_FLAG;
+import static android.graphics.Paint.FILTER_BITMAP_FLAG;
+
/**
* View that displays an adaptive icon with an app-badge and a dot.
*
@@ -43,6 +49,8 @@
public static final float WHITE_SCRIM_ALPHA = 0.54f;
/** Same as value in Launcher3 IconShape */
public static final int DEFAULT_PATH_SIZE = 100;
+ /** Same as value in Launcher3 BaseIconFactory */
+ private static final float ICON_BADGE_SCALE = 0.444f;
/**
* Flags that suppress the visibility of the 'new' dot, for one reason or another. If any of
@@ -69,6 +77,7 @@
private BubbleViewProvider mBubble;
private int mBubbleBitmapSize;
+ private int mBubbleSize;
private DotRenderer mDotRenderer;
private DotRenderer.DrawParams mDrawParams;
private boolean mOnLeft;
@@ -93,6 +102,7 @@
int defStyleRes) {
super(context, attrs, defStyleAttr, defStyleRes);
mBubbleBitmapSize = getResources().getDimensionPixelSize(R.dimen.bubble_bitmap_size);
+ mBubbleSize = getResources().getDimensionPixelSize(R.dimen.individual_bubble_size);
mDrawParams = new DotRenderer.DrawParams();
Path iconPath = PathParser.createPathFromPathData(
@@ -108,7 +118,7 @@
*/
public void setRenderedBubble(BubbleViewProvider bubble) {
mBubble = bubble;
- setImageBitmap(bubble.getBadgedImage());
+ showBadge();
mDotColor = bubble.getDotColor();
drawDot(bubble.getDotPath());
}
@@ -161,14 +171,6 @@
}
/**
- * Set whether the dot should appear on left or right side of the view.
- */
- void setDotOnLeft(boolean onLeft) {
- mOnLeft = onLeft;
- invalidate();
- }
-
- /**
* @param iconPath The new icon path to use when calculating dot position.
*/
void drawDot(Path iconPath) {
@@ -219,22 +221,29 @@
return mDotColor;
}
- /** Sets the position of the 'new' dot, animating it out and back in if requested. */
- void setDotPositionOnLeft(boolean onLeft, boolean animate) {
- if (animate && onLeft != getDotOnLeft() && shouldDrawDot()) {
+ /** Sets the position of the dot and badge, animating them out and back in if requested. */
+ void animateDotBadgePositions(boolean onLeft) {
+ mOnLeft = onLeft;
+
+ if (onLeft != getDotOnLeft() && shouldDrawDot()) {
animateDotScale(0f /* showDot */, () -> {
- setDotOnLeft(onLeft);
+ invalidate();
animateDotScale(1.0f, null /* after */);
});
- } else {
- setDotOnLeft(onLeft);
}
+ // TODO animate badge
+ showBadge();
+
}
- boolean getDotPositionOnLeft() {
- return getDotOnLeft();
+ /** Sets the position of the dot and badge. */
+ void setDotBadgeOnLeft(boolean onLeft) {
+ mOnLeft = onLeft;
+ invalidate();
+ showBadge();
}
+
/** Whether to draw the dot in onDraw(). */
private boolean shouldDrawDot() {
// Always render the dot if it's animating, since it could be animating out. Otherwise, show
@@ -276,4 +285,33 @@
}
}).start();
}
+
+ void showBadge() {
+ Drawable badge = mBubble.getAppBadge();
+ if (badge == null) {
+ setImageBitmap(mBubble.getBubbleIcon());
+ return;
+ }
+ Canvas bubbleCanvas = new Canvas();
+ Bitmap noBadgeBubble = mBubble.getBubbleIcon();
+ Bitmap bubble = noBadgeBubble.copy(noBadgeBubble.getConfig(), /* isMutable */ true);
+
+ bubbleCanvas.setDrawFilter(new PaintFlagsDrawFilter(DITHER_FLAG, FILTER_BITMAP_FLAG));
+ bubbleCanvas.setBitmap(bubble);
+
+ final int badgeSize = (int) (ICON_BADGE_SCALE * mBubbleSize);
+ if (mOnLeft) {
+ badge.setBounds(0, mBubbleSize - badgeSize, badgeSize, mBubbleSize);
+ } else {
+ badge.setBounds(mBubbleSize - badgeSize, mBubbleSize - badgeSize,
+ mBubbleSize, mBubbleSize);
+ }
+ badge.draw(bubbleCanvas);
+ bubbleCanvas.setBitmap(null);
+ setImageBitmap(bubble);
+ }
+
+ void hideBadge() {
+ setImageBitmap(mBubble.getBubbleIcon());
+ }
}
diff --git a/packages/SystemUI/src/com/android/systemui/bubbles/Bubble.java b/packages/SystemUI/src/com/android/systemui/bubbles/Bubble.java
index 62bc425..e6c1bc3 100644
--- a/packages/SystemUI/src/com/android/systemui/bubbles/Bubble.java
+++ b/packages/SystemUI/src/com/android/systemui/bubbles/Bubble.java
@@ -41,7 +41,6 @@
import com.android.internal.annotations.VisibleForTesting;
import com.android.internal.logging.InstanceId;
-import com.android.systemui.shared.system.SysUiStatsLog;
import com.android.systemui.statusbar.notification.collection.NotificationEntry;
import java.io.FileDescriptor;
@@ -92,8 +91,9 @@
}
private FlyoutMessage mFlyoutMessage;
- private Drawable mBadgedAppIcon;
- private Bitmap mBadgedImage;
+ private Drawable mBadgeDrawable;
+ // Bitmap with no badge, no dot
+ private Bitmap mBubbleBitmap;
private int mDotColor;
private Path mDotPath;
private int mFlags;
@@ -199,12 +199,13 @@
}
@Override
- public Bitmap getBadgedImage() {
- return mBadgedImage;
+ public Bitmap getBubbleIcon() {
+ return mBubbleBitmap;
}
- public Drawable getBadgedAppIcon() {
- return mBadgedAppIcon;
+ @Override
+ public Drawable getAppBadge() {
+ return mBadgeDrawable;
}
@Override
@@ -340,8 +341,9 @@
mAppName = info.appName;
mFlyoutMessage = info.flyoutMessage;
- mBadgedAppIcon = info.badgedAppIcon;
- mBadgedImage = info.badgedBubbleImage;
+ mBadgeDrawable = info.badgeDrawable;
+ mBubbleBitmap = info.bubbleBitmap;
+
mDotColor = info.dotColor;
mDotPath = info.dotPath;
diff --git a/packages/SystemUI/src/com/android/systemui/bubbles/BubbleIconFactory.java b/packages/SystemUI/src/com/android/systemui/bubbles/BubbleIconFactory.java
index d017bc0..371e849 100644
--- a/packages/SystemUI/src/com/android/systemui/bubbles/BubbleIconFactory.java
+++ b/packages/SystemUI/src/com/android/systemui/bubbles/BubbleIconFactory.java
@@ -156,17 +156,4 @@
canvas.setBitmap(null);
return bitmap;
}
-
- /**
- * Returns a {@link BitmapInfo} for the entire bubble icon including the badge.
- */
- BitmapInfo getBubbleBitmap(Drawable bubble, BitmapInfo badge) {
- BitmapInfo bubbleIconInfo = createBadgedIconBitmap(bubble,
- null /* user */,
- true /* shrinkNonAdaptiveIcons */);
-
- badgeWithDrawable(bubbleIconInfo.icon,
- new BitmapDrawable(mContext.getResources(), badge.icon));
- return bubbleIconInfo;
- }
}
diff --git a/packages/SystemUI/src/com/android/systemui/bubbles/BubbleOverflow.kt b/packages/SystemUI/src/com/android/systemui/bubbles/BubbleOverflow.kt
index 155b71b..6d3c2a6 100644
--- a/packages/SystemUI/src/com/android/systemui/bubbles/BubbleOverflow.kt
+++ b/packages/SystemUI/src/com/android/systemui/bubbles/BubbleOverflow.kt
@@ -23,6 +23,7 @@
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.util.PathParser
import android.util.TypedValue
@@ -36,8 +37,9 @@
private val stack: BubbleStackView
) : BubbleViewProvider {
- private var bitmap: Bitmap? = null
- private var dotPath: Path? = null
+ private lateinit var bitmap : Bitmap
+ private lateinit var dotPath : Path
+
private var bitmapSize = 0
private var iconBitmapSize = 0
private var dotColor = 0
@@ -80,40 +82,41 @@
expandedView.updateDimensions()
}
- fun updateBtnTheme() {
+ private 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)
+ overflowBtn.drawable?.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
+ // Update bitmap
+ 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))
+
+ val fg = InsetDrawable(overflowBtn.drawable,
+ bitmapSize - iconBitmapSize /* inset */)
+ bitmap = iconFactory.createBadgedIconBitmap(AdaptiveIconDrawable(bg, fg),
+ null /* user */, true /* shrinkNonAdaptiveIcons */).icon
+
+ // Update dot path
dotPath = PathParser.createPathFromPathData(
- res.getString(com.android.internal.R.string.config_icon_mask))
+ res.getString(com.android.internal.R.string.config_icon_mask))
val scale = iconFactory.normalizer.getScale(overflowBtn.getDrawable(),
- null /* outBounds */, null /* path */, null /* outMaskShape */)
+ 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)
+ radius /* pivot y */)
+ dotPath.transform(matrix)
+
+ // Attach BubbleOverflow to BadgedImageView
overflowBtn.setRenderedBubble(this)
}
@@ -129,7 +132,11 @@
return dotColor
}
- override fun getBadgedImage(): Bitmap? {
+ override fun getAppBadge(): Drawable? {
+ return null
+ }
+
+ override fun getBubbleIcon(): Bitmap {
return bitmap
}
diff --git a/packages/SystemUI/src/com/android/systemui/bubbles/BubbleStackView.java b/packages/SystemUI/src/com/android/systemui/bubbles/BubbleStackView.java
index c1b6882..55f9631 100644
--- a/packages/SystemUI/src/com/android/systemui/bubbles/BubbleStackView.java
+++ b/packages/SystemUI/src/com/android/systemui/bubbles/BubbleStackView.java
@@ -16,13 +16,6 @@
package com.android.systemui.bubbles;
-import static android.view.ViewGroup.LayoutParams.MATCH_PARENT;
-import static android.view.ViewGroup.LayoutParams.WRAP_CONTENT;
-
-import static com.android.systemui.bubbles.BubbleDebugConfig.DEBUG_BUBBLE_STACK_VIEW;
-import static com.android.systemui.bubbles.BubbleDebugConfig.TAG_BUBBLES;
-import static com.android.systemui.bubbles.BubbleDebugConfig.TAG_WITH_CLASS_NAME;
-
import android.animation.Animator;
import android.animation.AnimatorListenerAdapter;
import android.animation.ValueAnimator;
@@ -99,6 +92,12 @@
import java.util.List;
import java.util.function.Consumer;
+import static android.view.ViewGroup.LayoutParams.MATCH_PARENT;
+import static android.view.ViewGroup.LayoutParams.WRAP_CONTENT;
+import static com.android.systemui.bubbles.BubbleDebugConfig.DEBUG_BUBBLE_STACK_VIEW;
+import static com.android.systemui.bubbles.BubbleDebugConfig.TAG_BUBBLES;
+import static com.android.systemui.bubbles.BubbleDebugConfig.TAG_WITH_CLASS_NAME;
+
/**
* Renders bubbles in a stack and handles animating expanded and collapsed states.
*/
@@ -658,13 +657,10 @@
mStackOnLeftOrWillBe =
mStackAnimationController.flingStackThenSpringToEdge(
viewInitialX + dx, velX, velY) <= 0;
-
- updateBubbleZOrdersAndDotPosition(true /* animate */);
-
+ updateBubbleIcons();
logBubbleEvent(null /* no bubble associated with bubble stack move */,
SysUiStatsLog.BUBBLE_UICHANGED__ACTION__STACK_MOVED);
}
-
mDismissView.hide();
}
@@ -1468,8 +1464,7 @@
// Set the dot position to the opposite of the side the stack is resting on, since the stack
// resting slightly off-screen would result in the dot also being off-screen.
- bubble.getIconView().setDotPositionOnLeft(
- !mStackOnLeftOrWillBe /* onLeft */, false /* animate */);
+ bubble.getIconView().setDotBadgeOnLeft(!mStackOnLeftOrWillBe /* onLeft */);
bubble.getIconView().setOnClickListener(mBubbleClickListener);
bubble.getIconView().setOnTouchListener(mBubbleTouchListener);
@@ -1520,7 +1515,7 @@
Bubble bubble = bubbles.get(i);
mBubbleContainer.reorderView(bubble.getIconView(), i);
}
- updateBubbleZOrdersAndDotPosition(false /* animate */);
+ updateBubbleIcons();
updatePointerPosition();
}
@@ -2363,7 +2358,7 @@
// name and icon.
if (show && mBubbleData.hasBubbleInStackWithKey(mExpandedBubble.getKey())) {
final Bubble bubble = mBubbleData.getBubbleInStackWithKey(mExpandedBubble.getKey());
- mManageSettingsIcon.setImageDrawable(bubble.getBadgedAppIcon());
+ mManageSettingsIcon.setImageDrawable(bubble.getAppBadge());
mManageSettingsText.setText(getResources().getString(
R.string.bubbles_app_settings, bubble.getAppName()));
}
@@ -2551,28 +2546,31 @@
}
mStackOnLeftOrWillBe = mStackAnimationController.isStackOnLeftSide();
- updateBubbleZOrdersAndDotPosition(false);
+ updateBubbleIcons();
}
- /** Sets the appropriate Z-order and dot position for each bubble in the stack. */
- private void updateBubbleZOrdersAndDotPosition(boolean animate) {
+ /**
+ * Sets the appropriate Z-order, badge, and dot position for each bubble in the stack.
+ * Animate dot and badge changes.
+ */
+ private void updateBubbleIcons() {
int bubbleCount = getBubbleCount();
for (int i = 0; i < bubbleCount; i++) {
BadgedImageView bv = (BadgedImageView) mBubbleContainer.getChildAt(i);
bv.setZ((mMaxBubbles * mBubbleElevation) - i);
- // If the dot is on the left, and so is the stack, we need to change the dot position.
- if (bv.getDotPositionOnLeft() == mStackOnLeftOrWillBe) {
- bv.setDotPositionOnLeft(!mStackOnLeftOrWillBe, animate);
- }
-
- if (!mIsExpanded && i > 0) {
- // If we're collapsed and this bubble is behind other bubbles, suppress its dot.
- bv.addDotSuppressionFlag(
- BadgedImageView.SuppressionFlag.BEHIND_STACK);
- } else {
+ if (mIsExpanded) {
bv.removeDotSuppressionFlag(
BadgedImageView.SuppressionFlag.BEHIND_STACK);
+ bv.animateDotBadgePositions(false /* onLeft */);
+ } else if (i == 0) {
+ bv.removeDotSuppressionFlag(
+ BadgedImageView.SuppressionFlag.BEHIND_STACK);
+ bv.animateDotBadgePositions(!mStackOnLeftOrWillBe);
+ } else {
+ bv.addDotSuppressionFlag(
+ BadgedImageView.SuppressionFlag.BEHIND_STACK);
+ bv.hideBadge();
}
}
}
diff --git a/packages/SystemUI/src/com/android/systemui/bubbles/BubbleViewInfoTask.java b/packages/SystemUI/src/com/android/systemui/bubbles/BubbleViewInfoTask.java
index 5749169..28757fa 100644
--- a/packages/SystemUI/src/com/android/systemui/bubbles/BubbleViewInfoTask.java
+++ b/packages/SystemUI/src/com/android/systemui/bubbles/BubbleViewInfoTask.java
@@ -120,8 +120,8 @@
BubbleExpandedView expandedView;
ShortcutInfo shortcutInfo;
String appName;
- Bitmap badgedBubbleImage;
- Drawable badgedAppIcon;
+ Bitmap bubbleBitmap;
+ Drawable badgeDrawable;
int dotColor;
Path dotPath;
Bubble.FlyoutMessage flyoutMessage;
@@ -179,9 +179,10 @@
BitmapInfo badgeBitmapInfo = iconFactory.getBadgeBitmap(badgedIcon,
b.isImportantConversation());
- info.badgedAppIcon = badgedIcon;
- info.badgedBubbleImage = iconFactory.getBubbleBitmap(bubbleDrawable,
- badgeBitmapInfo).icon;
+ info.badgeDrawable = badgedIcon;
+ info.bubbleBitmap = iconFactory.createBadgedIconBitmap(bubbleDrawable,
+ null /* user */,
+ true /* shrinkNonAdaptiveIcons */).icon;
// Dot color & placement
Path iconPath = PathParser.createPathFromPathData(
diff --git a/packages/SystemUI/src/com/android/systemui/bubbles/BubbleViewProvider.java b/packages/SystemUI/src/com/android/systemui/bubbles/BubbleViewProvider.java
index f1a01be..916ad18 100644
--- a/packages/SystemUI/src/com/android/systemui/bubbles/BubbleViewProvider.java
+++ b/packages/SystemUI/src/com/android/systemui/bubbles/BubbleViewProvider.java
@@ -18,6 +18,7 @@
import android.graphics.Bitmap;
import android.graphics.Path;
+import android.graphics.drawable.Drawable;
import android.view.View;
import androidx.annotation.Nullable;
@@ -34,12 +35,17 @@
String getKey();
- Bitmap getBadgedImage();
+ /** Bubble icon bitmap with no badge and no dot. */
+ Bitmap getBubbleIcon();
+
+ /** App badge drawable to draw above bubble icon. */
+ @Nullable Drawable getAppBadge();
+
+ /** Path of normalized bubble icon to draw dot on. */
+ Path getDotPath();
int getDotColor();
- Path getDotPath();
-
boolean showDot();
int getDisplayId();