• 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.shade.ui.viewmodel
18 
19 import com.android.systemui.authentication.domain.interactor.AuthenticationInteractor
20 import com.android.systemui.authentication.shared.model.AuthenticationMethodModel
21 import com.android.systemui.bouncer.domain.interactor.PrimaryBouncerInteractor
22 import com.android.systemui.bouncer.shared.flag.ComposeBouncerFlags
23 import com.android.systemui.dagger.SysUISingleton
24 import com.android.systemui.keyguard.domain.interactor.KeyguardTransitionInteractor
25 import com.android.systemui.keyguard.shared.model.Edge
26 import com.android.systemui.keyguard.shared.model.KeyguardState.DREAMING
27 import com.android.systemui.keyguard.shared.model.KeyguardState.GLANCEABLE_HUB
28 import com.android.systemui.keyguard.shared.model.KeyguardState.OCCLUDED
29 import com.android.systemui.scene.domain.interactor.SceneInteractor
30 import com.android.systemui.scene.shared.flag.SceneContainerFlag
31 import com.android.systemui.scene.shared.model.Overlays
32 import com.android.systemui.scene.shared.model.Scenes
33 import com.android.systemui.util.kotlin.BooleanFlowOperators.any
34 import com.android.systemui.util.kotlin.sample
35 import javax.inject.Inject
36 import kotlinx.coroutines.flow.Flow
37 import kotlinx.coroutines.flow.distinctUntilChanged
38 import kotlinx.coroutines.flow.flow
39 import kotlinx.coroutines.flow.map
40 
41 /** Models UI state for the shade window. */
42 @SysUISingleton
43 class NotificationShadeWindowModel
44 @Inject
45 constructor(
46     keyguardTransitionInteractor: KeyguardTransitionInteractor,
47     sceneInteractor: dagger.Lazy<SceneInteractor>,
48     authenticationInteractor: dagger.Lazy<AuthenticationInteractor>,
49     primaryBouncerInteractor: PrimaryBouncerInteractor,
50 ) {
51     /**
52      * Considered to be occluded if in OCCLUDED, DREAMING, GLANCEABLE_HUB/Communal, or transitioning
53      * between those states. Every permutation is listed so we can use optimal flows and support
54      * Scenes.
55      */
56     val isKeyguardOccluded: Flow<Boolean> =
57         listOf(
58                 // Finished in state...
59                 keyguardTransitionInteractor.transitionValue(OCCLUDED).map { it == 1f },
60                 keyguardTransitionInteractor.transitionValue(DREAMING).map { it == 1f },
61                 keyguardTransitionInteractor.transitionValue(Scenes.Communal, GLANCEABLE_HUB).map {
62                     it == 1f
63                 },
64 
65                 // ... or transitions between those states
66                 keyguardTransitionInteractor.isInTransition(Edge.create(OCCLUDED, DREAMING)),
67                 keyguardTransitionInteractor.isInTransition(Edge.create(DREAMING, OCCLUDED)),
68                 keyguardTransitionInteractor.isInTransition(
69                     edge = Edge.create(from = OCCLUDED, to = Scenes.Communal),
70                     edgeWithoutSceneContainer = Edge.create(from = OCCLUDED, to = GLANCEABLE_HUB),
71                 ),
72                 keyguardTransitionInteractor.isInTransition(
73                     edge = Edge.create(from = Scenes.Communal, to = OCCLUDED),
74                     edgeWithoutSceneContainer = Edge.create(from = GLANCEABLE_HUB, to = OCCLUDED),
75                 ),
76                 keyguardTransitionInteractor.isInTransition(
77                     edge = Edge.create(from = DREAMING, to = Scenes.Communal),
78                     edgeWithoutSceneContainer = Edge.create(from = DREAMING, to = GLANCEABLE_HUB),
79                 ),
80                 keyguardTransitionInteractor.isInTransition(
81                     edge = Edge.create(from = Scenes.Communal, to = DREAMING),
82                     edgeWithoutSceneContainer = Edge.create(from = GLANCEABLE_HUB, to = DREAMING),
83                 ),
84             )
85             .any()
86 
87     /**
88      * Whether bouncer is currently showing or not.
89      *
90      * Applicable only when either [SceneContainerFlag] or [ComposeBouncerFlags] are enabled,
91      * otherwise it throws an error.
92      */
93     val isBouncerShowing: Flow<Boolean> =
94         when {
95             SceneContainerFlag.isEnabled -> {
96                 sceneInteractor.get().transitionState.map { it.isIdle(Overlays.Bouncer) }
97             }
98             ComposeBouncerFlags.isOnlyComposeBouncerEnabled() -> primaryBouncerInteractor.isShowing
99             else ->
100                 flow {
101                     error(
102                         "Consume this flow only when SceneContainerFlag " +
103                             "or ComposeBouncerFlags are enabled"
104                     )
105                 }
106         }.distinctUntilChanged()
107 
108     /**
109      * Whether the bouncer currently require IME for device entry.
110      *
111      * This emits true when the authentication method is set to password and the bouncer is
112      * currently showing. Throws an error when this is used without either [SceneContainerFlag] or
113      * [ComposeBouncerFlags]
114      */
115     val doesBouncerRequireIme: Flow<Boolean> =
116         if (ComposeBouncerFlags.isComposeBouncerOrSceneContainerEnabled()) {
117                 // This is required to make the window, where the bouncer resides,
118                 // focusable. InputMethodManager allows IME to be shown only for views
119                 // in windows that do not have the FLAG_NOT_FOCUSABLE flag.
120 
121                 isBouncerShowing
122                     .sample(authenticationInteractor.get().authenticationMethod, ::Pair)
123                     .map { (showing, authMethod) ->
124                         showing && authMethod == AuthenticationMethodModel.Password
125                     }
126             } else {
127                 flow {
128                     error(
129                         "Consume this flow only when SceneContainerFlag " +
130                             "or ComposeBouncerFlags are enabled"
131                     )
132                 }
133             }
134             .distinctUntilChanged()
135 }
136