• 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 
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