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