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