MediaRouter.java revision 2444ae7e2b8658a4a90f996e678423558744b4a2
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 198e37a85bf3dc39519942698dc90a3951306b934bAdam Powellimport android.content.BroadcastReceiver; 209a1de308cea2d160778fd977825f10a07b49d738Adam Powellimport android.content.Context; 218e37a85bf3dc39519942698dc90a3951306b934bAdam Powellimport android.content.Intent; 228e37a85bf3dc39519942698dc90a3951306b934bAdam Powellimport android.content.IntentFilter; 23b58b8f832d06b0ffa8886eba5a4916578a3b8743Dianne Hackbornimport android.content.res.Resources; 24ae20ae1a8aaa013813c356ae1d9541ca7ff020aeAdam Powellimport android.graphics.drawable.Drawable; 25705ab808cf023e0cc38c2ba7cdb9571942cdc04fAdam Powellimport android.hardware.display.DisplayManager; 26705ab808cf023e0cc38c2ba7cdb9571942cdc04fAdam Powellimport android.hardware.display.WifiDisplay; 27705ab808cf023e0cc38c2ba7cdb9571942cdc04fAdam Powellimport android.hardware.display.WifiDisplayStatus; 289a1de308cea2d160778fd977825f10a07b49d738Adam Powellimport android.os.Handler; 29632ca417f0a33e3fa9ccece531afa2db3f0d4a30Dianne Hackbornimport android.os.IBinder; 30632ca417f0a33e3fa9ccece531afa2db3f0d4a30Dianne Hackbornimport android.os.RemoteException; 31632ca417f0a33e3fa9ccece531afa2db3f0d4a30Dianne Hackbornimport android.os.ServiceManager; 32632ca417f0a33e3fa9ccece531afa2db3f0d4a30Dianne Hackbornimport android.text.TextUtils; 339a1de308cea2d160778fd977825f10a07b49d738Adam Powellimport android.util.Log; 34705ab808cf023e0cc38c2ba7cdb9571942cdc04fAdam Powellimport android.view.Display; 35705ab808cf023e0cc38c2ba7cdb9571942cdc04fAdam Powellimport android.view.DisplayInfo; 369a1de308cea2d160778fd977825f10a07b49d738Adam Powell 379a1de308cea2d160778fd977825f10a07b49d738Adam Powellimport java.util.ArrayList; 389a1de308cea2d160778fd977825f10a07b49d738Adam Powellimport java.util.HashMap; 39d0d2cda9d414da73773285d7fee9e13aef3495e9Adam Powellimport java.util.List; 4039d5c6172503620ac3761148adac5fd7fa20d02dAdam Powellimport java.util.concurrent.CopyOnWriteArrayList; 419a1de308cea2d160778fd977825f10a07b49d738Adam Powell 429a1de308cea2d160778fd977825f10a07b49d738Adam Powell/** 439a1de308cea2d160778fd977825f10a07b49d738Adam Powell * MediaRouter allows applications to control the routing of media channels 449a1de308cea2d160778fd977825f10a07b49d738Adam Powell * and streams from the current device to external speakers and destination devices. 459a1de308cea2d160778fd977825f10a07b49d738Adam Powell * 46b58b8f832d06b0ffa8886eba5a4916578a3b8743Dianne Hackborn * <p>A MediaRouter is retrieved through {@link Context#getSystemService(String) 47b58b8f832d06b0ffa8886eba5a4916578a3b8743Dianne Hackborn * Context.getSystemService()} of a {@link Context#MEDIA_ROUTER_SERVICE 48b58b8f832d06b0ffa8886eba5a4916578a3b8743Dianne Hackborn * Context.MEDIA_ROUTER_SERVICE}. 49b58b8f832d06b0ffa8886eba5a4916578a3b8743Dianne Hackborn * 50b58b8f832d06b0ffa8886eba5a4916578a3b8743Dianne Hackborn * <p>The media router API is not thread-safe; all interactions with it must be 51b58b8f832d06b0ffa8886eba5a4916578a3b8743Dianne Hackborn * done from the main thread of the process.</p> 529a1de308cea2d160778fd977825f10a07b49d738Adam Powell */ 539a1de308cea2d160778fd977825f10a07b49d738Adam Powellpublic class MediaRouter { 549a1de308cea2d160778fd977825f10a07b49d738Adam Powell private static final String TAG = "MediaRouter"; 559a1de308cea2d160778fd977825f10a07b49d738Adam Powell 56b58b8f832d06b0ffa8886eba5a4916578a3b8743Dianne Hackborn static class Static { 57b35c445f34e1a18e17aef3e3dfbc1c39b4d1815cAdam Powell final Resources mResources; 58632ca417f0a33e3fa9ccece531afa2db3f0d4a30Dianne Hackborn final IAudioService mAudioService; 59705ab808cf023e0cc38c2ba7cdb9571942cdc04fAdam Powell final DisplayManager mDisplayService; 60b35c445f34e1a18e17aef3e3dfbc1c39b4d1815cAdam Powell final Handler mHandler; 6139d5c6172503620ac3761148adac5fd7fa20d02dAdam Powell final CopyOnWriteArrayList<CallbackInfo> mCallbacks = 6239d5c6172503620ac3761148adac5fd7fa20d02dAdam Powell new CopyOnWriteArrayList<CallbackInfo>(); 63b58b8f832d06b0ffa8886eba5a4916578a3b8743Dianne Hackborn 64b35c445f34e1a18e17aef3e3dfbc1c39b4d1815cAdam Powell final ArrayList<RouteInfo> mRoutes = new ArrayList<RouteInfo>(); 65b35c445f34e1a18e17aef3e3dfbc1c39b4d1815cAdam Powell final ArrayList<RouteCategory> mCategories = new ArrayList<RouteCategory>(); 66b58b8f832d06b0ffa8886eba5a4916578a3b8743Dianne Hackborn 67b35c445f34e1a18e17aef3e3dfbc1c39b4d1815cAdam Powell final RouteCategory mSystemCategory; 68632ca417f0a33e3fa9ccece531afa2db3f0d4a30Dianne Hackborn 69705ab808cf023e0cc38c2ba7cdb9571942cdc04fAdam Powell final AudioRoutesInfo mCurAudioRoutesInfo = new AudioRoutesInfo(); 70b58b8f832d06b0ffa8886eba5a4916578a3b8743Dianne Hackborn 71705ab808cf023e0cc38c2ba7cdb9571942cdc04fAdam Powell RouteInfo mDefaultAudioVideo; 72b35c445f34e1a18e17aef3e3dfbc1c39b4d1815cAdam Powell RouteInfo mBluetoothA2dpRoute; 73b58b8f832d06b0ffa8886eba5a4916578a3b8743Dianne Hackborn 74b35c445f34e1a18e17aef3e3dfbc1c39b4d1815cAdam Powell RouteInfo mSelectedRoute; 759a1de308cea2d160778fd977825f10a07b49d738Adam Powell 76705ab808cf023e0cc38c2ba7cdb9571942cdc04fAdam Powell WifiDisplayStatus mLastKnownWifiDisplayStatus; 77705ab808cf023e0cc38c2ba7cdb9571942cdc04fAdam Powell 78705ab808cf023e0cc38c2ba7cdb9571942cdc04fAdam Powell final IAudioRoutesObserver.Stub mAudioRoutesObserver = new IAudioRoutesObserver.Stub() { 79632ca417f0a33e3fa9ccece531afa2db3f0d4a30Dianne Hackborn public void dispatchAudioRoutesChanged(final AudioRoutesInfo newRoutes) { 80632ca417f0a33e3fa9ccece531afa2db3f0d4a30Dianne Hackborn mHandler.post(new Runnable() { 81632ca417f0a33e3fa9ccece531afa2db3f0d4a30Dianne Hackborn @Override public void run() { 82705ab808cf023e0cc38c2ba7cdb9571942cdc04fAdam Powell updateAudioRoutes(newRoutes); 83632ca417f0a33e3fa9ccece531afa2db3f0d4a30Dianne Hackborn } 84632ca417f0a33e3fa9ccece531afa2db3f0d4a30Dianne Hackborn }); 85632ca417f0a33e3fa9ccece531afa2db3f0d4a30Dianne Hackborn } 86632ca417f0a33e3fa9ccece531afa2db3f0d4a30Dianne Hackborn }; 87632ca417f0a33e3fa9ccece531afa2db3f0d4a30Dianne Hackborn 88b58b8f832d06b0ffa8886eba5a4916578a3b8743Dianne Hackborn Static(Context appContext) { 89b58b8f832d06b0ffa8886eba5a4916578a3b8743Dianne Hackborn mResources = Resources.getSystem(); 90b58b8f832d06b0ffa8886eba5a4916578a3b8743Dianne Hackborn mHandler = new Handler(appContext.getMainLooper()); 919a1de308cea2d160778fd977825f10a07b49d738Adam Powell 92632ca417f0a33e3fa9ccece531afa2db3f0d4a30Dianne Hackborn IBinder b = ServiceManager.getService(Context.AUDIO_SERVICE); 93632ca417f0a33e3fa9ccece531afa2db3f0d4a30Dianne Hackborn mAudioService = IAudioService.Stub.asInterface(b); 949a1de308cea2d160778fd977825f10a07b49d738Adam Powell 95705ab808cf023e0cc38c2ba7cdb9571942cdc04fAdam Powell mDisplayService = (DisplayManager) appContext.getSystemService(Context.DISPLAY_SERVICE); 96705ab808cf023e0cc38c2ba7cdb9571942cdc04fAdam Powell 97dd0a19266d5c837069da1ea188744d54c8d723a8Adam Powell mSystemCategory = new RouteCategory( 98dd0a19266d5c837069da1ea188744d54c8d723a8Adam Powell com.android.internal.R.string.default_audio_route_category_name, 99705ab808cf023e0cc38c2ba7cdb9571942cdc04fAdam Powell ROUTE_TYPE_LIVE_AUDIO | ROUTE_TYPE_LIVE_VIDEO, false); 100705ab808cf023e0cc38c2ba7cdb9571942cdc04fAdam Powell mSystemCategory.mIsSystem = true; 101b35c445f34e1a18e17aef3e3dfbc1c39b4d1815cAdam Powell } 102b58b8f832d06b0ffa8886eba5a4916578a3b8743Dianne Hackborn 103b35c445f34e1a18e17aef3e3dfbc1c39b4d1815cAdam Powell // Called after sStatic is initialized 1048e37a85bf3dc39519942698dc90a3951306b934bAdam Powell void startMonitoringRoutes(Context appContext) { 105705ab808cf023e0cc38c2ba7cdb9571942cdc04fAdam Powell mDefaultAudioVideo = new RouteInfo(mSystemCategory); 106705ab808cf023e0cc38c2ba7cdb9571942cdc04fAdam Powell mDefaultAudioVideo.mNameResId = com.android.internal.R.string.default_audio_route_name; 107705ab808cf023e0cc38c2ba7cdb9571942cdc04fAdam Powell mDefaultAudioVideo.mSupportedTypes = ROUTE_TYPE_LIVE_AUDIO | ROUTE_TYPE_LIVE_VIDEO; 1082ee6a2a83262d05a566bd713d238e89edfd33a29Adam Powell addRouteStatic(mDefaultAudioVideo); 109632ca417f0a33e3fa9ccece531afa2db3f0d4a30Dianne Hackborn 1102ee6a2a83262d05a566bd713d238e89edfd33a29Adam Powell // This will select the active wifi display route if there is one. 1112ee6a2a83262d05a566bd713d238e89edfd33a29Adam Powell updateWifiDisplayStatus(mDisplayService.getWifiDisplayStatus()); 1122ee6a2a83262d05a566bd713d238e89edfd33a29Adam Powell 1132ee6a2a83262d05a566bd713d238e89edfd33a29Adam Powell appContext.registerReceiver(new WifiDisplayStatusChangedReceiver(), 1142ee6a2a83262d05a566bd713d238e89edfd33a29Adam Powell new IntentFilter(DisplayManager.ACTION_WIFI_DISPLAY_STATUS_CHANGED)); 1158e37a85bf3dc39519942698dc90a3951306b934bAdam Powell appContext.registerReceiver(new VolumeChangeReceiver(), 1168e37a85bf3dc39519942698dc90a3951306b934bAdam Powell new IntentFilter(AudioManager.VOLUME_CHANGED_ACTION)); 1178e37a85bf3dc39519942698dc90a3951306b934bAdam Powell 118705ab808cf023e0cc38c2ba7cdb9571942cdc04fAdam Powell AudioRoutesInfo newAudioRoutes = null; 119632ca417f0a33e3fa9ccece531afa2db3f0d4a30Dianne Hackborn try { 120705ab808cf023e0cc38c2ba7cdb9571942cdc04fAdam Powell newAudioRoutes = mAudioService.startWatchingRoutes(mAudioRoutesObserver); 121632ca417f0a33e3fa9ccece531afa2db3f0d4a30Dianne Hackborn } catch (RemoteException e) { 122632ca417f0a33e3fa9ccece531afa2db3f0d4a30Dianne Hackborn } 123705ab808cf023e0cc38c2ba7cdb9571942cdc04fAdam Powell if (newAudioRoutes != null) { 1242ee6a2a83262d05a566bd713d238e89edfd33a29Adam Powell // This will select the active BT route if there is one and the current 1252ee6a2a83262d05a566bd713d238e89edfd33a29Adam Powell // selected route is the default system route, or if there is no selected 1262ee6a2a83262d05a566bd713d238e89edfd33a29Adam Powell // route yet. 127705ab808cf023e0cc38c2ba7cdb9571942cdc04fAdam Powell updateAudioRoutes(newAudioRoutes); 128632ca417f0a33e3fa9ccece531afa2db3f0d4a30Dianne Hackborn } 129705ab808cf023e0cc38c2ba7cdb9571942cdc04fAdam Powell 1302ee6a2a83262d05a566bd713d238e89edfd33a29Adam Powell // Select the default route if the above didn't sync us up 1312ee6a2a83262d05a566bd713d238e89edfd33a29Adam Powell // appropriately with relevant system state. 1322ee6a2a83262d05a566bd713d238e89edfd33a29Adam Powell if (mSelectedRoute == null) { 1332ee6a2a83262d05a566bd713d238e89edfd33a29Adam Powell selectRouteStatic(mDefaultAudioVideo.getSupportedTypes(), mDefaultAudioVideo); 1342ee6a2a83262d05a566bd713d238e89edfd33a29Adam Powell } 135632ca417f0a33e3fa9ccece531afa2db3f0d4a30Dianne Hackborn } 136632ca417f0a33e3fa9ccece531afa2db3f0d4a30Dianne Hackborn 137705ab808cf023e0cc38c2ba7cdb9571942cdc04fAdam Powell void updateAudioRoutes(AudioRoutesInfo newRoutes) { 138705ab808cf023e0cc38c2ba7cdb9571942cdc04fAdam Powell if (newRoutes.mMainType != mCurAudioRoutesInfo.mMainType) { 139705ab808cf023e0cc38c2ba7cdb9571942cdc04fAdam Powell mCurAudioRoutesInfo.mMainType = newRoutes.mMainType; 140632ca417f0a33e3fa9ccece531afa2db3f0d4a30Dianne Hackborn int name; 141632ca417f0a33e3fa9ccece531afa2db3f0d4a30Dianne Hackborn if ((newRoutes.mMainType&AudioRoutesInfo.MAIN_HEADPHONES) != 0 142632ca417f0a33e3fa9ccece531afa2db3f0d4a30Dianne Hackborn || (newRoutes.mMainType&AudioRoutesInfo.MAIN_HEADSET) != 0) { 143632ca417f0a33e3fa9ccece531afa2db3f0d4a30Dianne Hackborn name = com.android.internal.R.string.default_audio_route_name_headphones; 144632ca417f0a33e3fa9ccece531afa2db3f0d4a30Dianne Hackborn } else if ((newRoutes.mMainType&AudioRoutesInfo.MAIN_DOCK_SPEAKERS) != 0) { 145632ca417f0a33e3fa9ccece531afa2db3f0d4a30Dianne Hackborn name = com.android.internal.R.string.default_audio_route_name_dock_speakers; 146632ca417f0a33e3fa9ccece531afa2db3f0d4a30Dianne Hackborn } else if ((newRoutes.mMainType&AudioRoutesInfo.MAIN_HDMI) != 0) { 1474131a37366d59b5e61f55c4e48d2b22ee0c4cad4Adam Powell name = com.android.internal.R.string.default_media_route_name_hdmi; 148632ca417f0a33e3fa9ccece531afa2db3f0d4a30Dianne Hackborn } else { 149632ca417f0a33e3fa9ccece531afa2db3f0d4a30Dianne Hackborn name = com.android.internal.R.string.default_audio_route_name; 150632ca417f0a33e3fa9ccece531afa2db3f0d4a30Dianne Hackborn } 151705ab808cf023e0cc38c2ba7cdb9571942cdc04fAdam Powell sStatic.mDefaultAudioVideo.mNameResId = name; 152705ab808cf023e0cc38c2ba7cdb9571942cdc04fAdam Powell dispatchRouteChanged(sStatic.mDefaultAudioVideo); 153632ca417f0a33e3fa9ccece531afa2db3f0d4a30Dianne Hackborn } 154bcf21e913af7252fb1994e07b6cf179321ecd049Adam Powell 1553f369684e13dfea0ba8ea134f3e95930b0dd7df0Adam Powell final int mainType = mCurAudioRoutesInfo.mMainType; 1563f369684e13dfea0ba8ea134f3e95930b0dd7df0Adam Powell 157bcf21e913af7252fb1994e07b6cf179321ecd049Adam Powell boolean a2dpEnabled; 158bcf21e913af7252fb1994e07b6cf179321ecd049Adam Powell try { 159bcf21e913af7252fb1994e07b6cf179321ecd049Adam Powell a2dpEnabled = mAudioService.isBluetoothA2dpOn(); 160bcf21e913af7252fb1994e07b6cf179321ecd049Adam Powell } catch (RemoteException e) { 161bcf21e913af7252fb1994e07b6cf179321ecd049Adam Powell Log.e(TAG, "Error querying Bluetooth A2DP state", e); 162bcf21e913af7252fb1994e07b6cf179321ecd049Adam Powell a2dpEnabled = false; 163bcf21e913af7252fb1994e07b6cf179321ecd049Adam Powell } 164bcf21e913af7252fb1994e07b6cf179321ecd049Adam Powell 165705ab808cf023e0cc38c2ba7cdb9571942cdc04fAdam Powell if (!TextUtils.equals(newRoutes.mBluetoothName, mCurAudioRoutesInfo.mBluetoothName)) { 166705ab808cf023e0cc38c2ba7cdb9571942cdc04fAdam Powell mCurAudioRoutesInfo.mBluetoothName = newRoutes.mBluetoothName; 167705ab808cf023e0cc38c2ba7cdb9571942cdc04fAdam Powell if (mCurAudioRoutesInfo.mBluetoothName != null) { 168632ca417f0a33e3fa9ccece531afa2db3f0d4a30Dianne Hackborn if (sStatic.mBluetoothA2dpRoute == null) { 169632ca417f0a33e3fa9ccece531afa2db3f0d4a30Dianne Hackborn final RouteInfo info = new RouteInfo(sStatic.mSystemCategory); 170705ab808cf023e0cc38c2ba7cdb9571942cdc04fAdam Powell info.mName = mCurAudioRoutesInfo.mBluetoothName; 171632ca417f0a33e3fa9ccece531afa2db3f0d4a30Dianne Hackborn info.mSupportedTypes = ROUTE_TYPE_LIVE_AUDIO; 172632ca417f0a33e3fa9ccece531afa2db3f0d4a30Dianne Hackborn sStatic.mBluetoothA2dpRoute = info; 1732ee6a2a83262d05a566bd713d238e89edfd33a29Adam Powell addRouteStatic(sStatic.mBluetoothA2dpRoute); 174632ca417f0a33e3fa9ccece531afa2db3f0d4a30Dianne Hackborn } else { 175705ab808cf023e0cc38c2ba7cdb9571942cdc04fAdam Powell sStatic.mBluetoothA2dpRoute.mName = mCurAudioRoutesInfo.mBluetoothName; 176632ca417f0a33e3fa9ccece531afa2db3f0d4a30Dianne Hackborn dispatchRouteChanged(sStatic.mBluetoothA2dpRoute); 177632ca417f0a33e3fa9ccece531afa2db3f0d4a30Dianne Hackborn } 178632ca417f0a33e3fa9ccece531afa2db3f0d4a30Dianne Hackborn } else if (sStatic.mBluetoothA2dpRoute != null) { 179632ca417f0a33e3fa9ccece531afa2db3f0d4a30Dianne Hackborn removeRoute(sStatic.mBluetoothA2dpRoute); 180632ca417f0a33e3fa9ccece531afa2db3f0d4a30Dianne Hackborn sStatic.mBluetoothA2dpRoute = null; 181632ca417f0a33e3fa9ccece531afa2db3f0d4a30Dianne Hackborn } 182632ca417f0a33e3fa9ccece531afa2db3f0d4a30Dianne Hackborn } 183bcf21e913af7252fb1994e07b6cf179321ecd049Adam Powell 184bcf21e913af7252fb1994e07b6cf179321ecd049Adam Powell if (mBluetoothA2dpRoute != null) { 1853f369684e13dfea0ba8ea134f3e95930b0dd7df0Adam Powell if (mainType != AudioRoutesInfo.MAIN_SPEAKER && 1863f369684e13dfea0ba8ea134f3e95930b0dd7df0Adam Powell mSelectedRoute == mBluetoothA2dpRoute && !a2dpEnabled) { 187705ab808cf023e0cc38c2ba7cdb9571942cdc04fAdam Powell selectRouteStatic(ROUTE_TYPE_LIVE_AUDIO, mDefaultAudioVideo); 1883f369684e13dfea0ba8ea134f3e95930b0dd7df0Adam Powell } else if ((mSelectedRoute == mDefaultAudioVideo || mSelectedRoute == null) && 1892ee6a2a83262d05a566bd713d238e89edfd33a29Adam Powell a2dpEnabled) { 190bcf21e913af7252fb1994e07b6cf179321ecd049Adam Powell selectRouteStatic(ROUTE_TYPE_LIVE_AUDIO, mBluetoothA2dpRoute); 191bcf21e913af7252fb1994e07b6cf179321ecd049Adam Powell } 192bcf21e913af7252fb1994e07b6cf179321ecd049Adam Powell } 193b58b8f832d06b0ffa8886eba5a4916578a3b8743Dianne Hackborn } 194b58b8f832d06b0ffa8886eba5a4916578a3b8743Dianne Hackborn } 1959a1de308cea2d160778fd977825f10a07b49d738Adam Powell 196b58b8f832d06b0ffa8886eba5a4916578a3b8743Dianne Hackborn static Static sStatic; 1979a1de308cea2d160778fd977825f10a07b49d738Adam Powell 1989a1de308cea2d160778fd977825f10a07b49d738Adam Powell /** 1999a1de308cea2d160778fd977825f10a07b49d738Adam Powell * Route type flag for live audio. 2009a1de308cea2d160778fd977825f10a07b49d738Adam Powell * 2019a1de308cea2d160778fd977825f10a07b49d738Adam Powell * <p>A device that supports live audio routing will allow the media audio stream 2029a1de308cea2d160778fd977825f10a07b49d738Adam Powell * to be routed to supported destinations. This can include internal speakers or 2039a1de308cea2d160778fd977825f10a07b49d738Adam Powell * audio jacks on the device itself, A2DP devices, and more.</p> 2049a1de308cea2d160778fd977825f10a07b49d738Adam Powell * 2059a1de308cea2d160778fd977825f10a07b49d738Adam Powell * <p>Once initiated this routing is transparent to the application. All audio 2069a1de308cea2d160778fd977825f10a07b49d738Adam Powell * played on the media stream will be routed to the selected destination.</p> 2079a1de308cea2d160778fd977825f10a07b49d738Adam Powell */ 2089a1de308cea2d160778fd977825f10a07b49d738Adam Powell public static final int ROUTE_TYPE_LIVE_AUDIO = 0x1; 2099a1de308cea2d160778fd977825f10a07b49d738Adam Powell 2109a1de308cea2d160778fd977825f10a07b49d738Adam Powell /** 211705ab808cf023e0cc38c2ba7cdb9571942cdc04fAdam Powell * Route type flag for live video. 212705ab808cf023e0cc38c2ba7cdb9571942cdc04fAdam Powell * 213705ab808cf023e0cc38c2ba7cdb9571942cdc04fAdam Powell * <p>A device that supports live video routing will allow a mirrored version 214705ab808cf023e0cc38c2ba7cdb9571942cdc04fAdam Powell * of the device's primary display or a customized 215705ab808cf023e0cc38c2ba7cdb9571942cdc04fAdam Powell * {@link android.app.Presentation Presentation} to be routed to supported destinations.</p> 216705ab808cf023e0cc38c2ba7cdb9571942cdc04fAdam Powell * 217705ab808cf023e0cc38c2ba7cdb9571942cdc04fAdam Powell * <p>Once initiated, display mirroring is transparent to the application. 218705ab808cf023e0cc38c2ba7cdb9571942cdc04fAdam Powell * While remote routing is active the application may use a 219705ab808cf023e0cc38c2ba7cdb9571942cdc04fAdam Powell * {@link android.app.Presentation Presentation} to replace the mirrored view 220705ab808cf023e0cc38c2ba7cdb9571942cdc04fAdam Powell * on the external display with different content.</p> 221705ab808cf023e0cc38c2ba7cdb9571942cdc04fAdam Powell */ 222705ab808cf023e0cc38c2ba7cdb9571942cdc04fAdam Powell public static final int ROUTE_TYPE_LIVE_VIDEO = 0x2; 223705ab808cf023e0cc38c2ba7cdb9571942cdc04fAdam Powell 224705ab808cf023e0cc38c2ba7cdb9571942cdc04fAdam Powell /** 2259a1de308cea2d160778fd977825f10a07b49d738Adam Powell * Route type flag for application-specific usage. 2269a1de308cea2d160778fd977825f10a07b49d738Adam Powell * 2279a1de308cea2d160778fd977825f10a07b49d738Adam Powell * <p>Unlike other media route types, user routes are managed by the application. 2289a1de308cea2d160778fd977825f10a07b49d738Adam Powell * The MediaRouter will manage and dispatch events for user routes, but the application 2299a1de308cea2d160778fd977825f10a07b49d738Adam Powell * is expected to interpret the meaning of these events and perform the requested 2309a1de308cea2d160778fd977825f10a07b49d738Adam Powell * routing tasks.</p> 2319a1de308cea2d160778fd977825f10a07b49d738Adam Powell */ 2329a1de308cea2d160778fd977825f10a07b49d738Adam Powell public static final int ROUTE_TYPE_USER = 0x00800000; 2339a1de308cea2d160778fd977825f10a07b49d738Adam Powell 2349a1de308cea2d160778fd977825f10a07b49d738Adam Powell // Maps application contexts 2359a1de308cea2d160778fd977825f10a07b49d738Adam Powell static final HashMap<Context, MediaRouter> sRouters = new HashMap<Context, MediaRouter>(); 2369a1de308cea2d160778fd977825f10a07b49d738Adam Powell 2379a1de308cea2d160778fd977825f10a07b49d738Adam Powell static String typesToString(int types) { 2389a1de308cea2d160778fd977825f10a07b49d738Adam Powell final StringBuilder result = new StringBuilder(); 2399a1de308cea2d160778fd977825f10a07b49d738Adam Powell if ((types & ROUTE_TYPE_LIVE_AUDIO) != 0) { 2409a1de308cea2d160778fd977825f10a07b49d738Adam Powell result.append("ROUTE_TYPE_LIVE_AUDIO "); 2419a1de308cea2d160778fd977825f10a07b49d738Adam Powell } 2429a1de308cea2d160778fd977825f10a07b49d738Adam Powell if ((types & ROUTE_TYPE_USER) != 0) { 2439a1de308cea2d160778fd977825f10a07b49d738Adam Powell result.append("ROUTE_TYPE_USER "); 2449a1de308cea2d160778fd977825f10a07b49d738Adam Powell } 2459a1de308cea2d160778fd977825f10a07b49d738Adam Powell return result.toString(); 2469a1de308cea2d160778fd977825f10a07b49d738Adam Powell } 2479a1de308cea2d160778fd977825f10a07b49d738Adam Powell 248b58b8f832d06b0ffa8886eba5a4916578a3b8743Dianne Hackborn /** @hide */ 249b58b8f832d06b0ffa8886eba5a4916578a3b8743Dianne Hackborn public MediaRouter(Context context) { 250b58b8f832d06b0ffa8886eba5a4916578a3b8743Dianne Hackborn synchronized (Static.class) { 251b58b8f832d06b0ffa8886eba5a4916578a3b8743Dianne Hackborn if (sStatic == null) { 2528e37a85bf3dc39519942698dc90a3951306b934bAdam Powell final Context appContext = context.getApplicationContext(); 2538e37a85bf3dc39519942698dc90a3951306b934bAdam Powell sStatic = new Static(appContext); 2548e37a85bf3dc39519942698dc90a3951306b934bAdam Powell sStatic.startMonitoringRoutes(appContext); 255b58b8f832d06b0ffa8886eba5a4916578a3b8743Dianne Hackborn } 2569a1de308cea2d160778fd977825f10a07b49d738Adam Powell } 2579a1de308cea2d160778fd977825f10a07b49d738Adam Powell } 2589a1de308cea2d160778fd977825f10a07b49d738Adam Powell 259690ffb4e1f735148a15f2036d9a3c1962fba188cAdam Powell /** 260690ffb4e1f735148a15f2036d9a3c1962fba188cAdam Powell * @hide for use by framework routing UI 261690ffb4e1f735148a15f2036d9a3c1962fba188cAdam Powell */ 262690ffb4e1f735148a15f2036d9a3c1962fba188cAdam Powell public RouteInfo getSystemAudioRoute() { 263705ab808cf023e0cc38c2ba7cdb9571942cdc04fAdam Powell return sStatic.mDefaultAudioVideo; 264690ffb4e1f735148a15f2036d9a3c1962fba188cAdam Powell } 265690ffb4e1f735148a15f2036d9a3c1962fba188cAdam Powell 266690ffb4e1f735148a15f2036d9a3c1962fba188cAdam Powell /** 2674599696591f745b3a546197d2ba7e5cfc5562484Adam Powell * @hide for use by framework routing UI 2684599696591f745b3a546197d2ba7e5cfc5562484Adam Powell */ 2694599696591f745b3a546197d2ba7e5cfc5562484Adam Powell public RouteCategory getSystemAudioCategory() { 2704599696591f745b3a546197d2ba7e5cfc5562484Adam Powell return sStatic.mSystemCategory; 2714599696591f745b3a546197d2ba7e5cfc5562484Adam Powell } 2724599696591f745b3a546197d2ba7e5cfc5562484Adam Powell 2734599696591f745b3a546197d2ba7e5cfc5562484Adam Powell /** 274690ffb4e1f735148a15f2036d9a3c1962fba188cAdam Powell * Return the currently selected route for the given types 275690ffb4e1f735148a15f2036d9a3c1962fba188cAdam Powell * 276690ffb4e1f735148a15f2036d9a3c1962fba188cAdam Powell * @param type route types 277690ffb4e1f735148a15f2036d9a3c1962fba188cAdam Powell * @return the selected route 278690ffb4e1f735148a15f2036d9a3c1962fba188cAdam Powell */ 279690ffb4e1f735148a15f2036d9a3c1962fba188cAdam Powell public RouteInfo getSelectedRoute(int type) { 280b58b8f832d06b0ffa8886eba5a4916578a3b8743Dianne Hackborn return sStatic.mSelectedRoute; 281690ffb4e1f735148a15f2036d9a3c1962fba188cAdam Powell } 282690ffb4e1f735148a15f2036d9a3c1962fba188cAdam Powell 2839a1de308cea2d160778fd977825f10a07b49d738Adam Powell /** 2849a1de308cea2d160778fd977825f10a07b49d738Adam Powell * Add a callback to listen to events about specific kinds of media routes. 2859a1de308cea2d160778fd977825f10a07b49d738Adam Powell * If the specified callback is already registered, its registration will be updated for any 2869a1de308cea2d160778fd977825f10a07b49d738Adam Powell * additional route types specified. 2879a1de308cea2d160778fd977825f10a07b49d738Adam Powell * 2889a1de308cea2d160778fd977825f10a07b49d738Adam Powell * @param types Types of routes this callback is interested in 2899a1de308cea2d160778fd977825f10a07b49d738Adam Powell * @param cb Callback to add 2909a1de308cea2d160778fd977825f10a07b49d738Adam Powell */ 2919a1de308cea2d160778fd977825f10a07b49d738Adam Powell public void addCallback(int types, Callback cb) { 292b58b8f832d06b0ffa8886eba5a4916578a3b8743Dianne Hackborn final int count = sStatic.mCallbacks.size(); 2939a1de308cea2d160778fd977825f10a07b49d738Adam Powell for (int i = 0; i < count; i++) { 294b58b8f832d06b0ffa8886eba5a4916578a3b8743Dianne Hackborn final CallbackInfo info = sStatic.mCallbacks.get(i); 2959a1de308cea2d160778fd977825f10a07b49d738Adam Powell if (info.cb == cb) { 296dbbfa702a09f6d2d36dee1b552442d04a4673f89Adam Powell info.type |= types; 2979a1de308cea2d160778fd977825f10a07b49d738Adam Powell return; 2989a1de308cea2d160778fd977825f10a07b49d738Adam Powell } 2999a1de308cea2d160778fd977825f10a07b49d738Adam Powell } 300b58b8f832d06b0ffa8886eba5a4916578a3b8743Dianne Hackborn sStatic.mCallbacks.add(new CallbackInfo(cb, types, this)); 3019a1de308cea2d160778fd977825f10a07b49d738Adam Powell } 3029a1de308cea2d160778fd977825f10a07b49d738Adam Powell 3039a1de308cea2d160778fd977825f10a07b49d738Adam Powell /** 3049a1de308cea2d160778fd977825f10a07b49d738Adam Powell * Remove the specified callback. It will no longer receive events about media routing. 3059a1de308cea2d160778fd977825f10a07b49d738Adam Powell * 3069a1de308cea2d160778fd977825f10a07b49d738Adam Powell * @param cb Callback to remove 3079a1de308cea2d160778fd977825f10a07b49d738Adam Powell */ 3089a1de308cea2d160778fd977825f10a07b49d738Adam Powell public void removeCallback(Callback cb) { 309b58b8f832d06b0ffa8886eba5a4916578a3b8743Dianne Hackborn final int count = sStatic.mCallbacks.size(); 3109a1de308cea2d160778fd977825f10a07b49d738Adam Powell for (int i = 0; i < count; i++) { 311b58b8f832d06b0ffa8886eba5a4916578a3b8743Dianne Hackborn if (sStatic.mCallbacks.get(i).cb == cb) { 312b58b8f832d06b0ffa8886eba5a4916578a3b8743Dianne Hackborn sStatic.mCallbacks.remove(i); 3139a1de308cea2d160778fd977825f10a07b49d738Adam Powell return; 3149a1de308cea2d160778fd977825f10a07b49d738Adam Powell } 3159a1de308cea2d160778fd977825f10a07b49d738Adam Powell } 3169a1de308cea2d160778fd977825f10a07b49d738Adam Powell Log.w(TAG, "removeCallback(" + cb + "): callback not registered"); 3179a1de308cea2d160778fd977825f10a07b49d738Adam Powell } 3189a1de308cea2d160778fd977825f10a07b49d738Adam Powell 319d0d2cda9d414da73773285d7fee9e13aef3495e9Adam Powell /** 320d0d2cda9d414da73773285d7fee9e13aef3495e9Adam Powell * Select the specified route to use for output of the given media types. 321d0d2cda9d414da73773285d7fee9e13aef3495e9Adam Powell * 322d0d2cda9d414da73773285d7fee9e13aef3495e9Adam Powell * @param types type flags indicating which types this route should be used for. 323d0d2cda9d414da73773285d7fee9e13aef3495e9Adam Powell * The route must support at least a subset. 324d0d2cda9d414da73773285d7fee9e13aef3495e9Adam Powell * @param route Route to select 325d0d2cda9d414da73773285d7fee9e13aef3495e9Adam Powell */ 3269a1de308cea2d160778fd977825f10a07b49d738Adam Powell public void selectRoute(int types, RouteInfo route) { 3270d03c042f90bf62d5bad7c64e77028a5f9f8fae0Adam Powell // Applications shouldn't programmatically change anything but user routes. 3280d03c042f90bf62d5bad7c64e77028a5f9f8fae0Adam Powell types &= ROUTE_TYPE_USER; 3290d03c042f90bf62d5bad7c64e77028a5f9f8fae0Adam Powell selectRouteStatic(types, route); 3300d03c042f90bf62d5bad7c64e77028a5f9f8fae0Adam Powell } 3310d03c042f90bf62d5bad7c64e77028a5f9f8fae0Adam Powell 3320d03c042f90bf62d5bad7c64e77028a5f9f8fae0Adam Powell /** 3330d03c042f90bf62d5bad7c64e77028a5f9f8fae0Adam Powell * @hide internal use 3340d03c042f90bf62d5bad7c64e77028a5f9f8fae0Adam Powell */ 3350d03c042f90bf62d5bad7c64e77028a5f9f8fae0Adam Powell public void selectRouteInt(int types, RouteInfo route) { 336b58b8f832d06b0ffa8886eba5a4916578a3b8743Dianne Hackborn selectRouteStatic(types, route); 337b58b8f832d06b0ffa8886eba5a4916578a3b8743Dianne Hackborn } 338b58b8f832d06b0ffa8886eba5a4916578a3b8743Dianne Hackborn 339b58b8f832d06b0ffa8886eba5a4916578a3b8743Dianne Hackborn static void selectRouteStatic(int types, RouteInfo route) { 340705ab808cf023e0cc38c2ba7cdb9571942cdc04fAdam Powell final RouteInfo oldRoute = sStatic.mSelectedRoute; 341705ab808cf023e0cc38c2ba7cdb9571942cdc04fAdam Powell if (oldRoute == route) return; 3420d03c042f90bf62d5bad7c64e77028a5f9f8fae0Adam Powell if ((route.getSupportedTypes() & types) == 0) { 3430d03c042f90bf62d5bad7c64e77028a5f9f8fae0Adam Powell Log.w(TAG, "selectRoute ignored; cannot select route with supported types " + 3440d03c042f90bf62d5bad7c64e77028a5f9f8fae0Adam Powell typesToString(route.getSupportedTypes()) + " into route types " + 3450d03c042f90bf62d5bad7c64e77028a5f9f8fae0Adam Powell typesToString(types)); 3464ee1f55ce0f4909a7430ab44563a81852f335071Adam Powell return; 3470d03c042f90bf62d5bad7c64e77028a5f9f8fae0Adam Powell } 3489a1de308cea2d160778fd977825f10a07b49d738Adam Powell 349dd0a19266d5c837069da1ea188744d54c8d723a8Adam Powell final RouteInfo btRoute = sStatic.mBluetoothA2dpRoute; 350dd0a19266d5c837069da1ea188744d54c8d723a8Adam Powell if (btRoute != null && (types & ROUTE_TYPE_LIVE_AUDIO) != 0 && 351705ab808cf023e0cc38c2ba7cdb9571942cdc04fAdam Powell (route == btRoute || route == sStatic.mDefaultAudioVideo)) { 352dd0a19266d5c837069da1ea188744d54c8d723a8Adam Powell try { 353dd0a19266d5c837069da1ea188744d54c8d723a8Adam Powell sStatic.mAudioService.setBluetoothA2dpOn(route == btRoute); 354dd0a19266d5c837069da1ea188744d54c8d723a8Adam Powell } catch (RemoteException e) { 355dd0a19266d5c837069da1ea188744d54c8d723a8Adam Powell Log.e(TAG, "Error changing Bluetooth A2DP state", e); 356dd0a19266d5c837069da1ea188744d54c8d723a8Adam Powell } 357dd0a19266d5c837069da1ea188744d54c8d723a8Adam Powell } 358dd0a19266d5c837069da1ea188744d54c8d723a8Adam Powell 359705ab808cf023e0cc38c2ba7cdb9571942cdc04fAdam Powell final WifiDisplay activeDisplay = 360705ab808cf023e0cc38c2ba7cdb9571942cdc04fAdam Powell sStatic.mDisplayService.getWifiDisplayStatus().getActiveDisplay(); 361705ab808cf023e0cc38c2ba7cdb9571942cdc04fAdam Powell final boolean oldRouteHasAddress = oldRoute != null && oldRoute.mDeviceAddress != null; 362705ab808cf023e0cc38c2ba7cdb9571942cdc04fAdam Powell final boolean newRouteHasAddress = route != null && route.mDeviceAddress != null; 363705ab808cf023e0cc38c2ba7cdb9571942cdc04fAdam Powell if (activeDisplay != null || oldRouteHasAddress || newRouteHasAddress) { 364705ab808cf023e0cc38c2ba7cdb9571942cdc04fAdam Powell if (newRouteHasAddress && !matchesDeviceAddress(activeDisplay, route)) { 365705ab808cf023e0cc38c2ba7cdb9571942cdc04fAdam Powell sStatic.mDisplayService.connectWifiDisplay(route.mDeviceAddress); 366705ab808cf023e0cc38c2ba7cdb9571942cdc04fAdam Powell } else if (activeDisplay != null && !newRouteHasAddress) { 367705ab808cf023e0cc38c2ba7cdb9571942cdc04fAdam Powell sStatic.mDisplayService.disconnectWifiDisplay(); 368705ab808cf023e0cc38c2ba7cdb9571942cdc04fAdam Powell } 369705ab808cf023e0cc38c2ba7cdb9571942cdc04fAdam Powell } 370705ab808cf023e0cc38c2ba7cdb9571942cdc04fAdam Powell 371705ab808cf023e0cc38c2ba7cdb9571942cdc04fAdam Powell if (oldRoute != null) { 3729a1de308cea2d160778fd977825f10a07b49d738Adam Powell // TODO filter types properly 373705ab808cf023e0cc38c2ba7cdb9571942cdc04fAdam Powell dispatchRouteUnselected(types & oldRoute.getSupportedTypes(), oldRoute); 3749a1de308cea2d160778fd977825f10a07b49d738Adam Powell } 375b58b8f832d06b0ffa8886eba5a4916578a3b8743Dianne Hackborn sStatic.mSelectedRoute = route; 3769a1de308cea2d160778fd977825f10a07b49d738Adam Powell if (route != null) { 3779a1de308cea2d160778fd977825f10a07b49d738Adam Powell // TODO filter types properly 3789a1de308cea2d160778fd977825f10a07b49d738Adam Powell dispatchRouteSelected(types & route.getSupportedTypes(), route); 3799a1de308cea2d160778fd977825f10a07b49d738Adam Powell } 3809a1de308cea2d160778fd977825f10a07b49d738Adam Powell } 3819a1de308cea2d160778fd977825f10a07b49d738Adam Powell 3829a1de308cea2d160778fd977825f10a07b49d738Adam Powell /** 383705ab808cf023e0cc38c2ba7cdb9571942cdc04fAdam Powell * Compare the device address of a display and a route. 384705ab808cf023e0cc38c2ba7cdb9571942cdc04fAdam Powell * Nulls/no device address will match another null/no address. 385705ab808cf023e0cc38c2ba7cdb9571942cdc04fAdam Powell */ 386705ab808cf023e0cc38c2ba7cdb9571942cdc04fAdam Powell static boolean matchesDeviceAddress(WifiDisplay display, RouteInfo info) { 387705ab808cf023e0cc38c2ba7cdb9571942cdc04fAdam Powell final boolean routeHasAddress = info != null && info.mDeviceAddress != null; 388705ab808cf023e0cc38c2ba7cdb9571942cdc04fAdam Powell if (display == null && !routeHasAddress) { 389705ab808cf023e0cc38c2ba7cdb9571942cdc04fAdam Powell return true; 390705ab808cf023e0cc38c2ba7cdb9571942cdc04fAdam Powell } 391705ab808cf023e0cc38c2ba7cdb9571942cdc04fAdam Powell 392705ab808cf023e0cc38c2ba7cdb9571942cdc04fAdam Powell if (display != null && routeHasAddress) { 393705ab808cf023e0cc38c2ba7cdb9571942cdc04fAdam Powell return display.getDeviceAddress().equals(info.mDeviceAddress); 394705ab808cf023e0cc38c2ba7cdb9571942cdc04fAdam Powell } 395705ab808cf023e0cc38c2ba7cdb9571942cdc04fAdam Powell return false; 396705ab808cf023e0cc38c2ba7cdb9571942cdc04fAdam Powell } 397705ab808cf023e0cc38c2ba7cdb9571942cdc04fAdam Powell 398705ab808cf023e0cc38c2ba7cdb9571942cdc04fAdam Powell /** 3999a1de308cea2d160778fd977825f10a07b49d738Adam Powell * Add an app-specified route for media to the MediaRouter. 4009a1de308cea2d160778fd977825f10a07b49d738Adam Powell * App-specified route definitions are created using {@link #createUserRoute(RouteCategory)} 4019a1de308cea2d160778fd977825f10a07b49d738Adam Powell * 4029a1de308cea2d160778fd977825f10a07b49d738Adam Powell * @param info Definition of the route to add 4039a1de308cea2d160778fd977825f10a07b49d738Adam Powell * @see #createUserRoute() 4049a1de308cea2d160778fd977825f10a07b49d738Adam Powell * @see #removeUserRoute(UserRouteInfo) 4059a1de308cea2d160778fd977825f10a07b49d738Adam Powell */ 4069a1de308cea2d160778fd977825f10a07b49d738Adam Powell public void addUserRoute(UserRouteInfo info) { 4072ee6a2a83262d05a566bd713d238e89edfd33a29Adam Powell addRouteStatic(info); 4089a1de308cea2d160778fd977825f10a07b49d738Adam Powell } 4099a1de308cea2d160778fd977825f10a07b49d738Adam Powell 410d6d0bddee363e0c7fe61f63bd9d9864a71d887d6Adam Powell /** 411d6d0bddee363e0c7fe61f63bd9d9864a71d887d6Adam Powell * @hide Framework use only 412d6d0bddee363e0c7fe61f63bd9d9864a71d887d6Adam Powell */ 413d6d0bddee363e0c7fe61f63bd9d9864a71d887d6Adam Powell public void addRouteInt(RouteInfo info) { 4142ee6a2a83262d05a566bd713d238e89edfd33a29Adam Powell addRouteStatic(info); 415d6d0bddee363e0c7fe61f63bd9d9864a71d887d6Adam Powell } 416d6d0bddee363e0c7fe61f63bd9d9864a71d887d6Adam Powell 4172ee6a2a83262d05a566bd713d238e89edfd33a29Adam Powell static void addRouteStatic(RouteInfo info) { 4189a1de308cea2d160778fd977825f10a07b49d738Adam Powell final RouteCategory cat = info.getCategory(); 419b58b8f832d06b0ffa8886eba5a4916578a3b8743Dianne Hackborn if (!sStatic.mCategories.contains(cat)) { 420b58b8f832d06b0ffa8886eba5a4916578a3b8743Dianne Hackborn sStatic.mCategories.add(cat); 4219a1de308cea2d160778fd977825f10a07b49d738Adam Powell } 422d0d2cda9d414da73773285d7fee9e13aef3495e9Adam Powell if (cat.isGroupable() && !(info instanceof RouteGroup)) { 4239a1de308cea2d160778fd977825f10a07b49d738Adam Powell // Enforce that any added route in a groupable category must be in a group. 4249a1de308cea2d160778fd977825f10a07b49d738Adam Powell final RouteGroup group = new RouteGroup(info.getCategory()); 425dbbfa702a09f6d2d36dee1b552442d04a4673f89Adam Powell group.mSupportedTypes = info.mSupportedTypes; 426b58b8f832d06b0ffa8886eba5a4916578a3b8743Dianne Hackborn sStatic.mRoutes.add(group); 427d0d2cda9d414da73773285d7fee9e13aef3495e9Adam Powell dispatchRouteAdded(group); 428b5e2af5919351486a385effe77409d2a91ae9c19Adam Powell group.addRoute(info); 429d0d2cda9d414da73773285d7fee9e13aef3495e9Adam Powell 4309a1de308cea2d160778fd977825f10a07b49d738Adam Powell info = group; 431d0d2cda9d414da73773285d7fee9e13aef3495e9Adam Powell } else { 432b58b8f832d06b0ffa8886eba5a4916578a3b8743Dianne Hackborn sStatic.mRoutes.add(info); 433d0d2cda9d414da73773285d7fee9e13aef3495e9Adam Powell dispatchRouteAdded(info); 4349a1de308cea2d160778fd977825f10a07b49d738Adam Powell } 4359a1de308cea2d160778fd977825f10a07b49d738Adam Powell } 4369a1de308cea2d160778fd977825f10a07b49d738Adam Powell 4379a1de308cea2d160778fd977825f10a07b49d738Adam Powell /** 4389a1de308cea2d160778fd977825f10a07b49d738Adam Powell * Remove an app-specified route for media from the MediaRouter. 4399a1de308cea2d160778fd977825f10a07b49d738Adam Powell * 4409a1de308cea2d160778fd977825f10a07b49d738Adam Powell * @param info Definition of the route to remove 4419a1de308cea2d160778fd977825f10a07b49d738Adam Powell * @see #addUserRoute(UserRouteInfo) 4429a1de308cea2d160778fd977825f10a07b49d738Adam Powell */ 4439a1de308cea2d160778fd977825f10a07b49d738Adam Powell public void removeUserRoute(UserRouteInfo info) { 4449a1de308cea2d160778fd977825f10a07b49d738Adam Powell removeRoute(info); 4459a1de308cea2d160778fd977825f10a07b49d738Adam Powell } 4469a1de308cea2d160778fd977825f10a07b49d738Adam Powell 447690ffb4e1f735148a15f2036d9a3c1962fba188cAdam Powell /** 448690ffb4e1f735148a15f2036d9a3c1962fba188cAdam Powell * Remove all app-specified routes from the MediaRouter. 449690ffb4e1f735148a15f2036d9a3c1962fba188cAdam Powell * 450690ffb4e1f735148a15f2036d9a3c1962fba188cAdam Powell * @see #removeUserRoute(UserRouteInfo) 451690ffb4e1f735148a15f2036d9a3c1962fba188cAdam Powell */ 452690ffb4e1f735148a15f2036d9a3c1962fba188cAdam Powell public void clearUserRoutes() { 453b58b8f832d06b0ffa8886eba5a4916578a3b8743Dianne Hackborn for (int i = 0; i < sStatic.mRoutes.size(); i++) { 454b58b8f832d06b0ffa8886eba5a4916578a3b8743Dianne Hackborn final RouteInfo info = sStatic.mRoutes.get(i); 455d6d0bddee363e0c7fe61f63bd9d9864a71d887d6Adam Powell // TODO Right now, RouteGroups only ever contain user routes. 456d6d0bddee363e0c7fe61f63bd9d9864a71d887d6Adam Powell // The code below will need to change if this assumption does. 457d6d0bddee363e0c7fe61f63bd9d9864a71d887d6Adam Powell if (info instanceof UserRouteInfo || info instanceof RouteGroup) { 458690ffb4e1f735148a15f2036d9a3c1962fba188cAdam Powell removeRouteAt(i); 459690ffb4e1f735148a15f2036d9a3c1962fba188cAdam Powell i--; 460690ffb4e1f735148a15f2036d9a3c1962fba188cAdam Powell } 461690ffb4e1f735148a15f2036d9a3c1962fba188cAdam Powell } 462690ffb4e1f735148a15f2036d9a3c1962fba188cAdam Powell } 463690ffb4e1f735148a15f2036d9a3c1962fba188cAdam Powell 464d6d0bddee363e0c7fe61f63bd9d9864a71d887d6Adam Powell /** 465d6d0bddee363e0c7fe61f63bd9d9864a71d887d6Adam Powell * @hide internal use only 466d6d0bddee363e0c7fe61f63bd9d9864a71d887d6Adam Powell */ 467d6d0bddee363e0c7fe61f63bd9d9864a71d887d6Adam Powell public void removeRouteInt(RouteInfo info) { 468d6d0bddee363e0c7fe61f63bd9d9864a71d887d6Adam Powell removeRoute(info); 469d6d0bddee363e0c7fe61f63bd9d9864a71d887d6Adam Powell } 470d6d0bddee363e0c7fe61f63bd9d9864a71d887d6Adam Powell 471b58b8f832d06b0ffa8886eba5a4916578a3b8743Dianne Hackborn static void removeRoute(RouteInfo info) { 472b58b8f832d06b0ffa8886eba5a4916578a3b8743Dianne Hackborn if (sStatic.mRoutes.remove(info)) { 4739a1de308cea2d160778fd977825f10a07b49d738Adam Powell final RouteCategory removingCat = info.getCategory(); 474b58b8f832d06b0ffa8886eba5a4916578a3b8743Dianne Hackborn final int count = sStatic.mRoutes.size(); 4759a1de308cea2d160778fd977825f10a07b49d738Adam Powell boolean found = false; 4769a1de308cea2d160778fd977825f10a07b49d738Adam Powell for (int i = 0; i < count; i++) { 477b58b8f832d06b0ffa8886eba5a4916578a3b8743Dianne Hackborn final RouteCategory cat = sStatic.mRoutes.get(i).getCategory(); 4789a1de308cea2d160778fd977825f10a07b49d738Adam Powell if (removingCat == cat) { 4799a1de308cea2d160778fd977825f10a07b49d738Adam Powell found = true; 4809a1de308cea2d160778fd977825f10a07b49d738Adam Powell break; 4819a1de308cea2d160778fd977825f10a07b49d738Adam Powell } 4829a1de308cea2d160778fd977825f10a07b49d738Adam Powell } 483d6d0bddee363e0c7fe61f63bd9d9864a71d887d6Adam Powell if (info == sStatic.mSelectedRoute) { 484d6d0bddee363e0c7fe61f63bd9d9864a71d887d6Adam Powell // Removing the currently selected route? Select the default before we remove it. 485d6d0bddee363e0c7fe61f63bd9d9864a71d887d6Adam Powell // TODO: Be smarter about the route types here; this selects for all valid. 486705ab808cf023e0cc38c2ba7cdb9571942cdc04fAdam Powell selectRouteStatic(ROUTE_TYPE_LIVE_AUDIO | ROUTE_TYPE_USER, sStatic.mDefaultAudioVideo); 487d6d0bddee363e0c7fe61f63bd9d9864a71d887d6Adam Powell } 4889a1de308cea2d160778fd977825f10a07b49d738Adam Powell if (!found) { 489b58b8f832d06b0ffa8886eba5a4916578a3b8743Dianne Hackborn sStatic.mCategories.remove(removingCat); 4909a1de308cea2d160778fd977825f10a07b49d738Adam Powell } 4919a1de308cea2d160778fd977825f10a07b49d738Adam Powell dispatchRouteRemoved(info); 4929a1de308cea2d160778fd977825f10a07b49d738Adam Powell } 4939a1de308cea2d160778fd977825f10a07b49d738Adam Powell } 4949a1de308cea2d160778fd977825f10a07b49d738Adam Powell 495690ffb4e1f735148a15f2036d9a3c1962fba188cAdam Powell void removeRouteAt(int routeIndex) { 496b58b8f832d06b0ffa8886eba5a4916578a3b8743Dianne Hackborn if (routeIndex >= 0 && routeIndex < sStatic.mRoutes.size()) { 497b58b8f832d06b0ffa8886eba5a4916578a3b8743Dianne Hackborn final RouteInfo info = sStatic.mRoutes.remove(routeIndex); 498690ffb4e1f735148a15f2036d9a3c1962fba188cAdam Powell final RouteCategory removingCat = info.getCategory(); 499b58b8f832d06b0ffa8886eba5a4916578a3b8743Dianne Hackborn final int count = sStatic.mRoutes.size(); 500690ffb4e1f735148a15f2036d9a3c1962fba188cAdam Powell boolean found = false; 501690ffb4e1f735148a15f2036d9a3c1962fba188cAdam Powell for (int i = 0; i < count; i++) { 502b58b8f832d06b0ffa8886eba5a4916578a3b8743Dianne Hackborn final RouteCategory cat = sStatic.mRoutes.get(i).getCategory(); 503690ffb4e1f735148a15f2036d9a3c1962fba188cAdam Powell if (removingCat == cat) { 504690ffb4e1f735148a15f2036d9a3c1962fba188cAdam Powell found = true; 505690ffb4e1f735148a15f2036d9a3c1962fba188cAdam Powell break; 506690ffb4e1f735148a15f2036d9a3c1962fba188cAdam Powell } 507690ffb4e1f735148a15f2036d9a3c1962fba188cAdam Powell } 508d6d0bddee363e0c7fe61f63bd9d9864a71d887d6Adam Powell if (info == sStatic.mSelectedRoute) { 509d6d0bddee363e0c7fe61f63bd9d9864a71d887d6Adam Powell // Removing the currently selected route? Select the default before we remove it. 510d6d0bddee363e0c7fe61f63bd9d9864a71d887d6Adam Powell // TODO: Be smarter about the route types here; this selects for all valid. 511705ab808cf023e0cc38c2ba7cdb9571942cdc04fAdam Powell selectRouteStatic(ROUTE_TYPE_LIVE_AUDIO | ROUTE_TYPE_LIVE_VIDEO | ROUTE_TYPE_USER, 512705ab808cf023e0cc38c2ba7cdb9571942cdc04fAdam Powell sStatic.mDefaultAudioVideo); 513d6d0bddee363e0c7fe61f63bd9d9864a71d887d6Adam Powell } 514690ffb4e1f735148a15f2036d9a3c1962fba188cAdam Powell if (!found) { 515b58b8f832d06b0ffa8886eba5a4916578a3b8743Dianne Hackborn sStatic.mCategories.remove(removingCat); 516690ffb4e1f735148a15f2036d9a3c1962fba188cAdam Powell } 517690ffb4e1f735148a15f2036d9a3c1962fba188cAdam Powell dispatchRouteRemoved(info); 518690ffb4e1f735148a15f2036d9a3c1962fba188cAdam Powell } 519690ffb4e1f735148a15f2036d9a3c1962fba188cAdam Powell } 520690ffb4e1f735148a15f2036d9a3c1962fba188cAdam Powell 5219a1de308cea2d160778fd977825f10a07b49d738Adam Powell /** 5229a1de308cea2d160778fd977825f10a07b49d738Adam Powell * Return the number of {@link MediaRouter.RouteCategory categories} currently 5239a1de308cea2d160778fd977825f10a07b49d738Adam Powell * represented by routes known to this MediaRouter. 5249a1de308cea2d160778fd977825f10a07b49d738Adam Powell * 5259a1de308cea2d160778fd977825f10a07b49d738Adam Powell * @return the number of unique categories represented by this MediaRouter's known routes 5269a1de308cea2d160778fd977825f10a07b49d738Adam Powell */ 5279a1de308cea2d160778fd977825f10a07b49d738Adam Powell public int getCategoryCount() { 528b58b8f832d06b0ffa8886eba5a4916578a3b8743Dianne Hackborn return sStatic.mCategories.size(); 5299a1de308cea2d160778fd977825f10a07b49d738Adam Powell } 5309a1de308cea2d160778fd977825f10a07b49d738Adam Powell 5319a1de308cea2d160778fd977825f10a07b49d738Adam Powell /** 5329a1de308cea2d160778fd977825f10a07b49d738Adam Powell * Return the {@link MediaRouter.RouteCategory category} at the given index. 5339a1de308cea2d160778fd977825f10a07b49d738Adam Powell * Valid indices are in the range [0-getCategoryCount). 5349a1de308cea2d160778fd977825f10a07b49d738Adam Powell * 5359a1de308cea2d160778fd977825f10a07b49d738Adam Powell * @param index which category to return 5369a1de308cea2d160778fd977825f10a07b49d738Adam Powell * @return the category at index 5379a1de308cea2d160778fd977825f10a07b49d738Adam Powell */ 5389a1de308cea2d160778fd977825f10a07b49d738Adam Powell public RouteCategory getCategoryAt(int index) { 539b58b8f832d06b0ffa8886eba5a4916578a3b8743Dianne Hackborn return sStatic.mCategories.get(index); 5409a1de308cea2d160778fd977825f10a07b49d738Adam Powell } 5419a1de308cea2d160778fd977825f10a07b49d738Adam Powell 5429a1de308cea2d160778fd977825f10a07b49d738Adam Powell /** 5439a1de308cea2d160778fd977825f10a07b49d738Adam Powell * Return the number of {@link MediaRouter.RouteInfo routes} currently known 5449a1de308cea2d160778fd977825f10a07b49d738Adam Powell * to this MediaRouter. 5459a1de308cea2d160778fd977825f10a07b49d738Adam Powell * 5469a1de308cea2d160778fd977825f10a07b49d738Adam Powell * @return the number of routes tracked by this router 5479a1de308cea2d160778fd977825f10a07b49d738Adam Powell */ 5489a1de308cea2d160778fd977825f10a07b49d738Adam Powell public int getRouteCount() { 549b58b8f832d06b0ffa8886eba5a4916578a3b8743Dianne Hackborn return sStatic.mRoutes.size(); 5509a1de308cea2d160778fd977825f10a07b49d738Adam Powell } 5519a1de308cea2d160778fd977825f10a07b49d738Adam Powell 5529a1de308cea2d160778fd977825f10a07b49d738Adam Powell /** 5539a1de308cea2d160778fd977825f10a07b49d738Adam Powell * Return the route at the specified index. 5549a1de308cea2d160778fd977825f10a07b49d738Adam Powell * 5559a1de308cea2d160778fd977825f10a07b49d738Adam Powell * @param index index of the route to return 5569a1de308cea2d160778fd977825f10a07b49d738Adam Powell * @return the route at index 5579a1de308cea2d160778fd977825f10a07b49d738Adam Powell */ 5589a1de308cea2d160778fd977825f10a07b49d738Adam Powell public RouteInfo getRouteAt(int index) { 559b58b8f832d06b0ffa8886eba5a4916578a3b8743Dianne Hackborn return sStatic.mRoutes.get(index); 560b58b8f832d06b0ffa8886eba5a4916578a3b8743Dianne Hackborn } 561b58b8f832d06b0ffa8886eba5a4916578a3b8743Dianne Hackborn 562b58b8f832d06b0ffa8886eba5a4916578a3b8743Dianne Hackborn static int getRouteCountStatic() { 563b58b8f832d06b0ffa8886eba5a4916578a3b8743Dianne Hackborn return sStatic.mRoutes.size(); 564b58b8f832d06b0ffa8886eba5a4916578a3b8743Dianne Hackborn } 565b58b8f832d06b0ffa8886eba5a4916578a3b8743Dianne Hackborn 566b58b8f832d06b0ffa8886eba5a4916578a3b8743Dianne Hackborn static RouteInfo getRouteAtStatic(int index) { 567b58b8f832d06b0ffa8886eba5a4916578a3b8743Dianne Hackborn return sStatic.mRoutes.get(index); 5689a1de308cea2d160778fd977825f10a07b49d738Adam Powell } 5699a1de308cea2d160778fd977825f10a07b49d738Adam Powell 5709a1de308cea2d160778fd977825f10a07b49d738Adam Powell /** 5719a1de308cea2d160778fd977825f10a07b49d738Adam Powell * Create a new user route that may be modified and registered for use by the application. 5729a1de308cea2d160778fd977825f10a07b49d738Adam Powell * 5739a1de308cea2d160778fd977825f10a07b49d738Adam Powell * @param category The category the new route will belong to 5749a1de308cea2d160778fd977825f10a07b49d738Adam Powell * @return A new UserRouteInfo for use by the application 5759a1de308cea2d160778fd977825f10a07b49d738Adam Powell * 5769a1de308cea2d160778fd977825f10a07b49d738Adam Powell * @see #addUserRoute(UserRouteInfo) 5779a1de308cea2d160778fd977825f10a07b49d738Adam Powell * @see #removeUserRoute(UserRouteInfo) 5789a1de308cea2d160778fd977825f10a07b49d738Adam Powell * @see #createRouteCategory(CharSequence) 5799a1de308cea2d160778fd977825f10a07b49d738Adam Powell */ 5809a1de308cea2d160778fd977825f10a07b49d738Adam Powell public UserRouteInfo createUserRoute(RouteCategory category) { 5819a1de308cea2d160778fd977825f10a07b49d738Adam Powell return new UserRouteInfo(category); 5829a1de308cea2d160778fd977825f10a07b49d738Adam Powell } 5839a1de308cea2d160778fd977825f10a07b49d738Adam Powell 5849a1de308cea2d160778fd977825f10a07b49d738Adam Powell /** 5859a1de308cea2d160778fd977825f10a07b49d738Adam Powell * Create a new route category. Each route must belong to a category. 5869a1de308cea2d160778fd977825f10a07b49d738Adam Powell * 5879a1de308cea2d160778fd977825f10a07b49d738Adam Powell * @param name Name of the new category 5889a1de308cea2d160778fd977825f10a07b49d738Adam Powell * @param isGroupable true if routes in this category may be grouped with one another 5899a1de308cea2d160778fd977825f10a07b49d738Adam Powell * @return the new RouteCategory 5909a1de308cea2d160778fd977825f10a07b49d738Adam Powell */ 5919a1de308cea2d160778fd977825f10a07b49d738Adam Powell public RouteCategory createRouteCategory(CharSequence name, boolean isGroupable) { 5929a1de308cea2d160778fd977825f10a07b49d738Adam Powell return new RouteCategory(name, ROUTE_TYPE_USER, isGroupable); 5939a1de308cea2d160778fd977825f10a07b49d738Adam Powell } 5940d03c042f90bf62d5bad7c64e77028a5f9f8fae0Adam Powell 5950d03c042f90bf62d5bad7c64e77028a5f9f8fae0Adam Powell /** 5960d03c042f90bf62d5bad7c64e77028a5f9f8fae0Adam Powell * Create a new route category. Each route must belong to a category. 5970d03c042f90bf62d5bad7c64e77028a5f9f8fae0Adam Powell * 5980d03c042f90bf62d5bad7c64e77028a5f9f8fae0Adam Powell * @param nameResId Resource ID of the name of the new category 5990d03c042f90bf62d5bad7c64e77028a5f9f8fae0Adam Powell * @param isGroupable true if routes in this category may be grouped with one another 6000d03c042f90bf62d5bad7c64e77028a5f9f8fae0Adam Powell * @return the new RouteCategory 6010d03c042f90bf62d5bad7c64e77028a5f9f8fae0Adam Powell */ 6020d03c042f90bf62d5bad7c64e77028a5f9f8fae0Adam Powell public RouteCategory createRouteCategory(int nameResId, boolean isGroupable) { 6030d03c042f90bf62d5bad7c64e77028a5f9f8fae0Adam Powell return new RouteCategory(nameResId, ROUTE_TYPE_USER, isGroupable); 6040d03c042f90bf62d5bad7c64e77028a5f9f8fae0Adam Powell } 6059a1de308cea2d160778fd977825f10a07b49d738Adam Powell 606b58b8f832d06b0ffa8886eba5a4916578a3b8743Dianne Hackborn static void updateRoute(final RouteInfo info) { 6079a1de308cea2d160778fd977825f10a07b49d738Adam Powell dispatchRouteChanged(info); 6089a1de308cea2d160778fd977825f10a07b49d738Adam Powell } 6099a1de308cea2d160778fd977825f10a07b49d738Adam Powell 610b58b8f832d06b0ffa8886eba5a4916578a3b8743Dianne Hackborn static void dispatchRouteSelected(int type, RouteInfo info) { 61139d5c6172503620ac3761148adac5fd7fa20d02dAdam Powell for (CallbackInfo cbi : sStatic.mCallbacks) { 6129a1de308cea2d160778fd977825f10a07b49d738Adam Powell if ((cbi.type & type) != 0) { 613b58b8f832d06b0ffa8886eba5a4916578a3b8743Dianne Hackborn cbi.cb.onRouteSelected(cbi.router, type, info); 6149a1de308cea2d160778fd977825f10a07b49d738Adam Powell } 6159a1de308cea2d160778fd977825f10a07b49d738Adam Powell } 6169a1de308cea2d160778fd977825f10a07b49d738Adam Powell } 6179a1de308cea2d160778fd977825f10a07b49d738Adam Powell 618b58b8f832d06b0ffa8886eba5a4916578a3b8743Dianne Hackborn static void dispatchRouteUnselected(int type, RouteInfo info) { 61939d5c6172503620ac3761148adac5fd7fa20d02dAdam Powell for (CallbackInfo cbi : sStatic.mCallbacks) { 6209a1de308cea2d160778fd977825f10a07b49d738Adam Powell if ((cbi.type & type) != 0) { 621b58b8f832d06b0ffa8886eba5a4916578a3b8743Dianne Hackborn cbi.cb.onRouteUnselected(cbi.router, type, info); 6229a1de308cea2d160778fd977825f10a07b49d738Adam Powell } 6239a1de308cea2d160778fd977825f10a07b49d738Adam Powell } 6249a1de308cea2d160778fd977825f10a07b49d738Adam Powell } 6259a1de308cea2d160778fd977825f10a07b49d738Adam Powell 626b58b8f832d06b0ffa8886eba5a4916578a3b8743Dianne Hackborn static void dispatchRouteChanged(RouteInfo info) { 62739d5c6172503620ac3761148adac5fd7fa20d02dAdam Powell for (CallbackInfo cbi : sStatic.mCallbacks) { 6289a1de308cea2d160778fd977825f10a07b49d738Adam Powell if ((cbi.type & info.mSupportedTypes) != 0) { 629b58b8f832d06b0ffa8886eba5a4916578a3b8743Dianne Hackborn cbi.cb.onRouteChanged(cbi.router, info); 6309a1de308cea2d160778fd977825f10a07b49d738Adam Powell } 6319a1de308cea2d160778fd977825f10a07b49d738Adam Powell } 6329a1de308cea2d160778fd977825f10a07b49d738Adam Powell } 6339a1de308cea2d160778fd977825f10a07b49d738Adam Powell 634b58b8f832d06b0ffa8886eba5a4916578a3b8743Dianne Hackborn static void dispatchRouteAdded(RouteInfo info) { 63539d5c6172503620ac3761148adac5fd7fa20d02dAdam Powell for (CallbackInfo cbi : sStatic.mCallbacks) { 6369a1de308cea2d160778fd977825f10a07b49d738Adam Powell if ((cbi.type & info.mSupportedTypes) != 0) { 637b58b8f832d06b0ffa8886eba5a4916578a3b8743Dianne Hackborn cbi.cb.onRouteAdded(cbi.router, info); 6389a1de308cea2d160778fd977825f10a07b49d738Adam Powell } 6399a1de308cea2d160778fd977825f10a07b49d738Adam Powell } 6409a1de308cea2d160778fd977825f10a07b49d738Adam Powell } 6419a1de308cea2d160778fd977825f10a07b49d738Adam Powell 642b58b8f832d06b0ffa8886eba5a4916578a3b8743Dianne Hackborn static void dispatchRouteRemoved(RouteInfo info) { 64339d5c6172503620ac3761148adac5fd7fa20d02dAdam Powell for (CallbackInfo cbi : sStatic.mCallbacks) { 6449a1de308cea2d160778fd977825f10a07b49d738Adam Powell if ((cbi.type & info.mSupportedTypes) != 0) { 645b58b8f832d06b0ffa8886eba5a4916578a3b8743Dianne Hackborn cbi.cb.onRouteRemoved(cbi.router, info); 6469a1de308cea2d160778fd977825f10a07b49d738Adam Powell } 6479a1de308cea2d160778fd977825f10a07b49d738Adam Powell } 6489a1de308cea2d160778fd977825f10a07b49d738Adam Powell } 6499a1de308cea2d160778fd977825f10a07b49d738Adam Powell 650b58b8f832d06b0ffa8886eba5a4916578a3b8743Dianne Hackborn static void dispatchRouteGrouped(RouteInfo info, RouteGroup group, int index) { 65139d5c6172503620ac3761148adac5fd7fa20d02dAdam Powell for (CallbackInfo cbi : sStatic.mCallbacks) { 652d0d2cda9d414da73773285d7fee9e13aef3495e9Adam Powell if ((cbi.type & group.mSupportedTypes) != 0) { 653b58b8f832d06b0ffa8886eba5a4916578a3b8743Dianne Hackborn cbi.cb.onRouteGrouped(cbi.router, info, group, index); 654d0d2cda9d414da73773285d7fee9e13aef3495e9Adam Powell } 655d0d2cda9d414da73773285d7fee9e13aef3495e9Adam Powell } 656d0d2cda9d414da73773285d7fee9e13aef3495e9Adam Powell } 657d0d2cda9d414da73773285d7fee9e13aef3495e9Adam Powell 658b58b8f832d06b0ffa8886eba5a4916578a3b8743Dianne Hackborn static void dispatchRouteUngrouped(RouteInfo info, RouteGroup group) { 65939d5c6172503620ac3761148adac5fd7fa20d02dAdam Powell for (CallbackInfo cbi : sStatic.mCallbacks) { 660d0d2cda9d414da73773285d7fee9e13aef3495e9Adam Powell if ((cbi.type & group.mSupportedTypes) != 0) { 661b58b8f832d06b0ffa8886eba5a4916578a3b8743Dianne Hackborn cbi.cb.onRouteUngrouped(cbi.router, info, group); 6629a1de308cea2d160778fd977825f10a07b49d738Adam Powell } 6639a1de308cea2d160778fd977825f10a07b49d738Adam Powell } 6649a1de308cea2d160778fd977825f10a07b49d738Adam Powell } 6659a1de308cea2d160778fd977825f10a07b49d738Adam Powell 6668e37a85bf3dc39519942698dc90a3951306b934bAdam Powell static void dispatchRouteVolumeChanged(RouteInfo info) { 6678e37a85bf3dc39519942698dc90a3951306b934bAdam Powell for (CallbackInfo cbi : sStatic.mCallbacks) { 6688e37a85bf3dc39519942698dc90a3951306b934bAdam Powell if ((cbi.type & info.mSupportedTypes) != 0) { 6698e37a85bf3dc39519942698dc90a3951306b934bAdam Powell cbi.cb.onRouteVolumeChanged(cbi.router, info); 6708e37a85bf3dc39519942698dc90a3951306b934bAdam Powell } 6718e37a85bf3dc39519942698dc90a3951306b934bAdam Powell } 6728e37a85bf3dc39519942698dc90a3951306b934bAdam Powell } 6738e37a85bf3dc39519942698dc90a3951306b934bAdam Powell 6748e37a85bf3dc39519942698dc90a3951306b934bAdam Powell static void systemVolumeChanged(int newValue) { 6758e37a85bf3dc39519942698dc90a3951306b934bAdam Powell final RouteInfo selectedRoute = sStatic.mSelectedRoute; 6768e37a85bf3dc39519942698dc90a3951306b934bAdam Powell if (selectedRoute == null) return; 6778e37a85bf3dc39519942698dc90a3951306b934bAdam Powell 6788e37a85bf3dc39519942698dc90a3951306b934bAdam Powell if (selectedRoute == sStatic.mBluetoothA2dpRoute || 679705ab808cf023e0cc38c2ba7cdb9571942cdc04fAdam Powell selectedRoute == sStatic.mDefaultAudioVideo) { 6808e37a85bf3dc39519942698dc90a3951306b934bAdam Powell dispatchRouteVolumeChanged(selectedRoute); 6818e37a85bf3dc39519942698dc90a3951306b934bAdam Powell } else if (sStatic.mBluetoothA2dpRoute != null) { 6828e37a85bf3dc39519942698dc90a3951306b934bAdam Powell try { 6838e37a85bf3dc39519942698dc90a3951306b934bAdam Powell dispatchRouteVolumeChanged(sStatic.mAudioService.isBluetoothA2dpOn() ? 684705ab808cf023e0cc38c2ba7cdb9571942cdc04fAdam Powell sStatic.mBluetoothA2dpRoute : sStatic.mDefaultAudioVideo); 6858e37a85bf3dc39519942698dc90a3951306b934bAdam Powell } catch (RemoteException e) { 6868e37a85bf3dc39519942698dc90a3951306b934bAdam Powell Log.e(TAG, "Error checking Bluetooth A2DP state to report volume change", e); 6878e37a85bf3dc39519942698dc90a3951306b934bAdam Powell } 6888e37a85bf3dc39519942698dc90a3951306b934bAdam Powell } else { 689705ab808cf023e0cc38c2ba7cdb9571942cdc04fAdam Powell dispatchRouteVolumeChanged(sStatic.mDefaultAudioVideo); 690705ab808cf023e0cc38c2ba7cdb9571942cdc04fAdam Powell } 691705ab808cf023e0cc38c2ba7cdb9571942cdc04fAdam Powell } 692705ab808cf023e0cc38c2ba7cdb9571942cdc04fAdam Powell 693705ab808cf023e0cc38c2ba7cdb9571942cdc04fAdam Powell static void updateWifiDisplayStatus(WifiDisplayStatus newStatus) { 694705ab808cf023e0cc38c2ba7cdb9571942cdc04fAdam Powell final WifiDisplayStatus oldStatus = sStatic.mLastKnownWifiDisplayStatus; 695705ab808cf023e0cc38c2ba7cdb9571942cdc04fAdam Powell 696705ab808cf023e0cc38c2ba7cdb9571942cdc04fAdam Powell // TODO Naive implementation. Make this smarter later. 697b072a9686c29bfbc05b732076a4e89bcca8db08aAdam Powell boolean wantScan = false; 698b072a9686c29bfbc05b732076a4e89bcca8db08aAdam Powell boolean blockScan = false; 699705ab808cf023e0cc38c2ba7cdb9571942cdc04fAdam Powell WifiDisplay[] oldDisplays = oldStatus != null ? 700705ab808cf023e0cc38c2ba7cdb9571942cdc04fAdam Powell oldStatus.getRememberedDisplays() : new WifiDisplay[0]; 701705ab808cf023e0cc38c2ba7cdb9571942cdc04fAdam Powell WifiDisplay[] newDisplays = newStatus.getRememberedDisplays(); 702705ab808cf023e0cc38c2ba7cdb9571942cdc04fAdam Powell WifiDisplay[] availableDisplays = newStatus.getAvailableDisplays(); 7032ee6a2a83262d05a566bd713d238e89edfd33a29Adam Powell WifiDisplay activeDisplay = newStatus.getActiveDisplay(); 704705ab808cf023e0cc38c2ba7cdb9571942cdc04fAdam Powell 705705ab808cf023e0cc38c2ba7cdb9571942cdc04fAdam Powell for (int i = 0; i < newDisplays.length; i++) { 706705ab808cf023e0cc38c2ba7cdb9571942cdc04fAdam Powell final WifiDisplay d = newDisplays[i]; 707705ab808cf023e0cc38c2ba7cdb9571942cdc04fAdam Powell final WifiDisplay oldRemembered = findMatchingDisplay(d, oldDisplays); 708705ab808cf023e0cc38c2ba7cdb9571942cdc04fAdam Powell if (oldRemembered == null) { 70911b999d0aaca72a33526795b6849b473cc3dd569Adam Powell addRouteStatic(makeWifiDisplayRoute(d, 71011b999d0aaca72a33526795b6849b473cc3dd569Adam Powell findMatchingDisplay(d, availableDisplays) != null)); 711b072a9686c29bfbc05b732076a4e89bcca8db08aAdam Powell wantScan = true; 712705ab808cf023e0cc38c2ba7cdb9571942cdc04fAdam Powell } else { 713705ab808cf023e0cc38c2ba7cdb9571942cdc04fAdam Powell final boolean available = findMatchingDisplay(d, availableDisplays) != null; 714705ab808cf023e0cc38c2ba7cdb9571942cdc04fAdam Powell final RouteInfo route = findWifiDisplayRoute(d); 715705ab808cf023e0cc38c2ba7cdb9571942cdc04fAdam Powell updateWifiDisplayRoute(route, d, available, newStatus); 716705ab808cf023e0cc38c2ba7cdb9571942cdc04fAdam Powell } 7172ee6a2a83262d05a566bd713d238e89edfd33a29Adam Powell if (d.equals(activeDisplay)) { 7182ee6a2a83262d05a566bd713d238e89edfd33a29Adam Powell final RouteInfo activeRoute = findWifiDisplayRoute(d); 7192ee6a2a83262d05a566bd713d238e89edfd33a29Adam Powell if (activeRoute != null) { 7202ee6a2a83262d05a566bd713d238e89edfd33a29Adam Powell selectRouteStatic(activeRoute.getSupportedTypes(), activeRoute); 721b072a9686c29bfbc05b732076a4e89bcca8db08aAdam Powell 722b072a9686c29bfbc05b732076a4e89bcca8db08aAdam Powell // Don't scan if we're already connected to a wifi display, 723b072a9686c29bfbc05b732076a4e89bcca8db08aAdam Powell // the scanning process can cause a hiccup with some configurations. 724b072a9686c29bfbc05b732076a4e89bcca8db08aAdam Powell blockScan = true; 7252ee6a2a83262d05a566bd713d238e89edfd33a29Adam Powell } 7262ee6a2a83262d05a566bd713d238e89edfd33a29Adam Powell } 727705ab808cf023e0cc38c2ba7cdb9571942cdc04fAdam Powell } 728705ab808cf023e0cc38c2ba7cdb9571942cdc04fAdam Powell for (int i = 0; i < oldDisplays.length; i++) { 729705ab808cf023e0cc38c2ba7cdb9571942cdc04fAdam Powell final WifiDisplay d = oldDisplays[i]; 730705ab808cf023e0cc38c2ba7cdb9571942cdc04fAdam Powell final WifiDisplay newDisplay = findMatchingDisplay(d, newDisplays); 731705ab808cf023e0cc38c2ba7cdb9571942cdc04fAdam Powell if (newDisplay == null) { 732705ab808cf023e0cc38c2ba7cdb9571942cdc04fAdam Powell removeRoute(findWifiDisplayRoute(d)); 733705ab808cf023e0cc38c2ba7cdb9571942cdc04fAdam Powell } 734705ab808cf023e0cc38c2ba7cdb9571942cdc04fAdam Powell } 735705ab808cf023e0cc38c2ba7cdb9571942cdc04fAdam Powell 736b072a9686c29bfbc05b732076a4e89bcca8db08aAdam Powell if (wantScan && !blockScan) { 737705ab808cf023e0cc38c2ba7cdb9571942cdc04fAdam Powell sStatic.mDisplayService.scanWifiDisplays(); 738705ab808cf023e0cc38c2ba7cdb9571942cdc04fAdam Powell } 739705ab808cf023e0cc38c2ba7cdb9571942cdc04fAdam Powell 740705ab808cf023e0cc38c2ba7cdb9571942cdc04fAdam Powell sStatic.mLastKnownWifiDisplayStatus = newStatus; 741705ab808cf023e0cc38c2ba7cdb9571942cdc04fAdam Powell } 742705ab808cf023e0cc38c2ba7cdb9571942cdc04fAdam Powell 74311b999d0aaca72a33526795b6849b473cc3dd569Adam Powell static RouteInfo makeWifiDisplayRoute(WifiDisplay display, boolean available) { 744705ab808cf023e0cc38c2ba7cdb9571942cdc04fAdam Powell final RouteInfo newRoute = new RouteInfo(sStatic.mSystemCategory); 745705ab808cf023e0cc38c2ba7cdb9571942cdc04fAdam Powell newRoute.mDeviceAddress = display.getDeviceAddress(); 746705ab808cf023e0cc38c2ba7cdb9571942cdc04fAdam Powell newRoute.mSupportedTypes = ROUTE_TYPE_LIVE_AUDIO | ROUTE_TYPE_LIVE_VIDEO; 747705ab808cf023e0cc38c2ba7cdb9571942cdc04fAdam Powell newRoute.mVolumeHandling = RouteInfo.PLAYBACK_VOLUME_FIXED; 748705ab808cf023e0cc38c2ba7cdb9571942cdc04fAdam Powell newRoute.mPlaybackType = RouteInfo.PLAYBACK_TYPE_REMOTE; 74911b999d0aaca72a33526795b6849b473cc3dd569Adam Powell 75011b999d0aaca72a33526795b6849b473cc3dd569Adam Powell newRoute.setStatusCode(available ? 75111b999d0aaca72a33526795b6849b473cc3dd569Adam Powell RouteInfo.STATUS_AVAILABLE : RouteInfo.STATUS_CONNECTING); 75211b999d0aaca72a33526795b6849b473cc3dd569Adam Powell newRoute.mEnabled = available; 753705ab808cf023e0cc38c2ba7cdb9571942cdc04fAdam Powell 7542444ae7e2b8658a4a90f996e678423558744b4a2Jeff Brown newRoute.mName = display.getFriendlyDisplayName(); 755705ab808cf023e0cc38c2ba7cdb9571942cdc04fAdam Powell return newRoute; 756705ab808cf023e0cc38c2ba7cdb9571942cdc04fAdam Powell } 757705ab808cf023e0cc38c2ba7cdb9571942cdc04fAdam Powell 758705ab808cf023e0cc38c2ba7cdb9571942cdc04fAdam Powell private static void updateWifiDisplayRoute(RouteInfo route, WifiDisplay display, 759705ab808cf023e0cc38c2ba7cdb9571942cdc04fAdam Powell boolean available, WifiDisplayStatus wifiDisplayStatus) { 760705ab808cf023e0cc38c2ba7cdb9571942cdc04fAdam Powell final boolean isScanning = 761705ab808cf023e0cc38c2ba7cdb9571942cdc04fAdam Powell wifiDisplayStatus.getScanState() == WifiDisplayStatus.SCAN_STATE_SCANNING; 762705ab808cf023e0cc38c2ba7cdb9571942cdc04fAdam Powell 763705ab808cf023e0cc38c2ba7cdb9571942cdc04fAdam Powell boolean changed = false; 764705ab808cf023e0cc38c2ba7cdb9571942cdc04fAdam Powell int newStatus = RouteInfo.STATUS_NONE; 765705ab808cf023e0cc38c2ba7cdb9571942cdc04fAdam Powell 766705ab808cf023e0cc38c2ba7cdb9571942cdc04fAdam Powell if (available) { 767705ab808cf023e0cc38c2ba7cdb9571942cdc04fAdam Powell newStatus = isScanning ? RouteInfo.STATUS_SCANNING : RouteInfo.STATUS_AVAILABLE; 768705ab808cf023e0cc38c2ba7cdb9571942cdc04fAdam Powell } else { 769705ab808cf023e0cc38c2ba7cdb9571942cdc04fAdam Powell newStatus = RouteInfo.STATUS_NOT_AVAILABLE; 770705ab808cf023e0cc38c2ba7cdb9571942cdc04fAdam Powell } 771705ab808cf023e0cc38c2ba7cdb9571942cdc04fAdam Powell 772705ab808cf023e0cc38c2ba7cdb9571942cdc04fAdam Powell if (display.equals(wifiDisplayStatus.getActiveDisplay())) { 773705ab808cf023e0cc38c2ba7cdb9571942cdc04fAdam Powell final int activeState = wifiDisplayStatus.getActiveDisplayState(); 774705ab808cf023e0cc38c2ba7cdb9571942cdc04fAdam Powell switch (activeState) { 775705ab808cf023e0cc38c2ba7cdb9571942cdc04fAdam Powell case WifiDisplayStatus.DISPLAY_STATE_CONNECTED: 776705ab808cf023e0cc38c2ba7cdb9571942cdc04fAdam Powell newStatus = RouteInfo.STATUS_NONE; 777705ab808cf023e0cc38c2ba7cdb9571942cdc04fAdam Powell break; 778705ab808cf023e0cc38c2ba7cdb9571942cdc04fAdam Powell case WifiDisplayStatus.DISPLAY_STATE_CONNECTING: 779705ab808cf023e0cc38c2ba7cdb9571942cdc04fAdam Powell newStatus = RouteInfo.STATUS_CONNECTING; 780705ab808cf023e0cc38c2ba7cdb9571942cdc04fAdam Powell break; 781705ab808cf023e0cc38c2ba7cdb9571942cdc04fAdam Powell case WifiDisplayStatus.DISPLAY_STATE_NOT_CONNECTED: 782705ab808cf023e0cc38c2ba7cdb9571942cdc04fAdam Powell Log.e(TAG, "Active display is not connected!"); 783705ab808cf023e0cc38c2ba7cdb9571942cdc04fAdam Powell break; 784705ab808cf023e0cc38c2ba7cdb9571942cdc04fAdam Powell } 785705ab808cf023e0cc38c2ba7cdb9571942cdc04fAdam Powell } 786705ab808cf023e0cc38c2ba7cdb9571942cdc04fAdam Powell 7872444ae7e2b8658a4a90f996e678423558744b4a2Jeff Brown final String newName = display.getFriendlyDisplayName(); 7882444ae7e2b8658a4a90f996e678423558744b4a2Jeff Brown if (!route.getName().equals(newName)) { 789705ab808cf023e0cc38c2ba7cdb9571942cdc04fAdam Powell route.mName = newName; 790705ab808cf023e0cc38c2ba7cdb9571942cdc04fAdam Powell changed = true; 791705ab808cf023e0cc38c2ba7cdb9571942cdc04fAdam Powell } 792705ab808cf023e0cc38c2ba7cdb9571942cdc04fAdam Powell 793705ab808cf023e0cc38c2ba7cdb9571942cdc04fAdam Powell changed |= route.mEnabled != available; 794705ab808cf023e0cc38c2ba7cdb9571942cdc04fAdam Powell route.mEnabled = available; 795705ab808cf023e0cc38c2ba7cdb9571942cdc04fAdam Powell 796705ab808cf023e0cc38c2ba7cdb9571942cdc04fAdam Powell changed |= route.setStatusCode(newStatus); 797705ab808cf023e0cc38c2ba7cdb9571942cdc04fAdam Powell 798705ab808cf023e0cc38c2ba7cdb9571942cdc04fAdam Powell if (changed) { 799705ab808cf023e0cc38c2ba7cdb9571942cdc04fAdam Powell dispatchRouteChanged(route); 800705ab808cf023e0cc38c2ba7cdb9571942cdc04fAdam Powell } 801705ab808cf023e0cc38c2ba7cdb9571942cdc04fAdam Powell 802705ab808cf023e0cc38c2ba7cdb9571942cdc04fAdam Powell if (!available && route == sStatic.mSelectedRoute) { 803705ab808cf023e0cc38c2ba7cdb9571942cdc04fAdam Powell // Oops, no longer available. Reselect the default. 804705ab808cf023e0cc38c2ba7cdb9571942cdc04fAdam Powell final RouteInfo defaultRoute = sStatic.mDefaultAudioVideo; 805705ab808cf023e0cc38c2ba7cdb9571942cdc04fAdam Powell selectRouteStatic(defaultRoute.getSupportedTypes(), defaultRoute); 806705ab808cf023e0cc38c2ba7cdb9571942cdc04fAdam Powell } 807705ab808cf023e0cc38c2ba7cdb9571942cdc04fAdam Powell } 808705ab808cf023e0cc38c2ba7cdb9571942cdc04fAdam Powell 8092444ae7e2b8658a4a90f996e678423558744b4a2Jeff Brown private static WifiDisplay findMatchingDisplay(WifiDisplay d, WifiDisplay[] displays) { 810705ab808cf023e0cc38c2ba7cdb9571942cdc04fAdam Powell for (int i = 0; i < displays.length; i++) { 8112444ae7e2b8658a4a90f996e678423558744b4a2Jeff Brown final WifiDisplay other = displays[i]; 8122444ae7e2b8658a4a90f996e678423558744b4a2Jeff Brown if (d.getDeviceAddress().equals(other.getDeviceAddress())) { 8132444ae7e2b8658a4a90f996e678423558744b4a2Jeff Brown return other; 814705ab808cf023e0cc38c2ba7cdb9571942cdc04fAdam Powell } 815705ab808cf023e0cc38c2ba7cdb9571942cdc04fAdam Powell } 816705ab808cf023e0cc38c2ba7cdb9571942cdc04fAdam Powell return null; 817705ab808cf023e0cc38c2ba7cdb9571942cdc04fAdam Powell } 818705ab808cf023e0cc38c2ba7cdb9571942cdc04fAdam Powell 819705ab808cf023e0cc38c2ba7cdb9571942cdc04fAdam Powell private static RouteInfo findWifiDisplayRoute(WifiDisplay d) { 820705ab808cf023e0cc38c2ba7cdb9571942cdc04fAdam Powell final int count = sStatic.mRoutes.size(); 821705ab808cf023e0cc38c2ba7cdb9571942cdc04fAdam Powell for (int i = 0; i < count; i++) { 822705ab808cf023e0cc38c2ba7cdb9571942cdc04fAdam Powell final RouteInfo info = sStatic.mRoutes.get(i); 823705ab808cf023e0cc38c2ba7cdb9571942cdc04fAdam Powell if (d.getDeviceAddress().equals(info.mDeviceAddress)) { 824705ab808cf023e0cc38c2ba7cdb9571942cdc04fAdam Powell return info; 825705ab808cf023e0cc38c2ba7cdb9571942cdc04fAdam Powell } 826705ab808cf023e0cc38c2ba7cdb9571942cdc04fAdam Powell } 827705ab808cf023e0cc38c2ba7cdb9571942cdc04fAdam Powell return null; 8288e37a85bf3dc39519942698dc90a3951306b934bAdam Powell } 8298e37a85bf3dc39519942698dc90a3951306b934bAdam Powell 8309a1de308cea2d160778fd977825f10a07b49d738Adam Powell /** 8319a1de308cea2d160778fd977825f10a07b49d738Adam Powell * Information about a media route. 8329a1de308cea2d160778fd977825f10a07b49d738Adam Powell */ 833b58b8f832d06b0ffa8886eba5a4916578a3b8743Dianne Hackborn public static class RouteInfo { 8349a1de308cea2d160778fd977825f10a07b49d738Adam Powell CharSequence mName; 8350d03c042f90bf62d5bad7c64e77028a5f9f8fae0Adam Powell int mNameResId; 8369a1de308cea2d160778fd977825f10a07b49d738Adam Powell private CharSequence mStatus; 8379a1de308cea2d160778fd977825f10a07b49d738Adam Powell int mSupportedTypes; 8389a1de308cea2d160778fd977825f10a07b49d738Adam Powell RouteGroup mGroup; 8399a1de308cea2d160778fd977825f10a07b49d738Adam Powell final RouteCategory mCategory; 840ae20ae1a8aaa013813c356ae1d9541ca7ff020aeAdam Powell Drawable mIcon; 8411357012968f9066ea3051d83995e9bac69526c3cJean-Michel Trivi // playback information 8421357012968f9066ea3051d83995e9bac69526c3cJean-Michel Trivi int mPlaybackType = PLAYBACK_TYPE_LOCAL; 8431357012968f9066ea3051d83995e9bac69526c3cJean-Michel Trivi int mVolumeMax = RemoteControlClient.DEFAULT_PLAYBACK_VOLUME; 8441357012968f9066ea3051d83995e9bac69526c3cJean-Michel Trivi int mVolume = RemoteControlClient.DEFAULT_PLAYBACK_VOLUME; 8451357012968f9066ea3051d83995e9bac69526c3cJean-Michel Trivi int mVolumeHandling = RemoteControlClient.DEFAULT_PLAYBACK_VOLUME_HANDLING; 8461357012968f9066ea3051d83995e9bac69526c3cJean-Michel Trivi int mPlaybackStream = AudioManager.STREAM_MUSIC; 8471357012968f9066ea3051d83995e9bac69526c3cJean-Michel Trivi VolumeCallbackInfo mVcb; 8489a1de308cea2d160778fd977825f10a07b49d738Adam Powell 849705ab808cf023e0cc38c2ba7cdb9571942cdc04fAdam Powell String mDeviceAddress; 850705ab808cf023e0cc38c2ba7cdb9571942cdc04fAdam Powell boolean mEnabled = true; 851705ab808cf023e0cc38c2ba7cdb9571942cdc04fAdam Powell 852705ab808cf023e0cc38c2ba7cdb9571942cdc04fAdam Powell // A predetermined connection status that can override mStatus 853705ab808cf023e0cc38c2ba7cdb9571942cdc04fAdam Powell private int mStatusCode; 854705ab808cf023e0cc38c2ba7cdb9571942cdc04fAdam Powell 8552ee6a2a83262d05a566bd713d238e89edfd33a29Adam Powell /** @hide */ public static final int STATUS_NONE = 0; 8562ee6a2a83262d05a566bd713d238e89edfd33a29Adam Powell /** @hide */ public static final int STATUS_SCANNING = 1; 8572ee6a2a83262d05a566bd713d238e89edfd33a29Adam Powell /** @hide */ public static final int STATUS_CONNECTING = 2; 8582ee6a2a83262d05a566bd713d238e89edfd33a29Adam Powell /** @hide */ public static final int STATUS_AVAILABLE = 3; 8592ee6a2a83262d05a566bd713d238e89edfd33a29Adam Powell /** @hide */ public static final int STATUS_NOT_AVAILABLE = 4; 860705ab808cf023e0cc38c2ba7cdb9571942cdc04fAdam Powell 861b5e2af5919351486a385effe77409d2a91ae9c19Adam Powell private Object mTag; 862b5e2af5919351486a385effe77409d2a91ae9c19Adam Powell 8631357012968f9066ea3051d83995e9bac69526c3cJean-Michel Trivi /** 8641357012968f9066ea3051d83995e9bac69526c3cJean-Michel Trivi * The default playback type, "local", indicating the presentation of the media is happening 8651357012968f9066ea3051d83995e9bac69526c3cJean-Michel Trivi * on the same device (e.g. a phone, a tablet) as where it is controlled from. 8661357012968f9066ea3051d83995e9bac69526c3cJean-Michel Trivi * @see #setPlaybackType(int) 8671357012968f9066ea3051d83995e9bac69526c3cJean-Michel Trivi */ 8681357012968f9066ea3051d83995e9bac69526c3cJean-Michel Trivi public final static int PLAYBACK_TYPE_LOCAL = 0; 8691357012968f9066ea3051d83995e9bac69526c3cJean-Michel Trivi /** 8701357012968f9066ea3051d83995e9bac69526c3cJean-Michel Trivi * A playback type indicating the presentation of the media is happening on 8711357012968f9066ea3051d83995e9bac69526c3cJean-Michel Trivi * a different device (i.e. the remote device) than where it is controlled from. 8721357012968f9066ea3051d83995e9bac69526c3cJean-Michel Trivi * @see #setPlaybackType(int) 8731357012968f9066ea3051d83995e9bac69526c3cJean-Michel Trivi */ 8741357012968f9066ea3051d83995e9bac69526c3cJean-Michel Trivi public final static int PLAYBACK_TYPE_REMOTE = 1; 8751357012968f9066ea3051d83995e9bac69526c3cJean-Michel Trivi /** 8761357012968f9066ea3051d83995e9bac69526c3cJean-Michel Trivi * Playback information indicating the playback volume is fixed, i.e. it cannot be 8771357012968f9066ea3051d83995e9bac69526c3cJean-Michel Trivi * controlled from this object. An example of fixed playback volume is a remote player, 8781357012968f9066ea3051d83995e9bac69526c3cJean-Michel Trivi * playing over HDMI where the user prefers to control the volume on the HDMI sink, rather 8791357012968f9066ea3051d83995e9bac69526c3cJean-Michel Trivi * than attenuate at the source. 8801357012968f9066ea3051d83995e9bac69526c3cJean-Michel Trivi * @see #setVolumeHandling(int) 8811357012968f9066ea3051d83995e9bac69526c3cJean-Michel Trivi */ 8821357012968f9066ea3051d83995e9bac69526c3cJean-Michel Trivi public final static int PLAYBACK_VOLUME_FIXED = 0; 8831357012968f9066ea3051d83995e9bac69526c3cJean-Michel Trivi /** 8841357012968f9066ea3051d83995e9bac69526c3cJean-Michel Trivi * Playback information indicating the playback volume is variable and can be controlled 8851357012968f9066ea3051d83995e9bac69526c3cJean-Michel Trivi * from this object. 8861357012968f9066ea3051d83995e9bac69526c3cJean-Michel Trivi */ 8871357012968f9066ea3051d83995e9bac69526c3cJean-Michel Trivi public final static int PLAYBACK_VOLUME_VARIABLE = 1; 8881357012968f9066ea3051d83995e9bac69526c3cJean-Michel Trivi 8899a1de308cea2d160778fd977825f10a07b49d738Adam Powell RouteInfo(RouteCategory category) { 8909a1de308cea2d160778fd977825f10a07b49d738Adam Powell mCategory = category; 8919a1de308cea2d160778fd977825f10a07b49d738Adam Powell } 8929a1de308cea2d160778fd977825f10a07b49d738Adam Powell 8939a1de308cea2d160778fd977825f10a07b49d738Adam Powell /** 8949a1de308cea2d160778fd977825f10a07b49d738Adam Powell * @return The user-friendly name of a media route. This is the string presented 8959a1de308cea2d160778fd977825f10a07b49d738Adam Powell * to users who may select this as the active route. 8969a1de308cea2d160778fd977825f10a07b49d738Adam Powell */ 8979a1de308cea2d160778fd977825f10a07b49d738Adam Powell public CharSequence getName() { 8980d03c042f90bf62d5bad7c64e77028a5f9f8fae0Adam Powell return getName(sStatic.mResources); 8990d03c042f90bf62d5bad7c64e77028a5f9f8fae0Adam Powell } 9000d03c042f90bf62d5bad7c64e77028a5f9f8fae0Adam Powell 9010d03c042f90bf62d5bad7c64e77028a5f9f8fae0Adam Powell /** 9020d03c042f90bf62d5bad7c64e77028a5f9f8fae0Adam Powell * Return the properly localized/resource selected name of this route. 9030d03c042f90bf62d5bad7c64e77028a5f9f8fae0Adam Powell * 9040d03c042f90bf62d5bad7c64e77028a5f9f8fae0Adam Powell * @param context Context used to resolve the correct configuration to load 9050d03c042f90bf62d5bad7c64e77028a5f9f8fae0Adam Powell * @return The user-friendly name of the media route. This is the string presented 9060d03c042f90bf62d5bad7c64e77028a5f9f8fae0Adam Powell * to users who may select this as the active route. 9070d03c042f90bf62d5bad7c64e77028a5f9f8fae0Adam Powell */ 9080d03c042f90bf62d5bad7c64e77028a5f9f8fae0Adam Powell public CharSequence getName(Context context) { 9090d03c042f90bf62d5bad7c64e77028a5f9f8fae0Adam Powell return getName(context.getResources()); 9100d03c042f90bf62d5bad7c64e77028a5f9f8fae0Adam Powell } 9110d03c042f90bf62d5bad7c64e77028a5f9f8fae0Adam Powell 9120d03c042f90bf62d5bad7c64e77028a5f9f8fae0Adam Powell CharSequence getName(Resources res) { 9130d03c042f90bf62d5bad7c64e77028a5f9f8fae0Adam Powell if (mNameResId != 0) { 9140d03c042f90bf62d5bad7c64e77028a5f9f8fae0Adam Powell return mName = res.getText(mNameResId); 9150d03c042f90bf62d5bad7c64e77028a5f9f8fae0Adam Powell } 9169a1de308cea2d160778fd977825f10a07b49d738Adam Powell return mName; 9179a1de308cea2d160778fd977825f10a07b49d738Adam Powell } 9189a1de308cea2d160778fd977825f10a07b49d738Adam Powell 9199a1de308cea2d160778fd977825f10a07b49d738Adam Powell /** 9209a1de308cea2d160778fd977825f10a07b49d738Adam Powell * @return The user-friendly status for a media route. This may include a description 9219a1de308cea2d160778fd977825f10a07b49d738Adam Powell * of the currently playing media, if available. 9229a1de308cea2d160778fd977825f10a07b49d738Adam Powell */ 9239a1de308cea2d160778fd977825f10a07b49d738Adam Powell public CharSequence getStatus() { 9249a1de308cea2d160778fd977825f10a07b49d738Adam Powell return mStatus; 9259a1de308cea2d160778fd977825f10a07b49d738Adam Powell } 9269a1de308cea2d160778fd977825f10a07b49d738Adam Powell 9279a1de308cea2d160778fd977825f10a07b49d738Adam Powell /** 928705ab808cf023e0cc38c2ba7cdb9571942cdc04fAdam Powell * Set this route's status by predetermined status code. If the caller 929705ab808cf023e0cc38c2ba7cdb9571942cdc04fAdam Powell * should dispatch a route changed event this call will return true; 930705ab808cf023e0cc38c2ba7cdb9571942cdc04fAdam Powell */ 931705ab808cf023e0cc38c2ba7cdb9571942cdc04fAdam Powell boolean setStatusCode(int statusCode) { 932705ab808cf023e0cc38c2ba7cdb9571942cdc04fAdam Powell if (statusCode != mStatusCode) { 933705ab808cf023e0cc38c2ba7cdb9571942cdc04fAdam Powell mStatusCode = statusCode; 934705ab808cf023e0cc38c2ba7cdb9571942cdc04fAdam Powell int resId = 0; 935705ab808cf023e0cc38c2ba7cdb9571942cdc04fAdam Powell switch (statusCode) { 936705ab808cf023e0cc38c2ba7cdb9571942cdc04fAdam Powell case STATUS_SCANNING: 937705ab808cf023e0cc38c2ba7cdb9571942cdc04fAdam Powell resId = com.android.internal.R.string.media_route_status_scanning; 938705ab808cf023e0cc38c2ba7cdb9571942cdc04fAdam Powell break; 939705ab808cf023e0cc38c2ba7cdb9571942cdc04fAdam Powell case STATUS_CONNECTING: 940705ab808cf023e0cc38c2ba7cdb9571942cdc04fAdam Powell resId = com.android.internal.R.string.media_route_status_connecting; 941705ab808cf023e0cc38c2ba7cdb9571942cdc04fAdam Powell break; 942705ab808cf023e0cc38c2ba7cdb9571942cdc04fAdam Powell case STATUS_AVAILABLE: 943705ab808cf023e0cc38c2ba7cdb9571942cdc04fAdam Powell resId = com.android.internal.R.string.media_route_status_available; 944705ab808cf023e0cc38c2ba7cdb9571942cdc04fAdam Powell break; 945705ab808cf023e0cc38c2ba7cdb9571942cdc04fAdam Powell case STATUS_NOT_AVAILABLE: 946705ab808cf023e0cc38c2ba7cdb9571942cdc04fAdam Powell resId = com.android.internal.R.string.media_route_status_not_available; 947705ab808cf023e0cc38c2ba7cdb9571942cdc04fAdam Powell break; 948705ab808cf023e0cc38c2ba7cdb9571942cdc04fAdam Powell } 949705ab808cf023e0cc38c2ba7cdb9571942cdc04fAdam Powell mStatus = resId != 0 ? sStatic.mResources.getText(resId) : null; 950705ab808cf023e0cc38c2ba7cdb9571942cdc04fAdam Powell return true; 951705ab808cf023e0cc38c2ba7cdb9571942cdc04fAdam Powell } 952705ab808cf023e0cc38c2ba7cdb9571942cdc04fAdam Powell return false; 953705ab808cf023e0cc38c2ba7cdb9571942cdc04fAdam Powell } 954705ab808cf023e0cc38c2ba7cdb9571942cdc04fAdam Powell 955705ab808cf023e0cc38c2ba7cdb9571942cdc04fAdam Powell /** 9562ee6a2a83262d05a566bd713d238e89edfd33a29Adam Powell * @hide 9572ee6a2a83262d05a566bd713d238e89edfd33a29Adam Powell */ 9582ee6a2a83262d05a566bd713d238e89edfd33a29Adam Powell public int getStatusCode() { 9592ee6a2a83262d05a566bd713d238e89edfd33a29Adam Powell return mStatusCode; 9602ee6a2a83262d05a566bd713d238e89edfd33a29Adam Powell } 9612ee6a2a83262d05a566bd713d238e89edfd33a29Adam Powell 9622ee6a2a83262d05a566bd713d238e89edfd33a29Adam Powell /** 9639a1de308cea2d160778fd977825f10a07b49d738Adam Powell * @return A media type flag set describing which types this route supports. 9649a1de308cea2d160778fd977825f10a07b49d738Adam Powell */ 9659a1de308cea2d160778fd977825f10a07b49d738Adam Powell public int getSupportedTypes() { 9669a1de308cea2d160778fd977825f10a07b49d738Adam Powell return mSupportedTypes; 9679a1de308cea2d160778fd977825f10a07b49d738Adam Powell } 9689a1de308cea2d160778fd977825f10a07b49d738Adam Powell 9699a1de308cea2d160778fd977825f10a07b49d738Adam Powell /** 9709a1de308cea2d160778fd977825f10a07b49d738Adam Powell * @return The group that this route belongs to. 9719a1de308cea2d160778fd977825f10a07b49d738Adam Powell */ 9729a1de308cea2d160778fd977825f10a07b49d738Adam Powell public RouteGroup getGroup() { 9739a1de308cea2d160778fd977825f10a07b49d738Adam Powell return mGroup; 9749a1de308cea2d160778fd977825f10a07b49d738Adam Powell } 9759a1de308cea2d160778fd977825f10a07b49d738Adam Powell 9769a1de308cea2d160778fd977825f10a07b49d738Adam Powell /** 9779a1de308cea2d160778fd977825f10a07b49d738Adam Powell * @return the category this route belongs to. 9789a1de308cea2d160778fd977825f10a07b49d738Adam Powell */ 9799a1de308cea2d160778fd977825f10a07b49d738Adam Powell public RouteCategory getCategory() { 9809a1de308cea2d160778fd977825f10a07b49d738Adam Powell return mCategory; 9819a1de308cea2d160778fd977825f10a07b49d738Adam Powell } 9829a1de308cea2d160778fd977825f10a07b49d738Adam Powell 983ae20ae1a8aaa013813c356ae1d9541ca7ff020aeAdam Powell /** 984ae20ae1a8aaa013813c356ae1d9541ca7ff020aeAdam Powell * Get the icon representing this route. 985ae20ae1a8aaa013813c356ae1d9541ca7ff020aeAdam Powell * This icon will be used in picker UIs if available. 986ae20ae1a8aaa013813c356ae1d9541ca7ff020aeAdam Powell * 987ae20ae1a8aaa013813c356ae1d9541ca7ff020aeAdam Powell * @return the icon representing this route or null if no icon is available 988ae20ae1a8aaa013813c356ae1d9541ca7ff020aeAdam Powell */ 989ae20ae1a8aaa013813c356ae1d9541ca7ff020aeAdam Powell public Drawable getIconDrawable() { 990ae20ae1a8aaa013813c356ae1d9541ca7ff020aeAdam Powell return mIcon; 991ae20ae1a8aaa013813c356ae1d9541ca7ff020aeAdam Powell } 992ae20ae1a8aaa013813c356ae1d9541ca7ff020aeAdam Powell 993b5e2af5919351486a385effe77409d2a91ae9c19Adam Powell /** 994b5e2af5919351486a385effe77409d2a91ae9c19Adam Powell * Set an application-specific tag object for this route. 995b5e2af5919351486a385effe77409d2a91ae9c19Adam Powell * The application may use this to store arbitrary data associated with the 996b5e2af5919351486a385effe77409d2a91ae9c19Adam Powell * route for internal tracking. 997b5e2af5919351486a385effe77409d2a91ae9c19Adam Powell * 998b5e2af5919351486a385effe77409d2a91ae9c19Adam Powell * <p>Note that the lifespan of a route may be well past the lifespan of 999b5e2af5919351486a385effe77409d2a91ae9c19Adam Powell * an Activity or other Context; take care that objects you store here 1000b5e2af5919351486a385effe77409d2a91ae9c19Adam Powell * will not keep more data in memory alive than you intend.</p> 1001b5e2af5919351486a385effe77409d2a91ae9c19Adam Powell * 1002b5e2af5919351486a385effe77409d2a91ae9c19Adam Powell * @param tag Arbitrary, app-specific data for this route to hold for later use 1003b5e2af5919351486a385effe77409d2a91ae9c19Adam Powell */ 1004b5e2af5919351486a385effe77409d2a91ae9c19Adam Powell public void setTag(Object tag) { 1005b5e2af5919351486a385effe77409d2a91ae9c19Adam Powell mTag = tag; 1006130b4572d1f3df702e5b296a655d15a41f6d4c66Adam Powell routeUpdated(); 1007b5e2af5919351486a385effe77409d2a91ae9c19Adam Powell } 1008b5e2af5919351486a385effe77409d2a91ae9c19Adam Powell 1009b5e2af5919351486a385effe77409d2a91ae9c19Adam Powell /** 1010b5e2af5919351486a385effe77409d2a91ae9c19Adam Powell * @return The tag object previously set by the application 1011b5e2af5919351486a385effe77409d2a91ae9c19Adam Powell * @see #setTag(Object) 1012b5e2af5919351486a385effe77409d2a91ae9c19Adam Powell */ 1013b5e2af5919351486a385effe77409d2a91ae9c19Adam Powell public Object getTag() { 1014b5e2af5919351486a385effe77409d2a91ae9c19Adam Powell return mTag; 1015b5e2af5919351486a385effe77409d2a91ae9c19Adam Powell } 1016b5e2af5919351486a385effe77409d2a91ae9c19Adam Powell 10171357012968f9066ea3051d83995e9bac69526c3cJean-Michel Trivi /** 10181357012968f9066ea3051d83995e9bac69526c3cJean-Michel Trivi * @return the type of playback associated with this route 10191357012968f9066ea3051d83995e9bac69526c3cJean-Michel Trivi * @see UserRouteInfo#setPlaybackType(int) 10201357012968f9066ea3051d83995e9bac69526c3cJean-Michel Trivi */ 10211357012968f9066ea3051d83995e9bac69526c3cJean-Michel Trivi public int getPlaybackType() { 10221357012968f9066ea3051d83995e9bac69526c3cJean-Michel Trivi return mPlaybackType; 10231357012968f9066ea3051d83995e9bac69526c3cJean-Michel Trivi } 10241357012968f9066ea3051d83995e9bac69526c3cJean-Michel Trivi 10251357012968f9066ea3051d83995e9bac69526c3cJean-Michel Trivi /** 10261357012968f9066ea3051d83995e9bac69526c3cJean-Michel Trivi * @return the stream over which the playback associated with this route is performed 10271357012968f9066ea3051d83995e9bac69526c3cJean-Michel Trivi * @see UserRouteInfo#setPlaybackStream(int) 10281357012968f9066ea3051d83995e9bac69526c3cJean-Michel Trivi */ 10291357012968f9066ea3051d83995e9bac69526c3cJean-Michel Trivi public int getPlaybackStream() { 10301357012968f9066ea3051d83995e9bac69526c3cJean-Michel Trivi return mPlaybackStream; 10311357012968f9066ea3051d83995e9bac69526c3cJean-Michel Trivi } 10321357012968f9066ea3051d83995e9bac69526c3cJean-Michel Trivi 10331357012968f9066ea3051d83995e9bac69526c3cJean-Michel Trivi /** 10348e37a85bf3dc39519942698dc90a3951306b934bAdam Powell * Return the current volume for this route. Depending on the route, this may only 10358e37a85bf3dc39519942698dc90a3951306b934bAdam Powell * be valid if the route is currently selected. 10368e37a85bf3dc39519942698dc90a3951306b934bAdam Powell * 10371357012968f9066ea3051d83995e9bac69526c3cJean-Michel Trivi * @return the volume at which the playback associated with this route is performed 10381357012968f9066ea3051d83995e9bac69526c3cJean-Michel Trivi * @see UserRouteInfo#setVolume(int) 10391357012968f9066ea3051d83995e9bac69526c3cJean-Michel Trivi */ 10401357012968f9066ea3051d83995e9bac69526c3cJean-Michel Trivi public int getVolume() { 10411357012968f9066ea3051d83995e9bac69526c3cJean-Michel Trivi if (mPlaybackType == PLAYBACK_TYPE_LOCAL) { 10421357012968f9066ea3051d83995e9bac69526c3cJean-Michel Trivi int vol = 0; 10431357012968f9066ea3051d83995e9bac69526c3cJean-Michel Trivi try { 10441357012968f9066ea3051d83995e9bac69526c3cJean-Michel Trivi vol = sStatic.mAudioService.getStreamVolume(mPlaybackStream); 10451357012968f9066ea3051d83995e9bac69526c3cJean-Michel Trivi } catch (RemoteException e) { 10461357012968f9066ea3051d83995e9bac69526c3cJean-Michel Trivi Log.e(TAG, "Error getting local stream volume", e); 10471357012968f9066ea3051d83995e9bac69526c3cJean-Michel Trivi } 10481357012968f9066ea3051d83995e9bac69526c3cJean-Michel Trivi return vol; 10491357012968f9066ea3051d83995e9bac69526c3cJean-Michel Trivi } else { 10501357012968f9066ea3051d83995e9bac69526c3cJean-Michel Trivi return mVolume; 10511357012968f9066ea3051d83995e9bac69526c3cJean-Michel Trivi } 10521357012968f9066ea3051d83995e9bac69526c3cJean-Michel Trivi } 10531357012968f9066ea3051d83995e9bac69526c3cJean-Michel Trivi 10541357012968f9066ea3051d83995e9bac69526c3cJean-Michel Trivi /** 10558e37a85bf3dc39519942698dc90a3951306b934bAdam Powell * Request a volume change for this route. 10568e37a85bf3dc39519942698dc90a3951306b934bAdam Powell * @param volume value between 0 and getVolumeMax 10578e37a85bf3dc39519942698dc90a3951306b934bAdam Powell */ 10588e37a85bf3dc39519942698dc90a3951306b934bAdam Powell public void requestSetVolume(int volume) { 10598e37a85bf3dc39519942698dc90a3951306b934bAdam Powell if (mPlaybackType == PLAYBACK_TYPE_LOCAL) { 10608e37a85bf3dc39519942698dc90a3951306b934bAdam Powell try { 10618e37a85bf3dc39519942698dc90a3951306b934bAdam Powell sStatic.mAudioService.setStreamVolume(mPlaybackStream, volume, 0); 10628e37a85bf3dc39519942698dc90a3951306b934bAdam Powell } catch (RemoteException e) { 10638e37a85bf3dc39519942698dc90a3951306b934bAdam Powell Log.e(TAG, "Error setting local stream volume", e); 10648e37a85bf3dc39519942698dc90a3951306b934bAdam Powell } 10658e37a85bf3dc39519942698dc90a3951306b934bAdam Powell } else { 10668e37a85bf3dc39519942698dc90a3951306b934bAdam Powell Log.e(TAG, getClass().getSimpleName() + ".requestSetVolume(): " + 10678e37a85bf3dc39519942698dc90a3951306b934bAdam Powell "Non-local volume playback on system route? " + 10688e37a85bf3dc39519942698dc90a3951306b934bAdam Powell "Could not request volume change."); 10698e37a85bf3dc39519942698dc90a3951306b934bAdam Powell } 10708e37a85bf3dc39519942698dc90a3951306b934bAdam Powell } 10718e37a85bf3dc39519942698dc90a3951306b934bAdam Powell 10728e37a85bf3dc39519942698dc90a3951306b934bAdam Powell /** 10738e37a85bf3dc39519942698dc90a3951306b934bAdam Powell * Request an incremental volume update for this route. 10748e37a85bf3dc39519942698dc90a3951306b934bAdam Powell * @param direction Delta to apply to the current volume 10758e37a85bf3dc39519942698dc90a3951306b934bAdam Powell */ 10768e37a85bf3dc39519942698dc90a3951306b934bAdam Powell public void requestUpdateVolume(int direction) { 10778e37a85bf3dc39519942698dc90a3951306b934bAdam Powell if (mPlaybackType == PLAYBACK_TYPE_LOCAL) { 10788e37a85bf3dc39519942698dc90a3951306b934bAdam Powell try { 10798e37a85bf3dc39519942698dc90a3951306b934bAdam Powell final int volume = 10808e37a85bf3dc39519942698dc90a3951306b934bAdam Powell Math.max(0, Math.min(getVolume() + direction, getVolumeMax())); 10818e37a85bf3dc39519942698dc90a3951306b934bAdam Powell sStatic.mAudioService.setStreamVolume(mPlaybackStream, volume, 0); 10828e37a85bf3dc39519942698dc90a3951306b934bAdam Powell } catch (RemoteException e) { 10838e37a85bf3dc39519942698dc90a3951306b934bAdam Powell Log.e(TAG, "Error setting local stream volume", e); 10848e37a85bf3dc39519942698dc90a3951306b934bAdam Powell } 10858e37a85bf3dc39519942698dc90a3951306b934bAdam Powell } else { 10868e37a85bf3dc39519942698dc90a3951306b934bAdam Powell Log.e(TAG, getClass().getSimpleName() + ".requestChangeVolume(): " + 10878e37a85bf3dc39519942698dc90a3951306b934bAdam Powell "Non-local volume playback on system route? " + 10888e37a85bf3dc39519942698dc90a3951306b934bAdam Powell "Could not request volume change."); 10898e37a85bf3dc39519942698dc90a3951306b934bAdam Powell } 10908e37a85bf3dc39519942698dc90a3951306b934bAdam Powell } 10918e37a85bf3dc39519942698dc90a3951306b934bAdam Powell 10928e37a85bf3dc39519942698dc90a3951306b934bAdam Powell /** 10931357012968f9066ea3051d83995e9bac69526c3cJean-Michel Trivi * @return the maximum volume at which the playback associated with this route is performed 10941357012968f9066ea3051d83995e9bac69526c3cJean-Michel Trivi * @see UserRouteInfo#setVolumeMax(int) 10951357012968f9066ea3051d83995e9bac69526c3cJean-Michel Trivi */ 10961357012968f9066ea3051d83995e9bac69526c3cJean-Michel Trivi public int getVolumeMax() { 10971357012968f9066ea3051d83995e9bac69526c3cJean-Michel Trivi if (mPlaybackType == PLAYBACK_TYPE_LOCAL) { 10981357012968f9066ea3051d83995e9bac69526c3cJean-Michel Trivi int volMax = 0; 10991357012968f9066ea3051d83995e9bac69526c3cJean-Michel Trivi try { 11001357012968f9066ea3051d83995e9bac69526c3cJean-Michel Trivi volMax = sStatic.mAudioService.getStreamMaxVolume(mPlaybackStream); 11011357012968f9066ea3051d83995e9bac69526c3cJean-Michel Trivi } catch (RemoteException e) { 11021357012968f9066ea3051d83995e9bac69526c3cJean-Michel Trivi Log.e(TAG, "Error getting local stream volume", e); 11031357012968f9066ea3051d83995e9bac69526c3cJean-Michel Trivi } 11041357012968f9066ea3051d83995e9bac69526c3cJean-Michel Trivi return volMax; 11051357012968f9066ea3051d83995e9bac69526c3cJean-Michel Trivi } else { 11061357012968f9066ea3051d83995e9bac69526c3cJean-Michel Trivi return mVolumeMax; 11071357012968f9066ea3051d83995e9bac69526c3cJean-Michel Trivi } 11081357012968f9066ea3051d83995e9bac69526c3cJean-Michel Trivi } 11091357012968f9066ea3051d83995e9bac69526c3cJean-Michel Trivi 11101357012968f9066ea3051d83995e9bac69526c3cJean-Michel Trivi /** 11111357012968f9066ea3051d83995e9bac69526c3cJean-Michel Trivi * @return how volume is handling on the route 11121357012968f9066ea3051d83995e9bac69526c3cJean-Michel Trivi * @see UserRouteInfo#setVolumeHandling(int) 11131357012968f9066ea3051d83995e9bac69526c3cJean-Michel Trivi */ 11141357012968f9066ea3051d83995e9bac69526c3cJean-Michel Trivi public int getVolumeHandling() { 11151357012968f9066ea3051d83995e9bac69526c3cJean-Michel Trivi return mVolumeHandling; 11161357012968f9066ea3051d83995e9bac69526c3cJean-Michel Trivi } 11171357012968f9066ea3051d83995e9bac69526c3cJean-Michel Trivi 1118705ab808cf023e0cc38c2ba7cdb9571942cdc04fAdam Powell /** 1119705ab808cf023e0cc38c2ba7cdb9571942cdc04fAdam Powell * @return true if this route is enabled and may be selected 1120705ab808cf023e0cc38c2ba7cdb9571942cdc04fAdam Powell */ 1121705ab808cf023e0cc38c2ba7cdb9571942cdc04fAdam Powell public boolean isEnabled() { 1122705ab808cf023e0cc38c2ba7cdb9571942cdc04fAdam Powell return mEnabled; 1123705ab808cf023e0cc38c2ba7cdb9571942cdc04fAdam Powell } 1124705ab808cf023e0cc38c2ba7cdb9571942cdc04fAdam Powell 11259a1de308cea2d160778fd977825f10a07b49d738Adam Powell void setStatusInt(CharSequence status) { 11269a1de308cea2d160778fd977825f10a07b49d738Adam Powell if (!status.equals(mStatus)) { 11279a1de308cea2d160778fd977825f10a07b49d738Adam Powell mStatus = status; 11289a1de308cea2d160778fd977825f10a07b49d738Adam Powell if (mGroup != null) { 11299a1de308cea2d160778fd977825f10a07b49d738Adam Powell mGroup.memberStatusChanged(this, status); 11309a1de308cea2d160778fd977825f10a07b49d738Adam Powell } 11319a1de308cea2d160778fd977825f10a07b49d738Adam Powell routeUpdated(); 11329a1de308cea2d160778fd977825f10a07b49d738Adam Powell } 11339a1de308cea2d160778fd977825f10a07b49d738Adam Powell } 11349a1de308cea2d160778fd977825f10a07b49d738Adam Powell 11351357012968f9066ea3051d83995e9bac69526c3cJean-Michel Trivi final IRemoteVolumeObserver.Stub mRemoteVolObserver = new IRemoteVolumeObserver.Stub() { 11361357012968f9066ea3051d83995e9bac69526c3cJean-Michel Trivi public void dispatchRemoteVolumeUpdate(final int direction, final int value) { 11371357012968f9066ea3051d83995e9bac69526c3cJean-Michel Trivi sStatic.mHandler.post(new Runnable() { 11381357012968f9066ea3051d83995e9bac69526c3cJean-Michel Trivi @Override 11391357012968f9066ea3051d83995e9bac69526c3cJean-Michel Trivi public void run() { 11401357012968f9066ea3051d83995e9bac69526c3cJean-Michel Trivi if (mVcb != null) { 11411357012968f9066ea3051d83995e9bac69526c3cJean-Michel Trivi if (direction != 0) { 11421357012968f9066ea3051d83995e9bac69526c3cJean-Michel Trivi mVcb.vcb.onVolumeUpdateRequest(mVcb.route, direction); 11431357012968f9066ea3051d83995e9bac69526c3cJean-Michel Trivi } else { 11441357012968f9066ea3051d83995e9bac69526c3cJean-Michel Trivi mVcb.vcb.onVolumeSetRequest(mVcb.route, value); 11451357012968f9066ea3051d83995e9bac69526c3cJean-Michel Trivi } 11461357012968f9066ea3051d83995e9bac69526c3cJean-Michel Trivi } 11471357012968f9066ea3051d83995e9bac69526c3cJean-Michel Trivi } 11481357012968f9066ea3051d83995e9bac69526c3cJean-Michel Trivi }); 11491357012968f9066ea3051d83995e9bac69526c3cJean-Michel Trivi } 11501357012968f9066ea3051d83995e9bac69526c3cJean-Michel Trivi }; 11511357012968f9066ea3051d83995e9bac69526c3cJean-Michel Trivi 11529a1de308cea2d160778fd977825f10a07b49d738Adam Powell void routeUpdated() { 11539a1de308cea2d160778fd977825f10a07b49d738Adam Powell updateRoute(this); 11549a1de308cea2d160778fd977825f10a07b49d738Adam Powell } 11559a1de308cea2d160778fd977825f10a07b49d738Adam Powell 11569a1de308cea2d160778fd977825f10a07b49d738Adam Powell @Override 11579a1de308cea2d160778fd977825f10a07b49d738Adam Powell public String toString() { 1158d6d0bddee363e0c7fe61f63bd9d9864a71d887d6Adam Powell String supportedTypes = typesToString(getSupportedTypes()); 1159d6d0bddee363e0c7fe61f63bd9d9864a71d887d6Adam Powell return getClass().getSimpleName() + "{ name=" + getName() + ", status=" + getStatus() + 1160d6d0bddee363e0c7fe61f63bd9d9864a71d887d6Adam Powell " category=" + getCategory() + 11619a1de308cea2d160778fd977825f10a07b49d738Adam Powell " supportedTypes=" + supportedTypes + "}"; 11629a1de308cea2d160778fd977825f10a07b49d738Adam Powell } 11639a1de308cea2d160778fd977825f10a07b49d738Adam Powell } 11649a1de308cea2d160778fd977825f10a07b49d738Adam Powell 11659a1de308cea2d160778fd977825f10a07b49d738Adam Powell /** 11669a1de308cea2d160778fd977825f10a07b49d738Adam Powell * Information about a route that the application may define and modify. 11678e37a85bf3dc39519942698dc90a3951306b934bAdam Powell * A user route defaults to {@link RouteInfo#PLAYBACK_TYPE_REMOTE} and 11688e37a85bf3dc39519942698dc90a3951306b934bAdam Powell * {@link RouteInfo#PLAYBACK_VOLUME_FIXED}. 11699a1de308cea2d160778fd977825f10a07b49d738Adam Powell * 11709a1de308cea2d160778fd977825f10a07b49d738Adam Powell * @see MediaRouter.RouteInfo 11719a1de308cea2d160778fd977825f10a07b49d738Adam Powell */ 1172b58b8f832d06b0ffa8886eba5a4916578a3b8743Dianne Hackborn public static class UserRouteInfo extends RouteInfo { 1173ae20ae1a8aaa013813c356ae1d9541ca7ff020aeAdam Powell RemoteControlClient mRcc; 11749a1de308cea2d160778fd977825f10a07b49d738Adam Powell 11759a1de308cea2d160778fd977825f10a07b49d738Adam Powell UserRouteInfo(RouteCategory category) { 11769a1de308cea2d160778fd977825f10a07b49d738Adam Powell super(category); 11779a1de308cea2d160778fd977825f10a07b49d738Adam Powell mSupportedTypes = ROUTE_TYPE_USER; 11788e37a85bf3dc39519942698dc90a3951306b934bAdam Powell mPlaybackType = PLAYBACK_TYPE_REMOTE; 11798e37a85bf3dc39519942698dc90a3951306b934bAdam Powell mVolumeHandling = PLAYBACK_VOLUME_FIXED; 11809a1de308cea2d160778fd977825f10a07b49d738Adam Powell } 11819a1de308cea2d160778fd977825f10a07b49d738Adam Powell 11829a1de308cea2d160778fd977825f10a07b49d738Adam Powell /** 11839a1de308cea2d160778fd977825f10a07b49d738Adam Powell * Set the user-visible name of this route. 11849a1de308cea2d160778fd977825f10a07b49d738Adam Powell * @param name Name to display to the user to describe this route 11859a1de308cea2d160778fd977825f10a07b49d738Adam Powell */ 11869a1de308cea2d160778fd977825f10a07b49d738Adam Powell public void setName(CharSequence name) { 11879a1de308cea2d160778fd977825f10a07b49d738Adam Powell mName = name; 11889a1de308cea2d160778fd977825f10a07b49d738Adam Powell routeUpdated(); 11899a1de308cea2d160778fd977825f10a07b49d738Adam Powell } 11900d03c042f90bf62d5bad7c64e77028a5f9f8fae0Adam Powell 11910d03c042f90bf62d5bad7c64e77028a5f9f8fae0Adam Powell /** 11920d03c042f90bf62d5bad7c64e77028a5f9f8fae0Adam Powell * Set the user-visible name of this route. 11930d03c042f90bf62d5bad7c64e77028a5f9f8fae0Adam Powell * @param resId Resource ID of the name to display to the user to describe this route 11940d03c042f90bf62d5bad7c64e77028a5f9f8fae0Adam Powell */ 11950d03c042f90bf62d5bad7c64e77028a5f9f8fae0Adam Powell public void setName(int resId) { 11960d03c042f90bf62d5bad7c64e77028a5f9f8fae0Adam Powell mNameResId = resId; 11970d03c042f90bf62d5bad7c64e77028a5f9f8fae0Adam Powell mName = null; 11980d03c042f90bf62d5bad7c64e77028a5f9f8fae0Adam Powell routeUpdated(); 11990d03c042f90bf62d5bad7c64e77028a5f9f8fae0Adam Powell } 12009a1de308cea2d160778fd977825f10a07b49d738Adam Powell 12019a1de308cea2d160778fd977825f10a07b49d738Adam Powell /** 12029a1de308cea2d160778fd977825f10a07b49d738Adam Powell * Set the current user-visible status for this route. 12039a1de308cea2d160778fd977825f10a07b49d738Adam Powell * @param status Status to display to the user to describe what the endpoint 12049a1de308cea2d160778fd977825f10a07b49d738Adam Powell * of this route is currently doing 12059a1de308cea2d160778fd977825f10a07b49d738Adam Powell */ 12069a1de308cea2d160778fd977825f10a07b49d738Adam Powell public void setStatus(CharSequence status) { 12079a1de308cea2d160778fd977825f10a07b49d738Adam Powell setStatusInt(status); 12089a1de308cea2d160778fd977825f10a07b49d738Adam Powell } 1209ae20ae1a8aaa013813c356ae1d9541ca7ff020aeAdam Powell 1210ae20ae1a8aaa013813c356ae1d9541ca7ff020aeAdam Powell /** 1211ae20ae1a8aaa013813c356ae1d9541ca7ff020aeAdam Powell * Set the RemoteControlClient responsible for reporting playback info for this 1212ae20ae1a8aaa013813c356ae1d9541ca7ff020aeAdam Powell * user route. 1213ae20ae1a8aaa013813c356ae1d9541ca7ff020aeAdam Powell * 1214ae20ae1a8aaa013813c356ae1d9541ca7ff020aeAdam Powell * <p>If this route manages remote playback, the data exposed by this 1215ae20ae1a8aaa013813c356ae1d9541ca7ff020aeAdam Powell * RemoteControlClient will be used to reflect and update information 1216ae20ae1a8aaa013813c356ae1d9541ca7ff020aeAdam Powell * such as route volume info in related UIs.</p> 1217ae20ae1a8aaa013813c356ae1d9541ca7ff020aeAdam Powell * 12181357012968f9066ea3051d83995e9bac69526c3cJean-Michel Trivi * <p>The RemoteControlClient must have been previously registered with 12191357012968f9066ea3051d83995e9bac69526c3cJean-Michel Trivi * {@link AudioManager#registerRemoteControlClient(RemoteControlClient)}.</p> 12201357012968f9066ea3051d83995e9bac69526c3cJean-Michel Trivi * 1221ae20ae1a8aaa013813c356ae1d9541ca7ff020aeAdam Powell * @param rcc RemoteControlClient associated with this route 1222ae20ae1a8aaa013813c356ae1d9541ca7ff020aeAdam Powell */ 1223ae20ae1a8aaa013813c356ae1d9541ca7ff020aeAdam Powell public void setRemoteControlClient(RemoteControlClient rcc) { 1224ae20ae1a8aaa013813c356ae1d9541ca7ff020aeAdam Powell mRcc = rcc; 12251357012968f9066ea3051d83995e9bac69526c3cJean-Michel Trivi updatePlaybackInfoOnRcc(); 1226ae20ae1a8aaa013813c356ae1d9541ca7ff020aeAdam Powell } 1227ae20ae1a8aaa013813c356ae1d9541ca7ff020aeAdam Powell 1228ae20ae1a8aaa013813c356ae1d9541ca7ff020aeAdam Powell /** 12294599696591f745b3a546197d2ba7e5cfc5562484Adam Powell * Retrieve the RemoteControlClient associated with this route, if one has been set. 12304599696591f745b3a546197d2ba7e5cfc5562484Adam Powell * 12314599696591f745b3a546197d2ba7e5cfc5562484Adam Powell * @return the RemoteControlClient associated with this route 12324599696591f745b3a546197d2ba7e5cfc5562484Adam Powell * @see #setRemoteControlClient(RemoteControlClient) 12334599696591f745b3a546197d2ba7e5cfc5562484Adam Powell */ 12344599696591f745b3a546197d2ba7e5cfc5562484Adam Powell public RemoteControlClient getRemoteControlClient() { 12354599696591f745b3a546197d2ba7e5cfc5562484Adam Powell return mRcc; 12364599696591f745b3a546197d2ba7e5cfc5562484Adam Powell } 12374599696591f745b3a546197d2ba7e5cfc5562484Adam Powell 12384599696591f745b3a546197d2ba7e5cfc5562484Adam Powell /** 1239ae20ae1a8aaa013813c356ae1d9541ca7ff020aeAdam Powell * Set an icon that will be used to represent this route. 1240ae20ae1a8aaa013813c356ae1d9541ca7ff020aeAdam Powell * The system may use this icon in picker UIs or similar. 1241ae20ae1a8aaa013813c356ae1d9541ca7ff020aeAdam Powell * 1242ae20ae1a8aaa013813c356ae1d9541ca7ff020aeAdam Powell * @param icon icon drawable to use to represent this route 1243ae20ae1a8aaa013813c356ae1d9541ca7ff020aeAdam Powell */ 1244ae20ae1a8aaa013813c356ae1d9541ca7ff020aeAdam Powell public void setIconDrawable(Drawable icon) { 1245ae20ae1a8aaa013813c356ae1d9541ca7ff020aeAdam Powell mIcon = icon; 1246ae20ae1a8aaa013813c356ae1d9541ca7ff020aeAdam Powell } 1247ae20ae1a8aaa013813c356ae1d9541ca7ff020aeAdam Powell 1248ae20ae1a8aaa013813c356ae1d9541ca7ff020aeAdam Powell /** 1249ae20ae1a8aaa013813c356ae1d9541ca7ff020aeAdam Powell * Set an icon that will be used to represent this route. 1250ae20ae1a8aaa013813c356ae1d9541ca7ff020aeAdam Powell * The system may use this icon in picker UIs or similar. 1251ae20ae1a8aaa013813c356ae1d9541ca7ff020aeAdam Powell * 125271c69897ad0a55d590698bfa399bfe99c763b9dbAdam Powell * @param resId Resource ID of an icon drawable to use to represent this route 1253ae20ae1a8aaa013813c356ae1d9541ca7ff020aeAdam Powell */ 1254ae20ae1a8aaa013813c356ae1d9541ca7ff020aeAdam Powell public void setIconResource(int resId) { 1255ae20ae1a8aaa013813c356ae1d9541ca7ff020aeAdam Powell setIconDrawable(sStatic.mResources.getDrawable(resId)); 1256ae20ae1a8aaa013813c356ae1d9541ca7ff020aeAdam Powell } 12571357012968f9066ea3051d83995e9bac69526c3cJean-Michel Trivi 12581357012968f9066ea3051d83995e9bac69526c3cJean-Michel Trivi /** 12591357012968f9066ea3051d83995e9bac69526c3cJean-Michel Trivi * Set a callback to be notified of volume update requests 12601357012968f9066ea3051d83995e9bac69526c3cJean-Michel Trivi * @param vcb 12611357012968f9066ea3051d83995e9bac69526c3cJean-Michel Trivi */ 12621357012968f9066ea3051d83995e9bac69526c3cJean-Michel Trivi public void setVolumeCallback(VolumeCallback vcb) { 12631357012968f9066ea3051d83995e9bac69526c3cJean-Michel Trivi mVcb = new VolumeCallbackInfo(vcb, this); 12641357012968f9066ea3051d83995e9bac69526c3cJean-Michel Trivi } 12651357012968f9066ea3051d83995e9bac69526c3cJean-Michel Trivi 12661357012968f9066ea3051d83995e9bac69526c3cJean-Michel Trivi /** 12671357012968f9066ea3051d83995e9bac69526c3cJean-Michel Trivi * Defines whether playback associated with this route is "local" 12681357012968f9066ea3051d83995e9bac69526c3cJean-Michel Trivi * ({@link RouteInfo#PLAYBACK_TYPE_LOCAL}) or "remote" 12691357012968f9066ea3051d83995e9bac69526c3cJean-Michel Trivi * ({@link RouteInfo#PLAYBACK_TYPE_REMOTE}). 12701357012968f9066ea3051d83995e9bac69526c3cJean-Michel Trivi * @param type 12711357012968f9066ea3051d83995e9bac69526c3cJean-Michel Trivi */ 12721357012968f9066ea3051d83995e9bac69526c3cJean-Michel Trivi public void setPlaybackType(int type) { 12731357012968f9066ea3051d83995e9bac69526c3cJean-Michel Trivi if (mPlaybackType != type) { 12741357012968f9066ea3051d83995e9bac69526c3cJean-Michel Trivi mPlaybackType = type; 12751357012968f9066ea3051d83995e9bac69526c3cJean-Michel Trivi setPlaybackInfoOnRcc(RemoteControlClient.PLAYBACKINFO_PLAYBACK_TYPE, type); 12761357012968f9066ea3051d83995e9bac69526c3cJean-Michel Trivi } 12771357012968f9066ea3051d83995e9bac69526c3cJean-Michel Trivi } 12781357012968f9066ea3051d83995e9bac69526c3cJean-Michel Trivi 12791357012968f9066ea3051d83995e9bac69526c3cJean-Michel Trivi /** 12801357012968f9066ea3051d83995e9bac69526c3cJean-Michel Trivi * Defines whether volume for the playback associated with this route is fixed 12811357012968f9066ea3051d83995e9bac69526c3cJean-Michel Trivi * ({@link RouteInfo#PLAYBACK_VOLUME_FIXED}) or can modified 12821357012968f9066ea3051d83995e9bac69526c3cJean-Michel Trivi * ({@link RouteInfo#PLAYBACK_VOLUME_VARIABLE}). 12831357012968f9066ea3051d83995e9bac69526c3cJean-Michel Trivi * @param volumeHandling 12841357012968f9066ea3051d83995e9bac69526c3cJean-Michel Trivi */ 12851357012968f9066ea3051d83995e9bac69526c3cJean-Michel Trivi public void setVolumeHandling(int volumeHandling) { 12861357012968f9066ea3051d83995e9bac69526c3cJean-Michel Trivi if (mVolumeHandling != volumeHandling) { 12871357012968f9066ea3051d83995e9bac69526c3cJean-Michel Trivi mVolumeHandling = volumeHandling; 12881357012968f9066ea3051d83995e9bac69526c3cJean-Michel Trivi setPlaybackInfoOnRcc( 12891357012968f9066ea3051d83995e9bac69526c3cJean-Michel Trivi RemoteControlClient.PLAYBACKINFO_VOLUME_HANDLING, volumeHandling); 12901357012968f9066ea3051d83995e9bac69526c3cJean-Michel Trivi } 12911357012968f9066ea3051d83995e9bac69526c3cJean-Michel Trivi } 12921357012968f9066ea3051d83995e9bac69526c3cJean-Michel Trivi 12931357012968f9066ea3051d83995e9bac69526c3cJean-Michel Trivi /** 12941357012968f9066ea3051d83995e9bac69526c3cJean-Michel Trivi * Defines at what volume the playback associated with this route is performed (for user 12951357012968f9066ea3051d83995e9bac69526c3cJean-Michel Trivi * feedback purposes). This information is only used when the playback is not local. 12961357012968f9066ea3051d83995e9bac69526c3cJean-Michel Trivi * @param volume 12971357012968f9066ea3051d83995e9bac69526c3cJean-Michel Trivi */ 12981357012968f9066ea3051d83995e9bac69526c3cJean-Michel Trivi public void setVolume(int volume) { 12998e37a85bf3dc39519942698dc90a3951306b934bAdam Powell volume = Math.max(0, Math.min(volume, getVolumeMax())); 13001357012968f9066ea3051d83995e9bac69526c3cJean-Michel Trivi if (mVolume != volume) { 13011357012968f9066ea3051d83995e9bac69526c3cJean-Michel Trivi mVolume = volume; 13021357012968f9066ea3051d83995e9bac69526c3cJean-Michel Trivi setPlaybackInfoOnRcc(RemoteControlClient.PLAYBACKINFO_VOLUME, volume); 13038e37a85bf3dc39519942698dc90a3951306b934bAdam Powell dispatchRouteVolumeChanged(this); 1304f8ac14a7f5a59b4ec8e89283a2da40b626e42065Adam Powell if (mGroup != null) { 1305f8ac14a7f5a59b4ec8e89283a2da40b626e42065Adam Powell mGroup.memberVolumeChanged(this); 1306f8ac14a7f5a59b4ec8e89283a2da40b626e42065Adam Powell } 13078e37a85bf3dc39519942698dc90a3951306b934bAdam Powell } 13088e37a85bf3dc39519942698dc90a3951306b934bAdam Powell } 13098e37a85bf3dc39519942698dc90a3951306b934bAdam Powell 13108e37a85bf3dc39519942698dc90a3951306b934bAdam Powell @Override 13118e37a85bf3dc39519942698dc90a3951306b934bAdam Powell public void requestSetVolume(int volume) { 13128e37a85bf3dc39519942698dc90a3951306b934bAdam Powell if (mVolumeHandling == PLAYBACK_VOLUME_VARIABLE) { 13138e37a85bf3dc39519942698dc90a3951306b934bAdam Powell if (mVcb == null) { 13148e37a85bf3dc39519942698dc90a3951306b934bAdam Powell Log.e(TAG, "Cannot requestSetVolume on user route - no volume callback set"); 13158e37a85bf3dc39519942698dc90a3951306b934bAdam Powell return; 13168e37a85bf3dc39519942698dc90a3951306b934bAdam Powell } 13178e37a85bf3dc39519942698dc90a3951306b934bAdam Powell mVcb.vcb.onVolumeSetRequest(this, volume); 13188e37a85bf3dc39519942698dc90a3951306b934bAdam Powell } 13198e37a85bf3dc39519942698dc90a3951306b934bAdam Powell } 13208e37a85bf3dc39519942698dc90a3951306b934bAdam Powell 13218e37a85bf3dc39519942698dc90a3951306b934bAdam Powell @Override 13228e37a85bf3dc39519942698dc90a3951306b934bAdam Powell public void requestUpdateVolume(int direction) { 13238e37a85bf3dc39519942698dc90a3951306b934bAdam Powell if (mVolumeHandling == PLAYBACK_VOLUME_VARIABLE) { 13248e37a85bf3dc39519942698dc90a3951306b934bAdam Powell if (mVcb == null) { 13258e37a85bf3dc39519942698dc90a3951306b934bAdam Powell Log.e(TAG, "Cannot requestChangeVolume on user route - no volumec callback set"); 13268e37a85bf3dc39519942698dc90a3951306b934bAdam Powell return; 13278e37a85bf3dc39519942698dc90a3951306b934bAdam Powell } 13288e37a85bf3dc39519942698dc90a3951306b934bAdam Powell mVcb.vcb.onVolumeUpdateRequest(this, direction); 13291357012968f9066ea3051d83995e9bac69526c3cJean-Michel Trivi } 13301357012968f9066ea3051d83995e9bac69526c3cJean-Michel Trivi } 13311357012968f9066ea3051d83995e9bac69526c3cJean-Michel Trivi 13321357012968f9066ea3051d83995e9bac69526c3cJean-Michel Trivi /** 13331357012968f9066ea3051d83995e9bac69526c3cJean-Michel Trivi * Defines the maximum volume at which the playback associated with this route is performed 13341357012968f9066ea3051d83995e9bac69526c3cJean-Michel Trivi * (for user feedback purposes). This information is only used when the playback is not 13351357012968f9066ea3051d83995e9bac69526c3cJean-Michel Trivi * local. 13361357012968f9066ea3051d83995e9bac69526c3cJean-Michel Trivi * @param volumeMax 13371357012968f9066ea3051d83995e9bac69526c3cJean-Michel Trivi */ 13381357012968f9066ea3051d83995e9bac69526c3cJean-Michel Trivi public void setVolumeMax(int volumeMax) { 13391357012968f9066ea3051d83995e9bac69526c3cJean-Michel Trivi if (mVolumeMax != volumeMax) { 13401357012968f9066ea3051d83995e9bac69526c3cJean-Michel Trivi mVolumeMax = volumeMax; 13411357012968f9066ea3051d83995e9bac69526c3cJean-Michel Trivi setPlaybackInfoOnRcc(RemoteControlClient.PLAYBACKINFO_VOLUME_MAX, volumeMax); 13421357012968f9066ea3051d83995e9bac69526c3cJean-Michel Trivi } 13431357012968f9066ea3051d83995e9bac69526c3cJean-Michel Trivi } 13441357012968f9066ea3051d83995e9bac69526c3cJean-Michel Trivi 13451357012968f9066ea3051d83995e9bac69526c3cJean-Michel Trivi /** 13461357012968f9066ea3051d83995e9bac69526c3cJean-Michel Trivi * Defines over what stream type the media is presented. 13471357012968f9066ea3051d83995e9bac69526c3cJean-Michel Trivi * @param stream 13481357012968f9066ea3051d83995e9bac69526c3cJean-Michel Trivi */ 13491357012968f9066ea3051d83995e9bac69526c3cJean-Michel Trivi public void setPlaybackStream(int stream) { 13501357012968f9066ea3051d83995e9bac69526c3cJean-Michel Trivi if (mPlaybackStream != stream) { 13511357012968f9066ea3051d83995e9bac69526c3cJean-Michel Trivi mPlaybackStream = stream; 13521357012968f9066ea3051d83995e9bac69526c3cJean-Michel Trivi setPlaybackInfoOnRcc(RemoteControlClient.PLAYBACKINFO_USES_STREAM, stream); 13531357012968f9066ea3051d83995e9bac69526c3cJean-Michel Trivi } 13541357012968f9066ea3051d83995e9bac69526c3cJean-Michel Trivi } 13551357012968f9066ea3051d83995e9bac69526c3cJean-Michel Trivi 13561357012968f9066ea3051d83995e9bac69526c3cJean-Michel Trivi private void updatePlaybackInfoOnRcc() { 13571357012968f9066ea3051d83995e9bac69526c3cJean-Michel Trivi if ((mRcc != null) && (mRcc.getRcseId() != RemoteControlClient.RCSE_ID_UNREGISTERED)) { 13581357012968f9066ea3051d83995e9bac69526c3cJean-Michel Trivi mRcc.setPlaybackInformation( 13591357012968f9066ea3051d83995e9bac69526c3cJean-Michel Trivi RemoteControlClient.PLAYBACKINFO_VOLUME_MAX, mVolumeMax); 13601357012968f9066ea3051d83995e9bac69526c3cJean-Michel Trivi mRcc.setPlaybackInformation( 13611357012968f9066ea3051d83995e9bac69526c3cJean-Michel Trivi RemoteControlClient.PLAYBACKINFO_VOLUME, mVolume); 13621357012968f9066ea3051d83995e9bac69526c3cJean-Michel Trivi mRcc.setPlaybackInformation( 13631357012968f9066ea3051d83995e9bac69526c3cJean-Michel Trivi RemoteControlClient.PLAYBACKINFO_VOLUME_HANDLING, mVolumeHandling); 13641357012968f9066ea3051d83995e9bac69526c3cJean-Michel Trivi mRcc.setPlaybackInformation( 13651357012968f9066ea3051d83995e9bac69526c3cJean-Michel Trivi RemoteControlClient.PLAYBACKINFO_USES_STREAM, mPlaybackStream); 13661357012968f9066ea3051d83995e9bac69526c3cJean-Michel Trivi mRcc.setPlaybackInformation( 13671357012968f9066ea3051d83995e9bac69526c3cJean-Michel Trivi RemoteControlClient.PLAYBACKINFO_PLAYBACK_TYPE, mPlaybackType); 13681357012968f9066ea3051d83995e9bac69526c3cJean-Michel Trivi // let AudioService know whom to call when remote volume needs to be updated 13691357012968f9066ea3051d83995e9bac69526c3cJean-Michel Trivi try { 13701357012968f9066ea3051d83995e9bac69526c3cJean-Michel Trivi sStatic.mAudioService.registerRemoteVolumeObserverForRcc( 13711357012968f9066ea3051d83995e9bac69526c3cJean-Michel Trivi mRcc.getRcseId() /* rccId */, mRemoteVolObserver /* rvo */); 13721357012968f9066ea3051d83995e9bac69526c3cJean-Michel Trivi } catch (RemoteException e) { 13731357012968f9066ea3051d83995e9bac69526c3cJean-Michel Trivi Log.e(TAG, "Error registering remote volume observer", e); 13741357012968f9066ea3051d83995e9bac69526c3cJean-Michel Trivi } 13751357012968f9066ea3051d83995e9bac69526c3cJean-Michel Trivi } 13761357012968f9066ea3051d83995e9bac69526c3cJean-Michel Trivi } 13771357012968f9066ea3051d83995e9bac69526c3cJean-Michel Trivi 13781357012968f9066ea3051d83995e9bac69526c3cJean-Michel Trivi private void setPlaybackInfoOnRcc(int what, int value) { 13791357012968f9066ea3051d83995e9bac69526c3cJean-Michel Trivi if (mRcc != null) { 13801357012968f9066ea3051d83995e9bac69526c3cJean-Michel Trivi mRcc.setPlaybackInformation(what, value); 13811357012968f9066ea3051d83995e9bac69526c3cJean-Michel Trivi } 13821357012968f9066ea3051d83995e9bac69526c3cJean-Michel Trivi } 13839a1de308cea2d160778fd977825f10a07b49d738Adam Powell } 13849a1de308cea2d160778fd977825f10a07b49d738Adam Powell 13859a1de308cea2d160778fd977825f10a07b49d738Adam Powell /** 13869a1de308cea2d160778fd977825f10a07b49d738Adam Powell * Information about a route that consists of multiple other routes in a group. 13879a1de308cea2d160778fd977825f10a07b49d738Adam Powell */ 1388b58b8f832d06b0ffa8886eba5a4916578a3b8743Dianne Hackborn public static class RouteGroup extends RouteInfo { 13899a1de308cea2d160778fd977825f10a07b49d738Adam Powell final ArrayList<RouteInfo> mRoutes = new ArrayList<RouteInfo>(); 13909a1de308cea2d160778fd977825f10a07b49d738Adam Powell private boolean mUpdateName; 13919a1de308cea2d160778fd977825f10a07b49d738Adam Powell 13929a1de308cea2d160778fd977825f10a07b49d738Adam Powell RouteGroup(RouteCategory category) { 13939a1de308cea2d160778fd977825f10a07b49d738Adam Powell super(category); 13949a1de308cea2d160778fd977825f10a07b49d738Adam Powell mGroup = this; 13958e37a85bf3dc39519942698dc90a3951306b934bAdam Powell mVolumeHandling = PLAYBACK_VOLUME_FIXED; 13969a1de308cea2d160778fd977825f10a07b49d738Adam Powell } 13979a1de308cea2d160778fd977825f10a07b49d738Adam Powell 13980d03c042f90bf62d5bad7c64e77028a5f9f8fae0Adam Powell CharSequence getName(Resources res) { 13999a1de308cea2d160778fd977825f10a07b49d738Adam Powell if (mUpdateName) updateName(); 14000d03c042f90bf62d5bad7c64e77028a5f9f8fae0Adam Powell return super.getName(res); 14019a1de308cea2d160778fd977825f10a07b49d738Adam Powell } 14029a1de308cea2d160778fd977825f10a07b49d738Adam Powell 14039a1de308cea2d160778fd977825f10a07b49d738Adam Powell /** 14049a1de308cea2d160778fd977825f10a07b49d738Adam Powell * Add a route to this group. The route must not currently belong to another group. 14059a1de308cea2d160778fd977825f10a07b49d738Adam Powell * 14069a1de308cea2d160778fd977825f10a07b49d738Adam Powell * @param route route to add to this group 14079a1de308cea2d160778fd977825f10a07b49d738Adam Powell */ 14089a1de308cea2d160778fd977825f10a07b49d738Adam Powell public void addRoute(RouteInfo route) { 14099a1de308cea2d160778fd977825f10a07b49d738Adam Powell if (route.getGroup() != null) { 14109a1de308cea2d160778fd977825f10a07b49d738Adam Powell throw new IllegalStateException("Route " + route + " is already part of a group."); 14119a1de308cea2d160778fd977825f10a07b49d738Adam Powell } 14129a1de308cea2d160778fd977825f10a07b49d738Adam Powell if (route.getCategory() != mCategory) { 14139a1de308cea2d160778fd977825f10a07b49d738Adam Powell throw new IllegalArgumentException( 14149a1de308cea2d160778fd977825f10a07b49d738Adam Powell "Route cannot be added to a group with a different category. " + 14159a1de308cea2d160778fd977825f10a07b49d738Adam Powell "(Route category=" + route.getCategory() + 14169a1de308cea2d160778fd977825f10a07b49d738Adam Powell " group category=" + mCategory + ")"); 14179a1de308cea2d160778fd977825f10a07b49d738Adam Powell } 1418d0d2cda9d414da73773285d7fee9e13aef3495e9Adam Powell final int at = mRoutes.size(); 14199a1de308cea2d160778fd977825f10a07b49d738Adam Powell mRoutes.add(route); 1420d6d0bddee363e0c7fe61f63bd9d9864a71d887d6Adam Powell route.mGroup = this; 14219a1de308cea2d160778fd977825f10a07b49d738Adam Powell mUpdateName = true; 1422f8ac14a7f5a59b4ec8e89283a2da40b626e42065Adam Powell updateVolume(); 14239a1de308cea2d160778fd977825f10a07b49d738Adam Powell routeUpdated(); 1424f3b653a21cdffe04c94c275e69ecb56e00766e82Adam Powell dispatchRouteGrouped(route, this, at); 14259a1de308cea2d160778fd977825f10a07b49d738Adam Powell } 14269a1de308cea2d160778fd977825f10a07b49d738Adam Powell 14279a1de308cea2d160778fd977825f10a07b49d738Adam Powell /** 14289a1de308cea2d160778fd977825f10a07b49d738Adam Powell * Add a route to this group before the specified index. 14299a1de308cea2d160778fd977825f10a07b49d738Adam Powell * 14309a1de308cea2d160778fd977825f10a07b49d738Adam Powell * @param route route to add 14319a1de308cea2d160778fd977825f10a07b49d738Adam Powell * @param insertAt insert the new route before this index 14329a1de308cea2d160778fd977825f10a07b49d738Adam Powell */ 14339a1de308cea2d160778fd977825f10a07b49d738Adam Powell public void addRoute(RouteInfo route, int insertAt) { 14349a1de308cea2d160778fd977825f10a07b49d738Adam Powell if (route.getGroup() != null) { 14359a1de308cea2d160778fd977825f10a07b49d738Adam Powell throw new IllegalStateException("Route " + route + " is already part of a group."); 14369a1de308cea2d160778fd977825f10a07b49d738Adam Powell } 14379a1de308cea2d160778fd977825f10a07b49d738Adam Powell if (route.getCategory() != mCategory) { 14389a1de308cea2d160778fd977825f10a07b49d738Adam Powell throw new IllegalArgumentException( 14399a1de308cea2d160778fd977825f10a07b49d738Adam Powell "Route cannot be added to a group with a different category. " + 14409a1de308cea2d160778fd977825f10a07b49d738Adam Powell "(Route category=" + route.getCategory() + 14419a1de308cea2d160778fd977825f10a07b49d738Adam Powell " group category=" + mCategory + ")"); 14429a1de308cea2d160778fd977825f10a07b49d738Adam Powell } 14439a1de308cea2d160778fd977825f10a07b49d738Adam Powell mRoutes.add(insertAt, route); 1444d6d0bddee363e0c7fe61f63bd9d9864a71d887d6Adam Powell route.mGroup = this; 14459a1de308cea2d160778fd977825f10a07b49d738Adam Powell mUpdateName = true; 1446f8ac14a7f5a59b4ec8e89283a2da40b626e42065Adam Powell updateVolume(); 14479a1de308cea2d160778fd977825f10a07b49d738Adam Powell routeUpdated(); 1448f3b653a21cdffe04c94c275e69ecb56e00766e82Adam Powell dispatchRouteGrouped(route, this, insertAt); 14499a1de308cea2d160778fd977825f10a07b49d738Adam Powell } 14509a1de308cea2d160778fd977825f10a07b49d738Adam Powell 14519a1de308cea2d160778fd977825f10a07b49d738Adam Powell /** 14529a1de308cea2d160778fd977825f10a07b49d738Adam Powell * Remove a route from this group. 14539a1de308cea2d160778fd977825f10a07b49d738Adam Powell * 14549a1de308cea2d160778fd977825f10a07b49d738Adam Powell * @param route route to remove 14559a1de308cea2d160778fd977825f10a07b49d738Adam Powell */ 14569a1de308cea2d160778fd977825f10a07b49d738Adam Powell public void removeRoute(RouteInfo route) { 14579a1de308cea2d160778fd977825f10a07b49d738Adam Powell if (route.getGroup() != this) { 14589a1de308cea2d160778fd977825f10a07b49d738Adam Powell throw new IllegalArgumentException("Route " + route + 14599a1de308cea2d160778fd977825f10a07b49d738Adam Powell " is not a member of this group."); 14609a1de308cea2d160778fd977825f10a07b49d738Adam Powell } 14619a1de308cea2d160778fd977825f10a07b49d738Adam Powell mRoutes.remove(route); 1462d6d0bddee363e0c7fe61f63bd9d9864a71d887d6Adam Powell route.mGroup = null; 14639a1de308cea2d160778fd977825f10a07b49d738Adam Powell mUpdateName = true; 1464f8ac14a7f5a59b4ec8e89283a2da40b626e42065Adam Powell updateVolume(); 1465d0d2cda9d414da73773285d7fee9e13aef3495e9Adam Powell dispatchRouteUngrouped(route, this); 14669a1de308cea2d160778fd977825f10a07b49d738Adam Powell routeUpdated(); 14679a1de308cea2d160778fd977825f10a07b49d738Adam Powell } 14689a1de308cea2d160778fd977825f10a07b49d738Adam Powell 14699a1de308cea2d160778fd977825f10a07b49d738Adam Powell /** 14709a1de308cea2d160778fd977825f10a07b49d738Adam Powell * Remove the route at the specified index from this group. 14719a1de308cea2d160778fd977825f10a07b49d738Adam Powell * 14729a1de308cea2d160778fd977825f10a07b49d738Adam Powell * @param index index of the route to remove 14739a1de308cea2d160778fd977825f10a07b49d738Adam Powell */ 14749a1de308cea2d160778fd977825f10a07b49d738Adam Powell public void removeRoute(int index) { 1475d0d2cda9d414da73773285d7fee9e13aef3495e9Adam Powell RouteInfo route = mRoutes.remove(index); 1476d6d0bddee363e0c7fe61f63bd9d9864a71d887d6Adam Powell route.mGroup = null; 14779a1de308cea2d160778fd977825f10a07b49d738Adam Powell mUpdateName = true; 1478f8ac14a7f5a59b4ec8e89283a2da40b626e42065Adam Powell updateVolume(); 1479d0d2cda9d414da73773285d7fee9e13aef3495e9Adam Powell dispatchRouteUngrouped(route, this); 14809a1de308cea2d160778fd977825f10a07b49d738Adam Powell routeUpdated(); 14819a1de308cea2d160778fd977825f10a07b49d738Adam Powell } 14829a1de308cea2d160778fd977825f10a07b49d738Adam Powell 1483d0d2cda9d414da73773285d7fee9e13aef3495e9Adam Powell /** 1484d0d2cda9d414da73773285d7fee9e13aef3495e9Adam Powell * @return The number of routes in this group 1485d0d2cda9d414da73773285d7fee9e13aef3495e9Adam Powell */ 1486d0d2cda9d414da73773285d7fee9e13aef3495e9Adam Powell public int getRouteCount() { 1487d0d2cda9d414da73773285d7fee9e13aef3495e9Adam Powell return mRoutes.size(); 1488d0d2cda9d414da73773285d7fee9e13aef3495e9Adam Powell } 1489d0d2cda9d414da73773285d7fee9e13aef3495e9Adam Powell 1490d0d2cda9d414da73773285d7fee9e13aef3495e9Adam Powell /** 1491d0d2cda9d414da73773285d7fee9e13aef3495e9Adam Powell * Return the route in this group at the specified index 1492d0d2cda9d414da73773285d7fee9e13aef3495e9Adam Powell * 1493d0d2cda9d414da73773285d7fee9e13aef3495e9Adam Powell * @param index Index to fetch 1494d0d2cda9d414da73773285d7fee9e13aef3495e9Adam Powell * @return The route at index 1495d0d2cda9d414da73773285d7fee9e13aef3495e9Adam Powell */ 1496d0d2cda9d414da73773285d7fee9e13aef3495e9Adam Powell public RouteInfo getRouteAt(int index) { 1497d0d2cda9d414da73773285d7fee9e13aef3495e9Adam Powell return mRoutes.get(index); 1498d0d2cda9d414da73773285d7fee9e13aef3495e9Adam Powell } 1499d0d2cda9d414da73773285d7fee9e13aef3495e9Adam Powell 1500ae20ae1a8aaa013813c356ae1d9541ca7ff020aeAdam Powell /** 1501ae20ae1a8aaa013813c356ae1d9541ca7ff020aeAdam Powell * Set an icon that will be used to represent this group. 1502ae20ae1a8aaa013813c356ae1d9541ca7ff020aeAdam Powell * The system may use this icon in picker UIs or similar. 1503ae20ae1a8aaa013813c356ae1d9541ca7ff020aeAdam Powell * 1504ae20ae1a8aaa013813c356ae1d9541ca7ff020aeAdam Powell * @param icon icon drawable to use to represent this group 1505ae20ae1a8aaa013813c356ae1d9541ca7ff020aeAdam Powell */ 1506ae20ae1a8aaa013813c356ae1d9541ca7ff020aeAdam Powell public void setIconDrawable(Drawable icon) { 1507ae20ae1a8aaa013813c356ae1d9541ca7ff020aeAdam Powell mIcon = icon; 1508ae20ae1a8aaa013813c356ae1d9541ca7ff020aeAdam Powell } 1509ae20ae1a8aaa013813c356ae1d9541ca7ff020aeAdam Powell 1510ae20ae1a8aaa013813c356ae1d9541ca7ff020aeAdam Powell /** 1511ae20ae1a8aaa013813c356ae1d9541ca7ff020aeAdam Powell * Set an icon that will be used to represent this group. 1512ae20ae1a8aaa013813c356ae1d9541ca7ff020aeAdam Powell * The system may use this icon in picker UIs or similar. 1513ae20ae1a8aaa013813c356ae1d9541ca7ff020aeAdam Powell * 151471c69897ad0a55d590698bfa399bfe99c763b9dbAdam Powell * @param resId Resource ID of an icon drawable to use to represent this group 1515ae20ae1a8aaa013813c356ae1d9541ca7ff020aeAdam Powell */ 1516ae20ae1a8aaa013813c356ae1d9541ca7ff020aeAdam Powell public void setIconResource(int resId) { 1517ae20ae1a8aaa013813c356ae1d9541ca7ff020aeAdam Powell setIconDrawable(sStatic.mResources.getDrawable(resId)); 1518ae20ae1a8aaa013813c356ae1d9541ca7ff020aeAdam Powell } 1519ae20ae1a8aaa013813c356ae1d9541ca7ff020aeAdam Powell 15208e37a85bf3dc39519942698dc90a3951306b934bAdam Powell @Override 15218e37a85bf3dc39519942698dc90a3951306b934bAdam Powell public void requestSetVolume(int volume) { 15228e37a85bf3dc39519942698dc90a3951306b934bAdam Powell final int maxVol = getVolumeMax(); 15238e37a85bf3dc39519942698dc90a3951306b934bAdam Powell if (maxVol == 0) { 15248e37a85bf3dc39519942698dc90a3951306b934bAdam Powell return; 15258e37a85bf3dc39519942698dc90a3951306b934bAdam Powell } 15268e37a85bf3dc39519942698dc90a3951306b934bAdam Powell 15278e37a85bf3dc39519942698dc90a3951306b934bAdam Powell final float scaledVolume = (float) volume / maxVol; 15288e37a85bf3dc39519942698dc90a3951306b934bAdam Powell final int routeCount = getRouteCount(); 15298e37a85bf3dc39519942698dc90a3951306b934bAdam Powell for (int i = 0; i < routeCount; i++) { 15308e37a85bf3dc39519942698dc90a3951306b934bAdam Powell final RouteInfo route = getRouteAt(i); 15318e37a85bf3dc39519942698dc90a3951306b934bAdam Powell final int routeVol = (int) (scaledVolume * route.getVolumeMax()); 15328e37a85bf3dc39519942698dc90a3951306b934bAdam Powell route.requestSetVolume(routeVol); 15338e37a85bf3dc39519942698dc90a3951306b934bAdam Powell } 15348e37a85bf3dc39519942698dc90a3951306b934bAdam Powell if (volume != mVolume) { 15358e37a85bf3dc39519942698dc90a3951306b934bAdam Powell mVolume = volume; 15368e37a85bf3dc39519942698dc90a3951306b934bAdam Powell dispatchRouteVolumeChanged(this); 15378e37a85bf3dc39519942698dc90a3951306b934bAdam Powell } 15388e37a85bf3dc39519942698dc90a3951306b934bAdam Powell } 15398e37a85bf3dc39519942698dc90a3951306b934bAdam Powell 15408e37a85bf3dc39519942698dc90a3951306b934bAdam Powell @Override 15418e37a85bf3dc39519942698dc90a3951306b934bAdam Powell public void requestUpdateVolume(int direction) { 15428e37a85bf3dc39519942698dc90a3951306b934bAdam Powell final int maxVol = getVolumeMax(); 15438e37a85bf3dc39519942698dc90a3951306b934bAdam Powell if (maxVol == 0) { 15448e37a85bf3dc39519942698dc90a3951306b934bAdam Powell return; 15458e37a85bf3dc39519942698dc90a3951306b934bAdam Powell } 15468e37a85bf3dc39519942698dc90a3951306b934bAdam Powell 15478e37a85bf3dc39519942698dc90a3951306b934bAdam Powell final int routeCount = getRouteCount(); 1548f8ac14a7f5a59b4ec8e89283a2da40b626e42065Adam Powell int volume = 0; 15498e37a85bf3dc39519942698dc90a3951306b934bAdam Powell for (int i = 0; i < routeCount; i++) { 15508e37a85bf3dc39519942698dc90a3951306b934bAdam Powell final RouteInfo route = getRouteAt(i); 15518e37a85bf3dc39519942698dc90a3951306b934bAdam Powell route.requestUpdateVolume(direction); 1552f8ac14a7f5a59b4ec8e89283a2da40b626e42065Adam Powell final int routeVol = route.getVolume(); 1553f8ac14a7f5a59b4ec8e89283a2da40b626e42065Adam Powell if (routeVol > volume) { 1554f8ac14a7f5a59b4ec8e89283a2da40b626e42065Adam Powell volume = routeVol; 1555f8ac14a7f5a59b4ec8e89283a2da40b626e42065Adam Powell } 15568e37a85bf3dc39519942698dc90a3951306b934bAdam Powell } 15578e37a85bf3dc39519942698dc90a3951306b934bAdam Powell if (volume != mVolume) { 15588e37a85bf3dc39519942698dc90a3951306b934bAdam Powell mVolume = volume; 15598e37a85bf3dc39519942698dc90a3951306b934bAdam Powell dispatchRouteVolumeChanged(this); 15608e37a85bf3dc39519942698dc90a3951306b934bAdam Powell } 15618e37a85bf3dc39519942698dc90a3951306b934bAdam Powell } 15628e37a85bf3dc39519942698dc90a3951306b934bAdam Powell 15639a1de308cea2d160778fd977825f10a07b49d738Adam Powell void memberNameChanged(RouteInfo info, CharSequence name) { 15649a1de308cea2d160778fd977825f10a07b49d738Adam Powell mUpdateName = true; 15659a1de308cea2d160778fd977825f10a07b49d738Adam Powell routeUpdated(); 15669a1de308cea2d160778fd977825f10a07b49d738Adam Powell } 15679a1de308cea2d160778fd977825f10a07b49d738Adam Powell 15689a1de308cea2d160778fd977825f10a07b49d738Adam Powell void memberStatusChanged(RouteInfo info, CharSequence status) { 15699a1de308cea2d160778fd977825f10a07b49d738Adam Powell setStatusInt(status); 15709a1de308cea2d160778fd977825f10a07b49d738Adam Powell } 15719a1de308cea2d160778fd977825f10a07b49d738Adam Powell 1572f8ac14a7f5a59b4ec8e89283a2da40b626e42065Adam Powell void memberVolumeChanged(RouteInfo info) { 1573f8ac14a7f5a59b4ec8e89283a2da40b626e42065Adam Powell updateVolume(); 1574f8ac14a7f5a59b4ec8e89283a2da40b626e42065Adam Powell } 1575f8ac14a7f5a59b4ec8e89283a2da40b626e42065Adam Powell 1576f8ac14a7f5a59b4ec8e89283a2da40b626e42065Adam Powell void updateVolume() { 1577f8ac14a7f5a59b4ec8e89283a2da40b626e42065Adam Powell // A group always represents the highest component volume value. 1578f8ac14a7f5a59b4ec8e89283a2da40b626e42065Adam Powell final int routeCount = getRouteCount(); 1579f8ac14a7f5a59b4ec8e89283a2da40b626e42065Adam Powell int volume = 0; 1580f8ac14a7f5a59b4ec8e89283a2da40b626e42065Adam Powell for (int i = 0; i < routeCount; i++) { 1581f8ac14a7f5a59b4ec8e89283a2da40b626e42065Adam Powell final int routeVol = getRouteAt(i).getVolume(); 1582f8ac14a7f5a59b4ec8e89283a2da40b626e42065Adam Powell if (routeVol > volume) { 1583f8ac14a7f5a59b4ec8e89283a2da40b626e42065Adam Powell volume = routeVol; 1584f8ac14a7f5a59b4ec8e89283a2da40b626e42065Adam Powell } 1585f8ac14a7f5a59b4ec8e89283a2da40b626e42065Adam Powell } 1586f8ac14a7f5a59b4ec8e89283a2da40b626e42065Adam Powell if (volume != mVolume) { 1587f8ac14a7f5a59b4ec8e89283a2da40b626e42065Adam Powell mVolume = volume; 1588f8ac14a7f5a59b4ec8e89283a2da40b626e42065Adam Powell dispatchRouteVolumeChanged(this); 1589f8ac14a7f5a59b4ec8e89283a2da40b626e42065Adam Powell } 1590f8ac14a7f5a59b4ec8e89283a2da40b626e42065Adam Powell } 1591f8ac14a7f5a59b4ec8e89283a2da40b626e42065Adam Powell 1592d6d0bddee363e0c7fe61f63bd9d9864a71d887d6Adam Powell @Override 1593d6d0bddee363e0c7fe61f63bd9d9864a71d887d6Adam Powell void routeUpdated() { 1594d6d0bddee363e0c7fe61f63bd9d9864a71d887d6Adam Powell int types = 0; 1595d6d0bddee363e0c7fe61f63bd9d9864a71d887d6Adam Powell final int count = mRoutes.size(); 1596b5e2af5919351486a385effe77409d2a91ae9c19Adam Powell if (count == 0) { 1597b5e2af5919351486a385effe77409d2a91ae9c19Adam Powell // Don't keep empty groups in the router. 1598b5e2af5919351486a385effe77409d2a91ae9c19Adam Powell MediaRouter.removeRoute(this); 1599b5e2af5919351486a385effe77409d2a91ae9c19Adam Powell return; 1600b5e2af5919351486a385effe77409d2a91ae9c19Adam Powell } 1601b5e2af5919351486a385effe77409d2a91ae9c19Adam Powell 16028e37a85bf3dc39519942698dc90a3951306b934bAdam Powell int maxVolume = 0; 16038e37a85bf3dc39519942698dc90a3951306b934bAdam Powell boolean isLocal = true; 16048e37a85bf3dc39519942698dc90a3951306b934bAdam Powell boolean isFixedVolume = true; 1605d6d0bddee363e0c7fe61f63bd9d9864a71d887d6Adam Powell for (int i = 0; i < count; i++) { 16068e37a85bf3dc39519942698dc90a3951306b934bAdam Powell final RouteInfo route = mRoutes.get(i); 16078e37a85bf3dc39519942698dc90a3951306b934bAdam Powell types |= route.mSupportedTypes; 16088e37a85bf3dc39519942698dc90a3951306b934bAdam Powell final int routeMaxVolume = route.getVolumeMax(); 16098e37a85bf3dc39519942698dc90a3951306b934bAdam Powell if (routeMaxVolume > maxVolume) { 16108e37a85bf3dc39519942698dc90a3951306b934bAdam Powell maxVolume = routeMaxVolume; 16118e37a85bf3dc39519942698dc90a3951306b934bAdam Powell } 16128e37a85bf3dc39519942698dc90a3951306b934bAdam Powell isLocal &= route.getPlaybackType() == PLAYBACK_TYPE_LOCAL; 16138e37a85bf3dc39519942698dc90a3951306b934bAdam Powell isFixedVolume &= route.getVolumeHandling() == PLAYBACK_VOLUME_FIXED; 1614d6d0bddee363e0c7fe61f63bd9d9864a71d887d6Adam Powell } 16158e37a85bf3dc39519942698dc90a3951306b934bAdam Powell mPlaybackType = isLocal ? PLAYBACK_TYPE_LOCAL : PLAYBACK_TYPE_REMOTE; 16168e37a85bf3dc39519942698dc90a3951306b934bAdam Powell mVolumeHandling = isFixedVolume ? PLAYBACK_VOLUME_FIXED : PLAYBACK_VOLUME_VARIABLE; 1617d6d0bddee363e0c7fe61f63bd9d9864a71d887d6Adam Powell mSupportedTypes = types; 16188e37a85bf3dc39519942698dc90a3951306b934bAdam Powell mVolumeMax = maxVolume; 1619d6d0bddee363e0c7fe61f63bd9d9864a71d887d6Adam Powell mIcon = count == 1 ? mRoutes.get(0).getIconDrawable() : null; 1620d6d0bddee363e0c7fe61f63bd9d9864a71d887d6Adam Powell super.routeUpdated(); 1621d6d0bddee363e0c7fe61f63bd9d9864a71d887d6Adam Powell } 1622d6d0bddee363e0c7fe61f63bd9d9864a71d887d6Adam Powell 16239a1de308cea2d160778fd977825f10a07b49d738Adam Powell void updateName() { 16249a1de308cea2d160778fd977825f10a07b49d738Adam Powell final StringBuilder sb = new StringBuilder(); 16259a1de308cea2d160778fd977825f10a07b49d738Adam Powell final int count = mRoutes.size(); 16269a1de308cea2d160778fd977825f10a07b49d738Adam Powell for (int i = 0; i < count; i++) { 16279a1de308cea2d160778fd977825f10a07b49d738Adam Powell final RouteInfo info = mRoutes.get(i); 1628b5e2af5919351486a385effe77409d2a91ae9c19Adam Powell // TODO: There's probably a much more correct way to localize this. 16299a1de308cea2d160778fd977825f10a07b49d738Adam Powell if (i > 0) sb.append(", "); 16309a1de308cea2d160778fd977825f10a07b49d738Adam Powell sb.append(info.mName); 16319a1de308cea2d160778fd977825f10a07b49d738Adam Powell } 16329a1de308cea2d160778fd977825f10a07b49d738Adam Powell mName = sb.toString(); 16339a1de308cea2d160778fd977825f10a07b49d738Adam Powell mUpdateName = false; 16349a1de308cea2d160778fd977825f10a07b49d738Adam Powell } 1635d6d0bddee363e0c7fe61f63bd9d9864a71d887d6Adam Powell 1636d6d0bddee363e0c7fe61f63bd9d9864a71d887d6Adam Powell @Override 1637d6d0bddee363e0c7fe61f63bd9d9864a71d887d6Adam Powell public String toString() { 1638d6d0bddee363e0c7fe61f63bd9d9864a71d887d6Adam Powell StringBuilder sb = new StringBuilder(super.toString()); 1639d6d0bddee363e0c7fe61f63bd9d9864a71d887d6Adam Powell sb.append('['); 1640d6d0bddee363e0c7fe61f63bd9d9864a71d887d6Adam Powell final int count = mRoutes.size(); 1641d6d0bddee363e0c7fe61f63bd9d9864a71d887d6Adam Powell for (int i = 0; i < count; i++) { 1642d6d0bddee363e0c7fe61f63bd9d9864a71d887d6Adam Powell if (i > 0) sb.append(", "); 1643d6d0bddee363e0c7fe61f63bd9d9864a71d887d6Adam Powell sb.append(mRoutes.get(i)); 1644d6d0bddee363e0c7fe61f63bd9d9864a71d887d6Adam Powell } 1645d6d0bddee363e0c7fe61f63bd9d9864a71d887d6Adam Powell sb.append(']'); 1646d6d0bddee363e0c7fe61f63bd9d9864a71d887d6Adam Powell return sb.toString(); 1647d6d0bddee363e0c7fe61f63bd9d9864a71d887d6Adam Powell } 16489a1de308cea2d160778fd977825f10a07b49d738Adam Powell } 16499a1de308cea2d160778fd977825f10a07b49d738Adam Powell 16509a1de308cea2d160778fd977825f10a07b49d738Adam Powell /** 16519a1de308cea2d160778fd977825f10a07b49d738Adam Powell * Definition of a category of routes. All routes belong to a category. 16529a1de308cea2d160778fd977825f10a07b49d738Adam Powell */ 1653b58b8f832d06b0ffa8886eba5a4916578a3b8743Dianne Hackborn public static class RouteCategory { 16549a1de308cea2d160778fd977825f10a07b49d738Adam Powell CharSequence mName; 16550d03c042f90bf62d5bad7c64e77028a5f9f8fae0Adam Powell int mNameResId; 16569a1de308cea2d160778fd977825f10a07b49d738Adam Powell int mTypes; 16579a1de308cea2d160778fd977825f10a07b49d738Adam Powell final boolean mGroupable; 1658705ab808cf023e0cc38c2ba7cdb9571942cdc04fAdam Powell boolean mIsSystem; 16599a1de308cea2d160778fd977825f10a07b49d738Adam Powell 16609a1de308cea2d160778fd977825f10a07b49d738Adam Powell RouteCategory(CharSequence name, int types, boolean groupable) { 16619a1de308cea2d160778fd977825f10a07b49d738Adam Powell mName = name; 16629a1de308cea2d160778fd977825f10a07b49d738Adam Powell mTypes = types; 16639a1de308cea2d160778fd977825f10a07b49d738Adam Powell mGroupable = groupable; 16649a1de308cea2d160778fd977825f10a07b49d738Adam Powell } 16659a1de308cea2d160778fd977825f10a07b49d738Adam Powell 16660d03c042f90bf62d5bad7c64e77028a5f9f8fae0Adam Powell RouteCategory(int nameResId, int types, boolean groupable) { 16670d03c042f90bf62d5bad7c64e77028a5f9f8fae0Adam Powell mNameResId = nameResId; 16680d03c042f90bf62d5bad7c64e77028a5f9f8fae0Adam Powell mTypes = types; 16690d03c042f90bf62d5bad7c64e77028a5f9f8fae0Adam Powell mGroupable = groupable; 16700d03c042f90bf62d5bad7c64e77028a5f9f8fae0Adam Powell } 16710d03c042f90bf62d5bad7c64e77028a5f9f8fae0Adam Powell 16729a1de308cea2d160778fd977825f10a07b49d738Adam Powell /** 16739a1de308cea2d160778fd977825f10a07b49d738Adam Powell * @return the name of this route category 16749a1de308cea2d160778fd977825f10a07b49d738Adam Powell */ 16759a1de308cea2d160778fd977825f10a07b49d738Adam Powell public CharSequence getName() { 16760d03c042f90bf62d5bad7c64e77028a5f9f8fae0Adam Powell return getName(sStatic.mResources); 16770d03c042f90bf62d5bad7c64e77028a5f9f8fae0Adam Powell } 16780d03c042f90bf62d5bad7c64e77028a5f9f8fae0Adam Powell 16790d03c042f90bf62d5bad7c64e77028a5f9f8fae0Adam Powell /** 16800d03c042f90bf62d5bad7c64e77028a5f9f8fae0Adam Powell * Return the properly localized/configuration dependent name of this RouteCategory. 16810d03c042f90bf62d5bad7c64e77028a5f9f8fae0Adam Powell * 16820d03c042f90bf62d5bad7c64e77028a5f9f8fae0Adam Powell * @param context Context to resolve name resources 16830d03c042f90bf62d5bad7c64e77028a5f9f8fae0Adam Powell * @return the name of this route category 16840d03c042f90bf62d5bad7c64e77028a5f9f8fae0Adam Powell */ 16850d03c042f90bf62d5bad7c64e77028a5f9f8fae0Adam Powell public CharSequence getName(Context context) { 16860d03c042f90bf62d5bad7c64e77028a5f9f8fae0Adam Powell return getName(context.getResources()); 16870d03c042f90bf62d5bad7c64e77028a5f9f8fae0Adam Powell } 16880d03c042f90bf62d5bad7c64e77028a5f9f8fae0Adam Powell 16890d03c042f90bf62d5bad7c64e77028a5f9f8fae0Adam Powell CharSequence getName(Resources res) { 16900d03c042f90bf62d5bad7c64e77028a5f9f8fae0Adam Powell if (mNameResId != 0) { 16910d03c042f90bf62d5bad7c64e77028a5f9f8fae0Adam Powell return res.getText(mNameResId); 16920d03c042f90bf62d5bad7c64e77028a5f9f8fae0Adam Powell } 16939a1de308cea2d160778fd977825f10a07b49d738Adam Powell return mName; 16949a1de308cea2d160778fd977825f10a07b49d738Adam Powell } 16959a1de308cea2d160778fd977825f10a07b49d738Adam Powell 16969a1de308cea2d160778fd977825f10a07b49d738Adam Powell /** 1697d0d2cda9d414da73773285d7fee9e13aef3495e9Adam Powell * Return the current list of routes in this category that have been added 1698d0d2cda9d414da73773285d7fee9e13aef3495e9Adam Powell * to the MediaRouter. 16999a1de308cea2d160778fd977825f10a07b49d738Adam Powell * 1700d0d2cda9d414da73773285d7fee9e13aef3495e9Adam Powell * <p>This list will not include routes that are nested within RouteGroups. 1701d0d2cda9d414da73773285d7fee9e13aef3495e9Adam Powell * A RouteGroup is treated as a single route within its category.</p> 1702d0d2cda9d414da73773285d7fee9e13aef3495e9Adam Powell * 1703d0d2cda9d414da73773285d7fee9e13aef3495e9Adam Powell * @param out a List to fill with the routes in this category. If this parameter is 1704d0d2cda9d414da73773285d7fee9e13aef3495e9Adam Powell * non-null, it will be cleared, filled with the current routes with this 1705d0d2cda9d414da73773285d7fee9e13aef3495e9Adam Powell * category, and returned. If this parameter is null, a new List will be 1706d0d2cda9d414da73773285d7fee9e13aef3495e9Adam Powell * allocated to report the category's current routes. 1707d0d2cda9d414da73773285d7fee9e13aef3495e9Adam Powell * @return A list with the routes in this category that have been added to the MediaRouter. 17089a1de308cea2d160778fd977825f10a07b49d738Adam Powell */ 1709d0d2cda9d414da73773285d7fee9e13aef3495e9Adam Powell public List<RouteInfo> getRoutes(List<RouteInfo> out) { 1710d0d2cda9d414da73773285d7fee9e13aef3495e9Adam Powell if (out == null) { 1711d0d2cda9d414da73773285d7fee9e13aef3495e9Adam Powell out = new ArrayList<RouteInfo>(); 1712d0d2cda9d414da73773285d7fee9e13aef3495e9Adam Powell } else { 1713d0d2cda9d414da73773285d7fee9e13aef3495e9Adam Powell out.clear(); 1714d0d2cda9d414da73773285d7fee9e13aef3495e9Adam Powell } 1715d0d2cda9d414da73773285d7fee9e13aef3495e9Adam Powell 1716b58b8f832d06b0ffa8886eba5a4916578a3b8743Dianne Hackborn final int count = getRouteCountStatic(); 1717d0d2cda9d414da73773285d7fee9e13aef3495e9Adam Powell for (int i = 0; i < count; i++) { 1718b58b8f832d06b0ffa8886eba5a4916578a3b8743Dianne Hackborn final RouteInfo route = getRouteAtStatic(i); 1719d0d2cda9d414da73773285d7fee9e13aef3495e9Adam Powell if (route.mCategory == this) { 1720d0d2cda9d414da73773285d7fee9e13aef3495e9Adam Powell out.add(route); 1721d0d2cda9d414da73773285d7fee9e13aef3495e9Adam Powell } 1722d0d2cda9d414da73773285d7fee9e13aef3495e9Adam Powell } 1723d0d2cda9d414da73773285d7fee9e13aef3495e9Adam Powell return out; 17249a1de308cea2d160778fd977825f10a07b49d738Adam Powell } 17259a1de308cea2d160778fd977825f10a07b49d738Adam Powell 17269a1de308cea2d160778fd977825f10a07b49d738Adam Powell /** 17279a1de308cea2d160778fd977825f10a07b49d738Adam Powell * @return Flag set describing the route types supported by this category 17289a1de308cea2d160778fd977825f10a07b49d738Adam Powell */ 17299a1de308cea2d160778fd977825f10a07b49d738Adam Powell public int getSupportedTypes() { 17309a1de308cea2d160778fd977825f10a07b49d738Adam Powell return mTypes; 17319a1de308cea2d160778fd977825f10a07b49d738Adam Powell } 17329a1de308cea2d160778fd977825f10a07b49d738Adam Powell 17339a1de308cea2d160778fd977825f10a07b49d738Adam Powell /** 17349a1de308cea2d160778fd977825f10a07b49d738Adam Powell * Return whether or not this category supports grouping. 17359a1de308cea2d160778fd977825f10a07b49d738Adam Powell * 17369a1de308cea2d160778fd977825f10a07b49d738Adam Powell * <p>If this method returns true, all routes obtained from this category 1737d0d2cda9d414da73773285d7fee9e13aef3495e9Adam Powell * via calls to {@link #getRouteAt(int)} will be {@link MediaRouter.RouteGroup}s.</p> 17389a1de308cea2d160778fd977825f10a07b49d738Adam Powell * 17399a1de308cea2d160778fd977825f10a07b49d738Adam Powell * @return true if this category supports 17409a1de308cea2d160778fd977825f10a07b49d738Adam Powell */ 17419a1de308cea2d160778fd977825f10a07b49d738Adam Powell public boolean isGroupable() { 17429a1de308cea2d160778fd977825f10a07b49d738Adam Powell return mGroupable; 17439a1de308cea2d160778fd977825f10a07b49d738Adam Powell } 17449a1de308cea2d160778fd977825f10a07b49d738Adam Powell 1745705ab808cf023e0cc38c2ba7cdb9571942cdc04fAdam Powell /** 1746705ab808cf023e0cc38c2ba7cdb9571942cdc04fAdam Powell * @return true if this is the category reserved for system routes. 1747705ab808cf023e0cc38c2ba7cdb9571942cdc04fAdam Powell * @hide 1748705ab808cf023e0cc38c2ba7cdb9571942cdc04fAdam Powell */ 1749705ab808cf023e0cc38c2ba7cdb9571942cdc04fAdam Powell public boolean isSystem() { 1750705ab808cf023e0cc38c2ba7cdb9571942cdc04fAdam Powell return mIsSystem; 1751705ab808cf023e0cc38c2ba7cdb9571942cdc04fAdam Powell } 1752705ab808cf023e0cc38c2ba7cdb9571942cdc04fAdam Powell 17539a1de308cea2d160778fd977825f10a07b49d738Adam Powell public String toString() { 17549a1de308cea2d160778fd977825f10a07b49d738Adam Powell return "RouteCategory{ name=" + mName + " types=" + typesToString(mTypes) + 1755d6d0bddee363e0c7fe61f63bd9d9864a71d887d6Adam Powell " groupable=" + mGroupable + " }"; 17569a1de308cea2d160778fd977825f10a07b49d738Adam Powell } 17579a1de308cea2d160778fd977825f10a07b49d738Adam Powell } 17589a1de308cea2d160778fd977825f10a07b49d738Adam Powell 17599a1de308cea2d160778fd977825f10a07b49d738Adam Powell static class CallbackInfo { 17609a1de308cea2d160778fd977825f10a07b49d738Adam Powell public int type; 1761b58b8f832d06b0ffa8886eba5a4916578a3b8743Dianne Hackborn public final Callback cb; 1762b58b8f832d06b0ffa8886eba5a4916578a3b8743Dianne Hackborn public final MediaRouter router; 17639a1de308cea2d160778fd977825f10a07b49d738Adam Powell 1764b58b8f832d06b0ffa8886eba5a4916578a3b8743Dianne Hackborn public CallbackInfo(Callback cb, int type, MediaRouter router) { 17659a1de308cea2d160778fd977825f10a07b49d738Adam Powell this.cb = cb; 17669a1de308cea2d160778fd977825f10a07b49d738Adam Powell this.type = type; 1767b58b8f832d06b0ffa8886eba5a4916578a3b8743Dianne Hackborn this.router = router; 17689a1de308cea2d160778fd977825f10a07b49d738Adam Powell } 17699a1de308cea2d160778fd977825f10a07b49d738Adam Powell } 17709a1de308cea2d160778fd977825f10a07b49d738Adam Powell 17719a1de308cea2d160778fd977825f10a07b49d738Adam Powell /** 17729a1de308cea2d160778fd977825f10a07b49d738Adam Powell * Interface for receiving events about media routing changes. 17739a1de308cea2d160778fd977825f10a07b49d738Adam Powell * All methods of this interface will be called from the application's main thread. 17749a1de308cea2d160778fd977825f10a07b49d738Adam Powell * 17759a1de308cea2d160778fd977825f10a07b49d738Adam Powell * <p>A Callback will only receive events relevant to routes that the callback 17769a1de308cea2d160778fd977825f10a07b49d738Adam Powell * was registered for.</p> 17779a1de308cea2d160778fd977825f10a07b49d738Adam Powell * 17789a1de308cea2d160778fd977825f10a07b49d738Adam Powell * @see MediaRouter#addCallback(int, Callback) 17799a1de308cea2d160778fd977825f10a07b49d738Adam Powell * @see MediaRouter#removeCallback(Callback) 17809a1de308cea2d160778fd977825f10a07b49d738Adam Powell */ 17810d03c042f90bf62d5bad7c64e77028a5f9f8fae0Adam Powell public static abstract class Callback { 17829a1de308cea2d160778fd977825f10a07b49d738Adam Powell /** 17839a1de308cea2d160778fd977825f10a07b49d738Adam Powell * Called when the supplied route becomes selected as the active route 17849a1de308cea2d160778fd977825f10a07b49d738Adam Powell * for the given route type. 17859a1de308cea2d160778fd977825f10a07b49d738Adam Powell * 1786d0d2cda9d414da73773285d7fee9e13aef3495e9Adam Powell * @param router the MediaRouter reporting the event 17879a1de308cea2d160778fd977825f10a07b49d738Adam Powell * @param type Type flag set indicating the routes that have been selected 17889a1de308cea2d160778fd977825f10a07b49d738Adam Powell * @param info Route that has been selected for the given route types 17899a1de308cea2d160778fd977825f10a07b49d738Adam Powell */ 17900d03c042f90bf62d5bad7c64e77028a5f9f8fae0Adam Powell public abstract void onRouteSelected(MediaRouter router, int type, RouteInfo info); 17919a1de308cea2d160778fd977825f10a07b49d738Adam Powell 17929a1de308cea2d160778fd977825f10a07b49d738Adam Powell /** 17939a1de308cea2d160778fd977825f10a07b49d738Adam Powell * Called when the supplied route becomes unselected as the active route 17949a1de308cea2d160778fd977825f10a07b49d738Adam Powell * for the given route type. 17959a1de308cea2d160778fd977825f10a07b49d738Adam Powell * 1796d0d2cda9d414da73773285d7fee9e13aef3495e9Adam Powell * @param router the MediaRouter reporting the event 17979a1de308cea2d160778fd977825f10a07b49d738Adam Powell * @param type Type flag set indicating the routes that have been unselected 17989a1de308cea2d160778fd977825f10a07b49d738Adam Powell * @param info Route that has been unselected for the given route types 17999a1de308cea2d160778fd977825f10a07b49d738Adam Powell */ 18000d03c042f90bf62d5bad7c64e77028a5f9f8fae0Adam Powell public abstract void onRouteUnselected(MediaRouter router, int type, RouteInfo info); 18019a1de308cea2d160778fd977825f10a07b49d738Adam Powell 18029a1de308cea2d160778fd977825f10a07b49d738Adam Powell /** 18039a1de308cea2d160778fd977825f10a07b49d738Adam Powell * Called when a route for the specified type was added. 18049a1de308cea2d160778fd977825f10a07b49d738Adam Powell * 1805d0d2cda9d414da73773285d7fee9e13aef3495e9Adam Powell * @param router the MediaRouter reporting the event 18069a1de308cea2d160778fd977825f10a07b49d738Adam Powell * @param info Route that has become available for use 18079a1de308cea2d160778fd977825f10a07b49d738Adam Powell */ 18080d03c042f90bf62d5bad7c64e77028a5f9f8fae0Adam Powell public abstract void onRouteAdded(MediaRouter router, RouteInfo info); 18099a1de308cea2d160778fd977825f10a07b49d738Adam Powell 18109a1de308cea2d160778fd977825f10a07b49d738Adam Powell /** 18119a1de308cea2d160778fd977825f10a07b49d738Adam Powell * Called when a route for the specified type was removed. 18129a1de308cea2d160778fd977825f10a07b49d738Adam Powell * 1813d0d2cda9d414da73773285d7fee9e13aef3495e9Adam Powell * @param router the MediaRouter reporting the event 18149a1de308cea2d160778fd977825f10a07b49d738Adam Powell * @param info Route that has been removed from availability 18159a1de308cea2d160778fd977825f10a07b49d738Adam Powell */ 18160d03c042f90bf62d5bad7c64e77028a5f9f8fae0Adam Powell public abstract void onRouteRemoved(MediaRouter router, RouteInfo info); 18179a1de308cea2d160778fd977825f10a07b49d738Adam Powell 18189a1de308cea2d160778fd977825f10a07b49d738Adam Powell /** 18199a1de308cea2d160778fd977825f10a07b49d738Adam Powell * Called when an aspect of the indicated route has changed. 18209a1de308cea2d160778fd977825f10a07b49d738Adam Powell * 18219a1de308cea2d160778fd977825f10a07b49d738Adam Powell * <p>This will not indicate that the types supported by this route have 18229a1de308cea2d160778fd977825f10a07b49d738Adam Powell * changed, only that cosmetic info such as name or status have been updated.</p> 18239a1de308cea2d160778fd977825f10a07b49d738Adam Powell * 1824d0d2cda9d414da73773285d7fee9e13aef3495e9Adam Powell * @param router the MediaRouter reporting the event 18259a1de308cea2d160778fd977825f10a07b49d738Adam Powell * @param info The route that was changed 18269a1de308cea2d160778fd977825f10a07b49d738Adam Powell */ 18270d03c042f90bf62d5bad7c64e77028a5f9f8fae0Adam Powell public abstract void onRouteChanged(MediaRouter router, RouteInfo info); 1828d0d2cda9d414da73773285d7fee9e13aef3495e9Adam Powell 1829d0d2cda9d414da73773285d7fee9e13aef3495e9Adam Powell /** 1830d0d2cda9d414da73773285d7fee9e13aef3495e9Adam Powell * Called when a route is added to a group. 1831d0d2cda9d414da73773285d7fee9e13aef3495e9Adam Powell * 1832d0d2cda9d414da73773285d7fee9e13aef3495e9Adam Powell * @param router the MediaRouter reporting the event 1833d0d2cda9d414da73773285d7fee9e13aef3495e9Adam Powell * @param info The route that was added 1834d0d2cda9d414da73773285d7fee9e13aef3495e9Adam Powell * @param group The group the route was added to 1835d0d2cda9d414da73773285d7fee9e13aef3495e9Adam Powell * @param index The route index within group that info was added at 1836d0d2cda9d414da73773285d7fee9e13aef3495e9Adam Powell */ 18370d03c042f90bf62d5bad7c64e77028a5f9f8fae0Adam Powell public abstract void onRouteGrouped(MediaRouter router, RouteInfo info, RouteGroup group, 18380d03c042f90bf62d5bad7c64e77028a5f9f8fae0Adam Powell int index); 1839d0d2cda9d414da73773285d7fee9e13aef3495e9Adam Powell 1840d0d2cda9d414da73773285d7fee9e13aef3495e9Adam Powell /** 1841d0d2cda9d414da73773285d7fee9e13aef3495e9Adam Powell * Called when a route is removed from a group. 1842d0d2cda9d414da73773285d7fee9e13aef3495e9Adam Powell * 1843d0d2cda9d414da73773285d7fee9e13aef3495e9Adam Powell * @param router the MediaRouter reporting the event 1844d0d2cda9d414da73773285d7fee9e13aef3495e9Adam Powell * @param info The route that was removed 1845d0d2cda9d414da73773285d7fee9e13aef3495e9Adam Powell * @param group The group the route was removed from 1846d0d2cda9d414da73773285d7fee9e13aef3495e9Adam Powell */ 18470d03c042f90bf62d5bad7c64e77028a5f9f8fae0Adam Powell public abstract void onRouteUngrouped(MediaRouter router, RouteInfo info, RouteGroup group); 18488e37a85bf3dc39519942698dc90a3951306b934bAdam Powell 18498e37a85bf3dc39519942698dc90a3951306b934bAdam Powell /** 18508e37a85bf3dc39519942698dc90a3951306b934bAdam Powell * Called when a route's volume changes. 18518e37a85bf3dc39519942698dc90a3951306b934bAdam Powell * 18528e37a85bf3dc39519942698dc90a3951306b934bAdam Powell * @param router the MediaRouter reporting the event 18538e37a85bf3dc39519942698dc90a3951306b934bAdam Powell * @param info The route with altered volume 18548e37a85bf3dc39519942698dc90a3951306b934bAdam Powell */ 18558e37a85bf3dc39519942698dc90a3951306b934bAdam Powell public abstract void onRouteVolumeChanged(MediaRouter router, RouteInfo info); 18569a1de308cea2d160778fd977825f10a07b49d738Adam Powell } 18579a1de308cea2d160778fd977825f10a07b49d738Adam Powell 18589a1de308cea2d160778fd977825f10a07b49d738Adam Powell /** 18590d03c042f90bf62d5bad7c64e77028a5f9f8fae0Adam Powell * Stub implementation of {@link MediaRouter.Callback}. 18600d03c042f90bf62d5bad7c64e77028a5f9f8fae0Adam Powell * Each abstract method is defined as a no-op. Override just the ones 18619a1de308cea2d160778fd977825f10a07b49d738Adam Powell * you need. 18629a1de308cea2d160778fd977825f10a07b49d738Adam Powell */ 18630d03c042f90bf62d5bad7c64e77028a5f9f8fae0Adam Powell public static class SimpleCallback extends Callback { 18649a1de308cea2d160778fd977825f10a07b49d738Adam Powell 18659a1de308cea2d160778fd977825f10a07b49d738Adam Powell @Override 1866d0d2cda9d414da73773285d7fee9e13aef3495e9Adam Powell public void onRouteSelected(MediaRouter router, int type, RouteInfo info) { 18679a1de308cea2d160778fd977825f10a07b49d738Adam Powell } 18689a1de308cea2d160778fd977825f10a07b49d738Adam Powell 18699a1de308cea2d160778fd977825f10a07b49d738Adam Powell @Override 1870d0d2cda9d414da73773285d7fee9e13aef3495e9Adam Powell public void onRouteUnselected(MediaRouter router, int type, RouteInfo info) { 18719a1de308cea2d160778fd977825f10a07b49d738Adam Powell } 18729a1de308cea2d160778fd977825f10a07b49d738Adam Powell 18739a1de308cea2d160778fd977825f10a07b49d738Adam Powell @Override 1874d0d2cda9d414da73773285d7fee9e13aef3495e9Adam Powell public void onRouteAdded(MediaRouter router, RouteInfo info) { 18759a1de308cea2d160778fd977825f10a07b49d738Adam Powell } 18769a1de308cea2d160778fd977825f10a07b49d738Adam Powell 18779a1de308cea2d160778fd977825f10a07b49d738Adam Powell @Override 1878d0d2cda9d414da73773285d7fee9e13aef3495e9Adam Powell public void onRouteRemoved(MediaRouter router, RouteInfo info) { 18799a1de308cea2d160778fd977825f10a07b49d738Adam Powell } 18809a1de308cea2d160778fd977825f10a07b49d738Adam Powell 18819a1de308cea2d160778fd977825f10a07b49d738Adam Powell @Override 1882d0d2cda9d414da73773285d7fee9e13aef3495e9Adam Powell public void onRouteChanged(MediaRouter router, RouteInfo info) { 18839a1de308cea2d160778fd977825f10a07b49d738Adam Powell } 18849a1de308cea2d160778fd977825f10a07b49d738Adam Powell 18859a1de308cea2d160778fd977825f10a07b49d738Adam Powell @Override 1886d0d2cda9d414da73773285d7fee9e13aef3495e9Adam Powell public void onRouteGrouped(MediaRouter router, RouteInfo info, RouteGroup group, 1887d0d2cda9d414da73773285d7fee9e13aef3495e9Adam Powell int index) { 18889a1de308cea2d160778fd977825f10a07b49d738Adam Powell } 18899a1de308cea2d160778fd977825f10a07b49d738Adam Powell 18909a1de308cea2d160778fd977825f10a07b49d738Adam Powell @Override 1891d0d2cda9d414da73773285d7fee9e13aef3495e9Adam Powell public void onRouteUngrouped(MediaRouter router, RouteInfo info, RouteGroup group) { 18929a1de308cea2d160778fd977825f10a07b49d738Adam Powell } 1893d0d2cda9d414da73773285d7fee9e13aef3495e9Adam Powell 18948e37a85bf3dc39519942698dc90a3951306b934bAdam Powell @Override 18958e37a85bf3dc39519942698dc90a3951306b934bAdam Powell public void onRouteVolumeChanged(MediaRouter router, RouteInfo info) { 18968e37a85bf3dc39519942698dc90a3951306b934bAdam Powell } 18979a1de308cea2d160778fd977825f10a07b49d738Adam Powell } 18981357012968f9066ea3051d83995e9bac69526c3cJean-Michel Trivi 18991357012968f9066ea3051d83995e9bac69526c3cJean-Michel Trivi static class VolumeCallbackInfo { 19001357012968f9066ea3051d83995e9bac69526c3cJean-Michel Trivi public final VolumeCallback vcb; 19011357012968f9066ea3051d83995e9bac69526c3cJean-Michel Trivi public final RouteInfo route; 19021357012968f9066ea3051d83995e9bac69526c3cJean-Michel Trivi 19031357012968f9066ea3051d83995e9bac69526c3cJean-Michel Trivi public VolumeCallbackInfo(VolumeCallback vcb, RouteInfo route) { 19041357012968f9066ea3051d83995e9bac69526c3cJean-Michel Trivi this.vcb = vcb; 19051357012968f9066ea3051d83995e9bac69526c3cJean-Michel Trivi this.route = route; 19061357012968f9066ea3051d83995e9bac69526c3cJean-Michel Trivi } 19071357012968f9066ea3051d83995e9bac69526c3cJean-Michel Trivi } 19081357012968f9066ea3051d83995e9bac69526c3cJean-Michel Trivi 19091357012968f9066ea3051d83995e9bac69526c3cJean-Michel Trivi /** 19101357012968f9066ea3051d83995e9bac69526c3cJean-Michel Trivi * Interface for receiving events about volume changes. 19111357012968f9066ea3051d83995e9bac69526c3cJean-Michel Trivi * All methods of this interface will be called from the application's main thread. 19121357012968f9066ea3051d83995e9bac69526c3cJean-Michel Trivi * 19131357012968f9066ea3051d83995e9bac69526c3cJean-Michel Trivi * <p>A VolumeCallback will only receive events relevant to routes that the callback 19141357012968f9066ea3051d83995e9bac69526c3cJean-Michel Trivi * was registered for.</p> 19151357012968f9066ea3051d83995e9bac69526c3cJean-Michel Trivi * 19161357012968f9066ea3051d83995e9bac69526c3cJean-Michel Trivi * @see UserRouteInfo#setVolumeCallback(VolumeCallback) 19171357012968f9066ea3051d83995e9bac69526c3cJean-Michel Trivi */ 19181357012968f9066ea3051d83995e9bac69526c3cJean-Michel Trivi public static abstract class VolumeCallback { 19191357012968f9066ea3051d83995e9bac69526c3cJean-Michel Trivi /** 19201357012968f9066ea3051d83995e9bac69526c3cJean-Michel Trivi * Called when the volume for the route should be increased or decreased. 19211357012968f9066ea3051d83995e9bac69526c3cJean-Michel Trivi * @param info the route affected by this event 19221357012968f9066ea3051d83995e9bac69526c3cJean-Michel Trivi * @param direction an integer indicating whether the volume is to be increased 19231357012968f9066ea3051d83995e9bac69526c3cJean-Michel Trivi * (positive value) or decreased (negative value). 19241357012968f9066ea3051d83995e9bac69526c3cJean-Michel Trivi * For bundled changes, the absolute value indicates the number of changes 19251357012968f9066ea3051d83995e9bac69526c3cJean-Michel Trivi * in the same direction, e.g. +3 corresponds to three "volume up" changes. 19261357012968f9066ea3051d83995e9bac69526c3cJean-Michel Trivi */ 19271357012968f9066ea3051d83995e9bac69526c3cJean-Michel Trivi public abstract void onVolumeUpdateRequest(RouteInfo info, int direction); 19281357012968f9066ea3051d83995e9bac69526c3cJean-Michel Trivi /** 19291357012968f9066ea3051d83995e9bac69526c3cJean-Michel Trivi * Called when the volume for the route should be set to the given value 19301357012968f9066ea3051d83995e9bac69526c3cJean-Michel Trivi * @param info the route affected by this event 19311357012968f9066ea3051d83995e9bac69526c3cJean-Michel Trivi * @param volume an integer indicating the new volume value that should be used, always 19321357012968f9066ea3051d83995e9bac69526c3cJean-Michel Trivi * between 0 and the value set by {@link UserRouteInfo#setVolumeMax(int)}. 19331357012968f9066ea3051d83995e9bac69526c3cJean-Michel Trivi */ 19341357012968f9066ea3051d83995e9bac69526c3cJean-Michel Trivi public abstract void onVolumeSetRequest(RouteInfo info, int volume); 19351357012968f9066ea3051d83995e9bac69526c3cJean-Michel Trivi } 19361357012968f9066ea3051d83995e9bac69526c3cJean-Michel Trivi 19378e37a85bf3dc39519942698dc90a3951306b934bAdam Powell static class VolumeChangeReceiver extends BroadcastReceiver { 19388e37a85bf3dc39519942698dc90a3951306b934bAdam Powell @Override 19398e37a85bf3dc39519942698dc90a3951306b934bAdam Powell public void onReceive(Context context, Intent intent) { 19408e37a85bf3dc39519942698dc90a3951306b934bAdam Powell if (intent.getAction().equals(AudioManager.VOLUME_CHANGED_ACTION)) { 19418e37a85bf3dc39519942698dc90a3951306b934bAdam Powell final int streamType = intent.getIntExtra(AudioManager.EXTRA_VOLUME_STREAM_TYPE, 19428e37a85bf3dc39519942698dc90a3951306b934bAdam Powell -1); 19438e37a85bf3dc39519942698dc90a3951306b934bAdam Powell if (streamType != AudioManager.STREAM_MUSIC) { 19448e37a85bf3dc39519942698dc90a3951306b934bAdam Powell return; 19458e37a85bf3dc39519942698dc90a3951306b934bAdam Powell } 19468e37a85bf3dc39519942698dc90a3951306b934bAdam Powell 19478e37a85bf3dc39519942698dc90a3951306b934bAdam Powell final int newVolume = intent.getIntExtra(AudioManager.EXTRA_VOLUME_STREAM_VALUE, 0); 19488e37a85bf3dc39519942698dc90a3951306b934bAdam Powell final int oldVolume = intent.getIntExtra( 19498e37a85bf3dc39519942698dc90a3951306b934bAdam Powell AudioManager.EXTRA_PREV_VOLUME_STREAM_VALUE, 0); 19508e37a85bf3dc39519942698dc90a3951306b934bAdam Powell if (newVolume != oldVolume) { 19518e37a85bf3dc39519942698dc90a3951306b934bAdam Powell systemVolumeChanged(newVolume); 19528e37a85bf3dc39519942698dc90a3951306b934bAdam Powell } 19538e37a85bf3dc39519942698dc90a3951306b934bAdam Powell } 19548e37a85bf3dc39519942698dc90a3951306b934bAdam Powell } 1955705ab808cf023e0cc38c2ba7cdb9571942cdc04fAdam Powell } 19568e37a85bf3dc39519942698dc90a3951306b934bAdam Powell 1957705ab808cf023e0cc38c2ba7cdb9571942cdc04fAdam Powell static class WifiDisplayStatusChangedReceiver extends BroadcastReceiver { 1958705ab808cf023e0cc38c2ba7cdb9571942cdc04fAdam Powell @Override 1959705ab808cf023e0cc38c2ba7cdb9571942cdc04fAdam Powell public void onReceive(Context context, Intent intent) { 1960705ab808cf023e0cc38c2ba7cdb9571942cdc04fAdam Powell if (intent.getAction().equals(DisplayManager.ACTION_WIFI_DISPLAY_STATUS_CHANGED)) { 1961705ab808cf023e0cc38c2ba7cdb9571942cdc04fAdam Powell updateWifiDisplayStatus((WifiDisplayStatus) intent.getParcelableExtra( 1962705ab808cf023e0cc38c2ba7cdb9571942cdc04fAdam Powell DisplayManager.EXTRA_WIFI_DISPLAY_STATUS)); 1963705ab808cf023e0cc38c2ba7cdb9571942cdc04fAdam Powell } 1964705ab808cf023e0cc38c2ba7cdb9571942cdc04fAdam Powell } 19658e37a85bf3dc39519942698dc90a3951306b934bAdam Powell } 19669a1de308cea2d160778fd977825f10a07b49d738Adam Powell} 1967