diff --git a/build.gradle b/build.gradle index 140d483da..f0df351b9 100644 --- a/build.gradle +++ b/build.gradle @@ -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 { diff --git a/proguard.cfg b/proguard.cfg index 323e0b673..1838f007c 100644 --- a/proguard.cfg +++ b/proguard.cfg @@ -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.** { *; } diff --git a/src/de/danoeh/antennapod/PodcastApp.java b/src/de/danoeh/antennapod/PodcastApp.java index 4c4766327..74628f3d6 100644 --- a/src/de/danoeh/antennapod/PodcastApp.java +++ b/src/de/danoeh/antennapod/PodcastApp.java @@ -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; } diff --git a/src/de/danoeh/antennapod/activity/AudioplayerActivity.java b/src/de/danoeh/antennapod/activity/AudioplayerActivity.java index 6373ff240..50f5d8f2e 100644 --- a/src/de/danoeh/antennapod/activity/AudioplayerActivity.java +++ b/src/de/danoeh/antennapod/activity/AudioplayerActivity.java @@ -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])); diff --git a/src/de/danoeh/antennapod/activity/DefaultOnlineFeedViewActivity.java b/src/de/danoeh/antennapod/activity/DefaultOnlineFeedViewActivity.java index e89f8d05c..86b278bf0 100644 --- a/src/de/danoeh/antennapod/activity/DefaultOnlineFeedViewActivity.java +++ b/src/de/danoeh/antennapod/activity/DefaultOnlineFeedViewActivity.java @@ -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()); diff --git a/src/de/danoeh/antennapod/activity/FeedInfoActivity.java b/src/de/danoeh/antennapod/activity/FeedInfoActivity.java index 7f60d0b10..b46bc7546 100644 --- a/src/de/danoeh/antennapod/activity/FeedInfoActivity.java +++ b/src/de/danoeh/antennapod/activity/FeedInfoActivity.java @@ -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); } }); diff --git a/src/de/danoeh/antennapod/adapter/DownloadedEpisodesListAdapter.java b/src/de/danoeh/antennapod/adapter/DownloadedEpisodesListAdapter.java index 33b11774f..641a1368d 100644 --- a/src/de/danoeh/antennapod/adapter/DownloadedEpisodesListAdapter.java +++ b/src/de/danoeh/antennapod/adapter/DownloadedEpisodesListAdapter.java @@ -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; } diff --git a/src/de/danoeh/antennapod/adapter/ExternalEpisodesListAdapter.java b/src/de/danoeh/antennapod/adapter/ExternalEpisodesListAdapter.java index 5e857c131..56c3e1ca6 100644 --- a/src/de/danoeh/antennapod/adapter/ExternalEpisodesListAdapter.java +++ b/src/de/danoeh/antennapod/adapter/ExternalEpisodesListAdapter.java @@ -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); } diff --git a/src/de/danoeh/antennapod/adapter/NavListAdapter.java b/src/de/danoeh/antennapod/adapter/NavListAdapter.java index 9676372fb..ed85c8836 100644 --- a/src/de/danoeh/antennapod/adapter/NavListAdapter.java +++ b/src/de/danoeh/antennapod/adapter/NavListAdapter.java @@ -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; } diff --git a/src/de/danoeh/antennapod/adapter/NewEpisodesListAdapter.java b/src/de/danoeh/antennapod/adapter/NewEpisodesListAdapter.java index 07fd3e6b1..4370de14d 100644 --- a/src/de/danoeh/antennapod/adapter/NewEpisodesListAdapter.java +++ b/src/de/danoeh/antennapod/adapter/NewEpisodesListAdapter.java @@ -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; } diff --git a/src/de/danoeh/antennapod/adapter/QueueListAdapter.java b/src/de/danoeh/antennapod/adapter/QueueListAdapter.java index f671ba5c6..c670089b9 100644 --- a/src/de/danoeh/antennapod/adapter/QueueListAdapter.java +++ b/src/de/danoeh/antennapod/adapter/QueueListAdapter.java @@ -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; } diff --git a/src/de/danoeh/antennapod/adapter/SearchlistAdapter.java b/src/de/danoeh/antennapod/adapter/SearchlistAdapter.java index ecfbb4660..6b1fefaad 100644 --- a/src/de/danoeh/antennapod/adapter/SearchlistAdapter.java +++ b/src/de/danoeh/antennapod/adapter/SearchlistAdapter.java @@ -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); } diff --git a/src/de/danoeh/antennapod/adapter/gpodnet/PodcastListAdapter.java b/src/de/danoeh/antennapod/adapter/gpodnet/PodcastListAdapter.java index f20232a6f..dcad2d524 100644 --- a/src/de/danoeh/antennapod/adapter/gpodnet/PodcastListAdapter.java +++ b/src/de/danoeh/antennapod/adapter/gpodnet/PodcastListAdapter.java @@ -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 { - private final ImageDiskCache diskCache; private final int thumbnailLength; public PodcastListAdapter(Context context, int resource, List 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 { 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; } diff --git a/src/de/danoeh/antennapod/asynctask/BitmapDecodeWorkerTask.java b/src/de/danoeh/antennapod/asynctask/BitmapDecodeWorkerTask.java deleted file mode 100644 index 43118c3af..000000000 --- a/src/de/danoeh/antennapod/asynctask/BitmapDecodeWorkerTask.java +++ /dev/null @@ -1,115 +0,0 @@ -package de.danoeh.antennapod.asynctask; - -import android.graphics.BitmapFactory; -import android.graphics.drawable.BitmapDrawable; -import android.graphics.drawable.Drawable; -import android.graphics.drawable.TransitionDrawable; -import android.os.Handler; -import android.util.Log; -import android.widget.ImageView; -import de.danoeh.antennapod.BuildConfig; -import de.danoeh.antennapod.PodcastApp; -import de.danoeh.antennapod.R; -import de.danoeh.antennapod.asynctask.ImageLoader.ImageWorkerTaskResource; -import de.danoeh.antennapod.util.BitmapDecoder; - -public class BitmapDecodeWorkerTask extends Thread { - - protected int PREFERRED_LENGTH; - public static final int FADE_DURATION = 500; - - /** - * Can be thumbnail or cover - */ - protected int imageType; - - private static final String TAG = "BitmapDecodeWorkerTask"; - private ImageView target; - protected CachedBitmap cBitmap; - - protected ImageLoader.ImageWorkerTaskResource imageResource; - - private Handler handler; - - private final int defaultCoverResource; - - public BitmapDecodeWorkerTask(Handler handler, ImageView target, - ImageWorkerTaskResource imageResource, int length, int imageType) { - super(); - this.handler = handler; - this.target = target; - this.imageResource = imageResource; - this.PREFERRED_LENGTH = length; - this.imageType = imageType; - this.defaultCoverResource = android.R.color.transparent; - } - - /** - * Should return true if tag of the imageview is still the same it was - * before the bitmap was decoded - */ - protected boolean tagsMatching(ImageView target) { - Object tag = target.getTag(R.id.imageloader_key); - return tag != null && tag.equals(imageResource.getImageLoaderCacheKey()); - } - - protected void onPostExecute() { - // check if imageview is still supposed to display this image - if (tagsMatching(target) && cBitmap.getBitmap() != null) { - Drawable[] drawables = new Drawable[]{ - PodcastApp.getInstance().getResources().getDrawable(android.R.color.transparent), - new BitmapDrawable(PodcastApp.getInstance().getResources(), cBitmap.getBitmap()) - }; - TransitionDrawable transitionDrawable = new TransitionDrawable(drawables); - target.setImageDrawable(transitionDrawable); - transitionDrawable.startTransition(FADE_DURATION); - } else { - if (BuildConfig.DEBUG) - Log.d(TAG, "Not displaying image"); - } - } - - @Override - public void run() { - cBitmap = new CachedBitmap(BitmapDecoder.decodeBitmapFromWorkerTaskResource( - PREFERRED_LENGTH, imageResource), PREFERRED_LENGTH); - if (cBitmap.getBitmap() != null) { - storeBitmapInCache(cBitmap); - } else { - Log.w(TAG, "Could not load bitmap. Using default image."); - cBitmap = new CachedBitmap(BitmapFactory.decodeResource( - target.getResources(), defaultCoverResource), - PREFERRED_LENGTH); - } - if (BuildConfig.DEBUG) - Log.d(TAG, "Finished loading bitmaps"); - - endBackgroundTask(); - } - - protected final void endBackgroundTask() { - handler.post(new Runnable() { - - @Override - public void run() { - onPostExecute(); - } - - }); - } - - protected void onInvalidStream() { - cBitmap = new CachedBitmap(BitmapFactory.decodeResource( - target.getResources(), defaultCoverResource), PREFERRED_LENGTH); - } - - protected void storeBitmapInCache(CachedBitmap cb) { - ImageLoader loader = ImageLoader.getInstance(); - if (imageType == ImageLoader.IMAGE_TYPE_COVER) { - loader.addBitmapToCoverCache(imageResource.getImageLoaderCacheKey(), cb); - } else if (imageType == ImageLoader.IMAGE_TYPE_THUMBNAIL) { - loader.addBitmapToThumbnailCache(imageResource.getImageLoaderCacheKey(), cb); - } - } - -} diff --git a/src/de/danoeh/antennapod/asynctask/CachedBitmap.java b/src/de/danoeh/antennapod/asynctask/CachedBitmap.java deleted file mode 100644 index 5a89b7b53..000000000 --- a/src/de/danoeh/antennapod/asynctask/CachedBitmap.java +++ /dev/null @@ -1,27 +0,0 @@ -package de.danoeh.antennapod.asynctask; - -import android.graphics.Bitmap; - -/** Stores a bitmap and the length it was decoded with. */ -public class CachedBitmap { - - private Bitmap bitmap; - private int length; - - public CachedBitmap(Bitmap bitmap, int length) { - super(); - this.bitmap = bitmap; - this.length = length; - } - - public Bitmap getBitmap() { - return bitmap; - } - public int getLength() { - return length; - } - - - - -} diff --git a/src/de/danoeh/antennapod/asynctask/ImageDiskCache.java b/src/de/danoeh/antennapod/asynctask/ImageDiskCache.java deleted file mode 100644 index 77609f28b..000000000 --- a/src/de/danoeh/antennapod/asynctask/ImageDiskCache.java +++ /dev/null @@ -1,397 +0,0 @@ -package de.danoeh.antennapod.asynctask; - -import android.os.Handler; -import android.util.Log; -import android.util.Pair; -import android.widget.ImageView; -import de.danoeh.antennapod.BuildConfig; -import de.danoeh.antennapod.PodcastApp; -import de.danoeh.antennapod.R; -import de.danoeh.antennapod.service.download.DownloadRequest; -import de.danoeh.antennapod.service.download.HttpDownloader; -import org.apache.commons.io.IOUtils; -import org.apache.commons.lang3.StringUtils; -import org.apache.commons.lang3.Validate; - -import java.io.*; -import java.util.ArrayList; -import java.util.Collection; -import java.util.HashMap; -import java.util.List; -import java.util.concurrent.ConcurrentHashMap; -import java.util.concurrent.ExecutorService; -import java.util.concurrent.Executors; - -/** - * Provides local cache for storing downloaded image. An image disk cache downloads images and stores them as long - * as the cache is not full. Once the cache is full, the image disk cache will delete older images. - */ -public class ImageDiskCache { - private static final String TAG = "ImageDiskCache"; - - private static HashMap cacheSingletons = new HashMap(); - - /** - * 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 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) objectInput.readObject(); - // calculate cache size - for (DiskCacheObject dco : diskCache.values()) { - cacheSize += dco.size; - } - deleteInvalidFiles(); - } catch (IOException e) { - e.printStackTrace(); - diskCache = new ConcurrentHashMap(); - } catch (ClassCastException e) { - e.printStackTrace(); - diskCache = new ConcurrentHashMap(); - } catch (ClassNotFoundException e) { - e.printStackTrace(); - diskCache = new ConcurrentHashMap(); - } - } else { - diskCache = new ConcurrentHashMap(); - } - } - } - - private List getCacheFileList() { - Collection values = diskCache.values(); - List files = new ArrayList(); - for (DiskCacheObject dco : values) { - files.add(dco.getFile()); - } - files.add(new File(cacheFolder, CACHE_FILE_NAME)); - return files; - } - - private Pair getOldestCacheObject() { - Collection 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(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 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 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 runningDownloads = new ConcurrentHashMap(); - - private abstract class ImageDownloader implements Runnable { - private String downloadUrl; - - public ImageDownloader(String downloadUrl) { - this.downloadUrl = downloadUrl; - } - - protected abstract void onImageLoaded(DiskCacheObject diskCacheObject); - - public void run() { - DiskCacheObject tmp = getFromCacheIfAvailable(downloadUrl); - if (tmp != null) { - onImageLoaded(tmp); - return; - } - - DiskCacheObject dco = null; - File newFile = new File(cacheFolder, Integer.toString(downloadUrl.hashCode())); - synchronized (ImageDiskCache.this) { - if (runningDownloads.containsKey(newFile.getAbsolutePath())) { - Log.d(TAG, "Download is already running: " + newFile.getAbsolutePath()); - return; - } else { - runningDownloads.put(newFile.getAbsolutePath(), newFile); - } - } - if (newFile.exists()) { - newFile.delete(); - } - - HttpDownloader result = downloadFile(newFile.getAbsolutePath(), downloadUrl); - if (result.getResult().isSuccessful()) { - long size = result.getDownloadRequest().getSoFar(); - - dco = new DiskCacheObject(newFile.getAbsolutePath(), size); - addToDiskCache(downloadUrl, dco); - if (BuildConfig.DEBUG) Log.d(TAG, "Image was downloaded"); - } else { - Log.w(TAG, "Download of url " + downloadUrl + " failed. Reason: " + result.getResult().getReasonDetailed() + "(" + result.getResult().getReason() + ")"); - } - - if (dco != null) { - final DiskCacheObject dcoRef = dco; - handler.post(new Runnable() { - @Override - public void run() { - onImageLoaded(dcoRef); - } - }); - - } - runningDownloads.remove(newFile.getAbsolutePath()); - - } - - private HttpDownloader downloadFile(String destination, String source) { - DownloadRequest request = new DownloadRequest(destination, source, "", 0, 0); - HttpDownloader downloader = new HttpDownloader(request); - downloader.call(); - return downloader; - } - } - - private static class DiskCacheObject implements Serializable { - private final String fileUrl; - - /** - * Last usage of this image cache object. - */ - private long timestamp; - private final long size; - - public DiskCacheObject(String fileUrl, long size) { - Validate.notNull(fileUrl); - this.fileUrl = fileUrl; - this.timestamp = System.currentTimeMillis(); - this.size = size; - } - - public File getFile() { - return new File(fileUrl); - } - - public ImageLoader.ImageWorkerTaskResource loadImage() { - return new ImageLoader.ImageWorkerTaskResource() { - - @Override - public InputStream openImageInputStream() { - try { - return new FileInputStream(getFile()); - } catch (FileNotFoundException e) { - e.printStackTrace(); - } - return null; - } - - @Override - public InputStream reopenImageInputStream(InputStream input) { - IOUtils.closeQuietly(input); - return openImageInputStream(); - } - - @Override - public String getImageLoaderCacheKey() { - return fileUrl; - } - }; - } - } -} diff --git a/src/de/danoeh/antennapod/asynctask/ImageLoader.java b/src/de/danoeh/antennapod/asynctask/ImageLoader.java deleted file mode 100644 index 6c60b7b1f..000000000 --- a/src/de/danoeh/antennapod/asynctask/ImageLoader.java +++ /dev/null @@ -1,246 +0,0 @@ -package de.danoeh.antennapod.asynctask; - -import android.annotation.SuppressLint; -import android.app.ActivityManager; -import android.content.Context; -import android.os.Handler; -import android.support.v4.util.LruCache; -import android.util.Log; -import android.widget.ImageView; -import de.danoeh.antennapod.BuildConfig; -import de.danoeh.antennapod.PodcastApp; -import de.danoeh.antennapod.R; - -import java.io.InputStream; -import java.util.concurrent.ExecutorService; -import java.util.concurrent.Executors; -import java.util.concurrent.ThreadFactory; - -/** - * Caches and loads FeedImage bitmaps in the background - */ -public class ImageLoader { - private static final String TAG = "ImageLoader"; - private static ImageLoader singleton; - - public static final int IMAGE_TYPE_THUMBNAIL = 0; - public static final int IMAGE_TYPE_COVER = 1; - - /** - * Used by loadThumbnailBitmap and loadCoverBitmap to denote an ImageView that displays the default image resource. - * This is the case if the given source to load the image from was null or did not return any image data. - */ - private static final Object DEFAULT_IMAGE_RESOURCE_TAG = new Object(); - - private Handler handler; - private ExecutorService executor; - - /** - * Stores references to loaded bitmaps. Bitmaps can be accessed by the id of - * the FeedImage the bitmap belongs to. - */ - - final int memClass = ((ActivityManager) PodcastApp.getInstance() - .getSystemService(Context.ACTIVITY_SERVICE)).getMemoryClass(); - - // Use 1/8th of the available memory for this memory cache. - final int thumbnailCacheSize = 1024 * 1024 * memClass / 8; - - private LruCache coverCache; - private LruCache thumbnailCache; - - private ImageLoader() { - handler = new Handler(); - executor = createExecutor(); - - coverCache = new LruCache(1); - - thumbnailCache = new LruCache(thumbnailCacheSize) { - - @SuppressLint("NewApi") - @Override - protected int sizeOf(String key, CachedBitmap value) { - if (Integer.valueOf(android.os.Build.VERSION.SDK_INT) >= 12) - return value.getBitmap().getByteCount(); - else - return (value.getBitmap().getRowBytes() * value.getBitmap() - .getHeight()); - - } - - }; - } - - private ExecutorService createExecutor() { - return Executors.newFixedThreadPool(Runtime.getRuntime() - .availableProcessors(), new ThreadFactory() { - - @Override - public Thread newThread(Runnable r) { - Thread t = new Thread(r); - t.setPriority(Thread.MIN_PRIORITY); - return t; - } - }); - } - - public static synchronized ImageLoader getInstance() { - if (singleton == null) { - singleton = new ImageLoader(); - } - return singleton; - } - - /** - * Load a bitmap from the cover cache. If the bitmap is not in the cache, it - * will be loaded from the disk. This method should either be called if the - * ImageView's size has already been set or inside a Runnable which is - * posted to the ImageView's message queue. - */ - public void loadCoverBitmap(ImageWorkerTaskResource source, ImageView target) { - loadCoverBitmap(source, target, target.getHeight()); - } - - /** - * Load a bitmap from the cover cache. If the bitmap is not in the cache, it - * will be loaded from the disk. This method should either be called if the - * ImageView's size has already been set or inside a Runnable which is - * posted to the ImageView's message queue. - */ - public void loadCoverBitmap(ImageWorkerTaskResource source, - ImageView target, int length) { - final int defaultCoverResource = getDefaultCoverResource(target - .getContext()); - final String cacheKey; - if (source != null && (cacheKey = source.getImageLoaderCacheKey()) != null) { - final Object currentTag = target.getTag(R.id.imageloader_key); - if (currentTag == null || !cacheKey.equals(currentTag)) { - target.setTag(R.id.imageloader_key, cacheKey); - CachedBitmap cBitmap = getBitmapFromCoverCache(cacheKey); - if (cBitmap != null && cBitmap.getLength() >= length) { - target.setImageBitmap(cBitmap.getBitmap()); - } else { - target.setImageResource(defaultCoverResource); - BitmapDecodeWorkerTask worker = new BitmapDecodeWorkerTask( - handler, target, source, length, IMAGE_TYPE_COVER); - executor.submit(worker); - } - } - } else { - target.setImageResource(defaultCoverResource); - target.setTag(R.id.imageloader_key, DEFAULT_IMAGE_RESOURCE_TAG); - } - } - - /** - * Load a bitmap from the thumbnail cache. If the bitmap is not in the - * cache, it will be loaded from the disk. This method should either be - * called if the ImageView's size has already been set or inside a Runnable - * which is posted to the ImageView's message queue. - */ - public void loadThumbnailBitmap(ImageWorkerTaskResource source, - ImageView target) { - loadThumbnailBitmap(source, target, target.getHeight()); - } - - /** - * Load a bitmap from the thumbnail cache. If the bitmap is not in the - * cache, it will be loaded from the disk. This method should either be - * called if the ImageView's size has already been set or inside a Runnable - * which is posted to the ImageView's message queue. - */ - public void loadThumbnailBitmap(ImageWorkerTaskResource source, - ImageView target, int length) { - final int defaultCoverResource = getDefaultCoverResource(target - .getContext()); - final String cacheKey; - if (source != null && (cacheKey = source.getImageLoaderCacheKey()) != null) { - final Object currentTag = target.getTag(R.id.imageloader_key); - if (currentTag == null || !cacheKey.equals(currentTag)) { - target.setTag(R.id.imageloader_key, cacheKey); - CachedBitmap cBitmap = getBitmapFromThumbnailCache(cacheKey); - if (cBitmap != null && cBitmap.getLength() >= length) { - target.setImageBitmap(cBitmap.getBitmap()); - } else { - target.setImageResource(defaultCoverResource); - BitmapDecodeWorkerTask worker = new BitmapDecodeWorkerTask( - handler, target, source, length, IMAGE_TYPE_THUMBNAIL); - executor.submit(worker); - } - } - } else { - target.setImageResource(defaultCoverResource); - target.setTag(R.id.imageloader_key, DEFAULT_IMAGE_RESOURCE_TAG); - } - } - - public void clearExecutorQueue() { - executor.shutdownNow(); - if (BuildConfig.DEBUG) - Log.d(TAG, "Executor was shut down."); - executor = createExecutor(); - - } - - public void wipeImageCache() { - coverCache.evictAll(); - thumbnailCache.evictAll(); - } - - public boolean isInThumbnailCache(String fileUrl) { - return thumbnailCache.get(fileUrl) != null; - } - - private CachedBitmap getBitmapFromThumbnailCache(String key) { - return thumbnailCache.get(key); - } - - public void addBitmapToThumbnailCache(String key, CachedBitmap bitmap) { - thumbnailCache.put(key, bitmap); - } - - public boolean isInCoverCache(String fileUrl) { - return coverCache.get(fileUrl) != null; - } - - private CachedBitmap getBitmapFromCoverCache(String key) { - return coverCache.get(key); - } - - public void addBitmapToCoverCache(String key, CachedBitmap bitmap) { - coverCache.put(key, bitmap); - } - - private int getDefaultCoverResource(Context context) { - return android.R.color.transparent; - } - - /** - * Used by the BitmapDecodeWorker task to retrieve the source of the bitmap. - */ - public interface ImageWorkerTaskResource { - /** - * Opens a new InputStream that can be decoded as a bitmap by the - * BitmapFactory. - */ - public InputStream openImageInputStream(); - - /** - * Returns an InputStream that points to the beginning of the image - * resource. Implementations can either create a new InputStream or - * reset the existing one, depending on their implementation of - * openInputStream. If a new InputStream is returned, the one given as a - * parameter MUST be closed. - * - * @param input The input stream that was returned by openImageInputStream() - */ - public InputStream reopenImageInputStream(InputStream input); - - /** - * Returns a string that identifies the image resource. Example: file - * path of an image - */ - public String getImageLoaderCacheKey(); - } - -} diff --git a/src/de/danoeh/antennapod/asynctask/PicassoImageResource.java b/src/de/danoeh/antennapod/asynctask/PicassoImageResource.java new file mode 100644 index 000000000..84179cfcb --- /dev/null +++ b/src/de/danoeh/antennapod/asynctask/PicassoImageResource.java @@ -0,0 +1,25 @@ +package de.danoeh.antennapod.asynctask; + +import android.net.Uri; + +/** + * Classes that implement this interface provide access to an image resource that can + * be loaded by the Picasso library. + */ +public interface PicassoImageResource { + + /** + * This scheme should be used by PicassoImageResources to + * indicate that the image Uri points to a file that is not an image + * (e.g. a media file). This workaround is needed so that the Picasso library + * loads these Uri with a Downloader instead of trying to load it directly. + *

+ * For example implementations, see FeedMedia or ExternalMedia. + */ + public static final String SCHEME_MEDIA = "media"; + + /** + * Returns a Uri to the image or null if no image is available. + */ + public Uri getImageUri(); +} diff --git a/src/de/danoeh/antennapod/asynctask/PicassoProvider.java b/src/de/danoeh/antennapod/asynctask/PicassoProvider.java new file mode 100644 index 000000000..fe70a9343 --- /dev/null +++ b/src/de/danoeh/antennapod/asynctask/PicassoProvider.java @@ -0,0 +1,137 @@ +package de.danoeh.antennapod.asynctask; + +import android.content.Context; +import android.media.MediaMetadataRetriever; +import android.net.Uri; +import android.util.Log; +import android.webkit.MimeTypeMap; + +import com.squareup.picasso.Cache; +import com.squareup.picasso.Downloader; +import com.squareup.picasso.LruCache; +import com.squareup.picasso.OkHttpDownloader; +import com.squareup.picasso.Picasso; + +import org.apache.commons.io.FilenameUtils; +import org.apache.commons.lang3.StringUtils; +import org.apache.commons.lang3.Validate; + +import java.io.BufferedInputStream; +import java.io.ByteArrayInputStream; +import java.io.File; +import java.io.FileInputStream; +import java.io.IOException; +import java.util.concurrent.ExecutorService; +import java.util.concurrent.Executors; + +/** + * Provides access to Picasso instances. + */ +public class PicassoProvider { + private static final String TAG = "PicassoProvider"; + + private static final boolean DEBUG = false; + + private static ExecutorService executorService; + private static Cache memoryCache; + + private static Picasso defaultPicassoInstance; + private static Picasso mediaMetadataPicassoInstance; + + private static synchronized ExecutorService getExecutorService() { + if (executorService == null) { + executorService = Executors.newFixedThreadPool(3); + } + return executorService; + } + + private static synchronized Cache getMemoryCache(Context context) { + if (memoryCache == null) { + memoryCache = new LruCache(context); + } + return memoryCache; + } + + /** + * Returns a Picasso instance that uses an OkHttpDownloader. This instance can only load images + * from image files. + *

+ * 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); + } + } +} diff --git a/src/de/danoeh/antennapod/feed/Feed.java b/src/de/danoeh/antennapod/feed/Feed.java index f9da65e03..b5415c69c 100644 --- a/src/de/danoeh/antennapod/feed/Feed.java +++ b/src/de/danoeh/antennapod/feed/Feed.java @@ -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; + } + } } diff --git a/src/de/danoeh/antennapod/feed/FeedImage.java b/src/de/danoeh/antennapod/feed/FeedImage.java index 9c9170294..c588f5e71 100644 --- a/src/de/danoeh/antennapod/feed/FeedImage.java +++ b/src/de/danoeh/antennapod/feed/FeedImage.java @@ -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; + } + } } diff --git a/src/de/danoeh/antennapod/feed/FeedItem.java b/src/de/danoeh/antennapod/feed/FeedItem.java index 956131ab2..78091ea33 100644 --- a/src/de/danoeh/antennapod/feed/FeedItem.java +++ b/src/de/danoeh/antennapod/feed/FeedItem.java @@ -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; } diff --git a/src/de/danoeh/antennapod/feed/FeedMedia.java b/src/de/danoeh/antennapod/feed/FeedMedia.java index dc941cb48..f555654d0 100644 --- a/src/de/danoeh/antennapod/feed/FeedMedia.java +++ b/src/de/danoeh/antennapod/feed/FeedMedia.java @@ -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); } } } diff --git a/src/de/danoeh/antennapod/fragment/CoverFragment.java b/src/de/danoeh/antennapod/fragment/CoverFragment.java index 0e1fe35e0..ffce518bf 100644 --- a/src/de/danoeh/antennapod/fragment/CoverFragment.java +++ b/src/de/danoeh/antennapod/fragment/CoverFragment.java @@ -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(); + } - } + } } diff --git a/src/de/danoeh/antennapod/fragment/ExternalPlayerFragment.java b/src/de/danoeh/antennapod/fragment/ExternalPlayerFragment.java index db47cd8a4..77587194b 100644 --- a/src/de/danoeh/antennapod/fragment/ExternalPlayerFragment.java +++ b/src/de/danoeh/antennapod/fragment/ExternalPlayerFragment.java @@ -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); + } } diff --git a/src/de/danoeh/antennapod/fragment/ItemlistFragment.java b/src/de/danoeh/antennapod/fragment/ItemlistFragment.java index d37f17b6d..5ef914f6c 100644 --- a/src/de/danoeh/antennapod/fragment/ItemlistFragment.java +++ b/src/de/danoeh/antennapod/fragment/ItemlistFragment.java @@ -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 { diff --git a/src/de/danoeh/antennapod/service/playback/PlaybackService.java b/src/de/danoeh/antennapod/service/playback/PlaybackService.java index 163a57ed2..59d7ddbb9 100644 --- a/src/de/danoeh/antennapod/service/playback/PlaybackService.java +++ b/src/de/danoeh/antennapod/service/playback/PlaybackService.java @@ -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 + ); } } diff --git a/src/de/danoeh/antennapod/util/BitmapDecoder.java b/src/de/danoeh/antennapod/util/BitmapDecoder.java deleted file mode 100644 index 5296d675a..000000000 --- a/src/de/danoeh/antennapod/util/BitmapDecoder.java +++ /dev/null @@ -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; - } -} diff --git a/src/de/danoeh/antennapod/util/playback/ExternalMedia.java b/src/de/danoeh/antennapod/util/playback/ExternalMedia.java index 390498cea..3f6e6ae0a 100644 --- a/src/de/danoeh/antennapod/util/playback/ExternalMedia.java +++ b/src/de/danoeh/antennapod/util/playback/ExternalMedia.java @@ -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; + } + } } diff --git a/src/de/danoeh/antennapod/util/playback/Playable.java b/src/de/danoeh/antennapod/util/playback/Playable.java index 9ed45abfc..004ae56bb 100644 --- a/src/de/danoeh/antennapod/util/playback/Playable.java +++ b/src/de/danoeh/antennapod/util/playback/Playable.java @@ -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; - } - } - } }