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
This commit is contained in:
Beverly
2022-10-24 18:48:28 +00:00
parent fd0eb61758
commit 3c0fb42de0
17 changed files with 1266 additions and 1063 deletions

View File

@@ -63,6 +63,7 @@ import com.android.systemui.dump.DumpManager;
import com.android.systemui.flags.FeatureFlags;
import com.android.systemui.flags.Flags;
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;
@@ -133,6 +134,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.
@@ -219,7 +221,8 @@ public class UdfpsController implements DozeReceiver {
mUnlockedScreenOffAnimationController,
mUdfpsDisplayMode, requestId, reason, callback,
(view, event, fromUdfpsView) -> onTouch(requestId, event,
fromUdfpsView), mActivityLaunchAnimator)));
fromUdfpsView), mActivityLaunchAnimator, mFeatureFlags,
mBouncerInteractor)));
}
@Override
@@ -620,7 +623,8 @@ public class UdfpsController implements DozeReceiver {
@NonNull LatencyTracker latencyTracker,
@NonNull ActivityLaunchAnimator activityLaunchAnimator,
@NonNull Optional<AlternateUdfpsTouchProvider> alternateTouchProvider,
@BiometricsBackground Executor biometricsExecutor) {
@BiometricsBackground Executor biometricsExecutor,
@NonNull BouncerInteractor bouncerInteractor) {
mContext = context;
mExecution = execution;
mVibrator = vibrator;
@@ -651,6 +655,7 @@ public class UdfpsController implements DozeReceiver {
mActivityLaunchAnimator = activityLaunchAnimator;
mAlternateTouchProvider = alternateTouchProvider.orElse(null);
mBiometricExecutor = biometricsExecutor;
mBouncerInteractor = bouncerInteractor;
mOrientationListener = new BiometricDisplayListener(
context,

View File

@@ -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 -> {

View File

@@ -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<UdfpsKeyguardView> {
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<UdfpsKeyguardView>(
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<String>) {
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<Ud
*
* Does nothing if we weren't previously showing the UDFPS bouncer.
*/
private void maybeShowInputBouncer() {
if (mShowingUdfpsBouncer && hasUdfpsBouncerShownWithMinTime()) {
mKeyguardViewManager.showBouncer(true);
private fun maybeShowInputBouncer() {
if (showingUdfpsBouncer && hasUdfpsBouncerShownWithMinTime()) {
keyguardViewManager.showBouncer(true)
}
}
/**
* Whether the udfps bouncer has shown for at least 200ms before allowing touches outside
* of the udfps icon area to dismiss the udfps bouncer and show the pin/pattern/password
* bouncer.
* Whether the udfps bouncer has shown for at least 200ms before allowing touches outside of the
* udfps icon area to dismiss the udfps bouncer and show the pin/pattern/password bouncer.
*/
private boolean hasUdfpsBouncerShownWithMinTime() {
return (mSystemClock.uptimeMillis() - mLastUdfpsBouncerShowTime) > 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();
}
};
}

View File

@@ -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<KeyguardBouncerModel?>(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) {

View File

@@ -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<Unit> = repository.startingToHide.filter { it }.map {}
val isVisible: Flow<Boolean> = repository.isVisible
val isBackButtonEnabled: Flow<Boolean> = repository.isBackButtonEnabled.filterNotNull()
val expansionAmount: Flow<Float> = repository.expansionAmount
val showMessage: Flow<BouncerShowMessageModel> = repository.showMessage.filterNotNull()
val startingDisappearAnimation: Flow<Runnable> =
repository.startingDisappearAnimation.filterNotNull()
@@ -98,6 +98,17 @@ constructor(
repository.onDismissAction.filterNotNull()
val resourceUpdateRequests: Flow<Boolean> = repository.resourceUpdateRequests.filter { it }
val keyguardPosition: Flow<Float> = repository.keyguardPosition
val panelExpansionAmount: Flow<Float> = repository.panelExpansionAmount
/** 0f = bouncer fully hidden. 1f = bouncer fully visible. */
val bouncerExpansion: Flow<Float> = //
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. */

View File

@@ -38,7 +38,7 @@ constructor(
private val interactor: BouncerInteractor,
) {
/** Observe on bouncer expansion amount. */
val bouncerExpansionAmount: Flow<Float> = interactor.expansionAmount
val bouncerExpansionAmount: Flow<Float> = interactor.panelExpansionAmount
/** Observe on bouncer visibility. */
val isBouncerVisible: Flow<Boolean> = interactor.isVisible

View File

@@ -4644,7 +4644,7 @@ public final class NotificationPanelViewController {
mUpdateFlingVelocity = vel;
}
} else if (!mCentralSurfaces.isBouncerShowing()
&& !mStatusBarKeyguardViewManager.isShowingAlternateAuthOrAnimating()
&& !mStatusBarKeyguardViewManager.isShowingAlternateAuth()
&& !mKeyguardStateController.isKeyguardGoingAway()) {
onEmptySpaceClick();
onTrackingStopped(true);

View File

@@ -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;
}

View File

@@ -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
*/

View File

@@ -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<KeyguardViewManagerCallback> 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
@@ -1355,7 +1352,7 @@ public class StatusBarKeyguardViewManager implements RemoteInputController.Callb
mBouncerInteractor.notifyKeyguardAuthenticated(strongAuth);
}
if (mAlternateAuthInterceptor != null && isShowingAlternateAuthOrAnimating()) {
if (mAlternateAuthInterceptor != null && isShowingAlternateAuth()) {
resetAlternateAuth(false);
executeAfterKeyguardGoneAction();
}
@@ -1441,6 +1438,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);
@@ -1464,6 +1465,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.
*/
@@ -1476,8 +1491,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);
}
}
@@ -1491,21 +1506,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. */
@@ -1638,38 +1645,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.
@@ -1679,5 +1654,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) { }
}
}

View File

@@ -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<WindowManager.LayoutParams>
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()
}

View File

@@ -71,6 +71,7 @@ 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;
@@ -192,6 +193,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<IUdfpsOverlayController> mOverlayCaptor;
@@ -272,7 +275,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());

View File

@@ -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<StatusBarStateController.StateListener> mStateListenerCaptor;
protected StatusBarStateController.StateListener mStatusBarStateListener;
private @Captor ArgumentCaptor<ShadeExpansionListener> mExpansionListenerCaptor;
protected List<ShadeExpansionListener> mExpansionListeners;
private @Captor ArgumentCaptor<StatusBarKeyguardViewManager.AlternateAuthInterceptor>
mAltAuthInterceptorCaptor;
protected StatusBarKeyguardViewManager.AlternateAuthInterceptor mAltAuthInterceptor;
private @Captor ArgumentCaptor<KeyguardStateController.Callback>
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);
}
}

View File

@@ -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<KeyguardBouncer.BouncerExpansionCallback>
mBouncerExpansionCallbackCaptor;
private KeyguardBouncer.BouncerExpansionCallback mBouncerExpansionCallback;
private UdfpsKeyguardViewController mController;
// Capture listeners so that they can be used to send events
@Captor private ArgumentCaptor<StatusBarStateController.StateListener> mStateListenerCaptor;
private StatusBarStateController.StateListener mStatusBarStateListener;
@Captor private ArgumentCaptor<ShadeExpansionListener> mExpansionListenerCaptor;
private List<ShadeExpansionListener> mExpansionListeners;
@Captor private ArgumentCaptor<StatusBarKeyguardViewManager.AlternateAuthInterceptor>
mAltAuthInterceptorCaptor;
private StatusBarKeyguardViewManager.AlternateAuthInterceptor mAltAuthInterceptor;
@Captor private ArgumentCaptor<KeyguardStateController.Callback>
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();
}
}

View File

@@ -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
}
}

View File

@@ -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()
}

View File

@@ -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