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