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