1 /*
<lambda>null2  * 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.app.animation.Interpolators.EMPHASIZED_ACCELERATE
20 import com.android.systemui.bouncer.domain.interactor.PrimaryBouncerInteractor
21 import com.android.systemui.keyguard.domain.interactor.KeyguardDismissActionInteractor
22 import com.android.systemui.keyguard.shared.model.Edge
23 import com.android.systemui.keyguard.shared.model.KeyguardState
24 import com.android.systemui.keyguard.shared.model.KeyguardState.GONE
25 import com.android.systemui.keyguard.shared.model.ScrimAlpha
26 import com.android.systemui.keyguard.ui.KeyguardTransitionAnimationFlow
27 import com.android.systemui.scene.shared.flag.SceneContainerFlag
28 import com.android.systemui.scene.shared.model.Scenes
29 import com.android.systemui.shade.domain.interactor.ShadeInteractor
30 import com.android.systemui.statusbar.SysuiStatusBarStateController
31 import dagger.Lazy
32 import javax.inject.Inject
33 import kotlin.time.Duration
34 import kotlinx.coroutines.ExperimentalCoroutinesApi
35 import kotlinx.coroutines.flow.Flow
36 import kotlinx.coroutines.flow.distinctUntilChanged
37 import kotlinx.coroutines.flow.flatMapLatest
38 import kotlinx.coroutines.flow.map
39 
40 /** ALTERNATE and PRIMARY bouncers common animations */
41 @OptIn(ExperimentalCoroutinesApi::class)
42 class BouncerToGoneFlows
43 @Inject
44 constructor(
45     private val statusBarStateController: SysuiStatusBarStateController,
46     private val primaryBouncerInteractor: PrimaryBouncerInteractor,
47     private val keyguardDismissActionInteractor: Lazy<KeyguardDismissActionInteractor>,
48     private val shadeInteractor: ShadeInteractor,
49     private val animationFlow: KeyguardTransitionAnimationFlow,
50 ) {
51     /** Common fade for scrim alpha values during *BOUNCER->GONE */
52     fun scrimAlpha(duration: Duration, fromState: KeyguardState): Flow<ScrimAlpha> {
53         return if (SceneContainerFlag.isEnabled) {
54             keyguardDismissActionInteractor
55                 .get()
56                 .willAnimateDismissActionOnLockscreen
57                 .flatMapLatest { createScrimAlphaFlow(duration, fromState) { it } }
58         } else {
59             createScrimAlphaFlow(
60                 duration,
61                 fromState,
62                 primaryBouncerInteractor::willRunDismissFromKeyguard
63             )
64         }
65     }
66 
67     /**
68      * When the shade is expanded, make sure that all notifications can be seen immediately during a
69      * transition to GONE. This matters especially when the user has chosen to not show
70      * notifications on the lockscreen and then pulls down the shade, which presents them with an
71      * immediate auth prompt, followed by a notification animation.
72      */
73     fun showAllNotifications(duration: Duration, from: KeyguardState): Flow<Boolean> {
74         var leaveShadeOpen = false
75         return animationFlow
76             .setup(
77                 duration = duration,
78                 // TODO(b/330311871): from can be PRIMARY_BOUNCER which would be a scene -> scene
79                 //  transition
80                 edge = Edge.create(from = from, to = Scenes.Gone)
81             )
82             .setupWithoutSceneContainer(
83                 edge = Edge.create(from = from, to = GONE),
84             )
85             .sharedFlow(
86                 duration = duration,
87                 onStart = { leaveShadeOpen = statusBarStateController.leaveOpenOnKeyguardHide() },
88                 onStep = { if (leaveShadeOpen) 1f else 0f },
89                 onFinish = { 0f },
90                 onCancel = { 0f },
91             )
92             .map { it == 1f }
93             .distinctUntilChanged()
94     }
95 
96     private fun createScrimAlphaFlow(
97         duration: Duration,
98         fromState: KeyguardState,
99         willRunAnimationOnKeyguard: () -> Boolean
100     ): Flow<ScrimAlpha> {
101         var isShadeExpanded = false
102         var leaveShadeOpen: Boolean = false
103         var willRunDismissFromKeyguard: Boolean = false
104         val transitionAnimation =
105             animationFlow
106                 .setup(
107                     duration = duration,
108                     // TODO(b/330311871): from can be PRIMARY_BOUNCER which would be a scene ->
109                     //  scene transition
110                     edge = Edge.create(from = fromState, to = Scenes.Gone),
111                 )
112                 .setupWithoutSceneContainer(
113                     edge = Edge.create(from = fromState, to = GONE),
114                 )
115 
116         return shadeInteractor.anyExpansion
117             .map { it > 0f }
118             .distinctUntilChanged()
119             .flatMapLatest { isAnyExpanded ->
120                 transitionAnimation
121                     .sharedFlow(
122                         duration = duration,
123                         interpolator = EMPHASIZED_ACCELERATE,
124                         onStart = {
125                             leaveShadeOpen = statusBarStateController.leaveOpenOnKeyguardHide()
126                             willRunDismissFromKeyguard = willRunAnimationOnKeyguard()
127                             isShadeExpanded = isAnyExpanded
128                         },
129                         onStep = { 1f - it },
130                     )
131                     .map {
132                         if (willRunDismissFromKeyguard) {
133                             if (isShadeExpanded) {
134                                 ScrimAlpha(
135                                     behindAlpha = it,
136                                     notificationsAlpha = it,
137                                 )
138                             } else {
139                                 ScrimAlpha()
140                             }
141                         } else if (leaveShadeOpen) {
142                             ScrimAlpha(
143                                 behindAlpha = 1f,
144                                 notificationsAlpha = 1f,
145                             )
146                         } else {
147                             ScrimAlpha(behindAlpha = it)
148                         }
149                     }
150             }
151     }
152 }
153