1c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown/* 2c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown * Copyright (C) 2013 The Android Open Source Project 3c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown * 4c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown * Licensed under the Apache License, Version 2.0 (the "License"); 5c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown * you may not use this file except in compliance with the License. 6c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown * You may obtain a copy of the License at 7c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown * 8c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown * http://www.apache.org/licenses/LICENSE-2.0 9c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown * 10c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown * Unless required by applicable law or agreed to in writing, software 11c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown * distributed under the License is distributed on an "AS IS" BASIS, 12c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown * See the License for the specific language governing permissions and 14c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown * limitations under the License. 15c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown */ 16c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown 17b507e525a61ed761eecfc2eaaf19af7e8db5dca5Jeff Brownpackage android.support.v7.media; 18c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown 19c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brownimport android.content.BroadcastReceiver; 20adf0f4a217e14894af07dfa9f46cad7d98b8a7f4Jeff Brownimport android.content.ComponentName; 21c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brownimport android.content.Context; 22c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brownimport android.content.Intent; 23c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brownimport android.content.IntentFilter; 24b507e525a61ed761eecfc2eaaf19af7e8db5dca5Jeff Brownimport android.content.res.Resources; 25c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brownimport android.media.AudioManager; 26c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brownimport android.os.Build; 27b507e525a61ed761eecfc2eaaf19af7e8db5dca5Jeff Brownimport android.support.v7.mediarouter.R; 28c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brownimport android.view.Display; 29c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown 30c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brownimport java.util.ArrayList; 3111417b1cfde8f1749905f2d735623af9214148afJeff Brownimport java.util.List; 32cb63b6ecac9786891514f241dec71695f09d3efbJeff Brownimport java.util.Locale; 33c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown 34c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown/** 35c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown * Provides routes for built-in system destinations such as the local display 36c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown * and speaker. On Jellybean and newer platform releases, queries the framework 37c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown * MediaRouter for framework-provided routes and registers non-framework-provided 38c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown * routes as user routes. 39c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown */ 40c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brownabstract class SystemMediaRouteProvider extends MediaRouteProvider { 41c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown private static final String TAG = "SystemMediaRouteProvider"; 42c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown 43fa326a4649d9d0e8113e315f6c8251fe686abce4Jeff Brown public static final String PACKAGE_NAME = "android"; 44c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown public static final String DEFAULT_ROUTE_ID = "DEFAULT_ROUTE"; 45c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown 46c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown protected SystemMediaRouteProvider(Context context) { 47adf0f4a217e14894af07dfa9f46cad7d98b8a7f4Jeff Brown super(context, new ProviderMetadata(new ComponentName(PACKAGE_NAME, 48adf0f4a217e14894af07dfa9f46cad7d98b8a7f4Jeff Brown SystemMediaRouteProvider.class.getName()))); 49c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown } 50c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown 51c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown public static SystemMediaRouteProvider obtain(Context context, SyncCallback syncCallback) { 52c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown if (Build.VERSION.SDK_INT >= 18) { 53c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown return new JellybeanMr2Impl(context, syncCallback); 54c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown } 55c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown if (Build.VERSION.SDK_INT >= 17) { 56c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown return new JellybeanMr1Impl(context, syncCallback); 57c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown } 58c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown if (Build.VERSION.SDK_INT >= 16) { 59c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown return new JellybeanImpl(context, syncCallback); 60c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown } 61c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown return new LegacyImpl(context); 62c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown } 63c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown 64c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown /** 65c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown * Called by the media router when a route is added to synchronize state with 66c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown * the framework media router. 67c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown */ 68c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown public void onSyncRouteAdded(MediaRouter.RouteInfo route) { 69c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown } 70c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown 71c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown /** 72c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown * Called by the media router when a route is removed to synchronize state with 73c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown * the framework media router. 74c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown */ 75c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown public void onSyncRouteRemoved(MediaRouter.RouteInfo route) { 76c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown } 77c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown 78c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown /** 79c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown * Called by the media router when a route is changed to synchronize state with 80c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown * the framework media router. 81c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown */ 82c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown public void onSyncRouteChanged(MediaRouter.RouteInfo route) { 83c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown } 84c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown 85c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown /** 86c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown * Called by the media router when a route is selected to synchronize state with 87c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown * the framework media router. 88c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown */ 89c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown public void onSyncRouteSelected(MediaRouter.RouteInfo route) { 90c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown } 91c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown 92c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown /** 93c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown * Callbacks into the media router to synchronize state with the framework media router. 94c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown */ 95c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown public interface SyncCallback { 96c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown public MediaRouter.RouteInfo getSystemRouteByDescriptorId(String id); 97c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown } 98c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown 99c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown /** 100c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown * Legacy implementation for platform versions prior to Jellybean. 101c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown */ 102c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown static class LegacyImpl extends SystemMediaRouteProvider { 103c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown private static final int PLAYBACK_STREAM = AudioManager.STREAM_MUSIC; 104c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown 10511417b1cfde8f1749905f2d735623af9214148afJeff Brown private static final ArrayList<IntentFilter> CONTROL_FILTERS; 106fa326a4649d9d0e8113e315f6c8251fe686abce4Jeff Brown static { 10711417b1cfde8f1749905f2d735623af9214148afJeff Brown IntentFilter f = new IntentFilter(); 10811417b1cfde8f1749905f2d735623af9214148afJeff Brown f.addCategory(MediaControlIntent.CATEGORY_LIVE_AUDIO); 10911417b1cfde8f1749905f2d735623af9214148afJeff Brown f.addCategory(MediaControlIntent.CATEGORY_LIVE_VIDEO); 11011417b1cfde8f1749905f2d735623af9214148afJeff Brown 11111417b1cfde8f1749905f2d735623af9214148afJeff Brown CONTROL_FILTERS = new ArrayList<IntentFilter>(); 11211417b1cfde8f1749905f2d735623af9214148afJeff Brown CONTROL_FILTERS.add(f); 113fa326a4649d9d0e8113e315f6c8251fe686abce4Jeff Brown } 114fa326a4649d9d0e8113e315f6c8251fe686abce4Jeff Brown 115c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown private final AudioManager mAudioManager; 116c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown private final VolumeChangeReceiver mVolumeChangeReceiver; 117c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown private int mLastReportedVolume = -1; 118c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown 119c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown public LegacyImpl(Context context) { 120c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown super(context); 121c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown mAudioManager = (AudioManager)context.getSystemService(Context.AUDIO_SERVICE); 122c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown mVolumeChangeReceiver = new VolumeChangeReceiver(); 123c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown 124c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown context.registerReceiver(mVolumeChangeReceiver, 125c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown new IntentFilter(VolumeChangeReceiver.VOLUME_CHANGED_ACTION)); 126c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown publishRoutes(); 127c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown } 128c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown 129c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown private void publishRoutes() { 130b507e525a61ed761eecfc2eaaf19af7e8db5dca5Jeff Brown Resources r = getContext().getResources(); 13111417b1cfde8f1749905f2d735623af9214148afJeff Brown int maxVolume = mAudioManager.getStreamMaxVolume(PLAYBACK_STREAM); 132c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown mLastReportedVolume = mAudioManager.getStreamVolume(PLAYBACK_STREAM); 13311417b1cfde8f1749905f2d735623af9214148afJeff Brown MediaRouteDescriptor defaultRoute = new MediaRouteDescriptor.Builder( 1343d4c9459ed77f732dd3ba602713af6ebf9280c8cJeff Brown DEFAULT_ROUTE_ID, r.getString(R.string.mr_system_route_name)) 13511417b1cfde8f1749905f2d735623af9214148afJeff Brown .addControlFilters(CONTROL_FILTERS) 13611417b1cfde8f1749905f2d735623af9214148afJeff Brown .setPlaybackStream(PLAYBACK_STREAM) 13711417b1cfde8f1749905f2d735623af9214148afJeff Brown .setPlaybackType(MediaRouter.RouteInfo.PLAYBACK_TYPE_LOCAL) 13811417b1cfde8f1749905f2d735623af9214148afJeff Brown .setVolumeHandling(MediaRouter.RouteInfo.PLAYBACK_VOLUME_VARIABLE) 13911417b1cfde8f1749905f2d735623af9214148afJeff Brown .setVolumeMax(maxVolume) 14011417b1cfde8f1749905f2d735623af9214148afJeff Brown .setVolume(mLastReportedVolume) 14111417b1cfde8f1749905f2d735623af9214148afJeff Brown .build(); 14211417b1cfde8f1749905f2d735623af9214148afJeff Brown 14311417b1cfde8f1749905f2d735623af9214148afJeff Brown MediaRouteProviderDescriptor providerDescriptor = 14411417b1cfde8f1749905f2d735623af9214148afJeff Brown new MediaRouteProviderDescriptor.Builder() 14511417b1cfde8f1749905f2d735623af9214148afJeff Brown .addRoute(defaultRoute) 14611417b1cfde8f1749905f2d735623af9214148afJeff Brown .build(); 147c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown setDescriptor(providerDescriptor); 148c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown } 149c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown 150c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown @Override 151c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown public RouteController onCreateRouteController(String routeId) { 152c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown if (routeId.equals(DEFAULT_ROUTE_ID)) { 153c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown return new DefaultRouteController(); 154c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown } 155c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown return null; 156c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown } 157c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown 158c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown final class DefaultRouteController extends RouteController { 159c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown @Override 160129abf73ce9be1bc172b945263c7975ad1a3006fJeff Brown public void onSetVolume(int volume) { 161c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown mAudioManager.setStreamVolume(PLAYBACK_STREAM, volume, 0); 162c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown publishRoutes(); 163c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown } 164c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown 165c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown @Override 166129abf73ce9be1bc172b945263c7975ad1a3006fJeff Brown public void onUpdateVolume(int delta) { 167c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown int volume = mAudioManager.getStreamVolume(PLAYBACK_STREAM); 168c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown int maxVolume = mAudioManager.getStreamMaxVolume(PLAYBACK_STREAM); 169c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown int newVolume = Math.min(maxVolume, Math.max(0, volume + delta)); 170c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown if (newVolume != volume) { 171c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown mAudioManager.setStreamVolume(PLAYBACK_STREAM, volume, 0); 172c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown } 173c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown publishRoutes(); 174c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown } 175c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown } 176c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown 177c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown final class VolumeChangeReceiver extends BroadcastReceiver { 178c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown // These constants come from AudioManager. 179c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown public static final String VOLUME_CHANGED_ACTION = 180c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown "android.media.VOLUME_CHANGED_ACTION"; 181c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown public static final String EXTRA_VOLUME_STREAM_TYPE = 182c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown "android.media.EXTRA_VOLUME_STREAM_TYPE"; 183c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown public static final String EXTRA_VOLUME_STREAM_VALUE = 184c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown "android.media.EXTRA_VOLUME_STREAM_VALUE"; 185c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown 186c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown @Override 187c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown public void onReceive(Context context, Intent intent) { 188c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown if (intent.getAction().equals(VOLUME_CHANGED_ACTION)) { 189c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown final int streamType = intent.getIntExtra(EXTRA_VOLUME_STREAM_TYPE, -1); 190c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown if (streamType == PLAYBACK_STREAM) { 191c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown final int volume = intent.getIntExtra(EXTRA_VOLUME_STREAM_VALUE, -1); 192c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown if (volume >= 0 && volume != mLastReportedVolume) { 193c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown publishRoutes(); 194c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown } 195c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown } 196c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown } 197c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown } 198c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown } 199c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown } 200c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown 201c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown /** 202c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown * Jellybean implementation. 203c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown */ 204c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown static class JellybeanImpl extends SystemMediaRouteProvider 205c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown implements MediaRouterJellybean.Callback, MediaRouterJellybean.VolumeCallback { 20611417b1cfde8f1749905f2d735623af9214148afJeff Brown private static final ArrayList<IntentFilter> LIVE_AUDIO_CONTROL_FILTERS; 207fa326a4649d9d0e8113e315f6c8251fe686abce4Jeff Brown static { 20811417b1cfde8f1749905f2d735623af9214148afJeff Brown IntentFilter f = new IntentFilter(); 20911417b1cfde8f1749905f2d735623af9214148afJeff Brown f.addCategory(MediaControlIntent.CATEGORY_LIVE_AUDIO); 210fa326a4649d9d0e8113e315f6c8251fe686abce4Jeff Brown 21111417b1cfde8f1749905f2d735623af9214148afJeff Brown LIVE_AUDIO_CONTROL_FILTERS = new ArrayList<IntentFilter>(); 21211417b1cfde8f1749905f2d735623af9214148afJeff Brown LIVE_AUDIO_CONTROL_FILTERS.add(f); 213fa326a4649d9d0e8113e315f6c8251fe686abce4Jeff Brown } 214fa326a4649d9d0e8113e315f6c8251fe686abce4Jeff Brown 21511417b1cfde8f1749905f2d735623af9214148afJeff Brown private static final ArrayList<IntentFilter> LIVE_VIDEO_CONTROL_FILTERS; 216fa326a4649d9d0e8113e315f6c8251fe686abce4Jeff Brown static { 21711417b1cfde8f1749905f2d735623af9214148afJeff Brown IntentFilter f = new IntentFilter(); 21811417b1cfde8f1749905f2d735623af9214148afJeff Brown f.addCategory(MediaControlIntent.CATEGORY_LIVE_VIDEO); 21911417b1cfde8f1749905f2d735623af9214148afJeff Brown 22011417b1cfde8f1749905f2d735623af9214148afJeff Brown LIVE_VIDEO_CONTROL_FILTERS = new ArrayList<IntentFilter>(); 22111417b1cfde8f1749905f2d735623af9214148afJeff Brown LIVE_VIDEO_CONTROL_FILTERS.add(f); 222fa326a4649d9d0e8113e315f6c8251fe686abce4Jeff Brown } 223fa326a4649d9d0e8113e315f6c8251fe686abce4Jeff Brown 224c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown private final SyncCallback mSyncCallback; 225c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown 226c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown protected final Object mRouterObj; 227c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown protected final Object mCallbackObj; 228c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown protected final Object mVolumeCallbackObj; 229c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown protected final Object mUserRouteCategoryObj; 23011417b1cfde8f1749905f2d735623af9214148afJeff Brown protected int mRouteTypes; 23111417b1cfde8f1749905f2d735623af9214148afJeff Brown protected boolean mActiveScan; 23211417b1cfde8f1749905f2d735623af9214148afJeff Brown protected boolean mCallbackRegistered; 233c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown 234c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown // Maintains an association from framework routes to support library routes. 235c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown // Note that we cannot use the tag field for this because an application may 236c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown // have published its own user routes to the framework media router and already 237c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown // used the tag for its own purposes. 238c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown protected final ArrayList<SystemRouteRecord> mSystemRouteRecords = 239c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown new ArrayList<SystemRouteRecord>(); 240c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown 241c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown // Maintains an association from support library routes to framework routes. 242c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown protected final ArrayList<UserRouteRecord> mUserRouteRecords = 243c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown new ArrayList<UserRouteRecord>(); 244c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown 24511417b1cfde8f1749905f2d735623af9214148afJeff Brown private MediaRouterJellybean.SelectRouteWorkaround mSelectRouteWorkaround; 24611417b1cfde8f1749905f2d735623af9214148afJeff Brown private MediaRouterJellybean.GetDefaultRouteWorkaround mGetDefaultRouteWorkaround; 24711417b1cfde8f1749905f2d735623af9214148afJeff Brown 248c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown public JellybeanImpl(Context context, SyncCallback syncCallback) { 249c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown super(context); 250c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown mSyncCallback = syncCallback; 251c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown mRouterObj = MediaRouterJellybean.getMediaRouter(context); 252c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown mCallbackObj = createCallbackObj(); 253c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown mVolumeCallbackObj = createVolumeCallbackObj(); 254c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown 255b507e525a61ed761eecfc2eaaf19af7e8db5dca5Jeff Brown Resources r = context.getResources(); 256c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown mUserRouteCategoryObj = MediaRouterJellybean.createRouteCategory( 2573d4c9459ed77f732dd3ba602713af6ebf9280c8cJeff Brown mRouterObj, r.getString(R.string.mr_user_route_category_name), false); 258c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown 25911417b1cfde8f1749905f2d735623af9214148afJeff Brown updateSystemRoutes(); 260c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown } 261c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown 262c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown @Override 26319e77645aa6ce65b466d570375b36e2428f170b3Jeff Brown public RouteController onCreateRouteController(String routeId) { 26419e77645aa6ce65b466d570375b36e2428f170b3Jeff Brown int index = findSystemRouteRecordByDescriptorId(routeId); 26519e77645aa6ce65b466d570375b36e2428f170b3Jeff Brown if (index >= 0) { 26619e77645aa6ce65b466d570375b36e2428f170b3Jeff Brown SystemRouteRecord record = mSystemRouteRecords.get(index); 26719e77645aa6ce65b466d570375b36e2428f170b3Jeff Brown return new SystemRouteController(record.mRouteObj); 26819e77645aa6ce65b466d570375b36e2428f170b3Jeff Brown } 26919e77645aa6ce65b466d570375b36e2428f170b3Jeff Brown return null; 27019e77645aa6ce65b466d570375b36e2428f170b3Jeff Brown } 27119e77645aa6ce65b466d570375b36e2428f170b3Jeff Brown 27219e77645aa6ce65b466d570375b36e2428f170b3Jeff Brown @Override 27311417b1cfde8f1749905f2d735623af9214148afJeff Brown public void onDiscoveryRequestChanged(MediaRouteDiscoveryRequest request) { 27411417b1cfde8f1749905f2d735623af9214148afJeff Brown int newRouteTypes = 0; 27511417b1cfde8f1749905f2d735623af9214148afJeff Brown boolean newActiveScan = false; 27611417b1cfde8f1749905f2d735623af9214148afJeff Brown if (request != null) { 27711417b1cfde8f1749905f2d735623af9214148afJeff Brown final MediaRouteSelector selector = request.getSelector(); 27811417b1cfde8f1749905f2d735623af9214148afJeff Brown final List<String> categories = selector.getControlCategories(); 27911417b1cfde8f1749905f2d735623af9214148afJeff Brown final int count = categories.size(); 28011417b1cfde8f1749905f2d735623af9214148afJeff Brown for (int i = 0; i < count; i++) { 28111417b1cfde8f1749905f2d735623af9214148afJeff Brown String category = categories.get(i); 28211417b1cfde8f1749905f2d735623af9214148afJeff Brown if (category.equals(MediaControlIntent.CATEGORY_LIVE_AUDIO)) { 28311417b1cfde8f1749905f2d735623af9214148afJeff Brown newRouteTypes |= MediaRouterJellybean.ROUTE_TYPE_LIVE_AUDIO; 28411417b1cfde8f1749905f2d735623af9214148afJeff Brown } else if (category.equals(MediaControlIntent.CATEGORY_LIVE_VIDEO)) { 28511417b1cfde8f1749905f2d735623af9214148afJeff Brown newRouteTypes |= MediaRouterJellybean.ROUTE_TYPE_LIVE_VIDEO; 28611417b1cfde8f1749905f2d735623af9214148afJeff Brown } else { 28711417b1cfde8f1749905f2d735623af9214148afJeff Brown newRouteTypes |= MediaRouterJellybean.ROUTE_TYPE_USER; 28811417b1cfde8f1749905f2d735623af9214148afJeff Brown } 289fa326a4649d9d0e8113e315f6c8251fe686abce4Jeff Brown } 29011417b1cfde8f1749905f2d735623af9214148afJeff Brown newActiveScan = request.isActiveScan(); 29111417b1cfde8f1749905f2d735623af9214148afJeff Brown } 29211417b1cfde8f1749905f2d735623af9214148afJeff Brown 29311417b1cfde8f1749905f2d735623af9214148afJeff Brown if (mRouteTypes != newRouteTypes || mActiveScan != newActiveScan) { 29411417b1cfde8f1749905f2d735623af9214148afJeff Brown mRouteTypes = newRouteTypes; 29511417b1cfde8f1749905f2d735623af9214148afJeff Brown mActiveScan = newActiveScan; 29611417b1cfde8f1749905f2d735623af9214148afJeff Brown updateCallback(); 29711417b1cfde8f1749905f2d735623af9214148afJeff Brown updateSystemRoutes(); 298c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown } 299c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown } 300c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown 30111417b1cfde8f1749905f2d735623af9214148afJeff Brown @Override 30211417b1cfde8f1749905f2d735623af9214148afJeff Brown public void onRouteAdded(Object routeObj) { 30311417b1cfde8f1749905f2d735623af9214148afJeff Brown if (addSystemRouteNoPublish(routeObj)) { 30411417b1cfde8f1749905f2d735623af9214148afJeff Brown publishRoutes(); 30511417b1cfde8f1749905f2d735623af9214148afJeff Brown } 30611417b1cfde8f1749905f2d735623af9214148afJeff Brown } 30711417b1cfde8f1749905f2d735623af9214148afJeff Brown 30811417b1cfde8f1749905f2d735623af9214148afJeff Brown private void updateSystemRoutes() { 30911417b1cfde8f1749905f2d735623af9214148afJeff Brown boolean changed = false; 310c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown for (Object routeObj : MediaRouterJellybean.getRoutes(mRouterObj)) { 31111417b1cfde8f1749905f2d735623af9214148afJeff Brown changed |= addSystemRouteNoPublish(routeObj); 31211417b1cfde8f1749905f2d735623af9214148afJeff Brown } 31311417b1cfde8f1749905f2d735623af9214148afJeff Brown if (changed) { 31411417b1cfde8f1749905f2d735623af9214148afJeff Brown publishRoutes(); 315c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown } 316c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown } 317c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown 31811417b1cfde8f1749905f2d735623af9214148afJeff Brown private boolean addSystemRouteNoPublish(Object routeObj) { 31911417b1cfde8f1749905f2d735623af9214148afJeff Brown if (getUserRouteRecord(routeObj) == null 32011417b1cfde8f1749905f2d735623af9214148afJeff Brown && findSystemRouteRecord(routeObj) < 0) { 321cb63b6ecac9786891514f241dec71695f09d3efbJeff Brown String id = assignRouteId(routeObj); 322cb63b6ecac9786891514f241dec71695f09d3efbJeff Brown SystemRouteRecord record = new SystemRouteRecord(routeObj, id); 323fa326a4649d9d0e8113e315f6c8251fe686abce4Jeff Brown updateSystemRouteDescriptor(record); 324fa326a4649d9d0e8113e315f6c8251fe686abce4Jeff Brown mSystemRouteRecords.add(record); 32511417b1cfde8f1749905f2d735623af9214148afJeff Brown return true; 326fa326a4649d9d0e8113e315f6c8251fe686abce4Jeff Brown } 32711417b1cfde8f1749905f2d735623af9214148afJeff Brown return false; 328c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown } 329c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown 330cb63b6ecac9786891514f241dec71695f09d3efbJeff Brown private String assignRouteId(Object routeObj) { 331cb63b6ecac9786891514f241dec71695f09d3efbJeff Brown // TODO: The framework media router should supply a unique route id that 332cb63b6ecac9786891514f241dec71695f09d3efbJeff Brown // we can use here. For now we use a hash of the route name and take care 333cb63b6ecac9786891514f241dec71695f09d3efbJeff Brown // to dedupe it. 334cb63b6ecac9786891514f241dec71695f09d3efbJeff Brown boolean isDefault = (getDefaultRoute() == routeObj); 335cb63b6ecac9786891514f241dec71695f09d3efbJeff Brown String id = isDefault ? DEFAULT_ROUTE_ID : 336cb63b6ecac9786891514f241dec71695f09d3efbJeff Brown String.format(Locale.US, "ROUTE_%08x", getRouteName(routeObj).hashCode()); 337cb63b6ecac9786891514f241dec71695f09d3efbJeff Brown if (findSystemRouteRecordByDescriptorId(id) < 0) { 338cb63b6ecac9786891514f241dec71695f09d3efbJeff Brown return id; 339cb63b6ecac9786891514f241dec71695f09d3efbJeff Brown } 340cb63b6ecac9786891514f241dec71695f09d3efbJeff Brown for (int i = 2; ; i++) { 341cb63b6ecac9786891514f241dec71695f09d3efbJeff Brown String newId = String.format(Locale.US, "%s_%d", id, i); 342cb63b6ecac9786891514f241dec71695f09d3efbJeff Brown if (findSystemRouteRecordByDescriptorId(newId) < 0) { 343cb63b6ecac9786891514f241dec71695f09d3efbJeff Brown return newId; 344cb63b6ecac9786891514f241dec71695f09d3efbJeff Brown } 345cb63b6ecac9786891514f241dec71695f09d3efbJeff Brown } 346cb63b6ecac9786891514f241dec71695f09d3efbJeff Brown } 347cb63b6ecac9786891514f241dec71695f09d3efbJeff Brown 348c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown @Override 349c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown public void onRouteRemoved(Object routeObj) { 350fa326a4649d9d0e8113e315f6c8251fe686abce4Jeff Brown if (getUserRouteRecord(routeObj) == null) { 351fa326a4649d9d0e8113e315f6c8251fe686abce4Jeff Brown int index = findSystemRouteRecord(routeObj); 352fa326a4649d9d0e8113e315f6c8251fe686abce4Jeff Brown if (index >= 0) { 353fa326a4649d9d0e8113e315f6c8251fe686abce4Jeff Brown mSystemRouteRecords.remove(index); 354fa326a4649d9d0e8113e315f6c8251fe686abce4Jeff Brown publishRoutes(); 355fa326a4649d9d0e8113e315f6c8251fe686abce4Jeff Brown } 356c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown } 357c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown } 358c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown 359c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown @Override 360c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown public void onRouteChanged(Object routeObj) { 361fa326a4649d9d0e8113e315f6c8251fe686abce4Jeff Brown if (getUserRouteRecord(routeObj) == null) { 362fa326a4649d9d0e8113e315f6c8251fe686abce4Jeff Brown int index = findSystemRouteRecord(routeObj); 363fa326a4649d9d0e8113e315f6c8251fe686abce4Jeff Brown if (index >= 0) { 364fa326a4649d9d0e8113e315f6c8251fe686abce4Jeff Brown SystemRouteRecord record = mSystemRouteRecords.get(index); 365fa326a4649d9d0e8113e315f6c8251fe686abce4Jeff Brown updateSystemRouteDescriptor(record); 366fa326a4649d9d0e8113e315f6c8251fe686abce4Jeff Brown publishRoutes(); 367fa326a4649d9d0e8113e315f6c8251fe686abce4Jeff Brown } 368c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown } 369c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown } 370c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown 371c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown @Override 372c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown public void onRouteVolumeChanged(Object routeObj) { 373fa326a4649d9d0e8113e315f6c8251fe686abce4Jeff Brown if (getUserRouteRecord(routeObj) == null) { 374fa326a4649d9d0e8113e315f6c8251fe686abce4Jeff Brown int index = findSystemRouteRecord(routeObj); 375fa326a4649d9d0e8113e315f6c8251fe686abce4Jeff Brown if (index >= 0) { 376fa326a4649d9d0e8113e315f6c8251fe686abce4Jeff Brown SystemRouteRecord record = mSystemRouteRecords.get(index); 377fa326a4649d9d0e8113e315f6c8251fe686abce4Jeff Brown int newVolume = MediaRouterJellybean.RouteInfo.getVolume(routeObj); 378fa326a4649d9d0e8113e315f6c8251fe686abce4Jeff Brown if (newVolume != record.mRouteDescriptor.getVolume()) { 37911417b1cfde8f1749905f2d735623af9214148afJeff Brown record.mRouteDescriptor = 38011417b1cfde8f1749905f2d735623af9214148afJeff Brown new MediaRouteDescriptor.Builder(record.mRouteDescriptor) 38111417b1cfde8f1749905f2d735623af9214148afJeff Brown .setVolume(newVolume) 38211417b1cfde8f1749905f2d735623af9214148afJeff Brown .build(); 383fa326a4649d9d0e8113e315f6c8251fe686abce4Jeff Brown publishRoutes(); 384fa326a4649d9d0e8113e315f6c8251fe686abce4Jeff Brown } 385c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown } 386c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown } 387c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown } 388c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown 389c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown @Override 390c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown public void onRouteSelected(int type, Object routeObj) { 39111417b1cfde8f1749905f2d735623af9214148afJeff Brown if (routeObj != MediaRouterJellybean.getSelectedRoute(mRouterObj, 39211417b1cfde8f1749905f2d735623af9214148afJeff Brown MediaRouterJellybean.ALL_ROUTE_TYPES)) { 393fa326a4649d9d0e8113e315f6c8251fe686abce4Jeff Brown // The currently selected route has already changed so this callback 394fa326a4649d9d0e8113e315f6c8251fe686abce4Jeff Brown // is stale. Drop it to prevent getting into sync loops. 395fa326a4649d9d0e8113e315f6c8251fe686abce4Jeff Brown return; 396fa326a4649d9d0e8113e315f6c8251fe686abce4Jeff Brown } 397fa326a4649d9d0e8113e315f6c8251fe686abce4Jeff Brown 398fa326a4649d9d0e8113e315f6c8251fe686abce4Jeff Brown UserRouteRecord userRouteRecord = getUserRouteRecord(routeObj); 399fa326a4649d9d0e8113e315f6c8251fe686abce4Jeff Brown if (userRouteRecord != null) { 400fa326a4649d9d0e8113e315f6c8251fe686abce4Jeff Brown userRouteRecord.mRoute.select(); 401c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown } else { 402c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown // Select the route if it already exists in the compat media router. 403c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown // If not, we will select it instead when the route is added. 404c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown int index = findSystemRouteRecord(routeObj); 405c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown if (index >= 0) { 406c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown SystemRouteRecord record = mSystemRouteRecords.get(index); 407c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown MediaRouter.RouteInfo route = mSyncCallback.getSystemRouteByDescriptorId( 408c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown record.mRouteDescriptorId); 409c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown if (route != null) { 410c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown route.select(); 411c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown } 412c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown } 413c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown } 414c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown } 415c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown 416c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown @Override 417c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown public void onRouteUnselected(int type, Object routeObj) { 418c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown // Nothing to do when a route is unselected. 419c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown // We only need to handle when a route is selected. 420c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown } 421c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown 422c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown @Override 423c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown public void onRouteGrouped(Object routeObj, Object groupObj, int index) { 424c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown // Route grouping is deprecated and no longer supported. 425c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown } 426c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown 427c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown @Override 428c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown public void onRouteUngrouped(Object routeObj, Object groupObj) { 429c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown // Route grouping is deprecated and no longer supported. 430c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown } 431c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown 432c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown @Override 433c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown public void onVolumeSetRequest(Object routeObj, int volume) { 434fa326a4649d9d0e8113e315f6c8251fe686abce4Jeff Brown UserRouteRecord record = getUserRouteRecord(routeObj); 435fa326a4649d9d0e8113e315f6c8251fe686abce4Jeff Brown if (record != null) { 436c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown record.mRoute.requestSetVolume(volume); 437c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown } 438c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown } 439c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown 440c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown @Override 441c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown public void onVolumeUpdateRequest(Object routeObj, int direction) { 442fa326a4649d9d0e8113e315f6c8251fe686abce4Jeff Brown UserRouteRecord record = getUserRouteRecord(routeObj); 443fa326a4649d9d0e8113e315f6c8251fe686abce4Jeff Brown if (record != null) { 444c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown record.mRoute.requestUpdateVolume(direction); 445c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown } 446c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown } 447c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown 448c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown @Override 449c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown public void onSyncRouteAdded(MediaRouter.RouteInfo route) { 450fa326a4649d9d0e8113e315f6c8251fe686abce4Jeff Brown if (route.getProviderInstance() != this) { 451c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown Object routeObj = MediaRouterJellybean.createUserRoute( 452c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown mRouterObj, mUserRouteCategoryObj); 453c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown UserRouteRecord record = new UserRouteRecord(route, routeObj); 454c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown MediaRouterJellybean.RouteInfo.setTag(routeObj, record); 455c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown MediaRouterJellybean.UserRouteInfo.setVolumeCallback(routeObj, mVolumeCallbackObj); 456c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown updateUserRouteProperties(record); 457c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown mUserRouteRecords.add(record); 458c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown MediaRouterJellybean.addUserRoute(mRouterObj, routeObj); 459c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown } else { 460c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown // If the newly added route is the counterpart of the currently selected 461c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown // route in the framework media router then ensure it is selected in 462c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown // the compat media router. 463c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown Object routeObj = MediaRouterJellybean.getSelectedRoute( 46411417b1cfde8f1749905f2d735623af9214148afJeff Brown mRouterObj, MediaRouterJellybean.ALL_ROUTE_TYPES); 465c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown int index = findSystemRouteRecord(routeObj); 466c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown if (index >= 0) { 467c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown SystemRouteRecord record = mSystemRouteRecords.get(index); 468c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown if (record.mRouteDescriptorId.equals(route.getDescriptorId())) { 469c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown route.select(); 470c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown } 471c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown } 472c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown } 473c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown } 474c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown 475c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown @Override 476c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown public void onSyncRouteRemoved(MediaRouter.RouteInfo route) { 477fa326a4649d9d0e8113e315f6c8251fe686abce4Jeff Brown if (route.getProviderInstance() != this) { 478c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown int index = findUserRouteRecord(route); 479c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown if (index >= 0) { 480c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown UserRouteRecord record = mUserRouteRecords.remove(index); 481c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown MediaRouterJellybean.RouteInfo.setTag(record.mRouteObj, null); 482c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown MediaRouterJellybean.UserRouteInfo.setVolumeCallback(record.mRouteObj, null); 483c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown MediaRouterJellybean.removeUserRoute(mRouterObj, record.mRouteObj); 484c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown } 485c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown } 486c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown } 487c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown 488c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown @Override 489c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown public void onSyncRouteChanged(MediaRouter.RouteInfo route) { 490fa326a4649d9d0e8113e315f6c8251fe686abce4Jeff Brown if (route.getProviderInstance() != this) { 491c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown int index = findUserRouteRecord(route); 492c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown if (index >= 0) { 493c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown UserRouteRecord record = mUserRouteRecords.get(index); 494c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown updateUserRouteProperties(record); 495c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown } 496c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown } 497c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown } 498c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown 499c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown @Override 500c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown public void onSyncRouteSelected(MediaRouter.RouteInfo route) { 501fa326a4649d9d0e8113e315f6c8251fe686abce4Jeff Brown if (!route.isSelected()) { 502fa326a4649d9d0e8113e315f6c8251fe686abce4Jeff Brown // The currently selected route has already changed so this callback 503fa326a4649d9d0e8113e315f6c8251fe686abce4Jeff Brown // is stale. Drop it to prevent getting into sync loops. 504fa326a4649d9d0e8113e315f6c8251fe686abce4Jeff Brown return; 505fa326a4649d9d0e8113e315f6c8251fe686abce4Jeff Brown } 506fa326a4649d9d0e8113e315f6c8251fe686abce4Jeff Brown 507fa326a4649d9d0e8113e315f6c8251fe686abce4Jeff Brown if (route.getProviderInstance() != this) { 508c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown int index = findUserRouteRecord(route); 509c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown if (index >= 0) { 510c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown UserRouteRecord record = mUserRouteRecords.get(index); 511c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown selectRoute(record.mRouteObj); 512c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown } 513c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown } else { 514c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown int index = findSystemRouteRecordByDescriptorId(route.getDescriptorId()); 515c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown if (index >= 0) { 516c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown SystemRouteRecord record = mSystemRouteRecords.get(index); 517c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown selectRoute(record.mRouteObj); 518c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown } 519c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown } 520c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown } 521c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown 522c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown protected void publishRoutes() { 52311417b1cfde8f1749905f2d735623af9214148afJeff Brown MediaRouteProviderDescriptor.Builder builder = 524d11aa1784335270b8d85e385f2c8be79ee6a586cJeff Brown new MediaRouteProviderDescriptor.Builder(); 525c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown int count = mSystemRouteRecords.size(); 526c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown for (int i = 0; i < count; i++) { 52711417b1cfde8f1749905f2d735623af9214148afJeff Brown builder.addRoute(mSystemRouteRecords.get(i).mRouteDescriptor); 528c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown } 529c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown 53011417b1cfde8f1749905f2d735623af9214148afJeff Brown setDescriptor(builder.build()); 531c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown } 532c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown 533c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown protected int findSystemRouteRecord(Object routeObj) { 534c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown final int count = mSystemRouteRecords.size(); 535c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown for (int i = 0; i < count; i++) { 536c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown if (mSystemRouteRecords.get(i).mRouteObj == routeObj) { 537c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown return i; 538c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown } 539c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown } 540c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown return -1; 541c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown } 542c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown 543c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown protected int findSystemRouteRecordByDescriptorId(String id) { 544c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown final int count = mSystemRouteRecords.size(); 545c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown for (int i = 0; i < count; i++) { 546c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown if (mSystemRouteRecords.get(i).mRouteDescriptorId.equals(id)) { 547c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown return i; 548c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown } 549c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown } 550c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown return -1; 551c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown } 552c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown 553c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown protected int findUserRouteRecord(MediaRouter.RouteInfo route) { 554c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown final int count = mUserRouteRecords.size(); 555c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown for (int i = 0; i < count; i++) { 556c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown if (mUserRouteRecords.get(i).mRoute == route) { 557c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown return i; 558c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown } 559c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown } 560c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown return -1; 561c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown } 562c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown 563fa326a4649d9d0e8113e315f6c8251fe686abce4Jeff Brown protected UserRouteRecord getUserRouteRecord(Object routeObj) { 564fa326a4649d9d0e8113e315f6c8251fe686abce4Jeff Brown Object tag = MediaRouterJellybean.RouteInfo.getTag(routeObj); 565fa326a4649d9d0e8113e315f6c8251fe686abce4Jeff Brown return tag instanceof UserRouteRecord ? (UserRouteRecord)tag : null; 566fa326a4649d9d0e8113e315f6c8251fe686abce4Jeff Brown } 567fa326a4649d9d0e8113e315f6c8251fe686abce4Jeff Brown 568c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown protected void updateSystemRouteDescriptor(SystemRouteRecord record) { 569fa326a4649d9d0e8113e315f6c8251fe686abce4Jeff Brown // We must always recreate the route descriptor when making any changes 570fa326a4649d9d0e8113e315f6c8251fe686abce4Jeff Brown // because they are intended to be immutable once published. 571cb63b6ecac9786891514f241dec71695f09d3efbJeff Brown MediaRouteDescriptor.Builder builder = new MediaRouteDescriptor.Builder( 572cb63b6ecac9786891514f241dec71695f09d3efbJeff Brown record.mRouteDescriptorId, getRouteName(record.mRouteObj)); 573cb63b6ecac9786891514f241dec71695f09d3efbJeff Brown onBuildSystemRouteDescriptor(record, builder); 574cb63b6ecac9786891514f241dec71695f09d3efbJeff Brown record.mRouteDescriptor = builder.build(); 575cb63b6ecac9786891514f241dec71695f09d3efbJeff Brown } 576f6a2eb6ce9fef191df8368681ed5f825dcd24340Jeff Brown 577cb63b6ecac9786891514f241dec71695f09d3efbJeff Brown protected String getRouteName(Object routeObj) { 578f6a2eb6ce9fef191df8368681ed5f825dcd24340Jeff Brown // Routes should not have null names but it may happen for badly configured 579f6a2eb6ce9fef191df8368681ed5f825dcd24340Jeff Brown // user routes. We tolerate this by using an empty name string here but 580f6a2eb6ce9fef191df8368681ed5f825dcd24340Jeff Brown // such unnamed routes will be discarded by the media router upstream 581f6a2eb6ce9fef191df8368681ed5f825dcd24340Jeff Brown // (with a log message so we can track down the problem). 582cb63b6ecac9786891514f241dec71695f09d3efbJeff Brown CharSequence name = MediaRouterJellybean.RouteInfo.getName(routeObj, getContext()); 583cb63b6ecac9786891514f241dec71695f09d3efbJeff Brown return name != null ? name.toString() : ""; 58411417b1cfde8f1749905f2d735623af9214148afJeff Brown } 585c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown 58611417b1cfde8f1749905f2d735623af9214148afJeff Brown protected void onBuildSystemRouteDescriptor(SystemRouteRecord record, 58711417b1cfde8f1749905f2d735623af9214148afJeff Brown MediaRouteDescriptor.Builder builder) { 588c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown int supportedTypes = MediaRouterJellybean.RouteInfo.getSupportedTypes( 589c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown record.mRouteObj); 590c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown if ((supportedTypes & MediaRouterJellybean.ROUTE_TYPE_LIVE_AUDIO) != 0) { 59111417b1cfde8f1749905f2d735623af9214148afJeff Brown builder.addControlFilters(LIVE_AUDIO_CONTROL_FILTERS); 59211417b1cfde8f1749905f2d735623af9214148afJeff Brown } 59311417b1cfde8f1749905f2d735623af9214148afJeff Brown if ((supportedTypes & MediaRouterJellybean.ROUTE_TYPE_LIVE_VIDEO) != 0) { 59411417b1cfde8f1749905f2d735623af9214148afJeff Brown builder.addControlFilters(LIVE_VIDEO_CONTROL_FILTERS); 595c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown } 596c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown 59711417b1cfde8f1749905f2d735623af9214148afJeff Brown builder.setPlaybackType( 598c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown MediaRouterJellybean.RouteInfo.getPlaybackType(record.mRouteObj)); 59911417b1cfde8f1749905f2d735623af9214148afJeff Brown builder.setPlaybackStream( 600c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown MediaRouterJellybean.RouteInfo.getPlaybackStream(record.mRouteObj)); 60111417b1cfde8f1749905f2d735623af9214148afJeff Brown builder.setVolume( 602c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown MediaRouterJellybean.RouteInfo.getVolume(record.mRouteObj)); 60311417b1cfde8f1749905f2d735623af9214148afJeff Brown builder.setVolumeMax( 604c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown MediaRouterJellybean.RouteInfo.getVolumeMax(record.mRouteObj)); 60511417b1cfde8f1749905f2d735623af9214148afJeff Brown builder.setVolumeHandling( 606c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown MediaRouterJellybean.RouteInfo.getVolumeHandling(record.mRouteObj)); 607c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown } 608c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown 609c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown protected void updateUserRouteProperties(UserRouteRecord record) { 610c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown MediaRouterJellybean.UserRouteInfo.setName( 611c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown record.mRouteObj, record.mRoute.getName()); 612c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown MediaRouterJellybean.UserRouteInfo.setPlaybackType( 613c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown record.mRouteObj, record.mRoute.getPlaybackType()); 614c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown MediaRouterJellybean.UserRouteInfo.setPlaybackStream( 615c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown record.mRouteObj, record.mRoute.getPlaybackStream()); 616c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown MediaRouterJellybean.UserRouteInfo.setVolume( 617c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown record.mRouteObj, record.mRoute.getVolume()); 618c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown MediaRouterJellybean.UserRouteInfo.setVolumeMax( 619c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown record.mRouteObj, record.mRoute.getVolumeMax()); 620c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown MediaRouterJellybean.UserRouteInfo.setVolumeHandling( 621c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown record.mRouteObj, record.mRoute.getVolumeHandling()); 622c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown } 623c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown 62411417b1cfde8f1749905f2d735623af9214148afJeff Brown protected void updateCallback() { 62511417b1cfde8f1749905f2d735623af9214148afJeff Brown if (mCallbackRegistered) { 62611417b1cfde8f1749905f2d735623af9214148afJeff Brown mCallbackRegistered = false; 62711417b1cfde8f1749905f2d735623af9214148afJeff Brown MediaRouterJellybean.removeCallback(mRouterObj, mCallbackObj); 62811417b1cfde8f1749905f2d735623af9214148afJeff Brown } 62911417b1cfde8f1749905f2d735623af9214148afJeff Brown 63011417b1cfde8f1749905f2d735623af9214148afJeff Brown if (mRouteTypes != 0) { 63111417b1cfde8f1749905f2d735623af9214148afJeff Brown mCallbackRegistered = true; 63211417b1cfde8f1749905f2d735623af9214148afJeff Brown MediaRouterJellybean.addCallback(mRouterObj, mRouteTypes, mCallbackObj); 63311417b1cfde8f1749905f2d735623af9214148afJeff Brown } 63411417b1cfde8f1749905f2d735623af9214148afJeff Brown } 63511417b1cfde8f1749905f2d735623af9214148afJeff Brown 636c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown protected Object createCallbackObj() { 637c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown return MediaRouterJellybean.createCallback(this); 638c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown } 639c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown 640c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown protected Object createVolumeCallbackObj() { 641c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown return MediaRouterJellybean.createVolumeCallback(this); 642c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown } 643c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown 644c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown protected void selectRoute(Object routeObj) { 64511417b1cfde8f1749905f2d735623af9214148afJeff Brown if (mSelectRouteWorkaround == null) { 64611417b1cfde8f1749905f2d735623af9214148afJeff Brown mSelectRouteWorkaround = new MediaRouterJellybean.SelectRouteWorkaround(); 647c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown } 64811417b1cfde8f1749905f2d735623af9214148afJeff Brown mSelectRouteWorkaround.selectRoute(mRouterObj, 64911417b1cfde8f1749905f2d735623af9214148afJeff Brown MediaRouterJellybean.ALL_ROUTE_TYPES, routeObj); 650c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown } 651c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown 652c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown protected Object getDefaultRoute() { 65311417b1cfde8f1749905f2d735623af9214148afJeff Brown if (mGetDefaultRouteWorkaround == null) { 65411417b1cfde8f1749905f2d735623af9214148afJeff Brown mGetDefaultRouteWorkaround = new MediaRouterJellybean.GetDefaultRouteWorkaround(); 655c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown } 65611417b1cfde8f1749905f2d735623af9214148afJeff Brown return mGetDefaultRouteWorkaround.getDefaultRoute(mRouterObj); 657c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown } 658c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown 659c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown /** 660c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown * Represents a route that is provided by the framework media router 661c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown * and published by this route provider to the support library media router. 662c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown */ 663c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown protected static final class SystemRouteRecord { 664c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown public final Object mRouteObj; 665c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown public final String mRouteDescriptorId; 66611417b1cfde8f1749905f2d735623af9214148afJeff Brown public MediaRouteDescriptor mRouteDescriptor; // assigned immediately after creation 667c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown 668cb63b6ecac9786891514f241dec71695f09d3efbJeff Brown public SystemRouteRecord(Object routeObj, String id) { 669c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown mRouteObj = routeObj; 670cb63b6ecac9786891514f241dec71695f09d3efbJeff Brown mRouteDescriptorId = id; 671c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown } 672c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown } 673c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown 674c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown /** 675c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown * Represents a route that is provided by the support library media router 676c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown * and published by this route provider to the framework media router. 677c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown */ 678c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown protected static final class UserRouteRecord { 679c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown public final MediaRouter.RouteInfo mRoute; 680c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown public final Object mRouteObj; 681c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown 682c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown public UserRouteRecord(MediaRouter.RouteInfo route, Object routeObj) { 683c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown mRoute = route; 684c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown mRouteObj = routeObj; 685c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown } 686c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown } 68719e77645aa6ce65b466d570375b36e2428f170b3Jeff Brown 68819e77645aa6ce65b466d570375b36e2428f170b3Jeff Brown protected final class SystemRouteController extends RouteController { 68919e77645aa6ce65b466d570375b36e2428f170b3Jeff Brown private final Object mRouteObj; 69019e77645aa6ce65b466d570375b36e2428f170b3Jeff Brown 69119e77645aa6ce65b466d570375b36e2428f170b3Jeff Brown public SystemRouteController(Object routeObj) { 69219e77645aa6ce65b466d570375b36e2428f170b3Jeff Brown mRouteObj = routeObj; 69319e77645aa6ce65b466d570375b36e2428f170b3Jeff Brown } 69419e77645aa6ce65b466d570375b36e2428f170b3Jeff Brown 69519e77645aa6ce65b466d570375b36e2428f170b3Jeff Brown @Override 69619e77645aa6ce65b466d570375b36e2428f170b3Jeff Brown public void onSetVolume(int volume) { 69719e77645aa6ce65b466d570375b36e2428f170b3Jeff Brown MediaRouterJellybean.RouteInfo.requestSetVolume(mRouteObj, volume); 69819e77645aa6ce65b466d570375b36e2428f170b3Jeff Brown } 69919e77645aa6ce65b466d570375b36e2428f170b3Jeff Brown 70019e77645aa6ce65b466d570375b36e2428f170b3Jeff Brown @Override 70119e77645aa6ce65b466d570375b36e2428f170b3Jeff Brown public void onUpdateVolume(int delta) { 70219e77645aa6ce65b466d570375b36e2428f170b3Jeff Brown MediaRouterJellybean.RouteInfo.requestUpdateVolume(mRouteObj, delta); 70319e77645aa6ce65b466d570375b36e2428f170b3Jeff Brown } 70419e77645aa6ce65b466d570375b36e2428f170b3Jeff Brown } 705c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown } 706c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown 707c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown /** 708c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown * Jellybean MR1 implementation. 709c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown */ 710c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown private static class JellybeanMr1Impl extends JellybeanImpl 711c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown implements MediaRouterJellybeanMr1.Callback { 71211417b1cfde8f1749905f2d735623af9214148afJeff Brown private MediaRouterJellybeanMr1.ActiveScanWorkaround mActiveScanWorkaround; 71311417b1cfde8f1749905f2d735623af9214148afJeff Brown private MediaRouterJellybeanMr1.IsConnectingWorkaround mIsConnectingWorkaround; 71411417b1cfde8f1749905f2d735623af9214148afJeff Brown 715c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown public JellybeanMr1Impl(Context context, SyncCallback syncCallback) { 716c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown super(context, syncCallback); 717c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown } 718c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown 719c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown @Override 720c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown public void onRoutePresentationDisplayChanged(Object routeObj) { 721c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown int index = findSystemRouteRecord(routeObj); 722c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown if (index >= 0) { 723c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown SystemRouteRecord record = mSystemRouteRecords.get(index); 724c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown Display newPresentationDisplay = 725c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown MediaRouterJellybeanMr1.RouteInfo.getPresentationDisplay(routeObj); 726c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown int newPresentationDisplayId = (newPresentationDisplay != null 727c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown ? newPresentationDisplay.getDisplayId() : -1); 728c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown if (newPresentationDisplayId 729c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown != record.mRouteDescriptor.getPresentationDisplayId()) { 73011417b1cfde8f1749905f2d735623af9214148afJeff Brown record.mRouteDescriptor = 73111417b1cfde8f1749905f2d735623af9214148afJeff Brown new MediaRouteDescriptor.Builder(record.mRouteDescriptor) 73211417b1cfde8f1749905f2d735623af9214148afJeff Brown .setPresentationDisplayId(newPresentationDisplayId) 73311417b1cfde8f1749905f2d735623af9214148afJeff Brown .build(); 734c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown publishRoutes(); 735c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown } 736c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown } 737c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown } 738c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown 739c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown @Override 74011417b1cfde8f1749905f2d735623af9214148afJeff Brown protected void onBuildSystemRouteDescriptor(SystemRouteRecord record, 74111417b1cfde8f1749905f2d735623af9214148afJeff Brown MediaRouteDescriptor.Builder builder) { 74211417b1cfde8f1749905f2d735623af9214148afJeff Brown super.onBuildSystemRouteDescriptor(record, builder); 743c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown 744c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown if (!MediaRouterJellybeanMr1.RouteInfo.isEnabled(record.mRouteObj)) { 74511417b1cfde8f1749905f2d735623af9214148afJeff Brown builder.setEnabled(false); 746c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown } 74711417b1cfde8f1749905f2d735623af9214148afJeff Brown 74811417b1cfde8f1749905f2d735623af9214148afJeff Brown if (isConnecting(record)) { 74911417b1cfde8f1749905f2d735623af9214148afJeff Brown builder.setConnecting(true); 75011417b1cfde8f1749905f2d735623af9214148afJeff Brown } 75111417b1cfde8f1749905f2d735623af9214148afJeff Brown 752c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown Display presentationDisplay = 753c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown MediaRouterJellybeanMr1.RouteInfo.getPresentationDisplay(record.mRouteObj); 754c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown if (presentationDisplay != null) { 75511417b1cfde8f1749905f2d735623af9214148afJeff Brown builder.setPresentationDisplayId(presentationDisplay.getDisplayId()); 756c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown } 757c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown } 758c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown 759c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown @Override 76011417b1cfde8f1749905f2d735623af9214148afJeff Brown protected void updateCallback() { 76111417b1cfde8f1749905f2d735623af9214148afJeff Brown super.updateCallback(); 76211417b1cfde8f1749905f2d735623af9214148afJeff Brown 76311417b1cfde8f1749905f2d735623af9214148afJeff Brown if (mActiveScanWorkaround == null) { 76411417b1cfde8f1749905f2d735623af9214148afJeff Brown mActiveScanWorkaround = new MediaRouterJellybeanMr1.ActiveScanWorkaround( 76511417b1cfde8f1749905f2d735623af9214148afJeff Brown getContext(), getHandler()); 76611417b1cfde8f1749905f2d735623af9214148afJeff Brown } 76711417b1cfde8f1749905f2d735623af9214148afJeff Brown mActiveScanWorkaround.setActiveScanRouteTypes(mActiveScan ? mRouteTypes : 0); 76811417b1cfde8f1749905f2d735623af9214148afJeff Brown } 76911417b1cfde8f1749905f2d735623af9214148afJeff Brown 77011417b1cfde8f1749905f2d735623af9214148afJeff Brown @Override 771c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown protected Object createCallbackObj() { 772c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown return MediaRouterJellybeanMr1.createCallback(this); 773c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown } 77411417b1cfde8f1749905f2d735623af9214148afJeff Brown 77511417b1cfde8f1749905f2d735623af9214148afJeff Brown protected boolean isConnecting(SystemRouteRecord record) { 77611417b1cfde8f1749905f2d735623af9214148afJeff Brown if (mIsConnectingWorkaround == null) { 77711417b1cfde8f1749905f2d735623af9214148afJeff Brown mIsConnectingWorkaround = new MediaRouterJellybeanMr1.IsConnectingWorkaround(); 77811417b1cfde8f1749905f2d735623af9214148afJeff Brown } 77911417b1cfde8f1749905f2d735623af9214148afJeff Brown return mIsConnectingWorkaround.isConnecting(record.mRouteObj); 78011417b1cfde8f1749905f2d735623af9214148afJeff Brown } 781c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown } 782c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown 783c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown /** 784c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown * Jellybean MR2 implementation. 785c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown */ 786c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown private static class JellybeanMr2Impl extends JellybeanMr1Impl { 787c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown public JellybeanMr2Impl(Context context, SyncCallback syncCallback) { 788c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown super(context, syncCallback); 789c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown } 790c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown 791c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown @Override 792d63957d28aaabcec588b8cde12eac16414783aebJeff Brown protected void onBuildSystemRouteDescriptor(SystemRouteRecord record, 793d63957d28aaabcec588b8cde12eac16414783aebJeff Brown MediaRouteDescriptor.Builder builder) { 794d63957d28aaabcec588b8cde12eac16414783aebJeff Brown super.onBuildSystemRouteDescriptor(record, builder); 795d63957d28aaabcec588b8cde12eac16414783aebJeff Brown 796d63957d28aaabcec588b8cde12eac16414783aebJeff Brown CharSequence description = 797d63957d28aaabcec588b8cde12eac16414783aebJeff Brown MediaRouterJellybeanMr2.RouteInfo.getDescription(record.mRouteObj); 798d63957d28aaabcec588b8cde12eac16414783aebJeff Brown if (description != null) { 799d63957d28aaabcec588b8cde12eac16414783aebJeff Brown builder.setDescription(description.toString()); 800d63957d28aaabcec588b8cde12eac16414783aebJeff Brown } 801d63957d28aaabcec588b8cde12eac16414783aebJeff Brown } 802d63957d28aaabcec588b8cde12eac16414783aebJeff Brown 803d63957d28aaabcec588b8cde12eac16414783aebJeff Brown @Override 804c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown protected void selectRoute(Object routeObj) { 80511417b1cfde8f1749905f2d735623af9214148afJeff Brown MediaRouterJellybean.selectRoute(mRouterObj, 80611417b1cfde8f1749905f2d735623af9214148afJeff Brown MediaRouterJellybean.ALL_ROUTE_TYPES, routeObj); 807c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown } 808c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown 809c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown @Override 810c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown protected Object getDefaultRoute() { 811c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown return MediaRouterJellybeanMr2.getDefaultRoute(mRouterObj); 812c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown } 81311417b1cfde8f1749905f2d735623af9214148afJeff Brown 81411417b1cfde8f1749905f2d735623af9214148afJeff Brown @Override 815d63957d28aaabcec588b8cde12eac16414783aebJeff Brown protected void updateUserRouteProperties(UserRouteRecord record) { 816d63957d28aaabcec588b8cde12eac16414783aebJeff Brown super.updateUserRouteProperties(record); 817d63957d28aaabcec588b8cde12eac16414783aebJeff Brown 818d63957d28aaabcec588b8cde12eac16414783aebJeff Brown MediaRouterJellybeanMr2.UserRouteInfo.setDescription( 819d63957d28aaabcec588b8cde12eac16414783aebJeff Brown record.mRouteObj, record.mRoute.getDescription()); 820d63957d28aaabcec588b8cde12eac16414783aebJeff Brown } 821d63957d28aaabcec588b8cde12eac16414783aebJeff Brown 822d63957d28aaabcec588b8cde12eac16414783aebJeff Brown @Override 82311417b1cfde8f1749905f2d735623af9214148afJeff Brown protected void updateCallback() { 82411417b1cfde8f1749905f2d735623af9214148afJeff Brown if (mCallbackRegistered) { 82511417b1cfde8f1749905f2d735623af9214148afJeff Brown MediaRouterJellybean.removeCallback(mRouterObj, mCallbackObj); 82611417b1cfde8f1749905f2d735623af9214148afJeff Brown } 82711417b1cfde8f1749905f2d735623af9214148afJeff Brown 82811417b1cfde8f1749905f2d735623af9214148afJeff Brown mCallbackRegistered = true; 82911417b1cfde8f1749905f2d735623af9214148afJeff Brown MediaRouterJellybeanMr2.addCallback(mRouterObj, mRouteTypes, mCallbackObj, 83011417b1cfde8f1749905f2d735623af9214148afJeff Brown MediaRouter.CALLBACK_FLAG_UNFILTERED_EVENTS 831f03da4a9e6cc02251c2f804eb6f25da61821d6a7Jeff Brown | (mActiveScan ? MediaRouter.CALLBACK_FLAG_PERFORM_ACTIVE_SCAN : 0)); 83211417b1cfde8f1749905f2d735623af9214148afJeff Brown } 83311417b1cfde8f1749905f2d735623af9214148afJeff Brown 83411417b1cfde8f1749905f2d735623af9214148afJeff Brown @Override 83511417b1cfde8f1749905f2d735623af9214148afJeff Brown protected boolean isConnecting(SystemRouteRecord record) { 83611417b1cfde8f1749905f2d735623af9214148afJeff Brown return MediaRouterJellybeanMr2.RouteInfo.isConnecting(record.mRouteObj); 83711417b1cfde8f1749905f2d735623af9214148afJeff Brown } 838c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown } 839c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown} 840