Home | History | Annotate | Download | only in session
      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