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.hardware.display.DisplayManager; 21 import android.os.Build; 22 import android.os.Handler; 23 import android.util.Log; 24 import android.view.Display; 25 26 import java.lang.reflect.Field; 27 import java.lang.reflect.InvocationTargetException; 28 import java.lang.reflect.Method; 29 30 final class MediaRouterJellybeanMr1 { 31 private static final String TAG = "MediaRouterJellybeanMr1"; 32 createCallback(Callback callback)33 public static Object createCallback(Callback callback) { 34 return new CallbackProxy<Callback>(callback); 35 } 36 37 public static final class RouteInfo { isEnabled(Object routeObj)38 public static boolean isEnabled(Object routeObj) { 39 return ((android.media.MediaRouter.RouteInfo)routeObj).isEnabled(); 40 } 41 getPresentationDisplay(Object routeObj)42 public static Display getPresentationDisplay(Object routeObj) { 43 // android.media.MediaRouter.RouteInfo.getPresentationDisplay() was 44 // added in API 17. However, some factory releases of JB MR1 missed it. 45 try { 46 return ((android.media.MediaRouter.RouteInfo)routeObj).getPresentationDisplay(); 47 } catch (NoSuchMethodError ex) { 48 Log.w(TAG, "Cannot get presentation display for the route.", ex); 49 } 50 return null; 51 } 52 } 53 54 public static interface Callback extends MediaRouterJellybean.Callback { onRoutePresentationDisplayChanged(Object routeObj)55 public void onRoutePresentationDisplayChanged(Object routeObj); 56 } 57 58 /** 59 * Workaround the fact that the version of MediaRouter.addCallback() that accepts a 60 * flag to perform an active scan does not exist in JB MR1 so we need to force 61 * wifi display scans directly through the DisplayManager. 62 * Do not use on JB MR2 and above. 63 */ 64 public static final class ActiveScanWorkaround implements Runnable { 65 // Time between wifi display scans when actively scanning in milliseconds. 66 private static final int WIFI_DISPLAY_SCAN_INTERVAL = 15000; 67 68 private final DisplayManager mDisplayManager; 69 private final Handler mHandler; 70 private Method mScanWifiDisplaysMethod; 71 72 private boolean mActivelyScanningWifiDisplays; 73 ActiveScanWorkaround(Context context, Handler handler)74 public ActiveScanWorkaround(Context context, Handler handler) { 75 if (Build.VERSION.SDK_INT != 17) { 76 throw new UnsupportedOperationException(); 77 } 78 79 mDisplayManager = (DisplayManager) context.getSystemService(Context.DISPLAY_SERVICE); 80 mHandler = handler; 81 try { 82 mScanWifiDisplaysMethod = DisplayManager.class.getMethod("scanWifiDisplays"); 83 } catch (NoSuchMethodException ex) { 84 } 85 } 86 setActiveScanRouteTypes(int routeTypes)87 public void setActiveScanRouteTypes(int routeTypes) { 88 // On JB MR1, there is no API to scan wifi display routes. 89 // Instead we must make a direct call into the DisplayManager to scan 90 // wifi displays on this version but only when live video routes are requested. 91 // See also the JellybeanMr2Impl implementation of this method. 92 // This was fixed in JB MR2 by adding a new overload of addCallback() to 93 // enable active scanning on request. 94 if ((routeTypes & MediaRouterJellybean.ROUTE_TYPE_LIVE_VIDEO) != 0) { 95 if (!mActivelyScanningWifiDisplays) { 96 if (mScanWifiDisplaysMethod != null) { 97 mActivelyScanningWifiDisplays = true; 98 mHandler.post(this); 99 } else { 100 Log.w(TAG, "Cannot scan for wifi displays because the " 101 + "DisplayManager.scanWifiDisplays() method is " 102 + "not available on this device."); 103 } 104 } 105 } else { 106 if (mActivelyScanningWifiDisplays) { 107 mActivelyScanningWifiDisplays = false; 108 mHandler.removeCallbacks(this); 109 } 110 } 111 } 112 113 @Override run()114 public void run() { 115 if (mActivelyScanningWifiDisplays) { 116 try { 117 mScanWifiDisplaysMethod.invoke(mDisplayManager); 118 } catch (IllegalAccessException ex) { 119 Log.w(TAG, "Cannot scan for wifi displays.", ex); 120 } catch (InvocationTargetException ex) { 121 Log.w(TAG, "Cannot scan for wifi displays.", ex); 122 } 123 mHandler.postDelayed(this, WIFI_DISPLAY_SCAN_INTERVAL); 124 } 125 } 126 } 127 128 /** 129 * Workaround the fact that the isConnecting() method does not exist in JB MR1. 130 * Do not use on JB MR2 and above. 131 */ 132 public static final class IsConnectingWorkaround { 133 private Method mGetStatusCodeMethod; 134 private int mStatusConnecting; 135 IsConnectingWorkaround()136 public IsConnectingWorkaround() { 137 if (Build.VERSION.SDK_INT != 17) { 138 throw new UnsupportedOperationException(); 139 } 140 141 try { 142 Field statusConnectingField = 143 android.media.MediaRouter.RouteInfo.class.getField("STATUS_CONNECTING"); 144 mStatusConnecting = statusConnectingField.getInt(null); 145 mGetStatusCodeMethod = 146 android.media.MediaRouter.RouteInfo.class.getMethod("getStatusCode"); 147 } catch (NoSuchFieldException ex) { 148 } catch (NoSuchMethodException ex) { 149 } catch (IllegalAccessException ex) { 150 } 151 } 152 isConnecting(Object routeObj)153 public boolean isConnecting(Object routeObj) { 154 android.media.MediaRouter.RouteInfo route = 155 (android.media.MediaRouter.RouteInfo)routeObj; 156 157 if (mGetStatusCodeMethod != null) { 158 try { 159 int statusCode = (Integer)mGetStatusCodeMethod.invoke(route); 160 return statusCode == mStatusConnecting; 161 } catch (IllegalAccessException ex) { 162 } catch (InvocationTargetException ex) { 163 } 164 } 165 166 // Assume not connecting. 167 return false; 168 } 169 } 170 171 static class CallbackProxy<T extends Callback> 172 extends MediaRouterJellybean.CallbackProxy<T> { CallbackProxy(T callback)173 public CallbackProxy(T callback) { 174 super(callback); 175 } 176 177 @Override onRoutePresentationDisplayChanged(android.media.MediaRouter router, android.media.MediaRouter.RouteInfo route)178 public void onRoutePresentationDisplayChanged(android.media.MediaRouter router, 179 android.media.MediaRouter.RouteInfo route) { 180 mCallback.onRoutePresentationDisplayChanged(route); 181 } 182 } 183 } 184