• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1  /*
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   */
16  package android.util;
17  
18  import android.app.ActivityThread;
19  import android.content.Context;
20  import android.content.res.Resources;
21  import android.graphics.Bitmap;
22  import android.graphics.Canvas;
23  import android.graphics.Color;
24  import android.graphics.Paint;
25  import android.graphics.Rect;
26  import android.graphics.drawable.AdaptiveIconDrawable;
27  import android.graphics.drawable.Drawable;
28  import android.graphics.drawable.DrawableWrapper;
29  import android.graphics.drawable.LayerDrawable;
30  
31  /**
32   * Utility class to handle icon treatments (e.g., shadow generation) for the Launcher icons.
33   * @hide
34   */
35  public final class LauncherIcons {
36  
37      // 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;
41  
42      private static final int KEY_SHADOW_ALPHA = 61;
43      private static final int AMBIENT_SHADOW_ALPHA = 30;
44  
45      private final SparseArray<Bitmap> mShadowCache = new SparseArray<>();
46      private final int mIconSize;
47      private final Resources mRes;
48      private final Context mContext;
49  
LauncherIcons(Context context)50      public LauncherIcons(Context context) {
51          mRes = context.getResources();
52          mIconSize = mRes.getDimensionPixelSize(android.R.dimen.app_icon_size);
53          mContext = context;
54      }
55  
wrapIconDrawableWithShadow(Drawable drawable)56      public Drawable wrapIconDrawableWithShadow(Drawable drawable) {
57          if (!(drawable instanceof AdaptiveIconDrawable)) {
58              return drawable;
59          }
60          Bitmap shadow = getShadowBitmap((AdaptiveIconDrawable) drawable);
61          return new ShadowDrawable(shadow, drawable);
62      }
63  
getShadowBitmap(AdaptiveIconDrawable d)64      private Bitmap getShadowBitmap(AdaptiveIconDrawable d) {
65          int shadowSize = Math.max(mIconSize, d.getIntrinsicHeight());
66          synchronized (mShadowCache) {
67              Bitmap shadow = mShadowCache.get(shadowSize);
68              if (shadow != null) {
69                  return shadow;
70              }
71          }
72  
73          d.setBounds(0, 0, shadowSize, shadowSize);
74  
75          float blur = ICON_SIZE_BLUR_FACTOR * shadowSize;
76          float keyShadowDistance = ICON_SIZE_KEY_SHADOW_DELTA_FACTOR * shadowSize;
77  
78          int bitmapSize = (int) (shadowSize + 2 * blur + keyShadowDistance);
79          Bitmap shadow = Bitmap.createBitmap(bitmapSize, bitmapSize, Bitmap.Config.ARGB_8888);
80  
81          Canvas canvas = new Canvas(shadow);
82          canvas.translate(blur + keyShadowDistance / 2, blur);
83  
84          Paint paint = new Paint(Paint.ANTI_ALIAS_FLAG);
85          paint.setColor(Color.TRANSPARENT);
86  
87          // Draw ambient shadow
88          paint.setShadowLayer(blur, 0, 0, AMBIENT_SHADOW_ALPHA << 24);
89          canvas.drawPath(d.getIconMask(), paint);
90  
91          // Draw key shadow
92          canvas.translate(0, keyShadowDistance);
93          paint.setShadowLayer(blur, 0, 0, KEY_SHADOW_ALPHA << 24);
94          canvas.drawPath(d.getIconMask(), paint);
95  
96          canvas.setBitmap(null);
97          synchronized (mShadowCache) {
98              mShadowCache.put(shadowSize, shadow);
99          }
100          return shadow;
101      }
102  
getBadgeDrawable(Drawable badgeForeground, int backgroundColor)103      public Drawable getBadgeDrawable(Drawable badgeForeground, int backgroundColor) {
104          return getBadgedDrawable(null, badgeForeground, backgroundColor);
105      }
106  
getBadgedDrawable( Drawable base, Drawable badgeForeground, int backgroundColor)107      public Drawable getBadgedDrawable(
108              Drawable base, Drawable badgeForeground, int backgroundColor) {
109          Resources overlayableRes =
110                  ActivityThread.currentActivityThread().getApplication().getResources();
111          // ic_corp_icon_badge_shadow is not work-profile-specific.
112          Drawable badgeShadow = overlayableRes.getDrawable(
113                  com.android.internal.R.drawable.ic_corp_icon_badge_shadow);
114  
115          // ic_corp_icon_badge_color is not work-profile-specific.
116          Drawable badgeColor = overlayableRes.getDrawable(
117                  com.android.internal.R.drawable.ic_corp_icon_badge_color)
118                  .getConstantState().newDrawable().mutate();
119  
120          badgeForeground.setTint(backgroundColor);
121  
122          Drawable[] drawables = base == null
123                  ? new Drawable[] {badgeShadow, badgeColor, badgeForeground }
124                  : new Drawable[] {base, badgeShadow, badgeColor, badgeForeground };
125          return new LayerDrawable(drawables);
126      }
127  
128      /**
129       * A drawable which draws a shadow bitmap behind a drawable
130       */
131      private static class ShadowDrawable extends DrawableWrapper {
132  
133          final MyConstantState mState;
134  
ShadowDrawable(Bitmap shadow, Drawable dr)135          public ShadowDrawable(Bitmap shadow, Drawable dr) {
136              super(dr);
137              mState = new MyConstantState(shadow, dr.getConstantState());
138          }
139  
ShadowDrawable(MyConstantState state)140          ShadowDrawable(MyConstantState state) {
141              super(state.mChildState.newDrawable());
142              mState = state;
143          }
144  
145          @Override
getConstantState()146          public ConstantState getConstantState() {
147              return mState;
148          }
149  
150          @Override
draw(Canvas canvas)151          public void draw(Canvas canvas) {
152              Rect bounds = getBounds();
153              canvas.drawBitmap(mState.mShadow, null, bounds, mState.mPaint);
154              canvas.save();
155              // Ratio of child drawable size to shadow bitmap size
156              float factor = 1 / (1 + 2 * ICON_SIZE_BLUR_FACTOR + ICON_SIZE_KEY_SHADOW_DELTA_FACTOR);
157  
158              canvas.translate(
159                      bounds.width() * factor *
160                              (ICON_SIZE_BLUR_FACTOR + ICON_SIZE_KEY_SHADOW_DELTA_FACTOR / 2),
161                      bounds.height() * factor * ICON_SIZE_BLUR_FACTOR);
162              canvas.scale(factor, factor);
163              super.draw(canvas);
164              canvas.restore();
165          }
166  
167          private static class MyConstantState extends ConstantState {
168  
169              final Paint mPaint = new Paint(Paint.FILTER_BITMAP_FLAG);
170              final Bitmap mShadow;
171              final ConstantState mChildState;
172  
MyConstantState(Bitmap shadow, ConstantState childState)173              MyConstantState(Bitmap shadow, ConstantState childState) {
174                  mShadow = shadow;
175                  mChildState = childState;
176              }
177  
178              @Override
newDrawable()179              public Drawable newDrawable() {
180                  return new ShadowDrawable(this);
181              }
182  
183              @Override
getChangingConfigurations()184              public int getChangingConfigurations() {
185                  return mChildState.getChangingConfigurations();
186              }
187          }
188      }
189  }
190