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