Merge "Fix condition where FP would require confirmation" into sc-dev

This commit is contained in:
Kevin Chyn
2021-07-31 00:10:05 +00:00
committed by Android (Google) Code Review
4 changed files with 83 additions and 61 deletions

View File

@@ -93,6 +93,8 @@ public class AuthBiometricFaceToFingerprintView extends AuthBiometricFaceView {
@Nullable private ModalityListener mModalityListener; @Nullable private ModalityListener mModalityListener;
@Nullable private FingerprintSensorPropertiesInternal mFingerprintSensorProps; @Nullable private FingerprintSensorPropertiesInternal mFingerprintSensorProps;
@Nullable private UdfpsDialogMeasureAdapter mUdfpsMeasureAdapter; @Nullable private UdfpsDialogMeasureAdapter mUdfpsMeasureAdapter;
@Nullable @VisibleForTesting UdfpsIconController mUdfpsIconController;
public AuthBiometricFaceToFingerprintView(Context context) { public AuthBiometricFaceToFingerprintView(Context context) {
super(context); super(context);
@@ -107,6 +109,12 @@ public class AuthBiometricFaceToFingerprintView extends AuthBiometricFaceView {
super(context, attrs, injector); super(context, attrs, injector);
} }
@Override
protected void onFinishInflate() {
super.onFinishInflate();
mUdfpsIconController = new UdfpsIconController(mContext, mIconView, mIndicatorView);
}
@Modality @Modality
int getActiveSensorType() { int getActiveSensorType() {
return mActiveSensorType; return mActiveSensorType;
@@ -167,36 +175,27 @@ public class AuthBiometricFaceToFingerprintView extends AuthBiometricFaceView {
return super.getStateForAfterError(); return super.getStateForAfterError();
} }
@Override
@NonNull
protected IconController getIconController() {
if (mActiveSensorType == TYPE_FINGERPRINT) {
if (!(mIconController instanceof UdfpsIconController)) {
mIconController = createUdfpsIconController();
}
return mIconController;
}
return super.getIconController();
}
@NonNull
protected IconController createUdfpsIconController() {
return new UdfpsIconController(getContext(), mIconView, mIndicatorView);
}
@Override @Override
public void updateState(@BiometricState int newState) { public void updateState(@BiometricState int newState) {
if (mState == STATE_HELP || mState == STATE_ERROR) { if (mActiveSensorType == TYPE_FACE) {
@Modality final int currentType = mActiveSensorType; if (newState == STATE_HELP || newState == STATE_ERROR) {
mActiveSensorType = TYPE_FINGERPRINT; mActiveSensorType = TYPE_FINGERPRINT;
setRequireConfirmation(false); setRequireConfirmation(false);
mConfirmButton.setEnabled(false); mConfirmButton.setEnabled(false);
mConfirmButton.setVisibility(View.GONE); mConfirmButton.setVisibility(View.GONE);
if (mModalityListener != null && currentType != mActiveSensorType) { if (mModalityListener != null) {
mModalityListener.onModalitySwitched(currentType, mActiveSensorType); mModalityListener.onModalitySwitched(TYPE_FACE, mActiveSensorType);
}
// Deactivate the face icon controller so it stops drawing to the view
mFaceIconController.deactivate();
// Then, activate this icon controller. We need to start in the "error" state
mUdfpsIconController.updateState(mState, newState);
} }
} else { // Fingerprint
mUdfpsIconController.updateState(mState, newState);
} }
super.updateState(newState); super.updateState(newState);

View File

@@ -49,6 +49,7 @@ public class AuthBiometricFaceView extends AuthBiometricView {
protected Handler mHandler; protected Handler mHandler;
protected boolean mLastPulseLightToDark; // false = dark to light, true = light to dark protected boolean mLastPulseLightToDark; // false = dark to light, true = light to dark
protected @BiometricState int mState; protected @BiometricState int mState;
protected boolean mDeactivated;
protected IconController(Context context, ImageView iconView, TextView textView) { protected IconController(Context context, ImageView iconView, TextView textView) {
mContext = context; mContext = context;
@@ -67,6 +68,11 @@ public class AuthBiometricFaceView extends AuthBiometricView {
} }
protected void animateIcon(int iconRes, boolean repeat) { protected void animateIcon(int iconRes, boolean repeat) {
Log.d(TAG, "animateIcon, state: " + mState + ", deactivated: " + mDeactivated);
if (mDeactivated) {
return;
}
final AnimatedVectorDrawable icon = final AnimatedVectorDrawable icon =
(AnimatedVectorDrawable) mContext.getDrawable(iconRes); (AnimatedVectorDrawable) mContext.getDrawable(iconRes);
mIconView.setImageDrawable(icon); mIconView.setImageDrawable(icon);
@@ -92,12 +98,26 @@ public class AuthBiometricFaceView extends AuthBiometricView {
@Override @Override
public void onAnimationEnd(Drawable drawable) { public void onAnimationEnd(Drawable drawable) {
super.onAnimationEnd(drawable); super.onAnimationEnd(drawable);
Log.d(TAG, "onAnimationEnd, mState: " + mState + ", deactivated: " + mDeactivated);
if (mDeactivated) {
return;
}
if (mState == STATE_AUTHENTICATING || mState == STATE_HELP) { if (mState == STATE_AUTHENTICATING || mState == STATE_HELP) {
pulseInNextDirection(); pulseInNextDirection();
} }
} }
protected void deactivate() {
mDeactivated = true;
}
protected void updateState(int lastState, int newState) { protected void updateState(int lastState, int newState) {
if (mDeactivated) {
Log.w(TAG, "Ignoring updateState when deactivated: " + newState);
return;
}
final boolean lastStateIsErrorIcon = final boolean lastStateIsErrorIcon =
lastState == STATE_ERROR || lastState == STATE_HELP; lastState == STATE_ERROR || lastState == STATE_HELP;
@@ -142,7 +162,7 @@ public class AuthBiometricFaceView extends AuthBiometricView {
} }
} }
protected IconController mIconController; @Nullable @VisibleForTesting IconController mFaceIconController;
public AuthBiometricFaceView(Context context) { public AuthBiometricFaceView(Context context) {
this(context, null); this(context, null);
@@ -157,6 +177,12 @@ public class AuthBiometricFaceView extends AuthBiometricView {
super(context, attrs, injector); super(context, attrs, injector);
} }
@Override
protected void onFinishInflate() {
super.onFinishInflate();
mFaceIconController = new IconController(mContext, mIconView, mIndicatorView);
}
@Override @Override
protected int getDelayAfterAuthenticatedDurationMs() { protected int getDelayAfterAuthenticatedDurationMs() {
return HIDE_DELAY_MS; return HIDE_DELAY_MS;
@@ -187,17 +213,9 @@ public class AuthBiometricFaceView extends AuthBiometricView {
return true; return true;
} }
@NonNull
protected IconController getIconController() {
if (mIconController == null) {
mIconController = new IconController(mContext, mIconView, mIndicatorView);
}
return mIconController;
}
@Override @Override
public void updateState(@BiometricState int newState) { public void updateState(@BiometricState int newState) {
getIconController().updateState(mState, newState); mFaceIconController.updateState(mState, newState);
if (newState == STATE_AUTHENTICATING_ANIMATING_IN || if (newState == STATE_AUTHENTICATING_ANIMATING_IN ||
(newState == STATE_AUTHENTICATING && getSize() == AuthDialog.SIZE_MEDIUM)) { (newState == STATE_AUTHENTICATING && getSize() == AuthDialog.SIZE_MEDIUM)) {

View File

@@ -23,7 +23,6 @@ import static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertTrue; import static org.junit.Assert.assertTrue;
import static org.mockito.ArgumentMatchers.anyInt; import static org.mockito.ArgumentMatchers.anyInt;
import static org.mockito.ArgumentMatchers.eq; import static org.mockito.ArgumentMatchers.eq;
import static org.mockito.Mockito.inOrder;
import static org.mockito.Mockito.never; import static org.mockito.Mockito.never;
import static org.mockito.Mockito.reset; import static org.mockito.Mockito.reset;
import static org.mockito.Mockito.verify; import static org.mockito.Mockito.verify;
@@ -50,7 +49,6 @@ import com.android.systemui.SysuiTestCase;
import org.junit.Before; import org.junit.Before;
import org.junit.Test; import org.junit.Test;
import org.junit.runner.RunWith; import org.junit.runner.RunWith;
import org.mockito.InOrder;
import org.mockito.Mock; import org.mockito.Mock;
import org.mockito.MockitoAnnotations; import org.mockito.MockitoAnnotations;
@@ -78,14 +76,16 @@ public class AuthBiometricFaceToFingerprintViewTest extends SysuiTestCase {
@Mock private TextView mIndicatorView; @Mock private TextView mIndicatorView;
@Mock private ImageView mIconView; @Mock private ImageView mIconView;
@Mock private View mIconHolderView; @Mock private View mIconHolderView;
@Mock private AuthBiometricFaceView.IconController mIconController; @Mock private AuthBiometricFaceView.IconController mFaceIconController;
@Mock private AuthBiometricFaceToFingerprintView.UdfpsIconController mUdfpsIconController;
@Before @Before
public void setup() { public void setup() {
MockitoAnnotations.initMocks(this); MockitoAnnotations.initMocks(this);
mFaceToFpView = new TestableView(mContext); mFaceToFpView = new TestableView(mContext);
mFaceToFpView.mIconController = mIconController; mFaceToFpView.mFaceIconController = mFaceIconController;
mFaceToFpView.mUdfpsIconController = mUdfpsIconController;
mFaceToFpView.setCallback(mCallback); mFaceToFpView.setCallback(mCallback);
mFaceToFpView.mNegativeButton = mNegativeButton; mFaceToFpView.mNegativeButton = mNegativeButton;
@@ -99,20 +99,23 @@ public class AuthBiometricFaceToFingerprintViewTest extends SysuiTestCase {
@Test @Test
public void testStateUpdated_whenDialogAnimatedIn() { public void testStateUpdated_whenDialogAnimatedIn() {
mFaceToFpView.onDialogAnimatedIn(); mFaceToFpView.onDialogAnimatedIn();
verify(mFaceToFpView.mIconController) verify(mFaceToFpView.mFaceIconController)
.updateState(anyInt(), eq(AuthBiometricFaceToFingerprintView.STATE_AUTHENTICATING)); .updateState(anyInt(), eq(AuthBiometricFaceToFingerprintView.STATE_AUTHENTICATING));
verify(mFaceToFpView.mUdfpsIconController, never()).updateState(anyInt(), anyInt());
} }
@Test @Test
public void testIconUpdatesState_whenDialogStateUpdated() { public void testIconUpdatesState_whenDialogStateUpdated() {
mFaceToFpView.onDialogAnimatedIn(); mFaceToFpView.onDialogAnimatedIn();
verify(mFaceToFpView.mIconController) verify(mFaceToFpView.mFaceIconController)
.updateState(anyInt(), eq(AuthBiometricFaceToFingerprintView.STATE_AUTHENTICATING)); .updateState(anyInt(), eq(AuthBiometricFaceToFingerprintView.STATE_AUTHENTICATING));
verify(mFaceToFpView.mUdfpsIconController, never()).updateState(anyInt(), anyInt());
mFaceToFpView.updateState(AuthBiometricFaceView.STATE_AUTHENTICATED); mFaceToFpView.updateState(AuthBiometricFaceView.STATE_AUTHENTICATED);
verify(mFaceToFpView.mIconController).updateState( verify(mFaceToFpView.mFaceIconController).updateState(
eq(AuthBiometricFaceToFingerprintView.STATE_AUTHENTICATING), eq(AuthBiometricFaceToFingerprintView.STATE_AUTHENTICATING),
eq(AuthBiometricFaceToFingerprintView.STATE_AUTHENTICATED)); eq(AuthBiometricFaceToFingerprintView.STATE_AUTHENTICATED));
verify(mFaceToFpView.mUdfpsIconController, never()).updateState(anyInt(), anyInt());
assertEquals(AuthBiometricFaceToFingerprintView.STATE_AUTHENTICATED, mFaceToFpView.mState); assertEquals(AuthBiometricFaceToFingerprintView.STATE_AUTHENTICATED, mFaceToFpView.mState);
} }
@@ -120,21 +123,22 @@ public class AuthBiometricFaceToFingerprintViewTest extends SysuiTestCase {
@Test @Test
public void testStateUpdated_whenSwitchToFingerprint() { public void testStateUpdated_whenSwitchToFingerprint() {
mFaceToFpView.onDialogAnimatedIn(); mFaceToFpView.onDialogAnimatedIn();
verify(mFaceToFpView.mIconController) verify(mFaceToFpView.mFaceIconController)
.updateState(anyInt(), eq(AuthBiometricFaceToFingerprintView.STATE_AUTHENTICATING)); .updateState(anyInt(), eq(AuthBiometricFaceToFingerprintView.STATE_AUTHENTICATING));
mFaceToFpView.updateState(AuthBiometricFaceToFingerprintView.STATE_ERROR); mFaceToFpView.updateState(AuthBiometricFaceToFingerprintView.STATE_ERROR);
mFaceToFpView.updateState(AuthBiometricFaceToFingerprintView.STATE_AUTHENTICATING);
InOrder order = inOrder(mFaceToFpView.mIconController); verify(mFaceToFpView.mFaceIconController).deactivate();
order.verify(mFaceToFpView.mIconController).updateState( verify(mFaceToFpView.mUdfpsIconController).updateState(
eq(AuthBiometricFaceToFingerprintView.STATE_AUTHENTICATING), eq(AuthBiometricFaceToFingerprintView.STATE_AUTHENTICATING),
eq(AuthBiometricFaceToFingerprintView.STATE_ERROR)); eq(AuthBiometricFaceToFingerprintView.STATE_ERROR));
order.verify(mFaceToFpView.mIconController).updateState( verify(mConfirmButton).setVisibility(eq(View.GONE));
mFaceToFpView.updateState(AuthBiometricFaceToFingerprintView.STATE_AUTHENTICATING);
verify(mFaceToFpView.mUdfpsIconController).updateState(
eq(AuthBiometricFaceToFingerprintView.STATE_ERROR), eq(AuthBiometricFaceToFingerprintView.STATE_ERROR),
eq(AuthBiometricFaceToFingerprintView.STATE_AUTHENTICATING)); eq(AuthBiometricFaceToFingerprintView.STATE_AUTHENTICATING));
verify(mConfirmButton).setVisibility(eq(View.GONE));
} }
@Test @Test
@@ -172,7 +176,10 @@ public class AuthBiometricFaceToFingerprintViewTest extends SysuiTestCase {
eq(mContext.getString(R.string.fingerprint_dialog_use_fingerprint_instead))); eq(mContext.getString(R.string.fingerprint_dialog_use_fingerprint_instead)));
verify(mCallback).onAction( verify(mCallback).onAction(
eq(AuthBiometricView.Callback.ACTION_START_DELAYED_FINGERPRINT_SENSOR)); eq(AuthBiometricView.Callback.ACTION_START_DELAYED_FINGERPRINT_SENSOR));
assertEquals(AuthBiometricFaceToFingerprintView.STATE_AUTHENTICATING, mFaceToFpView.mState);
// First we enter the error state, since we need to show the error animation/text. The
// error state is later cleared based on a timer, and we enter STATE_AUTHENTICATING.
assertEquals(AuthBiometricFaceToFingerprintView.STATE_ERROR, mFaceToFpView.mState);
} }
@Test @Test
@@ -185,13 +192,16 @@ public class AuthBiometricFaceToFingerprintViewTest extends SysuiTestCase {
eq(mContext.getString(R.string.fingerprint_dialog_use_fingerprint_instead))); eq(mContext.getString(R.string.fingerprint_dialog_use_fingerprint_instead)));
verify(mCallback).onAction( verify(mCallback).onAction(
eq(AuthBiometricView.Callback.ACTION_START_DELAYED_FINGERPRINT_SENSOR)); eq(AuthBiometricView.Callback.ACTION_START_DELAYED_FINGERPRINT_SENSOR));
assertEquals(AuthBiometricFaceToFingerprintView.STATE_AUTHENTICATING, mFaceToFpView.mState);
// First we enter the error state, since we need to show the error animation/text. The
// error state is later cleared based on a timer, and we enter STATE_AUTHENTICATING.
assertEquals(AuthBiometricFaceToFingerprintView.STATE_ERROR, mFaceToFpView.mState);
} }
@Test @Test
public void testFingerprintOnlyStartsOnFirstError() { public void testFingerprintOnlyStartsOnFirstError() {
mFaceToFpView.onDialogAnimatedIn(); mFaceToFpView.onDialogAnimatedIn();
verify(mFaceToFpView.mIconController) verify(mFaceToFpView.mFaceIconController)
.updateState(anyInt(), eq(AuthBiometricFaceToFingerprintView.STATE_AUTHENTICATING)); .updateState(anyInt(), eq(AuthBiometricFaceToFingerprintView.STATE_AUTHENTICATING));
mFaceToFpView.onDialogAnimatedIn(); mFaceToFpView.onDialogAnimatedIn();
@@ -260,11 +270,6 @@ public class AuthBiometricFaceToFingerprintViewTest extends SysuiTestCase {
protected int getDelayAfterAuthenticatedDurationMs() { protected int getDelayAfterAuthenticatedDurationMs() {
return 0; return 0;
} }
@Override
protected IconController createUdfpsIconController() {
return AuthBiometricFaceToFingerprintViewTest.this.mIconController;
}
} }
private class MockInjector extends AuthBiometricView.Injector { private class MockInjector extends AuthBiometricView.Injector {

View File

@@ -62,7 +62,7 @@ public class AuthBiometricFaceViewTest extends SysuiTestCase {
public void setup() { public void setup() {
MockitoAnnotations.initMocks(this); MockitoAnnotations.initMocks(this);
mFaceView = new TestableFaceView(mContext); mFaceView = new TestableFaceView(mContext);
mFaceView.mIconController = mock(TestableFaceView.TestableIconController.class); mFaceView.mFaceIconController = mock(TestableFaceView.TestableIconController.class);
mFaceView.setCallback(mCallback); mFaceView.setCallback(mCallback);
mFaceView.mNegativeButton = mNegativeButton; mFaceView.mNegativeButton = mNegativeButton;
@@ -78,18 +78,18 @@ public class AuthBiometricFaceViewTest extends SysuiTestCase {
@Test @Test
public void testStateUpdated_whenDialogAnimatedIn() { public void testStateUpdated_whenDialogAnimatedIn() {
mFaceView.onDialogAnimatedIn(); mFaceView.onDialogAnimatedIn();
verify(mFaceView.mIconController) verify(mFaceView.mFaceIconController)
.updateState(anyInt(), eq(AuthBiometricFaceView.STATE_AUTHENTICATING)); .updateState(anyInt(), eq(AuthBiometricFaceView.STATE_AUTHENTICATING));
} }
@Test @Test
public void testIconUpdatesState_whenDialogStateUpdated() { public void testIconUpdatesState_whenDialogStateUpdated() {
mFaceView.updateState(AuthBiometricFaceView.STATE_AUTHENTICATING); mFaceView.updateState(AuthBiometricFaceView.STATE_AUTHENTICATING);
verify(mFaceView.mIconController) verify(mFaceView.mFaceIconController)
.updateState(anyInt(), eq(AuthBiometricFaceView.STATE_AUTHENTICATING)); .updateState(anyInt(), eq(AuthBiometricFaceView.STATE_AUTHENTICATING));
mFaceView.updateState(AuthBiometricFaceView.STATE_AUTHENTICATED); mFaceView.updateState(AuthBiometricFaceView.STATE_AUTHENTICATED);
verify(mFaceView.mIconController).updateState( verify(mFaceView.mFaceIconController).updateState(
eq(AuthBiometricFaceView.STATE_AUTHENTICATING), eq(AuthBiometricFaceView.STATE_AUTHENTICATING),
eq(AuthBiometricFaceView.STATE_AUTHENTICATED)); eq(AuthBiometricFaceView.STATE_AUTHENTICATED));
} }