DO NOT MERGE Add MediaBrowse state to MediaBrowserViewModel and remove deprecated MediaSource.
SimpleMediaSource is now renamed to MediaSource.
Test: Manual
Change-Id: I5e04532b3c88ae20a7ed1d9ca0dfae36b42c7949
diff --git a/car-media-common/src/com/android/car/media/common/MediaSource.java b/car-media-common/src/com/android/car/media/common/MediaSource.java
deleted file mode 100644
index 5e65a49..0000000
--- a/car-media-common/src/com/android/car/media/common/MediaSource.java
+++ /dev/null
@@ -1,606 +0,0 @@
-/*
- * Copyright 2018 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package com.android.car.media.common;
-
-import android.annotation.NonNull;
-import android.annotation.Nullable;
-import android.content.ComponentName;
-import android.content.Context;
-import android.content.Intent;
-import android.content.pm.ApplicationInfo;
-import android.content.pm.PackageManager;
-import android.content.pm.ResolveInfo;
-import android.content.pm.ServiceInfo;
-import android.graphics.Bitmap;
-import android.graphics.Canvas;
-import android.graphics.Paint;
-import android.graphics.PorterDuff;
-import android.graphics.PorterDuffXfermode;
-import android.graphics.Rect;
-import android.graphics.drawable.BitmapDrawable;
-import android.graphics.drawable.Drawable;
-import android.os.Bundle;
-import android.os.Handler;
-import android.os.RemoteException;
-import android.service.media.MediaBrowserService;
-import android.support.v4.media.MediaBrowserCompat;
-import android.support.v4.media.session.MediaControllerCompat;
-import android.support.v4.media.session.MediaSessionCompat;
-import android.util.Log;
-
-import java.util.ArrayList;
-import java.util.HashMap;
-import java.util.HashSet;
-import java.util.List;
-import java.util.Map;
-import java.util.Objects;
-import java.util.Set;
-import java.util.function.Consumer;
-import java.util.stream.Collectors;
-
-/**
- * This represents a source of media content. It provides convenient methods to access media source
- * metadata, such as primary color and application name.
- *
- * <p>It also allows consumers to subscribe to its {@link android.service.media.MediaBrowserService}
- * if such service is implemented by the source.
- */
-public class MediaSource {
- private static final String TAG = "MediaSource";
-
- /** Number of times we will retry obtaining the list of children of a certain node */
- private static final int CHILDREN_SUBSCRIPTION_RETRIES = 3;
- /** Time between retries while trying to obtain the list of children of a certain node */
- private static final int CHILDREN_SUBSCRIPTION_RETRY_TIME_MS = 1000;
-
- private final String mPackageName;
- @Nullable
- private final String mBrowseServiceClassName;
- @Nullable
- private final MediaBrowserCompat mBrowser;
- private final Context mContext;
- private final Handler mHandler = new Handler();
- private List<Observer> mObservers = new ArrayList<>();
- private CharSequence mName;
- private String mRootNode;
-
- /**
- * Custom media sources which should not be templatized.
- */
- private static final Set<String> CUSTOM_MEDIA_SOURCES = new HashSet<>();
- static {
- CUSTOM_MEDIA_SOURCES.add("com.android.car.radio");
- }
-
- /**
- * An observer of this media source.
- */
- public abstract static class Observer {
- /**
- * This method is called if a successful connection to the {@link MediaBrowserService} is
- * made for this source. A connection is initiated as soon as there is at least one
- * {@link Observer} subscribed by using {@link MediaSource#subscribe(Observer)}.
- *
- * @param success true if the connection was successful or false otherwise.
- */
- protected void onBrowseConnected(boolean success) {};
-
- /**
- * This method is called if the connection to the {@link MediaBrowserService} is lost.
- */
- protected void onBrowseDisconnected() {};
- }
-
- /**
- * A subscription to a collection of items
- */
- public interface ItemsSubscription {
- /**
- * This method is called whenever media items are loaded or updated.
- *
- * @param mediaSource {@link MediaSource} these items belongs to
- * @param parentId identifier of the items parent.
- * @param items items loaded, or null if there was an error trying to load them.
- */
- void onChildrenLoaded(MediaSource mediaSource, String parentId,
- @Nullable List<MediaItemMetadata> items);
- }
-
- private final MediaBrowserCompat.ConnectionCallback mConnectionCallback =
- new MediaBrowserCompat.ConnectionCallback() {
- @Override
- public void onConnected() {
- MediaSource.this.notify(observer -> observer.onBrowseConnected(true));
- }
-
- @Override
- public void onConnectionSuspended() {
- MediaSource.this.notify(Observer::onBrowseDisconnected);
- }
-
- @Override
- public void onConnectionFailed() {
- MediaSource.this.notify(observer -> observer.onBrowseConnected(false));
- }
- };
-
- /**
- * Creates a {@link MediaSource} for the given application package name
- */
- public MediaSource(Context context, String packageName) {
- mContext = context;
- mPackageName = packageName;
- mBrowseServiceClassName = getBrowseServiceClassName(packageName);
- if (mBrowseServiceClassName != null) {
- mBrowser = new MediaBrowserCompat(mContext,
- new ComponentName(mPackageName, mBrowseServiceClassName),
- mConnectionCallback,
- null);
- } else {
- // This media source doesn't provide browsing.
- mBrowser = null;
- }
- extractComponentInfo(mPackageName, mBrowseServiceClassName);
- }
-
- /**
- * @return the classname corresponding to a {@link MediaBrowserService} in the
- * media source, or null if the media source doesn't implement {@link MediaBrowserService}.
- * A non-null result doesn't imply that this service is accessible. The consumer code should
- * attempt to connect and handle rejections gracefully.
- */
- @Nullable
- private String getBrowseServiceClassName(String packageName) {
- PackageManager packageManager = mContext.getPackageManager();
- Intent intent = new Intent();
- intent.setAction(MediaBrowserService.SERVICE_INTERFACE);
- intent.setPackage(packageName);
- List<ResolveInfo> resolveInfos = packageManager.queryIntentServices(intent,
- PackageManager.GET_RESOLVED_FILTER);
- if (resolveInfos == null || resolveInfos.isEmpty()) {
- return null;
- }
- return resolveInfos.get(0).serviceInfo.name;
- }
-
- /**
- * Subscribes to this media source. This allows consuming browse information.
-
- * @return true if a subscription could be added, or false otherwise (for example, if the
- * {@link MediaBrowserService} is not available for this source.
- */
- public boolean subscribe(Observer observer) {
- if (mBrowser == null) {
- return false;
- }
- mObservers.add(observer);
- if (!mBrowser.isConnected()) {
- try {
- mBrowser.connect();
- } catch (IllegalStateException ex) {
- // Ignore: MediaBrowse could be in an intermediate state (not connected, but not
- // disconnected either.). In this situation, trying to connect again can throw
- // this exception, but there is no way to know without trying.
- }
- } else {
- observer.onBrowseConnected(true);
- }
- return true;
- }
-
- /**
- * Unsubscribe from this media source
- */
- public void unsubscribe(Observer observer) {
- mObservers.remove(observer);
- if (mObservers.isEmpty()) {
- // TODO(b/77640010): Review MediaBrowse disconnection.
- // Some media sources are not responding correctly to MediaBrowser#disconnect(). We
- // are keeping the connection going.
- // mBrowser.disconnect();
- }
- }
-
- private Map<String, ChildrenSubscription> mChildrenSubscriptions = new HashMap<>();
-
- /**
- * Obtains the root node in the browse tree of this node.
- */
- public String getRoot() {
- if (mRootNode == null) {
- mRootNode = mBrowser.getRoot();
- }
- return mRootNode;
- }
-
- /**
- * Subscribes to changes on the list of media item children of the given parent. Multiple
- * subscription can be added to the same node. If the node has been already loaded, then all
- * new subscription will immediately obtain a copy of the last obtained list.
- *
- * @param parentId parent of the children to load, or null to indicate children of the root
- * node.
- * @param callback callback used to provide updates on the subscribed node.
- * @throws IllegalStateException if browsing is not available or it is not connected.
- */
- public void subscribeChildren(@Nullable String parentId, ItemsSubscription callback) {
- if (mBrowser == null) {
- throw new IllegalStateException("Browsing is not available for this source: "
- + getName());
- }
- if (mRootNode == null && !mBrowser.isConnected()) {
- throw new IllegalStateException("Subscribing to the root node can only be done while "
- + "connected: " + getName());
- }
- mRootNode = mBrowser.getRoot();
-
- String itemId = parentId != null ? parentId : mRootNode;
- ChildrenSubscription subscription = mChildrenSubscriptions.get(itemId);
- if (subscription != null) {
- subscription.add(callback);
- } else {
- subscription = new ChildrenSubscription(mBrowser, itemId);
- subscription.add(callback);
- mChildrenSubscriptions.put(itemId, subscription);
- subscription.start(CHILDREN_SUBSCRIPTION_RETRIES,
- CHILDREN_SUBSCRIPTION_RETRY_TIME_MS);
- }
- }
-
- /**
- * Unsubscribes to changes on the list of media items children of the given parent
- *
- * @param parentId parent to unsubscribe, or null to unsubscribe from the root node.
- * @param callback callback to remove
- * @throws IllegalStateException if browsing is not available or it is not connected.
- */
- public void unsubscribeChildren(@Nullable String parentId, ItemsSubscription callback) {
- // If we are not connected
- if (mBrowser == null) {
- throw new IllegalStateException("Browsing is not available for this source: "
- + getName());
- }
- if (parentId == null && mRootNode == null) {
- // If we are trying to unsubscribe from root, but we haven't determine it's Id, then
- // there is nothing we can do.
- return;
- }
-
- String itemId = parentId != null ? parentId : mRootNode;
- ChildrenSubscription subscription = mChildrenSubscriptions.get(itemId);
- if (subscription != null) {
- subscription.remove(callback);
- if (subscription.count() == 0) {
- subscription.stop();
- mChildrenSubscriptions.remove(itemId);
- }
- }
- }
-
- /**
- * {@link MediaBrowserCompat.SubscriptionCallback} wrapper used to overcome the lack of a
- * reliable method to obtain the initial list of children of a given node.
- * <p>
- * When some 3rd party apps go through configuration changes (i.e., in the case of user-switch),
- * they leave subscriptions in an intermediate state where neither
- * {@link MediaBrowserCompat.SubscriptionCallback#onChildrenLoaded(String, List)} nor
- * {@link MediaBrowserCompat.SubscriptionCallback#onError(String)} are invoked.
- * <p>
- * This wrapper works around this problem by retrying the subscription a given number of times
- * if no data is received after a certain amount of time. This process is started by calling
- * {@link #start(int, int)}, passing the number of retries and delay between them as
- * parameters.
- */
- private class ChildrenSubscription extends MediaBrowserCompat.SubscriptionCallback {
- private List<MediaItemMetadata> mItems;
- private boolean mIsDataLoaded;
- private List<ItemsSubscription> mSubscriptions = new ArrayList<>();
- private String mParentId;
- private int mRetries;
- private int mRetryDelay;
- private MediaBrowserCompat mMediaBrowser;
- private Runnable mRetryRunnable = new Runnable() {
- @Override
- public void run() {
- if (!mIsDataLoaded) {
- if (mRetries > 0) {
- mRetries--;
- mMediaBrowser.unsubscribe(mParentId);
- mMediaBrowser.subscribe(mParentId, ChildrenSubscription.this);
- mHandler.postDelayed(this, mRetryDelay);
- } else {
- mItems = null;
- mIsDataLoaded = true;
- notifySubscriptions();
- }
- }
- }
- };
-
- /**
- * Creates a subscription to the list of children of a certain media browse item
- *
- * @param mediaBrowser {@link MediaBrowserCompat} used to create the subscription
- * @param parentId identifier of the parent node to subscribe to
- */
- ChildrenSubscription(@NonNull MediaBrowserCompat mediaBrowser, String parentId) {
- mParentId = parentId;
- mMediaBrowser = mediaBrowser;
- }
-
- /**
- * Adds a subscriber to this list of children
- */
- void add(ItemsSubscription subscription) {
- mSubscriptions.add(subscription);
- if (mIsDataLoaded) {
- subscription.onChildrenLoaded(MediaSource.this, mParentId, mItems);
- }
- }
-
- /**
- * Removes a subscriber previously added with {@link #add(ItemsSubscription)}
- */
- void remove(ItemsSubscription subscription) {
- mSubscriptions.remove(subscription);
- }
-
- /**
- * Number of subscribers currently registered
- */
- int count() {
- return mSubscriptions.size();
- }
-
- /**
- * Starts trying to obtain the list of children
- *
- * @param retries number of times to retry. If children are not obtained in this time then
- * the {@link ItemsSubscription#onChildrenLoaded(MediaSource, String, List)}
- * will be invoked with a NULL list.
- * @param retryDelay time between retries in milliseconds
- */
- void start(int retries, int retryDelay) {
- if (mIsDataLoaded) {
- notifySubscriptions();
- mMediaBrowser.subscribe(mParentId, this);
- } else {
- mRetries = retries;
- mRetryDelay = retryDelay;
- mHandler.post(mRetryRunnable);
- }
- }
-
- /**
- * Stops retrying
- */
- void stop() {
- mHandler.removeCallbacks(mRetryRunnable);
- mMediaBrowser.unsubscribe(mParentId);
- }
-
- @Override
- public void onChildrenLoaded(String parentId,
- List<MediaBrowserCompat.MediaItem> children) {
- mHandler.removeCallbacks(mRetryRunnable);
- mItems = children.stream()
- .map(child -> new MediaItemMetadata(child))
- .collect(Collectors.toList());
- mIsDataLoaded = true;
- notifySubscriptions();
- }
-
- @Override
- public void onChildrenLoaded(String parentId, List<MediaBrowserCompat.MediaItem> children,
- Bundle options) {
- onChildrenLoaded(parentId, children);
- }
-
- @Override
- public void onError(String parentId) {
- mHandler.removeCallbacks(mRetryRunnable);
- mItems = null;
- mIsDataLoaded = true;
- notifySubscriptions();
- }
-
- @Override
- public void onError(String parentId, Bundle options) {
- onError(parentId);
- }
-
- private void notifySubscriptions() {
- for (ItemsSubscription subscription : mSubscriptions) {
- subscription.onChildrenLoaded(MediaSource.this, mParentId, mItems);
- }
- }
- }
-
- private void extractComponentInfo(@NonNull String packageName,
- @Nullable String browseServiceClassName) {
- try {
- ApplicationInfo applicationInfo =
- mContext.getPackageManager().getApplicationInfo(packageName,
- PackageManager.GET_META_DATA);
- ServiceInfo serviceInfo = browseServiceClassName != null
- ? mContext.getPackageManager().getServiceInfo(
- new ComponentName(packageName, browseServiceClassName),
- PackageManager.GET_META_DATA)
- : null;
-
- // Get the proper app name, check service label, then application label.
- if (serviceInfo != null && serviceInfo.labelRes != 0) {
- mName = serviceInfo.loadLabel(mContext.getPackageManager());
- } else if (applicationInfo.labelRes != 0) {
- mName = applicationInfo.loadLabel(mContext.getPackageManager());
- } else {
- mName = null;
- }
- } catch (PackageManager.NameNotFoundException e) {
- Log.w(TAG, "Unable to update media client package attributes.", e);
- }
- }
-
- private void notify(Consumer<Observer> notification) {
- mHandler.post(() -> {
- List<Observer> observers = new ArrayList<>(mObservers);
- for (Observer observer : observers) {
- notification.accept(observer);
- }
- });
- }
-
- /**
- * @return media source human readable name.
- */
- public CharSequence getName() {
- return mName;
- }
-
- /**
- * @return the package name that identifies this media source.
- */
- public String getPackageName() {
- return mPackageName;
- }
-
- /**
- * @return a {@link ComponentName} referencing this media source's {@link MediaBrowserService},
- * or NULL if this media source doesn't implement such service.
- */
- @Nullable
- public ComponentName getBrowseServiceComponentName() {
- if (mBrowseServiceClassName != null) {
- return new ComponentName(mPackageName, mBrowseServiceClassName);
- } else {
- return null;
- }
- }
-
- /**
- * Returns a {@link MediaControllerCompat} that allows controlling this media source, or NULL
- * if the media source doesn't support browsing or the browser is not connected.
- */
- @Nullable
- public MediaControllerCompat getMediaController() {
- if (mBrowser == null || !mBrowser.isConnected()) {
- return null;
- }
-
- MediaSessionCompat.Token token = mBrowser.getSessionToken();
- try {
- return new MediaControllerCompat(mContext, token);
- } catch (RemoteException e) {
- Log.e(TAG, "Couldn't get MediaControllerCompat", e);
- return null;
- }
- }
-
- /**
- * Returns this media source's icon as a {@link Drawable}
- */
- public Drawable getPackageIcon() {
- try {
- return mContext.getPackageManager().getApplicationIcon(getPackageName());
- } catch (PackageManager.NameNotFoundException e) {
- return null;
- }
- }
-
- /**
- * Returns this media source's icon cropped to a circle.
- */
- public Bitmap getRoundPackageIcon() {
- Drawable packageIcon = getPackageIcon();
- return packageIcon != null
- ? getRoundCroppedBitmap(drawableToBitmap(getPackageIcon()))
- : null;
- }
-
- /**
- * Indicates if this media source should not be templatize.
- */
- public boolean isCustom() {
- return CUSTOM_MEDIA_SOURCES.contains(mPackageName);
- }
-
- private Bitmap drawableToBitmap(Drawable drawable) {
- Bitmap bitmap = null;
-
- if (drawable instanceof BitmapDrawable) {
- BitmapDrawable bitmapDrawable = (BitmapDrawable) drawable;
- if (bitmapDrawable.getBitmap() != null) {
- return bitmapDrawable.getBitmap();
- }
- }
-
- if (drawable.getIntrinsicWidth() <= 0 || drawable.getIntrinsicHeight() <= 0) {
- bitmap = Bitmap.createBitmap(1, 1, Bitmap.Config.ARGB_8888);
- } else {
- bitmap = Bitmap.createBitmap(drawable.getIntrinsicWidth(),
- drawable.getIntrinsicHeight(), Bitmap.Config.ARGB_8888);
- }
-
- Canvas canvas = new Canvas(bitmap);
- drawable.setBounds(0, 0, canvas.getWidth(), canvas.getHeight());
- drawable.draw(canvas);
- return bitmap;
- }
-
- private Bitmap getRoundCroppedBitmap(Bitmap bitmap) {
- Bitmap output = Bitmap.createBitmap(bitmap.getWidth(), bitmap.getHeight(),
- Bitmap.Config.ARGB_8888);
- Canvas canvas = new Canvas(output);
-
- final int color = 0xff424242;
- final Paint paint = new Paint();
- final Rect rect = new Rect(0, 0, bitmap.getWidth(), bitmap.getHeight());
-
- paint.setAntiAlias(true);
- canvas.drawARGB(0, 0, 0, 0);
- paint.setColor(color);
- canvas.drawCircle(bitmap.getWidth() / 2, bitmap.getHeight() / 2,
- bitmap.getWidth() / 2f, paint);
- paint.setXfermode(new PorterDuffXfermode(PorterDuff.Mode.SRC_IN));
- canvas.drawBitmap(bitmap, rect, rect, paint);
- return output;
- }
-
- @Override
- public boolean equals(Object o) {
- if (this == o) return true;
- if (o == null || getClass() != o.getClass()) return false;
- MediaSource that = (MediaSource) o;
- return Objects.equals(mPackageName, that.mPackageName)
- && Objects.equals(mBrowseServiceClassName, that.mBrowseServiceClassName);
- }
-
- /** @return the current media browser. This media browser might not be connected yet. */
- public MediaBrowserCompat getMediaBrowser() {
- return mBrowser;
- }
-
- @Override
- public int hashCode() {
- return Objects.hash(mPackageName, mBrowseServiceClassName);
- }
-
- @Override
- public String toString() {
- return getPackageName();
- }
-}
diff --git a/car-media-common/src/com/android/car/media/common/PlaybackFragment.java b/car-media-common/src/com/android/car/media/common/PlaybackFragment.java
index b4a8efb..7008f7e 100644
--- a/car-media-common/src/com/android/car/media/common/PlaybackFragment.java
+++ b/car-media-common/src/com/android/car/media/common/PlaybackFragment.java
@@ -39,8 +39,8 @@
import com.android.car.media.common.playback.AlbumArtLiveData;
import com.android.car.media.common.playback.PlaybackViewModel;
+import com.android.car.media.common.source.MediaSource;
import com.android.car.media.common.source.MediaSourceViewModel;
-import com.android.car.media.common.source.SimpleMediaSource;
import com.bumptech.glide.request.target.Target;
@@ -106,7 +106,7 @@
private static final Intent MEDIA_TEMPLATE_INTENT =
new Intent(Car.CAR_INTENT_ACTION_MEDIA_TEMPLATE);
- private LiveData<SimpleMediaSource> mMediaSource;
+ private LiveData<MediaSource> mMediaSource;
private LiveData<CharSequence> mAppName;
private LiveData<Bitmap> mAppIcon;
private LiveData<Intent> mOpenIntent;
@@ -129,8 +129,8 @@
mPlaybackViewModel = playbackViewModel;
mMediaSourceViewModel = mediaSourceViewModel;
mMediaSource = mMediaSourceViewModel.getSelectedMediaSource();
- mAppName = mapNonNull(mMediaSource, SimpleMediaSource::getName);
- mAppIcon = mapNonNull(mMediaSource, SimpleMediaSource::getRoundPackageIcon);
+ mAppName = mapNonNull(mMediaSource, MediaSource::getName);
+ mAppIcon = mapNonNull(mMediaSource, MediaSource::getRoundPackageIcon);
mOpenIntent = mapNonNull(mMediaSource, MEDIA_TEMPLATE_INTENT, source -> {
if (source.isCustom()) {
// We are playing a custom app. Jump to it, not to the template
diff --git a/car-media-common/src/com/android/car/media/common/browse/MediaBrowserViewModel.java b/car-media-common/src/com/android/car/media/common/browse/MediaBrowserViewModel.java
index f41bd16..8702719 100644
--- a/car-media-common/src/com/android/car/media/common/browse/MediaBrowserViewModel.java
+++ b/car-media-common/src/com/android/car/media/common/browse/MediaBrowserViewModel.java
@@ -30,6 +30,7 @@
import androidx.lifecycle.AndroidViewModel;
import androidx.lifecycle.LiveData;
+import androidx.lifecycle.MediatorLiveData;
import androidx.lifecycle.MutableLiveData;
import com.android.car.arch.common.LoadingSwitchMap;
@@ -44,6 +45,20 @@
public class MediaBrowserViewModel extends AndroidViewModel {
+ /**
+ * Possible states of the application UI
+ */
+ public enum BrowseState {
+ /** There is no content to show */
+ EMPTY,
+ /** We are still in the process of obtaining data */
+ LOADING,
+ /** Data has been loaded */
+ LOADED,
+ /** The content can't be shown due an error */
+ ERROR
+ }
+
private final SwitchingLiveData<MediaBrowserCompat> mMediaBrowserSwitch =
SwitchingLiveData.newInstance();
@@ -59,6 +74,38 @@
connectedMediaBrowser == null
? null
: new BrowsedMediaItems(connectedMediaBrowser, browseId)));
+ private final LiveData<BrowseState> mBrowseState = new MediatorLiveData<BrowseState>() {
+ {
+ setValue(BrowseState.EMPTY);
+ addSource(mCurrentMediaItems.isLoading(), isLoading -> update());
+ addSource(mCurrentMediaItems.getOutput(), items -> update());
+ }
+
+ private void update() {
+ setValue(getState());
+ }
+
+ private BrowseState getState() {
+ Boolean isLoading = mCurrentMediaItems.isLoading().getValue();
+ if (isLoading == null) {
+ // Uninitialized
+ return BrowseState.EMPTY;
+ }
+ if (isLoading) {
+ return BrowseState.LOADING;
+ }
+ List<MediaItemMetadata> items = mCurrentMediaItems.getOutput().getValue();
+ if (items == null) {
+ // Normally this could be null if it hasn't been initialized, but in that case
+ // isLoading would not be false, so this means it must have encountered an error.
+ return BrowseState.ERROR;
+ }
+ if (items.isEmpty()) {
+ return BrowseState.EMPTY;
+ }
+ return BrowseState.LOADED;
+ }
+ };
public MediaBrowserViewModel(@NonNull Application application) {
super(application);
@@ -93,6 +140,10 @@
return mCurrentBrowseId.getValue();
}
+ public LiveData<BrowseState> getBrowseState() {
+ return mBrowseState;
+ }
+
public LiveData<Boolean> isLoading() {
return mCurrentMediaItems.isLoading();
}
diff --git a/car-media-common/src/com/android/car/media/common/source/ActiveMediaSelector.java b/car-media-common/src/com/android/car/media/common/source/ActiveMediaSelector.java
index 3fa0bb4..754d943 100644
--- a/car-media-common/src/com/android/car/media/common/source/ActiveMediaSelector.java
+++ b/car-media-common/src/com/android/car/media/common/source/ActiveMediaSelector.java
@@ -64,7 +64,7 @@
*/
@Nullable
MediaControllerCompat getControllerForSource(@NonNull List<MediaControllerCompat> controllers,
- @NonNull SimpleMediaSource mediaSource) {
+ @NonNull MediaSource mediaSource) {
return getControllerForPackage(controllers, mediaSource.getPackageName());
}
diff --git a/car-media-common/src/com/android/car/media/common/source/SimpleMediaSource.java b/car-media-common/src/com/android/car/media/common/source/MediaSource.java
similarity index 92%
rename from car-media-common/src/com/android/car/media/common/source/SimpleMediaSource.java
rename to car-media-common/src/com/android/car/media/common/source/MediaSource.java
index 5268135..03275b0 100644
--- a/car-media-common/src/com/android/car/media/common/source/SimpleMediaSource.java
+++ b/car-media-common/src/com/android/car/media/common/source/MediaSource.java
@@ -36,8 +36,6 @@
import android.service.media.MediaBrowserService;
import android.util.Log;
-import com.android.car.media.common.MediaSource;
-
import java.util.HashSet;
import java.util.List;
import java.util.Objects;
@@ -47,8 +45,7 @@
* This represents a source of media content. It provides convenient methods to access media source
* metadata, such as primary color and application name.
*/
-// TODO (keyboardr): Rename to MediaSource once other MediaSource is removed
-public class SimpleMediaSource {
+public class MediaSource {
private static final String TAG = "MediaSource";
/**
@@ -66,9 +63,9 @@
private CharSequence mName;
/**
- * Creates a {@link SimpleMediaSource} for the given application package name
+ * Creates a {@link MediaSource} for the given application package name
*/
- public SimpleMediaSource(@NonNull Context context, @NonNull String packageName) {
+ public MediaSource(@NonNull Context context, @NonNull String packageName) {
mContext = context;
mPackageName = packageName;
mBrowseServiceClassName = getBrowseServiceClassName(packageName);
@@ -76,14 +73,6 @@
}
/**
- * Returns a MediaSource equivalent to the one represented by this instance.
- */
- // TODO (keyboardr): Remove this method once SimpleMediaSource and MediaSource are merged
- public MediaSource toMediaSource() {
- return new MediaSource(mContext, mPackageName);
- }
-
- /**
* @return the classname corresponding to a {@link MediaBrowserService} in the media source, or
* null if the media source doesn't implement {@link MediaBrowserService}. A non-null result
* doesn't imply that this service is accessible. The consumer code should attempt to connect
@@ -236,7 +225,7 @@
public boolean equals(Object o) {
if (this == o) return true;
if (o == null || getClass() != o.getClass()) return false;
- SimpleMediaSource that = (SimpleMediaSource) o;
+ MediaSource that = (MediaSource) o;
return Objects.equals(mPackageName, that.mPackageName)
&& Objects.equals(mBrowseServiceClassName, that.mBrowseServiceClassName);
}
diff --git a/car-media-common/src/com/android/car/media/common/source/MediaSourceColors.java b/car-media-common/src/com/android/car/media/common/source/MediaSourceColors.java
index 1db676a..ebab15d 100644
--- a/car-media-common/src/com/android/car/media/common/source/MediaSourceColors.java
+++ b/car-media-common/src/com/android/car/media/common/source/MediaSourceColors.java
@@ -25,7 +25,9 @@
import androidx.annotation.NonNull;
-/** Contains the colors for a {@link com.android.car.media.common.MediaSource MediaSource} */
+/**
+ * Contains the colors for a {@link MediaSource}
+ */
public class MediaSourceColors {
/**
* Mark used to indicate that we couldn't find a color and the default one should be used
@@ -77,7 +79,7 @@
}
/** Extract colors for (@code mediaSource} and create a MediaSourceColors for it */
- public MediaSourceColors extractColors(@NonNull SimpleMediaSource mediaSource) {
+ public MediaSourceColors extractColors(@NonNull MediaSource mediaSource) {
return extractColors(mediaSource.getPackageName());
}
diff --git a/car-media-common/src/com/android/car/media/common/source/MediaSourceViewModel.java b/car-media-common/src/com/android/car/media/common/source/MediaSourceViewModel.java
index 94de3f3..506d2df 100644
--- a/car-media-common/src/com/android/car/media/common/source/MediaSourceViewModel.java
+++ b/car-media-common/src/com/android/car/media/common/source/MediaSourceViewModel.java
@@ -55,11 +55,11 @@
public class MediaSourceViewModel extends AndroidViewModel {
private static final String TAG = "MediaSourceViewModel";
- private final LiveData<List<SimpleMediaSource>> mMediaSources;
+ private final LiveData<List<MediaSource>> mMediaSources;
private final LiveData<Boolean> mHasMediaSources;
- private final MutableLiveData<SimpleMediaSource> mSelectedMediaSource = new MutableLiveData<>();
+ private final MutableLiveData<MediaSource> mSelectedMediaSource = new MutableLiveData<>();
private final LiveData<MediaBrowserCompat> mConnectedMediaBrowser;
@@ -76,7 +76,7 @@
*/
@VisibleForTesting
interface InputFactory {
- LiveData<List<SimpleMediaSource>> createMediaSources();
+ LiveData<List<MediaSource>> createMediaSources();
LiveData<MediaBrowserState> createMediaBrowserConnector(
@NonNull ComponentName browseService);
@@ -97,7 +97,7 @@
this(application, new InputFactory() {
@Override
- public LiveData<List<SimpleMediaSource>> createMediaSources() {
+ public LiveData<List<MediaSource>> createMediaSources() {
return new MediaSourcesLiveData(application);
}
@@ -180,7 +180,7 @@
* Returns a live list of all MediaSources that can be selected for playback
*/
@NonNull
- public LiveData<List<SimpleMediaSource>> getMediaSources() {
+ public LiveData<List<MediaSource>> getMediaSources() {
return mMediaSources;
}
@@ -195,7 +195,7 @@
/**
* Returns a LiveData that emits the MediaSource that is to be browsed or displayed.
*/
- public LiveData<SimpleMediaSource> getSelectedMediaSource() {
+ public LiveData<MediaSource> getSelectedMediaSource() {
return mSelectedMediaSource;
}
@@ -204,7 +204,7 @@
* connection may be made and provided through {@link #getConnectedMediaBrowser()}.
*/
@UiThread
- public void setSelectedMediaSource(@Nullable SimpleMediaSource mediaSource) {
+ public void setSelectedMediaSource(@Nullable MediaSource mediaSource) {
mSelectedMediaSource.setValue(mediaSource);
}
diff --git a/car-media-common/src/com/android/car/media/common/source/MediaSourcesLiveData.java b/car-media-common/src/com/android/car/media/common/source/MediaSourcesLiveData.java
index 7634f9f..c396e79 100644
--- a/car-media-common/src/com/android/car/media/common/source/MediaSourcesLiveData.java
+++ b/car-media-common/src/com/android/car/media/common/source/MediaSourcesLiveData.java
@@ -39,7 +39,7 @@
* A LiveData that provides access to the list of all possible media sources that can be selected
* to be played.
*/
-class MediaSourcesLiveData extends LiveData<List<SimpleMediaSource>> {
+class MediaSourcesLiveData extends LiveData<List<MediaSource>> {
private static final String TAG = "MediaSourcesLiveData";
private final Context mContext;
@@ -81,9 +81,9 @@
}
private void updateMediaSources() {
- List<SimpleMediaSource> mediaSources = getPackageNames().stream()
+ List<MediaSource> mediaSources = getPackageNames().stream()
.filter(Objects::nonNull)
- .map(packageName -> new SimpleMediaSource(mContext, packageName))
+ .map(packageName -> new MediaSource(mContext, packageName))
.filter(mediaSource -> {
if (mediaSource.getName() == null) {
Log.w(TAG, "Found media source without name: "
diff --git a/car-media-common/tests/robotests/src/com/android/car/media/common/source/MediaSourceViewModelTest.java b/car-media-common/tests/robotests/src/com/android/car/media/common/source/MediaSourceViewModelTest.java
index c283fce..9a54066 100644
--- a/car-media-common/tests/robotests/src/com/android/car/media/common/source/MediaSourceViewModelTest.java
+++ b/car-media-common/tests/robotests/src/com/android/car/media/common/source/MediaSourceViewModelTest.java
@@ -65,7 +65,7 @@
public final TestLifecycleOwner mLifecycleOwner = new TestLifecycleOwner();
@Mock
- public SimpleMediaSource mMediaSource;
+ public MediaSource mMediaSource;
@Mock
public MediaBrowserCompat mMediaBrowser;
@Mock
@@ -75,7 +75,7 @@
@Mock
public MediaControllerCompat mMediaControllerFromSessionManager;
- private final MutableLiveData<List<SimpleMediaSource>> mMediaSources = new MutableLiveData<>();
+ private final MutableLiveData<List<MediaSource>> mMediaSources = new MutableLiveData<>();
private final MutableLiveData<MediaBrowserState> mMediaBrowserState = new MutableLiveData<>();
private final MutableLiveData<List<MediaControllerCompat>> mActiveMediaControllers =
new MutableLiveData<>();
@@ -100,7 +100,7 @@
mViewModel = new MediaSourceViewModel(application, new MediaSourceViewModel.InputFactory() {
@Override
- public LiveData<List<SimpleMediaSource>> createMediaSources() {
+ public LiveData<List<MediaSource>> createMediaSources() {
return mMediaSources;
}
@@ -161,7 +161,7 @@
@Test
public void testGetSelectedMediaSource() {
- CaptureObserver<SimpleMediaSource> observer = new CaptureObserver<>();
+ CaptureObserver<MediaSource> observer = new CaptureObserver<>();
mViewModel.getSelectedMediaSource().observe(mLifecycleOwner, observer);
diff --git a/car-media-common/tests/robotests/src/com/android/car/media/common/source/MediaSourcesLiveDataTest.java b/car-media-common/tests/robotests/src/com/android/car/media/common/source/MediaSourcesLiveDataTest.java
index 01f4190..95d8256 100644
--- a/car-media-common/tests/robotests/src/com/android/car/media/common/source/MediaSourcesLiveDataTest.java
+++ b/car-media-common/tests/robotests/src/com/android/car/media/common/source/MediaSourcesLiveDataTest.java
@@ -106,15 +106,15 @@
@Test
public void testGetAppsOnActive() {
- CaptureObserver<List<SimpleMediaSource>> observer = new CaptureObserver<>();
+ CaptureObserver<List<MediaSource>> observer = new CaptureObserver<>();
MediaSourcesLiveData liveData = new MediaSourcesLiveData(application);
liveData.observe(mLifecycleOwner, observer);
assertThat(observer.hasBeenNotified()).isTrue();
- List<SimpleMediaSource> observedValue = observer.getObservedValue();
+ List<MediaSource> observedValue = observer.getObservedValue();
assertThat(observedValue).isNotNull();
assertThat(
- observedValue.stream().map(SimpleMediaSource::getPackageName)
+ observedValue.stream().map(MediaSource::getPackageName)
.collect(Collectors.toList()))
.containsExactly(TEST_ACTIVITY_PACKAGE_1, TEST_SERVICE_PACKAGE_1,
TEST_SERVICE_PACKAGE_WITH_METADATA);
@@ -122,7 +122,7 @@
@Test
public void testGetAppsOnPackageAdded() {
- CaptureObserver<List<SimpleMediaSource>> observer = new CaptureObserver<>();
+ CaptureObserver<List<MediaSource>> observer = new CaptureObserver<>();
MediaSourcesLiveData liveData = new MediaSourcesLiveData(application);
liveData.observe(mLifecycleOwner, observer);
observer.reset();
@@ -141,10 +141,10 @@
packageAdded));
assertThat(observer.hasBeenNotified()).isTrue();
- List<SimpleMediaSource> observedValue = observer.getObservedValue();
+ List<MediaSource> observedValue = observer.getObservedValue();
assertThat(observedValue).isNotNull();
assertThat(
- observedValue.stream().map(SimpleMediaSource::getPackageName)
+ observedValue.stream().map(MediaSource::getPackageName)
.collect(Collectors.toList()))
.containsExactly(TEST_ACTIVITY_PACKAGE_1, TEST_ACTIVITY_PACKAGE_2,
TEST_SERVICE_PACKAGE_1, TEST_SERVICE_PACKAGE_2,
@@ -153,7 +153,7 @@
@Test
public void testGetAppsOnPackageRemoved() {
- CaptureObserver<List<SimpleMediaSource>> observer = new CaptureObserver<>();
+ CaptureObserver<List<MediaSource>> observer = new CaptureObserver<>();
MediaSourcesLiveData liveData = new MediaSourcesLiveData(application);
liveData.observe(mLifecycleOwner, observer);
observer.reset();
@@ -171,10 +171,10 @@
broadcastReceiver.onReceive(application, packageRemoved));
assertThat(observer.hasBeenNotified()).isTrue();
- List<SimpleMediaSource> observedValue = observer.getObservedValue();
+ List<MediaSource> observedValue = observer.getObservedValue();
assertThat(observedValue).isNotNull();
assertThat(
- observedValue.stream().map(SimpleMediaSource::getPackageName)
+ observedValue.stream().map(MediaSource::getPackageName)
.collect(Collectors.toList()))
.containsExactly(TEST_ACTIVITY_PACKAGE_1, TEST_SERVICE_PACKAGE_WITH_METADATA);
}