Merge "Add udfps/ and support a11y for the udfps enroll view."
diff --git a/res/layout/udfps_enroll_view.xml b/res/layout/udfps_enroll_view.xml
index e1c2152..6bf339b 100644
--- a/res/layout/udfps_enroll_view.xml
+++ b/res/layout/udfps_enroll_view.xml
@@ -20,15 +20,6 @@
     android:layout_width="match_parent"
     android:layout_height="match_parent">
 
-    <!-- The layout height/width are placeholders, which will be overwritten by
-         FingerprintSensorPropertiesInternal. -->
-    <View
-        android:id="@+id/udfps_enroll_accessibility_view"
-        android:layout_gravity="center"
-        android:layout_width="match_parent"
-        android:layout_height="match_parent"
-        android:contentDescription="@string/accessibility_fingerprint_label"/>
-
     <ImageView
         android:id="@+id/udfps_enroll_animation_fp_progress_view"
         android:layout_width="match_parent"
diff --git a/src/com/android/settings/biometrics/fingerprint/FingerprintEnrollEnrolling.java b/src/com/android/settings/biometrics/fingerprint/FingerprintEnrollEnrolling.java
index 139a18f..c9c5b37 100644
--- a/src/com/android/settings/biometrics/fingerprint/FingerprintEnrollEnrolling.java
+++ b/src/com/android/settings/biometrics/fingerprint/FingerprintEnrollEnrolling.java
@@ -32,6 +32,7 @@
 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;
@@ -40,7 +41,6 @@
 import android.graphics.drawable.Drawable;
 import android.graphics.drawable.LayerDrawable;
 import android.hardware.fingerprint.FingerprintManager;
-import android.hardware.fingerprint.FingerprintSensorProperties;
 import android.hardware.fingerprint.FingerprintSensorPropertiesInternal;
 import android.os.Bundle;
 import android.os.Process;
@@ -48,10 +48,8 @@
 import android.os.VibrationEffect;
 import android.os.Vibrator;
 import android.text.TextUtils;
-import android.util.DisplayUtils;
 import android.util.FeatureFlagUtils;
 import android.util.Log;
-import android.view.Display;
 import android.view.DisplayInfo;
 import android.view.MotionEvent;
 import android.view.OrientationEventListener;
@@ -77,6 +75,8 @@
 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;
@@ -200,6 +200,7 @@
     private boolean mHaveShownSfpsLeftEdgeLottie;
     private boolean mHaveShownSfpsRightEdgeLottie;
     private boolean mShouldShowLottie;
+    private UdfpsUtils mUdfpsUtils;
 
     private OrientationEventListener mOrientationEventListener;
     private int mPreviousRotation = 0;
@@ -254,6 +255,7 @@
 
         mAccessibilityManager = getSystemService(AccessibilityManager.class);
         mIsAccessibilityEnabled = mAccessibilityManager.isEnabled();
+        mUdfpsUtils = new UdfpsUtils();
 
         final boolean isLayoutRtl = (TextUtils.getLayoutDirectionFromLocale(
                 Locale.getDefault()) == View.LAYOUT_DIRECTION_RTL);
@@ -280,7 +282,9 @@
                     layoutContainer.setLayoutParams(lp);
                     if (FeatureFlagUtils.isEnabled(getApplicationContext(),
                             FeatureFlagUtils.SETTINGS_SHOW_UDFPS_ENROLL_IN_SETTINGS)) {
-                        layout.addView(addUdfpsEnrollView(props.get(0)));
+                        final UdfpsEnrollView udfpsEnrollView = addUdfpsEnrollView(props.get(0));
+                        layout.addView(udfpsEnrollView);
+                        setOnHoverListener(true, layout, udfpsEnrollView);
                     }
                     setContentView(layout, lp);
                     break;
@@ -293,6 +297,7 @@
                             R.layout.udfps_enroll_enrolling, null, false);
                     if (FeatureFlagUtils.isEnabled(getApplicationContext(),
                             FeatureFlagUtils.SETTINGS_SHOW_UDFPS_ENROLL_IN_SETTINGS)) {
+                        final UdfpsEnrollView udfpsEnrollView = addUdfpsEnrollView(props.get(0));
                         if (rotation == Surface.ROTATION_0 || rotation == Surface.ROTATION_180) {
                             // In the portrait mode, set layout_container's height 0, so it's
                             // always shown at the bottom of the screen.
@@ -307,9 +312,11 @@
                             final ViewGroup.LayoutParams containerLp =
                                     portraitLayoutContainer.getLayoutParams();
                             containerLp.height = 0;
-                            portraitLayoutContainer.addView(addUdfpsEnrollView(props.get(0)));
+                            portraitLayoutContainer.addView(udfpsEnrollView);
+                            setOnHoverListener(false, defaultLayout, udfpsEnrollView);
                         } else if (rotation == Surface.ROTATION_270) {
-                            defaultLayout.addView(addUdfpsEnrollView(props.get(0)));
+                            defaultLayout.addView(udfpsEnrollView);
+                            setOnHoverListener(true, defaultLayout, udfpsEnrollView);
                         }
                     }
 
@@ -1197,17 +1204,7 @@
 
         DisplayInfo displayInfo = new DisplayInfo();
         getDisplay().getDisplayInfo(displayInfo);
-        final Display.Mode maxDisplayMode =
-                DisplayUtils.getMaximumResolutionDisplayMode(displayInfo.supportedModes);
-        final float scaleFactor = android.util.DisplayUtils.getPhysicalPixelDisplaySizeRatio(
-                maxDisplayMode.getPhysicalWidth(), maxDisplayMode.getPhysicalHeight(),
-                displayInfo.getNaturalWidth(), displayInfo.getNaturalHeight());
-        if (scaleFactor == Float.POSITIVE_INFINITY) {
-            mScaleFactor = 1f;
-        } else {
-            mScaleFactor = scaleFactor;
-        }
-
+        mScaleFactor = mUdfpsUtils.getScaleFactor(displayInfo);
         Rect udfpsBounds = udfpsProps.getLocation().getRect();
         udfpsBounds.scale(mScaleFactor);
 
@@ -1217,16 +1214,13 @@
                 displayInfo.getNaturalWidth(), /* right */
                 displayInfo.getNaturalHeight() /* botom */);
 
-        // TODO(b/260617060): Extract this logic into a 3rd party library for both Settings and
-        //  SysUI to depend on.
         UdfpsOverlayParams params = new UdfpsOverlayParams(
                 udfpsBounds,
                 overlayBounds,
                 displayInfo.getNaturalWidth(),
                 displayInfo.getNaturalHeight(),
                 mScaleFactor,
-                displayInfo.rotation,
-                udfpsProps.sensorType == FingerprintSensorProperties.TYPE_UDFPS_OPTICAL);
+                displayInfo.rotation);
 
         udfpsEnrollView.setOverlayParams(params);
 
@@ -1244,6 +1238,31 @@
         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());
+
+            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);
+    }
+
     public static class IconTouchDialog extends InstrumentedDialogFragment {
 
         @Override
diff --git a/src/com/android/settings/biometrics/fingerprint/UdfpsEnrollView.java b/src/com/android/settings/biometrics/fingerprint/UdfpsEnrollView.java
index 3d77f0e..96b49aa 100644
--- a/src/com/android/settings/biometrics/fingerprint/UdfpsEnrollView.java
+++ b/src/com/android/settings/biometrics/fingerprint/UdfpsEnrollView.java
@@ -25,7 +25,6 @@
 import android.util.RotationUtils;
 import android.view.Gravity;
 import android.view.Surface;
-import android.view.View;
 import android.view.ViewGroup;
 import android.widget.FrameLayout;
 import android.widget.ImageView;
@@ -34,11 +33,13 @@
 import androidx.annotation.Nullable;
 
 import com.android.settings.R;
+import com.android.settingslib.udfps.UdfpsOverlayParams;
 
 /**
  * View corresponding with udfps_enroll_view.xml
  */
 public class UdfpsEnrollView extends FrameLayout implements UdfpsEnrollHelper.Listener {
+    private static final String TAG = "UdfpsEnrollView";
     @NonNull
     private final UdfpsEnrollDrawable mFingerprintDrawable;
     @NonNull
@@ -98,12 +99,15 @@
         onFingerDown();
     }
 
-
     @Override
     public void onPointerUp(int sensorId) {
         onFingerUp();
     }
 
+    public UdfpsOverlayParams getOverlayParams() {
+        return mOverlayParams;
+    }
+
     void setOverlayParams(UdfpsOverlayParams params) {
         mOverlayParams = params;
 
@@ -124,7 +128,6 @@
 
     private void onSensorRectUpdated() {
         updateDimensions();
-        updateAccessibilityViewLocation();
 
         // Updates sensor rect in relation to the overlay view
         mSensorRect.set(getPaddingX(), getPaddingY(),
@@ -135,7 +138,7 @@
 
     private void updateDimensions() {
         // Original sensorBounds assume portrait mode.
-        final Rect rotatedBounds = mOverlayParams.getSensorBounds();
+        final Rect rotatedBounds = new Rect(mOverlayParams.getSensorBounds());
         int rotation = mOverlayParams.getRotation();
         if (rotation == Surface.ROTATION_90 || rotation == Surface.ROTATION_270) {
             RotationUtils.rotateBounds(
@@ -192,27 +195,14 @@
         setLayoutParams(params);
     }
 
-    private void updateAccessibilityViewLocation() {
-        View fingerprintAccessibilityView = findViewById(R.id.udfps_enroll_accessibility_view);
-        ViewGroup.LayoutParams params = fingerprintAccessibilityView.getLayoutParams();
-        params.width = mOverlayParams.getSensorBounds().width();
-        params.height = mOverlayParams.getSensorBounds().height();
-        fingerprintAccessibilityView.setLayoutParams(params);
-        fingerprintAccessibilityView.requestLayout();
-    }
-
     private void onFingerDown() {
-        if (mOverlayParams.isOptical()) {
-            mFingerprintDrawable.setShouldSkipDraw(true);
-            mFingerprintDrawable.invalidateSelf();
-        }
+        mFingerprintDrawable.setShouldSkipDraw(true);
+        mFingerprintDrawable.invalidateSelf();
     }
 
     private void onFingerUp() {
-        if (mOverlayParams.isOptical()) {
-            mFingerprintDrawable.setShouldSkipDraw(false);
-            mFingerprintDrawable.invalidateSelf();
-        }
+        mFingerprintDrawable.setShouldSkipDraw(false);
+        mFingerprintDrawable.invalidateSelf();
     }
 
     private int getPaddingX() {
diff --git a/src/com/android/settings/biometrics/fingerprint/UdfpsOverlayParams.java b/src/com/android/settings/biometrics/fingerprint/UdfpsOverlayParams.java
deleted file mode 100644
index 9b52ad6..0000000
--- a/src/com/android/settings/biometrics/fingerprint/UdfpsOverlayParams.java
+++ /dev/null
@@ -1,95 +0,0 @@
-/*
- * Copyright (C) 2022 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.graphics.Rect;
-
-import androidx.annotation.NonNull;
-
-/**
- * Collection of parameters that define an under-display fingerprint sensor (UDFPS) overlay.
- *
- * [sensorBounds] coordinates of the bounding box around the sensor in natural orientation, in
- * pixels, for the current resolution.
- *
- * [overlayBounds] coordinates of the UI overlay in natural orientation, in pixels, for the current
- * resolution.
- *
- * [naturalDisplayWidth] width of the physical display in natural orientation, in pixels, for the
- * current resolution.
- *
- * [naturalDisplayHeight] height of the physical display in natural orientation, in pixels, for the
- * current resolution.
- *
- * [scaleFactor] ratio of a dimension in the current resolution to the corresponding dimension in
- * the native resolution.
- *
- * [rotation] current rotation of the display.
- */
-public final class UdfpsOverlayParams {
-    @NonNull
-    private final Rect mSensorBounds;
-    @NonNull
-    private final Rect mOverlayBounds;
-    private final int mNaturalDisplayWidth;
-    private final int mNaturalDisplayHeight;
-    private final float mScaleFactor;
-    private final int mRotation;
-    private final boolean mIsOptical;
-
-    public UdfpsOverlayParams(@NonNull Rect sensorBounds, @NonNull Rect overlayBounds,
-            int naturalDisplayWidth, int naturalDisplayHeight, float scaleFactor, int rotation,
-            boolean isOptical) {
-        mSensorBounds = sensorBounds;
-        mOverlayBounds = overlayBounds;
-        mNaturalDisplayWidth = naturalDisplayWidth;
-        mNaturalDisplayHeight = naturalDisplayHeight;
-        mScaleFactor = scaleFactor;
-        mRotation = rotation;
-        mIsOptical = isOptical;
-    }
-
-    @NonNull
-    public Rect getSensorBounds() {
-        return mSensorBounds;
-    }
-
-    @NonNull
-    public Rect getOverlayBounds() {
-        return mOverlayBounds;
-    }
-
-    public int getNaturalDisplayWidth() {
-        return mNaturalDisplayWidth;
-    }
-
-    public int getNaturalDisplayHeight() {
-        return mNaturalDisplayHeight;
-    }
-
-    public float getScaleFactor() {
-        return mScaleFactor;
-    }
-
-    public int getRotation() {
-        return mRotation;
-    }
-
-    public boolean isOptical() {
-        return mIsOptical;
-    }
-}