MediaItemStatus.java revision de3d4d3c033fc939af83775a443b295a4034444c
1/*
2 * Copyright (C) 2013 The Android Open Source Project
3 *
4 * Licensed under the Apache License, Version 2.0 (the "License");
5 * you may not use this file except in compliance with the License.
6 * You may obtain a copy of the License at
7 *
8 *      http://www.apache.org/licenses/LICENSE-2.0
9 *
10 * Unless required by applicable law or agreed to in writing, software
11 * distributed under the License is distributed on an "AS IS" BASIS,
12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 * See the License for the specific language governing permissions and
14 * limitations under the License.
15 */
16
17package android.support.v7.media;
18
19import android.app.PendingIntent;
20import android.os.Bundle;
21import android.os.SystemClock;
22import android.support.v4.util.TimeUtils;
23
24/**
25 * Describes the playback status of a media item.
26 * <p>
27 * As a media item is played, it transitions through a sequence of states including:
28 * {@link #PLAYBACK_STATE_QUEUED queued}, {@link #PLAYBACK_STATE_BUFFERING buffering},
29 * {@link #PLAYBACK_STATE_PLAYING playing}, {@link #PLAYBACK_STATE_PAUSED paused},
30 * {@link #PLAYBACK_STATE_STOPPED stopped}, {@link #PLAYBACK_STATE_CANCELED canceled},
31 * {@link #PLAYBACK_STATE_ERROR error}.  Refer to the documentation of each state
32 * for an explanation of its meaning.
33 * </p><p>
34 * While the item is playing, the playback status may also include progress information
35 * about the {@link #getContentPosition content position} and
36 * {@link #getContentDuration content duration} although not all route destinations
37 * will report it.
38 * </p><p>
39 * To monitor playback status, the application should supply a {@link PendingIntent} to use
40 * as the {@link MediaControlIntent#EXTRA_ITEM_STATUS_UPDATE_RECEIVER status update receiver}
41 * for a given {@link MediaControlIntent#ACTION_PLAY playback request}.  Note that
42 * the status update receiver will only be invoked for major status changes such as a
43 * transition from playing to stopped.
44 * </p><p class="note">
45 * The status update receiver will not be invoked for minor progress updates such as
46 * changes to playback position or duration.  If the application wants to monitor
47 * playback progress, then it must use the
48 * {@link MediaControlIntent#ACTION_GET_STATUS get status request} to poll for changes
49 * periodically and estimate the playback position while playing.  Note that there may
50 * be a significant power impact to polling so the application is advised only
51 * to poll when the screen is on and never more than about once every 5 seconds or so.
52 * </p><p>
53 * This object is immutable once created using a {@link Builder} instance.
54 * </p>
55 */
56public final class MediaItemStatus {
57    private static final String KEY_TIMESTAMP = "timestamp";
58    private static final String KEY_PLAYBACK_STATE = "playbackState";
59    private static final String KEY_CONTENT_POSITION = "contentPosition";
60    private static final String KEY_CONTENT_DURATION = "contentDuration";
61    private static final String KEY_HTTP_STATUS_CODE = "httpStatusCode";
62    private static final String KEY_EXTRAS = "extras";
63
64    private final Bundle mBundle;
65
66    /**
67     * Playback state: Queued.
68     * <p>
69     * Indicates that the media item is in the queue to be played eventually.
70     * </p>
71     */
72    public static final int PLAYBACK_STATE_QUEUED = 0;
73
74    /**
75     * Playback state: Playing.
76     * <p>
77     * Indicates that the media item is currently playing.
78     * </p>
79     */
80    public static final int PLAYBACK_STATE_PLAYING = 1;
81
82    /**
83     * Playback state: Paused.
84     * <p>
85     * Indicates that playback of the media item has been paused because the
86     * queue was paused.  Playback can be resumed playback by sending
87     * {@link MediaControlIntent#ACTION_RESUME_QUEUE} to resume playback of the queue.
88     * </p><p>
89     * Only the media item at the head of the queue enters the paused state when the
90     * queue is paused because that is the media item that would otherwise have been
91     * {@link #PLAYBACK_STATE_PLAYING playing}; other media items in the queue remain
92     * in the {@link #PLAYBACK_STATE_QUEUED queued} state until the head item
93     * finishes playing or is removed from the queue.
94     * </p>
95     */
96    public static final int PLAYBACK_STATE_PAUSED = 2;
97
98    /**
99     * Playback state: Buffering or seeking to a new position.
100     * <p>
101     * Indicates that the media item has been temporarily interrupted
102     * to fetch more content.  Playback will resume automatically
103     * when enough content has been buffered.
104     * </p>
105     */
106    public static final int PLAYBACK_STATE_BUFFERING = 3;
107
108    /**
109     * Playback state: Stopped.
110     * <p>
111     * Indicates that the media item has been stopped permanently either because
112     * it reached the end of the content or because the user ended playback.
113     * </p><p>
114     * A stopped media item cannot be resumed.  To play the content again, the application
115     * must send a new {@link MediaControlIntent#ACTION_PLAY} action to enqueue
116     * a new playback request and obtain a new media item id from that request.
117     * </p>
118     */
119    public static final int PLAYBACK_STATE_STOPPED = 4;
120
121    /**
122     * Playback state: Canceled.
123     * <p>
124     * Indicates that the media item was canceled permanently.  This may
125     * happen because the media item was removed from the queue, the queue was
126     * cleared by the application, or the queue was invalidated by another playback
127     * request that resulted in the creation of a new queue.
128     * </p><p>
129     * A canceled media item cannot be resumed.  To play the content again, the application
130     * must send a new {@link MediaControlIntent#ACTION_PLAY} action to enqueue
131     * a new playback request and obtain a new media item id from that request.
132     * </p>
133     */
134    public static final int PLAYBACK_STATE_CANCELED = 5;
135
136    /**
137     * Playback state: Playback halted or aborted due to an error.
138     * <p>
139     * Examples of errors are no network connectivity when attempting to retrieve content
140     * from a server, or expired user credentials when trying to play subscription-based
141     * content.
142     * </p><p>
143     * A media item in the error state cannot be resumed.  To play the content again,
144     * the application must send a new {@link MediaControlIntent#ACTION_PLAY} action to enqueue
145     * a new playback request and obtain a new media item id from that request.
146     * </p>
147     */
148    public static final int PLAYBACK_STATE_ERROR = 6;
149
150    private MediaItemStatus(Bundle bundle) {
151        mBundle = bundle;
152    }
153
154    /**
155     * Gets the timestamp associated with the status information in
156     * milliseconds since boot in the {@link SystemClock#elapsedRealtime} time base.
157     *
158     * @return The status timestamp in the {@link SystemClock#elapsedRealtime()} time base.
159     */
160    public long getTimestamp() {
161        return mBundle.getLong(KEY_TIMESTAMP);
162    }
163
164    /**
165     * Gets the playback state of the media item.
166     *
167     * @return The playback state.  One of {@link #PLAYBACK_STATE_QUEUED},
168     * {@link #PLAYBACK_STATE_PLAYING},
169     * {@link #PLAYBACK_STATE_PAUSED}, {@link #PLAYBACK_STATE_BUFFERING},
170     * {@link #PLAYBACK_STATE_CANCELED}, or {@link #PLAYBACK_STATE_ERROR}.
171     */
172    public int getPlaybackState() {
173        return mBundle.getInt(KEY_PLAYBACK_STATE, PLAYBACK_STATE_CANCELED);
174    }
175
176    /**
177     * Gets the content playback position as a floating point number of seconds
178     * from the beginning of the content.
179     *
180     * @return The content playback position in seconds, or -1 if unknown.
181     */
182    public double getContentPosition() {
183        return mBundle.getDouble(KEY_CONTENT_POSITION, -1);
184    }
185
186    /**
187     * Gets the total duration of the content to be played as a floating point number
188     * of seconds.
189     *
190     * @return The content duration in seconds, or -1 if unknown.
191     */
192    public double getContentDuration() {
193        return mBundle.getDouble(KEY_CONTENT_DURATION, -1);
194    }
195
196    /**
197     * Gets the associated HTTP status code.
198     * <p>
199     * Specifies the HTTP status code that was encountered when the content
200     * was requested after all redirects were followed.  This key only needs to
201     * specified when the content uri uses the HTTP or HTTPS scheme and an error
202     * occurred.  This key may be omitted if the content was able to be played
203     * successfully; there is no need to report a 200 (OK) status code.
204     * </p>
205     *
206     * @return The HTTP status code from playback such as 401 (Unauthorized), 404 (Not Found),
207     * or 500 (Server Error), or 0 if none.
208     */
209    public int getHttpStatusCode() {
210        return mBundle.getInt(KEY_HTTP_STATUS_CODE, 0);
211    }
212
213    /**
214     * Gets a bundle of extras for this status object.
215     * The extras will be ignored by the media router but they may be used
216     * by applications.
217     */
218    public Bundle getExtras() {
219        return mBundle.getBundle(KEY_EXTRAS);
220    }
221
222    @Override
223    public String toString() {
224        StringBuilder result = new StringBuilder();
225        result.append("MediaItemStatus{ ");
226        result.append("timestamp=");
227        TimeUtils.formatDuration(SystemClock.elapsedRealtime() - getTimestamp(), result);
228        result.append(" ms ago");
229        result.append(", playbackState=").append(getPlaybackState());
230        result.append(", contentPosition=").append(getContentPosition());
231        result.append(", contentDuration=").append(getContentDuration());
232        result.append(", httpStatusCode=").append(getHttpStatusCode());
233        result.append(", extras=").append(getExtras());
234        result.append(" }");
235        return result.toString();
236    }
237
238    /**
239     * Converts this object to a bundle for serialization.
240     *
241     * @return The contents of the object represented as a bundle.
242     */
243    public Bundle asBundle() {
244        return mBundle;
245    }
246
247    /**
248     * Creates an instance from a bundle.
249     *
250     * @param bundle The bundle, or null if none.
251     * @return The new instance, or null if the bundle was null.
252     */
253    public static MediaItemStatus fromBundle(Bundle bundle) {
254        return bundle != null ? new MediaItemStatus(bundle) : null;
255    }
256
257    /**
258     * Builder for {@link MediaItemStatus media item status objects}.
259     */
260    public static final class Builder {
261        private final Bundle mBundle;
262
263        /**
264         * Creates a media item status builder using the current time as the
265         * reference timestamp.
266         *
267         * @param playbackState The item playback state.
268         */
269        public Builder(int playbackState) {
270            mBundle = new Bundle();
271            setTimestamp(SystemClock.elapsedRealtime());
272            setPlaybackState(playbackState);
273        }
274
275        /**
276         * Creates a media item status builder whose initial contents are
277         * copied from an existing status.
278         */
279        public Builder(MediaItemStatus status) {
280            if (status == null) {
281                throw new IllegalArgumentException("status must not be null");
282            }
283
284            mBundle = new Bundle(status.mBundle);
285        }
286
287        /**
288         * Sets the timestamp associated with the status information in
289         * milliseconds since boot in the {@link SystemClock#elapsedRealtime} time base.
290         */
291        public Builder setTimestamp(long elapsedRealtimeTimestamp) {
292            mBundle.putLong(KEY_TIMESTAMP, elapsedRealtimeTimestamp);
293            return this;
294        }
295
296        /**
297         * Sets the playback state of the media item.
298         */
299        public Builder setPlaybackState(int playbackState) {
300            mBundle.putInt(KEY_PLAYBACK_STATE, playbackState);
301            return this;
302        }
303
304        /**
305         * Sets the content playback position as a floating point number of seconds
306         * from the beginning of the content.
307         */
308        public Builder setContentPosition(double positionSeconds) {
309            mBundle.putDouble(KEY_CONTENT_POSITION, positionSeconds);
310            return this;
311        }
312
313        /**
314         * Sets the total duration of the content to be played as a floating point number
315         * of seconds.
316         */
317        public Builder setContentDuration(double durationSeconds) {
318            mBundle.putDouble(KEY_CONTENT_DURATION, durationSeconds);
319            return this;
320        }
321
322        /**
323         * Sets the associated HTTP status code.
324         * <p>
325         * Specifies the HTTP status code that was encountered when the content
326         * was requested after all redirects were followed.  This key only needs to
327         * specified when the content uri uses the HTTP or HTTPS scheme and an error
328         * occurred.  This key may be omitted if the content was able to be played
329         * successfully; there is no need to report a 200 (OK) status code.
330         * </p>
331         */
332        public Builder setHttpStatusCode(int httpStatusCode) {
333            mBundle.putInt(KEY_HTTP_STATUS_CODE, httpStatusCode);
334            return this;
335        }
336
337        /**
338         * Sets a bundle of extras for this status object.
339         * The extras will be ignored by the media router but they may be used
340         * by applications.
341         */
342        public Builder setExtras(Bundle extras) {
343            mBundle.putBundle(KEY_EXTRAS, extras);
344            return this;
345        }
346
347        /**
348         * Builds the {@link MediaItemStatus media item status object}.
349         */
350        public MediaItemStatus build() {
351            return new MediaItemStatus(mBundle);
352        }
353    }
354}
355