1 /*
2  * Copyright (C) 2023 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.keyguard.ui.viewmodel
18 
19 import com.android.systemui.bouncer.domain.interactor.PrimaryBouncerInteractor
20 import com.android.systemui.dagger.SysUISingleton
21 import com.android.systemui.keyguard.domain.interactor.FromPrimaryBouncerTransitionInteractor.Companion.TO_GONE_DURATION
22 import com.android.systemui.keyguard.domain.interactor.KeyguardDismissActionInteractor
23 import com.android.systemui.keyguard.shared.model.Edge
24 import com.android.systemui.keyguard.shared.model.KeyguardState.GONE
25 import com.android.systemui.keyguard.shared.model.KeyguardState.PRIMARY_BOUNCER
26 import com.android.systemui.keyguard.shared.model.ScrimAlpha
27 import com.android.systemui.keyguard.ui.KeyguardTransitionAnimationFlow
28 import com.android.systemui.scene.shared.flag.SceneContainerFlag
29 import com.android.systemui.scene.shared.model.Scenes
30 import com.android.systemui.statusbar.SysuiStatusBarStateController
31 import dagger.Lazy
32 import javax.inject.Inject
33 import kotlin.time.Duration.Companion.milliseconds
34 import kotlinx.coroutines.ExperimentalCoroutinesApi
35 import kotlinx.coroutines.flow.Flow
36 import kotlinx.coroutines.flow.flatMapLatest
37 
38 /**
39  * Breaks down PRIMARY_BOUNCER->GONE transition into discrete steps for corresponding views to
40  * consume.
41  */
42 @OptIn(ExperimentalCoroutinesApi::class)
43 @SysUISingleton
44 class PrimaryBouncerToGoneTransitionViewModel
45 @Inject
46 constructor(
47     private val statusBarStateController: SysuiStatusBarStateController,
48     private val primaryBouncerInteractor: PrimaryBouncerInteractor,
49     keyguardDismissActionInteractor: Lazy<KeyguardDismissActionInteractor>,
50     bouncerToGoneFlows: BouncerToGoneFlows,
51     animationFlow: KeyguardTransitionAnimationFlow,
52 ) {
53     private val transitionAnimation =
54         animationFlow
55             .setup(
56                 duration = TO_GONE_DURATION,
57                 edge = Edge.create(from = PRIMARY_BOUNCER, to = Scenes.Gone),
58             )
59             .setupWithoutSceneContainer(
60                 edge = Edge.create(from = PRIMARY_BOUNCER, to = GONE),
61             )
62 
63     private var leaveShadeOpen: Boolean = false
64     private var willRunDismissFromKeyguard: Boolean = false
65 
66     /** See [BouncerToGoneFlows#showAllNotifications] */
67     val showAllNotifications: Flow<Boolean> =
68         bouncerToGoneFlows.showAllNotifications(TO_GONE_DURATION, PRIMARY_BOUNCER)
69 
70     val notificationAlpha: Flow<Float> =
71         transitionAnimation.sharedFlow(
72             duration = 200.milliseconds,
<lambda>null73             onStart = {
74                 leaveShadeOpen = statusBarStateController.leaveOpenOnKeyguardHide()
75                 willRunDismissFromKeyguard = primaryBouncerInteractor.willRunDismissFromKeyguard()
76             },
<lambda>null77             onStep = {
78                 if (willRunDismissFromKeyguard || leaveShadeOpen) {
79                     1f
80                 } else {
81                     1f - it
82                 }
83             },
84         )
85 
86     /** Bouncer container alpha */
87     val bouncerAlpha: Flow<Float> =
88         if (SceneContainerFlag.isEnabled) {
89             keyguardDismissActionInteractor
90                 .get()
91                 .willAnimateDismissActionOnLockscreen
<lambda>null92                 .flatMapLatest { createBouncerAlphaFlow { it } }
93         } else {
94             createBouncerAlphaFlow(primaryBouncerInteractor::willRunDismissFromKeyguard)
95         }
96 
createBouncerAlphaFlownull97     private fun createBouncerAlphaFlow(willRunAnimationOnKeyguard: () -> Boolean): Flow<Float> {
98         return transitionAnimation.sharedFlow(
99             duration = 200.milliseconds,
100             onStart = { willRunDismissFromKeyguard = willRunAnimationOnKeyguard() },
101             onStep = {
102                 if (willRunDismissFromKeyguard) {
103                     0f
104                 } else {
105                     1f - it
106                 }
107             },
108         )
109     }
110 
111     /** Lockscreen alpha */
112     val lockscreenAlpha: Flow<Float> =
113         if (SceneContainerFlag.isEnabled) {
114             keyguardDismissActionInteractor
115                 .get()
116                 .willAnimateDismissActionOnLockscreen
<lambda>null117                 .flatMapLatest { createLockscreenAlpha { it } }
118         } else {
119             createLockscreenAlpha(primaryBouncerInteractor::willRunDismissFromKeyguard)
120         }
121 
createLockscreenAlphanull122     private fun createLockscreenAlpha(willRunAnimationOnKeyguard: () -> Boolean): Flow<Float> {
123         return transitionAnimation.sharedFlow(
124             duration = 50.milliseconds,
125             onStart = {
126                 leaveShadeOpen = statusBarStateController.leaveOpenOnKeyguardHide()
127                 willRunDismissFromKeyguard = willRunAnimationOnKeyguard()
128             },
129             onStep = {
130                 if (willRunDismissFromKeyguard || leaveShadeOpen) {
131                     1f
132                 } else {
133                     0f
134                 }
135             },
136             onFinish = { 0f },
137         )
138     }
139 
140     val scrimAlpha: Flow<ScrimAlpha> =
141         bouncerToGoneFlows.scrimAlpha(TO_GONE_DURATION, PRIMARY_BOUNCER)
142 }
143