• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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 
17 package android.support.v7.media;
18 
19 import android.app.PendingIntent;
20 import android.os.Bundle;
21 import android.os.SystemClock;
22 import android.support.v4.util.TimeUtils;
23 
24 /**
25  * Describes the playback status of a media item.
26  * <p>
27  * This class is part of the remote playback protocol described by the
28  * {@link MediaControlIntent MediaControlIntent} class.
29  * </p><p>
30  * As a media item is played, it transitions through a sequence of states including:
31  * {@link #PLAYBACK_STATE_PENDING pending}, {@link #PLAYBACK_STATE_BUFFERING buffering},
32  * {@link #PLAYBACK_STATE_PLAYING playing}, {@link #PLAYBACK_STATE_PAUSED paused},
33  * {@link #PLAYBACK_STATE_FINISHED finished}, {@link #PLAYBACK_STATE_CANCELED canceled},
34  * {@link #PLAYBACK_STATE_INVALIDATED invalidated}, and
35  * {@link #PLAYBACK_STATE_ERROR error}.  Refer to the documentation of each state
36  * for an explanation of its meaning.
37  * </p><p>
38  * While the item is playing, the playback status may also include progress information
39  * about the {@link #getContentPosition content position} and
40  * {@link #getContentDuration content duration} although not all route destinations
41  * will report it.
42  * </p><p>
43  * To monitor playback status, the application should supply a {@link PendingIntent} to use as the
44  * {@link MediaControlIntent#EXTRA_ITEM_STATUS_UPDATE_RECEIVER item status update receiver}
45  * for a given {@link MediaControlIntent#ACTION_PLAY playback request}.  Note that
46  * the status update receiver will only be invoked for major status changes such as a
47  * transition from playing to finished.
48  * </p><p class="note">
49  * The status update receiver will not be invoked for minor progress updates such as
50  * changes to playback position or duration.  If the application wants to monitor
51  * playback progress, then it must use the
52  * {@link MediaControlIntent#ACTION_GET_STATUS get status request} to poll for changes
53  * periodically and estimate the playback position while playing.  Note that there may
54  * be a significant power impact to polling so the application is advised only
55  * to poll when the screen is on and never more than about once every 5 seconds or so.
56  * </p><p>
57  * This object is immutable once created using a {@link Builder} instance.
58  * </p>
59  */
60 public final class MediaItemStatus {
61     static final String KEY_TIMESTAMP = "timestamp";
62     static final String KEY_PLAYBACK_STATE = "playbackState";
63     static final String KEY_CONTENT_POSITION = "contentPosition";
64     static final String KEY_CONTENT_DURATION = "contentDuration";
65     static final String KEY_EXTRAS = "extras";
66 
67     final Bundle mBundle;
68 
69     /**
70      * Playback state: Pending.
71      * <p>
72      * Indicates that the media item has not yet started playback but will be played eventually.
73      * </p>
74      */
75     public static final int PLAYBACK_STATE_PENDING = 0;
76 
77     /**
78      * Playback state: Playing.
79      * <p>
80      * Indicates that the media item is currently playing.
81      * </p>
82      */
83     public static final int PLAYBACK_STATE_PLAYING = 1;
84 
85     /**
86      * Playback state: Paused.
87      * <p>
88      * Indicates that playback of the media item has been paused.  Playback can be
89      * resumed using the {@link MediaControlIntent#ACTION_RESUME resume} action.
90      * </p>
91      */
92     public static final int PLAYBACK_STATE_PAUSED = 2;
93 
94     /**
95      * Playback state: Buffering or seeking to a new position.
96      * <p>
97      * Indicates that the media item has been temporarily interrupted
98      * to fetch more content.  Playback will continue automatically
99      * when enough content has been buffered.
100      * </p>
101      */
102     public static final int PLAYBACK_STATE_BUFFERING = 3;
103 
104     /**
105      * Playback state: Finished.
106      * <p>
107      * Indicates that the media item played to the end of the content and finished normally.
108      * </p><p>
109      * A finished media item cannot be resumed.  To play the content again, the application
110      * must send a new {@link MediaControlIntent#ACTION_PLAY play} or
111      * {@link MediaControlIntent#ACTION_ENQUEUE enqueue} action.
112      * </p>
113      */
114     public static final int PLAYBACK_STATE_FINISHED = 4;
115 
116     /**
117      * Playback state: Canceled.
118      * <p>
119      * Indicates that the media item was explicitly removed from the queue by the
120      * application.  Items may be canceled and removed from the queue using
121      * the {@link MediaControlIntent#ACTION_REMOVE remove} or
122      * {@link MediaControlIntent#ACTION_STOP stop} action or by issuing
123      * another {@link MediaControlIntent#ACTION_PLAY play} action that has the
124      * side-effect of clearing the queue.
125      * </p><p>
126      * A canceled media item cannot be resumed.  To play the content again, the
127      * application must send a new {@link MediaControlIntent#ACTION_PLAY play} or
128      * {@link MediaControlIntent#ACTION_ENQUEUE enqueue} action.
129      * </p>
130      */
131     public static final int PLAYBACK_STATE_CANCELED = 5;
132 
133     /**
134      * Playback state: Invalidated.
135      * <p>
136      * Indicates that the media item was invalidated permanently and involuntarily.
137      * This state is used to indicate that the media item was invalidated and removed
138      * from the queue because the session to which it belongs was invalidated
139      * (typically by another application taking control of the route).
140      * </p><p>
141      * When invalidation occurs, the application should generally wait for the user
142      * to perform an explicit action, such as clicking on a play button in the UI,
143      * before creating a new media session to avoid unnecessarily interrupting
144      * another application that may have just started using the route.
145      * </p><p>
146      * An invalidated media item cannot be resumed.  To play the content again, the application
147      * must send a new {@link MediaControlIntent#ACTION_PLAY play} or
148      * {@link MediaControlIntent#ACTION_ENQUEUE enqueue} action.
149      * </p>
150      */
151     public static final int PLAYBACK_STATE_INVALIDATED = 6;
152 
153     /**
154      * Playback state: Playback halted or aborted due to an error.
155      * <p>
156      * Examples of errors are no network connectivity when attempting to retrieve content
157      * from a server, or expired user credentials when trying to play subscription-based
158      * content.
159      * </p><p>
160      * A media item in the error state cannot be resumed.  To play the content again,
161      * the application must send a new {@link MediaControlIntent#ACTION_PLAY play} or
162      * {@link MediaControlIntent#ACTION_ENQUEUE enqueue} action.
163      * </p>
164      */
165     public static final int PLAYBACK_STATE_ERROR = 7;
166 
167     /**
168      * Integer extra: HTTP status code.
169      * <p>
170      * Specifies the HTTP status code that was encountered when the content
171      * was requested after all redirects were followed.  This key only needs to
172      * specified when the content uri uses the HTTP or HTTPS scheme and an error
173      * occurred.  This key may be omitted if the content was able to be played
174      * successfully; there is no need to report a 200 (OK) status code.
175      * </p><p>
176      * The value is an integer HTTP status code, such as 401 (Unauthorized),
177      * 404 (Not Found), or 500 (Server Error), or 0 if none.
178      * </p>
179      */
180     public static final String EXTRA_HTTP_STATUS_CODE =
181             "android.media.status.extra.HTTP_STATUS_CODE";
182 
183     /**
184      * Bundle extra: HTTP response headers.
185      * <p>
186      * Specifies the HTTP response headers that were returned when the content was
187      * requested from the network.  The headers may include additional information
188      * about the content or any errors conditions that were encountered while
189      * trying to fetch the content.
190      * </p><p>
191      * The value is a {@link android.os.Bundle} of string based key-value pairs
192      * that describe the HTTP response headers.
193      * </p>
194      */
195     public static final String EXTRA_HTTP_RESPONSE_HEADERS =
196             "android.media.status.extra.HTTP_RESPONSE_HEADERS";
197 
MediaItemStatus(Bundle bundle)198     MediaItemStatus(Bundle bundle) {
199         mBundle = bundle;
200     }
201 
202     /**
203      * Gets the timestamp associated with the status information in
204      * milliseconds since boot in the {@link SystemClock#elapsedRealtime} time base.
205      *
206      * @return The status timestamp in the {@link SystemClock#elapsedRealtime()} time base.
207      */
getTimestamp()208     public long getTimestamp() {
209         return mBundle.getLong(KEY_TIMESTAMP);
210     }
211 
212     /**
213      * Gets the playback state of the media item.
214      *
215      * @return The playback state.  One of {@link #PLAYBACK_STATE_PENDING},
216      * {@link #PLAYBACK_STATE_PLAYING}, {@link #PLAYBACK_STATE_PAUSED},
217      * {@link #PLAYBACK_STATE_BUFFERING}, {@link #PLAYBACK_STATE_FINISHED},
218      * {@link #PLAYBACK_STATE_CANCELED}, {@link #PLAYBACK_STATE_INVALIDATED},
219      * or {@link #PLAYBACK_STATE_ERROR}.
220      */
getPlaybackState()221     public int getPlaybackState() {
222         return mBundle.getInt(KEY_PLAYBACK_STATE, PLAYBACK_STATE_ERROR);
223     }
224 
225     /**
226      * Gets the content playback position as a long integer number of milliseconds
227      * from the beginning of the content.
228      *
229      * @return The content playback position in milliseconds, or -1 if unknown.
230      */
getContentPosition()231     public long getContentPosition() {
232         return mBundle.getLong(KEY_CONTENT_POSITION, -1);
233     }
234 
235     /**
236      * Gets the total duration of the content to be played as a long integer number of
237      * milliseconds.
238      *
239      * @return The content duration in milliseconds, or -1 if unknown.
240      */
getContentDuration()241     public long getContentDuration() {
242         return mBundle.getLong(KEY_CONTENT_DURATION, -1);
243     }
244 
245     /**
246      * Gets a bundle of extras for this status object.
247      * The extras will be ignored by the media router but they may be used
248      * by applications.
249      */
getExtras()250     public Bundle getExtras() {
251         return mBundle.getBundle(KEY_EXTRAS);
252     }
253 
254     @Override
toString()255     public String toString() {
256         StringBuilder result = new StringBuilder();
257         result.append("MediaItemStatus{ ");
258         result.append("timestamp=");
259         TimeUtils.formatDuration(SystemClock.elapsedRealtime() - getTimestamp(), result);
260         result.append(" ms ago");
261         result.append(", playbackState=").append(playbackStateToString(getPlaybackState()));
262         result.append(", contentPosition=").append(getContentPosition());
263         result.append(", contentDuration=").append(getContentDuration());
264         result.append(", extras=").append(getExtras());
265         result.append(" }");
266         return result.toString();
267     }
268 
playbackStateToString(int playbackState)269     private static String playbackStateToString(int playbackState) {
270         switch (playbackState) {
271             case PLAYBACK_STATE_PENDING:
272                 return "pending";
273             case PLAYBACK_STATE_BUFFERING:
274                 return "buffering";
275             case PLAYBACK_STATE_PLAYING:
276                 return "playing";
277             case PLAYBACK_STATE_PAUSED:
278                 return "paused";
279             case PLAYBACK_STATE_FINISHED:
280                 return "finished";
281             case PLAYBACK_STATE_CANCELED:
282                 return "canceled";
283             case PLAYBACK_STATE_INVALIDATED:
284                 return "invalidated";
285             case PLAYBACK_STATE_ERROR:
286                 return "error";
287         }
288         return Integer.toString(playbackState);
289     }
290 
291     /**
292      * Converts this object to a bundle for serialization.
293      *
294      * @return The contents of the object represented as a bundle.
295      */
asBundle()296     public Bundle asBundle() {
297         return mBundle;
298     }
299 
300     /**
301      * Creates an instance from a bundle.
302      *
303      * @param bundle The bundle, or null if none.
304      * @return The new instance, or null if the bundle was null.
305      */
fromBundle(Bundle bundle)306     public static MediaItemStatus fromBundle(Bundle bundle) {
307         return bundle != null ? new MediaItemStatus(bundle) : null;
308     }
309 
310     /**
311      * Builder for {@link MediaItemStatus media item status objects}.
312      */
313     public static final class Builder {
314         private final Bundle mBundle;
315 
316         /**
317          * Creates a media item status builder using the current time as the
318          * reference timestamp.
319          *
320          * @param playbackState The item playback state.
321          */
Builder(int playbackState)322         public Builder(int playbackState) {
323             mBundle = new Bundle();
324             setTimestamp(SystemClock.elapsedRealtime());
325             setPlaybackState(playbackState);
326         }
327 
328         /**
329          * Creates a media item status builder whose initial contents are
330          * copied from an existing status.
331          */
Builder(MediaItemStatus status)332         public Builder(MediaItemStatus status) {
333             if (status == null) {
334                 throw new IllegalArgumentException("status must not be null");
335             }
336 
337             mBundle = new Bundle(status.mBundle);
338         }
339 
340         /**
341          * Sets the timestamp associated with the status information in
342          * milliseconds since boot in the {@link SystemClock#elapsedRealtime} time base.
343          */
setTimestamp(long elapsedRealtimeTimestamp)344         public Builder setTimestamp(long elapsedRealtimeTimestamp) {
345             mBundle.putLong(KEY_TIMESTAMP, elapsedRealtimeTimestamp);
346             return this;
347         }
348 
349         /**
350          * Sets the playback state of the media item.
351          */
setPlaybackState(int playbackState)352         public Builder setPlaybackState(int playbackState) {
353             mBundle.putInt(KEY_PLAYBACK_STATE, playbackState);
354             return this;
355         }
356 
357         /**
358          * Sets the content playback position as a long integer number of milliseconds
359          * from the beginning of the content.
360          */
setContentPosition(long positionMilliseconds)361         public Builder setContentPosition(long positionMilliseconds) {
362             mBundle.putLong(KEY_CONTENT_POSITION, positionMilliseconds);
363             return this;
364         }
365 
366         /**
367          * Sets the total duration of the content to be played as a long integer number
368          * of milliseconds.
369          */
setContentDuration(long durationMilliseconds)370         public Builder setContentDuration(long durationMilliseconds) {
371             mBundle.putLong(KEY_CONTENT_DURATION, durationMilliseconds);
372             return this;
373         }
374 
375         /**
376          * Sets a bundle of extras for this status object.
377          * The extras will be ignored by the media router but they may be used
378          * by applications.
379          */
setExtras(Bundle extras)380         public Builder setExtras(Bundle extras) {
381             mBundle.putBundle(KEY_EXTRAS, extras);
382             return this;
383         }
384 
385         /**
386          * Builds the {@link MediaItemStatus media item status object}.
387          */
build()388         public MediaItemStatus build() {
389             return new MediaItemStatus(mBundle);
390         }
391     }
392 }
393