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 package com.android.systemui.keyguard.domain.interactor 18 19 import android.content.Context 20 import android.util.Log 21 import com.android.keyguard.KeyguardUpdateMonitor 22 import com.android.systemui.bouncer.domain.interactor.AlternateBouncerInteractor 23 import com.android.systemui.bouncer.domain.interactor.PrimaryBouncerInteractor 24 import com.android.systemui.dagger.SysUISingleton 25 import com.android.systemui.dagger.qualifiers.Application 26 import com.android.systemui.deviceentry.shared.DeviceEntryUdfpsRefactor 27 import com.android.systemui.keyguard.data.repository.BiometricType 28 import com.android.systemui.keyguard.data.repository.DeviceEntryFingerprintAuthRepository 29 import com.android.systemui.res.R 30 import com.android.systemui.scene.domain.interactor.SceneInteractor 31 import com.android.systemui.scene.shared.flag.SceneContainerFlag 32 import com.android.systemui.scene.shared.model.Scenes 33 import javax.inject.Inject 34 import kotlinx.coroutines.CoroutineScope 35 import kotlinx.coroutines.flow.Flow 36 import kotlinx.coroutines.flow.combine 37 import kotlinx.coroutines.flow.distinctUntilChanged 38 import kotlinx.coroutines.flow.filter 39 import kotlinx.coroutines.flow.filterNotNull 40 import kotlinx.coroutines.flow.flowOf 41 import kotlinx.coroutines.flow.map 42 import kotlinx.coroutines.flow.merge 43 import kotlinx.coroutines.flow.onEach 44 import kotlinx.coroutines.launch 45 46 /** 47 * Encapsulates business logic for device entry events that impact the side fingerprint sensor 48 * overlay. 49 */ 50 @SysUISingleton 51 class DeviceEntrySideFpsOverlayInteractor 52 @Inject 53 constructor( 54 @Application private val applicationScope: CoroutineScope, 55 @Application private val context: Context, 56 deviceEntryFingerprintAuthRepository: DeviceEntryFingerprintAuthRepository, 57 private val sceneInteractor: SceneInteractor, 58 private val primaryBouncerInteractor: PrimaryBouncerInteractor, 59 alternateBouncerInteractor: AlternateBouncerInteractor, 60 private val keyguardUpdateMonitor: KeyguardUpdateMonitor, 61 ) { 62 63 init { 64 if (!DeviceEntryUdfpsRefactor.isEnabled) { 65 applicationScope.launch { 66 deviceEntryFingerprintAuthRepository.availableFpSensorType.collect { sensorType -> 67 if (sensorType == BiometricType.SIDE_FINGERPRINT) { 68 alternateBouncerInteractor.setAlternateBouncerUIAvailable(true, TAG) 69 } 70 } 71 } 72 } 73 } 74 75 private val isSideFpsIndicatorOnPrimaryBouncerEnabled: Boolean 76 get() = context.resources.getBoolean(R.bool.config_show_sidefps_hint_on_bouncer) 77 78 private val isBouncerSceneActive: Flow<Boolean> = 79 if (SceneContainerFlag.isEnabled) { 80 sceneInteractor.currentScene.map { it == Scenes.Bouncer }.distinctUntilChanged() 81 } else { 82 flowOf(false) 83 } 84 85 private val showIndicatorForPrimaryBouncer: Flow<Boolean> = 86 merge( 87 // Legacy bouncer visibility changes. 88 primaryBouncerInteractor.isShowing, 89 primaryBouncerInteractor.startingToHide, 90 primaryBouncerInteractor.startingDisappearAnimation.filterNotNull(), 91 // Bouncer scene visibility changes. 92 isBouncerSceneActive, 93 deviceEntryFingerprintAuthRepository.shouldUpdateIndicatorVisibility.filter { it } 94 ) 95 .map { 96 isBouncerActive() && 97 isSideFpsIndicatorOnPrimaryBouncerEnabled && 98 keyguardUpdateMonitor.isFingerprintDetectionRunning && 99 keyguardUpdateMonitor.isUnlockingWithFingerprintAllowed 100 } 101 .onEach { Log.d(TAG, "showIndicatorForPrimaryBouncer updated: $it") } 102 103 private val showIndicatorForAlternateBouncer: Flow<Boolean> = 104 // Note: this interactor internally verifies that SideFPS is enabled and running. 105 alternateBouncerInteractor.isVisible.onEach { 106 Log.d(TAG, "showIndicatorForAlternateBouncer updated: $it") 107 } 108 109 /** 110 * Indicates whether the primary or alternate bouncers request showing the side fingerprint 111 * sensor indicator. 112 */ 113 val showIndicatorForDeviceEntry: Flow<Boolean> = 114 combine(showIndicatorForPrimaryBouncer, showIndicatorForAlternateBouncer) { 115 showForPrimaryBouncer, 116 showForAlternateBouncer -> 117 showForPrimaryBouncer || showForAlternateBouncer 118 } 119 .distinctUntilChanged() 120 .onEach { Log.d(TAG, "showIndicatorForDeviceEntry updated: $it") } 121 122 private fun isBouncerActive(): Boolean { 123 if (SceneContainerFlag.isEnabled) { 124 return sceneInteractor.currentScene.value == Scenes.Bouncer 125 } 126 return primaryBouncerInteractor.isBouncerShowing() && 127 !primaryBouncerInteractor.isAnimatingAway() 128 } 129 130 companion object { 131 private const val TAG = "DeviceEntrySideFpsOverlayInteractor" 132 } 133 } 134