1 /*
2  * Copyright (C) 2020 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 
17 package com.android.systemui.car.keyguard;
18 
19 
20 import android.animation.Animator;
21 import android.animation.AnimatorListenerAdapter;
22 import android.content.Context;
23 import android.content.res.Resources;
24 import android.graphics.PixelFormat;
25 import android.os.Bundle;
26 import android.os.UserHandle;
27 import android.util.Log;
28 import android.view.Gravity;
29 import android.view.View;
30 import android.view.ViewGroup;
31 import android.view.ViewRootImpl;
32 import android.view.WindowManager;
33 
34 import androidx.annotation.MainThread;
35 
36 import com.android.car.ui.FocusParkingView;
37 import com.android.internal.widget.LockPatternView;
38 import com.android.keyguard.KeyguardMessageAreaController;
39 import com.android.keyguard.KeyguardSecurityModel;
40 import com.android.keyguard.KeyguardUpdateMonitor;
41 import com.android.keyguard.KeyguardViewController;
42 import com.android.keyguard.ViewMediatorCallback;
43 import com.android.keyguard.dagger.KeyguardBouncerComponent;
44 import com.android.systemui.R;
45 import com.android.systemui.bouncer.domain.interactor.BouncerMessageInteractor;
46 import com.android.systemui.bouncer.domain.interactor.PrimaryBouncerCallbackInteractor;
47 import com.android.systemui.bouncer.domain.interactor.PrimaryBouncerCallbackInteractor.PrimaryBouncerExpansionCallback;
48 import com.android.systemui.bouncer.domain.interactor.PrimaryBouncerInteractor;
49 import com.android.systemui.bouncer.ui.BouncerView;
50 import com.android.systemui.bouncer.ui.binder.KeyguardBouncerViewBinder;
51 import com.android.systemui.bouncer.ui.viewmodel.KeyguardBouncerViewModel;
52 import com.android.systemui.car.systembar.CarSystemBarController;
53 import com.android.systemui.car.window.OverlayViewController;
54 import com.android.systemui.car.window.OverlayViewGlobalStateController;
55 import com.android.systemui.car.window.SystemUIOverlayWindowController;
56 import com.android.systemui.dagger.SysUISingleton;
57 import com.android.systemui.dagger.qualifiers.Main;
58 import com.android.systemui.keyguard.ui.viewmodel.PrimaryBouncerToGoneTransitionViewModel;
59 import com.android.systemui.log.BouncerLogger;
60 import com.android.systemui.settings.UserTracker;
61 import com.android.systemui.shade.ShadeExpansionStateManager;
62 import com.android.systemui.shade.domain.interactor.ShadeLockscreenInteractor;
63 import com.android.systemui.statusbar.phone.BiometricUnlockController;
64 import com.android.systemui.statusbar.phone.CentralSurfaces;
65 import com.android.systemui.statusbar.policy.KeyguardStateController;
66 import com.android.systemui.toast.SystemUIToast;
67 import com.android.systemui.toast.ToastFactory;
68 import com.android.systemui.user.domain.interactor.SelectedUserInteractor;
69 import com.android.systemui.util.concurrency.DelayableExecutor;
70 
71 import dagger.Lazy;
72 
73 import javax.inject.Inject;
74 
75 /**
76  * Automotive implementation of the {@link KeyguardViewController}. It controls the Keyguard View
77  * that is mounted to the SystemUIOverlayWindow.
78  */
79 @SysUISingleton
80 public class CarKeyguardViewController extends OverlayViewController implements
81         KeyguardViewController {
82     private static final String TAG = "CarKeyguardViewController";
83     private static final boolean DEBUG = true;
84     private static final float TOAST_PARAMS_HORIZONTAL_WEIGHT = 1.0f;
85     private static final float TOAST_PARAMS_VERTICAL_WEIGHT = 1.0f;
86 
87     private final Context mContext;
88     private final DelayableExecutor mMainExecutor;
89     private final WindowManager mWindowManager;
90     private final ToastFactory mToastFactory;
91     private final FocusParkingView mFocusParkingView;
92     private final KeyguardStateController mKeyguardStateController;
93     private final KeyguardUpdateMonitor mKeyguardUpdateMonitor;
94     private final Lazy<BiometricUnlockController> mBiometricUnlockControllerLazy;
95     private final ViewMediatorCallback mViewMediatorCallback;
96     private final CarSystemBarController mCarSystemBarController;
97     private final PrimaryBouncerInteractor mPrimaryBouncerInteractor;
98     private final KeyguardSecurityModel mKeyguardSecurityModel;
99     private final KeyguardBouncerViewModel mKeyguardBouncerViewModel;
100     private final KeyguardBouncerComponent.Factory mKeyguardBouncerComponentFactory;
101     private final BouncerView mBouncerView;
102     private final PrimaryBouncerExpansionCallback mExpansionCallback =
103             new PrimaryBouncerExpansionCallback() {
104                 @Override
105                 public void onFullyShown() {
106                     LockPatternView patternView = getLayout().findViewById(R.id.lockPatternView);
107                     if (patternView != null) {
108                         patternView.setOnFocusChangeListener((v, hasFocus) -> {
109                             if (hasFocus) {
110                                 makeOverlayToast(R.string.lockpattern_does_not_support_rotary);
111                             }
112                         });
113                     }
114                 }
115 
116                 @Override
117                 public void onStartingToHide() {
118                 }
119 
120                 @Override
121                 public void onStartingToShow() {
122                 }
123 
124                 @Override
125                 public void onFullyHidden() {
126                 }
127 
128                 @Override
129                 public void onVisibilityChanged(boolean isVisible) {
130                 }
131 
132                 @Override
133                 public void onExpansionChanged(float bouncerHideAmount) {
134                 }
135             };
136     private final KeyguardMessageAreaController.Factory mMessageAreaControllerFactory;
137     private final BouncerLogger mBouncerLogger;
138     private final SelectedUserInteractor mSelectedUserInteractor;
139     private final BouncerMessageInteractor mBouncerMessageInteractor;
140 
141     private OnKeyguardCancelClickedListener mKeyguardCancelClickedListener;
142     private boolean mShowing;
143     private boolean mIsSleeping;
144     private int mToastShowDurationMillisecond;
145     private ViewGroup mKeyguardContainer;
146     private PrimaryBouncerToGoneTransitionViewModel mPrimaryBouncerToGoneTransitionViewModel;
147 
148     @Inject
CarKeyguardViewController( Context context, UserTracker userTracker, @Main DelayableExecutor mainExecutor, WindowManager windowManager, ToastFactory toastFactory, SystemUIOverlayWindowController systemUIOverlayWindowController, OverlayViewGlobalStateController overlayViewGlobalStateController, KeyguardStateController keyguardStateController, KeyguardUpdateMonitor keyguardUpdateMonitor, Lazy<BiometricUnlockController> biometricUnlockControllerLazy, ViewMediatorCallback viewMediatorCallback, CarSystemBarController carSystemBarController, PrimaryBouncerCallbackInteractor primaryBouncerCallbackInteractor, PrimaryBouncerInteractor primaryBouncerInteractor, KeyguardSecurityModel keyguardSecurityModel, KeyguardBouncerViewModel keyguardBouncerViewModel, PrimaryBouncerToGoneTransitionViewModel primaryBouncerToGoneTransitionViewModel, KeyguardBouncerComponent.Factory keyguardBouncerComponentFactory, BouncerView bouncerView, KeyguardMessageAreaController.Factory messageAreaControllerFactory, BouncerLogger bouncerLogger, BouncerMessageInteractor bouncerMessageInteractor, SelectedUserInteractor selectedUserInteractor)149     public CarKeyguardViewController(
150             Context context,
151             UserTracker userTracker,
152             @Main DelayableExecutor mainExecutor,
153             WindowManager windowManager,
154             ToastFactory toastFactory,
155             SystemUIOverlayWindowController systemUIOverlayWindowController,
156             OverlayViewGlobalStateController overlayViewGlobalStateController,
157             KeyguardStateController keyguardStateController,
158             KeyguardUpdateMonitor keyguardUpdateMonitor,
159             Lazy<BiometricUnlockController> biometricUnlockControllerLazy,
160             ViewMediatorCallback viewMediatorCallback,
161             CarSystemBarController carSystemBarController,
162             PrimaryBouncerCallbackInteractor primaryBouncerCallbackInteractor,
163             PrimaryBouncerInteractor primaryBouncerInteractor,
164             KeyguardSecurityModel keyguardSecurityModel,
165             KeyguardBouncerViewModel keyguardBouncerViewModel,
166             PrimaryBouncerToGoneTransitionViewModel primaryBouncerToGoneTransitionViewModel,
167             KeyguardBouncerComponent.Factory keyguardBouncerComponentFactory,
168             BouncerView bouncerView,
169             KeyguardMessageAreaController.Factory messageAreaControllerFactory,
170             BouncerLogger bouncerLogger,
171             BouncerMessageInteractor bouncerMessageInteractor,
172             SelectedUserInteractor selectedUserInteractor) {
173         super(R.id.keyguard_stub, overlayViewGlobalStateController);
174 
175         mContext = context;
176         mMainExecutor = mainExecutor;
177         mWindowManager = windowManager;
178         mToastFactory = toastFactory;
179         mFocusParkingView = systemUIOverlayWindowController.getBaseLayout().findViewById(
180                 R.id.focus_parking_view);
181         mKeyguardStateController = keyguardStateController;
182         mKeyguardUpdateMonitor = keyguardUpdateMonitor;
183         mBiometricUnlockControllerLazy = biometricUnlockControllerLazy;
184         mViewMediatorCallback = viewMediatorCallback;
185         mCarSystemBarController = carSystemBarController;
186         mPrimaryBouncerInteractor = primaryBouncerInteractor;
187         mKeyguardSecurityModel = keyguardSecurityModel;
188         mKeyguardBouncerViewModel = keyguardBouncerViewModel;
189         mKeyguardBouncerComponentFactory = keyguardBouncerComponentFactory;
190         mPrimaryBouncerToGoneTransitionViewModel = primaryBouncerToGoneTransitionViewModel;
191         mBouncerView = bouncerView;
192         mSelectedUserInteractor = selectedUserInteractor;
193 
194         mToastShowDurationMillisecond = mContext.getResources().getInteger(
195                 R.integer.car_keyguard_toast_show_duration_millisecond);
196         mMessageAreaControllerFactory = messageAreaControllerFactory;
197         mBouncerLogger = bouncerLogger;
198         mBouncerMessageInteractor = bouncerMessageInteractor;
199         primaryBouncerCallbackInteractor.addBouncerExpansionCallback(mExpansionCallback);
200     }
201 
202     @Override
getFocusAreaViewId()203     protected int getFocusAreaViewId() {
204         return R.id.keyguard_container;
205     }
206 
207     @Override
shouldShowNavigationBarInsets()208     protected boolean shouldShowNavigationBarInsets() {
209         return true;
210     }
211 
212     @Override
onFinishInflate()213     public void onFinishInflate() {
214         mKeyguardContainer = getLayout().findViewById(R.id.keyguard_container);
215         KeyguardBouncerViewBinder.bind(mKeyguardContainer,
216                 mKeyguardBouncerViewModel, mPrimaryBouncerToGoneTransitionViewModel,
217                 mKeyguardBouncerComponentFactory,
218                 mMessageAreaControllerFactory,
219                 mBouncerMessageInteractor,
220                 mBouncerLogger,
221                 mSelectedUserInteractor);
222         mBiometricUnlockControllerLazy.get().setKeyguardViewController(this);
223     }
224 
225     @Override
226     @MainThread
notifyKeyguardAuthenticated(boolean strongAuth)227     public void notifyKeyguardAuthenticated(boolean strongAuth) {
228         mPrimaryBouncerInteractor.notifyKeyguardAuthenticatedBiometrics(strongAuth);
229     }
230 
231     @Override
232     @MainThread
showPrimaryBouncer(boolean scrimmed)233     public void showPrimaryBouncer(boolean scrimmed) {
234         if (mShowing && !mPrimaryBouncerInteractor.isFullyShowing()) {
235             mPrimaryBouncerInteractor.show(/* isScrimmed= */ true);
236         }
237     }
238 
239     @Override
240     @MainThread
show(Bundle options)241     public void show(Bundle options) {
242         if (mShowing) return;
243 
244         mShowing = true;
245         mKeyguardStateController.notifyKeyguardState(mShowing,
246                 mKeyguardStateController.isOccluded());
247         mCarSystemBarController.showAllKeyguardButtons(/* isSetUp= */ true);
248         start();
249         reset(/* hideBouncerWhenShowing= */ false);
250         notifyKeyguardUpdateMonitor();
251     }
252 
253     @Override
254     @MainThread
hide(long startTime, long fadeoutDuration)255     public void hide(long startTime, long fadeoutDuration) {
256         if (!mShowing || mIsSleeping) return;
257 
258         mViewMediatorCallback.readyForKeyguardDone();
259         mShowing = false;
260         mKeyguardStateController.notifyKeyguardState(mShowing,
261                 mKeyguardStateController.isOccluded());
262         mPrimaryBouncerInteractor.hide();
263         mCarSystemBarController.showAllNavigationButtons(/* isSetUp= */ true);
264         stop();
265         mKeyguardStateController.notifyKeyguardDoneFading();
266         mMainExecutor.execute(mViewMediatorCallback::keyguardGone);
267         notifyKeyguardUpdateMonitor();
268     }
269 
270     @Override
reset(boolean hideBouncerWhenShowing)271     public void reset(boolean hideBouncerWhenShowing) {
272         if (mIsSleeping) return;
273 
274         mMainExecutor.execute(() -> {
275             if (mShowing) {
276                 if (!isSecure()) {
277                     dismissAndCollapse();
278                 } else {
279                     resetBouncer();
280                 }
281                 mKeyguardUpdateMonitor.sendKeyguardReset();
282                 notifyKeyguardUpdateMonitor();
283             } else {
284                 // This is necessary in order to address an inconsistency between the keyguard
285                 // service and the keyguard views.
286                 // TODO: Investigate the source of the inconsistency.
287                 show(/* options= */ null);
288             }
289         });
290     }
291 
292     @Override
hideAlternateBouncer(boolean forceUpdateScrim)293     public void hideAlternateBouncer(boolean forceUpdateScrim) {
294         // no-op
295     }
296 
297     @Override
298     @MainThread
onFinishedGoingToSleep()299     public void onFinishedGoingToSleep() {
300         mPrimaryBouncerInteractor.hide();
301     }
302 
303     @Override
304     @MainThread
setOccluded(boolean occluded, boolean animate)305     public void setOccluded(boolean occluded, boolean animate) {
306         mKeyguardStateController.notifyKeyguardState(
307                 mKeyguardStateController.isShowing(), occluded);
308         getOverlayViewGlobalStateController().setOccluded(occluded);
309         if (occluded && !mKeyguardStateController.isUnlocked()) {
310             mCarSystemBarController.showAllOcclusionButtons(/* isSetup= */ true);
311         } else {
312             if (mShowing && isSecure()) {
313                 mCarSystemBarController.showAllKeyguardButtons(/* isSetup= */ true);
314             } else {
315                 mCarSystemBarController.showAllNavigationButtons(/* isSetUp= */ true);
316             }
317         }
318     }
319 
320     @Override
321     @MainThread
onCancelClicked()322     public void onCancelClicked() {
323         mPrimaryBouncerInteractor.hide();
324         mKeyguardCancelClickedListener.onCancelClicked();
325     }
326 
327     @Override
328     @MainThread
dismissAndCollapse()329     public void dismissAndCollapse() {
330         // If dismissing and collapsing Keyguard is requested (e.g. by a Keyguard-dismissing
331         // Activity) while Keyguard is occluded, unocclude Keyguard so the user can authenticate to
332         // dismiss Keyguard.
333         if (mKeyguardStateController.isOccluded()) {
334             setOccluded(/* occluded= */ false, /* animate= */ false);
335         }
336         if (!isSecure()) {
337             hide(/* startTime= */ 0, /* fadeoutDuration= */ 0);
338             resetBouncer();
339         }
340     }
341 
342     @Override
343     @MainThread
startPreHideAnimation(Runnable finishRunnable)344     public void startPreHideAnimation(Runnable finishRunnable) {
345         if (isBouncerShowing()) {
346             mPrimaryBouncerInteractor.startDisappearAnimation(finishRunnable);
347         } else if (finishRunnable != null) {
348             finishRunnable.run();
349         }
350     }
351 
352     @Override
setNeedsInput(boolean needsInput)353     public void setNeedsInput(boolean needsInput) {
354         getOverlayViewGlobalStateController().setWindowNeedsInput(needsInput);
355     }
356 
357     @Override
onStartedGoingToSleep()358     public void onStartedGoingToSleep() {
359         mIsSleeping = true;
360     }
361 
362     @Override
onStartedWakingUp()363     public void onStartedWakingUp() {
364         mIsSleeping = false;
365         reset(/* hideBouncerWhenShowing= */ false);
366     }
367 
368     /**
369      * Add listener for keyguard cancel clicked.
370      */
registerOnKeyguardCancelClickedListener( OnKeyguardCancelClickedListener keyguardCancelClickedListener)371     public void registerOnKeyguardCancelClickedListener(
372             OnKeyguardCancelClickedListener keyguardCancelClickedListener) {
373         mKeyguardCancelClickedListener = keyguardCancelClickedListener;
374     }
375 
376     /**
377      * Remove listener for keyguard cancel clicked.
378      */
unregisterOnKeyguardCancelClickedListener( OnKeyguardCancelClickedListener keyguardCancelClickedListener)379     public void unregisterOnKeyguardCancelClickedListener(
380             OnKeyguardCancelClickedListener keyguardCancelClickedListener) {
381         mKeyguardCancelClickedListener = null;
382     }
383 
384     @Override
getViewRootImpl()385     public ViewRootImpl getViewRootImpl() {
386         if (getLayout() == null) return null;
387         return ((View) getLayout().getParent()).getViewRootImpl();
388     }
389 
390     @Override
391     @MainThread
isBouncerShowing()392     public boolean isBouncerShowing() {
393         return mPrimaryBouncerInteractor.isFullyShowing();
394     }
395 
396     @Override
397     @MainThread
primaryBouncerIsOrWillBeShowing()398     public boolean primaryBouncerIsOrWillBeShowing() {
399         return mPrimaryBouncerInteractor.isFullyShowing()
400                 || mPrimaryBouncerInteractor.isInTransit();
401     }
402 
403     @Override
keyguardGoingAway()404     public void keyguardGoingAway() {
405         // no-op
406     }
407 
408     @Override
setKeyguardGoingAwayState(boolean isKeyguardGoingAway)409     public void setKeyguardGoingAwayState(boolean isKeyguardGoingAway) {
410         // no-op
411     }
412 
413     @Override
shouldDisableWindowAnimationsForUnlock()414     public boolean shouldDisableWindowAnimationsForUnlock() {
415         return false;
416     }
417 
418     @Override
isGoingToNotificationShade()419     public boolean isGoingToNotificationShade() {
420         return false;
421     }
422 
423     @Override
isUnlockWithWallpaper()424     public boolean isUnlockWithWallpaper() {
425         return false;
426     }
427 
428     @Override
isBouncerShowingOverDream()429     public boolean isBouncerShowingOverDream() {
430         return false;
431     }
432 
433     @Override
shouldSubtleWindowAnimationsForUnlock()434     public boolean shouldSubtleWindowAnimationsForUnlock() {
435         return false;
436     }
437 
438     @Override
blockPanelExpansionFromCurrentTouch()439     public void blockPanelExpansionFromCurrentTouch() {
440         // no-op
441     }
442 
443     @Override
registerCentralSurfaces( CentralSurfaces centralSurfaces, ShadeLockscreenInteractor shadeLockscreenInteractor, ShadeExpansionStateManager shadeExpansionStateManager, BiometricUnlockController biometricUnlockController, View notificationContainer)444     public void registerCentralSurfaces(
445             CentralSurfaces centralSurfaces,
446             ShadeLockscreenInteractor shadeLockscreenInteractor,
447             ShadeExpansionStateManager shadeExpansionStateManager,
448             BiometricUnlockController biometricUnlockController,
449             View notificationContainer) {
450         // no-op
451     }
452 
453     /**
454      * Hides Keyguard so that the transitioning Bouncer can be hidden until it is prepared. To be
455      * called by {@link com.android.systemui.car.userswitcher.FullscreenUserSwitcherViewMediator}
456      * when a new user is selected.
457      */
hideKeyguardToPrepareBouncer()458     public void hideKeyguardToPrepareBouncer() {
459         getLayout().setVisibility(View.INVISIBLE);
460     }
461 
462     @Override
setAllowRotaryFocus(boolean allowRotaryFocus)463     public boolean setAllowRotaryFocus(boolean allowRotaryFocus) {
464         boolean changed = super.setAllowRotaryFocus(allowRotaryFocus);
465         if (changed && allowRotaryFocus && mBouncerView.getDelegate() != null) {
466             // Resume the view so it can regain focus
467             mBouncerView.getDelegate().resume();
468         }
469         return changed;
470     }
471 
revealKeyguardIfBouncerPrepared()472     private void revealKeyguardIfBouncerPrepared() {
473         int reattemptDelayMillis = 50;
474         Runnable revealKeyguard = () -> {
475             if (!mPrimaryBouncerInteractor.isInTransit() || !isSecure()) {
476                 if (mShowing) {
477                     // Only set the layout as visible if the keyguard should be showing
478                     showInternal();
479                 }
480             } else {
481                 if (DEBUG) {
482                     Log.d(TAG, "revealKeyguardIfBouncerPrepared: Bouncer is not prepared "
483                             + "yet so reattempting after " + reattemptDelayMillis + "ms.");
484                 }
485                 mMainExecutor.executeDelayed(this::revealKeyguardIfBouncerPrepared,
486                         reattemptDelayMillis);
487             }
488         };
489         mMainExecutor.execute(revealKeyguard);
490     }
491 
notifyKeyguardUpdateMonitor()492     private void notifyKeyguardUpdateMonitor() {
493         mKeyguardUpdateMonitor.sendPrimaryBouncerChanged(
494                 primaryBouncerIsOrWillBeShowing(), isBouncerShowing());
495     }
496 
497     /**
498      * WARNING: This method might cause Binder calls.
499      */
isSecure()500     private boolean isSecure() {
501         return mKeyguardSecurityModel.getSecurityMode(mSelectedUserInteractor.getSelectedUserId())
502                 != KeyguardSecurityModel.SecurityMode.None;
503     }
504 
505 
resetBouncer()506     private void resetBouncer() {
507         mMainExecutor.execute(() -> {
508             hideInternal();
509             mPrimaryBouncerInteractor.hide();
510             mPrimaryBouncerInteractor.show(/* isScrimmed= */ true);
511             revealKeyguardIfBouncerPrepared();
512         });
513     }
514 
makeOverlayToast(int stringId)515     private void makeOverlayToast(int stringId) {
516         Resources res = mContext.getResources();
517 
518         SystemUIToast systemUIToast = mToastFactory.createToast(mContext,
519                 res.getString(stringId), mContext.getPackageName(), UserHandle.myUserId(),
520                 res.getConfiguration().orientation);
521 
522         if (systemUIToast == null) {
523             return;
524         }
525 
526         View toastView = systemUIToast.getView();
527 
528         WindowManager.LayoutParams params = new WindowManager.LayoutParams();
529         params.height = WindowManager.LayoutParams.WRAP_CONTENT;
530         params.width = WindowManager.LayoutParams.WRAP_CONTENT;
531         params.format = PixelFormat.TRANSLUCENT;
532         params.type = WindowManager.LayoutParams.TYPE_STATUS_BAR_SUB_PANEL;
533         params.y = systemUIToast.getYOffset();
534 
535         int absGravity = Gravity.getAbsoluteGravity(systemUIToast.getGravity(),
536                 res.getConfiguration().getLayoutDirection());
537         params.gravity = absGravity;
538 
539         if ((absGravity & Gravity.HORIZONTAL_GRAVITY_MASK) == Gravity.FILL_HORIZONTAL) {
540             params.horizontalWeight = TOAST_PARAMS_HORIZONTAL_WEIGHT;
541         }
542         if ((absGravity & Gravity.VERTICAL_GRAVITY_MASK) == Gravity.FILL_VERTICAL) {
543             params.verticalWeight = TOAST_PARAMS_VERTICAL_WEIGHT;
544         }
545 
546         // Make FocusParkingView temporarily unfocusable so it does not steal the focus.
547         // If FocusParkingView is focusable, it first steals focus and then returns it to Pattern
548         // Lock, which causes the Toast to appear repeatedly.
549         mFocusParkingView.setFocusable(false);
550         mWindowManager.addView(toastView, params);
551 
552         Animator inAnimator = systemUIToast.getInAnimation();
553         if (inAnimator != null) {
554             inAnimator.start();
555         }
556 
557         mMainExecutor.executeDelayed(new Runnable() {
558             @Override
559             public void run() {
560                 Animator outAnimator = systemUIToast.getOutAnimation();
561                 if (outAnimator != null) {
562                     outAnimator.start();
563                     outAnimator.addListener(new AnimatorListenerAdapter() {
564                         @Override
565                         public void onAnimationEnd(Animator animator) {
566                             mWindowManager.removeViewImmediate(toastView);
567                             mFocusParkingView.setFocusable(true);
568                         }
569                     });
570                 } else {
571                     mFocusParkingView.setFocusable(true);
572                 }
573             }
574         }, mToastShowDurationMillisecond);
575     }
576 
577     /**
578      * Defines a callback for keyguard cancel button clicked listeners.
579      */
580     public interface OnKeyguardCancelClickedListener {
581         /**
582          * Called when keyguard cancel button is clicked.
583          */
onCancelClicked()584         void onCancelClicked();
585     }
586 }
587