Donate to e Foundation | Murena handsets with /e/OS | Own a part of Murena! Learn more

Commit 7eca60d9 authored by Beverly's avatar Beverly Committed by Beverly Tai
Browse files

Add alternate bouncer UDFPS rotation support

The alternate bouncer can show over an occluding application.
In this case, the alternate bouncer may be requested in
different orientations. Add support for the UDFPS icon in
the alternate bouncer to support different rotations.

This adds support for landscape and seascape but doesn't
support upside down because UdfpsOverlayParams doesn't
support upside down yet.

This CL also updates a11y announcements for UDFPS.

Bug: 330880858
Flag: ACONFIG com.android.systemui.device_entry_udfps_refactor TRUNKFOOD
Test: atest UdfpsUtilsTest
Test: with udfps enrolled and auto-rotate enabled, double press
the power button to navigate to the camera app from the lockscreen. Tap
the gallery icon and then rotate. Observe UDFPS icon is visible and
user can auth from seascape and landscape.

Change-Id: I63018579c118323d7721cd94486f14e4362df196
parent e3dcebf5
Loading
Loading
Loading
Loading
+76 −19
Original line number Diff line number Diff line
@@ -54,8 +54,9 @@ public class UdfpsUtils {
    }

    /**
     * Gets the touch in native coordinates. Map the touch to portrait mode if the device is in
     * landscape mode.
     * Gets the touch in native coordinates.
     *
     * Maps the touch to portrait mode if the device is in landscape mode.
     *
     * @param idx                The pointer identifier.
     * @param event              The MotionEvent object containing full information about the event.
@@ -64,35 +65,87 @@ public class UdfpsUtils {
     */
    public Point getTouchInNativeCoordinates(int idx, MotionEvent event,
            UdfpsOverlayParams udfpsOverlayParams) {
        Point portraitTouch = getPortraitTouch(idx, event, udfpsOverlayParams);
        return getTouchInNativeCoordinates(idx, event, udfpsOverlayParams, true);
    }

    /**
     * Gets the touch in native coordinates.
     *
     * Optionally map the touch to portrait mode if the device is in landscape mode.
     *
     * @param idx                The pointer identifier.
     * @param event              The MotionEvent object containing full information about the event.
     * @param udfpsOverlayParams The [UdfpsOverlayParams] used.
     * @param rotateToPortrait   Whether to rotate the touch to portrait orientation.
     * @return The mapped touch event.
     */
    public Point getTouchInNativeCoordinates(int idx, MotionEvent event,
            UdfpsOverlayParams udfpsOverlayParams, boolean rotateToPortrait) {
        Point touch;
        if (rotateToPortrait) {
            touch = getPortraitTouch(idx, event, udfpsOverlayParams);
        } else {
            touch = new Point((int) event.getRawX(idx), (int) event.getRawY(idx));
        }

        // Scale the coordinates to native resolution.
        float scale = udfpsOverlayParams.getScaleFactor();
        portraitTouch.x = (int) (portraitTouch.x / scale);
        portraitTouch.y = (int) (portraitTouch.y / scale);
        return portraitTouch;
        touch.x = (int) (touch.x / scale);
        touch.y = (int) (touch.y / scale);
        return touch;
    }

    /**
     * @param idx                The pointer identifier.
     * @param event              The MotionEvent object containing full information about the event.
     * @param udfpsOverlayParams The [UdfpsOverlayParams] used.
     * @return Whether the touch event is within sensor area.
     * @return Whether the touch event (that needs to be rotated to portrait) is within sensor area.
     */
    public boolean isWithinSensorArea(int idx, MotionEvent event,
            UdfpsOverlayParams udfpsOverlayParams) {
        Point portraitTouch = getPortraitTouch(idx, event, udfpsOverlayParams);
        return udfpsOverlayParams.getSensorBounds().contains(portraitTouch.x, portraitTouch.y);
        return isWithinSensorArea(idx, event, udfpsOverlayParams, true);
    }

    /**
     * @param idx                The pointer identifier.
     * @param event              The MotionEvent object containing full information about the event.
     * @param udfpsOverlayParams The [UdfpsOverlayParams] used.
     * @param rotateTouchToPortrait Whether to rotate the touch coordinates to portrait.
     * @return Whether the touch event is within sensor area.
     */
    public boolean isWithinSensorArea(int idx, MotionEvent event,
            UdfpsOverlayParams udfpsOverlayParams, boolean rotateTouchToPortrait) {
        Point touch;
        if (rotateTouchToPortrait) {
            touch = getPortraitTouch(idx, event, udfpsOverlayParams);
        } else {
            touch = new Point((int) event.getRawX(idx), (int) event.getRawY(idx));
        }
        return udfpsOverlayParams.getSensorBounds().contains(touch.x, touch.y);
    }

    /**
     * This function computes the angle of touch relative to the sensor, rotated to portrait,
     * and maps the angle to a list of help messages which are announced if accessibility is
     * enabled.
     *
     * @return announcement string
     */
    public String onTouchOutsideOfSensorArea(boolean touchExplorationEnabled, Context context,
            int scaledTouchX, int scaledTouchY, UdfpsOverlayParams udfpsOverlayParams) {
        return onTouchOutsideOfSensorArea(touchExplorationEnabled, context, scaledTouchX,
                scaledTouchY, udfpsOverlayParams, true);
    }

    /**
     * This function computes the angle of touch relative to the sensor and maps the angle to a list
     * of help messages which are announced if accessibility is enabled.
     *
     * @return Whether the announcing string is null
     * @return announcement string
     */
    public String onTouchOutsideOfSensorArea(boolean touchExplorationEnabled, Context context,
            int scaledTouchX, int scaledTouchY, UdfpsOverlayParams udfpsOverlayParams) {
            int scaledTouchX, int scaledTouchY, UdfpsOverlayParams udfpsOverlayParams,
            boolean touchRotatedToPortrait) {
        if (!touchExplorationEnabled) {
            return null;
        }
@@ -116,7 +169,8 @@ public class UdfpsUtils {
                        scaledTouchY,
                        scaledSensorX,
                        scaledSensorY,
                        udfpsOverlayParams.getRotation()
                        udfpsOverlayParams.getRotation(),
                        touchRotatedToPortrait
                );
        Log.v(TAG, "Announcing touch outside : $theStr");
        return theStr;
@@ -132,7 +186,7 @@ public class UdfpsUtils {
     * touchHints[1] = "Move Fingerprint down" And so on.
     */
    private String onTouchOutsideOfSensorAreaImpl(String[] touchHints, float touchX,
            float touchY, float sensorX, float sensorY, int rotation) {
            float touchY, float sensorX, float sensorY, int rotation, boolean rotatedToPortrait) {
        float xRelativeToSensor = touchX - sensorX;
        // Touch coordinates are with respect to the upper left corner, so reverse
        // this calculation
@@ -153,6 +207,7 @@ public class UdfpsUtils {
        int index = (int) ((degrees + halfBucketDegrees) % 360 / degreesPerBucket);
        index %= touchHints.length;

        if (rotatedToPortrait) {
            // A rotation of 90 degrees corresponds to increasing the index by 1.
            if (rotation == Surface.ROTATION_90) {
                index = (index + 1) % touchHints.length;
@@ -160,6 +215,8 @@ public class UdfpsUtils {
            if (rotation == Surface.ROTATION_270) {
                index = (index + 3) % touchHints.length;
            }
        }

        return touchHints[index];
    }

+15 −10
Original line number Diff line number Diff line
@@ -400,10 +400,15 @@ public class UdfpsController implements DozeReceiver, Dumpable {
        if (!mOverlayParams.equals(overlayParams)) {
            mOverlayParams = overlayParams;

            final boolean wasShowingAlternateBouncer = mAlternateBouncerInteractor.isVisibleState();

            // When the bounds change it's always necessary to re-create the overlay's window with
            // new LayoutParams. If the overlay needs to be shown, this will re-create and show the
            if (DeviceEntryUdfpsRefactor.isEnabled()) {
                if (mOverlay != null && mOverlay.getRequestReason() == REASON_AUTH_KEYGUARD) {
                    mOverlay.updateOverlayParams(mOverlayParams);
                }
            } else {
                final boolean wasShowingAlternateBouncer =
                        mAlternateBouncerInteractor.isVisibleState();
                // When the bounds change it's always to re-create the overlay's window with new
                // LayoutParams. If the overlay needs to be shown, this will re-create and show the
                // overlay with the updated LayoutParams. Otherwise, the overlay will remain hidden.
                redrawOverlay();
                if (wasShowingAlternateBouncer) {
@@ -411,6 +416,7 @@ public class UdfpsController implements DozeReceiver, Dumpable {
                }
            }
        }
    }

    // TODO(b/229290039): UDFPS controller should manage its dimensions on its own. Remove this.
    public void setAuthControllerUpdateUdfpsLocation(@Nullable Runnable r) {
@@ -850,7 +856,6 @@ public class UdfpsController implements DozeReceiver, Dumpable {

        mOverlay = null;
        mOrientationListener.disable();

    }

    private void unconfigureDisplay(View view) {
@@ -859,7 +864,7 @@ public class UdfpsController implements DozeReceiver, Dumpable {
        }
        if (DeviceEntryUdfpsRefactor.isEnabled()) {
            if (mUdfpsDisplayMode != null) {
                mUdfpsDisplayMode.disable(null); // beverlt
                mUdfpsDisplayMode.disable(null);
            }
        } else {
            if (view != null) {
+9 −0
Original line number Diff line number Diff line
@@ -318,6 +318,15 @@ class UdfpsControllerOverlay @JvmOverloads constructor(
        addViewRunnable = null
    }

    fun updateOverlayParams(updatedOverlayParams: UdfpsOverlayParams) {
        DeviceEntryUdfpsRefactor.isUnexpectedlyInLegacyMode()
        overlayParams = updatedOverlayParams
        sensorBounds = updatedOverlayParams.sensorBounds
        getTouchOverlay()?.let {
            windowManager.updateViewLayout(it, coreLayoutParams.updateDimensions(null))
        }
    }

    fun inflateUdfpsAnimation(
        view: UdfpsView,
        controller: UdfpsController
+11 −0
Original line number Diff line number Diff line
@@ -17,6 +17,7 @@
package com.android.systemui.biometrics.domain.interactor

import android.content.Context
import android.graphics.Rect
import android.hardware.biometrics.SensorLocationInternal
import com.android.systemui.biometrics.data.repository.FingerprintPropertyRepository
import com.android.systemui.biometrics.shared.model.SensorLocation
@@ -43,6 +44,7 @@ constructor(
    repository: FingerprintPropertyRepository,
    configurationInteractor: ConfigurationInteractor,
    displayStateInteractor: DisplayStateInteractor,
    udfpsOverlayInteractor: UdfpsOverlayInteractor,
) {
    val propertiesInitialized: StateFlow<Boolean> = repository.propertiesInitialized
    val isUdfps: StateFlow<Boolean> =
@@ -103,4 +105,13 @@ constructor(
            sensorLocation.scale = scale
            sensorLocation
        }

    /**
     * Sensor location for the:
     * - current physical display
     * - current screen resolution
     * - device's current orientation
     */
    val udfpsSensorBounds: Flow<Rect> =
        udfpsOverlayInteractor.udfpsOverlayParams.map { it.sensorBounds }.distinctUntilChanged()
}
+15 −2
Original line number Diff line number Diff line
@@ -54,9 +54,21 @@ abstract class UdfpsAccessibilityOverlayViewModel(
    fun onHoverEvent(v: View, event: MotionEvent): Boolean {
        val overlayParams = udfpsOverlayParams.value
        val scaledTouch: Point =
            udfpsUtils.getTouchInNativeCoordinates(event.getPointerId(0), event, overlayParams)
            udfpsUtils.getTouchInNativeCoordinates(
                event.getPointerId(0),
                event,
                overlayParams, /* rotateToPortrait */
                false
            )

        if (!udfpsUtils.isWithinSensorArea(event.getPointerId(0), event, overlayParams)) {
        if (
            !udfpsUtils.isWithinSensorArea(
                event.getPointerId(0),
                event,
                overlayParams,
                /* rotateTouchToPortrait */ false
            )
        ) {
            // view only receives motionEvents when [visible] which requires touchExplorationEnabled
            val announceStr =
                udfpsUtils.onTouchOutsideOfSensorArea(
@@ -65,6 +77,7 @@ abstract class UdfpsAccessibilityOverlayViewModel(
                    scaledTouch.x,
                    scaledTouch.y,
                    overlayParams,
                    /* touchRotatedToPortrait */ false
                )
            if (announceStr != null) {
                v.announceForAccessibility(announceStr)
Loading