blob: 0ec60e5dff7dc80c4d64c4423a0d6dfcccdc673a [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
Jim Miller31921482013-11-06 20:43:55 -080020import android.app.Presentation;
21import android.content.Context;
Charles Chen64172bb2019-04-22 17:30:29 +080022import android.graphics.Color;
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;
Robert Snoebergerbe35b762019-03-15 14:33:02 -040032import android.view.LayoutInflater;
Jim Miller31921482013-11-06 20:43:55 -080033import android.view.View;
34import android.view.WindowManager;
35
Lucas Dupin7a271052019-05-27 22:33:32 -070036import com.android.internal.annotations.VisibleForTesting;
Charles Chen64172bb2019-04-22 17:30:29 +080037import com.android.systemui.Dependency;
38import com.android.systemui.statusbar.NavigationBarController;
39import com.android.systemui.statusbar.phone.NavigationBarView;
Robert Snoebergerbe35b762019-03-15 14:33:02 -040040import com.android.systemui.util.InjectionInflationController;
41
Jim Miller31921482013-11-06 20:43:55 -080042public class KeyguardDisplayManager {
43 protected static final String TAG = "KeyguardDisplayManager";
Jorim Jaggi5cf17872014-03-26 18:31:48 +010044 private static boolean DEBUG = KeyguardConstants.DEBUG;
David Stevens53a39ea2017-08-23 18:41:49 -070045
David Stevens53a39ea2017-08-23 18:41:49 -070046 private final MediaRouter mMediaRouter;
wilsonshihe7903ea2018-09-26 16:17:59 +080047 private final DisplayManager mDisplayService;
Robert Snoebergerbe35b762019-03-15 14:33:02 -040048 private final InjectionInflationController mInjectableInflater;
David Stevens53a39ea2017-08-23 18:41:49 -070049 private final Context mContext;
50
Jim Miller31921482013-11-06 20:43:55 -080051 private boolean mShowing;
wilsonshihb5d1ab92018-12-06 12:52:31 +080052 private final DisplayInfo mTmpDisplayInfo = new DisplayInfo();
Jim Miller31921482013-11-06 20:43:55 -080053
wilsonshihe7903ea2018-09-26 16:17:59 +080054 private final SparseArray<Presentation> mPresentations = new SparseArray<>();
55
Charles Chen64172bb2019-04-22 17:30:29 +080056 private final NavigationBarController mNavBarController =
57 Dependency.get(NavigationBarController.class);
58
wilsonshihe7903ea2018-09-26 16:17:59 +080059 private final DisplayManager.DisplayListener mDisplayListener =
60 new DisplayManager.DisplayListener() {
61
62 @Override
63 public void onDisplayAdded(int displayId) {
64 final Display display = mDisplayService.getDisplay(displayId);
65 if (mShowing) {
Charles Chen64172bb2019-04-22 17:30:29 +080066 updateNavigationBarVisibility(displayId, false /* navBarVisible */);
wilsonshih177261f2019-02-22 12:02:18 +080067 showPresentation(display);
wilsonshihe7903ea2018-09-26 16:17:59 +080068 }
69 }
70
71 @Override
72 public void onDisplayChanged(int displayId) {
73 if (displayId == DEFAULT_DISPLAY) return;
74 final Display display = mDisplayService.getDisplay(displayId);
75 if (display != null && mShowing) {
76 final Presentation presentation = mPresentations.get(displayId);
77 if (presentation != null && !presentation.getDisplay().equals(display)) {
78 hidePresentation(displayId);
79 showPresentation(display);
80 }
81 }
82 }
83
84 @Override
85 public void onDisplayRemoved(int displayId) {
wilsonshih177261f2019-02-22 12:02:18 +080086 hidePresentation(displayId);
wilsonshihe7903ea2018-09-26 16:17:59 +080087 }
88 };
89
Robert Snoebergerbe35b762019-03-15 14:33:02 -040090 public KeyguardDisplayManager(Context context,
91 InjectionInflationController injectableInflater) {
Jim Miller31921482013-11-06 20:43:55 -080092 mContext = context;
Robert Snoebergerbe35b762019-03-15 14:33:02 -040093 mInjectableInflater = injectableInflater;
wilsonshihe7903ea2018-09-26 16:17:59 +080094 mMediaRouter = mContext.getSystemService(MediaRouter.class);
95 mDisplayService = mContext.getSystemService(DisplayManager.class);
96 mDisplayService.registerDisplayListener(mDisplayListener, null /* handler */);
97 }
98
wilsonshihb5d1ab92018-12-06 12:52:31 +080099 private boolean isKeyguardShowable(Display display) {
100 if (display == null) {
101 if (DEBUG) Log.i(TAG, "Cannot show Keyguard on null display");
102 return false;
103 }
104 if (display.getDisplayId() == DEFAULT_DISPLAY) {
105 if (DEBUG) Log.i(TAG, "Do not show KeyguardPresentation on the default display");
106 return false;
107 }
108 display.getDisplayInfo(mTmpDisplayInfo);
109 if ((mTmpDisplayInfo.flags & Display.FLAG_PRIVATE) != 0) {
110 if (DEBUG) Log.i(TAG, "Do not show KeyguardPresentation on a private display");
111 return false;
112 }
113 return true;
114 }
wilsonshihe7903ea2018-09-26 16:17:59 +0800115 /**
116 * @param display The display to show the presentation on.
117 * @return {@code true} if a presentation was added.
118 * {@code false} if the presentation cannot be added on that display or the presentation
119 * was already there.
120 */
121 private boolean showPresentation(Display display) {
wilsonshihb5d1ab92018-12-06 12:52:31 +0800122 if (!isKeyguardShowable(display)) return false;
wilsonshihe7903ea2018-09-26 16:17:59 +0800123 if (DEBUG) Log.i(TAG, "Keyguard enabled on display: " + display);
124 final int displayId = display.getDisplayId();
125 Presentation presentation = mPresentations.get(displayId);
126 if (presentation == null) {
Robert Snoebergerbe35b762019-03-15 14:33:02 -0400127 presentation = new KeyguardPresentation(mContext, display, mInjectableInflater);
wilsonshihe7903ea2018-09-26 16:17:59 +0800128 presentation.setOnDismissListener(dialog -> {
129 if (null != mPresentations.get(displayId)) {
130 mPresentations.remove(displayId);
131 }
132 });
133 try {
134 presentation.show();
135 } catch (WindowManager.InvalidDisplayException ex) {
136 Log.w(TAG, "Invalid display:", ex);
137 presentation = null;
138 }
139 if (presentation != null) {
140 mPresentations.append(displayId, presentation);
141 return true;
142 }
143 }
144 return false;
145 }
146
147 /**
148 * @param displayId The id of the display to hide the presentation off.
wilsonshihe7903ea2018-09-26 16:17:59 +0800149 */
wilsonshih177261f2019-02-22 12:02:18 +0800150 private void hidePresentation(int displayId) {
wilsonshihe7903ea2018-09-26 16:17:59 +0800151 final Presentation presentation = mPresentations.get(displayId);
152 if (presentation != null) {
153 presentation.dismiss();
154 mPresentations.remove(displayId);
wilsonshihe7903ea2018-09-26 16:17:59 +0800155 }
Jim Miller31921482013-11-06 20:43:55 -0800156 }
157
Jorim Jaggi5cf17872014-03-26 18:31:48 +0100158 public void show() {
Jim Miller31921482013-11-06 20:43:55 -0800159 if (!mShowing) {
wilsonshihe7903ea2018-09-26 16:17:59 +0800160 if (DEBUG) Log.v(TAG, "show");
Jim Miller31921482013-11-06 20:43:55 -0800161 mMediaRouter.addCallback(MediaRouter.ROUTE_TYPE_REMOTE_DISPLAY,
162 mMediaRouterCallback, MediaRouter.CALLBACK_FLAG_PASSIVE_DISCOVERY);
wilsonshih177261f2019-02-22 12:02:18 +0800163 updateDisplays(true /* showing */);
Jim Miller31921482013-11-06 20:43:55 -0800164 }
165 mShowing = true;
166 }
167
Jorim Jaggi5cf17872014-03-26 18:31:48 +0100168 public void hide() {
Jim Miller31921482013-11-06 20:43:55 -0800169 if (mShowing) {
wilsonshihe7903ea2018-09-26 16:17:59 +0800170 if (DEBUG) Log.v(TAG, "hide");
Jim Miller31921482013-11-06 20:43:55 -0800171 mMediaRouter.removeCallback(mMediaRouterCallback);
wilsonshih177261f2019-02-22 12:02:18 +0800172 updateDisplays(false /* showing */);
Jim Miller31921482013-11-06 20:43:55 -0800173 }
174 mShowing = false;
175 }
176
177 private final MediaRouter.SimpleCallback mMediaRouterCallback =
178 new MediaRouter.SimpleCallback() {
179 @Override
180 public void onRouteSelected(MediaRouter router, int type, RouteInfo info) {
wilsonshihe7903ea2018-09-26 16:17:59 +0800181 if (DEBUG) Log.d(TAG, "onRouteSelected: type=" + type + ", info=" + info);
wilsonshih177261f2019-02-22 12:02:18 +0800182 updateDisplays(mShowing);
Jim Miller31921482013-11-06 20:43:55 -0800183 }
184
185 @Override
186 public void onRouteUnselected(MediaRouter router, int type, RouteInfo info) {
wilsonshihe7903ea2018-09-26 16:17:59 +0800187 if (DEBUG) Log.d(TAG, "onRouteUnselected: type=" + type + ", info=" + info);
wilsonshih177261f2019-02-22 12:02:18 +0800188 updateDisplays(mShowing);
Jim Miller31921482013-11-06 20:43:55 -0800189 }
190
191 @Override
192 public void onRoutePresentationDisplayChanged(MediaRouter router, RouteInfo info) {
wilsonshihe7903ea2018-09-26 16:17:59 +0800193 if (DEBUG) Log.d(TAG, "onRoutePresentationDisplayChanged: info=" + info);
wilsonshih177261f2019-02-22 12:02:18 +0800194 updateDisplays(mShowing);
Jim Miller31921482013-11-06 20:43:55 -0800195 }
196 };
197
wilsonshihe7903ea2018-09-26 16:17:59 +0800198 protected boolean updateDisplays(boolean showing) {
199 boolean changed = false;
Jim Miller31921482013-11-06 20:43:55 -0800200 if (showing) {
wilsonshihe7903ea2018-09-26 16:17:59 +0800201 final Display[] displays = mDisplayService.getDisplays();
202 for (Display display : displays) {
Charles Chen64172bb2019-04-22 17:30:29 +0800203 int displayId = display.getDisplayId();
204 updateNavigationBarVisibility(displayId, false /* navBarVisible */);
wilsonshihe7903ea2018-09-26 16:17:59 +0800205 changed |= showPresentation(display);
Jim Miller31921482013-11-06 20:43:55 -0800206 }
207 } else {
wilsonshihe7903ea2018-09-26 16:17:59 +0800208 changed = mPresentations.size() > 0;
209 for (int i = mPresentations.size() - 1; i >= 0; i--) {
Charles Chen64172bb2019-04-22 17:30:29 +0800210 int displayId = mPresentations.keyAt(i);
211 updateNavigationBarVisibility(displayId, true /* navBarVisible */);
wilsonshihe7903ea2018-09-26 16:17:59 +0800212 mPresentations.valueAt(i).dismiss();
Jim Miller31921482013-11-06 20:43:55 -0800213 }
wilsonshihe7903ea2018-09-26 16:17:59 +0800214 mPresentations.clear();
Jim Miller31921482013-11-06 20:43:55 -0800215 }
wilsonshihe7903ea2018-09-26 16:17:59 +0800216 return changed;
Jim Miller31921482013-11-06 20:43:55 -0800217 }
218
Charles Chen64172bb2019-04-22 17:30:29 +0800219 // TODO(b/127878649): this logic is from
220 // {@link StatusBarKeyguardViewManager#updateNavigationBarVisibility}. Try to revisit a long
221 // term solution in R.
222 private void updateNavigationBarVisibility(int displayId, boolean navBarVisible) {
223 // Leave this task to {@link StatusBarKeyguardViewManager}
224 if (displayId == DEFAULT_DISPLAY) return;
225
226 NavigationBarView navBarView = mNavBarController.getNavigationBarView(displayId);
227 // We may not have nav bar on a display.
228 if (navBarView == null) return;
229
230 if (navBarVisible) {
231 navBarView.getRootView().setVisibility(View.VISIBLE);
232 } else {
233 navBarView.getRootView().setVisibility(View.GONE);
234 }
235
236 }
237
Lucas Dupin7a271052019-05-27 22:33:32 -0700238 @VisibleForTesting
239 static final class KeyguardPresentation extends Presentation {
Jim Miller31921482013-11-06 20:43:55 -0800240 private static final int VIDEO_SAFE_REGION = 80; // Percentage of display width & height
241 private static final int MOVE_CLOCK_TIMEOUT = 10000; // 10s
Robert Snoebergerbe35b762019-03-15 14:33:02 -0400242 private final InjectionInflationController mInjectableInflater;
Jim Miller31921482013-11-06 20:43:55 -0800243 private View mClock;
244 private int mUsableWidth;
245 private int mUsableHeight;
246 private int mMarginTop;
247 private int mMarginLeft;
248 Runnable mMoveTextRunnable = new Runnable() {
249 @Override
250 public void run() {
251 int x = mMarginLeft + (int) (Math.random() * (mUsableWidth - mClock.getWidth()));
252 int y = mMarginTop + (int) (Math.random() * (mUsableHeight - mClock.getHeight()));
253 mClock.setTranslationX(x);
254 mClock.setTranslationY(y);
255 mClock.postDelayed(mMoveTextRunnable, MOVE_CLOCK_TIMEOUT);
256 }
257 };
258
Robert Snoebergerbe35b762019-03-15 14:33:02 -0400259 KeyguardPresentation(Context context, Display display,
260 InjectionInflationController injectionInflater) {
Lucas Dupin7a271052019-05-27 22:33:32 -0700261 super(context, display, R.style.Theme_SystemUI_KeyguardPresentation);
Robert Snoebergerbe35b762019-03-15 14:33:02 -0400262 mInjectableInflater = injectionInflater;
Jim Miller31921482013-11-06 20:43:55 -0800263 getWindow().setType(WindowManager.LayoutParams.TYPE_KEYGUARD_DIALOG);
wilsonshihe7903ea2018-09-26 16:17:59 +0800264 setCancelable(false);
Jim Miller31921482013-11-06 20:43:55 -0800265 }
266
Jim Millerc90d6452015-07-06 18:17:34 -0700267 @Override
Jim Miller31921482013-11-06 20:43:55 -0800268 public void onDetachedFromWindow() {
269 mClock.removeCallbacks(mMoveTextRunnable);
270 }
271
272 @Override
273 protected void onCreate(Bundle savedInstanceState) {
274 super.onCreate(savedInstanceState);
275
276 Point p = new Point();
277 getDisplay().getSize(p);
278 mUsableWidth = VIDEO_SAFE_REGION * p.x/100;
279 mUsableHeight = VIDEO_SAFE_REGION * p.y/100;
280 mMarginLeft = (100 - VIDEO_SAFE_REGION) * p.x / 200;
281 mMarginTop = (100 - VIDEO_SAFE_REGION) * p.y / 200;
282
Robert Snoebergerbe35b762019-03-15 14:33:02 -0400283 LayoutInflater inflater = mInjectableInflater.injectable(
284 LayoutInflater.from(getContext()));
285 setContentView(inflater.inflate(R.layout.keyguard_presentation, null));
Charles Chen64172bb2019-04-22 17:30:29 +0800286
287 // Logic to make the lock screen fullscreen
288 getWindow().getDecorView().setSystemUiVisibility(
289 View.SYSTEM_UI_FLAG_LAYOUT_FULLSCREEN
290 | View.SYSTEM_UI_FLAG_LAYOUT_HIDE_NAVIGATION
291 | View.SYSTEM_UI_FLAG_LAYOUT_STABLE);
292 getWindow().setNavigationBarContrastEnforced(false);
293 getWindow().setNavigationBarColor(Color.TRANSPARENT);
294
Jim Miller31921482013-11-06 20:43:55 -0800295 mClock = findViewById(R.id.clock);
296
297 // Avoid screen burn in
298 mClock.post(mMoveTextRunnable);
299 }
300 }
301}