Home | History | Annotate | Download | only in media
      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