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.DrawableRes 21 import android.annotation.RawRes 22 import android.content.res.Configuration 23 import android.graphics.Rect 24 import android.hardware.face.Face 25 import android.util.RotationUtils 26 import com.android.systemui.biometrics.domain.interactor.DisplayStateInteractor 27 import com.android.systemui.biometrics.domain.interactor.PromptSelectorInteractor 28 import com.android.systemui.biometrics.domain.interactor.UdfpsOverlayInteractor 29 import com.android.systemui.biometrics.shared.model.DisplayRotation 30 import com.android.systemui.biometrics.shared.model.FingerprintSensorType 31 import com.android.systemui.res.R 32 import com.android.systemui.util.kotlin.combine 33 import kotlinx.coroutines.flow.Flow 34 import kotlinx.coroutines.flow.MutableStateFlow 35 import kotlinx.coroutines.flow.combine 36 import kotlinx.coroutines.flow.distinctUntilChanged 37 import kotlinx.coroutines.flow.flatMapLatest 38 import kotlinx.coroutines.flow.flowOf 39 40 /** 41 * Models UI of [BiometricPromptLayout.iconView] and [BiometricPromptLayout.biometric_icon_overlay] 42 */ 43 class PromptIconViewModel 44 constructor( 45 promptViewModel: PromptViewModel, 46 private val displayStateInteractor: DisplayStateInteractor, 47 promptSelectorInteractor: PromptSelectorInteractor, 48 udfpsOverlayInteractor: UdfpsOverlayInteractor, 49 ) { 50 51 /** Auth types for the UI to display. */ 52 enum class AuthType { 53 Fingerprint, 54 Face, 55 Coex 56 } 57 58 /** 59 * Indicates what auth type the UI currently displays. Fingerprint-only auth -> Fingerprint 60 * Face-only auth -> Face Co-ex auth, implicit flow -> Face Co-ex auth, explicit flow -> Coex 61 */ 62 val activeAuthType: Flow<AuthType> = 63 combine( 64 promptViewModel.modalities.distinctUntilChanged(), 65 promptViewModel.faceMode.distinctUntilChanged() 66 ) { modalities, faceMode -> 67 if (modalities.hasFaceAndFingerprint && !faceMode) { 68 AuthType.Coex 69 } else if (modalities.hasFaceOnly || faceMode) { 70 AuthType.Face 71 } else if (modalities.hasFingerprintOnly) { 72 AuthType.Fingerprint 73 } else { 74 // TODO(b/288175072): Remove, currently needed for transition to credential view 75 AuthType.Fingerprint 76 } 77 } 78 79 val udfpsSensorBounds: Flow<Rect> = 80 combine( 81 udfpsOverlayInteractor.udfpsOverlayParams, 82 displayStateInteractor.currentRotation 83 ) { params, rotation -> 84 val rotatedBounds = Rect(params.sensorBounds) 85 RotationUtils.rotateBounds( 86 rotatedBounds, 87 params.naturalDisplayWidth, 88 params.naturalDisplayHeight, 89 rotation.ordinal 90 ) 91 Rect( 92 rotatedBounds.left, 93 rotatedBounds.top, 94 params.logicalDisplayWidth - rotatedBounds.right, 95 params.logicalDisplayHeight - rotatedBounds.bottom 96 ) 97 } 98 .distinctUntilChanged() 99 100 /** Whether an error message is currently being shown. */ 101 val showingError = promptViewModel.showingError 102 103 /** Whether the previous icon shown displayed an error. */ 104 private val _previousIconWasError: MutableStateFlow<Boolean> = MutableStateFlow(false) 105 106 /** Whether the previous icon overlay shown displayed an error. */ 107 private val _previousIconOverlayWasError: MutableStateFlow<Boolean> = MutableStateFlow(false) 108 109 fun setPreviousIconWasError(previousIconWasError: Boolean) { 110 _previousIconWasError.value = previousIconWasError 111 } 112 113 fun setPreviousIconOverlayWasError(previousIconOverlayWasError: Boolean) { 114 _previousIconOverlayWasError.value = previousIconOverlayWasError 115 } 116 117 val iconSize: Flow<Pair<Int, Int>> = 118 combine( 119 promptViewModel.position, 120 activeAuthType, 121 promptViewModel.legacyFingerprintSensorWidth, 122 promptViewModel.legacyFingerprintSensorHeight, 123 ) { _, activeAuthType, fingerprintSensorWidth, fingerprintSensorHeight -> 124 if (activeAuthType == AuthType.Face) { 125 Pair(promptViewModel.faceIconWidth, promptViewModel.faceIconHeight) 126 } else { 127 Pair(fingerprintSensorWidth, fingerprintSensorHeight) 128 } 129 } 130 131 /** Current BiometricPromptLayout.iconView asset. */ 132 val iconAsset: Flow<Int> = 133 activeAuthType.flatMapLatest { activeAuthType: AuthType -> 134 when (activeAuthType) { 135 AuthType.Fingerprint -> 136 combine( 137 displayStateInteractor.currentRotation, 138 displayStateInteractor.isFolded, 139 displayStateInteractor.isInRearDisplayMode, 140 promptSelectorInteractor.sensorType, 141 promptViewModel.isAuthenticated, 142 promptViewModel.isAuthenticating, 143 promptViewModel.showingError 144 ) { 145 rotation: DisplayRotation, 146 isFolded: Boolean, 147 isInRearDisplayMode: Boolean, 148 sensorType: FingerprintSensorType, 149 authState: PromptAuthState, 150 isAuthenticating: Boolean, 151 showingError: Boolean -> 152 when (sensorType) { 153 FingerprintSensorType.POWER_BUTTON -> 154 getSfpsIconViewAsset(rotation, isFolded, isInRearDisplayMode) 155 else -> 156 getFingerprintIconViewAsset( 157 authState.isAuthenticated, 158 isAuthenticating, 159 showingError 160 ) 161 } 162 } 163 AuthType.Face -> 164 combine( 165 promptViewModel.isAuthenticated.distinctUntilChanged(), 166 promptViewModel.isAuthenticating.distinctUntilChanged(), 167 promptViewModel.isPendingConfirmation.distinctUntilChanged(), 168 promptViewModel.showingError.distinctUntilChanged() 169 ) { 170 authState: PromptAuthState, 171 isAuthenticating: Boolean, 172 isPendingConfirmation: Boolean, 173 showingError: Boolean -> 174 getFaceIconViewAsset( 175 authState, 176 isAuthenticating, 177 isPendingConfirmation, 178 showingError 179 ) 180 } 181 AuthType.Coex -> 182 combine( 183 displayStateInteractor.currentRotation, 184 displayStateInteractor.isFolded, 185 displayStateInteractor.isInRearDisplayMode, 186 promptSelectorInteractor.sensorType, 187 promptViewModel.isAuthenticated, 188 promptViewModel.isAuthenticating, 189 promptViewModel.isPendingConfirmation, 190 promptViewModel.showingError, 191 ) { 192 rotation: DisplayRotation, 193 isFolded: Boolean, 194 isInRearDisplayMode: Boolean, 195 sensorType: FingerprintSensorType, 196 authState: PromptAuthState, 197 isAuthenticating: Boolean, 198 isPendingConfirmation: Boolean, 199 showingError: Boolean -> 200 when (sensorType) { 201 FingerprintSensorType.POWER_BUTTON -> 202 getSfpsIconViewAsset(rotation, isFolded, isInRearDisplayMode) 203 else -> 204 getCoexIconViewAsset( 205 authState, 206 isAuthenticating, 207 isPendingConfirmation, 208 showingError 209 ) 210 } 211 } 212 } 213 } 214 215 private fun getFingerprintIconViewAsset( 216 isAuthenticated: Boolean, 217 isAuthenticating: Boolean, 218 showingError: Boolean 219 ): Int = 220 if (isAuthenticated) { 221 if (_previousIconWasError.value) { 222 R.raw.fingerprint_dialogue_error_to_success_lottie 223 } else { 224 R.raw.fingerprint_dialogue_fingerprint_to_success_lottie 225 } 226 } else if (isAuthenticating) { 227 if (_previousIconWasError.value) { 228 R.raw.fingerprint_dialogue_error_to_fingerprint_lottie 229 } else { 230 R.raw.fingerprint_dialogue_fingerprint_to_error_lottie 231 } 232 } else if (showingError) { 233 R.raw.fingerprint_dialogue_fingerprint_to_error_lottie 234 } else { 235 -1 236 } 237 238 @RawRes 239 private fun getSfpsIconViewAsset( 240 rotation: DisplayRotation, 241 isDeviceFolded: Boolean, 242 isInRearDisplayMode: Boolean, 243 ): Int = 244 when (rotation) { 245 DisplayRotation.ROTATION_90 -> 246 if (isInRearDisplayMode) { 247 R.raw.biometricprompt_rear_portrait_reverse_base 248 } else if (isDeviceFolded) { 249 R.raw.biometricprompt_folded_base_topleft 250 } else { 251 R.raw.biometricprompt_portrait_base_topleft 252 } 253 DisplayRotation.ROTATION_270 -> 254 if (isInRearDisplayMode) { 255 R.raw.biometricprompt_rear_portrait_base 256 } else if (isDeviceFolded) { 257 R.raw.biometricprompt_folded_base_bottomright 258 } else { 259 R.raw.biometricprompt_portrait_base_bottomright 260 } 261 else -> 262 if (isInRearDisplayMode) { 263 R.raw.biometricprompt_rear_landscape_base 264 } else if (isDeviceFolded) { 265 R.raw.biometricprompt_folded_base_default 266 } else { 267 R.raw.biometricprompt_landscape_base 268 } 269 } 270 271 @DrawableRes 272 private fun getFaceIconViewAsset( 273 authState: PromptAuthState, 274 isAuthenticating: Boolean, 275 isPendingConfirmation: Boolean, 276 showingError: Boolean 277 ): Int = 278 if (authState.isAuthenticated && isPendingConfirmation) { 279 R.drawable.face_dialog_wink_from_dark 280 } else if (authState.isAuthenticated) { 281 R.drawable.face_dialog_dark_to_checkmark 282 } else if (isAuthenticating) { 283 R.raw.face_dialog_authenticating 284 } else if (showingError) { 285 R.drawable.face_dialog_dark_to_error 286 } else if (_previousIconWasError.value) { 287 R.drawable.face_dialog_error_to_idle 288 } else { 289 R.drawable.face_dialog_idle_static 290 } 291 292 @RawRes 293 private fun getCoexIconViewAsset( 294 authState: PromptAuthState, 295 isAuthenticating: Boolean, 296 isPendingConfirmation: Boolean, 297 showingError: Boolean 298 ): Int = 299 if (authState.isAuthenticatedAndExplicitlyConfirmed) { 300 R.raw.fingerprint_dialogue_unlocked_to_checkmark_success_lottie 301 } else if (isPendingConfirmation) { 302 if (_previousIconWasError.value) { 303 R.raw.fingerprint_dialogue_error_to_unlock_lottie 304 } else { 305 R.raw.fingerprint_dialogue_fingerprint_to_unlock_lottie 306 } 307 } else if (authState.isAuthenticated) { 308 if (_previousIconWasError.value) { 309 R.raw.fingerprint_dialogue_error_to_success_lottie 310 } else { 311 R.raw.fingerprint_dialogue_fingerprint_to_success_lottie 312 } 313 } else if (isAuthenticating) { 314 if (_previousIconWasError.value) { 315 R.raw.fingerprint_dialogue_error_to_fingerprint_lottie 316 } else { 317 R.raw.fingerprint_dialogue_fingerprint_to_error_lottie 318 } 319 } else if (showingError) { 320 R.raw.fingerprint_dialogue_fingerprint_to_error_lottie 321 } else { 322 -1 323 } 324 325 /** Current BiometricPromptLayout.biometric_icon_overlay asset. */ 326 var iconOverlayAsset: Flow<Int> = 327 activeAuthType.flatMapLatest { activeAuthType: AuthType -> 328 when (activeAuthType) { 329 AuthType.Fingerprint, 330 AuthType.Coex -> 331 combine( 332 displayStateInteractor.currentRotation, 333 promptSelectorInteractor.sensorType, 334 promptViewModel.isAuthenticated, 335 promptViewModel.isAuthenticating, 336 promptViewModel.showingError 337 ) { 338 rotation: DisplayRotation, 339 sensorType: FingerprintSensorType, 340 authState: PromptAuthState, 341 isAuthenticating: Boolean, 342 showingError: Boolean -> 343 when (sensorType) { 344 FingerprintSensorType.POWER_BUTTON -> 345 getSfpsIconOverlayAsset( 346 rotation, 347 authState.isAuthenticated, 348 isAuthenticating, 349 showingError 350 ) 351 else -> -1 352 } 353 } 354 AuthType.Face -> flowOf(-1) 355 } 356 } 357 358 @RawRes 359 private fun getSfpsIconOverlayAsset( 360 rotation: DisplayRotation, 361 isAuthenticated: Boolean, 362 isAuthenticating: Boolean, 363 showingError: Boolean 364 ): Int = 365 if (isAuthenticated) { 366 if (_previousIconOverlayWasError.value) { 367 when (rotation) { 368 DisplayRotation.ROTATION_0 -> 369 R.raw.biometricprompt_symbol_error_to_success_landscape 370 DisplayRotation.ROTATION_90 -> 371 R.raw.biometricprompt_symbol_error_to_success_portrait_topleft 372 DisplayRotation.ROTATION_180 -> 373 R.raw.biometricprompt_symbol_error_to_success_landscape 374 DisplayRotation.ROTATION_270 -> 375 R.raw.biometricprompt_symbol_error_to_success_portrait_bottomright 376 } 377 } else { 378 when (rotation) { 379 DisplayRotation.ROTATION_0 -> 380 R.raw.biometricprompt_symbol_fingerprint_to_success_landscape 381 DisplayRotation.ROTATION_90 -> 382 R.raw.biometricprompt_symbol_fingerprint_to_success_portrait_topleft 383 DisplayRotation.ROTATION_180 -> 384 R.raw.biometricprompt_symbol_fingerprint_to_success_landscape 385 DisplayRotation.ROTATION_270 -> 386 R.raw.biometricprompt_symbol_fingerprint_to_success_portrait_bottomright 387 } 388 } 389 } else if (isAuthenticating) { 390 if (_previousIconOverlayWasError.value) { 391 when (rotation) { 392 DisplayRotation.ROTATION_0 -> 393 R.raw.biometricprompt_symbol_error_to_fingerprint_landscape 394 DisplayRotation.ROTATION_90 -> 395 R.raw.biometricprompt_symbol_error_to_fingerprint_portrait_topleft 396 DisplayRotation.ROTATION_180 -> 397 R.raw.biometricprompt_symbol_error_to_fingerprint_landscape 398 DisplayRotation.ROTATION_270 -> 399 R.raw.biometricprompt_symbol_error_to_fingerprint_portrait_bottomright 400 } 401 } else { 402 when (rotation) { 403 DisplayRotation.ROTATION_0 -> 404 R.raw.biometricprompt_fingerprint_to_error_landscape 405 DisplayRotation.ROTATION_90 -> 406 R.raw.biometricprompt_symbol_fingerprint_to_error_portrait_topleft 407 DisplayRotation.ROTATION_180 -> 408 R.raw.biometricprompt_fingerprint_to_error_landscape 409 DisplayRotation.ROTATION_270 -> 410 R.raw.biometricprompt_symbol_fingerprint_to_error_portrait_bottomright 411 } 412 } 413 } else if (showingError) { 414 when (rotation) { 415 DisplayRotation.ROTATION_0 -> R.raw.biometricprompt_fingerprint_to_error_landscape 416 DisplayRotation.ROTATION_90 -> 417 R.raw.biometricprompt_symbol_fingerprint_to_error_portrait_topleft 418 DisplayRotation.ROTATION_180 -> R.raw.biometricprompt_fingerprint_to_error_landscape 419 DisplayRotation.ROTATION_270 -> 420 R.raw.biometricprompt_symbol_fingerprint_to_error_portrait_bottomright 421 } 422 } else { 423 -1 424 } 425 426 /** Content description for iconView */ 427 val contentDescriptionId: Flow<Int> = 428 activeAuthType.flatMapLatest { activeAuthType: AuthType -> 429 when (activeAuthType) { 430 AuthType.Fingerprint, 431 AuthType.Coex -> 432 combine( 433 promptSelectorInteractor.sensorType, 434 promptViewModel.isAuthenticated, 435 promptViewModel.isAuthenticating, 436 promptViewModel.isPendingConfirmation, 437 promptViewModel.showingError 438 ) { 439 sensorType: FingerprintSensorType, 440 authState: PromptAuthState, 441 isAuthenticating: Boolean, 442 isPendingConfirmation: Boolean, 443 showingError: Boolean -> 444 getFingerprintIconContentDescriptionId( 445 sensorType, 446 authState.isAuthenticated, 447 isAuthenticating, 448 isPendingConfirmation, 449 showingError 450 ) 451 } 452 AuthType.Face -> 453 combine( 454 promptViewModel.isAuthenticated, 455 promptViewModel.isAuthenticating, 456 promptViewModel.showingError, 457 ) { authState: PromptAuthState, isAuthenticating: Boolean, showingError: Boolean 458 -> 459 getFaceIconContentDescriptionId(authState, isAuthenticating, showingError) 460 } 461 } 462 } 463 464 private fun getFingerprintIconContentDescriptionId( 465 sensorType: FingerprintSensorType, 466 isAuthenticated: Boolean, 467 isAuthenticating: Boolean, 468 isPendingConfirmation: Boolean, 469 showingError: Boolean 470 ): Int = 471 if (isPendingConfirmation) { 472 when (sensorType) { 473 FingerprintSensorType.POWER_BUTTON -> -1 474 else -> R.string.fingerprint_dialog_authenticated_confirmation 475 } 476 } else if (isAuthenticating || isAuthenticated) { 477 when (sensorType) { 478 FingerprintSensorType.POWER_BUTTON -> 479 R.string.security_settings_sfps_enroll_find_sensor_message 480 else -> R.string.fingerprint_dialog_touch_sensor 481 } 482 } else if (showingError) { 483 R.string.biometric_dialog_try_again 484 } else { 485 -1 486 } 487 488 private fun getFaceIconContentDescriptionId( 489 authState: PromptAuthState, 490 isAuthenticating: Boolean, 491 showingError: Boolean 492 ): Int = 493 if (authState.isAuthenticatedAndExplicitlyConfirmed) { 494 R.string.biometric_dialog_face_icon_description_confirmed 495 } else if (authState.isAuthenticated) { 496 R.string.biometric_dialog_face_icon_description_authenticated 497 } else if (isAuthenticating) { 498 R.string.biometric_dialog_face_icon_description_authenticating 499 } else if (showingError) { 500 R.string.keyguard_face_failed 501 } else { 502 R.string.biometric_dialog_face_icon_description_idle 503 } 504 505 /** Whether the current BiometricPromptLayout.iconView asset animation should be playing. */ 506 val shouldAnimateIconView: Flow<Boolean> = 507 activeAuthType.flatMapLatest { activeAuthType: AuthType -> 508 when (activeAuthType) { 509 AuthType.Fingerprint -> 510 combine( 511 promptSelectorInteractor.sensorType, 512 promptViewModel.isAuthenticated, 513 promptViewModel.isAuthenticating, 514 promptViewModel.showingError 515 ) { 516 sensorType: FingerprintSensorType, 517 authState: PromptAuthState, 518 isAuthenticating: Boolean, 519 showingError: Boolean -> 520 when (sensorType) { 521 FingerprintSensorType.POWER_BUTTON -> 522 shouldAnimateSfpsIconView( 523 authState.isAuthenticated, 524 isAuthenticating, 525 showingError 526 ) 527 else -> 528 shouldAnimateFingerprintIconView( 529 authState.isAuthenticated, 530 isAuthenticating, 531 showingError 532 ) 533 } 534 } 535 AuthType.Face -> 536 combine( 537 promptViewModel.isAuthenticated, 538 promptViewModel.isAuthenticating, 539 promptViewModel.showingError 540 ) { authState: PromptAuthState, isAuthenticating: Boolean, showingError: Boolean 541 -> 542 isAuthenticating || 543 authState.isAuthenticated || 544 showingError || 545 _previousIconWasError.value 546 } 547 AuthType.Coex -> 548 combine( 549 promptSelectorInteractor.sensorType, 550 promptViewModel.isAuthenticated, 551 promptViewModel.isAuthenticating, 552 promptViewModel.isPendingConfirmation, 553 promptViewModel.showingError, 554 ) { 555 sensorType: FingerprintSensorType, 556 authState: PromptAuthState, 557 isAuthenticating: Boolean, 558 isPendingConfirmation: Boolean, 559 showingError: Boolean -> 560 when (sensorType) { 561 FingerprintSensorType.POWER_BUTTON -> 562 shouldAnimateSfpsIconView( 563 authState.isAuthenticated, 564 isAuthenticating, 565 showingError 566 ) 567 else -> 568 shouldAnimateCoexIconView( 569 authState.isAuthenticated, 570 isAuthenticating, 571 isPendingConfirmation, 572 showingError 573 ) 574 } 575 } 576 } 577 } 578 579 private fun shouldAnimateFingerprintIconView( 580 isAuthenticated: Boolean, 581 isAuthenticating: Boolean, 582 showingError: Boolean 583 ) = (isAuthenticating && _previousIconWasError.value) || isAuthenticated || showingError 584 585 private fun shouldAnimateSfpsIconView( 586 isAuthenticated: Boolean, 587 isAuthenticating: Boolean, 588 showingError: Boolean 589 ) = isAuthenticated || isAuthenticating || showingError 590 591 private fun shouldAnimateCoexIconView( 592 isAuthenticated: Boolean, 593 isAuthenticating: Boolean, 594 isPendingConfirmation: Boolean, 595 showingError: Boolean 596 ) = 597 (isAuthenticating && _previousIconWasError.value) || 598 isPendingConfirmation || 599 isAuthenticated || 600 showingError 601 602 /** Whether the current iconOverlayAsset animation should be playing. */ 603 val shouldAnimateIconOverlay: Flow<Boolean> = 604 activeAuthType.flatMapLatest { activeAuthType: AuthType -> 605 when (activeAuthType) { 606 AuthType.Fingerprint, 607 AuthType.Coex -> 608 combine( 609 promptSelectorInteractor.sensorType, 610 promptViewModel.isAuthenticated, 611 promptViewModel.isAuthenticating, 612 promptViewModel.showingError 613 ) { 614 sensorType: FingerprintSensorType, 615 authState: PromptAuthState, 616 isAuthenticating: Boolean, 617 showingError: Boolean -> 618 when (sensorType) { 619 FingerprintSensorType.POWER_BUTTON -> 620 shouldAnimateSfpsIconOverlay( 621 authState.isAuthenticated, 622 isAuthenticating, 623 showingError 624 ) 625 else -> false 626 } 627 } 628 AuthType.Face -> flowOf(false) 629 } 630 } 631 632 private fun shouldAnimateSfpsIconOverlay( 633 isAuthenticated: Boolean, 634 isAuthenticating: Boolean, 635 showingError: Boolean 636 ) = (isAuthenticating && _previousIconOverlayWasError.value) || isAuthenticated || showingError 637 638 /** Whether the iconView should be flipped due to a device using reverse default rotation . */ 639 val shouldFlipIconView: Flow<Boolean> = 640 activeAuthType.flatMapLatest { activeAuthType: AuthType -> 641 when (activeAuthType) { 642 AuthType.Fingerprint, 643 AuthType.Coex -> 644 combine( 645 promptSelectorInteractor.sensorType, 646 displayStateInteractor.currentRotation 647 ) { sensorType: FingerprintSensorType, rotation: DisplayRotation -> 648 when (sensorType) { 649 FingerprintSensorType.POWER_BUTTON -> 650 (rotation == DisplayRotation.ROTATION_180) 651 else -> false 652 } 653 } 654 AuthType.Face -> flowOf(false) 655 } 656 } 657 658 /** Called on configuration changes */ 659 fun onConfigurationChanged(newConfig: Configuration) { 660 displayStateInteractor.onConfigurationChanged(newConfig) 661 } 662 663 /** iconView assets for caching */ 664 fun getRawAssets(hasSfps: Boolean): List<Int> { 665 return if (hasSfps) { 666 listOf( 667 R.raw.biometricprompt_fingerprint_to_error_landscape, 668 R.raw.biometricprompt_folded_base_bottomright, 669 R.raw.biometricprompt_folded_base_default, 670 R.raw.biometricprompt_folded_base_topleft, 671 R.raw.biometricprompt_landscape_base, 672 R.raw.biometricprompt_portrait_base_bottomright, 673 R.raw.biometricprompt_portrait_base_topleft, 674 R.raw.biometricprompt_symbol_error_to_fingerprint_landscape, 675 R.raw.biometricprompt_symbol_error_to_fingerprint_portrait_bottomright, 676 R.raw.biometricprompt_symbol_error_to_fingerprint_portrait_topleft, 677 R.raw.biometricprompt_symbol_error_to_success_landscape, 678 R.raw.biometricprompt_symbol_error_to_success_portrait_bottomright, 679 R.raw.biometricprompt_symbol_error_to_success_portrait_topleft, 680 R.raw.biometricprompt_symbol_fingerprint_to_error_portrait_bottomright, 681 R.raw.biometricprompt_symbol_fingerprint_to_error_portrait_topleft, 682 R.raw.biometricprompt_symbol_fingerprint_to_success_landscape, 683 R.raw.biometricprompt_symbol_fingerprint_to_success_portrait_bottomright, 684 R.raw.biometricprompt_symbol_fingerprint_to_success_portrait_topleft 685 ) 686 } else { 687 listOf( 688 R.raw.fingerprint_dialogue_error_to_fingerprint_lottie, 689 R.raw.fingerprint_dialogue_error_to_success_lottie, 690 R.raw.fingerprint_dialogue_fingerprint_to_error_lottie, 691 R.raw.fingerprint_dialogue_fingerprint_to_success_lottie 692 ) 693 } 694 } 695 } 696