• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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