• 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.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