Merge "Allow footer string support both mobile and tablet devices" 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/accounts/AccountDetailDashboardFragment.java b/src/com/android/settings/accounts/AccountDetailDashboardFragment.java
index 1485500..0668c62 100644
--- a/src/com/android/settings/accounts/AccountDetailDashboardFragment.java
+++ b/src/com/android/settings/accounts/AccountDetailDashboardFragment.java
@@ -64,8 +64,7 @@
 
     @Override
     public void onCreate(Bundle icicle) {
-        super.onCreate(icicle);
-        getPreferenceManager().setPreferenceComparisonCallback(null);
+        // Initialize the parameters since displayTile() will be called in super.onCreate().
         Bundle args = getArguments();
         final Activity activity = getActivity();
         mUserHandle = Utils.getSecureTargetUser(activity.getActivityToken(),
@@ -82,6 +81,9 @@
                 mAccountType = args.getString(KEY_ACCOUNT_TYPE);
             }
         }
+
+        super.onCreate(icicle);
+        getPreferenceManager().setPreferenceComparisonCallback(null);
         mAccountSynController.init(mAccount, mUserHandle);
         mRemoveAccountController.init(mAccount, mUserHandle);
     }
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/src/com/android/settings/fuelgauge/batteryusage/BatteryChartPreferenceController.java b/src/com/android/settings/fuelgauge/batteryusage/BatteryChartPreferenceController.java
index f3fec8e..6d2c1a1 100644
--- a/src/com/android/settings/fuelgauge/batteryusage/BatteryChartPreferenceController.java
+++ b/src/com/android/settings/fuelgauge/batteryusage/BatteryChartPreferenceController.java
@@ -369,10 +369,6 @@
             addFooterPreferenceIfNeeded(false);
             return false;
         }
-        if (mBatteryUsageMap == null) {
-            // Battery usage data is not ready, wait for data ready to refresh UI.
-            return false;
-        }
 
         if (isBatteryLevelDataInOneDay()) {
             // Only 1 day data, hide the daily chart view.
@@ -394,6 +390,10 @@
             mHourlyChartView.setViewModel(hourlyViewModel);
         }
 
+        if (mBatteryUsageMap == null) {
+            // Battery usage data is not ready, wait for data ready to refresh UI.
+            return false;
+        }
         mHandler.post(() -> {
             final long start = System.currentTimeMillis();
             removeAndCacheAllPrefs();
diff --git a/src/com/android/settings/fuelgauge/batteryusage/BatteryChartView.java b/src/com/android/settings/fuelgauge/batteryusage/BatteryChartView.java
index cfdd851..c1cffc8 100644
--- a/src/com/android/settings/fuelgauge/batteryusage/BatteryChartView.java
+++ b/src/com/android/settings/fuelgauge/batteryusage/BatteryChartView.java
@@ -515,7 +515,7 @@
 
     private boolean hasOverlap(
             final Rect[] displayAreas, final int leftIndex, final int rightIndex) {
-        return displayAreas[leftIndex].right + mTextPadding * 2f > displayAreas[rightIndex].left;
+        return displayAreas[leftIndex].right + mTextPadding * 2.3f > displayAreas[rightIndex].left;
     }
 
     private void drawAxisLabelText(
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() {