Merge "Clean up mediabrowser callbacks" into rvc-dev

This commit is contained in:
Beth Thibodeau
2020-05-01 16:39:27 +00:00
committed by Android (Google) Code Review
3 changed files with 142 additions and 67 deletions

View File

@@ -98,6 +98,7 @@ public class MediaControlPanel {
private SharedPreferences mSharedPrefs; private SharedPreferences mSharedPrefs;
private boolean mCheckedForResumption = false; private boolean mCheckedForResumption = false;
private boolean mIsRemotePlayback; private boolean mIsRemotePlayback;
private QSMediaBrowser mQSMediaBrowser;
// Button IDs used in notifications // Button IDs used in notifications
protected static final int[] NOTIF_ACTION_IDS = { protected static final int[] NOTIF_ACTION_IDS = {
@@ -232,6 +233,11 @@ public class MediaControlPanel {
String key) { String key) {
// Ensure that component names are updated if token has changed // Ensure that component names are updated if token has changed
if (mToken == null || !mToken.equals(token)) { if (mToken == null || !mToken.equals(token)) {
if (mQSMediaBrowser != null) {
Log.d(TAG, "Disconnecting old media browser");
mQSMediaBrowser.disconnect();
mQSMediaBrowser = null;
}
mToken = token; mToken = token;
mServiceComponent = null; mServiceComponent = null;
mCheckedForResumption = false; mCheckedForResumption = false;
@@ -618,8 +624,22 @@ public class MediaControlPanel {
ImageButton btn = mMediaNotifView.findViewById(mActionIds[0]); ImageButton btn = mMediaNotifView.findViewById(mActionIds[0]);
btn.setOnClickListener(v -> { btn.setOnClickListener(v -> {
Log.d(TAG, "Attempting to restart session"); Log.d(TAG, "Attempting to restart session");
QSMediaBrowser browser = new QSMediaBrowser(mContext, null, mServiceComponent); if (mQSMediaBrowser != null) {
browser.restart(); mQSMediaBrowser.disconnect();
}
mQSMediaBrowser = new QSMediaBrowser(mContext, new QSMediaBrowser.Callback(){
@Override
public void onConnected() {
Log.d(TAG, "Successfully restarted");
}
@Override
public void onError() {
Log.e(TAG, "Error restarting");
mQSMediaBrowser.disconnect();
mQSMediaBrowser = null;
}
}, mServiceComponent);
mQSMediaBrowser.restart();
}); });
btn.setImageDrawable(mContext.getResources().getDrawable(R.drawable.lb_ic_play)); btn.setImageDrawable(mContext.getResources().getDrawable(R.drawable.lb_ic_play));
btn.setImageTintList(ColorStateList.valueOf(mForegroundColor)); btn.setImageTintList(ColorStateList.valueOf(mForegroundColor));
@@ -654,13 +674,18 @@ public class MediaControlPanel {
*/ */
private void tryUpdateResumptionList(ComponentName componentName) { private void tryUpdateResumptionList(ComponentName componentName) {
Log.d(TAG, "Testing if we can connect to " + componentName); Log.d(TAG, "Testing if we can connect to " + componentName);
QSMediaBrowser.testConnection(mContext, if (mQSMediaBrowser != null) {
mQSMediaBrowser.disconnect();
}
mQSMediaBrowser = new QSMediaBrowser(mContext,
new QSMediaBrowser.Callback() { new QSMediaBrowser.Callback() {
@Override @Override
public void onConnected() { public void onConnected() {
Log.d(TAG, "yes we can resume with " + componentName); Log.d(TAG, "yes we can resume with " + componentName);
mServiceComponent = componentName; mServiceComponent = componentName;
updateResumptionList(componentName); updateResumptionList(componentName);
mQSMediaBrowser.disconnect();
mQSMediaBrowser = null;
} }
@Override @Override
@@ -671,9 +696,12 @@ public class MediaControlPanel {
// If it's not active and we can't resume, remove // If it's not active and we can't resume, remove
removePlayer(); removePlayer();
} }
mQSMediaBrowser.disconnect();
mQSMediaBrowser = null;
} }
}, },
componentName); componentName);
mQSMediaBrowser.testConnection();
} }
/** /**

View File

@@ -58,21 +58,25 @@ public class QSMediaBrowser {
mContext = context; mContext = context;
mCallback = callback; mCallback = callback;
mComponentName = componentName; mComponentName = componentName;
}
/**
* Connects to the MediaBrowserService and looks for valid media. If a media item is returned
* by the service, QSMediaBrowser.Callback#addTrack will be called with its MediaDescription.
* QSMediaBrowser.Callback#onConnected and QSMediaBrowser.Callback#onError will also be called
* when the initial connection is successful, or an error occurs. Note that it is possible for
* the service to connect but for no playable tracks to be found later.
* QSMediaBrowser#disconnect will be called automatically with this function.
*/
public void findRecentMedia() {
Log.d(TAG, "Connecting to " + mComponentName);
disconnect();
Bundle rootHints = new Bundle(); Bundle rootHints = new Bundle();
rootHints.putBoolean(MediaBrowserService.BrowserRoot.EXTRA_RECENT, true); rootHints.putBoolean(MediaBrowserService.BrowserRoot.EXTRA_RECENT, true);
mMediaBrowser = new MediaBrowser(mContext, mMediaBrowser = new MediaBrowser(mContext,
mComponentName, mComponentName,
mConnectionCallback, mConnectionCallback,
rootHints); rootHints);
}
/**
* Connects to the MediaBrowserService and looks for valid media. If a media item is returned
* by the service, QSMediaBrowser.Callback#addTrack will be called with its MediaDescription
*/
public void findRecentMedia() {
Log.d(TAG, "Connecting to " + mComponentName);
mMediaBrowser.connect(); mMediaBrowser.connect();
} }
@@ -94,20 +98,22 @@ public class QSMediaBrowser {
} else { } else {
Log.e(TAG, "Child found but not playable for " + mComponentName); Log.e(TAG, "Child found but not playable for " + mComponentName);
} }
mMediaBrowser.disconnect(); disconnect();
} }
@Override @Override
public void onError(String parentId) { public void onError(String parentId) {
Log.e(TAG, "Subscribe error for " + mComponentName + ": " + parentId); Log.e(TAG, "Subscribe error for " + mComponentName + ": " + parentId);
mMediaBrowser.disconnect(); mCallback.onError();
disconnect();
} }
@Override @Override
public void onError(String parentId, Bundle options) { public void onError(String parentId, Bundle options) {
Log.e(TAG, "Subscribe error for " + mComponentName + ": " + parentId Log.e(TAG, "Subscribe error for " + mComponentName + ": " + parentId
+ ", options: " + options); + ", options: " + options);
mMediaBrowser.disconnect(); mCallback.onError();
disconnect();
} }
}; };
@@ -134,6 +140,8 @@ public class QSMediaBrowser {
@Override @Override
public void onConnectionSuspended() { public void onConnectionSuspended() {
Log.d(TAG, "Connection suspended for " + mComponentName); Log.d(TAG, "Connection suspended for " + mComponentName);
mCallback.onError();
disconnect();
} }
/** /**
@@ -143,16 +151,28 @@ public class QSMediaBrowser {
public void onConnectionFailed() { public void onConnectionFailed() {
Log.e(TAG, "Connection failed for " + mComponentName); Log.e(TAG, "Connection failed for " + mComponentName);
mCallback.onError(); mCallback.onError();
disconnect();
} }
}; };
/** /**
* Connects to the MediaBrowserService and starts playback * Disconnect the media browser. This should be called after restart or testConnection have
* completed to close the connection.
*/ */
public void restart() { public void disconnect() {
if (mMediaBrowser.isConnected()) { if (mMediaBrowser != null) {
mMediaBrowser.disconnect(); mMediaBrowser.disconnect();
} }
mMediaBrowser = null;
}
/**
* Connects to the MediaBrowserService and starts playback. QSMediaBrowser.Callback#onError or
* QSMediaBrowser.Callback#onConnected will be called depending on whether it was successful.
* QSMediaBrowser#disconnect should be called after this to ensure the connection is closed.
*/
public void restart() {
disconnect();
Bundle rootHints = new Bundle(); Bundle rootHints = new Bundle();
rootHints.putBoolean(MediaBrowserService.BrowserRoot.EXTRA_RECENT, true); rootHints.putBoolean(MediaBrowserService.BrowserRoot.EXTRA_RECENT, true);
mMediaBrowser = new MediaBrowser(mContext, mComponentName, mMediaBrowser = new MediaBrowser(mContext, mComponentName,
@@ -165,6 +185,17 @@ public class QSMediaBrowser {
controller.getTransportControls(); controller.getTransportControls();
controller.getTransportControls().prepare(); controller.getTransportControls().prepare();
controller.getTransportControls().play(); controller.getTransportControls().play();
mCallback.onConnected();
}
@Override
public void onConnectionFailed() {
mCallback.onError();
}
@Override
public void onConnectionSuspended() {
mCallback.onError();
} }
}, rootHints); }, rootHints);
mMediaBrowser.connect(); mMediaBrowser.connect();
@@ -192,42 +223,44 @@ public class QSMediaBrowser {
} }
/** /**
* Used to test if SystemUI is allowed to connect to the given component as a MediaBrowser * Used to test if SystemUI is allowed to connect to the given component as a MediaBrowser.
* @param mContext the context * QSMediaBrowser.Callback#onError or QSMediaBrowser.Callback#onConnected will be called
* @param callback methods onConnected or onError will be called to indicate whether the * depending on whether it was successful.
* connection was successful or not * QSMediaBrowser#disconnect should be called after this to ensure the connection is closed.
* @param mComponentName Component name of the MediaBrowserService this browser will connect to
*/ */
public static MediaBrowser testConnection(Context mContext, Callback callback, public void testConnection() {
ComponentName mComponentName) { disconnect();
final MediaBrowser.ConnectionCallback mConnectionCallback = final MediaBrowser.ConnectionCallback connectionCallback =
new MediaBrowser.ConnectionCallback() { new MediaBrowser.ConnectionCallback() {
@Override @Override
public void onConnected() { public void onConnected() {
Log.d(TAG, "connected"); Log.d(TAG, "connected");
callback.onConnected(); if (mMediaBrowser.getRoot() == null) {
mCallback.onError();
} else {
mCallback.onConnected();
}
} }
@Override @Override
public void onConnectionSuspended() { public void onConnectionSuspended() {
Log.d(TAG, "suspended"); Log.d(TAG, "suspended");
callback.onError(); mCallback.onError();
} }
@Override @Override
public void onConnectionFailed() { public void onConnectionFailed() {
Log.d(TAG, "failed"); Log.d(TAG, "failed");
callback.onError(); mCallback.onError();
} }
}; };
Bundle rootHints = new Bundle(); Bundle rootHints = new Bundle();
rootHints.putBoolean(MediaBrowserService.BrowserRoot.EXTRA_RECENT, true); rootHints.putBoolean(MediaBrowserService.BrowserRoot.EXTRA_RECENT, true);
MediaBrowser browser = new MediaBrowser(mContext, mMediaBrowser = new MediaBrowser(mContext,
mComponentName, mComponentName,
mConnectionCallback, connectionCallback,
rootHints); rootHints);
browser.connect(); mMediaBrowser.connect();
return browser;
} }
/** /**

View File

@@ -270,27 +270,8 @@ public class QSPanel extends LinearLayout implements Tunable, Callback, Brightne
return; return;
} }
QSMediaPlayer player = null;
String packageName = notif.getPackageName(); String packageName = notif.getPackageName();
for (QSMediaPlayer p : mMediaPlayers) { QSMediaPlayer player = findMediaPlayer(packageName, token, key);
if (p.getKey() == null) {
// No notification key = loaded via mediabrowser, so just match on package
if (packageName.equals(p.getMediaPlayerPackage())) {
Log.d(TAG, "Found matching resume player by package: " + packageName);
player = p;
break;
}
} else if (p.getMediaSessionToken().equals(token)) {
Log.d(TAG, "Found matching player by token " + packageName);
player = p;
break;
} else if (packageName.equals(p.getMediaPlayerPackage()) && key.equals(p.getKey())) {
// Also match if it's the same package and notification key
Log.d(TAG, "Found matching player by package " + packageName + ", " + key);
player = p;
break;
}
}
int playerWidth = (int) getResources().getDimension(R.dimen.qs_media_width); int playerWidth = (int) getResources().getDimension(R.dimen.qs_media_width);
int padding = (int) getResources().getDimension(R.dimen.qs_media_padding); int padding = (int) getResources().getDimension(R.dimen.qs_media_padding);
@@ -299,7 +280,7 @@ public class QSPanel extends LinearLayout implements Tunable, Callback, Brightne
lp.setMarginEnd(padding); lp.setMarginEnd(padding);
if (player == null) { if (player == null) {
Log.d(TAG, "creating new player"); Log.d(TAG, "creating new player for " + packageName);
// Set up listener for device changes // Set up listener for device changes
// TODO: integrate with MediaTransferManager? // TODO: integrate with MediaTransferManager?
InfoMediaManager imm = new InfoMediaManager(mContext, notif.getPackageName(), InfoMediaManager imm = new InfoMediaManager(mContext, notif.getPackageName(),
@@ -331,6 +312,35 @@ public class QSPanel extends LinearLayout implements Tunable, Callback, Brightne
} }
} }
/**
* Check for an existing media player using the given information
* @param packageName
* @param token
* @param key
* @return a player, or null if no match found
*/
private QSMediaPlayer findMediaPlayer(String packageName, MediaSession.Token token,
String key) {
for (QSMediaPlayer player : mMediaPlayers) {
if (player.getKey() == null || key == null) {
// No notification key = loaded via mediabrowser, so just match on package
if (packageName.equals(player.getMediaPlayerPackage())) {
Log.d(TAG, "Found matching resume player by package: " + packageName);
return player;
}
} else if (player.getMediaSessionToken().equals(token)) {
Log.d(TAG, "Found matching player by token " + packageName);
return player;
} else if (packageName.equals(player.getMediaPlayerPackage())
&& key.equals(player.getKey())) {
// Also match if it's the same package and notification key
Log.d(TAG, "Found matching player by package " + packageName + ", " + key);
return player;
}
}
return null;
}
protected View getMediaPanel() { protected View getMediaPanel() {
return mMediaCarousel; return mMediaCarousel;
} }
@@ -369,26 +379,30 @@ public class QSPanel extends LinearLayout implements Tunable, Callback, Brightne
} }
Log.d(TAG, "adding track from browser: " + desc + ", " + component); Log.d(TAG, "adding track from browser: " + desc + ", " + component);
QSMediaPlayer player = new QSMediaPlayer(mContext, QSPanel.this,
null, mForegroundExecutor, mBackgroundExecutor, mActivityStarter);
// Check if there's an old player for this app
String pkgName = component.getPackageName(); String pkgName = component.getPackageName();
MediaSession.Token token = browser.getToken();
QSMediaPlayer player = findMediaPlayer(pkgName, token, null);
// Add controls to carousel if (player == null) {
int playerWidth = (int) getResources().getDimension(R.dimen.qs_media_width); player = new QSMediaPlayer(mContext, QSPanel.this,
int padding = (int) getResources().getDimension(R.dimen.qs_media_padding); null, mForegroundExecutor, mBackgroundExecutor, mActivityStarter);
LinearLayout.LayoutParams lp = new LinearLayout.LayoutParams(playerWidth,
LayoutParams.MATCH_PARENT); // Add to carousel
lp.setMarginStart(padding); int playerWidth = (int) getResources().getDimension(R.dimen.qs_media_width);
lp.setMarginEnd(padding); int padding = (int) getResources().getDimension(R.dimen.qs_media_padding);
mMediaCarousel.addView(player.getView(), lp); LinearLayout.LayoutParams lp = new LinearLayout.LayoutParams(playerWidth,
((View) mMediaCarousel.getParent()).setVisibility(View.VISIBLE); LayoutParams.MATCH_PARENT);
mMediaPlayers.add(player); lp.setMarginStart(padding);
lp.setMarginEnd(padding);
mMediaCarousel.addView(player.getView(), lp);
((View) mMediaCarousel.getParent()).setVisibility(View.VISIBLE);
mMediaPlayers.add(player);
}
int iconColor = Color.DKGRAY; int iconColor = Color.DKGRAY;
int bgColor = Color.LTGRAY; int bgColor = Color.LTGRAY;
MediaSession.Token token = browser.getToken();
player.setMediaSession(token, desc, iconColor, bgColor, browser.getAppIntent(), player.setMediaSession(token, desc, iconColor, bgColor, browser.getAppIntent(),
pkgName); pkgName);
} }