From 4a164f864519309b0783483a4f508a56feab4cd6 Mon Sep 17 00:00:00 2001 From: Beverly Date: Tue, 1 Nov 2022 18:52:20 +0000 Subject: [PATCH 1/3] Refactor file from java => kotlin Change file name in its own CL to preserve the history. This CL should only be merged with the followup CL. Test: See ag/20272142 Bug: 237362467 Change-Id: Ic598a4ad88aa64d05d90e66d0f07cb7eb06c76cd Merged-In: Ic598a4ad88aa64d05d90e66d0f07cb7eb06c76cd --- ...KeyguardViewController.java => UdfpsKeyguardViewController.kt} | 0 1 file changed, 0 insertions(+), 0 deletions(-) rename packages/SystemUI/src/com/android/systemui/biometrics/{UdfpsKeyguardViewController.java => UdfpsKeyguardViewController.kt} (100%) diff --git a/packages/SystemUI/src/com/android/systemui/biometrics/UdfpsKeyguardViewController.java b/packages/SystemUI/src/com/android/systemui/biometrics/UdfpsKeyguardViewController.kt similarity index 100% rename from packages/SystemUI/src/com/android/systemui/biometrics/UdfpsKeyguardViewController.java rename to packages/SystemUI/src/com/android/systemui/biometrics/UdfpsKeyguardViewController.kt From 71f0123c9d6c567da7137014507b9ad5195beb69 Mon Sep 17 00:00:00 2001 From: Beverly Date: Mon, 24 Oct 2022 18:48:28 +0000 Subject: [PATCH 2/3] 1/N sideFPs bouncer, separate altBouncer callback The AltBouncer callback should only handle events relevant to any alternate bouncer (ie: show and hide). Other callbacks like setQsExpanded and onTouch should be moved out to a different callback since they're not relevant to all alternate bouncers. This CL also converts UdfpsKeyguardViewController to kotlin and, when the modern bouncer is enabled, uses the BouncerInteractor for the bouncerExpansion to determine whether to show the UDPFS affordance or not. Test: atest UdfpsKeyguardViewControllerTest Test: atest UdfpsKeyguardViewControllerWithCoroutinesTest Test: wallet activity shows udfps affordance Test: tapping home controls affordances shows udfps bouncer Test: tapping notification w/ intent shows udfps bouncer Bug: 237362467 Change-Id: Ibb9b55e6b1a629e28f523ac62058f7e5cf40faed Merged-In: Ibb9b55e6b1a629e28f523ac62058f7e5cf40faed --- .../systemui/biometrics/UdfpsController.java | 13 +- .../biometrics/UdfpsControllerOverlay.kt | 54 +- .../biometrics/UdfpsKeyguardViewController.kt | 904 +++++++++--------- .../repository/KeyguardBouncerRepository.kt | 18 +- .../domain/interactor/BouncerInteractor.kt | 36 +- .../ui/viewmodel/KeyguardBouncerViewModel.kt | 2 +- .../NotificationPanelViewController.java | 2 +- ...NotificationShadeWindowViewController.java | 4 +- .../statusbar/phone/KeyguardBouncer.java | 13 + .../phone/StatusBarKeyguardViewManager.java | 109 +-- .../biometrics/UdfpsControllerOverlayTest.kt | 7 +- .../biometrics/UdfpsControllerTest.java | 10 +- .../UdfpsKeyguardViewControllerBaseTest.java | 527 ++++++++++ .../UdfpsKeyguardViewControllerTest.java | 504 +--------- ...eyguardViewControllerWithCoroutinesTest.kt | 102 ++ .../interactor/BouncerInteractorTest.kt | 26 +- .../NotificationShadeWindowViewTest.java | 6 +- 17 files changed, 1274 insertions(+), 1063 deletions(-) create mode 100644 packages/SystemUI/tests/src/com/android/systemui/biometrics/UdfpsKeyguardViewControllerBaseTest.java create mode 100644 packages/SystemUI/tests/src/com/android/systemui/biometrics/UdfpsKeyguardViewControllerWithCoroutinesTest.kt diff --git a/packages/SystemUI/src/com/android/systemui/biometrics/UdfpsController.java b/packages/SystemUI/src/com/android/systemui/biometrics/UdfpsController.java index 96fe65f8fd40b..65fcd760360c2 100644 --- a/packages/SystemUI/src/com/android/systemui/biometrics/UdfpsController.java +++ b/packages/SystemUI/src/com/android/systemui/biometrics/UdfpsController.java @@ -60,7 +60,9 @@ import com.android.systemui.dagger.SysUISingleton; import com.android.systemui.dagger.qualifiers.Main; import com.android.systemui.doze.DozeReceiver; import com.android.systemui.dump.DumpManager; +import com.android.systemui.flags.FeatureFlags; import com.android.systemui.keyguard.ScreenLifecycle; +import com.android.systemui.keyguard.domain.interactor.BouncerInteractor; import com.android.systemui.plugins.FalsingManager; import com.android.systemui.plugins.statusbar.StatusBarStateController; import com.android.systemui.shade.ShadeExpansionStateManager; @@ -119,6 +121,7 @@ public class UdfpsController implements DozeReceiver { @NonNull private final SystemUIDialogManager mDialogManager; @NonNull private final KeyguardUpdateMonitor mKeyguardUpdateMonitor; @NonNull private final VibratorHelper mVibrator; + @NonNull private final FeatureFlags mFeatureFlags; @NonNull private final FalsingManager mFalsingManager; @NonNull private final PowerManager mPowerManager; @NonNull private final AccessibilityManager mAccessibilityManager; @@ -130,6 +133,7 @@ public class UdfpsController implements DozeReceiver { @NonNull private final LatencyTracker mLatencyTracker; @VisibleForTesting @NonNull final BiometricDisplayListener mOrientationListener; @NonNull private final ActivityLaunchAnimator mActivityLaunchAnimator; + @NonNull private final BouncerInteractor mBouncerInteractor; // Currently the UdfpsController supports a single UDFPS sensor. If devices have multiple // sensors, this, in addition to a lot of the code here, will be updated. @@ -212,7 +216,8 @@ public class UdfpsController implements DozeReceiver { mUnlockedScreenOffAnimationController, mUdfpsDisplayMode, requestId, reason, callback, (view, event, fromUdfpsView) -> onTouch(requestId, event, - fromUdfpsView), mActivityLaunchAnimator))); + fromUdfpsView), mActivityLaunchAnimator, mFeatureFlags, + mBouncerInteractor))); } @Override @@ -590,6 +595,7 @@ public class UdfpsController implements DozeReceiver { @NonNull StatusBarKeyguardViewManager statusBarKeyguardViewManager, @NonNull DumpManager dumpManager, @NonNull KeyguardUpdateMonitor keyguardUpdateMonitor, + @NonNull FeatureFlags featureFlags, @NonNull FalsingManager falsingManager, @NonNull PowerManager powerManager, @NonNull AccessibilityManager accessibilityManager, @@ -608,7 +614,8 @@ public class UdfpsController implements DozeReceiver { @NonNull LatencyTracker latencyTracker, @NonNull ActivityLaunchAnimator activityLaunchAnimator, @NonNull Optional alternateTouchProvider, - @BiometricsBackground Executor biometricsExecutor) { + @BiometricsBackground Executor biometricsExecutor, + @NonNull BouncerInteractor bouncerInteractor) { mContext = context; mExecution = execution; mVibrator = vibrator; @@ -638,6 +645,8 @@ public class UdfpsController implements DozeReceiver { mActivityLaunchAnimator = activityLaunchAnimator; mAlternateTouchProvider = alternateTouchProvider.orElse(null); mBiometricExecutor = biometricsExecutor; + mFeatureFlags = featureFlags; + mBouncerInteractor = bouncerInteractor; mOrientationListener = new BiometricDisplayListener( context, diff --git a/packages/SystemUI/src/com/android/systemui/biometrics/UdfpsControllerOverlay.kt b/packages/SystemUI/src/com/android/systemui/biometrics/UdfpsControllerOverlay.kt index 7d01096863518..d70861ac5f195 100644 --- a/packages/SystemUI/src/com/android/systemui/biometrics/UdfpsControllerOverlay.kt +++ b/packages/SystemUI/src/com/android/systemui/biometrics/UdfpsControllerOverlay.kt @@ -48,6 +48,8 @@ import com.android.keyguard.KeyguardUpdateMonitor import com.android.systemui.R import com.android.systemui.animation.ActivityLaunchAnimator import com.android.systemui.dump.DumpManager +import com.android.systemui.flags.FeatureFlags +import com.android.systemui.keyguard.domain.interactor.BouncerInteractor import com.android.systemui.plugins.statusbar.StatusBarStateController import com.android.systemui.shade.ShadeExpansionStateManager import com.android.systemui.statusbar.LockscreenShadeTransitionController @@ -70,29 +72,31 @@ const val SETTING_REMOVE_ENROLLMENT_UI = "udfps_overlay_remove_enrollment_ui" */ @UiThread class UdfpsControllerOverlay @JvmOverloads constructor( - private val context: Context, - fingerprintManager: FingerprintManager, - private val inflater: LayoutInflater, - private val windowManager: WindowManager, - private val accessibilityManager: AccessibilityManager, - private val statusBarStateController: StatusBarStateController, - private val shadeExpansionStateManager: ShadeExpansionStateManager, - private val statusBarKeyguardViewManager: StatusBarKeyguardViewManager, - private val keyguardUpdateMonitor: KeyguardUpdateMonitor, - private val dialogManager: SystemUIDialogManager, - private val dumpManager: DumpManager, - private val transitionController: LockscreenShadeTransitionController, - private val configurationController: ConfigurationController, - private val systemClock: SystemClock, - private val keyguardStateController: KeyguardStateController, - private val unlockedScreenOffAnimationController: UnlockedScreenOffAnimationController, - private var udfpsDisplayModeProvider: UdfpsDisplayModeProvider, - val requestId: Long, - @ShowReason val requestReason: Int, - private val controllerCallback: IUdfpsOverlayControllerCallback, - private val onTouch: (View, MotionEvent, Boolean) -> Boolean, - private val activityLaunchAnimator: ActivityLaunchAnimator, - private val isDebuggable: Boolean = Build.IS_DEBUGGABLE + private val context: Context, + fingerprintManager: FingerprintManager, + private val inflater: LayoutInflater, + private val windowManager: WindowManager, + private val accessibilityManager: AccessibilityManager, + private val statusBarStateController: StatusBarStateController, + private val shadeExpansionStateManager: ShadeExpansionStateManager, + private val statusBarKeyguardViewManager: StatusBarKeyguardViewManager, + private val keyguardUpdateMonitor: KeyguardUpdateMonitor, + private val dialogManager: SystemUIDialogManager, + private val dumpManager: DumpManager, + private val transitionController: LockscreenShadeTransitionController, + private val configurationController: ConfigurationController, + private val systemClock: SystemClock, + private val keyguardStateController: KeyguardStateController, + private val unlockedScreenOffAnimationController: UnlockedScreenOffAnimationController, + private var udfpsDisplayModeProvider: UdfpsDisplayModeProvider, + val requestId: Long, + @ShowReason val requestReason: Int, + private val controllerCallback: IUdfpsOverlayControllerCallback, + private val onTouch: (View, MotionEvent, Boolean) -> Boolean, + private val activityLaunchAnimator: ActivityLaunchAnimator, + private val featureFlags: FeatureFlags, + private val bouncerInteractor: BouncerInteractor, + private val isDebuggable: Boolean = Build.IS_DEBUGGABLE ) { /** The view, when [isShowing], or null. */ var overlayView: UdfpsView? = null @@ -246,7 +250,9 @@ class UdfpsControllerOverlay @JvmOverloads constructor( unlockedScreenOffAnimationController, dialogManager, controller, - activityLaunchAnimator + activityLaunchAnimator, + featureFlags, + bouncerInteractor ) } REASON_AUTH_BP -> { diff --git a/packages/SystemUI/src/com/android/systemui/biometrics/UdfpsKeyguardViewController.kt b/packages/SystemUI/src/com/android/systemui/biometrics/UdfpsKeyguardViewController.kt index 4d7f89d7b7272..5bae2dc502d6d 100644 --- a/packages/SystemUI/src/com/android/systemui/biometrics/UdfpsKeyguardViewController.kt +++ b/packages/SystemUI/src/com/android/systemui/biometrics/UdfpsKeyguardViewController.kt @@ -14,275 +14,444 @@ * limitations under the License. */ -package com.android.systemui.biometrics; +package com.android.systemui.biometrics -import static com.android.systemui.statusbar.StatusBarState.KEYGUARD; +import android.animation.ValueAnimator +import android.content.res.Configuration +import android.util.MathUtils +import android.view.MotionEvent +import androidx.annotation.VisibleForTesting +import androidx.lifecycle.Lifecycle +import androidx.lifecycle.repeatOnLifecycle +import com.android.keyguard.BouncerPanelExpansionCalculator.aboutToShowBouncerProgress +import com.android.keyguard.KeyguardUpdateMonitor +import com.android.systemui.R +import com.android.systemui.animation.ActivityLaunchAnimator +import com.android.systemui.animation.Interpolators +import com.android.systemui.dump.DumpManager +import com.android.systemui.flags.FeatureFlags +import com.android.systemui.flags.Flags +import com.android.systemui.keyguard.domain.interactor.BouncerInteractor +import com.android.systemui.lifecycle.repeatWhenAttached +import com.android.systemui.plugins.statusbar.StatusBarStateController +import com.android.systemui.shade.ShadeExpansionListener +import com.android.systemui.shade.ShadeExpansionStateManager +import com.android.systemui.statusbar.LockscreenShadeTransitionController +import com.android.systemui.statusbar.StatusBarState +import com.android.systemui.statusbar.notification.stack.StackStateAnimator +import com.android.systemui.statusbar.phone.KeyguardBouncer +import com.android.systemui.statusbar.phone.KeyguardBouncer.BouncerExpansionCallback +import com.android.systemui.statusbar.phone.StatusBarKeyguardViewManager +import com.android.systemui.statusbar.phone.StatusBarKeyguardViewManager.AlternateAuthInterceptor +import com.android.systemui.statusbar.phone.StatusBarKeyguardViewManager.KeyguardViewManagerCallback +import com.android.systemui.statusbar.phone.SystemUIDialogManager +import com.android.systemui.statusbar.phone.UnlockedScreenOffAnimationController +import com.android.systemui.statusbar.policy.ConfigurationController +import com.android.systemui.statusbar.policy.KeyguardStateController +import com.android.systemui.util.time.SystemClock +import java.io.PrintWriter +import kotlinx.coroutines.CoroutineScope +import kotlinx.coroutines.Job +import kotlinx.coroutines.flow.collect +import kotlinx.coroutines.launch -import android.animation.ValueAnimator; -import android.annotation.NonNull; -import android.content.res.Configuration; -import android.util.MathUtils; -import android.view.MotionEvent; - -import com.android.keyguard.BouncerPanelExpansionCalculator; -import com.android.keyguard.KeyguardUpdateMonitor; -import com.android.systemui.R; -import com.android.systemui.animation.ActivityLaunchAnimator; -import com.android.systemui.animation.Interpolators; -import com.android.systemui.dump.DumpManager; -import com.android.systemui.plugins.statusbar.StatusBarStateController; -import com.android.systemui.shade.ShadeExpansionChangeEvent; -import com.android.systemui.shade.ShadeExpansionListener; -import com.android.systemui.shade.ShadeExpansionStateManager; -import com.android.systemui.statusbar.LockscreenShadeTransitionController; -import com.android.systemui.statusbar.StatusBarState; -import com.android.systemui.statusbar.notification.stack.StackStateAnimator; -import com.android.systemui.statusbar.phone.KeyguardBouncer; -import com.android.systemui.statusbar.phone.StatusBarKeyguardViewManager; -import com.android.systemui.statusbar.phone.SystemUIDialogManager; -import com.android.systemui.statusbar.phone.UnlockedScreenOffAnimationController; -import com.android.systemui.statusbar.policy.ConfigurationController; -import com.android.systemui.statusbar.policy.KeyguardStateController; -import com.android.systemui.util.time.SystemClock; - -import java.io.PrintWriter; - -/** - * Class that coordinates non-HBM animations during keyguard authentication. - */ -public class UdfpsKeyguardViewController extends UdfpsAnimationViewController { - public static final String TAG = "UdfpsKeyguardViewCtrl"; - @NonNull private final StatusBarKeyguardViewManager mKeyguardViewManager; - @NonNull private final KeyguardUpdateMonitor mKeyguardUpdateMonitor; - @NonNull private final LockscreenShadeTransitionController mLockScreenShadeTransitionController; - @NonNull private final ConfigurationController mConfigurationController; - @NonNull private final SystemClock mSystemClock; - @NonNull private final KeyguardStateController mKeyguardStateController; - @NonNull private final UdfpsController mUdfpsController; - @NonNull private final UnlockedScreenOffAnimationController - mUnlockedScreenOffAnimationController; - @NonNull private final ActivityLaunchAnimator mActivityLaunchAnimator; - private final ValueAnimator mUnlockedScreenOffDozeAnimator = ValueAnimator.ofFloat(0f, 1f); - - private boolean mShowingUdfpsBouncer; - private boolean mUdfpsRequested; - private float mQsExpansion; - private boolean mFaceDetectRunning; - private int mStatusBarState; - private float mTransitionToFullShadeProgress; - private float mLastDozeAmount; - private long mLastUdfpsBouncerShowTime = -1; - private float mPanelExpansionFraction; - private boolean mLaunchTransitionFadingAway; - private boolean mIsLaunchingActivity; - private float mActivityLaunchProgress; - - /** - * hidden amount of pin/pattern/password bouncer - * {@link KeyguardBouncer#EXPANSION_VISIBLE} (0f) to - * {@link KeyguardBouncer#EXPANSION_HIDDEN} (1f) - */ - private float mInputBouncerHiddenAmount; - private boolean mIsGenericBouncerShowing; // whether UDFPS bouncer or input bouncer is visible - - protected UdfpsKeyguardViewController( - @NonNull UdfpsKeyguardView view, - @NonNull StatusBarStateController statusBarStateController, - @NonNull ShadeExpansionStateManager shadeExpansionStateManager, - @NonNull StatusBarKeyguardViewManager statusBarKeyguardViewManager, - @NonNull KeyguardUpdateMonitor keyguardUpdateMonitor, - @NonNull DumpManager dumpManager, - @NonNull LockscreenShadeTransitionController transitionController, - @NonNull ConfigurationController configurationController, - @NonNull SystemClock systemClock, - @NonNull KeyguardStateController keyguardStateController, - @NonNull UnlockedScreenOffAnimationController unlockedScreenOffAnimationController, - @NonNull SystemUIDialogManager systemUIDialogManager, - @NonNull UdfpsController udfpsController, - @NonNull ActivityLaunchAnimator activityLaunchAnimator) { - super(view, statusBarStateController, shadeExpansionStateManager, systemUIDialogManager, - dumpManager); - mKeyguardViewManager = statusBarKeyguardViewManager; - mKeyguardUpdateMonitor = keyguardUpdateMonitor; - mLockScreenShadeTransitionController = transitionController; - mConfigurationController = configurationController; - mSystemClock = systemClock; - mKeyguardStateController = keyguardStateController; - mUdfpsController = udfpsController; - mUnlockedScreenOffAnimationController = unlockedScreenOffAnimationController; - mActivityLaunchAnimator = activityLaunchAnimator; - - mUnlockedScreenOffDozeAnimator.setDuration(StackStateAnimator.ANIMATION_DURATION_STANDARD); - mUnlockedScreenOffDozeAnimator.setInterpolator(Interpolators.ALPHA_IN); - mUnlockedScreenOffDozeAnimator.addUpdateListener( - new ValueAnimator.AnimatorUpdateListener() { - @Override - public void onAnimationUpdate(ValueAnimator animation) { - mView.onDozeAmountChanged( - animation.getAnimatedFraction(), - (float) animation.getAnimatedValue(), - UdfpsKeyguardView.ANIMATION_UNLOCKED_SCREEN_OFF); - } - }); - } - - @Override - @NonNull protected String getTag() { - return "UdfpsKeyguardViewController"; - } - - @Override - public void onInit() { - super.onInit(); - mKeyguardViewManager.setAlternateAuthInterceptor(mAlternateAuthInterceptor); - } - - @Override - protected void onViewAttached() { - super.onViewAttached(); - final float dozeAmount = getStatusBarStateController().getDozeAmount(); - mLastDozeAmount = dozeAmount; - mStateListener.onDozeAmountChanged(dozeAmount, dozeAmount); - getStatusBarStateController().addCallback(mStateListener); - - mUdfpsRequested = false; - - mLaunchTransitionFadingAway = mKeyguardStateController.isLaunchTransitionFadingAway(); - mKeyguardStateController.addCallback(mKeyguardStateControllerCallback); - mStatusBarState = getStatusBarStateController().getState(); - mQsExpansion = mKeyguardViewManager.getQsExpansion(); - updateGenericBouncerVisibility(); - mConfigurationController.addCallback(mConfigurationListener); - getShadeExpansionStateManager().addExpansionListener(mShadeExpansionListener); - updateScaleFactor(); - mView.updatePadding(); - updateAlpha(); - updatePauseAuth(); - - mKeyguardViewManager.setAlternateAuthInterceptor(mAlternateAuthInterceptor); - mLockScreenShadeTransitionController.setUdfpsKeyguardViewController(this); - mActivityLaunchAnimator.addListener(mActivityLaunchAnimatorListener); - } - - @Override - protected void onViewDetached() { - super.onViewDetached(); - mFaceDetectRunning = false; - - mKeyguardStateController.removeCallback(mKeyguardStateControllerCallback); - getStatusBarStateController().removeCallback(mStateListener); - mKeyguardViewManager.removeAlternateAuthInterceptor(mAlternateAuthInterceptor); - mKeyguardUpdateMonitor.requestFaceAuthOnOccludingApp(false); - mConfigurationController.removeCallback(mConfigurationListener); - getShadeExpansionStateManager().removeExpansionListener(mShadeExpansionListener); - if (mLockScreenShadeTransitionController.getUdfpsKeyguardViewController() == this) { - mLockScreenShadeTransitionController.setUdfpsKeyguardViewController(null); +/** Class that coordinates non-HBM animations during keyguard authentication. */ +open class UdfpsKeyguardViewController +constructor( + private val view: UdfpsKeyguardView, + statusBarStateController: StatusBarStateController, + shadeExpansionStateManager: ShadeExpansionStateManager, + private val keyguardViewManager: StatusBarKeyguardViewManager, + private val keyguardUpdateMonitor: KeyguardUpdateMonitor, + dumpManager: DumpManager, + private val lockScreenShadeTransitionController: LockscreenShadeTransitionController, + private val configurationController: ConfigurationController, + private val systemClock: SystemClock, + private val keyguardStateController: KeyguardStateController, + private val unlockedScreenOffAnimationController: UnlockedScreenOffAnimationController, + systemUIDialogManager: SystemUIDialogManager, + private val udfpsController: UdfpsController, + private val activityLaunchAnimator: ActivityLaunchAnimator, + featureFlags: FeatureFlags, + private val bouncerInteractor: BouncerInteractor +) : + UdfpsAnimationViewController( + view, + statusBarStateController, + shadeExpansionStateManager, + systemUIDialogManager, + dumpManager + ) { + private val isModernBouncerEnabled: Boolean = featureFlags.isEnabled(Flags.MODERN_BOUNCER) + private var showingUdfpsBouncer = false + private var udfpsRequested = false + private var qsExpansion = 0f + private var faceDetectRunning = false + private var statusBarState = 0 + private var transitionToFullShadeProgress = 0f + private var lastDozeAmount = 0f + private var lastUdfpsBouncerShowTime: Long = -1 + private var panelExpansionFraction = 0f + private var launchTransitionFadingAway = false + private var isLaunchingActivity = false + private var activityLaunchProgress = 0f + private val unlockedScreenOffDozeAnimator = + ValueAnimator.ofFloat(0f, 1f).apply { + duration = StackStateAnimator.ANIMATION_DURATION_STANDARD.toLong() + interpolator = Interpolators.ALPHA_IN + addUpdateListener { animation -> + view.onDozeAmountChanged( + animation.animatedFraction, + animation.animatedValue as Float, + UdfpsKeyguardView.ANIMATION_UNLOCKED_SCREEN_OFF + ) + } } - mActivityLaunchAnimator.removeListener(mActivityLaunchAnimatorListener); + /** + * Hidden amount of input (pin/pattern/password) bouncer. This is used + * [KeyguardBouncer.EXPANSION_VISIBLE] (0f) to [KeyguardBouncer.EXPANSION_HIDDEN] (1f). Only + * used for the non-modernBouncer. + */ + private var inputBouncerHiddenAmount = KeyguardBouncer.EXPANSION_HIDDEN + private var inputBouncerExpansion = 0f // only used for modernBouncer + + private val stateListener: StatusBarStateController.StateListener = + object : StatusBarStateController.StateListener { + override fun onDozeAmountChanged(linear: Float, eased: Float) { + if (lastDozeAmount < linear) { + showUdfpsBouncer(false) + } + unlockedScreenOffDozeAnimator.cancel() + val animatingFromUnlockedScreenOff = + unlockedScreenOffAnimationController.isAnimationPlaying() + if (animatingFromUnlockedScreenOff && linear != 0f) { + // we manually animate the fade in of the UDFPS icon since the unlocked + // screen off animation prevents the doze amounts to be incrementally eased in + unlockedScreenOffDozeAnimator.start() + } else { + view.onDozeAmountChanged( + linear, + eased, + UdfpsKeyguardView.ANIMATION_BETWEEN_AOD_AND_LOCKSCREEN + ) + } + lastDozeAmount = linear + updatePauseAuth() + } + + override fun onStateChanged(statusBarState: Int) { + this@UdfpsKeyguardViewController.statusBarState = statusBarState + updateAlpha() + updatePauseAuth() + } + } + + private val bouncerExpansionCallback: BouncerExpansionCallback = + object : BouncerExpansionCallback { + override fun onExpansionChanged(expansion: Float) { + inputBouncerHiddenAmount = expansion + updateAlpha() + updatePauseAuth() + } + + override fun onVisibilityChanged(isVisible: Boolean) { + updateBouncerHiddenAmount() + updateAlpha() + updatePauseAuth() + } + } + + private val configurationListener: ConfigurationController.ConfigurationListener = + object : ConfigurationController.ConfigurationListener { + override fun onUiModeChanged() { + view.updateColor() + } + + override fun onThemeChanged() { + view.updateColor() + } + + override fun onConfigChanged(newConfig: Configuration) { + updateScaleFactor() + view.updatePadding() + view.updateColor() + } + } + + private val shadeExpansionListener = ShadeExpansionListener { (fraction) -> + panelExpansionFraction = + if (keyguardViewManager.isBouncerInTransit) { + aboutToShowBouncerProgress(fraction) + } else { + fraction + } + updateAlpha() + updatePauseAuth() } - @Override - public void dump(PrintWriter pw, String[] args) { - super.dump(pw, args); - pw.println("mShowingUdfpsBouncer=" + mShowingUdfpsBouncer); - pw.println("mFaceDetectRunning=" + mFaceDetectRunning); - pw.println("mStatusBarState=" + StatusBarState.toString(mStatusBarState)); - pw.println("mTransitionToFullShadeProgress=" + mTransitionToFullShadeProgress); - pw.println("mQsExpansion=" + mQsExpansion); - pw.println("mIsGenericBouncerShowing=" + mIsGenericBouncerShowing); - pw.println("mInputBouncerHiddenAmount=" + mInputBouncerHiddenAmount); - pw.println("mPanelExpansionFraction=" + mPanelExpansionFraction); - pw.println("unpausedAlpha=" + mView.getUnpausedAlpha()); - pw.println("mUdfpsRequested=" + mUdfpsRequested); - pw.println("mLaunchTransitionFadingAway=" + mLaunchTransitionFadingAway); - pw.println("mLastDozeAmount=" + mLastDozeAmount); + private val keyguardStateControllerCallback: KeyguardStateController.Callback = + object : KeyguardStateController.Callback { + override fun onLaunchTransitionFadingAwayChanged() { + launchTransitionFadingAway = keyguardStateController.isLaunchTransitionFadingAway + updatePauseAuth() + } + } - mView.dump(pw); + private val activityLaunchAnimatorListener: ActivityLaunchAnimator.Listener = + object : ActivityLaunchAnimator.Listener { + override fun onLaunchAnimationStart() { + isLaunchingActivity = true + activityLaunchProgress = 0f + updateAlpha() + } + + override fun onLaunchAnimationEnd() { + isLaunchingActivity = false + updateAlpha() + } + + override fun onLaunchAnimationProgress(linearProgress: Float) { + activityLaunchProgress = linearProgress + updateAlpha() + } + } + + private val statusBarKeyguardViewManagerCallback: KeyguardViewManagerCallback = + object : KeyguardViewManagerCallback { + override fun onQSExpansionChanged(qsExpansion: Float) { + this@UdfpsKeyguardViewController.qsExpansion = qsExpansion + updateAlpha() + updatePauseAuth() + } + + /** + * Forward touches to the UdfpsController. This allows the touch to start from outside + * the sensor area and then slide their finger into the sensor area. + */ + override fun onTouch(event: MotionEvent) { + // Don't forward touches if the shade has already started expanding. + if (transitionToFullShadeProgress != 0f) { + return + } + udfpsController.onTouch(event) + } + } + + private val alternateAuthInterceptor: AlternateAuthInterceptor = + object : AlternateAuthInterceptor { + override fun showAlternateAuthBouncer(): Boolean { + return showUdfpsBouncer(true) + } + + override fun hideAlternateAuthBouncer(): Boolean { + return showUdfpsBouncer(false) + } + + override fun isShowingAlternateAuthBouncer(): Boolean { + return showingUdfpsBouncer + } + + override fun requestUdfps(request: Boolean, color: Int) { + udfpsRequested = request + view.requestUdfps(request, color) + updateAlpha() + updatePauseAuth() + } + + override fun dump(pw: PrintWriter) { + pw.println(tag) + } + } + + override val tag: String + get() = TAG + + override fun onInit() { + super.onInit() + keyguardViewManager.setAlternateAuthInterceptor(alternateAuthInterceptor) + } + + init { + if (isModernBouncerEnabled) { + view.repeatWhenAttached { + // repeatOnLifecycle CREATED (as opposed to STARTED) because the Bouncer expansion + // can make the view not visible; and we still want to listen for events + // that may make the view visible again. + repeatOnLifecycle(Lifecycle.State.CREATED) { listenForBouncerExpansion(this) } + } + } + } + + @VisibleForTesting + internal suspend fun listenForBouncerExpansion(scope: CoroutineScope): Job { + return scope.launch { + bouncerInteractor.bouncerExpansion.collect { bouncerExpansion: Float -> + inputBouncerExpansion = bouncerExpansion + updateAlpha() + updatePauseAuth() + } + } + } + + public override fun onViewAttached() { + super.onViewAttached() + val dozeAmount = statusBarStateController.dozeAmount + lastDozeAmount = dozeAmount + stateListener.onDozeAmountChanged(dozeAmount, dozeAmount) + statusBarStateController.addCallback(stateListener) + udfpsRequested = false + launchTransitionFadingAway = keyguardStateController.isLaunchTransitionFadingAway + keyguardStateController.addCallback(keyguardStateControllerCallback) + statusBarState = statusBarStateController.state + qsExpansion = keyguardViewManager.qsExpansion + keyguardViewManager.addCallback(statusBarKeyguardViewManagerCallback) + if (!isModernBouncerEnabled) { + val bouncer = keyguardViewManager.bouncer + bouncer?.expansion?.let { + bouncerExpansionCallback.onExpansionChanged(it) + bouncer.addBouncerExpansionCallback(bouncerExpansionCallback) + } + updateBouncerHiddenAmount() + } + configurationController.addCallback(configurationListener) + shadeExpansionStateManager.addExpansionListener(shadeExpansionListener) + updateScaleFactor() + view.updatePadding() + updateAlpha() + updatePauseAuth() + keyguardViewManager.setAlternateAuthInterceptor(alternateAuthInterceptor) + lockScreenShadeTransitionController.udfpsKeyguardViewController = this + activityLaunchAnimator.addListener(activityLaunchAnimatorListener) + } + + override fun onViewDetached() { + super.onViewDetached() + faceDetectRunning = false + keyguardStateController.removeCallback(keyguardStateControllerCallback) + statusBarStateController.removeCallback(stateListener) + keyguardViewManager.removeAlternateAuthInterceptor(alternateAuthInterceptor) + keyguardUpdateMonitor.requestFaceAuthOnOccludingApp(false) + configurationController.removeCallback(configurationListener) + shadeExpansionStateManager.removeExpansionListener(shadeExpansionListener) + if (lockScreenShadeTransitionController.udfpsKeyguardViewController === this) { + lockScreenShadeTransitionController.udfpsKeyguardViewController = null + } + activityLaunchAnimator.removeListener(activityLaunchAnimatorListener) + keyguardViewManager.removeCallback(statusBarKeyguardViewManagerCallback) + if (!isModernBouncerEnabled) { + keyguardViewManager.bouncer?.removeBouncerExpansionCallback(bouncerExpansionCallback) + } + } + + override fun dump(pw: PrintWriter, args: Array) { + super.dump(pw, args) + pw.println("isModernBouncerEnabled=$isModernBouncerEnabled") + pw.println("showingUdfpsAltBouncer=$showingUdfpsBouncer") + pw.println("faceDetectRunning=$faceDetectRunning") + pw.println("statusBarState=" + StatusBarState.toString(statusBarState)) + pw.println("transitionToFullShadeProgress=$transitionToFullShadeProgress") + pw.println("qsExpansion=$qsExpansion") + pw.println("panelExpansionFraction=$panelExpansionFraction") + pw.println("unpausedAlpha=" + view.unpausedAlpha) + pw.println("udfpsRequestedByApp=$udfpsRequested") + pw.println("launchTransitionFadingAway=$launchTransitionFadingAway") + pw.println("lastDozeAmount=$lastDozeAmount") + if (isModernBouncerEnabled) { + pw.println("inputBouncerExpansion=$inputBouncerExpansion") + } else { + pw.println("inputBouncerHiddenAmount=$inputBouncerHiddenAmount") + } + view.dump(pw) } /** * Overrides non-bouncer show logic in shouldPauseAuth to still show icon. * @return whether the udfpsBouncer has been newly shown or hidden */ - private boolean showUdfpsBouncer(boolean show) { - if (mShowingUdfpsBouncer == show) { - return false; + private fun showUdfpsBouncer(show: Boolean): Boolean { + if (showingUdfpsBouncer == show) { + return false } - - boolean udfpsAffordanceWasNotShowing = shouldPauseAuth(); - mShowingUdfpsBouncer = show; - if (mShowingUdfpsBouncer) { - mLastUdfpsBouncerShowTime = mSystemClock.uptimeMillis(); + val udfpsAffordanceWasNotShowing = shouldPauseAuth() + showingUdfpsBouncer = show + if (showingUdfpsBouncer) { + lastUdfpsBouncerShowTime = systemClock.uptimeMillis() } - if (mShowingUdfpsBouncer) { + if (showingUdfpsBouncer) { if (udfpsAffordanceWasNotShowing) { - mView.animateInUdfpsBouncer(null); + view.animateInUdfpsBouncer(null) } - - if (mKeyguardStateController.isOccluded()) { - mKeyguardUpdateMonitor.requestFaceAuthOnOccludingApp(true); + if (keyguardStateController.isOccluded) { + keyguardUpdateMonitor.requestFaceAuthOnOccludingApp(true) } - - mView.announceForAccessibility(mView.getContext().getString( - R.string.accessibility_fingerprint_bouncer)); + view.announceForAccessibility( + view.context.getString(R.string.accessibility_fingerprint_bouncer) + ) } else { - mKeyguardUpdateMonitor.requestFaceAuthOnOccludingApp(false); + keyguardUpdateMonitor.requestFaceAuthOnOccludingApp(false) } - - updateGenericBouncerVisibility(); - updateAlpha(); - updatePauseAuth(); - return true; + updateBouncerHiddenAmount() + updateAlpha() + updatePauseAuth() + return true } /** * Returns true if the fingerprint manager is running but we want to temporarily pause - * authentication. On the keyguard, we may want to show udfps when the shade - * is expanded, so this can be overridden with the showBouncer method. + * authentication. On the keyguard, we may want to show udfps when the shade is expanded, so + * this can be overridden with the showBouncer method. */ - public boolean shouldPauseAuth() { - if (mShowingUdfpsBouncer) { - return false; + override fun shouldPauseAuth(): Boolean { + if (showingUdfpsBouncer) { + return false } - - if (mUdfpsRequested && !getNotificationShadeVisible() - && (!mIsGenericBouncerShowing - || mInputBouncerHiddenAmount != KeyguardBouncer.EXPANSION_VISIBLE) - && mKeyguardStateController.isShowing()) { - return false; + if ( + udfpsRequested && + !notificationShadeVisible && + !isInputBouncerFullyVisible() && + keyguardStateController.isShowing + ) { + return false } - - if (mLaunchTransitionFadingAway) { - return true; + if (launchTransitionFadingAway) { + return true } // Only pause auth if we're not on the keyguard AND we're not transitioning to doze // (ie: dozeAmount = 0f). For the UnlockedScreenOffAnimation, the statusBarState is - // delayed. However, we still animate in the UDFPS affordance with the + // delayed. However, we still animate in the UDFPS affordance with the // mUnlockedScreenOffDozeAnimator. - if (mStatusBarState != KEYGUARD && mLastDozeAmount == 0f) { - return true; + if (statusBarState != StatusBarState.KEYGUARD && lastDozeAmount == 0f) { + return true } - - if (mInputBouncerHiddenAmount < .5f) { - return true; + if (isBouncerExpansionGreaterThan(.5f)) { + return true } - - if (mView.getUnpausedAlpha() < (255 * .1)) { - return true; - } - - return false; + return view.unpausedAlpha < 255 * .1 } - @Override - public boolean listenForTouchesOutsideView() { - return true; + fun isBouncerExpansionGreaterThan(bouncerExpansionThreshold: Float): Boolean { + return if (isModernBouncerEnabled) { + inputBouncerExpansion >= bouncerExpansionThreshold + } else { + inputBouncerHiddenAmount < bouncerExpansionThreshold + } } - @Override - public void onTouchOutsideView() { - maybeShowInputBouncer(); + fun isInputBouncerFullyVisible(): Boolean { + return if (isModernBouncerEnabled) { + inputBouncerExpansion == 1f + } else { + keyguardViewManager.isBouncerShowing && !keyguardViewManager.isShowingAlternateAuth + } + } + + override fun listenForTouchesOutsideView(): Boolean { + return true + } + + override fun onTouchOutsideView() { + maybeShowInputBouncer() } /** @@ -291,258 +460,91 @@ public class UdfpsKeyguardViewController extends UdfpsAnimationViewController 200; + private fun hasUdfpsBouncerShownWithMinTime(): Boolean { + return systemClock.uptimeMillis() - lastUdfpsBouncerShowTime > 200 } /** * Set the progress we're currently transitioning to the full shade. 0.0f means we're not - * transitioning yet, while 1.0f means we've fully dragged down. - * - * For example, start swiping down to expand the notification shade from the empty space in - * the middle of the lock screen. + * transitioning yet, while 1.0f means we've fully dragged down. For example, start swiping down + * to expand the notification shade from the empty space in the middle of the lock screen. */ - public void setTransitionToFullShadeProgress(float progress) { - mTransitionToFullShadeProgress = progress; - updateAlpha(); + fun setTransitionToFullShadeProgress(progress: Float) { + transitionToFullShadeProgress = progress + updateAlpha() } /** - * Update alpha for the UDFPS lock screen affordance. The AoD UDFPS visual affordance's - * alpha is based on the doze amount. + * Update alpha for the UDFPS lock screen affordance. The AoD UDFPS visual affordance's alpha is + * based on the doze amount. */ - @Override - public void updateAlpha() { + override fun updateAlpha() { // Fade icon on transitions to showing the status bar or bouncer, but if mUdfpsRequested, // then the keyguard is occluded by some application - so instead use the input bouncer // hidden amount to determine the fade. - float expansion = mUdfpsRequested ? mInputBouncerHiddenAmount : mPanelExpansionFraction; - - int alpha = mShowingUdfpsBouncer ? 255 - : (int) MathUtils.constrain( - MathUtils.map(.5f, .9f, 0f, 255f, expansion), - 0f, 255f); - - if (!mShowingUdfpsBouncer) { + val expansion = if (udfpsRequested) getInputBouncerHiddenAmt() else panelExpansionFraction + var alpha: Int = + if (showingUdfpsBouncer) 255 + else MathUtils.constrain(MathUtils.map(.5f, .9f, 0f, 255f, expansion), 0f, 255f).toInt() + if (!showingUdfpsBouncer) { // swipe from top of the lockscreen to expand full QS: - alpha *= (1.0f - Interpolators.EMPHASIZED_DECELERATE.getInterpolation(mQsExpansion)); + alpha = + (alpha * (1.0f - Interpolators.EMPHASIZED_DECELERATE.getInterpolation(qsExpansion))) + .toInt() // swipe from the middle (empty space) of lockscreen to expand the notification shade: - alpha *= (1.0f - mTransitionToFullShadeProgress); + alpha = (alpha * (1.0f - transitionToFullShadeProgress)).toInt() // Fade out the icon if we are animating an activity launch over the lockscreen and the // activity didn't request the UDFPS. - if (mIsLaunchingActivity && !mUdfpsRequested) { - alpha *= (1.0f - mActivityLaunchProgress); + if (isLaunchingActivity && !udfpsRequested) { + alpha = (alpha * (1.0f - activityLaunchProgress)).toInt() } // Fade out alpha when a dialog is shown // Fade in alpha when a dialog is hidden - alpha *= mView.getDialogSuggestedAlpha(); + alpha = (alpha * view.dialogSuggestedAlpha).toInt() } - mView.setUnpausedAlpha(alpha); + view.unpausedAlpha = alpha } - /** - * Updates mIsGenericBouncerShowing (whether any bouncer is showing) and updates the - * mInputBouncerHiddenAmount to reflect whether the input bouncer is fully showing or not. - */ - private void updateGenericBouncerVisibility() { - mIsGenericBouncerShowing = mKeyguardViewManager.isBouncerShowing(); // includes altBouncer - final boolean altBouncerShowing = mKeyguardViewManager.isShowingAlternateAuth(); - if (altBouncerShowing || !mKeyguardViewManager.bouncerIsOrWillBeShowing()) { - mInputBouncerHiddenAmount = 1f; - } else if (mIsGenericBouncerShowing) { + private fun getInputBouncerHiddenAmt(): Float { + return if (isModernBouncerEnabled) { + 1f - inputBouncerExpansion + } else { + inputBouncerHiddenAmount + } + } + + /** Update the scale factor based on the device's resolution. */ + private fun updateScaleFactor() { + udfpsController.mOverlayParams?.scaleFactor?.let { view.setScaleFactor(it) } + } + + private fun updateBouncerHiddenAmount() { + if (isModernBouncerEnabled) { + return + } + val altBouncerShowing = keyguardViewManager.isShowingAlternateAuth + if (altBouncerShowing || !keyguardViewManager.bouncerIsOrWillBeShowing()) { + inputBouncerHiddenAmount = 1f + } else if (keyguardViewManager.isBouncerShowing) { // input bouncer is fully showing - mInputBouncerHiddenAmount = 0f; + inputBouncerHiddenAmount = 0f } } - /** - * Update the scale factor based on the device's resolution. - */ - private void updateScaleFactor() { - if (mUdfpsController != null && mUdfpsController.mOverlayParams != null) { - mView.setScaleFactor(mUdfpsController.mOverlayParams.getScaleFactor()); - } + companion object { + const val TAG = "UdfpsKeyguardViewController" } - - private final StatusBarStateController.StateListener mStateListener = - new StatusBarStateController.StateListener() { - @Override - public void onDozeAmountChanged(float linear, float eased) { - if (mLastDozeAmount < linear) { - showUdfpsBouncer(false); - } - mUnlockedScreenOffDozeAnimator.cancel(); - final boolean animatingFromUnlockedScreenOff = - mUnlockedScreenOffAnimationController.isAnimationPlaying(); - if (animatingFromUnlockedScreenOff && linear != 0f) { - // we manually animate the fade in of the UDFPS icon since the unlocked - // screen off animation prevents the doze amounts to be incrementally eased in - mUnlockedScreenOffDozeAnimator.start(); - } else { - mView.onDozeAmountChanged(linear, eased, - UdfpsKeyguardView.ANIMATION_BETWEEN_AOD_AND_LOCKSCREEN); - } - - mLastDozeAmount = linear; - updatePauseAuth(); - } - - @Override - public void onStateChanged(int statusBarState) { - mStatusBarState = statusBarState; - updateAlpha(); - updatePauseAuth(); - } - }; - - private final StatusBarKeyguardViewManager.AlternateAuthInterceptor mAlternateAuthInterceptor = - new StatusBarKeyguardViewManager.AlternateAuthInterceptor() { - @Override - public boolean showAlternateAuthBouncer() { - return showUdfpsBouncer(true); - } - - @Override - public boolean hideAlternateAuthBouncer() { - return showUdfpsBouncer(false); - } - - @Override - public boolean isShowingAlternateAuthBouncer() { - return mShowingUdfpsBouncer; - } - - @Override - public void requestUdfps(boolean request, int color) { - mUdfpsRequested = request; - mView.requestUdfps(request, color); - updateAlpha(); - updatePauseAuth(); - } - - @Override - public boolean isAnimating() { - return false; - } - - /** - * Set the amount qs is expanded. Forxample, swipe down from the top of the - * lock screen to start the full QS expansion. - */ - @Override - public void setQsExpansion(float qsExpansion) { - mQsExpansion = qsExpansion; - updateAlpha(); - updatePauseAuth(); - } - - @Override - public boolean onTouch(MotionEvent event) { - if (mTransitionToFullShadeProgress != 0) { - return false; - } - return mUdfpsController.onTouch(event); - } - - @Override - public void setBouncerExpansionChanged(float expansion) { - mInputBouncerHiddenAmount = expansion; - updateAlpha(); - updatePauseAuth(); - } - - /** - * Only called on primary auth bouncer changes, not on whether the UDFPS bouncer - * visibility changes. - */ - @Override - public void onBouncerVisibilityChanged() { - updateGenericBouncerVisibility(); - updateAlpha(); - updatePauseAuth(); - } - - @Override - public void dump(PrintWriter pw) { - pw.println(getTag()); - } - }; - - private final ConfigurationController.ConfigurationListener mConfigurationListener = - new ConfigurationController.ConfigurationListener() { - @Override - public void onUiModeChanged() { - mView.updateColor(); - } - - @Override - public void onThemeChanged() { - mView.updateColor(); - } - - @Override - public void onConfigChanged(Configuration newConfig) { - updateScaleFactor(); - mView.updatePadding(); - mView.updateColor(); - } - }; - - private final ShadeExpansionListener mShadeExpansionListener = new ShadeExpansionListener() { - @Override - public void onPanelExpansionChanged(ShadeExpansionChangeEvent event) { - float fraction = event.getFraction(); - mPanelExpansionFraction = - mKeyguardViewManager.isBouncerInTransit() ? BouncerPanelExpansionCalculator - .aboutToShowBouncerProgress(fraction) : fraction; - updateAlpha(); - updatePauseAuth(); - } - }; - - private final KeyguardStateController.Callback mKeyguardStateControllerCallback = - new KeyguardStateController.Callback() { - @Override - public void onLaunchTransitionFadingAwayChanged() { - mLaunchTransitionFadingAway = - mKeyguardStateController.isLaunchTransitionFadingAway(); - updatePauseAuth(); - } - }; - - private final ActivityLaunchAnimator.Listener mActivityLaunchAnimatorListener = - new ActivityLaunchAnimator.Listener() { - @Override - public void onLaunchAnimationStart() { - mIsLaunchingActivity = true; - mActivityLaunchProgress = 0f; - updateAlpha(); - } - - @Override - public void onLaunchAnimationEnd() { - mIsLaunchingActivity = false; - updateAlpha(); - } - - @Override - public void onLaunchAnimationProgress(float linearProgress) { - mActivityLaunchProgress = linearProgress; - updateAlpha(); - } - }; } 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 index 543389e0a7cd0..76b8f26ea92c9 100644 --- a/packages/SystemUI/src/com/android/systemui/keyguard/data/repository/KeyguardBouncerRepository.kt +++ b/packages/SystemUI/src/com/android/systemui/keyguard/data/repository/KeyguardBouncerRepository.kt @@ -24,7 +24,7 @@ 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 com.android.systemui.statusbar.phone.KeyguardBouncer import javax.inject.Inject import kotlinx.coroutines.flow.MutableStateFlow import kotlinx.coroutines.flow.asStateFlow @@ -41,9 +41,15 @@ constructor( /** 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() + /** + * Set how much of the panel is showing on the screen. + * ``` + * 0f = panel fully hidden = bouncer fully showing + * 1f = panel fully showing = bouncer fully hidden + * ``` + */ + private val _panelExpansionAmount = MutableStateFlow(KeyguardBouncer.EXPANSION_HIDDEN) + val panelExpansionAmount = _panelExpansionAmount.asStateFlow() private val _isVisible = MutableStateFlow(false) val isVisible = _isVisible.asStateFlow() private val _show = MutableStateFlow(null) @@ -96,8 +102,8 @@ constructor( _isScrimmed.value = isScrimmed } - fun setExpansion(expansion: Float) { - _expansionAmount.value = expansion + fun setPanelExpansion(panelExpansion: Float) { + _panelExpansionAmount.value = panelExpansion } fun setVisible(isVisible: Boolean) { 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 index 2af9318d92ec3..e644347f5577e 100644 --- a/packages/SystemUI/src/com/android/systemui/keyguard/domain/interactor/BouncerInteractor.kt +++ b/packages/SystemUI/src/com/android/systemui/keyguard/domain/interactor/BouncerInteractor.kt @@ -40,6 +40,7 @@ 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.combine import kotlinx.coroutines.flow.filter import kotlinx.coroutines.flow.filterNotNull import kotlinx.coroutines.flow.map @@ -77,7 +78,7 @@ constructor( KeyguardBouncerModel( promptReason = repository.bouncerPromptReason ?: 0, errorMessage = repository.bouncerErrorMessage, - expansionAmount = repository.expansionAmount.value + expansionAmount = repository.panelExpansionAmount.value ) ) repository.setShowingSoon(false) @@ -90,7 +91,6 @@ constructor( 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() @@ -98,6 +98,17 @@ constructor( repository.onDismissAction.filterNotNull() val resourceUpdateRequests: Flow = repository.resourceUpdateRequests.filter { it } val keyguardPosition: Flow = repository.keyguardPosition + val panelExpansionAmount: Flow = repository.panelExpansionAmount + /** 0f = bouncer fully hidden. 1f = bouncer fully visible. */ + val bouncerExpansion: Flow = // + combine(repository.panelExpansionAmount, repository.isVisible) { expansionAmount, isVisible + -> + if (isVisible) { + 1f - expansionAmount + } else { + 0f + } + } // TODO(b/243685699): Move isScrimmed logic to data layer. // TODO(b/243695312): Encapsulate all of the show logic for the bouncer. @@ -128,7 +139,7 @@ constructor( Trace.beginSection("KeyguardBouncer#show") repository.setScrimmed(isScrimmed) if (isScrimmed) { - setExpansion(KeyguardBouncer.EXPANSION_VISIBLE) + setPanelExpansion(KeyguardBouncer.EXPANSION_VISIBLE) } if (resumeBouncer) { @@ -176,14 +187,17 @@ constructor( } /** - * Sets the panel expansion which is calculated further upstream. Expansion is from 0f to 1f - * where 0f => showing and 1f => hiding + * Sets the panel expansion which is calculated further upstream. Panel expansion is from 0f + * (panel fully hidden) to 1f (panel fully showing). As the panel shows (from 0f => 1f), the + * bouncer hides and as the panel becomes hidden (1f => 0f), the bouncer starts to show. + * Therefore, a panel expansion of 1f represents the bouncer fully hidden and a panel expansion + * of 0f represents the bouncer fully showing. */ - fun setExpansion(expansion: Float) { - val oldExpansion = repository.expansionAmount.value + fun setPanelExpansion(expansion: Float) { + val oldExpansion = repository.panelExpansionAmount.value val expansionChanged = oldExpansion != expansion if (repository.startingDisappearAnimation.value == null) { - repository.setExpansion(expansion) + repository.setPanelExpansion(expansion) } if ( @@ -282,7 +296,7 @@ constructor( /** Returns whether bouncer is fully showing. */ fun isFullyShowing(): Boolean { return (repository.showingSoon.value || repository.isVisible.value) && - repository.expansionAmount.value == KeyguardBouncer.EXPANSION_VISIBLE && + repository.panelExpansionAmount.value == KeyguardBouncer.EXPANSION_VISIBLE && repository.startingDisappearAnimation.value == null } @@ -294,8 +308,8 @@ constructor( /** 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 + repository.panelExpansionAmount.value != KeyguardBouncer.EXPANSION_HIDDEN && + repository.panelExpansionAmount.value != KeyguardBouncer.EXPANSION_VISIBLE } /** Return whether bouncer is animating away. */ 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 index 9ad52117bfc62..9b76a2c9b073b 100644 --- a/packages/SystemUI/src/com/android/systemui/keyguard/ui/viewmodel/KeyguardBouncerViewModel.kt +++ b/packages/SystemUI/src/com/android/systemui/keyguard/ui/viewmodel/KeyguardBouncerViewModel.kt @@ -38,7 +38,7 @@ constructor( private val interactor: BouncerInteractor, ) { /** Observe on bouncer expansion amount. */ - val bouncerExpansionAmount: Flow = interactor.expansionAmount + val bouncerExpansionAmount: Flow = interactor.panelExpansionAmount /** Observe on bouncer visibility. */ val isBouncerVisible: Flow = interactor.isVisible diff --git a/packages/SystemUI/src/com/android/systemui/shade/NotificationPanelViewController.java b/packages/SystemUI/src/com/android/systemui/shade/NotificationPanelViewController.java index e00f5ebad571c..106fc75ac15a5 100644 --- a/packages/SystemUI/src/com/android/systemui/shade/NotificationPanelViewController.java +++ b/packages/SystemUI/src/com/android/systemui/shade/NotificationPanelViewController.java @@ -4644,7 +4644,7 @@ public final class NotificationPanelViewController { mUpdateFlingVelocity = vel; } } else if (!mCentralSurfaces.isBouncerShowing() - && !mStatusBarKeyguardViewManager.isShowingAlternateAuthOrAnimating() + && !mStatusBarKeyguardViewManager.isShowingAlternateAuth() && !mKeyguardStateController.isKeyguardGoingAway()) { onEmptySpaceClick(); onTrackingStopped(true); diff --git a/packages/SystemUI/src/com/android/systemui/shade/NotificationShadeWindowViewController.java b/packages/SystemUI/src/com/android/systemui/shade/NotificationShadeWindowViewController.java index 65bd58d0d8011..1e63b2dd134f1 100644 --- a/packages/SystemUI/src/com/android/systemui/shade/NotificationShadeWindowViewController.java +++ b/packages/SystemUI/src/com/android/systemui/shade/NotificationShadeWindowViewController.java @@ -284,7 +284,7 @@ public class NotificationShadeWindowViewController { return true; } - if (mStatusBarKeyguardViewManager.isShowingAlternateAuthOrAnimating()) { + if (mStatusBarKeyguardViewManager.isShowingAlternateAuth()) { // capture all touches if the alt auth bouncer is showing return true; } @@ -322,7 +322,7 @@ public class NotificationShadeWindowViewController { handled = !mService.isPulsing(); } - if (mStatusBarKeyguardViewManager.isShowingAlternateAuthOrAnimating()) { + if (mStatusBarKeyguardViewManager.isShowingAlternateAuth()) { // eat the touch handled = true; } 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 9bb4132490d4e..b2a9509a03b7e 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/KeyguardBouncer.java +++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/KeyguardBouncer.java @@ -64,6 +64,11 @@ public class KeyguardBouncer { private static final String TAG = "KeyguardBouncer"; static final long BOUNCER_FACE_DELAY = 1200; public static final float ALPHA_EXPANSION_THRESHOLD = 0.95f; + /** + * Values for the bouncer expansion represented as the panel expansion. + * Panel expansion 1f = panel fully showing = bouncer fully hidden + * Panel expansion 0f = panel fully hiding = bouncer fully showing + */ public static final float EXPANSION_HIDDEN = 1f; public static final float EXPANSION_VISIBLE = 0f; @@ -142,6 +147,14 @@ public class KeyguardBouncer { mExpansionCallbacks.add(expansionCallback); } + /** + * Get the KeyguardBouncer expansion + * @return 1=HIDDEN, 0=SHOWING, in between 0 and 1 means the bouncer is in transition. + */ + public float getExpansion() { + return mExpansion; + } + /** * Enable/disable only the back button */ 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 ccb5d8800ddb4..f2d603ff0ca42 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarKeyguardViewManager.java +++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarKeyguardViewManager.java @@ -86,8 +86,10 @@ import com.android.systemui.unfold.SysUIUnfoldComponent; import java.io.PrintWriter; import java.util.ArrayList; +import java.util.HashSet; import java.util.Objects; import java.util.Optional; +import java.util.Set; import javax.inject.Inject; @@ -166,9 +168,6 @@ public class StatusBarKeyguardViewManager implements RemoteInputController.Callb @Override public void onExpansionChanged(float expansion) { - if (mAlternateAuthInterceptor != null) { - mAlternateAuthInterceptor.setBouncerExpansionChanged(expansion); - } if (mBouncerAnimating) { mCentralSurfaces.setBouncerHiddenFraction(expansion); } @@ -184,9 +183,6 @@ public class StatusBarKeyguardViewManager implements RemoteInputController.Callb if (!isVisible) { mCentralSurfaces.setBouncerHiddenFraction(KeyguardBouncer.EXPANSION_HIDDEN); } - if (mAlternateAuthInterceptor != null) { - mAlternateAuthInterceptor.onBouncerVisibilityChanged(); - } /* Register predictive back callback when keyguard becomes visible, and unregister when it's hidden. */ @@ -252,6 +248,7 @@ public class StatusBarKeyguardViewManager implements RemoteInputController.Callb private int mLastBiometricMode; private boolean mLastScreenOffAnimationPlaying; private float mQsExpansion; + final Set mCallbacks = new HashSet<>(); private boolean mIsModernBouncerEnabled; private OnDismissAction mAfterKeyguardGoneAction; @@ -465,7 +462,7 @@ public class StatusBarKeyguardViewManager implements RemoteInputController.Callb if (mBouncer != null) { mBouncer.setExpansion(KeyguardBouncer.EXPANSION_HIDDEN); } else { - mBouncerInteractor.setExpansion(KeyguardBouncer.EXPANSION_HIDDEN); + mBouncerInteractor.setPanelExpansion(KeyguardBouncer.EXPANSION_HIDDEN); } } else if (mStatusBarStateController.getState() == StatusBarState.SHADE_LOCKED) { // Don't expand to the bouncer. Instead transition back to the lock screen (see @@ -475,7 +472,7 @@ public class StatusBarKeyguardViewManager implements RemoteInputController.Callb if (mBouncer != null) { mBouncer.setExpansion(KeyguardBouncer.EXPANSION_VISIBLE); } else { - mBouncerInteractor.setExpansion(KeyguardBouncer.EXPANSION_VISIBLE); + mBouncerInteractor.setPanelExpansion(KeyguardBouncer.EXPANSION_VISIBLE); } } else if (mKeyguardStateController.isShowing() && !hideBouncerOverDream) { if (!isWakeAndUnlocking() @@ -485,7 +482,7 @@ public class StatusBarKeyguardViewManager implements RemoteInputController.Callb if (mBouncer != null) { mBouncer.setExpansion(fraction); } else { - mBouncerInteractor.setExpansion(fraction); + mBouncerInteractor.setPanelExpansion(fraction); } } if (fraction != KeyguardBouncer.EXPANSION_HIDDEN && tracking @@ -504,7 +501,7 @@ public class StatusBarKeyguardViewManager implements RemoteInputController.Callb if (mBouncer != null) { mBouncer.setExpansion(KeyguardBouncer.EXPANSION_HIDDEN); } else { - mBouncerInteractor.setExpansion(KeyguardBouncer.EXPANSION_HIDDEN); + mBouncerInteractor.setPanelExpansion(KeyguardBouncer.EXPANSION_HIDDEN); } } else if (mPulsing && fraction == KeyguardBouncer.EXPANSION_VISIBLE) { // Panel expanded while pulsing but didn't translate the bouncer (because we are @@ -1356,7 +1353,7 @@ public class StatusBarKeyguardViewManager implements RemoteInputController.Callb mBouncerInteractor.notifyKeyguardAuthenticated(strongAuth); } - if (mAlternateAuthInterceptor != null && isShowingAlternateAuthOrAnimating()) { + if (mAlternateAuthInterceptor != null && isShowingAlternateAuth()) { resetAlternateAuth(false); executeAfterKeyguardGoneAction(); } @@ -1442,6 +1439,10 @@ public class StatusBarKeyguardViewManager implements RemoteInputController.Callb pw.println(" mPendingWakeupAction: " + mPendingWakeupAction); pw.println(" isBouncerShowing(): " + isBouncerShowing()); pw.println(" bouncerIsOrWillBeShowing(): " + bouncerIsOrWillBeShowing()); + pw.println(" Registered KeyguardViewManagerCallbacks:"); + for (KeyguardViewManagerCallback callback : mCallbacks) { + pw.println(" " + callback); + } if (mBouncer != null) { mBouncer.dump(pw); @@ -1465,6 +1466,20 @@ public class StatusBarKeyguardViewManager implements RemoteInputController.Callb } } + /** + * Add a callback to listen for changes + */ + public void addCallback(KeyguardViewManagerCallback callback) { + mCallbacks.add(callback); + } + + /** + * Removes callback to stop receiving updates + */ + public void removeCallback(KeyguardViewManagerCallback callback) { + mCallbacks.remove(callback); + } + /** * Whether qs is currently expanded. */ @@ -1477,8 +1492,8 @@ public class StatusBarKeyguardViewManager implements RemoteInputController.Callb */ public void setQsExpansion(float qsExpansion) { mQsExpansion = qsExpansion; - if (mAlternateAuthInterceptor != null) { - mAlternateAuthInterceptor.setQsExpansion(qsExpansion); + for (KeyguardViewManagerCallback callback : mCallbacks) { + callback.onQSExpansionChanged(mQsExpansion); } } @@ -1492,21 +1507,13 @@ public class StatusBarKeyguardViewManager implements RemoteInputController.Callb && mAlternateAuthInterceptor.isShowingAlternateAuthBouncer(); } - public boolean isShowingAlternateAuthOrAnimating() { - return mAlternateAuthInterceptor != null - && (mAlternateAuthInterceptor.isShowingAlternateAuthBouncer() - || mAlternateAuthInterceptor.isAnimating()); - } - /** - * Forward touches to any alternate authentication affordances. + * Forward touches to callbacks. */ - public boolean onTouch(MotionEvent event) { - if (mAlternateAuthInterceptor == null) { - return false; + public void onTouch(MotionEvent event) { + for (KeyguardViewManagerCallback callback: mCallbacks) { + callback.onTouch(event); } - - return mAlternateAuthInterceptor.onTouch(event); } /** Update keyguard position based on a tapped X coordinate. */ @@ -1639,38 +1646,6 @@ public class StatusBarKeyguardViewManager implements RemoteInputController.Callb */ boolean isShowingAlternateAuthBouncer(); - /** - * print information for the alternate auth interceptor registered - */ - void dump(PrintWriter pw); - - /** - * @return true if the new auth method bouncer is currently animating in or out. - */ - boolean isAnimating(); - - /** - * How much QS is fully expanded where 0f is not showing and 1f is fully expanded. - */ - void setQsExpansion(float qsExpansion); - - /** - * Forward potential touches to authentication interceptor - * @return true if event was handled - */ - boolean onTouch(MotionEvent event); - - /** - * Update pin/pattern/password bouncer expansion amount where 0 is visible and 1 is fully - * hidden - */ - void setBouncerExpansionChanged(float expansion); - - /** - * called when the bouncer view visibility has changed. - */ - void onBouncerVisibilityChanged(); - /** * Use when an app occluding the keyguard would like to give the user ability to * unlock the device using udfps. @@ -1680,5 +1655,25 @@ public class StatusBarKeyguardViewManager implements RemoteInputController.Callb */ void requestUdfps(boolean requestUdfps, int color); + /** + * print information for the alternate auth interceptor registered + */ + void dump(PrintWriter pw); + } + + /** + * Callback for KeyguardViewManager state changes. + */ + public interface KeyguardViewManagerCallback { + /** + * Set the amount qs is expanded. For example, swipe down from the top of the + * lock screen to start the full QS expansion. + */ + default void onQSExpansionChanged(float qsExpansion) { } + + /** + * Forward touch events to callbacks + */ + default void onTouch(MotionEvent event) { } } } diff --git a/packages/SystemUI/tests/src/com/android/systemui/biometrics/UdfpsControllerOverlayTest.kt b/packages/SystemUI/tests/src/com/android/systemui/biometrics/UdfpsControllerOverlayTest.kt index c85334db94990..90948ff3b7697 100644 --- a/packages/SystemUI/tests/src/com/android/systemui/biometrics/UdfpsControllerOverlayTest.kt +++ b/packages/SystemUI/tests/src/com/android/systemui/biometrics/UdfpsControllerOverlayTest.kt @@ -42,6 +42,8 @@ import com.android.systemui.R import com.android.systemui.SysuiTestCase import com.android.systemui.animation.ActivityLaunchAnimator import com.android.systemui.dump.DumpManager +import com.android.systemui.flags.FeatureFlags +import com.android.systemui.keyguard.domain.interactor.BouncerInteractor import com.android.systemui.plugins.statusbar.StatusBarStateController import com.android.systemui.shade.ShadeExpansionStateManager import com.android.systemui.statusbar.LockscreenShadeTransitionController @@ -103,6 +105,8 @@ class UdfpsControllerOverlayTest : SysuiTestCase() { @Mock private lateinit var udfpsView: UdfpsView @Mock private lateinit var udfpsEnrollView: UdfpsEnrollView @Mock private lateinit var activityLaunchAnimator: ActivityLaunchAnimator + @Mock private lateinit var featureFlags: FeatureFlags + @Mock private lateinit var bouncerInteractor: BouncerInteractor @Captor private lateinit var layoutParamsCaptor: ArgumentCaptor private val onTouch = { _: View, _: MotionEvent, _: Boolean -> true } @@ -136,7 +140,8 @@ class UdfpsControllerOverlayTest : SysuiTestCase() { keyguardUpdateMonitor, dialogManager, dumpManager, transitionController, configurationController, systemClock, keyguardStateController, unlockedScreenOffAnimationController, udfpsDisplayMode, REQUEST_ID, reason, - controllerCallback, onTouch, activityLaunchAnimator, isDebuggable + controllerCallback, onTouch, activityLaunchAnimator, featureFlags, + bouncerInteractor, isDebuggable ) block() } diff --git a/packages/SystemUI/tests/src/com/android/systemui/biometrics/UdfpsControllerTest.java b/packages/SystemUI/tests/src/com/android/systemui/biometrics/UdfpsControllerTest.java index 28e13b8e81aba..be39c0de22a1e 100644 --- a/packages/SystemUI/tests/src/com/android/systemui/biometrics/UdfpsControllerTest.java +++ b/packages/SystemUI/tests/src/com/android/systemui/biometrics/UdfpsControllerTest.java @@ -69,7 +69,9 @@ import com.android.systemui.R; import com.android.systemui.SysuiTestCase; import com.android.systemui.animation.ActivityLaunchAnimator; import com.android.systemui.dump.DumpManager; +import com.android.systemui.flags.FeatureFlags; import com.android.systemui.keyguard.ScreenLifecycle; +import com.android.systemui.keyguard.domain.interactor.BouncerInteractor; import com.android.systemui.plugins.FalsingManager; import com.android.systemui.plugins.statusbar.StatusBarStateController; import com.android.systemui.shade.ShadeExpansionStateManager; @@ -171,6 +173,8 @@ public class UdfpsControllerTest extends SysuiTestCase { private FakeExecutor mFgExecutor; @Mock private UdfpsDisplayMode mUdfpsDisplayMode; + @Mock + private FeatureFlags mFeatureFlags; // Stuff for configuring mocks @Mock @@ -191,6 +195,8 @@ public class UdfpsControllerTest extends SysuiTestCase { private ActivityLaunchAnimator mActivityLaunchAnimator; @Mock private AlternateUdfpsTouchProvider mAlternateTouchProvider; + @Mock + private BouncerInteractor mBouncerInteractor; // Capture listeners so that they can be used to send events @Captor private ArgumentCaptor mOverlayCaptor; @@ -252,6 +258,7 @@ public class UdfpsControllerTest extends SysuiTestCase { mStatusBarKeyguardViewManager, mDumpManager, mKeyguardUpdateMonitor, + mFeatureFlags, mFalsingManager, mPowerManager, mAccessibilityManager, @@ -270,7 +277,8 @@ public class UdfpsControllerTest extends SysuiTestCase { mLatencyTracker, mActivityLaunchAnimator, Optional.of(mAlternateTouchProvider), - mBiometricsExecutor); + mBiometricsExecutor, + mBouncerInteractor); verify(mFingerprintManager).setUdfpsOverlayController(mOverlayCaptor.capture()); mOverlayController = mOverlayCaptor.getValue(); verify(mScreenLifecycle).addObserver(mScreenObserverCaptor.capture()); diff --git a/packages/SystemUI/tests/src/com/android/systemui/biometrics/UdfpsKeyguardViewControllerBaseTest.java b/packages/SystemUI/tests/src/com/android/systemui/biometrics/UdfpsKeyguardViewControllerBaseTest.java new file mode 100644 index 0000000000000..c313c8dc1a7c9 --- /dev/null +++ b/packages/SystemUI/tests/src/com/android/systemui/biometrics/UdfpsKeyguardViewControllerBaseTest.java @@ -0,0 +1,527 @@ +/* + * Copyright (C) 2020 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.biometrics; + +import static org.junit.Assert.assertFalse; +import static org.junit.Assert.assertTrue; +import static org.mockito.ArgumentMatchers.anyBoolean; +import static org.mockito.ArgumentMatchers.anyInt; +import static org.mockito.Mockito.atLeast; +import static org.mockito.Mockito.atLeastOnce; +import static org.mockito.Mockito.eq; +import static org.mockito.Mockito.never; +import static org.mockito.Mockito.reset; +import static org.mockito.Mockito.times; +import static org.mockito.Mockito.verify; +import static org.mockito.Mockito.when; + +import android.content.Context; + +import com.android.keyguard.KeyguardUpdateMonitor; +import com.android.systemui.SysuiTestCase; +import com.android.systemui.animation.ActivityLaunchAnimator; +import com.android.systemui.dump.DumpManager; +import com.android.systemui.flags.FakeFeatureFlags; +import com.android.systemui.flags.Flags; +import com.android.systemui.keyguard.KeyguardViewMediator; +import com.android.systemui.keyguard.domain.interactor.BouncerInteractor; +import com.android.systemui.plugins.statusbar.StatusBarStateController; +import com.android.systemui.shade.ShadeExpansionChangeEvent; +import com.android.systemui.shade.ShadeExpansionListener; +import com.android.systemui.shade.ShadeExpansionStateManager; +import com.android.systemui.statusbar.LockscreenShadeTransitionController; +import com.android.systemui.statusbar.StatusBarState; +import com.android.systemui.statusbar.phone.KeyguardBouncer; +import com.android.systemui.statusbar.phone.StatusBarKeyguardViewManager; +import com.android.systemui.statusbar.phone.SystemUIDialogManager; +import com.android.systemui.statusbar.phone.UnlockedScreenOffAnimationController; +import com.android.systemui.statusbar.policy.ConfigurationController; +import com.android.systemui.statusbar.policy.KeyguardStateController; +import com.android.systemui.util.concurrency.DelayableExecutor; +import com.android.systemui.util.time.FakeSystemClock; + +import org.junit.Before; +import org.junit.Test; +import org.mockito.ArgumentCaptor; +import org.mockito.Captor; +import org.mockito.Mock; +import org.mockito.MockitoAnnotations; + +import java.util.List; + +public class UdfpsKeyguardViewControllerBaseTest extends SysuiTestCase { + // Dependencies + protected @Mock UdfpsKeyguardView mView; + protected @Mock Context mResourceContext; + protected @Mock StatusBarStateController mStatusBarStateController; + protected @Mock ShadeExpansionStateManager mShadeExpansionStateManager; + protected @Mock StatusBarKeyguardViewManager mStatusBarKeyguardViewManager; + protected @Mock LockscreenShadeTransitionController mLockscreenShadeTransitionController; + protected @Mock DumpManager mDumpManager; + protected @Mock DelayableExecutor mExecutor; + protected @Mock KeyguardUpdateMonitor mKeyguardUpdateMonitor; + protected @Mock KeyguardStateController mKeyguardStateController; + protected @Mock KeyguardViewMediator mKeyguardViewMediator; + protected @Mock ConfigurationController mConfigurationController; + protected @Mock UnlockedScreenOffAnimationController mUnlockedScreenOffAnimationController; + protected @Mock SystemUIDialogManager mDialogManager; + protected @Mock UdfpsController mUdfpsController; + protected @Mock ActivityLaunchAnimator mActivityLaunchAnimator; + protected @Mock KeyguardBouncer mBouncer; + protected @Mock BouncerInteractor mBouncerInteractor; + + protected FakeFeatureFlags mFeatureFlags = new FakeFeatureFlags(); + protected FakeSystemClock mSystemClock = new FakeSystemClock(); + + protected UdfpsKeyguardViewController mController; + + // Capture listeners so that they can be used to send events + private @Captor ArgumentCaptor mStateListenerCaptor; + protected StatusBarStateController.StateListener mStatusBarStateListener; + + private @Captor ArgumentCaptor mExpansionListenerCaptor; + protected List mExpansionListeners; + + private @Captor ArgumentCaptor + mAltAuthInterceptorCaptor; + protected StatusBarKeyguardViewManager.AlternateAuthInterceptor mAltAuthInterceptor; + + private @Captor ArgumentCaptor + mKeyguardStateControllerCallbackCaptor; + protected KeyguardStateController.Callback mKeyguardStateControllerCallback; + + @Before + public void setUp() { + MockitoAnnotations.initMocks(this); + when(mView.getContext()).thenReturn(mResourceContext); + when(mResourceContext.getString(anyInt())).thenReturn("test string"); + when(mKeyguardViewMediator.isAnimatingScreenOff()).thenReturn(false); + when(mView.getUnpausedAlpha()).thenReturn(255); + mController = createUdfpsKeyguardViewController(); + } + + @Test + public void testRegistersExpansionChangedListenerOnAttached() { + mController.onViewAttached(); + captureStatusBarExpansionListeners(); + } + + @Test + public void testRegistersStatusBarStateListenersOnAttached() { + mController.onViewAttached(); + captureStatusBarStateListeners(); + } + + @Test + public void testViewControllerQueriesSBStateOnAttached() { + mController.onViewAttached(); + verify(mStatusBarStateController).getState(); + verify(mStatusBarStateController).getDozeAmount(); + + final float dozeAmount = .88f; + when(mStatusBarStateController.getState()).thenReturn(StatusBarState.SHADE_LOCKED); + when(mStatusBarStateController.getDozeAmount()).thenReturn(dozeAmount); + captureStatusBarStateListeners(); + + mController.onViewAttached(); + verify(mView, atLeast(1)).setPauseAuth(true); + verify(mView).onDozeAmountChanged(dozeAmount, dozeAmount, + UdfpsKeyguardView.ANIMATION_BETWEEN_AOD_AND_LOCKSCREEN); + } + + @Test + public void testListenersUnregisteredOnDetached() { + mController.onViewAttached(); + captureStatusBarStateListeners(); + captureStatusBarExpansionListeners(); + captureKeyguardStateControllerCallback(); + mController.onViewDetached(); + + verify(mStatusBarStateController).removeCallback(mStatusBarStateListener); + for (ShadeExpansionListener listener : mExpansionListeners) { + verify(mShadeExpansionStateManager).removeExpansionListener(listener); + } + verify(mKeyguardStateController).removeCallback(mKeyguardStateControllerCallback); + } + + @Test + public void testDozeEventsSentToView() { + mController.onViewAttached(); + captureStatusBarStateListeners(); + + final float linear = .55f; + final float eased = .65f; + mStatusBarStateListener.onDozeAmountChanged(linear, eased); + + verify(mView).onDozeAmountChanged(linear, eased, + UdfpsKeyguardView.ANIMATION_BETWEEN_AOD_AND_LOCKSCREEN); + } + + @Test + public void testShouldPauseAuthUnpausedAlpha0() { + mController.onViewAttached(); + captureStatusBarStateListeners(); + + when(mView.getUnpausedAlpha()).thenReturn(0); + sendStatusBarStateChanged(StatusBarState.KEYGUARD); + + assertTrue(mController.shouldPauseAuth()); + } + + @Test + public void testFadeFromDialogSuggestedAlpha() { + // GIVEN view is attached and status bar expansion is 1f + mController.onViewAttached(); + captureStatusBarStateListeners(); + captureStatusBarExpansionListeners(); + updateStatusBarExpansion(1f, true); + reset(mView); + + // WHEN dialog suggested alpha is .6f + when(mView.getDialogSuggestedAlpha()).thenReturn(.6f); + sendStatusBarStateChanged(StatusBarState.KEYGUARD); + + // THEN alpha is updated based on dialog suggested alpha + verify(mView).setUnpausedAlpha((int) (.6f * 255)); + } + + @Test + public void testShouldNotPauseAuthOnKeyguard() { + mController.onViewAttached(); + captureStatusBarStateListeners(); + + sendStatusBarStateChanged(StatusBarState.KEYGUARD); + + assertFalse(mController.shouldPauseAuth()); + } + + @Test + public void testShouldPauseAuthIsLaunchTransitionFadingAway() { + // GIVEN view is attached and we're on the keyguard (see testShouldNotPauseAuthOnKeyguard) + mController.onViewAttached(); + captureStatusBarStateListeners(); + sendStatusBarStateChanged(StatusBarState.KEYGUARD); + + // WHEN isLaunchTransitionFadingAway=true + captureKeyguardStateControllerCallback(); + when(mKeyguardStateController.isLaunchTransitionFadingAway()).thenReturn(true); + mKeyguardStateControllerCallback.onLaunchTransitionFadingAwayChanged(); + + // THEN pause auth + assertTrue(mController.shouldPauseAuth()); + } + + @Test + public void testShouldPauseAuthOnShadeLocked() { + mController.onViewAttached(); + captureStatusBarStateListeners(); + + sendStatusBarStateChanged(StatusBarState.SHADE_LOCKED); + + assertTrue(mController.shouldPauseAuth()); + } + + @Test + public void testShouldPauseAuthOnShade() { + mController.onViewAttached(); + captureStatusBarStateListeners(); + + // WHEN not on keyguard yet (shade = home) + sendStatusBarStateChanged(StatusBarState.SHADE); + + // THEN pause auth + assertTrue(mController.shouldPauseAuth()); + } + + @Test + public void testShouldPauseAuthAnimatingScreenOffFromShade() { + mController.onViewAttached(); + captureStatusBarStateListeners(); + + // WHEN transitioning from home/shade => keyguard + animating screen off + mStatusBarStateListener.onStatePreChange(StatusBarState.SHADE, StatusBarState.KEYGUARD); + when(mKeyguardViewMediator.isAnimatingScreenOff()).thenReturn(true); + + // THEN pause auth + assertTrue(mController.shouldPauseAuth()); + } + + @Test + public void testDoNotPauseAuthAnimatingScreenOffFromLS() { + mController.onViewAttached(); + captureStatusBarStateListeners(); + + // WHEN animating screen off transition from LS => AOD + sendStatusBarStateChanged(StatusBarState.KEYGUARD); + when(mKeyguardViewMediator.isAnimatingScreenOff()).thenReturn(true); + + // THEN don't pause auth + assertFalse(mController.shouldPauseAuth()); + } + + @Test + public void testOverrideShouldPauseAuthOnShadeLocked() { + mController.onViewAttached(); + captureStatusBarStateListeners(); + captureAltAuthInterceptor(); + + sendStatusBarStateChanged(StatusBarState.SHADE_LOCKED); + assertTrue(mController.shouldPauseAuth()); + + mAltAuthInterceptor.showAlternateAuthBouncer(); // force show + assertFalse(mController.shouldPauseAuth()); + assertTrue(mAltAuthInterceptor.isShowingAlternateAuthBouncer()); + + mAltAuthInterceptor.hideAlternateAuthBouncer(); // stop force show + assertTrue(mController.shouldPauseAuth()); + assertFalse(mAltAuthInterceptor.isShowingAlternateAuthBouncer()); + } + + @Test + public void testOnDetachedStateReset() { + // GIVEN view is attached + mController.onViewAttached(); + captureAltAuthInterceptor(); + + // WHEN view is detached + mController.onViewDetached(); + + // THEN remove alternate auth interceptor + verify(mStatusBarKeyguardViewManager).removeAlternateAuthInterceptor(mAltAuthInterceptor); + } + + @Test + public void testHiddenUdfpsBouncerOnTouchOutside_nothingHappens() { + // GIVEN view is attached + mController.onViewAttached(); + captureAltAuthInterceptor(); + + // GIVEN udfps bouncer isn't showing + mAltAuthInterceptor.hideAlternateAuthBouncer(); + + // WHEN touch is observed outside the view + mController.onTouchOutsideView(); + + // THEN bouncer / alt auth methods are never called + verify(mStatusBarKeyguardViewManager, never()).showBouncer(anyBoolean()); + verify(mStatusBarKeyguardViewManager, never()).resetAlternateAuth(anyBoolean()); + } + + @Test + public void testShowingUdfpsBouncerOnTouchOutsideWithinThreshold_nothingHappens() { + // GIVEN view is attached + mController.onViewAttached(); + captureAltAuthInterceptor(); + + // GIVEN udfps bouncer is showing + mAltAuthInterceptor.showAlternateAuthBouncer(); + + // WHEN touch is observed outside the view 200ms later (just within threshold) + mSystemClock.advanceTime(200); + mController.onTouchOutsideView(); + + // THEN bouncer / alt auth methods are never called because not enough time has passed + verify(mStatusBarKeyguardViewManager, never()).showBouncer(anyBoolean()); + verify(mStatusBarKeyguardViewManager, never()).resetAlternateAuth(anyBoolean()); + } + + @Test + public void testShowingUdfpsBouncerOnTouchOutsideAboveThreshold_showInputBouncer() { + // GIVEN view is attached + mController.onViewAttached(); + captureAltAuthInterceptor(); + + // GIVEN udfps bouncer is showing + mAltAuthInterceptor.showAlternateAuthBouncer(); + + // WHEN touch is observed outside the view 205ms later + mSystemClock.advanceTime(205); + mController.onTouchOutsideView(); + + // THEN show the bouncer + verify(mStatusBarKeyguardViewManager).showBouncer(eq(true)); + } + + @Test + public void testFadeInWithStatusBarExpansion() { + // GIVEN view is attached + mController.onViewAttached(); + captureStatusBarExpansionListeners(); + captureKeyguardStateControllerCallback(); + reset(mView); + + // WHEN status bar expansion is 0 + updateStatusBarExpansion(0, true); + + // THEN alpha is 0 + verify(mView).setUnpausedAlpha(0); + } + + @Test + public void testShowUdfpsBouncer() { + // GIVEN view is attached and status bar expansion is 0 + mController.onViewAttached(); + captureStatusBarExpansionListeners(); + captureKeyguardStateControllerCallback(); + captureAltAuthInterceptor(); + updateStatusBarExpansion(0, true); + reset(mView); + when(mView.getContext()).thenReturn(mResourceContext); + when(mResourceContext.getString(anyInt())).thenReturn("test string"); + + // WHEN status bar expansion is 0 but udfps bouncer is requested + mAltAuthInterceptor.showAlternateAuthBouncer(); + + // THEN alpha is 255 + verify(mView).setUnpausedAlpha(255); + } + + @Test + public void testTransitionToFullShadeProgress() { + // GIVEN view is attached and status bar expansion is 1f + mController.onViewAttached(); + captureStatusBarExpansionListeners(); + updateStatusBarExpansion(1f, true); + reset(mView); + when(mView.getDialogSuggestedAlpha()).thenReturn(1f); + + // WHEN we're transitioning to the full shade + float transitionProgress = .6f; + mController.setTransitionToFullShadeProgress(transitionProgress); + + // THEN alpha is between 0 and 255 + verify(mView).setUnpausedAlpha((int) ((1f - transitionProgress) * 255)); + } + + @Test + public void testShowUdfpsBouncer_transitionToFullShadeProgress() { + // GIVEN view is attached and status bar expansion is 1f + mController.onViewAttached(); + captureStatusBarExpansionListeners(); + captureKeyguardStateControllerCallback(); + captureAltAuthInterceptor(); + updateStatusBarExpansion(1f, true); + mAltAuthInterceptor.showAlternateAuthBouncer(); + reset(mView); + + // WHEN we're transitioning to the full shade + mController.setTransitionToFullShadeProgress(1.0f); + + // THEN alpha is 255 (b/c udfps bouncer is requested) + verify(mView).setUnpausedAlpha(255); + } + + @Test + public void testUpdatePanelExpansion_pauseAuth() { + // GIVEN view is attached + on the keyguard + mController.onViewAttached(); + captureStatusBarStateListeners(); + captureStatusBarExpansionListeners(); + sendStatusBarStateChanged(StatusBarState.KEYGUARD); + reset(mView); + + // WHEN panelViewExpansion changes to hide + when(mView.getUnpausedAlpha()).thenReturn(0); + updateStatusBarExpansion(0f, false); + + // THEN pause auth is updated to PAUSE + verify(mView, atLeastOnce()).setPauseAuth(true); + } + + @Test + public void testUpdatePanelExpansion_unpauseAuth() { + // GIVEN view is attached + on the keyguard + panel expansion is 0f + mController.onViewAttached(); + captureStatusBarStateListeners(); + captureStatusBarExpansionListeners(); + sendStatusBarStateChanged(StatusBarState.KEYGUARD); + reset(mView); + + // WHEN panelViewExpansion changes to expanded + when(mView.getUnpausedAlpha()).thenReturn(255); + updateStatusBarExpansion(1f, true); + + // THEN pause auth is updated to NOT pause + verify(mView, atLeastOnce()).setPauseAuth(false); + } + + protected void sendStatusBarStateChanged(int statusBarState) { + mStatusBarStateListener.onStateChanged(statusBarState); + } + + protected void captureStatusBarStateListeners() { + verify(mStatusBarStateController).addCallback(mStateListenerCaptor.capture()); + mStatusBarStateListener = mStateListenerCaptor.getValue(); + } + + protected void captureStatusBarExpansionListeners() { + verify(mShadeExpansionStateManager, times(2)) + .addExpansionListener(mExpansionListenerCaptor.capture()); + // first (index=0) is from super class, UdfpsAnimationViewController. + // second (index=1) is from UdfpsKeyguardViewController + mExpansionListeners = mExpansionListenerCaptor.getAllValues(); + } + + protected void updateStatusBarExpansion(float fraction, boolean expanded) { + ShadeExpansionChangeEvent event = + new ShadeExpansionChangeEvent( + fraction, expanded, /* tracking= */ false, /* dragDownPxAmount= */ 0f); + for (ShadeExpansionListener listener : mExpansionListeners) { + listener.onPanelExpansionChanged(event); + } + } + + protected void captureAltAuthInterceptor() { + verify(mStatusBarKeyguardViewManager).setAlternateAuthInterceptor( + mAltAuthInterceptorCaptor.capture()); + mAltAuthInterceptor = mAltAuthInterceptorCaptor.getValue(); + } + + protected void captureKeyguardStateControllerCallback() { + verify(mKeyguardStateController).addCallback( + mKeyguardStateControllerCallbackCaptor.capture()); + mKeyguardStateControllerCallback = mKeyguardStateControllerCallbackCaptor.getValue(); + } + + public UdfpsKeyguardViewController createUdfpsKeyguardViewController() { + return createUdfpsKeyguardViewController(false); + } + + protected UdfpsKeyguardViewController createUdfpsKeyguardViewController( + boolean useModernBouncer) { + mFeatureFlags.set(Flags.MODERN_BOUNCER, useModernBouncer); + when(mStatusBarKeyguardViewManager.getBouncer()).thenReturn( + useModernBouncer ? null : mBouncer); + return new UdfpsKeyguardViewController( + mView, + mStatusBarStateController, + mShadeExpansionStateManager, + mStatusBarKeyguardViewManager, + mKeyguardUpdateMonitor, + mDumpManager, + mLockscreenShadeTransitionController, + mConfigurationController, + mSystemClock, + mKeyguardStateController, + mUnlockedScreenOffAnimationController, + mDialogManager, + mUdfpsController, + mActivityLaunchAnimator, + mFeatureFlags, + mBouncerInteractor); + } +} diff --git a/packages/SystemUI/tests/src/com/android/systemui/biometrics/UdfpsKeyguardViewControllerTest.java b/packages/SystemUI/tests/src/com/android/systemui/biometrics/UdfpsKeyguardViewControllerTest.java index c0f9c82fb1319..a067e430c2487 100644 --- a/packages/SystemUI/tests/src/com/android/systemui/biometrics/UdfpsKeyguardViewControllerTest.java +++ b/packages/SystemUI/tests/src/com/android/systemui/biometrics/UdfpsKeyguardViewControllerTest.java @@ -16,528 +16,52 @@ package com.android.systemui.biometrics; -import static org.junit.Assert.assertFalse; import static org.junit.Assert.assertTrue; -import static org.mockito.ArgumentMatchers.anyBoolean; -import static org.mockito.ArgumentMatchers.anyInt; -import static org.mockito.Mockito.atLeast; -import static org.mockito.Mockito.atLeastOnce; -import static org.mockito.Mockito.eq; -import static org.mockito.Mockito.never; -import static org.mockito.Mockito.reset; -import static org.mockito.Mockito.times; import static org.mockito.Mockito.verify; import static org.mockito.Mockito.when; -import android.content.Context; import android.testing.AndroidTestingRunner; import android.testing.TestableLooper.RunWithLooper; import androidx.test.filters.SmallTest; -import com.android.keyguard.KeyguardUpdateMonitor; -import com.android.systemui.SysuiTestCase; -import com.android.systemui.animation.ActivityLaunchAnimator; -import com.android.systemui.dump.DumpManager; -import com.android.systemui.keyguard.KeyguardViewMediator; -import com.android.systemui.plugins.statusbar.StatusBarStateController; -import com.android.systemui.shade.ShadeExpansionChangeEvent; -import com.android.systemui.shade.ShadeExpansionListener; -import com.android.systemui.shade.ShadeExpansionStateManager; -import com.android.systemui.statusbar.LockscreenShadeTransitionController; import com.android.systemui.statusbar.StatusBarState; -import com.android.systemui.statusbar.phone.StatusBarKeyguardViewManager; -import com.android.systemui.statusbar.phone.SystemUIDialogManager; -import com.android.systemui.statusbar.phone.UnlockedScreenOffAnimationController; -import com.android.systemui.statusbar.policy.ConfigurationController; -import com.android.systemui.statusbar.policy.KeyguardStateController; -import com.android.systemui.util.concurrency.DelayableExecutor; -import com.android.systemui.util.time.FakeSystemClock; +import com.android.systemui.statusbar.phone.KeyguardBouncer; -import org.junit.Before; import org.junit.Test; import org.junit.runner.RunWith; import org.mockito.ArgumentCaptor; import org.mockito.Captor; -import org.mockito.Mock; -import org.mockito.MockitoAnnotations; - -import java.util.List; @SmallTest @RunWith(AndroidTestingRunner.class) @RunWithLooper -public class UdfpsKeyguardViewControllerTest extends SysuiTestCase { - // Dependencies - @Mock - private UdfpsKeyguardView mView; - @Mock - private Context mResourceContext; - @Mock - private StatusBarStateController mStatusBarStateController; - @Mock - private ShadeExpansionStateManager mShadeExpansionStateManager; - @Mock - private StatusBarKeyguardViewManager mStatusBarKeyguardViewManager; - @Mock - private LockscreenShadeTransitionController mLockscreenShadeTransitionController; - @Mock - private DumpManager mDumpManager; - @Mock - private DelayableExecutor mExecutor; - @Mock - private KeyguardUpdateMonitor mKeyguardUpdateMonitor; - @Mock - private KeyguardStateController mKeyguardStateController; - @Mock - private KeyguardViewMediator mKeyguardViewMediator; - @Mock - private ConfigurationController mConfigurationController; - @Mock - private UnlockedScreenOffAnimationController mUnlockedScreenOffAnimationController; - @Mock - private SystemUIDialogManager mDialogManager; - @Mock - private UdfpsController mUdfpsController; - @Mock - private ActivityLaunchAnimator mActivityLaunchAnimator; - private FakeSystemClock mSystemClock = new FakeSystemClock(); +public class UdfpsKeyguardViewControllerTest extends UdfpsKeyguardViewControllerBaseTest { + private @Captor ArgumentCaptor + mBouncerExpansionCallbackCaptor; + private KeyguardBouncer.BouncerExpansionCallback mBouncerExpansionCallback; - private UdfpsKeyguardViewController mController; - - // Capture listeners so that they can be used to send events - @Captor private ArgumentCaptor mStateListenerCaptor; - private StatusBarStateController.StateListener mStatusBarStateListener; - - @Captor private ArgumentCaptor mExpansionListenerCaptor; - private List mExpansionListeners; - - @Captor private ArgumentCaptor - mAltAuthInterceptorCaptor; - private StatusBarKeyguardViewManager.AlternateAuthInterceptor mAltAuthInterceptor; - - @Captor private ArgumentCaptor - mKeyguardStateControllerCallbackCaptor; - private KeyguardStateController.Callback mKeyguardStateControllerCallback; - - @Before - public void setUp() { - MockitoAnnotations.initMocks(this); - when(mView.getContext()).thenReturn(mResourceContext); - when(mResourceContext.getString(anyInt())).thenReturn("test string"); - when(mKeyguardViewMediator.isAnimatingScreenOff()).thenReturn(false); - when(mView.getUnpausedAlpha()).thenReturn(255); - mController = new UdfpsKeyguardViewController( - mView, - mStatusBarStateController, - mShadeExpansionStateManager, - mStatusBarKeyguardViewManager, - mKeyguardUpdateMonitor, - mDumpManager, - mLockscreenShadeTransitionController, - mConfigurationController, - mSystemClock, - mKeyguardStateController, - mUnlockedScreenOffAnimationController, - mDialogManager, - mUdfpsController, - mActivityLaunchAnimator); + @Override + public UdfpsKeyguardViewController createUdfpsKeyguardViewController() { + return createUdfpsKeyguardViewController(/* useModernBouncer */ false); } @Test - public void testRegistersExpansionChangedListenerOnAttached() { - mController.onViewAttached(); - captureStatusBarExpansionListeners(); - } - - @Test - public void testRegistersStatusBarStateListenersOnAttached() { - mController.onViewAttached(); - captureStatusBarStateListeners(); - } - - @Test - public void testViewControllerQueriesSBStateOnAttached() { - mController.onViewAttached(); - verify(mStatusBarStateController).getState(); - verify(mStatusBarStateController).getDozeAmount(); - - final float dozeAmount = .88f; - when(mStatusBarStateController.getState()).thenReturn(StatusBarState.SHADE_LOCKED); - when(mStatusBarStateController.getDozeAmount()).thenReturn(dozeAmount); - captureStatusBarStateListeners(); - - mController.onViewAttached(); - verify(mView, atLeast(1)).setPauseAuth(true); - verify(mView).onDozeAmountChanged(dozeAmount, dozeAmount, - UdfpsKeyguardView.ANIMATION_BETWEEN_AOD_AND_LOCKSCREEN); - } - - @Test - public void testListenersUnregisteredOnDetached() { - mController.onViewAttached(); - captureStatusBarStateListeners(); - captureStatusBarExpansionListeners(); - captureKeyguardStateControllerCallback(); - mController.onViewDetached(); - - verify(mStatusBarStateController).removeCallback(mStatusBarStateListener); - for (ShadeExpansionListener listener : mExpansionListeners) { - verify(mShadeExpansionStateManager).removeExpansionListener(listener); - } - verify(mKeyguardStateController).removeCallback(mKeyguardStateControllerCallback); - } - - @Test - public void testDozeEventsSentToView() { - mController.onViewAttached(); - captureStatusBarStateListeners(); - - final float linear = .55f; - final float eased = .65f; - mStatusBarStateListener.onDozeAmountChanged(linear, eased); - - verify(mView).onDozeAmountChanged(linear, eased, - UdfpsKeyguardView.ANIMATION_BETWEEN_AOD_AND_LOCKSCREEN); - } - - @Test - public void testShouldPauseAuthBouncerShowing() { + public void testShouldPauseAuth_bouncerShowing() { mController.onViewAttached(); captureStatusBarStateListeners(); sendStatusBarStateChanged(StatusBarState.KEYGUARD); - captureAltAuthInterceptor(); + captureBouncerExpansionCallback(); when(mStatusBarKeyguardViewManager.isBouncerShowing()).thenReturn(true); when(mStatusBarKeyguardViewManager.bouncerIsOrWillBeShowing()).thenReturn(true); - mAltAuthInterceptor.onBouncerVisibilityChanged(); + mBouncerExpansionCallback.onVisibilityChanged(true); assertTrue(mController.shouldPauseAuth()); } - @Test - public void testShouldPauseAuthUnpausedAlpha0() { - mController.onViewAttached(); - captureStatusBarStateListeners(); - - when(mView.getUnpausedAlpha()).thenReturn(0); - sendStatusBarStateChanged(StatusBarState.KEYGUARD); - - assertTrue(mController.shouldPauseAuth()); - } - - @Test - public void testFadeFromDialogSuggestedAlpha() { - // GIVEN view is attached and status bar expansion is 1f - mController.onViewAttached(); - captureStatusBarStateListeners(); - captureStatusBarExpansionListeners(); - updateStatusBarExpansion(1f, true); - reset(mView); - - // WHEN dialog suggested alpha is .6f - when(mView.getDialogSuggestedAlpha()).thenReturn(.6f); - sendStatusBarStateChanged(StatusBarState.KEYGUARD); - - // THEN alpha is updated based on dialog suggested alpha - verify(mView).setUnpausedAlpha((int) (.6f * 255)); - } - - @Test - public void testShouldNotPauseAuthOnKeyguard() { - mController.onViewAttached(); - captureStatusBarStateListeners(); - - sendStatusBarStateChanged(StatusBarState.KEYGUARD); - - assertFalse(mController.shouldPauseAuth()); - } - - @Test - public void testShouldPauseAuthIsLaunchTransitionFadingAway() { - // GIVEN view is attached and we're on the keyguard (see testShouldNotPauseAuthOnKeyguard) - mController.onViewAttached(); - captureStatusBarStateListeners(); - sendStatusBarStateChanged(StatusBarState.KEYGUARD); - - // WHEN isLaunchTransitionFadingAway=true - captureKeyguardStateControllerCallback(); - when(mKeyguardStateController.isLaunchTransitionFadingAway()).thenReturn(true); - mKeyguardStateControllerCallback.onLaunchTransitionFadingAwayChanged(); - - // THEN pause auth - assertTrue(mController.shouldPauseAuth()); - } - - @Test - public void testShouldPauseAuthOnShadeLocked() { - mController.onViewAttached(); - captureStatusBarStateListeners(); - - sendStatusBarStateChanged(StatusBarState.SHADE_LOCKED); - - assertTrue(mController.shouldPauseAuth()); - } - - @Test - public void testShouldPauseAuthOnShade() { - mController.onViewAttached(); - captureStatusBarStateListeners(); - - // WHEN not on keyguard yet (shade = home) - sendStatusBarStateChanged(StatusBarState.SHADE); - - // THEN pause auth - assertTrue(mController.shouldPauseAuth()); - } - - @Test - public void testShouldPauseAuthAnimatingScreenOffFromShade() { - mController.onViewAttached(); - captureStatusBarStateListeners(); - - // WHEN transitioning from home/shade => keyguard + animating screen off - mStatusBarStateListener.onStatePreChange(StatusBarState.SHADE, StatusBarState.KEYGUARD); - when(mKeyguardViewMediator.isAnimatingScreenOff()).thenReturn(true); - - // THEN pause auth - assertTrue(mController.shouldPauseAuth()); - } - - @Test - public void testDoNotPauseAuthAnimatingScreenOffFromLS() { - mController.onViewAttached(); - captureStatusBarStateListeners(); - - // WHEN animating screen off transition from LS => AOD - sendStatusBarStateChanged(StatusBarState.KEYGUARD); - when(mKeyguardViewMediator.isAnimatingScreenOff()).thenReturn(true); - - // THEN don't pause auth - assertFalse(mController.shouldPauseAuth()); - } - - @Test - public void testOverrideShouldPauseAuthOnShadeLocked() { - mController.onViewAttached(); - captureStatusBarStateListeners(); - captureAltAuthInterceptor(); - - sendStatusBarStateChanged(StatusBarState.SHADE_LOCKED); - assertTrue(mController.shouldPauseAuth()); - - mAltAuthInterceptor.showAlternateAuthBouncer(); // force show - assertFalse(mController.shouldPauseAuth()); - assertTrue(mAltAuthInterceptor.isShowingAlternateAuthBouncer()); - - mAltAuthInterceptor.hideAlternateAuthBouncer(); // stop force show - assertTrue(mController.shouldPauseAuth()); - assertFalse(mAltAuthInterceptor.isShowingAlternateAuthBouncer()); - } - - @Test - public void testOnDetachedStateReset() { - // GIVEN view is attached - mController.onViewAttached(); - captureAltAuthInterceptor(); - - // WHEN view is detached - mController.onViewDetached(); - - // THEN remove alternate auth interceptor - verify(mStatusBarKeyguardViewManager).removeAlternateAuthInterceptor(mAltAuthInterceptor); - } - - @Test - public void testHiddenUdfpsBouncerOnTouchOutside_nothingHappens() { - // GIVEN view is attached - mController.onViewAttached(); - captureAltAuthInterceptor(); - - // GIVEN udfps bouncer isn't showing - mAltAuthInterceptor.hideAlternateAuthBouncer(); - - // WHEN touch is observed outside the view - mController.onTouchOutsideView(); - - // THEN bouncer / alt auth methods are never called - verify(mStatusBarKeyguardViewManager, never()).showBouncer(anyBoolean()); - verify(mStatusBarKeyguardViewManager, never()).resetAlternateAuth(anyBoolean()); - } - - @Test - public void testShowingUdfpsBouncerOnTouchOutsideWithinThreshold_nothingHappens() { - // GIVEN view is attached - mController.onViewAttached(); - captureAltAuthInterceptor(); - - // GIVEN udfps bouncer is showing - mAltAuthInterceptor.showAlternateAuthBouncer(); - - // WHEN touch is observed outside the view 200ms later (just within threshold) - mSystemClock.advanceTime(200); - mController.onTouchOutsideView(); - - // THEN bouncer / alt auth methods are never called because not enough time has passed - verify(mStatusBarKeyguardViewManager, never()).showBouncer(anyBoolean()); - verify(mStatusBarKeyguardViewManager, never()).resetAlternateAuth(anyBoolean()); - } - - @Test - public void testShowingUdfpsBouncerOnTouchOutsideAboveThreshold_showInputBouncer() { - // GIVEN view is attached - mController.onViewAttached(); - captureAltAuthInterceptor(); - - // GIVEN udfps bouncer is showing - mAltAuthInterceptor.showAlternateAuthBouncer(); - - // WHEN touch is observed outside the view 205ms later - mSystemClock.advanceTime(205); - mController.onTouchOutsideView(); - - // THEN show the bouncer - verify(mStatusBarKeyguardViewManager).showBouncer(eq(true)); - } - - @Test - public void testFadeInWithStatusBarExpansion() { - // GIVEN view is attached - mController.onViewAttached(); - captureStatusBarExpansionListeners(); - captureKeyguardStateControllerCallback(); - reset(mView); - - // WHEN status bar expansion is 0 - updateStatusBarExpansion(0, true); - - // THEN alpha is 0 - verify(mView).setUnpausedAlpha(0); - } - - @Test - public void testShowUdfpsBouncer() { - // GIVEN view is attached and status bar expansion is 0 - mController.onViewAttached(); - captureStatusBarExpansionListeners(); - captureKeyguardStateControllerCallback(); - captureAltAuthInterceptor(); - updateStatusBarExpansion(0, true); - reset(mView); - when(mView.getContext()).thenReturn(mResourceContext); - when(mResourceContext.getString(anyInt())).thenReturn("test string"); - - // WHEN status bar expansion is 0 but udfps bouncer is requested - mAltAuthInterceptor.showAlternateAuthBouncer(); - - // THEN alpha is 255 - verify(mView).setUnpausedAlpha(255); - } - - @Test - public void testTransitionToFullShadeProgress() { - // GIVEN view is attached and status bar expansion is 1f - mController.onViewAttached(); - captureStatusBarExpansionListeners(); - updateStatusBarExpansion(1f, true); - reset(mView); - when(mView.getDialogSuggestedAlpha()).thenReturn(1f); - - // WHEN we're transitioning to the full shade - float transitionProgress = .6f; - mController.setTransitionToFullShadeProgress(transitionProgress); - - // THEN alpha is between 0 and 255 - verify(mView).setUnpausedAlpha((int) ((1f - transitionProgress) * 255)); - } - - @Test - public void testShowUdfpsBouncer_transitionToFullShadeProgress() { - // GIVEN view is attached and status bar expansion is 1f - mController.onViewAttached(); - captureStatusBarExpansionListeners(); - captureKeyguardStateControllerCallback(); - captureAltAuthInterceptor(); - updateStatusBarExpansion(1f, true); - mAltAuthInterceptor.showAlternateAuthBouncer(); - reset(mView); - - // WHEN we're transitioning to the full shade - mController.setTransitionToFullShadeProgress(1.0f); - - // THEN alpha is 255 (b/c udfps bouncer is requested) - verify(mView).setUnpausedAlpha(255); - } - - @Test - public void testUpdatePanelExpansion_pauseAuth() { - // GIVEN view is attached + on the keyguard - mController.onViewAttached(); - captureStatusBarStateListeners(); - captureStatusBarExpansionListeners(); - sendStatusBarStateChanged(StatusBarState.KEYGUARD); - reset(mView); - - // WHEN panelViewExpansion changes to hide - when(mView.getUnpausedAlpha()).thenReturn(0); - updateStatusBarExpansion(0f, false); - - // THEN pause auth is updated to PAUSE - verify(mView, atLeastOnce()).setPauseAuth(true); - } - - @Test - public void testUpdatePanelExpansion_unpauseAuth() { - // GIVEN view is attached + on the keyguard + panel expansion is 0f - mController.onViewAttached(); - captureStatusBarStateListeners(); - captureStatusBarExpansionListeners(); - sendStatusBarStateChanged(StatusBarState.KEYGUARD); - reset(mView); - - // WHEN panelViewExpansion changes to expanded - when(mView.getUnpausedAlpha()).thenReturn(255); - updateStatusBarExpansion(1f, true); - - // THEN pause auth is updated to NOT pause - verify(mView, atLeastOnce()).setPauseAuth(false); - } - - private void sendStatusBarStateChanged(int statusBarState) { - mStatusBarStateListener.onStateChanged(statusBarState); - } - - private void captureStatusBarStateListeners() { - verify(mStatusBarStateController).addCallback(mStateListenerCaptor.capture()); - mStatusBarStateListener = mStateListenerCaptor.getValue(); - } - - private void captureStatusBarExpansionListeners() { - verify(mShadeExpansionStateManager, times(2)) - .addExpansionListener(mExpansionListenerCaptor.capture()); - // first (index=0) is from super class, UdfpsAnimationViewController. - // second (index=1) is from UdfpsKeyguardViewController - mExpansionListeners = mExpansionListenerCaptor.getAllValues(); - } - - private void updateStatusBarExpansion(float fraction, boolean expanded) { - ShadeExpansionChangeEvent event = - new ShadeExpansionChangeEvent( - fraction, expanded, /* tracking= */ false, /* dragDownPxAmount= */ 0f); - for (ShadeExpansionListener listener : mExpansionListeners) { - listener.onPanelExpansionChanged(event); - } - } - - private void captureAltAuthInterceptor() { - verify(mStatusBarKeyguardViewManager).setAlternateAuthInterceptor( - mAltAuthInterceptorCaptor.capture()); - mAltAuthInterceptor = mAltAuthInterceptorCaptor.getValue(); - } - - private void captureKeyguardStateControllerCallback() { - verify(mKeyguardStateController).addCallback( - mKeyguardStateControllerCallbackCaptor.capture()); - mKeyguardStateControllerCallback = mKeyguardStateControllerCallbackCaptor.getValue(); + private void captureBouncerExpansionCallback() { + verify(mBouncer).addBouncerExpansionCallback(mBouncerExpansionCallbackCaptor.capture()); + mBouncerExpansionCallback = mBouncerExpansionCallbackCaptor.getValue(); } } diff --git a/packages/SystemUI/tests/src/com/android/systemui/biometrics/UdfpsKeyguardViewControllerWithCoroutinesTest.kt b/packages/SystemUI/tests/src/com/android/systemui/biometrics/UdfpsKeyguardViewControllerWithCoroutinesTest.kt new file mode 100644 index 0000000000000..7b19768118686 --- /dev/null +++ b/packages/SystemUI/tests/src/com/android/systemui/biometrics/UdfpsKeyguardViewControllerWithCoroutinesTest.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.biometrics + +import android.os.Handler +import android.testing.AndroidTestingRunner +import android.testing.TestableLooper +import androidx.test.filters.SmallTest +import com.android.keyguard.KeyguardSecurityModel +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.domain.interactor.BouncerCallbackInteractor +import com.android.systemui.keyguard.domain.interactor.BouncerInteractor +import com.android.systemui.statusbar.StatusBarState +import com.android.systemui.statusbar.phone.KeyguardBouncer +import com.android.systemui.statusbar.phone.KeyguardBypassController +import kotlinx.coroutines.Dispatchers +import kotlinx.coroutines.runBlocking +import kotlinx.coroutines.yield +import org.junit.Assert.assertTrue +import org.junit.Before +import org.junit.Test +import org.junit.runner.RunWith +import org.mockito.Mockito.mock +import org.mockito.MockitoAnnotations + +@RunWith(AndroidTestingRunner::class) +@SmallTest +@TestableLooper.RunWithLooper +class UdfpsKeyguardViewControllerWithCoroutinesTest : UdfpsKeyguardViewControllerBaseTest() { + lateinit var keyguardBouncerRepository: KeyguardBouncerRepository + + @Before + override fun setUp() { + allowTestableLooperAsMainThread() // repeatWhenAttached requires the main thread + MockitoAnnotations.initMocks(this) + keyguardBouncerRepository = + KeyguardBouncerRepository( + mock(com.android.keyguard.ViewMediatorCallback::class.java), + mKeyguardUpdateMonitor + ) + super.setUp() + } + + override fun createUdfpsKeyguardViewController(): UdfpsKeyguardViewController? { + mBouncerInteractor = + BouncerInteractor( + keyguardBouncerRepository, + mock(BouncerView::class.java), + mock(Handler::class.java), + mKeyguardStateController, + mock(KeyguardSecurityModel::class.java), + mock(BouncerCallbackInteractor::class.java), + mock(FalsingCollector::class.java), + mock(DismissCallbackRegistry::class.java), + mock(KeyguardBypassController::class.java), + mKeyguardUpdateMonitor + ) + return createUdfpsKeyguardViewController(/* useModernBouncer */ true) + } + + /** After migration, replaces LockIconViewControllerTest version */ + @Test + fun testShouldPauseAuthBouncerShowing() = + runBlocking(IMMEDIATE) { + // GIVEN view attached and we're on the keyguard + mController.onViewAttached() + captureStatusBarStateListeners() + sendStatusBarStateChanged(StatusBarState.KEYGUARD) + + // WHEN the bouncer expansion is VISIBLE + val job = mController.listenForBouncerExpansion(this) + keyguardBouncerRepository.setVisible(true) + keyguardBouncerRepository.setPanelExpansion(KeyguardBouncer.EXPANSION_VISIBLE) + yield() + + // THEN UDFPS shouldPauseAuth == true + assertTrue(mController.shouldPauseAuth()) + + job.cancel() + } + + companion object { + private val IMMEDIATE = Dispatchers.Main.immediate + } +} 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 index e6c8dd87d9824..c4e7dd241eb9c 100644 --- 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 @@ -97,7 +97,7 @@ class BouncerInteractorTest : SysuiTestCase() { verify(repository).setHide(false) verify(repository).setStartingToHide(false) verify(repository).setScrimmed(true) - verify(repository).setExpansion(EXPANSION_VISIBLE) + verify(repository).setPanelExpansion(EXPANSION_VISIBLE) verify(repository).setShowingSoon(true) verify(keyguardStateController).notifyBouncerShowing(true) verify(bouncerCallbackInteractor).dispatchStartingToShow() @@ -108,7 +108,7 @@ class BouncerInteractorTest : SysuiTestCase() { @Test fun testShow_isNotScrimmed() { - verify(repository, never()).setExpansion(EXPANSION_VISIBLE) + verify(repository, never()).setPanelExpansion(EXPANSION_VISIBLE) } @Test @@ -132,26 +132,26 @@ class BouncerInteractorTest : SysuiTestCase() { @Test fun testExpansion() { - `when`(repository.expansionAmount.value).thenReturn(0.5f) - bouncerInteractor.setExpansion(0.6f) - verify(repository).setExpansion(0.6f) + `when`(repository.panelExpansionAmount.value).thenReturn(0.5f) + bouncerInteractor.setPanelExpansion(0.6f) + verify(repository).setPanelExpansion(0.6f) verify(bouncerCallbackInteractor).dispatchExpansionChanged(0.6f) } @Test fun testExpansion_fullyShown() { - `when`(repository.expansionAmount.value).thenReturn(0.5f) + `when`(repository.panelExpansionAmount.value).thenReturn(0.5f) `when`(repository.startingDisappearAnimation.value).thenReturn(null) - bouncerInteractor.setExpansion(EXPANSION_VISIBLE) + bouncerInteractor.setPanelExpansion(EXPANSION_VISIBLE) verify(falsingCollector).onBouncerShown() verify(bouncerCallbackInteractor).dispatchFullyShown() } @Test fun testExpansion_fullyHidden() { - `when`(repository.expansionAmount.value).thenReturn(0.5f) + `when`(repository.panelExpansionAmount.value).thenReturn(0.5f) `when`(repository.startingDisappearAnimation.value).thenReturn(null) - bouncerInteractor.setExpansion(EXPANSION_HIDDEN) + bouncerInteractor.setPanelExpansion(EXPANSION_HIDDEN) verify(repository).setVisible(false) verify(repository).setShow(null) verify(falsingCollector).onBouncerHidden() @@ -161,8 +161,8 @@ class BouncerInteractorTest : SysuiTestCase() { @Test fun testExpansion_startingToHide() { - `when`(repository.expansionAmount.value).thenReturn(EXPANSION_VISIBLE) - bouncerInteractor.setExpansion(0.1f) + `when`(repository.panelExpansionAmount.value).thenReturn(EXPANSION_VISIBLE) + bouncerInteractor.setPanelExpansion(0.1f) verify(repository).setStartingToHide(true) verify(bouncerCallbackInteractor).dispatchStartingToHide() } @@ -234,7 +234,7 @@ class BouncerInteractorTest : SysuiTestCase() { @Test fun testIsFullShowing() { `when`(repository.isVisible.value).thenReturn(true) - `when`(repository.expansionAmount.value).thenReturn(EXPANSION_VISIBLE) + `when`(repository.panelExpansionAmount.value).thenReturn(EXPANSION_VISIBLE) `when`(repository.startingDisappearAnimation.value).thenReturn(null) assertThat(bouncerInteractor.isFullyShowing()).isTrue() `when`(repository.isVisible.value).thenReturn(false) @@ -255,7 +255,7 @@ class BouncerInteractorTest : SysuiTestCase() { assertThat(bouncerInteractor.isInTransit()).isTrue() `when`(repository.showingSoon.value).thenReturn(false) assertThat(bouncerInteractor.isInTransit()).isFalse() - `when`(repository.expansionAmount.value).thenReturn(0.5f) + `when`(repository.panelExpansionAmount.value).thenReturn(0.5f) assertThat(bouncerInteractor.isInTransit()).isTrue() } 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 26a0770a7bba1..a4a7995ae3c8e 100644 --- a/packages/SystemUI/tests/src/com/android/systemui/shade/NotificationShadeWindowViewTest.java +++ b/packages/SystemUI/tests/src/com/android/systemui/shade/NotificationShadeWindowViewTest.java @@ -152,7 +152,7 @@ public class NotificationShadeWindowViewTest extends SysuiTestCase { // WHEN showing alt auth, not dozing, drag down helper doesn't want to intercept when(mStatusBarStateController.isDozing()).thenReturn(false); - when(mStatusBarKeyguardViewManager.isShowingAlternateAuthOrAnimating()).thenReturn(true); + when(mStatusBarKeyguardViewManager.isShowingAlternateAuth()).thenReturn(true); when(mDragDownHelper.onInterceptTouchEvent(any())).thenReturn(false); // THEN we should intercept touch @@ -165,7 +165,7 @@ public class NotificationShadeWindowViewTest extends SysuiTestCase { // WHEN not showing alt auth, not dozing, drag down helper doesn't want to intercept when(mStatusBarStateController.isDozing()).thenReturn(false); - when(mStatusBarKeyguardViewManager.isShowingAlternateAuthOrAnimating()).thenReturn(false); + when(mStatusBarKeyguardViewManager.isShowingAlternateAuth()).thenReturn(false); when(mDragDownHelper.onInterceptTouchEvent(any())).thenReturn(false); // THEN we shouldn't intercept touch @@ -178,7 +178,7 @@ public class NotificationShadeWindowViewTest extends SysuiTestCase { // WHEN showing alt auth, not dozing, drag down helper doesn't want to intercept when(mStatusBarStateController.isDozing()).thenReturn(false); - when(mStatusBarKeyguardViewManager.isShowingAlternateAuthOrAnimating()).thenReturn(true); + when(mStatusBarKeyguardViewManager.isShowingAlternateAuth()).thenReturn(true); when(mDragDownHelper.onInterceptTouchEvent(any())).thenReturn(false); // THEN we should handle the touch From 14a53f10b0fc10e3eba587404fa0a13c869f1d43 Mon Sep 17 00:00:00 2001 From: Beverly Date: Wed, 2 Nov 2022 13:20:01 +0000 Subject: [PATCH 3/3] DO NOT MERGE [1.5/N] side fps bouncer - Update UdfpsKeyguardViewControllerBaseTest It shouldn't run tests on its own. Only its extending classes should run. Test: atest AAAPlusPlusVerifySysuiRequiredTestPropertiesTest Bug: 237362467 Change-Id: Ib03445206098949c6e0b09a948e4caf92c4b9be3 --- .../UdfpsKeyguardViewControllerBaseTest.java | 355 ----------------- .../UdfpsKeyguardViewControllerTest.java | 356 ++++++++++++++++++ 2 files changed, 356 insertions(+), 355 deletions(-) diff --git a/packages/SystemUI/tests/src/com/android/systemui/biometrics/UdfpsKeyguardViewControllerBaseTest.java b/packages/SystemUI/tests/src/com/android/systemui/biometrics/UdfpsKeyguardViewControllerBaseTest.java index c313c8dc1a7c9..e5c7a42c06a6c 100644 --- a/packages/SystemUI/tests/src/com/android/systemui/biometrics/UdfpsKeyguardViewControllerBaseTest.java +++ b/packages/SystemUI/tests/src/com/android/systemui/biometrics/UdfpsKeyguardViewControllerBaseTest.java @@ -16,15 +16,7 @@ package com.android.systemui.biometrics; -import static org.junit.Assert.assertFalse; -import static org.junit.Assert.assertTrue; -import static org.mockito.ArgumentMatchers.anyBoolean; import static org.mockito.ArgumentMatchers.anyInt; -import static org.mockito.Mockito.atLeast; -import static org.mockito.Mockito.atLeastOnce; -import static org.mockito.Mockito.eq; -import static org.mockito.Mockito.never; -import static org.mockito.Mockito.reset; import static org.mockito.Mockito.times; import static org.mockito.Mockito.verify; import static org.mockito.Mockito.when; @@ -44,7 +36,6 @@ import com.android.systemui.shade.ShadeExpansionChangeEvent; import com.android.systemui.shade.ShadeExpansionListener; import com.android.systemui.shade.ShadeExpansionStateManager; import com.android.systemui.statusbar.LockscreenShadeTransitionController; -import com.android.systemui.statusbar.StatusBarState; import com.android.systemui.statusbar.phone.KeyguardBouncer; import com.android.systemui.statusbar.phone.StatusBarKeyguardViewManager; import com.android.systemui.statusbar.phone.SystemUIDialogManager; @@ -55,7 +46,6 @@ import com.android.systemui.util.concurrency.DelayableExecutor; import com.android.systemui.util.time.FakeSystemClock; import org.junit.Before; -import org.junit.Test; import org.mockito.ArgumentCaptor; import org.mockito.Captor; import org.mockito.Mock; @@ -114,351 +104,6 @@ public class UdfpsKeyguardViewControllerBaseTest extends SysuiTestCase { mController = createUdfpsKeyguardViewController(); } - @Test - public void testRegistersExpansionChangedListenerOnAttached() { - mController.onViewAttached(); - captureStatusBarExpansionListeners(); - } - - @Test - public void testRegistersStatusBarStateListenersOnAttached() { - mController.onViewAttached(); - captureStatusBarStateListeners(); - } - - @Test - public void testViewControllerQueriesSBStateOnAttached() { - mController.onViewAttached(); - verify(mStatusBarStateController).getState(); - verify(mStatusBarStateController).getDozeAmount(); - - final float dozeAmount = .88f; - when(mStatusBarStateController.getState()).thenReturn(StatusBarState.SHADE_LOCKED); - when(mStatusBarStateController.getDozeAmount()).thenReturn(dozeAmount); - captureStatusBarStateListeners(); - - mController.onViewAttached(); - verify(mView, atLeast(1)).setPauseAuth(true); - verify(mView).onDozeAmountChanged(dozeAmount, dozeAmount, - UdfpsKeyguardView.ANIMATION_BETWEEN_AOD_AND_LOCKSCREEN); - } - - @Test - public void testListenersUnregisteredOnDetached() { - mController.onViewAttached(); - captureStatusBarStateListeners(); - captureStatusBarExpansionListeners(); - captureKeyguardStateControllerCallback(); - mController.onViewDetached(); - - verify(mStatusBarStateController).removeCallback(mStatusBarStateListener); - for (ShadeExpansionListener listener : mExpansionListeners) { - verify(mShadeExpansionStateManager).removeExpansionListener(listener); - } - verify(mKeyguardStateController).removeCallback(mKeyguardStateControllerCallback); - } - - @Test - public void testDozeEventsSentToView() { - mController.onViewAttached(); - captureStatusBarStateListeners(); - - final float linear = .55f; - final float eased = .65f; - mStatusBarStateListener.onDozeAmountChanged(linear, eased); - - verify(mView).onDozeAmountChanged(linear, eased, - UdfpsKeyguardView.ANIMATION_BETWEEN_AOD_AND_LOCKSCREEN); - } - - @Test - public void testShouldPauseAuthUnpausedAlpha0() { - mController.onViewAttached(); - captureStatusBarStateListeners(); - - when(mView.getUnpausedAlpha()).thenReturn(0); - sendStatusBarStateChanged(StatusBarState.KEYGUARD); - - assertTrue(mController.shouldPauseAuth()); - } - - @Test - public void testFadeFromDialogSuggestedAlpha() { - // GIVEN view is attached and status bar expansion is 1f - mController.onViewAttached(); - captureStatusBarStateListeners(); - captureStatusBarExpansionListeners(); - updateStatusBarExpansion(1f, true); - reset(mView); - - // WHEN dialog suggested alpha is .6f - when(mView.getDialogSuggestedAlpha()).thenReturn(.6f); - sendStatusBarStateChanged(StatusBarState.KEYGUARD); - - // THEN alpha is updated based on dialog suggested alpha - verify(mView).setUnpausedAlpha((int) (.6f * 255)); - } - - @Test - public void testShouldNotPauseAuthOnKeyguard() { - mController.onViewAttached(); - captureStatusBarStateListeners(); - - sendStatusBarStateChanged(StatusBarState.KEYGUARD); - - assertFalse(mController.shouldPauseAuth()); - } - - @Test - public void testShouldPauseAuthIsLaunchTransitionFadingAway() { - // GIVEN view is attached and we're on the keyguard (see testShouldNotPauseAuthOnKeyguard) - mController.onViewAttached(); - captureStatusBarStateListeners(); - sendStatusBarStateChanged(StatusBarState.KEYGUARD); - - // WHEN isLaunchTransitionFadingAway=true - captureKeyguardStateControllerCallback(); - when(mKeyguardStateController.isLaunchTransitionFadingAway()).thenReturn(true); - mKeyguardStateControllerCallback.onLaunchTransitionFadingAwayChanged(); - - // THEN pause auth - assertTrue(mController.shouldPauseAuth()); - } - - @Test - public void testShouldPauseAuthOnShadeLocked() { - mController.onViewAttached(); - captureStatusBarStateListeners(); - - sendStatusBarStateChanged(StatusBarState.SHADE_LOCKED); - - assertTrue(mController.shouldPauseAuth()); - } - - @Test - public void testShouldPauseAuthOnShade() { - mController.onViewAttached(); - captureStatusBarStateListeners(); - - // WHEN not on keyguard yet (shade = home) - sendStatusBarStateChanged(StatusBarState.SHADE); - - // THEN pause auth - assertTrue(mController.shouldPauseAuth()); - } - - @Test - public void testShouldPauseAuthAnimatingScreenOffFromShade() { - mController.onViewAttached(); - captureStatusBarStateListeners(); - - // WHEN transitioning from home/shade => keyguard + animating screen off - mStatusBarStateListener.onStatePreChange(StatusBarState.SHADE, StatusBarState.KEYGUARD); - when(mKeyguardViewMediator.isAnimatingScreenOff()).thenReturn(true); - - // THEN pause auth - assertTrue(mController.shouldPauseAuth()); - } - - @Test - public void testDoNotPauseAuthAnimatingScreenOffFromLS() { - mController.onViewAttached(); - captureStatusBarStateListeners(); - - // WHEN animating screen off transition from LS => AOD - sendStatusBarStateChanged(StatusBarState.KEYGUARD); - when(mKeyguardViewMediator.isAnimatingScreenOff()).thenReturn(true); - - // THEN don't pause auth - assertFalse(mController.shouldPauseAuth()); - } - - @Test - public void testOverrideShouldPauseAuthOnShadeLocked() { - mController.onViewAttached(); - captureStatusBarStateListeners(); - captureAltAuthInterceptor(); - - sendStatusBarStateChanged(StatusBarState.SHADE_LOCKED); - assertTrue(mController.shouldPauseAuth()); - - mAltAuthInterceptor.showAlternateAuthBouncer(); // force show - assertFalse(mController.shouldPauseAuth()); - assertTrue(mAltAuthInterceptor.isShowingAlternateAuthBouncer()); - - mAltAuthInterceptor.hideAlternateAuthBouncer(); // stop force show - assertTrue(mController.shouldPauseAuth()); - assertFalse(mAltAuthInterceptor.isShowingAlternateAuthBouncer()); - } - - @Test - public void testOnDetachedStateReset() { - // GIVEN view is attached - mController.onViewAttached(); - captureAltAuthInterceptor(); - - // WHEN view is detached - mController.onViewDetached(); - - // THEN remove alternate auth interceptor - verify(mStatusBarKeyguardViewManager).removeAlternateAuthInterceptor(mAltAuthInterceptor); - } - - @Test - public void testHiddenUdfpsBouncerOnTouchOutside_nothingHappens() { - // GIVEN view is attached - mController.onViewAttached(); - captureAltAuthInterceptor(); - - // GIVEN udfps bouncer isn't showing - mAltAuthInterceptor.hideAlternateAuthBouncer(); - - // WHEN touch is observed outside the view - mController.onTouchOutsideView(); - - // THEN bouncer / alt auth methods are never called - verify(mStatusBarKeyguardViewManager, never()).showBouncer(anyBoolean()); - verify(mStatusBarKeyguardViewManager, never()).resetAlternateAuth(anyBoolean()); - } - - @Test - public void testShowingUdfpsBouncerOnTouchOutsideWithinThreshold_nothingHappens() { - // GIVEN view is attached - mController.onViewAttached(); - captureAltAuthInterceptor(); - - // GIVEN udfps bouncer is showing - mAltAuthInterceptor.showAlternateAuthBouncer(); - - // WHEN touch is observed outside the view 200ms later (just within threshold) - mSystemClock.advanceTime(200); - mController.onTouchOutsideView(); - - // THEN bouncer / alt auth methods are never called because not enough time has passed - verify(mStatusBarKeyguardViewManager, never()).showBouncer(anyBoolean()); - verify(mStatusBarKeyguardViewManager, never()).resetAlternateAuth(anyBoolean()); - } - - @Test - public void testShowingUdfpsBouncerOnTouchOutsideAboveThreshold_showInputBouncer() { - // GIVEN view is attached - mController.onViewAttached(); - captureAltAuthInterceptor(); - - // GIVEN udfps bouncer is showing - mAltAuthInterceptor.showAlternateAuthBouncer(); - - // WHEN touch is observed outside the view 205ms later - mSystemClock.advanceTime(205); - mController.onTouchOutsideView(); - - // THEN show the bouncer - verify(mStatusBarKeyguardViewManager).showBouncer(eq(true)); - } - - @Test - public void testFadeInWithStatusBarExpansion() { - // GIVEN view is attached - mController.onViewAttached(); - captureStatusBarExpansionListeners(); - captureKeyguardStateControllerCallback(); - reset(mView); - - // WHEN status bar expansion is 0 - updateStatusBarExpansion(0, true); - - // THEN alpha is 0 - verify(mView).setUnpausedAlpha(0); - } - - @Test - public void testShowUdfpsBouncer() { - // GIVEN view is attached and status bar expansion is 0 - mController.onViewAttached(); - captureStatusBarExpansionListeners(); - captureKeyguardStateControllerCallback(); - captureAltAuthInterceptor(); - updateStatusBarExpansion(0, true); - reset(mView); - when(mView.getContext()).thenReturn(mResourceContext); - when(mResourceContext.getString(anyInt())).thenReturn("test string"); - - // WHEN status bar expansion is 0 but udfps bouncer is requested - mAltAuthInterceptor.showAlternateAuthBouncer(); - - // THEN alpha is 255 - verify(mView).setUnpausedAlpha(255); - } - - @Test - public void testTransitionToFullShadeProgress() { - // GIVEN view is attached and status bar expansion is 1f - mController.onViewAttached(); - captureStatusBarExpansionListeners(); - updateStatusBarExpansion(1f, true); - reset(mView); - when(mView.getDialogSuggestedAlpha()).thenReturn(1f); - - // WHEN we're transitioning to the full shade - float transitionProgress = .6f; - mController.setTransitionToFullShadeProgress(transitionProgress); - - // THEN alpha is between 0 and 255 - verify(mView).setUnpausedAlpha((int) ((1f - transitionProgress) * 255)); - } - - @Test - public void testShowUdfpsBouncer_transitionToFullShadeProgress() { - // GIVEN view is attached and status bar expansion is 1f - mController.onViewAttached(); - captureStatusBarExpansionListeners(); - captureKeyguardStateControllerCallback(); - captureAltAuthInterceptor(); - updateStatusBarExpansion(1f, true); - mAltAuthInterceptor.showAlternateAuthBouncer(); - reset(mView); - - // WHEN we're transitioning to the full shade - mController.setTransitionToFullShadeProgress(1.0f); - - // THEN alpha is 255 (b/c udfps bouncer is requested) - verify(mView).setUnpausedAlpha(255); - } - - @Test - public void testUpdatePanelExpansion_pauseAuth() { - // GIVEN view is attached + on the keyguard - mController.onViewAttached(); - captureStatusBarStateListeners(); - captureStatusBarExpansionListeners(); - sendStatusBarStateChanged(StatusBarState.KEYGUARD); - reset(mView); - - // WHEN panelViewExpansion changes to hide - when(mView.getUnpausedAlpha()).thenReturn(0); - updateStatusBarExpansion(0f, false); - - // THEN pause auth is updated to PAUSE - verify(mView, atLeastOnce()).setPauseAuth(true); - } - - @Test - public void testUpdatePanelExpansion_unpauseAuth() { - // GIVEN view is attached + on the keyguard + panel expansion is 0f - mController.onViewAttached(); - captureStatusBarStateListeners(); - captureStatusBarExpansionListeners(); - sendStatusBarStateChanged(StatusBarState.KEYGUARD); - reset(mView); - - // WHEN panelViewExpansion changes to expanded - when(mView.getUnpausedAlpha()).thenReturn(255); - updateStatusBarExpansion(1f, true); - - // THEN pause auth is updated to NOT pause - verify(mView, atLeastOnce()).setPauseAuth(false); - } - protected void sendStatusBarStateChanged(int statusBarState) { mStatusBarStateListener.onStateChanged(statusBarState); } diff --git a/packages/SystemUI/tests/src/com/android/systemui/biometrics/UdfpsKeyguardViewControllerTest.java b/packages/SystemUI/tests/src/com/android/systemui/biometrics/UdfpsKeyguardViewControllerTest.java index a067e430c2487..55b61948ee45e 100644 --- a/packages/SystemUI/tests/src/com/android/systemui/biometrics/UdfpsKeyguardViewControllerTest.java +++ b/packages/SystemUI/tests/src/com/android/systemui/biometrics/UdfpsKeyguardViewControllerTest.java @@ -16,7 +16,15 @@ package com.android.systemui.biometrics; +import static org.junit.Assert.assertFalse; import static org.junit.Assert.assertTrue; +import static org.mockito.ArgumentMatchers.anyBoolean; +import static org.mockito.ArgumentMatchers.anyInt; +import static org.mockito.Mockito.atLeast; +import static org.mockito.Mockito.atLeastOnce; +import static org.mockito.Mockito.eq; +import static org.mockito.Mockito.never; +import static org.mockito.Mockito.reset; import static org.mockito.Mockito.verify; import static org.mockito.Mockito.when; @@ -25,6 +33,7 @@ import android.testing.TestableLooper.RunWithLooper; import androidx.test.filters.SmallTest; +import com.android.systemui.shade.ShadeExpansionListener; import com.android.systemui.statusbar.StatusBarState; import com.android.systemui.statusbar.phone.KeyguardBouncer; @@ -60,6 +69,353 @@ public class UdfpsKeyguardViewControllerTest extends UdfpsKeyguardViewController assertTrue(mController.shouldPauseAuth()); } + + + @Test + public void testRegistersExpansionChangedListenerOnAttached() { + mController.onViewAttached(); + captureStatusBarExpansionListeners(); + } + + @Test + public void testRegistersStatusBarStateListenersOnAttached() { + mController.onViewAttached(); + captureStatusBarStateListeners(); + } + + @Test + public void testViewControllerQueriesSBStateOnAttached() { + mController.onViewAttached(); + verify(mStatusBarStateController).getState(); + verify(mStatusBarStateController).getDozeAmount(); + + final float dozeAmount = .88f; + when(mStatusBarStateController.getState()).thenReturn(StatusBarState.SHADE_LOCKED); + when(mStatusBarStateController.getDozeAmount()).thenReturn(dozeAmount); + captureStatusBarStateListeners(); + + mController.onViewAttached(); + verify(mView, atLeast(1)).setPauseAuth(true); + verify(mView).onDozeAmountChanged(dozeAmount, dozeAmount, + UdfpsKeyguardView.ANIMATION_BETWEEN_AOD_AND_LOCKSCREEN); + } + + @Test + public void testListenersUnregisteredOnDetached() { + mController.onViewAttached(); + captureStatusBarStateListeners(); + captureStatusBarExpansionListeners(); + captureKeyguardStateControllerCallback(); + mController.onViewDetached(); + + verify(mStatusBarStateController).removeCallback(mStatusBarStateListener); + for (ShadeExpansionListener listener : mExpansionListeners) { + verify(mShadeExpansionStateManager).removeExpansionListener(listener); + } + verify(mKeyguardStateController).removeCallback(mKeyguardStateControllerCallback); + } + + @Test + public void testDozeEventsSentToView() { + mController.onViewAttached(); + captureStatusBarStateListeners(); + + final float linear = .55f; + final float eased = .65f; + mStatusBarStateListener.onDozeAmountChanged(linear, eased); + + verify(mView).onDozeAmountChanged(linear, eased, + UdfpsKeyguardView.ANIMATION_BETWEEN_AOD_AND_LOCKSCREEN); + } + + @Test + public void testShouldPauseAuthUnpausedAlpha0() { + mController.onViewAttached(); + captureStatusBarStateListeners(); + + when(mView.getUnpausedAlpha()).thenReturn(0); + sendStatusBarStateChanged(StatusBarState.KEYGUARD); + + assertTrue(mController.shouldPauseAuth()); + } + + @Test + public void testFadeFromDialogSuggestedAlpha() { + // GIVEN view is attached and status bar expansion is 1f + mController.onViewAttached(); + captureStatusBarStateListeners(); + captureStatusBarExpansionListeners(); + updateStatusBarExpansion(1f, true); + reset(mView); + + // WHEN dialog suggested alpha is .6f + when(mView.getDialogSuggestedAlpha()).thenReturn(.6f); + sendStatusBarStateChanged(StatusBarState.KEYGUARD); + + // THEN alpha is updated based on dialog suggested alpha + verify(mView).setUnpausedAlpha((int) (.6f * 255)); + } + + @Test + public void testShouldNotPauseAuthOnKeyguard() { + mController.onViewAttached(); + captureStatusBarStateListeners(); + + sendStatusBarStateChanged(StatusBarState.KEYGUARD); + + assertFalse(mController.shouldPauseAuth()); + } + + @Test + public void testShouldPauseAuthIsLaunchTransitionFadingAway() { + // GIVEN view is attached and we're on the keyguard (see testShouldNotPauseAuthOnKeyguard) + mController.onViewAttached(); + captureStatusBarStateListeners(); + sendStatusBarStateChanged(StatusBarState.KEYGUARD); + + // WHEN isLaunchTransitionFadingAway=true + captureKeyguardStateControllerCallback(); + when(mKeyguardStateController.isLaunchTransitionFadingAway()).thenReturn(true); + mKeyguardStateControllerCallback.onLaunchTransitionFadingAwayChanged(); + + // THEN pause auth + assertTrue(mController.shouldPauseAuth()); + } + + @Test + public void testShouldPauseAuthOnShadeLocked() { + mController.onViewAttached(); + captureStatusBarStateListeners(); + + sendStatusBarStateChanged(StatusBarState.SHADE_LOCKED); + + assertTrue(mController.shouldPauseAuth()); + } + + @Test + public void testShouldPauseAuthOnShade() { + mController.onViewAttached(); + captureStatusBarStateListeners(); + + // WHEN not on keyguard yet (shade = home) + sendStatusBarStateChanged(StatusBarState.SHADE); + + // THEN pause auth + assertTrue(mController.shouldPauseAuth()); + } + + @Test + public void testShouldPauseAuthAnimatingScreenOffFromShade() { + mController.onViewAttached(); + captureStatusBarStateListeners(); + + // WHEN transitioning from home/shade => keyguard + animating screen off + mStatusBarStateListener.onStatePreChange(StatusBarState.SHADE, StatusBarState.KEYGUARD); + when(mKeyguardViewMediator.isAnimatingScreenOff()).thenReturn(true); + + // THEN pause auth + assertTrue(mController.shouldPauseAuth()); + } + + @Test + public void testDoNotPauseAuthAnimatingScreenOffFromLS() { + mController.onViewAttached(); + captureStatusBarStateListeners(); + + // WHEN animating screen off transition from LS => AOD + sendStatusBarStateChanged(StatusBarState.KEYGUARD); + when(mKeyguardViewMediator.isAnimatingScreenOff()).thenReturn(true); + + // THEN don't pause auth + assertFalse(mController.shouldPauseAuth()); + } + + @Test + public void testOverrideShouldPauseAuthOnShadeLocked() { + mController.onViewAttached(); + captureStatusBarStateListeners(); + captureAltAuthInterceptor(); + + sendStatusBarStateChanged(StatusBarState.SHADE_LOCKED); + assertTrue(mController.shouldPauseAuth()); + + mAltAuthInterceptor.showAlternateAuthBouncer(); // force show + assertFalse(mController.shouldPauseAuth()); + assertTrue(mAltAuthInterceptor.isShowingAlternateAuthBouncer()); + + mAltAuthInterceptor.hideAlternateAuthBouncer(); // stop force show + assertTrue(mController.shouldPauseAuth()); + assertFalse(mAltAuthInterceptor.isShowingAlternateAuthBouncer()); + } + + @Test + public void testOnDetachedStateReset() { + // GIVEN view is attached + mController.onViewAttached(); + captureAltAuthInterceptor(); + + // WHEN view is detached + mController.onViewDetached(); + + // THEN remove alternate auth interceptor + verify(mStatusBarKeyguardViewManager).removeAlternateAuthInterceptor(mAltAuthInterceptor); + } + + @Test + public void testHiddenUdfpsBouncerOnTouchOutside_nothingHappens() { + // GIVEN view is attached + mController.onViewAttached(); + captureAltAuthInterceptor(); + + // GIVEN udfps bouncer isn't showing + mAltAuthInterceptor.hideAlternateAuthBouncer(); + + // WHEN touch is observed outside the view + mController.onTouchOutsideView(); + + // THEN bouncer / alt auth methods are never called + verify(mStatusBarKeyguardViewManager, never()).showBouncer(anyBoolean()); + verify(mStatusBarKeyguardViewManager, never()).resetAlternateAuth(anyBoolean()); + } + + @Test + public void testShowingUdfpsBouncerOnTouchOutsideWithinThreshold_nothingHappens() { + // GIVEN view is attached + mController.onViewAttached(); + captureAltAuthInterceptor(); + + // GIVEN udfps bouncer is showing + mAltAuthInterceptor.showAlternateAuthBouncer(); + + // WHEN touch is observed outside the view 200ms later (just within threshold) + mSystemClock.advanceTime(200); + mController.onTouchOutsideView(); + + // THEN bouncer / alt auth methods are never called because not enough time has passed + verify(mStatusBarKeyguardViewManager, never()).showBouncer(anyBoolean()); + verify(mStatusBarKeyguardViewManager, never()).resetAlternateAuth(anyBoolean()); + } + + @Test + public void testShowingUdfpsBouncerOnTouchOutsideAboveThreshold_showInputBouncer() { + // GIVEN view is attached + mController.onViewAttached(); + captureAltAuthInterceptor(); + + // GIVEN udfps bouncer is showing + mAltAuthInterceptor.showAlternateAuthBouncer(); + + // WHEN touch is observed outside the view 205ms later + mSystemClock.advanceTime(205); + mController.onTouchOutsideView(); + + // THEN show the bouncer + verify(mStatusBarKeyguardViewManager).showBouncer(eq(true)); + } + + @Test + public void testFadeInWithStatusBarExpansion() { + // GIVEN view is attached + mController.onViewAttached(); + captureStatusBarExpansionListeners(); + captureKeyguardStateControllerCallback(); + reset(mView); + + // WHEN status bar expansion is 0 + updateStatusBarExpansion(0, true); + + // THEN alpha is 0 + verify(mView).setUnpausedAlpha(0); + } + + @Test + public void testShowUdfpsBouncer() { + // GIVEN view is attached and status bar expansion is 0 + mController.onViewAttached(); + captureStatusBarExpansionListeners(); + captureKeyguardStateControllerCallback(); + captureAltAuthInterceptor(); + updateStatusBarExpansion(0, true); + reset(mView); + when(mView.getContext()).thenReturn(mResourceContext); + when(mResourceContext.getString(anyInt())).thenReturn("test string"); + + // WHEN status bar expansion is 0 but udfps bouncer is requested + mAltAuthInterceptor.showAlternateAuthBouncer(); + + // THEN alpha is 255 + verify(mView).setUnpausedAlpha(255); + } + + @Test + public void testTransitionToFullShadeProgress() { + // GIVEN view is attached and status bar expansion is 1f + mController.onViewAttached(); + captureStatusBarExpansionListeners(); + updateStatusBarExpansion(1f, true); + reset(mView); + when(mView.getDialogSuggestedAlpha()).thenReturn(1f); + + // WHEN we're transitioning to the full shade + float transitionProgress = .6f; + mController.setTransitionToFullShadeProgress(transitionProgress); + + // THEN alpha is between 0 and 255 + verify(mView).setUnpausedAlpha((int) ((1f - transitionProgress) * 255)); + } + + @Test + public void testShowUdfpsBouncer_transitionToFullShadeProgress() { + // GIVEN view is attached and status bar expansion is 1f + mController.onViewAttached(); + captureStatusBarExpansionListeners(); + captureKeyguardStateControllerCallback(); + captureAltAuthInterceptor(); + updateStatusBarExpansion(1f, true); + mAltAuthInterceptor.showAlternateAuthBouncer(); + reset(mView); + + // WHEN we're transitioning to the full shade + mController.setTransitionToFullShadeProgress(1.0f); + + // THEN alpha is 255 (b/c udfps bouncer is requested) + verify(mView).setUnpausedAlpha(255); + } + + @Test + public void testUpdatePanelExpansion_pauseAuth() { + // GIVEN view is attached + on the keyguard + mController.onViewAttached(); + captureStatusBarStateListeners(); + captureStatusBarExpansionListeners(); + sendStatusBarStateChanged(StatusBarState.KEYGUARD); + reset(mView); + + // WHEN panelViewExpansion changes to hide + when(mView.getUnpausedAlpha()).thenReturn(0); + updateStatusBarExpansion(0f, false); + + // THEN pause auth is updated to PAUSE + verify(mView, atLeastOnce()).setPauseAuth(true); + } + + @Test + public void testUpdatePanelExpansion_unpauseAuth() { + // GIVEN view is attached + on the keyguard + panel expansion is 0f + mController.onViewAttached(); + captureStatusBarStateListeners(); + captureStatusBarExpansionListeners(); + sendStatusBarStateChanged(StatusBarState.KEYGUARD); + reset(mView); + + // WHEN panelViewExpansion changes to expanded + when(mView.getUnpausedAlpha()).thenReturn(255); + updateStatusBarExpansion(1f, true); + + // THEN pause auth is updated to NOT pause + verify(mView, atLeastOnce()).setPauseAuth(false); + } + private void captureBouncerExpansionCallback() { verify(mBouncer).addBouncerExpansionCallback(mBouncerExpansionCallbackCaptor.capture()); mBouncerExpansionCallback = mBouncerExpansionCallbackCaptor.getValue();