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