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