MediaRouter.java revision e7a694bf5fc4bf417502509a8604fffc7a41d84c
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 19fb81e09002d1d611168b204fc50d259fc15f27b2Jeff Brownimport android.app.ActivityManager; 20adf0f4a217e14894af07dfa9f46cad7d98b8a7f4Jeff Brownimport android.content.ComponentName; 21c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brownimport android.content.ContentResolver; 22c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brownimport android.content.Context; 23c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brownimport android.content.Intent; 24c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brownimport android.content.IntentFilter; 2594be6100218126ce6a08bf1f56209578500b361fRoboErikimport android.content.IntentSender; 26fa326a4649d9d0e8113e315f6c8251fe686abce4Jeff Brownimport android.content.pm.PackageManager.NameNotFoundException; 27fa326a4649d9d0e8113e315f6c8251fe686abce4Jeff Brownimport android.content.res.Resources; 28c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brownimport android.os.Bundle; 29c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brownimport android.os.Handler; 30c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brownimport android.os.Looper; 31c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brownimport android.os.Message; 32429c3b17b88ebd8c4512e9179fd9d48333c0979eTor Norbyeimport android.support.annotation.IntDef; 33429c3b17b88ebd8c4512e9179fd9d48333c0979eTor Norbyeimport android.support.annotation.NonNull; 34429c3b17b88ebd8c4512e9179fd9d48333c0979eTor Norbyeimport android.support.annotation.Nullable; 35fb81e09002d1d611168b204fc50d259fc15f27b2Jeff Brownimport android.support.v4.app.ActivityManagerCompat; 36c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brownimport android.support.v4.hardware.display.DisplayManagerCompat; 37bbcdf78e350d58ecd6baa75e282d4908d3129fe2RoboErikimport android.support.v4.media.VolumeProviderCompat; 38bbcdf78e350d58ecd6baa75e282d4908d3129fe2RoboErikimport android.support.v4.media.session.MediaSessionCompat; 39e7a694bf5fc4bf417502509a8604fffc7a41d84cJae Seoimport android.support.v4.util.Pair; 40b507e525a61ed761eecfc2eaaf19af7e8db5dca5Jeff Brownimport android.support.v7.media.MediaRouteProvider.ProviderMetadata; 41c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brownimport android.util.Log; 42c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brownimport android.view.Display; 43c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown 44429c3b17b88ebd8c4512e9179fd9d48333c0979eTor Norbyeimport java.lang.annotation.Retention; 45429c3b17b88ebd8c4512e9179fd9d48333c0979eTor Norbyeimport java.lang.annotation.RetentionPolicy; 469fcedc160282e6620f409ea46bf6728b35d011ddJeff Brownimport java.lang.ref.WeakReference; 47c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brownimport java.util.ArrayList; 48c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brownimport java.util.Collections; 49c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brownimport java.util.List; 50cb63b6ecac9786891514f241dec71695f09d3efbJeff Brownimport java.util.Locale; 51c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown 52c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown/** 53c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown * MediaRouter allows applications to control the routing of media channels 54c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown * and streams from the current device to external speakers and destination devices. 55c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown * <p> 56c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown * A MediaRouter instance is retrieved through {@link #getInstance}. Applications 57c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown * can query the media router about the currently selected route and its capabilities 58c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown * to determine how to send content to the route's destination. Applications can 59c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown * also {@link RouteInfo#sendControlRequest send control requests} to the route 60c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown * to ask the route's destination to perform certain remote control functions 61fa326a4649d9d0e8113e315f6c8251fe686abce4Jeff Brown * such as playing media. 62c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown * </p><p> 63c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown * See also {@link MediaRouteProvider} for information on how an application 64c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown * can publish new media routes to the media router. 65c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown * </p><p> 66c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown * The media router API is not thread-safe; all interactions with it must be 67c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown * done from the main thread of the process. 68c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown * </p> 69c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown */ 70c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brownpublic final class MediaRouter { 71c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown private static final String TAG = "MediaRouter"; 72f03da4a9e6cc02251c2f804eb6f25da61821d6a7Jeff Brown private static final boolean DEBUG = Log.isLoggable(TAG, Log.DEBUG); 73c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown 7494be6100218126ce6a08bf1f56209578500b361fRoboErik /** 75b6362d072f329a3207aa709ac6b79a4aa3c4d522Ying Wang * Passed to {@link android.support.v7.media.MediaRouteProvider.RouteController#onUnselect(int)} 76b6362d072f329a3207aa709ac6b79a4aa3c4d522Ying Wang * when the reason the route was unselected is unknown. 7794be6100218126ce6a08bf1f56209578500b361fRoboErik */ 7894be6100218126ce6a08bf1f56209578500b361fRoboErik public static final int UNSELECT_REASON_UNKNOWN = 0; 7994be6100218126ce6a08bf1f56209578500b361fRoboErik /** 80b6362d072f329a3207aa709ac6b79a4aa3c4d522Ying Wang * Passed to {@link android.support.v7.media.MediaRouteProvider.RouteController#onUnselect(int)} 81b6362d072f329a3207aa709ac6b79a4aa3c4d522Ying Wang * when the user pressed the disconnect button to disconnect and keep playing. 8294be6100218126ce6a08bf1f56209578500b361fRoboErik * <p> 8394be6100218126ce6a08bf1f56209578500b361fRoboErik * 8494be6100218126ce6a08bf1f56209578500b361fRoboErik * @see {@link MediaRouteDescriptor#canDisconnectAndKeepPlaying()}. 8594be6100218126ce6a08bf1f56209578500b361fRoboErik */ 8694be6100218126ce6a08bf1f56209578500b361fRoboErik public static final int UNSELECT_REASON_DISCONNECTED = 1; 8794be6100218126ce6a08bf1f56209578500b361fRoboErik /** 88b6362d072f329a3207aa709ac6b79a4aa3c4d522Ying Wang * Passed to {@link android.support.v7.media.MediaRouteProvider.RouteController#onUnselect(int)} 89b6362d072f329a3207aa709ac6b79a4aa3c4d522Ying Wang * when the user pressed the stop casting button. 9094be6100218126ce6a08bf1f56209578500b361fRoboErik */ 9194be6100218126ce6a08bf1f56209578500b361fRoboErik public static final int UNSELECT_REASON_STOPPED = 2; 9294be6100218126ce6a08bf1f56209578500b361fRoboErik /** 93b6362d072f329a3207aa709ac6b79a4aa3c4d522Ying Wang * Passed to {@link android.support.v7.media.MediaRouteProvider.RouteController#onUnselect(int)} 94b6362d072f329a3207aa709ac6b79a4aa3c4d522Ying Wang * when the user selected a different route. 9594be6100218126ce6a08bf1f56209578500b361fRoboErik */ 9694be6100218126ce6a08bf1f56209578500b361fRoboErik public static final int UNSELECT_REASON_ROUTE_CHANGED = 3; 9794be6100218126ce6a08bf1f56209578500b361fRoboErik 98c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown // Maintains global media router state for the process. 99c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown // This field is initialized in MediaRouter.getInstance() before any 100c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown // MediaRouter objects are instantiated so it is guaranteed to be 101c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown // valid whenever any instance method is invoked. 102c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown static GlobalMediaRouter sGlobal; 103c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown 104c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown // Context-bound state of the media router. 105c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown final Context mContext; 1069fcedc160282e6620f409ea46bf6728b35d011ddJeff Brown final ArrayList<CallbackRecord> mCallbackRecords = new ArrayList<CallbackRecord>(); 10711417b1cfde8f1749905f2d735623af9214148afJeff Brown 108429c3b17b88ebd8c4512e9179fd9d48333c0979eTor Norbye /** @hide */ 109429c3b17b88ebd8c4512e9179fd9d48333c0979eTor Norbye @IntDef(flag = true, 110429c3b17b88ebd8c4512e9179fd9d48333c0979eTor Norbye value = { 111429c3b17b88ebd8c4512e9179fd9d48333c0979eTor Norbye CALLBACK_FLAG_PERFORM_ACTIVE_SCAN, 112429c3b17b88ebd8c4512e9179fd9d48333c0979eTor Norbye CALLBACK_FLAG_REQUEST_DISCOVERY, 113429c3b17b88ebd8c4512e9179fd9d48333c0979eTor Norbye CALLBACK_FLAG_UNFILTERED_EVENTS 114429c3b17b88ebd8c4512e9179fd9d48333c0979eTor Norbye } 115429c3b17b88ebd8c4512e9179fd9d48333c0979eTor Norbye ) 116429c3b17b88ebd8c4512e9179fd9d48333c0979eTor Norbye @Retention(RetentionPolicy.SOURCE) 117429c3b17b88ebd8c4512e9179fd9d48333c0979eTor Norbye private @interface CallbackFlags {} 118429c3b17b88ebd8c4512e9179fd9d48333c0979eTor Norbye 11911417b1cfde8f1749905f2d735623af9214148afJeff Brown /** 12011417b1cfde8f1749905f2d735623af9214148afJeff Brown * Flag for {@link #addCallback}: Actively scan for routes while this callback 12111417b1cfde8f1749905f2d735623af9214148afJeff Brown * is registered. 12211417b1cfde8f1749905f2d735623af9214148afJeff Brown * <p> 12311417b1cfde8f1749905f2d735623af9214148afJeff Brown * When this flag is specified, the media router will actively scan for new 12411417b1cfde8f1749905f2d735623af9214148afJeff Brown * routes. Certain routes, such as wifi display routes, may not be discoverable 12511417b1cfde8f1749905f2d735623af9214148afJeff Brown * except when actively scanning. This flag is typically used when the route picker 12611417b1cfde8f1749905f2d735623af9214148afJeff Brown * dialog has been opened by the user to ensure that the route information is 12711417b1cfde8f1749905f2d735623af9214148afJeff Brown * up to date. 12811417b1cfde8f1749905f2d735623af9214148afJeff Brown * </p><p> 12911417b1cfde8f1749905f2d735623af9214148afJeff Brown * Active scanning may consume a significant amount of power and may have intrusive 13011417b1cfde8f1749905f2d735623af9214148afJeff Brown * effects on wireless connectivity. Therefore it is important that active scanning 13111417b1cfde8f1749905f2d735623af9214148afJeff Brown * only be requested when it is actually needed to satisfy a user request to 13211417b1cfde8f1749905f2d735623af9214148afJeff Brown * discover and select a new route. 133f03da4a9e6cc02251c2f804eb6f25da61821d6a7Jeff Brown * </p><p> 134f03da4a9e6cc02251c2f804eb6f25da61821d6a7Jeff Brown * This flag implies {@link #CALLBACK_FLAG_REQUEST_DISCOVERY} but performing 135f03da4a9e6cc02251c2f804eb6f25da61821d6a7Jeff Brown * active scans is much more expensive than a normal discovery request. 13611417b1cfde8f1749905f2d735623af9214148afJeff Brown * </p> 137f03da4a9e6cc02251c2f804eb6f25da61821d6a7Jeff Brown * 138f03da4a9e6cc02251c2f804eb6f25da61821d6a7Jeff Brown * @see #CALLBACK_FLAG_REQUEST_DISCOVERY 13911417b1cfde8f1749905f2d735623af9214148afJeff Brown */ 140f03da4a9e6cc02251c2f804eb6f25da61821d6a7Jeff Brown public static final int CALLBACK_FLAG_PERFORM_ACTIVE_SCAN = 1 << 0; 14111417b1cfde8f1749905f2d735623af9214148afJeff Brown 14211417b1cfde8f1749905f2d735623af9214148afJeff Brown /** 14311417b1cfde8f1749905f2d735623af9214148afJeff Brown * Flag for {@link #addCallback}: Do not filter route events. 14411417b1cfde8f1749905f2d735623af9214148afJeff Brown * <p> 14511417b1cfde8f1749905f2d735623af9214148afJeff Brown * When this flag is specified, the callback will be invoked for events that affect any 1463efa63d3b896244713e84acbb5945562dce41d77Jeff Brown * route even if they do not match the callback's filter. 14711417b1cfde8f1749905f2d735623af9214148afJeff Brown * </p> 14811417b1cfde8f1749905f2d735623af9214148afJeff Brown */ 14911417b1cfde8f1749905f2d735623af9214148afJeff Brown public static final int CALLBACK_FLAG_UNFILTERED_EVENTS = 1 << 1; 15011417b1cfde8f1749905f2d735623af9214148afJeff Brown 15111417b1cfde8f1749905f2d735623af9214148afJeff Brown /** 152fb81e09002d1d611168b204fc50d259fc15f27b2Jeff Brown * Flag for {@link #addCallback}: Request passive route discovery while this 153fb81e09002d1d611168b204fc50d259fc15f27b2Jeff Brown * callback is registered, except on {@link ActivityManager#isLowRamDevice low-RAM devices}. 154f03da4a9e6cc02251c2f804eb6f25da61821d6a7Jeff Brown * <p> 155f03da4a9e6cc02251c2f804eb6f25da61821d6a7Jeff Brown * When this flag is specified, the media router will try to discover routes. 156f03da4a9e6cc02251c2f804eb6f25da61821d6a7Jeff Brown * Although route discovery is intended to be efficient, checking for new routes may 157f03da4a9e6cc02251c2f804eb6f25da61821d6a7Jeff Brown * result in some network activity and could slowly drain the battery. Therefore 158f03da4a9e6cc02251c2f804eb6f25da61821d6a7Jeff Brown * applications should only specify {@link #CALLBACK_FLAG_REQUEST_DISCOVERY} when 159f03da4a9e6cc02251c2f804eb6f25da61821d6a7Jeff Brown * they are running in the foreground and would like to provide the user with the 160f03da4a9e6cc02251c2f804eb6f25da61821d6a7Jeff Brown * option of connecting to new routes. 161f03da4a9e6cc02251c2f804eb6f25da61821d6a7Jeff Brown * </p><p> 162f03da4a9e6cc02251c2f804eb6f25da61821d6a7Jeff Brown * Applications should typically add a callback using this flag in the 163f03da4a9e6cc02251c2f804eb6f25da61821d6a7Jeff Brown * {@link android.app.Activity activity's} {@link android.app.Activity#onStart onStart} 164f03da4a9e6cc02251c2f804eb6f25da61821d6a7Jeff Brown * method and remove it in the {@link android.app.Activity#onStop onStop} method. 165f03da4a9e6cc02251c2f804eb6f25da61821d6a7Jeff Brown * The {@link android.support.v7.app.MediaRouteDiscoveryFragment} fragment may 166f03da4a9e6cc02251c2f804eb6f25da61821d6a7Jeff Brown * also be used for this purpose. 167fb81e09002d1d611168b204fc50d259fc15f27b2Jeff Brown * </p><p class="note"> 168fb81e09002d1d611168b204fc50d259fc15f27b2Jeff Brown * On {@link ActivityManager#isLowRamDevice low-RAM devices} this flag 169fb81e09002d1d611168b204fc50d259fc15f27b2Jeff Brown * will be ignored. Refer to 170fb81e09002d1d611168b204fc50d259fc15f27b2Jeff Brown * {@link #addCallback(MediaRouteSelector, Callback, int) addCallback} for details. 171f03da4a9e6cc02251c2f804eb6f25da61821d6a7Jeff Brown * </p> 172f03da4a9e6cc02251c2f804eb6f25da61821d6a7Jeff Brown * 173f03da4a9e6cc02251c2f804eb6f25da61821d6a7Jeff Brown * @see android.support.v7.app.MediaRouteDiscoveryFragment 174f03da4a9e6cc02251c2f804eb6f25da61821d6a7Jeff Brown */ 175f03da4a9e6cc02251c2f804eb6f25da61821d6a7Jeff Brown public static final int CALLBACK_FLAG_REQUEST_DISCOVERY = 1 << 2; 176f03da4a9e6cc02251c2f804eb6f25da61821d6a7Jeff Brown 177f03da4a9e6cc02251c2f804eb6f25da61821d6a7Jeff Brown /** 178fb81e09002d1d611168b204fc50d259fc15f27b2Jeff Brown * Flag for {@link #addCallback}: Request passive route discovery while this 179fb81e09002d1d611168b204fc50d259fc15f27b2Jeff Brown * callback is registered, even on {@link ActivityManager#isLowRamDevice low-RAM devices}. 180fb81e09002d1d611168b204fc50d259fc15f27b2Jeff Brown * <p class="note"> 181fb81e09002d1d611168b204fc50d259fc15f27b2Jeff Brown * This flag has a significant performance impact on low-RAM devices 182fb81e09002d1d611168b204fc50d259fc15f27b2Jeff Brown * since it may cause many media route providers to be started simultaneously. 183fb81e09002d1d611168b204fc50d259fc15f27b2Jeff Brown * It is much better to use {@link #CALLBACK_FLAG_REQUEST_DISCOVERY} instead to avoid 184fb81e09002d1d611168b204fc50d259fc15f27b2Jeff Brown * performing passive discovery on these devices altogether. Refer to 185fb81e09002d1d611168b204fc50d259fc15f27b2Jeff Brown * {@link #addCallback(MediaRouteSelector, Callback, int) addCallback} for details. 186fb81e09002d1d611168b204fc50d259fc15f27b2Jeff Brown * </p> 187fb81e09002d1d611168b204fc50d259fc15f27b2Jeff Brown * 188fb81e09002d1d611168b204fc50d259fc15f27b2Jeff Brown * @see android.support.v7.app.MediaRouteDiscoveryFragment 189fb81e09002d1d611168b204fc50d259fc15f27b2Jeff Brown */ 190fb81e09002d1d611168b204fc50d259fc15f27b2Jeff Brown public static final int CALLBACK_FLAG_FORCE_DISCOVERY = 1 << 3; 191fb81e09002d1d611168b204fc50d259fc15f27b2Jeff Brown 192fb81e09002d1d611168b204fc50d259fc15f27b2Jeff Brown /** 19311417b1cfde8f1749905f2d735623af9214148afJeff Brown * Flag for {@link #isRouteAvailable}: Ignore the default route. 19411417b1cfde8f1749905f2d735623af9214148afJeff Brown * <p> 19511417b1cfde8f1749905f2d735623af9214148afJeff Brown * This flag is used to determine whether a matching non-default route is available. 19611417b1cfde8f1749905f2d735623af9214148afJeff Brown * This constraint may be used to decide whether to offer the route chooser dialog 19711417b1cfde8f1749905f2d735623af9214148afJeff Brown * to the user. There is no point offering the chooser if there are no 19811417b1cfde8f1749905f2d735623af9214148afJeff Brown * non-default choices. 19911417b1cfde8f1749905f2d735623af9214148afJeff Brown * </p> 20011417b1cfde8f1749905f2d735623af9214148afJeff Brown */ 20111417b1cfde8f1749905f2d735623af9214148afJeff Brown public static final int AVAILABILITY_FLAG_IGNORE_DEFAULT_ROUTE = 1 << 0; 20211417b1cfde8f1749905f2d735623af9214148afJeff Brown 203fb81e09002d1d611168b204fc50d259fc15f27b2Jeff Brown /** 204fb81e09002d1d611168b204fc50d259fc15f27b2Jeff Brown * Flag for {@link #isRouteAvailable}: Require an actual route to be matched. 205fb81e09002d1d611168b204fc50d259fc15f27b2Jeff Brown * <p> 206fb81e09002d1d611168b204fc50d259fc15f27b2Jeff Brown * If this flag is not set, then {@link #isRouteAvailable} will return true 207fb81e09002d1d611168b204fc50d259fc15f27b2Jeff Brown * if it is possible to discover a matching route even if discovery is not in 208fb81e09002d1d611168b204fc50d259fc15f27b2Jeff Brown * progress or if no matching route has yet been found. This feature is used to 209fb81e09002d1d611168b204fc50d259fc15f27b2Jeff Brown * save resources by removing the need to perform passive route discovery on 210fb81e09002d1d611168b204fc50d259fc15f27b2Jeff Brown * {@link ActivityManager#isLowRamDevice low-RAM devices}. 211fb81e09002d1d611168b204fc50d259fc15f27b2Jeff Brown * </p><p> 212fb81e09002d1d611168b204fc50d259fc15f27b2Jeff Brown * If this flag is set, then {@link #isRouteAvailable} will only return true if 213fb81e09002d1d611168b204fc50d259fc15f27b2Jeff Brown * a matching route has actually been discovered. 214fb81e09002d1d611168b204fc50d259fc15f27b2Jeff Brown * </p> 215fb81e09002d1d611168b204fc50d259fc15f27b2Jeff Brown */ 216fb81e09002d1d611168b204fc50d259fc15f27b2Jeff Brown public static final int AVAILABILITY_FLAG_REQUIRE_MATCH = 1 << 1; 217fb81e09002d1d611168b204fc50d259fc15f27b2Jeff Brown 218c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown MediaRouter(Context context) { 219c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown mContext = context; 220c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown } 221c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown 222c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown /** 2239fcedc160282e6620f409ea46bf6728b35d011ddJeff Brown * Gets an instance of the media router service associated with the context. 2249fcedc160282e6620f409ea46bf6728b35d011ddJeff Brown * <p> 2259fcedc160282e6620f409ea46bf6728b35d011ddJeff Brown * The application is responsible for holding a strong reference to the returned 2269fcedc160282e6620f409ea46bf6728b35d011ddJeff Brown * {@link MediaRouter} instance, such as by storing the instance in a field of 2279fcedc160282e6620f409ea46bf6728b35d011ddJeff Brown * the {@link android.app.Activity}, to ensure that the media router remains alive 2289fcedc160282e6620f409ea46bf6728b35d011ddJeff Brown * as long as the application is using its features. 2299fcedc160282e6620f409ea46bf6728b35d011ddJeff Brown * </p><p> 2309fcedc160282e6620f409ea46bf6728b35d011ddJeff Brown * In other words, the support library only holds a {@link WeakReference weak reference} 2319fcedc160282e6620f409ea46bf6728b35d011ddJeff Brown * to each media router instance. When there are no remaining strong references to the 2329fcedc160282e6620f409ea46bf6728b35d011ddJeff Brown * media router instance, all of its callbacks will be removed and route discovery 2339fcedc160282e6620f409ea46bf6728b35d011ddJeff Brown * will no longer be performed on its behalf. 2349fcedc160282e6620f409ea46bf6728b35d011ddJeff Brown * </p> 2359fcedc160282e6620f409ea46bf6728b35d011ddJeff Brown * 2369fcedc160282e6620f409ea46bf6728b35d011ddJeff Brown * @return The media router instance for the context. The application must hold 2379fcedc160282e6620f409ea46bf6728b35d011ddJeff Brown * a strong reference to this object as long as it is in use. 238c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown */ 239429c3b17b88ebd8c4512e9179fd9d48333c0979eTor Norbye public static MediaRouter getInstance(@NonNull Context context) { 240c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown if (context == null) { 241c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown throw new IllegalArgumentException("context must not be null"); 242c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown } 243c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown checkCallingThread(); 244c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown 245c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown if (sGlobal == null) { 246c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown sGlobal = new GlobalMediaRouter(context.getApplicationContext()); 247fa326a4649d9d0e8113e315f6c8251fe686abce4Jeff Brown sGlobal.start(); 248c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown } 249c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown return sGlobal.getRouter(context); 250c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown } 251c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown 252c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown /** 253fa326a4649d9d0e8113e315f6c8251fe686abce4Jeff Brown * Gets information about the {@link MediaRouter.RouteInfo routes} currently known to 254fa326a4649d9d0e8113e315f6c8251fe686abce4Jeff Brown * this media router. 255c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown */ 256c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown public List<RouteInfo> getRoutes() { 257c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown checkCallingThread(); 258c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown return sGlobal.getRoutes(); 259c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown } 260c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown 261c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown /** 262fa326a4649d9d0e8113e315f6c8251fe686abce4Jeff Brown * Gets information about the {@link MediaRouter.ProviderInfo route providers} 263fa326a4649d9d0e8113e315f6c8251fe686abce4Jeff Brown * currently known to this media router. 264fa326a4649d9d0e8113e315f6c8251fe686abce4Jeff Brown */ 265fa326a4649d9d0e8113e315f6c8251fe686abce4Jeff Brown public List<ProviderInfo> getProviders() { 266fa326a4649d9d0e8113e315f6c8251fe686abce4Jeff Brown checkCallingThread(); 267fa326a4649d9d0e8113e315f6c8251fe686abce4Jeff Brown return sGlobal.getProviders(); 268fa326a4649d9d0e8113e315f6c8251fe686abce4Jeff Brown } 269fa326a4649d9d0e8113e315f6c8251fe686abce4Jeff Brown 270fa326a4649d9d0e8113e315f6c8251fe686abce4Jeff Brown /** 271c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown * Gets the default route for playing media content on the system. 272c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown * <p> 273c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown * The system always provides a default route. 274c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown * </p> 275c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown * 276c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown * @return The default route, which is guaranteed to never be null. 277c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown */ 278429c3b17b88ebd8c4512e9179fd9d48333c0979eTor Norbye @NonNull 279c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown public RouteInfo getDefaultRoute() { 280c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown checkCallingThread(); 281c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown return sGlobal.getDefaultRoute(); 282c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown } 283c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown 284c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown /** 285c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown * Gets the currently selected route. 286c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown * <p> 287c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown * The application should examine the route's 288fa326a4649d9d0e8113e315f6c8251fe686abce4Jeff Brown * {@link RouteInfo#getControlFilters media control intent filters} to assess the 289c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown * capabilities of the route before attempting to use it. 290c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown * </p> 291c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown * 292c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown * <h3>Example</h3> 293c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown * <pre> 294c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown * public boolean playMovie() { 295c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown * MediaRouter mediaRouter = MediaRouter.getInstance(context); 296c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown * MediaRouter.RouteInfo route = mediaRouter.getSelectedRoute(); 297c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown * 298c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown * // First try using the remote playback interface, if supported. 299c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown * if (route.supportsControlCategory(MediaControlIntent.CATEGORY_REMOTE_PLAYBACK)) { 300c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown * // The route supports remote playback. 301c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown * // Try to send it the Uri of the movie to play. 302c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown * Intent intent = new Intent(MediaControlIntent.ACTION_PLAY); 303c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown * intent.addCategory(MediaControlIntent.CATEGORY_REMOTE_PLAYBACK); 304c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown * intent.setDataAndType("http://example.com/videos/movie.mp4", "video/mp4"); 305fa326a4649d9d0e8113e315f6c8251fe686abce4Jeff Brown * if (route.supportsControlRequest(intent)) { 306fa326a4649d9d0e8113e315f6c8251fe686abce4Jeff Brown * route.sendControlRequest(intent, null); 307fa326a4649d9d0e8113e315f6c8251fe686abce4Jeff Brown * return true; // sent the request to play the movie 308c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown * } 309c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown * } 310c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown * 311c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown * // If remote playback was not possible, then play locally. 312c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown * if (route.supportsControlCategory(MediaControlIntent.CATEGORY_LIVE_VIDEO)) { 313c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown * // The route supports live video streaming. 314c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown * // Prepare to play content locally in a window or in a presentation. 315c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown * return playMovieInWindow(); 316c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown * } 317c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown * 318c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown * // Neither interface is supported, so we can't play the movie to this route. 319c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown * return false; 320c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown * } 321c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown * </pre> 322c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown * 323c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown * @return The selected route, which is guaranteed to never be null. 324c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown * 325fa326a4649d9d0e8113e315f6c8251fe686abce4Jeff Brown * @see RouteInfo#getControlFilters 326c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown * @see RouteInfo#supportsControlCategory 327c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown * @see RouteInfo#supportsControlRequest 328c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown */ 329429c3b17b88ebd8c4512e9179fd9d48333c0979eTor Norbye @NonNull 330c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown public RouteInfo getSelectedRoute() { 331c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown checkCallingThread(); 332c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown return sGlobal.getSelectedRoute(); 333c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown } 334c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown 335c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown /** 33628520a15611522424b52cf88e4a2dbeb1a9be42bJeff Brown * Returns the selected route if it matches the specified selector, otherwise 33728520a15611522424b52cf88e4a2dbeb1a9be42bJeff Brown * selects the default route and returns it. 33828520a15611522424b52cf88e4a2dbeb1a9be42bJeff Brown * 33928520a15611522424b52cf88e4a2dbeb1a9be42bJeff Brown * @param selector The selector to match. 34028520a15611522424b52cf88e4a2dbeb1a9be42bJeff Brown * @return The previously selected route if it matched the selector, otherwise the 34128520a15611522424b52cf88e4a2dbeb1a9be42bJeff Brown * newly selected default route which is guaranteed to never be null. 34228520a15611522424b52cf88e4a2dbeb1a9be42bJeff Brown * 34311417b1cfde8f1749905f2d735623af9214148afJeff Brown * @see MediaRouteSelector 34428520a15611522424b52cf88e4a2dbeb1a9be42bJeff Brown * @see RouteInfo#matchesSelector 34528520a15611522424b52cf88e4a2dbeb1a9be42bJeff Brown * @see RouteInfo#isDefault 34628520a15611522424b52cf88e4a2dbeb1a9be42bJeff Brown */ 347429c3b17b88ebd8c4512e9179fd9d48333c0979eTor Norbye @NonNull 348429c3b17b88ebd8c4512e9179fd9d48333c0979eTor Norbye public RouteInfo updateSelectedRoute(@NonNull MediaRouteSelector selector) { 34928520a15611522424b52cf88e4a2dbeb1a9be42bJeff Brown if (selector == null) { 35028520a15611522424b52cf88e4a2dbeb1a9be42bJeff Brown throw new IllegalArgumentException("selector must not be null"); 35128520a15611522424b52cf88e4a2dbeb1a9be42bJeff Brown } 35228520a15611522424b52cf88e4a2dbeb1a9be42bJeff Brown checkCallingThread(); 35328520a15611522424b52cf88e4a2dbeb1a9be42bJeff Brown 35411417b1cfde8f1749905f2d735623af9214148afJeff Brown if (DEBUG) { 35511417b1cfde8f1749905f2d735623af9214148afJeff Brown Log.d(TAG, "updateSelectedRoute: " + selector); 35611417b1cfde8f1749905f2d735623af9214148afJeff Brown } 35728520a15611522424b52cf88e4a2dbeb1a9be42bJeff Brown RouteInfo route = sGlobal.getSelectedRoute(); 35811417b1cfde8f1749905f2d735623af9214148afJeff Brown if (!route.isDefault() && !route.matchesSelector(selector)) { 35928520a15611522424b52cf88e4a2dbeb1a9be42bJeff Brown route = sGlobal.getDefaultRoute(); 36028520a15611522424b52cf88e4a2dbeb1a9be42bJeff Brown sGlobal.selectRoute(route); 36128520a15611522424b52cf88e4a2dbeb1a9be42bJeff Brown } 36228520a15611522424b52cf88e4a2dbeb1a9be42bJeff Brown return route; 36328520a15611522424b52cf88e4a2dbeb1a9be42bJeff Brown } 36428520a15611522424b52cf88e4a2dbeb1a9be42bJeff Brown 36528520a15611522424b52cf88e4a2dbeb1a9be42bJeff Brown /** 366c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown * Selects the specified route. 367c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown * 368c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown * @param route The route to select. 369c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown */ 370429c3b17b88ebd8c4512e9179fd9d48333c0979eTor Norbye public void selectRoute(@NonNull RouteInfo route) { 371c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown if (route == null) { 372c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown throw new IllegalArgumentException("route must not be null"); 373c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown } 374c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown checkCallingThread(); 375c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown 37611417b1cfde8f1749905f2d735623af9214148afJeff Brown if (DEBUG) { 37711417b1cfde8f1749905f2d735623af9214148afJeff Brown Log.d(TAG, "selectRoute: " + route); 37811417b1cfde8f1749905f2d735623af9214148afJeff Brown } 379c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown sGlobal.selectRoute(route); 380c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown } 381c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown 382c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown /** 38394be6100218126ce6a08bf1f56209578500b361fRoboErik * Unselects the current round and selects the default route instead. 38494be6100218126ce6a08bf1f56209578500b361fRoboErik * <p> 38594be6100218126ce6a08bf1f56209578500b361fRoboErik * The reason given must be one of: 38694be6100218126ce6a08bf1f56209578500b361fRoboErik * <ul> 38794be6100218126ce6a08bf1f56209578500b361fRoboErik * <li>{@link MediaRouter#UNSELECT_REASON_UNKNOWN}</li> 38894be6100218126ce6a08bf1f56209578500b361fRoboErik * <li>{@link MediaRouter#UNSELECT_REASON_DISCONNECTED}</li> 38994be6100218126ce6a08bf1f56209578500b361fRoboErik * <li>{@link MediaRouter#UNSELECT_REASON_STOPPED}</li> 39094be6100218126ce6a08bf1f56209578500b361fRoboErik * <li>{@link MediaRouter#UNSELECT_REASON_ROUTE_CHANGED}</li> 39194be6100218126ce6a08bf1f56209578500b361fRoboErik * </ul> 39294be6100218126ce6a08bf1f56209578500b361fRoboErik * 39394be6100218126ce6a08bf1f56209578500b361fRoboErik * @param reason The reason for disconnecting the current route. 39494be6100218126ce6a08bf1f56209578500b361fRoboErik */ 39594be6100218126ce6a08bf1f56209578500b361fRoboErik public void unselect(int reason) { 39694be6100218126ce6a08bf1f56209578500b361fRoboErik if (reason < MediaRouter.UNSELECT_REASON_UNKNOWN || 39794be6100218126ce6a08bf1f56209578500b361fRoboErik reason > MediaRouter.UNSELECT_REASON_ROUTE_CHANGED) { 39894be6100218126ce6a08bf1f56209578500b361fRoboErik throw new IllegalArgumentException("Unsupported reason to unselect route"); 39994be6100218126ce6a08bf1f56209578500b361fRoboErik } 40094be6100218126ce6a08bf1f56209578500b361fRoboErik checkCallingThread(); 40194be6100218126ce6a08bf1f56209578500b361fRoboErik 40294be6100218126ce6a08bf1f56209578500b361fRoboErik sGlobal.selectRoute(getDefaultRoute(), reason); 40394be6100218126ce6a08bf1f56209578500b361fRoboErik } 40494be6100218126ce6a08bf1f56209578500b361fRoboErik 40594be6100218126ce6a08bf1f56209578500b361fRoboErik /** 406d11aa1784335270b8d85e385f2c8be79ee6a586cJeff Brown * Returns true if there is a route that matches the specified selector. 40711417b1cfde8f1749905f2d735623af9214148afJeff Brown * <p> 40894be6100218126ce6a08bf1f56209578500b361fRoboErik * This method returns true if there are any available routes that match the 40994be6100218126ce6a08bf1f56209578500b361fRoboErik * selector regardless of whether they are enabled or disabled. If the 410d11aa1784335270b8d85e385f2c8be79ee6a586cJeff Brown * {@link #AVAILABILITY_FLAG_IGNORE_DEFAULT_ROUTE} flag is specified, then 411d11aa1784335270b8d85e385f2c8be79ee6a586cJeff Brown * the method will only consider non-default routes. 41294be6100218126ce6a08bf1f56209578500b361fRoboErik * </p> 41394be6100218126ce6a08bf1f56209578500b361fRoboErik * <p class="note"> 414fb81e09002d1d611168b204fc50d259fc15f27b2Jeff Brown * On {@link ActivityManager#isLowRamDevice low-RAM devices} this method 415fb81e09002d1d611168b204fc50d259fc15f27b2Jeff Brown * will return true if it is possible to discover a matching route even if 416fb81e09002d1d611168b204fc50d259fc15f27b2Jeff Brown * discovery is not in progress or if no matching route has yet been found. 417fb81e09002d1d611168b204fc50d259fc15f27b2Jeff Brown * Use {@link #AVAILABILITY_FLAG_REQUIRE_MATCH} to require an actual match. 41811417b1cfde8f1749905f2d735623af9214148afJeff Brown * </p> 419c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown * 42011417b1cfde8f1749905f2d735623af9214148afJeff Brown * @param selector The selector to match. 42194be6100218126ce6a08bf1f56209578500b361fRoboErik * @param flags Flags to control the determination of whether a route may be 42294be6100218126ce6a08bf1f56209578500b361fRoboErik * available. May be zero or some combination of 42394be6100218126ce6a08bf1f56209578500b361fRoboErik * {@link #AVAILABILITY_FLAG_IGNORE_DEFAULT_ROUTE} and 42494be6100218126ce6a08bf1f56209578500b361fRoboErik * {@link #AVAILABILITY_FLAG_REQUIRE_MATCH}. 42511417b1cfde8f1749905f2d735623af9214148afJeff Brown * @return True if a matching route may be available. 426c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown */ 427429c3b17b88ebd8c4512e9179fd9d48333c0979eTor Norbye public boolean isRouteAvailable(@NonNull MediaRouteSelector selector, int flags) { 42811417b1cfde8f1749905f2d735623af9214148afJeff Brown if (selector == null) { 42911417b1cfde8f1749905f2d735623af9214148afJeff Brown throw new IllegalArgumentException("selector must not be null"); 430c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown } 431c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown checkCallingThread(); 432c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown 43311417b1cfde8f1749905f2d735623af9214148afJeff Brown return sGlobal.isRouteAvailable(selector, flags); 434c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown } 435c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown 436c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown /** 43711417b1cfde8f1749905f2d735623af9214148afJeff Brown * Registers a callback to discover routes that match the selector and to receive 43811417b1cfde8f1749905f2d735623af9214148afJeff Brown * events when they change. 43911417b1cfde8f1749905f2d735623af9214148afJeff Brown * <p> 44011417b1cfde8f1749905f2d735623af9214148afJeff Brown * This is a convenience method that has the same effect as calling 44111417b1cfde8f1749905f2d735623af9214148afJeff Brown * {@link #addCallback(MediaRouteSelector, Callback, int)} without flags. 44211417b1cfde8f1749905f2d735623af9214148afJeff Brown * </p> 443c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown * 44411417b1cfde8f1749905f2d735623af9214148afJeff Brown * @param selector A route selector that indicates the kinds of routes that the 44511417b1cfde8f1749905f2d735623af9214148afJeff Brown * callback would like to discover. 44611417b1cfde8f1749905f2d735623af9214148afJeff Brown * @param callback The callback to add. 44711417b1cfde8f1749905f2d735623af9214148afJeff Brown * @see #removeCallback 448c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown */ 44911417b1cfde8f1749905f2d735623af9214148afJeff Brown public void addCallback(MediaRouteSelector selector, Callback callback) { 45011417b1cfde8f1749905f2d735623af9214148afJeff Brown addCallback(selector, callback, 0); 451c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown } 452c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown 453c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown /** 45411417b1cfde8f1749905f2d735623af9214148afJeff Brown * Registers a callback to discover routes that match the selector and to receive 45511417b1cfde8f1749905f2d735623af9214148afJeff Brown * events when they change. 45628520a15611522424b52cf88e4a2dbeb1a9be42bJeff Brown * <p> 45711417b1cfde8f1749905f2d735623af9214148afJeff Brown * The selector describes the kinds of routes that the application wants to 45811417b1cfde8f1749905f2d735623af9214148afJeff Brown * discover. For example, if the application wants to use 45911417b1cfde8f1749905f2d735623af9214148afJeff Brown * live audio routes then it should include the 46011417b1cfde8f1749905f2d735623af9214148afJeff Brown * {@link MediaControlIntent#CATEGORY_LIVE_AUDIO live audio media control intent category} 46111417b1cfde8f1749905f2d735623af9214148afJeff Brown * in its selector when it adds a callback to the media router. 46211417b1cfde8f1749905f2d735623af9214148afJeff Brown * The selector may include any number of categories. 46328520a15611522424b52cf88e4a2dbeb1a9be42bJeff Brown * </p><p> 46411417b1cfde8f1749905f2d735623af9214148afJeff Brown * If the callback has already been registered, then the selector is added to 46511417b1cfde8f1749905f2d735623af9214148afJeff Brown * the set of selectors being monitored by the callback. 46611417b1cfde8f1749905f2d735623af9214148afJeff Brown * </p><p> 46711417b1cfde8f1749905f2d735623af9214148afJeff Brown * By default, the callback will only be invoked for events that affect routes 46811417b1cfde8f1749905f2d735623af9214148afJeff Brown * that match the specified selector. Event filtering may be disabled by specifying 46911417b1cfde8f1749905f2d735623af9214148afJeff Brown * the {@link #CALLBACK_FLAG_UNFILTERED_EVENTS} flag when the callback is registered. 470fb81e09002d1d611168b204fc50d259fc15f27b2Jeff Brown * </p><p> 471fb81e09002d1d611168b204fc50d259fc15f27b2Jeff Brown * Applications should use the {@link #isRouteAvailable} method to determine 472fb81e09002d1d611168b204fc50d259fc15f27b2Jeff Brown * whether is it possible to discover a route with the desired capabilities 473fb81e09002d1d611168b204fc50d259fc15f27b2Jeff Brown * and therefore whether the media route button should be shown to the user. 474fb81e09002d1d611168b204fc50d259fc15f27b2Jeff Brown * </p><p> 475fb81e09002d1d611168b204fc50d259fc15f27b2Jeff Brown * The {@link #CALLBACK_FLAG_REQUEST_DISCOVERY} flag should be used while the application 476fb81e09002d1d611168b204fc50d259fc15f27b2Jeff Brown * is in the foreground to request that passive discovery be performed if there are 477fb81e09002d1d611168b204fc50d259fc15f27b2Jeff Brown * sufficient resources to allow continuous passive discovery. 478fb81e09002d1d611168b204fc50d259fc15f27b2Jeff Brown * On {@link ActivityManager#isLowRamDevice low-RAM devices} this flag will be 479fb81e09002d1d611168b204fc50d259fc15f27b2Jeff Brown * ignored to conserve resources. 480fb81e09002d1d611168b204fc50d259fc15f27b2Jeff Brown * </p><p> 481fb81e09002d1d611168b204fc50d259fc15f27b2Jeff Brown * The {@link #CALLBACK_FLAG_FORCE_DISCOVERY} flag should be used when 482fb81e09002d1d611168b204fc50d259fc15f27b2Jeff Brown * passive discovery absolutely must be performed, even on low-RAM devices. 483fb81e09002d1d611168b204fc50d259fc15f27b2Jeff Brown * This flag has a significant performance impact on low-RAM devices 484fb81e09002d1d611168b204fc50d259fc15f27b2Jeff Brown * since it may cause many media route providers to be started simultaneously. 485fb81e09002d1d611168b204fc50d259fc15f27b2Jeff Brown * It is much better to use {@link #CALLBACK_FLAG_REQUEST_DISCOVERY} instead to avoid 486fb81e09002d1d611168b204fc50d259fc15f27b2Jeff Brown * performing passive discovery on these devices altogether. 487fb81e09002d1d611168b204fc50d259fc15f27b2Jeff Brown * </p><p> 488fb81e09002d1d611168b204fc50d259fc15f27b2Jeff Brown * The {@link #CALLBACK_FLAG_PERFORM_ACTIVE_SCAN} flag should be used when the 489fb81e09002d1d611168b204fc50d259fc15f27b2Jeff Brown * media route chooser dialog is showing to confirm the presence of available 490fb81e09002d1d611168b204fc50d259fc15f27b2Jeff Brown * routes that the user may connect to. This flag may use substantially more 491fb81e09002d1d611168b204fc50d259fc15f27b2Jeff Brown * power. 49228520a15611522424b52cf88e4a2dbeb1a9be42bJeff Brown * </p> 49328520a15611522424b52cf88e4a2dbeb1a9be42bJeff Brown * 49428520a15611522424b52cf88e4a2dbeb1a9be42bJeff Brown * <h3>Example</h3> 49528520a15611522424b52cf88e4a2dbeb1a9be42bJeff Brown * <pre> 49611417b1cfde8f1749905f2d735623af9214148afJeff Brown * public class MyActivity extends Activity { 49711417b1cfde8f1749905f2d735623af9214148afJeff Brown * private MediaRouter mRouter; 49811417b1cfde8f1749905f2d735623af9214148afJeff Brown * private MediaRouter.Callback mCallback; 49911417b1cfde8f1749905f2d735623af9214148afJeff Brown * private MediaRouteSelector mSelector; 50028520a15611522424b52cf88e4a2dbeb1a9be42bJeff Brown * 50111417b1cfde8f1749905f2d735623af9214148afJeff Brown * protected void onCreate(Bundle savedInstanceState) { 50211417b1cfde8f1749905f2d735623af9214148afJeff Brown * super.onCreate(savedInstanceState); 50311417b1cfde8f1749905f2d735623af9214148afJeff Brown * 50411417b1cfde8f1749905f2d735623af9214148afJeff Brown * mRouter = Mediarouter.getInstance(this); 50511417b1cfde8f1749905f2d735623af9214148afJeff Brown * mCallback = new MyCallback(); 50611417b1cfde8f1749905f2d735623af9214148afJeff Brown * mSelector = new MediaRouteSelector.Builder() 50711417b1cfde8f1749905f2d735623af9214148afJeff Brown * .addControlCategory(MediaControlIntent.CATEGORY_LIVE_AUDIO) 50811417b1cfde8f1749905f2d735623af9214148afJeff Brown * .addControlCategory(MediaControlIntent.CATEGORY_REMOTE_PLAYBACK) 50911417b1cfde8f1749905f2d735623af9214148afJeff Brown * .build(); 51028520a15611522424b52cf88e4a2dbeb1a9be42bJeff Brown * } 51128520a15611522424b52cf88e4a2dbeb1a9be42bJeff Brown * 512f03da4a9e6cc02251c2f804eb6f25da61821d6a7Jeff Brown * // Add the callback on start to tell the media router what kinds of routes 51311417b1cfde8f1749905f2d735623af9214148afJeff Brown * // the application is interested in so that it can try to discover suitable ones. 514f03da4a9e6cc02251c2f804eb6f25da61821d6a7Jeff Brown * public void onStart() { 515f03da4a9e6cc02251c2f804eb6f25da61821d6a7Jeff Brown * super.onStart(); 51611417b1cfde8f1749905f2d735623af9214148afJeff Brown * 517f03da4a9e6cc02251c2f804eb6f25da61821d6a7Jeff Brown * mediaRouter.addCallback(mSelector, mCallback, 518f03da4a9e6cc02251c2f804eb6f25da61821d6a7Jeff Brown * MediaRouter.CALLBACK_FLAG_REQUEST_DISCOVERY); 51911417b1cfde8f1749905f2d735623af9214148afJeff Brown * 52011417b1cfde8f1749905f2d735623af9214148afJeff Brown * MediaRouter.RouteInfo route = mediaRouter.updateSelectedRoute(mSelector); 52111417b1cfde8f1749905f2d735623af9214148afJeff Brown * // do something with the route... 52211417b1cfde8f1749905f2d735623af9214148afJeff Brown * } 52311417b1cfde8f1749905f2d735623af9214148afJeff Brown * 524f03da4a9e6cc02251c2f804eb6f25da61821d6a7Jeff Brown * // Remove the selector on stop to tell the media router that it no longer 52511417b1cfde8f1749905f2d735623af9214148afJeff Brown * // needs to invest effort trying to discover routes of these kinds for now. 526f03da4a9e6cc02251c2f804eb6f25da61821d6a7Jeff Brown * public void onStop() { 527f03da4a9e6cc02251c2f804eb6f25da61821d6a7Jeff Brown * super.onStop(); 52811417b1cfde8f1749905f2d735623af9214148afJeff Brown * 52911417b1cfde8f1749905f2d735623af9214148afJeff Brown * mediaRouter.removeCallback(mCallback); 53011417b1cfde8f1749905f2d735623af9214148afJeff Brown * } 53111417b1cfde8f1749905f2d735623af9214148afJeff Brown * 53211417b1cfde8f1749905f2d735623af9214148afJeff Brown * private final class MyCallback extends MediaRouter.Callback { 53311417b1cfde8f1749905f2d735623af9214148afJeff Brown * // Implement callback methods as needed. 53428520a15611522424b52cf88e4a2dbeb1a9be42bJeff Brown * } 53528520a15611522424b52cf88e4a2dbeb1a9be42bJeff Brown * } 53628520a15611522424b52cf88e4a2dbeb1a9be42bJeff Brown * </pre> 53728520a15611522424b52cf88e4a2dbeb1a9be42bJeff Brown * 53811417b1cfde8f1749905f2d735623af9214148afJeff Brown * @param selector A route selector that indicates the kinds of routes that the 53911417b1cfde8f1749905f2d735623af9214148afJeff Brown * callback would like to discover. 54011417b1cfde8f1749905f2d735623af9214148afJeff Brown * @param callback The callback to add. 54111417b1cfde8f1749905f2d735623af9214148afJeff Brown * @param flags Flags to control the behavior of the callback. 542f03da4a9e6cc02251c2f804eb6f25da61821d6a7Jeff Brown * May be zero or a combination of {@link #CALLBACK_FLAG_PERFORM_ACTIVE_SCAN} and 54311417b1cfde8f1749905f2d735623af9214148afJeff Brown * {@link #CALLBACK_FLAG_UNFILTERED_EVENTS}. 54411417b1cfde8f1749905f2d735623af9214148afJeff Brown * @see #removeCallback 54528520a15611522424b52cf88e4a2dbeb1a9be42bJeff Brown */ 546429c3b17b88ebd8c4512e9179fd9d48333c0979eTor Norbye public void addCallback(@NonNull MediaRouteSelector selector, @NonNull Callback callback, 547429c3b17b88ebd8c4512e9179fd9d48333c0979eTor Norbye @CallbackFlags int flags) { 54828520a15611522424b52cf88e4a2dbeb1a9be42bJeff Brown if (selector == null) { 54928520a15611522424b52cf88e4a2dbeb1a9be42bJeff Brown throw new IllegalArgumentException("selector must not be null"); 55028520a15611522424b52cf88e4a2dbeb1a9be42bJeff Brown } 55111417b1cfde8f1749905f2d735623af9214148afJeff Brown if (callback == null) { 55211417b1cfde8f1749905f2d735623af9214148afJeff Brown throw new IllegalArgumentException("callback must not be null"); 55311417b1cfde8f1749905f2d735623af9214148afJeff Brown } 55428520a15611522424b52cf88e4a2dbeb1a9be42bJeff Brown checkCallingThread(); 55528520a15611522424b52cf88e4a2dbeb1a9be42bJeff Brown 55611417b1cfde8f1749905f2d735623af9214148afJeff Brown if (DEBUG) { 55711417b1cfde8f1749905f2d735623af9214148afJeff Brown Log.d(TAG, "addCallback: selector=" + selector 55811417b1cfde8f1749905f2d735623af9214148afJeff Brown + ", callback=" + callback + ", flags=" + Integer.toHexString(flags)); 55911417b1cfde8f1749905f2d735623af9214148afJeff Brown } 56011417b1cfde8f1749905f2d735623af9214148afJeff Brown 56111417b1cfde8f1749905f2d735623af9214148afJeff Brown CallbackRecord record; 56211417b1cfde8f1749905f2d735623af9214148afJeff Brown int index = findCallbackRecord(callback); 56311417b1cfde8f1749905f2d735623af9214148afJeff Brown if (index < 0) { 5649fcedc160282e6620f409ea46bf6728b35d011ddJeff Brown record = new CallbackRecord(this, callback); 56511417b1cfde8f1749905f2d735623af9214148afJeff Brown mCallbackRecords.add(record); 56611417b1cfde8f1749905f2d735623af9214148afJeff Brown } else { 56711417b1cfde8f1749905f2d735623af9214148afJeff Brown record = mCallbackRecords.get(index); 56811417b1cfde8f1749905f2d735623af9214148afJeff Brown } 56911417b1cfde8f1749905f2d735623af9214148afJeff Brown boolean updateNeeded = false; 57011417b1cfde8f1749905f2d735623af9214148afJeff Brown if ((flags & ~record.mFlags) != 0) { 57111417b1cfde8f1749905f2d735623af9214148afJeff Brown record.mFlags |= flags; 57211417b1cfde8f1749905f2d735623af9214148afJeff Brown updateNeeded = true; 57311417b1cfde8f1749905f2d735623af9214148afJeff Brown } 57411417b1cfde8f1749905f2d735623af9214148afJeff Brown if (!record.mSelector.contains(selector)) { 57511417b1cfde8f1749905f2d735623af9214148afJeff Brown record.mSelector = new MediaRouteSelector.Builder(record.mSelector) 57611417b1cfde8f1749905f2d735623af9214148afJeff Brown .addSelector(selector) 57711417b1cfde8f1749905f2d735623af9214148afJeff Brown .build(); 57811417b1cfde8f1749905f2d735623af9214148afJeff Brown updateNeeded = true; 57911417b1cfde8f1749905f2d735623af9214148afJeff Brown } 58011417b1cfde8f1749905f2d735623af9214148afJeff Brown if (updateNeeded) { 58111417b1cfde8f1749905f2d735623af9214148afJeff Brown sGlobal.updateDiscoveryRequest(); 58228520a15611522424b52cf88e4a2dbeb1a9be42bJeff Brown } 58328520a15611522424b52cf88e4a2dbeb1a9be42bJeff Brown } 58428520a15611522424b52cf88e4a2dbeb1a9be42bJeff Brown 58528520a15611522424b52cf88e4a2dbeb1a9be42bJeff Brown /** 58611417b1cfde8f1749905f2d735623af9214148afJeff Brown * Removes the specified callback. It will no longer receive events about 58711417b1cfde8f1749905f2d735623af9214148afJeff Brown * changes to media routes. 58828520a15611522424b52cf88e4a2dbeb1a9be42bJeff Brown * 58911417b1cfde8f1749905f2d735623af9214148afJeff Brown * @param callback The callback to remove. 59011417b1cfde8f1749905f2d735623af9214148afJeff Brown * @see #addCallback 59128520a15611522424b52cf88e4a2dbeb1a9be42bJeff Brown */ 592429c3b17b88ebd8c4512e9179fd9d48333c0979eTor Norbye public void removeCallback(@NonNull Callback callback) { 59311417b1cfde8f1749905f2d735623af9214148afJeff Brown if (callback == null) { 59411417b1cfde8f1749905f2d735623af9214148afJeff Brown throw new IllegalArgumentException("callback must not be null"); 59528520a15611522424b52cf88e4a2dbeb1a9be42bJeff Brown } 59628520a15611522424b52cf88e4a2dbeb1a9be42bJeff Brown checkCallingThread(); 59728520a15611522424b52cf88e4a2dbeb1a9be42bJeff Brown 59811417b1cfde8f1749905f2d735623af9214148afJeff Brown if (DEBUG) { 59911417b1cfde8f1749905f2d735623af9214148afJeff Brown Log.d(TAG, "removeCallback: callback=" + callback); 60011417b1cfde8f1749905f2d735623af9214148afJeff Brown } 60111417b1cfde8f1749905f2d735623af9214148afJeff Brown 60211417b1cfde8f1749905f2d735623af9214148afJeff Brown int index = findCallbackRecord(callback); 60311417b1cfde8f1749905f2d735623af9214148afJeff Brown if (index >= 0) { 60411417b1cfde8f1749905f2d735623af9214148afJeff Brown mCallbackRecords.remove(index); 60511417b1cfde8f1749905f2d735623af9214148afJeff Brown sGlobal.updateDiscoveryRequest(); 60628520a15611522424b52cf88e4a2dbeb1a9be42bJeff Brown } 60728520a15611522424b52cf88e4a2dbeb1a9be42bJeff Brown } 60828520a15611522424b52cf88e4a2dbeb1a9be42bJeff Brown 60911417b1cfde8f1749905f2d735623af9214148afJeff Brown private int findCallbackRecord(Callback callback) { 61011417b1cfde8f1749905f2d735623af9214148afJeff Brown final int count = mCallbackRecords.size(); 61111417b1cfde8f1749905f2d735623af9214148afJeff Brown for (int i = 0; i < count; i++) { 61211417b1cfde8f1749905f2d735623af9214148afJeff Brown if (mCallbackRecords.get(i).mCallback == callback) { 61311417b1cfde8f1749905f2d735623af9214148afJeff Brown return i; 61411417b1cfde8f1749905f2d735623af9214148afJeff Brown } 61511417b1cfde8f1749905f2d735623af9214148afJeff Brown } 61611417b1cfde8f1749905f2d735623af9214148afJeff Brown return -1; 61711417b1cfde8f1749905f2d735623af9214148afJeff Brown } 61811417b1cfde8f1749905f2d735623af9214148afJeff Brown 61928520a15611522424b52cf88e4a2dbeb1a9be42bJeff Brown /** 6209942d40d0d952b03b583fe66f434676793697aa2Jeff Brown * Registers a media route provider within this application process. 6219942d40d0d952b03b583fe66f434676793697aa2Jeff Brown * <p> 6229942d40d0d952b03b583fe66f434676793697aa2Jeff Brown * The provider will be added to the list of providers that all {@link MediaRouter} 6239942d40d0d952b03b583fe66f434676793697aa2Jeff Brown * instances within this process can use to discover routes. 6249942d40d0d952b03b583fe66f434676793697aa2Jeff Brown * </p> 625c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown * 626fa326a4649d9d0e8113e315f6c8251fe686abce4Jeff Brown * @param providerInstance The media route provider instance to add. 627c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown * 628c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown * @see MediaRouteProvider 62928520a15611522424b52cf88e4a2dbeb1a9be42bJeff Brown * @see #removeCallback 630c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown */ 631429c3b17b88ebd8c4512e9179fd9d48333c0979eTor Norbye public void addProvider(@NonNull MediaRouteProvider providerInstance) { 632fa326a4649d9d0e8113e315f6c8251fe686abce4Jeff Brown if (providerInstance == null) { 633fa326a4649d9d0e8113e315f6c8251fe686abce4Jeff Brown throw new IllegalArgumentException("providerInstance must not be null"); 634c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown } 635c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown checkCallingThread(); 636c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown 63711417b1cfde8f1749905f2d735623af9214148afJeff Brown if (DEBUG) { 63811417b1cfde8f1749905f2d735623af9214148afJeff Brown Log.d(TAG, "addProvider: " + providerInstance); 63911417b1cfde8f1749905f2d735623af9214148afJeff Brown } 640fa326a4649d9d0e8113e315f6c8251fe686abce4Jeff Brown sGlobal.addProvider(providerInstance); 641c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown } 642c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown 643c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown /** 6449942d40d0d952b03b583fe66f434676793697aa2Jeff Brown * Unregisters a media route provider within this application process. 6459942d40d0d952b03b583fe66f434676793697aa2Jeff Brown * <p> 6469942d40d0d952b03b583fe66f434676793697aa2Jeff Brown * The provider will be removed from the list of providers that all {@link MediaRouter} 6479942d40d0d952b03b583fe66f434676793697aa2Jeff Brown * instances within this process can use to discover routes. 6489942d40d0d952b03b583fe66f434676793697aa2Jeff Brown * </p> 649c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown * 650fa326a4649d9d0e8113e315f6c8251fe686abce4Jeff Brown * @param providerInstance The media route provider instance to remove. 651c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown * 652c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown * @see MediaRouteProvider 65328520a15611522424b52cf88e4a2dbeb1a9be42bJeff Brown * @see #addCallback 654c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown */ 655429c3b17b88ebd8c4512e9179fd9d48333c0979eTor Norbye public void removeProvider(@NonNull MediaRouteProvider providerInstance) { 656fa326a4649d9d0e8113e315f6c8251fe686abce4Jeff Brown if (providerInstance == null) { 657fa326a4649d9d0e8113e315f6c8251fe686abce4Jeff Brown throw new IllegalArgumentException("providerInstance must not be null"); 658c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown } 659c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown checkCallingThread(); 660c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown 66111417b1cfde8f1749905f2d735623af9214148afJeff Brown if (DEBUG) { 66211417b1cfde8f1749905f2d735623af9214148afJeff Brown Log.d(TAG, "removeProvider: " + providerInstance); 66328520a15611522424b52cf88e4a2dbeb1a9be42bJeff Brown } 66411417b1cfde8f1749905f2d735623af9214148afJeff Brown sGlobal.removeProvider(providerInstance); 66528520a15611522424b52cf88e4a2dbeb1a9be42bJeff Brown } 66628520a15611522424b52cf88e4a2dbeb1a9be42bJeff Brown 66728520a15611522424b52cf88e4a2dbeb1a9be42bJeff Brown /** 668567dbb1c7f8a0cb3c4340ad607b6d833fc1b2270Jeff Brown * Adds a remote control client to enable remote control of the volume 669567dbb1c7f8a0cb3c4340ad607b6d833fc1b2270Jeff Brown * of the selected route. 670567dbb1c7f8a0cb3c4340ad607b6d833fc1b2270Jeff Brown * <p> 671567dbb1c7f8a0cb3c4340ad607b6d833fc1b2270Jeff Brown * The remote control client must have previously been registered with 672567dbb1c7f8a0cb3c4340ad607b6d833fc1b2270Jeff Brown * the audio manager using the {@link android.media.AudioManager#registerRemoteControlClient 673567dbb1c7f8a0cb3c4340ad607b6d833fc1b2270Jeff Brown * AudioManager.registerRemoteControlClient} method. 674567dbb1c7f8a0cb3c4340ad607b6d833fc1b2270Jeff Brown * </p> 675567dbb1c7f8a0cb3c4340ad607b6d833fc1b2270Jeff Brown * 676567dbb1c7f8a0cb3c4340ad607b6d833fc1b2270Jeff Brown * @param remoteControlClient The {@link android.media.RemoteControlClient} to register. 677567dbb1c7f8a0cb3c4340ad607b6d833fc1b2270Jeff Brown */ 678429c3b17b88ebd8c4512e9179fd9d48333c0979eTor Norbye public void addRemoteControlClient(@NonNull Object remoteControlClient) { 679567dbb1c7f8a0cb3c4340ad607b6d833fc1b2270Jeff Brown if (remoteControlClient == null) { 680567dbb1c7f8a0cb3c4340ad607b6d833fc1b2270Jeff Brown throw new IllegalArgumentException("remoteControlClient must not be null"); 681567dbb1c7f8a0cb3c4340ad607b6d833fc1b2270Jeff Brown } 682567dbb1c7f8a0cb3c4340ad607b6d833fc1b2270Jeff Brown checkCallingThread(); 683567dbb1c7f8a0cb3c4340ad607b6d833fc1b2270Jeff Brown 684567dbb1c7f8a0cb3c4340ad607b6d833fc1b2270Jeff Brown if (DEBUG) { 685567dbb1c7f8a0cb3c4340ad607b6d833fc1b2270Jeff Brown Log.d(TAG, "addRemoteControlClient: " + remoteControlClient); 686567dbb1c7f8a0cb3c4340ad607b6d833fc1b2270Jeff Brown } 687567dbb1c7f8a0cb3c4340ad607b6d833fc1b2270Jeff Brown sGlobal.addRemoteControlClient(remoteControlClient); 688567dbb1c7f8a0cb3c4340ad607b6d833fc1b2270Jeff Brown } 689567dbb1c7f8a0cb3c4340ad607b6d833fc1b2270Jeff Brown 690567dbb1c7f8a0cb3c4340ad607b6d833fc1b2270Jeff Brown /** 691567dbb1c7f8a0cb3c4340ad607b6d833fc1b2270Jeff Brown * Removes a remote control client. 692567dbb1c7f8a0cb3c4340ad607b6d833fc1b2270Jeff Brown * 693bbcdf78e350d58ecd6baa75e282d4908d3129fe2RoboErik * @param remoteControlClient The {@link android.media.RemoteControlClient} 694bbcdf78e350d58ecd6baa75e282d4908d3129fe2RoboErik * to unregister. 695567dbb1c7f8a0cb3c4340ad607b6d833fc1b2270Jeff Brown */ 696429c3b17b88ebd8c4512e9179fd9d48333c0979eTor Norbye public void removeRemoteControlClient(@NonNull Object remoteControlClient) { 697567dbb1c7f8a0cb3c4340ad607b6d833fc1b2270Jeff Brown if (remoteControlClient == null) { 698567dbb1c7f8a0cb3c4340ad607b6d833fc1b2270Jeff Brown throw new IllegalArgumentException("remoteControlClient must not be null"); 699567dbb1c7f8a0cb3c4340ad607b6d833fc1b2270Jeff Brown } 700567dbb1c7f8a0cb3c4340ad607b6d833fc1b2270Jeff Brown 701567dbb1c7f8a0cb3c4340ad607b6d833fc1b2270Jeff Brown if (DEBUG) { 702567dbb1c7f8a0cb3c4340ad607b6d833fc1b2270Jeff Brown Log.d(TAG, "removeRemoteControlClient: " + remoteControlClient); 703567dbb1c7f8a0cb3c4340ad607b6d833fc1b2270Jeff Brown } 704567dbb1c7f8a0cb3c4340ad607b6d833fc1b2270Jeff Brown sGlobal.removeRemoteControlClient(remoteControlClient); 705567dbb1c7f8a0cb3c4340ad607b6d833fc1b2270Jeff Brown } 706567dbb1c7f8a0cb3c4340ad607b6d833fc1b2270Jeff Brown 707567dbb1c7f8a0cb3c4340ad607b6d833fc1b2270Jeff Brown /** 708bbcdf78e350d58ecd6baa75e282d4908d3129fe2RoboErik * Sets the media session to enable remote control of the volume of the 709bbcdf78e350d58ecd6baa75e282d4908d3129fe2RoboErik * selected route. This should be used instead of 710bbcdf78e350d58ecd6baa75e282d4908d3129fe2RoboErik * {@link #addRemoteControlClient} when using media sessions. Set the 711bbcdf78e350d58ecd6baa75e282d4908d3129fe2RoboErik * session to null to clear it. 712bbcdf78e350d58ecd6baa75e282d4908d3129fe2RoboErik * 713bbcdf78e350d58ecd6baa75e282d4908d3129fe2RoboErik * @param mediaSession The {@link android.media.session.MediaSession} to 714bbcdf78e350d58ecd6baa75e282d4908d3129fe2RoboErik * use. 715bbcdf78e350d58ecd6baa75e282d4908d3129fe2RoboErik */ 716bbcdf78e350d58ecd6baa75e282d4908d3129fe2RoboErik public void setMediaSession(Object mediaSession) { 717bbcdf78e350d58ecd6baa75e282d4908d3129fe2RoboErik if (DEBUG) { 718bbcdf78e350d58ecd6baa75e282d4908d3129fe2RoboErik Log.d(TAG, "addMediaSession: " + mediaSession); 719bbcdf78e350d58ecd6baa75e282d4908d3129fe2RoboErik } 720bbcdf78e350d58ecd6baa75e282d4908d3129fe2RoboErik sGlobal.setMediaSession(mediaSession); 721bbcdf78e350d58ecd6baa75e282d4908d3129fe2RoboErik } 722bbcdf78e350d58ecd6baa75e282d4908d3129fe2RoboErik 723bbcdf78e350d58ecd6baa75e282d4908d3129fe2RoboErik /** 72494be6100218126ce6a08bf1f56209578500b361fRoboErik * Sets a compat media session to enable remote control of the volume of the 72594be6100218126ce6a08bf1f56209578500b361fRoboErik * selected route. This should be used instead of 72694be6100218126ce6a08bf1f56209578500b361fRoboErik * {@link #addRemoteControlClient} when using {@link MediaSessionCompat}. 72794be6100218126ce6a08bf1f56209578500b361fRoboErik * Set the session to null to clear it. 72894be6100218126ce6a08bf1f56209578500b361fRoboErik * 72994be6100218126ce6a08bf1f56209578500b361fRoboErik * @param mediaSession 73094be6100218126ce6a08bf1f56209578500b361fRoboErik */ 73194be6100218126ce6a08bf1f56209578500b361fRoboErik public void setMediaSessionCompat(MediaSessionCompat mediaSession) { 73294be6100218126ce6a08bf1f56209578500b361fRoboErik if (DEBUG) { 73394be6100218126ce6a08bf1f56209578500b361fRoboErik Log.d(TAG, "addMediaSessionCompat: " + mediaSession); 73494be6100218126ce6a08bf1f56209578500b361fRoboErik } 7355c9469e010106467791b47b0fa83efda84491a21RoboErik sGlobal.setMediaSessionCompat(mediaSession); 73694be6100218126ce6a08bf1f56209578500b361fRoboErik } 73794be6100218126ce6a08bf1f56209578500b361fRoboErik 73894be6100218126ce6a08bf1f56209578500b361fRoboErik public MediaSessionCompat.Token getMediaSessionToken() { 73994be6100218126ce6a08bf1f56209578500b361fRoboErik return sGlobal.getMediaSessionToken(); 74094be6100218126ce6a08bf1f56209578500b361fRoboErik } 74194be6100218126ce6a08bf1f56209578500b361fRoboErik 74294be6100218126ce6a08bf1f56209578500b361fRoboErik /** 743c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown * Ensures that calls into the media router are on the correct thread. 744c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown * It pays to be a little paranoid when global state invariants are at risk. 745c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown */ 746c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown static void checkCallingThread() { 747c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown if (Looper.myLooper() != Looper.getMainLooper()) { 748c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown throw new IllegalStateException("The media router service must only be " 749c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown + "accessed on the application's main thread."); 750c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown } 751c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown } 752c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown 753c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown static <T> boolean equal(T a, T b) { 754c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown return a == b || (a != null && b != null && a.equals(b)); 755c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown } 756c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown 757c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown /** 758c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown * Provides information about a media route. 759c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown * <p> 760fa326a4649d9d0e8113e315f6c8251fe686abce4Jeff Brown * Each media route has a list of {@link MediaControlIntent media control} 761fa326a4649d9d0e8113e315f6c8251fe686abce4Jeff Brown * {@link #getControlFilters intent filters} that describe the capabilities of the 762c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown * route and the manner in which it is used and controlled. 763c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown * </p> 764c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown */ 765e7a694bf5fc4bf417502509a8604fffc7a41d84cJae Seo public static class RouteInfo { 766fa326a4649d9d0e8113e315f6c8251fe686abce4Jeff Brown private final ProviderInfo mProvider; 767c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown private final String mDescriptorId; 768cb63b6ecac9786891514f241dec71695f09d3efbJeff Brown private final String mUniqueId; 769c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown private String mName; 770d63957d28aaabcec588b8cde12eac16414783aebJeff Brown private String mDescription; 771c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown private boolean mEnabled; 77211417b1cfde8f1749905f2d735623af9214148afJeff Brown private boolean mConnecting; 77394be6100218126ce6a08bf1f56209578500b361fRoboErik private boolean mCanDisconnect; 774e7a694bf5fc4bf417502509a8604fffc7a41d84cJae Seo private final ArrayList<IntentFilter> mControlFilters = new ArrayList<>(); 775c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown private int mPlaybackType; 776c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown private int mPlaybackStream; 777c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown private int mVolumeHandling; 778c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown private int mVolume; 779c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown private int mVolumeMax; 780c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown private Display mPresentationDisplay; 781c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown private int mPresentationDisplayId = -1; 782c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown private Bundle mExtras; 78394be6100218126ce6a08bf1f56209578500b361fRoboErik private IntentSender mSettingsIntent; 784e7a694bf5fc4bf417502509a8604fffc7a41d84cJae Seo MediaRouteDescriptor mDescriptor; 785c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown 786429c3b17b88ebd8c4512e9179fd9d48333c0979eTor Norbye /** @hide */ 787429c3b17b88ebd8c4512e9179fd9d48333c0979eTor Norbye @IntDef({PLAYBACK_TYPE_LOCAL,PLAYBACK_TYPE_REMOTE}) 788429c3b17b88ebd8c4512e9179fd9d48333c0979eTor Norbye @Retention(RetentionPolicy.SOURCE) 789429c3b17b88ebd8c4512e9179fd9d48333c0979eTor Norbye private @interface PlaybackType {} 790429c3b17b88ebd8c4512e9179fd9d48333c0979eTor Norbye 791c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown /** 792c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown * The default playback type, "local", indicating the presentation of the media 793c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown * is happening on the same device (e.g. a phone, a tablet) as where it is 794c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown * controlled from. 795c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown * 796c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown * @see #getPlaybackType 797c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown */ 798c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown public static final int PLAYBACK_TYPE_LOCAL = 0; 799c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown 800c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown /** 801c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown * A playback type indicating the presentation of the media is happening on 802c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown * a different device (i.e. the remote device) than where it is controlled from. 803c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown * 804c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown * @see #getPlaybackType 805c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown */ 806c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown public static final int PLAYBACK_TYPE_REMOTE = 1; 807c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown 808429c3b17b88ebd8c4512e9179fd9d48333c0979eTor Norbye /** @hide */ 809429c3b17b88ebd8c4512e9179fd9d48333c0979eTor Norbye @IntDef({PLAYBACK_VOLUME_FIXED,PLAYBACK_VOLUME_VARIABLE}) 810429c3b17b88ebd8c4512e9179fd9d48333c0979eTor Norbye @Retention(RetentionPolicy.SOURCE) 811429c3b17b88ebd8c4512e9179fd9d48333c0979eTor Norbye private @interface PlaybackVolume {} 812429c3b17b88ebd8c4512e9179fd9d48333c0979eTor Norbye 813c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown /** 814c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown * Playback information indicating the playback volume is fixed, i.e. it cannot be 815c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown * controlled from this object. An example of fixed playback volume is a remote player, 816c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown * playing over HDMI where the user prefers to control the volume on the HDMI sink, rather 817c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown * than attenuate at the source. 818c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown * 819c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown * @see #getVolumeHandling 820c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown */ 821c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown public static final int PLAYBACK_VOLUME_FIXED = 0; 822c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown 823c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown /** 824c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown * Playback information indicating the playback volume is variable and can be controlled 825c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown * from this object. 826c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown * 827c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown * @see #getVolumeHandling 828c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown */ 829c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown public static final int PLAYBACK_VOLUME_VARIABLE = 1; 830c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown 831c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown static final int CHANGE_GENERAL = 1 << 0; 832c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown static final int CHANGE_VOLUME = 1 << 1; 833c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown static final int CHANGE_PRESENTATION_DISPLAY = 1 << 2; 834c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown 835cb63b6ecac9786891514f241dec71695f09d3efbJeff Brown RouteInfo(ProviderInfo provider, String descriptorId, String uniqueId) { 836fa326a4649d9d0e8113e315f6c8251fe686abce4Jeff Brown mProvider = provider; 837c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown mDescriptorId = descriptorId; 838cb63b6ecac9786891514f241dec71695f09d3efbJeff Brown mUniqueId = uniqueId; 839c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown } 840c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown 841fa326a4649d9d0e8113e315f6c8251fe686abce4Jeff Brown /** 842fa326a4649d9d0e8113e315f6c8251fe686abce4Jeff Brown * Gets information about the provider of this media route. 843fa326a4649d9d0e8113e315f6c8251fe686abce4Jeff Brown */ 844fa326a4649d9d0e8113e315f6c8251fe686abce4Jeff Brown public ProviderInfo getProvider() { 845fa326a4649d9d0e8113e315f6c8251fe686abce4Jeff Brown return mProvider; 846c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown } 847c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown 848c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown /** 849cb63b6ecac9786891514f241dec71695f09d3efbJeff Brown * Gets the unique id of the route. 850cb63b6ecac9786891514f241dec71695f09d3efbJeff Brown * <p> 851cb63b6ecac9786891514f241dec71695f09d3efbJeff Brown * The route unique id functions as a stable identifier by which the route is known. 852cb63b6ecac9786891514f241dec71695f09d3efbJeff Brown * For example, an application can use this id as a token to remember the 853cb63b6ecac9786891514f241dec71695f09d3efbJeff Brown * selected route across restarts or to communicate its identity to a service. 854cb63b6ecac9786891514f241dec71695f09d3efbJeff Brown * </p> 855cb63b6ecac9786891514f241dec71695f09d3efbJeff Brown * 856cb63b6ecac9786891514f241dec71695f09d3efbJeff Brown * @return The unique id of the route, never null. 857cb63b6ecac9786891514f241dec71695f09d3efbJeff Brown */ 858429c3b17b88ebd8c4512e9179fd9d48333c0979eTor Norbye @NonNull 859cb63b6ecac9786891514f241dec71695f09d3efbJeff Brown public String getId() { 860cb63b6ecac9786891514f241dec71695f09d3efbJeff Brown return mUniqueId; 861cb63b6ecac9786891514f241dec71695f09d3efbJeff Brown } 862cb63b6ecac9786891514f241dec71695f09d3efbJeff Brown 863cb63b6ecac9786891514f241dec71695f09d3efbJeff Brown /** 864d63957d28aaabcec588b8cde12eac16414783aebJeff Brown * Gets the user-visible name of the route. 865d63957d28aaabcec588b8cde12eac16414783aebJeff Brown * <p> 866d63957d28aaabcec588b8cde12eac16414783aebJeff Brown * The route name identifies the destination represented by the route. 867d63957d28aaabcec588b8cde12eac16414783aebJeff Brown * It may be a user-supplied name, an alias, or device serial number. 868d63957d28aaabcec588b8cde12eac16414783aebJeff Brown * </p> 869c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown * 870d63957d28aaabcec588b8cde12eac16414783aebJeff Brown * @return The user-visible name of a media route. This is the string presented 871c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown * to users who may select this as the active route. 872c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown */ 873c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown public String getName() { 874c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown return mName; 875c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown } 876c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown 877c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown /** 878d63957d28aaabcec588b8cde12eac16414783aebJeff Brown * Gets the user-visible description of the route. 879d63957d28aaabcec588b8cde12eac16414783aebJeff Brown * <p> 880d63957d28aaabcec588b8cde12eac16414783aebJeff Brown * The route description describes the kind of destination represented by the route. 881d63957d28aaabcec588b8cde12eac16414783aebJeff Brown * It may be a user-supplied string, a model number or brand of device. 882d63957d28aaabcec588b8cde12eac16414783aebJeff Brown * </p> 883c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown * 884d63957d28aaabcec588b8cde12eac16414783aebJeff Brown * @return The description of the route, or null if none. 885c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown */ 886429c3b17b88ebd8c4512e9179fd9d48333c0979eTor Norbye @Nullable 887d63957d28aaabcec588b8cde12eac16414783aebJeff Brown public String getDescription() { 888d63957d28aaabcec588b8cde12eac16414783aebJeff Brown return mDescription; 889c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown } 890c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown 891c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown /** 892c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown * Returns true if this route is enabled and may be selected. 893c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown * 89411417b1cfde8f1749905f2d735623af9214148afJeff Brown * @return True if this route is enabled. 895c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown */ 896c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown public boolean isEnabled() { 897c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown return mEnabled; 898c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown } 899c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown 900c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown /** 90111417b1cfde8f1749905f2d735623af9214148afJeff Brown * Returns true if the route is in the process of connecting and is not 90211417b1cfde8f1749905f2d735623af9214148afJeff Brown * yet ready for use. 90311417b1cfde8f1749905f2d735623af9214148afJeff Brown * 90411417b1cfde8f1749905f2d735623af9214148afJeff Brown * @return True if this route is in the process of connecting. 90511417b1cfde8f1749905f2d735623af9214148afJeff Brown */ 90611417b1cfde8f1749905f2d735623af9214148afJeff Brown public boolean isConnecting() { 90711417b1cfde8f1749905f2d735623af9214148afJeff Brown return mConnecting; 90811417b1cfde8f1749905f2d735623af9214148afJeff Brown } 90911417b1cfde8f1749905f2d735623af9214148afJeff Brown 91011417b1cfde8f1749905f2d735623af9214148afJeff Brown /** 911fa326a4649d9d0e8113e315f6c8251fe686abce4Jeff Brown * Returns true if this route is currently selected. 912c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown * 91311417b1cfde8f1749905f2d735623af9214148afJeff Brown * @return True if this route is currently selected. 914fa326a4649d9d0e8113e315f6c8251fe686abce4Jeff Brown * 915fa326a4649d9d0e8113e315f6c8251fe686abce4Jeff Brown * @see MediaRouter#getSelectedRoute 916fa326a4649d9d0e8113e315f6c8251fe686abce4Jeff Brown */ 917fa326a4649d9d0e8113e315f6c8251fe686abce4Jeff Brown public boolean isSelected() { 918fa326a4649d9d0e8113e315f6c8251fe686abce4Jeff Brown checkCallingThread(); 919fa326a4649d9d0e8113e315f6c8251fe686abce4Jeff Brown return sGlobal.getSelectedRoute() == this; 920fa326a4649d9d0e8113e315f6c8251fe686abce4Jeff Brown } 921fa326a4649d9d0e8113e315f6c8251fe686abce4Jeff Brown 922fa326a4649d9d0e8113e315f6c8251fe686abce4Jeff Brown /** 923fa326a4649d9d0e8113e315f6c8251fe686abce4Jeff Brown * Returns true if this route is the default route. 924fa326a4649d9d0e8113e315f6c8251fe686abce4Jeff Brown * 92511417b1cfde8f1749905f2d735623af9214148afJeff Brown * @return True if this route is the default route. 926fa326a4649d9d0e8113e315f6c8251fe686abce4Jeff Brown * 927fa326a4649d9d0e8113e315f6c8251fe686abce4Jeff Brown * @see MediaRouter#getDefaultRoute 928fa326a4649d9d0e8113e315f6c8251fe686abce4Jeff Brown */ 929fa326a4649d9d0e8113e315f6c8251fe686abce4Jeff Brown public boolean isDefault() { 930fa326a4649d9d0e8113e315f6c8251fe686abce4Jeff Brown checkCallingThread(); 931fa326a4649d9d0e8113e315f6c8251fe686abce4Jeff Brown return sGlobal.getDefaultRoute() == this; 932fa326a4649d9d0e8113e315f6c8251fe686abce4Jeff Brown } 933fa326a4649d9d0e8113e315f6c8251fe686abce4Jeff Brown 934fa326a4649d9d0e8113e315f6c8251fe686abce4Jeff Brown /** 935fa326a4649d9d0e8113e315f6c8251fe686abce4Jeff Brown * Gets a list of {@link MediaControlIntent media control intent} filters that 936fa326a4649d9d0e8113e315f6c8251fe686abce4Jeff Brown * describe the capabilities of this route and the media control actions that 937fa326a4649d9d0e8113e315f6c8251fe686abce4Jeff Brown * it supports. 938fa326a4649d9d0e8113e315f6c8251fe686abce4Jeff Brown * 939fa326a4649d9d0e8113e315f6c8251fe686abce4Jeff Brown * @return A list of intent filters that specifies the media control intents that 940fa326a4649d9d0e8113e315f6c8251fe686abce4Jeff Brown * this route supports. 941c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown * 942c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown * @see MediaControlIntent 943c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown * @see #supportsControlCategory 944c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown * @see #supportsControlRequest 945c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown */ 946fa326a4649d9d0e8113e315f6c8251fe686abce4Jeff Brown public List<IntentFilter> getControlFilters() { 947fa326a4649d9d0e8113e315f6c8251fe686abce4Jeff Brown return mControlFilters; 948c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown } 949c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown 950c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown /** 95128520a15611522424b52cf88e4a2dbeb1a9be42bJeff Brown * Returns true if the route supports at least one of the capabilities 95228520a15611522424b52cf88e4a2dbeb1a9be42bJeff Brown * described by a media route selector. 95328520a15611522424b52cf88e4a2dbeb1a9be42bJeff Brown * 95428520a15611522424b52cf88e4a2dbeb1a9be42bJeff Brown * @param selector The selector that specifies the capabilities to check. 95528520a15611522424b52cf88e4a2dbeb1a9be42bJeff Brown * @return True if the route supports at least one of the capabilities 95628520a15611522424b52cf88e4a2dbeb1a9be42bJeff Brown * described in the media route selector. 95728520a15611522424b52cf88e4a2dbeb1a9be42bJeff Brown */ 958429c3b17b88ebd8c4512e9179fd9d48333c0979eTor Norbye public boolean matchesSelector(@NonNull MediaRouteSelector selector) { 95928520a15611522424b52cf88e4a2dbeb1a9be42bJeff Brown if (selector == null) { 96028520a15611522424b52cf88e4a2dbeb1a9be42bJeff Brown throw new IllegalArgumentException("selector must not be null"); 96128520a15611522424b52cf88e4a2dbeb1a9be42bJeff Brown } 96228520a15611522424b52cf88e4a2dbeb1a9be42bJeff Brown checkCallingThread(); 96311417b1cfde8f1749905f2d735623af9214148afJeff Brown return selector.matchesControlFilters(mControlFilters); 96428520a15611522424b52cf88e4a2dbeb1a9be42bJeff Brown } 96528520a15611522424b52cf88e4a2dbeb1a9be42bJeff Brown 96628520a15611522424b52cf88e4a2dbeb1a9be42bJeff Brown /** 967c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown * Returns true if the route supports the specified 968c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown * {@link MediaControlIntent media control} category. 969c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown * <p> 970c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown * Media control categories describe the capabilities of this route 971c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown * such as whether it supports live audio streaming or remote playback. 972c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown * </p> 973c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown * 974c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown * @param category A {@link MediaControlIntent media control} category 975c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown * such as {@link MediaControlIntent#CATEGORY_LIVE_AUDIO}, 976c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown * {@link MediaControlIntent#CATEGORY_LIVE_VIDEO}, 977fa326a4649d9d0e8113e315f6c8251fe686abce4Jeff Brown * {@link MediaControlIntent#CATEGORY_REMOTE_PLAYBACK}, or a provider-defined 978c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown * media control category. 97928520a15611522424b52cf88e4a2dbeb1a9be42bJeff Brown * @return True if the route supports the specified intent category. 980c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown * 981c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown * @see MediaControlIntent 982fa326a4649d9d0e8113e315f6c8251fe686abce4Jeff Brown * @see #getControlFilters 983c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown */ 984429c3b17b88ebd8c4512e9179fd9d48333c0979eTor Norbye public boolean supportsControlCategory(@NonNull String category) { 985c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown if (category == null) { 986c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown throw new IllegalArgumentException("category must not be null"); 987c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown } 988fa326a4649d9d0e8113e315f6c8251fe686abce4Jeff Brown checkCallingThread(); 989c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown 990fa326a4649d9d0e8113e315f6c8251fe686abce4Jeff Brown int count = mControlFilters.size(); 991fa326a4649d9d0e8113e315f6c8251fe686abce4Jeff Brown for (int i = 0; i < count; i++) { 992fa326a4649d9d0e8113e315f6c8251fe686abce4Jeff Brown if (mControlFilters.get(i).hasCategory(category)) { 993fa326a4649d9d0e8113e315f6c8251fe686abce4Jeff Brown return true; 994fa326a4649d9d0e8113e315f6c8251fe686abce4Jeff Brown } 995fa326a4649d9d0e8113e315f6c8251fe686abce4Jeff Brown } 996fa326a4649d9d0e8113e315f6c8251fe686abce4Jeff Brown return false; 997c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown } 998c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown 999c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown /** 1000c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown * Returns true if the route supports the specified 1001a97f1edf7624785c41ec0bfec6fd12c2388d9234Jeff Brown * {@link MediaControlIntent media control} category and action. 1002a97f1edf7624785c41ec0bfec6fd12c2388d9234Jeff Brown * <p> 1003a97f1edf7624785c41ec0bfec6fd12c2388d9234Jeff Brown * Media control actions describe specific requests that an application 1004a97f1edf7624785c41ec0bfec6fd12c2388d9234Jeff Brown * can ask a route to perform. 1005a97f1edf7624785c41ec0bfec6fd12c2388d9234Jeff Brown * </p> 1006a97f1edf7624785c41ec0bfec6fd12c2388d9234Jeff Brown * 1007a97f1edf7624785c41ec0bfec6fd12c2388d9234Jeff Brown * @param category A {@link MediaControlIntent media control} category 1008a97f1edf7624785c41ec0bfec6fd12c2388d9234Jeff Brown * such as {@link MediaControlIntent#CATEGORY_LIVE_AUDIO}, 1009a97f1edf7624785c41ec0bfec6fd12c2388d9234Jeff Brown * {@link MediaControlIntent#CATEGORY_LIVE_VIDEO}, 1010a97f1edf7624785c41ec0bfec6fd12c2388d9234Jeff Brown * {@link MediaControlIntent#CATEGORY_REMOTE_PLAYBACK}, or a provider-defined 1011a97f1edf7624785c41ec0bfec6fd12c2388d9234Jeff Brown * media control category. 1012a97f1edf7624785c41ec0bfec6fd12c2388d9234Jeff Brown * @param action A {@link MediaControlIntent media control} action 1013a97f1edf7624785c41ec0bfec6fd12c2388d9234Jeff Brown * such as {@link MediaControlIntent#ACTION_PLAY}. 1014a97f1edf7624785c41ec0bfec6fd12c2388d9234Jeff Brown * @return True if the route supports the specified intent action. 1015a97f1edf7624785c41ec0bfec6fd12c2388d9234Jeff Brown * 1016a97f1edf7624785c41ec0bfec6fd12c2388d9234Jeff Brown * @see MediaControlIntent 1017a97f1edf7624785c41ec0bfec6fd12c2388d9234Jeff Brown * @see #getControlFilters 1018a97f1edf7624785c41ec0bfec6fd12c2388d9234Jeff Brown */ 1019429c3b17b88ebd8c4512e9179fd9d48333c0979eTor Norbye public boolean supportsControlAction(@NonNull String category, @NonNull String action) { 1020a97f1edf7624785c41ec0bfec6fd12c2388d9234Jeff Brown if (category == null) { 1021a97f1edf7624785c41ec0bfec6fd12c2388d9234Jeff Brown throw new IllegalArgumentException("category must not be null"); 1022a97f1edf7624785c41ec0bfec6fd12c2388d9234Jeff Brown } 1023a97f1edf7624785c41ec0bfec6fd12c2388d9234Jeff Brown if (action == null) { 1024a97f1edf7624785c41ec0bfec6fd12c2388d9234Jeff Brown throw new IllegalArgumentException("action must not be null"); 1025a97f1edf7624785c41ec0bfec6fd12c2388d9234Jeff Brown } 1026a97f1edf7624785c41ec0bfec6fd12c2388d9234Jeff Brown checkCallingThread(); 1027a97f1edf7624785c41ec0bfec6fd12c2388d9234Jeff Brown 1028a97f1edf7624785c41ec0bfec6fd12c2388d9234Jeff Brown int count = mControlFilters.size(); 1029a97f1edf7624785c41ec0bfec6fd12c2388d9234Jeff Brown for (int i = 0; i < count; i++) { 1030a97f1edf7624785c41ec0bfec6fd12c2388d9234Jeff Brown IntentFilter filter = mControlFilters.get(i); 1031a97f1edf7624785c41ec0bfec6fd12c2388d9234Jeff Brown if (filter.hasCategory(category) && filter.hasAction(action)) { 1032a97f1edf7624785c41ec0bfec6fd12c2388d9234Jeff Brown return true; 1033a97f1edf7624785c41ec0bfec6fd12c2388d9234Jeff Brown } 1034a97f1edf7624785c41ec0bfec6fd12c2388d9234Jeff Brown } 1035a97f1edf7624785c41ec0bfec6fd12c2388d9234Jeff Brown return false; 1036a97f1edf7624785c41ec0bfec6fd12c2388d9234Jeff Brown } 1037a97f1edf7624785c41ec0bfec6fd12c2388d9234Jeff Brown 1038a97f1edf7624785c41ec0bfec6fd12c2388d9234Jeff Brown /** 1039a97f1edf7624785c41ec0bfec6fd12c2388d9234Jeff Brown * Returns true if the route supports the specified 1040c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown * {@link MediaControlIntent media control} request. 1041c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown * <p> 1042c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown * Media control requests are used to request the route to perform 104343f79f79a5117550a7dfedf9c65124afd163ea43Jeff Brown * actions such as starting remote playback of a media item. 1044c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown * </p> 1045c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown * 1046c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown * @param intent A {@link MediaControlIntent media control intent}. 1047c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown * @return True if the route can handle the specified intent. 1048c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown * 1049c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown * @see MediaControlIntent 1050fa326a4649d9d0e8113e315f6c8251fe686abce4Jeff Brown * @see #getControlFilters 1051c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown */ 1052429c3b17b88ebd8c4512e9179fd9d48333c0979eTor Norbye public boolean supportsControlRequest(@NonNull Intent intent) { 1053c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown if (intent == null) { 1054c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown throw new IllegalArgumentException("intent must not be null"); 1055c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown } 1056c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown checkCallingThread(); 1057c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown 1058c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown ContentResolver contentResolver = sGlobal.getContentResolver(); 1059fa326a4649d9d0e8113e315f6c8251fe686abce4Jeff Brown int count = mControlFilters.size(); 1060fa326a4649d9d0e8113e315f6c8251fe686abce4Jeff Brown for (int i = 0; i < count; i++) { 1061fa326a4649d9d0e8113e315f6c8251fe686abce4Jeff Brown if (mControlFilters.get(i).match(contentResolver, intent, true, TAG) >= 0) { 1062fa326a4649d9d0e8113e315f6c8251fe686abce4Jeff Brown return true; 1063fa326a4649d9d0e8113e315f6c8251fe686abce4Jeff Brown } 1064fa326a4649d9d0e8113e315f6c8251fe686abce4Jeff Brown } 1065fa326a4649d9d0e8113e315f6c8251fe686abce4Jeff Brown return false; 1066c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown } 1067c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown 1068c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown /** 1069c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown * Sends a {@link MediaControlIntent media control} request to be performed 1070c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown * asynchronously by the route's destination. 1071c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown * <p> 1072c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown * Media control requests are used to request the route to perform 107343f79f79a5117550a7dfedf9c65124afd163ea43Jeff Brown * actions such as starting remote playback of a media item. 1074c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown * </p><p> 1075fa326a4649d9d0e8113e315f6c8251fe686abce4Jeff Brown * This function may only be called on a selected route. Control requests 1076fa326a4649d9d0e8113e315f6c8251fe686abce4Jeff Brown * sent to unselected routes will fail. 1077c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown * </p> 1078c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown * 1079c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown * @param intent A {@link MediaControlIntent media control intent}. 1080c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown * @param callback A {@link ControlRequestCallback} to invoke with the result 1081c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown * of the request, or null if no result is required. 1082c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown * 1083c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown * @see MediaControlIntent 1084c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown */ 1085429c3b17b88ebd8c4512e9179fd9d48333c0979eTor Norbye public void sendControlRequest(@NonNull Intent intent, 1086429c3b17b88ebd8c4512e9179fd9d48333c0979eTor Norbye @Nullable ControlRequestCallback callback) { 1087c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown if (intent == null) { 1088c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown throw new IllegalArgumentException("intent must not be null"); 1089c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown } 1090c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown checkCallingThread(); 1091c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown 1092fa326a4649d9d0e8113e315f6c8251fe686abce4Jeff Brown sGlobal.sendControlRequest(this, intent, callback); 1093c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown } 1094c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown 1095c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown /** 1096c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown * Gets the type of playback associated with this route. 1097c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown * 1098c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown * @return The type of playback associated with this route: {@link #PLAYBACK_TYPE_LOCAL} 1099c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown * or {@link #PLAYBACK_TYPE_REMOTE}. 1100c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown */ 1101429c3b17b88ebd8c4512e9179fd9d48333c0979eTor Norbye @PlaybackType 1102c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown public int getPlaybackType() { 1103c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown return mPlaybackType; 1104c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown } 1105c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown 1106c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown /** 1107350ba6e4a1b5ec28721a098e50eaf6a508eb28f0Jeff Brown * Gets the audio stream over which the playback associated with this route is performed. 1108c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown * 1109c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown * @return The stream over which the playback associated with this route is performed. 1110c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown */ 1111c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown public int getPlaybackStream() { 1112c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown return mPlaybackStream; 1113c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown } 1114c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown 1115c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown /** 1116c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown * Gets information about how volume is handled on the route. 1117c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown * 1118c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown * @return How volume is handled on the route: {@link #PLAYBACK_VOLUME_FIXED} 1119c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown * or {@link #PLAYBACK_VOLUME_VARIABLE}. 1120c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown */ 1121429c3b17b88ebd8c4512e9179fd9d48333c0979eTor Norbye @PlaybackVolume 1122c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown public int getVolumeHandling() { 1123c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown return mVolumeHandling; 1124c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown } 1125c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown 1126c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown /** 1127c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown * Gets the current volume for this route. Depending on the route, this may only 1128c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown * be valid if the route is currently selected. 1129c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown * 1130c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown * @return The volume at which the playback associated with this route is performed. 1131c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown */ 1132c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown public int getVolume() { 1133c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown return mVolume; 1134c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown } 1135c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown 1136c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown /** 1137c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown * Gets the maximum volume at which the playback associated with this route is performed. 1138c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown * 1139c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown * @return The maximum volume at which the playback associated with 1140c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown * this route is performed. 1141c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown */ 1142c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown public int getVolumeMax() { 1143c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown return mVolumeMax; 1144c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown } 1145c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown 1146c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown /** 114794be6100218126ce6a08bf1f56209578500b361fRoboErik * Gets whether this route supports disconnecting without interrupting 114894be6100218126ce6a08bf1f56209578500b361fRoboErik * playback. 114994be6100218126ce6a08bf1f56209578500b361fRoboErik * 115094be6100218126ce6a08bf1f56209578500b361fRoboErik * @return True if this route can disconnect without stopping playback, 115194be6100218126ce6a08bf1f56209578500b361fRoboErik * false otherwise. 115294be6100218126ce6a08bf1f56209578500b361fRoboErik */ 115394be6100218126ce6a08bf1f56209578500b361fRoboErik public boolean canDisconnect() { 115494be6100218126ce6a08bf1f56209578500b361fRoboErik return mCanDisconnect; 115594be6100218126ce6a08bf1f56209578500b361fRoboErik } 115694be6100218126ce6a08bf1f56209578500b361fRoboErik 115794be6100218126ce6a08bf1f56209578500b361fRoboErik /** 1158c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown * Requests a volume change for this route asynchronously. 1159c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown * <p> 1160c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown * This function may only be called on a selected route. It will have 1161c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown * no effect if the route is currently unselected. 1162c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown * </p> 1163c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown * 1164c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown * @param volume The new volume value between 0 and {@link #getVolumeMax}. 1165c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown */ 1166c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown public void requestSetVolume(int volume) { 1167c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown checkCallingThread(); 1168c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown sGlobal.requestSetVolume(this, Math.min(mVolumeMax, Math.max(0, volume))); 1169c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown } 1170c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown 1171c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown /** 1172c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown * Requests an incremental volume update for this route asynchronously. 1173c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown * <p> 1174c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown * This function may only be called on a selected route. It will have 1175c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown * no effect if the route is currently unselected. 1176c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown * </p> 1177c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown * 1178c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown * @param delta The delta to add to the current volume. 1179c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown */ 1180c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown public void requestUpdateVolume(int delta) { 1181c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown checkCallingThread(); 1182c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown if (delta != 0) { 1183c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown sGlobal.requestUpdateVolume(this, delta); 1184c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown } 1185c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown } 1186c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown 1187c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown /** 1188c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown * Gets the {@link Display} that should be used by the application to show 1189c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown * a {@link android.app.Presentation} on an external display when this route is selected. 1190c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown * Depending on the route, this may only be valid if the route is currently 1191c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown * selected. 1192c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown * <p> 1193c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown * The preferred presentation display may change independently of the route 1194c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown * being selected or unselected. For example, the presentation display 1195c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown * of the default system route may change when an external HDMI display is connected 1196c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown * or disconnected even though the route itself has not changed. 1197c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown * </p><p> 1198c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown * This method may return null if there is no external display associated with 1199c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown * the route or if the display is not ready to show UI yet. 1200c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown * </p><p> 1201c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown * The application should listen for changes to the presentation display 1202c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown * using the {@link Callback#onRoutePresentationDisplayChanged} callback and 1203c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown * show or dismiss its {@link android.app.Presentation} accordingly when the display 1204c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown * becomes available or is removed. 1205c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown * </p><p> 1206c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown * This method only makes sense for 1207c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown * {@link MediaControlIntent#CATEGORY_LIVE_VIDEO live video} routes. 1208c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown * </p> 1209c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown * 1210c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown * @return The preferred presentation display to use when this route is 1211c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown * selected or null if none. 1212c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown * 1213c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown * @see MediaControlIntent#CATEGORY_LIVE_VIDEO 1214c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown * @see android.app.Presentation 1215c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown */ 1216429c3b17b88ebd8c4512e9179fd9d48333c0979eTor Norbye @Nullable 1217c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown public Display getPresentationDisplay() { 1218fa326a4649d9d0e8113e315f6c8251fe686abce4Jeff Brown checkCallingThread(); 1219c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown if (mPresentationDisplayId >= 0 && mPresentationDisplay == null) { 1220c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown mPresentationDisplay = sGlobal.getDisplay(mPresentationDisplayId); 1221c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown } 1222c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown return mPresentationDisplay; 1223c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown } 1224c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown 1225c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown /** 1226c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown * Gets a collection of extra properties about this route that were supplied 1227c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown * by its media route provider, or null if none. 1228c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown */ 1229429c3b17b88ebd8c4512e9179fd9d48333c0979eTor Norbye @Nullable 1230c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown public Bundle getExtras() { 1231c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown return mExtras; 1232c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown } 1233c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown 1234fa326a4649d9d0e8113e315f6c8251fe686abce4Jeff Brown /** 123594be6100218126ce6a08bf1f56209578500b361fRoboErik * Gets an intent sender for launching a settings activity for this 123694be6100218126ce6a08bf1f56209578500b361fRoboErik * route. 123794be6100218126ce6a08bf1f56209578500b361fRoboErik */ 123894be6100218126ce6a08bf1f56209578500b361fRoboErik @Nullable 123994be6100218126ce6a08bf1f56209578500b361fRoboErik public IntentSender getSettingsIntent() { 124094be6100218126ce6a08bf1f56209578500b361fRoboErik return mSettingsIntent; 124194be6100218126ce6a08bf1f56209578500b361fRoboErik } 124294be6100218126ce6a08bf1f56209578500b361fRoboErik 124394be6100218126ce6a08bf1f56209578500b361fRoboErik /** 1244fa326a4649d9d0e8113e315f6c8251fe686abce4Jeff Brown * Selects this media route. 1245fa326a4649d9d0e8113e315f6c8251fe686abce4Jeff Brown */ 1246fa326a4649d9d0e8113e315f6c8251fe686abce4Jeff Brown public void select() { 1247fa326a4649d9d0e8113e315f6c8251fe686abce4Jeff Brown checkCallingThread(); 1248fa326a4649d9d0e8113e315f6c8251fe686abce4Jeff Brown sGlobal.selectRoute(this); 1249fa326a4649d9d0e8113e315f6c8251fe686abce4Jeff Brown } 1250fa326a4649d9d0e8113e315f6c8251fe686abce4Jeff Brown 1251c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown @Override 1252c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown public String toString() { 1253cb63b6ecac9786891514f241dec71695f09d3efbJeff Brown return "MediaRouter.RouteInfo{ uniqueId=" + mUniqueId 1254cb63b6ecac9786891514f241dec71695f09d3efbJeff Brown + ", name=" + mName 1255d63957d28aaabcec588b8cde12eac16414783aebJeff Brown + ", description=" + mDescription 1256c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown + ", enabled=" + mEnabled 125711417b1cfde8f1749905f2d735623af9214148afJeff Brown + ", connecting=" + mConnecting 125894be6100218126ce6a08bf1f56209578500b361fRoboErik + ", canDisconnect=" + mCanDisconnect 1259c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown + ", playbackType=" + mPlaybackType 1260c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown + ", playbackStream=" + mPlaybackStream 1261c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown + ", volumeHandling=" + mVolumeHandling 1262c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown + ", volume=" + mVolume 1263c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown + ", volumeMax=" + mVolumeMax 1264c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown + ", presentationDisplayId=" + mPresentationDisplayId 1265c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown + ", extras=" + mExtras 126694be6100218126ce6a08bf1f56209578500b361fRoboErik + ", settingsIntent=" + mSettingsIntent 1267fa326a4649d9d0e8113e315f6c8251fe686abce4Jeff Brown + ", providerPackageName=" + mProvider.getPackageName() 1268fa326a4649d9d0e8113e315f6c8251fe686abce4Jeff Brown + " }"; 1269fa326a4649d9d0e8113e315f6c8251fe686abce4Jeff Brown } 1270fa326a4649d9d0e8113e315f6c8251fe686abce4Jeff Brown 1271e7a694bf5fc4bf417502509a8604fffc7a41d84cJae Seo int maybeUpdateDescriptor(MediaRouteDescriptor descriptor) { 1272fa326a4649d9d0e8113e315f6c8251fe686abce4Jeff Brown int changes = 0; 1273fa326a4649d9d0e8113e315f6c8251fe686abce4Jeff Brown if (mDescriptor != descriptor) { 1274e7a694bf5fc4bf417502509a8604fffc7a41d84cJae Seo changes = updateDescriptor(descriptor); 1275e7a694bf5fc4bf417502509a8604fffc7a41d84cJae Seo } 1276e7a694bf5fc4bf417502509a8604fffc7a41d84cJae Seo return changes; 1277e7a694bf5fc4bf417502509a8604fffc7a41d84cJae Seo } 1278e7a694bf5fc4bf417502509a8604fffc7a41d84cJae Seo 1279e7a694bf5fc4bf417502509a8604fffc7a41d84cJae Seo int updateDescriptor(MediaRouteDescriptor descriptor) { 1280e7a694bf5fc4bf417502509a8604fffc7a41d84cJae Seo int changes = 0; 1281e7a694bf5fc4bf417502509a8604fffc7a41d84cJae Seo mDescriptor = descriptor; 1282e7a694bf5fc4bf417502509a8604fffc7a41d84cJae Seo if (descriptor != null) { 1283e7a694bf5fc4bf417502509a8604fffc7a41d84cJae Seo if (!equal(mName, descriptor.getName())) { 1284e7a694bf5fc4bf417502509a8604fffc7a41d84cJae Seo mName = descriptor.getName(); 1285e7a694bf5fc4bf417502509a8604fffc7a41d84cJae Seo changes |= CHANGE_GENERAL; 1286e7a694bf5fc4bf417502509a8604fffc7a41d84cJae Seo } 1287e7a694bf5fc4bf417502509a8604fffc7a41d84cJae Seo if (!equal(mDescription, descriptor.getDescription())) { 1288e7a694bf5fc4bf417502509a8604fffc7a41d84cJae Seo mDescription = descriptor.getDescription(); 1289e7a694bf5fc4bf417502509a8604fffc7a41d84cJae Seo changes |= CHANGE_GENERAL; 1290e7a694bf5fc4bf417502509a8604fffc7a41d84cJae Seo } 1291e7a694bf5fc4bf417502509a8604fffc7a41d84cJae Seo if (mEnabled != descriptor.isEnabled()) { 1292e7a694bf5fc4bf417502509a8604fffc7a41d84cJae Seo mEnabled = descriptor.isEnabled(); 1293e7a694bf5fc4bf417502509a8604fffc7a41d84cJae Seo changes |= CHANGE_GENERAL; 1294e7a694bf5fc4bf417502509a8604fffc7a41d84cJae Seo } 1295e7a694bf5fc4bf417502509a8604fffc7a41d84cJae Seo if (mConnecting != descriptor.isConnecting()) { 1296e7a694bf5fc4bf417502509a8604fffc7a41d84cJae Seo mConnecting = descriptor.isConnecting(); 1297e7a694bf5fc4bf417502509a8604fffc7a41d84cJae Seo changes |= CHANGE_GENERAL; 1298e7a694bf5fc4bf417502509a8604fffc7a41d84cJae Seo } 1299e7a694bf5fc4bf417502509a8604fffc7a41d84cJae Seo if (!mControlFilters.equals(descriptor.getControlFilters())) { 1300e7a694bf5fc4bf417502509a8604fffc7a41d84cJae Seo mControlFilters.clear(); 1301e7a694bf5fc4bf417502509a8604fffc7a41d84cJae Seo mControlFilters.addAll(descriptor.getControlFilters()); 1302e7a694bf5fc4bf417502509a8604fffc7a41d84cJae Seo changes |= CHANGE_GENERAL; 1303e7a694bf5fc4bf417502509a8604fffc7a41d84cJae Seo } 1304e7a694bf5fc4bf417502509a8604fffc7a41d84cJae Seo if (mPlaybackType != descriptor.getPlaybackType()) { 1305e7a694bf5fc4bf417502509a8604fffc7a41d84cJae Seo mPlaybackType = descriptor.getPlaybackType(); 1306e7a694bf5fc4bf417502509a8604fffc7a41d84cJae Seo changes |= CHANGE_GENERAL; 1307e7a694bf5fc4bf417502509a8604fffc7a41d84cJae Seo } 1308e7a694bf5fc4bf417502509a8604fffc7a41d84cJae Seo if (mPlaybackStream != descriptor.getPlaybackStream()) { 1309e7a694bf5fc4bf417502509a8604fffc7a41d84cJae Seo mPlaybackStream = descriptor.getPlaybackStream(); 1310e7a694bf5fc4bf417502509a8604fffc7a41d84cJae Seo changes |= CHANGE_GENERAL; 1311e7a694bf5fc4bf417502509a8604fffc7a41d84cJae Seo } 1312e7a694bf5fc4bf417502509a8604fffc7a41d84cJae Seo if (mVolumeHandling != descriptor.getVolumeHandling()) { 1313e7a694bf5fc4bf417502509a8604fffc7a41d84cJae Seo mVolumeHandling = descriptor.getVolumeHandling(); 1314e7a694bf5fc4bf417502509a8604fffc7a41d84cJae Seo changes |= CHANGE_GENERAL | CHANGE_VOLUME; 1315e7a694bf5fc4bf417502509a8604fffc7a41d84cJae Seo } 1316e7a694bf5fc4bf417502509a8604fffc7a41d84cJae Seo if (mVolume != descriptor.getVolume()) { 1317e7a694bf5fc4bf417502509a8604fffc7a41d84cJae Seo mVolume = descriptor.getVolume(); 1318e7a694bf5fc4bf417502509a8604fffc7a41d84cJae Seo changes |= CHANGE_GENERAL | CHANGE_VOLUME; 1319e7a694bf5fc4bf417502509a8604fffc7a41d84cJae Seo } 1320e7a694bf5fc4bf417502509a8604fffc7a41d84cJae Seo if (mVolumeMax != descriptor.getVolumeMax()) { 1321e7a694bf5fc4bf417502509a8604fffc7a41d84cJae Seo mVolumeMax = descriptor.getVolumeMax(); 1322e7a694bf5fc4bf417502509a8604fffc7a41d84cJae Seo changes |= CHANGE_GENERAL | CHANGE_VOLUME; 1323e7a694bf5fc4bf417502509a8604fffc7a41d84cJae Seo } 1324e7a694bf5fc4bf417502509a8604fffc7a41d84cJae Seo if (mPresentationDisplayId != descriptor.getPresentationDisplayId()) { 1325e7a694bf5fc4bf417502509a8604fffc7a41d84cJae Seo mPresentationDisplayId = descriptor.getPresentationDisplayId(); 1326e7a694bf5fc4bf417502509a8604fffc7a41d84cJae Seo mPresentationDisplay = null; 1327e7a694bf5fc4bf417502509a8604fffc7a41d84cJae Seo changes |= CHANGE_GENERAL | CHANGE_PRESENTATION_DISPLAY; 1328e7a694bf5fc4bf417502509a8604fffc7a41d84cJae Seo } 1329e7a694bf5fc4bf417502509a8604fffc7a41d84cJae Seo if (!equal(mExtras, descriptor.getExtras())) { 1330e7a694bf5fc4bf417502509a8604fffc7a41d84cJae Seo mExtras = descriptor.getExtras(); 1331e7a694bf5fc4bf417502509a8604fffc7a41d84cJae Seo changes |= CHANGE_GENERAL; 1332e7a694bf5fc4bf417502509a8604fffc7a41d84cJae Seo } 1333e7a694bf5fc4bf417502509a8604fffc7a41d84cJae Seo if (!equal(mSettingsIntent, descriptor.getSettingsActivity())) { 1334e7a694bf5fc4bf417502509a8604fffc7a41d84cJae Seo mSettingsIntent = descriptor.getSettingsActivity(); 1335e7a694bf5fc4bf417502509a8604fffc7a41d84cJae Seo changes |= CHANGE_GENERAL; 1336e7a694bf5fc4bf417502509a8604fffc7a41d84cJae Seo } 1337e7a694bf5fc4bf417502509a8604fffc7a41d84cJae Seo if (mCanDisconnect != descriptor.canDisconnectAndKeepPlaying()) { 1338e7a694bf5fc4bf417502509a8604fffc7a41d84cJae Seo mCanDisconnect = descriptor.canDisconnectAndKeepPlaying(); 1339e7a694bf5fc4bf417502509a8604fffc7a41d84cJae Seo changes |= CHANGE_GENERAL | CHANGE_PRESENTATION_DISPLAY; 1340fa326a4649d9d0e8113e315f6c8251fe686abce4Jeff Brown } 1341fa326a4649d9d0e8113e315f6c8251fe686abce4Jeff Brown } 1342fa326a4649d9d0e8113e315f6c8251fe686abce4Jeff Brown return changes; 1343fa326a4649d9d0e8113e315f6c8251fe686abce4Jeff Brown } 1344fa326a4649d9d0e8113e315f6c8251fe686abce4Jeff Brown 1345fa326a4649d9d0e8113e315f6c8251fe686abce4Jeff Brown String getDescriptorId() { 1346fa326a4649d9d0e8113e315f6c8251fe686abce4Jeff Brown return mDescriptorId; 1347fa326a4649d9d0e8113e315f6c8251fe686abce4Jeff Brown } 1348fa326a4649d9d0e8113e315f6c8251fe686abce4Jeff Brown 134914b631fc79b2fc69f392ab8d2f096b0e86b9bea2Jae Seo /** @hide */ 135014b631fc79b2fc69f392ab8d2f096b0e86b9bea2Jae Seo public MediaRouteProvider getProviderInstance() { 1351fa326a4649d9d0e8113e315f6c8251fe686abce4Jeff Brown return mProvider.getProviderInstance(); 1352fa326a4649d9d0e8113e315f6c8251fe686abce4Jeff Brown } 1353fa326a4649d9d0e8113e315f6c8251fe686abce4Jeff Brown } 1354fa326a4649d9d0e8113e315f6c8251fe686abce4Jeff Brown 1355fa326a4649d9d0e8113e315f6c8251fe686abce4Jeff Brown /** 1356e7a694bf5fc4bf417502509a8604fffc7a41d84cJae Seo * Information about a route that consists of multiple other routes in a group. 1357e7a694bf5fc4bf417502509a8604fffc7a41d84cJae Seo * @hide 1358e7a694bf5fc4bf417502509a8604fffc7a41d84cJae Seo * STOPSHIP: Unhide or remove. 1359e7a694bf5fc4bf417502509a8604fffc7a41d84cJae Seo */ 1360e7a694bf5fc4bf417502509a8604fffc7a41d84cJae Seo public static class RouteGroup extends RouteInfo { 1361e7a694bf5fc4bf417502509a8604fffc7a41d84cJae Seo private List<RouteInfo> mRoutes = new ArrayList<>(); 1362e7a694bf5fc4bf417502509a8604fffc7a41d84cJae Seo 1363e7a694bf5fc4bf417502509a8604fffc7a41d84cJae Seo RouteGroup(ProviderInfo provider, String descriptorId, String uniqueId) { 1364e7a694bf5fc4bf417502509a8604fffc7a41d84cJae Seo super(provider, descriptorId, uniqueId); 1365e7a694bf5fc4bf417502509a8604fffc7a41d84cJae Seo } 1366e7a694bf5fc4bf417502509a8604fffc7a41d84cJae Seo 1367e7a694bf5fc4bf417502509a8604fffc7a41d84cJae Seo /** 1368e7a694bf5fc4bf417502509a8604fffc7a41d84cJae Seo * @return The number of routes in this group 1369e7a694bf5fc4bf417502509a8604fffc7a41d84cJae Seo */ 1370e7a694bf5fc4bf417502509a8604fffc7a41d84cJae Seo public int getRouteCount() { 1371e7a694bf5fc4bf417502509a8604fffc7a41d84cJae Seo return mRoutes.size(); 1372e7a694bf5fc4bf417502509a8604fffc7a41d84cJae Seo } 1373e7a694bf5fc4bf417502509a8604fffc7a41d84cJae Seo 1374e7a694bf5fc4bf417502509a8604fffc7a41d84cJae Seo /** 1375e7a694bf5fc4bf417502509a8604fffc7a41d84cJae Seo * Returns the route in this group at the specified index 1376e7a694bf5fc4bf417502509a8604fffc7a41d84cJae Seo * 1377e7a694bf5fc4bf417502509a8604fffc7a41d84cJae Seo * @param index Index to fetch 1378e7a694bf5fc4bf417502509a8604fffc7a41d84cJae Seo * @return The route at index 1379e7a694bf5fc4bf417502509a8604fffc7a41d84cJae Seo */ 1380e7a694bf5fc4bf417502509a8604fffc7a41d84cJae Seo public RouteInfo getRouteAt(int index) { 1381e7a694bf5fc4bf417502509a8604fffc7a41d84cJae Seo return mRoutes.get(index); 1382e7a694bf5fc4bf417502509a8604fffc7a41d84cJae Seo } 1383e7a694bf5fc4bf417502509a8604fffc7a41d84cJae Seo 1384e7a694bf5fc4bf417502509a8604fffc7a41d84cJae Seo @Override 1385e7a694bf5fc4bf417502509a8604fffc7a41d84cJae Seo public String toString() { 1386e7a694bf5fc4bf417502509a8604fffc7a41d84cJae Seo StringBuilder sb = new StringBuilder(super.toString()); 1387e7a694bf5fc4bf417502509a8604fffc7a41d84cJae Seo sb.append('['); 1388e7a694bf5fc4bf417502509a8604fffc7a41d84cJae Seo final int count = mRoutes.size(); 1389e7a694bf5fc4bf417502509a8604fffc7a41d84cJae Seo for (int i = 0; i < count; i++) { 1390e7a694bf5fc4bf417502509a8604fffc7a41d84cJae Seo if (i > 0) sb.append(", "); 1391e7a694bf5fc4bf417502509a8604fffc7a41d84cJae Seo sb.append(mRoutes.get(i)); 1392e7a694bf5fc4bf417502509a8604fffc7a41d84cJae Seo } 1393e7a694bf5fc4bf417502509a8604fffc7a41d84cJae Seo sb.append(']'); 1394e7a694bf5fc4bf417502509a8604fffc7a41d84cJae Seo return sb.toString(); 1395e7a694bf5fc4bf417502509a8604fffc7a41d84cJae Seo } 1396e7a694bf5fc4bf417502509a8604fffc7a41d84cJae Seo 1397e7a694bf5fc4bf417502509a8604fffc7a41d84cJae Seo @Override 1398e7a694bf5fc4bf417502509a8604fffc7a41d84cJae Seo int maybeUpdateDescriptor(MediaRouteDescriptor descriptor) { 1399e7a694bf5fc4bf417502509a8604fffc7a41d84cJae Seo boolean changed = false; 1400e7a694bf5fc4bf417502509a8604fffc7a41d84cJae Seo if (mDescriptor != descriptor) { 1401e7a694bf5fc4bf417502509a8604fffc7a41d84cJae Seo mDescriptor = descriptor; 1402e7a694bf5fc4bf417502509a8604fffc7a41d84cJae Seo if (descriptor != null) { 1403e7a694bf5fc4bf417502509a8604fffc7a41d84cJae Seo List<String> childIds = descriptor.getChildIds(); 1404e7a694bf5fc4bf417502509a8604fffc7a41d84cJae Seo List<RouteInfo> routes = new ArrayList<>(); 1405e7a694bf5fc4bf417502509a8604fffc7a41d84cJae Seo changed = childIds.size() != mRoutes.size(); 1406e7a694bf5fc4bf417502509a8604fffc7a41d84cJae Seo for (String childId : childIds) { 1407e7a694bf5fc4bf417502509a8604fffc7a41d84cJae Seo RouteInfo child = sGlobal.getRoute(childId); 1408e7a694bf5fc4bf417502509a8604fffc7a41d84cJae Seo if (child != null) { 1409e7a694bf5fc4bf417502509a8604fffc7a41d84cJae Seo routes.add(child); 1410e7a694bf5fc4bf417502509a8604fffc7a41d84cJae Seo if (!changed && !mRoutes.contains(child)) { 1411e7a694bf5fc4bf417502509a8604fffc7a41d84cJae Seo changed = true; 1412e7a694bf5fc4bf417502509a8604fffc7a41d84cJae Seo } 1413e7a694bf5fc4bf417502509a8604fffc7a41d84cJae Seo } 1414e7a694bf5fc4bf417502509a8604fffc7a41d84cJae Seo } 1415e7a694bf5fc4bf417502509a8604fffc7a41d84cJae Seo if (changed) { 1416e7a694bf5fc4bf417502509a8604fffc7a41d84cJae Seo mRoutes = routes; 1417e7a694bf5fc4bf417502509a8604fffc7a41d84cJae Seo } 1418e7a694bf5fc4bf417502509a8604fffc7a41d84cJae Seo } 1419e7a694bf5fc4bf417502509a8604fffc7a41d84cJae Seo } 1420e7a694bf5fc4bf417502509a8604fffc7a41d84cJae Seo return (changed ? CHANGE_GENERAL : 0) | super.updateDescriptor(descriptor); 1421e7a694bf5fc4bf417502509a8604fffc7a41d84cJae Seo } 1422e7a694bf5fc4bf417502509a8604fffc7a41d84cJae Seo } 1423e7a694bf5fc4bf417502509a8604fffc7a41d84cJae Seo 1424e7a694bf5fc4bf417502509a8604fffc7a41d84cJae Seo /** 1425fa326a4649d9d0e8113e315f6c8251fe686abce4Jeff Brown * Provides information about a media route provider. 1426fa326a4649d9d0e8113e315f6c8251fe686abce4Jeff Brown * <p> 1427fa326a4649d9d0e8113e315f6c8251fe686abce4Jeff Brown * This object may be used to determine which media route provider has 1428fa326a4649d9d0e8113e315f6c8251fe686abce4Jeff Brown * published a particular route. 1429fa326a4649d9d0e8113e315f6c8251fe686abce4Jeff Brown * </p> 1430fa326a4649d9d0e8113e315f6c8251fe686abce4Jeff Brown */ 1431fa326a4649d9d0e8113e315f6c8251fe686abce4Jeff Brown public static final class ProviderInfo { 1432fa326a4649d9d0e8113e315f6c8251fe686abce4Jeff Brown private final MediaRouteProvider mProviderInstance; 1433e7a694bf5fc4bf417502509a8604fffc7a41d84cJae Seo private final List<RouteInfo> mRoutes = new ArrayList<>(); 1434fa326a4649d9d0e8113e315f6c8251fe686abce4Jeff Brown 1435fa326a4649d9d0e8113e315f6c8251fe686abce4Jeff Brown private final ProviderMetadata mMetadata; 143611417b1cfde8f1749905f2d735623af9214148afJeff Brown private MediaRouteProviderDescriptor mDescriptor; 1437fa326a4649d9d0e8113e315f6c8251fe686abce4Jeff Brown private Resources mResources; 1438fa326a4649d9d0e8113e315f6c8251fe686abce4Jeff Brown private boolean mResourcesNotAvailable; 1439fa326a4649d9d0e8113e315f6c8251fe686abce4Jeff Brown 1440fa326a4649d9d0e8113e315f6c8251fe686abce4Jeff Brown ProviderInfo(MediaRouteProvider provider) { 1441fa326a4649d9d0e8113e315f6c8251fe686abce4Jeff Brown mProviderInstance = provider; 1442fa326a4649d9d0e8113e315f6c8251fe686abce4Jeff Brown mMetadata = provider.getMetadata(); 1443fa326a4649d9d0e8113e315f6c8251fe686abce4Jeff Brown } 1444fa326a4649d9d0e8113e315f6c8251fe686abce4Jeff Brown 1445fa326a4649d9d0e8113e315f6c8251fe686abce4Jeff Brown /** 1446fa326a4649d9d0e8113e315f6c8251fe686abce4Jeff Brown * Gets the provider's underlying {@link MediaRouteProvider} instance. 1447fa326a4649d9d0e8113e315f6c8251fe686abce4Jeff Brown */ 1448fa326a4649d9d0e8113e315f6c8251fe686abce4Jeff Brown public MediaRouteProvider getProviderInstance() { 1449fa326a4649d9d0e8113e315f6c8251fe686abce4Jeff Brown checkCallingThread(); 1450fa326a4649d9d0e8113e315f6c8251fe686abce4Jeff Brown return mProviderInstance; 1451fa326a4649d9d0e8113e315f6c8251fe686abce4Jeff Brown } 1452fa326a4649d9d0e8113e315f6c8251fe686abce4Jeff Brown 1453fa326a4649d9d0e8113e315f6c8251fe686abce4Jeff Brown /** 1454adf0f4a217e14894af07dfa9f46cad7d98b8a7f4Jeff Brown * Gets the package name of the media route provider. 1455fa326a4649d9d0e8113e315f6c8251fe686abce4Jeff Brown */ 1456fa326a4649d9d0e8113e315f6c8251fe686abce4Jeff Brown public String getPackageName() { 1457fa326a4649d9d0e8113e315f6c8251fe686abce4Jeff Brown return mMetadata.getPackageName(); 1458fa326a4649d9d0e8113e315f6c8251fe686abce4Jeff Brown } 1459fa326a4649d9d0e8113e315f6c8251fe686abce4Jeff Brown 1460fa326a4649d9d0e8113e315f6c8251fe686abce4Jeff Brown /** 1461adf0f4a217e14894af07dfa9f46cad7d98b8a7f4Jeff Brown * Gets the component name of the media route provider. 1462adf0f4a217e14894af07dfa9f46cad7d98b8a7f4Jeff Brown */ 1463adf0f4a217e14894af07dfa9f46cad7d98b8a7f4Jeff Brown public ComponentName getComponentName() { 1464adf0f4a217e14894af07dfa9f46cad7d98b8a7f4Jeff Brown return mMetadata.getComponentName(); 1465adf0f4a217e14894af07dfa9f46cad7d98b8a7f4Jeff Brown } 1466adf0f4a217e14894af07dfa9f46cad7d98b8a7f4Jeff Brown 1467adf0f4a217e14894af07dfa9f46cad7d98b8a7f4Jeff Brown /** 1468fa326a4649d9d0e8113e315f6c8251fe686abce4Jeff Brown * Gets the {@link MediaRouter.RouteInfo routes} published by this route provider. 1469fa326a4649d9d0e8113e315f6c8251fe686abce4Jeff Brown */ 1470fa326a4649d9d0e8113e315f6c8251fe686abce4Jeff Brown public List<RouteInfo> getRoutes() { 1471fa326a4649d9d0e8113e315f6c8251fe686abce4Jeff Brown checkCallingThread(); 1472fa326a4649d9d0e8113e315f6c8251fe686abce4Jeff Brown return mRoutes; 1473fa326a4649d9d0e8113e315f6c8251fe686abce4Jeff Brown } 1474fa326a4649d9d0e8113e315f6c8251fe686abce4Jeff Brown 1475fa326a4649d9d0e8113e315f6c8251fe686abce4Jeff Brown Resources getResources() { 1476fa326a4649d9d0e8113e315f6c8251fe686abce4Jeff Brown if (mResources == null && !mResourcesNotAvailable) { 1477fa326a4649d9d0e8113e315f6c8251fe686abce4Jeff Brown String packageName = getPackageName(); 1478fa326a4649d9d0e8113e315f6c8251fe686abce4Jeff Brown Context context = sGlobal.getProviderContext(packageName); 1479fa326a4649d9d0e8113e315f6c8251fe686abce4Jeff Brown if (context != null) { 1480fa326a4649d9d0e8113e315f6c8251fe686abce4Jeff Brown mResources = context.getResources(); 1481fa326a4649d9d0e8113e315f6c8251fe686abce4Jeff Brown } else { 1482fa326a4649d9d0e8113e315f6c8251fe686abce4Jeff Brown Log.w(TAG, "Unable to obtain resources for route provider package: " 1483fa326a4649d9d0e8113e315f6c8251fe686abce4Jeff Brown + packageName); 1484fa326a4649d9d0e8113e315f6c8251fe686abce4Jeff Brown mResourcesNotAvailable = true; 1485fa326a4649d9d0e8113e315f6c8251fe686abce4Jeff Brown } 1486fa326a4649d9d0e8113e315f6c8251fe686abce4Jeff Brown } 1487fa326a4649d9d0e8113e315f6c8251fe686abce4Jeff Brown return mResources; 1488fa326a4649d9d0e8113e315f6c8251fe686abce4Jeff Brown } 1489fa326a4649d9d0e8113e315f6c8251fe686abce4Jeff Brown 149011417b1cfde8f1749905f2d735623af9214148afJeff Brown boolean updateDescriptor(MediaRouteProviderDescriptor descriptor) { 1491fa326a4649d9d0e8113e315f6c8251fe686abce4Jeff Brown if (mDescriptor != descriptor) { 1492fa326a4649d9d0e8113e315f6c8251fe686abce4Jeff Brown mDescriptor = descriptor; 1493fa326a4649d9d0e8113e315f6c8251fe686abce4Jeff Brown return true; 1494fa326a4649d9d0e8113e315f6c8251fe686abce4Jeff Brown } 1495fa326a4649d9d0e8113e315f6c8251fe686abce4Jeff Brown return false; 1496fa326a4649d9d0e8113e315f6c8251fe686abce4Jeff Brown } 1497fa326a4649d9d0e8113e315f6c8251fe686abce4Jeff Brown 1498fa326a4649d9d0e8113e315f6c8251fe686abce4Jeff Brown int findRouteByDescriptorId(String id) { 1499fa326a4649d9d0e8113e315f6c8251fe686abce4Jeff Brown final int count = mRoutes.size(); 1500fa326a4649d9d0e8113e315f6c8251fe686abce4Jeff Brown for (int i = 0; i < count; i++) { 1501fa326a4649d9d0e8113e315f6c8251fe686abce4Jeff Brown if (mRoutes.get(i).mDescriptorId.equals(id)) { 1502fa326a4649d9d0e8113e315f6c8251fe686abce4Jeff Brown return i; 1503fa326a4649d9d0e8113e315f6c8251fe686abce4Jeff Brown } 1504fa326a4649d9d0e8113e315f6c8251fe686abce4Jeff Brown } 1505fa326a4649d9d0e8113e315f6c8251fe686abce4Jeff Brown return -1; 1506fa326a4649d9d0e8113e315f6c8251fe686abce4Jeff Brown } 1507fa326a4649d9d0e8113e315f6c8251fe686abce4Jeff Brown 1508fa326a4649d9d0e8113e315f6c8251fe686abce4Jeff Brown @Override 1509fa326a4649d9d0e8113e315f6c8251fe686abce4Jeff Brown public String toString() { 1510fa326a4649d9d0e8113e315f6c8251fe686abce4Jeff Brown return "MediaRouter.RouteProviderInfo{ packageName=" + getPackageName() 1511c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown + " }"; 1512c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown } 1513c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown } 1514c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown 1515c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown /** 1516c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown * Interface for receiving events about media routing changes. 1517c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown * All methods of this interface will be called from the application's main thread. 1518c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown * <p> 1519c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown * A Callback will only receive events relevant to routes that the callback 152011417b1cfde8f1749905f2d735623af9214148afJeff Brown * was registered for unless the {@link MediaRouter#CALLBACK_FLAG_UNFILTERED_EVENTS} 152111417b1cfde8f1749905f2d735623af9214148afJeff Brown * flag was specified in {@link MediaRouter#addCallback(MediaRouteSelector, Callback, int)}. 1522c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown * </p> 1523c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown * 152411417b1cfde8f1749905f2d735623af9214148afJeff Brown * @see MediaRouter#addCallback(MediaRouteSelector, Callback, int) 1525c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown * @see MediaRouter#removeCallback(Callback) 1526c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown */ 1527c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown public static abstract class Callback { 1528c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown /** 1529fa326a4649d9d0e8113e315f6c8251fe686abce4Jeff Brown * Called when the supplied media route becomes selected as the active route. 1530c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown * 1531fa326a4649d9d0e8113e315f6c8251fe686abce4Jeff Brown * @param router The media router reporting the event. 1532c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown * @param route The route that has been selected. 1533c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown */ 1534c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown public void onRouteSelected(MediaRouter router, RouteInfo route) { 1535c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown } 1536c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown 1537c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown /** 1538fa326a4649d9d0e8113e315f6c8251fe686abce4Jeff Brown * Called when the supplied media route becomes unselected as the active route. 1539c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown * 1540fa326a4649d9d0e8113e315f6c8251fe686abce4Jeff Brown * @param router The media router reporting the event. 1541c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown * @param route The route that has been unselected. 1542c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown */ 1543c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown public void onRouteUnselected(MediaRouter router, RouteInfo route) { 1544c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown } 1545c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown 1546c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown /** 1547fa326a4649d9d0e8113e315f6c8251fe686abce4Jeff Brown * Called when a media route has been added. 1548c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown * 1549fa326a4649d9d0e8113e315f6c8251fe686abce4Jeff Brown * @param router The media router reporting the event. 1550c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown * @param route The route that has become available for use. 1551c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown */ 1552c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown public void onRouteAdded(MediaRouter router, RouteInfo route) { 1553c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown } 1554c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown 1555c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown /** 1556fa326a4649d9d0e8113e315f6c8251fe686abce4Jeff Brown * Called when a media route has been removed. 1557c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown * 1558fa326a4649d9d0e8113e315f6c8251fe686abce4Jeff Brown * @param router The media router reporting the event. 1559c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown * @param route The route that has been removed from availability. 1560c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown */ 1561c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown public void onRouteRemoved(MediaRouter router, RouteInfo route) { 1562c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown } 1563c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown 1564c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown /** 1565fa326a4649d9d0e8113e315f6c8251fe686abce4Jeff Brown * Called when a property of the indicated media route has changed. 1566c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown * 1567fa326a4649d9d0e8113e315f6c8251fe686abce4Jeff Brown * @param router The media router reporting the event. 1568c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown * @param route The route that was changed. 1569c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown */ 1570c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown public void onRouteChanged(MediaRouter router, RouteInfo route) { 1571c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown } 1572c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown 1573c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown /** 1574fa326a4649d9d0e8113e315f6c8251fe686abce4Jeff Brown * Called when a media route's volume changes. 1575c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown * 1576fa326a4649d9d0e8113e315f6c8251fe686abce4Jeff Brown * @param router The media router reporting the event. 1577c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown * @param route The route whose volume changed. 1578c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown */ 1579c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown public void onRouteVolumeChanged(MediaRouter router, RouteInfo route) { 1580c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown } 1581c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown 1582c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown /** 1583fa326a4649d9d0e8113e315f6c8251fe686abce4Jeff Brown * Called when a media route's presentation display changes. 1584c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown * <p> 1585c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown * This method is called whenever the route's presentation display becomes 1586fa326a4649d9d0e8113e315f6c8251fe686abce4Jeff Brown * available, is removed or has changes to some of its properties (such as its size). 1587c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown * </p> 1588c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown * 1589fa326a4649d9d0e8113e315f6c8251fe686abce4Jeff Brown * @param router The media router reporting the event. 1590c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown * @param route The route whose presentation display changed. 1591c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown * 1592c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown * @see RouteInfo#getPresentationDisplay() 1593c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown */ 1594c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown public void onRoutePresentationDisplayChanged(MediaRouter router, RouteInfo route) { 1595c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown } 1596fa326a4649d9d0e8113e315f6c8251fe686abce4Jeff Brown 1597fa326a4649d9d0e8113e315f6c8251fe686abce4Jeff Brown /** 1598fa326a4649d9d0e8113e315f6c8251fe686abce4Jeff Brown * Called when a media route provider has been added. 1599fa326a4649d9d0e8113e315f6c8251fe686abce4Jeff Brown * 1600fa326a4649d9d0e8113e315f6c8251fe686abce4Jeff Brown * @param router The media router reporting the event. 1601fa326a4649d9d0e8113e315f6c8251fe686abce4Jeff Brown * @param provider The provider that has become available for use. 1602fa326a4649d9d0e8113e315f6c8251fe686abce4Jeff Brown */ 1603fa326a4649d9d0e8113e315f6c8251fe686abce4Jeff Brown public void onProviderAdded(MediaRouter router, ProviderInfo provider) { 1604fa326a4649d9d0e8113e315f6c8251fe686abce4Jeff Brown } 1605fa326a4649d9d0e8113e315f6c8251fe686abce4Jeff Brown 1606fa326a4649d9d0e8113e315f6c8251fe686abce4Jeff Brown /** 1607fa326a4649d9d0e8113e315f6c8251fe686abce4Jeff Brown * Called when a media route provider has been removed. 1608fa326a4649d9d0e8113e315f6c8251fe686abce4Jeff Brown * 1609fa326a4649d9d0e8113e315f6c8251fe686abce4Jeff Brown * @param router The media router reporting the event. 1610fa326a4649d9d0e8113e315f6c8251fe686abce4Jeff Brown * @param provider The provider that has been removed from availability. 1611fa326a4649d9d0e8113e315f6c8251fe686abce4Jeff Brown */ 1612fa326a4649d9d0e8113e315f6c8251fe686abce4Jeff Brown public void onProviderRemoved(MediaRouter router, ProviderInfo provider) { 1613fa326a4649d9d0e8113e315f6c8251fe686abce4Jeff Brown } 161411417b1cfde8f1749905f2d735623af9214148afJeff Brown 161511417b1cfde8f1749905f2d735623af9214148afJeff Brown /** 161611417b1cfde8f1749905f2d735623af9214148afJeff Brown * Called when a property of the indicated media route provider has changed. 161711417b1cfde8f1749905f2d735623af9214148afJeff Brown * 161811417b1cfde8f1749905f2d735623af9214148afJeff Brown * @param router The media router reporting the event. 161911417b1cfde8f1749905f2d735623af9214148afJeff Brown * @param provider The provider that was changed. 162011417b1cfde8f1749905f2d735623af9214148afJeff Brown */ 162111417b1cfde8f1749905f2d735623af9214148afJeff Brown public void onProviderChanged(MediaRouter router, ProviderInfo provider) { 162211417b1cfde8f1749905f2d735623af9214148afJeff Brown } 1623c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown } 1624c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown 1625c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown /** 1626c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown * Callback which is invoked with the result of a media control request. 1627c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown * 1628c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown * @see RouteInfo#sendControlRequest 1629c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown */ 1630fa326a4649d9d0e8113e315f6c8251fe686abce4Jeff Brown public static abstract class ControlRequestCallback { 1631c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown /** 16323d4c9459ed77f732dd3ba602713af6ebf9280c8cJeff Brown * Called when a media control request succeeds. 16333d4c9459ed77f732dd3ba602713af6ebf9280c8cJeff Brown * 16343d4c9459ed77f732dd3ba602713af6ebf9280c8cJeff Brown * @param data Result data, or null if none. 16353d4c9459ed77f732dd3ba602713af6ebf9280c8cJeff Brown * Contents depend on the {@link MediaControlIntent media control action}. 1636c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown */ 16373d4c9459ed77f732dd3ba602713af6ebf9280c8cJeff Brown public void onResult(Bundle data) { 16383d4c9459ed77f732dd3ba602713af6ebf9280c8cJeff Brown } 1639c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown 1640c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown /** 16413d4c9459ed77f732dd3ba602713af6ebf9280c8cJeff Brown * Called when a media control request fails. 1642c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown * 16433d4c9459ed77f732dd3ba602713af6ebf9280c8cJeff Brown * @param error A localized error message which may be shown to the user, or null 16443d4c9459ed77f732dd3ba602713af6ebf9280c8cJeff Brown * if the cause of the error is unclear. 16453d4c9459ed77f732dd3ba602713af6ebf9280c8cJeff Brown * @param data Error data, or null if none. 16463d4c9459ed77f732dd3ba602713af6ebf9280c8cJeff Brown * Contents depend on the {@link MediaControlIntent media control action}. 1647c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown */ 16483d4c9459ed77f732dd3ba602713af6ebf9280c8cJeff Brown public void onError(String error, Bundle data) { 1649c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown } 1650c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown } 1651c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown 165211417b1cfde8f1749905f2d735623af9214148afJeff Brown private static final class CallbackRecord { 16539fcedc160282e6620f409ea46bf6728b35d011ddJeff Brown public final MediaRouter mRouter; 165411417b1cfde8f1749905f2d735623af9214148afJeff Brown public final Callback mCallback; 165511417b1cfde8f1749905f2d735623af9214148afJeff Brown public MediaRouteSelector mSelector; 165611417b1cfde8f1749905f2d735623af9214148afJeff Brown public int mFlags; 165711417b1cfde8f1749905f2d735623af9214148afJeff Brown 16589fcedc160282e6620f409ea46bf6728b35d011ddJeff Brown public CallbackRecord(MediaRouter router, Callback callback) { 16599fcedc160282e6620f409ea46bf6728b35d011ddJeff Brown mRouter = router; 166011417b1cfde8f1749905f2d735623af9214148afJeff Brown mCallback = callback; 166111417b1cfde8f1749905f2d735623af9214148afJeff Brown mSelector = MediaRouteSelector.EMPTY; 166211417b1cfde8f1749905f2d735623af9214148afJeff Brown } 166311417b1cfde8f1749905f2d735623af9214148afJeff Brown 166411417b1cfde8f1749905f2d735623af9214148afJeff Brown public boolean filterRouteEvent(RouteInfo route) { 166511417b1cfde8f1749905f2d735623af9214148afJeff Brown return (mFlags & CALLBACK_FLAG_UNFILTERED_EVENTS) != 0 166611417b1cfde8f1749905f2d735623af9214148afJeff Brown || route.matchesSelector(mSelector); 166711417b1cfde8f1749905f2d735623af9214148afJeff Brown } 166811417b1cfde8f1749905f2d735623af9214148afJeff Brown } 166911417b1cfde8f1749905f2d735623af9214148afJeff Brown 1670c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown /** 1671c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown * Global state for the media router. 1672c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown * <p> 1673c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown * Media routes and media route providers are global to the process; their 1674c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown * state and the bulk of the media router implementation lives here. 1675c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown * </p> 1676c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown */ 16773efa63d3b896244713e84acbb5945562dce41d77Jeff Brown private static final class GlobalMediaRouter 16783efa63d3b896244713e84acbb5945562dce41d77Jeff Brown implements SystemMediaRouteProvider.SyncCallback, 16793efa63d3b896244713e84acbb5945562dce41d77Jeff Brown RegisteredMediaRouteProviderWatcher.Callback { 1680c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown private final Context mApplicationContext; 16819fcedc160282e6620f409ea46bf6728b35d011ddJeff Brown private final ArrayList<WeakReference<MediaRouter>> mRouters = 16829fcedc160282e6620f409ea46bf6728b35d011ddJeff Brown new ArrayList<WeakReference<MediaRouter>>(); 1683c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown private final ArrayList<RouteInfo> mRoutes = new ArrayList<RouteInfo>(); 1684fa326a4649d9d0e8113e315f6c8251fe686abce4Jeff Brown private final ArrayList<ProviderInfo> mProviders = 1685fa326a4649d9d0e8113e315f6c8251fe686abce4Jeff Brown new ArrayList<ProviderInfo>(); 1686567dbb1c7f8a0cb3c4340ad607b6d833fc1b2270Jeff Brown private final ArrayList<RemoteControlClientRecord> mRemoteControlClients = 1687567dbb1c7f8a0cb3c4340ad607b6d833fc1b2270Jeff Brown new ArrayList<RemoteControlClientRecord>(); 1688567dbb1c7f8a0cb3c4340ad607b6d833fc1b2270Jeff Brown private final RemoteControlClientCompat.PlaybackInfo mPlaybackInfo = 1689567dbb1c7f8a0cb3c4340ad607b6d833fc1b2270Jeff Brown new RemoteControlClientCompat.PlaybackInfo(); 1690c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown private final ProviderCallback mProviderCallback = new ProviderCallback(); 1691c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown private final CallbackHandler mCallbackHandler = new CallbackHandler(); 1692c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown private final DisplayManagerCompat mDisplayManager; 1693c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown private final SystemMediaRouteProvider mSystemProvider; 1694fb81e09002d1d611168b204fc50d259fc15f27b2Jeff Brown private final boolean mLowRam; 1695c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown 1696fa326a4649d9d0e8113e315f6c8251fe686abce4Jeff Brown private RegisteredMediaRouteProviderWatcher mRegisteredProviderWatcher; 1697c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown private RouteInfo mDefaultRoute; 1698c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown private RouteInfo mSelectedRoute; 1699c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown private MediaRouteProvider.RouteController mSelectedRouteController; 170011417b1cfde8f1749905f2d735623af9214148afJeff Brown private MediaRouteDiscoveryRequest mDiscoveryRequest; 1701bbcdf78e350d58ecd6baa75e282d4908d3129fe2RoboErik private MediaSessionRecord mMediaSession; 17025c9469e010106467791b47b0fa83efda84491a21RoboErik private MediaSessionCompat mRccMediaSession; 1703e49860b0f76d8336c1d41831ed370b0ff94278efRoboErik private MediaSessionCompat mCompatSession; 17045c9469e010106467791b47b0fa83efda84491a21RoboErik private MediaSessionCompat.OnActiveChangeListener mSessionActiveListener = 17055c9469e010106467791b47b0fa83efda84491a21RoboErik new MediaSessionCompat.OnActiveChangeListener() { 17065c9469e010106467791b47b0fa83efda84491a21RoboErik @Override 17075c9469e010106467791b47b0fa83efda84491a21RoboErik public void onActiveChanged() { 17085c9469e010106467791b47b0fa83efda84491a21RoboErik if(mRccMediaSession != null) { 17095c9469e010106467791b47b0fa83efda84491a21RoboErik if (mRccMediaSession.isActive()) { 17105c9469e010106467791b47b0fa83efda84491a21RoboErik addRemoteControlClient(mRccMediaSession.getRemoteControlClient()); 17115c9469e010106467791b47b0fa83efda84491a21RoboErik } else { 17125c9469e010106467791b47b0fa83efda84491a21RoboErik removeRemoteControlClient(mRccMediaSession.getRemoteControlClient()); 17135c9469e010106467791b47b0fa83efda84491a21RoboErik } 17145c9469e010106467791b47b0fa83efda84491a21RoboErik } 17155c9469e010106467791b47b0fa83efda84491a21RoboErik } 17165c9469e010106467791b47b0fa83efda84491a21RoboErik }; 1717c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown 1718c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown GlobalMediaRouter(Context applicationContext) { 1719c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown mApplicationContext = applicationContext; 1720c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown mDisplayManager = DisplayManagerCompat.getInstance(applicationContext); 1721fb81e09002d1d611168b204fc50d259fc15f27b2Jeff Brown mLowRam = ActivityManagerCompat.isLowRamDevice( 1722fb81e09002d1d611168b204fc50d259fc15f27b2Jeff Brown (ActivityManager)applicationContext.getSystemService( 1723fb81e09002d1d611168b204fc50d259fc15f27b2Jeff Brown Context.ACTIVITY_SERVICE)); 1724fa326a4649d9d0e8113e315f6c8251fe686abce4Jeff Brown 1725fa326a4649d9d0e8113e315f6c8251fe686abce4Jeff Brown // Add the system media route provider for interoperating with 1726fa326a4649d9d0e8113e315f6c8251fe686abce4Jeff Brown // the framework media router. This one is special and receives 1727fa326a4649d9d0e8113e315f6c8251fe686abce4Jeff Brown // synchronization messages from the media router. 1728c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown mSystemProvider = SystemMediaRouteProvider.obtain(applicationContext, this); 1729fa326a4649d9d0e8113e315f6c8251fe686abce4Jeff Brown addProvider(mSystemProvider); 1730fa326a4649d9d0e8113e315f6c8251fe686abce4Jeff Brown } 1731fa326a4649d9d0e8113e315f6c8251fe686abce4Jeff Brown 1732fa326a4649d9d0e8113e315f6c8251fe686abce4Jeff Brown public void start() { 1733fa326a4649d9d0e8113e315f6c8251fe686abce4Jeff Brown // Start watching for routes published by registered media route 1734fa326a4649d9d0e8113e315f6c8251fe686abce4Jeff Brown // provider services. 1735fa326a4649d9d0e8113e315f6c8251fe686abce4Jeff Brown mRegisteredProviderWatcher = new RegisteredMediaRouteProviderWatcher( 17363efa63d3b896244713e84acbb5945562dce41d77Jeff Brown mApplicationContext, this); 1737fa326a4649d9d0e8113e315f6c8251fe686abce4Jeff Brown mRegisteredProviderWatcher.start(); 1738c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown } 1739c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown 1740c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown public MediaRouter getRouter(Context context) { 17419fcedc160282e6620f409ea46bf6728b35d011ddJeff Brown MediaRouter router; 17429fcedc160282e6620f409ea46bf6728b35d011ddJeff Brown for (int i = mRouters.size(); --i >= 0; ) { 17439fcedc160282e6620f409ea46bf6728b35d011ddJeff Brown router = mRouters.get(i).get(); 17449fcedc160282e6620f409ea46bf6728b35d011ddJeff Brown if (router == null) { 17459fcedc160282e6620f409ea46bf6728b35d011ddJeff Brown mRouters.remove(i); 17469fcedc160282e6620f409ea46bf6728b35d011ddJeff Brown } else if (router.mContext == context) { 17479fcedc160282e6620f409ea46bf6728b35d011ddJeff Brown return router; 17489fcedc160282e6620f409ea46bf6728b35d011ddJeff Brown } 1749c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown } 17509fcedc160282e6620f409ea46bf6728b35d011ddJeff Brown router = new MediaRouter(context); 17519fcedc160282e6620f409ea46bf6728b35d011ddJeff Brown mRouters.add(new WeakReference<MediaRouter>(router)); 1752c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown return router; 1753c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown } 1754c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown 1755c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown public ContentResolver getContentResolver() { 1756c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown return mApplicationContext.getContentResolver(); 1757c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown } 1758c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown 1759fa326a4649d9d0e8113e315f6c8251fe686abce4Jeff Brown public Context getProviderContext(String packageName) { 1760fa326a4649d9d0e8113e315f6c8251fe686abce4Jeff Brown if (packageName.equals(SystemMediaRouteProvider.PACKAGE_NAME)) { 1761fa326a4649d9d0e8113e315f6c8251fe686abce4Jeff Brown return mApplicationContext; 1762fa326a4649d9d0e8113e315f6c8251fe686abce4Jeff Brown } 1763fa326a4649d9d0e8113e315f6c8251fe686abce4Jeff Brown try { 1764fa326a4649d9d0e8113e315f6c8251fe686abce4Jeff Brown return mApplicationContext.createPackageContext( 1765fa326a4649d9d0e8113e315f6c8251fe686abce4Jeff Brown packageName, Context.CONTEXT_RESTRICTED); 1766fa326a4649d9d0e8113e315f6c8251fe686abce4Jeff Brown } catch (NameNotFoundException ex) { 1767fa326a4649d9d0e8113e315f6c8251fe686abce4Jeff Brown return null; 1768fa326a4649d9d0e8113e315f6c8251fe686abce4Jeff Brown } 1769fa326a4649d9d0e8113e315f6c8251fe686abce4Jeff Brown } 1770fa326a4649d9d0e8113e315f6c8251fe686abce4Jeff Brown 1771c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown public Display getDisplay(int displayId) { 1772c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown return mDisplayManager.getDisplay(displayId); 1773c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown } 1774c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown 1775fa326a4649d9d0e8113e315f6c8251fe686abce4Jeff Brown public void sendControlRequest(RouteInfo route, 1776c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown Intent intent, ControlRequestCallback callback) { 1777c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown if (route == mSelectedRoute && mSelectedRouteController != null) { 1778129abf73ce9be1bc172b945263c7975ad1a3006fJeff Brown if (mSelectedRouteController.onControlRequest(intent, callback)) { 1779fa326a4649d9d0e8113e315f6c8251fe686abce4Jeff Brown return; 1780fa326a4649d9d0e8113e315f6c8251fe686abce4Jeff Brown } 1781fa326a4649d9d0e8113e315f6c8251fe686abce4Jeff Brown } 1782fa326a4649d9d0e8113e315f6c8251fe686abce4Jeff Brown if (callback != null) { 17833d4c9459ed77f732dd3ba602713af6ebf9280c8cJeff Brown callback.onError(null, null); 1784c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown } 1785c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown } 1786c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown 1787c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown public void requestSetVolume(RouteInfo route, int volume) { 1788c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown if (route == mSelectedRoute && mSelectedRouteController != null) { 1789129abf73ce9be1bc172b945263c7975ad1a3006fJeff Brown mSelectedRouteController.onSetVolume(volume); 1790c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown } 1791c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown } 1792c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown 1793c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown public void requestUpdateVolume(RouteInfo route, int delta) { 1794c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown if (route == mSelectedRoute && mSelectedRouteController != null) { 1795129abf73ce9be1bc172b945263c7975ad1a3006fJeff Brown mSelectedRouteController.onUpdateVolume(delta); 1796c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown } 1797c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown } 1798c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown 1799e7a694bf5fc4bf417502509a8604fffc7a41d84cJae Seo public RouteInfo getRoute(String uniqueId) { 1800e7a694bf5fc4bf417502509a8604fffc7a41d84cJae Seo for (RouteInfo info : mRoutes) { 1801e7a694bf5fc4bf417502509a8604fffc7a41d84cJae Seo if (info.mUniqueId.equals(uniqueId)) { 1802e7a694bf5fc4bf417502509a8604fffc7a41d84cJae Seo return info; 1803e7a694bf5fc4bf417502509a8604fffc7a41d84cJae Seo } 1804e7a694bf5fc4bf417502509a8604fffc7a41d84cJae Seo } 1805e7a694bf5fc4bf417502509a8604fffc7a41d84cJae Seo return null; 1806e7a694bf5fc4bf417502509a8604fffc7a41d84cJae Seo } 1807e7a694bf5fc4bf417502509a8604fffc7a41d84cJae Seo 1808c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown public List<RouteInfo> getRoutes() { 1809c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown return mRoutes; 1810c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown } 1811c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown 1812fa326a4649d9d0e8113e315f6c8251fe686abce4Jeff Brown public List<ProviderInfo> getProviders() { 1813fa326a4649d9d0e8113e315f6c8251fe686abce4Jeff Brown return mProviders; 1814fa326a4649d9d0e8113e315f6c8251fe686abce4Jeff Brown } 1815fa326a4649d9d0e8113e315f6c8251fe686abce4Jeff Brown 1816c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown public RouteInfo getDefaultRoute() { 1817c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown if (mDefaultRoute == null) { 1818c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown // This should never happen once the media router has been fully 1819c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown // initialized but it is good to check for the error in case there 1820c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown // is a bug in provider initialization. 1821c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown throw new IllegalStateException("There is no default route. " 1822c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown + "The media router has not yet been fully initialized."); 1823c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown } 1824c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown return mDefaultRoute; 1825c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown } 1826c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown 1827c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown public RouteInfo getSelectedRoute() { 1828c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown if (mSelectedRoute == null) { 1829c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown // This should never happen once the media router has been fully 1830c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown // initialized but it is good to check for the error in case there 1831c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown // is a bug in provider initialization. 1832c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown throw new IllegalStateException("There is no currently selected route. " 1833c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown + "The media router has not yet been fully initialized."); 1834c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown } 1835c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown return mSelectedRoute; 1836c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown } 1837c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown 1838c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown public void selectRoute(RouteInfo route) { 183994be6100218126ce6a08bf1f56209578500b361fRoboErik selectRoute(route, MediaRouter.UNSELECT_REASON_ROUTE_CHANGED); 184094be6100218126ce6a08bf1f56209578500b361fRoboErik } 184194be6100218126ce6a08bf1f56209578500b361fRoboErik 184294be6100218126ce6a08bf1f56209578500b361fRoboErik public void selectRoute(RouteInfo route, int unselectReason) { 1843c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown if (!mRoutes.contains(route)) { 1844c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown Log.w(TAG, "Ignoring attempt to select removed route: " + route); 1845c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown return; 1846c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown } 1847c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown if (!route.mEnabled) { 1848c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown Log.w(TAG, "Ignoring attempt to select disabled route: " + route); 1849c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown return; 1850c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown } 1851c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown 185294be6100218126ce6a08bf1f56209578500b361fRoboErik setSelectedRouteInternal(route, unselectReason); 1853c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown } 1854c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown 185511417b1cfde8f1749905f2d735623af9214148afJeff Brown public boolean isRouteAvailable(MediaRouteSelector selector, int flags) { 1856fb81e09002d1d611168b204fc50d259fc15f27b2Jeff Brown if (selector.isEmpty()) { 1857fb81e09002d1d611168b204fc50d259fc15f27b2Jeff Brown return false; 1858fb81e09002d1d611168b204fc50d259fc15f27b2Jeff Brown } 1859fb81e09002d1d611168b204fc50d259fc15f27b2Jeff Brown 1860fb81e09002d1d611168b204fc50d259fc15f27b2Jeff Brown // On low-RAM devices, do not rely on actual discovery results unless asked to. 1861fb81e09002d1d611168b204fc50d259fc15f27b2Jeff Brown if ((flags & AVAILABILITY_FLAG_REQUIRE_MATCH) == 0 && mLowRam) { 1862fb81e09002d1d611168b204fc50d259fc15f27b2Jeff Brown return true; 1863fb81e09002d1d611168b204fc50d259fc15f27b2Jeff Brown } 1864fb81e09002d1d611168b204fc50d259fc15f27b2Jeff Brown 186511417b1cfde8f1749905f2d735623af9214148afJeff Brown // Check whether any existing routes match the selector. 186611417b1cfde8f1749905f2d735623af9214148afJeff Brown final int routeCount = mRoutes.size(); 186711417b1cfde8f1749905f2d735623af9214148afJeff Brown for (int i = 0; i < routeCount; i++) { 186811417b1cfde8f1749905f2d735623af9214148afJeff Brown RouteInfo route = mRoutes.get(i); 186911417b1cfde8f1749905f2d735623af9214148afJeff Brown if ((flags & AVAILABILITY_FLAG_IGNORE_DEFAULT_ROUTE) != 0 187011417b1cfde8f1749905f2d735623af9214148afJeff Brown && route.isDefault()) { 187111417b1cfde8f1749905f2d735623af9214148afJeff Brown continue; 187211417b1cfde8f1749905f2d735623af9214148afJeff Brown } 187311417b1cfde8f1749905f2d735623af9214148afJeff Brown if (route.matchesSelector(selector)) { 187411417b1cfde8f1749905f2d735623af9214148afJeff Brown return true; 187511417b1cfde8f1749905f2d735623af9214148afJeff Brown } 187611417b1cfde8f1749905f2d735623af9214148afJeff Brown } 187711417b1cfde8f1749905f2d735623af9214148afJeff Brown 187811417b1cfde8f1749905f2d735623af9214148afJeff Brown // It doesn't look like we can find a matching route right now. 187911417b1cfde8f1749905f2d735623af9214148afJeff Brown return false; 188011417b1cfde8f1749905f2d735623af9214148afJeff Brown } 188111417b1cfde8f1749905f2d735623af9214148afJeff Brown 188211417b1cfde8f1749905f2d735623af9214148afJeff Brown public void updateDiscoveryRequest() { 188311417b1cfde8f1749905f2d735623af9214148afJeff Brown // Combine all of the callback selectors and active scan flags. 1884f03da4a9e6cc02251c2f804eb6f25da61821d6a7Jeff Brown boolean discover = false; 188511417b1cfde8f1749905f2d735623af9214148afJeff Brown boolean activeScan = false; 188611417b1cfde8f1749905f2d735623af9214148afJeff Brown MediaRouteSelector.Builder builder = new MediaRouteSelector.Builder(); 18879fcedc160282e6620f409ea46bf6728b35d011ddJeff Brown for (int i = mRouters.size(); --i >= 0; ) { 18889fcedc160282e6620f409ea46bf6728b35d011ddJeff Brown MediaRouter router = mRouters.get(i).get(); 18899fcedc160282e6620f409ea46bf6728b35d011ddJeff Brown if (router == null) { 18909fcedc160282e6620f409ea46bf6728b35d011ddJeff Brown mRouters.remove(i); 18919fcedc160282e6620f409ea46bf6728b35d011ddJeff Brown } else { 18929fcedc160282e6620f409ea46bf6728b35d011ddJeff Brown final int count = router.mCallbackRecords.size(); 18939fcedc160282e6620f409ea46bf6728b35d011ddJeff Brown for (int j = 0; j < count; j++) { 18949fcedc160282e6620f409ea46bf6728b35d011ddJeff Brown CallbackRecord callback = router.mCallbackRecords.get(j); 18959fcedc160282e6620f409ea46bf6728b35d011ddJeff Brown builder.addSelector(callback.mSelector); 1896f03da4a9e6cc02251c2f804eb6f25da61821d6a7Jeff Brown if ((callback.mFlags & CALLBACK_FLAG_PERFORM_ACTIVE_SCAN) != 0) { 18979fcedc160282e6620f409ea46bf6728b35d011ddJeff Brown activeScan = true; 1898f03da4a9e6cc02251c2f804eb6f25da61821d6a7Jeff Brown discover = true; // perform active scan implies request discovery 1899f03da4a9e6cc02251c2f804eb6f25da61821d6a7Jeff Brown } 1900f03da4a9e6cc02251c2f804eb6f25da61821d6a7Jeff Brown if ((callback.mFlags & CALLBACK_FLAG_REQUEST_DISCOVERY) != 0) { 1901fb81e09002d1d611168b204fc50d259fc15f27b2Jeff Brown if (!mLowRam) { 1902fb81e09002d1d611168b204fc50d259fc15f27b2Jeff Brown discover = true; 1903fb81e09002d1d611168b204fc50d259fc15f27b2Jeff Brown } 1904fb81e09002d1d611168b204fc50d259fc15f27b2Jeff Brown } 1905fb81e09002d1d611168b204fc50d259fc15f27b2Jeff Brown if ((callback.mFlags & CALLBACK_FLAG_FORCE_DISCOVERY) != 0) { 1906f03da4a9e6cc02251c2f804eb6f25da61821d6a7Jeff Brown discover = true; 19079fcedc160282e6620f409ea46bf6728b35d011ddJeff Brown } 190811417b1cfde8f1749905f2d735623af9214148afJeff Brown } 190911417b1cfde8f1749905f2d735623af9214148afJeff Brown } 191011417b1cfde8f1749905f2d735623af9214148afJeff Brown } 1911f03da4a9e6cc02251c2f804eb6f25da61821d6a7Jeff Brown MediaRouteSelector selector = discover ? builder.build() : MediaRouteSelector.EMPTY; 191211417b1cfde8f1749905f2d735623af9214148afJeff Brown 191311417b1cfde8f1749905f2d735623af9214148afJeff Brown // Create a new discovery request. 191411417b1cfde8f1749905f2d735623af9214148afJeff Brown if (mDiscoveryRequest != null 191511417b1cfde8f1749905f2d735623af9214148afJeff Brown && mDiscoveryRequest.getSelector().equals(selector) 191611417b1cfde8f1749905f2d735623af9214148afJeff Brown && mDiscoveryRequest.isActiveScan() == activeScan) { 191711417b1cfde8f1749905f2d735623af9214148afJeff Brown return; // no change 191811417b1cfde8f1749905f2d735623af9214148afJeff Brown } 191911417b1cfde8f1749905f2d735623af9214148afJeff Brown if (selector.isEmpty() && !activeScan) { 192011417b1cfde8f1749905f2d735623af9214148afJeff Brown // Discovery is not needed. 192111417b1cfde8f1749905f2d735623af9214148afJeff Brown if (mDiscoveryRequest == null) { 192211417b1cfde8f1749905f2d735623af9214148afJeff Brown return; // no change 192311417b1cfde8f1749905f2d735623af9214148afJeff Brown } 192411417b1cfde8f1749905f2d735623af9214148afJeff Brown mDiscoveryRequest = null; 192511417b1cfde8f1749905f2d735623af9214148afJeff Brown } else { 192611417b1cfde8f1749905f2d735623af9214148afJeff Brown // Discovery is needed. 192711417b1cfde8f1749905f2d735623af9214148afJeff Brown mDiscoveryRequest = new MediaRouteDiscoveryRequest(selector, activeScan); 192811417b1cfde8f1749905f2d735623af9214148afJeff Brown } 192911417b1cfde8f1749905f2d735623af9214148afJeff Brown if (DEBUG) { 193011417b1cfde8f1749905f2d735623af9214148afJeff Brown Log.d(TAG, "Updated discovery request: " + mDiscoveryRequest); 193111417b1cfde8f1749905f2d735623af9214148afJeff Brown } 1932fb81e09002d1d611168b204fc50d259fc15f27b2Jeff Brown if (discover && !activeScan && mLowRam) { 1933fb81e09002d1d611168b204fc50d259fc15f27b2Jeff Brown Log.i(TAG, "Forcing passive route discovery on a low-RAM device, " 1934fb81e09002d1d611168b204fc50d259fc15f27b2Jeff Brown + "system performance may be affected. Please consider using " 1935fb81e09002d1d611168b204fc50d259fc15f27b2Jeff Brown + "CALLBACK_FLAG_REQUEST_DISCOVERY instead of " 1936fb81e09002d1d611168b204fc50d259fc15f27b2Jeff Brown + "CALLBACK_FLAG_FORCE_DISCOVERY."); 1937fb81e09002d1d611168b204fc50d259fc15f27b2Jeff Brown } 193811417b1cfde8f1749905f2d735623af9214148afJeff Brown 193911417b1cfde8f1749905f2d735623af9214148afJeff Brown // Notify providers. 194011417b1cfde8f1749905f2d735623af9214148afJeff Brown final int providerCount = mProviders.size(); 194111417b1cfde8f1749905f2d735623af9214148afJeff Brown for (int i = 0; i < providerCount; i++) { 194211417b1cfde8f1749905f2d735623af9214148afJeff Brown mProviders.get(i).mProviderInstance.setDiscoveryRequest(mDiscoveryRequest); 194328520a15611522424b52cf88e4a2dbeb1a9be42bJeff Brown } 194428520a15611522424b52cf88e4a2dbeb1a9be42bJeff Brown } 194528520a15611522424b52cf88e4a2dbeb1a9be42bJeff Brown 19463efa63d3b896244713e84acbb5945562dce41d77Jeff Brown @Override 1947fa326a4649d9d0e8113e315f6c8251fe686abce4Jeff Brown public void addProvider(MediaRouteProvider providerInstance) { 1948fa326a4649d9d0e8113e315f6c8251fe686abce4Jeff Brown int index = findProviderInfo(providerInstance); 1949c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown if (index < 0) { 1950c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown // 1. Add the provider to the list. 1951fa326a4649d9d0e8113e315f6c8251fe686abce4Jeff Brown ProviderInfo provider = new ProviderInfo(providerInstance); 1952fa326a4649d9d0e8113e315f6c8251fe686abce4Jeff Brown mProviders.add(provider); 195311417b1cfde8f1749905f2d735623af9214148afJeff Brown if (DEBUG) { 195411417b1cfde8f1749905f2d735623af9214148afJeff Brown Log.d(TAG, "Provider added: " + provider); 195511417b1cfde8f1749905f2d735623af9214148afJeff Brown } 1956fa326a4649d9d0e8113e315f6c8251fe686abce4Jeff Brown mCallbackHandler.post(CallbackHandler.MSG_PROVIDER_ADDED, provider); 1957c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown // 2. Create the provider's contents. 1958fa326a4649d9d0e8113e315f6c8251fe686abce4Jeff Brown updateProviderContents(provider, providerInstance.getDescriptor()); 1959c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown // 3. Register the provider callback. 196011417b1cfde8f1749905f2d735623af9214148afJeff Brown providerInstance.setCallback(mProviderCallback); 196111417b1cfde8f1749905f2d735623af9214148afJeff Brown // 4. Set the discovery request. 196211417b1cfde8f1749905f2d735623af9214148afJeff Brown providerInstance.setDiscoveryRequest(mDiscoveryRequest); 1963c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown } 1964c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown } 1965c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown 19663efa63d3b896244713e84acbb5945562dce41d77Jeff Brown @Override 1967fa326a4649d9d0e8113e315f6c8251fe686abce4Jeff Brown public void removeProvider(MediaRouteProvider providerInstance) { 1968fa326a4649d9d0e8113e315f6c8251fe686abce4Jeff Brown int index = findProviderInfo(providerInstance); 1969c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown if (index >= 0) { 197011417b1cfde8f1749905f2d735623af9214148afJeff Brown // 1. Unregister the provider callback. 197111417b1cfde8f1749905f2d735623af9214148afJeff Brown providerInstance.setCallback(null); 197211417b1cfde8f1749905f2d735623af9214148afJeff Brown // 2. Clear the discovery request. 197311417b1cfde8f1749905f2d735623af9214148afJeff Brown providerInstance.setDiscoveryRequest(null); 197428520a15611522424b52cf88e4a2dbeb1a9be42bJeff Brown // 3. Delete the provider's contents. 1975fa326a4649d9d0e8113e315f6c8251fe686abce4Jeff Brown ProviderInfo provider = mProviders.get(index); 1976fa326a4649d9d0e8113e315f6c8251fe686abce4Jeff Brown updateProviderContents(provider, null); 197728520a15611522424b52cf88e4a2dbeb1a9be42bJeff Brown // 4. Remove the provider from the list. 197811417b1cfde8f1749905f2d735623af9214148afJeff Brown if (DEBUG) { 197911417b1cfde8f1749905f2d735623af9214148afJeff Brown Log.d(TAG, "Provider removed: " + provider); 198011417b1cfde8f1749905f2d735623af9214148afJeff Brown } 1981fa326a4649d9d0e8113e315f6c8251fe686abce4Jeff Brown mCallbackHandler.post(CallbackHandler.MSG_PROVIDER_REMOVED, provider); 1982fa326a4649d9d0e8113e315f6c8251fe686abce4Jeff Brown mProviders.remove(index); 1983c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown } 1984c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown } 1985c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown 1986fa326a4649d9d0e8113e315f6c8251fe686abce4Jeff Brown private void updateProviderDescriptor(MediaRouteProvider providerInstance, 198711417b1cfde8f1749905f2d735623af9214148afJeff Brown MediaRouteProviderDescriptor descriptor) { 1988fa326a4649d9d0e8113e315f6c8251fe686abce4Jeff Brown int index = findProviderInfo(providerInstance); 1989c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown if (index >= 0) { 1990fa326a4649d9d0e8113e315f6c8251fe686abce4Jeff Brown // Update the provider's contents. 1991fa326a4649d9d0e8113e315f6c8251fe686abce4Jeff Brown ProviderInfo provider = mProviders.get(index); 1992fa326a4649d9d0e8113e315f6c8251fe686abce4Jeff Brown updateProviderContents(provider, descriptor); 1993c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown } 1994c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown } 1995c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown 1996fa326a4649d9d0e8113e315f6c8251fe686abce4Jeff Brown private int findProviderInfo(MediaRouteProvider providerInstance) { 1997fa326a4649d9d0e8113e315f6c8251fe686abce4Jeff Brown final int count = mProviders.size(); 1998c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown for (int i = 0; i < count; i++) { 1999fa326a4649d9d0e8113e315f6c8251fe686abce4Jeff Brown if (mProviders.get(i).mProviderInstance == providerInstance) { 2000c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown return i; 2001c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown } 2002c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown } 2003c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown return -1; 2004c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown } 2005c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown 2006fa326a4649d9d0e8113e315f6c8251fe686abce4Jeff Brown private void updateProviderContents(ProviderInfo provider, 200711417b1cfde8f1749905f2d735623af9214148afJeff Brown MediaRouteProviderDescriptor providerDescriptor) { 2008fa326a4649d9d0e8113e315f6c8251fe686abce4Jeff Brown if (provider.updateDescriptor(providerDescriptor)) { 2009c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown // Update all existing routes and reorder them to match 2010c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown // the order of their descriptors. 2011c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown int targetIndex = 0; 2012567dbb1c7f8a0cb3c4340ad607b6d833fc1b2270Jeff Brown boolean selectedRouteDescriptorChanged = false; 2013c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown if (providerDescriptor != null) { 2014fa326a4649d9d0e8113e315f6c8251fe686abce4Jeff Brown if (providerDescriptor.isValid()) { 201511417b1cfde8f1749905f2d735623af9214148afJeff Brown final List<MediaRouteDescriptor> routeDescriptors = 201611417b1cfde8f1749905f2d735623af9214148afJeff Brown providerDescriptor.getRoutes(); 201711417b1cfde8f1749905f2d735623af9214148afJeff Brown final int routeCount = routeDescriptors.size(); 2018e7a694bf5fc4bf417502509a8604fffc7a41d84cJae Seo // Updating route group's contents requires all member routes' information. 2019e7a694bf5fc4bf417502509a8604fffc7a41d84cJae Seo // Add the groups to the lists and update them later. 2020e7a694bf5fc4bf417502509a8604fffc7a41d84cJae Seo List<Pair<RouteInfo, MediaRouteDescriptor>> addedGroups = new ArrayList<>(); 2021e7a694bf5fc4bf417502509a8604fffc7a41d84cJae Seo List<Pair<RouteInfo, MediaRouteDescriptor>> updatedGroups = 2022e7a694bf5fc4bf417502509a8604fffc7a41d84cJae Seo new ArrayList<>(); 202311417b1cfde8f1749905f2d735623af9214148afJeff Brown for (int i = 0; i < routeCount; i++) { 202411417b1cfde8f1749905f2d735623af9214148afJeff Brown final MediaRouteDescriptor routeDescriptor = routeDescriptors.get(i); 2025c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown final String id = routeDescriptor.getId(); 2026fa326a4649d9d0e8113e315f6c8251fe686abce4Jeff Brown final int sourceIndex = provider.findRouteByDescriptorId(id); 2027c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown if (sourceIndex < 0) { 2028c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown // 1. Add the route to the list. 2029cb63b6ecac9786891514f241dec71695f09d3efbJeff Brown String uniqueId = assignRouteUniqueId(provider, id); 2030e7a694bf5fc4bf417502509a8604fffc7a41d84cJae Seo boolean isGroup = routeDescriptor.getChildIds() != null; 2031e7a694bf5fc4bf417502509a8604fffc7a41d84cJae Seo RouteInfo route = isGroup ? new RouteGroup(provider, id, uniqueId) : 2032e7a694bf5fc4bf417502509a8604fffc7a41d84cJae Seo new RouteInfo(provider, id, uniqueId); 2033fa326a4649d9d0e8113e315f6c8251fe686abce4Jeff Brown provider.mRoutes.add(targetIndex++, route); 2034c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown mRoutes.add(route); 2035c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown // 2. Create the route's contents. 2036e7a694bf5fc4bf417502509a8604fffc7a41d84cJae Seo if (isGroup) { 2037e7a694bf5fc4bf417502509a8604fffc7a41d84cJae Seo addedGroups.add(new Pair(route, routeDescriptor)); 2038e7a694bf5fc4bf417502509a8604fffc7a41d84cJae Seo } else { 2039e7a694bf5fc4bf417502509a8604fffc7a41d84cJae Seo route.maybeUpdateDescriptor(routeDescriptor); 2040e7a694bf5fc4bf417502509a8604fffc7a41d84cJae Seo // 3. Notify clients about addition. 2041e7a694bf5fc4bf417502509a8604fffc7a41d84cJae Seo if (DEBUG) { 2042e7a694bf5fc4bf417502509a8604fffc7a41d84cJae Seo Log.d(TAG, "Route added: " + route); 2043e7a694bf5fc4bf417502509a8604fffc7a41d84cJae Seo } 2044e7a694bf5fc4bf417502509a8604fffc7a41d84cJae Seo mCallbackHandler.post(CallbackHandler.MSG_ROUTE_ADDED, route); 204511417b1cfde8f1749905f2d735623af9214148afJeff Brown } 2046e7a694bf5fc4bf417502509a8604fffc7a41d84cJae Seo 2047fa326a4649d9d0e8113e315f6c8251fe686abce4Jeff Brown } else if (sourceIndex < targetIndex) { 2048fa326a4649d9d0e8113e315f6c8251fe686abce4Jeff Brown Log.w(TAG, "Ignoring route descriptor with duplicate id: " 2049fa326a4649d9d0e8113e315f6c8251fe686abce4Jeff Brown + routeDescriptor); 2050c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown } else { 2051c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown // 1. Reorder the route within the list. 2052fa326a4649d9d0e8113e315f6c8251fe686abce4Jeff Brown RouteInfo route = provider.mRoutes.get(sourceIndex); 2053fa326a4649d9d0e8113e315f6c8251fe686abce4Jeff Brown Collections.swap(provider.mRoutes, 2054c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown sourceIndex, targetIndex++); 2055c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown // 2. Update the route's contents. 2056e7a694bf5fc4bf417502509a8604fffc7a41d84cJae Seo if (route instanceof RouteGroup) { 2057e7a694bf5fc4bf417502509a8604fffc7a41d84cJae Seo updatedGroups.add(new Pair(route, routeDescriptor)); 2058e7a694bf5fc4bf417502509a8604fffc7a41d84cJae Seo } else { 2059e7a694bf5fc4bf417502509a8604fffc7a41d84cJae Seo // 3. Notify clients about changes. 2060e7a694bf5fc4bf417502509a8604fffc7a41d84cJae Seo if (updateRouteDescriptorAndNotify(route, routeDescriptor) 2061e7a694bf5fc4bf417502509a8604fffc7a41d84cJae Seo != 0) { 2062e7a694bf5fc4bf417502509a8604fffc7a41d84cJae Seo if (route == mSelectedRoute) { 2063e7a694bf5fc4bf417502509a8604fffc7a41d84cJae Seo selectedRouteDescriptorChanged = true; 2064567dbb1c7f8a0cb3c4340ad607b6d833fc1b2270Jeff Brown } 206511417b1cfde8f1749905f2d735623af9214148afJeff Brown } 2066c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown } 2067c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown } 2068c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown } 2069e7a694bf5fc4bf417502509a8604fffc7a41d84cJae Seo // Update the new and/or existing groups. 2070e7a694bf5fc4bf417502509a8604fffc7a41d84cJae Seo for (Pair<RouteInfo, MediaRouteDescriptor> pair : addedGroups) { 2071e7a694bf5fc4bf417502509a8604fffc7a41d84cJae Seo RouteInfo route = pair.first; 2072e7a694bf5fc4bf417502509a8604fffc7a41d84cJae Seo route.maybeUpdateDescriptor(pair.second); 2073e7a694bf5fc4bf417502509a8604fffc7a41d84cJae Seo if (DEBUG) { 2074e7a694bf5fc4bf417502509a8604fffc7a41d84cJae Seo Log.d(TAG, "Route added: " + route); 2075e7a694bf5fc4bf417502509a8604fffc7a41d84cJae Seo } 2076e7a694bf5fc4bf417502509a8604fffc7a41d84cJae Seo mCallbackHandler.post(CallbackHandler.MSG_ROUTE_ADDED, route); 2077e7a694bf5fc4bf417502509a8604fffc7a41d84cJae Seo } 2078e7a694bf5fc4bf417502509a8604fffc7a41d84cJae Seo for (Pair<RouteInfo, MediaRouteDescriptor> pair : updatedGroups) { 2079e7a694bf5fc4bf417502509a8604fffc7a41d84cJae Seo RouteInfo route = pair.first; 2080e7a694bf5fc4bf417502509a8604fffc7a41d84cJae Seo if (updateRouteDescriptorAndNotify(route, pair.second) != 0) { 2081e7a694bf5fc4bf417502509a8604fffc7a41d84cJae Seo if (route == mSelectedRoute) { 2082e7a694bf5fc4bf417502509a8604fffc7a41d84cJae Seo selectedRouteDescriptorChanged = true; 2083e7a694bf5fc4bf417502509a8604fffc7a41d84cJae Seo } 2084e7a694bf5fc4bf417502509a8604fffc7a41d84cJae Seo } 2085e7a694bf5fc4bf417502509a8604fffc7a41d84cJae Seo } 2086fa326a4649d9d0e8113e315f6c8251fe686abce4Jeff Brown } else { 2087fa326a4649d9d0e8113e315f6c8251fe686abce4Jeff Brown Log.w(TAG, "Ignoring invalid provider descriptor: " + providerDescriptor); 2088c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown } 2089c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown } 2090c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown 2091c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown // Dispose all remaining routes that do not have matching descriptors. 2092fa326a4649d9d0e8113e315f6c8251fe686abce4Jeff Brown for (int i = provider.mRoutes.size() - 1; i >= targetIndex; i--) { 2093fa326a4649d9d0e8113e315f6c8251fe686abce4Jeff Brown // 1. Delete the route's contents. 2094fa326a4649d9d0e8113e315f6c8251fe686abce4Jeff Brown RouteInfo route = provider.mRoutes.get(i); 2095e7a694bf5fc4bf417502509a8604fffc7a41d84cJae Seo route.maybeUpdateDescriptor(null); 2096fa326a4649d9d0e8113e315f6c8251fe686abce4Jeff Brown // 2. Remove the route from the list. 209711417b1cfde8f1749905f2d735623af9214148afJeff Brown mRoutes.remove(route); 209835af7c925d660508fe0892f38eec99a3845630e4Chong Zhang } 209935af7c925d660508fe0892f38eec99a3845630e4Chong Zhang 2100567dbb1c7f8a0cb3c4340ad607b6d833fc1b2270Jeff Brown // Update the selected route if needed. 2101567dbb1c7f8a0cb3c4340ad607b6d833fc1b2270Jeff Brown updateSelectedRouteIfNeeded(selectedRouteDescriptorChanged); 210235af7c925d660508fe0892f38eec99a3845630e4Chong Zhang 210335af7c925d660508fe0892f38eec99a3845630e4Chong Zhang // Now notify clients about routes that were removed. 210435af7c925d660508fe0892f38eec99a3845630e4Chong Zhang // We do this after updating the selected route to ensure 210535af7c925d660508fe0892f38eec99a3845630e4Chong Zhang // that the framework media router observes the new route 210635af7c925d660508fe0892f38eec99a3845630e4Chong Zhang // selection before the removal since removing the currently 210735af7c925d660508fe0892f38eec99a3845630e4Chong Zhang // selected route may have side-effects. 210835af7c925d660508fe0892f38eec99a3845630e4Chong Zhang for (int i = provider.mRoutes.size() - 1; i >= targetIndex; i--) { 210935af7c925d660508fe0892f38eec99a3845630e4Chong Zhang RouteInfo route = provider.mRoutes.remove(i); 211011417b1cfde8f1749905f2d735623af9214148afJeff Brown if (DEBUG) { 211111417b1cfde8f1749905f2d735623af9214148afJeff Brown Log.d(TAG, "Route removed: " + route); 211211417b1cfde8f1749905f2d735623af9214148afJeff Brown } 2113fa326a4649d9d0e8113e315f6c8251fe686abce4Jeff Brown mCallbackHandler.post(CallbackHandler.MSG_ROUTE_REMOVED, route); 2114c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown } 2115fa326a4649d9d0e8113e315f6c8251fe686abce4Jeff Brown 211611417b1cfde8f1749905f2d735623af9214148afJeff Brown // Notify provider changed. 211711417b1cfde8f1749905f2d735623af9214148afJeff Brown if (DEBUG) { 211811417b1cfde8f1749905f2d735623af9214148afJeff Brown Log.d(TAG, "Provider changed: " + provider); 211911417b1cfde8f1749905f2d735623af9214148afJeff Brown } 212011417b1cfde8f1749905f2d735623af9214148afJeff Brown mCallbackHandler.post(CallbackHandler.MSG_PROVIDER_CHANGED, provider); 2121c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown } 2122c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown } 2123c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown 2124e7a694bf5fc4bf417502509a8604fffc7a41d84cJae Seo private int updateRouteDescriptorAndNotify(RouteInfo route, 2125e7a694bf5fc4bf417502509a8604fffc7a41d84cJae Seo MediaRouteDescriptor routeDescriptor) { 2126e7a694bf5fc4bf417502509a8604fffc7a41d84cJae Seo int changes = route.maybeUpdateDescriptor(routeDescriptor); 2127e7a694bf5fc4bf417502509a8604fffc7a41d84cJae Seo if (changes != 0) { 2128e7a694bf5fc4bf417502509a8604fffc7a41d84cJae Seo if ((changes & RouteInfo.CHANGE_GENERAL) != 0) { 2129e7a694bf5fc4bf417502509a8604fffc7a41d84cJae Seo if (DEBUG) { 2130e7a694bf5fc4bf417502509a8604fffc7a41d84cJae Seo Log.d(TAG, "Route changed: " + route); 2131e7a694bf5fc4bf417502509a8604fffc7a41d84cJae Seo } 2132e7a694bf5fc4bf417502509a8604fffc7a41d84cJae Seo mCallbackHandler.post( 2133e7a694bf5fc4bf417502509a8604fffc7a41d84cJae Seo CallbackHandler.MSG_ROUTE_CHANGED, route); 2134e7a694bf5fc4bf417502509a8604fffc7a41d84cJae Seo } 2135e7a694bf5fc4bf417502509a8604fffc7a41d84cJae Seo if ((changes & RouteInfo.CHANGE_VOLUME) != 0) { 2136e7a694bf5fc4bf417502509a8604fffc7a41d84cJae Seo if (DEBUG) { 2137e7a694bf5fc4bf417502509a8604fffc7a41d84cJae Seo Log.d(TAG, "Route volume changed: " + route); 2138e7a694bf5fc4bf417502509a8604fffc7a41d84cJae Seo } 2139e7a694bf5fc4bf417502509a8604fffc7a41d84cJae Seo mCallbackHandler.post( 2140e7a694bf5fc4bf417502509a8604fffc7a41d84cJae Seo CallbackHandler.MSG_ROUTE_VOLUME_CHANGED, route); 2141e7a694bf5fc4bf417502509a8604fffc7a41d84cJae Seo } 2142e7a694bf5fc4bf417502509a8604fffc7a41d84cJae Seo if ((changes & RouteInfo.CHANGE_PRESENTATION_DISPLAY) != 0) { 2143e7a694bf5fc4bf417502509a8604fffc7a41d84cJae Seo if (DEBUG) { 2144e7a694bf5fc4bf417502509a8604fffc7a41d84cJae Seo Log.d(TAG, "Route presentation display changed: " 2145e7a694bf5fc4bf417502509a8604fffc7a41d84cJae Seo + route); 2146e7a694bf5fc4bf417502509a8604fffc7a41d84cJae Seo } 2147e7a694bf5fc4bf417502509a8604fffc7a41d84cJae Seo mCallbackHandler.post(CallbackHandler. 2148e7a694bf5fc4bf417502509a8604fffc7a41d84cJae Seo MSG_ROUTE_PRESENTATION_DISPLAY_CHANGED, route); 2149e7a694bf5fc4bf417502509a8604fffc7a41d84cJae Seo } 2150e7a694bf5fc4bf417502509a8604fffc7a41d84cJae Seo } 2151e7a694bf5fc4bf417502509a8604fffc7a41d84cJae Seo return changes; 2152e7a694bf5fc4bf417502509a8604fffc7a41d84cJae Seo } 2153e7a694bf5fc4bf417502509a8604fffc7a41d84cJae Seo 2154cb63b6ecac9786891514f241dec71695f09d3efbJeff Brown private String assignRouteUniqueId(ProviderInfo provider, String routeDescriptorId) { 2155cb63b6ecac9786891514f241dec71695f09d3efbJeff Brown // Although route descriptor ids are unique within a provider, it's 2156cb63b6ecac9786891514f241dec71695f09d3efbJeff Brown // possible for there to be two providers with the same package name. 2157cb63b6ecac9786891514f241dec71695f09d3efbJeff Brown // Therefore we must dedupe the composite id. 2158adf0f4a217e14894af07dfa9f46cad7d98b8a7f4Jeff Brown String uniqueId = provider.getComponentName().flattenToShortString() 2159adf0f4a217e14894af07dfa9f46cad7d98b8a7f4Jeff Brown + ":" + routeDescriptorId; 2160cb63b6ecac9786891514f241dec71695f09d3efbJeff Brown if (findRouteByUniqueId(uniqueId) < 0) { 2161cb63b6ecac9786891514f241dec71695f09d3efbJeff Brown return uniqueId; 2162cb63b6ecac9786891514f241dec71695f09d3efbJeff Brown } 2163cb63b6ecac9786891514f241dec71695f09d3efbJeff Brown for (int i = 2; ; i++) { 2164cb63b6ecac9786891514f241dec71695f09d3efbJeff Brown String newUniqueId = String.format(Locale.US, "%s_%d", uniqueId, i); 2165cb63b6ecac9786891514f241dec71695f09d3efbJeff Brown if (findRouteByUniqueId(newUniqueId) < 0) { 2166cb63b6ecac9786891514f241dec71695f09d3efbJeff Brown return newUniqueId; 2167cb63b6ecac9786891514f241dec71695f09d3efbJeff Brown } 2168cb63b6ecac9786891514f241dec71695f09d3efbJeff Brown } 2169cb63b6ecac9786891514f241dec71695f09d3efbJeff Brown } 2170cb63b6ecac9786891514f241dec71695f09d3efbJeff Brown 2171cb63b6ecac9786891514f241dec71695f09d3efbJeff Brown private int findRouteByUniqueId(String uniqueId) { 2172cb63b6ecac9786891514f241dec71695f09d3efbJeff Brown final int count = mRoutes.size(); 2173cb63b6ecac9786891514f241dec71695f09d3efbJeff Brown for (int i = 0; i < count; i++) { 2174cb63b6ecac9786891514f241dec71695f09d3efbJeff Brown if (mRoutes.get(i).mUniqueId.equals(uniqueId)) { 2175cb63b6ecac9786891514f241dec71695f09d3efbJeff Brown return i; 2176cb63b6ecac9786891514f241dec71695f09d3efbJeff Brown } 2177cb63b6ecac9786891514f241dec71695f09d3efbJeff Brown } 2178cb63b6ecac9786891514f241dec71695f09d3efbJeff Brown return -1; 2179cb63b6ecac9786891514f241dec71695f09d3efbJeff Brown } 2180cb63b6ecac9786891514f241dec71695f09d3efbJeff Brown 2181567dbb1c7f8a0cb3c4340ad607b6d833fc1b2270Jeff Brown private void updateSelectedRouteIfNeeded(boolean selectedRouteDescriptorChanged) { 2182567dbb1c7f8a0cb3c4340ad607b6d833fc1b2270Jeff Brown // Update default route. 2183567dbb1c7f8a0cb3c4340ad607b6d833fc1b2270Jeff Brown if (mDefaultRoute != null && !isRouteSelectable(mDefaultRoute)) { 218435af7c925d660508fe0892f38eec99a3845630e4Chong Zhang Log.i(TAG, "Clearing the default route because it " 2185567dbb1c7f8a0cb3c4340ad607b6d833fc1b2270Jeff Brown + "is no longer selectable: " + mDefaultRoute); 2186c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown mDefaultRoute = null; 2187c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown } 2188c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown if (mDefaultRoute == null && !mRoutes.isEmpty()) { 2189c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown for (RouteInfo route : mRoutes) { 2190c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown if (isSystemDefaultRoute(route) && isRouteSelectable(route)) { 2191c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown mDefaultRoute = route; 2192567dbb1c7f8a0cb3c4340ad607b6d833fc1b2270Jeff Brown Log.i(TAG, "Found default route: " + mDefaultRoute); 2193c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown break; 2194c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown } 2195c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown } 2196c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown } 2197567dbb1c7f8a0cb3c4340ad607b6d833fc1b2270Jeff Brown 2198567dbb1c7f8a0cb3c4340ad607b6d833fc1b2270Jeff Brown // Update selected route. 2199567dbb1c7f8a0cb3c4340ad607b6d833fc1b2270Jeff Brown if (mSelectedRoute != null && !isRouteSelectable(mSelectedRoute)) { 2200567dbb1c7f8a0cb3c4340ad607b6d833fc1b2270Jeff Brown Log.i(TAG, "Unselecting the current route because it " 2201567dbb1c7f8a0cb3c4340ad607b6d833fc1b2270Jeff Brown + "is no longer selectable: " + mSelectedRoute); 220294be6100218126ce6a08bf1f56209578500b361fRoboErik setSelectedRouteInternal(null, 220394be6100218126ce6a08bf1f56209578500b361fRoboErik MediaRouter.UNSELECT_REASON_UNKNOWN); 2204567dbb1c7f8a0cb3c4340ad607b6d833fc1b2270Jeff Brown } 2205c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown if (mSelectedRoute == null) { 2206567dbb1c7f8a0cb3c4340ad607b6d833fc1b2270Jeff Brown // Choose a new route. 2207567dbb1c7f8a0cb3c4340ad607b6d833fc1b2270Jeff Brown // This will have the side-effect of updating the playback info when 2208567dbb1c7f8a0cb3c4340ad607b6d833fc1b2270Jeff Brown // the new route is selected. 220994be6100218126ce6a08bf1f56209578500b361fRoboErik setSelectedRouteInternal(chooseFallbackRoute(), 221094be6100218126ce6a08bf1f56209578500b361fRoboErik MediaRouter.UNSELECT_REASON_UNKNOWN); 2211567dbb1c7f8a0cb3c4340ad607b6d833fc1b2270Jeff Brown } else if (selectedRouteDescriptorChanged) { 2212567dbb1c7f8a0cb3c4340ad607b6d833fc1b2270Jeff Brown // Update the playback info because the properties of the route have changed. 2213567dbb1c7f8a0cb3c4340ad607b6d833fc1b2270Jeff Brown updatePlaybackInfoFromSelectedRoute(); 2214c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown } 2215c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown } 2216c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown 221735af7c925d660508fe0892f38eec99a3845630e4Chong Zhang private RouteInfo chooseFallbackRoute() { 221835af7c925d660508fe0892f38eec99a3845630e4Chong Zhang // When the current route is removed or no longer selectable, 221935af7c925d660508fe0892f38eec99a3845630e4Chong Zhang // we want to revert to a live audio route if there is 222035af7c925d660508fe0892f38eec99a3845630e4Chong Zhang // one (usually Bluetooth A2DP). Failing that, use 222135af7c925d660508fe0892f38eec99a3845630e4Chong Zhang // the default route. 222235af7c925d660508fe0892f38eec99a3845630e4Chong Zhang for (RouteInfo route : mRoutes) { 222335af7c925d660508fe0892f38eec99a3845630e4Chong Zhang if (route != mDefaultRoute 222435af7c925d660508fe0892f38eec99a3845630e4Chong Zhang && isSystemLiveAudioOnlyRoute(route) 222535af7c925d660508fe0892f38eec99a3845630e4Chong Zhang && isRouteSelectable(route)) { 222635af7c925d660508fe0892f38eec99a3845630e4Chong Zhang return route; 222735af7c925d660508fe0892f38eec99a3845630e4Chong Zhang } 222835af7c925d660508fe0892f38eec99a3845630e4Chong Zhang } 222935af7c925d660508fe0892f38eec99a3845630e4Chong Zhang return mDefaultRoute; 223035af7c925d660508fe0892f38eec99a3845630e4Chong Zhang } 223135af7c925d660508fe0892f38eec99a3845630e4Chong Zhang 223235af7c925d660508fe0892f38eec99a3845630e4Chong Zhang private boolean isSystemLiveAudioOnlyRoute(RouteInfo route) { 223335af7c925d660508fe0892f38eec99a3845630e4Chong Zhang return route.getProviderInstance() == mSystemProvider 223435af7c925d660508fe0892f38eec99a3845630e4Chong Zhang && route.supportsControlCategory(MediaControlIntent.CATEGORY_LIVE_AUDIO) 223535af7c925d660508fe0892f38eec99a3845630e4Chong Zhang && !route.supportsControlCategory(MediaControlIntent.CATEGORY_LIVE_VIDEO); 223635af7c925d660508fe0892f38eec99a3845630e4Chong Zhang } 223735af7c925d660508fe0892f38eec99a3845630e4Chong Zhang 2238c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown private boolean isRouteSelectable(RouteInfo route) { 2239c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown // This tests whether the route is still valid and enabled. 2240c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown // The route descriptor field is set to null when the route is removed. 2241c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown return route.mDescriptor != null && route.mEnabled; 2242c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown } 2243c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown 2244c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown private boolean isSystemDefaultRoute(RouteInfo route) { 2245fa326a4649d9d0e8113e315f6c8251fe686abce4Jeff Brown return route.getProviderInstance() == mSystemProvider 2246c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown && route.mDescriptorId.equals( 2247c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown SystemMediaRouteProvider.DEFAULT_ROUTE_ID); 2248c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown } 2249c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown 225094be6100218126ce6a08bf1f56209578500b361fRoboErik private void setSelectedRouteInternal(RouteInfo route, int unselectReason) { 2251c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown if (mSelectedRoute != route) { 2252c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown if (mSelectedRoute != null) { 225311417b1cfde8f1749905f2d735623af9214148afJeff Brown if (DEBUG) { 225494be6100218126ce6a08bf1f56209578500b361fRoboErik Log.d(TAG, "Route unselected: " + mSelectedRoute + " reason: " 225594be6100218126ce6a08bf1f56209578500b361fRoboErik + unselectReason); 225611417b1cfde8f1749905f2d735623af9214148afJeff Brown } 2257c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown mCallbackHandler.post(CallbackHandler.MSG_ROUTE_UNSELECTED, mSelectedRoute); 2258c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown if (mSelectedRouteController != null) { 225994be6100218126ce6a08bf1f56209578500b361fRoboErik mSelectedRouteController.onUnselect(unselectReason); 2260129abf73ce9be1bc172b945263c7975ad1a3006fJeff Brown mSelectedRouteController.onRelease(); 2261c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown mSelectedRouteController = null; 2262c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown } 2263c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown } 2264c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown 2265c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown mSelectedRoute = route; 2266c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown 2267c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown if (mSelectedRoute != null) { 2268fa326a4649d9d0e8113e315f6c8251fe686abce4Jeff Brown mSelectedRouteController = route.getProviderInstance().onCreateRouteController( 2269c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown route.mDescriptorId); 2270c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown if (mSelectedRouteController != null) { 2271129abf73ce9be1bc172b945263c7975ad1a3006fJeff Brown mSelectedRouteController.onSelect(); 2272c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown } 227311417b1cfde8f1749905f2d735623af9214148afJeff Brown if (DEBUG) { 227411417b1cfde8f1749905f2d735623af9214148afJeff Brown Log.d(TAG, "Route selected: " + mSelectedRoute); 227511417b1cfde8f1749905f2d735623af9214148afJeff Brown } 2276c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown mCallbackHandler.post(CallbackHandler.MSG_ROUTE_SELECTED, mSelectedRoute); 2277c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown } 2278567dbb1c7f8a0cb3c4340ad607b6d833fc1b2270Jeff Brown 2279567dbb1c7f8a0cb3c4340ad607b6d833fc1b2270Jeff Brown updatePlaybackInfoFromSelectedRoute(); 2280c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown } 2281c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown } 2282c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown 2283c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown @Override 2284c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown public RouteInfo getSystemRouteByDescriptorId(String id) { 2285fa326a4649d9d0e8113e315f6c8251fe686abce4Jeff Brown int providerIndex = findProviderInfo(mSystemProvider); 2286c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown if (providerIndex >= 0) { 2287fa326a4649d9d0e8113e315f6c8251fe686abce4Jeff Brown ProviderInfo provider = mProviders.get(providerIndex); 2288fa326a4649d9d0e8113e315f6c8251fe686abce4Jeff Brown int routeIndex = provider.findRouteByDescriptorId(id); 2289c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown if (routeIndex >= 0) { 2290fa326a4649d9d0e8113e315f6c8251fe686abce4Jeff Brown return provider.mRoutes.get(routeIndex); 2291c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown } 2292c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown } 2293c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown return null; 2294c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown } 2295c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown 2296567dbb1c7f8a0cb3c4340ad607b6d833fc1b2270Jeff Brown public void addRemoteControlClient(Object rcc) { 2297567dbb1c7f8a0cb3c4340ad607b6d833fc1b2270Jeff Brown int index = findRemoteControlClientRecord(rcc); 2298567dbb1c7f8a0cb3c4340ad607b6d833fc1b2270Jeff Brown if (index < 0) { 2299567dbb1c7f8a0cb3c4340ad607b6d833fc1b2270Jeff Brown RemoteControlClientRecord record = new RemoteControlClientRecord(rcc); 2300567dbb1c7f8a0cb3c4340ad607b6d833fc1b2270Jeff Brown mRemoteControlClients.add(record); 2301567dbb1c7f8a0cb3c4340ad607b6d833fc1b2270Jeff Brown } 2302567dbb1c7f8a0cb3c4340ad607b6d833fc1b2270Jeff Brown } 2303567dbb1c7f8a0cb3c4340ad607b6d833fc1b2270Jeff Brown 2304567dbb1c7f8a0cb3c4340ad607b6d833fc1b2270Jeff Brown public void removeRemoteControlClient(Object rcc) { 2305567dbb1c7f8a0cb3c4340ad607b6d833fc1b2270Jeff Brown int index = findRemoteControlClientRecord(rcc); 2306567dbb1c7f8a0cb3c4340ad607b6d833fc1b2270Jeff Brown if (index >= 0) { 2307567dbb1c7f8a0cb3c4340ad607b6d833fc1b2270Jeff Brown RemoteControlClientRecord record = mRemoteControlClients.remove(index); 2308567dbb1c7f8a0cb3c4340ad607b6d833fc1b2270Jeff Brown record.disconnect(); 2309567dbb1c7f8a0cb3c4340ad607b6d833fc1b2270Jeff Brown } 2310567dbb1c7f8a0cb3c4340ad607b6d833fc1b2270Jeff Brown } 2311567dbb1c7f8a0cb3c4340ad607b6d833fc1b2270Jeff Brown 2312bbcdf78e350d58ecd6baa75e282d4908d3129fe2RoboErik public void setMediaSession(Object session) { 2313bbcdf78e350d58ecd6baa75e282d4908d3129fe2RoboErik if (mMediaSession != null) { 2314bbcdf78e350d58ecd6baa75e282d4908d3129fe2RoboErik mMediaSession.clearVolumeHandling(); 2315bbcdf78e350d58ecd6baa75e282d4908d3129fe2RoboErik } 2316bbcdf78e350d58ecd6baa75e282d4908d3129fe2RoboErik if (session == null) { 2317bbcdf78e350d58ecd6baa75e282d4908d3129fe2RoboErik mMediaSession = null; 2318bbcdf78e350d58ecd6baa75e282d4908d3129fe2RoboErik } else { 2319bbcdf78e350d58ecd6baa75e282d4908d3129fe2RoboErik mMediaSession = new MediaSessionRecord(session); 2320bbcdf78e350d58ecd6baa75e282d4908d3129fe2RoboErik updatePlaybackInfoFromSelectedRoute(); 2321bbcdf78e350d58ecd6baa75e282d4908d3129fe2RoboErik } 2322bbcdf78e350d58ecd6baa75e282d4908d3129fe2RoboErik } 2323bbcdf78e350d58ecd6baa75e282d4908d3129fe2RoboErik 23245c9469e010106467791b47b0fa83efda84491a21RoboErik public void setMediaSessionCompat(final MediaSessionCompat session) { 2325e49860b0f76d8336c1d41831ed370b0ff94278efRoboErik mCompatSession = session; 23265c9469e010106467791b47b0fa83efda84491a21RoboErik if (android.os.Build.VERSION.SDK_INT >= 21) { 2327d81fed9a46023f2ec728a20fd130480bd5c90f73Ian Lake setMediaSession(session != null ? session.getMediaSession() : null); 23285c9469e010106467791b47b0fa83efda84491a21RoboErik } else if (android.os.Build.VERSION.SDK_INT >= 14) { 23295c9469e010106467791b47b0fa83efda84491a21RoboErik if (mRccMediaSession != null) { 23305c9469e010106467791b47b0fa83efda84491a21RoboErik removeRemoteControlClient(mRccMediaSession.getRemoteControlClient()); 23315c9469e010106467791b47b0fa83efda84491a21RoboErik mRccMediaSession.removeOnActiveChangeListener(mSessionActiveListener); 23325c9469e010106467791b47b0fa83efda84491a21RoboErik } 23335c9469e010106467791b47b0fa83efda84491a21RoboErik mRccMediaSession = session; 2334d81fed9a46023f2ec728a20fd130480bd5c90f73Ian Lake if (session != null) { 2335d81fed9a46023f2ec728a20fd130480bd5c90f73Ian Lake session.addOnActiveChangeListener(mSessionActiveListener); 2336d81fed9a46023f2ec728a20fd130480bd5c90f73Ian Lake if (session.isActive()) { 2337d81fed9a46023f2ec728a20fd130480bd5c90f73Ian Lake addRemoteControlClient(session.getRemoteControlClient()); 2338d81fed9a46023f2ec728a20fd130480bd5c90f73Ian Lake } 23395c9469e010106467791b47b0fa83efda84491a21RoboErik } 23405c9469e010106467791b47b0fa83efda84491a21RoboErik } 23415c9469e010106467791b47b0fa83efda84491a21RoboErik } 23425c9469e010106467791b47b0fa83efda84491a21RoboErik 234394be6100218126ce6a08bf1f56209578500b361fRoboErik public MediaSessionCompat.Token getMediaSessionToken() { 234494be6100218126ce6a08bf1f56209578500b361fRoboErik if (mMediaSession != null) { 234594be6100218126ce6a08bf1f56209578500b361fRoboErik return mMediaSession.getToken(); 2346e49860b0f76d8336c1d41831ed370b0ff94278efRoboErik } else if (mCompatSession != null) { 2347e49860b0f76d8336c1d41831ed370b0ff94278efRoboErik return mCompatSession.getSessionToken(); 234894be6100218126ce6a08bf1f56209578500b361fRoboErik } 234994be6100218126ce6a08bf1f56209578500b361fRoboErik return null; 235094be6100218126ce6a08bf1f56209578500b361fRoboErik } 235194be6100218126ce6a08bf1f56209578500b361fRoboErik 2352567dbb1c7f8a0cb3c4340ad607b6d833fc1b2270Jeff Brown private int findRemoteControlClientRecord(Object rcc) { 2353567dbb1c7f8a0cb3c4340ad607b6d833fc1b2270Jeff Brown final int count = mRemoteControlClients.size(); 2354567dbb1c7f8a0cb3c4340ad607b6d833fc1b2270Jeff Brown for (int i = 0; i < count; i++) { 2355567dbb1c7f8a0cb3c4340ad607b6d833fc1b2270Jeff Brown RemoteControlClientRecord record = mRemoteControlClients.get(i); 2356567dbb1c7f8a0cb3c4340ad607b6d833fc1b2270Jeff Brown if (record.getRemoteControlClient() == rcc) { 2357567dbb1c7f8a0cb3c4340ad607b6d833fc1b2270Jeff Brown return i; 2358567dbb1c7f8a0cb3c4340ad607b6d833fc1b2270Jeff Brown } 2359567dbb1c7f8a0cb3c4340ad607b6d833fc1b2270Jeff Brown } 2360567dbb1c7f8a0cb3c4340ad607b6d833fc1b2270Jeff Brown return -1; 2361567dbb1c7f8a0cb3c4340ad607b6d833fc1b2270Jeff Brown } 2362567dbb1c7f8a0cb3c4340ad607b6d833fc1b2270Jeff Brown 2363567dbb1c7f8a0cb3c4340ad607b6d833fc1b2270Jeff Brown private void updatePlaybackInfoFromSelectedRoute() { 2364567dbb1c7f8a0cb3c4340ad607b6d833fc1b2270Jeff Brown if (mSelectedRoute != null) { 2365567dbb1c7f8a0cb3c4340ad607b6d833fc1b2270Jeff Brown mPlaybackInfo.volume = mSelectedRoute.getVolume(); 2366567dbb1c7f8a0cb3c4340ad607b6d833fc1b2270Jeff Brown mPlaybackInfo.volumeMax = mSelectedRoute.getVolumeMax(); 2367567dbb1c7f8a0cb3c4340ad607b6d833fc1b2270Jeff Brown mPlaybackInfo.volumeHandling = mSelectedRoute.getVolumeHandling(); 2368567dbb1c7f8a0cb3c4340ad607b6d833fc1b2270Jeff Brown mPlaybackInfo.playbackStream = mSelectedRoute.getPlaybackStream(); 2369567dbb1c7f8a0cb3c4340ad607b6d833fc1b2270Jeff Brown mPlaybackInfo.playbackType = mSelectedRoute.getPlaybackType(); 2370567dbb1c7f8a0cb3c4340ad607b6d833fc1b2270Jeff Brown 2371567dbb1c7f8a0cb3c4340ad607b6d833fc1b2270Jeff Brown final int count = mRemoteControlClients.size(); 2372567dbb1c7f8a0cb3c4340ad607b6d833fc1b2270Jeff Brown for (int i = 0; i < count; i++) { 2373567dbb1c7f8a0cb3c4340ad607b6d833fc1b2270Jeff Brown RemoteControlClientRecord record = mRemoteControlClients.get(i); 2374567dbb1c7f8a0cb3c4340ad607b6d833fc1b2270Jeff Brown record.updatePlaybackInfo(); 2375567dbb1c7f8a0cb3c4340ad607b6d833fc1b2270Jeff Brown } 2376bbcdf78e350d58ecd6baa75e282d4908d3129fe2RoboErik if (mMediaSession != null) { 2377f9ea1ad16cec2b123cb01426a9526ebf8a460d1bRoboErik if (mSelectedRoute == getDefaultRoute()) { 2378f9ea1ad16cec2b123cb01426a9526ebf8a460d1bRoboErik // Local route 2379f9ea1ad16cec2b123cb01426a9526ebf8a460d1bRoboErik mMediaSession.clearVolumeHandling(); 2380f9ea1ad16cec2b123cb01426a9526ebf8a460d1bRoboErik } else { 2381f0e4dea75691d2fd1256508136ecce88bef6067bIan Lake @VolumeProviderCompat.ControlType int controlType = 2382f0e4dea75691d2fd1256508136ecce88bef6067bIan Lake VolumeProviderCompat.VOLUME_CONTROL_FIXED; 2383f9ea1ad16cec2b123cb01426a9526ebf8a460d1bRoboErik if (mPlaybackInfo.volumeHandling 2384f9ea1ad16cec2b123cb01426a9526ebf8a460d1bRoboErik == MediaRouter.RouteInfo.PLAYBACK_VOLUME_VARIABLE) { 2385f9ea1ad16cec2b123cb01426a9526ebf8a460d1bRoboErik controlType = VolumeProviderCompat.VOLUME_CONTROL_ABSOLUTE; 2386f9ea1ad16cec2b123cb01426a9526ebf8a460d1bRoboErik } 2387f9ea1ad16cec2b123cb01426a9526ebf8a460d1bRoboErik mMediaSession.configureVolume(controlType, mPlaybackInfo.volumeMax, 2388f9ea1ad16cec2b123cb01426a9526ebf8a460d1bRoboErik mPlaybackInfo.volume); 2389bbcdf78e350d58ecd6baa75e282d4908d3129fe2RoboErik } 2390f9ea1ad16cec2b123cb01426a9526ebf8a460d1bRoboErik } 2391f9ea1ad16cec2b123cb01426a9526ebf8a460d1bRoboErik } else { 2392f9ea1ad16cec2b123cb01426a9526ebf8a460d1bRoboErik if (mMediaSession != null) { 2393f9ea1ad16cec2b123cb01426a9526ebf8a460d1bRoboErik mMediaSession.clearVolumeHandling(); 2394bbcdf78e350d58ecd6baa75e282d4908d3129fe2RoboErik } 2395567dbb1c7f8a0cb3c4340ad607b6d833fc1b2270Jeff Brown } 2396567dbb1c7f8a0cb3c4340ad607b6d833fc1b2270Jeff Brown } 2397567dbb1c7f8a0cb3c4340ad607b6d833fc1b2270Jeff Brown 2398c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown private final class ProviderCallback extends MediaRouteProvider.Callback { 2399c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown @Override 2400c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown public void onDescriptorChanged(MediaRouteProvider provider, 240111417b1cfde8f1749905f2d735623af9214148afJeff Brown MediaRouteProviderDescriptor descriptor) { 2402c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown updateProviderDescriptor(provider, descriptor); 2403c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown } 2404c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown } 2405c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown 2406bbcdf78e350d58ecd6baa75e282d4908d3129fe2RoboErik private final class MediaSessionRecord { 2407bbcdf78e350d58ecd6baa75e282d4908d3129fe2RoboErik private final MediaSessionCompat mMsCompat; 2408bbcdf78e350d58ecd6baa75e282d4908d3129fe2RoboErik 2409f0e4dea75691d2fd1256508136ecce88bef6067bIan Lake private @VolumeProviderCompat.ControlType int mControlType; 2410bbcdf78e350d58ecd6baa75e282d4908d3129fe2RoboErik private int mMaxVolume; 2411bbcdf78e350d58ecd6baa75e282d4908d3129fe2RoboErik private VolumeProviderCompat mVpCompat; 2412bbcdf78e350d58ecd6baa75e282d4908d3129fe2RoboErik 2413bbcdf78e350d58ecd6baa75e282d4908d3129fe2RoboErik public MediaSessionRecord(Object mediaSession) { 24145c9469e010106467791b47b0fa83efda84491a21RoboErik mMsCompat = MediaSessionCompat.obtain(mApplicationContext, mediaSession); 2415bbcdf78e350d58ecd6baa75e282d4908d3129fe2RoboErik } 2416bbcdf78e350d58ecd6baa75e282d4908d3129fe2RoboErik 2417f0e4dea75691d2fd1256508136ecce88bef6067bIan Lake public void configureVolume(@VolumeProviderCompat.ControlType int controlType, 2418f0e4dea75691d2fd1256508136ecce88bef6067bIan Lake int max, int current) { 2419bbcdf78e350d58ecd6baa75e282d4908d3129fe2RoboErik if (mVpCompat != null && controlType == mControlType && max == mMaxVolume) { 2420bbcdf78e350d58ecd6baa75e282d4908d3129fe2RoboErik // If we haven't changed control type or max just set the 2421bbcdf78e350d58ecd6baa75e282d4908d3129fe2RoboErik // new current volume 2422bbcdf78e350d58ecd6baa75e282d4908d3129fe2RoboErik mVpCompat.setCurrentVolume(current); 2423bbcdf78e350d58ecd6baa75e282d4908d3129fe2RoboErik } else { 2424bbcdf78e350d58ecd6baa75e282d4908d3129fe2RoboErik // Otherwise create a new provider and update 2425bbcdf78e350d58ecd6baa75e282d4908d3129fe2RoboErik mVpCompat = new VolumeProviderCompat(controlType, max, current) { 2426bbcdf78e350d58ecd6baa75e282d4908d3129fe2RoboErik @Override 2427a9b9eae2cf9c9ef87c94c6a09a8664d30d60d7e4RoboErik public void onSetVolumeTo(final int volume) { 2428a9b9eae2cf9c9ef87c94c6a09a8664d30d60d7e4RoboErik mCallbackHandler.post(new Runnable() { 2429a9b9eae2cf9c9ef87c94c6a09a8664d30d60d7e4RoboErik @Override 2430a9b9eae2cf9c9ef87c94c6a09a8664d30d60d7e4RoboErik public void run() { 2431a9b9eae2cf9c9ef87c94c6a09a8664d30d60d7e4RoboErik if (mSelectedRoute != null) { 2432a9b9eae2cf9c9ef87c94c6a09a8664d30d60d7e4RoboErik mSelectedRoute.requestSetVolume(volume); 2433a9b9eae2cf9c9ef87c94c6a09a8664d30d60d7e4RoboErik } 2434a9b9eae2cf9c9ef87c94c6a09a8664d30d60d7e4RoboErik } 2435a9b9eae2cf9c9ef87c94c6a09a8664d30d60d7e4RoboErik }); 2436bbcdf78e350d58ecd6baa75e282d4908d3129fe2RoboErik } 2437bbcdf78e350d58ecd6baa75e282d4908d3129fe2RoboErik 2438bbcdf78e350d58ecd6baa75e282d4908d3129fe2RoboErik @Override 2439a9b9eae2cf9c9ef87c94c6a09a8664d30d60d7e4RoboErik public void onAdjustVolume(final int direction) { 2440a9b9eae2cf9c9ef87c94c6a09a8664d30d60d7e4RoboErik mCallbackHandler.post(new Runnable() { 2441a9b9eae2cf9c9ef87c94c6a09a8664d30d60d7e4RoboErik @Override 2442a9b9eae2cf9c9ef87c94c6a09a8664d30d60d7e4RoboErik public void run() { 2443a9b9eae2cf9c9ef87c94c6a09a8664d30d60d7e4RoboErik if (mSelectedRoute != null) { 2444a9b9eae2cf9c9ef87c94c6a09a8664d30d60d7e4RoboErik mSelectedRoute.requestUpdateVolume(direction); 2445a9b9eae2cf9c9ef87c94c6a09a8664d30d60d7e4RoboErik } 2446a9b9eae2cf9c9ef87c94c6a09a8664d30d60d7e4RoboErik } 2447a9b9eae2cf9c9ef87c94c6a09a8664d30d60d7e4RoboErik }); 2448bbcdf78e350d58ecd6baa75e282d4908d3129fe2RoboErik } 2449bbcdf78e350d58ecd6baa75e282d4908d3129fe2RoboErik }; 2450bbcdf78e350d58ecd6baa75e282d4908d3129fe2RoboErik mMsCompat.setPlaybackToRemote(mVpCompat); 2451bbcdf78e350d58ecd6baa75e282d4908d3129fe2RoboErik } 2452bbcdf78e350d58ecd6baa75e282d4908d3129fe2RoboErik } 2453bbcdf78e350d58ecd6baa75e282d4908d3129fe2RoboErik 2454bbcdf78e350d58ecd6baa75e282d4908d3129fe2RoboErik public void clearVolumeHandling() { 2455bbcdf78e350d58ecd6baa75e282d4908d3129fe2RoboErik mMsCompat.setPlaybackToLocal(mPlaybackInfo.playbackStream); 2456bbcdf78e350d58ecd6baa75e282d4908d3129fe2RoboErik mVpCompat = null; 2457bbcdf78e350d58ecd6baa75e282d4908d3129fe2RoboErik } 2458bbcdf78e350d58ecd6baa75e282d4908d3129fe2RoboErik 245994be6100218126ce6a08bf1f56209578500b361fRoboErik public MediaSessionCompat.Token getToken() { 246094be6100218126ce6a08bf1f56209578500b361fRoboErik return mMsCompat.getSessionToken(); 246194be6100218126ce6a08bf1f56209578500b361fRoboErik } 246294be6100218126ce6a08bf1f56209578500b361fRoboErik 2463bbcdf78e350d58ecd6baa75e282d4908d3129fe2RoboErik } 2464bbcdf78e350d58ecd6baa75e282d4908d3129fe2RoboErik 2465567dbb1c7f8a0cb3c4340ad607b6d833fc1b2270Jeff Brown private final class RemoteControlClientRecord 2466567dbb1c7f8a0cb3c4340ad607b6d833fc1b2270Jeff Brown implements RemoteControlClientCompat.VolumeCallback { 2467567dbb1c7f8a0cb3c4340ad607b6d833fc1b2270Jeff Brown private final RemoteControlClientCompat mRccCompat; 2468567dbb1c7f8a0cb3c4340ad607b6d833fc1b2270Jeff Brown private boolean mDisconnected; 2469567dbb1c7f8a0cb3c4340ad607b6d833fc1b2270Jeff Brown 2470567dbb1c7f8a0cb3c4340ad607b6d833fc1b2270Jeff Brown public RemoteControlClientRecord(Object rcc) { 2471567dbb1c7f8a0cb3c4340ad607b6d833fc1b2270Jeff Brown mRccCompat = RemoteControlClientCompat.obtain(mApplicationContext, rcc); 2472567dbb1c7f8a0cb3c4340ad607b6d833fc1b2270Jeff Brown mRccCompat.setVolumeCallback(this); 2473567dbb1c7f8a0cb3c4340ad607b6d833fc1b2270Jeff Brown updatePlaybackInfo(); 2474567dbb1c7f8a0cb3c4340ad607b6d833fc1b2270Jeff Brown } 2475567dbb1c7f8a0cb3c4340ad607b6d833fc1b2270Jeff Brown 2476567dbb1c7f8a0cb3c4340ad607b6d833fc1b2270Jeff Brown public Object getRemoteControlClient() { 2477567dbb1c7f8a0cb3c4340ad607b6d833fc1b2270Jeff Brown return mRccCompat.getRemoteControlClient(); 2478567dbb1c7f8a0cb3c4340ad607b6d833fc1b2270Jeff Brown } 2479567dbb1c7f8a0cb3c4340ad607b6d833fc1b2270Jeff Brown 2480567dbb1c7f8a0cb3c4340ad607b6d833fc1b2270Jeff Brown public void disconnect() { 2481567dbb1c7f8a0cb3c4340ad607b6d833fc1b2270Jeff Brown mDisconnected = true; 2482567dbb1c7f8a0cb3c4340ad607b6d833fc1b2270Jeff Brown mRccCompat.setVolumeCallback(null); 2483567dbb1c7f8a0cb3c4340ad607b6d833fc1b2270Jeff Brown } 2484567dbb1c7f8a0cb3c4340ad607b6d833fc1b2270Jeff Brown 2485567dbb1c7f8a0cb3c4340ad607b6d833fc1b2270Jeff Brown public void updatePlaybackInfo() { 2486567dbb1c7f8a0cb3c4340ad607b6d833fc1b2270Jeff Brown mRccCompat.setPlaybackInfo(mPlaybackInfo); 2487567dbb1c7f8a0cb3c4340ad607b6d833fc1b2270Jeff Brown } 2488567dbb1c7f8a0cb3c4340ad607b6d833fc1b2270Jeff Brown 2489567dbb1c7f8a0cb3c4340ad607b6d833fc1b2270Jeff Brown @Override 2490567dbb1c7f8a0cb3c4340ad607b6d833fc1b2270Jeff Brown public void onVolumeSetRequest(int volume) { 2491567dbb1c7f8a0cb3c4340ad607b6d833fc1b2270Jeff Brown if (!mDisconnected && mSelectedRoute != null) { 2492567dbb1c7f8a0cb3c4340ad607b6d833fc1b2270Jeff Brown mSelectedRoute.requestSetVolume(volume); 2493567dbb1c7f8a0cb3c4340ad607b6d833fc1b2270Jeff Brown } 2494567dbb1c7f8a0cb3c4340ad607b6d833fc1b2270Jeff Brown } 2495567dbb1c7f8a0cb3c4340ad607b6d833fc1b2270Jeff Brown 2496567dbb1c7f8a0cb3c4340ad607b6d833fc1b2270Jeff Brown @Override 2497567dbb1c7f8a0cb3c4340ad607b6d833fc1b2270Jeff Brown public void onVolumeUpdateRequest(int direction) { 2498567dbb1c7f8a0cb3c4340ad607b6d833fc1b2270Jeff Brown if (!mDisconnected && mSelectedRoute != null) { 2499567dbb1c7f8a0cb3c4340ad607b6d833fc1b2270Jeff Brown mSelectedRoute.requestUpdateVolume(direction); 2500567dbb1c7f8a0cb3c4340ad607b6d833fc1b2270Jeff Brown } 2501567dbb1c7f8a0cb3c4340ad607b6d833fc1b2270Jeff Brown } 2502567dbb1c7f8a0cb3c4340ad607b6d833fc1b2270Jeff Brown } 2503567dbb1c7f8a0cb3c4340ad607b6d833fc1b2270Jeff Brown 2504c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown private final class CallbackHandler extends Handler { 25059fcedc160282e6620f409ea46bf6728b35d011ddJeff Brown private final ArrayList<CallbackRecord> mTempCallbackRecords = 25069fcedc160282e6620f409ea46bf6728b35d011ddJeff Brown new ArrayList<CallbackRecord>(); 2507c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown 250811417b1cfde8f1749905f2d735623af9214148afJeff Brown private static final int MSG_TYPE_MASK = 0xff00; 250911417b1cfde8f1749905f2d735623af9214148afJeff Brown private static final int MSG_TYPE_ROUTE = 0x0100; 251011417b1cfde8f1749905f2d735623af9214148afJeff Brown private static final int MSG_TYPE_PROVIDER = 0x0200; 251111417b1cfde8f1749905f2d735623af9214148afJeff Brown 251211417b1cfde8f1749905f2d735623af9214148afJeff Brown public static final int MSG_ROUTE_ADDED = MSG_TYPE_ROUTE | 1; 251311417b1cfde8f1749905f2d735623af9214148afJeff Brown public static final int MSG_ROUTE_REMOVED = MSG_TYPE_ROUTE | 2; 251411417b1cfde8f1749905f2d735623af9214148afJeff Brown public static final int MSG_ROUTE_CHANGED = MSG_TYPE_ROUTE | 3; 251511417b1cfde8f1749905f2d735623af9214148afJeff Brown public static final int MSG_ROUTE_VOLUME_CHANGED = MSG_TYPE_ROUTE | 4; 251611417b1cfde8f1749905f2d735623af9214148afJeff Brown public static final int MSG_ROUTE_PRESENTATION_DISPLAY_CHANGED = MSG_TYPE_ROUTE | 5; 251711417b1cfde8f1749905f2d735623af9214148afJeff Brown public static final int MSG_ROUTE_SELECTED = MSG_TYPE_ROUTE | 6; 251811417b1cfde8f1749905f2d735623af9214148afJeff Brown public static final int MSG_ROUTE_UNSELECTED = MSG_TYPE_ROUTE | 7; 251911417b1cfde8f1749905f2d735623af9214148afJeff Brown 252011417b1cfde8f1749905f2d735623af9214148afJeff Brown public static final int MSG_PROVIDER_ADDED = MSG_TYPE_PROVIDER | 1; 252111417b1cfde8f1749905f2d735623af9214148afJeff Brown public static final int MSG_PROVIDER_REMOVED = MSG_TYPE_PROVIDER | 2; 252211417b1cfde8f1749905f2d735623af9214148afJeff Brown public static final int MSG_PROVIDER_CHANGED = MSG_TYPE_PROVIDER | 3; 2523c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown 2524fa326a4649d9d0e8113e315f6c8251fe686abce4Jeff Brown public void post(int msg, Object obj) { 2525fa326a4649d9d0e8113e315f6c8251fe686abce4Jeff Brown obtainMessage(msg, obj).sendToTarget(); 2526c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown } 2527c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown 2528c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown @Override 2529c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown public void handleMessage(Message msg) { 2530c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown final int what = msg.what; 2531fa326a4649d9d0e8113e315f6c8251fe686abce4Jeff Brown final Object obj = msg.obj; 2532c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown 2533c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown // Synchronize state with the system media router. 2534fa326a4649d9d0e8113e315f6c8251fe686abce4Jeff Brown syncWithSystemProvider(what, obj); 2535c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown 2536c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown // Invoke all registered callbacks. 25379fcedc160282e6620f409ea46bf6728b35d011ddJeff Brown // Build a list of callbacks before invoking them in case callbacks 25389fcedc160282e6620f409ea46bf6728b35d011ddJeff Brown // are added or removed during dispatch. 2539c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown try { 25409fcedc160282e6620f409ea46bf6728b35d011ddJeff Brown for (int i = mRouters.size(); --i >= 0; ) { 25419fcedc160282e6620f409ea46bf6728b35d011ddJeff Brown MediaRouter router = mRouters.get(i).get(); 25429fcedc160282e6620f409ea46bf6728b35d011ddJeff Brown if (router == null) { 25439fcedc160282e6620f409ea46bf6728b35d011ddJeff Brown mRouters.remove(i); 25449fcedc160282e6620f409ea46bf6728b35d011ddJeff Brown } else { 25459fcedc160282e6620f409ea46bf6728b35d011ddJeff Brown mTempCallbackRecords.addAll(router.mCallbackRecords); 2546c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown } 2547c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown } 25489fcedc160282e6620f409ea46bf6728b35d011ddJeff Brown 25499fcedc160282e6620f409ea46bf6728b35d011ddJeff Brown final int callbackCount = mTempCallbackRecords.size(); 25509fcedc160282e6620f409ea46bf6728b35d011ddJeff Brown for (int i = 0; i < callbackCount; i++) { 25519fcedc160282e6620f409ea46bf6728b35d011ddJeff Brown invokeCallback(mTempCallbackRecords.get(i), what, obj); 25529fcedc160282e6620f409ea46bf6728b35d011ddJeff Brown } 2553c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown } finally { 25549fcedc160282e6620f409ea46bf6728b35d011ddJeff Brown mTempCallbackRecords.clear(); 2555c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown } 2556c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown } 2557c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown 2558fa326a4649d9d0e8113e315f6c8251fe686abce4Jeff Brown private void syncWithSystemProvider(int what, Object obj) { 2559c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown switch (what) { 2560c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown case MSG_ROUTE_ADDED: 2561fa326a4649d9d0e8113e315f6c8251fe686abce4Jeff Brown mSystemProvider.onSyncRouteAdded((RouteInfo)obj); 2562c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown break; 2563c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown case MSG_ROUTE_REMOVED: 2564fa326a4649d9d0e8113e315f6c8251fe686abce4Jeff Brown mSystemProvider.onSyncRouteRemoved((RouteInfo)obj); 2565c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown break; 2566c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown case MSG_ROUTE_CHANGED: 2567fa326a4649d9d0e8113e315f6c8251fe686abce4Jeff Brown mSystemProvider.onSyncRouteChanged((RouteInfo)obj); 2568c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown break; 2569c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown case MSG_ROUTE_SELECTED: 2570fa326a4649d9d0e8113e315f6c8251fe686abce4Jeff Brown mSystemProvider.onSyncRouteSelected((RouteInfo)obj); 2571c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown break; 2572c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown } 2573c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown } 2574c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown 25759fcedc160282e6620f409ea46bf6728b35d011ddJeff Brown private void invokeCallback(CallbackRecord record, int what, Object obj) { 25769fcedc160282e6620f409ea46bf6728b35d011ddJeff Brown final MediaRouter router = record.mRouter; 257711417b1cfde8f1749905f2d735623af9214148afJeff Brown final MediaRouter.Callback callback = record.mCallback; 257811417b1cfde8f1749905f2d735623af9214148afJeff Brown switch (what & MSG_TYPE_MASK) { 257911417b1cfde8f1749905f2d735623af9214148afJeff Brown case MSG_TYPE_ROUTE: { 258011417b1cfde8f1749905f2d735623af9214148afJeff Brown final RouteInfo route = (RouteInfo)obj; 258111417b1cfde8f1749905f2d735623af9214148afJeff Brown if (!record.filterRouteEvent(route)) { 258211417b1cfde8f1749905f2d735623af9214148afJeff Brown break; 258311417b1cfde8f1749905f2d735623af9214148afJeff Brown } 258411417b1cfde8f1749905f2d735623af9214148afJeff Brown switch (what) { 258511417b1cfde8f1749905f2d735623af9214148afJeff Brown case MSG_ROUTE_ADDED: 258611417b1cfde8f1749905f2d735623af9214148afJeff Brown callback.onRouteAdded(router, route); 258711417b1cfde8f1749905f2d735623af9214148afJeff Brown break; 258811417b1cfde8f1749905f2d735623af9214148afJeff Brown case MSG_ROUTE_REMOVED: 258911417b1cfde8f1749905f2d735623af9214148afJeff Brown callback.onRouteRemoved(router, route); 259011417b1cfde8f1749905f2d735623af9214148afJeff Brown break; 259111417b1cfde8f1749905f2d735623af9214148afJeff Brown case MSG_ROUTE_CHANGED: 259211417b1cfde8f1749905f2d735623af9214148afJeff Brown callback.onRouteChanged(router, route); 259311417b1cfde8f1749905f2d735623af9214148afJeff Brown break; 259411417b1cfde8f1749905f2d735623af9214148afJeff Brown case MSG_ROUTE_VOLUME_CHANGED: 259511417b1cfde8f1749905f2d735623af9214148afJeff Brown callback.onRouteVolumeChanged(router, route); 259611417b1cfde8f1749905f2d735623af9214148afJeff Brown break; 259711417b1cfde8f1749905f2d735623af9214148afJeff Brown case MSG_ROUTE_PRESENTATION_DISPLAY_CHANGED: 259811417b1cfde8f1749905f2d735623af9214148afJeff Brown callback.onRoutePresentationDisplayChanged(router, route); 259911417b1cfde8f1749905f2d735623af9214148afJeff Brown break; 260011417b1cfde8f1749905f2d735623af9214148afJeff Brown case MSG_ROUTE_SELECTED: 260111417b1cfde8f1749905f2d735623af9214148afJeff Brown callback.onRouteSelected(router, route); 260211417b1cfde8f1749905f2d735623af9214148afJeff Brown break; 260311417b1cfde8f1749905f2d735623af9214148afJeff Brown case MSG_ROUTE_UNSELECTED: 260411417b1cfde8f1749905f2d735623af9214148afJeff Brown callback.onRouteUnselected(router, route); 260511417b1cfde8f1749905f2d735623af9214148afJeff Brown break; 260611417b1cfde8f1749905f2d735623af9214148afJeff Brown } 2607c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown break; 260811417b1cfde8f1749905f2d735623af9214148afJeff Brown } 260911417b1cfde8f1749905f2d735623af9214148afJeff Brown case MSG_TYPE_PROVIDER: { 261011417b1cfde8f1749905f2d735623af9214148afJeff Brown final ProviderInfo provider = (ProviderInfo)obj; 261111417b1cfde8f1749905f2d735623af9214148afJeff Brown switch (what) { 261211417b1cfde8f1749905f2d735623af9214148afJeff Brown case MSG_PROVIDER_ADDED: 261311417b1cfde8f1749905f2d735623af9214148afJeff Brown callback.onProviderAdded(router, provider); 261411417b1cfde8f1749905f2d735623af9214148afJeff Brown break; 261511417b1cfde8f1749905f2d735623af9214148afJeff Brown case MSG_PROVIDER_REMOVED: 261611417b1cfde8f1749905f2d735623af9214148afJeff Brown callback.onProviderRemoved(router, provider); 261711417b1cfde8f1749905f2d735623af9214148afJeff Brown break; 261811417b1cfde8f1749905f2d735623af9214148afJeff Brown case MSG_PROVIDER_CHANGED: 261911417b1cfde8f1749905f2d735623af9214148afJeff Brown callback.onProviderChanged(router, provider); 262011417b1cfde8f1749905f2d735623af9214148afJeff Brown break; 262111417b1cfde8f1749905f2d735623af9214148afJeff Brown } 262211417b1cfde8f1749905f2d735623af9214148afJeff Brown } 2623c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown } 2624c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown } 2625c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown } 2626c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown } 2627c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown} 2628