1 /*
2  * Copyright (C) 2023 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 com.android.systemui.dagger.SysUISingleton
21 import com.android.systemui.dagger.qualifiers.Application
22 import com.android.systemui.keyguard.data.repository.KeyguardRepository
23 import com.android.systemui.keyguard.shared.model.DismissAction
24 import com.android.systemui.keyguard.shared.model.KeyguardDone
25 import com.android.systemui.keyguard.shared.model.KeyguardState.ALTERNATE_BOUNCER
26 import com.android.systemui.keyguard.shared.model.KeyguardState.GONE
27 import com.android.systemui.keyguard.shared.model.KeyguardState.PRIMARY_BOUNCER
28 import com.android.systemui.scene.domain.interactor.SceneInteractor
29 import com.android.systemui.scene.shared.flag.SceneContainerFlag
30 import com.android.systemui.scene.shared.model.Scenes
31 import com.android.systemui.util.kotlin.sample
32 import javax.inject.Inject
33 import kotlinx.coroutines.CoroutineScope
34 import kotlinx.coroutines.ExperimentalCoroutinesApi
35 import kotlinx.coroutines.flow.Flow
36 import kotlinx.coroutines.flow.SharingStarted
37 import kotlinx.coroutines.flow.StateFlow
38 import kotlinx.coroutines.flow.filter
39 import kotlinx.coroutines.flow.filterNot
40 import kotlinx.coroutines.flow.map
41 import kotlinx.coroutines.flow.merge
42 import kotlinx.coroutines.flow.stateIn
43 
44 /** Encapsulates business-logic for actions to run when the keyguard is dismissed. */
45 @ExperimentalCoroutinesApi
46 @SysUISingleton
47 class KeyguardDismissActionInteractor
48 @Inject
49 constructor(
50     private val repository: KeyguardRepository,
51     transitionInteractor: KeyguardTransitionInteractor,
52     val dismissInteractor: KeyguardDismissInteractor,
53     @Application private val applicationScope: CoroutineScope,
54     sceneInteractor: SceneInteractor,
55 ) {
56     val dismissAction: Flow<DismissAction> = repository.dismissAction
57 
<lambda>null58     val onCancel: Flow<Runnable> = dismissAction.map { it.onCancelAction }
59 
60     // TODO (b/268240415): use message in alt + primary bouncer message
61     // message to show to the user about the dismiss action, else empty string
<lambda>null62     val message = dismissAction.map { it.message }
63 
64     /**
65      * True if the dismiss action will run an animation on the lockscreen and requires any views
66      * that would obscure this animation (ie: the primary bouncer) to immediately hide, so the
67      * animation would be visible.
68      */
69     val willAnimateDismissActionOnLockscreen: StateFlow<Boolean> =
70         dismissAction
<lambda>null71             .map { it.willAnimateOnLockscreen }
72             .stateIn(
73                 scope = applicationScope,
74                 started = SharingStarted.WhileSubscribed(),
75                 initialValue = false,
76             )
77 
78     private val finishedTransitionToGone: Flow<Unit> =
79         if (SceneContainerFlag.isEnabled) {
<lambda>null80             sceneInteractor.transitionState.filter { it.isIdle(Scenes.Gone) }.map {}
81         } else {
<lambda>null82             transitionInteractor.finishedKeyguardState.filter { it == GONE }.map {}
83         }
84 
85     val executeDismissAction: Flow<() -> KeyguardDone> =
86         merge(
87                 finishedTransitionToGone,
88                 dismissInteractor.dismissKeyguardRequestWithImmediateDismissAction
89             )
90             .sample(dismissAction)
<lambda>null91             .filterNot { it is DismissAction.None }
<lambda>null92             .map { it.onDismissAction }
93     val resetDismissAction: Flow<Unit> =
94         transitionInteractor.finishedKeyguardTransitionStep
<lambda>null95             .filter { it.to != ALTERNATE_BOUNCER && it.to != PRIMARY_BOUNCER && it.to != GONE }
96             .sample(dismissAction)
<lambda>null97             .filterNot { it is DismissAction.None }
<lambda>null98             .map {} // map to Unit
99 
runDismissAnimationOnKeyguardnull100     fun runDismissAnimationOnKeyguard(): Boolean {
101         return willAnimateDismissActionOnLockscreen.value
102     }
103 
runAfterKeyguardGonenull104     fun runAfterKeyguardGone(runnable: Runnable) {
105         setDismissAction(
106             DismissAction.RunAfterKeyguardGone(
107                 dismissAction = { runnable.run() },
108                 onCancelAction = {},
109                 message = "",
110                 willAnimateOnLockscreen = false,
111             )
112         )
113     }
114 
setDismissActionnull115     fun setDismissAction(dismissAction: DismissAction) {
116         repository.dismissAction.value.onCancelAction.run()
117         repository.setDismissAction(dismissAction)
118     }
119 
handleDismissActionnull120     fun handleDismissAction() {
121         repository.setDismissAction(DismissAction.None)
122     }
123 
setKeyguardDonenull124     suspend fun setKeyguardDone(keyguardDoneTiming: KeyguardDone) {
125         dismissInteractor.setKeyguardDone(keyguardDoneTiming)
126     }
127 }
128