blob: 3b3f24960a3917ea33f7825caf5abe947a09d43c [file] [log] [blame]
/*
* 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.media.session;
import android.media.RemoteControlClient;
import android.os.Parcel;
import android.os.Parcelable;
import android.os.SystemClock;
/**
* Playback state for a {@link MediaSession}. This includes a state like
* {@link PlaybackState#STATE_PLAYING}, the current playback position,
* and the current control capabilities.
*/
public final class PlaybackState implements Parcelable {
/**
* Indicates this performer supports the stop command.
*
* @see #setActions
*/
public static final long ACTION_STOP = 1 << 0;
/**
* Indicates this performer supports the pause command.
*
* @see #setActions
*/
public static final long ACTION_PAUSE = 1 << 1;
/**
* Indicates this performer supports the play command.
*
* @see #setActions
*/
public static final long ACTION_PLAY = 1 << 2;
/**
* Indicates this performer supports the rewind command.
*
* @see #setActions
*/
public static final long ACTION_REWIND = 1 << 3;
/**
* Indicates this performer supports the previous command.
*
* @see #setActions
*/
public static final long ACTION_SKIP_TO_PREVIOUS = 1 << 4;
/**
* Indicates this performer supports the next command.
*
* @see #setActions
*/
public static final long ACTION_SKIP_TO_NEXT = 1 << 5;
/**
* Indicates this performer supports the fast forward command.
*
* @see #setActions
*/
public static final long ACTION_FAST_FORWARD = 1 << 6;
/**
* Indicates this performer supports the set rating command.
*
* @see #setActions
*/
public static final long ACTION_SET_RATING = 1 << 7;
/**
* Indicates this performer supports the seek to command.
*
* @see #setActions
*/
public static final long ACTION_SEEK_TO = 1 << 8;
/**
* Indicates this performer supports the play/pause toggle command.
*
* @see #setActions
*/
public static final long ACTION_PLAY_PAUSE = 1 << 9;
/**
* 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 #setState
*/
public final static int STATE_NONE = 0;
/**
* State indicating this item is currently stopped.
*
* @see #setState
*/
public final static int STATE_STOPPED = 1;
/**
* State indicating this item is currently paused.
*
* @see #setState
*/
public final static int STATE_PAUSED = 2;
/**
* State indicating this item is currently playing.
*
* @see #setState
*/
public final static int STATE_PLAYING = 3;
/**
* State indicating this item is currently fast forwarding.
*
* @see #setState
*/
public final static int STATE_FAST_FORWARDING = 4;
/**
* State indicating this item is currently rewinding.
*
* @see #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 #setState
*/
public final static int STATE_BUFFERING = 6;
/**
* State indicating this item is currently in an error state. The error
* message should also be set when entering this state.
*
* @see #setState
*/
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.
* @hide
*/
public final static int STATE_CONNECTING = 8;
/**
* State indicating the player is currently skipping to the previous item.
*
* @see #setState
*/
public final static int STATE_SKIPPING_TO_PREVIOUS = 9;
/**
* State indicating the player is currently skipping to the next item.
*
* @see #setState
*/
public final static int STATE_SKIPPING_TO_NEXT = 10;
/**
* Use this value for the position to indicate the position is not known.
*/
public final static long PLAYBACK_POSITION_UNKNOWN = -1;
private int mState;
private long mPosition;
private long mBufferPosition;
private float mRate;
private long mActions;
private CharSequence mErrorMessage;
private long mUpdateTime;
/**
* Create an empty PlaybackState. At minimum a state and actions should be
* set before publishing a PlaybackState.
*/
public PlaybackState() {
}
/**
* Create a new PlaybackState from an existing PlaybackState. All fields
* will be copied to the new state.
*
* @param from The PlaybackState to duplicate
*/
public PlaybackState(PlaybackState from) {
mState = from.mState;
mPosition = from.mPosition;
mRate = from.mRate;
mUpdateTime = from.mUpdateTime;
mBufferPosition = from.mBufferPosition;
mActions = from.mActions;
mErrorMessage = from.mErrorMessage;
}
private PlaybackState(Parcel in) {
mState = in.readInt();
mPosition = in.readLong();
mRate = in.readFloat();
mUpdateTime = in.readLong();
mBufferPosition = in.readLong();
mActions = in.readLong();
mErrorMessage = in.readCharSequence();
}
@Override
public String toString() {
StringBuilder bob = new StringBuilder("PlaybackState {");
bob.append("state=").append(mState);
bob.append(", position=").append(mPosition);
bob.append(", buffered position=").append(mBufferPosition);
bob.append(", rate=").append(mRate);
bob.append(", updated=").append(mUpdateTime);
bob.append(", actions=").append(mActions);
bob.append(", error=").append(mErrorMessage);
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(mRate);
dest.writeLong(mUpdateTime);
dest.writeLong(mBufferPosition);
dest.writeLong(mActions);
dest.writeCharSequence(mErrorMessage);
}
/**
* Get the current state of playback. One of the following:
* <ul>
* <li> {@link PlaybackState#STATE_NONE}</li>
* <li> {@link PlaybackState#STATE_STOPPED}</li>
* <li> {@link PlaybackState#STATE_PLAYING}</li>
* <li> {@link PlaybackState#STATE_PAUSED}</li>
* <li> {@link PlaybackState#STATE_FAST_FORWARDING}</li>
* <li> {@link PlaybackState#STATE_REWINDING}</li>
* <li> {@link PlaybackState#STATE_BUFFERING}</li>
* <li> {@link PlaybackState#STATE_ERROR}</li>
*/
public int getState() {
return mState;
}
/**
* 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 PlaybackState#STATE_NONE}</li>
* <li> {@link PlaybackState#STATE_STOPPED}</li>
* <li> {@link PlaybackState#STATE_PLAYING}</li>
* <li> {@link PlaybackState#STATE_PAUSED}</li>
* <li> {@link PlaybackState#STATE_FAST_FORWARDING}</li>
* <li> {@link PlaybackState#STATE_REWINDING}</li>
* <li> {@link PlaybackState#STATE_BUFFERING}</li>
* <li> {@link PlaybackState#STATE_ERROR}</li>
* </ul>
*
* @param state The current state of playback.
* @param position The position in the current track in ms.
* @param playbackRate The current rate of playback as a multiple of normal
* playback.
*/
public void setState(int state, long position, float playbackRate) {
this.mState = state;
this.mPosition = position;
this.mRate = playbackRate;
mUpdateTime = SystemClock.elapsedRealtime();
}
/**
* Get the current playback position in ms.
*/
public long getPosition() {
return mPosition;
}
/**
* Get the current buffer position in ms. This is the farthest playback
* point that can be reached from the current position using only buffered
* content.
*/
public long getBufferPosition() {
return mBufferPosition;
}
/**
* Set the current buffer position in ms. This is the farthest playback
* point that can be reached from the current position using only buffered
* content.
*/
public void setBufferPosition(long bufferPosition) {
mBufferPosition = bufferPosition;
}
/**
* Get the current playback rate 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 rate of playback.
*/
public float getPlaybackRate() {
return mRate;
}
/**
* Get the current actions available on this session. This should use a
* bitmask of the available actions.
* <ul>
* <li> {@link PlaybackState#ACTION_SKIP_TO_PREVIOUS}</li>
* <li> {@link PlaybackState#ACTION_REWIND}</li>
* <li> {@link PlaybackState#ACTION_PLAY}</li>
* <li> {@link PlaybackState#ACTION_PAUSE}</li>
* <li> {@link PlaybackState#ACTION_STOP}</li>
* <li> {@link PlaybackState#ACTION_FAST_FORWARD}</li>
* <li> {@link PlaybackState#ACTION_SKIP_TO_NEXT}</li>
* <li> {@link PlaybackState#ACTION_SEEK_TO}</li>
* <li> {@link PlaybackState#ACTION_SET_RATING}</li>
* </ul>
*/
public long getActions() {
return mActions;
}
/**
* Set the current capabilities available on this session. This should use a
* bitmask of the available capabilities.
* <ul>
* <li> {@link PlaybackState#ACTION_SKIP_TO_PREVIOUS}</li>
* <li> {@link PlaybackState#ACTION_REWIND}</li>
* <li> {@link PlaybackState#ACTION_PLAY}</li>
* <li> {@link PlaybackState#ACTION_PAUSE}</li>
* <li> {@link PlaybackState#ACTION_STOP}</li>
* <li> {@link PlaybackState#ACTION_FAST_FORWARD}</li>
* <li> {@link PlaybackState#ACTION_SKIP_TO_NEXT}</li>
* <li> {@link PlaybackState#ACTION_SEEK_TO}</li>
* <li> {@link PlaybackState#ACTION_SET_RATING}</li>
* </ul>
*/
public void setActions(long capabilities) {
mActions = capabilities;
}
/**
* Get a user readable error message. This should be set when the state is
* {@link PlaybackState#STATE_ERROR}.
*/
public CharSequence getErrorMessage() {
return mErrorMessage;
}
/**
* 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.
* @hide
*/
public long getLastPositionUpdateTime() {
return mUpdateTime;
}
/**
* Set a user readable error message. This should be set when the state is
* {@link PlaybackState#STATE_ERROR}.
*/
public void setErrorMessage(CharSequence errorMessage) {
mErrorMessage = errorMessage;
}
/**
* Get the {@link PlaybackState} state for the given
* {@link RemoteControlClient} state.
*
* @param rccState The state used by {@link RemoteControlClient}.
* @return The equivalent state used by {@link PlaybackState}.
* @hide
*/
public static int getStateFromRccState(int rccState) {
switch (rccState) {
case RemoteControlClient.PLAYSTATE_BUFFERING:
return STATE_BUFFERING;
case RemoteControlClient.PLAYSTATE_ERROR:
return STATE_ERROR;
case RemoteControlClient.PLAYSTATE_FAST_FORWARDING:
return STATE_FAST_FORWARDING;
case RemoteControlClient.PLAYSTATE_NONE:
return STATE_NONE;
case RemoteControlClient.PLAYSTATE_PAUSED:
return STATE_PAUSED;
case RemoteControlClient.PLAYSTATE_PLAYING:
return STATE_PLAYING;
case RemoteControlClient.PLAYSTATE_REWINDING:
return STATE_REWINDING;
case RemoteControlClient.PLAYSTATE_SKIPPING_BACKWARDS:
return STATE_SKIPPING_TO_PREVIOUS;
case RemoteControlClient.PLAYSTATE_SKIPPING_FORWARDS:
return STATE_SKIPPING_TO_NEXT;
case RemoteControlClient.PLAYSTATE_STOPPED:
return STATE_STOPPED;
default:
return -1;
}
}
/**
* Get the {@link RemoteControlClient} state for the given
* {@link PlaybackState} state.
*
* @param state The state used by {@link PlaybackState}.
* @return The equivalent state used by {@link RemoteControlClient}.
* @hide
*/
public static int getRccStateFromState(int state) {
switch (state) {
case STATE_BUFFERING:
return RemoteControlClient.PLAYSTATE_BUFFERING;
case STATE_ERROR:
return RemoteControlClient.PLAYSTATE_ERROR;
case STATE_FAST_FORWARDING:
return RemoteControlClient.PLAYSTATE_FAST_FORWARDING;
case STATE_NONE:
return RemoteControlClient.PLAYSTATE_NONE;
case STATE_PAUSED:
return RemoteControlClient.PLAYSTATE_PAUSED;
case STATE_PLAYING:
return RemoteControlClient.PLAYSTATE_PLAYING;
case STATE_REWINDING:
return RemoteControlClient.PLAYSTATE_REWINDING;
case STATE_SKIPPING_TO_PREVIOUS:
return RemoteControlClient.PLAYSTATE_SKIPPING_BACKWARDS;
case STATE_SKIPPING_TO_NEXT:
return RemoteControlClient.PLAYSTATE_SKIPPING_FORWARDS;
case STATE_STOPPED:
return RemoteControlClient.PLAYSTATE_STOPPED;
default:
return -1;
}
}
/**
* @hide
*/
public static long getActionsFromRccControlFlags(int rccFlags) {
long actions = 0;
long flag = 1;
while (flag <= rccFlags) {
if ((flag & rccFlags) != 0) {
actions |= getActionForRccFlag((int) flag);
}
flag = flag << 1;
}
return actions;
}
/**
* @hide
*/
public static int getRccControlFlagsFromActions(long actions) {
int rccFlags = 0;
long action = 1;
while (action <= actions && action < Integer.MAX_VALUE) {
if ((action & actions) != 0) {
rccFlags |= getRccFlagForAction(action);
}
action = action << 1;
}
return rccFlags;
}
private static long getActionForRccFlag(int flag) {
switch (flag) {
case RemoteControlClient.FLAG_KEY_MEDIA_PREVIOUS:
return ACTION_SKIP_TO_PREVIOUS;
case RemoteControlClient.FLAG_KEY_MEDIA_REWIND:
return ACTION_REWIND;
case RemoteControlClient.FLAG_KEY_MEDIA_PLAY:
return ACTION_PLAY;
case RemoteControlClient.FLAG_KEY_MEDIA_PLAY_PAUSE:
return ACTION_PLAY_PAUSE;
case RemoteControlClient.FLAG_KEY_MEDIA_PAUSE:
return ACTION_PAUSE;
case RemoteControlClient.FLAG_KEY_MEDIA_STOP:
return ACTION_STOP;
case RemoteControlClient.FLAG_KEY_MEDIA_FAST_FORWARD:
return ACTION_FAST_FORWARD;
case RemoteControlClient.FLAG_KEY_MEDIA_NEXT:
return ACTION_SKIP_TO_NEXT;
case RemoteControlClient.FLAG_KEY_MEDIA_POSITION_UPDATE:
return ACTION_SEEK_TO;
case RemoteControlClient.FLAG_KEY_MEDIA_RATING:
return ACTION_SET_RATING;
}
return 0;
}
private static int getRccFlagForAction(long action) {
// We only care about the lower set of actions that can map to rcc
// flags.
int testAction = action < Integer.MAX_VALUE ? (int) action : 0;
switch (testAction) {
case (int) ACTION_SKIP_TO_PREVIOUS:
return RemoteControlClient.FLAG_KEY_MEDIA_PREVIOUS;
case (int) ACTION_REWIND:
return RemoteControlClient.FLAG_KEY_MEDIA_REWIND;
case (int) ACTION_PLAY:
return RemoteControlClient.FLAG_KEY_MEDIA_PLAY;
case (int) ACTION_PLAY_PAUSE:
return RemoteControlClient.FLAG_KEY_MEDIA_PLAY_PAUSE;
case (int) ACTION_PAUSE:
return RemoteControlClient.FLAG_KEY_MEDIA_PAUSE;
case (int) ACTION_STOP:
return RemoteControlClient.FLAG_KEY_MEDIA_STOP;
case (int) ACTION_FAST_FORWARD:
return RemoteControlClient.FLAG_KEY_MEDIA_FAST_FORWARD;
case (int) ACTION_SKIP_TO_NEXT:
return RemoteControlClient.FLAG_KEY_MEDIA_NEXT;
case (int) ACTION_SEEK_TO:
return RemoteControlClient.FLAG_KEY_MEDIA_POSITION_UPDATE;
case (int) ACTION_SET_RATING:
return RemoteControlClient.FLAG_KEY_MEDIA_RATING;
}
return 0;
}
public static final Parcelable.Creator<PlaybackState> CREATOR
= new Parcelable.Creator<PlaybackState>() {
@Override
public PlaybackState createFromParcel(Parcel in) {
return new PlaybackState(in);
}
@Override
public PlaybackState[] newArray(int size) {
return new PlaybackState[size];
}
};
}