OSDN Git Service

Eleven: Make sure we have vibrant colors for all default colors
[android-x86/packages-apps-Eleven.git] / src / com / cyanogenmod / eleven / cache / ImageFetcher.java
1 /*
2  * Copyright (C) 2012 Andrew Neal
3  * Copyright (C) 2014 The CyanogenMod Project
4  * Licensed under the Apache License, Version 2.0
5  * (the "License"); you may not use this file except in compliance with the
6  * License. You may obtain a copy of the License at
7  * http://www.apache.org/licenses/LICENSE-2.0 Unless required by applicable law
8  * or agreed to in writing, software distributed under the License is
9  * distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
10  * KIND, either express or implied. See the License for the specific language
11  * governing permissions and limitations under the License.
12  */
13
14 package com.cyanogenmod.eleven.cache;
15
16 import android.content.ContentResolver;
17 import android.content.Context;
18 import android.graphics.Bitmap;
19 import android.graphics.BitmapFactory;
20 import android.net.Uri;
21 import android.widget.ImageView;
22 import com.cyanogenmod.eleven.Config;
23 import com.cyanogenmod.eleven.MusicPlaybackService;
24 import com.cyanogenmod.eleven.cache.PlaylistWorkerTask.PlaylistWorkerType;
25 import com.cyanogenmod.eleven.utils.BitmapWithColors;
26 import com.cyanogenmod.eleven.utils.MusicUtils;
27 import com.cyanogenmod.eleven.widgets.BlurScrimImage;
28 import com.cyanogenmod.eleven.widgets.LetterTileDrawable;
29
30 import java.io.FileNotFoundException;
31 import java.io.IOException;
32 import java.io.InputStream;
33
34 /**
35  * A subclass of {@link ImageWorker} that fetches images from a URL.
36  */
37 public class ImageFetcher extends ImageWorker {
38
39     private static final int DEFAULT_MAX_IMAGE_HEIGHT = 1024;
40
41     private static final int DEFAULT_MAX_IMAGE_WIDTH = 1024;
42
43     private static ImageFetcher sInstance = null;
44
45     /**
46      * Creates a new instance of {@link ImageFetcher}.
47      *
48      * @param context The {@link Context} to use.
49      */
50     public ImageFetcher(final Context context) {
51         super(context);
52     }
53
54     /**
55      * Used to create a singleton of the image fetcher
56      *
57      * @param context The {@link Context} to use
58      * @return A new instance of this class.
59      */
60     public static final ImageFetcher getInstance(final Context context) {
61         if (sInstance == null) {
62             sInstance = new ImageFetcher(context.getApplicationContext());
63         }
64         return sInstance;
65     }
66
67     /**
68      * Loads a playlist's most played song's artist image
69      * @param playlistId id of the playlist
70      * @param imageView imageview to load into
71      */
72     public void loadPlaylistArtistImage(final long playlistId, final ImageView imageView) {
73         loadPlaylistImage(playlistId, PlaylistWorkerType.Artist, imageView);
74     }
75
76     /**
77      * Loads a playlist's most played songs into a combined image, or show 1 if not enough images
78      * @param playlistId id of the playlist
79      * @param imageView imageview to load into
80      */
81     public void loadPlaylistCoverArtImage(final long playlistId, final ImageView imageView) {
82         loadPlaylistImage(playlistId, PlaylistWorkerType.CoverArt, imageView);
83     }
84
85     /**
86      * Used to fetch album images.
87      */
88     public void loadAlbumImage(final String artistName, final String albumName, final long albumId,
89                                final ImageView imageView) {
90         loadImage(generateAlbumCacheKey(albumName, artistName), artistName, albumName, albumId, imageView,
91                 ImageType.ALBUM);
92     }
93
94     /**
95      * Used to fetch the current artwork.
96      */
97     public void loadCurrentArtwork(final ImageView imageView) {
98         loadImage(getCurrentCacheKey(),
99                 MusicUtils.getArtistName(), MusicUtils.getAlbumName(), MusicUtils.getCurrentAlbumId(),
100                 imageView, ImageType.ALBUM);
101     }
102
103     /**
104      * Used to fetch the current artwork blurred.
105      */
106     public void loadCurrentBlurredArtwork(final BlurScrimImage image) {
107         loadBlurImage(getCurrentCacheKey(),
108                 MusicUtils.getArtistName(), MusicUtils.getAlbumName(), MusicUtils.getCurrentAlbumId(),
109                 image, ImageType.ALBUM);
110     }
111
112     public static String getCurrentCacheKey() {
113         return generateAlbumCacheKey(MusicUtils.getAlbumName(), MusicUtils.getArtistName());
114     }
115
116     /**
117      * Used to fetch artist images.
118      */
119     public void loadArtistImage(final String key, final ImageView imageView) {
120         loadImage(key, key, null, -1, imageView, ImageType.ARTIST);
121     }
122
123     /**
124      * Used to fetch artist images. It also scales the image to fit the image view, if necessary.
125      */
126     public void loadArtistImage(final String key, final ImageView imageView, boolean scaleImgToView) {
127         loadImage(key, key, null, -1, imageView, ImageType.ARTIST, scaleImgToView);
128     }
129
130     /**
131      * Used to fetch the current artist image.
132      */
133     public void loadCurrentArtistImage(final ImageView imageView) {
134         loadImage(MusicUtils.getArtistName(), MusicUtils.getArtistName(), null, -1, imageView,
135                 ImageType.ARTIST);
136     }
137
138     /**
139      * @param pause True to temporarily pause the disk cache, false otherwise.
140      */
141     public void setPauseDiskCache(final boolean pause) {
142         if (mImageCache != null) {
143             mImageCache.setPauseDiskCache(pause);
144         }
145     }
146
147     /**
148      * Clears the disk and memory caches
149      */
150     public void clearCaches() {
151         if (mImageCache != null) {
152             mImageCache.clearCaches();
153         }
154
155         // clear the keys of images we've already downloaded
156         sKeys.clear();
157     }
158
159     public void addCacheListener(ICacheListener listener) {
160         if (mImageCache != null) {
161             mImageCache.addCacheListener(listener);
162         }
163     }
164
165     public void removeCacheListener(ICacheListener listener) {
166         if (mImageCache != null) {
167             mImageCache.removeCacheListener(listener);
168         }
169     }
170
171     /**
172      * @param key The key used to find the image to remove
173      */
174     public void removeFromCache(final String key) {
175         if (mImageCache != null) {
176             mImageCache.removeFromCache(key);
177         }
178     }
179
180     /**
181      * Finds cached or downloads album art. Used in {@link MusicPlaybackService}
182      * to set the current album art in the notification and lock screen
183      *
184      * @param albumName  The name of the current album
185      * @param albumId    The ID of the current album
186      * @param artistName The album artist in case we should have to download
187      *                   missing artwork
188      * @param smallArtwork Get the small version of the default artwork if no artwork exists
189      * @return The album art as an {@link Bitmap}
190      */
191     public BitmapWithColors getArtwork(final String albumName, final long albumId,
192             final String artistName, boolean smallArtwork) {
193         // Check the disk cache
194         Bitmap artwork = null;
195         String key = String.valueOf(albumId);
196
197         if (artwork == null && albumName != null && mImageCache != null) {
198             artwork = mImageCache.getBitmapFromDiskCache(key);
199         }
200         if (artwork == null && albumId >= 0 && mImageCache != null) {
201             // Check for local artwork
202             artwork = mImageCache.getArtworkFromFile(mContext, albumId);
203         }
204         if (artwork != null) {
205             return new BitmapWithColors(artwork);
206         }
207
208         return LetterTileDrawable.createDefaultBitmap(mContext, key, ImageType.ALBUM, false,
209                 smallArtwork);
210     }
211
212     /**
213      * Generates key used by album art cache. It needs both album name and artist name
214      * to let to select correct image for the case when there are two albums with the
215      * same artist.
216      *
217      * @param albumName  The album name the cache key needs to be generated.
218      * @param artistName The artist name the cache key needs to be generated.
219      * @return
220      */
221     public static String generateAlbumCacheKey(final String albumName, final String artistName) {
222         if (albumName == null || artistName == null) {
223             return null;
224         }
225         return new StringBuilder(albumName)
226                 .append("_")
227                 .append(artistName)
228                 .append("_")
229                 .append(Config.ALBUM_ART_SUFFIX)
230                 .toString();
231     }
232
233     /**
234      * Decode and sample down a {@link Bitmap} from a Uri.
235      *
236      * @param selectedImage Uri of the Image to decode
237      * @return A {@link Bitmap} sampled down from the original with the same
238      *         aspect ratio and dimensions that are equal to or greater than the
239      *         requested width and height
240      */
241     public static Bitmap decodeSampledBitmapFromUri(ContentResolver cr, final Uri selectedImage) {
242         // First decode with inJustDecodeBounds=true to check dimensions
243         final BitmapFactory.Options options = new BitmapFactory.Options();
244         options.inJustDecodeBounds = true;
245
246         try {
247             InputStream input = cr.openInputStream(selectedImage);
248             BitmapFactory.decodeStream(input, null, options);
249             input.close();
250
251             if (options.outHeight == -1 || options.outWidth == -1) {
252                 return null;
253             }
254
255             // Calculate inSampleSize
256             options.inSampleSize = calculateInSampleSize(options, DEFAULT_MAX_IMAGE_WIDTH,
257                     DEFAULT_MAX_IMAGE_HEIGHT);
258
259             // Decode bitmap with inSampleSize set
260             options.inJustDecodeBounds = false;
261             input = cr.openInputStream(selectedImage);
262             return BitmapFactory.decodeStream(input, null, options);
263         } catch (FileNotFoundException e) {
264             e.printStackTrace();
265             return null;
266         } catch (IOException e) {
267             e.printStackTrace();
268         }
269
270         return null;
271     }
272
273     /**
274      * Calculate an inSampleSize for use in a
275      * {@link android.graphics.BitmapFactory.Options} object when decoding
276      * bitmaps using the decode* methods from {@link BitmapFactory}. This
277      * implementation calculates the closest inSampleSize that will result in
278      * the final decoded bitmap having a width and height equal to or larger
279      * than the requested width and height. This implementation does not ensure
280      * a power of 2 is returned for inSampleSize which can be faster when
281      * decoding but results in a larger bitmap which isn't as useful for caching
282      * purposes.
283      *
284      * @param options An options object with out* params already populated (run
285      *            through a decode* method with inJustDecodeBounds==true
286      * @param reqWidth The requested width of the resulting bitmap
287      * @param reqHeight The requested height of the resulting bitmap
288      * @return The value to be used for inSampleSize
289      */
290     public static final int calculateInSampleSize(final BitmapFactory.Options options,
291                                                   final int reqWidth, final int reqHeight) {
292         /* Raw height and width of image */
293         final int height = options.outHeight;
294         final int width = options.outWidth;
295         int inSampleSize = 1;
296
297         if (height > reqHeight || width > reqWidth) {
298             if (width > height) {
299                 inSampleSize = Math.round((float)height / (float)reqHeight);
300             } else {
301                 inSampleSize = Math.round((float)width / (float)reqWidth);
302             }
303
304             // This offers some additional logic in case the image has a strange
305             // aspect ratio. For example, a panorama may have a much larger
306             // width than height. In these cases the total pixels might still
307             // end up being too large to fit comfortably in memory, so we should
308             // be more aggressive with sample down the image (=larger
309             // inSampleSize).
310
311             final float totalPixels = width * height;
312
313             /* More than 2x the requested pixels we'll sample down further */
314             final float totalReqPixelsCap = reqWidth * reqHeight * 2;
315
316             while (totalPixels / (inSampleSize * inSampleSize) > totalReqPixelsCap) {
317                 inSampleSize++;
318             }
319         }
320         return inSampleSize;
321     }
322 }