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.footer.ui.viewbinder 18 19 import android.view.View 20 import androidx.lifecycle.lifecycleScope 21 import com.android.app.tracing.coroutines.launchTraced as launch 22 import com.android.internal.jank.InteractionJankMonitor 23 import com.android.systemui.Flags.notificationShadeBlur 24 import com.android.systemui.lifecycle.repeatWhenAttached 25 import com.android.systemui.statusbar.notification.NotificationActivityStarter 26 import com.android.systemui.statusbar.notification.NotificationActivityStarter.SettingsIntent 27 import com.android.systemui.statusbar.notification.emptyshade.shared.ModesEmptyShadeFix 28 import com.android.systemui.statusbar.notification.footer.shared.NotifRedesignFooter 29 import com.android.systemui.statusbar.notification.footer.ui.view.FooterView 30 import com.android.systemui.statusbar.notification.footer.ui.viewmodel.FooterViewModel 31 import com.android.systemui.util.ui.isAnimating 32 import com.android.systemui.util.ui.stopAnimating 33 import com.android.systemui.util.ui.value 34 import kotlinx.coroutines.DisposableHandle 35 import kotlinx.coroutines.coroutineScope 36 37 /** Binds a [FooterView] to its [view model][FooterViewModel]. */ 38 object FooterViewBinder { 39 fun bindWhileAttached( 40 footer: FooterView, 41 viewModel: FooterViewModel, 42 clearAllNotifications: View.OnClickListener, 43 launchNotificationSettings: View.OnClickListener, 44 launchNotificationHistory: View.OnClickListener, 45 notificationActivityStarter: NotificationActivityStarter, 46 ): DisposableHandle { 47 return footer.repeatWhenAttached { 48 lifecycleScope.launch { 49 bind( 50 footer, 51 viewModel, 52 clearAllNotifications, 53 launchNotificationSettings, 54 launchNotificationHistory, 55 notificationActivityStarter, 56 ) 57 } 58 } 59 } 60 61 suspend fun bind( 62 footer: FooterView, 63 viewModel: FooterViewModel, 64 clearAllNotifications: View.OnClickListener, 65 launchNotificationSettings: View.OnClickListener, 66 launchNotificationHistory: View.OnClickListener, 67 notificationActivityStarter: NotificationActivityStarter, 68 ) = coroutineScope { 69 launch { bindClearAllButton(footer, viewModel, clearAllNotifications) } 70 if (!NotifRedesignFooter.isEnabled) { 71 launch { 72 bindManageOrHistoryButton( 73 footer, 74 viewModel, 75 launchNotificationSettings, 76 launchNotificationHistory, 77 notificationActivityStarter, 78 ) 79 } 80 } else { 81 launch { bindSettingsButton(footer, viewModel, notificationActivityStarter) } 82 launch { bindHistoryButton(footer, viewModel, notificationActivityStarter) } 83 } 84 launch { bindMessage(footer, viewModel) } 85 86 if (notificationShadeBlur()) { 87 launch { 88 viewModel.isBlurSupported.collect { supported -> 89 footer.setIsBlurSupported(supported) 90 } 91 } 92 } 93 } 94 95 private suspend fun bindClearAllButton( 96 footer: FooterView, 97 viewModel: FooterViewModel, 98 clearAllNotifications: View.OnClickListener, 99 ) = coroutineScope { 100 launch { 101 viewModel.clearAllButton.labelId.collect { textId -> 102 footer.setClearAllButtonText(textId) 103 } 104 } 105 106 launch { 107 viewModel.clearAllButton.accessibilityDescriptionId.collect { textId -> 108 footer.setClearAllButtonDescription(textId) 109 } 110 } 111 112 launch { 113 viewModel.clearAllButton.isVisible.collect { isVisible -> 114 if (isVisible.value) { 115 footer.setClearAllButtonClickListener(clearAllNotifications) 116 } else { 117 // When the button isn't visible, it also shouldn't react to clicks. This is 118 // necessary because when the clear all button is not visible, it's actually 119 // just the alpha that becomes 0 so it can still be tapped. 120 footer.setClearAllButtonClickListener(null) 121 } 122 123 if (isVisible.isAnimating) { 124 footer.setClearAllButtonVisible(isVisible.value, /* animate= */ true) { _ -> 125 isVisible.stopAnimating() 126 } 127 } else { 128 footer.setClearAllButtonVisible(isVisible.value, /* animate= */ false) 129 } 130 } 131 } 132 } 133 134 private suspend fun bindSettingsButton( 135 footer: FooterView, 136 viewModel: FooterViewModel, 137 notificationActivityStarter: NotificationActivityStarter, 138 ) = coroutineScope { 139 val settingsIntent = 140 SettingsIntent.forNotificationSettings( 141 cujType = InteractionJankMonitor.CUJ_SHADE_APP_LAUNCH_FROM_HISTORY_BUTTON 142 ) 143 val onClickListener = { view: View -> 144 notificationActivityStarter.startSettingsIntent(view, settingsIntent) 145 } 146 footer.setSettingsButtonClickListener(onClickListener) 147 148 launch { 149 // NOTE: This visibility change is never animated. We also don't need to do anything 150 // special about the onClickListener here, since we're changing the visibility to 151 // GONE so it won't be clickable anyway. 152 viewModel.settingsButtonVisible.collect { footer.setSettingsButtonVisible(it) } 153 } 154 } 155 156 private suspend fun bindHistoryButton( 157 footer: FooterView, 158 viewModel: FooterViewModel, 159 notificationActivityStarter: NotificationActivityStarter, 160 ) = coroutineScope { 161 val settingsIntent = 162 SettingsIntent.forNotificationHistory( 163 cujType = InteractionJankMonitor.CUJ_SHADE_APP_LAUNCH_FROM_HISTORY_BUTTON 164 ) 165 val onClickListener = { view: View -> 166 notificationActivityStarter.startSettingsIntent(view, settingsIntent) 167 } 168 footer.setHistoryButtonClickListener(onClickListener) 169 170 launch { 171 // NOTE: This visibility change is never animated. We also don't need to do anything 172 // special about the onClickListener here, since we're changing the visibility to 173 // GONE so it won't be clickable anyway. 174 viewModel.historyButtonVisible.collect { footer.setHistoryButtonVisible(it) } 175 } 176 } 177 178 private suspend fun bindManageOrHistoryButton( 179 footer: FooterView, 180 viewModel: FooterViewModel, 181 launchNotificationSettings: View.OnClickListener, 182 launchNotificationHistory: View.OnClickListener, 183 notificationActivityStarter: NotificationActivityStarter, 184 ) = coroutineScope { 185 launch { 186 if (ModesEmptyShadeFix.isEnabled) { 187 viewModel.manageOrHistoryButtonClick.collect { settingsIntent -> 188 val onClickListener = { view: View -> 189 notificationActivityStarter.startSettingsIntent(view, settingsIntent) 190 } 191 footer.setManageButtonClickListener(onClickListener) 192 } 193 } else { 194 viewModel.manageButtonShouldLaunchHistory.collect { shouldLaunchHistory -> 195 if (shouldLaunchHistory) { 196 footer.setManageButtonClickListener(launchNotificationHistory) 197 } else { 198 footer.setManageButtonClickListener(launchNotificationSettings) 199 } 200 } 201 } 202 } 203 204 launch { 205 viewModel.manageOrHistoryButton.labelId.collect { textId -> 206 footer.setManageOrHistoryButtonText(textId) 207 } 208 } 209 210 launch { 211 viewModel.manageOrHistoryButton.accessibilityDescriptionId.collect { textId -> 212 footer.setManageOrHistoryButtonDescription(textId) 213 } 214 } 215 216 launch { 217 viewModel.manageOrHistoryButton.isVisible.collect { isVisible -> 218 // NOTE: This visibility change is never animated. We also don't need to do anything 219 // special about the onClickListener here, since we're changing the visibility to 220 // GONE so it won't be clickable anyway. 221 footer.setManageOrHistoryButtonVisible(isVisible.value) 222 } 223 } 224 } 225 226 private suspend fun bindMessage(footer: FooterView, viewModel: FooterViewModel) = 227 coroutineScope { 228 // Bind the resource IDs 229 footer.setMessageString(viewModel.message.messageId) 230 footer.setMessageIcon(viewModel.message.iconId) 231 232 launch { 233 viewModel.message.isVisible.collect { visible -> 234 footer.setFooterLabelVisible(visible) 235 } 236 } 237 } 238 } 239