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.communal.ui.viewmodel 18 19 import android.graphics.Color 20 import com.android.systemui.communal.domain.interactor.CommunalInteractor 21 import com.android.systemui.communal.domain.interactor.CommunalSceneInteractor 22 import com.android.systemui.communal.domain.interactor.CommunalSettingsInteractor 23 import com.android.systemui.communal.shared.model.CommunalScenes 24 import com.android.systemui.communal.util.CommunalColors 25 import com.android.systemui.dagger.SysUISingleton 26 import com.android.systemui.dagger.qualifiers.Application 27 import com.android.systemui.keyguard.domain.interactor.KeyguardTransitionInteractor 28 import com.android.systemui.keyguard.shared.model.Edge 29 import com.android.systemui.keyguard.shared.model.KeyguardState 30 import com.android.systemui.keyguard.shared.model.TransitionState 31 import com.android.systemui.keyguard.ui.viewmodel.DreamingToGlanceableHubTransitionViewModel 32 import com.android.systemui.keyguard.ui.viewmodel.GlanceableHubToDreamingTransitionViewModel 33 import com.android.systemui.keyguard.ui.viewmodel.GlanceableHubToLockscreenTransitionViewModel 34 import com.android.systemui.keyguard.ui.viewmodel.LockscreenToGlanceableHubTransitionViewModel 35 import com.android.systemui.scene.shared.flag.SceneContainerFlag 36 import com.android.systemui.scene.shared.model.Scenes 37 import com.android.systemui.util.kotlin.BooleanFlowOperators.allOf 38 import com.android.systemui.util.kotlin.BooleanFlowOperators.anyOf 39 import javax.inject.Inject 40 import kotlinx.coroutines.CoroutineScope 41 import kotlinx.coroutines.flow.Flow 42 import kotlinx.coroutines.flow.SharingStarted 43 import kotlinx.coroutines.flow.combine 44 import kotlinx.coroutines.flow.distinctUntilChanged 45 import kotlinx.coroutines.flow.filter 46 import kotlinx.coroutines.flow.flowOf 47 import kotlinx.coroutines.flow.map 48 import kotlinx.coroutines.flow.merge 49 import kotlinx.coroutines.flow.onStart 50 import kotlinx.coroutines.flow.stateIn 51 52 /** View model for transitions related to the communal hub. */ 53 @SysUISingleton 54 class CommunalTransitionViewModel 55 @Inject 56 constructor( 57 @Application applicationScope: CoroutineScope, 58 communalColors: CommunalColors, 59 glanceableHubToLockscreenTransitionViewModel: GlanceableHubToLockscreenTransitionViewModel, 60 lockscreenToGlanceableHubTransitionViewModel: LockscreenToGlanceableHubTransitionViewModel, 61 dreamToGlanceableHubTransitionViewModel: DreamingToGlanceableHubTransitionViewModel, 62 glanceableHubToDreamTransitionViewModel: GlanceableHubToDreamingTransitionViewModel, 63 communalInteractor: CommunalInteractor, 64 private val communalSceneInteractor: CommunalSceneInteractor, 65 communalSettingsInteractor: CommunalSettingsInteractor, 66 keyguardTransitionInteractor: KeyguardTransitionInteractor, 67 ) { 68 /** 69 * Snaps to [CommunalScenes.Communal], showing the glanceable hub immediately without any 70 * transition. 71 */ 72 fun snapToCommunal() { 73 communalSceneInteractor.snapToScene( 74 newScene = CommunalScenes.Communal, 75 loggingReason = "transition view model", 76 ) 77 } 78 79 // Show UMO on glanceable hub immediately on transition into glanceable hub 80 private val showUmoFromOccludedToGlanceableHub: Flow<Boolean> = 81 keyguardTransitionInteractor 82 .transition( 83 Edge.create(from = KeyguardState.OCCLUDED, to = KeyguardState.GLANCEABLE_HUB) 84 ) 85 .filter { 86 (it.transitionState == TransitionState.STARTED || 87 it.transitionState == TransitionState.CANCELED) 88 } 89 .map { it.transitionState == TransitionState.STARTED } 90 91 private val showUmoFromGlanceableHubToOccluded: Flow<Boolean> = 92 keyguardTransitionInteractor 93 .transition( 94 edge = Edge.create(from = Scenes.Communal), 95 edgeWithoutSceneContainer = Edge.create(from = KeyguardState.GLANCEABLE_HUB), 96 ) 97 .filter { 98 it.to == KeyguardState.OCCLUDED && 99 (it.transitionState == TransitionState.FINISHED || 100 it.transitionState == TransitionState.CANCELED) 101 } 102 .map { it.transitionState != TransitionState.FINISHED } 103 104 /** 105 * Whether UMO location should be on communal. This flow is responsive to transitions so that a 106 * new value is emitted at the right step of a transition to/from communal hub that the location 107 * of UMO should be updated. 108 */ 109 val isUmoOnCommunal: Flow<Boolean> = 110 anyOf( 111 communalSceneInteractor.isIdleOnCommunal, 112 allOf( 113 // Only show UMO on the hub if the hub is at least partially visible. This 114 // prevents 115 // the UMO from being missing on the lock screen when going from the hub to lock 116 // screen in some way other than through a direct transition, such as unlocking 117 // from 118 // the hub, then pressing power twice to go back to the lock screen. 119 communalSceneInteractor.isCommunalVisible, 120 // TODO(b/378942852): polish UMO transitions when scene container is enabled 121 if (SceneContainerFlag.isEnabled) flowOf(true) 122 else 123 merge( 124 lockscreenToGlanceableHubTransitionViewModel.showUmo, 125 glanceableHubToLockscreenTransitionViewModel.showUmo, 126 dreamToGlanceableHubTransitionViewModel.showUmo, 127 glanceableHubToDreamTransitionViewModel.showUmo, 128 showUmoFromOccludedToGlanceableHub, 129 showUmoFromGlanceableHubToOccluded, 130 ) 131 .onStart { emit(false) }, 132 ), 133 ) 134 .stateIn( 135 scope = applicationScope, 136 started = SharingStarted.WhileSubscribed(), 137 initialValue = false, 138 ) 139 140 /** Whether to show communal when exiting the occluded state. */ 141 val showCommunalFromOccluded: Flow<Boolean> = communalInteractor.showCommunalFromOccluded 142 143 val transitionFromOccludedEnded = 144 keyguardTransitionInteractor 145 .transition(Edge.create(from = KeyguardState.OCCLUDED)) 146 .filter { step -> 147 step.transitionState == TransitionState.FINISHED || 148 step.transitionState == TransitionState.CANCELED 149 } 150 151 val recentsBackgroundColor: Flow<Color?> = 152 combine( 153 showCommunalFromOccluded, 154 communalColors.backgroundColor, 155 communalSettingsInteractor.communalBackground, 156 ) { showCommunalFromOccluded, backgroundColor, backgroundType -> 157 if (showCommunalFromOccluded && backgroundType.opaque) { 158 backgroundColor 159 } else { 160 null 161 } 162 } 163 .distinctUntilChanged() 164 } 165