Java tutorial
/* * Copyright (C) 2014 The Android Open Source Project * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package android.support.v4.media.session; import static androidx.annotation.RestrictTo.Scope.LIBRARY_GROUP_PREFIX; import android.annotation.SuppressLint; import android.media.session.PlaybackState; import android.os.Build; import android.os.Bundle; import android.os.Parcel; import android.os.Parcelable; import android.os.SystemClock; import android.text.TextUtils; import android.view.KeyEvent; import androidx.annotation.IntDef; import androidx.annotation.LongDef; import androidx.annotation.Nullable; import androidx.annotation.RestrictTo; import java.lang.annotation.Retention; import java.lang.annotation.RetentionPolicy; import java.util.ArrayList; import java.util.List; /** * Playback state for a {@link MediaSessionCompat}. This includes a state like * {@link PlaybackStateCompat#STATE_PLAYING}, the current playback position, * and the current control capabilities. */ @SuppressLint("BanParcelableUsage") public final class PlaybackStateCompat implements Parcelable { /** * @hide */ @RestrictTo(LIBRARY_GROUP_PREFIX) @LongDef(flag = true, value = { ACTION_STOP, ACTION_PAUSE, ACTION_PLAY, ACTION_REWIND, ACTION_SKIP_TO_PREVIOUS, ACTION_SKIP_TO_NEXT, ACTION_FAST_FORWARD, ACTION_SET_RATING, ACTION_SEEK_TO, ACTION_PLAY_PAUSE, ACTION_PLAY_FROM_MEDIA_ID, ACTION_PLAY_FROM_SEARCH, ACTION_SKIP_TO_QUEUE_ITEM, ACTION_PLAY_FROM_URI, ACTION_PREPARE, ACTION_PREPARE_FROM_MEDIA_ID, ACTION_PREPARE_FROM_SEARCH, ACTION_PREPARE_FROM_URI, ACTION_SET_REPEAT_MODE, ACTION_SET_SHUFFLE_MODE, ACTION_SET_CAPTIONING_ENABLED }) @Retention(RetentionPolicy.SOURCE) public @interface Actions { } /** * @hide */ @RestrictTo(LIBRARY_GROUP_PREFIX) @LongDef({ ACTION_STOP, ACTION_PAUSE, ACTION_PLAY, ACTION_REWIND, ACTION_SKIP_TO_PREVIOUS, ACTION_SKIP_TO_NEXT, ACTION_FAST_FORWARD, ACTION_PLAY_PAUSE }) @Retention(RetentionPolicy.SOURCE) public @interface MediaKeyAction { } /** * Indicates this session supports the stop command. * * @see Builder#setActions(long) */ public static final long ACTION_STOP = 1 << 0; /** * Indicates this session supports the pause command. * * @see Builder#setActions(long) */ public static final long ACTION_PAUSE = 1 << 1; /** * Indicates this session supports the play command. * * @see Builder#setActions(long) */ public static final long ACTION_PLAY = 1 << 2; /** * Indicates this session supports the rewind command. * * @see Builder#setActions(long) */ public static final long ACTION_REWIND = 1 << 3; /** * Indicates this session supports the previous command. * * @see Builder#setActions(long) */ public static final long ACTION_SKIP_TO_PREVIOUS = 1 << 4; /** * Indicates this session supports the next command. * * @see Builder#setActions(long) */ public static final long ACTION_SKIP_TO_NEXT = 1 << 5; /** * Indicates this session supports the fast forward command. * * @see Builder#setActions(long) */ public static final long ACTION_FAST_FORWARD = 1 << 6; /** * Indicates this session supports the set rating command. * * @see Builder#setActions(long) */ public static final long ACTION_SET_RATING = 1 << 7; /** * Indicates this session supports the seek to command. * * @see Builder#setActions(long) */ public static final long ACTION_SEEK_TO = 1 << 8; /** * Indicates this session supports the play/pause toggle command. * * @see Builder#setActions(long) */ public static final long ACTION_PLAY_PAUSE = 1 << 9; /** * Indicates this session supports the play from media id command. * * @see Builder#setActions(long) */ public static final long ACTION_PLAY_FROM_MEDIA_ID = 1 << 10; /** * Indicates this session supports the play from search command. * * @see Builder#setActions(long) */ public static final long ACTION_PLAY_FROM_SEARCH = 1 << 11; /** * Indicates this session supports the skip to queue item command. * * @see Builder#setActions(long) */ public static final long ACTION_SKIP_TO_QUEUE_ITEM = 1 << 12; /** * Indicates this session supports the play from URI command. * * @see Builder#setActions(long) */ public static final long ACTION_PLAY_FROM_URI = 1 << 13; /** * Indicates this session supports the prepare command. * * @see Builder#setActions(long) */ public static final long ACTION_PREPARE = 1 << 14; /** * Indicates this session supports the prepare from media id command. * * @see Builder#setActions(long) */ public static final long ACTION_PREPARE_FROM_MEDIA_ID = 1 << 15; /** * Indicates this session supports the prepare from search command. * * @see Builder#setActions(long) */ public static final long ACTION_PREPARE_FROM_SEARCH = 1 << 16; /** * Indicates this session supports the prepare from URI command. * * @see Builder#setActions(long) */ public static final long ACTION_PREPARE_FROM_URI = 1 << 17; /** * Indicates this session supports the set repeat mode command. * * @see Builder#setActions(long) */ public static final long ACTION_SET_REPEAT_MODE = 1 << 18; /** * Indicates this session supports the set shuffle mode enabled command. * * @see Builder#setActions(long) * @deprecated Use {@link #ACTION_SET_SHUFFLE_MODE} instead. */ @Deprecated public static final long ACTION_SET_SHUFFLE_MODE_ENABLED = 1 << 19; /** * Indicates this session supports the set captioning enabled command. * * @see Builder#setActions(long) */ public static final long ACTION_SET_CAPTIONING_ENABLED = 1 << 20; /** * Indicates this session supports the set shuffle mode command. * * @see Builder#setActions(long) */ public static final long ACTION_SET_SHUFFLE_MODE = 1 << 21; /** * @hide */ @RestrictTo(LIBRARY_GROUP_PREFIX) @IntDef({ STATE_NONE, STATE_STOPPED, STATE_PAUSED, STATE_PLAYING, STATE_FAST_FORWARDING, STATE_REWINDING, STATE_BUFFERING, STATE_ERROR, STATE_CONNECTING, STATE_SKIPPING_TO_PREVIOUS, STATE_SKIPPING_TO_NEXT, STATE_SKIPPING_TO_QUEUE_ITEM }) @Retention(RetentionPolicy.SOURCE) public @interface State { } /** * This is the default playback state and indicates that no media has been * added yet, or the performer has been reset and has no content to play. * * @see Builder#setState */ public final static int STATE_NONE = 0; /** * State indicating this item is currently stopped. * * @see Builder#setState */ public final static int STATE_STOPPED = 1; /** * State indicating this item is currently paused. * * @see Builder#setState */ public final static int STATE_PAUSED = 2; /** * State indicating this item is currently playing. * * @see Builder#setState */ public final static int STATE_PLAYING = 3; /** * State indicating this item is currently fast forwarding. * * @see Builder#setState */ public final static int STATE_FAST_FORWARDING = 4; /** * State indicating this item is currently rewinding. * * @see Builder#setState */ public final static int STATE_REWINDING = 5; /** * State indicating this item is currently buffering and will begin playing * when enough data has buffered. * * @see Builder#setState */ public final static int STATE_BUFFERING = 6; /** * State indicating this item is currently in an error state. The error * code should also be set when entering this state. * * @see Builder#setState * @see Builder#setErrorMessage(int, CharSequence) */ public final static int STATE_ERROR = 7; /** * State indicating the class doing playback is currently connecting to a * route. Depending on the implementation you may return to the previous * state when the connection finishes or enter {@link #STATE_NONE}. If * the connection failed {@link #STATE_ERROR} should be used. * <p> * On devices earlier than API 21, this will appear as {@link #STATE_BUFFERING} * </p> * * @see Builder#setState */ public final static int STATE_CONNECTING = 8; /** * State indicating the player is currently skipping to the previous item. * * @see Builder#setState */ public final static int STATE_SKIPPING_TO_PREVIOUS = 9; /** * State indicating the player is currently skipping to the next item. * * @see Builder#setState */ public final static int STATE_SKIPPING_TO_NEXT = 10; /** * State indicating the player is currently skipping to a specific item in * the queue. * <p> * On devices earlier than API 21, this will appear as {@link #STATE_SKIPPING_TO_NEXT} * </p> * * @see Builder#setState */ public final static int STATE_SKIPPING_TO_QUEUE_ITEM = 11; /** * Use this value for the position to indicate the position is not known. */ public final static long PLAYBACK_POSITION_UNKNOWN = -1; /** * @hide */ @RestrictTo(LIBRARY_GROUP_PREFIX) @IntDef({ REPEAT_MODE_INVALID, REPEAT_MODE_NONE, REPEAT_MODE_ONE, REPEAT_MODE_ALL, REPEAT_MODE_GROUP }) @Retention(RetentionPolicy.SOURCE) public @interface RepeatMode { } /** * {@link MediaControllerCompat.TransportControls#getRepeatMode} returns this value * when the session is not ready for providing its repeat mode. */ public static final int REPEAT_MODE_INVALID = -1; /** * Use this value with {@link MediaControllerCompat.TransportControls#setRepeatMode} * to indicate that the playback will be stopped at the end of the playing media list. */ public static final int REPEAT_MODE_NONE = 0; /** * Use this value with {@link MediaControllerCompat.TransportControls#setRepeatMode} * to indicate that the playback of the current playing media item will be repeated. */ public static final int REPEAT_MODE_ONE = 1; /** * Use this value with {@link MediaControllerCompat.TransportControls#setRepeatMode} * to indicate that the playback of the playing media list will be repeated. */ public static final int REPEAT_MODE_ALL = 2; /** * Use this value with {@link MediaControllerCompat.TransportControls#setRepeatMode} * to indicate that the playback of the playing media group will be repeated. * A group is a logical block of media items which is specified in the section 5.7 of the * Bluetooth AVRCP 1.6. */ public static final int REPEAT_MODE_GROUP = 3; /** * @hide */ @RestrictTo(LIBRARY_GROUP_PREFIX) @IntDef({ SHUFFLE_MODE_INVALID, SHUFFLE_MODE_NONE, SHUFFLE_MODE_ALL, SHUFFLE_MODE_GROUP }) @Retention(RetentionPolicy.SOURCE) public @interface ShuffleMode { } /** * {@link MediaControllerCompat.TransportControls#getShuffleMode} returns this value * when the session is not ready for providing its shuffle mode. */ public static final int SHUFFLE_MODE_INVALID = -1; /** * Use this value with {@link MediaControllerCompat.TransportControls#setShuffleMode} * to indicate that the media list will be played in order. */ public static final int SHUFFLE_MODE_NONE = 0; /** * Use this value with {@link MediaControllerCompat.TransportControls#setShuffleMode} * to indicate that the media list will be played in shuffled order. */ public static final int SHUFFLE_MODE_ALL = 1; /** * Use this value with {@link MediaControllerCompat.TransportControls#setShuffleMode} * to indicate that the media group will be played in shuffled order. * A group is a logical block of media items which is specified in the section 5.7 of the * Bluetooth AVRCP 1.6. */ public static final int SHUFFLE_MODE_GROUP = 2; /** * @hide */ @RestrictTo(LIBRARY_GROUP_PREFIX) @IntDef({ ERROR_CODE_UNKNOWN_ERROR, ERROR_CODE_APP_ERROR, ERROR_CODE_NOT_SUPPORTED, ERROR_CODE_AUTHENTICATION_EXPIRED, ERROR_CODE_PREMIUM_ACCOUNT_REQUIRED, ERROR_CODE_CONCURRENT_STREAM_LIMIT, ERROR_CODE_PARENTAL_CONTROL_RESTRICTED, ERROR_CODE_NOT_AVAILABLE_IN_REGION, ERROR_CODE_CONTENT_ALREADY_PLAYING, ERROR_CODE_SKIP_LIMIT_REACHED, ERROR_CODE_ACTION_ABORTED, ERROR_CODE_END_OF_QUEUE }) @Retention(RetentionPolicy.SOURCE) public @interface ErrorCode { } /** * This is the default error code and indicates that none of the other error codes applies. * The error code should be set when entering {@link #STATE_ERROR}. */ public static final int ERROR_CODE_UNKNOWN_ERROR = 0; /** * Error code when the application state is invalid to fulfill the request. * The error code should be set when entering {@link #STATE_ERROR}. */ public static final int ERROR_CODE_APP_ERROR = 1; /** * Error code when the request is not supported by the application. * The error code should be set when entering {@link #STATE_ERROR}. */ public static final int ERROR_CODE_NOT_SUPPORTED = 2; /** * Error code when the request cannot be performed because authentication has expired. * The error code should be set when entering {@link #STATE_ERROR}. */ public static final int ERROR_CODE_AUTHENTICATION_EXPIRED = 3; /** * Error code when a premium account is required for the request to succeed. * The error code should be set when entering {@link #STATE_ERROR}. */ public static final int ERROR_CODE_PREMIUM_ACCOUNT_REQUIRED = 4; /** * Error code when too many concurrent streams are detected. * The error code should be set when entering {@link #STATE_ERROR}. */ public static final int ERROR_CODE_CONCURRENT_STREAM_LIMIT = 5; /** * Error code when the content is blocked due to parental controls. * The error code should be set when entering {@link #STATE_ERROR}. */ public static final int ERROR_CODE_PARENTAL_CONTROL_RESTRICTED = 6; /** * Error code when the content is blocked due to being regionally unavailable. * The error code should be set when entering {@link #STATE_ERROR}. */ public static final int ERROR_CODE_NOT_AVAILABLE_IN_REGION = 7; /** * Error code when the requested content is already playing. * The error code should be set when entering {@link #STATE_ERROR}. */ public static final int ERROR_CODE_CONTENT_ALREADY_PLAYING = 8; /** * Error code when the application cannot skip any more songs because skip limit is reached. * The error code should be set when entering {@link #STATE_ERROR}. */ public static final int ERROR_CODE_SKIP_LIMIT_REACHED = 9; /** * Error code when the action is interrupted due to some external event. * The error code should be set when entering {@link #STATE_ERROR}. */ public static final int ERROR_CODE_ACTION_ABORTED = 10; /** * Error code when the playback navigation (previous, next) is not possible because the queue * was exhausted. * The error code should be set when entering {@link #STATE_ERROR}. */ public static final int ERROR_CODE_END_OF_QUEUE = 11; // KeyEvent constants only available on API 11+ private static final int KEYCODE_MEDIA_PAUSE = 127; private static final int KEYCODE_MEDIA_PLAY = 126; /** * Translates a given action into a matched key code defined in {@link KeyEvent}. The given * action should be one of the following: * <ul> * <li>{@link PlaybackStateCompat#ACTION_PLAY}</li> * <li>{@link PlaybackStateCompat#ACTION_PAUSE}</li> * <li>{@link PlaybackStateCompat#ACTION_SKIP_TO_NEXT}</li> * <li>{@link PlaybackStateCompat#ACTION_SKIP_TO_PREVIOUS}</li> * <li>{@link PlaybackStateCompat#ACTION_STOP}</li> * <li>{@link PlaybackStateCompat#ACTION_FAST_FORWARD}</li> * <li>{@link PlaybackStateCompat#ACTION_REWIND}</li> * <li>{@link PlaybackStateCompat#ACTION_PLAY_PAUSE}</li> * </ul> * * @param action The action to be translated. * * @return the key code matched to the given action. */ public static int toKeyCode(@MediaKeyAction long action) { if (action == ACTION_PLAY) { return KEYCODE_MEDIA_PLAY; } else if (action == ACTION_PAUSE) { return KEYCODE_MEDIA_PAUSE; } else if (action == ACTION_SKIP_TO_NEXT) { return KeyEvent.KEYCODE_MEDIA_NEXT; } else if (action == ACTION_SKIP_TO_PREVIOUS) { return KeyEvent.KEYCODE_MEDIA_PREVIOUS; } else if (action == ACTION_STOP) { return KeyEvent.KEYCODE_MEDIA_STOP; } else if (action == ACTION_FAST_FORWARD) { return KeyEvent.KEYCODE_MEDIA_FAST_FORWARD; } else if (action == ACTION_REWIND) { return KeyEvent.KEYCODE_MEDIA_REWIND; } else if (action == ACTION_PLAY_PAUSE) { return KeyEvent.KEYCODE_MEDIA_PLAY_PAUSE; } return KeyEvent.KEYCODE_UNKNOWN; } final int mState; final long mPosition; final long mBufferedPosition; final float mSpeed; final long mActions; final int mErrorCode; final CharSequence mErrorMessage; final long mUpdateTime; List<PlaybackStateCompat.CustomAction> mCustomActions; final long mActiveItemId; final Bundle mExtras; private PlaybackState mStateFwk; PlaybackStateCompat(int state, long position, long bufferedPosition, float rate, long actions, int errorCode, CharSequence errorMessage, long updateTime, List<PlaybackStateCompat.CustomAction> customActions, long activeItemId, Bundle extras) { mState = state; mPosition = position; mBufferedPosition = bufferedPosition; mSpeed = rate; mActions = actions; mErrorCode = errorCode; mErrorMessage = errorMessage; mUpdateTime = updateTime; mCustomActions = new ArrayList<>(customActions); mActiveItemId = activeItemId; mExtras = extras; } PlaybackStateCompat(Parcel in) { mState = in.readInt(); mPosition = in.readLong(); mSpeed = in.readFloat(); mUpdateTime = in.readLong(); mBufferedPosition = in.readLong(); mActions = in.readLong(); mErrorMessage = TextUtils.CHAR_SEQUENCE_CREATOR.createFromParcel(in); mCustomActions = in.createTypedArrayList(CustomAction.CREATOR); mActiveItemId = in.readLong(); mExtras = in.readBundle(MediaSessionCompat.class.getClassLoader()); // New attributes should be added at the end for backward compatibility. mErrorCode = in.readInt(); } @Override public String toString() { StringBuilder bob = new StringBuilder("PlaybackState {"); bob.append("state=").append(mState); bob.append(", position=").append(mPosition); bob.append(", buffered position=").append(mBufferedPosition); bob.append(", speed=").append(mSpeed); bob.append(", updated=").append(mUpdateTime); bob.append(", actions=").append(mActions); bob.append(", error code=").append(mErrorCode); bob.append(", error message=").append(mErrorMessage); bob.append(", custom actions=").append(mCustomActions); bob.append(", active item id=").append(mActiveItemId); bob.append("}"); return bob.toString(); } @Override public int describeContents() { return 0; } @Override public void writeToParcel(Parcel dest, int flags) { dest.writeInt(mState); dest.writeLong(mPosition); dest.writeFloat(mSpeed); dest.writeLong(mUpdateTime); dest.writeLong(mBufferedPosition); dest.writeLong(mActions); TextUtils.writeToParcel(mErrorMessage, dest, flags); dest.writeTypedList(mCustomActions); dest.writeLong(mActiveItemId); dest.writeBundle(mExtras); // New attributes should be added at the end for backward compatibility. dest.writeInt(mErrorCode); } /** * Get the current state of playback. One of the following: * <ul> * <li> {@link PlaybackStateCompat#STATE_NONE}</li> * <li> {@link PlaybackStateCompat#STATE_STOPPED}</li> * <li> {@link PlaybackStateCompat#STATE_PLAYING}</li> * <li> {@link PlaybackStateCompat#STATE_PAUSED}</li> * <li> {@link PlaybackStateCompat#STATE_FAST_FORWARDING}</li> * <li> {@link PlaybackStateCompat#STATE_REWINDING}</li> * <li> {@link PlaybackStateCompat#STATE_BUFFERING}</li> * <li> {@link PlaybackStateCompat#STATE_ERROR}</li> * <li> {@link PlaybackStateCompat#STATE_CONNECTING}</li> * <li> {@link PlaybackStateCompat#STATE_SKIPPING_TO_PREVIOUS}</li> * <li> {@link PlaybackStateCompat#STATE_SKIPPING_TO_NEXT}</li> * <li> {@link PlaybackStateCompat#STATE_SKIPPING_TO_QUEUE_ITEM}</li> */ @State public int getState() { return mState; } /** * Get the playback position in ms at last position update time. */ public long getPosition() { return mPosition; } /** * Get the elapsed real time at which position was last updated. If the * position has never been set this will return 0; * * @return The last time the position was updated. */ public long getLastPositionUpdateTime() { return mUpdateTime; } /** * Get the current playback position in ms. * * @param timeDiff Only used for testing, otherwise it should be null. * @return The current playback position in ms * @hide */ @RestrictTo(LIBRARY_GROUP_PREFIX) public long getCurrentPosition(Long timeDiff) { long expectedPosition = mPosition + (long) (mSpeed * ((timeDiff != null) ? timeDiff : SystemClock.elapsedRealtime() - mUpdateTime)); return Math.max(0, expectedPosition); } /** * Get the current buffered position in ms. This is the farthest playback * point that can be reached from the current position using only buffered * content. */ public long getBufferedPosition() { return mBufferedPosition; } /** * Get the current playback speed as a multiple of normal playback. This * should be negative when rewinding. A value of 1 means normal playback and * 0 means paused. * * @return The current speed of playback. */ public float getPlaybackSpeed() { return mSpeed; } /** * Get the current actions available on this session. This should use a * bitmask of the available actions. * <ul> * <li> {@link PlaybackStateCompat#ACTION_SKIP_TO_PREVIOUS}</li> * <li> {@link PlaybackStateCompat#ACTION_REWIND}</li> * <li> {@link PlaybackStateCompat#ACTION_PLAY}</li> * <li> {@link PlaybackStateCompat#ACTION_PLAY_PAUSE}</li> * <li> {@link PlaybackStateCompat#ACTION_PAUSE}</li> * <li> {@link PlaybackStateCompat#ACTION_STOP}</li> * <li> {@link PlaybackStateCompat#ACTION_FAST_FORWARD}</li> * <li> {@link PlaybackStateCompat#ACTION_SKIP_TO_NEXT}</li> * <li> {@link PlaybackStateCompat#ACTION_SEEK_TO}</li> * <li> {@link PlaybackStateCompat#ACTION_SET_RATING}</li> * <li> {@link PlaybackStateCompat#ACTION_PLAY_FROM_MEDIA_ID}</li> * <li> {@link PlaybackStateCompat#ACTION_PLAY_FROM_SEARCH}</li> * <li> {@link PlaybackStateCompat#ACTION_SKIP_TO_QUEUE_ITEM}</li> * <li> {@link PlaybackStateCompat#ACTION_PLAY_FROM_URI}</li> * <li> {@link PlaybackStateCompat#ACTION_PREPARE}</li> * <li> {@link PlaybackStateCompat#ACTION_PREPARE_FROM_MEDIA_ID}</li> * <li> {@link PlaybackStateCompat#ACTION_PREPARE_FROM_SEARCH}</li> * <li> {@link PlaybackStateCompat#ACTION_PREPARE_FROM_URI}</li> * <li> {@link PlaybackStateCompat#ACTION_SET_REPEAT_MODE}</li> * <li> {@link PlaybackStateCompat#ACTION_SET_SHUFFLE_MODE}</li> * <li> {@link PlaybackStateCompat#ACTION_SET_CAPTIONING_ENABLED}</li> * </ul> */ @Actions public long getActions() { return mActions; } /** * Get the list of custom actions. */ public List<PlaybackStateCompat.CustomAction> getCustomActions() { return mCustomActions; } /** * Get the error code. This should be set when the state is * {@link PlaybackStateCompat#STATE_ERROR}. * * @see #ERROR_CODE_UNKNOWN_ERROR * @see #ERROR_CODE_APP_ERROR * @see #ERROR_CODE_NOT_SUPPORTED * @see #ERROR_CODE_AUTHENTICATION_EXPIRED * @see #ERROR_CODE_PREMIUM_ACCOUNT_REQUIRED * @see #ERROR_CODE_CONCURRENT_STREAM_LIMIT * @see #ERROR_CODE_PARENTAL_CONTROL_RESTRICTED * @see #ERROR_CODE_NOT_AVAILABLE_IN_REGION * @see #ERROR_CODE_CONTENT_ALREADY_PLAYING * @see #ERROR_CODE_SKIP_LIMIT_REACHED * @see #ERROR_CODE_ACTION_ABORTED * @see #ERROR_CODE_END_OF_QUEUE * @see #getErrorMessage() */ @ErrorCode public int getErrorCode() { return mErrorCode; } /** * Get the user readable optional error message. This may be set when the state is * {@link PlaybackStateCompat#STATE_ERROR}. * * @see #getErrorCode() */ public CharSequence getErrorMessage() { return mErrorMessage; } /** * Get the id of the currently active item in the queue. If there is no * queue or a queue is not supported by the session this will be * {@link MediaSessionCompat.QueueItem#UNKNOWN_ID}. * * @return The id of the currently active item in the queue or * {@link MediaSessionCompat.QueueItem#UNKNOWN_ID}. */ public long getActiveQueueItemId() { return mActiveItemId; } /** * Get any custom extras that were set on this playback state. * * @return The extras for this state or null. */ public @Nullable Bundle getExtras() { return mExtras; } /** * Creates an instance from a framework {@link android.media.session.PlaybackState} object. * <p> * This method is only supported on API 21+. * </p> * * @param stateObj A {@link android.media.session.PlaybackState} object, or null if none. * @return An equivalent {@link PlaybackStateCompat} object, or null if none. */ public static PlaybackStateCompat fromPlaybackState(Object stateObj) { if (stateObj != null && Build.VERSION.SDK_INT >= 21) { PlaybackState stateFwk = (PlaybackState) stateObj; List<PlaybackState.CustomAction> customActionFwks = stateFwk.getCustomActions(); List<PlaybackStateCompat.CustomAction> customActions = null; if (customActionFwks != null) { customActions = new ArrayList<>(customActionFwks.size()); for (Object customActionFwk : customActionFwks) { customActions.add(CustomAction.fromCustomAction(customActionFwk)); } } Bundle extras; if (Build.VERSION.SDK_INT >= 22) { extras = stateFwk.getExtras(); } else { extras = null; } PlaybackStateCompat stateCompat = new PlaybackStateCompat(stateFwk.getState(), stateFwk.getPosition(), stateFwk.getBufferedPosition(), stateFwk.getPlaybackSpeed(), stateFwk.getActions(), ERROR_CODE_UNKNOWN_ERROR, stateFwk.getErrorMessage(), stateFwk.getLastPositionUpdateTime(), customActions, stateFwk.getActiveQueueItemId(), extras); stateCompat.mStateFwk = stateFwk; return stateCompat; } else { return null; } } /** * Gets the underlying framework {@link android.media.session.PlaybackState} object. * <p> * This method is only supported on API 21+. * </p> * * @return An equivalent {@link android.media.session.PlaybackState} object, or null if none. */ public Object getPlaybackState() { if (mStateFwk == null && Build.VERSION.SDK_INT >= 21) { PlaybackState.Builder builder = new PlaybackState.Builder(); builder.setState(mState, mPosition, mSpeed, mUpdateTime); builder.setBufferedPosition(mBufferedPosition); builder.setActions(mActions); builder.setErrorMessage(mErrorMessage); for (PlaybackStateCompat.CustomAction customAction : mCustomActions) { builder.addCustomAction((PlaybackState.CustomAction) customAction.getCustomAction()); } builder.setActiveQueueItemId(mActiveItemId); if (Build.VERSION.SDK_INT >= 22) { builder.setExtras(mExtras); } mStateFwk = builder.build(); } return mStateFwk; } public static final Parcelable.Creator<PlaybackStateCompat> CREATOR = new Parcelable.Creator<PlaybackStateCompat>() { @Override public PlaybackStateCompat createFromParcel(Parcel in) { return new PlaybackStateCompat(in); } @Override public PlaybackStateCompat[] newArray(int size) { return new PlaybackStateCompat[size]; } }; /** * {@link PlaybackStateCompat.CustomAction CustomActions} can be used to * extend the capabilities of the standard transport controls by exposing * app specific actions to {@link MediaControllerCompat Controllers}. */ public static final class CustomAction implements Parcelable { private final String mAction; private final CharSequence mName; private final int mIcon; private final Bundle mExtras; private PlaybackState.CustomAction mCustomActionFwk; /** * Use {@link PlaybackStateCompat.CustomAction.Builder#build()}. */ CustomAction(String action, CharSequence name, int icon, Bundle extras) { mAction = action; mName = name; mIcon = icon; mExtras = extras; } CustomAction(Parcel in) { mAction = in.readString(); mName = TextUtils.CHAR_SEQUENCE_CREATOR.createFromParcel(in); mIcon = in.readInt(); mExtras = in.readBundle(MediaSessionCompat.class.getClassLoader()); } @Override public void writeToParcel(Parcel dest, int flags) { dest.writeString(mAction); TextUtils.writeToParcel(mName, dest, flags); dest.writeInt(mIcon); dest.writeBundle(mExtras); } @Override public int describeContents() { return 0; } /** * Creates an instance from a framework * {@link android.media.session.PlaybackState.CustomAction} object. * <p> * This method is only supported on API 21+. * </p> * * @param customActionObj A {@link android.media.session.PlaybackState.CustomAction} object, * or null if none. * @return An equivalent {@link PlaybackStateCompat.CustomAction} object, or null if none. */ public static PlaybackStateCompat.CustomAction fromCustomAction(Object customActionObj) { if (customActionObj == null || Build.VERSION.SDK_INT < 21) { return null; } PlaybackState.CustomAction customActionFwk = (PlaybackState.CustomAction) customActionObj; PlaybackStateCompat.CustomAction customActionCompat = new PlaybackStateCompat.CustomAction( customActionFwk.getAction(), customActionFwk.getName(), customActionFwk.getIcon(), customActionFwk.getExtras()); customActionCompat.mCustomActionFwk = customActionFwk; return customActionCompat; } /** * Gets the underlying framework {@link android.media.session.PlaybackState.CustomAction} * object. * <p> * This method is only supported on API 21+. * </p> * * @return An equivalent {@link android.media.session.PlaybackState.CustomAction} object, * or null if none. */ public Object getCustomAction() { if (mCustomActionFwk != null || Build.VERSION.SDK_INT < 21) { return mCustomActionFwk; } PlaybackState.CustomAction.Builder builder = new PlaybackState.CustomAction.Builder(mAction, mName, mIcon); builder.setExtras(mExtras); return builder.build(); } public static final Parcelable.Creator<PlaybackStateCompat.CustomAction> CREATOR = new Parcelable.Creator<PlaybackStateCompat.CustomAction>() { @Override public PlaybackStateCompat.CustomAction createFromParcel(Parcel p) { return new PlaybackStateCompat.CustomAction(p); } @Override public PlaybackStateCompat.CustomAction[] newArray(int size) { return new PlaybackStateCompat.CustomAction[size]; } }; /** * Returns the action of the {@link CustomAction}. * * @return The action of the {@link CustomAction}. */ public String getAction() { return mAction; } /** * Returns the display name of this action. e.g. "Favorite" * * @return The display name of this {@link CustomAction}. */ public CharSequence getName() { return mName; } /** * Returns the resource id of the icon in the {@link MediaSessionCompat * Session's} package. * * @return The resource id of the icon in the {@link MediaSessionCompat * Session's} package. */ public int getIcon() { return mIcon; } /** * Returns extras which provide additional application-specific * information about the action, or null if none. These arguments are * meant to be consumed by a {@link MediaControllerCompat} if it knows * how to handle them. * * @return Optional arguments for the {@link CustomAction}. */ public Bundle getExtras() { return mExtras; } @Override public String toString() { return "Action:" + "mName='" + mName + ", mIcon=" + mIcon + ", mExtras=" + mExtras; } /** * Builder for {@link CustomAction} objects. */ public static final class Builder { private final String mAction; private final CharSequence mName; private final int mIcon; private Bundle mExtras; /** * Creates a {@link CustomAction} builder with the id, name, and * icon set. * * @param action The action of the {@link CustomAction}. * @param name The display name of the {@link CustomAction}. This * name will be displayed along side the action if the UI * supports it. * @param icon The icon resource id of the {@link CustomAction}. * This resource id must be in the same package as the * {@link MediaSessionCompat}. It will be displayed with * the custom action if the UI supports it. */ public Builder(String action, CharSequence name, int icon) { if (TextUtils.isEmpty(action)) { throw new IllegalArgumentException("You must specify an action to build a CustomAction"); } if (TextUtils.isEmpty(name)) { throw new IllegalArgumentException("You must specify a name to build a CustomAction"); } if (icon == 0) { throw new IllegalArgumentException( "You must specify an icon resource id to build a CustomAction"); } mAction = action; mName = name; mIcon = icon; } /** * Set optional extras for the {@link CustomAction}. These extras * are meant to be consumed by a {@link MediaControllerCompat} if it * knows how to handle them. Keys should be fully qualified (e.g. * "com.example.MY_ARG") to avoid collisions. * * @param extras Optional extras for the {@link CustomAction}. * @return this. */ public Builder setExtras(Bundle extras) { mExtras = extras; return this; } /** * Build and return the {@link CustomAction} instance with the * specified values. * * @return A new {@link CustomAction} instance. */ public CustomAction build() { return new CustomAction(mAction, mName, mIcon, mExtras); } } } /** * Builder for {@link PlaybackStateCompat} objects. */ public static final class Builder { private final List<PlaybackStateCompat.CustomAction> mCustomActions = new ArrayList<>(); private int mState; private long mPosition; private long mBufferedPosition; private float mRate; private long mActions; private int mErrorCode; private CharSequence mErrorMessage; private long mUpdateTime; private long mActiveItemId = MediaSessionCompat.QueueItem.UNKNOWN_ID; private Bundle mExtras; /** * Create an empty Builder. */ public Builder() { } /** * Create a Builder using a {@link PlaybackStateCompat} instance to set the * initial values. * * @param source The playback state to copy. */ public Builder(PlaybackStateCompat source) { mState = source.mState; mPosition = source.mPosition; mRate = source.mSpeed; mUpdateTime = source.mUpdateTime; mBufferedPosition = source.mBufferedPosition; mActions = source.mActions; mErrorCode = source.mErrorCode; mErrorMessage = source.mErrorMessage; if (source.mCustomActions != null) { mCustomActions.addAll(source.mCustomActions); } mActiveItemId = source.mActiveItemId; mExtras = source.mExtras; } /** * Set the current state of playback. * <p> * The position must be in ms and indicates the current playback * position within the track. If the position is unknown use * {@link #PLAYBACK_POSITION_UNKNOWN}. * <p> * The rate is a multiple of normal playback and should be 0 when paused * and negative when rewinding. Normal playback rate is 1.0. * <p> * The state must be one of the following: * <ul> * <li> {@link PlaybackStateCompat#STATE_NONE}</li> * <li> {@link PlaybackStateCompat#STATE_STOPPED}</li> * <li> {@link PlaybackStateCompat#STATE_PLAYING}</li> * <li> {@link PlaybackStateCompat#STATE_PAUSED}</li> * <li> {@link PlaybackStateCompat#STATE_FAST_FORWARDING}</li> * <li> {@link PlaybackStateCompat#STATE_REWINDING}</li> * <li> {@link PlaybackStateCompat#STATE_BUFFERING}</li> * <li> {@link PlaybackStateCompat#STATE_ERROR}</li> * <li> {@link PlaybackStateCompat#STATE_CONNECTING}</li> * <li> {@link PlaybackStateCompat#STATE_SKIPPING_TO_PREVIOUS}</li> * <li> {@link PlaybackStateCompat#STATE_SKIPPING_TO_NEXT}</li> * <li> {@link PlaybackStateCompat#STATE_SKIPPING_TO_QUEUE_ITEM}</li> * </ul> * * @param state The current state of playback. * @param position The position in the current track in ms. * @param playbackSpeed The current rate of playback as a multiple of * normal playback. */ public Builder setState(@State int state, long position, float playbackSpeed) { return setState(state, position, playbackSpeed, SystemClock.elapsedRealtime()); } /** * Set the current state of playback. * <p> * The position must be in ms and indicates the current playback * position within the track. If the position is unknown use * {@link #PLAYBACK_POSITION_UNKNOWN}. * <p> * The rate is a multiple of normal playback and should be 0 when paused * and negative when rewinding. Normal playback rate is 1.0. * <p> * The state must be one of the following: * <ul> * <li> {@link PlaybackStateCompat#STATE_NONE}</li> * <li> {@link PlaybackStateCompat#STATE_STOPPED}</li> * <li> {@link PlaybackStateCompat#STATE_PLAYING}</li> * <li> {@link PlaybackStateCompat#STATE_PAUSED}</li> * <li> {@link PlaybackStateCompat#STATE_FAST_FORWARDING}</li> * <li> {@link PlaybackStateCompat#STATE_REWINDING}</li> * <li> {@link PlaybackStateCompat#STATE_BUFFERING}</li> * <li> {@link PlaybackStateCompat#STATE_ERROR}</li> * <li> {@link PlaybackStateCompat#STATE_CONNECTING}</li> * <li> {@link PlaybackStateCompat#STATE_SKIPPING_TO_PREVIOUS}</li> * <li> {@link PlaybackStateCompat#STATE_SKIPPING_TO_NEXT}</li> * <li> {@link PlaybackStateCompat#STATE_SKIPPING_TO_QUEUE_ITEM}</li> * </ul> * * @param state The current state of playback. * @param position The position in the current item in ms. * @param playbackSpeed The current speed of playback as a multiple of * normal playback. * @param updateTime The time in the {@link SystemClock#elapsedRealtime} * timebase that the position was updated at. * @return this */ public Builder setState(@State int state, long position, float playbackSpeed, long updateTime) { mState = state; mPosition = position; mUpdateTime = updateTime; mRate = playbackSpeed; return this; } /** * Set the current buffered position in ms. This is the farthest * playback point that can be reached from the current position using * only buffered content. * * @return this */ public Builder setBufferedPosition(long bufferPosition) { mBufferedPosition = bufferPosition; return this; } /** * Set the current capabilities available on this session. This should * use a bitmask of the available capabilities. * <ul> * <li> {@link PlaybackStateCompat#ACTION_SKIP_TO_PREVIOUS}</li> * <li> {@link PlaybackStateCompat#ACTION_REWIND}</li> * <li> {@link PlaybackStateCompat#ACTION_PLAY}</li> * <li> {@link PlaybackStateCompat#ACTION_PLAY_PAUSE}</li> * <li> {@link PlaybackStateCompat#ACTION_PAUSE}</li> * <li> {@link PlaybackStateCompat#ACTION_STOP}</li> * <li> {@link PlaybackStateCompat#ACTION_FAST_FORWARD}</li> * <li> {@link PlaybackStateCompat#ACTION_SKIP_TO_NEXT}</li> * <li> {@link PlaybackStateCompat#ACTION_SEEK_TO}</li> * <li> {@link PlaybackStateCompat#ACTION_SET_RATING}</li> * <li> {@link PlaybackStateCompat#ACTION_PLAY_FROM_MEDIA_ID}</li> * <li> {@link PlaybackStateCompat#ACTION_PLAY_FROM_SEARCH}</li> * <li> {@link PlaybackStateCompat#ACTION_SKIP_TO_QUEUE_ITEM}</li> * <li> {@link PlaybackStateCompat#ACTION_PLAY_FROM_URI}</li> * <li> {@link PlaybackStateCompat#ACTION_PREPARE}</li> * <li> {@link PlaybackStateCompat#ACTION_PREPARE_FROM_MEDIA_ID}</li> * <li> {@link PlaybackStateCompat#ACTION_PREPARE_FROM_SEARCH}</li> * <li> {@link PlaybackStateCompat#ACTION_PREPARE_FROM_URI}</li> * <li> {@link PlaybackStateCompat#ACTION_SET_REPEAT_MODE}</li> * <li> {@link PlaybackStateCompat#ACTION_SET_SHUFFLE_MODE}</li> * <li> {@link PlaybackStateCompat#ACTION_SET_CAPTIONING_ENABLED}</li> * </ul> * * @return this */ public Builder setActions(@Actions long capabilities) { mActions = capabilities; return this; } /** * Add a custom action to the playback state. Actions can be used to * expose additional functionality to {@link MediaControllerCompat * Controllers} beyond what is offered by the standard transport * controls. * <p> * e.g. start a radio station based on the current item or skip ahead by * 30 seconds. * * @param action An identifier for this action. It can be sent back to * the {@link MediaSessionCompat} through * {@link MediaControllerCompat.TransportControls#sendCustomAction(String, Bundle)}. * @param name The display name for the action. If text is shown with * the action or used for accessibility, this is what should * be used. * @param icon The resource action of the icon that should be displayed * for the action. The resource should be in the package of * the {@link MediaSessionCompat}. * @return this */ public Builder addCustomAction(String action, String name, int icon) { return addCustomAction(new PlaybackStateCompat.CustomAction(action, name, icon, null)); } /** * Add a custom action to the playback state. Actions can be used to expose additional * functionality to {@link MediaControllerCompat Controllers} beyond what is offered * by the standard transport controls. * <p> * An example of an action would be to start a radio station based on the current item * or to skip ahead by 30 seconds. * * @param customAction The custom action to add to the {@link PlaybackStateCompat}. * @return this */ public Builder addCustomAction(PlaybackStateCompat.CustomAction customAction) { if (customAction == null) { throw new IllegalArgumentException("You may not add a null CustomAction to PlaybackStateCompat"); } mCustomActions.add(customAction); return this; } /** * Set the active item in the play queue by specifying its id. The * default value is {@link MediaSessionCompat.QueueItem#UNKNOWN_ID} * * @param id The id of the active item. * @return this */ public Builder setActiveQueueItemId(long id) { mActiveItemId = id; return this; } /** * Set a user readable error message. This should be set when the state * is {@link PlaybackStateCompat#STATE_ERROR}. * * @return this * @deprecated Use {@link #setErrorMessage(int, CharSequence)} instead. */ public Builder setErrorMessage(CharSequence errorMessage) { mErrorMessage = errorMessage; return this; } /** * Set the error code with an optional user readable error message. This should be set when * the state is {@link PlaybackStateCompat#STATE_ERROR}. * * @param errorCode The errorCode to set. * @param errorMessage The user readable error message. Can be null. * @return this */ public Builder setErrorMessage(@ErrorCode int errorCode, CharSequence errorMessage) { mErrorCode = errorCode; mErrorMessage = errorMessage; return this; } /** * Set any custom extras to be included with the playback state. * * @param extras The extras to include. * @return this */ public Builder setExtras(Bundle extras) { mExtras = extras; return this; } /** * Creates the playback state object. */ public PlaybackStateCompat build() { return new PlaybackStateCompat(mState, mPosition, mBufferedPosition, mRate, mActions, mErrorCode, mErrorMessage, mUpdateTime, mCustomActions, mActiveItemId, mExtras); } } }