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 android.view.View 20 import android.view.WindowInsets 21 import androidx.lifecycle.Lifecycle 22 import androidx.lifecycle.repeatOnLifecycle 23 import com.android.systemui.Flags.communalHub 24 import com.android.systemui.common.ui.view.onApplyWindowInsets 25 import com.android.systemui.common.ui.view.onLayoutChanged 26 import com.android.systemui.dagger.SysUISingleton 27 import com.android.systemui.dagger.qualifiers.Main 28 import com.android.systemui.keyguard.ui.viewmodel.BurnInParameters 29 import com.android.systemui.keyguard.ui.viewmodel.ViewStateAccessor 30 import com.android.systemui.lifecycle.repeatWhenAttached 31 import com.android.systemui.scene.shared.flag.SceneContainerFlag 32 import com.android.systemui.statusbar.notification.footer.shared.FooterViewRefactor 33 import com.android.systemui.statusbar.notification.stack.NotificationStackScrollLayoutController 34 import com.android.systemui.statusbar.notification.stack.NotificationStackSizeCalculator 35 import com.android.systemui.statusbar.notification.stack.ui.view.SharedNotificationContainer 36 import com.android.systemui.statusbar.notification.stack.ui.viewmodel.SharedNotificationContainerViewModel 37 import com.android.systemui.util.kotlin.DisposableHandles 38 import javax.inject.Inject 39 import kotlinx.coroutines.CoroutineDispatcher 40 import kotlinx.coroutines.DisposableHandle 41 import kotlinx.coroutines.flow.MutableStateFlow 42 import kotlinx.coroutines.flow.flatMapLatest 43 import kotlinx.coroutines.flow.update 44 import kotlinx.coroutines.launch 45 46 /** Binds the shared notification container to its view-model. */ 47 @SysUISingleton 48 class SharedNotificationContainerBinder 49 @Inject 50 constructor( 51 private val controller: NotificationStackScrollLayoutController, 52 private val notificationStackSizeCalculator: NotificationStackSizeCalculator, 53 private val notificationScrollViewBinder: NotificationScrollViewBinder, 54 @Main private val mainImmediateDispatcher: CoroutineDispatcher, 55 ) { 56 57 fun bind( 58 view: SharedNotificationContainer, 59 viewModel: SharedNotificationContainerViewModel, 60 ): DisposableHandle { 61 val disposables = DisposableHandles() 62 63 disposables += 64 view.repeatWhenAttached { 65 repeatOnLifecycle(Lifecycle.State.CREATED) { 66 launch { 67 viewModel.configurationBasedDimensions.collect { 68 view.updateConstraints( 69 useSplitShade = it.useSplitShade, 70 marginStart = it.marginStart, 71 marginTop = it.marginTop, 72 marginEnd = it.marginEnd, 73 marginBottom = it.marginBottom, 74 ) 75 76 controller.setOverExpansion(0f) 77 controller.setOverScrollAmount(0) 78 if (!FooterViewRefactor.isEnabled) { 79 controller.updateFooter() 80 } 81 } 82 } 83 } 84 } 85 86 val burnInParams = MutableStateFlow(BurnInParameters()) 87 val viewState = 88 ViewStateAccessor( 89 alpha = { controller.getAlpha() }, 90 ) 91 92 /* 93 * For animation sensitive coroutines, immediately run just like applicationScope does 94 * instead of doing a post() to the main thread. This extra delay can cause visible jitter. 95 */ 96 disposables += 97 view.repeatWhenAttached(mainImmediateDispatcher) { 98 repeatOnLifecycle(Lifecycle.State.CREATED) { 99 launch { 100 // Only temporarily needed, until flexi notifs go live 101 viewModel.shadeCollapseFadeIn.collect { fadeIn -> 102 if (fadeIn) { 103 android.animation.ValueAnimator.ofFloat(0f, 1f).apply { 104 duration = 250 105 addUpdateListener { animation -> 106 controller.setMaxAlphaForKeyguard( 107 animation.animatedFraction, 108 "SharedNotificationContainerVB (collapseFadeIn)" 109 ) 110 } 111 start() 112 } 113 } 114 } 115 } 116 117 launch { 118 viewModel 119 .getMaxNotifications { space, extraShelfSpace -> 120 val shelfHeight = controller.getShelfHeight().toFloat() 121 notificationStackSizeCalculator.computeMaxKeyguardNotifications( 122 controller.getView(), 123 space, 124 if (extraShelfSpace) shelfHeight else 0f, 125 shelfHeight, 126 ) 127 } 128 .collect { controller.setMaxDisplayedNotifications(it) } 129 } 130 131 if (!SceneContainerFlag.isEnabled) { 132 launch { 133 viewModel.bounds.collect { 134 val animate = 135 it.isAnimated || controller.isAddOrRemoveAnimationPending 136 controller.updateTopPadding(it.top, animate) 137 } 138 } 139 } 140 141 if (!SceneContainerFlag.isEnabled) { 142 launch { 143 burnInParams 144 .flatMapLatest { params -> viewModel.translationY(params) } 145 .collect { y -> controller.setTranslationY(y) } 146 } 147 } 148 149 launch { viewModel.translationX.collect { x -> controller.translationX = x } } 150 151 launch { 152 viewModel.keyguardAlpha(viewState).collect { 153 controller.setMaxAlphaForKeyguard(it, "SharedNotificationContainerVB") 154 } 155 } 156 157 if (communalHub()) { 158 launch { 159 viewModel.glanceableHubAlpha.collect { 160 controller.setMaxAlphaForGlanceableHub(it) 161 } 162 } 163 } 164 } 165 } 166 167 if (SceneContainerFlag.isEnabled) { 168 disposables += notificationScrollViewBinder.bindWhileAttached() 169 } 170 171 controller.setOnHeightChangedRunnable { viewModel.notificationStackChanged() } 172 disposables += DisposableHandle { controller.setOnHeightChangedRunnable(null) } 173 174 disposables += 175 view.onApplyWindowInsets { _: View, insets: WindowInsets -> 176 val insetTypes = WindowInsets.Type.systemBars() or WindowInsets.Type.displayCutout() 177 burnInParams.update { current -> 178 current.copy(topInset = insets.getInsetsIgnoringVisibility(insetTypes).top) 179 } 180 insets 181 } 182 183 disposables += view.onLayoutChanged { viewModel.notificationStackChanged() } 184 185 return disposables 186 } 187 } 188