diff --git a/src/de/danoeh/antennapod/asynctask/ImageLoader.java b/src/de/danoeh/antennapod/asynctask/ImageLoader.java index a4a9bc823..c49d7549b 100644 --- a/src/de/danoeh/antennapod/asynctask/ImageLoader.java +++ b/src/de/danoeh/antennapod/asynctask/ImageLoader.java @@ -1,14 +1,8 @@ package de.danoeh.antennapod.asynctask; -import java.io.InputStream; -import java.util.concurrent.ExecutorService; -import java.util.concurrent.Executors; -import java.util.concurrent.ThreadFactory; - import android.annotation.SuppressLint; import android.app.ActivityManager; import android.content.Context; -import android.content.res.TypedArray; import android.os.Handler; import android.support.v4.util.LruCache; import android.util.Log; @@ -17,214 +11,228 @@ import de.danoeh.antennapod.AppConfig; import de.danoeh.antennapod.PodcastApp; import de.danoeh.antennapod.R; -/** Caches and loads FeedImage bitmaps in the background */ +import java.io.InputStream; +import java.util.concurrent.ExecutorService; +import java.util.concurrent.Executors; +import java.util.concurrent.ThreadFactory; + +/** + * Caches and loads FeedImage bitmaps in the background + */ public class ImageLoader { - private static final String TAG = "ImageLoader"; - private static ImageLoader singleton; + private static final String TAG = "ImageLoader"; + private static ImageLoader singleton; - public static final int IMAGE_TYPE_THUMBNAIL = 0; - public static final int IMAGE_TYPE_COVER = 1; + public static final int IMAGE_TYPE_THUMBNAIL = 0; + public static final int IMAGE_TYPE_COVER = 1; - private Handler handler; - private ExecutorService executor; + private Handler handler; + private ExecutorService executor; - /** - * Stores references to loaded bitmaps. Bitmaps can be accessed by the id of - * the FeedImage the bitmap belongs to. - */ + /** + * Stores references to loaded bitmaps. Bitmaps can be accessed by the id of + * the FeedImage the bitmap belongs to. + */ - final int memClass = ((ActivityManager) PodcastApp.getInstance() - .getSystemService(Context.ACTIVITY_SERVICE)).getMemoryClass(); + final int memClass = ((ActivityManager) PodcastApp.getInstance() + .getSystemService(Context.ACTIVITY_SERVICE)).getMemoryClass(); - // Use 1/8th of the available memory for this memory cache. - final int thumbnailCacheSize = 1024 * 1024 * memClass / 8; + // Use 1/8th of the available memory for this memory cache. + final int thumbnailCacheSize = 1024 * 1024 * memClass / 8; - private LruCache coverCache; - private LruCache thumbnailCache; + private LruCache coverCache; + private LruCache thumbnailCache; - private ImageLoader() { - handler = new Handler(); - executor = createExecutor(); + private ImageLoader() { + handler = new Handler(); + executor = createExecutor(); - coverCache = new LruCache(1); + coverCache = new LruCache(1); - thumbnailCache = new LruCache(thumbnailCacheSize) { + thumbnailCache = new LruCache(thumbnailCacheSize) { - @SuppressLint("NewApi") - @Override - protected int sizeOf(String key, CachedBitmap value) { - if (Integer.valueOf(android.os.Build.VERSION.SDK_INT) >= 12) - return value.getBitmap().getByteCount(); - else - return (value.getBitmap().getRowBytes() * value.getBitmap() - .getHeight()); + @SuppressLint("NewApi") + @Override + protected int sizeOf(String key, CachedBitmap value) { + if (Integer.valueOf(android.os.Build.VERSION.SDK_INT) >= 12) + return value.getBitmap().getByteCount(); + else + return (value.getBitmap().getRowBytes() * value.getBitmap() + .getHeight()); - } + } - }; - } + }; + } - private ExecutorService createExecutor() { - return Executors.newFixedThreadPool(Runtime.getRuntime() - .availableProcessors(), new ThreadFactory() { + private ExecutorService createExecutor() { + return Executors.newFixedThreadPool(Runtime.getRuntime() + .availableProcessors(), new ThreadFactory() { - @Override - public Thread newThread(Runnable r) { - Thread t = new Thread(r); - t.setPriority(Thread.MIN_PRIORITY); - return t; - } - }); - } + @Override + public Thread newThread(Runnable r) { + Thread t = new Thread(r); + t.setPriority(Thread.MIN_PRIORITY); + return t; + } + }); + } - public static synchronized ImageLoader getInstance() { - if (singleton == null) { - singleton = new ImageLoader(); - } - return singleton; - } + public static synchronized ImageLoader getInstance() { + if (singleton == null) { + singleton = new ImageLoader(); + } + return singleton; + } - /** - * Load a bitmap from the cover cache. If the bitmap is not in the cache, it - * will be loaded from the disk. This method should either be called if the - * ImageView's size has already been set or inside a Runnable which is - * posted to the ImageView's message queue. - */ - public void loadCoverBitmap(ImageWorkerTaskResource source, ImageView target) { - loadCoverBitmap(source, target, target.getHeight()); - } + /** + * Load a bitmap from the cover cache. If the bitmap is not in the cache, it + * will be loaded from the disk. This method should either be called if the + * ImageView's size has already been set or inside a Runnable which is + * posted to the ImageView's message queue. + */ + public void loadCoverBitmap(ImageWorkerTaskResource source, ImageView target) { + loadCoverBitmap(source, target, target.getHeight()); + } - /** - * Load a bitmap from the cover cache. If the bitmap is not in the cache, it - * will be loaded from the disk. This method should either be called if the - * ImageView's size has already been set or inside a Runnable which is - * posted to the ImageView's message queue. - */ - public void loadCoverBitmap(ImageWorkerTaskResource source, - ImageView target, int length) { - final int defaultCoverResource = getDefaultCoverResource(target - .getContext()); + /** + * Load a bitmap from the cover cache. If the bitmap is not in the cache, it + * will be loaded from the disk. This method should either be called if the + * ImageView's size has already been set or inside a Runnable which is + * posted to the ImageView's message queue. + */ + public void loadCoverBitmap(ImageWorkerTaskResource source, + ImageView target, int length) { + final int defaultCoverResource = getDefaultCoverResource(target + .getContext()); + final String cacheKey; + if (source != null && (cacheKey = source.getImageLoaderCacheKey()) != null) { + final Object currentTag = target.getTag(R.id.imageloader_key); + if (currentTag == null || !cacheKey.equals(currentTag)) { + target.setTag(R.id.imageloader_key, cacheKey); + CachedBitmap cBitmap = getBitmapFromCoverCache(cacheKey); + if (cBitmap != null && cBitmap.getLength() >= length) { + target.setImageBitmap(cBitmap.getBitmap()); + } else { + target.setImageResource(defaultCoverResource); + BitmapDecodeWorkerTask worker = new BitmapDecodeWorkerTask( + handler, target, source, length, IMAGE_TYPE_COVER); + executor.submit(worker); + } + } + } else { + target.setImageResource(defaultCoverResource); + } + } - if (source != null && source.getImageLoaderCacheKey() != null) { - target.setTag(R.id.imageloader_key, source.getImageLoaderCacheKey()); - CachedBitmap cBitmap = getBitmapFromCoverCache(source.getImageLoaderCacheKey()); - if (cBitmap != null && cBitmap.getLength() >= length) { - target.setImageBitmap(cBitmap.getBitmap()); - } else { - target.setImageResource(defaultCoverResource); - BitmapDecodeWorkerTask worker = new BitmapDecodeWorkerTask( - handler, target, source, length, IMAGE_TYPE_COVER); - executor.submit(worker); - } - } else { - target.setImageResource(defaultCoverResource); - } - } + /** + * Load a bitmap from the thumbnail cache. If the bitmap is not in the + * cache, it will be loaded from the disk. This method should either be + * called if the ImageView's size has already been set or inside a Runnable + * which is posted to the ImageView's message queue. + */ + public void loadThumbnailBitmap(ImageWorkerTaskResource source, + ImageView target) { + loadThumbnailBitmap(source, target, target.getHeight()); + } - /** - * Load a bitmap from the thumbnail cache. If the bitmap is not in the - * cache, it will be loaded from the disk. This method should either be - * called if the ImageView's size has already been set or inside a Runnable - * which is posted to the ImageView's message queue. - */ - public void loadThumbnailBitmap(ImageWorkerTaskResource source, - ImageView target) { - loadThumbnailBitmap(source, target, target.getHeight()); - } + /** + * Load a bitmap from the thumbnail cache. If the bitmap is not in the + * cache, it will be loaded from the disk. This method should either be + * called if the ImageView's size has already been set or inside a Runnable + * which is posted to the ImageView's message queue. + */ + public void loadThumbnailBitmap(ImageWorkerTaskResource source, + ImageView target, int length) { + final int defaultCoverResource = getDefaultCoverResource(target + .getContext()); + final String cacheKey; + if (source != null && (cacheKey = source.getImageLoaderCacheKey()) != null) { + final Object currentTag = target.getTag(R.id.imageloader_key); + if (currentTag == null || !cacheKey.equals(currentTag)) { + target.setTag(R.id.imageloader_key, cacheKey); + CachedBitmap cBitmap = getBitmapFromThumbnailCache(cacheKey); + if (cBitmap != null && cBitmap.getLength() >= length) { + target.setImageBitmap(cBitmap.getBitmap()); + } else { + target.setImageResource(defaultCoverResource); + BitmapDecodeWorkerTask worker = new BitmapDecodeWorkerTask( + handler, target, source, length, IMAGE_TYPE_THUMBNAIL); + executor.submit(worker); + } + } + } else { + target.setImageResource(defaultCoverResource); + } + } - /** - * Load a bitmap from the thumbnail cache. If the bitmap is not in the - * cache, it will be loaded from the disk. This method should either be - * called if the ImageView's size has already been set or inside a Runnable - * which is posted to the ImageView's message queue. - */ - public void loadThumbnailBitmap(ImageWorkerTaskResource source, - ImageView target, int length) { - final int defaultCoverResource = getDefaultCoverResource(target - .getContext()); + public void clearExecutorQueue() { + executor.shutdownNow(); + if (AppConfig.DEBUG) + Log.d(TAG, "Executor was shut down."); + executor = createExecutor(); - if (source != null && source.getImageLoaderCacheKey() != null) { - target.setTag(R.id.imageloader_key, source.getImageLoaderCacheKey()); - CachedBitmap cBitmap = getBitmapFromThumbnailCache(source.getImageLoaderCacheKey()); - if (cBitmap != null && cBitmap.getLength() >= length) { - target.setImageBitmap(cBitmap.getBitmap()); - } else { - target.setImageResource(defaultCoverResource); - BitmapDecodeWorkerTask worker = new BitmapDecodeWorkerTask( - handler, target, source, length, IMAGE_TYPE_THUMBNAIL); - executor.submit(worker); - } - } else { - target.setImageResource(defaultCoverResource); - } - } + } - public void clearExecutorQueue() { - executor.shutdownNow(); - if (AppConfig.DEBUG) - Log.d(TAG, "Executor was shut down."); - executor = createExecutor(); + public void wipeImageCache() { + coverCache.evictAll(); + thumbnailCache.evictAll(); + } - } + public boolean isInThumbnailCache(String fileUrl) { + return thumbnailCache.get(fileUrl) != null; + } - public void wipeImageCache() { - coverCache.evictAll(); - thumbnailCache.evictAll(); - } + private CachedBitmap getBitmapFromThumbnailCache(String key) { + return thumbnailCache.get(key); + } - public boolean isInThumbnailCache(String fileUrl) { - return thumbnailCache.get(fileUrl) != null; - } + public void addBitmapToThumbnailCache(String key, CachedBitmap bitmap) { + thumbnailCache.put(key, bitmap); + } - private CachedBitmap getBitmapFromThumbnailCache(String key) { - return thumbnailCache.get(key); - } + public boolean isInCoverCache(String fileUrl) { + return coverCache.get(fileUrl) != null; + } - public void addBitmapToThumbnailCache(String key, CachedBitmap bitmap) { - thumbnailCache.put(key, bitmap); - } + private CachedBitmap getBitmapFromCoverCache(String key) { + return coverCache.get(key); + } - public boolean isInCoverCache(String fileUrl) { - return coverCache.get(fileUrl) != null; - } + public void addBitmapToCoverCache(String key, CachedBitmap bitmap) { + coverCache.put(key, bitmap); + } - private CachedBitmap getBitmapFromCoverCache(String key) { - return coverCache.get(key); - } + private int getDefaultCoverResource(Context context) { + return android.R.color.transparent; + } - public void addBitmapToCoverCache(String key, CachedBitmap bitmap) { - coverCache.put(key, bitmap); - } + /** + * Used by the BitmapDecodeWorker task to retrieve the source of the bitmap. + */ + public interface ImageWorkerTaskResource { + /** + * Opens a new InputStream that can be decoded as a bitmap by the + * BitmapFactory. + */ + public InputStream openImageInputStream(); - private int getDefaultCoverResource(Context context) { - return android.R.color.transparent; - } + /** + * Returns an InputStream that points to the beginning of the image + * resource. Implementations can either create a new InputStream or + * reset the existing one, depending on their implementation of + * openInputStream. If a new InputStream is returned, the one given as a + * parameter MUST be closed. + * + * @param input The input stream that was returned by openImageInputStream() + */ + public InputStream reopenImageInputStream(InputStream input); - /** - * Used by the BitmapDecodeWorker task to retrieve the source of the bitmap. - */ - public interface ImageWorkerTaskResource { - /** - * Opens a new InputStream that can be decoded as a bitmap by the - * BitmapFactory. - */ - public InputStream openImageInputStream(); - - /** - * Returns an InputStream that points to the beginning of the image - * resource. Implementations can either create a new InputStream or - * reset the existing one, depending on their implementation of - * openInputStream. If a new InputStream is returned, the one given as a - * parameter MUST be closed. - * @param input The input stream that was returned by openImageInputStream() - * */ - public InputStream reopenImageInputStream(InputStream input); - - /** - * Returns a string that identifies the image resource. Example: file - * path of an image - */ - public String getImageLoaderCacheKey(); - } + /** + * Returns a string that identifies the image resource. Example: file + * path of an image + */ + public String getImageLoaderCacheKey(); + } }