MediaRouter.java revision 9a1de308cea2d160778fd977825f10a07b49d738
19a1de308cea2d160778fd977825f10a07b49d738Adam Powell/*
29a1de308cea2d160778fd977825f10a07b49d738Adam Powell * Copyright (C) 2012 The Android Open Source Project
39a1de308cea2d160778fd977825f10a07b49d738Adam Powell *
49a1de308cea2d160778fd977825f10a07b49d738Adam Powell * Licensed under the Apache License, Version 2.0 (the "License");
59a1de308cea2d160778fd977825f10a07b49d738Adam Powell * you may not use this file except in compliance with the License.
69a1de308cea2d160778fd977825f10a07b49d738Adam Powell * You may obtain a copy of the License at
79a1de308cea2d160778fd977825f10a07b49d738Adam Powell *
89a1de308cea2d160778fd977825f10a07b49d738Adam Powell *      http://www.apache.org/licenses/LICENSE-2.0
99a1de308cea2d160778fd977825f10a07b49d738Adam Powell *
109a1de308cea2d160778fd977825f10a07b49d738Adam Powell * Unless required by applicable law or agreed to in writing, software
119a1de308cea2d160778fd977825f10a07b49d738Adam Powell * distributed under the License is distributed on an "AS IS" BASIS,
129a1de308cea2d160778fd977825f10a07b49d738Adam Powell * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
139a1de308cea2d160778fd977825f10a07b49d738Adam Powell * See the License for the specific language governing permissions and
149a1de308cea2d160778fd977825f10a07b49d738Adam Powell * limitations under the License.
159a1de308cea2d160778fd977825f10a07b49d738Adam Powell */
169a1de308cea2d160778fd977825f10a07b49d738Adam Powell
179a1de308cea2d160778fd977825f10a07b49d738Adam Powellpackage android.media;
189a1de308cea2d160778fd977825f10a07b49d738Adam Powell
199a1de308cea2d160778fd977825f10a07b49d738Adam Powellimport android.bluetooth.BluetoothA2dp;
209a1de308cea2d160778fd977825f10a07b49d738Adam Powellimport android.content.BroadcastReceiver;
219a1de308cea2d160778fd977825f10a07b49d738Adam Powellimport android.content.Context;
229a1de308cea2d160778fd977825f10a07b49d738Adam Powellimport android.content.Intent;
239a1de308cea2d160778fd977825f10a07b49d738Adam Powellimport android.content.IntentFilter;
249a1de308cea2d160778fd977825f10a07b49d738Adam Powellimport android.os.Handler;
259a1de308cea2d160778fd977825f10a07b49d738Adam Powellimport android.util.Log;
269a1de308cea2d160778fd977825f10a07b49d738Adam Powell
279a1de308cea2d160778fd977825f10a07b49d738Adam Powellimport java.util.ArrayList;
289a1de308cea2d160778fd977825f10a07b49d738Adam Powellimport java.util.HashMap;
299a1de308cea2d160778fd977825f10a07b49d738Adam Powell
309a1de308cea2d160778fd977825f10a07b49d738Adam Powell/**
319a1de308cea2d160778fd977825f10a07b49d738Adam Powell * MediaRouter allows applications to control the routing of media channels
329a1de308cea2d160778fd977825f10a07b49d738Adam Powell * and streams from the current device to external speakers and destination devices.
339a1de308cea2d160778fd977825f10a07b49d738Adam Powell *
349a1de308cea2d160778fd977825f10a07b49d738Adam Powell * <p>Media routes should only be modified on your application's main thread.</p>
359a1de308cea2d160778fd977825f10a07b49d738Adam Powell */
369a1de308cea2d160778fd977825f10a07b49d738Adam Powellpublic class MediaRouter {
379a1de308cea2d160778fd977825f10a07b49d738Adam Powell    private static final String TAG = "MediaRouter";
389a1de308cea2d160778fd977825f10a07b49d738Adam Powell
399a1de308cea2d160778fd977825f10a07b49d738Adam Powell    private Context mAppContext;
409a1de308cea2d160778fd977825f10a07b49d738Adam Powell    private AudioManager mAudioManager;
419a1de308cea2d160778fd977825f10a07b49d738Adam Powell    private Handler mHandler;
429a1de308cea2d160778fd977825f10a07b49d738Adam Powell    private final ArrayList<CallbackInfo> mCallbacks = new ArrayList<CallbackInfo>();
439a1de308cea2d160778fd977825f10a07b49d738Adam Powell
449a1de308cea2d160778fd977825f10a07b49d738Adam Powell    private final ArrayList<RouteInfo> mRoutes = new ArrayList<RouteInfo>();
459a1de308cea2d160778fd977825f10a07b49d738Adam Powell    private final ArrayList<RouteCategory> mCategories = new ArrayList<RouteCategory>();
469a1de308cea2d160778fd977825f10a07b49d738Adam Powell
479a1de308cea2d160778fd977825f10a07b49d738Adam Powell    private final RouteCategory mSystemCategory;
489a1de308cea2d160778fd977825f10a07b49d738Adam Powell    private RouteInfo mDefaultAudio;
499a1de308cea2d160778fd977825f10a07b49d738Adam Powell    private RouteInfo mBluetoothA2dpRoute;
509a1de308cea2d160778fd977825f10a07b49d738Adam Powell
519a1de308cea2d160778fd977825f10a07b49d738Adam Powell    private RouteInfo mSelectedRoute;
529a1de308cea2d160778fd977825f10a07b49d738Adam Powell
539a1de308cea2d160778fd977825f10a07b49d738Adam Powell    // These get removed when an activity dies
549a1de308cea2d160778fd977825f10a07b49d738Adam Powell    final ArrayList<BroadcastReceiver> mRegisteredReceivers = new ArrayList<BroadcastReceiver>();
559a1de308cea2d160778fd977825f10a07b49d738Adam Powell
569a1de308cea2d160778fd977825f10a07b49d738Adam Powell    /**
579a1de308cea2d160778fd977825f10a07b49d738Adam Powell     * Route type flag for live audio.
589a1de308cea2d160778fd977825f10a07b49d738Adam Powell     *
599a1de308cea2d160778fd977825f10a07b49d738Adam Powell     * <p>A device that supports live audio routing will allow the media audio stream
609a1de308cea2d160778fd977825f10a07b49d738Adam Powell     * to be routed to supported destinations. This can include internal speakers or
619a1de308cea2d160778fd977825f10a07b49d738Adam Powell     * audio jacks on the device itself, A2DP devices, and more.</p>
629a1de308cea2d160778fd977825f10a07b49d738Adam Powell     *
639a1de308cea2d160778fd977825f10a07b49d738Adam Powell     * <p>Once initiated this routing is transparent to the application. All audio
649a1de308cea2d160778fd977825f10a07b49d738Adam Powell     * played on the media stream will be routed to the selected destination.</p>
659a1de308cea2d160778fd977825f10a07b49d738Adam Powell     */
669a1de308cea2d160778fd977825f10a07b49d738Adam Powell    public static final int ROUTE_TYPE_LIVE_AUDIO = 0x1;
679a1de308cea2d160778fd977825f10a07b49d738Adam Powell
689a1de308cea2d160778fd977825f10a07b49d738Adam Powell    /**
699a1de308cea2d160778fd977825f10a07b49d738Adam Powell     * Route type flag for application-specific usage.
709a1de308cea2d160778fd977825f10a07b49d738Adam Powell     *
719a1de308cea2d160778fd977825f10a07b49d738Adam Powell     * <p>Unlike other media route types, user routes are managed by the application.
729a1de308cea2d160778fd977825f10a07b49d738Adam Powell     * The MediaRouter will manage and dispatch events for user routes, but the application
739a1de308cea2d160778fd977825f10a07b49d738Adam Powell     * is expected to interpret the meaning of these events and perform the requested
749a1de308cea2d160778fd977825f10a07b49d738Adam Powell     * routing tasks.</p>
759a1de308cea2d160778fd977825f10a07b49d738Adam Powell     */
769a1de308cea2d160778fd977825f10a07b49d738Adam Powell    public static final int ROUTE_TYPE_USER = 0x00800000;
779a1de308cea2d160778fd977825f10a07b49d738Adam Powell
789a1de308cea2d160778fd977825f10a07b49d738Adam Powell    // Maps application contexts
799a1de308cea2d160778fd977825f10a07b49d738Adam Powell    static final HashMap<Context, MediaRouter> sRouters = new HashMap<Context, MediaRouter>();
809a1de308cea2d160778fd977825f10a07b49d738Adam Powell
819a1de308cea2d160778fd977825f10a07b49d738Adam Powell    /**
829a1de308cea2d160778fd977825f10a07b49d738Adam Powell     * Return a MediaRouter for the application that the specified Context belongs to.
839a1de308cea2d160778fd977825f10a07b49d738Adam Powell     * The behavior or availability of media routing may depend on
849a1de308cea2d160778fd977825f10a07b49d738Adam Powell     * various parameters of the context.
859a1de308cea2d160778fd977825f10a07b49d738Adam Powell     *
869a1de308cea2d160778fd977825f10a07b49d738Adam Powell     * @param context Context for the desired router
879a1de308cea2d160778fd977825f10a07b49d738Adam Powell     * @return Router for the supplied Context
889a1de308cea2d160778fd977825f10a07b49d738Adam Powell     */
899a1de308cea2d160778fd977825f10a07b49d738Adam Powell    public static MediaRouter forApplication(Context context) {
909a1de308cea2d160778fd977825f10a07b49d738Adam Powell        final Context appContext = context.getApplicationContext();
919a1de308cea2d160778fd977825f10a07b49d738Adam Powell        if (!sRouters.containsKey(appContext)) {
929a1de308cea2d160778fd977825f10a07b49d738Adam Powell            final MediaRouter r = new MediaRouter(appContext);
939a1de308cea2d160778fd977825f10a07b49d738Adam Powell            sRouters.put(appContext, r);
949a1de308cea2d160778fd977825f10a07b49d738Adam Powell            return r;
959a1de308cea2d160778fd977825f10a07b49d738Adam Powell        } else {
969a1de308cea2d160778fd977825f10a07b49d738Adam Powell            return sRouters.get(appContext);
979a1de308cea2d160778fd977825f10a07b49d738Adam Powell        }
989a1de308cea2d160778fd977825f10a07b49d738Adam Powell    }
999a1de308cea2d160778fd977825f10a07b49d738Adam Powell
1009a1de308cea2d160778fd977825f10a07b49d738Adam Powell    static String typesToString(int types) {
1019a1de308cea2d160778fd977825f10a07b49d738Adam Powell        final StringBuilder result = new StringBuilder();
1029a1de308cea2d160778fd977825f10a07b49d738Adam Powell        if ((types & ROUTE_TYPE_LIVE_AUDIO) != 0) {
1039a1de308cea2d160778fd977825f10a07b49d738Adam Powell            result.append("ROUTE_TYPE_LIVE_AUDIO ");
1049a1de308cea2d160778fd977825f10a07b49d738Adam Powell        }
1059a1de308cea2d160778fd977825f10a07b49d738Adam Powell        if ((types & ROUTE_TYPE_USER) != 0) {
1069a1de308cea2d160778fd977825f10a07b49d738Adam Powell            result.append("ROUTE_TYPE_USER ");
1079a1de308cea2d160778fd977825f10a07b49d738Adam Powell        }
1089a1de308cea2d160778fd977825f10a07b49d738Adam Powell        return result.toString();
1099a1de308cea2d160778fd977825f10a07b49d738Adam Powell    }
1109a1de308cea2d160778fd977825f10a07b49d738Adam Powell
1119a1de308cea2d160778fd977825f10a07b49d738Adam Powell    private MediaRouter(Context context) {
1129a1de308cea2d160778fd977825f10a07b49d738Adam Powell        mAppContext = context;
1139a1de308cea2d160778fd977825f10a07b49d738Adam Powell        mHandler = new Handler(mAppContext.getMainLooper());
1149a1de308cea2d160778fd977825f10a07b49d738Adam Powell
1159a1de308cea2d160778fd977825f10a07b49d738Adam Powell        mAudioManager = (AudioManager) mAppContext.getSystemService(Context.AUDIO_SERVICE);
1169a1de308cea2d160778fd977825f10a07b49d738Adam Powell        mSystemCategory = new RouteCategory(mAppContext.getText(
1179a1de308cea2d160778fd977825f10a07b49d738Adam Powell                com.android.internal.R.string.default_audio_route_category_name),
1189a1de308cea2d160778fd977825f10a07b49d738Adam Powell                ROUTE_TYPE_LIVE_AUDIO, false);
1199a1de308cea2d160778fd977825f10a07b49d738Adam Powell
1209a1de308cea2d160778fd977825f10a07b49d738Adam Powell        registerReceivers();
1219a1de308cea2d160778fd977825f10a07b49d738Adam Powell
1229a1de308cea2d160778fd977825f10a07b49d738Adam Powell        createDefaultRoutes();
1239a1de308cea2d160778fd977825f10a07b49d738Adam Powell    }
1249a1de308cea2d160778fd977825f10a07b49d738Adam Powell
1259a1de308cea2d160778fd977825f10a07b49d738Adam Powell    private void registerReceivers() {
1269a1de308cea2d160778fd977825f10a07b49d738Adam Powell        final BroadcastReceiver volumeReceiver = new VolumeChangedBroadcastReceiver();
1279a1de308cea2d160778fd977825f10a07b49d738Adam Powell        mAppContext.registerReceiver(volumeReceiver,
1289a1de308cea2d160778fd977825f10a07b49d738Adam Powell                new IntentFilter(AudioManager.VOLUME_CHANGED_ACTION));
1299a1de308cea2d160778fd977825f10a07b49d738Adam Powell        mRegisteredReceivers.add(volumeReceiver);
1309a1de308cea2d160778fd977825f10a07b49d738Adam Powell
1319a1de308cea2d160778fd977825f10a07b49d738Adam Powell        final IntentFilter speakerFilter = new IntentFilter(Intent.ACTION_HEADSET_PLUG);
1329a1de308cea2d160778fd977825f10a07b49d738Adam Powell        speakerFilter.addAction(Intent.ACTION_ANALOG_AUDIO_DOCK_PLUG);
1339a1de308cea2d160778fd977825f10a07b49d738Adam Powell        speakerFilter.addAction(Intent.ACTION_DIGITAL_AUDIO_DOCK_PLUG);
1349a1de308cea2d160778fd977825f10a07b49d738Adam Powell        speakerFilter.addAction(Intent.ACTION_HDMI_AUDIO_PLUG);
1359a1de308cea2d160778fd977825f10a07b49d738Adam Powell        final BroadcastReceiver plugReceiver = new HeadphoneChangedBroadcastReceiver();
1369a1de308cea2d160778fd977825f10a07b49d738Adam Powell        mAppContext.registerReceiver(plugReceiver, speakerFilter);
1379a1de308cea2d160778fd977825f10a07b49d738Adam Powell        mRegisteredReceivers.add(plugReceiver);
1389a1de308cea2d160778fd977825f10a07b49d738Adam Powell    }
1399a1de308cea2d160778fd977825f10a07b49d738Adam Powell
1409a1de308cea2d160778fd977825f10a07b49d738Adam Powell    void unregisterReceivers() {
1419a1de308cea2d160778fd977825f10a07b49d738Adam Powell        final int count = mRegisteredReceivers.size();
1429a1de308cea2d160778fd977825f10a07b49d738Adam Powell        for (int i = 0; i < count; i++) {
1439a1de308cea2d160778fd977825f10a07b49d738Adam Powell            final BroadcastReceiver r = mRegisteredReceivers.get(i);
1449a1de308cea2d160778fd977825f10a07b49d738Adam Powell            mAppContext.unregisterReceiver(r);
1459a1de308cea2d160778fd977825f10a07b49d738Adam Powell        }
1469a1de308cea2d160778fd977825f10a07b49d738Adam Powell        mRegisteredReceivers.clear();
1479a1de308cea2d160778fd977825f10a07b49d738Adam Powell    }
1489a1de308cea2d160778fd977825f10a07b49d738Adam Powell
1499a1de308cea2d160778fd977825f10a07b49d738Adam Powell    private void createDefaultRoutes() {
1509a1de308cea2d160778fd977825f10a07b49d738Adam Powell        mDefaultAudio = new RouteInfo(mSystemCategory);
1519a1de308cea2d160778fd977825f10a07b49d738Adam Powell        mDefaultAudio.mName = mAppContext.getText(
1529a1de308cea2d160778fd977825f10a07b49d738Adam Powell                com.android.internal.R.string.default_audio_route_name);
1539a1de308cea2d160778fd977825f10a07b49d738Adam Powell        mDefaultAudio.mSupportedTypes = ROUTE_TYPE_LIVE_AUDIO;
1549a1de308cea2d160778fd977825f10a07b49d738Adam Powell        addRoute(mDefaultAudio);
1559a1de308cea2d160778fd977825f10a07b49d738Adam Powell    }
1569a1de308cea2d160778fd977825f10a07b49d738Adam Powell
1579a1de308cea2d160778fd977825f10a07b49d738Adam Powell    void onHeadphonesPlugged(boolean headphonesPresent, String headphonesName) {
1589a1de308cea2d160778fd977825f10a07b49d738Adam Powell        mDefaultAudio.mName = headphonesPresent ? headphonesName : mAppContext.getText(
1599a1de308cea2d160778fd977825f10a07b49d738Adam Powell                com.android.internal.R.string.default_audio_route_name);
1609a1de308cea2d160778fd977825f10a07b49d738Adam Powell        dispatchRouteChanged(mDefaultAudio);
1619a1de308cea2d160778fd977825f10a07b49d738Adam Powell    }
1629a1de308cea2d160778fd977825f10a07b49d738Adam Powell
1639a1de308cea2d160778fd977825f10a07b49d738Adam Powell    /**
1649a1de308cea2d160778fd977825f10a07b49d738Adam Powell     * Set volume for the specified route types.
1659a1de308cea2d160778fd977825f10a07b49d738Adam Powell     *
1669a1de308cea2d160778fd977825f10a07b49d738Adam Powell     * @param types Volume will be set for these route types
1679a1de308cea2d160778fd977825f10a07b49d738Adam Powell     * @param volume Volume to set in the range 0.f (inaudible) to 1.f (full volume).
1689a1de308cea2d160778fd977825f10a07b49d738Adam Powell     */
1699a1de308cea2d160778fd977825f10a07b49d738Adam Powell    public void setRouteVolume(int types, float volume) {
1709a1de308cea2d160778fd977825f10a07b49d738Adam Powell        if ((types & ROUTE_TYPE_LIVE_AUDIO) != 0) {
1719a1de308cea2d160778fd977825f10a07b49d738Adam Powell            final int index = mAudioManager.getStreamMaxVolume(AudioManager.STREAM_MUSIC);
1729a1de308cea2d160778fd977825f10a07b49d738Adam Powell            mAudioManager.setStreamVolume(AudioManager.STREAM_MUSIC, index, 0);
1739a1de308cea2d160778fd977825f10a07b49d738Adam Powell        }
1749a1de308cea2d160778fd977825f10a07b49d738Adam Powell        if ((types & ROUTE_TYPE_USER) != 0) {
1759a1de308cea2d160778fd977825f10a07b49d738Adam Powell            dispatchVolumeChanged(ROUTE_TYPE_USER, volume);
1769a1de308cea2d160778fd977825f10a07b49d738Adam Powell        }
1779a1de308cea2d160778fd977825f10a07b49d738Adam Powell    }
1789a1de308cea2d160778fd977825f10a07b49d738Adam Powell
1799a1de308cea2d160778fd977825f10a07b49d738Adam Powell    /**
1809a1de308cea2d160778fd977825f10a07b49d738Adam Powell     * Add a callback to listen to events about specific kinds of media routes.
1819a1de308cea2d160778fd977825f10a07b49d738Adam Powell     * If the specified callback is already registered, its registration will be updated for any
1829a1de308cea2d160778fd977825f10a07b49d738Adam Powell     * additional route types specified.
1839a1de308cea2d160778fd977825f10a07b49d738Adam Powell     *
1849a1de308cea2d160778fd977825f10a07b49d738Adam Powell     * @param types Types of routes this callback is interested in
1859a1de308cea2d160778fd977825f10a07b49d738Adam Powell     * @param cb Callback to add
1869a1de308cea2d160778fd977825f10a07b49d738Adam Powell     */
1879a1de308cea2d160778fd977825f10a07b49d738Adam Powell    public void addCallback(int types, Callback cb) {
1889a1de308cea2d160778fd977825f10a07b49d738Adam Powell        final int count = mCallbacks.size();
1899a1de308cea2d160778fd977825f10a07b49d738Adam Powell        for (int i = 0; i < count; i++) {
1909a1de308cea2d160778fd977825f10a07b49d738Adam Powell            final CallbackInfo info = mCallbacks.get(i);
1919a1de308cea2d160778fd977825f10a07b49d738Adam Powell            if (info.cb == cb) {
1929a1de308cea2d160778fd977825f10a07b49d738Adam Powell                info.type &= types;
1939a1de308cea2d160778fd977825f10a07b49d738Adam Powell                return;
1949a1de308cea2d160778fd977825f10a07b49d738Adam Powell            }
1959a1de308cea2d160778fd977825f10a07b49d738Adam Powell        }
1969a1de308cea2d160778fd977825f10a07b49d738Adam Powell        mCallbacks.add(new CallbackInfo(cb, types));
1979a1de308cea2d160778fd977825f10a07b49d738Adam Powell    }
1989a1de308cea2d160778fd977825f10a07b49d738Adam Powell
1999a1de308cea2d160778fd977825f10a07b49d738Adam Powell    /**
2009a1de308cea2d160778fd977825f10a07b49d738Adam Powell     * Remove the specified callback. It will no longer receive events about media routing.
2019a1de308cea2d160778fd977825f10a07b49d738Adam Powell     *
2029a1de308cea2d160778fd977825f10a07b49d738Adam Powell     * @param cb Callback to remove
2039a1de308cea2d160778fd977825f10a07b49d738Adam Powell     */
2049a1de308cea2d160778fd977825f10a07b49d738Adam Powell    public void removeCallback(Callback cb) {
2059a1de308cea2d160778fd977825f10a07b49d738Adam Powell        final int count = mCallbacks.size();
2069a1de308cea2d160778fd977825f10a07b49d738Adam Powell        for (int i = 0; i < count; i++) {
2079a1de308cea2d160778fd977825f10a07b49d738Adam Powell            if (mCallbacks.get(i).cb == cb) {
2089a1de308cea2d160778fd977825f10a07b49d738Adam Powell                mCallbacks.remove(i);
2099a1de308cea2d160778fd977825f10a07b49d738Adam Powell                return;
2109a1de308cea2d160778fd977825f10a07b49d738Adam Powell            }
2119a1de308cea2d160778fd977825f10a07b49d738Adam Powell        }
2129a1de308cea2d160778fd977825f10a07b49d738Adam Powell        Log.w(TAG, "removeCallback(" + cb + "): callback not registered");
2139a1de308cea2d160778fd977825f10a07b49d738Adam Powell    }
2149a1de308cea2d160778fd977825f10a07b49d738Adam Powell
2159a1de308cea2d160778fd977825f10a07b49d738Adam Powell    public void selectRoute(int types, RouteInfo route) {
2169a1de308cea2d160778fd977825f10a07b49d738Adam Powell        if (mSelectedRoute == route) return;
2179a1de308cea2d160778fd977825f10a07b49d738Adam Powell
2189a1de308cea2d160778fd977825f10a07b49d738Adam Powell        if (mSelectedRoute != null) {
2199a1de308cea2d160778fd977825f10a07b49d738Adam Powell            // TODO filter types properly
2209a1de308cea2d160778fd977825f10a07b49d738Adam Powell            dispatchRouteUnselected(types & mSelectedRoute.getSupportedTypes(), mSelectedRoute);
2219a1de308cea2d160778fd977825f10a07b49d738Adam Powell        }
2229a1de308cea2d160778fd977825f10a07b49d738Adam Powell        mSelectedRoute = route;
2239a1de308cea2d160778fd977825f10a07b49d738Adam Powell        if (route != null) {
2249a1de308cea2d160778fd977825f10a07b49d738Adam Powell            // TODO filter types properly
2259a1de308cea2d160778fd977825f10a07b49d738Adam Powell            dispatchRouteSelected(types & route.getSupportedTypes(), route);
2269a1de308cea2d160778fd977825f10a07b49d738Adam Powell        }
2279a1de308cea2d160778fd977825f10a07b49d738Adam Powell    }
2289a1de308cea2d160778fd977825f10a07b49d738Adam Powell
2299a1de308cea2d160778fd977825f10a07b49d738Adam Powell    /**
2309a1de308cea2d160778fd977825f10a07b49d738Adam Powell     * Add an app-specified route for media to the MediaRouter.
2319a1de308cea2d160778fd977825f10a07b49d738Adam Powell     * App-specified route definitions are created using {@link #createUserRoute(RouteCategory)}
2329a1de308cea2d160778fd977825f10a07b49d738Adam Powell     *
2339a1de308cea2d160778fd977825f10a07b49d738Adam Powell     * @param info Definition of the route to add
2349a1de308cea2d160778fd977825f10a07b49d738Adam Powell     * @see #createUserRoute()
2359a1de308cea2d160778fd977825f10a07b49d738Adam Powell     * @see #removeUserRoute(UserRouteInfo)
2369a1de308cea2d160778fd977825f10a07b49d738Adam Powell     */
2379a1de308cea2d160778fd977825f10a07b49d738Adam Powell    public void addUserRoute(UserRouteInfo info) {
2389a1de308cea2d160778fd977825f10a07b49d738Adam Powell        addRoute(info);
2399a1de308cea2d160778fd977825f10a07b49d738Adam Powell    }
2409a1de308cea2d160778fd977825f10a07b49d738Adam Powell
2419a1de308cea2d160778fd977825f10a07b49d738Adam Powell    void addRoute(RouteInfo info) {
2429a1de308cea2d160778fd977825f10a07b49d738Adam Powell        final RouteCategory cat = info.getCategory();
2439a1de308cea2d160778fd977825f10a07b49d738Adam Powell        if (!mCategories.contains(cat)) {
2449a1de308cea2d160778fd977825f10a07b49d738Adam Powell            mCategories.add(cat);
2459a1de308cea2d160778fd977825f10a07b49d738Adam Powell        }
2469a1de308cea2d160778fd977825f10a07b49d738Adam Powell        if (info.getCategory().isGroupable() && !(info instanceof RouteGroup)) {
2479a1de308cea2d160778fd977825f10a07b49d738Adam Powell            // Enforce that any added route in a groupable category must be in a group.
2489a1de308cea2d160778fd977825f10a07b49d738Adam Powell            final RouteGroup group = new RouteGroup(info.getCategory());
2499a1de308cea2d160778fd977825f10a07b49d738Adam Powell            group.addRoute(info);
2509a1de308cea2d160778fd977825f10a07b49d738Adam Powell            info = group;
2519a1de308cea2d160778fd977825f10a07b49d738Adam Powell        }
2529a1de308cea2d160778fd977825f10a07b49d738Adam Powell        final boolean onlyRoute = mRoutes.isEmpty();
2539a1de308cea2d160778fd977825f10a07b49d738Adam Powell        mRoutes.add(info);
2549a1de308cea2d160778fd977825f10a07b49d738Adam Powell        dispatchRouteAdded(info);
2559a1de308cea2d160778fd977825f10a07b49d738Adam Powell        if (onlyRoute) {
2569a1de308cea2d160778fd977825f10a07b49d738Adam Powell            selectRoute(info.getSupportedTypes(), info);
2579a1de308cea2d160778fd977825f10a07b49d738Adam Powell        }
2589a1de308cea2d160778fd977825f10a07b49d738Adam Powell    }
2599a1de308cea2d160778fd977825f10a07b49d738Adam Powell
2609a1de308cea2d160778fd977825f10a07b49d738Adam Powell    /**
2619a1de308cea2d160778fd977825f10a07b49d738Adam Powell     * Remove an app-specified route for media from the MediaRouter.
2629a1de308cea2d160778fd977825f10a07b49d738Adam Powell     *
2639a1de308cea2d160778fd977825f10a07b49d738Adam Powell     * @param info Definition of the route to remove
2649a1de308cea2d160778fd977825f10a07b49d738Adam Powell     * @see #addUserRoute(UserRouteInfo)
2659a1de308cea2d160778fd977825f10a07b49d738Adam Powell     */
2669a1de308cea2d160778fd977825f10a07b49d738Adam Powell    public void removeUserRoute(UserRouteInfo info) {
2679a1de308cea2d160778fd977825f10a07b49d738Adam Powell        removeRoute(info);
2689a1de308cea2d160778fd977825f10a07b49d738Adam Powell    }
2699a1de308cea2d160778fd977825f10a07b49d738Adam Powell
2709a1de308cea2d160778fd977825f10a07b49d738Adam Powell    void removeRoute(RouteInfo info) {
2719a1de308cea2d160778fd977825f10a07b49d738Adam Powell        if (mRoutes.remove(info)) {
2729a1de308cea2d160778fd977825f10a07b49d738Adam Powell            final RouteCategory removingCat = info.getCategory();
2739a1de308cea2d160778fd977825f10a07b49d738Adam Powell            final int count = mRoutes.size();
2749a1de308cea2d160778fd977825f10a07b49d738Adam Powell            boolean found = false;
2759a1de308cea2d160778fd977825f10a07b49d738Adam Powell            for (int i = 0; i < count; i++) {
2769a1de308cea2d160778fd977825f10a07b49d738Adam Powell                final RouteCategory cat = mRoutes.get(i).getCategory();
2779a1de308cea2d160778fd977825f10a07b49d738Adam Powell                if (removingCat == cat) {
2789a1de308cea2d160778fd977825f10a07b49d738Adam Powell                    found = true;
2799a1de308cea2d160778fd977825f10a07b49d738Adam Powell                    break;
2809a1de308cea2d160778fd977825f10a07b49d738Adam Powell                }
2819a1de308cea2d160778fd977825f10a07b49d738Adam Powell            }
2829a1de308cea2d160778fd977825f10a07b49d738Adam Powell            if (!found) {
2839a1de308cea2d160778fd977825f10a07b49d738Adam Powell                mCategories.remove(removingCat);
2849a1de308cea2d160778fd977825f10a07b49d738Adam Powell            }
2859a1de308cea2d160778fd977825f10a07b49d738Adam Powell            dispatchRouteRemoved(info);
2869a1de308cea2d160778fd977825f10a07b49d738Adam Powell        }
2879a1de308cea2d160778fd977825f10a07b49d738Adam Powell    }
2889a1de308cea2d160778fd977825f10a07b49d738Adam Powell
2899a1de308cea2d160778fd977825f10a07b49d738Adam Powell    /**
2909a1de308cea2d160778fd977825f10a07b49d738Adam Powell     * Return the number of {@link MediaRouter.RouteCategory categories} currently
2919a1de308cea2d160778fd977825f10a07b49d738Adam Powell     * represented by routes known to this MediaRouter.
2929a1de308cea2d160778fd977825f10a07b49d738Adam Powell     *
2939a1de308cea2d160778fd977825f10a07b49d738Adam Powell     * @return the number of unique categories represented by this MediaRouter's known routes
2949a1de308cea2d160778fd977825f10a07b49d738Adam Powell     */
2959a1de308cea2d160778fd977825f10a07b49d738Adam Powell    public int getCategoryCount() {
2969a1de308cea2d160778fd977825f10a07b49d738Adam Powell        return mCategories.size();
2979a1de308cea2d160778fd977825f10a07b49d738Adam Powell    }
2989a1de308cea2d160778fd977825f10a07b49d738Adam Powell
2999a1de308cea2d160778fd977825f10a07b49d738Adam Powell    /**
3009a1de308cea2d160778fd977825f10a07b49d738Adam Powell     * Return the {@link MediaRouter.RouteCategory category} at the given index.
3019a1de308cea2d160778fd977825f10a07b49d738Adam Powell     * Valid indices are in the range [0-getCategoryCount).
3029a1de308cea2d160778fd977825f10a07b49d738Adam Powell     *
3039a1de308cea2d160778fd977825f10a07b49d738Adam Powell     * @param index which category to return
3049a1de308cea2d160778fd977825f10a07b49d738Adam Powell     * @return the category at index
3059a1de308cea2d160778fd977825f10a07b49d738Adam Powell     */
3069a1de308cea2d160778fd977825f10a07b49d738Adam Powell    public RouteCategory getCategoryAt(int index) {
3079a1de308cea2d160778fd977825f10a07b49d738Adam Powell        return mCategories.get(index);
3089a1de308cea2d160778fd977825f10a07b49d738Adam Powell    }
3099a1de308cea2d160778fd977825f10a07b49d738Adam Powell
3109a1de308cea2d160778fd977825f10a07b49d738Adam Powell    /**
3119a1de308cea2d160778fd977825f10a07b49d738Adam Powell     * Return the number of {@link MediaRouter.RouteInfo routes} currently known
3129a1de308cea2d160778fd977825f10a07b49d738Adam Powell     * to this MediaRouter.
3139a1de308cea2d160778fd977825f10a07b49d738Adam Powell     *
3149a1de308cea2d160778fd977825f10a07b49d738Adam Powell     * @return the number of routes tracked by this router
3159a1de308cea2d160778fd977825f10a07b49d738Adam Powell     */
3169a1de308cea2d160778fd977825f10a07b49d738Adam Powell    public int getRouteCount() {
3179a1de308cea2d160778fd977825f10a07b49d738Adam Powell        return mRoutes.size();
3189a1de308cea2d160778fd977825f10a07b49d738Adam Powell    }
3199a1de308cea2d160778fd977825f10a07b49d738Adam Powell
3209a1de308cea2d160778fd977825f10a07b49d738Adam Powell    /**
3219a1de308cea2d160778fd977825f10a07b49d738Adam Powell     * Return the route at the specified index.
3229a1de308cea2d160778fd977825f10a07b49d738Adam Powell     *
3239a1de308cea2d160778fd977825f10a07b49d738Adam Powell     * @param index index of the route to return
3249a1de308cea2d160778fd977825f10a07b49d738Adam Powell     * @return the route at index
3259a1de308cea2d160778fd977825f10a07b49d738Adam Powell     */
3269a1de308cea2d160778fd977825f10a07b49d738Adam Powell    public RouteInfo getRouteAt(int index) {
3279a1de308cea2d160778fd977825f10a07b49d738Adam Powell        return mRoutes.get(index);
3289a1de308cea2d160778fd977825f10a07b49d738Adam Powell    }
3299a1de308cea2d160778fd977825f10a07b49d738Adam Powell
3309a1de308cea2d160778fd977825f10a07b49d738Adam Powell    /**
3319a1de308cea2d160778fd977825f10a07b49d738Adam Powell     * Create a new user route that may be modified and registered for use by the application.
3329a1de308cea2d160778fd977825f10a07b49d738Adam Powell     *
3339a1de308cea2d160778fd977825f10a07b49d738Adam Powell     * @param category The category the new route will belong to
3349a1de308cea2d160778fd977825f10a07b49d738Adam Powell     * @return A new UserRouteInfo for use by the application
3359a1de308cea2d160778fd977825f10a07b49d738Adam Powell     *
3369a1de308cea2d160778fd977825f10a07b49d738Adam Powell     * @see #addUserRoute(UserRouteInfo)
3379a1de308cea2d160778fd977825f10a07b49d738Adam Powell     * @see #removeUserRoute(UserRouteInfo)
3389a1de308cea2d160778fd977825f10a07b49d738Adam Powell     * @see #createRouteCategory(CharSequence)
3399a1de308cea2d160778fd977825f10a07b49d738Adam Powell     */
3409a1de308cea2d160778fd977825f10a07b49d738Adam Powell    public UserRouteInfo createUserRoute(RouteCategory category) {
3419a1de308cea2d160778fd977825f10a07b49d738Adam Powell        return new UserRouteInfo(category);
3429a1de308cea2d160778fd977825f10a07b49d738Adam Powell    }
3439a1de308cea2d160778fd977825f10a07b49d738Adam Powell
3449a1de308cea2d160778fd977825f10a07b49d738Adam Powell    /**
3459a1de308cea2d160778fd977825f10a07b49d738Adam Powell     * Create a new route category. Each route must belong to a category.
3469a1de308cea2d160778fd977825f10a07b49d738Adam Powell     *
3479a1de308cea2d160778fd977825f10a07b49d738Adam Powell     * @param name Name of the new category
3489a1de308cea2d160778fd977825f10a07b49d738Adam Powell     * @param isGroupable true if routes in this category may be grouped with one another
3499a1de308cea2d160778fd977825f10a07b49d738Adam Powell     * @return the new RouteCategory
3509a1de308cea2d160778fd977825f10a07b49d738Adam Powell     */
3519a1de308cea2d160778fd977825f10a07b49d738Adam Powell    public RouteCategory createRouteCategory(CharSequence name, boolean isGroupable) {
3529a1de308cea2d160778fd977825f10a07b49d738Adam Powell        return new RouteCategory(name, ROUTE_TYPE_USER, isGroupable);
3539a1de308cea2d160778fd977825f10a07b49d738Adam Powell    }
3549a1de308cea2d160778fd977825f10a07b49d738Adam Powell
3559a1de308cea2d160778fd977825f10a07b49d738Adam Powell    void updateRoute(final RouteInfo info) {
3569a1de308cea2d160778fd977825f10a07b49d738Adam Powell        dispatchRouteChanged(info);
3579a1de308cea2d160778fd977825f10a07b49d738Adam Powell    }
3589a1de308cea2d160778fd977825f10a07b49d738Adam Powell
3599a1de308cea2d160778fd977825f10a07b49d738Adam Powell    void dispatchRouteSelected(int type, RouteInfo info) {
3609a1de308cea2d160778fd977825f10a07b49d738Adam Powell        final int count = mCallbacks.size();
3619a1de308cea2d160778fd977825f10a07b49d738Adam Powell        for (int i = 0; i < count; i++) {
3629a1de308cea2d160778fd977825f10a07b49d738Adam Powell            final CallbackInfo cbi = mCallbacks.get(i);
3639a1de308cea2d160778fd977825f10a07b49d738Adam Powell            if ((cbi.type & type) != 0) {
3649a1de308cea2d160778fd977825f10a07b49d738Adam Powell                cbi.cb.onRouteSelected(type, info);
3659a1de308cea2d160778fd977825f10a07b49d738Adam Powell            }
3669a1de308cea2d160778fd977825f10a07b49d738Adam Powell        }
3679a1de308cea2d160778fd977825f10a07b49d738Adam Powell    }
3689a1de308cea2d160778fd977825f10a07b49d738Adam Powell
3699a1de308cea2d160778fd977825f10a07b49d738Adam Powell    void dispatchRouteUnselected(int type, RouteInfo info) {
3709a1de308cea2d160778fd977825f10a07b49d738Adam Powell        final int count = mCallbacks.size();
3719a1de308cea2d160778fd977825f10a07b49d738Adam Powell        for (int i = 0; i < count; i++) {
3729a1de308cea2d160778fd977825f10a07b49d738Adam Powell            final CallbackInfo cbi = mCallbacks.get(i);
3739a1de308cea2d160778fd977825f10a07b49d738Adam Powell            if ((cbi.type & type) != 0) {
3749a1de308cea2d160778fd977825f10a07b49d738Adam Powell                cbi.cb.onRouteUnselected(type, info);
3759a1de308cea2d160778fd977825f10a07b49d738Adam Powell            }
3769a1de308cea2d160778fd977825f10a07b49d738Adam Powell        }
3779a1de308cea2d160778fd977825f10a07b49d738Adam Powell    }
3789a1de308cea2d160778fd977825f10a07b49d738Adam Powell
3799a1de308cea2d160778fd977825f10a07b49d738Adam Powell    void dispatchRouteChanged(RouteInfo info) {
3809a1de308cea2d160778fd977825f10a07b49d738Adam Powell        final int count = mCallbacks.size();
3819a1de308cea2d160778fd977825f10a07b49d738Adam Powell        for (int i = 0; i < count; i++) {
3829a1de308cea2d160778fd977825f10a07b49d738Adam Powell            final CallbackInfo cbi = mCallbacks.get(i);
3839a1de308cea2d160778fd977825f10a07b49d738Adam Powell            if ((cbi.type & info.mSupportedTypes) != 0) {
3849a1de308cea2d160778fd977825f10a07b49d738Adam Powell                cbi.cb.onRouteChanged(info);
3859a1de308cea2d160778fd977825f10a07b49d738Adam Powell            }
3869a1de308cea2d160778fd977825f10a07b49d738Adam Powell        }
3879a1de308cea2d160778fd977825f10a07b49d738Adam Powell    }
3889a1de308cea2d160778fd977825f10a07b49d738Adam Powell
3899a1de308cea2d160778fd977825f10a07b49d738Adam Powell    void dispatchRouteAdded(RouteInfo info) {
3909a1de308cea2d160778fd977825f10a07b49d738Adam Powell        final int count = mCallbacks.size();
3919a1de308cea2d160778fd977825f10a07b49d738Adam Powell        for (int i = 0; i < count; i++) {
3929a1de308cea2d160778fd977825f10a07b49d738Adam Powell            final CallbackInfo cbi = mCallbacks.get(i);
3939a1de308cea2d160778fd977825f10a07b49d738Adam Powell            if ((cbi.type & info.mSupportedTypes) != 0) {
3949a1de308cea2d160778fd977825f10a07b49d738Adam Powell                cbi.cb.onRouteAdded(info.mSupportedTypes, info);
3959a1de308cea2d160778fd977825f10a07b49d738Adam Powell            }
3969a1de308cea2d160778fd977825f10a07b49d738Adam Powell        }
3979a1de308cea2d160778fd977825f10a07b49d738Adam Powell    }
3989a1de308cea2d160778fd977825f10a07b49d738Adam Powell
3999a1de308cea2d160778fd977825f10a07b49d738Adam Powell    void dispatchRouteRemoved(RouteInfo info) {
4009a1de308cea2d160778fd977825f10a07b49d738Adam Powell        final int count = mCallbacks.size();
4019a1de308cea2d160778fd977825f10a07b49d738Adam Powell        for (int i = 0; i < count; i++) {
4029a1de308cea2d160778fd977825f10a07b49d738Adam Powell            final CallbackInfo cbi = mCallbacks.get(i);
4039a1de308cea2d160778fd977825f10a07b49d738Adam Powell            if ((cbi.type & info.mSupportedTypes) != 0) {
4049a1de308cea2d160778fd977825f10a07b49d738Adam Powell                cbi.cb.onRouteRemoved(info.mSupportedTypes, info);
4059a1de308cea2d160778fd977825f10a07b49d738Adam Powell            }
4069a1de308cea2d160778fd977825f10a07b49d738Adam Powell        }
4079a1de308cea2d160778fd977825f10a07b49d738Adam Powell    }
4089a1de308cea2d160778fd977825f10a07b49d738Adam Powell
4099a1de308cea2d160778fd977825f10a07b49d738Adam Powell    void dispatchVolumeChanged(int type, float volume) {
4109a1de308cea2d160778fd977825f10a07b49d738Adam Powell        final int count = mCallbacks.size();
4119a1de308cea2d160778fd977825f10a07b49d738Adam Powell        for (int i = 0; i < count; i++) {
4129a1de308cea2d160778fd977825f10a07b49d738Adam Powell            final CallbackInfo cbi = mCallbacks.get(i);
4139a1de308cea2d160778fd977825f10a07b49d738Adam Powell            if ((cbi.type & type) != 0) {
4149a1de308cea2d160778fd977825f10a07b49d738Adam Powell                cbi.cb.onVolumeChanged(type, volume);
4159a1de308cea2d160778fd977825f10a07b49d738Adam Powell            }
4169a1de308cea2d160778fd977825f10a07b49d738Adam Powell        }
4179a1de308cea2d160778fd977825f10a07b49d738Adam Powell    }
4189a1de308cea2d160778fd977825f10a07b49d738Adam Powell
4199a1de308cea2d160778fd977825f10a07b49d738Adam Powell    void onA2dpDeviceConnected() {
4209a1de308cea2d160778fd977825f10a07b49d738Adam Powell        final RouteInfo info = new RouteInfo(mSystemCategory);
4219a1de308cea2d160778fd977825f10a07b49d738Adam Powell        info.mName = "Bluetooth"; // TODO Fetch the real name of the device
4229a1de308cea2d160778fd977825f10a07b49d738Adam Powell        mBluetoothA2dpRoute = info;
4239a1de308cea2d160778fd977825f10a07b49d738Adam Powell        addRoute(mBluetoothA2dpRoute);
4249a1de308cea2d160778fd977825f10a07b49d738Adam Powell    }
4259a1de308cea2d160778fd977825f10a07b49d738Adam Powell
4269a1de308cea2d160778fd977825f10a07b49d738Adam Powell    void onA2dpDeviceDisconnected() {
4279a1de308cea2d160778fd977825f10a07b49d738Adam Powell        removeRoute(mBluetoothA2dpRoute);
4289a1de308cea2d160778fd977825f10a07b49d738Adam Powell        mBluetoothA2dpRoute = null;
4299a1de308cea2d160778fd977825f10a07b49d738Adam Powell    }
4309a1de308cea2d160778fd977825f10a07b49d738Adam Powell
4319a1de308cea2d160778fd977825f10a07b49d738Adam Powell    /**
4329a1de308cea2d160778fd977825f10a07b49d738Adam Powell     * Information about a media route.
4339a1de308cea2d160778fd977825f10a07b49d738Adam Powell     */
4349a1de308cea2d160778fd977825f10a07b49d738Adam Powell    public class RouteInfo {
4359a1de308cea2d160778fd977825f10a07b49d738Adam Powell        CharSequence mName;
4369a1de308cea2d160778fd977825f10a07b49d738Adam Powell        private CharSequence mStatus;
4379a1de308cea2d160778fd977825f10a07b49d738Adam Powell        int mSupportedTypes;
4389a1de308cea2d160778fd977825f10a07b49d738Adam Powell        RouteGroup mGroup;
4399a1de308cea2d160778fd977825f10a07b49d738Adam Powell        final RouteCategory mCategory;
4409a1de308cea2d160778fd977825f10a07b49d738Adam Powell
4419a1de308cea2d160778fd977825f10a07b49d738Adam Powell        RouteInfo(RouteCategory category) {
4429a1de308cea2d160778fd977825f10a07b49d738Adam Powell            mCategory = category;
4439a1de308cea2d160778fd977825f10a07b49d738Adam Powell            category.mRoutes.add(this);
4449a1de308cea2d160778fd977825f10a07b49d738Adam Powell        }
4459a1de308cea2d160778fd977825f10a07b49d738Adam Powell
4469a1de308cea2d160778fd977825f10a07b49d738Adam Powell        /**
4479a1de308cea2d160778fd977825f10a07b49d738Adam Powell         * @return The user-friendly name of a media route. This is the string presented
4489a1de308cea2d160778fd977825f10a07b49d738Adam Powell         * to users who may select this as the active route.
4499a1de308cea2d160778fd977825f10a07b49d738Adam Powell         */
4509a1de308cea2d160778fd977825f10a07b49d738Adam Powell        public CharSequence getName() {
4519a1de308cea2d160778fd977825f10a07b49d738Adam Powell            return mName;
4529a1de308cea2d160778fd977825f10a07b49d738Adam Powell        }
4539a1de308cea2d160778fd977825f10a07b49d738Adam Powell
4549a1de308cea2d160778fd977825f10a07b49d738Adam Powell        /**
4559a1de308cea2d160778fd977825f10a07b49d738Adam Powell         * @return The user-friendly status for a media route. This may include a description
4569a1de308cea2d160778fd977825f10a07b49d738Adam Powell         * of the currently playing media, if available.
4579a1de308cea2d160778fd977825f10a07b49d738Adam Powell         */
4589a1de308cea2d160778fd977825f10a07b49d738Adam Powell        public CharSequence getStatus() {
4599a1de308cea2d160778fd977825f10a07b49d738Adam Powell            return mStatus;
4609a1de308cea2d160778fd977825f10a07b49d738Adam Powell        }
4619a1de308cea2d160778fd977825f10a07b49d738Adam Powell
4629a1de308cea2d160778fd977825f10a07b49d738Adam Powell        /**
4639a1de308cea2d160778fd977825f10a07b49d738Adam Powell         * @return A media type flag set describing which types this route supports.
4649a1de308cea2d160778fd977825f10a07b49d738Adam Powell         */
4659a1de308cea2d160778fd977825f10a07b49d738Adam Powell        public int getSupportedTypes() {
4669a1de308cea2d160778fd977825f10a07b49d738Adam Powell            return mSupportedTypes;
4679a1de308cea2d160778fd977825f10a07b49d738Adam Powell        }
4689a1de308cea2d160778fd977825f10a07b49d738Adam Powell
4699a1de308cea2d160778fd977825f10a07b49d738Adam Powell        /**
4709a1de308cea2d160778fd977825f10a07b49d738Adam Powell         * @return The group that this route belongs to.
4719a1de308cea2d160778fd977825f10a07b49d738Adam Powell         */
4729a1de308cea2d160778fd977825f10a07b49d738Adam Powell        public RouteGroup getGroup() {
4739a1de308cea2d160778fd977825f10a07b49d738Adam Powell            return mGroup;
4749a1de308cea2d160778fd977825f10a07b49d738Adam Powell        }
4759a1de308cea2d160778fd977825f10a07b49d738Adam Powell
4769a1de308cea2d160778fd977825f10a07b49d738Adam Powell        /**
4779a1de308cea2d160778fd977825f10a07b49d738Adam Powell         * @return the category this route belongs to.
4789a1de308cea2d160778fd977825f10a07b49d738Adam Powell         */
4799a1de308cea2d160778fd977825f10a07b49d738Adam Powell        public RouteCategory getCategory() {
4809a1de308cea2d160778fd977825f10a07b49d738Adam Powell            return mCategory;
4819a1de308cea2d160778fd977825f10a07b49d738Adam Powell        }
4829a1de308cea2d160778fd977825f10a07b49d738Adam Powell
4839a1de308cea2d160778fd977825f10a07b49d738Adam Powell        void setStatusInt(CharSequence status) {
4849a1de308cea2d160778fd977825f10a07b49d738Adam Powell            if (!status.equals(mStatus)) {
4859a1de308cea2d160778fd977825f10a07b49d738Adam Powell                mStatus = status;
4869a1de308cea2d160778fd977825f10a07b49d738Adam Powell                routeUpdated();
4879a1de308cea2d160778fd977825f10a07b49d738Adam Powell                if (mGroup != null) {
4889a1de308cea2d160778fd977825f10a07b49d738Adam Powell                    mGroup.memberStatusChanged(this, status);
4899a1de308cea2d160778fd977825f10a07b49d738Adam Powell                }
4909a1de308cea2d160778fd977825f10a07b49d738Adam Powell                routeUpdated();
4919a1de308cea2d160778fd977825f10a07b49d738Adam Powell            }
4929a1de308cea2d160778fd977825f10a07b49d738Adam Powell        }
4939a1de308cea2d160778fd977825f10a07b49d738Adam Powell
4949a1de308cea2d160778fd977825f10a07b49d738Adam Powell        void routeUpdated() {
4959a1de308cea2d160778fd977825f10a07b49d738Adam Powell            updateRoute(this);
4969a1de308cea2d160778fd977825f10a07b49d738Adam Powell        }
4979a1de308cea2d160778fd977825f10a07b49d738Adam Powell
4989a1de308cea2d160778fd977825f10a07b49d738Adam Powell        @Override
4999a1de308cea2d160778fd977825f10a07b49d738Adam Powell        public String toString() {
5009a1de308cea2d160778fd977825f10a07b49d738Adam Powell            String supportedTypes = typesToString(mSupportedTypes);
5019a1de308cea2d160778fd977825f10a07b49d738Adam Powell            return "RouteInfo{ name=" + mName + ", status=" + mStatus +
5029a1de308cea2d160778fd977825f10a07b49d738Adam Powell                    " category=" + mCategory +
5039a1de308cea2d160778fd977825f10a07b49d738Adam Powell                    " supportedTypes=" + supportedTypes + "}";
5049a1de308cea2d160778fd977825f10a07b49d738Adam Powell        }
5059a1de308cea2d160778fd977825f10a07b49d738Adam Powell    }
5069a1de308cea2d160778fd977825f10a07b49d738Adam Powell
5079a1de308cea2d160778fd977825f10a07b49d738Adam Powell    /**
5089a1de308cea2d160778fd977825f10a07b49d738Adam Powell     * Information about a route that the application may define and modify.
5099a1de308cea2d160778fd977825f10a07b49d738Adam Powell     *
5109a1de308cea2d160778fd977825f10a07b49d738Adam Powell     * @see MediaRouter.RouteInfo
5119a1de308cea2d160778fd977825f10a07b49d738Adam Powell     */
5129a1de308cea2d160778fd977825f10a07b49d738Adam Powell    public class UserRouteInfo extends RouteInfo {
5139a1de308cea2d160778fd977825f10a07b49d738Adam Powell
5149a1de308cea2d160778fd977825f10a07b49d738Adam Powell        UserRouteInfo(RouteCategory category) {
5159a1de308cea2d160778fd977825f10a07b49d738Adam Powell            super(category);
5169a1de308cea2d160778fd977825f10a07b49d738Adam Powell            mSupportedTypes = ROUTE_TYPE_USER;
5179a1de308cea2d160778fd977825f10a07b49d738Adam Powell        }
5189a1de308cea2d160778fd977825f10a07b49d738Adam Powell
5199a1de308cea2d160778fd977825f10a07b49d738Adam Powell        /**
5209a1de308cea2d160778fd977825f10a07b49d738Adam Powell         * Set the user-visible name of this route.
5219a1de308cea2d160778fd977825f10a07b49d738Adam Powell         * @param name Name to display to the user to describe this route
5229a1de308cea2d160778fd977825f10a07b49d738Adam Powell         */
5239a1de308cea2d160778fd977825f10a07b49d738Adam Powell        public void setName(CharSequence name) {
5249a1de308cea2d160778fd977825f10a07b49d738Adam Powell            mName = name;
5259a1de308cea2d160778fd977825f10a07b49d738Adam Powell            routeUpdated();
5269a1de308cea2d160778fd977825f10a07b49d738Adam Powell        }
5279a1de308cea2d160778fd977825f10a07b49d738Adam Powell
5289a1de308cea2d160778fd977825f10a07b49d738Adam Powell        /**
5299a1de308cea2d160778fd977825f10a07b49d738Adam Powell         * Set the current user-visible status for this route.
5309a1de308cea2d160778fd977825f10a07b49d738Adam Powell         * @param status Status to display to the user to describe what the endpoint
5319a1de308cea2d160778fd977825f10a07b49d738Adam Powell         * of this route is currently doing
5329a1de308cea2d160778fd977825f10a07b49d738Adam Powell         */
5339a1de308cea2d160778fd977825f10a07b49d738Adam Powell        public void setStatus(CharSequence status) {
5349a1de308cea2d160778fd977825f10a07b49d738Adam Powell            setStatusInt(status);
5359a1de308cea2d160778fd977825f10a07b49d738Adam Powell        }
5369a1de308cea2d160778fd977825f10a07b49d738Adam Powell    }
5379a1de308cea2d160778fd977825f10a07b49d738Adam Powell
5389a1de308cea2d160778fd977825f10a07b49d738Adam Powell    /**
5399a1de308cea2d160778fd977825f10a07b49d738Adam Powell     * Information about a route that consists of multiple other routes in a group.
5409a1de308cea2d160778fd977825f10a07b49d738Adam Powell     */
5419a1de308cea2d160778fd977825f10a07b49d738Adam Powell    public class RouteGroup extends RouteInfo {
5429a1de308cea2d160778fd977825f10a07b49d738Adam Powell        final ArrayList<RouteInfo> mRoutes = new ArrayList<RouteInfo>();
5439a1de308cea2d160778fd977825f10a07b49d738Adam Powell        private boolean mUpdateName;
5449a1de308cea2d160778fd977825f10a07b49d738Adam Powell
5459a1de308cea2d160778fd977825f10a07b49d738Adam Powell        RouteGroup(RouteCategory category) {
5469a1de308cea2d160778fd977825f10a07b49d738Adam Powell            super(category);
5479a1de308cea2d160778fd977825f10a07b49d738Adam Powell            mGroup = this;
5489a1de308cea2d160778fd977825f10a07b49d738Adam Powell        }
5499a1de308cea2d160778fd977825f10a07b49d738Adam Powell
5509a1de308cea2d160778fd977825f10a07b49d738Adam Powell        public CharSequence getName() {
5519a1de308cea2d160778fd977825f10a07b49d738Adam Powell            if (mUpdateName) updateName();
5529a1de308cea2d160778fd977825f10a07b49d738Adam Powell            return super.getName();
5539a1de308cea2d160778fd977825f10a07b49d738Adam Powell        }
5549a1de308cea2d160778fd977825f10a07b49d738Adam Powell
5559a1de308cea2d160778fd977825f10a07b49d738Adam Powell        /**
5569a1de308cea2d160778fd977825f10a07b49d738Adam Powell         * Add a route to this group. The route must not currently belong to another group.
5579a1de308cea2d160778fd977825f10a07b49d738Adam Powell         *
5589a1de308cea2d160778fd977825f10a07b49d738Adam Powell         * @param route route to add to this group
5599a1de308cea2d160778fd977825f10a07b49d738Adam Powell         */
5609a1de308cea2d160778fd977825f10a07b49d738Adam Powell        public void addRoute(RouteInfo route) {
5619a1de308cea2d160778fd977825f10a07b49d738Adam Powell            if (route.getGroup() != null) {
5629a1de308cea2d160778fd977825f10a07b49d738Adam Powell                throw new IllegalStateException("Route " + route + " is already part of a group.");
5639a1de308cea2d160778fd977825f10a07b49d738Adam Powell            }
5649a1de308cea2d160778fd977825f10a07b49d738Adam Powell            if (route.getCategory() != mCategory) {
5659a1de308cea2d160778fd977825f10a07b49d738Adam Powell                throw new IllegalArgumentException(
5669a1de308cea2d160778fd977825f10a07b49d738Adam Powell                        "Route cannot be added to a group with a different category. " +
5679a1de308cea2d160778fd977825f10a07b49d738Adam Powell                            "(Route category=" + route.getCategory() +
5689a1de308cea2d160778fd977825f10a07b49d738Adam Powell                            " group category=" + mCategory + ")");
5699a1de308cea2d160778fd977825f10a07b49d738Adam Powell            }
5709a1de308cea2d160778fd977825f10a07b49d738Adam Powell            mRoutes.add(route);
5719a1de308cea2d160778fd977825f10a07b49d738Adam Powell            mUpdateName = true;
5729a1de308cea2d160778fd977825f10a07b49d738Adam Powell            routeUpdated();
5739a1de308cea2d160778fd977825f10a07b49d738Adam Powell        }
5749a1de308cea2d160778fd977825f10a07b49d738Adam Powell
5759a1de308cea2d160778fd977825f10a07b49d738Adam Powell        /**
5769a1de308cea2d160778fd977825f10a07b49d738Adam Powell         * Add a route to this group before the specified index.
5779a1de308cea2d160778fd977825f10a07b49d738Adam Powell         *
5789a1de308cea2d160778fd977825f10a07b49d738Adam Powell         * @param route route to add
5799a1de308cea2d160778fd977825f10a07b49d738Adam Powell         * @param insertAt insert the new route before this index
5809a1de308cea2d160778fd977825f10a07b49d738Adam Powell         */
5819a1de308cea2d160778fd977825f10a07b49d738Adam Powell        public void addRoute(RouteInfo route, int insertAt) {
5829a1de308cea2d160778fd977825f10a07b49d738Adam Powell            if (route.getGroup() != null) {
5839a1de308cea2d160778fd977825f10a07b49d738Adam Powell                throw new IllegalStateException("Route " + route + " is already part of a group.");
5849a1de308cea2d160778fd977825f10a07b49d738Adam Powell            }
5859a1de308cea2d160778fd977825f10a07b49d738Adam Powell            if (route.getCategory() != mCategory) {
5869a1de308cea2d160778fd977825f10a07b49d738Adam Powell                throw new IllegalArgumentException(
5879a1de308cea2d160778fd977825f10a07b49d738Adam Powell                        "Route cannot be added to a group with a different category. " +
5889a1de308cea2d160778fd977825f10a07b49d738Adam Powell                            "(Route category=" + route.getCategory() +
5899a1de308cea2d160778fd977825f10a07b49d738Adam Powell                            " group category=" + mCategory + ")");
5909a1de308cea2d160778fd977825f10a07b49d738Adam Powell            }
5919a1de308cea2d160778fd977825f10a07b49d738Adam Powell            mRoutes.add(insertAt, route);
5929a1de308cea2d160778fd977825f10a07b49d738Adam Powell            mUpdateName = true;
5939a1de308cea2d160778fd977825f10a07b49d738Adam Powell            routeUpdated();
5949a1de308cea2d160778fd977825f10a07b49d738Adam Powell        }
5959a1de308cea2d160778fd977825f10a07b49d738Adam Powell
5969a1de308cea2d160778fd977825f10a07b49d738Adam Powell        /**
5979a1de308cea2d160778fd977825f10a07b49d738Adam Powell         * Remove a route from this group.
5989a1de308cea2d160778fd977825f10a07b49d738Adam Powell         *
5999a1de308cea2d160778fd977825f10a07b49d738Adam Powell         * @param route route to remove
6009a1de308cea2d160778fd977825f10a07b49d738Adam Powell         */
6019a1de308cea2d160778fd977825f10a07b49d738Adam Powell        public void removeRoute(RouteInfo route) {
6029a1de308cea2d160778fd977825f10a07b49d738Adam Powell            if (route.getGroup() != this) {
6039a1de308cea2d160778fd977825f10a07b49d738Adam Powell                throw new IllegalArgumentException("Route " + route +
6049a1de308cea2d160778fd977825f10a07b49d738Adam Powell                        " is not a member of this group.");
6059a1de308cea2d160778fd977825f10a07b49d738Adam Powell            }
6069a1de308cea2d160778fd977825f10a07b49d738Adam Powell            mRoutes.remove(route);
6079a1de308cea2d160778fd977825f10a07b49d738Adam Powell            mUpdateName = true;
6089a1de308cea2d160778fd977825f10a07b49d738Adam Powell            routeUpdated();
6099a1de308cea2d160778fd977825f10a07b49d738Adam Powell        }
6109a1de308cea2d160778fd977825f10a07b49d738Adam Powell
6119a1de308cea2d160778fd977825f10a07b49d738Adam Powell        /**
6129a1de308cea2d160778fd977825f10a07b49d738Adam Powell         * Remove the route at the specified index from this group.
6139a1de308cea2d160778fd977825f10a07b49d738Adam Powell         *
6149a1de308cea2d160778fd977825f10a07b49d738Adam Powell         * @param index index of the route to remove
6159a1de308cea2d160778fd977825f10a07b49d738Adam Powell         */
6169a1de308cea2d160778fd977825f10a07b49d738Adam Powell        public void removeRoute(int index) {
6179a1de308cea2d160778fd977825f10a07b49d738Adam Powell            mRoutes.remove(index);
6189a1de308cea2d160778fd977825f10a07b49d738Adam Powell            mUpdateName = true;
6199a1de308cea2d160778fd977825f10a07b49d738Adam Powell            routeUpdated();
6209a1de308cea2d160778fd977825f10a07b49d738Adam Powell        }
6219a1de308cea2d160778fd977825f10a07b49d738Adam Powell
6229a1de308cea2d160778fd977825f10a07b49d738Adam Powell        void memberNameChanged(RouteInfo info, CharSequence name) {
6239a1de308cea2d160778fd977825f10a07b49d738Adam Powell            mUpdateName = true;
6249a1de308cea2d160778fd977825f10a07b49d738Adam Powell            routeUpdated();
6259a1de308cea2d160778fd977825f10a07b49d738Adam Powell        }
6269a1de308cea2d160778fd977825f10a07b49d738Adam Powell
6279a1de308cea2d160778fd977825f10a07b49d738Adam Powell        void memberStatusChanged(RouteInfo info, CharSequence status) {
6289a1de308cea2d160778fd977825f10a07b49d738Adam Powell            setStatusInt(status);
6299a1de308cea2d160778fd977825f10a07b49d738Adam Powell        }
6309a1de308cea2d160778fd977825f10a07b49d738Adam Powell
6319a1de308cea2d160778fd977825f10a07b49d738Adam Powell        void updateName() {
6329a1de308cea2d160778fd977825f10a07b49d738Adam Powell            final StringBuilder sb = new StringBuilder();
6339a1de308cea2d160778fd977825f10a07b49d738Adam Powell            final int count = mRoutes.size();
6349a1de308cea2d160778fd977825f10a07b49d738Adam Powell            for (int i = 0; i < count; i++) {
6359a1de308cea2d160778fd977825f10a07b49d738Adam Powell                final RouteInfo info = mRoutes.get(i);
6369a1de308cea2d160778fd977825f10a07b49d738Adam Powell                if (i > 0) sb.append(", ");
6379a1de308cea2d160778fd977825f10a07b49d738Adam Powell                sb.append(info.mName);
6389a1de308cea2d160778fd977825f10a07b49d738Adam Powell            }
6399a1de308cea2d160778fd977825f10a07b49d738Adam Powell            mName = sb.toString();
6409a1de308cea2d160778fd977825f10a07b49d738Adam Powell            mUpdateName = false;
6419a1de308cea2d160778fd977825f10a07b49d738Adam Powell        }
6429a1de308cea2d160778fd977825f10a07b49d738Adam Powell    }
6439a1de308cea2d160778fd977825f10a07b49d738Adam Powell
6449a1de308cea2d160778fd977825f10a07b49d738Adam Powell    /**
6459a1de308cea2d160778fd977825f10a07b49d738Adam Powell     * Definition of a category of routes. All routes belong to a category.
6469a1de308cea2d160778fd977825f10a07b49d738Adam Powell     */
6479a1de308cea2d160778fd977825f10a07b49d738Adam Powell    public class RouteCategory {
6489a1de308cea2d160778fd977825f10a07b49d738Adam Powell        final ArrayList<RouteInfo> mRoutes = new ArrayList<RouteInfo>();
6499a1de308cea2d160778fd977825f10a07b49d738Adam Powell        CharSequence mName;
6509a1de308cea2d160778fd977825f10a07b49d738Adam Powell        int mTypes;
6519a1de308cea2d160778fd977825f10a07b49d738Adam Powell        final boolean mGroupable;
6529a1de308cea2d160778fd977825f10a07b49d738Adam Powell
6539a1de308cea2d160778fd977825f10a07b49d738Adam Powell        RouteCategory(CharSequence name, int types, boolean groupable) {
6549a1de308cea2d160778fd977825f10a07b49d738Adam Powell            mName = name;
6559a1de308cea2d160778fd977825f10a07b49d738Adam Powell            mTypes = types;
6569a1de308cea2d160778fd977825f10a07b49d738Adam Powell            mGroupable = groupable;
6579a1de308cea2d160778fd977825f10a07b49d738Adam Powell        }
6589a1de308cea2d160778fd977825f10a07b49d738Adam Powell
6599a1de308cea2d160778fd977825f10a07b49d738Adam Powell        /**
6609a1de308cea2d160778fd977825f10a07b49d738Adam Powell         * @return the name of this route category
6619a1de308cea2d160778fd977825f10a07b49d738Adam Powell         */
6629a1de308cea2d160778fd977825f10a07b49d738Adam Powell        public CharSequence getName() {
6639a1de308cea2d160778fd977825f10a07b49d738Adam Powell            return mName;
6649a1de308cea2d160778fd977825f10a07b49d738Adam Powell        }
6659a1de308cea2d160778fd977825f10a07b49d738Adam Powell
6669a1de308cea2d160778fd977825f10a07b49d738Adam Powell        /**
6679a1de308cea2d160778fd977825f10a07b49d738Adam Powell         * @return the number of routes in this category
6689a1de308cea2d160778fd977825f10a07b49d738Adam Powell         */
6699a1de308cea2d160778fd977825f10a07b49d738Adam Powell        public int getRouteCount() {
6709a1de308cea2d160778fd977825f10a07b49d738Adam Powell            return mRoutes.size();
6719a1de308cea2d160778fd977825f10a07b49d738Adam Powell        }
6729a1de308cea2d160778fd977825f10a07b49d738Adam Powell
6739a1de308cea2d160778fd977825f10a07b49d738Adam Powell        /**
6749a1de308cea2d160778fd977825f10a07b49d738Adam Powell         * Return a route from this category
6759a1de308cea2d160778fd977825f10a07b49d738Adam Powell         *
6769a1de308cea2d160778fd977825f10a07b49d738Adam Powell         * @param index Index from [0-getRouteCount)
6779a1de308cea2d160778fd977825f10a07b49d738Adam Powell         * @return the route at the given index
6789a1de308cea2d160778fd977825f10a07b49d738Adam Powell         */
6799a1de308cea2d160778fd977825f10a07b49d738Adam Powell        public RouteInfo getRouteAt(int index) {
6809a1de308cea2d160778fd977825f10a07b49d738Adam Powell            return mRoutes.get(index);
6819a1de308cea2d160778fd977825f10a07b49d738Adam Powell        }
6829a1de308cea2d160778fd977825f10a07b49d738Adam Powell
6839a1de308cea2d160778fd977825f10a07b49d738Adam Powell        /**
6849a1de308cea2d160778fd977825f10a07b49d738Adam Powell         * @return Flag set describing the route types supported by this category
6859a1de308cea2d160778fd977825f10a07b49d738Adam Powell         */
6869a1de308cea2d160778fd977825f10a07b49d738Adam Powell        public int getSupportedTypes() {
6879a1de308cea2d160778fd977825f10a07b49d738Adam Powell            return mTypes;
6889a1de308cea2d160778fd977825f10a07b49d738Adam Powell        }
6899a1de308cea2d160778fd977825f10a07b49d738Adam Powell
6909a1de308cea2d160778fd977825f10a07b49d738Adam Powell        /**
6919a1de308cea2d160778fd977825f10a07b49d738Adam Powell         * Return whether or not this category supports grouping.
6929a1de308cea2d160778fd977825f10a07b49d738Adam Powell         *
6939a1de308cea2d160778fd977825f10a07b49d738Adam Powell         * <p>If this method returns true, all routes obtained from this category
6949a1de308cea2d160778fd977825f10a07b49d738Adam Powell         * via calls to {@link #getRouteAt(int)} will be {@link MediaRouter.RouteGroup}s.
6959a1de308cea2d160778fd977825f10a07b49d738Adam Powell         *
6969a1de308cea2d160778fd977825f10a07b49d738Adam Powell         * @return true if this category supports
6979a1de308cea2d160778fd977825f10a07b49d738Adam Powell         */
6989a1de308cea2d160778fd977825f10a07b49d738Adam Powell        public boolean isGroupable() {
6999a1de308cea2d160778fd977825f10a07b49d738Adam Powell            return mGroupable;
7009a1de308cea2d160778fd977825f10a07b49d738Adam Powell        }
7019a1de308cea2d160778fd977825f10a07b49d738Adam Powell
7029a1de308cea2d160778fd977825f10a07b49d738Adam Powell        public String toString() {
7039a1de308cea2d160778fd977825f10a07b49d738Adam Powell            return "RouteCategory{ name=" + mName + " types=" + typesToString(mTypes) +
7049a1de308cea2d160778fd977825f10a07b49d738Adam Powell                    " groupable=" + mGroupable + " routes=" + mRoutes.size() + " }";
7059a1de308cea2d160778fd977825f10a07b49d738Adam Powell        }
7069a1de308cea2d160778fd977825f10a07b49d738Adam Powell    }
7079a1de308cea2d160778fd977825f10a07b49d738Adam Powell
7089a1de308cea2d160778fd977825f10a07b49d738Adam Powell    static class CallbackInfo {
7099a1de308cea2d160778fd977825f10a07b49d738Adam Powell        public int type;
7109a1de308cea2d160778fd977825f10a07b49d738Adam Powell        public Callback cb;
7119a1de308cea2d160778fd977825f10a07b49d738Adam Powell
7129a1de308cea2d160778fd977825f10a07b49d738Adam Powell        public CallbackInfo(Callback cb, int type) {
7139a1de308cea2d160778fd977825f10a07b49d738Adam Powell            this.cb = cb;
7149a1de308cea2d160778fd977825f10a07b49d738Adam Powell            this.type = type;
7159a1de308cea2d160778fd977825f10a07b49d738Adam Powell        }
7169a1de308cea2d160778fd977825f10a07b49d738Adam Powell    }
7179a1de308cea2d160778fd977825f10a07b49d738Adam Powell
7189a1de308cea2d160778fd977825f10a07b49d738Adam Powell    /**
7199a1de308cea2d160778fd977825f10a07b49d738Adam Powell     * Interface for receiving events about media routing changes.
7209a1de308cea2d160778fd977825f10a07b49d738Adam Powell     * All methods of this interface will be called from the application's main thread.
7219a1de308cea2d160778fd977825f10a07b49d738Adam Powell     *
7229a1de308cea2d160778fd977825f10a07b49d738Adam Powell     * <p>A Callback will only receive events relevant to routes that the callback
7239a1de308cea2d160778fd977825f10a07b49d738Adam Powell     * was registered for.</p>
7249a1de308cea2d160778fd977825f10a07b49d738Adam Powell     *
7259a1de308cea2d160778fd977825f10a07b49d738Adam Powell     * @see MediaRouter#addCallback(int, Callback)
7269a1de308cea2d160778fd977825f10a07b49d738Adam Powell     * @see MediaRouter#removeCallback(Callback)
7279a1de308cea2d160778fd977825f10a07b49d738Adam Powell     */
7289a1de308cea2d160778fd977825f10a07b49d738Adam Powell    public interface Callback {
7299a1de308cea2d160778fd977825f10a07b49d738Adam Powell        /**
7309a1de308cea2d160778fd977825f10a07b49d738Adam Powell         * Called when the supplied route becomes selected as the active route
7319a1de308cea2d160778fd977825f10a07b49d738Adam Powell         * for the given route type.
7329a1de308cea2d160778fd977825f10a07b49d738Adam Powell         *
7339a1de308cea2d160778fd977825f10a07b49d738Adam Powell         * @param type Type flag set indicating the routes that have been selected
7349a1de308cea2d160778fd977825f10a07b49d738Adam Powell         * @param info Route that has been selected for the given route types
7359a1de308cea2d160778fd977825f10a07b49d738Adam Powell         */
7369a1de308cea2d160778fd977825f10a07b49d738Adam Powell        public void onRouteSelected(int type, RouteInfo info);
7379a1de308cea2d160778fd977825f10a07b49d738Adam Powell
7389a1de308cea2d160778fd977825f10a07b49d738Adam Powell        /**
7399a1de308cea2d160778fd977825f10a07b49d738Adam Powell         * Called when the supplied route becomes unselected as the active route
7409a1de308cea2d160778fd977825f10a07b49d738Adam Powell         * for the given route type.
7419a1de308cea2d160778fd977825f10a07b49d738Adam Powell         *
7429a1de308cea2d160778fd977825f10a07b49d738Adam Powell         * @param type Type flag set indicating the routes that have been unselected
7439a1de308cea2d160778fd977825f10a07b49d738Adam Powell         * @param info Route that has been unselected for the given route types
7449a1de308cea2d160778fd977825f10a07b49d738Adam Powell         */
7459a1de308cea2d160778fd977825f10a07b49d738Adam Powell        public void onRouteUnselected(int type, RouteInfo info);
7469a1de308cea2d160778fd977825f10a07b49d738Adam Powell
7479a1de308cea2d160778fd977825f10a07b49d738Adam Powell        /**
7489a1de308cea2d160778fd977825f10a07b49d738Adam Powell         * Called when the volume is changed for the specified route types.
7499a1de308cea2d160778fd977825f10a07b49d738Adam Powell         *
7509a1de308cea2d160778fd977825f10a07b49d738Adam Powell         * @param type Type flags indicating which volume type was changed
7519a1de308cea2d160778fd977825f10a07b49d738Adam Powell         * @param volume New volume value in the range 0 (inaudible) to 1 (full)
7529a1de308cea2d160778fd977825f10a07b49d738Adam Powell         */
7539a1de308cea2d160778fd977825f10a07b49d738Adam Powell        public void onVolumeChanged(int type, float volume);
7549a1de308cea2d160778fd977825f10a07b49d738Adam Powell
7559a1de308cea2d160778fd977825f10a07b49d738Adam Powell        /**
7569a1de308cea2d160778fd977825f10a07b49d738Adam Powell         * Called when a route for the specified type was added.
7579a1de308cea2d160778fd977825f10a07b49d738Adam Powell         *
7589a1de308cea2d160778fd977825f10a07b49d738Adam Powell         * @param type Type flags indicating which types the added route supports
7599a1de308cea2d160778fd977825f10a07b49d738Adam Powell         * @param info Route that has become available for use
7609a1de308cea2d160778fd977825f10a07b49d738Adam Powell         */
7619a1de308cea2d160778fd977825f10a07b49d738Adam Powell        public void onRouteAdded(int type, RouteInfo info);
7629a1de308cea2d160778fd977825f10a07b49d738Adam Powell
7639a1de308cea2d160778fd977825f10a07b49d738Adam Powell        /**
7649a1de308cea2d160778fd977825f10a07b49d738Adam Powell         * Called when a route for the specified type was removed.
7659a1de308cea2d160778fd977825f10a07b49d738Adam Powell         *
7669a1de308cea2d160778fd977825f10a07b49d738Adam Powell         * @param type Type flags indicating which types the removed route supported
7679a1de308cea2d160778fd977825f10a07b49d738Adam Powell         * @param info Route that has been removed from availability
7689a1de308cea2d160778fd977825f10a07b49d738Adam Powell         */
7699a1de308cea2d160778fd977825f10a07b49d738Adam Powell        public void onRouteRemoved(int type, RouteInfo info);
7709a1de308cea2d160778fd977825f10a07b49d738Adam Powell
7719a1de308cea2d160778fd977825f10a07b49d738Adam Powell        /**
7729a1de308cea2d160778fd977825f10a07b49d738Adam Powell         * Called when an aspect of the indicated route has changed.
7739a1de308cea2d160778fd977825f10a07b49d738Adam Powell         *
7749a1de308cea2d160778fd977825f10a07b49d738Adam Powell         * <p>This will not indicate that the types supported by this route have
7759a1de308cea2d160778fd977825f10a07b49d738Adam Powell         * changed, only that cosmetic info such as name or status have been updated.</p>
7769a1de308cea2d160778fd977825f10a07b49d738Adam Powell         *
7779a1de308cea2d160778fd977825f10a07b49d738Adam Powell         * @param info The route that was changed
7789a1de308cea2d160778fd977825f10a07b49d738Adam Powell         */
7799a1de308cea2d160778fd977825f10a07b49d738Adam Powell        public void onRouteChanged(RouteInfo info);
7809a1de308cea2d160778fd977825f10a07b49d738Adam Powell    }
7819a1de308cea2d160778fd977825f10a07b49d738Adam Powell
7829a1de308cea2d160778fd977825f10a07b49d738Adam Powell    /**
7839a1de308cea2d160778fd977825f10a07b49d738Adam Powell     * Stub implementation of the {@link MediaRouter.Callback} interface.
7849a1de308cea2d160778fd977825f10a07b49d738Adam Powell     * Each interface method is defined as a no-op. Override just the ones
7859a1de308cea2d160778fd977825f10a07b49d738Adam Powell     * you need.
7869a1de308cea2d160778fd977825f10a07b49d738Adam Powell     */
7879a1de308cea2d160778fd977825f10a07b49d738Adam Powell    public static class SimpleCallback implements Callback {
7889a1de308cea2d160778fd977825f10a07b49d738Adam Powell
7899a1de308cea2d160778fd977825f10a07b49d738Adam Powell        @Override
7909a1de308cea2d160778fd977825f10a07b49d738Adam Powell        public void onRouteSelected(int type, RouteInfo info) {
7919a1de308cea2d160778fd977825f10a07b49d738Adam Powell
7929a1de308cea2d160778fd977825f10a07b49d738Adam Powell        }
7939a1de308cea2d160778fd977825f10a07b49d738Adam Powell
7949a1de308cea2d160778fd977825f10a07b49d738Adam Powell        @Override
7959a1de308cea2d160778fd977825f10a07b49d738Adam Powell        public void onRouteUnselected(int type, RouteInfo info) {
7969a1de308cea2d160778fd977825f10a07b49d738Adam Powell
7979a1de308cea2d160778fd977825f10a07b49d738Adam Powell        }
7989a1de308cea2d160778fd977825f10a07b49d738Adam Powell
7999a1de308cea2d160778fd977825f10a07b49d738Adam Powell        @Override
8009a1de308cea2d160778fd977825f10a07b49d738Adam Powell        public void onVolumeChanged(int type, float volume) {
8019a1de308cea2d160778fd977825f10a07b49d738Adam Powell
8029a1de308cea2d160778fd977825f10a07b49d738Adam Powell        }
8039a1de308cea2d160778fd977825f10a07b49d738Adam Powell
8049a1de308cea2d160778fd977825f10a07b49d738Adam Powell        @Override
8059a1de308cea2d160778fd977825f10a07b49d738Adam Powell        public void onRouteAdded(int type, RouteInfo info) {
8069a1de308cea2d160778fd977825f10a07b49d738Adam Powell
8079a1de308cea2d160778fd977825f10a07b49d738Adam Powell        }
8089a1de308cea2d160778fd977825f10a07b49d738Adam Powell
8099a1de308cea2d160778fd977825f10a07b49d738Adam Powell        @Override
8109a1de308cea2d160778fd977825f10a07b49d738Adam Powell        public void onRouteRemoved(int type, RouteInfo info) {
8119a1de308cea2d160778fd977825f10a07b49d738Adam Powell
8129a1de308cea2d160778fd977825f10a07b49d738Adam Powell        }
8139a1de308cea2d160778fd977825f10a07b49d738Adam Powell
8149a1de308cea2d160778fd977825f10a07b49d738Adam Powell        @Override
8159a1de308cea2d160778fd977825f10a07b49d738Adam Powell        public void onRouteChanged(RouteInfo info) {
8169a1de308cea2d160778fd977825f10a07b49d738Adam Powell
8179a1de308cea2d160778fd977825f10a07b49d738Adam Powell        }
8189a1de308cea2d160778fd977825f10a07b49d738Adam Powell
8199a1de308cea2d160778fd977825f10a07b49d738Adam Powell    }
8209a1de308cea2d160778fd977825f10a07b49d738Adam Powell
8219a1de308cea2d160778fd977825f10a07b49d738Adam Powell    class VolumeChangedBroadcastReceiver extends BroadcastReceiver {
8229a1de308cea2d160778fd977825f10a07b49d738Adam Powell        @Override
8239a1de308cea2d160778fd977825f10a07b49d738Adam Powell        public void onReceive(Context context, Intent intent) {
8249a1de308cea2d160778fd977825f10a07b49d738Adam Powell            final String action = intent.getAction();
8259a1de308cea2d160778fd977825f10a07b49d738Adam Powell            if (AudioManager.VOLUME_CHANGED_ACTION.equals(action) &&
8269a1de308cea2d160778fd977825f10a07b49d738Adam Powell                    AudioManager.STREAM_MUSIC == intent.getIntExtra(
8279a1de308cea2d160778fd977825f10a07b49d738Adam Powell                            AudioManager.EXTRA_VOLUME_STREAM_TYPE, -1)) {
8289a1de308cea2d160778fd977825f10a07b49d738Adam Powell                final int maxVol = mAudioManager.getStreamMaxVolume(AudioManager.STREAM_MUSIC);
8299a1de308cea2d160778fd977825f10a07b49d738Adam Powell                final int volExtra = intent.getIntExtra(AudioManager.EXTRA_VOLUME_STREAM_VALUE, 0);
8309a1de308cea2d160778fd977825f10a07b49d738Adam Powell                final float volume = (float) volExtra / maxVol;
8319a1de308cea2d160778fd977825f10a07b49d738Adam Powell                dispatchVolumeChanged(ROUTE_TYPE_LIVE_AUDIO, volume);
8329a1de308cea2d160778fd977825f10a07b49d738Adam Powell            }
8339a1de308cea2d160778fd977825f10a07b49d738Adam Powell        }
8349a1de308cea2d160778fd977825f10a07b49d738Adam Powell    }
8359a1de308cea2d160778fd977825f10a07b49d738Adam Powell
8369a1de308cea2d160778fd977825f10a07b49d738Adam Powell    class BtChangedBroadcastReceiver extends BroadcastReceiver {
8379a1de308cea2d160778fd977825f10a07b49d738Adam Powell        @Override
8389a1de308cea2d160778fd977825f10a07b49d738Adam Powell        public void onReceive(Context context, Intent intent) {
8399a1de308cea2d160778fd977825f10a07b49d738Adam Powell            final String action = intent.getAction();
8409a1de308cea2d160778fd977825f10a07b49d738Adam Powell            if (BluetoothA2dp.ACTION_CONNECTION_STATE_CHANGED.equals(action)) {
8419a1de308cea2d160778fd977825f10a07b49d738Adam Powell                final int state = intent.getIntExtra(BluetoothA2dp.EXTRA_STATE, -1);
8429a1de308cea2d160778fd977825f10a07b49d738Adam Powell                if (state == BluetoothA2dp.STATE_CONNECTED) {
8439a1de308cea2d160778fd977825f10a07b49d738Adam Powell                    onA2dpDeviceConnected();
8449a1de308cea2d160778fd977825f10a07b49d738Adam Powell                } else if (state == BluetoothA2dp.STATE_DISCONNECTING ||
8459a1de308cea2d160778fd977825f10a07b49d738Adam Powell                        state == BluetoothA2dp.STATE_DISCONNECTED) {
8469a1de308cea2d160778fd977825f10a07b49d738Adam Powell                    onA2dpDeviceDisconnected();
8479a1de308cea2d160778fd977825f10a07b49d738Adam Powell                }
8489a1de308cea2d160778fd977825f10a07b49d738Adam Powell            }
8499a1de308cea2d160778fd977825f10a07b49d738Adam Powell        }
8509a1de308cea2d160778fd977825f10a07b49d738Adam Powell    }
8519a1de308cea2d160778fd977825f10a07b49d738Adam Powell
8529a1de308cea2d160778fd977825f10a07b49d738Adam Powell    class HeadphoneChangedBroadcastReceiver extends BroadcastReceiver {
8539a1de308cea2d160778fd977825f10a07b49d738Adam Powell        @Override
8549a1de308cea2d160778fd977825f10a07b49d738Adam Powell        public void onReceive(Context context, Intent intent) {
8559a1de308cea2d160778fd977825f10a07b49d738Adam Powell            final String action = intent.getAction();
8569a1de308cea2d160778fd977825f10a07b49d738Adam Powell            if (Intent.ACTION_HEADSET_PLUG.equals(action)) {
8579a1de308cea2d160778fd977825f10a07b49d738Adam Powell                final boolean plugged = intent.getIntExtra("state", 0) != 0;
8589a1de308cea2d160778fd977825f10a07b49d738Adam Powell                final String name = mAppContext.getString(
8599a1de308cea2d160778fd977825f10a07b49d738Adam Powell                        com.android.internal.R.string.default_audio_route_name_headphones);
8609a1de308cea2d160778fd977825f10a07b49d738Adam Powell                onHeadphonesPlugged(plugged, name);
8619a1de308cea2d160778fd977825f10a07b49d738Adam Powell            } else if (Intent.ACTION_ANALOG_AUDIO_DOCK_PLUG.equals(action) ||
8629a1de308cea2d160778fd977825f10a07b49d738Adam Powell                    Intent.ACTION_DIGITAL_AUDIO_DOCK_PLUG.equals(action)) {
8639a1de308cea2d160778fd977825f10a07b49d738Adam Powell                final boolean plugged = intent.getIntExtra("state", 0) != 0;
8649a1de308cea2d160778fd977825f10a07b49d738Adam Powell                final String name = mAppContext.getString(
8659a1de308cea2d160778fd977825f10a07b49d738Adam Powell                        com.android.internal.R.string.default_audio_route_name_dock_speakers);
8669a1de308cea2d160778fd977825f10a07b49d738Adam Powell                onHeadphonesPlugged(plugged, name);
8679a1de308cea2d160778fd977825f10a07b49d738Adam Powell            } else if (Intent.ACTION_HDMI_AUDIO_PLUG.equals(action)) {
8689a1de308cea2d160778fd977825f10a07b49d738Adam Powell                final boolean plugged = intent.getIntExtra("state", 0) != 0;
8699a1de308cea2d160778fd977825f10a07b49d738Adam Powell                final String name = mAppContext.getString(
8709a1de308cea2d160778fd977825f10a07b49d738Adam Powell                        com.android.internal.R.string.default_audio_route_name_hdmi);
8719a1de308cea2d160778fd977825f10a07b49d738Adam Powell                onHeadphonesPlugged(plugged, name);
8729a1de308cea2d160778fd977825f10a07b49d738Adam Powell            }
8739a1de308cea2d160778fd977825f10a07b49d738Adam Powell        }
8749a1de308cea2d160778fd977825f10a07b49d738Adam Powell    }
8759a1de308cea2d160778fd977825f10a07b49d738Adam Powell}
876