diff options
| author | 2024-01-02 18:38:20 +0000 | |
|---|---|---|
| committer | 2024-01-04 20:05:51 +0000 | |
| commit | 188b2bdd2623c46f17c316265cde4fdcf9afe59d (patch) | |
| tree | 7d8877d8a8d81541548704f24f9d8b399e0e32d0 | |
| parent | ae34ea145b10c8473045612da483c813312c38ac (diff) | |
Fix race condition between PromptIconViewBinder and BiometricSizeBinder
Sets BiometricPrompt view invisible until all resizing is complete. Adds
new MutableStateFlow updated on iconView onCompositionLoadedListener,
and combines this new flow with the BiometricSizeBinder viewModel.size
collect call, to force the resizing logic to wait for the iconAsset to
update first to account for it in the prompt size. Reworking of
ag/25666425 to avoid CTS failures the original CL caused
Flag: NONE
Fixes: 313605043
Bug: 317291041
Test: atest CtsBiometricsTestCases
Test: (manual) manually verified prompt appears only once and in the correct size, no resizing visible to the user
Change-Id: Ic3cbf1785737c19c5434030d45a73b2435328a71
4 files changed, 84 insertions, 17 deletions
diff --git a/packages/SystemUI/src/com/android/systemui/biometrics/ui/binder/BiometricViewBinder.kt b/packages/SystemUI/src/com/android/systemui/biometrics/ui/binder/BiometricViewBinder.kt index 90e4a3821634..7b8cb829e03f 100644 --- a/packages/SystemUI/src/com/android/systemui/biometrics/ui/binder/BiometricViewBinder.kt +++ b/packages/SystemUI/src/com/android/systemui/biometrics/ui/binder/BiometricViewBinder.kt @@ -77,6 +77,13 @@ object BiometricViewBinder { applicationScope: CoroutineScope, vibratorHelper: VibratorHelper, ): Spaghetti { + /** + * View is only set visible in BiometricViewSizeBinder once PromptSize is determined that + * accounts for iconView size, to prevent prompt resizing being visible to the user. + * + * TODO(b/288175072): May be able to remove this once constraint layout is implemented + */ + view.visibility = View.INVISIBLE val accessibilityManager = view.context.getSystemService(AccessibilityManager::class.java)!! val textColorError = @@ -102,7 +109,7 @@ object BiometricViewBinder { iconView, iconOverlayView, view.getUpdatedFingerprintAffordanceSize(), - viewModel.iconViewModel + viewModel ) val indicatorMessageView = view.requireViewById<TextView>(R.id.indicator) diff --git a/packages/SystemUI/src/com/android/systemui/biometrics/ui/binder/BiometricViewSizeBinder.kt b/packages/SystemUI/src/com/android/systemui/biometrics/ui/binder/BiometricViewSizeBinder.kt index 7e16d1e1d668..1a7b6c974d0c 100644 --- a/packages/SystemUI/src/com/android/systemui/biometrics/ui/binder/BiometricViewSizeBinder.kt +++ b/packages/SystemUI/src/com/android/systemui/biometrics/ui/binder/BiometricViewSizeBinder.kt @@ -30,7 +30,6 @@ import androidx.core.animation.addListener import androidx.core.view.doOnLayout import androidx.core.view.isGone import androidx.lifecycle.lifecycleScope -import com.android.systemui.res.R import com.android.systemui.biometrics.AuthPanelController import com.android.systemui.biometrics.Utils import com.android.systemui.biometrics.ui.BiometricPromptLayout @@ -41,6 +40,8 @@ import com.android.systemui.biometrics.ui.viewmodel.isMedium import com.android.systemui.biometrics.ui.viewmodel.isNullOrNotSmall import com.android.systemui.biometrics.ui.viewmodel.isSmall import com.android.systemui.lifecycle.repeatWhenAttached +import com.android.systemui.res.R +import kotlinx.coroutines.flow.combine import kotlinx.coroutines.launch /** Helper for [BiometricViewBinder] to handle resize transitions. */ @@ -93,7 +94,20 @@ object BiometricViewSizeBinder { view.repeatWhenAttached { var currentSize: PromptSize? = null lifecycleScope.launch { - viewModel.size.collect { size -> + /** + * View is only set visible in BiometricViewSizeBinder once PromptSize is + * determined that accounts for iconView size, to prevent prompt resizing being + * visible to the user. + * + * TODO(b/288175072): May be able to remove isIconViewLoaded once constraint + * layout is implemented + */ + combine(viewModel.isIconViewLoaded, viewModel.size, ::Pair).collect { + (isIconViewLoaded, size) -> + if (!isIconViewLoaded) { + return@collect + } + // prepare for animated size transitions for (v in viewsToHideWhenSmall) { v.showTextOrHide(forceHide = size.isSmall) @@ -198,6 +212,8 @@ object BiometricViewSizeBinder { } currentSize = size + view.visibility = View.VISIBLE + viewModel.setIsIconViewLoaded(false) notifyAccessibilityChanged() } } diff --git a/packages/SystemUI/src/com/android/systemui/biometrics/ui/binder/PromptIconViewBinder.kt b/packages/SystemUI/src/com/android/systemui/biometrics/ui/binder/PromptIconViewBinder.kt index 475ef18e5099..6e3bcf575072 100644 --- a/packages/SystemUI/src/com/android/systemui/biometrics/ui/binder/PromptIconViewBinder.kt +++ b/packages/SystemUI/src/com/android/systemui/biometrics/ui/binder/PromptIconViewBinder.kt @@ -26,6 +26,7 @@ import com.airbnb.lottie.LottieAnimationView import com.android.settingslib.widget.LottieColorUtils 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.util.kotlin.Utils.Companion.toQuad import com.android.systemui.util.kotlin.Utils.Companion.toQuint @@ -45,8 +46,9 @@ object PromptIconViewBinder { iconView: LottieAnimationView, iconOverlayView: LottieAnimationView, iconViewLayoutParamSizeOverride: Pair<Int, Int>?, - viewModel: PromptIconViewModel + promptViewModel: PromptViewModel ) { + val viewModel = promptViewModel.iconViewModel iconView.repeatWhenAttached { repeatOnLifecycle(Lifecycle.State.STARTED) { viewModel.onConfigurationChanged(iconView.context.resources.configuration) @@ -71,25 +73,45 @@ object PromptIconViewBinder { } launch { + var width: Int + var height: Int viewModel.activeAuthType.collect { activeAuthType -> - if (iconViewLayoutParamSizeOverride == null) { - val width: Int - val height: Int - when (activeAuthType) { - AuthType.Fingerprint, - AuthType.Coex -> { - width = viewModel.fingerprintIconWidth - height = viewModel.fingerprintIconHeight - } - AuthType.Face -> { - width = viewModel.faceIconWidth - height = viewModel.faceIconHeight + when (activeAuthType) { + AuthType.Fingerprint, + AuthType.Coex -> { + width = viewModel.fingerprintIconWidth + height = viewModel.fingerprintIconHeight + + /** + * View is only set visible in BiometricViewSizeBinder once + * PromptSize is determined that accounts for iconView size, to + * prevent prompt resizing being visible to the user. + * + * TODO(b/288175072): May be able to remove this once constraint + * layout is implemented + */ + iconView.removeAllLottieOnCompositionLoadedListener() + iconView.addLottieOnCompositionLoadedListener { + promptViewModel.setIsIconViewLoaded(true) } } + AuthType.Face -> { + width = viewModel.faceIconWidth + height = viewModel.faceIconHeight + /** + * Set to true by default since face icon is a drawable, which + * doesn't have a LottieOnCompositionLoadedListener equivalent. + * + * TODO(b/318569643): To be updated once face assets are updated + * from drawables + */ + promptViewModel.setIsIconViewLoaded(true) + } + } + if (iconViewLayoutParamSizeOverride == null) { iconView.layoutParams.width = width iconView.layoutParams.height = height - iconOverlayView.layoutParams.width = width iconOverlayView.layoutParams.height = height } diff --git a/packages/SystemUI/src/com/android/systemui/biometrics/ui/viewmodel/PromptViewModel.kt b/packages/SystemUI/src/com/android/systemui/biometrics/ui/viewmodel/PromptViewModel.kt index 6d0a58e202bd..d899827ebb2e 100644 --- a/packages/SystemUI/src/com/android/systemui/biometrics/ui/viewmodel/PromptViewModel.kt +++ b/packages/SystemUI/src/com/android/systemui/biometrics/ui/viewmodel/PromptViewModel.kt @@ -192,6 +192,28 @@ constructor( val iconViewModel: PromptIconViewModel = PromptIconViewModel(this, displayStateInteractor, promptSelectorInteractor) + private val _isIconViewLoaded = MutableStateFlow(false) + + /** + * For prompts with an iconView, false until the prompt's iconView animation has been loaded in + * the view, otherwise true by default. Used for BiometricViewSizeBinder to wait for the icon + * asset to be loaded before determining the prompt size. + */ + val isIconViewLoaded: Flow<Boolean> = + combine(credentialKind, _isIconViewLoaded.asStateFlow()) { credentialKind, isIconViewLoaded + -> + if (credentialKind is PromptKind.Biometric) { + isIconViewLoaded + } else { + true + } + } + + // Sets whether the prompt's iconView animation has been loaded in the view yet. + fun setIsIconViewLoaded(iconViewLoaded: Boolean) { + _isIconViewLoaded.value = iconViewLoaded + } + /** Padding for prompt UI elements */ val promptPadding: Flow<Rect> = combine(size, displayStateInteractor.currentRotation) { size, rotation -> |