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