1/*
2 * Copyright (C) 2008 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
17package com.android.launcher2;
18
19import android.app.ActivityManager;
20import android.content.ComponentName;
21import android.content.Context;
22import android.content.Intent;
23import android.content.pm.ActivityInfo;
24import android.content.pm.LauncherActivityInfo;
25import android.content.pm.LauncherApps;
26import android.content.pm.PackageManager;
27import android.content.pm.ResolveInfo;
28import android.content.res.Resources;
29import android.graphics.Bitmap;
30import android.graphics.Canvas;
31import android.graphics.drawable.Drawable;
32import android.os.UserHandle;
33
34import java.util.HashMap;
35
36/**
37 * Cache of application icons.  Icons can be made from any thread.
38 */
39public class IconCache {
40    @SuppressWarnings("unused")
41    private static final String TAG = "Launcher.IconCache";
42
43    private static final int INITIAL_ICON_CACHE_CAPACITY = 50;
44
45    private static class CacheEntry {
46        public Bitmap icon;
47        public String title;
48        public CharSequence contentDescription;
49    }
50
51    private static class CacheKey {
52        public ComponentName componentName;
53        public UserHandle user;
54
55        CacheKey(ComponentName componentName, UserHandle user) {
56            this.componentName = componentName;
57            this.user = user;
58        }
59
60        @Override
61        public int hashCode() {
62            return componentName.hashCode() + user.hashCode();
63        }
64
65        @Override
66        public boolean equals(Object o) {
67            CacheKey other = (CacheKey) o;
68            return other.componentName.equals(componentName) && other.user.equals(user);
69        }
70    }
71
72    private final Bitmap mDefaultIcon;
73    private final LauncherApplication mContext;
74    private final PackageManager mPackageManager;
75    private final HashMap<CacheKey, CacheEntry> mCache =
76            new HashMap<CacheKey, CacheEntry>(INITIAL_ICON_CACHE_CAPACITY);
77    private int mIconDpi;
78
79    public IconCache(LauncherApplication context) {
80        ActivityManager activityManager =
81                (ActivityManager) context.getSystemService(Context.ACTIVITY_SERVICE);
82
83        mContext = context;
84        mPackageManager = context.getPackageManager();
85        mIconDpi = activityManager.getLauncherLargeIconDensity();
86        // need to set mIconDpi before getting default icon
87        mDefaultIcon = makeDefaultIcon();
88    }
89
90    public Drawable getFullResDefaultActivityIcon() {
91        return getFullResIcon(Resources.getSystem(),
92                android.R.mipmap.sym_def_app_icon, android.os.Process.myUserHandle());
93    }
94
95    public Drawable getFullResIcon(Resources resources, int iconId, UserHandle user) {
96        Drawable d;
97        try {
98            d = resources.getDrawableForDensity(iconId, mIconDpi);
99        } catch (Resources.NotFoundException e) {
100            d = null;
101        }
102
103        if (d == null) {
104            d = getFullResDefaultActivityIcon();
105        }
106        return mPackageManager.getUserBadgedIcon(d, user);
107    }
108
109    public Drawable getFullResIcon(String packageName, int iconId, UserHandle user) {
110        Resources resources;
111        try {
112            // TODO: Check if this needs to use the user param if we support
113            // shortcuts/widgets from other profiles. It won't work as is
114            // for packages that are only available in a different user profile.
115            resources = mPackageManager.getResourcesForApplication(packageName);
116        } catch (PackageManager.NameNotFoundException e) {
117            resources = null;
118        }
119        if (resources != null) {
120            if (iconId != 0) {
121                return getFullResIcon(resources, iconId, user);
122            }
123        }
124        return getFullResDefaultActivityIcon();
125    }
126
127    public Drawable getFullResIcon(ResolveInfo info, UserHandle user) {
128        return getFullResIcon(info.activityInfo, user);
129    }
130
131    public Drawable getFullResIcon(ActivityInfo info, UserHandle user) {
132        Resources resources;
133        try {
134            resources = mPackageManager.getResourcesForApplication(
135                    info.applicationInfo);
136        } catch (PackageManager.NameNotFoundException e) {
137            resources = null;
138        }
139        if (resources != null) {
140            int iconId = info.getIconResource();
141            if (iconId != 0) {
142                return getFullResIcon(resources, iconId, user);
143            }
144        }
145        return getFullResDefaultActivityIcon();
146    }
147
148    private Bitmap makeDefaultIcon() {
149        Drawable d = getFullResDefaultActivityIcon();
150        Bitmap b = Bitmap.createBitmap(Math.max(d.getIntrinsicWidth(), 1),
151                Math.max(d.getIntrinsicHeight(), 1),
152                Bitmap.Config.ARGB_8888);
153        Canvas c = new Canvas(b);
154        d.setBounds(0, 0, b.getWidth(), b.getHeight());
155        d.draw(c);
156        c.setBitmap(null);
157        return b;
158    }
159
160    /**
161     * Remove any records for the supplied ComponentName.
162     */
163    public void remove(ComponentName componentName, UserHandle user) {
164        synchronized (mCache) {
165            mCache.remove(new CacheKey(componentName, user));
166        }
167    }
168
169    /**
170     * Empty out the cache.
171     */
172    public void flush() {
173        synchronized (mCache) {
174            mCache.clear();
175        }
176    }
177
178    /**
179     * Fill in "application" with the icon and label for "info."
180     */
181    public void getTitleAndIcon(ApplicationInfo application, LauncherActivityInfo info,
182            HashMap<Object, CharSequence> labelCache) {
183        synchronized (mCache) {
184            CacheEntry entry = cacheLocked(application.componentName, info, labelCache,
185                    info.getUser());
186
187            application.title = entry.title;
188            application.iconBitmap = entry.icon;
189            application.contentDescription = entry.contentDescription;
190        }
191    }
192
193    public Bitmap getIcon(Intent intent, UserHandle user) {
194        synchronized (mCache) {
195            LauncherApps launcherApps = (LauncherApps)
196                    mContext.getSystemService(Context.LAUNCHER_APPS_SERVICE);
197            final LauncherActivityInfo launcherActInfo =
198                    launcherApps.resolveActivity(intent, user);
199            ComponentName component = intent.getComponent();
200
201            if (launcherActInfo == null || component == null) {
202                return mDefaultIcon;
203            }
204
205            CacheEntry entry = cacheLocked(component, launcherActInfo, null, user);
206            return entry.icon;
207        }
208    }
209
210    public Bitmap getIcon(ComponentName component, LauncherActivityInfo info,
211            HashMap<Object, CharSequence> labelCache) {
212        synchronized (mCache) {
213            if (info == null || component == null) {
214                return null;
215            }
216
217            CacheEntry entry = cacheLocked(component, info, labelCache, info.getUser());
218            return entry.icon;
219        }
220    }
221
222    public boolean isDefaultIcon(Bitmap icon) {
223        return mDefaultIcon == icon;
224    }
225
226    private CacheEntry cacheLocked(ComponentName componentName, LauncherActivityInfo info,
227            HashMap<Object, CharSequence> labelCache, UserHandle user) {
228        CacheKey cacheKey = new CacheKey(componentName, user);
229        CacheEntry entry = mCache.get(cacheKey);
230        if (entry == null) {
231            entry = new CacheEntry();
232
233            mCache.put(cacheKey, entry);
234
235            ComponentName key = info.getComponentName();
236            if (labelCache != null && labelCache.containsKey(key)) {
237                entry.title = labelCache.get(key).toString();
238            } else {
239                entry.title = info.getLabel().toString();
240                if (labelCache != null) {
241                    labelCache.put(key, entry.title);
242                }
243            }
244            if (entry.title == null) {
245                entry.title = info.getComponentName().getShortClassName();
246            }
247            entry.contentDescription = mPackageManager.getUserBadgedLabel(entry.title, user);
248            entry.icon = Utilities.createIconBitmap(info.getBadgedIcon(mIconDpi), mContext);
249        }
250        return entry;
251    }
252}
253