Merge changes from topic "udfps_keyguard_view_controller_to_kotlin" into tm-qpr-dev

* changes:
  DO NOT MERGE [1.5/N] side fps bouncer - Update UdfpsKeyguardViewControllerBaseTest
  1/N sideFPs bouncer, separate altBouncer callback
  Refactor file from java => kotlin
This commit is contained in:
Beverly Tai
2022-11-02 16:33:00 +00:00
committed by Android (Google) Code Review
18 changed files with 1029 additions and 817 deletions

View File

@@ -60,7 +60,9 @@ import com.android.systemui.dagger.SysUISingleton;
import com.android.systemui.dagger.qualifiers.Main;
import com.android.systemui.doze.DozeReceiver;
import com.android.systemui.dump.DumpManager;
import com.android.systemui.flags.FeatureFlags;
import com.android.systemui.keyguard.ScreenLifecycle;
import com.android.systemui.keyguard.domain.interactor.BouncerInteractor;
import com.android.systemui.plugins.FalsingManager;
import com.android.systemui.plugins.statusbar.StatusBarStateController;
import com.android.systemui.shade.ShadeExpansionStateManager;
@@ -119,6 +121,7 @@ public class UdfpsController implements DozeReceiver {
@NonNull private final SystemUIDialogManager mDialogManager;
@NonNull private final KeyguardUpdateMonitor mKeyguardUpdateMonitor;
@NonNull private final VibratorHelper mVibrator;
@NonNull private final FeatureFlags mFeatureFlags;
@NonNull private final FalsingManager mFalsingManager;
@NonNull private final PowerManager mPowerManager;
@NonNull private final AccessibilityManager mAccessibilityManager;
@@ -130,6 +133,7 @@ public class UdfpsController implements DozeReceiver {
@NonNull private final LatencyTracker mLatencyTracker;
@VisibleForTesting @NonNull final BiometricDisplayListener mOrientationListener;
@NonNull private final ActivityLaunchAnimator mActivityLaunchAnimator;
@NonNull private final BouncerInteractor mBouncerInteractor;
// Currently the UdfpsController supports a single UDFPS sensor. If devices have multiple
// sensors, this, in addition to a lot of the code here, will be updated.
@@ -212,7 +216,8 @@ public class UdfpsController implements DozeReceiver {
mUnlockedScreenOffAnimationController,
mUdfpsDisplayMode, requestId, reason, callback,
(view, event, fromUdfpsView) -> onTouch(requestId, event,
fromUdfpsView), mActivityLaunchAnimator)));
fromUdfpsView), mActivityLaunchAnimator, mFeatureFlags,
mBouncerInteractor)));
}
@Override
@@ -590,6 +595,7 @@ public class UdfpsController implements DozeReceiver {
@NonNull StatusBarKeyguardViewManager statusBarKeyguardViewManager,
@NonNull DumpManager dumpManager,
@NonNull KeyguardUpdateMonitor keyguardUpdateMonitor,
@NonNull FeatureFlags featureFlags,
@NonNull FalsingManager falsingManager,
@NonNull PowerManager powerManager,
@NonNull AccessibilityManager accessibilityManager,
@@ -608,7 +614,8 @@ public class UdfpsController implements DozeReceiver {
@NonNull LatencyTracker latencyTracker,
@NonNull ActivityLaunchAnimator activityLaunchAnimator,
@NonNull Optional<AlternateUdfpsTouchProvider> alternateTouchProvider,
@BiometricsBackground Executor biometricsExecutor) {
@BiometricsBackground Executor biometricsExecutor,
@NonNull BouncerInteractor bouncerInteractor) {
mContext = context;
mExecution = execution;
mVibrator = vibrator;
@@ -638,6 +645,8 @@ public class UdfpsController implements DozeReceiver {
mActivityLaunchAnimator = activityLaunchAnimator;
mAlternateTouchProvider = alternateTouchProvider.orElse(null);
mBiometricExecutor = biometricsExecutor;
mFeatureFlags = featureFlags;
mBouncerInteractor = bouncerInteractor;
mOrientationListener = new BiometricDisplayListener(
context,

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

@@ -1,548 +0,0 @@
/*
* Copyright (C) 2021 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 com.android.systemui.statusbar.StatusBarState.KEYGUARD;
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);
}
mActivityLaunchAnimator.removeListener(mActivityLaunchAnimatorListener);
}
@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);
mView.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;
}
boolean udfpsAffordanceWasNotShowing = shouldPauseAuth();
mShowingUdfpsBouncer = show;
if (mShowingUdfpsBouncer) {
mLastUdfpsBouncerShowTime = mSystemClock.uptimeMillis();
}
if (mShowingUdfpsBouncer) {
if (udfpsAffordanceWasNotShowing) {
mView.animateInUdfpsBouncer(null);
}
if (mKeyguardStateController.isOccluded()) {
mKeyguardUpdateMonitor.requestFaceAuthOnOccludingApp(true);
}
mView.announceForAccessibility(mView.getContext().getString(
R.string.accessibility_fingerprint_bouncer));
} else {
mKeyguardUpdateMonitor.requestFaceAuthOnOccludingApp(false);
}
updateGenericBouncerVisibility();
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.
*/
public boolean shouldPauseAuth() {
if (mShowingUdfpsBouncer) {
return false;
}
if (mUdfpsRequested && !getNotificationShadeVisible()
&& (!mIsGenericBouncerShowing
|| mInputBouncerHiddenAmount != KeyguardBouncer.EXPANSION_VISIBLE)
&& mKeyguardStateController.isShowing()) {
return false;
}
if (mLaunchTransitionFadingAway) {
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
// mUnlockedScreenOffDozeAnimator.
if (mStatusBarState != KEYGUARD && mLastDozeAmount == 0f) {
return true;
}
if (mInputBouncerHiddenAmount < .5f) {
return true;
}
if (mView.getUnpausedAlpha() < (255 * .1)) {
return true;
}
return false;
}
@Override
public boolean listenForTouchesOutsideView() {
return true;
}
@Override
public void onTouchOutsideView() {
maybeShowInputBouncer();
}
/**
* If we were previously showing the udfps bouncer, hide it and instead show the regular
* (pin/pattern/password) bouncer.
*
* Does nothing if we weren't previously showing the UDFPS bouncer.
*/
private void maybeShowInputBouncer() {
if (mShowingUdfpsBouncer && hasUdfpsBouncerShownWithMinTime()) {
mKeyguardViewManager.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.
*/
private boolean hasUdfpsBouncerShownWithMinTime() {
return (mSystemClock.uptimeMillis() - mLastUdfpsBouncerShowTime) > 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.
*/
public void setTransitionToFullShadeProgress(float progress) {
mTransitionToFullShadeProgress = progress;
updateAlpha();
}
/**
* 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() {
// 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) {
// swipe from top of the lockscreen to expand full QS:
alpha *= (1.0f - Interpolators.EMPHASIZED_DECELERATE.getInterpolation(mQsExpansion));
// swipe from the middle (empty space) of lockscreen to expand the notification shade:
alpha *= (1.0f - mTransitionToFullShadeProgress);
// 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);
}
// Fade out alpha when a dialog is shown
// Fade in alpha when a dialog is hidden
alpha *= mView.getDialogSuggestedAlpha();
}
mView.setUnpausedAlpha(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) {
// input bouncer is fully showing
mInputBouncerHiddenAmount = 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());
}
}
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

@@ -0,0 +1,550 @@
/*
* Copyright (C) 2021 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.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
/** 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
)
}
}
/**
* 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()
}
private val keyguardStateControllerCallback: KeyguardStateController.Callback =
object : KeyguardStateController.Callback {
override fun onLaunchTransitionFadingAwayChanged() {
launchTransitionFadingAway = keyguardStateController.isLaunchTransitionFadingAway
updatePauseAuth()
}
}
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 fun showUdfpsBouncer(show: Boolean): Boolean {
if (showingUdfpsBouncer == show) {
return false
}
val udfpsAffordanceWasNotShowing = shouldPauseAuth()
showingUdfpsBouncer = show
if (showingUdfpsBouncer) {
lastUdfpsBouncerShowTime = systemClock.uptimeMillis()
}
if (showingUdfpsBouncer) {
if (udfpsAffordanceWasNotShowing) {
view.animateInUdfpsBouncer(null)
}
if (keyguardStateController.isOccluded) {
keyguardUpdateMonitor.requestFaceAuthOnOccludingApp(true)
}
view.announceForAccessibility(
view.context.getString(R.string.accessibility_fingerprint_bouncer)
)
} else {
keyguardUpdateMonitor.requestFaceAuthOnOccludingApp(false)
}
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.
*/
override fun shouldPauseAuth(): Boolean {
if (showingUdfpsBouncer) {
return false
}
if (
udfpsRequested &&
!notificationShadeVisible &&
!isInputBouncerFullyVisible() &&
keyguardStateController.isShowing
) {
return false
}
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
// mUnlockedScreenOffDozeAnimator.
if (statusBarState != StatusBarState.KEYGUARD && lastDozeAmount == 0f) {
return true
}
if (isBouncerExpansionGreaterThan(.5f)) {
return true
}
return view.unpausedAlpha < 255 * .1
}
fun isBouncerExpansionGreaterThan(bouncerExpansionThreshold: Float): Boolean {
return if (isModernBouncerEnabled) {
inputBouncerExpansion >= bouncerExpansionThreshold
} else {
inputBouncerHiddenAmount < bouncerExpansionThreshold
}
}
fun isInputBouncerFullyVisible(): Boolean {
return if (isModernBouncerEnabled) {
inputBouncerExpansion == 1f
} else {
keyguardViewManager.isBouncerShowing && !keyguardViewManager.isShowingAlternateAuth
}
}
override fun listenForTouchesOutsideView(): Boolean {
return true
}
override fun onTouchOutsideView() {
maybeShowInputBouncer()
}
/**
* If we were previously showing the udfps bouncer, hide it and instead show the regular
* (pin/pattern/password) bouncer.
*
* Does nothing if we weren't previously showing the UDFPS bouncer.
*/
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.
*/
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.
*/
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.
*/
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.
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 =
(alpha * (1.0f - Interpolators.EMPHASIZED_DECELERATE.getInterpolation(qsExpansion)))
.toInt()
// swipe from the middle (empty space) of lockscreen to expand the notification shade:
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 (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 = (alpha * view.dialogSuggestedAlpha).toInt()
}
view.unpausedAlpha = alpha
}
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
inputBouncerHiddenAmount = 0f
}
}
companion object {
const val TAG = "UdfpsKeyguardViewController"
}
}

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
@@ -1356,7 +1353,7 @@ public class StatusBarKeyguardViewManager implements RemoteInputController.Callb
mBouncerInteractor.notifyKeyguardAuthenticated(strongAuth);
}
if (mAlternateAuthInterceptor != null && isShowingAlternateAuthOrAnimating()) {
if (mAlternateAuthInterceptor != null && isShowingAlternateAuth()) {
resetAlternateAuth(false);
executeAfterKeyguardGoneAction();
}
@@ -1442,6 +1439,10 @@ public class StatusBarKeyguardViewManager implements RemoteInputController.Callb
pw.println(" mPendingWakeupAction: " + mPendingWakeupAction);
pw.println(" isBouncerShowing(): " + isBouncerShowing());
pw.println(" bouncerIsOrWillBeShowing(): " + bouncerIsOrWillBeShowing());
pw.println(" Registered KeyguardViewManagerCallbacks:");
for (KeyguardViewManagerCallback callback : mCallbacks) {
pw.println(" " + callback);
}
if (mBouncer != null) {
mBouncer.dump(pw);
@@ -1465,6 +1466,20 @@ public class StatusBarKeyguardViewManager implements RemoteInputController.Callb
}
}
/**
* Add a callback to listen for changes
*/
public void addCallback(KeyguardViewManagerCallback callback) {
mCallbacks.add(callback);
}
/**
* Removes callback to stop receiving updates
*/
public void removeCallback(KeyguardViewManagerCallback callback) {
mCallbacks.remove(callback);
}
/**
* Whether qs is currently expanded.
*/
@@ -1477,8 +1492,8 @@ public class StatusBarKeyguardViewManager implements RemoteInputController.Callb
*/
public void setQsExpansion(float qsExpansion) {
mQsExpansion = qsExpansion;
if (mAlternateAuthInterceptor != null) {
mAlternateAuthInterceptor.setQsExpansion(qsExpansion);
for (KeyguardViewManagerCallback callback : mCallbacks) {
callback.onQSExpansionChanged(mQsExpansion);
}
}
@@ -1492,21 +1507,13 @@ public class StatusBarKeyguardViewManager implements RemoteInputController.Callb
&& mAlternateAuthInterceptor.isShowingAlternateAuthBouncer();
}
public boolean isShowingAlternateAuthOrAnimating() {
return mAlternateAuthInterceptor != null
&& (mAlternateAuthInterceptor.isShowingAlternateAuthBouncer()
|| mAlternateAuthInterceptor.isAnimating());
}
/**
* Forward touches to any alternate authentication affordances.
* Forward touches to callbacks.
*/
public boolean onTouch(MotionEvent event) {
if (mAlternateAuthInterceptor == null) {
return false;
public void onTouch(MotionEvent event) {
for (KeyguardViewManagerCallback callback: mCallbacks) {
callback.onTouch(event);
}
return mAlternateAuthInterceptor.onTouch(event);
}
/** Update keyguard position based on a tapped X coordinate. */
@@ -1639,38 +1646,6 @@ public class StatusBarKeyguardViewManager implements RemoteInputController.Callb
*/
boolean isShowingAlternateAuthBouncer();
/**
* print information for the alternate auth interceptor registered
*/
void dump(PrintWriter pw);
/**
* @return true if the new auth method bouncer is currently animating in or out.
*/
boolean isAnimating();
/**
* How much QS is fully expanded where 0f is not showing and 1f is fully expanded.
*/
void setQsExpansion(float qsExpansion);
/**
* Forward potential touches to authentication interceptor
* @return true if event was handled
*/
boolean onTouch(MotionEvent event);
/**
* Update pin/pattern/password bouncer expansion amount where 0 is visible and 1 is fully
* hidden
*/
void setBouncerExpansionChanged(float expansion);
/**
* called when the bouncer view visibility has changed.
*/
void onBouncerVisibilityChanged();
/**
* Use when an app occluding the keyguard would like to give the user ability to
* unlock the device using udfps.
@@ -1680,5 +1655,25 @@ public class StatusBarKeyguardViewManager implements RemoteInputController.Callb
*/
void requestUdfps(boolean requestUdfps, int color);
/**
* print information for the alternate auth interceptor registered
*/
void dump(PrintWriter pw);
}
/**
* Callback for KeyguardViewManager state changes.
*/
public interface KeyguardViewManagerCallback {
/**
* Set the amount qs is expanded. For example, swipe down from the top of the
* lock screen to start the full QS expansion.
*/
default void onQSExpansionChanged(float qsExpansion) { }
/**
* Forward touch events to callbacks
*/
default void onTouch(MotionEvent event) { }
}
}

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

@@ -69,7 +69,9 @@ import com.android.systemui.R;
import com.android.systemui.SysuiTestCase;
import com.android.systemui.animation.ActivityLaunchAnimator;
import com.android.systemui.dump.DumpManager;
import com.android.systemui.flags.FeatureFlags;
import com.android.systemui.keyguard.ScreenLifecycle;
import com.android.systemui.keyguard.domain.interactor.BouncerInteractor;
import com.android.systemui.plugins.FalsingManager;
import com.android.systemui.plugins.statusbar.StatusBarStateController;
import com.android.systemui.shade.ShadeExpansionStateManager;
@@ -171,6 +173,8 @@ public class UdfpsControllerTest extends SysuiTestCase {
private FakeExecutor mFgExecutor;
@Mock
private UdfpsDisplayMode mUdfpsDisplayMode;
@Mock
private FeatureFlags mFeatureFlags;
// Stuff for configuring mocks
@Mock
@@ -191,6 +195,8 @@ public class UdfpsControllerTest extends SysuiTestCase {
private ActivityLaunchAnimator mActivityLaunchAnimator;
@Mock
private AlternateUdfpsTouchProvider mAlternateTouchProvider;
@Mock
private BouncerInteractor mBouncerInteractor;
// Capture listeners so that they can be used to send events
@Captor private ArgumentCaptor<IUdfpsOverlayController> mOverlayCaptor;
@@ -252,6 +258,7 @@ public class UdfpsControllerTest extends SysuiTestCase {
mStatusBarKeyguardViewManager,
mDumpManager,
mKeyguardUpdateMonitor,
mFeatureFlags,
mFalsingManager,
mPowerManager,
mAccessibilityManager,
@@ -270,7 +277,8 @@ public class UdfpsControllerTest extends SysuiTestCase {
mLatencyTracker,
mActivityLaunchAnimator,
Optional.of(mAlternateTouchProvider),
mBiometricsExecutor);
mBiometricsExecutor,
mBouncerInteractor);
verify(mFingerprintManager).setUdfpsOverlayController(mOverlayCaptor.capture());
mOverlayController = mOverlayCaptor.getValue();
verify(mScreenLifecycle).addObserver(mScreenObserverCaptor.capture());

View File

@@ -0,0 +1,172 @@
/*
* 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.mockito.ArgumentMatchers.anyInt;
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.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.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();
}
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

@@ -25,125 +25,52 @@ 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 testShouldPauseAuth_bouncerShowing() {
mController.onViewAttached();
captureStatusBarStateListeners();
sendStatusBarStateChanged(StatusBarState.KEYGUARD);
captureBouncerExpansionCallback();
when(mStatusBarKeyguardViewManager.isBouncerShowing()).thenReturn(true);
when(mStatusBarKeyguardViewManager.bouncerIsOrWillBeShowing()).thenReturn(true);
mBouncerExpansionCallback.onVisibilityChanged(true);
assertTrue(mController.shouldPauseAuth());
}
@Test
public void testRegistersExpansionChangedListenerOnAttached() {
mController.onViewAttached();
@@ -201,20 +128,6 @@ public class UdfpsKeyguardViewControllerTest extends SysuiTestCase {
UdfpsKeyguardView.ANIMATION_BETWEEN_AOD_AND_LOCKSCREEN);
}
@Test
public void testShouldPauseAuthBouncerShowing() {
mController.onViewAttached();
captureStatusBarStateListeners();
sendStatusBarStateChanged(StatusBarState.KEYGUARD);
captureAltAuthInterceptor();
when(mStatusBarKeyguardViewManager.isBouncerShowing()).thenReturn(true);
when(mStatusBarKeyguardViewManager.bouncerIsOrWillBeShowing()).thenReturn(true);
mAltAuthInterceptor.onBouncerVisibilityChanged();
assertTrue(mController.shouldPauseAuth());
}
@Test
public void testShouldPauseAuthUnpausedAlpha0() {
mController.onViewAttached();
@@ -503,41 +416,8 @@ public class UdfpsKeyguardViewControllerTest extends SysuiTestCase {
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