• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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.statusbar.notification.stack.ui.viewbinder
18 
19 import androidx.lifecycle.Lifecycle
20 import androidx.lifecycle.repeatOnLifecycle
21 import com.android.app.tracing.coroutines.launchTraced as launch
22 import com.android.systemui.Flags
23 import com.android.systemui.common.ui.view.onLayoutChanged
24 import com.android.systemui.communal.domain.interactor.CommunalSettingsInteractor
25 import com.android.systemui.dagger.SysUISingleton
26 import com.android.systemui.dagger.qualifiers.Main
27 import com.android.systemui.keyguard.domain.interactor.KeyguardInteractor
28 import com.android.systemui.keyguard.ui.viewmodel.ViewStateAccessor
29 import com.android.systemui.lifecycle.repeatWhenAttached
30 import com.android.systemui.scene.shared.flag.SceneContainerFlag
31 import com.android.systemui.shared.Flags.extendedWallpaperEffects
32 import com.android.systemui.statusbar.notification.stack.NotificationStackScrollLayoutController
33 import com.android.systemui.statusbar.notification.stack.NotificationStackSizeCalculator
34 import com.android.systemui.statusbar.notification.stack.ui.view.SharedNotificationContainer
35 import com.android.systemui.statusbar.notification.stack.ui.viewmodel.SharedNotificationContainerViewModel
36 import com.android.systemui.util.kotlin.DisposableHandles
37 import javax.inject.Inject
38 import kotlinx.coroutines.CoroutineDispatcher
39 import kotlinx.coroutines.DisposableHandle
40 import kotlinx.coroutines.flow.combine
41 import kotlinx.coroutines.flow.map
42 
43 /** Binds the shared notification container to its view-model. */
44 @SysUISingleton
45 class SharedNotificationContainerBinder
46 @Inject
47 constructor(
48     private val controller: NotificationStackScrollLayoutController,
49     private val notificationStackSizeCalculator: NotificationStackSizeCalculator,
50     private val notificationScrollViewBinder: NotificationScrollViewBinder,
51     private val communalSettingsInteractor: CommunalSettingsInteractor,
52     @Main private val mainImmediateDispatcher: CoroutineDispatcher,
53     val keyguardInteractor: KeyguardInteractor,
54 ) {
55 
56     private val calculateMaxNotifications: (Float, Boolean) -> Int = { space, extraShelfSpace ->
57         val shelfHeight = controller.getShelfHeight().toFloat()
58         notificationStackSizeCalculator.computeMaxKeyguardNotifications(
59             controller.view,
60             space,
61             if (extraShelfSpace) shelfHeight else 0f,
62             shelfHeight,
63         )
64     }
65 
66     fun bind(
67         view: SharedNotificationContainer,
68         viewModel: SharedNotificationContainerViewModel,
69     ): DisposableHandle {
70         val disposables = DisposableHandles()
71         disposables +=
72             view.repeatWhenAttached {
73                 repeatOnLifecycle(Lifecycle.State.CREATED) {
74                     launch {
75                         viewModel.configurationBasedDimensions.collect {
76                             view.updateConstraints(
77                                 horizontalPosition = it.horizontalPosition,
78                                 marginStart = it.marginStart,
79                                 marginTop = it.marginTop,
80                                 marginEnd = it.marginEnd,
81                                 marginBottom = it.marginBottom,
82                                 nsslAlpha = controller.alpha,
83                             )
84 
85                             controller.setOverExpansion(0f)
86                             controller.setOverScrollAmount(0)
87                         }
88                     }
89                 }
90             }
91 
92         val viewState = ViewStateAccessor(alpha = { controller.getAlpha() })
93 
94         /*
95          * For animation sensitive coroutines, immediately run just like applicationScope does
96          * instead of doing a post() to the main thread. This extra delay can cause visible jitter.
97          */
98         disposables +=
99             view.repeatWhenAttached(mainImmediateDispatcher) {
100                 repeatOnLifecycle(Lifecycle.State.CREATED) {
101                     if (!SceneContainerFlag.isEnabled) {
102                         launch {
103                             viewModel.shadeCollapseFadeIn.collect { fadeIn ->
104                                 if (fadeIn) {
105                                     android.animation.ValueAnimator.ofFloat(0f, 1f).apply {
106                                         duration = 250
107                                         addUpdateListener { animation ->
108                                             controller.setMaxAlphaForKeyguard(
109                                                 animation.animatedFraction,
110                                                 "SharedNotificationContainerVB (collapseFadeIn)",
111                                             )
112                                         }
113                                         start()
114                                     }
115                                 }
116                             }
117                         }
118                     }
119 
120                     launch {
121                         viewModel.getLockscreenDisplayConfig(calculateMaxNotifications).collect {
122                             (isOnLockscreen, maxNotifications) ->
123                             if (SceneContainerFlag.isEnabled) {
124                                 controller.setOnLockscreen(isOnLockscreen)
125                             }
126                             controller.setMaxDisplayedNotifications(maxNotifications)
127                         }
128                     }
129 
130                     if (!SceneContainerFlag.isEnabled) {
131                         launch {
132                             viewModel.bounds.collect {
133                                 val animate =
134                                     it.isAnimated || controller.isAddOrRemoveAnimationPending
135                                 controller.updateTopPadding(it.top, animate)
136                             }
137                         }
138                     }
139 
140                     if (!SceneContainerFlag.isEnabled) {
141                         launch {
142                             viewModel.translationY.collect { y -> controller.setTranslationY(y) }
143                         }
144                     }
145 
146                     if (!SceneContainerFlag.isEnabled) {
147                         if (extendedWallpaperEffects()) {
148                             launch {
149                                 combine(
150                                         viewModel.getNotificationStackAbsoluteBottom(
151                                             calculateMaxNotifications = calculateMaxNotifications,
152                                             calculateHeight = { maxNotifications ->
153                                                 notificationStackSizeCalculator.computeHeight(
154                                                     maxNotifs = maxNotifications,
155                                                     shelfHeight =
156                                                         controller.getShelfHeight().toFloat(),
157                                                     stack = controller.view,
158                                                 )
159                                             },
160                                             controller.getShelfHeight().toFloat(),
161                                         ),
162                                         viewModel.configurationBasedDimensions.map { it.marginTop },
163                                         ::Pair,
164                                     )
165                                     .collect { (bottom: Float, marginTop: Int) ->
166                                         keyguardInteractor.setNotificationStackAbsoluteBottom(
167                                             marginTop + bottom
168                                         )
169                                     }
170                             }
171                         }
172                     }
173 
174                     launch { viewModel.translationX.collect { x -> controller.translationX = x } }
175 
176                     launch {
177                         viewModel.keyguardAlpha(viewState, this).collect {
178                             controller.setMaxAlphaForKeyguard(it, "SharedNotificationContainerVB")
179                         }
180                     }
181 
182                     if (!SceneContainerFlag.isEnabled) {
183                         launch {
184                             // For when the entire view should fade, such as with the brightness
185                             // slider
186                             viewModel.panelAlpha.collect { controller.setMaxAlphaFromView(it) }
187                         }
188                     }
189 
190                     if (Flags.bouncerUiRevamp()) {
191                         launch { viewModel.blurRadius.collect { controller.setBlurRadius(it) } }
192                     }
193 
194                     if (communalSettingsInteractor.isCommunalFlagEnabled()) {
195                         launch {
196                             viewModel.glanceableHubAlpha.collect {
197                                 controller.setMaxAlphaForGlanceableHub(it)
198                             }
199                         }
200                     }
201                 }
202             }
203 
204         if (SceneContainerFlag.isEnabled) {
205             disposables += notificationScrollViewBinder.bindWhileAttached()
206         }
207 
208         controller.setOnHeightChangedRunnable { viewModel.notificationStackChanged() }
209         disposables += DisposableHandle { controller.setOnHeightChangedRunnable(null) }
210         disposables += view.onLayoutChanged { viewModel.notificationStackChanged() }
211 
212         return disposables
213     }
214 }
215