MediaRouteActionProvider.java revision 9942d40d0d952b03b583fe66f434676793697aa2
1/* 2 * Copyright (C) 2013 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.support.v7.app; 18 19import android.content.Context; 20import android.support.v4.view.ActionProvider; 21import android.support.v7.media.MediaRouter; 22import android.support.v7.media.MediaRouteSelector; 23import android.util.Log; 24import android.view.View; 25import android.view.ViewGroup; 26 27import java.lang.ref.WeakReference; 28 29/** 30 * The media route action provider displays a {@link MediaRouteButton media route button} 31 * in the application's {@link ActionBar} to allow the user to select routes and 32 * to control the currently selected route. 33 * <p> 34 * The application must specify the kinds of routes that the user should be allowed 35 * to select by specifying a {@link MediaRouteSelector selector} with the 36 * {@link #setRouteSelector} 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 * <h3>Prerequisites</h3> 46 * <p> 47 * To use the media route action provider, the activity must be a subclass of 48 * {@link ActionBarActivity} from the <code>android.support.v7.appcompat</code> 49 * support library. Refer to support library documentation for details. 50 * </p> 51 * 52 * <h3>Example</h3> 53 * <p> 54 * </p><p> 55 * The application should define a menu resource to include the provider in the 56 * action bar options menu. Note that the support library action bar uses attributes 57 * that are defined in the application's resource namespace rather than the framework's 58 * resource namespace to configure each item. 59 * </p><pre> 60 * <menu xmlns:android="http://schemas.android.com/apk/res/android" 61 * xmlns:app="http://schemas.android.com/apk/res-auto"> 62 * <item android:id="@+id/media_route_menu_item" 63 * android:title="@string/media_route_menu_title" 64 * app:showAsAction="always" 65 * app:actionProviderClass="android.support.v7.app.MediaRouteActionProvider"/> 66 * </menu> 67 * </pre><p> 68 * Then configure the menu and set the route selector for the chooser. 69 * </p><pre> 70 * public class MyActivity extends ActionBarActivity { 71 * private MediaRouter mRouter; 72 * private MediaRouter.Callback mCallback; 73 * private MediaRouteSelector mSelector; 74 * 75 * protected void onCreate(Bundle savedInstanceState) { 76 * super.onCreate(savedInstanceState); 77 * 78 * mRouter = Mediarouter.getInstance(this); 79 * mSelector = new MediaRouteSelector.Builder() 80 * .addControlCategory(MediaControlIntent.CATEGORY_LIVE_AUDIO) 81 * .addControlCategory(MediaControlIntent.CATEGORY_REMOTE_PLAYBACK) 82 * .build(); 83 * mCallback = new MyCallback(); 84 * } 85 * 86 * // Add the callback on resume to tell the media router what kinds of routes 87 * // the application is interested in so that it can try to discover suitable ones. 88 * public void onResume() { 89 * super.onResume(); 90 * 91 * mediaRouter.addCallback(mSelector, mCallback); 92 * 93 * MediaRouter.RouteInfo route = mediaRouter.updateSelectedRoute(mSelector); 94 * // do something with the route... 95 * } 96 * 97 * // Remove the selector on pause to tell the media router that it no longer 98 * // needs to invest effort trying to discover routes of these kinds for now. 99 * public void onPause() { 100 * super.onPause(); 101 * 102 * mediaRouter.removeCallback(mCallback); 103 * } 104 * 105 * public boolean onCreateOptionsMenu(Menu menu) { 106 * super.onCreateOptionsMenu(menu); 107 * 108 * getMenuInflater().inflate(R.menu.sample_media_router_menu, menu); 109 * 110 * MenuItem mediaRouteMenuItem = menu.findItem(R.id.media_route_menu_item); 111 * MediaRouteActionProvider mediaRouteActionProvider = 112 * (MediaRouteActionProvider)MenuItemCompat.getActionProvider(mediaRouteMenuItem); 113 * mediaRouteActionProvider.setRouteSelector(mSelector); 114 * return true; 115 * } 116 * 117 * private final class MyCallback extends MediaRouter.Callback { 118 * // Implement callback methods as needed. 119 * } 120 * } 121 * </pre> 122 * 123 * @see #setRouteSelector 124 */ 125public class MediaRouteActionProvider extends ActionProvider { 126 private static final String TAG = "MediaRouteActionProvider"; 127 128 private final MediaRouter mRouter; 129 private final MediaRouterCallback mCallback; 130 131 private MediaRouteSelector mSelector = MediaRouteSelector.EMPTY; 132 private MediaRouteButton mButton; 133 134 /** 135 * Creates the action provider. 136 * 137 * @param context The context. 138 */ 139 public MediaRouteActionProvider(Context context) { 140 super(context); 141 142 mRouter = MediaRouter.getInstance(context); 143 mCallback = new MediaRouterCallback(this); 144 } 145 146 /** 147 * Gets the media route selector for filtering the routes that the user can 148 * select using the media route chooser dialog. 149 * 150 * @return The selector, never null. 151 */ 152 public MediaRouteSelector getRouteSelector() { 153 return mSelector; 154 } 155 156 /** 157 * Sets the media route selector for filtering the routes that the user can 158 * select using the media route chooser dialog. 159 * 160 * @param selector The selector, must not be null. 161 */ 162 public void setRouteSelector(MediaRouteSelector selector) { 163 if (selector == null) { 164 throw new IllegalArgumentException("selector must not be null"); 165 } 166 167 if (!mSelector.equals(selector)) { 168 // FIXME: We currently have no way of knowing whether the action provider 169 // is still needed by the UI. Unfortunately this means the action provider 170 // may leak callbacks until garbage collection occurs. This may result in 171 // media route providers doing more work than necessary in the short term 172 // while trying to discover routes that are no longer of interest to the 173 // application. To solve this problem, the action provider will need some 174 // indication from the framework that it is being destroyed. 175 if (!mSelector.isEmpty()) { 176 mRouter.removeCallback(mCallback); 177 } 178 if (!selector.isEmpty()) { 179 mRouter.addCallback(selector, mCallback); 180 } 181 mSelector = selector; 182 refreshRoute(); 183 184 if (mButton != null) { 185 mButton.setRouteSelector(selector); 186 } 187 } 188 } 189 190 /** 191 * Gets the associated media route button, or null if it has not yet been created. 192 */ 193 public MediaRouteButton getMediaRouteButton() { 194 return mButton; 195 } 196 197 /** 198 * Called when the media route button is being created. 199 */ 200 @SuppressWarnings("deprecation") 201 public MediaRouteButton onCreateMediaRouteButton() { 202 if (mButton != null) { 203 Log.e(TAG, "onCreateMediaRouteButton: This ActionProvider is already associated " 204 + "with a menu item. Don't reuse MediaRouteActionProvider instances! " 205 + "Abandoning the old button..."); 206 } 207 208 mButton = new MediaRouteButton(getContext()); 209 mButton.setCheatSheetEnabled(true); 210 mButton.setRouteSelector(mSelector); 211 mButton.setLayoutParams(new ViewGroup.LayoutParams( 212 ViewGroup.LayoutParams.WRAP_CONTENT, 213 ViewGroup.LayoutParams.FILL_PARENT)); 214 return mButton; 215 } 216 217 @Override 218 public View onCreateActionView() { 219 if (mButton != null) { 220 Log.e(TAG, "onCreateActionView: this ActionProvider is already associated " + 221 "with a menu item. Don't reuse MediaRouteActionProvider instances! " + 222 "Abandoning the old menu item..."); 223 } 224 225 mButton = onCreateMediaRouteButton(); 226 return mButton; 227 } 228 229 @Override 230 public boolean onPerformDefaultAction() { 231 if (mButton != null) { 232 return mButton.showDialog(); 233 } 234 return false; 235 } 236 237 @Override 238 public boolean overridesItemVisibility() { 239 return true; 240 } 241 242 @Override 243 public boolean isVisible() { 244 return mRouter.isRouteAvailable(mSelector, 245 MediaRouter.AVAILABILITY_FLAG_IGNORE_DEFAULT_ROUTE 246 | MediaRouter.AVAILABILITY_FLAG_CONSIDER_ACTIVE_SCAN); 247 } 248 249 private void refreshRoute() { 250 refreshVisibility(); 251 } 252 253 private static final class MediaRouterCallback extends MediaRouter.Callback { 254 private final WeakReference<MediaRouteActionProvider> mProviderWeak; 255 256 public MediaRouterCallback(MediaRouteActionProvider provider) { 257 mProviderWeak = new WeakReference<MediaRouteActionProvider>(provider); 258 } 259 260 @Override 261 public void onRouteAdded(MediaRouter router, MediaRouter.RouteInfo info) { 262 refreshRoute(router); 263 } 264 265 @Override 266 public void onRouteRemoved(MediaRouter router, MediaRouter.RouteInfo info) { 267 refreshRoute(router); 268 } 269 270 @Override 271 public void onRouteChanged(MediaRouter router, MediaRouter.RouteInfo info) { 272 refreshRoute(router); 273 } 274 275 @Override 276 public void onProviderAdded(MediaRouter router, MediaRouter.ProviderInfo provider) { 277 refreshRoute(router); 278 } 279 280 @Override 281 public void onProviderRemoved(MediaRouter router, MediaRouter.ProviderInfo provider) { 282 refreshRoute(router); 283 } 284 285 @Override 286 public void onProviderChanged(MediaRouter router, MediaRouter.ProviderInfo provider) { 287 refreshRoute(router); 288 } 289 290 private void refreshRoute(MediaRouter router) { 291 MediaRouteActionProvider provider = mProviderWeak.get(); 292 if (provider != null) { 293 provider.refreshRoute(); 294 } else { 295 router.removeCallback(this); 296 } 297 } 298 } 299} 300