summaryrefslogtreecommitdiff
path: root/src/de/danoeh/antennapod/asynctask
diff options
context:
space:
mode:
authordaniel oeh <daniel.oeh@gmail.com>2014-07-27 23:29:47 +0200
committerdaniel oeh <daniel.oeh@gmail.com>2014-07-30 12:42:01 +0200
commit09c4736867acafee9c008caa8ab78f7b3c4cef68 (patch)
tree04b5ca6258c3449a9ee89fc832edc44c652fe0de /src/de/danoeh/antennapod/asynctask
parent460e061d35e45268d3dcfebeba00e7231ce8cfd0 (diff)
downloadAntennaPod-09c4736867acafee9c008caa8ab78f7b3c4cef68.zip
Replaced ImageLoader and DiskCache with Picasso
Implemented Picasso Downloaders Replaced ImageLoader and DiskCache with Picasso Removed ImageLoader, DiskCache code
Diffstat (limited to 'src/de/danoeh/antennapod/asynctask')
-rw-r--r--src/de/danoeh/antennapod/asynctask/BitmapDecodeWorkerTask.java115
-rw-r--r--src/de/danoeh/antennapod/asynctask/CachedBitmap.java27
-rw-r--r--src/de/danoeh/antennapod/asynctask/ImageDiskCache.java397
-rw-r--r--src/de/danoeh/antennapod/asynctask/ImageLoader.java246
-rw-r--r--src/de/danoeh/antennapod/asynctask/PicassoImageResource.java25
-rw-r--r--src/de/danoeh/antennapod/asynctask/PicassoProvider.java137
6 files changed, 162 insertions, 785 deletions
diff --git a/src/de/danoeh/antennapod/asynctask/BitmapDecodeWorkerTask.java b/src/de/danoeh/antennapod/asynctask/BitmapDecodeWorkerTask.java
deleted file mode 100644
index 43118c3af..000000000
--- a/src/de/danoeh/antennapod/asynctask/BitmapDecodeWorkerTask.java
+++ /dev/null
@@ -1,115 +0,0 @@
-package de.danoeh.antennapod.asynctask;
-
-import android.graphics.BitmapFactory;
-import android.graphics.drawable.BitmapDrawable;
-import android.graphics.drawable.Drawable;
-import android.graphics.drawable.TransitionDrawable;
-import android.os.Handler;
-import android.util.Log;
-import android.widget.ImageView;
-import de.danoeh.antennapod.BuildConfig;
-import de.danoeh.antennapod.PodcastApp;
-import de.danoeh.antennapod.R;
-import de.danoeh.antennapod.asynctask.ImageLoader.ImageWorkerTaskResource;
-import de.danoeh.antennapod.util.BitmapDecoder;
-
-public class BitmapDecodeWorkerTask extends Thread {
-
- protected int PREFERRED_LENGTH;
- public static final int FADE_DURATION = 500;
-
- /**
- * Can be thumbnail or cover
- */
- protected int imageType;
-
- private static final String TAG = "BitmapDecodeWorkerTask";
- private ImageView target;
- protected CachedBitmap cBitmap;
-
- protected ImageLoader.ImageWorkerTaskResource imageResource;
-
- private Handler handler;
-
- private final int defaultCoverResource;
-
- public BitmapDecodeWorkerTask(Handler handler, ImageView target,
- ImageWorkerTaskResource imageResource, int length, int imageType) {
- super();
- this.handler = handler;
- this.target = target;
- this.imageResource = imageResource;
- this.PREFERRED_LENGTH = length;
- this.imageType = imageType;
- this.defaultCoverResource = android.R.color.transparent;
- }
-
- /**
- * Should return true if tag of the imageview is still the same it was
- * before the bitmap was decoded
- */
- protected boolean tagsMatching(ImageView target) {
- Object tag = target.getTag(R.id.imageloader_key);
- return tag != null && tag.equals(imageResource.getImageLoaderCacheKey());
- }
-
- protected void onPostExecute() {
- // check if imageview is still supposed to display this image
- if (tagsMatching(target) && cBitmap.getBitmap() != null) {
- Drawable[] drawables = new Drawable[]{
- PodcastApp.getInstance().getResources().getDrawable(android.R.color.transparent),
- new BitmapDrawable(PodcastApp.getInstance().getResources(), cBitmap.getBitmap())
- };
- TransitionDrawable transitionDrawable = new TransitionDrawable(drawables);
- target.setImageDrawable(transitionDrawable);
- transitionDrawable.startTransition(FADE_DURATION);
- } else {
- if (BuildConfig.DEBUG)
- Log.d(TAG, "Not displaying image");
- }
- }
-
- @Override
- public void run() {
- cBitmap = new CachedBitmap(BitmapDecoder.decodeBitmapFromWorkerTaskResource(
- PREFERRED_LENGTH, imageResource), PREFERRED_LENGTH);
- if (cBitmap.getBitmap() != null) {
- storeBitmapInCache(cBitmap);
- } else {
- Log.w(TAG, "Could not load bitmap. Using default image.");
- cBitmap = new CachedBitmap(BitmapFactory.decodeResource(
- target.getResources(), defaultCoverResource),
- PREFERRED_LENGTH);
- }
- if (BuildConfig.DEBUG)
- Log.d(TAG, "Finished loading bitmaps");
-
- endBackgroundTask();
- }
-
- protected final void endBackgroundTask() {
- handler.post(new Runnable() {
-
- @Override
- public void run() {
- onPostExecute();
- }
-
- });
- }
-
- protected void onInvalidStream() {
- cBitmap = new CachedBitmap(BitmapFactory.decodeResource(
- target.getResources(), defaultCoverResource), PREFERRED_LENGTH);
- }
-
- protected void storeBitmapInCache(CachedBitmap cb) {
- ImageLoader loader = ImageLoader.getInstance();
- if (imageType == ImageLoader.IMAGE_TYPE_COVER) {
- loader.addBitmapToCoverCache(imageResource.getImageLoaderCacheKey(), cb);
- } else if (imageType == ImageLoader.IMAGE_TYPE_THUMBNAIL) {
- loader.addBitmapToThumbnailCache(imageResource.getImageLoaderCacheKey(), cb);
- }
- }
-
-}
diff --git a/src/de/danoeh/antennapod/asynctask/CachedBitmap.java b/src/de/danoeh/antennapod/asynctask/CachedBitmap.java
deleted file mode 100644
index 5a89b7b53..000000000
--- a/src/de/danoeh/antennapod/asynctask/CachedBitmap.java
+++ /dev/null
@@ -1,27 +0,0 @@
-package de.danoeh.antennapod.asynctask;
-
-import android.graphics.Bitmap;
-
-/** Stores a bitmap and the length it was decoded with. */
-public class CachedBitmap {
-
- private Bitmap bitmap;
- private int length;
-
- public CachedBitmap(Bitmap bitmap, int length) {
- super();
- this.bitmap = bitmap;
- this.length = length;
- }
-
- public Bitmap getBitmap() {
- return bitmap;
- }
- public int getLength() {
- return length;
- }
-
-
-
-
-}
diff --git a/src/de/danoeh/antennapod/asynctask/ImageDiskCache.java b/src/de/danoeh/antennapod/asynctask/ImageDiskCache.java
deleted file mode 100644
index 77609f28b..000000000
--- a/src/de/danoeh/antennapod/asynctask/ImageDiskCache.java
+++ /dev/null
@@ -1,397 +0,0 @@
-package de.danoeh.antennapod.asynctask;
-
-import android.os.Handler;
-import android.util.Log;
-import android.util.Pair;
-import android.widget.ImageView;
-import de.danoeh.antennapod.BuildConfig;
-import de.danoeh.antennapod.PodcastApp;
-import de.danoeh.antennapod.R;
-import de.danoeh.antennapod.service.download.DownloadRequest;
-import de.danoeh.antennapod.service.download.HttpDownloader;
-import org.apache.commons.io.IOUtils;
-import org.apache.commons.lang3.StringUtils;
-import org.apache.commons.lang3.Validate;
-
-import java.io.*;
-import java.util.ArrayList;
-import java.util.Collection;
-import java.util.HashMap;
-import java.util.List;
-import java.util.concurrent.ConcurrentHashMap;
-import java.util.concurrent.ExecutorService;
-import java.util.concurrent.Executors;
-
-/**
- * Provides local cache for storing downloaded image. An image disk cache downloads images and stores them as long
- * as the cache is not full. Once the cache is full, the image disk cache will delete older images.
- */
-public class ImageDiskCache {
- private static final String TAG = "ImageDiskCache";
-
- private static HashMap<String, ImageDiskCache> cacheSingletons = new HashMap<String, ImageDiskCache>();
-
- /**
- * Return a default instance of an ImageDiskCache. This cache will store data in the external cache folder.
- */
- public static synchronized ImageDiskCache getDefaultInstance() {
- final String DEFAULT_PATH = "imagecache";
- final long DEFAULT_MAX_CACHE_SIZE = 10 * 1024 * 1024;
-
- File cacheDir = PodcastApp.getInstance().getExternalCacheDir();
- if (cacheDir == null) {
- return null;
- }
- return getInstance(new File(cacheDir, DEFAULT_PATH).getAbsolutePath(), DEFAULT_MAX_CACHE_SIZE);
- }
-
- /**
- * Return an instance of an ImageDiskCache that stores images in the specified folder.
- */
- public static synchronized ImageDiskCache getInstance(String path, long maxCacheSize) {
- Validate.notNull(path);
-
- if (cacheSingletons.containsKey(path)) {
- return cacheSingletons.get(path);
- }
-
- ImageDiskCache cache = cacheSingletons.get(path);
- if (cache == null) {
- cache = new ImageDiskCache(path, maxCacheSize);
- cacheSingletons.put(new File(path).getAbsolutePath(), cache);
- }
- cacheSingletons.put(path, cache);
- return cache;
- }
-
- /**
- * Filename - cache object mapping
- */
- private static final String CACHE_FILE_NAME = "cachefile";
- private ExecutorService executor;
- private ConcurrentHashMap<String, DiskCacheObject> diskCache;
- private final long maxCacheSize;
- private int cacheSize;
- private final File cacheFolder;
- private Handler handler;
-
- private ImageDiskCache(String path, long maxCacheSize) {
- this.maxCacheSize = maxCacheSize;
- this.cacheFolder = new File(path);
- if (!cacheFolder.exists() && !cacheFolder.mkdir()) {
- throw new IllegalArgumentException("Image disk cache could not create cache folder in: " + path);
- }
-
- executor = Executors.newFixedThreadPool(Runtime.getRuntime()
- .availableProcessors());
- handler = new Handler();
- }
-
- private synchronized void initCacheFolder() {
- if (diskCache == null) {
- if (BuildConfig.DEBUG) Log.d(TAG, "Initializing cache folder");
- File cacheFile = new File(cacheFolder, CACHE_FILE_NAME);
- if (cacheFile.exists()) {
- try {
- InputStream in = new FileInputStream(cacheFile);
- BufferedInputStream buffer = new BufferedInputStream(in);
- ObjectInputStream objectInput = new ObjectInputStream(buffer);
- diskCache = (ConcurrentHashMap<String, DiskCacheObject>) objectInput.readObject();
- // calculate cache size
- for (DiskCacheObject dco : diskCache.values()) {
- cacheSize += dco.size;
- }
- deleteInvalidFiles();
- } catch (IOException e) {
- e.printStackTrace();
- diskCache = new ConcurrentHashMap<String, DiskCacheObject>();
- } catch (ClassCastException e) {
- e.printStackTrace();
- diskCache = new ConcurrentHashMap<String, DiskCacheObject>();
- } catch (ClassNotFoundException e) {
- e.printStackTrace();
- diskCache = new ConcurrentHashMap<String, DiskCacheObject>();
- }
- } else {
- diskCache = new ConcurrentHashMap<String, DiskCacheObject>();
- }
- }
- }
-
- private List<File> getCacheFileList() {
- Collection<DiskCacheObject> values = diskCache.values();
- List<File> files = new ArrayList<File>();
- for (DiskCacheObject dco : values) {
- files.add(dco.getFile());
- }
- files.add(new File(cacheFolder, CACHE_FILE_NAME));
- return files;
- }
-
- private Pair<String, DiskCacheObject> getOldestCacheObject() {
- Collection<String> keys = diskCache.keySet();
- DiskCacheObject oldest = null;
- String oldestKey = null;
-
- for (String key : keys) {
-
- if (oldestKey == null) {
- oldestKey = key;
- oldest = diskCache.get(key);
- } else {
- DiskCacheObject dco = diskCache.get(key);
- if (oldest.timestamp > dco.timestamp) {
- oldestKey = key;
- oldest = diskCache.get(key);
- }
- }
- }
- return new Pair<String, DiskCacheObject>(oldestKey, oldest);
- }
-
- private synchronized void deleteCacheObject(String key, DiskCacheObject value) {
- Log.i(TAG, "Deleting cached object: " + key);
- diskCache.remove(key);
- boolean result = value.getFile().delete();
- if (!result) {
- Log.w(TAG, "Could not delete file " + value.fileUrl);
- }
- cacheSize -= value.size;
- }
-
- private synchronized void deleteInvalidFiles() {
- // delete files that are not stored inside the cache
- File[] files = cacheFolder.listFiles();
- List<File> cacheFiles = getCacheFileList();
- for (File file : files) {
- if (!cacheFiles.contains(file)) {
- Log.i(TAG, "Deleting unused file: " + file.getAbsolutePath());
- boolean result = file.delete();
- if (!result) {
- Log.w(TAG, "Could not delete file: " + file.getAbsolutePath());
- }
- }
- }
- }
-
- private synchronized void cleanup() {
- if (cacheSize > maxCacheSize) {
- while (cacheSize > maxCacheSize) {
- Pair<String, DiskCacheObject> oldest = getOldestCacheObject();
- deleteCacheObject(oldest.first, oldest.second);
- }
- }
- }
-
- /**
- * Loads a new image from the disk cache. If the image that the url points to has already been downloaded, the image will
- * be loaded from the disk. Otherwise, the image will be downloaded first.
- * The image will be stored in the thumbnail cache.
- */
- public void loadThumbnailBitmap(final String url, final ImageView target, final int length) {
- if (url == null) {
- Log.w(TAG, "loadThumbnailBitmap: Call was ignored because url = null");
- return;
- }
- final ImageLoader il = ImageLoader.getInstance();
- target.setTag(R.id.image_disk_cache_key, url);
- if (diskCache != null) {
- DiskCacheObject dco = getFromCacheIfAvailable(url);
- if (dco != null) {
- il.loadThumbnailBitmap(dco.loadImage(), target, length);
- return;
- }
- }
- target.setImageResource(android.R.color.transparent);
- executor.submit(new ImageDownloader(url) {
- @Override
- protected void onImageLoaded(DiskCacheObject diskCacheObject) {
- final Object tag = target.getTag(R.id.image_disk_cache_key);
- if (tag != null && StringUtils.equals((String) tag, url)) {
- il.loadThumbnailBitmap(diskCacheObject.loadImage(), target, length);
- }
- }
- });
-
- }
-
- /**
- * Loads a new image from the disk cache. If the image that the url points to has already been downloaded, the image will
- * be loaded from the disk. Otherwise, the image will be downloaded first.
- * The image will be stored in the cover cache.
- */
- public void loadCoverBitmap(final String url, final ImageView target, final int length) {
- if (url == null) {
- Log.w(TAG, "loadCoverBitmap: Call was ignored because url = null");
- return;
- }
- final ImageLoader il = ImageLoader.getInstance();
- target.setTag(R.id.image_disk_cache_key, url);
- if (diskCache != null) {
- DiskCacheObject dco = getFromCacheIfAvailable(url);
- if (dco != null) {
- il.loadThumbnailBitmap(dco.loadImage(), target, length);
- return;
- }
- }
- target.setImageResource(android.R.color.transparent);
- executor.submit(new ImageDownloader(url) {
- @Override
- protected void onImageLoaded(DiskCacheObject diskCacheObject) {
- final Object tag = target.getTag(R.id.image_disk_cache_key);
- if (tag != null && StringUtils.equals((String) tag, url)) {
- il.loadCoverBitmap(diskCacheObject.loadImage(), target, length);
- }
- }
- });
- }
-
- private synchronized void addToDiskCache(String url, DiskCacheObject obj) {
- if (diskCache == null) {
- initCacheFolder();
- }
- if (BuildConfig.DEBUG) Log.d(TAG, "Adding new image to disk cache: " + url);
- diskCache.put(url, obj);
- cacheSize += obj.size;
- if (cacheSize > maxCacheSize) {
- cleanup();
- }
- saveCacheInfoFile();
- }
-
- private synchronized void saveCacheInfoFile() {
- OutputStream out = null;
- try {
- out = new BufferedOutputStream(new FileOutputStream(new File(cacheFolder, CACHE_FILE_NAME)));
- ObjectOutputStream objOut = new ObjectOutputStream(out);
- objOut.writeObject(diskCache);
- } catch (IOException e) {
- e.printStackTrace();
- } finally {
- IOUtils.closeQuietly(out);
- }
- }
-
- private synchronized DiskCacheObject getFromCacheIfAvailable(String key) {
- if (diskCache == null) {
- initCacheFolder();
- }
- DiskCacheObject dco = diskCache.get(key);
- if (dco != null) {
- dco.timestamp = System.currentTimeMillis();
- }
- return dco;
- }
-
- ConcurrentHashMap<String, File> runningDownloads = new ConcurrentHashMap<String, File>();
-
- private abstract class ImageDownloader implements Runnable {
- private String downloadUrl;
-
- public ImageDownloader(String downloadUrl) {
- this.downloadUrl = downloadUrl;
- }
-
- protected abstract void onImageLoaded(DiskCacheObject diskCacheObject);
-
- public void run() {
- DiskCacheObject tmp = getFromCacheIfAvailable(downloadUrl);
- if (tmp != null) {
- onImageLoaded(tmp);
- return;
- }
-
- DiskCacheObject dco = null;
- File newFile = new File(cacheFolder, Integer.toString(downloadUrl.hashCode()));
- synchronized (ImageDiskCache.this) {
- if (runningDownloads.containsKey(newFile.getAbsolutePath())) {
- Log.d(TAG, "Download is already running: " + newFile.getAbsolutePath());
- return;
- } else {
- runningDownloads.put(newFile.getAbsolutePath(), newFile);
- }
- }
- if (newFile.exists()) {
- newFile.delete();
- }
-
- HttpDownloader result = downloadFile(newFile.getAbsolutePath(), downloadUrl);
- if (result.getResult().isSuccessful()) {
- long size = result.getDownloadRequest().getSoFar();
-
- dco = new DiskCacheObject(newFile.getAbsolutePath(), size);
- addToDiskCache(downloadUrl, dco);
- if (BuildConfig.DEBUG) Log.d(TAG, "Image was downloaded");
- } else {
- Log.w(TAG, "Download of url " + downloadUrl + " failed. Reason: " + result.getResult().getReasonDetailed() + "(" + result.getResult().getReason() + ")");
- }
-
- if (dco != null) {
- final DiskCacheObject dcoRef = dco;
- handler.post(new Runnable() {
- @Override
- public void run() {
- onImageLoaded(dcoRef);
- }
- });
-
- }
- runningDownloads.remove(newFile.getAbsolutePath());
-
- }
-
- private HttpDownloader downloadFile(String destination, String source) {
- DownloadRequest request = new DownloadRequest(destination, source, "", 0, 0);
- HttpDownloader downloader = new HttpDownloader(request);
- downloader.call();
- return downloader;
- }
- }
-
- private static class DiskCacheObject implements Serializable {
- private final String fileUrl;
-
- /**
- * Last usage of this image cache object.
- */
- private long timestamp;
- private final long size;
-
- public DiskCacheObject(String fileUrl, long size) {
- Validate.notNull(fileUrl);
- this.fileUrl = fileUrl;
- this.timestamp = System.currentTimeMillis();
- this.size = size;
- }
-
- public File getFile() {
- return new File(fileUrl);
- }
-
- public ImageLoader.ImageWorkerTaskResource loadImage() {
- return new ImageLoader.ImageWorkerTaskResource() {
-
- @Override
- public InputStream openImageInputStream() {
- try {
- return new FileInputStream(getFile());
- } catch (FileNotFoundException e) {
- e.printStackTrace();
- }
- return null;
- }
-
- @Override
- public InputStream reopenImageInputStream(InputStream input) {
- IOUtils.closeQuietly(input);
- return openImageInputStream();
- }
-
- @Override
- public String getImageLoaderCacheKey() {
- return fileUrl;
- }
- };
- }
- }
-}
diff --git a/src/de/danoeh/antennapod/asynctask/ImageLoader.java b/src/de/danoeh/antennapod/asynctask/ImageLoader.java
deleted file mode 100644
index 6c60b7b1f..000000000
--- a/src/de/danoeh/antennapod/asynctask/ImageLoader.java
+++ /dev/null
@@ -1,246 +0,0 @@
-package de.danoeh.antennapod.asynctask;
-
-import android.annotation.SuppressLint;
-import android.app.ActivityManager;
-import android.content.Context;
-import android.os.Handler;
-import android.support.v4.util.LruCache;
-import android.util.Log;
-import android.widget.ImageView;
-import de.danoeh.antennapod.BuildConfig;
-import de.danoeh.antennapod.PodcastApp;
-import de.danoeh.antennapod.R;
-
-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;
-
- public static final int IMAGE_TYPE_THUMBNAIL = 0;
- public static final int IMAGE_TYPE_COVER = 1;
-
- /**
- * Used by loadThumbnailBitmap and loadCoverBitmap to denote an ImageView that displays the default image resource.
- * This is the case if the given source to load the image from was null or did not return any image data.
- */
- private static final Object DEFAULT_IMAGE_RESOURCE_TAG = new Object();
-
- 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.
- */
-
- 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;
-
- private LruCache<String, CachedBitmap> coverCache;
- private LruCache<String, CachedBitmap> thumbnailCache;
-
- private ImageLoader() {
- handler = new Handler();
- executor = createExecutor();
-
- coverCache = new LruCache<String, CachedBitmap>(1);
-
- thumbnailCache = new LruCache<String, CachedBitmap>(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());
-
- }
-
- };
- }
-
- 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;
- }
- });
- }
-
- 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, 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);
- target.setTag(R.id.imageloader_key, DEFAULT_IMAGE_RESOURCE_TAG);
- }
- }
-
- /**
- * 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);
- target.setTag(R.id.imageloader_key, DEFAULT_IMAGE_RESOURCE_TAG);
- }
- }
-
- public void clearExecutorQueue() {
- executor.shutdownNow();
- if (BuildConfig.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;
- }
-
- private CachedBitmap getBitmapFromThumbnailCache(String key) {
- return thumbnailCache.get(key);
- }
-
- public void addBitmapToThumbnailCache(String key, CachedBitmap bitmap) {
- thumbnailCache.put(key, bitmap);
- }
-
- public boolean isInCoverCache(String fileUrl) {
- return coverCache.get(fileUrl) != null;
- }
-
- private CachedBitmap getBitmapFromCoverCache(String key) {
- return coverCache.get(key);
- }
-
- public void addBitmapToCoverCache(String key, CachedBitmap bitmap) {
- coverCache.put(key, bitmap);
- }
-
- private int getDefaultCoverResource(Context context) {
- return android.R.color.transparent;
- }
-
- /**
- * 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();
- }
-
-}
diff --git a/src/de/danoeh/antennapod/asynctask/PicassoImageResource.java b/src/de/danoeh/antennapod/asynctask/PicassoImageResource.java
new file mode 100644
index 000000000..84179cfcb
--- /dev/null
+++ b/src/de/danoeh/antennapod/asynctask/PicassoImageResource.java
@@ -0,0 +1,25 @@
+package de.danoeh.antennapod.asynctask;
+
+import android.net.Uri;
+
+/**
+ * Classes that implement this interface provide access to an image resource that can
+ * be loaded by the Picasso library.
+ */
+public interface PicassoImageResource {
+
+ /**
+ * This scheme should be used by PicassoImageResources to
+ * indicate that the image Uri points to a file that is not an image
+ * (e.g. a media file). This workaround is needed so that the Picasso library
+ * loads these Uri with a Downloader instead of trying to load it directly.
+ * <p/>
+ * For example implementations, see FeedMedia or ExternalMedia.
+ */
+ public static final String SCHEME_MEDIA = "media";
+
+ /**
+ * Returns a Uri to the image or null if no image is available.
+ */
+ public Uri getImageUri();
+}
diff --git a/src/de/danoeh/antennapod/asynctask/PicassoProvider.java b/src/de/danoeh/antennapod/asynctask/PicassoProvider.java
new file mode 100644
index 000000000..fe70a9343
--- /dev/null
+++ b/src/de/danoeh/antennapod/asynctask/PicassoProvider.java
@@ -0,0 +1,137 @@
+package de.danoeh.antennapod.asynctask;
+
+import android.content.Context;
+import android.media.MediaMetadataRetriever;
+import android.net.Uri;
+import android.util.Log;
+import android.webkit.MimeTypeMap;
+
+import com.squareup.picasso.Cache;
+import com.squareup.picasso.Downloader;
+import com.squareup.picasso.LruCache;
+import com.squareup.picasso.OkHttpDownloader;
+import com.squareup.picasso.Picasso;
+
+import org.apache.commons.io.FilenameUtils;
+import org.apache.commons.lang3.StringUtils;
+import org.apache.commons.lang3.Validate;
+
+import java.io.BufferedInputStream;
+import java.io.ByteArrayInputStream;
+import java.io.File;
+import java.io.FileInputStream;
+import java.io.IOException;
+import java.util.concurrent.ExecutorService;
+import java.util.concurrent.Executors;
+
+/**
+ * Provides access to Picasso instances.
+ */
+public class PicassoProvider {
+ private static final String TAG = "PicassoProvider";
+
+ private static final boolean DEBUG = false;
+
+ private static ExecutorService executorService;
+ private static Cache memoryCache;
+
+ private static Picasso defaultPicassoInstance;
+ private static Picasso mediaMetadataPicassoInstance;
+
+ private static synchronized ExecutorService getExecutorService() {
+ if (executorService == null) {
+ executorService = Executors.newFixedThreadPool(3);
+ }
+ return executorService;
+ }
+
+ private static synchronized Cache getMemoryCache(Context context) {
+ if (memoryCache == null) {
+ memoryCache = new LruCache(context);
+ }
+ return memoryCache;
+ }
+
+ /**
+ * Returns a Picasso instance that uses an OkHttpDownloader. This instance can only load images
+ * from image files.
+ * <p/>
+ * This instance should be used as long as no images from media files are loaded.
+ */
+ public static synchronized Picasso getDefaultPicassoInstance(Context context) {
+ Validate.notNull(context);
+ if (defaultPicassoInstance == null) {
+ defaultPicassoInstance = new Picasso.Builder(context)
+ .indicatorsEnabled(DEBUG)
+ .loggingEnabled(DEBUG)
+ .downloader(new OkHttpDownloader(context))
+ .executor(getExecutorService())
+ .memoryCache(getMemoryCache(context))
+ .listener(new Picasso.Listener() {
+ @Override
+ public void onImageLoadFailed(Picasso picasso, Uri uri, Exception e) {
+ Log.e(TAG, "Failed to load Uri:" + uri.toString());
+ e.printStackTrace();
+ }
+ })
+ .build();
+ }
+ return defaultPicassoInstance;
+ }
+
+ /**
+ * Returns a Picasso instance that uses a MediaMetadataRetriever if the given Uri is a media file
+ * and a default OkHttpDownloader otherwise.
+ */
+ public static synchronized Picasso getMediaMetadataPicassoInstance(Context context) {
+ Validate.notNull(context);
+ if (mediaMetadataPicassoInstance == null) {
+ mediaMetadataPicassoInstance = new Picasso.Builder(context)
+ .indicatorsEnabled(DEBUG)
+ .loggingEnabled(DEBUG)
+ .downloader(new MediaMetadataDownloader(context))
+ .executor(getExecutorService())
+ .memoryCache(getMemoryCache(context))
+ .listener(new Picasso.Listener() {
+ @Override
+ public void onImageLoadFailed(Picasso picasso, Uri uri, Exception e) {
+ Log.e(TAG, "Failed to load Uri:" + uri.toString());
+ e.printStackTrace();
+ }
+ })
+ .build();
+ }
+ return mediaMetadataPicassoInstance;
+ }
+
+ private static class MediaMetadataDownloader implements Downloader {
+
+ private static final String TAG = "MediaMetadataDownloader";
+
+ private final OkHttpDownloader okHttpDownloader;
+
+ public MediaMetadataDownloader(Context context) {
+ Validate.notNull(context);
+ okHttpDownloader = new OkHttpDownloader(context);
+ }
+
+ @Override
+ public Response load(Uri uri, boolean b) throws IOException {
+ if (StringUtils.equals(uri.getScheme(), PicassoImageResource.SCHEME_MEDIA)) {
+ String type = MimeTypeMap.getSingleton().getMimeTypeFromExtension(FilenameUtils.getExtension(uri.getLastPathSegment()));
+ if (StringUtils.startsWith(type, "image")) {
+ File imageFile = new File(uri.toString());
+ return new Response(new BufferedInputStream(new FileInputStream(imageFile)), true, imageFile.length());
+ } else {
+ MediaMetadataRetriever mmr = new MediaMetadataRetriever();
+ mmr.setDataSource(uri.getPath());
+ byte[] data = mmr.getEmbeddedPicture();
+ mmr.release();
+ return new Response(new ByteArrayInputStream(data), true, data.length);
+ }
+ }
+
+ return okHttpDownloader.load(uri, b);
+ }
+ }
+}