Loading packages/SystemUI/src/com/android/systemui/media/MediaControlPanel.java +31 −3 Original line number Diff line number Diff line Loading @@ -98,6 +98,7 @@ public class MediaControlPanel { private SharedPreferences mSharedPrefs; private boolean mCheckedForResumption = false; private boolean mIsRemotePlayback; private QSMediaBrowser mQSMediaBrowser; // Button IDs used in notifications protected static final int[] NOTIF_ACTION_IDS = { Loading Loading @@ -232,6 +233,11 @@ public class MediaControlPanel { String key) { // Ensure that component names are updated if token has changed if (mToken == null || !mToken.equals(token)) { if (mQSMediaBrowser != null) { Log.d(TAG, "Disconnecting old media browser"); mQSMediaBrowser.disconnect(); mQSMediaBrowser = null; } mToken = token; mServiceComponent = null; mCheckedForResumption = false; Loading Loading @@ -618,8 +624,22 @@ public class MediaControlPanel { ImageButton btn = mMediaNotifView.findViewById(mActionIds[0]); btn.setOnClickListener(v -> { Log.d(TAG, "Attempting to restart session"); QSMediaBrowser browser = new QSMediaBrowser(mContext, null, mServiceComponent); browser.restart(); if (mQSMediaBrowser != null) { 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.setImageTintList(ColorStateList.valueOf(mForegroundColor)); Loading Loading @@ -654,13 +674,18 @@ public class MediaControlPanel { */ private void tryUpdateResumptionList(ComponentName 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() { @Override public void onConnected() { Log.d(TAG, "yes we can resume with " + componentName); mServiceComponent = componentName; updateResumptionList(componentName); mQSMediaBrowser.disconnect(); mQSMediaBrowser = null; } @Override Loading @@ -671,9 +696,12 @@ public class MediaControlPanel { // If it's not active and we can't resume, remove removePlayer(); } mQSMediaBrowser.disconnect(); mQSMediaBrowser = null; } }, componentName); mQSMediaBrowser.testConnection(); } /** Loading packages/SystemUI/src/com/android/systemui/qs/QSMediaBrowser.java +62 −29 Original line number Diff line number Diff line Loading @@ -58,21 +58,25 @@ public class QSMediaBrowser { mContext = context; mCallback = callback; mComponentName = componentName; Bundle rootHints = new Bundle(); rootHints.putBoolean(MediaBrowserService.BrowserRoot.EXTRA_RECENT, true); mMediaBrowser = new MediaBrowser(mContext, mComponentName, mConnectionCallback, 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 * 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(); rootHints.putBoolean(MediaBrowserService.BrowserRoot.EXTRA_RECENT, true); mMediaBrowser = new MediaBrowser(mContext, mComponentName, mConnectionCallback, rootHints); mMediaBrowser.connect(); } Loading @@ -94,20 +98,22 @@ public class QSMediaBrowser { } else { Log.e(TAG, "Child found but not playable for " + mComponentName); } mMediaBrowser.disconnect(); disconnect(); } @Override public void onError(String parentId) { Log.e(TAG, "Subscribe error for " + mComponentName + ": " + parentId); mMediaBrowser.disconnect(); mCallback.onError(); disconnect(); } @Override public void onError(String parentId, Bundle options) { Log.e(TAG, "Subscribe error for " + mComponentName + ": " + parentId + ", options: " + options); mMediaBrowser.disconnect(); mCallback.onError(); disconnect(); } }; Loading @@ -134,6 +140,8 @@ public class QSMediaBrowser { @Override public void onConnectionSuspended() { Log.d(TAG, "Connection suspended for " + mComponentName); mCallback.onError(); disconnect(); } /** Loading @@ -143,16 +151,28 @@ public class QSMediaBrowser { public void onConnectionFailed() { Log.e(TAG, "Connection failed for " + mComponentName); 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() { if (mMediaBrowser.isConnected()) { public void disconnect() { if (mMediaBrowser != null) { 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(); rootHints.putBoolean(MediaBrowserService.BrowserRoot.EXTRA_RECENT, true); mMediaBrowser = new MediaBrowser(mContext, mComponentName, Loading @@ -165,6 +185,17 @@ public class QSMediaBrowser { controller.getTransportControls(); controller.getTransportControls().prepare(); controller.getTransportControls().play(); mCallback.onConnected(); } @Override public void onConnectionFailed() { mCallback.onError(); } @Override public void onConnectionSuspended() { mCallback.onError(); } }, rootHints); mMediaBrowser.connect(); Loading Loading @@ -192,42 +223,44 @@ public class QSMediaBrowser { } /** * Used to test if SystemUI is allowed to connect to the given component as a MediaBrowser * @param mContext the context * @param callback methods onConnected or onError will be called to indicate whether the * connection was successful or not * @param mComponentName Component name of the MediaBrowserService this browser will connect to * Used to test if SystemUI is allowed to connect to the given component as a MediaBrowser. * 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 static MediaBrowser testConnection(Context mContext, Callback callback, ComponentName mComponentName) { final MediaBrowser.ConnectionCallback mConnectionCallback = public void testConnection() { disconnect(); final MediaBrowser.ConnectionCallback connectionCallback = new MediaBrowser.ConnectionCallback() { @Override public void onConnected() { Log.d(TAG, "connected"); callback.onConnected(); if (mMediaBrowser.getRoot() == null) { mCallback.onError(); } else { mCallback.onConnected(); } } @Override public void onConnectionSuspended() { Log.d(TAG, "suspended"); callback.onError(); mCallback.onError(); } @Override public void onConnectionFailed() { Log.d(TAG, "failed"); callback.onError(); mCallback.onError(); } }; Bundle rootHints = new Bundle(); rootHints.putBoolean(MediaBrowserService.BrowserRoot.EXTRA_RECENT, true); MediaBrowser browser = new MediaBrowser(mContext, mMediaBrowser = new MediaBrowser(mContext, mComponentName, mConnectionCallback, connectionCallback, rootHints); browser.connect(); return browser; mMediaBrowser.connect(); } /** Loading packages/SystemUI/src/com/android/systemui/qs/QSPanel.java +50 −36 Original line number Diff line number Diff line Loading @@ -270,27 +270,8 @@ public class QSPanel extends LinearLayout implements Tunable, Callback, Brightne return; } QSMediaPlayer player = null; String packageName = notif.getPackageName(); for (QSMediaPlayer p : mMediaPlayers) { 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; } } QSMediaPlayer player = findMediaPlayer(packageName, token, key); int playerWidth = (int) getResources().getDimension(R.dimen.qs_media_width); int padding = (int) getResources().getDimension(R.dimen.qs_media_padding); Loading @@ -299,7 +280,7 @@ public class QSPanel extends LinearLayout implements Tunable, Callback, Brightne lp.setMarginEnd(padding); if (player == null) { Log.d(TAG, "creating new player"); Log.d(TAG, "creating new player for " + packageName); // Set up listener for device changes // TODO: integrate with MediaTransferManager? InfoMediaManager imm = new InfoMediaManager(mContext, notif.getPackageName(), Loading Loading @@ -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() { return mMediaCarousel; } Loading Loading @@ -369,12 +379,17 @@ public class QSPanel extends LinearLayout implements Tunable, Callback, Brightne } 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(); MediaSession.Token token = browser.getToken(); QSMediaPlayer player = findMediaPlayer(pkgName, token, null); // Add controls to carousel if (player == null) { player = new QSMediaPlayer(mContext, QSPanel.this, null, mForegroundExecutor, mBackgroundExecutor, mActivityStarter); // Add to carousel int playerWidth = (int) getResources().getDimension(R.dimen.qs_media_width); int padding = (int) getResources().getDimension(R.dimen.qs_media_padding); LinearLayout.LayoutParams lp = new LinearLayout.LayoutParams(playerWidth, Loading @@ -384,11 +399,10 @@ public class QSPanel extends LinearLayout implements Tunable, Callback, Brightne mMediaCarousel.addView(player.getView(), lp); ((View) mMediaCarousel.getParent()).setVisibility(View.VISIBLE); mMediaPlayers.add(player); } int iconColor = Color.DKGRAY; int bgColor = Color.LTGRAY; MediaSession.Token token = browser.getToken(); player.setMediaSession(token, desc, iconColor, bgColor, browser.getAppIntent(), pkgName); } Loading Loading
packages/SystemUI/src/com/android/systemui/media/MediaControlPanel.java +31 −3 Original line number Diff line number Diff line Loading @@ -98,6 +98,7 @@ public class MediaControlPanel { private SharedPreferences mSharedPrefs; private boolean mCheckedForResumption = false; private boolean mIsRemotePlayback; private QSMediaBrowser mQSMediaBrowser; // Button IDs used in notifications protected static final int[] NOTIF_ACTION_IDS = { Loading Loading @@ -232,6 +233,11 @@ public class MediaControlPanel { String key) { // Ensure that component names are updated if token has changed if (mToken == null || !mToken.equals(token)) { if (mQSMediaBrowser != null) { Log.d(TAG, "Disconnecting old media browser"); mQSMediaBrowser.disconnect(); mQSMediaBrowser = null; } mToken = token; mServiceComponent = null; mCheckedForResumption = false; Loading Loading @@ -618,8 +624,22 @@ public class MediaControlPanel { ImageButton btn = mMediaNotifView.findViewById(mActionIds[0]); btn.setOnClickListener(v -> { Log.d(TAG, "Attempting to restart session"); QSMediaBrowser browser = new QSMediaBrowser(mContext, null, mServiceComponent); browser.restart(); if (mQSMediaBrowser != null) { 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.setImageTintList(ColorStateList.valueOf(mForegroundColor)); Loading Loading @@ -654,13 +674,18 @@ public class MediaControlPanel { */ private void tryUpdateResumptionList(ComponentName 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() { @Override public void onConnected() { Log.d(TAG, "yes we can resume with " + componentName); mServiceComponent = componentName; updateResumptionList(componentName); mQSMediaBrowser.disconnect(); mQSMediaBrowser = null; } @Override Loading @@ -671,9 +696,12 @@ public class MediaControlPanel { // If it's not active and we can't resume, remove removePlayer(); } mQSMediaBrowser.disconnect(); mQSMediaBrowser = null; } }, componentName); mQSMediaBrowser.testConnection(); } /** Loading
packages/SystemUI/src/com/android/systemui/qs/QSMediaBrowser.java +62 −29 Original line number Diff line number Diff line Loading @@ -58,21 +58,25 @@ public class QSMediaBrowser { mContext = context; mCallback = callback; mComponentName = componentName; Bundle rootHints = new Bundle(); rootHints.putBoolean(MediaBrowserService.BrowserRoot.EXTRA_RECENT, true); mMediaBrowser = new MediaBrowser(mContext, mComponentName, mConnectionCallback, 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 * 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(); rootHints.putBoolean(MediaBrowserService.BrowserRoot.EXTRA_RECENT, true); mMediaBrowser = new MediaBrowser(mContext, mComponentName, mConnectionCallback, rootHints); mMediaBrowser.connect(); } Loading @@ -94,20 +98,22 @@ public class QSMediaBrowser { } else { Log.e(TAG, "Child found but not playable for " + mComponentName); } mMediaBrowser.disconnect(); disconnect(); } @Override public void onError(String parentId) { Log.e(TAG, "Subscribe error for " + mComponentName + ": " + parentId); mMediaBrowser.disconnect(); mCallback.onError(); disconnect(); } @Override public void onError(String parentId, Bundle options) { Log.e(TAG, "Subscribe error for " + mComponentName + ": " + parentId + ", options: " + options); mMediaBrowser.disconnect(); mCallback.onError(); disconnect(); } }; Loading @@ -134,6 +140,8 @@ public class QSMediaBrowser { @Override public void onConnectionSuspended() { Log.d(TAG, "Connection suspended for " + mComponentName); mCallback.onError(); disconnect(); } /** Loading @@ -143,16 +151,28 @@ public class QSMediaBrowser { public void onConnectionFailed() { Log.e(TAG, "Connection failed for " + mComponentName); 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() { if (mMediaBrowser.isConnected()) { public void disconnect() { if (mMediaBrowser != null) { 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(); rootHints.putBoolean(MediaBrowserService.BrowserRoot.EXTRA_RECENT, true); mMediaBrowser = new MediaBrowser(mContext, mComponentName, Loading @@ -165,6 +185,17 @@ public class QSMediaBrowser { controller.getTransportControls(); controller.getTransportControls().prepare(); controller.getTransportControls().play(); mCallback.onConnected(); } @Override public void onConnectionFailed() { mCallback.onError(); } @Override public void onConnectionSuspended() { mCallback.onError(); } }, rootHints); mMediaBrowser.connect(); Loading Loading @@ -192,42 +223,44 @@ public class QSMediaBrowser { } /** * Used to test if SystemUI is allowed to connect to the given component as a MediaBrowser * @param mContext the context * @param callback methods onConnected or onError will be called to indicate whether the * connection was successful or not * @param mComponentName Component name of the MediaBrowserService this browser will connect to * Used to test if SystemUI is allowed to connect to the given component as a MediaBrowser. * 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 static MediaBrowser testConnection(Context mContext, Callback callback, ComponentName mComponentName) { final MediaBrowser.ConnectionCallback mConnectionCallback = public void testConnection() { disconnect(); final MediaBrowser.ConnectionCallback connectionCallback = new MediaBrowser.ConnectionCallback() { @Override public void onConnected() { Log.d(TAG, "connected"); callback.onConnected(); if (mMediaBrowser.getRoot() == null) { mCallback.onError(); } else { mCallback.onConnected(); } } @Override public void onConnectionSuspended() { Log.d(TAG, "suspended"); callback.onError(); mCallback.onError(); } @Override public void onConnectionFailed() { Log.d(TAG, "failed"); callback.onError(); mCallback.onError(); } }; Bundle rootHints = new Bundle(); rootHints.putBoolean(MediaBrowserService.BrowserRoot.EXTRA_RECENT, true); MediaBrowser browser = new MediaBrowser(mContext, mMediaBrowser = new MediaBrowser(mContext, mComponentName, mConnectionCallback, connectionCallback, rootHints); browser.connect(); return browser; mMediaBrowser.connect(); } /** Loading
packages/SystemUI/src/com/android/systemui/qs/QSPanel.java +50 −36 Original line number Diff line number Diff line Loading @@ -270,27 +270,8 @@ public class QSPanel extends LinearLayout implements Tunable, Callback, Brightne return; } QSMediaPlayer player = null; String packageName = notif.getPackageName(); for (QSMediaPlayer p : mMediaPlayers) { 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; } } QSMediaPlayer player = findMediaPlayer(packageName, token, key); int playerWidth = (int) getResources().getDimension(R.dimen.qs_media_width); int padding = (int) getResources().getDimension(R.dimen.qs_media_padding); Loading @@ -299,7 +280,7 @@ public class QSPanel extends LinearLayout implements Tunable, Callback, Brightne lp.setMarginEnd(padding); if (player == null) { Log.d(TAG, "creating new player"); Log.d(TAG, "creating new player for " + packageName); // Set up listener for device changes // TODO: integrate with MediaTransferManager? InfoMediaManager imm = new InfoMediaManager(mContext, notif.getPackageName(), Loading Loading @@ -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() { return mMediaCarousel; } Loading Loading @@ -369,12 +379,17 @@ public class QSPanel extends LinearLayout implements Tunable, Callback, Brightne } 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(); MediaSession.Token token = browser.getToken(); QSMediaPlayer player = findMediaPlayer(pkgName, token, null); // Add controls to carousel if (player == null) { player = new QSMediaPlayer(mContext, QSPanel.this, null, mForegroundExecutor, mBackgroundExecutor, mActivityStarter); // Add to carousel int playerWidth = (int) getResources().getDimension(R.dimen.qs_media_width); int padding = (int) getResources().getDimension(R.dimen.qs_media_padding); LinearLayout.LayoutParams lp = new LinearLayout.LayoutParams(playerWidth, Loading @@ -384,11 +399,10 @@ public class QSPanel extends LinearLayout implements Tunable, Callback, Brightne mMediaCarousel.addView(player.getView(), lp); ((View) mMediaCarousel.getParent()).setVisibility(View.VISIBLE); mMediaPlayers.add(player); } int iconColor = Color.DKGRAY; int bgColor = Color.LTGRAY; MediaSession.Token token = browser.getToken(); player.setMediaSession(token, desc, iconColor, bgColor, browser.getAppIntent(), pkgName); } Loading