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