Enable RRS (FHD<->QHD) support for UDFPS
UDFPS position and assets are configured in pixels in native resolution. This CL adds a mechanism to detect a runtime resolution change and scale the UDFPS coordinates accordingly. Bug: 223342274 Test: atest SystemUITests:com.android.systemui.biometrics Change-Id: Iaa1f3d633b50019d06bdecb5063490ad6eb11272
This commit is contained in:
@@ -18,6 +18,7 @@ package android.hardware.biometrics;
|
||||
|
||||
import android.annotation.NonNull;
|
||||
import android.annotation.Nullable;
|
||||
import android.graphics.Rect;
|
||||
import android.os.Parcel;
|
||||
import android.os.Parcelable;
|
||||
|
||||
@@ -109,4 +110,12 @@ public class SensorLocationInternal implements Parcelable {
|
||||
+ ", y: " + sensorLocationY
|
||||
+ ", r: " + sensorRadius + "]";
|
||||
}
|
||||
|
||||
/** Returns coordinates of a bounding box around the sensor. */
|
||||
public Rect getRect() {
|
||||
return new Rect(sensorLocationX - sensorRadius,
|
||||
sensorLocationY - sensorRadius,
|
||||
sensorLocationX + sensorRadius,
|
||||
sensorLocationY + sensorRadius);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -87,6 +87,40 @@ public class RotationUtils {
|
||||
rotateBounds(inOutBounds, parentBounds, deltaRotation(oldRotation, newRotation));
|
||||
}
|
||||
|
||||
/**
|
||||
* Rotates inOutBounds together with the parent for a given rotation delta. This assumes that
|
||||
* the parent starts at 0,0 and remains at 0,0 after the rotation. The inOutBounds will remain
|
||||
* at the same physical position within the parent.
|
||||
*
|
||||
* Only 'inOutBounds' is mutated.
|
||||
*/
|
||||
public static void rotateBounds(Rect inOutBounds, int parentWidth, int parentHeight,
|
||||
@Rotation int rotation) {
|
||||
final int origLeft = inOutBounds.left;
|
||||
final int origTop = inOutBounds.top;
|
||||
switch (rotation) {
|
||||
case ROTATION_0:
|
||||
return;
|
||||
case ROTATION_90:
|
||||
inOutBounds.left = inOutBounds.top;
|
||||
inOutBounds.top = parentWidth - inOutBounds.right;
|
||||
inOutBounds.right = inOutBounds.bottom;
|
||||
inOutBounds.bottom = parentWidth - origLeft;
|
||||
return;
|
||||
case ROTATION_180:
|
||||
inOutBounds.left = parentWidth - inOutBounds.right;
|
||||
inOutBounds.right = parentWidth - origLeft;
|
||||
inOutBounds.top = parentHeight - inOutBounds.bottom;
|
||||
inOutBounds.bottom = parentHeight - origTop;
|
||||
return;
|
||||
case ROTATION_270:
|
||||
inOutBounds.left = parentHeight - inOutBounds.bottom;
|
||||
inOutBounds.bottom = inOutBounds.right;
|
||||
inOutBounds.right = parentHeight - inOutBounds.top;
|
||||
inOutBounds.top = origLeft;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Rotates bounds as if parentBounds and bounds are a group. The group is rotated by `delta`
|
||||
* 90-degree counter-clockwise increments. This assumes that parentBounds is at 0,0 and
|
||||
@@ -96,29 +130,7 @@ public class RotationUtils {
|
||||
* Only 'inOutBounds' is mutated.
|
||||
*/
|
||||
public static void rotateBounds(Rect inOutBounds, Rect parentBounds, @Rotation int rotation) {
|
||||
final int origLeft = inOutBounds.left;
|
||||
final int origTop = inOutBounds.top;
|
||||
switch (rotation) {
|
||||
case ROTATION_0:
|
||||
return;
|
||||
case ROTATION_90:
|
||||
inOutBounds.left = inOutBounds.top;
|
||||
inOutBounds.top = parentBounds.right - inOutBounds.right;
|
||||
inOutBounds.right = inOutBounds.bottom;
|
||||
inOutBounds.bottom = parentBounds.right - origLeft;
|
||||
return;
|
||||
case ROTATION_180:
|
||||
inOutBounds.left = parentBounds.right - inOutBounds.right;
|
||||
inOutBounds.right = parentBounds.right - origLeft;
|
||||
inOutBounds.top = parentBounds.bottom - inOutBounds.bottom;
|
||||
inOutBounds.bottom = parentBounds.bottom - origTop;
|
||||
return;
|
||||
case ROTATION_270:
|
||||
inOutBounds.left = parentBounds.bottom - inOutBounds.bottom;
|
||||
inOutBounds.bottom = inOutBounds.right;
|
||||
inOutBounds.right = parentBounds.bottom - inOutBounds.top;
|
||||
inOutBounds.top = origLeft;
|
||||
}
|
||||
rotateBounds(inOutBounds, parentBounds.right, parentBounds.bottom, rotation);
|
||||
}
|
||||
|
||||
/** @return the rotation needed to rotate from oldRotation to newRotation. */
|
||||
|
||||
@@ -30,7 +30,9 @@ import android.content.Intent;
|
||||
import android.content.IntentFilter;
|
||||
import android.content.res.Configuration;
|
||||
import android.content.res.Resources;
|
||||
import android.graphics.Point;
|
||||
import android.graphics.PointF;
|
||||
import android.graphics.Rect;
|
||||
import android.hardware.SensorPrivacyManager;
|
||||
import android.hardware.biometrics.BiometricAuthenticator.Modality;
|
||||
import android.hardware.biometrics.BiometricConstants;
|
||||
@@ -54,6 +56,7 @@ import android.os.RemoteException;
|
||||
import android.os.UserManager;
|
||||
import android.util.Log;
|
||||
import android.util.SparseBooleanArray;
|
||||
import android.view.DisplayInfo;
|
||||
import android.view.MotionEvent;
|
||||
import android.view.WindowManager;
|
||||
|
||||
@@ -104,16 +107,16 @@ public class AuthController extends CoreStartable implements CommandQueue.Callba
|
||||
private final CommandQueue mCommandQueue;
|
||||
private final StatusBarStateController mStatusBarStateController;
|
||||
private final ActivityTaskManager mActivityTaskManager;
|
||||
@Nullable
|
||||
private final FingerprintManager mFingerprintManager;
|
||||
@Nullable
|
||||
private final FaceManager mFaceManager;
|
||||
@Nullable private final FingerprintManager mFingerprintManager;
|
||||
@Nullable private final FaceManager mFaceManager;
|
||||
private final Provider<UdfpsController> mUdfpsControllerFactory;
|
||||
private final Provider<SidefpsController> mSidefpsControllerFactory;
|
||||
@Nullable
|
||||
private final PointF mFaceAuthSensorLocation;
|
||||
@Nullable
|
||||
private PointF mFingerprintLocation;
|
||||
|
||||
@NonNull private Point mStableDisplaySize = new Point();
|
||||
|
||||
@Nullable private final PointF mFaceAuthSensorLocation;
|
||||
@Nullable private PointF mFingerprintLocation;
|
||||
@Nullable private Rect mUdfpsBounds;
|
||||
private final Set<Callback> mCallbacks = new HashSet<>();
|
||||
|
||||
// TODO: These should just be saved from onSaveState
|
||||
@@ -122,14 +125,13 @@ public class AuthController extends CoreStartable implements CommandQueue.Callba
|
||||
AuthDialog mCurrentDialog;
|
||||
|
||||
@NonNull private final WindowManager mWindowManager;
|
||||
@NonNull private final DisplayManager mDisplayManager;
|
||||
@Nullable private UdfpsController mUdfpsController;
|
||||
@Nullable private IUdfpsHbmListener mUdfpsHbmListener;
|
||||
@Nullable private SidefpsController mSidefpsController;
|
||||
@Nullable private IBiometricContextListener mBiometricContextListener;
|
||||
@VisibleForTesting
|
||||
IBiometricSysuiReceiver mReceiver;
|
||||
@VisibleForTesting
|
||||
@NonNull final BiometricDisplayListener mOrientationListener;
|
||||
@VisibleForTesting IBiometricSysuiReceiver mReceiver;
|
||||
@VisibleForTesting @NonNull final BiometricDisplayListener mOrientationListener;
|
||||
@Nullable private final List<FaceSensorPropertiesInternal> mFaceProps;
|
||||
@Nullable private List<FingerprintSensorPropertiesInternal> mFpProps;
|
||||
@Nullable private List<FingerprintSensorPropertiesInternal> mUdfpsProps;
|
||||
@@ -249,6 +251,7 @@ public class AuthController extends CoreStartable implements CommandQueue.Callba
|
||||
}
|
||||
mAllFingerprintAuthenticatorsRegistered = true;
|
||||
mFpProps = sensors;
|
||||
|
||||
List<FingerprintSensorPropertiesInternal> udfpsProps = new ArrayList<>();
|
||||
List<FingerprintSensorPropertiesInternal> sidefpsProps = new ArrayList<>();
|
||||
for (FingerprintSensorPropertiesInternal props : mFpProps) {
|
||||
@@ -259,12 +262,14 @@ public class AuthController extends CoreStartable implements CommandQueue.Callba
|
||||
sidefpsProps.add(props);
|
||||
}
|
||||
}
|
||||
|
||||
mUdfpsProps = !udfpsProps.isEmpty() ? udfpsProps : null;
|
||||
if (mUdfpsProps != null) {
|
||||
mUdfpsController = mUdfpsControllerFactory.get();
|
||||
mUdfpsController.addCallback(new UdfpsController.Callback() {
|
||||
@Override
|
||||
public void onFingerUp() {}
|
||||
public void onFingerUp() {
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onFingerDown() {
|
||||
@@ -273,15 +278,22 @@ public class AuthController extends CoreStartable implements CommandQueue.Callba
|
||||
}
|
||||
}
|
||||
});
|
||||
mUdfpsController.setAuthControllerUpdateUdfpsLocation(this::updateUdfpsLocation);
|
||||
mUdfpsBounds = mUdfpsProps.get(0).getLocation().getRect();
|
||||
updateUdfpsLocation();
|
||||
}
|
||||
|
||||
mSidefpsProps = !sidefpsProps.isEmpty() ? sidefpsProps : null;
|
||||
if (mSidefpsProps != null) {
|
||||
mSidefpsController = mSidefpsControllerFactory.get();
|
||||
}
|
||||
|
||||
mFingerprintManager.registerBiometricStateListener(mBiometricStateListener);
|
||||
updateFingerprintLocation();
|
||||
|
||||
for (Callback cb : mCallbacks) {
|
||||
cb.onAllAuthenticatorsRegistered();
|
||||
}
|
||||
mFingerprintManager.registerBiometricStateListener(mBiometricStateListener);
|
||||
}
|
||||
|
||||
private void handleEnrollmentsChanged(int userId, int sensorId, boolean hasEnrollments) {
|
||||
@@ -424,12 +436,11 @@ public class AuthController extends CoreStartable implements CommandQueue.Callba
|
||||
/**
|
||||
* @return where the UDFPS exists on the screen in pixels in portrait mode.
|
||||
*/
|
||||
@Nullable public PointF getUdfpsSensorLocation() {
|
||||
if (mUdfpsController == null) {
|
||||
@Nullable public PointF getUdfpsLocation() {
|
||||
if (mUdfpsController == null || mUdfpsBounds == null) {
|
||||
return null;
|
||||
}
|
||||
return new PointF(mUdfpsController.getSensorLocation().centerX(),
|
||||
mUdfpsController.getSensorLocation().centerY());
|
||||
return new PointF(mUdfpsBounds.centerX(), mUdfpsBounds.centerY());
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -437,8 +448,8 @@ public class AuthController extends CoreStartable implements CommandQueue.Callba
|
||||
* overridden value will use the default value even if they don't have a fingerprint sensor
|
||||
*/
|
||||
@Nullable public PointF getFingerprintSensorLocation() {
|
||||
if (getUdfpsSensorLocation() != null) {
|
||||
return getUdfpsSensorLocation();
|
||||
if (getUdfpsLocation() != null) {
|
||||
return getUdfpsLocation();
|
||||
}
|
||||
return mFingerprintLocation;
|
||||
}
|
||||
@@ -525,12 +536,13 @@ public class AuthController extends CoreStartable implements CommandQueue.Callba
|
||||
mFaceManager = faceManager;
|
||||
mUdfpsControllerFactory = udfpsControllerFactory;
|
||||
mSidefpsControllerFactory = sidefpsControllerFactory;
|
||||
mDisplayManager = displayManager;
|
||||
mWindowManager = windowManager;
|
||||
mUdfpsEnrolledForUser = new SparseBooleanArray();
|
||||
|
||||
mOrientationListener = new BiometricDisplayListener(
|
||||
context,
|
||||
displayManager,
|
||||
mDisplayManager,
|
||||
mHandler,
|
||||
BiometricDisplayListener.SensorType.Generic.INSTANCE,
|
||||
() -> {
|
||||
@@ -582,6 +594,27 @@ public class AuthController extends CoreStartable implements CommandQueue.Callba
|
||||
yLocation);
|
||||
}
|
||||
|
||||
// TODO(b/229290039): UDFPS controller should manage its dimensions on its own. Remove this.
|
||||
// This is not combined with updateFingerprintLocation because this is invoked directly from
|
||||
// UdfpsController, only when it cares about a rotation change. The implications of calling
|
||||
// updateFingerprintLocation in such a case are unclear.
|
||||
private void updateUdfpsLocation() {
|
||||
if (mUdfpsController != null) {
|
||||
final DisplayInfo displayInfo = new DisplayInfo();
|
||||
mContext.getDisplay().getDisplayInfo(displayInfo);
|
||||
final float scaleFactor = android.util.DisplayUtils.getPhysicalPixelDisplaySizeRatio(
|
||||
mStableDisplaySize.x, mStableDisplaySize.y, displayInfo.getNaturalWidth(),
|
||||
displayInfo.getNaturalHeight());
|
||||
|
||||
final FingerprintSensorPropertiesInternal udfpsProp = mUdfpsProps.get(0);
|
||||
mUdfpsBounds = udfpsProp.getLocation().getRect();
|
||||
mUdfpsBounds.scale(scaleFactor);
|
||||
mUdfpsController.updateOverlayParams(udfpsProp.sensorId,
|
||||
new UdfpsOverlayParams(mUdfpsBounds, displayInfo.getNaturalWidth(),
|
||||
displayInfo.getNaturalHeight(), scaleFactor, displayInfo.rotation));
|
||||
}
|
||||
}
|
||||
|
||||
@SuppressWarnings("deprecation")
|
||||
@Override
|
||||
public void start() {
|
||||
@@ -592,6 +625,7 @@ public class AuthController extends CoreStartable implements CommandQueue.Callba
|
||||
mFingerprintAuthenticatorsRegisteredCallback);
|
||||
}
|
||||
|
||||
mStableDisplaySize = mDisplayManager.getStableDisplaySize();
|
||||
mActivityTaskManager.registerTaskStackListener(mTaskStackListener);
|
||||
}
|
||||
|
||||
@@ -906,6 +940,7 @@ public class AuthController extends CoreStartable implements CommandQueue.Callba
|
||||
protected void onConfigurationChanged(Configuration newConfig) {
|
||||
super.onConfigurationChanged(newConfig);
|
||||
updateFingerprintLocation();
|
||||
updateUdfpsLocation();
|
||||
|
||||
// Save the state of the current dialog (buttons showing, etc)
|
||||
if (mCurrentDialog != null) {
|
||||
@@ -935,6 +970,7 @@ public class AuthController extends CoreStartable implements CommandQueue.Callba
|
||||
|
||||
private void onOrientationChanged() {
|
||||
updateFingerprintLocation();
|
||||
updateUdfpsLocation();
|
||||
if (mCurrentDialog != null) {
|
||||
mCurrentDialog.onOrientationChanged();
|
||||
}
|
||||
|
||||
@@ -64,7 +64,11 @@ class BiometricDisplayListener(
|
||||
/** Listen for changes. */
|
||||
fun enable() {
|
||||
lastRotation = context.display?.rotation ?: Surface.ROTATION_0
|
||||
displayManager.registerDisplayListener(this, handler)
|
||||
displayManager.registerDisplayListener(
|
||||
this,
|
||||
handler,
|
||||
DisplayManager.EVENT_FLAG_DISPLAY_CHANGED
|
||||
)
|
||||
}
|
||||
|
||||
/** Stop listening for changes. */
|
||||
@@ -80,9 +84,7 @@ class BiometricDisplayListener(
|
||||
*/
|
||||
sealed class SensorType {
|
||||
object Generic : SensorType()
|
||||
data class UnderDisplayFingerprint(
|
||||
val properties: FingerprintSensorPropertiesInternal
|
||||
) : SensorType()
|
||||
object UnderDisplayFingerprint : SensorType()
|
||||
data class SideFingerprint(
|
||||
val properties: FingerprintSensorPropertiesInternal
|
||||
) : SensorType()
|
||||
|
||||
@@ -19,7 +19,6 @@ package com.android.systemui.biometrics;
|
||||
import static android.hardware.biometrics.BiometricFingerprintConstants.FINGERPRINT_ACQUIRED_GOOD;
|
||||
import static android.hardware.biometrics.BiometricOverlayConstants.REASON_AUTH_KEYGUARD;
|
||||
|
||||
import static com.android.internal.util.Preconditions.checkArgument;
|
||||
import static com.android.internal.util.Preconditions.checkNotNull;
|
||||
import static com.android.systemui.classifier.Classifier.UDFPS_AUTHENTICATION;
|
||||
|
||||
@@ -30,12 +29,9 @@ import android.content.Context;
|
||||
import android.content.Intent;
|
||||
import android.content.IntentFilter;
|
||||
import android.graphics.Point;
|
||||
import android.graphics.RectF;
|
||||
import android.hardware.biometrics.BiometricFingerprintConstants;
|
||||
import android.hardware.biometrics.SensorLocationInternal;
|
||||
import android.hardware.display.DisplayManager;
|
||||
import android.hardware.fingerprint.FingerprintManager;
|
||||
import android.hardware.fingerprint.FingerprintSensorPropertiesInternal;
|
||||
import android.hardware.fingerprint.IUdfpsOverlayController;
|
||||
import android.hardware.fingerprint.IUdfpsOverlayControllerCallback;
|
||||
import android.os.Handler;
|
||||
@@ -45,6 +41,7 @@ import android.os.Trace;
|
||||
import android.os.VibrationAttributes;
|
||||
import android.os.VibrationEffect;
|
||||
import android.util.Log;
|
||||
import android.util.RotationUtils;
|
||||
import android.view.LayoutInflater;
|
||||
import android.view.MotionEvent;
|
||||
import android.view.Surface;
|
||||
@@ -99,7 +96,6 @@ import kotlin.Unit;
|
||||
public class UdfpsController implements DozeReceiver {
|
||||
private static final String TAG = "UdfpsController";
|
||||
private static final long AOD_INTERRUPT_TIMEOUT_MILLIS = 1000;
|
||||
private static final long DEFAULT_VIBRATION_DURATION = 1000; // milliseconds
|
||||
|
||||
// Minimum required delay between consecutive touch logs in milliseconds.
|
||||
private static final long MIN_TOUCH_LOG_INTERVAL = 50;
|
||||
@@ -129,10 +125,14 @@ public class UdfpsController implements DozeReceiver {
|
||||
mUnlockedScreenOffAnimationController;
|
||||
@NonNull private final LatencyTracker mLatencyTracker;
|
||||
@VisibleForTesting @NonNull final BiometricDisplayListener mOrientationListener;
|
||||
@NonNull private final ActivityLaunchAnimator mActivityLaunchAnimator;
|
||||
|
||||
// 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;
|
||||
@NonNull private final ActivityLaunchAnimator mActivityLaunchAnimator;
|
||||
@VisibleForTesting int mSensorId;
|
||||
@VisibleForTesting @NonNull UdfpsOverlayParams mOverlayParams = new UdfpsOverlayParams();
|
||||
// TODO(b/229290039): UDFPS controller should manage its dimensions on its own. Remove this.
|
||||
@Nullable private Runnable mAuthControllerUpdateUdfpsLocation;
|
||||
@Nullable private final AlternateUdfpsTouchProvider mAlternateTouchProvider;
|
||||
|
||||
// Tracks the velocity of a touch to help filter out the touches that move too fast.
|
||||
@@ -193,19 +193,16 @@ public class UdfpsController implements DozeReceiver {
|
||||
@Override
|
||||
public void showUdfpsOverlay(long requestId, int sensorId, int reason,
|
||||
@NonNull IUdfpsOverlayControllerCallback callback) {
|
||||
mFgExecutor.execute(
|
||||
() -> UdfpsController.this.showUdfpsOverlay(new UdfpsControllerOverlay(
|
||||
mContext, mFingerprintManager, mInflater, mWindowManager,
|
||||
mAccessibilityManager, mStatusBarStateController,
|
||||
mFgExecutor.execute(() -> UdfpsController.this.showUdfpsOverlay(
|
||||
new UdfpsControllerOverlay(mContext, mFingerprintManager, mInflater,
|
||||
mWindowManager, mAccessibilityManager, mStatusBarStateController,
|
||||
mPanelExpansionStateManager, mKeyguardViewManager,
|
||||
mKeyguardUpdateMonitor, mDialogManager, mDumpManager,
|
||||
mLockscreenShadeTransitionController, mConfigurationController,
|
||||
mSystemClock, mKeyguardStateController,
|
||||
mUnlockedScreenOffAnimationController, mSensorProps,
|
||||
mHbmProvider, requestId, reason, callback,
|
||||
(view, event, fromUdfpsView) ->
|
||||
onTouch(requestId, event, fromUdfpsView),
|
||||
mActivityLaunchAnimator)));
|
||||
mUnlockedScreenOffAnimationController, mHbmProvider, requestId, reason,
|
||||
callback, (view, event, fromUdfpsView) -> onTouch(requestId, event,
|
||||
fromUdfpsView), mActivityLaunchAnimator)));
|
||||
}
|
||||
|
||||
@Override
|
||||
@@ -280,6 +277,38 @@ public class UdfpsController implements DozeReceiver {
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Updates the overlay parameters and reconstructs or redraws the overlay, if necessary.
|
||||
*
|
||||
* @param sensorId sensor for which the overlay is getting updated.
|
||||
* @param overlayParams See {@link UdfpsOverlayParams}.
|
||||
*/
|
||||
public void updateOverlayParams(int sensorId, @NonNull UdfpsOverlayParams overlayParams) {
|
||||
if (sensorId != mSensorId) {
|
||||
mSensorId = sensorId;
|
||||
Log.w(TAG, "updateUdfpsParams | sensorId has changed");
|
||||
}
|
||||
|
||||
if (!mOverlayParams.equals(overlayParams)) {
|
||||
mOverlayParams = overlayParams;
|
||||
|
||||
final boolean wasShowingAltAuth = mKeyguardViewManager.isShowingAlternateAuth();
|
||||
|
||||
// When the bounds change it's always necessary to re-create the overlay's window with
|
||||
// new LayoutParams. If the overlay needs to be shown, this will re-create and show the
|
||||
// overlay with the updated LayoutParams. Otherwise, the overlay will remain hidden.
|
||||
redrawOverlay();
|
||||
if (wasShowingAltAuth) {
|
||||
mKeyguardViewManager.showGenericBouncer(true);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// TODO(b/229290039): UDFPS controller should manage its dimensions on its own. Remove this.
|
||||
public void setAuthControllerUpdateUdfpsLocation(@Nullable Runnable r) {
|
||||
mAuthControllerUpdateUdfpsLocation = r;
|
||||
}
|
||||
|
||||
/**
|
||||
* Calculate the pointer speed given a velocity tracker and the pointer id.
|
||||
* This assumes that the velocity tracker has already been passed all relevant motion events.
|
||||
@@ -340,10 +369,11 @@ public class UdfpsController implements DozeReceiver {
|
||||
}
|
||||
|
||||
return !mOverlay.getAnimationViewController().shouldPauseAuth()
|
||||
&& getSensorLocation().contains(x, y);
|
||||
&& mOverlayParams.getSensorBounds().contains((int) x, (int) y);
|
||||
}
|
||||
|
||||
private boolean onTouch(long requestId, @NonNull MotionEvent event, boolean fromUdfpsView) {
|
||||
@VisibleForTesting
|
||||
boolean onTouch(long requestId, @NonNull MotionEvent event, boolean fromUdfpsView) {
|
||||
if (mOverlay == null) {
|
||||
Log.w(TAG, "ignoring onTouch with null overlay");
|
||||
return false;
|
||||
@@ -438,33 +468,28 @@ public class UdfpsController implements DozeReceiver {
|
||||
final long sinceLastLog = mSystemClock.elapsedRealtime() - mTouchLogTime;
|
||||
if (!isIlluminationRequested && !mAcquiredReceived
|
||||
&& !exceedsVelocityThreshold) {
|
||||
final int rawX = (int) event.getRawX();
|
||||
final int rawY = (int) event.getRawY();
|
||||
// Default coordinates assume portrait mode.
|
||||
int x = rawX;
|
||||
int y = rawY;
|
||||
|
||||
// Gets the size based on the current rotation of the display.
|
||||
Point p = new Point();
|
||||
mContext.getDisplay().getRealSize(p);
|
||||
|
||||
// Transform x, y to portrait mode if the device is in landscape mode.
|
||||
switch (mContext.getDisplay().getRotation()) {
|
||||
case Surface.ROTATION_90:
|
||||
x = p.y - rawY;
|
||||
y = rawX;
|
||||
break;
|
||||
|
||||
case Surface.ROTATION_270:
|
||||
x = rawY;
|
||||
y = p.x - rawX;
|
||||
break;
|
||||
|
||||
default:
|
||||
// Do nothing to stay in portrait mode.
|
||||
// Map the touch to portrait mode if the device is in landscape mode.
|
||||
Point portraitTouch = new Point(
|
||||
(int) event.getRawX(idx),
|
||||
(int) event.getRawY(idx)
|
||||
);
|
||||
final int rot = mOverlayParams.getRotation();
|
||||
if (rot == Surface.ROTATION_90 || rot == Surface.ROTATION_270) {
|
||||
RotationUtils.rotatePoint(portraitTouch,
|
||||
RotationUtils.deltaRotation(rot, Surface.ROTATION_0),
|
||||
mOverlayParams.getLogicalDisplayWidth(),
|
||||
mOverlayParams.getLogicalDisplayHeight()
|
||||
);
|
||||
}
|
||||
|
||||
onFingerDown(requestId, x, y, minor, major);
|
||||
// Scale the coordinates to native resolution.
|
||||
final float scale = mOverlayParams.getScaleFactor();
|
||||
int scaledX = (int) (portraitTouch.x / scale);
|
||||
int scaledY = (int) (portraitTouch.y / scale);
|
||||
float scaledMinor = minor / scale;
|
||||
float scaledMajor = major / scale;
|
||||
|
||||
onFingerDown(requestId, scaledX, scaledY, scaledMinor, scaledMajor);
|
||||
Log.v(TAG, "onTouch | finger down: " + touchInfo);
|
||||
mTouchLogTime = mSystemClock.elapsedRealtime();
|
||||
mPowerManager.userActivity(mSystemClock.uptimeMillis(),
|
||||
@@ -571,16 +596,15 @@ public class UdfpsController implements DozeReceiver {
|
||||
mActivityLaunchAnimator = activityLaunchAnimator;
|
||||
mAlternateTouchProvider = aternateTouchProvider.orElse(null);
|
||||
|
||||
mSensorProps = findFirstUdfps();
|
||||
// At least one UDFPS sensor exists
|
||||
checkArgument(mSensorProps != null);
|
||||
mOrientationListener = new BiometricDisplayListener(
|
||||
context,
|
||||
displayManager,
|
||||
mainHandler,
|
||||
new BiometricDisplayListener.SensorType.UnderDisplayFingerprint(mSensorProps),
|
||||
BiometricDisplayListener.SensorType.UnderDisplayFingerprint.INSTANCE,
|
||||
() -> {
|
||||
onOrientationChanged();
|
||||
if (mAuthControllerUpdateUdfpsLocation != null) {
|
||||
mAuthControllerUpdateUdfpsLocation.run();
|
||||
}
|
||||
return Unit.INSTANCE;
|
||||
});
|
||||
|
||||
@@ -609,17 +633,6 @@ public class UdfpsController implements DozeReceiver {
|
||||
}
|
||||
}
|
||||
|
||||
@Nullable
|
||||
private FingerprintSensorPropertiesInternal findFirstUdfps() {
|
||||
for (FingerprintSensorPropertiesInternal props :
|
||||
mFingerprintManager.getSensorPropertiesInternal()) {
|
||||
if (props.isAnyUdfpsType()) {
|
||||
return props;
|
||||
}
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void dozeTimeTick() {
|
||||
if (mOverlay != null) {
|
||||
@@ -630,21 +643,6 @@ public class UdfpsController implements DozeReceiver {
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* @return where the UDFPS exists on the screen in pixels.
|
||||
*/
|
||||
public RectF getSensorLocation() {
|
||||
// This is currently used to calculate the amount of space available for notifications
|
||||
// on lockscreen and for the udfps light reveal animation on keyguard.
|
||||
// Keyguard is only shown in portrait mode for now, so this will need to
|
||||
// be updated if that ever changes.
|
||||
final SensorLocationInternal location = mSensorProps.getLocation();
|
||||
return new RectF(location.sensorLocationX - location.sensorRadius,
|
||||
location.sensorLocationY - location.sensorRadius,
|
||||
location.sensorLocationX + location.sensorRadius,
|
||||
location.sensorLocationY + location.sensorRadius);
|
||||
}
|
||||
|
||||
private void redrawOverlay() {
|
||||
UdfpsControllerOverlay overlay = mOverlay;
|
||||
if (overlay != null) {
|
||||
@@ -653,26 +651,11 @@ public class UdfpsController implements DozeReceiver {
|
||||
}
|
||||
}
|
||||
|
||||
private void onOrientationChanged() {
|
||||
// When the configuration changes it's almost always necessary to destroy and re-create
|
||||
// the overlay's window to pass it the new LayoutParams.
|
||||
// Hiding the overlay will destroy its window. It's safe to hide the overlay regardless
|
||||
// of whether it is already hidden.
|
||||
final boolean wasShowingAltAuth = mKeyguardViewManager.isShowingAlternateAuth();
|
||||
|
||||
// If the overlay needs to be shown, this will re-create and show the overlay with the
|
||||
// updated LayoutParams. Otherwise, the overlay will remain hidden.
|
||||
redrawOverlay();
|
||||
if (wasShowingAltAuth) {
|
||||
mKeyguardViewManager.showGenericBouncer(true);
|
||||
}
|
||||
}
|
||||
|
||||
private void showUdfpsOverlay(@NonNull UdfpsControllerOverlay overlay) {
|
||||
mExecution.assertIsMainThread();
|
||||
|
||||
mOverlay = overlay;
|
||||
if (overlay.show(this)) {
|
||||
if (overlay.show(this, mOverlayParams)) {
|
||||
Log.v(TAG, "showUdfpsOverlay | adding window reason="
|
||||
+ overlay.getRequestReason());
|
||||
mOnFingerDown = false;
|
||||
@@ -814,14 +797,14 @@ public class UdfpsController implements DozeReceiver {
|
||||
if (mAlternateTouchProvider != null) {
|
||||
mAlternateTouchProvider.onPointerDown(requestId, x, y, minor, major);
|
||||
} else {
|
||||
mFingerprintManager.onPointerDown(requestId, mSensorProps.sensorId, x, y, minor, major);
|
||||
mFingerprintManager.onPointerDown(requestId, mSensorId, x, y, minor, major);
|
||||
}
|
||||
Trace.endAsyncSection("UdfpsController.e2e.onPointerDown", 0);
|
||||
|
||||
final UdfpsView view = mOverlay.getOverlayView();
|
||||
if (view != null) {
|
||||
view.startIllumination(() -> {
|
||||
mFingerprintManager.onUiReady(requestId, mSensorProps.sensorId);
|
||||
mFingerprintManager.onUiReady(requestId, mSensorId);
|
||||
mLatencyTracker.onActionEnd(LatencyTracker.ACTION_UDFPS_ILLUMINATE);
|
||||
});
|
||||
}
|
||||
@@ -839,7 +822,7 @@ public class UdfpsController implements DozeReceiver {
|
||||
if (mAlternateTouchProvider != null) {
|
||||
mAlternateTouchProvider.onPointerUp(requestId);
|
||||
} else {
|
||||
mFingerprintManager.onPointerUp(requestId, mSensorProps.sensorId);
|
||||
mFingerprintManager.onPointerUp(requestId, mSensorId);
|
||||
}
|
||||
for (Callback cb : mCallbacks) {
|
||||
cb.onFingerUp();
|
||||
|
||||
@@ -20,17 +20,16 @@ import android.annotation.SuppressLint
|
||||
import android.annotation.UiThread
|
||||
import android.content.Context
|
||||
import android.graphics.PixelFormat
|
||||
import android.graphics.Point
|
||||
import android.graphics.Rect
|
||||
import android.hardware.biometrics.BiometricOverlayConstants
|
||||
import android.hardware.biometrics.BiometricOverlayConstants.REASON_ENROLL_ENROLLING
|
||||
import android.hardware.biometrics.BiometricOverlayConstants.REASON_ENROLL_FIND_SENSOR
|
||||
import android.hardware.biometrics.BiometricOverlayConstants.ShowReason
|
||||
import android.hardware.biometrics.SensorLocationInternal
|
||||
import android.hardware.fingerprint.FingerprintManager
|
||||
import android.hardware.fingerprint.FingerprintSensorPropertiesInternal
|
||||
import android.hardware.fingerprint.IUdfpsOverlayControllerCallback
|
||||
import android.os.RemoteException
|
||||
import android.util.Log
|
||||
import android.util.RotationUtils
|
||||
import android.view.LayoutInflater
|
||||
import android.view.MotionEvent
|
||||
import android.view.Surface
|
||||
@@ -78,7 +77,6 @@ class UdfpsControllerOverlay(
|
||||
private val systemClock: SystemClock,
|
||||
private val keyguardStateController: KeyguardStateController,
|
||||
private val unlockedScreenOffAnimationController: UnlockedScreenOffAnimationController,
|
||||
private val sensorProps: FingerprintSensorPropertiesInternal,
|
||||
private var hbmProvider: UdfpsHbmProvider,
|
||||
val requestId: Long,
|
||||
@ShowReason val requestReason: Int,
|
||||
@@ -90,6 +88,8 @@ class UdfpsControllerOverlay(
|
||||
var overlayView: UdfpsView? = null
|
||||
private set
|
||||
|
||||
private var overlayParams: UdfpsOverlayParams = UdfpsOverlayParams()
|
||||
|
||||
private var overlayTouchListener: TouchExplorationStateChangeListener? = null
|
||||
|
||||
private val coreLayoutParams = WindowManager.LayoutParams(
|
||||
@@ -101,7 +101,11 @@ class UdfpsControllerOverlay(
|
||||
fitInsetsTypes = 0
|
||||
gravity = android.view.Gravity.TOP or android.view.Gravity.LEFT
|
||||
layoutInDisplayCutoutMode = WindowManager.LayoutParams.LAYOUT_IN_DISPLAY_CUTOUT_MODE_ALWAYS
|
||||
flags =
|
||||
(Utils.FINGERPRINT_OVERLAY_LAYOUT_PARAM_FLAGS or WindowManager.LayoutParams.FLAG_SPLIT_TOUCH)
|
||||
privateFlags = WindowManager.LayoutParams.PRIVATE_FLAG_TRUSTED_OVERLAY
|
||||
// Avoid announcing window title.
|
||||
accessibilityTitle = " "
|
||||
}
|
||||
|
||||
/** A helper if the [requestReason] was due to enrollment. */
|
||||
@@ -125,13 +129,14 @@ class UdfpsControllerOverlay(
|
||||
|
||||
/** Show the overlay or return false and do nothing if it is already showing. */
|
||||
@SuppressLint("ClickableViewAccessibility")
|
||||
fun show(controller: UdfpsController): Boolean {
|
||||
fun show(controller: UdfpsController, params: UdfpsOverlayParams): Boolean {
|
||||
if (overlayView == null) {
|
||||
overlayParams = params
|
||||
try {
|
||||
overlayView = (inflater.inflate(
|
||||
R.layout.udfps_view, null, false
|
||||
) as UdfpsView).apply {
|
||||
sensorProperties = sensorProps
|
||||
overlayParams = params
|
||||
setHbmProvider(hbmProvider)
|
||||
val animation = inflateUdfpsAnimation(this, controller)
|
||||
if (animation != null) {
|
||||
@@ -144,8 +149,7 @@ class UdfpsControllerOverlay(
|
||||
importantForAccessibility = View.IMPORTANT_FOR_ACCESSIBILITY_NO
|
||||
}
|
||||
|
||||
windowManager.addView(this,
|
||||
coreLayoutParams.updateForLocation(sensorProps.location, animation))
|
||||
windowManager.addView(this, coreLayoutParams.updateDimensions(animation))
|
||||
|
||||
overlayTouchListener = TouchExplorationStateChangeListener {
|
||||
if (accessibilityManager.isTouchExplorationEnabled) {
|
||||
@@ -180,13 +184,14 @@ class UdfpsControllerOverlay(
|
||||
REASON_ENROLL_ENROLLING -> {
|
||||
UdfpsEnrollViewController(
|
||||
view.addUdfpsView(R.layout.udfps_enroll_view) {
|
||||
updateSensorLocation(sensorProps)
|
||||
updateSensorLocation(overlayParams.sensorBounds)
|
||||
},
|
||||
enrollHelper ?: throw IllegalStateException("no enrollment helper"),
|
||||
statusBarStateController,
|
||||
panelExpansionStateManager,
|
||||
dialogManager,
|
||||
dumpManager
|
||||
dumpManager,
|
||||
overlayParams.scaleFactor
|
||||
)
|
||||
}
|
||||
BiometricOverlayConstants.REASON_AUTH_KEYGUARD -> {
|
||||
@@ -280,57 +285,42 @@ class UdfpsControllerOverlay(
|
||||
/** Checks if the id is relevant for this overlay. */
|
||||
fun matchesRequestId(id: Long): Boolean = requestId == -1L || requestId == id
|
||||
|
||||
private fun WindowManager.LayoutParams.updateForLocation(
|
||||
location: SensorLocationInternal,
|
||||
private fun WindowManager.LayoutParams.updateDimensions(
|
||||
animation: UdfpsAnimationViewController<*>?
|
||||
): WindowManager.LayoutParams {
|
||||
val paddingX = animation?.paddingX ?: 0
|
||||
val paddingY = animation?.paddingY ?: 0
|
||||
flags = (Utils.FINGERPRINT_OVERLAY_LAYOUT_PARAM_FLAGS
|
||||
or WindowManager.LayoutParams.FLAG_SPLIT_TOUCH)
|
||||
if (animation != null && animation.listenForTouchesOutsideView()) {
|
||||
flags = flags or WindowManager.LayoutParams.FLAG_WATCH_OUTSIDE_TOUCH
|
||||
}
|
||||
|
||||
// Default dimensions assume portrait mode.
|
||||
x = location.sensorLocationX - location.sensorRadius - paddingX
|
||||
y = location.sensorLocationY - location.sensorRadius - paddingY
|
||||
height = 2 * location.sensorRadius + 2 * paddingX
|
||||
width = 2 * location.sensorRadius + 2 * paddingY
|
||||
// Original sensorBounds assume portrait mode.
|
||||
val rotatedSensorBounds = Rect(overlayParams.sensorBounds)
|
||||
|
||||
// Gets the size based on the current rotation of the display.
|
||||
val p = Point()
|
||||
context.display!!.getRealSize(p)
|
||||
when (context.display!!.rotation) {
|
||||
Surface.ROTATION_90 -> {
|
||||
if (!shouldRotate(animation)) {
|
||||
Log.v(TAG, "skip rotating udfps location ROTATION_90" +
|
||||
val rot = overlayParams.rotation
|
||||
if (rot == Surface.ROTATION_90 || rot == Surface.ROTATION_270) {
|
||||
if (!shouldRotate(animation)) {
|
||||
Log.v(
|
||||
TAG, "Skip rotating UDFPS bounds " + Surface.rotationToString(rot) +
|
||||
" animation=$animation" +
|
||||
" isGoingToSleep=${keyguardUpdateMonitor.isGoingToSleep}" +
|
||||
" isOccluded=${keyguardStateController.isOccluded}")
|
||||
} else {
|
||||
Log.v(TAG, "rotate udfps location ROTATION_90")
|
||||
x = (location.sensorLocationY - location.sensorRadius - paddingX)
|
||||
y = (p.y - location.sensorLocationX - location.sensorRadius - paddingY)
|
||||
}
|
||||
" isOccluded=${keyguardStateController.isOccluded}"
|
||||
)
|
||||
} else {
|
||||
Log.v(TAG, "Rotate UDFPS bounds " + Surface.rotationToString(rot))
|
||||
RotationUtils.rotateBounds(
|
||||
rotatedSensorBounds,
|
||||
overlayParams.naturalDisplayWidth,
|
||||
overlayParams.naturalDisplayHeight,
|
||||
rot
|
||||
)
|
||||
}
|
||||
Surface.ROTATION_270 -> {
|
||||
if (!shouldRotate(animation)) {
|
||||
Log.v(TAG, "skip rotating udfps location ROTATION_270" +
|
||||
" animation=$animation" +
|
||||
" isGoingToSleep=${keyguardUpdateMonitor.isGoingToSleep}" +
|
||||
" isOccluded=${keyguardStateController.isOccluded}")
|
||||
} else {
|
||||
Log.v(TAG, "rotate udfps location ROTATION_270")
|
||||
x = (p.x - location.sensorLocationY - location.sensorRadius - paddingX)
|
||||
y = (location.sensorLocationX - location.sensorRadius - paddingY)
|
||||
}
|
||||
}
|
||||
else -> {}
|
||||
}
|
||||
|
||||
// avoid announcing window title
|
||||
accessibilityTitle = " "
|
||||
x = rotatedSensorBounds.left - paddingX
|
||||
y = rotatedSensorBounds.top - paddingY
|
||||
height = rotatedSensorBounds.height() + 2 * paddingX
|
||||
width = rotatedSensorBounds.width() + 2 * paddingY
|
||||
|
||||
return this
|
||||
}
|
||||
@@ -363,5 +353,5 @@ private fun Int.isEnrollmentReason() =
|
||||
@ShowReason
|
||||
private fun Int.isImportantForAccessibility() =
|
||||
this == REASON_ENROLL_FIND_SENSOR ||
|
||||
this == REASON_ENROLL_ENROLLING ||
|
||||
this == BiometricOverlayConstants.REASON_AUTH_BP
|
||||
this == REASON_ENROLL_ENROLLING ||
|
||||
this == BiometricOverlayConstants.REASON_AUTH_BP
|
||||
|
||||
@@ -17,7 +17,7 @@
|
||||
package com.android.systemui.biometrics;
|
||||
|
||||
import android.content.Context;
|
||||
import android.hardware.fingerprint.FingerprintSensorPropertiesInternal;
|
||||
import android.graphics.Rect;
|
||||
import android.os.Handler;
|
||||
import android.os.Looper;
|
||||
import android.util.AttributeSet;
|
||||
@@ -61,13 +61,11 @@ public class UdfpsEnrollView extends UdfpsAnimationView {
|
||||
return mFingerprintDrawable;
|
||||
}
|
||||
|
||||
void updateSensorLocation(@NonNull FingerprintSensorPropertiesInternal sensorProps) {
|
||||
void updateSensorLocation(@NonNull Rect sensorBounds) {
|
||||
View fingerprintAccessibilityView = findViewById(R.id.udfps_enroll_accessibility_view);
|
||||
final int sensorHeight = sensorProps.getLocation().sensorRadius * 2;
|
||||
final int sensorWidth = sensorHeight;
|
||||
ViewGroup.LayoutParams params = fingerprintAccessibilityView.getLayoutParams();
|
||||
params.width = sensorWidth;
|
||||
params.height = sensorHeight;
|
||||
params.width = sensorBounds.width();
|
||||
params.height = sensorBounds.height();
|
||||
fingerprintAccessibilityView.setLayoutParams(params);
|
||||
fingerprintAccessibilityView.requestLayout();
|
||||
}
|
||||
|
||||
@@ -56,11 +56,12 @@ public class UdfpsEnrollViewController extends UdfpsAnimationViewController<Udfp
|
||||
@NonNull StatusBarStateController statusBarStateController,
|
||||
@NonNull PanelExpansionStateManager panelExpansionStateManager,
|
||||
@NonNull SystemUIDialogManager systemUIDialogManager,
|
||||
@NonNull DumpManager dumpManager) {
|
||||
@NonNull DumpManager dumpManager,
|
||||
float scaleFactor) {
|
||||
super(view, statusBarStateController, panelExpansionStateManager, systemUIDialogManager,
|
||||
dumpManager);
|
||||
mEnrollProgressBarRadius = getContext().getResources()
|
||||
.getInteger(R.integer.config_udfpsEnrollProgressBar);
|
||||
mEnrollProgressBarRadius = (int) (scaleFactor * getContext().getResources().getInteger(
|
||||
R.integer.config_udfpsEnrollProgressBar));
|
||||
mEnrollHelper = enrollHelper;
|
||||
mView.setEnrollHelper(mEnrollHelper);
|
||||
}
|
||||
|
||||
@@ -0,0 +1,43 @@
|
||||
package com.android.systemui.biometrics
|
||||
|
||||
import android.graphics.Rect
|
||||
import android.view.Surface
|
||||
import android.view.Surface.Rotation
|
||||
|
||||
/**
|
||||
* Collection of parameters that define an under-display fingerprint sensor (UDFPS) overlay.
|
||||
*
|
||||
* @property sensorBounds coordinates of the bounding box around the sensor, in natural orientation,
|
||||
* in pixels, for the current resolution.
|
||||
* @property naturalDisplayWidth width of the physical display, in natural orientation, in pixels,
|
||||
* for the current resolution.
|
||||
* @property naturalDisplayHeight height of the physical display, in natural orientation, in pixels,
|
||||
* for the current resolution.
|
||||
* @property scaleFactor ratio of a dimension in the current resolution to the corresponding
|
||||
* dimension in the native resolution.
|
||||
* @property rotation current rotation of the display.
|
||||
*/
|
||||
|
||||
data class UdfpsOverlayParams(
|
||||
val sensorBounds: Rect = Rect(),
|
||||
val naturalDisplayWidth: Int = 0,
|
||||
val naturalDisplayHeight: Int = 0,
|
||||
val scaleFactor: Float = 1f,
|
||||
@Rotation val rotation: Int = Surface.ROTATION_0
|
||||
) {
|
||||
/** See [android.view.DisplayInfo.logicalWidth] */
|
||||
val logicalDisplayWidth
|
||||
get() = if (rotation == Surface.ROTATION_90 || rotation == Surface.ROTATION_270) {
|
||||
naturalDisplayHeight
|
||||
} else {
|
||||
naturalDisplayWidth
|
||||
}
|
||||
|
||||
/** See [android.view.DisplayInfo.logicalHeight] */
|
||||
val logicalDisplayHeight
|
||||
get() = if (rotation == Surface.ROTATION_90 || rotation == Surface.ROTATION_270) {
|
||||
naturalDisplayWidth
|
||||
} else {
|
||||
naturalDisplayHeight
|
||||
}
|
||||
}
|
||||
@@ -21,7 +21,6 @@ import android.graphics.Color
|
||||
import android.graphics.Paint
|
||||
import android.graphics.PointF
|
||||
import android.graphics.RectF
|
||||
import android.hardware.fingerprint.FingerprintSensorPropertiesInternal
|
||||
import android.util.AttributeSet
|
||||
import android.util.Log
|
||||
import android.view.MotionEvent
|
||||
@@ -39,6 +38,8 @@ class UdfpsView(
|
||||
attrs: AttributeSet?
|
||||
) : FrameLayout(context, attrs), DozeReceiver, UdfpsIlluminator {
|
||||
|
||||
// sensorRect may be bigger than the sensor. True sensor dimensions are defined in
|
||||
// overlayParams.sensorBounds
|
||||
private val sensorRect = RectF()
|
||||
private var hbmProvider: UdfpsHbmProvider? = null
|
||||
private val debugTextPaint = Paint().apply {
|
||||
@@ -62,8 +63,8 @@ class UdfpsView(
|
||||
/** View controller (can be different for enrollment, BiometricPrompt, Keyguard, etc.). */
|
||||
var animationViewController: UdfpsAnimationViewController<*>? = null
|
||||
|
||||
/** Properties used to obtain the sensor location. */
|
||||
var sensorProperties: FingerprintSensorPropertiesInternal? = null
|
||||
/** Parameters that affect the position and size of the overlay. Visible for testing. */
|
||||
var overlayParams = UdfpsOverlayParams()
|
||||
|
||||
/** Debug message. */
|
||||
var debugMessage: String? = null
|
||||
@@ -94,13 +95,12 @@ class UdfpsView(
|
||||
|
||||
val paddingX = animationViewController?.paddingX ?: 0
|
||||
val paddingY = animationViewController?.paddingY ?: 0
|
||||
val sensorRadius = sensorProperties?.location?.sensorRadius ?: 0
|
||||
|
||||
sensorRect.set(
|
||||
paddingX.toFloat(),
|
||||
paddingY.toFloat(),
|
||||
(2 * sensorRadius + paddingX).toFloat(),
|
||||
(2 * sensorRadius + paddingY).toFloat()
|
||||
(overlayParams.sensorBounds.width() + paddingX).toFloat(),
|
||||
(overlayParams.sensorBounds.height() + paddingY).toFloat()
|
||||
)
|
||||
animationViewController?.onSensorRectUpdated(RectF(sensorRect))
|
||||
}
|
||||
|
||||
@@ -27,6 +27,7 @@ import static junit.framework.Assert.assertNull;
|
||||
|
||||
import static org.mockito.ArgumentMatchers.any;
|
||||
import static org.mockito.ArgumentMatchers.anyInt;
|
||||
import static org.mockito.ArgumentMatchers.anyLong;
|
||||
import static org.mockito.ArgumentMatchers.anyString;
|
||||
import static org.mockito.ArgumentMatchers.eq;
|
||||
import static org.mockito.Mockito.doAnswer;
|
||||
@@ -46,6 +47,7 @@ import android.content.Context;
|
||||
import android.content.Intent;
|
||||
import android.content.pm.PackageManager;
|
||||
import android.content.res.Configuration;
|
||||
import android.graphics.Point;
|
||||
import android.hardware.biometrics.BiometricAuthenticator;
|
||||
import android.hardware.biometrics.BiometricConstants;
|
||||
import android.hardware.biometrics.BiometricManager;
|
||||
@@ -185,6 +187,8 @@ public class AuthControllerTest extends SysuiTestCase {
|
||||
when(mDialog1.getRequestId()).thenReturn(REQUEST_ID);
|
||||
when(mDialog2.getRequestId()).thenReturn(REQUEST_ID);
|
||||
|
||||
when(mDisplayManager.getStableDisplaySize()).thenReturn(new Point());
|
||||
|
||||
when(mFingerprintManager.isHardwareDetected()).thenReturn(true);
|
||||
|
||||
final List<ComponentInfoInternal> componentInfo = new ArrayList<>();
|
||||
@@ -663,7 +667,7 @@ public class AuthControllerTest extends SysuiTestCase {
|
||||
public void testSubscribesToOrientationChangesWhenShowingDialog() {
|
||||
showDialog(new int[]{1} /* sensorIds */, false /* credentialAllowed */);
|
||||
|
||||
verify(mDisplayManager).registerDisplayListener(any(), eq(mHandler));
|
||||
verify(mDisplayManager).registerDisplayListener(any(), eq(mHandler), anyLong());
|
||||
|
||||
mAuthController.hideAuthenticationDialog(REQUEST_ID);
|
||||
verify(mDisplayManager).unregisterDisplayListener(any());
|
||||
|
||||
@@ -163,7 +163,7 @@ class AuthRippleControllerTest : SysuiTestCase() {
|
||||
fun testFingerprintTrigger_KeyguardNotVisible_NotDreaming_NoRipple() {
|
||||
// GIVEN fp exists & user doesn't need strong auth
|
||||
val fpsLocation = PointF(5f, 5f)
|
||||
`when`(authController.udfpsSensorLocation).thenReturn(fpsLocation)
|
||||
`when`(authController.udfpsLocation).thenReturn(fpsLocation)
|
||||
controller.onViewAttached()
|
||||
`when`(keyguardUpdateMonitor.userNeedsStrongAuth()).thenReturn(false)
|
||||
|
||||
@@ -185,7 +185,7 @@ class AuthRippleControllerTest : SysuiTestCase() {
|
||||
fun testFingerprintTrigger_StrongAuthRequired_NoRipple() {
|
||||
// GIVEN fp exists & keyguard is visible
|
||||
val fpsLocation = PointF(5f, 5f)
|
||||
`when`(authController.udfpsSensorLocation).thenReturn(fpsLocation)
|
||||
`when`(authController.udfpsLocation).thenReturn(fpsLocation)
|
||||
controller.onViewAttached()
|
||||
`when`(keyguardUpdateMonitor.isKeyguardVisible).thenReturn(true)
|
||||
|
||||
|
||||
@@ -20,6 +20,7 @@ import static com.android.systemui.biometrics.BiometricDisplayListener.SensorTyp
|
||||
import static com.android.systemui.biometrics.BiometricDisplayListener.SensorType.UnderDisplayFingerprint;
|
||||
|
||||
import static org.mockito.ArgumentMatchers.any;
|
||||
import static org.mockito.ArgumentMatchers.eq;
|
||||
import static org.mockito.ArgumentMatchers.same;
|
||||
import static org.mockito.Mockito.never;
|
||||
import static org.mockito.Mockito.reset;
|
||||
@@ -89,7 +90,8 @@ public class BiometricDisplayListenerTest extends SysuiTestCase {
|
||||
mContextSpy, mDisplayManager, mHandler, mUdfpsType, mOnChangedCallback);
|
||||
|
||||
listener.enable();
|
||||
verify(mDisplayManager).registerDisplayListener(any(), same(mHandler));
|
||||
verify(mDisplayManager).registerDisplayListener(any(), same(mHandler),
|
||||
eq(DisplayManager.EVENT_FLAG_DISPLAY_CHANGED));
|
||||
}
|
||||
|
||||
@Test
|
||||
@@ -113,7 +115,7 @@ public class BiometricDisplayListenerTest extends SysuiTestCase {
|
||||
when(mDisplay.getRotation()).thenReturn(Surface.ROTATION_90);
|
||||
listener.enable();
|
||||
verify(mDisplayManager).registerDisplayListener(mDisplayListenerCaptor.capture(),
|
||||
same(mHandler));
|
||||
same(mHandler), eq(DisplayManager.EVENT_FLAG_DISPLAY_CHANGED));
|
||||
|
||||
// Rotate the device back to portrait and ensure the rotation is detected.
|
||||
when(mDisplay.getRotation()).thenReturn(Surface.ROTATION_0);
|
||||
@@ -150,8 +152,8 @@ public class BiometricDisplayListenerTest extends SysuiTestCase {
|
||||
|
||||
// The listener should record the current rotation and register a display listener.
|
||||
verify(mDisplay).getRotation();
|
||||
verify(mDisplayManager)
|
||||
.registerDisplayListener(mDisplayListenerCaptor.capture(), same(mHandler));
|
||||
verify(mDisplayManager).registerDisplayListener(mDisplayListenerCaptor.capture(),
|
||||
same(mHandler), eq(DisplayManager.EVENT_FLAG_DISPLAY_CHANGED));
|
||||
|
||||
// Test the first rotation since the listener was enabled.
|
||||
mDisplayListenerCaptor.getValue().onDisplayChanged(123);
|
||||
@@ -182,8 +184,8 @@ public class BiometricDisplayListenerTest extends SysuiTestCase {
|
||||
listener.enable();
|
||||
|
||||
// The listener should register a display listener.
|
||||
verify(mDisplayManager)
|
||||
.registerDisplayListener(mDisplayListenerCaptor.capture(), same(mHandler));
|
||||
verify(mDisplayManager).registerDisplayListener(mDisplayListenerCaptor.capture(),
|
||||
same(mHandler), eq(DisplayManager.EVENT_FLAG_DISPLAY_CHANGED));
|
||||
|
||||
// mOnChangedCallback should be invoked for all calls to onDisplayChanged.
|
||||
mDisplayListenerCaptor.getValue().onDisplayChanged(123);
|
||||
|
||||
@@ -188,7 +188,7 @@ class SidefpsControllerTest : SysuiTestCase() {
|
||||
overlayController.show(SENSOR_ID, REASON_UNKNOWN)
|
||||
executor.runAllReady()
|
||||
|
||||
verify(displayManager).registerDisplayListener(any(), eq(handler))
|
||||
verify(displayManager).registerDisplayListener(any(), eq(handler), anyLong())
|
||||
|
||||
overlayController.hide(SENSOR_ID)
|
||||
executor.runAllReady()
|
||||
|
||||
@@ -16,6 +16,7 @@
|
||||
|
||||
package com.android.systemui.biometrics
|
||||
|
||||
import android.graphics.Rect
|
||||
import android.hardware.biometrics.BiometricOverlayConstants.REASON_AUTH_BP
|
||||
import android.hardware.biometrics.BiometricOverlayConstants.REASON_AUTH_KEYGUARD
|
||||
import android.hardware.biometrics.BiometricOverlayConstants.REASON_AUTH_OTHER
|
||||
@@ -23,7 +24,6 @@ import android.hardware.biometrics.BiometricOverlayConstants.REASON_AUTH_SETTING
|
||||
import android.hardware.biometrics.BiometricOverlayConstants.REASON_ENROLL_ENROLLING
|
||||
import android.hardware.biometrics.BiometricOverlayConstants.REASON_ENROLL_FIND_SENSOR
|
||||
import android.hardware.biometrics.BiometricOverlayConstants.ShowReason
|
||||
import android.hardware.biometrics.SensorLocationInternal
|
||||
import android.hardware.fingerprint.FingerprintManager
|
||||
import android.hardware.fingerprint.IUdfpsOverlayControllerCallback
|
||||
import android.testing.AndroidTestingRunner
|
||||
@@ -31,6 +31,8 @@ import android.testing.TestableLooper.RunWithLooper
|
||||
import android.view.LayoutInflater
|
||||
import android.view.MotionEvent
|
||||
import android.view.View
|
||||
import android.view.Surface
|
||||
import android.view.Surface.Rotation
|
||||
import android.view.WindowManager
|
||||
import android.view.accessibility.AccessibilityManager
|
||||
import androidx.test.filters.SmallTest
|
||||
@@ -53,8 +55,10 @@ import org.junit.Before
|
||||
import org.junit.Rule
|
||||
import org.junit.Test
|
||||
import org.junit.runner.RunWith
|
||||
import org.mockito.ArgumentCaptor
|
||||
import org.mockito.ArgumentMatchers.any
|
||||
import org.mockito.ArgumentMatchers.eq
|
||||
import org.mockito.Captor
|
||||
import org.mockito.Mock
|
||||
import org.mockito.Mockito.mock
|
||||
import org.mockito.Mockito.verify
|
||||
@@ -63,13 +67,18 @@ import org.mockito.Mockito.`when` as whenever
|
||||
|
||||
private const val REQUEST_ID = 2L
|
||||
|
||||
// Dimensions for the current display resolution.
|
||||
private const val DISPLAY_WIDTH = 1080
|
||||
private const val DISPLAY_HEIGHT = 1920
|
||||
private const val SENSOR_WIDTH = 30
|
||||
private const val SENSOR_HEIGHT = 60
|
||||
|
||||
@SmallTest
|
||||
@RunWith(AndroidTestingRunner::class)
|
||||
@RunWithLooper(setAsMainLooper = true)
|
||||
class UdfpsControllerOverlayTest : SysuiTestCase() {
|
||||
|
||||
@JvmField @Rule
|
||||
var rule = MockitoJUnit.rule()
|
||||
@JvmField @Rule var rule = MockitoJUnit.rule()
|
||||
|
||||
@Mock private lateinit var fingerprintManager: FingerprintManager
|
||||
@Mock private lateinit var inflater: LayoutInflater
|
||||
@@ -85,18 +94,17 @@ class UdfpsControllerOverlayTest : SysuiTestCase() {
|
||||
@Mock private lateinit var configurationController: ConfigurationController
|
||||
@Mock private lateinit var systemClock: SystemClock
|
||||
@Mock private lateinit var keyguardStateController: KeyguardStateController
|
||||
@Mock
|
||||
private lateinit var unlockedScreenOffAnimationController: UnlockedScreenOffAnimationController
|
||||
@Mock private lateinit var unlockedScreenOffAnimationController: UnlockedScreenOffAnimationController
|
||||
@Mock private lateinit var hbmProvider: UdfpsHbmProvider
|
||||
@Mock private lateinit var controllerCallback: IUdfpsOverlayControllerCallback
|
||||
@Mock private lateinit var udfpsController: UdfpsController
|
||||
@Mock private lateinit var udfpsView: UdfpsView
|
||||
@Mock private lateinit var udfpsEnrollView: UdfpsEnrollView
|
||||
@Mock private lateinit var activityLaunchAnimator: ActivityLaunchAnimator
|
||||
@Captor private lateinit var layoutParamsCaptor: ArgumentCaptor<WindowManager.LayoutParams>
|
||||
|
||||
private val sensorProps = SensorLocationInternal("", 10, 100, 20)
|
||||
.asFingerprintSensorProperties()
|
||||
private val onTouch = { _: View, _: MotionEvent, _: Boolean -> true }
|
||||
private var overlayParams: UdfpsOverlayParams = UdfpsOverlayParams()
|
||||
private lateinit var controllerOverlay: UdfpsControllerOverlay
|
||||
|
||||
@Before
|
||||
@@ -121,7 +129,7 @@ class UdfpsControllerOverlayTest : SysuiTestCase() {
|
||||
statusBarStateController, panelExpansionStateManager, statusBarKeyguardViewManager,
|
||||
keyguardUpdateMonitor, dialogManager, dumpManager, transitionController,
|
||||
configurationController, systemClock, keyguardStateController,
|
||||
unlockedScreenOffAnimationController, sensorProps, hbmProvider, REQUEST_ID, reason,
|
||||
unlockedScreenOffAnimationController, hbmProvider, REQUEST_ID, reason,
|
||||
controllerCallback, onTouch, activityLaunchAnimator)
|
||||
block()
|
||||
}
|
||||
@@ -148,12 +156,96 @@ class UdfpsControllerOverlayTest : SysuiTestCase() {
|
||||
@Test
|
||||
fun showUdfpsOverlay_other() = withReason(REASON_AUTH_OTHER) { showUdfpsOverlay() }
|
||||
|
||||
private fun withRotation(@Rotation rotation: Int, block: () -> Unit) {
|
||||
// Sensor that's in the top left corner of the display in natural orientation.
|
||||
val sensorBounds = Rect(0, 0, SENSOR_WIDTH, SENSOR_HEIGHT)
|
||||
overlayParams = UdfpsOverlayParams(
|
||||
sensorBounds,
|
||||
DISPLAY_WIDTH,
|
||||
DISPLAY_HEIGHT,
|
||||
scaleFactor = 1f,
|
||||
rotation
|
||||
)
|
||||
block()
|
||||
}
|
||||
|
||||
@Test
|
||||
fun showUdfpsOverlay_withRotation0() = withRotation(Surface.ROTATION_0) {
|
||||
withReason(REASON_AUTH_BP) {
|
||||
controllerOverlay.show(udfpsController, overlayParams)
|
||||
verify(windowManager).addView(
|
||||
eq(controllerOverlay.overlayView),
|
||||
layoutParamsCaptor.capture()
|
||||
)
|
||||
|
||||
// ROTATION_0 is the native orientation. Sensor should stay in the top left corner.
|
||||
val lp = layoutParamsCaptor.value
|
||||
assertThat(lp.x).isEqualTo(0)
|
||||
assertThat(lp.y).isEqualTo(0)
|
||||
assertThat(lp.width).isEqualTo(SENSOR_WIDTH)
|
||||
assertThat(lp.height).isEqualTo(SENSOR_HEIGHT)
|
||||
}
|
||||
}
|
||||
|
||||
@Test
|
||||
fun showUdfpsOverlay_withRotation180() = withRotation(Surface.ROTATION_180) {
|
||||
withReason(REASON_AUTH_BP) {
|
||||
controllerOverlay.show(udfpsController, overlayParams)
|
||||
verify(windowManager).addView(
|
||||
eq(controllerOverlay.overlayView),
|
||||
layoutParamsCaptor.capture()
|
||||
)
|
||||
|
||||
// ROTATION_180 is not supported. Sensor should stay in the top left corner.
|
||||
val lp = layoutParamsCaptor.value
|
||||
assertThat(lp.x).isEqualTo(0)
|
||||
assertThat(lp.y).isEqualTo(0)
|
||||
assertThat(lp.width).isEqualTo(SENSOR_WIDTH)
|
||||
assertThat(lp.height).isEqualTo(SENSOR_HEIGHT)
|
||||
}
|
||||
}
|
||||
|
||||
@Test
|
||||
fun showUdfpsOverlay_withRotation90() = withRotation(Surface.ROTATION_90) {
|
||||
withReason(REASON_AUTH_BP) {
|
||||
controllerOverlay.show(udfpsController, overlayParams)
|
||||
verify(windowManager).addView(
|
||||
eq(controllerOverlay.overlayView),
|
||||
layoutParamsCaptor.capture()
|
||||
)
|
||||
|
||||
// Sensor should be in the bottom left corner in ROTATION_90.
|
||||
val lp = layoutParamsCaptor.value
|
||||
assertThat(lp.x).isEqualTo(0)
|
||||
assertThat(lp.y).isEqualTo(DISPLAY_WIDTH - SENSOR_WIDTH)
|
||||
assertThat(lp.width).isEqualTo(SENSOR_HEIGHT)
|
||||
assertThat(lp.height).isEqualTo(SENSOR_WIDTH)
|
||||
}
|
||||
}
|
||||
|
||||
@Test
|
||||
fun showUdfpsOverlay_withRotation270() = withRotation(Surface.ROTATION_270) {
|
||||
withReason(REASON_AUTH_BP) {
|
||||
controllerOverlay.show(udfpsController, overlayParams)
|
||||
verify(windowManager).addView(
|
||||
eq(controllerOverlay.overlayView),
|
||||
layoutParamsCaptor.capture()
|
||||
)
|
||||
|
||||
// Sensor should be in the top right corner in ROTATION_270.
|
||||
val lp = layoutParamsCaptor.value
|
||||
assertThat(lp.x).isEqualTo(DISPLAY_HEIGHT - SENSOR_HEIGHT)
|
||||
assertThat(lp.y).isEqualTo(0)
|
||||
assertThat(lp.width).isEqualTo(SENSOR_HEIGHT)
|
||||
assertThat(lp.height).isEqualTo(SENSOR_WIDTH)
|
||||
}
|
||||
}
|
||||
|
||||
private fun showUdfpsOverlay(isEnrollUseCase: Boolean = false) {
|
||||
val didShow = controllerOverlay.show(udfpsController)
|
||||
val didShow = controllerOverlay.show(udfpsController, overlayParams)
|
||||
|
||||
verify(windowManager).addView(eq(controllerOverlay.overlayView), any())
|
||||
verify(udfpsView).setHbmProvider(eq(hbmProvider))
|
||||
verify(udfpsView).sensorProperties = eq(sensorProps)
|
||||
verify(udfpsView).animationViewController = any()
|
||||
verify(udfpsView).addView(any())
|
||||
|
||||
@@ -162,7 +254,7 @@ class UdfpsControllerOverlayTest : SysuiTestCase() {
|
||||
assertThat(controllerOverlay.isHiding).isFalse()
|
||||
assertThat(controllerOverlay.overlayView).isNotNull()
|
||||
if (isEnrollUseCase) {
|
||||
verify(udfpsEnrollView).updateSensorLocation(eq(sensorProps))
|
||||
verify(udfpsEnrollView).updateSensorLocation(eq(overlayParams.sensorBounds))
|
||||
assertThat(controllerOverlay.enrollHelper).isNotNull()
|
||||
} else {
|
||||
assertThat(controllerOverlay.enrollHelper).isNull()
|
||||
@@ -188,7 +280,7 @@ class UdfpsControllerOverlayTest : SysuiTestCase() {
|
||||
fun hideUdfpsOverlay_other() = withReason(REASON_AUTH_OTHER) { hideUdfpsOverlay() }
|
||||
|
||||
private fun hideUdfpsOverlay() {
|
||||
val didShow = controllerOverlay.show(udfpsController)
|
||||
val didShow = controllerOverlay.show(udfpsController, overlayParams)
|
||||
val view = controllerOverlay.overlayView
|
||||
val didHide = controllerOverlay.hide()
|
||||
|
||||
@@ -209,13 +301,13 @@ class UdfpsControllerOverlayTest : SysuiTestCase() {
|
||||
|
||||
@Test
|
||||
fun canNotReshow() = withReason(REASON_AUTH_BP) {
|
||||
assertThat(controllerOverlay.show(udfpsController)).isTrue()
|
||||
assertThat(controllerOverlay.show(udfpsController)).isFalse()
|
||||
assertThat(controllerOverlay.show(udfpsController, overlayParams)).isTrue()
|
||||
assertThat(controllerOverlay.show(udfpsController, overlayParams)).isFalse()
|
||||
}
|
||||
|
||||
@Test
|
||||
fun forwardEnrollProgressEvents() = withReason(REASON_ENROLL_ENROLLING) {
|
||||
controllerOverlay.show(udfpsController)
|
||||
controllerOverlay.show(udfpsController, overlayParams)
|
||||
|
||||
with(EnrollListener(controllerOverlay)) {
|
||||
controllerOverlay.onEnrollmentProgress(/* remaining */20)
|
||||
@@ -228,7 +320,7 @@ class UdfpsControllerOverlayTest : SysuiTestCase() {
|
||||
|
||||
@Test
|
||||
fun forwardEnrollHelpEvents() = withReason(REASON_ENROLL_ENROLLING) {
|
||||
controllerOverlay.show(udfpsController)
|
||||
controllerOverlay.show(udfpsController, overlayParams)
|
||||
|
||||
with(EnrollListener(controllerOverlay)) {
|
||||
controllerOverlay.onEnrollmentHelp()
|
||||
@@ -240,7 +332,7 @@ class UdfpsControllerOverlayTest : SysuiTestCase() {
|
||||
|
||||
@Test
|
||||
fun forwardEnrollAcquiredEvents() = withReason(REASON_ENROLL_ENROLLING) {
|
||||
controllerOverlay.show(udfpsController)
|
||||
controllerOverlay.show(udfpsController, overlayParams)
|
||||
|
||||
with(EnrollListener(controllerOverlay)) {
|
||||
controllerOverlay.onEnrollmentProgress(/* remaining */ 1)
|
||||
@@ -261,7 +353,7 @@ class UdfpsControllerOverlayTest : SysuiTestCase() {
|
||||
fun stopIlluminatingOnHide() = withReason(REASON_AUTH_BP) {
|
||||
whenever(udfpsView.isIlluminationRequested).thenReturn(true)
|
||||
|
||||
controllerOverlay.show(udfpsController)
|
||||
controllerOverlay.show(udfpsController, overlayParams)
|
||||
controllerOverlay.hide()
|
||||
verify(udfpsView).stopIllumination()
|
||||
}
|
||||
|
||||
@@ -16,6 +16,9 @@
|
||||
|
||||
package com.android.systemui.biometrics;
|
||||
|
||||
import static android.view.MotionEvent.ACTION_DOWN;
|
||||
import static android.view.MotionEvent.ACTION_MOVE;
|
||||
|
||||
import static junit.framework.Assert.assertEquals;
|
||||
|
||||
import static org.mockito.ArgumentMatchers.any;
|
||||
@@ -28,11 +31,12 @@ import static org.mockito.ArgumentMatchers.eq;
|
||||
import static org.mockito.Mockito.inOrder;
|
||||
import static org.mockito.Mockito.mock;
|
||||
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.res.TypedArray;
|
||||
import android.graphics.Rect;
|
||||
import android.hardware.biometrics.BiometricOverlayConstants;
|
||||
import android.hardware.biometrics.ComponentInfoInternal;
|
||||
import android.hardware.biometrics.SensorProperties;
|
||||
@@ -50,6 +54,7 @@ import android.testing.AndroidTestingRunner;
|
||||
import android.testing.TestableLooper.RunWithLooper;
|
||||
import android.view.LayoutInflater;
|
||||
import android.view.MotionEvent;
|
||||
import android.view.Surface;
|
||||
import android.view.View;
|
||||
import android.view.WindowManager;
|
||||
import android.view.accessibility.AccessibilityManager;
|
||||
@@ -67,7 +72,6 @@ import com.android.systemui.plugins.FalsingManager;
|
||||
import com.android.systemui.plugins.statusbar.StatusBarStateController;
|
||||
import com.android.systemui.statusbar.LockscreenShadeTransitionController;
|
||||
import com.android.systemui.statusbar.VibratorHelper;
|
||||
import com.android.systemui.statusbar.phone.CentralSurfaces;
|
||||
import com.android.systemui.statusbar.phone.StatusBarKeyguardViewManager;
|
||||
import com.android.systemui.statusbar.phone.SystemUIDialogManager;
|
||||
import com.android.systemui.statusbar.phone.UnlockedScreenOffAnimationController;
|
||||
@@ -124,8 +128,6 @@ public class UdfpsControllerTest extends SysuiTestCase {
|
||||
@Mock
|
||||
private StatusBarStateController mStatusBarStateController;
|
||||
@Mock
|
||||
private CentralSurfaces mCentralSurfaces;
|
||||
@Mock
|
||||
private StatusBarKeyguardViewManager mStatusBarKeyguardViewManager;
|
||||
@Mock
|
||||
private DumpManager mDumpManager;
|
||||
@@ -174,18 +176,14 @@ public class UdfpsControllerTest extends SysuiTestCase {
|
||||
private UdfpsFpmOtherView mFpmOtherView;
|
||||
@Mock
|
||||
private UdfpsKeyguardView mKeyguardView;
|
||||
private UdfpsAnimationViewController mUdfpsKeyguardViewController =
|
||||
private final UdfpsAnimationViewController mUdfpsKeyguardViewController =
|
||||
mock(UdfpsKeyguardViewController.class);
|
||||
@Mock
|
||||
private TypedArray mBrightnessValues;
|
||||
@Mock
|
||||
private TypedArray mBrightnessBacklight;
|
||||
@Mock
|
||||
private SystemUIDialogManager mSystemUIDialogManager;
|
||||
@Mock
|
||||
private ActivityLaunchAnimator mActivityLaunchAnimator;
|
||||
@Mock
|
||||
private AlternateUdfpsTouchProvider mTouchProvider;
|
||||
private AlternateUdfpsTouchProvider mAlternateTouchProvider;
|
||||
|
||||
// Capture listeners so that they can be used to send events
|
||||
@Captor private ArgumentCaptor<IUdfpsOverlayController> mOverlayCaptor;
|
||||
@@ -198,7 +196,6 @@ public class UdfpsControllerTest extends SysuiTestCase {
|
||||
|
||||
@Before
|
||||
public void setUp() {
|
||||
setUpResources();
|
||||
mExecution = new FakeExecution();
|
||||
|
||||
when(mLayoutInflater.inflate(R.layout.udfps_view, null, false))
|
||||
@@ -260,22 +257,12 @@ public class UdfpsControllerTest extends SysuiTestCase {
|
||||
mSystemUIDialogManager,
|
||||
mLatencyTracker,
|
||||
mActivityLaunchAnimator,
|
||||
Optional.of(mTouchProvider));
|
||||
Optional.of(mAlternateTouchProvider));
|
||||
verify(mFingerprintManager).setUdfpsOverlayController(mOverlayCaptor.capture());
|
||||
mOverlayController = mOverlayCaptor.getValue();
|
||||
verify(mScreenLifecycle).addObserver(mScreenObserverCaptor.capture());
|
||||
mScreenObserver = mScreenObserverCaptor.getValue();
|
||||
|
||||
assertEquals(TEST_UDFPS_SENSOR_ID, mUdfpsController.mSensorProps.sensorId);
|
||||
}
|
||||
|
||||
private void setUpResources() {
|
||||
when(mBrightnessValues.length()).thenReturn(2);
|
||||
when(mBrightnessValues.getFloat(0, PowerManager.BRIGHTNESS_OFF_FLOAT)).thenReturn(1f);
|
||||
when(mBrightnessValues.getFloat(1, PowerManager.BRIGHTNESS_OFF_FLOAT)).thenReturn(2f);
|
||||
when(mBrightnessBacklight.length()).thenReturn(2);
|
||||
when(mBrightnessBacklight.getFloat(0, PowerManager.BRIGHTNESS_OFF_FLOAT)).thenReturn(1f);
|
||||
when(mBrightnessBacklight.getFloat(1, PowerManager.BRIGHTNESS_OFF_FLOAT)).thenReturn(2f);
|
||||
mUdfpsController.updateOverlayParams(TEST_UDFPS_SENSOR_ID, new UdfpsOverlayParams());
|
||||
}
|
||||
|
||||
@Test
|
||||
@@ -301,7 +288,7 @@ public class UdfpsControllerTest extends SysuiTestCase {
|
||||
|
||||
// WHEN ACTION_DOWN is received
|
||||
verify(mUdfpsView).setOnTouchListener(mTouchListenerCaptor.capture());
|
||||
MotionEvent downEvent = MotionEvent.obtain(0, 0, MotionEvent.ACTION_DOWN, 0, 0, 0);
|
||||
MotionEvent downEvent = MotionEvent.obtain(0, 0, ACTION_DOWN, 0, 0, 0);
|
||||
mTouchListenerCaptor.getValue().onTouch(mUdfpsView, downEvent);
|
||||
downEvent.recycle();
|
||||
|
||||
@@ -362,7 +349,7 @@ public class UdfpsControllerTest extends SysuiTestCase {
|
||||
|
||||
// WHEN multiple touches are received
|
||||
verify(mUdfpsView).setOnTouchListener(mTouchListenerCaptor.capture());
|
||||
MotionEvent downEvent = MotionEvent.obtain(0, 0, MotionEvent.ACTION_DOWN, 0, 0, 0);
|
||||
MotionEvent downEvent = MotionEvent.obtain(0, 0, ACTION_DOWN, 0, 0, 0);
|
||||
mTouchListenerCaptor.getValue().onTouch(mUdfpsView, downEvent);
|
||||
downEvent.recycle();
|
||||
MotionEvent moveEvent = MotionEvent.obtain(0, 0, MotionEvent.ACTION_MOVE, 0, 0, 0);
|
||||
@@ -395,7 +382,7 @@ public class UdfpsControllerTest extends SysuiTestCase {
|
||||
BiometricOverlayConstants.REASON_AUTH_KEYGUARD, mUdfpsOverlayControllerCallback);
|
||||
mFgExecutor.runAllReady();
|
||||
|
||||
verify(mDisplayManager).registerDisplayListener(any(), eq(mHandler));
|
||||
verify(mDisplayManager).registerDisplayListener(any(), eq(mHandler), anyLong());
|
||||
|
||||
mOverlayController.hideUdfpsOverlay(TEST_UDFPS_SENSOR_ID);
|
||||
mFgExecutor.runAllReady();
|
||||
@@ -403,6 +390,175 @@ public class UdfpsControllerTest extends SysuiTestCase {
|
||||
verify(mDisplayManager).unregisterDisplayListener(any());
|
||||
}
|
||||
|
||||
@Test
|
||||
public void updateOverlayParams_recreatesOverlay_ifParamsChanged() throws Exception {
|
||||
final Rect[] sensorBounds = new Rect[]{new Rect(10, 10, 20, 20), new Rect(5, 5, 25, 25)};
|
||||
final int[] displayWidth = new int[]{1080, 1440};
|
||||
final int[] displayHeight = new int[]{1920, 2560};
|
||||
final float[] scaleFactor = new float[]{1f, displayHeight[1] / (float) displayHeight[0]};
|
||||
final int[] rotation = new int[]{Surface.ROTATION_0, Surface.ROTATION_90};
|
||||
final UdfpsOverlayParams oldParams = new UdfpsOverlayParams(sensorBounds[0],
|
||||
displayWidth[0], displayHeight[0], scaleFactor[0], rotation[0]);
|
||||
|
||||
for (int i1 = 0; i1 <= 1; ++i1)
|
||||
for (int i2 = 0; i2 <= 1; ++i2)
|
||||
for (int i3 = 0; i3 <= 1; ++i3)
|
||||
for (int i4 = 0; i4 <= 1; ++i4)
|
||||
for (int i5 = 0; i5 <= 1; ++i5) {
|
||||
final UdfpsOverlayParams newParams = new UdfpsOverlayParams(sensorBounds[i1],
|
||||
displayWidth[i2], displayHeight[i3], scaleFactor[i4], rotation[i5]);
|
||||
|
||||
if (newParams.equals(oldParams)) {
|
||||
continue;
|
||||
}
|
||||
|
||||
// Initialize the overlay with old parameters.
|
||||
mUdfpsController.updateOverlayParams(TEST_UDFPS_SENSOR_ID, oldParams);
|
||||
|
||||
// Show the overlay.
|
||||
reset(mWindowManager);
|
||||
mOverlayController.showUdfpsOverlay(TEST_REQUEST_ID, TEST_UDFPS_SENSOR_ID,
|
||||
BiometricOverlayConstants.REASON_ENROLL_ENROLLING,
|
||||
mUdfpsOverlayControllerCallback);
|
||||
mFgExecutor.runAllReady();
|
||||
verify(mWindowManager).addView(any(), any());
|
||||
|
||||
// Update overlay parameters.
|
||||
reset(mWindowManager);
|
||||
mUdfpsController.updateOverlayParams(TEST_UDFPS_SENSOR_ID, newParams);
|
||||
mFgExecutor.runAllReady();
|
||||
|
||||
// Ensure the overlay was recreated.
|
||||
verify(mWindowManager).removeView(any());
|
||||
verify(mWindowManager).addView(any(), any());
|
||||
}
|
||||
}
|
||||
|
||||
@Test
|
||||
public void updateOverlayParams_doesNothing_ifParamsDidntChange() throws Exception {
|
||||
final Rect sensorBounds = new Rect(10, 10, 20, 20);
|
||||
final int displayWidth = 1080;
|
||||
final int displayHeight = 1920;
|
||||
final float scaleFactor = 1f;
|
||||
final int rotation = Surface.ROTATION_0;
|
||||
|
||||
// Initialize the overlay.
|
||||
mUdfpsController.updateOverlayParams(TEST_UDFPS_SENSOR_ID,
|
||||
new UdfpsOverlayParams(sensorBounds, displayWidth, displayHeight, scaleFactor,
|
||||
rotation));
|
||||
|
||||
// Show the overlay.
|
||||
mOverlayController.showUdfpsOverlay(TEST_REQUEST_ID, TEST_UDFPS_SENSOR_ID,
|
||||
BiometricOverlayConstants.REASON_ENROLL_ENROLLING, mUdfpsOverlayControllerCallback);
|
||||
mFgExecutor.runAllReady();
|
||||
verify(mWindowManager).addView(any(), any());
|
||||
|
||||
// Update overlay with the same parameters.
|
||||
mUdfpsController.updateOverlayParams(TEST_UDFPS_SENSOR_ID,
|
||||
new UdfpsOverlayParams(sensorBounds, displayWidth, displayHeight, scaleFactor,
|
||||
rotation));
|
||||
mFgExecutor.runAllReady();
|
||||
|
||||
// Ensure the overlay was not recreated.
|
||||
verify(mWindowManager, never()).removeView(any());
|
||||
}
|
||||
|
||||
private static MotionEvent obtainMotionEvent(int action, float x, float y, float minor,
|
||||
float major) {
|
||||
MotionEvent.PointerProperties pp = new MotionEvent.PointerProperties();
|
||||
pp.id = 1;
|
||||
MotionEvent.PointerCoords pc = new MotionEvent.PointerCoords();
|
||||
pc.x = x;
|
||||
pc.y = y;
|
||||
pc.touchMinor = minor;
|
||||
pc.touchMajor = major;
|
||||
return MotionEvent.obtain(0, 0, action, 1, new MotionEvent.PointerProperties[]{pp},
|
||||
new MotionEvent.PointerCoords[]{pc}, 0, 0, 1f, 1f, 0, 0, 0, 0);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void onTouch_propagatesTouchInNativeOrientationAndResolution() throws RemoteException {
|
||||
final Rect sensorBounds = new Rect(1000, 1900, 1080, 1920); // Bottom right corner.
|
||||
final int displayWidth = 1080;
|
||||
final int displayHeight = 1920;
|
||||
final float scaleFactor = 0.75f; // This means the native resolution is 1440x2560.
|
||||
final float touchMinor = 10f;
|
||||
final float touchMajor = 20f;
|
||||
|
||||
// Expecting a touch at the very bottom right corner in native orientation and resolution.
|
||||
final int expectedX = (int) (displayWidth / scaleFactor);
|
||||
final int expectedY = (int) (displayHeight / scaleFactor);
|
||||
final float expectedMinor = touchMinor / scaleFactor;
|
||||
final float expectedMajor = touchMajor / scaleFactor;
|
||||
|
||||
// Configure UdfpsView to accept the ACTION_DOWN event
|
||||
when(mUdfpsView.isIlluminationRequested()).thenReturn(false);
|
||||
when(mUdfpsView.isWithinSensorArea(anyFloat(), anyFloat())).thenReturn(true);
|
||||
|
||||
// Show the overlay.
|
||||
mOverlayController.showUdfpsOverlay(TEST_REQUEST_ID, TEST_UDFPS_SENSOR_ID,
|
||||
BiometricOverlayConstants.REASON_ENROLL_ENROLLING, mUdfpsOverlayControllerCallback);
|
||||
mFgExecutor.runAllReady();
|
||||
verify(mUdfpsView).setOnTouchListener(mTouchListenerCaptor.capture());
|
||||
|
||||
// Test ROTATION_0
|
||||
mUdfpsController.updateOverlayParams(TEST_UDFPS_SENSOR_ID,
|
||||
new UdfpsOverlayParams(sensorBounds, displayWidth, displayHeight, scaleFactor,
|
||||
Surface.ROTATION_0));
|
||||
MotionEvent event = obtainMotionEvent(ACTION_DOWN, displayWidth, displayHeight, touchMinor,
|
||||
touchMajor);
|
||||
mTouchListenerCaptor.getValue().onTouch(mUdfpsView, event);
|
||||
event.recycle();
|
||||
event = obtainMotionEvent(ACTION_MOVE, displayWidth, displayHeight, touchMinor, touchMajor);
|
||||
mTouchListenerCaptor.getValue().onTouch(mUdfpsView, event);
|
||||
event.recycle();
|
||||
verify(mAlternateTouchProvider).onPointerDown(eq(TEST_REQUEST_ID), eq(expectedX),
|
||||
eq(expectedY), eq(expectedMinor), eq(expectedMajor));
|
||||
|
||||
// Test ROTATION_90
|
||||
reset(mAlternateTouchProvider);
|
||||
mUdfpsController.updateOverlayParams(TEST_UDFPS_SENSOR_ID,
|
||||
new UdfpsOverlayParams(sensorBounds, displayWidth, displayHeight, scaleFactor,
|
||||
Surface.ROTATION_90));
|
||||
event = obtainMotionEvent(ACTION_DOWN, displayHeight, 0, touchMinor, touchMajor);
|
||||
mTouchListenerCaptor.getValue().onTouch(mUdfpsView, event);
|
||||
event.recycle();
|
||||
event = obtainMotionEvent(ACTION_MOVE, displayHeight, 0, touchMinor, touchMajor);
|
||||
mTouchListenerCaptor.getValue().onTouch(mUdfpsView, event);
|
||||
event.recycle();
|
||||
verify(mAlternateTouchProvider).onPointerDown(eq(TEST_REQUEST_ID), eq(expectedX),
|
||||
eq(expectedY), eq(expectedMinor), eq(expectedMajor));
|
||||
|
||||
// Test ROTATION_270
|
||||
reset(mAlternateTouchProvider);
|
||||
mUdfpsController.updateOverlayParams(TEST_UDFPS_SENSOR_ID,
|
||||
new UdfpsOverlayParams(sensorBounds, displayWidth, displayHeight, scaleFactor,
|
||||
Surface.ROTATION_270));
|
||||
event = obtainMotionEvent(ACTION_DOWN, 0, displayWidth, touchMinor, touchMajor);
|
||||
mTouchListenerCaptor.getValue().onTouch(mUdfpsView, event);
|
||||
event.recycle();
|
||||
event = obtainMotionEvent(ACTION_MOVE, 0, displayWidth, touchMinor, touchMajor);
|
||||
mTouchListenerCaptor.getValue().onTouch(mUdfpsView, event);
|
||||
event.recycle();
|
||||
verify(mAlternateTouchProvider).onPointerDown(eq(TEST_REQUEST_ID), eq(expectedX),
|
||||
eq(expectedY), eq(expectedMinor), eq(expectedMajor));
|
||||
|
||||
// Test ROTATION_180
|
||||
reset(mAlternateTouchProvider);
|
||||
mUdfpsController.updateOverlayParams(TEST_UDFPS_SENSOR_ID,
|
||||
new UdfpsOverlayParams(sensorBounds, displayWidth, displayHeight, scaleFactor,
|
||||
Surface.ROTATION_180));
|
||||
// ROTATION_180 is not supported. It should be treated like ROTATION_0.
|
||||
event = obtainMotionEvent(ACTION_DOWN, displayWidth, displayHeight, touchMinor, touchMajor);
|
||||
mTouchListenerCaptor.getValue().onTouch(mUdfpsView, event);
|
||||
event.recycle();
|
||||
event = obtainMotionEvent(ACTION_MOVE, displayWidth, displayHeight, touchMinor, touchMajor);
|
||||
mTouchListenerCaptor.getValue().onTouch(mUdfpsView, event);
|
||||
event.recycle();
|
||||
verify(mAlternateTouchProvider).onPointerDown(eq(TEST_REQUEST_ID), eq(expectedX),
|
||||
eq(expectedY), eq(expectedMinor), eq(expectedMajor));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void fingerDown() throws RemoteException {
|
||||
// Configure UdfpsView to accept the ACTION_DOWN event
|
||||
@@ -415,15 +571,17 @@ public class UdfpsControllerTest extends SysuiTestCase {
|
||||
mFgExecutor.runAllReady();
|
||||
// WHEN ACTION_DOWN is received
|
||||
verify(mUdfpsView).setOnTouchListener(mTouchListenerCaptor.capture());
|
||||
MotionEvent downEvent = MotionEvent.obtain(0, 0, MotionEvent.ACTION_DOWN, 0, 0, 0);
|
||||
MotionEvent downEvent = MotionEvent.obtain(0, 0, ACTION_DOWN, 0, 0, 0);
|
||||
mTouchListenerCaptor.getValue().onTouch(mUdfpsView, downEvent);
|
||||
downEvent.recycle();
|
||||
MotionEvent moveEvent = MotionEvent.obtain(0, 0, MotionEvent.ACTION_MOVE, 0, 0, 0);
|
||||
|
||||
// FIX THIS TEST
|
||||
mTouchListenerCaptor.getValue().onTouch(mUdfpsView, moveEvent);
|
||||
moveEvent.recycle();
|
||||
// THEN FingerprintManager is notified about onPointerDown
|
||||
verify(mTouchProvider).onPointerDown(eq(TEST_REQUEST_ID),
|
||||
eq(0), eq(0), eq(0f), eq(0f));
|
||||
verify(mAlternateTouchProvider).onPointerDown(eq(TEST_REQUEST_ID), eq(0), eq(0), eq(0f),
|
||||
eq(0f));
|
||||
verify(mFingerprintManager, never()).onPointerDown(anyLong(), anyInt(), anyInt(), anyInt(),
|
||||
anyFloat(), anyFloat());
|
||||
verify(mLatencyTracker).onActionStart(eq(LatencyTracker.ACTION_UDFPS_ILLUMINATE));
|
||||
@@ -434,7 +592,7 @@ public class UdfpsControllerTest extends SysuiTestCase {
|
||||
mOnIlluminatedRunnableCaptor.getValue().run();
|
||||
InOrder inOrder = inOrder(mFingerprintManager, mLatencyTracker);
|
||||
inOrder.verify(mFingerprintManager).onUiReady(
|
||||
eq(TEST_REQUEST_ID), eq(mUdfpsController.mSensorProps.sensorId));
|
||||
eq(TEST_REQUEST_ID), eq(mUdfpsController.mSensorId));
|
||||
inOrder.verify(mLatencyTracker).onActionEnd(eq(LatencyTracker.ACTION_UDFPS_ILLUMINATE));
|
||||
}
|
||||
|
||||
@@ -452,7 +610,7 @@ public class UdfpsControllerTest extends SysuiTestCase {
|
||||
// AND onIlluminatedRunnable that notifies FingerprintManager is set
|
||||
verify(mUdfpsView).startIllumination(mOnIlluminatedRunnableCaptor.capture());
|
||||
mOnIlluminatedRunnableCaptor.getValue().run();
|
||||
verify(mTouchProvider).onPointerDown(eq(TEST_REQUEST_ID),
|
||||
verify(mAlternateTouchProvider).onPointerDown(eq(TEST_REQUEST_ID),
|
||||
eq(0), eq(0), eq(3f) /* minor */, eq(2f) /* major */);
|
||||
verify(mFingerprintManager, never()).onPointerDown(anyLong(), anyInt(), anyInt(), anyInt(),
|
||||
anyFloat(), anyFloat());
|
||||
@@ -573,7 +731,7 @@ public class UdfpsControllerTest extends SysuiTestCase {
|
||||
|
||||
// WHEN ACTION_DOWN is received
|
||||
verify(mUdfpsView).setOnTouchListener(mTouchListenerCaptor.capture());
|
||||
MotionEvent downEvent = MotionEvent.obtain(0, 0, MotionEvent.ACTION_DOWN, 0, 0, 0);
|
||||
MotionEvent downEvent = MotionEvent.obtain(0, 0, ACTION_DOWN, 0, 0, 0);
|
||||
mTouchListenerCaptor.getValue().onTouch(mUdfpsView, downEvent);
|
||||
downEvent.recycle();
|
||||
MotionEvent moveEvent = MotionEvent.obtain(0, 0, MotionEvent.ACTION_MOVE, 0, 0, 0);
|
||||
|
||||
@@ -23,6 +23,7 @@ import android.testing.AndroidTestingRunner
|
||||
import android.testing.TestableLooper
|
||||
import android.testing.ViewUtils
|
||||
import android.view.LayoutInflater
|
||||
import android.view.Surface
|
||||
import androidx.test.filters.SmallTest
|
||||
import com.android.systemui.R
|
||||
import com.android.systemui.SysuiTestCase
|
||||
@@ -69,9 +70,8 @@ class UdfpsViewTest : SysuiTestCase() {
|
||||
com.android.internal.R.integer.config_udfps_illumination_transition_ms, 0)
|
||||
view = LayoutInflater.from(context).inflate(R.layout.udfps_view, null) as UdfpsView
|
||||
view.animationViewController = animationViewController
|
||||
view.sensorProperties =
|
||||
SensorLocationInternal(DISPLAY_ID, SENSOR_X, SENSOR_Y, SENSOR_RADIUS)
|
||||
.asFingerprintSensorProperties()
|
||||
val sensorBounds = SensorLocationInternal("", SENSOR_X, SENSOR_Y, SENSOR_RADIUS).rect
|
||||
view.overlayParams = UdfpsOverlayParams(sensorBounds, 1920, 1080, 1f, Surface.ROTATION_0)
|
||||
view.setHbmProvider(hbmProvider)
|
||||
ViewUtils.attachView(view)
|
||||
}
|
||||
|
||||
@@ -399,7 +399,7 @@ public class LockIconViewControllerTest extends SysuiTestCase {
|
||||
/* resetLockoutRequiresHwToken */ false,
|
||||
List.of(new SensorLocationInternal("" /* displayId */,
|
||||
(int) udfpsLocation.x, (int) udfpsLocation.y, radius)));
|
||||
when(mAuthController.getUdfpsSensorLocation()).thenReturn(udfpsLocation);
|
||||
when(mAuthController.getUdfpsLocation()).thenReturn(udfpsLocation);
|
||||
when(mAuthController.getUdfpsProps()).thenReturn(List.of(fpProps));
|
||||
|
||||
return new Pair(radius, udfpsLocation);
|
||||
|
||||
Reference in New Issue
Block a user