Merge "Enable left badge, hidden badge"

This commit is contained in:
TreeHugger Robot
2020-09-10 22:21:11 +00:00
committed by Android (Google) Code Review
7 changed files with 136 additions and 97 deletions

View File

@@ -17,9 +17,12 @@ package com.android.systemui.bubbles;
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 com.android.systemui.R;
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 class BadgedImageView extends ImageView {
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 @@ public class BadgedImageView extends ImageView {
private BubbleViewProvider mBubble;
private int mBubbleBitmapSize;
private int mBubbleSize;
private DotRenderer mDotRenderer;
private DotRenderer.DrawParams mDrawParams;
private boolean mOnLeft;
@@ -93,6 +102,7 @@ public class BadgedImageView extends ImageView {
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 class BadgedImageView extends ImageView {
*/
public void setRenderedBubble(BubbleViewProvider bubble) {
mBubble = bubble;
setImageBitmap(bubble.getBadgedImage());
showBadge();
mDotColor = bubble.getDotColor();
drawDot(bubble.getDotPath());
}
@@ -160,14 +170,6 @@ public class BadgedImageView extends ImageView {
}
}
/**
* 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.
*/
@@ -219,22 +221,29 @@ public class BadgedImageView extends ImageView {
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 @@ public class BadgedImageView extends ImageView {
}
}).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());
}
}

View File

@@ -41,7 +41,6 @@ import android.util.Log;
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 @@ class Bubble implements BubbleViewProvider {
}
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 @@ class Bubble implements BubbleViewProvider {
}
@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 @@ class Bubble implements BubbleViewProvider {
mAppName = info.appName;
mFlyoutMessage = info.flyoutMessage;
mBadgedAppIcon = info.badgedAppIcon;
mBadgedImage = info.badgedBubbleImage;
mBadgeDrawable = info.badgeDrawable;
mBubbleBitmap = info.bubbleBitmap;
mDotColor = info.dotColor;
mDotPath = info.dotPath;

View File

@@ -156,17 +156,4 @@ public class BubbleIconFactory extends BaseIconFactory {
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;
}
}

View File

@@ -23,6 +23,7 @@ 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.util.PathParser
import android.util.TypedValue
@@ -36,8 +37,9 @@ class BubbleOverflow(
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 @@ class BubbleOverflow(
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 @@ class BubbleOverflow(
return dotColor
}
override fun getBadgedImage(): Bitmap? {
override fun getAppBadge(): Drawable? {
return null
}
override fun getBubbleIcon(): Bitmap {
return bitmap
}

View File

@@ -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.Collections;
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 @@ public class BubbleStackView extends FrameLayout
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 @@ public class BubbleStackView extends FrameLayout
// 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 @@ public class BubbleStackView extends FrameLayout
Bubble bubble = bubbles.get(i);
mBubbleContainer.reorderView(bubble.getIconView(), i);
}
updateBubbleZOrdersAndDotPosition(false /* animate */);
updateBubbleIcons();
updatePointerPosition();
}
@@ -2363,7 +2358,7 @@ public class BubbleStackView extends FrameLayout
// 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 @@ public class BubbleStackView extends FrameLayout
}
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();
}
}
}

View File

@@ -120,8 +120,8 @@ public class BubbleViewInfoTask extends AsyncTask<Void, Void, BubbleViewInfoTask
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 @@ public class BubbleViewInfoTask extends AsyncTask<Void, Void, BubbleViewInfoTask
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(

View File

@@ -18,6 +18,7 @@ package com.android.systemui.bubbles;
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 @@ interface BubbleViewProvider {
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();