• 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.viewmodel
18 
19 import com.android.systemui.dagger.SysUISingleton
20 import com.android.systemui.res.R
21 import com.android.systemui.shade.domain.interactor.ShadeInteractor
22 import com.android.systemui.shared.notifications.domain.interactor.NotificationSettingsInteractor
23 import com.android.systemui.statusbar.notification.domain.interactor.ActiveNotificationsInteractor
24 import com.android.systemui.statusbar.notification.domain.interactor.SeenNotificationsInteractor
25 import com.android.systemui.statusbar.notification.footer.shared.FooterViewRefactor
26 import com.android.systemui.statusbar.notification.footer.ui.view.FooterView
27 import com.android.systemui.util.kotlin.sample
28 import com.android.systemui.util.ui.AnimatableEvent
29 import com.android.systemui.util.ui.AnimatedValue
30 import com.android.systemui.util.ui.toAnimatedValueFlow
31 import dagger.Module
32 import dagger.Provides
33 import java.util.Optional
34 import javax.inject.Provider
35 import kotlinx.coroutines.flow.Flow
36 import kotlinx.coroutines.flow.combine
37 import kotlinx.coroutines.flow.distinctUntilChanged
38 import kotlinx.coroutines.flow.flowOf
39 import kotlinx.coroutines.flow.map
40 import kotlinx.coroutines.flow.onStart
41 
42 /** ViewModel for [FooterView]. */
43 class FooterViewModel(
44     activeNotificationsInteractor: ActiveNotificationsInteractor,
45     notificationSettingsInteractor: NotificationSettingsInteractor,
46     seenNotificationsInteractor: SeenNotificationsInteractor,
47     shadeInteractor: ShadeInteractor,
48 ) {
49     /** A message to show instead of the footer buttons. */
50     val message: FooterMessageViewModel =
51         FooterMessageViewModel(
52             messageId = R.string.unlock_to_see_notif_text,
53             iconId = R.drawable.ic_friction_lock_closed,
54             isVisible = seenNotificationsInteractor.hasFilteredOutSeenNotifications,
55         )
56 
57     private val clearAllButtonVisible =
58         activeNotificationsInteractor.hasClearableNotifications
59             .combine(message.isVisible) { hasClearableNotifications, isMessageVisible ->
60                 if (isMessageVisible) {
61                     // If the message is visible, the button never is
62                     false
63                 } else {
64                     hasClearableNotifications
65                 }
66             }
67             .distinctUntilChanged()
68 
69     /** The button for clearing notifications. */
70     val clearAllButton: FooterButtonViewModel =
71         FooterButtonViewModel(
72             labelId = flowOf(R.string.clear_all_notifications_text),
73             accessibilityDescriptionId = flowOf(R.string.accessibility_clear_all),
74             isVisible =
75                 clearAllButtonVisible
76                     .sample(
77                         // TODO(b/322167853): This check is currently duplicated in
78                         //  NotificationListViewModel, but instead it should be a field in
79                         //  ShadeAnimationInteractor.
80                         combine(
81                                 shadeInteractor.isShadeFullyExpanded,
82                                 shadeInteractor.isShadeTouchable,
83                                 ::Pair
84                             )
85                             .onStart { emit(Pair(false, false)) }
86                     ) { clearAllButtonVisible, (isShadeFullyExpanded, animationsEnabled) ->
87                         val shouldAnimate = isShadeFullyExpanded && animationsEnabled
88                         AnimatableEvent(clearAllButtonVisible, shouldAnimate)
89                     }
90                     .toAnimatedValueFlow(),
91         )
92 
93     val manageButtonShouldLaunchHistory =
94         notificationSettingsInteractor.isNotificationHistoryEnabled
95 
96     private val manageOrHistoryButtonText: Flow<Int> =
97         manageButtonShouldLaunchHistory.map { shouldLaunchHistory ->
98             if (shouldLaunchHistory) R.string.manage_notifications_history_text
99             else R.string.manage_notifications_text
100         }
101 
102     /** The button for managing notification settings or opening notification history. */
103     val manageOrHistoryButton: FooterButtonViewModel =
104         FooterButtonViewModel(
105             labelId = manageOrHistoryButtonText,
106             accessibilityDescriptionId = manageOrHistoryButtonText,
107             isVisible =
108                 // Hide the manage button if the message is visible
109                 message.isVisible.map { messageVisible ->
110                     AnimatedValue.NotAnimating(!messageVisible)
111                 },
112         )
113 }
114 
115 @Module
116 object FooterViewModelModule {
117     @Provides
118     @SysUISingleton
provideOptionalnull119     fun provideOptional(
120         activeNotificationsInteractor: Provider<ActiveNotificationsInteractor>,
121         notificationSettingsInteractor: Provider<NotificationSettingsInteractor>,
122         seenNotificationsInteractor: Provider<SeenNotificationsInteractor>,
123         shadeInteractor: Provider<ShadeInteractor>,
124     ): Optional<FooterViewModel> {
125         return if (FooterViewRefactor.isEnabled) {
126             Optional.of(
127                 FooterViewModel(
128                     activeNotificationsInteractor.get(),
129                     notificationSettingsInteractor.get(),
130                     seenNotificationsInteractor.get(),
131                     shadeInteractor.get()
132                 )
133             )
134         } else {
135             Optional.empty()
136         }
137     }
138 }
139