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