• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 /*
2  * Copyright (C) 2008 The Android Open Source Project
3  *
4  * Licensed under the Apache License, Version 2.0 (the "License");
5  * you may not use this file except in compliance with the License.
6  * You may obtain a copy of the License at
7  *
8  *      http://www.apache.org/licenses/LICENSE-2.0
9  *
10  * Unless required by applicable law or agreed to in writing, software
11  * distributed under the License is distributed on an "AS IS" BASIS,
12  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13  * See the License for the specific language governing permissions and
14  * limitations under the License.
15  */
16 
17 package com.android.launcher3.icons;
18 
19 import android.animation.ObjectAnimator;
20 import android.graphics.Bitmap;
21 import android.graphics.Canvas;
22 import android.graphics.Color;
23 import android.graphics.ColorFilter;
24 import android.graphics.ColorMatrix;
25 import android.graphics.ColorMatrixColorFilter;
26 import android.graphics.Paint;
27 import android.graphics.PixelFormat;
28 import android.graphics.Rect;
29 import android.graphics.drawable.Drawable;
30 import android.util.Property;
31 import android.view.animation.AccelerateInterpolator;
32 import android.view.animation.DecelerateInterpolator;
33 import android.view.animation.Interpolator;
34 
35 import androidx.annotation.Nullable;
36 
37 public class FastBitmapDrawable extends Drawable {
38 
39     private static final Interpolator ACCEL = new AccelerateInterpolator();
40     private static final Interpolator DEACCEL = new DecelerateInterpolator();
41 
42     private static final float PRESSED_SCALE = 1.1f;
43 
44     private static final float DISABLED_DESATURATION = 1f;
45     private static final float DISABLED_BRIGHTNESS = 0.5f;
46 
47     public static final int CLICK_FEEDBACK_DURATION = 200;
48 
49     private static ColorFilter sDisabledFColorFilter;
50 
51     protected final Paint mPaint = new Paint(Paint.FILTER_BITMAP_FLAG | Paint.ANTI_ALIAS_FLAG);
52     protected Bitmap mBitmap;
53     protected final int mIconColor;
54 
55     @Nullable private ColorFilter mColorFilter;
56 
57     private boolean mIsPressed;
58     protected boolean mIsDisabled;
59     float mDisabledAlpha = 1f;
60 
61     // Animator and properties for the fast bitmap drawable's scale
62     private static final Property<FastBitmapDrawable, Float> SCALE
63             = new Property<FastBitmapDrawable, Float>(Float.TYPE, "scale") {
64         @Override
65         public Float get(FastBitmapDrawable fastBitmapDrawable) {
66             return fastBitmapDrawable.mScale;
67         }
68 
69         @Override
70         public void set(FastBitmapDrawable fastBitmapDrawable, Float value) {
71             fastBitmapDrawable.mScale = value;
72             fastBitmapDrawable.invalidateSelf();
73         }
74     };
75     private ObjectAnimator mScaleAnimation;
76     private float mScale = 1;
77 
78     private int mAlpha = 255;
79 
FastBitmapDrawable(Bitmap b)80     public FastBitmapDrawable(Bitmap b) {
81         this(b, Color.TRANSPARENT);
82     }
83 
FastBitmapDrawable(BitmapInfo info)84     public FastBitmapDrawable(BitmapInfo info) {
85         this(info.icon, info.color);
86     }
87 
FastBitmapDrawable(Bitmap b, int iconColor)88     protected FastBitmapDrawable(Bitmap b, int iconColor) {
89         this(b, iconColor, false);
90     }
91 
FastBitmapDrawable(Bitmap b, int iconColor, boolean isDisabled)92     protected FastBitmapDrawable(Bitmap b, int iconColor, boolean isDisabled) {
93         mBitmap = b;
94         mIconColor = iconColor;
95         setFilterBitmap(true);
96         setIsDisabled(isDisabled);
97     }
98 
99     @Override
draw(Canvas canvas)100     public final void draw(Canvas canvas) {
101         if (mScale != 1f) {
102             int count = canvas.save();
103             Rect bounds = getBounds();
104             canvas.scale(mScale, mScale, bounds.exactCenterX(), bounds.exactCenterY());
105             drawInternal(canvas, bounds);
106             canvas.restoreToCount(count);
107         } else {
108             drawInternal(canvas, getBounds());
109         }
110     }
111 
drawInternal(Canvas canvas, Rect bounds)112     protected void drawInternal(Canvas canvas, Rect bounds) {
113         canvas.drawBitmap(mBitmap, null, bounds, mPaint);
114     }
115 
116     /**
117      * Returns the primary icon color
118      */
getIconColor()119     public int getIconColor() {
120         return mIconColor;
121     }
122 
123     /**
124      * Returns if this represents a themed icon
125      */
isThemed()126     public boolean isThemed() {
127         return false;
128     }
129 
130     @Override
setColorFilter(ColorFilter cf)131     public void setColorFilter(ColorFilter cf) {
132         mColorFilter = cf;
133         updateFilter();
134     }
135 
136     @Override
getOpacity()137     public int getOpacity() {
138         return PixelFormat.TRANSLUCENT;
139     }
140 
141     @Override
setAlpha(int alpha)142     public void setAlpha(int alpha) {
143         if (mAlpha != alpha) {
144             mAlpha = alpha;
145             mPaint.setAlpha(alpha);
146             invalidateSelf();
147         }
148     }
149 
150     @Override
setFilterBitmap(boolean filterBitmap)151     public void setFilterBitmap(boolean filterBitmap) {
152         mPaint.setFilterBitmap(filterBitmap);
153         mPaint.setAntiAlias(filterBitmap);
154     }
155 
156     @Override
getAlpha()157     public int getAlpha() {
158         return mAlpha;
159     }
160 
resetScale()161     public void resetScale() {
162         if (mScaleAnimation != null) {
163             mScaleAnimation.cancel();
164             mScaleAnimation = null;
165         }
166         mScale = 1;
167         invalidateSelf();
168     }
169 
getAnimatedScale()170     public float getAnimatedScale() {
171         return mScaleAnimation == null ? 1 : mScale;
172     }
173 
174     @Override
getIntrinsicWidth()175     public int getIntrinsicWidth() {
176         return mBitmap.getWidth();
177     }
178 
179     @Override
getIntrinsicHeight()180     public int getIntrinsicHeight() {
181         return mBitmap.getHeight();
182     }
183 
184     @Override
getMinimumWidth()185     public int getMinimumWidth() {
186         return getBounds().width();
187     }
188 
189     @Override
getMinimumHeight()190     public int getMinimumHeight() {
191         return getBounds().height();
192     }
193 
194     @Override
isStateful()195     public boolean isStateful() {
196         return true;
197     }
198 
199     @Override
getColorFilter()200     public ColorFilter getColorFilter() {
201         return mPaint.getColorFilter();
202     }
203 
204     @Override
onStateChange(int[] state)205     protected boolean onStateChange(int[] state) {
206         boolean isPressed = false;
207         for (int s : state) {
208             if (s == android.R.attr.state_pressed) {
209                 isPressed = true;
210                 break;
211             }
212         }
213         if (mIsPressed != isPressed) {
214             mIsPressed = isPressed;
215 
216             if (mScaleAnimation != null) {
217                 mScaleAnimation.cancel();
218                 mScaleAnimation = null;
219             }
220 
221             if (mIsPressed) {
222                 // Animate when going to pressed state
223                 mScaleAnimation = ObjectAnimator.ofFloat(this, SCALE, PRESSED_SCALE);
224                 mScaleAnimation.setDuration(CLICK_FEEDBACK_DURATION);
225                 mScaleAnimation.setInterpolator(ACCEL);
226                 mScaleAnimation.start();
227             } else {
228                 if (isVisible()) {
229                     mScaleAnimation = ObjectAnimator.ofFloat(this, SCALE, 1f);
230                     mScaleAnimation.setDuration(CLICK_FEEDBACK_DURATION);
231                     mScaleAnimation.setInterpolator(DEACCEL);
232                     mScaleAnimation.start();
233                 } else {
234                     mScale = 1f;
235                     invalidateSelf();
236                 }
237             }
238             return true;
239         }
240         return false;
241     }
242 
setIsDisabled(boolean isDisabled)243     public void setIsDisabled(boolean isDisabled) {
244         if (mIsDisabled != isDisabled) {
245             mIsDisabled = isDisabled;
246             updateFilter();
247         }
248     }
249 
isDisabled()250     protected boolean isDisabled() {
251         return mIsDisabled;
252     }
253 
getDisabledColorFilter()254     private ColorFilter getDisabledColorFilter() {
255         if (sDisabledFColorFilter == null) {
256             sDisabledFColorFilter = getDisabledFColorFilter(mDisabledAlpha);
257         }
258         return sDisabledFColorFilter;
259     }
260 
261     /**
262      * Updates the paint to reflect the current brightness and saturation.
263      */
updateFilter()264     protected void updateFilter() {
265         mPaint.setColorFilter(mIsDisabled ? getDisabledColorFilter() : mColorFilter);
266         invalidateSelf();
267     }
268 
269     @Override
getConstantState()270     public ConstantState getConstantState() {
271         return new FastBitmapConstantState(mBitmap, mIconColor, mIsDisabled);
272     }
273 
getDisabledFColorFilter(float disabledAlpha)274     public static ColorFilter getDisabledFColorFilter(float disabledAlpha) {
275         ColorMatrix tempBrightnessMatrix = new ColorMatrix();
276         ColorMatrix tempFilterMatrix = new ColorMatrix();
277 
278         tempFilterMatrix.setSaturation(1f - DISABLED_DESATURATION);
279         float scale = 1 - DISABLED_BRIGHTNESS;
280         int brightnessI =   (int) (255 * DISABLED_BRIGHTNESS);
281         float[] mat = tempBrightnessMatrix.getArray();
282         mat[0] = scale;
283         mat[6] = scale;
284         mat[12] = scale;
285         mat[4] = brightnessI;
286         mat[9] = brightnessI;
287         mat[14] = brightnessI;
288         mat[18] = disabledAlpha;
289         tempFilterMatrix.preConcat(tempBrightnessMatrix);
290         return new ColorMatrixColorFilter(tempBrightnessMatrix);
291     }
292 
293     protected static class FastBitmapConstantState extends ConstantState {
294         protected final Bitmap mBitmap;
295         protected final int mIconColor;
296         protected final boolean mIsDisabled;
297 
FastBitmapConstantState(Bitmap bitmap, int color, boolean isDisabled)298         public FastBitmapConstantState(Bitmap bitmap, int color, boolean isDisabled) {
299             mBitmap = bitmap;
300             mIconColor = color;
301             mIsDisabled = isDisabled;
302         }
303 
304         @Override
newDrawable()305         public FastBitmapDrawable newDrawable() {
306             return new FastBitmapDrawable(mBitmap, mIconColor, mIsDisabled);
307         }
308 
309         @Override
getChangingConfigurations()310         public int getChangingConfigurations() {
311             return 0;
312         }
313     }
314 }
315