• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 /*
2  * Copyright (C) 2023 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.content.Context;
20 import android.content.res.Resources;
21 import android.content.res.Resources.Theme;
22 import android.graphics.Bitmap;
23 import android.graphics.Canvas;
24 import android.graphics.Color;
25 import android.graphics.ColorFilter;
26 import android.graphics.ColorMatrix;
27 import android.graphics.ColorMatrixColorFilter;
28 import android.graphics.Matrix;
29 import android.graphics.Paint;
30 import android.graphics.Path;
31 import android.graphics.Rect;
32 import android.graphics.RectF;
33 import android.graphics.drawable.Drawable;
34 import android.graphics.drawable.DrawableWrapper;
35 
36 import androidx.annotation.ColorInt;
37 import androidx.annotation.NonNull;
38 import androidx.annotation.Nullable;
39 import androidx.annotation.VisibleForTesting;
40 import androidx.core.graphics.ColorUtils;
41 
42 /**
43  * A drawable used for drawing user badge. It draws a circle around the actual badge,
44  * and has support for theming.
45  */
46 public class UserBadgeDrawable extends DrawableWrapper {
47 
48     private static final float VIEWPORT_SIZE = 24;
49     private static final float CENTER = VIEWPORT_SIZE / 2;
50 
51     private static final float BG_RADIUS = 11;
52     private static final float SHADOW_RADIUS = 11.5f;
53     private static final float SHADOW_OFFSET_Y = 0.25f;
54 
55     @VisibleForTesting
56     static final int SHADOW_COLOR = 0x11000000;
57 
58     private final Paint mPaint = new Paint(Paint.ANTI_ALIAS_FLAG);
59 
60     private final int mBaseColor;
61     private final int mBgColor;
62     private boolean mShouldDrawBackground = true;
63     @Nullable private Path mShape;
64 
65     private Matrix mShapeMatrix = new Matrix();
66 
67     @VisibleForTesting
68     public final boolean mIsThemed;
69 
UserBadgeDrawable(Context context, int badgeRes, int colorRes, boolean isThemed, @Nullable Path shape)70     public UserBadgeDrawable(Context context, int badgeRes, int colorRes, boolean isThemed,
71             @Nullable Path shape) {
72         super(context.getDrawable(badgeRes));
73         mShape = shape;
74         mShapeMatrix = new Matrix();
75         if (mShape != null) {
76             mShapeMatrix.setRectToRect(new RectF(0f, 0f, 100f, 100f),
77                     new RectF(0f, 0f, CENTER * 2, CENTER * 2),
78                     Matrix.ScaleToFit.CENTER);
79             mShape.transform(mShapeMatrix);
80         }
81         mIsThemed = isThemed;
82         if (isThemed) {
83             mutate();
84             mBaseColor = context.getColor(R.color.themed_badge_icon_color);
85             mBgColor = context.getColor(R.color.themed_badge_icon_background_color);
86         } else {
87             mBaseColor = context.getColor(colorRes);
88             mBgColor = Color.WHITE;
89         }
90         setTint(mBaseColor);
91     }
92 
UserBadgeDrawable(Drawable base, int bgColor, int baseColor, boolean shouldDrawBackground)93     private UserBadgeDrawable(Drawable base, int bgColor, int baseColor,
94             boolean shouldDrawBackground) {
95         super(base);
96         mIsThemed = false;
97         mBgColor = bgColor;
98         mBaseColor = baseColor;
99         mShouldDrawBackground = shouldDrawBackground;
100     }
101 
102     @Override
draw(@onNull Canvas canvas)103     public void draw(@NonNull Canvas canvas) {
104         if (mShouldDrawBackground) {
105             Rect b = getBounds();
106             int saveCount = canvas.save();
107             canvas.translate(b.left, b.top);
108             canvas.scale(b.width() / VIEWPORT_SIZE, b.height() / VIEWPORT_SIZE);
109 
110             mPaint.setColor(blendDrawableAlpha(SHADOW_COLOR));
111             if (mShape != null) {
112                 canvas.drawPath(mShape, mPaint);
113             } else {
114                 canvas.drawCircle(CENTER, CENTER + SHADOW_OFFSET_Y, SHADOW_RADIUS, mPaint);
115             }
116             mPaint.setColor(blendDrawableAlpha(mBgColor));
117             if (mShape != null) {
118                 canvas.drawPath(mShape, mPaint);
119             } else {
120                 canvas.drawCircle(CENTER, CENTER, BG_RADIUS, mPaint);
121             }
122             canvas.restoreToCount(saveCount);
123         }
124         super.draw(canvas);
125     }
126 
blendDrawableAlpha(@olorInt int color)127     private @ColorInt int blendDrawableAlpha(@ColorInt int color) {
128         int alpha = (int) (Color.valueOf(color).alpha() * getAlpha());
129         return ColorUtils.setAlphaComponent(color, alpha);
130     }
131 
132     @Override
setColorFilter(@ullable ColorFilter filter)133     public void setColorFilter(@Nullable ColorFilter filter) {
134         if (filter == null) {
135             super.setTint(mBaseColor);
136         } else if (filter instanceof ColorMatrixColorFilter cf) {
137             ColorMatrix cm = new ColorMatrix();
138             cf.getColorMatrix(cm);
139 
140             ColorMatrix cm2 = new ColorMatrix();
141             float[] base = cm2.getArray();
142             base[0] = Color.red(mBaseColor) / 255f;
143             base[6] = Color.green(mBaseColor) / 255f;
144             base[12] = Color.blue(mBaseColor) / 255f;
145             base[18] = Color.alpha(mBaseColor) / 255f;
146             cm2.postConcat(cm);
147 
148             super.setColorFilter(new ColorMatrixColorFilter(cm2));
149         } else {
150             // fail safe
151             Paint p = new Paint();
152             p.setColorFilter(filter);
153             Bitmap b = Bitmap.createBitmap(1, 1, Bitmap.Config.ARGB_8888);
154             new Canvas(b).drawPaint(p);
155             super.setTint(b.getPixel(0, 0));
156         }
157     }
158 
setShouldDrawBackground(boolean shouldDrawBackground)159     public void setShouldDrawBackground(boolean shouldDrawBackground) {
160         mutate();
161         mShouldDrawBackground = shouldDrawBackground;
162     }
163 
164     @Override
getConstantState()165     public ConstantState getConstantState() {
166         return new MyConstantState(
167                 getDrawable().getConstantState(), mBgColor, mBaseColor, mShouldDrawBackground);
168     }
169 
170     private static class MyConstantState extends ConstantState {
171 
172         private final ConstantState mBase;
173         private final int mBgColor;
174         private final int mBaseColor;
175         private final boolean mShouldDrawBackground;
176 
MyConstantState(ConstantState base, int bgColor, int baseColor, boolean shouldDrawBackground)177         MyConstantState(ConstantState base, int bgColor, int baseColor,
178                 boolean shouldDrawBackground) {
179             mBase = base;
180             mBgColor = bgColor;
181             mBaseColor = baseColor;
182             mShouldDrawBackground = shouldDrawBackground;
183         }
184 
185         @Override
getChangingConfigurations()186         public int getChangingConfigurations() {
187             return mBase.getChangingConfigurations();
188         }
189 
190         @Override
191         @NonNull
newDrawable()192         public Drawable newDrawable() {
193             return new UserBadgeDrawable(
194                     mBase.newDrawable(), mBgColor, mBaseColor, mShouldDrawBackground);
195         }
196 
197         @Override
198         @NonNull
newDrawable(Resources res)199         public Drawable newDrawable(Resources res) {
200             return new UserBadgeDrawable(
201                     mBase.newDrawable(res), mBgColor, mBaseColor, mShouldDrawBackground);
202         }
203 
204         @Override
205         @NonNull
newDrawable(Resources res, Theme theme)206         public Drawable newDrawable(Resources res, Theme theme) {
207             return new UserBadgeDrawable(
208                     mBase.newDrawable(res, theme), mBgColor, mBaseColor, mShouldDrawBackground);
209         }
210     }
211 }
212