MediaRouter.java revision 1357012968f9066ea3051d83995e9bac69526c3c
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.content.Context;
20b58b8f832d06b0ffa8886eba5a4916578a3b8743Dianne Hackbornimport android.content.res.Resources;
21ae20ae1a8aaa013813c356ae1d9541ca7ff020aeAdam Powellimport android.graphics.drawable.Drawable;
229a1de308cea2d160778fd977825f10a07b49d738Adam Powellimport android.os.Handler;
23632ca417f0a33e3fa9ccece531afa2db3f0d4a30Dianne Hackbornimport android.os.IBinder;
24632ca417f0a33e3fa9ccece531afa2db3f0d4a30Dianne Hackbornimport android.os.RemoteException;
25632ca417f0a33e3fa9ccece531afa2db3f0d4a30Dianne Hackbornimport android.os.ServiceManager;
26632ca417f0a33e3fa9ccece531afa2db3f0d4a30Dianne Hackbornimport android.text.TextUtils;
279a1de308cea2d160778fd977825f10a07b49d738Adam Powellimport android.util.Log;
289a1de308cea2d160778fd977825f10a07b49d738Adam Powell
299a1de308cea2d160778fd977825f10a07b49d738Adam Powellimport java.util.ArrayList;
309a1de308cea2d160778fd977825f10a07b49d738Adam Powellimport java.util.HashMap;
31d0d2cda9d414da73773285d7fee9e13aef3495e9Adam Powellimport java.util.List;
3239d5c6172503620ac3761148adac5fd7fa20d02dAdam Powellimport java.util.concurrent.CopyOnWriteArrayList;
339a1de308cea2d160778fd977825f10a07b49d738Adam Powell
349a1de308cea2d160778fd977825f10a07b49d738Adam Powell/**
359a1de308cea2d160778fd977825f10a07b49d738Adam Powell * MediaRouter allows applications to control the routing of media channels
369a1de308cea2d160778fd977825f10a07b49d738Adam Powell * and streams from the current device to external speakers and destination devices.
379a1de308cea2d160778fd977825f10a07b49d738Adam Powell *
38b58b8f832d06b0ffa8886eba5a4916578a3b8743Dianne Hackborn * <p>A MediaRouter is retrieved through {@link Context#getSystemService(String)
39b58b8f832d06b0ffa8886eba5a4916578a3b8743Dianne Hackborn * Context.getSystemService()} of a {@link Context#MEDIA_ROUTER_SERVICE
40b58b8f832d06b0ffa8886eba5a4916578a3b8743Dianne Hackborn * Context.MEDIA_ROUTER_SERVICE}.
41b58b8f832d06b0ffa8886eba5a4916578a3b8743Dianne Hackborn *
42b58b8f832d06b0ffa8886eba5a4916578a3b8743Dianne Hackborn * <p>The media router API is not thread-safe; all interactions with it must be
43b58b8f832d06b0ffa8886eba5a4916578a3b8743Dianne Hackborn * done from the main thread of the process.</p>
449a1de308cea2d160778fd977825f10a07b49d738Adam Powell */
459a1de308cea2d160778fd977825f10a07b49d738Adam Powellpublic class MediaRouter {
469a1de308cea2d160778fd977825f10a07b49d738Adam Powell    private static final String TAG = "MediaRouter";
479a1de308cea2d160778fd977825f10a07b49d738Adam Powell
48b58b8f832d06b0ffa8886eba5a4916578a3b8743Dianne Hackborn    static class Static {
49b35c445f34e1a18e17aef3e3dfbc1c39b4d1815cAdam Powell        final Resources mResources;
50632ca417f0a33e3fa9ccece531afa2db3f0d4a30Dianne Hackborn        final IAudioService mAudioService;
51b35c445f34e1a18e17aef3e3dfbc1c39b4d1815cAdam Powell        final Handler mHandler;
5239d5c6172503620ac3761148adac5fd7fa20d02dAdam Powell        final CopyOnWriteArrayList<CallbackInfo> mCallbacks =
5339d5c6172503620ac3761148adac5fd7fa20d02dAdam Powell                new CopyOnWriteArrayList<CallbackInfo>();
54b58b8f832d06b0ffa8886eba5a4916578a3b8743Dianne Hackborn
55b35c445f34e1a18e17aef3e3dfbc1c39b4d1815cAdam Powell        final ArrayList<RouteInfo> mRoutes = new ArrayList<RouteInfo>();
56b35c445f34e1a18e17aef3e3dfbc1c39b4d1815cAdam Powell        final ArrayList<RouteCategory> mCategories = new ArrayList<RouteCategory>();
57b58b8f832d06b0ffa8886eba5a4916578a3b8743Dianne Hackborn
58b35c445f34e1a18e17aef3e3dfbc1c39b4d1815cAdam Powell        final RouteCategory mSystemCategory;
59632ca417f0a33e3fa9ccece531afa2db3f0d4a30Dianne Hackborn
60632ca417f0a33e3fa9ccece531afa2db3f0d4a30Dianne Hackborn        final AudioRoutesInfo mCurRoutesInfo = new AudioRoutesInfo();
61b58b8f832d06b0ffa8886eba5a4916578a3b8743Dianne Hackborn
62b35c445f34e1a18e17aef3e3dfbc1c39b4d1815cAdam Powell        RouteInfo mDefaultAudio;
63b35c445f34e1a18e17aef3e3dfbc1c39b4d1815cAdam Powell        RouteInfo mBluetoothA2dpRoute;
64b58b8f832d06b0ffa8886eba5a4916578a3b8743Dianne Hackborn
65b35c445f34e1a18e17aef3e3dfbc1c39b4d1815cAdam Powell        RouteInfo mSelectedRoute;
669a1de308cea2d160778fd977825f10a07b49d738Adam Powell
67632ca417f0a33e3fa9ccece531afa2db3f0d4a30Dianne Hackborn        final IAudioRoutesObserver.Stub mRoutesObserver = new IAudioRoutesObserver.Stub() {
68632ca417f0a33e3fa9ccece531afa2db3f0d4a30Dianne Hackborn            public void dispatchAudioRoutesChanged(final AudioRoutesInfo newRoutes) {
69632ca417f0a33e3fa9ccece531afa2db3f0d4a30Dianne Hackborn                mHandler.post(new Runnable() {
70632ca417f0a33e3fa9ccece531afa2db3f0d4a30Dianne Hackborn                    @Override public void run() {
71632ca417f0a33e3fa9ccece531afa2db3f0d4a30Dianne Hackborn                        updateRoutes(newRoutes);
72632ca417f0a33e3fa9ccece531afa2db3f0d4a30Dianne Hackborn                    }
73632ca417f0a33e3fa9ccece531afa2db3f0d4a30Dianne Hackborn                });
74632ca417f0a33e3fa9ccece531afa2db3f0d4a30Dianne Hackborn            }
75632ca417f0a33e3fa9ccece531afa2db3f0d4a30Dianne Hackborn        };
76632ca417f0a33e3fa9ccece531afa2db3f0d4a30Dianne Hackborn
77b58b8f832d06b0ffa8886eba5a4916578a3b8743Dianne Hackborn        Static(Context appContext) {
78b58b8f832d06b0ffa8886eba5a4916578a3b8743Dianne Hackborn            mResources = Resources.getSystem();
79b58b8f832d06b0ffa8886eba5a4916578a3b8743Dianne Hackborn            mHandler = new Handler(appContext.getMainLooper());
809a1de308cea2d160778fd977825f10a07b49d738Adam Powell
81632ca417f0a33e3fa9ccece531afa2db3f0d4a30Dianne Hackborn            IBinder b = ServiceManager.getService(Context.AUDIO_SERVICE);
82632ca417f0a33e3fa9ccece531afa2db3f0d4a30Dianne Hackborn            mAudioService = IAudioService.Stub.asInterface(b);
839a1de308cea2d160778fd977825f10a07b49d738Adam Powell
84dd0a19266d5c837069da1ea188744d54c8d723a8Adam Powell            mSystemCategory = new RouteCategory(
85dd0a19266d5c837069da1ea188744d54c8d723a8Adam Powell                    com.android.internal.R.string.default_audio_route_category_name,
86b58b8f832d06b0ffa8886eba5a4916578a3b8743Dianne Hackborn                    ROUTE_TYPE_LIVE_AUDIO, false);
87b35c445f34e1a18e17aef3e3dfbc1c39b4d1815cAdam Powell        }
88b58b8f832d06b0ffa8886eba5a4916578a3b8743Dianne Hackborn
89b35c445f34e1a18e17aef3e3dfbc1c39b4d1815cAdam Powell        // Called after sStatic is initialized
90632ca417f0a33e3fa9ccece531afa2db3f0d4a30Dianne Hackborn        void startMonitoringRoutes() {
91b58b8f832d06b0ffa8886eba5a4916578a3b8743Dianne Hackborn            mDefaultAudio = new RouteInfo(mSystemCategory);
920d03c042f90bf62d5bad7c64e77028a5f9f8fae0Adam Powell            mDefaultAudio.mNameResId = com.android.internal.R.string.default_audio_route_name;
93b58b8f832d06b0ffa8886eba5a4916578a3b8743Dianne Hackborn            mDefaultAudio.mSupportedTypes = ROUTE_TYPE_LIVE_AUDIO;
94b58b8f832d06b0ffa8886eba5a4916578a3b8743Dianne Hackborn            addRoute(mDefaultAudio);
95632ca417f0a33e3fa9ccece531afa2db3f0d4a30Dianne Hackborn
96632ca417f0a33e3fa9ccece531afa2db3f0d4a30Dianne Hackborn            AudioRoutesInfo newRoutes = null;
97632ca417f0a33e3fa9ccece531afa2db3f0d4a30Dianne Hackborn            try {
98632ca417f0a33e3fa9ccece531afa2db3f0d4a30Dianne Hackborn                newRoutes = mAudioService.startWatchingRoutes(mRoutesObserver);
99632ca417f0a33e3fa9ccece531afa2db3f0d4a30Dianne Hackborn            } catch (RemoteException e) {
100632ca417f0a33e3fa9ccece531afa2db3f0d4a30Dianne Hackborn            }
101632ca417f0a33e3fa9ccece531afa2db3f0d4a30Dianne Hackborn            if (newRoutes != null) {
102632ca417f0a33e3fa9ccece531afa2db3f0d4a30Dianne Hackborn                updateRoutes(newRoutes);
103632ca417f0a33e3fa9ccece531afa2db3f0d4a30Dianne Hackborn            }
104632ca417f0a33e3fa9ccece531afa2db3f0d4a30Dianne Hackborn        }
105632ca417f0a33e3fa9ccece531afa2db3f0d4a30Dianne Hackborn
106632ca417f0a33e3fa9ccece531afa2db3f0d4a30Dianne Hackborn        void updateRoutes(AudioRoutesInfo newRoutes) {
107632ca417f0a33e3fa9ccece531afa2db3f0d4a30Dianne Hackborn            if (newRoutes.mMainType != mCurRoutesInfo.mMainType) {
108632ca417f0a33e3fa9ccece531afa2db3f0d4a30Dianne Hackborn                mCurRoutesInfo.mMainType = newRoutes.mMainType;
109632ca417f0a33e3fa9ccece531afa2db3f0d4a30Dianne Hackborn                int name;
110632ca417f0a33e3fa9ccece531afa2db3f0d4a30Dianne Hackborn                if ((newRoutes.mMainType&AudioRoutesInfo.MAIN_HEADPHONES) != 0
111632ca417f0a33e3fa9ccece531afa2db3f0d4a30Dianne Hackborn                        || (newRoutes.mMainType&AudioRoutesInfo.MAIN_HEADSET) != 0) {
112632ca417f0a33e3fa9ccece531afa2db3f0d4a30Dianne Hackborn                    name = com.android.internal.R.string.default_audio_route_name_headphones;
113632ca417f0a33e3fa9ccece531afa2db3f0d4a30Dianne Hackborn                } else if ((newRoutes.mMainType&AudioRoutesInfo.MAIN_DOCK_SPEAKERS) != 0) {
114632ca417f0a33e3fa9ccece531afa2db3f0d4a30Dianne Hackborn                    name = com.android.internal.R.string.default_audio_route_name_dock_speakers;
115632ca417f0a33e3fa9ccece531afa2db3f0d4a30Dianne Hackborn                } else if ((newRoutes.mMainType&AudioRoutesInfo.MAIN_HDMI) != 0) {
116632ca417f0a33e3fa9ccece531afa2db3f0d4a30Dianne Hackborn                    name = com.android.internal.R.string.default_audio_route_name_hdmi;
117632ca417f0a33e3fa9ccece531afa2db3f0d4a30Dianne Hackborn                } else {
118632ca417f0a33e3fa9ccece531afa2db3f0d4a30Dianne Hackborn                    name = com.android.internal.R.string.default_audio_route_name;
119632ca417f0a33e3fa9ccece531afa2db3f0d4a30Dianne Hackborn                }
120632ca417f0a33e3fa9ccece531afa2db3f0d4a30Dianne Hackborn                sStatic.mDefaultAudio.mNameResId = name;
121632ca417f0a33e3fa9ccece531afa2db3f0d4a30Dianne Hackborn                dispatchRouteChanged(sStatic.mDefaultAudio);
122632ca417f0a33e3fa9ccece531afa2db3f0d4a30Dianne Hackborn            }
123632ca417f0a33e3fa9ccece531afa2db3f0d4a30Dianne Hackborn            if (!TextUtils.equals(newRoutes.mBluetoothName, mCurRoutesInfo.mBluetoothName)) {
124632ca417f0a33e3fa9ccece531afa2db3f0d4a30Dianne Hackborn                mCurRoutesInfo.mBluetoothName = newRoutes.mBluetoothName;
125632ca417f0a33e3fa9ccece531afa2db3f0d4a30Dianne Hackborn                if (mCurRoutesInfo.mBluetoothName != null) {
126632ca417f0a33e3fa9ccece531afa2db3f0d4a30Dianne Hackborn                    if (sStatic.mBluetoothA2dpRoute == null) {
127632ca417f0a33e3fa9ccece531afa2db3f0d4a30Dianne Hackborn                        final RouteInfo info = new RouteInfo(sStatic.mSystemCategory);
128632ca417f0a33e3fa9ccece531afa2db3f0d4a30Dianne Hackborn                        info.mName = mCurRoutesInfo.mBluetoothName;
129632ca417f0a33e3fa9ccece531afa2db3f0d4a30Dianne Hackborn                        info.mSupportedTypes = ROUTE_TYPE_LIVE_AUDIO;
130632ca417f0a33e3fa9ccece531afa2db3f0d4a30Dianne Hackborn                        sStatic.mBluetoothA2dpRoute = info;
131632ca417f0a33e3fa9ccece531afa2db3f0d4a30Dianne Hackborn                        addRoute(sStatic.mBluetoothA2dpRoute);
132dd0a19266d5c837069da1ea188744d54c8d723a8Adam Powell                        try {
133dd0a19266d5c837069da1ea188744d54c8d723a8Adam Powell                            if (mAudioService.isBluetoothA2dpOn()) {
134dd0a19266d5c837069da1ea188744d54c8d723a8Adam Powell                                selectRouteStatic(ROUTE_TYPE_LIVE_AUDIO, mBluetoothA2dpRoute);
135dd0a19266d5c837069da1ea188744d54c8d723a8Adam Powell                            }
136dd0a19266d5c837069da1ea188744d54c8d723a8Adam Powell                        } catch (RemoteException e) {
137dd0a19266d5c837069da1ea188744d54c8d723a8Adam Powell                            Log.e(TAG, "Error selecting Bluetooth A2DP route", e);
138dd0a19266d5c837069da1ea188744d54c8d723a8Adam Powell                        }
139632ca417f0a33e3fa9ccece531afa2db3f0d4a30Dianne Hackborn                    } else {
140632ca417f0a33e3fa9ccece531afa2db3f0d4a30Dianne Hackborn                        sStatic.mBluetoothA2dpRoute.mName = mCurRoutesInfo.mBluetoothName;
141632ca417f0a33e3fa9ccece531afa2db3f0d4a30Dianne Hackborn                        dispatchRouteChanged(sStatic.mBluetoothA2dpRoute);
142632ca417f0a33e3fa9ccece531afa2db3f0d4a30Dianne Hackborn                    }
143632ca417f0a33e3fa9ccece531afa2db3f0d4a30Dianne Hackborn                } else if (sStatic.mBluetoothA2dpRoute != null) {
144632ca417f0a33e3fa9ccece531afa2db3f0d4a30Dianne Hackborn                    removeRoute(sStatic.mBluetoothA2dpRoute);
145632ca417f0a33e3fa9ccece531afa2db3f0d4a30Dianne Hackborn                    sStatic.mBluetoothA2dpRoute = null;
146632ca417f0a33e3fa9ccece531afa2db3f0d4a30Dianne Hackborn                }
147632ca417f0a33e3fa9ccece531afa2db3f0d4a30Dianne Hackborn            }
148b58b8f832d06b0ffa8886eba5a4916578a3b8743Dianne Hackborn        }
149b58b8f832d06b0ffa8886eba5a4916578a3b8743Dianne Hackborn    }
1509a1de308cea2d160778fd977825f10a07b49d738Adam Powell
151b58b8f832d06b0ffa8886eba5a4916578a3b8743Dianne Hackborn    static Static sStatic;
1529a1de308cea2d160778fd977825f10a07b49d738Adam Powell
1539a1de308cea2d160778fd977825f10a07b49d738Adam Powell    /**
1549a1de308cea2d160778fd977825f10a07b49d738Adam Powell     * Route type flag for live audio.
1559a1de308cea2d160778fd977825f10a07b49d738Adam Powell     *
1569a1de308cea2d160778fd977825f10a07b49d738Adam Powell     * <p>A device that supports live audio routing will allow the media audio stream
1579a1de308cea2d160778fd977825f10a07b49d738Adam Powell     * to be routed to supported destinations. This can include internal speakers or
1589a1de308cea2d160778fd977825f10a07b49d738Adam Powell     * audio jacks on the device itself, A2DP devices, and more.</p>
1599a1de308cea2d160778fd977825f10a07b49d738Adam Powell     *
1609a1de308cea2d160778fd977825f10a07b49d738Adam Powell     * <p>Once initiated this routing is transparent to the application. All audio
1619a1de308cea2d160778fd977825f10a07b49d738Adam Powell     * played on the media stream will be routed to the selected destination.</p>
1629a1de308cea2d160778fd977825f10a07b49d738Adam Powell     */
1639a1de308cea2d160778fd977825f10a07b49d738Adam Powell    public static final int ROUTE_TYPE_LIVE_AUDIO = 0x1;
1649a1de308cea2d160778fd977825f10a07b49d738Adam Powell
1659a1de308cea2d160778fd977825f10a07b49d738Adam Powell    /**
1669a1de308cea2d160778fd977825f10a07b49d738Adam Powell     * Route type flag for application-specific usage.
1679a1de308cea2d160778fd977825f10a07b49d738Adam Powell     *
1689a1de308cea2d160778fd977825f10a07b49d738Adam Powell     * <p>Unlike other media route types, user routes are managed by the application.
1699a1de308cea2d160778fd977825f10a07b49d738Adam Powell     * The MediaRouter will manage and dispatch events for user routes, but the application
1709a1de308cea2d160778fd977825f10a07b49d738Adam Powell     * is expected to interpret the meaning of these events and perform the requested
1719a1de308cea2d160778fd977825f10a07b49d738Adam Powell     * routing tasks.</p>
1729a1de308cea2d160778fd977825f10a07b49d738Adam Powell     */
1739a1de308cea2d160778fd977825f10a07b49d738Adam Powell    public static final int ROUTE_TYPE_USER = 0x00800000;
1749a1de308cea2d160778fd977825f10a07b49d738Adam Powell
1759a1de308cea2d160778fd977825f10a07b49d738Adam Powell    // Maps application contexts
1769a1de308cea2d160778fd977825f10a07b49d738Adam Powell    static final HashMap<Context, MediaRouter> sRouters = new HashMap<Context, MediaRouter>();
1779a1de308cea2d160778fd977825f10a07b49d738Adam Powell
1789a1de308cea2d160778fd977825f10a07b49d738Adam Powell    static String typesToString(int types) {
1799a1de308cea2d160778fd977825f10a07b49d738Adam Powell        final StringBuilder result = new StringBuilder();
1809a1de308cea2d160778fd977825f10a07b49d738Adam Powell        if ((types & ROUTE_TYPE_LIVE_AUDIO) != 0) {
1819a1de308cea2d160778fd977825f10a07b49d738Adam Powell            result.append("ROUTE_TYPE_LIVE_AUDIO ");
1829a1de308cea2d160778fd977825f10a07b49d738Adam Powell        }
1839a1de308cea2d160778fd977825f10a07b49d738Adam Powell        if ((types & ROUTE_TYPE_USER) != 0) {
1849a1de308cea2d160778fd977825f10a07b49d738Adam Powell            result.append("ROUTE_TYPE_USER ");
1859a1de308cea2d160778fd977825f10a07b49d738Adam Powell        }
1869a1de308cea2d160778fd977825f10a07b49d738Adam Powell        return result.toString();
1879a1de308cea2d160778fd977825f10a07b49d738Adam Powell    }
1889a1de308cea2d160778fd977825f10a07b49d738Adam Powell
189b58b8f832d06b0ffa8886eba5a4916578a3b8743Dianne Hackborn    /** @hide */
190b58b8f832d06b0ffa8886eba5a4916578a3b8743Dianne Hackborn    public MediaRouter(Context context) {
191b58b8f832d06b0ffa8886eba5a4916578a3b8743Dianne Hackborn        synchronized (Static.class) {
192b58b8f832d06b0ffa8886eba5a4916578a3b8743Dianne Hackborn            if (sStatic == null) {
193b58b8f832d06b0ffa8886eba5a4916578a3b8743Dianne Hackborn                sStatic = new Static(context.getApplicationContext());
194632ca417f0a33e3fa9ccece531afa2db3f0d4a30Dianne Hackborn                sStatic.startMonitoringRoutes();
195b58b8f832d06b0ffa8886eba5a4916578a3b8743Dianne Hackborn            }
1969a1de308cea2d160778fd977825f10a07b49d738Adam Powell        }
1979a1de308cea2d160778fd977825f10a07b49d738Adam Powell    }
1989a1de308cea2d160778fd977825f10a07b49d738Adam Powell
199690ffb4e1f735148a15f2036d9a3c1962fba188cAdam Powell    /**
200690ffb4e1f735148a15f2036d9a3c1962fba188cAdam Powell     * @hide for use by framework routing UI
201690ffb4e1f735148a15f2036d9a3c1962fba188cAdam Powell     */
202690ffb4e1f735148a15f2036d9a3c1962fba188cAdam Powell    public RouteInfo getSystemAudioRoute() {
203b58b8f832d06b0ffa8886eba5a4916578a3b8743Dianne Hackborn        return sStatic.mDefaultAudio;
204690ffb4e1f735148a15f2036d9a3c1962fba188cAdam Powell    }
205690ffb4e1f735148a15f2036d9a3c1962fba188cAdam Powell
206690ffb4e1f735148a15f2036d9a3c1962fba188cAdam Powell    /**
2074599696591f745b3a546197d2ba7e5cfc5562484Adam Powell     * @hide for use by framework routing UI
2084599696591f745b3a546197d2ba7e5cfc5562484Adam Powell     */
2094599696591f745b3a546197d2ba7e5cfc5562484Adam Powell    public RouteCategory getSystemAudioCategory() {
2104599696591f745b3a546197d2ba7e5cfc5562484Adam Powell        return sStatic.mSystemCategory;
2114599696591f745b3a546197d2ba7e5cfc5562484Adam Powell    }
2124599696591f745b3a546197d2ba7e5cfc5562484Adam Powell
2134599696591f745b3a546197d2ba7e5cfc5562484Adam Powell    /**
214690ffb4e1f735148a15f2036d9a3c1962fba188cAdam Powell     * Return the currently selected route for the given types
215690ffb4e1f735148a15f2036d9a3c1962fba188cAdam Powell     *
216690ffb4e1f735148a15f2036d9a3c1962fba188cAdam Powell     * @param type route types
217690ffb4e1f735148a15f2036d9a3c1962fba188cAdam Powell     * @return the selected route
218690ffb4e1f735148a15f2036d9a3c1962fba188cAdam Powell     */
219690ffb4e1f735148a15f2036d9a3c1962fba188cAdam Powell    public RouteInfo getSelectedRoute(int type) {
220b58b8f832d06b0ffa8886eba5a4916578a3b8743Dianne Hackborn        return sStatic.mSelectedRoute;
221690ffb4e1f735148a15f2036d9a3c1962fba188cAdam Powell    }
222690ffb4e1f735148a15f2036d9a3c1962fba188cAdam Powell
2239a1de308cea2d160778fd977825f10a07b49d738Adam Powell    /**
2249a1de308cea2d160778fd977825f10a07b49d738Adam Powell     * Add a callback to listen to events about specific kinds of media routes.
2259a1de308cea2d160778fd977825f10a07b49d738Adam Powell     * If the specified callback is already registered, its registration will be updated for any
2269a1de308cea2d160778fd977825f10a07b49d738Adam Powell     * additional route types specified.
2279a1de308cea2d160778fd977825f10a07b49d738Adam Powell     *
2289a1de308cea2d160778fd977825f10a07b49d738Adam Powell     * @param types Types of routes this callback is interested in
2299a1de308cea2d160778fd977825f10a07b49d738Adam Powell     * @param cb Callback to add
2309a1de308cea2d160778fd977825f10a07b49d738Adam Powell     */
2319a1de308cea2d160778fd977825f10a07b49d738Adam Powell    public void addCallback(int types, Callback cb) {
232b58b8f832d06b0ffa8886eba5a4916578a3b8743Dianne Hackborn        final int count = sStatic.mCallbacks.size();
2339a1de308cea2d160778fd977825f10a07b49d738Adam Powell        for (int i = 0; i < count; i++) {
234b58b8f832d06b0ffa8886eba5a4916578a3b8743Dianne Hackborn            final CallbackInfo info = sStatic.mCallbacks.get(i);
2359a1de308cea2d160778fd977825f10a07b49d738Adam Powell            if (info.cb == cb) {
2369a1de308cea2d160778fd977825f10a07b49d738Adam Powell                info.type &= types;
2379a1de308cea2d160778fd977825f10a07b49d738Adam Powell                return;
2389a1de308cea2d160778fd977825f10a07b49d738Adam Powell            }
2399a1de308cea2d160778fd977825f10a07b49d738Adam Powell        }
240b58b8f832d06b0ffa8886eba5a4916578a3b8743Dianne Hackborn        sStatic.mCallbacks.add(new CallbackInfo(cb, types, this));
2419a1de308cea2d160778fd977825f10a07b49d738Adam Powell    }
2429a1de308cea2d160778fd977825f10a07b49d738Adam Powell
2439a1de308cea2d160778fd977825f10a07b49d738Adam Powell    /**
2449a1de308cea2d160778fd977825f10a07b49d738Adam Powell     * Remove the specified callback. It will no longer receive events about media routing.
2459a1de308cea2d160778fd977825f10a07b49d738Adam Powell     *
2469a1de308cea2d160778fd977825f10a07b49d738Adam Powell     * @param cb Callback to remove
2479a1de308cea2d160778fd977825f10a07b49d738Adam Powell     */
2489a1de308cea2d160778fd977825f10a07b49d738Adam Powell    public void removeCallback(Callback cb) {
249b58b8f832d06b0ffa8886eba5a4916578a3b8743Dianne Hackborn        final int count = sStatic.mCallbacks.size();
2509a1de308cea2d160778fd977825f10a07b49d738Adam Powell        for (int i = 0; i < count; i++) {
251b58b8f832d06b0ffa8886eba5a4916578a3b8743Dianne Hackborn            if (sStatic.mCallbacks.get(i).cb == cb) {
252b58b8f832d06b0ffa8886eba5a4916578a3b8743Dianne Hackborn                sStatic.mCallbacks.remove(i);
2539a1de308cea2d160778fd977825f10a07b49d738Adam Powell                return;
2549a1de308cea2d160778fd977825f10a07b49d738Adam Powell            }
2559a1de308cea2d160778fd977825f10a07b49d738Adam Powell        }
2569a1de308cea2d160778fd977825f10a07b49d738Adam Powell        Log.w(TAG, "removeCallback(" + cb + "): callback not registered");
2579a1de308cea2d160778fd977825f10a07b49d738Adam Powell    }
2589a1de308cea2d160778fd977825f10a07b49d738Adam Powell
259d0d2cda9d414da73773285d7fee9e13aef3495e9Adam Powell    /**
260d0d2cda9d414da73773285d7fee9e13aef3495e9Adam Powell     * Select the specified route to use for output of the given media types.
261d0d2cda9d414da73773285d7fee9e13aef3495e9Adam Powell     *
262d0d2cda9d414da73773285d7fee9e13aef3495e9Adam Powell     * @param types type flags indicating which types this route should be used for.
263d0d2cda9d414da73773285d7fee9e13aef3495e9Adam Powell     *              The route must support at least a subset.
264d0d2cda9d414da73773285d7fee9e13aef3495e9Adam Powell     * @param route Route to select
265d0d2cda9d414da73773285d7fee9e13aef3495e9Adam Powell     */
2669a1de308cea2d160778fd977825f10a07b49d738Adam Powell    public void selectRoute(int types, RouteInfo route) {
2670d03c042f90bf62d5bad7c64e77028a5f9f8fae0Adam Powell        // Applications shouldn't programmatically change anything but user routes.
2680d03c042f90bf62d5bad7c64e77028a5f9f8fae0Adam Powell        types &= ROUTE_TYPE_USER;
2690d03c042f90bf62d5bad7c64e77028a5f9f8fae0Adam Powell        selectRouteStatic(types, route);
2700d03c042f90bf62d5bad7c64e77028a5f9f8fae0Adam Powell    }
2710d03c042f90bf62d5bad7c64e77028a5f9f8fae0Adam Powell
2720d03c042f90bf62d5bad7c64e77028a5f9f8fae0Adam Powell    /**
2730d03c042f90bf62d5bad7c64e77028a5f9f8fae0Adam Powell     * @hide internal use
2740d03c042f90bf62d5bad7c64e77028a5f9f8fae0Adam Powell     */
2750d03c042f90bf62d5bad7c64e77028a5f9f8fae0Adam Powell    public void selectRouteInt(int types, RouteInfo route) {
276b58b8f832d06b0ffa8886eba5a4916578a3b8743Dianne Hackborn        selectRouteStatic(types, route);
277b58b8f832d06b0ffa8886eba5a4916578a3b8743Dianne Hackborn    }
278b58b8f832d06b0ffa8886eba5a4916578a3b8743Dianne Hackborn
279b58b8f832d06b0ffa8886eba5a4916578a3b8743Dianne Hackborn    static void selectRouteStatic(int types, RouteInfo route) {
280b58b8f832d06b0ffa8886eba5a4916578a3b8743Dianne Hackborn        if (sStatic.mSelectedRoute == route) return;
2810d03c042f90bf62d5bad7c64e77028a5f9f8fae0Adam Powell        if ((route.getSupportedTypes() & types) == 0) {
2820d03c042f90bf62d5bad7c64e77028a5f9f8fae0Adam Powell            Log.w(TAG, "selectRoute ignored; cannot select route with supported types " +
2830d03c042f90bf62d5bad7c64e77028a5f9f8fae0Adam Powell                    typesToString(route.getSupportedTypes()) + " into route types " +
2840d03c042f90bf62d5bad7c64e77028a5f9f8fae0Adam Powell                    typesToString(types));
2854ee1f55ce0f4909a7430ab44563a81852f335071Adam Powell            return;
2860d03c042f90bf62d5bad7c64e77028a5f9f8fae0Adam Powell        }
2879a1de308cea2d160778fd977825f10a07b49d738Adam Powell
288dd0a19266d5c837069da1ea188744d54c8d723a8Adam Powell        final RouteInfo btRoute = sStatic.mBluetoothA2dpRoute;
289dd0a19266d5c837069da1ea188744d54c8d723a8Adam Powell        if (btRoute != null && (types & ROUTE_TYPE_LIVE_AUDIO) != 0 &&
290dd0a19266d5c837069da1ea188744d54c8d723a8Adam Powell                (route == btRoute || route == sStatic.mDefaultAudio)) {
291dd0a19266d5c837069da1ea188744d54c8d723a8Adam Powell            try {
292dd0a19266d5c837069da1ea188744d54c8d723a8Adam Powell                sStatic.mAudioService.setBluetoothA2dpOn(route == btRoute);
293dd0a19266d5c837069da1ea188744d54c8d723a8Adam Powell            } catch (RemoteException e) {
294dd0a19266d5c837069da1ea188744d54c8d723a8Adam Powell                Log.e(TAG, "Error changing Bluetooth A2DP state", e);
295dd0a19266d5c837069da1ea188744d54c8d723a8Adam Powell            }
296dd0a19266d5c837069da1ea188744d54c8d723a8Adam Powell        }
297dd0a19266d5c837069da1ea188744d54c8d723a8Adam Powell
298b58b8f832d06b0ffa8886eba5a4916578a3b8743Dianne Hackborn        if (sStatic.mSelectedRoute != null) {
2999a1de308cea2d160778fd977825f10a07b49d738Adam Powell            // TODO filter types properly
300b58b8f832d06b0ffa8886eba5a4916578a3b8743Dianne Hackborn            dispatchRouteUnselected(types & sStatic.mSelectedRoute.getSupportedTypes(),
301b58b8f832d06b0ffa8886eba5a4916578a3b8743Dianne Hackborn                    sStatic.mSelectedRoute);
3029a1de308cea2d160778fd977825f10a07b49d738Adam Powell        }
303b58b8f832d06b0ffa8886eba5a4916578a3b8743Dianne Hackborn        sStatic.mSelectedRoute = route;
3049a1de308cea2d160778fd977825f10a07b49d738Adam Powell        if (route != null) {
3059a1de308cea2d160778fd977825f10a07b49d738Adam Powell            // TODO filter types properly
3069a1de308cea2d160778fd977825f10a07b49d738Adam Powell            dispatchRouteSelected(types & route.getSupportedTypes(), route);
3079a1de308cea2d160778fd977825f10a07b49d738Adam Powell        }
3089a1de308cea2d160778fd977825f10a07b49d738Adam Powell    }
3099a1de308cea2d160778fd977825f10a07b49d738Adam Powell
3109a1de308cea2d160778fd977825f10a07b49d738Adam Powell    /**
3119a1de308cea2d160778fd977825f10a07b49d738Adam Powell     * Add an app-specified route for media to the MediaRouter.
3129a1de308cea2d160778fd977825f10a07b49d738Adam Powell     * App-specified route definitions are created using {@link #createUserRoute(RouteCategory)}
3139a1de308cea2d160778fd977825f10a07b49d738Adam Powell     *
3149a1de308cea2d160778fd977825f10a07b49d738Adam Powell     * @param info Definition of the route to add
3159a1de308cea2d160778fd977825f10a07b49d738Adam Powell     * @see #createUserRoute()
3169a1de308cea2d160778fd977825f10a07b49d738Adam Powell     * @see #removeUserRoute(UserRouteInfo)
3179a1de308cea2d160778fd977825f10a07b49d738Adam Powell     */
3189a1de308cea2d160778fd977825f10a07b49d738Adam Powell    public void addUserRoute(UserRouteInfo info) {
3199a1de308cea2d160778fd977825f10a07b49d738Adam Powell        addRoute(info);
3209a1de308cea2d160778fd977825f10a07b49d738Adam Powell    }
3219a1de308cea2d160778fd977825f10a07b49d738Adam Powell
322d6d0bddee363e0c7fe61f63bd9d9864a71d887d6Adam Powell    /**
323d6d0bddee363e0c7fe61f63bd9d9864a71d887d6Adam Powell     * @hide Framework use only
324d6d0bddee363e0c7fe61f63bd9d9864a71d887d6Adam Powell     */
325d6d0bddee363e0c7fe61f63bd9d9864a71d887d6Adam Powell    public void addRouteInt(RouteInfo info) {
326d6d0bddee363e0c7fe61f63bd9d9864a71d887d6Adam Powell        addRoute(info);
327d6d0bddee363e0c7fe61f63bd9d9864a71d887d6Adam Powell    }
328d6d0bddee363e0c7fe61f63bd9d9864a71d887d6Adam Powell
329b58b8f832d06b0ffa8886eba5a4916578a3b8743Dianne Hackborn    static void addRoute(RouteInfo info) {
3309a1de308cea2d160778fd977825f10a07b49d738Adam Powell        final RouteCategory cat = info.getCategory();
331b58b8f832d06b0ffa8886eba5a4916578a3b8743Dianne Hackborn        if (!sStatic.mCategories.contains(cat)) {
332b58b8f832d06b0ffa8886eba5a4916578a3b8743Dianne Hackborn            sStatic.mCategories.add(cat);
3339a1de308cea2d160778fd977825f10a07b49d738Adam Powell        }
334b58b8f832d06b0ffa8886eba5a4916578a3b8743Dianne Hackborn        final boolean onlyRoute = sStatic.mRoutes.isEmpty();
335d0d2cda9d414da73773285d7fee9e13aef3495e9Adam Powell        if (cat.isGroupable() && !(info instanceof RouteGroup)) {
3369a1de308cea2d160778fd977825f10a07b49d738Adam Powell            // Enforce that any added route in a groupable category must be in a group.
3379a1de308cea2d160778fd977825f10a07b49d738Adam Powell            final RouteGroup group = new RouteGroup(info.getCategory());
338b58b8f832d06b0ffa8886eba5a4916578a3b8743Dianne Hackborn            sStatic.mRoutes.add(group);
339d0d2cda9d414da73773285d7fee9e13aef3495e9Adam Powell            dispatchRouteAdded(group);
340b5e2af5919351486a385effe77409d2a91ae9c19Adam Powell            group.addRoute(info);
341d0d2cda9d414da73773285d7fee9e13aef3495e9Adam Powell
3429a1de308cea2d160778fd977825f10a07b49d738Adam Powell            info = group;
343d0d2cda9d414da73773285d7fee9e13aef3495e9Adam Powell        } else {
344b58b8f832d06b0ffa8886eba5a4916578a3b8743Dianne Hackborn            sStatic.mRoutes.add(info);
345d0d2cda9d414da73773285d7fee9e13aef3495e9Adam Powell            dispatchRouteAdded(info);
3469a1de308cea2d160778fd977825f10a07b49d738Adam Powell        }
347d0d2cda9d414da73773285d7fee9e13aef3495e9Adam Powell
3489a1de308cea2d160778fd977825f10a07b49d738Adam Powell        if (onlyRoute) {
349b58b8f832d06b0ffa8886eba5a4916578a3b8743Dianne Hackborn            selectRouteStatic(info.getSupportedTypes(), info);
3509a1de308cea2d160778fd977825f10a07b49d738Adam Powell        }
3519a1de308cea2d160778fd977825f10a07b49d738Adam Powell    }
3529a1de308cea2d160778fd977825f10a07b49d738Adam Powell
3539a1de308cea2d160778fd977825f10a07b49d738Adam Powell    /**
3549a1de308cea2d160778fd977825f10a07b49d738Adam Powell     * Remove an app-specified route for media from the MediaRouter.
3559a1de308cea2d160778fd977825f10a07b49d738Adam Powell     *
3569a1de308cea2d160778fd977825f10a07b49d738Adam Powell     * @param info Definition of the route to remove
3579a1de308cea2d160778fd977825f10a07b49d738Adam Powell     * @see #addUserRoute(UserRouteInfo)
3589a1de308cea2d160778fd977825f10a07b49d738Adam Powell     */
3599a1de308cea2d160778fd977825f10a07b49d738Adam Powell    public void removeUserRoute(UserRouteInfo info) {
3609a1de308cea2d160778fd977825f10a07b49d738Adam Powell        removeRoute(info);
3619a1de308cea2d160778fd977825f10a07b49d738Adam Powell    }
3629a1de308cea2d160778fd977825f10a07b49d738Adam Powell
363690ffb4e1f735148a15f2036d9a3c1962fba188cAdam Powell    /**
364690ffb4e1f735148a15f2036d9a3c1962fba188cAdam Powell     * Remove all app-specified routes from the MediaRouter.
365690ffb4e1f735148a15f2036d9a3c1962fba188cAdam Powell     *
366690ffb4e1f735148a15f2036d9a3c1962fba188cAdam Powell     * @see #removeUserRoute(UserRouteInfo)
367690ffb4e1f735148a15f2036d9a3c1962fba188cAdam Powell     */
368690ffb4e1f735148a15f2036d9a3c1962fba188cAdam Powell    public void clearUserRoutes() {
369b58b8f832d06b0ffa8886eba5a4916578a3b8743Dianne Hackborn        for (int i = 0; i < sStatic.mRoutes.size(); i++) {
370b58b8f832d06b0ffa8886eba5a4916578a3b8743Dianne Hackborn            final RouteInfo info = sStatic.mRoutes.get(i);
371d6d0bddee363e0c7fe61f63bd9d9864a71d887d6Adam Powell            // TODO Right now, RouteGroups only ever contain user routes.
372d6d0bddee363e0c7fe61f63bd9d9864a71d887d6Adam Powell            // The code below will need to change if this assumption does.
373d6d0bddee363e0c7fe61f63bd9d9864a71d887d6Adam Powell            if (info instanceof UserRouteInfo || info instanceof RouteGroup) {
374690ffb4e1f735148a15f2036d9a3c1962fba188cAdam Powell                removeRouteAt(i);
375690ffb4e1f735148a15f2036d9a3c1962fba188cAdam Powell                i--;
376690ffb4e1f735148a15f2036d9a3c1962fba188cAdam Powell            }
377690ffb4e1f735148a15f2036d9a3c1962fba188cAdam Powell        }
378690ffb4e1f735148a15f2036d9a3c1962fba188cAdam Powell    }
379690ffb4e1f735148a15f2036d9a3c1962fba188cAdam Powell
380d6d0bddee363e0c7fe61f63bd9d9864a71d887d6Adam Powell    /**
381d6d0bddee363e0c7fe61f63bd9d9864a71d887d6Adam Powell     * @hide internal use only
382d6d0bddee363e0c7fe61f63bd9d9864a71d887d6Adam Powell     */
383d6d0bddee363e0c7fe61f63bd9d9864a71d887d6Adam Powell    public void removeRouteInt(RouteInfo info) {
384d6d0bddee363e0c7fe61f63bd9d9864a71d887d6Adam Powell        removeRoute(info);
385d6d0bddee363e0c7fe61f63bd9d9864a71d887d6Adam Powell    }
386d6d0bddee363e0c7fe61f63bd9d9864a71d887d6Adam Powell
387b58b8f832d06b0ffa8886eba5a4916578a3b8743Dianne Hackborn    static void removeRoute(RouteInfo info) {
388b58b8f832d06b0ffa8886eba5a4916578a3b8743Dianne Hackborn        if (sStatic.mRoutes.remove(info)) {
3899a1de308cea2d160778fd977825f10a07b49d738Adam Powell            final RouteCategory removingCat = info.getCategory();
390b58b8f832d06b0ffa8886eba5a4916578a3b8743Dianne Hackborn            final int count = sStatic.mRoutes.size();
3919a1de308cea2d160778fd977825f10a07b49d738Adam Powell            boolean found = false;
3929a1de308cea2d160778fd977825f10a07b49d738Adam Powell            for (int i = 0; i < count; i++) {
393b58b8f832d06b0ffa8886eba5a4916578a3b8743Dianne Hackborn                final RouteCategory cat = sStatic.mRoutes.get(i).getCategory();
3949a1de308cea2d160778fd977825f10a07b49d738Adam Powell                if (removingCat == cat) {
3959a1de308cea2d160778fd977825f10a07b49d738Adam Powell                    found = true;
3969a1de308cea2d160778fd977825f10a07b49d738Adam Powell                    break;
3979a1de308cea2d160778fd977825f10a07b49d738Adam Powell                }
3989a1de308cea2d160778fd977825f10a07b49d738Adam Powell            }
399d6d0bddee363e0c7fe61f63bd9d9864a71d887d6Adam Powell            if (info == sStatic.mSelectedRoute) {
400d6d0bddee363e0c7fe61f63bd9d9864a71d887d6Adam Powell                // Removing the currently selected route? Select the default before we remove it.
401d6d0bddee363e0c7fe61f63bd9d9864a71d887d6Adam Powell                // TODO: Be smarter about the route types here; this selects for all valid.
402d6d0bddee363e0c7fe61f63bd9d9864a71d887d6Adam Powell                selectRouteStatic(ROUTE_TYPE_LIVE_AUDIO | ROUTE_TYPE_USER, sStatic.mDefaultAudio);
403d6d0bddee363e0c7fe61f63bd9d9864a71d887d6Adam Powell            }
4049a1de308cea2d160778fd977825f10a07b49d738Adam Powell            if (!found) {
405b58b8f832d06b0ffa8886eba5a4916578a3b8743Dianne Hackborn                sStatic.mCategories.remove(removingCat);
4069a1de308cea2d160778fd977825f10a07b49d738Adam Powell            }
4079a1de308cea2d160778fd977825f10a07b49d738Adam Powell            dispatchRouteRemoved(info);
4089a1de308cea2d160778fd977825f10a07b49d738Adam Powell        }
4099a1de308cea2d160778fd977825f10a07b49d738Adam Powell    }
4109a1de308cea2d160778fd977825f10a07b49d738Adam Powell
411690ffb4e1f735148a15f2036d9a3c1962fba188cAdam Powell    void removeRouteAt(int routeIndex) {
412b58b8f832d06b0ffa8886eba5a4916578a3b8743Dianne Hackborn        if (routeIndex >= 0 && routeIndex < sStatic.mRoutes.size()) {
413b58b8f832d06b0ffa8886eba5a4916578a3b8743Dianne Hackborn            final RouteInfo info = sStatic.mRoutes.remove(routeIndex);
414690ffb4e1f735148a15f2036d9a3c1962fba188cAdam Powell            final RouteCategory removingCat = info.getCategory();
415b58b8f832d06b0ffa8886eba5a4916578a3b8743Dianne Hackborn            final int count = sStatic.mRoutes.size();
416690ffb4e1f735148a15f2036d9a3c1962fba188cAdam Powell            boolean found = false;
417690ffb4e1f735148a15f2036d9a3c1962fba188cAdam Powell            for (int i = 0; i < count; i++) {
418b58b8f832d06b0ffa8886eba5a4916578a3b8743Dianne Hackborn                final RouteCategory cat = sStatic.mRoutes.get(i).getCategory();
419690ffb4e1f735148a15f2036d9a3c1962fba188cAdam Powell                if (removingCat == cat) {
420690ffb4e1f735148a15f2036d9a3c1962fba188cAdam Powell                    found = true;
421690ffb4e1f735148a15f2036d9a3c1962fba188cAdam Powell                    break;
422690ffb4e1f735148a15f2036d9a3c1962fba188cAdam Powell                }
423690ffb4e1f735148a15f2036d9a3c1962fba188cAdam Powell            }
424d6d0bddee363e0c7fe61f63bd9d9864a71d887d6Adam Powell            if (info == sStatic.mSelectedRoute) {
425d6d0bddee363e0c7fe61f63bd9d9864a71d887d6Adam Powell                // Removing the currently selected route? Select the default before we remove it.
426d6d0bddee363e0c7fe61f63bd9d9864a71d887d6Adam Powell                // TODO: Be smarter about the route types here; this selects for all valid.
427d6d0bddee363e0c7fe61f63bd9d9864a71d887d6Adam Powell                selectRouteStatic(ROUTE_TYPE_LIVE_AUDIO | ROUTE_TYPE_USER, sStatic.mDefaultAudio);
428d6d0bddee363e0c7fe61f63bd9d9864a71d887d6Adam Powell            }
429690ffb4e1f735148a15f2036d9a3c1962fba188cAdam Powell            if (!found) {
430b58b8f832d06b0ffa8886eba5a4916578a3b8743Dianne Hackborn                sStatic.mCategories.remove(removingCat);
431690ffb4e1f735148a15f2036d9a3c1962fba188cAdam Powell            }
432690ffb4e1f735148a15f2036d9a3c1962fba188cAdam Powell            dispatchRouteRemoved(info);
433690ffb4e1f735148a15f2036d9a3c1962fba188cAdam Powell        }
434690ffb4e1f735148a15f2036d9a3c1962fba188cAdam Powell    }
435690ffb4e1f735148a15f2036d9a3c1962fba188cAdam Powell
4369a1de308cea2d160778fd977825f10a07b49d738Adam Powell    /**
4379a1de308cea2d160778fd977825f10a07b49d738Adam Powell     * Return the number of {@link MediaRouter.RouteCategory categories} currently
4389a1de308cea2d160778fd977825f10a07b49d738Adam Powell     * represented by routes known to this MediaRouter.
4399a1de308cea2d160778fd977825f10a07b49d738Adam Powell     *
4409a1de308cea2d160778fd977825f10a07b49d738Adam Powell     * @return the number of unique categories represented by this MediaRouter's known routes
4419a1de308cea2d160778fd977825f10a07b49d738Adam Powell     */
4429a1de308cea2d160778fd977825f10a07b49d738Adam Powell    public int getCategoryCount() {
443b58b8f832d06b0ffa8886eba5a4916578a3b8743Dianne Hackborn        return sStatic.mCategories.size();
4449a1de308cea2d160778fd977825f10a07b49d738Adam Powell    }
4459a1de308cea2d160778fd977825f10a07b49d738Adam Powell
4469a1de308cea2d160778fd977825f10a07b49d738Adam Powell    /**
4479a1de308cea2d160778fd977825f10a07b49d738Adam Powell     * Return the {@link MediaRouter.RouteCategory category} at the given index.
4489a1de308cea2d160778fd977825f10a07b49d738Adam Powell     * Valid indices are in the range [0-getCategoryCount).
4499a1de308cea2d160778fd977825f10a07b49d738Adam Powell     *
4509a1de308cea2d160778fd977825f10a07b49d738Adam Powell     * @param index which category to return
4519a1de308cea2d160778fd977825f10a07b49d738Adam Powell     * @return the category at index
4529a1de308cea2d160778fd977825f10a07b49d738Adam Powell     */
4539a1de308cea2d160778fd977825f10a07b49d738Adam Powell    public RouteCategory getCategoryAt(int index) {
454b58b8f832d06b0ffa8886eba5a4916578a3b8743Dianne Hackborn        return sStatic.mCategories.get(index);
4559a1de308cea2d160778fd977825f10a07b49d738Adam Powell    }
4569a1de308cea2d160778fd977825f10a07b49d738Adam Powell
4579a1de308cea2d160778fd977825f10a07b49d738Adam Powell    /**
4589a1de308cea2d160778fd977825f10a07b49d738Adam Powell     * Return the number of {@link MediaRouter.RouteInfo routes} currently known
4599a1de308cea2d160778fd977825f10a07b49d738Adam Powell     * to this MediaRouter.
4609a1de308cea2d160778fd977825f10a07b49d738Adam Powell     *
4619a1de308cea2d160778fd977825f10a07b49d738Adam Powell     * @return the number of routes tracked by this router
4629a1de308cea2d160778fd977825f10a07b49d738Adam Powell     */
4639a1de308cea2d160778fd977825f10a07b49d738Adam Powell    public int getRouteCount() {
464b58b8f832d06b0ffa8886eba5a4916578a3b8743Dianne Hackborn        return sStatic.mRoutes.size();
4659a1de308cea2d160778fd977825f10a07b49d738Adam Powell    }
4669a1de308cea2d160778fd977825f10a07b49d738Adam Powell
4679a1de308cea2d160778fd977825f10a07b49d738Adam Powell    /**
4689a1de308cea2d160778fd977825f10a07b49d738Adam Powell     * Return the route at the specified index.
4699a1de308cea2d160778fd977825f10a07b49d738Adam Powell     *
4709a1de308cea2d160778fd977825f10a07b49d738Adam Powell     * @param index index of the route to return
4719a1de308cea2d160778fd977825f10a07b49d738Adam Powell     * @return the route at index
4729a1de308cea2d160778fd977825f10a07b49d738Adam Powell     */
4739a1de308cea2d160778fd977825f10a07b49d738Adam Powell    public RouteInfo getRouteAt(int index) {
474b58b8f832d06b0ffa8886eba5a4916578a3b8743Dianne Hackborn        return sStatic.mRoutes.get(index);
475b58b8f832d06b0ffa8886eba5a4916578a3b8743Dianne Hackborn    }
476b58b8f832d06b0ffa8886eba5a4916578a3b8743Dianne Hackborn
477b58b8f832d06b0ffa8886eba5a4916578a3b8743Dianne Hackborn    static int getRouteCountStatic() {
478b58b8f832d06b0ffa8886eba5a4916578a3b8743Dianne Hackborn        return sStatic.mRoutes.size();
479b58b8f832d06b0ffa8886eba5a4916578a3b8743Dianne Hackborn    }
480b58b8f832d06b0ffa8886eba5a4916578a3b8743Dianne Hackborn
481b58b8f832d06b0ffa8886eba5a4916578a3b8743Dianne Hackborn    static RouteInfo getRouteAtStatic(int index) {
482b58b8f832d06b0ffa8886eba5a4916578a3b8743Dianne Hackborn        return sStatic.mRoutes.get(index);
4839a1de308cea2d160778fd977825f10a07b49d738Adam Powell    }
4849a1de308cea2d160778fd977825f10a07b49d738Adam Powell
4859a1de308cea2d160778fd977825f10a07b49d738Adam Powell    /**
4869a1de308cea2d160778fd977825f10a07b49d738Adam Powell     * Create a new user route that may be modified and registered for use by the application.
4879a1de308cea2d160778fd977825f10a07b49d738Adam Powell     *
4889a1de308cea2d160778fd977825f10a07b49d738Adam Powell     * @param category The category the new route will belong to
4899a1de308cea2d160778fd977825f10a07b49d738Adam Powell     * @return A new UserRouteInfo for use by the application
4909a1de308cea2d160778fd977825f10a07b49d738Adam Powell     *
4919a1de308cea2d160778fd977825f10a07b49d738Adam Powell     * @see #addUserRoute(UserRouteInfo)
4929a1de308cea2d160778fd977825f10a07b49d738Adam Powell     * @see #removeUserRoute(UserRouteInfo)
4939a1de308cea2d160778fd977825f10a07b49d738Adam Powell     * @see #createRouteCategory(CharSequence)
4949a1de308cea2d160778fd977825f10a07b49d738Adam Powell     */
4959a1de308cea2d160778fd977825f10a07b49d738Adam Powell    public UserRouteInfo createUserRoute(RouteCategory category) {
4969a1de308cea2d160778fd977825f10a07b49d738Adam Powell        return new UserRouteInfo(category);
4979a1de308cea2d160778fd977825f10a07b49d738Adam Powell    }
4989a1de308cea2d160778fd977825f10a07b49d738Adam Powell
4999a1de308cea2d160778fd977825f10a07b49d738Adam Powell    /**
5009a1de308cea2d160778fd977825f10a07b49d738Adam Powell     * Create a new route category. Each route must belong to a category.
5019a1de308cea2d160778fd977825f10a07b49d738Adam Powell     *
5029a1de308cea2d160778fd977825f10a07b49d738Adam Powell     * @param name Name of the new category
5039a1de308cea2d160778fd977825f10a07b49d738Adam Powell     * @param isGroupable true if routes in this category may be grouped with one another
5049a1de308cea2d160778fd977825f10a07b49d738Adam Powell     * @return the new RouteCategory
5059a1de308cea2d160778fd977825f10a07b49d738Adam Powell     */
5069a1de308cea2d160778fd977825f10a07b49d738Adam Powell    public RouteCategory createRouteCategory(CharSequence name, boolean isGroupable) {
5079a1de308cea2d160778fd977825f10a07b49d738Adam Powell        return new RouteCategory(name, ROUTE_TYPE_USER, isGroupable);
5089a1de308cea2d160778fd977825f10a07b49d738Adam Powell    }
5090d03c042f90bf62d5bad7c64e77028a5f9f8fae0Adam Powell
5100d03c042f90bf62d5bad7c64e77028a5f9f8fae0Adam Powell    /**
5110d03c042f90bf62d5bad7c64e77028a5f9f8fae0Adam Powell     * Create a new route category. Each route must belong to a category.
5120d03c042f90bf62d5bad7c64e77028a5f9f8fae0Adam Powell     *
5130d03c042f90bf62d5bad7c64e77028a5f9f8fae0Adam Powell     * @param nameResId Resource ID of the name of the new category
5140d03c042f90bf62d5bad7c64e77028a5f9f8fae0Adam Powell     * @param isGroupable true if routes in this category may be grouped with one another
5150d03c042f90bf62d5bad7c64e77028a5f9f8fae0Adam Powell     * @return the new RouteCategory
5160d03c042f90bf62d5bad7c64e77028a5f9f8fae0Adam Powell     */
5170d03c042f90bf62d5bad7c64e77028a5f9f8fae0Adam Powell    public RouteCategory createRouteCategory(int nameResId, boolean isGroupable) {
5180d03c042f90bf62d5bad7c64e77028a5f9f8fae0Adam Powell        return new RouteCategory(nameResId, ROUTE_TYPE_USER, isGroupable);
5190d03c042f90bf62d5bad7c64e77028a5f9f8fae0Adam Powell    }
5209a1de308cea2d160778fd977825f10a07b49d738Adam Powell
521b58b8f832d06b0ffa8886eba5a4916578a3b8743Dianne Hackborn    static void updateRoute(final RouteInfo info) {
5229a1de308cea2d160778fd977825f10a07b49d738Adam Powell        dispatchRouteChanged(info);
5239a1de308cea2d160778fd977825f10a07b49d738Adam Powell    }
5249a1de308cea2d160778fd977825f10a07b49d738Adam Powell
525b58b8f832d06b0ffa8886eba5a4916578a3b8743Dianne Hackborn    static void dispatchRouteSelected(int type, RouteInfo info) {
52639d5c6172503620ac3761148adac5fd7fa20d02dAdam Powell        for (CallbackInfo cbi : sStatic.mCallbacks) {
5279a1de308cea2d160778fd977825f10a07b49d738Adam Powell            if ((cbi.type & type) != 0) {
528b58b8f832d06b0ffa8886eba5a4916578a3b8743Dianne Hackborn                cbi.cb.onRouteSelected(cbi.router, type, info);
5299a1de308cea2d160778fd977825f10a07b49d738Adam Powell            }
5309a1de308cea2d160778fd977825f10a07b49d738Adam Powell        }
5319a1de308cea2d160778fd977825f10a07b49d738Adam Powell    }
5329a1de308cea2d160778fd977825f10a07b49d738Adam Powell
533b58b8f832d06b0ffa8886eba5a4916578a3b8743Dianne Hackborn    static void dispatchRouteUnselected(int type, RouteInfo info) {
53439d5c6172503620ac3761148adac5fd7fa20d02dAdam Powell        for (CallbackInfo cbi : sStatic.mCallbacks) {
5359a1de308cea2d160778fd977825f10a07b49d738Adam Powell            if ((cbi.type & type) != 0) {
536b58b8f832d06b0ffa8886eba5a4916578a3b8743Dianne Hackborn                cbi.cb.onRouteUnselected(cbi.router, type, info);
5379a1de308cea2d160778fd977825f10a07b49d738Adam Powell            }
5389a1de308cea2d160778fd977825f10a07b49d738Adam Powell        }
5399a1de308cea2d160778fd977825f10a07b49d738Adam Powell    }
5409a1de308cea2d160778fd977825f10a07b49d738Adam Powell
541b58b8f832d06b0ffa8886eba5a4916578a3b8743Dianne Hackborn    static void dispatchRouteChanged(RouteInfo info) {
54239d5c6172503620ac3761148adac5fd7fa20d02dAdam Powell        for (CallbackInfo cbi : sStatic.mCallbacks) {
5439a1de308cea2d160778fd977825f10a07b49d738Adam Powell            if ((cbi.type & info.mSupportedTypes) != 0) {
544b58b8f832d06b0ffa8886eba5a4916578a3b8743Dianne Hackborn                cbi.cb.onRouteChanged(cbi.router, info);
5459a1de308cea2d160778fd977825f10a07b49d738Adam Powell            }
5469a1de308cea2d160778fd977825f10a07b49d738Adam Powell        }
5479a1de308cea2d160778fd977825f10a07b49d738Adam Powell    }
5489a1de308cea2d160778fd977825f10a07b49d738Adam Powell
549b58b8f832d06b0ffa8886eba5a4916578a3b8743Dianne Hackborn    static void dispatchRouteAdded(RouteInfo info) {
55039d5c6172503620ac3761148adac5fd7fa20d02dAdam Powell        for (CallbackInfo cbi : sStatic.mCallbacks) {
5519a1de308cea2d160778fd977825f10a07b49d738Adam Powell            if ((cbi.type & info.mSupportedTypes) != 0) {
552b58b8f832d06b0ffa8886eba5a4916578a3b8743Dianne Hackborn                cbi.cb.onRouteAdded(cbi.router, info);
5539a1de308cea2d160778fd977825f10a07b49d738Adam Powell            }
5549a1de308cea2d160778fd977825f10a07b49d738Adam Powell        }
5559a1de308cea2d160778fd977825f10a07b49d738Adam Powell    }
5569a1de308cea2d160778fd977825f10a07b49d738Adam Powell
557b58b8f832d06b0ffa8886eba5a4916578a3b8743Dianne Hackborn    static void dispatchRouteRemoved(RouteInfo info) {
55839d5c6172503620ac3761148adac5fd7fa20d02dAdam Powell        for (CallbackInfo cbi : sStatic.mCallbacks) {
5599a1de308cea2d160778fd977825f10a07b49d738Adam Powell            if ((cbi.type & info.mSupportedTypes) != 0) {
560b58b8f832d06b0ffa8886eba5a4916578a3b8743Dianne Hackborn                cbi.cb.onRouteRemoved(cbi.router, info);
5619a1de308cea2d160778fd977825f10a07b49d738Adam Powell            }
5629a1de308cea2d160778fd977825f10a07b49d738Adam Powell        }
5639a1de308cea2d160778fd977825f10a07b49d738Adam Powell    }
5649a1de308cea2d160778fd977825f10a07b49d738Adam Powell
565b58b8f832d06b0ffa8886eba5a4916578a3b8743Dianne Hackborn    static void dispatchRouteGrouped(RouteInfo info, RouteGroup group, int index) {
56639d5c6172503620ac3761148adac5fd7fa20d02dAdam Powell        for (CallbackInfo cbi : sStatic.mCallbacks) {
567d0d2cda9d414da73773285d7fee9e13aef3495e9Adam Powell            if ((cbi.type & group.mSupportedTypes) != 0) {
568b58b8f832d06b0ffa8886eba5a4916578a3b8743Dianne Hackborn                cbi.cb.onRouteGrouped(cbi.router, info, group, index);
569d0d2cda9d414da73773285d7fee9e13aef3495e9Adam Powell            }
570d0d2cda9d414da73773285d7fee9e13aef3495e9Adam Powell        }
571d0d2cda9d414da73773285d7fee9e13aef3495e9Adam Powell    }
572d0d2cda9d414da73773285d7fee9e13aef3495e9Adam Powell
573b58b8f832d06b0ffa8886eba5a4916578a3b8743Dianne Hackborn    static void dispatchRouteUngrouped(RouteInfo info, RouteGroup group) {
57439d5c6172503620ac3761148adac5fd7fa20d02dAdam Powell        for (CallbackInfo cbi : sStatic.mCallbacks) {
575d0d2cda9d414da73773285d7fee9e13aef3495e9Adam Powell            if ((cbi.type & group.mSupportedTypes) != 0) {
576b58b8f832d06b0ffa8886eba5a4916578a3b8743Dianne Hackborn                cbi.cb.onRouteUngrouped(cbi.router, info, group);
5779a1de308cea2d160778fd977825f10a07b49d738Adam Powell            }
5789a1de308cea2d160778fd977825f10a07b49d738Adam Powell        }
5799a1de308cea2d160778fd977825f10a07b49d738Adam Powell    }
5809a1de308cea2d160778fd977825f10a07b49d738Adam Powell
5819a1de308cea2d160778fd977825f10a07b49d738Adam Powell    /**
5829a1de308cea2d160778fd977825f10a07b49d738Adam Powell     * Information about a media route.
5839a1de308cea2d160778fd977825f10a07b49d738Adam Powell     */
584b58b8f832d06b0ffa8886eba5a4916578a3b8743Dianne Hackborn    public static class RouteInfo {
5859a1de308cea2d160778fd977825f10a07b49d738Adam Powell        CharSequence mName;
5860d03c042f90bf62d5bad7c64e77028a5f9f8fae0Adam Powell        int mNameResId;
5879a1de308cea2d160778fd977825f10a07b49d738Adam Powell        private CharSequence mStatus;
5889a1de308cea2d160778fd977825f10a07b49d738Adam Powell        int mSupportedTypes;
5899a1de308cea2d160778fd977825f10a07b49d738Adam Powell        RouteGroup mGroup;
5909a1de308cea2d160778fd977825f10a07b49d738Adam Powell        final RouteCategory mCategory;
591ae20ae1a8aaa013813c356ae1d9541ca7ff020aeAdam Powell        Drawable mIcon;
5921357012968f9066ea3051d83995e9bac69526c3cJean-Michel Trivi        // playback information
5931357012968f9066ea3051d83995e9bac69526c3cJean-Michel Trivi        int mPlaybackType = PLAYBACK_TYPE_LOCAL;
5941357012968f9066ea3051d83995e9bac69526c3cJean-Michel Trivi        int mVolumeMax = RemoteControlClient.DEFAULT_PLAYBACK_VOLUME;
5951357012968f9066ea3051d83995e9bac69526c3cJean-Michel Trivi        int mVolume = RemoteControlClient.DEFAULT_PLAYBACK_VOLUME;
5961357012968f9066ea3051d83995e9bac69526c3cJean-Michel Trivi        int mVolumeHandling = RemoteControlClient.DEFAULT_PLAYBACK_VOLUME_HANDLING;
5971357012968f9066ea3051d83995e9bac69526c3cJean-Michel Trivi        int mPlaybackStream = AudioManager.STREAM_MUSIC;
5981357012968f9066ea3051d83995e9bac69526c3cJean-Michel Trivi        VolumeCallbackInfo mVcb;
5999a1de308cea2d160778fd977825f10a07b49d738Adam Powell
600b5e2af5919351486a385effe77409d2a91ae9c19Adam Powell        private Object mTag;
601b5e2af5919351486a385effe77409d2a91ae9c19Adam Powell
6021357012968f9066ea3051d83995e9bac69526c3cJean-Michel Trivi        /**
6031357012968f9066ea3051d83995e9bac69526c3cJean-Michel Trivi         * @hide (to be un-hidden)
6041357012968f9066ea3051d83995e9bac69526c3cJean-Michel Trivi         * The default playback type, "local", indicating the presentation of the media is happening
6051357012968f9066ea3051d83995e9bac69526c3cJean-Michel Trivi         * on the same device (e.g. a phone, a tablet) as where it is controlled from.
6061357012968f9066ea3051d83995e9bac69526c3cJean-Michel Trivi         * @see #setPlaybackType(int)
6071357012968f9066ea3051d83995e9bac69526c3cJean-Michel Trivi         */
6081357012968f9066ea3051d83995e9bac69526c3cJean-Michel Trivi        public final static int PLAYBACK_TYPE_LOCAL = 0;
6091357012968f9066ea3051d83995e9bac69526c3cJean-Michel Trivi        /**
6101357012968f9066ea3051d83995e9bac69526c3cJean-Michel Trivi         * @hide (to be un-hidden)
6111357012968f9066ea3051d83995e9bac69526c3cJean-Michel Trivi         * A playback type indicating the presentation of the media is happening on
6121357012968f9066ea3051d83995e9bac69526c3cJean-Michel Trivi         * a different device (i.e. the remote device) than where it is controlled from.
6131357012968f9066ea3051d83995e9bac69526c3cJean-Michel Trivi         * @see #setPlaybackType(int)
6141357012968f9066ea3051d83995e9bac69526c3cJean-Michel Trivi         */
6151357012968f9066ea3051d83995e9bac69526c3cJean-Michel Trivi        public final static int PLAYBACK_TYPE_REMOTE = 1;
6161357012968f9066ea3051d83995e9bac69526c3cJean-Michel Trivi        /**
6171357012968f9066ea3051d83995e9bac69526c3cJean-Michel Trivi         * @hide (to be un-hidden)
6181357012968f9066ea3051d83995e9bac69526c3cJean-Michel Trivi         * Playback information indicating the playback volume is fixed, i.e. it cannot be
6191357012968f9066ea3051d83995e9bac69526c3cJean-Michel Trivi         * controlled from this object. An example of fixed playback volume is a remote player,
6201357012968f9066ea3051d83995e9bac69526c3cJean-Michel Trivi         * playing over HDMI where the user prefers to control the volume on the HDMI sink, rather
6211357012968f9066ea3051d83995e9bac69526c3cJean-Michel Trivi         * than attenuate at the source.
6221357012968f9066ea3051d83995e9bac69526c3cJean-Michel Trivi         * @see #setVolumeHandling(int)
6231357012968f9066ea3051d83995e9bac69526c3cJean-Michel Trivi         */
6241357012968f9066ea3051d83995e9bac69526c3cJean-Michel Trivi        public final static int PLAYBACK_VOLUME_FIXED = 0;
6251357012968f9066ea3051d83995e9bac69526c3cJean-Michel Trivi        /**
6261357012968f9066ea3051d83995e9bac69526c3cJean-Michel Trivi         * @hide (to be un-hidden)
6271357012968f9066ea3051d83995e9bac69526c3cJean-Michel Trivi         * Playback information indicating the playback volume is variable and can be controlled
6281357012968f9066ea3051d83995e9bac69526c3cJean-Michel Trivi         * from this object.
6291357012968f9066ea3051d83995e9bac69526c3cJean-Michel Trivi         */
6301357012968f9066ea3051d83995e9bac69526c3cJean-Michel Trivi        public final static int PLAYBACK_VOLUME_VARIABLE = 1;
6311357012968f9066ea3051d83995e9bac69526c3cJean-Michel Trivi
6329a1de308cea2d160778fd977825f10a07b49d738Adam Powell        RouteInfo(RouteCategory category) {
6339a1de308cea2d160778fd977825f10a07b49d738Adam Powell            mCategory = category;
6349a1de308cea2d160778fd977825f10a07b49d738Adam Powell        }
6359a1de308cea2d160778fd977825f10a07b49d738Adam Powell
6369a1de308cea2d160778fd977825f10a07b49d738Adam Powell        /**
6379a1de308cea2d160778fd977825f10a07b49d738Adam Powell         * @return The user-friendly name of a media route. This is the string presented
6389a1de308cea2d160778fd977825f10a07b49d738Adam Powell         * to users who may select this as the active route.
6399a1de308cea2d160778fd977825f10a07b49d738Adam Powell         */
6409a1de308cea2d160778fd977825f10a07b49d738Adam Powell        public CharSequence getName() {
6410d03c042f90bf62d5bad7c64e77028a5f9f8fae0Adam Powell            return getName(sStatic.mResources);
6420d03c042f90bf62d5bad7c64e77028a5f9f8fae0Adam Powell        }
6430d03c042f90bf62d5bad7c64e77028a5f9f8fae0Adam Powell
6440d03c042f90bf62d5bad7c64e77028a5f9f8fae0Adam Powell        /**
6450d03c042f90bf62d5bad7c64e77028a5f9f8fae0Adam Powell         * Return the properly localized/resource selected name of this route.
6460d03c042f90bf62d5bad7c64e77028a5f9f8fae0Adam Powell         *
6470d03c042f90bf62d5bad7c64e77028a5f9f8fae0Adam Powell         * @param context Context used to resolve the correct configuration to load
6480d03c042f90bf62d5bad7c64e77028a5f9f8fae0Adam Powell         * @return The user-friendly name of the media route. This is the string presented
6490d03c042f90bf62d5bad7c64e77028a5f9f8fae0Adam Powell         * to users who may select this as the active route.
6500d03c042f90bf62d5bad7c64e77028a5f9f8fae0Adam Powell         */
6510d03c042f90bf62d5bad7c64e77028a5f9f8fae0Adam Powell        public CharSequence getName(Context context) {
6520d03c042f90bf62d5bad7c64e77028a5f9f8fae0Adam Powell            return getName(context.getResources());
6530d03c042f90bf62d5bad7c64e77028a5f9f8fae0Adam Powell        }
6540d03c042f90bf62d5bad7c64e77028a5f9f8fae0Adam Powell
6550d03c042f90bf62d5bad7c64e77028a5f9f8fae0Adam Powell        CharSequence getName(Resources res) {
6560d03c042f90bf62d5bad7c64e77028a5f9f8fae0Adam Powell            if (mNameResId != 0) {
6570d03c042f90bf62d5bad7c64e77028a5f9f8fae0Adam Powell                return mName = res.getText(mNameResId);
6580d03c042f90bf62d5bad7c64e77028a5f9f8fae0Adam Powell            }
6599a1de308cea2d160778fd977825f10a07b49d738Adam Powell            return mName;
6609a1de308cea2d160778fd977825f10a07b49d738Adam Powell        }
6619a1de308cea2d160778fd977825f10a07b49d738Adam Powell
6629a1de308cea2d160778fd977825f10a07b49d738Adam Powell        /**
6639a1de308cea2d160778fd977825f10a07b49d738Adam Powell         * @return The user-friendly status for a media route. This may include a description
6649a1de308cea2d160778fd977825f10a07b49d738Adam Powell         * of the currently playing media, if available.
6659a1de308cea2d160778fd977825f10a07b49d738Adam Powell         */
6669a1de308cea2d160778fd977825f10a07b49d738Adam Powell        public CharSequence getStatus() {
6679a1de308cea2d160778fd977825f10a07b49d738Adam Powell            return mStatus;
6689a1de308cea2d160778fd977825f10a07b49d738Adam Powell        }
6699a1de308cea2d160778fd977825f10a07b49d738Adam Powell
6709a1de308cea2d160778fd977825f10a07b49d738Adam Powell        /**
6719a1de308cea2d160778fd977825f10a07b49d738Adam Powell         * @return A media type flag set describing which types this route supports.
6729a1de308cea2d160778fd977825f10a07b49d738Adam Powell         */
6739a1de308cea2d160778fd977825f10a07b49d738Adam Powell        public int getSupportedTypes() {
6749a1de308cea2d160778fd977825f10a07b49d738Adam Powell            return mSupportedTypes;
6759a1de308cea2d160778fd977825f10a07b49d738Adam Powell        }
6769a1de308cea2d160778fd977825f10a07b49d738Adam Powell
6779a1de308cea2d160778fd977825f10a07b49d738Adam Powell        /**
6789a1de308cea2d160778fd977825f10a07b49d738Adam Powell         * @return The group that this route belongs to.
6799a1de308cea2d160778fd977825f10a07b49d738Adam Powell         */
6809a1de308cea2d160778fd977825f10a07b49d738Adam Powell        public RouteGroup getGroup() {
6819a1de308cea2d160778fd977825f10a07b49d738Adam Powell            return mGroup;
6829a1de308cea2d160778fd977825f10a07b49d738Adam Powell        }
6839a1de308cea2d160778fd977825f10a07b49d738Adam Powell
6849a1de308cea2d160778fd977825f10a07b49d738Adam Powell        /**
6859a1de308cea2d160778fd977825f10a07b49d738Adam Powell         * @return the category this route belongs to.
6869a1de308cea2d160778fd977825f10a07b49d738Adam Powell         */
6879a1de308cea2d160778fd977825f10a07b49d738Adam Powell        public RouteCategory getCategory() {
6889a1de308cea2d160778fd977825f10a07b49d738Adam Powell            return mCategory;
6899a1de308cea2d160778fd977825f10a07b49d738Adam Powell        }
6909a1de308cea2d160778fd977825f10a07b49d738Adam Powell
691ae20ae1a8aaa013813c356ae1d9541ca7ff020aeAdam Powell        /**
692ae20ae1a8aaa013813c356ae1d9541ca7ff020aeAdam Powell         * Get the icon representing this route.
693ae20ae1a8aaa013813c356ae1d9541ca7ff020aeAdam Powell         * This icon will be used in picker UIs if available.
694ae20ae1a8aaa013813c356ae1d9541ca7ff020aeAdam Powell         *
695ae20ae1a8aaa013813c356ae1d9541ca7ff020aeAdam Powell         * @return the icon representing this route or null if no icon is available
696ae20ae1a8aaa013813c356ae1d9541ca7ff020aeAdam Powell         */
697ae20ae1a8aaa013813c356ae1d9541ca7ff020aeAdam Powell        public Drawable getIconDrawable() {
698ae20ae1a8aaa013813c356ae1d9541ca7ff020aeAdam Powell            return mIcon;
699ae20ae1a8aaa013813c356ae1d9541ca7ff020aeAdam Powell        }
700ae20ae1a8aaa013813c356ae1d9541ca7ff020aeAdam Powell
701b5e2af5919351486a385effe77409d2a91ae9c19Adam Powell        /**
702b5e2af5919351486a385effe77409d2a91ae9c19Adam Powell         * Set an application-specific tag object for this route.
703b5e2af5919351486a385effe77409d2a91ae9c19Adam Powell         * The application may use this to store arbitrary data associated with the
704b5e2af5919351486a385effe77409d2a91ae9c19Adam Powell         * route for internal tracking.
705b5e2af5919351486a385effe77409d2a91ae9c19Adam Powell         *
706b5e2af5919351486a385effe77409d2a91ae9c19Adam Powell         * <p>Note that the lifespan of a route may be well past the lifespan of
707b5e2af5919351486a385effe77409d2a91ae9c19Adam Powell         * an Activity or other Context; take care that objects you store here
708b5e2af5919351486a385effe77409d2a91ae9c19Adam Powell         * will not keep more data in memory alive than you intend.</p>
709b5e2af5919351486a385effe77409d2a91ae9c19Adam Powell         *
710b5e2af5919351486a385effe77409d2a91ae9c19Adam Powell         * @param tag Arbitrary, app-specific data for this route to hold for later use
711b5e2af5919351486a385effe77409d2a91ae9c19Adam Powell         */
712b5e2af5919351486a385effe77409d2a91ae9c19Adam Powell        public void setTag(Object tag) {
713b5e2af5919351486a385effe77409d2a91ae9c19Adam Powell            mTag = tag;
714130b4572d1f3df702e5b296a655d15a41f6d4c66Adam Powell            routeUpdated();
715b5e2af5919351486a385effe77409d2a91ae9c19Adam Powell        }
716b5e2af5919351486a385effe77409d2a91ae9c19Adam Powell
717b5e2af5919351486a385effe77409d2a91ae9c19Adam Powell        /**
718b5e2af5919351486a385effe77409d2a91ae9c19Adam Powell         * @return The tag object previously set by the application
719b5e2af5919351486a385effe77409d2a91ae9c19Adam Powell         * @see #setTag(Object)
720b5e2af5919351486a385effe77409d2a91ae9c19Adam Powell         */
721b5e2af5919351486a385effe77409d2a91ae9c19Adam Powell        public Object getTag() {
722b5e2af5919351486a385effe77409d2a91ae9c19Adam Powell            return mTag;
723b5e2af5919351486a385effe77409d2a91ae9c19Adam Powell        }
724b5e2af5919351486a385effe77409d2a91ae9c19Adam Powell
7251357012968f9066ea3051d83995e9bac69526c3cJean-Michel Trivi        /**
7261357012968f9066ea3051d83995e9bac69526c3cJean-Michel Trivi         * @hide (to be un-hidden)
7271357012968f9066ea3051d83995e9bac69526c3cJean-Michel Trivi         * @return the type of playback associated with this route
7281357012968f9066ea3051d83995e9bac69526c3cJean-Michel Trivi         * @see UserRouteInfo#setPlaybackType(int)
7291357012968f9066ea3051d83995e9bac69526c3cJean-Michel Trivi         */
7301357012968f9066ea3051d83995e9bac69526c3cJean-Michel Trivi        public int getPlaybackType() {
7311357012968f9066ea3051d83995e9bac69526c3cJean-Michel Trivi            return mPlaybackType;
7321357012968f9066ea3051d83995e9bac69526c3cJean-Michel Trivi        }
7331357012968f9066ea3051d83995e9bac69526c3cJean-Michel Trivi
7341357012968f9066ea3051d83995e9bac69526c3cJean-Michel Trivi        /**
7351357012968f9066ea3051d83995e9bac69526c3cJean-Michel Trivi         * @hide (to be un-hidden)
7361357012968f9066ea3051d83995e9bac69526c3cJean-Michel Trivi         * @return the stream over which the playback associated with this route is performed
7371357012968f9066ea3051d83995e9bac69526c3cJean-Michel Trivi         * @see UserRouteInfo#setPlaybackStream(int)
7381357012968f9066ea3051d83995e9bac69526c3cJean-Michel Trivi         */
7391357012968f9066ea3051d83995e9bac69526c3cJean-Michel Trivi        public int getPlaybackStream() {
7401357012968f9066ea3051d83995e9bac69526c3cJean-Michel Trivi            return mPlaybackStream;
7411357012968f9066ea3051d83995e9bac69526c3cJean-Michel Trivi        }
7421357012968f9066ea3051d83995e9bac69526c3cJean-Michel Trivi
7431357012968f9066ea3051d83995e9bac69526c3cJean-Michel Trivi        /**
7441357012968f9066ea3051d83995e9bac69526c3cJean-Michel Trivi         * @hide (to be un-hidden)
7451357012968f9066ea3051d83995e9bac69526c3cJean-Michel Trivi         * @return the volume at which the playback associated with this route is performed
7461357012968f9066ea3051d83995e9bac69526c3cJean-Michel Trivi         * @see UserRouteInfo#setVolume(int)
7471357012968f9066ea3051d83995e9bac69526c3cJean-Michel Trivi         */
7481357012968f9066ea3051d83995e9bac69526c3cJean-Michel Trivi        public int getVolume() {
7491357012968f9066ea3051d83995e9bac69526c3cJean-Michel Trivi            if (mPlaybackType == PLAYBACK_TYPE_LOCAL) {
7501357012968f9066ea3051d83995e9bac69526c3cJean-Michel Trivi                int vol = 0;
7511357012968f9066ea3051d83995e9bac69526c3cJean-Michel Trivi                try {
7521357012968f9066ea3051d83995e9bac69526c3cJean-Michel Trivi                    vol = sStatic.mAudioService.getStreamVolume(mPlaybackStream);
7531357012968f9066ea3051d83995e9bac69526c3cJean-Michel Trivi                } catch (RemoteException e) {
7541357012968f9066ea3051d83995e9bac69526c3cJean-Michel Trivi                    Log.e(TAG, "Error getting local stream volume", e);
7551357012968f9066ea3051d83995e9bac69526c3cJean-Michel Trivi                }
7561357012968f9066ea3051d83995e9bac69526c3cJean-Michel Trivi                return vol;
7571357012968f9066ea3051d83995e9bac69526c3cJean-Michel Trivi            } else {
7581357012968f9066ea3051d83995e9bac69526c3cJean-Michel Trivi                return mVolume;
7591357012968f9066ea3051d83995e9bac69526c3cJean-Michel Trivi            }
7601357012968f9066ea3051d83995e9bac69526c3cJean-Michel Trivi        }
7611357012968f9066ea3051d83995e9bac69526c3cJean-Michel Trivi
7621357012968f9066ea3051d83995e9bac69526c3cJean-Michel Trivi        /**
7631357012968f9066ea3051d83995e9bac69526c3cJean-Michel Trivi         * @hide (to be un-hidden)
7641357012968f9066ea3051d83995e9bac69526c3cJean-Michel Trivi         * @return the maximum volume at which the playback associated with this route is performed
7651357012968f9066ea3051d83995e9bac69526c3cJean-Michel Trivi         * @see UserRouteInfo#setVolumeMax(int)
7661357012968f9066ea3051d83995e9bac69526c3cJean-Michel Trivi         */
7671357012968f9066ea3051d83995e9bac69526c3cJean-Michel Trivi        public int getVolumeMax() {
7681357012968f9066ea3051d83995e9bac69526c3cJean-Michel Trivi            if (mPlaybackType == PLAYBACK_TYPE_LOCAL) {
7691357012968f9066ea3051d83995e9bac69526c3cJean-Michel Trivi                int volMax = 0;
7701357012968f9066ea3051d83995e9bac69526c3cJean-Michel Trivi                try {
7711357012968f9066ea3051d83995e9bac69526c3cJean-Michel Trivi                    volMax = sStatic.mAudioService.getStreamMaxVolume(mPlaybackStream);
7721357012968f9066ea3051d83995e9bac69526c3cJean-Michel Trivi                } catch (RemoteException e) {
7731357012968f9066ea3051d83995e9bac69526c3cJean-Michel Trivi                    Log.e(TAG, "Error getting local stream volume", e);
7741357012968f9066ea3051d83995e9bac69526c3cJean-Michel Trivi                }
7751357012968f9066ea3051d83995e9bac69526c3cJean-Michel Trivi                return volMax;
7761357012968f9066ea3051d83995e9bac69526c3cJean-Michel Trivi            } else {
7771357012968f9066ea3051d83995e9bac69526c3cJean-Michel Trivi                return mVolumeMax;
7781357012968f9066ea3051d83995e9bac69526c3cJean-Michel Trivi            }
7791357012968f9066ea3051d83995e9bac69526c3cJean-Michel Trivi        }
7801357012968f9066ea3051d83995e9bac69526c3cJean-Michel Trivi
7811357012968f9066ea3051d83995e9bac69526c3cJean-Michel Trivi        /**
7821357012968f9066ea3051d83995e9bac69526c3cJean-Michel Trivi         * @hide (to be un-hidden)
7831357012968f9066ea3051d83995e9bac69526c3cJean-Michel Trivi         * @return how volume is handling on the route
7841357012968f9066ea3051d83995e9bac69526c3cJean-Michel Trivi         * @see UserRouteInfo#setVolumeHandling(int)
7851357012968f9066ea3051d83995e9bac69526c3cJean-Michel Trivi         */
7861357012968f9066ea3051d83995e9bac69526c3cJean-Michel Trivi        public int getVolumeHandling() {
7871357012968f9066ea3051d83995e9bac69526c3cJean-Michel Trivi            return mVolumeHandling;
7881357012968f9066ea3051d83995e9bac69526c3cJean-Michel Trivi        }
7891357012968f9066ea3051d83995e9bac69526c3cJean-Michel Trivi
7909a1de308cea2d160778fd977825f10a07b49d738Adam Powell        void setStatusInt(CharSequence status) {
7919a1de308cea2d160778fd977825f10a07b49d738Adam Powell            if (!status.equals(mStatus)) {
7929a1de308cea2d160778fd977825f10a07b49d738Adam Powell                mStatus = status;
7939a1de308cea2d160778fd977825f10a07b49d738Adam Powell                if (mGroup != null) {
7949a1de308cea2d160778fd977825f10a07b49d738Adam Powell                    mGroup.memberStatusChanged(this, status);
7959a1de308cea2d160778fd977825f10a07b49d738Adam Powell                }
7969a1de308cea2d160778fd977825f10a07b49d738Adam Powell                routeUpdated();
7979a1de308cea2d160778fd977825f10a07b49d738Adam Powell            }
7989a1de308cea2d160778fd977825f10a07b49d738Adam Powell        }
7999a1de308cea2d160778fd977825f10a07b49d738Adam Powell
8001357012968f9066ea3051d83995e9bac69526c3cJean-Michel Trivi        final IRemoteVolumeObserver.Stub mRemoteVolObserver = new IRemoteVolumeObserver.Stub() {
8011357012968f9066ea3051d83995e9bac69526c3cJean-Michel Trivi            public void dispatchRemoteVolumeUpdate(final int direction, final int value) {
8021357012968f9066ea3051d83995e9bac69526c3cJean-Michel Trivi                sStatic.mHandler.post(new Runnable() {
8031357012968f9066ea3051d83995e9bac69526c3cJean-Michel Trivi                    @Override
8041357012968f9066ea3051d83995e9bac69526c3cJean-Michel Trivi                    public void run() {
8051357012968f9066ea3051d83995e9bac69526c3cJean-Michel Trivi                      //Log.d(TAG, "dispatchRemoteVolumeUpdate dir=" + direction + " val=" + value);
8061357012968f9066ea3051d83995e9bac69526c3cJean-Michel Trivi                        if (mVcb != null) {
8071357012968f9066ea3051d83995e9bac69526c3cJean-Michel Trivi                            if (direction != 0) {
8081357012968f9066ea3051d83995e9bac69526c3cJean-Michel Trivi                                mVcb.vcb.onVolumeUpdateRequest(mVcb.route, direction);
8091357012968f9066ea3051d83995e9bac69526c3cJean-Michel Trivi                            } else {
8101357012968f9066ea3051d83995e9bac69526c3cJean-Michel Trivi                                mVcb.vcb.onVolumeSetRequest(mVcb.route, value);
8111357012968f9066ea3051d83995e9bac69526c3cJean-Michel Trivi                            }
8121357012968f9066ea3051d83995e9bac69526c3cJean-Michel Trivi                        }
8131357012968f9066ea3051d83995e9bac69526c3cJean-Michel Trivi                    }
8141357012968f9066ea3051d83995e9bac69526c3cJean-Michel Trivi                });
8151357012968f9066ea3051d83995e9bac69526c3cJean-Michel Trivi            }
8161357012968f9066ea3051d83995e9bac69526c3cJean-Michel Trivi        };
8171357012968f9066ea3051d83995e9bac69526c3cJean-Michel Trivi
8189a1de308cea2d160778fd977825f10a07b49d738Adam Powell        void routeUpdated() {
8199a1de308cea2d160778fd977825f10a07b49d738Adam Powell            updateRoute(this);
8209a1de308cea2d160778fd977825f10a07b49d738Adam Powell        }
8219a1de308cea2d160778fd977825f10a07b49d738Adam Powell
8229a1de308cea2d160778fd977825f10a07b49d738Adam Powell        @Override
8239a1de308cea2d160778fd977825f10a07b49d738Adam Powell        public String toString() {
824d6d0bddee363e0c7fe61f63bd9d9864a71d887d6Adam Powell            String supportedTypes = typesToString(getSupportedTypes());
825d6d0bddee363e0c7fe61f63bd9d9864a71d887d6Adam Powell            return getClass().getSimpleName() + "{ name=" + getName() + ", status=" + getStatus() +
826d6d0bddee363e0c7fe61f63bd9d9864a71d887d6Adam Powell                    " category=" + getCategory() +
8279a1de308cea2d160778fd977825f10a07b49d738Adam Powell                    " supportedTypes=" + supportedTypes + "}";
8289a1de308cea2d160778fd977825f10a07b49d738Adam Powell        }
8299a1de308cea2d160778fd977825f10a07b49d738Adam Powell    }
8309a1de308cea2d160778fd977825f10a07b49d738Adam Powell
8319a1de308cea2d160778fd977825f10a07b49d738Adam Powell    /**
8329a1de308cea2d160778fd977825f10a07b49d738Adam Powell     * Information about a route that the application may define and modify.
8339a1de308cea2d160778fd977825f10a07b49d738Adam Powell     *
8349a1de308cea2d160778fd977825f10a07b49d738Adam Powell     * @see MediaRouter.RouteInfo
8359a1de308cea2d160778fd977825f10a07b49d738Adam Powell     */
836b58b8f832d06b0ffa8886eba5a4916578a3b8743Dianne Hackborn    public static class UserRouteInfo extends RouteInfo {
837ae20ae1a8aaa013813c356ae1d9541ca7ff020aeAdam Powell        RemoteControlClient mRcc;
8389a1de308cea2d160778fd977825f10a07b49d738Adam Powell
8399a1de308cea2d160778fd977825f10a07b49d738Adam Powell        UserRouteInfo(RouteCategory category) {
8409a1de308cea2d160778fd977825f10a07b49d738Adam Powell            super(category);
8419a1de308cea2d160778fd977825f10a07b49d738Adam Powell            mSupportedTypes = ROUTE_TYPE_USER;
8429a1de308cea2d160778fd977825f10a07b49d738Adam Powell        }
8439a1de308cea2d160778fd977825f10a07b49d738Adam Powell
8449a1de308cea2d160778fd977825f10a07b49d738Adam Powell        /**
8459a1de308cea2d160778fd977825f10a07b49d738Adam Powell         * Set the user-visible name of this route.
8469a1de308cea2d160778fd977825f10a07b49d738Adam Powell         * @param name Name to display to the user to describe this route
8479a1de308cea2d160778fd977825f10a07b49d738Adam Powell         */
8489a1de308cea2d160778fd977825f10a07b49d738Adam Powell        public void setName(CharSequence name) {
8499a1de308cea2d160778fd977825f10a07b49d738Adam Powell            mName = name;
8509a1de308cea2d160778fd977825f10a07b49d738Adam Powell            routeUpdated();
8519a1de308cea2d160778fd977825f10a07b49d738Adam Powell        }
8520d03c042f90bf62d5bad7c64e77028a5f9f8fae0Adam Powell
8530d03c042f90bf62d5bad7c64e77028a5f9f8fae0Adam Powell        /**
8540d03c042f90bf62d5bad7c64e77028a5f9f8fae0Adam Powell         * Set the user-visible name of this route.
8550d03c042f90bf62d5bad7c64e77028a5f9f8fae0Adam Powell         * @param resId Resource ID of the name to display to the user to describe this route
8560d03c042f90bf62d5bad7c64e77028a5f9f8fae0Adam Powell         */
8570d03c042f90bf62d5bad7c64e77028a5f9f8fae0Adam Powell        public void setName(int resId) {
8580d03c042f90bf62d5bad7c64e77028a5f9f8fae0Adam Powell            mNameResId = resId;
8590d03c042f90bf62d5bad7c64e77028a5f9f8fae0Adam Powell            mName = null;
8600d03c042f90bf62d5bad7c64e77028a5f9f8fae0Adam Powell            routeUpdated();
8610d03c042f90bf62d5bad7c64e77028a5f9f8fae0Adam Powell        }
8629a1de308cea2d160778fd977825f10a07b49d738Adam Powell
8639a1de308cea2d160778fd977825f10a07b49d738Adam Powell        /**
8649a1de308cea2d160778fd977825f10a07b49d738Adam Powell         * Set the current user-visible status for this route.
8659a1de308cea2d160778fd977825f10a07b49d738Adam Powell         * @param status Status to display to the user to describe what the endpoint
8669a1de308cea2d160778fd977825f10a07b49d738Adam Powell         * of this route is currently doing
8679a1de308cea2d160778fd977825f10a07b49d738Adam Powell         */
8689a1de308cea2d160778fd977825f10a07b49d738Adam Powell        public void setStatus(CharSequence status) {
8699a1de308cea2d160778fd977825f10a07b49d738Adam Powell            setStatusInt(status);
8709a1de308cea2d160778fd977825f10a07b49d738Adam Powell        }
871ae20ae1a8aaa013813c356ae1d9541ca7ff020aeAdam Powell
872ae20ae1a8aaa013813c356ae1d9541ca7ff020aeAdam Powell        /**
873ae20ae1a8aaa013813c356ae1d9541ca7ff020aeAdam Powell         * Set the RemoteControlClient responsible for reporting playback info for this
874ae20ae1a8aaa013813c356ae1d9541ca7ff020aeAdam Powell         * user route.
875ae20ae1a8aaa013813c356ae1d9541ca7ff020aeAdam Powell         *
876ae20ae1a8aaa013813c356ae1d9541ca7ff020aeAdam Powell         * <p>If this route manages remote playback, the data exposed by this
877ae20ae1a8aaa013813c356ae1d9541ca7ff020aeAdam Powell         * RemoteControlClient will be used to reflect and update information
878ae20ae1a8aaa013813c356ae1d9541ca7ff020aeAdam Powell         * such as route volume info in related UIs.</p>
879ae20ae1a8aaa013813c356ae1d9541ca7ff020aeAdam Powell         *
8801357012968f9066ea3051d83995e9bac69526c3cJean-Michel Trivi         * <p>The RemoteControlClient must have been previously registered with
8811357012968f9066ea3051d83995e9bac69526c3cJean-Michel Trivi         * {@link AudioManager#registerRemoteControlClient(RemoteControlClient)}.</p>
8821357012968f9066ea3051d83995e9bac69526c3cJean-Michel Trivi         *
883ae20ae1a8aaa013813c356ae1d9541ca7ff020aeAdam Powell         * @param rcc RemoteControlClient associated with this route
884ae20ae1a8aaa013813c356ae1d9541ca7ff020aeAdam Powell         */
885ae20ae1a8aaa013813c356ae1d9541ca7ff020aeAdam Powell        public void setRemoteControlClient(RemoteControlClient rcc) {
886ae20ae1a8aaa013813c356ae1d9541ca7ff020aeAdam Powell            mRcc = rcc;
8871357012968f9066ea3051d83995e9bac69526c3cJean-Michel Trivi            updatePlaybackInfoOnRcc();
888ae20ae1a8aaa013813c356ae1d9541ca7ff020aeAdam Powell        }
889ae20ae1a8aaa013813c356ae1d9541ca7ff020aeAdam Powell
890ae20ae1a8aaa013813c356ae1d9541ca7ff020aeAdam Powell        /**
8914599696591f745b3a546197d2ba7e5cfc5562484Adam Powell         * Retrieve the RemoteControlClient associated with this route, if one has been set.
8924599696591f745b3a546197d2ba7e5cfc5562484Adam Powell         *
8934599696591f745b3a546197d2ba7e5cfc5562484Adam Powell         * @return the RemoteControlClient associated with this route
8944599696591f745b3a546197d2ba7e5cfc5562484Adam Powell         * @see #setRemoteControlClient(RemoteControlClient)
8954599696591f745b3a546197d2ba7e5cfc5562484Adam Powell         */
8964599696591f745b3a546197d2ba7e5cfc5562484Adam Powell        public RemoteControlClient getRemoteControlClient() {
8974599696591f745b3a546197d2ba7e5cfc5562484Adam Powell            return mRcc;
8984599696591f745b3a546197d2ba7e5cfc5562484Adam Powell        }
8994599696591f745b3a546197d2ba7e5cfc5562484Adam Powell
9004599696591f745b3a546197d2ba7e5cfc5562484Adam Powell        /**
901ae20ae1a8aaa013813c356ae1d9541ca7ff020aeAdam Powell         * Set an icon that will be used to represent this route.
902ae20ae1a8aaa013813c356ae1d9541ca7ff020aeAdam Powell         * The system may use this icon in picker UIs or similar.
903ae20ae1a8aaa013813c356ae1d9541ca7ff020aeAdam Powell         *
904ae20ae1a8aaa013813c356ae1d9541ca7ff020aeAdam Powell         * @param icon icon drawable to use to represent this route
905ae20ae1a8aaa013813c356ae1d9541ca7ff020aeAdam Powell         */
906ae20ae1a8aaa013813c356ae1d9541ca7ff020aeAdam Powell        public void setIconDrawable(Drawable icon) {
907ae20ae1a8aaa013813c356ae1d9541ca7ff020aeAdam Powell            mIcon = icon;
908ae20ae1a8aaa013813c356ae1d9541ca7ff020aeAdam Powell        }
909ae20ae1a8aaa013813c356ae1d9541ca7ff020aeAdam Powell
910ae20ae1a8aaa013813c356ae1d9541ca7ff020aeAdam Powell        /**
911ae20ae1a8aaa013813c356ae1d9541ca7ff020aeAdam Powell         * Set an icon that will be used to represent this route.
912ae20ae1a8aaa013813c356ae1d9541ca7ff020aeAdam Powell         * The system may use this icon in picker UIs or similar.
913ae20ae1a8aaa013813c356ae1d9541ca7ff020aeAdam Powell         *
91471c69897ad0a55d590698bfa399bfe99c763b9dbAdam Powell         * @param resId Resource ID of an icon drawable to use to represent this route
915ae20ae1a8aaa013813c356ae1d9541ca7ff020aeAdam Powell         */
916ae20ae1a8aaa013813c356ae1d9541ca7ff020aeAdam Powell        public void setIconResource(int resId) {
917ae20ae1a8aaa013813c356ae1d9541ca7ff020aeAdam Powell            setIconDrawable(sStatic.mResources.getDrawable(resId));
918ae20ae1a8aaa013813c356ae1d9541ca7ff020aeAdam Powell        }
9191357012968f9066ea3051d83995e9bac69526c3cJean-Michel Trivi
9201357012968f9066ea3051d83995e9bac69526c3cJean-Michel Trivi        /**
9211357012968f9066ea3051d83995e9bac69526c3cJean-Michel Trivi         * @hide (to be un-hidden)
9221357012968f9066ea3051d83995e9bac69526c3cJean-Michel Trivi         * Set a callback to be notified of volume update requests
9231357012968f9066ea3051d83995e9bac69526c3cJean-Michel Trivi         * @param vcb
9241357012968f9066ea3051d83995e9bac69526c3cJean-Michel Trivi         */
9251357012968f9066ea3051d83995e9bac69526c3cJean-Michel Trivi        public void setVolumeCallback(VolumeCallback vcb) {
9261357012968f9066ea3051d83995e9bac69526c3cJean-Michel Trivi            mVcb = new VolumeCallbackInfo(vcb, this);
9271357012968f9066ea3051d83995e9bac69526c3cJean-Michel Trivi        }
9281357012968f9066ea3051d83995e9bac69526c3cJean-Michel Trivi
9291357012968f9066ea3051d83995e9bac69526c3cJean-Michel Trivi        /**
9301357012968f9066ea3051d83995e9bac69526c3cJean-Michel Trivi         * @hide (to be un-hidden)
9311357012968f9066ea3051d83995e9bac69526c3cJean-Michel Trivi         * Defines whether playback associated with this route is "local"
9321357012968f9066ea3051d83995e9bac69526c3cJean-Michel Trivi         *    ({@link RouteInfo#PLAYBACK_TYPE_LOCAL}) or "remote"
9331357012968f9066ea3051d83995e9bac69526c3cJean-Michel Trivi         *    ({@link RouteInfo#PLAYBACK_TYPE_REMOTE}).
9341357012968f9066ea3051d83995e9bac69526c3cJean-Michel Trivi         * @param type
9351357012968f9066ea3051d83995e9bac69526c3cJean-Michel Trivi         */
9361357012968f9066ea3051d83995e9bac69526c3cJean-Michel Trivi        public void setPlaybackType(int type) {
9371357012968f9066ea3051d83995e9bac69526c3cJean-Michel Trivi            if (mPlaybackType != type) {
9381357012968f9066ea3051d83995e9bac69526c3cJean-Michel Trivi                mPlaybackType = type;
9391357012968f9066ea3051d83995e9bac69526c3cJean-Michel Trivi                setPlaybackInfoOnRcc(RemoteControlClient.PLAYBACKINFO_PLAYBACK_TYPE, type);
9401357012968f9066ea3051d83995e9bac69526c3cJean-Michel Trivi            }
9411357012968f9066ea3051d83995e9bac69526c3cJean-Michel Trivi        }
9421357012968f9066ea3051d83995e9bac69526c3cJean-Michel Trivi
9431357012968f9066ea3051d83995e9bac69526c3cJean-Michel Trivi        /**
9441357012968f9066ea3051d83995e9bac69526c3cJean-Michel Trivi         * @hide (to be un-hidden)
9451357012968f9066ea3051d83995e9bac69526c3cJean-Michel Trivi         * Defines whether volume for the playback associated with this route is fixed
9461357012968f9066ea3051d83995e9bac69526c3cJean-Michel Trivi         * ({@link RouteInfo#PLAYBACK_VOLUME_FIXED}) or can modified
9471357012968f9066ea3051d83995e9bac69526c3cJean-Michel Trivi         * ({@link RouteInfo#PLAYBACK_VOLUME_VARIABLE}).
9481357012968f9066ea3051d83995e9bac69526c3cJean-Michel Trivi         * @param volumeHandling
9491357012968f9066ea3051d83995e9bac69526c3cJean-Michel Trivi         */
9501357012968f9066ea3051d83995e9bac69526c3cJean-Michel Trivi        public void setVolumeHandling(int volumeHandling) {
9511357012968f9066ea3051d83995e9bac69526c3cJean-Michel Trivi            if (mVolumeHandling != volumeHandling) {
9521357012968f9066ea3051d83995e9bac69526c3cJean-Michel Trivi                mVolumeHandling = volumeHandling;
9531357012968f9066ea3051d83995e9bac69526c3cJean-Michel Trivi                setPlaybackInfoOnRcc(
9541357012968f9066ea3051d83995e9bac69526c3cJean-Michel Trivi                        RemoteControlClient.PLAYBACKINFO_VOLUME_HANDLING, volumeHandling);
9551357012968f9066ea3051d83995e9bac69526c3cJean-Michel Trivi            }
9561357012968f9066ea3051d83995e9bac69526c3cJean-Michel Trivi        }
9571357012968f9066ea3051d83995e9bac69526c3cJean-Michel Trivi
9581357012968f9066ea3051d83995e9bac69526c3cJean-Michel Trivi        /**
9591357012968f9066ea3051d83995e9bac69526c3cJean-Michel Trivi         * @hide (to be un-hidden)
9601357012968f9066ea3051d83995e9bac69526c3cJean-Michel Trivi         * Defines at what volume the playback associated with this route is performed (for user
9611357012968f9066ea3051d83995e9bac69526c3cJean-Michel Trivi         * feedback purposes). This information is only used when the playback is not local.
9621357012968f9066ea3051d83995e9bac69526c3cJean-Michel Trivi         * @param volume
9631357012968f9066ea3051d83995e9bac69526c3cJean-Michel Trivi         */
9641357012968f9066ea3051d83995e9bac69526c3cJean-Michel Trivi        public void setVolume(int volume) {
9651357012968f9066ea3051d83995e9bac69526c3cJean-Michel Trivi            if (mVolume != volume) {
9661357012968f9066ea3051d83995e9bac69526c3cJean-Michel Trivi                mVolume = volume;
9671357012968f9066ea3051d83995e9bac69526c3cJean-Michel Trivi                setPlaybackInfoOnRcc(RemoteControlClient.PLAYBACKINFO_VOLUME, volume);
9681357012968f9066ea3051d83995e9bac69526c3cJean-Michel Trivi            }
9691357012968f9066ea3051d83995e9bac69526c3cJean-Michel Trivi        }
9701357012968f9066ea3051d83995e9bac69526c3cJean-Michel Trivi
9711357012968f9066ea3051d83995e9bac69526c3cJean-Michel Trivi        /**
9721357012968f9066ea3051d83995e9bac69526c3cJean-Michel Trivi         * @hide (to be un-hidden)
9731357012968f9066ea3051d83995e9bac69526c3cJean-Michel Trivi         * Defines the maximum volume at which the playback associated with this route is performed
9741357012968f9066ea3051d83995e9bac69526c3cJean-Michel Trivi         * (for user feedback purposes). This information is only used when the playback is not
9751357012968f9066ea3051d83995e9bac69526c3cJean-Michel Trivi         * local.
9761357012968f9066ea3051d83995e9bac69526c3cJean-Michel Trivi         * @param volumeMax
9771357012968f9066ea3051d83995e9bac69526c3cJean-Michel Trivi         */
9781357012968f9066ea3051d83995e9bac69526c3cJean-Michel Trivi        public void setVolumeMax(int volumeMax) {
9791357012968f9066ea3051d83995e9bac69526c3cJean-Michel Trivi            if (mVolumeMax != volumeMax) {
9801357012968f9066ea3051d83995e9bac69526c3cJean-Michel Trivi                mVolumeMax = volumeMax;
9811357012968f9066ea3051d83995e9bac69526c3cJean-Michel Trivi                setPlaybackInfoOnRcc(RemoteControlClient.PLAYBACKINFO_VOLUME_MAX, volumeMax);
9821357012968f9066ea3051d83995e9bac69526c3cJean-Michel Trivi            }
9831357012968f9066ea3051d83995e9bac69526c3cJean-Michel Trivi        }
9841357012968f9066ea3051d83995e9bac69526c3cJean-Michel Trivi
9851357012968f9066ea3051d83995e9bac69526c3cJean-Michel Trivi        /**
9861357012968f9066ea3051d83995e9bac69526c3cJean-Michel Trivi         * @hide (to be un-hidden)
9871357012968f9066ea3051d83995e9bac69526c3cJean-Michel Trivi         * Defines over what stream type the media is presented.
9881357012968f9066ea3051d83995e9bac69526c3cJean-Michel Trivi         * @param stream
9891357012968f9066ea3051d83995e9bac69526c3cJean-Michel Trivi         */
9901357012968f9066ea3051d83995e9bac69526c3cJean-Michel Trivi        public void setPlaybackStream(int stream) {
9911357012968f9066ea3051d83995e9bac69526c3cJean-Michel Trivi            if (mPlaybackStream != stream) {
9921357012968f9066ea3051d83995e9bac69526c3cJean-Michel Trivi                mPlaybackStream = stream;
9931357012968f9066ea3051d83995e9bac69526c3cJean-Michel Trivi                setPlaybackInfoOnRcc(RemoteControlClient.PLAYBACKINFO_USES_STREAM, stream);
9941357012968f9066ea3051d83995e9bac69526c3cJean-Michel Trivi            }
9951357012968f9066ea3051d83995e9bac69526c3cJean-Michel Trivi        }
9961357012968f9066ea3051d83995e9bac69526c3cJean-Michel Trivi
9971357012968f9066ea3051d83995e9bac69526c3cJean-Michel Trivi        private void updatePlaybackInfoOnRcc() {
9981357012968f9066ea3051d83995e9bac69526c3cJean-Michel Trivi            if ((mRcc != null) && (mRcc.getRcseId() != RemoteControlClient.RCSE_ID_UNREGISTERED)) {
9991357012968f9066ea3051d83995e9bac69526c3cJean-Michel Trivi                mRcc.setPlaybackInformation(
10001357012968f9066ea3051d83995e9bac69526c3cJean-Michel Trivi                        RemoteControlClient.PLAYBACKINFO_VOLUME_MAX, mVolumeMax);
10011357012968f9066ea3051d83995e9bac69526c3cJean-Michel Trivi                mRcc.setPlaybackInformation(
10021357012968f9066ea3051d83995e9bac69526c3cJean-Michel Trivi                        RemoteControlClient.PLAYBACKINFO_VOLUME, mVolume);
10031357012968f9066ea3051d83995e9bac69526c3cJean-Michel Trivi                mRcc.setPlaybackInformation(
10041357012968f9066ea3051d83995e9bac69526c3cJean-Michel Trivi                        RemoteControlClient.PLAYBACKINFO_VOLUME_HANDLING, mVolumeHandling);
10051357012968f9066ea3051d83995e9bac69526c3cJean-Michel Trivi                mRcc.setPlaybackInformation(
10061357012968f9066ea3051d83995e9bac69526c3cJean-Michel Trivi                        RemoteControlClient.PLAYBACKINFO_USES_STREAM, mPlaybackStream);
10071357012968f9066ea3051d83995e9bac69526c3cJean-Michel Trivi                mRcc.setPlaybackInformation(
10081357012968f9066ea3051d83995e9bac69526c3cJean-Michel Trivi                        RemoteControlClient.PLAYBACKINFO_PLAYBACK_TYPE, mPlaybackType);
10091357012968f9066ea3051d83995e9bac69526c3cJean-Michel Trivi                // let AudioService know whom to call when remote volume needs to be updated
10101357012968f9066ea3051d83995e9bac69526c3cJean-Michel Trivi                try {
10111357012968f9066ea3051d83995e9bac69526c3cJean-Michel Trivi                    sStatic.mAudioService.registerRemoteVolumeObserverForRcc(
10121357012968f9066ea3051d83995e9bac69526c3cJean-Michel Trivi                            mRcc.getRcseId() /* rccId */, mRemoteVolObserver /* rvo */);
10131357012968f9066ea3051d83995e9bac69526c3cJean-Michel Trivi                } catch (RemoteException e) {
10141357012968f9066ea3051d83995e9bac69526c3cJean-Michel Trivi                    Log.e(TAG, "Error registering remote volume observer", e);
10151357012968f9066ea3051d83995e9bac69526c3cJean-Michel Trivi                }
10161357012968f9066ea3051d83995e9bac69526c3cJean-Michel Trivi            }
10171357012968f9066ea3051d83995e9bac69526c3cJean-Michel Trivi        }
10181357012968f9066ea3051d83995e9bac69526c3cJean-Michel Trivi
10191357012968f9066ea3051d83995e9bac69526c3cJean-Michel Trivi        private void setPlaybackInfoOnRcc(int what, int value) {
10201357012968f9066ea3051d83995e9bac69526c3cJean-Michel Trivi            if (mRcc != null) {
10211357012968f9066ea3051d83995e9bac69526c3cJean-Michel Trivi                mRcc.setPlaybackInformation(what, value);
10221357012968f9066ea3051d83995e9bac69526c3cJean-Michel Trivi            }
10231357012968f9066ea3051d83995e9bac69526c3cJean-Michel Trivi        }
10249a1de308cea2d160778fd977825f10a07b49d738Adam Powell    }
10259a1de308cea2d160778fd977825f10a07b49d738Adam Powell
10269a1de308cea2d160778fd977825f10a07b49d738Adam Powell    /**
10279a1de308cea2d160778fd977825f10a07b49d738Adam Powell     * Information about a route that consists of multiple other routes in a group.
10289a1de308cea2d160778fd977825f10a07b49d738Adam Powell     */
1029b58b8f832d06b0ffa8886eba5a4916578a3b8743Dianne Hackborn    public static class RouteGroup extends RouteInfo {
10309a1de308cea2d160778fd977825f10a07b49d738Adam Powell        final ArrayList<RouteInfo> mRoutes = new ArrayList<RouteInfo>();
10319a1de308cea2d160778fd977825f10a07b49d738Adam Powell        private boolean mUpdateName;
10329a1de308cea2d160778fd977825f10a07b49d738Adam Powell
10339a1de308cea2d160778fd977825f10a07b49d738Adam Powell        RouteGroup(RouteCategory category) {
10349a1de308cea2d160778fd977825f10a07b49d738Adam Powell            super(category);
10359a1de308cea2d160778fd977825f10a07b49d738Adam Powell            mGroup = this;
10369a1de308cea2d160778fd977825f10a07b49d738Adam Powell        }
10379a1de308cea2d160778fd977825f10a07b49d738Adam Powell
10380d03c042f90bf62d5bad7c64e77028a5f9f8fae0Adam Powell        CharSequence getName(Resources res) {
10399a1de308cea2d160778fd977825f10a07b49d738Adam Powell            if (mUpdateName) updateName();
10400d03c042f90bf62d5bad7c64e77028a5f9f8fae0Adam Powell            return super.getName(res);
10419a1de308cea2d160778fd977825f10a07b49d738Adam Powell        }
10429a1de308cea2d160778fd977825f10a07b49d738Adam Powell
10439a1de308cea2d160778fd977825f10a07b49d738Adam Powell        /**
10449a1de308cea2d160778fd977825f10a07b49d738Adam Powell         * Add a route to this group. The route must not currently belong to another group.
10459a1de308cea2d160778fd977825f10a07b49d738Adam Powell         *
10469a1de308cea2d160778fd977825f10a07b49d738Adam Powell         * @param route route to add to this group
10479a1de308cea2d160778fd977825f10a07b49d738Adam Powell         */
10489a1de308cea2d160778fd977825f10a07b49d738Adam Powell        public void addRoute(RouteInfo route) {
10499a1de308cea2d160778fd977825f10a07b49d738Adam Powell            if (route.getGroup() != null) {
10509a1de308cea2d160778fd977825f10a07b49d738Adam Powell                throw new IllegalStateException("Route " + route + " is already part of a group.");
10519a1de308cea2d160778fd977825f10a07b49d738Adam Powell            }
10529a1de308cea2d160778fd977825f10a07b49d738Adam Powell            if (route.getCategory() != mCategory) {
10539a1de308cea2d160778fd977825f10a07b49d738Adam Powell                throw new IllegalArgumentException(
10549a1de308cea2d160778fd977825f10a07b49d738Adam Powell                        "Route cannot be added to a group with a different category. " +
10559a1de308cea2d160778fd977825f10a07b49d738Adam Powell                            "(Route category=" + route.getCategory() +
10569a1de308cea2d160778fd977825f10a07b49d738Adam Powell                            " group category=" + mCategory + ")");
10579a1de308cea2d160778fd977825f10a07b49d738Adam Powell            }
1058d0d2cda9d414da73773285d7fee9e13aef3495e9Adam Powell            final int at = mRoutes.size();
10599a1de308cea2d160778fd977825f10a07b49d738Adam Powell            mRoutes.add(route);
1060d6d0bddee363e0c7fe61f63bd9d9864a71d887d6Adam Powell            route.mGroup = this;
10619a1de308cea2d160778fd977825f10a07b49d738Adam Powell            mUpdateName = true;
1062d0d2cda9d414da73773285d7fee9e13aef3495e9Adam Powell            dispatchRouteGrouped(route, this, at);
10639a1de308cea2d160778fd977825f10a07b49d738Adam Powell            routeUpdated();
10649a1de308cea2d160778fd977825f10a07b49d738Adam Powell        }
10659a1de308cea2d160778fd977825f10a07b49d738Adam Powell
10669a1de308cea2d160778fd977825f10a07b49d738Adam Powell        /**
10679a1de308cea2d160778fd977825f10a07b49d738Adam Powell         * Add a route to this group before the specified index.
10689a1de308cea2d160778fd977825f10a07b49d738Adam Powell         *
10699a1de308cea2d160778fd977825f10a07b49d738Adam Powell         * @param route route to add
10709a1de308cea2d160778fd977825f10a07b49d738Adam Powell         * @param insertAt insert the new route before this index
10719a1de308cea2d160778fd977825f10a07b49d738Adam Powell         */
10729a1de308cea2d160778fd977825f10a07b49d738Adam Powell        public void addRoute(RouteInfo route, int insertAt) {
10739a1de308cea2d160778fd977825f10a07b49d738Adam Powell            if (route.getGroup() != null) {
10749a1de308cea2d160778fd977825f10a07b49d738Adam Powell                throw new IllegalStateException("Route " + route + " is already part of a group.");
10759a1de308cea2d160778fd977825f10a07b49d738Adam Powell            }
10769a1de308cea2d160778fd977825f10a07b49d738Adam Powell            if (route.getCategory() != mCategory) {
10779a1de308cea2d160778fd977825f10a07b49d738Adam Powell                throw new IllegalArgumentException(
10789a1de308cea2d160778fd977825f10a07b49d738Adam Powell                        "Route cannot be added to a group with a different category. " +
10799a1de308cea2d160778fd977825f10a07b49d738Adam Powell                            "(Route category=" + route.getCategory() +
10809a1de308cea2d160778fd977825f10a07b49d738Adam Powell                            " group category=" + mCategory + ")");
10819a1de308cea2d160778fd977825f10a07b49d738Adam Powell            }
10829a1de308cea2d160778fd977825f10a07b49d738Adam Powell            mRoutes.add(insertAt, route);
1083d6d0bddee363e0c7fe61f63bd9d9864a71d887d6Adam Powell            route.mGroup = this;
10849a1de308cea2d160778fd977825f10a07b49d738Adam Powell            mUpdateName = true;
1085d0d2cda9d414da73773285d7fee9e13aef3495e9Adam Powell            dispatchRouteGrouped(route, this, insertAt);
10869a1de308cea2d160778fd977825f10a07b49d738Adam Powell            routeUpdated();
10879a1de308cea2d160778fd977825f10a07b49d738Adam Powell        }
10889a1de308cea2d160778fd977825f10a07b49d738Adam Powell
10899a1de308cea2d160778fd977825f10a07b49d738Adam Powell        /**
10909a1de308cea2d160778fd977825f10a07b49d738Adam Powell         * Remove a route from this group.
10919a1de308cea2d160778fd977825f10a07b49d738Adam Powell         *
10929a1de308cea2d160778fd977825f10a07b49d738Adam Powell         * @param route route to remove
10939a1de308cea2d160778fd977825f10a07b49d738Adam Powell         */
10949a1de308cea2d160778fd977825f10a07b49d738Adam Powell        public void removeRoute(RouteInfo route) {
10959a1de308cea2d160778fd977825f10a07b49d738Adam Powell            if (route.getGroup() != this) {
10969a1de308cea2d160778fd977825f10a07b49d738Adam Powell                throw new IllegalArgumentException("Route " + route +
10979a1de308cea2d160778fd977825f10a07b49d738Adam Powell                        " is not a member of this group.");
10989a1de308cea2d160778fd977825f10a07b49d738Adam Powell            }
10999a1de308cea2d160778fd977825f10a07b49d738Adam Powell            mRoutes.remove(route);
1100d6d0bddee363e0c7fe61f63bd9d9864a71d887d6Adam Powell            route.mGroup = null;
11019a1de308cea2d160778fd977825f10a07b49d738Adam Powell            mUpdateName = true;
1102d0d2cda9d414da73773285d7fee9e13aef3495e9Adam Powell            dispatchRouteUngrouped(route, this);
11039a1de308cea2d160778fd977825f10a07b49d738Adam Powell            routeUpdated();
11049a1de308cea2d160778fd977825f10a07b49d738Adam Powell        }
11059a1de308cea2d160778fd977825f10a07b49d738Adam Powell
11069a1de308cea2d160778fd977825f10a07b49d738Adam Powell        /**
11079a1de308cea2d160778fd977825f10a07b49d738Adam Powell         * Remove the route at the specified index from this group.
11089a1de308cea2d160778fd977825f10a07b49d738Adam Powell         *
11099a1de308cea2d160778fd977825f10a07b49d738Adam Powell         * @param index index of the route to remove
11109a1de308cea2d160778fd977825f10a07b49d738Adam Powell         */
11119a1de308cea2d160778fd977825f10a07b49d738Adam Powell        public void removeRoute(int index) {
1112d0d2cda9d414da73773285d7fee9e13aef3495e9Adam Powell            RouteInfo route = mRoutes.remove(index);
1113d6d0bddee363e0c7fe61f63bd9d9864a71d887d6Adam Powell            route.mGroup = null;
11149a1de308cea2d160778fd977825f10a07b49d738Adam Powell            mUpdateName = true;
1115d0d2cda9d414da73773285d7fee9e13aef3495e9Adam Powell            dispatchRouteUngrouped(route, this);
11169a1de308cea2d160778fd977825f10a07b49d738Adam Powell            routeUpdated();
11179a1de308cea2d160778fd977825f10a07b49d738Adam Powell        }
11189a1de308cea2d160778fd977825f10a07b49d738Adam Powell
1119d0d2cda9d414da73773285d7fee9e13aef3495e9Adam Powell        /**
1120d0d2cda9d414da73773285d7fee9e13aef3495e9Adam Powell         * @return The number of routes in this group
1121d0d2cda9d414da73773285d7fee9e13aef3495e9Adam Powell         */
1122d0d2cda9d414da73773285d7fee9e13aef3495e9Adam Powell        public int getRouteCount() {
1123d0d2cda9d414da73773285d7fee9e13aef3495e9Adam Powell            return mRoutes.size();
1124d0d2cda9d414da73773285d7fee9e13aef3495e9Adam Powell        }
1125d0d2cda9d414da73773285d7fee9e13aef3495e9Adam Powell
1126d0d2cda9d414da73773285d7fee9e13aef3495e9Adam Powell        /**
1127d0d2cda9d414da73773285d7fee9e13aef3495e9Adam Powell         * Return the route in this group at the specified index
1128d0d2cda9d414da73773285d7fee9e13aef3495e9Adam Powell         *
1129d0d2cda9d414da73773285d7fee9e13aef3495e9Adam Powell         * @param index Index to fetch
1130d0d2cda9d414da73773285d7fee9e13aef3495e9Adam Powell         * @return The route at index
1131d0d2cda9d414da73773285d7fee9e13aef3495e9Adam Powell         */
1132d0d2cda9d414da73773285d7fee9e13aef3495e9Adam Powell        public RouteInfo getRouteAt(int index) {
1133d0d2cda9d414da73773285d7fee9e13aef3495e9Adam Powell            return mRoutes.get(index);
1134d0d2cda9d414da73773285d7fee9e13aef3495e9Adam Powell        }
1135d0d2cda9d414da73773285d7fee9e13aef3495e9Adam Powell
1136ae20ae1a8aaa013813c356ae1d9541ca7ff020aeAdam Powell        /**
1137ae20ae1a8aaa013813c356ae1d9541ca7ff020aeAdam Powell         * Set an icon that will be used to represent this group.
1138ae20ae1a8aaa013813c356ae1d9541ca7ff020aeAdam Powell         * The system may use this icon in picker UIs or similar.
1139ae20ae1a8aaa013813c356ae1d9541ca7ff020aeAdam Powell         *
1140ae20ae1a8aaa013813c356ae1d9541ca7ff020aeAdam Powell         * @param icon icon drawable to use to represent this group
1141ae20ae1a8aaa013813c356ae1d9541ca7ff020aeAdam Powell         */
1142ae20ae1a8aaa013813c356ae1d9541ca7ff020aeAdam Powell        public void setIconDrawable(Drawable icon) {
1143ae20ae1a8aaa013813c356ae1d9541ca7ff020aeAdam Powell            mIcon = icon;
1144ae20ae1a8aaa013813c356ae1d9541ca7ff020aeAdam Powell        }
1145ae20ae1a8aaa013813c356ae1d9541ca7ff020aeAdam Powell
1146ae20ae1a8aaa013813c356ae1d9541ca7ff020aeAdam Powell        /**
1147ae20ae1a8aaa013813c356ae1d9541ca7ff020aeAdam Powell         * Set an icon that will be used to represent this group.
1148ae20ae1a8aaa013813c356ae1d9541ca7ff020aeAdam Powell         * The system may use this icon in picker UIs or similar.
1149ae20ae1a8aaa013813c356ae1d9541ca7ff020aeAdam Powell         *
115071c69897ad0a55d590698bfa399bfe99c763b9dbAdam Powell         * @param resId Resource ID of an icon drawable to use to represent this group
1151ae20ae1a8aaa013813c356ae1d9541ca7ff020aeAdam Powell         */
1152ae20ae1a8aaa013813c356ae1d9541ca7ff020aeAdam Powell        public void setIconResource(int resId) {
1153ae20ae1a8aaa013813c356ae1d9541ca7ff020aeAdam Powell            setIconDrawable(sStatic.mResources.getDrawable(resId));
1154ae20ae1a8aaa013813c356ae1d9541ca7ff020aeAdam Powell        }
1155ae20ae1a8aaa013813c356ae1d9541ca7ff020aeAdam Powell
11569a1de308cea2d160778fd977825f10a07b49d738Adam Powell        void memberNameChanged(RouteInfo info, CharSequence name) {
11579a1de308cea2d160778fd977825f10a07b49d738Adam Powell            mUpdateName = true;
11589a1de308cea2d160778fd977825f10a07b49d738Adam Powell            routeUpdated();
11599a1de308cea2d160778fd977825f10a07b49d738Adam Powell        }
11609a1de308cea2d160778fd977825f10a07b49d738Adam Powell
11619a1de308cea2d160778fd977825f10a07b49d738Adam Powell        void memberStatusChanged(RouteInfo info, CharSequence status) {
11629a1de308cea2d160778fd977825f10a07b49d738Adam Powell            setStatusInt(status);
11639a1de308cea2d160778fd977825f10a07b49d738Adam Powell        }
11649a1de308cea2d160778fd977825f10a07b49d738Adam Powell
1165d6d0bddee363e0c7fe61f63bd9d9864a71d887d6Adam Powell        @Override
1166d6d0bddee363e0c7fe61f63bd9d9864a71d887d6Adam Powell        void routeUpdated() {
1167d6d0bddee363e0c7fe61f63bd9d9864a71d887d6Adam Powell            int types = 0;
1168d6d0bddee363e0c7fe61f63bd9d9864a71d887d6Adam Powell            final int count = mRoutes.size();
1169b5e2af5919351486a385effe77409d2a91ae9c19Adam Powell            if (count == 0) {
1170b5e2af5919351486a385effe77409d2a91ae9c19Adam Powell                // Don't keep empty groups in the router.
1171b5e2af5919351486a385effe77409d2a91ae9c19Adam Powell                MediaRouter.removeRoute(this);
1172b5e2af5919351486a385effe77409d2a91ae9c19Adam Powell                return;
1173b5e2af5919351486a385effe77409d2a91ae9c19Adam Powell            }
1174b5e2af5919351486a385effe77409d2a91ae9c19Adam Powell
1175d6d0bddee363e0c7fe61f63bd9d9864a71d887d6Adam Powell            for (int i = 0; i < count; i++) {
1176d6d0bddee363e0c7fe61f63bd9d9864a71d887d6Adam Powell                types |= mRoutes.get(i).mSupportedTypes;
1177d6d0bddee363e0c7fe61f63bd9d9864a71d887d6Adam Powell            }
1178d6d0bddee363e0c7fe61f63bd9d9864a71d887d6Adam Powell            mSupportedTypes = types;
1179d6d0bddee363e0c7fe61f63bd9d9864a71d887d6Adam Powell            mIcon = count == 1 ? mRoutes.get(0).getIconDrawable() : null;
1180d6d0bddee363e0c7fe61f63bd9d9864a71d887d6Adam Powell            super.routeUpdated();
1181d6d0bddee363e0c7fe61f63bd9d9864a71d887d6Adam Powell        }
1182d6d0bddee363e0c7fe61f63bd9d9864a71d887d6Adam Powell
11839a1de308cea2d160778fd977825f10a07b49d738Adam Powell        void updateName() {
11849a1de308cea2d160778fd977825f10a07b49d738Adam Powell            final StringBuilder sb = new StringBuilder();
11859a1de308cea2d160778fd977825f10a07b49d738Adam Powell            final int count = mRoutes.size();
11869a1de308cea2d160778fd977825f10a07b49d738Adam Powell            for (int i = 0; i < count; i++) {
11879a1de308cea2d160778fd977825f10a07b49d738Adam Powell                final RouteInfo info = mRoutes.get(i);
1188b5e2af5919351486a385effe77409d2a91ae9c19Adam Powell                // TODO: There's probably a much more correct way to localize this.
11899a1de308cea2d160778fd977825f10a07b49d738Adam Powell                if (i > 0) sb.append(", ");
11909a1de308cea2d160778fd977825f10a07b49d738Adam Powell                sb.append(info.mName);
11919a1de308cea2d160778fd977825f10a07b49d738Adam Powell            }
11929a1de308cea2d160778fd977825f10a07b49d738Adam Powell            mName = sb.toString();
11939a1de308cea2d160778fd977825f10a07b49d738Adam Powell            mUpdateName = false;
11949a1de308cea2d160778fd977825f10a07b49d738Adam Powell        }
1195d6d0bddee363e0c7fe61f63bd9d9864a71d887d6Adam Powell
1196d6d0bddee363e0c7fe61f63bd9d9864a71d887d6Adam Powell        @Override
1197d6d0bddee363e0c7fe61f63bd9d9864a71d887d6Adam Powell        public String toString() {
1198d6d0bddee363e0c7fe61f63bd9d9864a71d887d6Adam Powell            StringBuilder sb = new StringBuilder(super.toString());
1199d6d0bddee363e0c7fe61f63bd9d9864a71d887d6Adam Powell            sb.append('[');
1200d6d0bddee363e0c7fe61f63bd9d9864a71d887d6Adam Powell            final int count = mRoutes.size();
1201d6d0bddee363e0c7fe61f63bd9d9864a71d887d6Adam Powell            for (int i = 0; i < count; i++) {
1202d6d0bddee363e0c7fe61f63bd9d9864a71d887d6Adam Powell                if (i > 0) sb.append(", ");
1203d6d0bddee363e0c7fe61f63bd9d9864a71d887d6Adam Powell                sb.append(mRoutes.get(i));
1204d6d0bddee363e0c7fe61f63bd9d9864a71d887d6Adam Powell            }
1205d6d0bddee363e0c7fe61f63bd9d9864a71d887d6Adam Powell            sb.append(']');
1206d6d0bddee363e0c7fe61f63bd9d9864a71d887d6Adam Powell            return sb.toString();
1207d6d0bddee363e0c7fe61f63bd9d9864a71d887d6Adam Powell        }
12089a1de308cea2d160778fd977825f10a07b49d738Adam Powell    }
12099a1de308cea2d160778fd977825f10a07b49d738Adam Powell
12109a1de308cea2d160778fd977825f10a07b49d738Adam Powell    /**
12119a1de308cea2d160778fd977825f10a07b49d738Adam Powell     * Definition of a category of routes. All routes belong to a category.
12129a1de308cea2d160778fd977825f10a07b49d738Adam Powell     */
1213b58b8f832d06b0ffa8886eba5a4916578a3b8743Dianne Hackborn    public static class RouteCategory {
12149a1de308cea2d160778fd977825f10a07b49d738Adam Powell        CharSequence mName;
12150d03c042f90bf62d5bad7c64e77028a5f9f8fae0Adam Powell        int mNameResId;
12169a1de308cea2d160778fd977825f10a07b49d738Adam Powell        int mTypes;
12179a1de308cea2d160778fd977825f10a07b49d738Adam Powell        final boolean mGroupable;
12189a1de308cea2d160778fd977825f10a07b49d738Adam Powell
12199a1de308cea2d160778fd977825f10a07b49d738Adam Powell        RouteCategory(CharSequence name, int types, boolean groupable) {
12209a1de308cea2d160778fd977825f10a07b49d738Adam Powell            mName = name;
12219a1de308cea2d160778fd977825f10a07b49d738Adam Powell            mTypes = types;
12229a1de308cea2d160778fd977825f10a07b49d738Adam Powell            mGroupable = groupable;
12239a1de308cea2d160778fd977825f10a07b49d738Adam Powell        }
12249a1de308cea2d160778fd977825f10a07b49d738Adam Powell
12250d03c042f90bf62d5bad7c64e77028a5f9f8fae0Adam Powell        RouteCategory(int nameResId, int types, boolean groupable) {
12260d03c042f90bf62d5bad7c64e77028a5f9f8fae0Adam Powell            mNameResId = nameResId;
12270d03c042f90bf62d5bad7c64e77028a5f9f8fae0Adam Powell            mTypes = types;
12280d03c042f90bf62d5bad7c64e77028a5f9f8fae0Adam Powell            mGroupable = groupable;
12290d03c042f90bf62d5bad7c64e77028a5f9f8fae0Adam Powell        }
12300d03c042f90bf62d5bad7c64e77028a5f9f8fae0Adam Powell
12319a1de308cea2d160778fd977825f10a07b49d738Adam Powell        /**
12329a1de308cea2d160778fd977825f10a07b49d738Adam Powell         * @return the name of this route category
12339a1de308cea2d160778fd977825f10a07b49d738Adam Powell         */
12349a1de308cea2d160778fd977825f10a07b49d738Adam Powell        public CharSequence getName() {
12350d03c042f90bf62d5bad7c64e77028a5f9f8fae0Adam Powell            return getName(sStatic.mResources);
12360d03c042f90bf62d5bad7c64e77028a5f9f8fae0Adam Powell        }
12370d03c042f90bf62d5bad7c64e77028a5f9f8fae0Adam Powell
12380d03c042f90bf62d5bad7c64e77028a5f9f8fae0Adam Powell        /**
12390d03c042f90bf62d5bad7c64e77028a5f9f8fae0Adam Powell         * Return the properly localized/configuration dependent name of this RouteCategory.
12400d03c042f90bf62d5bad7c64e77028a5f9f8fae0Adam Powell         *
12410d03c042f90bf62d5bad7c64e77028a5f9f8fae0Adam Powell         * @param context Context to resolve name resources
12420d03c042f90bf62d5bad7c64e77028a5f9f8fae0Adam Powell         * @return the name of this route category
12430d03c042f90bf62d5bad7c64e77028a5f9f8fae0Adam Powell         */
12440d03c042f90bf62d5bad7c64e77028a5f9f8fae0Adam Powell        public CharSequence getName(Context context) {
12450d03c042f90bf62d5bad7c64e77028a5f9f8fae0Adam Powell            return getName(context.getResources());
12460d03c042f90bf62d5bad7c64e77028a5f9f8fae0Adam Powell        }
12470d03c042f90bf62d5bad7c64e77028a5f9f8fae0Adam Powell
12480d03c042f90bf62d5bad7c64e77028a5f9f8fae0Adam Powell        CharSequence getName(Resources res) {
12490d03c042f90bf62d5bad7c64e77028a5f9f8fae0Adam Powell            if (mNameResId != 0) {
12500d03c042f90bf62d5bad7c64e77028a5f9f8fae0Adam Powell                return res.getText(mNameResId);
12510d03c042f90bf62d5bad7c64e77028a5f9f8fae0Adam Powell            }
12529a1de308cea2d160778fd977825f10a07b49d738Adam Powell            return mName;
12539a1de308cea2d160778fd977825f10a07b49d738Adam Powell        }
12549a1de308cea2d160778fd977825f10a07b49d738Adam Powell
12559a1de308cea2d160778fd977825f10a07b49d738Adam Powell        /**
1256d0d2cda9d414da73773285d7fee9e13aef3495e9Adam Powell         * Return the current list of routes in this category that have been added
1257d0d2cda9d414da73773285d7fee9e13aef3495e9Adam Powell         * to the MediaRouter.
12589a1de308cea2d160778fd977825f10a07b49d738Adam Powell         *
1259d0d2cda9d414da73773285d7fee9e13aef3495e9Adam Powell         * <p>This list will not include routes that are nested within RouteGroups.
1260d0d2cda9d414da73773285d7fee9e13aef3495e9Adam Powell         * A RouteGroup is treated as a single route within its category.</p>
1261d0d2cda9d414da73773285d7fee9e13aef3495e9Adam Powell         *
1262d0d2cda9d414da73773285d7fee9e13aef3495e9Adam Powell         * @param out a List to fill with the routes in this category. If this parameter is
1263d0d2cda9d414da73773285d7fee9e13aef3495e9Adam Powell         *            non-null, it will be cleared, filled with the current routes with this
1264d0d2cda9d414da73773285d7fee9e13aef3495e9Adam Powell         *            category, and returned. If this parameter is null, a new List will be
1265d0d2cda9d414da73773285d7fee9e13aef3495e9Adam Powell         *            allocated to report the category's current routes.
1266d0d2cda9d414da73773285d7fee9e13aef3495e9Adam Powell         * @return A list with the routes in this category that have been added to the MediaRouter.
12679a1de308cea2d160778fd977825f10a07b49d738Adam Powell         */
1268d0d2cda9d414da73773285d7fee9e13aef3495e9Adam Powell        public List<RouteInfo> getRoutes(List<RouteInfo> out) {
1269d0d2cda9d414da73773285d7fee9e13aef3495e9Adam Powell            if (out == null) {
1270d0d2cda9d414da73773285d7fee9e13aef3495e9Adam Powell                out = new ArrayList<RouteInfo>();
1271d0d2cda9d414da73773285d7fee9e13aef3495e9Adam Powell            } else {
1272d0d2cda9d414da73773285d7fee9e13aef3495e9Adam Powell                out.clear();
1273d0d2cda9d414da73773285d7fee9e13aef3495e9Adam Powell            }
1274d0d2cda9d414da73773285d7fee9e13aef3495e9Adam Powell
1275b58b8f832d06b0ffa8886eba5a4916578a3b8743Dianne Hackborn            final int count = getRouteCountStatic();
1276d0d2cda9d414da73773285d7fee9e13aef3495e9Adam Powell            for (int i = 0; i < count; i++) {
1277b58b8f832d06b0ffa8886eba5a4916578a3b8743Dianne Hackborn                final RouteInfo route = getRouteAtStatic(i);
1278d0d2cda9d414da73773285d7fee9e13aef3495e9Adam Powell                if (route.mCategory == this) {
1279d0d2cda9d414da73773285d7fee9e13aef3495e9Adam Powell                    out.add(route);
1280d0d2cda9d414da73773285d7fee9e13aef3495e9Adam Powell                }
1281d0d2cda9d414da73773285d7fee9e13aef3495e9Adam Powell            }
1282d0d2cda9d414da73773285d7fee9e13aef3495e9Adam Powell            return out;
12839a1de308cea2d160778fd977825f10a07b49d738Adam Powell        }
12849a1de308cea2d160778fd977825f10a07b49d738Adam Powell
12859a1de308cea2d160778fd977825f10a07b49d738Adam Powell        /**
12869a1de308cea2d160778fd977825f10a07b49d738Adam Powell         * @return Flag set describing the route types supported by this category
12879a1de308cea2d160778fd977825f10a07b49d738Adam Powell         */
12889a1de308cea2d160778fd977825f10a07b49d738Adam Powell        public int getSupportedTypes() {
12899a1de308cea2d160778fd977825f10a07b49d738Adam Powell            return mTypes;
12909a1de308cea2d160778fd977825f10a07b49d738Adam Powell        }
12919a1de308cea2d160778fd977825f10a07b49d738Adam Powell
12929a1de308cea2d160778fd977825f10a07b49d738Adam Powell        /**
12939a1de308cea2d160778fd977825f10a07b49d738Adam Powell         * Return whether or not this category supports grouping.
12949a1de308cea2d160778fd977825f10a07b49d738Adam Powell         *
12959a1de308cea2d160778fd977825f10a07b49d738Adam Powell         * <p>If this method returns true, all routes obtained from this category
1296d0d2cda9d414da73773285d7fee9e13aef3495e9Adam Powell         * via calls to {@link #getRouteAt(int)} will be {@link MediaRouter.RouteGroup}s.</p>
12979a1de308cea2d160778fd977825f10a07b49d738Adam Powell         *
12989a1de308cea2d160778fd977825f10a07b49d738Adam Powell         * @return true if this category supports
12999a1de308cea2d160778fd977825f10a07b49d738Adam Powell         */
13009a1de308cea2d160778fd977825f10a07b49d738Adam Powell        public boolean isGroupable() {
13019a1de308cea2d160778fd977825f10a07b49d738Adam Powell            return mGroupable;
13029a1de308cea2d160778fd977825f10a07b49d738Adam Powell        }
13039a1de308cea2d160778fd977825f10a07b49d738Adam Powell
13049a1de308cea2d160778fd977825f10a07b49d738Adam Powell        public String toString() {
13059a1de308cea2d160778fd977825f10a07b49d738Adam Powell            return "RouteCategory{ name=" + mName + " types=" + typesToString(mTypes) +
1306d6d0bddee363e0c7fe61f63bd9d9864a71d887d6Adam Powell                    " groupable=" + mGroupable + " }";
13079a1de308cea2d160778fd977825f10a07b49d738Adam Powell        }
13089a1de308cea2d160778fd977825f10a07b49d738Adam Powell    }
13099a1de308cea2d160778fd977825f10a07b49d738Adam Powell
13109a1de308cea2d160778fd977825f10a07b49d738Adam Powell    static class CallbackInfo {
13119a1de308cea2d160778fd977825f10a07b49d738Adam Powell        public int type;
1312b58b8f832d06b0ffa8886eba5a4916578a3b8743Dianne Hackborn        public final Callback cb;
1313b58b8f832d06b0ffa8886eba5a4916578a3b8743Dianne Hackborn        public final MediaRouter router;
13149a1de308cea2d160778fd977825f10a07b49d738Adam Powell
1315b58b8f832d06b0ffa8886eba5a4916578a3b8743Dianne Hackborn        public CallbackInfo(Callback cb, int type, MediaRouter router) {
13169a1de308cea2d160778fd977825f10a07b49d738Adam Powell            this.cb = cb;
13179a1de308cea2d160778fd977825f10a07b49d738Adam Powell            this.type = type;
1318b58b8f832d06b0ffa8886eba5a4916578a3b8743Dianne Hackborn            this.router = router;
13199a1de308cea2d160778fd977825f10a07b49d738Adam Powell        }
13209a1de308cea2d160778fd977825f10a07b49d738Adam Powell    }
13219a1de308cea2d160778fd977825f10a07b49d738Adam Powell
13229a1de308cea2d160778fd977825f10a07b49d738Adam Powell    /**
13239a1de308cea2d160778fd977825f10a07b49d738Adam Powell     * Interface for receiving events about media routing changes.
13249a1de308cea2d160778fd977825f10a07b49d738Adam Powell     * All methods of this interface will be called from the application's main thread.
13259a1de308cea2d160778fd977825f10a07b49d738Adam Powell     *
13269a1de308cea2d160778fd977825f10a07b49d738Adam Powell     * <p>A Callback will only receive events relevant to routes that the callback
13279a1de308cea2d160778fd977825f10a07b49d738Adam Powell     * was registered for.</p>
13289a1de308cea2d160778fd977825f10a07b49d738Adam Powell     *
13299a1de308cea2d160778fd977825f10a07b49d738Adam Powell     * @see MediaRouter#addCallback(int, Callback)
13309a1de308cea2d160778fd977825f10a07b49d738Adam Powell     * @see MediaRouter#removeCallback(Callback)
13319a1de308cea2d160778fd977825f10a07b49d738Adam Powell     */
13320d03c042f90bf62d5bad7c64e77028a5f9f8fae0Adam Powell    public static abstract class Callback {
13339a1de308cea2d160778fd977825f10a07b49d738Adam Powell        /**
13349a1de308cea2d160778fd977825f10a07b49d738Adam Powell         * Called when the supplied route becomes selected as the active route
13359a1de308cea2d160778fd977825f10a07b49d738Adam Powell         * for the given route type.
13369a1de308cea2d160778fd977825f10a07b49d738Adam Powell         *
1337d0d2cda9d414da73773285d7fee9e13aef3495e9Adam Powell         * @param router the MediaRouter reporting the event
13389a1de308cea2d160778fd977825f10a07b49d738Adam Powell         * @param type Type flag set indicating the routes that have been selected
13399a1de308cea2d160778fd977825f10a07b49d738Adam Powell         * @param info Route that has been selected for the given route types
13409a1de308cea2d160778fd977825f10a07b49d738Adam Powell         */
13410d03c042f90bf62d5bad7c64e77028a5f9f8fae0Adam Powell        public abstract void onRouteSelected(MediaRouter router, int type, RouteInfo info);
13429a1de308cea2d160778fd977825f10a07b49d738Adam Powell
13439a1de308cea2d160778fd977825f10a07b49d738Adam Powell        /**
13449a1de308cea2d160778fd977825f10a07b49d738Adam Powell         * Called when the supplied route becomes unselected as the active route
13459a1de308cea2d160778fd977825f10a07b49d738Adam Powell         * for the given route type.
13469a1de308cea2d160778fd977825f10a07b49d738Adam Powell         *
1347d0d2cda9d414da73773285d7fee9e13aef3495e9Adam Powell         * @param router the MediaRouter reporting the event
13489a1de308cea2d160778fd977825f10a07b49d738Adam Powell         * @param type Type flag set indicating the routes that have been unselected
13499a1de308cea2d160778fd977825f10a07b49d738Adam Powell         * @param info Route that has been unselected for the given route types
13509a1de308cea2d160778fd977825f10a07b49d738Adam Powell         */
13510d03c042f90bf62d5bad7c64e77028a5f9f8fae0Adam Powell        public abstract void onRouteUnselected(MediaRouter router, int type, RouteInfo info);
13529a1de308cea2d160778fd977825f10a07b49d738Adam Powell
13539a1de308cea2d160778fd977825f10a07b49d738Adam Powell        /**
13549a1de308cea2d160778fd977825f10a07b49d738Adam Powell         * Called when a route for the specified type was added.
13559a1de308cea2d160778fd977825f10a07b49d738Adam Powell         *
1356d0d2cda9d414da73773285d7fee9e13aef3495e9Adam Powell         * @param router the MediaRouter reporting the event
13579a1de308cea2d160778fd977825f10a07b49d738Adam Powell         * @param info Route that has become available for use
13589a1de308cea2d160778fd977825f10a07b49d738Adam Powell         */
13590d03c042f90bf62d5bad7c64e77028a5f9f8fae0Adam Powell        public abstract void onRouteAdded(MediaRouter router, RouteInfo info);
13609a1de308cea2d160778fd977825f10a07b49d738Adam Powell
13619a1de308cea2d160778fd977825f10a07b49d738Adam Powell        /**
13629a1de308cea2d160778fd977825f10a07b49d738Adam Powell         * Called when a route for the specified type was removed.
13639a1de308cea2d160778fd977825f10a07b49d738Adam Powell         *
1364d0d2cda9d414da73773285d7fee9e13aef3495e9Adam Powell         * @param router the MediaRouter reporting the event
13659a1de308cea2d160778fd977825f10a07b49d738Adam Powell         * @param info Route that has been removed from availability
13669a1de308cea2d160778fd977825f10a07b49d738Adam Powell         */
13670d03c042f90bf62d5bad7c64e77028a5f9f8fae0Adam Powell        public abstract void onRouteRemoved(MediaRouter router, RouteInfo info);
13689a1de308cea2d160778fd977825f10a07b49d738Adam Powell
13699a1de308cea2d160778fd977825f10a07b49d738Adam Powell        /**
13709a1de308cea2d160778fd977825f10a07b49d738Adam Powell         * Called when an aspect of the indicated route has changed.
13719a1de308cea2d160778fd977825f10a07b49d738Adam Powell         *
13729a1de308cea2d160778fd977825f10a07b49d738Adam Powell         * <p>This will not indicate that the types supported by this route have
13739a1de308cea2d160778fd977825f10a07b49d738Adam Powell         * changed, only that cosmetic info such as name or status have been updated.</p>
13749a1de308cea2d160778fd977825f10a07b49d738Adam Powell         *
1375d0d2cda9d414da73773285d7fee9e13aef3495e9Adam Powell         * @param router the MediaRouter reporting the event
13769a1de308cea2d160778fd977825f10a07b49d738Adam Powell         * @param info The route that was changed
13779a1de308cea2d160778fd977825f10a07b49d738Adam Powell         */
13780d03c042f90bf62d5bad7c64e77028a5f9f8fae0Adam Powell        public abstract void onRouteChanged(MediaRouter router, RouteInfo info);
1379d0d2cda9d414da73773285d7fee9e13aef3495e9Adam Powell
1380d0d2cda9d414da73773285d7fee9e13aef3495e9Adam Powell        /**
1381d0d2cda9d414da73773285d7fee9e13aef3495e9Adam Powell         * Called when a route is added to a group.
1382d0d2cda9d414da73773285d7fee9e13aef3495e9Adam Powell         *
1383d0d2cda9d414da73773285d7fee9e13aef3495e9Adam Powell         * @param router the MediaRouter reporting the event
1384d0d2cda9d414da73773285d7fee9e13aef3495e9Adam Powell         * @param info The route that was added
1385d0d2cda9d414da73773285d7fee9e13aef3495e9Adam Powell         * @param group The group the route was added to
1386d0d2cda9d414da73773285d7fee9e13aef3495e9Adam Powell         * @param index The route index within group that info was added at
1387d0d2cda9d414da73773285d7fee9e13aef3495e9Adam Powell         */
13880d03c042f90bf62d5bad7c64e77028a5f9f8fae0Adam Powell        public abstract void onRouteGrouped(MediaRouter router, RouteInfo info, RouteGroup group,
13890d03c042f90bf62d5bad7c64e77028a5f9f8fae0Adam Powell                int index);
1390d0d2cda9d414da73773285d7fee9e13aef3495e9Adam Powell
1391d0d2cda9d414da73773285d7fee9e13aef3495e9Adam Powell        /**
1392d0d2cda9d414da73773285d7fee9e13aef3495e9Adam Powell         * Called when a route is removed from a group.
1393d0d2cda9d414da73773285d7fee9e13aef3495e9Adam Powell         *
1394d0d2cda9d414da73773285d7fee9e13aef3495e9Adam Powell         * @param router the MediaRouter reporting the event
1395d0d2cda9d414da73773285d7fee9e13aef3495e9Adam Powell         * @param info The route that was removed
1396d0d2cda9d414da73773285d7fee9e13aef3495e9Adam Powell         * @param group The group the route was removed from
1397d0d2cda9d414da73773285d7fee9e13aef3495e9Adam Powell         */
13980d03c042f90bf62d5bad7c64e77028a5f9f8fae0Adam Powell        public abstract void onRouteUngrouped(MediaRouter router, RouteInfo info, RouteGroup group);
13999a1de308cea2d160778fd977825f10a07b49d738Adam Powell    }
14009a1de308cea2d160778fd977825f10a07b49d738Adam Powell
14019a1de308cea2d160778fd977825f10a07b49d738Adam Powell    /**
14020d03c042f90bf62d5bad7c64e77028a5f9f8fae0Adam Powell     * Stub implementation of {@link MediaRouter.Callback}.
14030d03c042f90bf62d5bad7c64e77028a5f9f8fae0Adam Powell     * Each abstract method is defined as a no-op. Override just the ones
14049a1de308cea2d160778fd977825f10a07b49d738Adam Powell     * you need.
14059a1de308cea2d160778fd977825f10a07b49d738Adam Powell     */
14060d03c042f90bf62d5bad7c64e77028a5f9f8fae0Adam Powell    public static class SimpleCallback extends Callback {
14079a1de308cea2d160778fd977825f10a07b49d738Adam Powell
14089a1de308cea2d160778fd977825f10a07b49d738Adam Powell        @Override
1409d0d2cda9d414da73773285d7fee9e13aef3495e9Adam Powell        public void onRouteSelected(MediaRouter router, int type, RouteInfo info) {
14109a1de308cea2d160778fd977825f10a07b49d738Adam Powell        }
14119a1de308cea2d160778fd977825f10a07b49d738Adam Powell
14129a1de308cea2d160778fd977825f10a07b49d738Adam Powell        @Override
1413d0d2cda9d414da73773285d7fee9e13aef3495e9Adam Powell        public void onRouteUnselected(MediaRouter router, int type, RouteInfo info) {
14149a1de308cea2d160778fd977825f10a07b49d738Adam Powell        }
14159a1de308cea2d160778fd977825f10a07b49d738Adam Powell
14169a1de308cea2d160778fd977825f10a07b49d738Adam Powell        @Override
1417d0d2cda9d414da73773285d7fee9e13aef3495e9Adam Powell        public void onRouteAdded(MediaRouter router, RouteInfo info) {
14189a1de308cea2d160778fd977825f10a07b49d738Adam Powell        }
14199a1de308cea2d160778fd977825f10a07b49d738Adam Powell
14209a1de308cea2d160778fd977825f10a07b49d738Adam Powell        @Override
1421d0d2cda9d414da73773285d7fee9e13aef3495e9Adam Powell        public void onRouteRemoved(MediaRouter router, RouteInfo info) {
14229a1de308cea2d160778fd977825f10a07b49d738Adam Powell        }
14239a1de308cea2d160778fd977825f10a07b49d738Adam Powell
14249a1de308cea2d160778fd977825f10a07b49d738Adam Powell        @Override
1425d0d2cda9d414da73773285d7fee9e13aef3495e9Adam Powell        public void onRouteChanged(MediaRouter router, RouteInfo info) {
14269a1de308cea2d160778fd977825f10a07b49d738Adam Powell        }
14279a1de308cea2d160778fd977825f10a07b49d738Adam Powell
14289a1de308cea2d160778fd977825f10a07b49d738Adam Powell        @Override
1429d0d2cda9d414da73773285d7fee9e13aef3495e9Adam Powell        public void onRouteGrouped(MediaRouter router, RouteInfo info, RouteGroup group,
1430d0d2cda9d414da73773285d7fee9e13aef3495e9Adam Powell                int index) {
14319a1de308cea2d160778fd977825f10a07b49d738Adam Powell        }
14329a1de308cea2d160778fd977825f10a07b49d738Adam Powell
14339a1de308cea2d160778fd977825f10a07b49d738Adam Powell        @Override
1434d0d2cda9d414da73773285d7fee9e13aef3495e9Adam Powell        public void onRouteUngrouped(MediaRouter router, RouteInfo info, RouteGroup group) {
14359a1de308cea2d160778fd977825f10a07b49d738Adam Powell        }
1436d0d2cda9d414da73773285d7fee9e13aef3495e9Adam Powell
14379a1de308cea2d160778fd977825f10a07b49d738Adam Powell    }
14381357012968f9066ea3051d83995e9bac69526c3cJean-Michel Trivi
14391357012968f9066ea3051d83995e9bac69526c3cJean-Michel Trivi    static class VolumeCallbackInfo {
14401357012968f9066ea3051d83995e9bac69526c3cJean-Michel Trivi        public final VolumeCallback vcb;
14411357012968f9066ea3051d83995e9bac69526c3cJean-Michel Trivi        public final RouteInfo route;
14421357012968f9066ea3051d83995e9bac69526c3cJean-Michel Trivi
14431357012968f9066ea3051d83995e9bac69526c3cJean-Michel Trivi        public VolumeCallbackInfo(VolumeCallback vcb, RouteInfo route) {
14441357012968f9066ea3051d83995e9bac69526c3cJean-Michel Trivi            this.vcb = vcb;
14451357012968f9066ea3051d83995e9bac69526c3cJean-Michel Trivi            this.route = route;
14461357012968f9066ea3051d83995e9bac69526c3cJean-Michel Trivi        }
14471357012968f9066ea3051d83995e9bac69526c3cJean-Michel Trivi    }
14481357012968f9066ea3051d83995e9bac69526c3cJean-Michel Trivi
14491357012968f9066ea3051d83995e9bac69526c3cJean-Michel Trivi    /**
14501357012968f9066ea3051d83995e9bac69526c3cJean-Michel Trivi     * @hide (to be un-hidden)
14511357012968f9066ea3051d83995e9bac69526c3cJean-Michel Trivi     * Interface for receiving events about volume changes.
14521357012968f9066ea3051d83995e9bac69526c3cJean-Michel Trivi     * All methods of this interface will be called from the application's main thread.
14531357012968f9066ea3051d83995e9bac69526c3cJean-Michel Trivi     *
14541357012968f9066ea3051d83995e9bac69526c3cJean-Michel Trivi     * <p>A VolumeCallback will only receive events relevant to routes that the callback
14551357012968f9066ea3051d83995e9bac69526c3cJean-Michel Trivi     * was registered for.</p>
14561357012968f9066ea3051d83995e9bac69526c3cJean-Michel Trivi     *
14571357012968f9066ea3051d83995e9bac69526c3cJean-Michel Trivi     * @see UserRouteInfo#setVolumeCallback(VolumeCallback)
14581357012968f9066ea3051d83995e9bac69526c3cJean-Michel Trivi     */
14591357012968f9066ea3051d83995e9bac69526c3cJean-Michel Trivi    public static abstract class VolumeCallback {
14601357012968f9066ea3051d83995e9bac69526c3cJean-Michel Trivi        /**
14611357012968f9066ea3051d83995e9bac69526c3cJean-Michel Trivi         * Called when the volume for the route should be increased or decreased.
14621357012968f9066ea3051d83995e9bac69526c3cJean-Michel Trivi         * @param info the route affected by this event
14631357012968f9066ea3051d83995e9bac69526c3cJean-Michel Trivi         * @param direction an integer indicating whether the volume is to be increased
14641357012968f9066ea3051d83995e9bac69526c3cJean-Michel Trivi         *     (positive value) or decreased (negative value).
14651357012968f9066ea3051d83995e9bac69526c3cJean-Michel Trivi         *     For bundled changes, the absolute value indicates the number of changes
14661357012968f9066ea3051d83995e9bac69526c3cJean-Michel Trivi         *     in the same direction, e.g. +3 corresponds to three "volume up" changes.
14671357012968f9066ea3051d83995e9bac69526c3cJean-Michel Trivi         */
14681357012968f9066ea3051d83995e9bac69526c3cJean-Michel Trivi        public abstract void onVolumeUpdateRequest(RouteInfo info, int direction);
14691357012968f9066ea3051d83995e9bac69526c3cJean-Michel Trivi        /**
14701357012968f9066ea3051d83995e9bac69526c3cJean-Michel Trivi         * Called when the volume for the route should be set to the given value
14711357012968f9066ea3051d83995e9bac69526c3cJean-Michel Trivi         * @param info the route affected by this event
14721357012968f9066ea3051d83995e9bac69526c3cJean-Michel Trivi         * @param volume an integer indicating the new volume value that should be used, always
14731357012968f9066ea3051d83995e9bac69526c3cJean-Michel Trivi         *     between 0 and the value set by {@link UserRouteInfo#setVolumeMax(int)}.
14741357012968f9066ea3051d83995e9bac69526c3cJean-Michel Trivi         */
14751357012968f9066ea3051d83995e9bac69526c3cJean-Michel Trivi        public abstract void onVolumeSetRequest(RouteInfo info, int volume);
14761357012968f9066ea3051d83995e9bac69526c3cJean-Michel Trivi    }
14771357012968f9066ea3051d83995e9bac69526c3cJean-Michel Trivi
14789a1de308cea2d160778fd977825f10a07b49d738Adam Powell}
1479