1 /*
<lambda>null2  * Copyright (C) 2024 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.keyguard.domain.interactor
18 
19 import android.app.ActivityManager.RunningTaskInfo
20 import com.android.systemui.dagger.SysUISingleton
21 import com.android.systemui.dagger.qualifiers.Application
22 import com.android.systemui.deviceentry.domain.interactor.DeviceUnlockedInteractor
23 import com.android.systemui.keyguard.data.repository.KeyguardOcclusionRepository
24 import com.android.systemui.keyguard.shared.model.KeyguardState
25 import com.android.systemui.power.domain.interactor.PowerInteractor
26 import com.android.systemui.scene.shared.flag.SceneContainerFlag
27 import com.android.systemui.util.kotlin.sample
28 import dagger.Lazy
29 import javax.inject.Inject
30 import kotlinx.coroutines.CoroutineScope
31 import kotlinx.coroutines.flow.SharingStarted
32 import kotlinx.coroutines.flow.StateFlow
33 import kotlinx.coroutines.flow.asStateFlow
34 import kotlinx.coroutines.flow.filter
35 import kotlinx.coroutines.flow.map
36 import kotlinx.coroutines.flow.merge
37 import kotlinx.coroutines.flow.stateIn
38 
39 /**
40  * Logic related to keyguard occlusion. The keyguard is occluded when an activity with
41  * FLAG_SHOW_WHEN_LOCKED is on top of the activity task stack, with that activity displaying on top
42  * of ("occluding") the lockscreen UI. Common examples of this are Google Maps Navigation and the
43  * secure camera.
44  *
45  * This should usually be used only by keyguard internal classes. Most System UI use cases should
46  * use [KeyguardTransitionInteractor] to see if we're in [KeyguardState.OCCLUDED] instead.
47  */
48 @SysUISingleton
49 class KeyguardOcclusionInteractor
50 @Inject
51 constructor(
52     @Application applicationScope: CoroutineScope,
53     private val repository: KeyguardOcclusionRepository,
54     private val powerInteractor: PowerInteractor,
55     private val transitionInteractor: KeyguardTransitionInteractor,
56     keyguardInteractor: KeyguardInteractor,
57     deviceUnlockedInteractor: Lazy<DeviceUnlockedInteractor>,
58 ) {
59     val showWhenLockedActivityInfo = repository.showWhenLockedActivityInfo.asStateFlow()
60 
61     /**
62      * Whether a SHOW_WHEN_LOCKED activity is on top of the task stack. This does not necessarily
63      * mean we're OCCLUDED, as we could be GONE (unlocked), with an activity that can (but is not
64      * currently) displaying over the lockscreen.
65      *
66      * Transition interactors use this to determine when we should transition to the OCCLUDED state.
67      *
68      * Outside of the transition/occlusion interactors, you almost certainly don't want to use this.
69      * Instead, use KeyguardTransitionInteractor to figure out if we're in KeyguardState.OCCLUDED.
70      */
71     val isShowWhenLockedActivityOnTop = showWhenLockedActivityInfo.map { it.isOnTop }
72 
73     /** Whether we should start a transition due to the power button launch gesture. */
74     fun shouldTransitionFromPowerButtonGesture(): Boolean {
75         // powerButtonLaunchGestureTriggered remains true while we're awake from a power button
76         // gesture. Check that we were asleep or transitioning to asleep before starting a
77         // transition, to ensure we don't transition while moving between, for example,
78         // *_BOUNCER -> LOCKSCREEN.
79         return powerInteractor.detailedWakefulness.value.powerButtonLaunchGestureTriggered &&
80             KeyguardState.deviceIsAsleepInState(
81                 transitionInteractor.currentTransitionInfoInternal.value.to
82             )
83     }
84 
85     /**
86      * Whether the SHOW_WHEN_LOCKED activity was launched from the double tap power button gesture.
87      * This remains true while the activity is running and emits false once it is killed.
88      */
89     val showWhenLockedActivityLaunchedFromPowerGesture =
90         merge(
91                 // Emit true when the power launch gesture is triggered, since this means a
92                 // SHOW_WHEN_LOCKED activity will be launched from the gesture (unless we're
93                 // currently
94                 // GONE, in which case we're going back to GONE and launching the insecure camera).
95                 powerInteractor.detailedWakefulness
96                     .sample(transitionInteractor.currentKeyguardState, ::Pair)
97                     .map { (wakefulness, currentKeyguardState) ->
98                         wakefulness.powerButtonLaunchGestureTriggered &&
99                             currentKeyguardState != KeyguardState.GONE
100                     },
101                 // Emit false once that activity goes away.
102                 isShowWhenLockedActivityOnTop.filter { !it }.map { false }
103             )
104             .stateIn(applicationScope, SharingStarted.Eagerly, false)
105 
106     /**
107      * Whether launching an occluding activity will automatically dismiss keyguard. This happens if
108      * the keyguard is dismissable.
109      */
110     val occludingActivityWillDismissKeyguard: StateFlow<Boolean> =
111         if (SceneContainerFlag.isEnabled) {
112                 deviceUnlockedInteractor.get().deviceUnlockStatus.map { it.isUnlocked }
113             } else {
114                 keyguardInteractor.isKeyguardDismissible
115             }
116             .stateIn(scope = applicationScope, SharingStarted.Eagerly, false)
117 
118     /**
119      * Called to let System UI know that WM says a SHOW_WHEN_LOCKED activity is on top (or no longer
120      * on top).
121      *
122      * This signal arrives from WM when a SHOW_WHEN_LOCKED activity is started or killed - it is
123      * never set directly by System UI. While we might be the reason the activity was started
124      * (launching the camera from the power button gesture), we ultimately only receive this signal
125      * once that activity starts. It's up to us to start the appropriate keyguard transitions,
126      * because that activity is going to be visible (or not) regardless.
127      */
128     fun setWmNotifiedShowWhenLockedActivityOnTop(
129         showWhenLockedActivityOnTop: Boolean,
130         taskInfo: RunningTaskInfo? = null
131     ) {
132         repository.setShowWhenLockedActivityInfo(showWhenLockedActivityOnTop, taskInfo)
133     }
134 }
135