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

Commit 4b20e0c3 authored by Tony Wickham's avatar Tony Wickham Committed by Android (Google) Code Review
Browse files

Merge "Animate badges when they are added or removed" into ub-launcher3-master

parents 72d90876 1e61849b
Loading
Loading
Loading
Loading
+4 −4
Original line number Diff line number Diff line
@@ -167,7 +167,7 @@ public class BubbleTextView extends TextView
            applyPromiseState(promiseStateChanged);
        }

        applyBadgeState(info);
        applyBadgeState(info, false /* animate */);
    }

    public void applyFromApplicationInfo(AppInfo info) {
@@ -179,7 +179,7 @@ public class BubbleTextView extends TextView
        // Verify high res immediately
        verifyHighRes();

        applyBadgeState(info);
        applyBadgeState(info, false /* animate */);
    }

    public void applyFromPackageItemInfo(PackageItemInfo info) {
@@ -501,11 +501,11 @@ public class BubbleTextView extends TextView
        }
    }

    public void applyBadgeState(ItemInfo itemInfo) {
    public void applyBadgeState(ItemInfo itemInfo, boolean animate) {
        if (mIcon instanceof FastBitmapDrawable) {
            BadgeInfo badgeInfo = mLauncher.getPopupDataProvider().getBadgeInfoForItem(itemInfo);
            BadgeRenderer badgeRenderer = mLauncher.getDeviceProfile().mBadgeRenderer;
            ((FastBitmapDrawable) mIcon).applyIconBadge(badgeInfo, badgeRenderer);
            ((FastBitmapDrawable) mIcon).applyIconBadge(badgeInfo, badgeRenderer, animate);
        }
    }

+28 −5
Original line number Diff line number Diff line
@@ -30,7 +30,7 @@ import android.graphics.PixelFormat;
import android.graphics.PorterDuff;
import android.graphics.PorterDuffColorFilter;
import android.graphics.drawable.Drawable;
import android.util.Log;
import android.util.Property;
import android.util.SparseArray;
import android.view.animation.DecelerateInterpolator;

@@ -107,6 +107,21 @@ public class FastBitmapDrawable extends Drawable {
    private BadgeInfo mBadgeInfo;
    private BadgeRenderer mBadgeRenderer;
    private IconPalette mIconPalette;
    private float mBadgeScale;

    private static final Property<FastBitmapDrawable, Float> BADGE_SCALE_PROPERTY
            = new Property<FastBitmapDrawable, Float>(Float.TYPE, "badgeScale") {
        @Override
        public Float get(FastBitmapDrawable fastBitmapDrawable) {
            return fastBitmapDrawable.mBadgeScale;
        }

        @Override
        public void set(FastBitmapDrawable fastBitmapDrawable, Float value) {
            fastBitmapDrawable.mBadgeScale = value;
            fastBitmapDrawable.invalidateSelf();
        }
    };

    // The saturation and brightness are values that are mapped to REDUCED_FILTER_VALUE_SPACE and
    // as a result, can be used to compose the key for the cached ColorMatrixColorFilters
@@ -123,16 +138,24 @@ public class FastBitmapDrawable extends Drawable {
        setFilterBitmap(true);
    }

    public void applyIconBadge(BadgeInfo badgeInfo, BadgeRenderer badgeRenderer) {
    public void applyIconBadge(final BadgeInfo badgeInfo, BadgeRenderer badgeRenderer,
            boolean animate) {
        boolean wasBadged = mBadgeInfo != null;
        boolean isBadged = badgeInfo != null;
        float newBadgeScale = isBadged ? 1f : 0;
        mBadgeInfo = badgeInfo;
        mBadgeRenderer = badgeRenderer;
        if (wasBadged || isBadged) {
            mIconPalette = getIconPalette();
            // Animate when a badge is first added or when it is removed.
            if (animate && (wasBadged ^ isBadged) && isVisible()) {
                ObjectAnimator.ofFloat(this, BADGE_SCALE_PROPERTY, newBadgeScale).start();
            } else {
                mBadgeScale = newBadgeScale;
                invalidateSelf();
            }
        }
    }

    @Override
    public void draw(Canvas canvas) {
@@ -154,7 +177,7 @@ public class FastBitmapDrawable extends Drawable {

    protected void drawBadgeIfNecessary(Canvas canvas) {
        if (hasBadge()) {
            mBadgeRenderer.draw(canvas, mIconPalette, mBadgeInfo, getBounds());
            mBadgeRenderer.draw(canvas, mIconPalette, mBadgeInfo, getBounds(), mBadgeScale);
        }
    }

@@ -167,7 +190,7 @@ public class FastBitmapDrawable extends Drawable {
    }

    private boolean hasBadge() {
        return mBadgeInfo != null && mBadgeInfo.getNotificationCount() != 0;
        return (mBadgeInfo != null && mBadgeInfo.getNotificationCount() > 0) || mBadgeScale > 0;
    }

    @Override
+1 −1
Original line number Diff line number Diff line
@@ -3993,7 +3993,7 @@ public class Workspace extends PagedView
                if (info instanceof ShortcutInfo && v instanceof BubbleTextView
                        && packageUserKey.updateFromItemInfo(info)) {
                    if (updatedBadges.contains(packageUserKey)) {
                        ((BubbleTextView) v).applyBadgeState(info);
                        ((BubbleTextView) v).applyBadgeState(info, true /* animate */);
                        folderIds.add(info.container);
                    }
                }
+32 −34
Original line number Diff line number Diff line
@@ -24,6 +24,7 @@ import android.graphics.Paint;
import android.graphics.Rect;
import android.graphics.RectF;
import android.graphics.Shader;
import android.support.annotation.Nullable;

import com.android.launcher3.R;
import com.android.launcher3.graphics.IconPalette;
@@ -34,62 +35,59 @@ import com.android.launcher3.graphics.IconPalette;
 */
public class BadgeRenderer {

    public int size;
    public int textSize;
    public IconDrawer largeIconDrawer;
    public IconDrawer smallIconDrawer;

    private final Context mContext;
    private final RectF mBackgroundRect = new RectF();
    private final int mSize;
    private final int mTextHeight;
    private final IconDrawer mLargeIconDrawer;
    private final IconDrawer mSmallIconDrawer;
    private final Paint mBackgroundPaint = new Paint(Paint.ANTI_ALIAS_FLAG);
    private final Paint mTextPaint = new Paint(Paint.ANTI_ALIAS_FLAG);
    private final int mTextHeight;

    public BadgeRenderer(Context context) {
        mContext = context;
        Resources res = context.getResources();
        size = res.getDimensionPixelSize(R.dimen.badge_size);
        textSize = res.getDimensionPixelSize(R.dimen.badge_text_size);
        largeIconDrawer = new IconDrawer(res.getDimensionPixelSize(R.dimen.badge_small_padding));
        smallIconDrawer = new IconDrawer(res.getDimensionPixelSize(R.dimen.badge_large_padding));
        mSize = res.getDimensionPixelSize(R.dimen.badge_size);
        mLargeIconDrawer = new IconDrawer(res.getDimensionPixelSize(R.dimen.badge_small_padding));
        mSmallIconDrawer = new IconDrawer(res.getDimensionPixelSize(R.dimen.badge_large_padding));
        mTextPaint.setTextAlign(Paint.Align.CENTER);
        mTextPaint.setTextSize(textSize);
        mTextPaint.setTextSize(res.getDimensionPixelSize(R.dimen.badge_text_size));
        // Measure the text height.
        Rect temp = new Rect();
        mTextPaint.getTextBounds("0", 0, 1, temp);
        mTextHeight = temp.height();
        Rect tempTextHeight = new Rect();
        mTextPaint.getTextBounds("0", 0, 1, tempTextHeight);
        mTextHeight = tempTextHeight.height();
    }

    /**
     * Draw a circle in the top right corner of the given bounds, and draw
     * {@link BadgeInfo#getNotificationCount()} on top of the circle.
     * @param palette The colors (based on the icon) to use for the badge.
     * @param badgeInfo Contains data to draw on the badge.
     * @param badgeInfo Contains data to draw on the badge. Could be null if we are animating out.
     * @param iconBounds The bounds of the icon being badged.
     * @param badgeScale The progress of the animation, from 0 to 1.
     */
    public void draw(Canvas canvas, IconPalette palette, BadgeInfo badgeInfo, Rect iconBounds) {
    public void draw(Canvas canvas, IconPalette palette, @Nullable BadgeInfo badgeInfo,
            Rect iconBounds, float badgeScale) {
        mBackgroundPaint.setColor(palette.backgroundColor);
        mTextPaint.setColor(palette.textColor);
        mBackgroundRect.set(iconBounds.right - size, iconBounds.top, iconBounds.right,
                iconBounds.top + size);
        canvas.drawOval(mBackgroundRect, mBackgroundPaint);
        IconDrawer iconDrawer = badgeInfo.isIconLarge() ? largeIconDrawer : smallIconDrawer;
        Shader icon = badgeInfo.getNotificationIconForBadge(mContext, palette.backgroundColor, size,
                iconDrawer.mPadding);
        canvas.save(Canvas.MATRIX_SAVE_FLAG);
        // We draw the badge relative to its center.
        canvas.translate(iconBounds.right - mSize / 2, iconBounds.top + mSize / 2);
        canvas.scale(badgeScale, badgeScale);
        canvas.drawCircle(0, 0, mSize / 2, mBackgroundPaint);
        IconDrawer iconDrawer = badgeInfo != null && badgeInfo.isIconLarge()
                ? mLargeIconDrawer : mSmallIconDrawer;
        Shader icon = badgeInfo == null ? null : badgeInfo.getNotificationIconForBadge(
                mContext, palette.backgroundColor, mSize, iconDrawer.mPadding);
        if (icon != null) {
            // Draw the notification icon with padding.
            canvas.save();
            canvas.translate(mBackgroundRect.left, mBackgroundRect.top);
            iconDrawer.drawIcon(icon, canvas);
            canvas.restore();
        } else {
            // Draw the notification count.
            String notificationCount = String.valueOf(badgeInfo.getNotificationCount());
            canvas.drawText(notificationCount,
                    mBackgroundRect.centerX(),
                    mBackgroundRect.centerY() + mTextHeight / 2,
                    mTextPaint);
            String notificationCount = badgeInfo == null ? "0"
                    : String.valueOf(badgeInfo.getNotificationCount());
            canvas.drawText(notificationCount, 0, mTextHeight / 2, mTextPaint);
        }
        canvas.restore();
    }

    /** Draws the notification icon with padding of a given size. */
@@ -102,15 +100,15 @@ public class BadgeRenderer {

        public IconDrawer(int padding) {
            mPadding = padding;
            mCircleClipBitmap = Bitmap.createBitmap(size, size, Bitmap.Config.ALPHA_8);
            mCircleClipBitmap = Bitmap.createBitmap(mSize, mSize, Bitmap.Config.ALPHA_8);
            Canvas canvas = new Canvas();
            canvas.setBitmap(mCircleClipBitmap);
            canvas.drawCircle(size / 2, size / 2, size / 2 - padding, mPaint);
            canvas.drawCircle(mSize / 2, mSize / 2, mSize / 2 - padding, mPaint);
        }

        public void drawIcon(Shader icon, Canvas canvas) {
            mPaint.setShader(icon);
            canvas.drawBitmap(mCircleClipBitmap, 0f, 0f, mPaint);
            canvas.drawBitmap(mCircleClipBitmap, -mSize / 2, -mSize / 2, mPaint);
            mPaint.setShader(null);
        }
    }
+52 −3
Original line number Diff line number Diff line
@@ -33,6 +33,7 @@ import android.graphics.drawable.Drawable;
import android.os.Parcelable;
import android.util.AttributeSet;
import android.util.DisplayMetrics;
import android.util.Property;
import android.view.LayoutInflater;
import android.view.MotionEvent;
import android.view.View;
@@ -130,6 +131,21 @@ public class FolderIcon extends FrameLayout implements FolderListener {

    private FolderBadgeInfo mBadgeInfo = new FolderBadgeInfo();
    private BadgeRenderer mBadgeRenderer;
    private float mBadgeScale;

    private static final Property<FolderIcon, Float> BADGE_SCALE_PROPERTY
            = new Property<FolderIcon, Float>(Float.TYPE, "badgeScale") {
        @Override
        public Float get(FolderIcon folderIcon) {
            return folderIcon.mBadgeScale;
        }

        @Override
        public void set(FolderIcon folderIcon, Float value) {
            folderIcon.mBadgeScale = value;
            folderIcon.invalidate();
        }
    };

    public FolderIcon(Context context, AttributeSet attrs) {
        super(context, attrs);
@@ -388,9 +404,26 @@ public class FolderIcon extends FrameLayout implements FolderListener {
    }

    public void setBadgeInfo(FolderBadgeInfo badgeInfo) {
        updateBadgeScale(mBadgeInfo.getNotificationCount(), badgeInfo.getNotificationCount());
        mBadgeInfo = badgeInfo;
    }

    /**
     * Sets mBadgeScale to 1 or 0, animating if oldCount or newCount is 0
     * (the badge is being added or removed).
     */
    private void updateBadgeScale(int oldCount, int newCount) {
        boolean wasBadged = oldCount > 0;
        boolean isBadged = newCount > 0;
        float newBadgeScale = isBadged ? 1f : 0f;
        // Animate when a badge is first added or when it is removed.
        if ((wasBadged ^ isBadged) && isShown()) {
            ObjectAnimator.ofFloat(this, BADGE_SCALE_PROPERTY, newBadgeScale).start();
        } else {
            mBadgeScale = newBadgeScale;
            invalidate();
        }
    }

    static class PreviewItemDrawingParams {
        PreviewItemDrawingParams(float transX, float transY, float scale, float overlayAlpha) {
@@ -550,6 +583,14 @@ public class FolderIcon extends FrameLayout implements FolderListener {
            return basePreviewOffsetY - (getScaledRadius() - getRadius());
        }

        /**
         * Returns the progress of the scale animation, where 0 means the scale is at 1f
         * and 1 means the scale is at ACCEPT_SCALE_FACTOR.
         */
        float getScaleProgress() {
            return (mScale - 1f) / (ACCEPT_SCALE_FACTOR - 1f);
        }

        void invalidate() {
            int radius = getScaledRadius();
            mClipPath.reset();
@@ -785,8 +826,10 @@ public class FolderIcon extends FrameLayout implements FolderListener {
        int offsetY = mBackground.getOffsetY();
        int previewSize = (int) (mBackground.previewSize * mBackground.mScale);
        Rect bounds = new Rect(offsetX, offsetY, offsetX + previewSize, offsetY + previewSize);
        if (mBadgeInfo != null && mBadgeInfo.getNotificationCount() > 0) {
            mBadgeRenderer.draw(canvas, IconPalette.FOLDER_ICON_PALETTE, mBadgeInfo, bounds);
        if ((mBadgeInfo != null && mBadgeInfo.getNotificationCount() > 0) || mBadgeScale > 0) {
            // If we are animating to the accepting state, animate the badge out.
            float badgeScale = Math.max(0, mBadgeScale - mBackground.getScaleProgress());
            mBadgeRenderer.draw(canvas, IconPalette.FOLDER_ICON_PALETTE, mBadgeInfo, bounds, badgeScale);
        }
    }

@@ -939,14 +982,20 @@ public class FolderIcon extends FrameLayout implements FolderListener {

    @Override
    public void onAdd(ShortcutInfo item) {
        int oldCount = mBadgeInfo.getNotificationCount();
        mBadgeInfo.addBadgeInfo(mLauncher.getPopupDataProvider().getBadgeInfoForItem(item));
        int newCount = mBadgeInfo.getNotificationCount();
        updateBadgeScale(oldCount, newCount);
        invalidate();
        requestLayout();
    }

    @Override
    public void onRemove(ShortcutInfo item) {
        int oldCount = mBadgeInfo.getNotificationCount();
        mBadgeInfo.subtractBadgeInfo(mLauncher.getPopupDataProvider().getBadgeInfoForItem(item));
        int newCount = mBadgeInfo.getNotificationCount();
        updateBadgeScale(oldCount, newCount);
        invalidate();
        requestLayout();
    }