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