Replaced ImageLoader and DiskCache with Picasso

Implemented Picasso Downloaders

Replaced ImageLoader and DiskCache with Picasso

Removed ImageLoader, DiskCache code
This commit is contained in:
daniel oeh 2014-07-27 23:29:47 +02:00
parent 460e061d35
commit 09c4736867
30 changed files with 910 additions and 1648 deletions

View File

@ -34,6 +34,10 @@ dependencies {
exclude group: 'com.android.support', module: 'support-v4'
}
compile 'org.jsoup:jsoup:1.7.3'
compile 'com.squareup.picasso:picasso:2.3.+'
compile 'com.squareup.okhttp:okhttp:2.0.+'
compile 'com.squareup.okhttp:okhttp-urlconnection:2.0.0'
compile 'com.squareup.okio:okio:1.0.0'
}
android {

View File

@ -50,6 +50,10 @@
-keep public class org.jsoup.** {
public *;
}
-dontwarn com.squareup.okhttp.**
-dontwarn okio.**
-keep class android.support.v4.** { *; }
-keep interface android.support.v4.** { *; }
-keep class android.support.v7.** { *; }

View File

@ -3,7 +3,6 @@ package de.danoeh.antennapod;
import android.app.Application;
import android.content.res.Configuration;
import android.util.Log;
import de.danoeh.antennapod.asynctask.ImageLoader;
import de.danoeh.antennapod.feed.EventDistributor;
import de.danoeh.antennapod.preferences.PlaybackPreferences;
import de.danoeh.antennapod.preferences.UserPreferences;
@ -36,13 +35,6 @@ public class PodcastApp extends Application {
SPAUtil.sendSPAppsQueryFeedsIntent(this);
}
@Override
public void onLowMemory() {
super.onLowMemory();
Log.w(TAG, "Received onLowOnMemory warning. Cleaning image cache...");
ImageLoader.getInstance().wipeImageCache();
}
public static float getLogicalDensity() {
return LOGICAL_DENSITY;
}

View File

@ -31,7 +31,7 @@ import de.danoeh.antennapod.BuildConfig;
import de.danoeh.antennapod.R;
import de.danoeh.antennapod.adapter.ChapterListAdapter;
import de.danoeh.antennapod.adapter.NavListAdapter;
import de.danoeh.antennapod.asynctask.ImageLoader;
import de.danoeh.antennapod.asynctask.PicassoProvider;
import de.danoeh.antennapod.dialog.VariableSpeedDialog;
import de.danoeh.antennapod.feed.Chapter;
import de.danoeh.antennapod.feed.EventDistributor;
@ -381,8 +381,9 @@ public class AudioplayerActivity extends MediaplayerActivity implements ItemDesc
@Override
public void run() {
ImageLoader.getInstance().loadThumbnailBitmap(media,
butNavLeft);
PicassoProvider.getMediaMetadataPicassoInstance(AudioplayerActivity.this)
.load(media.getImageUri())
.into(butNavLeft);
}
});
butNavLeft.setContentDescription(getString(buttonTexts[2]));
@ -396,9 +397,11 @@ public class AudioplayerActivity extends MediaplayerActivity implements ItemDesc
@Override
public void run() {
ImageLoader.getInstance().loadThumbnailBitmap(media,
butNavLeft);
PicassoProvider.getMediaMetadataPicassoInstance(AudioplayerActivity.this)
.load(media.getImageUri())
.into(butNavLeft);
}
});
butNavLeft.setContentDescription(getString(buttonTexts[2]));

View File

@ -9,18 +9,14 @@ import android.util.Log;
import android.view.LayoutInflater;
import android.view.MenuItem;
import android.view.View;
import android.widget.*;
import de.danoeh.antennapod.BuildConfig;
import de.danoeh.antennapod.R;
import de.danoeh.antennapod.adapter.FeedItemlistDescriptionAdapter;
import de.danoeh.antennapod.asynctask.ImageDiskCache;
import de.danoeh.antennapod.dialog.DownloadRequestErrorDialogCreator;
import de.danoeh.antennapod.feed.EventDistributor;
import de.danoeh.antennapod.feed.Feed;
import de.danoeh.antennapod.feed.FeedItem;
import de.danoeh.antennapod.storage.DBReader;
import de.danoeh.antennapod.storage.DownloadRequestException;
import de.danoeh.antennapod.storage.DownloadRequester;
import android.widget.AdapterView;
import android.widget.ArrayAdapter;
import android.widget.Button;
import android.widget.ImageView;
import android.widget.ListView;
import android.widget.Spinner;
import android.widget.TextView;
import org.apache.commons.lang3.StringUtils;
import org.jsoup.Jsoup;
import org.jsoup.examples.HtmlToPlainText;
@ -31,6 +27,18 @@ import java.util.Date;
import java.util.List;
import java.util.Map;
import de.danoeh.antennapod.BuildConfig;
import de.danoeh.antennapod.R;
import de.danoeh.antennapod.adapter.FeedItemlistDescriptionAdapter;
import de.danoeh.antennapod.asynctask.PicassoProvider;
import de.danoeh.antennapod.dialog.DownloadRequestErrorDialogCreator;
import de.danoeh.antennapod.feed.EventDistributor;
import de.danoeh.antennapod.feed.Feed;
import de.danoeh.antennapod.feed.FeedItem;
import de.danoeh.antennapod.storage.DBReader;
import de.danoeh.antennapod.storage.DownloadRequestException;
import de.danoeh.antennapod.storage.DownloadRequester;
/**
* Default implementation of OnlineFeedViewActivity. Shows the downloaded feed's items with their descriptions,
* a subscribe button and a spinner for choosing alternate feed URLs.
@ -115,9 +123,13 @@ public class DefaultOnlineFeedViewActivity extends OnlineFeedViewActivity {
subscribeButton = (Button) header.findViewById(R.id.butSubscribe);
if (feed.getImage() != null) {
ImageDiskCache.getDefaultInstance().loadThumbnailBitmap(feed.getImage().getDownload_url(), cover, (int) getResources().getDimension(
R.dimen.thumbnail_length));
int imageSize = (int) getResources().getDimension(R.dimen.thumbnail_length);
PicassoProvider.getDefaultPicassoInstance(this)
.load(feed.getImage().getDownload_url())
.resize(imageSize, imageSize)
.into(cover);
}
title.setText(feed.getTitle());
author.setText(feed.getAuthor());
description.setText(feed.getDescription());

View File

@ -12,7 +12,7 @@ import android.view.MenuItem;
import android.widget.*;
import de.danoeh.antennapod.BuildConfig;
import de.danoeh.antennapod.R;
import de.danoeh.antennapod.asynctask.ImageLoader;
import de.danoeh.antennapod.asynctask.PicassoProvider;
import de.danoeh.antennapod.dialog.DownloadRequestErrorDialogCreator;
import de.danoeh.antennapod.feed.Feed;
import de.danoeh.antennapod.feed.FeedPreferences;
@ -78,8 +78,9 @@ public class FeedInfoActivity extends ActionBarActivity {
@Override
public void run() {
ImageLoader.getInstance().loadThumbnailBitmap(
feed.getImage(), imgvCover);
PicassoProvider.getDefaultPicassoInstance(FeedInfoActivity.this)
.load(feed.getImageUri())
.into(imgvCover);
}
});

View File

@ -9,8 +9,9 @@ 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.asynctask.ImageLoader;
import de.danoeh.antennapod.asynctask.PicassoProvider;
import de.danoeh.antennapod.feed.FeedItem;
import de.danoeh.antennapod.util.Converter;
@ -22,10 +23,13 @@ public class DownloadedEpisodesListAdapter extends BaseAdapter {
private final Context context;
private final ItemAccess itemAccess;
private final int imageSize;
public DownloadedEpisodesListAdapter(Context context, ItemAccess itemAccess) {
super();
this.context = context;
this.itemAccess = itemAccess;
this.imageSize = (int) context.getResources().getDimension(R.dimen.thumbnail_length_downloaded_item);
}
@Override
@ -83,12 +87,11 @@ public class DownloadedEpisodesListAdapter extends BaseAdapter {
holder.butSecondary.setOnClickListener(secondaryActionListener);
ImageLoader.getInstance().loadThumbnailBitmap(
item,
holder.imageView,
(int) convertView.getResources().getDimension(
R.dimen.thumbnail_length)
);
PicassoProvider.getMediaMetadataPicassoInstance(context)
.load(item.getImageUri())
.resize(imageSize, imageSize)
.into(holder.imageView);
return convertView;
}

View File

@ -6,9 +6,14 @@ import android.view.LayoutInflater;
import android.view.View;
import android.view.View.OnClickListener;
import android.view.ViewGroup;
import android.widget.*;
import android.widget.BaseExpandableListAdapter;
import android.widget.ImageButton;
import android.widget.ImageView;
import android.widget.ProgressBar;
import android.widget.TextView;
import de.danoeh.antennapod.R;
import de.danoeh.antennapod.asynctask.ImageLoader;
import de.danoeh.antennapod.asynctask.PicassoProvider;
import de.danoeh.antennapod.feed.FeedItem;
import de.danoeh.antennapod.feed.FeedMedia;
import de.danoeh.antennapod.storage.DownloadRequester;
@ -19,276 +24,282 @@ import de.danoeh.antennapod.util.Converter;
* structure of this list is: [header] [queueItems] [header] [unreadItems].
*/
public class ExternalEpisodesListAdapter extends BaseExpandableListAdapter {
private static final String TAG = "ExternalEpisodesListAdapter";
private static final String TAG = "ExternalEpisodesListAdapter";
public static final int GROUP_POS_QUEUE = 0;
public static final int GROUP_POS_UNREAD = 1;
public static final int GROUP_POS_QUEUE = 0;
public static final int GROUP_POS_UNREAD = 1;
private Context context;
private Context context;
private ItemAccess itemAccess;
private ActionButtonCallback feedItemActionCallback;
private OnGroupActionClicked groupActionCallback;
private ActionButtonCallback feedItemActionCallback;
private OnGroupActionClicked groupActionCallback;
public ExternalEpisodesListAdapter(Context context,
ActionButtonCallback callback,
OnGroupActionClicked groupActionCallback,
ItemAccess itemAccess) {
super();
this.context = context;
private final int imageSize;
public ExternalEpisodesListAdapter(Context context,
ActionButtonCallback callback,
OnGroupActionClicked groupActionCallback,
ItemAccess itemAccess) {
super();
this.context = context;
this.itemAccess = itemAccess;
this.feedItemActionCallback = callback;
this.groupActionCallback = groupActionCallback;
}
this.feedItemActionCallback = callback;
this.groupActionCallback = groupActionCallback;
this.imageSize = (int) context.getResources().getDimension(R.dimen.thumbnail_length);
}
@Override
public boolean areAllItemsEnabled() {
return true;
}
@Override
public boolean areAllItemsEnabled() {
return true;
}
@Override
public FeedItem getChild(int groupPosition, int childPosition) {
if (groupPosition == GROUP_POS_QUEUE) {
return itemAccess.getQueueItemAt(childPosition);
} else if (groupPosition == GROUP_POS_UNREAD) {
@Override
public FeedItem getChild(int groupPosition, int childPosition) {
if (groupPosition == GROUP_POS_QUEUE) {
return itemAccess.getQueueItemAt(childPosition);
} else if (groupPosition == GROUP_POS_UNREAD) {
return itemAccess.getUnreadItemAt(childPosition);
}
return null;
}
}
return null;
}
@Override
public long getChildId(int groupPosition, int childPosition) {
return childPosition;
}
@Override
public long getChildId(int groupPosition, int childPosition) {
return childPosition;
}
@Override
public View getChildView(int groupPosition, final int childPosition,
boolean isLastChild, View convertView, ViewGroup parent) {
Holder holder;
final FeedItem item = getChild(groupPosition, childPosition);
@Override
public View getChildView(int groupPosition, final int childPosition,
boolean isLastChild, View convertView, ViewGroup parent) {
Holder holder;
final FeedItem item = getChild(groupPosition, childPosition);
if (convertView == null) {
holder = new Holder();
LayoutInflater inflater = (LayoutInflater) context
.getSystemService(Context.LAYOUT_INFLATER_SERVICE);
convertView = inflater.inflate(R.layout.external_itemlist_item,
parent, false);
holder.title = (TextView) convertView.findViewById(R.id.txtvTitle);
holder.feedTitle = (TextView) convertView
.findViewById(R.id.txtvFeedname);
holder.lenSize = (TextView) convertView
.findViewById(R.id.txtvLenSize);
holder.downloadStatus = (ImageView) convertView
.findViewById(R.id.imgvDownloadStatus);
holder.feedImage = (ImageView) convertView
.findViewById(R.id.imgvFeedimage);
holder.butAction = (ImageButton) convertView
.findViewById(R.id.butAction);
holder.statusPlaying = (View) convertView
.findViewById(R.id.statusPlaying);
holder.episodeProgress = (ProgressBar) convertView
.findViewById(R.id.pbar_episode_progress);
convertView.setTag(holder);
} else {
holder = (Holder) convertView.getTag();
}
if (convertView == null) {
holder = new Holder();
LayoutInflater inflater = (LayoutInflater) context
.getSystemService(Context.LAYOUT_INFLATER_SERVICE);
convertView = inflater.inflate(R.layout.external_itemlist_item,
parent, false);
holder.title = (TextView) convertView.findViewById(R.id.txtvTitle);
holder.feedTitle = (TextView) convertView
.findViewById(R.id.txtvFeedname);
holder.lenSize = (TextView) convertView
.findViewById(R.id.txtvLenSize);
holder.downloadStatus = (ImageView) convertView
.findViewById(R.id.imgvDownloadStatus);
holder.feedImage = (ImageView) convertView
.findViewById(R.id.imgvFeedimage);
holder.butAction = (ImageButton) convertView
.findViewById(R.id.butAction);
holder.statusPlaying = (View) convertView
.findViewById(R.id.statusPlaying);
holder.episodeProgress = (ProgressBar) convertView
.findViewById(R.id.pbar_episode_progress);
convertView.setTag(holder);
} else {
holder = (Holder) convertView.getTag();
}
holder.title.setText(item.getTitle());
holder.feedTitle.setText(item.getFeed().getTitle());
FeedItem.State state = item.getState();
holder.title.setText(item.getTitle());
holder.feedTitle.setText(item.getFeed().getTitle());
FeedItem.State state = item.getState();
if (groupPosition == GROUP_POS_QUEUE) {
switch (state) {
case PLAYING:
holder.statusPlaying.setVisibility(View.VISIBLE);
holder.episodeProgress.setVisibility(View.VISIBLE);
break;
case IN_PROGRESS:
holder.statusPlaying.setVisibility(View.GONE);
holder.episodeProgress.setVisibility(View.VISIBLE);
break;
case NEW:
holder.statusPlaying.setVisibility(View.GONE);
holder.episodeProgress.setVisibility(View.GONE);
break;
default:
holder.statusPlaying.setVisibility(View.GONE);
holder.episodeProgress.setVisibility(View.GONE);
break;
}
} else {
holder.statusPlaying.setVisibility(View.GONE);
holder.episodeProgress.setVisibility(View.GONE);
}
if (groupPosition == GROUP_POS_QUEUE) {
switch (state) {
case PLAYING:
holder.statusPlaying.setVisibility(View.VISIBLE);
holder.episodeProgress.setVisibility(View.VISIBLE);
break;
case IN_PROGRESS:
holder.statusPlaying.setVisibility(View.GONE);
holder.episodeProgress.setVisibility(View.VISIBLE);
break;
case NEW:
holder.statusPlaying.setVisibility(View.GONE);
holder.episodeProgress.setVisibility(View.GONE);
break;
default:
holder.statusPlaying.setVisibility(View.GONE);
holder.episodeProgress.setVisibility(View.GONE);
break;
}
} else {
holder.statusPlaying.setVisibility(View.GONE);
holder.episodeProgress.setVisibility(View.GONE);
}
FeedMedia media = item.getMedia();
if (media != null) {
FeedMedia media = item.getMedia();
if (media != null) {
if (state == FeedItem.State.PLAYING
|| state == FeedItem.State.IN_PROGRESS) {
if (media.getDuration() > 0) {
holder.episodeProgress.setProgress((int) (((double) media
.getPosition()) / media.getDuration() * 100));
holder.lenSize.setText(Converter
.getDurationStringLong(media.getDuration()
- media.getPosition()));
}
} else if (!media.isDownloaded()) {
holder.lenSize.setText(context.getString(R.string.size_prefix)
+ Converter.byteToString(media.getSize()));
} else {
holder.lenSize.setText(context
.getString(R.string.length_prefix)
+ Converter.getDurationStringLong(media.getDuration()));
}
if (state == FeedItem.State.PLAYING
|| state == FeedItem.State.IN_PROGRESS) {
if (media.getDuration() > 0) {
holder.episodeProgress.setProgress((int) (((double) media
.getPosition()) / media.getDuration() * 100));
holder.lenSize.setText(Converter
.getDurationStringLong(media.getDuration()
- media.getPosition()));
}
} else if (!media.isDownloaded()) {
holder.lenSize.setText(context.getString(R.string.size_prefix)
+ Converter.byteToString(media.getSize()));
} else {
holder.lenSize.setText(context
.getString(R.string.length_prefix)
+ Converter.getDurationStringLong(media.getDuration()));
}
TypedArray drawables = context.obtainStyledAttributes(new int[] {
R.attr.av_download, R.attr.navigation_refresh });
final int[] labels = new int[] {R.string.status_downloaded_label, R.string.downloading_label};
holder.lenSize.setVisibility(View.VISIBLE);
if (!media.isDownloaded()) {
if (DownloadRequester.getInstance().isDownloadingFile(media)) {
holder.downloadStatus.setVisibility(View.VISIBLE);
holder.downloadStatus.setImageDrawable(drawables
.getDrawable(1));
TypedArray drawables = context.obtainStyledAttributes(new int[]{
R.attr.av_download, R.attr.navigation_refresh});
final int[] labels = new int[]{R.string.status_downloaded_label, R.string.downloading_label};
holder.lenSize.setVisibility(View.VISIBLE);
if (!media.isDownloaded()) {
if (DownloadRequester.getInstance().isDownloadingFile(media)) {
holder.downloadStatus.setVisibility(View.VISIBLE);
holder.downloadStatus.setImageDrawable(drawables
.getDrawable(1));
holder.downloadStatus.setContentDescription(context.getString(labels[1]));
} else {
holder.downloadStatus.setVisibility(View.INVISIBLE);
}
} else {
holder.downloadStatus.setVisibility(View.VISIBLE);
holder.downloadStatus
.setImageDrawable(drawables.getDrawable(0));
} else {
holder.downloadStatus.setVisibility(View.INVISIBLE);
}
} else {
holder.downloadStatus.setVisibility(View.VISIBLE);
holder.downloadStatus
.setImageDrawable(drawables.getDrawable(0));
holder.downloadStatus.setContentDescription(context.getString(labels[0]));
}
} else {
holder.downloadStatus.setVisibility(View.INVISIBLE);
holder.lenSize.setVisibility(View.INVISIBLE);
}
}
} else {
holder.downloadStatus.setVisibility(View.INVISIBLE);
holder.lenSize.setVisibility(View.INVISIBLE);
}
ImageLoader.getInstance().loadThumbnailBitmap(
item,
holder.feedImage,
(int) convertView.getResources().getDimension(
R.dimen.thumbnail_length));
holder.butAction.setFocusable(false);
holder.butAction.setOnClickListener(new OnClickListener() {
PicassoProvider.getMediaMetadataPicassoInstance(context)
.load(item.getImageUri())
.resize(imageSize, imageSize)
.into(holder.feedImage);
@Override
public void onClick(View v) {
feedItemActionCallback.onActionButtonPressed(item);
}
});
holder.butAction.setFocusable(false);
holder.butAction.setOnClickListener(new OnClickListener() {
return convertView;
@Override
public void onClick(View v) {
feedItemActionCallback.onActionButtonPressed(item);
}
});
}
return convertView;
static class Holder {
TextView title;
TextView feedTitle;
TextView lenSize;
ImageView downloadStatus;
ImageView feedImage;
ImageButton butAction;
View statusPlaying;
ProgressBar episodeProgress;
}
}
@Override
public int getChildrenCount(int groupPosition) {
if (groupPosition == GROUP_POS_QUEUE) {
return itemAccess.getQueueSize();
} else if (groupPosition == GROUP_POS_UNREAD) {
return itemAccess.getUnreadItemsSize();
}
return 0;
}
static class Holder {
TextView title;
TextView feedTitle;
TextView lenSize;
ImageView downloadStatus;
ImageView feedImage;
ImageButton butAction;
View statusPlaying;
ProgressBar episodeProgress;
}
@Override
public int getGroupCount() {
// Hide 'unread items' group if empty
if (itemAccess.getUnreadItemsSize() > 0) {
return 2;
} else {
return 1;
}
}
@Override
public int getChildrenCount(int groupPosition) {
if (groupPosition == GROUP_POS_QUEUE) {
return itemAccess.getQueueSize();
} else if (groupPosition == GROUP_POS_UNREAD) {
return itemAccess.getUnreadItemsSize();
}
return 0;
}
@Override
public long getGroupId(int groupPosition) {
return groupPosition;
}
@Override
public int getGroupCount() {
// Hide 'unread items' group if empty
if (itemAccess.getUnreadItemsSize() > 0) {
return 2;
} else {
return 1;
}
}
@Override
public View getGroupView(final int groupPosition, boolean isExpanded,
View convertView, ViewGroup parent) {
LayoutInflater inflater = (LayoutInflater) context
.getSystemService(Context.LAYOUT_INFLATER_SERVICE);
convertView = inflater.inflate(R.layout.feeditemlist_header, parent, false);
TextView headerTitle = (TextView) convertView
.findViewById(0);
ImageButton actionButton = (ImageButton) convertView
.findViewById(R.id.butAction);
TextView numItems = (TextView) convertView.findViewById(0);
String headerString = null;
int childrenCount = 0;
if (groupPosition == 0) {
headerString = context.getString(R.string.queue_label);
childrenCount = getChildrenCount(GROUP_POS_QUEUE);
} else {
headerString = context.getString(R.string.waiting_list_label);
childrenCount = getChildrenCount(GROUP_POS_UNREAD);
}
headerTitle.setText(headerString);
if (childrenCount <= 0) {
numItems.setVisibility(View.INVISIBLE);
} else {
numItems.setVisibility(View.VISIBLE);
numItems.setText(Integer.toString(childrenCount));
}
actionButton.setFocusable(false);
actionButton.setOnClickListener(new OnClickListener() {
@Override
public long getGroupId(int groupPosition) {
return groupPosition;
}
@Override
public void onClick(View v) {
groupActionCallback.onClick(getGroupId(groupPosition));
}
});
return convertView;
}
@Override
public View getGroupView(final int groupPosition, boolean isExpanded,
View convertView, ViewGroup parent) {
LayoutInflater inflater = (LayoutInflater) context
.getSystemService(Context.LAYOUT_INFLATER_SERVICE);
convertView = inflater.inflate(R.layout.feeditemlist_header, parent, false);
TextView headerTitle = (TextView) convertView
.findViewById(0);
ImageButton actionButton = (ImageButton) convertView
.findViewById(R.id.butAction);
TextView numItems = (TextView) convertView.findViewById(0);
@Override
public boolean isEmpty() {
return itemAccess.getUnreadItemsSize() == 0
&& itemAccess.getQueueSize() == 0;
}
String headerString = null;
int childrenCount = 0;
@Override
public Object getGroup(int groupPosition) {
return null;
}
if (groupPosition == 0) {
headerString = context.getString(R.string.queue_label);
childrenCount = getChildrenCount(GROUP_POS_QUEUE);
} else {
headerString = context.getString(R.string.waiting_list_label);
childrenCount = getChildrenCount(GROUP_POS_UNREAD);
}
headerTitle.setText(headerString);
if (childrenCount <= 0) {
numItems.setVisibility(View.INVISIBLE);
} else {
numItems.setVisibility(View.VISIBLE);
numItems.setText(Integer.toString(childrenCount));
}
actionButton.setFocusable(false);
actionButton.setOnClickListener(new OnClickListener() {
@Override
public boolean hasStableIds() {
return true;
}
@Override
public void onClick(View v) {
groupActionCallback.onClick(getGroupId(groupPosition));
}
});
return convertView;
}
@Override
public boolean isChildSelectable(int groupPosition, int childPosition) {
return true;
}
@Override
public boolean isEmpty() {
return itemAccess.getUnreadItemsSize() == 0
&& itemAccess.getQueueSize() == 0;
}
public interface OnGroupActionClicked {
public void onClick(long groupId);
}
@Override
public Object getGroup(int groupPosition) {
return null;
}
@Override
public boolean hasStableIds() {
return true;
}
@Override
public boolean isChildSelectable(int groupPosition, int childPosition) {
return true;
}
public interface OnGroupActionClicked {
public void onClick(long groupId);
}
public static interface ItemAccess {
public int getQueueSize();
public int getUnreadItemsSize();
public FeedItem getQueueItemAt(int position);
public FeedItem getUnreadItemAt(int position);
}

View File

@ -11,7 +11,7 @@ import android.widget.BaseAdapter;
import android.widget.ImageView;
import android.widget.TextView;
import de.danoeh.antennapod.R;
import de.danoeh.antennapod.asynctask.ImageLoader;
import de.danoeh.antennapod.asynctask.PicassoProvider;
import de.danoeh.antennapod.feed.Feed;
/**
@ -32,6 +32,8 @@ public class NavListAdapter extends BaseAdapter {
private ItemAccess itemAccess;
private Context context;
private final int imageSize;
public NavListAdapter(ItemAccess itemAccess, Context context) {
this.itemAccess = itemAccess;
this.context = context;
@ -41,6 +43,7 @@ public class NavListAdapter extends BaseAdapter {
drawables = new Drawable[]{ta.getDrawable(0), ta.getDrawable(1), ta.getDrawable(2),
ta.getDrawable(3), ta.getDrawable(4)};
ta.recycle();
this.imageSize = (int) context.getResources().getDimension(R.dimen.thumbnail_length_navlist);
}
@Override
@ -189,7 +192,11 @@ public class NavListAdapter extends BaseAdapter {
}
holder.title.setText(feed.getTitle());
ImageLoader.getInstance().loadThumbnailBitmap(feed.getImage(), holder.image, (int) context.getResources().getDimension(R.dimen.thumbnail_length_navlist));
PicassoProvider.getDefaultPicassoInstance(context)
.load(feed.getImageUri())
.resize(imageSize, imageSize)
.into(holder.image);
return convertView;
}

View File

@ -5,9 +5,14 @@ import android.text.format.DateUtils;
import android.view.LayoutInflater;
import android.view.View;
import android.view.ViewGroup;
import android.widget.*;
import android.widget.BaseAdapter;
import android.widget.ImageButton;
import android.widget.ImageView;
import android.widget.ProgressBar;
import android.widget.TextView;
import de.danoeh.antennapod.R;
import de.danoeh.antennapod.asynctask.ImageLoader;
import de.danoeh.antennapod.asynctask.PicassoProvider;
import de.danoeh.antennapod.feed.FeedItem;
import de.danoeh.antennapod.feed.FeedMedia;
import de.danoeh.antennapod.storage.DownloadRequester;
@ -22,6 +27,7 @@ public class NewEpisodesListAdapter extends BaseAdapter {
private final ItemAccess itemAccess;
private final ActionButtonCallback actionButtonCallback;
private final ActionButtonUtils actionButtonUtils;
private final int imageSize;
public NewEpisodesListAdapter(Context context, ItemAccess itemAccess, ActionButtonCallback actionButtonCallback) {
super();
@ -29,6 +35,7 @@ public class NewEpisodesListAdapter extends BaseAdapter {
this.itemAccess = itemAccess;
this.actionButtonUtils = new ActionButtonUtils(context);
this.actionButtonCallback = actionButtonCallback;
this.imageSize = (int) context.getResources().getDimension(R.dimen.thumbnail_length_itemlist);
}
@Override
@ -124,13 +131,11 @@ public class NewEpisodesListAdapter extends BaseAdapter {
holder.butSecondary.setTag(item);
holder.butSecondary.setOnClickListener(secondaryActionListener);
PicassoProvider.getMediaMetadataPicassoInstance(context)
.load(item.getImageUri())
.resize(imageSize, imageSize)
.into(holder.imageView);
ImageLoader.getInstance().loadThumbnailBitmap(
item,
holder.imageView,
(int) convertView.getResources().getDimension(
R.dimen.thumbnail_length)
);
return convertView;
}

View File

@ -6,7 +6,7 @@ import android.view.View;
import android.view.ViewGroup;
import android.widget.*;
import de.danoeh.antennapod.R;
import de.danoeh.antennapod.asynctask.ImageLoader;
import de.danoeh.antennapod.asynctask.PicassoProvider;
import de.danoeh.antennapod.feed.FeedItem;
import de.danoeh.antennapod.feed.FeedMedia;
import de.danoeh.antennapod.storage.DownloadRequester;
@ -22,12 +22,15 @@ public class QueueListAdapter extends BaseAdapter {
private final ActionButtonCallback actionButtonCallback;
private final ActionButtonUtils actionButtonUtils;
private final int imageSize;
public QueueListAdapter(Context context, ItemAccess itemAccess, ActionButtonCallback actionButtonCallback) {
super();
this.context = context;
this.itemAccess = itemAccess;
this.actionButtonUtils = new ActionButtonUtils(context);
this.actionButtonCallback = actionButtonCallback;
this.imageSize = (int) context.getResources().getDimension(R.dimen.thumbnail_length_queue_item);
}
@ -92,13 +95,11 @@ public class QueueListAdapter extends BaseAdapter {
holder.butSecondary.setTag(item);
holder.butSecondary.setOnClickListener(secondaryActionListener);
PicassoProvider.getMediaMetadataPicassoInstance(context)
.load(item.getImageUri())
.resize(imageSize, imageSize)
.into(holder.imageView);
ImageLoader.getInstance().loadThumbnailBitmap(
item,
holder.imageView,
(int) convertView.getResources().getDimension(
R.dimen.thumbnail_length)
);
return convertView;
}

View File

@ -4,28 +4,31 @@ import android.content.Context;
import android.view.LayoutInflater;
import android.view.View;
import android.view.ViewGroup;
import android.widget.ArrayAdapter;
import android.widget.BaseAdapter;
import android.widget.ImageView;
import android.widget.TextView;
import de.danoeh.antennapod.R;
import de.danoeh.antennapod.asynctask.ImageLoader;
import de.danoeh.antennapod.asynctask.PicassoProvider;
import de.danoeh.antennapod.feed.Feed;
import de.danoeh.antennapod.feed.FeedComponent;
import de.danoeh.antennapod.feed.FeedItem;
import de.danoeh.antennapod.feed.SearchResult;
import java.util.List;
/** List adapter for search activity. */
/**
* List adapter for search activity.
*/
public class SearchlistAdapter extends BaseAdapter {
private final Context context;
private final Context context;
private final ItemAccess itemAccess;
private final int imageSize;
public SearchlistAdapter(Context context, ItemAccess itemAccess) {
this.context = context;
this.itemAccess = itemAccess;
this.imageSize = (int) context.getResources().getDimension(R.dimen.thumbnail_length);
}
@Override
@ -44,61 +47,65 @@ public class SearchlistAdapter extends BaseAdapter {
}
@Override
public View getView(int position, View convertView, ViewGroup parent) {
final Holder holder;
SearchResult result = getItem(position);
FeedComponent component = result.getComponent();
public View getView(int position, View convertView, ViewGroup parent) {
final Holder holder;
SearchResult result = getItem(position);
FeedComponent component = result.getComponent();
// Inflate Layout
if (convertView == null) {
holder = new Holder();
LayoutInflater inflater = (LayoutInflater) context
.getSystemService(Context.LAYOUT_INFLATER_SERVICE);
// Inflate Layout
if (convertView == null) {
holder = new Holder();
LayoutInflater inflater = (LayoutInflater) context
.getSystemService(Context.LAYOUT_INFLATER_SERVICE);
convertView = inflater.inflate(R.layout.searchlist_item, parent, false);
holder.title = (TextView) convertView.findViewById(R.id.txtvTitle);
holder.cover = (ImageView) convertView
.findViewById(R.id.imgvFeedimage);
holder.subtitle = (TextView) convertView
.findViewById(R.id.txtvSubtitle);
convertView = inflater.inflate(R.layout.searchlist_item, parent, false);
holder.title = (TextView) convertView.findViewById(R.id.txtvTitle);
holder.cover = (ImageView) convertView
.findViewById(R.id.imgvFeedimage);
holder.subtitle = (TextView) convertView
.findViewById(R.id.txtvSubtitle);
convertView.setTag(holder);
} else {
holder = (Holder) convertView.getTag();
}
if (component.getClass() == Feed.class) {
final Feed feed = (Feed) component;
holder.title.setText(feed.getTitle());
holder.subtitle.setVisibility(View.GONE);
ImageLoader.getInstance().loadThumbnailBitmap(feed.getImage(),
holder.cover, (int) convertView.getResources().getDimension(R.dimen.thumbnail_length));
} else if (component.getClass() == FeedItem.class) {
final FeedItem item = (FeedItem) component;
holder.title.setText(item.getTitle());
if (result.getSubtitle() != null) {
holder.subtitle.setVisibility(View.VISIBLE);
holder.subtitle.setText(result.getSubtitle());
}
convertView.setTag(holder);
} else {
holder = (Holder) convertView.getTag();
}
if (component.getClass() == Feed.class) {
final Feed feed = (Feed) component;
holder.title.setText(feed.getTitle());
holder.subtitle.setVisibility(View.GONE);
ImageLoader.getInstance().loadThumbnailBitmap(
item.getFeed().getImage(),
holder.cover,
(int) convertView.getResources().getDimension(
R.dimen.thumbnail_length));
PicassoProvider.getDefaultPicassoInstance(context)
.load(feed.getImageUri())
.resize(imageSize, imageSize)
.into(holder.cover);
}
} else if (component.getClass() == FeedItem.class) {
final FeedItem item = (FeedItem) component;
holder.title.setText(item.getTitle());
if (result.getSubtitle() != null) {
holder.subtitle.setVisibility(View.VISIBLE);
holder.subtitle.setText(result.getSubtitle());
}
return convertView;
}
PicassoProvider.getDefaultPicassoInstance(context)
.load(item.getFeed().getImageUri())
.resize(imageSize, imageSize)
.into(holder.cover);
static class Holder {
ImageView cover;
TextView title;
TextView subtitle;
}
}
return convertView;
}
static class Holder {
ImageView cover;
TextView title;
TextView subtitle;
}
public static interface ItemAccess {
int getCount();
SearchResult getItem(int position);
}

View File

@ -7,22 +7,21 @@ import android.view.ViewGroup;
import android.widget.ArrayAdapter;
import android.widget.ImageView;
import android.widget.TextView;
import de.danoeh.antennapod.R;
import de.danoeh.antennapod.asynctask.ImageDiskCache;
import de.danoeh.antennapod.gpoddernet.model.GpodnetPodcast;
import java.util.List;
import de.danoeh.antennapod.R;
import de.danoeh.antennapod.asynctask.PicassoProvider;
import de.danoeh.antennapod.gpoddernet.model.GpodnetPodcast;
/**
* Adapter for displaying a list of GPodnetPodcast-Objects.
*/
public class PodcastListAdapter extends ArrayAdapter<GpodnetPodcast> {
private final ImageDiskCache diskCache;
private final int thumbnailLength;
public PodcastListAdapter(Context context, int resource, List<GpodnetPodcast> objects) {
super(context, resource, objects);
diskCache = ImageDiskCache.getDefaultInstance();
thumbnailLength = (int) context.getResources().getDimension(R.dimen.thumbnail_length);
}
@ -50,7 +49,11 @@ public class PodcastListAdapter extends ArrayAdapter<GpodnetPodcast> {
holder.title.setText(podcast.getTitle());
holder.description.setText(podcast.getDescription());
diskCache.loadThumbnailBitmap(podcast.getLogoUrl(), holder.image, thumbnailLength);
PicassoProvider.getDefaultPicassoInstance(convertView.getContext())
.load(podcast.getLogoUrl())
.resize(thumbnailLength, thumbnailLength)
.into(holder.image);
return convertView;
}

View File

@ -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);
}
}
}

View File

@ -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;
}
}

View File

@ -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;
}
};
}
}
}

View File

@ -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();
}
}

View File

@ -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();
}

View File

@ -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);
}
}
}

View File

@ -1,6 +1,9 @@
package de.danoeh.antennapod.feed;
import android.content.Context;
import android.net.Uri;
import de.danoeh.antennapod.asynctask.PicassoImageResource;
import de.danoeh.antennapod.preferences.UserPreferences;
import de.danoeh.antennapod.storage.DBWriter;
import de.danoeh.antennapod.util.EpisodeFilter;
@ -16,7 +19,7 @@ import java.util.List;
*
* @author daniel
*/
public class Feed extends FeedFile implements FlattrThing {
public class Feed extends FeedFile implements FlattrThing, PicassoImageResource {
public static final int FEEDFILETYPE_FEED = 0;
public static final String TYPE_RSS2 = "rss";
public static final String TYPE_RSS091 = "rss";
@ -430,4 +433,13 @@ public class Feed extends FeedFile implements FlattrThing {
preferences.setFeedID(id);
}
}
@Override
public Uri getImageUri() {
if (image != null) {
return image.getImageUri();
} else {
return null;
}
}
}

View File

@ -1,6 +1,9 @@
package de.danoeh.antennapod.feed;
import de.danoeh.antennapod.asynctask.ImageLoader;
import android.net.Uri;
import de.danoeh.antennapod.asynctask.PicassoImageResource;
import org.apache.commons.io.IOUtils;
import java.io.File;
@ -10,8 +13,7 @@ import java.io.InputStream;
public class FeedImage extends FeedFile implements
ImageLoader.ImageWorkerTaskResource {
public class FeedImage extends FeedFile implements PicassoImageResource {
public static final int FEEDFILETYPE_FEEDIMAGE = 1;
protected String title;
@ -64,30 +66,12 @@ public class FeedImage extends FeedFile implements
this.owner = owner;
}
@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();
}
@Override
public Uri getImageUri() {
if (file_url != null && downloaded) {
return Uri.fromFile(new File(file_url));
} else {
return null;
}
}
}

View File

@ -1,7 +1,9 @@
package de.danoeh.antennapod.feed;
import android.net.Uri;
import de.danoeh.antennapod.PodcastApp;
import de.danoeh.antennapod.asynctask.ImageLoader;
import de.danoeh.antennapod.asynctask.PicassoImageResource;
import de.danoeh.antennapod.storage.DBReader;
import de.danoeh.antennapod.util.ShownotesProvider;
import de.danoeh.antennapod.util.flattr.FlattrStatus;
@ -17,8 +19,7 @@ import java.util.concurrent.Callable;
*
* @author daniel
*/
public class FeedItem extends FeedComponent implements
ImageLoader.ImageWorkerTaskResource, ShownotesProvider, FlattrThing {
public class FeedItem extends FeedComponent implements ShownotesProvider, FlattrThing, PicassoImageResource {
/**
* The id/guid that can be found in the rss/atom feed. Might not be set.
@ -261,6 +262,17 @@ public class FeedItem extends FeedComponent implements
};
}
@Override
public Uri getImageUri() {
if (hasMedia()) {
return media.getImageUri();
} else if (feed != null) {
return feed.getImageUri();
} else {
return null;
}
}
public enum State {
NEW, IN_PROGRESS, READ, PLAYING
}
@ -277,45 +289,6 @@ public class FeedItem extends FeedComponent implements
return (isRead() ? State.READ : State.NEW);
}
@Override
public InputStream openImageInputStream() {
InputStream out = null;
if (hasItemImageDownloaded()) {
out = image.openImageInputStream();
} else if (hasMedia()) {
out = media.openImageInputStream();
} else if (feed.getImage() != null) {
out = feed.getImage().openImageInputStream();
}
return out;
}
@Override
public InputStream reopenImageInputStream(InputStream input) {
InputStream out = null;
if (hasItemImageDownloaded()) {
out = image.reopenImageInputStream(input);
} else if (hasMedia()) {
out = media.reopenImageInputStream(input);
} else if (feed.getImage() != null) {
out = feed.getImage().reopenImageInputStream(input);
}
return out;
}
@Override
public String getImageLoaderCacheKey() {
String out = null;
if (hasItemImageDownloaded()) {
out = image.getImageLoaderCacheKey();
} else if (hasMedia()) {
out = media.getImageLoaderCacheKey();
} else if (feed.getImage() != null) {
out = feed.getImage().getImageLoaderCacheKey();
}
return out;
}
public long getFeedId() {
return feedId;
}

View File

@ -2,8 +2,17 @@ package de.danoeh.antennapod.feed;
import android.content.SharedPreferences;
import android.content.SharedPreferences.Editor;
import android.net.Uri;
import android.os.Parcel;
import android.os.Parcelable;
import java.io.File;
import java.io.FileInputStream;
import java.io.InputStream;
import java.util.Date;
import java.util.List;
import java.util.concurrent.Callable;
import de.danoeh.antennapod.PodcastApp;
import de.danoeh.antennapod.preferences.PlaybackPreferences;
import de.danoeh.antennapod.storage.DBReader;
@ -11,12 +20,6 @@ import de.danoeh.antennapod.storage.DBWriter;
import de.danoeh.antennapod.util.ChapterUtils;
import de.danoeh.antennapod.util.playback.Playable;
import java.io.FileInputStream;
import java.io.InputStream;
import java.util.Date;
import java.util.List;
import java.util.concurrent.Callable;
public class FeedMedia extends FeedFile implements Playable {
private static final String TAG = "FeedMedia";
@ -382,52 +385,13 @@ public class FeedMedia extends FeedFile implements Playable {
};
@Override
public InputStream openImageInputStream() {
InputStream out;
if (item.hasItemImageDownloaded()) {
out = item.openImageInputStream();
public Uri getImageUri() {
if (localFileAvailable()) {
return new Uri.Builder().scheme(SCHEME_MEDIA).encodedPath(getLocalMediaUrl()).build();
} else if (item != null && item.getFeed() != null) {
return item.getFeed().getImageUri();
} else {
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;
if (item == null) {
return null;
} else if (item.hasItemImageDownloaded()) {
out = item.getImageLoaderCacheKey();
} else {
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) {
if (item.hasItemImageDownloaded()) {
return item.getImage().reopenImageInputStream(input);
} else {
return item.getFeed().getImage().reopenImageInputStream(input);
}
} else {
return new Playable.DefaultPlayableImageLoader(this)
.reopenImageInputStream(input);
}
}
}

View File

@ -1,5 +1,6 @@
package de.danoeh.antennapod.fragment;
import android.content.Context;
import android.os.Bundle;
import android.support.v4.app.Fragment;
import android.util.Log;
@ -7,91 +8,98 @@ import android.view.LayoutInflater;
import android.view.View;
import android.view.ViewGroup;
import android.widget.ImageView;
import de.danoeh.antennapod.BuildConfig;
import de.danoeh.antennapod.R;
import de.danoeh.antennapod.activity.AudioplayerActivity.AudioplayerContentFragment;
import de.danoeh.antennapod.asynctask.ImageLoader;
import de.danoeh.antennapod.asynctask.PicassoProvider;
import de.danoeh.antennapod.util.playback.Playable;
/** Displays the cover and the title of a FeedItem. */
/**
* Displays the cover and the title of a FeedItem.
*/
public class CoverFragment extends Fragment implements
AudioplayerContentFragment {
private static final String TAG = "CoverFragment";
private static final String ARG_PLAYABLE = "arg.playable";
AudioplayerContentFragment {
private static final String TAG = "CoverFragment";
private static final String ARG_PLAYABLE = "arg.playable";
private Playable media;
private Playable media;
private ImageView imgvCover;
private ImageView imgvCover;
private boolean viewCreated = false;
private boolean viewCreated = false;
public static CoverFragment newInstance(Playable item) {
CoverFragment f = new CoverFragment();
if (item != null) {
Bundle args = new Bundle();
args.putParcelable(ARG_PLAYABLE, item);
f.setArguments(args);
}
return f;
}
public static CoverFragment newInstance(Playable item) {
CoverFragment f = new CoverFragment();
if (item != null) {
Bundle args = new Bundle();
args.putParcelable(ARG_PLAYABLE, item);
f.setArguments(args);
}
return f;
}
@Override
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setRetainInstance(true);
Bundle args = getArguments();
if (args != null) {
media = args.getParcelable(ARG_PLAYABLE);
} else {
Log.e(TAG, TAG + " was called with invalid arguments");
}
}
@Override
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setRetainInstance(true);
Bundle args = getArguments();
if (args != null) {
media = args.getParcelable(ARG_PLAYABLE);
} else {
Log.e(TAG, TAG + " was called with invalid arguments");
}
}
@Override
public View onCreateView(LayoutInflater inflater, ViewGroup container,
Bundle savedInstanceState) {
View root = inflater.inflate(R.layout.cover_fragment, container, false);
imgvCover = (ImageView) root.findViewById(R.id.imgvCover);
viewCreated = true;
return root;
}
@Override
public View onCreateView(LayoutInflater inflater, ViewGroup container,
Bundle savedInstanceState) {
View root = inflater.inflate(R.layout.cover_fragment, container, false);
imgvCover = (ImageView) root.findViewById(R.id.imgvCover);
viewCreated = true;
return root;
}
private void loadMediaInfo() {
if (media != null) {
imgvCover.post(new Runnable() {
private void loadMediaInfo() {
if (media != null) {
imgvCover.post(new Runnable() {
@Override
public void run() {
ImageLoader.getInstance().loadCoverBitmap(
media, imgvCover);
}
});
} else {
Log.w(TAG, "loadMediaInfo was called while media was null");
}
}
@Override
public void run() {
Context c = getActivity();
if (c != null) {
PicassoProvider.getMediaMetadataPicassoInstance(c)
.load(media.getImageUri())
.into(imgvCover);
}
}
});
} else {
Log.w(TAG, "loadMediaInfo was called while media was null");
}
}
@Override
public void onStart() {
if (BuildConfig.DEBUG)
Log.d(TAG, "On Start");
super.onStart();
if (media != null) {
if (BuildConfig.DEBUG)
Log.d(TAG, "Loading media info");
loadMediaInfo();
} else {
Log.w(TAG, "Unable to load media info: media was null");
}
}
@Override
public void onStart() {
if (BuildConfig.DEBUG)
Log.d(TAG, "On Start");
super.onStart();
if (media != null) {
if (BuildConfig.DEBUG)
Log.d(TAG, "Loading media info");
loadMediaInfo();
} else {
Log.w(TAG, "Unable to load media info: media was null");
}
}
@Override
public void onDataSetChanged(Playable media) {
this.media = media;
if (viewCreated) {
loadMediaInfo();
}
@Override
public void onDataSetChanged(Playable media) {
this.media = media;
if (viewCreated) {
loadMediaInfo();
}
}
}
}

View File

@ -10,9 +10,10 @@ import android.view.ViewGroup;
import android.widget.ImageButton;
import android.widget.ImageView;
import android.widget.TextView;
import de.danoeh.antennapod.BuildConfig;
import de.danoeh.antennapod.R;
import de.danoeh.antennapod.asynctask.ImageLoader;
import de.danoeh.antennapod.asynctask.PicassoProvider;
import de.danoeh.antennapod.service.playback.PlaybackService;
import de.danoeh.antennapod.util.Converter;
import de.danoeh.antennapod.util.playback.Playable;
@ -23,215 +24,216 @@ import de.danoeh.antennapod.util.playback.PlaybackController;
* if the PlaybackService is running
*/
public class ExternalPlayerFragment extends Fragment {
private static final String TAG = "ExternalPlayerFragment";
private static final String TAG = "ExternalPlayerFragment";
private ViewGroup fragmentLayout;
private ImageView imgvCover;
private ViewGroup layoutInfo;
private TextView txtvTitle;
private ImageButton butPlay;
private ViewGroup fragmentLayout;
private ImageView imgvCover;
private ViewGroup layoutInfo;
private TextView txtvTitle;
private ImageButton butPlay;
private PlaybackController controller;
private PlaybackController controller;
public ExternalPlayerFragment() {
super();
}
public ExternalPlayerFragment() {
super();
}
@Override
public View onCreateView(LayoutInflater inflater, ViewGroup container,
Bundle savedInstanceState) {
View root = inflater.inflate(R.layout.external_player_fragment,
container, false);
fragmentLayout = (ViewGroup) root.findViewById(R.id.fragmentLayout);
imgvCover = (ImageView) root.findViewById(R.id.imgvCover);
layoutInfo = (ViewGroup) root.findViewById(R.id.layoutInfo);
txtvTitle = (TextView) root.findViewById(R.id.txtvTitle);
butPlay = (ImageButton) root.findViewById(R.id.butPlay);
@Override
public View onCreateView(LayoutInflater inflater, ViewGroup container,
Bundle savedInstanceState) {
View root = inflater.inflate(R.layout.external_player_fragment,
container, false);
fragmentLayout = (ViewGroup) root.findViewById(R.id.fragmentLayout);
imgvCover = (ImageView) root.findViewById(R.id.imgvCover);
layoutInfo = (ViewGroup) root.findViewById(R.id.layoutInfo);
txtvTitle = (TextView) root.findViewById(R.id.txtvTitle);
butPlay = (ImageButton) root.findViewById(R.id.butPlay);
layoutInfo.setOnClickListener(new OnClickListener() {
layoutInfo.setOnClickListener(new OnClickListener() {
@Override
public void onClick(View v) {
if (BuildConfig.DEBUG)
Log.d(TAG, "layoutInfo was clicked");
@Override
public void onClick(View v) {
if (BuildConfig.DEBUG)
Log.d(TAG, "layoutInfo was clicked");
if (controller.getMedia() != null) {
startActivity(PlaybackService.getPlayerActivityIntent(
getActivity(), controller.getMedia()));
}
}
});
return root;
}
if (controller.getMedia() != null) {
startActivity(PlaybackService.getPlayerActivityIntent(
getActivity(), controller.getMedia()));
}
}
});
return root;
}
@Override
public void onActivityCreated(Bundle savedInstanceState) {
super.onActivityCreated(savedInstanceState);
controller = setupPlaybackController();
butPlay.setOnClickListener(controller.newOnPlayButtonClickListener());
}
@Override
public void onActivityCreated(Bundle savedInstanceState) {
super.onActivityCreated(savedInstanceState);
controller = setupPlaybackController();
butPlay.setOnClickListener(controller.newOnPlayButtonClickListener());
}
private PlaybackController setupPlaybackController() {
return new PlaybackController(getActivity(), true) {
private PlaybackController setupPlaybackController() {
return new PlaybackController(getActivity(), true) {
@Override
public void setupGUI() {
}
@Override
public void setupGUI() {
}
@Override
public void onPositionObserverUpdate() {
}
@Override
public void onPositionObserverUpdate() {
}
@Override
public void onReloadNotification(int code) {
}
@Override
public void onReloadNotification(int code) {
}
@Override
public void onBufferStart() {
// TODO Auto-generated method stub
@Override
public void onBufferStart() {
// TODO Auto-generated method stub
}
}
@Override
public void onBufferEnd() {
// TODO Auto-generated method stub
@Override
public void onBufferEnd() {
// TODO Auto-generated method stub
}
}
@Override
public void onBufferUpdate(float progress) {
}
@Override
public void onBufferUpdate(float progress) {
}
@Override
public void onSleepTimerUpdate() {
}
@Override
public void onSleepTimerUpdate() {
}
@Override
public void handleError(int code) {
}
@Override
public void handleError(int code) {
}
@Override
public ImageButton getPlayButton() {
return butPlay;
}
@Override
public ImageButton getPlayButton() {
return butPlay;
}
@Override
public void postStatusMsg(int msg) {
}
@Override
public void postStatusMsg(int msg) {
}
@Override
public void clearStatusMsg() {
}
@Override
public void clearStatusMsg() {
}
@Override
public boolean loadMediaInfo() {
@Override
public boolean loadMediaInfo() {
ExternalPlayerFragment fragment = ExternalPlayerFragment.this;
if (fragment != null) {
return fragment.loadMediaInfo();
return fragment.loadMediaInfo();
} else {
return false;
}
}
}
@Override
public void onAwaitingVideoSurface() {
}
@Override
public void onAwaitingVideoSurface() {
}
@Override
public void onServiceQueried() {
}
@Override
public void onServiceQueried() {
}
@Override
public void onShutdownNotification() {
if (fragmentLayout != null) {
fragmentLayout.setVisibility(View.GONE);
}
controller = setupPlaybackController();
if (butPlay != null) {
butPlay.setOnClickListener(controller
.newOnPlayButtonClickListener());
}
@Override
public void onShutdownNotification() {
if (fragmentLayout != null) {
fragmentLayout.setVisibility(View.GONE);
}
controller = setupPlaybackController();
if (butPlay != null) {
butPlay.setOnClickListener(controller
.newOnPlayButtonClickListener());
}
}
}
@Override
public void onPlaybackEnd() {
if (fragmentLayout != null) {
fragmentLayout.setVisibility(View.GONE);
}
controller = setupPlaybackController();
if (butPlay != null) {
butPlay.setOnClickListener(controller
.newOnPlayButtonClickListener());
}
}
@Override
public void onPlaybackEnd() {
if (fragmentLayout != null) {
fragmentLayout.setVisibility(View.GONE);
}
controller = setupPlaybackController();
if (butPlay != null) {
butPlay.setOnClickListener(controller
.newOnPlayButtonClickListener());
}
}
@Override
public void onPlaybackSpeedChange() {
// TODO Auto-generated method stub
@Override
public void onPlaybackSpeedChange() {
// TODO Auto-generated method stub
}
};
}
}
};
}
@Override
public void onResume() {
super.onResume();
controller.init();
}
@Override
public void onResume() {
super.onResume();
controller.init();
}
@Override
public void onDestroy() {
super.onDestroy();
if (BuildConfig.DEBUG)
Log.d(TAG, "Fragment is about to be destroyed");
if (controller != null) {
controller.release();
}
}
@Override
public void onDestroy() {
super.onDestroy();
if (BuildConfig.DEBUG)
Log.d(TAG, "Fragment is about to be destroyed");
if (controller != null) {
controller.release();
}
}
@Override
public void onPause() {
super.onPause();
if (controller != null) {
controller.pause();
}
}
@Override
public void onPause() {
super.onPause();
if (controller != null) {
controller.pause();
}
}
private boolean loadMediaInfo() {
if (BuildConfig.DEBUG)
Log.d(TAG, "Loading media info");
if (controller.serviceAvailable()) {
Playable media = controller.getMedia();
if (media != null) {
txtvTitle.setText(media.getEpisodeTitle());
ImageLoader.getInstance().loadThumbnailBitmap(
media,
imgvCover,
(int) getActivity().getResources().getDimension(
R.dimen.external_player_height));
private boolean loadMediaInfo() {
if (BuildConfig.DEBUG)
Log.d(TAG, "Loading media info");
if (controller.serviceAvailable()) {
Playable media = controller.getMedia();
if (media != null) {
txtvTitle.setText(media.getEpisodeTitle());
fragmentLayout.setVisibility(View.VISIBLE);
if (controller.isPlayingVideo()) {
butPlay.setVisibility(View.GONE);
} else {
butPlay.setVisibility(View.VISIBLE);
}
int imageSize = (int) getResources().getDimension(R.dimen.external_player_height);
PicassoProvider.getMediaMetadataPicassoInstance(getActivity())
.load(media.getImageUri())
.resize(imageSize, imageSize)
.into(imgvCover);
fragmentLayout.setVisibility(View.VISIBLE);
if (controller.isPlayingVideo()) {
butPlay.setVisibility(View.GONE);
} else {
butPlay.setVisibility(View.VISIBLE);
}
return true;
} else {
Log.w(TAG,
"loadMediaInfo was called while the media object of playbackService was null!");
} else {
Log.w(TAG,
"loadMediaInfo was called while the media object of playbackService was null!");
return false;
}
} else {
Log.w(TAG,
"loadMediaInfo was called while playbackService was null!");
}
} else {
Log.w(TAG,
"loadMediaInfo was called while playbackService was null!");
return false;
}
}
}
}
private String getPositionString(int position, int duration) {
return Converter.getDurationStringLong(position) + " / "
+ Converter.getDurationStringLong(duration);
}
private String getPositionString(int position, int duration) {
return Converter.getDurationStringLong(position) + " / "
+ Converter.getDurationStringLong(duration);
}
}

View File

@ -34,7 +34,7 @@ import de.danoeh.antennapod.adapter.DefaultActionButtonCallback;
import de.danoeh.antennapod.adapter.FeedItemlistAdapter;
import de.danoeh.antennapod.asynctask.DownloadObserver;
import de.danoeh.antennapod.asynctask.FeedRemover;
import de.danoeh.antennapod.asynctask.ImageLoader;
import de.danoeh.antennapod.asynctask.PicassoProvider;
import de.danoeh.antennapod.dialog.ConfirmationDialog;
import de.danoeh.antennapod.dialog.DownloadRequestErrorDialogCreator;
import de.danoeh.antennapod.dialog.FeedItemDialog;
@ -349,8 +349,13 @@ public class ItemlistFragment extends ListFragment {
txtvTitle.setText(feed.getTitle());
txtvAuthor.setText(feed.getAuthor());
ImageLoader.getInstance().loadThumbnailBitmap(feed.getImage(), imgvCover,
(int) getResources().getDimension(R.dimen.thumbnail_length_onlinefeedview));
int imageSize = (int) getResources().getDimension(R.dimen.thumbnail_length_onlinefeedview);
PicassoProvider.getDefaultPicassoInstance(getActivity())
.load(feed.getImageUri())
.resize(imageSize, imageSize)
.into(imgvCover);
if (feed.getLink() == null) {
butVisitWebsite.setVisibility(View.INVISIBLE);
} else {

View File

@ -4,7 +4,12 @@ import android.annotation.SuppressLint;
import android.app.Notification;
import android.app.PendingIntent;
import android.app.Service;
import android.content.*;
import android.content.BroadcastReceiver;
import android.content.ComponentName;
import android.content.Context;
import android.content.Intent;
import android.content.IntentFilter;
import android.content.SharedPreferences;
import android.graphics.Bitmap;
import android.graphics.BitmapFactory;
import android.media.AudioManager;
@ -26,11 +31,14 @@ import android.widget.Toast;
import org.apache.commons.lang3.StringUtils;
import java.io.IOException;
import java.util.List;
import de.danoeh.antennapod.BuildConfig;
import de.danoeh.antennapod.PodcastApp;
import de.danoeh.antennapod.R;
import de.danoeh.antennapod.activity.AudioplayerActivity;
import de.danoeh.antennapod.activity.VideoplayerActivity;
import de.danoeh.antennapod.asynctask.PicassoProvider;
import de.danoeh.antennapod.feed.Chapter;
import de.danoeh.antennapod.feed.FeedItem;
import de.danoeh.antennapod.feed.FeedMedia;
@ -41,14 +49,10 @@ import de.danoeh.antennapod.receiver.MediaButtonReceiver;
import de.danoeh.antennapod.receiver.PlayerWidget;
import de.danoeh.antennapod.storage.DBTasks;
import de.danoeh.antennapod.storage.DBWriter;
import de.danoeh.antennapod.util.BitmapDecoder;
import de.danoeh.antennapod.util.QueueAccess;
import de.danoeh.antennapod.util.flattr.FlattrThing;
import de.danoeh.antennapod.util.flattr.FlattrUtils;
import de.danoeh.antennapod.util.playback.Playable;
import java.util.List;
/**
* Controls the MediaPlayer that plays a FeedMedia-file
*/
@ -257,7 +261,8 @@ public class PlaybackService extends Service {
}
if ((flags & Service.START_FLAG_REDELIVERY) != 0) {
if (BuildConfig.DEBUG) Log.d(TAG, "onStartCommand is a redelivered intent, calling stopForeground now.");
if (BuildConfig.DEBUG)
Log.d(TAG, "onStartCommand is a redelivered intent, calling stopForeground now.");
stopForeground(true);
} else {
@ -678,11 +683,16 @@ public class PlaybackService extends Service {
Log.d(TAG, "Starting background work");
if (android.os.Build.VERSION.SDK_INT >= 11) {
if (info.playable != null) {
int iconSize = getResources().getDimensionPixelSize(
android.R.dimen.notification_large_icon_width);
icon = BitmapDecoder
.decodeBitmapFromWorkerTaskResource(iconSize,
info.playable);
try {
int iconSize = getResources().getDimensionPixelSize(
android.R.dimen.notification_large_icon_width);
icon = PicassoProvider.getMediaMetadataPicassoInstance(PlaybackService.this)
.load(info.playable.getImageUri())
.resize(iconSize, iconSize)
.get();
} catch (IOException e) {
e.printStackTrace();
}
}
}
@ -766,7 +776,7 @@ public class PlaybackService extends Service {
if (updatePlayedDuration && playable instanceof FeedMedia) {
FeedMedia m = (FeedMedia) playable;
FeedItem item = m.getItem();
m.setPlayedDuration(m.getPlayedDuration() + ((int)(deltaPlayedDuration * playbackSpeed)));
m.setPlayedDuration(m.getPlayedDuration() + ((int) (deltaPlayedDuration * playbackSpeed)));
// Auto flattr
if (isAutoFlattrable(m) &&
(m.getPlayedDuration() > UserPreferences.getAutoFlattrPlayedDurationThreshold() * duration)) {
@ -778,8 +788,9 @@ public class PlaybackService extends Service {
}
}
playable.saveCurrentPosition(PreferenceManager
.getDefaultSharedPreferences(getApplicationContext()),
position);
.getDefaultSharedPreferences(getApplicationContext()),
position
);
}
}

View File

@ -1,50 +0,0 @@
package de.danoeh.antennapod.util;
import android.graphics.Bitmap;
import android.graphics.BitmapFactory;
import android.graphics.Rect;
import android.util.Log;
import de.danoeh.antennapod.BuildConfig;
import de.danoeh.antennapod.asynctask.ImageLoader;
import org.apache.commons.io.IOUtils;
import java.io.InputStream;
public class BitmapDecoder {
private static final String TAG = "BitmapDecoder";
private static int calculateSampleSize(int preferredLength, int length) {
int sampleSize = 1;
if (length > preferredLength) {
sampleSize = Math.round(((float) length / (float) preferredLength));
}
return sampleSize;
}
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.decodeStream(input, new Rect(), options);
int srcWidth = options.outWidth;
int srcHeight = options.outHeight;
int length = Math.max(srcWidth, srcHeight);
int sampleSize = calculateSampleSize(preferredLength, length);
if (BuildConfig.DEBUG)
Log.d(TAG, "Using samplesize " + sampleSize);
options.inJustDecodeBounds = false;
options.inSampleSize = sampleSize;
options.inPreferredConfig = Bitmap.Config.ARGB_8888;
Bitmap decodedBitmap = BitmapFactory.decodeStream(source.reopenImageInputStream(input),
null, options);
if (decodedBitmap == null) {
decodedBitmap = BitmapFactory.decodeStream(source.reopenImageInputStream(input));
}
IOUtils.closeQuietly(input);
return decodedBitmap;
}
return null;
}
}

View File

@ -3,12 +3,14 @@ package de.danoeh.antennapod.util.playback;
import android.content.SharedPreferences;
import android.content.SharedPreferences.Editor;
import android.media.MediaMetadataRetriever;
import android.net.Uri;
import android.os.Parcel;
import android.os.Parcelable;
import de.danoeh.antennapod.feed.Chapter;
import de.danoeh.antennapod.feed.MediaType;
import de.danoeh.antennapod.util.ChapterUtils;
import java.io.File;
import java.io.InputStream;
import java.util.List;
import java.util.concurrent.Callable;
@ -224,22 +226,12 @@ 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);
}
@Override
public Uri getImageUri() {
if (localFileAvailable()) {
return new Uri.Builder().scheme(SCHEME_MEDIA).encodedPath(getLocalMediaUrl()).build();
} else {
return null;
}
}
}

View File

@ -2,27 +2,23 @@ package de.danoeh.antennapod.util.playback;
import android.content.Context;
import android.content.SharedPreferences;
import android.media.MediaMetadataRetriever;
import android.os.Parcelable;
import android.util.Log;
import de.danoeh.antennapod.asynctask.ImageLoader;
import java.util.List;
import de.danoeh.antennapod.asynctask.PicassoImageResource;
import de.danoeh.antennapod.feed.Chapter;
import de.danoeh.antennapod.feed.FeedMedia;
import de.danoeh.antennapod.feed.MediaType;
import de.danoeh.antennapod.storage.DBReader;
import de.danoeh.antennapod.util.ShownotesProvider;
import org.apache.commons.io.IOUtils;
import org.apache.commons.lang3.Validate;
import java.io.ByteArrayInputStream;
import java.io.InputStream;
import java.util.List;
/**
* Interface for objects that can be played by the PlaybackService.
*/
public interface Playable extends Parcelable,
ImageLoader.ImageWorkerTaskResource, ShownotesProvider {
ShownotesProvider, PicassoImageResource {
/**
* Save information about the playable in a preference so that it can be
@ -208,69 +204,4 @@ public interface Playable extends Parcelable,
}
}
/**
* Uses local file as image resource if it is available.
*/
public static class DefaultPlayableImageLoader implements
ImageLoader.ImageWorkerTaskResource {
private Playable playable;
public DefaultPlayableImageLoader(Playable playable) {
Validate.notNull(playable);
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;
}
}
}
}