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