diff --git a/packages/SystemUI/src/com/android/systemui/biometrics/UdfpsAnimation.java b/packages/SystemUI/src/com/android/systemui/biometrics/UdfpsAnimation.java index 5fc2e5361689d..40fe7b17f37d5 100644 --- a/packages/SystemUI/src/com/android/systemui/biometrics/UdfpsAnimation.java +++ b/packages/SystemUI/src/com/android/systemui/biometrics/UdfpsAnimation.java @@ -47,4 +47,9 @@ public abstract class UdfpsAnimation extends Drawable { (int) sensorRect.right - margin, (int) sensorRect.bottom - margin); } + + @Override + public void setAlpha(int alpha) { + mFingerprintDrawable.setAlpha(alpha); + } } diff --git a/packages/SystemUI/src/com/android/systemui/biometrics/UdfpsAnimationEnroll.java b/packages/SystemUI/src/com/android/systemui/biometrics/UdfpsAnimationEnroll.java index 1a2a4927c3fd8..8e6cd5d2b97fe 100644 --- a/packages/SystemUI/src/com/android/systemui/biometrics/UdfpsAnimationEnroll.java +++ b/packages/SystemUI/src/com/android/systemui/biometrics/UdfpsAnimationEnroll.java @@ -27,6 +27,8 @@ import android.graphics.RectF; import androidx.annotation.NonNull; import androidx.annotation.Nullable; +import com.android.internal.graphics.ColorUtils; +import com.android.settingslib.Utils; import com.android.systemui.R; /** @@ -35,8 +37,11 @@ import com.android.systemui.R; public class UdfpsAnimationEnroll extends UdfpsAnimation { private static final String TAG = "UdfpsAnimationEnroll"; + private static final float SHADOW_RADIUS = 5.f; + @Nullable private RectF mSensorRect; @NonNull private final Paint mSensorPaint; + private final int mNotificationShadeColor; UdfpsAnimationEnroll(@NonNull Context context) { super(context); @@ -44,8 +49,11 @@ public class UdfpsAnimationEnroll extends UdfpsAnimation { mSensorPaint = new Paint(0 /* flags */); mSensorPaint.setAntiAlias(true); mSensorPaint.setColor(Color.WHITE); - mSensorPaint.setShadowLayer(UdfpsView.SENSOR_SHADOW_RADIUS, 0, 0, Color.BLACK); + mSensorPaint.setShadowLayer(SHADOW_RADIUS, 0, 0, Color.BLACK); mSensorPaint.setStyle(Paint.Style.FILL); + + mNotificationShadeColor = Utils.getColorAttr(context, + android.R.attr.colorBackgroundFloating).getDefaultColor(); } @Override @@ -73,7 +81,14 @@ public class UdfpsAnimationEnroll extends UdfpsAnimation { @Override public void setAlpha(int alpha) { + super.setAlpha(alpha); + // Gradually fade into the notification shade color. This needs to be done because the + // UDFPS view is drawn on a layer on top of the notification shade + final float percent = alpha / 255.f; + mSensorPaint.setColor(ColorUtils.blendARGB(mNotificationShadeColor, Color.WHITE, percent)); + mSensorPaint.setShadowLayer(SHADOW_RADIUS, 0, 0, + ColorUtils.blendARGB(mNotificationShadeColor, Color.BLACK, percent)); } @Override diff --git a/packages/SystemUI/src/com/android/systemui/biometrics/UdfpsAnimationFpmOther.java b/packages/SystemUI/src/com/android/systemui/biometrics/UdfpsAnimationFpmOther.java index 7563e739c93a8..efc864ade5ff5 100644 --- a/packages/SystemUI/src/com/android/systemui/biometrics/UdfpsAnimationFpmOther.java +++ b/packages/SystemUI/src/com/android/systemui/biometrics/UdfpsAnimationFpmOther.java @@ -43,11 +43,6 @@ public class UdfpsAnimationFpmOther extends UdfpsAnimation { mFingerprintDrawable.draw(canvas); } - @Override - public void setAlpha(int alpha) { - - } - @Override public void setColorFilter(@Nullable ColorFilter colorFilter) { diff --git a/packages/SystemUI/src/com/android/systemui/biometrics/UdfpsAnimationKeyguard.java b/packages/SystemUI/src/com/android/systemui/biometrics/UdfpsAnimationKeyguard.java index 3e46a656ecd5c..501de9df575bf 100644 --- a/packages/SystemUI/src/com/android/systemui/biometrics/UdfpsAnimationKeyguard.java +++ b/packages/SystemUI/src/com/android/systemui/biometrics/UdfpsAnimationKeyguard.java @@ -98,11 +98,6 @@ public class UdfpsAnimationKeyguard extends UdfpsAnimation implements DozeReceiv canvas.restore(); } - @Override - public void setAlpha(int alpha) { - - } - @Override public void setColorFilter(@Nullable ColorFilter colorFilter) { diff --git a/packages/SystemUI/src/com/android/systemui/biometrics/UdfpsController.java b/packages/SystemUI/src/com/android/systemui/biometrics/UdfpsController.java index a2b2bea8a06f8..5d75133b6bb68 100644 --- a/packages/SystemUI/src/com/android/systemui/biometrics/UdfpsController.java +++ b/packages/SystemUI/src/com/android/systemui/biometrics/UdfpsController.java @@ -50,6 +50,7 @@ import com.android.systemui.R; import com.android.systemui.dagger.qualifiers.Main; import com.android.systemui.doze.DozeReceiver; import com.android.systemui.plugins.statusbar.StatusBarStateController; +import com.android.systemui.statusbar.phone.ScrimController; import com.android.systemui.util.concurrency.DelayableExecutor; import com.android.systemui.util.settings.SystemSettings; @@ -174,7 +175,8 @@ class UdfpsController implements DozeReceiver { WindowManager windowManager, SystemSettings systemSettings, @NonNull StatusBarStateController statusBarStateController, - @Main DelayableExecutor fgExecutor) { + @Main DelayableExecutor fgExecutor, + @NonNull ScrimController scrimController) { mContext = context; // The fingerprint manager is queried for UDFPS before this class is constructed, so the // fingerprint manager should never be null. @@ -210,6 +212,8 @@ class UdfpsController implements DozeReceiver { mHbmSupported = !TextUtils.isEmpty(mHbmPath); mView.setHbmSupported(mHbmSupported); + scrimController.addScrimChangedListener(mView); + statusBarStateController.addCallback(mView); // This range only consists of the minimum and maximum values, which only cover // non-high-brightness mode. diff --git a/packages/SystemUI/src/com/android/systemui/biometrics/UdfpsView.java b/packages/SystemUI/src/com/android/systemui/biometrics/UdfpsView.java index 4cb8101385eaf..686c2784c7825 100644 --- a/packages/SystemUI/src/com/android/systemui/biometrics/UdfpsView.java +++ b/packages/SystemUI/src/com/android/systemui/biometrics/UdfpsView.java @@ -16,6 +16,10 @@ package com.android.systemui.biometrics; +import static com.android.systemui.statusbar.StatusBarState.FULLSCREEN_USER_SWITCHER; +import static com.android.systemui.statusbar.StatusBarState.KEYGUARD; +import static com.android.systemui.statusbar.StatusBarState.SHADE_LOCKED; + import android.annotation.NonNull; import android.annotation.Nullable; import android.content.Context; @@ -35,15 +39,19 @@ import android.view.ViewTreeObserver; import com.android.systemui.R; import com.android.systemui.doze.DozeReceiver; +import com.android.systemui.plugins.statusbar.StatusBarStateController; +import com.android.systemui.statusbar.StatusBarState; +import com.android.systemui.statusbar.phone.ScrimController; /** * A full screen view with a configurable illumination dot and scrim. */ -public class UdfpsView extends View implements DozeReceiver { +public class UdfpsView extends View implements DozeReceiver, + StatusBarStateController.StateListener, ScrimController.ScrimChangedListener{ private static final String TAG = "UdfpsView"; // Values in pixels. - public static final float SENSOR_SHADOW_RADIUS = 2.0f; + private static final float SENSOR_SHADOW_RADIUS = 2.0f; private static final int DEBUG_TEXT_SIZE_PX = 32; @@ -71,6 +79,9 @@ public class UdfpsView extends View implements DozeReceiver { private boolean mShowScrimAndDot; private boolean mIsHbmSupported; @Nullable private String mDebugMessage; + private int mStatusBarState; + private boolean mNotificationShadeExpanded; + private int mNotificationPanelAlpha; // Runnable that will be run after the illumination dot and scrim are shown. // The runnable is reset to null after it's executed once. @@ -137,6 +148,22 @@ public class UdfpsView extends View implements DozeReceiver { } } + @Override + public void onExpandedChanged(boolean isExpanded) { + mNotificationShadeExpanded = isExpanded; + } + + @Override + public void onStateChanged(int newState) { + mStatusBarState = newState; + } + + @Override + public void onAlphaChanged(float alpha) { + mNotificationPanelAlpha = (int) (alpha * 255); + postInvalidate(); + } + // The "h" and "w" are the display's height and width relative to its current rotation. protected void updateSensorRect(int h, int w) { // mSensorProps coordinates assume portrait mode. @@ -148,10 +175,12 @@ public class UdfpsView extends View implements DozeReceiver { // Transform mSensorRect if the device is in landscape mode. switch (mContext.getDisplay().getRotation()) { case Surface.ROTATION_90: + //noinspection SuspiciousNameCombination mSensorRect.set(mSensorRect.top, h - mSensorRect.right, mSensorRect.bottom, h - mSensorRect.left); break; case Surface.ROTATION_270: + //noinspection SuspiciousNameCombination mSensorRect.set(w - mSensorRect.bottom, mSensorRect.left, w - mSensorRect.top, mSensorRect.right); break; @@ -223,6 +252,8 @@ public class UdfpsView extends View implements DozeReceiver { canvas.drawOval(mSensorRect, mSensorPaint); } else { if (mUdfpsAnimation != null) { + final int alpha = shouldPauseAuth() ? 255 - mNotificationPanelAlpha : 255; + mUdfpsAnimation.setAlpha(alpha); mUdfpsAnimation.draw(canvas); } } @@ -261,7 +292,19 @@ public class UdfpsView extends View implements DozeReceiver { return x > (cx - rx * mSensorTouchAreaCoefficient) && x < (cx + rx * mSensorTouchAreaCoefficient) && y > (cy - ry * mSensorTouchAreaCoefficient) - && y < (cy + ry * mSensorTouchAreaCoefficient); + && y < (cy + ry * mSensorTouchAreaCoefficient) + && !shouldPauseAuth(); + } + + /** + * States where UDFPS should temporarily not be authenticating. Instead of completely stopping + * authentication which would cause the UDFPS icons to abruptly disappear, do it here by not + * sending onFingerDown and smoothly animating away. + */ + private boolean shouldPauseAuth() { + return (mNotificationShadeExpanded && mStatusBarState != KEYGUARD) + || mStatusBarState == SHADE_LOCKED + || mStatusBarState == FULLSCREEN_USER_SWITCHER; } void setScrimAlpha(int alpha) { diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/ScrimController.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/ScrimController.java index 03d17e5050da0..19c0b6dc193a4 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/ScrimController.java +++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/ScrimController.java @@ -34,6 +34,7 @@ import android.view.ViewTreeObserver; import android.view.animation.DecelerateInterpolator; import android.view.animation.Interpolator; +import androidx.annotation.NonNull; import androidx.annotation.Nullable; import com.android.internal.annotations.VisibleForTesting; @@ -62,6 +63,8 @@ import java.io.FileDescriptor; import java.io.PrintWriter; import java.lang.annotation.Retention; import java.lang.annotation.RetentionPolicy; +import java.util.ArrayList; +import java.util.List; import java.util.function.Consumer; import javax.inject.Inject; @@ -195,6 +198,16 @@ public class ScrimController implements ViewTreeObserver.OnPreDrawListener, Dump private boolean mWakeLockHeld; private boolean mKeyguardOccluded; + /** + * Notifies listeners of animation-related changes (currently just opacity changes). + */ + public interface ScrimChangedListener { + void onAlphaChanged(float alpha); + } + + @NonNull + private final List mScrimChangedListeners; + @Inject public ScrimController(LightBarController lightBarController, DozeParameters dozeParameters, AlarmManager alarmManager, KeyguardStateController keyguardStateController, @@ -208,6 +221,7 @@ public class ScrimController implements ViewTreeObserver.OnPreDrawListener, Dump ScrimState.BUBBLE_EXPANDED.setBubbleAlpha(featureFlags.isShadeOpaque() ? BUSY_SCRIM_ALPHA : GAR_SCRIM_ALPHA); mBlurUtils = blurUtils; + mScrimChangedListeners = new ArrayList<>(); mKeyguardStateController = keyguardStateController; mDarkenWhileDragging = !mKeyguardStateController.canDismissLockScreen(); @@ -284,6 +298,10 @@ public class ScrimController implements ViewTreeObserver.OnPreDrawListener, Dump mScrimVisibleListener = listener; } + public void addScrimChangedListener(@NonNull ScrimChangedListener listener) { + mScrimChangedListeners.add(listener); + } + public void transitionTo(ScrimState state) { transitionTo(state, null); } @@ -559,6 +577,10 @@ public class ScrimController implements ViewTreeObserver.OnPreDrawListener, Dump throw new IllegalStateException("Scrim opacity is NaN for state: " + mState + ", front: " + mInFrontAlpha + ", back: " + mBehindAlpha); } + + for (ScrimChangedListener listener : mScrimChangedListeners) { + listener.onAlphaChanged(mBehindAlpha); + } } private void applyAndDispatchExpansion() { 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 ed3cf9a49a340..dd145e4193215 100644 --- a/packages/SystemUI/tests/src/com/android/systemui/biometrics/UdfpsControllerTest.java +++ b/packages/SystemUI/tests/src/com/android/systemui/biometrics/UdfpsControllerTest.java @@ -45,6 +45,7 @@ import androidx.test.filters.SmallTest; import com.android.systemui.R; import com.android.systemui.SysuiTestCase; import com.android.systemui.plugins.statusbar.StatusBarStateController; +import com.android.systemui.statusbar.phone.ScrimController; import com.android.systemui.util.concurrency.FakeExecutor; import com.android.systemui.util.settings.FakeSettings; import com.android.systemui.util.time.FakeSystemClock; @@ -90,6 +91,8 @@ public class UdfpsControllerTest extends SysuiTestCase { private WindowManager mWindowManager; @Mock private StatusBarStateController mStatusBarStateController; + @Mock + private ScrimController mScrimController; private FakeSettings mSystemSettings; private FakeExecutor mFgExecutor; @@ -130,7 +133,8 @@ public class UdfpsControllerTest extends SysuiTestCase { mWindowManager, mSystemSettings, mStatusBarStateController, - mFgExecutor); + mFgExecutor, + mScrimController); verify(mFingerprintManager).setUdfpsOverlayController(mOverlayCaptor.capture()); mOverlayController = mOverlayCaptor.getValue(); @@ -245,4 +249,10 @@ public class UdfpsControllerTest extends SysuiTestCase { // THEN the scrim and dot is hidden verify(mUdfpsView).hideScrimAndDot(); } + + @Test + public void registersViewForCallbacks() throws RemoteException { + verify(mStatusBarStateController).addCallback(mUdfpsView); + verify(mScrimController).addScrimChangedListener(mUdfpsView); + } }