• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 /*
<lambda>null2  * Copyright (C) 2023 The Android Open Source Project
3  *
4  * Licensed under the Apache License, Version 2.0 (the "License");
5  * you may not use this file except in compliance with the License.
6  * You may obtain a copy of the License at
7  *
8  *      http://www.apache.org/licenses/LICENSE-2.0
9  *
10  * Unless required by applicable law or agreed to in writing, software
11  * distributed under the License is distributed on an "AS IS" BASIS,
12  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13  * See the License for the specific language governing permissions and
14  * limitations under the License.
15  *
16  */
17 
18 package com.android.systemui.biometrics.ui.viewmodel
19 
20 import android.annotation.RawRes
21 import android.content.res.Configuration
22 import com.android.systemui.biometrics.domain.interactor.DisplayStateInteractor
23 import com.android.systemui.biometrics.domain.interactor.PromptSelectorInteractor
24 import com.android.systemui.biometrics.shared.model.DisplayRotation
25 import com.android.systemui.biometrics.shared.model.FingerprintSensorType
26 import com.android.systemui.res.R
27 import com.android.systemui.util.kotlin.combine
28 import kotlinx.coroutines.flow.Flow
29 import kotlinx.coroutines.flow.MutableStateFlow
30 import kotlinx.coroutines.flow.combine
31 import kotlinx.coroutines.flow.distinctUntilChanged
32 import kotlinx.coroutines.flow.flatMapLatest
33 import kotlinx.coroutines.flow.flowOf
34 
35 /** Models UI of [BiometricPromptLayout.iconView] */
36 class PromptIconViewModel(
37     promptViewModel: PromptViewModel,
38     private val displayStateInteractor: DisplayStateInteractor,
39     promptSelectorInteractor: PromptSelectorInteractor,
40 ) {
41 
42     /** Auth types for the UI to display. */
43     enum class AuthType {
44         Fingerprint,
45         Face,
46         Coex,
47     }
48 
49     /**
50      * Indicates what auth type the UI currently displays. Fingerprint-only auth -> Fingerprint
51      * Face-only auth -> Face Co-ex auth, implicit flow -> Face Co-ex auth, explicit flow -> Coex
52      */
53     val activeAuthType: Flow<AuthType> =
54         combine(
55             promptViewModel.modalities.distinctUntilChanged(),
56             promptViewModel.faceMode.distinctUntilChanged(),
57         ) { modalities, faceMode ->
58             if (modalities.hasFaceAndFingerprint && !faceMode) {
59                 AuthType.Coex
60             } else if (modalities.hasFaceOnly || faceMode) {
61                 AuthType.Face
62             } else if (modalities.hasFingerprintOnly) {
63                 AuthType.Fingerprint
64             } else {
65                 // TODO(b/288175072): Remove, currently needed for transition to credential view
66                 AuthType.Fingerprint
67             }
68         }
69 
70     /** Whether an error message is currently being shown. */
71     val showingError = promptViewModel.showingError
72 
73     /** Whether the previous icon shown displayed an error. */
74     private val _previousIconWasError: MutableStateFlow<Boolean> = MutableStateFlow(false)
75 
76     fun setPreviousIconWasError(previousIconWasError: Boolean) {
77         _previousIconWasError.value = previousIconWasError
78     }
79 
80     val iconSize: Flow<Pair<Int, Int>> =
81         combine(
82             promptViewModel.position,
83             activeAuthType,
84             promptViewModel.legacyFingerprintSensorWidth,
85             promptViewModel.legacyFingerprintSensorHeight,
86         ) { _, activeAuthType, fingerprintSensorWidth, fingerprintSensorHeight ->
87             if (activeAuthType == AuthType.Face) {
88                 Pair(promptViewModel.faceIconWidth, promptViewModel.faceIconHeight)
89             } else {
90                 Pair(fingerprintSensorWidth, fingerprintSensorHeight)
91             }
92         }
93 
94     /** Current BiometricPromptLayout.iconView asset. */
95     val iconAsset: Flow<Int> =
96         activeAuthType.flatMapLatest { activeAuthType: AuthType ->
97             when (activeAuthType) {
98                 AuthType.Fingerprint ->
99                     combine(
100                         displayStateInteractor.currentRotation,
101                         displayStateInteractor.isInRearDisplayMode,
102                         promptSelectorInteractor.fingerprintSensorType,
103                         promptViewModel.isAuthenticated,
104                         promptViewModel.isAuthenticating,
105                         promptViewModel.showingError,
106                     ) {
107                         rotation: DisplayRotation,
108                         isInRearDisplayMode: Boolean,
109                         sensorType: FingerprintSensorType,
110                         authState: PromptAuthState,
111                         isAuthenticating: Boolean,
112                         showingError: Boolean ->
113                         when (sensorType) {
114                             FingerprintSensorType.POWER_BUTTON ->
115                                 getSfpsIconViewAsset(
116                                     rotation,
117                                     isInRearDisplayMode,
118                                     authState.isAuthenticated,
119                                     isAuthenticating,
120                                     showingError,
121                                 )
122                             else ->
123                                 getFingerprintIconViewAsset(
124                                     authState.isAuthenticated,
125                                     isAuthenticating,
126                                     showingError,
127                                 )
128                         }
129                     }
130                 AuthType.Face ->
131                     combine(
132                         promptViewModel.isAuthenticated.distinctUntilChanged(),
133                         promptViewModel.isAuthenticating.distinctUntilChanged(),
134                         promptViewModel.isPendingConfirmation.distinctUntilChanged(),
135                         promptViewModel.showingError.distinctUntilChanged(),
136                     ) {
137                         authState: PromptAuthState,
138                         isAuthenticating: Boolean,
139                         isPendingConfirmation: Boolean,
140                         showingError: Boolean ->
141                         getFaceIconViewAsset(
142                             authState,
143                             isAuthenticating,
144                             isPendingConfirmation,
145                             showingError,
146                         )
147                     }
148                 AuthType.Coex ->
149                     combine(
150                         displayStateInteractor.currentRotation,
151                         displayStateInteractor.isInRearDisplayMode,
152                         promptSelectorInteractor.fingerprintSensorType,
153                         promptViewModel.isAuthenticated,
154                         promptViewModel.isAuthenticating,
155                         promptViewModel.isPendingConfirmation,
156                         promptViewModel.showingError,
157                     ) {
158                         rotation: DisplayRotation,
159                         isInRearDisplayMode: Boolean,
160                         sensorType: FingerprintSensorType,
161                         authState: PromptAuthState,
162                         isAuthenticating: Boolean,
163                         isPendingConfirmation: Boolean,
164                         showingError: Boolean ->
165                         when (sensorType) {
166                             FingerprintSensorType.POWER_BUTTON ->
167                                 getCoexSfpsIconViewAsset(
168                                     rotation,
169                                     isInRearDisplayMode,
170                                     authState,
171                                     isAuthenticating,
172                                     isPendingConfirmation,
173                                     showingError,
174                                 )
175                             else ->
176                                 getCoexIconViewAsset(
177                                     authState,
178                                     isAuthenticating,
179                                     isPendingConfirmation,
180                                     showingError,
181                                 )
182                         }
183                     }
184             }
185         }
186 
187     private fun getFingerprintIconViewAsset(
188         isAuthenticated: Boolean,
189         isAuthenticating: Boolean,
190         showingError: Boolean,
191     ): Int {
192         return if (isAuthenticated) {
193             if (_previousIconWasError.value) {
194                 R.raw.fingerprint_dialogue_error_to_success_lottie
195             } else {
196                 R.raw.fingerprint_dialogue_fingerprint_to_success_lottie
197             }
198         } else if (isAuthenticating) {
199             if (_previousIconWasError.value) {
200                 R.raw.fingerprint_dialogue_error_to_fingerprint_lottie
201             } else {
202                 R.raw.fingerprint_dialogue_fingerprint_to_error_lottie
203             }
204         } else if (showingError) {
205             R.raw.fingerprint_dialogue_fingerprint_to_error_lottie
206         } else {
207             -1
208         }
209     }
210 
211     @RawRes
212     private fun getSfpsIconViewAsset(
213         rotation: DisplayRotation,
214         isInRearDisplayMode: Boolean,
215         isAuthenticated: Boolean,
216         isAuthenticating: Boolean,
217         showingError: Boolean,
218     ): Int {
219         return if (isAuthenticated) {
220             if (_previousIconWasError.value) {
221                 R.raw.biometricprompt_sfps_error_to_success
222             } else {
223                 getSfpsAsset_fingerprintToSuccess(rotation, isInRearDisplayMode)
224             }
225         } else if (isAuthenticating) {
226             if (_previousIconWasError.value) {
227                 getSfpsAsset_errorToFingerprint(rotation, isInRearDisplayMode)
228             } else {
229                 getSfpsAsset_fingerprintAuthenticating(isInRearDisplayMode)
230             }
231         } else if (showingError) {
232             getSfpsAsset_fingerprintToError(rotation, isInRearDisplayMode)
233         } else {
234             -1
235         }
236     }
237 
238     @RawRes
239     private fun getFaceIconViewAsset(
240         authState: PromptAuthState,
241         isAuthenticating: Boolean,
242         isPendingConfirmation: Boolean,
243         showingError: Boolean,
244     ): Int {
245         return if (authState.isAuthenticated && isPendingConfirmation) {
246             R.raw.face_dialog_wink_from_dark
247         } else if (authState.isAuthenticated) {
248             R.raw.face_dialog_dark_to_checkmark
249         } else if (isAuthenticating) {
250             R.raw.face_dialog_authenticating
251         } else if (showingError) {
252             R.raw.face_dialog_dark_to_error
253         } else if (_previousIconWasError.value) {
254             R.raw.face_dialog_error_to_idle
255         } else {
256             R.raw.face_dialog_idle_static
257         }
258     }
259 
260     @RawRes
261     private fun getCoexIconViewAsset(
262         authState: PromptAuthState,
263         isAuthenticating: Boolean,
264         isPendingConfirmation: Boolean,
265         showingError: Boolean,
266     ): Int {
267         return if (authState.isAuthenticatedAndExplicitlyConfirmed) {
268             R.raw.fingerprint_dialogue_unlocked_to_checkmark_success_lottie
269         } else if (isPendingConfirmation) {
270             if (_previousIconWasError.value) {
271                 R.raw.fingerprint_dialogue_error_to_unlock_lottie
272             } else {
273                 R.raw.fingerprint_dialogue_fingerprint_to_unlock_lottie
274             }
275         } else if (authState.isAuthenticated) {
276             if (_previousIconWasError.value) {
277                 R.raw.fingerprint_dialogue_error_to_success_lottie
278             } else {
279                 R.raw.fingerprint_dialogue_fingerprint_to_success_lottie
280             }
281         } else if (isAuthenticating) {
282             if (_previousIconWasError.value) {
283                 R.raw.fingerprint_dialogue_error_to_fingerprint_lottie
284             } else {
285                 R.raw.fingerprint_dialogue_fingerprint_to_error_lottie
286             }
287         } else if (showingError) {
288             R.raw.fingerprint_dialogue_fingerprint_to_error_lottie
289         } else {
290             -1
291         }
292     }
293 
294     @RawRes
295     private fun getCoexSfpsIconViewAsset(
296         rotation: DisplayRotation,
297         isInRearDisplayMode: Boolean,
298         authState: PromptAuthState,
299         isAuthenticating: Boolean,
300         isPendingConfirmation: Boolean,
301         showingError: Boolean,
302     ): Int {
303         return if (authState.isAuthenticatedAndExplicitlyConfirmed) {
304             R.raw.biometricprompt_sfps_unlock_to_success
305         } else if (isPendingConfirmation) {
306             if (_previousIconWasError.value) {
307                 R.raw.biometricprompt_sfps_error_to_unlock
308             } else {
309                 getSfpsAsset_fingerprintToUnlock(rotation, isInRearDisplayMode)
310             }
311         } else if (authState.isAuthenticated) {
312             if (_previousIconWasError.value) {
313                 R.raw.biometricprompt_sfps_error_to_success
314             } else {
315                 getSfpsAsset_fingerprintToSuccess(rotation, isInRearDisplayMode)
316             }
317         } else if (isAuthenticating) {
318             if (_previousIconWasError.value) {
319                 getSfpsAsset_errorToFingerprint(rotation, isInRearDisplayMode)
320             } else {
321                 getSfpsAsset_fingerprintAuthenticating(isInRearDisplayMode)
322             }
323         } else if (showingError) {
324             getSfpsAsset_fingerprintToError(rotation, isInRearDisplayMode)
325         } else {
326             -1
327         }
328     }
329 
330     /** Content description for iconView */
331     val contentDescriptionId: Flow<Int> =
332         activeAuthType.flatMapLatest { activeAuthType: AuthType ->
333             when (activeAuthType) {
334                 AuthType.Fingerprint,
335                 AuthType.Coex ->
336                     combine(
337                         promptSelectorInteractor.fingerprintSensorType,
338                         promptViewModel.isAuthenticated,
339                         promptViewModel.isAuthenticating,
340                         promptViewModel.isPendingConfirmation,
341                         promptViewModel.showingError,
342                     ) {
343                         sensorType: FingerprintSensorType,
344                         authState: PromptAuthState,
345                         isAuthenticating: Boolean,
346                         isPendingConfirmation: Boolean,
347                         showingError: Boolean ->
348                         getFingerprintIconContentDescriptionId(
349                             sensorType,
350                             authState.isAuthenticated,
351                             isAuthenticating,
352                             isPendingConfirmation,
353                             showingError,
354                         )
355                     }
356                 AuthType.Face ->
357                     combine(
358                         promptViewModel.isAuthenticated,
359                         promptViewModel.isAuthenticating,
360                         promptViewModel.showingError,
361                     ) { authState: PromptAuthState, isAuthenticating: Boolean, showingError: Boolean
362                         ->
363                         getFaceIconContentDescriptionId(authState, isAuthenticating, showingError)
364                     }
365             }
366         }
367 
368     private fun getFingerprintIconContentDescriptionId(
369         sensorType: FingerprintSensorType,
370         isAuthenticated: Boolean,
371         isAuthenticating: Boolean,
372         isPendingConfirmation: Boolean,
373         showingError: Boolean,
374     ): Int =
375         if (isPendingConfirmation) {
376             when (sensorType) {
377                 FingerprintSensorType.POWER_BUTTON -> -1
378                 else -> R.string.biometric_dialog_confirm
379             }
380         } else if (isAuthenticating || isAuthenticated) {
381             when (sensorType) {
382                 FingerprintSensorType.POWER_BUTTON ->
383                     R.string.security_settings_sfps_enroll_find_sensor_message
384                 else -> R.string.accessibility_fingerprint_label
385             }
386         } else if (showingError) {
387             R.string.biometric_dialog_try_again
388         } else {
389             -1
390         }
391 
392     private fun getFaceIconContentDescriptionId(
393         authState: PromptAuthState,
394         isAuthenticating: Boolean,
395         showingError: Boolean,
396     ): Int =
397         if (authState.isAuthenticatedAndExplicitlyConfirmed) {
398             R.string.biometric_dialog_face_icon_description_confirmed
399         } else if (authState.isAuthenticated) {
400             R.string.biometric_dialog_face_icon_description_authenticated
401         } else if (isAuthenticating) {
402             R.string.biometric_dialog_face_icon_description_authenticating
403         } else if (showingError) {
404             R.string.keyguard_face_failed
405         } else {
406             R.string.biometric_dialog_face_icon_description_idle
407         }
408 
409     /** Whether the current BiometricPromptLayout.iconView asset animation should be playing. */
410     val shouldAnimateIconView: Flow<Boolean> =
411         activeAuthType.flatMapLatest { activeAuthType: AuthType ->
412             when (activeAuthType) {
413                 AuthType.Fingerprint ->
414                     combine(
415                         promptSelectorInteractor.fingerprintSensorType,
416                         promptViewModel.isAuthenticated,
417                         promptViewModel.isAuthenticating,
418                         promptViewModel.showingError,
419                     ) {
420                         sensorType: FingerprintSensorType,
421                         authState: PromptAuthState,
422                         isAuthenticating: Boolean,
423                         showingError: Boolean ->
424                         when (sensorType) {
425                             FingerprintSensorType.POWER_BUTTON -> true
426                             else ->
427                                 shouldAnimateFingerprintIconView(
428                                     authState.isAuthenticated,
429                                     isAuthenticating,
430                                     showingError,
431                                 )
432                         }
433                     }
434                 AuthType.Face ->
435                     combine(
436                         promptViewModel.isAuthenticated,
437                         promptViewModel.isAuthenticating,
438                         promptViewModel.showingError,
439                     ) { authState: PromptAuthState, isAuthenticating: Boolean, showingError: Boolean
440                         ->
441                         isAuthenticating ||
442                             authState.isAuthenticated ||
443                             showingError ||
444                             _previousIconWasError.value
445                     }
446                 AuthType.Coex ->
447                     combine(
448                         promptSelectorInteractor.fingerprintSensorType,
449                         promptViewModel.isAuthenticated,
450                         promptViewModel.isAuthenticating,
451                         promptViewModel.isPendingConfirmation,
452                         promptViewModel.showingError,
453                     ) {
454                         sensorType: FingerprintSensorType,
455                         authState: PromptAuthState,
456                         isAuthenticating: Boolean,
457                         isPendingConfirmation: Boolean,
458                         showingError: Boolean ->
459                         when (sensorType) {
460                             FingerprintSensorType.POWER_BUTTON -> true
461                             else ->
462                                 shouldAnimateCoexIconView(
463                                     authState.isAuthenticated,
464                                     isAuthenticating,
465                                     isPendingConfirmation,
466                                     showingError,
467                                 )
468                         }
469                     }
470             }
471         }
472 
473     /** Whether the current BiometricPromptLayout.iconView asset animation should be looping. */
474     val shouldLoopIconView: Flow<Boolean> =
475         activeAuthType.flatMapLatest { activeAuthType: AuthType ->
476             when (activeAuthType) {
477                 AuthType.Fingerprint,
478                 AuthType.Coex -> flowOf(false)
479                 AuthType.Face -> promptViewModel.isAuthenticating
480             }
481         }
482 
483     private fun shouldAnimateFingerprintIconView(
484         isAuthenticated: Boolean,
485         isAuthenticating: Boolean,
486         showingError: Boolean,
487     ) = (isAuthenticating && _previousIconWasError.value) || isAuthenticated || showingError
488 
489     private fun shouldAnimateCoexIconView(
490         isAuthenticated: Boolean,
491         isAuthenticating: Boolean,
492         isPendingConfirmation: Boolean,
493         showingError: Boolean,
494     ) =
495         (isAuthenticating && _previousIconWasError.value) ||
496             isPendingConfirmation ||
497             isAuthenticated ||
498             showingError
499 
500     /* Used to rotate the iconView for assets reused across rotations. */
501     val iconViewRotation: Flow<Float> =
502         combine(iconAsset, displayStateInteractor.currentRotation) {
503             icon: Int,
504             rotation: DisplayRotation ->
505             if (assetReusedAcrossRotations(icon)) {
506                 when (rotation) {
507                     DisplayRotation.ROTATION_0 -> 0f
508                     DisplayRotation.ROTATION_90 -> 270f
509                     DisplayRotation.ROTATION_180 -> 180f
510                     DisplayRotation.ROTATION_270 -> 90f
511                 }
512             } else {
513                 0f
514             }
515         }
516 
517     private fun assetReusedAcrossRotations(asset: Int): Boolean {
518         return asset in assetsReusedAcrossRotations
519     }
520 
521     private val assetsReusedAcrossRotations: List<Int> =
522         listOf(
523             R.raw.biometricprompt_sfps_fingerprint_authenticating,
524             R.raw.biometricprompt_sfps_rear_display_fingerprint_authenticating,
525             R.raw.biometricprompt_sfps_rear_display_fingerprint_authenticating,
526         )
527 
528     /** Called on configuration changes */
529     fun onConfigurationChanged(newConfig: Configuration) {
530         displayStateInteractor.onConfigurationChanged(newConfig)
531     }
532 
533     /** Coex iconView assets for caching */
534     fun getCoexAssetsList(hasSfps: Boolean): List<Int> =
535         if (hasSfps) {
536             listOf(
537                 R.raw.biometricprompt_sfps_fingerprint_authenticating,
538                 R.raw.biometricprompt_sfps_rear_display_fingerprint_authenticating,
539                 R.raw.biometricprompt_sfps_error_to_unlock,
540                 R.raw.biometricprompt_sfps_error_to_success,
541                 R.raw.biometricprompt_sfps_fingerprint_to_error,
542                 R.raw.biometricprompt_sfps_fingerprint_to_error_90,
543                 R.raw.biometricprompt_sfps_fingerprint_to_error_180,
544                 R.raw.biometricprompt_sfps_fingerprint_to_error_270,
545                 R.raw.biometricprompt_sfps_rear_display_fingerprint_to_error,
546                 R.raw.biometricprompt_sfps_rear_display_fingerprint_to_error_90,
547                 R.raw.biometricprompt_sfps_rear_display_fingerprint_to_error_180,
548                 R.raw.biometricprompt_sfps_rear_display_fingerprint_to_error_270,
549                 R.raw.biometricprompt_sfps_error_to_fingerprint,
550                 R.raw.biometricprompt_sfps_error_to_fingerprint_90,
551                 R.raw.biometricprompt_sfps_error_to_fingerprint_180,
552                 R.raw.biometricprompt_sfps_error_to_fingerprint_270,
553                 R.raw.biometricprompt_sfps_rear_display_error_to_fingerprint,
554                 R.raw.biometricprompt_sfps_rear_display_error_to_fingerprint_90,
555                 R.raw.biometricprompt_sfps_rear_display_error_to_fingerprint_180,
556                 R.raw.biometricprompt_sfps_rear_display_error_to_fingerprint_270,
557                 R.raw.biometricprompt_sfps_fingerprint_to_unlock,
558                 R.raw.biometricprompt_sfps_fingerprint_to_unlock_90,
559                 R.raw.biometricprompt_sfps_fingerprint_to_unlock_180,
560                 R.raw.biometricprompt_sfps_fingerprint_to_unlock_270,
561                 R.raw.biometricprompt_sfps_rear_display_fingerprint_to_unlock,
562                 R.raw.biometricprompt_sfps_rear_display_fingerprint_to_unlock_90,
563                 R.raw.biometricprompt_sfps_rear_display_fingerprint_to_unlock_180,
564                 R.raw.biometricprompt_sfps_rear_display_fingerprint_to_unlock_270,
565                 R.raw.biometricprompt_sfps_fingerprint_to_success,
566                 R.raw.biometricprompt_sfps_fingerprint_to_success_90,
567                 R.raw.biometricprompt_sfps_fingerprint_to_success_180,
568                 R.raw.biometricprompt_sfps_fingerprint_to_success_270,
569                 R.raw.biometricprompt_sfps_rear_display_fingerprint_to_success,
570                 R.raw.biometricprompt_sfps_rear_display_fingerprint_to_success_90,
571                 R.raw.biometricprompt_sfps_rear_display_fingerprint_to_success_180,
572                 R.raw.biometricprompt_sfps_rear_display_fingerprint_to_success_270,
573             )
574         } else {
575             listOf(
576                 R.raw.fingerprint_dialogue_unlocked_to_checkmark_success_lottie,
577                 R.raw.fingerprint_dialogue_error_to_unlock_lottie,
578                 R.raw.fingerprint_dialogue_fingerprint_to_unlock_lottie,
579                 R.raw.fingerprint_dialogue_error_to_success_lottie,
580                 R.raw.fingerprint_dialogue_fingerprint_to_success_lottie,
581                 R.raw.fingerprint_dialogue_error_to_fingerprint_lottie,
582                 R.raw.fingerprint_dialogue_fingerprint_to_error_lottie,
583             )
584         }
585 
586     /** Fingerprint iconView assets for caching */
587     fun getFingerprintAssetsList(hasSfps: Boolean): List<Int> =
588         if (hasSfps) {
589             listOf(
590                 R.raw.biometricprompt_sfps_fingerprint_authenticating,
591                 R.raw.biometricprompt_sfps_rear_display_fingerprint_authenticating,
592                 R.raw.biometricprompt_sfps_error_to_success,
593                 R.raw.biometricprompt_sfps_fingerprint_to_error,
594                 R.raw.biometricprompt_sfps_fingerprint_to_error_90,
595                 R.raw.biometricprompt_sfps_fingerprint_to_error_180,
596                 R.raw.biometricprompt_sfps_fingerprint_to_error_270,
597                 R.raw.biometricprompt_sfps_rear_display_fingerprint_to_error,
598                 R.raw.biometricprompt_sfps_rear_display_fingerprint_to_error_90,
599                 R.raw.biometricprompt_sfps_rear_display_fingerprint_to_error_180,
600                 R.raw.biometricprompt_sfps_rear_display_fingerprint_to_error_270,
601                 R.raw.biometricprompt_sfps_error_to_fingerprint,
602                 R.raw.biometricprompt_sfps_error_to_fingerprint_90,
603                 R.raw.biometricprompt_sfps_error_to_fingerprint_180,
604                 R.raw.biometricprompt_sfps_error_to_fingerprint_270,
605                 R.raw.biometricprompt_sfps_rear_display_error_to_fingerprint,
606                 R.raw.biometricprompt_sfps_rear_display_error_to_fingerprint_90,
607                 R.raw.biometricprompt_sfps_rear_display_error_to_fingerprint_180,
608                 R.raw.biometricprompt_sfps_rear_display_error_to_fingerprint_270,
609                 R.raw.biometricprompt_sfps_fingerprint_to_success,
610                 R.raw.biometricprompt_sfps_fingerprint_to_success_90,
611                 R.raw.biometricprompt_sfps_fingerprint_to_success_180,
612                 R.raw.biometricprompt_sfps_fingerprint_to_success_270,
613                 R.raw.biometricprompt_sfps_rear_display_fingerprint_to_success,
614                 R.raw.biometricprompt_sfps_rear_display_fingerprint_to_success_90,
615                 R.raw.biometricprompt_sfps_rear_display_fingerprint_to_success_180,
616                 R.raw.biometricprompt_sfps_rear_display_fingerprint_to_success_270,
617             )
618         } else {
619             listOf(
620                 R.raw.fingerprint_dialogue_error_to_fingerprint_lottie,
621                 R.raw.fingerprint_dialogue_error_to_success_lottie,
622                 R.raw.fingerprint_dialogue_fingerprint_to_error_lottie,
623                 R.raw.fingerprint_dialogue_fingerprint_to_success_lottie,
624             )
625         }
626 
627     /** Face iconView assets for caching */
628     fun getFaceAssetsList(): List<Int> =
629         listOf(
630             R.raw.face_dialog_wink_from_dark,
631             R.raw.face_dialog_dark_to_checkmark,
632             R.raw.face_dialog_dark_to_error,
633             R.raw.face_dialog_error_to_idle,
634             R.raw.face_dialog_idle_static,
635             R.raw.face_dialog_authenticating,
636         )
637 
638     private fun getSfpsAsset_fingerprintAuthenticating(isInRearDisplayMode: Boolean): Int =
639         if (isInRearDisplayMode) {
640             R.raw.biometricprompt_sfps_rear_display_fingerprint_authenticating
641         } else {
642             R.raw.biometricprompt_sfps_fingerprint_authenticating
643         }
644 
645     private fun getSfpsAsset_fingerprintToError(
646         rotation: DisplayRotation,
647         isInRearDisplayMode: Boolean,
648     ): Int =
649         if (isInRearDisplayMode) {
650             when (rotation) {
651                 DisplayRotation.ROTATION_0 ->
652                     R.raw.biometricprompt_sfps_rear_display_fingerprint_to_error
653                 DisplayRotation.ROTATION_90 ->
654                     R.raw.biometricprompt_sfps_rear_display_fingerprint_to_error_90
655                 DisplayRotation.ROTATION_180 ->
656                     R.raw.biometricprompt_sfps_rear_display_fingerprint_to_error_180
657                 DisplayRotation.ROTATION_270 ->
658                     R.raw.biometricprompt_sfps_rear_display_fingerprint_to_error_270
659             }
660         } else {
661             when (rotation) {
662                 DisplayRotation.ROTATION_0 -> R.raw.biometricprompt_sfps_fingerprint_to_error
663                 DisplayRotation.ROTATION_90 -> R.raw.biometricprompt_sfps_fingerprint_to_error_90
664                 DisplayRotation.ROTATION_180 -> R.raw.biometricprompt_sfps_fingerprint_to_error_180
665                 DisplayRotation.ROTATION_270 -> R.raw.biometricprompt_sfps_fingerprint_to_error_270
666             }
667         }
668 
669     private fun getSfpsAsset_errorToFingerprint(
670         rotation: DisplayRotation,
671         isInRearDisplayMode: Boolean,
672     ): Int =
673         if (isInRearDisplayMode) {
674             when (rotation) {
675                 DisplayRotation.ROTATION_0 ->
676                     R.raw.biometricprompt_sfps_rear_display_error_to_fingerprint
677                 DisplayRotation.ROTATION_90 ->
678                     R.raw.biometricprompt_sfps_rear_display_error_to_fingerprint_90
679                 DisplayRotation.ROTATION_180 ->
680                     R.raw.biometricprompt_sfps_rear_display_error_to_fingerprint_180
681                 DisplayRotation.ROTATION_270 ->
682                     R.raw.biometricprompt_sfps_rear_display_error_to_fingerprint_270
683             }
684         } else {
685             when (rotation) {
686                 DisplayRotation.ROTATION_0 -> R.raw.biometricprompt_sfps_error_to_fingerprint
687                 DisplayRotation.ROTATION_90 -> R.raw.biometricprompt_sfps_error_to_fingerprint_90
688                 DisplayRotation.ROTATION_180 -> R.raw.biometricprompt_sfps_error_to_fingerprint_180
689                 DisplayRotation.ROTATION_270 -> R.raw.biometricprompt_sfps_error_to_fingerprint_270
690             }
691         }
692 
693     private fun getSfpsAsset_fingerprintToUnlock(
694         rotation: DisplayRotation,
695         isInRearDisplayMode: Boolean,
696     ): Int =
697         if (isInRearDisplayMode) {
698             when (rotation) {
699                 DisplayRotation.ROTATION_0 ->
700                     R.raw.biometricprompt_sfps_rear_display_fingerprint_to_unlock
701                 DisplayRotation.ROTATION_90 ->
702                     R.raw.biometricprompt_sfps_rear_display_fingerprint_to_unlock_90
703                 DisplayRotation.ROTATION_180 ->
704                     R.raw.biometricprompt_sfps_rear_display_fingerprint_to_unlock_180
705                 DisplayRotation.ROTATION_270 ->
706                     R.raw.biometricprompt_sfps_rear_display_fingerprint_to_unlock_270
707             }
708         } else {
709             when (rotation) {
710                 DisplayRotation.ROTATION_0 -> R.raw.biometricprompt_sfps_fingerprint_to_unlock
711                 DisplayRotation.ROTATION_90 -> R.raw.biometricprompt_sfps_fingerprint_to_unlock_90
712                 DisplayRotation.ROTATION_180 -> R.raw.biometricprompt_sfps_fingerprint_to_unlock_180
713                 DisplayRotation.ROTATION_270 -> R.raw.biometricprompt_sfps_fingerprint_to_unlock_270
714             }
715         }
716 
717     private fun getSfpsAsset_fingerprintToSuccess(
718         rotation: DisplayRotation,
719         isInRearDisplayMode: Boolean,
720     ): Int =
721         if (isInRearDisplayMode) {
722             when (rotation) {
723                 DisplayRotation.ROTATION_0 ->
724                     R.raw.biometricprompt_sfps_rear_display_fingerprint_to_success
725                 DisplayRotation.ROTATION_90 ->
726                     R.raw.biometricprompt_sfps_rear_display_fingerprint_to_success_90
727                 DisplayRotation.ROTATION_180 ->
728                     R.raw.biometricprompt_sfps_rear_display_fingerprint_to_success_180
729                 DisplayRotation.ROTATION_270 ->
730                     R.raw.biometricprompt_sfps_rear_display_fingerprint_to_success_270
731             }
732         } else {
733             when (rotation) {
734                 DisplayRotation.ROTATION_0 -> R.raw.biometricprompt_sfps_fingerprint_to_success
735                 DisplayRotation.ROTATION_90 -> R.raw.biometricprompt_sfps_fingerprint_to_success_90
736                 DisplayRotation.ROTATION_180 ->
737                     R.raw.biometricprompt_sfps_fingerprint_to_success_180
738                 DisplayRotation.ROTATION_270 ->
739                     R.raw.biometricprompt_sfps_fingerprint_to_success_270
740             }
741         }
742 }
743