From 8854bfa19a3e9c8226806a33e97b4e1bd785a6de Mon Sep 17 00:00:00 2001 From: Aaron Liu Date: Tue, 26 Jul 2022 13:37:19 -0700 Subject: [PATCH] [Keyguard Bouncer] Add new data flow. Add MVVM architecture to the existing bouncer logic. Make this feature flag enabled but true by default. Bug: 240298897 Test: Added unit tests and test behavior on device. - Tested on Large screen and standard screen device. - Unlock sim - Unlock pattern - Unlock password - Unlock from tapping settings icon in LS - Unlock from dream - Sim pin unlock - Sim pin -> sim puk unlock - Tested with feature flag off. Change-Id: I0391f552b628991f6504a8c032dbe0e2ad65e859 --- .../res/layout/super_notification_shade.xml | 1 + .../keyguard/KeyguardSecurityContainer.java | 1 + .../KeyguardSimPukViewController.java | 8 + .../systemui/dagger/SystemUIModule.java | 2 + .../DreamOverlayContainerViewController.java | 8 +- .../systemui/keyguard/data/BouncerView.kt | 48 +++ .../keyguard/data/BouncerViewModule.kt | 26 ++ .../repository/KeyguardBouncerRepository.kt | 154 +++++++++ .../interactor/BouncerCallbackInteractor.kt | 102 ++++++ .../domain/interactor/BouncerInteractor.kt | 324 ++++++++++++++++++ .../model/BouncerCallbackActionsModel.kt | 26 ++ .../shared/model/BouncerShowMessageModel.kt | 22 ++ .../shared/model/KeyguardBouncerModel.kt | 24 ++ .../ui/binder/KeyguardBouncerViewBinder.kt | 220 ++++++++++++ .../ui/viewmodel/KeyguardBouncerViewModel.kt | 119 +++++++ ...NotificationShadeWindowViewController.java | 16 +- .../statusbar/phone/KeyguardBouncer.java | 2 + .../phone/StatusBarKeyguardViewManager.java | 267 ++++++++++++--- ...eamOverlayContainerViewControllerTest.java | 7 +- .../BouncerCallbackInteractorTest.kt | 86 +++++ .../interactor/BouncerInteractorTest.kt | 278 +++++++++++++++ ...tificationShadeWindowViewControllerTest.kt | 20 +- .../NotificationShadeWindowViewTest.java | 11 +- .../StatusBarKeyguardViewManagerTest.java | 22 +- 24 files changed, 1734 insertions(+), 60 deletions(-) create mode 100644 packages/SystemUI/src/com/android/systemui/keyguard/data/BouncerView.kt create mode 100644 packages/SystemUI/src/com/android/systemui/keyguard/data/BouncerViewModule.kt create mode 100644 packages/SystemUI/src/com/android/systemui/keyguard/data/repository/KeyguardBouncerRepository.kt create mode 100644 packages/SystemUI/src/com/android/systemui/keyguard/domain/interactor/BouncerCallbackInteractor.kt create mode 100644 packages/SystemUI/src/com/android/systemui/keyguard/domain/interactor/BouncerInteractor.kt create mode 100644 packages/SystemUI/src/com/android/systemui/keyguard/shared/model/BouncerCallbackActionsModel.kt create mode 100644 packages/SystemUI/src/com/android/systemui/keyguard/shared/model/BouncerShowMessageModel.kt create mode 100644 packages/SystemUI/src/com/android/systemui/keyguard/shared/model/KeyguardBouncerModel.kt create mode 100644 packages/SystemUI/src/com/android/systemui/keyguard/ui/binder/KeyguardBouncerViewBinder.kt create mode 100644 packages/SystemUI/src/com/android/systemui/keyguard/ui/viewmodel/KeyguardBouncerViewModel.kt create mode 100644 packages/SystemUI/tests/src/com/android/systemui/keyguard/domain/interactor/BouncerCallbackInteractorTest.kt create mode 100644 packages/SystemUI/tests/src/com/android/systemui/keyguard/domain/interactor/BouncerInteractorTest.kt diff --git a/packages/SystemUI/res/layout/super_notification_shade.xml b/packages/SystemUI/res/layout/super_notification_shade.xml index 0c57b934d27e6..8388b67c1fa1e 100644 --- a/packages/SystemUI/res/layout/super_notification_shade.xml +++ b/packages/SystemUI/res/layout/super_notification_shade.xml @@ -103,6 +103,7 @@ android:layout_width="match_parent" android:layout_weight="1" android:background="@android:color/transparent" + android:visibility="invisible" android:clipChildren="false" android:clipToPadding="false" /> diff --git a/packages/SystemUI/src/com/android/keyguard/KeyguardSecurityContainer.java b/packages/SystemUI/src/com/android/keyguard/KeyguardSecurityContainer.java index f73c98e4971b2..e115653dd431b 100644 --- a/packages/SystemUI/src/com/android/keyguard/KeyguardSecurityContainer.java +++ b/packages/SystemUI/src/com/android/keyguard/KeyguardSecurityContainer.java @@ -1098,6 +1098,7 @@ public class KeyguardSecurityContainer extends FrameLayout { return; } + mView.setAlpha(1f); mUserSwitcherViewGroup.setAlpha(0f); ObjectAnimator alphaAnim = ObjectAnimator.ofFloat(mUserSwitcherViewGroup, View.ALPHA, 1f); diff --git a/packages/SystemUI/src/com/android/keyguard/KeyguardSimPukViewController.java b/packages/SystemUI/src/com/android/keyguard/KeyguardSimPukViewController.java index d8cffd7984ba1..5995e859c786d 100644 --- a/packages/SystemUI/src/com/android/keyguard/KeyguardSimPukViewController.java +++ b/packages/SystemUI/src/com/android/keyguard/KeyguardSimPukViewController.java @@ -106,6 +106,14 @@ public class KeyguardSimPukViewController mKeyguardUpdateMonitor.removeCallback(mUpdateMonitorCallback); } + @Override + public void onResume(int reason) { + super.onResume(reason); + if (mShowDefaultMessage) { + showDefaultMessage(); + } + } + @Override void resetState() { super.resetState(); diff --git a/packages/SystemUI/src/com/android/systemui/dagger/SystemUIModule.java b/packages/SystemUI/src/com/android/systemui/dagger/SystemUIModule.java index 0469152de7766..443d2774f0e0f 100644 --- a/packages/SystemUI/src/com/android/systemui/dagger/SystemUIModule.java +++ b/packages/SystemUI/src/com/android/systemui/dagger/SystemUIModule.java @@ -41,6 +41,7 @@ import com.android.systemui.dreams.dagger.DreamModule; import com.android.systemui.dump.DumpManager; import com.android.systemui.flags.FlagsModule; import com.android.systemui.fragments.FragmentService; +import com.android.systemui.keyguard.data.BouncerViewModule; import com.android.systemui.log.dagger.LogModule; import com.android.systemui.media.dagger.MediaProjectionModule; import com.android.systemui.model.SysUiState; @@ -116,6 +117,7 @@ import dagger.Provides; AppOpsModule.class, AssistModule.class, BiometricsModule.class, + BouncerViewModule.class, ClockModule.class, CoroutinesModule.class, DreamModule.class, diff --git a/packages/SystemUI/src/com/android/systemui/dreams/DreamOverlayContainerViewController.java b/packages/SystemUI/src/com/android/systemui/dreams/DreamOverlayContainerViewController.java index d7b7777559da7..733a80dd7f69f 100644 --- a/packages/SystemUI/src/com/android/systemui/dreams/DreamOverlayContainerViewController.java +++ b/packages/SystemUI/src/com/android/systemui/dreams/DreamOverlayContainerViewController.java @@ -35,6 +35,7 @@ import com.android.systemui.dagger.qualifiers.Main; import com.android.systemui.dreams.complication.ComplicationHostViewController; import com.android.systemui.dreams.dagger.DreamOverlayComponent; import com.android.systemui.dreams.dagger.DreamOverlayModule; +import com.android.systemui.keyguard.domain.interactor.BouncerCallbackInteractor; import com.android.systemui.statusbar.BlurUtils; import com.android.systemui.statusbar.phone.KeyguardBouncer; import com.android.systemui.statusbar.phone.StatusBarKeyguardViewManager; @@ -73,6 +74,7 @@ public class DreamOverlayContainerViewController extends ViewController = WeakReference(null) + override var delegate: BouncerViewDelegate? + get() = _delegate.get() + set(value) { + _delegate = WeakReference(value) + } +} + +/** An abstraction that implements view logic. */ +interface BouncerViewDelegate { + fun isFullScreenBouncer(): Boolean + fun shouldDismissOnMenuPressed(): Boolean + fun interceptMediaKey(event: KeyEvent?): Boolean + fun dispatchBackKeyEventPreIme(): Boolean + fun showNextSecurityScreenOrFinish(): Boolean + fun resume() +} diff --git a/packages/SystemUI/src/com/android/systemui/keyguard/data/BouncerViewModule.kt b/packages/SystemUI/src/com/android/systemui/keyguard/data/BouncerViewModule.kt new file mode 100644 index 0000000000000..390c54e0350ab --- /dev/null +++ b/packages/SystemUI/src/com/android/systemui/keyguard/data/BouncerViewModule.kt @@ -0,0 +1,26 @@ +/* + * Copyright (C) 2022 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.android.systemui.keyguard.data + +import dagger.Binds +import dagger.Module + +@Module +interface BouncerViewModule { + /** Binds BouncerView to BouncerViewImpl and makes it injectable. */ + @Binds fun bindBouncerView(bouncerViewImpl: BouncerViewImpl): BouncerView +} diff --git a/packages/SystemUI/src/com/android/systemui/keyguard/data/repository/KeyguardBouncerRepository.kt b/packages/SystemUI/src/com/android/systemui/keyguard/data/repository/KeyguardBouncerRepository.kt new file mode 100644 index 0000000000000..543389e0a7cd0 --- /dev/null +++ b/packages/SystemUI/src/com/android/systemui/keyguard/data/repository/KeyguardBouncerRepository.kt @@ -0,0 +1,154 @@ +/* + * Copyright (C) 2022 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License + */ + +package com.android.systemui.keyguard.data.repository + +import android.hardware.biometrics.BiometricSourceType +import com.android.keyguard.KeyguardUpdateMonitor +import com.android.keyguard.KeyguardUpdateMonitorCallback +import com.android.keyguard.ViewMediatorCallback +import com.android.systemui.dagger.SysUISingleton +import com.android.systemui.keyguard.shared.model.BouncerCallbackActionsModel +import com.android.systemui.keyguard.shared.model.BouncerShowMessageModel +import com.android.systemui.keyguard.shared.model.KeyguardBouncerModel +import com.android.systemui.statusbar.phone.KeyguardBouncer.EXPANSION_HIDDEN +import javax.inject.Inject +import kotlinx.coroutines.flow.MutableStateFlow +import kotlinx.coroutines.flow.asStateFlow + +/** Encapsulates app state for the lock screen bouncer. */ +@SysUISingleton +class KeyguardBouncerRepository +@Inject +constructor( + private val viewMediatorCallback: ViewMediatorCallback, + keyguardUpdateMonitor: KeyguardUpdateMonitor, +) { + var bouncerPromptReason: Int? = null + /** Determines if we want to instantaneously show the bouncer instead of translating. */ + private val _isScrimmed = MutableStateFlow(false) + val isScrimmed = _isScrimmed.asStateFlow() + /** Set amount of how much of the bouncer is showing on the screen */ + private val _expansionAmount = MutableStateFlow(EXPANSION_HIDDEN) + val expansionAmount = _expansionAmount.asStateFlow() + private val _isVisible = MutableStateFlow(false) + val isVisible = _isVisible.asStateFlow() + private val _show = MutableStateFlow(null) + val show = _show.asStateFlow() + private val _showingSoon = MutableStateFlow(false) + val showingSoon = _showingSoon.asStateFlow() + private val _hide = MutableStateFlow(false) + val hide = _hide.asStateFlow() + private val _startingToHide = MutableStateFlow(false) + val startingToHide = _startingToHide.asStateFlow() + private val _onDismissAction = MutableStateFlow(null) + val onDismissAction = _onDismissAction.asStateFlow() + private val _disappearAnimation = MutableStateFlow(null) + val startingDisappearAnimation = _disappearAnimation.asStateFlow() + private val _keyguardPosition = MutableStateFlow(0f) + val keyguardPosition = _keyguardPosition.asStateFlow() + private val _resourceUpdateRequests = MutableStateFlow(false) + val resourceUpdateRequests = _resourceUpdateRequests.asStateFlow() + private val _showMessage = MutableStateFlow(null) + val showMessage = _showMessage.asStateFlow() + private val _keyguardAuthenticated = MutableStateFlow(null) + /** Determines if user is already unlocked */ + val keyguardAuthenticated = _keyguardAuthenticated.asStateFlow() + private val _isBackButtonEnabled = MutableStateFlow(null) + val isBackButtonEnabled = _isBackButtonEnabled.asStateFlow() + private val _onScreenTurnedOff = MutableStateFlow(false) + val onScreenTurnedOff = _onScreenTurnedOff.asStateFlow() + + val bouncerErrorMessage: CharSequence? + get() = viewMediatorCallback.consumeCustomMessage() + + init { + val callback = + object : KeyguardUpdateMonitorCallback() { + override fun onStrongAuthStateChanged(userId: Int) { + bouncerPromptReason = viewMediatorCallback.bouncerPromptReason + } + + override fun onLockedOutStateChanged(type: BiometricSourceType) { + if (type == BiometricSourceType.FINGERPRINT) { + bouncerPromptReason = viewMediatorCallback.bouncerPromptReason + } + } + } + + keyguardUpdateMonitor.registerCallback(callback) + } + + fun setScrimmed(isScrimmed: Boolean) { + _isScrimmed.value = isScrimmed + } + + fun setExpansion(expansion: Float) { + _expansionAmount.value = expansion + } + + fun setVisible(isVisible: Boolean) { + _isVisible.value = isVisible + } + + fun setShow(keyguardBouncerModel: KeyguardBouncerModel?) { + _show.value = keyguardBouncerModel + } + + fun setShowingSoon(showingSoon: Boolean) { + _showingSoon.value = showingSoon + } + + fun setHide(hide: Boolean) { + _hide.value = hide + } + + fun setStartingToHide(startingToHide: Boolean) { + _startingToHide.value = startingToHide + } + + fun setOnDismissAction(bouncerCallbackActionsModel: BouncerCallbackActionsModel?) { + _onDismissAction.value = bouncerCallbackActionsModel + } + + fun setStartDisappearAnimation(runnable: Runnable?) { + _disappearAnimation.value = runnable + } + + fun setKeyguardPosition(keyguardPosition: Float) { + _keyguardPosition.value = keyguardPosition + } + + fun setResourceUpdateRequests(willUpdateResources: Boolean) { + _resourceUpdateRequests.value = willUpdateResources + } + + fun setShowMessage(bouncerShowMessageModel: BouncerShowMessageModel?) { + _showMessage.value = bouncerShowMessageModel + } + + fun setKeyguardAuthenticated(keyguardAuthenticated: Boolean?) { + _keyguardAuthenticated.value = keyguardAuthenticated + } + + fun setIsBackButtonEnabled(isBackButtonEnabled: Boolean) { + _isBackButtonEnabled.value = isBackButtonEnabled + } + + fun setOnScreenTurnedOff(onScreenTurnedOff: Boolean) { + _onScreenTurnedOff.value = onScreenTurnedOff + } +} diff --git a/packages/SystemUI/src/com/android/systemui/keyguard/domain/interactor/BouncerCallbackInteractor.kt b/packages/SystemUI/src/com/android/systemui/keyguard/domain/interactor/BouncerCallbackInteractor.kt new file mode 100644 index 0000000000000..10c7a3774e090 --- /dev/null +++ b/packages/SystemUI/src/com/android/systemui/keyguard/domain/interactor/BouncerCallbackInteractor.kt @@ -0,0 +1,102 @@ +/* + * Copyright (C) 2022 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License + */ + +package com.android.systemui.keyguard.domain.interactor + +import android.view.View +import com.android.systemui.dagger.SysUISingleton +import com.android.systemui.statusbar.phone.KeyguardBouncer +import com.android.systemui.util.ListenerSet +import javax.inject.Inject + +/** Interactor to add and remove callbacks for the bouncer. */ +@SysUISingleton +class BouncerCallbackInteractor @Inject constructor() { + private var resetCallbacks = ListenerSet() + private var expansionCallbacks = ArrayList() + /** Add a KeyguardResetCallback. */ + fun addKeyguardResetCallback(callback: KeyguardBouncer.KeyguardResetCallback) { + resetCallbacks.addIfAbsent(callback) + } + + /** Remove a KeyguardResetCallback. */ + fun removeKeyguardResetCallback(callback: KeyguardBouncer.KeyguardResetCallback) { + resetCallbacks.remove(callback) + } + + /** Adds a callback to listen to bouncer expansion updates. */ + fun addBouncerExpansionCallback(callback: KeyguardBouncer.BouncerExpansionCallback) { + if (!expansionCallbacks.contains(callback)) { + expansionCallbacks.add(callback) + } + } + + /** + * Removes a previously added callback. If the callback was never added, this method does + * nothing. + */ + fun removeBouncerExpansionCallback(callback: KeyguardBouncer.BouncerExpansionCallback) { + expansionCallbacks.remove(callback) + } + + /** Propagate fully shown to bouncer expansion callbacks. */ + fun dispatchFullyShown() { + for (callback in expansionCallbacks) { + callback.onFullyShown() + } + } + + /** Propagate starting to hide to bouncer expansion callbacks. */ + fun dispatchStartingToHide() { + for (callback in expansionCallbacks) { + callback.onStartingToHide() + } + } + + /** Propagate starting to show to bouncer expansion callbacks. */ + fun dispatchStartingToShow() { + for (callback in expansionCallbacks) { + callback.onStartingToShow() + } + } + + /** Propagate fully hidden to bouncer expansion callbacks. */ + fun dispatchFullyHidden() { + for (callback in expansionCallbacks) { + callback.onFullyHidden() + } + } + + /** Propagate expansion changes to bouncer expansion callbacks. */ + fun dispatchExpansionChanged(expansion: Float) { + for (callback in expansionCallbacks) { + callback.onExpansionChanged(expansion) + } + } + /** Propagate visibility changes to bouncer expansion callbacks. */ + fun dispatchVisibilityChanged(visibility: Int) { + for (callback in expansionCallbacks) { + callback.onVisibilityChanged(visibility == View.VISIBLE) + } + } + + /** Propagate keyguard reset. */ + fun dispatchReset() { + for (callback in resetCallbacks) { + callback.onKeyguardReset() + } + } +} diff --git a/packages/SystemUI/src/com/android/systemui/keyguard/domain/interactor/BouncerInteractor.kt b/packages/SystemUI/src/com/android/systemui/keyguard/domain/interactor/BouncerInteractor.kt new file mode 100644 index 0000000000000..7d4db37c6b0f4 --- /dev/null +++ b/packages/SystemUI/src/com/android/systemui/keyguard/domain/interactor/BouncerInteractor.kt @@ -0,0 +1,324 @@ +/* + * Copyright (C) 2022 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.android.systemui.keyguard.domain.interactor + +import android.content.res.ColorStateList +import android.os.Handler +import android.os.Trace +import android.os.UserHandle +import android.os.UserManager +import com.android.keyguard.KeyguardSecurityModel +import com.android.keyguard.KeyguardUpdateMonitor +import com.android.systemui.DejankUtils +import com.android.systemui.classifier.FalsingCollector +import com.android.systemui.dagger.SysUISingleton +import com.android.systemui.dagger.qualifiers.Main +import com.android.systemui.keyguard.DismissCallbackRegistry +import com.android.systemui.keyguard.data.BouncerView +import com.android.systemui.keyguard.data.repository.KeyguardBouncerRepository +import com.android.systemui.keyguard.shared.model.BouncerCallbackActionsModel +import com.android.systemui.keyguard.shared.model.BouncerShowMessageModel +import com.android.systemui.keyguard.shared.model.KeyguardBouncerModel +import com.android.systemui.plugins.ActivityStarter +import com.android.systemui.shared.system.SysUiStatsLog +import com.android.systemui.statusbar.phone.KeyguardBouncer +import com.android.systemui.statusbar.phone.KeyguardBypassController +import com.android.systemui.statusbar.policy.KeyguardStateController +import javax.inject.Inject +import kotlinx.coroutines.flow.Flow +import kotlinx.coroutines.flow.filter +import kotlinx.coroutines.flow.filterNotNull +import kotlinx.coroutines.flow.map + +/** Encapsulates business logic for interacting with the lock-screen bouncer. */ +@SysUISingleton +class BouncerInteractor +@Inject +constructor( + private val repository: KeyguardBouncerRepository, + private val bouncerView: BouncerView, + @Main private val mainHandler: Handler, + private val keyguardStateController: KeyguardStateController, + private val keyguardSecurityModel: KeyguardSecurityModel, + private val callbackInteractor: BouncerCallbackInteractor, + private val falsingCollector: FalsingCollector, + private val dismissCallbackRegistry: DismissCallbackRegistry, + keyguardBypassController: KeyguardBypassController, + keyguardUpdateMonitor: KeyguardUpdateMonitor, +) { + /** Whether we want to wait for face auth. */ + private val bouncerFaceDelay = + keyguardStateController.isFaceAuthEnabled && + !keyguardUpdateMonitor.getCachedIsUnlockWithFingerprintPossible( + KeyguardUpdateMonitor.getCurrentUser() + ) && + !needsFullscreenBouncer() && + !keyguardUpdateMonitor.userNeedsStrongAuth() && + !keyguardBypassController.bypassEnabled + + /** Runnable to show the bouncer. */ + val showRunnable = Runnable { + repository.setVisible(true) + repository.setShow( + KeyguardBouncerModel( + promptReason = repository.bouncerPromptReason ?: 0, + errorMessage = repository.bouncerErrorMessage, + expansionAmount = repository.expansionAmount.value + ) + ) + repository.setShowingSoon(false) + } + + val keyguardAuthenticated: Flow = repository.keyguardAuthenticated.filterNotNull() + val screenTurnedOff: Flow = repository.onScreenTurnedOff.filter { it }.map {} + val show: Flow = repository.show.filterNotNull() + val hide: Flow = repository.hide.filter { it }.map {} + val startingToHide: Flow = repository.startingToHide.filter { it }.map {} + val isVisible: Flow = repository.isVisible + val isBackButtonEnabled: Flow = repository.isBackButtonEnabled.filterNotNull() + val expansionAmount: Flow = repository.expansionAmount + val showMessage: Flow = repository.showMessage.filterNotNull() + val startingDisappearAnimation: Flow = + repository.startingDisappearAnimation.filterNotNull() + val onDismissAction: Flow = + repository.onDismissAction.filterNotNull() + val resourceUpdateRequests: Flow = repository.resourceUpdateRequests.filter { it } + val keyguardPosition: Flow = repository.keyguardPosition + + // TODO(b/243685699): Move isScrimmed logic to data layer. + // TODO(b/243695312): Encapsulate all of the show logic for the bouncer. + /** Show the bouncer if necessary and set the relevant states. */ + @JvmOverloads + fun show(isScrimmed: Boolean) { + // Reset some states as we show the bouncer. + repository.setShowMessage(null) + repository.setOnScreenTurnedOff(false) + repository.setKeyguardAuthenticated(null) + repository.setHide(false) + repository.setStartingToHide(false) + + val resumeBouncer = + (repository.isVisible.value || repository.showingSoon.value) && needsFullscreenBouncer() + + if (!resumeBouncer && repository.show.value != null) { + // If bouncer is visible, the bouncer is already showing. + return + } + + val keyguardUserId = KeyguardUpdateMonitor.getCurrentUser() + if (keyguardUserId == UserHandle.USER_SYSTEM && UserManager.isSplitSystemUser()) { + // In split system user mode, we never unlock system user. + return + } + + Trace.beginSection("KeyguardBouncer#show") + repository.setScrimmed(isScrimmed) + if (isScrimmed) { + setExpansion(KeyguardBouncer.EXPANSION_VISIBLE) + } + + if (resumeBouncer) { + bouncerView.delegate?.resume() + // Bouncer is showing the next security screen and we just need to prompt a resume. + return + } + if (bouncerView.delegate?.showNextSecurityScreenOrFinish() == true) { + // Keyguard is done. + return + } + + repository.setShowingSoon(true) + if (bouncerFaceDelay) { + mainHandler.postDelayed(showRunnable, 1200L) + } else { + DejankUtils.postAfterTraversal(showRunnable) + } + keyguardStateController.notifyBouncerShowing(true) + callbackInteractor.dispatchStartingToShow() + + Trace.endSection() + } + + /** Sets the correct bouncer states to hide the bouncer. */ + fun hide() { + Trace.beginSection("KeyguardBouncer#hide") + if (isFullyShowing()) { + SysUiStatsLog.write( + SysUiStatsLog.KEYGUARD_BOUNCER_STATE_CHANGED, + SysUiStatsLog.KEYGUARD_BOUNCER_STATE_CHANGED__STATE__HIDDEN + ) + dismissCallbackRegistry.notifyDismissCancelled() + } + + falsingCollector.onBouncerHidden() + keyguardStateController.notifyBouncerShowing(false /* showing */) + cancelShowRunnable() + repository.setShowingSoon(false) + repository.setOnDismissAction(null) + repository.setVisible(false) + repository.setHide(true) + repository.setShow(null) + Trace.endSection() + } + + /** + * Sets the panel expansion which is calculated further upstream. Expansion is from 0f to 1f + * where 0f => showing and 1f => hiding + */ + fun setExpansion(expansion: Float) { + val oldExpansion = repository.expansionAmount.value + val expansionChanged = oldExpansion != expansion + if (repository.startingDisappearAnimation.value == null) { + repository.setExpansion(expansion) + } + + if ( + expansion == KeyguardBouncer.EXPANSION_VISIBLE && + oldExpansion != KeyguardBouncer.EXPANSION_VISIBLE + ) { + falsingCollector.onBouncerShown() + callbackInteractor.dispatchFullyShown() + } else if ( + expansion == KeyguardBouncer.EXPANSION_HIDDEN && + oldExpansion != KeyguardBouncer.EXPANSION_HIDDEN + ) { + repository.setVisible(false) + repository.setShow(null) + falsingCollector.onBouncerHidden() + DejankUtils.postAfterTraversal { callbackInteractor.dispatchReset() } + callbackInteractor.dispatchFullyHidden() + } else if ( + expansion != KeyguardBouncer.EXPANSION_VISIBLE && + oldExpansion == KeyguardBouncer.EXPANSION_VISIBLE + ) { + callbackInteractor.dispatchStartingToHide() + repository.setStartingToHide(true) + } + if (expansionChanged) { + callbackInteractor.dispatchExpansionChanged(expansion) + } + } + + /** Set the initial keyguard message to show when bouncer is shown. */ + fun showMessage(message: String?, colorStateList: ColorStateList?) { + repository.setShowMessage(BouncerShowMessageModel(message, colorStateList)) + } + + /** + * Sets actions to the bouncer based on how the bouncer is dismissed. If the bouncer is + * unlocked, we will run the onDismissAction. If the bouncer is existed before unlocking, we + * call cancelAction. + */ + fun setDismissAction( + onDismissAction: ActivityStarter.OnDismissAction?, + cancelAction: Runnable? + ) { + repository.setOnDismissAction(BouncerCallbackActionsModel(onDismissAction, cancelAction)) + } + + /** Update the resources of the views. */ + fun updateResources() { + repository.setResourceUpdateRequests(true) + } + + /** Tell the bouncer that keyguard is authenticated. */ + fun notifyKeyguardAuthenticated(strongAuth: Boolean) { + repository.setKeyguardAuthenticated(strongAuth) + } + + /** Tell the bouncer the screen has turned off. */ + fun onScreenTurnedOff() { + repository.setOnScreenTurnedOff(true) + } + + /** Update the position of the bouncer when showing. */ + fun setKeyguardPosition(position: Float) { + repository.setKeyguardPosition(position) + } + + /** Notifies that the state change was handled. */ + fun notifyKeyguardAuthenticatedHandled() { + repository.setKeyguardAuthenticated(null) + } + + /** Notify that view visibility has changed. */ + fun notifyBouncerVisibilityHasChanged(visibility: Int) { + callbackInteractor.dispatchVisibilityChanged(visibility) + } + + /** Notify that the resources have been updated */ + fun notifyUpdatedResources() { + repository.setResourceUpdateRequests(false) + } + + /** Set whether back button is enabled when on the bouncer screen. */ + fun setBackButtonEnabled(enabled: Boolean) { + repository.setIsBackButtonEnabled(enabled) + } + + /** Tell the bouncer to start the pre hide animation. */ + fun startDisappearAnimation(runnable: Runnable) { + val finishRunnable = Runnable { + repository.setStartDisappearAnimation(null) + runnable.run() + } + repository.setStartDisappearAnimation(finishRunnable) + } + + /** Returns whether bouncer is fully showing. */ + fun isFullyShowing(): Boolean { + return (repository.showingSoon.value || repository.isVisible.value) && + repository.expansionAmount.value == KeyguardBouncer.EXPANSION_VISIBLE && + repository.startingDisappearAnimation.value == null + } + + /** Returns whether bouncer is scrimmed. */ + fun isScrimmed(): Boolean { + return repository.isScrimmed.value + } + + /** If bouncer expansion is between 0f and 1f non-inclusive. */ + fun isInTransit(): Boolean { + return repository.showingSoon.value || + repository.expansionAmount.value != KeyguardBouncer.EXPANSION_HIDDEN && + repository.expansionAmount.value != KeyguardBouncer.EXPANSION_VISIBLE + } + + /** Return whether bouncer is animating away. */ + fun isAnimatingAway(): Boolean { + return repository.startingDisappearAnimation.value != null + } + + /** Return whether bouncer will dismiss with actions */ + fun willDismissWithAction(): Boolean { + return repository.onDismissAction.value?.onDismissAction != null + } + + /** Returns whether the bouncer should be full screen. */ + private fun needsFullscreenBouncer(): Boolean { + val mode: KeyguardSecurityModel.SecurityMode = + keyguardSecurityModel.getSecurityMode(KeyguardUpdateMonitor.getCurrentUser()) + return mode == KeyguardSecurityModel.SecurityMode.SimPin || + mode == KeyguardSecurityModel.SecurityMode.SimPuk + } + + /** Remove the show runnable from the main handler queue to improve performance. */ + private fun cancelShowRunnable() { + DejankUtils.removeCallbacks(showRunnable) + mainHandler.removeCallbacks(showRunnable) + } +} diff --git a/packages/SystemUI/src/com/android/systemui/keyguard/shared/model/BouncerCallbackActionsModel.kt b/packages/SystemUI/src/com/android/systemui/keyguard/shared/model/BouncerCallbackActionsModel.kt new file mode 100644 index 0000000000000..81cf5b41ea712 --- /dev/null +++ b/packages/SystemUI/src/com/android/systemui/keyguard/shared/model/BouncerCallbackActionsModel.kt @@ -0,0 +1,26 @@ +/* + * Copyright (C) 2022 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License + */ + +package com.android.systemui.keyguard.shared.model + +import com.android.systemui.plugins.ActivityStarter + +/** Encapsulates callbacks to be invoked by the bouncer logic. */ +// TODO(b/243683121): Move dismiss logic from view controllers +data class BouncerCallbackActionsModel( + val onDismissAction: ActivityStarter.OnDismissAction?, + val cancelAction: Runnable? +) diff --git a/packages/SystemUI/src/com/android/systemui/keyguard/shared/model/BouncerShowMessageModel.kt b/packages/SystemUI/src/com/android/systemui/keyguard/shared/model/BouncerShowMessageModel.kt new file mode 100644 index 0000000000000..05cdeaaa106d8 --- /dev/null +++ b/packages/SystemUI/src/com/android/systemui/keyguard/shared/model/BouncerShowMessageModel.kt @@ -0,0 +1,22 @@ +/* + * Copyright (C) 2022 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License + */ + +package com.android.systemui.keyguard.shared.model + +import android.content.res.ColorStateList + +/** Show a keyguard message to the bouncer. */ +data class BouncerShowMessageModel(val message: String?, val colorStateList: ColorStateList?) diff --git a/packages/SystemUI/src/com/android/systemui/keyguard/shared/model/KeyguardBouncerModel.kt b/packages/SystemUI/src/com/android/systemui/keyguard/shared/model/KeyguardBouncerModel.kt new file mode 100644 index 0000000000000..ad783da7f304d --- /dev/null +++ b/packages/SystemUI/src/com/android/systemui/keyguard/shared/model/KeyguardBouncerModel.kt @@ -0,0 +1,24 @@ +/* + * Copyright (C) 2022 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License + */ + +package com.android.systemui.keyguard.shared.model + +/** Models the state of the lock-screen bouncer */ +data class KeyguardBouncerModel( + val promptReason: Int = 0, + val errorMessage: CharSequence? = null, + val expansionAmount: Float = 0f, +) diff --git a/packages/SystemUI/src/com/android/systemui/keyguard/ui/binder/KeyguardBouncerViewBinder.kt b/packages/SystemUI/src/com/android/systemui/keyguard/ui/binder/KeyguardBouncerViewBinder.kt new file mode 100644 index 0000000000000..df260148751cb --- /dev/null +++ b/packages/SystemUI/src/com/android/systemui/keyguard/ui/binder/KeyguardBouncerViewBinder.kt @@ -0,0 +1,220 @@ +/* + * Copyright (C) 2022 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License + */ + +package com.android.systemui.keyguard.ui.binder + +import android.view.KeyEvent +import android.view.View +import android.view.ViewGroup +import androidx.lifecycle.Lifecycle +import androidx.lifecycle.repeatOnLifecycle +import com.android.internal.policy.SystemBarUtils +import com.android.keyguard.KeyguardHostViewController +import com.android.keyguard.KeyguardSecurityModel +import com.android.keyguard.KeyguardUpdateMonitor +import com.android.keyguard.dagger.KeyguardBouncerComponent +import com.android.systemui.keyguard.data.BouncerViewDelegate +import com.android.systemui.keyguard.ui.viewmodel.KeyguardBouncerViewModel +import com.android.systemui.lifecycle.repeatWhenAttached +import com.android.systemui.statusbar.phone.KeyguardBouncer.EXPANSION_VISIBLE +import kotlinx.coroutines.awaitCancellation +import kotlinx.coroutines.flow.collect +import kotlinx.coroutines.flow.filter +import kotlinx.coroutines.launch + +/** Binds the bouncer container to its view model. */ +object KeyguardBouncerViewBinder { + @JvmStatic + fun bind( + view: ViewGroup, + viewModel: KeyguardBouncerViewModel, + componentFactory: KeyguardBouncerComponent.Factory + ) { + // Builds the KeyguardHostViewController from bouncer view group. + val hostViewController: KeyguardHostViewController = + componentFactory.create(view).keyguardHostViewController + hostViewController.init() + val delegate = + object : BouncerViewDelegate { + override fun isFullScreenBouncer(): Boolean { + val mode = hostViewController.currentSecurityMode + return mode == KeyguardSecurityModel.SecurityMode.SimPin || + mode == KeyguardSecurityModel.SecurityMode.SimPuk + } + + override fun shouldDismissOnMenuPressed(): Boolean { + return hostViewController.shouldEnableMenuKey() + } + + override fun interceptMediaKey(event: KeyEvent?): Boolean { + return hostViewController.interceptMediaKey(event) + } + + override fun dispatchBackKeyEventPreIme(): Boolean { + return hostViewController.dispatchBackKeyEventPreIme() + } + + override fun showNextSecurityScreenOrFinish(): Boolean { + return hostViewController.dismiss(KeyguardUpdateMonitor.getCurrentUser()) + } + + override fun resume() { + hostViewController.showPrimarySecurityScreen() + hostViewController.onResume() + } + } + view.repeatWhenAttached { + repeatOnLifecycle(Lifecycle.State.STARTED) { + try { + viewModel.setBouncerViewDelegate(delegate) + launch { + viewModel.show.collect { + hostViewController.showPrimarySecurityScreen() + hostViewController.appear( + SystemBarUtils.getStatusBarHeight(view.context) + ) + } + } + + launch { + viewModel.showPromptReason.collect { prompt -> + hostViewController.showPromptReason(prompt) + } + } + + launch { + viewModel.showBouncerErrorMessage.collect { errorMessage -> + hostViewController.showErrorMessage(errorMessage) + } + } + + launch { + viewModel.showWithFullExpansion.collect { model -> + hostViewController.resetSecurityContainer() + hostViewController.showPromptReason(model.promptReason) + hostViewController.onResume() + } + } + + launch { + viewModel.hide.collect { + hostViewController.cancelDismissAction() + hostViewController.cleanUp() + hostViewController.resetSecurityContainer() + } + } + + launch { + viewModel.startingToHide.collect { hostViewController.onStartingToHide() } + } + + launch { + viewModel.setDismissAction.collect { + hostViewController.setOnDismissAction( + it.onDismissAction, + it.cancelAction + ) + } + } + + launch { + viewModel.startDisappearAnimation.collect { + hostViewController.startDisappearAnimation(it) + } + } + + launch { + viewModel.bouncerExpansionAmount.collect { expansion -> + hostViewController.setExpansion(expansion) + } + } + + launch { + viewModel.bouncerExpansionAmount + .filter { it == EXPANSION_VISIBLE } + .collect { + hostViewController.onResume() + view.announceForAccessibility( + hostViewController.accessibilityTitleForCurrentMode + ) + } + } + + launch { + viewModel.isBouncerVisible.collect { isVisible -> + val visibility = if (isVisible) View.VISIBLE else View.INVISIBLE + view.visibility = visibility + hostViewController.onBouncerVisibilityChanged(visibility) + viewModel.notifyBouncerVisibilityHasChanged(visibility) + } + } + + launch { + viewModel.isBouncerVisible + .filter { !it } + .collect { + // Remove existing input for security reasons. + hostViewController.resetSecurityContainer() + } + } + + launch { + viewModel.keyguardPosition.collect { position -> + hostViewController.updateKeyguardPosition(position) + } + } + + launch { + viewModel.updateResources.collect { + hostViewController.updateResources() + viewModel.notifyUpdateResources() + } + } + + launch { + viewModel.bouncerShowMessage.collect { + hostViewController.showMessage(it.message, it.colorStateList) + } + } + + launch { + viewModel.keyguardAuthenticated.collect { + hostViewController.finish(it, KeyguardUpdateMonitor.getCurrentUser()) + viewModel.notifyKeyguardAuthenticated() + } + } + + launch { + viewModel + .observeOnIsBackButtonEnabled { view.systemUiVisibility } + .collect { view.systemUiVisibility = it } + } + + launch { + viewModel.screenTurnedOff.collect { + if (view.visibility == View.VISIBLE) { + hostViewController.onPause() + } + } + } + awaitCancellation() + } finally { + viewModel.setBouncerViewDelegate(null) + } + } + } + } +} diff --git a/packages/SystemUI/src/com/android/systemui/keyguard/ui/viewmodel/KeyguardBouncerViewModel.kt b/packages/SystemUI/src/com/android/systemui/keyguard/ui/viewmodel/KeyguardBouncerViewModel.kt new file mode 100644 index 0000000000000..9ad52117bfc62 --- /dev/null +++ b/packages/SystemUI/src/com/android/systemui/keyguard/ui/viewmodel/KeyguardBouncerViewModel.kt @@ -0,0 +1,119 @@ +/* + * Copyright (C) 2022 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License + */ + +package com.android.systemui.keyguard.ui.viewmodel + +import android.view.View +import com.android.systemui.keyguard.data.BouncerView +import com.android.systemui.keyguard.data.BouncerViewDelegate +import com.android.systemui.keyguard.domain.interactor.BouncerInteractor +import com.android.systemui.keyguard.shared.model.BouncerCallbackActionsModel +import com.android.systemui.keyguard.shared.model.BouncerShowMessageModel +import com.android.systemui.keyguard.shared.model.KeyguardBouncerModel +import com.android.systemui.statusbar.phone.KeyguardBouncer.EXPANSION_VISIBLE +import javax.inject.Inject +import kotlinx.coroutines.flow.Flow +import kotlinx.coroutines.flow.filter +import kotlinx.coroutines.flow.filterNotNull +import kotlinx.coroutines.flow.map + +/** Models UI state for the lock screen bouncer; handles user input. */ +class KeyguardBouncerViewModel +@Inject +constructor( + private val view: BouncerView, + private val interactor: BouncerInteractor, +) { + /** Observe on bouncer expansion amount. */ + val bouncerExpansionAmount: Flow = interactor.expansionAmount + + /** Observe on bouncer visibility. */ + val isBouncerVisible: Flow = interactor.isVisible + + /** Observe whether bouncer is showing. */ + val show: Flow = interactor.show + + /** Observe bouncer prompt when bouncer is showing. */ + val showPromptReason: Flow = interactor.show.map { it.promptReason } + + /** Observe bouncer error message when bouncer is showing. */ + val showBouncerErrorMessage: Flow = + interactor.show.map { it.errorMessage }.filterNotNull() + + /** Observe visible expansion when bouncer is showing. */ + val showWithFullExpansion: Flow = + interactor.show.filter { it.expansionAmount == EXPANSION_VISIBLE } + + /** Observe whether bouncer is hiding. */ + val hide: Flow = interactor.hide + + /** Observe whether bouncer is starting to hide. */ + val startingToHide: Flow = interactor.startingToHide + + /** Observe whether we want to set the dismiss action to the bouncer. */ + val setDismissAction: Flow = interactor.onDismissAction + + /** Observe whether we want to start the disappear animation. */ + val startDisappearAnimation: Flow = interactor.startingDisappearAnimation + + /** Observe whether we want to update keyguard position. */ + val keyguardPosition: Flow = interactor.keyguardPosition + + /** Observe whether we want to update resources. */ + val updateResources: Flow = interactor.resourceUpdateRequests + + /** Observe whether we want to set a keyguard message when the bouncer shows. */ + val bouncerShowMessage: Flow = interactor.showMessage + + /** Observe whether keyguard is authenticated already. */ + val keyguardAuthenticated: Flow = interactor.keyguardAuthenticated + + /** Observe whether screen is turned off. */ + val screenTurnedOff: Flow = interactor.screenTurnedOff + + /** Notify that view visibility has changed. */ + fun notifyBouncerVisibilityHasChanged(visibility: Int) { + return interactor.notifyBouncerVisibilityHasChanged(visibility) + } + /** Observe whether we want to update resources. */ + fun notifyUpdateResources() { + interactor.notifyUpdatedResources() + } + + /** Notify that keyguard authenticated was handled */ + fun notifyKeyguardAuthenticated() { + interactor.notifyKeyguardAuthenticatedHandled() + } + + /** Observe whether back button is enabled. */ + fun observeOnIsBackButtonEnabled(systemUiVisibility: () -> Int): Flow { + return interactor.isBackButtonEnabled.map { enabled -> + var vis: Int = systemUiVisibility() + vis = + if (enabled) { + vis and View.STATUS_BAR_DISABLE_BACK.inv() + } else { + vis or View.STATUS_BAR_DISABLE_BACK + } + vis + } + } + + /** Set an abstraction that will hold reference to the ui delegate for the bouncer view. */ + fun setBouncerViewDelegate(delegate: BouncerViewDelegate?) { + view.delegate = delegate + } +} diff --git a/packages/SystemUI/src/com/android/systemui/shade/NotificationShadeWindowViewController.java b/packages/SystemUI/src/com/android/systemui/shade/NotificationShadeWindowViewController.java index 8d74a09ab8b15..6be9bbbf4e0d8 100644 --- a/packages/SystemUI/src/com/android/systemui/shade/NotificationShadeWindowViewController.java +++ b/packages/SystemUI/src/com/android/systemui/shade/NotificationShadeWindowViewController.java @@ -31,10 +31,15 @@ import android.view.ViewGroup; import com.android.internal.annotations.VisibleForTesting; import com.android.keyguard.AuthKeyguardMessageArea; import com.android.keyguard.LockIconViewController; +import com.android.keyguard.dagger.KeyguardBouncerComponent; import com.android.systemui.R; import com.android.systemui.classifier.FalsingCollector; import com.android.systemui.dock.DockManager; +import com.android.systemui.flags.FeatureFlags; +import com.android.systemui.flags.Flags; import com.android.systemui.keyguard.KeyguardUnlockAnimationController; +import com.android.systemui.keyguard.ui.binder.KeyguardBouncerViewBinder; +import com.android.systemui.keyguard.ui.viewmodel.KeyguardBouncerViewModel; import com.android.systemui.statusbar.DragDownHelper; import com.android.systemui.statusbar.LockscreenShadeTransitionController; import com.android.systemui.statusbar.NotificationShadeDepthController; @@ -108,7 +113,10 @@ public class NotificationShadeWindowViewController { NotificationShadeWindowController controller, KeyguardUnlockAnimationController keyguardUnlockAnimationController, AmbientState ambientState, - PulsingGestureListener pulsingGestureListener + PulsingGestureListener pulsingGestureListener, + FeatureFlags featureFlags, + KeyguardBouncerViewModel keyguardBouncerViewModel, + KeyguardBouncerComponent.Factory keyguardBouncerComponentFactory ) { mLockscreenShadeTransitionController = transitionController; mFalsingCollector = falsingCollector; @@ -130,6 +138,12 @@ public class NotificationShadeWindowViewController { // This view is not part of the newly inflated expanded status bar. mBrightnessMirror = mView.findViewById(R.id.brightness_mirror_container); + if (featureFlags.isEnabled(Flags.MODERN_BOUNCER)) { + KeyguardBouncerViewBinder.bind( + mView.findViewById(R.id.keyguard_bouncer_container), + keyguardBouncerViewModel, + keyguardBouncerComponentFactory); + } } /** diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/KeyguardBouncer.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/KeyguardBouncer.java index d380e9f03720c..d61c51e76e86e 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/KeyguardBouncer.java +++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/KeyguardBouncer.java @@ -56,7 +56,9 @@ import javax.inject.Inject; /** * A class which manages the bouncer on the lockscreen. + * @deprecated Use KeyguardBouncerRepository */ +@Deprecated public class KeyguardBouncer { private static final String TAG = "KeyguardBouncer"; diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarKeyguardViewManager.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarKeyguardViewManager.java index 7867147165962..e61794b0243e8 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarKeyguardViewManager.java +++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarKeyguardViewManager.java @@ -46,6 +46,7 @@ import com.android.internal.util.LatencyTracker; import com.android.internal.widget.LockPatternUtils; import com.android.keyguard.AuthKeyguardMessageArea; import com.android.keyguard.KeyguardMessageAreaController; +import com.android.keyguard.KeyguardSecurityModel; import com.android.keyguard.KeyguardUpdateMonitor; import com.android.keyguard.KeyguardUpdateMonitorCallback; import com.android.keyguard.KeyguardViewController; @@ -53,6 +54,12 @@ import com.android.keyguard.ViewMediatorCallback; import com.android.systemui.dagger.SysUISingleton; import com.android.systemui.dock.DockManager; import com.android.systemui.dreams.DreamOverlayStateController; +import com.android.systemui.flags.FeatureFlags; +import com.android.systemui.flags.Flags; +import com.android.systemui.keyguard.data.BouncerView; +import com.android.systemui.keyguard.data.BouncerViewDelegate; +import com.android.systemui.keyguard.domain.interactor.BouncerCallbackInteractor; +import com.android.systemui.keyguard.domain.interactor.BouncerInteractor; import com.android.systemui.navigationbar.NavigationBarView; import com.android.systemui.navigationbar.NavigationModeController; import com.android.systemui.plugins.statusbar.StatusBarStateController; @@ -123,6 +130,9 @@ public class StatusBarKeyguardViewManager implements RemoteInputController.Callb @Nullable private final FoldAodAnimationController mFoldAodAnimationController; private KeyguardMessageAreaController mKeyguardMessageAreaController; + private final BouncerCallbackInteractor mBouncerCallbackInteractor; + private final BouncerInteractor mBouncerInteractor; + private final BouncerViewDelegate mBouncerViewDelegate; private final Lazy mShadeController; private final BouncerExpansionCallback mExpansionCallback = new BouncerExpansionCallback() { @@ -197,7 +207,7 @@ public class StatusBarKeyguardViewManager implements RemoteInputController.Callb private View mNotificationContainer; - protected KeyguardBouncer mBouncer; + @Nullable protected KeyguardBouncer mBouncer; protected boolean mShowing; protected boolean mOccluded; protected boolean mRemoteInputActive; @@ -223,6 +233,7 @@ public class StatusBarKeyguardViewManager implements RemoteInputController.Callb private int mLastBiometricMode; private boolean mLastScreenOffAnimationPlaying; private float mQsExpansion; + private boolean mIsModernBouncerEnabled; private OnDismissAction mAfterKeyguardGoneAction; private Runnable mKeyguardGoneCancelAction; @@ -237,6 +248,7 @@ public class StatusBarKeyguardViewManager implements RemoteInputController.Callb private final DockManager mDockManager; private final KeyguardUpdateMonitor mKeyguardUpdateManager; private final LatencyTracker mLatencyTracker; + private final KeyguardSecurityModel mKeyguardSecurityModel; private KeyguardBypassController mBypassController; @Nullable private AlternateAuthInterceptor mAlternateAuthInterceptor; @@ -271,7 +283,12 @@ public class StatusBarKeyguardViewManager implements RemoteInputController.Callb KeyguardMessageAreaController.Factory keyguardMessageAreaFactory, Optional sysUIUnfoldComponent, Lazy shadeController, - LatencyTracker latencyTracker) { + LatencyTracker latencyTracker, + KeyguardSecurityModel keyguardSecurityModel, + FeatureFlags featureFlags, + BouncerCallbackInteractor bouncerCallbackInteractor, + BouncerInteractor bouncerInteractor, + BouncerView bouncerView) { mContext = context; mViewMediatorCallback = callback; mLockPatternUtils = lockPatternUtils; @@ -288,8 +305,13 @@ public class StatusBarKeyguardViewManager implements RemoteInputController.Callb mKeyguardMessageAreaFactory = keyguardMessageAreaFactory; mShadeController = shadeController; mLatencyTracker = latencyTracker; + mKeyguardSecurityModel = keyguardSecurityModel; + mBouncerCallbackInteractor = bouncerCallbackInteractor; + mBouncerInteractor = bouncerInteractor; + mBouncerViewDelegate = bouncerView.getDelegate(); mFoldAodAnimationController = sysUIUnfoldComponent .map(SysUIUnfoldComponent::getFoldAodAnimationController).orElse(null); + mIsModernBouncerEnabled = featureFlags.isEnabled(Flags.MODERN_BOUNCER); } @Override @@ -303,7 +325,11 @@ public class StatusBarKeyguardViewManager implements RemoteInputController.Callb mBiometricUnlockController = biometricUnlockController; ViewGroup container = mCentralSurfaces.getBouncerContainer(); - mBouncer = mKeyguardBouncerFactory.create(container, mExpansionCallback); + if (mIsModernBouncerEnabled) { + mBouncerCallbackInteractor.addBouncerExpansionCallback(mExpansionCallback); + } else { + mBouncer = mKeyguardBouncerFactory.create(container, mExpansionCallback); + } mNotificationPanelViewController = notificationPanelViewController; if (panelExpansionStateManager != null) { panelExpansionStateManager.addExpansionListener(this); @@ -377,29 +403,45 @@ public class StatusBarKeyguardViewManager implements RemoteInputController.Callb if (mDozing && !mPulsing) { return; } else if (mNotificationPanelViewController.isUnlockHintRunning()) { - mBouncer.setExpansion(KeyguardBouncer.EXPANSION_HIDDEN); + if (mBouncer != null) { + mBouncer.setExpansion(KeyguardBouncer.EXPANSION_HIDDEN); + } + mBouncerInteractor.setExpansion(KeyguardBouncer.EXPANSION_HIDDEN); } else if (mStatusBarStateController.getState() == StatusBarState.SHADE_LOCKED) { // Don't expand to the bouncer. Instead transition back to the lock screen (see // CentralSurfaces#showBouncerOrLockScreenIfKeyguard) return; } else if (bouncerNeedsScrimming()) { - mBouncer.setExpansion(KeyguardBouncer.EXPANSION_VISIBLE); + if (mBouncer != null) { + mBouncer.setExpansion(KeyguardBouncer.EXPANSION_VISIBLE); + } + mBouncerInteractor.setExpansion(KeyguardBouncer.EXPANSION_VISIBLE); } else if (mShowing && !hideBouncerOverDream) { if (!isWakeAndUnlocking() && !(mBiometricUnlockController.getMode() == MODE_DISMISS_BOUNCER) && !mCentralSurfaces.isInLaunchTransition() && !isUnlockCollapsing()) { - mBouncer.setExpansion(fraction); + if (mBouncer != null) { + mBouncer.setExpansion(fraction); + } + mBouncerInteractor.setExpansion(fraction); } if (fraction != KeyguardBouncer.EXPANSION_HIDDEN && tracking && !mKeyguardStateController.canDismissLockScreen() - && !mBouncer.isShowing() && !mBouncer.isAnimatingAway()) { - mBouncer.show(false /* resetSecuritySelection */, false /* scrimmed */); + && !bouncerIsShowing() + && !bouncerIsAnimatingAway()) { + if (mBouncer != null) { + mBouncer.show(false /* resetSecuritySelection */, false /* scrimmed */); + } + mBouncerInteractor.show(/* isScrimmed= */false); } - } else if (!mShowing && mBouncer.inTransit()) { + } else if (!mShowing && isBouncerInTransit()) { // Keyguard is not visible anymore, but expansion animation was still running. // We need to hide the bouncer, otherwise it will be stuck in transit. - mBouncer.setExpansion(KeyguardBouncer.EXPANSION_HIDDEN); + if (mBouncer != null) { + mBouncer.setExpansion(KeyguardBouncer.EXPANSION_HIDDEN); + } + mBouncerInteractor.setExpansion(KeyguardBouncer.EXPANSION_HIDDEN); } else if (mPulsing && fraction == KeyguardBouncer.EXPANSION_VISIBLE) { // Panel expanded while pulsing but didn't translate the bouncer (because we are // unlocked.) Let's simply wake-up to dismiss the lock screen. @@ -440,15 +482,20 @@ public class StatusBarKeyguardViewManager implements RemoteInputController.Callb * {@link KeyguardBouncer#needsFullscreenBouncer()}. */ protected void showBouncerOrKeyguard(boolean hideBouncerWhenShowing) { - if (mBouncer.needsFullscreenBouncer() && !mDozing) { + if (needsFullscreenBouncer() && !mDozing) { // The keyguard might be showing (already). So we need to hide it. mCentralSurfaces.hideKeyguard(); - mBouncer.show(true /* resetSecuritySelection */); + if (mBouncer != null) { + mBouncer.show(true /* resetSecuritySelection */); + } + mBouncerInteractor.show(true); } else { mCentralSurfaces.showKeyguard(); if (hideBouncerWhenShowing) { hideBouncer(false /* destroyView */); - mBouncer.prepare(); + if (mBouncer != null) { + mBouncer.prepare(); + } } } updateStates(); @@ -480,10 +527,10 @@ public class StatusBarKeyguardViewManager implements RemoteInputController.Callb */ @VisibleForTesting void hideBouncer(boolean destroyView) { - if (mBouncer == null) { - return; + if (mBouncer != null) { + mBouncer.hide(destroyView); } - mBouncer.hide(destroyView); + mBouncerInteractor.hide(); if (mShowing) { // If we were showing the bouncer and then aborting, we need to also clear out any // potential actions unless we actually unlocked. @@ -501,8 +548,11 @@ public class StatusBarKeyguardViewManager implements RemoteInputController.Callb public void showBouncer(boolean scrimmed) { resetAlternateAuth(false); - if (mShowing && !mBouncer.isShowing()) { - mBouncer.show(false /* resetSecuritySelection */, scrimmed); + if (mShowing && !isBouncerShowing()) { + if (mBouncer != null) { + mBouncer.show(false /* resetSecuritySelection */, scrimmed); + } + mBouncerInteractor.show(scrimmed); } updateStates(); } @@ -535,7 +585,11 @@ public class StatusBarKeyguardViewManager implements RemoteInputController.Callb // instead of the bouncer. if (shouldShowAltAuth()) { if (!afterKeyguardGone) { - mBouncer.setDismissAction(mAfterKeyguardGoneAction, + if (mBouncer != null) { + mBouncer.setDismissAction(mAfterKeyguardGoneAction, + mKeyguardGoneCancelAction); + } + mBouncerInteractor.setDismissAction(mAfterKeyguardGoneAction, mKeyguardGoneCancelAction); mAfterKeyguardGoneAction = null; mKeyguardGoneCancelAction = null; @@ -549,12 +603,18 @@ public class StatusBarKeyguardViewManager implements RemoteInputController.Callb if (afterKeyguardGone) { // we'll handle the dismiss action after keyguard is gone, so just show the // bouncer - mBouncer.show(false /* resetSecuritySelection */); + mBouncerInteractor.show(/* isScrimmed= */true); + if (mBouncer != null) mBouncer.show(false /* resetSecuritySelection */); } else { // after authentication success, run dismiss action with the option to defer // hiding the keyguard based on the return value of the OnDismissAction - mBouncer.showWithDismissAction(mAfterKeyguardGoneAction, - mKeyguardGoneCancelAction); + mBouncerInteractor.setDismissAction( + mAfterKeyguardGoneAction, mKeyguardGoneCancelAction); + mBouncerInteractor.show(/* isScrimmed= */true); + if (mBouncer != null) { + mBouncer.showWithDismissAction(mAfterKeyguardGoneAction, + mKeyguardGoneCancelAction); + } // bouncer will handle the dismiss action, so we no longer need to track it here mAfterKeyguardGoneAction = null; mKeyguardGoneCancelAction = null; @@ -591,7 +651,7 @@ public class StatusBarKeyguardViewManager implements RemoteInputController.Callb // Hide bouncer and quick-quick settings. if (mOccluded && !mDozing) { mCentralSurfaces.hideKeyguard(); - if (hideBouncerWhenShowing || mBouncer.needsFullscreenBouncer()) { + if (hideBouncerWhenShowing || needsFullscreenBouncer()) { hideBouncer(false /* destroyView */); } } else { @@ -655,7 +715,10 @@ public class StatusBarKeyguardViewManager implements RemoteInputController.Callb @Override public void onFinishedGoingToSleep() { - mBouncer.onScreenTurnedOff(); + if (mBouncer != null) { + mBouncer.onScreenTurnedOff(); + } + mBouncerInteractor.onScreenTurnedOff(); } @Override @@ -746,7 +809,7 @@ public class StatusBarKeyguardViewManager implements RemoteInputController.Callb // by a FLAG_DISMISS_KEYGUARD_ACTIVITY. reset(isOccluding /* hideBouncerWhenShowing*/); } - if (animate && !mOccluded && mShowing && !mBouncer.isShowing()) { + if (animate && !mOccluded && mShowing && !bouncerIsShowing()) { mCentralSurfaces.animateKeyguardUnoccluding(); } } @@ -762,8 +825,11 @@ public class StatusBarKeyguardViewManager implements RemoteInputController.Callb @Override public void startPreHideAnimation(Runnable finishRunnable) { - if (mBouncer.isShowing()) { - mBouncer.startPreHideAnimation(finishRunnable); + if (bouncerIsShowing()) { + if (mBouncer != null) { + mBouncer.startPreHideAnimation(finishRunnable); + } + mBouncerInteractor.startDisappearAnimation(finishRunnable); mCentralSurfaces.onBouncerPreHideAnimation(); // We update the state (which will show the keyguard) only if an animation will run on @@ -873,8 +939,12 @@ public class StatusBarKeyguardViewManager implements RemoteInputController.Callb } public void onThemeChanged() { - boolean wasShowing = mBouncer.isShowing(); - boolean wasScrimmed = mBouncer.isScrimmed(); + if (mIsModernBouncerEnabled) { + updateResources(); + return; + } + boolean wasShowing = bouncerIsShowing(); + boolean wasScrimmed = bouncerIsScrimmed(); hideBouncer(true /* destroyView */); mBouncer.prepare(); @@ -924,7 +994,12 @@ public class StatusBarKeyguardViewManager implements RemoteInputController.Callb * WARNING: This method might cause Binder calls. */ public boolean isSecure() { - return mBouncer.isSecure(); + if (mBouncer != null) { + return mBouncer.isSecure(); + } + + return mKeyguardSecurityModel.getSecurityMode( + KeyguardUpdateMonitor.getCurrentUser()) != KeyguardSecurityModel.SecurityMode.None; } @Override @@ -941,10 +1016,11 @@ public class StatusBarKeyguardViewManager implements RemoteInputController.Callb * @return whether the back press has been handled */ public boolean onBackPressed(boolean hideImmediately) { - if (mBouncer.isShowing()) { + if (bouncerIsShowing()) { mCentralSurfaces.endAffordanceLaunch(); // The second condition is for SIM card locked bouncer - if (mBouncer.isScrimmed() && !mBouncer.needsFullscreenBouncer()) { + if (bouncerIsScrimmed() + && !needsFullscreenBouncer()) { hideBouncer(false); updateStates(); } else { @@ -957,16 +1033,19 @@ public class StatusBarKeyguardViewManager implements RemoteInputController.Callb @Override public boolean isBouncerShowing() { - return mBouncer.isShowing() || isShowingAlternateAuth(); + return bouncerIsShowing() || isShowingAlternateAuth(); } @Override public boolean bouncerIsOrWillBeShowing() { - return isBouncerShowing() || mBouncer.inTransit(); + return isBouncerShowing() || isBouncerInTransit(); } public boolean isFullscreenBouncer() { - return mBouncer.isFullscreenBouncer(); + if (mBouncerViewDelegate != null) { + return mBouncerViewDelegate.isFullScreenBouncer(); + } + return mBouncer != null && mBouncer.isFullscreenBouncer(); } /** @@ -987,7 +1066,7 @@ public class StatusBarKeyguardViewManager implements RemoteInputController.Callb private long getNavBarShowDelay() { if (mKeyguardStateController.isKeyguardFadingAway()) { return mKeyguardStateController.getKeyguardFadingAwayDelay(); - } else if (mBouncer.isShowing()) { + } else if (isBouncerShowing()) { return NAV_BAR_SHOW_DELAY_BOUNCER; } else { // No longer dozing, or remote input is active. No delay. @@ -1010,18 +1089,24 @@ public class StatusBarKeyguardViewManager implements RemoteInputController.Callb protected void updateStates() { boolean showing = mShowing; boolean occluded = mOccluded; - boolean bouncerShowing = mBouncer.isShowing(); + boolean bouncerShowing = bouncerIsShowing(); boolean bouncerIsOrWillBeShowing = bouncerIsOrWillBeShowing(); - boolean bouncerDismissible = !mBouncer.isFullscreenBouncer(); + boolean bouncerDismissible = !isFullscreenBouncer(); boolean remoteInputActive = mRemoteInputActive; if ((bouncerDismissible || !showing || remoteInputActive) != (mLastBouncerDismissible || !mLastShowing || mLastRemoteInputActive) || mFirstUpdate) { if (bouncerDismissible || !showing || remoteInputActive) { - mBouncer.setBackButtonEnabled(true); + if (mBouncer != null) { + mBouncer.setBackButtonEnabled(true); + } + mBouncerInteractor.setBackButtonEnabled(true); } else { - mBouncer.setBackButtonEnabled(false); + if (mBouncer != null) { + mBouncer.setBackButtonEnabled(false); + } + mBouncerInteractor.setBackButtonEnabled(false); } } @@ -1098,7 +1183,9 @@ public class StatusBarKeyguardViewManager implements RemoteInputController.Callb || mPulsing && !mIsDocked) && mGesturalNav; return (!keyguardShowing && !hideWhileDozing && !mScreenOffAnimationPlaying - || mBouncer.isShowing() || mRemoteInputActive || keyguardWithGestureNav + || bouncerIsShowing() + || mRemoteInputActive + || keyguardWithGestureNav || mGlobalActionsVisible); } @@ -1117,18 +1204,27 @@ public class StatusBarKeyguardViewManager implements RemoteInputController.Callb } public boolean shouldDismissOnMenuPressed() { - return mBouncer.shouldDismissOnMenuPressed(); + if (mBouncerViewDelegate != null) { + return mBouncerViewDelegate.shouldDismissOnMenuPressed(); + } + return mBouncer != null && mBouncer.shouldDismissOnMenuPressed(); } public boolean interceptMediaKey(KeyEvent event) { - return mBouncer.interceptMediaKey(event); + if (mBouncerViewDelegate != null) { + return mBouncerViewDelegate.interceptMediaKey(event); + } + return mBouncer != null && mBouncer.interceptMediaKey(event); } /** * @return true if the pre IME back event should be handled */ public boolean dispatchBackKeyEventPreIme() { - return mBouncer.dispatchBackKeyEventPreIme(); + if (mBouncerViewDelegate != null) { + return mBouncerViewDelegate.dispatchBackKeyEventPreIme(); + } + return mBouncer != null && mBouncer.dispatchBackKeyEventPreIme(); } public void readyForKeyguardDone() { @@ -1151,7 +1247,7 @@ public class StatusBarKeyguardViewManager implements RemoteInputController.Callb } public boolean isSecure(int userId) { - return mBouncer.isSecure() || mLockPatternUtils.isSecure(userId); + return isSecure() || mLockPatternUtils.isSecure(userId); } @Override @@ -1174,7 +1270,10 @@ public class StatusBarKeyguardViewManager implements RemoteInputController.Callb * fingerprint. */ public void notifyKeyguardAuthenticated(boolean strongAuth) { - mBouncer.notifyKeyguardAuthenticated(strongAuth); + if (mBouncer != null) { + mBouncer.notifyKeyguardAuthenticated(strongAuth); + } + mBouncerInteractor.notifyKeyguardAuthenticated(strongAuth); if (mAlternateAuthInterceptor != null && isShowingAlternateAuthOrAnimating()) { resetAlternateAuth(false); @@ -1189,7 +1288,10 @@ public class StatusBarKeyguardViewManager implements RemoteInputController.Callb mKeyguardMessageAreaController.setMessage(message); } } else { - mBouncer.showMessage(message, colorState); + if (mBouncer != null) { + mBouncer.showMessage(message, colorState); + } + mBouncerInteractor.showMessage(message, colorState); } } @@ -1222,9 +1324,10 @@ public class StatusBarKeyguardViewManager implements RemoteInputController.Callb public boolean bouncerNeedsScrimming() { // When a dream overlay is active, scrimming will cause any expansion to immediately expand. return (mOccluded && !mDreamOverlayStateController.isOverlayActive()) - || mBouncer.willDismissWithAction() - || (mBouncer.isShowing() && mBouncer.isScrimmed()) - || mBouncer.isFullscreenBouncer(); + || bouncerWillDismissWithAction() + || (bouncerIsShowing() + && bouncerIsScrimmed()) + || isFullscreenBouncer(); } /** @@ -1236,6 +1339,7 @@ public class StatusBarKeyguardViewManager implements RemoteInputController.Callb if (mBouncer != null) { mBouncer.updateResources(); } + mBouncerInteractor.updateResources(); } public void dump(PrintWriter pw) { @@ -1289,6 +1393,7 @@ public class StatusBarKeyguardViewManager implements RemoteInputController.Callb } } + @Nullable public KeyguardBouncer getBouncer() { return mBouncer; } @@ -1320,6 +1425,8 @@ public class StatusBarKeyguardViewManager implements RemoteInputController.Callb if (mBouncer != null) { mBouncer.updateKeyguardPosition(x); } + + mBouncerInteractor.setKeyguardPosition(x); } private static class DismissWithActionRequest { @@ -1359,9 +1466,65 @@ public class StatusBarKeyguardViewManager implements RemoteInputController.Callb * Returns if bouncer expansion is between 0 and 1 non-inclusive. */ public boolean isBouncerInTransit() { - if (mBouncer == null) return false; + if (mBouncer != null) { + return mBouncer.inTransit(); + } - return mBouncer.inTransit(); + return mBouncerInteractor.isInTransit(); + } + + /** + * Returns if bouncer is showing + */ + public boolean bouncerIsShowing() { + if (mBouncer != null) { + return mBouncer.isShowing(); + } + + return mBouncerInteractor.isFullyShowing(); + } + + /** + * Returns if bouncer is scrimmed + */ + public boolean bouncerIsScrimmed() { + if (mBouncer != null) { + return mBouncer.isScrimmed(); + } + + return mBouncerInteractor.isScrimmed(); + } + + /** + * Returns if bouncer is animating away + */ + public boolean bouncerIsAnimatingAway() { + if (mBouncer != null) { + return mBouncer.isAnimatingAway(); + } + + return mBouncerInteractor.isAnimatingAway(); + } + + /** + * Returns if bouncer will dismiss with action + */ + public boolean bouncerWillDismissWithAction() { + if (mBouncer != null) { + return mBouncer.willDismissWithAction(); + } + + return mBouncerInteractor.willDismissWithAction(); + } + + /** + * Returns if bouncer needs fullscreen bouncer. i.e. sim pin security method + */ + public boolean needsFullscreenBouncer() { + KeyguardSecurityModel.SecurityMode mode = mKeyguardSecurityModel.getSecurityMode( + KeyguardUpdateMonitor.getCurrentUser()); + return mode == KeyguardSecurityModel.SecurityMode.SimPin + || mode == KeyguardSecurityModel.SecurityMode.SimPuk; } /** diff --git a/packages/SystemUI/tests/src/com/android/systemui/dreams/DreamOverlayContainerViewControllerTest.java b/packages/SystemUI/tests/src/com/android/systemui/dreams/DreamOverlayContainerViewControllerTest.java index d70467ddeebe1..c5a7de410eb0f 100644 --- a/packages/SystemUI/tests/src/com/android/systemui/dreams/DreamOverlayContainerViewControllerTest.java +++ b/packages/SystemUI/tests/src/com/android/systemui/dreams/DreamOverlayContainerViewControllerTest.java @@ -36,6 +36,7 @@ import androidx.test.filters.SmallTest; import com.android.keyguard.BouncerPanelExpansionCalculator; import com.android.systemui.SysuiTestCase; import com.android.systemui.dreams.complication.ComplicationHostViewController; +import com.android.systemui.keyguard.domain.interactor.BouncerCallbackInteractor; import com.android.systemui.statusbar.BlurUtils; import com.android.systemui.statusbar.phone.KeyguardBouncer; import com.android.systemui.statusbar.phone.KeyguardBouncer.BouncerExpansionCallback; @@ -88,6 +89,9 @@ public class DreamOverlayContainerViewControllerTest extends SysuiTestCase { @Mock ViewRootImpl mViewRoot; + @Mock + BouncerCallbackInteractor mBouncerCallbackInteractor; + DreamOverlayContainerViewController mController; @Before @@ -110,7 +114,8 @@ public class DreamOverlayContainerViewControllerTest extends SysuiTestCase { mResources, MAX_BURN_IN_OFFSET, BURN_IN_PROTECTION_UPDATE_INTERVAL, - MILLIS_UNTIL_FULL_JITTER); + MILLIS_UNTIL_FULL_JITTER, + mBouncerCallbackInteractor); } @Test diff --git a/packages/SystemUI/tests/src/com/android/systemui/keyguard/domain/interactor/BouncerCallbackInteractorTest.kt b/packages/SystemUI/tests/src/com/android/systemui/keyguard/domain/interactor/BouncerCallbackInteractorTest.kt new file mode 100644 index 0000000000000..3a61c57d086fc --- /dev/null +++ b/packages/SystemUI/tests/src/com/android/systemui/keyguard/domain/interactor/BouncerCallbackInteractorTest.kt @@ -0,0 +1,86 @@ +/* + * Copyright (C) 2022 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.android.systemui.keyguard.domain.interactor + +import android.view.View +import androidx.test.filters.SmallTest +import com.android.systemui.SysuiTestCase +import com.android.systemui.statusbar.phone.KeyguardBouncer +import org.junit.Before +import org.junit.Test +import org.junit.runner.RunWith +import org.junit.runners.JUnit4 +import org.mockito.Mock +import org.mockito.Mockito.verify +import org.mockito.MockitoAnnotations + +@SmallTest +@RunWith(JUnit4::class) +class BouncerCallbackInteractorTest : SysuiTestCase() { + private val bouncerCallbackInteractor = BouncerCallbackInteractor() + @Mock private lateinit var bouncerExpansionCallback: KeyguardBouncer.BouncerExpansionCallback + @Mock private lateinit var keyguardResetCallback: KeyguardBouncer.KeyguardResetCallback + + @Before + fun setup() { + MockitoAnnotations.initMocks(this) + bouncerCallbackInteractor.addBouncerExpansionCallback(bouncerExpansionCallback) + bouncerCallbackInteractor.addKeyguardResetCallback(keyguardResetCallback) + } + + @Test + fun testOnFullyShown() { + bouncerCallbackInteractor.dispatchFullyShown() + verify(bouncerExpansionCallback).onFullyShown() + } + + @Test + fun testOnFullyHidden() { + bouncerCallbackInteractor.dispatchFullyHidden() + verify(bouncerExpansionCallback).onFullyHidden() + } + + @Test + fun testOnExpansionChanged() { + bouncerCallbackInteractor.dispatchExpansionChanged(5f) + verify(bouncerExpansionCallback).onExpansionChanged(5f) + } + + @Test + fun testOnVisibilityChanged() { + bouncerCallbackInteractor.dispatchVisibilityChanged(View.INVISIBLE) + verify(bouncerExpansionCallback).onVisibilityChanged(false) + } + + @Test + fun testOnStartingToHide() { + bouncerCallbackInteractor.dispatchStartingToHide() + verify(bouncerExpansionCallback).onStartingToHide() + } + + @Test + fun testOnStartingToShow() { + bouncerCallbackInteractor.dispatchStartingToShow() + verify(bouncerExpansionCallback).onStartingToShow() + } + + @Test + fun testOnKeyguardReset() { + bouncerCallbackInteractor.dispatchReset() + verify(keyguardResetCallback).onKeyguardReset() + } +} diff --git a/packages/SystemUI/tests/src/com/android/systemui/keyguard/domain/interactor/BouncerInteractorTest.kt b/packages/SystemUI/tests/src/com/android/systemui/keyguard/domain/interactor/BouncerInteractorTest.kt new file mode 100644 index 0000000000000..e6c8dd87d9824 --- /dev/null +++ b/packages/SystemUI/tests/src/com/android/systemui/keyguard/domain/interactor/BouncerInteractorTest.kt @@ -0,0 +1,278 @@ +/* + * Copyright (C) 2022 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License + */ + +package com.android.systemui.keyguard.domain.interactor + +import android.os.Looper +import android.testing.AndroidTestingRunner +import android.testing.TestableLooper.RunWithLooper +import androidx.test.filters.SmallTest +import com.android.keyguard.KeyguardSecurityModel +import com.android.keyguard.KeyguardUpdateMonitor +import com.android.systemui.DejankUtils +import com.android.systemui.SysuiTestCase +import com.android.systemui.classifier.FalsingCollector +import com.android.systemui.keyguard.DismissCallbackRegistry +import com.android.systemui.keyguard.data.BouncerView +import com.android.systemui.keyguard.data.repository.KeyguardBouncerRepository +import com.android.systemui.keyguard.shared.model.BouncerCallbackActionsModel +import com.android.systemui.keyguard.shared.model.BouncerShowMessageModel +import com.android.systemui.keyguard.shared.model.KeyguardBouncerModel +import com.android.systemui.plugins.ActivityStarter +import com.android.systemui.statusbar.phone.KeyguardBouncer.EXPANSION_HIDDEN +import com.android.systemui.statusbar.phone.KeyguardBouncer.EXPANSION_VISIBLE +import com.android.systemui.statusbar.phone.KeyguardBypassController +import com.android.systemui.statusbar.policy.KeyguardStateController +import com.android.systemui.util.mockito.any +import com.android.systemui.utils.os.FakeHandler +import com.google.common.truth.Truth.assertThat +import org.junit.Before +import org.junit.Test +import org.junit.runner.RunWith +import org.mockito.Answers +import org.mockito.Mock +import org.mockito.Mockito.mock +import org.mockito.Mockito.never +import org.mockito.Mockito.verify +import org.mockito.Mockito.`when` +import org.mockito.MockitoAnnotations + +@SmallTest +@RunWithLooper(setAsMainLooper = true) +@RunWith(AndroidTestingRunner::class) +class BouncerInteractorTest : SysuiTestCase() { + @Mock(answer = Answers.RETURNS_DEEP_STUBS) + private lateinit var repository: KeyguardBouncerRepository + @Mock(answer = Answers.RETURNS_DEEP_STUBS) private lateinit var bouncerView: BouncerView + @Mock private lateinit var keyguardStateController: KeyguardStateController + @Mock private lateinit var keyguardSecurityModel: KeyguardSecurityModel + @Mock private lateinit var bouncerCallbackInteractor: BouncerCallbackInteractor + @Mock private lateinit var falsingCollector: FalsingCollector + @Mock private lateinit var dismissCallbackRegistry: DismissCallbackRegistry + @Mock private lateinit var keyguardBypassController: KeyguardBypassController + @Mock private lateinit var keyguardUpdateMonitor: KeyguardUpdateMonitor + private val mainHandler = FakeHandler(Looper.getMainLooper()) + private lateinit var bouncerInteractor: BouncerInteractor + + @Before + fun setUp() { + MockitoAnnotations.initMocks(this) + DejankUtils.setImmediate(true) + bouncerInteractor = + BouncerInteractor( + repository, + bouncerView, + mainHandler, + keyguardStateController, + keyguardSecurityModel, + bouncerCallbackInteractor, + falsingCollector, + dismissCallbackRegistry, + keyguardBypassController, + keyguardUpdateMonitor, + ) + `when`(repository.startingDisappearAnimation.value).thenReturn(null) + `when`(repository.show.value).thenReturn(null) + } + + @Test + fun testShow_isScrimmed() { + bouncerInteractor.show(true) + verify(repository).setShowMessage(null) + verify(repository).setOnScreenTurnedOff(false) + verify(repository).setKeyguardAuthenticated(null) + verify(repository).setHide(false) + verify(repository).setStartingToHide(false) + verify(repository).setScrimmed(true) + verify(repository).setExpansion(EXPANSION_VISIBLE) + verify(repository).setShowingSoon(true) + verify(keyguardStateController).notifyBouncerShowing(true) + verify(bouncerCallbackInteractor).dispatchStartingToShow() + verify(repository).setVisible(true) + verify(repository).setShow(any(KeyguardBouncerModel::class.java)) + verify(repository).setShowingSoon(false) + } + + @Test + fun testShow_isNotScrimmed() { + verify(repository, never()).setExpansion(EXPANSION_VISIBLE) + } + + @Test + fun testShow_keyguardIsDone() { + `when`(bouncerView.delegate?.showNextSecurityScreenOrFinish()).thenReturn(true) + verify(keyguardStateController, never()).notifyBouncerShowing(true) + verify(bouncerCallbackInteractor, never()).dispatchStartingToShow() + } + + @Test + fun testHide() { + bouncerInteractor.hide() + verify(falsingCollector).onBouncerHidden() + verify(keyguardStateController).notifyBouncerShowing(false) + verify(repository).setShowingSoon(false) + verify(repository).setOnDismissAction(null) + verify(repository).setVisible(false) + verify(repository).setHide(true) + verify(repository).setShow(null) + } + + @Test + fun testExpansion() { + `when`(repository.expansionAmount.value).thenReturn(0.5f) + bouncerInteractor.setExpansion(0.6f) + verify(repository).setExpansion(0.6f) + verify(bouncerCallbackInteractor).dispatchExpansionChanged(0.6f) + } + + @Test + fun testExpansion_fullyShown() { + `when`(repository.expansionAmount.value).thenReturn(0.5f) + `when`(repository.startingDisappearAnimation.value).thenReturn(null) + bouncerInteractor.setExpansion(EXPANSION_VISIBLE) + verify(falsingCollector).onBouncerShown() + verify(bouncerCallbackInteractor).dispatchFullyShown() + } + + @Test + fun testExpansion_fullyHidden() { + `when`(repository.expansionAmount.value).thenReturn(0.5f) + `when`(repository.startingDisappearAnimation.value).thenReturn(null) + bouncerInteractor.setExpansion(EXPANSION_HIDDEN) + verify(repository).setVisible(false) + verify(repository).setShow(null) + verify(falsingCollector).onBouncerHidden() + verify(bouncerCallbackInteractor).dispatchReset() + verify(bouncerCallbackInteractor).dispatchFullyHidden() + } + + @Test + fun testExpansion_startingToHide() { + `when`(repository.expansionAmount.value).thenReturn(EXPANSION_VISIBLE) + bouncerInteractor.setExpansion(0.1f) + verify(repository).setStartingToHide(true) + verify(bouncerCallbackInteractor).dispatchStartingToHide() + } + + @Test + fun testShowMessage() { + bouncerInteractor.showMessage("abc", null) + verify(repository).setShowMessage(BouncerShowMessageModel("abc", null)) + } + + @Test + fun testDismissAction() { + val onDismissAction = mock(ActivityStarter.OnDismissAction::class.java) + val cancelAction = mock(Runnable::class.java) + bouncerInteractor.setDismissAction(onDismissAction, cancelAction) + verify(repository) + .setOnDismissAction(BouncerCallbackActionsModel(onDismissAction, cancelAction)) + } + + @Test + fun testUpdateResources() { + bouncerInteractor.updateResources() + verify(repository).setResourceUpdateRequests(true) + } + + @Test + fun testNotifyKeyguardAuthenticated() { + bouncerInteractor.notifyKeyguardAuthenticated(true) + verify(repository).setKeyguardAuthenticated(true) + } + + @Test + fun testOnScreenTurnedOff() { + bouncerInteractor.onScreenTurnedOff() + verify(repository).setOnScreenTurnedOff(true) + } + + @Test + fun testSetKeyguardPosition() { + bouncerInteractor.setKeyguardPosition(0f) + verify(repository).setKeyguardPosition(0f) + } + + @Test + fun testNotifyKeyguardAuthenticatedHandled() { + bouncerInteractor.notifyKeyguardAuthenticatedHandled() + verify(repository).setKeyguardAuthenticated(null) + } + + @Test + fun testNotifyUpdatedResources() { + bouncerInteractor.notifyUpdatedResources() + verify(repository).setResourceUpdateRequests(false) + } + + @Test + fun testSetBackButtonEnabled() { + bouncerInteractor.setBackButtonEnabled(true) + verify(repository).setIsBackButtonEnabled(true) + } + + @Test + fun testStartDisappearAnimation() { + val runnable = mock(Runnable::class.java) + bouncerInteractor.startDisappearAnimation(runnable) + verify(repository).setStartDisappearAnimation(any(Runnable::class.java)) + } + + @Test + fun testIsFullShowing() { + `when`(repository.isVisible.value).thenReturn(true) + `when`(repository.expansionAmount.value).thenReturn(EXPANSION_VISIBLE) + `when`(repository.startingDisappearAnimation.value).thenReturn(null) + assertThat(bouncerInteractor.isFullyShowing()).isTrue() + `when`(repository.isVisible.value).thenReturn(false) + assertThat(bouncerInteractor.isFullyShowing()).isFalse() + } + + @Test + fun testIsScrimmed() { + `when`(repository.isScrimmed.value).thenReturn(true) + assertThat(bouncerInteractor.isScrimmed()).isTrue() + `when`(repository.isScrimmed.value).thenReturn(false) + assertThat(bouncerInteractor.isScrimmed()).isFalse() + } + + @Test + fun testIsInTransit() { + `when`(repository.showingSoon.value).thenReturn(true) + assertThat(bouncerInteractor.isInTransit()).isTrue() + `when`(repository.showingSoon.value).thenReturn(false) + assertThat(bouncerInteractor.isInTransit()).isFalse() + `when`(repository.expansionAmount.value).thenReturn(0.5f) + assertThat(bouncerInteractor.isInTransit()).isTrue() + } + + @Test + fun testIsAnimatingAway() { + `when`(repository.startingDisappearAnimation.value).thenReturn(Runnable {}) + assertThat(bouncerInteractor.isAnimatingAway()).isTrue() + `when`(repository.startingDisappearAnimation.value).thenReturn(null) + assertThat(bouncerInteractor.isAnimatingAway()).isFalse() + } + + @Test + fun testWillDismissWithAction() { + `when`(repository.onDismissAction.value?.onDismissAction) + .thenReturn(mock(ActivityStarter.OnDismissAction::class.java)) + assertThat(bouncerInteractor.willDismissWithAction()).isTrue() + `when`(repository.onDismissAction.value?.onDismissAction).thenReturn(null) + assertThat(bouncerInteractor.willDismissWithAction()).isFalse() + } +} diff --git a/packages/SystemUI/tests/src/com/android/systemui/shade/NotificationShadeWindowViewControllerTest.kt b/packages/SystemUI/tests/src/com/android/systemui/shade/NotificationShadeWindowViewControllerTest.kt index 2adc389a964e7..481e4e9992d41 100644 --- a/packages/SystemUI/tests/src/com/android/systemui/shade/NotificationShadeWindowViewControllerTest.kt +++ b/packages/SystemUI/tests/src/com/android/systemui/shade/NotificationShadeWindowViewControllerTest.kt @@ -21,12 +21,16 @@ import android.testing.TestableLooper.RunWithLooper import android.view.MotionEvent import android.view.ViewGroup import androidx.test.filters.SmallTest +import com.android.keyguard.KeyguardHostViewController import com.android.keyguard.LockIconViewController +import com.android.keyguard.dagger.KeyguardBouncerComponent import com.android.systemui.R import com.android.systemui.SysuiTestCase import com.android.systemui.classifier.FalsingCollectorFake import com.android.systemui.dock.DockManager +import com.android.systemui.flags.FeatureFlags import com.android.systemui.keyguard.KeyguardUnlockAnimationController +import com.android.systemui.keyguard.ui.viewmodel.KeyguardBouncerViewModel import com.android.systemui.shade.NotificationShadeWindowView.InteractionEventHandler import com.android.systemui.statusbar.LockscreenShadeTransitionController import com.android.systemui.statusbar.NotificationShadeDepthController @@ -51,9 +55,9 @@ import org.mockito.Mockito.verify import org.mockito.Mockito.`when` as whenever import org.mockito.MockitoAnnotations +@SmallTest @RunWith(AndroidTestingRunner::class) @RunWithLooper(setAsMainLooper = true) -@SmallTest class NotificationShadeWindowViewControllerTest : SysuiTestCase() { @Mock private lateinit var view: NotificationShadeWindowView @@ -72,8 +76,12 @@ class NotificationShadeWindowViewControllerTest : SysuiTestCase() { @Mock private lateinit var keyguardUnlockAnimationController: KeyguardUnlockAnimationController @Mock + private lateinit var featureFlags: FeatureFlags + @Mock private lateinit var ambientState: AmbientState @Mock + private lateinit var keyguardBouncerViewModel: KeyguardBouncerViewModel + @Mock private lateinit var stackScrollLayoutController: NotificationStackScrollLayoutController @Mock private lateinit var statusBarKeyguardViewManager: StatusBarKeyguardViewManager @@ -87,6 +95,10 @@ class NotificationShadeWindowViewControllerTest : SysuiTestCase() { private lateinit var phoneStatusBarViewController: PhoneStatusBarViewController @Mock private lateinit var pulsingGestureListener: PulsingGestureListener + @Mock lateinit var keyguardBouncerComponentFactory: KeyguardBouncerComponent.Factory + @Mock lateinit var keyguardBouncerContainer: ViewGroup + @Mock lateinit var keyguardBouncerComponent: KeyguardBouncerComponent + @Mock lateinit var keyguardHostViewController: KeyguardHostViewController private lateinit var interactionEventHandlerCaptor: ArgumentCaptor private lateinit var interactionEventHandler: InteractionEventHandler @@ -97,7 +109,6 @@ class NotificationShadeWindowViewControllerTest : SysuiTestCase() { fun setUp() { MockitoAnnotations.initMocks(this) whenever(view.bottom).thenReturn(VIEW_BOTTOM) - underTest = NotificationShadeWindowViewController( lockscreenShadeTransitionController, FalsingCollectorFake(), @@ -115,7 +126,10 @@ class NotificationShadeWindowViewControllerTest : SysuiTestCase() { notificationShadeWindowController, keyguardUnlockAnimationController, ambientState, - pulsingGestureListener + pulsingGestureListener, + featureFlags, + keyguardBouncerViewModel, + keyguardBouncerComponentFactory ) underTest.setupExpandedStatusBar() diff --git a/packages/SystemUI/tests/src/com/android/systemui/shade/NotificationShadeWindowViewTest.java b/packages/SystemUI/tests/src/com/android/systemui/shade/NotificationShadeWindowViewTest.java index 001bfeeabe6f6..4a7dec912895b 100644 --- a/packages/SystemUI/tests/src/com/android/systemui/shade/NotificationShadeWindowViewTest.java +++ b/packages/SystemUI/tests/src/com/android/systemui/shade/NotificationShadeWindowViewTest.java @@ -33,11 +33,14 @@ import android.view.MotionEvent; import androidx.test.filters.SmallTest; import com.android.keyguard.LockIconViewController; +import com.android.keyguard.dagger.KeyguardBouncerComponent; import com.android.systemui.R; import com.android.systemui.SysuiTestCase; import com.android.systemui.classifier.FalsingCollectorFake; import com.android.systemui.dock.DockManager; +import com.android.systemui.flags.FeatureFlags; import com.android.systemui.keyguard.KeyguardUnlockAnimationController; +import com.android.systemui.keyguard.ui.viewmodel.KeyguardBouncerViewModel; import com.android.systemui.statusbar.DragDownHelper; import com.android.systemui.statusbar.LockscreenShadeTransitionController; import com.android.systemui.statusbar.NotificationShadeDepthController; @@ -86,6 +89,9 @@ public class NotificationShadeWindowViewTest extends SysuiTestCase { @Mock private KeyguardUnlockAnimationController mKeyguardUnlockAnimationController; @Mock private AmbientState mAmbientState; @Mock private PulsingGestureListener mPulsingGestureListener; + @Mock private FeatureFlags mFeatureFlags; + @Mock private KeyguardBouncerViewModel mKeyguardBouncerViewModel; + @Mock private KeyguardBouncerComponent.Factory mKeyguardBouncerComponentFactory; @Captor private ArgumentCaptor mInteractionEventHandlerCaptor; @@ -121,7 +127,10 @@ public class NotificationShadeWindowViewTest extends SysuiTestCase { mNotificationShadeWindowController, mKeyguardUnlockAnimationController, mAmbientState, - mPulsingGestureListener + mPulsingGestureListener, + mFeatureFlags, + mKeyguardBouncerViewModel, + mKeyguardBouncerComponentFactory ); mController.setupExpandedStatusBar(); mController.setDragDownHelper(mDragDownHelper); diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/StatusBarKeyguardViewManagerTest.java b/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/StatusBarKeyguardViewManagerTest.java index a4453f854ce5a..ee4b9d9c93f2f 100644 --- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/StatusBarKeyguardViewManagerTest.java +++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/StatusBarKeyguardViewManagerTest.java @@ -41,11 +41,17 @@ import com.android.internal.util.LatencyTracker; import com.android.internal.widget.LockPatternUtils; import com.android.keyguard.KeyguardMessageArea; import com.android.keyguard.KeyguardMessageAreaController; +import com.android.keyguard.KeyguardSecurityModel; import com.android.keyguard.KeyguardUpdateMonitor; import com.android.keyguard.ViewMediatorCallback; import com.android.systemui.SysuiTestCase; import com.android.systemui.dock.DockManager; import com.android.systemui.dreams.DreamOverlayStateController; +import com.android.systemui.flags.FeatureFlags; +import com.android.systemui.keyguard.data.BouncerView; +import com.android.systemui.keyguard.data.BouncerViewDelegate; +import com.android.systemui.keyguard.domain.interactor.BouncerCallbackInteractor; +import com.android.systemui.keyguard.domain.interactor.BouncerInteractor; import com.android.systemui.navigationbar.NavigationModeController; import com.android.systemui.plugins.ActivityStarter.OnDismissAction; import com.android.systemui.shade.NotificationPanelViewController; @@ -101,6 +107,13 @@ public class StatusBarKeyguardViewManagerTest extends SysuiTestCase { @Mock private SysUIUnfoldComponent mSysUiUnfoldComponent; @Mock private DreamOverlayStateController mDreamOverlayStateController; @Mock private LatencyTracker mLatencyTracker; + @Mock private FeatureFlags mFeatureFlags; + @Mock private KeyguardSecurityModel mKeyguardSecurityModel; + @Mock private BouncerCallbackInteractor mBouncerCallbackInteractor; + @Mock private BouncerInteractor mBouncerInteractor; + @Mock private BouncerView mBouncerView; +// @Mock private WeakReference mBouncerViewDelegateWeakReference; + @Mock private BouncerViewDelegate mBouncerViewDelegate; private StatusBarKeyguardViewManager mStatusBarKeyguardViewManager; private KeyguardBouncer.BouncerExpansionCallback mBouncerExpansionCallback; @@ -115,6 +128,8 @@ public class StatusBarKeyguardViewManagerTest extends SysuiTestCase { when(mContainer.findViewById(anyInt())).thenReturn(mKeyguardMessageArea); when(mKeyguardMessageAreaFactory.create(any(KeyguardMessageArea.class))) .thenReturn(mKeyguardMessageAreaController); + when(mBouncerView.getDelegate()).thenReturn(mBouncerViewDelegate); + mStatusBarKeyguardViewManager = new StatusBarKeyguardViewManager( getContext(), @@ -133,7 +148,12 @@ public class StatusBarKeyguardViewManagerTest extends SysuiTestCase { mKeyguardMessageAreaFactory, Optional.of(mSysUiUnfoldComponent), () -> mShadeController, - mLatencyTracker); + mLatencyTracker, + mKeyguardSecurityModel, + mFeatureFlags, + mBouncerCallbackInteractor, + mBouncerInteractor, + mBouncerView); mStatusBarKeyguardViewManager.registerCentralSurfaces( mCentralSurfaces, mNotificationPanelView,