aboutsummaryrefslogtreecommitdiff
path: root/mediarouter
diff options
context:
space:
mode:
authorTreehugger Robot <treehugger-gerrit@google.com>2018-08-30 06:54:41 +0000
committerGerrit Code Review <noreply-gerritcodereview@google.com>2018-08-30 06:54:41 +0000
commitfa5ef35a700fd3ab2df2b6514572fe71cb66f78d (patch)
tree4272051135cb4211eae9991dcacc1c584209555b /mediarouter
parent60d4217c3d479d7e6c838971038fdf66fe03ee6e (diff)
parent07f617e626b33f51f7b79fd28b5e90d2619d03b0 (diff)
downloadsupport-fa5ef35a700fd3ab2df2b6514572fe71cb66f78d.tar.gz
Merge "MediaRouter: Support dynmaic group routes." into androidx-master-dev
Diffstat (limited to 'mediarouter')
-rw-r--r--mediarouter/api/current.txt4
-rw-r--r--mediarouter/src/main/java/androidx/mediarouter/app/MediaRouteButton.java3
-rw-r--r--mediarouter/src/main/java/androidx/mediarouter/media/MainHandlerExecutor.java48
-rw-r--r--mediarouter/src/main/java/androidx/mediarouter/media/MediaRouteProvider.java5
-rw-r--r--mediarouter/src/main/java/androidx/mediarouter/media/MediaRouter.java411
5 files changed, 309 insertions, 162 deletions
diff --git a/mediarouter/api/current.txt b/mediarouter/api/current.txt
index 994a33e33e2..8648a95431f 100644
--- a/mediarouter/api/current.txt
+++ b/mediarouter/api/current.txt
@@ -271,7 +271,7 @@ package androidx.mediarouter.media {
}
public static abstract interface MediaRouteProvider.DynamicGroupRouteController.OnDynamicRoutesChangedListener {
- method public abstract void onRoutesChanged(java.util.Collection<androidx.mediarouter.media.MediaRouteProvider.DynamicGroupRouteController.DynamicRouteDescriptor>);
+ method public abstract void onRoutesChanged(androidx.mediarouter.media.MediaRouteProvider.DynamicGroupRouteController, java.util.Collection<androidx.mediarouter.media.MediaRouteProvider.DynamicGroupRouteController.DynamicRouteDescriptor>);
}
public static final class MediaRouteProvider.ProviderMetadata {
@@ -419,7 +419,7 @@ package androidx.mediarouter.media {
method public int getVolumeHandling();
method public int getVolumeMax();
method public boolean isBluetooth();
- method public boolean isConnecting();
+ method public deprecated boolean isConnecting();
method public boolean isDefault();
method public boolean isDeviceSpeaker();
method public boolean isEnabled();
diff --git a/mediarouter/src/main/java/androidx/mediarouter/app/MediaRouteButton.java b/mediarouter/src/main/java/androidx/mediarouter/app/MediaRouteButton.java
index cc40f72ad06..38d472df643 100644
--- a/mediarouter/src/main/java/androidx/mediarouter/app/MediaRouteButton.java
+++ b/mediarouter/src/main/java/androidx/mediarouter/app/MediaRouteButton.java
@@ -488,7 +488,8 @@ public class MediaRouteButton extends View {
void refreshRoute() {
final MediaRouter.RouteInfo route = mRouter.getSelectedRoute();
final boolean isRemote = !route.isDefaultOrBluetooth() && route.matchesSelector(mSelector);
- final boolean isConnecting = isRemote && route.isConnecting();
+ final boolean isConnecting = isRemote
+ && route.getConnectionState() == MediaRouter.RouteInfo.CONNECTION_STATE_CONNECTING;
boolean needsRefresh = false;
if (mRemoteActive != isRemote) {
mRemoteActive = isRemote;
diff --git a/mediarouter/src/main/java/androidx/mediarouter/media/MainHandlerExecutor.java b/mediarouter/src/main/java/androidx/mediarouter/media/MainHandlerExecutor.java
new file mode 100644
index 00000000000..c5d658ad36a
--- /dev/null
+++ b/mediarouter/src/main/java/androidx/mediarouter/media/MainHandlerExecutor.java
@@ -0,0 +1,48 @@
+/*
+ * 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 androidx.mediarouter.media;
+
+import android.content.Context;
+import android.os.Build;
+import android.os.Handler;
+
+import androidx.annotation.NonNull;
+
+import java.util.concurrent.Executor;
+import java.util.concurrent.RejectedExecutionException;
+
+class MainHandlerExecutor implements Executor {
+ static Executor getExecutor(Context context) {
+ if (Build.VERSION.SDK_INT >= 28) {
+ return context.getMainExecutor();
+ }
+ return new MainHandlerExecutor(new Handler(context.getMainLooper()));
+ }
+
+ private final Handler mHandler;
+
+ MainHandlerExecutor(@NonNull Handler handler) {
+ mHandler = handler;
+ }
+
+ @Override
+ public void execute(Runnable command) {
+ if (!mHandler.post(command)) {
+ throw new RejectedExecutionException(mHandler + " is shutting down");
+ }
+ }
+}
diff --git a/mediarouter/src/main/java/androidx/mediarouter/media/MediaRouteProvider.java b/mediarouter/src/main/java/androidx/mediarouter/media/MediaRouteProvider.java
index 31ab37349d2..6a5253c2a39 100644
--- a/mediarouter/src/main/java/androidx/mediarouter/media/MediaRouteProvider.java
+++ b/mediarouter/src/main/java/androidx/mediarouter/media/MediaRouteProvider.java
@@ -516,11 +516,14 @@ public abstract class MediaRouteProvider {
*/
public interface OnDynamicRoutesChangedListener {
/**
+ * @param controller the {@link DynamicGroupRouteController} which keeps this listener.
* @param routes the collection of routes contains selected routes
* (can be unselectable or not)
* and unselected routes (can be groupable or transferable or not).
*/
- void onRoutesChanged(Collection<DynamicRouteDescriptor> routes);
+ void onRoutesChanged(
+ DynamicGroupRouteController controller,
+ Collection<DynamicRouteDescriptor> routes);
}
/**
diff --git a/mediarouter/src/main/java/androidx/mediarouter/media/MediaRouter.java b/mediarouter/src/main/java/androidx/mediarouter/media/MediaRouter.java
index 1ae14108950..0d4c981dbb8 100644
--- a/mediarouter/src/main/java/androidx/mediarouter/media/MediaRouter.java
+++ b/mediarouter/src/main/java/androidx/mediarouter/media/MediaRouter.java
@@ -50,6 +50,9 @@ import androidx.media2.MediaPlayerConnector;
import androidx.media2.MediaPlaylistAgent;
import androidx.media2.MediaSession2;
import androidx.mediarouter.app.MediaRouteDiscoveryFragment;
+import androidx.mediarouter.media.MediaRouteProvider.DynamicGroupRouteController;
+import androidx.mediarouter.media.MediaRouteProvider.DynamicGroupRouteController
+ .DynamicRouteDescriptor;
import androidx.mediarouter.media.MediaRouteProvider.ProviderMetadata;
import androidx.mediarouter.media.MediaRouteProvider.RouteController;
@@ -57,6 +60,7 @@ import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.ref.WeakReference;
import java.util.ArrayList;
+import java.util.Collection;
import java.util.Collections;
import java.util.HashMap;
import java.util.HashSet;
@@ -301,6 +305,11 @@ public final class MediaRouter {
return sGlobal.getRoute(uniqueId);
}
+ @Nullable RouteInfo getRoute(String uniqueId) {
+ checkCallingThread();
+ return sGlobal.getRoute(uniqueId);
+ }
+
/**
* Gets information about the {@link MediaRouter.ProviderInfo route providers}
* currently known to this media router.
@@ -839,13 +848,12 @@ public final class MediaRouter {
private String mDescription;
private Uri mIconUri;
boolean mEnabled;
- private boolean mConnecting;
- private int mConnectionState;
+ private @ConnectionState int mConnectionState;
private boolean mCanDisconnect;
private final ArrayList<IntentFilter> mControlFilters = new ArrayList<>();
private int mPlaybackType;
private int mPlaybackStream;
- private int mDeviceType;
+ private @DeviceType int mDeviceType;
private int mVolumeHandling;
private int mVolume;
private int mVolumeMax;
@@ -1061,9 +1069,11 @@ public final class MediaRouter {
* yet ready for use.
*
* @return True if this route is in the process of connecting.
+ * @deprecated use {@link #getConnectionState} instead.
*/
+ @Deprecated
public boolean isConnecting() {
- return mConnecting;
+ return mConnectionState == CONNECTION_STATE_CONNECTING;
}
/**
@@ -1512,7 +1522,6 @@ public final class MediaRouter {
+ ", description=" + mDescription
+ ", iconUri=" + mIconUri
+ ", enabled=" + mEnabled
- + ", connecting=" + mConnecting
+ ", connectionState=" + mConnectionState
+ ", canDisconnect=" + mCanDisconnect
+ ", playbackType=" + mPlaybackType
@@ -1556,10 +1565,6 @@ public final class MediaRouter {
mEnabled = descriptor.isEnabled();
changes |= CHANGE_GENERAL;
}
- if (mConnecting != descriptor.isConnecting()) {
- mConnecting = descriptor.isConnecting();
- changes |= CHANGE_GENERAL;
- }
if (mConnectionState != descriptor.getConnectionState()) {
mConnectionState = descriptor.getConnectionState();
changes |= CHANGE_GENERAL;
@@ -1631,30 +1636,13 @@ public final class MediaRouter {
*/
@RestrictTo(LIBRARY_GROUP)
public static class RouteGroup extends RouteInfo {
- private List<RouteInfo> mRoutes = new ArrayList<>();
+ protected List<RouteInfo> mRoutes = new ArrayList<>();
RouteGroup(ProviderInfo provider, String descriptorId, String uniqueId) {
super(provider, descriptorId, uniqueId);
}
/**
- * @return The number of routes in this group
- */
- public int getRouteCount() {
- return mRoutes.size();
- }
-
- /**
- * Returns the route in this group at the specified index
- *
- * @param index Index to fetch
- * @return The route at index
- */
- public RouteInfo getRouteAt(int index) {
- return mRoutes.get(index);
- }
-
- /**
* Returns the routes in this group
*
* @return The list of the routes in this group
@@ -1710,6 +1698,78 @@ public final class MediaRouter {
}
/**
+ * Information about a route that consists of multiple other routes in a group.
+ * @hide
+ */
+ @RestrictTo(LIBRARY_GROUP)
+ public static class DynamicGroupInfo extends RouteGroup {
+ private List<String> mMemberRoutes = new ArrayList<>();
+ private List<String> mUnselectableRoutes = new ArrayList<>();
+ private List<String> mGroupableRoutes = new ArrayList<>();
+ private List<String> mTransferableRoutes = new ArrayList<>();
+
+ DynamicGroupInfo(ProviderInfo provider, String descriptorId, String uniqueId) {
+ super(provider, descriptorId, uniqueId);
+ }
+
+ void updateDescriptors(
+ Collection<DynamicRouteDescriptor> dynamicDescriptors) {
+ mMemberRoutes.clear();
+ mUnselectableRoutes.clear();
+ mGroupableRoutes.clear();
+ mTransferableRoutes.clear();
+
+ for (DynamicRouteDescriptor dynamicDescriptor :
+ dynamicDescriptors) {
+ if (dynamicDescriptor.isGroupable()) {
+ RouteInfo route = findRouteByDynamicRouteDescriptor(dynamicDescriptor);
+ if (route != null) {
+ mGroupableRoutes.add(route.getId());
+ }
+ }
+ if (dynamicDescriptor.isTransferable()) {
+ RouteInfo route = findRouteByDynamicRouteDescriptor(dynamicDescriptor);
+ if (route != null) {
+ mTransferableRoutes.add(route.getId());
+ }
+ }
+ if ((dynamicDescriptor.getSelectionState() == DynamicRouteDescriptor.SELECTING)
+ || (dynamicDescriptor.getSelectionState()
+ == DynamicRouteDescriptor.SELECTED)) {
+ RouteInfo route = findRouteByDynamicRouteDescriptor(dynamicDescriptor);
+ if (route != null) {
+ mMemberRoutes.add(route.getId());
+ if (dynamicDescriptor.isUnselectable()) {
+ mUnselectableRoutes.add(route.getId());
+ }
+ }
+ }
+ }
+ }
+
+ public List<String> getMemberRoutes() {
+ return mMemberRoutes;
+ }
+
+ public List<String> getUnselectableRoutes() {
+ return mUnselectableRoutes;
+ }
+
+ public List<String> getGroupableRoutes() {
+ return mGroupableRoutes;
+ }
+
+ public List<String> getTransferableRoutes() {
+ return mTransferableRoutes;
+ }
+
+ RouteInfo findRouteByDynamicRouteDescriptor(DynamicRouteDescriptor dynamicDescriptor) {
+ String descriptorId = dynamicDescriptor.getRouteDescriptor().getId();
+ return getProvider().findRouteByDescriptorId(descriptorId);
+ }
+ }
+
+ /**
* Provides information about a media route provider.
* <p>
* This object may be used to determine which media route provider has
@@ -1783,7 +1843,7 @@ public final class MediaRouter {
return false;
}
- int findRouteByDescriptorId(String id) {
+ int findRouteIndexByDescriptorId(String id) {
final int count = mRoutes.size();
for (int i = 0; i < count; i++) {
if (mRoutes.get(i).mDescriptorId.equals(id)) {
@@ -1793,6 +1853,20 @@ public final class MediaRouter {
return -1;
}
+ RouteInfo findRouteByDescriptorId(String id) {
+ final int count = mRoutes.size();
+ for (int i = 0; i < count; i++) {
+ if (mRoutes.get(i).mDescriptorId.equals(id)) {
+ return mRoutes.get(i);
+ }
+ }
+ return null;
+ }
+
+ boolean supportsDynamicGroup() {
+ return mDescriptor != null && mDescriptor.supportsDynamicGroupRoute();
+ }
+
@Override
public String toString() {
return "MediaRouter.RouteProviderInfo{ packageName=" + getPackageName()
@@ -2006,8 +2080,8 @@ public final class MediaRouter {
private RouteInfo mDefaultRoute;
private RouteInfo mBluetoothRoute;
RouteInfo mSelectedRoute;
- private RouteController mSelectedRouteController;
- // A map from route descriptor ID to RouteController for the member routes in the currently
+ RouteController mSelectedRouteController;
+ // A map from unique route ID to RouteController for the member routes in the currently
// selected route group.
private final Map<String, RouteController> mRouteControllerMap = new HashMap<>();
private MediaRouteDiscoveryRequest mDiscoveryRequest;
@@ -2102,7 +2176,7 @@ public final class MediaRouter {
if (route == mSelectedRoute && mSelectedRouteController != null) {
mSelectedRouteController.onSetVolume(volume);
} else if (!mRouteControllerMap.isEmpty()) {
- RouteController controller = mRouteControllerMap.get(route.mDescriptorId);
+ RouteController controller = mRouteControllerMap.get(route.mUniqueId);
if (controller != null) {
controller.onSetVolume(volume);
}
@@ -2267,8 +2341,7 @@ public final class MediaRouter {
@Override
public void addProvider(MediaRouteProvider providerInstance) {
- int index = findProviderInfo(providerInstance);
- if (index < 0) {
+ if (findProviderInfo(providerInstance) == null) {
// 1. Add the provider to the list.
ProviderInfo provider = new ProviderInfo(providerInstance);
mProviders.add(provider);
@@ -2287,167 +2360,158 @@ public final class MediaRouter {
@Override
public void removeProvider(MediaRouteProvider providerInstance) {
- int index = findProviderInfo(providerInstance);
- if (index >= 0) {
+ ProviderInfo provider = findProviderInfo(providerInstance);
+ if (provider != null) {
// 1. Unregister the provider callback.
providerInstance.setCallback(null);
// 2. Clear the discovery request.
providerInstance.setDiscoveryRequest(null);
// 3. Delete the provider's contents.
- ProviderInfo provider = mProviders.get(index);
updateProviderContents(provider, null);
// 4. Remove the provider from the list.
if (DEBUG) {
Log.d(TAG, "Provider removed: " + provider);
}
mCallbackHandler.post(CallbackHandler.MSG_PROVIDER_REMOVED, provider);
- mProviders.remove(index);
+ mProviders.remove(provider);
}
}
void updateProviderDescriptor(MediaRouteProvider providerInstance,
MediaRouteProviderDescriptor descriptor) {
- int index = findProviderInfo(providerInstance);
- if (index >= 0) {
+ ProviderInfo provider = findProviderInfo(providerInstance);
+ if (provider != null) {
// Update the provider's contents.
- ProviderInfo provider = mProviders.get(index);
updateProviderContents(provider, descriptor);
}
}
- private int findProviderInfo(MediaRouteProvider providerInstance) {
+ private ProviderInfo findProviderInfo(MediaRouteProvider providerInstance) {
final int count = mProviders.size();
for (int i = 0; i < count; i++) {
if (mProviders.get(i).mProviderInstance == providerInstance) {
- return i;
+ return mProviders.get(i);
}
}
- return -1;
+ return null;
}
private void updateProviderContents(ProviderInfo provider,
MediaRouteProviderDescriptor providerDescriptor) {
- if (provider.updateDescriptor(providerDescriptor)) {
- // Update all existing routes and reorder them to match
- // the order of their descriptors.
- int targetIndex = 0;
- boolean selectedRouteDescriptorChanged = false;
- if (providerDescriptor != null) {
- if (providerDescriptor.isValid()) {
- final List<MediaRouteDescriptor> routeDescriptors =
- providerDescriptor.getRoutes();
- final int routeCount = routeDescriptors.size();
- // Updating route group's contents requires all member routes' information.
- // Add the groups to the lists and update them later.
- List<Pair<RouteInfo, MediaRouteDescriptor>> addedGroups = new ArrayList<>();
- List<Pair<RouteInfo, MediaRouteDescriptor>> updatedGroups =
- new ArrayList<>();
- for (int i = 0; i < routeCount; i++) {
- final MediaRouteDescriptor routeDescriptor = routeDescriptors.get(i);
- final String id = routeDescriptor.getId();
- final int sourceIndex = provider.findRouteByDescriptorId(id);
- boolean isGroup = routeDescriptor.getGroupMemberIds() != null;
- if (sourceIndex < 0) {
- // 1. Add the route to the list.
- String uniqueId = assignRouteUniqueId(provider, id);
- RouteInfo route = isGroup ? new RouteGroup(provider, id, uniqueId) :
- new RouteInfo(provider, id, uniqueId);
- provider.mRoutes.add(targetIndex++, route);
- mRoutes.add(route);
- // 2. Create the route's contents.
- if (isGroup) {
- addedGroups.add(new Pair<>(route, routeDescriptor));
- } else {
- route.maybeUpdateDescriptor(routeDescriptor);
- // 3. Notify clients about addition.
- if (DEBUG) {
- Log.d(TAG, "Route added: " + route);
- }
- mCallbackHandler.post(CallbackHandler.MSG_ROUTE_ADDED, route);
- }
-
- } else if (sourceIndex < targetIndex) {
- Log.w(TAG, "Ignoring route descriptor with duplicate id: "
- + routeDescriptor);
- } else {
- RouteInfo route = provider.mRoutes.get(sourceIndex);
- // 1. Replace route if a group route becomes a normal route
- // or vice versa.
- if ((route instanceof RouteGroup) != isGroup) {
- route = isGroup ? new RouteGroup(provider, id, route.getId()) :
- new RouteInfo(provider, id, route.getId());
- provider.mRoutes.set(sourceIndex, route);
- }
- // 2. Reorder the route within the list.
- Collections.swap(provider.mRoutes,
- sourceIndex, targetIndex++);
- // 3. Update the route's contents.
- if (route instanceof RouteGroup) {
- updatedGroups.add(new Pair<>(route, routeDescriptor));
- } else {
- // 4. Notify clients about changes.
- if (updateRouteDescriptorAndNotify(route, routeDescriptor)
- != 0) {
- if (route == mSelectedRoute) {
- selectedRouteDescriptorChanged = true;
- }
- }
- }
- }
- }
- // Update the new and/or existing groups.
- for (Pair<RouteInfo, MediaRouteDescriptor> pair : addedGroups) {
- RouteInfo route = pair.first;
- route.maybeUpdateDescriptor(pair.second);
+ if (!provider.updateDescriptor(providerDescriptor)) {
+ // Nothing to update.
+ return;
+ }
+ // Update all existing routes and reorder them to match
+ // the order of their descriptors.
+ int targetIndex = 0;
+ boolean selectedRouteDescriptorChanged = false;
+ if (providerDescriptor != null && providerDescriptor.isValid()) {
+ final List<MediaRouteDescriptor> routeDescriptors = providerDescriptor.getRoutes();
+ // Updating route group's contents requires all member routes' information.
+ // Add the groups to the lists and update them later.
+ List<Pair<RouteInfo, MediaRouteDescriptor>> addedGroups = new ArrayList<>();
+ List<Pair<RouteInfo, MediaRouteDescriptor>> updatedGroups = new ArrayList<>();
+ for (MediaRouteDescriptor routeDescriptor : routeDescriptors) {
+ final String id = routeDescriptor.getId();
+ final int sourceIndex = provider.findRouteIndexByDescriptorId(id);
+ boolean isGroup = routeDescriptor.getGroupMemberIds() != null;
+ if (sourceIndex < 0) {
+ // 1. Add the route to the list.
+ String uniqueId = assignRouteUniqueId(provider, id);
+ RouteInfo route = isGroup ? new RouteGroup(provider, id, uniqueId) :
+ new RouteInfo(provider, id, uniqueId);
+ provider.mRoutes.add(targetIndex++, route);
+ mRoutes.add(route);
+ // 2. Create the route's contents.
+ if (isGroup) {
+ addedGroups.add(new Pair<>(route, routeDescriptor));
+ } else {
+ route.maybeUpdateDescriptor(routeDescriptor);
+ // 3. Notify clients about addition.
if (DEBUG) {
Log.d(TAG, "Route added: " + route);
}
mCallbackHandler.post(CallbackHandler.MSG_ROUTE_ADDED, route);
}
- for (Pair<RouteInfo, MediaRouteDescriptor> pair : updatedGroups) {
- RouteInfo route = pair.first;
- if (updateRouteDescriptorAndNotify(route, pair.second) != 0) {
+ } else if (sourceIndex < targetIndex) {
+ Log.w(TAG, "Ignoring route descriptor with duplicate id: "
+ + routeDescriptor);
+ } else {
+ RouteInfo route = provider.mRoutes.get(sourceIndex);
+ // 1. Replace route if a group route becomes a normal route
+ // or vice versa.
+ if ((route instanceof RouteGroup) != isGroup) {
+ route = isGroup ? new RouteGroup(provider, id, route.getId()) :
+ new RouteInfo(provider, id, route.getId());
+ provider.mRoutes.set(sourceIndex, route);
+ }
+ // 2. Reorder the route within the list.
+ Collections.swap(provider.mRoutes, sourceIndex, targetIndex++);
+ // 3. Update the route's contents.
+ if (route instanceof RouteGroup) {
+ updatedGroups.add(new Pair<>(route, routeDescriptor));
+ } else {
+ // 4. Notify clients about changes.
+ if (updateRouteDescriptorAndNotify(route, routeDescriptor) != 0) {
if (route == mSelectedRoute) {
selectedRouteDescriptorChanged = true;
}
}
}
- } else {
- Log.w(TAG, "Ignoring invalid provider descriptor: " + providerDescriptor);
}
}
-
- // Dispose all remaining routes that do not have matching descriptors.
- for (int i = provider.mRoutes.size() - 1; i >= targetIndex; i--) {
- // 1. Delete the route's contents.
- RouteInfo route = provider.mRoutes.get(i);
- route.maybeUpdateDescriptor(null);
- // 2. Remove the route from the list.
- mRoutes.remove(route);
- }
-
- // Update the selected route if needed.
- updateSelectedRouteIfNeeded(selectedRouteDescriptorChanged);
-
- // Now notify clients about routes that were removed.
- // We do this after updating the selected route to ensure
- // that the framework media router observes the new route
- // selection before the removal since removing the currently
- // selected route may have side-effects.
- for (int i = provider.mRoutes.size() - 1; i >= targetIndex; i--) {
- RouteInfo route = provider.mRoutes.remove(i);
+ // Update the new and/or existing groups.
+ for (Pair<RouteInfo, MediaRouteDescriptor> pair : addedGroups) {
+ RouteInfo route = pair.first;
+ route.maybeUpdateDescriptor(pair.second);
if (DEBUG) {
- Log.d(TAG, "Route removed: " + route);
+ Log.d(TAG, "Route added: " + route);
+ }
+ mCallbackHandler.post(CallbackHandler.MSG_ROUTE_ADDED, route);
+ }
+ for (Pair<RouteInfo, MediaRouteDescriptor> pair : updatedGroups) {
+ RouteInfo route = pair.first;
+ if (updateRouteDescriptorAndNotify(route, pair.second) != 0) {
+ if (route == mSelectedRoute) {
+ selectedRouteDescriptorChanged = true;
+ }
}
- mCallbackHandler.post(CallbackHandler.MSG_ROUTE_REMOVED, route);
}
+ } else {
+ Log.w(TAG, "Ignoring invalid provider descriptor: " + providerDescriptor);
+ }
+
+ // Dispose all remaining routes that do not have matching descriptors.
+ for (int i = provider.mRoutes.size() - 1; i >= targetIndex; i--) {
+ // 1. Delete the route's contents.
+ RouteInfo route = provider.mRoutes.get(i);
+ route.maybeUpdateDescriptor(null);
+ // 2. Remove the route from the list.
+ mRoutes.remove(route);
+ }
- // Notify provider changed.
+ // Update the selected route if needed.
+ updateSelectedRouteIfNeeded(selectedRouteDescriptorChanged);
+
+ // Now notify clients about routes that were removed.
+ // We do this after updating the selected route to ensure
+ // that the framework media router observes the new route
+ // selection before the removal since removing the currently
+ // selected route may have side-effects.
+ for (int i = provider.mRoutes.size() - 1; i >= targetIndex; i--) {
+ RouteInfo route = provider.mRoutes.remove(i);
if (DEBUG) {
- Log.d(TAG, "Provider changed: " + provider);
+ Log.d(TAG, "Route removed: " + route);
}
- mCallbackHandler.post(CallbackHandler.MSG_PROVIDER_CHANGED, provider);
+ mCallbackHandler.post(CallbackHandler.MSG_ROUTE_REMOVED, route);
+ }
+
+ // Notify provider changed.
+ if (DEBUG) {
+ Log.d(TAG, "Provider changed: " + provider);
}
+ mCallbackHandler.post(CallbackHandler.MSG_PROVIDER_CHANGED, provider);
}
private int updateRouteDescriptorAndNotify(RouteInfo route,
@@ -2480,7 +2544,7 @@ public final class MediaRouter {
return changes;
}
- private String assignRouteUniqueId(ProviderInfo provider, String routeDescriptorId) {
+ String assignRouteUniqueId(ProviderInfo provider, String routeDescriptorId) {
// Although route descriptor ids are unique within a provider, it's
// possible for there to be two providers with the same package name.
// Therefore we must dedupe the composite id.
@@ -2579,12 +2643,12 @@ public final class MediaRouter {
}
// Select route controllers for the added routes.
for (RouteInfo route : routes) {
- if (!mRouteControllerMap.containsKey(route.mDescriptorId)) {
+ if (!mRouteControllerMap.containsKey(route.mUniqueId)) {
RouteController controller = route.getProviderInstance()
.onCreateRouteController(
route.mDescriptorId, mSelectedRoute.mDescriptorId);
controller.onSelect();
- mRouteControllerMap.put(route.mDescriptorId, controller);
+ mRouteControllerMap.put(route.mUniqueId, controller);
}
}
}
@@ -2666,9 +2730,26 @@ public final class MediaRouter {
}
}
- mSelectedRoute = route;
- mSelectedRouteController = route.getProviderInstance().onCreateRouteController(
- route.mDescriptorId);
+ if (route.getProvider().supportsDynamicGroup()) {
+ // MRP will create a new dynamic group route with initially selected route.
+ MediaRouteProvider.DynamicGroupRouteController controller =
+ route.getProviderInstance().onCreateDynamicGroupRouteController(
+ route.mDescriptorId);
+ // Controller has the new route's id which MediaRouteDescriptor might not be
+ // published yet.
+ String uniqueId = assignRouteUniqueId(
+ route.getProvider(), controller.getDynamicGroupRouteId());
+ controller.setOnDynamicRoutesChangedListener(
+ MainHandlerExecutor.getExecutor(mApplicationContext),
+ mDynamicRoutesListener);
+ mSelectedRouteController = controller;
+ mSelectedRoute = new DynamicGroupInfo(mSelectedRoute.getProvider(),
+ route.mDescriptorId, uniqueId);
+ } else {
+ mSelectedRouteController = route.getProviderInstance().onCreateRouteController(
+ route.mDescriptorId);
+ mSelectedRoute = route;
+ }
if (mSelectedRouteController != null) {
mSelectedRouteController.onSelect();
}
@@ -2685,7 +2766,7 @@ public final class MediaRouter {
r.getProviderInstance().onCreateRouteController(
r.mDescriptorId, mSelectedRoute.mDescriptorId);
controller.onSelect();
- mRouteControllerMap.put(r.mDescriptorId, controller);
+ mRouteControllerMap.put(r.mUniqueId, controller);
}
}
@@ -2693,16 +2774,30 @@ public final class MediaRouter {
}
}
+ DynamicGroupRouteController.OnDynamicRoutesChangedListener mDynamicRoutesListener =
+ new DynamicGroupRouteController.OnDynamicRoutesChangedListener() {
+ @Override
+ public void onRoutesChanged(
+ DynamicGroupRouteController controller,
+ Collection<DynamicGroupRouteController.DynamicRouteDescriptor> routes) {
+ if (controller == mSelectedRouteController
+ && controller.getDynamicGroupRouteId().equals(
+ mSelectedRoute.mDescriptorId)
+ && mSelectedRoute instanceof DynamicGroupInfo) {
+ ((DynamicGroupInfo) mSelectedRoute).updateDescriptors(routes);
+ }
+ }
+ };
+
@Override
public void onSystemRouteSelectedByDescriptorId(String id) {
// System route is selected, do not sync the route we selected before.
mCallbackHandler.removeMessages(CallbackHandler.MSG_ROUTE_SELECTED);
- int providerIndex = findProviderInfo(mSystemProvider);
- if (providerIndex >= 0) {
- ProviderInfo provider = mProviders.get(providerIndex);
- int routeIndex = provider.findRouteByDescriptorId(id);
- if (routeIndex >= 0) {
- provider.mRoutes.get(routeIndex).select();
+ ProviderInfo provider = findProviderInfo(mSystemProvider);
+ if (provider != null) {
+ RouteInfo route = provider.findRouteByDescriptorId(id);
+ if (route != null) {
+ route.select();
}
}
}