package com.cyngn.eleven.cache;
+import android.content.ContentResolver;
import android.content.Context;
import android.graphics.Bitmap;
+import android.graphics.BitmapFactory;
+import android.net.Uri;
import android.widget.ImageView;
-
import com.cyngn.eleven.Config;
import com.cyngn.eleven.MusicPlaybackService;
import com.cyngn.eleven.cache.PlaylistWorkerTask.PlaylistWorkerType;
import com.cyngn.eleven.utils.MusicUtils;
import com.cyngn.eleven.widgets.BlurScrimImage;
+import java.io.FileNotFoundException;
+import java.io.IOException;
+import java.io.InputStream;
+
/**
* A subclass of {@link ImageWorker} that fetches images from a URL.
*/
public class ImageFetcher extends ImageWorker {
+ private static final int DEFAULT_MAX_IMAGE_HEIGHT = 1024;
+
+ private static final int DEFAULT_MAX_IMAGE_WIDTH = 1024;
+
private static ImageFetcher sInstance = null;
/**
* Used to fetch the current artwork.
*/
public void loadCurrentArtwork(final ImageView imageView) {
- loadImage(generateAlbumCacheKey(MusicUtils.getAlbumName(), MusicUtils.getArtistName()),
+ loadImage(getCurrentCacheKey(),
MusicUtils.getArtistName(), MusicUtils.getAlbumName(), MusicUtils.getCurrentAlbumId(),
imageView, ImageType.ALBUM);
}
* Used to fetch the current artwork blurred.
*/
public void loadCurrentBlurredArtwork(final BlurScrimImage image) {
- loadBlurImage(generateAlbumCacheKey(MusicUtils.getAlbumName(), MusicUtils.getArtistName()),
+ loadBlurImage(getCurrentCacheKey(),
MusicUtils.getArtistName(), MusicUtils.getAlbumName(), MusicUtils.getCurrentAlbumId(),
image, ImageType.ALBUM);
}
+ public static String getCurrentCacheKey() {
+ return generateAlbumCacheKey(MusicUtils.getAlbumName(), MusicUtils.getArtistName());
+ }
+
/**
* Used to fetch artist images.
*/
}
/**
+ * Used to fetch artist images. It also scales the image to fit the image view, if necessary.
+ */
+ public void loadArtistImage(final String key, final ImageView imageView, boolean scaleImgToView) {
+ loadImage(key, key, null, -1, imageView, ImageType.ARTIST, scaleImgToView);
+ }
+
+ /**
* Used to fetch the current artist image.
*/
public void loadCurrentArtistImage(final ImageView imageView) {
if (mImageCache != null) {
mImageCache.clearCaches();
}
+
+ // clear the keys of images we've already downloaded
+ sKeys.clear();
+ }
+
+ public void addCacheListener(ICacheListener listener) {
+ if (mImageCache != null) {
+ mImageCache.addCacheListener(listener);
+ }
+ }
+
+ public void removeCacheListener(ICacheListener listener) {
+ if (mImageCache != null) {
+ mImageCache.removeCacheListener(listener);
+ }
}
/**
* @param albumId The ID of the current album
* @param artistName The album artist in case we should have to download
* missing artwork
+ * @param smallArtwork Get the small version of the default artwork if no artwork exists
* @return The album art as an {@link Bitmap}
*/
- public Bitmap getArtwork(final String albumName, final long albumId, final String artistName) {
+ public Bitmap getArtwork(final String albumName, final long albumId, final String artistName,
+ boolean smallArtwork) {
// Check the disk cache
Bitmap artwork = null;
if (artwork != null) {
return artwork;
}
- return getDefaultArtwork();
+ return getDefaultArtwork(smallArtwork);
}
/**
.append(Config.ALBUM_ART_SUFFIX)
.toString();
}
+
+ /**
+ * Decode and sample down a {@link Bitmap} from a Uri.
+ *
+ * @param selectedImage Uri of the Image to decode
+ * @return A {@link Bitmap} sampled down from the original with the same
+ * aspect ratio and dimensions that are equal to or greater than the
+ * requested width and height
+ */
+ public static Bitmap decodeSampledBitmapFromUri(ContentResolver cr, final Uri selectedImage) {
+ // First decode with inJustDecodeBounds=true to check dimensions
+ final BitmapFactory.Options options = new BitmapFactory.Options();
+ options.inJustDecodeBounds = true;
+
+ try {
+ InputStream input = cr.openInputStream(selectedImage);
+ BitmapFactory.decodeStream(input, null, options);
+ input.close();
+
+ if (options.outHeight == -1 || options.outWidth == -1) {
+ return null;
+ }
+
+ // Calculate inSampleSize
+ options.inSampleSize = calculateInSampleSize(options, DEFAULT_MAX_IMAGE_WIDTH,
+ DEFAULT_MAX_IMAGE_HEIGHT);
+
+ // Decode bitmap with inSampleSize set
+ options.inJustDecodeBounds = false;
+ input = cr.openInputStream(selectedImage);
+ return BitmapFactory.decodeStream(input, null, options);
+ } catch (FileNotFoundException e) {
+ e.printStackTrace();
+ return null;
+ } catch (IOException e) {
+ e.printStackTrace();
+ }
+
+ return null;
+ }
+
+ /**
+ * Calculate an inSampleSize for use in a
+ * {@link android.graphics.BitmapFactory.Options} object when decoding
+ * bitmaps using the decode* methods from {@link BitmapFactory}. This
+ * implementation calculates the closest inSampleSize that will result in
+ * the final decoded bitmap having a width and height equal to or larger
+ * than the requested width and height. This implementation does not ensure
+ * a power of 2 is returned for inSampleSize which can be faster when
+ * decoding but results in a larger bitmap which isn't as useful for caching
+ * purposes.
+ *
+ * @param options An options object with out* params already populated (run
+ * through a decode* method with inJustDecodeBounds==true
+ * @param reqWidth The requested width of the resulting bitmap
+ * @param reqHeight The requested height of the resulting bitmap
+ * @return The value to be used for inSampleSize
+ */
+ public static final int calculateInSampleSize(final BitmapFactory.Options options,
+ final int reqWidth, final int reqHeight) {
+ /* Raw height and width of image */
+ final int height = options.outHeight;
+ final int width = options.outWidth;
+ int inSampleSize = 1;
+
+ if (height > reqHeight || width > reqWidth) {
+ if (width > height) {
+ inSampleSize = Math.round((float)height / (float)reqHeight);
+ } else {
+ inSampleSize = Math.round((float)width / (float)reqWidth);
+ }
+
+ // This offers some additional logic in case the image has a strange
+ // aspect ratio. For example, a panorama may have a much larger
+ // width than height. In these cases the total pixels might still
+ // end up being too large to fit comfortably in memory, so we should
+ // be more aggressive with sample down the image (=larger
+ // inSampleSize).
+
+ final float totalPixels = width * height;
+
+ /* More than 2x the requested pixels we'll sample down further */
+ final float totalReqPixelsCap = reqWidth * reqHeight * 2;
+
+ while (totalPixels / (inSampleSize * inSampleSize) > totalReqPixelsCap) {
+ inSampleSize++;
+ }
+ }
+ return inSampleSize;
+ }
}