am 668885a4: Merge "Add Session API calls to RCC and AudioManager"

* commit '668885a41af63ed7a889e7fb2fa939f5ce3150c6':
  Add Session API calls to RCC and AudioManager
This commit is contained in:
RoboErik
2014-05-01 17:20:26 +00:00
committed by Android Git Automerger
11 changed files with 663 additions and 101 deletions

View File

@@ -15103,6 +15103,7 @@ package android.media.routeprovider {
package android.media.session {
public final class MediaMetadata implements android.os.Parcelable {
method public boolean containsKey(java.lang.String);
method public int describeContents();
method public android.graphics.Bitmap getBitmap(java.lang.String);
method public long getLong(java.lang.String);
@@ -15118,6 +15119,7 @@ package android.media.session {
field public static final java.lang.String METADATA_KEY_ARTIST = "android.media.metadata.ARTIST";
field public static final java.lang.String METADATA_KEY_ART_URI = "android.media.metadata.ART_URI";
field public static final java.lang.String METADATA_KEY_AUTHOR = "android.media.metadata.AUTHOR";
field public static final java.lang.String METADATA_KEY_COMPILATION = "android.media.metadata.COMPILATION";
field public static final java.lang.String METADATA_KEY_COMPOSER = "android.media.metadata.COMPOSER";
field public static final java.lang.String METADATA_KEY_DATE = "android.media.metadata.DATE";
field public static final java.lang.String METADATA_KEY_DISC_NUMBER = "android.media.metadata.DISC_NUMBER";
@@ -15150,25 +15152,25 @@ package android.media.session {
method public long getBufferPosition();
method public java.lang.String getErrorMessage();
method public long getPosition();
method public float getSpeed();
method public float getRate();
method public int getState();
method public void setActions(long);
method public void setBufferPosition(long);
method public void setErrorMessage(java.lang.String);
method public void setPosition(long);
method public void setSpeed(float);
method public void setState(int);
method public void setState(int, long, float);
method public void writeToParcel(android.os.Parcel, int);
field public static final long ACTION_FASTFORWARD = 64L; // 0x40L
field public static final long ACTION_NEXT_ITEM = 32L; // 0x20L
field public static final long ACTION_PAUSE = 2L; // 0x2L
field public static final long ACTION_PLAY = 4L; // 0x4L
field public static final long ACTION_PLAY_PAUSE = 512L; // 0x200L
field public static final long ACTION_PREVIOUS_ITEM = 16L; // 0x10L
field public static final long ACTION_RATING = 128L; // 0x80L
field public static final long ACTION_REWIND = 8L; // 0x8L
field public static final long ACTION_SEEK_TO = 256L; // 0x100L
field public static final long ACTION_STOP = 1L; // 0x1L
field public static final android.os.Parcelable.Creator CREATOR;
field public static final long PLAYBACK_POSITION_UNKNOWN = -1L; // 0xffffffffffffffffL
field public static final int PLAYSTATE_BUFFERING = 6; // 0x6
field public static final int PLAYSTATE_CONNECTING = 8; // 0x8
field public static final int PLAYSTATE_ERROR = 7; // 0x7
@@ -15177,6 +15179,8 @@ package android.media.session {
field public static final int PLAYSTATE_PAUSED = 2; // 0x2
field public static final int PLAYSTATE_PLAYING = 3; // 0x3
field public static final int PLAYSTATE_REWINDING = 5; // 0x5
field public static final int PLAYSTATE_SKIPPING_BACKWARDS = 9; // 0x9
field public static final int PLAYSTATE_SKIPPING_FORWARDS = 10; // 0xa
field public static final int PLAYSTATE_STOPPED = 1; // 0x1
}

View File

@@ -25,6 +25,7 @@ import android.content.ComponentName;
import android.content.Context;
import android.content.Intent;
import android.media.RemoteController.OnClientUpdateListener;
import android.media.session.MediaSessionLegacyHelper;
import android.os.Binder;
import android.os.Handler;
import android.os.IBinder;
@@ -48,6 +49,12 @@ import java.util.HashMap;
*/
public class AudioManager {
// If we should use the new sessions APIs.
private final static boolean USE_SESSIONS = true;
// If we should use the legacy APIs. If both are true information will be
// duplicated through both paths. Currently this flag isn't used.
private final static boolean USE_LEGACY = true;
private final Context mContext;
private long mVolumeKeyUpTime;
private final boolean mUseMasterVolume;
@@ -421,6 +428,7 @@ public class AudioManager {
public static final int USE_DEFAULT_STREAM_TYPE = Integer.MIN_VALUE;
private static IAudioService sService;
private MediaSessionLegacyHelper mSessionHelper;
/**
* @hide
@@ -431,6 +439,9 @@ public class AudioManager {
com.android.internal.R.bool.config_useMasterVolume);
mUseVolumeKeySounds = mContext.getResources().getBoolean(
com.android.internal.R.bool.config_useVolumeKeySounds);
if (USE_SESSIONS) {
mSessionHelper = MediaSessionLegacyHelper.getHelper(context);
}
}
private static IAudioService getService()
@@ -2166,6 +2177,9 @@ public class AudioManager {
} catch (RemoteException e) {
Log.e(TAG, "Dead object in registerMediaButtonIntent"+e);
}
if (USE_SESSIONS) {
mSessionHelper.addMediaButtonListener(pi, mContext);
}
}
/**
@@ -2239,6 +2253,9 @@ public class AudioManager {
} catch (RemoteException e) {
Log.e(TAG, "Dead object in unregisterMediaButtonIntent"+e);
}
if (USE_SESSIONS) {
mSessionHelper.removeMediaButtonListener(pi);
}
}
/**
@@ -2263,6 +2280,9 @@ public class AudioManager {
} catch (RemoteException e) {
Log.e(TAG, "Dead object in registerRemoteControlClient"+e);
}
if (USE_SESSIONS) {
rcClient.registerWithSession(mSessionHelper);
}
}
/**
@@ -2282,6 +2302,9 @@ public class AudioManager {
} catch (RemoteException e) {
Log.e(TAG, "Dead object in unregisterRemoteControlClient"+e);
}
if (USE_SESSIONS) {
rcClient.unregisterWithSession(mSessionHelper);
}
}
/**

View File

@@ -17,6 +17,7 @@
package android.media;
import android.graphics.Bitmap;
import android.media.session.MediaMetadata;
import android.os.Bundle;
import android.os.Parcelable;
import android.util.Log;
@@ -106,6 +107,10 @@ public abstract class MediaMetadataEditor {
*/
protected Bundle mEditorMetadata;
/**
* @hide
*/
protected MediaMetadata.Builder mMetadataBuilder;
/**
* Clears all the pending metadata changes set since the MediaMetadataEditor instance was
@@ -120,6 +125,7 @@ public abstract class MediaMetadataEditor {
}
mEditorMetadata.clear();
mEditorArtwork = null;
mMetadataBuilder = new MediaMetadata.Builder();
}
/**

View File

@@ -24,6 +24,11 @@ import android.graphics.Bitmap;
import android.graphics.Canvas;
import android.graphics.Paint;
import android.graphics.RectF;
import android.media.session.MediaMetadata;
import android.media.session.MediaSessionLegacyHelper;
import android.media.session.PlaybackState;
import android.media.session.Session;
import android.media.session.TransportPerformer;
import android.os.Bundle;
import android.os.Handler;
import android.os.IBinder;
@@ -336,6 +341,8 @@ public class RemoteControlClient
*/
public final static int FLAG_INFORMATION_REQUEST_ALBUM_ART = 1 << 3;
private Session mSession;
/**
* Class constructor.
* @param mediaButtonIntent The intent that will be sent for the media button events sent
@@ -384,6 +391,22 @@ public class RemoteControlClient
mEventHandler = new EventHandler(this, looper);
}
/**
* @hide
*/
public void registerWithSession(MediaSessionLegacyHelper helper) {
helper.addRccListener(mRcMediaIntent, mTransportListener);
mSession = helper.getSession(mRcMediaIntent);
}
/**
* @hide
*/
public void unregisterWithSession(MediaSessionLegacyHelper helper) {
helper.removeRccListener(mRcMediaIntent);
mSession = null;
}
/**
* Class used to modify metadata in a {@link RemoteControlClient} object.
* Use {@link RemoteControlClient#editMetadata(boolean)} to create an instance of an editor,
@@ -438,6 +461,15 @@ public class RemoteControlClient
public synchronized MetadataEditor putString(int key, String value)
throws IllegalArgumentException {
super.putString(key, value);
if (mMetadataBuilder != null) {
// MediaMetadata supports all the same fields as MetadataEditor
String metadataKey = MediaMetadata.getKeyFromMetadataEditorKey(key);
// But just in case, don't add things we don't understand
if (metadataKey != null) {
mMetadataBuilder.putString(metadataKey, value);
}
}
return this;
}
@@ -459,6 +491,14 @@ public class RemoteControlClient
public synchronized MetadataEditor putLong(int key, long value)
throws IllegalArgumentException {
super.putLong(key, value);
if (mMetadataBuilder != null) {
// MediaMetadata supports all the same fields as MetadataEditor
String metadataKey = MediaMetadata.getKeyFromMetadataEditorKey(key);
// But just in case, don't add things we don't understand
if (metadataKey != null) {
mMetadataBuilder.putLong(metadataKey, value);
}
}
return this;
}
@@ -476,6 +516,14 @@ public class RemoteControlClient
public synchronized MetadataEditor putBitmap(int key, Bitmap bitmap)
throws IllegalArgumentException {
super.putBitmap(key, bitmap);
if (mMetadataBuilder != null) {
// MediaMetadata supports all the same fields as MetadataEditor
String metadataKey = MediaMetadata.getKeyFromMetadataEditorKey(key);
// But just in case, don't add things we don't understand
if (metadataKey != null) {
mMetadataBuilder.putBitmap(metadataKey, bitmap);
}
}
return this;
}
@@ -501,7 +549,7 @@ public class RemoteControlClient
Log.e(TAG, "Can't apply a previously applied MetadataEditor");
return;
}
synchronized(mCacheLock) {
synchronized (mCacheLock) {
// assign the edited data
mMetadata = new Bundle(mEditorMetadata);
// add the information about editable keys
@@ -521,6 +569,11 @@ public class RemoteControlClient
// send to remote control display if conditions are met
sendArtwork_syncCacheLock(null, 0, 0);
}
// USE_SESSIONS
if (mSession != null && mMetadataBuilder != null) {
mSession.getTransportPerformer().setMetadata(mMetadataBuilder.build());
}
mApplied = true;
}
}
@@ -546,6 +599,12 @@ public class RemoteControlClient
editor.mMetadataChanged = false;
editor.mArtworkChanged = false;
}
// USE_SESSIONS
if (startEmpty || mMediaMetadata == null) {
editor.mMetadataBuilder = new MediaMetadata.Builder();
} else {
editor.mMetadataBuilder = new MediaMetadata.Builder(mMediaMetadata);
}
return editor;
}
@@ -624,6 +683,15 @@ public class RemoteControlClient
// handle automatic playback position refreshes
initiateCheckForDrift_syncCacheLock();
// USE_SESSIONS
if (mSession != null) {
int pbState = PlaybackState.getStateFromRccState(state);
mSessionPlaybackState.setState(pbState, hasPosition ?
mPlaybackPositionMs : PlaybackState.PLAYBACK_POSITION_UNKNOWN,
playbackSpeed);
mSession.getTransportPerformer().setPlaybackState(mSessionPlaybackState);
}
}
}
}
@@ -704,6 +772,13 @@ public class RemoteControlClient
// send to remote control display if conditions are met
sendTransportControlInfo_syncCacheLock(null);
// USE_SESSIONS
if (mSession != null) {
mSessionPlaybackState.setActions(PlaybackState
.getActionsFromRccControlFlags(transportControlFlags));
mSession.getTransportPerformer().setPlaybackState(mSessionPlaybackState);
}
}
}
@@ -1037,6 +1112,16 @@ public class RemoteControlClient
// TODO consider using a ref count for IRemoteControlDisplay requiring sync instead
private boolean mNeedsPositionSync = false;
/**
* Cache for the current playback state using Session APIs.
*/
private final PlaybackState mSessionPlaybackState = new PlaybackState();
/**
* Cache for metadata using Session APIs. This is re-initialized in apply().
*/
private MediaMetadata mMediaMetadata;
/**
* A class to encapsulate all the information about a remote control display.
* A RemoteControlClient's metadata and state may be displayed on multiple IRemoteControlDisplay
@@ -1219,6 +1304,26 @@ public class RemoteControlClient
return mRcseId;
}
// USE_SESSIONS
private TransportPerformer.Listener mTransportListener = new TransportPerformer.Listener() {
@Override
public void onSeekTo(long pos) {
RemoteControlClient.this.onSeekTo(mCurrentClientGenId, pos);
}
@Override
public void onRate(Rating rating) {
if ((mTransportControlFlags & FLAG_KEY_MEDIA_RATING) != 0) {
if (mEventHandler != null) {
mEventHandler.sendMessage(mEventHandler.obtainMessage(
MSG_UPDATE_METADATA, mCurrentClientGenId,
MetadataEditor.RATING_KEY_BY_USER, rating));
}
}
}
};
private EventHandler mEventHandler;
private final static int MSG_REQUEST_PLAYBACK_STATE = 1;
private final static int MSG_REQUEST_METADATA = 2;
@@ -1325,7 +1430,7 @@ public class RemoteControlClient
// target == null implies all displays must be updated
final Iterator<DisplayInfoForClient> displayIterator = mRcDisplays.iterator();
while (displayIterator.hasNext()) {
final DisplayInfoForClient di = (DisplayInfoForClient) displayIterator.next();
final DisplayInfoForClient di = displayIterator.next();
if (di.mEnabled) {
try {
di.mRcDisplay.setPlaybackState(mInternalClientGenId,
@@ -1353,7 +1458,7 @@ public class RemoteControlClient
// target == null implies all displays must be updated
final Iterator<DisplayInfoForClient> displayIterator = mRcDisplays.iterator();
while (displayIterator.hasNext()) {
final DisplayInfoForClient di = (DisplayInfoForClient) displayIterator.next();
final DisplayInfoForClient di = displayIterator.next();
if (di.mEnabled) {
try {
di.mRcDisplay.setMetadata(mInternalClientGenId, mMetadata);
@@ -1381,7 +1486,7 @@ public class RemoteControlClient
// target == null implies all displays must be updated
final Iterator<DisplayInfoForClient> displayIterator = mRcDisplays.iterator();
while (displayIterator.hasNext()) {
final DisplayInfoForClient di = (DisplayInfoForClient) displayIterator.next();
final DisplayInfoForClient di = displayIterator.next();
if (di.mEnabled) {
try {
di.mRcDisplay.setTransportControlInfo(mInternalClientGenId,
@@ -1407,7 +1512,7 @@ public class RemoteControlClient
// target == null implies all displays must be updated
final Iterator<DisplayInfoForClient> displayIterator = mRcDisplays.iterator();
while (displayIterator.hasNext()) {
if (!sendArtworkToDisplay((DisplayInfoForClient) displayIterator.next())) {
if (!sendArtworkToDisplay(displayIterator.next())) {
displayIterator.remove();
}
}
@@ -1453,7 +1558,7 @@ public class RemoteControlClient
// target == null implies all displays must be updated
final Iterator<DisplayInfoForClient> displayIterator = mRcDisplays.iterator();
while (displayIterator.hasNext()) {
final DisplayInfoForClient di = (DisplayInfoForClient) displayIterator.next();
final DisplayInfoForClient di = displayIterator.next();
try {
if (di.mEnabled) {
if ((di.mArtworkExpectedWidth > 0) && (di.mArtworkExpectedHeight > 0)) {
@@ -1537,7 +1642,7 @@ public class RemoteControlClient
boolean displayKnown = false;
final Iterator<DisplayInfoForClient> displayIterator = mRcDisplays.iterator();
while (displayIterator.hasNext() && !displayKnown) {
final DisplayInfoForClient di = (DisplayInfoForClient) displayIterator.next();
final DisplayInfoForClient di = displayIterator.next();
displayKnown = di.mRcDisplay.asBinder().equals(rcd.asBinder());
if (displayKnown) {
// this display was known but the change in artwork size will cause the
@@ -1562,7 +1667,7 @@ public class RemoteControlClient
synchronized(mCacheLock) {
Iterator<DisplayInfoForClient> displayIterator = mRcDisplays.iterator();
while (displayIterator.hasNext()) {
final DisplayInfoForClient di = (DisplayInfoForClient) displayIterator.next();
final DisplayInfoForClient di = displayIterator.next();
if (di.mRcDisplay.asBinder().equals(rcd.asBinder())) {
displayIterator.remove();
break;
@@ -1573,7 +1678,7 @@ public class RemoteControlClient
boolean newNeedsPositionSync = false;
displayIterator = mRcDisplays.iterator();
while (displayIterator.hasNext()) {
final DisplayInfoForClient di = (DisplayInfoForClient) displayIterator.next();
final DisplayInfoForClient di = displayIterator.next();
if (di.mWantsPositionSync) {
newNeedsPositionSync = true;
break;
@@ -1592,7 +1697,7 @@ public class RemoteControlClient
synchronized(mCacheLock) {
final Iterator<DisplayInfoForClient> displayIterator = mRcDisplays.iterator();
while (displayIterator.hasNext()) {
final DisplayInfoForClient di = (DisplayInfoForClient) displayIterator.next();
final DisplayInfoForClient di = displayIterator.next();
if (di.mRcDisplay.asBinder().equals(rcd.asBinder()) &&
((di.mArtworkExpectedWidth != w) || (di.mArtworkExpectedHeight != h))) {
di.mArtworkExpectedWidth = w;
@@ -1617,7 +1722,7 @@ public class RemoteControlClient
// go through the list of RCDs and for each entry, check both whether this is the RCD
// that gets upated, and whether the list has one entry that wants position sync
while (displayIterator.hasNext()) {
final DisplayInfoForClient di = (DisplayInfoForClient) displayIterator.next();
final DisplayInfoForClient di = displayIterator.next();
if (di.mEnabled) {
if (di.mRcDisplay.asBinder().equals(rcd.asBinder())) {
di.mWantsPositionSync = wantsSync;
@@ -1640,7 +1745,7 @@ public class RemoteControlClient
synchronized(mCacheLock) {
final Iterator<DisplayInfoForClient> displayIterator = mRcDisplays.iterator();
while (displayIterator.hasNext()) {
final DisplayInfoForClient di = (DisplayInfoForClient) displayIterator.next();
final DisplayInfoForClient di = displayIterator.next();
if (di.mRcDisplay.asBinder().equals(rcd.asBinder())) {
di.mEnabled = enable;
}

View File

@@ -16,12 +16,15 @@
package android.media.session;
import android.graphics.Bitmap;
import android.media.MediaMetadataEditor;
import android.media.MediaMetadataRetriever;
import android.media.Rating;
import android.os.Bundle;
import android.os.Parcel;
import android.os.Parcelable;
import android.util.ArrayMap;
import android.util.Log;
import android.util.SparseArray;
/**
* Contains metadata about an item, such as the title, artist, etc.
@@ -40,7 +43,8 @@ public final class MediaMetadata implements Parcelable {
public static final String METADATA_KEY_ARTIST = "android.media.metadata.ARTIST";
/**
* The duration of the media in ms. A duration of 0 is the default.
* The duration of the media in ms. A negative duration indicates that the
* duration is unknown (or infinite).
*/
public static final String METADATA_KEY_DURATION = "android.media.metadata.DURATION";
@@ -64,13 +68,18 @@ public final class MediaMetadata implements Parcelable {
*/
public static final String METADATA_KEY_COMPOSER = "android.media.metadata.COMPOSER";
/**
* The compilation status of the media.
*/
public static final String METADATA_KEY_COMPILATION = "android.media.metadata.COMPILATION";
/**
* The date the media was created or published as TODO determine format.
*/
public static final String METADATA_KEY_DATE = "android.media.metadata.DATE";
/**
* The year the media was created or published as a numeric String.
* The year the media was created or published as a long.
*/
public static final String METADATA_KEY_YEAR = "android.media.metadata.YEAR";
@@ -151,8 +160,9 @@ public final class MediaMetadata implements Parcelable {
METADATA_KEYS_TYPE.put(METADATA_KEY_AUTHOR, METADATA_TYPE_STRING);
METADATA_KEYS_TYPE.put(METADATA_KEY_WRITER, METADATA_TYPE_STRING);
METADATA_KEYS_TYPE.put(METADATA_KEY_COMPOSER, METADATA_TYPE_STRING);
METADATA_KEYS_TYPE.put(METADATA_KEY_COMPILATION, METADATA_TYPE_STRING);
METADATA_KEYS_TYPE.put(METADATA_KEY_DATE, METADATA_TYPE_STRING);
METADATA_KEYS_TYPE.put(METADATA_KEY_YEAR, METADATA_TYPE_STRING);
METADATA_KEYS_TYPE.put(METADATA_KEY_YEAR, METADATA_TYPE_LONG);
METADATA_KEYS_TYPE.put(METADATA_KEY_GENRE, METADATA_TYPE_STRING);
METADATA_KEYS_TYPE.put(METADATA_KEY_TRACK_NUMBER, METADATA_TYPE_LONG);
METADATA_KEYS_TYPE.put(METADATA_KEY_NUM_TRACKS, METADATA_TYPE_LONG);
@@ -165,6 +175,36 @@ public final class MediaMetadata implements Parcelable {
METADATA_KEYS_TYPE.put(METADATA_KEY_USER_RATING, METADATA_TYPE_RATING);
METADATA_KEYS_TYPE.put(METADATA_KEY_RATING, METADATA_TYPE_RATING);
}
private static final SparseArray<String> EDITOR_KEY_MAPPING;
static {
EDITOR_KEY_MAPPING = new SparseArray<String>();
EDITOR_KEY_MAPPING.put(MediaMetadataEditor.BITMAP_KEY_ARTWORK, METADATA_KEY_ART);
EDITOR_KEY_MAPPING.put(MediaMetadataEditor.RATING_KEY_BY_OTHERS, METADATA_KEY_RATING);
EDITOR_KEY_MAPPING.put(MediaMetadataEditor.RATING_KEY_BY_USER, METADATA_KEY_USER_RATING);
EDITOR_KEY_MAPPING.put(MediaMetadataRetriever.METADATA_KEY_ALBUM, METADATA_KEY_ALBUM);
EDITOR_KEY_MAPPING.put(MediaMetadataRetriever.METADATA_KEY_ALBUMARTIST,
METADATA_KEY_ALBUM_ARTIST);
EDITOR_KEY_MAPPING.put(MediaMetadataRetriever.METADATA_KEY_ARTIST, METADATA_KEY_ARTIST);
EDITOR_KEY_MAPPING.put(MediaMetadataRetriever.METADATA_KEY_AUTHOR, METADATA_KEY_AUTHOR);
EDITOR_KEY_MAPPING.put(MediaMetadataRetriever.METADATA_KEY_CD_TRACK_NUMBER,
METADATA_KEY_TRACK_NUMBER);
EDITOR_KEY_MAPPING.put(MediaMetadataRetriever.METADATA_KEY_COMPOSER, METADATA_KEY_COMPOSER);
EDITOR_KEY_MAPPING.put(MediaMetadataRetriever.METADATA_KEY_COMPILATION,
METADATA_KEY_COMPILATION);
EDITOR_KEY_MAPPING.put(MediaMetadataRetriever.METADATA_KEY_DATE, METADATA_KEY_DATE);
EDITOR_KEY_MAPPING.put(MediaMetadataRetriever.METADATA_KEY_DISC_NUMBER,
METADATA_KEY_DISC_NUMBER);
EDITOR_KEY_MAPPING.put(MediaMetadataRetriever.METADATA_KEY_DURATION, METADATA_KEY_DURATION);
EDITOR_KEY_MAPPING.put(MediaMetadataRetriever.METADATA_KEY_GENRE, METADATA_KEY_GENRE);
EDITOR_KEY_MAPPING.put(MediaMetadataRetriever.METADATA_KEY_NUM_TRACKS,
METADATA_KEY_NUM_TRACKS);
EDITOR_KEY_MAPPING.put(MediaMetadataRetriever.METADATA_KEY_TITLE, METADATA_KEY_TITLE);
EDITOR_KEY_MAPPING.put(MediaMetadataRetriever.METADATA_KEY_WRITER, METADATA_KEY_WRITER);
EDITOR_KEY_MAPPING.put(MediaMetadataRetriever.METADATA_KEY_YEAR, METADATA_KEY_YEAR);
}
private final Bundle mBundle;
private MediaMetadata(Bundle bundle) {
@@ -175,6 +215,16 @@ public final class MediaMetadata implements Parcelable {
mBundle = in.readBundle();
}
/**
* Returns true if the given key is contained in the metadata
*
* @param key a String key
* @return true if the key exists in this metadata, false otherwise
*/
public boolean containsKey(String key) {
return mBundle.containsKey(key);
}
/**
* Returns the value associated with the given key, or null if no mapping of
* the desired type exists for the given key or a null value is explicitly
@@ -195,7 +245,7 @@ public final class MediaMetadata implements Parcelable {
* @return a long value
*/
public long getLong(String key) {
return mBundle.getLong(key);
return mBundle.getLong(key, 0);
}
/**
@@ -244,6 +294,18 @@ public final class MediaMetadata implements Parcelable {
dest.writeBundle(mBundle);
}
/**
* Helper for getting the String key used by {@link MediaMetadata} from the
* integer key that {@link MediaMetadataEditor} uses.
*
* @param editorKey The key used by the editor
* @return The key used by this class or null if no mapping exists
* @hide
*/
public static String getKeyFromMetadataEditorKey(int editorKey) {
return EDITOR_KEY_MAPPING.get(editorKey, null);
}
public static final Parcelable.Creator<MediaMetadata> CREATOR
= new Parcelable.Creator<MediaMetadata>() {
@Override
@@ -295,10 +357,9 @@ public final class MediaMetadata implements Parcelable {
* <li>{@link #METADATA_KEY_WRITER}</li>
* <li>{@link #METADATA_KEY_COMPOSER}</li>
* <li>{@link #METADATA_KEY_DATE}</li>
* <li>{@link #METADATA_KEY_YEAR}</li>
* <li>{@link #METADATA_KEY_GENRE}</li>
* <li>{@link #METADATA_KEY_ALBUM_ARTIST}</li>li>
* <li>{@link #METADATA_KEY_ART_URI}</li>li>
* <li>{@link #METADATA_KEY_ALBUM_ARTIST}</li>
* <li>{@link #METADATA_KEY_ART_URI}</li>
* <li>{@link #METADATA_KEY_ALBUM_ART_URI}</li>
* </ul>
*
@@ -326,6 +387,7 @@ public final class MediaMetadata implements Parcelable {
* <li>{@link #METADATA_KEY_TRACK_NUMBER}</li>
* <li>{@link #METADATA_KEY_NUM_TRACKS}</li>
* <li>{@link #METADATA_KEY_DISC_NUMBER}</li>
* <li>{@link #METADATA_KEY_YEAR}</li>
* </ul>
*
* @param key The key for referencing this value

View File

@@ -0,0 +1,213 @@
/*
* 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.app.PendingIntent;
import android.app.PendingIntent.CanceledException;
import android.content.Context;
import android.content.Intent;
import android.os.Handler;
import android.os.Looper;
import android.util.ArrayMap;
import android.util.Log;
import android.view.KeyEvent;
/**
* Helper for connecting existing APIs up to the new session APIs. This can be
* used by RCC, AudioFocus, etc. to create a single session that translates to
* all those components.
*
* @hide
*/
public class MediaSessionLegacyHelper {
private static final String TAG = "MediaSessionHelper";
private static final Object sLock = new Object();
private static MediaSessionLegacyHelper sInstance;
private SessionManager mSessionManager;
private Handler mHandler = new Handler(Looper.getMainLooper());
// The legacy APIs use PendingIntents to register/unregister media button
// receivers and these are associated with RCC.
private ArrayMap<PendingIntent, SessionHolder> mSessions = new ArrayMap<PendingIntent, SessionHolder>();
private MediaSessionLegacyHelper(Context context) {
mSessionManager = (SessionManager) context
.getSystemService(Context.MEDIA_SESSION_SERVICE);
}
public static MediaSessionLegacyHelper getHelper(Context context) {
synchronized (sLock) {
if (sInstance == null) {
sInstance = new MediaSessionLegacyHelper(context);
}
}
return sInstance;
}
public Session getSession(PendingIntent pi) {
SessionHolder holder = mSessions.get(pi);
return holder == null ? null : holder.mSession;
}
public void addRccListener(PendingIntent pi, TransportPerformer.Listener listener) {
SessionHolder holder = getHolder(pi, true);
TransportPerformer performer = holder.mSession.getTransportPerformer();
if (holder.mRccListener != null) {
if (holder.mRccListener == listener) {
// This is already the registered listener, ignore
return;
}
// Otherwise it changed so we need to switch to the new one
performer.removeListener(holder.mRccListener);
}
performer.addListener(listener, mHandler);
holder.mRccListener = listener;
holder.update();
}
public void removeRccListener(PendingIntent pi) {
SessionHolder holder = getHolder(pi, false);
if (holder != null && holder.mRccListener != null) {
holder.mSession.getTransportPerformer().removeListener(holder.mRccListener);
holder.mRccListener = null;
holder.update();
}
}
public void addMediaButtonListener(PendingIntent pi,
Context context) {
SessionHolder holder = getHolder(pi, true);
if (holder.mMediaButtonListener != null) {
// Already have this listener registered
return;
}
holder.mMediaButtonListener = new MediaButtonListener(pi, context);
holder.mSession.getTransportPerformer().addListener(holder.mMediaButtonListener, mHandler);
}
public void removeMediaButtonListener(PendingIntent pi) {
SessionHolder holder = getHolder(pi, false);
if (holder != null && holder.mMediaButtonListener != null) {
holder.mSession.getTransportPerformer().removeListener(holder.mMediaButtonListener);
holder.update();
}
}
private SessionHolder getHolder(PendingIntent pi, boolean createIfMissing) {
SessionHolder holder = mSessions.get(pi);
if (holder == null && createIfMissing) {
Session session = mSessionManager.createSession(TAG);
session.setTransportPerformerEnabled();
session.publish();
holder = new SessionHolder(session, pi);
mSessions.put(pi, holder);
}
return holder;
}
public static class MediaButtonListener extends TransportPerformer.Listener {
private final PendingIntent mPendingIntent;
private final Context mContext;
public MediaButtonListener(PendingIntent pi, Context context) {
mPendingIntent = pi;
mContext = context;
}
@Override
public void onPlay() {
sendKeyEvent(KeyEvent.KEYCODE_MEDIA_PLAY);
}
@Override
public void onPause() {
sendKeyEvent(KeyEvent.KEYCODE_MEDIA_PAUSE);
}
@Override
public void onNext() {
sendKeyEvent(KeyEvent.KEYCODE_MEDIA_NEXT);
}
@Override
public void onPrevious() {
sendKeyEvent(KeyEvent.KEYCODE_MEDIA_PREVIOUS);
}
@Override
public void onFastForward() {
sendKeyEvent(KeyEvent.KEYCODE_MEDIA_FAST_FORWARD);
}
@Override
public void onRewind() {
sendKeyEvent(KeyEvent.KEYCODE_MEDIA_REWIND);
}
@Override
public void onStop() {
sendKeyEvent(KeyEvent.KEYCODE_MEDIA_STOP);
}
private void sendKeyEvent(int keyCode) {
KeyEvent ke = new KeyEvent(KeyEvent.ACTION_DOWN, keyCode);
Intent intent = new Intent(Intent.ACTION_MEDIA_BUTTON);
intent.putExtra(Intent.EXTRA_KEY_EVENT, ke);
try {
mPendingIntent.send(mContext, 0, intent);
} catch (CanceledException e) {
Log.e(TAG, "Error sending media key down event:", e);
// Don't bother sending up if down failed
return;
}
ke = new KeyEvent(KeyEvent.ACTION_UP, keyCode);
intent.putExtra(Intent.EXTRA_KEY_EVENT, ke);
try {
mPendingIntent.send(mContext, 0, intent);
} catch (CanceledException e) {
Log.e(TAG, "Error sending media key up event:", e);
}
}
}
private class SessionHolder {
public final Session mSession;
public final PendingIntent mPi;
public MediaButtonListener mMediaButtonListener;
public TransportPerformer.Listener mRccListener;
public SessionHolder(Session session, PendingIntent pi) {
mSession = session;
mPi = pi;
}
public void update() {
if (mMediaButtonListener == null && mRccListener == null) {
mSession.release();
mSessions.remove(mPi);
} else if (mMediaButtonListener != null && mRccListener != null) {
// TODO set session to active
} else {
// TODO set session to inactive
}
}
}
}

View File

@@ -15,8 +15,10 @@
*/
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 Session}. This includes a state like
@@ -87,6 +89,13 @@ public final class PlaybackState implements Parcelable {
*/
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.
@@ -154,12 +163,33 @@ public final class PlaybackState implements Parcelable {
*/
public final static int PLAYSTATE_CONNECTING = 8;
/**
* State indicating the player is currently skipping to the previous item.
*
* @see #setState
*/
public final static int PLAYSTATE_SKIPPING_BACKWARDS = 9;
/**
* State indicating the player is currently skipping to the next item.
*
* @see #setState
*/
public final static int PLAYSTATE_SKIPPING_FORWARDS = 10;
/**
* Set this value on {@link #setPosition(long)} to indicate the position is
* not known for this item.
*/
public final static long PLAYBACK_POSITION_UNKNOWN = -1;
private int mState;
private long mPosition;
private long mBufferPosition;
private float mSpeed;
private long mCapabilities;
private float mRate;
private long mActions;
private String mErrorMessage;
private long mUpdateTime;
/**
* Create an empty PlaybackState. At minimum a state and actions should be
@@ -175,21 +205,24 @@ public final class PlaybackState implements Parcelable {
* @param from The PlaybackState to duplicate
*/
public PlaybackState(PlaybackState from) {
this.setState(from.getState());
this.setPosition(from.getPosition());
this.setBufferPosition(from.getBufferPosition());
this.setSpeed(from.getSpeed());
this.setActions(from.getActions());
this.setErrorMessage(from.getErrorMessage());
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) {
this.setState(in.readInt());
this.setPosition(in.readLong());
this.setBufferPosition(in.readLong());
this.setSpeed(in.readFloat());
this.setActions(in.readLong());
this.setErrorMessage(in.readString());
mState = in.readInt();
mPosition = in.readLong();
mRate = in.readFloat();
mUpdateTime = in.readLong();
mBufferPosition = in.readLong();
mActions = in.readLong();
mErrorMessage = in.readString();
}
@Override
@@ -199,12 +232,13 @@ public final class PlaybackState implements Parcelable {
@Override
public void writeToParcel(Parcel dest, int flags) {
dest.writeInt(getState());
dest.writeLong(getPosition());
dest.writeLong(getBufferPosition());
dest.writeFloat(getSpeed());
dest.writeLong(getActions());
dest.writeString(getErrorMessage());
dest.writeInt(mState);
dest.writeLong(mPosition);
dest.writeFloat(mRate);
dest.writeLong(mUpdateTime);
dest.writeLong(mBufferPosition);
dest.writeLong(mActions);
dest.writeString(mErrorMessage);
}
/**
@@ -224,7 +258,16 @@ public final class PlaybackState implements Parcelable {
}
/**
* Set the current state of playback. One of the following:
* 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#PLAYSTATE_NONE}</li>
* <li> {@link PlaybackState#PLAYSTATE_STOPPED}</li>
@@ -234,9 +277,18 @@ public final class PlaybackState implements Parcelable {
* <li> {@link PlaybackState#PLAYSTATE_REWINDING}</li>
* <li> {@link PlaybackState#PLAYSTATE_BUFFERING}</li>
* <li> {@link PlaybackState#PLAYSTATE_ERROR}</li>
* </ul>
*
* @param state The current state of playback.
* @param position The position in the current track in ms.
* @param rate The current rate of playback as a multiple of normal
* playback.
*/
public void setState(int mState) {
this.mState = mState;
public void setState(int state, long position, float rate) {
this.mState = state;
this.mPosition = position;
this.mRate = rate;
mUpdateTime = SystemClock.elapsedRealtime();
}
/**
@@ -246,13 +298,6 @@ public final class PlaybackState implements Parcelable {
return mPosition;
}
/**
* Set the current playback position in ms.
*/
public void setPosition(long position) {
mPosition = position;
}
/**
* Get the current buffer position in ms. This is the farthest playback
* point that can be reached from the current position using only buffered
@@ -272,21 +317,14 @@ public final class PlaybackState implements Parcelable {
}
/**
* Get the current playback speed as a multiple of normal playback. This
* 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 getSpeed() {
return mSpeed;
}
/**
* Set 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.
*/
public void setSpeed(float speed) {
mSpeed = speed;
public float getRate() {
return mRate;
}
/**
@@ -305,7 +343,7 @@ public final class PlaybackState implements Parcelable {
* </ul>
*/
public long getActions() {
return mCapabilities;
return mActions;
}
/**
@@ -324,7 +362,7 @@ public final class PlaybackState implements Parcelable {
* </ul>
*/
public void setActions(long capabilities) {
mCapabilities = capabilities;
mActions = capabilities;
}
/**
@@ -335,6 +373,17 @@ public final class PlaybackState implements Parcelable {
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#PLAYSTATE_ERROR}.
@@ -343,6 +392,80 @@ public final class PlaybackState implements Parcelable {
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 PLAYSTATE_BUFFERING;
case RemoteControlClient.PLAYSTATE_ERROR:
return PLAYSTATE_ERROR;
case RemoteControlClient.PLAYSTATE_FAST_FORWARDING:
return PLAYSTATE_FAST_FORWARDING;
case RemoteControlClient.PLAYSTATE_NONE:
return PLAYSTATE_NONE;
case RemoteControlClient.PLAYSTATE_PAUSED:
return PLAYSTATE_PAUSED;
case RemoteControlClient.PLAYSTATE_PLAYING:
return PLAYSTATE_PLAYING;
case RemoteControlClient.PLAYSTATE_REWINDING:
return PLAYSTATE_REWINDING;
case RemoteControlClient.PLAYSTATE_SKIPPING_BACKWARDS:
return PLAYSTATE_SKIPPING_BACKWARDS;
case RemoteControlClient.PLAYSTATE_STOPPED:
return 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;
}
private static long getActionForRccFlag(int flag) {
switch (flag) {
case RemoteControlClient.FLAG_KEY_MEDIA_PREVIOUS:
return ACTION_PREVIOUS_ITEM;
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_FASTFORWARD;
case RemoteControlClient.FLAG_KEY_MEDIA_NEXT:
return ACTION_NEXT_ITEM;
case RemoteControlClient.FLAG_KEY_MEDIA_POSITION_UPDATE:
return ACTION_SEEK_TO;
case RemoteControlClient.FLAG_KEY_MEDIA_RATING:
return ACTION_RATING;
}
return 0;
}
public static final Parcelable.Creator<PlaybackState> CREATOR
= new Parcelable.Creator<PlaybackState>() {
@Override

View File

@@ -280,18 +280,11 @@ public final class TransportPerformer {
/**
* Report that audio focus has changed on the app. This only happens if
* you have indicated you have started playing with
* {@link #setPlaybackState}. TODO figure out route focus apis/handling.
* {@link #setPlaybackState}.
*
* @param focusChange The type of focus change, TBD. The default
* implementation will deliver a call to {@link #onPause}
* when focus is lost.
* @param focusChange The type of focus change, TBD.
*/
public void onRouteFocusChange(int focusChange) {
switch (focusChange) {
case AudioManager.AUDIOFOCUS_LOSS:
onPause();
break;
}
}
}

View File

@@ -40,6 +40,7 @@ import android.os.Looper;
import android.os.Message;
import android.os.RemoteException;
import android.os.ResultReceiver;
import android.os.SystemClock;
import android.text.TextUtils;
import android.util.Log;
import android.util.Pair;
@@ -323,6 +324,34 @@ public class MediaSessionRecord implements IBinder.DeathRecipient {
}
}
private PlaybackState getStateWithUpdatedPosition() {
PlaybackState state = mPlaybackState;
long duration = -1;
if (mMetadata != null && mMetadata.containsKey(MediaMetadata.METADATA_KEY_DURATION)) {
duration = mMetadata.getLong(MediaMetadata.METADATA_KEY_DURATION);
}
PlaybackState result = null;
if (state != null) {
if (state.getState() == PlaybackState.PLAYSTATE_PLAYING
|| state.getState() == PlaybackState.PLAYSTATE_FAST_FORWARDING
|| state.getState() == PlaybackState.PLAYSTATE_REWINDING) {
long updateTime = state.getLastPositionUpdateTime();
if (updateTime > 0) {
long position = (long) (state.getRate()
* (SystemClock.elapsedRealtime() - 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.getRate());
}
}
}
return result == null ? state : result;
}
private final RouteConnectionRecord.Listener mConnectionListener
= new RouteConnectionRecord.Listener() {
@Override
@@ -625,7 +654,7 @@ public class MediaSessionRecord implements IBinder.DeathRecipient {
@Override
public PlaybackState getPlaybackState() {
return mPlaybackState;
return getStateWithUpdatedPosition();
}
@Override

View File

@@ -119,7 +119,9 @@ public class PlayerSession {
}
private void updateState(int newState) {
mPlaybackState.setState(newState);
float rate = newState == PlaybackState.PLAYSTATE_PLAYING ? 1 : 0;
long position = mRenderer == null ? -1 : mRenderer.getSeekPosition();
mPlaybackState.setState(newState, position, rate);
mPerformer.setPlaybackState(mPlaybackState);
}
@@ -132,7 +134,7 @@ 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.PLAYSTATE_ERROR);
mPlaybackState.setState(PlaybackState.PLAYSTATE_ERROR, -1, 0);
if (error != null) {
mPlaybackState.setErrorMessage(error.getLocalizedMessage());
}
@@ -147,34 +149,33 @@ public class PlayerSession {
if (newState != Renderer.STATE_ERROR) {
mPlaybackState.setErrorMessage(null);
}
long position = -1;
if (mRenderer != null) {
position = mRenderer.getSeekPosition();
}
switch (newState) {
case Renderer.STATE_ENDED:
case Renderer.STATE_STOPPED:
mPlaybackState.setState(PlaybackState.PLAYSTATE_STOPPED);
mPlaybackState.setState(PlaybackState.PLAYSTATE_STOPPED, position, 0);
break;
case Renderer.STATE_INIT:
case Renderer.STATE_PREPARING:
mPlaybackState.setState(PlaybackState.PLAYSTATE_BUFFERING);
mPlaybackState.setState(PlaybackState.PLAYSTATE_BUFFERING, position, 0);
break;
case Renderer.STATE_ERROR:
mPlaybackState.setState(PlaybackState.PLAYSTATE_ERROR);
mPlaybackState.setState(PlaybackState.PLAYSTATE_ERROR, position, 0);
break;
case Renderer.STATE_PAUSED:
mPlaybackState.setState(PlaybackState.PLAYSTATE_PAUSED);
mPlaybackState.setState(PlaybackState.PLAYSTATE_PAUSED, position, 0);
break;
case Renderer.STATE_PLAYING:
mPlaybackState.setState(PlaybackState.PLAYSTATE_PLAYING);
mPlaybackState.setState(PlaybackState.PLAYSTATE_PLAYING, position, 1);
break;
default:
mPlaybackState.setState(PlaybackState.PLAYSTATE_ERROR);
mPlaybackState.setState(PlaybackState.PLAYSTATE_ERROR, position, 0);
mPlaybackState.setErrorMessage("unkown state");
break;
}
if (mRenderer != null) {
mPlaybackState.setPosition(mRenderer.getSeekPosition());
} else {
mPlaybackState.setPosition(-1);
}
mPerformer.setPlaybackState(mPlaybackState);
if (mListener != null) {
mListener.onPlayStateChanged(mPlaybackState);
@@ -188,8 +189,8 @@ public class PlayerSession {
@Override
public void onFocusLost() {
Log.d(TAG, "Focus lost, changing state to " + Renderer.STATE_PAUSED);
mPlaybackState.setState(PlaybackState.PLAYSTATE_PAUSED);
mPlaybackState.setPosition(mRenderer.getSeekPosition());
long position = mRenderer == null ? -1 : mRenderer.getSeekPosition();
mPlaybackState.setState(PlaybackState.PLAYSTATE_PAUSED, position, 0);
mPerformer.setPlaybackState(mPlaybackState);
if (mListener != null) {
mListener.onPlayStateChanged(mPlaybackState);

View File

@@ -158,30 +158,33 @@ public class OneMediaRouteProvider extends RouteProviderService {
if (newState != Renderer.STATE_ERROR) {
mPlaybackState.setErrorMessage(null);
}
long position = -1;
if (mRenderer != null) {
position = mRenderer.getSeekPosition();
}
switch (newState) {
case Renderer.STATE_ENDED:
case Renderer.STATE_STOPPED:
mPlaybackState.setState(PlaybackState.PLAYSTATE_STOPPED);
mPlaybackState.setState(PlaybackState.PLAYSTATE_STOPPED, position, 0);
break;
case Renderer.STATE_INIT:
case Renderer.STATE_PREPARING:
mPlaybackState.setState(PlaybackState.PLAYSTATE_BUFFERING);
mPlaybackState.setState(PlaybackState.PLAYSTATE_BUFFERING, position, 0);
break;
case Renderer.STATE_ERROR:
mPlaybackState.setState(PlaybackState.PLAYSTATE_ERROR);
mPlaybackState.setState(PlaybackState.PLAYSTATE_ERROR, position, 0);
break;
case Renderer.STATE_PAUSED:
mPlaybackState.setState(PlaybackState.PLAYSTATE_PAUSED);
mPlaybackState.setState(PlaybackState.PLAYSTATE_PAUSED, position, 0);
break;
case Renderer.STATE_PLAYING:
mPlaybackState.setState(PlaybackState.PLAYSTATE_PLAYING);
mPlaybackState.setState(PlaybackState.PLAYSTATE_PLAYING, position, 1);
break;
default:
mPlaybackState.setState(PlaybackState.PLAYSTATE_ERROR);
mPlaybackState.setState(PlaybackState.PLAYSTATE_ERROR, position, 0);
mPlaybackState.setErrorMessage("unkown state");
break;
}
mPlaybackState.setPosition(mRenderer.getSeekPosition());
mControls.sendPlaybackChangeEvent(mPlaybackState.getState());
}
@@ -193,8 +196,8 @@ public class OneMediaRouteProvider extends RouteProviderService {
@Override
public void onFocusLost() {
Log.d(TAG, "Focus lost, changing state to " + Renderer.STATE_PAUSED);
mPlaybackState.setState(PlaybackState.PLAYSTATE_PAUSED);
mPlaybackState.setPosition(mRenderer.getSeekPosition());
mPlaybackState.setState(PlaybackState.PLAYSTATE_PAUSED, mRenderer.getSeekPosition(), 0);
mRenderer.onPause();
}
@Override