blob: e652e17bceb352af6a55abf9d09e97a15b0fb6d2 [file] [log] [blame]
Hyunyoung Song547010f2017-03-13 01:23:15 -07001/*
2 * Copyright (C) 2017 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 */
16package android.util;
17
Hyunyoung Song3ba5d562019-05-03 21:57:51 -070018import android.app.ActivityThread;
Hyunyoung Song547010f2017-03-13 01:23:15 -070019import android.content.Context;
20import android.content.res.Resources;
21import android.graphics.Bitmap;
22import android.graphics.Canvas;
23import android.graphics.Color;
Hyunyoung Song547010f2017-03-13 01:23:15 -070024import android.graphics.Paint;
Sunny Goyalbab30752017-04-12 15:36:42 -070025import android.graphics.Rect;
Hyunyoung Song547010f2017-03-13 01:23:15 -070026import android.graphics.drawable.AdaptiveIconDrawable;
Hyunyoung Song547010f2017-03-13 01:23:15 -070027import android.graphics.drawable.Drawable;
Sunny Goyalbab30752017-04-12 15:36:42 -070028import android.graphics.drawable.DrawableWrapper;
29import android.graphics.drawable.LayerDrawable;
Hyunyoung Song547010f2017-03-13 01:23:15 -070030
31/**
32 * Utility class to handle icon treatments (e.g., shadow generation) for the Launcher icons.
33 * @hide
34 */
35public final class LauncherIcons {
36
Sunny Goyalbab30752017-04-12 15:36:42 -070037 // Percent of actual icon size
38 private static final float ICON_SIZE_BLUR_FACTOR = 0.5f/48;
39 // Percent of actual icon size
40 private static final float ICON_SIZE_KEY_SHADOW_DELTA_FACTOR = 1f/48;
Hyunyoung Song547010f2017-03-13 01:23:15 -070041
42 private static final int KEY_SHADOW_ALPHA = 61;
43 private static final int AMBIENT_SHADOW_ALPHA = 30;
Sunny Goyalbab30752017-04-12 15:36:42 -070044
45 private final SparseArray<Bitmap> mShadowCache = new SparseArray<>();
46 private final int mIconSize;
47 private final Resources mRes;
Hyunyoung Song547010f2017-03-13 01:23:15 -070048
49 public LauncherIcons(Context context) {
50 mRes = context.getResources();
Sunny Goyalbab30752017-04-12 15:36:42 -070051 mIconSize = mRes.getDimensionPixelSize(android.R.dimen.app_icon_size);
Hyunyoung Song547010f2017-03-13 01:23:15 -070052 }
53
54 public Drawable wrapIconDrawableWithShadow(Drawable drawable) {
55 if (!(drawable instanceof AdaptiveIconDrawable)) {
56 return drawable;
57 }
Sunny Goyalbab30752017-04-12 15:36:42 -070058 Bitmap shadow = getShadowBitmap((AdaptiveIconDrawable) drawable);
59 return new ShadowDrawable(shadow, drawable);
Hyunyoung Song547010f2017-03-13 01:23:15 -070060 }
61
62 private Bitmap getShadowBitmap(AdaptiveIconDrawable d) {
Sunny Goyalbab30752017-04-12 15:36:42 -070063 int shadowSize = Math.max(mIconSize, d.getIntrinsicHeight());
64 synchronized (mShadowCache) {
65 Bitmap shadow = mShadowCache.get(shadowSize);
66 if (shadow != null) {
67 return shadow;
68 }
Hyunyoung Song547010f2017-03-13 01:23:15 -070069 }
70
Sunny Goyalbab30752017-04-12 15:36:42 -070071 d.setBounds(0, 0, shadowSize, shadowSize);
Hyunyoung Song547010f2017-03-13 01:23:15 -070072
Sunny Goyalbab30752017-04-12 15:36:42 -070073 float blur = ICON_SIZE_BLUR_FACTOR * shadowSize;
74 float keyShadowDistance = ICON_SIZE_KEY_SHADOW_DELTA_FACTOR * shadowSize;
75
76 int bitmapSize = (int) (shadowSize + 2 * blur + keyShadowDistance);
77 Bitmap shadow = Bitmap.createBitmap(bitmapSize, bitmapSize, Bitmap.Config.ARGB_8888);
78
79 Canvas canvas = new Canvas(shadow);
80 canvas.translate(blur + keyShadowDistance / 2, blur);
81
82 Paint paint = new Paint(Paint.ANTI_ALIAS_FLAG);
83 paint.setColor(Color.TRANSPARENT);
Hyunyoung Song547010f2017-03-13 01:23:15 -070084
85 // Draw ambient shadow
Sunny Goyalbab30752017-04-12 15:36:42 -070086 paint.setShadowLayer(blur, 0, 0, AMBIENT_SHADOW_ALPHA << 24);
87 canvas.drawPath(d.getIconMask(), paint);
Hyunyoung Song547010f2017-03-13 01:23:15 -070088
Sunny Goyalbab30752017-04-12 15:36:42 -070089 // Draw key shadow
90 canvas.translate(0, keyShadowDistance);
91 paint.setShadowLayer(blur, 0, 0, KEY_SHADOW_ALPHA << 24);
92 canvas.drawPath(d.getIconMask(), paint);
93
94 canvas.setBitmap(null);
95 synchronized (mShadowCache) {
96 mShadowCache.put(shadowSize, shadow);
97 }
98 return shadow;
99 }
100
101 public Drawable getBadgeDrawable(int foregroundRes, int backgroundColor) {
102 return getBadgedDrawable(null, foregroundRes, backgroundColor);
103 }
104
105 public Drawable getBadgedDrawable(Drawable base, int foregroundRes, int backgroundColor) {
Hyunyoung Song3ba5d562019-05-03 21:57:51 -0700106 Resources overlayableRes =
107 ActivityThread.currentActivityThread().getApplication().getResources();
Sunny Goyalbab30752017-04-12 15:36:42 -0700108
Bookatz029832a2019-10-04 16:50:22 -0700109 // ic_corp_icon_badge_shadow is not work-profile-specific.
Hyunyoung Song3ba5d562019-05-03 21:57:51 -0700110 Drawable badgeShadow = overlayableRes.getDrawable(
Sunny Goyalbab30752017-04-12 15:36:42 -0700111 com.android.internal.R.drawable.ic_corp_icon_badge_shadow);
112
Bookatz029832a2019-10-04 16:50:22 -0700113 // ic_corp_icon_badge_color is not work-profile-specific.
Hyunyoung Song3ba5d562019-05-03 21:57:51 -0700114 Drawable badgeColor = overlayableRes.getDrawable(
Sunny Goyalbab30752017-04-12 15:36:42 -0700115 com.android.internal.R.drawable.ic_corp_icon_badge_color)
116 .getConstantState().newDrawable().mutate();
Sunny Goyalbab30752017-04-12 15:36:42 -0700117
Hyunyoung Song3ba5d562019-05-03 21:57:51 -0700118 Drawable badgeForeground = overlayableRes.getDrawable(foregroundRes);
Tony Make21a1d02018-02-16 11:46:58 +0000119 badgeForeground.setTint(backgroundColor);
Sunny Goyalbab30752017-04-12 15:36:42 -0700120
121 Drawable[] drawables = base == null
122 ? new Drawable[] {badgeShadow, badgeColor, badgeForeground }
123 : new Drawable[] {base, badgeShadow, badgeColor, badgeForeground };
124 return new LayerDrawable(drawables);
125 }
126
127 /**
128 * A drawable which draws a shadow bitmap behind a drawable
129 */
130 private static class ShadowDrawable extends DrawableWrapper {
131
132 final MyConstantState mState;
133
134 public ShadowDrawable(Bitmap shadow, Drawable dr) {
135 super(dr);
136 mState = new MyConstantState(shadow, dr.getConstantState());
137 }
138
139 ShadowDrawable(MyConstantState state) {
140 super(state.mChildState.newDrawable());
141 mState = state;
142 }
143
144 @Override
145 public ConstantState getConstantState() {
146 return mState;
147 }
148
149 @Override
150 public void draw(Canvas canvas) {
151 Rect bounds = getBounds();
152 canvas.drawBitmap(mState.mShadow, null, bounds, mState.mPaint);
153 canvas.save();
154 // Ratio of child drawable size to shadow bitmap size
155 float factor = 1 / (1 + 2 * ICON_SIZE_BLUR_FACTOR + ICON_SIZE_KEY_SHADOW_DELTA_FACTOR);
156
157 canvas.translate(
158 bounds.width() * factor *
159 (ICON_SIZE_BLUR_FACTOR + ICON_SIZE_KEY_SHADOW_DELTA_FACTOR / 2),
160 bounds.height() * factor * ICON_SIZE_BLUR_FACTOR);
161 canvas.scale(factor, factor);
162 super.draw(canvas);
163 canvas.restore();
164 }
165
166 private static class MyConstantState extends ConstantState {
167
168 final Paint mPaint = new Paint(Paint.FILTER_BITMAP_FLAG);
169 final Bitmap mShadow;
170 final ConstantState mChildState;
171
172 MyConstantState(Bitmap shadow, ConstantState childState) {
173 mShadow = shadow;
174 mChildState = childState;
175 }
176
177 @Override
178 public Drawable newDrawable() {
179 return new ShadowDrawable(this);
180 }
181
182 @Override
183 public int getChangingConfigurations() {
184 return mChildState.getChangingConfigurations();
185 }
186 }
Hyunyoung Song547010f2017-03-13 01:23:15 -0700187 }
188}