OSDN Git Service

42480f4acb86485938afd71a49e5d593246fce46
[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, key.hashCode());
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 albumName + "_" + artistName + "_" + Config.ALBUM_ART_SUFFIX;
226     }
227
228     /**
229      * Decode and sample down a {@link Bitmap} from a Uri.
230      *
231      * @param selectedImage Uri of the Image to decode
232      * @return A {@link Bitmap} sampled down from the original with the same
233      *         aspect ratio and dimensions that are equal to or greater than the
234      *         requested width and height
235      */
236     public static Bitmap decodeSampledBitmapFromUri(ContentResolver cr, final Uri selectedImage) {
237         // First decode with inJustDecodeBounds=true to check dimensions
238         final BitmapFactory.Options options = new BitmapFactory.Options();
239         options.inJustDecodeBounds = true;
240
241         try {
242             InputStream input = cr.openInputStream(selectedImage);
243             BitmapFactory.decodeStream(input, null, options);
244             input.close();
245
246             if (options.outHeight == -1 || options.outWidth == -1) {
247                 return null;
248             }
249
250             // Calculate inSampleSize
251             options.inSampleSize = calculateInSampleSize(options, DEFAULT_MAX_IMAGE_WIDTH,
252                     DEFAULT_MAX_IMAGE_HEIGHT);
253
254             // Decode bitmap with inSampleSize set
255             options.inJustDecodeBounds = false;
256             input = cr.openInputStream(selectedImage);
257             return BitmapFactory.decodeStream(input, null, options);
258         } catch (FileNotFoundException e) {
259             e.printStackTrace();
260             return null;
261         } catch (IOException e) {
262             e.printStackTrace();
263         }
264
265         return null;
266     }
267
268     /**
269      * Calculate an inSampleSize for use in a
270      * {@link android.graphics.BitmapFactory.Options} object when decoding
271      * bitmaps using the decode* methods from {@link BitmapFactory}. This
272      * implementation calculates the closest inSampleSize that will result in
273      * the final decoded bitmap having a width and height equal to or larger
274      * than the requested width and height. This implementation does not ensure
275      * a power of 2 is returned for inSampleSize which can be faster when
276      * decoding but results in a larger bitmap which isn't as useful for caching
277      * purposes.
278      *
279      * @param options An options object with out* params already populated (run
280      *            through a decode* method with inJustDecodeBounds==true
281      * @param reqWidth The requested width of the resulting bitmap
282      * @param reqHeight The requested height of the resulting bitmap
283      * @return The value to be used for inSampleSize
284      */
285     public static final int calculateInSampleSize(final BitmapFactory.Options options,
286                                                   final int reqWidth, final int reqHeight) {
287         /* Raw height and width of image */
288         final int height = options.outHeight;
289         final int width = options.outWidth;
290         int inSampleSize = 1;
291
292         if (height > reqHeight || width > reqWidth) {
293             if (width > height) {
294                 inSampleSize = Math.round((float)height / (float)reqHeight);
295             } else {
296                 inSampleSize = Math.round((float)width / (float)reqWidth);
297             }
298
299             // This offers some additional logic in case the image has a strange
300             // aspect ratio. For example, a panorama may have a much larger
301             // width than height. In these cases the total pixels might still
302             // end up being too large to fit comfortably in memory, so we should
303             // be more aggressive with sample down the image (=larger
304             // inSampleSize).
305
306             final float totalPixels = width * height;
307
308             /* More than 2x the requested pixels we'll sample down further */
309             final float totalReqPixelsCap = reqWidth * reqHeight * 2;
310
311             while (totalPixels / (inSampleSize * inSampleSize) > totalReqPixelsCap) {
312                 inSampleSize++;
313             }
314         }
315         return inSampleSize;
316     }
317 }