• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 /*
<lambda>null2  * Copyright (C) 2022 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.bouncer.domain.interactor
18 
19 import android.util.Log
20 import com.android.systemui.biometrics.data.repository.FingerprintPropertyRepository
21 import com.android.systemui.bouncer.data.repository.KeyguardBouncerRepository
22 import com.android.systemui.dagger.SysUISingleton
23 import com.android.systemui.dagger.qualifiers.Application
24 import com.android.systemui.deviceentry.domain.interactor.DeviceEntryBiometricsAllowedInteractor
25 import com.android.systemui.keyguard.domain.interactor.KeyguardInteractor
26 import com.android.systemui.keyguard.domain.interactor.KeyguardTransitionInteractor
27 import com.android.systemui.keyguard.shared.model.KeyguardState
28 import com.android.systemui.scene.domain.interactor.SceneInteractor
29 import com.android.systemui.scene.shared.flag.SceneContainerFlag
30 import com.android.systemui.scene.shared.model.Scenes
31 import com.android.systemui.util.kotlin.BooleanFlowOperators.anyOf
32 import com.android.systemui.util.time.SystemClock
33 import dagger.Lazy
34 import javax.inject.Inject
35 import kotlinx.coroutines.CoroutineScope
36 import kotlinx.coroutines.flow.Flow
37 import kotlinx.coroutines.flow.SharingStarted
38 import kotlinx.coroutines.flow.SharingStarted.Companion.WhileSubscribed
39 import kotlinx.coroutines.flow.StateFlow
40 import kotlinx.coroutines.flow.combine
41 import kotlinx.coroutines.flow.distinctUntilChanged
42 import kotlinx.coroutines.flow.flatMapLatest
43 import kotlinx.coroutines.flow.flowOf
44 import kotlinx.coroutines.flow.map
45 import kotlinx.coroutines.flow.onEach
46 import kotlinx.coroutines.flow.stateIn
47 
48 /** Encapsulates business logic for interacting with the lock-screen alternate bouncer. */
49 @SysUISingleton
50 class AlternateBouncerInteractor
51 @Inject
52 constructor(
53     private val bouncerRepository: KeyguardBouncerRepository,
54     fingerprintPropertyRepository: FingerprintPropertyRepository,
55     private val systemClock: SystemClock,
56     private val deviceEntryBiometricsAllowedInteractor:
57         Lazy<DeviceEntryBiometricsAllowedInteractor>,
58     private val keyguardInteractor: Lazy<KeyguardInteractor>,
59     keyguardTransitionInteractor: Lazy<KeyguardTransitionInteractor>,
60     sceneInteractor: Lazy<SceneInteractor>,
61     @Application scope: CoroutineScope,
62 ) {
63     var receivedDownTouch = false
64     val isVisible: Flow<Boolean> = bouncerRepository.alternateBouncerVisible
65     private val alternateBouncerUiAvailableFromSource: HashSet<String> = HashSet()
66     val alternateBouncerSupported: StateFlow<Boolean> =
67         fingerprintPropertyRepository.sensorType
68             .map { sensorType -> sensorType.isUdfps() || sensorType.isPowerButton() }
69             .stateIn(scope = scope, started = SharingStarted.Eagerly, initialValue = false)
70 
71     private val isDozingOrAod: Flow<Boolean> =
72         anyOf(
73                 keyguardTransitionInteractor.get().transitionValue(KeyguardState.DOZING).map {
74                     it > 0f
75                 },
76                 keyguardTransitionInteractor.get().transitionValue(KeyguardState.AOD).map {
77                     it > 0f
78                 },
79             )
80             .distinctUntilChanged()
81 
82     /**
83      * Whether the current biometric, bouncer, and keyguard states allow the alternate bouncer to
84      * show.
85      */
86     val canShowAlternateBouncer: StateFlow<Boolean> =
87         alternateBouncerSupported
88             .flatMapLatest { alternateBouncerSupported ->
89                 if (alternateBouncerSupported) {
90                     combine(
91                             keyguardTransitionInteractor.get().currentKeyguardState,
92                             sceneInteractor.get().currentScene,
93                             ::Pair,
94                         )
95                         .flatMapLatest { (currentKeyguardState, transitionState) ->
96                             if (currentKeyguardState == KeyguardState.GONE) {
97                                 flowOf(false)
98                             } else if (
99                                 SceneContainerFlag.isEnabled && transitionState == Scenes.Gone
100                             ) {
101                                 flowOf(false)
102                             } else {
103                                 combine(
104                                     deviceEntryBiometricsAllowedInteractor
105                                         .get()
106                                         .isFingerprintAuthCurrentlyAllowed,
107                                     keyguardInteractor.get().isKeyguardDismissible,
108                                     bouncerRepository.primaryBouncerShow,
109                                     isDozingOrAod,
110                                 ) {
111                                     fingerprintAllowed,
112                                     keyguardDismissible,
113                                     primaryBouncerShowing,
114                                     dozing ->
115                                     fingerprintAllowed &&
116                                         !keyguardDismissible &&
117                                         !primaryBouncerShowing &&
118                                         !dozing
119                                 }
120                             }
121                         }
122                 } else {
123                     flowOf(false)
124                 }
125             }
126             .distinctUntilChanged()
127             .onEach { Log.d(TAG, "canShowAlternateBouncer changed to $it") }
128             .stateIn(scope = scope, started = WhileSubscribed(), initialValue = false)
129 
130     /**
131      * Always shows the alternate bouncer. Requesters must check [canShowAlternateBouncer]` before
132      * calling this.
133      */
134     fun forceShow() {
135         bouncerRepository.setAlternateVisible(true)
136     }
137 
138     /**
139      * Sets the correct bouncer states to hide the bouncer. Should only be called through
140      * StatusBarKeyguardViewManager until ScrimController is refactored to use
141      * alternateBouncerInteractor.
142      *
143      * @return true if the alternate bouncer was newly hidden, else false.
144      */
145     fun hide(): Boolean {
146         receivedDownTouch = false
147         val wasAlternateBouncerVisible = isVisibleState()
148         bouncerRepository.setAlternateVisible(false)
149         return wasAlternateBouncerVisible && !isVisibleState()
150     }
151 
152     fun isVisibleState(): Boolean {
153         return bouncerRepository.alternateBouncerVisible.value
154     }
155 
156     fun canShowAlternateBouncerForFingerprint(): Boolean {
157         return canShowAlternateBouncer.value
158     }
159 
160     /**
161      * Whether the alt bouncer has shown for a minimum time before allowing touches to dismiss the
162      * alternate bouncer and show the primary bouncer.
163      */
164     fun hasAlternateBouncerShownWithMinTime(): Boolean {
165         return (systemClock.uptimeMillis() - bouncerRepository.lastAlternateBouncerVisibleTime) >
166             MIN_VISIBILITY_DURATION_UNTIL_TOUCHES_DISMISS_ALTERNATE_BOUNCER_MS
167     }
168 
169     /**
170      * Should only be called through StatusBarKeyguardViewManager which propagates the source of
171      * truth to other concerned controllers. Will hide the alternate bouncer if it's no longer
172      * allowed to show.
173      *
174      * @return true if the alternate bouncer was newly hidden, else false.
175      */
176     fun maybeHide(): Boolean {
177         if (isVisibleState() && !canShowAlternateBouncerForFingerprint()) {
178             return hide()
179         }
180         return false
181     }
182 
183     companion object {
184         private const val MIN_VISIBILITY_DURATION_UNTIL_TOUCHES_DISMISS_ALTERNATE_BOUNCER_MS = 200L
185 
186         private const val TAG = "AlternateBouncerInteractor"
187     }
188 }
189