Home | History | Annotate | Download | only in media
      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     private static final String KEY_TIMESTAMP = "timestamp";
     62     private static final String KEY_PLAYBACK_STATE = "playbackState";
     63     private static final String KEY_CONTENT_POSITION = "contentPosition";
     64     private static final String KEY_CONTENT_DURATION = "contentDuration";
     65     private static final String KEY_EXTRAS = "extras";
     66 
     67     private 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 
    198     private 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      */
    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      */
    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      */
    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      */
    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      */
    250     public Bundle getExtras() {
    251         return mBundle.getBundle(KEY_EXTRAS);
    252     }
    253 
    254     @Override
    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 
    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      */
    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      */
    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          */
    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          */
    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          */
    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          */
    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          */
    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          */
    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          */
    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          */
    388         public MediaItemStatus build() {
    389             return new MediaItemStatus(mBundle);
    390         }
    391     }
    392 }
    393