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

Commit ccff4ce8 authored by Treehugger Robot's avatar Treehugger Robot Committed by Android (Google) Code Review
Browse files

Merge "Run face detect if bypass face auth is locked out" into main

parents cf612cb2 19b88bbd
Loading
Loading
Loading
Loading
+1 −1
Original line number Diff line number Diff line
@@ -115,7 +115,7 @@ class FaceScanningProviderFactoryTest : SysuiTestCase() {
    @Test
    fun shouldShowFaceScanningAnimationIfKeyguardFaceDetectionIsShowing() {
        whenever(keyguardUpdateMonitor.isFaceEnabledAndEnrolled).thenReturn(true)
        whenever(keyguardUpdateMonitor.isFaceDetectionRunning).thenReturn(true)
        whenever(keyguardUpdateMonitor.isFaceAuthOrDetectionRunning).thenReturn(true)

        assertThat(underTest.shouldShowFaceScanningAnim()).isTrue()
    }
+23 −2
Original line number Diff line number Diff line
@@ -737,7 +737,7 @@ class DeviceEntryFaceAuthRepositoryTest : SysuiTestCase() {
            biometricSettingsRepository.setIsFaceAuthCurrentlyAllowed(false)
            assertThat(canFaceAuthRun()).isFalse()
            underTest.requestAuthenticate(
                FACE_AUTH_TRIGGERED_SWIPE_UP_ON_BOUNCER,
                FaceAuthUiEvent.FACE_AUTH_TRIGGERED_PICK_UP_GESTURE_TRIGGERED,
                fallbackToDetection = true,
            )
            faceAuthenticateIsNotCalled()
@@ -758,7 +758,28 @@ class DeviceEntryFaceAuthRepositoryTest : SysuiTestCase() {
            keyguardRepository.setKeyguardDismissible(true)
            assertThat(canFaceAuthRun()).isFalse()
            underTest.requestAuthenticate(
                FACE_AUTH_TRIGGERED_SWIPE_UP_ON_BOUNCER,
                FaceAuthUiEvent.FACE_AUTH_TRIGGERED_PICK_UP_GESTURE_TRIGGERED,
                fallbackToDetection = true,
            )
            faceAuthenticateIsNotCalled()

            faceDetectIsCalled()
        }

    @Test
    fun authenticateFallbacksToDetectionWhenFaceIsLockedOut() =
        testScope.runTest {
            whenever(faceManager.sensorPropertiesInternal)
                .thenReturn(listOf(createFaceSensorProperties(supportsFaceDetection = true)))
            whenever(bypassController.bypassEnabled).thenReturn(true)
            underTest = createDeviceEntryFaceAuthRepositoryImpl()
            initCollectors()
            allPreconditionsToRunFaceAuthAreTrue()

            underTest.setLockedOut(true)
            assertThat(canFaceAuthRun()).isFalse()
            underTest.requestAuthenticate(
                FaceAuthUiEvent.FACE_AUTH_TRIGGERED_PICK_UP_GESTURE_TRIGGERED,
                fallbackToDetection = true,
            )
            faceAuthenticateIsNotCalled()
+1 −2
Original line number Diff line number Diff line
@@ -190,10 +190,9 @@ class DeviceEntryFaceAuthInteractorTest : SysuiTestCase() {
        }

    @Test
    fun whenFaceIsLockedOutAndBypass_DetectRuns() =
    fun whenFaceIsLockedOutAndBypass_runningAuthRequestNotNull() =
        kosmos.runTest {
            underTest.start()
            val authenticationStatus = collectLastValue(underTest.authenticationStatus)
            faceAuthRepository.setLockedOut(true)
            fakeDeviceEntryFaceAuthRepository.isBypassEnabled.value = true

+2 −2
Original line number Diff line number Diff line
@@ -1294,7 +1294,7 @@ public class KeyguardUpdateMonitor implements TrustManager.TrustListener, CoreSt
        for (int i = 0; i < mCallbacks.size(); i++) {
            KeyguardUpdateMonitorCallback cb = mCallbacks.get(i).get();
            if (cb != null) {
                cb.onBiometricRunningStateChanged(isFaceDetectionRunning(),
                cb.onBiometricRunningStateChanged(isFaceAuthOrDetectionRunning(),
                        FACE);
            }
        }
@@ -1308,7 +1308,7 @@ public class KeyguardUpdateMonitor implements TrustManager.TrustListener, CoreSt
     * @deprecated This is being migrated to use modern architecture.
     */
    @Deprecated
    public boolean isFaceDetectionRunning() {
    public boolean isFaceAuthOrDetectionRunning() {
        return getFaceAuthInteractor() != null
                && (getFaceAuthInteractor().isAuthRunning()
                || getFaceAuthInteractor().isDetectRunning());
+114 −98
Original line number Diff line number Diff line
@@ -87,7 +87,7 @@ class FaceScanningOverlay(

    override fun enableShowProtection(isCameraActive: Boolean) {
        val scanningAnimationRequiredWhenCameraActive =
                keyguardUpdateMonitor.isFaceDetectionRunning || authController.isShowing || mDebug
            keyguardUpdateMonitor.isFaceAuthOrDetectionRunning || authController.isShowing || mDebug
        val faceAuthSucceeded = keyguardUpdateMonitor.isFaceAuthenticated
        val showScanningAnimationNow = scanningAnimationRequiredWhenCameraActive && isCameraActive
        if (showScanningAnimationNow == showScanningAnim) {
@@ -95,11 +95,12 @@ class FaceScanningOverlay(
        }
        logger.cameraProtectionShownOrHidden(
            showScanningAnimationNow,
                keyguardUpdateMonitor.isFaceDetectionRunning,
            keyguardUpdateMonitor.isFaceAuthOrDetectionRunning,
            authController.isShowing,
            faceAuthSucceeded,
            isCameraActive,
                showScanningAnim)
            showScanningAnim,
        )
        showScanningAnim = showScanningAnimationNow
        updateProtectionBoundingPath()
        // Delay the relayout until the end of the animation when hiding,
@@ -115,7 +116,8 @@ class FaceScanningOverlay(
        }

        rimAnimator?.cancel()
        rimAnimator = faceScanningRimAnimator(
        rimAnimator =
            faceScanningRimAnimator(
                faceAuthSucceeded,
                if (Flags.faceScanningAnimationNpeFix()) {
                    cameraProtectionAnimator(faceAuthSucceeded)
@@ -128,23 +130,28 @@ class FaceScanningOverlay(

    private fun faceScanningRimAnimator(
        faceAuthSucceeded: Boolean,
        cameraProtectAnimator: ValueAnimator?
        cameraProtectAnimator: ValueAnimator?,
    ): AnimatorSet {
        return if (showScanningAnim) {
            createFaceScanningRimAnimator(cameraProtectAnimator)
        } else if (faceAuthSucceeded) {
        } else {
            if (faceAuthSucceeded) {
                    createFaceSuccessRimAnimator(cameraProtectAnimator)
                } else {
                    createFaceNotSuccessRimAnimator(cameraProtectAnimator)
        }.apply {
            addListener(object : AnimatorListenerAdapter() {
                }
                .apply {
                    addListener(
                        object : AnimatorListenerAdapter() {
                            override fun onAnimationEnd(animation: Animator) {
                                rimAnimator = null
                                if (!showScanningAnim) {
                                    requestLayout()
                                }
                            }
            })
                        }
                    )
                }
        }
    }

@@ -152,8 +159,9 @@ class FaceScanningOverlay(
        return ValueAnimator.ofFloat(
                cameraProtectionProgress,
                if (showScanningAnim) SHOW_CAMERA_PROTECTION_SCALE
            else HIDDEN_CAMERA_PROTECTION_SCALE
        ).apply {
                else HIDDEN_CAMERA_PROTECTION_SCALE,
            )
            .apply {
                startDelay =
                    if (showScanningAnim) 0
                    else if (faceAuthSucceeded) PULSE_SUCCESS_DISAPPEAR_DURATION
@@ -167,7 +175,8 @@ class FaceScanningOverlay(
                    else if (faceAuthSucceeded) Interpolators.STANDARD
                    else Interpolators.STANDARD_DECELERATE
                addUpdateListener(this@FaceScanningOverlay::updateCameraProtectionProgress)
            addListener(object : AnimatorListenerAdapter() {
                addListener(
                    object : AnimatorListenerAdapter() {
                        override fun onAnimationEnd(animation: Animator) {
                            if (!Flags.faceScanningAnimationNpeFix()) {
                                cameraProtectionAnimator = null
@@ -176,7 +185,8 @@ class FaceScanningOverlay(
                                hide()
                            }
                        }
            })
                    }
                )
            }
    }

@@ -202,21 +212,25 @@ class FaceScanningOverlay(
                rimRect.left.toInt(),
                rimRect.top.toInt(),
                rimRect.right.toInt(),
                rimRect.bottom.toInt())
                rimRect.bottom.toInt(),
            )
            val measuredWidth = resolveSizeAndState(mTotalBounds.width(), widthMeasureSpec, 0)
            val measuredHeight = resolveSizeAndState(mTotalBounds.height(), heightMeasureSpec, 0)
            logger.boundingRect(rimRect, "onMeasure: Face scanning animation")
            logger.boundingRect(mBoundingRect, "onMeasure: Display cutout view bounding rect")
            logger.boundingRect(mTotalBounds, "onMeasure: TotalBounds")
            logger.onMeasureDimensions(widthMeasureSpec,
            logger.onMeasureDimensions(
                widthMeasureSpec,
                heightMeasureSpec,
                measuredWidth,
                    measuredHeight)
                measuredHeight,
            )
            setMeasuredDimension(measuredWidth, measuredHeight)
        } else {
            setMeasuredDimension(
                resolveSizeAndState(mBoundingRect.width(), widthMeasureSpec, 0),
                resolveSizeAndState(mBoundingRect.height(), heightMeasureSpec, 0))
                resolveSizeAndState(mBoundingRect.height(), heightMeasureSpec, 0),
            )
        }
    }

@@ -225,10 +239,11 @@ class FaceScanningOverlay(
        scalePath(rimPath, rimProgress)
        rimPaint.style = Paint.Style.FILL
        val rimPaintAlpha = rimPaint.alpha
        rimPaint.color = ColorUtils.blendARGB(
        rimPaint.color =
            ColorUtils.blendARGB(
                faceScanningAnimColor,
                Color.WHITE,
            statusBarStateController.dozeAmount
                statusBarStateController.dozeAmount,
            )
        rimPaint.alpha = rimPaintAlpha
        canvas.drawPath(rimPath, rimPaint)
@@ -248,22 +263,22 @@ class FaceScanningOverlay(
            createRimDisappearAnimator(
                PULSE_RADIUS_SUCCESS,
                PULSE_SUCCESS_DISAPPEAR_DURATION,
                Interpolators.STANDARD_DECELERATE
                Interpolators.STANDARD_DECELERATE,
            ),
            createSuccessOpacityAnimator(),
        )
        return AnimatorSet().apply {
            playTogether(rimSuccessAnimator, cameraProtectAnimator)
        }
        return AnimatorSet().apply { playTogether(rimSuccessAnimator, cameraProtectAnimator) }
    }

    private fun createFaceNotSuccessRimAnimator(cameraProtectAnimator: ValueAnimator?): AnimatorSet {
    private fun createFaceNotSuccessRimAnimator(
        cameraProtectAnimator: ValueAnimator?
    ): AnimatorSet {
        return AnimatorSet().apply {
            playTogether(
                createRimDisappearAnimator(
                    SHOW_CAMERA_PROTECTION_SCALE,
                    PULSE_ERROR_DISAPPEAR_DURATION,
                    Interpolators.STANDARD
                    Interpolators.STANDARD,
                ),
                cameraProtectAnimator,
            )
@@ -273,18 +288,20 @@ class FaceScanningOverlay(
    private fun createRimDisappearAnimator(
        endValue: Float,
        animDuration: Long,
        timeInterpolator: TimeInterpolator
        timeInterpolator: TimeInterpolator,
    ): ValueAnimator {
        return ValueAnimator.ofFloat(rimProgress, endValue).apply {
            duration = animDuration
            interpolator = timeInterpolator
            addUpdateListener(this@FaceScanningOverlay::updateRimProgress)
            addListener(object : AnimatorListenerAdapter() {
            addListener(
                object : AnimatorListenerAdapter() {
                    override fun onAnimationEnd(animation: Animator) {
                        rimProgress = HIDDEN_RIM_SCALE
                        invalidate()
                    }
            })
                }
            )
        }
    }

@@ -293,29 +310,25 @@ class FaceScanningOverlay(
            duration = PULSE_SUCCESS_DISAPPEAR_DURATION
            interpolator = Interpolators.LINEAR
            addUpdateListener(this@FaceScanningOverlay::updateRimAlpha)
            addListener(object : AnimatorListenerAdapter() {
            addListener(
                object : AnimatorListenerAdapter() {
                    override fun onAnimationEnd(animation: Animator) {
                        rimPaint.alpha = 255
                        invalidate()
                    }
            })
                }
            )
        }
    }

    private fun createFaceScanningRimAnimator(cameraProtectAnimator: ValueAnimator?): AnimatorSet {
        return AnimatorSet().apply {
            playSequentially(
                cameraProtectAnimator,
                createRimAppearAnimator(),
            )
            playSequentially(cameraProtectAnimator, createRimAppearAnimator())
        }
    }

    private fun createRimAppearAnimator(): ValueAnimator {
        return ValueAnimator.ofFloat(
            SHOW_CAMERA_PROTECTION_SCALE,
            PULSE_RADIUS_OUT
        ).apply {
        return ValueAnimator.ofFloat(SHOW_CAMERA_PROTECTION_SCALE, PULSE_RADIUS_OUT).apply {
            duration = PULSE_APPEAR_DURATION
            interpolator = Interpolators.STANDARD_DECELERATE
            addUpdateListener(this@FaceScanningOverlay::updateRimProgress)
@@ -361,12 +374,15 @@ class FaceScanningOverlay(
        private const val CAMERA_PROTECTION_ERROR_DISAPPEAR_DURATION = 300L // without start delay

        private fun scalePath(path: Path, scalingFactor: Float) {
            val scaleMatrix = Matrix().apply {
            val scaleMatrix =
                Matrix().apply {
                    val boundingRectangle = RectF()
                    path.computeBounds(boundingRectangle, true)
                    setScale(
                    scalingFactor, scalingFactor,
                    boundingRectangle.centerX(), boundingRectangle.centerY()
                        scalingFactor,
                        scalingFactor,
                        boundingRectangle.centerX(),
                        boundingRectangle.centerY(),
                    )
                }
            path.transform(scaleMatrix)
Loading