Merge "Fix Fingerprint enrollment UI when display size is largest." into udc-qpr-dev am: 6cb1f9401d am: 81587b1864

Original change: https://googleplex-android-review.googlesource.com/c/platform/packages/apps/Settings/+/23418521

Change-Id: I47013f77ec1436048a41afbba34bdb4eeaa6ac6b
Signed-off-by: Automerger Merge Worker <android-build-automerger-merge-worker@system.gserviceaccount.com>
diff --git a/res/layout-land/udfps_enroll_enrolling.xml b/res/layout-land/udfps_enroll_enrolling.xml
deleted file mode 100644
index 743684f..0000000
--- a/res/layout-land/udfps_enroll_enrolling.xml
+++ /dev/null
@@ -1,101 +0,0 @@
-<?xml version="1.0" encoding="utf-8"?>
-<!--
-  Copyright (C) 2021 The Android Open Source Project
-
-  Licensed under the Apache License, Version 2.0 (the "License");
-  you may not use this file except in compliance with the License.
-  You may obtain a copy of the License at
-
-       http://www.apache.org/licenses/LICENSE-2.0
-
-  Unless required by applicable law or agreed to in writing, software
-  distributed under the License is distributed on an "AS IS" BASIS,
-  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-  See the License for the specific language governing permissions and
-  limitations under the License.
--->
-
-<com.google.android.setupdesign.GlifLayout
-    xmlns:android="http://schemas.android.com/apk/res/android"
-    android:id="@+id/setup_wizard_layout"
-    android:layout_width="match_parent"
-    android:layout_height="match_parent"
-    android:layout="@layout/sud_glif_blank_template"
-    style="?attr/fingerprint_layout_theme">
-
-    <LinearLayout
-        android:layout_width="match_parent"
-        android:layout_height="match_parent"
-        android:orientation="horizontal"
-        android:clipToPadding="false"
-        android:clipChildren="false">
-
-        <!-- Both texts are kept as separate text views so it doesn't jump around in portrait.
-            See layouts/fingerprint_enroll_enrolling_base.xml. -->
-        <LinearLayout
-            android:id="@+id/layout_container"
-            android:layout_width="0dp"
-            android:layout_weight="1"
-            android:layout_height="match_parent"
-            android:layout_marginStart="?attr/sudMarginStart"
-            android:layout_marginEnd="@dimen/enroll_margin_end"
-            android:layout_marginBottom="@dimen/sud_content_frame_padding_bottom"
-            android:paddingStart="@dimen/enroll_padding_start"
-            android:paddingEnd="@dimen/enroll_padding_end"
-            android:clipChildren="false"
-            android:clipToPadding="false"
-            android:orientation="vertical">
-
-            <ScrollView
-                android:layout_width="match_parent"
-                android:layout_height="match_parent"
-                android:clipChildren="false"
-                android:clipToPadding="false"
-                android:fillViewport="true">
-
-                <LinearLayout
-                    android:layout_width="match_parent"
-                    android:layout_height="wrap_content"
-                    android:orientation="vertical"
-                    android:clipChildren="false"
-                    android:clipToPadding="false">
-
-                    <ImageView
-                        android:id="@+id/sud_layout_icon"
-                        style="@style/SudGlifIcon"
-                        android:layout_width="wrap_content"
-                        android:layout_height="wrap_content"
-                        android:scaleType="fitStart"
-                        android:layout_marginStart="0dp"
-                        android:layout_marginEnd="0dp"
-                        android:src="@drawable/ic_lock" />
-
-                    <TextView
-                        android:id="@+id/suc_layout_title"
-                        style="@style/SudGlifHeaderTitle"
-                        android:layout_width="match_parent"
-                        android:layout_height="wrap_content"
-                        android:layout_marginStart="0dp"
-                        android:layout_marginEnd="0dp" />
-
-                    <TextView
-                        style="@style/SudDescription.Glif"
-                        android:id="@+id/sud_layout_subtitle"
-                        android:layout_width="match_parent"
-                        android:layout_height="wrap_content"/>
-
-                    <Space
-                        android:layout_width="0dp"
-                        android:layout_height="0dp"
-                        android:layout_weight="1" />
-
-                </LinearLayout>
-
-            </ScrollView>
-
-        </LinearLayout>
-
-    </LinearLayout>
-
-    <include layout="@layout/udfps_enroll_view" />
-</com.google.android.setupdesign.GlifLayout>
\ No newline at end of file
diff --git a/res/layout/udfps_enroll_enrolling.xml b/res/layout/udfps_enroll_enrolling.xml
index 05556ff..366a87c 100644
--- a/res/layout/udfps_enroll_enrolling.xml
+++ b/res/layout/udfps_enroll_enrolling.xml
@@ -15,7 +15,7 @@
   ~ limitations under the License.
   -->
 
-<com.google.android.setupdesign.GlifLayout
+<com.android.settings.biometrics.fingerprint.UdfpsEnrollEnrollingView
     xmlns:android="http://schemas.android.com/apk/res/android"
     xmlns:app="http://schemas.android.com/apk/res-auto"
     xmlns:tools="http://schemas.android.com/tools"
@@ -78,4 +78,4 @@
       </LinearLayout>
     </LinearLayout>
 
-</com.google.android.setupdesign.GlifLayout>
+</com.android.settings.biometrics.fingerprint.UdfpsEnrollEnrollingView>
diff --git a/src/com/android/settings/biometrics/BiometricEnrollBase.java b/src/com/android/settings/biometrics/BiometricEnrollBase.java
index 2f852f0..6e11079 100644
--- a/src/com/android/settings/biometrics/BiometricEnrollBase.java
+++ b/src/com/android/settings/biometrics/BiometricEnrollBase.java
@@ -133,6 +133,7 @@
     protected long mChallenge;
     protected boolean mFromSettingsSummary;
     protected FooterBarMixin mFooterBarMixin;
+    protected boolean mShouldSetFooterBarBackground = true;
     @Nullable
     protected ScreenSizeFoldProvider mScreenSizeFoldProvider;
     @Nullable
@@ -191,12 +192,14 @@
         super.onPostCreate(savedInstanceState);
         initViews();
 
-        @SuppressLint("VisibleForTests")
-        final LinearLayout buttonContainer = mFooterBarMixin != null
-                ? mFooterBarMixin.getButtonContainer()
-                : null;
-        if (buttonContainer != null) {
-            buttonContainer.setBackgroundColor(getBackgroundColor());
+        if (mShouldSetFooterBarBackground) {
+            @SuppressLint("VisibleForTests")
+            final LinearLayout buttonContainer = mFooterBarMixin != null
+                    ? mFooterBarMixin.getButtonContainer()
+                    : null;
+            if (buttonContainer != null) {
+                buttonContainer.setBackgroundColor(getBackgroundColor());
+            }
         }
     }
 
@@ -331,7 +334,7 @@
     }
 
     @ColorInt
-    private int getBackgroundColor() {
+    public int getBackgroundColor() {
         final ColorStateList stateList = Utils.getColorAttr(this, android.R.attr.windowBackground);
         return stateList != null ? stateList.getDefaultColor() : Color.TRANSPARENT;
     }
diff --git a/src/com/android/settings/biometrics/fingerprint/FingerprintEnrollEnrolling.java b/src/com/android/settings/biometrics/fingerprint/FingerprintEnrollEnrolling.java
index f653942..400a92e 100644
--- a/src/com/android/settings/biometrics/fingerprint/FingerprintEnrollEnrolling.java
+++ b/src/com/android/settings/biometrics/fingerprint/FingerprintEnrollEnrolling.java
@@ -32,10 +32,8 @@
 import android.content.res.ColorStateList;
 import android.content.res.Configuration;
 import android.content.res.Resources;
-import android.graphics.Point;
 import android.graphics.PorterDuff;
 import android.graphics.PorterDuffColorFilter;
-import android.graphics.Rect;
 import android.graphics.drawable.Animatable2;
 import android.graphics.drawable.AnimatedVectorDrawable;
 import android.graphics.drawable.Drawable;
@@ -48,22 +46,16 @@
 import android.os.VibrationEffect;
 import android.os.Vibrator;
 import android.text.TextUtils;
-import android.util.FeatureFlagUtils;
 import android.util.Log;
-import android.view.DisplayInfo;
 import android.view.MotionEvent;
 import android.view.OrientationEventListener;
 import android.view.Surface;
 import android.view.View;
-import android.view.ViewGroup;
 import android.view.accessibility.AccessibilityEvent;
 import android.view.accessibility.AccessibilityManager;
 import android.view.animation.AccelerateDecelerateInterpolator;
 import android.view.animation.AnimationUtils;
 import android.view.animation.Interpolator;
-import android.widget.FrameLayout;
-import android.widget.ImageView;
-import android.widget.LinearLayout;
 import android.widget.ProgressBar;
 import android.widget.RelativeLayout;
 import android.widget.TextView;
@@ -79,25 +71,20 @@
 import com.android.settings.biometrics.BiometricsEnrollEnrolling;
 import com.android.settings.core.instrumentation.InstrumentedDialogFragment;
 import com.android.settingslib.display.DisplayDensityUtils;
-import com.android.settingslib.udfps.UdfpsOverlayParams;
-import com.android.settingslib.udfps.UdfpsUtils;
 
 import com.airbnb.lottie.LottieAnimationView;
 import com.airbnb.lottie.LottieCompositionFactory;
 import com.airbnb.lottie.LottieProperty;
 import com.airbnb.lottie.model.KeyPath;
-import com.google.android.setupcompat.template.FooterActionButton;
 import com.google.android.setupcompat.template.FooterBarMixin;
 import com.google.android.setupcompat.template.FooterButton;
 import com.google.android.setupcompat.util.WizardManagerHelper;
-import com.google.android.setupdesign.GlifLayout;
 import com.google.android.setupdesign.template.DescriptionMixin;
 import com.google.android.setupdesign.template.HeaderMixin;
 
 import java.lang.annotation.Retention;
 import java.lang.annotation.RetentionPolicy;
 import java.util.List;
-import java.util.Locale;
 
 /**
  * Activity which handles the actual enrolling for fingerprint.
@@ -176,8 +163,6 @@
     @VisibleForTesting
     @Nullable
     UdfpsEnrollHelper mUdfpsEnrollHelper;
-    // TODO(b/260617060): Do not hard-code mScaleFactor, referring to AuthController.
-    private float mScaleFactor = 1.0f;
     private ObjectAnimator mProgressAnim;
     private TextView mErrorText;
     private Interpolator mFastOutSlowInInterpolator;
@@ -206,7 +191,7 @@
     private boolean mHaveShownSfpsLeftEdgeLottie;
     private boolean mHaveShownSfpsRightEdgeLottie;
     private boolean mShouldShowLottie;
-    private UdfpsUtils mUdfpsUtils;
+
     private ObjectAnimator mHelpAnimation;
 
     private OrientationEventListener mOrientationEventListener;
@@ -251,88 +236,17 @@
 
         mAccessibilityManager = getSystemService(AccessibilityManager.class);
         mIsAccessibilityEnabled = mAccessibilityManager.isEnabled();
-        mUdfpsUtils = new UdfpsUtils();
 
-        final boolean isLayoutRtl = (TextUtils.getLayoutDirectionFromLocale(
-                Locale.getDefault()) == View.LAYOUT_DIRECTION_RTL);
         listenOrientationEvent();
 
         if (mCanAssumeUdfps) {
-            int rotation = getApplicationContext().getDisplay().getRotation();
-            final GlifLayout layout = (GlifLayout) getLayoutInflater().inflate(
-                    R.layout.udfps_enroll_enrolling, null, false);
-            final UdfpsEnrollView udfpsEnrollView = layout.findViewById(R.id.udfps_animation_view);
-            updateUdfpsEnrollView(udfpsEnrollView, props.get(0));
-            switch (rotation) {
-                case Surface.ROTATION_90:
-                    final View sudContent = layout.findViewById(R.id.sud_layout_content);
-                    if (sudContent != null) {
-                        sudContent.setPadding(sudContent.getPaddingLeft(), 0,
-                                sudContent.getPaddingRight(), sudContent.getPaddingBottom());
-                    }
+            final UdfpsEnrollEnrollingView layout =
+                    (UdfpsEnrollEnrollingView) getLayoutInflater().inflate(
+                            R.layout.udfps_enroll_enrolling, null, false);
+            setUdfpsEnrollHelper();
+            layout.initView(props.get(0), mUdfpsEnrollHelper, mAccessibilityManager);
 
-                    final LinearLayout layoutContainer = layout.findViewById(
-                            R.id.layout_container);
-                    final LinearLayout.LayoutParams lp = new LinearLayout.LayoutParams(
-                            LinearLayout.LayoutParams.MATCH_PARENT,
-                            LinearLayout.LayoutParams.MATCH_PARENT);
-
-                    lp.setMarginEnd((int) getResources().getDimension(
-                            R.dimen.rotation_90_enroll_margin_end));
-                    layoutContainer.setPaddingRelative((int) getResources().getDimension(
-                            R.dimen.rotation_90_enroll_padding_start), 0, isLayoutRtl
-                            ? 0 : (int) getResources().getDimension(
-                            R.dimen.rotation_90_enroll_padding_end), 0);
-                    layoutContainer.setLayoutParams(lp);
-
-                    setOnHoverListener(true, layout, udfpsEnrollView);
-                    setContentView(layout, lp);
-                    break;
-
-                case Surface.ROTATION_0:
-                case Surface.ROTATION_180:
-                    // In the portrait mode, layout_container's height is 0, so it's
-                    // always shown at the bottom of the screen.
-                    final FrameLayout portraitLayoutContainer = layout.findViewById(
-                            R.id.layout_container);
-
-                    // In the portrait mode, the title and lottie animation view may
-                    // overlap when title needs three lines, so adding some paddings
-                    // between them, and adjusting the fp progress view here accordingly.
-                    final int layoutLottieAnimationPadding = (int) getResources()
-                            .getDimension(R.dimen.udfps_lottie_padding_top);
-                    portraitLayoutContainer.setPadding(0,
-                            layoutLottieAnimationPadding, 0, 0);
-                    final ImageView progressView = udfpsEnrollView.findViewById(
-                            R.id.udfps_enroll_animation_fp_progress_view);
-                    progressView.setPadding(0, -(layoutLottieAnimationPadding),
-                            0, layoutLottieAnimationPadding);
-                    final ImageView fingerprintView = udfpsEnrollView.findViewById(
-                            R.id.udfps_enroll_animation_fp_view);
-                    fingerprintView.setPadding(0, -layoutLottieAnimationPadding,
-                            0, layoutLottieAnimationPadding);
-
-                    // TODO(b/260970216) Instead of hiding the description text view, we should
-                    //  make the header view scrollable if the text is too long.
-                    // If description text view has overlap with udfps progress view, hide it.
-                    View view = layout.getDescriptionTextView();
-                    layout.getViewTreeObserver().addOnDrawListener(() -> {
-                        if (view.getVisibility() == View.VISIBLE
-                                && hasOverlap(view, udfpsEnrollView)) {
-                            view.setVisibility(View.GONE);
-                        }
-                    });
-
-                    setOnHoverListener(false, layout, udfpsEnrollView);
-                    setContentView(layout);
-                    break;
-
-                case Surface.ROTATION_270:
-                default:
-                    setOnHoverListener(true, layout, udfpsEnrollView);
-                    setContentView(layout);
-                    break;
-            }
+            setContentView(layout);
             setDescriptionText(R.string.security_settings_udfps_enroll_start_message);
         } else if (mCanAssumeSfps) {
             setContentView(R.layout.sfps_enroll_enrolling);
@@ -372,22 +286,11 @@
                         .build()
         );
 
-        if (FeatureFlagUtils.isEnabled(getApplicationContext(),
-                FeatureFlagUtils.SETTINGS_SHOW_UDFPS_ENROLL_IN_SETTINGS)) {
-            // Remove the space view and make the width of footer button container WRAP_CONTENT
-            // to avoid hiding the udfps view progress bar bottom.
-            final LinearLayout buttonContainer = mFooterBarMixin.getButtonContainer();
-            View spaceView = null;
-            for (int i = 0; i < buttonContainer.getChildCount(); i++) {
-                if (!(buttonContainer.getChildAt(i) instanceof FooterActionButton)) {
-                    spaceView = buttonContainer.getChildAt(i);
-                    break;
-                }
-            }
-            if (spaceView != null) {
-                spaceView.setVisibility(View.GONE);
-                buttonContainer.getLayoutParams().width = ViewGroup.LayoutParams.WRAP_CONTENT;
-            }
+        // If it's udfps, set the background color only for secondary button if necessary.
+        if (mCanAssumeUdfps) {
+            mShouldSetFooterBarBackground = false;
+            ((UdfpsEnrollEnrollingView) getLayout()).setSecondaryButtonBackground(
+                    getBackgroundColor());
         }
 
         final LayerDrawable fingerprintDrawable = mProgressBar != null
@@ -1230,30 +1133,7 @@
         }
     }
 
-    private UdfpsEnrollView updateUdfpsEnrollView(UdfpsEnrollView udfpsEnrollView,
-                                                  FingerprintSensorPropertiesInternal udfpsProps) {
-        DisplayInfo displayInfo = new DisplayInfo();
-        getDisplay().getDisplayInfo(displayInfo);
-        mScaleFactor = mUdfpsUtils.getScaleFactor(displayInfo);
-        Rect udfpsBounds = udfpsProps.getLocation().getRect();
-        udfpsBounds.scale(mScaleFactor);
-
-        final Rect overlayBounds = new Rect(
-                0, /* left */
-                displayInfo.getNaturalHeight() / 2, /* top */
-                displayInfo.getNaturalWidth(), /* right */
-                displayInfo.getNaturalHeight() /* botom */);
-
-        UdfpsOverlayParams params = new UdfpsOverlayParams(
-                udfpsBounds,
-                overlayBounds,
-                displayInfo.getNaturalWidth(),
-                displayInfo.getNaturalHeight(),
-                mScaleFactor,
-                displayInfo.rotation);
-
-        udfpsEnrollView.setOverlayParams(params);
-
+    private void setUdfpsEnrollHelper() {
         mUdfpsEnrollHelper = (UdfpsEnrollHelper) getSupportFragmentManager().findFragmentByTag(
                 FingerprintEnrollEnrolling.TAG_UDFPS_HELPER);
         if (mUdfpsEnrollHelper == null) {
@@ -1263,57 +1143,6 @@
                     .add(mUdfpsEnrollHelper, FingerprintEnrollEnrolling.TAG_UDFPS_HELPER)
                     .commitAllowingStateLoss();
         }
-        udfpsEnrollView.setEnrollHelper(mUdfpsEnrollHelper);
-
-        return udfpsEnrollView;
-    }
-
-    private void setOnHoverListener(boolean isLandscape, GlifLayout enrollLayout,
-            UdfpsEnrollView udfpsEnrollView) {
-        if (!mIsAccessibilityEnabled) return;
-
-        final Context context = getApplicationContext();
-        final View.OnHoverListener onHoverListener = (v, event) -> {
-            // Map the touch to portrait mode if the device is in
-            // landscape mode.
-            final Point scaledTouch =
-                    mUdfpsUtils.getTouchInNativeCoordinates(event.getPointerId(0),
-                            event, udfpsEnrollView.getOverlayParams());
-
-            if (mUdfpsUtils.isWithinSensorArea(event.getPointerId(0), event,
-                    udfpsEnrollView.getOverlayParams())) {
-                return false;
-            }
-
-            final String theStr = mUdfpsUtils.onTouchOutsideOfSensorArea(
-                    mAccessibilityManager.isTouchExplorationEnabled(), context,
-                    scaledTouch.x, scaledTouch.y, udfpsEnrollView.getOverlayParams());
-            if (theStr != null) {
-                v.announceForAccessibility(theStr);
-            }
-            return false;
-        };
-
-        enrollLayout.findManagedViewById(isLandscape ? R.id.sud_landscape_content_area
-                : R.id.sud_layout_content).setOnHoverListener(onHoverListener);
-    }
-
-
-    @VisibleForTesting boolean hasOverlap(View view1, View view2) {
-        int[] firstPosition = new int[2];
-        int[] secondPosition = new int[2];
-
-        view1.getLocationOnScreen(firstPosition);
-        view2.getLocationOnScreen(secondPosition);
-
-        // Rect constructor parameters: left, top, right, bottom
-        Rect rectView1 = new Rect(firstPosition[0], firstPosition[1],
-                firstPosition[0] + view1.getMeasuredWidth(),
-                firstPosition[1] + view1.getMeasuredHeight());
-        Rect rectView2 = new Rect(secondPosition[0], secondPosition[1],
-                secondPosition[0] + view2.getMeasuredWidth(),
-                secondPosition[1] + view2.getMeasuredHeight());
-        return rectView1.intersect(rectView2);
     }
 
     public static class IconTouchDialog extends InstrumentedDialogFragment {
diff --git a/src/com/android/settings/biometrics/fingerprint/UdfpsEnrollEnrollingView.java b/src/com/android/settings/biometrics/fingerprint/UdfpsEnrollEnrollingView.java
new file mode 100644
index 0000000..9225c64
--- /dev/null
+++ b/src/com/android/settings/biometrics/fingerprint/UdfpsEnrollEnrollingView.java
@@ -0,0 +1,237 @@
+/*
+ * Copyright (C) 2023 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.settings.biometrics.fingerprint;
+
+import android.content.Context;
+import android.graphics.Point;
+import android.graphics.Rect;
+import android.hardware.fingerprint.FingerprintSensorPropertiesInternal;
+import android.text.TextUtils;
+import android.util.AttributeSet;
+import android.view.DisplayInfo;
+import android.view.Gravity;
+import android.view.Surface;
+import android.view.View;
+import android.view.ViewGroup;
+import android.view.accessibility.AccessibilityManager;
+import android.widget.Button;
+import android.widget.FrameLayout;
+import android.widget.ImageView;
+import android.widget.LinearLayout;
+
+import androidx.annotation.ColorInt;
+
+import com.android.internal.annotations.VisibleForTesting;
+import com.android.settings.R;
+import com.android.settingslib.udfps.UdfpsOverlayParams;
+import com.android.settingslib.udfps.UdfpsUtils;
+
+import com.google.android.setupcompat.template.FooterBarMixin;
+import com.google.android.setupdesign.GlifLayout;
+import com.google.android.setupdesign.view.BottomScrollView;
+
+import java.util.Locale;
+
+/**
+ * View for udfps enrolling.
+ */
+public class UdfpsEnrollEnrollingView extends GlifLayout {
+    private final UdfpsUtils mUdfpsUtils;
+    private final Context mContext;
+    // We don't need to listen to onConfigurationChanged() for mRotation here because
+    // FingerprintEnrollEnrolling is always recreated once the configuration is changed.
+    private final int mRotation;
+    private final boolean mIsLandscape;
+    private final boolean mShouldUseReverseLandscape;
+    private UdfpsEnrollView mUdfpsEnrollView;
+    private View mHeaderView;
+    private AccessibilityManager mAccessibilityManager;
+
+
+    public UdfpsEnrollEnrollingView(Context context, AttributeSet attrs) {
+        super(context, attrs);
+        mContext = context;
+        mRotation = mContext.getDisplay().getRotation();
+        mIsLandscape = mRotation == Surface.ROTATION_90 || mRotation == Surface.ROTATION_270;
+        final boolean isLayoutRtl = (TextUtils.getLayoutDirectionFromLocale(Locale.getDefault())
+                == View.LAYOUT_DIRECTION_RTL);
+        mShouldUseReverseLandscape = (mRotation == Surface.ROTATION_90 && isLayoutRtl)
+                || (mRotation == Surface.ROTATION_270 && !isLayoutRtl);
+
+        mUdfpsUtils = new UdfpsUtils();
+    }
+
+    @Override
+    protected void onFinishInflate() {
+        super.onFinishInflate();
+        mHeaderView = findViewById(R.id.sud_landscape_header_area);
+        mUdfpsEnrollView = findViewById(R.id.udfps_animation_view);
+    }
+
+    void initView(FingerprintSensorPropertiesInternal udfpsProps,
+            UdfpsEnrollHelper udfpsEnrollHelper,
+            AccessibilityManager accessibilityManager) {
+        mAccessibilityManager = accessibilityManager;
+        initUdfpsEnrollView(mUdfpsEnrollView, udfpsProps, udfpsEnrollHelper);
+
+        if (!mIsLandscape) {
+            adjustPortraitPaddings();
+        } else if (mShouldUseReverseLandscape) {
+            swapHeaderAndContent();
+        }
+        setOnHoverListener();
+    }
+
+    void setSecondaryButtonBackground(@ColorInt int color) {
+        // Set the button background only when the button is not under udfps overlay to avoid UI
+        // overlap.
+        if (!mIsLandscape || mShouldUseReverseLandscape) {
+            return;
+        }
+        final Button secondaryButtonView =
+                getMixin(FooterBarMixin.class).getSecondaryButtonView();
+        secondaryButtonView.setBackgroundColor(color);
+        if (mRotation == Surface.ROTATION_90) {
+            secondaryButtonView.setGravity(Gravity.START);
+        } else {
+            secondaryButtonView.setGravity(Gravity.END);
+        }
+        mHeaderView.post(() -> {
+            secondaryButtonView.setLayoutParams(
+                    new LinearLayout.LayoutParams(mHeaderView.getMeasuredWidth(),
+                            ViewGroup.LayoutParams.WRAP_CONTENT));
+        });
+    }
+
+    private void initUdfpsEnrollView(UdfpsEnrollView udfpsEnrollView,
+                                     FingerprintSensorPropertiesInternal udfpsProps,
+                                     UdfpsEnrollHelper udfpsEnrollHelper) {
+        DisplayInfo displayInfo = new DisplayInfo();
+        mContext.getDisplay().getDisplayInfo(displayInfo);
+
+        final float scaleFactor = mUdfpsUtils.getScaleFactor(displayInfo);
+        Rect udfpsBounds = udfpsProps.getLocation().getRect();
+        udfpsBounds.scale(scaleFactor);
+
+        final Rect overlayBounds = new Rect(
+                0, /* left */
+                displayInfo.getNaturalHeight() / 2, /* top */
+                displayInfo.getNaturalWidth(), /* right */
+                displayInfo.getNaturalHeight() /* botom */);
+
+        UdfpsOverlayParams params = new UdfpsOverlayParams(
+                udfpsBounds,
+                overlayBounds,
+                displayInfo.getNaturalWidth(),
+                displayInfo.getNaturalHeight(),
+                scaleFactor,
+                displayInfo.rotation);
+
+        udfpsEnrollView.setOverlayParams(params);
+        udfpsEnrollView.setEnrollHelper(udfpsEnrollHelper);
+    }
+
+    private void adjustPortraitPaddings() {
+        // In the portrait mode, layout_container's height is 0, so it's
+        // always shown at the bottom of the screen.
+        final FrameLayout portraitLayoutContainer = findViewById(R.id.layout_container);
+
+        // In the portrait mode, the title and lottie animation view may
+        // overlap when title needs three lines, so adding some paddings
+        // between them, and adjusting the fp progress view here accordingly.
+        final int layoutLottieAnimationPadding = (int) getResources()
+                .getDimension(R.dimen.udfps_lottie_padding_top);
+        portraitLayoutContainer.setPadding(0,
+                layoutLottieAnimationPadding, 0, 0);
+        final ImageView progressView = mUdfpsEnrollView.findViewById(
+                R.id.udfps_enroll_animation_fp_progress_view);
+        progressView.setPadding(0, -(layoutLottieAnimationPadding),
+                0, layoutLottieAnimationPadding);
+        final ImageView fingerprintView = mUdfpsEnrollView.findViewById(
+                R.id.udfps_enroll_animation_fp_view);
+        fingerprintView.setPadding(0, -layoutLottieAnimationPadding,
+                0, layoutLottieAnimationPadding);
+
+        // TODO(b/260970216) Instead of hiding the description text view, we should
+        //  make the header view scrollable if the text is too long.
+        // If description text view has overlap with udfps progress view, hide it.
+        final View descView = getDescriptionTextView();
+        getViewTreeObserver().addOnDrawListener(() -> {
+            if (descView.getVisibility() == View.VISIBLE
+                    && hasOverlap(descView, mUdfpsEnrollView)) {
+                descView.setVisibility(View.GONE);
+            }
+        });
+    }
+
+    private void setOnHoverListener() {
+        if (!mAccessibilityManager.isEnabled()) return;
+
+        final View.OnHoverListener onHoverListener = (v, event) -> {
+            // Map the touch to portrait mode if the device is in
+            // landscape mode.
+            final Point scaledTouch =
+                    mUdfpsUtils.getTouchInNativeCoordinates(event.getPointerId(0),
+                            event, mUdfpsEnrollView.getOverlayParams());
+
+            if (mUdfpsUtils.isWithinSensorArea(event.getPointerId(0), event,
+                    mUdfpsEnrollView.getOverlayParams())) {
+                return false;
+            }
+
+            final String theStr = mUdfpsUtils.onTouchOutsideOfSensorArea(
+                    mAccessibilityManager.isTouchExplorationEnabled(), mContext,
+                    scaledTouch.x, scaledTouch.y, mUdfpsEnrollView.getOverlayParams());
+            if (theStr != null) {
+                v.announceForAccessibility(theStr);
+            }
+            return false;
+        };
+
+        findManagedViewById(mIsLandscape ? R.id.sud_landscape_content_area
+                : R.id.sud_layout_content).setOnHoverListener(onHoverListener);
+    }
+
+    private void swapHeaderAndContent() {
+        // Reverse header and body
+        ViewGroup parentView = (ViewGroup) mHeaderView.getParent();
+        parentView.removeView(mHeaderView);
+        parentView.addView(mHeaderView);
+
+        // Hide scroll indicators
+        BottomScrollView headerScrollView = mHeaderView.findViewById(R.id.sud_header_scroll_view);
+        headerScrollView.setScrollIndicators(0);
+    }
+
+    @VisibleForTesting
+    boolean hasOverlap(View view1, View view2) {
+        int[] firstPosition = new int[2];
+        int[] secondPosition = new int[2];
+
+        view1.getLocationOnScreen(firstPosition);
+        view2.getLocationOnScreen(secondPosition);
+
+        // Rect constructor parameters: left, top, right, bottom
+        Rect rectView1 = new Rect(firstPosition[0], firstPosition[1],
+                firstPosition[0] + view1.getMeasuredWidth(),
+                firstPosition[1] + view1.getMeasuredHeight());
+        Rect rectView2 = new Rect(secondPosition[0], secondPosition[1],
+                secondPosition[0] + view2.getMeasuredWidth(),
+                secondPosition[1] + view2.getMeasuredHeight());
+        return rectView1.intersect(rectView2);
+    }
+}
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 959c642..8c84128 100644
--- a/tests/robotests/src/com/android/settings/biometrics/fingerprint/FingerprintEnrollEnrollingTest.java
+++ b/tests/robotests/src/com/android/settings/biometrics/fingerprint/FingerprintEnrollEnrollingTest.java
@@ -56,6 +56,7 @@
 import android.view.Display;
 import android.view.Surface;
 import android.view.View;
+import android.view.accessibility.AccessibilityManager;
 import android.widget.FrameLayout;
 import android.widget.ImageView;
 import android.widget.TextView;
@@ -314,11 +315,17 @@
     @Test
     public void fingerprintUdfpsOverlayEnrollment_descriptionViewGoneWithOverlap() {
         initializeActivityWithoutCreate(TYPE_UDFPS_OPTICAL);
-        doReturn(true).when(mActivity).hasOverlap(any(), any());
         when(mMockDisplay.getRotation()).thenReturn(Surface.ROTATION_0);
         createActivity();
 
-        final GlifLayout defaultLayout = spy(mActivity.findViewById(R.id.setup_wizard_layout));
+        final UdfpsEnrollEnrollingView defaultLayout = spy(
+                mActivity.findViewById(R.id.setup_wizard_layout));
+        doReturn(true).when(defaultLayout).hasOverlap(any(), any());
+
+        // Somehow spy doesn't work, and we need to call initView manually.
+        defaultLayout.initView(mFingerprintManager.getSensorPropertiesInternal().get(0),
+                mActivity.mUdfpsEnrollHelper,
+                mActivity.getSystemService(AccessibilityManager.class));
         final TextView descriptionTextView = defaultLayout.getDescriptionTextView();
 
         defaultLayout.getViewTreeObserver().dispatchOnDraw();
@@ -328,11 +335,17 @@
     @Test
     public void fingerprintUdfpsOverlayEnrollment_descriptionViewVisibleWithoutOverlap() {
         initializeActivityWithoutCreate(TYPE_UDFPS_OPTICAL);
-        doReturn(false).when(mActivity).hasOverlap(any(), any());
         when(mMockDisplay.getRotation()).thenReturn(Surface.ROTATION_0);
         createActivity();
 
-        final GlifLayout defaultLayout = spy(mActivity.findViewById(R.id.setup_wizard_layout));
+        final UdfpsEnrollEnrollingView defaultLayout = spy(
+                mActivity.findViewById(R.id.setup_wizard_layout));
+        doReturn(false).when(defaultLayout).hasOverlap(any(), any());
+
+        // Somehow spy doesn't work, and we need to call initView manually.
+        defaultLayout.initView(mFingerprintManager.getSensorPropertiesInternal().get(0),
+                mActivity.mUdfpsEnrollHelper,
+                mActivity.getSystemService(AccessibilityManager.class));
         final TextView descriptionTextView = defaultLayout.getDescriptionTextView();
 
         defaultLayout.getViewTreeObserver().dispatchOnDraw();
@@ -578,7 +591,6 @@
         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);