From 99068c82ccf1ac03153e5eb6e79ff2c610caf3cf Mon Sep 17 00:00:00 2001
From: daniel oeh <daniel.oeh@gmail.com>
Date: Mon, 10 Nov 2014 22:00:49 +0100
Subject: Updated picasso library

Use RequestHandler instead of Downloader for extracting bitmaps from media files
---
 .../antennapod/core/asynctask/PicassoProvider.java | 241 +++++++++++++--------
 .../core/service/playback/PlaybackService.java     |  53 +++--
 2 files changed, 174 insertions(+), 120 deletions(-)

(limited to 'core/src')

diff --git a/core/src/main/java/de/danoeh/antennapod/core/asynctask/PicassoProvider.java b/core/src/main/java/de/danoeh/antennapod/core/asynctask/PicassoProvider.java
index 6ace92800..6d9353a93 100644
--- a/core/src/main/java/de/danoeh/antennapod/core/asynctask/PicassoProvider.java
+++ b/core/src/main/java/de/danoeh/antennapod/core/asynctask/PicassoProvider.java
@@ -1,26 +1,26 @@
 package de.danoeh.antennapod.core.asynctask;
 
+import android.content.ContentResolver;
 import android.content.Context;
+import android.graphics.Bitmap;
+import android.graphics.BitmapFactory;
 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 com.squareup.picasso.Request;
+import com.squareup.picasso.RequestHandler;
 
-import org.apache.commons.io.FilenameUtils;
+import org.apache.commons.io.IOUtils;
 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.io.InputStream;
 import java.util.concurrent.ExecutorService;
 import java.util.concurrent.Executors;
 
@@ -35,9 +35,6 @@ public class PicassoProvider {
     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);
@@ -52,101 +49,161 @@ public class PicassoProvider {
         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;
-    }
+    private static volatile boolean picassoSetup = false;
 
-    /**
-     * 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();
+    public static synchronized void setupPicassoInstance(Context appContext) {
+        if (picassoSetup) {
+            return;
         }
-        return mediaMetadataPicassoInstance;
+        Picasso picasso = new Picasso.Builder(appContext)
+                .indicatorsEnabled(DEBUG)
+                .loggingEnabled(DEBUG)
+                .downloader(new OkHttpDownloader(appContext))
+                .addRequestHandler(new MediaRequestHandler(appContext))
+                .executor(getExecutorService())
+                .memoryCache(getMemoryCache(appContext))
+                .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();
+        Picasso.setSingletonInstance(picasso);
+        picassoSetup = true;
     }
 
-    private static class MediaMetadataDownloader implements Downloader {
+    private static class MediaRequestHandler extends RequestHandler {
+
+        final MediaMetadataRetriever mmr;
+        final Context context;
 
-        private static final String TAG = "MediaMetadataDownloader";
+        public MediaRequestHandler(Context context) {
+            super();
+            this.context = context;
+            mmr = new MediaMetadataRetriever();
+        }
 
-        private final OkHttpDownloader okHttpDownloader;
+        @Override
+        protected void finalize() throws Throwable {
+            super.finalize();
+            mmr.release();
+        }
 
-        public MediaMetadataDownloader(Context context) {
-            Validate.notNull(context);
-            okHttpDownloader = new OkHttpDownloader(context);
+        @Override
+        public boolean canHandleRequest(Request data) {
+            return StringUtils.equals(data.uri.getScheme(), PicassoImageResource.SCHEME_MEDIA);
         }
 
         @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());
+        public Result load(Request data) throws IOException {
+            Bitmap bitmap = null;
+            mmr.setDataSource(data.uri.getPath());
+            byte[] image = mmr.getEmbeddedPicture();
+            if (image != null) {
+                bitmap = decodeStreamFromByteArray(data, image);
+            }
+            if (bitmap == null) {
+                // check for fallback Uri
+                String fallbackParam = data.uri.getQueryParameter(PicassoImageResource.PARAM_FALLBACK);
+
+                if (fallbackParam != null) {
+                    Uri fallback = Uri.parse(fallbackParam);
+                    bitmap = decodeStreamFromFile(data, fallback);
+                }
+            }
+            return new Result(bitmap, Picasso.LoadedFrom.DISK);
+
+        }
+
+        /* Copied/Adapted from Picasso RequestHandler classes  */
+
+        private Bitmap decodeStreamFromByteArray(Request data, byte[] bytes) throws IOException {
+
+            final BitmapFactory.Options options = createBitmapOptions(data);
+            final ByteArrayInputStream in = new ByteArrayInputStream(bytes);
+            in.mark(0);
+            if (requiresInSampleSize(options)) {
+                try {
+                    BitmapFactory.decodeStream(in, null, options);
+                } finally {
+                    in.reset();
+                }
+                calculateInSampleSize(data.targetWidth, data.targetHeight, options, data);
+            }
+            try {
+                return BitmapFactory.decodeStream(in, null, options);
+            } finally {
+                IOUtils.closeQuietly(in);
+            }
+        }
+
+        private Bitmap decodeStreamFromFile(Request data, Uri uri) throws IOException {
+            ContentResolver contentResolver = context.getContentResolver();
+            final BitmapFactory.Options options = createBitmapOptions(data);
+            if (requiresInSampleSize(options)) {
+                InputStream is = null;
+                try {
+                    is = contentResolver.openInputStream(uri);
+                    BitmapFactory.decodeStream(is, null, options);
+                } finally {
+                    IOUtils.closeQuietly(is);
+                }
+                calculateInSampleSize(data.targetWidth, data.targetHeight, options, data);
+            }
+            InputStream is = contentResolver.openInputStream(uri);
+            try {
+                return BitmapFactory.decodeStream(is, null, options);
+            } finally {
+                IOUtils.closeQuietly(is);
+            }
+        }
+
+        private BitmapFactory.Options createBitmapOptions(Request data) {
+            final boolean justBounds = data.hasSize();
+            final boolean hasConfig = data.config != null;
+            BitmapFactory.Options options = null;
+            if (justBounds || hasConfig) {
+                options = new BitmapFactory.Options();
+                options.inJustDecodeBounds = justBounds;
+                if (hasConfig) {
+                    options.inPreferredConfig = data.config;
+                }
+            }
+            return options;
+        }
+
+        private static boolean requiresInSampleSize(BitmapFactory.Options options) {
+            return options != null && options.inJustDecodeBounds;
+        }
+
+        private static void calculateInSampleSize(int reqWidth, int reqHeight, BitmapFactory.Options options,
+                                                  Request request) {
+            calculateInSampleSize(reqWidth, reqHeight, options.outWidth, options.outHeight, options,
+                    request);
+        }
+
+        private static void calculateInSampleSize(int reqWidth, int reqHeight, int width, int height,
+                                                  BitmapFactory.Options options, Request request) {
+            int sampleSize = 1;
+            if (height > reqHeight || width > reqWidth) {
+                final int heightRatio;
+                final int widthRatio;
+                if (reqHeight == 0) {
+                    sampleSize = (int) Math.floor((float) width / (float) reqWidth);
+                } else if (reqWidth == 0) {
+                    sampleSize = (int) Math.floor((float) height / (float) reqHeight);
                 } else {
-                    MediaMetadataRetriever mmr = new MediaMetadataRetriever();
-                    mmr.setDataSource(uri.getPath());
-                    byte[] data = mmr.getEmbeddedPicture();
-                    mmr.release();
-
-                    if (data != null) {
-                        return new Response(new ByteArrayInputStream(data), true, data.length);
-                    } else {
-
-                        // check for fallback Uri
-                        String fallbackParam = uri.getQueryParameter(PicassoImageResource.PARAM_FALLBACK);
-
-                        if (fallbackParam != null) {
-                            String fallback = Uri.decode(Uri.parse(fallbackParam).getPath());
-                            if (fallback != null) {
-                                File imageFile = new File(fallback);
-                                return new Response(new BufferedInputStream(new FileInputStream(imageFile)), true, imageFile.length());
-                            }
-                        }
-                        return null;
-                    }
+                    heightRatio = (int) Math.floor((float) height / (float) reqHeight);
+                    widthRatio = (int) Math.floor((float) width / (float) reqWidth);
+                    sampleSize = request.centerInside
+                            ? Math.max(heightRatio, widthRatio)
+                            : Math.min(heightRatio, widthRatio);
                 }
             }
-            return okHttpDownloader.load(uri, b);
+            options.inSampleSize = sampleSize;
+            options.inJustDecodeBounds = false;
         }
     }
 }
diff --git a/core/src/main/java/de/danoeh/antennapod/core/service/playback/PlaybackService.java b/core/src/main/java/de/danoeh/antennapod/core/service/playback/PlaybackService.java
index 1261c21fe..a5560e3fb 100644
--- a/core/src/main/java/de/danoeh/antennapod/core/service/playback/PlaybackService.java
+++ b/core/src/main/java/de/danoeh/antennapod/core/service/playback/PlaybackService.java
@@ -29,6 +29,8 @@ import android.view.KeyEvent;
 import android.view.SurfaceHolder;
 import android.widget.Toast;
 
+import com.squareup.picasso.Picasso;
+
 import org.apache.commons.lang3.StringUtils;
 
 import java.io.IOException;
@@ -37,7 +39,6 @@ import java.util.List;
 import de.danoeh.antennapod.core.BuildConfig;
 import de.danoeh.antennapod.core.ClientConfig;
 import de.danoeh.antennapod.core.R;
-import de.danoeh.antennapod.core.asynctask.PicassoProvider;
 import de.danoeh.antennapod.core.feed.Chapter;
 import de.danoeh.antennapod.core.feed.FeedItem;
 import de.danoeh.antennapod.core.feed.FeedMedia;
@@ -291,10 +292,9 @@ public class PlaybackService extends Service {
             case KeyEvent.KEYCODE_MEDIA_PLAY_PAUSE:
                 if (status == PlayerStatus.PLAYING) {
                     if (UserPreferences.isPersistNotify()) {
-                      mediaPlayer.pause(false, true);
-                    }
-                    else {
-                      mediaPlayer.pause(true, true);
+                        mediaPlayer.pause(false, true);
+                    } else {
+                        mediaPlayer.pause(true, true);
                     }
                 } else if (status == PlayerStatus.PAUSED || status == PlayerStatus.PREPARED) {
                     mediaPlayer.resume();
@@ -315,12 +315,11 @@ public class PlaybackService extends Service {
                 break;
             case KeyEvent.KEYCODE_MEDIA_PAUSE:
                 if (status == PlayerStatus.PLAYING) {
-                  if (UserPreferences.isPersistNotify()) {
-                    mediaPlayer.pause(false, true);
-                  }
-                  else {
-                    mediaPlayer.pause(true, true);
-                  }
+                    if (UserPreferences.isPersistNotify()) {
+                        mediaPlayer.pause(false, true);
+                    } else {
+                        mediaPlayer.pause(true, true);
+                    }
                 }
                 break;
             case KeyEvent.KEYCODE_MEDIA_NEXT:
@@ -332,11 +331,11 @@ public class PlaybackService extends Service {
                 mediaPlayer.seekDelta(-UserPreferences.getSeekDeltaMs());
                 break;
             case KeyEvent.KEYCODE_MEDIA_STOP:
-              if (status == PlayerStatus.PLAYING) {
-                mediaPlayer.pause(true, true);
-              }
-              stopForeground(true); // gets rid of persistent notification
-              break;
+                if (status == PlayerStatus.PLAYING) {
+                    mediaPlayer.pause(true, true);
+                }
+                stopForeground(true); // gets rid of persistent notification
+                break;
             default:
                 if (info.playable != null && info.playerStatus == PlayerStatus.PLAYING) {   // only notify the user about an unknown key event if it is actually doing something
                     String message = String.format(getResources().getString(R.string.unknown_media_key), keycode);
@@ -411,11 +410,10 @@ public class PlaybackService extends Service {
                     saveCurrentPosition(false, 0);
                     taskManager.cancelWidgetUpdater();
                     if (UserPreferences.isPersistNotify() && android.os.Build.VERSION.SDK_INT >= Build.VERSION_CODES.JELLY_BEAN) {
-                      // do not remove notification on pause based on user pref and whether android version supports expanded notifications
-                    }
-                    else {
-                      // remove notifcation on pause
-                      stopForeground(true);    
+                        // do not remove notification on pause based on user pref and whether android version supports expanded notifications
+                    } else {
+                        // remove notifcation on pause
+                        stopForeground(true);
                     }
                     break;
 
@@ -709,7 +707,7 @@ public class PlaybackService extends Service {
                         try {
                             int iconSize = getResources().getDimensionPixelSize(
                                     android.R.dimen.notification_large_icon_width);
-                            icon = PicassoProvider.getMediaMetadataPicassoInstance(PlaybackService.this)
+                            icon = Picasso.with(PlaybackService.this)
                                     .load(info.playable.getImageUri())
                                     .resize(iconSize, iconSize)
                                     .get();
@@ -1005,12 +1003,11 @@ public class PlaybackService extends Service {
      */
     private void pauseIfPauseOnDisconnect() {
         if (UserPreferences.isPauseOnHeadsetDisconnect()) {
-          if (UserPreferences.isPersistNotify()) {
-            mediaPlayer.pause(false, true);
-          }
-          else {
-            mediaPlayer.pause(true, true);
-          }
+            if (UserPreferences.isPersistNotify()) {
+                mediaPlayer.pause(false, true);
+            } else {
+                mediaPlayer.pause(true, true);
+            }
         }
     }
 
-- 
cgit v1.2.3