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