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

Commit 5cc433e3 authored by Treehugger Robot's avatar Treehugger Robot Committed by Android (Google) Code Review
Browse files

Merge "Converting FastBitmapDrawable to kotlin" into main

parents d6571891 68b0b5c0
Loading
Loading
Loading
Loading
+2 −2
Original line number Diff line number Diff line
@@ -191,8 +191,8 @@ public class BitmapInfo {
    protected void applyFlags(Context context, FastBitmapDrawable drawable,
            @DrawableCreationFlags int creationFlags, @Nullable Path badgeShape) {
        this.creationFlags = creationFlags;
        drawable.mDisabledAlpha = GraphicsUtils.getFloat(context, R.attr.disabledIconAlpha, 1f);
        drawable.mCreationFlags = creationFlags;
        drawable.disabledAlpha = GraphicsUtils.getFloat(context, R.attr.disabledIconAlpha, 1f);
        drawable.creationFlags = creationFlags;
        if ((creationFlags & FLAG_NO_BADGE) == 0) {
            Drawable badge = getBadgeDrawable(context, (creationFlags & FLAG_THEMED) != 0,
                    (creationFlags & FLAG_SKIP_USER_BADGE) != 0, badgeShape);
+6 −5
Original line number Diff line number Diff line
@@ -349,7 +349,7 @@ public class ClockDrawableWrapper extends AdaptiveIconDrawable implements Bitmap
        private final float mCanvasScale;

        ClockIconDrawable(ClockConstantState cs) {
            super(cs.mBitmapInfo);
            super(cs.getBitmapInfo());
            mBoundsOffset = cs.mBoundsOffset;
            mAnimInfo = cs.mAnimInfo;

@@ -412,10 +412,11 @@ public class ClockDrawableWrapper extends AdaptiveIconDrawable implements Bitmap
        @Override
        protected void updateFilter() {
            super.updateFilter();
            int alpha = mIsDisabled ? (int) (mDisabledAlpha * FULLY_OPAQUE) : FULLY_OPAQUE;
            boolean isDisabled = isDisabled();
            int alpha = isDisabled ? (int) (disabledAlpha * FULLY_OPAQUE) : FULLY_OPAQUE;
            setAlpha(alpha);
            mBgPaint.setColorFilter(mIsDisabled ? getDisabledColorFilter() : mBgFilter);
            mFG.setColorFilter(mIsDisabled ? getDisabledColorFilter() : null);
            mBgPaint.setColorFilter(isDisabled ? getDisabledColorFilter() : mBgFilter);
            mFG.setColorFilter(isDisabled ? getDisabledColorFilter() : null);
        }

        @Override
@@ -455,7 +456,7 @@ public class ClockDrawableWrapper extends AdaptiveIconDrawable implements Bitmap

        @Override
        public FastBitmapConstantState newConstantState() {
            return new ClockConstantState(mBitmapInfo, mThemedFgColor, mBoundsOffset,
            return new ClockConstantState(bitmapInfo, mThemedFgColor, mBoundsOffset,
                    mAnimInfo, mBG, mBgPaint.getColorFilter());
        }

+0 −503
Original line number Diff line number Diff line
/*
 * Copyright (C) 2008 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.launcher3.icons;

import static com.android.launcher3.icons.BaseIconFactory.getBadgeSizeForIconSize;
import static com.android.launcher3.icons.BitmapInfo.FLAG_NO_BADGE;
import static com.android.launcher3.icons.BitmapInfo.FLAG_THEMED;
import static com.android.launcher3.icons.GraphicsUtils.setColorAlphaBound;

import android.animation.ObjectAnimator;
import android.graphics.Bitmap;
import android.graphics.Canvas;
import android.graphics.Color;
import android.graphics.ColorFilter;
import android.graphics.ColorMatrix;
import android.graphics.ColorMatrixColorFilter;
import android.graphics.Paint;
import android.graphics.PixelFormat;
import android.graphics.Rect;
import android.graphics.drawable.Drawable;
import android.util.FloatProperty;
import android.util.Log;
import android.view.animation.AccelerateInterpolator;
import android.view.animation.DecelerateInterpolator;
import android.view.animation.Interpolator;
import android.view.animation.PathInterpolator;

import androidx.annotation.Nullable;
import androidx.annotation.VisibleForTesting;
import androidx.core.graphics.ColorUtils;

import com.android.launcher3.icons.BitmapInfo.DrawableCreationFlags;

public class FastBitmapDrawable extends Drawable implements Drawable.Callback {

    private static final String TAG = "FastBitmapDrawable";
    private static final Interpolator ACCEL = new AccelerateInterpolator();
    private static final Interpolator DEACCEL = new DecelerateInterpolator();
    private static final Interpolator HOVER_EMPHASIZED_DECELERATE_INTERPOLATOR =
            new PathInterpolator(0.05f, 0.7f, 0.1f, 1.0f);

    @VisibleForTesting protected static final float PRESSED_SCALE = 1.1f;
    @VisibleForTesting protected static final float HOVERED_SCALE = 1.1f;
    public static final int WHITE_SCRIM_ALPHA = 138;

    private static final float DISABLED_DESATURATION = 1f;
    private static final float DISABLED_BRIGHTNESS = 0.5f;
    protected static final int FULLY_OPAQUE = 255;

    public static final int CLICK_FEEDBACK_DURATION = 200;
    public static final int HOVER_FEEDBACK_DURATION = 300;

    private static boolean sFlagHoverEnabled = false;

    private boolean mAnimationEnabled = true;

    protected final Paint mPaint = new Paint(Paint.FILTER_BITMAP_FLAG | Paint.ANTI_ALIAS_FLAG);
    // b/404578798 - mBitmapInfo isn't expected to be null, but it is in some cases.
    @Nullable public final BitmapInfo mBitmapInfo;

    @Nullable private ColorFilter mColorFilter;

    @VisibleForTesting protected boolean mIsPressed;
    @VisibleForTesting protected boolean mIsHovered;
    protected boolean mIsDisabled;
    protected float mDisabledAlpha = 1f;

    @DrawableCreationFlags int mCreationFlags = 0;

    // Animator and properties for the fast bitmap drawable's scale
    @VisibleForTesting protected static final FloatProperty<FastBitmapDrawable> SCALE
            = new FloatProperty<FastBitmapDrawable>("scale") {
        @Override
        public Float get(FastBitmapDrawable fastBitmapDrawable) {
            return fastBitmapDrawable.mScale;
        }

        @Override
        public void setValue(FastBitmapDrawable fastBitmapDrawable, float value) {
            fastBitmapDrawable.mScale = value;
            fastBitmapDrawable.invalidateSelf();
        }
    };
    @VisibleForTesting protected ObjectAnimator mScaleAnimation;
    private float mScale = 1;
    private int mAlpha = 255;

    private Drawable mBadge;

    private boolean mHoverScaleEnabledForDisplay = true;

    protected FastBitmapDrawable(Bitmap b, int iconColor) {
        this(BitmapInfo.of(b, iconColor));
    }

    public FastBitmapDrawable(Bitmap b) {
        this(BitmapInfo.fromBitmap(b));
    }

    public FastBitmapDrawable(BitmapInfo info) {
        if (info == null) {
            Log.e(TAG, "BitmapInfo should not be null.", new Exception());
        }
        mBitmapInfo = info;
        setFilterBitmap(true);
    }

    /**
     * Returns true if the drawable points to the same bitmap icon object
     */
    public boolean isSameInfo(BitmapInfo info) {
        return mBitmapInfo == info;
    }

    @Override
    protected void onBoundsChange(Rect bounds) {
        super.onBoundsChange(bounds);
        updateBadgeBounds(bounds);
    }

    private void updateBadgeBounds(Rect bounds) {
        if (mBadge != null) {
            setBadgeBounds(mBadge, bounds);
        }
    }

    @Override
    public final void draw(Canvas canvas) {
        if (mScale != 1f) {
            int count = canvas.save();
            Rect bounds = getBounds();
            canvas.scale(mScale, mScale, bounds.exactCenterX(), bounds.exactCenterY());
            drawInternal(canvas, bounds);
            if (mBadge != null) {
                mBadge.draw(canvas);
            }
            canvas.restoreToCount(count);
        } else {
            drawInternal(canvas, getBounds());
            if (mBadge != null) {
                mBadge.draw(canvas);
            }
        }
    }

    protected void drawInternal(Canvas canvas, Rect bounds) {
        if (mBitmapInfo == null) {
            return;
        }
        canvas.drawBitmap(mBitmapInfo.icon, null, bounds, mPaint);
    }

    /**
     * Returns the primary icon color, slightly tinted white
     */
    public int getIconColor() {
        int whiteScrim = setColorAlphaBound(Color.WHITE, WHITE_SCRIM_ALPHA);
        if (mBitmapInfo == null) {
            return whiteScrim;
        }
        return ColorUtils.compositeColors(whiteScrim, mBitmapInfo.color);
    }

    /**
     * Returns if this represents a themed icon
     */
    public boolean isThemed() {
        return false;
    }

    /**
     * Returns true if the drawable was created with theme, even if it doesn't
     * support theming itself.
     */
    public boolean isCreatedForTheme() {
        return isThemed() || (mCreationFlags & FLAG_THEMED) != 0;
    }

    @Override
    public void setColorFilter(ColorFilter cf) {
        mColorFilter = cf;
        updateFilter();
    }

    @Override
    public int getOpacity() {
        return PixelFormat.TRANSLUCENT;
    }

    @Override
    public void setAlpha(int alpha) {
        if (mAlpha != alpha) {
            mAlpha = alpha;
            mPaint.setAlpha(alpha);
            invalidateSelf();
            if (mBadge != null) {
                mBadge.setAlpha(alpha);
            }
        }
    }

    @Override
    public void setFilterBitmap(boolean filterBitmap) {
        mPaint.setFilterBitmap(filterBitmap);
        mPaint.setAntiAlias(filterBitmap);
    }

    @Override
    public int getAlpha() {
        return mAlpha;
    }

    public void resetScale() {
        if (mScaleAnimation != null) {
            mScaleAnimation.cancel();
            mScaleAnimation = null;
        }
        mScale = 1;
        invalidateSelf();
    }

    public float getAnimatedScale() {
        return mScaleAnimation == null ? 1 : mScale;
    }

    @Override
    public int getIntrinsicWidth() {
        if (mBitmapInfo == null) {
            return 0;
        }
        return mBitmapInfo.icon.getWidth();
    }

    @Override
    public int getIntrinsicHeight() {
        if (mBitmapInfo == null) {
            return 0;
        }
        return mBitmapInfo.icon.getHeight();
    }

    @Override
    public int getMinimumWidth() {
        return getBounds().width();
    }

    @Override
    public int getMinimumHeight() {
        return getBounds().height();
    }

    @Override
    public boolean isStateful() {
        return true;
    }

    @Override
    public ColorFilter getColorFilter() {
        return mPaint.getColorFilter();
    }

    @Override
    protected boolean onStateChange(int[] state) {
        if (!mAnimationEnabled) {
            return false;
        }

        boolean isPressed = false;
        boolean isHovered = false;
        for (int s : state) {
            if (s == android.R.attr.state_pressed) {
                isPressed = true;
                break;
            } else if (sFlagHoverEnabled
                    && s == android.R.attr.state_hovered
                    && mHoverScaleEnabledForDisplay) {
                isHovered = true;
                // Do not break on hovered state, as pressed state should take precedence.
            }
        }
        if (mIsPressed != isPressed || mIsHovered != isHovered) {
            if (mScaleAnimation != null) {
                mScaleAnimation.cancel();
            }

            float endScale = isPressed ? PRESSED_SCALE : (isHovered ? HOVERED_SCALE : 1f);
            if (mScale != endScale) {
                if (isVisible()) {
                    Interpolator interpolator =
                            isPressed != mIsPressed ? (isPressed ? ACCEL : DEACCEL)
                                    : HOVER_EMPHASIZED_DECELERATE_INTERPOLATOR;
                    int duration =
                            isPressed != mIsPressed ? CLICK_FEEDBACK_DURATION
                                    : HOVER_FEEDBACK_DURATION;
                    mScaleAnimation = ObjectAnimator.ofFloat(this, SCALE, endScale);
                    mScaleAnimation.setDuration(duration);
                    mScaleAnimation.setInterpolator(interpolator);
                    mScaleAnimation.start();
                } else {
                    mScale = endScale;
                    invalidateSelf();
                }
            }
            mIsPressed = isPressed;
            mIsHovered = isHovered;
            return true;
        }
        return false;
    }

    public void setIsDisabled(boolean isDisabled) {
        if (mIsDisabled != isDisabled) {
            mIsDisabled = isDisabled;
            if (mBadge instanceof FastBitmapDrawable fbd) {
                fbd.setIsDisabled(isDisabled);
            }
            updateFilter();
        }
    }

    protected boolean isDisabled() {
        return mIsDisabled;
    }

    public void setBadge(Drawable badge) {
        if (mBadge != null) {
            mBadge.setCallback(null);
        }
        mBadge = badge;
        if (mBadge != null) {
            mBadge.setCallback(this);
        }
        updateBadgeBounds(getBounds());
        updateFilter();
    }

    @VisibleForTesting
    public Drawable getBadge() {
        return mBadge;
    }

    /**
     * Updates the paint to reflect the current brightness and saturation.
     */
    protected void updateFilter() {
        mPaint.setColorFilter(mIsDisabled ? getDisabledColorFilter(mDisabledAlpha) : mColorFilter);
        if (mBadge != null) {
            mBadge.setColorFilter(getColorFilter());
        }
        invalidateSelf();
    }

    protected FastBitmapConstantState newConstantState() {
        return new FastBitmapConstantState(mBitmapInfo);
    }

    @Override
    public final ConstantState getConstantState() {
        FastBitmapConstantState cs = newConstantState();
        cs.mIsDisabled = mIsDisabled;
        if (mBadge != null) {
            cs.mBadgeConstantState = mBadge.getConstantState();
        }
        cs.mCreationFlags = mCreationFlags;
        return cs;
    }

    public static ColorFilter getDisabledColorFilter() {
        return getDisabledColorFilter(1);
    }

    // Returns if the FastBitmapDrawable contains a badge.
    public boolean hasBadge() {
        return (mCreationFlags & FLAG_NO_BADGE) == 0;
    }

    private static ColorFilter getDisabledColorFilter(float disabledAlpha) {
        ColorMatrix tempBrightnessMatrix = new ColorMatrix();
        ColorMatrix tempFilterMatrix = new ColorMatrix();

        tempFilterMatrix.setSaturation(1f - DISABLED_DESATURATION);
        float scale = 1 - DISABLED_BRIGHTNESS;
        int brightnessI =   (int) (255 * DISABLED_BRIGHTNESS);
        float[] mat = tempBrightnessMatrix.getArray();
        mat[0] = scale;
        mat[6] = scale;
        mat[12] = scale;
        mat[4] = brightnessI;
        mat[9] = brightnessI;
        mat[14] = brightnessI;
        mat[18] = disabledAlpha;
        tempFilterMatrix.preConcat(tempBrightnessMatrix);
        return new ColorMatrixColorFilter(tempFilterMatrix);
    }

    protected static final int getDisabledColor(int color) {
        int component = (Color.red(color) + Color.green(color) + Color.blue(color)) / 3;
        float scale = 1 - DISABLED_BRIGHTNESS;
        int brightnessI = (int) (255 * DISABLED_BRIGHTNESS);
        component = Math.min(Math.round(scale * component + brightnessI), FULLY_OPAQUE);
        return Color.rgb(component, component, component);
    }

    /**
     * Sets the bounds for the badge drawable based on the main icon bounds
     */
    public static void setBadgeBounds(Drawable badge, Rect iconBounds) {
        int size = getBadgeSizeForIconSize(iconBounds.width());
        badge.setBounds(iconBounds.right - size, iconBounds.bottom - size,
                iconBounds.right, iconBounds.bottom);
    }

    @Override
    public void invalidateDrawable(Drawable who) {
        if (who == mBadge) {
            invalidateSelf();
        }
    }

    @Override
    public void scheduleDrawable(Drawable who, Runnable what, long when) {
        if (who == mBadge) {
            scheduleSelf(what, when);
        }
    }

    @Override
    public void unscheduleDrawable(Drawable who, Runnable what) {
        unscheduleSelf(what);
    }

    /**
     * Sets whether hover state functionality is enabled.
     */
    public static void setFlagHoverEnabled(boolean isFlagHoverEnabled) {
        sFlagHoverEnabled = isFlagHoverEnabled;
    }

    public void setHoverScaleEnabledForDisplay(boolean hoverScaleEnabledForDisplay) {
        mHoverScaleEnabledForDisplay = hoverScaleEnabledForDisplay;
    }

    public boolean isAnimationEnabled() {
        return mAnimationEnabled;
    }

    public void setAnimationEnabled(boolean animationEnabled) {
        mAnimationEnabled = animationEnabled;
    }

    public static class FastBitmapConstantState extends ConstantState {
        protected final BitmapInfo mBitmapInfo;

        // These are initialized later so that subclasses don't need to
        // pass everything in constructor
        protected boolean mIsDisabled;
        private ConstantState mBadgeConstantState;

        @DrawableCreationFlags int mCreationFlags = 0;

        public FastBitmapConstantState(Bitmap bitmap, int color) {
            this(BitmapInfo.of(bitmap, color));
        }

        public FastBitmapConstantState(BitmapInfo info) {
            mBitmapInfo = info;
        }

        protected FastBitmapDrawable createDrawable() {
            return new FastBitmapDrawable(mBitmapInfo);
        }

        @Override
        public final FastBitmapDrawable newDrawable() {
            FastBitmapDrawable drawable = createDrawable();
            drawable.setIsDisabled(mIsDisabled);
            if (mBadgeConstantState != null) {
                drawable.setBadge(mBadgeConstantState.newDrawable());
            }
            drawable.mCreationFlags = mCreationFlags;
            return drawable;
        }

        @Override
        public int getChangingConfigurations() {
            return 0;
        }
    }
}
+369 −0

File added.

Preview size limit exceeded, changes collapsed.

+3 −3
Original line number Diff line number Diff line
@@ -42,7 +42,7 @@ public class PlaceHolderIconDrawable extends FastBitmapDrawable {
    public PlaceHolderIconDrawable(BitmapInfo info, Context context) {
        super(info);
        mProgressPath = getDefaultPath();
        mPaint.setColor(ColorUtils.compositeColors(
        paint.setColor(ColorUtils.compositeColors(
                GraphicsUtils.getAttrColor(context, R.attr.loadingIconColor), info.color));
    }

@@ -62,13 +62,13 @@ public class PlaceHolderIconDrawable extends FastBitmapDrawable {
        int saveCount = canvas.save();
        canvas.translate(bounds.left, bounds.top);
        canvas.scale(bounds.width() / 100f, bounds.height() / 100f);
        canvas.drawPath(mProgressPath, mPaint);
        canvas.drawPath(mProgressPath, paint);
        canvas.restoreToCount(saveCount);
    }

    /** Updates this placeholder to {@code newIcon} with animation. */
    public void animateIconUpdate(Drawable newIcon) {
        int placeholderColor = mPaint.getColor();
        int placeholderColor = paint.getColor();
        int originalAlpha = Color.alpha(placeholderColor);

        ValueAnimator iconUpdateAnimation = ValueAnimator.ofInt(originalAlpha, 0);
Loading