MediaItemStatus.java revision 3d4c9459ed77f732dd3ba602713af6ebf9280c8c
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 the media item has been paused.  Playback can be
86     * resumed playback by sending {@link MediaControlIntent#ACTION_RESUME}.
87     * </p>
88     */
89    public static final int PLAYBACK_STATE_PAUSED = 2;
90
91    /**
92     * Playback state: Buffering or seeking to a new position.
93     * <p>
94     * Indicates that the media item has been temporarily interrupted
95     * to fetch more content.  Playback will resume automatically
96     * when enough content has been buffered.
97     * </p>
98     */
99    public static final int PLAYBACK_STATE_BUFFERING = 3;
100
101    /**
102     * Playback state: Stopped.
103     * <p>
104     * Indicates that the media item has been stopped permanently either because
105     * it reached the end of the content or because the user ended playback.
106     * </p><p>
107     * A stopped media item cannot be resumed.  To play the content again, the application
108     * must send a new {@link MediaControlIntent#ACTION_PLAY} action to enqueue
109     * a new playback request and obtain a new media item id from that request.
110     * </p>
111     */
112    public static final int PLAYBACK_STATE_STOPPED = 4;
113
114    /**
115     * Playback state: Canceled.
116     * <p>
117     * Indicates that the media item was canceled permanently.  This may
118     * happen because a new media item was queued which caused this media item
119     * to be stopped and removed from the queue.
120     * </p><p>
121     * A canceled media item cannot be resumed.  To play the content again, the application
122     * must send a new {@link MediaControlIntent#ACTION_PLAY} action to enqueue
123     * a new playback request and obtain a new media item id from that request.
124     * </p>
125     */
126    public static final int PLAYBACK_STATE_CANCELED = 5;
127
128    /**
129     * Playback state: Playback halted or aborted due to an error.
130     * <p>
131     * Examples of errors are no network connectivity when attempting to retrieve content
132     * from a server, or expired user credentials when trying to play subscription-based
133     * content.
134     * </p><p>
135     * A media item in the error state cannot be resumed.  To play the content again,
136     * the application must send a new {@link MediaControlIntent#ACTION_PLAY} action to enqueue
137     * a new playback request and obtain a new media item id from that request.
138     * </p>
139     */
140    public static final int PLAYBACK_STATE_ERROR = 6;
141
142    private MediaItemStatus(Bundle bundle) {
143        mBundle = bundle;
144    }
145
146    /**
147     * Gets the timestamp associated with the status information in
148     * milliseconds since boot in the {@link SystemClock#elapsedRealtime} time base.
149     *
150     * @return The status timestamp in the {@link SystemClock#elapsedRealtime()} time base.
151     */
152    public long getTimestamp() {
153        return mBundle.getLong(KEY_TIMESTAMP);
154    }
155
156    /**
157     * Gets the playback state of the media item.
158     *
159     * @return The playback state.  One of {@link #PLAYBACK_STATE_QUEUED},
160     * {@link #PLAYBACK_STATE_PLAYING},
161     * {@link #PLAYBACK_STATE_PAUSED}, {@link #PLAYBACK_STATE_BUFFERING},
162     * {@link #PLAYBACK_STATE_CANCELED}, or {@link #PLAYBACK_STATE_ERROR}.
163     */
164    public int getPlaybackState() {
165        return mBundle.getInt(KEY_PLAYBACK_STATE, PLAYBACK_STATE_CANCELED);
166    }
167
168    /**
169     * Gets the content playback position as a floating point number of seconds
170     * from the beginning of the content.
171     *
172     * @return The content playback position in seconds, or -1 if unknown.
173     */
174    public double getContentPosition() {
175        return mBundle.getDouble(KEY_CONTENT_POSITION, -1);
176    }
177
178    /**
179     * Gets the total duration of the content to be played as a floating point number
180     * of seconds.
181     *
182     * @return The content duration in seconds, or -1 if unknown.
183     */
184    public double getContentDuration() {
185        return mBundle.getDouble(KEY_CONTENT_DURATION, -1);
186    }
187
188    /**
189     * Gets the associated HTTP status code.
190     * <p>
191     * Specifies the HTTP status code that was encountered when the content
192     * was requested after all redirects were followed.  This key only needs to
193     * specified when the content uri uses the HTTP or HTTPS scheme and an error
194     * occurred.  This key may be omitted if the content was able to be played
195     * successfully; there is no need to report a 200 (OK) status code.
196     * </p>
197     *
198     * @return The HTTP status code from playback such as 401 (Unauthorized), 404 (Not Found),
199     * or 500 (Server Error), or 0 if none.
200     */
201    public int getHttpStatusCode() {
202        return mBundle.getInt(KEY_HTTP_STATUS_CODE, 0);
203    }
204
205    /**
206     * Gets a bundle of extras for this status object.
207     * The extras will be ignored by the media router but they may be used
208     * by applications.
209     */
210    public Bundle getExtras() {
211        return mBundle.getBundle(KEY_EXTRAS);
212    }
213
214    @Override
215    public String toString() {
216        StringBuilder result = new StringBuilder();
217        result.append("MediaItemStatus{ ");
218        result.append("timestamp=");
219        TimeUtils.formatDuration(SystemClock.elapsedRealtime() - getTimestamp(), result);
220        result.append(" ms ago");
221        result.append(", playbackState=").append(getPlaybackState());
222        result.append(", contentPosition=").append(getContentPosition());
223        result.append(", contentDuration=").append(getContentDuration());
224        result.append(", httpStatusCode=").append(getHttpStatusCode());
225        result.append(", extras=").append(getExtras());
226        result.append(" }");
227        return result.toString();
228    }
229
230    /**
231     * Converts this object to a bundle for serialization.
232     *
233     * @return The contents of the object represented as a bundle.
234     */
235    public Bundle asBundle() {
236        return mBundle;
237    }
238
239    /**
240     * Creates an instance from a bundle.
241     *
242     * @param bundle The bundle, or null if none.
243     * @return The new instance, or null if the bundle was null.
244     */
245    public static MediaItemStatus fromBundle(Bundle bundle) {
246        return bundle != null ? new MediaItemStatus(bundle) : null;
247    }
248
249    /**
250     * Builder for {@link MediaItemStatus media item status objects}.
251     */
252    public static final class Builder {
253        private final Bundle mBundle;
254
255        /**
256         * Creates a media item status builder using the current time as the
257         * reference timestamp.
258         *
259         * @param playbackState The item playback state.
260         */
261        public Builder(int playbackState) {
262            mBundle = new Bundle();
263            setTimestamp(SystemClock.elapsedRealtime());
264            setPlaybackState(playbackState);
265        }
266
267        /**
268         * Creates a media item status builder whose initial contents are
269         * copied from an existing status.
270         */
271        public Builder(MediaItemStatus status) {
272            if (status == null) {
273                throw new IllegalArgumentException("status must not be null");
274            }
275
276            mBundle = new Bundle(status.mBundle);
277        }
278
279        /**
280         * Sets the timestamp associated with the status information in
281         * milliseconds since boot in the {@link SystemClock#elapsedRealtime} time base.
282         */
283        public Builder setTimestamp(long elapsedRealtimeTimestamp) {
284            mBundle.putLong(KEY_TIMESTAMP, elapsedRealtimeTimestamp);
285            return this;
286        }
287
288        /**
289         * Sets the playback state of the media item.
290         */
291        public Builder setPlaybackState(int playbackState) {
292            mBundle.putInt(KEY_PLAYBACK_STATE, playbackState);
293            return this;
294        }
295
296        /**
297         * Sets the content playback position as a floating point number of seconds
298         * from the beginning of the content.
299         */
300        public Builder setContentPosition(double positionSeconds) {
301            mBundle.putDouble(KEY_CONTENT_POSITION, positionSeconds);
302            return this;
303        }
304
305        /**
306         * Sets the total duration of the content to be played as a floating point number
307         * of seconds.
308         */
309        public Builder setContentDuration(double durationSeconds) {
310            mBundle.putDouble(KEY_CONTENT_DURATION, durationSeconds);
311            return this;
312        }
313
314        /**
315         * Sets the associated HTTP status code.
316         * <p>
317         * Specifies the HTTP status code that was encountered when the content
318         * was requested after all redirects were followed.  This key only needs to
319         * specified when the content uri uses the HTTP or HTTPS scheme and an error
320         * occurred.  This key may be omitted if the content was able to be played
321         * successfully; there is no need to report a 200 (OK) status code.
322         * </p>
323         */
324        public Builder setHttpStatusCode(int httpStatusCode) {
325            mBundle.putInt(KEY_HTTP_STATUS_CODE, httpStatusCode);
326            return this;
327        }
328
329        /**
330         * Sets a bundle of extras for this status object.
331         * The extras will be ignored by the media router but they may be used
332         * by applications.
333         */
334        public Builder setExtras(Bundle extras) {
335            mBundle.putBundle(KEY_EXTRAS, extras);
336            return this;
337        }
338
339        /**
340         * Builds the {@link MediaItemStatus media item status object}.
341         */
342        public MediaItemStatus build() {
343            return new MediaItemStatus(mBundle);
344        }
345    }
346}
347