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

Commit 86da9b28 authored by Android Build Coastguard Worker's avatar Android Build Coastguard Worker
Browse files

Snap for 13580881 from 5cc433e3 to 25Q3-release

Change-Id: Ie994e37438a09df225ae23ad7d082df0201b0841
parents 6bd24639 5cc433e3
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