From 107e5c266f7e61972ce6d1b361b4287dbd8bde80 Mon Sep 17 00:00:00 2001 From: Domingos Lopes Date: Mon, 6 Jun 2016 12:18:37 -0400 Subject: adapt FeedMedia class to different flavors --- .../de/danoeh/antennapod/core/feed/FeedMedia.java | 567 +++++++++++++++++++++ 1 file changed, 567 insertions(+) create mode 100644 core/src/main/java/de/danoeh/antennapod/core/feed/FeedMedia.java (limited to 'core/src/main') diff --git a/core/src/main/java/de/danoeh/antennapod/core/feed/FeedMedia.java b/core/src/main/java/de/danoeh/antennapod/core/feed/FeedMedia.java new file mode 100644 index 000000000..3aea015c0 --- /dev/null +++ b/core/src/main/java/de/danoeh/antennapod/core/feed/FeedMedia.java @@ -0,0 +1,567 @@ +package de.danoeh.antennapod.core.feed; + +import android.content.SharedPreferences; +import android.content.SharedPreferences.Editor; +import android.database.Cursor; +import android.media.MediaMetadataRetriever; +import android.os.Parcel; +import android.os.Parcelable; +import android.support.annotation.Nullable; + +import java.util.Date; +import java.util.List; +import java.util.concurrent.Callable; + +import de.danoeh.antennapod.core.preferences.PlaybackPreferences; +import de.danoeh.antennapod.core.preferences.UserPreferences; +import de.danoeh.antennapod.core.storage.DBReader; +import de.danoeh.antennapod.core.storage.DBWriter; +import de.danoeh.antennapod.core.storage.PodDBAdapter; +import de.danoeh.antennapod.core.util.ChapterUtils; +import de.danoeh.antennapod.core.util.playback.Playable; + +public class FeedMedia extends FeedFile implements Playable { + private static final String TAG = "FeedMedia"; + + 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"; + + /** + * Indicates we've checked on the size of the item via the network + * and got an invalid response. Using Integer.MIN_VALUE because + * 1) we'll still check on it in case it gets downloaded (it's <= 0) + * 2) By default all FeedMedia have a size of 0 if we don't know it, + * so this won't conflict with existing practice. + */ + private static final int CHECKED_ON_SIZE_BUT_UNKNOWN = Integer.MIN_VALUE; + + private int duration; + private int position; // Current position in file + private long lastPlayedTime; // Last time this media was played (in ms) + private int played_duration; // How many ms of this file have been played (for autoflattring) + private long size; // File size in Byte + private String mime_type; + @Nullable private volatile FeedItem item; + private Date playbackCompletionDate; + + // if null: unknown, will be checked + private Boolean hasEmbeddedPicture; + + /* 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, int played_duration, + long lastPlayedTime) { + super(file_url, download_url, downloaded); + this.id = id; + this.item = item; + this.duration = duration; + this.position = position; + this.played_duration = played_duration; + this.size = size; + this.mime_type = mime_type; + this.playbackCompletionDate = playbackCompletionDate == null + ? null : (Date) playbackCompletionDate.clone(); + this.lastPlayedTime = lastPlayedTime; + } + + 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, int played_duration, + Boolean hasEmbeddedPicture, long lastPlayedTime) { + this(id, item, duration, position, size, mime_type, file_url, download_url, downloaded, + playbackCompletionDate, played_duration, lastPlayedTime); + this.hasEmbeddedPicture = hasEmbeddedPicture; + } + + public static FeedMedia fromCursor(Cursor cursor) { + int indexId = cursor.getColumnIndex(PodDBAdapter.KEY_ID); + int indexPlaybackCompletionDate = cursor.getColumnIndex(PodDBAdapter.KEY_PLAYBACK_COMPLETION_DATE); + int indexDuration = cursor.getColumnIndex(PodDBAdapter.KEY_DURATION); + int indexPosition = cursor.getColumnIndex(PodDBAdapter.KEY_POSITION); + int indexSize = cursor.getColumnIndex(PodDBAdapter.KEY_SIZE); + int indexMimeType = cursor.getColumnIndex(PodDBAdapter.KEY_MIME_TYPE); + int indexFileUrl = cursor.getColumnIndex(PodDBAdapter.KEY_FILE_URL); + int indexDownloadUrl = cursor.getColumnIndex(PodDBAdapter.KEY_DOWNLOAD_URL); + int indexDownloaded = cursor.getColumnIndex(PodDBAdapter.KEY_DOWNLOADED); + int indexPlayedDuration = cursor.getColumnIndex(PodDBAdapter.KEY_PLAYED_DURATION); + int indexLastPlayedTime = cursor.getColumnIndex(PodDBAdapter.KEY_LAST_PLAYED_TIME); + + long mediaId = cursor.getLong(indexId); + Date playbackCompletionDate = null; + long playbackCompletionTime = cursor.getLong(indexPlaybackCompletionDate); + if (playbackCompletionTime > 0) { + playbackCompletionDate = new Date(playbackCompletionTime); + } + + Boolean hasEmbeddedPicture; + switch(cursor.getInt(cursor.getColumnIndex(PodDBAdapter.KEY_HAS_EMBEDDED_PICTURE))) { + case 1: + hasEmbeddedPicture = Boolean.TRUE; + break; + case 0: + hasEmbeddedPicture = Boolean.FALSE; + break; + default: + hasEmbeddedPicture = null; + break; + } + + return new FeedMedia( + mediaId, + null, + cursor.getInt(indexDuration), + cursor.getInt(indexPosition), + cursor.getLong(indexSize), + cursor.getString(indexMimeType), + cursor.getString(indexFileUrl), + cursor.getString(indexDownloadUrl), + cursor.getInt(indexDownloaded) > 0, + playbackCompletionDate, + cursor.getInt(indexPlayedDuration), + hasEmbeddedPicture, + cursor.getLong(indexLastPlayedTime) + ); + } + + + @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() { + return MediaType.fromMimeType(mime_type); + } + + 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; + } + + /** + * Reads playback preferences to determine whether this FeedMedia object is + * currently being played and the current player status is playing. + */ + public boolean isCurrentlyPlaying() { + return isPlaying() && + ((PlaybackPreferences.getCurrentPlayerStatus() == PlaybackPreferences.PLAYER_STATUS_PLAYING)); + } + + /** + * Reads playback preferences to determine whether this FeedMedia object is + * currently being played and the current player status is paused. + */ + public boolean isCurrentlyPaused() { + return isPlaying() && + ((PlaybackPreferences.getCurrentPlayerStatus() == PlaybackPreferences.PLAYER_STATUS_PAUSED)); + } + + + public boolean hasAlmostEnded() { + int smartMarkAsPlayedSecs = UserPreferences.getSmartMarkAsPlayedSecs(); + return this.position >= this.duration - smartMarkAsPlayedSecs * 1000; + } + + @Override + public int getTypeAsInt() { + return FEEDFILETYPE_FEEDMEDIA; + } + + public int getDuration() { + return duration; + } + + public void setDuration(int duration) { + this.duration = duration; + } + + @Override + public void setLastPlayedTime(long lastPlayedTime) { + this.lastPlayedTime = lastPlayedTime; + } + + public int getPlayedDuration() { + return played_duration; + } + + public void setPlayedDuration(int played_duration) { + this.played_duration = played_duration; + } + + public int getPosition() { + return position; + } + + @Override + public long getLastPlayedTime() { + return lastPlayedTime; + } + + public void setPosition(int position) { + this.position = position; + if(position > 0 && item != null && item.isNew()) { + this.item.setPlayed(false); + } + } + + public long getSize() { + return size; + } + + public void setSize(long size) { + this.size = size; + } + + /** + * Indicates we asked the service what the size was, but didn't + * get a valid answer and we shoudln't check using the network again. + */ + public void setCheckedOnSizeButUnknown() { + this.size = CHECKED_ON_SIZE_BUT_UNKNOWN; + } + + public boolean checkedOnSizeButUnknown() { + return (CHECKED_ON_SIZE_BUT_UNKNOWN == this.size); + } + + public String getMime_type() { + return mime_type; + } + + public void setMime_type(String mime_type) { + this.mime_type = mime_type; + } + + @Nullable + public FeedItem getItem() { + return item; + } + + /** + * Sets the item object of this FeedMedia. If the given + * FeedItem object is not null, it's 'media'-attribute value + * will also be set to this media object. + */ + public void setItem(FeedItem item) { + this.item = item; + if (item != null && item.getMedia() != this) { + item.setMedia(this); + } + } + + public Date getPlaybackCompletionDate() { + return playbackCompletionDate == null + ? null : (Date) playbackCompletionDate.clone(); + } + + public void setPlaybackCompletionDate(Date playbackCompletionDate) { + this.playbackCompletionDate = playbackCompletionDate == null + ? null : (Date) playbackCompletionDate.clone(); + } + + public boolean isInProgress() { + return (this.position > 0); + } + + @Override + public int describeContents() { + return 0; + } + + public boolean hasEmbeddedPicture() { + if(hasEmbeddedPicture == null) { + checkEmbeddedPicture(); + } + return hasEmbeddedPicture; + } + + @Override + public void writeToParcel(Parcel dest, int flags) { + dest.writeLong(id); + dest.writeLong(item != null ? item.getId() : 0L); + + 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); + dest.writeInt(played_duration); + dest.writeLong(lastPlayedTime); + } + + @Override + public void writeToPreferences(Editor prefEditor) { + if(item != null && item.getFeed() != null) { + prefEditor.putLong(PREF_FEED_ID, item.getFeed().getId()); + } else { + prefEditor.putLong(PREF_FEED_ID, 0L); + } + prefEditor.putLong(PREF_MEDIA_ID, id); + } + + @Override + public void loadMetadata() throws PlayableException { + if (item == null && itemID != 0) { + item = DBReader.getFeedItem(itemID); + } + } + + @Override + public void loadChapterMarks() { + if (item == null && itemID != 0) { + item = DBReader.getFeedItem(itemID); + } + // check if chapters are stored in db and not loaded yet. + if (item != null && item.hasChapters() && item.getChapters() == null) { + DBReader.loadChaptersOfFeedItem(item); + } else if (item != null && item.getChapters() == null) { + if(localFileAvailable()) { + ChapterUtils.loadChaptersFromFileUrl(this); + } else { + ChapterUtils.loadChaptersFromStreamUrl(this); + } + if (getChapters() != null && item != null) { + DBWriter.setFeedItem(item); + } + } + } + + @Override + public String getEpisodeTitle() { + if (item == null) { + return null; + } + if (item.getTitle() != null) { + return item.getTitle(); + } else { + return item.getIdentifyingValue(); + } + } + + @Override + public List getChapters() { + if (item == null) { + return null; + } + return item.getChapters(); + } + + @Override + public String getWebsiteLink() { + if (item == null) { + return null; + } + return item.getLink(); + } + + @Override + public String getFeedTitle() { + if (item == null || item.getFeed() == null) { + return null; + } + return item.getFeed().getTitle(); + } + + @Override + public Object getIdentifier() { + return id; + } + + @Override + public String getLocalMediaUrl() { + return file_url; + } + + @Override + public String getStreamUrl() { + return download_url; + } + + @Override + public String getPaymentLink() { + if (item == null) { + return null; + } + return item.getPaymentLink(); + } + + @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, long timeStamp) { + if(item != null && item.isNew()) { + DBWriter.markItemPlayed(FeedItem.UNPLAYED, item.getId()); + } + setPosition(newPosition); + setLastPlayedTime(timeStamp); + DBWriter.setFeedMediaPlaybackInformation(this); + } + + @Override + public void onPlaybackStart() { + } + @Override + public void onPlaybackCompleted() { + + } + + @Override + public int getPlayableType() { + return PLAYABLE_TYPE_FEEDMEDIA; + } + + @Override + public void setChapters(List chapters) { + if(item != null) { + item.setChapters(chapters); + } + } + + @Override + public Callable loadShownotes() { + return () -> { + if (item == null) { + item = DBReader.getFeedItem( + itemID); + } + if (item.getContentEncoded() == null || item.getDescription() == null) { + DBReader.loadExtraInformationOfFeedItem( + item); + + } + return (item.getContentEncoded() != null) ? item.getContentEncoded() : item.getDescription(); + }; + } + + public static final Parcelable.Creator CREATOR = new Parcelable.Creator() { + 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()), in.readInt(), in.readLong()); + result.itemID = itemID; + return result; + } + + public FeedMedia[] newArray(int size) { + return new FeedMedia[size]; + } + }; + + @Override + public String getImageLocation() { + if (hasEmbeddedPicture()) { + return getLocalMediaUrl(); + } else if(item != null) { + return item.getImageLocation(); + } else { + return null; + } + } + + public void setHasEmbeddedPicture(Boolean hasEmbeddedPicture) { + this.hasEmbeddedPicture = hasEmbeddedPicture; + } + + @Override + public void setDownloaded(boolean downloaded) { + super.setDownloaded(downloaded); + if(item != null && downloaded) { + item.setPlayed(false); + } + } + + @Override + public void setFile_url(String file_url) { + super.setFile_url(file_url); + } + + public void checkEmbeddedPicture() { + if (!localFileAvailable()) { + hasEmbeddedPicture = Boolean.FALSE; + return; + } + MediaMetadataRetriever mmr = new MediaMetadataRetriever(); + try { + mmr.setDataSource(getLocalMediaUrl()); + byte[] image = mmr.getEmbeddedPicture(); + if(image != null) { + hasEmbeddedPicture = Boolean.TRUE; + } else { + hasEmbeddedPicture = Boolean.FALSE; + } + } catch (Exception e) { + e.printStackTrace(); + hasEmbeddedPicture = Boolean.FALSE; + } + } + + @Override + public boolean equals(Object o) { + if (FeedMediaHelper.instanceOfRemoteMedia(o)) { + return o.equals(this); + } + return super.equals(o); + } +} -- cgit v1.2.3