• 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 package com.android.keyguard;
17 
18 import android.app.Presentation;
19 import android.content.Context;
20 import android.graphics.Color;
21 import android.graphics.Rect;
22 import android.hardware.display.DisplayManager;
23 import android.media.MediaRouter;
24 import android.media.MediaRouter.RouteInfo;
25 import android.os.Bundle;
26 import android.os.Trace;
27 import android.util.Log;
28 import android.util.SparseArray;
29 import android.view.Display;
30 import android.view.DisplayInfo;
31 import android.view.LayoutInflater;
32 import android.view.View;
33 import android.view.WindowManager;
34 
35 import com.android.internal.annotations.VisibleForTesting;
36 import com.android.keyguard.dagger.KeyguardStatusViewComponent;
37 import com.android.systemui.R;
38 import com.android.systemui.dagger.qualifiers.Main;
39 import com.android.systemui.dagger.qualifiers.UiBackground;
40 import com.android.systemui.navigationbar.NavigationBarController;
41 import com.android.systemui.navigationbar.NavigationBarView;
42 import com.android.systemui.settings.DisplayTracker;
43 
44 import java.util.concurrent.Executor;
45 
46 import javax.inject.Inject;
47 
48 import dagger.Lazy;
49 
50 public class KeyguardDisplayManager {
51     protected static final String TAG = "KeyguardDisplayManager";
52     private static final boolean DEBUG = KeyguardConstants.DEBUG;
53 
54     private MediaRouter mMediaRouter = null;
55     private final DisplayManager mDisplayService;
56     private final DisplayTracker mDisplayTracker;
57     private final Lazy<NavigationBarController> mNavigationBarControllerLazy;
58     private final KeyguardStatusViewComponent.Factory mKeyguardStatusViewComponentFactory;
59     private final Context mContext;
60 
61     private boolean mShowing;
62     private final DisplayInfo mTmpDisplayInfo = new DisplayInfo();
63 
64     private final SparseArray<Presentation> mPresentations = new SparseArray<>();
65 
66     private final DisplayTracker.Callback mDisplayCallback =
67             new DisplayTracker.Callback() {
68                 @Override
69                 public void onDisplayAdded(int displayId) {
70                     Trace.beginSection(
71                             "KeyguardDisplayManager#onDisplayAdded(displayId=" + displayId + ")");
72                     final Display display = mDisplayService.getDisplay(displayId);
73                     if (mShowing) {
74                         updateNavigationBarVisibility(displayId, false /* navBarVisible */);
75                         showPresentation(display);
76                     }
77                     Trace.endSection();
78                 }
79 
80                 @Override
81                 public void onDisplayRemoved(int displayId) {
82                     Trace.beginSection(
83                             "KeyguardDisplayManager#onDisplayRemoved(displayId=" + displayId + ")");
84                     hidePresentation(displayId);
85                     Trace.endSection();
86                 }
87             };
88 
89     @Inject
KeyguardDisplayManager(Context context, Lazy<NavigationBarController> navigationBarControllerLazy, KeyguardStatusViewComponent.Factory keyguardStatusViewComponentFactory, DisplayTracker displayTracker, @Main Executor mainExecutor, @UiBackground Executor uiBgExecutor)90     public KeyguardDisplayManager(Context context,
91             Lazy<NavigationBarController> navigationBarControllerLazy,
92             KeyguardStatusViewComponent.Factory keyguardStatusViewComponentFactory,
93             DisplayTracker displayTracker,
94             @Main Executor mainExecutor,
95             @UiBackground Executor uiBgExecutor) {
96         mContext = context;
97         mNavigationBarControllerLazy = navigationBarControllerLazy;
98         mKeyguardStatusViewComponentFactory = keyguardStatusViewComponentFactory;
99         uiBgExecutor.execute(() -> mMediaRouter = mContext.getSystemService(MediaRouter.class));
100         mDisplayService = mContext.getSystemService(DisplayManager.class);
101         mDisplayTracker = displayTracker;
102         mDisplayTracker.addDisplayChangeCallback(mDisplayCallback, mainExecutor);
103     }
104 
isKeyguardShowable(Display display)105     private boolean isKeyguardShowable(Display display) {
106         if (display == null) {
107             if (DEBUG) Log.i(TAG, "Cannot show Keyguard on null display");
108             return false;
109         }
110         if (display.getDisplayId() == mDisplayTracker.getDefaultDisplayId()) {
111             if (DEBUG) Log.i(TAG, "Do not show KeyguardPresentation on the default display");
112             return false;
113         }
114         display.getDisplayInfo(mTmpDisplayInfo);
115         if ((mTmpDisplayInfo.flags & Display.FLAG_PRIVATE) != 0) {
116             if (DEBUG) Log.i(TAG, "Do not show KeyguardPresentation on a private display");
117             return false;
118         }
119         if ((mTmpDisplayInfo.flags & Display.FLAG_ALWAYS_UNLOCKED) != 0) {
120             if (DEBUG) {
121                 Log.i(TAG, "Do not show KeyguardPresentation on an unlocked display");
122             }
123             return false;
124         }
125 
126         return true;
127     }
128     /**
129      * @param display The display to show the presentation on.
130      * @return {@code true} if a presentation was added.
131      *         {@code false} if the presentation cannot be added on that display or the presentation
132      *         was already there.
133      */
showPresentation(Display display)134     private boolean showPresentation(Display display) {
135         if (!isKeyguardShowable(display)) return false;
136         if (DEBUG) Log.i(TAG, "Keyguard enabled on display: " + display);
137         final int displayId = display.getDisplayId();
138         Presentation presentation = mPresentations.get(displayId);
139         if (presentation == null) {
140             final Presentation newPresentation = createPresentation(display);
141             newPresentation.setOnDismissListener(dialog -> {
142                 if (newPresentation.equals(mPresentations.get(displayId))) {
143                     mPresentations.remove(displayId);
144                 }
145             });
146             presentation = newPresentation;
147             try {
148                 presentation.show();
149             } catch (WindowManager.InvalidDisplayException ex) {
150                 Log.w(TAG, "Invalid display:", ex);
151                 presentation = null;
152             }
153             if (presentation != null) {
154                 mPresentations.append(displayId, presentation);
155                 return true;
156             }
157         }
158         return false;
159     }
160 
createPresentation(Display display)161     KeyguardPresentation createPresentation(Display display) {
162         return new KeyguardPresentation(mContext, display, mKeyguardStatusViewComponentFactory);
163     }
164 
165     /**
166      * @param displayId The id of the display to hide the presentation off.
167      */
hidePresentation(int displayId)168     private void hidePresentation(int displayId) {
169         final Presentation presentation = mPresentations.get(displayId);
170         if (presentation != null) {
171             presentation.dismiss();
172             mPresentations.remove(displayId);
173         }
174     }
175 
show()176     public void show() {
177         if (!mShowing) {
178             if (DEBUG) Log.v(TAG, "show");
179             if (mMediaRouter != null) {
180                 mMediaRouter.addCallback(MediaRouter.ROUTE_TYPE_REMOTE_DISPLAY,
181                         mMediaRouterCallback, MediaRouter.CALLBACK_FLAG_PASSIVE_DISCOVERY);
182             } else {
183                 Log.w(TAG, "MediaRouter not yet initialized");
184             }
185             updateDisplays(true /* showing */);
186         }
187         mShowing = true;
188     }
189 
hide()190     public void hide() {
191         if (mShowing) {
192             if (DEBUG) Log.v(TAG, "hide");
193             if (mMediaRouter != null) {
194                 mMediaRouter.removeCallback(mMediaRouterCallback);
195             }
196             updateDisplays(false /* showing */);
197         }
198         mShowing = false;
199     }
200 
201     private final MediaRouter.SimpleCallback mMediaRouterCallback =
202             new MediaRouter.SimpleCallback() {
203         @Override
204         public void onRouteSelected(MediaRouter router, int type, RouteInfo info) {
205             if (DEBUG) Log.d(TAG, "onRouteSelected: type=" + type + ", info=" + info);
206             updateDisplays(mShowing);
207         }
208 
209         @Override
210         public void onRouteUnselected(MediaRouter router, int type, RouteInfo info) {
211             if (DEBUG) Log.d(TAG, "onRouteUnselected: type=" + type + ", info=" + info);
212             updateDisplays(mShowing);
213         }
214 
215         @Override
216         public void onRoutePresentationDisplayChanged(MediaRouter router, RouteInfo info) {
217             if (DEBUG) Log.d(TAG, "onRoutePresentationDisplayChanged: info=" + info);
218             updateDisplays(mShowing);
219         }
220     };
221 
updateDisplays(boolean showing)222     protected boolean updateDisplays(boolean showing) {
223         boolean changed = false;
224         if (showing) {
225             final Display[] displays = mDisplayTracker.getAllDisplays();
226             for (Display display : displays) {
227                 int displayId = display.getDisplayId();
228                 updateNavigationBarVisibility(displayId, false /* navBarVisible */);
229                 changed |= showPresentation(display);
230             }
231         } else {
232             changed = mPresentations.size() > 0;
233             for (int i = mPresentations.size() - 1; i >= 0; i--) {
234                 int displayId = mPresentations.keyAt(i);
235                 updateNavigationBarVisibility(displayId, true /* navBarVisible */);
236                 mPresentations.valueAt(i).dismiss();
237             }
238             mPresentations.clear();
239         }
240         return changed;
241     }
242 
243     // TODO(b/127878649): this logic is from
244     //  {@link StatusBarKeyguardViewManager#updateNavigationBarVisibility}. Try to revisit a long
245     //  term solution in R.
updateNavigationBarVisibility(int displayId, boolean navBarVisible)246     private void updateNavigationBarVisibility(int displayId, boolean navBarVisible) {
247         // Leave this task to {@link StatusBarKeyguardViewManager}
248         if (displayId == mDisplayTracker.getDefaultDisplayId()) return;
249 
250         NavigationBarView navBarView = mNavigationBarControllerLazy.get()
251                 .getNavigationBarView(displayId);
252         // We may not have nav bar on a display.
253         if (navBarView == null) return;
254 
255         if (navBarVisible) {
256             navBarView.getRootView().setVisibility(View.VISIBLE);
257         } else {
258             navBarView.getRootView().setVisibility(View.GONE);
259         }
260 
261     }
262 
263     @VisibleForTesting
264     static final class KeyguardPresentation extends Presentation {
265         private static final int VIDEO_SAFE_REGION = 80; // Percentage of display width & height
266         private static final int MOVE_CLOCK_TIMEOUT = 10000; // 10s
267         private final KeyguardStatusViewComponent.Factory mKeyguardStatusViewComponentFactory;
268         private KeyguardClockSwitchController mKeyguardClockSwitchController;
269         private View mClock;
270         private int mUsableWidth;
271         private int mUsableHeight;
272         private int mMarginTop;
273         private int mMarginLeft;
274         Runnable mMoveTextRunnable = new Runnable() {
275             @Override
276             public void run() {
277                 int x = mMarginLeft + (int) (Math.random() * (mUsableWidth - mClock.getWidth()));
278                 int y = mMarginTop + (int) (Math.random() * (mUsableHeight - mClock.getHeight()));
279                 mClock.setTranslationX(x);
280                 mClock.setTranslationY(y);
281                 mClock.postDelayed(mMoveTextRunnable, MOVE_CLOCK_TIMEOUT);
282             }
283         };
284 
KeyguardPresentation(Context context, Display display, KeyguardStatusViewComponent.Factory keyguardStatusViewComponentFactory)285         KeyguardPresentation(Context context, Display display,
286                 KeyguardStatusViewComponent.Factory keyguardStatusViewComponentFactory) {
287             super(context, display, R.style.Theme_SystemUI_KeyguardPresentation,
288                     WindowManager.LayoutParams.TYPE_KEYGUARD_DIALOG);
289             mKeyguardStatusViewComponentFactory = keyguardStatusViewComponentFactory;
290             setCancelable(false);
291         }
292 
293         @Override
cancel()294         public void cancel() {
295             // Do not allow anything to cancel KeyguardPresentation except KeyguardDisplayManager.
296         }
297 
298         @Override
onDetachedFromWindow()299         public void onDetachedFromWindow() {
300             mClock.removeCallbacks(mMoveTextRunnable);
301         }
302 
303         @Override
onDisplayChanged()304         public void onDisplayChanged() {
305             updateBounds();
306             getWindow().getDecorView().requestLayout();
307         }
308 
309         @Override
onCreate(Bundle savedInstanceState)310         protected void onCreate(Bundle savedInstanceState) {
311             super.onCreate(savedInstanceState);
312 
313             updateBounds();
314 
315             setContentView(LayoutInflater.from(getContext())
316                     .inflate(R.layout.keyguard_presentation, null));
317 
318             // Logic to make the lock screen fullscreen
319             getWindow().getDecorView().setSystemUiVisibility(
320                     View.SYSTEM_UI_FLAG_LAYOUT_FULLSCREEN
321                             | View.SYSTEM_UI_FLAG_LAYOUT_HIDE_NAVIGATION
322                             | View.SYSTEM_UI_FLAG_LAYOUT_STABLE);
323             getWindow().getAttributes().setFitInsetsTypes(0 /* types */);
324             getWindow().setNavigationBarContrastEnforced(false);
325             getWindow().setNavigationBarColor(Color.TRANSPARENT);
326 
327             mClock = findViewById(R.id.clock);
328 
329             // Avoid screen burn in
330             mClock.post(mMoveTextRunnable);
331 
332             mKeyguardClockSwitchController = mKeyguardStatusViewComponentFactory
333                     .build(findViewById(R.id.clock))
334                     .getKeyguardClockSwitchController();
335 
336             mKeyguardClockSwitchController.setOnlyClock(true);
337             mKeyguardClockSwitchController.init();
338         }
339 
updateBounds()340         private void updateBounds() {
341             final Rect bounds = getWindow().getWindowManager().getMaximumWindowMetrics()
342                     .getBounds();
343             mUsableWidth = VIDEO_SAFE_REGION * bounds.width() / 100;
344             mUsableHeight = VIDEO_SAFE_REGION * bounds.height() / 100;
345             mMarginLeft = (100 - VIDEO_SAFE_REGION) * bounds.width() / 200;
346             mMarginTop = (100 - VIDEO_SAFE_REGION) * bounds.height() / 200;
347         }
348     }
349 }
350