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 com.android.keyguard.KeyguardUpdateMonitor 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.DeviceEntryFingerprintAuthInteractor 25 import com.android.systemui.deviceentry.shared.DeviceEntryUdfpsRefactor 26 import com.android.systemui.keyguard.data.repository.BiometricSettingsRepository 27 import com.android.systemui.keyguard.domain.interactor.KeyguardInteractor 28 import com.android.systemui.keyguard.domain.interactor.KeyguardTransitionInteractor 29 import com.android.systemui.keyguard.shared.model.KeyguardState 30 import com.android.systemui.plugins.statusbar.StatusBarStateController 31 import com.android.systemui.scene.domain.interactor.SceneInteractor 32 import com.android.systemui.scene.shared.flag.SceneContainerFlag 33 import com.android.systemui.scene.shared.model.Scenes 34 import com.android.systemui.statusbar.policy.KeyguardStateController 35 import com.android.systemui.util.kotlin.BooleanFlowOperators.anyOf 36 import com.android.systemui.util.time.SystemClock 37 import dagger.Lazy 38 import javax.inject.Inject 39 import kotlinx.coroutines.CoroutineScope 40 import kotlinx.coroutines.flow.Flow 41 import kotlinx.coroutines.flow.SharingStarted 42 import kotlinx.coroutines.flow.SharingStarted.Companion.WhileSubscribed 43 import kotlinx.coroutines.flow.StateFlow 44 import kotlinx.coroutines.flow.combine 45 import kotlinx.coroutines.flow.distinctUntilChanged 46 import kotlinx.coroutines.flow.flatMapLatest 47 import kotlinx.coroutines.flow.flowOf 48 import kotlinx.coroutines.flow.map 49 import kotlinx.coroutines.flow.stateIn 50 51 /** Encapsulates business logic for interacting with the lock-screen alternate bouncer. */ 52 @SysUISingleton 53 class AlternateBouncerInteractor 54 @Inject 55 constructor( 56 private val statusBarStateController: StatusBarStateController, 57 private val keyguardStateController: KeyguardStateController, 58 private val bouncerRepository: KeyguardBouncerRepository, 59 fingerprintPropertyRepository: FingerprintPropertyRepository, 60 private val biometricSettingsRepository: BiometricSettingsRepository, 61 private val systemClock: SystemClock, 62 private val keyguardUpdateMonitor: KeyguardUpdateMonitor, 63 private val deviceEntryFingerprintAuthInteractor: Lazy<DeviceEntryFingerprintAuthInteractor>, 64 private val keyguardInteractor: Lazy<KeyguardInteractor>, 65 keyguardTransitionInteractor: Lazy<KeyguardTransitionInteractor>, 66 sceneInteractor: Lazy<SceneInteractor>, 67 @Application scope: CoroutineScope, 68 ) { 69 var receivedDownTouch = false 70 val isVisible: Flow<Boolean> = bouncerRepository.alternateBouncerVisible 71 private val alternateBouncerUiAvailableFromSource: HashSet<String> = HashSet() 72 val alternateBouncerSupported: StateFlow<Boolean> = 73 if (DeviceEntryUdfpsRefactor.isEnabled) { 74 fingerprintPropertyRepository.sensorType 75 .map { sensorType -> sensorType.isUdfps() || sensorType.isPowerButton() } 76 .stateIn( 77 scope = scope, 78 started = SharingStarted.Eagerly, 79 initialValue = false, 80 ) 81 } else { 82 bouncerRepository.alternateBouncerUIAvailable 83 } 84 private val isDozingOrAod: Flow<Boolean> = 85 anyOf( 86 keyguardTransitionInteractor.get().transitionValue(KeyguardState.DOZING).map { 87 it > 0f 88 }, 89 keyguardTransitionInteractor.get().transitionValue(KeyguardState.AOD).map { 90 it > 0f 91 }, 92 ) 93 .distinctUntilChanged() 94 95 /** 96 * Whether the current biometric, bouncer, and keyguard states allow the alternate bouncer to 97 * show. 98 */ 99 val canShowAlternateBouncer: StateFlow<Boolean> = 100 alternateBouncerSupported 101 .flatMapLatest { alternateBouncerSupported -> 102 if (alternateBouncerSupported) { 103 combine( 104 keyguardTransitionInteractor.get().currentKeyguardState, 105 if (SceneContainerFlag.isEnabled) { 106 sceneInteractor.get().currentScene 107 } else { 108 flowOf(Scenes.Lockscreen) 109 }, 110 ::Pair 111 ) 112 .flatMapLatest { (currentKeyguardState, transitionState) -> 113 if (currentKeyguardState == KeyguardState.GONE) { 114 flowOf(false) 115 } else if ( 116 SceneContainerFlag.isEnabled && transitionState == Scenes.Gone 117 ) { 118 flowOf(false) 119 } else { 120 combine( 121 deviceEntryFingerprintAuthInteractor 122 .get() 123 .isFingerprintAuthCurrentlyAllowed, 124 keyguardInteractor.get().isKeyguardDismissible, 125 bouncerRepository.primaryBouncerShow, 126 isDozingOrAod 127 ) { 128 fingerprintAllowed, 129 keyguardDismissible, 130 primaryBouncerShowing, 131 dozing -> 132 fingerprintAllowed && 133 !keyguardDismissible && 134 !primaryBouncerShowing && 135 !dozing 136 } 137 } 138 } 139 } else { 140 flowOf(false) 141 } 142 } 143 .stateIn( 144 scope = scope, 145 started = WhileSubscribed(), 146 initialValue = false, 147 ) 148 149 /** 150 * Always shows the alternate bouncer. Requesters must check [canShowAlternateBouncer]` before 151 * calling this. 152 */ 153 fun forceShow() { 154 if (DeviceEntryUdfpsRefactor.isUnexpectedlyInLegacyMode()) { 155 show() 156 return 157 } 158 bouncerRepository.setAlternateVisible(true) 159 } 160 161 /** 162 * Sets the correct bouncer states to show the alternate bouncer if it can show. 163 * 164 * @return whether alternateBouncer is visible 165 * @deprecated use [forceShow] and manually check [canShowAlternateBouncer] beforehand 166 */ 167 fun show(): Boolean { 168 DeviceEntryUdfpsRefactor.assertInLegacyMode() 169 bouncerRepository.setAlternateVisible(canShowAlternateBouncerForFingerprint()) 170 return isVisibleState() 171 } 172 173 /** 174 * Sets the correct bouncer states to hide the bouncer. Should only be called through 175 * StatusBarKeyguardViewManager until ScrimController is refactored to use 176 * alternateBouncerInteractor. 177 * 178 * @return true if the alternate bouncer was newly hidden, else false. 179 */ 180 fun hide(): Boolean { 181 receivedDownTouch = false 182 val wasAlternateBouncerVisible = isVisibleState() 183 bouncerRepository.setAlternateVisible(false) 184 return wasAlternateBouncerVisible && !isVisibleState() 185 } 186 187 fun isVisibleState(): Boolean { 188 return bouncerRepository.alternateBouncerVisible.value 189 } 190 191 fun setAlternateBouncerUIAvailable(isAvailable: Boolean, token: String) { 192 DeviceEntryUdfpsRefactor.assertInLegacyMode() 193 if (isAvailable) { 194 alternateBouncerUiAvailableFromSource.add(token) 195 } else { 196 alternateBouncerUiAvailableFromSource.remove(token) 197 } 198 bouncerRepository.setAlternateBouncerUIAvailable( 199 alternateBouncerUiAvailableFromSource.isNotEmpty() 200 ) 201 } 202 203 fun canShowAlternateBouncerForFingerprint(): Boolean { 204 if (DeviceEntryUdfpsRefactor.isEnabled) { 205 return canShowAlternateBouncer.value 206 } 207 return alternateBouncerSupported.value && 208 biometricSettingsRepository.isFingerprintAuthCurrentlyAllowed.value && 209 !keyguardUpdateMonitor.isFingerprintLockedOut && 210 !keyguardStateController.isUnlocked && 211 !statusBarStateController.isDozing && 212 !bouncerRepository.primaryBouncerShow.value 213 } 214 215 /** 216 * Whether the alt bouncer has shown for a minimum time before allowing touches to dismiss the 217 * alternate bouncer and show the primary bouncer. 218 */ 219 fun hasAlternateBouncerShownWithMinTime(): Boolean { 220 return (systemClock.uptimeMillis() - bouncerRepository.lastAlternateBouncerVisibleTime) > 221 MIN_VISIBILITY_DURATION_UNTIL_TOUCHES_DISMISS_ALTERNATE_BOUNCER_MS 222 } 223 /** 224 * Should only be called through StatusBarKeyguardViewManager which propagates the source of 225 * truth to other concerned controllers. Will hide the alternate bouncer if it's no longer 226 * allowed to show. 227 * 228 * @return true if the alternate bouncer was newly hidden, else false. 229 */ 230 fun maybeHide(): Boolean { 231 if (isVisibleState() && !canShowAlternateBouncerForFingerprint()) { 232 return hide() 233 } 234 return false 235 } 236 237 companion object { 238 private const val MIN_VISIBILITY_DURATION_UNTIL_TOUCHES_DISMISS_ALTERNATE_BOUNCER_MS = 200L 239 } 240 } 241