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.setCheatSheetEnabled(true); 1230abd3a6ce83ed23abe614155e776b600ef2a66c3Jeff Brown mButton.setRouteTypes(mRouteTypes); 1240abd3a6ce83ed23abe614155e776b600ef2a66c3Jeff Brown mButton.setExtendedSettingsClickListener(mExtendedSettingsListener); 1250abd3a6ce83ed23abe614155e776b600ef2a66c3Jeff Brown mButton.setLayoutParams(new ViewGroup.LayoutParams( 1260abd3a6ce83ed23abe614155e776b600ef2a66c3Jeff Brown ViewGroup.LayoutParams.WRAP_CONTENT, 1278c1b02e7592dd02f30750c56bf88c65f8acbd3c9Adam Powell ViewGroup.LayoutParams.MATCH_PARENT)); 1280abd3a6ce83ed23abe614155e776b600ef2a66c3Jeff Brown return mButton; 129690ffb4e1f735148a15f2036d9a3c1962fba188cAdam Powell } 130690ffb4e1f735148a15f2036d9a3c1962fba188cAdam Powell 131690ffb4e1f735148a15f2036d9a3c1962fba188cAdam Powell @Override 132690ffb4e1f735148a15f2036d9a3c1962fba188cAdam Powell public boolean onPerformDefaultAction() { 1330abd3a6ce83ed23abe614155e776b600ef2a66c3Jeff Brown if (mButton != null) { 1340abd3a6ce83ed23abe614155e776b600ef2a66c3Jeff Brown return mButton.showDialogInternal(); 135b35c445f34e1a18e17aef3e3dfbc1c39b4d1815cAdam Powell } 1360abd3a6ce83ed23abe614155e776b600ef2a66c3Jeff Brown return false; 137b35c445f34e1a18e17aef3e3dfbc1c39b4d1815cAdam Powell } 138b35c445f34e1a18e17aef3e3dfbc1c39b4d1815cAdam Powell 139130b4572d1f3df702e5b296a655d15a41f6d4c66Adam Powell @Override 140130b4572d1f3df702e5b296a655d15a41f6d4c66Adam Powell public boolean overridesItemVisibility() { 141130b4572d1f3df702e5b296a655d15a41f6d4c66Adam Powell return true; 142130b4572d1f3df702e5b296a655d15a41f6d4c66Adam Powell } 143690ffb4e1f735148a15f2036d9a3c1962fba188cAdam Powell 144130b4572d1f3df702e5b296a655d15a41f6d4c66Adam Powell @Override 145130b4572d1f3df702e5b296a655d15a41f6d4c66Adam Powell public boolean isVisible() { 1460abd3a6ce83ed23abe614155e776b600ef2a66c3Jeff Brown return mRouter.isRouteAvailable(mRouteTypes, 1470abd3a6ce83ed23abe614155e776b600ef2a66c3Jeff Brown MediaRouter.AVAILABILITY_FLAG_IGNORE_DEFAULT_ROUTE); 1480abd3a6ce83ed23abe614155e776b600ef2a66c3Jeff Brown } 1490abd3a6ce83ed23abe614155e776b600ef2a66c3Jeff Brown 1500abd3a6ce83ed23abe614155e776b600ef2a66c3Jeff Brown private void refreshRoute() { 1510abd3a6ce83ed23abe614155e776b600ef2a66c3Jeff Brown refreshVisibility(); 152690ffb4e1f735148a15f2036d9a3c1962fba188cAdam Powell } 15339d5c6172503620ac3761148adac5fd7fa20d02dAdam Powell 1540abd3a6ce83ed23abe614155e776b600ef2a66c3Jeff Brown private static class MediaRouterCallback extends MediaRouter.SimpleCallback { 1550abd3a6ce83ed23abe614155e776b600ef2a66c3Jeff Brown private final WeakReference<MediaRouteActionProvider> mProviderWeak; 15639d5c6172503620ac3761148adac5fd7fa20d02dAdam Powell 1570abd3a6ce83ed23abe614155e776b600ef2a66c3Jeff Brown public MediaRouterCallback(MediaRouteActionProvider provider) { 1580abd3a6ce83ed23abe614155e776b600ef2a66c3Jeff Brown mProviderWeak = new WeakReference<MediaRouteActionProvider>(provider); 15939d5c6172503620ac3761148adac5fd7fa20d02dAdam Powell } 16039d5c6172503620ac3761148adac5fd7fa20d02dAdam Powell 16139d5c6172503620ac3761148adac5fd7fa20d02dAdam Powell @Override 16239d5c6172503620ac3761148adac5fd7fa20d02dAdam Powell public void onRouteAdded(MediaRouter router, RouteInfo info) { 1630abd3a6ce83ed23abe614155e776b600ef2a66c3Jeff Brown refreshRoute(router); 16439d5c6172503620ac3761148adac5fd7fa20d02dAdam Powell } 16539d5c6172503620ac3761148adac5fd7fa20d02dAdam Powell 16639d5c6172503620ac3761148adac5fd7fa20d02dAdam Powell @Override 16739d5c6172503620ac3761148adac5fd7fa20d02dAdam Powell public void onRouteRemoved(MediaRouter router, RouteInfo info) { 1680abd3a6ce83ed23abe614155e776b600ef2a66c3Jeff Brown refreshRoute(router); 1690abd3a6ce83ed23abe614155e776b600ef2a66c3Jeff Brown } 1700abd3a6ce83ed23abe614155e776b600ef2a66c3Jeff Brown 1710abd3a6ce83ed23abe614155e776b600ef2a66c3Jeff Brown @Override 1720abd3a6ce83ed23abe614155e776b600ef2a66c3Jeff Brown public void onRouteChanged(MediaRouter router, RouteInfo info) { 1730abd3a6ce83ed23abe614155e776b600ef2a66c3Jeff Brown refreshRoute(router); 1740abd3a6ce83ed23abe614155e776b600ef2a66c3Jeff Brown } 1750abd3a6ce83ed23abe614155e776b600ef2a66c3Jeff Brown 1760abd3a6ce83ed23abe614155e776b600ef2a66c3Jeff Brown private void refreshRoute(MediaRouter router) { 1770abd3a6ce83ed23abe614155e776b600ef2a66c3Jeff Brown MediaRouteActionProvider provider = mProviderWeak.get(); 1780abd3a6ce83ed23abe614155e776b600ef2a66c3Jeff Brown if (provider != null) { 1790abd3a6ce83ed23abe614155e776b600ef2a66c3Jeff Brown provider.refreshRoute(); 1800abd3a6ce83ed23abe614155e776b600ef2a66c3Jeff Brown } else { 18139d5c6172503620ac3761148adac5fd7fa20d02dAdam Powell router.removeCallback(this); 18239d5c6172503620ac3761148adac5fd7fa20d02dAdam Powell } 18339d5c6172503620ac3761148adac5fd7fa20d02dAdam Powell } 18439d5c6172503620ac3761148adac5fd7fa20d02dAdam Powell } 185690ffb4e1f735148a15f2036d9a3c1962fba188cAdam Powell} 186