Added support for episode images

This commit is contained in:
daniel oeh 2013-03-16 19:26:13 +01:00
parent d0bfd7c02d
commit 60f7dd332d
27 changed files with 311 additions and 179 deletions

View File

@ -1,7 +1,5 @@
package de.danoeh.antennapod.activity;
import java.io.File;
import android.content.Intent;
import android.content.res.TypedArray;
import android.os.Bundle;
@ -214,7 +212,7 @@ public class AudioplayerActivity extends MediaplayerActivity {
@Override
public void run() {
ImageLoader.getInstance().loadThumbnailBitmap(
media.getImageFileUrl(), butNavLeft);
media, butNavLeft);
}
});
butNavRight.setImageDrawable(drawables.getDrawable(1));
@ -226,7 +224,7 @@ public class AudioplayerActivity extends MediaplayerActivity {
@Override
public void run() {
ImageLoader.getInstance().loadThumbnailBitmap(
media.getImageFileUrl(), butNavLeft);
media, butNavLeft);
}
});
butNavRight.setImageDrawable(drawables.getDrawable(0));

View File

@ -4,26 +4,20 @@ import java.util.ArrayList;
import android.content.Context;
import android.content.Intent;
import android.graphics.drawable.Drawable;
import android.os.Bundle;
import android.support.v4.app.Fragment;
import android.support.v4.app.FragmentManager;
import android.support.v4.app.FragmentPagerAdapter;
import android.support.v4.app.FragmentStatePagerAdapter;
import android.support.v4.app.FragmentTransaction;
import android.support.v4.view.ViewPager;
import android.util.Log;
import android.view.View;
import com.actionbarsherlock.app.ActionBar;
import com.actionbarsherlock.app.ActionBar.Tab;
import com.actionbarsherlock.app.ActionBar.TabListener;
import com.actionbarsherlock.app.SherlockFragmentActivity;
import com.actionbarsherlock.view.Menu;
import com.actionbarsherlock.view.MenuInflater;
import com.actionbarsherlock.view.MenuItem;
import com.actionbarsherlock.view.Window;
import com.viewpagerindicator.TabPageIndicator;
import de.danoeh.antennapod.AppConfig;
import de.danoeh.antennapod.R;

View File

@ -155,10 +155,9 @@ public class OrganizeQueueActivity extends SherlockListActivity {
holder.title.setText(item.getTitle());
holder.feedTitle.setText(item.getFeed().getTitle());
holder.feedImage.setTag((item.getFeed().getImage() != null) ? item
.getFeed().getImage().getFile_url() : null);
holder.feedImage.setTag(item.getImageLoaderCacheKey());
ImageLoader.getInstance().loadThumbnailBitmap(
item.getFeed().getImage(),
item,
holder.feedImage,
(int) convertView.getResources().getDimension(
R.dimen.thumbnail_length));

View File

@ -7,7 +7,6 @@ import java.util.List;
import android.content.Context;
import android.content.Intent;
import android.content.SharedPreferences;
import android.content.res.Resources.Theme;
import android.net.wifi.WifiConfiguration;
import android.net.wifi.WifiManager;

View File

@ -8,20 +8,14 @@ import android.text.format.DateUtils;
import android.view.LayoutInflater;
import android.view.View;
import android.view.ViewGroup;
import android.view.View.OnClickListener;
import android.widget.Adapter;
import android.widget.BaseAdapter;
import android.widget.ImageButton;
import android.widget.ImageView;
import android.widget.TextView;
import de.danoeh.antennapod.R;
import de.danoeh.antennapod.adapter.InternalFeedItemlistAdapter.Holder;
import de.danoeh.antennapod.feed.FeedItem;
import de.danoeh.antennapod.feed.FeedManager;
import de.danoeh.antennapod.feed.MediaType;
import de.danoeh.antennapod.storage.DownloadRequester;
import de.danoeh.antennapod.util.Converter;
import de.danoeh.antennapod.util.ThemeUtils;
public class DefaultFeedItemlistAdapter extends BaseAdapter {

View File

@ -166,11 +166,10 @@ public class ExternalEpisodesListAdapter extends BaseExpandableListAdapter {
holder.downloadStatus.setVisibility(View.INVISIBLE);
holder.lenSize.setVisibility(View.INVISIBLE);
}
holder.feedImage.setTag((item.getFeed().getImage() != null) ? item
.getFeed().getImage().getFile_url() : null);
holder.feedImage.setTag(item.getImageLoaderCacheKey());
ImageLoader.getInstance().loadThumbnailBitmap(
item.getFeed().getImage(),
item,
holder.feedImage,
(int) convertView.getResources().getDimension(
R.dimen.thumbnail_length));

View File

@ -1,7 +1,5 @@
package de.danoeh.antennapod.asynctask;
import java.io.File;
import android.content.res.TypedArray;
import android.graphics.BitmapFactory;
import android.os.Handler;
@ -9,6 +7,7 @@ import android.util.Log;
import android.widget.ImageView;
import de.danoeh.antennapod.AppConfig;
import de.danoeh.antennapod.R;
import de.danoeh.antennapod.asynctask.ImageLoader.ImageWorkerTaskResource;
import de.danoeh.antennapod.util.BitmapDecoder;
public class BitmapDecodeWorkerTask extends Thread {
@ -22,18 +21,18 @@ public class BitmapDecodeWorkerTask extends Thread {
private ImageView target;
protected CachedBitmap cBitmap;
protected String fileUrl;
protected ImageLoader.ImageWorkerTaskResource imageResource;
private Handler handler;
private final int defaultCoverResource;
public BitmapDecodeWorkerTask(Handler handler, ImageView target,
String fileUrl, int length, int imageType) {
ImageWorkerTaskResource imageResource, int length, int imageType) {
super();
this.handler = handler;
this.target = target;
this.fileUrl = fileUrl;
this.imageResource = imageResource;
this.PREFERRED_LENGTH = length;
this.imageType = imageType;
TypedArray res = target.getContext().obtainStyledAttributes(
@ -47,7 +46,8 @@ public class BitmapDecodeWorkerTask extends Thread {
* before the bitmap was decoded
*/
protected boolean tagsMatching(ImageView target) {
return target.getTag() == null || target.getTag() == fileUrl;
return target.getTag() == null
|| target.getTag() == imageResource.getImageLoaderCacheKey();
}
protected void onPostExecute() {
@ -62,31 +62,19 @@ public class BitmapDecodeWorkerTask extends Thread {
@Override
public void run() {
File f = null;
if (fileUrl != null) {
f = new File(fileUrl);
}
if (fileUrl != null && f.exists()) {
cBitmap = new CachedBitmap(BitmapDecoder.decodeBitmap(
PREFERRED_LENGTH, fileUrl), 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 (AppConfig.DEBUG)
Log.d(TAG, "Finished loading bitmaps");
cBitmap = new CachedBitmap(BitmapDecoder.decodeBitmapFromWorkerTaskResource(
PREFERRED_LENGTH, imageResource), PREFERRED_LENGTH);
if (cBitmap.getBitmap() != null) {
storeBitmapInCache(cBitmap);
} else {
if (fileUrl == null) {
Log.w(TAG, "File URL is null");
} else {
Log.w(TAG, "File does not exist anymore.");
}
onInvalidFileUrl();
Log.w(TAG, "Could not load bitmap. Using default image.");
cBitmap = new CachedBitmap(BitmapFactory.decodeResource(
target.getResources(), defaultCoverResource),
PREFERRED_LENGTH);
}
if (AppConfig.DEBUG)
Log.d(TAG, "Finished loading bitmaps");
endBackgroundTask();
}
@ -101,8 +89,7 @@ public class BitmapDecodeWorkerTask extends Thread {
});
}
protected void onInvalidFileUrl() {
Log.e(TAG, "FeedImage has no valid file url. Using default image");
protected void onInvalidStream() {
cBitmap = new CachedBitmap(BitmapFactory.decodeResource(
target.getResources(), defaultCoverResource), PREFERRED_LENGTH);
}
@ -110,9 +97,10 @@ public class BitmapDecodeWorkerTask extends Thread {
protected void storeBitmapInCache(CachedBitmap cb) {
ImageLoader loader = ImageLoader.getInstance();
if (imageType == ImageLoader.IMAGE_TYPE_COVER) {
loader.addBitmapToCoverCache(fileUrl, cb);
loader.addBitmapToCoverCache(imageResource.getImageLoaderCacheKey(), cb);
} else if (imageType == ImageLoader.IMAGE_TYPE_THUMBNAIL) {
loader.addBitmapToThumbnailCache(fileUrl, cb);
loader.addBitmapToThumbnailCache(imageResource.getImageLoaderCacheKey(), cb);
}
}
}

View File

@ -1,5 +1,6 @@
package de.danoeh.antennapod.asynctask;
import java.io.InputStream;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
import java.util.concurrent.ThreadFactory;
@ -15,7 +16,6 @@ import android.widget.ImageView;
import de.danoeh.antennapod.AppConfig;
import de.danoeh.antennapod.PodcastApp;
import de.danoeh.antennapod.R;
import de.danoeh.antennapod.feed.FeedImage;
/** Caches and loads FeedImage bitmaps in the background */
public class ImageLoader {
@ -90,18 +90,8 @@ public class ImageLoader {
* ImageView's size has already been set or inside a Runnable which is
* posted to the ImageView's message queue.
*/
public void loadCoverBitmap(String fileUrl, ImageView target) {
loadCoverBitmap(fileUrl, target, target.getHeight());
}
public void loadCoverBitmap(FeedImage image, ImageView target) {
loadCoverBitmap((image != null) ? image.getFile_url() : null, target,
target.getHeight());
}
public void loadCoverBitmap(FeedImage image, ImageView target, int length) {
loadCoverBitmap((image != null) ? image.getFile_url() : null, target,
length);
public void loadCoverBitmap(ImageWorkerTaskResource source, ImageView target) {
loadCoverBitmap(source, target, target.getHeight());
}
/**
@ -110,18 +100,19 @@ public class ImageLoader {
* ImageView's size has already been set or inside a Runnable which is
* posted to the ImageView's message queue.
*/
public void loadCoverBitmap(String fileUrl, ImageView target, int length) {
public void loadCoverBitmap(ImageWorkerTaskResource source,
ImageView target, int length) {
final int defaultCoverResource = getDefaultCoverResource(target
.getContext());
if (fileUrl != null) {
CachedBitmap cBitmap = getBitmapFromCoverCache(fileUrl);
if (source != null && source.getImageLoaderCacheKey() != null) {
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, fileUrl, length, IMAGE_TYPE_COVER);
handler, target, source, length, IMAGE_TYPE_COVER);
executor.submit(worker);
}
} else {
@ -135,19 +126,9 @@ public class ImageLoader {
* 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(String fileUrl, ImageView target) {
loadThumbnailBitmap(fileUrl, target, target.getHeight());
}
public void loadThumbnailBitmap(FeedImage image, ImageView target) {
loadThumbnailBitmap((image != null) ? image.getFile_url() : null,
target, target.getHeight());
}
public void loadThumbnailBitmap(FeedImage image, ImageView target,
int length) {
loadThumbnailBitmap((image != null) ? image.getFile_url() : null,
target, length);
public void loadThumbnailBitmap(ImageWorkerTaskResource source,
ImageView target) {
loadThumbnailBitmap(source, target, target.getHeight());
}
/**
@ -156,18 +137,19 @@ public class ImageLoader {
* 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(String fileUrl, ImageView target, int length) {
public void loadThumbnailBitmap(ImageWorkerTaskResource source,
ImageView target, int length) {
final int defaultCoverResource = getDefaultCoverResource(target
.getContext());
if (fileUrl != null) {
CachedBitmap cBitmap = getBitmapFromThumbnailCache(fileUrl);
if (source != null && source.getImageLoaderCacheKey() != null) {
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, fileUrl, length, IMAGE_TYPE_THUMBNAIL);
handler, target, source, length, IMAGE_TYPE_THUMBNAIL);
executor.submit(worker);
}
} else {
@ -220,4 +202,31 @@ public class ImageLoader {
return defaultCoverResource;
}
/**
* 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();
}
}

View File

@ -1,6 +1,18 @@
package de.danoeh.antennapod.feed;
public class FeedImage extends FeedFile {
import java.io.File;
import java.io.FileInputStream;
import java.io.FileNotFoundException;
import java.io.InputStream;
import org.apache.commons.io.IOUtils;
import de.danoeh.antennapod.asynctask.ImageLoader;
;
public class FeedImage extends FeedFile implements
ImageLoader.ImageWorkerTaskResource {
public static final int FEEDFILETYPE_FEEDIMAGE = 1;
protected String title;
@ -53,4 +65,30 @@ public class FeedImage extends FeedFile {
this.feed = feed;
}
@Override
public InputStream openImageInputStream() {
if (file_url != null) {
File file = new File(file_url);
if (file.exists()) {
try {
return new FileInputStream(file_url);
} catch (FileNotFoundException e) {
e.printStackTrace();
}
}
}
return null;
}
@Override
public String getImageLoaderCacheKey() {
return file_url;
}
@Override
public InputStream reopenImageInputStream(InputStream input) {
IOUtils.closeQuietly(input);
return openImageInputStream();
}
}

View File

@ -1,10 +1,11 @@
package de.danoeh.antennapod.feed;
import java.io.InputStream;
import java.lang.ref.SoftReference;
import java.util.Date;
import java.util.List;
import de.danoeh.antennapod.preferences.PlaybackPreferences;
import de.danoeh.antennapod.asynctask.ImageLoader;
/**
* Data Object for a XML message
@ -12,7 +13,8 @@ import de.danoeh.antennapod.preferences.PlaybackPreferences;
* @author daniel
*
*/
public class FeedItem extends FeedComponent {
public class FeedItem extends FeedComponent implements
ImageLoader.ImageWorkerTaskResource {
/** The id/guid that can be found in the rss/atom feed. Might not be set. */
private String itemIdentifier;
@ -241,4 +243,40 @@ public class FeedItem extends FeedComponent {
}
return (isRead() ? State.READ : State.NEW);
}
@Override
public InputStream openImageInputStream() {
InputStream out = null;
if (hasMedia()) {
out = media.openImageInputStream();
}
if (out == null && feed.getImage() != null) {
out = feed.getImage().openImageInputStream();
}
return out;
}
@Override
public InputStream reopenImageInputStream(InputStream input) {
InputStream out = null;
if (hasMedia()) {
out = media.reopenImageInputStream(input);
}
if (out == null && feed.getImage() != null) {
out = feed.getImage().reopenImageInputStream(input);
}
return out;
}
@Override
public String getImageLoaderCacheKey() {
String out = null;
if (hasMedia()) {
out = media.getImageLoaderCacheKey();
}
if (out == null && feed.getImage() != null) {
out = feed.getImage().getImageLoaderCacheKey();
}
return out;
}
}

View File

@ -1,5 +1,7 @@
package de.danoeh.antennapod.feed;
import java.io.FileInputStream;
import java.io.InputStream;
import java.util.Date;
import java.util.List;
@ -225,15 +227,6 @@ public class FeedMedia extends FeedFile implements Playable {
return getItem().getFeed().getTitle();
}
@Override
public String getImageFileUrl() {
if (getItem().getFeed().getImage() != null) {
return getItem().getFeed().getImage().getFile_url();
} else {
return null;
}
}
@Override
public Object getIdentifier() {
return id;
@ -329,4 +322,37 @@ public class FeedMedia extends FeedFile implements Playable {
return new FeedMedia[size];
}
};
@Override
public InputStream openImageInputStream() {
InputStream out = new Playable.DefaultPlayableImageLoader(this)
.openImageInputStream();
if (out == null) {
if (item.getFeed().getImage() != null) {
return item.getFeed().getImage().openImageInputStream();
}
}
return out;
}
@Override
public String getImageLoaderCacheKey() {
String out = new Playable.DefaultPlayableImageLoader(this).getImageLoaderCacheKey();
if (out == null) {
if (item.getFeed().getImage() != null) {
return item.getFeed().getImage().getImageLoaderCacheKey();
}
}
return out;
}
@Override
public InputStream reopenImageInputStream(InputStream input) {
if (input instanceof FileInputStream) {
return item.getFeed().getImage().reopenImageInputStream(input);
} else {
return new Playable.DefaultPlayableImageLoader(this)
.reopenImageInputStream(input);
}
}
}

View File

@ -65,7 +65,7 @@ public class CoverFragment extends SherlockFragment implements
@Override
public void run() {
ImageLoader.getInstance().loadCoverBitmap(
media.getImageFileUrl(), imgvCover);
media, imgvCover);
}
});
} else {

View File

@ -209,7 +209,7 @@ public class ExternalPlayerFragment extends SherlockFragment {
if (media != null) {
txtvTitle.setText(media.getEpisodeTitle());
ImageLoader.getInstance().loadThumbnailBitmap(
media.getImageFileUrl(),
media,
imgvCover,
(int) getActivity().getResources().getDimension(
R.dimen.external_player_height));

View File

@ -17,8 +17,8 @@ import com.actionbarsherlock.app.SherlockListFragment;
import de.danoeh.antennapod.AppConfig;
import de.danoeh.antennapod.R;
import de.danoeh.antennapod.activity.ItemviewActivity;
import de.danoeh.antennapod.adapter.DefaultFeedItemlistAdapter;
import de.danoeh.antennapod.adapter.ActionButtonCallback;
import de.danoeh.antennapod.adapter.DefaultFeedItemlistAdapter;
import de.danoeh.antennapod.adapter.InternalFeedItemlistAdapter;
import de.danoeh.antennapod.dialog.DownloadRequestErrorDialogCreator;
import de.danoeh.antennapod.feed.EventDistributor;

View File

@ -4,7 +4,6 @@ import android.os.Bundle;
import android.util.Log;
import de.danoeh.antennapod.AppConfig;
import de.danoeh.antennapod.adapter.DefaultFeedItemlistAdapter;
import de.danoeh.antennapod.adapter.InternalFeedItemlistAdapter;
import de.danoeh.antennapod.feed.EventDistributor;
import de.danoeh.antennapod.feed.FeedItem;
import de.danoeh.antennapod.feed.FeedManager;

View File

@ -5,8 +5,6 @@ import android.content.SharedPreferences;
import android.preference.PreferenceManager;
import android.util.Log;
import de.danoeh.antennapod.AppConfig;
import de.danoeh.antennapod.feed.FeedManager;
import de.danoeh.antennapod.feed.FeedMedia;
/**
* Provides access to preferences set by the playback service. A private

View File

@ -2,7 +2,6 @@ package de.danoeh.antennapod.preferences;
import java.io.File;
import java.io.IOException;
import java.util.Set;
import java.util.concurrent.TimeUnit;
import org.apache.commons.lang3.StringUtils;

View File

@ -1,11 +1,11 @@
package de.danoeh.antennapod.receiver;
import de.danoeh.antennapod.AppConfig;
import de.danoeh.antennapod.preferences.UserPreferences;
import android.content.BroadcastReceiver;
import android.content.Context;
import android.content.Intent;
import android.util.Log;
import de.danoeh.antennapod.AppConfig;
import de.danoeh.antennapod.preferences.UserPreferences;
/** Listens for events that make it necessary to reset the update alarm. */
public class AlarmUpdateReceiver extends BroadcastReceiver {

View File

@ -1,15 +1,15 @@
package de.danoeh.antennapod.receiver;
import de.danoeh.antennapod.AppConfig;
import de.danoeh.antennapod.feed.FeedManager;
import de.danoeh.antennapod.storage.DownloadRequester;
import de.danoeh.antennapod.util.NetworkUtils;
import android.content.BroadcastReceiver;
import android.content.Context;
import android.content.Intent;
import android.net.ConnectivityManager;
import android.net.NetworkInfo;
import android.util.Log;
import de.danoeh.antennapod.AppConfig;
import de.danoeh.antennapod.feed.FeedManager;
import de.danoeh.antennapod.storage.DownloadRequester;
import de.danoeh.antennapod.util.NetworkUtils;
public class ConnectivityActionReceiver extends BroadcastReceiver {
private static final String TAG = "ConnectivityActionReceiver";

View File

@ -41,7 +41,6 @@ import de.danoeh.antennapod.R;
import de.danoeh.antennapod.activity.AudioplayerActivity;
import de.danoeh.antennapod.activity.VideoplayerActivity;
import de.danoeh.antennapod.feed.Chapter;
import de.danoeh.antennapod.feed.Feed;
import de.danoeh.antennapod.feed.FeedComponent;
import de.danoeh.antennapod.feed.FeedItem;
import de.danoeh.antennapod.feed.FeedManager;
@ -730,8 +729,7 @@ public class PlaybackService extends Service {
prepareImmediately = startWhenPrepared = true;
} else {
if (AppConfig.DEBUG)
Log.d(TAG,
"No more episodes available to play");
Log.d(TAG, "No more episodes available to play");
media = null;
prepareImmediately = startWhenPrepared = false;
stopForeground(true);
@ -752,7 +750,8 @@ public class PlaybackService extends Service {
if (media != null) {
resetVideoSurface();
refreshRemoteControlClientState();
sendNotificationBroadcast(NOTIFICATION_TYPE_RELOAD, notificationCode);
sendNotificationBroadcast(NOTIFICATION_TYPE_RELOAD,
notificationCode);
} else {
sendNotificationBroadcast(NOTIFICATION_TYPE_PLAYBACK_END, 0);
stopSelf();
@ -958,12 +957,12 @@ public class PlaybackService extends Service {
Bitmap icon = null;
if (android.os.Build.VERSION.SDK_INT >= 11) {
if (media != null && media.getImageFileUrl() != null) {
if (media != null && media != null) {
int iconSize = getResources().getDimensionPixelSize(
android.R.dimen.notification_large_icon_width);
icon = BitmapDecoder.decodeBitmap(iconSize,
media.getImageFileUrl());
icon = BitmapDecoder.decodeBitmapFromWorkerTaskResource(iconSize, media);
}
}
if (icon == null) {
icon = BitmapFactory.decodeResource(getResources(),

View File

@ -7,8 +7,8 @@ import org.apache.http.HttpResponse;
import org.apache.http.impl.client.DefaultRedirectHandler;
import org.apache.http.protocol.HttpContext;
import de.danoeh.antennapod.AppConfig;
import android.util.Log;
import de.danoeh.antennapod.AppConfig;
public class APRedirectHandler extends DefaultRedirectHandler {
// Identifier for logger

View File

@ -58,7 +58,6 @@ import de.danoeh.antennapod.feed.FeedImage;
import de.danoeh.antennapod.feed.FeedItem;
import de.danoeh.antennapod.feed.FeedManager;
import de.danoeh.antennapod.feed.FeedMedia;
import de.danoeh.antennapod.preferences.UserPreferences;
import de.danoeh.antennapod.storage.DownloadRequestException;
import de.danoeh.antennapod.storage.DownloadRequester;
import de.danoeh.antennapod.syndication.handler.FeedHandler;

View File

@ -27,7 +27,6 @@ import de.danoeh.antennapod.AppConfig;
import de.danoeh.antennapod.PodcastApp;
import de.danoeh.antennapod.R;
import de.danoeh.antennapod.asynctask.DownloadStatus;
import de.danoeh.antennapod.service.download.APRedirectHandler;
import de.danoeh.antennapod.util.DownloadError;
import de.danoeh.antennapod.util.StorageUtils;

View File

@ -1,11 +1,15 @@
package de.danoeh.antennapod.util;
import java.io.File;
import java.io.InputStream;
import org.apache.commons.io.IOUtils;
import android.graphics.Bitmap;
import android.graphics.BitmapFactory;
import android.graphics.Rect;
import android.util.Log;
import de.danoeh.antennapod.AppConfig;
import de.danoeh.antennapod.asynctask.ImageLoader;
public class BitmapDecoder {
private static final String TAG = "BitmapDecoder";
@ -18,11 +22,13 @@ public class BitmapDecoder {
return sampleSize;
}
public static Bitmap decodeBitmap(int preferredLength, String fileUrl) {
if (fileUrl != null && new File(fileUrl).exists()) {
public static Bitmap decodeBitmapFromWorkerTaskResource(int preferredLength,
ImageLoader.ImageWorkerTaskResource source) {
InputStream input = source.openImageInputStream();
if (input != null) {
BitmapFactory.Options options = new BitmapFactory.Options();
options.inJustDecodeBounds = true;
BitmapFactory.decodeFile(fileUrl, options);
BitmapFactory.decodeStream(input, new Rect(), options);
int srcWidth = options.outWidth;
int srcHeight = options.outHeight;
int length = Math.max(srcWidth, srcHeight);
@ -32,17 +38,14 @@ public class BitmapDecoder {
options.inJustDecodeBounds = false;
options.inSampleSize = sampleSize;
options.inPreferredConfig = Bitmap.Config.ARGB_8888;
Bitmap decodedBitmap = BitmapFactory.decodeFile(fileUrl, options);
Bitmap decodedBitmap = BitmapFactory.decodeStream(source.reopenImageInputStream(input),
null, options);
if (decodedBitmap == null) {
Log.i(TAG,
"Bitmap could not be decoded in custom sample size. Trying default sample size (path was "
+ fileUrl + ")");
decodedBitmap = BitmapFactory.decodeFile(fileUrl);
decodedBitmap = BitmapFactory.decodeStream(source.reopenImageInputStream(input));
}
IOUtils.closeQuietly(input);
return decodedBitmap;
} else {
return null;
}
return null;
}
}

View File

@ -3,14 +3,14 @@ package de.danoeh.antennapod.util;
import java.util.Arrays;
import java.util.List;
import de.danoeh.antennapod.AppConfig;
import de.danoeh.antennapod.preferences.UserPreferences;
import android.content.Context;
import android.net.ConnectivityManager;
import android.net.NetworkInfo;
import android.net.wifi.WifiInfo;
import android.net.wifi.WifiManager;
import android.util.Log;
import de.danoeh.antennapod.AppConfig;
import de.danoeh.antennapod.preferences.UserPreferences;
public class NetworkUtils {
private static final String TAG = "NetworkUtils";

View File

@ -1,24 +1,16 @@
package de.danoeh.antennapod.util.playback;
import java.io.BufferedOutputStream;
import java.io.File;
import java.io.FileOutputStream;
import java.io.IOException;
import java.io.OutputStream;
import java.io.InputStream;
import java.util.List;
import org.apache.commons.io.IOUtils;
import android.content.SharedPreferences;
import android.content.SharedPreferences.Editor;
import android.media.MediaMetadataRetriever;
import android.os.Parcel;
import android.os.Parcelable;
import de.danoeh.antennapod.PodcastApp;
import de.danoeh.antennapod.feed.Chapter;
import de.danoeh.antennapod.feed.MediaType;
import de.danoeh.antennapod.util.ChapterUtils;
import de.danoeh.antennapod.util.FileNameGenerator;
/** Represents a media file that is stored on the local storage device. */
public class ExternalMedia implements Playable {
@ -35,7 +27,6 @@ public class ExternalMedia implements Playable {
private String shownotes;
private MediaType mediaType = MediaType.AUDIO;
private List<Chapter> chapters;
private String imageUrl;
private int duration;
private int position;
@ -71,12 +62,15 @@ public class ExternalMedia implements Playable {
@Override
public void loadMetadata() throws PlayableException {
final String tmpFileName = "tmpExternalMediaimage";
MediaMetadataRetriever mmr = new MediaMetadataRetriever();
try {
mmr.setDataSource(source);
mmr.setDataSource(source);
} catch (IllegalArgumentException e) {
e.printStackTrace();
throw new PlayableException("IllegalArgumentException when setting up MediaMetadataReceiver");
throw new PlayableException(
"IllegalArgumentException when setting up MediaMetadataReceiver");
}
episodeTitle = mmr
.extractMetadata(MediaMetadataRetriever.METADATA_KEY_TITLE);
@ -85,24 +79,6 @@ public class ExternalMedia implements Playable {
duration = Integer.parseInt(mmr
.extractMetadata(MediaMetadataRetriever.METADATA_KEY_DURATION));
ChapterUtils.loadChaptersFromFileUrl(this);
byte[] imgData = mmr.getEmbeddedPicture();
File cacheDir = PodcastApp.getInstance().getExternalCacheDir();
if (cacheDir != null) {
OutputStream out = null;
try {
File tmpFile = File.createTempFile(
FileNameGenerator.generateFileName(source) + "-img",
null, cacheDir);
out = new BufferedOutputStream(new FileOutputStream(tmpFile));
IOUtils.write(imgData, out);
imageUrl = tmpFile.getAbsolutePath();
} catch (IOException e) {
e.printStackTrace();
throw new PlayableException("IOException during loadMetadata()");
} finally {
IOUtils.closeQuietly(out);
}
}
}
@Override
@ -135,11 +111,6 @@ public class ExternalMedia implements Playable {
return feedTitle;
}
@Override
public String getImageFileUrl() {
return imageUrl;
}
@Override
public Object getIdentifier() {
return source;
@ -235,4 +206,20 @@ public class ExternalMedia implements Playable {
}
};
@Override
public InputStream openImageInputStream() {
return new Playable.DefaultPlayableImageLoader(this)
.openImageInputStream();
}
@Override
public String getImageLoaderCacheKey() {
return new Playable.DefaultPlayableImageLoader(this).getImageLoaderCacheKey();
}
@Override
public InputStream reopenImageInputStream(InputStream input) {
return new Playable.DefaultPlayableImageLoader(this).reopenImageInputStream(input);
}
}

View File

@ -1,10 +1,16 @@
package de.danoeh.antennapod.util.playback;
import java.io.ByteArrayInputStream;
import java.io.InputStream;
import java.util.List;
import org.apache.commons.io.IOUtils;
import android.content.SharedPreferences;
import android.media.MediaMetadataRetriever;
import android.os.Parcelable;
import android.util.Log;
import de.danoeh.antennapod.asynctask.ImageLoader;
import de.danoeh.antennapod.feed.Chapter;
import de.danoeh.antennapod.feed.Feed;
import de.danoeh.antennapod.feed.FeedManager;
@ -12,7 +18,8 @@ import de.danoeh.antennapod.feed.FeedMedia;
import de.danoeh.antennapod.feed.MediaType;
/** Interface for objects that can be played by the PlaybackService. */
public interface Playable extends Parcelable {
public interface Playable extends Parcelable,
ImageLoader.ImageWorkerTaskResource {
/**
* Save information about the playable in a preference so that it can be
@ -52,9 +59,6 @@ public interface Playable extends Parcelable {
/** Returns the title of the feed this Playable belongs to. */
public String getFeedTitle();
/** Returns a file url to an image or null if no such image exists. */
public String getImageFileUrl();
/**
* Returns a unique identifier, for example a file url or an ID from a
* database.
@ -194,4 +198,67 @@ public interface Playable extends Parcelable {
public static interface ShownoteLoaderCallback {
void onShownotesLoaded(String shownotes);
}
/** Uses local file as image resource if it is available. */
public static class DefaultPlayableImageLoader implements
ImageLoader.ImageWorkerTaskResource {
private Playable playable;
public DefaultPlayableImageLoader(Playable playable) {
if (playable == null) {
throw new IllegalArgumentException("Playable must not be null");
}
this.playable = playable;
}
@Override
public InputStream openImageInputStream() {
if (playable.localFileAvailable()
&& playable.getLocalMediaUrl() != null) {
MediaMetadataRetriever mmr = new MediaMetadataRetriever();
try {
mmr.setDataSource(playable.getLocalMediaUrl());
} catch (IllegalArgumentException e) {
e.printStackTrace();
return null;
}
byte[] imgData = mmr.getEmbeddedPicture();
if (imgData != null) {
return new PublicByteArrayInputStream(imgData);
}
}
return null;
}
@Override
public String getImageLoaderCacheKey() {
return playable.getLocalMediaUrl();
}
@Override
public InputStream reopenImageInputStream(InputStream input) {
if (input instanceof PublicByteArrayInputStream) {
IOUtils.closeQuietly(input);
byte[] imgData = ((PublicByteArrayInputStream) input).getByteArray();
if (imgData != null) {
ByteArrayInputStream out = new ByteArrayInputStream(imgData);
return out;
}
}
return null;
}
private static class PublicByteArrayInputStream extends
ByteArrayInputStream {
public PublicByteArrayInputStream(byte[] buf) {
super(buf);
}
public byte[] getByteArray() {
return buf;
}
}
}
}