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 17 package android.support.v7.media; 18 19 import android.content.Context; 20 import android.graphics.drawable.Drawable; 21 import android.os.Build; 22 import android.util.Log; 23 24 import java.lang.reflect.InvocationTargetException; 25 import java.lang.reflect.Method; 26 import java.util.ArrayList; 27 import java.util.List; 28 29 final class MediaRouterJellybean { 30 private static final String TAG = "MediaRouterJellybean"; 31 32 public static final int ROUTE_TYPE_LIVE_AUDIO = 0x1; 33 public static final int ROUTE_TYPE_LIVE_VIDEO = 0x2; 34 public static final int ROUTE_TYPE_USER = 0x00800000; 35 36 public static final int ALL_ROUTE_TYPES = 37 MediaRouterJellybean.ROUTE_TYPE_LIVE_AUDIO 38 | MediaRouterJellybean.ROUTE_TYPE_LIVE_VIDEO 39 | MediaRouterJellybean.ROUTE_TYPE_USER; 40 41 public static Object getMediaRouter(Context context) { 42 return context.getSystemService(Context.MEDIA_ROUTER_SERVICE); 43 } 44 45 @SuppressWarnings({ "rawtypes", "unchecked" }) 46 public static List getRoutes(Object routerObj) { 47 final android.media.MediaRouter router = (android.media.MediaRouter)routerObj; 48 final int count = router.getRouteCount(); 49 List out = new ArrayList(count); 50 for (int i = 0; i < count; i++) { 51 out.add(router.getRouteAt(i)); 52 } 53 return out; 54 } 55 56 @SuppressWarnings({ "rawtypes", "unchecked" }) 57 public static List getCategories(Object routerObj) { 58 final android.media.MediaRouter router = (android.media.MediaRouter)routerObj; 59 final int count = router.getCategoryCount(); 60 List out = new ArrayList(count); 61 for (int i = 0; i < count; i++) { 62 out.add(router.getCategoryAt(i)); 63 } 64 return out; 65 } 66 67 public static Object getSelectedRoute(Object routerObj, int type) { 68 return ((android.media.MediaRouter)routerObj).getSelectedRoute(type); 69 } 70 71 public static void selectRoute(Object routerObj, int types, Object routeObj) { 72 ((android.media.MediaRouter)routerObj).selectRoute(types, 73 (android.media.MediaRouter.RouteInfo)routeObj); 74 } 75 76 public static void addCallback(Object routerObj, int types, Object callbackObj) { 77 ((android.media.MediaRouter)routerObj).addCallback(types, 78 (android.media.MediaRouter.Callback)callbackObj); 79 } 80 81 public static void removeCallback(Object routerObj, Object callbackObj) { 82 ((android.media.MediaRouter)routerObj).removeCallback( 83 (android.media.MediaRouter.Callback)callbackObj); 84 } 85 86 public static Object createRouteCategory(Object routerObj, 87 String name, boolean isGroupable) { 88 return ((android.media.MediaRouter)routerObj).createRouteCategory(name, isGroupable); 89 } 90 91 public static Object createUserRoute(Object routerObj, Object categoryObj) { 92 return ((android.media.MediaRouter)routerObj).createUserRoute( 93 (android.media.MediaRouter.RouteCategory)categoryObj); 94 } 95 96 public static void addUserRoute(Object routerObj, Object routeObj) { 97 ((android.media.MediaRouter)routerObj).addUserRoute( 98 (android.media.MediaRouter.UserRouteInfo)routeObj); 99 } 100 101 public static void removeUserRoute(Object routerObj, Object routeObj) { 102 ((android.media.MediaRouter)routerObj).removeUserRoute( 103 (android.media.MediaRouter.UserRouteInfo)routeObj); 104 } 105 106 public static Object createCallback(Callback callback) { 107 return new CallbackProxy<Callback>(callback); 108 } 109 110 public static Object createVolumeCallback(VolumeCallback callback) { 111 return new VolumeCallbackProxy<VolumeCallback>(callback); 112 } 113 114 public static final class RouteInfo { 115 public static CharSequence getName(Object routeObj, Context context) { 116 return ((android.media.MediaRouter.RouteInfo)routeObj).getName(context); 117 } 118 119 public static CharSequence getStatus(Object routeObj) { 120 return ((android.media.MediaRouter.RouteInfo)routeObj).getStatus(); 121 } 122 123 public static int getSupportedTypes(Object routeObj) { 124 return ((android.media.MediaRouter.RouteInfo)routeObj).getSupportedTypes(); 125 } 126 127 public static Object getCategory(Object routeObj) { 128 return ((android.media.MediaRouter.RouteInfo)routeObj).getCategory(); 129 } 130 131 public static Drawable getIconDrawable(Object routeObj) { 132 return ((android.media.MediaRouter.RouteInfo)routeObj).getIconDrawable(); 133 } 134 135 public static int getPlaybackType(Object routeObj) { 136 return ((android.media.MediaRouter.RouteInfo)routeObj).getPlaybackType(); 137 } 138 139 public static int getPlaybackStream(Object routeObj) { 140 return ((android.media.MediaRouter.RouteInfo)routeObj).getPlaybackStream(); 141 } 142 143 public static int getVolume(Object routeObj) { 144 return ((android.media.MediaRouter.RouteInfo)routeObj).getVolume(); 145 } 146 147 public static int getVolumeMax(Object routeObj) { 148 return ((android.media.MediaRouter.RouteInfo)routeObj).getVolumeMax(); 149 } 150 151 public static int getVolumeHandling(Object routeObj) { 152 return ((android.media.MediaRouter.RouteInfo)routeObj).getVolumeHandling(); 153 } 154 155 public static Object getTag(Object routeObj) { 156 return ((android.media.MediaRouter.RouteInfo)routeObj).getTag(); 157 } 158 159 public static void setTag(Object routeObj, Object tag) { 160 ((android.media.MediaRouter.RouteInfo)routeObj).setTag(tag); 161 } 162 163 public static void requestSetVolume(Object routeObj, int volume) { 164 ((android.media.MediaRouter.RouteInfo)routeObj).requestSetVolume(volume); 165 } 166 167 public static void requestUpdateVolume(Object routeObj, int direction) { 168 ((android.media.MediaRouter.RouteInfo)routeObj).requestUpdateVolume(direction); 169 } 170 171 public static Object getGroup(Object routeObj) { 172 return ((android.media.MediaRouter.RouteInfo)routeObj).getGroup(); 173 } 174 175 public static boolean isGroup(Object routeObj) { 176 return routeObj instanceof android.media.MediaRouter.RouteGroup; 177 } 178 } 179 180 public static final class RouteGroup { 181 @SuppressWarnings({ "rawtypes", "unchecked" }) 182 public static List getGroupedRoutes(Object groupObj) { 183 final android.media.MediaRouter.RouteGroup group = 184 (android.media.MediaRouter.RouteGroup)groupObj; 185 final int count = group.getRouteCount(); 186 List out = new ArrayList(count); 187 for (int i = 0; i < count; i++) { 188 out.add(group.getRouteAt(i)); 189 } 190 return out; 191 } 192 } 193 194 public static final class UserRouteInfo { 195 public static void setName(Object routeObj, CharSequence name) { 196 ((android.media.MediaRouter.UserRouteInfo)routeObj).setName(name); 197 } 198 199 public static void setStatus(Object routeObj, CharSequence status) { 200 ((android.media.MediaRouter.UserRouteInfo)routeObj).setStatus(status); 201 } 202 203 public static void setIconDrawable(Object routeObj, Drawable icon) { 204 ((android.media.MediaRouter.UserRouteInfo)routeObj).setIconDrawable(icon); 205 } 206 207 public static void setPlaybackType(Object routeObj, int type) { 208 ((android.media.MediaRouter.UserRouteInfo)routeObj).setPlaybackType(type); 209 } 210 211 public static void setPlaybackStream(Object routeObj, int stream) { 212 ((android.media.MediaRouter.UserRouteInfo)routeObj).setPlaybackStream(stream); 213 } 214 215 public static void setVolume(Object routeObj, int volume) { 216 ((android.media.MediaRouter.UserRouteInfo)routeObj).setVolume(volume); 217 } 218 219 public static void setVolumeMax(Object routeObj, int volumeMax) { 220 ((android.media.MediaRouter.UserRouteInfo)routeObj).setVolumeMax(volumeMax); 221 } 222 223 public static void setVolumeHandling(Object routeObj, int volumeHandling) { 224 ((android.media.MediaRouter.UserRouteInfo)routeObj).setVolumeHandling(volumeHandling); 225 } 226 227 public static void setVolumeCallback(Object routeObj, Object volumeCallbackObj) { 228 ((android.media.MediaRouter.UserRouteInfo)routeObj).setVolumeCallback( 229 ((android.media.MediaRouter.VolumeCallback)volumeCallbackObj)); 230 } 231 } 232 233 public static final class RouteCategory { 234 public static CharSequence getName(Object categoryObj, Context context) { 235 return ((android.media.MediaRouter.RouteCategory)categoryObj).getName(context); 236 } 237 238 @SuppressWarnings({ "rawtypes", "unchecked" }) 239 public static List getRoutes(Object categoryObj) { 240 ArrayList out = new ArrayList(); 241 ((android.media.MediaRouter.RouteCategory)categoryObj).getRoutes(out); 242 return out; 243 } 244 245 public static int getSupportedTypes(Object categoryObj) { 246 return ((android.media.MediaRouter.RouteCategory)categoryObj).getSupportedTypes(); 247 } 248 249 public static boolean isGroupable(Object categoryObj) { 250 return ((android.media.MediaRouter.RouteCategory)categoryObj).isGroupable(); 251 } 252 } 253 254 public static interface Callback { 255 public void onRouteSelected(int type, Object routeObj); 256 public void onRouteUnselected(int type, Object routeObj); 257 public void onRouteAdded(Object routeObj); 258 public void onRouteRemoved(Object routeObj); 259 public void onRouteChanged(Object routeObj); 260 public void onRouteGrouped(Object routeObj, Object groupObj, int index); 261 public void onRouteUngrouped(Object routeObj, Object groupObj); 262 public void onRouteVolumeChanged(Object routeObj); 263 } 264 265 public static interface VolumeCallback { 266 public void onVolumeSetRequest(Object routeObj, int volume); 267 public void onVolumeUpdateRequest(Object routeObj, int direction); 268 } 269 270 /** 271 * Workaround for limitations of selectRoute() on JB and JB MR1. 272 * Do not use on JB MR2 and above. 273 */ 274 public static final class SelectRouteWorkaround { 275 private Method mSelectRouteIntMethod; 276 277 public SelectRouteWorkaround() { 278 if (Build.VERSION.SDK_INT < 16 || Build.VERSION.SDK_INT > 17) { 279 throw new UnsupportedOperationException(); 280 } 281 try { 282 mSelectRouteIntMethod = android.media.MediaRouter.class.getMethod( 283 "selectRouteInt", int.class, android.media.MediaRouter.RouteInfo.class); 284 } catch (NoSuchMethodException ex) { 285 } 286 } 287 288 public void selectRoute(Object routerObj, int types, Object routeObj) { 289 android.media.MediaRouter router = (android.media.MediaRouter)routerObj; 290 android.media.MediaRouter.RouteInfo route = 291 (android.media.MediaRouter.RouteInfo)routeObj; 292 293 int routeTypes = route.getSupportedTypes(); 294 if ((routeTypes & ROUTE_TYPE_USER) == 0) { 295 // Handle non-user routes. 296 // On JB and JB MR1, the selectRoute() API only supports programmatically 297 // selecting user routes. So instead we rely on the hidden selectRouteInt() 298 // method on these versions of the platform. 299 // This limitation was removed in JB MR2. 300 if (mSelectRouteIntMethod != null) { 301 try { 302 mSelectRouteIntMethod.invoke(router, types, route); 303 return; // success! 304 } catch (IllegalAccessException ex) { 305 Log.w(TAG, "Cannot programmatically select non-user route. " 306 + "Media routing may not work.", ex); 307 } catch (InvocationTargetException ex) { 308 Log.w(TAG, "Cannot programmatically select non-user route. " 309 + "Media routing may not work.", ex); 310 } 311 } else { 312 Log.w(TAG, "Cannot programmatically select non-user route " 313 + "because the platform is missing the selectRouteInt() " 314 + "method. Media routing may not work."); 315 } 316 } 317 318 // Default handling. 319 router.selectRoute(types, route); 320 } 321 } 322 323 /** 324 * Workaround the fact that the getDefaultRoute() method does not exist in JB and JB MR1. 325 * Do not use on JB MR2 and above. 326 */ 327 public static final class GetDefaultRouteWorkaround { 328 private Method mGetSystemAudioRouteMethod; 329 330 public GetDefaultRouteWorkaround() { 331 if (Build.VERSION.SDK_INT < 16 || Build.VERSION.SDK_INT > 17) { 332 throw new UnsupportedOperationException(); 333 } 334 try { 335 mGetSystemAudioRouteMethod = 336 android.media.MediaRouter.class.getMethod("getSystemAudioRoute"); 337 } catch (NoSuchMethodException ex) { 338 } 339 } 340 341 public Object getDefaultRoute(Object routerObj) { 342 android.media.MediaRouter router = (android.media.MediaRouter)routerObj; 343 344 if (mGetSystemAudioRouteMethod != null) { 345 try { 346 return mGetSystemAudioRouteMethod.invoke(router); 347 } catch (IllegalAccessException ex) { 348 } catch (InvocationTargetException ex) { 349 } 350 } 351 352 // Could not find the method or it does not work. 353 // Return the first route and hope for the best. 354 return router.getRouteAt(0); 355 } 356 } 357 358 static class CallbackProxy<T extends Callback> 359 extends android.media.MediaRouter.Callback { 360 protected final T mCallback; 361 362 public CallbackProxy(T callback) { 363 mCallback = callback; 364 } 365 366 @Override 367 public void onRouteSelected(android.media.MediaRouter router, 368 int type, android.media.MediaRouter.RouteInfo route) { 369 mCallback.onRouteSelected(type, route); 370 } 371 372 @Override 373 public void onRouteUnselected(android.media.MediaRouter router, 374 int type, android.media.MediaRouter.RouteInfo route) { 375 mCallback.onRouteUnselected(type, route); 376 } 377 378 @Override 379 public void onRouteAdded(android.media.MediaRouter router, 380 android.media.MediaRouter.RouteInfo route) { 381 mCallback.onRouteAdded(route); 382 } 383 384 @Override 385 public void onRouteRemoved(android.media.MediaRouter router, 386 android.media.MediaRouter.RouteInfo route) { 387 mCallback.onRouteRemoved(route); 388 } 389 390 @Override 391 public void onRouteChanged(android.media.MediaRouter router, 392 android.media.MediaRouter.RouteInfo route) { 393 mCallback.onRouteChanged(route); 394 } 395 396 @Override 397 public void onRouteGrouped(android.media.MediaRouter router, 398 android.media.MediaRouter.RouteInfo route, 399 android.media.MediaRouter.RouteGroup group, int index) { 400 mCallback.onRouteGrouped(route, group, index); 401 } 402 403 @Override 404 public void onRouteUngrouped(android.media.MediaRouter router, 405 android.media.MediaRouter.RouteInfo route, 406 android.media.MediaRouter.RouteGroup group) { 407 mCallback.onRouteUngrouped(route, group); 408 } 409 410 @Override 411 public void onRouteVolumeChanged(android.media.MediaRouter router, 412 android.media.MediaRouter.RouteInfo route) { 413 mCallback.onRouteVolumeChanged(route); 414 } 415 } 416 417 static class VolumeCallbackProxy<T extends VolumeCallback> 418 extends android.media.MediaRouter.VolumeCallback { 419 protected final T mCallback; 420 421 public VolumeCallbackProxy(T callback) { 422 mCallback = callback; 423 } 424 425 @Override 426 public void onVolumeSetRequest(android.media.MediaRouter.RouteInfo route, 427 int volume) { 428 mCallback.onVolumeSetRequest(route, volume); 429 } 430 431 @Override 432 public void onVolumeUpdateRequest(android.media.MediaRouter.RouteInfo route, 433 int direction) { 434 mCallback.onVolumeUpdateRequest(route, direction); 435 } 436 } 437 } 438