MediaRouter.java revision c21f57ed68b81a77167f1df000b0e272e1598bc0
1c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown/* 2c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown * Copyright (C) 2013 The Android Open Source Project 3c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown * 4c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown * Licensed under the Apache License, Version 2.0 (the "License"); 5c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown * you may not use this file except in compliance with the License. 6c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown * You may obtain a copy of the License at 7c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown * 8c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown * http://www.apache.org/licenses/LICENSE-2.0 9c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown * 10c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown * Unless required by applicable law or agreed to in writing, software 11c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown * distributed under the License is distributed on an "AS IS" BASIS, 12c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown * See the License for the specific language governing permissions and 14c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown * limitations under the License. 15c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown */ 16c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown 17c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brownpackage android.support.v4.media; 18c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown 19c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brownimport android.content.ContentResolver; 20c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brownimport android.content.Context; 21c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brownimport android.content.Intent; 22c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brownimport android.content.IntentFilter; 23c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brownimport android.graphics.drawable.Drawable; 24c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brownimport android.os.Bundle; 25c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brownimport android.os.Handler; 26c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brownimport android.os.Looper; 27c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brownimport android.os.Message; 28c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brownimport android.support.v4.hardware.display.DisplayManagerCompat; 29c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brownimport android.support.v4.media.MediaRouteProvider.RouteDescriptor; 30c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brownimport android.support.v4.media.MediaRouteProvider.RouteProviderDescriptor; 31c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brownimport android.util.Log; 32c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brownimport android.view.Display; 33c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown 34c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brownimport java.util.ArrayList; 35c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brownimport java.util.Collections; 36c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brownimport java.util.List; 37c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brownimport java.util.WeakHashMap; 38c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brownimport java.util.concurrent.CopyOnWriteArrayList; 39c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown 40c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown/** 41c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown * MediaRouter allows applications to control the routing of media channels 42c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown * and streams from the current device to external speakers and destination devices. 43c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown * <p> 44c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown * A MediaRouter instance is retrieved through {@link #getInstance}. Applications 45c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown * can query the media router about the currently selected route and its capabilities 46c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown * to determine how to send content to the route's destination. Applications can 47c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown * also {@link RouteInfo#sendControlRequest send control requests} to the route 48c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown * to ask the route's destination to perform certain remote control functions 49c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown * such as playing media. 50c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown * </p><p> 51c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown * See also {@link MediaRouteProvider} for information on how an application 52c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown * can publish new media routes to the media router. 53c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown * </p><p> 54c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown * The media router API is not thread-safe; all interactions with it must be 55c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown * done from the main thread of the process. 56c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown * </p> 57c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown */ 58c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brownpublic final class MediaRouter { 59c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown private static final String TAG = "MediaRouter"; 60c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown 61c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown // Maintains global media router state for the process. 62c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown // This field is initialized in MediaRouter.getInstance() before any 63c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown // MediaRouter objects are instantiated so it is guaranteed to be 64c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown // valid whenever any instance method is invoked. 65c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown static GlobalMediaRouter sGlobal; 66c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown 67c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown // Context-bound state of the media router. 68c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown final Context mContext; 69c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown final CopyOnWriteArrayList<Callback> mCallbacks = new CopyOnWriteArrayList<Callback>(); 70c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown 71c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown MediaRouter(Context context) { 72c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown mContext = context; 73c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown } 74c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown 75c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown /** 76c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown * Gets an instance of the media router service from the context. 77c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown */ 78c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown public static MediaRouter getInstance(Context context) { 79c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown if (context == null) { 80c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown throw new IllegalArgumentException("context must not be null"); 81c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown } 82c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown checkCallingThread(); 83c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown 84c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown if (sGlobal == null) { 85c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown sGlobal = new GlobalMediaRouter(context.getApplicationContext()); 86c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown } 87c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown return sGlobal.getRouter(context); 88c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown } 89c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown 90c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown /** 91c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown * Gets the {@link MediaRouter.RouteInfo routes} currently known to this MediaRouter. 92c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown */ 93c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown public List<RouteInfo> getRoutes() { 94c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown checkCallingThread(); 95c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown return sGlobal.getRoutes(); 96c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown } 97c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown 98c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown /** 99c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown * Gets the default route for playing media content on the system. 100c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown * <p> 101c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown * The system always provides a default route. 102c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown * </p> 103c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown * 104c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown * @return The default route, which is guaranteed to never be null. 105c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown */ 106c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown public RouteInfo getDefaultRoute() { 107c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown checkCallingThread(); 108c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown return sGlobal.getDefaultRoute(); 109c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown } 110c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown 111c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown /** 112c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown * Gets the currently selected route. 113c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown * <p> 114c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown * The application should examine the route's 115c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown * {@link RouteInfo#getControlFilter media control intent filter} to assess the 116c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown * capabilities of the route before attempting to use it. 117c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown * </p> 118c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown * 119c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown * <h3>Example</h3> 120c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown * <pre> 121c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown * public boolean playMovie() { 122c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown * MediaRouter mediaRouter = MediaRouter.getInstance(context); 123c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown * MediaRouter.RouteInfo route = mediaRouter.getSelectedRoute(); 124c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown * 125c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown * // First try using the remote playback interface, if supported. 126c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown * if (route.supportsControlCategory(MediaControlIntent.CATEGORY_REMOTE_PLAYBACK)) { 127c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown * // The route supports remote playback. 128c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown * // Try to send it the Uri of the movie to play. 129c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown * Intent intent = new Intent(MediaControlIntent.ACTION_PLAY); 130c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown * intent.addCategory(MediaControlIntent.CATEGORY_REMOTE_PLAYBACK); 131c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown * intent.setDataAndType("http://example.com/videos/movie.mp4", "video/mp4"); 132c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown * if (route.sendControlRequest(intent, null)) { 133c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown * return true; // successfully sent the request to play the movie 134c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown * } 135c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown * } 136c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown * 137c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown * // If remote playback was not possible, then play locally. 138c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown * if (route.supportsControlCategory(MediaControlIntent.CATEGORY_LIVE_VIDEO)) { 139c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown * // The route supports live video streaming. 140c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown * // Prepare to play content locally in a window or in a presentation. 141c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown * return playMovieInWindow(); 142c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown * } 143c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown * 144c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown * // Neither interface is supported, so we can't play the movie to this route. 145c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown * return false; 146c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown * } 147c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown * </pre> 148c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown * 149c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown * @return The selected route, which is guaranteed to never be null. 150c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown * 151c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown * @see RouteInfo#getControlFilter 152c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown * @see RouteInfo#supportsControlCategory 153c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown * @see RouteInfo#supportsControlRequest 154c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown */ 155c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown public RouteInfo getSelectedRoute() { 156c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown checkCallingThread(); 157c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown return sGlobal.getSelectedRoute(); 158c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown } 159c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown 160c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown /** 161c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown * Selects the specified route. 162c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown * 163c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown * @param route The route to select. 164c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown */ 165c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown public void selectRoute(RouteInfo route) { 166c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown if (route == null) { 167c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown throw new IllegalArgumentException("route must not be null"); 168c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown } 169c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown checkCallingThread(); 170c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown 171c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown sGlobal.selectRoute(route); 172c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown } 173c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown 174c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown /** 175c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown * Adds a callback to listen to changes to media routes. 176c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown * 177c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown * @param callback The callback to add. 178c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown */ 179c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown public void addCallback(Callback callback) { 180c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown if (callback == null) { 181c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown throw new IllegalArgumentException("callback must not be null"); 182c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown } 183c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown checkCallingThread(); 184c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown 185c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown if (!mCallbacks.contains(callback)) { 186c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown mCallbacks.add(callback); 187c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown } 188c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown } 189c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown 190c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown /** 191c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown * Removes the specified callback. It will no longer receive information about 192c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown * changes to media routes. 193c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown * 194c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown * @param callback The callback to remove. 195c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown */ 196c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown public void removeCallback(Callback callback) { 197c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown if (callback == null) { 198c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown throw new IllegalArgumentException("callback must not be null"); 199c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown } 200c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown checkCallingThread(); 201c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown 202c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown mCallbacks.remove(callback); 203c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown } 204c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown 205c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown /** 206c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown * Registers a media route provider globally for this application process. 207c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown * 208c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown * @param provider The media route provider to add. 209c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown * 210c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown * @see MediaRouteProvider 211c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown */ 212c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown public void addProvider(MediaRouteProvider provider) { 213c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown if (provider == null) { 214c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown throw new IllegalArgumentException("provider must not be null"); 215c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown } 216c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown checkCallingThread(); 217c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown 218c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown sGlobal.addProvider(provider); 219c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown } 220c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown 221c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown /** 222c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown * Unregisters a media route provider globally for this application process. 223c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown * 224c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown * @param provider The media route provider to remove. 225c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown * 226c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown * @see MediaRouteProvider 227c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown */ 228c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown public void removeProvider(MediaRouteProvider provider) { 229c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown if (provider == null) { 230c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown throw new IllegalArgumentException("provider must not be null"); 231c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown } 232c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown checkCallingThread(); 233c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown 234c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown sGlobal.removeProvider(provider); 235c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown } 236c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown 237c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown /** 238c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown * Ensures that calls into the media router are on the correct thread. 239c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown * It pays to be a little paranoid when global state invariants are at risk. 240c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown */ 241c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown static void checkCallingThread() { 242c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown if (Looper.myLooper() != Looper.getMainLooper()) { 243c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown throw new IllegalStateException("The media router service must only be " 244c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown + "accessed on the application's main thread."); 245c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown } 246c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown } 247c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown 248c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown static <T> boolean equal(T a, T b) { 249c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown return a == b || (a != null && b != null && a.equals(b)); 250c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown } 251c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown 252c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown /** 253c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown * Provides information about a media route. 254c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown * <p> 255c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown * Each media route has a {@link MediaControlIntent media control} 256c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown * {@link #getControlFilter intent filter} that describes the capabilities of the 257c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown * route and the manner in which it is used and controlled. 258c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown * </p> 259c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown */ 260c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown public static final class RouteInfo { 261c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown private final ProviderRecord mProviderRecord; 262c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown private final String mDescriptorId; 263c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown private String mName; 264c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown private String mStatus; 265c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown private Drawable mIconDrawable; 266c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown private boolean mEnabled; 267c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown private IntentFilter mControlFilter; 268c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown private int mPlaybackType; 269c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown private int mPlaybackStream; 270c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown private int mVolumeHandling; 271c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown private int mVolume; 272c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown private int mVolumeMax; 273c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown private Display mPresentationDisplay; 274c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown private int mPresentationDisplayId = -1; 275c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown private Bundle mExtras; 276c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown private RouteDescriptor mDescriptor; 277c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown 278c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown /** 279c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown * The default playback type, "local", indicating the presentation of the media 280c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown * is happening on the same device (e.g. a phone, a tablet) as where it is 281c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown * controlled from. 282c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown * 283c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown * @see #getPlaybackType 284c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown */ 285c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown public static final int PLAYBACK_TYPE_LOCAL = 0; 286c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown 287c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown /** 288c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown * A playback type indicating the presentation of the media is happening on 289c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown * a different device (i.e. the remote device) than where it is controlled from. 290c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown * 291c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown * @see #getPlaybackType 292c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown */ 293c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown public static final int PLAYBACK_TYPE_REMOTE = 1; 294c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown 295c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown /** 296c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown * Playback information indicating the playback volume is fixed, i.e. it cannot be 297c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown * controlled from this object. An example of fixed playback volume is a remote player, 298c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown * playing over HDMI where the user prefers to control the volume on the HDMI sink, rather 299c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown * than attenuate at the source. 300c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown * 301c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown * @see #getVolumeHandling 302c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown */ 303c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown public static final int PLAYBACK_VOLUME_FIXED = 0; 304c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown 305c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown /** 306c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown * Playback information indicating the playback volume is variable and can be controlled 307c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown * from this object. 308c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown * 309c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown * @see #getVolumeHandling 310c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown */ 311c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown public static final int PLAYBACK_VOLUME_VARIABLE = 1; 312c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown 313c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown static final int CHANGE_GENERAL = 1 << 0; 314c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown static final int CHANGE_VOLUME = 1 << 1; 315c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown static final int CHANGE_PRESENTATION_DISPLAY = 1 << 2; 316c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown 317c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown RouteInfo(ProviderRecord providerRecord, String descriptorId) { 318c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown mProviderRecord = providerRecord; 319c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown mDescriptorId = descriptorId; 320c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown } 321c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown 322c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown int updateDescriptor(RouteDescriptor descriptor) { 323c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown int changes = 0; 324c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown if (mDescriptor != descriptor) { 325c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown mDescriptor = descriptor; 326c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown if (descriptor != null) { 327c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown if (!equal(mName, descriptor.getName())) { 328c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown mName = descriptor.getName(); 329c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown changes |= CHANGE_GENERAL; 330c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown } 331c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown if (!equal(mStatus, descriptor.getStatus())) { 332c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown mStatus = descriptor.getStatus(); 333c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown changes |= CHANGE_GENERAL; 334c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown } 335c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown // TODO: mIconDrawable, probably set this as a resource package name + id 336c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown if (mEnabled != descriptor.isEnabled()) { 337c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown mEnabled = descriptor.isEnabled(); 338c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown changes |= CHANGE_GENERAL; 339c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown } 340c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown if (!equal(mControlFilter, descriptor.getControlFilter())) { 341c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown mControlFilter = descriptor.getControlFilter(); 342c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown changes |= CHANGE_GENERAL; 343c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown } 344c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown if (mPlaybackType != descriptor.getPlaybackType()) { 345c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown mPlaybackType = descriptor.getPlaybackType(); 346c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown changes |= CHANGE_GENERAL; 347c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown } 348c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown if (mPlaybackStream != descriptor.getPlaybackStream()) { 349c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown mPlaybackStream = descriptor.getPlaybackStream(); 350c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown changes |= CHANGE_GENERAL; 351c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown } 352c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown if (mVolumeHandling != descriptor.getVolumeHandling()) { 353c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown mVolumeHandling = descriptor.getVolumeHandling(); 354c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown changes |= CHANGE_GENERAL | CHANGE_VOLUME; 355c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown } 356c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown if (mVolume != descriptor.getVolume()) { 357c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown mVolume = descriptor.getVolume(); 358c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown changes |= CHANGE_GENERAL | CHANGE_VOLUME; 359c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown } 360c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown if (mVolumeMax != descriptor.getVolumeMax()) { 361c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown mVolumeMax = descriptor.getVolumeMax(); 362c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown changes |= CHANGE_GENERAL | CHANGE_VOLUME; 363c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown } 364c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown if (mPresentationDisplayId != descriptor.getPresentationDisplayId()) { 365c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown mPresentationDisplayId = descriptor.getPresentationDisplayId(); 366c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown mPresentationDisplay = null; 367c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown changes |= CHANGE_GENERAL | CHANGE_PRESENTATION_DISPLAY; 368c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown } 369c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown if (!equal(mExtras, descriptor.getExtras())) { 370c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown mExtras = descriptor.getExtras(); 371c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown changes |= CHANGE_GENERAL; 372c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown } 373c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown } 374c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown } 375c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown return changes; 376c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown } 377c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown 378c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown MediaRouteProvider getProvider() { 379c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown return mProviderRecord.mProvider; 380c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown } 381c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown 382c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown String getDescriptorId() { 383c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown return mDescriptorId; 384c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown } 385c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown 386c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown void select() { 387c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown sGlobal.selectRoute(this); 388c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown } 389c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown 390c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown /** 391c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown * Gets the name of this route. 392c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown * 393c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown * @return The user-friendly name of a media route. This is the string presented 394c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown * to users who may select this as the active route. 395c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown */ 396c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown public String getName() { 397c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown return mName; 398c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown } 399c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown 400c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown /** 401c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown * Gets the status of this route. 402c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown * 403c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown * @return The user-friendly status for a media route. This may include a description 404c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown * of the currently playing media, if available. 405c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown */ 406c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown public String getStatus() { 407c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown return mStatus; 408c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown } 409c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown 410c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown /** 411c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown * Get the icon representing this route. 412c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown * This icon will be used in picker UIs if available. 413c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown * 414c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown * @return The icon representing this route or null if no icon is available 415c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown */ 416c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown public Drawable getIconDrawable() { 417c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown return mIconDrawable; 418c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown } 419c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown 420c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown /** 421c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown * Returns true if this route is enabled and may be selected. 422c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown * 423c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown * @return true if this route is enabled and may be selected. 424c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown */ 425c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown public boolean isEnabled() { 426c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown return mEnabled; 427c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown } 428c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown 429c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown /** 430c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown * Gets a {@link MediaControlIntent media control intent} filter that describes 431c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown * the capabilities of this route and the media control actions that it supports. 432c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown * 433c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown * @return An intent filter that specifies the media control intents that 434c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown * are supported by this route. 435c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown * 436c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown * @see MediaControlIntent 437c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown * @see #supportsControlCategory 438c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown * @see #supportsControlRequest 439c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown */ 440c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown public IntentFilter getControlFilter() { 441c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown return mControlFilter; 442c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown } 443c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown 444c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown /** 445c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown * Returns true if the route supports the specified 446c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown * {@link MediaControlIntent media control} category. 447c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown * <p> 448c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown * Media control categories describe the capabilities of this route 449c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown * such as whether it supports live audio streaming or remote playback. 450c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown * </p> 451c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown * 452c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown * @param category A {@link MediaControlIntent media control} category 453c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown * such as {@link MediaControlIntent#CATEGORY_LIVE_AUDIO}, 454c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown * {@link MediaControlIntent#CATEGORY_LIVE_VIDEO}, 455c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown * {@link MediaControlIntent#CATEGORY_REMOTE_PLAYBACK} or a provider-defined 456c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown * media control category. 457c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown * 458c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown * @see MediaControlIntent 459c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown * @see #getControlFilter 460c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown */ 461c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown public boolean supportsControlCategory(String category) { 462c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown if (category == null) { 463c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown throw new IllegalArgumentException("category must not be null"); 464c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown } 465c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown 466c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown return mControlFilter.hasCategory(category); 467c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown } 468c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown 469c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown /** 470c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown * Returns true if the route supports the specified 471c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown * {@link MediaControlIntent media control} request. 472c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown * <p> 473c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown * Media control requests are used to request the route to perform 474c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown * actions such as starting remote playback of a content stream. 475c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown * </p> 476c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown * 477c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown * @param intent A {@link MediaControlIntent media control intent}. 478c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown * @return True if the route can handle the specified intent. 479c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown * 480c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown * @see MediaControlIntent 481c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown * @see #getControlFilter 482c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown */ 483c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown public boolean supportsControlRequest(Intent intent) { 484c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown if (intent == null) { 485c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown throw new IllegalArgumentException("intent must not be null"); 486c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown } 487c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown checkCallingThread(); 488c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown 489c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown ContentResolver contentResolver = sGlobal.getContentResolver(); 490c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown return mControlFilter.match(contentResolver, intent, true, TAG) >= 0; 491c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown } 492c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown 493c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown /** 494c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown * Sends a {@link MediaControlIntent media control} request to be performed 495c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown * asynchronously by the route's destination. 496c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown * <p> 497c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown * Media control requests are used to request the route to perform 498c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown * actions such as starting remote playback of a content stream. 499c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown * </p><p> 500c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown * This function may only be called on a selected route. It will return 501c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown * <code>false</code> and have no effect if the route is currently unselected. 502c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown * </p> 503c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown * 504c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown * @param intent A {@link MediaControlIntent media control intent}. 505c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown * @param callback A {@link ControlRequestCallback} to invoke with the result 506c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown * of the request, or null if no result is required. 507c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown * @return True if the control request was delivered to the route 508c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown * which does not necessarily mean that the request succeeded. Provide a 509c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown * callback to obtain the result of the request. 510c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown * 511c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown * @see MediaControlIntent 512c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown */ 513c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown public boolean sendControlRequest(Intent intent, ControlRequestCallback callback) { 514c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown if (intent == null) { 515c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown throw new IllegalArgumentException("intent must not be null"); 516c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown } 517c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown checkCallingThread(); 518c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown 519c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown return sGlobal.sendControlRequest(this, intent, callback); 520c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown } 521c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown 522c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown /** 523c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown * Gets the type of playback associated with this route. 524c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown * 525c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown * @return The type of playback associated with this route: {@link #PLAYBACK_TYPE_LOCAL} 526c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown * or {@link #PLAYBACK_TYPE_REMOTE}. 527c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown */ 528c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown public int getPlaybackType() { 529c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown return mPlaybackType; 530c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown } 531c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown 532c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown /** 533c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown * Gets the the stream over which the playback associated with this route is performed. 534c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown * 535c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown * @return The stream over which the playback associated with this route is performed. 536c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown */ 537c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown public int getPlaybackStream() { 538c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown return mPlaybackStream; 539c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown } 540c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown 541c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown /** 542c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown * Gets information about how volume is handled on the route. 543c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown * 544c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown * @return How volume is handled on the route: {@link #PLAYBACK_VOLUME_FIXED} 545c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown * or {@link #PLAYBACK_VOLUME_VARIABLE}. 546c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown */ 547c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown public int getVolumeHandling() { 548c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown return mVolumeHandling; 549c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown } 550c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown 551c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown /** 552c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown * Gets the current volume for this route. Depending on the route, this may only 553c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown * be valid if the route is currently selected. 554c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown * 555c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown * @return The volume at which the playback associated with this route is performed. 556c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown */ 557c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown public int getVolume() { 558c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown return mVolume; 559c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown } 560c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown 561c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown /** 562c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown * Gets the maximum volume at which the playback associated with this route is performed. 563c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown * 564c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown * @return The maximum volume at which the playback associated with 565c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown * this route is performed. 566c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown */ 567c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown public int getVolumeMax() { 568c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown return mVolumeMax; 569c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown } 570c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown 571c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown /** 572c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown * Requests a volume change for this route asynchronously. 573c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown * <p> 574c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown * This function may only be called on a selected route. It will have 575c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown * no effect if the route is currently unselected. 576c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown * </p> 577c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown * 578c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown * @param volume The new volume value between 0 and {@link #getVolumeMax}. 579c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown */ 580c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown public void requestSetVolume(int volume) { 581c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown checkCallingThread(); 582c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown sGlobal.requestSetVolume(this, Math.min(mVolumeMax, Math.max(0, volume))); 583c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown } 584c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown 585c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown /** 586c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown * Requests an incremental volume update for this route asynchronously. 587c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown * <p> 588c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown * This function may only be called on a selected route. It will have 589c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown * no effect if the route is currently unselected. 590c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown * </p> 591c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown * 592c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown * @param delta The delta to add to the current volume. 593c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown */ 594c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown public void requestUpdateVolume(int delta) { 595c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown checkCallingThread(); 596c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown if (delta != 0) { 597c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown sGlobal.requestUpdateVolume(this, delta); 598c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown } 599c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown } 600c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown 601c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown /** 602c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown * Gets the {@link Display} that should be used by the application to show 603c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown * a {@link android.app.Presentation} on an external display when this route is selected. 604c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown * Depending on the route, this may only be valid if the route is currently 605c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown * selected. 606c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown * <p> 607c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown * The preferred presentation display may change independently of the route 608c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown * being selected or unselected. For example, the presentation display 609c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown * of the default system route may change when an external HDMI display is connected 610c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown * or disconnected even though the route itself has not changed. 611c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown * </p><p> 612c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown * This method may return null if there is no external display associated with 613c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown * the route or if the display is not ready to show UI yet. 614c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown * </p><p> 615c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown * The application should listen for changes to the presentation display 616c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown * using the {@link Callback#onRoutePresentationDisplayChanged} callback and 617c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown * show or dismiss its {@link android.app.Presentation} accordingly when the display 618c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown * becomes available or is removed. 619c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown * </p><p> 620c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown * This method only makes sense for 621c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown * {@link MediaControlIntent#CATEGORY_LIVE_VIDEO live video} routes. 622c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown * </p> 623c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown * 624c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown * @return The preferred presentation display to use when this route is 625c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown * selected or null if none. 626c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown * 627c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown * @see MediaControlIntent#CATEGORY_LIVE_VIDEO 628c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown * @see android.app.Presentation 629c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown */ 630c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown public Display getPresentationDisplay() { 631c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown if (mPresentationDisplayId >= 0 && mPresentationDisplay == null) { 632c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown mPresentationDisplay = sGlobal.getDisplay(mPresentationDisplayId); 633c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown } 634c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown return mPresentationDisplay; 635c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown } 636c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown 637c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown /** 638c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown * Gets a collection of extra properties about this route that were supplied 639c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown * by its media route provider, or null if none. 640c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown */ 641c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown public Bundle getExtras() { 642c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown return mExtras; 643c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown } 644c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown 645c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown @Override 646c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown public String toString() { 647c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown return "MediaRouter.RouteInfo{ name=" + mName 648c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown + ", status=" + mStatus 649c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown + ", enabled=" + mEnabled 650c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown + ", controlFilter=" + mControlFilter 651c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown + ", playbackType=" + mPlaybackType 652c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown + ", playbackStream=" + mPlaybackStream 653c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown + ", volumeHandling=" + mVolumeHandling 654c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown + ", volume=" + mVolume 655c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown + ", volumeMax=" + mVolumeMax 656c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown + ", presentationDisplayId=" + mPresentationDisplayId 657c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown + ", extras=" + mExtras 658c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown + " }"; 659c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown } 660c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown } 661c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown 662c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown /** 663c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown * Interface for receiving events about media routing changes. 664c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown * All methods of this interface will be called from the application's main thread. 665c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown * <p> 666c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown * A Callback will only receive events relevant to routes that the callback 667c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown * was registered for. 668c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown * </p> 669c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown * 670c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown * @see MediaRouter#addCallback(Callback) 671c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown * @see MediaRouter#removeCallback(Callback) 672c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown */ 673c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown public static abstract class Callback { 674c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown /** 675c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown * Called when the supplied route becomes selected as the active route. 676c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown * 677c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown * @param router The MediaRouter reporting the event. 678c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown * @param route The route that has been selected. 679c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown */ 680c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown public void onRouteSelected(MediaRouter router, RouteInfo route) { 681c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown } 682c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown 683c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown /** 684c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown * Called when the supplied route becomes unselected as the active route. 685c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown * 686c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown * @param router The MediaRouter reporting the event. 687c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown * @param route The route that has been unselected. 688c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown */ 689c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown public void onRouteUnselected(MediaRouter router, RouteInfo route) { 690c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown } 691c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown 692c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown /** 693c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown * Called when a route has been added. 694c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown * 695c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown * @param router The MediaRouter reporting the event. 696c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown * @param route The route that has become available for use. 697c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown */ 698c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown public void onRouteAdded(MediaRouter router, RouteInfo route) { 699c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown } 700c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown 701c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown /** 702c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown * Called when a route has been removed. 703c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown * 704c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown * @param router The MediaRouter reporting the event. 705c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown * @param route The route that has been removed from availability. 706c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown */ 707c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown public void onRouteRemoved(MediaRouter router, RouteInfo route) { 708c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown } 709c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown 710c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown /** 711c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown * Called when a property of the indicated route has changed. 712c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown * 713c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown * @param router The MediaRouter reporting the event. 714c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown * @param route The route that was changed. 715c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown */ 716c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown public void onRouteChanged(MediaRouter router, RouteInfo route) { 717c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown } 718c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown 719c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown /** 720c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown * Called when a route's volume changes. 721c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown * 722c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown * @param router The MediaRouter reporting the event. 723c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown * @param route The route whose volume changed. 724c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown */ 725c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown public void onRouteVolumeChanged(MediaRouter router, RouteInfo route) { 726c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown } 727c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown 728c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown /** 729c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown * Called when a route's presentation display changes. 730c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown * <p> 731c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown * This method is called whenever the route's presentation display becomes 732c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown * available, is removes or has changes to some of its properties (such as its size). 733c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown * </p> 734c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown * 735c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown * @param router The MediaRouter reporting the event. 736c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown * @param route The route whose presentation display changed. 737c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown * 738c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown * @see RouteInfo#getPresentationDisplay() 739c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown */ 740c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown public void onRoutePresentationDisplayChanged(MediaRouter router, RouteInfo route) { 741c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown } 742c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown } 743c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown 744c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown /** 745c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown * Callback which is invoked with the result of a media control request. 746c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown * 747c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown * @see RouteInfo#sendControlRequest 748c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown */ 749c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown public abstract class ControlRequestCallback { 750c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown /** 751c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown * Result code: The requested media control action is not supported. 752c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown */ 753c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown public static final int REQUEST_NOT_SUPPORTED = -2; 754c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown 755c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown /** 756c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown * Result code: The media control action failed. 757c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown */ 758c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown public static final int REQUEST_FAILED = -1; 759c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown 760c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown /** 761c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown * Result code: The media control action succeeded. 762c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown */ 763c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown public static final int REQUEST_SUCCEEDED = 0; 764c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown 765c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown /** 766c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown * Called with the result of the media control request. 767c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown * 768c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown * @param result The result code: {@link #REQUEST_NOT_SUPPORTED}, 769c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown * {@link #REQUEST_FAILED}, or {@link #REQUEST_SUCCEEDED}. 770c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown * @param data Additional result data. Contents depend on the media control action. 771c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown */ 772c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown public void onResult(int result, Bundle data) { 773c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown } 774c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown } 775c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown 776c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown /** 777c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown * State associated with a media route provider. 778c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown */ 779c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown private static final class ProviderRecord { 780c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown public final MediaRouteProvider mProvider; 781c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown public final ArrayList<RouteInfo> mRoutes = new ArrayList<RouteInfo>(); 782c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown public RouteProviderDescriptor mDescriptor; 783c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown 784c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown public ProviderRecord(MediaRouteProvider provider) { 785c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown mProvider = provider; 786c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown } 787c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown 788c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown boolean updateDescriptor(RouteProviderDescriptor descriptor) { 789c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown if (mDescriptor != descriptor) { 790c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown mDescriptor = descriptor; 791c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown return true; 792c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown } 793c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown return false; 794c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown } 795c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown 796c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown int findRouteByDescriptorId(String id) { 797c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown final int count = mRoutes.size(); 798c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown for (int i = 0; i < count; i++) { 799c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown if (mRoutes.get(i).mDescriptorId.equals(id)) { 800c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown return i; 801c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown } 802c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown } 803c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown return -1; 804c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown } 805c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown } 806c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown 807c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown /** 808c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown * Global state for the media router. 809c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown * <p> 810c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown * Media routes and media route providers are global to the process; their 811c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown * state and the bulk of the media router implementation lives here. 812c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown * </p> 813c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown */ 814c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown private static final class GlobalMediaRouter implements SystemMediaRouteProvider.SyncCallback { 815c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown private final Context mApplicationContext; 816c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown private final WeakHashMap<Context, MediaRouter> mRouters = 817c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown new WeakHashMap<Context, MediaRouter>(); 818c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown private final ArrayList<ProviderRecord> mProviderRecords = 819c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown new ArrayList<ProviderRecord>(); 820c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown private final ArrayList<RouteInfo> mRoutes = new ArrayList<RouteInfo>(); 821c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown private final ProviderCallback mProviderCallback = new ProviderCallback(); 822c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown private final CallbackHandler mCallbackHandler = new CallbackHandler(); 823c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown private final DisplayManagerCompat mDisplayManager; 824c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown private final SystemMediaRouteProvider mSystemProvider; 825c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown 826c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown private RouteInfo mDefaultRoute; 827c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown private RouteInfo mSelectedRoute; 828c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown private MediaRouteProvider.RouteController mSelectedRouteController; 829c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown 830c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown GlobalMediaRouter(Context applicationContext) { 831c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown mApplicationContext = applicationContext; 832c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown mDisplayManager = DisplayManagerCompat.getInstance(applicationContext); 833c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown mSystemProvider = SystemMediaRouteProvider.obtain(applicationContext, this); 834c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown addDefaultProviders(); 835c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown } 836c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown 837c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown public MediaRouter getRouter(Context context) { 838c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown MediaRouter router = mRouters.get(context); 839c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown if (router == null) { 840c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown router = new MediaRouter(context); 841c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown mRouters.put(context, router); 842c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown } 843c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown return router; 844c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown } 845c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown 846c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown public ContentResolver getContentResolver() { 847c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown return mApplicationContext.getContentResolver(); 848c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown } 849c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown 850c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown public Display getDisplay(int displayId) { 851c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown return mDisplayManager.getDisplay(displayId); 852c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown } 853c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown 854c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown public boolean sendControlRequest(RouteInfo route, 855c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown Intent intent, ControlRequestCallback callback) { 856c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown if (route == mSelectedRoute && mSelectedRouteController != null) { 857c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown return mSelectedRouteController.sendControlRequest(intent, callback); 858c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown } 859c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown return false; 860c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown } 861c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown 862c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown public void requestSetVolume(RouteInfo route, int volume) { 863c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown if (route == mSelectedRoute && mSelectedRouteController != null) { 864c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown mSelectedRouteController.requestSetVolume(volume); 865c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown } 866c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown } 867c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown 868c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown public void requestUpdateVolume(RouteInfo route, int delta) { 869c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown if (route == mSelectedRoute && mSelectedRouteController != null) { 870c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown mSelectedRouteController.requestUpdateVolume(delta); 871c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown } 872c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown } 873c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown 874c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown public List<RouteInfo> getRoutes() { 875c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown return mRoutes; 876c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown } 877c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown 878c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown public RouteInfo getDefaultRoute() { 879c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown if (mDefaultRoute == null) { 880c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown // This should never happen once the media router has been fully 881c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown // initialized but it is good to check for the error in case there 882c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown // is a bug in provider initialization. 883c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown throw new IllegalStateException("There is no default route. " 884c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown + "The media router has not yet been fully initialized."); 885c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown } 886c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown return mDefaultRoute; 887c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown } 888c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown 889c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown public RouteInfo getSelectedRoute() { 890c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown if (mSelectedRoute == null) { 891c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown // This should never happen once the media router has been fully 892c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown // initialized but it is good to check for the error in case there 893c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown // is a bug in provider initialization. 894c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown throw new IllegalStateException("There is no currently selected route. " 895c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown + "The media router has not yet been fully initialized."); 896c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown } 897c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown return mSelectedRoute; 898c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown } 899c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown 900c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown public void selectRoute(RouteInfo route) { 901c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown if (!mRoutes.contains(route)) { 902c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown Log.w(TAG, "Ignoring attempt to select removed route: " + route); 903c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown return; 904c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown } 905c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown if (!route.mEnabled) { 906c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown Log.w(TAG, "Ignoring attempt to select disabled route: " + route); 907c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown return; 908c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown } 909c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown 910c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown setSelectedRouteInternal(route); 911c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown } 912c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown 913c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown public void addProvider(MediaRouteProvider provider) { 914c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown int index = findProviderRecord(provider); 915c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown if (index < 0) { 916c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown // 1. Add the provider to the list. 917c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown ProviderRecord providerRecord = new ProviderRecord(provider); 918c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown mProviderRecords.add(providerRecord); 919c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown // 2. Create the provider's contents. 920c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown updateProviderContents(providerRecord, provider.getDescriptor()); 921c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown // 3. Register the provider callback. 922c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown provider.addCallback(mProviderCallback); 923c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown // 4. Update the selected route if needed. 924c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown updateSelectedRoute(); 925c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown } 926c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown } 927c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown 928c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown public void removeProvider(MediaRouteProvider provider) { 929c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown int index = findProviderRecord(provider); 930c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown if (index >= 0) { 931c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown // 1. Unregister the provider callback. 932c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown provider.removeCallback(mProviderCallback); 933c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown // 2. Delete the provider's contents. 934c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown ProviderRecord providerRecord = mProviderRecords.get(index); 935c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown updateProviderContents(providerRecord, null); 936c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown // 3. Remove the provider from the list. 937c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown mProviderRecords.remove(index); 938c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown // 4. Update the selected route if needed. 939c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown updateSelectedRoute(); 940c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown } 941c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown } 942c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown 943c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown private void updateProviderDescriptor(MediaRouteProvider provider, 944c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown RouteProviderDescriptor descriptor) { 945c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown int index = findProviderRecord(provider); 946c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown if (index >= 0) { 947c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown // 1. Update the provider's contents. 948c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown ProviderRecord providerRecord = mProviderRecords.get(index); 949c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown updateProviderContents(providerRecord, descriptor); 950c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown // 2. Update the selected route if needed. 951c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown updateSelectedRoute(); 952c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown } 953c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown } 954c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown 955c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown private void addDefaultProviders() { 956c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown addProvider(mSystemProvider); 957c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown } 958c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown 959c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown private int findProviderRecord(MediaRouteProvider provider) { 960c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown final int count = mProviderRecords.size(); 961c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown for (int i = 0; i < count; i++) { 962c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown if (mProviderRecords.get(i).mProvider == provider) { 963c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown return i; 964c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown } 965c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown } 966c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown return -1; 967c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown } 968c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown 969c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown private void updateProviderContents(ProviderRecord providerRecord, 970c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown RouteProviderDescriptor providerDescriptor) { 971c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown if (providerRecord.updateDescriptor(providerDescriptor)) { 972c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown // Update all existing routes and reorder them to match 973c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown // the order of their descriptors. 974c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown int targetIndex = 0; 975c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown if (providerDescriptor != null) { 976c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown final RouteDescriptor[] routeDescriptors = providerDescriptor.getRoutes(); 977c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown for (int i = 0; i < routeDescriptors.length; i++) { 978c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown final RouteDescriptor routeDescriptor = routeDescriptors[i]; 979c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown if (routeDescriptor.isValid()) { 980c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown final String id = routeDescriptor.getId(); 981c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown final int sourceIndex = providerRecord.findRouteByDescriptorId(id); 982c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown if (sourceIndex < 0) { 983c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown // 1. Add the route to the list. 984c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown RouteInfo route = new RouteInfo(providerRecord, id); 985c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown providerRecord.mRoutes.add(targetIndex++, route); 986c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown mRoutes.add(route); 987c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown // 2. Create the route's contents. 988c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown route.updateDescriptor(routeDescriptor); 989c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown // 3. Notify clients. 990c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown mCallbackHandler.post(CallbackHandler.MSG_ROUTE_ADDED, route); 991c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown } else { 992c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown // 1. Reorder the route within the list. 993c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown RouteInfo route = providerRecord.mRoutes.get(sourceIndex); 994c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown Collections.swap(providerRecord.mRoutes, 995c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown sourceIndex, targetIndex++); 996c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown // 2. Update the route's contents. 997c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown int changes = route.updateDescriptor(routeDescriptor); 998c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown // 3. Notify clients. 999c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown if ((changes & RouteInfo.CHANGE_GENERAL) != 0) { 1000c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown mCallbackHandler.post( 1001c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown CallbackHandler.MSG_ROUTE_CHANGED, route); 1002c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown } 1003c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown if ((changes & RouteInfo.CHANGE_VOLUME) != 0) { 1004c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown mCallbackHandler.post( 1005c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown CallbackHandler.MSG_ROUTE_VOLUME_CHANGED, route); 1006c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown } 1007c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown if ((changes & RouteInfo.CHANGE_PRESENTATION_DISPLAY) != 0) { 1008c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown mCallbackHandler.post( 1009c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown CallbackHandler.MSG_ROUTE_PRESENTATION_DISPLAY_CHANGED, 1010c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown route); 1011c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown } 1012c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown } 1013c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown } else { 1014c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown Log.w(TAG, "Ignoring invalid route descriptor: " + routeDescriptor); 1015c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown } 1016c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown } 1017c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown } 1018c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown 1019c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown // Dispose all remaining routes that do not have matching descriptors. 1020c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown for (int i = providerRecord.mRoutes.size() - 1; i >= targetIndex; i--) { 1021c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown // 1. Notify clients. 1022c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown RouteInfo route = providerRecord.mRoutes.get(i); 1023c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown mCallbackHandler.post(CallbackHandler.MSG_ROUTE_REMOVED, route); 1024c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown // 2. Delete the route's contents. 1025c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown route.updateDescriptor(null); 1026c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown // 3. Remove the route from the list. 1027c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown mRoutes.remove(providerRecord); 1028c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown providerRecord.mRoutes.remove(i); 1029c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown } 1030c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown } 1031c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown } 1032c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown 1033c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown private void updateSelectedRoute() { 1034c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown // Update the default route. 1035c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown if (mDefaultRoute != null && !isRouteSelectable(mDefaultRoute)) { 1036c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown Log.i(TAG, "Choosing a new default route because the current one " 1037c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown + "is no longer selectable: " + mDefaultRoute); 1038c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown mDefaultRoute = null; 1039c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown } 1040c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown if (mDefaultRoute == null && !mRoutes.isEmpty()) { 1041c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown for (RouteInfo route : mRoutes) { 1042c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown if (isSystemDefaultRoute(route) && isRouteSelectable(route)) { 1043c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown mDefaultRoute = route; 1044c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown break; 1045c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown } 1046c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown } 1047c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown } 1048c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown 1049c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown // Update the selected route. 1050c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown if (mSelectedRoute != null && !isRouteSelectable(mSelectedRoute)) { 1051c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown Log.i(TAG, "Choosing a new selected route because the current one " 1052c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown + "is no longer selectable: " + mSelectedRoute); 1053c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown setSelectedRouteInternal(null); 1054c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown } 1055c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown if (mSelectedRoute == null) { 1056c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown setSelectedRouteInternal(mDefaultRoute); 1057c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown } 1058c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown } 1059c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown 1060c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown private boolean isRouteSelectable(RouteInfo route) { 1061c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown // This tests whether the route is still valid and enabled. 1062c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown // The route descriptor field is set to null when the route is removed. 1063c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown return route.mDescriptor != null && route.mEnabled; 1064c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown } 1065c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown 1066c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown private boolean isSystemDefaultRoute(RouteInfo route) { 1067c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown return route.getProvider() == mSystemProvider 1068c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown && route.mDescriptorId.equals( 1069c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown SystemMediaRouteProvider.DEFAULT_ROUTE_ID); 1070c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown } 1071c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown 1072c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown private void setSelectedRouteInternal(RouteInfo route) { 1073c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown if (mSelectedRoute != route) { 1074c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown if (mSelectedRoute != null) { 1075c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown mCallbackHandler.post(CallbackHandler.MSG_ROUTE_UNSELECTED, mSelectedRoute); 1076c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown if (mSelectedRouteController != null) { 1077c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown mSelectedRouteController.unselect(); 1078c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown mSelectedRouteController.release(); 1079c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown mSelectedRouteController = null; 1080c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown } 1081c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown } 1082c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown 1083c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown mSelectedRoute = route; 1084c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown 1085c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown if (mSelectedRoute != null) { 1086c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown mSelectedRouteController = route.getProvider().onCreateRouteController( 1087c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown route.mDescriptorId); 1088c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown if (mSelectedRouteController != null) { 1089c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown mSelectedRouteController.select(); 1090c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown } 1091c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown mCallbackHandler.post(CallbackHandler.MSG_ROUTE_SELECTED, mSelectedRoute); 1092c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown } 1093c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown } 1094c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown } 1095c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown 1096c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown @Override 1097c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown public RouteInfo getSystemRouteByDescriptorId(String id) { 1098c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown int providerIndex = findProviderRecord(mSystemProvider); 1099c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown if (providerIndex >= 0) { 1100c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown ProviderRecord providerRecord = mProviderRecords.get(providerIndex); 1101c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown int routeIndex = providerRecord.findRouteByDescriptorId(id); 1102c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown if (routeIndex >= 0) { 1103c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown return providerRecord.mRoutes.get(routeIndex); 1104c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown } 1105c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown } 1106c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown return null; 1107c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown } 1108c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown 1109c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown private final class ProviderCallback extends MediaRouteProvider.Callback { 1110c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown @Override 1111c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown public void onDescriptorChanged(MediaRouteProvider provider, 1112c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown RouteProviderDescriptor descriptor) { 1113c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown updateProviderDescriptor(provider, descriptor); 1114c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown } 1115c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown } 1116c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown 1117c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown private final class CallbackHandler extends Handler { 1118c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown private final ArrayList<MediaRouter> mTempMediaRouters = 1119c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown new ArrayList<MediaRouter>(); 1120c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown 1121c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown public static final int MSG_ROUTE_ADDED = 1; 1122c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown public static final int MSG_ROUTE_REMOVED = 2; 1123c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown public static final int MSG_ROUTE_CHANGED = 3; 1124c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown public static final int MSG_ROUTE_VOLUME_CHANGED = 4; 1125c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown public static final int MSG_ROUTE_PRESENTATION_DISPLAY_CHANGED = 5; 1126c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown public static final int MSG_ROUTE_SELECTED = 6; 1127c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown public static final int MSG_ROUTE_UNSELECTED = 7; 1128c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown 1129c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown public void post(int msg, RouteInfo route) { 1130c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown obtainMessage(msg, route).sendToTarget(); 1131c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown } 1132c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown 1133c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown @Override 1134c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown public void handleMessage(Message msg) { 1135c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown final int what = msg.what; 1136c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown final RouteInfo route = (RouteInfo)msg.obj; 1137c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown 1138c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown // Synchronize state with the system media router. 1139c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown syncWithSystemProvider(what, route); 1140c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown 1141c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown // Invoke all registered callbacks. 1142c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown mTempMediaRouters.addAll(mRouters.values()); 1143c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown try { 1144c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown final int routerCount = mTempMediaRouters.size(); 1145c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown for (int i = 0; i < routerCount; i++) { 1146c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown final MediaRouter router = mTempMediaRouters.get(i); 1147c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown if (!router.mCallbacks.isEmpty()) { 1148c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown for (MediaRouter.Callback callback : router.mCallbacks) { 1149c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown invokeCallback(router, callback, what, route); 1150c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown } 1151c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown } 1152c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown } 1153c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown } finally { 1154c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown mTempMediaRouters.clear(); 1155c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown } 1156c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown } 1157c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown 1158c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown private void syncWithSystemProvider(int what, RouteInfo route) { 1159c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown switch (what) { 1160c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown case MSG_ROUTE_ADDED: 1161c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown mSystemProvider.onSyncRouteAdded(route); 1162c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown break; 1163c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown case MSG_ROUTE_REMOVED: 1164c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown mSystemProvider.onSyncRouteRemoved(route); 1165c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown break; 1166c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown case MSG_ROUTE_CHANGED: 1167c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown mSystemProvider.onSyncRouteChanged(route); 1168c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown break; 1169c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown case MSG_ROUTE_SELECTED: 1170c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown mSystemProvider.onSyncRouteSelected(route); 1171c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown break; 1172c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown } 1173c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown } 1174c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown 1175c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown private void invokeCallback(MediaRouter router, MediaRouter.Callback callback, 1176c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown int what, RouteInfo route) { 1177c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown switch (what) { 1178c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown case MSG_ROUTE_ADDED: 1179c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown callback.onRouteAdded(router, route); 1180c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown break; 1181c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown case MSG_ROUTE_REMOVED: 1182c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown callback.onRouteRemoved(router, route); 1183c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown break; 1184c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown case MSG_ROUTE_CHANGED: 1185c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown callback.onRouteChanged(router, route); 1186c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown break; 1187c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown case MSG_ROUTE_VOLUME_CHANGED: 1188c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown callback.onRouteVolumeChanged(router, route); 1189c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown break; 1190c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown case MSG_ROUTE_PRESENTATION_DISPLAY_CHANGED: 1191c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown callback.onRoutePresentationDisplayChanged(router, route); 1192c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown break; 1193c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown case MSG_ROUTE_SELECTED: 1194c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown callback.onRouteSelected(router, route); 1195c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown break; 1196c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown case MSG_ROUTE_UNSELECTED: 1197c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown callback.onRouteUnselected(router, route); 1198c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown break; 1199c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown } 1200c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown } 1201c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown } 1202c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown } 1203c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown} 1204