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