1 /* 2 * 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.bouncer.data.repository.KeyguardBouncerRepository 23 import com.android.systemui.common.coroutine.ChannelExt.trySendWithFailureLogging 24 import com.android.systemui.common.coroutine.ConflatedCallbackFlow.conflatedCallbackFlow 25 import com.android.systemui.common.shared.model.Position 26 import com.android.systemui.common.ui.data.repository.ConfigurationRepository 27 import com.android.systemui.dagger.SysUISingleton 28 import com.android.systemui.flags.FeatureFlags 29 import com.android.systemui.flags.Flags 30 import com.android.systemui.keyguard.data.repository.KeyguardRepository 31 import com.android.systemui.keyguard.shared.model.BiometricUnlockModel 32 import com.android.systemui.keyguard.shared.model.CameraLaunchSourceModel 33 import com.android.systemui.keyguard.shared.model.DozeStateModel 34 import com.android.systemui.keyguard.shared.model.DozeStateModel.Companion.isDozeOff 35 import com.android.systemui.keyguard.shared.model.DozeTransitionModel 36 import com.android.systemui.keyguard.shared.model.KeyguardRootViewVisibilityState 37 import com.android.systemui.keyguard.shared.model.KeyguardState 38 import com.android.systemui.keyguard.shared.model.ScreenModel 39 import com.android.systemui.keyguard.shared.model.StatusBarState 40 import com.android.systemui.keyguard.shared.model.WakefulnessModel 41 import com.android.systemui.statusbar.CommandQueue 42 import com.android.systemui.util.kotlin.sample 43 import kotlinx.coroutines.channels.awaitClose 44 import kotlinx.coroutines.delay 45 import kotlinx.coroutines.flow.Flow 46 import kotlinx.coroutines.flow.MutableStateFlow 47 import kotlinx.coroutines.flow.StateFlow 48 import kotlinx.coroutines.flow.combine 49 import kotlinx.coroutines.flow.distinctUntilChanged 50 import kotlinx.coroutines.flow.filter 51 import kotlinx.coroutines.flow.flatMapLatest 52 import kotlinx.coroutines.flow.flow 53 import kotlinx.coroutines.flow.flowOf 54 import kotlinx.coroutines.flow.merge 55 import kotlinx.coroutines.flow.onStart 56 import javax.inject.Inject 57 58 /** 59 * Encapsulates business-logic related to the keyguard but not to a more specific part within it. 60 */ 61 @SysUISingleton 62 class KeyguardInteractor 63 @Inject 64 constructor( 65 private val repository: KeyguardRepository, 66 private val commandQueue: CommandQueue, 67 featureFlags: FeatureFlags, 68 bouncerRepository: KeyguardBouncerRepository, 69 configurationRepository: ConfigurationRepository, 70 ) { 71 72 data class PreviewMode( 73 val isInPreviewMode: Boolean = false, 74 val shouldHighlightSelectedAffordance: Boolean = false, 75 ) 76 77 /** 78 * Whether this view-model instance is powering the preview experience that renders exclusively 79 * in the wallpaper picker application. This should _always_ be `false` for the real lock screen 80 * experience. 81 */ 82 val previewMode = MutableStateFlow(PreviewMode()) 83 /** 84 * The amount of doze the system is in, where `1.0` is fully dozing and `0.0` is not dozing at 85 * all. 86 */ 87 val dozeAmount: Flow<Float> = repository.linearDozeAmount 88 /** Whether the system is in doze mode. */ 89 val isDozing: Flow<Boolean> = repository.isDozing 90 /** Receive an event for doze time tick */ 91 val dozeTimeTick: Flow<Long> = repository.dozeTimeTick 92 /** Whether Always-on Display mode is available. */ 93 val isAodAvailable: Flow<Boolean> = repository.isAodAvailable 94 /** Doze transition information. */ 95 val dozeTransitionModel: Flow<DozeTransitionModel> = repository.dozeTransitionModel 96 /** 97 * Whether the system is dreaming. [isDreaming] will be always be true when [isDozing] is true, 98 * but not vice-versa. 99 */ 100 val isDreaming: Flow<Boolean> = repository.isDreaming 101 /** Whether the system is dreaming with an overlay active */ 102 val isDreamingWithOverlay: Flow<Boolean> = repository.isDreamingWithOverlay 103 /** Whether the system is dreaming and the active dream is hosted in lockscreen */ 104 val isActiveDreamLockscreenHosted: StateFlow<Boolean> = repository.isActiveDreamLockscreenHosted 105 /** Event for when the camera gesture is detected */ 106 val onCameraLaunchDetected: Flow<CameraLaunchSourceModel> = conflatedCallbackFlow { 107 val callback = 108 object : CommandQueue.Callbacks { 109 override fun onCameraLaunchGestureDetected(source: Int) { 110 trySendWithFailureLogging( 111 cameraLaunchSourceIntToModel(source), 112 TAG, 113 "updated onCameraLaunchGestureDetected" 114 ) 115 } 116 } 117 118 commandQueue.addCallback(callback) 119 120 awaitClose { commandQueue.removeCallback(callback) } 121 } 122 123 /** The device wake/sleep state */ 124 val wakefulnessModel: StateFlow<WakefulnessModel> = repository.wakefulness 125 126 /** The device screen state */ 127 val screenModel: StateFlow<ScreenModel> = repository.screenModel 128 129 /** 130 * Dozing and dreaming have overlapping events. If the doze state remains in FINISH, it means 131 * that doze mode is not running and DREAMING is ok to commence. 132 * 133 * Allow a brief moment to prevent rapidly oscillating between true/false signals. 134 */ 135 val isAbleToDream: Flow<Boolean> = 136 merge(isDreaming, isDreamingWithOverlay) 137 .combine(dozeTransitionModel) { isDreaming, dozeTransitionModel -> 138 isDreaming && isDozeOff(dozeTransitionModel.to) 139 } 140 .sample(wakefulnessModel) { isAbleToDream, wakefulnessModel -> 141 isAbleToDream && wakefulnessModel.isStartingToWakeOrAwake() 142 } 143 .flatMapLatest { isAbleToDream -> 144 flow { 145 delay(50) 146 emit(isAbleToDream) 147 } 148 } 149 .distinctUntilChanged() 150 151 /** Whether the keyguard is showing or not. */ 152 val isKeyguardShowing: Flow<Boolean> = repository.isKeyguardShowing 153 /** Whether the keyguard is unlocked or not. */ 154 val isKeyguardUnlocked: Flow<Boolean> = repository.isKeyguardUnlocked 155 /** Whether the keyguard is occluded (covered by an activity). */ 156 val isKeyguardOccluded: Flow<Boolean> = repository.isKeyguardOccluded 157 /** Whether the keyguard is going away. */ 158 val isKeyguardGoingAway: Flow<Boolean> = repository.isKeyguardGoingAway 159 /** Whether the primary bouncer is showing or not. */ 160 val primaryBouncerShowing: Flow<Boolean> = bouncerRepository.primaryBouncerShow 161 /** Whether the alternate bouncer is showing or not. */ 162 val alternateBouncerShowing: Flow<Boolean> = bouncerRepository.alternateBouncerVisible 163 /** Observable for the [StatusBarState] */ 164 val statusBarState: Flow<StatusBarState> = repository.statusBarState 165 /** 166 * Observable for [BiometricUnlockModel] when biometrics like face or any fingerprint (rear, 167 * side, under display) is used to unlock the device. 168 */ 169 val biometricUnlockState: Flow<BiometricUnlockModel> = repository.biometricUnlockState 170 171 /** Keyguard is present and is not occluded. */ 172 val isKeyguardVisible: Flow<Boolean> = 173 combine(isKeyguardShowing, isKeyguardOccluded) { showing, occluded -> showing && !occluded } 174 175 /** Whether camera is launched over keyguard. */ 176 var isSecureCameraActive = 177 if (featureFlags.isEnabled(Flags.FACE_AUTH_REFACTOR)) { 178 combine( 179 isKeyguardVisible, 180 primaryBouncerShowing, 181 onCameraLaunchDetected, 182 ) { isKeyguardVisible, isPrimaryBouncerShowing, cameraLaunchEvent -> 183 when { 184 isKeyguardVisible -> false 185 isPrimaryBouncerShowing -> false 186 else -> cameraLaunchEvent == CameraLaunchSourceModel.POWER_DOUBLE_TAP 187 } 188 } 189 .onStart { emit(false) } 190 } else { 191 flowOf(false) 192 } 193 194 /** The approximate location on the screen of the fingerprint sensor, if one is available. */ 195 val fingerprintSensorLocation: Flow<Point?> = repository.fingerprintSensorLocation 196 197 /** The approximate location on the screen of the face unlock sensor, if one is available. */ 198 val faceSensorLocation: Flow<Point?> = repository.faceSensorLocation 199 200 /** Notifies when a new configuration is set */ 201 val configurationChange: Flow<Unit> = configurationRepository.onAnyConfigurationChange 202 203 /** Represents the current state of the KeyguardRootView visibility */ 204 val keyguardRootViewVisibilityState: Flow<KeyguardRootViewVisibilityState> = 205 repository.keyguardRootViewVisibility 206 207 /** The position of the keyguard clock. */ 208 val clockPosition: Flow<Position> = repository.clockPosition 209 210 val keyguardAlpha: Flow<Float> = repository.keyguardAlpha 211 212 /** Whether to animate the next doze mode transition. */ 213 val animateDozingTransitions: Flow<Boolean> = repository.animateBottomAreaDozingTransitions 214 215 fun dozeTransitionTo(vararg states: DozeStateModel): Flow<DozeTransitionModel> { 216 return dozeTransitionModel.filter { states.contains(it.to) } 217 } 218 fun isKeyguardShowing(): Boolean { 219 return repository.isKeyguardShowing() 220 } 221 222 private fun cameraLaunchSourceIntToModel(value: Int): CameraLaunchSourceModel { 223 return when (value) { 224 StatusBarManager.CAMERA_LAUNCH_SOURCE_WIGGLE -> CameraLaunchSourceModel.WIGGLE 225 StatusBarManager.CAMERA_LAUNCH_SOURCE_POWER_DOUBLE_TAP -> 226 CameraLaunchSourceModel.POWER_DOUBLE_TAP 227 StatusBarManager.CAMERA_LAUNCH_SOURCE_LIFT_TRIGGER -> 228 CameraLaunchSourceModel.LIFT_TRIGGER 229 StatusBarManager.CAMERA_LAUNCH_SOURCE_QUICK_AFFORDANCE -> 230 CameraLaunchSourceModel.QUICK_AFFORDANCE 231 else -> throw IllegalArgumentException("Invalid CameraLaunchSourceModel value: $value") 232 } 233 } 234 235 fun setIsActiveDreamLockscreenHosted(isLockscreenHosted: Boolean) { 236 repository.setIsActiveDreamLockscreenHosted(isLockscreenHosted) 237 } 238 239 /** Sets whether quick settings or quick-quick settings is visible. */ 240 fun setQuickSettingsVisible(isVisible: Boolean) { 241 repository.setQuickSettingsVisible(isVisible) 242 } 243 244 fun setKeyguardRootVisibility( 245 statusBarState: Int, 246 goingToFullShade: Boolean, 247 isOcclusionTransitionRunning: Boolean 248 ) { 249 repository.setKeyguardVisibility( 250 statusBarState, 251 goingToFullShade, 252 isOcclusionTransitionRunning 253 ) 254 } 255 256 fun setClockPosition(x: Int, y: Int) { 257 repository.setClockPosition(x, y) 258 } 259 260 fun setAlpha(alpha: Float) { 261 repository.setKeyguardAlpha(alpha) 262 } 263 264 fun setAnimateDozingTransitions(animate: Boolean) { 265 repository.setAnimateDozingTransitions(animate) 266 } 267 268 fun isKeyguardDismissable(): Boolean { 269 return repository.isKeyguardUnlocked.value 270 } 271 272 companion object { 273 private const val TAG = "KeyguardInteractor" 274 275 fun isKeyguardVisibleInState(state: KeyguardState): Boolean { 276 return when (state) { 277 KeyguardState.OFF -> true 278 KeyguardState.DOZING -> true 279 KeyguardState.DREAMING -> true 280 KeyguardState.AOD -> true 281 KeyguardState.ALTERNATE_BOUNCER -> true 282 KeyguardState.PRIMARY_BOUNCER -> true 283 KeyguardState.LOCKSCREEN -> true 284 KeyguardState.GONE -> false 285 KeyguardState.OCCLUDED -> true 286 KeyguardState.DREAMING_LOCKSCREEN_HOSTED -> false 287 } 288 } 289 } 290 } 291