blob: e051317b96b6fb4aaef44b7a57077a14dcc90a6d [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;
31import android.view.View;
32import android.view.WindowManager;
33
wilsonshihe7903ea2018-09-26 16:17:59 +080034import java.util.function.BooleanSupplier;
35
David Stevens53a39ea2017-08-23 18:41:49 -070036// TODO(multi-display): Support multiple external displays
Jim Miller31921482013-11-06 20:43:55 -080037public class KeyguardDisplayManager {
38 protected static final String TAG = "KeyguardDisplayManager";
Jorim Jaggi5cf17872014-03-26 18:31:48 +010039 private static boolean DEBUG = KeyguardConstants.DEBUG;
David Stevens53a39ea2017-08-23 18:41:49 -070040
41 private final ViewMediatorCallback mCallback;
wilsonshihe7903ea2018-09-26 16:17:59 +080042
David Stevens53a39ea2017-08-23 18:41:49 -070043 private final MediaRouter mMediaRouter;
wilsonshihe7903ea2018-09-26 16:17:59 +080044 private final DisplayManager mDisplayService;
David Stevens53a39ea2017-08-23 18:41:49 -070045 private final Context mContext;
46
Jim Miller31921482013-11-06 20:43:55 -080047 private boolean mShowing;
48
wilsonshihe7903ea2018-09-26 16:17:59 +080049 private final SparseArray<Presentation> mPresentations = new SparseArray<>();
50
51 private final DisplayManager.DisplayListener mDisplayListener =
52 new DisplayManager.DisplayListener() {
53
54 @Override
55 public void onDisplayAdded(int displayId) {
56 final Display display = mDisplayService.getDisplay(displayId);
57 if (mShowing) {
58 notifyIfChanged(() -> showPresentation(display));
59 }
60 }
61
62 @Override
63 public void onDisplayChanged(int displayId) {
64 if (displayId == DEFAULT_DISPLAY) return;
65 final Display display = mDisplayService.getDisplay(displayId);
66 if (display != null && mShowing) {
67 final Presentation presentation = mPresentations.get(displayId);
68 if (presentation != null && !presentation.getDisplay().equals(display)) {
69 hidePresentation(displayId);
70 showPresentation(display);
71 }
72 }
73 }
74
75 @Override
76 public void onDisplayRemoved(int displayId) {
77 notifyIfChanged(() -> hidePresentation(displayId));
78 }
79 };
80
David Stevens53a39ea2017-08-23 18:41:49 -070081 public KeyguardDisplayManager(Context context, ViewMediatorCallback callback) {
Jim Miller31921482013-11-06 20:43:55 -080082 mContext = context;
David Stevens53a39ea2017-08-23 18:41:49 -070083 mCallback = callback;
wilsonshihe7903ea2018-09-26 16:17:59 +080084 mMediaRouter = mContext.getSystemService(MediaRouter.class);
85 mDisplayService = mContext.getSystemService(DisplayManager.class);
86 mDisplayService.registerDisplayListener(mDisplayListener, null /* handler */);
87 }
88
89 /**
90 * @param display The display to show the presentation on.
91 * @return {@code true} if a presentation was added.
92 * {@code false} if the presentation cannot be added on that display or the presentation
93 * was already there.
94 */
95 private boolean showPresentation(Display display) {
96 if (display == null || display.getDisplayId() == DEFAULT_DISPLAY) return false;
97 if (DEBUG) Log.i(TAG, "Keyguard enabled on display: " + display);
98 final int displayId = display.getDisplayId();
99 Presentation presentation = mPresentations.get(displayId);
100 if (presentation == null) {
101 presentation = new KeyguardPresentation(mContext, display);
102 presentation.setOnDismissListener(dialog -> {
103 if (null != mPresentations.get(displayId)) {
104 mPresentations.remove(displayId);
105 }
106 });
107 try {
108 presentation.show();
109 } catch (WindowManager.InvalidDisplayException ex) {
110 Log.w(TAG, "Invalid display:", ex);
111 presentation = null;
112 }
113 if (presentation != null) {
114 mPresentations.append(displayId, presentation);
115 return true;
116 }
117 }
118 return false;
119 }
120
121 /**
122 * @param displayId The id of the display to hide the presentation off.
123 * @return {@code true} if the a presentation was removed.
124 * {@code false} if the presentation was not added before.
125 */
126 private boolean hidePresentation(int displayId) {
127 final Presentation presentation = mPresentations.get(displayId);
128 if (presentation != null) {
129 presentation.dismiss();
130 mPresentations.remove(displayId);
131 return true;
132 }
133 return false;
134 }
135
136 private void notifyIfChanged(BooleanSupplier updateMethod) {
137 if (updateMethod.getAsBoolean()) {
138 final int[] displayList = getPresentationDisplayIds();
139 mCallback.onSecondaryDisplayShowingChanged(displayList);
140 }
141 }
142
143 /**
144 * @return An array of displayId's on which a {@link KeyguardPresentation} is showing on.
145 */
146 @Nullable
147 private int[] getPresentationDisplayIds() {
148 final int size = mPresentations.size();
149 if (size == 0) return null;
150
151 final int[] displayIds = new int[size];
152 for (int i = mPresentations.size() - 1; i >= 0; i--) {
153 final Presentation presentation = mPresentations.valueAt(i);
154 if (presentation != null) {
155 displayIds[i] = presentation.getDisplay().getDisplayId();
156 }
157 }
158 return displayIds;
Jim Miller31921482013-11-06 20:43:55 -0800159 }
160
Jorim Jaggi5cf17872014-03-26 18:31:48 +0100161 public void show() {
Jim Miller31921482013-11-06 20:43:55 -0800162 if (!mShowing) {
wilsonshihe7903ea2018-09-26 16:17:59 +0800163 if (DEBUG) Log.v(TAG, "show");
Jim Miller31921482013-11-06 20:43:55 -0800164 mMediaRouter.addCallback(MediaRouter.ROUTE_TYPE_REMOTE_DISPLAY,
165 mMediaRouterCallback, MediaRouter.CALLBACK_FLAG_PASSIVE_DISCOVERY);
wilsonshihe7903ea2018-09-26 16:17:59 +0800166 notifyIfChanged(() -> updateDisplays(true /* showing */));
Jim Miller31921482013-11-06 20:43:55 -0800167 }
168 mShowing = true;
169 }
170
Jorim Jaggi5cf17872014-03-26 18:31:48 +0100171 public void hide() {
Jim Miller31921482013-11-06 20:43:55 -0800172 if (mShowing) {
wilsonshihe7903ea2018-09-26 16:17:59 +0800173 if (DEBUG) Log.v(TAG, "hide");
Jim Miller31921482013-11-06 20:43:55 -0800174 mMediaRouter.removeCallback(mMediaRouterCallback);
wilsonshihe7903ea2018-09-26 16:17:59 +0800175 notifyIfChanged(() -> updateDisplays(false /* showing */));
Jim Miller31921482013-11-06 20:43:55 -0800176 }
177 mShowing = false;
178 }
179
180 private final MediaRouter.SimpleCallback mMediaRouterCallback =
181 new MediaRouter.SimpleCallback() {
182 @Override
183 public void onRouteSelected(MediaRouter router, int type, RouteInfo info) {
wilsonshihe7903ea2018-09-26 16:17:59 +0800184 if (DEBUG) Log.d(TAG, "onRouteSelected: type=" + type + ", info=" + info);
185 notifyIfChanged(() -> updateDisplays(mShowing));
Jim Miller31921482013-11-06 20:43:55 -0800186 }
187
188 @Override
189 public void onRouteUnselected(MediaRouter router, int type, RouteInfo info) {
wilsonshihe7903ea2018-09-26 16:17:59 +0800190 if (DEBUG) Log.d(TAG, "onRouteUnselected: type=" + type + ", info=" + info);
191 notifyIfChanged(() -> updateDisplays(mShowing));
Jim Miller31921482013-11-06 20:43:55 -0800192 }
193
194 @Override
195 public void onRoutePresentationDisplayChanged(MediaRouter router, RouteInfo info) {
wilsonshihe7903ea2018-09-26 16:17:59 +0800196 if (DEBUG) Log.d(TAG, "onRoutePresentationDisplayChanged: info=" + info);
197 notifyIfChanged(() -> updateDisplays(mShowing));
Jim Miller31921482013-11-06 20:43:55 -0800198 }
199 };
200
wilsonshihe7903ea2018-09-26 16:17:59 +0800201 protected boolean updateDisplays(boolean showing) {
202 boolean changed = false;
Jim Miller31921482013-11-06 20:43:55 -0800203 if (showing) {
wilsonshihe7903ea2018-09-26 16:17:59 +0800204 final Display[] displays = mDisplayService.getDisplays();
205 for (Display display : displays) {
206 changed |= showPresentation(display);
Jim Miller31921482013-11-06 20:43:55 -0800207 }
208 } else {
wilsonshihe7903ea2018-09-26 16:17:59 +0800209 changed = mPresentations.size() > 0;
210 for (int i = mPresentations.size() - 1; i >= 0; i--) {
211 mPresentations.valueAt(i).dismiss();
Jim Miller31921482013-11-06 20:43:55 -0800212 }
wilsonshihe7903ea2018-09-26 16:17:59 +0800213 mPresentations.clear();
Jim Miller31921482013-11-06 20:43:55 -0800214 }
wilsonshihe7903ea2018-09-26 16:17:59 +0800215 return changed;
Jim Miller31921482013-11-06 20:43:55 -0800216 }
217
218 private final static class KeyguardPresentation extends Presentation {
219 private static final int VIDEO_SAFE_REGION = 80; // Percentage of display width & height
220 private static final int MOVE_CLOCK_TIMEOUT = 10000; // 10s
221 private View mClock;
222 private int mUsableWidth;
223 private int mUsableHeight;
224 private int mMarginTop;
225 private int mMarginLeft;
226 Runnable mMoveTextRunnable = new Runnable() {
227 @Override
228 public void run() {
229 int x = mMarginLeft + (int) (Math.random() * (mUsableWidth - mClock.getWidth()));
230 int y = mMarginTop + (int) (Math.random() * (mUsableHeight - mClock.getHeight()));
231 mClock.setTranslationX(x);
232 mClock.setTranslationY(y);
233 mClock.postDelayed(mMoveTextRunnable, MOVE_CLOCK_TIMEOUT);
234 }
235 };
236
wilsonshihe7903ea2018-09-26 16:17:59 +0800237 KeyguardPresentation(Context context, Display display) {
238 super(context, display, R.style.keyguard_presentation_theme);
Jim Miller31921482013-11-06 20:43:55 -0800239 getWindow().setType(WindowManager.LayoutParams.TYPE_KEYGUARD_DIALOG);
wilsonshihe7903ea2018-09-26 16:17:59 +0800240 setCancelable(false);
Jim Miller31921482013-11-06 20:43:55 -0800241 }
242
Jim Millerc90d6452015-07-06 18:17:34 -0700243 @Override
Jim Miller31921482013-11-06 20:43:55 -0800244 public void onDetachedFromWindow() {
245 mClock.removeCallbacks(mMoveTextRunnable);
246 }
247
248 @Override
249 protected void onCreate(Bundle savedInstanceState) {
250 super.onCreate(savedInstanceState);
251
252 Point p = new Point();
253 getDisplay().getSize(p);
254 mUsableWidth = VIDEO_SAFE_REGION * p.x/100;
255 mUsableHeight = VIDEO_SAFE_REGION * p.y/100;
256 mMarginLeft = (100 - VIDEO_SAFE_REGION) * p.x / 200;
257 mMarginTop = (100 - VIDEO_SAFE_REGION) * p.y / 200;
258
259 setContentView(R.layout.keyguard_presentation);
260 mClock = findViewById(R.id.clock);
261
262 // Avoid screen burn in
263 mClock.post(mMoveTextRunnable);
264 }
265 }
266}