create RemoteMedia class, add more fields to remote mediametadata

This commit is contained in:
Domingos Lopes 2016-03-27 02:25:15 -04:00
parent c4b6f366ca
commit 94a16bb9ba
3 changed files with 333 additions and 6 deletions

View File

@ -0,0 +1,257 @@
package de.danoeh.antennapod.core.cast;
import android.content.SharedPreferences;
import android.net.Uri;
import android.os.Parcel;
import android.os.Parcelable;
import android.text.TextUtils;
import java.util.Date;
import java.util.List;
import java.util.concurrent.Callable;
import de.danoeh.antennapod.core.feed.Chapter;
import de.danoeh.antennapod.core.feed.MediaType;
import de.danoeh.antennapod.core.util.ChapterUtils;
import de.danoeh.antennapod.core.util.playback.Playable;
/**
* Playable implementation for media on a Cast Device for which a local version of
* {@link de.danoeh.antennapod.core.feed.FeedMedia} could not be found.
*/
public class RemoteMedia implements Playable {
public static final int PLAYABLE_TYPE_REMOTE_MEDIA = 3;
private String downloadUrl;
private String itemIdentifier;
private String feedUrl;
private String feedTitle;
private String episodeTitle;
private String episodeLink;
private String feedAuthor;
private String imageUrl;
private String mime_type;
private Date pubDate;
private String notes;
private List<Chapter> chapters;
private int duration;
private int position;
private long lastPlayedTime;
public RemoteMedia(String downloadUrl, String itemId, String feedUrl, String feedTitle,
String episodeTitle, String episodeLink, String feedAuthor,
String imageUrl, String mime_type, Date pubDate) {
this.downloadUrl = downloadUrl;
this.itemIdentifier = itemId;
this.feedUrl = feedUrl;
this.feedTitle = feedTitle;
this.episodeTitle = episodeTitle;
this.episodeLink = episodeLink;
this.feedAuthor = feedAuthor;
this.imageUrl = imageUrl;
this.mime_type = mime_type;
this.pubDate = pubDate;
}
public void setNotes(String notes) {
this.notes = notes;
}
@Override
public void writeToPreferences(SharedPreferences.Editor prefEditor) {
//it seems pointless to do it, since the session should be kept by the remote device.
}
@Override
public void loadMetadata() throws PlayableException {
//Already loaded
}
@Override
public void loadChapterMarks() {
ChapterUtils.loadChaptersFromStreamUrl(this);
}
@Override
public String getEpisodeTitle() {
return episodeTitle;
}
@Override
public List<Chapter> getChapters() {
return chapters;
}
@Override
public String getWebsiteLink() {
if (episodeLink != null) {
return episodeLink;
} else {
return feedUrl;
}
}
@Override
public String getPaymentLink() {
return null;
}
@Override
public String getFeedTitle() {
return feedTitle;
}
@Override
public Object getIdentifier() {
return itemIdentifier + "@" + feedUrl;
}
@Override
public int getDuration() {
return duration;
}
@Override
public int getPosition() {
return position;
}
@Override
public long getLastPlayedTime() {
return lastPlayedTime;
}
@Override
public MediaType getMediaType() {
if (TextUtils.isEmpty(mime_type)) {
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;
}
@Override
public String getLocalMediaUrl() {
return null;
}
@Override
public String getStreamUrl() {
return downloadUrl;
}
@Override
public boolean localFileAvailable() {
return false;
}
@Override
public boolean streamAvailable() {
return true;
}
@Override
public void saveCurrentPosition(SharedPreferences pref, int newPosition, long timestamp) {
//we're not saving playback information for this kind of items on preferences
setPosition(newPosition);
setLastPlayedTime(timestamp);
}
@Override
public void setPosition(int newPosition) {
position = newPosition;
}
@Override
public void setDuration(int newDuration) {
duration = newDuration;
}
@Override
public void setLastPlayedTime(long lastPlayedTimestamp) {
lastPlayedTime = lastPlayedTimestamp;
}
@Override
public void onPlaybackStart() {
// no-op
}
@Override
public void onPlaybackCompleted() {
// no-op
}
@Override
public int getPlayableType() {
return PLAYABLE_TYPE_REMOTE_MEDIA;
}
@Override
public void setChapters(List<Chapter> chapters) {
this.chapters = chapters;
}
@Override
public Uri getImageUri() {
if (imageUrl != null) {
return Uri.parse(imageUrl);
}
return null;
}
@Override
public int describeContents() {
return 0;
}
@Override
public Callable<String> loadShownotes() {
return () -> (notes != null) ? notes : "";
}
@Override
public void writeToParcel(Parcel dest, int flags) {
dest.writeString(downloadUrl);
dest.writeString(itemIdentifier);
dest.writeString(feedUrl);
dest.writeString(feedTitle);
dest.writeString(episodeTitle);
dest.writeString(episodeLink);
dest.writeString(feedAuthor);
dest.writeString(imageUrl);
dest.writeString(mime_type);
dest.writeLong(pubDate.getTime());
dest.writeString(notes);
dest.writeInt(duration);
dest.writeInt(position);
dest.writeLong(lastPlayedTime);
}
public static final Parcelable.Creator<RemoteMedia> CREATOR = new Parcelable.Creator<RemoteMedia>() {
@Override
public RemoteMedia createFromParcel(Parcel in) {
RemoteMedia result = new RemoteMedia(in.readString(), in.readString(), in.readString(),
in.readString(), in.readString(), in.readString(), in.readString(), in.readString(),
in.readString(), new Date(in.readLong()));
result.setNotes(in.readString());
result.setDuration(in.readInt());
result.setPosition(in.readInt());
result.setLastPlayedTime(in.readLong());
return result;
}
@Override
public RemoteMedia[] newArray(int size) {
return new RemoteMedia[size];
}
};
}

View File

@ -1,6 +1,7 @@
package de.danoeh.antennapod.core.util;
import android.net.Uri;
import android.text.TextUtils;
import android.util.Log;
import com.google.android.gms.cast.CastDevice;
@ -11,6 +12,7 @@ import com.google.android.gms.common.images.WebImage;
import java.util.Calendar;
import de.danoeh.antennapod.core.cast.CastManager;
import de.danoeh.antennapod.core.feed.Feed;
import de.danoeh.antennapod.core.feed.FeedImage;
import de.danoeh.antennapod.core.feed.FeedItem;
import de.danoeh.antennapod.core.feed.FeedMedia;
@ -23,7 +25,27 @@ import de.danoeh.antennapod.core.util.playback.Playable;
public class CastUtils {
private static final String TAG = "CastUtils";
public static final String KEY_MEDIA_ID = "CastUtils.Id";
public static final String KEY_MEDIA_ID = "AntennaPod.MediaId";
public static final String KEY_EPISODE_IDENTIFIER = "AntennaPod.EpisodeId";
public static final String KEY_EPISODE_LINK = "AntennaPod.EpisodeLink";
public static final String KEY_FEED_URL = "AntennaPod.FeedUrl";
public static final String KEY_FEED_WEBSITE = "AntennaPod.FeedWebsite";
public static final String KEY_EPISODE_NOTES = "AntennaPod.EpisodeNotes";
public static final int EPISODE_NOTES_MAX_LENGTH = Integer.MAX_VALUE;
/**
* The field <code>AntennaPod.FormatVersion</code> specifies which version of MediaMetaData
* fields we're using. Future implementations should try to be backwards compatible with earlier
* versions, and earlier versions should be forward compatible until the version indicated by
* <code>MAX_VERSION_FORWARD_COMPATIBILITY</code>. If an update makes the format unreadable for
* an earlier version, then its version number should be greater than the
* <code>MAX_VERSION_FORWARD_COMPATIBILITY</code> value set on the earlier one, so that it
* doesn't try to parse the object.
*/
public static final String KEY_FORMAT_VERSION = "AntennaPod.FormatVersion";
public static final int FORMAT_VERSION_VALUE = 1;
public static final int MAX_VERSION_FORWARD_COMPATIBILITY = 9999;
public static boolean isCastable(Playable media){
if (media == null || media instanceof ExternalMedia) {
@ -75,17 +97,53 @@ public class CastUtils {
metadata.putString(MediaMetadata.KEY_SUBTITLE, subtitle);
}
FeedImage image = feedItem.getImage();
if (image != null && image.getDownload_url() != null &&
!image.getDownload_url().isEmpty()) {
if (image != null && !TextUtils.isEmpty(image.getDownload_url())) {
metadata.addImage(new WebImage(Uri.parse(image.getDownload_url())));
}
Calendar calendar = Calendar.getInstance();
calendar.setTime(media.getItem().getPubDate());
metadata.putDate(MediaMetadata.KEY_RELEASE_DATE, calendar);
Feed feed = feedItem.getFeed();
if (feed != null) {
if (!TextUtils.isEmpty(feed.getAuthor())) {
metadata.putString(MediaMetadata.KEY_ARTIST, feed.getAuthor());
}
if (!TextUtils.isEmpty(feed.getDownload_url())) {
metadata.putString(KEY_FEED_URL, feed.getDownload_url());
}
if (!TextUtils.isEmpty(feed.getLink())) {
metadata.putString(KEY_FEED_WEBSITE, feed.getLink());
}
}
if (!TextUtils.isEmpty(feedItem.getItemIdentifier())) {
metadata.putString(KEY_EPISODE_IDENTIFIER, feedItem.getItemIdentifier());
} else {
metadata.putString(KEY_EPISODE_IDENTIFIER, media.getStreamUrl());
}
if (!TextUtils.isEmpty(feedItem.getLink())) {
metadata.putString(KEY_EPISODE_LINK, feedItem.getLink());
}
}
//metadata.putString(MediaMetadata.KEY_ARTIST, null);
metadata.putString(KEY_MEDIA_ID, media.getIdentifier().toString());
String notes = null;
try {
notes = media.loadShownotes().call();
} catch (Exception e) {
Log.e(TAG, "Unable to load FeedMedia notes", e);
}
if (notes != null) {
if (notes.length() > EPISODE_NOTES_MAX_LENGTH) {
notes = notes.substring(0, EPISODE_NOTES_MAX_LENGTH);
}
metadata.putString(KEY_EPISODE_NOTES, notes);
}
// This field only identifies the id on the device that has the original version.
// Idea is to perhaps, on a first approach, check if the version on the local DB with the
// same id matches the remote object, and if not then search for episode and feed identifiers.
// This at least should make media recognition for a single device much quicker.
metadata.putInt(KEY_MEDIA_ID, ((Long) media.getIdentifier()).intValue());
// A way to identify different casting media formats in case we change it in the future and
// senders with different versions share a casting device.
metadata.putInt(KEY_FORMAT_VERSION, FORMAT_VERSION_VALUE);
return new MediaInfo.Builder(media.getStreamUrl())
.setContentType(media.getMime_type())
@ -94,5 +152,7 @@ public class CastUtils {
.build();
}
//TODO Queue handling perhaps
}

View File

@ -8,6 +8,7 @@ import android.util.Log;
import java.util.List;
import de.danoeh.antennapod.core.asynctask.ImageResource;
import de.danoeh.antennapod.core.cast.RemoteMedia;
import de.danoeh.antennapod.core.feed.Chapter;
import de.danoeh.antennapod.core.feed.FeedMedia;
import de.danoeh.antennapod.core.feed.MediaType;
@ -183,6 +184,9 @@ public interface Playable extends Parcelable,
case ExternalMedia.PLAYABLE_TYPE_EXTERNAL_MEDIA:
result = createExternalMediaInstance(pref);
break;
case RemoteMedia.PLAYABLE_TYPE_REMOTE_MEDIA:
result = createRemoteMediaInstance(pref);
break;
}
if (result == null) {
Log.e(TAG, "Could not restore Playable object from preferences");
@ -211,6 +215,12 @@ public interface Playable extends Parcelable,
}
return result;
}
private static Playable createRemoteMediaInstance(SharedPreferences pref) {
//TODO there's probably no point in restoring RemoteMedia from preferences, because we
//only care about it while it's playing on the cast device.
return null;
}
}
class PlayableException extends Exception {