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