Merge "Move libutils static -> shared."
diff --git a/AndroidManifest.xml b/AndroidManifest.xml
index 3b505fd..b43606a 100644
--- a/AndroidManifest.xml
+++ b/AndroidManifest.xml
@@ -310,7 +310,7 @@
</service>
<service
android:process="@string/process"
- android:name=".a2dpsink.mbs.A2dpMediaBrowserService"
+ android:name=".avrcpcontroller.BluetoothMediaBrowserService"
android:exported="true"
android:enabled="@bool/profile_supported_a2dp_sink"
android:label="@string/a2dp_sink_mbs_label">
diff --git a/src/com/android/bluetooth/a2dpsink/A2dpSinkService.java b/src/com/android/bluetooth/a2dpsink/A2dpSinkService.java
index 17c8885..17095c0 100644
--- a/src/com/android/bluetooth/a2dpsink/A2dpSinkService.java
+++ b/src/com/android/bluetooth/a2dpsink/A2dpSinkService.java
@@ -25,8 +25,8 @@
import android.util.Log;
import com.android.bluetooth.Utils;
-import com.android.bluetooth.a2dpsink.mbs.A2dpMediaBrowserService;
import com.android.bluetooth.avrcpcontroller.AvrcpControllerService;
+import com.android.bluetooth.avrcpcontroller.BluetoothMediaBrowserService;
import com.android.bluetooth.btservice.ProfileService;
import java.util.ArrayList;
@@ -54,7 +54,7 @@
Log.d(TAG, "start()");
}
// Start the media browser service.
- Intent startIntent = new Intent(this, A2dpMediaBrowserService.class);
+ Intent startIntent = new Intent(this, BluetoothMediaBrowserService.class);
startService(startIntent);
mStateMachine = A2dpSinkStateMachine.make(this, this);
setA2dpSinkService(this);
@@ -70,7 +70,7 @@
if (mStateMachine != null) {
mStateMachine.doQuit();
}
- Intent stopIntent = new Intent(this, A2dpMediaBrowserService.class);
+ Intent stopIntent = new Intent(this, BluetoothMediaBrowserService.class);
stopService(stopIntent);
return true;
}
diff --git a/src/com/android/bluetooth/avrcpcontroller/AvrcpControllerService.java b/src/com/android/bluetooth/avrcpcontroller/AvrcpControllerService.java
index 76adadb..d234c5b 100644
--- a/src/com/android/bluetooth/avrcpcontroller/AvrcpControllerService.java
+++ b/src/com/android/bluetooth/avrcpcontroller/AvrcpControllerService.java
@@ -172,8 +172,8 @@
/* Folder navigation directions
* This is borrowed from AVRCP 1.6 spec and must be kept with same values
*/
- public static final int FOLDER_NAVIGATION_DIRECTION_UP = 0x00;
- public static final int FOLDER_NAVIGATION_DIRECTION_DOWN = 0x01;
+ public static final byte FOLDER_NAVIGATION_DIRECTION_UP = 0x00;
+ public static final byte FOLDER_NAVIGATION_DIRECTION_DOWN = 0x01;
/* Folder/Media Item scopes.
* Keep in sync with AVRCP 1.6 sec. 6.10.1
@@ -398,77 +398,12 @@
}
/**
- * Fetches the list of children for the parentID node.
- *
- * This function manages the overall tree for browsing structure.
- *
- * Arguments:
- * device - Device to browse content for.
- * parentMediaId - ID of the parent that we need to browse content for. Since most
- * of the players are database unware, fetching a root invalidates all the children.
- * start - number of item to start scanning from
- * items - number of items to fetch
+ * Retreive the contents of the directory from the associated bluetooth device.
*/
- public synchronized boolean getChildren(BluetoothDevice device, String parentMediaId, int start,
- int items) {
- if (DBG) {
- Log.d(TAG, "getChildren device = " + device + " parent " + parentMediaId);
- }
-
- if (device == null) {
- Log.e(TAG, "getChildren device is null");
- return false;
- }
-
- if (!device.equals(mConnectedDevice)) {
- Log.e(TAG, "getChildren device " + device + " does not match " + mConnectedDevice);
- return false;
- }
-
- if (!mBrowseConnected) {
- Log.e(TAG, "getChildren browse not yet connected");
- return false;
- }
-
- if (!mAvrcpCtSm.isConnected()) {
- return false;
- }
- mAvrcpCtSm.getChildren(parentMediaId, start, items);
- return true;
+ public synchronized List<MediaItem> getContents(BluetoothDevice device, String parentMediaId) {
+ return mAvrcpCtSm.getContents(parentMediaId);
}
-
- public synchronized boolean getNowPlayingList(BluetoothDevice device, String id, int start,
- int items) {
- if (DBG) {
- Log.d(TAG, "getNowPlayingList device = " + device + " start = " + start + "items = "
- + items);
- }
-
- if (device == null) {
- Log.e(TAG, "getNowPlayingList device is null");
- return false;
- }
-
- if (!device.equals(mConnectedDevice)) {
- Log.e(TAG,
- "getNowPlayingList device " + device + " does not match " + mConnectedDevice);
- return false;
- }
-
- if (!mBrowseConnected) {
- Log.e(TAG, "getNowPlayingList browse not yet connected");
- return false;
- }
-
- enforceCallingOrSelfPermission(BLUETOOTH_PERM, "Need BLUETOOTH permission");
-
- Message msg =
- mAvrcpCtSm.obtainMessage(AvrcpControllerStateMachine.MESSAGE_GET_NOW_PLAYING_LIST,
- start, items, id);
- mAvrcpCtSm.sendMessage(msg);
- return true;
- }
-
+/*
public synchronized boolean getFolderList(BluetoothDevice device, String id, int start,
int items) {
if (DBG) {
@@ -499,100 +434,7 @@
mAvrcpCtSm.sendMessage(msg);
return true;
}
-
- public synchronized boolean getPlayerList(BluetoothDevice device, int start, int items) {
- if (DBG) {
- Log.d(TAG,
- "getPlayerList device = " + device + " start = " + start + "items = " + items);
- }
-
- if (device == null) {
- Log.e(TAG, "getPlayerList device is null");
- return false;
- }
-
- if (!device.equals(mConnectedDevice)) {
- Log.e(TAG, "getPlayerList device " + device + " does not match " + mConnectedDevice);
- return false;
- }
-
- if (!mBrowseConnected) {
- Log.e(TAG, "getPlayerList browse not yet connected");
- return false;
- }
-
- enforceCallingOrSelfPermission(BLUETOOTH_PERM, "Need BLUETOOTH permission");
-
- Message msg =
- mAvrcpCtSm.obtainMessage(AvrcpControllerStateMachine.MESSAGE_GET_PLAYER_LIST, start,
- items);
- mAvrcpCtSm.sendMessage(msg);
- return true;
- }
-
- public synchronized boolean changeFolderPath(BluetoothDevice device, int direction, String uid,
- String fid) {
- if (DBG) {
- Log.d(TAG, "changeFolderPath device = " + device + " direction " + direction + " uid "
- + uid);
- }
-
- if (device == null) {
- Log.e(TAG, "changeFolderPath device is null");
- return false;
- }
-
- if (!device.equals(mConnectedDevice)) {
- Log.e(TAG, "changeFolderPath device " + device + " does not match " + mConnectedDevice);
- return false;
- }
-
- if (!mBrowseConnected) {
- Log.e(TAG, "changeFolderPath browse not yet connected");
- return false;
- }
-
- enforceCallingOrSelfPermission(BLUETOOTH_PERM, "Need BLUETOOTH permission");
-
- Bundle b = new Bundle();
- b.putString(EXTRA_FOLDER_ID, fid);
- b.putString(EXTRA_FOLDER_BT_ID, uid);
- Message msg =
- mAvrcpCtSm.obtainMessage(AvrcpControllerStateMachine.MESSAGE_CHANGE_FOLDER_PATH,
- direction, 0, b);
- mAvrcpCtSm.sendMessage(msg);
- return true;
- }
-
- public synchronized boolean setBrowsedPlayer(BluetoothDevice device, int id, String fid) {
- if (DBG) {
- Log.d(TAG, "setBrowsedPlayer device = " + device + " id" + id + " fid " + fid);
- }
-
- if (device == null) {
- Log.e(TAG, "setBrowsedPlayer device is null");
- return false;
- }
-
- if (!device.equals(mConnectedDevice)) {
- Log.e(TAG, "changeFolderPath device " + device + " does not match " + mConnectedDevice);
- return false;
- }
-
- if (!mBrowseConnected) {
- Log.e(TAG, "setBrowsedPlayer browse not yet connected");
- return false;
- }
-
- enforceCallingOrSelfPermission(BLUETOOTH_PERM, "Need BLUETOOTH permission");
-
- Message msg =
- mAvrcpCtSm.obtainMessage(AvrcpControllerStateMachine.MESSAGE_SET_BROWSED_PLAYER, id,
- 0, fid);
- mAvrcpCtSm.sendMessage(msg);
- return true;
- }
-
+*/
public synchronized void fetchAttrAndPlayItem(BluetoothDevice device, String uid) {
if (DBG) {
Log.d(TAG, "fetchAttrAndPlayItem device = " + device + " uid " + uid);
diff --git a/src/com/android/bluetooth/avrcpcontroller/AvrcpControllerStateMachine.java b/src/com/android/bluetooth/avrcpcontroller/AvrcpControllerStateMachine.java
index 5624d22..b2b8f0a 100644
--- a/src/com/android/bluetooth/avrcpcontroller/AvrcpControllerStateMachine.java
+++ b/src/com/android/bluetooth/avrcpcontroller/AvrcpControllerStateMachine.java
@@ -17,7 +17,6 @@
package com.android.bluetooth.avrcpcontroller;
import android.bluetooth.BluetoothAvrcpController;
-import android.bluetooth.BluetoothAvrcpPlayerSettings;
import android.bluetooth.BluetoothDevice;
import android.bluetooth.BluetoothProfile;
import android.content.BroadcastReceiver;
@@ -25,11 +24,9 @@
import android.content.Intent;
import android.content.IntentFilter;
import android.media.AudioManager;
-import android.media.MediaDescription;
import android.media.MediaMetadata;
import android.media.browse.MediaBrowser.MediaItem;
import android.media.session.PlaybackState;
-import android.os.Bundle;
import android.os.Message;
import android.util.Log;
import android.util.SparseArray;
@@ -54,12 +51,8 @@
// commands from Binder service
static final int MESSAGE_SEND_PASS_THROUGH_CMD = 1;
static final int MESSAGE_SEND_GROUP_NAVIGATION_CMD = 3;
- static final int MESSAGE_GET_NOW_PLAYING_LIST = 5;
static final int MESSAGE_GET_FOLDER_LIST = 6;
- static final int MESSAGE_GET_PLAYER_LIST = 7;
- static final int MESSAGE_CHANGE_FOLDER_PATH = 8;
static final int MESSAGE_FETCH_ATTR_AND_PLAY_ITEM = 9;
- static final int MESSAGE_SET_BROWSED_PLAYER = 10;
// commands from native layer
static final int MESSAGE_PROCESS_SET_ABS_VOL_CMD = 103;
@@ -86,8 +79,6 @@
static final int MESSAGE_PROCESS_BROWSE_CONNECTION_CHANGE = 303;
// Interal messages
- static final int MESSAGE_INTERNAL_BROWSE_DEPTH_INCREMENT = 401;
- static final int MESSAGE_INTERNAL_MOVE_N_LEVELS_UP = 402;
static final int MESSAGE_INTERNAL_CMD_TIMEOUT = 403;
static final int MESSAGE_INTERNAL_ABS_VOL_TIMEOUT = 404;
@@ -119,15 +110,10 @@
private final State mDisconnected;
private final State mConnected;
- private final SetBrowsedPlayer mSetBrowsedPlayer;
private final SetAddresedPlayerAndPlayItem mSetAddrPlayer;
- private final ChangeFolderPath mChangeFolderPath;
private final GetFolderList mGetFolderList;
- private final GetPlayerListing mGetPlayerListing;
- private final MoveToRoot mMoveToRoot;
private final Object mLock = new Object();
- private static final ArrayList<MediaItem> EMPTY_MEDIA_ITEM_LIST = new ArrayList<>();
private static final MediaMetadata EMPTY_MEDIA_METADATA = new MediaMetadata.Builder().build();
// APIs exist to access these so they must be thread safe
@@ -141,9 +127,6 @@
private int mAddressedPlayerID = -1;
private SparseArray<AvrcpPlayer> mAvailablePlayerList = new SparseArray<AvrcpPlayer>();
- // Depth from root of current browsing. This can be used to move to root directly.
- private int mBrowseDepth = 0;
-
// Browse tree.
private BrowseTree mBrowseTree = new BrowseTree();
@@ -159,12 +142,8 @@
mConnected = new Connected();
// Used to change folder path and fetch the new folder listing.
- mSetBrowsedPlayer = new SetBrowsedPlayer();
mSetAddrPlayer = new SetAddresedPlayerAndPlayItem();
- mChangeFolderPath = new ChangeFolderPath();
mGetFolderList = new GetFolderList();
- mGetPlayerListing = new GetPlayerListing();
- mMoveToRoot = new MoveToRoot();
addState(mDisconnected);
addState(mConnected);
@@ -173,12 +152,8 @@
// a separate substate of the mConnected state. Once transtition to the sub-state we should
// only handle the messages that are relevant to the sub-action. Everything else should be
// deferred so that once we transition to the mConnected we can process them hence.
- addState(mSetBrowsedPlayer, mConnected);
addState(mSetAddrPlayer, mConnected);
- addState(mChangeFolderPath, mConnected);
addState(mGetFolderList, mConnected);
- addState(mGetPlayerListing, mConnected);
- addState(mMoveToRoot, mConnected);
setInitialState(mDisconnected);
}
@@ -191,7 +166,7 @@
switch (msg.what) {
case MESSAGE_PROCESS_CONNECTION_CHANGE:
if (msg.arg1 == BluetoothProfile.STATE_CONNECTED) {
- mBrowseTree.init();
+ mBrowseTree = new BrowseTree();
transitionTo(mConnected);
BluetoothDevice rtDevice = (BluetoothDevice) msg.obj;
synchronized (mLock) {
@@ -243,68 +218,36 @@
mRemoteDevice.getBluetoothAddress(), msg.arg1, msg.arg2);
break;
- case MESSAGE_GET_NOW_PLAYING_LIST:
- mGetFolderList.setFolder((String) msg.obj);
- mGetFolderList.setBounds((int) msg.arg1, (int) msg.arg2);
- mGetFolderList.setScope(AvrcpControllerService.BROWSE_SCOPE_NOW_PLAYING);
- transitionTo(mGetFolderList);
- break;
-
case MESSAGE_GET_FOLDER_LIST:
// Whenever we transition we set the information for folder we need to
// return result.
- mGetFolderList.setBounds(msg.arg1, msg.arg2);
+ if (DBG) Log.d(TAG, "Message_GET_FOLDER_LIST" + (String) msg.obj);
mGetFolderList.setFolder((String) msg.obj);
- mGetFolderList.setScope(AvrcpControllerService.BROWSE_SCOPE_VFS);
transitionTo(mGetFolderList);
break;
- case MESSAGE_GET_PLAYER_LIST:
- AvrcpControllerService.getPlayerListNative(
- mRemoteDevice.getBluetoothAddress(), (byte) msg.arg1,
- (byte) msg.arg2);
- transitionTo(mGetPlayerListing);
- sendMessageDelayed(MESSAGE_INTERNAL_CMD_TIMEOUT, CMD_TIMEOUT_MILLIS);
- break;
-
- case MESSAGE_CHANGE_FOLDER_PATH: {
- int direction = msg.arg1;
- Bundle b = (Bundle) msg.obj;
- String uid = b.getString(AvrcpControllerService.EXTRA_FOLDER_BT_ID);
- String fid = b.getString(AvrcpControllerService.EXTRA_FOLDER_ID);
-
- // String is encoded as a Hex String (mostly for display purposes)
- // hence convert this back to real byte string.
- AvrcpControllerService.changeFolderPathNative(
- mRemoteDevice.getBluetoothAddress(), (byte) msg.arg1,
- AvrcpControllerService.hexStringToByteUID(uid));
- mChangeFolderPath.setFolder(fid);
- transitionTo(mChangeFolderPath);
- sendMessage(MESSAGE_INTERNAL_BROWSE_DEPTH_INCREMENT, (byte) msg.arg1);
- sendMessageDelayed(MESSAGE_INTERNAL_CMD_TIMEOUT, CMD_TIMEOUT_MILLIS);
- break;
- }
-
case MESSAGE_FETCH_ATTR_AND_PLAY_ITEM: {
int scope = msg.arg1;
String playItemUid = (String) msg.obj;
BrowseTree.BrowseNode currBrPlayer = mBrowseTree.getCurrentBrowsedPlayer();
BrowseTree.BrowseNode currAddrPlayer =
mBrowseTree.getCurrentAddressedPlayer();
+ BrowseTree.BrowseNode itemToPlay =
+ mBrowseTree.findBrowseNodeByID(playItemUid);
if (DBG) {
Log.d(TAG, "currBrPlayer " + currBrPlayer + " currAddrPlayer "
+ currAddrPlayer);
}
-
- if (currBrPlayer == null || currBrPlayer.equals(currAddrPlayer)) {
+ if (currBrPlayer == null
+ || currBrPlayer.equals(currAddrPlayer)
+ || scope == AvrcpControllerService.BROWSE_SCOPE_NOW_PLAYING) {
// String is encoded as a Hex String (mostly for display purposes)
// hence convert this back to real byte string.
// NOTE: It may be possible that sending play while the same item is
// playing leads to reset of track.
AvrcpControllerService.playItemNative(
mRemoteDevice.getBluetoothAddress(), (byte) scope,
- AvrcpControllerService.hexStringToByteUID(playItemUid),
- (int) 0);
+ AvrcpControllerService.hexStringToByteUID(playItemUid), 0);
} else {
// Send out the request for setting addressed player.
AvrcpControllerService.setAddressedPlayerNative(
@@ -317,22 +260,6 @@
break;
}
- case MESSAGE_SET_BROWSED_PLAYER: {
- AvrcpControllerService.setBrowsedPlayerNative(
- mRemoteDevice.getBluetoothAddress(), (int) msg.arg1);
- mSetBrowsedPlayer.setFolder((String) msg.obj);
- transitionTo(mSetBrowsedPlayer);
- break;
- }
-
- case MESSAGE_PROCESS_SET_ADDRESSED_PLAYER:
- AvrcpControllerService.getPlayerListNative(
- mRemoteDevice.getBluetoothAddress(), 0, 255);
- transitionTo(mGetPlayerListing);
- sendMessageDelayed(MESSAGE_INTERNAL_CMD_TIMEOUT, CMD_TIMEOUT_MILLIS);
- break;
-
-
case MESSAGE_PROCESS_CONNECTION_CHANGE:
if (msg.arg1 == BluetoothProfile.STATE_DISCONNECTED) {
synchronized (mLock) {
@@ -370,9 +297,6 @@
} else if (msg.arg1 == 0) {
intent.putExtra(BluetoothProfile.EXTRA_STATE,
BluetoothProfile.STATE_DISCONNECTED);
- // If browse is disconnected, the next time we connect we should
- // be at the ROOT.
- mBrowseDepth = 0;
} else {
Log.w(TAG, "Incorrect browse state " + msg.arg1);
}
@@ -438,9 +362,13 @@
case MESSAGE_PROCESS_TRACK_CHANGED:
// Music start playing automatically and update Metadata
- mAddressedPlayer.updateCurrentTrack((TrackInfo) msg.obj);
- broadcastMetaDataChanged(
- mAddressedPlayer.getCurrentTrack().getMediaMetaData());
+ boolean updateTrack =
+ mAddressedPlayer.updateCurrentTrack((TrackInfo) msg.obj);
+ if (updateTrack) {
+ mBrowseTree.mNowPlayingNode.setCached(false);
+ broadcastMetaDataChanged(
+ mAddressedPlayer.getCurrentTrack().getMediaMetaData());
+ }
break;
case MESSAGE_PROCESS_PLAY_POS_CHANGED:
@@ -469,11 +397,14 @@
if (updatedPlayer != null) {
mAddressedPlayer = updatedPlayer;
if (DBG) Log.d(TAG, "AddressedPlayer = " + mAddressedPlayer.getName());
+ } else {
+ mBrowseTree.mRootNode.setCached(false);
}
sendMessage(MESSAGE_PROCESS_SET_ADDRESSED_PLAYER);
break;
default:
+ Log.d(TAG, "Unhandled message" + msg.what);
return false;
}
}
@@ -481,133 +412,31 @@
}
}
- // Handle the change folder path meta-action.
- // a) Send Change folder command
- // b) Once successful transition to folder fetch state.
- class ChangeFolderPath extends CmdState {
- private static final String STATE_TAG = "AVRCPSM.ChangeFolderPath";
- private int mTmpIncrDirection;
- private String mID = "";
-
- public void setFolder(String id) {
- mID = id;
- }
-
- @Override
- public void enter() {
- super.enter();
- mTmpIncrDirection = -1;
- }
-
- @Override
- public boolean processMessage(Message msg) {
- if (DBG) Log.d(STATE_TAG, "processMessage " + msg.what);
- switch (msg.what) {
- case MESSAGE_INTERNAL_BROWSE_DEPTH_INCREMENT:
- mTmpIncrDirection = msg.arg1;
- break;
-
- case MESSAGE_PROCESS_FOLDER_PATH: {
- // Fetch the listing of objects in this folder.
- if (DBG) {
- Log.d(STATE_TAG,
- "MESSAGE_PROCESS_FOLDER_PATH returned " + msg.arg1 + " elements");
- }
-
- // Update the folder depth.
- if (mTmpIncrDirection
- == AvrcpControllerService.FOLDER_NAVIGATION_DIRECTION_UP) {
- mBrowseDepth -= 1;
- } else if (mTmpIncrDirection
- == AvrcpControllerService.FOLDER_NAVIGATION_DIRECTION_DOWN) {
- mBrowseDepth += 1;
- } else {
- throw new IllegalStateException("incorrect nav " + mTmpIncrDirection);
- }
- if (DBG) Log.d(STATE_TAG, "New browse depth " + mBrowseDepth);
-
- if (msg.arg1 > 0) {
- sendMessage(MESSAGE_GET_FOLDER_LIST, 0, msg.arg1 - 1, mID);
- } else {
- // Return an empty response to the upper layer.
- broadcastFolderList(mID, EMPTY_MEDIA_ITEM_LIST);
- }
- mBrowseTree.setCurrentBrowsedFolder(mID);
- transitionTo(mConnected);
- break;
- }
-
- case MESSAGE_INTERNAL_CMD_TIMEOUT:
- // We timed out changing folders. It is imperative we tell
- // the upper layers that we failed by giving them an empty list.
- Log.e(STATE_TAG, "change folder failed, sending empty list.");
- broadcastFolderList(mID, EMPTY_MEDIA_ITEM_LIST);
- transitionTo(mConnected);
- break;
-
- case MESSAGE_SEND_PASS_THROUGH_CMD:
- case MESSAGE_SEND_GROUP_NAVIGATION_CMD:
- case MESSAGE_PROCESS_SET_ABS_VOL_CMD:
- case MESSAGE_PROCESS_REGISTER_ABS_VOL_NOTIFICATION:
- case MESSAGE_PROCESS_TRACK_CHANGED:
- case MESSAGE_PROCESS_PLAY_POS_CHANGED:
- case MESSAGE_PROCESS_PLAY_STATUS_CHANGED:
- case MESSAGE_PROCESS_VOLUME_CHANGED_NOTIFICATION:
- case MESSAGE_STOP_METADATA_BROADCASTS:
- case MESSAGE_START_METADATA_BROADCASTS:
- case MESSAGE_PROCESS_CONNECTION_CHANGE:
- case MESSAGE_PROCESS_BROWSE_CONNECTION_CHANGE:
- // All of these messages should be handled by parent state immediately.
- return false;
-
- default:
- if (DBG) {
- Log.d(STATE_TAG, "deferring message " + msg.what + " to Connected state.");
- }
- deferMessage(msg);
- }
- return true;
- }
- }
-
// Handle the get folder listing action
// a) Fetch the listing of folders
// b) Once completed return the object listing
class GetFolderList extends CmdState {
private static final String STATE_TAG = "AVRCPSM.GetFolderList";
- String mID = "";
- int mStartInd;
- int mEndInd;
- int mCurrInd;
- int mScope;
- private ArrayList<MediaItem> mFolderList = new ArrayList<>();
+ boolean mAbort;
+ BrowseTree.BrowseNode mBrowseNode;
+ BrowseTree.BrowseNode mNextStep;
@Override
public void enter() {
// Setup the timeouts.
super.enter();
- mCurrInd = 0;
- mFolderList.clear();
- callNativeFunctionForScope(mStartInd,
- Math.min(mEndInd, mStartInd + GET_FOLDER_ITEMS_PAGINATION_SIZE - 1));
- }
-
- public void setScope(int scope) {
- mScope = scope;
+ mAbort = false;
+ if (mBrowseNode == null) {
+ transitionTo(mConnected);
+ } else {
+ navigateToFolderOrRetrieve(mBrowseNode);
+ }
}
public void setFolder(String id) {
if (DBG) Log.d(STATE_TAG, "Setting folder to " + id);
- mID = id;
- }
-
- public void setBounds(int startInd, int endInd) {
- if (DBG) {
- Log.d(STATE_TAG, "startInd " + startInd + " endInd " + endInd);
- }
- mStartInd = startInd;
- mEndInd = Math.min(endInd, MAX_FOLDER_ITEMS);
+ mBrowseNode = mBrowseTree.findBrowseNodeByID(id);
}
@Override
@@ -616,37 +445,75 @@
switch (msg.what) {
case MESSAGE_PROCESS_GET_FOLDER_ITEMS:
ArrayList<MediaItem> folderList = (ArrayList<MediaItem>) msg.obj;
- mFolderList.addAll(folderList);
+ int endIndicator = mBrowseNode.getExpectedChildren() - 1;
if (DBG) {
Log.d(STATE_TAG,
- "Start " + mStartInd + " End " + mEndInd + " Curr " + mCurrInd
+ " End " + endIndicator
+ " received " + folderList.size());
}
- mCurrInd += folderList.size();
// Always update the node so that the user does not wait forever
// for the list to populate.
- sendFolderBroadcastAndUpdateNode();
+ mBrowseNode.addChildren(folderList);
+ broadcastFolderList(mBrowseNode.getID());
- if (mCurrInd > mEndInd || folderList.size() == 0) {
+ if (mBrowseNode.getChildrenCount() >= endIndicator || folderList.size() == 0
+ || mAbort) {
// If we have fetched all the elements or if the remotes sends us 0 elements
// (which can lead us into a loop since mCurrInd does not proceed) we simply
// abort.
+ mBrowseNode.setCached(true);
transitionTo(mConnected);
} else {
// Fetch the next set of items.
- callNativeFunctionForScope(mCurrInd, Math.min(mEndInd,
- mCurrInd + GET_FOLDER_ITEMS_PAGINATION_SIZE - 1));
+ fetchContents(mBrowseNode);
// Reset the timeout message since we are doing a new fetch now.
removeMessages(MESSAGE_INTERNAL_CMD_TIMEOUT);
sendMessageDelayed(MESSAGE_INTERNAL_CMD_TIMEOUT, CMD_TIMEOUT_MILLIS);
}
break;
+ case MESSAGE_PROCESS_SET_BROWSED_PLAYER:
+ mBrowseTree.setCurrentBrowsedPlayer(mNextStep.getID(), msg.arg1, msg.arg2);
+ removeMessages(MESSAGE_INTERNAL_CMD_TIMEOUT);
+ sendMessageDelayed(MESSAGE_INTERNAL_CMD_TIMEOUT, CMD_TIMEOUT_MILLIS);
+ navigateToFolderOrRetrieve(mBrowseNode);
+ break;
+
+ case MESSAGE_PROCESS_FOLDER_PATH:
+ mBrowseTree.setCurrentBrowsedFolder(mNextStep.getID());
+ mBrowseTree.getCurrentBrowsedFolder().setExpectedChildren(msg.arg1);
+
+ if (mAbort) {
+ transitionTo(mConnected);
+ } else {
+ removeMessages(MESSAGE_INTERNAL_CMD_TIMEOUT);
+ sendMessageDelayed(MESSAGE_INTERNAL_CMD_TIMEOUT, CMD_TIMEOUT_MILLIS);
+ navigateToFolderOrRetrieve(mBrowseNode);
+ }
+ break;
+
+ case MESSAGE_PROCESS_GET_PLAYER_ITEMS:
+ BrowseTree.BrowseNode rootNode = mBrowseTree.mRootNode;
+ if (!rootNode.isCached()) {
+ List<AvrcpPlayer> playerList = (List<AvrcpPlayer>) msg.obj;
+ mAvailablePlayerList.clear();
+ for (AvrcpPlayer player : playerList) {
+ mAvailablePlayerList.put(player.getId(), player);
+ }
+ rootNode.addChildren(playerList);
+ mBrowseTree.setCurrentBrowsedFolder(BrowseTree.ROOT);
+ rootNode.setExpectedChildren(playerList.size());
+ rootNode.setCached(true);
+ broadcastFolderList(BrowseTree.ROOT);
+ }
+ transitionTo(mConnected);
+ break;
+
case MESSAGE_INTERNAL_CMD_TIMEOUT:
// We have timed out to execute the request, we should simply send
// whatever listing we have gotten until now.
- sendFolderBroadcastAndUpdateNode();
+ broadcastFolderList(mBrowseNode.getID());
transitionTo(mConnected);
break;
@@ -654,16 +521,24 @@
// If we have gotten an error for OUT OF RANGE we have
// already sent all the items to the client hence simply
// transition to Connected state here.
+ mBrowseNode.setCached(true);
+ broadcastFolderList(mBrowseNode.getID());
transitionTo(mConnected);
break;
- case MESSAGE_CHANGE_FOLDER_PATH:
+ case MESSAGE_GET_FOLDER_LIST:
+ if (!mBrowseNode.equals((String) msg.obj)) {
+ mAbort = true;
+ deferMessage(msg);
+ Log.d(STATE_TAG, "Go Get Another Directory");
+ } else {
+ Log.d(STATE_TAG, "Get The Same Directory, ignore");
+ }
+ break;
+
case MESSAGE_FETCH_ATTR_AND_PLAY_ITEM:
- case MESSAGE_GET_PLAYER_LIST:
- case MESSAGE_GET_NOW_PLAYING_LIST:
- case MESSAGE_SET_BROWSED_PLAYER:
// A new request has come in, no need to fetch more.
- mEndInd = 0;
+ mAbort = true;
deferMessage(msg);
break;
@@ -689,246 +564,85 @@
return true;
}
- private void sendFolderBroadcastAndUpdateNode() {
- BrowseTree.BrowseNode bn = mBrowseTree.findBrowseNodeByID(mID);
- if (bn == null) {
- Log.e(TAG, "Can not find BrowseNode by ID: " + mID);
- return;
- }
- if (bn.isPlayer()) {
- // Add the now playing folder.
- MediaDescription.Builder mdb = new MediaDescription.Builder();
- mdb.setMediaId(BrowseTree.NOW_PLAYING_PREFIX + ":" + bn.getPlayerID());
- mdb.setTitle(BrowseTree.NOW_PLAYING_PREFIX);
- Bundle mdBundle = new Bundle();
- mdBundle.putString(AvrcpControllerService.MEDIA_ITEM_UID_KEY,
- BrowseTree.NOW_PLAYING_PREFIX + ":" + bn.getID());
- mdb.setExtras(mdBundle);
- mFolderList.add(new MediaItem(mdb.build(), MediaItem.FLAG_BROWSABLE));
- }
- mBrowseTree.refreshChildren(bn, mFolderList);
- broadcastFolderList(mID, mFolderList);
-
- // For now playing we need to set the current browsed folder here.
- // For normal folders it is set after ChangeFolderPath.
- if (mScope == AvrcpControllerService.BROWSE_SCOPE_NOW_PLAYING) {
- mBrowseTree.setCurrentBrowsedFolder(mID);
- }
- }
-
- private void callNativeFunctionForScope(int start, int end) {
- switch (mScope) {
+ private void fetchContents(BrowseTree.BrowseNode target) {
+ switch (target.getScope()) {
+ case AvrcpControllerService.BROWSE_SCOPE_PLAYER_LIST:
+ AvrcpControllerService.getPlayerListNative(mRemoteDevice.getBluetoothAddress(),
+ 0, 255);
+ break;
case AvrcpControllerService.BROWSE_SCOPE_NOW_PLAYING:
AvrcpControllerService.getNowPlayingListNative(
- mRemoteDevice.getBluetoothAddress(), start, end);
+ mRemoteDevice.getBluetoothAddress(), target.getChildrenCount(),
+ Math.min(target.getExpectedChildren(), target.getChildrenCount()
+ + GET_FOLDER_ITEMS_PAGINATION_SIZE - 1));
break;
case AvrcpControllerService.BROWSE_SCOPE_VFS:
AvrcpControllerService.getFolderListNative(mRemoteDevice.getBluetoothAddress(),
- start, end);
+ target.getChildrenCount(), Math.min(target.getExpectedChildren(),
+ target.getChildrenCount() + GET_FOLDER_ITEMS_PAGINATION_SIZE - 1));
break;
default:
- Log.e(STATE_TAG, "Scope " + mScope + " cannot be handled here.");
+ Log.e(STATE_TAG, "Scope " + target.getScope() + " cannot be handled here.");
}
}
- }
- // Handle the get player listing action
- // a) Fetch the listing of players
- // b) Once completed return the object listing
- class GetPlayerListing extends CmdState {
- private static final String STATE_TAG = "AVRCPSM.GetPlayerList";
-
- @Override
- public boolean processMessage(Message msg) {
- if (DBG) Log.d(STATE_TAG, "processMessage " + msg.what);
- switch (msg.what) {
- case MESSAGE_PROCESS_GET_PLAYER_ITEMS:
- List<AvrcpPlayer> playerList = (List<AvrcpPlayer>) msg.obj;
- mAvailablePlayerList.clear();
- for (AvrcpPlayer player : playerList) {
- mAvailablePlayerList.put(player.getId(), player);
- }
- mBrowseTree.refreshChildren(BrowseTree.ROOT, playerList);
- ArrayList<MediaItem> mediaItemList = new ArrayList<>();
- for (BrowseTree.BrowseNode c : mBrowseTree.findBrowseNodeByID(BrowseTree.ROOT)
- .getChildren()) {
- mediaItemList.add(c.getMediaItem());
- }
- if (DBG) Log.d(TAG, "AddressedPlayer List Updated");
- AvrcpPlayer updatedPlayer = mAvailablePlayerList.get(mAddressedPlayerID);
- if (updatedPlayer != null) {
- mAddressedPlayer = updatedPlayer;
- if (DBG) Log.d(TAG, "AddressedPlayer = " + mAddressedPlayer.getName());
- }
- broadcastFolderList(BrowseTree.ROOT, mediaItemList);
- mBrowseTree.setCurrentBrowsedFolder(BrowseTree.ROOT);
- transitionTo(mConnected);
- break;
-
- case MESSAGE_INTERNAL_CMD_TIMEOUT:
- // We have timed out to execute the request.
- // Send an empty list here.
- broadcastFolderList(BrowseTree.ROOT, EMPTY_MEDIA_ITEM_LIST);
- transitionTo(mConnected);
- break;
-
- case MESSAGE_SEND_PASS_THROUGH_CMD:
- case MESSAGE_SEND_GROUP_NAVIGATION_CMD:
- case MESSAGE_PROCESS_SET_ABS_VOL_CMD:
- case MESSAGE_PROCESS_REGISTER_ABS_VOL_NOTIFICATION:
- case MESSAGE_PROCESS_TRACK_CHANGED:
- case MESSAGE_PROCESS_PLAY_POS_CHANGED:
- case MESSAGE_PROCESS_PLAY_STATUS_CHANGED:
- case MESSAGE_PROCESS_VOLUME_CHANGED_NOTIFICATION:
- case MESSAGE_STOP_METADATA_BROADCASTS:
- case MESSAGE_START_METADATA_BROADCASTS:
- case MESSAGE_PROCESS_CONNECTION_CHANGE:
- case MESSAGE_PROCESS_BROWSE_CONNECTION_CHANGE:
- // All of these messages should be handled by parent state immediately.
- return false;
-
- default:
- if (DBG) Log.d(STATE_TAG, "deferring message " + msg.what + " to connected!");
- deferMessage(msg);
- }
- return true;
- }
- }
-
- class MoveToRoot extends CmdState {
- private static final String STATE_TAG = "AVRCPSM.MoveToRoot";
- private String mID = "";
-
- public void setFolder(String id) {
- if (DBG) Log.d(STATE_TAG, "setFolder " + id);
- mID = id;
- }
-
- @Override
- public void enter() {
- // Setup the timeouts.
- super.enter();
-
- // We need to move mBrowseDepth levels up. The following message is
- // completely internal to this state.
- sendMessage(MESSAGE_INTERNAL_MOVE_N_LEVELS_UP);
- }
-
- @Override
- public boolean processMessage(Message msg) {
+ /* One of several things can happen when trying to get a folder list
+ *
+ *
+ * 0: The folder handle is no longer valid
+ * 1: The folder contents can be retrieved directly (NowPlaying, Root, Current)
+ * 2: The folder is a browsable player
+ * 3: The folder is a non browsable player
+ * 4: The folder is not a child of the current folder
+ * 5: The folder is a child of the current folder
+ *
+ */
+ private void navigateToFolderOrRetrieve(BrowseTree.BrowseNode target) {
+ mNextStep = mBrowseTree.getNextStepToFolder(target);
if (DBG) {
- Log.d(STATE_TAG, "processMessage " + msg.what + " browse depth " + mBrowseDepth);
+ Log.d(TAG, "NAVIGATING From " + mBrowseTree.getCurrentBrowsedFolder().toString());
+ Log.d(TAG, "NAVIGATING Toward " + target.toString());
}
- switch (msg.what) {
- case MESSAGE_INTERNAL_MOVE_N_LEVELS_UP:
- if (mBrowseDepth == 0) {
- Log.w(STATE_TAG, "Already in root!");
- transitionTo(mConnected);
- sendMessage(MESSAGE_GET_FOLDER_LIST, 0, 0xff, mID);
- } else {
- AvrcpControllerService.changeFolderPathNative(
- mRemoteDevice.getBluetoothAddress(),
- (byte) AvrcpControllerService.FOLDER_NAVIGATION_DIRECTION_UP,
- AvrcpControllerService.hexStringToByteUID(null));
- }
- break;
-
- case MESSAGE_PROCESS_FOLDER_PATH:
- mBrowseDepth -= 1;
- if (DBG) Log.d(STATE_TAG, "New browse depth " + mBrowseDepth);
- if (mBrowseDepth < 0) {
- throw new IllegalArgumentException("Browse depth negative!");
- }
-
- sendMessage(MESSAGE_INTERNAL_MOVE_N_LEVELS_UP);
- break;
-
- case MESSAGE_INTERNAL_CMD_TIMEOUT:
- broadcastFolderList(BrowseTree.ROOT, EMPTY_MEDIA_ITEM_LIST);
+ if (mNextStep == null) {
+ sendMessage(MESSAGE_INTERNAL_CMD_TIMEOUT);
+ } else if (target.equals(mBrowseTree.mNowPlayingNode)
+ || target.equals(mBrowseTree.mRootNode)
+ || mNextStep.equals(mBrowseTree.getCurrentBrowsedFolder())) {
+ fetchContents(mNextStep);
+ } else if (mNextStep.isPlayer()) {
+ if (DBG) Log.d(TAG, "NAVIGATING Player " + mNextStep.toString());
+ if (mNextStep.isBrowsable()) {
+ AvrcpControllerService.setBrowsedPlayerNative(
+ mRemoteDevice.getBluetoothAddress(), mNextStep.getPlayerID());
+ } else {
+ if (DBG) Log.d(TAG, "Player doesn't support browsing");
+ mNextStep.setCached(true);
+ broadcastFolderList(mNextStep.getID());
transitionTo(mConnected);
- break;
+ }
+ } else if (mNextStep.equals(mBrowseTree.mNavigateUpNode)) {
+ if (DBG) Log.d(TAG, "NAVIGATING UP " + mNextStep.toString());
+ mNextStep = mBrowseTree.getCurrentBrowsedFolder().getParent();
+ mBrowseTree.getCurrentBrowsedFolder().setCached(false);
- case MESSAGE_SEND_PASS_THROUGH_CMD:
- case MESSAGE_SEND_GROUP_NAVIGATION_CMD:
- case MESSAGE_PROCESS_SET_ABS_VOL_CMD:
- case MESSAGE_PROCESS_REGISTER_ABS_VOL_NOTIFICATION:
- case MESSAGE_PROCESS_TRACK_CHANGED:
- case MESSAGE_PROCESS_PLAY_POS_CHANGED:
- case MESSAGE_PROCESS_PLAY_STATUS_CHANGED:
- case MESSAGE_PROCESS_VOLUME_CHANGED_NOTIFICATION:
- case MESSAGE_STOP_METADATA_BROADCASTS:
- case MESSAGE_START_METADATA_BROADCASTS:
- case MESSAGE_PROCESS_CONNECTION_CHANGE:
- case MESSAGE_PROCESS_BROWSE_CONNECTION_CHANGE:
- // All of these messages should be handled by parent state immediately.
- return false;
+ AvrcpControllerService.changeFolderPathNative(
+ mRemoteDevice.getBluetoothAddress(),
+ AvrcpControllerService.FOLDER_NAVIGATION_DIRECTION_UP,
+ AvrcpControllerService.hexStringToByteUID(null));
- default:
- if (DBG) Log.d(STATE_TAG, "deferring message " + msg.what + " to connected!");
- deferMessage(msg);
+ } else {
+ if (DBG) Log.d(TAG, "NAVIGATING DOWN " + mNextStep.toString());
+ AvrcpControllerService.changeFolderPathNative(
+ mRemoteDevice.getBluetoothAddress(),
+ AvrcpControllerService.FOLDER_NAVIGATION_DIRECTION_DOWN,
+ AvrcpControllerService.hexStringToByteUID(mNextStep.getFolderUID()));
}
- return true;
- }
- }
-
- class SetBrowsedPlayer extends CmdState {
- private static final String STATE_TAG = "AVRCPSM.SetBrowsedPlayer";
- String mID = "";
-
- public void setFolder(String id) {
- mID = id;
}
@Override
- public boolean processMessage(Message msg) {
- if (DBG) Log.d(STATE_TAG, "processMessage " + msg.what);
- switch (msg.what) {
- case MESSAGE_PROCESS_SET_BROWSED_PLAYER:
- // Set the new depth.
- if (DBG) Log.d(STATE_TAG, "player depth " + msg.arg2);
- mBrowseDepth = msg.arg2;
-
- // If we already on top of player and there is no content.
- // This should very rarely happen.
- if (mBrowseDepth == 0 && msg.arg1 == 0) {
- broadcastFolderList(mID, EMPTY_MEDIA_ITEM_LIST);
- transitionTo(mConnected);
- } else {
- // Otherwise move to root and fetch the listing.
- // the MoveToRoot#enter() function takes care of fetch.
- mMoveToRoot.setFolder(mID);
- transitionTo(mMoveToRoot);
- }
- mBrowseTree.setCurrentBrowsedFolder(mID);
- // Also set the browsed player here.
- mBrowseTree.setCurrentBrowsedPlayer(mID);
- break;
-
- case MESSAGE_INTERNAL_CMD_TIMEOUT:
- broadcastFolderList(mID, EMPTY_MEDIA_ITEM_LIST);
- transitionTo(mConnected);
- break;
-
- case MESSAGE_SEND_PASS_THROUGH_CMD:
- case MESSAGE_SEND_GROUP_NAVIGATION_CMD:
- case MESSAGE_PROCESS_SET_ABS_VOL_CMD:
- case MESSAGE_PROCESS_REGISTER_ABS_VOL_NOTIFICATION:
- case MESSAGE_PROCESS_TRACK_CHANGED:
- case MESSAGE_PROCESS_PLAY_POS_CHANGED:
- case MESSAGE_PROCESS_PLAY_STATUS_CHANGED:
- case MESSAGE_PROCESS_VOLUME_CHANGED_NOTIFICATION:
- case MESSAGE_STOP_METADATA_BROADCASTS:
- case MESSAGE_START_METADATA_BROADCASTS:
- case MESSAGE_PROCESS_CONNECTION_CHANGE:
- case MESSAGE_PROCESS_BROWSE_CONNECTION_CHANGE:
- // All of these messages should be handled by parent state immediately.
- return false;
-
- default:
- if (DBG) Log.d(STATE_TAG, "deferring message " + msg.what + " to connected!");
- deferMessage(msg);
- }
- return true;
+ public void exit() {
+ mBrowseNode = null;
+ super.exit();
}
}
@@ -1062,110 +776,26 @@
}
}
- // Entry point to the state machine where the services should call to fetch children
- // for a specific node. It checks if the currently browsed node is the same as the one being
- // asked for, in that case it returns the currently cached children. This saves bandwidth and
- // also if we are already fetching elements for a current folder (since we need to batch
- // fetches) then we should not submit another request but simply return what we have fetched
- // until now.
- //
- // It handles fetches to all VFS, Now Playing and Media Player lists.
- void getChildren(String parentMediaId, int start, int items) {
- BrowseTree.BrowseNode bn = mBrowseTree.findBrowseNodeByID(parentMediaId);
- if (bn == null) {
- Log.e(TAG, "Invalid folder to browse " + mBrowseTree);
- broadcastFolderList(parentMediaId, EMPTY_MEDIA_ITEM_LIST);
- return;
- }
+ List<MediaItem> getContents(String uid) {
+ BrowseTree.BrowseNode currentNode = mBrowseTree.findBrowseNodeByID(uid);
- if (DBG) {
- Log.d(TAG, "To Browse folder " + bn + " is cached " + bn.isCached() + " current folder "
- + mBrowseTree.getCurrentBrowsedFolder());
- }
- if (bn.equals(mBrowseTree.getCurrentBrowsedFolder()) && bn.isCached()) {
- if (DBG) {
- Log.d(TAG, "Same cached folder -- returning existing children.");
+ if (DBG) Log.d(TAG, "getContents(" + uid + ") currentNode = " + currentNode);
+ if (currentNode != null) {
+ if (!currentNode.isCached()) {
+ sendMessage(AvrcpControllerStateMachine.MESSAGE_GET_FOLDER_LIST, uid);
}
- BrowseTree.BrowseNode n = mBrowseTree.findBrowseNodeByID(parentMediaId);
- ArrayList<MediaItem> childrenList = new ArrayList<MediaItem>();
- for (BrowseTree.BrowseNode cn : n.getChildren()) {
- childrenList.add(cn.getMediaItem());
- }
- broadcastFolderList(parentMediaId, childrenList);
- return;
+ return currentNode.getContents();
}
-
- Message msg = null;
- int btDirection = mBrowseTree.getDirection(parentMediaId);
- BrowseTree.BrowseNode currFol = mBrowseTree.getCurrentBrowsedFolder();
- if (DBG) {
- Log.d(TAG, "Browse direction parent " + mBrowseTree.getCurrentBrowsedFolder() + " req "
- + parentMediaId + " direction " + btDirection);
- }
- if (BrowseTree.ROOT.equals(parentMediaId)) {
- // Root contains the list of players.
- msg = obtainMessage(AvrcpControllerStateMachine.MESSAGE_GET_PLAYER_LIST, start, items);
- } else if (bn.isPlayer() && btDirection != BrowseTree.DIRECTION_SAME) {
- // Set browsed (and addressed player) as the new player.
- // This should fetch the list of folders.
- msg = obtainMessage(AvrcpControllerStateMachine.MESSAGE_SET_BROWSED_PLAYER,
- bn.getPlayerID(), 0, bn.getID());
- } else if (bn.isNowPlaying()) {
- // Issue a request to fetch the items.
- msg = obtainMessage(AvrcpControllerStateMachine.MESSAGE_GET_NOW_PLAYING_LIST, start,
- items, parentMediaId);
- } else {
- // Only change folder if desired. If an app refreshes a folder
- // (because it resumed etc) and current folder does not change
- // then we can simply fetch list.
-
- // We exempt two conditions from change folder:
- // a) If the new folder is the same as current folder (refresh of UI)
- // b) If the new folder is ROOT and current folder is NOW_PLAYING (or vice-versa)
- // In this condition we 'fake' child-parent hierarchy but it does not exist in
- // bluetooth world.
- boolean isNowPlayingToRoot =
- currFol.isNowPlaying() && bn.getID().equals(BrowseTree.ROOT);
- if (!isNowPlayingToRoot) {
- // Find the direction of traversal.
- int direction = -1;
- if (DBG) Log.d(TAG, "Browse direction " + currFol + " " + bn + " = " + btDirection);
- if (btDirection == BrowseTree.DIRECTION_UNKNOWN) {
- Log.w(TAG, "parent " + bn + " is not a direct "
- + "successor or predeccessor of current folder " + currFol);
- broadcastFolderList(parentMediaId, EMPTY_MEDIA_ITEM_LIST);
- return;
- }
-
- if (btDirection == BrowseTree.DIRECTION_DOWN) {
- direction = AvrcpControllerService.FOLDER_NAVIGATION_DIRECTION_DOWN;
- } else if (btDirection == BrowseTree.DIRECTION_UP) {
- direction = AvrcpControllerService.FOLDER_NAVIGATION_DIRECTION_UP;
- }
-
- Bundle b = new Bundle();
- b.putString(AvrcpControllerService.EXTRA_FOLDER_ID, bn.getID());
- b.putString(AvrcpControllerService.EXTRA_FOLDER_BT_ID, bn.getFolderUID());
- msg = obtainMessage(AvrcpControllerStateMachine.MESSAGE_CHANGE_FOLDER_PATH,
- direction, 0, b);
- } else {
- // Fetch the listing without changing paths.
- msg = obtainMessage(AvrcpControllerStateMachine.MESSAGE_GET_FOLDER_LIST, start,
- items, bn.getFolderUID());
- }
- }
-
- if (msg != null) {
- sendMessage(msg);
- }
+ return null;
}
public void fetchAttrAndPlayItem(String uid) {
- BrowseTree.BrowseNode currItem = mBrowseTree.findFolderByIDLocked(uid);
+ BrowseTree.BrowseNode currItem = mBrowseTree.findBrowseNodeByID(uid);
BrowseTree.BrowseNode currFolder = mBrowseTree.getCurrentBrowsedFolder();
if (DBG) Log.d(TAG, "fetchAttrAndPlayItem mediaId=" + uid + " node=" + currItem);
if (currItem != null) {
- int scope = currFolder.isNowPlaying() ? AvrcpControllerService.BROWSE_SCOPE_NOW_PLAYING
+ int scope = currItem.getParent().isNowPlaying()
+ ? AvrcpControllerService.BROWSE_SCOPE_NOW_PLAYING
: AvrcpControllerService.BROWSE_SCOPE_VFS;
Message msg =
obtainMessage(AvrcpControllerStateMachine.MESSAGE_FETCH_ATTR_AND_PLAY_ITEM,
@@ -1181,14 +811,18 @@
Log.d(TAG, " broadcastMetaDataChanged = " + metadata.getDescription());
}
mContext.sendBroadcast(intent, ProfileService.BLUETOOTH_PERM);
+
}
- private void broadcastFolderList(String id, ArrayList<MediaItem> items) {
+ private void broadcastFolderList(String id) {
Intent intent = new Intent(AvrcpControllerService.ACTION_FOLDER_LIST);
- if (VDBG) Log.d(TAG, "broadcastFolderList id " + id + " items " + items);
+ if (VDBG) Log.d(TAG, "broadcastFolderList id " + id);
intent.putExtra(AvrcpControllerService.EXTRA_FOLDER_ID, id);
- intent.putParcelableArrayListExtra(AvrcpControllerService.EXTRA_FOLDER_LIST, items);
- mContext.sendBroadcast(intent, ProfileService.BLUETOOTH_PERM);
+ BluetoothMediaBrowserService bluetoothMediaBrowserService =
+ BluetoothMediaBrowserService.getBluetoothMediaBrowserService();
+ if (bluetoothMediaBrowserService != null) {
+ bluetoothMediaBrowserService.processInternalEvent(intent);
+ }
}
private void broadcastPlayBackStateChanged(PlaybackState state) {
@@ -1285,33 +919,4 @@
}
return str;
}
-
- public static String displayBluetoothAvrcpSettings(BluetoothAvrcpPlayerSettings mSett) {
- StringBuffer sb = new StringBuffer();
- int supportedSetting = mSett.getSettings();
- if (VDBG) {
- Log.d(TAG, " setting: " + supportedSetting);
- }
- if ((supportedSetting & BluetoothAvrcpPlayerSettings.SETTING_EQUALIZER) != 0) {
- sb.append(" EQ : ");
- sb.append(Integer.toString(mSett.getSettingValue(
- BluetoothAvrcpPlayerSettings.SETTING_EQUALIZER)));
- }
- if ((supportedSetting & BluetoothAvrcpPlayerSettings.SETTING_REPEAT) != 0) {
- sb.append(" REPEAT : ");
- sb.append(Integer.toString(mSett.getSettingValue(
- BluetoothAvrcpPlayerSettings.SETTING_REPEAT)));
- }
- if ((supportedSetting & BluetoothAvrcpPlayerSettings.SETTING_SHUFFLE) != 0) {
- sb.append(" SHUFFLE : ");
- sb.append(Integer.toString(mSett.getSettingValue(
- BluetoothAvrcpPlayerSettings.SETTING_SHUFFLE)));
- }
- if ((supportedSetting & BluetoothAvrcpPlayerSettings.SETTING_SCAN) != 0) {
- sb.append(" SCAN : ");
- sb.append(Integer.toString(mSett.getSettingValue(
- BluetoothAvrcpPlayerSettings.SETTING_SCAN)));
- }
- return sb.toString();
- }
}
diff --git a/src/com/android/bluetooth/avrcpcontroller/AvrcpPlayer.java b/src/com/android/bluetooth/avrcpcontroller/AvrcpPlayer.java
index 1f54488..6dbe6b7 100644
--- a/src/com/android/bluetooth/avrcpcontroller/AvrcpPlayer.java
+++ b/src/com/android/bluetooth/avrcpcontroller/AvrcpPlayer.java
@@ -26,7 +26,8 @@
*/
class AvrcpPlayer {
private static final String TAG = "AvrcpPlayer";
- private static final boolean DBG = true;
+ private static final boolean DBG = false;
+ private static final boolean VDBG = false;
public static final int INVALID_ID = -1;
@@ -120,8 +121,15 @@
.setActions(mAvailableActions).build();
}
- public synchronized void updateCurrentTrack(TrackInfo update) {
+ public synchronized boolean updateCurrentTrack(TrackInfo update) {
+ if (update != null && mCurrentTrack != null
+ && update.toString().equals(mCurrentTrack.toString())) {
+ if (DBG) Log.d(TAG, "Update same as original");
+ return false;
+ }
+ if (VDBG) Log.d(TAG, "Track Changed Was:" + mCurrentTrack + "now " + update);
mCurrentTrack = update;
+ return true;
}
public synchronized TrackInfo getCurrentTrack() {
diff --git a/src/com/android/bluetooth/a2dpsink/mbs/A2dpMediaBrowserService.java b/src/com/android/bluetooth/avrcpcontroller/BluetoothMediaBrowserService.java
similarity index 88%
rename from src/com/android/bluetooth/a2dpsink/mbs/A2dpMediaBrowserService.java
rename to src/com/android/bluetooth/avrcpcontroller/BluetoothMediaBrowserService.java
index 6d5f7d2..7860803 100644
--- a/src/com/android/bluetooth/a2dpsink/mbs/A2dpMediaBrowserService.java
+++ b/src/com/android/bluetooth/avrcpcontroller/BluetoothMediaBrowserService.java
@@ -14,7 +14,7 @@
* limitations under the License.
*/
-package com.android.bluetooth.a2dpsink.mbs;
+package com.android.bluetooth.avrcpcontroller;
import android.bluetooth.BluetoothAvrcpController;
import android.bluetooth.BluetoothDevice;
@@ -33,18 +33,14 @@
import android.os.Handler;
import android.os.Looper;
import android.os.Message;
-import android.os.Parcelable;
import android.service.media.MediaBrowserService;
import android.util.Log;
import android.util.Pair;
import com.android.bluetooth.R;
import com.android.bluetooth.a2dpsink.A2dpSinkService;
-import com.android.bluetooth.avrcpcontroller.AvrcpControllerService;
-import com.android.bluetooth.avrcpcontroller.BrowseTree;
import java.lang.ref.WeakReference;
-import java.util.ArrayList;
import java.util.Collections;
import java.util.HashMap;
import java.util.List;
@@ -64,8 +60,8 @@
* 2. The session is de-activated when the device disconnects. It will be connected again when (1)
* happens.
*/
-public class A2dpMediaBrowserService extends MediaBrowserService {
- private static final String TAG = "A2dpMediaBrowserService";
+public class BluetoothMediaBrowserService extends MediaBrowserService {
+ private static final String TAG = "BluetoothMediaBrowserService";
private static final boolean DBG = false;
private static final boolean VDBG = false;
@@ -91,12 +87,13 @@
// Custom actions for PTS testing.
private static final String CUSTOM_ACTION_VOL_UP =
- "com.android.bluetooth.a2dpsink.mbs.CUSTOM_ACTION_VOL_UP";
+ "com.android.bluetooth.avrcpcontroller.CUSTOM_ACTION_VOL_UP";
private static final String CUSTOM_ACTION_VOL_DN =
- "com.android.bluetooth.a2dpsink.mbs.CUSTOM_ACTION_VOL_DN";
+ "com.android.bluetooth.avrcpcontroller.CUSTOM_ACTION_VOL_DN";
private static final String CUSTOM_ACTION_GET_PLAY_STATUS_NATIVE =
- "com.android.bluetooth.a2dpsink.mbs.CUSTOM_ACTION_GET_PLAY_STATUS_NATIVE";
+ "com.android.bluetooth.avrcpcontroller.CUSTOM_ACTION_GET_PLAY_STATUS_NATIVE";
+ private static BluetoothMediaBrowserService sBluetoothMediaBrowserService;
private MediaSession mSession;
private MediaMetadata mA2dpMetadata;
@@ -112,16 +109,16 @@
private List<MediaItem> mNowPlayingList = null;
private static final class AvrcpCommandQueueHandler extends Handler {
- WeakReference<A2dpMediaBrowserService> mInst;
+ WeakReference<BluetoothMediaBrowserService> mInst;
- AvrcpCommandQueueHandler(Looper looper, A2dpMediaBrowserService sink) {
+ AvrcpCommandQueueHandler(Looper looper, BluetoothMediaBrowserService sink) {
super(looper);
- mInst = new WeakReference<A2dpMediaBrowserService>(sink);
+ mInst = new WeakReference<BluetoothMediaBrowserService>(sink);
}
@Override
public void handleMessage(Message msg) {
- A2dpMediaBrowserService inst = mInst.get();
+ BluetoothMediaBrowserService inst = mInst.get();
if (inst == null) {
Log.e(TAG, "Parent class has died; aborting.");
return;
@@ -185,18 +182,45 @@
synchronized (this) {
mParentIdToRequestMap.clear();
}
+ setBluetoothMediaBrowserService(this);
+
}
@Override
public void onDestroy() {
if (DBG) Log.d(TAG, "onDestroy");
+ setBluetoothMediaBrowserService(null);
mSession.release();
unregisterReceiver(mBtReceiver);
super.onDestroy();
}
+
+ /**
+ * getBluetoothMediaBrowserService()
+ * Routine to get direct access to MediaBrowserService from within the same process.
+ */
+ public static synchronized BluetoothMediaBrowserService getBluetoothMediaBrowserService() {
+ if (sBluetoothMediaBrowserService == null) {
+ Log.w(TAG, "getBluetoothMediaBrowserService(): service is NULL");
+ return null;
+ }
+ if (DBG) {
+ Log.d(TAG, "getBluetoothMediaBrowserService(): returning "
+ + sBluetoothMediaBrowserService);
+ }
+ return sBluetoothMediaBrowserService;
+ }
+
+ private static synchronized void setBluetoothMediaBrowserService(
+ BluetoothMediaBrowserService instance) {
+ if (DBG) Log.d(TAG, "setBluetoothMediaBrowserService(): set to: " + instance);
+ sBluetoothMediaBrowserService = instance;
+ }
+
@Override
public BrowserRoot onGetRoot(String clientPackageName, int clientUid, Bundle rootHints) {
+ if (DBG) Log.d(TAG, "onGetRoot");
return new BrowserRoot(BrowseTree.ROOT, null);
}
@@ -210,17 +234,15 @@
}
if (DBG) Log.d(TAG, "onLoadChildren parentMediaId=" + parentMediaId);
- if (!mAvrcpCtrlSrvc.getChildren(mA2dpDevice, parentMediaId, 0, 0xff)) {
- result.sendResult(Collections.emptyList());
- return;
+ List<MediaItem> contents = mAvrcpCtrlSrvc.getContents(mA2dpDevice, parentMediaId);
+ if (contents == null) {
+ mParentIdToRequestMap.put(parentMediaId, result);
+ result.detach();
+ } else {
+ result.sendResult(contents);
}
- // Since we are using this thread from a binder thread we should make sure that
- // we synchronize against other such asynchronous calls.
- synchronized (this) {
- mParentIdToRequestMap.put(parentMediaId, result);
- }
- result.detach();
+ return;
}
@Override
@@ -294,7 +316,7 @@
@Override
public void onPlayFromMediaId(String mediaId, Bundle extras) {
- synchronized (A2dpMediaBrowserService.this) {
+ synchronized (BluetoothMediaBrowserService.this) {
// Play the item if possible.
mAvrcpCtrlSrvc.fetchAttrAndPlayItem(mA2dpDevice, mediaId);
@@ -501,7 +523,7 @@
mCurrentlyHeldKey = cmd;
} else {
mAvrcpCtrlSrvc.sendPassThroughCmd(mA2dpDevice, cmd,
- AvrcpControllerService.KEY_STATE_RELEASED);
+ AvrcpControllerService.KEY_STATE_RELEASED);
}
}
@@ -532,16 +554,8 @@
private void msgFolderList(Intent intent) {
// Parse the folder list for children list and id.
- List<Parcelable> extraParcelableList =
- (ArrayList<Parcelable>) intent.getParcelableArrayListExtra(
- AvrcpControllerService.EXTRA_FOLDER_LIST);
- List<MediaItem> folderList = new ArrayList<MediaItem>();
- for (Parcelable p : extraParcelableList) {
- folderList.add((MediaItem) p);
- }
-
String id = intent.getStringExtra(AvrcpControllerService.EXTRA_FOLDER_ID);
- if (VDBG) Log.d(TAG, "Parent: " + id + " Folder list: " + folderList);
+ if (VDBG) Log.d(TAG, "Parent: " + id);
synchronized (this) {
// If we have a result object then we should send the result back
// to client since it is blocking otherwise we may have gotten more items
@@ -551,11 +565,23 @@
Log.w(TAG, "Request no longer exists, notifying that children changed.");
notifyChildrenChanged(id);
} else {
+ List<MediaItem> folderList = mAvrcpCtrlSrvc.getContents(mA2dpDevice, id);
results.sendResult(folderList);
}
}
}
+ /**
+ * processInternalEvent(Intent intent)
+ * Routine to provide MediaBrowserService with content updates from within the same process.
+ */
+ public void processInternalEvent(Intent intent) {
+ String action = intent.getAction();
+ if (AvrcpControllerService.ACTION_FOLDER_LIST.equals(action)) {
+ mAvrcpCommandQueue.obtainMessage(MSG_FOLDER_LIST, intent).sendToTarget();
+ }
+ }
+
private void msgDeviceBrowseDisconnect(BluetoothDevice device) {
if (DBG) Log.d(TAG, "msgDeviceBrowseDisconnect device " + device);
// Disconnect only if mA2dpDevice is non null
@@ -566,4 +592,5 @@
}
mBrowseConnected = false;
}
+
}
diff --git a/src/com/android/bluetooth/avrcpcontroller/BrowseTree.java b/src/com/android/bluetooth/avrcpcontroller/BrowseTree.java
index fed0d5d..17a192a 100644
--- a/src/com/android/bluetooth/avrcpcontroller/BrowseTree.java
+++ b/src/com/android/bluetooth/avrcpcontroller/BrowseTree.java
@@ -20,7 +20,6 @@
import android.media.browse.MediaBrowser;
import android.media.browse.MediaBrowser.MediaItem;
import android.os.Bundle;
-import android.service.media.MediaBrowserService.Result;
import android.util.Log;
import java.util.ArrayList;
@@ -43,12 +42,8 @@
private static final boolean DBG = false;
private static final boolean VDBG = false;
- public static final int DIRECTION_DOWN = 0;
- public static final int DIRECTION_UP = 1;
- public static final int DIRECTION_SAME = 2;
- public static final int DIRECTION_UNKNOWN = -1;
-
public static final String ROOT = "__ROOT__";
+ public static final String UP = "__UP__";
public static final String NOW_PLAYING_PREFIX = "NOW_PLAYING";
public static final String PLAYER_PREFIX = "PLAYER";
@@ -57,19 +52,36 @@
private BrowseNode mCurrentBrowseNode;
private BrowseNode mCurrentBrowsedPlayer;
private BrowseNode mCurrentAddressedPlayer;
+ private int mDepth = 0;
+ final BrowseNode mRootNode;
+ final BrowseNode mNavigateUpNode;
+ final BrowseNode mNowPlayingNode;
BrowseTree() {
- }
-
- public void init() {
- MediaDescription.Builder mdb = new MediaDescription.Builder();
- mdb.setMediaId(ROOT);
- mdb.setTitle(ROOT);
Bundle mdBundle = new Bundle();
mdBundle.putString(AvrcpControllerService.MEDIA_ITEM_UID_KEY, ROOT);
- mdb.setExtras(mdBundle);
- mBrowseMap.put(ROOT, new BrowseNode(new MediaItem(mdb.build(), MediaItem.FLAG_BROWSABLE)));
- mCurrentBrowseNode = mBrowseMap.get(ROOT);
+ mRootNode = new BrowseNode(new MediaItem(new MediaDescription.Builder().setExtras(mdBundle)
+ .setMediaId(ROOT).setTitle(ROOT).build(), MediaItem.FLAG_BROWSABLE));
+ mRootNode.mBrowseScope = AvrcpControllerService.BROWSE_SCOPE_PLAYER_LIST;
+
+ Bundle upnodeBundle = new Bundle();
+ upnodeBundle.putString(AvrcpControllerService.MEDIA_ITEM_UID_KEY, UP);
+ mNavigateUpNode = new BrowseNode(new MediaItem(new MediaDescription.Builder()
+ .setExtras(upnodeBundle).setMediaId(UP).setTitle(UP).build(),
+ MediaItem.FLAG_BROWSABLE));
+
+ Bundle nowPlayingBundle = new Bundle();
+ nowPlayingBundle.putString(AvrcpControllerService.MEDIA_ITEM_UID_KEY, NOW_PLAYING_PREFIX);
+ mNowPlayingNode = new BrowseNode(new MediaItem(new MediaDescription.Builder()
+ .setExtras(nowPlayingBundle).setMediaId(NOW_PLAYING_PREFIX)
+ .setTitle(NOW_PLAYING_PREFIX).build(), MediaItem.FLAG_BROWSABLE));
+ mNowPlayingNode.mBrowseScope = AvrcpControllerService.BROWSE_SCOPE_NOW_PLAYING;
+ mNowPlayingNode.setExpectedChildren(255);
+ mBrowseMap.put(ROOT, mRootNode);
+ mRootNode.mChildren.add(mNowPlayingNode);
+ mBrowseMap.put(NOW_PLAYING_PREFIX, mNowPlayingNode);
+
+ mCurrentBrowseNode = mRootNode;
}
public void clear() {
@@ -91,12 +103,12 @@
// without doing another fetch.
boolean mCached = false;
- // Result object if this node is not loaded yet. This result object will be used
- // once loading is finished.
- Result<List<MediaItem>> mResult = null;
+ int mBrowseScope = AvrcpControllerService.BROWSE_SCOPE_VFS;
// List of children.
- final List<BrowseNode> mChildren = new ArrayList<BrowseNode>();
+ private BrowseNode mParent;
+ private final List<BrowseNode> mChildren = new ArrayList<BrowseNode>();
+ private int mExpectedChildrenCount;
BrowseNode(MediaItem item) {
mItem = item;
@@ -118,25 +130,90 @@
mItem = new MediaBrowser.MediaItem(mdb.build(), mediaItemFlags);
}
+ private BrowseNode(String name) {
+ MediaDescription.Builder mdb = new MediaDescription.Builder();
+ Bundle mdExtra = new Bundle();
+ mdExtra.putString(AvrcpControllerService.MEDIA_ITEM_UID_KEY, name);
+ mdb.setExtras(mdExtra);
+ mdb.setMediaId(name);
+ mdb.setTitle(name);
+ mItem = new MediaBrowser.MediaItem(mdb.build(), MediaBrowser.MediaItem.FLAG_BROWSABLE);
+ }
+
+ synchronized void setExpectedChildren(int count) {
+ mExpectedChildrenCount = count;
+ }
+
+ synchronized int getExpectedChildren() {
+ return mExpectedChildrenCount;
+ }
+
+ synchronized <E> int addChildren(List<E> newChildren) {
+ for (E child : newChildren) {
+ BrowseNode currentNode = null;
+ if (child instanceof MediaItem) {
+ currentNode = new BrowseNode((MediaItem) child);
+ } else if (child instanceof AvrcpPlayer) {
+ currentNode = new BrowseNode((AvrcpPlayer) child);
+ }
+ if (currentNode != null) {
+ currentNode.mParent = this;
+ mChildren.add(currentNode);
+ mBrowseMap.put(currentNode.getID(), currentNode);
+ }
+ }
+ return newChildren.size();
+ }
+
+ synchronized int getChildrenCount() {
+ return mChildren.size();
+ }
+
synchronized List<BrowseNode> getChildren() {
return mChildren;
}
- synchronized boolean isChild(BrowseNode node) {
- for (BrowseNode bn : mChildren) {
- if (bn.equals(node)) {
- return true;
+ synchronized BrowseNode getParent() {
+ return mParent;
+ }
+
+ synchronized List<MediaItem> getContents() {
+ if (mChildren != null) {
+ List<MediaItem> contents = new ArrayList<MediaItem>(mChildren.size());
+ for (BrowseNode child : mChildren) {
+ contents.add(child.getMediaItem());
}
+ return contents;
}
- return false;
+ return null;
+ }
+
+ synchronized boolean isChild(BrowseNode node) {
+ return mChildren.contains(node);
}
synchronized boolean isCached() {
return mCached;
}
+ synchronized boolean isBrowsable() {
+ return mItem.isBrowsable();
+ }
+
synchronized void setCached(boolean cached) {
+ if (DBG) Log.d(TAG, "Set Cache" + cached + "Node" + toString());
mCached = cached;
+ if (!cached) {
+ for (BrowseNode child : mChildren) {
+ mBrowseMap.remove(child.getID());
+ }
+ mChildren.clear();
+ if (this == mRootNode) {
+ mNowPlayingNode.setCached(false);
+ mRootNode.mChildren.add(mNowPlayingNode);
+ mBrowseMap.put(NOW_PLAYING_PREFIX, mNowPlayingNode);
+ }
+ }
}
// Fetch the Unique UID for this item, this is unique across all elements in the tree.
@@ -149,6 +226,10 @@
return Integer.parseInt(getID().replace(PLAYER_PREFIX, ""));
}
+ synchronized int getScope() {
+ return mBrowseScope;
+ }
+
// Fetch the Folder UID that can be used to fetch folder listing via bluetooth.
// This may not be unique hence this combined with direction will define the
// browsing here.
@@ -182,50 +263,16 @@
@Override
public String toString() {
if (VDBG) {
- return "ID: " + getID() + " desc: " + mItem;
+ return "[ Name: " + mItem.getDescription().getTitle() + " expected Children: "
+ + mExpectedChildrenCount + "] ";
} else {
return "ID: " + getID();
}
}
- }
-
- synchronized <E> void refreshChildren(String parentID, List<E> children) {
- BrowseNode parent = findFolderByIDLocked(parentID);
- if (parent == null) {
- Log.w(TAG, "parent not found for parentID " + parentID);
- return;
+ // Returns true if target is a descendant of this.
+ synchronized boolean isDescendant(BrowseNode target) {
+ return getEldestChild(this, target) == null ? false : true;
}
- refreshChildren(parent, children);
- }
-
- synchronized <E> void refreshChildren(BrowseNode parent, List<E> children) {
- if (children == null) {
- Log.e(TAG, "children cannot be null ");
- return;
- }
-
- List<BrowseNode> bnList = new ArrayList<BrowseNode>();
- for (E child : children) {
- if (child instanceof MediaItem) {
- bnList.add(new BrowseNode((MediaItem) child));
- } else if (child instanceof AvrcpPlayer) {
- bnList.add(new BrowseNode((AvrcpPlayer) child));
- }
- }
-
- String parentID = parent.getID();
- // Make sure that the child list is clean.
- if (VDBG) {
- Log.d(TAG, "parent " + parentID + " child list " + parent.getChildren());
- }
-
- addChildrenLocked(parent, bnList);
- List<MediaItem> childrenList = new ArrayList<MediaItem>();
- for (BrowseNode bn : parent.getChildren()) {
- childrenList.add(bn.getMediaItem());
- }
-
- parent.setCached(true);
}
synchronized BrowseNode findBrowseNodeByID(String parentID) {
@@ -235,50 +282,13 @@
return null;
}
if (VDBG) {
- Log.d(TAG, "Browse map: " + mBrowseMap);
+ Log.d(TAG, "Size" + mBrowseMap.size());
}
return bn;
}
- BrowseNode findFolderByIDLocked(String parentID) {
- return mBrowseMap.get(parentID);
- }
-
- void addChildrenLocked(BrowseNode parent, List<BrowseNode> items) {
- // Remove existing children and then add the new children.
- for (BrowseNode c : parent.getChildren()) {
- mBrowseMap.remove(c.getID());
- }
- parent.getChildren().clear();
-
- for (BrowseNode bn : items) {
- parent.getChildren().add(bn);
- mBrowseMap.put(bn.getID(), bn);
- }
- }
-
- synchronized int getDirection(String toUID) {
- BrowseNode fromFolder = mCurrentBrowseNode;
- BrowseNode toFolder = findFolderByIDLocked(toUID);
- if (fromFolder == null || toFolder == null) {
- Log.e(TAG, "from folder " + mCurrentBrowseNode + " or to folder " + toUID + " null!");
- }
-
- // Check the relationship.
- if (fromFolder.isChild(toFolder)) {
- return DIRECTION_DOWN;
- } else if (toFolder.isChild(fromFolder)) {
- return DIRECTION_UP;
- } else if (fromFolder.equals(toFolder)) {
- return DIRECTION_SAME;
- } else {
- Log.w(TAG, "from folder " + mCurrentBrowseNode + "to folder " + toUID);
- return DIRECTION_UNKNOWN;
- }
- }
-
synchronized boolean setCurrentBrowsedFolder(String uid) {
- BrowseNode bn = findFolderByIDLocked(uid);
+ BrowseNode bn = mBrowseMap.get(uid);
if (bn == null) {
Log.e(TAG, "Setting an unknown browsed folder, ignoring bn " + uid);
return false;
@@ -286,10 +296,8 @@
// Set the previous folder as not cached so that we fetch the contents again.
if (!bn.equals(mCurrentBrowseNode)) {
- Log.d(TAG, "Set cache false " + bn + " curr " + mCurrentBrowseNode);
- mCurrentBrowseNode.setCached(false);
+ Log.d(TAG, "Set cache " + bn + " curr " + mCurrentBrowseNode);
}
-
mCurrentBrowseNode = bn;
return true;
}
@@ -298,13 +306,21 @@
return mCurrentBrowseNode;
}
- synchronized boolean setCurrentBrowsedPlayer(String uid) {
- BrowseNode bn = findFolderByIDLocked(uid);
+ synchronized boolean setCurrentBrowsedPlayer(String uid, int items, int depth) {
+ BrowseNode bn = mBrowseMap.get(uid);
if (bn == null) {
Log.e(TAG, "Setting an unknown browsed player, ignoring bn " + uid);
return false;
}
mCurrentBrowsedPlayer = bn;
+ mCurrentBrowseNode = mCurrentBrowsedPlayer;
+ for (Integer level = 0; level < depth; level++) {
+ BrowseNode dummyNode = new BrowseNode(level.toString());
+ dummyNode.mParent = mCurrentBrowseNode;
+ mCurrentBrowseNode = dummyNode;
+ }
+ mCurrentBrowseNode.setExpectedChildren(items);
+ mDepth = depth;
return true;
}
@@ -313,9 +329,12 @@
}
synchronized boolean setCurrentAddressedPlayer(String uid) {
- BrowseNode bn = findFolderByIDLocked(uid);
+ BrowseNode bn = mBrowseMap.get(uid);
if (bn == null) {
- Log.e(TAG, "Setting an unknown addressed player, ignoring bn " + uid);
+ if (DBG) Log.d(TAG, "Setting an unknown addressed player, ignoring bn " + uid);
+ mRootNode.setCached(false);
+ mRootNode.mChildren.add(mNowPlayingNode);
+ mBrowseMap.put(NOW_PLAYING_PREFIX, mNowPlayingNode);
return false;
}
mCurrentAddressedPlayer = bn;
@@ -328,6 +347,60 @@
@Override
public String toString() {
- return mBrowseMap.toString();
+ return "Size: " + mBrowseMap.size();
+ }
+
+ // Calculates the path to target node.
+ // Returns: UP node to go up
+ // Returns: target node if there
+ // Returns: named node to go down
+ // Returns: null node if unknown
+ BrowseNode getNextStepToFolder(BrowseNode target) {
+ if (target == null) {
+ return null;
+ } else if (target.equals(mCurrentBrowseNode)
+ || target.equals(mNowPlayingNode)) {
+ return target;
+ } else if (target.isPlayer()) {
+ if (mDepth > 0) {
+ mDepth--;
+ return mNavigateUpNode;
+ } else {
+ return target;
+ }
+ } else if (mBrowseMap.get(target.getID()) == null) {
+ return null;
+ } else {
+ BrowseNode nextChild = getEldestChild(mCurrentBrowseNode, target);
+ if (nextChild == null) {
+ return mNavigateUpNode;
+ } else {
+ return nextChild;
+ }
+ }
+ /*
+ if (mCurrentBrowseNode.isDescendant(target)) {
+ return getEldestChild(mCurrentBrowseNode, target);
+ } else {
+ if (DBG) Log.d(TAG, "NAVIGATING UP");
+ return mNavigateUpNode;
+ }
+ */
+ }
+
+ BrowseNode getEldestChild(BrowseNode ancestor, BrowseNode target) {
+ // ancestor is an ancestor of target
+ BrowseNode descendant = target;
+ if (DBG) {
+ Log.d(TAG, "NAVIGATING ancestor" + ancestor.toString() + "Target" + target.toString());
+ }
+ while (!ancestor.equals(descendant.mParent)) {
+ descendant = descendant.mParent;
+ if (descendant == null) {
+ return null;
+ }
+ }
+ if (DBG) Log.d(TAG, "NAVIGATING Descendant" + descendant.toString());
+ return descendant;
}
}