diff options
19 files changed, 560 insertions, 50 deletions
diff --git a/packages/SystemUI/res/drawable/fingerprint_bg.xml b/packages/SystemUI/res/drawable/fingerprint_bg.xml new file mode 100644 index 000000000000..2b0ab6f9a8d2 --- /dev/null +++ b/packages/SystemUI/res/drawable/fingerprint_bg.xml @@ -0,0 +1,25 @@ +<!-- 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. +--> +<shape + xmlns:android="http://schemas.android.com/apk/res/android" + android:shape="oval"> + + <solid + android:color="?android:attr/colorBackground"/> + + <size + android:width="64dp" + android:height="64dp"/> +</shape> diff --git a/packages/SystemUI/res/layout/udfps_keyguard_view.xml b/packages/SystemUI/res/layout/udfps_keyguard_view.xml index 0199ccb04be6..562040b0ad82 100644 --- a/packages/SystemUI/res/layout/udfps_keyguard_view.xml +++ b/packages/SystemUI/res/layout/udfps_keyguard_view.xml @@ -20,7 +20,13 @@ android:layout_width="match_parent" android:layout_height="match_parent"> - <!-- TODO: add background protection --> + <!-- Background protection --> + <ImageView + android:id="@+id/udfps_keyguard_fp_bg" + android:layout_width="match_parent" + android:layout_height="match_parent" + android:background="@drawable/fingerprint_bg" + android:visibility="gone"/> <!-- Fingerprint --> <ImageView diff --git a/packages/SystemUI/src/com/android/systemui/biometrics/UdfpsAnimationView.java b/packages/SystemUI/src/com/android/systemui/biometrics/UdfpsAnimationView.java index cef0ce19edb6..60fdbab8482c 100644 --- a/packages/SystemUI/src/com/android/systemui/biometrics/UdfpsAnimationView.java +++ b/packages/SystemUI/src/com/android/systemui/biometrics/UdfpsAnimationView.java @@ -79,6 +79,10 @@ abstract class UdfpsAnimationView extends FrameLayout { return mPauseAuth ? mAlpha : 255; } + boolean isPauseAuth() { + return mPauseAuth; + } + private int expansionToAlpha(float expansion) { // Fade to 0 opacity when reaching this expansion amount final float maxExpansion = 0.4f; diff --git a/packages/SystemUI/src/com/android/systemui/biometrics/UdfpsAnimationViewController.java b/packages/SystemUI/src/com/android/systemui/biometrics/UdfpsAnimationViewController.java index b6d80ba14dc0..b7726f41e4a8 100644 --- a/packages/SystemUI/src/com/android/systemui/biometrics/UdfpsAnimationViewController.java +++ b/packages/SystemUI/src/com/android/systemui/biometrics/UdfpsAnimationViewController.java @@ -24,10 +24,16 @@ import android.annotation.NonNull; import android.graphics.PointF; import android.graphics.RectF; +import com.android.systemui.Dumpable; +import com.android.systemui.dump.DumpManager; import com.android.systemui.plugins.statusbar.StatusBarStateController; +import com.android.systemui.statusbar.StatusBarState; import com.android.systemui.statusbar.phone.StatusBar; import com.android.systemui.util.ViewController; +import java.io.FileDescriptor; +import java.io.PrintWriter; + /** * Handles: * 1. registering for listeners when its view is attached and unregistering on view detached @@ -39,33 +45,50 @@ import com.android.systemui.util.ViewController; * - doze time event */ abstract class UdfpsAnimationViewController<T extends UdfpsAnimationView> - extends ViewController<T> { + extends ViewController<T> implements Dumpable { @NonNull final StatusBarStateController mStatusBarStateController; @NonNull final StatusBar mStatusBar; + @NonNull final DumpManager mDumpManger; private boolean mNotificationShadeExpanded; private int mStatusBarState; protected UdfpsAnimationViewController( T view, - StatusBarStateController statusBarStateController, - StatusBar statusBar) { + @NonNull StatusBarStateController statusBarStateController, + @NonNull StatusBar statusBar, + @NonNull DumpManager dumpManager) { super(view); mStatusBarStateController = statusBarStateController; mStatusBar = statusBar; + mDumpManger = dumpManager; } + abstract @NonNull String getTag(); + @Override protected void onViewAttached() { mStatusBarStateController.addCallback(mStateListener); mStateListener.onStateChanged(mStatusBarStateController.getState()); mStatusBar.addExpansionChangedListener(mStatusBarExpansionChangedListener); + + mDumpManger.registerDumpable(getTag(), this); } @Override protected void onViewDetached() { mStatusBarStateController.removeCallback(mStateListener); mStatusBar.removeExpansionChangedListener(mStatusBarExpansionChangedListener); + + mDumpManger.unregisterDumpable(getTag()); + } + + @Override + public void dump(FileDescriptor fd, PrintWriter pw, String[] args) { + pw.println("mStatusBarState=" + StatusBarState.toShortString(mStatusBarState)); + pw.println("mNotificationShadeExpanded=" + mNotificationShadeExpanded); + pw.println("shouldPauseAuth()=" + shouldPauseAuth()); + pw.println("isPauseAuth=" + mView.isPauseAuth()); } /** diff --git a/packages/SystemUI/src/com/android/systemui/biometrics/UdfpsBpViewController.java b/packages/SystemUI/src/com/android/systemui/biometrics/UdfpsBpViewController.java index b712c655a6e7..93d80e29aded 100644 --- a/packages/SystemUI/src/com/android/systemui/biometrics/UdfpsBpViewController.java +++ b/packages/SystemUI/src/com/android/systemui/biometrics/UdfpsBpViewController.java @@ -16,6 +16,9 @@ package com.android.systemui.biometrics; +import android.annotation.NonNull; + +import com.android.systemui.dump.DumpManager; import com.android.systemui.plugins.statusbar.StatusBarStateController; import com.android.systemui.statusbar.phone.StatusBar; @@ -24,9 +27,15 @@ import com.android.systemui.statusbar.phone.StatusBar; */ class UdfpsBpViewController extends UdfpsAnimationViewController<UdfpsBpView> { protected UdfpsBpViewController( - UdfpsBpView view, - StatusBarStateController statusBarStateController, - StatusBar statusBar) { - super(view, statusBarStateController, statusBar); + @NonNull UdfpsBpView view, + @NonNull StatusBarStateController statusBarStateController, + @NonNull StatusBar statusBar, + @NonNull DumpManager dumpManager) { + super(view, statusBarStateController, statusBar, dumpManager); + } + + @Override + @NonNull String getTag() { + return "UdfpsBpViewController"; } } diff --git a/packages/SystemUI/src/com/android/systemui/biometrics/UdfpsController.java b/packages/SystemUI/src/com/android/systemui/biometrics/UdfpsController.java index 94aeb73c4b42..797a4411b8c9 100644 --- a/packages/SystemUI/src/com/android/systemui/biometrics/UdfpsController.java +++ b/packages/SystemUI/src/com/android/systemui/biometrics/UdfpsController.java @@ -50,8 +50,10 @@ import com.android.systemui.R; 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.plugins.statusbar.StatusBarStateController; import com.android.systemui.statusbar.phone.StatusBar; +import com.android.systemui.statusbar.phone.StatusBarKeyguardViewManager; import com.android.systemui.util.concurrency.DelayableExecutor; import javax.inject.Inject; @@ -83,6 +85,8 @@ public class UdfpsController implements DozeReceiver, HbmCallback { private final DelayableExecutor mFgExecutor; @NonNull private final StatusBar mStatusBar; @NonNull private final StatusBarStateController mStatusBarStateController; + @NonNull private final StatusBarKeyguardViewManager mKeyguardViewManager; + @NonNull private final DumpManager mDumpManager; // 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. @VisibleForTesting final FingerprintSensorPropertiesInternal mSensorProps; @@ -299,7 +303,9 @@ public class UdfpsController implements DozeReceiver, HbmCallback { WindowManager windowManager, @NonNull StatusBarStateController statusBarStateController, @Main DelayableExecutor fgExecutor, - @NonNull StatusBar statusBar) { + @NonNull StatusBar statusBar, + @NonNull StatusBarKeyguardViewManager statusBarKeyguardViewManager, + @NonNull DumpManager dumpManager) { mContext = context; mInflater = inflater; // The fingerprint manager is queried for UDFPS before this class is constructed, so the @@ -309,6 +315,8 @@ public class UdfpsController implements DozeReceiver, HbmCallback { mFgExecutor = fgExecutor; mStatusBar = statusBar; mStatusBarStateController = statusBarStateController; + mKeyguardViewManager = statusBarKeyguardViewManager; + mDumpManager = dumpManager; mSensorProps = findFirstUdfps(); // At least one UDFPS sensor exists @@ -457,7 +465,8 @@ public class UdfpsController implements DozeReceiver, HbmCallback { enrollView, mServerRequest.mEnrollHelper, mStatusBarStateController, - mStatusBar + mStatusBar, + mDumpManager ); case IUdfpsOverlayController.REASON_AUTH_FPM_KEYGUARD: UdfpsKeyguardView keyguardView = (UdfpsKeyguardView) @@ -466,7 +475,9 @@ public class UdfpsController implements DozeReceiver, HbmCallback { return new UdfpsKeyguardViewController( keyguardView, mStatusBarStateController, - mStatusBar + mStatusBar, + mKeyguardViewManager, + mDumpManager ); case IUdfpsOverlayController.REASON_AUTH_BP: // note: empty controller, currently shows no visual affordance @@ -475,7 +486,8 @@ public class UdfpsController implements DozeReceiver, HbmCallback { return new UdfpsBpViewController( bpView, mStatusBarStateController, - mStatusBar + mStatusBar, + mDumpManager ); case IUdfpsOverlayController.REASON_AUTH_FPM_OTHER: UdfpsFpmOtherView authOtherView = (UdfpsFpmOtherView) @@ -484,7 +496,8 @@ public class UdfpsController implements DozeReceiver, HbmCallback { return new UdfpsFpmOtherViewController( authOtherView, mStatusBarStateController, - mStatusBar + mStatusBar, + mDumpManager ); default: Log.d(TAG, "Animation for reason " + reason + " not supported yet"); diff --git a/packages/SystemUI/src/com/android/systemui/biometrics/UdfpsDrawable.java b/packages/SystemUI/src/com/android/systemui/biometrics/UdfpsDrawable.java index 13d31cb87fdc..18f54166ad28 100644 --- a/packages/SystemUI/src/com/android/systemui/biometrics/UdfpsDrawable.java +++ b/packages/SystemUI/src/com/android/systemui/biometrics/UdfpsDrawable.java @@ -61,12 +61,14 @@ public abstract class UdfpsDrawable extends Drawable { */ protected void updateFingerprintIconBounds(@NonNull Rect bounds) { mFingerprintDrawable.setBounds(bounds); + invalidateSelf(); } @Override public void setAlpha(int alpha) { mAlpha = alpha; mFingerprintDrawable.setAlpha(mAlpha); + invalidateSelf(); } boolean isIlluminationShowing() { @@ -74,7 +76,11 @@ public abstract class UdfpsDrawable extends Drawable { } void setIlluminationShowing(boolean showing) { + if (mIlluminationShowing == showing) { + return; + } mIlluminationShowing = showing; + invalidateSelf(); } @Override diff --git a/packages/SystemUI/src/com/android/systemui/biometrics/UdfpsEnrollViewController.java b/packages/SystemUI/src/com/android/systemui/biometrics/UdfpsEnrollViewController.java index da8d712ebbdc..1ebbfbf84814 100644 --- a/packages/SystemUI/src/com/android/systemui/biometrics/UdfpsEnrollViewController.java +++ b/packages/SystemUI/src/com/android/systemui/biometrics/UdfpsEnrollViewController.java @@ -21,6 +21,7 @@ import android.graphics.PointF; import android.view.View; import com.android.systemui.R; +import com.android.systemui.dump.DumpManager; import com.android.systemui.plugins.statusbar.StatusBarStateController; import com.android.systemui.statusbar.phone.StatusBar; @@ -32,17 +33,23 @@ public class UdfpsEnrollViewController extends UdfpsAnimationViewController<Udfp @NonNull private final UdfpsEnrollHelper mEnrollHelper; protected UdfpsEnrollViewController( - UdfpsEnrollView view, + @NonNull UdfpsEnrollView view, @NonNull UdfpsEnrollHelper enrollHelper, - StatusBarStateController statusBarStateController, - StatusBar statusBar) { - super(view, statusBarStateController, statusBar); + @NonNull StatusBarStateController statusBarStateController, + @NonNull StatusBar statusBar, + @NonNull DumpManager dumpManager) { + super(view, statusBarStateController, statusBar, dumpManager); mEnrollHelper = enrollHelper; mProgressBar = mView.findViewById(R.id.progress_bar); mView.setEnrollHelper(mEnrollHelper); } @Override + @NonNull String getTag() { + return "UdfpsEnrollViewController"; + } + + @Override protected void onViewAttached() { super.onViewAttached(); if (mEnrollHelper.shouldShowProgressBar()) { diff --git a/packages/SystemUI/src/com/android/systemui/biometrics/UdfpsFpmOtherViewController.java b/packages/SystemUI/src/com/android/systemui/biometrics/UdfpsFpmOtherViewController.java index 587501bd1aa5..6e2e4baf492b 100644 --- a/packages/SystemUI/src/com/android/systemui/biometrics/UdfpsFpmOtherViewController.java +++ b/packages/SystemUI/src/com/android/systemui/biometrics/UdfpsFpmOtherViewController.java @@ -16,6 +16,9 @@ package com.android.systemui.biometrics; +import android.annotation.NonNull; + +import com.android.systemui.dump.DumpManager; import com.android.systemui.plugins.statusbar.StatusBarStateController; import com.android.systemui.statusbar.phone.StatusBar; @@ -27,9 +30,15 @@ import com.android.systemui.statusbar.phone.StatusBar; */ class UdfpsFpmOtherViewController extends UdfpsAnimationViewController<UdfpsFpmOtherView> { protected UdfpsFpmOtherViewController( - UdfpsFpmOtherView view, - StatusBarStateController statusBarStateController, - StatusBar statusBar) { - super(view, statusBarStateController, statusBar); + @NonNull UdfpsFpmOtherView view, + @NonNull StatusBarStateController statusBarStateController, + @NonNull StatusBar statusBar, + @NonNull DumpManager dumpManager) { + super(view, statusBarStateController, statusBar, dumpManager); + } + + @Override + @NonNull String getTag() { + return "UdfpsFpmOtherViewController"; } } diff --git a/packages/SystemUI/src/com/android/systemui/biometrics/UdfpsKeyguardDrawable.java b/packages/SystemUI/src/com/android/systemui/biometrics/UdfpsKeyguardDrawable.java index b0c5da09d916..12c15a0882f9 100644 --- a/packages/SystemUI/src/com/android/systemui/biometrics/UdfpsKeyguardDrawable.java +++ b/packages/SystemUI/src/com/android/systemui/biometrics/UdfpsKeyguardDrawable.java @@ -36,14 +36,14 @@ import com.android.systemui.doze.DozeReceiver; public class UdfpsKeyguardDrawable extends UdfpsDrawable implements DozeReceiver { private static final String TAG = "UdfpsAnimationKeyguard"; - private final int mLockScreenColor; private final int mAmbientDisplayColor; @NonNull private final Context mContext; - private final int mMaxBurnInOffsetX; - private final int mMaxBurnInOffsetY; + private int mLockScreenColor; // AOD anti-burn-in offsets + private final int mMaxBurnInOffsetX; + private final int mMaxBurnInOffsetY; private float mInterpolatedDarkAmount; private float mBurnInOffsetX; private float mBurnInOffsetY; @@ -95,4 +95,10 @@ public class UdfpsKeyguardDrawable extends UdfpsDrawable implements DozeReceiver mInterpolatedDarkAmount = eased; updateAodPositionAndColor(); } + + void setLockScreenColor(int color) { + if (mLockScreenColor == color) return; + mLockScreenColor = color; + updateAodPositionAndColor(); + } } diff --git a/packages/SystemUI/src/com/android/systemui/biometrics/UdfpsKeyguardView.java b/packages/SystemUI/src/com/android/systemui/biometrics/UdfpsKeyguardView.java index 6a9356034d22..a78c223bf883 100644 --- a/packages/SystemUI/src/com/android/systemui/biometrics/UdfpsKeyguardView.java +++ b/packages/SystemUI/src/com/android/systemui/biometrics/UdfpsKeyguardView.java @@ -16,13 +16,23 @@ package com.android.systemui.biometrics; +import android.animation.Animator; +import android.animation.AnimatorListenerAdapter; +import android.animation.AnimatorSet; +import android.animation.ArgbEvaluator; +import android.animation.ObjectAnimator; +import android.animation.ValueAnimator; import android.content.Context; import android.util.AttributeSet; +import android.view.View; import android.widget.ImageView; import androidx.annotation.Nullable; +import com.android.settingslib.Utils; +import com.android.systemui.Interpolators; import com.android.systemui.R; +import com.android.systemui.statusbar.StatusBarState; /** * View corresponding with udfps_keyguard_view.xml @@ -30,6 +40,14 @@ import com.android.systemui.R; public class UdfpsKeyguardView extends UdfpsAnimationView { private final UdfpsKeyguardDrawable mFingerprintDrawable; private ImageView mFingerprintView; + private int mWallpaperTexColor; + private int mStatusBarState; + + // used when highlighting fp icon: + private int mTextColorPrimary; + private ImageView mBgProtection; + + private AnimatorSet mAnimatorSet; public UdfpsKeyguardView(Context context, @Nullable AttributeSet attrs) { super(context, attrs); @@ -38,8 +56,15 @@ public class UdfpsKeyguardView extends UdfpsAnimationView { @Override protected void onFinishInflate() { + super.onFinishInflate(); mFingerprintView = findViewById(R.id.udfps_keyguard_animation_fp_view); - mFingerprintView.setImageDrawable(mFingerprintDrawable); + mFingerprintView.setForeground(mFingerprintDrawable); + + mBgProtection = findViewById(R.id.udfps_keyguard_fp_bg); + + mWallpaperTexColor = Utils.getColorAttrDefaultColor(mContext, R.attr.wallpaperTextColor); + mTextColorPrimary = Utils.getColorAttrDefaultColor(mContext, + android.R.attr.textColorPrimary); } @Override @@ -54,7 +79,114 @@ public class UdfpsKeyguardView extends UdfpsAnimationView { return true; } + void setStatusBarState(int statusBarState) { + mStatusBarState = statusBarState; + if (!isShadeLocked()) { + mFingerprintView.setAlpha(1f); + mFingerprintDrawable.setLockScreenColor(mWallpaperTexColor); + } + } + void onDozeAmountChanged(float linear, float eased) { mFingerprintDrawable.onDozeAmountChanged(linear, eased); + invalidate(); + } + + /** + * Animates in the bg protection circle behind the fp icon to highlight the icon. + */ + void animateHighlightFp() { + if (mBgProtection.getVisibility() == View.VISIBLE && mBgProtection.getAlpha() == 1f) { + // already fully highlighted, don't re-animate + return; + } + + if (mAnimatorSet != null) { + mAnimatorSet.cancel(); + } + ValueAnimator fpIconAnim; + if (isShadeLocked()) { + // set color and fade in since we weren't showing before + mFingerprintDrawable.setLockScreenColor(mTextColorPrimary); + fpIconAnim = ObjectAnimator.ofFloat(mFingerprintView, View.ALPHA, 0f, 1f); + } else { + // update icon color + fpIconAnim = new ValueAnimator(); + fpIconAnim.setIntValues(mWallpaperTexColor, mTextColorPrimary); + fpIconAnim.setEvaluator(new ArgbEvaluator()); + fpIconAnim.addUpdateListener(valueAnimator -> mFingerprintDrawable.setLockScreenColor( + (Integer) valueAnimator.getAnimatedValue())); + } + + mAnimatorSet = new AnimatorSet(); + mAnimatorSet.setInterpolator(Interpolators.FAST_OUT_SLOW_IN); + mAnimatorSet.setDuration(500); + mAnimatorSet.addListener(new AnimatorListenerAdapter() { + @Override + public void onAnimationStart(Animator animation) { + mBgProtection.setVisibility(View.VISIBLE); + } + }); + + mAnimatorSet.playTogether( + ObjectAnimator.ofFloat(mBgProtection, View.ALPHA, 0f, 1f), + ObjectAnimator.ofFloat(mBgProtection, View.SCALE_X, 0f, 1f), + ObjectAnimator.ofFloat(mBgProtection, View.SCALE_Y, 0f, 1f), + fpIconAnim); + mAnimatorSet.start(); + } + + private boolean isShadeLocked() { + return mStatusBarState == StatusBarState.SHADE_LOCKED; + } + + /** + * Animates out the bg protection circle behind the fp icon to unhighlight the icon. + */ + void animateUnhighlightFp(@Nullable Runnable onEndAnimation) { + if (mBgProtection.getVisibility() == View.GONE) { + // already hidden + return; + } + + if (mAnimatorSet != null) { + mAnimatorSet.cancel(); + } + ValueAnimator fpIconAnim; + if (isShadeLocked()) { + // fade out + fpIconAnim = ObjectAnimator.ofFloat(mFingerprintView, View.ALPHA, 1f, 0f); + } else { + // update icon color + fpIconAnim = new ValueAnimator(); + fpIconAnim.setIntValues(mTextColorPrimary, mWallpaperTexColor); + fpIconAnim.setEvaluator(new ArgbEvaluator()); + fpIconAnim.addUpdateListener(valueAnimator -> mFingerprintDrawable.setLockScreenColor( + (Integer) valueAnimator.getAnimatedValue())); + } + + mAnimatorSet = new AnimatorSet(); + mAnimatorSet.playTogether( + ObjectAnimator.ofFloat(mBgProtection, View.ALPHA, 1f, 0f), + ObjectAnimator.ofFloat(mBgProtection, View.SCALE_X, 1f, 0f), + ObjectAnimator.ofFloat(mBgProtection, View.SCALE_Y, 1f, 0f), + fpIconAnim); + mAnimatorSet.setInterpolator(Interpolators.FAST_OUT_SLOW_IN); + mAnimatorSet.setDuration(500); + + mAnimatorSet.addListener(new AnimatorListenerAdapter() { + @Override + public void onAnimationEnd(Animator animation) { + mBgProtection.setVisibility(View.GONE); + if (onEndAnimation != null) { + onEndAnimation.run(); + } + } + }); + mAnimatorSet.start(); + } + + boolean isAnimating() { + return mAnimatorSet != null && mAnimatorSet.isRunning(); } } diff --git a/packages/SystemUI/src/com/android/systemui/biometrics/UdfpsKeyguardViewController.java b/packages/SystemUI/src/com/android/systemui/biometrics/UdfpsKeyguardViewController.java index 14bb3fee1174..08e5d562113d 100644 --- a/packages/SystemUI/src/com/android/systemui/biometrics/UdfpsKeyguardViewController.java +++ b/packages/SystemUI/src/com/android/systemui/biometrics/UdfpsKeyguardViewController.java @@ -16,34 +16,62 @@ package com.android.systemui.biometrics; +import android.annotation.NonNull; + +import com.android.systemui.dump.DumpManager; import com.android.systemui.plugins.statusbar.StatusBarStateController; import com.android.systemui.statusbar.phone.StatusBar; +import com.android.systemui.statusbar.phone.StatusBarKeyguardViewManager; + +import java.io.FileDescriptor; +import java.io.PrintWriter; /** * Class that coordinates non-HBM animations during keyguard authentication. */ public class UdfpsKeyguardViewController extends UdfpsAnimationViewController<UdfpsKeyguardView> { + @NonNull private final StatusBarKeyguardViewManager mKeyguardViewManager; + private boolean mForceShow; protected UdfpsKeyguardViewController( - UdfpsKeyguardView view, - StatusBarStateController statusBarStateController, - StatusBar statusBar) { - super(view, statusBarStateController, statusBar); + @NonNull UdfpsKeyguardView view, + @NonNull StatusBarStateController statusBarStateController, + @NonNull StatusBar statusBar, + @NonNull StatusBarKeyguardViewManager statusBarKeyguardViewManager, + @NonNull DumpManager dumpManager) { + super(view, statusBarStateController, statusBar, dumpManager); + mKeyguardViewManager = statusBarKeyguardViewManager; + } + + @Override + @NonNull String getTag() { + return "UdfpsKeyguardViewController"; } @Override protected void onViewAttached() { super.onViewAttached(); - mStatusBarStateController.addCallback(mStateListener); + final float dozeAmount = mStatusBarStateController.getDozeAmount(); + mStatusBarStateController.addCallback(mStateListener); mStateListener.onDozeAmountChanged(dozeAmount, dozeAmount); + mStateListener.onStateChanged(mStatusBarStateController.getState()); + mKeyguardViewManager.setAlternateAuthInterceptor(mAlternateAuthInterceptor); } @Override protected void onViewDetached() { super.onViewDetached(); mStatusBarStateController.removeCallback(mStateListener); + mAlternateAuthInterceptor.reset(); + mKeyguardViewManager.setAlternateAuthInterceptor(null); + } + + @Override + public void dump(FileDescriptor fd, PrintWriter pw, String[] args) { + super.dump(fd, pw, args); + pw.println("mForceShow=" + mForceShow); } /** @@ -56,7 +84,11 @@ public class UdfpsKeyguardViewController extends UdfpsAnimationViewController<Ud mForceShow = forceShow; updatePauseAuth(); - // TODO: animate show/hide background protection + if (mForceShow) { + mView.animateHighlightFp(); + } else { + mView.animateUnhighlightFp(() -> mKeyguardViewManager.cancelPostAuthActions()); + } } /** @@ -76,6 +108,50 @@ public class UdfpsKeyguardViewController extends UdfpsAnimationViewController<Ud @Override public void onDozeAmountChanged(float linear, float eased) { mView.onDozeAmountChanged(linear, eased); + if (linear != 0) forceShow(false); + } + + @Override + public void onStateChanged(int statusBarState) { + mView.setStatusBarState(statusBarState); } }; + + private final StatusBarKeyguardViewManager.AlternateAuthInterceptor mAlternateAuthInterceptor = + new StatusBarKeyguardViewManager.AlternateAuthInterceptor() { + @Override + public boolean showAlternativeAuthMethod() { + if (mForceShow) { + return false; + } + + forceShow(true); + return true; + } + + @Override + public boolean reset() { + if (!mForceShow) { + return false; + } + + forceShow(false); + return true; + } + + @Override + public boolean isShowingAlternativeAuth() { + return mForceShow; + } + + @Override + public boolean isAnimating() { + return mView.isAnimating(); + } + + @Override + public void dump(PrintWriter pw) { + pw.print(getTag()); + } + }; } diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/NotificationPanelViewController.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/NotificationPanelViewController.java index ca6e53d2ec04..acb3e5783525 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/NotificationPanelViewController.java +++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/NotificationPanelViewController.java @@ -3386,6 +3386,9 @@ public class NotificationPanelViewController extends PanelViewController { return new TouchHandler() { @Override public boolean onInterceptTouchEvent(MotionEvent event) { + if (mStatusBarKeyguardViewManager.isShowingAlternativeAuthOrAnimating()) { + return true; + } if (mBlockTouches || mQsFullyExpanded && mQs.disallowPanelTouches()) { return false; } @@ -3413,6 +3416,12 @@ public class NotificationPanelViewController extends PanelViewController { @Override public boolean onTouch(View v, MotionEvent event) { + final boolean showingOrAnimatingAltAuth = + mStatusBarKeyguardViewManager.isShowingAlternativeAuthOrAnimating(); + if (showingOrAnimatingAltAuth) { + mStatusBarKeyguardViewManager.resetAlternateAuth(); + } + if (mBlockTouches || (mQsFullyExpanded && mQs != null && mQs.disallowPanelTouches())) { return false; @@ -3464,7 +3473,7 @@ public class NotificationPanelViewController extends PanelViewController { handled = true; } handled |= super.onTouch(v, event); - return !mDozing || mPulsing || handled; + return !mDozing || mPulsing || handled || showingOrAnimatingAltAuth; } }; } diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/PanelViewController.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/PanelViewController.java index 3ac69378d7d1..26d1afe35452 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/PanelViewController.java +++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/PanelViewController.java @@ -453,7 +453,8 @@ public abstract class PanelViewController { // We need to collapse the panel since we peeked to the small height. mView.postOnAnimation(mPostCollapseRunnable); } - } else if (!mStatusBar.isBouncerShowing()) { + } else if (!mStatusBar.isBouncerShowing() + && !mStatusBarKeyguardViewManager.isShowingAlternativeAuthOrAnimating()) { boolean expands = onEmptySpaceClick(mInitialTouchX); onTrackingStopped(expands); } diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/ScrimState.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/ScrimState.java index 9b8b7160c95c..b82863e75fe6 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/ScrimState.java +++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/ScrimState.java @@ -88,6 +88,17 @@ public enum ScrimState { } }, + AUTH_SCRIMMED { + @Override + public void prepare(ScrimState previousState) { + mFrontTint = Color.BLACK; + + mBehindAlpha = 0f; + mFrontAlpha = .66f; + mBubbleAlpha = 0f; + } + }, + /** * Showing password challenge on the keyguard. */ diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBar.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBar.java index 117921dd860c..74707cf086de 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBar.java +++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBar.java @@ -4147,7 +4147,7 @@ public class StatusBar extends SystemUI implements DemoMode, } @VisibleForTesting - void updateScrimController() { + public void updateScrimController() { Trace.beginSection("StatusBar#updateScrimController"); // We don't want to end up in KEYGUARD state when we're unlocking with @@ -4163,7 +4163,9 @@ public class StatusBar extends SystemUI implements DemoMode, mNotificationPanelViewController.isLaunchingAffordanceWithPreview(); mScrimController.setLaunchingAffordanceWithPreview(launchingAffordanceWithPreview); - if (mBouncerShowing) { + if (mStatusBarKeyguardViewManager.isShowingAlternativeAuth()) { + mScrimController.transitionTo(ScrimState.AUTH_SCRIMMED); + } else if (mBouncerShowing) { // Bouncer needs the front scrim when it's on top of an activity, // tapping on a notification, editing QS or being dismissed by // FLAG_DISMISS_KEYGUARD_ACTIVITY. diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarKeyguardViewManager.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarKeyguardViewManager.java index 01ada0f4c86c..81e24cc25aa6 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarKeyguardViewManager.java +++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarKeyguardViewManager.java @@ -38,6 +38,7 @@ import android.view.ViewRootImpl; import android.view.WindowManagerGlobal; import android.widget.FrameLayout; +import androidx.annotation.Nullable; import androidx.annotation.VisibleForTesting; import com.android.internal.util.LatencyTracker; @@ -199,6 +200,7 @@ public class StatusBarKeyguardViewManager implements RemoteInputController.Callb private final DockManager mDockManager; private final KeyguardUpdateMonitor mKeyguardUpdateManager; private KeyguardBypassController mBypassController; + @Nullable private AlternateAuthInterceptor mAlternateAuthInterceptor; private final KeyguardUpdateMonitorCallback mUpdateMonitorCallback = new KeyguardUpdateMonitorCallback() { @@ -271,6 +273,10 @@ public class StatusBarKeyguardViewManager implements RemoteInputController.Callb registerListeners(); } + public void setAlternateAuthInterceptor(@Nullable AlternateAuthInterceptor authInterceptor) { + mAlternateAuthInterceptor = authInterceptor; + } + private void registerListeners() { mKeyguardUpdateManager.registerCallback(mUpdateMonitorCallback); mStatusBarStateController.addCallback(this); @@ -434,11 +440,7 @@ public class StatusBarKeyguardViewManager implements RemoteInputController.Callb if (mShowing) { // If we were showing the bouncer and then aborting, we need to also clear out any // potential actions unless we actually unlocked. - mAfterKeyguardGoneAction = null; - if (mKeyguardGoneCancelAction != null) { - mKeyguardGoneCancelAction.run(); - mKeyguardGoneCancelAction = null; - } + cancelPostAuthActions(); } mBouncer.hide(destroyView); cancelPendingWakeupAction(); @@ -474,6 +476,14 @@ public class StatusBarKeyguardViewManager implements RemoteInputController.Callb return; } + if (mAlternateAuthInterceptor != null + && mAlternateAuthInterceptor.showAlternativeAuthMethod()) { + mStatusBar.updateScrimController(); + mAfterKeyguardGoneAction = r; + mKeyguardGoneCancelAction = cancelAction; + return; + } + if (!afterKeyguardGone) { mBouncer.showWithDismissAction(r, cancelAction); } else { @@ -508,11 +518,21 @@ public class StatusBarKeyguardViewManager implements RemoteInputController.Callb } else { showBouncerOrKeyguard(hideBouncerWhenShowing); } + resetAlternateAuth(); mKeyguardUpdateManager.sendKeyguardReset(); updateStates(); } } + /** + * Stop showing any alternate auth methods + */ + public void resetAlternateAuth() { + if (mAlternateAuthInterceptor != null && mAlternateAuthInterceptor.reset()) { + mStatusBar.updateScrimController(); + } + } + @Override public void onStartedWakingUp() { mStatusBar.getNotificationShadeWindowView().getWindowInsetsController() @@ -834,6 +854,20 @@ public class StatusBarKeyguardViewManager implements RemoteInputController.Callb return mBouncer.isFullscreenBouncer(); } + /** + * Clear out any potential actions that were saved to run when the device is unlocked + */ + public void cancelPostAuthActions() { + if (bouncerIsOrWillBeShowing()) { + return; // allow bouncer to trigger saved actions + } + mAfterKeyguardGoneAction = null; + if (mKeyguardGoneCancelAction != null) { + mKeyguardGoneCancelAction.run(); + mKeyguardGoneCancelAction = null; + } + } + private long getNavBarShowDelay() { if (mKeyguardStateController.isKeyguardFadingAway()) { return mKeyguardStateController.getKeyguardFadingAwayDelay(); @@ -1063,6 +1097,11 @@ public class StatusBarKeyguardViewManager implements RemoteInputController.Callb if (mBouncer != null) { mBouncer.dump(pw); } + + if (mAlternateAuthInterceptor != null) { + pw.println("AltAuthInterceptor: "); + mAlternateAuthInterceptor.dump(pw); + } } @Override @@ -1079,6 +1118,17 @@ public class StatusBarKeyguardViewManager implements RemoteInputController.Callb return mBouncer; } + public boolean isShowingAlternativeAuth() { + return mAlternateAuthInterceptor != null + && mAlternateAuthInterceptor.isShowingAlternativeAuth(); + } + + public boolean isShowingAlternativeAuthOrAnimating() { + return mAlternateAuthInterceptor != null + && (mAlternateAuthInterceptor.isShowingAlternativeAuth() + || mAlternateAuthInterceptor.isAnimating()); + } + private static class DismissWithActionRequest { final OnDismissAction dismissAction; final Runnable cancelAction; @@ -1093,4 +1143,36 @@ public class StatusBarKeyguardViewManager implements RemoteInputController.Callb this.message = message; } } + + /** + * Delegate used to send show/reset events to an alternate authentication method instead of the + * bouncer. + */ + public interface AlternateAuthInterceptor { + /** + * @return whether alternative auth method was newly shown + */ + boolean showAlternativeAuthMethod(); + + /** + * reset the state to the default (only keyguard showing, no auth methods showing) + * @return whether alternative auth method was newly hidden + */ + boolean reset(); + + /** + * @return true if alternative auth method is showing + */ + boolean isShowingAlternativeAuth(); + + /** + * print information for the alternate auth interceptor registered + */ + void dump(PrintWriter pw); + + /** + * @return true if the new auth method is currently animating in or out. + */ + boolean isAnimating(); + } } diff --git a/packages/SystemUI/tests/src/com/android/systemui/biometrics/UdfpsControllerTest.java b/packages/SystemUI/tests/src/com/android/systemui/biometrics/UdfpsControllerTest.java index d6f4958942dd..be110fcf70ac 100644 --- a/packages/SystemUI/tests/src/com/android/systemui/biometrics/UdfpsControllerTest.java +++ b/packages/SystemUI/tests/src/com/android/systemui/biometrics/UdfpsControllerTest.java @@ -44,8 +44,10 @@ import androidx.test.filters.SmallTest; import com.android.systemui.R; import com.android.systemui.SysuiTestCase; +import com.android.systemui.dump.DumpManager; import com.android.systemui.plugins.statusbar.StatusBarStateController; import com.android.systemui.statusbar.phone.StatusBar; +import com.android.systemui.statusbar.phone.StatusBarKeyguardViewManager; import com.android.systemui.util.concurrency.FakeExecutor; import com.android.systemui.util.time.FakeSystemClock; @@ -91,6 +93,10 @@ public class UdfpsControllerTest extends SysuiTestCase { @Mock private StatusBar mStatusBar; @Mock + private StatusBarKeyguardViewManager mStatusBarKeyguardViewManager; + @Mock + private DumpManager mDumpManager; + @Mock private IUdfpsOverlayControllerCallback mUdfpsOverlayControllerCallback; private FakeExecutor mFgExecutor; @@ -129,7 +135,9 @@ public class UdfpsControllerTest extends SysuiTestCase { mWindowManager, mStatusBarStateController, mFgExecutor, - mStatusBar); + mStatusBar, + mStatusBarKeyguardViewManager, + mDumpManager); verify(mFingerprintManager).setUdfpsOverlayController(mOverlayCaptor.capture()); mOverlayController = mOverlayCaptor.getValue(); diff --git a/packages/SystemUI/tests/src/com/android/systemui/biometrics/UdfpsKeyguardViewControllerTest.java b/packages/SystemUI/tests/src/com/android/systemui/biometrics/UdfpsKeyguardViewControllerTest.java index 480b33556b27..65f0f7b87cf4 100644 --- a/packages/SystemUI/tests/src/com/android/systemui/biometrics/UdfpsKeyguardViewControllerTest.java +++ b/packages/SystemUI/tests/src/com/android/systemui/biometrics/UdfpsKeyguardViewControllerTest.java @@ -16,6 +16,8 @@ package com.android.systemui.biometrics; +import static org.junit.Assert.assertFalse; +import static org.junit.Assert.assertTrue; import static org.mockito.Mockito.times; import static org.mockito.Mockito.verify; import static org.mockito.Mockito.when; @@ -26,9 +28,11 @@ import android.testing.TestableLooper.RunWithLooper; import androidx.test.filters.SmallTest; import com.android.systemui.SysuiTestCase; +import com.android.systemui.dump.DumpManager; import com.android.systemui.plugins.statusbar.StatusBarStateController; import com.android.systemui.statusbar.StatusBarState; import com.android.systemui.statusbar.phone.StatusBar; +import com.android.systemui.statusbar.phone.StatusBarKeyguardViewManager; import org.junit.Before; import org.junit.Test; @@ -51,24 +55,34 @@ public class UdfpsKeyguardViewControllerTest extends SysuiTestCase { private StatusBarStateController mStatusBarStateController; @Mock private StatusBar mStatusBar; + @Mock + private StatusBarKeyguardViewManager mStatusBarKeyguardViewManager; + @Mock + private DumpManager mDumpManager; private UdfpsKeyguardViewController mController; // Capture listeners so that they can be used to send events @Captor private ArgumentCaptor<StatusBarStateController.StateListener> mStateListenerCaptor; - private StatusBarStateController.StateListener mParentListener; - private StatusBarStateController.StateListener mDozeListener; + private StatusBarStateController.StateListener mParentStatusBarStateListener; + private StatusBarStateController.StateListener mStatusBarStateListener; @Captor private ArgumentCaptor<StatusBar.ExpansionChangedListener> mExpansionListenerCaptor; private StatusBar.ExpansionChangedListener mExpansionListener; + @Captor private ArgumentCaptor<StatusBarKeyguardViewManager.AlternateAuthInterceptor> + mAltAuthInterceptorCaptor; + private StatusBarKeyguardViewManager.AlternateAuthInterceptor mAltAuthInterceptor; + @Before public void setUp() { MockitoAnnotations.initMocks(this); mController = new UdfpsKeyguardViewController( mView, mStatusBarStateController, - mStatusBar); + mStatusBar, + mStatusBarKeyguardViewManager, + mDumpManager); } @Test @@ -86,7 +100,7 @@ public class UdfpsKeyguardViewControllerTest extends SysuiTestCase { @Test public void testViewControllerQueriesSBStateOnAttached() { mController.onViewAttached(); - verify(mStatusBarStateController).getState(); + verify(mStatusBarStateController, times(2)).getState(); verify(mStatusBarStateController).getDozeAmount(); final float dozeAmount = .88f; @@ -106,8 +120,8 @@ public class UdfpsKeyguardViewControllerTest extends SysuiTestCase { captureExpansionListener(); mController.onViewDetached(); - verify(mStatusBarStateController).removeCallback(mParentListener); - verify(mStatusBarStateController).removeCallback(mDozeListener); + verify(mStatusBarStateController).removeCallback(mParentStatusBarStateListener); + verify(mStatusBarStateController).removeCallback(mStatusBarStateListener); verify(mStatusBar).removeExpansionChangedListener(mExpansionListener); } @@ -118,21 +132,88 @@ public class UdfpsKeyguardViewControllerTest extends SysuiTestCase { final float linear = .55f; final float eased = .65f; - mDozeListener.onDozeAmountChanged(linear, eased); + mStatusBarStateListener.onDozeAmountChanged(linear, eased); verify(mView).onDozeAmountChanged(linear, eased); } + @Test + public void testShouldNotPauseAuthOnKeyguard() { + mController.onViewAttached(); + captureStatusBarStateListeners(); + captureExpansionListener(); + + sendStatusBarStateChanged(StatusBarState.KEYGUARD); + + assertFalse(mController.shouldPauseAuth()); + } + + @Test + public void testShouldPauseAuthOnShadeLocked() { + mController.onViewAttached(); + captureStatusBarStateListeners(); + captureExpansionListener(); + + sendStatusBarStateChanged(StatusBarState.SHADE_LOCKED); + + assertTrue(mController.shouldPauseAuth()); + } + + @Test + public void testOverrideShouldPauseAuthOnShadeLocked() { + mController.onViewAttached(); + captureStatusBarStateListeners(); + captureAltAuthInterceptor(); + + sendStatusBarStateChanged(StatusBarState.SHADE_LOCKED); + assertTrue(mController.shouldPauseAuth()); + + mAltAuthInterceptor.showAlternativeAuthMethod(); // force show + assertFalse(mController.shouldPauseAuth()); + assertTrue(mAltAuthInterceptor.isShowingAlternativeAuth()); + + mAltAuthInterceptor.reset(); // stop force show + assertTrue(mController.shouldPauseAuth()); + assertFalse(mAltAuthInterceptor.isShowingAlternativeAuth()); + } + + @Test + public void testOnDetachedStateReset() { + // GIVEN view is attached, alt auth is force being shown + mController.onViewAttached(); + captureStatusBarStateListeners(); + captureAltAuthInterceptor(); + + mAltAuthInterceptor.showAlternativeAuthMethod(); // alt auth force show + + // WHEN view is detached + mController.onViewDetached(); + + // THEN alt auth state reports not showing + assertFalse(mAltAuthInterceptor.isShowingAlternativeAuth()); + } + + private void sendStatusBarStateChanged(int statusBarState) { + mStatusBarStateListener.onStateChanged(statusBarState); + mParentStatusBarStateListener.onStateChanged(statusBarState); + } + private void captureStatusBarStateListeners() { verify(mStatusBarStateController, times(2)).addCallback(mStateListenerCaptor.capture()); List<StatusBarStateController.StateListener> stateListeners = mStateListenerCaptor.getAllValues(); - mParentListener = stateListeners.get(0); - mDozeListener = stateListeners.get(1); + mParentStatusBarStateListener = stateListeners.get(0); + mStatusBarStateListener = stateListeners.get(1); } private void captureExpansionListener() { verify(mStatusBar).addExpansionChangedListener(mExpansionListenerCaptor.capture()); mExpansionListener = mExpansionListenerCaptor.getValue(); } + + private void captureAltAuthInterceptor() { + verify(mStatusBarKeyguardViewManager).setAlternateAuthInterceptor( + mAltAuthInterceptorCaptor.capture()); + mAltAuthInterceptor = mAltAuthInterceptorCaptor.getValue(); + } } |