1/*
2 * Copyright (C) 2012 The Android Open Source Project
3 *
4 * Licensed under the Apache License, Version 2.0 (the "License");
5 * you may not use this file except in compliance with the License.
6 * You may obtain a copy of the License at
7 *
8 *      http://www.apache.org/licenses/LICENSE-2.0
9 *
10 * Unless required by applicable law or agreed to in writing, software
11 * distributed under the License is distributed on an "AS IS" BASIS,
12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 * See the License for the specific language governing permissions and
14 * limitations under the License.
15 */
16
17package android.app;
18
19import android.content.Context;
20import android.media.MediaRouter;
21import android.media.MediaRouter.RouteInfo;
22import android.util.Log;
23import android.view.ActionProvider;
24import android.view.MenuItem;
25import android.view.View;
26import android.view.ViewGroup;
27
28import java.lang.ref.WeakReference;
29
30/**
31 * The media route action provider displays a {@link MediaRouteButton media route button}
32 * in the application's {@link ActionBar} to allow the user to select routes and
33 * to control the currently selected route.
34 * <p>
35 * The application must specify the kinds of routes that the user should be allowed
36 * to select by specifying the route types with the {@link #setRouteTypes} method.
37 * </p><p>
38 * Refer to {@link MediaRouteButton} for a description of the button that will
39 * appear in the action bar menu.  Note that instead of disabling the button
40 * when no routes are available, the action provider will instead make the
41 * menu item invisible.  In this way, the button will only be visible when it
42 * is possible for the user to discover and select a matching route.
43 * </p>
44 */
45public class MediaRouteActionProvider extends ActionProvider {
46    private static final String TAG = "MediaRouteActionProvider";
47
48    private final Context mContext;
49    private final MediaRouter mRouter;
50    private final MediaRouterCallback mCallback;
51
52    private int mRouteTypes;
53    private MediaRouteButton mButton;
54    private View.OnClickListener mExtendedSettingsListener;
55
56    public MediaRouteActionProvider(Context context) {
57        super(context);
58
59        mContext = context;
60        mRouter = (MediaRouter) context.getSystemService(Context.MEDIA_ROUTER_SERVICE);
61        mCallback = new MediaRouterCallback(this);
62
63        // Start with live audio by default.
64        // TODO Update this when new route types are added; segment by API level
65        // when different route types were added.
66        setRouteTypes(MediaRouter.ROUTE_TYPE_LIVE_AUDIO);
67    }
68
69    /**
70     * Sets the types of routes that will be shown in the media route chooser dialog
71     * launched by this button.
72     *
73     * @param types The route types to match.
74     */
75    public void setRouteTypes(int types) {
76        if (mRouteTypes != types) {
77            // FIXME: We currently have no way of knowing whether the action provider
78            // is still needed by the UI.  Unfortunately this means the action provider
79            // may leak callbacks until garbage collection occurs.  This may result in
80            // media route providers doing more work than necessary in the short term
81            // while trying to discover routes that are no longer of interest to the
82            // application.  To solve this problem, the action provider will need some
83            // indication from the framework that it is being destroyed.
84            if (mRouteTypes != 0) {
85                mRouter.removeCallback(mCallback);
86            }
87            mRouteTypes = types;
88            if (types != 0) {
89                mRouter.addCallback(types, mCallback,
90                        MediaRouter.CALLBACK_FLAG_PASSIVE_DISCOVERY);
91            }
92            refreshRoute();
93
94            if (mButton != null) {
95                mButton.setRouteTypes(mRouteTypes);
96            }
97        }
98    }
99
100    public void setExtendedSettingsClickListener(View.OnClickListener listener) {
101        mExtendedSettingsListener = listener;
102        if (mButton != null) {
103            mButton.setExtendedSettingsClickListener(listener);
104        }
105    }
106
107    @Override
108    @SuppressWarnings("deprecation")
109    public View onCreateActionView() {
110        throw new UnsupportedOperationException("Use onCreateActionView(MenuItem) instead.");
111    }
112
113    @Override
114    public View onCreateActionView(MenuItem item) {
115        if (mButton != null) {
116            Log.e(TAG, "onCreateActionView: this ActionProvider is already associated " +
117                    "with a menu item. Don't reuse MediaRouteActionProvider instances! " +
118                    "Abandoning the old one...");
119        }
120
121        mButton = new MediaRouteButton(mContext);
122        mButton.setCheatSheetEnabled(true);
123        mButton.setRouteTypes(mRouteTypes);
124        mButton.setExtendedSettingsClickListener(mExtendedSettingsListener);
125        mButton.setLayoutParams(new ViewGroup.LayoutParams(
126                ViewGroup.LayoutParams.WRAP_CONTENT,
127                ViewGroup.LayoutParams.MATCH_PARENT));
128        return mButton;
129    }
130
131    @Override
132    public boolean onPerformDefaultAction() {
133        if (mButton != null) {
134            return mButton.showDialogInternal();
135        }
136        return false;
137    }
138
139    @Override
140    public boolean overridesItemVisibility() {
141        return true;
142    }
143
144    @Override
145    public boolean isVisible() {
146        return mRouter.isRouteAvailable(mRouteTypes,
147                MediaRouter.AVAILABILITY_FLAG_IGNORE_DEFAULT_ROUTE);
148    }
149
150    private void refreshRoute() {
151        refreshVisibility();
152    }
153
154    private static class MediaRouterCallback extends MediaRouter.SimpleCallback {
155        private final WeakReference<MediaRouteActionProvider> mProviderWeak;
156
157        public MediaRouterCallback(MediaRouteActionProvider provider) {
158            mProviderWeak = new WeakReference<MediaRouteActionProvider>(provider);
159        }
160
161        @Override
162        public void onRouteAdded(MediaRouter router, RouteInfo info) {
163            refreshRoute(router);
164        }
165
166        @Override
167        public void onRouteRemoved(MediaRouter router, RouteInfo info) {
168            refreshRoute(router);
169        }
170
171        @Override
172        public void onRouteChanged(MediaRouter router, RouteInfo info) {
173            refreshRoute(router);
174        }
175
176        private void refreshRoute(MediaRouter router) {
177            MediaRouteActionProvider provider = mProviderWeak.get();
178            if (provider != null) {
179                provider.refreshRoute();
180            } else {
181                router.removeCallback(this);
182            }
183        }
184    }
185}
186