Merge changes I51f2e466,I5b67cb3e into lmp-dev

* changes:
  Make PlaybackState immutable with a builder
  Add API to set a default session in Activity
This commit is contained in:
RoboErik
2014-07-14 21:37:49 +00:00
committed by Android (Google) Code Review
11 changed files with 842 additions and 585 deletions

View File

@@ -3309,6 +3309,7 @@ package android.app {
method public android.view.LayoutInflater getLayoutInflater();
method public android.app.LoaderManager getLoaderManager();
method public java.lang.String getLocalClassName();
method public final android.media.session.MediaController getMediaController();
method public android.view.MenuInflater getMenuInflater();
method public final android.app.Activity getParent();
method public android.content.Intent getParentActivityIntent();
@@ -3432,6 +3433,7 @@ package android.app {
method public void setFinishOnTouchOutside(boolean);
method public void setImmersive(boolean);
method public void setIntent(android.content.Intent);
method public final void setMediaController(android.media.session.MediaController);
method public boolean setMediaPlaying(boolean);
method public final void setProgress(int);
method public final void setProgressBarIndeterminate(boolean);
@@ -16224,19 +16226,14 @@ package android.media.session {
}
public final class PlaybackState implements android.os.Parcelable {
ctor public PlaybackState();
ctor public PlaybackState(android.media.session.PlaybackState);
method public int describeContents();
method public long getActions();
method public long getBufferPosition();
method public java.lang.CharSequence getErrorMessage();
method public float getPlaybackRate();
method public long getLastPositionUpdateTime();
method public float getPlaybackSpeed();
method public long getPosition();
method public int getState();
method public void setActions(long);
method public void setBufferPosition(long);
method public void setErrorMessage(java.lang.CharSequence);
method public void setState(int, long, float);
method public void writeToParcel(android.os.Parcel, int);
field public static final long ACTION_FAST_FORWARD = 64L; // 0x40L
field public static final long ACTION_PAUSE = 2L; // 0x2L
@@ -16263,6 +16260,17 @@ package android.media.session {
field public static final int STATE_STOPPED = 1; // 0x1
}
public static final class PlaybackState.Builder {
ctor public PlaybackState.Builder();
ctor public PlaybackState.Builder(android.media.session.PlaybackState);
method public android.media.session.PlaybackState build();
method public android.media.session.PlaybackState.Builder setActions(long);
method public android.media.session.PlaybackState.Builder setBufferPosition(long);
method public android.media.session.PlaybackState.Builder setErrorMessage(java.lang.CharSequence);
method public android.media.session.PlaybackState.Builder setState(int, long, float, long);
method public android.media.session.PlaybackState.Builder setState(int, long, float);
}
}
package android.media.tv {
@@ -34285,6 +34293,7 @@ package android.view {
method protected final int getForcedWindowFlags();
method public abstract android.view.LayoutInflater getLayoutInflater();
method protected final int getLocalFeatures();
method public android.media.session.MediaController getMediaController();
method public abstract int getNavigationBarColor();
method public android.transition.Transition getSharedElementEnterTransition();
method public android.transition.Transition getSharedElementExitTransition();
@@ -34341,6 +34350,7 @@ package android.view {
method public void setLayout(int, int);
method public void setLocalFocus(boolean, boolean);
method public void setLogo(int);
method public void setMediaController(android.media.session.MediaController);
method public abstract void setNavigationBarColor(int);
method public void setSharedElementEnterTransition(android.transition.Transition);
method public void setSharedElementExitTransition(android.transition.Transition);

File diff suppressed because it is too large Load Diff

View File

@@ -23,6 +23,8 @@ import android.content.res.Configuration;
import android.content.res.TypedArray;
import android.graphics.PixelFormat;
import android.graphics.drawable.Drawable;
import android.media.session.MediaController;
import android.media.session.MediaSession;
import android.net.Uri;
import android.os.Bundle;
import android.os.IBinder;
@@ -157,7 +159,7 @@ public abstract class Window {
private static final String PROPERTY_HARDWARE_UI = "persist.sys.ui.hw";
private final Context mContext;
private TypedArray mWindowStyle;
private Callback mCallback;
private OnWindowDismissedCallback mOnWindowDismissedCallback;
@@ -181,7 +183,7 @@ public abstract class Window {
private int mDefaultWindowFormat = PixelFormat.OPAQUE;
private boolean mHasSoftInputMode = false;
private boolean mDestroyed;
// The current window attributes.
@@ -227,7 +229,7 @@ public abstract class Window {
* @return boolean Return true if this event was consumed.
*/
public boolean dispatchTouchEvent(MotionEvent event);
/**
* Called to process trackball events. At the very least your
* implementation must call
@@ -313,14 +315,14 @@ public abstract class Window {
* Called when a panel's menu is opened by the user. This may also be
* called when the menu is changing from one type to another (for
* example, from the icon menu to the expanded menu).
*
*
* @param featureId The panel that the menu is in.
* @param menu The menu that is opened.
* @return Return true to allow the menu to open, or false to prevent
* the menu from opening.
*/
public boolean onMenuOpened(int featureId, Menu menu);
/**
* Called when a panel's menu item has been selected by the user.
*
@@ -364,31 +366,31 @@ public abstract class Window {
* for more information.
*/
public void onAttachedToWindow();
/**
* Called when the window has been attached to the window manager.
* See {@link View#onDetachedFromWindow() View.onDetachedFromWindow()}
* for more information.
*/
public void onDetachedFromWindow();
/**
* Called when a panel is being closed. If another logical subsequent
* panel is being opened (and this panel is being closed to make room for the subsequent
* panel), this method will NOT be called.
*
*
* @param featureId The panel that is being displayed.
* @param menu If onCreatePanelView() returned null, this is the Menu
* being displayed in the panel.
*/
public void onPanelClosed(int featureId, Menu menu);
/**
* Called when the user signals the desire to start a search.
*
*
* @return true if search launched, false if activity refuses (blocks)
*
* @see android.app.Activity#onSearchRequested()
*
* @see android.app.Activity#onSearchRequested()
*/
public boolean onSearchRequested();
@@ -457,7 +459,7 @@ public abstract class Window {
return mWindowStyle;
}
}
/**
* Set the container for this window. If not set, the DecorWindow
* operates as a top-level window; otherwise, it negotiates with the
@@ -488,7 +490,7 @@ public abstract class Window {
public final boolean hasChildren() {
return mHasChildren;
}
/** @hide */
public final void destroy() {
mDestroyed = true;
@@ -622,14 +624,14 @@ public abstract class Window {
* callback will be used to tell you about state changes to the surface.
*/
public abstract void takeSurface(SurfaceHolder.Callback2 callback);
/**
* Take ownership of this window's InputQueue. The window will no
* longer read and dispatch input events from the queue; it is your
* responsibility to do so.
*/
public abstract void takeInputQueue(InputQueue.Callback callback);
/**
* Return whether this window is being displayed with a floating style
* (based on the {@link android.R.attr#windowIsFloating} attribute in
@@ -740,7 +742,7 @@ public abstract class Window {
}
dispatchWindowAttributesChanged(attrs);
}
/**
* Convenience function to set the flag bits as specified in flags, as
* per {@link #setFlags}.
@@ -756,7 +758,7 @@ public abstract class Window {
public void addPrivateFlags(int flags) {
setPrivateFlags(flags, flags);
}
/**
* Convenience function to clear the flag bits as specified in flags, as
* per {@link #setFlags}.
@@ -772,7 +774,7 @@ public abstract class Window {
* Set the flags of the window, as per the
* {@link WindowManager.LayoutParams WindowManager.LayoutParams}
* flags.
*
*
* <p>Note that some flags must be set before the window decoration is
* created (by the first call to
* {@link #setContentView(View, android.view.ViewGroup.LayoutParams)} or
@@ -859,20 +861,20 @@ public abstract class Window {
protected final int getForcedWindowFlags() {
return mForcedWindowFlags;
}
/**
* Has the app specified their own soft input mode?
*/
protected final boolean hasSoftInputMode() {
return mHasSoftInputMode;
}
/** @hide */
public void setCloseOnTouchOutside(boolean close) {
mCloseOnTouchOutside = close;
mSetCloseOnTouchOutside = true;
}
/** @hide */
public void setCloseOnTouchOutsideIfNotSet(boolean close) {
if (!mSetCloseOnTouchOutside) {
@@ -880,10 +882,10 @@ public abstract class Window {
mSetCloseOnTouchOutside = true;
}
}
/** @hide */
public abstract void alwaysReadCloseOnTouchAttr();
/** @hide */
public boolean shouldCloseOnTouch(Context context, MotionEvent event) {
if (mCloseOnTouchOutside && event.getAction() == MotionEvent.ACTION_DOWN
@@ -892,7 +894,7 @@ public abstract class Window {
}
return false;
}
private boolean isOutOfBounds(Context context, MotionEvent event) {
final int x = (int) event.getX();
final int y = (int) event.getY();
@@ -902,7 +904,7 @@ public abstract class Window {
|| (x > (decorView.getWidth()+slop))
|| (y > (decorView.getHeight()+slop));
}
/**
* Enable extended screen features. This must be called before
* setContentView(). May be called as many times as desired as long as it
@@ -989,7 +991,7 @@ public abstract class Window {
* of the window that can not, from this point forward, be changed: the
* features that have been requested with {@link #requestFeature(int)},
* and certain window flags as described in {@link #setFlags(int, int)}.
*
*
* @param view The desired content to display.
* @param params Layout parameters for the view.
*/
@@ -1037,7 +1039,7 @@ public abstract class Window {
public abstract void togglePanel(int featureId, KeyEvent event);
public abstract void invalidatePanelMenu(int featureId);
public abstract boolean performPanelShortcut(int featureId,
int keyCode,
KeyEvent event,
@@ -1052,17 +1054,17 @@ public abstract class Window {
/**
* Should be called when the configuration is changed.
*
*
* @param newConfig The new configuration.
*/
public abstract void onConfigurationChanged(Configuration newConfig);
/**
* Change the background of this window to a Drawable resource. Setting the
* background to null will make the window be opaque. To make the window
* transparent, you can use an empty drawable (for instance a ColorDrawable
* with the color 0 or the system drawable android:drawable/empty.)
*
*
* @param resid The resource identifier of a drawable resource which will be
* installed as the new background.
*/
@@ -1173,7 +1175,7 @@ public abstract class Window {
*
*/
public abstract boolean superDispatchTouchEvent(MotionEvent event);
/**
* Used by custom windows, such as Dialog, to pass the trackball event
* further down the view hierarchy. Application developers should
@@ -1181,7 +1183,7 @@ public abstract class Window {
*
*/
public abstract boolean superDispatchTrackballEvent(MotionEvent event);
/**
* Used by custom windows, such as Dialog, to pass the generic motion event
* further down the view hierarchy. Application developers should
@@ -1194,11 +1196,11 @@ public abstract class Window {
* Retrieve the top-level window decor view (containing the standard
* window frame/decorations and the client's content inside of that), which
* can be added as a window to the window manager.
*
*
* <p><em>Note that calling this function for the first time "locks in"
* various window characteristics as described in
* {@link #setContentView(View, android.view.ViewGroup.LayoutParams)}.</em></p>
*
*
* @return Returns the top-level window decor view.
*/
public abstract View getDecorView();
@@ -1206,16 +1208,16 @@ public abstract class Window {
/**
* Retrieve the current decor view, but only if it has already been created;
* otherwise returns null.
*
*
* @return Returns the top-level window decor or null.
* @see #getDecorView
*/
public abstract View peekDecorView();
public abstract Bundle saveHierarchyState();
public abstract void restoreHierarchyState(Bundle savedInstanceState);
protected abstract void onActive();
/**
@@ -1233,10 +1235,10 @@ public abstract class Window {
{
return mFeatures;
}
/**
* Query for the availability of a certain feature.
*
*
* @param feature The feature ID to check
* @return true if the feature is enabled, false otherwise.
*/
@@ -1290,9 +1292,9 @@ public abstract class Window {
* @param event the {@link android.view.KeyEvent} to use to help check.
*/
public abstract boolean isShortcutKey(int keyCode, KeyEvent event);
/**
* @see android.app.Activity#setVolumeControlStream(int)
* @see android.app.Activity#setVolumeControlStream(int)
*/
public abstract void setVolumeControlStream(int streamType);
@@ -1301,6 +1303,19 @@ public abstract class Window {
*/
public abstract int getVolumeControlStream();
/**
* @see android.app.Activity#setMediaController(android.media.session.MediaController)
*/
public void setMediaController(MediaController controller) {
}
/**
* @see android.app.Activity#getMediaController()
*/
public MediaController getMediaController() {
return null;
}
/**
* Set extra options that will influence the UI for this window.
* @param uiOptions Flags specifying extra options for this window.

View File

@@ -682,9 +682,13 @@ public class RemoteControlClient
// USE_SESSIONS
if (mSession != null) {
int pbState = PlaybackState.getStateFromRccState(state);
mSessionPlaybackState.setState(pbState, hasPosition ?
mPlaybackPositionMs : PlaybackState.PLAYBACK_POSITION_UNKNOWN,
playbackSpeed);
long position = hasPosition ? mPlaybackPositionMs
: PlaybackState.PLAYBACK_POSITION_UNKNOWN;
PlaybackState.Builder bob = new PlaybackState.Builder(mSessionPlaybackState);
bob.setState(pbState, position, playbackSpeed, SystemClock.elapsedRealtime());
bob.setErrorMessage(null);
mSessionPlaybackState = bob.build();
mSession.setPlaybackState(mSessionPlaybackState);
}
}
@@ -745,8 +749,9 @@ public class RemoteControlClient
// USE_SESSIONS
if (mSession != null) {
mSessionPlaybackState.setActions(PlaybackState
.getActionsFromRccControlFlags(transportControlFlags));
PlaybackState.Builder bob = new PlaybackState.Builder(mSessionPlaybackState);
bob.setActions(PlaybackState.getActionsFromRccControlFlags(transportControlFlags));
mSessionPlaybackState = bob.build();
mSession.setPlaybackState(mSessionPlaybackState);
}
}
@@ -946,7 +951,7 @@ public class RemoteControlClient
/**
* Cache for the current playback state using Session APIs.
*/
private final PlaybackState mSessionPlaybackState = new PlaybackState();
private PlaybackState mSessionPlaybackState = null;
/**
* Cache for metadata using Session APIs. This is re-initialized in apply().

View File

@@ -1020,7 +1020,7 @@ public final class RemoteController
l.onClientPlaybackStateUpdate(playstate);
} else {
l.onClientPlaybackStateUpdate(playstate, state.getLastPositionUpdateTime(),
state.getPosition(), state.getPlaybackRate());
state.getPosition(), state.getPlaybackSpeed());
}
if (state != null) {
l.onClientTransportControlUpdate(PlaybackState.getRccControlFlagsFromActions(state

View File

@@ -184,41 +184,29 @@ public final class PlaybackState implements Parcelable {
*/
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;
private final int mState;
private final long mPosition;
private final long mBufferPosition;
private final float mSpeed;
private final long mActions;
private final CharSequence mErrorMessage;
private final 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(int state, long position, long updateTime, float speed,
long bufferPosition, long actions, CharSequence error) {
mState = state;
mPosition = position;
mSpeed = speed;
mUpdateTime = updateTime;
mBufferPosition = bufferPosition;
mActions = actions;
mErrorMessage = error;
}
private PlaybackState(Parcel in) {
mState = in.readInt();
mPosition = in.readLong();
mRate = in.readFloat();
mSpeed = in.readFloat();
mUpdateTime = in.readLong();
mBufferPosition = in.readLong();
mActions = in.readLong();
@@ -232,7 +220,7 @@ public final class PlaybackState implements Parcelable {
bob.append("state=").append(mState);
bob.append(", position=").append(mPosition);
bob.append(", buffered position=").append(mBufferPosition);
bob.append(", rate=").append(mRate);
bob.append(", speed=").append(mSpeed);
bob.append(", updated=").append(mUpdateTime);
bob.append(", actions=").append(mActions);
bob.append(", error=").append(mErrorMessage);
@@ -249,7 +237,7 @@ public final class PlaybackState implements Parcelable {
public void writeToParcel(Parcel dest, int flags) {
dest.writeInt(mState);
dest.writeLong(mPosition);
dest.writeFloat(mRate);
dest.writeFloat(mSpeed);
dest.writeLong(mUpdateTime);
dest.writeLong(mBufferPosition);
dest.writeLong(mActions);
@@ -271,41 +259,6 @@ public final class PlaybackState implements Parcelable {
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.
*/
@@ -323,23 +276,14 @@ public final class PlaybackState implements Parcelable {
}
/**
* 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
* 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 rate of playback.
* @return The current speed of playback.
*/
public float getPlaybackRate() {
return mRate;
public float getPlaybackSpeed() {
return mSpeed;
}
/**
@@ -361,25 +305,6 @@ public final class PlaybackState implements Parcelable {
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}.
@@ -393,20 +318,11 @@ public final class PlaybackState implements Parcelable {
* 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.
@@ -574,4 +490,175 @@ public final class PlaybackState implements Parcelable {
return new PlaybackState[size];
}
};
/**
* Builder for {@link PlaybackState} objects.
*/
public static final class Builder {
private int mState;
private long mPosition;
private long mBufferPosition;
private float mSpeed;
private long mActions;
private CharSequence mErrorMessage;
private long mUpdateTime;
/**
* Creates an initially empty state builder.
*/
public Builder() {
}
/**
* Creates a builder with the same initial values as those in the from
* state.
*
* @param from The state to use for initializing the builder.
*/
public Builder(PlaybackState from) {
if (from == null) {
return;
}
mState = from.mState;
mPosition = from.mPosition;
mBufferPosition = from.mBufferPosition;
mSpeed = from.mSpeed;
mActions = from.mActions;
mErrorMessage = from.mErrorMessage;
mUpdateTime = from.mUpdateTime;
}
/**
* 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}. When not using an unknown
* position the time at which the position was updated must be provided.
* It is okay to use {@link SystemClock#elapsedRealtime()} if the
* current position was just retrieved.
* <p>
* The speed is a multiple of normal playback and should be 0 when
* paused and negative when rewinding. Normal playback speed 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 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(int state, long position, float playbackSpeed, long updateTime) {
mState = state;
mPosition = position;
mUpdateTime = updateTime;
mSpeed = playbackSpeed;
return this;
}
/**
* 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}. The update time will be set to
* the current {@link SystemClock#elapsedRealtime()}.
* <p>
* The speed is a multiple of normal playback and should be 0 when
* paused and negative when rewinding. Normal playback speed 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 playbackSpeed The current speed of playback as a multiple of
* normal playback.
* @return this
*/
public Builder setState(int state, long position, float playbackSpeed) {
return setState(state, position, playbackSpeed, SystemClock.elapsedRealtime());
}
/**
* Set the current actions available on this session. This should use a
* bitmask of possible 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>
*
* @param actions The set of actions allowed.
* @return this
*/
public Builder setActions(long actions) {
mActions = actions;
return this;
}
/**
* 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.
*
* @param bufferPosition The position in ms that playback is buffered
* to.
* @return this
*/
public Builder setBufferPosition(long bufferPosition) {
mBufferPosition = bufferPosition;
return this;
}
/**
* Set a user readable error message. This should be set when the state
* is {@link PlaybackState#STATE_ERROR}.
*
* @param error The error message for display to the user.
* @return this
*/
public Builder setErrorMessage(CharSequence error) {
mErrorMessage = error;
return this;
}
/**
* Build and return the PlaybackState instance with these values.
*
* @return A new state instance.
*/
public PlaybackState build() {
return new PlaybackState(mState, mPosition, mUpdateTime, mSpeed, mBufferPosition,
mActions, mErrorMessage);
}
}
}

View File

@@ -49,6 +49,9 @@ import android.graphics.PixelFormat;
import android.graphics.Rect;
import android.graphics.drawable.Drawable;
import android.media.AudioManager;
import android.media.session.MediaController;
import android.media.session.MediaSession;
import android.media.session.MediaSessionLegacyHelper;
import android.net.Uri;
import android.os.Bundle;
import android.os.Handler;
@@ -224,6 +227,7 @@ public class PhoneWindow extends Window implements MenuBuilder.Callback {
private boolean mClosingActionMenu;
private int mVolumeControlStreamType = AudioManager.USE_DEFAULT_STREAM_TYPE;
private MediaController mMediaController;
private AudioManager mAudioManager;
private KeyguardManager mKeyguardManager;
@@ -1688,15 +1692,42 @@ public class PhoneWindow extends Window implements MenuBuilder.Callback {
switch (keyCode) {
case KeyEvent.KEYCODE_VOLUME_UP:
case KeyEvent.KEYCODE_VOLUME_DOWN:
case KeyEvent.KEYCODE_VOLUME_DOWN: {
int direction = keyCode == KeyEvent.KEYCODE_VOLUME_UP ? AudioManager.ADJUST_RAISE
: AudioManager.ADJUST_LOWER;
// If we have a session send it the volume command, otherwise
// use the suggested stream.
if (mMediaController != null) {
mMediaController.adjustVolumeBy(direction, AudioManager.FLAG_SHOW_UI);
} else {
MediaSessionLegacyHelper.getHelper(getContext()).sendAdjustVolumeBy(
mVolumeControlStreamType, direction, AudioManager.FLAG_SHOW_UI);
}
return true;
}
case KeyEvent.KEYCODE_VOLUME_MUTE: {
// Similar code is in PhoneFallbackEventHandler in case the window
// doesn't have one of these. In this case, we execute it here and
// eat the event instead, because we have mVolumeControlStreamType
// and they don't.
getAudioManager().handleKeyDown(event, mVolumeControlStreamType);
return true;
}
// These are all the recognized media key codes in
// KeyEvent.isMediaKey()
case KeyEvent.KEYCODE_MEDIA_PLAY:
case KeyEvent.KEYCODE_MEDIA_PAUSE:
case KeyEvent.KEYCODE_MEDIA_PLAY_PAUSE:
case KeyEvent.KEYCODE_MUTE:
case KeyEvent.KEYCODE_HEADSETHOOK:
case KeyEvent.KEYCODE_MEDIA_STOP:
case KeyEvent.KEYCODE_MEDIA_NEXT:
case KeyEvent.KEYCODE_MEDIA_PREVIOUS:
case KeyEvent.KEYCODE_MEDIA_REWIND:
case KeyEvent.KEYCODE_MEDIA_RECORD:
case KeyEvent.KEYCODE_MEDIA_FAST_FORWARD: {
if (mMediaController != null) {
if (mMediaController.dispatchMediaButtonEvent(event)) {
return true;
}
}
}
case KeyEvent.KEYCODE_MENU: {
onKeyDownPanel((featureId < 0) ? FEATURE_OPTIONS_PANEL : featureId, event);
@@ -1750,7 +1781,19 @@ public class PhoneWindow extends Window implements MenuBuilder.Callback {
switch (keyCode) {
case KeyEvent.KEYCODE_VOLUME_UP:
case KeyEvent.KEYCODE_VOLUME_DOWN:
case KeyEvent.KEYCODE_VOLUME_DOWN: {
// If we have a session send it the volume command, otherwise
// use the suggested stream.
if (mMediaController != null) {
mMediaController.adjustVolumeBy(0, AudioManager.FLAG_PLAY_SOUND
| AudioManager.FLAG_VIBRATE);
} else {
MediaSessionLegacyHelper.getHelper(getContext()).sendAdjustVolumeBy(
mVolumeControlStreamType, 0,
AudioManager.FLAG_PLAY_SOUND | AudioManager.FLAG_VIBRATE);
}
return true;
}
case KeyEvent.KEYCODE_VOLUME_MUTE: {
// Similar code is in PhoneFallbackEventHandler in case the window
// doesn't have one of these. In this case, we execute it here and
@@ -1759,6 +1802,25 @@ public class PhoneWindow extends Window implements MenuBuilder.Callback {
getAudioManager().handleKeyUp(event, mVolumeControlStreamType);
return true;
}
// These are all the recognized media key codes in
// KeyEvent.isMediaKey()
case KeyEvent.KEYCODE_MEDIA_PLAY:
case KeyEvent.KEYCODE_MEDIA_PAUSE:
case KeyEvent.KEYCODE_MEDIA_PLAY_PAUSE:
case KeyEvent.KEYCODE_MUTE:
case KeyEvent.KEYCODE_HEADSETHOOK:
case KeyEvent.KEYCODE_MEDIA_STOP:
case KeyEvent.KEYCODE_MEDIA_NEXT:
case KeyEvent.KEYCODE_MEDIA_PREVIOUS:
case KeyEvent.KEYCODE_MEDIA_REWIND:
case KeyEvent.KEYCODE_MEDIA_RECORD:
case KeyEvent.KEYCODE_MEDIA_FAST_FORWARD: {
if (mMediaController != null) {
if (mMediaController.dispatchMediaButtonEvent(event)) {
return true;
}
}
}
case KeyEvent.KEYCODE_MENU: {
onKeyUpPanel(featureId < 0 ? FEATURE_OPTIONS_PANEL : featureId,
@@ -3773,6 +3835,16 @@ public class PhoneWindow extends Window implements MenuBuilder.Callback {
return mVolumeControlStreamType;
}
@Override
public void setMediaController(MediaController controller) {
mMediaController = controller;
}
@Override
public MediaController getMediaController() {
return mMediaController;
}
private boolean isTranslucent() {
TypedArray a = getWindowStyle();
return a.getBoolean(a.getResourceId(

View File

@@ -505,16 +505,19 @@ public class MediaSessionRecord implements IBinder.DeathRecipient {
|| state.getState() == PlaybackState.STATE_FAST_FORWARDING
|| state.getState() == PlaybackState.STATE_REWINDING) {
long updateTime = state.getLastPositionUpdateTime();
long currentTime = SystemClock.elapsedRealtime();
if (updateTime > 0) {
long position = (long) (state.getPlaybackRate()
* (SystemClock.elapsedRealtime() - updateTime)) + state.getPosition();
long position = (long) (state.getPlaybackSpeed()
* (currentTime - updateTime)) + state.getPosition();
if (duration >= 0 && position > duration) {
position = duration;
} else if (position < 0) {
position = 0;
}
result = new PlaybackState(state);
result.setState(state.getState(), position, state.getPlaybackRate());
PlaybackState.Builder builder = new PlaybackState.Builder(state);
builder.setState(state.getState(), position, state.getPlaybackSpeed(),
currentTime);
result = builder.build();
}
}
}

View File

@@ -18,12 +18,14 @@ package com.android.onemedia;
import android.media.MediaMetadata;
import android.media.session.MediaController;
import android.media.session.MediaSession;
import android.media.session.MediaSessionManager;
import android.media.session.PlaybackState;
import android.os.Bundle;
import android.os.Handler;
import android.os.IBinder;
import android.os.RemoteException;
import android.app.Activity;
import android.content.ComponentName;
import android.content.Context;
import android.content.Intent;
@@ -43,7 +45,7 @@ public class PlayerController {
protected MediaController.TransportControls mTransportControls;
private final Intent mServiceIntent;
private Context mContext;
private Activity mContext;
private Listener mListener;
private SessionCallback mControllerCb;
private MediaSessionManager mManager;
@@ -51,7 +53,7 @@ public class PlayerController {
private boolean mResumed;
public PlayerController(Context context, Intent serviceIntent) {
public PlayerController(Activity context, Intent serviceIntent) {
mContext = context;
if (serviceIntent == null) {
mServiceIntent = new Intent(mContext, PlayerService.class);
@@ -140,6 +142,7 @@ public class PlayerController {
mBinder = null;
mController = null;
mTransportControls = null;
mContext.setMediaController(null);
Log.d(TAG, "Disconnected from PlayerService");
if (mListener != null) {
@@ -151,12 +154,15 @@ public class PlayerController {
public void onServiceConnected(ComponentName name, IBinder service) {
mBinder = IPlayerService.Stub.asInterface(service);
Log.d(TAG, "service is " + service + " binder is " + mBinder);
MediaSession.Token token;
try {
mController = MediaController.fromToken(mBinder.getSessionToken());
token = mBinder.getSessionToken();
} catch (RemoteException e) {
Log.e(TAG, "Error getting session", e);
return;
}
mController = MediaController.fromToken(token);
mContext.setMediaController(mController);
mController.addCallback(mControllerCb, mHandler);
mTransportControls = mController.getTransportControls();
Log.d(TAG, "Ready to use PlayerService");

View File

@@ -28,6 +28,7 @@ import android.media.session.PlaybackState;
import android.os.Bundle;
import android.support.media.protocols.MediaPlayerProtocol;
import android.support.media.protocols.MediaPlayerProtocol.MediaStatus;
import android.os.SystemClock;
import android.util.Log;
import android.view.KeyEvent;
@@ -59,9 +60,9 @@ public class PlayerSession {
mRenderer = new LocalRenderer(context, null);
mCallback = new SessionCb();
mRenderListener = new RenderListener();
mPlaybackState = new PlaybackState();
mPlaybackState.setActions(PlaybackState.ACTION_PAUSE
| PlaybackState.ACTION_PLAY);
PlaybackState.Builder psBob = new PlaybackState.Builder();
psBob.setActions(PlaybackState.ACTION_PAUSE | PlaybackState.ACTION_PLAY);
mPlaybackState = psBob.build();
mRenderer.registerListener(mRenderListener);
}
@@ -131,7 +132,10 @@ public class PlayerSession {
private void updateState(int newState) {
float rate = newState == PlaybackState.STATE_PLAYING ? 1 : 0;
long position = mRenderer == null ? -1 : mRenderer.getSeekPosition();
mPlaybackState.setState(newState, position, rate);
PlaybackState.Builder bob = new PlaybackState.Builder(mPlaybackState);
bob.setState(newState, position, rate, SystemClock.elapsedRealtime());
bob.setErrorMessage(null);
mPlaybackState = bob.build();
mSession.setPlaybackState(mPlaybackState);
}
@@ -144,10 +148,12 @@ public class PlayerSession {
@Override
public void onError(int type, int extra, Bundle extras, Throwable error) {
Log.d(TAG, "Sending onError with type " + type + " and extra " + extra);
mPlaybackState.setState(PlaybackState.STATE_ERROR, -1, 0);
PlaybackState.Builder bob = new PlaybackState.Builder(mPlaybackState);
bob.setState(PlaybackState.STATE_ERROR, -1, 0, 0);
if (error != null) {
mPlaybackState.setErrorMessage(error.getLocalizedMessage());
bob.setErrorMessage(error.getLocalizedMessage());
}
mPlaybackState = bob.build();
mSession.setPlaybackState(mPlaybackState);
if (mListener != null) {
mListener.onPlayStateChanged(mPlaybackState);
@@ -156,36 +162,41 @@ public class PlayerSession {
@Override
public void onStateChanged(int newState) {
if (newState != Renderer.STATE_ERROR) {
mPlaybackState.setErrorMessage(null);
}
long position = -1;
if (mRenderer != null) {
position = mRenderer.getSeekPosition();
}
int pbState;
float rate = 0;
String errorMsg = null;
switch (newState) {
case Renderer.STATE_ENDED:
case Renderer.STATE_STOPPED:
mPlaybackState.setState(PlaybackState.STATE_STOPPED, position, 0);
pbState = PlaybackState.STATE_STOPPED;
break;
case Renderer.STATE_INIT:
case Renderer.STATE_PREPARING:
mPlaybackState.setState(PlaybackState.STATE_BUFFERING, position, 0);
pbState = PlaybackState.STATE_BUFFERING;
break;
case Renderer.STATE_ERROR:
mPlaybackState.setState(PlaybackState.STATE_ERROR, position, 0);
pbState = PlaybackState.STATE_ERROR;
break;
case Renderer.STATE_PAUSED:
mPlaybackState.setState(PlaybackState.STATE_PAUSED, position, 0);
pbState = PlaybackState.STATE_PAUSED;
break;
case Renderer.STATE_PLAYING:
mPlaybackState.setState(PlaybackState.STATE_PLAYING, position, 1);
pbState = PlaybackState.STATE_PLAYING;
rate = 1;
break;
default:
mPlaybackState.setState(PlaybackState.STATE_ERROR, position, 0);
mPlaybackState.setErrorMessage("unkown state");
pbState = PlaybackState.STATE_ERROR;
errorMsg = "unknown state";
break;
}
PlaybackState.Builder bob = new PlaybackState.Builder(mPlaybackState);
bob.setState(pbState, position, rate, SystemClock.elapsedRealtime());
bob.setErrorMessage(errorMsg);
mPlaybackState = bob.build();
mSession.setPlaybackState(mPlaybackState);
if (mListener != null) {
mListener.onPlayStateChanged(mPlaybackState);
@@ -200,7 +211,10 @@ public class PlayerSession {
public void onFocusLost() {
Log.d(TAG, "Focus lost, changing state to " + Renderer.STATE_PAUSED);
long position = mRenderer == null ? -1 : mRenderer.getSeekPosition();
mPlaybackState.setState(PlaybackState.STATE_PAUSED, position, 0);
PlaybackState.Builder bob = new PlaybackState.Builder(mPlaybackState);
bob.setState(PlaybackState.STATE_PAUSED, position, 0, SystemClock.elapsedRealtime());
bob.setErrorMessage(null);
mPlaybackState = bob.build();
mSession.setPlaybackState(mPlaybackState);
if (mListener != null) {
mListener.onPlayStateChanged(mPlaybackState);

View File

@@ -29,6 +29,9 @@ import android.os.Process;
import android.support.media.protocols.MediaPlayerProtocol;
import android.support.media.protocols.MediaPlayerProtocol.MediaInfo;
import android.support.media.protocols.MediaPlayerProtocol.MediaStatus;
import android.os.Looper;
import android.os.ResultReceiver;
import android.os.SystemClock;
import android.util.Log;
import com.android.onemedia.playback.LocalRenderer;
@@ -60,9 +63,9 @@ public class OneMediaRouteProvider extends MediaRouteService {
mHandler = new Handler();
mRenderer = new LocalRenderer(this, null);
mRenderListener = new RenderListener();
mPlaybackState = new PlaybackState();
mPlaybackState.setActions(PlaybackState.ACTION_PAUSE
| PlaybackState.ACTION_PLAY);
PlaybackState.Builder bob = new PlaybackState.Builder();
bob.setActions(PlaybackState.ACTION_PAUSE | PlaybackState.ACTION_PLAY);
mPlaybackState = bob.build();
mRenderer.registerListener(mRenderListener);
}
@@ -178,36 +181,41 @@ public class OneMediaRouteProvider extends MediaRouteService {
@Override
public void onStateChanged(int newState) {
if (newState != Renderer.STATE_ERROR) {
mPlaybackState.setErrorMessage(null);
}
long position = -1;
if (mRenderer != null) {
position = mRenderer.getSeekPosition();
}
int pbState;
float rate = 0;
String errorMsg = null;
switch (newState) {
case Renderer.STATE_ENDED:
case Renderer.STATE_STOPPED:
mPlaybackState.setState(PlaybackState.STATE_STOPPED, position, 0);
pbState = PlaybackState.STATE_STOPPED;
break;
case Renderer.STATE_INIT:
case Renderer.STATE_PREPARING:
mPlaybackState.setState(PlaybackState.STATE_BUFFERING, position, 0);
pbState = PlaybackState.STATE_BUFFERING;
break;
case Renderer.STATE_ERROR:
mPlaybackState.setState(PlaybackState.STATE_ERROR, position, 0);
pbState = PlaybackState.STATE_ERROR;
break;
case Renderer.STATE_PAUSED:
mPlaybackState.setState(PlaybackState.STATE_PAUSED, position, 0);
pbState = PlaybackState.STATE_PAUSED;
break;
case Renderer.STATE_PLAYING:
mPlaybackState.setState(PlaybackState.STATE_PLAYING, position, 1);
pbState = PlaybackState.STATE_PLAYING;
rate = 1;
break;
default:
mPlaybackState.setState(PlaybackState.STATE_ERROR, position, 0);
mPlaybackState.setErrorMessage("unkown state");
pbState = PlaybackState.STATE_ERROR;
errorMsg = "unknown state";
break;
}
PlaybackState.Builder bob = new PlaybackState.Builder(mPlaybackState);
bob.setState(pbState, position, rate, SystemClock.elapsedRealtime());
bob.setErrorMessage(errorMsg);
mPlaybackState = bob.build();
sendStatusUpdate(mPlaybackState.getState());
}
@@ -218,8 +226,9 @@ public class OneMediaRouteProvider extends MediaRouteService {
@Override
public void onFocusLost() {
Log.d(TAG, "Focus lost, changing state to " + Renderer.STATE_PAUSED);
mPlaybackState.setState(PlaybackState.STATE_PAUSED, mRenderer.getSeekPosition(), 0);
Log.d(TAG, "Focus lost, pausing");
// Don't update state here, we'll get a separate call to
// onStateChanged when it pauses
mRenderer.onPause();
}