OSDN Git Service

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