summaryrefslogtreecommitdiff
path: root/src/de/danoeh
diff options
context:
space:
mode:
Diffstat (limited to 'src/de/danoeh')
-rw-r--r--src/de/danoeh/antennapod/activity/PlaybackHistoryActivity.java1
-rw-r--r--src/de/danoeh/antennapod/feed/FeedItem.java576
-rw-r--r--src/de/danoeh/antennapod/feed/FeedMedia.java721
-rw-r--r--src/de/danoeh/antennapod/fragment/ItemDescriptionFragment.java848
-rw-r--r--src/de/danoeh/antennapod/storage/DBReader.java15
-rw-r--r--src/de/danoeh/antennapod/storage/DBWriter.java14
-rw-r--r--src/de/danoeh/antennapod/storage/PodDBAdapter.java11
-rw-r--r--src/de/danoeh/antennapod/util/playback/ExternalMedia.java10
-rw-r--r--src/de/danoeh/antennapod/util/playback/Playable.java16
9 files changed, 1139 insertions, 1073 deletions
diff --git a/src/de/danoeh/antennapod/activity/PlaybackHistoryActivity.java b/src/de/danoeh/antennapod/activity/PlaybackHistoryActivity.java
index 9730c805c..4d4a3d260 100644
--- a/src/de/danoeh/antennapod/activity/PlaybackHistoryActivity.java
+++ b/src/de/danoeh/antennapod/activity/PlaybackHistoryActivity.java
@@ -11,7 +11,6 @@ import com.actionbarsherlock.view.MenuItem;
import de.danoeh.antennapod.AppConfig;
import de.danoeh.antennapod.R;
-import de.danoeh.antennapod.feed.FeedManager;
import de.danoeh.antennapod.fragment.PlaybackHistoryFragment;
import de.danoeh.antennapod.preferences.UserPreferences;
import de.danoeh.antennapod.storage.DBWriter;
diff --git a/src/de/danoeh/antennapod/feed/FeedItem.java b/src/de/danoeh/antennapod/feed/FeedItem.java
index c7b130763..67ffcb1c6 100644
--- a/src/de/danoeh/antennapod/feed/FeedItem.java
+++ b/src/de/danoeh/antennapod/feed/FeedItem.java
@@ -4,291 +4,311 @@ import java.io.InputStream;
import java.lang.ref.SoftReference;
import java.util.Date;
import java.util.List;
+import java.util.concurrent.Callable;
+import de.danoeh.antennapod.PodcastApp;
import de.danoeh.antennapod.asynctask.ImageLoader;
+import de.danoeh.antennapod.storage.DBReader;
+import de.danoeh.antennapod.util.ShownotesProvider;
/**
* Data Object for a XML message
- *
+ *
* @author daniel
- *
*/
public class FeedItem extends FeedComponent implements
- ImageLoader.ImageWorkerTaskResource {
-
- /** The id/guid that can be found in the rss/atom feed. Might not be set. */
- private String itemIdentifier;
- private String title;
- /**
- * The description of a feeditem. This field should only be set by the
- * parser.
- */
- private String description;
- /**
- * The content of the content-encoded tag of a feeditem. This field should
- * only be set by the parser.
- */
- private String contentEncoded;
-
- private SoftReference<String> cachedDescription;
- private SoftReference<String> cachedContentEncoded;
-
- private String link;
- private Date pubDate;
- private FeedMedia media;
-
- private Feed feed;
- private long feedId;
-
- private boolean read;
- private String paymentLink;
- private List<Chapter> chapters;
-
- public FeedItem() {
- this.read = true;
- }
-
- public void updateFromOther(FeedItem other) {
- super.updateFromOther(other);
- if (other.title != null) {
- title = other.title;
- }
- if (other.getDescription() != null) {
- description = other.getDescription();
- }
- if (other.getContentEncoded() != null) {
- contentEncoded = other.contentEncoded;
- }
- if (other.link != null) {
- link = other.link;
- }
- if (other.pubDate != null && other.pubDate != pubDate) {
- pubDate = other.pubDate;
- }
- if (other.media != null) {
- if (media == null) {
- media = other.media;
- } else if (media.compareWithOther(other)) {
- media.updateFromOther(other);
- }
- }
- if (other.paymentLink != null) {
- paymentLink = other.paymentLink;
- }
- if (other.chapters != null) {
- if (chapters == null) {
- chapters = other.chapters;
- }
- }
- }
-
- /**
- * Moves the 'description' and 'contentEncoded' field of feeditem to their
- * SoftReference fields.
- */
- protected void cacheDescriptions() {
- if (description != null) {
- cachedDescription = new SoftReference<String>(description);
- }
- if (contentEncoded != null) {
- cachedContentEncoded = new SoftReference<String>(contentEncoded);
- }
- description = null;
- contentEncoded = null;
- }
-
- /**
- * Returns the value that uniquely identifies this FeedItem. If the
- * itemIdentifier attribute is not null, it will be returned. Else it will
- * try to return the title. If the title is not given, it will use the link
- * of the entry.
- * */
- public String getIdentifyingValue() {
- if (itemIdentifier != null) {
- return itemIdentifier;
- } else if (title != null) {
- return title;
- } else {
- return link;
- }
- }
-
- public String getTitle() {
- return title;
- }
-
- public void setTitle(String title) {
- this.title = title;
- }
-
- public String getDescription() {
- if (description == null && cachedDescription != null) {
- return cachedDescription.get();
- }
- return description;
- }
-
- public void setDescription(String description) {
- this.description = description;
- }
-
- public String getLink() {
- return link;
- }
-
- public void setLink(String link) {
- this.link = link;
- }
-
- public Date getPubDate() {
- return pubDate;
- }
-
- public void setPubDate(Date pubDate) {
- this.pubDate = pubDate;
- }
-
- public FeedMedia getMedia() {
- return media;
- }
-
- public void setMedia(FeedMedia media) {
- this.media = media;
- }
-
- public Feed getFeed() {
- return feed;
- }
-
- public void setFeed(Feed feed) {
- this.feed = feed;
- }
-
- public boolean isRead() {
- return read || isInProgress();
- }
-
- public void setRead(boolean read) {
- this.read = read;
- }
-
- private boolean isInProgress() {
- return (media != null && media.isInProgress());
- }
-
- public String getContentEncoded() {
- if (contentEncoded == null && cachedContentEncoded != null) {
- return cachedContentEncoded.get();
-
- }
- return contentEncoded;
- }
-
- public void setContentEncoded(String contentEncoded) {
- this.contentEncoded = contentEncoded;
- }
-
- public String getPaymentLink() {
- return paymentLink;
- }
-
- public void setPaymentLink(String paymentLink) {
- this.paymentLink = paymentLink;
- }
-
- public List<Chapter> getChapters() {
- return chapters;
- }
-
- public void setChapters(List<Chapter> chapters) {
- this.chapters = chapters;
- }
-
- public String getItemIdentifier() {
- return itemIdentifier;
- }
-
- public void setItemIdentifier(String itemIdentifier) {
- this.itemIdentifier = itemIdentifier;
- }
-
- public boolean hasMedia() {
- return media != null;
- }
-
- private boolean isPlaying() {
- if (media != null) {
- return media.isPlaying();
- }
- return false;
- }
-
- public void setCachedDescription(String d) {
- cachedDescription = new SoftReference<String>(d);
- }
-
- public void setCachedContentEncoded(String c) {
- cachedContentEncoded = new SoftReference<String>(c);
- }
-
- public enum State {
- NEW, IN_PROGRESS, READ, PLAYING
- }
-
- public State getState() {
- if (hasMedia()) {
- if (isPlaying()) {
- return State.PLAYING;
- }
- if (isInProgress()) {
- return State.IN_PROGRESS;
- }
- }
- return (isRead() ? State.READ : State.NEW);
- }
-
- @Override
- public InputStream openImageInputStream() {
- InputStream out = null;
- if (hasMedia()) {
- out = media.openImageInputStream();
- }
- if (out == null && feed.getImage() != null) {
- out = feed.getImage().openImageInputStream();
- }
- return out;
- }
-
- @Override
- public InputStream reopenImageInputStream(InputStream input) {
- InputStream out = null;
- if (hasMedia()) {
- out = media.reopenImageInputStream(input);
- }
- if (out == null && feed.getImage() != null) {
- out = feed.getImage().reopenImageInputStream(input);
- }
- return out;
- }
-
- @Override
- public String getImageLoaderCacheKey() {
- String out = null;
- if (hasMedia()) {
- out = media.getImageLoaderCacheKey();
- }
- if (out == null && feed.getImage() != null) {
- out = feed.getImage().getImageLoaderCacheKey();
- }
- return out;
- }
-
- public long getFeedId() {
- return feedId;
- }
-
- public void setFeedId(long feedId) {
- this.feedId = feedId;
- }
+ ImageLoader.ImageWorkerTaskResource, ShownotesProvider {
+
+ /**
+ * The id/guid that can be found in the rss/atom feed. Might not be set.
+ */
+ private String itemIdentifier;
+ private String title;
+ /**
+ * The description of a feeditem. This field should only be set by the
+ * parser.
+ */
+ private String description;
+ /**
+ * The content of the content-encoded tag of a feeditem. This field should
+ * only be set by the parser.
+ */
+ private String contentEncoded;
+
+ private SoftReference<String> cachedDescription;
+ private SoftReference<String> cachedContentEncoded;
+
+ private String link;
+ private Date pubDate;
+ private FeedMedia media;
+
+ private Feed feed;
+ private long feedId;
+
+ private boolean read;
+ private String paymentLink;
+ private List<Chapter> chapters;
+
+ public FeedItem() {
+ this.read = true;
+ }
+
+ public void updateFromOther(FeedItem other) {
+ super.updateFromOther(other);
+ if (other.title != null) {
+ title = other.title;
+ }
+ if (other.getDescription() != null) {
+ description = other.getDescription();
+ }
+ if (other.getContentEncoded() != null) {
+ contentEncoded = other.contentEncoded;
+ }
+ if (other.link != null) {
+ link = other.link;
+ }
+ if (other.pubDate != null && other.pubDate != pubDate) {
+ pubDate = other.pubDate;
+ }
+ if (other.media != null) {
+ if (media == null) {
+ media = other.media;
+ } else if (media.compareWithOther(other)) {
+ media.updateFromOther(other);
+ }
+ }
+ if (other.paymentLink != null) {
+ paymentLink = other.paymentLink;
+ }
+ if (other.chapters != null) {
+ if (chapters == null) {
+ chapters = other.chapters;
+ }
+ }
+ }
+
+ /**
+ * Moves the 'description' and 'contentEncoded' field of feeditem to their
+ * SoftReference fields.
+ */
+ protected void cacheDescriptions() {
+ if (description != null) {
+ cachedDescription = new SoftReference<String>(description);
+ }
+ if (contentEncoded != null) {
+ cachedContentEncoded = new SoftReference<String>(contentEncoded);
+ }
+ description = null;
+ contentEncoded = null;
+ }
+
+ /**
+ * Returns the value that uniquely identifies this FeedItem. If the
+ * itemIdentifier attribute is not null, it will be returned. Else it will
+ * try to return the title. If the title is not given, it will use the link
+ * of the entry.
+ */
+ public String getIdentifyingValue() {
+ if (itemIdentifier != null) {
+ return itemIdentifier;
+ } else if (title != null) {
+ return title;
+ } else {
+ return link;
+ }
+ }
+
+ public String getTitle() {
+ return title;
+ }
+
+ public void setTitle(String title) {
+ this.title = title;
+ }
+
+ public String getDescription() {
+ if (description == null && cachedDescription != null) {
+ return cachedDescription.get();
+ }
+ return description;
+ }
+
+ public void setDescription(String description) {
+ this.description = description;
+ }
+
+ public String getLink() {
+ return link;
+ }
+
+ public void setLink(String link) {
+ this.link = link;
+ }
+
+ public Date getPubDate() {
+ return pubDate;
+ }
+
+ public void setPubDate(Date pubDate) {
+ this.pubDate = pubDate;
+ }
+
+ public FeedMedia getMedia() {
+ return media;
+ }
+
+ public void setMedia(FeedMedia media) {
+ this.media = media;
+ }
+
+ public Feed getFeed() {
+ return feed;
+ }
+
+ public void setFeed(Feed feed) {
+ this.feed = feed;
+ }
+
+ public boolean isRead() {
+ return read || isInProgress();
+ }
+
+ public void setRead(boolean read) {
+ this.read = read;
+ }
+
+ private boolean isInProgress() {
+ return (media != null && media.isInProgress());
+ }
+
+ public String getContentEncoded() {
+ if (contentEncoded == null && cachedContentEncoded != null) {
+ return cachedContentEncoded.get();
+
+ }
+ return contentEncoded;
+ }
+
+ public void setContentEncoded(String contentEncoded) {
+ this.contentEncoded = contentEncoded;
+ }
+
+ public String getPaymentLink() {
+ return paymentLink;
+ }
+
+ public void setPaymentLink(String paymentLink) {
+ this.paymentLink = paymentLink;
+ }
+
+ public List<Chapter> getChapters() {
+ return chapters;
+ }
+
+ public void setChapters(List<Chapter> chapters) {
+ this.chapters = chapters;
+ }
+
+ public String getItemIdentifier() {
+ return itemIdentifier;
+ }
+
+ public void setItemIdentifier(String itemIdentifier) {
+ this.itemIdentifier = itemIdentifier;
+ }
+
+ public boolean hasMedia() {
+ return media != null;
+ }
+
+ private boolean isPlaying() {
+ if (media != null) {
+ return media.isPlaying();
+ }
+ return false;
+ }
+
+ public void setCachedDescription(String d) {
+ cachedDescription = new SoftReference<String>(d);
+ }
+
+ public void setCachedContentEncoded(String c) {
+ cachedContentEncoded = new SoftReference<String>(c);
+ }
+
+ @Override
+ public Callable<String> loadShownotes() {
+ return new Callable<String>() {
+ @Override
+ public String call() throws Exception {
+
+ if (contentEncoded == null || description == null) {
+ DBReader.loadExtraInformationOfFeedItem(PodcastApp.getInstance(), FeedItem.this);
+
+ }
+ return (contentEncoded != null) ? contentEncoded : description;
+ }
+ };
+ }
+
+ public enum State {
+ NEW, IN_PROGRESS, READ, PLAYING
+ }
+
+ public State getState() {
+ if (hasMedia()) {
+ if (isPlaying()) {
+ return State.PLAYING;
+ }
+ if (isInProgress()) {
+ return State.IN_PROGRESS;
+ }
+ }
+ return (isRead() ? State.READ : State.NEW);
+ }
+
+ @Override
+ public InputStream openImageInputStream() {
+ InputStream out = null;
+ if (hasMedia()) {
+ out = media.openImageInputStream();
+ }
+ if (out == null && feed.getImage() != null) {
+ out = feed.getImage().openImageInputStream();
+ }
+ return out;
+ }
+
+ @Override
+ public InputStream reopenImageInputStream(InputStream input) {
+ InputStream out = null;
+ if (hasMedia()) {
+ out = media.reopenImageInputStream(input);
+ }
+ if (out == null && feed.getImage() != null) {
+ out = feed.getImage().reopenImageInputStream(input);
+ }
+ return out;
+ }
+
+ @Override
+ public String getImageLoaderCacheKey() {
+ String out = null;
+ if (hasMedia()) {
+ out = media.getImageLoaderCacheKey();
+ }
+ if (out == null && feed.getImage() != null) {
+ out = feed.getImage().getImageLoaderCacheKey();
+ }
+ return out;
+ }
+
+ public long getFeedId() {
+ return feedId;
+ }
+
+ public void setFeedId(long feedId) {
+ this.feedId = feedId;
+ }
}
diff --git a/src/de/danoeh/antennapod/feed/FeedMedia.java b/src/de/danoeh/antennapod/feed/FeedMedia.java
index 1368cf854..3b7a5ec4e 100644
--- a/src/de/danoeh/antennapod/feed/FeedMedia.java
+++ b/src/de/danoeh/antennapod/feed/FeedMedia.java
@@ -4,6 +4,7 @@ import java.io.FileInputStream;
import java.io.InputStream;
import java.util.Date;
import java.util.List;
+import java.util.concurrent.Callable;
import android.content.SharedPreferences;
import android.content.SharedPreferences.Editor;
@@ -11,358 +12,380 @@ import android.os.Parcel;
import android.os.Parcelable;
import de.danoeh.antennapod.PodcastApp;
import de.danoeh.antennapod.preferences.PlaybackPreferences;
+import de.danoeh.antennapod.storage.DBReader;
+import de.danoeh.antennapod.storage.DBWriter;
import de.danoeh.antennapod.util.ChapterUtils;
import de.danoeh.antennapod.util.playback.Playable;
public class FeedMedia extends FeedFile implements Playable {
- public static final int FEEDFILETYPE_FEEDMEDIA = 2;
- public static final int PLAYABLE_TYPE_FEEDMEDIA = 1;
-
- public static final String PREF_MEDIA_ID = "FeedMedia.PrefMediaId";
- public static final String PREF_FEED_ID = "FeedMedia.PrefFeedId";
-
- private int duration;
- private int position; // Current position in file
- private long size; // File size in Byte
- private String mime_type;
- private FeedItem item;
- private Date playbackCompletionDate;
-
- public FeedMedia(FeedItem i, String download_url, long size,
- String mime_type) {
- super(null, download_url, false);
- this.item = i;
- this.size = size;
- this.mime_type = mime_type;
- }
-
- public FeedMedia(long id, FeedItem item, int duration, int position,
- long size, String mime_type, String file_url, String download_url,
- boolean downloaded, Date playbackCompletionDate) {
- super(file_url, download_url, downloaded);
- this.id = id;
- this.item = item;
- this.duration = duration;
- this.position = position;
- this.size = size;
- this.mime_type = mime_type;
- this.playbackCompletionDate = playbackCompletionDate;
- }
-
- public FeedMedia(long id, FeedItem item) {
- super();
- this.id = id;
- this.item = item;
- }
-
- @Override
- public String getHumanReadableIdentifier() {
- if (item != null && item.getTitle() != null) {
- return item.getTitle();
- } else {
- return download_url;
- }
- }
-
- /** Uses mimetype to determine the type of media. */
- public MediaType getMediaType() {
- if (mime_type == null || mime_type.isEmpty()) {
- return MediaType.UNKNOWN;
- } else {
- if (mime_type.startsWith("audio")) {
- return MediaType.AUDIO;
- } else if (mime_type.startsWith("video")) {
- return MediaType.VIDEO;
- } else if (mime_type.equals("application/ogg")) {
- return MediaType.AUDIO;
- }
- }
- return MediaType.UNKNOWN;
- }
-
- public void updateFromOther(FeedMedia other) {
- super.updateFromOther(other);
- if (other.size > 0) {
- size = other.size;
- }
- if (other.mime_type != null) {
- mime_type = other.mime_type;
- }
- }
-
- public boolean compareWithOther(FeedMedia other) {
- if (super.compareWithOther(other)) {
- return true;
- }
- if (other.mime_type != null) {
- if (mime_type == null || !mime_type.equals(other.mime_type)) {
- return true;
- }
- }
- if (other.size > 0 && other.size != size) {
- return true;
- }
- return false;
- }
-
- /**
- * Reads playback preferences to determine whether this FeedMedia object is
- * currently being played.
- */
- public boolean isPlaying() {
- return PlaybackPreferences.getCurrentlyPlayingMedia() == FeedMedia.PLAYABLE_TYPE_FEEDMEDIA
- && PlaybackPreferences.getCurrentlyPlayingFeedMediaId() == id;
- }
-
- @Override
- public int getTypeAsInt() {
- return FEEDFILETYPE_FEEDMEDIA;
- }
-
- public int getDuration() {
- return duration;
- }
-
- public void setDuration(int duration) {
- this.duration = duration;
- }
-
- public int getPosition() {
- return position;
- }
-
- public void setPosition(int position) {
- this.position = position;
- }
-
- public long getSize() {
- return size;
- }
-
- public void setSize(long size) {
- this.size = size;
- }
-
- public String getMime_type() {
- return mime_type;
- }
-
- public void setMime_type(String mime_type) {
- this.mime_type = mime_type;
- }
-
- public FeedItem getItem() {
- return item;
- }
-
- public void setItem(FeedItem item) {
- this.item = item;
- }
-
- public Date getPlaybackCompletionDate() {
- return playbackCompletionDate;
- }
-
- public void setPlaybackCompletionDate(Date playbackCompletionDate) {
- this.playbackCompletionDate = playbackCompletionDate;
- }
-
- public boolean isInProgress() {
- return (this.position > 0);
- }
-
- public FeedImage getImage() {
- if (item != null && item.getFeed() != null) {
- return item.getFeed().getImage();
- }
- return null;
- }
-
- @Override
- public int describeContents() {
- return 0;
- }
-
- @Override
- public void writeToParcel(Parcel dest, int flags) {
- dest.writeLong(item.getFeed().getId());
- dest.writeLong(item.getId());
- }
-
- @Override
- public void writeToPreferences(Editor prefEditor) {
- prefEditor.putLong(PREF_FEED_ID, item.getFeed().getId());
- prefEditor.putLong(PREF_MEDIA_ID, id);
- }
-
- @Override
- public void loadMetadata() throws PlayableException {
- }
-
- @Override
- public void loadChapterMarks() {
- if (getChapters() == null && !localFileAvailable()) {
- ChapterUtils.loadChaptersFromStreamUrl(this);
- if (getChapters() != null) {
- FeedManager.getInstance().setFeedItem(PodcastApp.getInstance(),
- item);
- }
- }
-
- }
-
- @Override
- public String getEpisodeTitle() {
- if (getItem().getTitle() != null) {
- return getItem().getTitle();
- } else {
- return getItem().getIdentifyingValue();
- }
- }
-
- @Override
- public List<Chapter> getChapters() {
- return getItem().getChapters();
- }
-
- @Override
- public String getWebsiteLink() {
- return getItem().getLink();
- }
-
- @Override
- public String getFeedTitle() {
- return getItem().getFeed().getTitle();
- }
-
- @Override
- public Object getIdentifier() {
- return id;
- }
-
- @Override
- public String getLocalMediaUrl() {
- return file_url;
- }
-
- @Override
- public String getStreamUrl() {
- return download_url;
- }
-
- @Override
- public boolean localFileAvailable() {
- return isDownloaded() && file_url != null;
- }
-
- @Override
- public boolean streamAvailable() {
- return download_url != null;
- }
-
- @Override
- public void saveCurrentPosition(SharedPreferences pref, int newPosition) {
- position = newPosition;
- FeedManager.getInstance().setFeedMedia(PodcastApp.getInstance(), this);
- }
-
- @Override
- public void onPlaybackStart() {
- }
-
- @Override
- public void onPlaybackCompleted() {
-
- }
-
- @Override
- public int getPlayableType() {
- return PLAYABLE_TYPE_FEEDMEDIA;
- }
-
- @Override
- public void setChapters(List<Chapter> chapters) {
- getItem().setChapters(chapters);
- }
-
- @Override
- public String getPaymentLink() {
- return getItem().getPaymentLink();
- }
-
- @Override
- public void loadShownotes(final ShownoteLoaderCallback callback) {
- String contentEncoded = item.getContentEncoded();
- if (item.getDescription() == null || contentEncoded == null) {
- FeedManager.getInstance().loadExtraInformationOfItem(
- PodcastApp.getInstance(), item,
- new FeedManager.TaskCallback<String[]>() {
- @Override
- public void onCompletion(String[] result) {
- if (result[1] != null) {
- callback.onShownotesLoaded(result[1]);
- } else {
- callback.onShownotesLoaded(result[0]);
-
- }
-
- }
- });
- } else {
- callback.onShownotesLoaded(contentEncoded);
- }
- }
-
- public static final Parcelable.Creator<FeedMedia> CREATOR = new Parcelable.Creator<FeedMedia>() {
- public FeedMedia createFromParcel(Parcel in) {
- long feedId = in.readLong();
- long itemId = in.readLong();
- FeedItem item = FeedManager.getInstance().getFeedItem(itemId,
- feedId);
- if (item != null) {
- return item.getMedia();
- } else {
- return null;
- }
- }
-
- public FeedMedia[] newArray(int size) {
- return new FeedMedia[size];
- }
- };
-
- @Override
- public InputStream openImageInputStream() {
- InputStream out = new Playable.DefaultPlayableImageLoader(this)
- .openImageInputStream();
- if (out == null) {
- if (item.getFeed().getImage() != null) {
- return item.getFeed().getImage().openImageInputStream();
- }
- }
- return out;
- }
-
- @Override
- public String getImageLoaderCacheKey() {
- String out = new Playable.DefaultPlayableImageLoader(this)
- .getImageLoaderCacheKey();
- if (out == null) {
- if (item.getFeed().getImage() != null) {
- return item.getFeed().getImage().getImageLoaderCacheKey();
- }
- }
- return out;
- }
-
- @Override
- public InputStream reopenImageInputStream(InputStream input) {
- if (input instanceof FileInputStream) {
- return item.getFeed().getImage().reopenImageInputStream(input);
- } else {
- return new Playable.DefaultPlayableImageLoader(this)
- .reopenImageInputStream(input);
- }
- }
+ public static final int FEEDFILETYPE_FEEDMEDIA = 2;
+ public static final int PLAYABLE_TYPE_FEEDMEDIA = 1;
+
+ public static final String PREF_MEDIA_ID = "FeedMedia.PrefMediaId";
+ public static final String PREF_FEED_ID = "FeedMedia.PrefFeedId";
+
+ private int duration;
+ private int position; // Current position in file
+ private long size; // File size in Byte
+ private String mime_type;
+ private volatile FeedItem item;
+ private Date playbackCompletionDate;
+
+ /* Used for loading item when restoring from parcel. */
+ private long itemID;
+
+ public FeedMedia(FeedItem i, String download_url, long size,
+ String mime_type) {
+ super(null, download_url, false);
+ this.item = i;
+ this.size = size;
+ this.mime_type = mime_type;
+ }
+
+ public FeedMedia(long id, FeedItem item, int duration, int position,
+ long size, String mime_type, String file_url, String download_url,
+ boolean downloaded, Date playbackCompletionDate) {
+ super(file_url, download_url, downloaded);
+ this.id = id;
+ this.item = item;
+ this.duration = duration;
+ this.position = position;
+ this.size = size;
+ this.mime_type = mime_type;
+ this.playbackCompletionDate = playbackCompletionDate;
+ }
+
+ public FeedMedia(long id, FeedItem item) {
+ super();
+ this.id = id;
+ this.item = item;
+ }
+
+ @Override
+ public String getHumanReadableIdentifier() {
+ if (item != null && item.getTitle() != null) {
+ return item.getTitle();
+ } else {
+ return download_url;
+ }
+ }
+
+ /**
+ * Uses mimetype to determine the type of media.
+ */
+ public MediaType getMediaType() {
+ if (mime_type == null || mime_type.isEmpty()) {
+ return MediaType.UNKNOWN;
+ } else {
+ if (mime_type.startsWith("audio")) {
+ return MediaType.AUDIO;
+ } else if (mime_type.startsWith("video")) {
+ return MediaType.VIDEO;
+ } else if (mime_type.equals("application/ogg")) {
+ return MediaType.AUDIO;
+ }
+ }
+ return MediaType.UNKNOWN;
+ }
+
+ public void updateFromOther(FeedMedia other) {
+ super.updateFromOther(other);
+ if (other.size > 0) {
+ size = other.size;
+ }
+ if (other.mime_type != null) {
+ mime_type = other.mime_type;
+ }
+ }
+
+ public boolean compareWithOther(FeedMedia other) {
+ if (super.compareWithOther(other)) {
+ return true;
+ }
+ if (other.mime_type != null) {
+ if (mime_type == null || !mime_type.equals(other.mime_type)) {
+ return true;
+ }
+ }
+ if (other.size > 0 && other.size != size) {
+ return true;
+ }
+ return false;
+ }
+
+ /**
+ * Reads playback preferences to determine whether this FeedMedia object is
+ * currently being played.
+ */
+ public boolean isPlaying() {
+ return PlaybackPreferences.getCurrentlyPlayingMedia() == FeedMedia.PLAYABLE_TYPE_FEEDMEDIA
+ && PlaybackPreferences.getCurrentlyPlayingFeedMediaId() == id;
+ }
+
+ @Override
+ public int getTypeAsInt() {
+ return FEEDFILETYPE_FEEDMEDIA;
+ }
+
+ public int getDuration() {
+ return duration;
+ }
+
+ public void setDuration(int duration) {
+ this.duration = duration;
+ }
+
+ public int getPosition() {
+ return position;
+ }
+
+ public void setPosition(int position) {
+ this.position = position;
+ }
+
+ public long getSize() {
+ return size;
+ }
+
+ public void setSize(long size) {
+ this.size = size;
+ }
+
+ public String getMime_type() {
+ return mime_type;
+ }
+
+ public void setMime_type(String mime_type) {
+ this.mime_type = mime_type;
+ }
+
+ public FeedItem getItem() {
+ return item;
+ }
+
+ public void setItem(FeedItem item) {
+ this.item = item;
+ }
+
+ public Date getPlaybackCompletionDate() {
+ return playbackCompletionDate;
+ }
+
+ public void setPlaybackCompletionDate(Date playbackCompletionDate) {
+ this.playbackCompletionDate = playbackCompletionDate;
+ }
+
+ public boolean isInProgress() {
+ return (this.position > 0);
+ }
+
+ public FeedImage getImage() {
+ if (item != null && item.getFeed() != null) {
+ return item.getFeed().getImage();
+ }
+ return null;
+ }
+
+ @Override
+ public int describeContents() {
+ return 0;
+ }
+
+ @Override
+ public void writeToParcel(Parcel dest, int flags) {
+ dest.writeLong(id);
+ dest.writeLong(item.getId());
+
+ dest.writeInt(duration);
+ dest.writeInt(position);
+ dest.writeLong(size);
+ dest.writeString(mime_type);
+ dest.writeString(file_url);
+ dest.writeString(download_url);
+ dest.writeByte((byte) ((downloaded) ? 1 : 0));
+ dest.writeLong((playbackCompletionDate != null) ? playbackCompletionDate.getTime() : 0);
+ }
+
+ @Override
+ public void writeToPreferences(Editor prefEditor) {
+ prefEditor.putLong(PREF_FEED_ID, item.getFeed().getId());
+ prefEditor.putLong(PREF_MEDIA_ID, id);
+ }
+
+ @Override
+ public void loadMetadata() throws PlayableException {
+ if (item == null && itemID != 0) {
+ item = DBReader.getFeedItem(PodcastApp.getInstance(), itemID);
+ }
+ }
+
+ @Override
+ public void loadChapterMarks() {
+ if (getChapters() == null && !localFileAvailable()) {
+ ChapterUtils.loadChaptersFromStreamUrl(this);
+ if (getChapters() != null) {
+ FeedManager.getInstance().setFeedItem(PodcastApp.getInstance(),
+ item);
+ }
+ }
+
+ }
+
+ @Override
+ public String getEpisodeTitle() {
+ if (item == null) {
+ return null;
+ }
+ if (getItem().getTitle() != null) {
+ return getItem().getTitle();
+ } else {
+ return getItem().getIdentifyingValue();
+ }
+ }
+
+ @Override
+ public List<Chapter> getChapters() {
+ if (item == null) {
+ return null;
+ }
+ return getItem().getChapters();
+ }
+
+ @Override
+ public String getWebsiteLink() {
+ if (item == null) {
+ return null;
+ }
+ return getItem().getLink();
+ }
+
+ @Override
+ public String getFeedTitle() {
+ if (item == null) {
+ return null;
+ }
+ return getItem().getFeed().getTitle();
+ }
+
+ @Override
+ public Object getIdentifier() {
+ return id;
+ }
+
+ @Override
+ public String getLocalMediaUrl() {
+ return file_url;
+ }
+
+ @Override
+ public String getStreamUrl() {
+ return download_url;
+ }
+
+ @Override
+ public boolean localFileAvailable() {
+ return isDownloaded() && file_url != null;
+ }
+
+ @Override
+ public boolean streamAvailable() {
+ return download_url != null;
+ }
+
+ @Override
+ public void saveCurrentPosition(SharedPreferences pref, int newPosition) {
+ position = newPosition;
+ DBWriter.setFeedMediaPosition(PodcastApp.getInstance(), this);
+ }
+
+ @Override
+ public void onPlaybackStart() {
+ }
+
+ @Override
+ public void onPlaybackCompleted() {
+
+ }
+
+ @Override
+ public int getPlayableType() {
+ return PLAYABLE_TYPE_FEEDMEDIA;
+ }
+
+ @Override
+ public void setChapters(List<Chapter> chapters) {
+ getItem().setChapters(chapters);
+ }
+
+ @Override
+ public String getPaymentLink() {
+ return getItem().getPaymentLink();
+ }
+
+ @Override
+ public Callable<String> loadShownotes() {
+ return new Callable<String>() {
+ @Override
+ public String call() throws Exception {
+ if (item == null) {
+ item = DBReader.getFeedItem(PodcastApp.getInstance(), itemID);
+ }
+ if (item.getContentEncoded() == null || item.getDescription() == null) {
+ DBReader.loadExtraInformationOfFeedItem(PodcastApp.getInstance(), item);
+
+ }
+ return (item.getContentEncoded() != null) ? item.getContentEncoded() : item.getDescription();
+ }
+ };
+ }
+
+ public static final Parcelable.Creator<FeedMedia> CREATOR = new Parcelable.Creator<FeedMedia>() {
+ public FeedMedia createFromParcel(Parcel in) {
+ final long id = in.readLong();
+ final long itemID = in.readLong();
+ FeedMedia result = new FeedMedia(id, null, in.readInt(), in.readInt(), in.readLong(), in.readString(), in.readString(),
+ in.readString(), in.readByte() != 0, new Date(in.readLong()));
+ result.itemID = itemID;
+ return result;
+ }
+
+ public FeedMedia[] newArray(int size) {
+ return new FeedMedia[size];
+ }
+ };
+
+ @Override
+ public InputStream openImageInputStream() {
+ InputStream out = new Playable.DefaultPlayableImageLoader(this)
+ .openImageInputStream();
+ if (out == null) {
+ if (item.getFeed().getImage() != null) {
+ return item.getFeed().getImage().openImageInputStream();
+ }
+ }
+ return out;
+ }
+
+ @Override
+ public String getImageLoaderCacheKey() {
+ String out = new Playable.DefaultPlayableImageLoader(this)
+ .getImageLoaderCacheKey();
+ if (out == null) {
+ if (item.getFeed().getImage() != null) {
+ return item.getFeed().getImage().getImageLoaderCacheKey();
+ }
+ }
+ return out;
+ }
+
+ @Override
+ public InputStream reopenImageInputStream(InputStream input) {
+ if (input instanceof FileInputStream) {
+ return item.getFeed().getImage().reopenImageInputStream(input);
+ } else {
+ return new Playable.DefaultPlayableImageLoader(this)
+ .reopenImageInputStream(input);
+ }
+ }
}
diff --git a/src/de/danoeh/antennapod/fragment/ItemDescriptionFragment.java b/src/de/danoeh/antennapod/fragment/ItemDescriptionFragment.java
index 10f43718f..3d74653f3 100644
--- a/src/de/danoeh/antennapod/fragment/ItemDescriptionFragment.java
+++ b/src/de/danoeh/antennapod/fragment/ItemDescriptionFragment.java
@@ -1,5 +1,9 @@
package de.danoeh.antennapod.fragment;
+import de.danoeh.antennapod.feed.FeedItem;
+import de.danoeh.antennapod.feed.FeedMedia;
+import de.danoeh.antennapod.storage.DBReader;
+import de.danoeh.antennapod.util.ShownotesProvider;
import org.apache.commons.lang3.StringEscapeUtils;
import android.annotation.SuppressLint;
@@ -30,439 +34,427 @@ import android.widget.Toast;
import com.actionbarsherlock.app.SherlockFragment;
import de.danoeh.antennapod.AppConfig;
-import de.danoeh.antennapod.PodcastApp;
import de.danoeh.antennapod.R;
-import de.danoeh.antennapod.feed.FeedItem;
-import de.danoeh.antennapod.feed.FeedManager;
import de.danoeh.antennapod.preferences.UserPreferences;
import de.danoeh.antennapod.util.ShareUtils;
import de.danoeh.antennapod.util.playback.Playable;
-/** Displays the description of a Playable object in a Webview. */
+import java.util.concurrent.Callable;
+import java.util.concurrent.ExecutionException;
+import java.util.concurrent.FutureTask;
+
+/**
+ * Displays the description of a Playable object in a Webview.
+ */
public class ItemDescriptionFragment extends SherlockFragment {
- private static final String TAG = "ItemDescriptionFragment";
-
- private static final String PREF = "ItemDescriptionFragmentPrefs";
- private static final String PREF_SCROLL_Y = "prefScrollY";
- private static final String PREF_PLAYABLE_ID = "prefPlayableId";
-
- private static final String ARG_PLAYABLE = "arg.playable";
-
- private static final String ARG_FEED_ID = "arg.feedId";
- private static final String ARG_FEED_ITEM_ID = "arg.feeditemId";
- private static final String ARG_SAVE_STATE = "arg.saveState";
-
- private WebView webvDescription;
- private Playable media;
-
- private FeedItem item;
-
- private AsyncTask<Void, Void, Void> webViewLoader;
-
- private String shownotes;
-
- /** URL that was selected via long-press. */
- private String selectedURL;
-
- /**
- * True if Fragment should save its state (e.g. scrolling position) in a
- * shared preference.
- */
- private boolean saveState;
-
- public static ItemDescriptionFragment newInstance(Playable media,
- boolean saveState) {
- ItemDescriptionFragment f = new ItemDescriptionFragment();
- Bundle args = new Bundle();
- args.putParcelable(ARG_PLAYABLE, media);
- args.putBoolean(ARG_SAVE_STATE, saveState);
- f.setArguments(args);
- return f;
- }
-
- public static ItemDescriptionFragment newInstance(FeedItem item,
- boolean saveState) {
- ItemDescriptionFragment f = new ItemDescriptionFragment();
- Bundle args = new Bundle();
- args.putLong(ARG_FEED_ID, item.getFeed().getId());
- args.putLong(ARG_FEED_ITEM_ID, item.getId());
- args.putBoolean(ARG_SAVE_STATE, saveState);
- f.setArguments(args);
- return f;
- }
-
- @SuppressLint("NewApi")
- @Override
- public View onCreateView(LayoutInflater inflater, ViewGroup container,
- Bundle savedInstanceState) {
- if (AppConfig.DEBUG)
- Log.d(TAG, "Creating view");
- webvDescription = new WebView(getActivity());
- if (UserPreferences.getTheme() == R.style.Theme_AntennaPod_Dark) {
- if (Build.VERSION.SDK_INT >= 11
- && Build.VERSION.SDK_INT <= Build.VERSION_CODES.ICE_CREAM_SANDWICH_MR1) {
- webvDescription.setLayerType(View.LAYER_TYPE_SOFTWARE, null);
- }
- webvDescription.setBackgroundColor(getResources().getColor(
- R.color.black));
- }
- webvDescription.getSettings().setUseWideViewPort(false);
- webvDescription.getSettings().setLayoutAlgorithm(
- LayoutAlgorithm.NARROW_COLUMNS);
- webvDescription.getSettings().setLoadWithOverviewMode(true);
- webvDescription.setOnLongClickListener(webViewLongClickListener);
- webvDescription.setWebViewClient(new WebViewClient() {
-
- @Override
- public boolean shouldOverrideUrlLoading(WebView view, String url) {
- Intent intent = new Intent(Intent.ACTION_VIEW, Uri.parse(url));
- startActivity(intent);
- return true;
- }
-
- @Override
- public void onPageFinished(WebView view, String url) {
- super.onPageFinished(view, url);
- if (AppConfig.DEBUG)
- Log.d(TAG, "Page finished");
- // Restoring the scroll position might not always work
- view.postDelayed(new Runnable() {
-
- @Override
- public void run() {
- restoreFromPreference();
- }
-
- }, 50);
- }
-
- });
- registerForContextMenu(webvDescription);
- return webvDescription;
- }
-
- @Override
- public void onDestroyView() {
- super.onDestroyView();
- }
-
- @Override
- public void onAttach(Activity activity) {
- super.onAttach(activity);
- if (AppConfig.DEBUG)
- Log.d(TAG, "Fragment attached");
- }
-
- @Override
- public void onDetach() {
- super.onDetach();
- if (AppConfig.DEBUG)
- Log.d(TAG, "Fragment detached");
- if (webViewLoader != null) {
- webViewLoader.cancel(true);
- }
- }
-
- @Override
- public void onDestroy() {
- super.onDestroy();
- if (AppConfig.DEBUG)
- Log.d(TAG, "Fragment destroyed");
- if (webViewLoader != null) {
- webViewLoader.cancel(true);
- }
- }
-
- @SuppressLint("NewApi")
- @Override
- public void onCreate(Bundle savedInstanceState) {
- super.onCreate(savedInstanceState);
- if (AppConfig.DEBUG)
- Log.d(TAG, "Creating fragment");
- Bundle args = getArguments();
- saveState = args.getBoolean(ARG_SAVE_STATE, false);
- if (args.containsKey(ARG_PLAYABLE)) {
- media = args.getParcelable(ARG_PLAYABLE);
- } else if (args.containsKey(ARG_FEED_ID)
- && args.containsKey(ARG_FEED_ITEM_ID)) {
- long feedId = args.getLong(ARG_FEED_ID);
- long itemId = args.getLong(ARG_FEED_ITEM_ID);
- FeedItem f = FeedManager.getInstance().getFeedItem(itemId, feedId);
- if (f != null) {
- item = f;
- }
- }
- }
-
- @Override
- public void onViewCreated(View view, Bundle savedInstanceState) {
- super.onViewCreated(view, savedInstanceState);
- if (media != null) {
- media.loadShownotes(new Playable.ShownoteLoaderCallback() {
-
- @Override
- public void onShownotesLoaded(String shownotes) {
- ItemDescriptionFragment.this.shownotes = shownotes;
- if (ItemDescriptionFragment.this.shownotes != null) {
- startLoader();
- }
- }
- });
- } else if (item != null) {
- if (item.getDescription() == null
- || item.getContentEncoded() == null) {
- FeedManager.getInstance().loadExtraInformationOfItem(
- PodcastApp.getInstance(), item,
- new FeedManager.TaskCallback<String[]>() {
- @Override
- public void onCompletion(String[] result) {
- if (result[1] != null) {
- shownotes = result[1];
- } else {
- shownotes = result[0];
- }
- if (shownotes != null) {
- startLoader();
- }
-
- }
- });
- } else {
- shownotes = item.getContentEncoded();
- startLoader();
- }
- } else {
- Log.e(TAG, "Error in onViewCreated: Item and media were null");
- }
- }
-
- @Override
- public void onResume() {
- super.onResume();
- }
-
- @SuppressLint("NewApi")
- private void startLoader() {
- webViewLoader = createLoader();
- if (android.os.Build.VERSION.SDK_INT > android.os.Build.VERSION_CODES.GINGERBREAD_MR1) {
- webViewLoader.executeOnExecutor(AsyncTask.THREAD_POOL_EXECUTOR);
- } else {
- webViewLoader.execute();
- }
- }
-
- /**
- * Return the CSS style of the Webview.
- *
- * @param textColor
- * the default color to use for the text in the webview. This
- * value is inserted directly into the CSS String.
- * */
- private String applyWebviewStyle(String textColor, String data) {
- final String WEBVIEW_STYLE = "<html><head><style type=\"text/css\"> * { color: %s; font-family: Helvetica; line-height: 1.5em; font-size: 11pt; } a { font-style: normal; text-decoration: none; font-weight: normal; color: #00A8DF; } img { display: block; margin: 10 auto; max-width: %s; height: auto; } body { margin: %dpx %dpx %dpx %dpx; }</style></head><body>%s</body></html>";
- final int pageMargin = (int) TypedValue.applyDimension(
- TypedValue.COMPLEX_UNIT_DIP, 8, getResources()
- .getDisplayMetrics());
- return String.format(WEBVIEW_STYLE, textColor, "100%", pageMargin,
- pageMargin, pageMargin, pageMargin, data);
- }
-
- private View.OnLongClickListener webViewLongClickListener = new View.OnLongClickListener() {
-
- @Override
- public boolean onLongClick(View v) {
- WebView.HitTestResult r = webvDescription.getHitTestResult();
- if (r != null
- && r.getType() == WebView.HitTestResult.SRC_ANCHOR_TYPE) {
- if (AppConfig.DEBUG)
- Log.d(TAG,
- "Link of webview was long-pressed. Extra: "
- + r.getExtra());
- selectedURL = r.getExtra();
- webvDescription.showContextMenu();
- return true;
- }
- selectedURL = null;
- return false;
- }
- };
-
- @SuppressWarnings("deprecation")
- @SuppressLint("NewApi")
- @Override
- public boolean onContextItemSelected(MenuItem item) {
- boolean handled = selectedURL != null;
- if (selectedURL != null) {
- switch (item.getItemId()) {
- case R.id.open_in_browser_item:
- Uri uri = Uri.parse(selectedURL);
- getActivity()
- .startActivity(new Intent(Intent.ACTION_VIEW, uri));
- break;
- case R.id.share_url_item:
- ShareUtils.shareLink(getActivity(), selectedURL);
- break;
- case R.id.copy_url_item:
- if (android.os.Build.VERSION.SDK_INT >= 11) {
- ClipData clipData = ClipData.newPlainText(selectedURL,
- selectedURL);
- android.content.ClipboardManager cm = (android.content.ClipboardManager) getActivity()
- .getSystemService(Context.CLIPBOARD_SERVICE);
- cm.setPrimaryClip(clipData);
- } else {
- android.text.ClipboardManager cm = (android.text.ClipboardManager) getActivity()
- .getSystemService(Context.CLIPBOARD_SERVICE);
- cm.setText(selectedURL);
- }
- Toast t = Toast.makeText(getActivity(),
- R.string.copied_url_msg, Toast.LENGTH_SHORT);
- t.show();
- break;
- default:
- handled = false;
- break;
-
- }
- selectedURL = null;
- }
- return handled;
-
- }
-
- @Override
- public void onCreateContextMenu(ContextMenu menu, View v,
- ContextMenuInfo menuInfo) {
- if (selectedURL != null) {
- super.onCreateContextMenu(menu, v, menuInfo);
- menu.add(Menu.NONE, R.id.open_in_browser_item, Menu.NONE,
- R.string.open_in_browser_label);
- menu.add(Menu.NONE, R.id.copy_url_item, Menu.NONE,
- R.string.copy_url_label);
- menu.add(Menu.NONE, R.id.share_url_item, Menu.NONE,
- R.string.share_url_label);
- menu.setHeaderTitle(selectedURL);
- }
- }
-
- private AsyncTask<Void, Void, Void> createLoader() {
- return new AsyncTask<Void, Void, Void>() {
- @Override
- protected void onCancelled() {
- super.onCancelled();
- if (getSherlockActivity() != null) {
- getSherlockActivity()
- .setSupportProgressBarIndeterminateVisibility(false);
- }
- webViewLoader = null;
- }
-
- String data;
-
- @Override
- protected void onPostExecute(Void result) {
- super.onPostExecute(result);
- // /webvDescription.loadData(url, "text/html", "utf-8");
- webvDescription.loadDataWithBaseURL(null, data, "text/html",
- "utf-8", "about:blank");
- if (getSherlockActivity() != null) {
- getSherlockActivity()
- .setSupportProgressBarIndeterminateVisibility(false);
- }
- if (AppConfig.DEBUG)
- Log.d(TAG, "Webview loaded");
- webViewLoader = null;
- }
-
- @Override
- protected void onPreExecute() {
- super.onPreExecute();
- if (getSherlockActivity() != null) {
- getSherlockActivity()
- .setSupportProgressBarIndeterminateVisibility(true);
- }
- }
-
- @Override
- protected Void doInBackground(Void... params) {
- if (AppConfig.DEBUG)
- Log.d(TAG, "Loading Webview");
- data = "";
- data = StringEscapeUtils.unescapeHtml4(shownotes);
- Activity activity = getActivity();
- if (activity != null) {
- TypedArray res = getActivity()
- .getTheme()
- .obtainStyledAttributes(
- new int[] { android.R.attr.textColorPrimary });
- int colorResource = res.getColor(0, 0);
- String colorString = String.format("#%06X",
- 0xFFFFFF & colorResource);
- Log.i(TAG, "text color: " + colorString);
- res.recycle();
- data = applyWebviewStyle(colorString, data);
- } else {
- cancel(true);
- }
- return null;
- }
-
- };
- }
-
- @Override
- public void onPause() {
- super.onPause();
- savePreference();
- }
-
- private void savePreference() {
- if (saveState) {
- if (AppConfig.DEBUG)
- Log.d(TAG, "Saving preferences");
- SharedPreferences prefs = getActivity().getSharedPreferences(PREF,
- Activity.MODE_PRIVATE);
- SharedPreferences.Editor editor = prefs.edit();
- if (media != null && webvDescription != null) {
- if (AppConfig.DEBUG)
- Log.d(TAG,
- "Saving scroll position: "
- + webvDescription.getScrollY());
- editor.putInt(PREF_SCROLL_Y, webvDescription.getScrollY());
- editor.putString(PREF_PLAYABLE_ID, media.getIdentifier()
- .toString());
- } else {
- if (AppConfig.DEBUG)
- Log.d(TAG,
- "savePreferences was called while media or webview was null");
- editor.putInt(PREF_SCROLL_Y, -1);
- editor.putString(PREF_PLAYABLE_ID, "");
- }
- editor.commit();
- }
- }
-
- private boolean restoreFromPreference() {
- if (saveState) {
- if (AppConfig.DEBUG)
- Log.d(TAG, "Restoring from preferences");
- Activity activity = getActivity();
- if (activity != null) {
- SharedPreferences prefs = activity.getSharedPreferences(
- PREF, Activity.MODE_PRIVATE);
- String id = prefs.getString(PREF_PLAYABLE_ID, "");
- int scrollY = prefs.getInt(PREF_SCROLL_Y, -1);
- if (scrollY != -1 && media != null
- && id.equals(media.getIdentifier().toString())
- && webvDescription != null) {
- if (AppConfig.DEBUG)
- Log.d(TAG, "Restored scroll Position: " + scrollY);
- webvDescription.scrollTo(webvDescription.getScrollX(),
- scrollY);
- return true;
- }
- }
- }
- return false;
- }
+ private static final String TAG = "ItemDescriptionFragment";
+
+ private static final String PREF = "ItemDescriptionFragmentPrefs";
+ private static final String PREF_SCROLL_Y = "prefScrollY";
+ private static final String PREF_PLAYABLE_ID = "prefPlayableId";
+
+ private static final String ARG_PLAYABLE = "arg.playable";
+ private static final String ARG_FEEDITEM_ID = "arg.feeditem";
+
+ private static final String ARG_SAVE_STATE = "arg.saveState";
+
+ private WebView webvDescription;
+
+ private ShownotesProvider shownotesProvider;
+ private Playable media;
+
+
+ private AsyncTask<Void, Void, Void> webViewLoader;
+
+ /**
+ * URL that was selected via long-press.
+ */
+ private String selectedURL;
+
+ /**
+ * True if Fragment should save its state (e.g. scrolling position) in a
+ * shared preference.
+ */
+ private boolean saveState;
+
+ public static ItemDescriptionFragment newInstance(Playable media,
+ boolean saveState) {
+ ItemDescriptionFragment f = new ItemDescriptionFragment();
+ Bundle args = new Bundle();
+ args.putParcelable(ARG_PLAYABLE, media);
+ args.putBoolean(ARG_SAVE_STATE, saveState);
+ f.setArguments(args);
+ return f;
+ }
+
+ public static ItemDescriptionFragment newInstance(FeedItem item, boolean saveState) {
+ ItemDescriptionFragment f = new ItemDescriptionFragment();
+ Bundle args = new Bundle();
+ args.putLong(ARG_FEEDITEM_ID, item.getId());
+ args.putBoolean(ARG_SAVE_STATE, saveState);
+ f.setArguments(args);
+ return f;
+ }
+
+ @SuppressLint("NewApi")
+ @Override
+ public View onCreateView(LayoutInflater inflater, ViewGroup container,
+ Bundle savedInstanceState) {
+ if (AppConfig.DEBUG)
+ Log.d(TAG, "Creating view");
+ webvDescription = new WebView(getActivity());
+ if (UserPreferences.getTheme() == R.style.Theme_AntennaPod_Dark) {
+ if (Build.VERSION.SDK_INT >= 11
+ && Build.VERSION.SDK_INT <= Build.VERSION_CODES.ICE_CREAM_SANDWICH_MR1) {
+ webvDescription.setLayerType(View.LAYER_TYPE_SOFTWARE, null);
+ }
+ webvDescription.setBackgroundColor(getResources().getColor(
+ R.color.black));
+ }
+ webvDescription.getSettings().setUseWideViewPort(false);
+ webvDescription.getSettings().setLayoutAlgorithm(
+ LayoutAlgorithm.NARROW_COLUMNS);
+ webvDescription.getSettings().setLoadWithOverviewMode(true);
+ webvDescription.setOnLongClickListener(webViewLongClickListener);
+ webvDescription.setWebViewClient(new WebViewClient() {
+
+ @Override
+ public boolean shouldOverrideUrlLoading(WebView view, String url) {
+ Intent intent = new Intent(Intent.ACTION_VIEW, Uri.parse(url));
+ startActivity(intent);
+ return true;
+ }
+
+ @Override
+ public void onPageFinished(WebView view, String url) {
+ super.onPageFinished(view, url);
+ if (AppConfig.DEBUG)
+ Log.d(TAG, "Page finished");
+ // Restoring the scroll position might not always work
+ view.postDelayed(new Runnable() {
+
+ @Override
+ public void run() {
+ restoreFromPreference();
+ }
+
+ }, 50);
+ }
+
+ });
+ registerForContextMenu(webvDescription);
+ return webvDescription;
+ }
+
+ @Override
+ public void onDestroyView() {
+ super.onDestroyView();
+ }
+
+ @Override
+ public void onAttach(Activity activity) {
+ super.onAttach(activity);
+ if (AppConfig.DEBUG)
+ Log.d(TAG, "Fragment attached");
+ }
+
+ @Override
+ public void onDetach() {
+ super.onDetach();
+ if (AppConfig.DEBUG)
+ Log.d(TAG, "Fragment detached");
+ if (webViewLoader != null) {
+ webViewLoader.cancel(true);
+ }
+ }
+
+ @Override
+ public void onDestroy() {
+ super.onDestroy();
+ if (AppConfig.DEBUG)
+ Log.d(TAG, "Fragment destroyed");
+ if (webViewLoader != null) {
+ webViewLoader.cancel(true);
+ }
+ }
+
+ @SuppressLint("NewApi")
+ @Override
+ public void onCreate(Bundle savedInstanceState) {
+ super.onCreate(savedInstanceState);
+ if (AppConfig.DEBUG)
+ Log.d(TAG, "Creating fragment");
+ Bundle args = getArguments();
+ saveState = args.getBoolean(ARG_SAVE_STATE, false);
+
+ }
+
+ @Override
+ public void onViewCreated(View view, Bundle savedInstanceState) {
+ super.onViewCreated(view, savedInstanceState);
+ Bundle args = getArguments();
+ if (args.containsKey(ARG_PLAYABLE)) {
+ media = args.getParcelable(ARG_PLAYABLE);
+ shownotesProvider = media;
+ startLoader();
+ } else if (args.containsKey(ARG_FEEDITEM_ID)) {
+ AsyncTask<Void, Void, FeedItem> itemLoadTask = new AsyncTask<Void, Void, FeedItem>() {
+
+ @Override
+ protected FeedItem doInBackground(Void... voids) {
+ return DBReader.getFeedItem(getActivity(), getArguments().getLong(ARG_FEEDITEM_ID));
+ }
+
+ @Override
+ protected void onPostExecute(FeedItem feedItem) {
+ super.onPostExecute(feedItem);
+ shownotesProvider = feedItem;
+ startLoader();
+ }
+ };
+ if (android.os.Build.VERSION.SDK_INT > android.os.Build.VERSION_CODES.GINGERBREAD_MR1) {
+ itemLoadTask.executeOnExecutor(AsyncTask.THREAD_POOL_EXECUTOR);
+ } else {
+ itemLoadTask.execute();
+ }
+ }
+
+
+ }
+
+ @Override
+ public void onResume() {
+ super.onResume();
+ }
+
+ @SuppressLint("NewApi")
+ private void startLoader() {
+ webViewLoader = createLoader();
+ if (android.os.Build.VERSION.SDK_INT > android.os.Build.VERSION_CODES.GINGERBREAD_MR1) {
+ webViewLoader.executeOnExecutor(AsyncTask.THREAD_POOL_EXECUTOR);
+ } else {
+ webViewLoader.execute();
+ }
+ }
+
+ /**
+ * Return the CSS style of the Webview.
+ *
+ * @param textColor the default color to use for the text in the webview. This
+ * value is inserted directly into the CSS String.
+ */
+ private String applyWebviewStyle(String textColor, String data) {
+ final String WEBVIEW_STYLE = "<html><head><style type=\"text/css\"> * { color: %s; font-family: Helvetica; line-height: 1.5em; font-size: 11pt; } a { font-style: normal; text-decoration: none; font-weight: normal; color: #00A8DF; } img { display: block; margin: 10 auto; max-width: %s; height: auto; } body { margin: %dpx %dpx %dpx %dpx; }</style></head><body>%s</body></html>";
+ final int pageMargin = (int) TypedValue.applyDimension(
+ TypedValue.COMPLEX_UNIT_DIP, 8, getResources()
+ .getDisplayMetrics());
+ return String.format(WEBVIEW_STYLE, textColor, "100%", pageMargin,
+ pageMargin, pageMargin, pageMargin, data);
+ }
+
+ private View.OnLongClickListener webViewLongClickListener = new View.OnLongClickListener() {
+
+ @Override
+ public boolean onLongClick(View v) {
+ WebView.HitTestResult r = webvDescription.getHitTestResult();
+ if (r != null
+ && r.getType() == WebView.HitTestResult.SRC_ANCHOR_TYPE) {
+ if (AppConfig.DEBUG)
+ Log.d(TAG,
+ "Link of webview was long-pressed. Extra: "
+ + r.getExtra());
+ selectedURL = r.getExtra();
+ webvDescription.showContextMenu();
+ return true;
+ }
+ selectedURL = null;
+ return false;
+ }
+ };
+
+ @SuppressWarnings("deprecation")
+ @SuppressLint("NewApi")
+ @Override
+ public boolean onContextItemSelected(MenuItem item) {
+ boolean handled = selectedURL != null;
+ if (selectedURL != null) {
+ switch (item.getItemId()) {
+ case R.id.open_in_browser_item:
+ Uri uri = Uri.parse(selectedURL);
+ getActivity()
+ .startActivity(new Intent(Intent.ACTION_VIEW, uri));
+ break;
+ case R.id.share_url_item:
+ ShareUtils.shareLink(getActivity(), selectedURL);
+ break;
+ case R.id.copy_url_item:
+ if (android.os.Build.VERSION.SDK_INT >= 11) {
+ ClipData clipData = ClipData.newPlainText(selectedURL,
+ selectedURL);
+ android.content.ClipboardManager cm = (android.content.ClipboardManager) getActivity()
+ .getSystemService(Context.CLIPBOARD_SERVICE);
+ cm.setPrimaryClip(clipData);
+ } else {
+ android.text.ClipboardManager cm = (android.text.ClipboardManager) getActivity()
+ .getSystemService(Context.CLIPBOARD_SERVICE);
+ cm.setText(selectedURL);
+ }
+ Toast t = Toast.makeText(getActivity(),
+ R.string.copied_url_msg, Toast.LENGTH_SHORT);
+ t.show();
+ break;
+ default:
+ handled = false;
+ break;
+
+ }
+ selectedURL = null;
+ }
+ return handled;
+
+ }
+
+ @Override
+ public void onCreateContextMenu(ContextMenu menu, View v,
+ ContextMenuInfo menuInfo) {
+ if (selectedURL != null) {
+ super.onCreateContextMenu(menu, v, menuInfo);
+ menu.add(Menu.NONE, R.id.open_in_browser_item, Menu.NONE,
+ R.string.open_in_browser_label);
+ menu.add(Menu.NONE, R.id.copy_url_item, Menu.NONE,
+ R.string.copy_url_label);
+ menu.add(Menu.NONE, R.id.share_url_item, Menu.NONE,
+ R.string.share_url_label);
+ menu.setHeaderTitle(selectedURL);
+ }
+ }
+
+ private AsyncTask<Void, Void, Void> createLoader() {
+ return new AsyncTask<Void, Void, Void>() {
+ @Override
+ protected void onCancelled() {
+ super.onCancelled();
+ if (getSherlockActivity() != null) {
+ getSherlockActivity()
+ .setSupportProgressBarIndeterminateVisibility(false);
+ }
+ webViewLoader = null;
+ }
+
+ String data;
+
+ @Override
+ protected void onPostExecute(Void result) {
+ super.onPostExecute(result);
+ // /webvDescription.loadData(url, "text/html", "utf-8");
+ webvDescription.loadDataWithBaseURL(null, data, "text/html",
+ "utf-8", "about:blank");
+ if (getSherlockActivity() != null) {
+ getSherlockActivity()
+ .setSupportProgressBarIndeterminateVisibility(false);
+ }
+ if (AppConfig.DEBUG)
+ Log.d(TAG, "Webview loaded");
+ webViewLoader = null;
+ }
+
+ @Override
+ protected void onPreExecute() {
+ super.onPreExecute();
+ if (getSherlockActivity() != null) {
+ getSherlockActivity()
+ .setSupportProgressBarIndeterminateVisibility(true);
+ }
+ }
+
+ @Override
+ protected Void doInBackground(Void... params) {
+ if (AppConfig.DEBUG)
+ Log.d(TAG, "Loading Webview");
+ try {
+ Callable<String> shownotesLoadTask = shownotesProvider.loadShownotes();
+ final String shownotes = shownotesLoadTask.call();
+
+ data = "";
+ data = StringEscapeUtils.unescapeHtml4(shownotes);
+ Activity activity = getActivity();
+ if (activity != null) {
+ TypedArray res = getActivity()
+ .getTheme()
+ .obtainStyledAttributes(
+ new int[]{android.R.attr.textColorPrimary});
+ int colorResource = res.getColor(0, 0);
+ String colorString = String.format("#%06X",
+ 0xFFFFFF & colorResource);
+ Log.i(TAG, "text color: " + colorString);
+ res.recycle();
+ data = applyWebviewStyle(colorString, data);
+ } else {
+ cancel(true);
+ }
+ } catch (Exception e) {
+ e.printStackTrace();
+ }
+ return null;
+ }
+
+ };
+ }
+
+ @Override
+ public void onPause() {
+ super.onPause();
+ savePreference();
+ }
+
+ private void savePreference() {
+ if (saveState) {
+ if (AppConfig.DEBUG)
+ Log.d(TAG, "Saving preferences");
+ SharedPreferences prefs = getActivity().getSharedPreferences(PREF,
+ Activity.MODE_PRIVATE);
+ SharedPreferences.Editor editor = prefs.edit();
+ if (media != null && webvDescription != null) {
+ if (AppConfig.DEBUG)
+ Log.d(TAG,
+ "Saving scroll position: "
+ + webvDescription.getScrollY());
+ editor.putInt(PREF_SCROLL_Y, webvDescription.getScrollY());
+ editor.putString(PREF_PLAYABLE_ID, media.getIdentifier()
+ .toString());
+ } else {
+ if (AppConfig.DEBUG)
+ Log.d(TAG,
+ "savePreferences was called while media or webview was null");
+ editor.putInt(PREF_SCROLL_Y, -1);
+ editor.putString(PREF_PLAYABLE_ID, "");
+ }
+ editor.commit();
+ }
+ }
+
+ private boolean restoreFromPreference() {
+ if (saveState) {
+ if (AppConfig.DEBUG)
+ Log.d(TAG, "Restoring from preferences");
+ Activity activity = getActivity();
+ if (activity != null) {
+ SharedPreferences prefs = activity.getSharedPreferences(
+ PREF, Activity.MODE_PRIVATE);
+ String id = prefs.getString(PREF_PLAYABLE_ID, "");
+ int scrollY = prefs.getInt(PREF_SCROLL_Y, -1);
+ if (scrollY != -1 && media != null
+ && id.equals(media.getIdentifier().toString())
+ && webvDescription != null) {
+ if (AppConfig.DEBUG)
+ Log.d(TAG, "Restored scroll Position: " + scrollY);
+ webvDescription.scrollTo(webvDescription.getScrollX(),
+ scrollY);
+ return true;
+ }
+ }
+ }
+ return false;
+ }
}
diff --git a/src/de/danoeh/antennapod/storage/DBReader.java b/src/de/danoeh/antennapod/storage/DBReader.java
index c69607473..bf81ad7d4 100644
--- a/src/de/danoeh/antennapod/storage/DBReader.java
+++ b/src/de/danoeh/antennapod/storage/DBReader.java
@@ -476,6 +476,21 @@ public final class DBReader {
return item;
}
+
+ public static void loadExtraInformationOfFeedItem(final Context context, final FeedItem item) {
+ PodDBAdapter adapter = new PodDBAdapter(context);
+ adapter.open();
+ Cursor extraCursor = adapter.getExtraInformationOfItem(item);
+ if (extraCursor.moveToFirst()) {
+ String description = extraCursor
+ .getString(PodDBAdapter.IDX_FI_EXTRA_DESCRIPTION);
+ String contentEncoded = extraCursor
+ .getString(PodDBAdapter.IDX_FI_EXTRA_CONTENT_ENCODED);
+ item.setDescription(description);
+ item.setContentEncoded(contentEncoded);
+ }
+ adapter.close();
+ }
public static int getNumberOfDownloadedEpisodes(final Context context) {
PodDBAdapter adapter = new PodDBAdapter(context);
diff --git a/src/de/danoeh/antennapod/storage/DBWriter.java b/src/de/danoeh/antennapod/storage/DBWriter.java
index a60694f35..3db0b8a33 100644
--- a/src/de/danoeh/antennapod/storage/DBWriter.java
+++ b/src/de/danoeh/antennapod/storage/DBWriter.java
@@ -529,10 +529,20 @@ public class DBWriter {
adapter.setMedia(media);
adapter.close();
}});
-
-
}
+ public static Future<?> setFeedMediaPosition(final Context context, final FeedMedia media) {
+ return dbExec.submit(new Runnable(){
+ @Override
+ public void run() {
+ PodDBAdapter adapter = new PodDBAdapter(context);
+ adapter.open();
+ adapter.setFeedMediaPosition(media);
+ adapter.close();
+ }
+ });
+ }
+
public static Future<?> setFeedItem(final Context context,
final FeedItem item) {
return dbExec.submit(new Runnable() {
diff --git a/src/de/danoeh/antennapod/storage/PodDBAdapter.java b/src/de/danoeh/antennapod/storage/PodDBAdapter.java
index 14ef07c34..4ef76fcd6 100644
--- a/src/de/danoeh/antennapod/storage/PodDBAdapter.java
+++ b/src/de/danoeh/antennapod/storage/PodDBAdapter.java
@@ -366,6 +366,17 @@ public class PodDBAdapter {
return media.getId();
}
+ public void setFeedMediaPosition(FeedMedia media) {
+ if (media.getId() != 0) {
+ ContentValues values = new ContentValues();
+ values.put(KEY_POSITION, media.getPosition());
+ db.update(TABLE_NAME_FEED_MEDIA, values, KEY_ID + "=?",
+ new String[] { String.valueOf(media.getId()) });
+ } else {
+ Log.e(TAG, "setFeedMediaPosition: ID of media was 0");
+ }
+ }
+
/**
* Insert all FeedItems of a feed and the feed object itself in a single
* transaction
diff --git a/src/de/danoeh/antennapod/util/playback/ExternalMedia.java b/src/de/danoeh/antennapod/util/playback/ExternalMedia.java
index c0a92904b..1ada0ec03 100644
--- a/src/de/danoeh/antennapod/util/playback/ExternalMedia.java
+++ b/src/de/danoeh/antennapod/util/playback/ExternalMedia.java
@@ -2,6 +2,7 @@ package de.danoeh.antennapod.util.playback;
import java.io.InputStream;
import java.util.List;
+import java.util.concurrent.Callable;
import android.content.SharedPreferences;
import android.content.SharedPreferences.Editor;
@@ -95,8 +96,13 @@ public class ExternalMedia implements Playable {
}
@Override
- public void loadShownotes(ShownoteLoaderCallback callback) {
- callback.onShownotesLoaded(null);
+ public Callable<String> loadShownotes() {
+ return new Callable<String>() {
+ @Override
+ public String call() throws Exception {
+ return "";
+ }
+ };
}
@Override
diff --git a/src/de/danoeh/antennapod/util/playback/Playable.java b/src/de/danoeh/antennapod/util/playback/Playable.java
index 39c6c6b83..98d5fbb36 100644
--- a/src/de/danoeh/antennapod/util/playback/Playable.java
+++ b/src/de/danoeh/antennapod/util/playback/Playable.java
@@ -3,9 +3,11 @@ package de.danoeh.antennapod.util.playback;
import java.io.ByteArrayInputStream;
import java.io.InputStream;
import java.util.List;
+import java.util.concurrent.FutureTask;
import android.content.Context;
import de.danoeh.antennapod.storage.DBReader;
+import de.danoeh.antennapod.util.ShownotesProvider;
import org.apache.commons.io.IOUtils;
import android.content.SharedPreferences;
@@ -14,7 +16,6 @@ import android.os.Parcelable;
import android.util.Log;
import de.danoeh.antennapod.asynctask.ImageLoader;
import de.danoeh.antennapod.feed.Chapter;
-import de.danoeh.antennapod.feed.Feed;
import de.danoeh.antennapod.feed.FeedMedia;
import de.danoeh.antennapod.feed.MediaType;
@@ -22,7 +23,7 @@ import de.danoeh.antennapod.feed.MediaType;
* Interface for objects that can be played by the PlaybackService.
*/
public interface Playable extends Parcelable,
- ImageLoader.ImageWorkerTaskResource {
+ ImageLoader.ImageWorkerTaskResource, ShownotesProvider {
/**
* Save information about the playable in a preference so that it can be
@@ -53,13 +54,6 @@ public interface Playable extends Parcelable,
public String getEpisodeTitle();
/**
- * Loads shownotes. If the shownotes have to be loaded from a file or from a
- * database, it should be done in a separate thread. After the shownotes
- * have been loaded, callback.onShownotesLoaded should be called.
- */
- public void loadShownotes(ShownoteLoaderCallback callback);
-
- /**
* Returns a list of chapter marks or null if this Playable has no chapters.
*/
public List<Chapter> getChapters();
@@ -216,10 +210,6 @@ public interface Playable extends Parcelable,
}
- public static interface ShownoteLoaderCallback {
- void onShownotesLoaded(String shownotes);
- }
-
/**
* Uses local file as image resource if it is available.
*/