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

Commit 5b854e20 authored by Grace Cheng's avatar Grace Cheng Committed by Android (Google) Code Review
Browse files

Merge "Update face authenticating asset" into 24D1-dev

parents 830adf4a 5ea97062
Loading
Loading
Loading
Loading
+1 −0
Original line number Diff line number Diff line
@@ -17,6 +17,7 @@

<resources>
    <!-- Dynamic colors-->
    <color name="settingslib_color_blue700">#0B57D0</color>
    <color name="settingslib_color_blue600">#1a73e8</color>
    <color name="settingslib_color_blue400">#669df6</color>
    <color name="settingslib_color_blue300">#8ab4f8</color>
+3 −0
Original line number Diff line number Diff line
@@ -55,6 +55,9 @@ public class LottieColorUtils {
        map.put(
                ".black",
                android.R.color.white);
        map.put(
                ".blue200",
                R.color.settingslib_color_blue700);
        map.put(
                ".blue400",
                R.color.settingslib_color_blue600);
+1 −0
Original line number Diff line number Diff line
{"v":"5.7.13","fr":60,"ip":0,"op":61,"w":64,"h":64,"nm":"face_scanning 3","ddd":0,"assets":[],"layers":[{"ddd":0,"ind":1,"ty":4,"nm":".blue200","sr":1,"ks":{"o":{"a":0,"k":100,"ix":11},"r":{"a":0,"k":0,"ix":10},"p":{"a":0,"k":[32,32,0],"ix":2,"l":2},"a":{"a":0,"k":[27.25,27.25,0],"ix":1,"l":2},"s":{"a":1,"k":[{"i":{"x":[0.667,0.667,0.667],"y":[1,1,1]},"o":{"x":[0.333,0.333,0.333],"y":[0,0,0]},"t":0,"s":[100,100,100]},{"i":{"x":[0.667,0.667,0.667],"y":[1,1,1]},"o":{"x":[0.333,0.333,0.333],"y":[0,0,0]},"t":30,"s":[95,95,100]},{"t":60,"s":[100,100,100]}],"ix":6,"l":2}},"ao":0,"shapes":[{"ty":"gr","it":[{"ind":0,"ty":"sh","ix":1,"ks":{"a":0,"k":{"i":[[0,-1.243],[-1.244,0],[0,1.243],[1.242,0]],"o":[[0,1.243],[1.242,0],[0,-1.243],[-1.244,0]],"v":[[-2.249,0.001],[0.001,2.251],[2.249,0.001],[0.001,-2.251]],"c":true},"ix":2},"nm":"Path 1","mn":"ADBE Vector Shape - Group","hd":false},{"ty":"fl","c":{"a":0,"k":[0.658823529412,0.780392216701,0.980392216701,1],"ix":4},"o":{"a":1,"k":[{"i":{"x":[0.833],"y":[0.833]},"o":{"x":[0.167],"y":[0.167]},"t":0,"s":[100]},{"i":{"x":[0.833],"y":[0.833]},"o":{"x":[0.167],"y":[0.167]},"t":30,"s":[60]},{"t":60,"s":[100]}],"ix":5},"r":1,"bm":0,"nm":"Fill 1","mn":"ADBE Vector Graphic - Fill","hd":false},{"ty":"tr","p":{"a":0,"k":[15.1,20.495],"ix":2},"a":{"a":0,"k":[0,0],"ix":1},"s":{"a":0,"k":[100,100],"ix":3},"r":{"a":0,"k":0,"ix":6},"o":{"a":0,"k":100,"ix":7},"sk":{"a":0,"k":0,"ix":4},"sa":{"a":0,"k":0,"ix":5},"nm":"Transform"}],"nm":"Group 1","np":2,"cix":2,"bm":0,"ix":1,"mn":"ADBE Vector Group","hd":false},{"ty":"gr","it":[{"ind":0,"ty":"sh","ix":1,"ks":{"a":0,"k":{"i":[[0,-1.243],[-1.242,0],[0,1.243],[1.242,0]],"o":[[0,1.243],[1.242,0],[0,-1.243],[-1.242,0]],"v":[[-2.249,0],[0.001,2.25],[2.249,0],[0.001,-2.25]],"c":true},"ix":2},"nm":"Path 1","mn":"ADBE Vector Shape - Group","hd":false},{"ty":"fl","c":{"a":0,"k":[0.658823529412,0.780392216701,0.980392216701,1],"ix":4},"o":{"a":1,"k":[{"i":{"x":[0.833],"y":[0.833]},"o":{"x":[0.167],"y":[0.167]},"t":0,"s":[100]},{"i":{"x":[0.833],"y":[0.833]},"o":{"x":[0.167],"y":[0.167]},"t":30,"s":[60]},{"t":60,"s":[100]}],"ix":5},"r":1,"bm":0,"nm":"Fill 1","mn":"ADBE Vector Graphic - Fill","hd":false},{"ty":"tr","p":{"a":0,"k":[39.4,20.495],"ix":2},"a":{"a":0,"k":[0,0],"ix":1},"s":{"a":0,"k":[100,100],"ix":3},"r":{"a":0,"k":0,"ix":6},"o":{"a":0,"k":100,"ix":7},"sk":{"a":0,"k":0,"ix":4},"sa":{"a":0,"k":0,"ix":5},"nm":"Transform"}],"nm":"Group 2","np":2,"cix":2,"bm":0,"ix":2,"mn":"ADBE Vector Group","hd":false},{"ty":"gr","it":[{"ind":0,"ty":"sh","ix":1,"ks":{"a":0,"k":{"i":[[0,0],[0,0],[0,0],[0,0],[0,0],[0,0]],"o":[[0,0],[0,0],[0,0],[0,0],[0,0],[0,0]],"v":[[2.814,3.523],[-2.814,3.523],[-2.814,1.363],[0.652,1.363],[0.652,-3.523],[2.814,-3.523]],"c":true},"ix":2},"nm":"Path 1","mn":"ADBE Vector Shape - Group","hd":false},{"ty":"fl","c":{"a":0,"k":[0.658823529412,0.780392216701,0.980392216701,1],"ix":4},"o":{"a":1,"k":[{"i":{"x":[0.833],"y":[0.833]},"o":{"x":[0.167],"y":[0.167]},"t":0,"s":[100]},{"i":{"x":[0.833],"y":[0.833]},"o":{"x":[0.167],"y":[0.167]},"t":30,"s":[60]},{"t":60,"s":[100]}],"ix":5},"r":1,"bm":0,"nm":"Fill 1","mn":"ADBE Vector Graphic - Fill","hd":false},{"ty":"tr","p":{"a":0,"k":[27.791,28.479],"ix":2},"a":{"a":0,"k":[0,0],"ix":1},"s":{"a":0,"k":[100,100],"ix":3},"r":{"a":0,"k":0,"ix":6},"o":{"a":0,"k":100,"ix":7},"sk":{"a":0,"k":0,"ix":4},"sa":{"a":0,"k":0,"ix":5},"nm":"Transform"}],"nm":"Group 3","np":2,"cix":2,"bm":0,"ix":3,"mn":"ADBE Vector Group","hd":false},{"ty":"gr","it":[{"ind":0,"ty":"sh","ix":1,"ks":{"a":0,"k":{"i":[[-0.154,0.15],[0,0],[0.117,-0.095],[0,0],[0.228,-0.121],[0.358,-0.103],[0.922,0.261],[0.3,0.16],[0.24,0.185],[0.14,0.139],[0.178,0.261],[0.143,0.451],[0,0],[0,0.494],[0,0],[-0.214,-0.676],[-0.392,-0.572],[-0.323,-0.317],[-0.228,-0.177],[-0.333,-0.179],[-0.503,-0.145],[-0.662,0],[-0.653,0.184],[-0.437,0.233],[-0.336,0.258],[0,0],[0,0]],"o":[[0,0],[-0.107,0.106],[0,0],[-0.24,0.185],[-0.301,0.16],[-0.92,0.261],[-0.357,-0.103],[-0.228,-0.121],[-0.158,-0.122],[-0.225,-0.221],[-0.272,-0.393],[0,0],[-0.147,-0.466],[0,0],[0,0.716],[0.206,0.656],[0.256,0.372],[0.204,0.201],[0.336,0.258],[0.436,0.233],[0.655,0.184],[0.662,0],[0.503,-0.145],[0.332,-0.179],[0,0],[0,0],[0.165,-0.136]],"v":[[6.094,1.465],[4.579,-0.076],[4.242,0.225],[4.124,0.315],[3.43,0.771],[2.439,1.165],[-0.342,1.165],[-1.331,0.771],[-2.027,0.315],[-2.48,-0.075],[-3.087,-0.801],[-3.712,-2.075],[-3.712,-2.075],[-3.934,-3.523],[-6.094,-3.523],[-5.771,-1.424],[-4.868,0.424],[-3.995,1.465],[-3.344,2.027],[-2.35,2.676],[-0.934,3.243],[1.049,3.523],[3.031,3.243],[4.449,2.676],[5.441,2.027],[5.482,1.997],[5.615,1.895]],"c":true},"ix":2},"nm":"Path 1","mn":"ADBE Vector Shape - Group","hd":false},{"ty":"fl","c":{"a":0,"k":[0.658823529412,0.780392216701,0.980392216701,1],"ix":4},"o":{"a":1,"k":[{"i":{"x":[0.833],"y":[0.833]},"o":{"x":[0.167],"y":[0.167]},"t":0,"s":[100]},{"i":{"x":[0.833],"y":[0.833]},"o":{"x":[0.167],"y":[0.167]},"t":30,"s":[60]},{"t":60,"s":[100]}],"ix":5},"r":1,"bm":0,"nm":"Fill 1","mn":"ADBE Vector Graphic - Fill","hd":false},{"ty":"tr","p":{"a":0,"k":[26.201,40.411],"ix":2},"a":{"a":0,"k":[0,0],"ix":1},"s":{"a":0,"k":[100,100],"ix":3},"r":{"a":0,"k":0,"ix":6},"o":{"a":0,"k":100,"ix":7},"sk":{"a":0,"k":0,"ix":4},"sa":{"a":0,"k":0,"ix":5},"nm":"Transform"}],"nm":"Group 4","np":2,"cix":2,"bm":0,"ix":4,"mn":"ADBE Vector Group","hd":false},{"ty":"gr","it":[{"ind":0,"ty":"sh","ix":1,"ks":{"a":0,"k":{"i":[[-13.398,0],[0,-13.4],[13.398,0],[0,13.4]],"o":[[13.398,0],[0,13.4],[-13.398,0],[0,-13.4]],"v":[[0,-24.3],[24.3,0],[0,24.3],[-24.3,0]],"c":true},"ix":2},"nm":"Path 1","mn":"ADBE Vector Shape - Group","hd":false},{"ind":1,"ty":"sh","ix":2,"ks":{"a":0,"k":{"i":[[14.904,0],[0,-14.904],[-14.904,0],[0,14.904]],"o":[[-14.904,0],[0,14.904],[14.904,0],[0,-14.904]],"v":[[0,-27],[-27,0],[0,27],[27,0]],"c":true},"ix":2},"nm":"Path 2","mn":"ADBE Vector Shape - Group","hd":false},{"ty":"mm","mm":1,"nm":"Merge Paths 1","mn":"ADBE Vector Filter - Merge","hd":false},{"ty":"fl","c":{"a":0,"k":[0.658823529412,0.780392216701,0.980392216701,1],"ix":4},"o":{"a":1,"k":[{"i":{"x":[0.833],"y":[0.833]},"o":{"x":[0.167],"y":[0.167]},"t":0,"s":[100]},{"i":{"x":[0.833],"y":[0.833]},"o":{"x":[0.167],"y":[0.167]},"t":30,"s":[60]},{"t":60,"s":[100]}],"ix":5},"r":1,"bm":0,"nm":"Fill 1","mn":"ADBE Vector Graphic - Fill","hd":false},{"ty":"tr","p":{"a":0,"k":[27.25,27.25],"ix":2},"a":{"a":0,"k":[0,0],"ix":1},"s":{"a":0,"k":[100,100],"ix":3},"r":{"a":0,"k":0,"ix":6},"o":{"a":0,"k":100,"ix":7},"sk":{"a":0,"k":0,"ix":4},"sa":{"a":0,"k":0,"ix":5},"nm":"Transform"}],"nm":"Group 5","np":4,"cix":2,"bm":0,"ix":5,"mn":"ADBE Vector Group","hd":false}],"ip":0,"op":1200,"st":0,"bm":0}],"markers":[]}
 No newline at end of file
+39 −53
Original line number Diff line number Diff line
@@ -18,9 +18,7 @@
package com.android.systemui.biometrics.ui.binder

import android.graphics.Rect
import android.graphics.drawable.Animatable2
import android.graphics.drawable.AnimatedVectorDrawable
import android.graphics.drawable.Drawable
import androidx.constraintlayout.widget.ConstraintLayout
import androidx.constraintlayout.widget.ConstraintSet
import androidx.lifecycle.Lifecycle
@@ -32,8 +30,8 @@ import com.android.systemui.biometrics.ui.viewmodel.PromptIconViewModel
import com.android.systemui.biometrics.ui.viewmodel.PromptIconViewModel.AuthType
import com.android.systemui.biometrics.ui.viewmodel.PromptViewModel
import com.android.systemui.lifecycle.repeatWhenAttached
import com.android.systemui.res.R
import com.android.systemui.util.kotlin.Utils.Companion.toQuad
import com.android.systemui.util.kotlin.Utils.Companion.toQuint
import com.android.systemui.util.kotlin.Utils.Companion.toTriple
import com.android.systemui.util.kotlin.sample
import kotlinx.coroutines.flow.combine
@@ -63,29 +61,11 @@ object PromptIconViewBinder {

                    iconOverlayView.layoutParams.width = iconViewLayoutParamSizeOverride.first
                    iconOverlayView.layoutParams.height = iconViewLayoutParamSizeOverride.second
                } else {
                    iconView.layoutParams.width = viewModel.fingerprintIconWidth.first()
                    iconView.layoutParams.height = viewModel.fingerprintIconWidth.first()

                    iconOverlayView.layoutParams.width = viewModel.fingerprintIconWidth.first()
                    iconOverlayView.layoutParams.height = viewModel.fingerprintIconWidth.first()
                }

                var faceIcon: AnimatedVectorDrawable? = null
                val faceIconCallback =
                    object : Animatable2.AnimationCallback() {
                        override fun onAnimationStart(drawable: Drawable) {
                            viewModel.onAnimationStart()
                        }

                        override fun onAnimationEnd(drawable: Drawable) {
                            viewModel.onAnimationEnd()
                        }
                    }

                launch {
                    var width = 0
                    var height = 0
                    combine(promptViewModel.size, viewModel.activeAuthType, ::Pair).collect {
                        (_, activeAuthType) ->
                        // Every time after bp shows, [isIconViewLoaded] is set to false in
@@ -95,6 +75,17 @@ object PromptIconViewBinder {
                        when (activeAuthType) {
                            AuthType.Fingerprint,
                            AuthType.Coex -> {
                                if (iconViewLayoutParamSizeOverride == null) {
                                    iconView.layoutParams.width =
                                        viewModel.fingerprintIconWidth.first()
                                    iconView.layoutParams.height =
                                        viewModel.fingerprintIconHeight.first()

                                    iconOverlayView.layoutParams.width =
                                        viewModel.fingerprintIconWidth.first()
                                    iconOverlayView.layoutParams.height =
                                        viewModel.fingerprintIconHeight.first()
                                }
                                /**
                                 * View is only set visible in BiometricViewSizeBinder once
                                 * PromptSize is determined that accounts for iconView size, to
@@ -109,8 +100,10 @@ object PromptIconViewBinder {
                                }
                            }
                            AuthType.Face -> {
                                width = viewModel.faceIconWidth
                                height = viewModel.faceIconHeight
                                if (iconViewLayoutParamSizeOverride == null) {
                                    iconView.layoutParams.width = viewModel.faceIconWidth
                                    iconView.layoutParams.height = viewModel.faceIconHeight
                                }
                                /**
                                 * Set to true by default since face icon is a drawable, which
                                 * doesn't have a LottieOnCompositionLoadedListener equivalent.
@@ -121,13 +114,6 @@ object PromptIconViewBinder {
                                promptViewModel.setIsIconViewLoaded(true)
                            }
                        }

                        if (width != 0 && height != 0) {
                            iconView.layoutParams.width = width
                            iconView.layoutParams.height = height
                            iconOverlayView.layoutParams.width = width
                            iconOverlayView.layoutParams.height = height
                        }
                    }
                }

@@ -155,19 +141,13 @@ object PromptIconViewBinder {
                            combine(
                                viewModel.activeAuthType,
                                viewModel.shouldAnimateIconView,
                                viewModel.shouldRepeatAnimation,
                                viewModel.showingError,
                                ::toQuad
                                ::Triple
                            ),
                            ::toQuint
                            ::toQuad
                        )
                        .collect {
                            (
                                iconAsset,
                                activeAuthType,
                                shouldAnimateIconView,
                                shouldRepeatAnimation,
                                showingError) ->
                        .collect { (iconAsset, activeAuthType, shouldAnimateIconView, showingError)
                            ->
                            if (iconAsset != -1) {
                                when (activeAuthType) {
                                    AuthType.Fingerprint,
@@ -180,10 +160,18 @@ object PromptIconViewBinder {
                                        }
                                    }
                                    AuthType.Face -> {
                                        faceIcon?.apply {
                                            unregisterAnimationCallback(faceIconCallback)
                                            stop()
                                        // TODO(b/318569643): Consolidate logic once all face auth
                                        // assets are migrated from drawable to json
                                        if (iconAsset == R.raw.face_dialog_authenticating) {
                                            iconView.setAnimation(iconAsset)
                                            iconView.frame = 0

                                            if (shouldAnimateIconView) {
                                                iconView.playAnimation()
                                                iconView.loop(true)
                                            }
                                        } else {
                                            faceIcon?.apply { stop() }
                                            faceIcon =
                                                iconView.context.getDrawable(iconAsset)
                                                    as AnimatedVectorDrawable
@@ -191,14 +179,12 @@ object PromptIconViewBinder {
                                                iconView.setImageDrawable(this)
                                                if (shouldAnimateIconView) {
                                                    forceAnimationOnUI()
                                                if (shouldRepeatAnimation) {
                                                    registerAnimationCallback(faceIconCallback)
                                                }
                                                    start()
                                                }
                                            }
                                        }
                                    }
                                }
                                LottieColorUtils.applyDynamicColors(iconView.context, iconView)
                                viewModel.setPreviousIconWasError(showingError)
                            }
+19 −77
Original line number Diff line number Diff line
@@ -31,12 +31,10 @@ import com.android.systemui.res.R
import com.android.systemui.util.kotlin.combine
import kotlinx.coroutines.flow.Flow
import kotlinx.coroutines.flow.MutableStateFlow
import kotlinx.coroutines.flow.asStateFlow
import kotlinx.coroutines.flow.combine
import kotlinx.coroutines.flow.distinctUntilChanged
import kotlinx.coroutines.flow.flatMapLatest
import kotlinx.coroutines.flow.flowOf
import kotlinx.coroutines.flow.map

/**
 * Models UI of [BiometricPromptLayout.iconView] and [BiometricPromptLayout.biometric_icon_overlay]
@@ -57,11 +55,8 @@ constructor(
    }

    /**
     * Indicates what auth type the UI currently displays.
     * Fingerprint-only auth -> Fingerprint
     * Face-only auth -> Face
     * Co-ex auth, implicit flow -> Face
     * Co-ex auth, explicit flow -> Coex
     * Indicates what auth type the UI currently displays. Fingerprint-only auth -> Fingerprint
     * Face-only auth -> Face Co-ex auth, implicit flow -> Face Co-ex auth, explicit flow -> Coex
     */
    val activeAuthType: Flow<AuthType> =
        combine(
@@ -129,35 +124,6 @@ constructor(
        _previousIconOverlayWasError.value = previousIconOverlayWasError
    }

    /** Called when iconView begins animating. */
    fun onAnimationStart() {
        _animationEnded.value = false
    }

    /** Called when iconView ends animating. */
    fun onAnimationEnd() {
        _animationEnded.value = true
    }

    private val _animationEnded: MutableStateFlow<Boolean> = MutableStateFlow(false)

    /**
     * Whether a face iconView should pulse (i.e. while isAuthenticating and previous animation
     * ended).
     */
    val shouldPulseAnimation: Flow<Boolean> =
        combine(_animationEnded, promptViewModel.isAuthenticating) {
                animationEnded,
                isAuthenticating ->
                animationEnded && isAuthenticating
            }
            .distinctUntilChanged()

    private val _lastPulseLightToDark: MutableStateFlow<Boolean> = MutableStateFlow(false)

    /** Tracks whether a face iconView last pulsed light to dark (vs. dark to light) */
    val lastPulseLightToDark: Flow<Boolean> = _lastPulseLightToDark.asStateFlow()

    /** Layout params for fingerprint iconView */
    val fingerprintIconWidth: Flow<Int> = promptViewModel.fingerprintSensorDiameter
    val fingerprintIconHeight: Flow<Int> = promptViewModel.fingerprintSensorDiameter
@@ -199,17 +165,6 @@ constructor(
                        }
                    }
                AuthType.Face ->
                    shouldPulseAnimation.flatMapLatest { shouldPulseAnimation: Boolean ->
                        if (shouldPulseAnimation) {
                            val iconAsset =
                                if (_lastPulseLightToDark.value) {
                                    R.drawable.face_dialog_pulse_dark_to_light
                                } else {
                                    R.drawable.face_dialog_pulse_light_to_dark
                                }
                            _lastPulseLightToDark.value = !_lastPulseLightToDark.value
                            flowOf(iconAsset)
                        } else {
                    combine(
                        promptViewModel.isAuthenticated.distinctUntilChanged(),
                        promptViewModel.isAuthenticating.distinctUntilChanged(),
@@ -227,8 +182,6 @@ constructor(
                            showingError
                        )
                    }
                        }
                    }
                AuthType.Coex ->
                    combine(
                        displayStateInteractor.currentRotation,
@@ -331,8 +284,7 @@ constructor(
        } else if (authState.isAuthenticated) {
            R.drawable.face_dialog_dark_to_checkmark
        } else if (isAuthenticating) {
            _lastPulseLightToDark.value = false
            R.drawable.face_dialog_pulse_dark_to_light
            R.raw.face_dialog_authenticating
        } else if (showingError) {
            R.drawable.face_dialog_dark_to_error
        } else if (_previousIconWasError.value) {
@@ -707,16 +659,6 @@ constructor(
            }
        }

    /** Whether the current BiometricPromptLayout.iconView asset animation should be repeated. */
    val shouldRepeatAnimation: Flow<Boolean> =
        activeAuthType.flatMapLatest { activeAuthType: AuthType ->
            when (activeAuthType) {
                AuthType.Fingerprint,
                AuthType.Coex -> flowOf(false)
                AuthType.Face -> promptViewModel.isAuthenticating.map { it }
            }
        }

    /** Called on configuration changes */
    fun onConfigurationChanged(newConfig: Configuration) {
        displayStateInteractor.onConfigurationChanged(newConfig)
Loading