1 /*
2  * Copyright (C) 2016 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.graphics;
18 
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.Path;
25 import android.graphics.Rect;
26 import android.graphics.drawable.BitmapDrawable;
27 import android.graphics.drawable.Drawable;
28 import android.os.Process;
29 import android.os.UserHandle;
30 import android.support.annotation.UiThread;
31 import android.util.Log;
32 
33 import com.android.launcher3.FastBitmapDrawable;
34 import com.android.launcher3.ItemInfo;
35 import com.android.launcher3.R;
36 import com.android.launcher3.Utilities;
37 import com.android.launcher3.allapps.AllAppsBackgroundDrawable;
38 
39 import java.util.HashMap;
40 
41 /**
42  * Factory for creating new drawables.
43  */
44 public class DrawableFactory {
45 
46     private static final String TAG = "DrawableFactory";
47 
48     private static DrawableFactory sInstance;
49     private static final Object LOCK = new Object();
50 
51     private Path mPreloadProgressPath;
52 
get(Context context)53     public static DrawableFactory get(Context context) {
54         synchronized (LOCK) {
55             if (sInstance == null) {
56                 sInstance = Utilities.getOverrideObject(DrawableFactory.class,
57                         context.getApplicationContext(), R.string.drawable_factory_class);
58             }
59             return sInstance;
60         }
61     }
62 
63     protected final UserHandle mMyUser = Process.myUserHandle();
64     protected final HashMap<UserHandle, Bitmap> mUserBadges = new HashMap<>();
65 
66     /**
67      * Returns a FastBitmapDrawable with the icon.
68      */
newIcon(Bitmap icon, ItemInfo info)69     public FastBitmapDrawable newIcon(Bitmap icon, ItemInfo info) {
70         return new FastBitmapDrawable(icon);
71     }
72 
73     /**
74      * Returns a FastBitmapDrawable with the icon.
75      */
newPendingIcon(Bitmap icon, Context context)76     public PreloadIconDrawable newPendingIcon(Bitmap icon, Context context) {
77         if (mPreloadProgressPath == null) {
78             mPreloadProgressPath = getPreloadProgressPath(context);
79         }
80         return new PreloadIconDrawable(icon, mPreloadProgressPath, context);
81     }
82 
83 
getPreloadProgressPath(Context context)84     protected Path getPreloadProgressPath(Context context) {
85         if (Utilities.isAtLeastO()) {
86             try {
87                 // Try to load the path from Mask Icon
88                 Drawable icon = context.getDrawable(R.drawable.adaptive_icon_drawable_wrapper);
89                 icon.setBounds(0, 0,
90                         PreloadIconDrawable.PATH_SIZE, PreloadIconDrawable.PATH_SIZE);
91                 return (Path) icon.getClass().getMethod("getIconMask").invoke(icon);
92             } catch (Exception e) {
93                 Log.e(TAG, "Error loading mask icon", e);
94             }
95         }
96 
97         // Create a circle static from top center and going clockwise.
98         Path p = new Path();
99         p.moveTo(PreloadIconDrawable.PATH_SIZE / 2, 0);
100         p.addArc(0, 0, PreloadIconDrawable.PATH_SIZE, PreloadIconDrawable.PATH_SIZE, -90, 360);
101         return p;
102     }
103 
getAllAppsBackground(Context context)104     public AllAppsBackgroundDrawable getAllAppsBackground(Context context) {
105         return new AllAppsBackgroundDrawable(context);
106     }
107 
108     /**
109      * Returns a drawable that can be used as a badge for the user or null.
110      */
111     @UiThread
getBadgeForUser(UserHandle user, Context context)112     public Drawable getBadgeForUser(UserHandle user, Context context) {
113         if (mMyUser.equals(user)) {
114             return null;
115         }
116 
117         Bitmap badgeBitmap = getUserBadge(user, context);
118         FastBitmapDrawable d = new FastBitmapDrawable(badgeBitmap);
119         d.setFilterBitmap(true);
120         d.setBounds(0, 0, badgeBitmap.getWidth(), badgeBitmap.getHeight());
121         return d;
122     }
123 
getUserBadge(UserHandle user, Context context)124     protected synchronized Bitmap getUserBadge(UserHandle user, Context context) {
125         Bitmap badgeBitmap = mUserBadges.get(user);
126         if (badgeBitmap != null) {
127             return badgeBitmap;
128         }
129 
130         final Resources res = context.getApplicationContext().getResources();
131         int badgeSize = res.getDimensionPixelSize(R.dimen.profile_badge_size);
132         badgeBitmap = Bitmap.createBitmap(badgeSize, badgeSize, Bitmap.Config.ARGB_8888);
133 
134         Drawable drawable = context.getPackageManager().getUserBadgedDrawableForDensity(
135                 new BitmapDrawable(res, badgeBitmap), user, new Rect(0, 0, badgeSize, badgeSize),
136                 0);
137         if (drawable instanceof BitmapDrawable) {
138             badgeBitmap = ((BitmapDrawable) drawable).getBitmap();
139         } else {
140             badgeBitmap.eraseColor(Color.TRANSPARENT);
141             Canvas c = new Canvas(badgeBitmap);
142             drawable.setBounds(0, 0, badgeSize, badgeSize);
143             drawable.draw(c);
144             c.setBitmap(null);
145         }
146 
147         mUserBadges.put(user, badgeBitmap);
148         return badgeBitmap;
149     }
150 }
151