AVRCP Controller Shuffle/Repeat support
Add Custom actions to the Playback status for Shuffle and Repeat
capabilities when available from the remote device.
Bug: 72495707
Test: AvrcpControllerStateMachineTest#testShuffle
AvrcpControllerStateMachineTest#testRepeat
Change Shuffle and Repeat mode on connected phone via Headunit
Change-Id: Ia0a62d10032b6e918a38e66bd486a29199b772e2
(cherry picked from commit 1c862460a544108b8b12fb24827f9a717961d410)
Merged-In: Ia0a62d10032b6e918a38e66bd486a29199b772e2
Change-Id: I8669dbbe9552e5a53cb8c864c3e00c1c4df569ec
diff --git a/Android.mk b/Android.mk
index a34d2a3..1f73091 100644
--- a/Android.mk
+++ b/Android.mk
@@ -29,6 +29,7 @@
LOCAL_STATIC_ANDROID_LIBRARIES := \
androidx.core_core \
+ androidx.legacy_legacy-support-v4 \
androidx.lifecycle_lifecycle-livedata \
androidx.room_room-runtime \
diff --git a/src/com/android/bluetooth/avrcpcontroller/AvrcpControllerService.java b/src/com/android/bluetooth/avrcpcontroller/AvrcpControllerService.java
index 64e63df..693b4a2 100644
--- a/src/com/android/bluetooth/avrcpcontroller/AvrcpControllerService.java
+++ b/src/com/android/bluetooth/avrcpcontroller/AvrcpControllerService.java
@@ -412,9 +412,10 @@
if (stateMachine != null) {
PlayerApplicationSettings supportedSettings =
PlayerApplicationSettings.makeSupportedSettings(playerAttribRsp);
+ stateMachine.sendMessage(
+ AvrcpControllerStateMachine.MESSAGE_PROCESS_SUPPORTED_APPLICATION_SETTINGS,
+ supportedSettings);
}
- /* Do nothing */
-
}
private synchronized void onPlayerAppSettingChanged(byte[] address, byte[] playerAttribRsp,
@@ -426,10 +427,12 @@
AvrcpControllerStateMachine stateMachine = getStateMachine(device);
if (stateMachine != null) {
- PlayerApplicationSettings desiredSettings =
+ PlayerApplicationSettings currentSettings =
PlayerApplicationSettings.makeSettings(playerAttribRsp);
+ stateMachine.sendMessage(
+ AvrcpControllerStateMachine.MESSAGE_PROCESS_CURRENT_APPLICATION_SETTINGS,
+ currentSettings);
}
- /* Do nothing */
}
// Browsing related JNI callbacks.
@@ -711,7 +714,7 @@
/**
* Send button press commands to addressed device
*
- * @param keyCode key code as defined in AVRCP specification
+ * @param keyCode key code as defined in AVRCP specification
* @param keyState 0 = key pressed, 1 = key released
* @return command was sent
*/
@@ -720,7 +723,7 @@
/**
* Send group navigation commands
*
- * @param keyCode next/previous
+ * @param keyCode next/previous
* @param keyState state
* @return command was sent
*/
@@ -741,7 +744,7 @@
* Send response to set absolute volume
*
* @param absVol new volume
- * @param label label
+ * @param label label
*/
public native void sendAbsVolRspNative(byte[] address, int absVol, int label);
@@ -749,8 +752,8 @@
* Register for any volume level changes
*
* @param rspType type of response
- * @param absVol current volume
- * @param label label
+ * @param absVol current volume
+ * @param label label
*/
public native void sendRegisterAbsVolRspNative(byte[] address, byte rspType, int absVol,
int label);
@@ -764,7 +767,7 @@
* Fetch the current now playing list
*
* @param start first index to retrieve
- * @param end last index to retrieve
+ * @param end last index to retrieve
*/
public native void getNowPlayingListNative(byte[] address, int start, int end);
@@ -772,7 +775,7 @@
* Fetch the current folder's listing
*
* @param start first index to retrieve
- * @param end last index to retrieve
+ * @param end last index to retrieve
*/
public native void getFolderListNative(byte[] address, int start, int end);
@@ -780,7 +783,7 @@
* Fetch the listing of players
*
* @param start first index to retrieve
- * @param end last index to retrieve
+ * @param end last index to retrieve
*/
public native void getPlayerListNative(byte[] address, int start, int end);
@@ -788,15 +791,15 @@
* Change the current browsed folder
*
* @param direction up/down
- * @param uid folder unique id
+ * @param uid folder unique id
*/
public native void changeFolderPathNative(byte[] address, byte direction, long uid);
/**
* Play item with provided uid
*
- * @param scope scope of item to played
- * @param uid song unique id
+ * @param scope scope of item to played
+ * @param uid song unique id
* @param uidCounter counter
*/
public native void playItemNative(byte[] address, byte scope, long uid, int uidCounter);
diff --git a/src/com/android/bluetooth/avrcpcontroller/AvrcpControllerStateMachine.java b/src/com/android/bluetooth/avrcpcontroller/AvrcpControllerStateMachine.java
index 1d1a635..7a6c541 100644
--- a/src/com/android/bluetooth/avrcpcontroller/AvrcpControllerStateMachine.java
+++ b/src/com/android/bluetooth/avrcpcontroller/AvrcpControllerStateMachine.java
@@ -24,10 +24,10 @@
import android.media.AudioManager;
import android.media.MediaMetadata;
import android.media.browse.MediaBrowser.MediaItem;
-import android.media.session.MediaSession;
-import android.media.session.PlaybackState;
import android.os.Bundle;
import android.os.Message;
+import android.support.v4.media.session.MediaSessionCompat;
+import android.support.v4.media.session.PlaybackStateCompat;
import android.util.Log;
import android.util.SparseArray;
@@ -42,6 +42,7 @@
import java.util.ArrayList;
import java.util.List;
+
/**
* Provides Bluetooth AVRCP Controller State Machine responsible for all remote control connections
* and interactions with a remote controlable device.
@@ -76,11 +77,15 @@
static final int MESSAGE_PROCESS_SET_ADDRESSED_PLAYER = 214;
static final int MESSAGE_PROCESS_ADDRESSED_PLAYER_CHANGED = 215;
static final int MESSAGE_PROCESS_NOW_PLAYING_CONTENTS_CHANGED = 216;
+ static final int MESSAGE_PROCESS_SUPPORTED_APPLICATION_SETTINGS = 217;
+ static final int MESSAGE_PROCESS_CURRENT_APPLICATION_SETTINGS = 218;
//300->399 Events for Browsing
static final int MESSAGE_GET_FOLDER_ITEMS = 300;
static final int MESSAGE_PLAY_ITEM = 301;
static final int MSG_AVRCP_PASSTHRU = 302;
+ static final int MSG_AVRCP_SET_SHUFFLE = 303;
+ static final int MSG_AVRCP_SET_REPEAT = 304;
static final int MESSAGE_INTERNAL_ABS_VOL_TIMEOUT = 404;
@@ -215,12 +220,12 @@
synchronized void onBrowsingDisconnected() {
if (!mBrowsingConnected) return;
- mAddressedPlayer.setPlayStatus(PlaybackState.STATE_ERROR);
+ mAddressedPlayer.setPlayStatus(PlaybackStateCompat.STATE_ERROR);
mAddressedPlayer.updateCurrentTrack(null);
mBrowseTree.mNowPlayingNode.setCached(false);
BluetoothMediaBrowserService.notifyChanged(mBrowseTree.mNowPlayingNode);
- PlaybackState.Builder pbb = new PlaybackState.Builder();
- pbb.setState(PlaybackState.STATE_ERROR, PlaybackState.PLAYBACK_POSITION_UNKNOWN,
+ PlaybackStateCompat.Builder pbb = new PlaybackStateCompat.Builder();
+ pbb.setState(PlaybackStateCompat.STATE_ERROR, PlaybackStateCompat.PLAYBACK_POSITION_UNKNOWN,
1.0f).setActions(0);
pbb.setErrorMessage(mService.getString(R.string.bluetooth_disconnected));
BluetoothMediaBrowserService.notifyChanged(pbb.build());
@@ -322,6 +327,14 @@
passThru(msg.arg1);
return true;
+ case MSG_AVRCP_SET_REPEAT:
+ setRepeat(msg.arg1);
+ return true;
+
+ case MSG_AVRCP_SET_SHUFFLE:
+ setShuffle(msg.arg1);
+ return true;
+
case MESSAGE_PROCESS_TRACK_CHANGED:
mAddressedPlayer.updateCurrentTrack((MediaMetadata) msg.obj);
BluetoothMediaBrowserService.trackChanged((MediaMetadata) msg.obj);
@@ -331,7 +344,7 @@
mAddressedPlayer.setPlayStatus(msg.arg1);
BluetoothMediaBrowserService.notifyChanged(mAddressedPlayer.getPlaybackState());
if (mAddressedPlayer.getPlaybackState().getState()
- == PlaybackState.STATE_PLAYING
+ == PlaybackStateCompat.STATE_PLAYING
&& A2dpSinkService.getFocusState() == AudioManager.AUDIOFOCUS_NONE
&& !shouldRequestFocus()) {
sendMessage(MSG_AVRCP_PASSTHRU,
@@ -362,6 +375,18 @@
}
return true;
+ case MESSAGE_PROCESS_SUPPORTED_APPLICATION_SETTINGS:
+ mAddressedPlayer.setSupportedPlayerApplicationSettings(
+ (PlayerApplicationSettings) msg.obj);
+ BluetoothMediaBrowserService.notifyChanged(mAddressedPlayer.getPlaybackState());
+ return true;
+
+ case MESSAGE_PROCESS_CURRENT_APPLICATION_SETTINGS:
+ mAddressedPlayer.setCurrentPlayerApplicationSettings(
+ (PlayerApplicationSettings) msg.obj);
+ BluetoothMediaBrowserService.notifyChanged(mAddressedPlayer.getPlaybackState());
+ return true;
+
case DISCONNECT:
transitionTo(mDisconnecting);
return true;
@@ -419,6 +444,20 @@
return (cmd == AvrcpControllerService.PASS_THRU_CMD_ID_REWIND)
|| (cmd == AvrcpControllerService.PASS_THRU_CMD_ID_FF);
}
+
+ private void setRepeat(int repeatMode) {
+ mService.setPlayerApplicationSettingValuesNative(mDeviceAddress, (byte) 1,
+ new byte[]{PlayerApplicationSettings.REPEAT_STATUS}, new byte[]{
+ PlayerApplicationSettings.mapAvrcpPlayerSettingstoBTattribVal(
+ PlayerApplicationSettings.REPEAT_STATUS, repeatMode)});
+ }
+
+ private void setShuffle(int shuffleMode) {
+ mService.setPlayerApplicationSettingValuesNative(mDeviceAddress, (byte) 1,
+ new byte[]{PlayerApplicationSettings.SHUFFLE_STATUS}, new byte[]{
+ PlayerApplicationSettings.mapAvrcpPlayerSettingstoBTattribVal(
+ PlayerApplicationSettings.SHUFFLE_STATUS, shuffleMode)});
+ }
}
// Handle the get folder listing action
@@ -538,7 +577,7 @@
case MESSAGE_GET_FOLDER_ITEMS:
if (!mBrowseNode.equals(msg.obj)) {
if (shouldAbort(mBrowseNode.getScope(),
- ((BrowseTree.BrowseNode) msg.obj).getScope())) {
+ ((BrowseTree.BrowseNode) msg.obj).getScope())) {
mAbort = true;
}
deferMessage(msg);
@@ -564,7 +603,7 @@
default:
logD(STATE_TAG + " deferring message " + msg.what
- + " to connected!");
+ + " to connected!");
deferMessage(msg);
}
return true;
@@ -575,8 +614,8 @@
* necessary.
*
* @return true: a new folder in the same scope
- * a new player while fetching contents of a folder
- * false: other cases, specifically Now Playing while fetching a folder
+ * a new player while fetching contents of a folder
+ * false: other cases, specifically Now Playing while fetching a folder
*/
private boolean shouldAbort(int currentScope, int fetchScope) {
if ((currentScope == fetchScope)
@@ -697,7 +736,7 @@
mService.sendAbsVolRspNative(mDeviceAddress, absVol, label);
}
- MediaSession.Callback mSessionCallbacks = new MediaSession.Callback() {
+ MediaSessionCompat.Callback mSessionCallbacks = new MediaSessionCompat.Callback() {
@Override
public void onPlay() {
logD("onPlay");
@@ -770,6 +809,19 @@
BrowseTree.BrowseNode node = mBrowseTree.findBrowseNodeByID(mediaId);
sendMessage(MESSAGE_PLAY_ITEM, node);
}
+
+ @Override
+ public void onSetRepeatMode(int repeatMode) {
+ logD("onSetRepeatMode");
+ sendMessage(MSG_AVRCP_SET_REPEAT, repeatMode);
+ }
+
+ @Override
+ public void onSetShuffleMode(int shuffleMode) {
+ logD("onSetShuffleMode");
+ sendMessage(MSG_AVRCP_SET_SHUFFLE, shuffleMode);
+
+ }
};
protected void broadcastConnectionStateChanged(int currentState) {
diff --git a/src/com/android/bluetooth/avrcpcontroller/AvrcpPlayer.java b/src/com/android/bluetooth/avrcpcontroller/AvrcpPlayer.java
index bed38d9..2ef3c14 100644
--- a/src/com/android/bluetooth/avrcpcontroller/AvrcpPlayer.java
+++ b/src/com/android/bluetooth/avrcpcontroller/AvrcpPlayer.java
@@ -17,8 +17,9 @@
package com.android.bluetooth.avrcpcontroller;
import android.media.MediaMetadata;
-import android.media.session.PlaybackState;
import android.os.SystemClock;
+import android.support.v4.media.session.MediaSessionCompat;
+import android.support.v4.media.session.PlaybackStateCompat;
import android.util.Log;
import java.util.Arrays;
@@ -41,27 +42,31 @@
public static final int FEATURE_PREVIOUS = 48;
public static final int FEATURE_BROWSING = 59;
- private int mPlayStatus = PlaybackState.STATE_NONE;
- private long mPlayTime = PlaybackState.PLAYBACK_POSITION_UNKNOWN;
+ private int mPlayStatus = PlaybackStateCompat.STATE_NONE;
+ private long mPlayTime = PlaybackStateCompat.PLAYBACK_POSITION_UNKNOWN;
private long mPlayTimeUpdate = 0;
private float mPlaySpeed = 1;
private int mId;
private String mName = "";
private int mPlayerType;
- private byte[] mPlayerFeatures;
+ private byte[] mPlayerFeatures = new byte[16];
private long mAvailableActions;
private MediaMetadata mCurrentTrack;
- private PlaybackState mPlaybackState;
+ private PlaybackStateCompat mPlaybackStateCompat;
+ private PlayerApplicationSettings mSupportedPlayerApplicationSettings =
+ new PlayerApplicationSettings();
+ private PlayerApplicationSettings mCurrentPlayerApplicationSettings;
AvrcpPlayer() {
mId = INVALID_ID;
//Set Default Actions in case Player data isn't available.
- mAvailableActions = PlaybackState.ACTION_PAUSE | PlaybackState.ACTION_PLAY
- | PlaybackState.ACTION_SKIP_TO_NEXT | PlaybackState.ACTION_SKIP_TO_PREVIOUS
- | PlaybackState.ACTION_STOP;
- PlaybackState.Builder playbackStateBuilder = new PlaybackState.Builder()
+ mAvailableActions = PlaybackStateCompat.ACTION_PAUSE | PlaybackStateCompat.ACTION_PLAY
+ | PlaybackStateCompat.ACTION_SKIP_TO_NEXT
+ | PlaybackStateCompat.ACTION_SKIP_TO_PREVIOUS
+ | PlaybackStateCompat.ACTION_STOP;
+ PlaybackStateCompat.Builder playbackStateBuilder = new PlaybackStateCompat.Builder()
.setActions(mAvailableActions);
- mPlaybackState = playbackStateBuilder.build();
+ mPlaybackStateCompat = playbackStateBuilder.build();
}
AvrcpPlayer(int id, String name, byte[] playerFeatures, int playStatus, int playerType) {
@@ -70,10 +75,10 @@
mPlayStatus = playStatus;
mPlayerType = playerType;
mPlayerFeatures = Arrays.copyOf(playerFeatures, playerFeatures.length);
- updateAvailableActions();
- PlaybackState.Builder playbackStateBuilder = new PlaybackState.Builder()
+ PlaybackStateCompat.Builder playbackStateBuilder = new PlaybackStateCompat.Builder()
.setActions(mAvailableActions);
- mPlaybackState = playbackStateBuilder.build();
+ mPlaybackStateCompat = playbackStateBuilder.build();
+ updateAvailableActions();
}
public int getId() {
@@ -87,7 +92,8 @@
public void setPlayTime(int playTime) {
mPlayTime = playTime;
mPlayTimeUpdate = SystemClock.elapsedRealtime();
- mPlaybackState = new PlaybackState.Builder(mPlaybackState).setState(mPlayStatus, mPlayTime,
+ mPlaybackStateCompat = new PlaybackStateCompat.Builder(mPlaybackStateCompat).setState(
+ mPlayStatus, mPlayTime,
mPlaySpeed).build();
}
@@ -97,30 +103,48 @@
public void setPlayStatus(int playStatus) {
mPlayTime += mPlaySpeed * (SystemClock.elapsedRealtime()
- - mPlaybackState.getLastPositionUpdateTime());
+ - mPlaybackStateCompat.getLastPositionUpdateTime());
mPlayStatus = playStatus;
switch (mPlayStatus) {
- case PlaybackState.STATE_STOPPED:
+ case PlaybackStateCompat.STATE_STOPPED:
mPlaySpeed = 0;
break;
- case PlaybackState.STATE_PLAYING:
+ case PlaybackStateCompat.STATE_PLAYING:
mPlaySpeed = 1;
break;
- case PlaybackState.STATE_PAUSED:
+ case PlaybackStateCompat.STATE_PAUSED:
mPlaySpeed = 0;
break;
- case PlaybackState.STATE_FAST_FORWARDING:
+ case PlaybackStateCompat.STATE_FAST_FORWARDING:
mPlaySpeed = 3;
break;
- case PlaybackState.STATE_REWINDING:
+ case PlaybackStateCompat.STATE_REWINDING:
mPlaySpeed = -3;
break;
}
- mPlaybackState = new PlaybackState.Builder(mPlaybackState).setState(mPlayStatus, mPlayTime,
+ mPlaybackStateCompat = new PlaybackStateCompat.Builder(mPlaybackStateCompat).setState(
+ mPlayStatus, mPlayTime,
mPlaySpeed).build();
}
+ public void setSupportedPlayerApplicationSettings(
+ PlayerApplicationSettings playerApplicationSettings) {
+ mSupportedPlayerApplicationSettings = playerApplicationSettings;
+ updateAvailableActions();
+ }
+
+ public void setCurrentPlayerApplicationSettings(
+ PlayerApplicationSettings playerApplicationSettings) {
+ Log.d(TAG, "Settings changed");
+ mCurrentPlayerApplicationSettings = playerApplicationSettings;
+ MediaSessionCompat session = BluetoothMediaBrowserService.getSession();
+ session.setRepeatMode(mCurrentPlayerApplicationSettings.getSetting(
+ PlayerApplicationSettings.REPEAT_STATUS));
+ session.setShuffleMode(mCurrentPlayerApplicationSettings.getSetting(
+ PlayerApplicationSettings.SHUFFLE_STATUS));
+ }
+
public int getPlayStatus() {
return mPlayStatus;
}
@@ -131,17 +155,22 @@
return (mPlayerFeatures[byteNumber] & bitMask) == bitMask;
}
- public PlaybackState getPlaybackState() {
+ public boolean supportsSetting(int settingType, int settingValue) {
+ return mSupportedPlayerApplicationSettings.supportsSetting(settingType, settingValue);
+ }
+
+ public PlaybackStateCompat getPlaybackState() {
if (DBG) {
Log.d(TAG, "getPlayBackState state " + mPlayStatus + " time " + mPlayTime);
}
- return mPlaybackState;
+ return mPlaybackStateCompat;
}
public synchronized void updateCurrentTrack(MediaMetadata update) {
if (update != null) {
long trackNumber = update.getLong(MediaMetadata.METADATA_KEY_TRACK_NUMBER);
- mPlaybackState = new PlaybackState.Builder(mPlaybackState).setActiveQueueItemId(
+ mPlaybackStateCompat = new PlaybackStateCompat.Builder(
+ mPlaybackStateCompat).setActiveQueueItemId(
trackNumber - 1).build();
}
mCurrentTrack = update;
@@ -153,26 +182,37 @@
private void updateAvailableActions() {
if (supportsFeature(FEATURE_PLAY)) {
- mAvailableActions = mAvailableActions | PlaybackState.ACTION_PLAY;
+ mAvailableActions = mAvailableActions | PlaybackStateCompat.ACTION_PLAY;
}
if (supportsFeature(FEATURE_STOP)) {
- mAvailableActions = mAvailableActions | PlaybackState.ACTION_STOP;
+ mAvailableActions = mAvailableActions | PlaybackStateCompat.ACTION_STOP;
}
if (supportsFeature(FEATURE_PAUSE)) {
- mAvailableActions = mAvailableActions | PlaybackState.ACTION_PAUSE;
+ mAvailableActions = mAvailableActions | PlaybackStateCompat.ACTION_PAUSE;
}
if (supportsFeature(FEATURE_REWIND)) {
- mAvailableActions = mAvailableActions | PlaybackState.ACTION_REWIND;
+ mAvailableActions = mAvailableActions | PlaybackStateCompat.ACTION_REWIND;
}
if (supportsFeature(FEATURE_FAST_FORWARD)) {
- mAvailableActions = mAvailableActions | PlaybackState.ACTION_FAST_FORWARD;
+ mAvailableActions = mAvailableActions | PlaybackStateCompat.ACTION_FAST_FORWARD;
}
if (supportsFeature(FEATURE_FORWARD)) {
- mAvailableActions = mAvailableActions | PlaybackState.ACTION_SKIP_TO_NEXT;
+ mAvailableActions = mAvailableActions | PlaybackStateCompat.ACTION_SKIP_TO_NEXT;
}
if (supportsFeature(FEATURE_PREVIOUS)) {
- mAvailableActions = mAvailableActions | PlaybackState.ACTION_SKIP_TO_PREVIOUS;
+ mAvailableActions = mAvailableActions | PlaybackStateCompat.ACTION_SKIP_TO_PREVIOUS;
}
+ if (mSupportedPlayerApplicationSettings.supportsSetting(
+ PlayerApplicationSettings.REPEAT_STATUS)) {
+ mAvailableActions |= PlaybackStateCompat.ACTION_SET_REPEAT_MODE;
+ }
+ if (mSupportedPlayerApplicationSettings.supportsSetting(
+ PlayerApplicationSettings.SHUFFLE_STATUS)) {
+ mAvailableActions |= PlaybackStateCompat.ACTION_SET_SHUFFLE_MODE;
+ }
+ mPlaybackStateCompat = new PlaybackStateCompat.Builder(mPlaybackStateCompat)
+ .setActions(mAvailableActions).build();
+
if (DBG) Log.d(TAG, "Supported Actions = " + mAvailableActions);
}
}
diff --git a/src/com/android/bluetooth/avrcpcontroller/BluetoothMediaBrowserService.java b/src/com/android/bluetooth/avrcpcontroller/BluetoothMediaBrowserService.java
index 3504cd4..774f953 100644
--- a/src/com/android/bluetooth/avrcpcontroller/BluetoothMediaBrowserService.java
+++ b/src/com/android/bluetooth/avrcpcontroller/BluetoothMediaBrowserService.java
@@ -18,13 +18,17 @@
import android.media.MediaMetadata;
import android.media.browse.MediaBrowser.MediaItem;
-import android.media.session.MediaController;
-import android.media.session.MediaSession;
-import android.media.session.PlaybackState;
import android.os.Bundle;
-import android.service.media.MediaBrowserService;
+import android.support.v4.media.MediaBrowserCompat;
+import android.support.v4.media.MediaDescriptionCompat;
+import android.support.v4.media.MediaMetadataCompat;
+import android.support.v4.media.session.MediaControllerCompat;
+import android.support.v4.media.session.MediaSessionCompat;
+import android.support.v4.media.session.PlaybackStateCompat;
import android.util.Log;
+import androidx.media.MediaBrowserServiceCompat;
+
import com.android.bluetooth.R;
import java.util.ArrayList;
@@ -37,43 +41,44 @@
* The applications are expected to use MediaBrowser (see API) and all the music
* browsing/playback/metadata can be controlled via MediaBrowser and MediaController.
*
- * The current behavior of MediaSession exposed by this service is as follows:
- * 1. MediaSession is active (i.e. SystemUI and other overview UIs can see updates) when device is
- * connected and first starts playing. Before it starts playing we do not active the session.
+ * The current behavior of MediaSessionCompat exposed by this service is as follows:
+ * 1. MediaSessionCompat is active (i.e. SystemUI and other overview UIs can see updates) when
+ * device is connected and first starts playing. Before it starts playing we do not activate the
+ * session.
* 1.1 The session is active throughout the duration of connection.
* 2. The session is de-activated when the device disconnects. It will be connected again when (1)
* happens.
*/
-public class BluetoothMediaBrowserService extends MediaBrowserService {
+public class BluetoothMediaBrowserService extends MediaBrowserServiceCompat {
private static final String TAG = "BluetoothMediaBrowserService";
private static final boolean DBG = Log.isLoggable(TAG, Log.DEBUG);
private static BluetoothMediaBrowserService sBluetoothMediaBrowserService;
- private MediaSession mSession;
+ private MediaSessionCompat mSession;
// Browsing related structures.
- private List<MediaSession.QueueItem> mMediaQueue = new ArrayList<>();
+ private List<MediaSessionCompat.QueueItem> mMediaQueue = new ArrayList<>();
/**
- * Initialize this BluetoothMediaBrowserService, creating our MediaSession, MediaPlayer and
- * MediaMetaData, and setting up mechanisms to talk with the AvrcpControllerService.
+ * Initialize this BluetoothMediaBrowserService, creating our MediaSessionCompat, MediaPlayer
+ * and MediaMetaData, and setting up mechanisms to talk with the AvrcpControllerService.
*/
@Override
public void onCreate() {
if (DBG) Log.d(TAG, "onCreate");
super.onCreate();
- // Create and configure the MediaSession
- mSession = new MediaSession(this, TAG);
+ // Create and configure the MediaSessionCompat
+ mSession = new MediaSessionCompat(this, TAG);
setSessionToken(mSession.getSessionToken());
- mSession.setFlags(MediaSession.FLAG_HANDLES_MEDIA_BUTTONS
- | MediaSession.FLAG_HANDLES_TRANSPORT_CONTROLS);
+ mSession.setFlags(MediaSessionCompat.FLAG_HANDLES_MEDIA_BUTTONS
+ | MediaSessionCompat.FLAG_HANDLES_TRANSPORT_CONTROLS);
mSession.setQueueTitle(getString(R.string.bluetooth_a2dp_sink_queue_name));
mSession.setQueue(mMediaQueue);
- PlaybackState.Builder playbackStateBuilder = new PlaybackState.Builder();
- playbackStateBuilder.setState(PlaybackState.STATE_ERROR,
- PlaybackState.PLAYBACK_POSITION_UNKNOWN, 1.0f).setActions(0);
+ PlaybackStateCompat.Builder playbackStateBuilder = new PlaybackStateCompat.Builder();
+ playbackStateBuilder.setState(PlaybackStateCompat.STATE_ERROR,
+ PlaybackStateCompat.PLAYBACK_POSITION_UNKNOWN, 1.0f).setActions(0);
playbackStateBuilder.setErrorMessage(getString(R.string.bluetooth_disconnected));
mSession.setPlaybackState(playbackStateBuilder.build());
sBluetoothMediaBrowserService = this;
@@ -91,9 +96,10 @@
@Override
public synchronized void onLoadChildren(final String parentMediaId,
- final Result<List<MediaItem>> result) {
+ final Result<List<MediaBrowserCompat.MediaItem>> result) {
if (DBG) Log.d(TAG, "onLoadChildren parentMediaId=" + parentMediaId);
- List<MediaItem> contents = getContents(parentMediaId);
+ List<MediaBrowserCompat.MediaItem> contents =
+ MediaBrowserCompat.MediaItem.fromMediaItemList(getContents(parentMediaId));
if (contents == null) {
result.detach();
} else {
@@ -112,7 +118,8 @@
mMediaQueue.clear();
if (songList != null) {
for (MediaItem song : songList) {
- mMediaQueue.add(new MediaSession.QueueItem(song.getDescription(),
+ mMediaQueue.add(new MediaSessionCompat.QueueItem(
+ MediaDescriptionCompat.fromMediaDescription(song.getDescription()),
mMediaQueue.size()));
}
}
@@ -129,7 +136,7 @@
}
}
- static synchronized void addressedPlayerChanged(MediaSession.Callback callback) {
+ static synchronized void addressedPlayerChanged(MediaSessionCompat.Callback callback) {
if (sBluetoothMediaBrowserService != null) {
sBluetoothMediaBrowserService.mSession.setCallback(callback);
} else {
@@ -139,13 +146,14 @@
static synchronized void trackChanged(MediaMetadata mediaMetadata) {
if (sBluetoothMediaBrowserService != null) {
- sBluetoothMediaBrowserService.mSession.setMetadata(mediaMetadata);
+ sBluetoothMediaBrowserService.mSession.setMetadata(
+ MediaMetadataCompat.fromMediaMetadata(mediaMetadata));
} else {
Log.w(TAG, "trackChanged Unavailable");
}
}
- static synchronized void notifyChanged(PlaybackState playbackState) {
+ static synchronized void notifyChanged(PlaybackStateCompat playbackState) {
Log.d(TAG, "notifyChanged PlaybackState" + playbackState);
if (sBluetoothMediaBrowserService != null) {
sBluetoothMediaBrowserService.mSession.setPlaybackState(playbackState);
@@ -179,7 +187,7 @@
/**
* Get object for controlling playback
*/
- public static synchronized MediaController.TransportControls getTransportControls() {
+ public static synchronized MediaControllerCompat.TransportControls getTransportControls() {
if (sBluetoothMediaBrowserService != null) {
return sBluetoothMediaBrowserService.mSession.getController().getTransportControls();
} else {
@@ -198,4 +206,16 @@
Log.w(TAG, "setActive Unavailable");
}
}
+
+ /**
+ * Get Media session for updating state
+ */
+ public static synchronized MediaSessionCompat getSession() {
+ if (sBluetoothMediaBrowserService != null) {
+ return sBluetoothMediaBrowserService.mSession;
+ } else {
+ Log.w(TAG, "getSession Unavailable");
+ return null;
+ }
+ }
}
diff --git a/src/com/android/bluetooth/avrcpcontroller/PlayerApplicationSettings.java b/src/com/android/bluetooth/avrcpcontroller/PlayerApplicationSettings.java
index c34a2d7..362548e 100644
--- a/src/com/android/bluetooth/avrcpcontroller/PlayerApplicationSettings.java
+++ b/src/com/android/bluetooth/avrcpcontroller/PlayerApplicationSettings.java
@@ -16,12 +16,11 @@
package com.android.bluetooth.avrcpcontroller;
-import android.bluetooth.BluetoothAvrcpPlayerSettings;
+import android.support.v4.media.session.PlaybackStateCompat;
import android.util.Log;
+import android.util.SparseArray;
import java.util.ArrayList;
-import java.util.HashMap;
-import java.util.Map;
/*
* Contains information Player Application Setting extended from BluetootAvrcpPlayerSettings
@@ -32,10 +31,10 @@
/*
* Values for SetPlayerApplicationSettings from AVRCP Spec V1.6 Appendix F.
*/
- private static final byte JNI_ATTRIB_EQUALIZER_STATUS = 0x01;
- private static final byte JNI_ATTRIB_REPEAT_STATUS = 0x02;
- private static final byte JNI_ATTRIB_SHUFFLE_STATUS = 0x03;
- private static final byte JNI_ATTRIB_SCAN_STATUS = 0x04;
+ static final byte EQUALIZER_STATUS = 0x01;
+ static final byte REPEAT_STATUS = 0x02;
+ static final byte SHUFFLE_STATUS = 0x03;
+ static final byte SCAN_STATUS = 0x04;
private static final byte JNI_EQUALIZER_STATUS_OFF = 0x01;
private static final byte JNI_EQUALIZER_STATUS_ON = 0x02;
@@ -55,18 +54,17 @@
private static final byte JNI_STATUS_INVALID = -1;
-
/*
* Hash map of current settings.
*/
- private Map<Integer, Integer> mSettings = new HashMap<Integer, Integer>();
+ private SparseArray<Integer> mSettings = new SparseArray<>();
/*
* Hash map of supported values, a setting should be supported by the remote in order to enable
* in mSettings.
*/
- private Map<Integer, ArrayList<Integer>> mSupportedValues =
- new HashMap<Integer, ArrayList<Integer>>();
+ private SparseArray<ArrayList<Integer>> mSupportedValues =
+ new SparseArray<ArrayList<Integer>>();
/* Convert from JNI array to Java classes. */
static PlayerApplicationSettings makeSupportedSettings(byte[] btAvrcpAttributeList) {
@@ -82,8 +80,7 @@
supportedValues.add(
mapAttribIdValtoAvrcpPlayerSetting(attrId, btAvrcpAttributeList[i++]));
}
- newObj.mSupportedValues.put(mapBTAttribIdToAvrcpPlayerSettings(attrId),
- supportedValues);
+ newObj.mSupportedValues.put(attrId, supportedValues);
}
} catch (ArrayIndexOutOfBoundsException exception) {
Log.e(TAG, "makeSupportedSettings attributeList index error.");
@@ -91,25 +88,13 @@
return newObj;
}
- public BluetoothAvrcpPlayerSettings getAvrcpSettings() {
- int supportedSettings = 0;
- for (Integer setting : mSettings.keySet()) {
- supportedSettings |= setting;
- }
- BluetoothAvrcpPlayerSettings result = new BluetoothAvrcpPlayerSettings(supportedSettings);
- for (Integer setting : mSettings.keySet()) {
- result.addSettingValue(setting, mSettings.get(setting));
- }
- return result;
- }
-
static PlayerApplicationSettings makeSettings(byte[] btAvrcpAttributeList) {
PlayerApplicationSettings newObj = new PlayerApplicationSettings();
try {
for (int i = 0; i < btAvrcpAttributeList.length; ) {
byte attrId = btAvrcpAttributeList[i++];
- newObj.mSettings.put(mapBTAttribIdToAvrcpPlayerSettings(attrId),
+ newObj.mSettings.put(attrId,
mapAttribIdValtoAvrcpPlayerSetting(attrId, btAvrcpAttributeList[i++]));
}
} catch (ArrayIndexOutOfBoundsException exception) {
@@ -123,177 +108,69 @@
mSupportedValues = updates.mSupportedValues;
}
- public void setValues(BluetoothAvrcpPlayerSettings updates) {
- int supportedSettings = updates.getSettings();
- for (int i = 1; i <= BluetoothAvrcpPlayerSettings.SETTING_SCAN; i++) {
- if ((i & supportedSettings) > 0) {
- mSettings.put(i, updates.getSettingValue(i));
- }
- }
+ public boolean supportsSetting(int settingType, int settingValue) {
+ if (null == mSupportedValues.get(settingType)) return false;
+ return mSupportedValues.valueAt(settingType).contains(settingValue);
}
- /*
- * Check through all settings to ensure that they are all available to be set and then check
- * that the desired value is in fact supported by our remote player.
- */
- public boolean supportsSettings(BluetoothAvrcpPlayerSettings settingsToCheck) {
- int settingSubset = settingsToCheck.getSettings();
- int supportedSettings = 0;
- for (Integer setting : mSupportedValues.keySet()) {
- supportedSettings |= setting;
- }
- try {
- if ((supportedSettings & settingSubset) == settingSubset) {
- for (Integer settingId : mSettings.keySet()) {
- // The setting is in both settings to check and supported settings but the
- // value is not supported.
- if ((settingId & settingSubset) == settingId && (!mSupportedValues.get(
- settingId).contains(settingsToCheck.getSettingValue(settingId)))) {
- return false;
- }
- }
- return true;
- }
- } catch (NullPointerException e) {
- Log.e(TAG,
- "supportsSettings received a supported setting that has no supported values.");
- }
- return false;
+ public boolean supportsSetting(int settingType) {
+ return (null != mSupportedValues.get(settingType));
}
- // Convert currently desired settings into an attribute array to pass to the native layer to
- // enable them.
- public ArrayList<Byte> getNativeSettings() {
- int i = 0;
- ArrayList<Byte> attribArray = new ArrayList<Byte>();
- for (Integer settingId : mSettings.keySet()) {
- switch (settingId) {
- case BluetoothAvrcpPlayerSettings.SETTING_EQUALIZER:
- attribArray.add(JNI_ATTRIB_EQUALIZER_STATUS);
- attribArray.add(mapAvrcpPlayerSettingstoBTattribVal(settingId,
- mSettings.get(settingId)));
- break;
- case BluetoothAvrcpPlayerSettings.SETTING_REPEAT:
- attribArray.add(JNI_ATTRIB_REPEAT_STATUS);
- attribArray.add(mapAvrcpPlayerSettingstoBTattribVal(settingId,
- mSettings.get(settingId)));
- break;
- case BluetoothAvrcpPlayerSettings.SETTING_SHUFFLE:
- attribArray.add(JNI_ATTRIB_SHUFFLE_STATUS);
- attribArray.add(mapAvrcpPlayerSettingstoBTattribVal(settingId,
- mSettings.get(settingId)));
- break;
- case BluetoothAvrcpPlayerSettings.SETTING_SCAN:
- attribArray.add(JNI_ATTRIB_SCAN_STATUS);
- attribArray.add(mapAvrcpPlayerSettingstoBTattribVal(settingId,
- mSettings.get(settingId)));
- break;
- default:
- Log.w(TAG, "Unknown setting found in getNativeSettings: " + settingId);
- }
- }
- return attribArray;
+ public int getSetting(int settingType) {
+ if (null == mSettings.get(settingType)) return -1;
+ return mSettings.get(settingType);
}
// Convert a native Attribute Id/Value pair into the AVRCP equivalent value.
private static int mapAttribIdValtoAvrcpPlayerSetting(byte attribId, byte attribVal) {
- if (attribId == JNI_ATTRIB_EQUALIZER_STATUS) {
- switch (attribVal) {
- case JNI_EQUALIZER_STATUS_OFF:
- return BluetoothAvrcpPlayerSettings.STATE_OFF;
- case JNI_EQUALIZER_STATUS_ON:
- return BluetoothAvrcpPlayerSettings.STATE_ON;
- }
- } else if (attribId == JNI_ATTRIB_REPEAT_STATUS) {
+ if (attribId == REPEAT_STATUS) {
switch (attribVal) {
case JNI_REPEAT_STATUS_ALL_TRACK_REPEAT:
- return BluetoothAvrcpPlayerSettings.STATE_ALL_TRACK;
+ return PlaybackStateCompat.REPEAT_MODE_ALL;
case JNI_REPEAT_STATUS_GROUP_REPEAT:
- return BluetoothAvrcpPlayerSettings.STATE_GROUP;
+ return PlaybackStateCompat.REPEAT_MODE_GROUP;
case JNI_REPEAT_STATUS_OFF:
- return BluetoothAvrcpPlayerSettings.STATE_OFF;
+ return PlaybackStateCompat.REPEAT_MODE_NONE;
case JNI_REPEAT_STATUS_SINGLE_TRACK_REPEAT:
- return BluetoothAvrcpPlayerSettings.STATE_SINGLE_TRACK;
+ return PlaybackStateCompat.REPEAT_MODE_ONE;
}
- } else if (attribId == JNI_ATTRIB_SCAN_STATUS) {
- switch (attribVal) {
- case JNI_SCAN_STATUS_ALL_TRACK_SCAN:
- return BluetoothAvrcpPlayerSettings.STATE_ALL_TRACK;
- case JNI_SCAN_STATUS_GROUP_SCAN:
- return BluetoothAvrcpPlayerSettings.STATE_GROUP;
- case JNI_SCAN_STATUS_OFF:
- return BluetoothAvrcpPlayerSettings.STATE_OFF;
- }
- } else if (attribId == JNI_ATTRIB_SHUFFLE_STATUS) {
+ } else if (attribId == SHUFFLE_STATUS) {
switch (attribVal) {
case JNI_SHUFFLE_STATUS_ALL_TRACK_SHUFFLE:
- return BluetoothAvrcpPlayerSettings.STATE_ALL_TRACK;
+ return PlaybackStateCompat.SHUFFLE_MODE_ALL;
case JNI_SHUFFLE_STATUS_GROUP_SHUFFLE:
- return BluetoothAvrcpPlayerSettings.STATE_GROUP;
+ return PlaybackStateCompat.SHUFFLE_MODE_GROUP;
case JNI_SHUFFLE_STATUS_OFF:
- return BluetoothAvrcpPlayerSettings.STATE_OFF;
- }
- }
- return BluetoothAvrcpPlayerSettings.STATE_INVALID;
- }
-
- // Convert an AVRCP Setting/Value pair into the native equivalent value;
- private static byte mapAvrcpPlayerSettingstoBTattribVal(int mSetting, int mSettingVal) {
- if (mSetting == BluetoothAvrcpPlayerSettings.SETTING_EQUALIZER) {
- switch (mSettingVal) {
- case BluetoothAvrcpPlayerSettings.STATE_OFF:
- return JNI_EQUALIZER_STATUS_OFF;
- case BluetoothAvrcpPlayerSettings.STATE_ON:
- return JNI_EQUALIZER_STATUS_ON;
- }
- } else if (mSetting == BluetoothAvrcpPlayerSettings.SETTING_REPEAT) {
- switch (mSettingVal) {
- case BluetoothAvrcpPlayerSettings.STATE_OFF:
- return JNI_REPEAT_STATUS_OFF;
- case BluetoothAvrcpPlayerSettings.STATE_SINGLE_TRACK:
- return JNI_REPEAT_STATUS_SINGLE_TRACK_REPEAT;
- case BluetoothAvrcpPlayerSettings.STATE_ALL_TRACK:
- return JNI_REPEAT_STATUS_ALL_TRACK_REPEAT;
- case BluetoothAvrcpPlayerSettings.STATE_GROUP:
- return JNI_REPEAT_STATUS_GROUP_REPEAT;
- }
- } else if (mSetting == BluetoothAvrcpPlayerSettings.SETTING_SHUFFLE) {
- switch (mSettingVal) {
- case BluetoothAvrcpPlayerSettings.STATE_OFF:
- return JNI_SHUFFLE_STATUS_OFF;
- case BluetoothAvrcpPlayerSettings.STATE_ALL_TRACK:
- return JNI_SHUFFLE_STATUS_ALL_TRACK_SHUFFLE;
- case BluetoothAvrcpPlayerSettings.STATE_GROUP:
- return JNI_SHUFFLE_STATUS_GROUP_SHUFFLE;
- }
- } else if (mSetting == BluetoothAvrcpPlayerSettings.SETTING_SCAN) {
- switch (mSettingVal) {
- case BluetoothAvrcpPlayerSettings.STATE_OFF:
- return JNI_SCAN_STATUS_OFF;
- case BluetoothAvrcpPlayerSettings.STATE_ALL_TRACK:
- return JNI_SCAN_STATUS_ALL_TRACK_SCAN;
- case BluetoothAvrcpPlayerSettings.STATE_GROUP:
- return JNI_SCAN_STATUS_GROUP_SCAN;
+ return PlaybackStateCompat.SHUFFLE_MODE_NONE;
}
}
return JNI_STATUS_INVALID;
}
- // convert a native Attribute Id into the AVRCP Setting equivalent value;
- private static int mapBTAttribIdToAvrcpPlayerSettings(byte attribId) {
- switch (attribId) {
- case JNI_ATTRIB_EQUALIZER_STATUS:
- return BluetoothAvrcpPlayerSettings.SETTING_EQUALIZER;
- case JNI_ATTRIB_REPEAT_STATUS:
- return BluetoothAvrcpPlayerSettings.SETTING_REPEAT;
- case JNI_ATTRIB_SHUFFLE_STATUS:
- return BluetoothAvrcpPlayerSettings.SETTING_SHUFFLE;
- case JNI_ATTRIB_SCAN_STATUS:
- return BluetoothAvrcpPlayerSettings.SETTING_SCAN;
- default:
- return BluetoothAvrcpPlayerSettings.STATE_INVALID;
+ // Convert an AVRCP Setting/Value pair into the native equivalent value;
+ static byte mapAvrcpPlayerSettingstoBTattribVal(int mSetting, int mSettingVal) {
+ if (mSetting == REPEAT_STATUS) {
+ switch (mSettingVal) {
+ case PlaybackStateCompat.REPEAT_MODE_NONE:
+ return JNI_REPEAT_STATUS_OFF;
+ case PlaybackStateCompat.REPEAT_MODE_ONE:
+ return JNI_REPEAT_STATUS_SINGLE_TRACK_REPEAT;
+ case PlaybackStateCompat.REPEAT_MODE_ALL:
+ return JNI_REPEAT_STATUS_ALL_TRACK_REPEAT;
+ case PlaybackStateCompat.REPEAT_MODE_GROUP:
+ return JNI_REPEAT_STATUS_GROUP_REPEAT;
+ }
+ } else if (mSetting == SHUFFLE_STATUS) {
+ switch (mSettingVal) {
+ case PlaybackStateCompat.SHUFFLE_MODE_NONE:
+ return JNI_SHUFFLE_STATUS_OFF;
+ case PlaybackStateCompat.SHUFFLE_MODE_ALL:
+ return JNI_SHUFFLE_STATUS_ALL_TRACK_SHUFFLE;
+ case PlaybackStateCompat.SHUFFLE_MODE_GROUP:
+ return JNI_SHUFFLE_STATUS_GROUP_SHUFFLE;
+ }
}
+ return JNI_STATUS_INVALID;
}
-
}
-
diff --git a/tests/unit/src/com/android/bluetooth/avrcpcontroller/AvrcpControllerStateMachineTest.java b/tests/unit/src/com/android/bluetooth/avrcpcontroller/AvrcpControllerStateMachineTest.java
index b1d1743..0ebb31e 100644
--- a/tests/unit/src/com/android/bluetooth/avrcpcontroller/AvrcpControllerStateMachineTest.java
+++ b/tests/unit/src/com/android/bluetooth/avrcpcontroller/AvrcpControllerStateMachineTest.java
@@ -23,8 +23,8 @@
import android.bluetooth.BluetoothProfile;
import android.content.Context;
import android.content.Intent;
-import android.media.session.MediaController;
import android.os.Looper;
+import android.support.v4.media.session.MediaControllerCompat;
import androidx.test.InstrumentationRegistry;
import androidx.test.filters.MediumTest;
@@ -213,7 +213,7 @@
@Test
public void testPlay() throws Exception {
setUpConnectedState(true, true);
- MediaController.TransportControls transportControls =
+ MediaControllerCompat.TransportControls transportControls =
BluetoothMediaBrowserService.getTransportControls();
//Play
@@ -232,7 +232,7 @@
@Test
public void testPause() throws Exception {
setUpConnectedState(true, true);
- MediaController.TransportControls transportControls =
+ MediaControllerCompat.TransportControls transportControls =
BluetoothMediaBrowserService.getTransportControls();
//Pause
@@ -251,7 +251,7 @@
@Test
public void testStop() throws Exception {
setUpConnectedState(true, true);
- MediaController.TransportControls transportControls =
+ MediaControllerCompat.TransportControls transportControls =
BluetoothMediaBrowserService.getTransportControls();
//Stop
@@ -270,7 +270,7 @@
@Test
public void testNext() throws Exception {
setUpConnectedState(true, true);
- MediaController.TransportControls transportControls =
+ MediaControllerCompat.TransportControls transportControls =
BluetoothMediaBrowserService.getTransportControls();
//Next
@@ -290,7 +290,7 @@
@Test
public void testPrevious() throws Exception {
setUpConnectedState(true, true);
- MediaController.TransportControls transportControls =
+ MediaControllerCompat.TransportControls transportControls =
BluetoothMediaBrowserService.getTransportControls();
//Previous
@@ -310,7 +310,7 @@
@Test
public void testFastForward() throws Exception {
setUpConnectedState(true, true);
- MediaController.TransportControls transportControls =
+ MediaControllerCompat.TransportControls transportControls =
BluetoothMediaBrowserService.getTransportControls();
//FastForward
@@ -331,7 +331,7 @@
@Test
public void testRewind() throws Exception {
setUpConnectedState(true, true);
- MediaController.TransportControls transportControls =
+ MediaControllerCompat.TransportControls transportControls =
BluetoothMediaBrowserService.getTransportControls();
//Rewind
@@ -347,6 +347,44 @@
}
/**
+ * Test media browser shuffle command
+ */
+ @Test
+ public void testShuffle() throws Exception {
+ byte[] shuffleSetting = new byte[]{3};
+ byte[] shuffleMode = new byte[]{2};
+
+ setUpConnectedState(true, true);
+ MediaControllerCompat.TransportControls transportControls =
+ BluetoothMediaBrowserService.getTransportControls();
+
+ //Shuffle
+ transportControls.setShuffleMode(1);
+ verify(mAvrcpControllerService, timeout(ASYNC_CALL_TIMEOUT_MILLIS).times(1))
+ .setPlayerApplicationSettingValuesNative(
+ eq(mTestAddress), eq((byte) 1), eq(shuffleSetting), eq(shuffleMode));
+ }
+
+ /**
+ * Test media browser repeat command
+ */
+ @Test
+ public void testRepeat() throws Exception {
+ byte[] repeatSetting = new byte[]{2};
+ byte[] repeatMode = new byte[]{3};
+
+ setUpConnectedState(true, true);
+ MediaControllerCompat.TransportControls transportControls =
+ BluetoothMediaBrowserService.getTransportControls();
+
+ //Shuffle
+ transportControls.setRepeatMode(2);
+ verify(mAvrcpControllerService, timeout(ASYNC_CALL_TIMEOUT_MILLIS).times(1))
+ .setPlayerApplicationSettingValuesNative(
+ eq(mTestAddress), eq((byte) 1), eq(repeatSetting), eq(repeatMode));
+ }
+
+ /**
* Test media browsing
* Verify that a browse tree is created with the proper root
* Verify that a player can be fetched and added to the browse tree
@@ -475,7 +513,7 @@
BrowseTree.BrowseNode playerNodes = mAvrcpStateMachine.findNode(results.getID());
mAvrcpStateMachine.requestContents(results);
- MediaController.TransportControls transportControls =
+ MediaControllerCompat.TransportControls transportControls =
BluetoothMediaBrowserService.getTransportControls();
transportControls.play();
verify(mAvrcpControllerService,