diff --git a/src/com/android/settings/biometrics/fingerprint/FingerprintEnrollEnrolling.java b/src/com/android/settings/biometrics/fingerprint/FingerprintEnrollEnrolling.java index bad1bbdfc1b..865694836ee 100644 --- a/src/com/android/settings/biometrics/fingerprint/FingerprintEnrollEnrolling.java +++ b/src/com/android/settings/biometrics/fingerprint/FingerprintEnrollEnrolling.java @@ -16,6 +16,8 @@ package com.android.settings.biometrics.fingerprint; +import static android.hardware.biometrics.BiometricFingerprintConstants.FINGERPRINT_ERROR_USER_CANCELED; + import android.animation.Animator; import android.animation.ObjectAnimator; import android.annotation.IntDef; @@ -51,6 +53,7 @@ import android.widget.LinearLayout; import android.widget.ProgressBar; import android.widget.TextView; +import androidx.annotation.IdRes; import androidx.appcompat.app.AlertDialog; import com.android.internal.annotations.VisibleForTesting; @@ -80,6 +83,7 @@ public class FingerprintEnrollEnrolling extends BiometricsEnrollEnrolling { private static final String TAG = "FingerprintEnrollEnrolling"; static final String TAG_SIDECAR = "sidecar"; static final String KEY_STATE_CANCELED = "is_canceled"; + static final String KEY_STATE_PREVIOUS_ROTATION = "previous_rotation"; private static final int PROGRESS_BAR_MAX = 10000; @@ -134,6 +138,7 @@ public class FingerprintEnrollEnrolling extends BiometricsEnrollEnrolling { private boolean mRestoring; private Vibrator mVibrator; private boolean mIsSetupWizard; + private boolean mIsOrientationChanged; private boolean mIsCanceled; private AccessibilityManager mAccessibilityManager; private boolean mIsAccessibilityEnabled; @@ -155,6 +160,23 @@ public class FingerprintEnrollEnrolling extends BiometricsEnrollEnrolling { return defaultDensity == currentDensity; } + @Override + public void onWindowFocusChanged(boolean hasFocus) { + if (hasFocus) { + return; + } + + // By UX design, we should ensure seamless enrollment CUJ even though user rotate device. + // Do NOT cancel enrollment progress after rotating, adding mIsOrientationChanged + // to judge if the focus changed was triggered by rotation, current WMS has triple callbacks + // (true > false > true), we need to reset mIsOrientationChanged when !hasFocus callback. + if (!mIsOrientationChanged) { + onCancelEnrollment(FINGERPRINT_ERROR_USER_CANCELED); + } else { + mIsOrientationChanged = false; + } + } + @Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); @@ -295,11 +317,15 @@ public class FingerprintEnrollEnrolling extends BiometricsEnrollEnrolling { protected void onSaveInstanceState(Bundle outState) { super.onSaveInstanceState(outState); outState.putBoolean(KEY_STATE_CANCELED, mIsCanceled); + outState.putInt(KEY_STATE_PREVIOUS_ROTATION, mPreviousRotation); } private void restoreSavedState(Bundle savedInstanceState) { mRestoring = true; mIsCanceled = savedInstanceState.getBoolean(KEY_STATE_CANCELED, false); + mPreviousRotation = savedInstanceState.getInt(KEY_STATE_PREVIOUS_ROTATION, + getDisplay().getRotation()); + mIsOrientationChanged = mPreviousRotation != getDisplay().getRotation(); } @Override @@ -337,6 +363,19 @@ public class FingerprintEnrollEnrolling extends BiometricsEnrollEnrolling { } } + @VisibleForTesting + void onCancelEnrollment(@IdRes int errorMsgId) { + FingerprintErrorDialog.showErrorDialog(this, errorMsgId); + mIsCanceled = true; + mIsOrientationChanged = false; + cancelEnrollment(); + stopIconAnimation(); + stopListenOrientationEvent(); + if (!mCanAssumeUdfps) { + mErrorText.removeCallbacks(mTouchAgainRunnable); + } + } + @Override protected void onStop() { if (!isChangingConfigurations()) { @@ -556,14 +595,7 @@ public class FingerprintEnrollEnrolling extends BiometricsEnrollEnrolling { @Override public void onEnrollmentError(int errMsgId, CharSequence errString) { - FingerprintErrorDialog.showErrorDialog(this, errMsgId); - mIsCanceled = true; - cancelEnrollment(); - stopIconAnimation(); - stopListenOrientationEvent(); - if (!mCanAssumeUdfps) { - mErrorText.removeCallbacks(mTouchAgainRunnable); - } + onCancelEnrollment(errMsgId); } @Override diff --git a/tests/robotests/src/com/android/settings/biometrics/fingerprint/FingerprintEnrollEnrollingTest.java b/tests/robotests/src/com/android/settings/biometrics/fingerprint/FingerprintEnrollEnrollingTest.java index 3af10fd43c9..6ed4169e7a0 100644 --- a/tests/robotests/src/com/android/settings/biometrics/fingerprint/FingerprintEnrollEnrollingTest.java +++ b/tests/robotests/src/com/android/settings/biometrics/fingerprint/FingerprintEnrollEnrollingTest.java @@ -16,6 +16,8 @@ package com.android.settings.biometrics.fingerprint; +import static com.android.settings.biometrics.fingerprint.FingerprintEnrollEnrolling.KEY_STATE_PREVIOUS_ROTATION; + import static com.google.common.truth.Truth.assertThat; import static org.mockito.Mockito.any; @@ -28,14 +30,18 @@ import static org.mockito.Mockito.spy; import static org.mockito.Mockito.verify; import static org.mockito.Mockito.when; +import android.content.Context; import android.hardware.biometrics.ComponentInfoInternal; import android.hardware.biometrics.SensorProperties; import android.hardware.fingerprint.FingerprintManager; import android.hardware.fingerprint.FingerprintManager.EnrollmentCallback; import android.hardware.fingerprint.FingerprintSensorProperties; import android.hardware.fingerprint.FingerprintSensorPropertiesInternal; +import android.os.Bundle; import android.os.CancellationSignal; import android.os.Vibrator; +import android.view.Display; +import android.view.Surface; import android.widget.TextView; import com.android.settings.R; @@ -49,6 +55,7 @@ import org.mockito.ArgumentCaptor; import org.mockito.Mock; import org.mockito.MockitoAnnotations; import org.robolectric.RobolectricTestRunner; +import org.robolectric.RuntimeEnvironment; import org.robolectric.android.controller.ActivityController; import java.util.ArrayList; @@ -61,7 +68,10 @@ public class FingerprintEnrollEnrollingTest { @Mock private Vibrator mVibrator; + @Mock private Display mMockDisplay; + private FingerprintEnrollEnrolling mActivity; + private Context mContext; @Before public void setUp() { @@ -100,6 +110,26 @@ public class FingerprintEnrollEnrollingTest { verify(mVibrator, never()).vibrate(anyInt(), anyString(), any(), anyString(), any()); } + @Test + public void fingerprintUdfpsOverlayEnrollment_gainFocus_shouldNotCancel() { + initializeActivityFor(FingerprintSensorProperties.TYPE_UDFPS_OPTICAL); + + mActivity.onEnrollmentProgressChange(1, 1); + mActivity.onWindowFocusChanged(true); + + verify(mActivity, never()).onCancelEnrollment(anyInt()); + } + + @Test + public void fingerprintUdfpsOverlayEnrollment_loseFocus_shouldCancel() { + initializeActivityFor(FingerprintSensorProperties.TYPE_UDFPS_OPTICAL); + + mActivity.onEnrollmentProgressChange(1, 1); + mActivity.onWindowFocusChanged(false); + + verify(mActivity, never()).onCancelEnrollment(anyInt()); + } + private void initializeActivityFor(int sensorType) { final List componentInfo = new ArrayList<>(); final FingerprintSensorPropertiesInternal prop = @@ -111,15 +141,21 @@ public class FingerprintEnrollEnrollingTest { sensorType, true /* resetLockoutRequiresHardwareAuthToken */); final ArrayList props = new ArrayList<>(); + final Bundle savedInstanceState = new Bundle(); + savedInstanceState.putInt(KEY_STATE_PREVIOUS_ROTATION, Surface.ROTATION_90); props.add(prop); - when(mFingerprintManager.getSensorPropertiesInternal()).thenReturn(props); - + mContext = spy(RuntimeEnvironment.application); mActivity = spy(FingerprintEnrollEnrolling.class); + + when(mFingerprintManager.getSensorPropertiesInternal()).thenReturn(props); + when(mContext.getDisplay()).thenReturn(mMockDisplay); + when(mMockDisplay.getRotation()).thenReturn(Surface.ROTATION_0); + doReturn(true).when(mActivity).shouldShowLottie(); doReturn(mFingerprintManager).when(mActivity).getSystemService(FingerprintManager.class); doReturn(mVibrator).when(mActivity).getSystemService(Vibrator.class); - ActivityController.of(mActivity).create(); + ActivityController.of(mActivity).create(savedInstanceState); } private EnrollmentCallback verifyAndCaptureEnrollmentCallback() {