Multi-listener support in MediaPlaybackModel
- Updating MediaPlaybackModel to support multiple listeners.
- Allowing MediaBrowser extras to be passed in optionally.
Bug: 34352155
Test: Manually
Change-Id: I309c9319cb68338a2716f6377a98ae5e22166a3e
diff --git a/src/com/android/car/media/MediaPlaybackFragment.java b/src/com/android/car/media/MediaPlaybackFragment.java
index 25b1e65..04bd846 100644
--- a/src/com/android/car/media/MediaPlaybackFragment.java
+++ b/src/com/android/car/media/MediaPlaybackFragment.java
@@ -52,6 +52,7 @@
import android.widget.ProgressBar;
import android.widget.SeekBar;
import android.widget.TextView;
+
import com.android.car.apps.common.BitmapDownloader;
import com.android.car.apps.common.BitmapWorkerOptions;
import com.android.car.apps.common.util.Assert;
@@ -143,7 +144,9 @@
mActivity = (MediaActivity) getHost();
mShowTitleDelayMs =
mActivity.getResources().getInteger(R.integer.new_album_art_fade_in_offset);
- mMediaPlaybackModel = new MediaPlaybackModel(mActivity.getContext(), this);
+ mMediaPlaybackModel =
+ new MediaPlaybackModel(mActivity.getContext(), null /* browserExtras */);
+ mMediaPlaybackModel.addListener(this);
mTelephonyManager = (TelephonyManager) mActivity.getContext()
.getSystemService(Context.TELEPHONY_SERVICE);
}
diff --git a/src/com/android/car/media/MediaPlaybackModel.java b/src/com/android/car/media/MediaPlaybackModel.java
index ab45e48..6f234e2 100644
--- a/src/com/android/car/media/MediaPlaybackModel.java
+++ b/src/com/android/car/media/MediaPlaybackModel.java
@@ -31,10 +31,13 @@
import android.support.annotation.NonNull;
import android.support.annotation.Nullable;
import android.util.Log;
+
import com.android.car.apps.common.util.Assert;
import java.util.ArrayList;
+import java.util.LinkedList;
import java.util.List;
+import java.util.function.Consumer;
/**
* A model for controlling media playback. This model will take care of all Media Manager, Browser,
@@ -45,8 +48,9 @@
public class MediaPlaybackModel {
private static final String TAG = "GH.MediaPlaybackModel";
- private final MediaPlaybackModel.Listener mListener;
private final Context mContext;
+ private final Bundle mBrowserExtras;
+ private final List<MediaPlaybackModel.Listener> mListeners = new LinkedList<>();
private Handler mHandler;
private MediaController mController;
@@ -61,21 +65,38 @@
* done in the main thread.
*/
public interface Listener {
+ /** Indicates active media app has changed. A new mediaBrowser is now connecting to the new
+ * app and mediaController has been released, pending connection to new service.
+ */
void onMediaAppChanged(@Nullable ComponentName currentName,
@Nullable ComponentName newName);
void onMediaAppStatusMessageChanged(@Nullable String message);
+
+ /**
+ * Indicates the mediaBrowser is not connected and mediaController is available.
+ */
void onMediaConnected();
+ /**
+ * Indicates mediaBrowser connection is temporarily suspended.
+ * */
void onMediaConnectionSuspended();
+ /**
+ * Indicates that the MediaBrowser connected failed. The mediaBrowser and controller have
+ * now been released.
+ */
void onMediaConnectionFailed(CharSequence failedMediaClientName);
void onPlaybackStateChanged(@Nullable PlaybackState state);
void onMetadataChanged(@Nullable MediaMetadata metadata);
void onQueueChanged(List<MediaSession.QueueItem> queue);
+ /**
+ * Indicates that the MediaSession was destroyed. The mediaController has been released.
+ */
void onSessionDestroyed(CharSequence destroyedMediaClientName);
}
- public MediaPlaybackModel(Context context, MediaPlaybackModel.Listener listener) {
+ public MediaPlaybackModel(Context context, Bundle browserExtras) {
mContext = context;
- mListener = listener;
+ mBrowserExtras = browserExtras;
mHandler = new Handler(Looper.getMainLooper());
}
@@ -111,6 +132,25 @@
}
@MainThread
+ public void addListener(MediaPlaybackModel.Listener listener) {
+ Assert.isMainThread();
+ mListeners.add(listener);
+ }
+
+ @MainThread
+ public void removeListener(MediaPlaybackModel.Listener listener) {
+ Assert.isMainThread();
+ mListeners.remove(listener);
+ }
+
+ @MainThread
+ private void notifyListeners(Consumer<Listener> callback) {
+ Assert.isMainThread();
+ // Invokes callback.accept(listener) for each listener.
+ mListeners.forEach(callback);
+ }
+
+ @MainThread
public Resources getPackageResources() {
Assert.isMainThread();
return mPackageResources;
@@ -207,46 +247,40 @@
private final MediaManager.Listener mMediaManagerListener = new MediaManager.Listener() {
@Override
public void onMediaAppChanged(final ComponentName name) {
- mHandler.post(new Runnable() {
- @Override
- public void run() {
- if (mBrowser != null) {
- mBrowser.disconnect();
- }
- mBrowser = new MediaBrowser(mContext, name, mConnectionCallback, null);
- try {
- mPackageResources = mContext.getPackageManager().getResourcesForApplication(
- name.getPackageName());
- } catch (PackageManager.NameNotFoundException e) {
- Log.e(TAG, "Unable to get resources for " + name.getPackageName());
- }
-
- if (mController != null) {
- mController.unregisterCallback(mMediaControllerCallback);
- mController = null;
- }
- mBrowser.connect();
-
- // reset the colors and views if we switch to another app.
- mAccentColor = MediaManager.getInstance(mContext)
- .getMediaClientAccentColor();
- mPrimaryColorDark = MediaManager.getInstance(mContext)
- .getMediaClientPrimaryColorDark();
-
- final ComponentName currentName = mCurrentComponentName;
- mListener.onMediaAppChanged(currentName, name);
- mCurrentComponentName = name;
+ mHandler.post(() -> {
+ if (mBrowser != null) {
+ mBrowser.disconnect();
}
+ mBrowser = new MediaBrowser(mContext, name, mConnectionCallback, mBrowserExtras);
+ try {
+ mPackageResources = mContext.getPackageManager().getResourcesForApplication(
+ name.getPackageName());
+ } catch (PackageManager.NameNotFoundException e) {
+ Log.e(TAG, "Unable to get resources for " + name.getPackageName());
+ }
+
+ if (mController != null) {
+ mController.unregisterCallback(mMediaControllerCallback);
+ mController = null;
+ }
+ mBrowser.connect();
+
+ // reset the colors and views if we switch to another app.
+ mAccentColor = MediaManager.getInstance(mContext)
+ .getMediaClientAccentColor();
+ mPrimaryColorDark = MediaManager.getInstance(mContext)
+ .getMediaClientPrimaryColorDark();
+
+ final ComponentName currentName = mCurrentComponentName;
+ notifyListeners((listener) -> listener.onMediaAppChanged(currentName, name));
+ mCurrentComponentName = name;
});
}
@Override
public void onStatusMessageChanged(final String message) {
- mHandler.post(new Runnable() {
- @Override
- public void run() {
- mListener.onMediaAppStatusMessageChanged(message);
- }
+ mHandler.post(() -> {
+ notifyListeners((listener) -> listener.onMediaAppStatusMessageChanged(message));
});
}
};
@@ -255,52 +289,44 @@
new MediaBrowser.ConnectionCallback() {
@Override
public void onConnected() {
- mHandler.post(new Runnable() {
- @Override
- public void run() {
- // Existing mController has already been disconnected before we call
- // MediaBrowser.connect()
- // getSessionToken returns a non null token
- MediaSession.Token token = mBrowser.getSessionToken();
- if (mController != null) {
- mController.unregisterCallback(mMediaControllerCallback);
- }
- mController = new MediaController(mContext, token);
- mController.registerCallback(mMediaControllerCallback);
- mListener.onMediaConnected();
+ mHandler.post(()->{
+ // Existing mController has already been disconnected before we call
+ // MediaBrowser.connect()
+ // getSessionToken returns a non null token
+ MediaSession.Token token = mBrowser.getSessionToken();
+ if (mController != null) {
+ mController.unregisterCallback(mMediaControllerCallback);
}
+ mController = new MediaController(mContext, token);
+ mController.registerCallback(mMediaControllerCallback);
+ notifyListeners(Listener::onMediaConnected);
});
}
@Override
public void onConnectionSuspended() {
- mHandler.post(new Runnable() {
- @Override
- public void run() {
- if (Log.isLoggable(TAG, Log.VERBOSE)) {
- Log.v(TAG, "Media browser service connection suspended."
- + " Waiting to be reconnected....");
- }
- mListener.onMediaConnectionSuspended();
+ mHandler.post(() -> {
+ if (Log.isLoggable(TAG, Log.VERBOSE)) {
+ Log.v(TAG, "Media browser service connection suspended."
+ + " Waiting to be reconnected....");
}
+ notifyListeners(Listener::onMediaConnectionSuspended);
});
}
@Override
public void onConnectionFailed() {
- mHandler.post(new Runnable() {
- @Override
- public void run() {
- Log.e(TAG, "Media browser service connection FAILED!");
- // disconnect anyway to make sure we get into a sanity state
- mBrowser.disconnect();
- mBrowser = null;
- mCurrentComponentName = null;
+ mHandler.post(() -> {
+ Log.e(TAG, "Media browser service connection FAILED!");
+ // disconnect anyway to make sure we get into a sanity state
+ mBrowser.disconnect();
+ mBrowser = null;
+ mCurrentComponentName = null;
- CharSequence failedClientName = MediaManager.getInstance(mContext)
- .getMediaClientName();
- mListener.onMediaConnectionFailed(failedClientName);
- }
+ CharSequence failedClientName = MediaManager.getInstance(mContext)
+ .getMediaClientName();
+ notifyListeners(
+ (listener) -> listener.onMediaConnectionFailed(failedClientName));
});
}
};
@@ -309,56 +335,43 @@
new MediaController.Callback() {
@Override
public void onPlaybackStateChanged(final PlaybackState state) {
- mHandler.post(new Runnable() {
- @Override
- public void run() {
- mListener.onPlaybackStateChanged(state);
- }
+ mHandler.post(() -> {
+ notifyListeners((listener) -> listener.onPlaybackStateChanged(state));
});
}
@Override
public void onMetadataChanged(final MediaMetadata metadata) {
- mHandler.post(new Runnable() {
- @Override
- public void run() {
- mListener.onMetadataChanged(metadata);
- }
+ mHandler.post(() -> {
+ notifyListeners((listener) -> listener.onMetadataChanged(metadata));
});
}
@Override
public void onQueueChanged(final List<MediaSession.QueueItem> queue) {
- mHandler.post(new Runnable() {
- @Override
- public void run() {
- List<MediaSession.QueueItem> currentQueue = queue;
- if (currentQueue == null) {
- currentQueue = new ArrayList<>();
- }
- mListener.onQueueChanged(currentQueue);
- }
+ mHandler.post(() -> {
+ final List<MediaSession.QueueItem> currentQueue =
+ queue != null ? queue : new ArrayList<>();
+ notifyListeners((listener) -> listener.onQueueChanged(currentQueue));
});
}
@Override
public void onSessionDestroyed() {
- mHandler.post(new Runnable() {
- @Override
- public void run() {
- if (Log.isLoggable(TAG, Log.VERBOSE)) {
- Log.v(TAG, "onSessionDestroyed()");
- }
- mCurrentComponentName = null;
- if (mController != null) {
- mController.unregisterCallback(mMediaControllerCallback);
- mController = null;
- }
-
- CharSequence destroyedMediaClientName = MediaManager.getInstance(
- mContext).getMediaClientName();
- mListener.onSessionDestroyed(destroyedMediaClientName);
+ mHandler.post(() -> {
+ if (Log.isLoggable(TAG, Log.VERBOSE)) {
+ Log.v(TAG, "onSessionDestroyed()");
}
+ mCurrentComponentName = null;
+ if (mController != null) {
+ mController.unregisterCallback(mMediaControllerCallback);
+ mController = null;
+ }
+
+ CharSequence destroyedClientName = MediaManager.getInstance(
+ mContext).getMediaClientName();
+ notifyListeners(
+ (listener) -> listener.onSessionDestroyed(destroyedClientName));
});
}
};