1/* 2 * Copyright (C) 2014 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 */ 16package android.support.v4.media.session; 17 18import android.os.Build; 19import android.os.Bundle; 20import android.os.Parcel; 21import android.os.Parcelable; 22import android.os.SystemClock; 23import android.text.TextUtils; 24 25/** 26 * Playback state for a {@link MediaSessionCompat}. This includes a state like 27 * {@link PlaybackStateCompat#STATE_PLAYING}, the current playback position, 28 * and the current control capabilities. 29 */ 30public final class PlaybackStateCompat implements Parcelable { 31 32 /** 33 * Indicates this session supports the stop command. 34 * 35 * @see Builder#setActions(long) 36 */ 37 public static final long ACTION_STOP = 1 << 0; 38 39 /** 40 * Indicates this session supports the pause command. 41 * 42 * @see Builder#setActions(long) 43 */ 44 public static final long ACTION_PAUSE = 1 << 1; 45 46 /** 47 * Indicates this session supports the play command. 48 * 49 * @see Builder#setActions(long) 50 */ 51 public static final long ACTION_PLAY = 1 << 2; 52 53 /** 54 * Indicates this session supports the rewind command. 55 * 56 * @see Builder#setActions(long) 57 */ 58 public static final long ACTION_REWIND = 1 << 3; 59 60 /** 61 * Indicates this session supports the previous command. 62 * 63 * @see Builder#setActions(long) 64 */ 65 public static final long ACTION_SKIP_TO_PREVIOUS = 1 << 4; 66 67 /** 68 * Indicates this session supports the next command. 69 * 70 * @see Builder#setActions(long) 71 */ 72 public static final long ACTION_SKIP_TO_NEXT = 1 << 5; 73 74 /** 75 * Indicates this session supports the fast forward command. 76 * 77 * @see Builder#setActions(long) 78 */ 79 public static final long ACTION_FAST_FORWARD = 1 << 6; 80 81 /** 82 * Indicates this session supports the set rating command. 83 * 84 * @see Builder#setActions(long) 85 */ 86 public static final long ACTION_SET_RATING = 1 << 7; 87 88 /** 89 * Indicates this session supports the seek to command. 90 * 91 * @see Builder#setActions(long) 92 */ 93 public static final long ACTION_SEEK_TO = 1 << 8; 94 95 /** 96 * Indicates this session supports the play/pause toggle command. 97 * 98 * @see Builder#setActions(long) 99 */ 100 public static final long ACTION_PLAY_PAUSE = 1 << 9; 101 102 /** 103 * Indicates this session supports the play from media id command. 104 * 105 * @see Builder#setActions(long) 106 */ 107 public static final long ACTION_PLAY_FROM_MEDIA_ID = 1 << 10; 108 109 /** 110 * Indicates this session supports the play from search command. 111 * 112 * @see Builder#setActions(long) 113 */ 114 public static final long ACTION_PLAY_FROM_SEARCH = 1 << 11; 115 116 /** 117 * Indicates this session supports the skip to queue item command. 118 * 119 * @see Builder#setActions(long) 120 */ 121 public static final long ACTION_SKIP_TO_QUEUE_ITEM = 1 << 12; 122 123 /** 124 * This is the default playback state and indicates that no media has been 125 * added yet, or the performer has been reset and has no content to play. 126 * 127 * @see Builder#setState 128 */ 129 public final static int STATE_NONE = 0; 130 131 /** 132 * State indicating this item is currently stopped. 133 * 134 * @see Builder#setState 135 */ 136 public final static int STATE_STOPPED = 1; 137 138 /** 139 * State indicating this item is currently paused. 140 * 141 * @see Builder#setState 142 */ 143 public final static int STATE_PAUSED = 2; 144 145 /** 146 * State indicating this item is currently playing. 147 * 148 * @see Builder#setState 149 */ 150 public final static int STATE_PLAYING = 3; 151 152 /** 153 * State indicating this item is currently fast forwarding. 154 * 155 * @see Builder#setState 156 */ 157 public final static int STATE_FAST_FORWARDING = 4; 158 159 /** 160 * State indicating this item is currently rewinding. 161 * 162 * @see Builder#setState 163 */ 164 public final static int STATE_REWINDING = 5; 165 166 /** 167 * State indicating this item is currently buffering and will begin playing 168 * when enough data has buffered. 169 * 170 * @see Builder#setState 171 */ 172 public final static int STATE_BUFFERING = 6; 173 174 /** 175 * State indicating this item is currently in an error state. The error 176 * message should also be set when entering this state. 177 * 178 * @see Builder#setState 179 */ 180 public final static int STATE_ERROR = 7; 181 182 /** 183 * State indicating the class doing playback is currently connecting to a 184 * route. Depending on the implementation you may return to the previous 185 * state when the connection finishes or enter {@link #STATE_NONE}. If 186 * the connection failed {@link #STATE_ERROR} should be used. 187 * @hide 188 */ 189 public final static int STATE_CONNECTING = 8; 190 191 /** 192 * State indicating the player is currently skipping to the previous item. 193 * 194 * @see Builder#setState 195 */ 196 public final static int STATE_SKIPPING_TO_PREVIOUS = 9; 197 198 /** 199 * State indicating the player is currently skipping to the next item. 200 * 201 * @see Builder#setState 202 */ 203 public final static int STATE_SKIPPING_TO_NEXT = 10; 204 205 /** 206 * Use this value for the position to indicate the position is not known. 207 */ 208 public final static long PLAYBACK_POSITION_UNKNOWN = -1; 209 210 private final int mState; 211 private final long mPosition; 212 private final long mBufferedPosition; 213 private final float mSpeed; 214 private final long mActions; 215 private final CharSequence mErrorMessage; 216 private final long mUpdateTime; 217 218 private Object mStateObj; 219 220 private PlaybackStateCompat(int state, long position, long bufferedPosition, 221 float rate, long actions, CharSequence errorMessage, long updateTime) { 222 mState = state; 223 mPosition = position; 224 mBufferedPosition = bufferedPosition; 225 mSpeed = rate; 226 mActions = actions; 227 mErrorMessage = errorMessage; 228 mUpdateTime = updateTime; 229 } 230 231 private PlaybackStateCompat(Parcel in) { 232 mState = in.readInt(); 233 mPosition = in.readLong(); 234 mSpeed = in.readFloat(); 235 mUpdateTime = in.readLong(); 236 mBufferedPosition = in.readLong(); 237 mActions = in.readLong(); 238 mErrorMessage = TextUtils.CHAR_SEQUENCE_CREATOR.createFromParcel(in); 239 } 240 241 @Override 242 public String toString() { 243 StringBuilder bob = new StringBuilder("PlaybackState {"); 244 bob.append("state=").append(mState); 245 bob.append(", position=").append(mPosition); 246 bob.append(", buffered position=").append(mBufferedPosition); 247 bob.append(", speed=").append(mSpeed); 248 bob.append(", updated=").append(mUpdateTime); 249 bob.append(", actions=").append(mActions); 250 bob.append(", error=").append(mErrorMessage); 251 bob.append("}"); 252 return bob.toString(); 253 } 254 255 @Override 256 public int describeContents() { 257 return 0; 258 } 259 260 @Override 261 public void writeToParcel(Parcel dest, int flags) { 262 dest.writeInt(mState); 263 dest.writeLong(mPosition); 264 dest.writeFloat(mSpeed); 265 dest.writeLong(mUpdateTime); 266 dest.writeLong(mBufferedPosition); 267 dest.writeLong(mActions); 268 TextUtils.writeToParcel(mErrorMessage, dest, flags); 269 } 270 271 /** 272 * Get the current state of playback. One of the following: 273 * <ul> 274 * <li> {@link PlaybackStateCompat#STATE_NONE}</li> 275 * <li> {@link PlaybackStateCompat#STATE_STOPPED}</li> 276 * <li> {@link PlaybackStateCompat#STATE_PLAYING}</li> 277 * <li> {@link PlaybackStateCompat#STATE_PAUSED}</li> 278 * <li> {@link PlaybackStateCompat#STATE_FAST_FORWARDING}</li> 279 * <li> {@link PlaybackStateCompat#STATE_REWINDING}</li> 280 * <li> {@link PlaybackStateCompat#STATE_BUFFERING}</li> 281 * <li> {@link PlaybackStateCompat#STATE_ERROR}</li> 282 */ 283 public int getState() { 284 return mState; 285 } 286 287 /** 288 * Get the current playback position in ms. 289 */ 290 public long getPosition() { 291 return mPosition; 292 } 293 294 /** 295 * Get the current buffered position in ms. This is the farthest playback 296 * point that can be reached from the current position using only buffered 297 * content. 298 */ 299 public long getBufferedPosition() { 300 return mBufferedPosition; 301 } 302 303 /** 304 * Get the current playback speed as a multiple of normal playback. This 305 * should be negative when rewinding. A value of 1 means normal playback and 306 * 0 means paused. 307 * 308 * @return The current speed of playback. 309 */ 310 public float getPlaybackSpeed() { 311 return mSpeed; 312 } 313 314 /** 315 * Get the current actions available on this session. This should use a 316 * bitmask of the available actions. 317 * <ul> 318 * <li> {@link PlaybackStateCompat#ACTION_SKIP_TO_PREVIOUS}</li> 319 * <li> {@link PlaybackStateCompat#ACTION_REWIND}</li> 320 * <li> {@link PlaybackStateCompat#ACTION_PLAY}</li> 321 * <li> {@link PlaybackStateCompat#ACTION_PAUSE}</li> 322 * <li> {@link PlaybackStateCompat#ACTION_STOP}</li> 323 * <li> {@link PlaybackStateCompat#ACTION_FAST_FORWARD}</li> 324 * <li> {@link PlaybackStateCompat#ACTION_SKIP_TO_NEXT}</li> 325 * <li> {@link PlaybackStateCompat#ACTION_SEEK_TO}</li> 326 * <li> {@link PlaybackStateCompat#ACTION_SET_RATING}</li> 327 * </ul> 328 */ 329 public long getActions() { 330 return mActions; 331 } 332 333 /** 334 * Get a user readable error message. This should be set when the state is 335 * {@link PlaybackStateCompat#STATE_ERROR}. 336 */ 337 public CharSequence getErrorMessage() { 338 return mErrorMessage; 339 } 340 341 /** 342 * Get the elapsed real time at which position was last updated. If the 343 * position has never been set this will return 0; 344 * 345 * @return The last time the position was updated. 346 */ 347 public long getLastPositionUpdateTime() { 348 return mUpdateTime; 349 } 350 351 /** 352 * Creates an instance from a framework {@link android.media.session.PlaybackState} object. 353 * <p> 354 * This method is only supported on API 21+. 355 * </p> 356 * 357 * @param stateObj A {@link android.media.session.PlaybackState} object, or null if none. 358 * @return An equivalent {@link PlaybackStateCompat} object, or null if none. 359 */ 360 public static PlaybackStateCompat fromPlaybackState(Object stateObj) { 361 if (stateObj == null || Build.VERSION.SDK_INT < 21) { 362 return null; 363 } 364 365 PlaybackStateCompat state = new PlaybackStateCompat( 366 PlaybackStateCompatApi21.getState(stateObj), 367 PlaybackStateCompatApi21.getPosition(stateObj), 368 PlaybackStateCompatApi21.getBufferedPosition(stateObj), 369 PlaybackStateCompatApi21.getPlaybackSpeed(stateObj), 370 PlaybackStateCompatApi21.getActions(stateObj), 371 PlaybackStateCompatApi21.getErrorMessage(stateObj), 372 PlaybackStateCompatApi21.getLastPositionUpdateTime(stateObj)); 373 state.mStateObj = stateObj; 374 return state; 375 } 376 377 /** 378 * Gets the underlying framework {@link android.media.session.PlaybackState} object. 379 * <p> 380 * This method is only supported on API 21+. 381 * </p> 382 * 383 * @return An equivalent {@link android.media.session.PlaybackState} object, or null if none. 384 */ 385 public Object getPlaybackState() { 386 if (mStateObj != null || Build.VERSION.SDK_INT < 21) { 387 return mStateObj; 388 } 389 390 mStateObj = PlaybackStateCompatApi21.newInstance(mState, mPosition, mBufferedPosition, 391 mSpeed, mActions, mErrorMessage, mUpdateTime); 392 return mStateObj; 393 } 394 395 public static final Parcelable.Creator<PlaybackStateCompat> CREATOR = 396 new Parcelable.Creator<PlaybackStateCompat>() { 397 @Override 398 public PlaybackStateCompat createFromParcel(Parcel in) { 399 return new PlaybackStateCompat(in); 400 } 401 402 @Override 403 public PlaybackStateCompat[] newArray(int size) { 404 return new PlaybackStateCompat[size]; 405 } 406 }; 407 408 /** 409 * {@link PlaybackStateCompat.CustomAction CustomActions} can be used to 410 * extend the capabilities of the standard transport controls by exposing 411 * app specific actions to {@link MediaControllerCompat Controllers}. 412 */ 413 public static final class CustomAction implements Parcelable { 414 private final String mAction; 415 private final CharSequence mName; 416 private final int mIcon; 417 private final Bundle mExtras; 418 419 /** 420 * Use {@link PlaybackStateCompat.CustomAction.Builder#build()}. 421 */ 422 private CustomAction(String action, CharSequence name, int icon, Bundle extras) { 423 mAction = action; 424 mName = name; 425 mIcon = icon; 426 mExtras = extras; 427 } 428 429 private CustomAction(Parcel in) { 430 mAction = in.readString(); 431 mName = TextUtils.CHAR_SEQUENCE_CREATOR.createFromParcel(in); 432 mIcon = in.readInt(); 433 mExtras = in.readBundle(); 434 } 435 436 @Override 437 public void writeToParcel(Parcel dest, int flags) { 438 dest.writeString(mAction); 439 TextUtils.writeToParcel(mName, dest, flags); 440 dest.writeInt(mIcon); 441 dest.writeBundle(mExtras); 442 } 443 444 @Override 445 public int describeContents() { 446 return 0; 447 } 448 449 public static final Parcelable.Creator<PlaybackStateCompat.CustomAction> CREATOR 450 = new Parcelable.Creator<PlaybackStateCompat.CustomAction>() { 451 452 @Override 453 public PlaybackStateCompat.CustomAction createFromParcel(Parcel p) { 454 return new PlaybackStateCompat.CustomAction(p); 455 } 456 457 @Override 458 public PlaybackStateCompat.CustomAction[] newArray(int size) { 459 return new PlaybackStateCompat.CustomAction[size]; 460 } 461 }; 462 463 /** 464 * Returns the action of the {@link CustomAction}. 465 * 466 * @return The action of the {@link CustomAction}. 467 */ 468 public String getAction() { 469 return mAction; 470 } 471 472 /** 473 * Returns the display name of this action. e.g. "Favorite" 474 * 475 * @return The display name of this {@link CustomAction}. 476 */ 477 public CharSequence getName() { 478 return mName; 479 } 480 481 /** 482 * Returns the resource id of the icon in the {@link MediaSessionCompat 483 * Session's} package. 484 * 485 * @return The resource id of the icon in the {@link MediaSessionCompat 486 * Session's} package. 487 */ 488 public int getIcon() { 489 return mIcon; 490 } 491 492 /** 493 * Returns extras which provide additional application-specific 494 * information about the action, or null if none. These arguments are 495 * meant to be consumed by a {@link MediaControllerCompat} if it knows 496 * how to handle them. 497 * 498 * @return Optional arguments for the {@link CustomAction}. 499 */ 500 public Bundle getExtras() { 501 return mExtras; 502 } 503 504 @Override 505 public String toString() { 506 return "Action:" + 507 "mName='" + mName + 508 ", mIcon=" + mIcon + 509 ", mExtras=" + mExtras; 510 } 511 512 /** 513 * Builder for {@link CustomAction} objects. 514 */ 515 public static final class Builder { 516 private final String mAction; 517 private final CharSequence mName; 518 private final int mIcon; 519 private Bundle mExtras; 520 521 /** 522 * Creates a {@link CustomAction} builder with the id, name, and 523 * icon set. 524 * 525 * @param action The action of the {@link CustomAction}. 526 * @param name The display name of the {@link CustomAction}. This 527 * name will be displayed along side the action if the UI 528 * supports it. 529 * @param icon The icon resource id of the {@link CustomAction}. 530 * This resource id must be in the same package as the 531 * {@link MediaSessionCompat}. It will be displayed with 532 * the custom action if the UI supports it. 533 */ 534 public Builder(String action, CharSequence name, int icon) { 535 if (TextUtils.isEmpty(action)) { 536 throw new IllegalArgumentException( 537 "You must specify an action to build a CustomAction."); 538 } 539 if (TextUtils.isEmpty(name)) { 540 throw new IllegalArgumentException( 541 "You must specify a name to build a CustomAction."); 542 } 543 if (icon == 0) { 544 throw new IllegalArgumentException( 545 "You must specify an icon resource id to build a CustomAction."); 546 } 547 mAction = action; 548 mName = name; 549 mIcon = icon; 550 } 551 552 /** 553 * Set optional extras for the {@link CustomAction}. These extras 554 * are meant to be consumed by a {@link MediaControllerCompat} if it 555 * knows how to handle them. Keys should be fully qualified (e.g. 556 * "com.example.MY_ARG") to avoid collisions. 557 * 558 * @param extras Optional extras for the {@link CustomAction}. 559 * @return this. 560 */ 561 public Builder setExtras(Bundle extras) { 562 mExtras = extras; 563 return this; 564 } 565 566 /** 567 * Build and return the {@link CustomAction} instance with the 568 * specified values. 569 * 570 * @return A new {@link CustomAction} instance. 571 */ 572 public CustomAction build() { 573 return new CustomAction(mAction, mName, mIcon, mExtras); 574 } 575 } 576 } 577 578 /** 579 * Builder for {@link PlaybackStateCompat} objects. 580 */ 581 public static final class Builder { 582 private int mState; 583 private long mPosition; 584 private long mBufferedPosition; 585 private float mRate; 586 private long mActions; 587 private CharSequence mErrorMessage; 588 private long mUpdateTime; 589 590 /** 591 * Create an empty Builder. 592 */ 593 public Builder() { 594 } 595 596 /** 597 * Create a Builder using a {@link PlaybackStateCompat} instance to set the 598 * initial values. 599 * 600 * @param source The playback state to copy. 601 */ 602 public Builder(PlaybackStateCompat source) { 603 mState = source.mState; 604 mPosition = source.mPosition; 605 mRate = source.mSpeed; 606 mUpdateTime = source.mUpdateTime; 607 mBufferedPosition = source.mBufferedPosition; 608 mActions = source.mActions; 609 mErrorMessage = source.mErrorMessage; 610 } 611 612 /** 613 * Set the current state of playback. 614 * <p> 615 * The position must be in ms and indicates the current playback 616 * position within the track. If the position is unknown use 617 * {@link #PLAYBACK_POSITION_UNKNOWN}. 618 * <p> 619 * The rate is a multiple of normal playback and should be 0 when paused 620 * and negative when rewinding. Normal playback rate is 1.0. 621 * <p> 622 * The state must be one of the following: 623 * <ul> 624 * <li> {@link PlaybackStateCompat#STATE_NONE}</li> 625 * <li> {@link PlaybackStateCompat#STATE_STOPPED}</li> 626 * <li> {@link PlaybackStateCompat#STATE_PLAYING}</li> 627 * <li> {@link PlaybackStateCompat#STATE_PAUSED}</li> 628 * <li> {@link PlaybackStateCompat#STATE_FAST_FORWARDING}</li> 629 * <li> {@link PlaybackStateCompat#STATE_REWINDING}</li> 630 * <li> {@link PlaybackStateCompat#STATE_BUFFERING}</li> 631 * <li> {@link PlaybackStateCompat#STATE_ERROR}</li> 632 * </ul> 633 * 634 * @param state The current state of playback. 635 * @param position The position in the current track in ms. 636 * @param playbackSpeed The current rate of playback as a multiple of 637 * normal playback. 638 */ 639 public Builder setState(int state, long position, float playbackSpeed) { 640 return setState(state, position, playbackSpeed, SystemClock.elapsedRealtime()); 641 } 642 643 /** 644 * Set the current state of playback. 645 * <p> 646 * The position must be in ms and indicates the current playback 647 * position within the track. If the position is unknown use 648 * {@link #PLAYBACK_POSITION_UNKNOWN}. 649 * <p> 650 * The rate is a multiple of normal playback and should be 0 when paused 651 * and negative when rewinding. Normal playback rate is 1.0. 652 * <p> 653 * The state must be one of the following: 654 * <ul> 655 * <li> {@link PlaybackStateCompat#STATE_NONE}</li> 656 * <li> {@link PlaybackStateCompat#STATE_STOPPED}</li> 657 * <li> {@link PlaybackStateCompat#STATE_PLAYING}</li> 658 * <li> {@link PlaybackStateCompat#STATE_PAUSED}</li> 659 * <li> {@link PlaybackStateCompat#STATE_FAST_FORWARDING}</li> 660 * <li> {@link PlaybackStateCompat#STATE_REWINDING}</li> 661 * <li> {@link PlaybackStateCompat#STATE_BUFFERING}</li> 662 * <li> {@link PlaybackStateCompat#STATE_ERROR}</li> 663 * </ul> 664 * 665 * @param state The current state of playback. 666 * @param position The position in the current item in ms. 667 * @param playbackSpeed The current speed of playback as a multiple of 668 * normal playback. 669 * @param updateTime The time in the {@link SystemClock#elapsedRealtime} 670 * timebase that the position was updated at. 671 * @return this 672 */ 673 public Builder setState(int state, long position, float playbackSpeed, long updateTime) { 674 mState = state; 675 mPosition = position; 676 mUpdateTime = updateTime; 677 mRate = playbackSpeed; 678 return this; 679 } 680 681 /** 682 * Set the current buffered position in ms. This is the farthest 683 * playback point that can be reached from the current position using 684 * only buffered content. 685 * 686 * @return this 687 */ 688 public Builder setBufferedPosition(long bufferPosition) { 689 mBufferedPosition = bufferPosition; 690 return this; 691 } 692 693 /** 694 * Set the current capabilities available on this session. This should 695 * use a bitmask of the available capabilities. 696 * <ul> 697 * <li> {@link PlaybackStateCompat#ACTION_SKIP_TO_PREVIOUS}</li> 698 * <li> {@link PlaybackStateCompat#ACTION_REWIND}</li> 699 * <li> {@link PlaybackStateCompat#ACTION_PLAY}</li> 700 * <li> {@link PlaybackStateCompat#ACTION_PAUSE}</li> 701 * <li> {@link PlaybackStateCompat#ACTION_STOP}</li> 702 * <li> {@link PlaybackStateCompat#ACTION_FAST_FORWARD}</li> 703 * <li> {@link PlaybackStateCompat#ACTION_SKIP_TO_NEXT}</li> 704 * <li> {@link PlaybackStateCompat#ACTION_SEEK_TO}</li> 705 * <li> {@link PlaybackStateCompat#ACTION_SET_RATING}</li> 706 * </ul> 707 * 708 * @return this 709 */ 710 public Builder setActions(long capabilities) { 711 mActions = capabilities; 712 return this; 713 } 714 715 /** 716 * Set a user readable error message. This should be set when the state 717 * is {@link PlaybackStateCompat#STATE_ERROR}. 718 * 719 * @return this 720 */ 721 public Builder setErrorMessage(CharSequence errorMessage) { 722 mErrorMessage = errorMessage; 723 return this; 724 } 725 726 /** 727 * Creates the playback state object. 728 */ 729 public PlaybackStateCompat build() { 730 return new PlaybackStateCompat(mState, mPosition, mBufferedPosition, 731 mRate, mActions, mErrorMessage, mUpdateTime); 732 } 733 } 734} 735