blob: 583dac7996aeb2bb114d3cec59071ccf4d627778 [file] [log] [blame]
Jim Miller31921482013-11-06 20:43:55 -08001/*
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 */
16package com.android.keyguard;
17
wilsonshihe7903ea2018-09-26 16:17:59 +080018import static android.view.Display.DEFAULT_DISPLAY;
David Stevens53a39ea2017-08-23 18:41:49 -070019
wilsonshihe7903ea2018-09-26 16:17:59 +080020import android.annotation.Nullable;
Jim Miller31921482013-11-06 20:43:55 -080021import android.app.Presentation;
22import android.content.Context;
Jim Miller31921482013-11-06 20:43:55 -080023import android.graphics.Point;
wilsonshihe7903ea2018-09-26 16:17:59 +080024import android.hardware.display.DisplayManager;
Jim Miller31921482013-11-06 20:43:55 -080025import android.media.MediaRouter;
26import android.media.MediaRouter.RouteInfo;
27import android.os.Bundle;
wilsonshihe7903ea2018-09-26 16:17:59 +080028import android.util.Log;
29import android.util.SparseArray;
Jim Miller31921482013-11-06 20:43:55 -080030import android.view.Display;
wilsonshihb5d1ab92018-12-06 12:52:31 +080031import android.view.DisplayInfo;
Jim Miller31921482013-11-06 20:43:55 -080032import android.view.View;
33import android.view.WindowManager;
34
wilsonshihe7903ea2018-09-26 16:17:59 +080035import java.util.function.BooleanSupplier;
36
David Stevens53a39ea2017-08-23 18:41:49 -070037// TODO(multi-display): Support multiple external displays
Jim Miller31921482013-11-06 20:43:55 -080038public class KeyguardDisplayManager {
39 protected static final String TAG = "KeyguardDisplayManager";
Jorim Jaggi5cf17872014-03-26 18:31:48 +010040 private static boolean DEBUG = KeyguardConstants.DEBUG;
David Stevens53a39ea2017-08-23 18:41:49 -070041
42 private final ViewMediatorCallback mCallback;
wilsonshihe7903ea2018-09-26 16:17:59 +080043
David Stevens53a39ea2017-08-23 18:41:49 -070044 private final MediaRouter mMediaRouter;
wilsonshihe7903ea2018-09-26 16:17:59 +080045 private final DisplayManager mDisplayService;
David Stevens53a39ea2017-08-23 18:41:49 -070046 private final Context mContext;
47
Jim Miller31921482013-11-06 20:43:55 -080048 private boolean mShowing;
wilsonshihb5d1ab92018-12-06 12:52:31 +080049 private final DisplayInfo mTmpDisplayInfo = new DisplayInfo();
Jim Miller31921482013-11-06 20:43:55 -080050
wilsonshihe7903ea2018-09-26 16:17:59 +080051 private final SparseArray<Presentation> mPresentations = new SparseArray<>();
52
53 private final DisplayManager.DisplayListener mDisplayListener =
54 new DisplayManager.DisplayListener() {
55
56 @Override
57 public void onDisplayAdded(int displayId) {
58 final Display display = mDisplayService.getDisplay(displayId);
59 if (mShowing) {
60 notifyIfChanged(() -> showPresentation(display));
61 }
62 }
63
64 @Override
65 public void onDisplayChanged(int displayId) {
66 if (displayId == DEFAULT_DISPLAY) return;
67 final Display display = mDisplayService.getDisplay(displayId);
68 if (display != null && mShowing) {
69 final Presentation presentation = mPresentations.get(displayId);
70 if (presentation != null && !presentation.getDisplay().equals(display)) {
71 hidePresentation(displayId);
72 showPresentation(display);
73 }
74 }
75 }
76
77 @Override
78 public void onDisplayRemoved(int displayId) {
79 notifyIfChanged(() -> hidePresentation(displayId));
80 }
81 };
82
David Stevens53a39ea2017-08-23 18:41:49 -070083 public KeyguardDisplayManager(Context context, ViewMediatorCallback callback) {
Jim Miller31921482013-11-06 20:43:55 -080084 mContext = context;
David Stevens53a39ea2017-08-23 18:41:49 -070085 mCallback = callback;
wilsonshihe7903ea2018-09-26 16:17:59 +080086 mMediaRouter = mContext.getSystemService(MediaRouter.class);
87 mDisplayService = mContext.getSystemService(DisplayManager.class);
88 mDisplayService.registerDisplayListener(mDisplayListener, null /* handler */);
89 }
90
wilsonshihb5d1ab92018-12-06 12:52:31 +080091 private boolean isKeyguardShowable(Display display) {
92 if (display == null) {
93 if (DEBUG) Log.i(TAG, "Cannot show Keyguard on null display");
94 return false;
95 }
96 if (display.getDisplayId() == DEFAULT_DISPLAY) {
97 if (DEBUG) Log.i(TAG, "Do not show KeyguardPresentation on the default display");
98 return false;
99 }
100 display.getDisplayInfo(mTmpDisplayInfo);
101 if ((mTmpDisplayInfo.flags & Display.FLAG_PRIVATE) != 0) {
102 if (DEBUG) Log.i(TAG, "Do not show KeyguardPresentation on a private display");
103 return false;
104 }
105 return true;
106 }
wilsonshihe7903ea2018-09-26 16:17:59 +0800107 /**
108 * @param display The display to show the presentation on.
109 * @return {@code true} if a presentation was added.
110 * {@code false} if the presentation cannot be added on that display or the presentation
111 * was already there.
112 */
113 private boolean showPresentation(Display display) {
wilsonshihb5d1ab92018-12-06 12:52:31 +0800114 if (!isKeyguardShowable(display)) return false;
wilsonshihe7903ea2018-09-26 16:17:59 +0800115 if (DEBUG) Log.i(TAG, "Keyguard enabled on display: " + display);
116 final int displayId = display.getDisplayId();
117 Presentation presentation = mPresentations.get(displayId);
118 if (presentation == null) {
119 presentation = new KeyguardPresentation(mContext, display);
120 presentation.setOnDismissListener(dialog -> {
121 if (null != mPresentations.get(displayId)) {
122 mPresentations.remove(displayId);
123 }
124 });
125 try {
126 presentation.show();
127 } catch (WindowManager.InvalidDisplayException ex) {
128 Log.w(TAG, "Invalid display:", ex);
129 presentation = null;
130 }
131 if (presentation != null) {
132 mPresentations.append(displayId, presentation);
133 return true;
134 }
135 }
136 return false;
137 }
138
139 /**
140 * @param displayId The id of the display to hide the presentation off.
141 * @return {@code true} if the a presentation was removed.
142 * {@code false} if the presentation was not added before.
143 */
144 private boolean hidePresentation(int displayId) {
145 final Presentation presentation = mPresentations.get(displayId);
146 if (presentation != null) {
147 presentation.dismiss();
148 mPresentations.remove(displayId);
149 return true;
150 }
151 return false;
152 }
153
154 private void notifyIfChanged(BooleanSupplier updateMethod) {
155 if (updateMethod.getAsBoolean()) {
156 final int[] displayList = getPresentationDisplayIds();
157 mCallback.onSecondaryDisplayShowingChanged(displayList);
158 }
159 }
160
161 /**
162 * @return An array of displayId's on which a {@link KeyguardPresentation} is showing on.
163 */
164 @Nullable
165 private int[] getPresentationDisplayIds() {
166 final int size = mPresentations.size();
167 if (size == 0) return null;
168
169 final int[] displayIds = new int[size];
170 for (int i = mPresentations.size() - 1; i >= 0; i--) {
171 final Presentation presentation = mPresentations.valueAt(i);
172 if (presentation != null) {
173 displayIds[i] = presentation.getDisplay().getDisplayId();
174 }
175 }
176 return displayIds;
Jim Miller31921482013-11-06 20:43:55 -0800177 }
178
Jorim Jaggi5cf17872014-03-26 18:31:48 +0100179 public void show() {
Jim Miller31921482013-11-06 20:43:55 -0800180 if (!mShowing) {
wilsonshihe7903ea2018-09-26 16:17:59 +0800181 if (DEBUG) Log.v(TAG, "show");
Jim Miller31921482013-11-06 20:43:55 -0800182 mMediaRouter.addCallback(MediaRouter.ROUTE_TYPE_REMOTE_DISPLAY,
183 mMediaRouterCallback, MediaRouter.CALLBACK_FLAG_PASSIVE_DISCOVERY);
wilsonshihe7903ea2018-09-26 16:17:59 +0800184 notifyIfChanged(() -> updateDisplays(true /* showing */));
Jim Miller31921482013-11-06 20:43:55 -0800185 }
186 mShowing = true;
187 }
188
Jorim Jaggi5cf17872014-03-26 18:31:48 +0100189 public void hide() {
Jim Miller31921482013-11-06 20:43:55 -0800190 if (mShowing) {
wilsonshihe7903ea2018-09-26 16:17:59 +0800191 if (DEBUG) Log.v(TAG, "hide");
Jim Miller31921482013-11-06 20:43:55 -0800192 mMediaRouter.removeCallback(mMediaRouterCallback);
wilsonshihe7903ea2018-09-26 16:17:59 +0800193 notifyIfChanged(() -> updateDisplays(false /* showing */));
Jim Miller31921482013-11-06 20:43:55 -0800194 }
195 mShowing = false;
196 }
197
198 private final MediaRouter.SimpleCallback mMediaRouterCallback =
199 new MediaRouter.SimpleCallback() {
200 @Override
201 public void onRouteSelected(MediaRouter router, int type, RouteInfo info) {
wilsonshihe7903ea2018-09-26 16:17:59 +0800202 if (DEBUG) Log.d(TAG, "onRouteSelected: type=" + type + ", info=" + info);
203 notifyIfChanged(() -> updateDisplays(mShowing));
Jim Miller31921482013-11-06 20:43:55 -0800204 }
205
206 @Override
207 public void onRouteUnselected(MediaRouter router, int type, RouteInfo info) {
wilsonshihe7903ea2018-09-26 16:17:59 +0800208 if (DEBUG) Log.d(TAG, "onRouteUnselected: type=" + type + ", info=" + info);
209 notifyIfChanged(() -> updateDisplays(mShowing));
Jim Miller31921482013-11-06 20:43:55 -0800210 }
211
212 @Override
213 public void onRoutePresentationDisplayChanged(MediaRouter router, RouteInfo info) {
wilsonshihe7903ea2018-09-26 16:17:59 +0800214 if (DEBUG) Log.d(TAG, "onRoutePresentationDisplayChanged: info=" + info);
215 notifyIfChanged(() -> updateDisplays(mShowing));
Jim Miller31921482013-11-06 20:43:55 -0800216 }
217 };
218
wilsonshihe7903ea2018-09-26 16:17:59 +0800219 protected boolean updateDisplays(boolean showing) {
220 boolean changed = false;
Jim Miller31921482013-11-06 20:43:55 -0800221 if (showing) {
wilsonshihe7903ea2018-09-26 16:17:59 +0800222 final Display[] displays = mDisplayService.getDisplays();
223 for (Display display : displays) {
224 changed |= showPresentation(display);
Jim Miller31921482013-11-06 20:43:55 -0800225 }
226 } else {
wilsonshihe7903ea2018-09-26 16:17:59 +0800227 changed = mPresentations.size() > 0;
228 for (int i = mPresentations.size() - 1; i >= 0; i--) {
229 mPresentations.valueAt(i).dismiss();
Jim Miller31921482013-11-06 20:43:55 -0800230 }
wilsonshihe7903ea2018-09-26 16:17:59 +0800231 mPresentations.clear();
Jim Miller31921482013-11-06 20:43:55 -0800232 }
wilsonshihe7903ea2018-09-26 16:17:59 +0800233 return changed;
Jim Miller31921482013-11-06 20:43:55 -0800234 }
235
236 private final static class KeyguardPresentation extends Presentation {
237 private static final int VIDEO_SAFE_REGION = 80; // Percentage of display width & height
238 private static final int MOVE_CLOCK_TIMEOUT = 10000; // 10s
239 private View mClock;
240 private int mUsableWidth;
241 private int mUsableHeight;
242 private int mMarginTop;
243 private int mMarginLeft;
244 Runnable mMoveTextRunnable = new Runnable() {
245 @Override
246 public void run() {
247 int x = mMarginLeft + (int) (Math.random() * (mUsableWidth - mClock.getWidth()));
248 int y = mMarginTop + (int) (Math.random() * (mUsableHeight - mClock.getHeight()));
249 mClock.setTranslationX(x);
250 mClock.setTranslationY(y);
251 mClock.postDelayed(mMoveTextRunnable, MOVE_CLOCK_TIMEOUT);
252 }
253 };
254
wilsonshihe7903ea2018-09-26 16:17:59 +0800255 KeyguardPresentation(Context context, Display display) {
256 super(context, display, R.style.keyguard_presentation_theme);
Jim Miller31921482013-11-06 20:43:55 -0800257 getWindow().setType(WindowManager.LayoutParams.TYPE_KEYGUARD_DIALOG);
wilsonshihe7903ea2018-09-26 16:17:59 +0800258 setCancelable(false);
Jim Miller31921482013-11-06 20:43:55 -0800259 }
260
Jim Millerc90d6452015-07-06 18:17:34 -0700261 @Override
Jim Miller31921482013-11-06 20:43:55 -0800262 public void onDetachedFromWindow() {
263 mClock.removeCallbacks(mMoveTextRunnable);
264 }
265
266 @Override
267 protected void onCreate(Bundle savedInstanceState) {
268 super.onCreate(savedInstanceState);
269
270 Point p = new Point();
271 getDisplay().getSize(p);
272 mUsableWidth = VIDEO_SAFE_REGION * p.x/100;
273 mUsableHeight = VIDEO_SAFE_REGION * p.y/100;
274 mMarginLeft = (100 - VIDEO_SAFE_REGION) * p.x / 200;
275 mMarginTop = (100 - VIDEO_SAFE_REGION) * p.y / 200;
276
277 setContentView(R.layout.keyguard_presentation);
278 mClock = findViewById(R.id.clock);
279
280 // Avoid screen burn in
281 mClock.post(mMoveTextRunnable);
282 }
283 }
284}