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;
27c69882cb9b130902c1554ef5d3e3b06d776cd796Alan Viveretteimport android.support.annotation.RequiresApi;
28b507e525a61ed761eecfc2eaaf19af7e8db5dca5Jeff Brownimport android.support.v7.mediarouter.R;
29c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brownimport android.view.Display;
30c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown
31c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brownimport java.util.ArrayList;
3211417b1cfde8f1749905f2d735623af9214148afJeff Brownimport java.util.List;
33cb63b6ecac9786891514f241dec71695f09d3efbJeff Brownimport java.util.Locale;
34c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown
35c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown/**
36c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown * Provides routes for built-in system destinations such as the local display
37c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown * and speaker.  On Jellybean and newer platform releases, queries the framework
38c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown * MediaRouter for framework-provided routes and registers non-framework-provided
39c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown * routes as user routes.
40c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown */
41c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brownabstract class SystemMediaRouteProvider extends MediaRouteProvider {
42c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown    private static final String TAG = "SystemMediaRouteProvider";
43c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown
44fa326a4649d9d0e8113e315f6c8251fe686abce4Jeff Brown    public static final String PACKAGE_NAME = "android";
45c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown    public static final String DEFAULT_ROUTE_ID = "DEFAULT_ROUTE";
46c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown
47c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown    protected SystemMediaRouteProvider(Context context) {
48adf0f4a217e14894af07dfa9f46cad7d98b8a7f4Jeff Brown        super(context, new ProviderMetadata(new ComponentName(PACKAGE_NAME,
49adf0f4a217e14894af07dfa9f46cad7d98b8a7f4Jeff Brown                SystemMediaRouteProvider.class.getName())));
50c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown    }
51c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown
52c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown    public static SystemMediaRouteProvider obtain(Context context, SyncCallback syncCallback) {
5396d4428ba99ad068679fca129e179e8187770d75Insun Kang        if (Build.VERSION.SDK_INT >= 24) {
5496d4428ba99ad068679fca129e179e8187770d75Insun Kang            return new Api24Impl(context, syncCallback);
5596d4428ba99ad068679fca129e179e8187770d75Insun Kang        }
56c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown        if (Build.VERSION.SDK_INT >= 18) {
57c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown            return new JellybeanMr2Impl(context, syncCallback);
58c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown        }
59c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown        if (Build.VERSION.SDK_INT >= 17) {
60c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown            return new JellybeanMr1Impl(context, syncCallback);
61c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown        }
62c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown        if (Build.VERSION.SDK_INT >= 16) {
63c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown            return new JellybeanImpl(context, syncCallback);
64c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown        }
65c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown        return new LegacyImpl(context);
66c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown    }
67c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown
68c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown    /**
69c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown     * Called by the media router when a route is added to synchronize state with
70c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown     * the framework media router.
71c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown     */
72c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown    public void onSyncRouteAdded(MediaRouter.RouteInfo route) {
73c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown    }
74c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown
75c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown    /**
76c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown     * Called by the media router when a route is removed to synchronize state with
77c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown     * the framework media router.
78c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown     */
79c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown    public void onSyncRouteRemoved(MediaRouter.RouteInfo route) {
80c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown    }
81c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown
82c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown    /**
83c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown     * Called by the media router when a route is changed to synchronize state with
84c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown     * the framework media router.
85c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown     */
86c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown    public void onSyncRouteChanged(MediaRouter.RouteInfo route) {
87c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown    }
88c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown
89c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown    /**
90c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown     * Called by the media router when a route is selected to synchronize state with
91c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown     * the framework media router.
92c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown     */
93c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown    public void onSyncRouteSelected(MediaRouter.RouteInfo route) {
94c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown    }
95c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown
96c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown    /**
97c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown     * Callbacks into the media router to synchronize state with the framework media router.
98c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown     */
99c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown    public interface SyncCallback {
100ee4926b3033899028886c7407f75bcf55e708192Conrad Chen        void onSystemRouteSelectedByDescriptorId(String id);
101c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown    }
102c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown
103c0b44f78f248e321d913db6077bf9a55976d7319Sungsoo    protected Object getDefaultRoute() {
104c0b44f78f248e321d913db6077bf9a55976d7319Sungsoo        return null;
105c0b44f78f248e321d913db6077bf9a55976d7319Sungsoo    }
106c0b44f78f248e321d913db6077bf9a55976d7319Sungsoo
1076adc8ba4e18a8b97b0c6719975d7259f7a2500feSungsoo    protected Object getSystemRoute(MediaRouter.RouteInfo route) {
1086adc8ba4e18a8b97b0c6719975d7259f7a2500feSungsoo        return null;
1096adc8ba4e18a8b97b0c6719975d7259f7a2500feSungsoo    }
1106adc8ba4e18a8b97b0c6719975d7259f7a2500feSungsoo
111c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown    /**
112c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown     * Legacy implementation for platform versions prior to Jellybean.
113c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown     */
114c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown    static class LegacyImpl extends SystemMediaRouteProvider {
115e2c6a94b6e4aab502f9b88dd3ff664bd90b25839Aurimas Liutikas        static final int PLAYBACK_STREAM = AudioManager.STREAM_MUSIC;
116c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown
11711417b1cfde8f1749905f2d735623af9214148afJeff Brown        private static final ArrayList<IntentFilter> CONTROL_FILTERS;
118fa326a4649d9d0e8113e315f6c8251fe686abce4Jeff Brown        static {
11911417b1cfde8f1749905f2d735623af9214148afJeff Brown            IntentFilter f = new IntentFilter();
12011417b1cfde8f1749905f2d735623af9214148afJeff Brown            f.addCategory(MediaControlIntent.CATEGORY_LIVE_AUDIO);
12111417b1cfde8f1749905f2d735623af9214148afJeff Brown            f.addCategory(MediaControlIntent.CATEGORY_LIVE_VIDEO);
12211417b1cfde8f1749905f2d735623af9214148afJeff Brown
12311417b1cfde8f1749905f2d735623af9214148afJeff Brown            CONTROL_FILTERS = new ArrayList<IntentFilter>();
12411417b1cfde8f1749905f2d735623af9214148afJeff Brown            CONTROL_FILTERS.add(f);
125fa326a4649d9d0e8113e315f6c8251fe686abce4Jeff Brown        }
126fa326a4649d9d0e8113e315f6c8251fe686abce4Jeff Brown
127e2c6a94b6e4aab502f9b88dd3ff664bd90b25839Aurimas Liutikas        final AudioManager mAudioManager;
128c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown        private final VolumeChangeReceiver mVolumeChangeReceiver;
129e2c6a94b6e4aab502f9b88dd3ff664bd90b25839Aurimas Liutikas        int mLastReportedVolume = -1;
130c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown
131c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown        public LegacyImpl(Context context) {
132c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown            super(context);
133c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown            mAudioManager = (AudioManager)context.getSystemService(Context.AUDIO_SERVICE);
134c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown            mVolumeChangeReceiver = new VolumeChangeReceiver();
135c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown
136c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown            context.registerReceiver(mVolumeChangeReceiver,
137c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown                    new IntentFilter(VolumeChangeReceiver.VOLUME_CHANGED_ACTION));
138c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown            publishRoutes();
139c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown        }
140c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown
141e2c6a94b6e4aab502f9b88dd3ff664bd90b25839Aurimas Liutikas        void publishRoutes() {
142b507e525a61ed761eecfc2eaaf19af7e8db5dca5Jeff Brown            Resources r = getContext().getResources();
14311417b1cfde8f1749905f2d735623af9214148afJeff Brown            int maxVolume = mAudioManager.getStreamMaxVolume(PLAYBACK_STREAM);
144c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown            mLastReportedVolume = mAudioManager.getStreamVolume(PLAYBACK_STREAM);
14511417b1cfde8f1749905f2d735623af9214148afJeff Brown            MediaRouteDescriptor defaultRoute = new MediaRouteDescriptor.Builder(
1463d4c9459ed77f732dd3ba602713af6ebf9280c8cJeff Brown                    DEFAULT_ROUTE_ID, r.getString(R.string.mr_system_route_name))
14711417b1cfde8f1749905f2d735623af9214148afJeff Brown                    .addControlFilters(CONTROL_FILTERS)
14811417b1cfde8f1749905f2d735623af9214148afJeff Brown                    .setPlaybackStream(PLAYBACK_STREAM)
14911417b1cfde8f1749905f2d735623af9214148afJeff Brown                    .setPlaybackType(MediaRouter.RouteInfo.PLAYBACK_TYPE_LOCAL)
15011417b1cfde8f1749905f2d735623af9214148afJeff Brown                    .setVolumeHandling(MediaRouter.RouteInfo.PLAYBACK_VOLUME_VARIABLE)
15111417b1cfde8f1749905f2d735623af9214148afJeff Brown                    .setVolumeMax(maxVolume)
15211417b1cfde8f1749905f2d735623af9214148afJeff Brown                    .setVolume(mLastReportedVolume)
15311417b1cfde8f1749905f2d735623af9214148afJeff Brown                    .build();
15411417b1cfde8f1749905f2d735623af9214148afJeff Brown
15511417b1cfde8f1749905f2d735623af9214148afJeff Brown            MediaRouteProviderDescriptor providerDescriptor =
15611417b1cfde8f1749905f2d735623af9214148afJeff Brown                    new MediaRouteProviderDescriptor.Builder()
15711417b1cfde8f1749905f2d735623af9214148afJeff Brown                    .addRoute(defaultRoute)
15811417b1cfde8f1749905f2d735623af9214148afJeff Brown                    .build();
159c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown            setDescriptor(providerDescriptor);
160c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown        }
161c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown
162c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown        @Override
163c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown        public RouteController onCreateRouteController(String routeId) {
164c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown            if (routeId.equals(DEFAULT_ROUTE_ID)) {
165c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown                return new DefaultRouteController();
166c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown            }
167c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown            return null;
168c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown        }
169c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown
170c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown        final class DefaultRouteController extends RouteController {
171c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown            @Override
172129abf73ce9be1bc172b945263c7975ad1a3006fJeff Brown            public void onSetVolume(int volume) {
173c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown                mAudioManager.setStreamVolume(PLAYBACK_STREAM, volume, 0);
174c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown                publishRoutes();
175c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown            }
176c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown
177c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown            @Override
178129abf73ce9be1bc172b945263c7975ad1a3006fJeff Brown            public void onUpdateVolume(int delta) {
179c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown                int volume = mAudioManager.getStreamVolume(PLAYBACK_STREAM);
180c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown                int maxVolume = mAudioManager.getStreamMaxVolume(PLAYBACK_STREAM);
181c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown                int newVolume = Math.min(maxVolume, Math.max(0, volume + delta));
182c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown                if (newVolume != volume) {
183c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown                    mAudioManager.setStreamVolume(PLAYBACK_STREAM, volume, 0);
184c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown                }
185c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown                publishRoutes();
186c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown            }
187c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown        }
188c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown
189c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown        final class VolumeChangeReceiver extends BroadcastReceiver {
190c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown            // These constants come from AudioManager.
191c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown            public static final String VOLUME_CHANGED_ACTION =
192c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown                    "android.media.VOLUME_CHANGED_ACTION";
193c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown            public static final String EXTRA_VOLUME_STREAM_TYPE =
194c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown                    "android.media.EXTRA_VOLUME_STREAM_TYPE";
195c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown            public static final String EXTRA_VOLUME_STREAM_VALUE =
196c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown                    "android.media.EXTRA_VOLUME_STREAM_VALUE";
197c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown
198c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown            @Override
199c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown            public void onReceive(Context context, Intent intent) {
200c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown                if (intent.getAction().equals(VOLUME_CHANGED_ACTION)) {
201c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown                    final int streamType = intent.getIntExtra(EXTRA_VOLUME_STREAM_TYPE, -1);
202c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown                    if (streamType == PLAYBACK_STREAM) {
203c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown                        final int volume = intent.getIntExtra(EXTRA_VOLUME_STREAM_VALUE, -1);
204c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown                        if (volume >= 0 && volume != mLastReportedVolume) {
205c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown                            publishRoutes();
206c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown                        }
207c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown                    }
208c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown                }
209c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown            }
210c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown        }
211c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown    }
212c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown
213c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown    /**
214c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown     * Jellybean implementation.
215c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown     */
216c69882cb9b130902c1554ef5d3e3b06d776cd796Alan Viverette    @RequiresApi(16)
217c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown    static class JellybeanImpl extends SystemMediaRouteProvider
218c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown            implements MediaRouterJellybean.Callback, MediaRouterJellybean.VolumeCallback {
21911417b1cfde8f1749905f2d735623af9214148afJeff Brown        private static final ArrayList<IntentFilter> LIVE_AUDIO_CONTROL_FILTERS;
220fa326a4649d9d0e8113e315f6c8251fe686abce4Jeff Brown        static {
22111417b1cfde8f1749905f2d735623af9214148afJeff Brown            IntentFilter f = new IntentFilter();
22211417b1cfde8f1749905f2d735623af9214148afJeff Brown            f.addCategory(MediaControlIntent.CATEGORY_LIVE_AUDIO);
223fa326a4649d9d0e8113e315f6c8251fe686abce4Jeff Brown
22411417b1cfde8f1749905f2d735623af9214148afJeff Brown            LIVE_AUDIO_CONTROL_FILTERS = new ArrayList<IntentFilter>();
22511417b1cfde8f1749905f2d735623af9214148afJeff Brown            LIVE_AUDIO_CONTROL_FILTERS.add(f);
226fa326a4649d9d0e8113e315f6c8251fe686abce4Jeff Brown        }
227fa326a4649d9d0e8113e315f6c8251fe686abce4Jeff Brown
22811417b1cfde8f1749905f2d735623af9214148afJeff Brown        private static final ArrayList<IntentFilter> LIVE_VIDEO_CONTROL_FILTERS;
229fa326a4649d9d0e8113e315f6c8251fe686abce4Jeff Brown        static {
23011417b1cfde8f1749905f2d735623af9214148afJeff Brown            IntentFilter f = new IntentFilter();
23111417b1cfde8f1749905f2d735623af9214148afJeff Brown            f.addCategory(MediaControlIntent.CATEGORY_LIVE_VIDEO);
23211417b1cfde8f1749905f2d735623af9214148afJeff Brown
23311417b1cfde8f1749905f2d735623af9214148afJeff Brown            LIVE_VIDEO_CONTROL_FILTERS = new ArrayList<IntentFilter>();
23411417b1cfde8f1749905f2d735623af9214148afJeff Brown            LIVE_VIDEO_CONTROL_FILTERS.add(f);
235fa326a4649d9d0e8113e315f6c8251fe686abce4Jeff Brown        }
236fa326a4649d9d0e8113e315f6c8251fe686abce4Jeff Brown
237c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown        private final SyncCallback mSyncCallback;
238c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown
239c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown        protected final Object mRouterObj;
240c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown        protected final Object mCallbackObj;
241c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown        protected final Object mVolumeCallbackObj;
242c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown        protected final Object mUserRouteCategoryObj;
24311417b1cfde8f1749905f2d735623af9214148afJeff Brown        protected int mRouteTypes;
24411417b1cfde8f1749905f2d735623af9214148afJeff Brown        protected boolean mActiveScan;
24511417b1cfde8f1749905f2d735623af9214148afJeff Brown        protected boolean mCallbackRegistered;
246c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown
247c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown        // Maintains an association from framework routes to support library routes.
248c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown        // Note that we cannot use the tag field for this because an application may
249c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown        // have published its own user routes to the framework media router and already
250c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown        // used the tag for its own purposes.
251c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown        protected final ArrayList<SystemRouteRecord> mSystemRouteRecords =
252c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown                new ArrayList<SystemRouteRecord>();
253c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown
254c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown        // Maintains an association from support library routes to framework routes.
255c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown        protected final ArrayList<UserRouteRecord> mUserRouteRecords =
256c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown                new ArrayList<UserRouteRecord>();
257c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown
25811417b1cfde8f1749905f2d735623af9214148afJeff Brown        private MediaRouterJellybean.SelectRouteWorkaround mSelectRouteWorkaround;
25911417b1cfde8f1749905f2d735623af9214148afJeff Brown        private MediaRouterJellybean.GetDefaultRouteWorkaround mGetDefaultRouteWorkaround;
26011417b1cfde8f1749905f2d735623af9214148afJeff Brown
261c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown        public JellybeanImpl(Context context, SyncCallback syncCallback) {
262c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown            super(context);
263c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown            mSyncCallback = syncCallback;
264c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown            mRouterObj = MediaRouterJellybean.getMediaRouter(context);
265c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown            mCallbackObj = createCallbackObj();
266c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown            mVolumeCallbackObj = createVolumeCallbackObj();
267c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown
268b507e525a61ed761eecfc2eaaf19af7e8db5dca5Jeff Brown            Resources r = context.getResources();
269c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown            mUserRouteCategoryObj = MediaRouterJellybean.createRouteCategory(
2703d4c9459ed77f732dd3ba602713af6ebf9280c8cJeff Brown                    mRouterObj, r.getString(R.string.mr_user_route_category_name), false);
271c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown
27211417b1cfde8f1749905f2d735623af9214148afJeff Brown            updateSystemRoutes();
273c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown        }
274c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown
275c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown        @Override
27619e77645aa6ce65b466d570375b36e2428f170b3Jeff Brown        public RouteController onCreateRouteController(String routeId) {
27719e77645aa6ce65b466d570375b36e2428f170b3Jeff Brown            int index = findSystemRouteRecordByDescriptorId(routeId);
27819e77645aa6ce65b466d570375b36e2428f170b3Jeff Brown            if (index >= 0) {
27919e77645aa6ce65b466d570375b36e2428f170b3Jeff Brown                SystemRouteRecord record = mSystemRouteRecords.get(index);
28019e77645aa6ce65b466d570375b36e2428f170b3Jeff Brown                return new SystemRouteController(record.mRouteObj);
28119e77645aa6ce65b466d570375b36e2428f170b3Jeff Brown            }
28219e77645aa6ce65b466d570375b36e2428f170b3Jeff Brown            return null;
28319e77645aa6ce65b466d570375b36e2428f170b3Jeff Brown        }
28419e77645aa6ce65b466d570375b36e2428f170b3Jeff Brown
28519e77645aa6ce65b466d570375b36e2428f170b3Jeff Brown        @Override
28611417b1cfde8f1749905f2d735623af9214148afJeff Brown        public void onDiscoveryRequestChanged(MediaRouteDiscoveryRequest request) {
28711417b1cfde8f1749905f2d735623af9214148afJeff Brown            int newRouteTypes = 0;
28811417b1cfde8f1749905f2d735623af9214148afJeff Brown            boolean newActiveScan = false;
28911417b1cfde8f1749905f2d735623af9214148afJeff Brown            if (request != null) {
29011417b1cfde8f1749905f2d735623af9214148afJeff Brown                final MediaRouteSelector selector = request.getSelector();
29111417b1cfde8f1749905f2d735623af9214148afJeff Brown                final List<String> categories = selector.getControlCategories();
29211417b1cfde8f1749905f2d735623af9214148afJeff Brown                final int count = categories.size();
29311417b1cfde8f1749905f2d735623af9214148afJeff Brown                for (int i = 0; i < count; i++) {
29411417b1cfde8f1749905f2d735623af9214148afJeff Brown                    String category = categories.get(i);
29511417b1cfde8f1749905f2d735623af9214148afJeff Brown                    if (category.equals(MediaControlIntent.CATEGORY_LIVE_AUDIO)) {
29611417b1cfde8f1749905f2d735623af9214148afJeff Brown                        newRouteTypes |= MediaRouterJellybean.ROUTE_TYPE_LIVE_AUDIO;
29711417b1cfde8f1749905f2d735623af9214148afJeff Brown                    } else if (category.equals(MediaControlIntent.CATEGORY_LIVE_VIDEO)) {
29811417b1cfde8f1749905f2d735623af9214148afJeff Brown                        newRouteTypes |= MediaRouterJellybean.ROUTE_TYPE_LIVE_VIDEO;
29911417b1cfde8f1749905f2d735623af9214148afJeff Brown                    } else {
30011417b1cfde8f1749905f2d735623af9214148afJeff Brown                        newRouteTypes |= MediaRouterJellybean.ROUTE_TYPE_USER;
30111417b1cfde8f1749905f2d735623af9214148afJeff Brown                    }
302fa326a4649d9d0e8113e315f6c8251fe686abce4Jeff Brown                }
30311417b1cfde8f1749905f2d735623af9214148afJeff Brown                newActiveScan = request.isActiveScan();
30411417b1cfde8f1749905f2d735623af9214148afJeff Brown            }
30511417b1cfde8f1749905f2d735623af9214148afJeff Brown
30611417b1cfde8f1749905f2d735623af9214148afJeff Brown            if (mRouteTypes != newRouteTypes || mActiveScan != newActiveScan) {
30711417b1cfde8f1749905f2d735623af9214148afJeff Brown                mRouteTypes = newRouteTypes;
30811417b1cfde8f1749905f2d735623af9214148afJeff Brown                mActiveScan = newActiveScan;
30911417b1cfde8f1749905f2d735623af9214148afJeff Brown                updateSystemRoutes();
310c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown            }
311c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown        }
312c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown
31311417b1cfde8f1749905f2d735623af9214148afJeff Brown        @Override
31411417b1cfde8f1749905f2d735623af9214148afJeff Brown        public void onRouteAdded(Object routeObj) {
31511417b1cfde8f1749905f2d735623af9214148afJeff Brown            if (addSystemRouteNoPublish(routeObj)) {
31611417b1cfde8f1749905f2d735623af9214148afJeff Brown                publishRoutes();
31711417b1cfde8f1749905f2d735623af9214148afJeff Brown            }
31811417b1cfde8f1749905f2d735623af9214148afJeff Brown        }
31911417b1cfde8f1749905f2d735623af9214148afJeff Brown
32011417b1cfde8f1749905f2d735623af9214148afJeff Brown        private void updateSystemRoutes() {
321d38d8790c68d4d82a278d42e7f2a1382d005c637Sungsoo            updateCallback();
32211417b1cfde8f1749905f2d735623af9214148afJeff Brown            boolean changed = false;
323c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown            for (Object routeObj : MediaRouterJellybean.getRoutes(mRouterObj)) {
32411417b1cfde8f1749905f2d735623af9214148afJeff Brown                changed |= addSystemRouteNoPublish(routeObj);
32511417b1cfde8f1749905f2d735623af9214148afJeff Brown            }
32611417b1cfde8f1749905f2d735623af9214148afJeff Brown            if (changed) {
32711417b1cfde8f1749905f2d735623af9214148afJeff Brown                publishRoutes();
328c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown            }
329c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown        }
330c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown
33111417b1cfde8f1749905f2d735623af9214148afJeff Brown        private boolean addSystemRouteNoPublish(Object routeObj) {
33211417b1cfde8f1749905f2d735623af9214148afJeff Brown            if (getUserRouteRecord(routeObj) == null
33311417b1cfde8f1749905f2d735623af9214148afJeff Brown                    && findSystemRouteRecord(routeObj) < 0) {
334cb63b6ecac9786891514f241dec71695f09d3efbJeff Brown                String id = assignRouteId(routeObj);
335cb63b6ecac9786891514f241dec71695f09d3efbJeff Brown                SystemRouteRecord record = new SystemRouteRecord(routeObj, id);
336fa326a4649d9d0e8113e315f6c8251fe686abce4Jeff Brown                updateSystemRouteDescriptor(record);
337fa326a4649d9d0e8113e315f6c8251fe686abce4Jeff Brown                mSystemRouteRecords.add(record);
33811417b1cfde8f1749905f2d735623af9214148afJeff Brown                return true;
339fa326a4649d9d0e8113e315f6c8251fe686abce4Jeff Brown            }
34011417b1cfde8f1749905f2d735623af9214148afJeff Brown            return false;
341c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown        }
342c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown
343cb63b6ecac9786891514f241dec71695f09d3efbJeff Brown        private String assignRouteId(Object routeObj) {
344cb63b6ecac9786891514f241dec71695f09d3efbJeff Brown            // TODO: The framework media router should supply a unique route id that
345cb63b6ecac9786891514f241dec71695f09d3efbJeff Brown            // we can use here.  For now we use a hash of the route name and take care
346cb63b6ecac9786891514f241dec71695f09d3efbJeff Brown            // to dedupe it.
347cb63b6ecac9786891514f241dec71695f09d3efbJeff Brown            boolean isDefault = (getDefaultRoute() == routeObj);
348cb63b6ecac9786891514f241dec71695f09d3efbJeff Brown            String id = isDefault ? DEFAULT_ROUTE_ID :
349cb63b6ecac9786891514f241dec71695f09d3efbJeff Brown                    String.format(Locale.US, "ROUTE_%08x", getRouteName(routeObj).hashCode());
350cb63b6ecac9786891514f241dec71695f09d3efbJeff Brown            if (findSystemRouteRecordByDescriptorId(id) < 0) {
351cb63b6ecac9786891514f241dec71695f09d3efbJeff Brown                return id;
352cb63b6ecac9786891514f241dec71695f09d3efbJeff Brown            }
353cb63b6ecac9786891514f241dec71695f09d3efbJeff Brown            for (int i = 2; ; i++) {
354cb63b6ecac9786891514f241dec71695f09d3efbJeff Brown                String newId = String.format(Locale.US, "%s_%d", id, i);
355cb63b6ecac9786891514f241dec71695f09d3efbJeff Brown                if (findSystemRouteRecordByDescriptorId(newId) < 0) {
356cb63b6ecac9786891514f241dec71695f09d3efbJeff Brown                    return newId;
357cb63b6ecac9786891514f241dec71695f09d3efbJeff Brown                }
358cb63b6ecac9786891514f241dec71695f09d3efbJeff Brown            }
359cb63b6ecac9786891514f241dec71695f09d3efbJeff Brown        }
360cb63b6ecac9786891514f241dec71695f09d3efbJeff Brown
361c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown        @Override
362c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown        public void onRouteRemoved(Object routeObj) {
363fa326a4649d9d0e8113e315f6c8251fe686abce4Jeff Brown            if (getUserRouteRecord(routeObj) == null) {
364fa326a4649d9d0e8113e315f6c8251fe686abce4Jeff Brown                int index = findSystemRouteRecord(routeObj);
365fa326a4649d9d0e8113e315f6c8251fe686abce4Jeff Brown                if (index >= 0) {
366fa326a4649d9d0e8113e315f6c8251fe686abce4Jeff Brown                    mSystemRouteRecords.remove(index);
367fa326a4649d9d0e8113e315f6c8251fe686abce4Jeff Brown                    publishRoutes();
368fa326a4649d9d0e8113e315f6c8251fe686abce4Jeff Brown                }
369c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown            }
370c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown        }
371c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown
372c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown        @Override
373c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown        public void onRouteChanged(Object routeObj) {
374fa326a4649d9d0e8113e315f6c8251fe686abce4Jeff Brown            if (getUserRouteRecord(routeObj) == null) {
375fa326a4649d9d0e8113e315f6c8251fe686abce4Jeff Brown                int index = findSystemRouteRecord(routeObj);
376fa326a4649d9d0e8113e315f6c8251fe686abce4Jeff Brown                if (index >= 0) {
377fa326a4649d9d0e8113e315f6c8251fe686abce4Jeff Brown                    SystemRouteRecord record = mSystemRouteRecords.get(index);
378fa326a4649d9d0e8113e315f6c8251fe686abce4Jeff Brown                    updateSystemRouteDescriptor(record);
379fa326a4649d9d0e8113e315f6c8251fe686abce4Jeff Brown                    publishRoutes();
380fa326a4649d9d0e8113e315f6c8251fe686abce4Jeff Brown                }
381c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown            }
382c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown        }
383c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown
384c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown        @Override
385c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown        public void onRouteVolumeChanged(Object routeObj) {
386fa326a4649d9d0e8113e315f6c8251fe686abce4Jeff Brown            if (getUserRouteRecord(routeObj) == null) {
387fa326a4649d9d0e8113e315f6c8251fe686abce4Jeff Brown                int index = findSystemRouteRecord(routeObj);
388fa326a4649d9d0e8113e315f6c8251fe686abce4Jeff Brown                if (index >= 0) {
389fa326a4649d9d0e8113e315f6c8251fe686abce4Jeff Brown                    SystemRouteRecord record = mSystemRouteRecords.get(index);
390fa326a4649d9d0e8113e315f6c8251fe686abce4Jeff Brown                    int newVolume = MediaRouterJellybean.RouteInfo.getVolume(routeObj);
391fa326a4649d9d0e8113e315f6c8251fe686abce4Jeff Brown                    if (newVolume != record.mRouteDescriptor.getVolume()) {
39211417b1cfde8f1749905f2d735623af9214148afJeff Brown                        record.mRouteDescriptor =
39311417b1cfde8f1749905f2d735623af9214148afJeff Brown                                new MediaRouteDescriptor.Builder(record.mRouteDescriptor)
39411417b1cfde8f1749905f2d735623af9214148afJeff Brown                                .setVolume(newVolume)
39511417b1cfde8f1749905f2d735623af9214148afJeff Brown                                .build();
396fa326a4649d9d0e8113e315f6c8251fe686abce4Jeff Brown                        publishRoutes();
397fa326a4649d9d0e8113e315f6c8251fe686abce4Jeff Brown                    }
398c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown                }
399c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown            }
400c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown        }
401c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown
402c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown        @Override
403c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown        public void onRouteSelected(int type, Object routeObj) {
40411417b1cfde8f1749905f2d735623af9214148afJeff Brown            if (routeObj != MediaRouterJellybean.getSelectedRoute(mRouterObj,
40511417b1cfde8f1749905f2d735623af9214148afJeff Brown                    MediaRouterJellybean.ALL_ROUTE_TYPES)) {
406fa326a4649d9d0e8113e315f6c8251fe686abce4Jeff Brown                // The currently selected route has already changed so this callback
407fa326a4649d9d0e8113e315f6c8251fe686abce4Jeff Brown                // is stale.  Drop it to prevent getting into sync loops.
408fa326a4649d9d0e8113e315f6c8251fe686abce4Jeff Brown                return;
409fa326a4649d9d0e8113e315f6c8251fe686abce4Jeff Brown            }
410fa326a4649d9d0e8113e315f6c8251fe686abce4Jeff Brown
411fa326a4649d9d0e8113e315f6c8251fe686abce4Jeff Brown            UserRouteRecord userRouteRecord = getUserRouteRecord(routeObj);
412fa326a4649d9d0e8113e315f6c8251fe686abce4Jeff Brown            if (userRouteRecord != null) {
413fa326a4649d9d0e8113e315f6c8251fe686abce4Jeff Brown                userRouteRecord.mRoute.select();
414c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown            } else {
415c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown                // Select the route if it already exists in the compat media router.
416c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown                // If not, we will select it instead when the route is added.
417c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown                int index = findSystemRouteRecord(routeObj);
418c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown                if (index >= 0) {
419c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown                    SystemRouteRecord record = mSystemRouteRecords.get(index);
420ee4926b3033899028886c7407f75bcf55e708192Conrad Chen                    mSyncCallback.onSystemRouteSelectedByDescriptorId(record.mRouteDescriptorId);
421c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown                }
422c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown            }
423c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown        }
424c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown
425c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown        @Override
426c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown        public void onRouteUnselected(int type, Object routeObj) {
427c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown            // Nothing to do when a route is unselected.
428c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown            // We only need to handle when a route is selected.
429c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown        }
430c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown
431c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown        @Override
432c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown        public void onRouteGrouped(Object routeObj, Object groupObj, int index) {
433c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown            // Route grouping is deprecated and no longer supported.
434c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown        }
435c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown
436c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown        @Override
437c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown        public void onRouteUngrouped(Object routeObj, Object groupObj) {
438c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown            // Route grouping is deprecated and no longer supported.
439c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown        }
440c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown
441c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown        @Override
442c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown        public void onVolumeSetRequest(Object routeObj, int volume) {
443fa326a4649d9d0e8113e315f6c8251fe686abce4Jeff Brown            UserRouteRecord record = getUserRouteRecord(routeObj);
444fa326a4649d9d0e8113e315f6c8251fe686abce4Jeff Brown            if (record != null) {
445c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown                record.mRoute.requestSetVolume(volume);
446c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown            }
447c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown        }
448c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown
449c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown        @Override
450c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown        public void onVolumeUpdateRequest(Object routeObj, int direction) {
451fa326a4649d9d0e8113e315f6c8251fe686abce4Jeff Brown            UserRouteRecord record = getUserRouteRecord(routeObj);
452fa326a4649d9d0e8113e315f6c8251fe686abce4Jeff Brown            if (record != null) {
453c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown                record.mRoute.requestUpdateVolume(direction);
454c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown            }
455c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown        }
456c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown
457c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown        @Override
458c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown        public void onSyncRouteAdded(MediaRouter.RouteInfo route) {
459fa326a4649d9d0e8113e315f6c8251fe686abce4Jeff Brown            if (route.getProviderInstance() != this) {
460c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown                Object routeObj = MediaRouterJellybean.createUserRoute(
461c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown                        mRouterObj, mUserRouteCategoryObj);
462c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown                UserRouteRecord record = new UserRouteRecord(route, routeObj);
463c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown                MediaRouterJellybean.RouteInfo.setTag(routeObj, record);
464c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown                MediaRouterJellybean.UserRouteInfo.setVolumeCallback(routeObj, mVolumeCallbackObj);
465c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown                updateUserRouteProperties(record);
466c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown                mUserRouteRecords.add(record);
467c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown                MediaRouterJellybean.addUserRoute(mRouterObj, routeObj);
468c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown            } else {
469c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown                // If the newly added route is the counterpart of the currently selected
470c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown                // route in the framework media router then ensure it is selected in
471c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown                // the compat media router.
472c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown                Object routeObj = MediaRouterJellybean.getSelectedRoute(
47311417b1cfde8f1749905f2d735623af9214148afJeff Brown                        mRouterObj, MediaRouterJellybean.ALL_ROUTE_TYPES);
474c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown                int index = findSystemRouteRecord(routeObj);
475c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown                if (index >= 0) {
476c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown                    SystemRouteRecord record = mSystemRouteRecords.get(index);
477c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown                    if (record.mRouteDescriptorId.equals(route.getDescriptorId())) {
478c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown                        route.select();
479c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown                    }
480c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown                }
481c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown            }
482c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown        }
483c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown
484c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown        @Override
485c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown        public void onSyncRouteRemoved(MediaRouter.RouteInfo route) {
486fa326a4649d9d0e8113e315f6c8251fe686abce4Jeff Brown            if (route.getProviderInstance() != this) {
487c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown                int index = findUserRouteRecord(route);
488c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown                if (index >= 0) {
489c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown                    UserRouteRecord record = mUserRouteRecords.remove(index);
490c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown                    MediaRouterJellybean.RouteInfo.setTag(record.mRouteObj, null);
491c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown                    MediaRouterJellybean.UserRouteInfo.setVolumeCallback(record.mRouteObj, null);
492c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown                    MediaRouterJellybean.removeUserRoute(mRouterObj, record.mRouteObj);
493c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown                }
494c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown            }
495c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown        }
496c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown
497c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown        @Override
498c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown        public void onSyncRouteChanged(MediaRouter.RouteInfo route) {
499fa326a4649d9d0e8113e315f6c8251fe686abce4Jeff Brown            if (route.getProviderInstance() != this) {
500c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown                int index = findUserRouteRecord(route);
501c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown                if (index >= 0) {
502c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown                    UserRouteRecord record = mUserRouteRecords.get(index);
503c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown                    updateUserRouteProperties(record);
504c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown                }
505c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown            }
506c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown        }
507c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown
508c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown        @Override
509c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown        public void onSyncRouteSelected(MediaRouter.RouteInfo route) {
510fa326a4649d9d0e8113e315f6c8251fe686abce4Jeff Brown            if (!route.isSelected()) {
511fa326a4649d9d0e8113e315f6c8251fe686abce4Jeff Brown                // The currently selected route has already changed so this callback
512fa326a4649d9d0e8113e315f6c8251fe686abce4Jeff Brown                // is stale.  Drop it to prevent getting into sync loops.
513fa326a4649d9d0e8113e315f6c8251fe686abce4Jeff Brown                return;
514fa326a4649d9d0e8113e315f6c8251fe686abce4Jeff Brown            }
515fa326a4649d9d0e8113e315f6c8251fe686abce4Jeff Brown
516fa326a4649d9d0e8113e315f6c8251fe686abce4Jeff Brown            if (route.getProviderInstance() != this) {
517c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown                int index = findUserRouteRecord(route);
518c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown                if (index >= 0) {
519c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown                    UserRouteRecord record = mUserRouteRecords.get(index);
520c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown                    selectRoute(record.mRouteObj);
521c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown                }
522c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown            } else {
523c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown                int index = findSystemRouteRecordByDescriptorId(route.getDescriptorId());
524c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown                if (index >= 0) {
525c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown                    SystemRouteRecord record = mSystemRouteRecords.get(index);
526c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown                    selectRoute(record.mRouteObj);
527c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown                }
528c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown            }
529c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown        }
530c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown
531c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown        protected void publishRoutes() {
53211417b1cfde8f1749905f2d735623af9214148afJeff Brown            MediaRouteProviderDescriptor.Builder builder =
533d11aa1784335270b8d85e385f2c8be79ee6a586cJeff Brown                    new MediaRouteProviderDescriptor.Builder();
534c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown            int count = mSystemRouteRecords.size();
535c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown            for (int i = 0; i < count; i++) {
53611417b1cfde8f1749905f2d735623af9214148afJeff Brown                builder.addRoute(mSystemRouteRecords.get(i).mRouteDescriptor);
537c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown            }
538c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown
53911417b1cfde8f1749905f2d735623af9214148afJeff Brown            setDescriptor(builder.build());
540c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown        }
541c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown
542c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown        protected int findSystemRouteRecord(Object routeObj) {
543c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown            final int count = mSystemRouteRecords.size();
544c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown            for (int i = 0; i < count; i++) {
545c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown                if (mSystemRouteRecords.get(i).mRouteObj == routeObj) {
546c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown                    return i;
547c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown                }
548c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown            }
549c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown            return -1;
550c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown        }
551c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown
552c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown        protected int findSystemRouteRecordByDescriptorId(String id) {
553c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown            final int count = mSystemRouteRecords.size();
554c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown            for (int i = 0; i < count; i++) {
555c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown                if (mSystemRouteRecords.get(i).mRouteDescriptorId.equals(id)) {
556c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown                    return i;
557c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown                }
558c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown            }
559c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown            return -1;
560c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown        }
561c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown
562c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown        protected int findUserRouteRecord(MediaRouter.RouteInfo route) {
563c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown            final int count = mUserRouteRecords.size();
564c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown            for (int i = 0; i < count; i++) {
565c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown                if (mUserRouteRecords.get(i).mRoute == route) {
566c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown                    return i;
567c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown                }
568c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown            }
569c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown            return -1;
570c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown        }
571c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown
572fa326a4649d9d0e8113e315f6c8251fe686abce4Jeff Brown        protected UserRouteRecord getUserRouteRecord(Object routeObj) {
573fa326a4649d9d0e8113e315f6c8251fe686abce4Jeff Brown            Object tag = MediaRouterJellybean.RouteInfo.getTag(routeObj);
574fa326a4649d9d0e8113e315f6c8251fe686abce4Jeff Brown            return tag instanceof UserRouteRecord ? (UserRouteRecord)tag : null;
575fa326a4649d9d0e8113e315f6c8251fe686abce4Jeff Brown        }
576fa326a4649d9d0e8113e315f6c8251fe686abce4Jeff Brown
577c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown        protected void updateSystemRouteDescriptor(SystemRouteRecord record) {
578fa326a4649d9d0e8113e315f6c8251fe686abce4Jeff Brown            // We must always recreate the route descriptor when making any changes
579fa326a4649d9d0e8113e315f6c8251fe686abce4Jeff Brown            // because they are intended to be immutable once published.
580cb63b6ecac9786891514f241dec71695f09d3efbJeff Brown            MediaRouteDescriptor.Builder builder = new MediaRouteDescriptor.Builder(
581cb63b6ecac9786891514f241dec71695f09d3efbJeff Brown                    record.mRouteDescriptorId, getRouteName(record.mRouteObj));
582cb63b6ecac9786891514f241dec71695f09d3efbJeff Brown            onBuildSystemRouteDescriptor(record, builder);
583cb63b6ecac9786891514f241dec71695f09d3efbJeff Brown            record.mRouteDescriptor = builder.build();
584cb63b6ecac9786891514f241dec71695f09d3efbJeff Brown        }
585f6a2eb6ce9fef191df8368681ed5f825dcd24340Jeff Brown
586cb63b6ecac9786891514f241dec71695f09d3efbJeff Brown        protected String getRouteName(Object routeObj) {
587f6a2eb6ce9fef191df8368681ed5f825dcd24340Jeff Brown            // Routes should not have null names but it may happen for badly configured
588f6a2eb6ce9fef191df8368681ed5f825dcd24340Jeff Brown            // user routes.  We tolerate this by using an empty name string here but
589f6a2eb6ce9fef191df8368681ed5f825dcd24340Jeff Brown            // such unnamed routes will be discarded by the media router upstream
590f6a2eb6ce9fef191df8368681ed5f825dcd24340Jeff Brown            // (with a log message so we can track down the problem).
591cb63b6ecac9786891514f241dec71695f09d3efbJeff Brown            CharSequence name = MediaRouterJellybean.RouteInfo.getName(routeObj, getContext());
592cb63b6ecac9786891514f241dec71695f09d3efbJeff Brown            return name != null ? name.toString() : "";
59311417b1cfde8f1749905f2d735623af9214148afJeff Brown        }
594c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown
59511417b1cfde8f1749905f2d735623af9214148afJeff Brown        protected void onBuildSystemRouteDescriptor(SystemRouteRecord record,
59611417b1cfde8f1749905f2d735623af9214148afJeff Brown                MediaRouteDescriptor.Builder builder) {
597c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown            int supportedTypes = MediaRouterJellybean.RouteInfo.getSupportedTypes(
598c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown                    record.mRouteObj);
599c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown            if ((supportedTypes & MediaRouterJellybean.ROUTE_TYPE_LIVE_AUDIO) != 0) {
60011417b1cfde8f1749905f2d735623af9214148afJeff Brown                builder.addControlFilters(LIVE_AUDIO_CONTROL_FILTERS);
60111417b1cfde8f1749905f2d735623af9214148afJeff Brown            }
60211417b1cfde8f1749905f2d735623af9214148afJeff Brown            if ((supportedTypes & MediaRouterJellybean.ROUTE_TYPE_LIVE_VIDEO) != 0) {
60311417b1cfde8f1749905f2d735623af9214148afJeff Brown                builder.addControlFilters(LIVE_VIDEO_CONTROL_FILTERS);
604c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown            }
605c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown
60611417b1cfde8f1749905f2d735623af9214148afJeff Brown            builder.setPlaybackType(
607c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown                    MediaRouterJellybean.RouteInfo.getPlaybackType(record.mRouteObj));
60811417b1cfde8f1749905f2d735623af9214148afJeff Brown            builder.setPlaybackStream(
609c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown                    MediaRouterJellybean.RouteInfo.getPlaybackStream(record.mRouteObj));
61011417b1cfde8f1749905f2d735623af9214148afJeff Brown            builder.setVolume(
611c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown                    MediaRouterJellybean.RouteInfo.getVolume(record.mRouteObj));
61211417b1cfde8f1749905f2d735623af9214148afJeff Brown            builder.setVolumeMax(
613c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown                    MediaRouterJellybean.RouteInfo.getVolumeMax(record.mRouteObj));
61411417b1cfde8f1749905f2d735623af9214148afJeff Brown            builder.setVolumeHandling(
615c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown                    MediaRouterJellybean.RouteInfo.getVolumeHandling(record.mRouteObj));
616c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown        }
617c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown
618c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown        protected void updateUserRouteProperties(UserRouteRecord record) {
619c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown            MediaRouterJellybean.UserRouteInfo.setName(
620c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown                    record.mRouteObj, record.mRoute.getName());
621c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown            MediaRouterJellybean.UserRouteInfo.setPlaybackType(
622c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown                    record.mRouteObj, record.mRoute.getPlaybackType());
623c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown            MediaRouterJellybean.UserRouteInfo.setPlaybackStream(
624c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown                    record.mRouteObj, record.mRoute.getPlaybackStream());
625c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown            MediaRouterJellybean.UserRouteInfo.setVolume(
626c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown                    record.mRouteObj, record.mRoute.getVolume());
627c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown            MediaRouterJellybean.UserRouteInfo.setVolumeMax(
628c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown                    record.mRouteObj, record.mRoute.getVolumeMax());
629c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown            MediaRouterJellybean.UserRouteInfo.setVolumeHandling(
630c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown                    record.mRouteObj, record.mRoute.getVolumeHandling());
631c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown        }
632c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown
63311417b1cfde8f1749905f2d735623af9214148afJeff Brown        protected void updateCallback() {
63411417b1cfde8f1749905f2d735623af9214148afJeff Brown            if (mCallbackRegistered) {
63511417b1cfde8f1749905f2d735623af9214148afJeff Brown                mCallbackRegistered = false;
63611417b1cfde8f1749905f2d735623af9214148afJeff Brown                MediaRouterJellybean.removeCallback(mRouterObj, mCallbackObj);
63711417b1cfde8f1749905f2d735623af9214148afJeff Brown            }
63811417b1cfde8f1749905f2d735623af9214148afJeff Brown
63911417b1cfde8f1749905f2d735623af9214148afJeff Brown            if (mRouteTypes != 0) {
64011417b1cfde8f1749905f2d735623af9214148afJeff Brown                mCallbackRegistered = true;
64111417b1cfde8f1749905f2d735623af9214148afJeff Brown                MediaRouterJellybean.addCallback(mRouterObj, mRouteTypes, mCallbackObj);
64211417b1cfde8f1749905f2d735623af9214148afJeff Brown            }
64311417b1cfde8f1749905f2d735623af9214148afJeff Brown        }
64411417b1cfde8f1749905f2d735623af9214148afJeff Brown
645c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown        protected Object createCallbackObj() {
646c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown            return MediaRouterJellybean.createCallback(this);
647c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown        }
648c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown
649c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown        protected Object createVolumeCallbackObj() {
650c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown            return MediaRouterJellybean.createVolumeCallback(this);
651c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown        }
652c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown
653c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown        protected void selectRoute(Object routeObj) {
65411417b1cfde8f1749905f2d735623af9214148afJeff Brown            if (mSelectRouteWorkaround == null) {
65511417b1cfde8f1749905f2d735623af9214148afJeff Brown                mSelectRouteWorkaround = new MediaRouterJellybean.SelectRouteWorkaround();
656c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown            }
65711417b1cfde8f1749905f2d735623af9214148afJeff Brown            mSelectRouteWorkaround.selectRoute(mRouterObj,
65811417b1cfde8f1749905f2d735623af9214148afJeff Brown                    MediaRouterJellybean.ALL_ROUTE_TYPES, routeObj);
659c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown        }
660c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown
661c0b44f78f248e321d913db6077bf9a55976d7319Sungsoo        @Override
662c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown        protected Object getDefaultRoute() {
66311417b1cfde8f1749905f2d735623af9214148afJeff Brown            if (mGetDefaultRouteWorkaround == null) {
66411417b1cfde8f1749905f2d735623af9214148afJeff Brown                mGetDefaultRouteWorkaround = new MediaRouterJellybean.GetDefaultRouteWorkaround();
665c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown            }
66611417b1cfde8f1749905f2d735623af9214148afJeff Brown            return mGetDefaultRouteWorkaround.getDefaultRoute(mRouterObj);
667c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown        }
668c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown
6696adc8ba4e18a8b97b0c6719975d7259f7a2500feSungsoo        @Override
6706adc8ba4e18a8b97b0c6719975d7259f7a2500feSungsoo        protected Object getSystemRoute(MediaRouter.RouteInfo route) {
6716adc8ba4e18a8b97b0c6719975d7259f7a2500feSungsoo            if (route == null) {
6726adc8ba4e18a8b97b0c6719975d7259f7a2500feSungsoo                return null;
6736adc8ba4e18a8b97b0c6719975d7259f7a2500feSungsoo            }
6746adc8ba4e18a8b97b0c6719975d7259f7a2500feSungsoo            int index = findSystemRouteRecordByDescriptorId(route.getDescriptorId());
6756adc8ba4e18a8b97b0c6719975d7259f7a2500feSungsoo            if (index >= 0) {
6766adc8ba4e18a8b97b0c6719975d7259f7a2500feSungsoo                return mSystemRouteRecords.get(index).mRouteObj;
6776adc8ba4e18a8b97b0c6719975d7259f7a2500feSungsoo            }
6786adc8ba4e18a8b97b0c6719975d7259f7a2500feSungsoo            return null;
6796adc8ba4e18a8b97b0c6719975d7259f7a2500feSungsoo        }
6806adc8ba4e18a8b97b0c6719975d7259f7a2500feSungsoo
681c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown        /**
682c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown         * Represents a route that is provided by the framework media router
683c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown         * and published by this route provider to the support library media router.
684c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown         */
685c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown        protected static final class SystemRouteRecord {
686c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown            public final Object mRouteObj;
687c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown            public final String mRouteDescriptorId;
68811417b1cfde8f1749905f2d735623af9214148afJeff Brown            public MediaRouteDescriptor mRouteDescriptor; // assigned immediately after creation
689c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown
690cb63b6ecac9786891514f241dec71695f09d3efbJeff Brown            public SystemRouteRecord(Object routeObj, String id) {
691c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown                mRouteObj = routeObj;
692cb63b6ecac9786891514f241dec71695f09d3efbJeff Brown                mRouteDescriptorId = id;
693c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown            }
694c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown        }
695c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown
696c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown        /**
697c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown         * Represents a route that is provided by the support library media router
698c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown         * and published by this route provider to the framework media router.
699c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown         */
700c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown        protected static final class UserRouteRecord {
701c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown            public final MediaRouter.RouteInfo mRoute;
702c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown            public final Object mRouteObj;
703c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown
704c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown            public UserRouteRecord(MediaRouter.RouteInfo route, Object routeObj) {
705c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown                mRoute = route;
706c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown                mRouteObj = routeObj;
707c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown            }
708c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown        }
70919e77645aa6ce65b466d570375b36e2428f170b3Jeff Brown
71098ec0d5b378c8417156037af6389e90f0074a26eAurimas Liutikas        protected static final class SystemRouteController extends RouteController {
71119e77645aa6ce65b466d570375b36e2428f170b3Jeff Brown            private final Object mRouteObj;
71219e77645aa6ce65b466d570375b36e2428f170b3Jeff Brown
71319e77645aa6ce65b466d570375b36e2428f170b3Jeff Brown            public SystemRouteController(Object routeObj) {
71419e77645aa6ce65b466d570375b36e2428f170b3Jeff Brown                mRouteObj = routeObj;
71519e77645aa6ce65b466d570375b36e2428f170b3Jeff Brown            }
71619e77645aa6ce65b466d570375b36e2428f170b3Jeff Brown
71719e77645aa6ce65b466d570375b36e2428f170b3Jeff Brown            @Override
71819e77645aa6ce65b466d570375b36e2428f170b3Jeff Brown            public void onSetVolume(int volume) {
71919e77645aa6ce65b466d570375b36e2428f170b3Jeff Brown                MediaRouterJellybean.RouteInfo.requestSetVolume(mRouteObj, volume);
72019e77645aa6ce65b466d570375b36e2428f170b3Jeff Brown            }
72119e77645aa6ce65b466d570375b36e2428f170b3Jeff Brown
72219e77645aa6ce65b466d570375b36e2428f170b3Jeff Brown            @Override
72319e77645aa6ce65b466d570375b36e2428f170b3Jeff Brown            public void onUpdateVolume(int delta) {
72419e77645aa6ce65b466d570375b36e2428f170b3Jeff Brown                MediaRouterJellybean.RouteInfo.requestUpdateVolume(mRouteObj, delta);
72519e77645aa6ce65b466d570375b36e2428f170b3Jeff Brown            }
72619e77645aa6ce65b466d570375b36e2428f170b3Jeff Brown        }
727c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown    }
728c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown
729c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown    /**
730c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown     * Jellybean MR1 implementation.
731c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown     */
732c69882cb9b130902c1554ef5d3e3b06d776cd796Alan Viverette    @RequiresApi(17)
733c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown    private static class JellybeanMr1Impl extends JellybeanImpl
734c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown            implements MediaRouterJellybeanMr1.Callback {
73511417b1cfde8f1749905f2d735623af9214148afJeff Brown        private MediaRouterJellybeanMr1.ActiveScanWorkaround mActiveScanWorkaround;
73611417b1cfde8f1749905f2d735623af9214148afJeff Brown        private MediaRouterJellybeanMr1.IsConnectingWorkaround mIsConnectingWorkaround;
73711417b1cfde8f1749905f2d735623af9214148afJeff Brown
738c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown        public JellybeanMr1Impl(Context context, SyncCallback syncCallback) {
739c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown            super(context, syncCallback);
740c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown        }
741c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown
742c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown        @Override
743c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown        public void onRoutePresentationDisplayChanged(Object routeObj) {
744c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown            int index = findSystemRouteRecord(routeObj);
745c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown            if (index >= 0) {
746c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown                SystemRouteRecord record = mSystemRouteRecords.get(index);
747c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown                Display newPresentationDisplay =
748c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown                        MediaRouterJellybeanMr1.RouteInfo.getPresentationDisplay(routeObj);
749c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown                int newPresentationDisplayId = (newPresentationDisplay != null
750c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown                        ? newPresentationDisplay.getDisplayId() : -1);
751c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown                if (newPresentationDisplayId
752c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown                        != record.mRouteDescriptor.getPresentationDisplayId()) {
75311417b1cfde8f1749905f2d735623af9214148afJeff Brown                    record.mRouteDescriptor =
75411417b1cfde8f1749905f2d735623af9214148afJeff Brown                            new MediaRouteDescriptor.Builder(record.mRouteDescriptor)
75511417b1cfde8f1749905f2d735623af9214148afJeff Brown                            .setPresentationDisplayId(newPresentationDisplayId)
75611417b1cfde8f1749905f2d735623af9214148afJeff Brown                            .build();
757c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown                    publishRoutes();
758c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown                }
759c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown            }
760c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown        }
761c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown
762c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown        @Override
76311417b1cfde8f1749905f2d735623af9214148afJeff Brown        protected void onBuildSystemRouteDescriptor(SystemRouteRecord record,
76411417b1cfde8f1749905f2d735623af9214148afJeff Brown                MediaRouteDescriptor.Builder builder) {
76511417b1cfde8f1749905f2d735623af9214148afJeff Brown            super.onBuildSystemRouteDescriptor(record, builder);
766c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown
767c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown            if (!MediaRouterJellybeanMr1.RouteInfo.isEnabled(record.mRouteObj)) {
76811417b1cfde8f1749905f2d735623af9214148afJeff Brown                builder.setEnabled(false);
769c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown            }
77011417b1cfde8f1749905f2d735623af9214148afJeff Brown
77111417b1cfde8f1749905f2d735623af9214148afJeff Brown            if (isConnecting(record)) {
77211417b1cfde8f1749905f2d735623af9214148afJeff Brown                builder.setConnecting(true);
77311417b1cfde8f1749905f2d735623af9214148afJeff Brown            }
77411417b1cfde8f1749905f2d735623af9214148afJeff Brown
775c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown            Display presentationDisplay =
776c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown                    MediaRouterJellybeanMr1.RouteInfo.getPresentationDisplay(record.mRouteObj);
777c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown            if (presentationDisplay != null) {
77811417b1cfde8f1749905f2d735623af9214148afJeff Brown                builder.setPresentationDisplayId(presentationDisplay.getDisplayId());
779c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown            }
780c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown        }
781c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown
782c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown        @Override
78311417b1cfde8f1749905f2d735623af9214148afJeff Brown        protected void updateCallback() {
78411417b1cfde8f1749905f2d735623af9214148afJeff Brown            super.updateCallback();
78511417b1cfde8f1749905f2d735623af9214148afJeff Brown
78611417b1cfde8f1749905f2d735623af9214148afJeff Brown            if (mActiveScanWorkaround == null) {
78711417b1cfde8f1749905f2d735623af9214148afJeff Brown                mActiveScanWorkaround = new MediaRouterJellybeanMr1.ActiveScanWorkaround(
78811417b1cfde8f1749905f2d735623af9214148afJeff Brown                        getContext(), getHandler());
78911417b1cfde8f1749905f2d735623af9214148afJeff Brown            }
79011417b1cfde8f1749905f2d735623af9214148afJeff Brown            mActiveScanWorkaround.setActiveScanRouteTypes(mActiveScan ? mRouteTypes : 0);
79111417b1cfde8f1749905f2d735623af9214148afJeff Brown        }
79211417b1cfde8f1749905f2d735623af9214148afJeff Brown
79311417b1cfde8f1749905f2d735623af9214148afJeff Brown        @Override
794c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown        protected Object createCallbackObj() {
795c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown            return MediaRouterJellybeanMr1.createCallback(this);
796c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown        }
79711417b1cfde8f1749905f2d735623af9214148afJeff Brown
79811417b1cfde8f1749905f2d735623af9214148afJeff Brown        protected boolean isConnecting(SystemRouteRecord record) {
79911417b1cfde8f1749905f2d735623af9214148afJeff Brown            if (mIsConnectingWorkaround == null) {
80011417b1cfde8f1749905f2d735623af9214148afJeff Brown                mIsConnectingWorkaround = new MediaRouterJellybeanMr1.IsConnectingWorkaround();
80111417b1cfde8f1749905f2d735623af9214148afJeff Brown            }
80211417b1cfde8f1749905f2d735623af9214148afJeff Brown            return mIsConnectingWorkaround.isConnecting(record.mRouteObj);
80311417b1cfde8f1749905f2d735623af9214148afJeff Brown        }
804c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown    }
805c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown
806c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown    /**
807c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown     * Jellybean MR2 implementation.
808c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown     */
809c69882cb9b130902c1554ef5d3e3b06d776cd796Alan Viverette    @RequiresApi(18)
810c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown    private static class JellybeanMr2Impl extends JellybeanMr1Impl {
811c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown        public JellybeanMr2Impl(Context context, SyncCallback syncCallback) {
812c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown            super(context, syncCallback);
813c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown        }
814c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown
815c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown        @Override
816d63957d28aaabcec588b8cde12eac16414783aebJeff Brown        protected void onBuildSystemRouteDescriptor(SystemRouteRecord record,
817d63957d28aaabcec588b8cde12eac16414783aebJeff Brown                MediaRouteDescriptor.Builder builder) {
818d63957d28aaabcec588b8cde12eac16414783aebJeff Brown            super.onBuildSystemRouteDescriptor(record, builder);
819d63957d28aaabcec588b8cde12eac16414783aebJeff Brown
820d63957d28aaabcec588b8cde12eac16414783aebJeff Brown            CharSequence description =
821d63957d28aaabcec588b8cde12eac16414783aebJeff Brown                    MediaRouterJellybeanMr2.RouteInfo.getDescription(record.mRouteObj);
822d63957d28aaabcec588b8cde12eac16414783aebJeff Brown            if (description != null) {
823d63957d28aaabcec588b8cde12eac16414783aebJeff Brown                builder.setDescription(description.toString());
824d63957d28aaabcec588b8cde12eac16414783aebJeff Brown            }
825d63957d28aaabcec588b8cde12eac16414783aebJeff Brown        }
826d63957d28aaabcec588b8cde12eac16414783aebJeff Brown
827d63957d28aaabcec588b8cde12eac16414783aebJeff Brown        @Override
828c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown        protected void selectRoute(Object routeObj) {
82911417b1cfde8f1749905f2d735623af9214148afJeff Brown            MediaRouterJellybean.selectRoute(mRouterObj,
83011417b1cfde8f1749905f2d735623af9214148afJeff Brown                    MediaRouterJellybean.ALL_ROUTE_TYPES, routeObj);
831c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown        }
832c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown
833c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown        @Override
834c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown        protected Object getDefaultRoute() {
835c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown            return MediaRouterJellybeanMr2.getDefaultRoute(mRouterObj);
836c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown        }
83711417b1cfde8f1749905f2d735623af9214148afJeff Brown
83811417b1cfde8f1749905f2d735623af9214148afJeff Brown        @Override
839d63957d28aaabcec588b8cde12eac16414783aebJeff Brown        protected void updateUserRouteProperties(UserRouteRecord record) {
840d63957d28aaabcec588b8cde12eac16414783aebJeff Brown            super.updateUserRouteProperties(record);
841d63957d28aaabcec588b8cde12eac16414783aebJeff Brown
842d63957d28aaabcec588b8cde12eac16414783aebJeff Brown            MediaRouterJellybeanMr2.UserRouteInfo.setDescription(
843d63957d28aaabcec588b8cde12eac16414783aebJeff Brown                    record.mRouteObj, record.mRoute.getDescription());
844d63957d28aaabcec588b8cde12eac16414783aebJeff Brown        }
845d63957d28aaabcec588b8cde12eac16414783aebJeff Brown
846d63957d28aaabcec588b8cde12eac16414783aebJeff Brown        @Override
84711417b1cfde8f1749905f2d735623af9214148afJeff Brown        protected void updateCallback() {
84811417b1cfde8f1749905f2d735623af9214148afJeff Brown            if (mCallbackRegistered) {
84911417b1cfde8f1749905f2d735623af9214148afJeff Brown                MediaRouterJellybean.removeCallback(mRouterObj, mCallbackObj);
85011417b1cfde8f1749905f2d735623af9214148afJeff Brown            }
85111417b1cfde8f1749905f2d735623af9214148afJeff Brown
85211417b1cfde8f1749905f2d735623af9214148afJeff Brown            mCallbackRegistered = true;
85311417b1cfde8f1749905f2d735623af9214148afJeff Brown            MediaRouterJellybeanMr2.addCallback(mRouterObj, mRouteTypes, mCallbackObj,
85411417b1cfde8f1749905f2d735623af9214148afJeff Brown                    MediaRouter.CALLBACK_FLAG_UNFILTERED_EVENTS
855f03da4a9e6cc02251c2f804eb6f25da61821d6a7Jeff Brown                    | (mActiveScan ? MediaRouter.CALLBACK_FLAG_PERFORM_ACTIVE_SCAN : 0));
85611417b1cfde8f1749905f2d735623af9214148afJeff Brown        }
85711417b1cfde8f1749905f2d735623af9214148afJeff Brown
85811417b1cfde8f1749905f2d735623af9214148afJeff Brown        @Override
85911417b1cfde8f1749905f2d735623af9214148afJeff Brown        protected boolean isConnecting(SystemRouteRecord record) {
86011417b1cfde8f1749905f2d735623af9214148afJeff Brown            return MediaRouterJellybeanMr2.RouteInfo.isConnecting(record.mRouteObj);
86111417b1cfde8f1749905f2d735623af9214148afJeff Brown        }
862c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown    }
86396d4428ba99ad068679fca129e179e8187770d75Insun Kang
86496d4428ba99ad068679fca129e179e8187770d75Insun Kang    /**
86596d4428ba99ad068679fca129e179e8187770d75Insun Kang     * Api24 implementation.
86696d4428ba99ad068679fca129e179e8187770d75Insun Kang     */
867c69882cb9b130902c1554ef5d3e3b06d776cd796Alan Viverette    @RequiresApi(24)
86896d4428ba99ad068679fca129e179e8187770d75Insun Kang    private static class Api24Impl extends JellybeanMr2Impl {
86996d4428ba99ad068679fca129e179e8187770d75Insun Kang        public Api24Impl(Context context, SyncCallback syncCallback) {
87096d4428ba99ad068679fca129e179e8187770d75Insun Kang            super(context, syncCallback);
87196d4428ba99ad068679fca129e179e8187770d75Insun Kang        }
87296d4428ba99ad068679fca129e179e8187770d75Insun Kang
87396d4428ba99ad068679fca129e179e8187770d75Insun Kang        @Override
87496d4428ba99ad068679fca129e179e8187770d75Insun Kang        protected void onBuildSystemRouteDescriptor(SystemRouteRecord record,
87596d4428ba99ad068679fca129e179e8187770d75Insun Kang                                                    MediaRouteDescriptor.Builder builder) {
87696d4428ba99ad068679fca129e179e8187770d75Insun Kang            super.onBuildSystemRouteDescriptor(record, builder);
87796d4428ba99ad068679fca129e179e8187770d75Insun Kang
87896d4428ba99ad068679fca129e179e8187770d75Insun Kang            builder.setDeviceType(MediaRouterApi24.RouteInfo.getDeviceType(record.mRouteObj));
87996d4428ba99ad068679fca129e179e8187770d75Insun Kang        }
88096d4428ba99ad068679fca129e179e8187770d75Insun Kang    }
881c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown}
882