summaryrefslogtreecommitdiff
path: root/src/de/danoeh/antennapod/util/playback/Playable.java
blob: 9ed45abfc36cc8c25bc1b58a204cdcfd4e940340 (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
package de.danoeh.antennapod.util.playback;

import android.content.Context;
import android.content.SharedPreferences;
import android.media.MediaMetadataRetriever;
import android.os.Parcelable;
import android.util.Log;
import de.danoeh.antennapod.asynctask.ImageLoader;
import de.danoeh.antennapod.feed.Chapter;
import de.danoeh.antennapod.feed.FeedMedia;
import de.danoeh.antennapod.feed.MediaType;
import de.danoeh.antennapod.storage.DBReader;
import de.danoeh.antennapod.util.ShownotesProvider;
import org.apache.commons.io.IOUtils;
import org.apache.commons.lang3.Validate;

import java.io.ByteArrayInputStream;
import java.io.InputStream;
import java.util.List;

/**
 * Interface for objects that can be played by the PlaybackService.
 */
public interface Playable extends Parcelable,
        ImageLoader.ImageWorkerTaskResource, ShownotesProvider {

    /**
     * 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;
                case ExternalMedia.PLAYABLE_TYPE_EXTERNAL_MEDIA:
                    String source = pref.getString(ExternalMedia.PREF_SOURCE_URL,
                            null);
                    String mediaType = pref.getString(
                            ExternalMedia.PREF_MEDIA_TYPE, null);
                    if (source != null && mediaType != null) {
                        int position = pref.getInt(ExternalMedia.PREF_POSITION, 0);
                        return new ExternalMedia(source,
                                MediaType.valueOf(mediaType), position);
                    }
                    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) {
            Validate.notNull(playable);

            this.playable = playable;
        }

        @Override
        public InputStream openImageInputStream() {
            if (playable.localFileAvailable()
                    && playable.getLocalMediaUrl() != null) {
                MediaMetadataRetriever mmr = new MediaMetadataRetriever();
                try {
                    mmr.setDataSource(playable.getLocalMediaUrl());
                } catch (IllegalArgumentException e) {
                    e.printStackTrace();
                    return null;
                }
                byte[] imgData = mmr.getEmbeddedPicture();
                if (imgData != null) {
                    return new PublicByteArrayInputStream(imgData);

                }
            }
            return null;
        }

        @Override
        public String getImageLoaderCacheKey() {
            return playable.getLocalMediaUrl();
        }

        @Override
        public InputStream reopenImageInputStream(InputStream input) {
            if (input instanceof PublicByteArrayInputStream) {
                IOUtils.closeQuietly(input);
                byte[] imgData = ((PublicByteArrayInputStream) input)
                        .getByteArray();
                if (imgData != null) {
                    ByteArrayInputStream out = new ByteArrayInputStream(imgData);
                    return out;
                }

            }
            return null;
        }

        private static class PublicByteArrayInputStream extends
                ByteArrayInputStream {
            public PublicByteArrayInputStream(byte[] buf) {
                super(buf);
            }

            public byte[] getByteArray() {
                return buf;
            }
        }
    }
}