1c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown/* 2c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown * Copyright (C) 2013 The Android Open Source Project 3c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown * 4c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown * Licensed under the Apache License, Version 2.0 (the "License"); 5c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown * you may not use this file except in compliance with the License. 6c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown * You may obtain a copy of the License at 7c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown * 8c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown * http://www.apache.org/licenses/LICENSE-2.0 9c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown * 10c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown * Unless required by applicable law or agreed to in writing, software 11c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown * distributed under the License is distributed on an "AS IS" BASIS, 12c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown * See the License for the specific language governing permissions and 14c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown * limitations under the License. 15c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown */ 16c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown 17b507e525a61ed761eecfc2eaaf19af7e8db5dca5Jeff Brownpackage android.support.v7.media; 18c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown 1911417b1cfde8f1749905f2d735623af9214148afJeff Brownimport android.content.Context; 2011417b1cfde8f1749905f2d735623af9214148afJeff Brownimport android.hardware.display.DisplayManager; 2111417b1cfde8f1749905f2d735623af9214148afJeff Brownimport android.os.Build; 2211417b1cfde8f1749905f2d735623af9214148afJeff Brownimport android.os.Handler; 2311417b1cfde8f1749905f2d735623af9214148afJeff Brownimport android.util.Log; 24c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brownimport android.view.Display; 25c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown 2611417b1cfde8f1749905f2d735623af9214148afJeff Brownimport java.lang.reflect.Field; 2711417b1cfde8f1749905f2d735623af9214148afJeff Brownimport java.lang.reflect.InvocationTargetException; 2811417b1cfde8f1749905f2d735623af9214148afJeff Brownimport java.lang.reflect.Method; 2911417b1cfde8f1749905f2d735623af9214148afJeff Brown 30c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brownfinal class MediaRouterJellybeanMr1 { 3111417b1cfde8f1749905f2d735623af9214148afJeff Brown private static final String TAG = "MediaRouterJellybeanMr1"; 3211417b1cfde8f1749905f2d735623af9214148afJeff Brown 33c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown public static Object createCallback(Callback callback) { 34c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown return new CallbackProxy<Callback>(callback); 35c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown } 36c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown 37c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown public static final class RouteInfo { 38c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown public static boolean isEnabled(Object routeObj) { 39c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown return ((android.media.MediaRouter.RouteInfo)routeObj).isEnabled(); 40c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown } 41c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown 42c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown public static Display getPresentationDisplay(Object routeObj) { 43c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown return ((android.media.MediaRouter.RouteInfo)routeObj).getPresentationDisplay(); 44c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown } 45c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown } 46c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown 47c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown public static interface Callback extends MediaRouterJellybean.Callback { 48c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown public void onRoutePresentationDisplayChanged(Object routeObj); 49c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown } 50c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown 5111417b1cfde8f1749905f2d735623af9214148afJeff Brown /** 5211417b1cfde8f1749905f2d735623af9214148afJeff Brown * Workaround the fact that the version of MediaRouter.addCallback() that accepts a 5311417b1cfde8f1749905f2d735623af9214148afJeff Brown * flag to perform an active scan does not exist in JB MR1 so we need to force 5411417b1cfde8f1749905f2d735623af9214148afJeff Brown * wifi display scans directly through the DisplayManager. 5511417b1cfde8f1749905f2d735623af9214148afJeff Brown * Do not use on JB MR2 and above. 5611417b1cfde8f1749905f2d735623af9214148afJeff Brown */ 5711417b1cfde8f1749905f2d735623af9214148afJeff Brown public static final class ActiveScanWorkaround implements Runnable { 5811417b1cfde8f1749905f2d735623af9214148afJeff Brown // Time between wifi display scans when actively scanning in milliseconds. 5911417b1cfde8f1749905f2d735623af9214148afJeff Brown private static final int WIFI_DISPLAY_SCAN_INTERVAL = 15000; 6011417b1cfde8f1749905f2d735623af9214148afJeff Brown 6111417b1cfde8f1749905f2d735623af9214148afJeff Brown private final DisplayManager mDisplayManager; 6211417b1cfde8f1749905f2d735623af9214148afJeff Brown private final Handler mHandler; 6311417b1cfde8f1749905f2d735623af9214148afJeff Brown private Method mScanWifiDisplaysMethod; 6411417b1cfde8f1749905f2d735623af9214148afJeff Brown 6511417b1cfde8f1749905f2d735623af9214148afJeff Brown private boolean mActivelyScanningWifiDisplays; 6611417b1cfde8f1749905f2d735623af9214148afJeff Brown 6711417b1cfde8f1749905f2d735623af9214148afJeff Brown public ActiveScanWorkaround(Context context, Handler handler) { 6811417b1cfde8f1749905f2d735623af9214148afJeff Brown if (Build.VERSION.SDK_INT != 17) { 6911417b1cfde8f1749905f2d735623af9214148afJeff Brown throw new UnsupportedOperationException(); 7011417b1cfde8f1749905f2d735623af9214148afJeff Brown } 7111417b1cfde8f1749905f2d735623af9214148afJeff Brown 7211417b1cfde8f1749905f2d735623af9214148afJeff Brown mDisplayManager = (DisplayManager) context.getSystemService(Context.DISPLAY_SERVICE); 7311417b1cfde8f1749905f2d735623af9214148afJeff Brown mHandler = handler; 7411417b1cfde8f1749905f2d735623af9214148afJeff Brown try { 7511417b1cfde8f1749905f2d735623af9214148afJeff Brown mScanWifiDisplaysMethod = DisplayManager.class.getMethod("scanWifiDisplays"); 7611417b1cfde8f1749905f2d735623af9214148afJeff Brown } catch (NoSuchMethodException ex) { 7711417b1cfde8f1749905f2d735623af9214148afJeff Brown } 7811417b1cfde8f1749905f2d735623af9214148afJeff Brown } 7911417b1cfde8f1749905f2d735623af9214148afJeff Brown 8011417b1cfde8f1749905f2d735623af9214148afJeff Brown public void setActiveScanRouteTypes(int routeTypes) { 8111417b1cfde8f1749905f2d735623af9214148afJeff Brown // On JB MR1, there is no API to scan wifi display routes. 8211417b1cfde8f1749905f2d735623af9214148afJeff Brown // Instead we must make a direct call into the DisplayManager to scan 8311417b1cfde8f1749905f2d735623af9214148afJeff Brown // wifi displays on this version but only when live video routes are requested. 8411417b1cfde8f1749905f2d735623af9214148afJeff Brown // See also the JellybeanMr2Impl implementation of this method. 8511417b1cfde8f1749905f2d735623af9214148afJeff Brown // This was fixed in JB MR2 by adding a new overload of addCallback() to 8611417b1cfde8f1749905f2d735623af9214148afJeff Brown // enable active scanning on request. 8711417b1cfde8f1749905f2d735623af9214148afJeff Brown if ((routeTypes & MediaRouterJellybean.ROUTE_TYPE_LIVE_VIDEO) != 0) { 8811417b1cfde8f1749905f2d735623af9214148afJeff Brown if (!mActivelyScanningWifiDisplays) { 8911417b1cfde8f1749905f2d735623af9214148afJeff Brown if (mScanWifiDisplaysMethod != null) { 9011417b1cfde8f1749905f2d735623af9214148afJeff Brown mActivelyScanningWifiDisplays = true; 9111417b1cfde8f1749905f2d735623af9214148afJeff Brown mHandler.post(this); 9211417b1cfde8f1749905f2d735623af9214148afJeff Brown } else { 9311417b1cfde8f1749905f2d735623af9214148afJeff Brown Log.w(TAG, "Cannot scan for wifi displays because the " 9411417b1cfde8f1749905f2d735623af9214148afJeff Brown + "DisplayManager.scanWifiDisplays() method is " 9511417b1cfde8f1749905f2d735623af9214148afJeff Brown + "not available on this device."); 9611417b1cfde8f1749905f2d735623af9214148afJeff Brown } 9711417b1cfde8f1749905f2d735623af9214148afJeff Brown } 9811417b1cfde8f1749905f2d735623af9214148afJeff Brown } else { 9911417b1cfde8f1749905f2d735623af9214148afJeff Brown if (mActivelyScanningWifiDisplays) { 10011417b1cfde8f1749905f2d735623af9214148afJeff Brown mActivelyScanningWifiDisplays = false; 10111417b1cfde8f1749905f2d735623af9214148afJeff Brown mHandler.removeCallbacks(this); 10211417b1cfde8f1749905f2d735623af9214148afJeff Brown } 10311417b1cfde8f1749905f2d735623af9214148afJeff Brown } 10411417b1cfde8f1749905f2d735623af9214148afJeff Brown } 10511417b1cfde8f1749905f2d735623af9214148afJeff Brown 10611417b1cfde8f1749905f2d735623af9214148afJeff Brown @Override 10711417b1cfde8f1749905f2d735623af9214148afJeff Brown public void run() { 10811417b1cfde8f1749905f2d735623af9214148afJeff Brown if (mActivelyScanningWifiDisplays) { 10911417b1cfde8f1749905f2d735623af9214148afJeff Brown try { 11011417b1cfde8f1749905f2d735623af9214148afJeff Brown mScanWifiDisplaysMethod.invoke(mDisplayManager); 11111417b1cfde8f1749905f2d735623af9214148afJeff Brown } catch (IllegalAccessException ex) { 11211417b1cfde8f1749905f2d735623af9214148afJeff Brown Log.w(TAG, "Cannot scan for wifi displays.", ex); 11311417b1cfde8f1749905f2d735623af9214148afJeff Brown } catch (InvocationTargetException ex) { 11411417b1cfde8f1749905f2d735623af9214148afJeff Brown Log.w(TAG, "Cannot scan for wifi displays.", ex); 11511417b1cfde8f1749905f2d735623af9214148afJeff Brown } 11611417b1cfde8f1749905f2d735623af9214148afJeff Brown mHandler.postDelayed(this, WIFI_DISPLAY_SCAN_INTERVAL); 11711417b1cfde8f1749905f2d735623af9214148afJeff Brown } 11811417b1cfde8f1749905f2d735623af9214148afJeff Brown } 11911417b1cfde8f1749905f2d735623af9214148afJeff Brown } 12011417b1cfde8f1749905f2d735623af9214148afJeff Brown 12111417b1cfde8f1749905f2d735623af9214148afJeff Brown /** 12211417b1cfde8f1749905f2d735623af9214148afJeff Brown * Workaround the fact that the isConnecting() method does not exist in JB MR1. 12311417b1cfde8f1749905f2d735623af9214148afJeff Brown * Do not use on JB MR2 and above. 12411417b1cfde8f1749905f2d735623af9214148afJeff Brown */ 12511417b1cfde8f1749905f2d735623af9214148afJeff Brown public static final class IsConnectingWorkaround { 12611417b1cfde8f1749905f2d735623af9214148afJeff Brown private Method mGetStatusCodeMethod; 12711417b1cfde8f1749905f2d735623af9214148afJeff Brown private int mStatusConnecting; 12811417b1cfde8f1749905f2d735623af9214148afJeff Brown 12911417b1cfde8f1749905f2d735623af9214148afJeff Brown public IsConnectingWorkaround() { 13011417b1cfde8f1749905f2d735623af9214148afJeff Brown if (Build.VERSION.SDK_INT != 17) { 13111417b1cfde8f1749905f2d735623af9214148afJeff Brown throw new UnsupportedOperationException(); 13211417b1cfde8f1749905f2d735623af9214148afJeff Brown } 13311417b1cfde8f1749905f2d735623af9214148afJeff Brown 13411417b1cfde8f1749905f2d735623af9214148afJeff Brown try { 13511417b1cfde8f1749905f2d735623af9214148afJeff Brown Field statusConnectingField = 13611417b1cfde8f1749905f2d735623af9214148afJeff Brown android.media.MediaRouter.RouteInfo.class.getField("STATUS_CONNECTING"); 13711417b1cfde8f1749905f2d735623af9214148afJeff Brown mStatusConnecting = statusConnectingField.getInt(null); 13811417b1cfde8f1749905f2d735623af9214148afJeff Brown mGetStatusCodeMethod = 13911417b1cfde8f1749905f2d735623af9214148afJeff Brown android.media.MediaRouter.RouteInfo.class.getMethod("getStatusCode"); 14011417b1cfde8f1749905f2d735623af9214148afJeff Brown } catch (NoSuchFieldException ex) { 14111417b1cfde8f1749905f2d735623af9214148afJeff Brown } catch (NoSuchMethodException ex) { 14211417b1cfde8f1749905f2d735623af9214148afJeff Brown } catch (IllegalAccessException ex) { 14311417b1cfde8f1749905f2d735623af9214148afJeff Brown } 14411417b1cfde8f1749905f2d735623af9214148afJeff Brown } 14511417b1cfde8f1749905f2d735623af9214148afJeff Brown 14611417b1cfde8f1749905f2d735623af9214148afJeff Brown public boolean isConnecting(Object routeObj) { 14711417b1cfde8f1749905f2d735623af9214148afJeff Brown android.media.MediaRouter.RouteInfo route = 14811417b1cfde8f1749905f2d735623af9214148afJeff Brown (android.media.MediaRouter.RouteInfo)routeObj; 14911417b1cfde8f1749905f2d735623af9214148afJeff Brown 15011417b1cfde8f1749905f2d735623af9214148afJeff Brown if (mGetStatusCodeMethod != null) { 15111417b1cfde8f1749905f2d735623af9214148afJeff Brown try { 15211417b1cfde8f1749905f2d735623af9214148afJeff Brown int statusCode = (Integer)mGetStatusCodeMethod.invoke(route); 15311417b1cfde8f1749905f2d735623af9214148afJeff Brown return statusCode == mStatusConnecting; 15411417b1cfde8f1749905f2d735623af9214148afJeff Brown } catch (IllegalAccessException ex) { 15511417b1cfde8f1749905f2d735623af9214148afJeff Brown } catch (InvocationTargetException ex) { 15611417b1cfde8f1749905f2d735623af9214148afJeff Brown } 15711417b1cfde8f1749905f2d735623af9214148afJeff Brown } 15811417b1cfde8f1749905f2d735623af9214148afJeff Brown 15911417b1cfde8f1749905f2d735623af9214148afJeff Brown // Assume not connecting. 16011417b1cfde8f1749905f2d735623af9214148afJeff Brown return false; 16111417b1cfde8f1749905f2d735623af9214148afJeff Brown } 16211417b1cfde8f1749905f2d735623af9214148afJeff Brown } 16311417b1cfde8f1749905f2d735623af9214148afJeff Brown 164c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown static class CallbackProxy<T extends Callback> 165c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown extends MediaRouterJellybean.CallbackProxy<T> { 166c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown public CallbackProxy(T callback) { 167c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown super(callback); 168c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown } 169c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown 170c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown @Override 171c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown public void onRoutePresentationDisplayChanged(android.media.MediaRouter router, 172c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown android.media.MediaRouter.RouteInfo route) { 173c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown mCallback.onRoutePresentationDisplayChanged(route); 174c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown } 175c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown } 176c21f57ed68b81a77167f1df000b0e272e1598bc0Jeff Brown} 177