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 android.os.Bundle; 20 import android.util.Log; 21 import android.view.View; 22 import android.view.ViewGroup; 23 import android.view.ViewRootImpl; 24 25 import androidx.annotation.VisibleForTesting; 26 27 import com.android.keyguard.KeyguardUpdateMonitor; 28 import com.android.keyguard.KeyguardViewController; 29 import com.android.keyguard.ViewMediatorCallback; 30 import com.android.systemui.R; 31 import com.android.systemui.car.systembar.CarSystemBarController; 32 import com.android.systemui.car.window.OverlayViewController; 33 import com.android.systemui.car.window.OverlayViewGlobalStateController; 34 import com.android.systemui.dagger.SysUISingleton; 35 import com.android.systemui.dagger.qualifiers.Main; 36 import com.android.systemui.statusbar.phone.BiometricUnlockController; 37 import com.android.systemui.statusbar.phone.KeyguardBouncer; 38 import com.android.systemui.statusbar.phone.KeyguardBouncer.Factory; 39 import com.android.systemui.statusbar.phone.KeyguardBypassController; 40 import com.android.systemui.statusbar.phone.NotificationPanelViewController; 41 import com.android.systemui.statusbar.phone.StatusBar; 42 import com.android.systemui.statusbar.policy.KeyguardStateController; 43 import com.android.systemui.util.concurrency.DelayableExecutor; 44 45 import javax.inject.Inject; 46 47 import dagger.Lazy; 48 49 /** 50 * Automotive implementation of the {@link KeyguardViewController}. It controls the Keyguard View 51 * that is mounted to the SystemUIOverlayWindow. 52 */ 53 @SysUISingleton 54 public class CarKeyguardViewController extends OverlayViewController implements 55 KeyguardViewController { 56 private static final String TAG = "CarKeyguardViewController"; 57 private static final boolean DEBUG = true; 58 59 private final DelayableExecutor mMainExecutor; 60 private final KeyguardStateController mKeyguardStateController; 61 private final KeyguardUpdateMonitor mKeyguardUpdateMonitor; 62 private final Lazy<BiometricUnlockController> mBiometricUnlockControllerLazy; 63 private final ViewMediatorCallback mViewMediatorCallback; 64 private final CarSystemBarController mCarSystemBarController; 65 private final Factory mKeyguardBouncerFactory; 66 // Needed to instantiate mBouncer. 67 private final KeyguardBouncer.BouncerExpansionCallback mExpansionCallback = 68 new KeyguardBouncer.BouncerExpansionCallback() { 69 @Override 70 public void onFullyShown() { 71 } 72 73 @Override 74 public void onStartingToHide() { 75 } 76 77 @Override 78 public void onStartingToShow() { 79 } 80 81 @Override 82 public void onFullyHidden() { 83 } 84 }; 85 86 private KeyguardBouncer mBouncer; 87 private OnKeyguardCancelClickedListener mKeyguardCancelClickedListener; 88 private boolean mShowing; 89 private boolean mIsOccluded; 90 91 @Inject CarKeyguardViewController( @ain DelayableExecutor mainExecutor, OverlayViewGlobalStateController overlayViewGlobalStateController, KeyguardStateController keyguardStateController, KeyguardUpdateMonitor keyguardUpdateMonitor, Lazy<BiometricUnlockController> biometricUnlockControllerLazy, ViewMediatorCallback viewMediatorCallback, CarSystemBarController carSystemBarController, KeyguardBouncer.Factory keyguardBouncerFactory)92 public CarKeyguardViewController( 93 @Main DelayableExecutor mainExecutor, 94 OverlayViewGlobalStateController overlayViewGlobalStateController, 95 KeyguardStateController keyguardStateController, 96 KeyguardUpdateMonitor keyguardUpdateMonitor, 97 Lazy<BiometricUnlockController> biometricUnlockControllerLazy, 98 ViewMediatorCallback viewMediatorCallback, 99 CarSystemBarController carSystemBarController, 100 KeyguardBouncer.Factory keyguardBouncerFactory) { 101 102 super(R.id.keyguard_stub, overlayViewGlobalStateController); 103 104 mMainExecutor = mainExecutor; 105 mKeyguardStateController = keyguardStateController; 106 mKeyguardUpdateMonitor = keyguardUpdateMonitor; 107 mBiometricUnlockControllerLazy = biometricUnlockControllerLazy; 108 mViewMediatorCallback = viewMediatorCallback; 109 mCarSystemBarController = carSystemBarController; 110 mKeyguardBouncerFactory = keyguardBouncerFactory; 111 } 112 113 @Override getFocusAreaViewId()114 protected int getFocusAreaViewId() { 115 return R.id.keyguard_container; 116 } 117 118 @Override shouldShowNavigationBarInsets()119 protected boolean shouldShowNavigationBarInsets() { 120 return true; 121 } 122 123 @Override onFinishInflate()124 public void onFinishInflate() { 125 mBouncer = mKeyguardBouncerFactory 126 .create(getLayout().findViewById(R.id.keyguard_container), mExpansionCallback); 127 mBiometricUnlockControllerLazy.get().setKeyguardViewController(this); 128 } 129 130 @Override notifyKeyguardAuthenticated(boolean strongAuth)131 public void notifyKeyguardAuthenticated(boolean strongAuth) { 132 if (mBouncer != null) { 133 mBouncer.notifyKeyguardAuthenticated(strongAuth); 134 } 135 } 136 137 @Override showBouncer(boolean scrimmed)138 public void showBouncer(boolean scrimmed) { 139 if (mShowing && !mBouncer.isShowing()) { 140 mBouncer.show(/* resetSecuritySelection= */ false); 141 } 142 } 143 144 @Override show(Bundle options)145 public void show(Bundle options) { 146 if (mShowing) return; 147 148 mShowing = true; 149 mKeyguardStateController.notifyKeyguardState(mShowing, /* occluded= */ false); 150 mCarSystemBarController.showAllKeyguardButtons(/* isSetUp= */ true); 151 start(); 152 reset(/* hideBouncerWhenShowing= */ false); 153 notifyKeyguardUpdateMonitor(); 154 } 155 156 @Override hide(long startTime, long fadeoutDuration)157 public void hide(long startTime, long fadeoutDuration) { 158 if (!mShowing) return; 159 160 mViewMediatorCallback.readyForKeyguardDone(); 161 mShowing = false; 162 mKeyguardStateController.notifyKeyguardState(mShowing, /* occluded= */ false); 163 mBouncer.hide(/* destroyView= */ true); 164 mCarSystemBarController.showAllNavigationButtons(/* isSetUp= */ true); 165 stop(); 166 mKeyguardStateController.notifyKeyguardDoneFading(); 167 mMainExecutor.execute(mViewMediatorCallback::keyguardGone); 168 notifyKeyguardUpdateMonitor(); 169 } 170 171 @Override reset(boolean hideBouncerWhenShowing)172 public void reset(boolean hideBouncerWhenShowing) { 173 mMainExecutor.execute(() -> { 174 if (mShowing) { 175 if (mBouncer != null) { 176 if (!mBouncer.isSecure()) { 177 dismissAndCollapse(); 178 } 179 resetBouncer(); 180 } 181 mKeyguardUpdateMonitor.sendKeyguardReset(); 182 notifyKeyguardUpdateMonitor(); 183 } else { 184 // This is necessary in order to address an inconsistency between the keyguard 185 // service and the keyguard views. 186 // TODO: Investigate the source of the inconsistency. 187 show(/* options= */ null); 188 } 189 }); 190 } 191 192 @Override onFinishedGoingToSleep()193 public void onFinishedGoingToSleep() { 194 if (mBouncer != null) { 195 mBouncer.onScreenTurnedOff(); 196 } 197 } 198 199 @Override setOccluded(boolean occluded, boolean animate)200 public void setOccluded(boolean occluded, boolean animate) { 201 mIsOccluded = occluded; 202 getOverlayViewGlobalStateController().setOccluded(occluded); 203 if (occluded) { 204 mCarSystemBarController.showAllOcclusionButtons(/* isSetup= */ true); 205 } else { 206 if (mShowing && mBouncer.isSecure()) { 207 mCarSystemBarController.showAllKeyguardButtons(/* isSetup= */ true); 208 } else { 209 mCarSystemBarController.showAllNavigationButtons(/* isSetUp= */ true); 210 } 211 } 212 } 213 214 @Override onCancelClicked()215 public void onCancelClicked() { 216 if (mBouncer == null) return; 217 218 getOverlayViewGlobalStateController().setWindowNeedsInput(/* needsInput= */ false); 219 220 mBouncer.hide(/* destroyView= */ true); 221 mKeyguardCancelClickedListener.onCancelClicked(); 222 } 223 224 @Override isShowing()225 public boolean isShowing() { 226 return mShowing; 227 } 228 229 @Override dismissAndCollapse()230 public void dismissAndCollapse() { 231 // If dismissing and collapsing Keyguard is requested (e.g. by a Keyguard-dismissing 232 // Activity) while Keyguard is occluded, unocclude Keyguard so the user can authenticate to 233 // dismiss Keyguard. 234 if (mIsOccluded) { 235 setOccluded(/* occluded= */ false, /* animate= */ false); 236 } 237 if (mBouncer != null && !mBouncer.isSecure()) { 238 hide(/* startTime= */ 0, /* fadeoutDuration= */ 0); 239 } 240 } 241 242 @Override startPreHideAnimation(Runnable finishRunnable)243 public void startPreHideAnimation(Runnable finishRunnable) { 244 if (mBouncer == null) return; 245 246 mBouncer.startPreHideAnimation(finishRunnable); 247 } 248 249 @Override setNeedsInput(boolean needsInput)250 public void setNeedsInput(boolean needsInput) { 251 getOverlayViewGlobalStateController().setWindowNeedsInput(needsInput); 252 } 253 254 /** 255 * Add listener for keyguard cancel clicked. 256 */ registerOnKeyguardCancelClickedListener( OnKeyguardCancelClickedListener keyguardCancelClickedListener)257 public void registerOnKeyguardCancelClickedListener( 258 OnKeyguardCancelClickedListener keyguardCancelClickedListener) { 259 mKeyguardCancelClickedListener = keyguardCancelClickedListener; 260 } 261 262 /** 263 * Remove listener for keyguard cancel clicked. 264 */ unregisterOnKeyguardCancelClickedListener( OnKeyguardCancelClickedListener keyguardCancelClickedListener)265 public void unregisterOnKeyguardCancelClickedListener( 266 OnKeyguardCancelClickedListener keyguardCancelClickedListener) { 267 mKeyguardCancelClickedListener = null; 268 } 269 270 @Override getViewRootImpl()271 public ViewRootImpl getViewRootImpl() { 272 return ((View) getLayout().getParent()).getViewRootImpl(); 273 } 274 275 @Override isBouncerShowing()276 public boolean isBouncerShowing() { 277 return mBouncer != null && mBouncer.isShowing(); 278 } 279 280 @Override bouncerIsOrWillBeShowing()281 public boolean bouncerIsOrWillBeShowing() { 282 return mBouncer != null && (mBouncer.isShowing() || mBouncer.inTransit()); 283 } 284 285 @Override keyguardGoingAway()286 public void keyguardGoingAway() { 287 // no-op 288 } 289 290 @Override setKeyguardGoingAwayState(boolean isKeyguardGoingAway)291 public void setKeyguardGoingAwayState(boolean isKeyguardGoingAway) { 292 // no-op 293 } 294 295 @Override onStartedGoingToSleep()296 public void onStartedGoingToSleep() { 297 // no-op 298 } 299 300 @Override onStartedWakingUp()301 public void onStartedWakingUp() { 302 // no-op 303 } 304 305 @Override onScreenTurningOn()306 public void onScreenTurningOn() { 307 // no-op 308 } 309 310 @Override onScreenTurnedOn()311 public void onScreenTurnedOn() { 312 // no-op 313 } 314 315 @Override shouldDisableWindowAnimationsForUnlock()316 public boolean shouldDisableWindowAnimationsForUnlock() { 317 return false; 318 } 319 320 @Override isGoingToNotificationShade()321 public boolean isGoingToNotificationShade() { 322 return false; 323 } 324 325 @Override isUnlockWithWallpaper()326 public boolean isUnlockWithWallpaper() { 327 return false; 328 } 329 330 @Override shouldSubtleWindowAnimationsForUnlock()331 public boolean shouldSubtleWindowAnimationsForUnlock() { 332 return false; 333 } 334 335 @Override blockPanelExpansionFromCurrentTouch()336 public void blockPanelExpansionFromCurrentTouch() { 337 // no-op 338 } 339 340 @Override registerStatusBar(StatusBar statusBar, ViewGroup container, NotificationPanelViewController notificationPanelViewController, BiometricUnlockController biometricUnlockController, View notificationContainer, KeyguardBypassController bypassController)341 public void registerStatusBar(StatusBar statusBar, ViewGroup container, 342 NotificationPanelViewController notificationPanelViewController, 343 BiometricUnlockController biometricUnlockController, 344 View notificationContainer, KeyguardBypassController bypassController) { 345 // no-op 346 } 347 348 /** 349 * Hides Keyguard so that the transitioning Bouncer can be hidden until it is prepared. To be 350 * called by {@link com.android.systemui.car.userswitcher.FullscreenUserSwitcherViewMediator} 351 * when a new user is selected. 352 */ hideKeyguardToPrepareBouncer()353 public void hideKeyguardToPrepareBouncer() { 354 getLayout().setVisibility(View.INVISIBLE); 355 } 356 357 @VisibleForTesting setKeyguardBouncer(KeyguardBouncer keyguardBouncer)358 void setKeyguardBouncer(KeyguardBouncer keyguardBouncer) { 359 mBouncer = keyguardBouncer; 360 } 361 revealKeyguardIfBouncerPrepared()362 private void revealKeyguardIfBouncerPrepared() { 363 int reattemptDelayMillis = 50; 364 Runnable revealKeyguard = () -> { 365 if (mBouncer == null) { 366 if (DEBUG) { 367 Log.d(TAG, "revealKeyguardIfBouncerPrepared: revealKeyguard request is ignored " 368 + "since the Bouncer has not been initialized yet."); 369 } 370 return; 371 } 372 if (!mBouncer.inTransit() || !mBouncer.isSecure()) { 373 showInternal(); 374 } else { 375 if (DEBUG) { 376 Log.d(TAG, "revealKeyguardIfBouncerPrepared: Bouncer is not prepared " 377 + "yet so reattempting after " + reattemptDelayMillis + "ms."); 378 } 379 mMainExecutor.executeDelayed(this::revealKeyguardIfBouncerPrepared, 380 reattemptDelayMillis); 381 } 382 }; 383 mMainExecutor.execute(revealKeyguard); 384 } 385 notifyKeyguardUpdateMonitor()386 private void notifyKeyguardUpdateMonitor() { 387 mKeyguardUpdateMonitor.onKeyguardVisibilityChanged(mShowing); 388 if (mBouncer != null) { 389 mKeyguardUpdateMonitor.sendKeyguardBouncerChanged(isBouncerShowing()); 390 } 391 } 392 resetBouncer()393 private void resetBouncer() { 394 mMainExecutor.execute(() -> { 395 hideInternal(); 396 mBouncer.hide(/* destroyView= */ false); 397 mBouncer.show(/* resetSecuritySelection= */ true); 398 revealKeyguardIfBouncerPrepared(); 399 }); 400 } 401 402 /** 403 * Defines a callback for keyguard cancel button clicked listeners. 404 */ 405 public interface OnKeyguardCancelClickedListener { 406 /** 407 * Called when keyguard cancel button is clicked. 408 */ onCancelClicked()409 void onCancelClicked(); 410 } 411 } 412