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

Commit 2c1e7f8c authored by Beverly Tai's avatar Beverly Tai Committed by Automerger Merge Worker
Browse files

Merge "Add face scanning error animation" into tm-d1-dev am: efcb352f

parents ab56bf20 efcb352f
Loading
Loading
Loading
Loading
+148 −40
Original line number Original line Diff line number Diff line
@@ -27,12 +27,15 @@ import android.graphics.Matrix
import android.graphics.Paint
import android.graphics.Paint
import android.graphics.Path
import android.graphics.Path
import android.graphics.RectF
import android.graphics.RectF
import android.hardware.biometrics.BiometricSourceType
import android.view.View
import android.view.View
import androidx.core.graphics.ColorUtils
import androidx.core.graphics.ColorUtils
import com.android.keyguard.KeyguardUpdateMonitor
import com.android.keyguard.KeyguardUpdateMonitor
import com.android.keyguard.KeyguardUpdateMonitorCallback
import com.android.settingslib.Utils
import com.android.settingslib.Utils
import com.android.systemui.animation.Interpolators
import com.android.systemui.animation.Interpolators
import com.android.systemui.plugins.statusbar.StatusBarStateController
import com.android.systemui.plugins.statusbar.StatusBarStateController
import java.util.concurrent.Executor


/**
/**
 * When the face is enrolled, we use this view to show the face scanning animation and the camera
 * When the face is enrolled, we use this view to show the face scanning animation and the camera
@@ -42,7 +45,8 @@ class FaceScanningOverlay(
    context: Context,
    context: Context,
    pos: Int,
    pos: Int,
    val statusBarStateController: StatusBarStateController,
    val statusBarStateController: StatusBarStateController,
    val keyguardUpdateMonitor: KeyguardUpdateMonitor
    val keyguardUpdateMonitor: KeyguardUpdateMonitor,
    val mainExecutor: Executor
) : ScreenDecorations.DisplayCutoutView(context, pos) {
) : ScreenDecorations.DisplayCutoutView(context, pos) {
    private var showScanningAnim = false
    private var showScanningAnim = false
    private val rimPaint = Paint()
    private val rimPaint = Paint()
@@ -54,11 +58,26 @@ class FaceScanningOverlay(
            com.android.systemui.R.attr.wallpaperTextColorAccent)
            com.android.systemui.R.attr.wallpaperTextColorAccent)
    private var cameraProtectionAnimator: ValueAnimator? = null
    private var cameraProtectionAnimator: ValueAnimator? = null
    var hideOverlayRunnable: Runnable? = null
    var hideOverlayRunnable: Runnable? = null
    var faceAuthSucceeded = false


    init {
    init {
        visibility = View.INVISIBLE // only show this view when face scanning is happening
        visibility = View.INVISIBLE // only show this view when face scanning is happening
    }
    }


    override fun onAttachedToWindow() {
        super.onAttachedToWindow()
        mainExecutor.execute {
            keyguardUpdateMonitor.registerCallback(keyguardUpdateMonitorCallback)
        }
    }

    override fun onDetachedFromWindow() {
        super.onDetachedFromWindow()
        mainExecutor.execute {
            keyguardUpdateMonitor.removeCallback(keyguardUpdateMonitorCallback)
        }
    }

    override fun setColor(color: Int) {
    override fun setColor(color: Int) {
        cameraProtectionColor = color
        cameraProtectionColor = color
        invalidate()
        invalidate()
@@ -108,7 +127,6 @@ class FaceScanningOverlay(
        if (showScanningAnimNow == showScanningAnim) {
        if (showScanningAnimNow == showScanningAnim) {
            return
            return
        }
        }

        showScanningAnim = showScanningAnimNow
        showScanningAnim = showScanningAnimNow
        updateProtectionBoundingPath()
        updateProtectionBoundingPath()
        // Delay the relayout until the end of the animation when hiding,
        // Delay the relayout until the end of the animation when hiding,
@@ -120,13 +138,20 @@ class FaceScanningOverlay(


        cameraProtectionAnimator?.cancel()
        cameraProtectionAnimator?.cancel()
        cameraProtectionAnimator = ValueAnimator.ofFloat(cameraProtectionProgress,
        cameraProtectionAnimator = ValueAnimator.ofFloat(cameraProtectionProgress,
                if (showScanningAnimNow) 1.0f else HIDDEN_CAMERA_PROTECTION_SCALE).apply {
                if (showScanningAnimNow) SHOW_CAMERA_PROTECTION_SCALE
            startDelay = if (showScanningAnim) 0 else PULSE_DISAPPEAR_DURATION
                else HIDDEN_CAMERA_PROTECTION_SCALE).apply {
            duration = if (showScanningAnim) PULSE_APPEAR_DURATION else
            startDelay =
                CAMERA_PROTECTION_DISAPPEAR_DURATION
                    if (showScanningAnim) 0
            interpolator = if (showScanningAnim) Interpolators.STANDARD else
                    else if (faceAuthSucceeded) PULSE_SUCCESS_DISAPPEAR_DURATION
                Interpolators.EMPHASIZED
                    else PULSE_ERROR_DISAPPEAR_DURATION

            duration =
                    if (showScanningAnim) CAMERA_PROTECTION_APPEAR_DURATION
                    else if (faceAuthSucceeded) CAMERA_PROTECTION_SUCCESS_DISAPPEAR_DURATION
                    else CAMERA_PROTECTION_ERROR_DISAPPEAR_DURATION
            interpolator =
                    if (showScanningAnim) Interpolators.STANDARD_ACCELERATE
                    else if (faceAuthSucceeded) Interpolators.STANDARD
                    else Interpolators.STANDARD_DECELERATE
            addUpdateListener(ValueAnimator.AnimatorUpdateListener {
            addUpdateListener(ValueAnimator.AnimatorUpdateListener {
                animation: ValueAnimator ->
                animation: ValueAnimator ->
                cameraProtectionProgress = animation.animatedValue as Float
                cameraProtectionProgress = animation.animatedValue as Float
@@ -143,47 +168,73 @@ class FaceScanningOverlay(
                    }
                    }
                }
                }
            })
            })
            start()
        }
        }


        rimAnimator?.cancel()
        rimAnimator?.cancel()
        rimAnimator = AnimatorSet().apply {
        rimAnimator = AnimatorSet().apply {
            val rimAppearOrDisappearAnimator = ValueAnimator.ofFloat(rimProgress,
            if (showScanningAnim) {
                    if (showScanningAnim) PULSE_RADIUS_OUT else (PULSE_RADIUS_IN * 1.15f)).apply {
                val rimAppearAnimator = ValueAnimator.ofFloat(SHOW_CAMERA_PROTECTION_SCALE,
                duration = if (showScanningAnim) PULSE_APPEAR_DURATION else PULSE_DISAPPEAR_DURATION
                        PULSE_RADIUS_OUT).apply {
                interpolator = Interpolators.STANDARD
                    duration = PULSE_APPEAR_DURATION
                    interpolator = Interpolators.STANDARD_DECELERATE
                    addUpdateListener(ValueAnimator.AnimatorUpdateListener {
                    addUpdateListener(ValueAnimator.AnimatorUpdateListener {
                        animation: ValueAnimator ->
                        animation: ValueAnimator ->
                        rimProgress = animation.animatedValue as Float
                        rimProgress = animation.animatedValue as Float
                        invalidate()
                        invalidate()
                    })
                    })
                }
                }
            if (showScanningAnim) {

                // appear and then pulse in/out
                // animate in camera protection, rim, and then pulse in/out
                playSequentially(rimAppearOrDisappearAnimator,
                playSequentially(cameraProtectionAnimator, rimAppearAnimator,
                        createPulseAnimator(), createPulseAnimator(),
                        createPulseAnimator(), createPulseAnimator(),
                        createPulseAnimator(), createPulseAnimator(),
                        createPulseAnimator(), createPulseAnimator(),
                        createPulseAnimator(), createPulseAnimator())
                        createPulseAnimator(), createPulseAnimator())
            } else {
            } else {
                val opacityAnimator = ValueAnimator.ofInt(255, 0).apply {
                val rimDisappearAnimator = ValueAnimator.ofFloat(
                    duration = PULSE_DISAPPEAR_DURATION
                        rimProgress,
                        if (faceAuthSucceeded) PULSE_RADIUS_SUCCESS
                        else SHOW_CAMERA_PROTECTION_SCALE
                ).apply {
                    duration =
                            if (faceAuthSucceeded) PULSE_SUCCESS_DISAPPEAR_DURATION
                            else PULSE_ERROR_DISAPPEAR_DURATION
                    interpolator =
                            if (faceAuthSucceeded) Interpolators.STANDARD_DECELERATE
                            else Interpolators.STANDARD
                    addUpdateListener(ValueAnimator.AnimatorUpdateListener {
                        animation: ValueAnimator ->
                        rimProgress = animation.animatedValue as Float
                        invalidate()
                    })
                    addListener(object : AnimatorListenerAdapter() {
                        override fun onAnimationEnd(animation: Animator) {
                            rimProgress = HIDDEN_RIM_SCALE
                            invalidate()
                        }
                    })
                }
                if (faceAuthSucceeded) {
                    val successOpacityAnimator = ValueAnimator.ofInt(255, 0).apply {
                        duration = PULSE_SUCCESS_DISAPPEAR_DURATION
                        interpolator = Interpolators.LINEAR
                        interpolator = Interpolators.LINEAR
                        addUpdateListener(ValueAnimator.AnimatorUpdateListener {
                        addUpdateListener(ValueAnimator.AnimatorUpdateListener {
                            animation: ValueAnimator ->
                            animation: ValueAnimator ->
                            rimPaint.alpha = animation.animatedValue as Int
                            rimPaint.alpha = animation.animatedValue as Int
                            invalidate()
                            invalidate()
                        })
                        })
                }
                        addListener(object : AnimatorListenerAdapter() {
                        addListener(object : AnimatorListenerAdapter() {
                            override fun onAnimationEnd(animation: Animator) {
                            override fun onAnimationEnd(animation: Animator) {
                        rimProgress = HIDDEN_RIM_SCALE
                                rimPaint.alpha = 255
                                rimPaint.alpha = 255
                                invalidate()
                                invalidate()
                            }
                            }
                        })
                        })

                    }
                // disappear
                    val rimSuccessAnimator = AnimatorSet()
                playTogether(rimAppearOrDisappearAnimator, opacityAnimator)
                    rimSuccessAnimator.playTogether(rimDisappearAnimator, successOpacityAnimator)
                    playTogether(rimSuccessAnimator, cameraProtectionAnimator)
                } else {
                    playTogether(rimDisappearAnimator, cameraProtectionAnimator)
                }
            }
            }


            addListener(object : AnimatorListenerAdapter() {
            addListener(object : AnimatorListenerAdapter() {
@@ -253,15 +304,72 @@ class FaceScanningOverlay(
        }
        }
    }
    }


    private val keyguardUpdateMonitorCallback = object : KeyguardUpdateMonitorCallback() {
        override fun onBiometricAuthenticated(
            userId: Int,
            biometricSourceType: BiometricSourceType?,
            isStrongBiometric: Boolean
        ) {
            if (biometricSourceType == BiometricSourceType.FACE) {
                post {
                    faceAuthSucceeded = true
                    enableShowProtection(true)
                }
            }
        }

        override fun onBiometricAcquired(
            biometricSourceType: BiometricSourceType?,
            acquireInfo: Int
        ) {
            if (biometricSourceType == BiometricSourceType.FACE) {
                post {
                    faceAuthSucceeded = false // reset
                }
            }
        }

        override fun onBiometricAuthFailed(biometricSourceType: BiometricSourceType?) {
            if (biometricSourceType == BiometricSourceType.FACE) {
                post {
                    faceAuthSucceeded = false
                    enableShowProtection(false)
                }
            }
        }

        override fun onBiometricError(
            msgId: Int,
            errString: String?,
            biometricSourceType: BiometricSourceType?
        ) {
            if (biometricSourceType == BiometricSourceType.FACE) {
                post {
                    faceAuthSucceeded = false
                    enableShowProtection(false)
                }
            }
        }
    }

    companion object {
    companion object {
        private const val HIDDEN_RIM_SCALE = HIDDEN_CAMERA_PROTECTION_SCALE
        private const val HIDDEN_RIM_SCALE = HIDDEN_CAMERA_PROTECTION_SCALE
        private const val SHOW_CAMERA_PROTECTION_SCALE = 1f

        private const val PULSE_RADIUS_IN = 1.1f
        private const val PULSE_RADIUS_OUT = 1.125f
        private const val PULSE_RADIUS_SUCCESS = 1.25f

        private const val CAMERA_PROTECTION_APPEAR_DURATION = 250L
        private const val PULSE_APPEAR_DURATION = 250L // without start delay


        private const val PULSE_APPEAR_DURATION = 350L
        private const val PULSE_DURATION_INWARDS = 500L
        private const val PULSE_DURATION_INWARDS = 500L
        private const val PULSE_DURATION_OUTWARDS = 500L
        private const val PULSE_DURATION_OUTWARDS = 500L
        private const val PULSE_DISAPPEAR_DURATION = 850L

        private const val CAMERA_PROTECTION_DISAPPEAR_DURATION = 700L // excluding start delay
        private const val PULSE_SUCCESS_DISAPPEAR_DURATION = 400L
        private const val PULSE_RADIUS_IN = 1.15f
        private const val CAMERA_PROTECTION_SUCCESS_DISAPPEAR_DURATION = 500L // without start delay
        private const val PULSE_RADIUS_OUT = 1.25f

        private const val PULSE_ERROR_DISAPPEAR_DURATION = 200L
        private const val CAMERA_PROTECTION_ERROR_DISAPPEAR_DURATION = 300L // without start delay
    }
    }
}
}
+11 −3
Original line number Original line Diff line number Diff line
@@ -32,10 +32,12 @@ import android.widget.FrameLayout
import com.android.keyguard.KeyguardUpdateMonitor
import com.android.keyguard.KeyguardUpdateMonitor
import com.android.systemui.FaceScanningOverlay
import com.android.systemui.FaceScanningOverlay
import com.android.systemui.biometrics.AuthController
import com.android.systemui.biometrics.AuthController
import com.android.systemui.dagger.qualifiers.Main
import com.android.systemui.dagger.SysUISingleton
import com.android.systemui.dagger.SysUISingleton
import com.android.systemui.flags.FeatureFlags
import com.android.systemui.flags.FeatureFlags
import com.android.systemui.flags.Flags
import com.android.systemui.flags.Flags
import com.android.systemui.plugins.statusbar.StatusBarStateController
import com.android.systemui.plugins.statusbar.StatusBarStateController
import java.util.concurrent.Executor
import javax.inject.Inject
import javax.inject.Inject


@SysUISingleton
@SysUISingleton
@@ -44,6 +46,7 @@ class FaceScanningProviderFactory @Inject constructor(
    private val context: Context,
    private val context: Context,
    private val statusBarStateController: StatusBarStateController,
    private val statusBarStateController: StatusBarStateController,
    private val keyguardUpdateMonitor: KeyguardUpdateMonitor,
    private val keyguardUpdateMonitor: KeyguardUpdateMonitor,
    @Main private val mainExecutor: Executor,
    private val featureFlags: FeatureFlags
    private val featureFlags: FeatureFlags
) : DecorProviderFactory() {
) : DecorProviderFactory() {
    private val display = context.display
    private val display = context.display
@@ -82,7 +85,9 @@ class FaceScanningProviderFactory @Inject constructor(
                                        bound.baseOnRotation0(displayInfo.rotation),
                                        bound.baseOnRotation0(displayInfo.rotation),
                                        authController,
                                        authController,
                                        statusBarStateController,
                                        statusBarStateController,
                                        keyguardUpdateMonitor)
                                        keyguardUpdateMonitor,
                                        mainExecutor
                                )
                        )
                        )
                    }
                    }
                }
                }
@@ -102,7 +107,8 @@ class FaceScanningOverlayProviderImpl(
    override val alignedBound: Int,
    override val alignedBound: Int,
    private val authController: AuthController,
    private val authController: AuthController,
    private val statusBarStateController: StatusBarStateController,
    private val statusBarStateController: StatusBarStateController,
    private val keyguardUpdateMonitor: KeyguardUpdateMonitor
    private val keyguardUpdateMonitor: KeyguardUpdateMonitor,
    private val mainExecutor: Executor
) : BoundDecorProvider() {
) : BoundDecorProvider() {
    override val viewId: Int = com.android.systemui.R.id.face_scanning_anim
    override val viewId: Int = com.android.systemui.R.id.face_scanning_anim


@@ -127,7 +133,9 @@ class FaceScanningOverlayProviderImpl(
                context,
                context,
                alignedBound,
                alignedBound,
                statusBarStateController,
                statusBarStateController,
                keyguardUpdateMonitor)
                keyguardUpdateMonitor,
                mainExecutor
        )
        view.id = viewId
        view.id = viewId
        FrameLayout.LayoutParams(ViewGroup.LayoutParams.MATCH_PARENT,
        FrameLayout.LayoutParams(ViewGroup.LayoutParams.MATCH_PARENT,
                ViewGroup.LayoutParams.MATCH_PARENT).let {
                ViewGroup.LayoutParams.MATCH_PARENT).let {
+2 −1
Original line number Original line Diff line number Diff line
@@ -210,7 +210,8 @@ public class ScreenDecorationsTest extends SysuiTestCase {
                BOUNDS_POSITION_TOP,
                BOUNDS_POSITION_TOP,
                mAuthController,
                mAuthController,
                mStatusBarStateController,
                mStatusBarStateController,
                mKeyguardUpdateMonitor));
                mKeyguardUpdateMonitor,
                mExecutor));


        mScreenDecorations = spy(new ScreenDecorations(mContext, mExecutor, mSecureSettings,
        mScreenDecorations = spy(new ScreenDecorations(mContext, mExecutor, mSecureSettings,
                mBroadcastDispatcher, mTunerService, mUserTracker, mDotViewController,
                mBroadcastDispatcher, mTunerService, mUserTracker, mDotViewController,