Move BubbleView methods into BadgedImageView; remove BubbleView
* Removed BubbleView
* Moved the icon / badge logic into BubbleIconFactory
* Moved everything else into BadgedImageView
* Introduced dot states in BadgedImageView which hopefully makes that
easier to read
* Altered Bubble#setShowDot to also animate the dot visibility
* Altered BubbleFlyoutView to be able to animate the dot away for
DND scenario
Test: atest SystemUITests (existing bubble tests pass)
Bug: 144719337
Bug: 145245204 (fixes the bit where tapping on bubble in expanded state
doesn't animate the dot away but jump cuts instead)
Change-Id: I8cebf3e7f93db1920ede95eb6f7392560270767f
diff --git a/packages/SystemUI/src/com/android/systemui/bubbles/BadgedImageView.java b/packages/SystemUI/src/com/android/systemui/bubbles/BadgedImageView.java
index c0053d1..a6a3ce0 100644
--- a/packages/SystemUI/src/com/android/systemui/bubbles/BadgedImageView.java
+++ b/packages/SystemUI/src/com/android/systemui/bubbles/BadgedImageView.java
@@ -15,35 +15,61 @@
*/
package com.android.systemui.bubbles;
+import android.annotation.Nullable;
+import android.app.Notification;
import android.content.Context;
-import android.content.res.TypedArray;
+import android.content.pm.LauncherApps;
import android.graphics.Canvas;
+import android.graphics.Color;
+import android.graphics.Matrix;
import android.graphics.Path;
import android.graphics.Rect;
+import android.graphics.drawable.Drawable;
+import android.graphics.drawable.Icon;
import android.util.AttributeSet;
+import android.util.PathParser;
import android.widget.ImageView;
import com.android.internal.graphics.ColorUtils;
+import com.android.launcher3.icons.BitmapInfo;
import com.android.launcher3.icons.DotRenderer;
+import com.android.systemui.Interpolators;
import com.android.systemui.R;
/**
- * View that circle crops its contents and supports displaying a coloured dot on a top corner.
+ * View that displays an adaptive icon with an app-badge and a dot.
+ *
+ * Dot = a small colored circle that indicates whether this bubble has an unread update.
+ * Badge = the icon associated with the app that created this bubble, this will show work profile
+ * badge if appropriate.
*/
public class BadgedImageView extends ImageView {
- private Rect mTempBounds = new Rect();
+ /** Same value as Launcher3 dot code */
+ private static final float WHITE_SCRIM_ALPHA = 0.54f;
+ /** Same as value in Launcher3 IconShape */
+ private static final int DEFAULT_PATH_SIZE = 100;
+ static final int DOT_STATE_DEFAULT = 0;
+ static final int DOT_STATE_SUPPRESSED_FOR_FLYOUT = 1;
+ static final int DOT_STATE_ANIMATING = 2;
+
+ // Flyout gets shown before the dot
+ private int mCurrentDotState = DOT_STATE_SUPPRESSED_FOR_FLYOUT;
+
+ private Bubble mBubble;
+ private BubbleIconFactory mBubbleIconFactory;
+
+ private int mIconBitmapSize;
private DotRenderer mDotRenderer;
private DotRenderer.DrawParams mDrawParams;
- private int mIconBitmapSize;
- private int mDotColor;
- private float mDotScale = 0f;
- private boolean mShowDot;
private boolean mOnLeft;
- /** Same as value in Launcher3 IconShape */
- static final int DEFAULT_PATH_SIZE = 100;
+ private int mDotColor;
+ private float mDotScale = 0f;
+ private boolean mDotDrawn;
+
+ private Rect mTempBounds = new Rect();
public BadgedImageView(Context context) {
this(context, null);
@@ -63,17 +89,19 @@
mIconBitmapSize = getResources().getDimensionPixelSize(R.dimen.bubble_icon_bitmap_size);
mDrawParams = new DotRenderer.DrawParams();
- TypedArray ta = context.obtainStyledAttributes(
- new int[]{android.R.attr.colorBackgroundFloating});
- ta.recycle();
+ Path iconPath = PathParser.createPathFromPathData(
+ getResources().getString(com.android.internal.R.string.config_icon_mask));
+ mDotRenderer = new DotRenderer(mIconBitmapSize, iconPath, DEFAULT_PATH_SIZE);
}
@Override
public void onDraw(Canvas canvas) {
super.onDraw(canvas);
- if (!mShowDot) {
+ if (isDotHidden()) {
+ mDotDrawn = false;
return;
}
+ mDotDrawn = mDotScale > 0.1f;
getDrawingRect(mTempBounds);
mDrawParams.color = mDotColor;
@@ -81,16 +109,29 @@
mDrawParams.leftAlign = mOnLeft;
mDrawParams.scale = mDotScale;
- if (mDotRenderer == null) {
- Path circlePath = new Path();
- float radius = DEFAULT_PATH_SIZE * 0.5f;
- circlePath.addCircle(radius /* x */, radius /* y */, radius, Path.Direction.CW);
- mDotRenderer = new DotRenderer(mIconBitmapSize, circlePath, DEFAULT_PATH_SIZE);
- }
mDotRenderer.draw(canvas, mDrawParams);
}
/**
+ * Sets the dot state, does not animate changes.
+ */
+ void setDotState(int state) {
+ mCurrentDotState = state;
+ if (state == DOT_STATE_SUPPRESSED_FOR_FLYOUT || state == DOT_STATE_DEFAULT) {
+ mDotScale = mBubble.showDot() ? 1f : 0f;
+ invalidate();
+ }
+ }
+
+ /**
+ * Whether the dot should be hidden based on current dot state.
+ */
+ private boolean isDotHidden() {
+ return (mCurrentDotState == DOT_STATE_DEFAULT && !mBubble.showDot())
+ || mCurrentDotState == DOT_STATE_SUPPRESSED_FOR_FLYOUT;
+ }
+
+ /**
* Set whether the dot should appear on left or right side of the view.
*/
void setDotOnLeft(boolean onLeft) {
@@ -98,29 +139,10 @@
invalidate();
}
- boolean getDotOnLeft() {
- return mOnLeft;
- }
-
- /**
- * Set whether the dot should show or not.
- */
- void setShowDot(boolean showDot) {
- mShowDot = showDot;
- invalidate();
- }
-
- /**
- * @return whether the dot is being displayed.
- */
- boolean isShowingDot() {
- return mShowDot;
- }
-
/**
* The colour to use for the dot.
*/
- public void setDotColor(int color) {
+ void setDotColor(int color) {
mDotColor = ColorUtils.setAlphaComponent(color, 255 /* alpha */);
invalidate();
}
@@ -128,7 +150,7 @@
/**
* @param iconPath The new icon path to use when calculating dot position.
*/
- public void drawDot(Path iconPath) {
+ void drawDot(Path iconPath) {
mDotRenderer = new DotRenderer(mIconBitmapSize, iconPath, DEFAULT_PATH_SIZE);
invalidate();
}
@@ -142,6 +164,13 @@
}
/**
+ * Whether decorations (badges or dots) are on the left.
+ */
+ boolean getDotOnLeft() {
+ return mOnLeft;
+ }
+
+ /**
* Return dot position relative to bubble view container bounds.
*/
float[] getDotCenter() {
@@ -149,11 +178,146 @@
if (mOnLeft) {
dotPosition = mDotRenderer.getLeftDotPosition();
} else {
- dotPosition = mDotRenderer.getRightDotPosition();
+ dotPosition = mDotRenderer.getRightDotPosition();
}
getDrawingRect(mTempBounds);
float dotCenterX = mTempBounds.width() * dotPosition[0];
float dotCenterY = mTempBounds.height() * dotPosition[1];
return new float[]{dotCenterX, dotCenterY};
}
+
+ /**
+ * Populates this view with a bubble.
+ * <p>
+ * This should only be called when a new bubble is being set on the view, updates to the
+ * current bubble should use {@link #update(Bubble)}.
+ *
+ * @param bubble the bubble to display in this view.
+ */
+ public void setBubble(Bubble bubble) {
+ mBubble = bubble;
+ }
+
+ /**
+ * @param factory Factory for creating normalized bubble icons.
+ */
+ public void setBubbleIconFactory(BubbleIconFactory factory) {
+ mBubbleIconFactory = factory;
+ }
+
+ /**
+ * The key for the {@link Bubble} associated with this view, if one exists.
+ */
+ @Nullable
+ public String getKey() {
+ return (mBubble != null) ? mBubble.getKey() : null;
+ }
+
+ /**
+ * Updates the UI based on the bubble, updates badge and animates messages as needed.
+ */
+ public void update(Bubble bubble) {
+ mBubble = bubble;
+ setDotState(DOT_STATE_SUPPRESSED_FOR_FLYOUT);
+ updateViews();
+ }
+
+ int getDotColor() {
+ return mDotColor;
+ }
+
+ /** Sets the position of the 'new' dot, animating it out and back in if requested. */
+ void setDotPosition(boolean onLeft, boolean animate) {
+ if (animate && onLeft != getDotOnLeft() && !isDotHidden()) {
+ animateDot(false /* showDot */, () -> {
+ setDotOnLeft(onLeft);
+ animateDot(true /* showDot */, null);
+ });
+ } else {
+ setDotOnLeft(onLeft);
+ }
+ }
+
+ boolean getDotPositionOnLeft() {
+ return getDotOnLeft();
+ }
+
+ /** Changes the dot's visibility to match the bubble view's state. */
+ void animateDot() {
+ if (mCurrentDotState == DOT_STATE_DEFAULT) {
+ animateDot(mBubble.showDot(), null);
+ }
+ }
+
+ /**
+ * Animates the dot to show or hide.
+ */
+ private void animateDot(boolean showDot, Runnable after) {
+ if (mDotDrawn == showDot) {
+ // State is consistent, do nothing.
+ return;
+ }
+
+ setDotState(DOT_STATE_ANIMATING);
+
+ // Do NOT wait until after animation ends to setShowDot
+ // to avoid overriding more recent showDot states.
+ clearAnimation();
+ animate().setDuration(200)
+ .setInterpolator(Interpolators.FAST_OUT_SLOW_IN)
+ .setUpdateListener((valueAnimator) -> {
+ float fraction = valueAnimator.getAnimatedFraction();
+ fraction = showDot ? fraction : 1f - fraction;
+ setDotScale(fraction);
+ }).withEndAction(() -> {
+ setDotScale(showDot ? 1f : 0f);
+ setDotState(DOT_STATE_DEFAULT);
+ if (after != null) {
+ after.run();
+ }
+ }).start();
+ }
+
+ void updateViews() {
+ if (mBubble == null || mBubbleIconFactory == null) {
+ return;
+ }
+
+ Drawable bubbleDrawable = getBubbleDrawable(mContext);
+ BitmapInfo badgeBitmapInfo = mBubbleIconFactory.getBadgedBitmap(mBubble);
+ BitmapInfo bubbleBitmapInfo = mBubbleIconFactory.getBubbleBitmap(bubbleDrawable,
+ badgeBitmapInfo);
+ setImageBitmap(bubbleBitmapInfo.icon);
+
+ // Update badge.
+ mDotColor = ColorUtils.blendARGB(badgeBitmapInfo.color, Color.WHITE, WHITE_SCRIM_ALPHA);
+ setDotColor(mDotColor);
+
+ // Update dot.
+ Path iconPath = PathParser.createPathFromPathData(
+ getResources().getString(com.android.internal.R.string.config_icon_mask));
+ Matrix matrix = new Matrix();
+ float scale = mBubbleIconFactory.getNormalizer().getScale(bubbleDrawable,
+ null /* outBounds */, null /* path */, null /* outMaskShape */);
+ float radius = BadgedImageView.DEFAULT_PATH_SIZE / 2f;
+ matrix.setScale(scale /* x scale */, scale /* y scale */, radius /* pivot x */,
+ radius /* pivot y */);
+ iconPath.transform(matrix);
+ drawDot(iconPath);
+
+ animateDot();
+ }
+
+ Drawable getBubbleDrawable(Context context) {
+ if (mBubble.getShortcutInfo() != null && mBubble.usingShortcutInfo()) {
+ LauncherApps launcherApps =
+ (LauncherApps) getContext().getSystemService(Context.LAUNCHER_APPS_SERVICE);
+ int density = getContext().getResources().getConfiguration().densityDpi;
+ return launcherApps.getShortcutIconDrawable(mBubble.getShortcutInfo(), density);
+ } else {
+ Notification.BubbleMetadata metadata = mBubble.getEntry().getBubbleMetadata();
+ Icon ic = metadata.getIcon();
+ return ic.loadDrawable(context);
+ }
+ }
}
diff --git a/packages/SystemUI/src/com/android/systemui/bubbles/Bubble.java b/packages/SystemUI/src/com/android/systemui/bubbles/Bubble.java
index 24ee9695..ef2868b 100644
--- a/packages/SystemUI/src/com/android/systemui/bubbles/Bubble.java
+++ b/packages/SystemUI/src/com/android/systemui/bubbles/Bubble.java
@@ -64,8 +64,9 @@
private ShortcutInfo mShortcutInfo;
private boolean mInflated;
- private BubbleView mIconView;
+ private BadgedImageView mIconView;
private BubbleExpandedView mExpandedView;
+ private BubbleIconFactory mBubbleIconFactory;
private long mLastUpdated;
private long mLastAccessed;
@@ -146,7 +147,7 @@
return mAppName;
}
- public Drawable getUserBadgedAppIcon() {
+ Drawable getUserBadgedAppIcon() {
return mUserBadgedAppIcon;
}
@@ -165,17 +166,15 @@
return BubbleExperimentConfig.isShortcutIntent(getBubbleIntent());
}
+ void setBubbleIconFactory(BubbleIconFactory factory) {
+ mBubbleIconFactory = factory;
+ }
+
boolean isInflated() {
return mInflated;
}
- void updateDotVisibility() {
- if (mIconView != null) {
- mIconView.updateDotVisibility(true /* animate */);
- }
- }
-
- BubbleView getIconView() {
+ BadgedImageView getIconView() {
return mIconView;
}
@@ -193,8 +192,9 @@
if (mInflated) {
return;
}
- mIconView = (BubbleView) inflater.inflate(
+ mIconView = (BadgedImageView) inflater.inflate(
R.layout.bubble_view, stackView, false /* attachToRoot */);
+ mIconView.setBubbleIconFactory(mBubbleIconFactory);
mIconView.setBubble(this);
mExpandedView = (BubbleExpandedView) inflater.inflate(
@@ -260,15 +260,15 @@
*/
void markAsAccessedAt(long lastAccessedMillis) {
mLastAccessed = lastAccessedMillis;
- setShowInShadeWhenBubble(false);
- setShowBubbleDot(false);
+ setShowInShade(false);
+ setShowDot(false /* show */, true /* animate */);
}
/**
* Whether this notification should be shown in the shade when it is also displayed as a
* bubble.
*/
- boolean showInShadeWhenBubble() {
+ boolean showInShade() {
return !mEntry.isRowDismissed() && !shouldSuppressNotification()
&& (!mEntry.isClearable() || mShowInShadeWhenBubble);
}
@@ -277,28 +277,33 @@
* Sets whether this notification should be shown in the shade when it is also displayed as a
* bubble.
*/
- void setShowInShadeWhenBubble(boolean showInShade) {
+ void setShowInShade(boolean showInShade) {
mShowInShadeWhenBubble = showInShade;
}
/**
* Sets whether the bubble for this notification should show a dot indicating updated content.
*/
- void setShowBubbleDot(boolean showDot) {
+ void setShowDot(boolean showDot, boolean animate) {
mShowBubbleUpdateDot = showDot;
+ if (animate && mIconView != null) {
+ mIconView.animateDot();
+ } else if (mIconView != null) {
+ mIconView.invalidate();
+ }
}
/**
* Whether the bubble for this notification should show a dot indicating updated content.
*/
- boolean showBubbleDot() {
+ boolean showDot() {
return mShowBubbleUpdateDot && !mEntry.shouldSuppressNotificationDot();
}
/**
* Whether the flyout for the bubble should be shown.
*/
- boolean showFlyoutForBubble() {
+ boolean showFlyout() {
return !mSuppressFlyout && !mEntry.shouldSuppressPeek()
&& !mEntry.shouldSuppressNotificationList();
}
@@ -470,9 +475,9 @@
public void dump(
@NonNull FileDescriptor fd, @NonNull PrintWriter pw, @NonNull String[] args) {
pw.print("key: "); pw.println(mKey);
- pw.print(" showInShade: "); pw.println(showInShadeWhenBubble());
- pw.print(" showDot: "); pw.println(showBubbleDot());
- pw.print(" showFlyout: "); pw.println(showFlyoutForBubble());
+ pw.print(" showInShade: "); pw.println(showInShade());
+ pw.print(" showDot: "); pw.println(showDot());
+ pw.print(" showFlyout: "); pw.println(showFlyout());
pw.print(" desiredHeight: "); pw.println(getDesiredHeightString());
pw.print(" suppressNotif: "); pw.println(shouldSuppressNotification());
pw.print(" autoExpand: "); pw.println(shouldAutoExpand());
diff --git a/packages/SystemUI/src/com/android/systemui/bubbles/BubbleController.java b/packages/SystemUI/src/com/android/systemui/bubbles/BubbleController.java
index ed21e14..1025321 100644
--- a/packages/SystemUI/src/com/android/systemui/bubbles/BubbleController.java
+++ b/packages/SystemUI/src/com/android/systemui/bubbles/BubbleController.java
@@ -251,15 +251,15 @@
mZenModeController.addCallback(new ZenModeController.Callback() {
@Override
public void onZenChanged(int zen) {
- if (mStackView != null) {
- mStackView.updateDots();
+ for (Bubble b : mBubbleData.getBubbles()) {
+ b.setShowDot(b.showInShade(), true /* animate */);
}
}
@Override
public void onConfigChanged(ZenModeConfig config) {
- if (mStackView != null) {
- mStackView.updateDots();
+ for (Bubble b : mBubbleData.getBubbles()) {
+ b.setShowDot(b.showInShade(), true /* animate */);
}
}
});
@@ -465,7 +465,7 @@
*/
public boolean isBubbleNotificationSuppressedFromShade(String key) {
boolean isBubbleAndSuppressed = mBubbleData.hasBubbleWithKey(key)
- && !mBubbleData.getBubbleWithKey(key).showInShadeWhenBubble();
+ && !mBubbleData.getBubbleWithKey(key).showInShade();
NotificationEntry entry = mNotificationEntryManager.getActiveNotificationUnfiltered(key);
String groupKey = entry != null ? entry.getSbn().getGroupKey() : null;
boolean isSuppressedSummary = mBubbleData.isSummarySuppressed(groupKey);
@@ -630,11 +630,8 @@
Bubble bubble = mBubbleData.getBubbleWithKey(key);
boolean bubbleExtended = entry != null && entry.isBubble() && userRemovedNotif;
if (bubbleExtended) {
- bubble.setShowInShadeWhenBubble(false);
- bubble.setShowBubbleDot(false);
- if (mStackView != null) {
- mStackView.updateDotVisibility(entry.getKey());
- }
+ bubble.setShowInShade(false);
+ bubble.setShowDot(false /* show */, true /* animate */);
mNotificationEntryManager.updateNotifications(
"BubbleController.onNotificationRemoveRequested");
return true;
@@ -660,11 +657,8 @@
// As far as group manager is concerned, once a child is no longer shown
// in the shade, it is essentially removed.
mNotificationGroupManager.onEntryRemoved(bubbleChild.getEntry());
- bubbleChild.setShowInShadeWhenBubble(false);
- bubbleChild.setShowBubbleDot(false);
- if (mStackView != null) {
- mStackView.updateDotVisibility(bubbleChild.getKey());
- }
+ bubbleChild.setShowInShade(false);
+ bubbleChild.setShowDot(false /* show */, true /* animate */);
}
// And since all children are removed, remove the summary.
mNotificationGroupManager.onEntryRemoved(summary);
@@ -765,7 +759,7 @@
// If the bubble is removed for user switching, leave the notification in place.
if (reason != DISMISS_USER_CHANGED) {
if (!mBubbleData.hasBubbleWithKey(bubble.getKey())
- && !bubble.showInShadeWhenBubble()) {
+ && !bubble.showInShade()) {
// The bubble is gone & the notification is gone, time to actually remove it
mNotificationEntryManager.performRemoveNotification(
bubble.getEntry().getSbn(), UNDEFINED_DISMISS_REASON);
diff --git a/packages/SystemUI/src/com/android/systemui/bubbles/BubbleData.java b/packages/SystemUI/src/com/android/systemui/bubbles/BubbleData.java
index 2ca993b..034bff3 100644
--- a/packages/SystemUI/src/com/android/systemui/bubbles/BubbleData.java
+++ b/packages/SystemUI/src/com/android/systemui/bubbles/BubbleData.java
@@ -210,8 +210,8 @@
setSelectedBubbleInternal(bubble);
}
boolean isBubbleExpandedAndSelected = mExpanded && mSelectedBubble == bubble;
- bubble.setShowInShadeWhenBubble(!isBubbleExpandedAndSelected && showInShade);
- bubble.setShowBubbleDot(!isBubbleExpandedAndSelected);
+ bubble.setShowInShade(!isBubbleExpandedAndSelected && showInShade);
+ bubble.setShowDot(!isBubbleExpandedAndSelected /* show */, true /* animate */);
dispatchPendingChanges();
}
@@ -303,9 +303,8 @@
if (notif.getRanking().visuallyInterruptive()) {
return true;
}
- final boolean suppressedFromShade = hasBubbleWithKey(notif.getKey())
- && !getBubbleWithKey(notif.getKey()).showInShadeWhenBubble();
- return suppressedFromShade;
+ return hasBubbleWithKey(notif.getKey())
+ && !getBubbleWithKey(notif.getKey()).showInShade();
}
private void doAdd(Bubble bubble) {
diff --git a/packages/SystemUI/src/com/android/systemui/bubbles/BubbleExpandedView.java b/packages/SystemUI/src/com/android/systemui/bubbles/BubbleExpandedView.java
index 512b38e..a69ff00 100644
--- a/packages/SystemUI/src/com/android/systemui/bubbles/BubbleExpandedView.java
+++ b/packages/SystemUI/src/com/android/systemui/bubbles/BubbleExpandedView.java
@@ -624,7 +624,7 @@
action,
mStackView.getNormalizedXPosition(),
mStackView.getNormalizedYPosition(),
- bubble.showInShadeWhenBubble(),
+ bubble.showInShade(),
bubble.isOngoing(),
false /* isAppForeground (unused) */);
}
diff --git a/packages/SystemUI/src/com/android/systemui/bubbles/BubbleFlyoutView.java b/packages/SystemUI/src/com/android/systemui/bubbles/BubbleFlyoutView.java
index 58f3f22..78e98eb 100644
--- a/packages/SystemUI/src/com/android/systemui/bubbles/BubbleFlyoutView.java
+++ b/packages/SystemUI/src/com/android/systemui/bubbles/BubbleFlyoutView.java
@@ -206,7 +206,7 @@
void setupFlyoutStartingAsDot(
CharSequence updateMessage, PointF stackPos, float parentWidth,
boolean arrowPointingLeft, int dotColor, @Nullable Runnable onLayoutComplete,
- @Nullable Runnable onHide, float[] dotCenter) {
+ @Nullable Runnable onHide, float[] dotCenter, boolean hideDot) {
mArrowPointingLeft = arrowPointingLeft;
mDotColor = dotColor;
mOnHide = onHide;
@@ -242,12 +242,14 @@
// Calculate the difference in size between the flyout and the 'dot' so that we can
// transform into the dot later.
- mFlyoutToDotWidthDelta = getWidth() - mNewDotSize;
- mFlyoutToDotHeightDelta = getHeight() - mNewDotSize;
+ final float newDotSize = hideDot ? 0f : mNewDotSize;
+ mFlyoutToDotWidthDelta = getWidth() - newDotSize;
+ mFlyoutToDotHeightDelta = getHeight() - newDotSize;
// Calculate the translation values needed to be in the correct 'new dot' position.
- final float dotPositionX = stackPos.x + mDotCenter[0] - (mOriginalDotSize / 2f);
- final float dotPositionY = stackPos.y + mDotCenter[1] - (mOriginalDotSize / 2f);
+ final float adjustmentForScaleAway = hideDot ? 0f : (mOriginalDotSize / 2f);
+ final float dotPositionX = stackPos.x + mDotCenter[0] - adjustmentForScaleAway;
+ final float dotPositionY = stackPos.y + mDotCenter[1] - adjustmentForScaleAway;
final float distanceFromFlyoutLeftToDotCenterX = mRestingTranslationX - dotPositionX;
final float distanceFromLayoutTopToDotCenterY = restingTranslationY - dotPositionY;
@@ -319,8 +321,7 @@
// percentage.
final float width = getWidth() - (mFlyoutToDotWidthDelta * mPercentTransitionedToDot);
final float height = getHeight() - (mFlyoutToDotHeightDelta * mPercentTransitionedToDot);
- final float interpolatedRadius = mNewDotRadius * mPercentTransitionedToDot
- + mCornerRadius * (1 - mPercentTransitionedToDot);
+ final float interpolatedRadius = getInterpolatedRadius();
// Translate the flyout background towards the collapsed 'dot' state.
mBgTranslationX = mTranslationXWhenDot * mPercentTransitionedToDot;
@@ -387,8 +388,7 @@
if (!mTriangleOutline.isEmpty()) {
// Draw the rect into the outline as a path so we can merge the triangle path into it.
final Path rectPath = new Path();
- final float interpolatedRadius = mNewDotRadius * mPercentTransitionedToDot
- + mCornerRadius * (1 - mPercentTransitionedToDot);
+ final float interpolatedRadius = getInterpolatedRadius();
rectPath.addRoundRect(mBgRect, interpolatedRadius,
interpolatedRadius, Path.Direction.CW);
outline.setConvexPath(rectPath);
@@ -420,4 +420,9 @@
outline.mPath.transform(outlineMatrix);
}
}
+
+ private float getInterpolatedRadius() {
+ return mNewDotRadius * mPercentTransitionedToDot
+ + mCornerRadius * (1 - mPercentTransitionedToDot);
+ }
}
diff --git a/packages/SystemUI/src/com/android/systemui/bubbles/BubbleIconFactory.java b/packages/SystemUI/src/com/android/systemui/bubbles/BubbleIconFactory.java
index a1c77c0..9ff033c 100644
--- a/packages/SystemUI/src/com/android/systemui/bubbles/BubbleIconFactory.java
+++ b/packages/SystemUI/src/com/android/systemui/bubbles/BubbleIconFactory.java
@@ -16,8 +16,14 @@
package com.android.systemui.bubbles;
import android.content.Context;
+import android.graphics.Bitmap;
+import android.graphics.Canvas;
+import android.graphics.drawable.BitmapDrawable;
+import android.graphics.drawable.Drawable;
import com.android.launcher3.icons.BaseIconFactory;
+import com.android.launcher3.icons.BitmapInfo;
+import com.android.launcher3.icons.ShadowGenerator;
import com.android.systemui.R;
/**
@@ -26,13 +32,37 @@
* so there is no need to manage a pool across multiple threads.
*/
public class BubbleIconFactory extends BaseIconFactory {
+
protected BubbleIconFactory(Context context) {
super(context, context.getResources().getConfiguration().densityDpi,
context.getResources().getDimensionPixelSize(R.dimen.individual_bubble_size));
}
- public int getBadgeSize() {
+ int getBadgeSize() {
return mContext.getResources().getDimensionPixelSize(
com.android.launcher3.icons.R.dimen.profile_badge_size);
}
+
+ BitmapInfo getBadgedBitmap(Bubble b) {
+ Bitmap userBadgedBitmap = createIconBitmap(
+ b.getUserBadgedAppIcon(), 1f, getBadgeSize());
+
+ Canvas c = new Canvas();
+ ShadowGenerator shadowGenerator = new ShadowGenerator(getBadgeSize());
+ c.setBitmap(userBadgedBitmap);
+ shadowGenerator.recreateIcon(Bitmap.createBitmap(userBadgedBitmap), c);
+ BitmapInfo bitmapInfo = createIconBitmap(userBadgedBitmap);
+ return bitmapInfo;
+ }
+
+ 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/BubbleStackView.java b/packages/SystemUI/src/com/android/systemui/bubbles/BubbleStackView.java
index 4a1bbe4..29de2f0 100644
--- a/packages/SystemUI/src/com/android/systemui/bubbles/BubbleStackView.java
+++ b/packages/SystemUI/src/com/android/systemui/bubbles/BubbleStackView.java
@@ -19,6 +19,8 @@
import static android.view.ViewGroup.LayoutParams.MATCH_PARENT;
import static android.view.ViewGroup.LayoutParams.WRAP_CONTENT;
+import static com.android.systemui.bubbles.BadgedImageView.DOT_STATE_DEFAULT;
+import static com.android.systemui.bubbles.BadgedImageView.DOT_STATE_SUPPRESSED_FOR_FLYOUT;
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;
@@ -169,7 +171,7 @@
* Callback to run after the flyout hides. Also called if a new flyout is shown before the
* previous one animates out.
*/
- private Runnable mFlyoutOnHide;
+ private Runnable mAfterFlyoutHidden;
/** Layout change listener that moves the stack to the nearest valid position on rotation. */
private OnLayoutChangeListener mOrientationChangedListener;
@@ -674,18 +676,6 @@
}
/**
- * Updates the visibility of the 'dot' indicating an update on the bubble.
- *
- * @param key the {@link NotificationEntry#key} associated with the bubble.
- */
- public void updateDotVisibility(String key) {
- Bubble b = mBubbleData.getBubbleWithKey(key);
- if (b != null) {
- b.updateDotVisibility();
- }
- }
-
- /**
* Sets the listener to notify when the bubble stack is expanded.
*/
public void setExpandListener(BubbleController.BubbleExpandListener listener) {
@@ -707,9 +697,9 @@
}
/**
- * The {@link BubbleView} that is expanded, null if one does not exist.
+ * The {@link BadgedImageView} that is expanded, null if one does not exist.
*/
- BubbleView getExpandedBubbleView() {
+ BadgedImageView getExpandedBubbleView() {
return mExpandedBubble != null ? mExpandedBubble.getIconView() : null;
}
@@ -731,7 +721,7 @@
Bubble bubbleToExpand = mBubbleData.getBubbleWithKey(key);
if (bubbleToExpand != null) {
setSelectedBubble(bubbleToExpand);
- bubbleToExpand.setShowInShadeWhenBubble(false);
+ bubbleToExpand.setShowInShade(false);
setExpanded(true);
}
}
@@ -746,8 +736,8 @@
mStackOnLeftOrWillBe = mStackAnimationController.isStackOnLeftSide();
}
+ bubble.setBubbleIconFactory(mBubbleIconFactory);
bubble.inflate(mInflater, this);
- bubble.getIconView().setBubbleIconFactory(mBubbleIconFactory);
bubble.getIconView().updateViews();
// Set the dot position to the opposite of the side the stack is resting on, since the stack
@@ -884,7 +874,7 @@
if (isIntersecting(mBubbleContainer, x, y)) {
// Could be tapping or dragging a bubble while expanded
for (int i = 0; i < mBubbleContainer.getChildCount(); i++) {
- BubbleView view = (BubbleView) mBubbleContainer.getChildAt(i);
+ BadgedImageView view = (BadgedImageView) mBubbleContainer.getChildAt(i);
if (isIntersecting(view, x, y)) {
return view;
}
@@ -1028,9 +1018,9 @@
}
/** Return the BubbleView at the given index from the bubble container. */
- public BubbleView getBubbleAt(int i) {
+ public BadgedImageView getBubbleAt(int i) {
return mBubbleContainer.getChildCount() > i
- ? (BubbleView) mBubbleContainer.getChildAt(i)
+ ? (BadgedImageView) mBubbleContainer.getChildAt(i)
: null;
}
@@ -1382,16 +1372,6 @@
: 0f);
}
- /** Updates the dot visibility, this is used in response to a zen mode config change. */
- void updateDots() {
- int bubbsCount = mBubbleContainer.getChildCount();
- for (int i = 0; i < bubbsCount; i++) {
- BubbleView bv = (BubbleView) mBubbleContainer.getChildAt(i);
- // If nothing changed the animation won't happen
- bv.updateDotVisibility(true /* animate */);
- }
- }
-
/**
* Calculates the y position of the expanded view when it is expanded.
*/
@@ -1405,37 +1385,40 @@
@VisibleForTesting
void animateInFlyoutForBubble(Bubble bubble) {
final CharSequence updateMessage = bubble.getUpdateMessage(getContext());
- if (!bubble.showFlyoutForBubble()) {
- // In case flyout was suppressed for this update, reset now.
- bubble.setSuppressFlyout(false);
- return;
- }
+ final BadgedImageView bubbleView = bubble.getIconView();
if (updateMessage == null
+ || !bubble.showFlyout()
|| isExpanded()
|| mIsExpansionAnimating
|| mIsGestureInProgress
|| mBubbleToExpandAfterFlyoutCollapse != null
- || bubble.getIconView() == null) {
+ || bubbleView == null) {
+ if (bubbleView != null) {
+ bubbleView.setDotState(DOT_STATE_DEFAULT);
+ }
// Skip the message if none exists, we're expanded or animating expansion, or we're
// about to expand a bubble from the previous tapped flyout, or if bubble view is null.
return;
}
+
mFlyoutDragDeltaX = 0f;
clearFlyoutOnHide();
- mFlyoutOnHide = () -> {
- resetDot(bubble);
- if (mBubbleToExpandAfterFlyoutCollapse == null) {
- return;
+ mAfterFlyoutHidden = () -> {
+ // Null it out to ensure it runs once.
+ mAfterFlyoutHidden = null;
+
+ if (mBubbleToExpandAfterFlyoutCollapse != null) {
+ // User tapped on the flyout and we should expand
+ mBubbleData.setSelectedBubble(mBubbleToExpandAfterFlyoutCollapse);
+ mBubbleData.setExpanded(true);
+ mBubbleToExpandAfterFlyoutCollapse = null;
}
- mBubbleData.setSelectedBubble(mBubbleToExpandAfterFlyoutCollapse);
- mBubbleData.setExpanded(true);
- mBubbleToExpandAfterFlyoutCollapse = null;
+ bubbleView.setDotState(DOT_STATE_DEFAULT);
};
mFlyout.setVisibility(INVISIBLE);
- // Temporarily suppress the dot while the flyout is visible.
- bubble.getIconView().setSuppressDot(
- true /* suppressDot */, false /* animate */);
+ // Don't show the dot when we're animating the flyout
+ bubbleView.setDotState(DOT_STATE_SUPPRESSED_FOR_FLYOUT);
// Start flyout expansion. Post in case layout isn't complete and getWidth returns 0.
post(() -> {
@@ -1461,8 +1444,9 @@
mStackAnimationController.isStackOnLeftSide(),
bubble.getIconView().getDotColor() /* dotColor */,
expandFlyoutAfterDelay /* onLayoutComplete */,
- mFlyoutOnHide,
- bubble.getIconView().getDotCenter());
+ mAfterFlyoutHidden,
+ bubble.getIconView().getDotCenter(),
+ !bubble.showDot());
mFlyout.bringToFront();
});
mFlyout.removeCallbacks(mHideFlyout);
@@ -1470,24 +1454,6 @@
logBubbleEvent(bubble, StatsLog.BUBBLE_UICHANGED__ACTION__FLYOUT);
}
- private void resetDot(Bubble bubble) {
- final boolean suppressDot = !bubble.showBubbleDot();
- // If we're going to suppress the dot, make it visible first so it'll
- // visibly animate away.
-
- if (suppressDot) {
- bubble.getIconView().setSuppressDot(
- false /* suppressDot */, false /* animate */);
- }
- // Reset dot suppression. If we're not suppressing due to DND, then
- // stop suppressing it with no animation (since the flyout has
- // transformed into the dot). If we are suppressing due to DND, animate
- // it away.
- bubble.getIconView().setSuppressDot(
- suppressDot /* suppressDot */,
- suppressDot /* animate */);
- }
-
/** Hide the flyout immediately and cancel any pending hide runnables. */
private void hideFlyoutImmediate() {
clearFlyoutOnHide();
@@ -1498,11 +1464,11 @@
private void clearFlyoutOnHide() {
mFlyout.removeCallbacks(mAnimateInFlyout);
- if (mFlyoutOnHide == null) {
+ if (mAfterFlyoutHidden == null) {
return;
}
- mFlyoutOnHide.run();
- mFlyoutOnHide = null;
+ mAfterFlyoutHidden.run();
+ mAfterFlyoutHidden = null;
}
@Override
@@ -1599,8 +1565,7 @@
private void updateBubbleZOrdersAndDotPosition(boolean animate) {
int bubbleCount = mBubbleContainer.getChildCount();
for (int i = 0; i < bubbleCount; i++) {
- BubbleView bv = (BubbleView) mBubbleContainer.getChildAt(i);
- bv.updateDotVisibility(true /* animate */);
+ 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) {
@@ -1705,7 +1670,7 @@
action,
getNormalizedXPosition(),
getNormalizedYPosition(),
- bubble.showInShadeWhenBubble(),
+ bubble.showInShade(),
bubble.isOngoing(),
false /* isAppForeground (unused) */);
}
@@ -1727,8 +1692,8 @@
List<Bubble> bubbles = new ArrayList<>();
for (int i = 0; i < mBubbleContainer.getChildCount(); i++) {
View child = mBubbleContainer.getChildAt(i);
- if (child instanceof BubbleView) {
- String key = ((BubbleView) child).getKey();
+ if (child instanceof BadgedImageView) {
+ String key = ((BadgedImageView) child).getKey();
Bubble bubble = mBubbleData.getBubbleWithKey(key);
bubbles.add(bubble);
}
diff --git a/packages/SystemUI/src/com/android/systemui/bubbles/BubbleTouchHandler.java b/packages/SystemUI/src/com/android/systemui/bubbles/BubbleTouchHandler.java
index 4240e06..44e013a 100644
--- a/packages/SystemUI/src/com/android/systemui/bubbles/BubbleTouchHandler.java
+++ b/packages/SystemUI/src/com/android/systemui/bubbles/BubbleTouchHandler.java
@@ -95,7 +95,7 @@
return false;
}
- if (!(mTouchedView instanceof BubbleView)
+ if (!(mTouchedView instanceof BadgedImageView)
&& !(mTouchedView instanceof BubbleStackView)
&& !(mTouchedView instanceof BubbleFlyoutView)) {
// Not touching anything touchable, but we shouldn't collapse (e.g. touching edge
@@ -187,7 +187,7 @@
mStack.onFlyoutDragFinished(rawX - mTouchDown.x /* deltaX */, velX);
} else if (shouldDismiss) {
final String individualBubbleKey =
- isStack ? null : ((BubbleView) mTouchedView).getKey();
+ isStack ? null : ((BadgedImageView) mTouchedView).getKey();
mStack.magnetToStackIfNeededThenAnimateDismissal(mTouchedView, velX, velY,
() -> {
if (isStack) {
@@ -214,7 +214,7 @@
// Toggle expansion
mBubbleData.setExpanded(!mBubbleData.isExpanded());
} else {
- final String key = ((BubbleView) mTouchedView).getKey();
+ final String key = ((BadgedImageView) mTouchedView).getKey();
mBubbleData.setSelectedBubble(mBubbleData.getBubbleWithKey(key));
}
diff --git a/packages/SystemUI/src/com/android/systemui/bubbles/BubbleView.java b/packages/SystemUI/src/com/android/systemui/bubbles/BubbleView.java
deleted file mode 100644
index 79807b3..0000000
--- a/packages/SystemUI/src/com/android/systemui/bubbles/BubbleView.java
+++ /dev/null
@@ -1,280 +0,0 @@
-/*
- * Copyright (C) 2018 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.annotation.Nullable;
-import android.app.Notification;
-import android.content.Context;
-import android.content.pm.LauncherApps;
-import android.graphics.Bitmap;
-import android.graphics.Canvas;
-import android.graphics.Color;
-import android.graphics.Matrix;
-import android.graphics.Path;
-import android.graphics.drawable.BitmapDrawable;
-import android.graphics.drawable.Drawable;
-import android.graphics.drawable.Icon;
-import android.util.AttributeSet;
-import android.util.PathParser;
-import android.widget.FrameLayout;
-
-import com.android.internal.graphics.ColorUtils;
-import com.android.launcher3.icons.BitmapInfo;
-import com.android.launcher3.icons.ColorExtractor;
-import com.android.launcher3.icons.ShadowGenerator;
-import com.android.systemui.Interpolators;
-import com.android.systemui.R;
-import com.android.systemui.statusbar.notification.collection.NotificationEntry;
-
-/**
- * A floating object on the screen that can post message updates.
- */
-public class BubbleView extends FrameLayout {
-
- // Same value as Launcher3 badge code
- private static final float WHITE_SCRIM_ALPHA = 0.54f;
- private Context mContext;
-
- private BadgedImageView mBadgedImageView;
- private int mDotColor;
- private ColorExtractor mColorExtractor;
-
- // mBubbleIconFactory cannot be static because it depends on Context.
- private BubbleIconFactory mBubbleIconFactory;
-
- private boolean mSuppressDot;
-
- private Bubble mBubble;
-
- public BubbleView(Context context) {
- this(context, null);
- }
-
- public BubbleView(Context context, AttributeSet attrs) {
- this(context, attrs, 0);
- }
-
- public BubbleView(Context context, AttributeSet attrs, int defStyleAttr) {
- this(context, attrs, defStyleAttr, 0);
- }
-
- public BubbleView(Context context, AttributeSet attrs, int defStyleAttr, int defStyleRes) {
- super(context, attrs, defStyleAttr, defStyleRes);
- mContext = context;
- }
-
- @Override
- protected void onFinishInflate() {
- super.onFinishInflate();
- mBadgedImageView = findViewById(R.id.bubble_image);
- mColorExtractor = new ColorExtractor();
- }
-
- @Override
- protected void onAttachedToWindow() {
- super.onAttachedToWindow();
- }
-
- /**
- * Populates this view with a bubble.
- * <p>
- * This should only be called when a new bubble is being set on the view, updates to the
- * current bubble should use {@link #update(Bubble)}.
- *
- * @param bubble the bubble to display in this view.
- */
- public void setBubble(Bubble bubble) {
- mBubble = bubble;
- }
-
- /**
- * @param factory Factory for creating normalized bubble icons.
- */
- public void setBubbleIconFactory(BubbleIconFactory factory) {
- mBubbleIconFactory = factory;
- }
-
- /**
- * The {@link NotificationEntry} associated with this view, if one exists.
- */
- @Nullable
- public NotificationEntry getEntry() {
- return mBubble != null ? mBubble.getEntry() : null;
- }
-
- /**
- * The key for the {@link NotificationEntry} associated with this view, if one exists.
- */
- @Nullable
- public String getKey() {
- return (mBubble != null) ? mBubble.getKey() : null;
- }
-
- /**
- * Updates the UI based on the bubble, updates badge and animates messages as needed.
- */
- public void update(Bubble bubble) {
- mBubble = bubble;
- updateViews();
- }
-
- /** Changes the dot's visibility to match the bubble view's state. */
- void updateDotVisibility(boolean animate) {
- updateDotVisibility(animate, null /* after */);
- }
-
- /**
- * Sets whether or not to hide the dot even if we'd otherwise show it. This is used while the
- * flyout is visible or animating, to hide the dot until the flyout visually transforms into it.
- */
- void setSuppressDot(boolean suppressDot, boolean animate) {
- mSuppressDot = suppressDot;
- updateDotVisibility(animate);
- }
-
- boolean isDotShowing() {
- return mBubble.showBubbleDot() && !mSuppressDot;
- }
-
- int getDotColor() {
- return mDotColor;
- }
-
- /** Sets the position of the 'new' dot, animating it out and back in if requested. */
- void setDotPosition(boolean onLeft, boolean animate) {
- if (animate && onLeft != mBadgedImageView.getDotOnLeft() && isDotShowing()) {
- animateDot(false /* showDot */, () -> {
- mBadgedImageView.setDotOnLeft(onLeft);
- animateDot(true /* showDot */, null);
- });
- } else {
- mBadgedImageView.setDotOnLeft(onLeft);
- }
- }
-
- float[] getDotCenter() {
- float[] unscaled = mBadgedImageView.getDotCenter();
- return new float[]{unscaled[0], unscaled[1]};
- }
-
- boolean getDotPositionOnLeft() {
- return mBadgedImageView.getDotOnLeft();
- }
-
- /**
- * Changes the dot's visibility to match the bubble view's state, running the provided callback
- * after animation if requested.
- */
- private void updateDotVisibility(boolean animate, Runnable after) {
- final boolean showDot = isDotShowing();
- if (animate) {
- animateDot(showDot, after);
- } else {
- mBadgedImageView.setShowDot(showDot);
- mBadgedImageView.setDotScale(showDot ? 1f : 0f);
- }
- }
-
- /**
- * Animates the badge to show or hide.
- */
- private void animateDot(boolean showDot, Runnable after) {
- if (mBadgedImageView.isShowingDot() == showDot) {
- return;
- }
- // Do NOT wait until after animation ends to setShowDot
- // to avoid overriding more recent showDot states.
- mBadgedImageView.setShowDot(showDot);
- mBadgedImageView.clearAnimation();
- mBadgedImageView.animate().setDuration(200)
- .setInterpolator(Interpolators.FAST_OUT_SLOW_IN)
- .setUpdateListener((valueAnimator) -> {
- float fraction = valueAnimator.getAnimatedFraction();
- fraction = showDot ? fraction : 1f - fraction;
- mBadgedImageView.setDotScale(fraction);
- }).withEndAction(() -> {
- mBadgedImageView.setDotScale(showDot ? 1f : 0f);
- if (after != null) {
- after.run();
- }
- }).start();
- }
-
- void updateViews() {
- if (mBubble == null || mBubbleIconFactory == null) {
- return;
- }
-
- Drawable bubbleDrawable = getBubbleDrawable(mContext);
- BitmapInfo badgeBitmapInfo = getBadgedBitmap();
- BitmapInfo bubbleBitmapInfo = getBubbleBitmap(bubbleDrawable, badgeBitmapInfo);
- mBadgedImageView.setImageBitmap(bubbleBitmapInfo.icon);
-
- // Update badge.
- mDotColor = ColorUtils.blendARGB(badgeBitmapInfo.color, Color.WHITE, WHITE_SCRIM_ALPHA);
- mBadgedImageView.setDotColor(mDotColor);
-
- // Update dot.
- Path iconPath = PathParser.createPathFromPathData(
- getResources().getString(com.android.internal.R.string.config_icon_mask));
- Matrix matrix = new Matrix();
- float scale = mBubbleIconFactory.getNormalizer().getScale(bubbleDrawable,
- null /* outBounds */, null /* path */, null /* outMaskShape */);
- float radius = BadgedImageView.DEFAULT_PATH_SIZE / 2f;
- matrix.setScale(scale /* x scale */, scale /* y scale */, radius /* pivot x */,
- radius /* pivot y */);
- iconPath.transform(matrix);
- mBadgedImageView.drawDot(iconPath);
-
- animateDot(isDotShowing(), null /* after */);
- }
-
- Drawable getBubbleDrawable(Context context) {
- if (mBubble.getShortcutInfo() != null && mBubble.usingShortcutInfo()) {
- LauncherApps launcherApps =
- (LauncherApps) getContext().getSystemService(Context.LAUNCHER_APPS_SERVICE);
- int density = getContext().getResources().getConfiguration().densityDpi;
- return launcherApps.getShortcutIconDrawable(mBubble.getShortcutInfo(), density);
- } else {
- Notification.BubbleMetadata metadata = getEntry().getBubbleMetadata();
- Icon ic = metadata.getIcon();
- return ic.loadDrawable(context);
- }
- }
-
- BitmapInfo getBadgedBitmap() {
- Bitmap userBadgedBitmap = mBubbleIconFactory.createIconBitmap(
- mBubble.getUserBadgedAppIcon(), 1f, mBubbleIconFactory.getBadgeSize());
-
- Canvas c = new Canvas();
- ShadowGenerator shadowGenerator = new ShadowGenerator(mBubbleIconFactory.getBadgeSize());
- c.setBitmap(userBadgedBitmap);
- shadowGenerator.recreateIcon(Bitmap.createBitmap(userBadgedBitmap), c);
- BitmapInfo bitmapInfo = mBubbleIconFactory.createIconBitmap(userBadgedBitmap);
- return bitmapInfo;
- }
-
- BitmapInfo getBubbleBitmap(Drawable bubble, BitmapInfo badge) {
- BitmapInfo bubbleIconInfo = mBubbleIconFactory.createBadgedIconBitmap(bubble,
- null /* user */,
- true /* shrinkNonAdaptiveIcons */);
-
- mBubbleIconFactory.badgeWithDrawable(bubbleIconInfo.icon,
- new BitmapDrawable(mContext.getResources(), badge.icon));
- return bubbleIconInfo;
- }
-}