1690ffb4e1f735148a15f2036d9a3c1962fba188cAdam Powell/*
2690ffb4e1f735148a15f2036d9a3c1962fba188cAdam Powell * Copyright (C) 2012 The Android Open Source Project
3690ffb4e1f735148a15f2036d9a3c1962fba188cAdam Powell *
4690ffb4e1f735148a15f2036d9a3c1962fba188cAdam Powell * Licensed under the Apache License, Version 2.0 (the "License");
5690ffb4e1f735148a15f2036d9a3c1962fba188cAdam Powell * you may not use this file except in compliance with the License.
6690ffb4e1f735148a15f2036d9a3c1962fba188cAdam Powell * You may obtain a copy of the License at
7690ffb4e1f735148a15f2036d9a3c1962fba188cAdam Powell *
8690ffb4e1f735148a15f2036d9a3c1962fba188cAdam Powell *      http://www.apache.org/licenses/LICENSE-2.0
9690ffb4e1f735148a15f2036d9a3c1962fba188cAdam Powell *
10690ffb4e1f735148a15f2036d9a3c1962fba188cAdam Powell * Unless required by applicable law or agreed to in writing, software
11690ffb4e1f735148a15f2036d9a3c1962fba188cAdam Powell * distributed under the License is distributed on an "AS IS" BASIS,
12690ffb4e1f735148a15f2036d9a3c1962fba188cAdam Powell * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13690ffb4e1f735148a15f2036d9a3c1962fba188cAdam Powell * See the License for the specific language governing permissions and
14690ffb4e1f735148a15f2036d9a3c1962fba188cAdam Powell * limitations under the License.
15690ffb4e1f735148a15f2036d9a3c1962fba188cAdam Powell */
16690ffb4e1f735148a15f2036d9a3c1962fba188cAdam Powell
17690ffb4e1f735148a15f2036d9a3c1962fba188cAdam Powellpackage android.app;
18690ffb4e1f735148a15f2036d9a3c1962fba188cAdam Powell
19690ffb4e1f735148a15f2036d9a3c1962fba188cAdam Powellimport android.content.Context;
20690ffb4e1f735148a15f2036d9a3c1962fba188cAdam Powellimport android.media.MediaRouter;
2139d5c6172503620ac3761148adac5fd7fa20d02dAdam Powellimport android.media.MediaRouter.RouteInfo;
22690ffb4e1f735148a15f2036d9a3c1962fba188cAdam Powellimport android.util.Log;
23690ffb4e1f735148a15f2036d9a3c1962fba188cAdam Powellimport android.view.ActionProvider;
24690ffb4e1f735148a15f2036d9a3c1962fba188cAdam Powellimport android.view.MenuItem;
25690ffb4e1f735148a15f2036d9a3c1962fba188cAdam Powellimport android.view.View;
268c1b02e7592dd02f30750c56bf88c65f8acbd3c9Adam Powellimport android.view.ViewGroup;
27690ffb4e1f735148a15f2036d9a3c1962fba188cAdam Powell
2839d5c6172503620ac3761148adac5fd7fa20d02dAdam Powellimport java.lang.ref.WeakReference;
2939d5c6172503620ac3761148adac5fd7fa20d02dAdam Powell
300abd3a6ce83ed23abe614155e776b600ef2a66c3Jeff Brown/**
310abd3a6ce83ed23abe614155e776b600ef2a66c3Jeff Brown * The media route action provider displays a {@link MediaRouteButton media route button}
320abd3a6ce83ed23abe614155e776b600ef2a66c3Jeff Brown * in the application's {@link ActionBar} to allow the user to select routes and
330abd3a6ce83ed23abe614155e776b600ef2a66c3Jeff Brown * to control the currently selected route.
340abd3a6ce83ed23abe614155e776b600ef2a66c3Jeff Brown * <p>
350abd3a6ce83ed23abe614155e776b600ef2a66c3Jeff Brown * The application must specify the kinds of routes that the user should be allowed
360abd3a6ce83ed23abe614155e776b600ef2a66c3Jeff Brown * to select by specifying the route types with the {@link #setRouteTypes} method.
370abd3a6ce83ed23abe614155e776b600ef2a66c3Jeff Brown * </p><p>
380abd3a6ce83ed23abe614155e776b600ef2a66c3Jeff Brown * Refer to {@link MediaRouteButton} for a description of the button that will
390abd3a6ce83ed23abe614155e776b600ef2a66c3Jeff Brown * appear in the action bar menu.  Note that instead of disabling the button
400abd3a6ce83ed23abe614155e776b600ef2a66c3Jeff Brown * when no routes are available, the action provider will instead make the
410abd3a6ce83ed23abe614155e776b600ef2a66c3Jeff Brown * menu item invisible.  In this way, the button will only be visible when it
420abd3a6ce83ed23abe614155e776b600ef2a66c3Jeff Brown * is possible for the user to discover and select a matching route.
430abd3a6ce83ed23abe614155e776b600ef2a66c3Jeff Brown * </p>
440abd3a6ce83ed23abe614155e776b600ef2a66c3Jeff Brown */
45690ffb4e1f735148a15f2036d9a3c1962fba188cAdam Powellpublic class MediaRouteActionProvider extends ActionProvider {
46690ffb4e1f735148a15f2036d9a3c1962fba188cAdam Powell    private static final String TAG = "MediaRouteActionProvider";
47690ffb4e1f735148a15f2036d9a3c1962fba188cAdam Powell
480abd3a6ce83ed23abe614155e776b600ef2a66c3Jeff Brown    private final Context mContext;
490abd3a6ce83ed23abe614155e776b600ef2a66c3Jeff Brown    private final MediaRouter mRouter;
500abd3a6ce83ed23abe614155e776b600ef2a66c3Jeff Brown    private final MediaRouterCallback mCallback;
510abd3a6ce83ed23abe614155e776b600ef2a66c3Jeff Brown
52690ffb4e1f735148a15f2036d9a3c1962fba188cAdam Powell    private int mRouteTypes;
530abd3a6ce83ed23abe614155e776b600ef2a66c3Jeff Brown    private MediaRouteButton mButton;
54b35c445f34e1a18e17aef3e3dfbc1c39b4d1815cAdam Powell    private View.OnClickListener mExtendedSettingsListener;
55690ffb4e1f735148a15f2036d9a3c1962fba188cAdam Powell
56690ffb4e1f735148a15f2036d9a3c1962fba188cAdam Powell    public MediaRouteActionProvider(Context context) {
57690ffb4e1f735148a15f2036d9a3c1962fba188cAdam Powell        super(context);
580abd3a6ce83ed23abe614155e776b600ef2a66c3Jeff Brown
59690ffb4e1f735148a15f2036d9a3c1962fba188cAdam Powell        mContext = context;
60b35c445f34e1a18e17aef3e3dfbc1c39b4d1815cAdam Powell        mRouter = (MediaRouter) context.getSystemService(Context.MEDIA_ROUTER_SERVICE);
610abd3a6ce83ed23abe614155e776b600ef2a66c3Jeff Brown        mCallback = new MediaRouterCallback(this);
62690ffb4e1f735148a15f2036d9a3c1962fba188cAdam Powell
63690ffb4e1f735148a15f2036d9a3c1962fba188cAdam Powell        // Start with live audio by default.
64690ffb4e1f735148a15f2036d9a3c1962fba188cAdam Powell        // TODO Update this when new route types are added; segment by API level
65690ffb4e1f735148a15f2036d9a3c1962fba188cAdam Powell        // when different route types were added.
66690ffb4e1f735148a15f2036d9a3c1962fba188cAdam Powell        setRouteTypes(MediaRouter.ROUTE_TYPE_LIVE_AUDIO);
67690ffb4e1f735148a15f2036d9a3c1962fba188cAdam Powell    }
68690ffb4e1f735148a15f2036d9a3c1962fba188cAdam Powell
690abd3a6ce83ed23abe614155e776b600ef2a66c3Jeff Brown    /**
700abd3a6ce83ed23abe614155e776b600ef2a66c3Jeff Brown     * Sets the types of routes that will be shown in the media route chooser dialog
710abd3a6ce83ed23abe614155e776b600ef2a66c3Jeff Brown     * launched by this button.
720abd3a6ce83ed23abe614155e776b600ef2a66c3Jeff Brown     *
730abd3a6ce83ed23abe614155e776b600ef2a66c3Jeff Brown     * @param types The route types to match.
740abd3a6ce83ed23abe614155e776b600ef2a66c3Jeff Brown     */
75690ffb4e1f735148a15f2036d9a3c1962fba188cAdam Powell    public void setRouteTypes(int types) {
760abd3a6ce83ed23abe614155e776b600ef2a66c3Jeff Brown        if (mRouteTypes != types) {
770abd3a6ce83ed23abe614155e776b600ef2a66c3Jeff Brown            // FIXME: We currently have no way of knowing whether the action provider
780abd3a6ce83ed23abe614155e776b600ef2a66c3Jeff Brown            // is still needed by the UI.  Unfortunately this means the action provider
790abd3a6ce83ed23abe614155e776b600ef2a66c3Jeff Brown            // may leak callbacks until garbage collection occurs.  This may result in
800abd3a6ce83ed23abe614155e776b600ef2a66c3Jeff Brown            // media route providers doing more work than necessary in the short term
810abd3a6ce83ed23abe614155e776b600ef2a66c3Jeff Brown            // while trying to discover routes that are no longer of interest to the
820abd3a6ce83ed23abe614155e776b600ef2a66c3Jeff Brown            // application.  To solve this problem, the action provider will need some
830abd3a6ce83ed23abe614155e776b600ef2a66c3Jeff Brown            // indication from the framework that it is being destroyed.
840abd3a6ce83ed23abe614155e776b600ef2a66c3Jeff Brown            if (mRouteTypes != 0) {
850abd3a6ce83ed23abe614155e776b600ef2a66c3Jeff Brown                mRouter.removeCallback(mCallback);
860abd3a6ce83ed23abe614155e776b600ef2a66c3Jeff Brown            }
870abd3a6ce83ed23abe614155e776b600ef2a66c3Jeff Brown            mRouteTypes = types;
880abd3a6ce83ed23abe614155e776b600ef2a66c3Jeff Brown            if (types != 0) {
890abd3a6ce83ed23abe614155e776b600ef2a66c3Jeff Brown                mRouter.addCallback(types, mCallback,
900abd3a6ce83ed23abe614155e776b600ef2a66c3Jeff Brown                        MediaRouter.CALLBACK_FLAG_PASSIVE_DISCOVERY);
910abd3a6ce83ed23abe614155e776b600ef2a66c3Jeff Brown            }
920abd3a6ce83ed23abe614155e776b600ef2a66c3Jeff Brown            refreshRoute();
930abd3a6ce83ed23abe614155e776b600ef2a66c3Jeff Brown
940abd3a6ce83ed23abe614155e776b600ef2a66c3Jeff Brown            if (mButton != null) {
950abd3a6ce83ed23abe614155e776b600ef2a66c3Jeff Brown                mButton.setRouteTypes(mRouteTypes);
960abd3a6ce83ed23abe614155e776b600ef2a66c3Jeff Brown            }
9739d5c6172503620ac3761148adac5fd7fa20d02dAdam Powell        }
980abd3a6ce83ed23abe614155e776b600ef2a66c3Jeff Brown    }
990abd3a6ce83ed23abe614155e776b600ef2a66c3Jeff Brown
1000abd3a6ce83ed23abe614155e776b600ef2a66c3Jeff Brown    public void setExtendedSettingsClickListener(View.OnClickListener listener) {
1010abd3a6ce83ed23abe614155e776b600ef2a66c3Jeff Brown        mExtendedSettingsListener = listener;
1020abd3a6ce83ed23abe614155e776b600ef2a66c3Jeff Brown        if (mButton != null) {
1030abd3a6ce83ed23abe614155e776b600ef2a66c3Jeff Brown            mButton.setExtendedSettingsClickListener(listener);
104690ffb4e1f735148a15f2036d9a3c1962fba188cAdam Powell        }
105690ffb4e1f735148a15f2036d9a3c1962fba188cAdam Powell    }
106690ffb4e1f735148a15f2036d9a3c1962fba188cAdam Powell
107690ffb4e1f735148a15f2036d9a3c1962fba188cAdam Powell    @Override
1080abd3a6ce83ed23abe614155e776b600ef2a66c3Jeff Brown    @SuppressWarnings("deprecation")
109690ffb4e1f735148a15f2036d9a3c1962fba188cAdam Powell    public View onCreateActionView() {
110690ffb4e1f735148a15f2036d9a3c1962fba188cAdam Powell        throw new UnsupportedOperationException("Use onCreateActionView(MenuItem) instead.");
111690ffb4e1f735148a15f2036d9a3c1962fba188cAdam Powell    }
112690ffb4e1f735148a15f2036d9a3c1962fba188cAdam Powell
113690ffb4e1f735148a15f2036d9a3c1962fba188cAdam Powell    @Override
114690ffb4e1f735148a15f2036d9a3c1962fba188cAdam Powell    public View onCreateActionView(MenuItem item) {
1150abd3a6ce83ed23abe614155e776b600ef2a66c3Jeff Brown        if (mButton != null) {
116690ffb4e1f735148a15f2036d9a3c1962fba188cAdam Powell            Log.e(TAG, "onCreateActionView: this ActionProvider is already associated " +
117690ffb4e1f735148a15f2036d9a3c1962fba188cAdam Powell                    "with a menu item. Don't reuse MediaRouteActionProvider instances! " +
118690ffb4e1f735148a15f2036d9a3c1962fba188cAdam Powell                    "Abandoning the old one...");
119690ffb4e1f735148a15f2036d9a3c1962fba188cAdam Powell        }
1200abd3a6ce83ed23abe614155e776b600ef2a66c3Jeff Brown
1210abd3a6ce83ed23abe614155e776b600ef2a66c3Jeff Brown        mButton = new MediaRouteButton(mContext);
1220abd3a6ce83ed23abe614155e776b600ef2a66c3Jeff Brown        mButton.setRouteTypes(mRouteTypes);
1230abd3a6ce83ed23abe614155e776b600ef2a66c3Jeff Brown        mButton.setExtendedSettingsClickListener(mExtendedSettingsListener);
1240abd3a6ce83ed23abe614155e776b600ef2a66c3Jeff Brown        mButton.setLayoutParams(new ViewGroup.LayoutParams(
1250abd3a6ce83ed23abe614155e776b600ef2a66c3Jeff Brown                ViewGroup.LayoutParams.WRAP_CONTENT,
1268c1b02e7592dd02f30750c56bf88c65f8acbd3c9Adam Powell                ViewGroup.LayoutParams.MATCH_PARENT));
1270abd3a6ce83ed23abe614155e776b600ef2a66c3Jeff Brown        return mButton;
128690ffb4e1f735148a15f2036d9a3c1962fba188cAdam Powell    }
129690ffb4e1f735148a15f2036d9a3c1962fba188cAdam Powell
130690ffb4e1f735148a15f2036d9a3c1962fba188cAdam Powell    @Override
131690ffb4e1f735148a15f2036d9a3c1962fba188cAdam Powell    public boolean onPerformDefaultAction() {
1320abd3a6ce83ed23abe614155e776b600ef2a66c3Jeff Brown        if (mButton != null) {
1330abd3a6ce83ed23abe614155e776b600ef2a66c3Jeff Brown            return mButton.showDialogInternal();
134b35c445f34e1a18e17aef3e3dfbc1c39b4d1815cAdam Powell        }
1350abd3a6ce83ed23abe614155e776b600ef2a66c3Jeff Brown        return false;
136b35c445f34e1a18e17aef3e3dfbc1c39b4d1815cAdam Powell    }
137b35c445f34e1a18e17aef3e3dfbc1c39b4d1815cAdam Powell
138130b4572d1f3df702e5b296a655d15a41f6d4c66Adam Powell    @Override
139130b4572d1f3df702e5b296a655d15a41f6d4c66Adam Powell    public boolean overridesItemVisibility() {
140130b4572d1f3df702e5b296a655d15a41f6d4c66Adam Powell        return true;
141130b4572d1f3df702e5b296a655d15a41f6d4c66Adam Powell    }
142690ffb4e1f735148a15f2036d9a3c1962fba188cAdam Powell
143130b4572d1f3df702e5b296a655d15a41f6d4c66Adam Powell    @Override
144130b4572d1f3df702e5b296a655d15a41f6d4c66Adam Powell    public boolean isVisible() {
1450abd3a6ce83ed23abe614155e776b600ef2a66c3Jeff Brown        return mRouter.isRouteAvailable(mRouteTypes,
1460abd3a6ce83ed23abe614155e776b600ef2a66c3Jeff Brown                MediaRouter.AVAILABILITY_FLAG_IGNORE_DEFAULT_ROUTE);
1470abd3a6ce83ed23abe614155e776b600ef2a66c3Jeff Brown    }
1480abd3a6ce83ed23abe614155e776b600ef2a66c3Jeff Brown
1490abd3a6ce83ed23abe614155e776b600ef2a66c3Jeff Brown    private void refreshRoute() {
1500abd3a6ce83ed23abe614155e776b600ef2a66c3Jeff Brown        refreshVisibility();
151690ffb4e1f735148a15f2036d9a3c1962fba188cAdam Powell    }
15239d5c6172503620ac3761148adac5fd7fa20d02dAdam Powell
1530abd3a6ce83ed23abe614155e776b600ef2a66c3Jeff Brown    private static class MediaRouterCallback extends MediaRouter.SimpleCallback {
1540abd3a6ce83ed23abe614155e776b600ef2a66c3Jeff Brown        private final WeakReference<MediaRouteActionProvider> mProviderWeak;
15539d5c6172503620ac3761148adac5fd7fa20d02dAdam Powell
1560abd3a6ce83ed23abe614155e776b600ef2a66c3Jeff Brown        public MediaRouterCallback(MediaRouteActionProvider provider) {
1570abd3a6ce83ed23abe614155e776b600ef2a66c3Jeff Brown            mProviderWeak = new WeakReference<MediaRouteActionProvider>(provider);
15839d5c6172503620ac3761148adac5fd7fa20d02dAdam Powell        }
15939d5c6172503620ac3761148adac5fd7fa20d02dAdam Powell
16039d5c6172503620ac3761148adac5fd7fa20d02dAdam Powell        @Override
16139d5c6172503620ac3761148adac5fd7fa20d02dAdam Powell        public void onRouteAdded(MediaRouter router, RouteInfo info) {
1620abd3a6ce83ed23abe614155e776b600ef2a66c3Jeff Brown            refreshRoute(router);
16339d5c6172503620ac3761148adac5fd7fa20d02dAdam Powell        }
16439d5c6172503620ac3761148adac5fd7fa20d02dAdam Powell
16539d5c6172503620ac3761148adac5fd7fa20d02dAdam Powell        @Override
16639d5c6172503620ac3761148adac5fd7fa20d02dAdam Powell        public void onRouteRemoved(MediaRouter router, RouteInfo info) {
1670abd3a6ce83ed23abe614155e776b600ef2a66c3Jeff Brown            refreshRoute(router);
1680abd3a6ce83ed23abe614155e776b600ef2a66c3Jeff Brown        }
1690abd3a6ce83ed23abe614155e776b600ef2a66c3Jeff Brown
1700abd3a6ce83ed23abe614155e776b600ef2a66c3Jeff Brown        @Override
1710abd3a6ce83ed23abe614155e776b600ef2a66c3Jeff Brown        public void onRouteChanged(MediaRouter router, RouteInfo info) {
1720abd3a6ce83ed23abe614155e776b600ef2a66c3Jeff Brown            refreshRoute(router);
1730abd3a6ce83ed23abe614155e776b600ef2a66c3Jeff Brown        }
1740abd3a6ce83ed23abe614155e776b600ef2a66c3Jeff Brown
1750abd3a6ce83ed23abe614155e776b600ef2a66c3Jeff Brown        private void refreshRoute(MediaRouter router) {
1760abd3a6ce83ed23abe614155e776b600ef2a66c3Jeff Brown            MediaRouteActionProvider provider = mProviderWeak.get();
1770abd3a6ce83ed23abe614155e776b600ef2a66c3Jeff Brown            if (provider != null) {
1780abd3a6ce83ed23abe614155e776b600ef2a66c3Jeff Brown                provider.refreshRoute();
1790abd3a6ce83ed23abe614155e776b600ef2a66c3Jeff Brown            } else {
18039d5c6172503620ac3761148adac5fd7fa20d02dAdam Powell                router.removeCallback(this);
18139d5c6172503620ac3761148adac5fd7fa20d02dAdam Powell            }
18239d5c6172503620ac3761148adac5fd7fa20d02dAdam Powell        }
18339d5c6172503620ac3761148adac5fd7fa20d02dAdam Powell    }
184690ffb4e1f735148a15f2036d9a3c1962fba188cAdam Powell}
185