• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 package com.android.launcher3.icons;
2 
3 import android.content.Context;
4 import android.content.Intent;
5 import android.content.pm.LauncherApps;
6 import android.content.pm.ShortcutInfo;
7 import android.graphics.Bitmap;
8 import android.graphics.Canvas;
9 import android.graphics.Path;
10 import android.graphics.Rect;
11 import android.graphics.drawable.AdaptiveIconDrawable;
12 import android.graphics.drawable.Drawable;
13 import android.graphics.drawable.Icon;
14 import android.os.Build;
15 
16 import androidx.annotation.NonNull;
17 import androidx.annotation.Nullable;
18 
19 /**
20  * Factory for creating normalized bubble icons and app badges.
21  */
22 public class BubbleIconFactory extends BaseIconFactory {
23 
24     private final int mRingColor;
25     private final int mRingWidth;
26 
27     private final BaseIconFactory mBadgeFactory;
28 
29     /**
30      * Creates a bubble icon factory.
31      *
32      * @param context the context for the factory.
33      * @param iconSize the size of the bubble icon (i.e. the large icon for the bubble).
34      * @param badgeSize the size of the badge (i.e. smaller icon shown on top of the large icon).
35      * @param ringColor the color of the ring optionally shown around the badge.
36      * @param ringWidth the width of the ring optionally shown around the badge.
37      */
BubbleIconFactory(Context context, int iconSize, int badgeSize, int ringColor, int ringWidth)38     public BubbleIconFactory(Context context, int iconSize, int badgeSize, int ringColor,
39             int ringWidth) {
40         super(context, context.getResources().getConfiguration().densityDpi, iconSize);
41         mRingColor = ringColor;
42         mRingWidth = ringWidth;
43 
44         mBadgeFactory = new BaseIconFactory(context,
45                 context.getResources().getConfiguration().densityDpi,
46                 badgeSize);
47     }
48 
49     /**
50      * Returns the drawable that the developer has provided to display in the bubble.
51      */
getBubbleDrawable(@onNull final Context context, @Nullable final ShortcutInfo shortcutInfo, @Nullable final Icon ic)52     public Drawable getBubbleDrawable(@NonNull final Context context,
53             @Nullable final ShortcutInfo shortcutInfo, @Nullable final Icon ic) {
54         if (shortcutInfo != null) {
55             LauncherApps launcherApps = context.getSystemService(LauncherApps.class);
56             int density = context.getResources().getConfiguration().densityDpi;
57             return launcherApps.getShortcutIconDrawable(shortcutInfo, density);
58         } else {
59             if (ic != null && Build.VERSION.SDK_INT >= Build.VERSION_CODES.P) {
60                 if (ic.getType() == Icon.TYPE_URI
61                         || ic.getType() == Icon.TYPE_URI_ADAPTIVE_BITMAP) {
62                     context.grantUriPermission(context.getPackageName(),
63                             ic.getUri(),
64                             Intent.FLAG_GRANT_READ_URI_PERMISSION);
65                 }
66                 return ic.loadDrawable(context);
67             }
68             return null;
69         }
70     }
71 
72     /**
73      * Creates the bitmap for the provided drawable and returns the scale used for
74      * drawing the actual drawable. This is used for the larger icon shown for the bubble.
75      */
getBubbleBitmap(@onNull Drawable icon, float[] outScale)76     public Bitmap getBubbleBitmap(@NonNull Drawable icon, float[] outScale) {
77         if (outScale == null) {
78             outScale = new float[1];
79         }
80         icon = normalizeAndWrapToAdaptiveIcon(icon, outScale);
81         return createIconBitmap(icon, outScale[0], MODE_WITH_SHADOW);
82     }
83 
84     /**
85      * Returns a {@link BitmapInfo} for the app-badge that is shown on top of each bubble. This
86      * will include the workprofile indicator on the badge if appropriate.
87      */
getBadgeBitmap(Drawable userBadgedAppIcon, boolean isImportantConversation)88     public BitmapInfo getBadgeBitmap(Drawable userBadgedAppIcon, boolean isImportantConversation) {
89         if (userBadgedAppIcon instanceof AdaptiveIconDrawable) {
90             AdaptiveIconDrawable ad = (AdaptiveIconDrawable) userBadgedAppIcon;
91             userBadgedAppIcon = new CircularAdaptiveIcon(ad.getBackground(),
92                     ad.getForeground());
93         }
94         if (isImportantConversation) {
95             userBadgedAppIcon = new CircularRingDrawable(userBadgedAppIcon);
96         }
97         Bitmap userBadgedBitmap = mBadgeFactory.createIconBitmap(
98                 userBadgedAppIcon, 1, MODE_WITH_SHADOW);
99         return mBadgeFactory.createIconBitmap(userBadgedBitmap);
100     }
101 
102     private class CircularRingDrawable extends CircularAdaptiveIcon {
103         final Rect mInnerBounds = new Rect();
104 
105         final Drawable mDr;
106 
CircularRingDrawable(Drawable dr)107         CircularRingDrawable(Drawable dr) {
108             super(null, null);
109             mDr = dr;
110         }
111 
112         @Override
draw(Canvas canvas)113         public void draw(Canvas canvas) {
114             int save = canvas.save();
115             canvas.clipPath(getIconMask());
116             canvas.drawColor(mRingColor);
117             mInnerBounds.set(getBounds());
118             mInnerBounds.inset(mRingWidth, mRingWidth);
119             canvas.translate(mInnerBounds.left, mInnerBounds.top);
120             mDr.setBounds(0, 0, mInnerBounds.width(), mInnerBounds.height());
121             mDr.draw(canvas);
122             canvas.restoreToCount(save);
123         }
124     }
125 
126     private static class CircularAdaptiveIcon extends AdaptiveIconDrawable {
127 
128         final Path mPath = new Path();
129 
CircularAdaptiveIcon(Drawable bg, Drawable fg)130         CircularAdaptiveIcon(Drawable bg, Drawable fg) {
131             super(bg, fg);
132         }
133 
134         @Override
getIconMask()135         public Path getIconMask() {
136             mPath.reset();
137             Rect bounds = getBounds();
138             mPath.addOval(bounds.left, bounds.top, bounds.right, bounds.bottom, Path.Direction.CW);
139             return mPath;
140         }
141 
142         @Override
draw(Canvas canvas)143         public void draw(Canvas canvas) {
144             int save = canvas.save();
145             canvas.clipPath(getIconMask());
146 
147             Drawable d;
148             if ((d = getBackground()) != null) {
149                 d.draw(canvas);
150             }
151             if ((d = getForeground()) != null) {
152                 d.draw(canvas);
153             }
154             canvas.restoreToCount(save);
155         }
156     }
157 }
158