blob: 0bb59b8949a609be684e4f6974eda7eea83069b5 [file] [log] [blame]
/*
* Copyright 2018 The Android Open Source Project
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package com.android.support.mediarouter.media;
import android.content.Context;
import android.graphics.drawable.Drawable;
import android.media.AudioManager;
import android.os.Build;
import android.util.Log;
import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Method;
import java.util.ArrayList;
import java.util.List;
// @@RequiresApi(16)
final class MediaRouterJellybean {
private static final String TAG = "MediaRouterJellybean";
// android.media.AudioSystem.DEVICE_OUT_BLUETOOTH_A2DP = 0x80;
// android.media.AudioSystem.DEVICE_OUT_BLUETOOTH_A2DP_HEADPHONES = 0x100;
// android.media.AudioSystem.DEVICE_OUT_BLUETOOTH_A2DP_SPEAKER = 0x200;
public static final int DEVICE_OUT_BLUETOOTH = 0x80 | 0x100 | 0x200;
public static final int ROUTE_TYPE_LIVE_AUDIO = 0x1;
public static final int ROUTE_TYPE_LIVE_VIDEO = 0x2;
public static final int ROUTE_TYPE_USER = 0x00800000;
public static final int ALL_ROUTE_TYPES =
MediaRouterJellybean.ROUTE_TYPE_LIVE_AUDIO
| MediaRouterJellybean.ROUTE_TYPE_LIVE_VIDEO
| MediaRouterJellybean.ROUTE_TYPE_USER;
public static Object getMediaRouter(Context context) {
return context.getSystemService(Context.MEDIA_ROUTER_SERVICE);
}
@SuppressWarnings({ "rawtypes", "unchecked" })
public static List getRoutes(Object routerObj) {
final android.media.MediaRouter router = (android.media.MediaRouter)routerObj;
final int count = router.getRouteCount();
List out = new ArrayList(count);
for (int i = 0; i < count; i++) {
out.add(router.getRouteAt(i));
}
return out;
}
@SuppressWarnings({ "rawtypes", "unchecked" })
public static List getCategories(Object routerObj) {
final android.media.MediaRouter router = (android.media.MediaRouter)routerObj;
final int count = router.getCategoryCount();
List out = new ArrayList(count);
for (int i = 0; i < count; i++) {
out.add(router.getCategoryAt(i));
}
return out;
}
public static Object getSelectedRoute(Object routerObj, int type) {
return ((android.media.MediaRouter)routerObj).getSelectedRoute(type);
}
public static void selectRoute(Object routerObj, int types, Object routeObj) {
((android.media.MediaRouter)routerObj).selectRoute(types,
(android.media.MediaRouter.RouteInfo)routeObj);
}
public static void addCallback(Object routerObj, int types, Object callbackObj) {
((android.media.MediaRouter)routerObj).addCallback(types,
(android.media.MediaRouter.Callback)callbackObj);
}
public static void removeCallback(Object routerObj, Object callbackObj) {
((android.media.MediaRouter)routerObj).removeCallback(
(android.media.MediaRouter.Callback)callbackObj);
}
public static Object createRouteCategory(Object routerObj,
String name, boolean isGroupable) {
return ((android.media.MediaRouter)routerObj).createRouteCategory(name, isGroupable);
}
public static Object createUserRoute(Object routerObj, Object categoryObj) {
return ((android.media.MediaRouter)routerObj).createUserRoute(
(android.media.MediaRouter.RouteCategory)categoryObj);
}
public static void addUserRoute(Object routerObj, Object routeObj) {
((android.media.MediaRouter)routerObj).addUserRoute(
(android.media.MediaRouter.UserRouteInfo)routeObj);
}
public static void removeUserRoute(Object routerObj, Object routeObj) {
((android.media.MediaRouter)routerObj).removeUserRoute(
(android.media.MediaRouter.UserRouteInfo)routeObj);
}
public static Object createCallback(Callback callback) {
return new CallbackProxy<Callback>(callback);
}
public static Object createVolumeCallback(VolumeCallback callback) {
return new VolumeCallbackProxy<VolumeCallback>(callback);
}
static boolean checkRoutedToBluetooth(Context context) {
try {
AudioManager audioManager = (AudioManager) context.getSystemService(
Context.AUDIO_SERVICE);
Method method = audioManager.getClass().getDeclaredMethod(
"getDevicesForStream", int.class);
int device = (Integer) method.invoke(audioManager, AudioManager.STREAM_MUSIC);
return (device & DEVICE_OUT_BLUETOOTH) != 0;
} catch (Exception e) {
return false;
}
}
public static final class RouteInfo {
public static CharSequence getName(Object routeObj, Context context) {
return ((android.media.MediaRouter.RouteInfo)routeObj).getName(context);
}
public static CharSequence getStatus(Object routeObj) {
return ((android.media.MediaRouter.RouteInfo)routeObj).getStatus();
}
public static int getSupportedTypes(Object routeObj) {
return ((android.media.MediaRouter.RouteInfo)routeObj).getSupportedTypes();
}
public static Object getCategory(Object routeObj) {
return ((android.media.MediaRouter.RouteInfo)routeObj).getCategory();
}
public static Drawable getIconDrawable(Object routeObj) {
return ((android.media.MediaRouter.RouteInfo)routeObj).getIconDrawable();
}
public static int getPlaybackType(Object routeObj) {
return ((android.media.MediaRouter.RouteInfo)routeObj).getPlaybackType();
}
public static int getPlaybackStream(Object routeObj) {
return ((android.media.MediaRouter.RouteInfo)routeObj).getPlaybackStream();
}
public static int getVolume(Object routeObj) {
return ((android.media.MediaRouter.RouteInfo)routeObj).getVolume();
}
public static int getVolumeMax(Object routeObj) {
return ((android.media.MediaRouter.RouteInfo)routeObj).getVolumeMax();
}
public static int getVolumeHandling(Object routeObj) {
return ((android.media.MediaRouter.RouteInfo)routeObj).getVolumeHandling();
}
public static Object getTag(Object routeObj) {
return ((android.media.MediaRouter.RouteInfo)routeObj).getTag();
}
public static void setTag(Object routeObj, Object tag) {
((android.media.MediaRouter.RouteInfo)routeObj).setTag(tag);
}
public static void requestSetVolume(Object routeObj, int volume) {
((android.media.MediaRouter.RouteInfo)routeObj).requestSetVolume(volume);
}
public static void requestUpdateVolume(Object routeObj, int direction) {
((android.media.MediaRouter.RouteInfo)routeObj).requestUpdateVolume(direction);
}
public static Object getGroup(Object routeObj) {
return ((android.media.MediaRouter.RouteInfo)routeObj).getGroup();
}
public static boolean isGroup(Object routeObj) {
return routeObj instanceof android.media.MediaRouter.RouteGroup;
}
}
public static final class RouteGroup {
@SuppressWarnings({ "rawtypes", "unchecked" })
public static List getGroupedRoutes(Object groupObj) {
final android.media.MediaRouter.RouteGroup group =
(android.media.MediaRouter.RouteGroup)groupObj;
final int count = group.getRouteCount();
List out = new ArrayList(count);
for (int i = 0; i < count; i++) {
out.add(group.getRouteAt(i));
}
return out;
}
}
public static final class UserRouteInfo {
public static void setName(Object routeObj, CharSequence name) {
((android.media.MediaRouter.UserRouteInfo)routeObj).setName(name);
}
public static void setStatus(Object routeObj, CharSequence status) {
((android.media.MediaRouter.UserRouteInfo)routeObj).setStatus(status);
}
public static void setIconDrawable(Object routeObj, Drawable icon) {
((android.media.MediaRouter.UserRouteInfo)routeObj).setIconDrawable(icon);
}
public static void setPlaybackType(Object routeObj, int type) {
((android.media.MediaRouter.UserRouteInfo)routeObj).setPlaybackType(type);
}
public static void setPlaybackStream(Object routeObj, int stream) {
((android.media.MediaRouter.UserRouteInfo)routeObj).setPlaybackStream(stream);
}
public static void setVolume(Object routeObj, int volume) {
((android.media.MediaRouter.UserRouteInfo)routeObj).setVolume(volume);
}
public static void setVolumeMax(Object routeObj, int volumeMax) {
((android.media.MediaRouter.UserRouteInfo)routeObj).setVolumeMax(volumeMax);
}
public static void setVolumeHandling(Object routeObj, int volumeHandling) {
((android.media.MediaRouter.UserRouteInfo)routeObj).setVolumeHandling(volumeHandling);
}
public static void setVolumeCallback(Object routeObj, Object volumeCallbackObj) {
((android.media.MediaRouter.UserRouteInfo)routeObj).setVolumeCallback(
(android.media.MediaRouter.VolumeCallback)volumeCallbackObj);
}
public static void setRemoteControlClient(Object routeObj, Object rccObj) {
((android.media.MediaRouter.UserRouteInfo)routeObj).setRemoteControlClient(
(android.media.RemoteControlClient)rccObj);
}
}
public static final class RouteCategory {
public static CharSequence getName(Object categoryObj, Context context) {
return ((android.media.MediaRouter.RouteCategory)categoryObj).getName(context);
}
@SuppressWarnings({ "rawtypes", "unchecked" })
public static List getRoutes(Object categoryObj) {
ArrayList out = new ArrayList();
((android.media.MediaRouter.RouteCategory)categoryObj).getRoutes(out);
return out;
}
public static int getSupportedTypes(Object categoryObj) {
return ((android.media.MediaRouter.RouteCategory)categoryObj).getSupportedTypes();
}
public static boolean isGroupable(Object categoryObj) {
return ((android.media.MediaRouter.RouteCategory)categoryObj).isGroupable();
}
}
public static interface Callback {
public void onRouteSelected(int type, Object routeObj);
public void onRouteUnselected(int type, Object routeObj);
public void onRouteAdded(Object routeObj);
public void onRouteRemoved(Object routeObj);
public void onRouteChanged(Object routeObj);
public void onRouteGrouped(Object routeObj, Object groupObj, int index);
public void onRouteUngrouped(Object routeObj, Object groupObj);
public void onRouteVolumeChanged(Object routeObj);
}
public static interface VolumeCallback {
public void onVolumeSetRequest(Object routeObj, int volume);
public void onVolumeUpdateRequest(Object routeObj, int direction);
}
/**
* Workaround for limitations of selectRoute() on JB and JB MR1.
* Do not use on JB MR2 and above.
*/
public static final class SelectRouteWorkaround {
private Method mSelectRouteIntMethod;
public SelectRouteWorkaround() {
if (Build.VERSION.SDK_INT < 16 || Build.VERSION.SDK_INT > 17) {
throw new UnsupportedOperationException();
}
try {
mSelectRouteIntMethod = android.media.MediaRouter.class.getMethod(
"selectRouteInt", int.class, android.media.MediaRouter.RouteInfo.class);
} catch (NoSuchMethodException ex) {
}
}
public void selectRoute(Object routerObj, int types, Object routeObj) {
android.media.MediaRouter router = (android.media.MediaRouter)routerObj;
android.media.MediaRouter.RouteInfo route =
(android.media.MediaRouter.RouteInfo)routeObj;
int routeTypes = route.getSupportedTypes();
if ((routeTypes & ROUTE_TYPE_USER) == 0) {
// Handle non-user routes.
// On JB and JB MR1, the selectRoute() API only supports programmatically
// selecting user routes. So instead we rely on the hidden selectRouteInt()
// method on these versions of the platform.
// This limitation was removed in JB MR2.
if (mSelectRouteIntMethod != null) {
try {
mSelectRouteIntMethod.invoke(router, types, route);
return; // success!
} catch (IllegalAccessException ex) {
Log.w(TAG, "Cannot programmatically select non-user route. "
+ "Media routing may not work.", ex);
} catch (InvocationTargetException ex) {
Log.w(TAG, "Cannot programmatically select non-user route. "
+ "Media routing may not work.", ex);
}
} else {
Log.w(TAG, "Cannot programmatically select non-user route "
+ "because the platform is missing the selectRouteInt() "
+ "method. Media routing may not work.");
}
}
// Default handling.
router.selectRoute(types, route);
}
}
/**
* Workaround the fact that the getDefaultRoute() method does not exist in JB and JB MR1.
* Do not use on JB MR2 and above.
*/
public static final class GetDefaultRouteWorkaround {
private Method mGetSystemAudioRouteMethod;
public GetDefaultRouteWorkaround() {
if (Build.VERSION.SDK_INT < 16 || Build.VERSION.SDK_INT > 17) {
throw new UnsupportedOperationException();
}
try {
mGetSystemAudioRouteMethod =
android.media.MediaRouter.class.getMethod("getSystemAudioRoute");
} catch (NoSuchMethodException ex) {
}
}
public Object getDefaultRoute(Object routerObj) {
android.media.MediaRouter router = (android.media.MediaRouter)routerObj;
if (mGetSystemAudioRouteMethod != null) {
try {
return mGetSystemAudioRouteMethod.invoke(router);
} catch (IllegalAccessException ex) {
} catch (InvocationTargetException ex) {
}
}
// Could not find the method or it does not work.
// Return the first route and hope for the best.
return router.getRouteAt(0);
}
}
static class CallbackProxy<T extends Callback>
extends android.media.MediaRouter.Callback {
protected final T mCallback;
public CallbackProxy(T callback) {
mCallback = callback;
}
@Override
public void onRouteSelected(android.media.MediaRouter router,
int type, android.media.MediaRouter.RouteInfo route) {
mCallback.onRouteSelected(type, route);
}
@Override
public void onRouteUnselected(android.media.MediaRouter router,
int type, android.media.MediaRouter.RouteInfo route) {
mCallback.onRouteUnselected(type, route);
}
@Override
public void onRouteAdded(android.media.MediaRouter router,
android.media.MediaRouter.RouteInfo route) {
mCallback.onRouteAdded(route);
}
@Override
public void onRouteRemoved(android.media.MediaRouter router,
android.media.MediaRouter.RouteInfo route) {
mCallback.onRouteRemoved(route);
}
@Override
public void onRouteChanged(android.media.MediaRouter router,
android.media.MediaRouter.RouteInfo route) {
mCallback.onRouteChanged(route);
}
@Override
public void onRouteGrouped(android.media.MediaRouter router,
android.media.MediaRouter.RouteInfo route,
android.media.MediaRouter.RouteGroup group, int index) {
mCallback.onRouteGrouped(route, group, index);
}
@Override
public void onRouteUngrouped(android.media.MediaRouter router,
android.media.MediaRouter.RouteInfo route,
android.media.MediaRouter.RouteGroup group) {
mCallback.onRouteUngrouped(route, group);
}
@Override
public void onRouteVolumeChanged(android.media.MediaRouter router,
android.media.MediaRouter.RouteInfo route) {
mCallback.onRouteVolumeChanged(route);
}
}
static class VolumeCallbackProxy<T extends VolumeCallback>
extends android.media.MediaRouter.VolumeCallback {
protected final T mCallback;
public VolumeCallbackProxy(T callback) {
mCallback = callback;
}
@Override
public void onVolumeSetRequest(android.media.MediaRouter.RouteInfo route,
int volume) {
mCallback.onVolumeSetRequest(route, volume);
}
@Override
public void onVolumeUpdateRequest(android.media.MediaRouter.RouteInfo route,
int direction) {
mCallback.onVolumeUpdateRequest(route, direction);
}
}
}