Hide Udfps icon when shade is expanded

1) Adds interface for ScrimController to notify listeners of new
   alpha
2) For SysUI modules such as UdfpsView that have views in layers
   ABOVE the notification shade (such as during lockscreen auth),
   yet need to pretend to be behind the shade (such as during
   enrollment), this allows UdfpsView to set its alpha and color
   to the right level

Bug: 177931181
Test: atest com.android.systemui.biometrics

Change-Id: I9b7bd5d72acee8ccbba9aef5b1c935bbf1da815f
This commit is contained in:
Kevin Chyn
2021-01-21 17:22:55 -08:00
parent 77bb0b7291
commit b7b70ace2e
8 changed files with 105 additions and 16 deletions

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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