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