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 17 package android.app; 18 19 import android.content.Context; 20 import android.media.MediaRouter; 21 import android.media.MediaRouter.RouteInfo; 22 import android.util.Log; 23 import android.view.ActionProvider; 24 import android.view.MenuItem; 25 import android.view.View; 26 import android.view.ViewGroup; 27 28 import 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 */ 45 public 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 MediaRouteActionProvider(Context context)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 */ setRouteTypes(int types)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 setExtendedSettingsClickListener(View.OnClickListener listener)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") onCreateActionView()109 public View onCreateActionView() { 110 throw new UnsupportedOperationException("Use onCreateActionView(MenuItem) instead."); 111 } 112 113 @Override onCreateActionView(MenuItem item)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.setRouteTypes(mRouteTypes); 123 mButton.setExtendedSettingsClickListener(mExtendedSettingsListener); 124 mButton.setLayoutParams(new ViewGroup.LayoutParams( 125 ViewGroup.LayoutParams.WRAP_CONTENT, 126 ViewGroup.LayoutParams.MATCH_PARENT)); 127 return mButton; 128 } 129 130 @Override onPerformDefaultAction()131 public boolean onPerformDefaultAction() { 132 if (mButton != null) { 133 return mButton.showDialogInternal(); 134 } 135 return false; 136 } 137 138 @Override overridesItemVisibility()139 public boolean overridesItemVisibility() { 140 return true; 141 } 142 143 @Override isVisible()144 public boolean isVisible() { 145 return mRouter.isRouteAvailable(mRouteTypes, 146 MediaRouter.AVAILABILITY_FLAG_IGNORE_DEFAULT_ROUTE); 147 } 148 refreshRoute()149 private void refreshRoute() { 150 refreshVisibility(); 151 } 152 153 private static class MediaRouterCallback extends MediaRouter.SimpleCallback { 154 private final WeakReference<MediaRouteActionProvider> mProviderWeak; 155 MediaRouterCallback(MediaRouteActionProvider provider)156 public MediaRouterCallback(MediaRouteActionProvider provider) { 157 mProviderWeak = new WeakReference<MediaRouteActionProvider>(provider); 158 } 159 160 @Override onRouteAdded(MediaRouter router, RouteInfo info)161 public void onRouteAdded(MediaRouter router, RouteInfo info) { 162 refreshRoute(router); 163 } 164 165 @Override onRouteRemoved(MediaRouter router, RouteInfo info)166 public void onRouteRemoved(MediaRouter router, RouteInfo info) { 167 refreshRoute(router); 168 } 169 170 @Override onRouteChanged(MediaRouter router, RouteInfo info)171 public void onRouteChanged(MediaRouter router, RouteInfo info) { 172 refreshRoute(router); 173 } 174 refreshRoute(MediaRouter router)175 private void refreshRoute(MediaRouter router) { 176 MediaRouteActionProvider provider = mProviderWeak.get(); 177 if (provider != null) { 178 provider.refreshRoute(); 179 } else { 180 router.removeCallback(this); 181 } 182 } 183 } 184 } 185