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