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