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.v4.media.routing; 18 19import android.content.Context; 20import android.graphics.drawable.Drawable; 21import android.os.Build; 22import android.util.Log; 23 24import java.lang.reflect.InvocationTargetException; 25import java.lang.reflect.Method; 26import java.util.ArrayList; 27import java.util.List; 28 29class 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 public static void setRemoteControlClient(Object routeObj, Object rccObj) { 233 ((android.media.MediaRouter.UserRouteInfo)routeObj).setRemoteControlClient( 234 (android.media.RemoteControlClient)rccObj); 235 } 236 } 237 238 public static final class RouteCategory { 239 public static CharSequence getName(Object categoryObj, Context context) { 240 return ((android.media.MediaRouter.RouteCategory)categoryObj).getName(context); 241 } 242 243 @SuppressWarnings({ "rawtypes", "unchecked" }) 244 public static List getRoutes(Object categoryObj) { 245 ArrayList out = new ArrayList(); 246 ((android.media.MediaRouter.RouteCategory)categoryObj).getRoutes(out); 247 return out; 248 } 249 250 public static int getSupportedTypes(Object categoryObj) { 251 return ((android.media.MediaRouter.RouteCategory)categoryObj).getSupportedTypes(); 252 } 253 254 public static boolean isGroupable(Object categoryObj) { 255 return ((android.media.MediaRouter.RouteCategory)categoryObj).isGroupable(); 256 } 257 } 258 259 public static interface Callback { 260 public void onRouteSelected(int type, Object routeObj); 261 public void onRouteUnselected(int type, Object routeObj); 262 public void onRouteAdded(Object routeObj); 263 public void onRouteRemoved(Object routeObj); 264 public void onRouteChanged(Object routeObj); 265 public void onRouteGrouped(Object routeObj, Object groupObj, int index); 266 public void onRouteUngrouped(Object routeObj, Object groupObj); 267 public void onRouteVolumeChanged(Object routeObj); 268 } 269 270 public static interface VolumeCallback { 271 public void onVolumeSetRequest(Object routeObj, int volume); 272 public void onVolumeUpdateRequest(Object routeObj, int direction); 273 } 274 275 /** 276 * Workaround for limitations of selectRoute() on JB and JB MR1. 277 * Do not use on JB MR2 and above. 278 */ 279 public static final class SelectRouteWorkaround { 280 private Method mSelectRouteIntMethod; 281 282 public SelectRouteWorkaround() { 283 if (Build.VERSION.SDK_INT < 16 || Build.VERSION.SDK_INT > 17) { 284 throw new UnsupportedOperationException(); 285 } 286 try { 287 mSelectRouteIntMethod = android.media.MediaRouter.class.getMethod( 288 "selectRouteInt", int.class, android.media.MediaRouter.RouteInfo.class); 289 } catch (NoSuchMethodException ex) { 290 } 291 } 292 293 public void selectRoute(Object routerObj, int types, Object routeObj) { 294 android.media.MediaRouter router = (android.media.MediaRouter)routerObj; 295 android.media.MediaRouter.RouteInfo route = 296 (android.media.MediaRouter.RouteInfo)routeObj; 297 298 int routeTypes = route.getSupportedTypes(); 299 if ((routeTypes & ROUTE_TYPE_USER) == 0) { 300 // Handle non-user routes. 301 // On JB and JB MR1, the selectRoute() API only supports programmatically 302 // selecting user routes. So instead we rely on the hidden selectRouteInt() 303 // method on these versions of the platform. 304 // This limitation was removed in JB MR2. 305 if (mSelectRouteIntMethod != null) { 306 try { 307 mSelectRouteIntMethod.invoke(router, types, route); 308 return; // success! 309 } catch (IllegalAccessException ex) { 310 Log.w(TAG, "Cannot programmatically select non-user route. " 311 + "Media routing may not work.", ex); 312 } catch (InvocationTargetException ex) { 313 Log.w(TAG, "Cannot programmatically select non-user route. " 314 + "Media routing may not work.", ex); 315 } 316 } else { 317 Log.w(TAG, "Cannot programmatically select non-user route " 318 + "because the platform is missing the selectRouteInt() " 319 + "method. Media routing may not work."); 320 } 321 } 322 323 // Default handling. 324 router.selectRoute(types, route); 325 } 326 } 327 328 /** 329 * Workaround the fact that the getDefaultRoute() method does not exist in JB and JB MR1. 330 * Do not use on JB MR2 and above. 331 */ 332 public static final class GetDefaultRouteWorkaround { 333 private Method mGetSystemAudioRouteMethod; 334 335 public GetDefaultRouteWorkaround() { 336 if (Build.VERSION.SDK_INT < 16 || Build.VERSION.SDK_INT > 17) { 337 throw new UnsupportedOperationException(); 338 } 339 try { 340 mGetSystemAudioRouteMethod = 341 android.media.MediaRouter.class.getMethod("getSystemAudioRoute"); 342 } catch (NoSuchMethodException ex) { 343 } 344 } 345 346 public Object getDefaultRoute(Object routerObj) { 347 android.media.MediaRouter router = (android.media.MediaRouter)routerObj; 348 349 if (mGetSystemAudioRouteMethod != null) { 350 try { 351 return mGetSystemAudioRouteMethod.invoke(router); 352 } catch (IllegalAccessException ex) { 353 } catch (InvocationTargetException ex) { 354 } 355 } 356 357 // Could not find the method or it does not work. 358 // Return the first route and hope for the best. 359 return router.getRouteAt(0); 360 } 361 } 362 363 static class CallbackProxy<T extends Callback> 364 extends android.media.MediaRouter.Callback { 365 protected final T mCallback; 366 367 public CallbackProxy(T callback) { 368 mCallback = callback; 369 } 370 371 @Override 372 public void onRouteSelected(android.media.MediaRouter router, 373 int type, android.media.MediaRouter.RouteInfo route) { 374 mCallback.onRouteSelected(type, route); 375 } 376 377 @Override 378 public void onRouteUnselected(android.media.MediaRouter router, 379 int type, android.media.MediaRouter.RouteInfo route) { 380 mCallback.onRouteUnselected(type, route); 381 } 382 383 @Override 384 public void onRouteAdded(android.media.MediaRouter router, 385 android.media.MediaRouter.RouteInfo route) { 386 mCallback.onRouteAdded(route); 387 } 388 389 @Override 390 public void onRouteRemoved(android.media.MediaRouter router, 391 android.media.MediaRouter.RouteInfo route) { 392 mCallback.onRouteRemoved(route); 393 } 394 395 @Override 396 public void onRouteChanged(android.media.MediaRouter router, 397 android.media.MediaRouter.RouteInfo route) { 398 mCallback.onRouteChanged(route); 399 } 400 401 @Override 402 public void onRouteGrouped(android.media.MediaRouter router, 403 android.media.MediaRouter.RouteInfo route, 404 android.media.MediaRouter.RouteGroup group, int index) { 405 mCallback.onRouteGrouped(route, group, index); 406 } 407 408 @Override 409 public void onRouteUngrouped(android.media.MediaRouter router, 410 android.media.MediaRouter.RouteInfo route, 411 android.media.MediaRouter.RouteGroup group) { 412 mCallback.onRouteUngrouped(route, group); 413 } 414 415 @Override 416 public void onRouteVolumeChanged(android.media.MediaRouter router, 417 android.media.MediaRouter.RouteInfo route) { 418 mCallback.onRouteVolumeChanged(route); 419 } 420 } 421 422 static class VolumeCallbackProxy<T extends VolumeCallback> 423 extends android.media.MediaRouter.VolumeCallback { 424 protected final T mCallback; 425 426 public VolumeCallbackProxy(T callback) { 427 mCallback = callback; 428 } 429 430 @Override 431 public void onVolumeSetRequest(android.media.MediaRouter.RouteInfo route, 432 int volume) { 433 mCallback.onVolumeSetRequest(route, volume); 434 } 435 436 @Override 437 public void onVolumeUpdateRequest(android.media.MediaRouter.RouteInfo route, 438 int direction) { 439 mCallback.onVolumeUpdateRequest(route, direction); 440 } 441 } 442} 443