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 */ 16 package android.media.session; 17 18 import static com.android.media.flags.Flags.FLAG_ENABLE_NOTIFYING_ACTIVITY_MANAGER_WITH_MEDIA_SESSION_STATUS_CHANGE; 19 20 import android.annotation.DrawableRes; 21 import android.annotation.FlaggedApi; 22 import android.annotation.IntDef; 23 import android.annotation.LongDef; 24 import android.annotation.Nullable; 25 import android.os.Bundle; 26 import android.os.Parcel; 27 import android.os.Parcelable; 28 import android.os.SystemClock; 29 import android.text.TextUtils; 30 31 import java.lang.annotation.Retention; 32 import java.lang.annotation.RetentionPolicy; 33 import java.util.ArrayList; 34 import java.util.List; 35 36 /** 37 * Playback state for a {@link MediaSession}. This includes a state like 38 * {@link PlaybackState#STATE_PLAYING}, the current playback position, 39 * and the current control capabilities. 40 */ 41 public final class PlaybackState implements Parcelable { 42 private static final String TAG = "PlaybackState"; 43 44 /** 45 * @hide 46 */ 47 @LongDef(flag = true, value = {ACTION_STOP, ACTION_PAUSE, ACTION_PLAY, ACTION_REWIND, 48 ACTION_SKIP_TO_PREVIOUS, ACTION_SKIP_TO_NEXT, ACTION_FAST_FORWARD, ACTION_SET_RATING, 49 ACTION_SEEK_TO, ACTION_PLAY_PAUSE, ACTION_PLAY_FROM_MEDIA_ID, ACTION_PLAY_FROM_SEARCH, 50 ACTION_SKIP_TO_QUEUE_ITEM, ACTION_PLAY_FROM_URI, ACTION_PREPARE, 51 ACTION_PREPARE_FROM_MEDIA_ID, ACTION_PREPARE_FROM_SEARCH, ACTION_PREPARE_FROM_URI, 52 ACTION_SET_PLAYBACK_SPEED}) 53 @Retention(RetentionPolicy.SOURCE) 54 public @interface Actions {} 55 56 /** 57 * Indicates this session supports the stop command. 58 * 59 * @see Builder#setActions(long) 60 */ 61 public static final long ACTION_STOP = 1 << 0; 62 63 /** 64 * Indicates this session supports the pause command. 65 * 66 * @see Builder#setActions(long) 67 */ 68 public static final long ACTION_PAUSE = 1 << 1; 69 70 /** 71 * Indicates this session supports the play command. 72 * 73 * @see Builder#setActions(long) 74 */ 75 public static final long ACTION_PLAY = 1 << 2; 76 77 /** 78 * Indicates this session supports the rewind command. 79 * 80 * @see Builder#setActions(long) 81 */ 82 public static final long ACTION_REWIND = 1 << 3; 83 84 /** 85 * Indicates this session supports the previous command. 86 * 87 * @see Builder#setActions(long) 88 */ 89 public static final long ACTION_SKIP_TO_PREVIOUS = 1 << 4; 90 91 /** 92 * Indicates this session supports the next command. 93 * 94 * @see Builder#setActions(long) 95 */ 96 public static final long ACTION_SKIP_TO_NEXT = 1 << 5; 97 98 /** 99 * Indicates this session supports the fast forward command. 100 * 101 * @see Builder#setActions(long) 102 */ 103 public static final long ACTION_FAST_FORWARD = 1 << 6; 104 105 /** 106 * Indicates this session supports the set rating command. 107 * 108 * @see Builder#setActions(long) 109 */ 110 public static final long ACTION_SET_RATING = 1 << 7; 111 112 /** 113 * Indicates this session supports the seek to command. 114 * 115 * @see Builder#setActions(long) 116 */ 117 public static final long ACTION_SEEK_TO = 1 << 8; 118 119 /** 120 * Indicates this session supports the play/pause toggle command. 121 * 122 * @see Builder#setActions(long) 123 */ 124 public static final long ACTION_PLAY_PAUSE = 1 << 9; 125 126 /** 127 * Indicates this session supports the play from media id command. 128 * 129 * @see Builder#setActions(long) 130 */ 131 public static final long ACTION_PLAY_FROM_MEDIA_ID = 1 << 10; 132 133 /** 134 * Indicates this session supports the play from search command. 135 * 136 * @see Builder#setActions(long) 137 */ 138 public static final long ACTION_PLAY_FROM_SEARCH = 1 << 11; 139 140 /** 141 * Indicates this session supports the skip to queue item command. 142 * 143 * @see Builder#setActions(long) 144 */ 145 public static final long ACTION_SKIP_TO_QUEUE_ITEM = 1 << 12; 146 147 /** 148 * Indicates this session supports the play from URI command. 149 * 150 * @see Builder#setActions(long) 151 */ 152 public static final long ACTION_PLAY_FROM_URI = 1 << 13; 153 154 /** 155 * Indicates this session supports the prepare command. 156 * 157 * @see Builder#setActions(long) 158 */ 159 public static final long ACTION_PREPARE = 1 << 14; 160 161 /** 162 * Indicates this session supports the prepare from media id command. 163 * 164 * @see Builder#setActions(long) 165 */ 166 public static final long ACTION_PREPARE_FROM_MEDIA_ID = 1 << 15; 167 168 /** 169 * Indicates this session supports the prepare from search command. 170 * 171 * @see Builder#setActions(long) 172 */ 173 public static final long ACTION_PREPARE_FROM_SEARCH = 1 << 16; 174 175 /** 176 * Indicates this session supports the prepare from URI command. 177 * 178 * @see Builder#setActions(long) 179 */ 180 public static final long ACTION_PREPARE_FROM_URI = 1 << 17; 181 182 // Note: The value jumps from 1 << 17 to 1 << 22 for matching same value with AndroidX. 183 /** 184 * Indicates this session supports the set playback speed command. 185 * 186 * @see Builder#setActions(long) 187 */ 188 public static final long ACTION_SET_PLAYBACK_SPEED = 1 << 22; 189 190 /** 191 * @hide 192 */ 193 @IntDef({STATE_NONE, STATE_STOPPED, STATE_PAUSED, STATE_PLAYING, STATE_FAST_FORWARDING, 194 STATE_REWINDING, STATE_BUFFERING, STATE_ERROR, STATE_CONNECTING, 195 STATE_SKIPPING_TO_PREVIOUS, STATE_SKIPPING_TO_NEXT, STATE_SKIPPING_TO_QUEUE_ITEM, 196 STATE_PLAYBACK_SUPPRESSED}) 197 @Retention(RetentionPolicy.SOURCE) 198 public @interface State {} 199 200 /** 201 * This is the default playback state and indicates that no media has been 202 * added yet, or the performer has been reset and has no content to play. 203 * 204 * @see Builder#setState(int, long, float) 205 * @see Builder#setState(int, long, float, long) 206 */ 207 public static final int STATE_NONE = 0; 208 209 /** 210 * State indicating this item is currently stopped. 211 * 212 * @see Builder#setState 213 */ 214 public static final int STATE_STOPPED = 1; 215 216 /** 217 * State indicating this item is currently paused. 218 * 219 * @see Builder#setState 220 */ 221 public static final int STATE_PAUSED = 2; 222 223 /** 224 * State indicating this item is currently playing. 225 * 226 * @see Builder#setState 227 */ 228 public static final int STATE_PLAYING = 3; 229 230 /** 231 * State indicating this item is currently fast forwarding. 232 * 233 * @see Builder#setState 234 */ 235 public static final int STATE_FAST_FORWARDING = 4; 236 237 /** 238 * State indicating this item is currently rewinding. 239 * 240 * @see Builder#setState 241 */ 242 public static final int STATE_REWINDING = 5; 243 244 /** 245 * State indicating this item is currently buffering and will begin playing 246 * when enough data has buffered. 247 * 248 * @see Builder#setState 249 */ 250 public static final int STATE_BUFFERING = 6; 251 252 /** 253 * State indicating this item is currently in an error state. The error 254 * message should also be set when entering this state. 255 * 256 * @see Builder#setState 257 */ 258 public static final int STATE_ERROR = 7; 259 260 /** 261 * State indicating the class doing playback is currently connecting to a 262 * new destination. Depending on the implementation you may return to the previous 263 * state when the connection finishes or enter {@link #STATE_NONE}. 264 * If the connection failed {@link #STATE_ERROR} should be used. 265 * 266 * @see Builder#setState 267 */ 268 public static final int STATE_CONNECTING = 8; 269 270 /** 271 * State indicating the player is currently skipping to the previous item. 272 * 273 * @see Builder#setState 274 */ 275 public static final int STATE_SKIPPING_TO_PREVIOUS = 9; 276 277 /** 278 * State indicating the player is currently skipping to the next item. 279 * 280 * @see Builder#setState 281 */ 282 public static final int STATE_SKIPPING_TO_NEXT = 10; 283 284 /** 285 * State indicating the player is currently skipping to a specific item in 286 * the queue. 287 * 288 * @see Builder#setState 289 */ 290 public static final int STATE_SKIPPING_TO_QUEUE_ITEM = 11; 291 292 /** 293 * State indicating that playback is paused due to an external transient interruption, like a 294 * phone call. 295 * 296 * <p>This state is different from {@link #STATE_PAUSED} in that it is deemed transitory, 297 * possibly allowing the service associated to the session in this state to run in the 298 * foreground. 299 * 300 * @see Builder#setState 301 */ 302 @FlaggedApi(FLAG_ENABLE_NOTIFYING_ACTIVITY_MANAGER_WITH_MEDIA_SESSION_STATUS_CHANGE) 303 public static final int STATE_PLAYBACK_SUPPRESSED = 12; 304 305 /** 306 * Use this value for the position to indicate the position is not known. 307 */ 308 public static final long PLAYBACK_POSITION_UNKNOWN = -1; 309 310 private final int mState; 311 private final long mPosition; 312 private final long mBufferedPosition; 313 private final float mSpeed; 314 private final long mActions; 315 private List<PlaybackState.CustomAction> mCustomActions; 316 private final CharSequence mErrorMessage; 317 private final long mUpdateTime; 318 private final long mActiveItemId; 319 private final Bundle mExtras; 320 PlaybackState(int state, long position, long updateTime, float speed, long bufferedPosition, long transportControls, List<PlaybackState.CustomAction> customActions, long activeItemId, CharSequence error, Bundle extras)321 private PlaybackState(int state, long position, long updateTime, float speed, 322 long bufferedPosition, long transportControls, 323 List<PlaybackState.CustomAction> customActions, long activeItemId, 324 CharSequence error, Bundle extras) { 325 mState = state; 326 mPosition = position; 327 mSpeed = speed; 328 mUpdateTime = updateTime; 329 mBufferedPosition = bufferedPosition; 330 mActions = transportControls; 331 mCustomActions = new ArrayList<>(customActions); 332 mActiveItemId = activeItemId; 333 mErrorMessage = error; 334 mExtras = extras; 335 } 336 PlaybackState(Parcel in)337 private PlaybackState(Parcel in) { 338 mState = in.readInt(); 339 mPosition = in.readLong(); 340 mSpeed = in.readFloat(); 341 mUpdateTime = in.readLong(); 342 mBufferedPosition = in.readLong(); 343 mActions = in.readLong(); 344 mCustomActions = in.createTypedArrayList(CustomAction.CREATOR); 345 mActiveItemId = in.readLong(); 346 mErrorMessage = TextUtils.CHAR_SEQUENCE_CREATOR.createFromParcel(in); 347 mExtras = in.readBundle(); 348 } 349 350 @Override toString()351 public String toString() { 352 StringBuilder bob = new StringBuilder("PlaybackState {"); 353 bob.append("state=") 354 .append(getStringForStateInt(mState)) 355 .append("(") 356 .append(mState) 357 .append(")"); 358 bob.append(", position=").append(mPosition); 359 bob.append(", buffered position=").append(mBufferedPosition); 360 bob.append(", speed=").append(mSpeed); 361 bob.append(", updated=").append(mUpdateTime); 362 bob.append(", actions=").append(mActions); 363 bob.append(", custom actions=").append(mCustomActions); 364 bob.append(", active item id=").append(mActiveItemId); 365 bob.append(", error=").append(mErrorMessage); 366 bob.append("}"); 367 return bob.toString(); 368 } 369 370 @Override describeContents()371 public int describeContents() { 372 return 0; 373 } 374 375 @Override writeToParcel(Parcel dest, int flags)376 public void writeToParcel(Parcel dest, int flags) { 377 dest.writeInt(mState); 378 dest.writeLong(mPosition); 379 dest.writeFloat(mSpeed); 380 dest.writeLong(mUpdateTime); 381 dest.writeLong(mBufferedPosition); 382 dest.writeLong(mActions); 383 dest.writeTypedList(mCustomActions); 384 dest.writeLong(mActiveItemId); 385 TextUtils.writeToParcel(mErrorMessage, dest, 0); 386 dest.writeBundle(mExtras); 387 } 388 389 /** 390 * Get the current state of playback. One of the following: 391 * <ul> 392 * <li> {@link PlaybackState#STATE_NONE}</li> 393 * <li> {@link PlaybackState#STATE_STOPPED}</li> 394 * <li> {@link PlaybackState#STATE_PLAYING}</li> 395 * <li> {@link PlaybackState#STATE_PAUSED}</li> 396 * <li> {@link PlaybackState#STATE_FAST_FORWARDING}</li> 397 * <li> {@link PlaybackState#STATE_REWINDING}</li> 398 * <li> {@link PlaybackState#STATE_BUFFERING}</li> 399 * <li> {@link PlaybackState#STATE_ERROR}</li> 400 * <li> {@link PlaybackState#STATE_CONNECTING}</li> 401 * <li> {@link PlaybackState#STATE_SKIPPING_TO_PREVIOUS}</li> 402 * <li> {@link PlaybackState#STATE_SKIPPING_TO_NEXT}</li> 403 * <li> {@link PlaybackState#STATE_SKIPPING_TO_QUEUE_ITEM}</li> 404 * <li> {@link PlaybackState#STATE_PLAYBACK_SUPPRESSED}</li> 405 * </ul> 406 */ 407 @State getState()408 public int getState() { 409 return mState; 410 } 411 412 /** 413 * Get the current playback position in ms. 414 */ getPosition()415 public long getPosition() { 416 return mPosition; 417 } 418 419 /** 420 * Get the current buffered position in ms. This is the farthest playback 421 * point that can be reached from the current position using only buffered 422 * content. 423 */ getBufferedPosition()424 public long getBufferedPosition() { 425 return mBufferedPosition; 426 } 427 428 /** 429 * Get the current playback speed as a multiple of normal playback. This 430 * should be negative when rewinding. A value of 1 means normal playback and 431 * 0 means paused. 432 * 433 * @return The current speed of playback. 434 */ getPlaybackSpeed()435 public float getPlaybackSpeed() { 436 return mSpeed; 437 } 438 439 /** 440 * Get the current actions available on this session. This should use a 441 * bitmask of the available actions. 442 * <ul> 443 * <li> {@link PlaybackState#ACTION_SKIP_TO_PREVIOUS}</li> 444 * <li> {@link PlaybackState#ACTION_REWIND}</li> 445 * <li> {@link PlaybackState#ACTION_PLAY}</li> 446 * <li> {@link PlaybackState#ACTION_PAUSE}</li> 447 * <li> {@link PlaybackState#ACTION_STOP}</li> 448 * <li> {@link PlaybackState#ACTION_FAST_FORWARD}</li> 449 * <li> {@link PlaybackState#ACTION_SKIP_TO_NEXT}</li> 450 * <li> {@link PlaybackState#ACTION_SEEK_TO}</li> 451 * <li> {@link PlaybackState#ACTION_SET_RATING}</li> 452 * <li> {@link PlaybackState#ACTION_PLAY_PAUSE}</li> 453 * <li> {@link PlaybackState#ACTION_PLAY_FROM_MEDIA_ID}</li> 454 * <li> {@link PlaybackState#ACTION_PLAY_FROM_SEARCH}</li> 455 * <li> {@link PlaybackState#ACTION_SKIP_TO_QUEUE_ITEM}</li> 456 * <li> {@link PlaybackState#ACTION_PLAY_FROM_URI}</li> 457 * <li> {@link PlaybackState#ACTION_PREPARE}</li> 458 * <li> {@link PlaybackState#ACTION_PREPARE_FROM_MEDIA_ID}</li> 459 * <li> {@link PlaybackState#ACTION_PREPARE_FROM_SEARCH}</li> 460 * <li> {@link PlaybackState#ACTION_PREPARE_FROM_URI}</li> 461 * <li> {@link PlaybackState#ACTION_SET_PLAYBACK_SPEED}</li> 462 * </ul> 463 */ 464 @Actions getActions()465 public long getActions() { 466 return mActions; 467 } 468 469 /** 470 * Get the list of custom actions. 471 */ getCustomActions()472 public List<PlaybackState.CustomAction> getCustomActions() { 473 return mCustomActions; 474 } 475 476 /** 477 * Get a user readable error message. This should be set when the state is 478 * {@link PlaybackState#STATE_ERROR}. 479 */ getErrorMessage()480 public CharSequence getErrorMessage() { 481 return mErrorMessage; 482 } 483 484 /** 485 * Get the elapsed real time at which position was last updated. If the 486 * position has never been set this will return 0; 487 * 488 * @return The last time the position was updated. 489 */ getLastPositionUpdateTime()490 public long getLastPositionUpdateTime() { 491 return mUpdateTime; 492 } 493 494 /** 495 * Get the id of the currently active item in the queue. If there is no 496 * queue or a queue is not supported by the session this will be 497 * {@link MediaSession.QueueItem#UNKNOWN_ID}. 498 * 499 * @return The id of the currently active item in the queue or 500 * {@link MediaSession.QueueItem#UNKNOWN_ID}. 501 */ getActiveQueueItemId()502 public long getActiveQueueItemId() { 503 return mActiveItemId; 504 } 505 506 /** 507 * Get any custom extras that were set on this playback state. 508 * 509 * @return The extras for this state or null. 510 */ getExtras()511 public @Nullable Bundle getExtras() { 512 return mExtras; 513 } 514 515 /** 516 * Returns whether this is considered as an active playback state. 517 * <p> 518 * The playback state is considered as an active if the state is one of the following: 519 * <ul> 520 * <li>{@link #STATE_BUFFERING}</li> 521 * <li>{@link #STATE_CONNECTING}</li> 522 * <li>{@link #STATE_FAST_FORWARDING}</li> 523 * <li>{@link #STATE_PLAYING}</li> 524 * <li>{@link #STATE_REWINDING}</li> 525 * <li>{@link #STATE_SKIPPING_TO_NEXT}</li> 526 * <li>{@link #STATE_SKIPPING_TO_PREVIOUS}</li> 527 * <li>{@link #STATE_SKIPPING_TO_QUEUE_ITEM}</li> 528 * <li>{@link #STATE_PLAYBACK_SUPPRESSED}</li> 529 * </ul> 530 */ isActive()531 public boolean isActive() { 532 switch (mState) { 533 case PlaybackState.STATE_FAST_FORWARDING: 534 case PlaybackState.STATE_REWINDING: 535 case PlaybackState.STATE_SKIPPING_TO_PREVIOUS: 536 case PlaybackState.STATE_SKIPPING_TO_NEXT: 537 case PlaybackState.STATE_SKIPPING_TO_QUEUE_ITEM: 538 case PlaybackState.STATE_BUFFERING: 539 case PlaybackState.STATE_CONNECTING: 540 case PlaybackState.STATE_PLAYING: 541 case PlaybackState.STATE_PLAYBACK_SUPPRESSED: 542 return true; 543 } 544 return false; 545 } 546 547 public static final @android.annotation.NonNull Parcelable.Creator<PlaybackState> CREATOR = 548 new Parcelable.Creator<PlaybackState>() { 549 @Override 550 public PlaybackState createFromParcel(Parcel in) { 551 return new PlaybackState(in); 552 } 553 554 @Override 555 public PlaybackState[] newArray(int size) { 556 return new PlaybackState[size]; 557 } 558 }; 559 560 /** Returns a human readable string representation of the given int {@code state} */ getStringForStateInt(int state)561 private static String getStringForStateInt(int state) { 562 switch (state) { 563 case STATE_NONE: 564 return "NONE"; 565 case STATE_STOPPED: 566 return "STOPPED"; 567 case STATE_PAUSED: 568 return "PAUSED"; 569 case STATE_PLAYING: 570 return "PLAYING"; 571 case STATE_FAST_FORWARDING: 572 return "FAST_FORWARDING"; 573 case STATE_REWINDING: 574 return "REWINDING"; 575 case STATE_BUFFERING: 576 return "BUFFERING"; 577 case STATE_ERROR: 578 return "ERROR"; 579 case STATE_CONNECTING: 580 return "CONNECTING"; 581 case STATE_SKIPPING_TO_PREVIOUS: 582 return "SKIPPING_TO_PREVIOUS"; 583 case STATE_SKIPPING_TO_NEXT: 584 return "SKIPPING_TO_NEXT"; 585 case STATE_SKIPPING_TO_QUEUE_ITEM: 586 return "SKIPPING_TO_QUEUE_ITEM"; 587 case STATE_PLAYBACK_SUPPRESSED: 588 return "STATE_PLAYBACK_SUPPRESSED"; 589 default: 590 return "UNKNOWN"; 591 } 592 } 593 594 /** 595 * {@link PlaybackState.CustomAction CustomActions} can be used to extend the capabilities of 596 * the standard transport controls by exposing app specific actions to 597 * {@link MediaController MediaControllers}. 598 */ 599 public static final class CustomAction implements Parcelable { 600 private final String mAction; 601 private final CharSequence mName; 602 private final int mIcon; 603 private final Bundle mExtras; 604 605 /** 606 * Use {@link PlaybackState.CustomAction.Builder#build()}. 607 */ CustomAction(String action, CharSequence name, int icon, Bundle extras)608 private CustomAction(String action, CharSequence name, int icon, Bundle extras) { 609 mAction = action; 610 mName = name; 611 mIcon = icon; 612 mExtras = extras; 613 } 614 CustomAction(Parcel in)615 private CustomAction(Parcel in) { 616 mAction = in.readString(); 617 mName = TextUtils.CHAR_SEQUENCE_CREATOR.createFromParcel(in); 618 mIcon = in.readInt(); 619 mExtras = in.readBundle(); 620 } 621 622 @Override writeToParcel(Parcel dest, int flags)623 public void writeToParcel(Parcel dest, int flags) { 624 dest.writeString(mAction); 625 TextUtils.writeToParcel(mName, dest, flags); 626 dest.writeInt(mIcon); 627 dest.writeBundle(mExtras); 628 } 629 630 @Override describeContents()631 public int describeContents() { 632 return 0; 633 } 634 635 public static final @android.annotation.NonNull Parcelable.Creator<PlaybackState.CustomAction> CREATOR = 636 new Parcelable.Creator<PlaybackState.CustomAction>() { 637 638 @Override 639 public PlaybackState.CustomAction createFromParcel(Parcel p) { 640 return new PlaybackState.CustomAction(p); 641 } 642 643 @Override 644 public PlaybackState.CustomAction[] newArray(int size) { 645 return new PlaybackState.CustomAction[size]; 646 } 647 }; 648 649 /** 650 * Returns the action of the {@link CustomAction}. 651 * 652 * @return The action of the {@link CustomAction}. 653 */ getAction()654 public String getAction() { 655 return mAction; 656 } 657 658 /** 659 * Returns the display name of this action. e.g. "Favorite" 660 * 661 * @return The display name of this {@link CustomAction}. 662 */ getName()663 public CharSequence getName() { 664 return mName; 665 } 666 667 /** 668 * Returns the resource id of the icon in the {@link MediaSession MediaSession's} package. 669 * 670 * @return The resource id of the icon in the {@link MediaSession MediaSession's} package. 671 */ getIcon()672 public int getIcon() { 673 return mIcon; 674 } 675 676 /** 677 * Returns extras which provide additional application-specific information about the 678 * action, or null if none. These arguments are meant to be consumed by a 679 * {@link MediaController} if it knows how to handle them. 680 * 681 * @return Optional arguments for the {@link CustomAction}. 682 */ getExtras()683 public Bundle getExtras() { 684 return mExtras; 685 } 686 687 @Override toString()688 public String toString() { 689 return "Action:" + "mName='" + mName + ", mIcon=" + mIcon + ", mExtras=" + mExtras; 690 } 691 692 /** 693 * Builder for {@link CustomAction} objects. 694 */ 695 public static final class Builder { 696 private final String mAction; 697 private final CharSequence mName; 698 private final int mIcon; 699 private Bundle mExtras; 700 701 /** 702 * Creates a {@link CustomAction} builder with the id, name, and icon set. 703 * 704 * @param action The action of the {@link CustomAction}. 705 * @param name The display name of the {@link CustomAction}. This name will be displayed 706 * along side the action if the UI supports it. 707 * @param icon The icon resource id of the {@link CustomAction}. This resource id 708 * must be in the same package as the {@link MediaSession}. It will be 709 * displayed with the custom action if the UI supports it. 710 */ Builder(String action, CharSequence name, @DrawableRes int icon)711 public Builder(String action, CharSequence name, @DrawableRes int icon) { 712 if (TextUtils.isEmpty(action)) { 713 throw new IllegalArgumentException( 714 "You must specify an action to build a CustomAction."); 715 } 716 if (TextUtils.isEmpty(name)) { 717 throw new IllegalArgumentException( 718 "You must specify a name to build a CustomAction."); 719 } 720 if (icon == 0) { 721 throw new IllegalArgumentException( 722 "You must specify an icon resource id to build a CustomAction."); 723 } 724 mAction = action; 725 mName = name; 726 mIcon = icon; 727 } 728 729 /** 730 * Set optional extras for the {@link CustomAction}. These extras are meant to be 731 * consumed by a {@link MediaController} if it knows how to handle them. 732 * Keys should be fully qualified (e.g. "com.example.MY_ARG") to avoid collisions. 733 * 734 * @param extras Optional extras for the {@link CustomAction}. 735 * @return this. 736 */ setExtras(Bundle extras)737 public Builder setExtras(Bundle extras) { 738 mExtras = extras; 739 return this; 740 } 741 742 /** 743 * Build and return the {@link CustomAction} instance with the specified values. 744 * 745 * @return A new {@link CustomAction} instance. 746 */ build()747 public CustomAction build() { 748 return new CustomAction(mAction, mName, mIcon, mExtras); 749 } 750 } 751 } 752 753 /** 754 * Builder for {@link PlaybackState} objects. 755 */ 756 public static final class Builder { 757 private final List<PlaybackState.CustomAction> mCustomActions = new ArrayList<>(); 758 759 private int mState; 760 private long mPosition; 761 private long mBufferedPosition; 762 private float mSpeed; 763 private long mActions; 764 private CharSequence mErrorMessage; 765 private long mUpdateTime; 766 private long mActiveItemId = MediaSession.QueueItem.UNKNOWN_ID; 767 private Bundle mExtras; 768 769 /** 770 * Creates an initially empty state builder. 771 */ Builder()772 public Builder() { 773 } 774 775 /** 776 * Creates a builder with the same initial values as those in the from 777 * state. 778 * 779 * @param from The state to use for initializing the builder. 780 */ Builder(PlaybackState from)781 public Builder(PlaybackState from) { 782 if (from == null) { 783 return; 784 } 785 mState = from.mState; 786 mPosition = from.mPosition; 787 mBufferedPosition = from.mBufferedPosition; 788 mSpeed = from.mSpeed; 789 mActions = from.mActions; 790 if (from.mCustomActions != null) { 791 mCustomActions.addAll(from.mCustomActions); 792 } 793 mErrorMessage = from.mErrorMessage; 794 mUpdateTime = from.mUpdateTime; 795 mActiveItemId = from.mActiveItemId; 796 mExtras = from.mExtras; 797 } 798 799 /** 800 * Set the current state of playback. 801 * <p> 802 * The position must be in ms and indicates the current playback 803 * position within the item. If the position is unknown use 804 * {@link #PLAYBACK_POSITION_UNKNOWN}. When not using an unknown 805 * position the time at which the position was updated must be provided. 806 * It is okay to use {@link SystemClock#elapsedRealtime()} if the 807 * current position was just retrieved. 808 * <p> 809 * The speed is a multiple of normal playback and should be 0 when 810 * paused and negative when rewinding. Normal playback speed is 1.0. 811 * <p> 812 * The state must be one of the following: 813 * <ul> 814 * <li> {@link PlaybackState#STATE_NONE}</li> 815 * <li> {@link PlaybackState#STATE_STOPPED}</li> 816 * <li> {@link PlaybackState#STATE_PLAYING}</li> 817 * <li> {@link PlaybackState#STATE_PAUSED}</li> 818 * <li> {@link PlaybackState#STATE_FAST_FORWARDING}</li> 819 * <li> {@link PlaybackState#STATE_REWINDING}</li> 820 * <li> {@link PlaybackState#STATE_BUFFERING}</li> 821 * <li> {@link PlaybackState#STATE_ERROR}</li> 822 * <li> {@link PlaybackState#STATE_CONNECTING}</li> 823 * <li> {@link PlaybackState#STATE_SKIPPING_TO_PREVIOUS}</li> 824 * <li> {@link PlaybackState#STATE_SKIPPING_TO_NEXT}</li> 825 * <li> {@link PlaybackState#STATE_SKIPPING_TO_QUEUE_ITEM}</li> 826 * <li> {@link PlaybackState#STATE_PLAYBACK_SUPPRESSED}</li> 827 * </ul> 828 * 829 * @param state The current state of playback. 830 * @param position The position in the current item in ms. 831 * @param playbackSpeed The current speed of playback as a multiple of 832 * normal playback. 833 * @param updateTime The time in the {@link SystemClock#elapsedRealtime} 834 * timebase that the position was updated at. 835 * @return this 836 */ setState(@tate int state, long position, float playbackSpeed, long updateTime)837 public Builder setState(@State int state, long position, float playbackSpeed, 838 long updateTime) { 839 mState = state; 840 mPosition = position; 841 mUpdateTime = updateTime; 842 mSpeed = playbackSpeed; 843 return this; 844 } 845 846 /** 847 * Set the current state of playback. 848 * <p> 849 * The position must be in ms and indicates the current playback 850 * position within the item. If the position is unknown use 851 * {@link #PLAYBACK_POSITION_UNKNOWN}. The update time will be set to 852 * the current {@link SystemClock#elapsedRealtime()}. 853 * <p> 854 * The speed is a multiple of normal playback and should be 0 when 855 * paused and negative when rewinding. Normal playback speed is 1.0. 856 * <p> 857 * The state must be one of the following: 858 * <ul> 859 * <li> {@link PlaybackState#STATE_NONE}</li> 860 * <li> {@link PlaybackState#STATE_STOPPED}</li> 861 * <li> {@link PlaybackState#STATE_PLAYING}</li> 862 * <li> {@link PlaybackState#STATE_PAUSED}</li> 863 * <li> {@link PlaybackState#STATE_FAST_FORWARDING}</li> 864 * <li> {@link PlaybackState#STATE_REWINDING}</li> 865 * <li> {@link PlaybackState#STATE_BUFFERING}</li> 866 * <li> {@link PlaybackState#STATE_ERROR}</li> 867 * <li> {@link PlaybackState#STATE_CONNECTING}</li> 868 * <li> {@link PlaybackState#STATE_SKIPPING_TO_PREVIOUS}</li> 869 * <li> {@link PlaybackState#STATE_SKIPPING_TO_NEXT}</li> 870 * <li> {@link PlaybackState#STATE_SKIPPING_TO_QUEUE_ITEM}</li> 871 * <li> {@link PlaybackState#STATE_PLAYBACK_SUPPRESSED}</li> 872 * </ul> 873 * 874 * @param state The current state of playback. 875 * @param position The position in the current item in ms. 876 * @param playbackSpeed The current speed of playback as a multiple of 877 * normal playback. 878 * @return this 879 */ setState(@tate int state, long position, float playbackSpeed)880 public Builder setState(@State int state, long position, float playbackSpeed) { 881 return setState(state, position, playbackSpeed, SystemClock.elapsedRealtime()); 882 } 883 884 /** 885 * Set the current actions available on this session. This should use a 886 * bitmask of possible actions. 887 * <ul> 888 * <li> {@link PlaybackState#ACTION_SKIP_TO_PREVIOUS}</li> 889 * <li> {@link PlaybackState#ACTION_REWIND}</li> 890 * <li> {@link PlaybackState#ACTION_PLAY}</li> 891 * <li> {@link PlaybackState#ACTION_PAUSE}</li> 892 * <li> {@link PlaybackState#ACTION_STOP}</li> 893 * <li> {@link PlaybackState#ACTION_FAST_FORWARD}</li> 894 * <li> {@link PlaybackState#ACTION_SKIP_TO_NEXT}</li> 895 * <li> {@link PlaybackState#ACTION_SEEK_TO}</li> 896 * <li> {@link PlaybackState#ACTION_SET_RATING}</li> 897 * <li> {@link PlaybackState#ACTION_PLAY_PAUSE}</li> 898 * <li> {@link PlaybackState#ACTION_PLAY_FROM_MEDIA_ID}</li> 899 * <li> {@link PlaybackState#ACTION_PLAY_FROM_SEARCH}</li> 900 * <li> {@link PlaybackState#ACTION_SKIP_TO_QUEUE_ITEM}</li> 901 * <li> {@link PlaybackState#ACTION_PLAY_FROM_URI}</li> 902 * <li> {@link PlaybackState#ACTION_PREPARE}</li> 903 * <li> {@link PlaybackState#ACTION_PREPARE_FROM_MEDIA_ID}</li> 904 * <li> {@link PlaybackState#ACTION_PREPARE_FROM_SEARCH}</li> 905 * <li> {@link PlaybackState#ACTION_PREPARE_FROM_URI}</li> 906 * <li> {@link PlaybackState#ACTION_SET_PLAYBACK_SPEED}</li> 907 * </ul> 908 * 909 * @param actions The set of actions allowed. 910 * @return this 911 */ setActions(@ctions long actions)912 public Builder setActions(@Actions long actions) { 913 mActions = actions; 914 return this; 915 } 916 917 /** 918 * Add a custom action to the playback state. Actions can be used to 919 * expose additional functionality to {@link MediaController 920 * MediaControllers} beyond what is offered by the standard transport 921 * controls. 922 * <p> 923 * e.g. start a radio station based on the current item or skip ahead by 924 * 30 seconds. 925 * 926 * @param action An identifier for this action. It can be sent back to 927 * the {@link MediaSession} through 928 * {@link MediaController.TransportControls#sendCustomAction(String, Bundle)}. 929 * @param name The display name for the action. If text is shown with 930 * the action or used for accessibility, this is what should 931 * be used. 932 * @param icon The resource action of the icon that should be displayed 933 * for the action. The resource should be in the package of 934 * the {@link MediaSession}. 935 * @return this 936 */ addCustomAction(String action, String name, int icon)937 public Builder addCustomAction(String action, String name, int icon) { 938 return addCustomAction(new PlaybackState.CustomAction(action, name, icon, null)); 939 } 940 941 /** 942 * Add a custom action to the playback state. Actions can be used to expose additional 943 * functionality to {@link MediaController MediaControllers} beyond what is offered by the 944 * standard transport controls. 945 * <p> 946 * An example of an action would be to start a radio station based on the current item 947 * or to skip ahead by 30 seconds. 948 * 949 * @param customAction The custom action to add to the {@link PlaybackState}. 950 * @return this 951 */ addCustomAction(PlaybackState.CustomAction customAction)952 public Builder addCustomAction(PlaybackState.CustomAction customAction) { 953 if (customAction == null) { 954 throw new IllegalArgumentException( 955 "You may not add a null CustomAction to PlaybackState."); 956 } 957 mCustomActions.add(customAction); 958 return this; 959 } 960 961 /** 962 * Set the current buffered position in ms. This is the farthest 963 * playback point that can be reached from the current position using 964 * only buffered content. 965 * 966 * @param bufferedPosition The position in ms that playback is buffered 967 * to. 968 * @return this 969 */ setBufferedPosition(long bufferedPosition)970 public Builder setBufferedPosition(long bufferedPosition) { 971 mBufferedPosition = bufferedPosition; 972 return this; 973 } 974 975 /** 976 * Set the active item in the play queue by specifying its id. The 977 * default value is {@link MediaSession.QueueItem#UNKNOWN_ID} 978 * 979 * @param id The id of the active item. 980 * @return this 981 */ setActiveQueueItemId(long id)982 public Builder setActiveQueueItemId(long id) { 983 mActiveItemId = id; 984 return this; 985 } 986 987 /** 988 * Set a user readable error message. This should be set when the state 989 * is {@link PlaybackState#STATE_ERROR}. 990 * 991 * @param error The error message for display to the user. 992 * @return this 993 */ setErrorMessage(CharSequence error)994 public Builder setErrorMessage(CharSequence error) { 995 mErrorMessage = error; 996 return this; 997 } 998 999 /** 1000 * Set any custom extras to be included with the playback state. 1001 * 1002 * @param extras The extras to include. 1003 * @return this 1004 */ setExtras(Bundle extras)1005 public Builder setExtras(Bundle extras) { 1006 mExtras = extras; 1007 return this; 1008 } 1009 1010 /** 1011 * Build and return the {@link PlaybackState} instance with these 1012 * values. 1013 * 1014 * @return A new state instance. 1015 */ build()1016 public PlaybackState build() { 1017 return new PlaybackState(mState, mPosition, mUpdateTime, mSpeed, mBufferedPosition, 1018 mActions, mCustomActions, mActiveItemId, mErrorMessage, mExtras); 1019 } 1020 } 1021 } 1022