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