Merge "Fix the injection disappearance in the Account detail page" into tm-qpr-dev
diff --git a/res/layout/fingerprint_enroll_introduction.xml b/res/layout/fingerprint_enroll_introduction.xml
index a01f3a9..0c10e52 100644
--- a/res/layout/fingerprint_enroll_introduction.xml
+++ b/res/layout/fingerprint_enroll_introduction.xml
@@ -199,6 +199,8 @@
                     android:layout_width="16dp"
                     android:layout_height="wrap_content"/>
                 <TextView
+                    android:id="@+id/footer_learn_more"
+                    android:linksClickable="true"
                     android:layout_width="match_parent"
                     android:layout_height="wrap_content"
                     style="@style/BiometricEnrollIntroMessage"
diff --git a/src/com/android/settings/biometrics/face/FaceEnrollIntroduction.java b/src/com/android/settings/biometrics/face/FaceEnrollIntroduction.java
index a8be8f7..2598296 100644
--- a/src/com/android/settings/biometrics/face/FaceEnrollIntroduction.java
+++ b/src/com/android/settings/biometrics/face/FaceEnrollIntroduction.java
@@ -26,6 +26,8 @@
 import android.hardware.face.FaceManager;
 import android.hardware.face.FaceSensorPropertiesInternal;
 import android.os.Bundle;
+import android.text.Html;
+import android.text.method.LinkMovementMethod;
 import android.util.Log;
 import android.view.View;
 import android.widget.ImageView;
@@ -120,7 +122,9 @@
         infoMessageLooking.setText(getInfoMessageLooking());
         inControlTitle.setText(getInControlTitle());
         howMessage.setText(getHowMessage());
-        inControlMessage.setText(getInControlMessage());
+        inControlMessage.setText(Html.fromHtml(getString(getInControlMessage()),
+                Html.FROM_HTML_MODE_LEGACY));
+        inControlMessage.setMovementMethod(LinkMovementMethod.getInstance());
         lessSecure.setText(getLessSecureMessage());
 
         // Set up and show the "less secure" info section if necessary.
diff --git a/src/com/android/settings/biometrics/fingerprint/FingerprintEnrollEnrolling.java b/src/com/android/settings/biometrics/fingerprint/FingerprintEnrollEnrolling.java
index bad1bbd..8656948 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.ProgressBar;
 import android.widget.TextView;
 
+import androidx.annotation.IdRes;
 import androidx.appcompat.app.AlertDialog;
 
 import com.android.internal.annotations.VisibleForTesting;
@@ -80,6 +83,7 @@
     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 @@
     private boolean mRestoring;
     private Vibrator mVibrator;
     private boolean mIsSetupWizard;
+    private boolean mIsOrientationChanged;
     private boolean mIsCanceled;
     private AccessibilityManager mAccessibilityManager;
     private boolean mIsAccessibilityEnabled;
@@ -156,6 +161,23 @@
     }
 
     @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 @@
     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 @@
         }
     }
 
+    @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 @@
 
     @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/src/com/android/settings/biometrics/fingerprint/FingerprintEnrollIntroduction.java b/src/com/android/settings/biometrics/fingerprint/FingerprintEnrollIntroduction.java
index 71d0c8e..838c472 100644
--- a/src/com/android/settings/biometrics/fingerprint/FingerprintEnrollIntroduction.java
+++ b/src/com/android/settings/biometrics/fingerprint/FingerprintEnrollIntroduction.java
@@ -26,6 +26,8 @@
 import android.hardware.fingerprint.FingerprintManager;
 import android.hardware.fingerprint.FingerprintSensorPropertiesInternal;
 import android.os.Bundle;
+import android.text.Html;
+import android.text.method.LinkMovementMethod;
 import android.util.Log;
 import android.view.View;
 import android.widget.ImageView;
@@ -104,6 +106,11 @@
         footerMessage5.setText(getFooterMessage5());
         footerMessage6.setText(getFooterMessage6());
 
+        final TextView footerLink = findViewById(R.id.footer_learn_more);
+        footerLink.setMovementMethod(LinkMovementMethod.getInstance());
+        footerLink.setText(Html.fromHtml(getString(getFooterLearnMore()),
+                Html.FROM_HTML_MODE_LEGACY));
+
         if (mCanAssumeUdfps) {
             footerMessage6.setVisibility(View.VISIBLE);
             iconShield.setVisibility(View.VISIBLE);
@@ -187,6 +194,11 @@
         return R.string.security_settings_fingerprint_v2_enroll_introduction_footer_message_6;
     }
 
+    @StringRes
+    protected int getFooterLearnMore() {
+        return R.string.security_settings_fingerprint_v2_enroll_introduction_message_learn_more;
+    }
+
     @Override
     protected boolean isDisabledByAdmin() {
         return RestrictedLockUtilsInternal.checkIfKeyguardFeaturesDisabled(
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 757a304..7395694 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.ArgumentMatchers.any;
@@ -28,14 +30,18 @@
 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.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 @@
 
     @Mock private Vibrator mVibrator;
 
+    @Mock private Display mMockDisplay;
+
     private FingerprintEnrollEnrolling mActivity;
+    private Context mContext;
 
     @Before
     public void setUp() {
@@ -100,6 +110,26 @@
         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<ComponentInfoInternal> componentInfo = new ArrayList<>();
         final FingerprintSensorPropertiesInternal prop =
@@ -111,15 +141,21 @@
                         sensorType,
                         true /* resetLockoutRequiresHardwareAuthToken */);
         final ArrayList<FingerprintSensorPropertiesInternal> 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() {