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 18 package com.android.systemui.keyguard.domain.interactor 19 20 import android.app.StatusBarManager 21 import android.graphics.Point 22 import com.android.systemui.common.coroutine.ChannelExt.trySendWithFailureLogging 23 import com.android.systemui.common.coroutine.ConflatedCallbackFlow.conflatedCallbackFlow 24 import com.android.systemui.dagger.SysUISingleton 25 import com.android.systemui.flags.FeatureFlags 26 import com.android.systemui.flags.Flags 27 import com.android.systemui.keyguard.data.repository.KeyguardBouncerRepository 28 import com.android.systemui.keyguard.data.repository.KeyguardRepository 29 import com.android.systemui.keyguard.shared.model.BiometricUnlockModel 30 import com.android.systemui.keyguard.shared.model.CameraLaunchSourceModel 31 import com.android.systemui.keyguard.shared.model.DozeStateModel 32 import com.android.systemui.keyguard.shared.model.DozeStateModel.Companion.isDozeOff 33 import com.android.systemui.keyguard.shared.model.DozeTransitionModel 34 import com.android.systemui.keyguard.shared.model.StatusBarState 35 import com.android.systemui.keyguard.shared.model.WakefulnessModel 36 import com.android.systemui.keyguard.shared.model.WakefulnessModel.Companion.isWakingOrStartingToWake 37 import com.android.systemui.statusbar.CommandQueue 38 import com.android.systemui.util.kotlin.sample 39 import javax.inject.Inject 40 import kotlinx.coroutines.channels.awaitClose 41 import kotlinx.coroutines.delay 42 import kotlinx.coroutines.flow.Flow 43 import kotlinx.coroutines.flow.combine 44 import kotlinx.coroutines.flow.distinctUntilChanged 45 import kotlinx.coroutines.flow.filter 46 import kotlinx.coroutines.flow.flatMapLatest 47 import kotlinx.coroutines.flow.flow 48 import kotlinx.coroutines.flow.flowOf 49 import kotlinx.coroutines.flow.merge 50 import kotlinx.coroutines.flow.onStart 51 52 /** 53 * Encapsulates business-logic related to the keyguard but not to a more specific part within it. 54 */ 55 @SysUISingleton 56 class KeyguardInteractor 57 @Inject 58 constructor( 59 private val repository: KeyguardRepository, 60 private val commandQueue: CommandQueue, 61 featureFlags: FeatureFlags, 62 bouncerRepository: KeyguardBouncerRepository, 63 ) { 64 /** 65 * The amount of doze the system is in, where `1.0` is fully dozing and `0.0` is not dozing at 66 * all. 67 */ 68 val dozeAmount: Flow<Float> = repository.linearDozeAmount 69 /** Whether the system is in doze mode. */ 70 val isDozing: Flow<Boolean> = repository.isDozing 71 /** Whether Always-on Display mode is available. */ 72 val isAodAvailable: Flow<Boolean> = repository.isAodAvailable 73 /** Doze transition information. */ 74 val dozeTransitionModel: Flow<DozeTransitionModel> = repository.dozeTransitionModel 75 /** 76 * Whether the system is dreaming. [isDreaming] will be always be true when [isDozing] is true, 77 * but not vice-versa. 78 */ 79 val isDreaming: Flow<Boolean> = repository.isDreaming 80 /** Whether the system is dreaming with an overlay active */ 81 val isDreamingWithOverlay: Flow<Boolean> = repository.isDreamingWithOverlay 82 /** Event for when the camera gesture is detected */ 83 val onCameraLaunchDetected: Flow<CameraLaunchSourceModel> = conflatedCallbackFlow { 84 val callback = 85 object : CommandQueue.Callbacks { 86 override fun onCameraLaunchGestureDetected(source: Int) { 87 trySendWithFailureLogging( 88 cameraLaunchSourceIntToModel(source), 89 TAG, 90 "updated onCameraLaunchGestureDetected" 91 ) 92 } 93 } 94 95 commandQueue.addCallback(callback) 96 97 awaitClose { commandQueue.removeCallback(callback) } 98 } 99 100 /** The device wake/sleep state */ 101 val wakefulnessModel: Flow<WakefulnessModel> = repository.wakefulness 102 103 /** 104 * Dozing and dreaming have overlapping events. If the doze state remains in FINISH, it means 105 * that doze mode is not running and DREAMING is ok to commence. 106 * 107 * Allow a brief moment to prevent rapidly oscillating between true/false signals. 108 */ 109 val isAbleToDream: Flow<Boolean> = 110 merge(isDreaming, isDreamingWithOverlay) 111 .combine( 112 dozeTransitionModel, 113 { isDreaming, dozeTransitionModel -> 114 isDreaming && isDozeOff(dozeTransitionModel.to) 115 } 116 ) 117 .sample( 118 wakefulnessModel, 119 { isAbleToDream, wakefulnessModel -> 120 isAbleToDream && isWakingOrStartingToWake(wakefulnessModel) 121 } 122 ) 123 .flatMapLatest { isAbleToDream -> 124 flow { 125 delay(50) 126 emit(isAbleToDream) 127 } 128 } 129 .distinctUntilChanged() 130 131 /** Whether the keyguard is showing or not. */ 132 val isKeyguardShowing: Flow<Boolean> = repository.isKeyguardShowing 133 /** Whether the keyguard is unlocked or not. */ 134 val isKeyguardUnlocked: Flow<Boolean> = repository.isKeyguardUnlocked 135 /** Whether the keyguard is occluded (covered by an activity). */ 136 val isKeyguardOccluded: Flow<Boolean> = repository.isKeyguardOccluded 137 /** Whether the keyguard is going away. */ 138 val isKeyguardGoingAway: Flow<Boolean> = repository.isKeyguardGoingAway 139 /** Whether the primary bouncer is showing or not. */ 140 val primaryBouncerShowing: Flow<Boolean> = bouncerRepository.primaryBouncerShow 141 /** Whether the alternate bouncer is showing or not. */ 142 val alternateBouncerShowing: Flow<Boolean> = bouncerRepository.alternateBouncerVisible 143 /** Observable for the [StatusBarState] */ 144 val statusBarState: Flow<StatusBarState> = repository.statusBarState 145 /** Whether or not quick settings or quick quick settings are showing. */ 146 val isQuickSettingsVisible: Flow<Boolean> = repository.isQuickSettingsVisible 147 /** 148 * Observable for [BiometricUnlockModel] when biometrics like face or any fingerprint (rear, 149 * side, under display) is used to unlock the device. 150 */ 151 val biometricUnlockState: Flow<BiometricUnlockModel> = repository.biometricUnlockState 152 153 /** Keyguard is present and is not occluded. */ 154 val isKeyguardVisible: Flow<Boolean> = 155 combine(isKeyguardShowing, isKeyguardOccluded) { showing, occluded -> showing && !occluded } 156 157 /** Whether camera is launched over keyguard. */ 158 var isSecureCameraActive = 159 if (featureFlags.isEnabled(Flags.FACE_AUTH_REFACTOR)) { 160 combine( 161 isKeyguardVisible, 162 primaryBouncerShowing, 163 onCameraLaunchDetected, 164 ) { isKeyguardVisible, isPrimaryBouncerShowing, cameraLaunchEvent -> 165 when { 166 isKeyguardVisible -> false 167 isPrimaryBouncerShowing -> false 168 else -> cameraLaunchEvent == CameraLaunchSourceModel.POWER_DOUBLE_TAP 169 } 170 } 171 .onStart { emit(false) } 172 } else { 173 flowOf(false) 174 } 175 176 /** The approximate location on the screen of the fingerprint sensor, if one is available. */ 177 val fingerprintSensorLocation: Flow<Point?> = repository.fingerprintSensorLocation 178 179 /** The approximate location on the screen of the face unlock sensor, if one is available. */ 180 val faceSensorLocation: Flow<Point?> = repository.faceSensorLocation 181 182 fun dozeTransitionTo(vararg states: DozeStateModel): Flow<DozeTransitionModel> { 183 return dozeTransitionModel.filter { states.contains(it.to) } 184 } 185 fun isKeyguardShowing(): Boolean { 186 return repository.isKeyguardShowing() 187 } 188 189 private fun cameraLaunchSourceIntToModel(value: Int): CameraLaunchSourceModel { 190 return when (value) { 191 StatusBarManager.CAMERA_LAUNCH_SOURCE_WIGGLE -> CameraLaunchSourceModel.WIGGLE 192 StatusBarManager.CAMERA_LAUNCH_SOURCE_POWER_DOUBLE_TAP -> 193 CameraLaunchSourceModel.POWER_DOUBLE_TAP 194 StatusBarManager.CAMERA_LAUNCH_SOURCE_LIFT_TRIGGER -> 195 CameraLaunchSourceModel.LIFT_TRIGGER 196 StatusBarManager.CAMERA_LAUNCH_SOURCE_QUICK_AFFORDANCE -> 197 CameraLaunchSourceModel.QUICK_AFFORDANCE 198 else -> throw IllegalArgumentException("Invalid CameraLaunchSourceModel value: $value") 199 } 200 } 201 202 /** Sets whether quick settings or quick-quick settings is visible. */ 203 fun setQuickSettingsVisible(isVisible: Boolean) { 204 repository.setQuickSettingsVisible(isVisible) 205 } 206 207 companion object { 208 private const val TAG = "KeyguardInteractor" 209 } 210 } 211