19a1de308cea2d160778fd977825f10a07b49d738Adam Powell/* 29a1de308cea2d160778fd977825f10a07b49d738Adam Powell * Copyright (C) 2012 The Android Open Source Project 39a1de308cea2d160778fd977825f10a07b49d738Adam Powell * 49a1de308cea2d160778fd977825f10a07b49d738Adam Powell * Licensed under the Apache License, Version 2.0 (the "License"); 59a1de308cea2d160778fd977825f10a07b49d738Adam Powell * you may not use this file except in compliance with the License. 69a1de308cea2d160778fd977825f10a07b49d738Adam Powell * You may obtain a copy of the License at 79a1de308cea2d160778fd977825f10a07b49d738Adam Powell * 89a1de308cea2d160778fd977825f10a07b49d738Adam Powell * http://www.apache.org/licenses/LICENSE-2.0 99a1de308cea2d160778fd977825f10a07b49d738Adam Powell * 109a1de308cea2d160778fd977825f10a07b49d738Adam Powell * Unless required by applicable law or agreed to in writing, software 119a1de308cea2d160778fd977825f10a07b49d738Adam Powell * distributed under the License is distributed on an "AS IS" BASIS, 129a1de308cea2d160778fd977825f10a07b49d738Adam Powell * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 139a1de308cea2d160778fd977825f10a07b49d738Adam Powell * See the License for the specific language governing permissions and 149a1de308cea2d160778fd977825f10a07b49d738Adam Powell * limitations under the License. 159a1de308cea2d160778fd977825f10a07b49d738Adam Powell */ 169a1de308cea2d160778fd977825f10a07b49d738Adam Powell 179a1de308cea2d160778fd977825f10a07b49d738Adam Powellpackage android.media; 189a1de308cea2d160778fd977825f10a07b49d738Adam Powell 19af574183c274f51d04487a9c8355e9f34a1150f2Jeff Brownimport android.Manifest; 207b9c912f536925ac6ec43935d6e97506851b33d6Tor Norbyeimport android.annotation.DrawableRes; 219dd66417ea984e4ff809ecdba78e3f55a8a17467Insun Kangimport android.annotation.IntDef; 229d93a378c5158d14f80d46af70433234330ec568P.Y. Laligandimport android.annotation.NonNull; 23d86b8fea43ebb6e5c31691b44d8ceb0d8d3c9072Jeff Sharkeyimport android.annotation.SystemService; 24ba50b97cff80e73620a0e3d13cae169e095974a7Dianne Hackbornimport android.app.ActivityThread; 258e37a85bf3dc39519942698dc90a3951306b934bAdam Powellimport android.content.BroadcastReceiver; 269a1de308cea2d160778fd977825f10a07b49d738Adam Powellimport android.content.Context; 278e37a85bf3dc39519942698dc90a3951306b934bAdam Powellimport android.content.Intent; 288e37a85bf3dc39519942698dc90a3951306b934bAdam Powellimport android.content.IntentFilter; 29af574183c274f51d04487a9c8355e9f34a1150f2Jeff Brownimport android.content.pm.PackageManager; 30b58b8f832d06b0ffa8886eba5a4916578a3b8743Dianne Hackbornimport android.content.res.Resources; 31ae20ae1a8aaa013813c356ae1d9541ca7ff020aeAdam Powellimport android.graphics.drawable.Drawable; 32705ab808cf023e0cc38c2ba7cdb9571942cdc04fAdam Powellimport android.hardware.display.DisplayManager; 33705ab808cf023e0cc38c2ba7cdb9571942cdc04fAdam Powellimport android.hardware.display.WifiDisplay; 34705ab808cf023e0cc38c2ba7cdb9571942cdc04fAdam Powellimport android.hardware.display.WifiDisplayStatus; 355d3114b64a88ac1f72becd8d46f148c666f64aa3RoboErikimport android.media.session.MediaSession; 369a1de308cea2d160778fd977825f10a07b49d738Adam Powellimport android.os.Handler; 37632ca417f0a33e3fa9ccece531afa2db3f0d4a30Dianne Hackbornimport android.os.IBinder; 38af574183c274f51d04487a9c8355e9f34a1150f2Jeff Brownimport android.os.Process; 39632ca417f0a33e3fa9ccece531afa2db3f0d4a30Dianne Hackbornimport android.os.RemoteException; 40632ca417f0a33e3fa9ccece531afa2db3f0d4a30Dianne Hackbornimport android.os.ServiceManager; 4169b07161bebdb2c726e3a826c2268866f1a94517Jeff Brownimport android.os.UserHandle; 42632ca417f0a33e3fa9ccece531afa2db3f0d4a30Dianne Hackbornimport android.text.TextUtils; 439a1de308cea2d160778fd977825f10a07b49d738Adam Powellimport android.util.Log; 44705ab808cf023e0cc38c2ba7cdb9571942cdc04fAdam Powellimport android.view.Display; 459a1de308cea2d160778fd977825f10a07b49d738Adam Powell 469dd66417ea984e4ff809ecdba78e3f55a8a17467Insun Kangimport java.lang.annotation.Retention; 479dd66417ea984e4ff809ecdba78e3f55a8a17467Insun Kangimport java.lang.annotation.RetentionPolicy; 489a1de308cea2d160778fd977825f10a07b49d738Adam Powellimport java.util.ArrayList; 499a1de308cea2d160778fd977825f10a07b49d738Adam Powellimport java.util.HashMap; 50d0d2cda9d414da73773285d7fee9e13aef3495e9Adam Powellimport java.util.List; 51e6585b32ea586743258a5457e2184ffc087f2d2fKenny Rootimport java.util.Objects; 5239d5c6172503620ac3761148adac5fd7fa20d02dAdam Powellimport java.util.concurrent.CopyOnWriteArrayList; 539a1de308cea2d160778fd977825f10a07b49d738Adam Powell 549a1de308cea2d160778fd977825f10a07b49d738Adam Powell/** 559a1de308cea2d160778fd977825f10a07b49d738Adam Powell * MediaRouter allows applications to control the routing of media channels 569a1de308cea2d160778fd977825f10a07b49d738Adam Powell * and streams from the current device to external speakers and destination devices. 579a1de308cea2d160778fd977825f10a07b49d738Adam Powell * 58b58b8f832d06b0ffa8886eba5a4916578a3b8743Dianne Hackborn * <p>A MediaRouter is retrieved through {@link Context#getSystemService(String) 59b58b8f832d06b0ffa8886eba5a4916578a3b8743Dianne Hackborn * Context.getSystemService()} of a {@link Context#MEDIA_ROUTER_SERVICE 60b58b8f832d06b0ffa8886eba5a4916578a3b8743Dianne Hackborn * Context.MEDIA_ROUTER_SERVICE}. 61b58b8f832d06b0ffa8886eba5a4916578a3b8743Dianne Hackborn * 62b58b8f832d06b0ffa8886eba5a4916578a3b8743Dianne Hackborn * <p>The media router API is not thread-safe; all interactions with it must be 63b58b8f832d06b0ffa8886eba5a4916578a3b8743Dianne Hackborn * done from the main thread of the process.</p> 649a1de308cea2d160778fd977825f10a07b49d738Adam Powell */ 65d86b8fea43ebb6e5c31691b44d8ceb0d8d3c9072Jeff Sharkey@SystemService(Context.MEDIA_ROUTER_SERVICE) 669a1de308cea2d160778fd977825f10a07b49d738Adam Powellpublic class MediaRouter { 679a1de308cea2d160778fd977825f10a07b49d738Adam Powell private static final String TAG = "MediaRouter"; 6869b07161bebdb2c726e3a826c2268866f1a94517Jeff Brown private static final boolean DEBUG = Log.isLoggable(TAG, Log.DEBUG); 699a1de308cea2d160778fd977825f10a07b49d738Adam Powell 7092130f6407dc51c58b3b941d28a6daf4e04b8d62Jeff Brown static class Static implements DisplayManager.DisplayListener { 71eadd6d2329972129c96dee390a8b786b37313d17Hyundo Moon final String mPackageName; 72b35c445f34e1a18e17aef3e3dfbc1c39b4d1815cAdam Powell final Resources mResources; 73632ca417f0a33e3fa9ccece531afa2db3f0d4a30Dianne Hackborn final IAudioService mAudioService; 74705ab808cf023e0cc38c2ba7cdb9571942cdc04fAdam Powell final DisplayManager mDisplayService; 7569b07161bebdb2c726e3a826c2268866f1a94517Jeff Brown final IMediaRouterService mMediaRouterService; 76b35c445f34e1a18e17aef3e3dfbc1c39b4d1815cAdam Powell final Handler mHandler; 7739d5c6172503620ac3761148adac5fd7fa20d02dAdam Powell final CopyOnWriteArrayList<CallbackInfo> mCallbacks = 7839d5c6172503620ac3761148adac5fd7fa20d02dAdam Powell new CopyOnWriteArrayList<CallbackInfo>(); 79b58b8f832d06b0ffa8886eba5a4916578a3b8743Dianne Hackborn 80b35c445f34e1a18e17aef3e3dfbc1c39b4d1815cAdam Powell final ArrayList<RouteInfo> mRoutes = new ArrayList<RouteInfo>(); 81b35c445f34e1a18e17aef3e3dfbc1c39b4d1815cAdam Powell final ArrayList<RouteCategory> mCategories = new ArrayList<RouteCategory>(); 82b58b8f832d06b0ffa8886eba5a4916578a3b8743Dianne Hackborn 83b35c445f34e1a18e17aef3e3dfbc1c39b4d1815cAdam Powell final RouteCategory mSystemCategory; 84632ca417f0a33e3fa9ccece531afa2db3f0d4a30Dianne Hackborn 85705ab808cf023e0cc38c2ba7cdb9571942cdc04fAdam Powell final AudioRoutesInfo mCurAudioRoutesInfo = new AudioRoutesInfo(); 86b58b8f832d06b0ffa8886eba5a4916578a3b8743Dianne Hackborn 87705ab808cf023e0cc38c2ba7cdb9571942cdc04fAdam Powell RouteInfo mDefaultAudioVideo; 88b35c445f34e1a18e17aef3e3dfbc1c39b4d1815cAdam Powell RouteInfo mBluetoothA2dpRoute; 89b58b8f832d06b0ffa8886eba5a4916578a3b8743Dianne Hackborn 90b35c445f34e1a18e17aef3e3dfbc1c39b4d1815cAdam Powell RouteInfo mSelectedRoute; 919a1de308cea2d160778fd977825f10a07b49d738Adam Powell 92af574183c274f51d04487a9c8355e9f34a1150f2Jeff Brown final boolean mCanConfigureWifiDisplays; 9366f3b39ec452a8a03e3254e77e19ccb764ee931fJeff Brown boolean mActivelyScanningWifiDisplays; 94ce468a35b388ca46578934706b38dbae94941643Jeff Brown String mPreviousActiveWifiDisplayAddress; 95705ab808cf023e0cc38c2ba7cdb9571942cdc04fAdam Powell 9669b07161bebdb2c726e3a826c2268866f1a94517Jeff Brown int mDiscoveryRequestRouteTypes; 9769b07161bebdb2c726e3a826c2268866f1a94517Jeff Brown boolean mDiscoverRequestActiveScan; 9869b07161bebdb2c726e3a826c2268866f1a94517Jeff Brown 9969b07161bebdb2c726e3a826c2268866f1a94517Jeff Brown int mCurrentUserId = -1; 10069b07161bebdb2c726e3a826c2268866f1a94517Jeff Brown IMediaRouterClient mClient; 10169b07161bebdb2c726e3a826c2268866f1a94517Jeff Brown MediaRouterClientState mClientState; 10269b07161bebdb2c726e3a826c2268866f1a94517Jeff Brown 103705ab808cf023e0cc38c2ba7cdb9571942cdc04fAdam Powell final IAudioRoutesObserver.Stub mAudioRoutesObserver = new IAudioRoutesObserver.Stub() { 10466f3b39ec452a8a03e3254e77e19ccb764ee931fJeff Brown @Override 105632ca417f0a33e3fa9ccece531afa2db3f0d4a30Dianne Hackborn public void dispatchAudioRoutesChanged(final AudioRoutesInfo newRoutes) { 106632ca417f0a33e3fa9ccece531afa2db3f0d4a30Dianne Hackborn mHandler.post(new Runnable() { 107632ca417f0a33e3fa9ccece531afa2db3f0d4a30Dianne Hackborn @Override public void run() { 108705ab808cf023e0cc38c2ba7cdb9571942cdc04fAdam Powell updateAudioRoutes(newRoutes); 109632ca417f0a33e3fa9ccece531afa2db3f0d4a30Dianne Hackborn } 110632ca417f0a33e3fa9ccece531afa2db3f0d4a30Dianne Hackborn }); 111632ca417f0a33e3fa9ccece531afa2db3f0d4a30Dianne Hackborn } 112632ca417f0a33e3fa9ccece531afa2db3f0d4a30Dianne Hackborn }; 113632ca417f0a33e3fa9ccece531afa2db3f0d4a30Dianne Hackborn 114b58b8f832d06b0ffa8886eba5a4916578a3b8743Dianne Hackborn Static(Context appContext) { 115eadd6d2329972129c96dee390a8b786b37313d17Hyundo Moon mPackageName = appContext.getPackageName(); 116eadd6d2329972129c96dee390a8b786b37313d17Hyundo Moon mResources = appContext.getResources(); 117b58b8f832d06b0ffa8886eba5a4916578a3b8743Dianne Hackborn mHandler = new Handler(appContext.getMainLooper()); 1189a1de308cea2d160778fd977825f10a07b49d738Adam Powell 119632ca417f0a33e3fa9ccece531afa2db3f0d4a30Dianne Hackborn IBinder b = ServiceManager.getService(Context.AUDIO_SERVICE); 120632ca417f0a33e3fa9ccece531afa2db3f0d4a30Dianne Hackborn mAudioService = IAudioService.Stub.asInterface(b); 1219a1de308cea2d160778fd977825f10a07b49d738Adam Powell 122705ab808cf023e0cc38c2ba7cdb9571942cdc04fAdam Powell mDisplayService = (DisplayManager) appContext.getSystemService(Context.DISPLAY_SERVICE); 123705ab808cf023e0cc38c2ba7cdb9571942cdc04fAdam Powell 12469b07161bebdb2c726e3a826c2268866f1a94517Jeff Brown mMediaRouterService = IMediaRouterService.Stub.asInterface( 12569b07161bebdb2c726e3a826c2268866f1a94517Jeff Brown ServiceManager.getService(Context.MEDIA_ROUTER_SERVICE)); 12669b07161bebdb2c726e3a826c2268866f1a94517Jeff Brown 127dd0a19266d5c837069da1ea188744d54c8d723a8Adam Powell mSystemCategory = new RouteCategory( 128dd0a19266d5c837069da1ea188744d54c8d723a8Adam Powell com.android.internal.R.string.default_audio_route_category_name, 129705ab808cf023e0cc38c2ba7cdb9571942cdc04fAdam Powell ROUTE_TYPE_LIVE_AUDIO | ROUTE_TYPE_LIVE_VIDEO, false); 130705ab808cf023e0cc38c2ba7cdb9571942cdc04fAdam Powell mSystemCategory.mIsSystem = true; 131af574183c274f51d04487a9c8355e9f34a1150f2Jeff Brown 132af574183c274f51d04487a9c8355e9f34a1150f2Jeff Brown // Only the system can configure wifi displays. The display manager 133af574183c274f51d04487a9c8355e9f34a1150f2Jeff Brown // enforces this with a permission check. Set a flag here so that we 134af574183c274f51d04487a9c8355e9f34a1150f2Jeff Brown // know whether this process is actually allowed to scan and connect. 135af574183c274f51d04487a9c8355e9f34a1150f2Jeff Brown mCanConfigureWifiDisplays = appContext.checkPermission( 136af574183c274f51d04487a9c8355e9f34a1150f2Jeff Brown Manifest.permission.CONFIGURE_WIFI_DISPLAY, 137af574183c274f51d04487a9c8355e9f34a1150f2Jeff Brown Process.myPid(), Process.myUid()) == PackageManager.PERMISSION_GRANTED; 138b35c445f34e1a18e17aef3e3dfbc1c39b4d1815cAdam Powell } 139b58b8f832d06b0ffa8886eba5a4916578a3b8743Dianne Hackborn 140b35c445f34e1a18e17aef3e3dfbc1c39b4d1815cAdam Powell // Called after sStatic is initialized 1418e37a85bf3dc39519942698dc90a3951306b934bAdam Powell void startMonitoringRoutes(Context appContext) { 142705ab808cf023e0cc38c2ba7cdb9571942cdc04fAdam Powell mDefaultAudioVideo = new RouteInfo(mSystemCategory); 143705ab808cf023e0cc38c2ba7cdb9571942cdc04fAdam Powell mDefaultAudioVideo.mNameResId = com.android.internal.R.string.default_audio_route_name; 144705ab808cf023e0cc38c2ba7cdb9571942cdc04fAdam Powell mDefaultAudioVideo.mSupportedTypes = ROUTE_TYPE_LIVE_AUDIO | ROUTE_TYPE_LIVE_VIDEO; 1455830b0b33618940d65197cec99d697b21908fec8Chong Zhang mDefaultAudioVideo.updatePresentationDisplay(); 146bd6118ffb453c7597c8679bc6bfefb4887d7662fSungsoo if (((AudioManager) appContext.getSystemService(Context.AUDIO_SERVICE)) 147bd6118ffb453c7597c8679bc6bfefb4887d7662fSungsoo .isVolumeFixed()) { 148bd6118ffb453c7597c8679bc6bfefb4887d7662fSungsoo mDefaultAudioVideo.mVolumeHandling = RouteInfo.PLAYBACK_VOLUME_FIXED; 149bd6118ffb453c7597c8679bc6bfefb4887d7662fSungsoo } 150bd6118ffb453c7597c8679bc6bfefb4887d7662fSungsoo 1512ee6a2a83262d05a566bd713d238e89edfd33a29Adam Powell addRouteStatic(mDefaultAudioVideo); 152632ca417f0a33e3fa9ccece531afa2db3f0d4a30Dianne Hackborn 1532ee6a2a83262d05a566bd713d238e89edfd33a29Adam Powell // This will select the active wifi display route if there is one. 1542ee6a2a83262d05a566bd713d238e89edfd33a29Adam Powell updateWifiDisplayStatus(mDisplayService.getWifiDisplayStatus()); 1552ee6a2a83262d05a566bd713d238e89edfd33a29Adam Powell 1562ee6a2a83262d05a566bd713d238e89edfd33a29Adam Powell appContext.registerReceiver(new WifiDisplayStatusChangedReceiver(), 1572ee6a2a83262d05a566bd713d238e89edfd33a29Adam Powell new IntentFilter(DisplayManager.ACTION_WIFI_DISPLAY_STATUS_CHANGED)); 1588e37a85bf3dc39519942698dc90a3951306b934bAdam Powell appContext.registerReceiver(new VolumeChangeReceiver(), 1598e37a85bf3dc39519942698dc90a3951306b934bAdam Powell new IntentFilter(AudioManager.VOLUME_CHANGED_ACTION)); 1608e37a85bf3dc39519942698dc90a3951306b934bAdam Powell 16192130f6407dc51c58b3b941d28a6daf4e04b8d62Jeff Brown mDisplayService.registerDisplayListener(this, mHandler); 16292130f6407dc51c58b3b941d28a6daf4e04b8d62Jeff Brown 163705ab808cf023e0cc38c2ba7cdb9571942cdc04fAdam Powell AudioRoutesInfo newAudioRoutes = null; 164632ca417f0a33e3fa9ccece531afa2db3f0d4a30Dianne Hackborn try { 165705ab808cf023e0cc38c2ba7cdb9571942cdc04fAdam Powell newAudioRoutes = mAudioService.startWatchingRoutes(mAudioRoutesObserver); 166632ca417f0a33e3fa9ccece531afa2db3f0d4a30Dianne Hackborn } catch (RemoteException e) { 167632ca417f0a33e3fa9ccece531afa2db3f0d4a30Dianne Hackborn } 168705ab808cf023e0cc38c2ba7cdb9571942cdc04fAdam Powell if (newAudioRoutes != null) { 1692ee6a2a83262d05a566bd713d238e89edfd33a29Adam Powell // This will select the active BT route if there is one and the current 1702ee6a2a83262d05a566bd713d238e89edfd33a29Adam Powell // selected route is the default system route, or if there is no selected 1712ee6a2a83262d05a566bd713d238e89edfd33a29Adam Powell // route yet. 172705ab808cf023e0cc38c2ba7cdb9571942cdc04fAdam Powell updateAudioRoutes(newAudioRoutes); 173632ca417f0a33e3fa9ccece531afa2db3f0d4a30Dianne Hackborn } 174705ab808cf023e0cc38c2ba7cdb9571942cdc04fAdam Powell 17569b07161bebdb2c726e3a826c2268866f1a94517Jeff Brown // Bind to the media router service. 17669b07161bebdb2c726e3a826c2268866f1a94517Jeff Brown rebindAsUser(UserHandle.myUserId()); 17769b07161bebdb2c726e3a826c2268866f1a94517Jeff Brown 1782ee6a2a83262d05a566bd713d238e89edfd33a29Adam Powell // Select the default route if the above didn't sync us up 1792ee6a2a83262d05a566bd713d238e89edfd33a29Adam Powell // appropriately with relevant system state. 1802ee6a2a83262d05a566bd713d238e89edfd33a29Adam Powell if (mSelectedRoute == null) { 18169b07161bebdb2c726e3a826c2268866f1a94517Jeff Brown selectDefaultRouteStatic(); 1822ee6a2a83262d05a566bd713d238e89edfd33a29Adam Powell } 183632ca417f0a33e3fa9ccece531afa2db3f0d4a30Dianne Hackborn } 184632ca417f0a33e3fa9ccece531afa2db3f0d4a30Dianne Hackborn 185705ab808cf023e0cc38c2ba7cdb9571942cdc04fAdam Powell void updateAudioRoutes(AudioRoutesInfo newRoutes) { 18676512a3d80dff2d32b68f3b3b6a14d4ed81ae90aSungsoo Lim boolean audioRoutesChanged = false; 187546a170bfa9aa8f877b76cbf7666c59161407029Sungsoo Lim boolean forceUseDefaultRoute = false; 188546a170bfa9aa8f877b76cbf7666c59161407029Sungsoo Lim 1896156017c2217d0fbbbb03434986250ec6bbd69d8John Spurlock if (newRoutes.mainType != mCurAudioRoutesInfo.mainType) { 1906156017c2217d0fbbbb03434986250ec6bbd69d8John Spurlock mCurAudioRoutesInfo.mainType = newRoutes.mainType; 191632ca417f0a33e3fa9ccece531afa2db3f0d4a30Dianne Hackborn int name; 192546a170bfa9aa8f877b76cbf7666c59161407029Sungsoo Lim if ((newRoutes.mainType & AudioRoutesInfo.MAIN_HEADPHONES) != 0 193546a170bfa9aa8f877b76cbf7666c59161407029Sungsoo Lim || (newRoutes.mainType & AudioRoutesInfo.MAIN_HEADSET) != 0) { 194632ca417f0a33e3fa9ccece531afa2db3f0d4a30Dianne Hackborn name = com.android.internal.R.string.default_audio_route_name_headphones; 195546a170bfa9aa8f877b76cbf7666c59161407029Sungsoo Lim } else if ((newRoutes.mainType & AudioRoutesInfo.MAIN_DOCK_SPEAKERS) != 0) { 196632ca417f0a33e3fa9ccece531afa2db3f0d4a30Dianne Hackborn name = com.android.internal.R.string.default_audio_route_name_dock_speakers; 1976156017c2217d0fbbbb03434986250ec6bbd69d8John Spurlock } else if ((newRoutes.mainType&AudioRoutesInfo.MAIN_HDMI) != 0) { 19860aa8b99a0f16b76feaf4a994bc1b235ef709830Sungsoo Lim name = com.android.internal.R.string.default_audio_route_name_hdmi; 19960aa8b99a0f16b76feaf4a994bc1b235ef709830Sungsoo Lim } else if ((newRoutes.mainType&AudioRoutesInfo.MAIN_USB) != 0) { 20060aa8b99a0f16b76feaf4a994bc1b235ef709830Sungsoo Lim name = com.android.internal.R.string.default_audio_route_name_usb; 201632ca417f0a33e3fa9ccece531afa2db3f0d4a30Dianne Hackborn } else { 202632ca417f0a33e3fa9ccece531afa2db3f0d4a30Dianne Hackborn name = com.android.internal.R.string.default_audio_route_name; 203632ca417f0a33e3fa9ccece531afa2db3f0d4a30Dianne Hackborn } 204b3658569e9a87afe1b3168837cc282235fd4d1f8Sungsoo mDefaultAudioVideo.mNameResId = name; 205b3658569e9a87afe1b3168837cc282235fd4d1f8Sungsoo dispatchRouteChanged(mDefaultAudioVideo); 206546a170bfa9aa8f877b76cbf7666c59161407029Sungsoo Lim 207546a170bfa9aa8f877b76cbf7666c59161407029Sungsoo Lim if ((newRoutes.mainType & (AudioRoutesInfo.MAIN_HEADSET 208546a170bfa9aa8f877b76cbf7666c59161407029Sungsoo Lim | AudioRoutesInfo.MAIN_HEADPHONES | AudioRoutesInfo.MAIN_USB)) != 0) { 209546a170bfa9aa8f877b76cbf7666c59161407029Sungsoo Lim forceUseDefaultRoute = true; 210546a170bfa9aa8f877b76cbf7666c59161407029Sungsoo Lim } 21176512a3d80dff2d32b68f3b3b6a14d4ed81ae90aSungsoo Lim audioRoutesChanged = true; 212632ca417f0a33e3fa9ccece531afa2db3f0d4a30Dianne Hackborn } 213bcf21e913af7252fb1994e07b6cf179321ecd049Adam Powell 2146156017c2217d0fbbbb03434986250ec6bbd69d8John Spurlock if (!TextUtils.equals(newRoutes.bluetoothName, mCurAudioRoutesInfo.bluetoothName)) { 215546a170bfa9aa8f877b76cbf7666c59161407029Sungsoo Lim forceUseDefaultRoute = false; 2166156017c2217d0fbbbb03434986250ec6bbd69d8John Spurlock mCurAudioRoutesInfo.bluetoothName = newRoutes.bluetoothName; 2176156017c2217d0fbbbb03434986250ec6bbd69d8John Spurlock if (mCurAudioRoutesInfo.bluetoothName != null) { 218b3658569e9a87afe1b3168837cc282235fd4d1f8Sungsoo if (mBluetoothA2dpRoute == null) { 219b3658569e9a87afe1b3168837cc282235fd4d1f8Sungsoo // BT connected 220b3658569e9a87afe1b3168837cc282235fd4d1f8Sungsoo final RouteInfo info = new RouteInfo(mSystemCategory); 2216156017c2217d0fbbbb03434986250ec6bbd69d8John Spurlock info.mName = mCurAudioRoutesInfo.bluetoothName; 222b3658569e9a87afe1b3168837cc282235fd4d1f8Sungsoo info.mDescription = mResources.getText( 22356d4b744b374af11f540567d2b43d66d8d7c301eJeff Brown com.android.internal.R.string.bluetooth_a2dp_audio_route_name); 224632ca417f0a33e3fa9ccece531afa2db3f0d4a30Dianne Hackborn info.mSupportedTypes = ROUTE_TYPE_LIVE_AUDIO; 2259dd66417ea984e4ff809ecdba78e3f55a8a17467Insun Kang info.mDeviceType = RouteInfo.DEVICE_TYPE_BLUETOOTH; 226b3658569e9a87afe1b3168837cc282235fd4d1f8Sungsoo mBluetoothA2dpRoute = info; 227b3658569e9a87afe1b3168837cc282235fd4d1f8Sungsoo addRouteStatic(mBluetoothA2dpRoute); 228632ca417f0a33e3fa9ccece531afa2db3f0d4a30Dianne Hackborn } else { 229b3658569e9a87afe1b3168837cc282235fd4d1f8Sungsoo mBluetoothA2dpRoute.mName = mCurAudioRoutesInfo.bluetoothName; 230b3658569e9a87afe1b3168837cc282235fd4d1f8Sungsoo dispatchRouteChanged(mBluetoothA2dpRoute); 231632ca417f0a33e3fa9ccece531afa2db3f0d4a30Dianne Hackborn } 232b3658569e9a87afe1b3168837cc282235fd4d1f8Sungsoo } else if (mBluetoothA2dpRoute != null) { 233b3658569e9a87afe1b3168837cc282235fd4d1f8Sungsoo // BT disconnected 234b3658569e9a87afe1b3168837cc282235fd4d1f8Sungsoo removeRouteStatic(mBluetoothA2dpRoute); 235b3658569e9a87afe1b3168837cc282235fd4d1f8Sungsoo mBluetoothA2dpRoute = null; 236bcf21e913af7252fb1994e07b6cf179321ecd049Adam Powell } 23776512a3d80dff2d32b68f3b3b6a14d4ed81ae90aSungsoo Lim audioRoutesChanged = true; 238bcf21e913af7252fb1994e07b6cf179321ecd049Adam Powell } 23976512a3d80dff2d32b68f3b3b6a14d4ed81ae90aSungsoo Lim 24076512a3d80dff2d32b68f3b3b6a14d4ed81ae90aSungsoo Lim if (audioRoutesChanged) { 241742e379863ff08a880cb6d9e5f274da7589f9a53Jaewan Kim Log.v(TAG, "Audio routes updated: " + newRoutes + ", a2dp=" + isBluetoothA2dpOn()); 242f8e38e2dbbf3410179c9e18e05b7d4022720cfb6Sungsoo Lim if (mSelectedRoute == null || mSelectedRoute == mDefaultAudioVideo 243f8e38e2dbbf3410179c9e18e05b7d4022720cfb6Sungsoo Lim || mSelectedRoute == mBluetoothA2dpRoute) { 244546a170bfa9aa8f877b76cbf7666c59161407029Sungsoo Lim if (forceUseDefaultRoute || mBluetoothA2dpRoute == null) { 245546a170bfa9aa8f877b76cbf7666c59161407029Sungsoo Lim selectRouteStatic(ROUTE_TYPE_LIVE_AUDIO, mDefaultAudioVideo, false); 246546a170bfa9aa8f877b76cbf7666c59161407029Sungsoo Lim } else { 247546a170bfa9aa8f877b76cbf7666c59161407029Sungsoo Lim selectRouteStatic(ROUTE_TYPE_LIVE_AUDIO, mBluetoothA2dpRoute, false); 248546a170bfa9aa8f877b76cbf7666c59161407029Sungsoo Lim } 249f8e38e2dbbf3410179c9e18e05b7d4022720cfb6Sungsoo Lim } 250742e379863ff08a880cb6d9e5f274da7589f9a53Jaewan Kim } 251b58b8f832d06b0ffa8886eba5a4916578a3b8743Dianne Hackborn } 25292130f6407dc51c58b3b941d28a6daf4e04b8d62Jeff Brown 253fa6f70e225a7370c1ecb21ee7076c9086ecb9e9aSungsoo boolean isBluetoothA2dpOn() { 254fa6f70e225a7370c1ecb21ee7076c9086ecb9e9aSungsoo try { 255546a170bfa9aa8f877b76cbf7666c59161407029Sungsoo Lim return mBluetoothA2dpRoute != null && mAudioService.isBluetoothA2dpOn(); 256fa6f70e225a7370c1ecb21ee7076c9086ecb9e9aSungsoo } catch (RemoteException e) { 257fa6f70e225a7370c1ecb21ee7076c9086ecb9e9aSungsoo Log.e(TAG, "Error querying Bluetooth A2DP state", e); 258fa6f70e225a7370c1ecb21ee7076c9086ecb9e9aSungsoo return false; 259fa6f70e225a7370c1ecb21ee7076c9086ecb9e9aSungsoo } 260fa6f70e225a7370c1ecb21ee7076c9086ecb9e9aSungsoo } 261fa6f70e225a7370c1ecb21ee7076c9086ecb9e9aSungsoo 26269b07161bebdb2c726e3a826c2268866f1a94517Jeff Brown void updateDiscoveryRequest() { 26369b07161bebdb2c726e3a826c2268866f1a94517Jeff Brown // What are we looking for today? 26469b07161bebdb2c726e3a826c2268866f1a94517Jeff Brown int routeTypes = 0; 26569b07161bebdb2c726e3a826c2268866f1a94517Jeff Brown int passiveRouteTypes = 0; 26669b07161bebdb2c726e3a826c2268866f1a94517Jeff Brown boolean activeScan = false; 26769b07161bebdb2c726e3a826c2268866f1a94517Jeff Brown boolean activeScanWifiDisplay = false; 26869b07161bebdb2c726e3a826c2268866f1a94517Jeff Brown final int count = mCallbacks.size(); 26969b07161bebdb2c726e3a826c2268866f1a94517Jeff Brown for (int i = 0; i < count; i++) { 27069b07161bebdb2c726e3a826c2268866f1a94517Jeff Brown CallbackInfo cbi = mCallbacks.get(i); 27169b07161bebdb2c726e3a826c2268866f1a94517Jeff Brown if ((cbi.flags & (CALLBACK_FLAG_PERFORM_ACTIVE_SCAN 27269b07161bebdb2c726e3a826c2268866f1a94517Jeff Brown | CALLBACK_FLAG_REQUEST_DISCOVERY)) != 0) { 27369b07161bebdb2c726e3a826c2268866f1a94517Jeff Brown // Discovery explicitly requested. 27469b07161bebdb2c726e3a826c2268866f1a94517Jeff Brown routeTypes |= cbi.type; 27569b07161bebdb2c726e3a826c2268866f1a94517Jeff Brown } else if ((cbi.flags & CALLBACK_FLAG_PASSIVE_DISCOVERY) != 0) { 27669b07161bebdb2c726e3a826c2268866f1a94517Jeff Brown // Discovery only passively requested. 27769b07161bebdb2c726e3a826c2268866f1a94517Jeff Brown passiveRouteTypes |= cbi.type; 27869b07161bebdb2c726e3a826c2268866f1a94517Jeff Brown } else { 27969b07161bebdb2c726e3a826c2268866f1a94517Jeff Brown // Legacy case since applications don't specify the discovery flag. 28069b07161bebdb2c726e3a826c2268866f1a94517Jeff Brown // Unfortunately we just have to assume they always need discovery 28169b07161bebdb2c726e3a826c2268866f1a94517Jeff Brown // whenever they have a callback registered. 28269b07161bebdb2c726e3a826c2268866f1a94517Jeff Brown routeTypes |= cbi.type; 28369b07161bebdb2c726e3a826c2268866f1a94517Jeff Brown } 28469b07161bebdb2c726e3a826c2268866f1a94517Jeff Brown if ((cbi.flags & CALLBACK_FLAG_PERFORM_ACTIVE_SCAN) != 0) { 28569b07161bebdb2c726e3a826c2268866f1a94517Jeff Brown activeScan = true; 286af574183c274f51d04487a9c8355e9f34a1150f2Jeff Brown if ((cbi.type & ROUTE_TYPE_REMOTE_DISPLAY) != 0) { 28769b07161bebdb2c726e3a826c2268866f1a94517Jeff Brown activeScanWifiDisplay = true; 28869b07161bebdb2c726e3a826c2268866f1a94517Jeff Brown } 28969b07161bebdb2c726e3a826c2268866f1a94517Jeff Brown } 29069b07161bebdb2c726e3a826c2268866f1a94517Jeff Brown } 29169b07161bebdb2c726e3a826c2268866f1a94517Jeff Brown if (routeTypes != 0 || activeScan) { 29269b07161bebdb2c726e3a826c2268866f1a94517Jeff Brown // If someone else requests discovery then enable the passive listeners. 29369b07161bebdb2c726e3a826c2268866f1a94517Jeff Brown // This is used by the MediaRouteButton and MediaRouteActionProvider since 29469b07161bebdb2c726e3a826c2268866f1a94517Jeff Brown // they don't receive lifecycle callbacks from the Activity. 29569b07161bebdb2c726e3a826c2268866f1a94517Jeff Brown routeTypes |= passiveRouteTypes; 29669b07161bebdb2c726e3a826c2268866f1a94517Jeff Brown } 29769b07161bebdb2c726e3a826c2268866f1a94517Jeff Brown 29869b07161bebdb2c726e3a826c2268866f1a94517Jeff Brown // Update wifi display scanning. 299ce468a35b388ca46578934706b38dbae94941643Jeff Brown // TODO: All of this should be managed by the media router service. 300ce468a35b388ca46578934706b38dbae94941643Jeff Brown if (mCanConfigureWifiDisplays) { 301ce468a35b388ca46578934706b38dbae94941643Jeff Brown if (mSelectedRoute != null 302ce468a35b388ca46578934706b38dbae94941643Jeff Brown && mSelectedRoute.matchesTypes(ROUTE_TYPE_REMOTE_DISPLAY)) { 303ce468a35b388ca46578934706b38dbae94941643Jeff Brown // Don't scan while already connected to a remote display since 304ce468a35b388ca46578934706b38dbae94941643Jeff Brown // it may interfere with the ongoing transmission. 305ce468a35b388ca46578934706b38dbae94941643Jeff Brown activeScanWifiDisplay = false; 30666f3b39ec452a8a03e3254e77e19ccb764ee931fJeff Brown } 307ce468a35b388ca46578934706b38dbae94941643Jeff Brown if (activeScanWifiDisplay) { 308ce468a35b388ca46578934706b38dbae94941643Jeff Brown if (!mActivelyScanningWifiDisplays) { 309ce468a35b388ca46578934706b38dbae94941643Jeff Brown mActivelyScanningWifiDisplays = true; 310ce468a35b388ca46578934706b38dbae94941643Jeff Brown mDisplayService.startWifiDisplayScan(); 311ce468a35b388ca46578934706b38dbae94941643Jeff Brown } 312ce468a35b388ca46578934706b38dbae94941643Jeff Brown } else { 313ce468a35b388ca46578934706b38dbae94941643Jeff Brown if (mActivelyScanningWifiDisplays) { 314ce468a35b388ca46578934706b38dbae94941643Jeff Brown mActivelyScanningWifiDisplays = false; 315ce468a35b388ca46578934706b38dbae94941643Jeff Brown mDisplayService.stopWifiDisplayScan(); 316ce468a35b388ca46578934706b38dbae94941643Jeff Brown } 31766f3b39ec452a8a03e3254e77e19ccb764ee931fJeff Brown } 31866f3b39ec452a8a03e3254e77e19ccb764ee931fJeff Brown } 31966f3b39ec452a8a03e3254e77e19ccb764ee931fJeff Brown 32069b07161bebdb2c726e3a826c2268866f1a94517Jeff Brown // Tell the media router service all about it. 32169b07161bebdb2c726e3a826c2268866f1a94517Jeff Brown if (routeTypes != mDiscoveryRequestRouteTypes 32269b07161bebdb2c726e3a826c2268866f1a94517Jeff Brown || activeScan != mDiscoverRequestActiveScan) { 32369b07161bebdb2c726e3a826c2268866f1a94517Jeff Brown mDiscoveryRequestRouteTypes = routeTypes; 32469b07161bebdb2c726e3a826c2268866f1a94517Jeff Brown mDiscoverRequestActiveScan = activeScan; 32569b07161bebdb2c726e3a826c2268866f1a94517Jeff Brown publishClientDiscoveryRequest(); 32666f3b39ec452a8a03e3254e77e19ccb764ee931fJeff Brown } 32766f3b39ec452a8a03e3254e77e19ccb764ee931fJeff Brown } 32866f3b39ec452a8a03e3254e77e19ccb764ee931fJeff Brown 32992130f6407dc51c58b3b941d28a6daf4e04b8d62Jeff Brown @Override 33092130f6407dc51c58b3b941d28a6daf4e04b8d62Jeff Brown public void onDisplayAdded(int displayId) { 33192130f6407dc51c58b3b941d28a6daf4e04b8d62Jeff Brown updatePresentationDisplays(displayId); 33292130f6407dc51c58b3b941d28a6daf4e04b8d62Jeff Brown } 33392130f6407dc51c58b3b941d28a6daf4e04b8d62Jeff Brown 33492130f6407dc51c58b3b941d28a6daf4e04b8d62Jeff Brown @Override 33592130f6407dc51c58b3b941d28a6daf4e04b8d62Jeff Brown public void onDisplayChanged(int displayId) { 33692130f6407dc51c58b3b941d28a6daf4e04b8d62Jeff Brown updatePresentationDisplays(displayId); 33792130f6407dc51c58b3b941d28a6daf4e04b8d62Jeff Brown } 33892130f6407dc51c58b3b941d28a6daf4e04b8d62Jeff Brown 33992130f6407dc51c58b3b941d28a6daf4e04b8d62Jeff Brown @Override 34092130f6407dc51c58b3b941d28a6daf4e04b8d62Jeff Brown public void onDisplayRemoved(int displayId) { 34192130f6407dc51c58b3b941d28a6daf4e04b8d62Jeff Brown updatePresentationDisplays(displayId); 34292130f6407dc51c58b3b941d28a6daf4e04b8d62Jeff Brown } 34392130f6407dc51c58b3b941d28a6daf4e04b8d62Jeff Brown 34492130f6407dc51c58b3b941d28a6daf4e04b8d62Jeff Brown public Display[] getAllPresentationDisplays() { 34592130f6407dc51c58b3b941d28a6daf4e04b8d62Jeff Brown return mDisplayService.getDisplays(DisplayManager.DISPLAY_CATEGORY_PRESENTATION); 34692130f6407dc51c58b3b941d28a6daf4e04b8d62Jeff Brown } 34792130f6407dc51c58b3b941d28a6daf4e04b8d62Jeff Brown 34892130f6407dc51c58b3b941d28a6daf4e04b8d62Jeff Brown private void updatePresentationDisplays(int changedDisplayId) { 34992130f6407dc51c58b3b941d28a6daf4e04b8d62Jeff Brown final int count = mRoutes.size(); 35092130f6407dc51c58b3b941d28a6daf4e04b8d62Jeff Brown for (int i = 0; i < count; i++) { 3515830b0b33618940d65197cec99d697b21908fec8Chong Zhang final RouteInfo route = mRoutes.get(i); 3525830b0b33618940d65197cec99d697b21908fec8Chong Zhang if (route.updatePresentationDisplay() || (route.mPresentationDisplay != null 3535830b0b33618940d65197cec99d697b21908fec8Chong Zhang && route.mPresentationDisplay.getDisplayId() == changedDisplayId)) { 3545830b0b33618940d65197cec99d697b21908fec8Chong Zhang dispatchRoutePresentationDisplayChanged(route); 35592130f6407dc51c58b3b941d28a6daf4e04b8d62Jeff Brown } 35692130f6407dc51c58b3b941d28a6daf4e04b8d62Jeff Brown } 35792130f6407dc51c58b3b941d28a6daf4e04b8d62Jeff Brown } 35869b07161bebdb2c726e3a826c2268866f1a94517Jeff Brown 35969b07161bebdb2c726e3a826c2268866f1a94517Jeff Brown void setSelectedRoute(RouteInfo info, boolean explicit) { 36069b07161bebdb2c726e3a826c2268866f1a94517Jeff Brown // Must be non-reentrant. 36169b07161bebdb2c726e3a826c2268866f1a94517Jeff Brown mSelectedRoute = info; 36269b07161bebdb2c726e3a826c2268866f1a94517Jeff Brown publishClientSelectedRoute(explicit); 36369b07161bebdb2c726e3a826c2268866f1a94517Jeff Brown } 36469b07161bebdb2c726e3a826c2268866f1a94517Jeff Brown 36569b07161bebdb2c726e3a826c2268866f1a94517Jeff Brown void rebindAsUser(int userId) { 36669b07161bebdb2c726e3a826c2268866f1a94517Jeff Brown if (mCurrentUserId != userId || userId < 0 || mClient == null) { 36769b07161bebdb2c726e3a826c2268866f1a94517Jeff Brown if (mClient != null) { 36869b07161bebdb2c726e3a826c2268866f1a94517Jeff Brown try { 36969b07161bebdb2c726e3a826c2268866f1a94517Jeff Brown mMediaRouterService.unregisterClient(mClient); 37069b07161bebdb2c726e3a826c2268866f1a94517Jeff Brown } catch (RemoteException ex) { 37169b07161bebdb2c726e3a826c2268866f1a94517Jeff Brown Log.e(TAG, "Unable to unregister media router client.", ex); 37269b07161bebdb2c726e3a826c2268866f1a94517Jeff Brown } 37369b07161bebdb2c726e3a826c2268866f1a94517Jeff Brown mClient = null; 37469b07161bebdb2c726e3a826c2268866f1a94517Jeff Brown } 37569b07161bebdb2c726e3a826c2268866f1a94517Jeff Brown 37669b07161bebdb2c726e3a826c2268866f1a94517Jeff Brown mCurrentUserId = userId; 37769b07161bebdb2c726e3a826c2268866f1a94517Jeff Brown 37869b07161bebdb2c726e3a826c2268866f1a94517Jeff Brown try { 37969b07161bebdb2c726e3a826c2268866f1a94517Jeff Brown Client client = new Client(); 380eadd6d2329972129c96dee390a8b786b37313d17Hyundo Moon mMediaRouterService.registerClientAsUser(client, mPackageName, userId); 38169b07161bebdb2c726e3a826c2268866f1a94517Jeff Brown mClient = client; 38269b07161bebdb2c726e3a826c2268866f1a94517Jeff Brown } catch (RemoteException ex) { 38369b07161bebdb2c726e3a826c2268866f1a94517Jeff Brown Log.e(TAG, "Unable to register media router client.", ex); 38469b07161bebdb2c726e3a826c2268866f1a94517Jeff Brown } 38569b07161bebdb2c726e3a826c2268866f1a94517Jeff Brown 38669b07161bebdb2c726e3a826c2268866f1a94517Jeff Brown publishClientDiscoveryRequest(); 38769b07161bebdb2c726e3a826c2268866f1a94517Jeff Brown publishClientSelectedRoute(false); 38869b07161bebdb2c726e3a826c2268866f1a94517Jeff Brown updateClientState(); 38969b07161bebdb2c726e3a826c2268866f1a94517Jeff Brown } 39069b07161bebdb2c726e3a826c2268866f1a94517Jeff Brown } 39169b07161bebdb2c726e3a826c2268866f1a94517Jeff Brown 39269b07161bebdb2c726e3a826c2268866f1a94517Jeff Brown void publishClientDiscoveryRequest() { 39369b07161bebdb2c726e3a826c2268866f1a94517Jeff Brown if (mClient != null) { 39469b07161bebdb2c726e3a826c2268866f1a94517Jeff Brown try { 39569b07161bebdb2c726e3a826c2268866f1a94517Jeff Brown mMediaRouterService.setDiscoveryRequest(mClient, 39669b07161bebdb2c726e3a826c2268866f1a94517Jeff Brown mDiscoveryRequestRouteTypes, mDiscoverRequestActiveScan); 39769b07161bebdb2c726e3a826c2268866f1a94517Jeff Brown } catch (RemoteException ex) { 39869b07161bebdb2c726e3a826c2268866f1a94517Jeff Brown Log.e(TAG, "Unable to publish media router client discovery request.", ex); 39969b07161bebdb2c726e3a826c2268866f1a94517Jeff Brown } 40069b07161bebdb2c726e3a826c2268866f1a94517Jeff Brown } 40169b07161bebdb2c726e3a826c2268866f1a94517Jeff Brown } 40269b07161bebdb2c726e3a826c2268866f1a94517Jeff Brown 40369b07161bebdb2c726e3a826c2268866f1a94517Jeff Brown void publishClientSelectedRoute(boolean explicit) { 40469b07161bebdb2c726e3a826c2268866f1a94517Jeff Brown if (mClient != null) { 40569b07161bebdb2c726e3a826c2268866f1a94517Jeff Brown try { 40669b07161bebdb2c726e3a826c2268866f1a94517Jeff Brown mMediaRouterService.setSelectedRoute(mClient, 40769b07161bebdb2c726e3a826c2268866f1a94517Jeff Brown mSelectedRoute != null ? mSelectedRoute.mGlobalRouteId : null, 40869b07161bebdb2c726e3a826c2268866f1a94517Jeff Brown explicit); 40969b07161bebdb2c726e3a826c2268866f1a94517Jeff Brown } catch (RemoteException ex) { 41069b07161bebdb2c726e3a826c2268866f1a94517Jeff Brown Log.e(TAG, "Unable to publish media router client selected route.", ex); 41169b07161bebdb2c726e3a826c2268866f1a94517Jeff Brown } 41269b07161bebdb2c726e3a826c2268866f1a94517Jeff Brown } 41369b07161bebdb2c726e3a826c2268866f1a94517Jeff Brown } 41469b07161bebdb2c726e3a826c2268866f1a94517Jeff Brown 41569b07161bebdb2c726e3a826c2268866f1a94517Jeff Brown void updateClientState() { 41669b07161bebdb2c726e3a826c2268866f1a94517Jeff Brown // Update the client state. 41769b07161bebdb2c726e3a826c2268866f1a94517Jeff Brown mClientState = null; 41869b07161bebdb2c726e3a826c2268866f1a94517Jeff Brown if (mClient != null) { 41969b07161bebdb2c726e3a826c2268866f1a94517Jeff Brown try { 42069b07161bebdb2c726e3a826c2268866f1a94517Jeff Brown mClientState = mMediaRouterService.getState(mClient); 42169b07161bebdb2c726e3a826c2268866f1a94517Jeff Brown } catch (RemoteException ex) { 42269b07161bebdb2c726e3a826c2268866f1a94517Jeff Brown Log.e(TAG, "Unable to retrieve media router client state.", ex); 42369b07161bebdb2c726e3a826c2268866f1a94517Jeff Brown } 42469b07161bebdb2c726e3a826c2268866f1a94517Jeff Brown } 42569b07161bebdb2c726e3a826c2268866f1a94517Jeff Brown final ArrayList<MediaRouterClientState.RouteInfo> globalRoutes = 42669b07161bebdb2c726e3a826c2268866f1a94517Jeff Brown mClientState != null ? mClientState.routes : null; 42769b07161bebdb2c726e3a826c2268866f1a94517Jeff Brown 42869b07161bebdb2c726e3a826c2268866f1a94517Jeff Brown // Add or update routes. 42969b07161bebdb2c726e3a826c2268866f1a94517Jeff Brown final int globalRouteCount = globalRoutes != null ? globalRoutes.size() : 0; 43069b07161bebdb2c726e3a826c2268866f1a94517Jeff Brown for (int i = 0; i < globalRouteCount; i++) { 43169b07161bebdb2c726e3a826c2268866f1a94517Jeff Brown final MediaRouterClientState.RouteInfo globalRoute = globalRoutes.get(i); 43269b07161bebdb2c726e3a826c2268866f1a94517Jeff Brown RouteInfo route = findGlobalRoute(globalRoute.id); 43369b07161bebdb2c726e3a826c2268866f1a94517Jeff Brown if (route == null) { 43469b07161bebdb2c726e3a826c2268866f1a94517Jeff Brown route = makeGlobalRoute(globalRoute); 43569b07161bebdb2c726e3a826c2268866f1a94517Jeff Brown addRouteStatic(route); 43669b07161bebdb2c726e3a826c2268866f1a94517Jeff Brown } else { 43769b07161bebdb2c726e3a826c2268866f1a94517Jeff Brown updateGlobalRoute(route, globalRoute); 43869b07161bebdb2c726e3a826c2268866f1a94517Jeff Brown } 43969b07161bebdb2c726e3a826c2268866f1a94517Jeff Brown } 44069b07161bebdb2c726e3a826c2268866f1a94517Jeff Brown 44169b07161bebdb2c726e3a826c2268866f1a94517Jeff Brown // Remove defunct routes. 44269b07161bebdb2c726e3a826c2268866f1a94517Jeff Brown outer: for (int i = mRoutes.size(); i-- > 0; ) { 44369b07161bebdb2c726e3a826c2268866f1a94517Jeff Brown final RouteInfo route = mRoutes.get(i); 44469b07161bebdb2c726e3a826c2268866f1a94517Jeff Brown final String globalRouteId = route.mGlobalRouteId; 44569b07161bebdb2c726e3a826c2268866f1a94517Jeff Brown if (globalRouteId != null) { 44669b07161bebdb2c726e3a826c2268866f1a94517Jeff Brown for (int j = 0; j < globalRouteCount; j++) { 44769b07161bebdb2c726e3a826c2268866f1a94517Jeff Brown MediaRouterClientState.RouteInfo globalRoute = globalRoutes.get(j); 44869b07161bebdb2c726e3a826c2268866f1a94517Jeff Brown if (globalRouteId.equals(globalRoute.id)) { 44969b07161bebdb2c726e3a826c2268866f1a94517Jeff Brown continue outer; // found 45069b07161bebdb2c726e3a826c2268866f1a94517Jeff Brown } 45169b07161bebdb2c726e3a826c2268866f1a94517Jeff Brown } 45269b07161bebdb2c726e3a826c2268866f1a94517Jeff Brown // not found 45369b07161bebdb2c726e3a826c2268866f1a94517Jeff Brown removeRouteStatic(route); 45469b07161bebdb2c726e3a826c2268866f1a94517Jeff Brown } 45569b07161bebdb2c726e3a826c2268866f1a94517Jeff Brown } 45669b07161bebdb2c726e3a826c2268866f1a94517Jeff Brown } 45769b07161bebdb2c726e3a826c2268866f1a94517Jeff Brown 45869b07161bebdb2c726e3a826c2268866f1a94517Jeff Brown void requestSetVolume(RouteInfo route, int volume) { 45969b07161bebdb2c726e3a826c2268866f1a94517Jeff Brown if (route.mGlobalRouteId != null && mClient != null) { 46069b07161bebdb2c726e3a826c2268866f1a94517Jeff Brown try { 46169b07161bebdb2c726e3a826c2268866f1a94517Jeff Brown mMediaRouterService.requestSetVolume(mClient, 46269b07161bebdb2c726e3a826c2268866f1a94517Jeff Brown route.mGlobalRouteId, volume); 46369b07161bebdb2c726e3a826c2268866f1a94517Jeff Brown } catch (RemoteException ex) { 46469b07161bebdb2c726e3a826c2268866f1a94517Jeff Brown Log.w(TAG, "Unable to request volume change.", ex); 46569b07161bebdb2c726e3a826c2268866f1a94517Jeff Brown } 46669b07161bebdb2c726e3a826c2268866f1a94517Jeff Brown } 46769b07161bebdb2c726e3a826c2268866f1a94517Jeff Brown } 46869b07161bebdb2c726e3a826c2268866f1a94517Jeff Brown 46969b07161bebdb2c726e3a826c2268866f1a94517Jeff Brown void requestUpdateVolume(RouteInfo route, int direction) { 47069b07161bebdb2c726e3a826c2268866f1a94517Jeff Brown if (route.mGlobalRouteId != null && mClient != null) { 47169b07161bebdb2c726e3a826c2268866f1a94517Jeff Brown try { 47269b07161bebdb2c726e3a826c2268866f1a94517Jeff Brown mMediaRouterService.requestUpdateVolume(mClient, 47369b07161bebdb2c726e3a826c2268866f1a94517Jeff Brown route.mGlobalRouteId, direction); 47469b07161bebdb2c726e3a826c2268866f1a94517Jeff Brown } catch (RemoteException ex) { 47569b07161bebdb2c726e3a826c2268866f1a94517Jeff Brown Log.w(TAG, "Unable to request volume change.", ex); 47669b07161bebdb2c726e3a826c2268866f1a94517Jeff Brown } 47769b07161bebdb2c726e3a826c2268866f1a94517Jeff Brown } 47869b07161bebdb2c726e3a826c2268866f1a94517Jeff Brown } 47969b07161bebdb2c726e3a826c2268866f1a94517Jeff Brown 48069b07161bebdb2c726e3a826c2268866f1a94517Jeff Brown RouteInfo makeGlobalRoute(MediaRouterClientState.RouteInfo globalRoute) { 481b3658569e9a87afe1b3168837cc282235fd4d1f8Sungsoo RouteInfo route = new RouteInfo(mSystemCategory); 48269b07161bebdb2c726e3a826c2268866f1a94517Jeff Brown route.mGlobalRouteId = globalRoute.id; 48369b07161bebdb2c726e3a826c2268866f1a94517Jeff Brown route.mName = globalRoute.name; 48469b07161bebdb2c726e3a826c2268866f1a94517Jeff Brown route.mDescription = globalRoute.description; 48569b07161bebdb2c726e3a826c2268866f1a94517Jeff Brown route.mSupportedTypes = globalRoute.supportedTypes; 4869dd66417ea984e4ff809ecdba78e3f55a8a17467Insun Kang route.mDeviceType = globalRoute.deviceType; 48769b07161bebdb2c726e3a826c2268866f1a94517Jeff Brown route.mEnabled = globalRoute.enabled; 48839ad0e559896b45185429ea17cd12f18f7ae842cJeff Brown route.setRealStatusCode(globalRoute.statusCode); 48969b07161bebdb2c726e3a826c2268866f1a94517Jeff Brown route.mPlaybackType = globalRoute.playbackType; 49069b07161bebdb2c726e3a826c2268866f1a94517Jeff Brown route.mPlaybackStream = globalRoute.playbackStream; 49169b07161bebdb2c726e3a826c2268866f1a94517Jeff Brown route.mVolume = globalRoute.volume; 49269b07161bebdb2c726e3a826c2268866f1a94517Jeff Brown route.mVolumeMax = globalRoute.volumeMax; 49369b07161bebdb2c726e3a826c2268866f1a94517Jeff Brown route.mVolumeHandling = globalRoute.volumeHandling; 4945830b0b33618940d65197cec99d697b21908fec8Chong Zhang route.mPresentationDisplayId = globalRoute.presentationDisplayId; 4955830b0b33618940d65197cec99d697b21908fec8Chong Zhang route.updatePresentationDisplay(); 49669b07161bebdb2c726e3a826c2268866f1a94517Jeff Brown return route; 49769b07161bebdb2c726e3a826c2268866f1a94517Jeff Brown } 49869b07161bebdb2c726e3a826c2268866f1a94517Jeff Brown 49969b07161bebdb2c726e3a826c2268866f1a94517Jeff Brown void updateGlobalRoute(RouteInfo route, MediaRouterClientState.RouteInfo globalRoute) { 50069b07161bebdb2c726e3a826c2268866f1a94517Jeff Brown boolean changed = false; 50169b07161bebdb2c726e3a826c2268866f1a94517Jeff Brown boolean volumeChanged = false; 50269b07161bebdb2c726e3a826c2268866f1a94517Jeff Brown boolean presentationDisplayChanged = false; 50369b07161bebdb2c726e3a826c2268866f1a94517Jeff Brown 504e6585b32ea586743258a5457e2184ffc087f2d2fKenny Root if (!Objects.equals(route.mName, globalRoute.name)) { 50569b07161bebdb2c726e3a826c2268866f1a94517Jeff Brown route.mName = globalRoute.name; 50669b07161bebdb2c726e3a826c2268866f1a94517Jeff Brown changed = true; 50769b07161bebdb2c726e3a826c2268866f1a94517Jeff Brown } 508e6585b32ea586743258a5457e2184ffc087f2d2fKenny Root if (!Objects.equals(route.mDescription, globalRoute.description)) { 50969b07161bebdb2c726e3a826c2268866f1a94517Jeff Brown route.mDescription = globalRoute.description; 51069b07161bebdb2c726e3a826c2268866f1a94517Jeff Brown changed = true; 51169b07161bebdb2c726e3a826c2268866f1a94517Jeff Brown } 512af574183c274f51d04487a9c8355e9f34a1150f2Jeff Brown final int oldSupportedTypes = route.mSupportedTypes; 513af574183c274f51d04487a9c8355e9f34a1150f2Jeff Brown if (oldSupportedTypes != globalRoute.supportedTypes) { 51469b07161bebdb2c726e3a826c2268866f1a94517Jeff Brown route.mSupportedTypes = globalRoute.supportedTypes; 51569b07161bebdb2c726e3a826c2268866f1a94517Jeff Brown changed = true; 51669b07161bebdb2c726e3a826c2268866f1a94517Jeff Brown } 51769b07161bebdb2c726e3a826c2268866f1a94517Jeff Brown if (route.mEnabled != globalRoute.enabled) { 51869b07161bebdb2c726e3a826c2268866f1a94517Jeff Brown route.mEnabled = globalRoute.enabled; 51969b07161bebdb2c726e3a826c2268866f1a94517Jeff Brown changed = true; 52069b07161bebdb2c726e3a826c2268866f1a94517Jeff Brown } 52139ad0e559896b45185429ea17cd12f18f7ae842cJeff Brown if (route.mRealStatusCode != globalRoute.statusCode) { 52239ad0e559896b45185429ea17cd12f18f7ae842cJeff Brown route.setRealStatusCode(globalRoute.statusCode); 52369b07161bebdb2c726e3a826c2268866f1a94517Jeff Brown changed = true; 52469b07161bebdb2c726e3a826c2268866f1a94517Jeff Brown } 52569b07161bebdb2c726e3a826c2268866f1a94517Jeff Brown if (route.mPlaybackType != globalRoute.playbackType) { 52669b07161bebdb2c726e3a826c2268866f1a94517Jeff Brown route.mPlaybackType = globalRoute.playbackType; 52769b07161bebdb2c726e3a826c2268866f1a94517Jeff Brown changed = true; 52869b07161bebdb2c726e3a826c2268866f1a94517Jeff Brown } 52969b07161bebdb2c726e3a826c2268866f1a94517Jeff Brown if (route.mPlaybackStream != globalRoute.playbackStream) { 53069b07161bebdb2c726e3a826c2268866f1a94517Jeff Brown route.mPlaybackStream = globalRoute.playbackStream; 53169b07161bebdb2c726e3a826c2268866f1a94517Jeff Brown changed = true; 53269b07161bebdb2c726e3a826c2268866f1a94517Jeff Brown } 53369b07161bebdb2c726e3a826c2268866f1a94517Jeff Brown if (route.mVolume != globalRoute.volume) { 53469b07161bebdb2c726e3a826c2268866f1a94517Jeff Brown route.mVolume = globalRoute.volume; 53569b07161bebdb2c726e3a826c2268866f1a94517Jeff Brown changed = true; 53669b07161bebdb2c726e3a826c2268866f1a94517Jeff Brown volumeChanged = true; 53769b07161bebdb2c726e3a826c2268866f1a94517Jeff Brown } 53869b07161bebdb2c726e3a826c2268866f1a94517Jeff Brown if (route.mVolumeMax != globalRoute.volumeMax) { 53969b07161bebdb2c726e3a826c2268866f1a94517Jeff Brown route.mVolumeMax = globalRoute.volumeMax; 54069b07161bebdb2c726e3a826c2268866f1a94517Jeff Brown changed = true; 54169b07161bebdb2c726e3a826c2268866f1a94517Jeff Brown volumeChanged = true; 54269b07161bebdb2c726e3a826c2268866f1a94517Jeff Brown } 54369b07161bebdb2c726e3a826c2268866f1a94517Jeff Brown if (route.mVolumeHandling != globalRoute.volumeHandling) { 54469b07161bebdb2c726e3a826c2268866f1a94517Jeff Brown route.mVolumeHandling = globalRoute.volumeHandling; 54569b07161bebdb2c726e3a826c2268866f1a94517Jeff Brown changed = true; 54669b07161bebdb2c726e3a826c2268866f1a94517Jeff Brown volumeChanged = true; 54769b07161bebdb2c726e3a826c2268866f1a94517Jeff Brown } 5485830b0b33618940d65197cec99d697b21908fec8Chong Zhang if (route.mPresentationDisplayId != globalRoute.presentationDisplayId) { 5495830b0b33618940d65197cec99d697b21908fec8Chong Zhang route.mPresentationDisplayId = globalRoute.presentationDisplayId; 5505830b0b33618940d65197cec99d697b21908fec8Chong Zhang route.updatePresentationDisplay(); 55169b07161bebdb2c726e3a826c2268866f1a94517Jeff Brown changed = true; 55269b07161bebdb2c726e3a826c2268866f1a94517Jeff Brown presentationDisplayChanged = true; 55369b07161bebdb2c726e3a826c2268866f1a94517Jeff Brown } 55469b07161bebdb2c726e3a826c2268866f1a94517Jeff Brown 55569b07161bebdb2c726e3a826c2268866f1a94517Jeff Brown if (changed) { 556af574183c274f51d04487a9c8355e9f34a1150f2Jeff Brown dispatchRouteChanged(route, oldSupportedTypes); 55769b07161bebdb2c726e3a826c2268866f1a94517Jeff Brown } 55869b07161bebdb2c726e3a826c2268866f1a94517Jeff Brown if (volumeChanged) { 55969b07161bebdb2c726e3a826c2268866f1a94517Jeff Brown dispatchRouteVolumeChanged(route); 56069b07161bebdb2c726e3a826c2268866f1a94517Jeff Brown } 56169b07161bebdb2c726e3a826c2268866f1a94517Jeff Brown if (presentationDisplayChanged) { 56269b07161bebdb2c726e3a826c2268866f1a94517Jeff Brown dispatchRoutePresentationDisplayChanged(route); 56369b07161bebdb2c726e3a826c2268866f1a94517Jeff Brown } 56469b07161bebdb2c726e3a826c2268866f1a94517Jeff Brown } 56569b07161bebdb2c726e3a826c2268866f1a94517Jeff Brown 56669b07161bebdb2c726e3a826c2268866f1a94517Jeff Brown RouteInfo findGlobalRoute(String globalRouteId) { 56769b07161bebdb2c726e3a826c2268866f1a94517Jeff Brown final int count = mRoutes.size(); 56869b07161bebdb2c726e3a826c2268866f1a94517Jeff Brown for (int i = 0; i < count; i++) { 56969b07161bebdb2c726e3a826c2268866f1a94517Jeff Brown final RouteInfo route = mRoutes.get(i); 57069b07161bebdb2c726e3a826c2268866f1a94517Jeff Brown if (globalRouteId.equals(route.mGlobalRouteId)) { 57169b07161bebdb2c726e3a826c2268866f1a94517Jeff Brown return route; 57269b07161bebdb2c726e3a826c2268866f1a94517Jeff Brown } 57369b07161bebdb2c726e3a826c2268866f1a94517Jeff Brown } 57469b07161bebdb2c726e3a826c2268866f1a94517Jeff Brown return null; 57569b07161bebdb2c726e3a826c2268866f1a94517Jeff Brown } 57669b07161bebdb2c726e3a826c2268866f1a94517Jeff Brown 577b3658569e9a87afe1b3168837cc282235fd4d1f8Sungsoo boolean isPlaybackActive() { 578b3658569e9a87afe1b3168837cc282235fd4d1f8Sungsoo if (mClient != null) { 579b3658569e9a87afe1b3168837cc282235fd4d1f8Sungsoo try { 580b3658569e9a87afe1b3168837cc282235fd4d1f8Sungsoo return mMediaRouterService.isPlaybackActive(mClient); 581b3658569e9a87afe1b3168837cc282235fd4d1f8Sungsoo } catch (RemoteException ex) { 582b3658569e9a87afe1b3168837cc282235fd4d1f8Sungsoo Log.e(TAG, "Unable to retrieve playback active state.", ex); 583b3658569e9a87afe1b3168837cc282235fd4d1f8Sungsoo } 584b3658569e9a87afe1b3168837cc282235fd4d1f8Sungsoo } 585b3658569e9a87afe1b3168837cc282235fd4d1f8Sungsoo return false; 586b3658569e9a87afe1b3168837cc282235fd4d1f8Sungsoo } 587b3658569e9a87afe1b3168837cc282235fd4d1f8Sungsoo 58869b07161bebdb2c726e3a826c2268866f1a94517Jeff Brown final class Client extends IMediaRouterClient.Stub { 58969b07161bebdb2c726e3a826c2268866f1a94517Jeff Brown @Override 59069b07161bebdb2c726e3a826c2268866f1a94517Jeff Brown public void onStateChanged() { 59169b07161bebdb2c726e3a826c2268866f1a94517Jeff Brown mHandler.post(new Runnable() { 59269b07161bebdb2c726e3a826c2268866f1a94517Jeff Brown @Override 59369b07161bebdb2c726e3a826c2268866f1a94517Jeff Brown public void run() { 59469b07161bebdb2c726e3a826c2268866f1a94517Jeff Brown if (Client.this == mClient) { 59569b07161bebdb2c726e3a826c2268866f1a94517Jeff Brown updateClientState(); 59669b07161bebdb2c726e3a826c2268866f1a94517Jeff Brown } 59769b07161bebdb2c726e3a826c2268866f1a94517Jeff Brown } 59869b07161bebdb2c726e3a826c2268866f1a94517Jeff Brown }); 59969b07161bebdb2c726e3a826c2268866f1a94517Jeff Brown } 600b3658569e9a87afe1b3168837cc282235fd4d1f8Sungsoo 601b3658569e9a87afe1b3168837cc282235fd4d1f8Sungsoo @Override 602b3658569e9a87afe1b3168837cc282235fd4d1f8Sungsoo public void onRestoreRoute() { 603133a24e09553ac9a8498475eb19bd16c915360a6Sungsoo Lim mHandler.post(new Runnable() { 604133a24e09553ac9a8498475eb19bd16c915360a6Sungsoo Lim @Override 605133a24e09553ac9a8498475eb19bd16c915360a6Sungsoo Lim public void run() { 606133a24e09553ac9a8498475eb19bd16c915360a6Sungsoo Lim // Skip restoring route if the selected route is not a system audio route, 607133a24e09553ac9a8498475eb19bd16c915360a6Sungsoo Lim // MediaRouter is initializing, or mClient was changed. 608133a24e09553ac9a8498475eb19bd16c915360a6Sungsoo Lim if (Client.this != mClient || mSelectedRoute == null 609133a24e09553ac9a8498475eb19bd16c915360a6Sungsoo Lim || (mSelectedRoute != mDefaultAudioVideo 610133a24e09553ac9a8498475eb19bd16c915360a6Sungsoo Lim && mSelectedRoute != mBluetoothA2dpRoute)) { 611133a24e09553ac9a8498475eb19bd16c915360a6Sungsoo Lim return; 612133a24e09553ac9a8498475eb19bd16c915360a6Sungsoo Lim } 613133a24e09553ac9a8498475eb19bd16c915360a6Sungsoo Lim Log.v(TAG, "onRestoreRoute() : route=" + mSelectedRoute); 614133a24e09553ac9a8498475eb19bd16c915360a6Sungsoo Lim mSelectedRoute.select(); 615133a24e09553ac9a8498475eb19bd16c915360a6Sungsoo Lim } 616133a24e09553ac9a8498475eb19bd16c915360a6Sungsoo Lim }); 617b3658569e9a87afe1b3168837cc282235fd4d1f8Sungsoo } 61869b07161bebdb2c726e3a826c2268866f1a94517Jeff Brown } 619b58b8f832d06b0ffa8886eba5a4916578a3b8743Dianne Hackborn } 6209a1de308cea2d160778fd977825f10a07b49d738Adam Powell 621b58b8f832d06b0ffa8886eba5a4916578a3b8743Dianne Hackborn static Static sStatic; 6229a1de308cea2d160778fd977825f10a07b49d738Adam Powell 6239a1de308cea2d160778fd977825f10a07b49d738Adam Powell /** 6249a1de308cea2d160778fd977825f10a07b49d738Adam Powell * Route type flag for live audio. 6259a1de308cea2d160778fd977825f10a07b49d738Adam Powell * 6269a1de308cea2d160778fd977825f10a07b49d738Adam Powell * <p>A device that supports live audio routing will allow the media audio stream 6279a1de308cea2d160778fd977825f10a07b49d738Adam Powell * to be routed to supported destinations. This can include internal speakers or 6289a1de308cea2d160778fd977825f10a07b49d738Adam Powell * audio jacks on the device itself, A2DP devices, and more.</p> 6299a1de308cea2d160778fd977825f10a07b49d738Adam Powell * 6309a1de308cea2d160778fd977825f10a07b49d738Adam Powell * <p>Once initiated this routing is transparent to the application. All audio 6319a1de308cea2d160778fd977825f10a07b49d738Adam Powell * played on the media stream will be routed to the selected destination.</p> 6329a1de308cea2d160778fd977825f10a07b49d738Adam Powell */ 63369b07161bebdb2c726e3a826c2268866f1a94517Jeff Brown public static final int ROUTE_TYPE_LIVE_AUDIO = 1 << 0; 6349a1de308cea2d160778fd977825f10a07b49d738Adam Powell 6359a1de308cea2d160778fd977825f10a07b49d738Adam Powell /** 636705ab808cf023e0cc38c2ba7cdb9571942cdc04fAdam Powell * Route type flag for live video. 637705ab808cf023e0cc38c2ba7cdb9571942cdc04fAdam Powell * 638705ab808cf023e0cc38c2ba7cdb9571942cdc04fAdam Powell * <p>A device that supports live video routing will allow a mirrored version 639705ab808cf023e0cc38c2ba7cdb9571942cdc04fAdam Powell * of the device's primary display or a customized 640705ab808cf023e0cc38c2ba7cdb9571942cdc04fAdam Powell * {@link android.app.Presentation Presentation} to be routed to supported destinations.</p> 641705ab808cf023e0cc38c2ba7cdb9571942cdc04fAdam Powell * 642705ab808cf023e0cc38c2ba7cdb9571942cdc04fAdam Powell * <p>Once initiated, display mirroring is transparent to the application. 643705ab808cf023e0cc38c2ba7cdb9571942cdc04fAdam Powell * While remote routing is active the application may use a 644705ab808cf023e0cc38c2ba7cdb9571942cdc04fAdam Powell * {@link android.app.Presentation Presentation} to replace the mirrored view 645705ab808cf023e0cc38c2ba7cdb9571942cdc04fAdam Powell * on the external display with different content.</p> 64692130f6407dc51c58b3b941d28a6daf4e04b8d62Jeff Brown * 64792130f6407dc51c58b3b941d28a6daf4e04b8d62Jeff Brown * @see RouteInfo#getPresentationDisplay() 64892130f6407dc51c58b3b941d28a6daf4e04b8d62Jeff Brown * @see android.app.Presentation 649705ab808cf023e0cc38c2ba7cdb9571942cdc04fAdam Powell */ 65069b07161bebdb2c726e3a826c2268866f1a94517Jeff Brown public static final int ROUTE_TYPE_LIVE_VIDEO = 1 << 1; 65169b07161bebdb2c726e3a826c2268866f1a94517Jeff Brown 65269b07161bebdb2c726e3a826c2268866f1a94517Jeff Brown /** 65369b07161bebdb2c726e3a826c2268866f1a94517Jeff Brown * Temporary interop constant to identify remote displays. 65469b07161bebdb2c726e3a826c2268866f1a94517Jeff Brown * @hide To be removed when media router API is updated. 65569b07161bebdb2c726e3a826c2268866f1a94517Jeff Brown */ 65669b07161bebdb2c726e3a826c2268866f1a94517Jeff Brown public static final int ROUTE_TYPE_REMOTE_DISPLAY = 1 << 2; 657705ab808cf023e0cc38c2ba7cdb9571942cdc04fAdam Powell 658705ab808cf023e0cc38c2ba7cdb9571942cdc04fAdam Powell /** 6599a1de308cea2d160778fd977825f10a07b49d738Adam Powell * Route type flag for application-specific usage. 6609a1de308cea2d160778fd977825f10a07b49d738Adam Powell * 6619a1de308cea2d160778fd977825f10a07b49d738Adam Powell * <p>Unlike other media route types, user routes are managed by the application. 6629a1de308cea2d160778fd977825f10a07b49d738Adam Powell * The MediaRouter will manage and dispatch events for user routes, but the application 6639a1de308cea2d160778fd977825f10a07b49d738Adam Powell * is expected to interpret the meaning of these events and perform the requested 6649a1de308cea2d160778fd977825f10a07b49d738Adam Powell * routing tasks.</p> 6659a1de308cea2d160778fd977825f10a07b49d738Adam Powell */ 66669b07161bebdb2c726e3a826c2268866f1a94517Jeff Brown public static final int ROUTE_TYPE_USER = 1 << 23; 66769b07161bebdb2c726e3a826c2268866f1a94517Jeff Brown 66869b07161bebdb2c726e3a826c2268866f1a94517Jeff Brown static final int ROUTE_TYPE_ANY = ROUTE_TYPE_LIVE_AUDIO | ROUTE_TYPE_LIVE_VIDEO 66969b07161bebdb2c726e3a826c2268866f1a94517Jeff Brown | ROUTE_TYPE_REMOTE_DISPLAY | ROUTE_TYPE_USER; 6709a1de308cea2d160778fd977825f10a07b49d738Adam Powell 67166f3b39ec452a8a03e3254e77e19ccb764ee931fJeff Brown /** 67266f3b39ec452a8a03e3254e77e19ccb764ee931fJeff Brown * Flag for {@link #addCallback}: Actively scan for routes while this callback 67366f3b39ec452a8a03e3254e77e19ccb764ee931fJeff Brown * is registered. 67466f3b39ec452a8a03e3254e77e19ccb764ee931fJeff Brown * <p> 67566f3b39ec452a8a03e3254e77e19ccb764ee931fJeff Brown * When this flag is specified, the media router will actively scan for new 67666f3b39ec452a8a03e3254e77e19ccb764ee931fJeff Brown * routes. Certain routes, such as wifi display routes, may not be discoverable 67766f3b39ec452a8a03e3254e77e19ccb764ee931fJeff Brown * except when actively scanning. This flag is typically used when the route picker 67866f3b39ec452a8a03e3254e77e19ccb764ee931fJeff Brown * dialog has been opened by the user to ensure that the route information is 67966f3b39ec452a8a03e3254e77e19ccb764ee931fJeff Brown * up to date. 68066f3b39ec452a8a03e3254e77e19ccb764ee931fJeff Brown * </p><p> 68166f3b39ec452a8a03e3254e77e19ccb764ee931fJeff Brown * Active scanning may consume a significant amount of power and may have intrusive 68266f3b39ec452a8a03e3254e77e19ccb764ee931fJeff Brown * effects on wireless connectivity. Therefore it is important that active scanning 68366f3b39ec452a8a03e3254e77e19ccb764ee931fJeff Brown * only be requested when it is actually needed to satisfy a user request to 68466f3b39ec452a8a03e3254e77e19ccb764ee931fJeff Brown * discover and select a new route. 68566f3b39ec452a8a03e3254e77e19ccb764ee931fJeff Brown * </p> 68666f3b39ec452a8a03e3254e77e19ccb764ee931fJeff Brown */ 68714507e257af5d71577574e25cbd690c4b54c9272Jeff Brown public static final int CALLBACK_FLAG_PERFORM_ACTIVE_SCAN = 1 << 0; 68866f3b39ec452a8a03e3254e77e19ccb764ee931fJeff Brown 68966f3b39ec452a8a03e3254e77e19ccb764ee931fJeff Brown /** 69066f3b39ec452a8a03e3254e77e19ccb764ee931fJeff Brown * Flag for {@link #addCallback}: Do not filter route events. 69166f3b39ec452a8a03e3254e77e19ccb764ee931fJeff Brown * <p> 69266f3b39ec452a8a03e3254e77e19ccb764ee931fJeff Brown * When this flag is specified, the callback will be invoked for event that affect any 69369b07161bebdb2c726e3a826c2268866f1a94517Jeff Brown * route even if they do not match the callback's filter. 69466f3b39ec452a8a03e3254e77e19ccb764ee931fJeff Brown * </p> 69566f3b39ec452a8a03e3254e77e19ccb764ee931fJeff Brown */ 69666f3b39ec452a8a03e3254e77e19ccb764ee931fJeff Brown public static final int CALLBACK_FLAG_UNFILTERED_EVENTS = 1 << 1; 69766f3b39ec452a8a03e3254e77e19ccb764ee931fJeff Brown 69869b07161bebdb2c726e3a826c2268866f1a94517Jeff Brown /** 69969b07161bebdb2c726e3a826c2268866f1a94517Jeff Brown * Explicitly requests discovery. 70069b07161bebdb2c726e3a826c2268866f1a94517Jeff Brown * 70169b07161bebdb2c726e3a826c2268866f1a94517Jeff Brown * @hide Future API ported from support library. Revisit this later. 70269b07161bebdb2c726e3a826c2268866f1a94517Jeff Brown */ 70369b07161bebdb2c726e3a826c2268866f1a94517Jeff Brown public static final int CALLBACK_FLAG_REQUEST_DISCOVERY = 1 << 2; 70469b07161bebdb2c726e3a826c2268866f1a94517Jeff Brown 70569b07161bebdb2c726e3a826c2268866f1a94517Jeff Brown /** 70669b07161bebdb2c726e3a826c2268866f1a94517Jeff Brown * Requests that discovery be performed but only if there is some other active 70769b07161bebdb2c726e3a826c2268866f1a94517Jeff Brown * callback already registered. 70869b07161bebdb2c726e3a826c2268866f1a94517Jeff Brown * 70969b07161bebdb2c726e3a826c2268866f1a94517Jeff Brown * @hide Compatibility workaround for the fact that applications do not currently 71069b07161bebdb2c726e3a826c2268866f1a94517Jeff Brown * request discovery explicitly (except when using the support library API). 71169b07161bebdb2c726e3a826c2268866f1a94517Jeff Brown */ 71269b07161bebdb2c726e3a826c2268866f1a94517Jeff Brown public static final int CALLBACK_FLAG_PASSIVE_DISCOVERY = 1 << 3; 71369b07161bebdb2c726e3a826c2268866f1a94517Jeff Brown 7140abd3a6ce83ed23abe614155e776b600ef2a66c3Jeff Brown /** 7150abd3a6ce83ed23abe614155e776b600ef2a66c3Jeff Brown * Flag for {@link #isRouteAvailable}: Ignore the default route. 7160abd3a6ce83ed23abe614155e776b600ef2a66c3Jeff Brown * <p> 7170abd3a6ce83ed23abe614155e776b600ef2a66c3Jeff Brown * This flag is used to determine whether a matching non-default route is available. 7180abd3a6ce83ed23abe614155e776b600ef2a66c3Jeff Brown * This constraint may be used to decide whether to offer the route chooser dialog 7190abd3a6ce83ed23abe614155e776b600ef2a66c3Jeff Brown * to the user. There is no point offering the chooser if there are no 7200abd3a6ce83ed23abe614155e776b600ef2a66c3Jeff Brown * non-default choices. 7210abd3a6ce83ed23abe614155e776b600ef2a66c3Jeff Brown * </p> 7220abd3a6ce83ed23abe614155e776b600ef2a66c3Jeff Brown * 7230abd3a6ce83ed23abe614155e776b600ef2a66c3Jeff Brown * @hide Future API ported from support library. Revisit this later. 7240abd3a6ce83ed23abe614155e776b600ef2a66c3Jeff Brown */ 7250abd3a6ce83ed23abe614155e776b600ef2a66c3Jeff Brown public static final int AVAILABILITY_FLAG_IGNORE_DEFAULT_ROUTE = 1 << 0; 7260abd3a6ce83ed23abe614155e776b600ef2a66c3Jeff Brown 7279a1de308cea2d160778fd977825f10a07b49d738Adam Powell // Maps application contexts 7289a1de308cea2d160778fd977825f10a07b49d738Adam Powell static final HashMap<Context, MediaRouter> sRouters = new HashMap<Context, MediaRouter>(); 7299a1de308cea2d160778fd977825f10a07b49d738Adam Powell 7309a1de308cea2d160778fd977825f10a07b49d738Adam Powell static String typesToString(int types) { 7319a1de308cea2d160778fd977825f10a07b49d738Adam Powell final StringBuilder result = new StringBuilder(); 7329a1de308cea2d160778fd977825f10a07b49d738Adam Powell if ((types & ROUTE_TYPE_LIVE_AUDIO) != 0) { 7339a1de308cea2d160778fd977825f10a07b49d738Adam Powell result.append("ROUTE_TYPE_LIVE_AUDIO "); 7349a1de308cea2d160778fd977825f10a07b49d738Adam Powell } 7352bb7c122ef6ce8539dfbaeb3292adcd942185f82Bryan Mawhinney if ((types & ROUTE_TYPE_LIVE_VIDEO) != 0) { 7362bb7c122ef6ce8539dfbaeb3292adcd942185f82Bryan Mawhinney result.append("ROUTE_TYPE_LIVE_VIDEO "); 7372bb7c122ef6ce8539dfbaeb3292adcd942185f82Bryan Mawhinney } 73869b07161bebdb2c726e3a826c2268866f1a94517Jeff Brown if ((types & ROUTE_TYPE_REMOTE_DISPLAY) != 0) { 73969b07161bebdb2c726e3a826c2268866f1a94517Jeff Brown result.append("ROUTE_TYPE_REMOTE_DISPLAY "); 74069b07161bebdb2c726e3a826c2268866f1a94517Jeff Brown } 7419a1de308cea2d160778fd977825f10a07b49d738Adam Powell if ((types & ROUTE_TYPE_USER) != 0) { 7429a1de308cea2d160778fd977825f10a07b49d738Adam Powell result.append("ROUTE_TYPE_USER "); 7439a1de308cea2d160778fd977825f10a07b49d738Adam Powell } 7449a1de308cea2d160778fd977825f10a07b49d738Adam Powell return result.toString(); 7459a1de308cea2d160778fd977825f10a07b49d738Adam Powell } 7469a1de308cea2d160778fd977825f10a07b49d738Adam Powell 747b58b8f832d06b0ffa8886eba5a4916578a3b8743Dianne Hackborn /** @hide */ 748b58b8f832d06b0ffa8886eba5a4916578a3b8743Dianne Hackborn public MediaRouter(Context context) { 749b58b8f832d06b0ffa8886eba5a4916578a3b8743Dianne Hackborn synchronized (Static.class) { 750b58b8f832d06b0ffa8886eba5a4916578a3b8743Dianne Hackborn if (sStatic == null) { 7518e37a85bf3dc39519942698dc90a3951306b934bAdam Powell final Context appContext = context.getApplicationContext(); 7528e37a85bf3dc39519942698dc90a3951306b934bAdam Powell sStatic = new Static(appContext); 7538e37a85bf3dc39519942698dc90a3951306b934bAdam Powell sStatic.startMonitoringRoutes(appContext); 754b58b8f832d06b0ffa8886eba5a4916578a3b8743Dianne Hackborn } 7559a1de308cea2d160778fd977825f10a07b49d738Adam Powell } 7569a1de308cea2d160778fd977825f10a07b49d738Adam Powell } 7579a1de308cea2d160778fd977825f10a07b49d738Adam Powell 758690ffb4e1f735148a15f2036d9a3c1962fba188cAdam Powell /** 7593afc18af2cea898753b10e8575dcf20c11356bcaJeff Brown * Gets the default route for playing media content on the system. 7603afc18af2cea898753b10e8575dcf20c11356bcaJeff Brown * <p> 7613afc18af2cea898753b10e8575dcf20c11356bcaJeff Brown * The system always provides a default route. 7623afc18af2cea898753b10e8575dcf20c11356bcaJeff Brown * </p> 7633afc18af2cea898753b10e8575dcf20c11356bcaJeff Brown * 7643afc18af2cea898753b10e8575dcf20c11356bcaJeff Brown * @return The default route, which is guaranteed to never be null. 765690ffb4e1f735148a15f2036d9a3c1962fba188cAdam Powell */ 7663afc18af2cea898753b10e8575dcf20c11356bcaJeff Brown public RouteInfo getDefaultRoute() { 767705ab808cf023e0cc38c2ba7cdb9571942cdc04fAdam Powell return sStatic.mDefaultAudioVideo; 768690ffb4e1f735148a15f2036d9a3c1962fba188cAdam Powell } 769690ffb4e1f735148a15f2036d9a3c1962fba188cAdam Powell 770690ffb4e1f735148a15f2036d9a3c1962fba188cAdam Powell /** 77159579ce59d6e2c42e6ea1b389ba7e228aaa9122fSungsoo Lim * Returns a Bluetooth route if available, otherwise the default route. 77259579ce59d6e2c42e6ea1b389ba7e228aaa9122fSungsoo Lim * @hide 77359579ce59d6e2c42e6ea1b389ba7e228aaa9122fSungsoo Lim */ 77459579ce59d6e2c42e6ea1b389ba7e228aaa9122fSungsoo Lim public RouteInfo getFallbackRoute() { 77559579ce59d6e2c42e6ea1b389ba7e228aaa9122fSungsoo Lim return (sStatic.mBluetoothA2dpRoute != null) 77659579ce59d6e2c42e6ea1b389ba7e228aaa9122fSungsoo Lim ? sStatic.mBluetoothA2dpRoute : sStatic.mDefaultAudioVideo; 77759579ce59d6e2c42e6ea1b389ba7e228aaa9122fSungsoo Lim } 77859579ce59d6e2c42e6ea1b389ba7e228aaa9122fSungsoo Lim 77959579ce59d6e2c42e6ea1b389ba7e228aaa9122fSungsoo Lim /** 7804599696591f745b3a546197d2ba7e5cfc5562484Adam Powell * @hide for use by framework routing UI 7814599696591f745b3a546197d2ba7e5cfc5562484Adam Powell */ 7823afc18af2cea898753b10e8575dcf20c11356bcaJeff Brown public RouteCategory getSystemCategory() { 7834599696591f745b3a546197d2ba7e5cfc5562484Adam Powell return sStatic.mSystemCategory; 7844599696591f745b3a546197d2ba7e5cfc5562484Adam Powell } 7854599696591f745b3a546197d2ba7e5cfc5562484Adam Powell 7860abd3a6ce83ed23abe614155e776b600ef2a66c3Jeff Brown /** @hide */ 7870abd3a6ce83ed23abe614155e776b600ef2a66c3Jeff Brown public RouteInfo getSelectedRoute() { 7880abd3a6ce83ed23abe614155e776b600ef2a66c3Jeff Brown return getSelectedRoute(ROUTE_TYPE_ANY); 7890abd3a6ce83ed23abe614155e776b600ef2a66c3Jeff Brown } 7900abd3a6ce83ed23abe614155e776b600ef2a66c3Jeff Brown 7914599696591f745b3a546197d2ba7e5cfc5562484Adam Powell /** 7921cf2ca83584a4cf0aa3ded787bd191b9a60e3521Adam Powell * Return the currently selected route for any of the given types 793690ffb4e1f735148a15f2036d9a3c1962fba188cAdam Powell * 794690ffb4e1f735148a15f2036d9a3c1962fba188cAdam Powell * @param type route types 795690ffb4e1f735148a15f2036d9a3c1962fba188cAdam Powell * @return the selected route 796690ffb4e1f735148a15f2036d9a3c1962fba188cAdam Powell */ 797690ffb4e1f735148a15f2036d9a3c1962fba188cAdam Powell public RouteInfo getSelectedRoute(int type) { 7981cf2ca83584a4cf0aa3ded787bd191b9a60e3521Adam Powell if (sStatic.mSelectedRoute != null && 7991cf2ca83584a4cf0aa3ded787bd191b9a60e3521Adam Powell (sStatic.mSelectedRoute.mSupportedTypes & type) != 0) { 8001cf2ca83584a4cf0aa3ded787bd191b9a60e3521Adam Powell // If the selected route supports any of the types supplied, it's still considered 8011cf2ca83584a4cf0aa3ded787bd191b9a60e3521Adam Powell // 'selected' for that type. 8021cf2ca83584a4cf0aa3ded787bd191b9a60e3521Adam Powell return sStatic.mSelectedRoute; 8031cf2ca83584a4cf0aa3ded787bd191b9a60e3521Adam Powell } else if (type == ROUTE_TYPE_USER) { 8041cf2ca83584a4cf0aa3ded787bd191b9a60e3521Adam Powell // The caller specifically asked for a user route and the currently selected route 8051cf2ca83584a4cf0aa3ded787bd191b9a60e3521Adam Powell // doesn't qualify. 8061cf2ca83584a4cf0aa3ded787bd191b9a60e3521Adam Powell return null; 8071cf2ca83584a4cf0aa3ded787bd191b9a60e3521Adam Powell } 8081cf2ca83584a4cf0aa3ded787bd191b9a60e3521Adam Powell // If the above didn't match and we're not specifically asking for a user route, 8091cf2ca83584a4cf0aa3ded787bd191b9a60e3521Adam Powell // consider the default selected. 8101cf2ca83584a4cf0aa3ded787bd191b9a60e3521Adam Powell return sStatic.mDefaultAudioVideo; 811690ffb4e1f735148a15f2036d9a3c1962fba188cAdam Powell } 812690ffb4e1f735148a15f2036d9a3c1962fba188cAdam Powell 8139a1de308cea2d160778fd977825f10a07b49d738Adam Powell /** 8140abd3a6ce83ed23abe614155e776b600ef2a66c3Jeff Brown * Returns true if there is a route that matches the specified types. 8150abd3a6ce83ed23abe614155e776b600ef2a66c3Jeff Brown * <p> 8160abd3a6ce83ed23abe614155e776b600ef2a66c3Jeff Brown * This method returns true if there are any available routes that match the types 8170abd3a6ce83ed23abe614155e776b600ef2a66c3Jeff Brown * regardless of whether they are enabled or disabled. If the 8180abd3a6ce83ed23abe614155e776b600ef2a66c3Jeff Brown * {@link #AVAILABILITY_FLAG_IGNORE_DEFAULT_ROUTE} flag is specified, then 8190abd3a6ce83ed23abe614155e776b600ef2a66c3Jeff Brown * the method will only consider non-default routes. 8200abd3a6ce83ed23abe614155e776b600ef2a66c3Jeff Brown * </p> 8210abd3a6ce83ed23abe614155e776b600ef2a66c3Jeff Brown * 8220abd3a6ce83ed23abe614155e776b600ef2a66c3Jeff Brown * @param types The types to match. 8230abd3a6ce83ed23abe614155e776b600ef2a66c3Jeff Brown * @param flags Flags to control the determination of whether a route may be available. 8240abd3a6ce83ed23abe614155e776b600ef2a66c3Jeff Brown * May be zero or {@link #AVAILABILITY_FLAG_IGNORE_DEFAULT_ROUTE}. 8250abd3a6ce83ed23abe614155e776b600ef2a66c3Jeff Brown * @return True if a matching route may be available. 8260abd3a6ce83ed23abe614155e776b600ef2a66c3Jeff Brown * 8270abd3a6ce83ed23abe614155e776b600ef2a66c3Jeff Brown * @hide Future API ported from support library. Revisit this later. 8280abd3a6ce83ed23abe614155e776b600ef2a66c3Jeff Brown */ 8290abd3a6ce83ed23abe614155e776b600ef2a66c3Jeff Brown public boolean isRouteAvailable(int types, int flags) { 8300abd3a6ce83ed23abe614155e776b600ef2a66c3Jeff Brown final int count = sStatic.mRoutes.size(); 8310abd3a6ce83ed23abe614155e776b600ef2a66c3Jeff Brown for (int i = 0; i < count; i++) { 8320abd3a6ce83ed23abe614155e776b600ef2a66c3Jeff Brown RouteInfo route = sStatic.mRoutes.get(i); 8330abd3a6ce83ed23abe614155e776b600ef2a66c3Jeff Brown if (route.matchesTypes(types)) { 8340abd3a6ce83ed23abe614155e776b600ef2a66c3Jeff Brown if ((flags & AVAILABILITY_FLAG_IGNORE_DEFAULT_ROUTE) == 0 8350abd3a6ce83ed23abe614155e776b600ef2a66c3Jeff Brown || route != sStatic.mDefaultAudioVideo) { 8360abd3a6ce83ed23abe614155e776b600ef2a66c3Jeff Brown return true; 8370abd3a6ce83ed23abe614155e776b600ef2a66c3Jeff Brown } 8380abd3a6ce83ed23abe614155e776b600ef2a66c3Jeff Brown } 8390abd3a6ce83ed23abe614155e776b600ef2a66c3Jeff Brown } 8400abd3a6ce83ed23abe614155e776b600ef2a66c3Jeff Brown 8410abd3a6ce83ed23abe614155e776b600ef2a66c3Jeff Brown // It doesn't look like we can find a matching route right now. 8420abd3a6ce83ed23abe614155e776b600ef2a66c3Jeff Brown return false; 8430abd3a6ce83ed23abe614155e776b600ef2a66c3Jeff Brown } 8440abd3a6ce83ed23abe614155e776b600ef2a66c3Jeff Brown 8450abd3a6ce83ed23abe614155e776b600ef2a66c3Jeff Brown /** 8469a1de308cea2d160778fd977825f10a07b49d738Adam Powell * Add a callback to listen to events about specific kinds of media routes. 8479a1de308cea2d160778fd977825f10a07b49d738Adam Powell * If the specified callback is already registered, its registration will be updated for any 8489a1de308cea2d160778fd977825f10a07b49d738Adam Powell * additional route types specified. 84966f3b39ec452a8a03e3254e77e19ccb764ee931fJeff Brown * <p> 85066f3b39ec452a8a03e3254e77e19ccb764ee931fJeff Brown * This is a convenience method that has the same effect as calling 85166f3b39ec452a8a03e3254e77e19ccb764ee931fJeff Brown * {@link #addCallback(int, Callback, int)} without flags. 85266f3b39ec452a8a03e3254e77e19ccb764ee931fJeff Brown * </p> 8539a1de308cea2d160778fd977825f10a07b49d738Adam Powell * 8549a1de308cea2d160778fd977825f10a07b49d738Adam Powell * @param types Types of routes this callback is interested in 8559a1de308cea2d160778fd977825f10a07b49d738Adam Powell * @param cb Callback to add 8569a1de308cea2d160778fd977825f10a07b49d738Adam Powell */ 8579a1de308cea2d160778fd977825f10a07b49d738Adam Powell public void addCallback(int types, Callback cb) { 85866f3b39ec452a8a03e3254e77e19ccb764ee931fJeff Brown addCallback(types, cb, 0); 85966f3b39ec452a8a03e3254e77e19ccb764ee931fJeff Brown } 86066f3b39ec452a8a03e3254e77e19ccb764ee931fJeff Brown 86166f3b39ec452a8a03e3254e77e19ccb764ee931fJeff Brown /** 86266f3b39ec452a8a03e3254e77e19ccb764ee931fJeff Brown * Add a callback to listen to events about specific kinds of media routes. 86366f3b39ec452a8a03e3254e77e19ccb764ee931fJeff Brown * If the specified callback is already registered, its registration will be updated for any 86466f3b39ec452a8a03e3254e77e19ccb764ee931fJeff Brown * additional route types specified. 86566f3b39ec452a8a03e3254e77e19ccb764ee931fJeff Brown * <p> 86666f3b39ec452a8a03e3254e77e19ccb764ee931fJeff Brown * By default, the callback will only be invoked for events that affect routes 86766f3b39ec452a8a03e3254e77e19ccb764ee931fJeff Brown * that match the specified selector. The filtering may be disabled by specifying 86866f3b39ec452a8a03e3254e77e19ccb764ee931fJeff Brown * the {@link #CALLBACK_FLAG_UNFILTERED_EVENTS} flag. 86966f3b39ec452a8a03e3254e77e19ccb764ee931fJeff Brown * </p> 87066f3b39ec452a8a03e3254e77e19ccb764ee931fJeff Brown * 87166f3b39ec452a8a03e3254e77e19ccb764ee931fJeff Brown * @param types Types of routes this callback is interested in 87266f3b39ec452a8a03e3254e77e19ccb764ee931fJeff Brown * @param cb Callback to add 87366f3b39ec452a8a03e3254e77e19ccb764ee931fJeff Brown * @param flags Flags to control the behavior of the callback. 87414507e257af5d71577574e25cbd690c4b54c9272Jeff Brown * May be zero or a combination of {@link #CALLBACK_FLAG_PERFORM_ACTIVE_SCAN} and 87566f3b39ec452a8a03e3254e77e19ccb764ee931fJeff Brown * {@link #CALLBACK_FLAG_UNFILTERED_EVENTS}. 87666f3b39ec452a8a03e3254e77e19ccb764ee931fJeff Brown */ 87766f3b39ec452a8a03e3254e77e19ccb764ee931fJeff Brown public void addCallback(int types, Callback cb, int flags) { 87866f3b39ec452a8a03e3254e77e19ccb764ee931fJeff Brown CallbackInfo info; 87966f3b39ec452a8a03e3254e77e19ccb764ee931fJeff Brown int index = findCallbackInfo(cb); 88066f3b39ec452a8a03e3254e77e19ccb764ee931fJeff Brown if (index >= 0) { 88166f3b39ec452a8a03e3254e77e19ccb764ee931fJeff Brown info = sStatic.mCallbacks.get(index); 88266f3b39ec452a8a03e3254e77e19ccb764ee931fJeff Brown info.type |= types; 88366f3b39ec452a8a03e3254e77e19ccb764ee931fJeff Brown info.flags |= flags; 88466f3b39ec452a8a03e3254e77e19ccb764ee931fJeff Brown } else { 88566f3b39ec452a8a03e3254e77e19ccb764ee931fJeff Brown info = new CallbackInfo(cb, types, flags, this); 88666f3b39ec452a8a03e3254e77e19ccb764ee931fJeff Brown sStatic.mCallbacks.add(info); 88766f3b39ec452a8a03e3254e77e19ccb764ee931fJeff Brown } 88869b07161bebdb2c726e3a826c2268866f1a94517Jeff Brown sStatic.updateDiscoveryRequest(); 8899a1de308cea2d160778fd977825f10a07b49d738Adam Powell } 8909a1de308cea2d160778fd977825f10a07b49d738Adam Powell 8919a1de308cea2d160778fd977825f10a07b49d738Adam Powell /** 8929a1de308cea2d160778fd977825f10a07b49d738Adam Powell * Remove the specified callback. It will no longer receive events about media routing. 8939a1de308cea2d160778fd977825f10a07b49d738Adam Powell * 8949a1de308cea2d160778fd977825f10a07b49d738Adam Powell * @param cb Callback to remove 8959a1de308cea2d160778fd977825f10a07b49d738Adam Powell */ 8969a1de308cea2d160778fd977825f10a07b49d738Adam Powell public void removeCallback(Callback cb) { 89766f3b39ec452a8a03e3254e77e19ccb764ee931fJeff Brown int index = findCallbackInfo(cb); 89866f3b39ec452a8a03e3254e77e19ccb764ee931fJeff Brown if (index >= 0) { 89969b07161bebdb2c726e3a826c2268866f1a94517Jeff Brown sStatic.mCallbacks.remove(index); 90069b07161bebdb2c726e3a826c2268866f1a94517Jeff Brown sStatic.updateDiscoveryRequest(); 90166f3b39ec452a8a03e3254e77e19ccb764ee931fJeff Brown } else { 90266f3b39ec452a8a03e3254e77e19ccb764ee931fJeff Brown Log.w(TAG, "removeCallback(" + cb + "): callback not registered"); 90366f3b39ec452a8a03e3254e77e19ccb764ee931fJeff Brown } 90466f3b39ec452a8a03e3254e77e19ccb764ee931fJeff Brown } 90566f3b39ec452a8a03e3254e77e19ccb764ee931fJeff Brown 90666f3b39ec452a8a03e3254e77e19ccb764ee931fJeff Brown private int findCallbackInfo(Callback cb) { 907b58b8f832d06b0ffa8886eba5a4916578a3b8743Dianne Hackborn final int count = sStatic.mCallbacks.size(); 9089a1de308cea2d160778fd977825f10a07b49d738Adam Powell for (int i = 0; i < count; i++) { 90966f3b39ec452a8a03e3254e77e19ccb764ee931fJeff Brown final CallbackInfo info = sStatic.mCallbacks.get(i); 91066f3b39ec452a8a03e3254e77e19ccb764ee931fJeff Brown if (info.cb == cb) { 91166f3b39ec452a8a03e3254e77e19ccb764ee931fJeff Brown return i; 9129a1de308cea2d160778fd977825f10a07b49d738Adam Powell } 9139a1de308cea2d160778fd977825f10a07b49d738Adam Powell } 91466f3b39ec452a8a03e3254e77e19ccb764ee931fJeff Brown return -1; 9159a1de308cea2d160778fd977825f10a07b49d738Adam Powell } 9169a1de308cea2d160778fd977825f10a07b49d738Adam Powell 917d0d2cda9d414da73773285d7fee9e13aef3495e9Adam Powell /** 918d0d2cda9d414da73773285d7fee9e13aef3495e9Adam Powell * Select the specified route to use for output of the given media types. 9193afc18af2cea898753b10e8575dcf20c11356bcaJeff Brown * <p class="note"> 9203afc18af2cea898753b10e8575dcf20c11356bcaJeff Brown * As API version 18, this function may be used to select any route. 9213afc18af2cea898753b10e8575dcf20c11356bcaJeff Brown * In prior versions, this function could only be used to select user 9223afc18af2cea898753b10e8575dcf20c11356bcaJeff Brown * routes and would ignore any attempt to select a system route. 9233afc18af2cea898753b10e8575dcf20c11356bcaJeff Brown * </p> 924d0d2cda9d414da73773285d7fee9e13aef3495e9Adam Powell * 925d0d2cda9d414da73773285d7fee9e13aef3495e9Adam Powell * @param types type flags indicating which types this route should be used for. 926d0d2cda9d414da73773285d7fee9e13aef3495e9Adam Powell * The route must support at least a subset. 927d0d2cda9d414da73773285d7fee9e13aef3495e9Adam Powell * @param route Route to select 9289d93a378c5158d14f80d46af70433234330ec568P.Y. Laligand * @throws IllegalArgumentException if the given route is {@code null} 929d0d2cda9d414da73773285d7fee9e13aef3495e9Adam Powell */ 9309d93a378c5158d14f80d46af70433234330ec568P.Y. Laligand public void selectRoute(int types, @NonNull RouteInfo route) { 9319d93a378c5158d14f80d46af70433234330ec568P.Y. Laligand if (route == null) { 9329d93a378c5158d14f80d46af70433234330ec568P.Y. Laligand throw new IllegalArgumentException("Route cannot be null."); 9339d93a378c5158d14f80d46af70433234330ec568P.Y. Laligand } 93469b07161bebdb2c726e3a826c2268866f1a94517Jeff Brown selectRouteStatic(types, route, true); 9350d03c042f90bf62d5bad7c64e77028a5f9f8fae0Adam Powell } 93669b07161bebdb2c726e3a826c2268866f1a94517Jeff Brown 9370d03c042f90bf62d5bad7c64e77028a5f9f8fae0Adam Powell /** 9380d03c042f90bf62d5bad7c64e77028a5f9f8fae0Adam Powell * @hide internal use 9390d03c042f90bf62d5bad7c64e77028a5f9f8fae0Adam Powell */ 94069b07161bebdb2c726e3a826c2268866f1a94517Jeff Brown public void selectRouteInt(int types, RouteInfo route, boolean explicit) { 94169b07161bebdb2c726e3a826c2268866f1a94517Jeff Brown selectRouteStatic(types, route, explicit); 942b58b8f832d06b0ffa8886eba5a4916578a3b8743Dianne Hackborn } 943b58b8f832d06b0ffa8886eba5a4916578a3b8743Dianne Hackborn 9449d93a378c5158d14f80d46af70433234330ec568P.Y. Laligand static void selectRouteStatic(int types, @NonNull RouteInfo route, boolean explicit) { 94518687b7a275b3782e63d52cdbe8ee9df191046e7Jae Seo Log.v(TAG, "Selecting route: " + route); 9469d93a378c5158d14f80d46af70433234330ec568P.Y. Laligand assert(route != null); 947705ab808cf023e0cc38c2ba7cdb9571942cdc04fAdam Powell final RouteInfo oldRoute = sStatic.mSelectedRoute; 948546a170bfa9aa8f877b76cbf7666c59161407029Sungsoo Lim final RouteInfo currentSystemRoute = sStatic.isBluetoothA2dpOn() 949546a170bfa9aa8f877b76cbf7666c59161407029Sungsoo Lim ? sStatic.mBluetoothA2dpRoute : sStatic.mDefaultAudioVideo; 950b3658569e9a87afe1b3168837cc282235fd4d1f8Sungsoo boolean wasDefaultOrBluetoothRoute = (oldRoute == sStatic.mDefaultAudioVideo 951b3658569e9a87afe1b3168837cc282235fd4d1f8Sungsoo || oldRoute == sStatic.mBluetoothA2dpRoute); 952b3658569e9a87afe1b3168837cc282235fd4d1f8Sungsoo if (oldRoute == route 953546a170bfa9aa8f877b76cbf7666c59161407029Sungsoo Lim && (!wasDefaultOrBluetoothRoute || route == currentSystemRoute)) { 954b3658569e9a87afe1b3168837cc282235fd4d1f8Sungsoo return; 955b3658569e9a87afe1b3168837cc282235fd4d1f8Sungsoo } 9560abd3a6ce83ed23abe614155e776b600ef2a66c3Jeff Brown if (!route.matchesTypes(types)) { 9570d03c042f90bf62d5bad7c64e77028a5f9f8fae0Adam Powell Log.w(TAG, "selectRoute ignored; cannot select route with supported types " + 9580d03c042f90bf62d5bad7c64e77028a5f9f8fae0Adam Powell typesToString(route.getSupportedTypes()) + " into route types " + 9590d03c042f90bf62d5bad7c64e77028a5f9f8fae0Adam Powell typesToString(types)); 9604ee1f55ce0f4909a7430ab44563a81852f335071Adam Powell return; 9610d03c042f90bf62d5bad7c64e77028a5f9f8fae0Adam Powell } 9629a1de308cea2d160778fd977825f10a07b49d738Adam Powell 963fa6f70e225a7370c1ecb21ee7076c9086ecb9e9aSungsoo final RouteInfo btRoute = sStatic.mBluetoothA2dpRoute; 964b3658569e9a87afe1b3168837cc282235fd4d1f8Sungsoo if (sStatic.isPlaybackActive() && btRoute != null && (types & ROUTE_TYPE_LIVE_AUDIO) != 0 965b3658569e9a87afe1b3168837cc282235fd4d1f8Sungsoo && (route == btRoute || route == sStatic.mDefaultAudioVideo)) { 966fa6f70e225a7370c1ecb21ee7076c9086ecb9e9aSungsoo try { 967fa6f70e225a7370c1ecb21ee7076c9086ecb9e9aSungsoo sStatic.mAudioService.setBluetoothA2dpOn(route == btRoute); 96859579ce59d6e2c42e6ea1b389ba7e228aaa9122fSungsoo Lim // TODO: Remove the following logging when no longer needed. 96959579ce59d6e2c42e6ea1b389ba7e228aaa9122fSungsoo Lim if (route != btRoute) { 97059579ce59d6e2c42e6ea1b389ba7e228aaa9122fSungsoo Lim StackTraceElement[] callStack = Thread.currentThread().getStackTrace(); 97159579ce59d6e2c42e6ea1b389ba7e228aaa9122fSungsoo Lim StringBuffer sb = new StringBuffer(); 97259579ce59d6e2c42e6ea1b389ba7e228aaa9122fSungsoo Lim // callStack[3] is the caller of this method. 97359579ce59d6e2c42e6ea1b389ba7e228aaa9122fSungsoo Lim for (int i = 3; i < callStack.length; i++) { 97459579ce59d6e2c42e6ea1b389ba7e228aaa9122fSungsoo Lim StackTraceElement caller = callStack[i]; 97559579ce59d6e2c42e6ea1b389ba7e228aaa9122fSungsoo Lim sb.append(caller.getClassName() + "." + caller.getMethodName() 97659579ce59d6e2c42e6ea1b389ba7e228aaa9122fSungsoo Lim + ":" + caller.getLineNumber()).append(" "); 97759579ce59d6e2c42e6ea1b389ba7e228aaa9122fSungsoo Lim } 97859579ce59d6e2c42e6ea1b389ba7e228aaa9122fSungsoo Lim Log.w(TAG, "Default route is selected while a BT route is available: pkgName=" 97959579ce59d6e2c42e6ea1b389ba7e228aaa9122fSungsoo Lim + sStatic.mPackageName + ", callers=" + sb.toString()); 98059579ce59d6e2c42e6ea1b389ba7e228aaa9122fSungsoo Lim } 981fa6f70e225a7370c1ecb21ee7076c9086ecb9e9aSungsoo } catch (RemoteException e) { 982fa6f70e225a7370c1ecb21ee7076c9086ecb9e9aSungsoo Log.e(TAG, "Error changing Bluetooth A2DP state", e); 983fa6f70e225a7370c1ecb21ee7076c9086ecb9e9aSungsoo } 984fa6f70e225a7370c1ecb21ee7076c9086ecb9e9aSungsoo } 985fa6f70e225a7370c1ecb21ee7076c9086ecb9e9aSungsoo 986705ab808cf023e0cc38c2ba7cdb9571942cdc04fAdam Powell final WifiDisplay activeDisplay = 987705ab808cf023e0cc38c2ba7cdb9571942cdc04fAdam Powell sStatic.mDisplayService.getWifiDisplayStatus().getActiveDisplay(); 988705ab808cf023e0cc38c2ba7cdb9571942cdc04fAdam Powell final boolean oldRouteHasAddress = oldRoute != null && oldRoute.mDeviceAddress != null; 9899d93a378c5158d14f80d46af70433234330ec568P.Y. Laligand final boolean newRouteHasAddress = route.mDeviceAddress != null; 990705ab808cf023e0cc38c2ba7cdb9571942cdc04fAdam Powell if (activeDisplay != null || oldRouteHasAddress || newRouteHasAddress) { 991705ab808cf023e0cc38c2ba7cdb9571942cdc04fAdam Powell if (newRouteHasAddress && !matchesDeviceAddress(activeDisplay, route)) { 992af574183c274f51d04487a9c8355e9f34a1150f2Jeff Brown if (sStatic.mCanConfigureWifiDisplays) { 993af574183c274f51d04487a9c8355e9f34a1150f2Jeff Brown sStatic.mDisplayService.connectWifiDisplay(route.mDeviceAddress); 994af574183c274f51d04487a9c8355e9f34a1150f2Jeff Brown } else { 995af574183c274f51d04487a9c8355e9f34a1150f2Jeff Brown Log.e(TAG, "Cannot connect to wifi displays because this process " 996af574183c274f51d04487a9c8355e9f34a1150f2Jeff Brown + "is not allowed to do so."); 997af574183c274f51d04487a9c8355e9f34a1150f2Jeff Brown } 998705ab808cf023e0cc38c2ba7cdb9571942cdc04fAdam Powell } else if (activeDisplay != null && !newRouteHasAddress) { 999705ab808cf023e0cc38c2ba7cdb9571942cdc04fAdam Powell sStatic.mDisplayService.disconnectWifiDisplay(); 1000705ab808cf023e0cc38c2ba7cdb9571942cdc04fAdam Powell } 1001705ab808cf023e0cc38c2ba7cdb9571942cdc04fAdam Powell } 1002705ab808cf023e0cc38c2ba7cdb9571942cdc04fAdam Powell 100369b07161bebdb2c726e3a826c2268866f1a94517Jeff Brown sStatic.setSelectedRoute(route, explicit); 100469b07161bebdb2c726e3a826c2268866f1a94517Jeff Brown 1005705ab808cf023e0cc38c2ba7cdb9571942cdc04fAdam Powell if (oldRoute != null) { 1006705ab808cf023e0cc38c2ba7cdb9571942cdc04fAdam Powell dispatchRouteUnselected(types & oldRoute.getSupportedTypes(), oldRoute); 100739ad0e559896b45185429ea17cd12f18f7ae842cJeff Brown if (oldRoute.resolveStatusCode()) { 100839ad0e559896b45185429ea17cd12f18f7ae842cJeff Brown dispatchRouteChanged(oldRoute); 100939ad0e559896b45185429ea17cd12f18f7ae842cJeff Brown } 10109a1de308cea2d160778fd977825f10a07b49d738Adam Powell } 10119a1de308cea2d160778fd977825f10a07b49d738Adam Powell if (route != null) { 101239ad0e559896b45185429ea17cd12f18f7ae842cJeff Brown if (route.resolveStatusCode()) { 101339ad0e559896b45185429ea17cd12f18f7ae842cJeff Brown dispatchRouteChanged(route); 101439ad0e559896b45185429ea17cd12f18f7ae842cJeff Brown } 10159a1de308cea2d160778fd977825f10a07b49d738Adam Powell dispatchRouteSelected(types & route.getSupportedTypes(), route); 10169a1de308cea2d160778fd977825f10a07b49d738Adam Powell } 1017ce468a35b388ca46578934706b38dbae94941643Jeff Brown 1018ce468a35b388ca46578934706b38dbae94941643Jeff Brown // The behavior of active scans may depend on the currently selected route. 1019ce468a35b388ca46578934706b38dbae94941643Jeff Brown sStatic.updateDiscoveryRequest(); 10209a1de308cea2d160778fd977825f10a07b49d738Adam Powell } 10219a1de308cea2d160778fd977825f10a07b49d738Adam Powell 102269b07161bebdb2c726e3a826c2268866f1a94517Jeff Brown static void selectDefaultRouteStatic() { 102369b07161bebdb2c726e3a826c2268866f1a94517Jeff Brown // TODO: Be smarter about the route types here; this selects for all valid. 1024546a170bfa9aa8f877b76cbf7666c59161407029Sungsoo Lim if (sStatic.mSelectedRoute != sStatic.mBluetoothA2dpRoute && sStatic.isBluetoothA2dpOn()) { 102569b07161bebdb2c726e3a826c2268866f1a94517Jeff Brown selectRouteStatic(ROUTE_TYPE_ANY, sStatic.mBluetoothA2dpRoute, false); 102669b07161bebdb2c726e3a826c2268866f1a94517Jeff Brown } else { 102769b07161bebdb2c726e3a826c2268866f1a94517Jeff Brown selectRouteStatic(ROUTE_TYPE_ANY, sStatic.mDefaultAudioVideo, false); 102869b07161bebdb2c726e3a826c2268866f1a94517Jeff Brown } 102969b07161bebdb2c726e3a826c2268866f1a94517Jeff Brown } 103069b07161bebdb2c726e3a826c2268866f1a94517Jeff Brown 10319a1de308cea2d160778fd977825f10a07b49d738Adam Powell /** 1032705ab808cf023e0cc38c2ba7cdb9571942cdc04fAdam Powell * Compare the device address of a display and a route. 1033705ab808cf023e0cc38c2ba7cdb9571942cdc04fAdam Powell * Nulls/no device address will match another null/no address. 1034705ab808cf023e0cc38c2ba7cdb9571942cdc04fAdam Powell */ 1035705ab808cf023e0cc38c2ba7cdb9571942cdc04fAdam Powell static boolean matchesDeviceAddress(WifiDisplay display, RouteInfo info) { 1036705ab808cf023e0cc38c2ba7cdb9571942cdc04fAdam Powell final boolean routeHasAddress = info != null && info.mDeviceAddress != null; 1037705ab808cf023e0cc38c2ba7cdb9571942cdc04fAdam Powell if (display == null && !routeHasAddress) { 1038705ab808cf023e0cc38c2ba7cdb9571942cdc04fAdam Powell return true; 1039705ab808cf023e0cc38c2ba7cdb9571942cdc04fAdam Powell } 1040705ab808cf023e0cc38c2ba7cdb9571942cdc04fAdam Powell 1041705ab808cf023e0cc38c2ba7cdb9571942cdc04fAdam Powell if (display != null && routeHasAddress) { 1042705ab808cf023e0cc38c2ba7cdb9571942cdc04fAdam Powell return display.getDeviceAddress().equals(info.mDeviceAddress); 1043705ab808cf023e0cc38c2ba7cdb9571942cdc04fAdam Powell } 1044705ab808cf023e0cc38c2ba7cdb9571942cdc04fAdam Powell return false; 1045705ab808cf023e0cc38c2ba7cdb9571942cdc04fAdam Powell } 1046705ab808cf023e0cc38c2ba7cdb9571942cdc04fAdam Powell 1047705ab808cf023e0cc38c2ba7cdb9571942cdc04fAdam Powell /** 10489a1de308cea2d160778fd977825f10a07b49d738Adam Powell * Add an app-specified route for media to the MediaRouter. 10499a1de308cea2d160778fd977825f10a07b49d738Adam Powell * App-specified route definitions are created using {@link #createUserRoute(RouteCategory)} 10509a1de308cea2d160778fd977825f10a07b49d738Adam Powell * 10519a1de308cea2d160778fd977825f10a07b49d738Adam Powell * @param info Definition of the route to add 10523afc18af2cea898753b10e8575dcf20c11356bcaJeff Brown * @see #createUserRoute(RouteCategory) 10539a1de308cea2d160778fd977825f10a07b49d738Adam Powell * @see #removeUserRoute(UserRouteInfo) 10549a1de308cea2d160778fd977825f10a07b49d738Adam Powell */ 10559a1de308cea2d160778fd977825f10a07b49d738Adam Powell public void addUserRoute(UserRouteInfo info) { 10562ee6a2a83262d05a566bd713d238e89edfd33a29Adam Powell addRouteStatic(info); 10579a1de308cea2d160778fd977825f10a07b49d738Adam Powell } 10589a1de308cea2d160778fd977825f10a07b49d738Adam Powell 1059d6d0bddee363e0c7fe61f63bd9d9864a71d887d6Adam Powell /** 1060d6d0bddee363e0c7fe61f63bd9d9864a71d887d6Adam Powell * @hide Framework use only 1061d6d0bddee363e0c7fe61f63bd9d9864a71d887d6Adam Powell */ 1062d6d0bddee363e0c7fe61f63bd9d9864a71d887d6Adam Powell public void addRouteInt(RouteInfo info) { 10632ee6a2a83262d05a566bd713d238e89edfd33a29Adam Powell addRouteStatic(info); 1064d6d0bddee363e0c7fe61f63bd9d9864a71d887d6Adam Powell } 1065d6d0bddee363e0c7fe61f63bd9d9864a71d887d6Adam Powell 10662ee6a2a83262d05a566bd713d238e89edfd33a29Adam Powell static void addRouteStatic(RouteInfo info) { 106718687b7a275b3782e63d52cdbe8ee9df191046e7Jae Seo Log.v(TAG, "Adding route: " + info); 10689a1de308cea2d160778fd977825f10a07b49d738Adam Powell final RouteCategory cat = info.getCategory(); 1069b58b8f832d06b0ffa8886eba5a4916578a3b8743Dianne Hackborn if (!sStatic.mCategories.contains(cat)) { 1070b58b8f832d06b0ffa8886eba5a4916578a3b8743Dianne Hackborn sStatic.mCategories.add(cat); 10719a1de308cea2d160778fd977825f10a07b49d738Adam Powell } 1072d0d2cda9d414da73773285d7fee9e13aef3495e9Adam Powell if (cat.isGroupable() && !(info instanceof RouteGroup)) { 10739a1de308cea2d160778fd977825f10a07b49d738Adam Powell // Enforce that any added route in a groupable category must be in a group. 10749a1de308cea2d160778fd977825f10a07b49d738Adam Powell final RouteGroup group = new RouteGroup(info.getCategory()); 1075dbbfa702a09f6d2d36dee1b552442d04a4673f89Adam Powell group.mSupportedTypes = info.mSupportedTypes; 1076b58b8f832d06b0ffa8886eba5a4916578a3b8743Dianne Hackborn sStatic.mRoutes.add(group); 1077d0d2cda9d414da73773285d7fee9e13aef3495e9Adam Powell dispatchRouteAdded(group); 1078b5e2af5919351486a385effe77409d2a91ae9c19Adam Powell group.addRoute(info); 1079d0d2cda9d414da73773285d7fee9e13aef3495e9Adam Powell 10809a1de308cea2d160778fd977825f10a07b49d738Adam Powell info = group; 1081d0d2cda9d414da73773285d7fee9e13aef3495e9Adam Powell } else { 1082b58b8f832d06b0ffa8886eba5a4916578a3b8743Dianne Hackborn sStatic.mRoutes.add(info); 1083d0d2cda9d414da73773285d7fee9e13aef3495e9Adam Powell dispatchRouteAdded(info); 10849a1de308cea2d160778fd977825f10a07b49d738Adam Powell } 10859a1de308cea2d160778fd977825f10a07b49d738Adam Powell } 10869a1de308cea2d160778fd977825f10a07b49d738Adam Powell 10879a1de308cea2d160778fd977825f10a07b49d738Adam Powell /** 10889a1de308cea2d160778fd977825f10a07b49d738Adam Powell * Remove an app-specified route for media from the MediaRouter. 10899a1de308cea2d160778fd977825f10a07b49d738Adam Powell * 10909a1de308cea2d160778fd977825f10a07b49d738Adam Powell * @param info Definition of the route to remove 10919a1de308cea2d160778fd977825f10a07b49d738Adam Powell * @see #addUserRoute(UserRouteInfo) 10929a1de308cea2d160778fd977825f10a07b49d738Adam Powell */ 10939a1de308cea2d160778fd977825f10a07b49d738Adam Powell public void removeUserRoute(UserRouteInfo info) { 109469b07161bebdb2c726e3a826c2268866f1a94517Jeff Brown removeRouteStatic(info); 10959a1de308cea2d160778fd977825f10a07b49d738Adam Powell } 10969a1de308cea2d160778fd977825f10a07b49d738Adam Powell 1097690ffb4e1f735148a15f2036d9a3c1962fba188cAdam Powell /** 1098690ffb4e1f735148a15f2036d9a3c1962fba188cAdam Powell * Remove all app-specified routes from the MediaRouter. 1099690ffb4e1f735148a15f2036d9a3c1962fba188cAdam Powell * 1100690ffb4e1f735148a15f2036d9a3c1962fba188cAdam Powell * @see #removeUserRoute(UserRouteInfo) 1101690ffb4e1f735148a15f2036d9a3c1962fba188cAdam Powell */ 1102690ffb4e1f735148a15f2036d9a3c1962fba188cAdam Powell public void clearUserRoutes() { 1103b58b8f832d06b0ffa8886eba5a4916578a3b8743Dianne Hackborn for (int i = 0; i < sStatic.mRoutes.size(); i++) { 1104b58b8f832d06b0ffa8886eba5a4916578a3b8743Dianne Hackborn final RouteInfo info = sStatic.mRoutes.get(i); 1105d6d0bddee363e0c7fe61f63bd9d9864a71d887d6Adam Powell // TODO Right now, RouteGroups only ever contain user routes. 1106d6d0bddee363e0c7fe61f63bd9d9864a71d887d6Adam Powell // The code below will need to change if this assumption does. 1107d6d0bddee363e0c7fe61f63bd9d9864a71d887d6Adam Powell if (info instanceof UserRouteInfo || info instanceof RouteGroup) { 110869b07161bebdb2c726e3a826c2268866f1a94517Jeff Brown removeRouteStatic(info); 1109690ffb4e1f735148a15f2036d9a3c1962fba188cAdam Powell i--; 1110690ffb4e1f735148a15f2036d9a3c1962fba188cAdam Powell } 1111690ffb4e1f735148a15f2036d9a3c1962fba188cAdam Powell } 1112690ffb4e1f735148a15f2036d9a3c1962fba188cAdam Powell } 1113690ffb4e1f735148a15f2036d9a3c1962fba188cAdam Powell 1114d6d0bddee363e0c7fe61f63bd9d9864a71d887d6Adam Powell /** 1115d6d0bddee363e0c7fe61f63bd9d9864a71d887d6Adam Powell * @hide internal use only 1116d6d0bddee363e0c7fe61f63bd9d9864a71d887d6Adam Powell */ 1117d6d0bddee363e0c7fe61f63bd9d9864a71d887d6Adam Powell public void removeRouteInt(RouteInfo info) { 111869b07161bebdb2c726e3a826c2268866f1a94517Jeff Brown removeRouteStatic(info); 1119d6d0bddee363e0c7fe61f63bd9d9864a71d887d6Adam Powell } 1120d6d0bddee363e0c7fe61f63bd9d9864a71d887d6Adam Powell 112169b07161bebdb2c726e3a826c2268866f1a94517Jeff Brown static void removeRouteStatic(RouteInfo info) { 112218687b7a275b3782e63d52cdbe8ee9df191046e7Jae Seo Log.v(TAG, "Removing route: " + info); 1123b58b8f832d06b0ffa8886eba5a4916578a3b8743Dianne Hackborn if (sStatic.mRoutes.remove(info)) { 11249a1de308cea2d160778fd977825f10a07b49d738Adam Powell final RouteCategory removingCat = info.getCategory(); 1125b58b8f832d06b0ffa8886eba5a4916578a3b8743Dianne Hackborn final int count = sStatic.mRoutes.size(); 11269a1de308cea2d160778fd977825f10a07b49d738Adam Powell boolean found = false; 11279a1de308cea2d160778fd977825f10a07b49d738Adam Powell for (int i = 0; i < count; i++) { 1128b58b8f832d06b0ffa8886eba5a4916578a3b8743Dianne Hackborn final RouteCategory cat = sStatic.mRoutes.get(i).getCategory(); 11299a1de308cea2d160778fd977825f10a07b49d738Adam Powell if (removingCat == cat) { 11309a1de308cea2d160778fd977825f10a07b49d738Adam Powell found = true; 11319a1de308cea2d160778fd977825f10a07b49d738Adam Powell break; 11329a1de308cea2d160778fd977825f10a07b49d738Adam Powell } 11339a1de308cea2d160778fd977825f10a07b49d738Adam Powell } 11340abd3a6ce83ed23abe614155e776b600ef2a66c3Jeff Brown if (info.isSelected()) { 1135d6d0bddee363e0c7fe61f63bd9d9864a71d887d6Adam Powell // Removing the currently selected route? Select the default before we remove it. 113669b07161bebdb2c726e3a826c2268866f1a94517Jeff Brown selectDefaultRouteStatic(); 1137d6d0bddee363e0c7fe61f63bd9d9864a71d887d6Adam Powell } 1138690ffb4e1f735148a15f2036d9a3c1962fba188cAdam Powell if (!found) { 1139b58b8f832d06b0ffa8886eba5a4916578a3b8743Dianne Hackborn sStatic.mCategories.remove(removingCat); 1140690ffb4e1f735148a15f2036d9a3c1962fba188cAdam Powell } 1141690ffb4e1f735148a15f2036d9a3c1962fba188cAdam Powell dispatchRouteRemoved(info); 1142690ffb4e1f735148a15f2036d9a3c1962fba188cAdam Powell } 1143690ffb4e1f735148a15f2036d9a3c1962fba188cAdam Powell } 1144690ffb4e1f735148a15f2036d9a3c1962fba188cAdam Powell 11459a1de308cea2d160778fd977825f10a07b49d738Adam Powell /** 11469a1de308cea2d160778fd977825f10a07b49d738Adam Powell * Return the number of {@link MediaRouter.RouteCategory categories} currently 11479a1de308cea2d160778fd977825f10a07b49d738Adam Powell * represented by routes known to this MediaRouter. 11489a1de308cea2d160778fd977825f10a07b49d738Adam Powell * 11499a1de308cea2d160778fd977825f10a07b49d738Adam Powell * @return the number of unique categories represented by this MediaRouter's known routes 11509a1de308cea2d160778fd977825f10a07b49d738Adam Powell */ 11519a1de308cea2d160778fd977825f10a07b49d738Adam Powell public int getCategoryCount() { 1152b58b8f832d06b0ffa8886eba5a4916578a3b8743Dianne Hackborn return sStatic.mCategories.size(); 11539a1de308cea2d160778fd977825f10a07b49d738Adam Powell } 11549a1de308cea2d160778fd977825f10a07b49d738Adam Powell 11559a1de308cea2d160778fd977825f10a07b49d738Adam Powell /** 11569a1de308cea2d160778fd977825f10a07b49d738Adam Powell * Return the {@link MediaRouter.RouteCategory category} at the given index. 11579a1de308cea2d160778fd977825f10a07b49d738Adam Powell * Valid indices are in the range [0-getCategoryCount). 11589a1de308cea2d160778fd977825f10a07b49d738Adam Powell * 11599a1de308cea2d160778fd977825f10a07b49d738Adam Powell * @param index which category to return 11609a1de308cea2d160778fd977825f10a07b49d738Adam Powell * @return the category at index 11619a1de308cea2d160778fd977825f10a07b49d738Adam Powell */ 11629a1de308cea2d160778fd977825f10a07b49d738Adam Powell public RouteCategory getCategoryAt(int index) { 1163b58b8f832d06b0ffa8886eba5a4916578a3b8743Dianne Hackborn return sStatic.mCategories.get(index); 11649a1de308cea2d160778fd977825f10a07b49d738Adam Powell } 11659a1de308cea2d160778fd977825f10a07b49d738Adam Powell 11669a1de308cea2d160778fd977825f10a07b49d738Adam Powell /** 11679a1de308cea2d160778fd977825f10a07b49d738Adam Powell * Return the number of {@link MediaRouter.RouteInfo routes} currently known 11689a1de308cea2d160778fd977825f10a07b49d738Adam Powell * to this MediaRouter. 11699a1de308cea2d160778fd977825f10a07b49d738Adam Powell * 11709a1de308cea2d160778fd977825f10a07b49d738Adam Powell * @return the number of routes tracked by this router 11719a1de308cea2d160778fd977825f10a07b49d738Adam Powell */ 11729a1de308cea2d160778fd977825f10a07b49d738Adam Powell public int getRouteCount() { 1173b58b8f832d06b0ffa8886eba5a4916578a3b8743Dianne Hackborn return sStatic.mRoutes.size(); 11749a1de308cea2d160778fd977825f10a07b49d738Adam Powell } 11759a1de308cea2d160778fd977825f10a07b49d738Adam Powell 11769a1de308cea2d160778fd977825f10a07b49d738Adam Powell /** 11779a1de308cea2d160778fd977825f10a07b49d738Adam Powell * Return the route at the specified index. 11789a1de308cea2d160778fd977825f10a07b49d738Adam Powell * 11799a1de308cea2d160778fd977825f10a07b49d738Adam Powell * @param index index of the route to return 11809a1de308cea2d160778fd977825f10a07b49d738Adam Powell * @return the route at index 11819a1de308cea2d160778fd977825f10a07b49d738Adam Powell */ 11829a1de308cea2d160778fd977825f10a07b49d738Adam Powell public RouteInfo getRouteAt(int index) { 1183b58b8f832d06b0ffa8886eba5a4916578a3b8743Dianne Hackborn return sStatic.mRoutes.get(index); 1184b58b8f832d06b0ffa8886eba5a4916578a3b8743Dianne Hackborn } 1185b58b8f832d06b0ffa8886eba5a4916578a3b8743Dianne Hackborn 1186b58b8f832d06b0ffa8886eba5a4916578a3b8743Dianne Hackborn static int getRouteCountStatic() { 1187b58b8f832d06b0ffa8886eba5a4916578a3b8743Dianne Hackborn return sStatic.mRoutes.size(); 1188b58b8f832d06b0ffa8886eba5a4916578a3b8743Dianne Hackborn } 1189b58b8f832d06b0ffa8886eba5a4916578a3b8743Dianne Hackborn 1190b58b8f832d06b0ffa8886eba5a4916578a3b8743Dianne Hackborn static RouteInfo getRouteAtStatic(int index) { 1191b58b8f832d06b0ffa8886eba5a4916578a3b8743Dianne Hackborn return sStatic.mRoutes.get(index); 11929a1de308cea2d160778fd977825f10a07b49d738Adam Powell } 11939a1de308cea2d160778fd977825f10a07b49d738Adam Powell 11949a1de308cea2d160778fd977825f10a07b49d738Adam Powell /** 11959a1de308cea2d160778fd977825f10a07b49d738Adam Powell * Create a new user route that may be modified and registered for use by the application. 11969a1de308cea2d160778fd977825f10a07b49d738Adam Powell * 11979a1de308cea2d160778fd977825f10a07b49d738Adam Powell * @param category The category the new route will belong to 11989a1de308cea2d160778fd977825f10a07b49d738Adam Powell * @return A new UserRouteInfo for use by the application 11999a1de308cea2d160778fd977825f10a07b49d738Adam Powell * 12009a1de308cea2d160778fd977825f10a07b49d738Adam Powell * @see #addUserRoute(UserRouteInfo) 12019a1de308cea2d160778fd977825f10a07b49d738Adam Powell * @see #removeUserRoute(UserRouteInfo) 120269b07161bebdb2c726e3a826c2268866f1a94517Jeff Brown * @see #createRouteCategory(CharSequence, boolean) 12039a1de308cea2d160778fd977825f10a07b49d738Adam Powell */ 12049a1de308cea2d160778fd977825f10a07b49d738Adam Powell public UserRouteInfo createUserRoute(RouteCategory category) { 12059a1de308cea2d160778fd977825f10a07b49d738Adam Powell return new UserRouteInfo(category); 12069a1de308cea2d160778fd977825f10a07b49d738Adam Powell } 12079a1de308cea2d160778fd977825f10a07b49d738Adam Powell 12089a1de308cea2d160778fd977825f10a07b49d738Adam Powell /** 12099a1de308cea2d160778fd977825f10a07b49d738Adam Powell * Create a new route category. Each route must belong to a category. 12109a1de308cea2d160778fd977825f10a07b49d738Adam Powell * 12119a1de308cea2d160778fd977825f10a07b49d738Adam Powell * @param name Name of the new category 12129a1de308cea2d160778fd977825f10a07b49d738Adam Powell * @param isGroupable true if routes in this category may be grouped with one another 12139a1de308cea2d160778fd977825f10a07b49d738Adam Powell * @return the new RouteCategory 12149a1de308cea2d160778fd977825f10a07b49d738Adam Powell */ 12159a1de308cea2d160778fd977825f10a07b49d738Adam Powell public RouteCategory createRouteCategory(CharSequence name, boolean isGroupable) { 12169a1de308cea2d160778fd977825f10a07b49d738Adam Powell return new RouteCategory(name, ROUTE_TYPE_USER, isGroupable); 12179a1de308cea2d160778fd977825f10a07b49d738Adam Powell } 12185d3114b64a88ac1f72becd8d46f148c666f64aa3RoboErik 12190d03c042f90bf62d5bad7c64e77028a5f9f8fae0Adam Powell /** 12200d03c042f90bf62d5bad7c64e77028a5f9f8fae0Adam Powell * Create a new route category. Each route must belong to a category. 12210d03c042f90bf62d5bad7c64e77028a5f9f8fae0Adam Powell * 12220d03c042f90bf62d5bad7c64e77028a5f9f8fae0Adam Powell * @param nameResId Resource ID of the name of the new category 12230d03c042f90bf62d5bad7c64e77028a5f9f8fae0Adam Powell * @param isGroupable true if routes in this category may be grouped with one another 12240d03c042f90bf62d5bad7c64e77028a5f9f8fae0Adam Powell * @return the new RouteCategory 12250d03c042f90bf62d5bad7c64e77028a5f9f8fae0Adam Powell */ 12260d03c042f90bf62d5bad7c64e77028a5f9f8fae0Adam Powell public RouteCategory createRouteCategory(int nameResId, boolean isGroupable) { 12270d03c042f90bf62d5bad7c64e77028a5f9f8fae0Adam Powell return new RouteCategory(nameResId, ROUTE_TYPE_USER, isGroupable); 12280d03c042f90bf62d5bad7c64e77028a5f9f8fae0Adam Powell } 12299a1de308cea2d160778fd977825f10a07b49d738Adam Powell 123069b07161bebdb2c726e3a826c2268866f1a94517Jeff Brown /** 123169b07161bebdb2c726e3a826c2268866f1a94517Jeff Brown * Rebinds the media router to handle routes that belong to the specified user. 123269b07161bebdb2c726e3a826c2268866f1a94517Jeff Brown * Requires the interact across users permission to access the routes of another user. 123369b07161bebdb2c726e3a826c2268866f1a94517Jeff Brown * <p> 123469b07161bebdb2c726e3a826c2268866f1a94517Jeff Brown * This method is a complete hack to work around the singleton nature of the 123569b07161bebdb2c726e3a826c2268866f1a94517Jeff Brown * media router when running inside of singleton processes like QuickSettings. 123669b07161bebdb2c726e3a826c2268866f1a94517Jeff Brown * This mechanism should be burned to the ground when MediaRouter is redesigned. 123769b07161bebdb2c726e3a826c2268866f1a94517Jeff Brown * Ideally the current user would be pulled from the Context but we need to break 123869b07161bebdb2c726e3a826c2268866f1a94517Jeff Brown * down MediaRouter.Static before we can get there. 123969b07161bebdb2c726e3a826c2268866f1a94517Jeff Brown * </p> 124069b07161bebdb2c726e3a826c2268866f1a94517Jeff Brown * 124169b07161bebdb2c726e3a826c2268866f1a94517Jeff Brown * @hide 124269b07161bebdb2c726e3a826c2268866f1a94517Jeff Brown */ 124369b07161bebdb2c726e3a826c2268866f1a94517Jeff Brown public void rebindAsUser(int userId) { 124469b07161bebdb2c726e3a826c2268866f1a94517Jeff Brown sStatic.rebindAsUser(userId); 124569b07161bebdb2c726e3a826c2268866f1a94517Jeff Brown } 124669b07161bebdb2c726e3a826c2268866f1a94517Jeff Brown 1247b58b8f832d06b0ffa8886eba5a4916578a3b8743Dianne Hackborn static void updateRoute(final RouteInfo info) { 12489a1de308cea2d160778fd977825f10a07b49d738Adam Powell dispatchRouteChanged(info); 12499a1de308cea2d160778fd977825f10a07b49d738Adam Powell } 12509a1de308cea2d160778fd977825f10a07b49d738Adam Powell 1251b58b8f832d06b0ffa8886eba5a4916578a3b8743Dianne Hackborn static void dispatchRouteSelected(int type, RouteInfo info) { 125239d5c6172503620ac3761148adac5fd7fa20d02dAdam Powell for (CallbackInfo cbi : sStatic.mCallbacks) { 125366f3b39ec452a8a03e3254e77e19ccb764ee931fJeff Brown if (cbi.filterRouteEvent(info)) { 1254b58b8f832d06b0ffa8886eba5a4916578a3b8743Dianne Hackborn cbi.cb.onRouteSelected(cbi.router, type, info); 12559a1de308cea2d160778fd977825f10a07b49d738Adam Powell } 12569a1de308cea2d160778fd977825f10a07b49d738Adam Powell } 12579a1de308cea2d160778fd977825f10a07b49d738Adam Powell } 12589a1de308cea2d160778fd977825f10a07b49d738Adam Powell 1259b58b8f832d06b0ffa8886eba5a4916578a3b8743Dianne Hackborn static void dispatchRouteUnselected(int type, RouteInfo info) { 126039d5c6172503620ac3761148adac5fd7fa20d02dAdam Powell for (CallbackInfo cbi : sStatic.mCallbacks) { 126166f3b39ec452a8a03e3254e77e19ccb764ee931fJeff Brown if (cbi.filterRouteEvent(info)) { 1262b58b8f832d06b0ffa8886eba5a4916578a3b8743Dianne Hackborn cbi.cb.onRouteUnselected(cbi.router, type, info); 12639a1de308cea2d160778fd977825f10a07b49d738Adam Powell } 12649a1de308cea2d160778fd977825f10a07b49d738Adam Powell } 12659a1de308cea2d160778fd977825f10a07b49d738Adam Powell } 12669a1de308cea2d160778fd977825f10a07b49d738Adam Powell 1267b58b8f832d06b0ffa8886eba5a4916578a3b8743Dianne Hackborn static void dispatchRouteChanged(RouteInfo info) { 1268af574183c274f51d04487a9c8355e9f34a1150f2Jeff Brown dispatchRouteChanged(info, info.mSupportedTypes); 1269af574183c274f51d04487a9c8355e9f34a1150f2Jeff Brown } 1270af574183c274f51d04487a9c8355e9f34a1150f2Jeff Brown 1271af574183c274f51d04487a9c8355e9f34a1150f2Jeff Brown static void dispatchRouteChanged(RouteInfo info, int oldSupportedTypes) { 12724bad663518908c20ed4f93a27f46ffb09448497fSungsoo Lim if (DEBUG) { 12734bad663518908c20ed4f93a27f46ffb09448497fSungsoo Lim Log.d(TAG, "Dispatching route change: " + info); 12744bad663518908c20ed4f93a27f46ffb09448497fSungsoo Lim } 1275af574183c274f51d04487a9c8355e9f34a1150f2Jeff Brown final int newSupportedTypes = info.mSupportedTypes; 127639d5c6172503620ac3761148adac5fd7fa20d02dAdam Powell for (CallbackInfo cbi : sStatic.mCallbacks) { 1277af574183c274f51d04487a9c8355e9f34a1150f2Jeff Brown // Reconstruct some of the history for callbacks that may not have observed 1278af574183c274f51d04487a9c8355e9f34a1150f2Jeff Brown // all of the events needed to correctly interpret the current state. 1279af574183c274f51d04487a9c8355e9f34a1150f2Jeff Brown // FIXME: This is a strong signal that we should deprecate route type filtering 1280af574183c274f51d04487a9c8355e9f34a1150f2Jeff Brown // completely in the future because it can lead to inconsistencies in 1281af574183c274f51d04487a9c8355e9f34a1150f2Jeff Brown // applications. 1282af574183c274f51d04487a9c8355e9f34a1150f2Jeff Brown final boolean oldVisibility = cbi.filterRouteEvent(oldSupportedTypes); 1283af574183c274f51d04487a9c8355e9f34a1150f2Jeff Brown final boolean newVisibility = cbi.filterRouteEvent(newSupportedTypes); 1284af574183c274f51d04487a9c8355e9f34a1150f2Jeff Brown if (!oldVisibility && newVisibility) { 1285af574183c274f51d04487a9c8355e9f34a1150f2Jeff Brown cbi.cb.onRouteAdded(cbi.router, info); 1286af574183c274f51d04487a9c8355e9f34a1150f2Jeff Brown if (info.isSelected()) { 1287af574183c274f51d04487a9c8355e9f34a1150f2Jeff Brown cbi.cb.onRouteSelected(cbi.router, newSupportedTypes, info); 1288af574183c274f51d04487a9c8355e9f34a1150f2Jeff Brown } 1289af574183c274f51d04487a9c8355e9f34a1150f2Jeff Brown } 1290af574183c274f51d04487a9c8355e9f34a1150f2Jeff Brown if (oldVisibility || newVisibility) { 1291b58b8f832d06b0ffa8886eba5a4916578a3b8743Dianne Hackborn cbi.cb.onRouteChanged(cbi.router, info); 12929a1de308cea2d160778fd977825f10a07b49d738Adam Powell } 1293af574183c274f51d04487a9c8355e9f34a1150f2Jeff Brown if (oldVisibility && !newVisibility) { 1294af574183c274f51d04487a9c8355e9f34a1150f2Jeff Brown if (info.isSelected()) { 1295af574183c274f51d04487a9c8355e9f34a1150f2Jeff Brown cbi.cb.onRouteUnselected(cbi.router, oldSupportedTypes, info); 1296af574183c274f51d04487a9c8355e9f34a1150f2Jeff Brown } 1297af574183c274f51d04487a9c8355e9f34a1150f2Jeff Brown cbi.cb.onRouteRemoved(cbi.router, info); 1298af574183c274f51d04487a9c8355e9f34a1150f2Jeff Brown } 12999a1de308cea2d160778fd977825f10a07b49d738Adam Powell } 13009a1de308cea2d160778fd977825f10a07b49d738Adam Powell } 13019a1de308cea2d160778fd977825f10a07b49d738Adam Powell 1302b58b8f832d06b0ffa8886eba5a4916578a3b8743Dianne Hackborn static void dispatchRouteAdded(RouteInfo info) { 130339d5c6172503620ac3761148adac5fd7fa20d02dAdam Powell for (CallbackInfo cbi : sStatic.mCallbacks) { 130466f3b39ec452a8a03e3254e77e19ccb764ee931fJeff Brown if (cbi.filterRouteEvent(info)) { 1305b58b8f832d06b0ffa8886eba5a4916578a3b8743Dianne Hackborn cbi.cb.onRouteAdded(cbi.router, info); 13069a1de308cea2d160778fd977825f10a07b49d738Adam Powell } 13079a1de308cea2d160778fd977825f10a07b49d738Adam Powell } 13089a1de308cea2d160778fd977825f10a07b49d738Adam Powell } 13099a1de308cea2d160778fd977825f10a07b49d738Adam Powell 1310b58b8f832d06b0ffa8886eba5a4916578a3b8743Dianne Hackborn static void dispatchRouteRemoved(RouteInfo info) { 131139d5c6172503620ac3761148adac5fd7fa20d02dAdam Powell for (CallbackInfo cbi : sStatic.mCallbacks) { 131266f3b39ec452a8a03e3254e77e19ccb764ee931fJeff Brown if (cbi.filterRouteEvent(info)) { 1313b58b8f832d06b0ffa8886eba5a4916578a3b8743Dianne Hackborn cbi.cb.onRouteRemoved(cbi.router, info); 13149a1de308cea2d160778fd977825f10a07b49d738Adam Powell } 13159a1de308cea2d160778fd977825f10a07b49d738Adam Powell } 13169a1de308cea2d160778fd977825f10a07b49d738Adam Powell } 13179a1de308cea2d160778fd977825f10a07b49d738Adam Powell 1318b58b8f832d06b0ffa8886eba5a4916578a3b8743Dianne Hackborn static void dispatchRouteGrouped(RouteInfo info, RouteGroup group, int index) { 131939d5c6172503620ac3761148adac5fd7fa20d02dAdam Powell for (CallbackInfo cbi : sStatic.mCallbacks) { 132066f3b39ec452a8a03e3254e77e19ccb764ee931fJeff Brown if (cbi.filterRouteEvent(group)) { 1321b58b8f832d06b0ffa8886eba5a4916578a3b8743Dianne Hackborn cbi.cb.onRouteGrouped(cbi.router, info, group, index); 1322d0d2cda9d414da73773285d7fee9e13aef3495e9Adam Powell } 1323d0d2cda9d414da73773285d7fee9e13aef3495e9Adam Powell } 1324d0d2cda9d414da73773285d7fee9e13aef3495e9Adam Powell } 1325d0d2cda9d414da73773285d7fee9e13aef3495e9Adam Powell 1326b58b8f832d06b0ffa8886eba5a4916578a3b8743Dianne Hackborn static void dispatchRouteUngrouped(RouteInfo info, RouteGroup group) { 132739d5c6172503620ac3761148adac5fd7fa20d02dAdam Powell for (CallbackInfo cbi : sStatic.mCallbacks) { 132866f3b39ec452a8a03e3254e77e19ccb764ee931fJeff Brown if (cbi.filterRouteEvent(group)) { 1329b58b8f832d06b0ffa8886eba5a4916578a3b8743Dianne Hackborn cbi.cb.onRouteUngrouped(cbi.router, info, group); 13309a1de308cea2d160778fd977825f10a07b49d738Adam Powell } 13319a1de308cea2d160778fd977825f10a07b49d738Adam Powell } 13329a1de308cea2d160778fd977825f10a07b49d738Adam Powell } 13339a1de308cea2d160778fd977825f10a07b49d738Adam Powell 13348e37a85bf3dc39519942698dc90a3951306b934bAdam Powell static void dispatchRouteVolumeChanged(RouteInfo info) { 13358e37a85bf3dc39519942698dc90a3951306b934bAdam Powell for (CallbackInfo cbi : sStatic.mCallbacks) { 133666f3b39ec452a8a03e3254e77e19ccb764ee931fJeff Brown if (cbi.filterRouteEvent(info)) { 13378e37a85bf3dc39519942698dc90a3951306b934bAdam Powell cbi.cb.onRouteVolumeChanged(cbi.router, info); 13388e37a85bf3dc39519942698dc90a3951306b934bAdam Powell } 13398e37a85bf3dc39519942698dc90a3951306b934bAdam Powell } 13408e37a85bf3dc39519942698dc90a3951306b934bAdam Powell } 13418e37a85bf3dc39519942698dc90a3951306b934bAdam Powell 134292130f6407dc51c58b3b941d28a6daf4e04b8d62Jeff Brown static void dispatchRoutePresentationDisplayChanged(RouteInfo info) { 134392130f6407dc51c58b3b941d28a6daf4e04b8d62Jeff Brown for (CallbackInfo cbi : sStatic.mCallbacks) { 134466f3b39ec452a8a03e3254e77e19ccb764ee931fJeff Brown if (cbi.filterRouteEvent(info)) { 134592130f6407dc51c58b3b941d28a6daf4e04b8d62Jeff Brown cbi.cb.onRoutePresentationDisplayChanged(cbi.router, info); 134692130f6407dc51c58b3b941d28a6daf4e04b8d62Jeff Brown } 134792130f6407dc51c58b3b941d28a6daf4e04b8d62Jeff Brown } 134892130f6407dc51c58b3b941d28a6daf4e04b8d62Jeff Brown } 134992130f6407dc51c58b3b941d28a6daf4e04b8d62Jeff Brown 13508e37a85bf3dc39519942698dc90a3951306b934bAdam Powell static void systemVolumeChanged(int newValue) { 13518e37a85bf3dc39519942698dc90a3951306b934bAdam Powell final RouteInfo selectedRoute = sStatic.mSelectedRoute; 13528e37a85bf3dc39519942698dc90a3951306b934bAdam Powell if (selectedRoute == null) return; 13538e37a85bf3dc39519942698dc90a3951306b934bAdam Powell 13548e37a85bf3dc39519942698dc90a3951306b934bAdam Powell if (selectedRoute == sStatic.mBluetoothA2dpRoute || 1355705ab808cf023e0cc38c2ba7cdb9571942cdc04fAdam Powell selectedRoute == sStatic.mDefaultAudioVideo) { 13568e37a85bf3dc39519942698dc90a3951306b934bAdam Powell dispatchRouteVolumeChanged(selectedRoute); 13578e37a85bf3dc39519942698dc90a3951306b934bAdam Powell } else if (sStatic.mBluetoothA2dpRoute != null) { 1358fa6f70e225a7370c1ecb21ee7076c9086ecb9e9aSungsoo try { 1359fa6f70e225a7370c1ecb21ee7076c9086ecb9e9aSungsoo dispatchRouteVolumeChanged(sStatic.mAudioService.isBluetoothA2dpOn() ? 1360fa6f70e225a7370c1ecb21ee7076c9086ecb9e9aSungsoo sStatic.mBluetoothA2dpRoute : sStatic.mDefaultAudioVideo); 1361fa6f70e225a7370c1ecb21ee7076c9086ecb9e9aSungsoo } catch (RemoteException e) { 1362fa6f70e225a7370c1ecb21ee7076c9086ecb9e9aSungsoo Log.e(TAG, "Error checking Bluetooth A2DP state to report volume change", e); 1363fa6f70e225a7370c1ecb21ee7076c9086ecb9e9aSungsoo } 13648e37a85bf3dc39519942698dc90a3951306b934bAdam Powell } else { 1365705ab808cf023e0cc38c2ba7cdb9571942cdc04fAdam Powell dispatchRouteVolumeChanged(sStatic.mDefaultAudioVideo); 1366705ab808cf023e0cc38c2ba7cdb9571942cdc04fAdam Powell } 1367705ab808cf023e0cc38c2ba7cdb9571942cdc04fAdam Powell } 1368705ab808cf023e0cc38c2ba7cdb9571942cdc04fAdam Powell 136975af171006ee80aaf7cfb56ded1378afe0084ccaJeff Brown static void updateWifiDisplayStatus(WifiDisplayStatus status) { 137075af171006ee80aaf7cfb56ded1378afe0084ccaJeff Brown WifiDisplay[] displays; 1371615e413a90ff5898d8c458ebc9649ca95fa9fd98Adam Powell WifiDisplay activeDisplay; 137275af171006ee80aaf7cfb56ded1378afe0084ccaJeff Brown if (status.getFeatureState() == WifiDisplayStatus.FEATURE_STATE_ON) { 137375af171006ee80aaf7cfb56ded1378afe0084ccaJeff Brown displays = status.getDisplays(); 137475af171006ee80aaf7cfb56ded1378afe0084ccaJeff Brown activeDisplay = status.getActiveDisplay(); 1375af574183c274f51d04487a9c8355e9f34a1150f2Jeff Brown 1376af574183c274f51d04487a9c8355e9f34a1150f2Jeff Brown // Only the system is able to connect to wifi display routes. 1377af574183c274f51d04487a9c8355e9f34a1150f2Jeff Brown // The display manager will enforce this with a permission check but it 1378af574183c274f51d04487a9c8355e9f34a1150f2Jeff Brown // still publishes information about all available displays. 1379af574183c274f51d04487a9c8355e9f34a1150f2Jeff Brown // Filter the list down to just the active display. 1380af574183c274f51d04487a9c8355e9f34a1150f2Jeff Brown if (!sStatic.mCanConfigureWifiDisplays) { 1381af574183c274f51d04487a9c8355e9f34a1150f2Jeff Brown if (activeDisplay != null) { 1382af574183c274f51d04487a9c8355e9f34a1150f2Jeff Brown displays = new WifiDisplay[] { activeDisplay }; 1383af574183c274f51d04487a9c8355e9f34a1150f2Jeff Brown } else { 1384af574183c274f51d04487a9c8355e9f34a1150f2Jeff Brown displays = WifiDisplay.EMPTY_ARRAY; 1385af574183c274f51d04487a9c8355e9f34a1150f2Jeff Brown } 1386af574183c274f51d04487a9c8355e9f34a1150f2Jeff Brown } 1387615e413a90ff5898d8c458ebc9649ca95fa9fd98Adam Powell } else { 138875af171006ee80aaf7cfb56ded1378afe0084ccaJeff Brown displays = WifiDisplay.EMPTY_ARRAY; 1389615e413a90ff5898d8c458ebc9649ca95fa9fd98Adam Powell activeDisplay = null; 1390615e413a90ff5898d8c458ebc9649ca95fa9fd98Adam Powell } 1391ce468a35b388ca46578934706b38dbae94941643Jeff Brown String activeDisplayAddress = activeDisplay != null ? 1392ce468a35b388ca46578934706b38dbae94941643Jeff Brown activeDisplay.getDeviceAddress() : null; 1393705ab808cf023e0cc38c2ba7cdb9571942cdc04fAdam Powell 139475af171006ee80aaf7cfb56ded1378afe0084ccaJeff Brown // Add or update routes. 139575af171006ee80aaf7cfb56ded1378afe0084ccaJeff Brown for (int i = 0; i < displays.length; i++) { 139675af171006ee80aaf7cfb56ded1378afe0084ccaJeff Brown final WifiDisplay d = displays[i]; 139775af171006ee80aaf7cfb56ded1378afe0084ccaJeff Brown if (shouldShowWifiDisplay(d, activeDisplay)) { 1398ab87a63997a7dc771acfd0dcd7efda990dc3d5feChong Zhang RouteInfo route = findWifiDisplayRoute(d); 1399ab87a63997a7dc771acfd0dcd7efda990dc3d5feChong Zhang if (route == null) { 140075af171006ee80aaf7cfb56ded1378afe0084ccaJeff Brown route = makeWifiDisplayRoute(d, status); 1401ab87a63997a7dc771acfd0dcd7efda990dc3d5feChong Zhang addRouteStatic(route); 1402ab87a63997a7dc771acfd0dcd7efda990dc3d5feChong Zhang } else { 1403ce468a35b388ca46578934706b38dbae94941643Jeff Brown String address = d.getDeviceAddress(); 1404ce468a35b388ca46578934706b38dbae94941643Jeff Brown boolean disconnected = !address.equals(activeDisplayAddress) 1405ce468a35b388ca46578934706b38dbae94941643Jeff Brown && address.equals(sStatic.mPreviousActiveWifiDisplayAddress); 1406ce468a35b388ca46578934706b38dbae94941643Jeff Brown updateWifiDisplayRoute(route, d, status, disconnected); 1407ab87a63997a7dc771acfd0dcd7efda990dc3d5feChong Zhang } 1408ab87a63997a7dc771acfd0dcd7efda990dc3d5feChong Zhang if (d.equals(activeDisplay)) { 140969b07161bebdb2c726e3a826c2268866f1a94517Jeff Brown selectRouteStatic(route.getSupportedTypes(), route, false); 1410ab87a63997a7dc771acfd0dcd7efda990dc3d5feChong Zhang } 14112ee6a2a83262d05a566bd713d238e89edfd33a29Adam Powell } 1412705ab808cf023e0cc38c2ba7cdb9571942cdc04fAdam Powell } 141375af171006ee80aaf7cfb56ded1378afe0084ccaJeff Brown 141475af171006ee80aaf7cfb56ded1378afe0084ccaJeff Brown // Remove stale routes. 141575af171006ee80aaf7cfb56ded1378afe0084ccaJeff Brown for (int i = sStatic.mRoutes.size(); i-- > 0; ) { 141675af171006ee80aaf7cfb56ded1378afe0084ccaJeff Brown RouteInfo route = sStatic.mRoutes.get(i); 141775af171006ee80aaf7cfb56ded1378afe0084ccaJeff Brown if (route.mDeviceAddress != null) { 141875af171006ee80aaf7cfb56ded1378afe0084ccaJeff Brown WifiDisplay d = findWifiDisplay(displays, route.mDeviceAddress); 141975af171006ee80aaf7cfb56ded1378afe0084ccaJeff Brown if (d == null || !shouldShowWifiDisplay(d, activeDisplay)) { 142075af171006ee80aaf7cfb56ded1378afe0084ccaJeff Brown removeRouteStatic(route); 1421ab87a63997a7dc771acfd0dcd7efda990dc3d5feChong Zhang } 1422705ab808cf023e0cc38c2ba7cdb9571942cdc04fAdam Powell } 1423705ab808cf023e0cc38c2ba7cdb9571942cdc04fAdam Powell } 1424ce468a35b388ca46578934706b38dbae94941643Jeff Brown 1425ce468a35b388ca46578934706b38dbae94941643Jeff Brown // Remember the current active wifi display address so that we can infer disconnections. 1426ce468a35b388ca46578934706b38dbae94941643Jeff Brown // TODO: This hack will go away once all of this is moved into the media router service. 1427ce468a35b388ca46578934706b38dbae94941643Jeff Brown sStatic.mPreviousActiveWifiDisplayAddress = activeDisplayAddress; 142875af171006ee80aaf7cfb56ded1378afe0084ccaJeff Brown } 1429705ab808cf023e0cc38c2ba7cdb9571942cdc04fAdam Powell 143075af171006ee80aaf7cfb56ded1378afe0084ccaJeff Brown private static boolean shouldShowWifiDisplay(WifiDisplay d, WifiDisplay activeDisplay) { 143175af171006ee80aaf7cfb56ded1378afe0084ccaJeff Brown return d.isRemembered() || d.equals(activeDisplay); 1432705ab808cf023e0cc38c2ba7cdb9571942cdc04fAdam Powell } 1433705ab808cf023e0cc38c2ba7cdb9571942cdc04fAdam Powell 1434ab87a63997a7dc771acfd0dcd7efda990dc3d5feChong Zhang static int getWifiDisplayStatusCode(WifiDisplay d, WifiDisplayStatus wfdStatus) { 143569b07161bebdb2c726e3a826c2268866f1a94517Jeff Brown int newStatus; 1436ab87a63997a7dc771acfd0dcd7efda990dc3d5feChong Zhang if (wfdStatus.getScanState() == WifiDisplayStatus.SCAN_STATE_SCANNING) { 1437ab87a63997a7dc771acfd0dcd7efda990dc3d5feChong Zhang newStatus = RouteInfo.STATUS_SCANNING; 1438ab87a63997a7dc771acfd0dcd7efda990dc3d5feChong Zhang } else if (d.isAvailable()) { 1439ab87a63997a7dc771acfd0dcd7efda990dc3d5feChong Zhang newStatus = d.canConnect() ? 1440ab87a63997a7dc771acfd0dcd7efda990dc3d5feChong Zhang RouteInfo.STATUS_AVAILABLE: RouteInfo.STATUS_IN_USE; 1441705ab808cf023e0cc38c2ba7cdb9571942cdc04fAdam Powell } else { 1442705ab808cf023e0cc38c2ba7cdb9571942cdc04fAdam Powell newStatus = RouteInfo.STATUS_NOT_AVAILABLE; 1443705ab808cf023e0cc38c2ba7cdb9571942cdc04fAdam Powell } 1444705ab808cf023e0cc38c2ba7cdb9571942cdc04fAdam Powell 1445ab87a63997a7dc771acfd0dcd7efda990dc3d5feChong Zhang if (d.equals(wfdStatus.getActiveDisplay())) { 1446ab87a63997a7dc771acfd0dcd7efda990dc3d5feChong Zhang final int activeState = wfdStatus.getActiveDisplayState(); 1447705ab808cf023e0cc38c2ba7cdb9571942cdc04fAdam Powell switch (activeState) { 1448705ab808cf023e0cc38c2ba7cdb9571942cdc04fAdam Powell case WifiDisplayStatus.DISPLAY_STATE_CONNECTED: 144969b07161bebdb2c726e3a826c2268866f1a94517Jeff Brown newStatus = RouteInfo.STATUS_CONNECTED; 1450705ab808cf023e0cc38c2ba7cdb9571942cdc04fAdam Powell break; 1451705ab808cf023e0cc38c2ba7cdb9571942cdc04fAdam Powell case WifiDisplayStatus.DISPLAY_STATE_CONNECTING: 1452705ab808cf023e0cc38c2ba7cdb9571942cdc04fAdam Powell newStatus = RouteInfo.STATUS_CONNECTING; 1453705ab808cf023e0cc38c2ba7cdb9571942cdc04fAdam Powell break; 1454705ab808cf023e0cc38c2ba7cdb9571942cdc04fAdam Powell case WifiDisplayStatus.DISPLAY_STATE_NOT_CONNECTED: 1455705ab808cf023e0cc38c2ba7cdb9571942cdc04fAdam Powell Log.e(TAG, "Active display is not connected!"); 1456705ab808cf023e0cc38c2ba7cdb9571942cdc04fAdam Powell break; 1457705ab808cf023e0cc38c2ba7cdb9571942cdc04fAdam Powell } 1458705ab808cf023e0cc38c2ba7cdb9571942cdc04fAdam Powell } 1459705ab808cf023e0cc38c2ba7cdb9571942cdc04fAdam Powell 1460ab87a63997a7dc771acfd0dcd7efda990dc3d5feChong Zhang return newStatus; 1461ab87a63997a7dc771acfd0dcd7efda990dc3d5feChong Zhang } 1462ab87a63997a7dc771acfd0dcd7efda990dc3d5feChong Zhang 1463ab87a63997a7dc771acfd0dcd7efda990dc3d5feChong Zhang static boolean isWifiDisplayEnabled(WifiDisplay d, WifiDisplayStatus wfdStatus) { 1464ab87a63997a7dc771acfd0dcd7efda990dc3d5feChong Zhang return d.isAvailable() && (d.canConnect() || d.equals(wfdStatus.getActiveDisplay())); 1465ab87a63997a7dc771acfd0dcd7efda990dc3d5feChong Zhang } 1466ab87a63997a7dc771acfd0dcd7efda990dc3d5feChong Zhang 1467ab87a63997a7dc771acfd0dcd7efda990dc3d5feChong Zhang static RouteInfo makeWifiDisplayRoute(WifiDisplay display, WifiDisplayStatus wfdStatus) { 1468ab87a63997a7dc771acfd0dcd7efda990dc3d5feChong Zhang final RouteInfo newRoute = new RouteInfo(sStatic.mSystemCategory); 1469ab87a63997a7dc771acfd0dcd7efda990dc3d5feChong Zhang newRoute.mDeviceAddress = display.getDeviceAddress(); 147069b07161bebdb2c726e3a826c2268866f1a94517Jeff Brown newRoute.mSupportedTypes = ROUTE_TYPE_LIVE_AUDIO | ROUTE_TYPE_LIVE_VIDEO 147169b07161bebdb2c726e3a826c2268866f1a94517Jeff Brown | ROUTE_TYPE_REMOTE_DISPLAY; 1472ab87a63997a7dc771acfd0dcd7efda990dc3d5feChong Zhang newRoute.mVolumeHandling = RouteInfo.PLAYBACK_VOLUME_FIXED; 1473ab87a63997a7dc771acfd0dcd7efda990dc3d5feChong Zhang newRoute.mPlaybackType = RouteInfo.PLAYBACK_TYPE_REMOTE; 1474ab87a63997a7dc771acfd0dcd7efda990dc3d5feChong Zhang 147539ad0e559896b45185429ea17cd12f18f7ae842cJeff Brown newRoute.setRealStatusCode(getWifiDisplayStatusCode(display, wfdStatus)); 1476ab87a63997a7dc771acfd0dcd7efda990dc3d5feChong Zhang newRoute.mEnabled = isWifiDisplayEnabled(display, wfdStatus); 1477ab87a63997a7dc771acfd0dcd7efda990dc3d5feChong Zhang newRoute.mName = display.getFriendlyDisplayName(); 1478ab87a63997a7dc771acfd0dcd7efda990dc3d5feChong Zhang newRoute.mDescription = sStatic.mResources.getText( 1479ab87a63997a7dc771acfd0dcd7efda990dc3d5feChong Zhang com.android.internal.R.string.wireless_display_route_description); 14805830b0b33618940d65197cec99d697b21908fec8Chong Zhang newRoute.updatePresentationDisplay(); 14819dd66417ea984e4ff809ecdba78e3f55a8a17467Insun Kang newRoute.mDeviceType = RouteInfo.DEVICE_TYPE_TV; 1482ab87a63997a7dc771acfd0dcd7efda990dc3d5feChong Zhang return newRoute; 1483ab87a63997a7dc771acfd0dcd7efda990dc3d5feChong Zhang } 1484ab87a63997a7dc771acfd0dcd7efda990dc3d5feChong Zhang 1485ab87a63997a7dc771acfd0dcd7efda990dc3d5feChong Zhang private static void updateWifiDisplayRoute( 1486ce468a35b388ca46578934706b38dbae94941643Jeff Brown RouteInfo route, WifiDisplay display, WifiDisplayStatus wfdStatus, 1487ce468a35b388ca46578934706b38dbae94941643Jeff Brown boolean disconnected) { 1488ab87a63997a7dc771acfd0dcd7efda990dc3d5feChong Zhang boolean changed = false; 14892444ae7e2b8658a4a90f996e678423558744b4a2Jeff Brown final String newName = display.getFriendlyDisplayName(); 14902444ae7e2b8658a4a90f996e678423558744b4a2Jeff Brown if (!route.getName().equals(newName)) { 1491705ab808cf023e0cc38c2ba7cdb9571942cdc04fAdam Powell route.mName = newName; 1492705ab808cf023e0cc38c2ba7cdb9571942cdc04fAdam Powell changed = true; 1493705ab808cf023e0cc38c2ba7cdb9571942cdc04fAdam Powell } 1494705ab808cf023e0cc38c2ba7cdb9571942cdc04fAdam Powell 1495ab87a63997a7dc771acfd0dcd7efda990dc3d5feChong Zhang boolean enabled = isWifiDisplayEnabled(display, wfdStatus); 1496ab87a63997a7dc771acfd0dcd7efda990dc3d5feChong Zhang changed |= route.mEnabled != enabled; 1497ab87a63997a7dc771acfd0dcd7efda990dc3d5feChong Zhang route.mEnabled = enabled; 1498705ab808cf023e0cc38c2ba7cdb9571942cdc04fAdam Powell 149939ad0e559896b45185429ea17cd12f18f7ae842cJeff Brown changed |= route.setRealStatusCode(getWifiDisplayStatusCode(display, wfdStatus)); 1500705ab808cf023e0cc38c2ba7cdb9571942cdc04fAdam Powell 1501705ab808cf023e0cc38c2ba7cdb9571942cdc04fAdam Powell if (changed) { 1502705ab808cf023e0cc38c2ba7cdb9571942cdc04fAdam Powell dispatchRouteChanged(route); 1503705ab808cf023e0cc38c2ba7cdb9571942cdc04fAdam Powell } 1504705ab808cf023e0cc38c2ba7cdb9571942cdc04fAdam Powell 1505ce468a35b388ca46578934706b38dbae94941643Jeff Brown if ((!enabled || disconnected) && route.isSelected()) { 1506705ab808cf023e0cc38c2ba7cdb9571942cdc04fAdam Powell // Oops, no longer available. Reselect the default. 150769b07161bebdb2c726e3a826c2268866f1a94517Jeff Brown selectDefaultRouteStatic(); 1508705ab808cf023e0cc38c2ba7cdb9571942cdc04fAdam Powell } 1509705ab808cf023e0cc38c2ba7cdb9571942cdc04fAdam Powell } 1510705ab808cf023e0cc38c2ba7cdb9571942cdc04fAdam Powell 151175af171006ee80aaf7cfb56ded1378afe0084ccaJeff Brown private static WifiDisplay findWifiDisplay(WifiDisplay[] displays, String deviceAddress) { 1512705ab808cf023e0cc38c2ba7cdb9571942cdc04fAdam Powell for (int i = 0; i < displays.length; i++) { 151375af171006ee80aaf7cfb56ded1378afe0084ccaJeff Brown final WifiDisplay d = displays[i]; 151475af171006ee80aaf7cfb56ded1378afe0084ccaJeff Brown if (d.getDeviceAddress().equals(deviceAddress)) { 151575af171006ee80aaf7cfb56ded1378afe0084ccaJeff Brown return d; 1516705ab808cf023e0cc38c2ba7cdb9571942cdc04fAdam Powell } 1517705ab808cf023e0cc38c2ba7cdb9571942cdc04fAdam Powell } 1518705ab808cf023e0cc38c2ba7cdb9571942cdc04fAdam Powell return null; 1519705ab808cf023e0cc38c2ba7cdb9571942cdc04fAdam Powell } 1520705ab808cf023e0cc38c2ba7cdb9571942cdc04fAdam Powell 1521705ab808cf023e0cc38c2ba7cdb9571942cdc04fAdam Powell private static RouteInfo findWifiDisplayRoute(WifiDisplay d) { 1522705ab808cf023e0cc38c2ba7cdb9571942cdc04fAdam Powell final int count = sStatic.mRoutes.size(); 1523705ab808cf023e0cc38c2ba7cdb9571942cdc04fAdam Powell for (int i = 0; i < count; i++) { 1524705ab808cf023e0cc38c2ba7cdb9571942cdc04fAdam Powell final RouteInfo info = sStatic.mRoutes.get(i); 1525705ab808cf023e0cc38c2ba7cdb9571942cdc04fAdam Powell if (d.getDeviceAddress().equals(info.mDeviceAddress)) { 1526705ab808cf023e0cc38c2ba7cdb9571942cdc04fAdam Powell return info; 1527705ab808cf023e0cc38c2ba7cdb9571942cdc04fAdam Powell } 1528705ab808cf023e0cc38c2ba7cdb9571942cdc04fAdam Powell } 1529705ab808cf023e0cc38c2ba7cdb9571942cdc04fAdam Powell return null; 15308e37a85bf3dc39519942698dc90a3951306b934bAdam Powell } 15318e37a85bf3dc39519942698dc90a3951306b934bAdam Powell 15329a1de308cea2d160778fd977825f10a07b49d738Adam Powell /** 15339a1de308cea2d160778fd977825f10a07b49d738Adam Powell * Information about a media route. 15349a1de308cea2d160778fd977825f10a07b49d738Adam Powell */ 1535b58b8f832d06b0ffa8886eba5a4916578a3b8743Dianne Hackborn public static class RouteInfo { 15369a1de308cea2d160778fd977825f10a07b49d738Adam Powell CharSequence mName; 15370d03c042f90bf62d5bad7c64e77028a5f9f8fae0Adam Powell int mNameResId; 153856d4b744b374af11f540567d2b43d66d8d7c301eJeff Brown CharSequence mDescription; 15399a1de308cea2d160778fd977825f10a07b49d738Adam Powell private CharSequence mStatus; 15409a1de308cea2d160778fd977825f10a07b49d738Adam Powell int mSupportedTypes; 15419dd66417ea984e4ff809ecdba78e3f55a8a17467Insun Kang int mDeviceType; 15429a1de308cea2d160778fd977825f10a07b49d738Adam Powell RouteGroup mGroup; 15439a1de308cea2d160778fd977825f10a07b49d738Adam Powell final RouteCategory mCategory; 1544ae20ae1a8aaa013813c356ae1d9541ca7ff020aeAdam Powell Drawable mIcon; 15451357012968f9066ea3051d83995e9bac69526c3cJean-Michel Trivi // playback information 15461357012968f9066ea3051d83995e9bac69526c3cJean-Michel Trivi int mPlaybackType = PLAYBACK_TYPE_LOCAL; 15471357012968f9066ea3051d83995e9bac69526c3cJean-Michel Trivi int mVolumeMax = RemoteControlClient.DEFAULT_PLAYBACK_VOLUME; 15481357012968f9066ea3051d83995e9bac69526c3cJean-Michel Trivi int mVolume = RemoteControlClient.DEFAULT_PLAYBACK_VOLUME; 15491357012968f9066ea3051d83995e9bac69526c3cJean-Michel Trivi int mVolumeHandling = RemoteControlClient.DEFAULT_PLAYBACK_VOLUME_HANDLING; 15501357012968f9066ea3051d83995e9bac69526c3cJean-Michel Trivi int mPlaybackStream = AudioManager.STREAM_MUSIC; 15511357012968f9066ea3051d83995e9bac69526c3cJean-Michel Trivi VolumeCallbackInfo mVcb; 155292130f6407dc51c58b3b941d28a6daf4e04b8d62Jeff Brown Display mPresentationDisplay; 15535830b0b33618940d65197cec99d697b21908fec8Chong Zhang int mPresentationDisplayId = -1; 15549a1de308cea2d160778fd977825f10a07b49d738Adam Powell 1555705ab808cf023e0cc38c2ba7cdb9571942cdc04fAdam Powell String mDeviceAddress; 1556705ab808cf023e0cc38c2ba7cdb9571942cdc04fAdam Powell boolean mEnabled = true; 1557705ab808cf023e0cc38c2ba7cdb9571942cdc04fAdam Powell 155869b07161bebdb2c726e3a826c2268866f1a94517Jeff Brown // An id by which the route is known to the media router service. 155969b07161bebdb2c726e3a826c2268866f1a94517Jeff Brown // Null if this route only exists as an artifact within this process. 156069b07161bebdb2c726e3a826c2268866f1a94517Jeff Brown String mGlobalRouteId; 156169b07161bebdb2c726e3a826c2268866f1a94517Jeff Brown 1562705ab808cf023e0cc38c2ba7cdb9571942cdc04fAdam Powell // A predetermined connection status that can override mStatus 156339ad0e559896b45185429ea17cd12f18f7ae842cJeff Brown private int mRealStatusCode; 156439ad0e559896b45185429ea17cd12f18f7ae842cJeff Brown private int mResolvedStatusCode; 1565705ab808cf023e0cc38c2ba7cdb9571942cdc04fAdam Powell 15662ee6a2a83262d05a566bd713d238e89edfd33a29Adam Powell /** @hide */ public static final int STATUS_NONE = 0; 15672ee6a2a83262d05a566bd713d238e89edfd33a29Adam Powell /** @hide */ public static final int STATUS_SCANNING = 1; 15682ee6a2a83262d05a566bd713d238e89edfd33a29Adam Powell /** @hide */ public static final int STATUS_CONNECTING = 2; 15692ee6a2a83262d05a566bd713d238e89edfd33a29Adam Powell /** @hide */ public static final int STATUS_AVAILABLE = 3; 15702ee6a2a83262d05a566bd713d238e89edfd33a29Adam Powell /** @hide */ public static final int STATUS_NOT_AVAILABLE = 4; 1571ab87a63997a7dc771acfd0dcd7efda990dc3d5feChong Zhang /** @hide */ public static final int STATUS_IN_USE = 5; 157269b07161bebdb2c726e3a826c2268866f1a94517Jeff Brown /** @hide */ public static final int STATUS_CONNECTED = 6; 1573705ab808cf023e0cc38c2ba7cdb9571942cdc04fAdam Powell 15749dd66417ea984e4ff809ecdba78e3f55a8a17467Insun Kang /** @hide */ 15759dd66417ea984e4ff809ecdba78e3f55a8a17467Insun Kang @IntDef({DEVICE_TYPE_UNKNOWN, DEVICE_TYPE_TV, DEVICE_TYPE_SPEAKER, DEVICE_TYPE_BLUETOOTH}) 15769dd66417ea984e4ff809ecdba78e3f55a8a17467Insun Kang @Retention(RetentionPolicy.SOURCE) 15779dd66417ea984e4ff809ecdba78e3f55a8a17467Insun Kang public @interface DeviceType {} 15789dd66417ea984e4ff809ecdba78e3f55a8a17467Insun Kang 15799dd66417ea984e4ff809ecdba78e3f55a8a17467Insun Kang /** 15809dd66417ea984e4ff809ecdba78e3f55a8a17467Insun Kang * The default receiver device type of the route indicating the type is unknown. 15819dd66417ea984e4ff809ecdba78e3f55a8a17467Insun Kang * 15829dd66417ea984e4ff809ecdba78e3f55a8a17467Insun Kang * @see #getDeviceType 15839dd66417ea984e4ff809ecdba78e3f55a8a17467Insun Kang */ 15849dd66417ea984e4ff809ecdba78e3f55a8a17467Insun Kang public static final int DEVICE_TYPE_UNKNOWN = 0; 15859dd66417ea984e4ff809ecdba78e3f55a8a17467Insun Kang 15869dd66417ea984e4ff809ecdba78e3f55a8a17467Insun Kang /** 15879dd66417ea984e4ff809ecdba78e3f55a8a17467Insun Kang * A receiver device type of the route indicating the presentation of the media is happening 15889dd66417ea984e4ff809ecdba78e3f55a8a17467Insun Kang * on a TV. 15899dd66417ea984e4ff809ecdba78e3f55a8a17467Insun Kang * 15909dd66417ea984e4ff809ecdba78e3f55a8a17467Insun Kang * @see #getDeviceType 15919dd66417ea984e4ff809ecdba78e3f55a8a17467Insun Kang */ 15929dd66417ea984e4ff809ecdba78e3f55a8a17467Insun Kang public static final int DEVICE_TYPE_TV = 1; 15939dd66417ea984e4ff809ecdba78e3f55a8a17467Insun Kang 15949dd66417ea984e4ff809ecdba78e3f55a8a17467Insun Kang /** 15959dd66417ea984e4ff809ecdba78e3f55a8a17467Insun Kang * A receiver device type of the route indicating the presentation of the media is happening 15969dd66417ea984e4ff809ecdba78e3f55a8a17467Insun Kang * on a speaker. 15979dd66417ea984e4ff809ecdba78e3f55a8a17467Insun Kang * 15989dd66417ea984e4ff809ecdba78e3f55a8a17467Insun Kang * @see #getDeviceType 15999dd66417ea984e4ff809ecdba78e3f55a8a17467Insun Kang */ 16009dd66417ea984e4ff809ecdba78e3f55a8a17467Insun Kang public static final int DEVICE_TYPE_SPEAKER = 2; 16019dd66417ea984e4ff809ecdba78e3f55a8a17467Insun Kang 16029dd66417ea984e4ff809ecdba78e3f55a8a17467Insun Kang /** 16039dd66417ea984e4ff809ecdba78e3f55a8a17467Insun Kang * A receiver device type of the route indicating the presentation of the media is happening 16049dd66417ea984e4ff809ecdba78e3f55a8a17467Insun Kang * on a bluetooth device such as a bluetooth speaker. 16059dd66417ea984e4ff809ecdba78e3f55a8a17467Insun Kang * 16069dd66417ea984e4ff809ecdba78e3f55a8a17467Insun Kang * @see #getDeviceType 16079dd66417ea984e4ff809ecdba78e3f55a8a17467Insun Kang */ 16089dd66417ea984e4ff809ecdba78e3f55a8a17467Insun Kang public static final int DEVICE_TYPE_BLUETOOTH = 3; 16099dd66417ea984e4ff809ecdba78e3f55a8a17467Insun Kang 1610b5e2af5919351486a385effe77409d2a91ae9c19Adam Powell private Object mTag; 1611b5e2af5919351486a385effe77409d2a91ae9c19Adam Powell 16127c090d54e2c0eb5309d3f7dc131e137d9c986793Insun Kang /** @hide */ 16137c090d54e2c0eb5309d3f7dc131e137d9c986793Insun Kang @IntDef({PLAYBACK_TYPE_LOCAL, PLAYBACK_TYPE_REMOTE}) 16147c090d54e2c0eb5309d3f7dc131e137d9c986793Insun Kang @Retention(RetentionPolicy.SOURCE) 16157c090d54e2c0eb5309d3f7dc131e137d9c986793Insun Kang public @interface PlaybackType {} 16167c090d54e2c0eb5309d3f7dc131e137d9c986793Insun Kang 16171357012968f9066ea3051d83995e9bac69526c3cJean-Michel Trivi /** 16181357012968f9066ea3051d83995e9bac69526c3cJean-Michel Trivi * The default playback type, "local", indicating the presentation of the media is happening 1619bc4cf00dc5bf6e0e3c01206d5c46e64306df260aJohn Spurlock * on the same device (e.g. a phone, a tablet) as where it is controlled from. 162069b07161bebdb2c726e3a826c2268866f1a94517Jeff Brown * @see #getPlaybackType() 16211357012968f9066ea3051d83995e9bac69526c3cJean-Michel Trivi */ 16221357012968f9066ea3051d83995e9bac69526c3cJean-Michel Trivi public final static int PLAYBACK_TYPE_LOCAL = 0; 16237c090d54e2c0eb5309d3f7dc131e137d9c986793Insun Kang 16241357012968f9066ea3051d83995e9bac69526c3cJean-Michel Trivi /** 16251357012968f9066ea3051d83995e9bac69526c3cJean-Michel Trivi * A playback type indicating the presentation of the media is happening on 1626bc4cf00dc5bf6e0e3c01206d5c46e64306df260aJohn Spurlock * a different device (i.e. the remote device) than where it is controlled from. 162769b07161bebdb2c726e3a826c2268866f1a94517Jeff Brown * @see #getPlaybackType() 16281357012968f9066ea3051d83995e9bac69526c3cJean-Michel Trivi */ 16291357012968f9066ea3051d83995e9bac69526c3cJean-Michel Trivi public final static int PLAYBACK_TYPE_REMOTE = 1; 16307c090d54e2c0eb5309d3f7dc131e137d9c986793Insun Kang 16317c090d54e2c0eb5309d3f7dc131e137d9c986793Insun Kang /** @hide */ 16327c090d54e2c0eb5309d3f7dc131e137d9c986793Insun Kang @IntDef({PLAYBACK_VOLUME_FIXED,PLAYBACK_VOLUME_VARIABLE}) 16337c090d54e2c0eb5309d3f7dc131e137d9c986793Insun Kang @Retention(RetentionPolicy.SOURCE) 16347c090d54e2c0eb5309d3f7dc131e137d9c986793Insun Kang private @interface PlaybackVolume {} 16357c090d54e2c0eb5309d3f7dc131e137d9c986793Insun Kang 16361357012968f9066ea3051d83995e9bac69526c3cJean-Michel Trivi /** 1637bc4cf00dc5bf6e0e3c01206d5c46e64306df260aJohn Spurlock * Playback information indicating the playback volume is fixed, i.e. it cannot be 16381357012968f9066ea3051d83995e9bac69526c3cJean-Michel Trivi * controlled from this object. An example of fixed playback volume is a remote player, 16391357012968f9066ea3051d83995e9bac69526c3cJean-Michel Trivi * playing over HDMI where the user prefers to control the volume on the HDMI sink, rather 16401357012968f9066ea3051d83995e9bac69526c3cJean-Michel Trivi * than attenuate at the source. 164169b07161bebdb2c726e3a826c2268866f1a94517Jeff Brown * @see #getVolumeHandling() 16421357012968f9066ea3051d83995e9bac69526c3cJean-Michel Trivi */ 16431357012968f9066ea3051d83995e9bac69526c3cJean-Michel Trivi public final static int PLAYBACK_VOLUME_FIXED = 0; 16441357012968f9066ea3051d83995e9bac69526c3cJean-Michel Trivi /** 16451357012968f9066ea3051d83995e9bac69526c3cJean-Michel Trivi * Playback information indicating the playback volume is variable and can be controlled 16461357012968f9066ea3051d83995e9bac69526c3cJean-Michel Trivi * from this object. 164769b07161bebdb2c726e3a826c2268866f1a94517Jeff Brown * @see #getVolumeHandling() 16481357012968f9066ea3051d83995e9bac69526c3cJean-Michel Trivi */ 16491357012968f9066ea3051d83995e9bac69526c3cJean-Michel Trivi public final static int PLAYBACK_VOLUME_VARIABLE = 1; 16501357012968f9066ea3051d83995e9bac69526c3cJean-Michel Trivi 16519a1de308cea2d160778fd977825f10a07b49d738Adam Powell RouteInfo(RouteCategory category) { 16529a1de308cea2d160778fd977825f10a07b49d738Adam Powell mCategory = category; 16539dd66417ea984e4ff809ecdba78e3f55a8a17467Insun Kang mDeviceType = DEVICE_TYPE_UNKNOWN; 16549a1de308cea2d160778fd977825f10a07b49d738Adam Powell } 16559a1de308cea2d160778fd977825f10a07b49d738Adam Powell 16569a1de308cea2d160778fd977825f10a07b49d738Adam Powell /** 165756d4b744b374af11f540567d2b43d66d8d7c301eJeff Brown * Gets the user-visible name of the route. 165856d4b744b374af11f540567d2b43d66d8d7c301eJeff Brown * <p> 165956d4b744b374af11f540567d2b43d66d8d7c301eJeff Brown * The route name identifies the destination represented by the route. 166056d4b744b374af11f540567d2b43d66d8d7c301eJeff Brown * It may be a user-supplied name, an alias, or device serial number. 166156d4b744b374af11f540567d2b43d66d8d7c301eJeff Brown * </p> 166256d4b744b374af11f540567d2b43d66d8d7c301eJeff Brown * 166356d4b744b374af11f540567d2b43d66d8d7c301eJeff Brown * @return The user-visible name of a media route. This is the string presented 16649a1de308cea2d160778fd977825f10a07b49d738Adam Powell * to users who may select this as the active route. 16659a1de308cea2d160778fd977825f10a07b49d738Adam Powell */ 16669a1de308cea2d160778fd977825f10a07b49d738Adam Powell public CharSequence getName() { 16670d03c042f90bf62d5bad7c64e77028a5f9f8fae0Adam Powell return getName(sStatic.mResources); 16680d03c042f90bf62d5bad7c64e77028a5f9f8fae0Adam Powell } 166956d4b744b374af11f540567d2b43d66d8d7c301eJeff Brown 16700d03c042f90bf62d5bad7c64e77028a5f9f8fae0Adam Powell /** 167156d4b744b374af11f540567d2b43d66d8d7c301eJeff Brown * Return the properly localized/resource user-visible name of this route. 167256d4b744b374af11f540567d2b43d66d8d7c301eJeff Brown * <p> 167356d4b744b374af11f540567d2b43d66d8d7c301eJeff Brown * The route name identifies the destination represented by the route. 167456d4b744b374af11f540567d2b43d66d8d7c301eJeff Brown * It may be a user-supplied name, an alias, or device serial number. 167556d4b744b374af11f540567d2b43d66d8d7c301eJeff Brown * </p> 167656d4b744b374af11f540567d2b43d66d8d7c301eJeff Brown * 16770d03c042f90bf62d5bad7c64e77028a5f9f8fae0Adam Powell * @param context Context used to resolve the correct configuration to load 167856d4b744b374af11f540567d2b43d66d8d7c301eJeff Brown * @return The user-visible name of a media route. This is the string presented 16790d03c042f90bf62d5bad7c64e77028a5f9f8fae0Adam Powell * to users who may select this as the active route. 16800d03c042f90bf62d5bad7c64e77028a5f9f8fae0Adam Powell */ 16810d03c042f90bf62d5bad7c64e77028a5f9f8fae0Adam Powell public CharSequence getName(Context context) { 16820d03c042f90bf62d5bad7c64e77028a5f9f8fae0Adam Powell return getName(context.getResources()); 16830d03c042f90bf62d5bad7c64e77028a5f9f8fae0Adam Powell } 168456d4b744b374af11f540567d2b43d66d8d7c301eJeff Brown 16850d03c042f90bf62d5bad7c64e77028a5f9f8fae0Adam Powell CharSequence getName(Resources res) { 16860d03c042f90bf62d5bad7c64e77028a5f9f8fae0Adam Powell if (mNameResId != 0) { 1687eadd6d2329972129c96dee390a8b786b37313d17Hyundo Moon return res.getText(mNameResId); 16880d03c042f90bf62d5bad7c64e77028a5f9f8fae0Adam Powell } 16899a1de308cea2d160778fd977825f10a07b49d738Adam Powell return mName; 16909a1de308cea2d160778fd977825f10a07b49d738Adam Powell } 16919a1de308cea2d160778fd977825f10a07b49d738Adam Powell 16929a1de308cea2d160778fd977825f10a07b49d738Adam Powell /** 169356d4b744b374af11f540567d2b43d66d8d7c301eJeff Brown * Gets the user-visible description of the route. 169456d4b744b374af11f540567d2b43d66d8d7c301eJeff Brown * <p> 169556d4b744b374af11f540567d2b43d66d8d7c301eJeff Brown * The route description describes the kind of destination represented by the route. 169656d4b744b374af11f540567d2b43d66d8d7c301eJeff Brown * It may be a user-supplied string, a model number or brand of device. 169756d4b744b374af11f540567d2b43d66d8d7c301eJeff Brown * </p> 169856d4b744b374af11f540567d2b43d66d8d7c301eJeff Brown * 169956d4b744b374af11f540567d2b43d66d8d7c301eJeff Brown * @return The description of the route, or null if none. 170056d4b744b374af11f540567d2b43d66d8d7c301eJeff Brown */ 170156d4b744b374af11f540567d2b43d66d8d7c301eJeff Brown public CharSequence getDescription() { 170256d4b744b374af11f540567d2b43d66d8d7c301eJeff Brown return mDescription; 170356d4b744b374af11f540567d2b43d66d8d7c301eJeff Brown } 170456d4b744b374af11f540567d2b43d66d8d7c301eJeff Brown 170556d4b744b374af11f540567d2b43d66d8d7c301eJeff Brown /** 170656d4b744b374af11f540567d2b43d66d8d7c301eJeff Brown * @return The user-visible status for a media route. This may include a description 17079a1de308cea2d160778fd977825f10a07b49d738Adam Powell * of the currently playing media, if available. 17089a1de308cea2d160778fd977825f10a07b49d738Adam Powell */ 17099a1de308cea2d160778fd977825f10a07b49d738Adam Powell public CharSequence getStatus() { 17109a1de308cea2d160778fd977825f10a07b49d738Adam Powell return mStatus; 17119a1de308cea2d160778fd977825f10a07b49d738Adam Powell } 17129a1de308cea2d160778fd977825f10a07b49d738Adam Powell 17139a1de308cea2d160778fd977825f10a07b49d738Adam Powell /** 1714705ab808cf023e0cc38c2ba7cdb9571942cdc04fAdam Powell * Set this route's status by predetermined status code. If the caller 1715705ab808cf023e0cc38c2ba7cdb9571942cdc04fAdam Powell * should dispatch a route changed event this call will return true; 1716705ab808cf023e0cc38c2ba7cdb9571942cdc04fAdam Powell */ 171739ad0e559896b45185429ea17cd12f18f7ae842cJeff Brown boolean setRealStatusCode(int statusCode) { 171839ad0e559896b45185429ea17cd12f18f7ae842cJeff Brown if (mRealStatusCode != statusCode) { 171939ad0e559896b45185429ea17cd12f18f7ae842cJeff Brown mRealStatusCode = statusCode; 172039ad0e559896b45185429ea17cd12f18f7ae842cJeff Brown return resolveStatusCode(); 172139ad0e559896b45185429ea17cd12f18f7ae842cJeff Brown } 172239ad0e559896b45185429ea17cd12f18f7ae842cJeff Brown return false; 172339ad0e559896b45185429ea17cd12f18f7ae842cJeff Brown } 172439ad0e559896b45185429ea17cd12f18f7ae842cJeff Brown 172539ad0e559896b45185429ea17cd12f18f7ae842cJeff Brown /** 172639ad0e559896b45185429ea17cd12f18f7ae842cJeff Brown * Resolves the status code whenever the real status code or selection state 172739ad0e559896b45185429ea17cd12f18f7ae842cJeff Brown * changes. 172839ad0e559896b45185429ea17cd12f18f7ae842cJeff Brown */ 172939ad0e559896b45185429ea17cd12f18f7ae842cJeff Brown boolean resolveStatusCode() { 173039ad0e559896b45185429ea17cd12f18f7ae842cJeff Brown int statusCode = mRealStatusCode; 173139ad0e559896b45185429ea17cd12f18f7ae842cJeff Brown if (isSelected()) { 1732705ab808cf023e0cc38c2ba7cdb9571942cdc04fAdam Powell switch (statusCode) { 173339ad0e559896b45185429ea17cd12f18f7ae842cJeff Brown // If the route is selected and its status appears to be between states 173439ad0e559896b45185429ea17cd12f18f7ae842cJeff Brown // then report it as connecting even though it has not yet had a chance 173539ad0e559896b45185429ea17cd12f18f7ae842cJeff Brown // to officially move into the CONNECTING state. Note that routes in 173639ad0e559896b45185429ea17cd12f18f7ae842cJeff Brown // the NONE state are assumed to not require an explicit connection 173739ad0e559896b45185429ea17cd12f18f7ae842cJeff Brown // lifecycle whereas those that are AVAILABLE are assumed to have 173839ad0e559896b45185429ea17cd12f18f7ae842cJeff Brown // to eventually proceed to CONNECTED. 1739705ab808cf023e0cc38c2ba7cdb9571942cdc04fAdam Powell case STATUS_AVAILABLE: 174039ad0e559896b45185429ea17cd12f18f7ae842cJeff Brown case STATUS_SCANNING: 174139ad0e559896b45185429ea17cd12f18f7ae842cJeff Brown statusCode = STATUS_CONNECTING; 174269b07161bebdb2c726e3a826c2268866f1a94517Jeff Brown break; 1743705ab808cf023e0cc38c2ba7cdb9571942cdc04fAdam Powell } 1744705ab808cf023e0cc38c2ba7cdb9571942cdc04fAdam Powell } 174539ad0e559896b45185429ea17cd12f18f7ae842cJeff Brown if (mResolvedStatusCode == statusCode) { 174639ad0e559896b45185429ea17cd12f18f7ae842cJeff Brown return false; 174739ad0e559896b45185429ea17cd12f18f7ae842cJeff Brown } 174839ad0e559896b45185429ea17cd12f18f7ae842cJeff Brown 174939ad0e559896b45185429ea17cd12f18f7ae842cJeff Brown mResolvedStatusCode = statusCode; 175039ad0e559896b45185429ea17cd12f18f7ae842cJeff Brown int resId; 175139ad0e559896b45185429ea17cd12f18f7ae842cJeff Brown switch (statusCode) { 175239ad0e559896b45185429ea17cd12f18f7ae842cJeff Brown case STATUS_SCANNING: 175339ad0e559896b45185429ea17cd12f18f7ae842cJeff Brown resId = com.android.internal.R.string.media_route_status_scanning; 175439ad0e559896b45185429ea17cd12f18f7ae842cJeff Brown break; 175539ad0e559896b45185429ea17cd12f18f7ae842cJeff Brown case STATUS_CONNECTING: 175639ad0e559896b45185429ea17cd12f18f7ae842cJeff Brown resId = com.android.internal.R.string.media_route_status_connecting; 175739ad0e559896b45185429ea17cd12f18f7ae842cJeff Brown break; 175839ad0e559896b45185429ea17cd12f18f7ae842cJeff Brown case STATUS_AVAILABLE: 175939ad0e559896b45185429ea17cd12f18f7ae842cJeff Brown resId = com.android.internal.R.string.media_route_status_available; 176039ad0e559896b45185429ea17cd12f18f7ae842cJeff Brown break; 176139ad0e559896b45185429ea17cd12f18f7ae842cJeff Brown case STATUS_NOT_AVAILABLE: 176239ad0e559896b45185429ea17cd12f18f7ae842cJeff Brown resId = com.android.internal.R.string.media_route_status_not_available; 176339ad0e559896b45185429ea17cd12f18f7ae842cJeff Brown break; 176439ad0e559896b45185429ea17cd12f18f7ae842cJeff Brown case STATUS_IN_USE: 176539ad0e559896b45185429ea17cd12f18f7ae842cJeff Brown resId = com.android.internal.R.string.media_route_status_in_use; 176639ad0e559896b45185429ea17cd12f18f7ae842cJeff Brown break; 176739ad0e559896b45185429ea17cd12f18f7ae842cJeff Brown case STATUS_CONNECTED: 176839ad0e559896b45185429ea17cd12f18f7ae842cJeff Brown case STATUS_NONE: 176939ad0e559896b45185429ea17cd12f18f7ae842cJeff Brown default: 177039ad0e559896b45185429ea17cd12f18f7ae842cJeff Brown resId = 0; 177139ad0e559896b45185429ea17cd12f18f7ae842cJeff Brown break; 177239ad0e559896b45185429ea17cd12f18f7ae842cJeff Brown } 177339ad0e559896b45185429ea17cd12f18f7ae842cJeff Brown mStatus = resId != 0 ? sStatic.mResources.getText(resId) : null; 177439ad0e559896b45185429ea17cd12f18f7ae842cJeff Brown return true; 1775705ab808cf023e0cc38c2ba7cdb9571942cdc04fAdam Powell } 1776705ab808cf023e0cc38c2ba7cdb9571942cdc04fAdam Powell 1777705ab808cf023e0cc38c2ba7cdb9571942cdc04fAdam Powell /** 17782ee6a2a83262d05a566bd713d238e89edfd33a29Adam Powell * @hide 17792ee6a2a83262d05a566bd713d238e89edfd33a29Adam Powell */ 17802ee6a2a83262d05a566bd713d238e89edfd33a29Adam Powell public int getStatusCode() { 178139ad0e559896b45185429ea17cd12f18f7ae842cJeff Brown return mResolvedStatusCode; 17822ee6a2a83262d05a566bd713d238e89edfd33a29Adam Powell } 17832ee6a2a83262d05a566bd713d238e89edfd33a29Adam Powell 17842ee6a2a83262d05a566bd713d238e89edfd33a29Adam Powell /** 17859a1de308cea2d160778fd977825f10a07b49d738Adam Powell * @return A media type flag set describing which types this route supports. 17869a1de308cea2d160778fd977825f10a07b49d738Adam Powell */ 17879a1de308cea2d160778fd977825f10a07b49d738Adam Powell public int getSupportedTypes() { 17889a1de308cea2d160778fd977825f10a07b49d738Adam Powell return mSupportedTypes; 17899a1de308cea2d160778fd977825f10a07b49d738Adam Powell } 17909a1de308cea2d160778fd977825f10a07b49d738Adam Powell 17919dd66417ea984e4ff809ecdba78e3f55a8a17467Insun Kang /** 17929dd66417ea984e4ff809ecdba78e3f55a8a17467Insun Kang * Gets the type of the receiver device associated with this route. 17939dd66417ea984e4ff809ecdba78e3f55a8a17467Insun Kang * 17949dd66417ea984e4ff809ecdba78e3f55a8a17467Insun Kang * @return The type of the receiver device associated with this route: 17959dd66417ea984e4ff809ecdba78e3f55a8a17467Insun Kang * {@link #DEVICE_TYPE_BLUETOOTH}, {@link #DEVICE_TYPE_TV}, {@link #DEVICE_TYPE_SPEAKER}, 17969dd66417ea984e4ff809ecdba78e3f55a8a17467Insun Kang * or {@link #DEVICE_TYPE_UNKNOWN}. 17979dd66417ea984e4ff809ecdba78e3f55a8a17467Insun Kang */ 17989dd66417ea984e4ff809ecdba78e3f55a8a17467Insun Kang @DeviceType 17999dd66417ea984e4ff809ecdba78e3f55a8a17467Insun Kang public int getDeviceType() { 18009dd66417ea984e4ff809ecdba78e3f55a8a17467Insun Kang return mDeviceType; 18019dd66417ea984e4ff809ecdba78e3f55a8a17467Insun Kang } 18029dd66417ea984e4ff809ecdba78e3f55a8a17467Insun Kang 18030abd3a6ce83ed23abe614155e776b600ef2a66c3Jeff Brown /** @hide */ 18040abd3a6ce83ed23abe614155e776b600ef2a66c3Jeff Brown public boolean matchesTypes(int types) { 18050abd3a6ce83ed23abe614155e776b600ef2a66c3Jeff Brown return (mSupportedTypes & types) != 0; 18060abd3a6ce83ed23abe614155e776b600ef2a66c3Jeff Brown } 18070abd3a6ce83ed23abe614155e776b600ef2a66c3Jeff Brown 18089a1de308cea2d160778fd977825f10a07b49d738Adam Powell /** 18099a1de308cea2d160778fd977825f10a07b49d738Adam Powell * @return The group that this route belongs to. 18109a1de308cea2d160778fd977825f10a07b49d738Adam Powell */ 18119a1de308cea2d160778fd977825f10a07b49d738Adam Powell public RouteGroup getGroup() { 18129a1de308cea2d160778fd977825f10a07b49d738Adam Powell return mGroup; 18139a1de308cea2d160778fd977825f10a07b49d738Adam Powell } 18149a1de308cea2d160778fd977825f10a07b49d738Adam Powell 18159a1de308cea2d160778fd977825f10a07b49d738Adam Powell /** 18169a1de308cea2d160778fd977825f10a07b49d738Adam Powell * @return the category this route belongs to. 18179a1de308cea2d160778fd977825f10a07b49d738Adam Powell */ 18189a1de308cea2d160778fd977825f10a07b49d738Adam Powell public RouteCategory getCategory() { 18199a1de308cea2d160778fd977825f10a07b49d738Adam Powell return mCategory; 18209a1de308cea2d160778fd977825f10a07b49d738Adam Powell } 18219a1de308cea2d160778fd977825f10a07b49d738Adam Powell 1822ae20ae1a8aaa013813c356ae1d9541ca7ff020aeAdam Powell /** 1823ae20ae1a8aaa013813c356ae1d9541ca7ff020aeAdam Powell * Get the icon representing this route. 1824ae20ae1a8aaa013813c356ae1d9541ca7ff020aeAdam Powell * This icon will be used in picker UIs if available. 1825ae20ae1a8aaa013813c356ae1d9541ca7ff020aeAdam Powell * 1826ae20ae1a8aaa013813c356ae1d9541ca7ff020aeAdam Powell * @return the icon representing this route or null if no icon is available 1827ae20ae1a8aaa013813c356ae1d9541ca7ff020aeAdam Powell */ 1828ae20ae1a8aaa013813c356ae1d9541ca7ff020aeAdam Powell public Drawable getIconDrawable() { 1829ae20ae1a8aaa013813c356ae1d9541ca7ff020aeAdam Powell return mIcon; 1830ae20ae1a8aaa013813c356ae1d9541ca7ff020aeAdam Powell } 1831ae20ae1a8aaa013813c356ae1d9541ca7ff020aeAdam Powell 1832b5e2af5919351486a385effe77409d2a91ae9c19Adam Powell /** 1833b5e2af5919351486a385effe77409d2a91ae9c19Adam Powell * Set an application-specific tag object for this route. 1834b5e2af5919351486a385effe77409d2a91ae9c19Adam Powell * The application may use this to store arbitrary data associated with the 1835b5e2af5919351486a385effe77409d2a91ae9c19Adam Powell * route for internal tracking. 1836b5e2af5919351486a385effe77409d2a91ae9c19Adam Powell * 1837b5e2af5919351486a385effe77409d2a91ae9c19Adam Powell * <p>Note that the lifespan of a route may be well past the lifespan of 1838b5e2af5919351486a385effe77409d2a91ae9c19Adam Powell * an Activity or other Context; take care that objects you store here 1839b5e2af5919351486a385effe77409d2a91ae9c19Adam Powell * will not keep more data in memory alive than you intend.</p> 1840b5e2af5919351486a385effe77409d2a91ae9c19Adam Powell * 1841b5e2af5919351486a385effe77409d2a91ae9c19Adam Powell * @param tag Arbitrary, app-specific data for this route to hold for later use 1842b5e2af5919351486a385effe77409d2a91ae9c19Adam Powell */ 1843b5e2af5919351486a385effe77409d2a91ae9c19Adam Powell public void setTag(Object tag) { 1844b5e2af5919351486a385effe77409d2a91ae9c19Adam Powell mTag = tag; 1845130b4572d1f3df702e5b296a655d15a41f6d4c66Adam Powell routeUpdated(); 1846b5e2af5919351486a385effe77409d2a91ae9c19Adam Powell } 1847b5e2af5919351486a385effe77409d2a91ae9c19Adam Powell 1848b5e2af5919351486a385effe77409d2a91ae9c19Adam Powell /** 1849b5e2af5919351486a385effe77409d2a91ae9c19Adam Powell * @return The tag object previously set by the application 1850b5e2af5919351486a385effe77409d2a91ae9c19Adam Powell * @see #setTag(Object) 1851b5e2af5919351486a385effe77409d2a91ae9c19Adam Powell */ 1852b5e2af5919351486a385effe77409d2a91ae9c19Adam Powell public Object getTag() { 1853b5e2af5919351486a385effe77409d2a91ae9c19Adam Powell return mTag; 1854b5e2af5919351486a385effe77409d2a91ae9c19Adam Powell } 1855b5e2af5919351486a385effe77409d2a91ae9c19Adam Powell 18561357012968f9066ea3051d83995e9bac69526c3cJean-Michel Trivi /** 18571357012968f9066ea3051d83995e9bac69526c3cJean-Michel Trivi * @return the type of playback associated with this route 18581357012968f9066ea3051d83995e9bac69526c3cJean-Michel Trivi * @see UserRouteInfo#setPlaybackType(int) 18591357012968f9066ea3051d83995e9bac69526c3cJean-Michel Trivi */ 18607c090d54e2c0eb5309d3f7dc131e137d9c986793Insun Kang @PlaybackType 18611357012968f9066ea3051d83995e9bac69526c3cJean-Michel Trivi public int getPlaybackType() { 18621357012968f9066ea3051d83995e9bac69526c3cJean-Michel Trivi return mPlaybackType; 18631357012968f9066ea3051d83995e9bac69526c3cJean-Michel Trivi } 18641357012968f9066ea3051d83995e9bac69526c3cJean-Michel Trivi 18651357012968f9066ea3051d83995e9bac69526c3cJean-Michel Trivi /** 18661357012968f9066ea3051d83995e9bac69526c3cJean-Michel Trivi * @return the stream over which the playback associated with this route is performed 18671357012968f9066ea3051d83995e9bac69526c3cJean-Michel Trivi * @see UserRouteInfo#setPlaybackStream(int) 18681357012968f9066ea3051d83995e9bac69526c3cJean-Michel Trivi */ 18691357012968f9066ea3051d83995e9bac69526c3cJean-Michel Trivi public int getPlaybackStream() { 18701357012968f9066ea3051d83995e9bac69526c3cJean-Michel Trivi return mPlaybackStream; 18711357012968f9066ea3051d83995e9bac69526c3cJean-Michel Trivi } 18721357012968f9066ea3051d83995e9bac69526c3cJean-Michel Trivi 18731357012968f9066ea3051d83995e9bac69526c3cJean-Michel Trivi /** 18748e37a85bf3dc39519942698dc90a3951306b934bAdam Powell * Return the current volume for this route. Depending on the route, this may only 18758e37a85bf3dc39519942698dc90a3951306b934bAdam Powell * be valid if the route is currently selected. 18768e37a85bf3dc39519942698dc90a3951306b934bAdam Powell * 18771357012968f9066ea3051d83995e9bac69526c3cJean-Michel Trivi * @return the volume at which the playback associated with this route is performed 18781357012968f9066ea3051d83995e9bac69526c3cJean-Michel Trivi * @see UserRouteInfo#setVolume(int) 18791357012968f9066ea3051d83995e9bac69526c3cJean-Michel Trivi */ 18801357012968f9066ea3051d83995e9bac69526c3cJean-Michel Trivi public int getVolume() { 18811357012968f9066ea3051d83995e9bac69526c3cJean-Michel Trivi if (mPlaybackType == PLAYBACK_TYPE_LOCAL) { 18821357012968f9066ea3051d83995e9bac69526c3cJean-Michel Trivi int vol = 0; 18831357012968f9066ea3051d83995e9bac69526c3cJean-Michel Trivi try { 18841357012968f9066ea3051d83995e9bac69526c3cJean-Michel Trivi vol = sStatic.mAudioService.getStreamVolume(mPlaybackStream); 18851357012968f9066ea3051d83995e9bac69526c3cJean-Michel Trivi } catch (RemoteException e) { 18861357012968f9066ea3051d83995e9bac69526c3cJean-Michel Trivi Log.e(TAG, "Error getting local stream volume", e); 18871357012968f9066ea3051d83995e9bac69526c3cJean-Michel Trivi } 18881357012968f9066ea3051d83995e9bac69526c3cJean-Michel Trivi return vol; 18891357012968f9066ea3051d83995e9bac69526c3cJean-Michel Trivi } else { 18901357012968f9066ea3051d83995e9bac69526c3cJean-Michel Trivi return mVolume; 18911357012968f9066ea3051d83995e9bac69526c3cJean-Michel Trivi } 18921357012968f9066ea3051d83995e9bac69526c3cJean-Michel Trivi } 18931357012968f9066ea3051d83995e9bac69526c3cJean-Michel Trivi 18941357012968f9066ea3051d83995e9bac69526c3cJean-Michel Trivi /** 18958e37a85bf3dc39519942698dc90a3951306b934bAdam Powell * Request a volume change for this route. 18968e37a85bf3dc39519942698dc90a3951306b934bAdam Powell * @param volume value between 0 and getVolumeMax 18978e37a85bf3dc39519942698dc90a3951306b934bAdam Powell */ 18988e37a85bf3dc39519942698dc90a3951306b934bAdam Powell public void requestSetVolume(int volume) { 18998e37a85bf3dc39519942698dc90a3951306b934bAdam Powell if (mPlaybackType == PLAYBACK_TYPE_LOCAL) { 19008e37a85bf3dc39519942698dc90a3951306b934bAdam Powell try { 1901ba50b97cff80e73620a0e3d13cae169e095974a7Dianne Hackborn sStatic.mAudioService.setStreamVolume(mPlaybackStream, volume, 0, 1902ba50b97cff80e73620a0e3d13cae169e095974a7Dianne Hackborn ActivityThread.currentPackageName()); 19038e37a85bf3dc39519942698dc90a3951306b934bAdam Powell } catch (RemoteException e) { 19048e37a85bf3dc39519942698dc90a3951306b934bAdam Powell Log.e(TAG, "Error setting local stream volume", e); 19058e37a85bf3dc39519942698dc90a3951306b934bAdam Powell } 19068e37a85bf3dc39519942698dc90a3951306b934bAdam Powell } else { 190769b07161bebdb2c726e3a826c2268866f1a94517Jeff Brown sStatic.requestSetVolume(this, volume); 19088e37a85bf3dc39519942698dc90a3951306b934bAdam Powell } 19098e37a85bf3dc39519942698dc90a3951306b934bAdam Powell } 19108e37a85bf3dc39519942698dc90a3951306b934bAdam Powell 19118e37a85bf3dc39519942698dc90a3951306b934bAdam Powell /** 19128e37a85bf3dc39519942698dc90a3951306b934bAdam Powell * Request an incremental volume update for this route. 19138e37a85bf3dc39519942698dc90a3951306b934bAdam Powell * @param direction Delta to apply to the current volume 19148e37a85bf3dc39519942698dc90a3951306b934bAdam Powell */ 19158e37a85bf3dc39519942698dc90a3951306b934bAdam Powell public void requestUpdateVolume(int direction) { 19168e37a85bf3dc39519942698dc90a3951306b934bAdam Powell if (mPlaybackType == PLAYBACK_TYPE_LOCAL) { 19178e37a85bf3dc39519942698dc90a3951306b934bAdam Powell try { 19188e37a85bf3dc39519942698dc90a3951306b934bAdam Powell final int volume = 19198e37a85bf3dc39519942698dc90a3951306b934bAdam Powell Math.max(0, Math.min(getVolume() + direction, getVolumeMax())); 1920ba50b97cff80e73620a0e3d13cae169e095974a7Dianne Hackborn sStatic.mAudioService.setStreamVolume(mPlaybackStream, volume, 0, 1921ba50b97cff80e73620a0e3d13cae169e095974a7Dianne Hackborn ActivityThread.currentPackageName()); 19228e37a85bf3dc39519942698dc90a3951306b934bAdam Powell } catch (RemoteException e) { 19238e37a85bf3dc39519942698dc90a3951306b934bAdam Powell Log.e(TAG, "Error setting local stream volume", e); 19248e37a85bf3dc39519942698dc90a3951306b934bAdam Powell } 19258e37a85bf3dc39519942698dc90a3951306b934bAdam Powell } else { 192669b07161bebdb2c726e3a826c2268866f1a94517Jeff Brown sStatic.requestUpdateVolume(this, direction); 19278e37a85bf3dc39519942698dc90a3951306b934bAdam Powell } 19288e37a85bf3dc39519942698dc90a3951306b934bAdam Powell } 19298e37a85bf3dc39519942698dc90a3951306b934bAdam Powell 19308e37a85bf3dc39519942698dc90a3951306b934bAdam Powell /** 19311357012968f9066ea3051d83995e9bac69526c3cJean-Michel Trivi * @return the maximum volume at which the playback associated with this route is performed 19321357012968f9066ea3051d83995e9bac69526c3cJean-Michel Trivi * @see UserRouteInfo#setVolumeMax(int) 19331357012968f9066ea3051d83995e9bac69526c3cJean-Michel Trivi */ 19341357012968f9066ea3051d83995e9bac69526c3cJean-Michel Trivi public int getVolumeMax() { 19351357012968f9066ea3051d83995e9bac69526c3cJean-Michel Trivi if (mPlaybackType == PLAYBACK_TYPE_LOCAL) { 19361357012968f9066ea3051d83995e9bac69526c3cJean-Michel Trivi int volMax = 0; 19371357012968f9066ea3051d83995e9bac69526c3cJean-Michel Trivi try { 19381357012968f9066ea3051d83995e9bac69526c3cJean-Michel Trivi volMax = sStatic.mAudioService.getStreamMaxVolume(mPlaybackStream); 19391357012968f9066ea3051d83995e9bac69526c3cJean-Michel Trivi } catch (RemoteException e) { 19401357012968f9066ea3051d83995e9bac69526c3cJean-Michel Trivi Log.e(TAG, "Error getting local stream volume", e); 19411357012968f9066ea3051d83995e9bac69526c3cJean-Michel Trivi } 19421357012968f9066ea3051d83995e9bac69526c3cJean-Michel Trivi return volMax; 19431357012968f9066ea3051d83995e9bac69526c3cJean-Michel Trivi } else { 19441357012968f9066ea3051d83995e9bac69526c3cJean-Michel Trivi return mVolumeMax; 19451357012968f9066ea3051d83995e9bac69526c3cJean-Michel Trivi } 19461357012968f9066ea3051d83995e9bac69526c3cJean-Michel Trivi } 19471357012968f9066ea3051d83995e9bac69526c3cJean-Michel Trivi 19481357012968f9066ea3051d83995e9bac69526c3cJean-Michel Trivi /** 19491357012968f9066ea3051d83995e9bac69526c3cJean-Michel Trivi * @return how volume is handling on the route 19501357012968f9066ea3051d83995e9bac69526c3cJean-Michel Trivi * @see UserRouteInfo#setVolumeHandling(int) 19511357012968f9066ea3051d83995e9bac69526c3cJean-Michel Trivi */ 19527c090d54e2c0eb5309d3f7dc131e137d9c986793Insun Kang @PlaybackVolume 19531357012968f9066ea3051d83995e9bac69526c3cJean-Michel Trivi public int getVolumeHandling() { 19541357012968f9066ea3051d83995e9bac69526c3cJean-Michel Trivi return mVolumeHandling; 19551357012968f9066ea3051d83995e9bac69526c3cJean-Michel Trivi } 19561357012968f9066ea3051d83995e9bac69526c3cJean-Michel Trivi 1957705ab808cf023e0cc38c2ba7cdb9571942cdc04fAdam Powell /** 195892130f6407dc51c58b3b941d28a6daf4e04b8d62Jeff Brown * Gets the {@link Display} that should be used by the application to show 195992130f6407dc51c58b3b941d28a6daf4e04b8d62Jeff Brown * a {@link android.app.Presentation} on an external display when this route is selected. 196092130f6407dc51c58b3b941d28a6daf4e04b8d62Jeff Brown * Depending on the route, this may only be valid if the route is currently 196192130f6407dc51c58b3b941d28a6daf4e04b8d62Jeff Brown * selected. 196292130f6407dc51c58b3b941d28a6daf4e04b8d62Jeff Brown * <p> 196392130f6407dc51c58b3b941d28a6daf4e04b8d62Jeff Brown * The preferred presentation display may change independently of the route 196492130f6407dc51c58b3b941d28a6daf4e04b8d62Jeff Brown * being selected or unselected. For example, the presentation display 196592130f6407dc51c58b3b941d28a6daf4e04b8d62Jeff Brown * of the default system route may change when an external HDMI display is connected 196692130f6407dc51c58b3b941d28a6daf4e04b8d62Jeff Brown * or disconnected even though the route itself has not changed. 196792130f6407dc51c58b3b941d28a6daf4e04b8d62Jeff Brown * </p><p> 196892130f6407dc51c58b3b941d28a6daf4e04b8d62Jeff Brown * This method may return null if there is no external display associated with 196992130f6407dc51c58b3b941d28a6daf4e04b8d62Jeff Brown * the route or if the display is not ready to show UI yet. 197092130f6407dc51c58b3b941d28a6daf4e04b8d62Jeff Brown * </p><p> 197192130f6407dc51c58b3b941d28a6daf4e04b8d62Jeff Brown * The application should listen for changes to the presentation display 197292130f6407dc51c58b3b941d28a6daf4e04b8d62Jeff Brown * using the {@link Callback#onRoutePresentationDisplayChanged} callback and 197392130f6407dc51c58b3b941d28a6daf4e04b8d62Jeff Brown * show or dismiss its {@link android.app.Presentation} accordingly when the display 197492130f6407dc51c58b3b941d28a6daf4e04b8d62Jeff Brown * becomes available or is removed. 197592130f6407dc51c58b3b941d28a6daf4e04b8d62Jeff Brown * </p><p> 197692130f6407dc51c58b3b941d28a6daf4e04b8d62Jeff Brown * This method only makes sense for {@link #ROUTE_TYPE_LIVE_VIDEO live video} routes. 197792130f6407dc51c58b3b941d28a6daf4e04b8d62Jeff Brown * </p> 197892130f6407dc51c58b3b941d28a6daf4e04b8d62Jeff Brown * 197992130f6407dc51c58b3b941d28a6daf4e04b8d62Jeff Brown * @return The preferred presentation display to use when this route is 198092130f6407dc51c58b3b941d28a6daf4e04b8d62Jeff Brown * selected or null if none. 198192130f6407dc51c58b3b941d28a6daf4e04b8d62Jeff Brown * 198292130f6407dc51c58b3b941d28a6daf4e04b8d62Jeff Brown * @see #ROUTE_TYPE_LIVE_VIDEO 198392130f6407dc51c58b3b941d28a6daf4e04b8d62Jeff Brown * @see android.app.Presentation 198492130f6407dc51c58b3b941d28a6daf4e04b8d62Jeff Brown */ 198592130f6407dc51c58b3b941d28a6daf4e04b8d62Jeff Brown public Display getPresentationDisplay() { 198692130f6407dc51c58b3b941d28a6daf4e04b8d62Jeff Brown return mPresentationDisplay; 198792130f6407dc51c58b3b941d28a6daf4e04b8d62Jeff Brown } 198892130f6407dc51c58b3b941d28a6daf4e04b8d62Jeff Brown 19895830b0b33618940d65197cec99d697b21908fec8Chong Zhang boolean updatePresentationDisplay() { 19905830b0b33618940d65197cec99d697b21908fec8Chong Zhang Display display = choosePresentationDisplay(); 19915830b0b33618940d65197cec99d697b21908fec8Chong Zhang if (mPresentationDisplay != display) { 19925830b0b33618940d65197cec99d697b21908fec8Chong Zhang mPresentationDisplay = display; 19935830b0b33618940d65197cec99d697b21908fec8Chong Zhang return true; 19945830b0b33618940d65197cec99d697b21908fec8Chong Zhang } 19955830b0b33618940d65197cec99d697b21908fec8Chong Zhang return false; 19965830b0b33618940d65197cec99d697b21908fec8Chong Zhang } 19975830b0b33618940d65197cec99d697b21908fec8Chong Zhang 19985830b0b33618940d65197cec99d697b21908fec8Chong Zhang private Display choosePresentationDisplay() { 19995830b0b33618940d65197cec99d697b21908fec8Chong Zhang if ((mSupportedTypes & ROUTE_TYPE_LIVE_VIDEO) != 0) { 20005830b0b33618940d65197cec99d697b21908fec8Chong Zhang Display[] displays = sStatic.getAllPresentationDisplays(); 20015830b0b33618940d65197cec99d697b21908fec8Chong Zhang 20025830b0b33618940d65197cec99d697b21908fec8Chong Zhang // Ensure that the specified display is valid for presentations. 20035830b0b33618940d65197cec99d697b21908fec8Chong Zhang // This check will normally disallow the default display unless it was 20045830b0b33618940d65197cec99d697b21908fec8Chong Zhang // configured as a presentation display for some reason. 20055830b0b33618940d65197cec99d697b21908fec8Chong Zhang if (mPresentationDisplayId >= 0) { 20065830b0b33618940d65197cec99d697b21908fec8Chong Zhang for (Display display : displays) { 20075830b0b33618940d65197cec99d697b21908fec8Chong Zhang if (display.getDisplayId() == mPresentationDisplayId) { 20085830b0b33618940d65197cec99d697b21908fec8Chong Zhang return display; 20095830b0b33618940d65197cec99d697b21908fec8Chong Zhang } 20105830b0b33618940d65197cec99d697b21908fec8Chong Zhang } 20115830b0b33618940d65197cec99d697b21908fec8Chong Zhang return null; 20125830b0b33618940d65197cec99d697b21908fec8Chong Zhang } 20135830b0b33618940d65197cec99d697b21908fec8Chong Zhang 20145830b0b33618940d65197cec99d697b21908fec8Chong Zhang // Find the indicated Wifi display by its address. 20155830b0b33618940d65197cec99d697b21908fec8Chong Zhang if (mDeviceAddress != null) { 20165830b0b33618940d65197cec99d697b21908fec8Chong Zhang for (Display display : displays) { 20175830b0b33618940d65197cec99d697b21908fec8Chong Zhang if (display.getType() == Display.TYPE_WIFI 20185830b0b33618940d65197cec99d697b21908fec8Chong Zhang && mDeviceAddress.equals(display.getAddress())) { 20195830b0b33618940d65197cec99d697b21908fec8Chong Zhang return display; 20205830b0b33618940d65197cec99d697b21908fec8Chong Zhang } 20215830b0b33618940d65197cec99d697b21908fec8Chong Zhang } 20225830b0b33618940d65197cec99d697b21908fec8Chong Zhang return null; 20235830b0b33618940d65197cec99d697b21908fec8Chong Zhang } 20245830b0b33618940d65197cec99d697b21908fec8Chong Zhang 20255830b0b33618940d65197cec99d697b21908fec8Chong Zhang // For the default route, choose the first presentation display from the list. 20265830b0b33618940d65197cec99d697b21908fec8Chong Zhang if (this == sStatic.mDefaultAudioVideo && displays.length > 0) { 20275830b0b33618940d65197cec99d697b21908fec8Chong Zhang return displays[0]; 20285830b0b33618940d65197cec99d697b21908fec8Chong Zhang } 20295830b0b33618940d65197cec99d697b21908fec8Chong Zhang } 20305830b0b33618940d65197cec99d697b21908fec8Chong Zhang return null; 20315830b0b33618940d65197cec99d697b21908fec8Chong Zhang } 20325830b0b33618940d65197cec99d697b21908fec8Chong Zhang 203375af171006ee80aaf7cfb56ded1378afe0084ccaJeff Brown /** @hide */ 203475af171006ee80aaf7cfb56ded1378afe0084ccaJeff Brown public String getDeviceAddress() { 203575af171006ee80aaf7cfb56ded1378afe0084ccaJeff Brown return mDeviceAddress; 203675af171006ee80aaf7cfb56ded1378afe0084ccaJeff Brown } 203775af171006ee80aaf7cfb56ded1378afe0084ccaJeff Brown 203892130f6407dc51c58b3b941d28a6daf4e04b8d62Jeff Brown /** 2039a27b8fb4296c1271ddf5916843ddffda6764e65fJeff Brown * Returns true if this route is enabled and may be selected. 2040a27b8fb4296c1271ddf5916843ddffda6764e65fJeff Brown * 2041a27b8fb4296c1271ddf5916843ddffda6764e65fJeff Brown * @return True if this route is enabled. 2042705ab808cf023e0cc38c2ba7cdb9571942cdc04fAdam Powell */ 2043705ab808cf023e0cc38c2ba7cdb9571942cdc04fAdam Powell public boolean isEnabled() { 2044705ab808cf023e0cc38c2ba7cdb9571942cdc04fAdam Powell return mEnabled; 2045705ab808cf023e0cc38c2ba7cdb9571942cdc04fAdam Powell } 2046705ab808cf023e0cc38c2ba7cdb9571942cdc04fAdam Powell 2047a27b8fb4296c1271ddf5916843ddffda6764e65fJeff Brown /** 2048a27b8fb4296c1271ddf5916843ddffda6764e65fJeff Brown * Returns true if the route is in the process of connecting and is not 2049a27b8fb4296c1271ddf5916843ddffda6764e65fJeff Brown * yet ready for use. 2050a27b8fb4296c1271ddf5916843ddffda6764e65fJeff Brown * 2051a27b8fb4296c1271ddf5916843ddffda6764e65fJeff Brown * @return True if this route is in the process of connecting. 2052a27b8fb4296c1271ddf5916843ddffda6764e65fJeff Brown */ 2053a27b8fb4296c1271ddf5916843ddffda6764e65fJeff Brown public boolean isConnecting() { 205439ad0e559896b45185429ea17cd12f18f7ae842cJeff Brown return mResolvedStatusCode == STATUS_CONNECTING; 20550abd3a6ce83ed23abe614155e776b600ef2a66c3Jeff Brown } 20560abd3a6ce83ed23abe614155e776b600ef2a66c3Jeff Brown 20570abd3a6ce83ed23abe614155e776b600ef2a66c3Jeff Brown /** @hide */ 20580abd3a6ce83ed23abe614155e776b600ef2a66c3Jeff Brown public boolean isSelected() { 20590abd3a6ce83ed23abe614155e776b600ef2a66c3Jeff Brown return this == sStatic.mSelectedRoute; 20600abd3a6ce83ed23abe614155e776b600ef2a66c3Jeff Brown } 20610abd3a6ce83ed23abe614155e776b600ef2a66c3Jeff Brown 20620abd3a6ce83ed23abe614155e776b600ef2a66c3Jeff Brown /** @hide */ 20630abd3a6ce83ed23abe614155e776b600ef2a66c3Jeff Brown public boolean isDefault() { 20640abd3a6ce83ed23abe614155e776b600ef2a66c3Jeff Brown return this == sStatic.mDefaultAudioVideo; 20650abd3a6ce83ed23abe614155e776b600ef2a66c3Jeff Brown } 20660abd3a6ce83ed23abe614155e776b600ef2a66c3Jeff Brown 20670abd3a6ce83ed23abe614155e776b600ef2a66c3Jeff Brown /** @hide */ 206859579ce59d6e2c42e6ea1b389ba7e228aaa9122fSungsoo Lim public boolean isBluetooth() { 206959579ce59d6e2c42e6ea1b389ba7e228aaa9122fSungsoo Lim return this == sStatic.mBluetoothA2dpRoute; 207059579ce59d6e2c42e6ea1b389ba7e228aaa9122fSungsoo Lim } 207159579ce59d6e2c42e6ea1b389ba7e228aaa9122fSungsoo Lim 207259579ce59d6e2c42e6ea1b389ba7e228aaa9122fSungsoo Lim /** @hide */ 20730abd3a6ce83ed23abe614155e776b600ef2a66c3Jeff Brown public void select() { 20740abd3a6ce83ed23abe614155e776b600ef2a66c3Jeff Brown selectRouteStatic(mSupportedTypes, this, true); 2075a27b8fb4296c1271ddf5916843ddffda6764e65fJeff Brown } 2076a27b8fb4296c1271ddf5916843ddffda6764e65fJeff Brown 20779a1de308cea2d160778fd977825f10a07b49d738Adam Powell void setStatusInt(CharSequence status) { 20789a1de308cea2d160778fd977825f10a07b49d738Adam Powell if (!status.equals(mStatus)) { 20799a1de308cea2d160778fd977825f10a07b49d738Adam Powell mStatus = status; 20809a1de308cea2d160778fd977825f10a07b49d738Adam Powell if (mGroup != null) { 20819a1de308cea2d160778fd977825f10a07b49d738Adam Powell mGroup.memberStatusChanged(this, status); 20829a1de308cea2d160778fd977825f10a07b49d738Adam Powell } 20839a1de308cea2d160778fd977825f10a07b49d738Adam Powell routeUpdated(); 20849a1de308cea2d160778fd977825f10a07b49d738Adam Powell } 20859a1de308cea2d160778fd977825f10a07b49d738Adam Powell } 20869a1de308cea2d160778fd977825f10a07b49d738Adam Powell 20871357012968f9066ea3051d83995e9bac69526c3cJean-Michel Trivi final IRemoteVolumeObserver.Stub mRemoteVolObserver = new IRemoteVolumeObserver.Stub() { 208869b07161bebdb2c726e3a826c2268866f1a94517Jeff Brown @Override 20891357012968f9066ea3051d83995e9bac69526c3cJean-Michel Trivi public void dispatchRemoteVolumeUpdate(final int direction, final int value) { 20901357012968f9066ea3051d83995e9bac69526c3cJean-Michel Trivi sStatic.mHandler.post(new Runnable() { 20911357012968f9066ea3051d83995e9bac69526c3cJean-Michel Trivi @Override 20921357012968f9066ea3051d83995e9bac69526c3cJean-Michel Trivi public void run() { 20931357012968f9066ea3051d83995e9bac69526c3cJean-Michel Trivi if (mVcb != null) { 20941357012968f9066ea3051d83995e9bac69526c3cJean-Michel Trivi if (direction != 0) { 20951357012968f9066ea3051d83995e9bac69526c3cJean-Michel Trivi mVcb.vcb.onVolumeUpdateRequest(mVcb.route, direction); 20961357012968f9066ea3051d83995e9bac69526c3cJean-Michel Trivi } else { 20971357012968f9066ea3051d83995e9bac69526c3cJean-Michel Trivi mVcb.vcb.onVolumeSetRequest(mVcb.route, value); 20981357012968f9066ea3051d83995e9bac69526c3cJean-Michel Trivi } 20991357012968f9066ea3051d83995e9bac69526c3cJean-Michel Trivi } 21001357012968f9066ea3051d83995e9bac69526c3cJean-Michel Trivi } 21011357012968f9066ea3051d83995e9bac69526c3cJean-Michel Trivi }); 21021357012968f9066ea3051d83995e9bac69526c3cJean-Michel Trivi } 21031357012968f9066ea3051d83995e9bac69526c3cJean-Michel Trivi }; 21041357012968f9066ea3051d83995e9bac69526c3cJean-Michel Trivi 21059a1de308cea2d160778fd977825f10a07b49d738Adam Powell void routeUpdated() { 21069a1de308cea2d160778fd977825f10a07b49d738Adam Powell updateRoute(this); 21079a1de308cea2d160778fd977825f10a07b49d738Adam Powell } 21089a1de308cea2d160778fd977825f10a07b49d738Adam Powell 21099a1de308cea2d160778fd977825f10a07b49d738Adam Powell @Override 21109a1de308cea2d160778fd977825f10a07b49d738Adam Powell public String toString() { 2111d6d0bddee363e0c7fe61f63bd9d9864a71d887d6Adam Powell String supportedTypes = typesToString(getSupportedTypes()); 211292130f6407dc51c58b3b941d28a6daf4e04b8d62Jeff Brown return getClass().getSimpleName() + "{ name=" + getName() + 211356d4b744b374af11f540567d2b43d66d8d7c301eJeff Brown ", description=" + getDescription() + 211492130f6407dc51c58b3b941d28a6daf4e04b8d62Jeff Brown ", status=" + getStatus() + 211592130f6407dc51c58b3b941d28a6daf4e04b8d62Jeff Brown ", category=" + getCategory() + 211692130f6407dc51c58b3b941d28a6daf4e04b8d62Jeff Brown ", supportedTypes=" + supportedTypes + 211769b07161bebdb2c726e3a826c2268866f1a94517Jeff Brown ", presentationDisplay=" + mPresentationDisplay + " }"; 21189a1de308cea2d160778fd977825f10a07b49d738Adam Powell } 21199a1de308cea2d160778fd977825f10a07b49d738Adam Powell } 21209a1de308cea2d160778fd977825f10a07b49d738Adam Powell 21219a1de308cea2d160778fd977825f10a07b49d738Adam Powell /** 21229a1de308cea2d160778fd977825f10a07b49d738Adam Powell * Information about a route that the application may define and modify. 21238e37a85bf3dc39519942698dc90a3951306b934bAdam Powell * A user route defaults to {@link RouteInfo#PLAYBACK_TYPE_REMOTE} and 21248e37a85bf3dc39519942698dc90a3951306b934bAdam Powell * {@link RouteInfo#PLAYBACK_VOLUME_FIXED}. 21259a1de308cea2d160778fd977825f10a07b49d738Adam Powell * 21269a1de308cea2d160778fd977825f10a07b49d738Adam Powell * @see MediaRouter.RouteInfo 21279a1de308cea2d160778fd977825f10a07b49d738Adam Powell */ 2128b58b8f832d06b0ffa8886eba5a4916578a3b8743Dianne Hackborn public static class UserRouteInfo extends RouteInfo { 2129ae20ae1a8aaa013813c356ae1d9541ca7ff020aeAdam Powell RemoteControlClient mRcc; 21305d3114b64a88ac1f72becd8d46f148c666f64aa3RoboErik SessionVolumeProvider mSvp; 21319a1de308cea2d160778fd977825f10a07b49d738Adam Powell 21329a1de308cea2d160778fd977825f10a07b49d738Adam Powell UserRouteInfo(RouteCategory category) { 21339a1de308cea2d160778fd977825f10a07b49d738Adam Powell super(category); 21349a1de308cea2d160778fd977825f10a07b49d738Adam Powell mSupportedTypes = ROUTE_TYPE_USER; 21358e37a85bf3dc39519942698dc90a3951306b934bAdam Powell mPlaybackType = PLAYBACK_TYPE_REMOTE; 21368e37a85bf3dc39519942698dc90a3951306b934bAdam Powell mVolumeHandling = PLAYBACK_VOLUME_FIXED; 21379a1de308cea2d160778fd977825f10a07b49d738Adam Powell } 21389a1de308cea2d160778fd977825f10a07b49d738Adam Powell 21399a1de308cea2d160778fd977825f10a07b49d738Adam Powell /** 21409a1de308cea2d160778fd977825f10a07b49d738Adam Powell * Set the user-visible name of this route. 21419a1de308cea2d160778fd977825f10a07b49d738Adam Powell * @param name Name to display to the user to describe this route 21429a1de308cea2d160778fd977825f10a07b49d738Adam Powell */ 21439a1de308cea2d160778fd977825f10a07b49d738Adam Powell public void setName(CharSequence name) { 2144eadd6d2329972129c96dee390a8b786b37313d17Hyundo Moon mNameResId = 0; 21459a1de308cea2d160778fd977825f10a07b49d738Adam Powell mName = name; 21469a1de308cea2d160778fd977825f10a07b49d738Adam Powell routeUpdated(); 21479a1de308cea2d160778fd977825f10a07b49d738Adam Powell } 21485d3114b64a88ac1f72becd8d46f148c666f64aa3RoboErik 21490d03c042f90bf62d5bad7c64e77028a5f9f8fae0Adam Powell /** 21500d03c042f90bf62d5bad7c64e77028a5f9f8fae0Adam Powell * Set the user-visible name of this route. 215156d4b744b374af11f540567d2b43d66d8d7c301eJeff Brown * <p> 215256d4b744b374af11f540567d2b43d66d8d7c301eJeff Brown * The route name identifies the destination represented by the route. 215356d4b744b374af11f540567d2b43d66d8d7c301eJeff Brown * It may be a user-supplied name, an alias, or device serial number. 215456d4b744b374af11f540567d2b43d66d8d7c301eJeff Brown * </p> 215556d4b744b374af11f540567d2b43d66d8d7c301eJeff Brown * 21560d03c042f90bf62d5bad7c64e77028a5f9f8fae0Adam Powell * @param resId Resource ID of the name to display to the user to describe this route 21570d03c042f90bf62d5bad7c64e77028a5f9f8fae0Adam Powell */ 21580d03c042f90bf62d5bad7c64e77028a5f9f8fae0Adam Powell public void setName(int resId) { 21590d03c042f90bf62d5bad7c64e77028a5f9f8fae0Adam Powell mNameResId = resId; 21600d03c042f90bf62d5bad7c64e77028a5f9f8fae0Adam Powell mName = null; 21610d03c042f90bf62d5bad7c64e77028a5f9f8fae0Adam Powell routeUpdated(); 21620d03c042f90bf62d5bad7c64e77028a5f9f8fae0Adam Powell } 21639a1de308cea2d160778fd977825f10a07b49d738Adam Powell 21649a1de308cea2d160778fd977825f10a07b49d738Adam Powell /** 216556d4b744b374af11f540567d2b43d66d8d7c301eJeff Brown * Set the user-visible description of this route. 216656d4b744b374af11f540567d2b43d66d8d7c301eJeff Brown * <p> 216756d4b744b374af11f540567d2b43d66d8d7c301eJeff Brown * The route description describes the kind of destination represented by the route. 216856d4b744b374af11f540567d2b43d66d8d7c301eJeff Brown * It may be a user-supplied string, a model number or brand of device. 216956d4b744b374af11f540567d2b43d66d8d7c301eJeff Brown * </p> 217056d4b744b374af11f540567d2b43d66d8d7c301eJeff Brown * 217156d4b744b374af11f540567d2b43d66d8d7c301eJeff Brown * @param description The description of the route, or null if none. 217256d4b744b374af11f540567d2b43d66d8d7c301eJeff Brown */ 217356d4b744b374af11f540567d2b43d66d8d7c301eJeff Brown public void setDescription(CharSequence description) { 217456d4b744b374af11f540567d2b43d66d8d7c301eJeff Brown mDescription = description; 217556d4b744b374af11f540567d2b43d66d8d7c301eJeff Brown routeUpdated(); 217656d4b744b374af11f540567d2b43d66d8d7c301eJeff Brown } 217756d4b744b374af11f540567d2b43d66d8d7c301eJeff Brown 217856d4b744b374af11f540567d2b43d66d8d7c301eJeff Brown /** 21799a1de308cea2d160778fd977825f10a07b49d738Adam Powell * Set the current user-visible status for this route. 21809a1de308cea2d160778fd977825f10a07b49d738Adam Powell * @param status Status to display to the user to describe what the endpoint 21819a1de308cea2d160778fd977825f10a07b49d738Adam Powell * of this route is currently doing 21829a1de308cea2d160778fd977825f10a07b49d738Adam Powell */ 21839a1de308cea2d160778fd977825f10a07b49d738Adam Powell public void setStatus(CharSequence status) { 21849a1de308cea2d160778fd977825f10a07b49d738Adam Powell setStatusInt(status); 21859a1de308cea2d160778fd977825f10a07b49d738Adam Powell } 2186ae20ae1a8aaa013813c356ae1d9541ca7ff020aeAdam Powell 2187ae20ae1a8aaa013813c356ae1d9541ca7ff020aeAdam Powell /** 2188ae20ae1a8aaa013813c356ae1d9541ca7ff020aeAdam Powell * Set the RemoteControlClient responsible for reporting playback info for this 2189ae20ae1a8aaa013813c356ae1d9541ca7ff020aeAdam Powell * user route. 2190ae20ae1a8aaa013813c356ae1d9541ca7ff020aeAdam Powell * 2191ae20ae1a8aaa013813c356ae1d9541ca7ff020aeAdam Powell * <p>If this route manages remote playback, the data exposed by this 2192ae20ae1a8aaa013813c356ae1d9541ca7ff020aeAdam Powell * RemoteControlClient will be used to reflect and update information 2193ae20ae1a8aaa013813c356ae1d9541ca7ff020aeAdam Powell * such as route volume info in related UIs.</p> 2194ae20ae1a8aaa013813c356ae1d9541ca7ff020aeAdam Powell * 21951357012968f9066ea3051d83995e9bac69526c3cJean-Michel Trivi * <p>The RemoteControlClient must have been previously registered with 21961357012968f9066ea3051d83995e9bac69526c3cJean-Michel Trivi * {@link AudioManager#registerRemoteControlClient(RemoteControlClient)}.</p> 21971357012968f9066ea3051d83995e9bac69526c3cJean-Michel Trivi * 2198ae20ae1a8aaa013813c356ae1d9541ca7ff020aeAdam Powell * @param rcc RemoteControlClient associated with this route 2199ae20ae1a8aaa013813c356ae1d9541ca7ff020aeAdam Powell */ 2200ae20ae1a8aaa013813c356ae1d9541ca7ff020aeAdam Powell public void setRemoteControlClient(RemoteControlClient rcc) { 2201ae20ae1a8aaa013813c356ae1d9541ca7ff020aeAdam Powell mRcc = rcc; 22021357012968f9066ea3051d83995e9bac69526c3cJean-Michel Trivi updatePlaybackInfoOnRcc(); 2203ae20ae1a8aaa013813c356ae1d9541ca7ff020aeAdam Powell } 2204ae20ae1a8aaa013813c356ae1d9541ca7ff020aeAdam Powell 2205ae20ae1a8aaa013813c356ae1d9541ca7ff020aeAdam Powell /** 22064599696591f745b3a546197d2ba7e5cfc5562484Adam Powell * Retrieve the RemoteControlClient associated with this route, if one has been set. 22074599696591f745b3a546197d2ba7e5cfc5562484Adam Powell * 22084599696591f745b3a546197d2ba7e5cfc5562484Adam Powell * @return the RemoteControlClient associated with this route 22094599696591f745b3a546197d2ba7e5cfc5562484Adam Powell * @see #setRemoteControlClient(RemoteControlClient) 22104599696591f745b3a546197d2ba7e5cfc5562484Adam Powell */ 22114599696591f745b3a546197d2ba7e5cfc5562484Adam Powell public RemoteControlClient getRemoteControlClient() { 22124599696591f745b3a546197d2ba7e5cfc5562484Adam Powell return mRcc; 22134599696591f745b3a546197d2ba7e5cfc5562484Adam Powell } 22144599696591f745b3a546197d2ba7e5cfc5562484Adam Powell 22154599696591f745b3a546197d2ba7e5cfc5562484Adam Powell /** 2216ae20ae1a8aaa013813c356ae1d9541ca7ff020aeAdam Powell * Set an icon that will be used to represent this route. 2217ae20ae1a8aaa013813c356ae1d9541ca7ff020aeAdam Powell * The system may use this icon in picker UIs or similar. 2218ae20ae1a8aaa013813c356ae1d9541ca7ff020aeAdam Powell * 2219ae20ae1a8aaa013813c356ae1d9541ca7ff020aeAdam Powell * @param icon icon drawable to use to represent this route 2220ae20ae1a8aaa013813c356ae1d9541ca7ff020aeAdam Powell */ 2221ae20ae1a8aaa013813c356ae1d9541ca7ff020aeAdam Powell public void setIconDrawable(Drawable icon) { 2222ae20ae1a8aaa013813c356ae1d9541ca7ff020aeAdam Powell mIcon = icon; 2223ae20ae1a8aaa013813c356ae1d9541ca7ff020aeAdam Powell } 2224ae20ae1a8aaa013813c356ae1d9541ca7ff020aeAdam Powell 2225ae20ae1a8aaa013813c356ae1d9541ca7ff020aeAdam Powell /** 2226ae20ae1a8aaa013813c356ae1d9541ca7ff020aeAdam Powell * Set an icon that will be used to represent this route. 2227ae20ae1a8aaa013813c356ae1d9541ca7ff020aeAdam Powell * The system may use this icon in picker UIs or similar. 2228ae20ae1a8aaa013813c356ae1d9541ca7ff020aeAdam Powell * 222971c69897ad0a55d590698bfa399bfe99c763b9dbAdam Powell * @param resId Resource ID of an icon drawable to use to represent this route 2230ae20ae1a8aaa013813c356ae1d9541ca7ff020aeAdam Powell */ 22317b9c912f536925ac6ec43935d6e97506851b33d6Tor Norbye public void setIconResource(@DrawableRes int resId) { 2232ae20ae1a8aaa013813c356ae1d9541ca7ff020aeAdam Powell setIconDrawable(sStatic.mResources.getDrawable(resId)); 2233ae20ae1a8aaa013813c356ae1d9541ca7ff020aeAdam Powell } 22341357012968f9066ea3051d83995e9bac69526c3cJean-Michel Trivi 22351357012968f9066ea3051d83995e9bac69526c3cJean-Michel Trivi /** 22361357012968f9066ea3051d83995e9bac69526c3cJean-Michel Trivi * Set a callback to be notified of volume update requests 22371357012968f9066ea3051d83995e9bac69526c3cJean-Michel Trivi * @param vcb 22381357012968f9066ea3051d83995e9bac69526c3cJean-Michel Trivi */ 22391357012968f9066ea3051d83995e9bac69526c3cJean-Michel Trivi public void setVolumeCallback(VolumeCallback vcb) { 22401357012968f9066ea3051d83995e9bac69526c3cJean-Michel Trivi mVcb = new VolumeCallbackInfo(vcb, this); 22411357012968f9066ea3051d83995e9bac69526c3cJean-Michel Trivi } 22421357012968f9066ea3051d83995e9bac69526c3cJean-Michel Trivi 22431357012968f9066ea3051d83995e9bac69526c3cJean-Michel Trivi /** 22441357012968f9066ea3051d83995e9bac69526c3cJean-Michel Trivi * Defines whether playback associated with this route is "local" 22451357012968f9066ea3051d83995e9bac69526c3cJean-Michel Trivi * ({@link RouteInfo#PLAYBACK_TYPE_LOCAL}) or "remote" 22461357012968f9066ea3051d83995e9bac69526c3cJean-Michel Trivi * ({@link RouteInfo#PLAYBACK_TYPE_REMOTE}). 22471357012968f9066ea3051d83995e9bac69526c3cJean-Michel Trivi * @param type 22481357012968f9066ea3051d83995e9bac69526c3cJean-Michel Trivi */ 22497c090d54e2c0eb5309d3f7dc131e137d9c986793Insun Kang public void setPlaybackType(@RouteInfo.PlaybackType int type) { 22501357012968f9066ea3051d83995e9bac69526c3cJean-Michel Trivi if (mPlaybackType != type) { 22511357012968f9066ea3051d83995e9bac69526c3cJean-Michel Trivi mPlaybackType = type; 2252430fc48865e5a371b08f180390946b96d73848feRoboErik configureSessionVolume(); 22531357012968f9066ea3051d83995e9bac69526c3cJean-Michel Trivi } 22541357012968f9066ea3051d83995e9bac69526c3cJean-Michel Trivi } 22551357012968f9066ea3051d83995e9bac69526c3cJean-Michel Trivi 22561357012968f9066ea3051d83995e9bac69526c3cJean-Michel Trivi /** 22571357012968f9066ea3051d83995e9bac69526c3cJean-Michel Trivi * Defines whether volume for the playback associated with this route is fixed 22581357012968f9066ea3051d83995e9bac69526c3cJean-Michel Trivi * ({@link RouteInfo#PLAYBACK_VOLUME_FIXED}) or can modified 22591357012968f9066ea3051d83995e9bac69526c3cJean-Michel Trivi * ({@link RouteInfo#PLAYBACK_VOLUME_VARIABLE}). 22601357012968f9066ea3051d83995e9bac69526c3cJean-Michel Trivi * @param volumeHandling 22611357012968f9066ea3051d83995e9bac69526c3cJean-Michel Trivi */ 22627c090d54e2c0eb5309d3f7dc131e137d9c986793Insun Kang public void setVolumeHandling(@RouteInfo.PlaybackVolume int volumeHandling) { 22631357012968f9066ea3051d83995e9bac69526c3cJean-Michel Trivi if (mVolumeHandling != volumeHandling) { 22641357012968f9066ea3051d83995e9bac69526c3cJean-Michel Trivi mVolumeHandling = volumeHandling; 2265430fc48865e5a371b08f180390946b96d73848feRoboErik configureSessionVolume(); 22661357012968f9066ea3051d83995e9bac69526c3cJean-Michel Trivi } 22671357012968f9066ea3051d83995e9bac69526c3cJean-Michel Trivi } 22681357012968f9066ea3051d83995e9bac69526c3cJean-Michel Trivi 22691357012968f9066ea3051d83995e9bac69526c3cJean-Michel Trivi /** 22701357012968f9066ea3051d83995e9bac69526c3cJean-Michel Trivi * Defines at what volume the playback associated with this route is performed (for user 22711357012968f9066ea3051d83995e9bac69526c3cJean-Michel Trivi * feedback purposes). This information is only used when the playback is not local. 22721357012968f9066ea3051d83995e9bac69526c3cJean-Michel Trivi * @param volume 22731357012968f9066ea3051d83995e9bac69526c3cJean-Michel Trivi */ 22741357012968f9066ea3051d83995e9bac69526c3cJean-Michel Trivi public void setVolume(int volume) { 22758e37a85bf3dc39519942698dc90a3951306b934bAdam Powell volume = Math.max(0, Math.min(volume, getVolumeMax())); 22761357012968f9066ea3051d83995e9bac69526c3cJean-Michel Trivi if (mVolume != volume) { 22771357012968f9066ea3051d83995e9bac69526c3cJean-Michel Trivi mVolume = volume; 2278430fc48865e5a371b08f180390946b96d73848feRoboErik if (mSvp != null) { 22790d0f67f5ee5f939a1b611bc4583212707afd9beeRoboErik mSvp.setCurrentVolume(mVolume); 22805d3114b64a88ac1f72becd8d46f148c666f64aa3RoboErik } 22818e37a85bf3dc39519942698dc90a3951306b934bAdam Powell dispatchRouteVolumeChanged(this); 2282f8ac14a7f5a59b4ec8e89283a2da40b626e42065Adam Powell if (mGroup != null) { 2283f8ac14a7f5a59b4ec8e89283a2da40b626e42065Adam Powell mGroup.memberVolumeChanged(this); 2284f8ac14a7f5a59b4ec8e89283a2da40b626e42065Adam Powell } 22858e37a85bf3dc39519942698dc90a3951306b934bAdam Powell } 22868e37a85bf3dc39519942698dc90a3951306b934bAdam Powell } 22878e37a85bf3dc39519942698dc90a3951306b934bAdam Powell 22888e37a85bf3dc39519942698dc90a3951306b934bAdam Powell @Override 22898e37a85bf3dc39519942698dc90a3951306b934bAdam Powell public void requestSetVolume(int volume) { 22908e37a85bf3dc39519942698dc90a3951306b934bAdam Powell if (mVolumeHandling == PLAYBACK_VOLUME_VARIABLE) { 22918e37a85bf3dc39519942698dc90a3951306b934bAdam Powell if (mVcb == null) { 22928e37a85bf3dc39519942698dc90a3951306b934bAdam Powell Log.e(TAG, "Cannot requestSetVolume on user route - no volume callback set"); 22938e37a85bf3dc39519942698dc90a3951306b934bAdam Powell return; 22948e37a85bf3dc39519942698dc90a3951306b934bAdam Powell } 22958e37a85bf3dc39519942698dc90a3951306b934bAdam Powell mVcb.vcb.onVolumeSetRequest(this, volume); 22968e37a85bf3dc39519942698dc90a3951306b934bAdam Powell } 22978e37a85bf3dc39519942698dc90a3951306b934bAdam Powell } 22988e37a85bf3dc39519942698dc90a3951306b934bAdam Powell 22998e37a85bf3dc39519942698dc90a3951306b934bAdam Powell @Override 23008e37a85bf3dc39519942698dc90a3951306b934bAdam Powell public void requestUpdateVolume(int direction) { 23018e37a85bf3dc39519942698dc90a3951306b934bAdam Powell if (mVolumeHandling == PLAYBACK_VOLUME_VARIABLE) { 23028e37a85bf3dc39519942698dc90a3951306b934bAdam Powell if (mVcb == null) { 23038e37a85bf3dc39519942698dc90a3951306b934bAdam Powell Log.e(TAG, "Cannot requestChangeVolume on user route - no volumec callback set"); 23048e37a85bf3dc39519942698dc90a3951306b934bAdam Powell return; 23058e37a85bf3dc39519942698dc90a3951306b934bAdam Powell } 23068e37a85bf3dc39519942698dc90a3951306b934bAdam Powell mVcb.vcb.onVolumeUpdateRequest(this, direction); 23071357012968f9066ea3051d83995e9bac69526c3cJean-Michel Trivi } 23081357012968f9066ea3051d83995e9bac69526c3cJean-Michel Trivi } 23091357012968f9066ea3051d83995e9bac69526c3cJean-Michel Trivi 23101357012968f9066ea3051d83995e9bac69526c3cJean-Michel Trivi /** 23111357012968f9066ea3051d83995e9bac69526c3cJean-Michel Trivi * Defines the maximum volume at which the playback associated with this route is performed 23121357012968f9066ea3051d83995e9bac69526c3cJean-Michel Trivi * (for user feedback purposes). This information is only used when the playback is not 23131357012968f9066ea3051d83995e9bac69526c3cJean-Michel Trivi * local. 23141357012968f9066ea3051d83995e9bac69526c3cJean-Michel Trivi * @param volumeMax 23151357012968f9066ea3051d83995e9bac69526c3cJean-Michel Trivi */ 23161357012968f9066ea3051d83995e9bac69526c3cJean-Michel Trivi public void setVolumeMax(int volumeMax) { 23171357012968f9066ea3051d83995e9bac69526c3cJean-Michel Trivi if (mVolumeMax != volumeMax) { 23181357012968f9066ea3051d83995e9bac69526c3cJean-Michel Trivi mVolumeMax = volumeMax; 2319430fc48865e5a371b08f180390946b96d73848feRoboErik configureSessionVolume(); 23201357012968f9066ea3051d83995e9bac69526c3cJean-Michel Trivi } 23211357012968f9066ea3051d83995e9bac69526c3cJean-Michel Trivi } 23221357012968f9066ea3051d83995e9bac69526c3cJean-Michel Trivi 23231357012968f9066ea3051d83995e9bac69526c3cJean-Michel Trivi /** 23241357012968f9066ea3051d83995e9bac69526c3cJean-Michel Trivi * Defines over what stream type the media is presented. 23251357012968f9066ea3051d83995e9bac69526c3cJean-Michel Trivi * @param stream 23261357012968f9066ea3051d83995e9bac69526c3cJean-Michel Trivi */ 23271357012968f9066ea3051d83995e9bac69526c3cJean-Michel Trivi public void setPlaybackStream(int stream) { 23281357012968f9066ea3051d83995e9bac69526c3cJean-Michel Trivi if (mPlaybackStream != stream) { 23291357012968f9066ea3051d83995e9bac69526c3cJean-Michel Trivi mPlaybackStream = stream; 2330430fc48865e5a371b08f180390946b96d73848feRoboErik configureSessionVolume(); 23311357012968f9066ea3051d83995e9bac69526c3cJean-Michel Trivi } 23321357012968f9066ea3051d83995e9bac69526c3cJean-Michel Trivi } 23331357012968f9066ea3051d83995e9bac69526c3cJean-Michel Trivi 23341357012968f9066ea3051d83995e9bac69526c3cJean-Michel Trivi private void updatePlaybackInfoOnRcc() { 2335430fc48865e5a371b08f180390946b96d73848feRoboErik configureSessionVolume(); 23365d3114b64a88ac1f72becd8d46f148c666f64aa3RoboErik } 23375d3114b64a88ac1f72becd8d46f148c666f64aa3RoboErik 23385d3114b64a88ac1f72becd8d46f148c666f64aa3RoboErik private void configureSessionVolume() { 23395d3114b64a88ac1f72becd8d46f148c666f64aa3RoboErik if (mRcc == null) { 23405d3114b64a88ac1f72becd8d46f148c666f64aa3RoboErik if (DEBUG) { 2341eadd6d2329972129c96dee390a8b786b37313d17Hyundo Moon Log.d(TAG, "No Rcc to configure volume for route " + getName()); 23425d3114b64a88ac1f72becd8d46f148c666f64aa3RoboErik } 23435d3114b64a88ac1f72becd8d46f148c666f64aa3RoboErik return; 23445d3114b64a88ac1f72becd8d46f148c666f64aa3RoboErik } 23455d3114b64a88ac1f72becd8d46f148c666f64aa3RoboErik MediaSession session = mRcc.getMediaSession(); 23465d3114b64a88ac1f72becd8d46f148c666f64aa3RoboErik if (session == null) { 23475d3114b64a88ac1f72becd8d46f148c666f64aa3RoboErik if (DEBUG) { 23485d3114b64a88ac1f72becd8d46f148c666f64aa3RoboErik Log.d(TAG, "Rcc has no session to configure volume"); 23495d3114b64a88ac1f72becd8d46f148c666f64aa3RoboErik } 23505d3114b64a88ac1f72becd8d46f148c666f64aa3RoboErik return; 23515d3114b64a88ac1f72becd8d46f148c666f64aa3RoboErik } 23525d3114b64a88ac1f72becd8d46f148c666f64aa3RoboErik if (mPlaybackType == RemoteControlClient.PLAYBACK_TYPE_REMOTE) { 23537c090d54e2c0eb5309d3f7dc131e137d9c986793Insun Kang @VolumeProvider.ControlType int volumeControl = 23547c090d54e2c0eb5309d3f7dc131e137d9c986793Insun Kang VolumeProvider.VOLUME_CONTROL_FIXED; 23555d3114b64a88ac1f72becd8d46f148c666f64aa3RoboErik switch (mVolumeHandling) { 23565d3114b64a88ac1f72becd8d46f148c666f64aa3RoboErik case RemoteControlClient.PLAYBACK_VOLUME_VARIABLE: 2357ef3c9e9b057a5aac2d0d012e8e6385660478e203RoboErik volumeControl = VolumeProvider.VOLUME_CONTROL_ABSOLUTE; 23585d3114b64a88ac1f72becd8d46f148c666f64aa3RoboErik break; 23595d3114b64a88ac1f72becd8d46f148c666f64aa3RoboErik case RemoteControlClient.PLAYBACK_VOLUME_FIXED: 23605d3114b64a88ac1f72becd8d46f148c666f64aa3RoboErik default: 23615d3114b64a88ac1f72becd8d46f148c666f64aa3RoboErik break; 23621357012968f9066ea3051d83995e9bac69526c3cJean-Michel Trivi } 23635d3114b64a88ac1f72becd8d46f148c666f64aa3RoboErik // Only register a new listener if necessary 23645d3114b64a88ac1f72becd8d46f148c666f64aa3RoboErik if (mSvp == null || mSvp.getVolumeControl() != volumeControl 23655d3114b64a88ac1f72becd8d46f148c666f64aa3RoboErik || mSvp.getMaxVolume() != mVolumeMax) { 23660d0f67f5ee5f939a1b611bc4583212707afd9beeRoboErik mSvp = new SessionVolumeProvider(volumeControl, mVolumeMax, mVolume); 23675d3114b64a88ac1f72becd8d46f148c666f64aa3RoboErik session.setPlaybackToRemote(mSvp); 23685d3114b64a88ac1f72becd8d46f148c666f64aa3RoboErik } 23695d3114b64a88ac1f72becd8d46f148c666f64aa3RoboErik } else { 23705d3114b64a88ac1f72becd8d46f148c666f64aa3RoboErik // We only know how to handle local and remote, fall back to local if not remote. 23719db9bf7034d7dcdf596dc22d521b18975d0dd2b9RoboErik AudioAttributes.Builder bob = new AudioAttributes.Builder(); 23729db9bf7034d7dcdf596dc22d521b18975d0dd2b9RoboErik bob.setLegacyStreamType(mPlaybackStream); 23739db9bf7034d7dcdf596dc22d521b18975d0dd2b9RoboErik session.setPlaybackToLocal(bob.build()); 23745d3114b64a88ac1f72becd8d46f148c666f64aa3RoboErik mSvp = null; 23751357012968f9066ea3051d83995e9bac69526c3cJean-Michel Trivi } 23761357012968f9066ea3051d83995e9bac69526c3cJean-Michel Trivi } 23771357012968f9066ea3051d83995e9bac69526c3cJean-Michel Trivi 2378ef3c9e9b057a5aac2d0d012e8e6385660478e203RoboErik class SessionVolumeProvider extends VolumeProvider { 23795d3114b64a88ac1f72becd8d46f148c666f64aa3RoboErik 23807c090d54e2c0eb5309d3f7dc131e137d9c986793Insun Kang public SessionVolumeProvider(@VolumeProvider.ControlType int volumeControl, 23817c090d54e2c0eb5309d3f7dc131e137d9c986793Insun Kang int maxVolume, int currentVolume) { 23820d0f67f5ee5f939a1b611bc4583212707afd9beeRoboErik super(volumeControl, maxVolume, currentVolume); 23835d3114b64a88ac1f72becd8d46f148c666f64aa3RoboErik } 23845d3114b64a88ac1f72becd8d46f148c666f64aa3RoboErik 23855d3114b64a88ac1f72becd8d46f148c666f64aa3RoboErik @Override 23865d3114b64a88ac1f72becd8d46f148c666f64aa3RoboErik public void onSetVolumeTo(final int volume) { 23875d3114b64a88ac1f72becd8d46f148c666f64aa3RoboErik sStatic.mHandler.post(new Runnable() { 23885d3114b64a88ac1f72becd8d46f148c666f64aa3RoboErik @Override 23895d3114b64a88ac1f72becd8d46f148c666f64aa3RoboErik public void run() { 23905d3114b64a88ac1f72becd8d46f148c666f64aa3RoboErik if (mVcb != null) { 23915d3114b64a88ac1f72becd8d46f148c666f64aa3RoboErik mVcb.vcb.onVolumeSetRequest(mVcb.route, volume); 23925d3114b64a88ac1f72becd8d46f148c666f64aa3RoboErik } 23935d3114b64a88ac1f72becd8d46f148c666f64aa3RoboErik } 23945d3114b64a88ac1f72becd8d46f148c666f64aa3RoboErik }); 23955d3114b64a88ac1f72becd8d46f148c666f64aa3RoboErik } 23965d3114b64a88ac1f72becd8d46f148c666f64aa3RoboErik 23975d3114b64a88ac1f72becd8d46f148c666f64aa3RoboErik @Override 23981ff5b1648a051e9650614f0c0f1b3f449777db81RoboErik public void onAdjustVolume(final int direction) { 23995d3114b64a88ac1f72becd8d46f148c666f64aa3RoboErik sStatic.mHandler.post(new Runnable() { 24005d3114b64a88ac1f72becd8d46f148c666f64aa3RoboErik @Override 24015d3114b64a88ac1f72becd8d46f148c666f64aa3RoboErik public void run() { 24025d3114b64a88ac1f72becd8d46f148c666f64aa3RoboErik if (mVcb != null) { 24031ff5b1648a051e9650614f0c0f1b3f449777db81RoboErik mVcb.vcb.onVolumeUpdateRequest(mVcb.route, direction); 24045d3114b64a88ac1f72becd8d46f148c666f64aa3RoboErik } 24055d3114b64a88ac1f72becd8d46f148c666f64aa3RoboErik } 24065d3114b64a88ac1f72becd8d46f148c666f64aa3RoboErik }); 24075d3114b64a88ac1f72becd8d46f148c666f64aa3RoboErik } 24085d3114b64a88ac1f72becd8d46f148c666f64aa3RoboErik } 24099a1de308cea2d160778fd977825f10a07b49d738Adam Powell } 24109a1de308cea2d160778fd977825f10a07b49d738Adam Powell 24119a1de308cea2d160778fd977825f10a07b49d738Adam Powell /** 24129a1de308cea2d160778fd977825f10a07b49d738Adam Powell * Information about a route that consists of multiple other routes in a group. 24139a1de308cea2d160778fd977825f10a07b49d738Adam Powell */ 2414b58b8f832d06b0ffa8886eba5a4916578a3b8743Dianne Hackborn public static class RouteGroup extends RouteInfo { 24159a1de308cea2d160778fd977825f10a07b49d738Adam Powell final ArrayList<RouteInfo> mRoutes = new ArrayList<RouteInfo>(); 24169a1de308cea2d160778fd977825f10a07b49d738Adam Powell private boolean mUpdateName; 24179a1de308cea2d160778fd977825f10a07b49d738Adam Powell 24189a1de308cea2d160778fd977825f10a07b49d738Adam Powell RouteGroup(RouteCategory category) { 24199a1de308cea2d160778fd977825f10a07b49d738Adam Powell super(category); 24209a1de308cea2d160778fd977825f10a07b49d738Adam Powell mGroup = this; 24218e37a85bf3dc39519942698dc90a3951306b934bAdam Powell mVolumeHandling = PLAYBACK_VOLUME_FIXED; 24229a1de308cea2d160778fd977825f10a07b49d738Adam Powell } 24239a1de308cea2d160778fd977825f10a07b49d738Adam Powell 242469b07161bebdb2c726e3a826c2268866f1a94517Jeff Brown @Override 24250d03c042f90bf62d5bad7c64e77028a5f9f8fae0Adam Powell CharSequence getName(Resources res) { 24269a1de308cea2d160778fd977825f10a07b49d738Adam Powell if (mUpdateName) updateName(); 24270d03c042f90bf62d5bad7c64e77028a5f9f8fae0Adam Powell return super.getName(res); 24289a1de308cea2d160778fd977825f10a07b49d738Adam Powell } 24299a1de308cea2d160778fd977825f10a07b49d738Adam Powell 24309a1de308cea2d160778fd977825f10a07b49d738Adam Powell /** 24319a1de308cea2d160778fd977825f10a07b49d738Adam Powell * Add a route to this group. The route must not currently belong to another group. 24329a1de308cea2d160778fd977825f10a07b49d738Adam Powell * 24339a1de308cea2d160778fd977825f10a07b49d738Adam Powell * @param route route to add to this group 24349a1de308cea2d160778fd977825f10a07b49d738Adam Powell */ 24359a1de308cea2d160778fd977825f10a07b49d738Adam Powell public void addRoute(RouteInfo route) { 24369a1de308cea2d160778fd977825f10a07b49d738Adam Powell if (route.getGroup() != null) { 24379a1de308cea2d160778fd977825f10a07b49d738Adam Powell throw new IllegalStateException("Route " + route + " is already part of a group."); 24389a1de308cea2d160778fd977825f10a07b49d738Adam Powell } 24399a1de308cea2d160778fd977825f10a07b49d738Adam Powell if (route.getCategory() != mCategory) { 24409a1de308cea2d160778fd977825f10a07b49d738Adam Powell throw new IllegalArgumentException( 24419a1de308cea2d160778fd977825f10a07b49d738Adam Powell "Route cannot be added to a group with a different category. " + 24429a1de308cea2d160778fd977825f10a07b49d738Adam Powell "(Route category=" + route.getCategory() + 24439a1de308cea2d160778fd977825f10a07b49d738Adam Powell " group category=" + mCategory + ")"); 24449a1de308cea2d160778fd977825f10a07b49d738Adam Powell } 2445d0d2cda9d414da73773285d7fee9e13aef3495e9Adam Powell final int at = mRoutes.size(); 24469a1de308cea2d160778fd977825f10a07b49d738Adam Powell mRoutes.add(route); 2447d6d0bddee363e0c7fe61f63bd9d9864a71d887d6Adam Powell route.mGroup = this; 24489a1de308cea2d160778fd977825f10a07b49d738Adam Powell mUpdateName = true; 2449f8ac14a7f5a59b4ec8e89283a2da40b626e42065Adam Powell updateVolume(); 24509a1de308cea2d160778fd977825f10a07b49d738Adam Powell routeUpdated(); 2451f3b653a21cdffe04c94c275e69ecb56e00766e82Adam Powell dispatchRouteGrouped(route, this, at); 24529a1de308cea2d160778fd977825f10a07b49d738Adam Powell } 24539a1de308cea2d160778fd977825f10a07b49d738Adam Powell 24549a1de308cea2d160778fd977825f10a07b49d738Adam Powell /** 24559a1de308cea2d160778fd977825f10a07b49d738Adam Powell * Add a route to this group before the specified index. 24569a1de308cea2d160778fd977825f10a07b49d738Adam Powell * 24579a1de308cea2d160778fd977825f10a07b49d738Adam Powell * @param route route to add 24589a1de308cea2d160778fd977825f10a07b49d738Adam Powell * @param insertAt insert the new route before this index 24599a1de308cea2d160778fd977825f10a07b49d738Adam Powell */ 24609a1de308cea2d160778fd977825f10a07b49d738Adam Powell public void addRoute(RouteInfo route, int insertAt) { 24619a1de308cea2d160778fd977825f10a07b49d738Adam Powell if (route.getGroup() != null) { 24629a1de308cea2d160778fd977825f10a07b49d738Adam Powell throw new IllegalStateException("Route " + route + " is already part of a group."); 24639a1de308cea2d160778fd977825f10a07b49d738Adam Powell } 24649a1de308cea2d160778fd977825f10a07b49d738Adam Powell if (route.getCategory() != mCategory) { 24659a1de308cea2d160778fd977825f10a07b49d738Adam Powell throw new IllegalArgumentException( 24669a1de308cea2d160778fd977825f10a07b49d738Adam Powell "Route cannot be added to a group with a different category. " + 24679a1de308cea2d160778fd977825f10a07b49d738Adam Powell "(Route category=" + route.getCategory() + 24689a1de308cea2d160778fd977825f10a07b49d738Adam Powell " group category=" + mCategory + ")"); 24699a1de308cea2d160778fd977825f10a07b49d738Adam Powell } 24709a1de308cea2d160778fd977825f10a07b49d738Adam Powell mRoutes.add(insertAt, route); 2471d6d0bddee363e0c7fe61f63bd9d9864a71d887d6Adam Powell route.mGroup = this; 24729a1de308cea2d160778fd977825f10a07b49d738Adam Powell mUpdateName = true; 2473f8ac14a7f5a59b4ec8e89283a2da40b626e42065Adam Powell updateVolume(); 24749a1de308cea2d160778fd977825f10a07b49d738Adam Powell routeUpdated(); 2475f3b653a21cdffe04c94c275e69ecb56e00766e82Adam Powell dispatchRouteGrouped(route, this, insertAt); 24769a1de308cea2d160778fd977825f10a07b49d738Adam Powell } 24779a1de308cea2d160778fd977825f10a07b49d738Adam Powell 24789a1de308cea2d160778fd977825f10a07b49d738Adam Powell /** 24799a1de308cea2d160778fd977825f10a07b49d738Adam Powell * Remove a route from this group. 24809a1de308cea2d160778fd977825f10a07b49d738Adam Powell * 24819a1de308cea2d160778fd977825f10a07b49d738Adam Powell * @param route route to remove 24829a1de308cea2d160778fd977825f10a07b49d738Adam Powell */ 24839a1de308cea2d160778fd977825f10a07b49d738Adam Powell public void removeRoute(RouteInfo route) { 24849a1de308cea2d160778fd977825f10a07b49d738Adam Powell if (route.getGroup() != this) { 24859a1de308cea2d160778fd977825f10a07b49d738Adam Powell throw new IllegalArgumentException("Route " + route + 24869a1de308cea2d160778fd977825f10a07b49d738Adam Powell " is not a member of this group."); 24879a1de308cea2d160778fd977825f10a07b49d738Adam Powell } 24889a1de308cea2d160778fd977825f10a07b49d738Adam Powell mRoutes.remove(route); 2489d6d0bddee363e0c7fe61f63bd9d9864a71d887d6Adam Powell route.mGroup = null; 24909a1de308cea2d160778fd977825f10a07b49d738Adam Powell mUpdateName = true; 2491f8ac14a7f5a59b4ec8e89283a2da40b626e42065Adam Powell updateVolume(); 2492d0d2cda9d414da73773285d7fee9e13aef3495e9Adam Powell dispatchRouteUngrouped(route, this); 24939a1de308cea2d160778fd977825f10a07b49d738Adam Powell routeUpdated(); 24949a1de308cea2d160778fd977825f10a07b49d738Adam Powell } 24959a1de308cea2d160778fd977825f10a07b49d738Adam Powell 24969a1de308cea2d160778fd977825f10a07b49d738Adam Powell /** 24979a1de308cea2d160778fd977825f10a07b49d738Adam Powell * Remove the route at the specified index from this group. 24989a1de308cea2d160778fd977825f10a07b49d738Adam Powell * 24999a1de308cea2d160778fd977825f10a07b49d738Adam Powell * @param index index of the route to remove 25009a1de308cea2d160778fd977825f10a07b49d738Adam Powell */ 25019a1de308cea2d160778fd977825f10a07b49d738Adam Powell public void removeRoute(int index) { 2502d0d2cda9d414da73773285d7fee9e13aef3495e9Adam Powell RouteInfo route = mRoutes.remove(index); 2503d6d0bddee363e0c7fe61f63bd9d9864a71d887d6Adam Powell route.mGroup = null; 25049a1de308cea2d160778fd977825f10a07b49d738Adam Powell mUpdateName = true; 2505f8ac14a7f5a59b4ec8e89283a2da40b626e42065Adam Powell updateVolume(); 2506d0d2cda9d414da73773285d7fee9e13aef3495e9Adam Powell dispatchRouteUngrouped(route, this); 25079a1de308cea2d160778fd977825f10a07b49d738Adam Powell routeUpdated(); 25089a1de308cea2d160778fd977825f10a07b49d738Adam Powell } 25099a1de308cea2d160778fd977825f10a07b49d738Adam Powell 2510d0d2cda9d414da73773285d7fee9e13aef3495e9Adam Powell /** 2511d0d2cda9d414da73773285d7fee9e13aef3495e9Adam Powell * @return The number of routes in this group 2512d0d2cda9d414da73773285d7fee9e13aef3495e9Adam Powell */ 2513d0d2cda9d414da73773285d7fee9e13aef3495e9Adam Powell public int getRouteCount() { 2514d0d2cda9d414da73773285d7fee9e13aef3495e9Adam Powell return mRoutes.size(); 2515d0d2cda9d414da73773285d7fee9e13aef3495e9Adam Powell } 2516d0d2cda9d414da73773285d7fee9e13aef3495e9Adam Powell 2517d0d2cda9d414da73773285d7fee9e13aef3495e9Adam Powell /** 2518d0d2cda9d414da73773285d7fee9e13aef3495e9Adam Powell * Return the route in this group at the specified index 2519d0d2cda9d414da73773285d7fee9e13aef3495e9Adam Powell * 2520d0d2cda9d414da73773285d7fee9e13aef3495e9Adam Powell * @param index Index to fetch 2521d0d2cda9d414da73773285d7fee9e13aef3495e9Adam Powell * @return The route at index 2522d0d2cda9d414da73773285d7fee9e13aef3495e9Adam Powell */ 2523d0d2cda9d414da73773285d7fee9e13aef3495e9Adam Powell public RouteInfo getRouteAt(int index) { 2524d0d2cda9d414da73773285d7fee9e13aef3495e9Adam Powell return mRoutes.get(index); 2525d0d2cda9d414da73773285d7fee9e13aef3495e9Adam Powell } 2526d0d2cda9d414da73773285d7fee9e13aef3495e9Adam Powell 2527ae20ae1a8aaa013813c356ae1d9541ca7ff020aeAdam Powell /** 2528ae20ae1a8aaa013813c356ae1d9541ca7ff020aeAdam Powell * Set an icon that will be used to represent this group. 2529ae20ae1a8aaa013813c356ae1d9541ca7ff020aeAdam Powell * The system may use this icon in picker UIs or similar. 2530ae20ae1a8aaa013813c356ae1d9541ca7ff020aeAdam Powell * 2531ae20ae1a8aaa013813c356ae1d9541ca7ff020aeAdam Powell * @param icon icon drawable to use to represent this group 2532ae20ae1a8aaa013813c356ae1d9541ca7ff020aeAdam Powell */ 2533ae20ae1a8aaa013813c356ae1d9541ca7ff020aeAdam Powell public void setIconDrawable(Drawable icon) { 2534ae20ae1a8aaa013813c356ae1d9541ca7ff020aeAdam Powell mIcon = icon; 2535ae20ae1a8aaa013813c356ae1d9541ca7ff020aeAdam Powell } 2536ae20ae1a8aaa013813c356ae1d9541ca7ff020aeAdam Powell 2537ae20ae1a8aaa013813c356ae1d9541ca7ff020aeAdam Powell /** 2538ae20ae1a8aaa013813c356ae1d9541ca7ff020aeAdam Powell * Set an icon that will be used to represent this group. 2539ae20ae1a8aaa013813c356ae1d9541ca7ff020aeAdam Powell * The system may use this icon in picker UIs or similar. 2540ae20ae1a8aaa013813c356ae1d9541ca7ff020aeAdam Powell * 254171c69897ad0a55d590698bfa399bfe99c763b9dbAdam Powell * @param resId Resource ID of an icon drawable to use to represent this group 2542ae20ae1a8aaa013813c356ae1d9541ca7ff020aeAdam Powell */ 25437b9c912f536925ac6ec43935d6e97506851b33d6Tor Norbye public void setIconResource(@DrawableRes int resId) { 2544ae20ae1a8aaa013813c356ae1d9541ca7ff020aeAdam Powell setIconDrawable(sStatic.mResources.getDrawable(resId)); 2545ae20ae1a8aaa013813c356ae1d9541ca7ff020aeAdam Powell } 2546ae20ae1a8aaa013813c356ae1d9541ca7ff020aeAdam Powell 25478e37a85bf3dc39519942698dc90a3951306b934bAdam Powell @Override 25488e37a85bf3dc39519942698dc90a3951306b934bAdam Powell public void requestSetVolume(int volume) { 25498e37a85bf3dc39519942698dc90a3951306b934bAdam Powell final int maxVol = getVolumeMax(); 25508e37a85bf3dc39519942698dc90a3951306b934bAdam Powell if (maxVol == 0) { 25518e37a85bf3dc39519942698dc90a3951306b934bAdam Powell return; 25528e37a85bf3dc39519942698dc90a3951306b934bAdam Powell } 25538e37a85bf3dc39519942698dc90a3951306b934bAdam Powell 25548e37a85bf3dc39519942698dc90a3951306b934bAdam Powell final float scaledVolume = (float) volume / maxVol; 25558e37a85bf3dc39519942698dc90a3951306b934bAdam Powell final int routeCount = getRouteCount(); 25568e37a85bf3dc39519942698dc90a3951306b934bAdam Powell for (int i = 0; i < routeCount; i++) { 25578e37a85bf3dc39519942698dc90a3951306b934bAdam Powell final RouteInfo route = getRouteAt(i); 25588e37a85bf3dc39519942698dc90a3951306b934bAdam Powell final int routeVol = (int) (scaledVolume * route.getVolumeMax()); 25598e37a85bf3dc39519942698dc90a3951306b934bAdam Powell route.requestSetVolume(routeVol); 25608e37a85bf3dc39519942698dc90a3951306b934bAdam Powell } 25618e37a85bf3dc39519942698dc90a3951306b934bAdam Powell if (volume != mVolume) { 25628e37a85bf3dc39519942698dc90a3951306b934bAdam Powell mVolume = volume; 25638e37a85bf3dc39519942698dc90a3951306b934bAdam Powell dispatchRouteVolumeChanged(this); 25648e37a85bf3dc39519942698dc90a3951306b934bAdam Powell } 25658e37a85bf3dc39519942698dc90a3951306b934bAdam Powell } 25668e37a85bf3dc39519942698dc90a3951306b934bAdam Powell 25678e37a85bf3dc39519942698dc90a3951306b934bAdam Powell @Override 25688e37a85bf3dc39519942698dc90a3951306b934bAdam Powell public void requestUpdateVolume(int direction) { 25698e37a85bf3dc39519942698dc90a3951306b934bAdam Powell final int maxVol = getVolumeMax(); 25708e37a85bf3dc39519942698dc90a3951306b934bAdam Powell if (maxVol == 0) { 25718e37a85bf3dc39519942698dc90a3951306b934bAdam Powell return; 25728e37a85bf3dc39519942698dc90a3951306b934bAdam Powell } 25738e37a85bf3dc39519942698dc90a3951306b934bAdam Powell 25748e37a85bf3dc39519942698dc90a3951306b934bAdam Powell final int routeCount = getRouteCount(); 2575f8ac14a7f5a59b4ec8e89283a2da40b626e42065Adam Powell int volume = 0; 25768e37a85bf3dc39519942698dc90a3951306b934bAdam Powell for (int i = 0; i < routeCount; i++) { 25778e37a85bf3dc39519942698dc90a3951306b934bAdam Powell final RouteInfo route = getRouteAt(i); 25788e37a85bf3dc39519942698dc90a3951306b934bAdam Powell route.requestUpdateVolume(direction); 2579f8ac14a7f5a59b4ec8e89283a2da40b626e42065Adam Powell final int routeVol = route.getVolume(); 2580f8ac14a7f5a59b4ec8e89283a2da40b626e42065Adam Powell if (routeVol > volume) { 2581f8ac14a7f5a59b4ec8e89283a2da40b626e42065Adam Powell volume = routeVol; 2582f8ac14a7f5a59b4ec8e89283a2da40b626e42065Adam Powell } 25838e37a85bf3dc39519942698dc90a3951306b934bAdam Powell } 25848e37a85bf3dc39519942698dc90a3951306b934bAdam Powell if (volume != mVolume) { 25858e37a85bf3dc39519942698dc90a3951306b934bAdam Powell mVolume = volume; 25868e37a85bf3dc39519942698dc90a3951306b934bAdam Powell dispatchRouteVolumeChanged(this); 25878e37a85bf3dc39519942698dc90a3951306b934bAdam Powell } 25888e37a85bf3dc39519942698dc90a3951306b934bAdam Powell } 25898e37a85bf3dc39519942698dc90a3951306b934bAdam Powell 25909a1de308cea2d160778fd977825f10a07b49d738Adam Powell void memberNameChanged(RouteInfo info, CharSequence name) { 25919a1de308cea2d160778fd977825f10a07b49d738Adam Powell mUpdateName = true; 25929a1de308cea2d160778fd977825f10a07b49d738Adam Powell routeUpdated(); 25939a1de308cea2d160778fd977825f10a07b49d738Adam Powell } 25949a1de308cea2d160778fd977825f10a07b49d738Adam Powell 25959a1de308cea2d160778fd977825f10a07b49d738Adam Powell void memberStatusChanged(RouteInfo info, CharSequence status) { 25969a1de308cea2d160778fd977825f10a07b49d738Adam Powell setStatusInt(status); 25979a1de308cea2d160778fd977825f10a07b49d738Adam Powell } 25989a1de308cea2d160778fd977825f10a07b49d738Adam Powell 2599f8ac14a7f5a59b4ec8e89283a2da40b626e42065Adam Powell void memberVolumeChanged(RouteInfo info) { 2600f8ac14a7f5a59b4ec8e89283a2da40b626e42065Adam Powell updateVolume(); 2601f8ac14a7f5a59b4ec8e89283a2da40b626e42065Adam Powell } 2602f8ac14a7f5a59b4ec8e89283a2da40b626e42065Adam Powell 2603f8ac14a7f5a59b4ec8e89283a2da40b626e42065Adam Powell void updateVolume() { 2604f8ac14a7f5a59b4ec8e89283a2da40b626e42065Adam Powell // A group always represents the highest component volume value. 2605f8ac14a7f5a59b4ec8e89283a2da40b626e42065Adam Powell final int routeCount = getRouteCount(); 2606f8ac14a7f5a59b4ec8e89283a2da40b626e42065Adam Powell int volume = 0; 2607f8ac14a7f5a59b4ec8e89283a2da40b626e42065Adam Powell for (int i = 0; i < routeCount; i++) { 2608f8ac14a7f5a59b4ec8e89283a2da40b626e42065Adam Powell final int routeVol = getRouteAt(i).getVolume(); 2609f8ac14a7f5a59b4ec8e89283a2da40b626e42065Adam Powell if (routeVol > volume) { 2610f8ac14a7f5a59b4ec8e89283a2da40b626e42065Adam Powell volume = routeVol; 2611f8ac14a7f5a59b4ec8e89283a2da40b626e42065Adam Powell } 2612f8ac14a7f5a59b4ec8e89283a2da40b626e42065Adam Powell } 2613f8ac14a7f5a59b4ec8e89283a2da40b626e42065Adam Powell if (volume != mVolume) { 2614f8ac14a7f5a59b4ec8e89283a2da40b626e42065Adam Powell mVolume = volume; 2615f8ac14a7f5a59b4ec8e89283a2da40b626e42065Adam Powell dispatchRouteVolumeChanged(this); 2616f8ac14a7f5a59b4ec8e89283a2da40b626e42065Adam Powell } 2617f8ac14a7f5a59b4ec8e89283a2da40b626e42065Adam Powell } 2618f8ac14a7f5a59b4ec8e89283a2da40b626e42065Adam Powell 2619d6d0bddee363e0c7fe61f63bd9d9864a71d887d6Adam Powell @Override 2620d6d0bddee363e0c7fe61f63bd9d9864a71d887d6Adam Powell void routeUpdated() { 2621d6d0bddee363e0c7fe61f63bd9d9864a71d887d6Adam Powell int types = 0; 2622d6d0bddee363e0c7fe61f63bd9d9864a71d887d6Adam Powell final int count = mRoutes.size(); 2623b5e2af5919351486a385effe77409d2a91ae9c19Adam Powell if (count == 0) { 2624b5e2af5919351486a385effe77409d2a91ae9c19Adam Powell // Don't keep empty groups in the router. 262569b07161bebdb2c726e3a826c2268866f1a94517Jeff Brown MediaRouter.removeRouteStatic(this); 2626b5e2af5919351486a385effe77409d2a91ae9c19Adam Powell return; 2627b5e2af5919351486a385effe77409d2a91ae9c19Adam Powell } 2628b5e2af5919351486a385effe77409d2a91ae9c19Adam Powell 26298e37a85bf3dc39519942698dc90a3951306b934bAdam Powell int maxVolume = 0; 26308e37a85bf3dc39519942698dc90a3951306b934bAdam Powell boolean isLocal = true; 26318e37a85bf3dc39519942698dc90a3951306b934bAdam Powell boolean isFixedVolume = true; 2632d6d0bddee363e0c7fe61f63bd9d9864a71d887d6Adam Powell for (int i = 0; i < count; i++) { 26338e37a85bf3dc39519942698dc90a3951306b934bAdam Powell final RouteInfo route = mRoutes.get(i); 26348e37a85bf3dc39519942698dc90a3951306b934bAdam Powell types |= route.mSupportedTypes; 26358e37a85bf3dc39519942698dc90a3951306b934bAdam Powell final int routeMaxVolume = route.getVolumeMax(); 26368e37a85bf3dc39519942698dc90a3951306b934bAdam Powell if (routeMaxVolume > maxVolume) { 26378e37a85bf3dc39519942698dc90a3951306b934bAdam Powell maxVolume = routeMaxVolume; 26388e37a85bf3dc39519942698dc90a3951306b934bAdam Powell } 26398e37a85bf3dc39519942698dc90a3951306b934bAdam Powell isLocal &= route.getPlaybackType() == PLAYBACK_TYPE_LOCAL; 26408e37a85bf3dc39519942698dc90a3951306b934bAdam Powell isFixedVolume &= route.getVolumeHandling() == PLAYBACK_VOLUME_FIXED; 2641d6d0bddee363e0c7fe61f63bd9d9864a71d887d6Adam Powell } 26428e37a85bf3dc39519942698dc90a3951306b934bAdam Powell mPlaybackType = isLocal ? PLAYBACK_TYPE_LOCAL : PLAYBACK_TYPE_REMOTE; 26438e37a85bf3dc39519942698dc90a3951306b934bAdam Powell mVolumeHandling = isFixedVolume ? PLAYBACK_VOLUME_FIXED : PLAYBACK_VOLUME_VARIABLE; 2644d6d0bddee363e0c7fe61f63bd9d9864a71d887d6Adam Powell mSupportedTypes = types; 26458e37a85bf3dc39519942698dc90a3951306b934bAdam Powell mVolumeMax = maxVolume; 2646d6d0bddee363e0c7fe61f63bd9d9864a71d887d6Adam Powell mIcon = count == 1 ? mRoutes.get(0).getIconDrawable() : null; 2647d6d0bddee363e0c7fe61f63bd9d9864a71d887d6Adam Powell super.routeUpdated(); 2648d6d0bddee363e0c7fe61f63bd9d9864a71d887d6Adam Powell } 2649d6d0bddee363e0c7fe61f63bd9d9864a71d887d6Adam Powell 26509a1de308cea2d160778fd977825f10a07b49d738Adam Powell void updateName() { 26519a1de308cea2d160778fd977825f10a07b49d738Adam Powell final StringBuilder sb = new StringBuilder(); 26529a1de308cea2d160778fd977825f10a07b49d738Adam Powell final int count = mRoutes.size(); 26539a1de308cea2d160778fd977825f10a07b49d738Adam Powell for (int i = 0; i < count; i++) { 26549a1de308cea2d160778fd977825f10a07b49d738Adam Powell final RouteInfo info = mRoutes.get(i); 2655b5e2af5919351486a385effe77409d2a91ae9c19Adam Powell // TODO: There's probably a much more correct way to localize this. 2656eadd6d2329972129c96dee390a8b786b37313d17Hyundo Moon if (i > 0) { 2657eadd6d2329972129c96dee390a8b786b37313d17Hyundo Moon sb.append(", "); 2658eadd6d2329972129c96dee390a8b786b37313d17Hyundo Moon } 2659eadd6d2329972129c96dee390a8b786b37313d17Hyundo Moon sb.append(info.getName()); 26609a1de308cea2d160778fd977825f10a07b49d738Adam Powell } 26619a1de308cea2d160778fd977825f10a07b49d738Adam Powell mName = sb.toString(); 26629a1de308cea2d160778fd977825f10a07b49d738Adam Powell mUpdateName = false; 26639a1de308cea2d160778fd977825f10a07b49d738Adam Powell } 2664d6d0bddee363e0c7fe61f63bd9d9864a71d887d6Adam Powell 2665d6d0bddee363e0c7fe61f63bd9d9864a71d887d6Adam Powell @Override 2666d6d0bddee363e0c7fe61f63bd9d9864a71d887d6Adam Powell public String toString() { 2667d6d0bddee363e0c7fe61f63bd9d9864a71d887d6Adam Powell StringBuilder sb = new StringBuilder(super.toString()); 2668d6d0bddee363e0c7fe61f63bd9d9864a71d887d6Adam Powell sb.append('['); 2669d6d0bddee363e0c7fe61f63bd9d9864a71d887d6Adam Powell final int count = mRoutes.size(); 2670d6d0bddee363e0c7fe61f63bd9d9864a71d887d6Adam Powell for (int i = 0; i < count; i++) { 2671d6d0bddee363e0c7fe61f63bd9d9864a71d887d6Adam Powell if (i > 0) sb.append(", "); 2672d6d0bddee363e0c7fe61f63bd9d9864a71d887d6Adam Powell sb.append(mRoutes.get(i)); 2673d6d0bddee363e0c7fe61f63bd9d9864a71d887d6Adam Powell } 2674d6d0bddee363e0c7fe61f63bd9d9864a71d887d6Adam Powell sb.append(']'); 2675d6d0bddee363e0c7fe61f63bd9d9864a71d887d6Adam Powell return sb.toString(); 2676d6d0bddee363e0c7fe61f63bd9d9864a71d887d6Adam Powell } 26779a1de308cea2d160778fd977825f10a07b49d738Adam Powell } 26789a1de308cea2d160778fd977825f10a07b49d738Adam Powell 26799a1de308cea2d160778fd977825f10a07b49d738Adam Powell /** 26809a1de308cea2d160778fd977825f10a07b49d738Adam Powell * Definition of a category of routes. All routes belong to a category. 26819a1de308cea2d160778fd977825f10a07b49d738Adam Powell */ 2682b58b8f832d06b0ffa8886eba5a4916578a3b8743Dianne Hackborn public static class RouteCategory { 26839a1de308cea2d160778fd977825f10a07b49d738Adam Powell CharSequence mName; 26840d03c042f90bf62d5bad7c64e77028a5f9f8fae0Adam Powell int mNameResId; 26859a1de308cea2d160778fd977825f10a07b49d738Adam Powell int mTypes; 26869a1de308cea2d160778fd977825f10a07b49d738Adam Powell final boolean mGroupable; 2687705ab808cf023e0cc38c2ba7cdb9571942cdc04fAdam Powell boolean mIsSystem; 26889a1de308cea2d160778fd977825f10a07b49d738Adam Powell 26899a1de308cea2d160778fd977825f10a07b49d738Adam Powell RouteCategory(CharSequence name, int types, boolean groupable) { 26909a1de308cea2d160778fd977825f10a07b49d738Adam Powell mName = name; 26919a1de308cea2d160778fd977825f10a07b49d738Adam Powell mTypes = types; 26929a1de308cea2d160778fd977825f10a07b49d738Adam Powell mGroupable = groupable; 26939a1de308cea2d160778fd977825f10a07b49d738Adam Powell } 26949a1de308cea2d160778fd977825f10a07b49d738Adam Powell 26950d03c042f90bf62d5bad7c64e77028a5f9f8fae0Adam Powell RouteCategory(int nameResId, int types, boolean groupable) { 26960d03c042f90bf62d5bad7c64e77028a5f9f8fae0Adam Powell mNameResId = nameResId; 26970d03c042f90bf62d5bad7c64e77028a5f9f8fae0Adam Powell mTypes = types; 26980d03c042f90bf62d5bad7c64e77028a5f9f8fae0Adam Powell mGroupable = groupable; 26990d03c042f90bf62d5bad7c64e77028a5f9f8fae0Adam Powell } 27000d03c042f90bf62d5bad7c64e77028a5f9f8fae0Adam Powell 27019a1de308cea2d160778fd977825f10a07b49d738Adam Powell /** 27029a1de308cea2d160778fd977825f10a07b49d738Adam Powell * @return the name of this route category 27039a1de308cea2d160778fd977825f10a07b49d738Adam Powell */ 27049a1de308cea2d160778fd977825f10a07b49d738Adam Powell public CharSequence getName() { 27050d03c042f90bf62d5bad7c64e77028a5f9f8fae0Adam Powell return getName(sStatic.mResources); 27060d03c042f90bf62d5bad7c64e77028a5f9f8fae0Adam Powell } 27075d3114b64a88ac1f72becd8d46f148c666f64aa3RoboErik 27080d03c042f90bf62d5bad7c64e77028a5f9f8fae0Adam Powell /** 27090d03c042f90bf62d5bad7c64e77028a5f9f8fae0Adam Powell * Return the properly localized/configuration dependent name of this RouteCategory. 27105d3114b64a88ac1f72becd8d46f148c666f64aa3RoboErik * 27110d03c042f90bf62d5bad7c64e77028a5f9f8fae0Adam Powell * @param context Context to resolve name resources 27120d03c042f90bf62d5bad7c64e77028a5f9f8fae0Adam Powell * @return the name of this route category 27130d03c042f90bf62d5bad7c64e77028a5f9f8fae0Adam Powell */ 27140d03c042f90bf62d5bad7c64e77028a5f9f8fae0Adam Powell public CharSequence getName(Context context) { 27150d03c042f90bf62d5bad7c64e77028a5f9f8fae0Adam Powell return getName(context.getResources()); 27160d03c042f90bf62d5bad7c64e77028a5f9f8fae0Adam Powell } 27175d3114b64a88ac1f72becd8d46f148c666f64aa3RoboErik 27180d03c042f90bf62d5bad7c64e77028a5f9f8fae0Adam Powell CharSequence getName(Resources res) { 27190d03c042f90bf62d5bad7c64e77028a5f9f8fae0Adam Powell if (mNameResId != 0) { 27200d03c042f90bf62d5bad7c64e77028a5f9f8fae0Adam Powell return res.getText(mNameResId); 27210d03c042f90bf62d5bad7c64e77028a5f9f8fae0Adam Powell } 27229a1de308cea2d160778fd977825f10a07b49d738Adam Powell return mName; 27239a1de308cea2d160778fd977825f10a07b49d738Adam Powell } 27249a1de308cea2d160778fd977825f10a07b49d738Adam Powell 27259a1de308cea2d160778fd977825f10a07b49d738Adam Powell /** 2726d0d2cda9d414da73773285d7fee9e13aef3495e9Adam Powell * Return the current list of routes in this category that have been added 2727d0d2cda9d414da73773285d7fee9e13aef3495e9Adam Powell * to the MediaRouter. 27289a1de308cea2d160778fd977825f10a07b49d738Adam Powell * 2729d0d2cda9d414da73773285d7fee9e13aef3495e9Adam Powell * <p>This list will not include routes that are nested within RouteGroups. 2730d0d2cda9d414da73773285d7fee9e13aef3495e9Adam Powell * A RouteGroup is treated as a single route within its category.</p> 2731d0d2cda9d414da73773285d7fee9e13aef3495e9Adam Powell * 2732d0d2cda9d414da73773285d7fee9e13aef3495e9Adam Powell * @param out a List to fill with the routes in this category. If this parameter is 2733d0d2cda9d414da73773285d7fee9e13aef3495e9Adam Powell * non-null, it will be cleared, filled with the current routes with this 2734d0d2cda9d414da73773285d7fee9e13aef3495e9Adam Powell * category, and returned. If this parameter is null, a new List will be 2735d0d2cda9d414da73773285d7fee9e13aef3495e9Adam Powell * allocated to report the category's current routes. 2736d0d2cda9d414da73773285d7fee9e13aef3495e9Adam Powell * @return A list with the routes in this category that have been added to the MediaRouter. 27379a1de308cea2d160778fd977825f10a07b49d738Adam Powell */ 2738d0d2cda9d414da73773285d7fee9e13aef3495e9Adam Powell public List<RouteInfo> getRoutes(List<RouteInfo> out) { 2739d0d2cda9d414da73773285d7fee9e13aef3495e9Adam Powell if (out == null) { 2740d0d2cda9d414da73773285d7fee9e13aef3495e9Adam Powell out = new ArrayList<RouteInfo>(); 2741d0d2cda9d414da73773285d7fee9e13aef3495e9Adam Powell } else { 2742d0d2cda9d414da73773285d7fee9e13aef3495e9Adam Powell out.clear(); 2743d0d2cda9d414da73773285d7fee9e13aef3495e9Adam Powell } 2744d0d2cda9d414da73773285d7fee9e13aef3495e9Adam Powell 2745b58b8f832d06b0ffa8886eba5a4916578a3b8743Dianne Hackborn final int count = getRouteCountStatic(); 2746d0d2cda9d414da73773285d7fee9e13aef3495e9Adam Powell for (int i = 0; i < count; i++) { 2747b58b8f832d06b0ffa8886eba5a4916578a3b8743Dianne Hackborn final RouteInfo route = getRouteAtStatic(i); 2748d0d2cda9d414da73773285d7fee9e13aef3495e9Adam Powell if (route.mCategory == this) { 2749d0d2cda9d414da73773285d7fee9e13aef3495e9Adam Powell out.add(route); 2750d0d2cda9d414da73773285d7fee9e13aef3495e9Adam Powell } 2751d0d2cda9d414da73773285d7fee9e13aef3495e9Adam Powell } 2752d0d2cda9d414da73773285d7fee9e13aef3495e9Adam Powell return out; 27539a1de308cea2d160778fd977825f10a07b49d738Adam Powell } 27549a1de308cea2d160778fd977825f10a07b49d738Adam Powell 27559a1de308cea2d160778fd977825f10a07b49d738Adam Powell /** 27569a1de308cea2d160778fd977825f10a07b49d738Adam Powell * @return Flag set describing the route types supported by this category 27579a1de308cea2d160778fd977825f10a07b49d738Adam Powell */ 27589a1de308cea2d160778fd977825f10a07b49d738Adam Powell public int getSupportedTypes() { 27599a1de308cea2d160778fd977825f10a07b49d738Adam Powell return mTypes; 27609a1de308cea2d160778fd977825f10a07b49d738Adam Powell } 27619a1de308cea2d160778fd977825f10a07b49d738Adam Powell 27629a1de308cea2d160778fd977825f10a07b49d738Adam Powell /** 27639a1de308cea2d160778fd977825f10a07b49d738Adam Powell * Return whether or not this category supports grouping. 27649a1de308cea2d160778fd977825f10a07b49d738Adam Powell * 27659a1de308cea2d160778fd977825f10a07b49d738Adam Powell * <p>If this method returns true, all routes obtained from this category 2766d0d2cda9d414da73773285d7fee9e13aef3495e9Adam Powell * via calls to {@link #getRouteAt(int)} will be {@link MediaRouter.RouteGroup}s.</p> 27679a1de308cea2d160778fd977825f10a07b49d738Adam Powell * 27689a1de308cea2d160778fd977825f10a07b49d738Adam Powell * @return true if this category supports 27699a1de308cea2d160778fd977825f10a07b49d738Adam Powell */ 27709a1de308cea2d160778fd977825f10a07b49d738Adam Powell public boolean isGroupable() { 27719a1de308cea2d160778fd977825f10a07b49d738Adam Powell return mGroupable; 27729a1de308cea2d160778fd977825f10a07b49d738Adam Powell } 27739a1de308cea2d160778fd977825f10a07b49d738Adam Powell 2774705ab808cf023e0cc38c2ba7cdb9571942cdc04fAdam Powell /** 2775705ab808cf023e0cc38c2ba7cdb9571942cdc04fAdam Powell * @return true if this is the category reserved for system routes. 2776705ab808cf023e0cc38c2ba7cdb9571942cdc04fAdam Powell * @hide 2777705ab808cf023e0cc38c2ba7cdb9571942cdc04fAdam Powell */ 2778705ab808cf023e0cc38c2ba7cdb9571942cdc04fAdam Powell public boolean isSystem() { 2779705ab808cf023e0cc38c2ba7cdb9571942cdc04fAdam Powell return mIsSystem; 2780705ab808cf023e0cc38c2ba7cdb9571942cdc04fAdam Powell } 2781705ab808cf023e0cc38c2ba7cdb9571942cdc04fAdam Powell 278269b07161bebdb2c726e3a826c2268866f1a94517Jeff Brown @Override 27839a1de308cea2d160778fd977825f10a07b49d738Adam Powell public String toString() { 2784eadd6d2329972129c96dee390a8b786b37313d17Hyundo Moon return "RouteCategory{ name=" + getName() + " types=" + typesToString(mTypes) + 2785d6d0bddee363e0c7fe61f63bd9d9864a71d887d6Adam Powell " groupable=" + mGroupable + " }"; 27869a1de308cea2d160778fd977825f10a07b49d738Adam Powell } 27879a1de308cea2d160778fd977825f10a07b49d738Adam Powell } 27889a1de308cea2d160778fd977825f10a07b49d738Adam Powell 27899a1de308cea2d160778fd977825f10a07b49d738Adam Powell static class CallbackInfo { 27909a1de308cea2d160778fd977825f10a07b49d738Adam Powell public int type; 279166f3b39ec452a8a03e3254e77e19ccb764ee931fJeff Brown public int flags; 2792b58b8f832d06b0ffa8886eba5a4916578a3b8743Dianne Hackborn public final Callback cb; 2793b58b8f832d06b0ffa8886eba5a4916578a3b8743Dianne Hackborn public final MediaRouter router; 27949a1de308cea2d160778fd977825f10a07b49d738Adam Powell 279566f3b39ec452a8a03e3254e77e19ccb764ee931fJeff Brown public CallbackInfo(Callback cb, int type, int flags, MediaRouter router) { 27969a1de308cea2d160778fd977825f10a07b49d738Adam Powell this.cb = cb; 27979a1de308cea2d160778fd977825f10a07b49d738Adam Powell this.type = type; 279866f3b39ec452a8a03e3254e77e19ccb764ee931fJeff Brown this.flags = flags; 2799b58b8f832d06b0ffa8886eba5a4916578a3b8743Dianne Hackborn this.router = router; 28009a1de308cea2d160778fd977825f10a07b49d738Adam Powell } 280166f3b39ec452a8a03e3254e77e19ccb764ee931fJeff Brown 280266f3b39ec452a8a03e3254e77e19ccb764ee931fJeff Brown public boolean filterRouteEvent(RouteInfo route) { 2803af574183c274f51d04487a9c8355e9f34a1150f2Jeff Brown return filterRouteEvent(route.mSupportedTypes); 2804af574183c274f51d04487a9c8355e9f34a1150f2Jeff Brown } 2805af574183c274f51d04487a9c8355e9f34a1150f2Jeff Brown 2806af574183c274f51d04487a9c8355e9f34a1150f2Jeff Brown public boolean filterRouteEvent(int supportedTypes) { 280766f3b39ec452a8a03e3254e77e19ccb764ee931fJeff Brown return (flags & CALLBACK_FLAG_UNFILTERED_EVENTS) != 0 2808af574183c274f51d04487a9c8355e9f34a1150f2Jeff Brown || (type & supportedTypes) != 0; 280966f3b39ec452a8a03e3254e77e19ccb764ee931fJeff Brown } 28109a1de308cea2d160778fd977825f10a07b49d738Adam Powell } 28119a1de308cea2d160778fd977825f10a07b49d738Adam Powell 28129a1de308cea2d160778fd977825f10a07b49d738Adam Powell /** 28139a1de308cea2d160778fd977825f10a07b49d738Adam Powell * Interface for receiving events about media routing changes. 28149a1de308cea2d160778fd977825f10a07b49d738Adam Powell * All methods of this interface will be called from the application's main thread. 281566f3b39ec452a8a03e3254e77e19ccb764ee931fJeff Brown * <p> 281666f3b39ec452a8a03e3254e77e19ccb764ee931fJeff Brown * A Callback will only receive events relevant to routes that the callback 281766f3b39ec452a8a03e3254e77e19ccb764ee931fJeff Brown * was registered for unless the {@link MediaRouter#CALLBACK_FLAG_UNFILTERED_EVENTS} 281866f3b39ec452a8a03e3254e77e19ccb764ee931fJeff Brown * flag was specified in {@link MediaRouter#addCallback(int, Callback, int)}. 281966f3b39ec452a8a03e3254e77e19ccb764ee931fJeff Brown * </p> 28209a1de308cea2d160778fd977825f10a07b49d738Adam Powell * 282166f3b39ec452a8a03e3254e77e19ccb764ee931fJeff Brown * @see MediaRouter#addCallback(int, Callback, int) 28229a1de308cea2d160778fd977825f10a07b49d738Adam Powell * @see MediaRouter#removeCallback(Callback) 28239a1de308cea2d160778fd977825f10a07b49d738Adam Powell */ 28240d03c042f90bf62d5bad7c64e77028a5f9f8fae0Adam Powell public static abstract class Callback { 28259a1de308cea2d160778fd977825f10a07b49d738Adam Powell /** 28269a1de308cea2d160778fd977825f10a07b49d738Adam Powell * Called when the supplied route becomes selected as the active route 28279a1de308cea2d160778fd977825f10a07b49d738Adam Powell * for the given route type. 28289a1de308cea2d160778fd977825f10a07b49d738Adam Powell * 2829d0d2cda9d414da73773285d7fee9e13aef3495e9Adam Powell * @param router the MediaRouter reporting the event 28309a1de308cea2d160778fd977825f10a07b49d738Adam Powell * @param type Type flag set indicating the routes that have been selected 28319a1de308cea2d160778fd977825f10a07b49d738Adam Powell * @param info Route that has been selected for the given route types 28329a1de308cea2d160778fd977825f10a07b49d738Adam Powell */ 28330d03c042f90bf62d5bad7c64e77028a5f9f8fae0Adam Powell public abstract void onRouteSelected(MediaRouter router, int type, RouteInfo info); 28349a1de308cea2d160778fd977825f10a07b49d738Adam Powell 28359a1de308cea2d160778fd977825f10a07b49d738Adam Powell /** 28369a1de308cea2d160778fd977825f10a07b49d738Adam Powell * Called when the supplied route becomes unselected as the active route 28379a1de308cea2d160778fd977825f10a07b49d738Adam Powell * for the given route type. 28389a1de308cea2d160778fd977825f10a07b49d738Adam Powell * 2839d0d2cda9d414da73773285d7fee9e13aef3495e9Adam Powell * @param router the MediaRouter reporting the event 28409a1de308cea2d160778fd977825f10a07b49d738Adam Powell * @param type Type flag set indicating the routes that have been unselected 28419a1de308cea2d160778fd977825f10a07b49d738Adam Powell * @param info Route that has been unselected for the given route types 28429a1de308cea2d160778fd977825f10a07b49d738Adam Powell */ 28430d03c042f90bf62d5bad7c64e77028a5f9f8fae0Adam Powell public abstract void onRouteUnselected(MediaRouter router, int type, RouteInfo info); 28449a1de308cea2d160778fd977825f10a07b49d738Adam Powell 28459a1de308cea2d160778fd977825f10a07b49d738Adam Powell /** 28469a1de308cea2d160778fd977825f10a07b49d738Adam Powell * Called when a route for the specified type was added. 28479a1de308cea2d160778fd977825f10a07b49d738Adam Powell * 2848d0d2cda9d414da73773285d7fee9e13aef3495e9Adam Powell * @param router the MediaRouter reporting the event 28499a1de308cea2d160778fd977825f10a07b49d738Adam Powell * @param info Route that has become available for use 28509a1de308cea2d160778fd977825f10a07b49d738Adam Powell */ 28510d03c042f90bf62d5bad7c64e77028a5f9f8fae0Adam Powell public abstract void onRouteAdded(MediaRouter router, RouteInfo info); 28529a1de308cea2d160778fd977825f10a07b49d738Adam Powell 28539a1de308cea2d160778fd977825f10a07b49d738Adam Powell /** 28549a1de308cea2d160778fd977825f10a07b49d738Adam Powell * Called when a route for the specified type was removed. 28559a1de308cea2d160778fd977825f10a07b49d738Adam Powell * 2856d0d2cda9d414da73773285d7fee9e13aef3495e9Adam Powell * @param router the MediaRouter reporting the event 28579a1de308cea2d160778fd977825f10a07b49d738Adam Powell * @param info Route that has been removed from availability 28589a1de308cea2d160778fd977825f10a07b49d738Adam Powell */ 28590d03c042f90bf62d5bad7c64e77028a5f9f8fae0Adam Powell public abstract void onRouteRemoved(MediaRouter router, RouteInfo info); 28609a1de308cea2d160778fd977825f10a07b49d738Adam Powell 28619a1de308cea2d160778fd977825f10a07b49d738Adam Powell /** 28629a1de308cea2d160778fd977825f10a07b49d738Adam Powell * Called when an aspect of the indicated route has changed. 28639a1de308cea2d160778fd977825f10a07b49d738Adam Powell * 28649a1de308cea2d160778fd977825f10a07b49d738Adam Powell * <p>This will not indicate that the types supported by this route have 28659a1de308cea2d160778fd977825f10a07b49d738Adam Powell * changed, only that cosmetic info such as name or status have been updated.</p> 28669a1de308cea2d160778fd977825f10a07b49d738Adam Powell * 2867d0d2cda9d414da73773285d7fee9e13aef3495e9Adam Powell * @param router the MediaRouter reporting the event 28689a1de308cea2d160778fd977825f10a07b49d738Adam Powell * @param info The route that was changed 28699a1de308cea2d160778fd977825f10a07b49d738Adam Powell */ 28700d03c042f90bf62d5bad7c64e77028a5f9f8fae0Adam Powell public abstract void onRouteChanged(MediaRouter router, RouteInfo info); 2871d0d2cda9d414da73773285d7fee9e13aef3495e9Adam Powell 2872d0d2cda9d414da73773285d7fee9e13aef3495e9Adam Powell /** 2873d0d2cda9d414da73773285d7fee9e13aef3495e9Adam Powell * Called when a route is added to a group. 2874d0d2cda9d414da73773285d7fee9e13aef3495e9Adam Powell * 2875d0d2cda9d414da73773285d7fee9e13aef3495e9Adam Powell * @param router the MediaRouter reporting the event 2876d0d2cda9d414da73773285d7fee9e13aef3495e9Adam Powell * @param info The route that was added 2877d0d2cda9d414da73773285d7fee9e13aef3495e9Adam Powell * @param group The group the route was added to 2878d0d2cda9d414da73773285d7fee9e13aef3495e9Adam Powell * @param index The route index within group that info was added at 2879d0d2cda9d414da73773285d7fee9e13aef3495e9Adam Powell */ 28800d03c042f90bf62d5bad7c64e77028a5f9f8fae0Adam Powell public abstract void onRouteGrouped(MediaRouter router, RouteInfo info, RouteGroup group, 28810d03c042f90bf62d5bad7c64e77028a5f9f8fae0Adam Powell int index); 2882d0d2cda9d414da73773285d7fee9e13aef3495e9Adam Powell 2883d0d2cda9d414da73773285d7fee9e13aef3495e9Adam Powell /** 2884d0d2cda9d414da73773285d7fee9e13aef3495e9Adam Powell * Called when a route is removed from a group. 2885d0d2cda9d414da73773285d7fee9e13aef3495e9Adam Powell * 2886d0d2cda9d414da73773285d7fee9e13aef3495e9Adam Powell * @param router the MediaRouter reporting the event 2887d0d2cda9d414da73773285d7fee9e13aef3495e9Adam Powell * @param info The route that was removed 2888d0d2cda9d414da73773285d7fee9e13aef3495e9Adam Powell * @param group The group the route was removed from 2889d0d2cda9d414da73773285d7fee9e13aef3495e9Adam Powell */ 28900d03c042f90bf62d5bad7c64e77028a5f9f8fae0Adam Powell public abstract void onRouteUngrouped(MediaRouter router, RouteInfo info, RouteGroup group); 28918e37a85bf3dc39519942698dc90a3951306b934bAdam Powell 28928e37a85bf3dc39519942698dc90a3951306b934bAdam Powell /** 28938e37a85bf3dc39519942698dc90a3951306b934bAdam Powell * Called when a route's volume changes. 28948e37a85bf3dc39519942698dc90a3951306b934bAdam Powell * 28958e37a85bf3dc39519942698dc90a3951306b934bAdam Powell * @param router the MediaRouter reporting the event 28968e37a85bf3dc39519942698dc90a3951306b934bAdam Powell * @param info The route with altered volume 28978e37a85bf3dc39519942698dc90a3951306b934bAdam Powell */ 28988e37a85bf3dc39519942698dc90a3951306b934bAdam Powell public abstract void onRouteVolumeChanged(MediaRouter router, RouteInfo info); 289992130f6407dc51c58b3b941d28a6daf4e04b8d62Jeff Brown 290092130f6407dc51c58b3b941d28a6daf4e04b8d62Jeff Brown /** 290192130f6407dc51c58b3b941d28a6daf4e04b8d62Jeff Brown * Called when a route's presentation display changes. 290292130f6407dc51c58b3b941d28a6daf4e04b8d62Jeff Brown * <p> 290392130f6407dc51c58b3b941d28a6daf4e04b8d62Jeff Brown * This method is called whenever the route's presentation display becomes 290492130f6407dc51c58b3b941d28a6daf4e04b8d62Jeff Brown * available, is removes or has changes to some of its properties (such as its size). 290592130f6407dc51c58b3b941d28a6daf4e04b8d62Jeff Brown * </p> 290692130f6407dc51c58b3b941d28a6daf4e04b8d62Jeff Brown * 290792130f6407dc51c58b3b941d28a6daf4e04b8d62Jeff Brown * @param router the MediaRouter reporting the event 290892130f6407dc51c58b3b941d28a6daf4e04b8d62Jeff Brown * @param info The route whose presentation display changed 290992130f6407dc51c58b3b941d28a6daf4e04b8d62Jeff Brown * 291092130f6407dc51c58b3b941d28a6daf4e04b8d62Jeff Brown * @see RouteInfo#getPresentationDisplay() 291192130f6407dc51c58b3b941d28a6daf4e04b8d62Jeff Brown */ 291292130f6407dc51c58b3b941d28a6daf4e04b8d62Jeff Brown public void onRoutePresentationDisplayChanged(MediaRouter router, RouteInfo info) { 291392130f6407dc51c58b3b941d28a6daf4e04b8d62Jeff Brown } 29149a1de308cea2d160778fd977825f10a07b49d738Adam Powell } 29159a1de308cea2d160778fd977825f10a07b49d738Adam Powell 29169a1de308cea2d160778fd977825f10a07b49d738Adam Powell /** 29170d03c042f90bf62d5bad7c64e77028a5f9f8fae0Adam Powell * Stub implementation of {@link MediaRouter.Callback}. 29180d03c042f90bf62d5bad7c64e77028a5f9f8fae0Adam Powell * Each abstract method is defined as a no-op. Override just the ones 29199a1de308cea2d160778fd977825f10a07b49d738Adam Powell * you need. 29209a1de308cea2d160778fd977825f10a07b49d738Adam Powell */ 29210d03c042f90bf62d5bad7c64e77028a5f9f8fae0Adam Powell public static class SimpleCallback extends Callback { 29229a1de308cea2d160778fd977825f10a07b49d738Adam Powell 29239a1de308cea2d160778fd977825f10a07b49d738Adam Powell @Override 2924d0d2cda9d414da73773285d7fee9e13aef3495e9Adam Powell public void onRouteSelected(MediaRouter router, int type, RouteInfo info) { 29259a1de308cea2d160778fd977825f10a07b49d738Adam Powell } 29269a1de308cea2d160778fd977825f10a07b49d738Adam Powell 29279a1de308cea2d160778fd977825f10a07b49d738Adam Powell @Override 2928d0d2cda9d414da73773285d7fee9e13aef3495e9Adam Powell public void onRouteUnselected(MediaRouter router, int type, RouteInfo info) { 29299a1de308cea2d160778fd977825f10a07b49d738Adam Powell } 29309a1de308cea2d160778fd977825f10a07b49d738Adam Powell 29319a1de308cea2d160778fd977825f10a07b49d738Adam Powell @Override 2932d0d2cda9d414da73773285d7fee9e13aef3495e9Adam Powell public void onRouteAdded(MediaRouter router, RouteInfo info) { 29339a1de308cea2d160778fd977825f10a07b49d738Adam Powell } 29349a1de308cea2d160778fd977825f10a07b49d738Adam Powell 29359a1de308cea2d160778fd977825f10a07b49d738Adam Powell @Override 2936d0d2cda9d414da73773285d7fee9e13aef3495e9Adam Powell public void onRouteRemoved(MediaRouter router, RouteInfo info) { 29379a1de308cea2d160778fd977825f10a07b49d738Adam Powell } 29389a1de308cea2d160778fd977825f10a07b49d738Adam Powell 29399a1de308cea2d160778fd977825f10a07b49d738Adam Powell @Override 2940d0d2cda9d414da73773285d7fee9e13aef3495e9Adam Powell public void onRouteChanged(MediaRouter router, RouteInfo info) { 29419a1de308cea2d160778fd977825f10a07b49d738Adam Powell } 29429a1de308cea2d160778fd977825f10a07b49d738Adam Powell 29439a1de308cea2d160778fd977825f10a07b49d738Adam Powell @Override 2944d0d2cda9d414da73773285d7fee9e13aef3495e9Adam Powell public void onRouteGrouped(MediaRouter router, RouteInfo info, RouteGroup group, 2945d0d2cda9d414da73773285d7fee9e13aef3495e9Adam Powell int index) { 29469a1de308cea2d160778fd977825f10a07b49d738Adam Powell } 29479a1de308cea2d160778fd977825f10a07b49d738Adam Powell 29489a1de308cea2d160778fd977825f10a07b49d738Adam Powell @Override 2949d0d2cda9d414da73773285d7fee9e13aef3495e9Adam Powell public void onRouteUngrouped(MediaRouter router, RouteInfo info, RouteGroup group) { 29509a1de308cea2d160778fd977825f10a07b49d738Adam Powell } 2951d0d2cda9d414da73773285d7fee9e13aef3495e9Adam Powell 29528e37a85bf3dc39519942698dc90a3951306b934bAdam Powell @Override 29538e37a85bf3dc39519942698dc90a3951306b934bAdam Powell public void onRouteVolumeChanged(MediaRouter router, RouteInfo info) { 29548e37a85bf3dc39519942698dc90a3951306b934bAdam Powell } 29559a1de308cea2d160778fd977825f10a07b49d738Adam Powell } 29561357012968f9066ea3051d83995e9bac69526c3cJean-Michel Trivi 29571357012968f9066ea3051d83995e9bac69526c3cJean-Michel Trivi static class VolumeCallbackInfo { 29581357012968f9066ea3051d83995e9bac69526c3cJean-Michel Trivi public final VolumeCallback vcb; 29591357012968f9066ea3051d83995e9bac69526c3cJean-Michel Trivi public final RouteInfo route; 29601357012968f9066ea3051d83995e9bac69526c3cJean-Michel Trivi 29611357012968f9066ea3051d83995e9bac69526c3cJean-Michel Trivi public VolumeCallbackInfo(VolumeCallback vcb, RouteInfo route) { 29621357012968f9066ea3051d83995e9bac69526c3cJean-Michel Trivi this.vcb = vcb; 29631357012968f9066ea3051d83995e9bac69526c3cJean-Michel Trivi this.route = route; 29641357012968f9066ea3051d83995e9bac69526c3cJean-Michel Trivi } 29651357012968f9066ea3051d83995e9bac69526c3cJean-Michel Trivi } 29661357012968f9066ea3051d83995e9bac69526c3cJean-Michel Trivi 29671357012968f9066ea3051d83995e9bac69526c3cJean-Michel Trivi /** 29681357012968f9066ea3051d83995e9bac69526c3cJean-Michel Trivi * Interface for receiving events about volume changes. 29691357012968f9066ea3051d83995e9bac69526c3cJean-Michel Trivi * All methods of this interface will be called from the application's main thread. 29701357012968f9066ea3051d83995e9bac69526c3cJean-Michel Trivi * 29711357012968f9066ea3051d83995e9bac69526c3cJean-Michel Trivi * <p>A VolumeCallback will only receive events relevant to routes that the callback 29721357012968f9066ea3051d83995e9bac69526c3cJean-Michel Trivi * was registered for.</p> 29731357012968f9066ea3051d83995e9bac69526c3cJean-Michel Trivi * 29741357012968f9066ea3051d83995e9bac69526c3cJean-Michel Trivi * @see UserRouteInfo#setVolumeCallback(VolumeCallback) 29751357012968f9066ea3051d83995e9bac69526c3cJean-Michel Trivi */ 29761357012968f9066ea3051d83995e9bac69526c3cJean-Michel Trivi public static abstract class VolumeCallback { 29771357012968f9066ea3051d83995e9bac69526c3cJean-Michel Trivi /** 29781357012968f9066ea3051d83995e9bac69526c3cJean-Michel Trivi * Called when the volume for the route should be increased or decreased. 29791357012968f9066ea3051d83995e9bac69526c3cJean-Michel Trivi * @param info the route affected by this event 29801357012968f9066ea3051d83995e9bac69526c3cJean-Michel Trivi * @param direction an integer indicating whether the volume is to be increased 29811357012968f9066ea3051d83995e9bac69526c3cJean-Michel Trivi * (positive value) or decreased (negative value). 29821357012968f9066ea3051d83995e9bac69526c3cJean-Michel Trivi * For bundled changes, the absolute value indicates the number of changes 29831357012968f9066ea3051d83995e9bac69526c3cJean-Michel Trivi * in the same direction, e.g. +3 corresponds to three "volume up" changes. 29841357012968f9066ea3051d83995e9bac69526c3cJean-Michel Trivi */ 29851357012968f9066ea3051d83995e9bac69526c3cJean-Michel Trivi public abstract void onVolumeUpdateRequest(RouteInfo info, int direction); 29861357012968f9066ea3051d83995e9bac69526c3cJean-Michel Trivi /** 29871357012968f9066ea3051d83995e9bac69526c3cJean-Michel Trivi * Called when the volume for the route should be set to the given value 29881357012968f9066ea3051d83995e9bac69526c3cJean-Michel Trivi * @param info the route affected by this event 29891357012968f9066ea3051d83995e9bac69526c3cJean-Michel Trivi * @param volume an integer indicating the new volume value that should be used, always 29901357012968f9066ea3051d83995e9bac69526c3cJean-Michel Trivi * between 0 and the value set by {@link UserRouteInfo#setVolumeMax(int)}. 29911357012968f9066ea3051d83995e9bac69526c3cJean-Michel Trivi */ 29921357012968f9066ea3051d83995e9bac69526c3cJean-Michel Trivi public abstract void onVolumeSetRequest(RouteInfo info, int volume); 29931357012968f9066ea3051d83995e9bac69526c3cJean-Michel Trivi } 29941357012968f9066ea3051d83995e9bac69526c3cJean-Michel Trivi 29958e37a85bf3dc39519942698dc90a3951306b934bAdam Powell static class VolumeChangeReceiver extends BroadcastReceiver { 29968e37a85bf3dc39519942698dc90a3951306b934bAdam Powell @Override 29978e37a85bf3dc39519942698dc90a3951306b934bAdam Powell public void onReceive(Context context, Intent intent) { 29988e37a85bf3dc39519942698dc90a3951306b934bAdam Powell if (intent.getAction().equals(AudioManager.VOLUME_CHANGED_ACTION)) { 29998e37a85bf3dc39519942698dc90a3951306b934bAdam Powell final int streamType = intent.getIntExtra(AudioManager.EXTRA_VOLUME_STREAM_TYPE, 30008e37a85bf3dc39519942698dc90a3951306b934bAdam Powell -1); 30018e37a85bf3dc39519942698dc90a3951306b934bAdam Powell if (streamType != AudioManager.STREAM_MUSIC) { 30028e37a85bf3dc39519942698dc90a3951306b934bAdam Powell return; 30038e37a85bf3dc39519942698dc90a3951306b934bAdam Powell } 30048e37a85bf3dc39519942698dc90a3951306b934bAdam Powell 30058e37a85bf3dc39519942698dc90a3951306b934bAdam Powell final int newVolume = intent.getIntExtra(AudioManager.EXTRA_VOLUME_STREAM_VALUE, 0); 30068e37a85bf3dc39519942698dc90a3951306b934bAdam Powell final int oldVolume = intent.getIntExtra( 30078e37a85bf3dc39519942698dc90a3951306b934bAdam Powell AudioManager.EXTRA_PREV_VOLUME_STREAM_VALUE, 0); 30088e37a85bf3dc39519942698dc90a3951306b934bAdam Powell if (newVolume != oldVolume) { 30098e37a85bf3dc39519942698dc90a3951306b934bAdam Powell systemVolumeChanged(newVolume); 30108e37a85bf3dc39519942698dc90a3951306b934bAdam Powell } 30118e37a85bf3dc39519942698dc90a3951306b934bAdam Powell } 30128e37a85bf3dc39519942698dc90a3951306b934bAdam Powell } 3013705ab808cf023e0cc38c2ba7cdb9571942cdc04fAdam Powell } 30148e37a85bf3dc39519942698dc90a3951306b934bAdam Powell 3015705ab808cf023e0cc38c2ba7cdb9571942cdc04fAdam Powell static class WifiDisplayStatusChangedReceiver extends BroadcastReceiver { 3016705ab808cf023e0cc38c2ba7cdb9571942cdc04fAdam Powell @Override 3017705ab808cf023e0cc38c2ba7cdb9571942cdc04fAdam Powell public void onReceive(Context context, Intent intent) { 3018705ab808cf023e0cc38c2ba7cdb9571942cdc04fAdam Powell if (intent.getAction().equals(DisplayManager.ACTION_WIFI_DISPLAY_STATUS_CHANGED)) { 3019705ab808cf023e0cc38c2ba7cdb9571942cdc04fAdam Powell updateWifiDisplayStatus((WifiDisplayStatus) intent.getParcelableExtra( 3020705ab808cf023e0cc38c2ba7cdb9571942cdc04fAdam Powell DisplayManager.EXTRA_WIFI_DISPLAY_STATUS)); 3021705ab808cf023e0cc38c2ba7cdb9571942cdc04fAdam Powell } 3022705ab808cf023e0cc38c2ba7cdb9571942cdc04fAdam Powell } 30238e37a85bf3dc39519942698dc90a3951306b934bAdam Powell } 30249a1de308cea2d160778fd977825f10a07b49d738Adam Powell} 3025