package de.danoeh.antennapodsp.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.antennapodsp.asynctask.ImageLoader;
import de.danoeh.antennapodsp.feed.Chapter;
import de.danoeh.antennapodsp.feed.FeedMedia;
import de.danoeh.antennapodsp.feed.MediaType;
import de.danoeh.antennapodsp.storage.DBReader;
import de.danoeh.antennapodsp.util.ShownotesProvider;
import org.apache.commons.io.IOUtils;
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 {
/**
* Save information about the playable in a preference so that it can be
* restored later via PlayableUtils.createInstanceFromPreferences.
* Implementations must NOT call commit() after they have written the values
* to the preferences file.
*/
public void writeToPreferences(SharedPreferences.Editor prefEditor);
/**
* This method is called from a separate thread by the PlaybackService.
* Playable objects should load their metadata in this method. This method
* should execute as quickly as possible and NOT load chapter marks if no
* local file is available.
*/
public void loadMetadata() throws PlayableException;
/**
* This method is called from a separate thread by the PlaybackService.
* Playable objects should load their chapter marks in this method if no
* local file was available when loadMetadata() was called.
*/
public void loadChapterMarks();
/**
* Returns the title of the episode that this playable represents
*/
public String getEpisodeTitle();
/**
* Returns a list of chapter marks or null if this Playable has no chapters.
*/
public List<Chapter> getChapters();
/**
* Returns a link to a website that is meant to be shown in a browser
*/
public String getWebsiteLink();
public String getPaymentLink();
/**
* Returns the title of the feed this Playable belongs to.
*/
public String getFeedTitle();
/**
* Returns a unique identifier, for example a file url or an ID from a
* database.
*/
public Object getIdentifier();
/**
* Return duration of object or 0 if duration is unknown.
*/
public int getDuration();
/**
* Return position of object or 0 if position is unknown.
*/
public int getPosition();
/**
* Returns the type of media. This method should return the correct value
* BEFORE loadMetadata() is called.
*/
public MediaType getMediaType();
/**
* Returns an url to a local file that can be played or null if this file
* does not exist.
*/
public String getLocalMediaUrl();
/**
* Returns an url to a file that can be streamed by the player or null if
* this url is not known.
*/
public String getStreamUrl();
/**
* Returns true if a local file that can be played is available. getFileUrl
* MUST return a non-null string if this method returns true.
*/
public boolean localFileAvailable();
/**
* Returns true if a streamable file is available. getStreamUrl MUST return
* a non-null string if this method returns true.
*/
public boolean streamAvailable();
/**
* Saves the current position of this object. Implementations can use the
* provided SharedPreference to save this information and retrieve it later
* via PlayableUtils.createInstanceFromPreferences.
*/
public void saveCurrentPosition(SharedPreferences pref, int newPosition);
public void setPosition(int newPosition);
public void setDuration(int newDuration);
/**
* Is called by the PlaybackService when playback starts.
*/
public void onPlaybackStart();
/**
* Is called by the PlaybackService when playback is completed.
*/
public void onPlaybackCompleted();
/**
* Returns an integer that must be unique among all Playable classes. The
* return value is later used by PlayableUtils to determine the type of the
* Playable object that is restored.
*/
public int getPlayableType();
public void setChapters(List<Chapter> chapters);
/**
* Provides utility methods for Playable objects.
*/
public static class PlayableUtils {
private static final String TAG = "PlayableUtils";
/**
* Restores a playable object from a sharedPreferences file. This method might load data from the database,
* depending on the type of playable that was restored.
*
* @param type An integer that represents the type of the Playable object
* that is restored.
* @param pref The SharedPreferences file from which the Playable object
* is restored
* @return The restored Playable object
*/
public static Playable createInstanceFromPreferences(Context context, int type,
SharedPreferences pref) {
// ADD new Playable types here:
switch (type) {
case FeedMedia.PLAYABLE_TYPE_FEEDMEDIA:
long mediaId = pref.getLong(FeedMedia.PREF_MEDIA_ID, -1);
if (mediaId != -1) {
return DBReader.getFeedMedia(context, mediaId);
}
break;
}
Log.e(TAG, "Could not restore Playable object from preferences");
return null;
}
}
public static class PlayableException extends Exception {
private static final long serialVersionUID = 1L;
public PlayableException() {
super();
}
public PlayableException(String detailMessage, Throwable throwable) {
super(detailMessage, throwable);
}
public PlayableException(String detailMessage) {
super(detailMessage);
}
public PlayableException(Throwable throwable) {
super(throwable);
}
}
/**
* Uses local file as image resource if it is available.
*/
public static class DefaultPlayableImageLoader implements
ImageLoader.ImageWorkerTaskResource {
private Playable playable;
public DefaultPlayableImageLoader(Playable playable) {
if (playable == null) {
throw new IllegalArgumentException("Playable must not be null");
}
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;
}
}
}
}