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