Donate to e Foundation | Murena handsets with /e/OS | Own a part of Murena! Learn more

Commit 323a8045 authored by Lyn Han's avatar Lyn Han
Browse files

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
parent 38cc1531
Loading
Loading
Loading
Loading
+55 −17
Original line number Diff line number Diff line
@@ -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());
    }
}
+11 −9
Original line number Diff line number Diff line
@@ -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;

+0 −13
Original line number Diff line number Diff line
@@ -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;
    }
}
+26 −19
Original line number Diff line number Diff line
@@ -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,31 +82,30 @@ 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 iconFactory = BubbleIconFactory(context)

        // 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))

        // Set button icon
        val iconFactory = BubbleIconFactory(context)
        val fg = InsetDrawable(overflowBtn.getDrawable(),
        val fg = InsetDrawable(overflowBtn.drawable,
            bitmapSize - iconBitmapSize /* inset */)
        bitmap = iconFactory.createBadgedIconBitmap(AdaptiveIconDrawable(bg, fg),
            null /* user */, true /* shrinkNonAdaptiveIcons */).icon

        // Set dot path
        // Update dot path
        dotPath = PathParser.createPathFromPathData(
            res.getString(com.android.internal.R.string.config_icon_mask))
        val scale = iconFactory.normalizer.getScale(overflowBtn.getDrawable(),
@@ -113,7 +114,9 @@ class BubbleOverflow(
        val matrix = Matrix()
        matrix.setScale(scale /* x scale */, scale /* y scale */, radius /* pivot x */,
            radius /* pivot y */)
        dotPath?.transform(matrix)
        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
    }

+25 −27
Original line number Diff line number Diff line
@@ -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(
            if (mIsExpanded) {
                bv.removeDotSuppressionFlag(
                        BadgedImageView.SuppressionFlag.BEHIND_STACK);
            } else {
                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();
            }
        }
    }
Loading