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