Loading packages/SettingsLib/Color/res/values/colors.xml +1 −0 Original line number Diff line number Diff line Loading @@ -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> Loading packages/SettingsLib/IllustrationPreference/src/com/android/settingslib/widget/LottieColorUtils.java +3 −0 Original line number Diff line number Diff line Loading @@ -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); Loading packages/SystemUI/res/raw/face_dialog_authenticating.json 0 → 100644 +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 packages/SystemUI/src/com/android/systemui/biometrics/ui/binder/PromptIconViewBinder.kt +39 −53 Original line number Diff line number Diff line Loading @@ -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 Loading @@ -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 Loading Loading @@ -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 Loading @@ -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 Loading @@ -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. Loading @@ -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 } } } Loading Loading @@ -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, Loading @@ -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 Loading @@ -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) } Loading packages/SystemUI/src/com/android/systemui/biometrics/ui/viewmodel/PromptIconViewModel.kt +19 −77 Original line number Diff line number Diff line Loading @@ -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] Loading @@ -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( Loading Loading @@ -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 Loading Loading @@ -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(), Loading @@ -227,8 +182,6 @@ constructor( showingError ) } } } AuthType.Coex -> combine( displayStateInteractor.currentRotation, Loading Loading @@ -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) { Loading Loading @@ -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 Loading
packages/SettingsLib/Color/res/values/colors.xml +1 −0 Original line number Diff line number Diff line Loading @@ -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> Loading
packages/SettingsLib/IllustrationPreference/src/com/android/settingslib/widget/LottieColorUtils.java +3 −0 Original line number Diff line number Diff line Loading @@ -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); Loading
packages/SystemUI/res/raw/face_dialog_authenticating.json 0 → 100644 +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
packages/SystemUI/src/com/android/systemui/biometrics/ui/binder/PromptIconViewBinder.kt +39 −53 Original line number Diff line number Diff line Loading @@ -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 Loading @@ -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 Loading Loading @@ -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 Loading @@ -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 Loading @@ -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. Loading @@ -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 } } } Loading Loading @@ -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, Loading @@ -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 Loading @@ -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) } Loading
packages/SystemUI/src/com/android/systemui/biometrics/ui/viewmodel/PromptIconViewModel.kt +19 −77 Original line number Diff line number Diff line Loading @@ -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] Loading @@ -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( Loading Loading @@ -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 Loading Loading @@ -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(), Loading @@ -227,8 +182,6 @@ constructor( showingError ) } } } AuthType.Coex -> combine( displayStateInteractor.currentRotation, Loading Loading @@ -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) { Loading Loading @@ -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