• 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 android.content.Intent
20 import android.provider.Settings
21 import com.android.internal.jank.InteractionJankMonitor
22 import com.android.systemui.res.R
23 import com.android.systemui.shade.domain.interactor.ShadeInteractor
24 import com.android.systemui.shared.notifications.domain.interactor.NotificationSettingsInteractor
25 import com.android.systemui.statusbar.notification.NotificationActivityStarter.SettingsIntent
26 import com.android.systemui.statusbar.notification.domain.interactor.ActiveNotificationsInteractor
27 import com.android.systemui.statusbar.notification.domain.interactor.SeenNotificationsInteractor
28 import com.android.systemui.statusbar.notification.emptyshade.shared.ModesEmptyShadeFix
29 import com.android.systemui.statusbar.notification.footer.ui.view.FooterView
30 import com.android.systemui.util.kotlin.sample
31 import com.android.systemui.util.ui.AnimatableEvent
32 import com.android.systemui.util.ui.AnimatedValue
33 import com.android.systemui.util.ui.toAnimatedValueFlow
34 import com.android.systemui.window.domain.interactor.WindowRootViewBlurInteractor
35 import dagger.assisted.AssistedFactory
36 import dagger.assisted.AssistedInject
37 import kotlinx.coroutines.flow.Flow
38 import kotlinx.coroutines.flow.combine
39 import kotlinx.coroutines.flow.distinctUntilChanged
40 import kotlinx.coroutines.flow.flowOf
41 import kotlinx.coroutines.flow.map
42 import kotlinx.coroutines.flow.onStart
43 
44 /** ViewModel for [FooterView]. */
45 class FooterViewModel
46 @AssistedInject
47 constructor(
48     activeNotificationsInteractor: ActiveNotificationsInteractor,
49     notificationSettingsInteractor: NotificationSettingsInteractor,
50     seenNotificationsInteractor: SeenNotificationsInteractor,
51     shadeInteractor: ShadeInteractor,
52     windowRootViewBlurInteractor: WindowRootViewBlurInteractor,
53 ) {
54     /** A message to show instead of the footer buttons. */
55     val message: FooterMessageViewModel =
56         FooterMessageViewModel(
57             messageId = R.string.unlock_to_see_notif_text,
58             iconId = R.drawable.ic_friction_lock_closed,
59             isVisible = seenNotificationsInteractor.hasFilteredOutSeenNotifications,
60         )
61 
62     private val clearAllButtonVisible =
63         activeNotificationsInteractor.hasClearableNotifications
64             .combine(message.isVisible) { hasClearableNotifications, isMessageVisible ->
65                 if (isMessageVisible) {
66                     // If the message is visible, the button never is
67                     false
68                 } else {
69                     hasClearableNotifications
70                 }
71             }
72             .distinctUntilChanged()
73 
74     /** The button for clearing notifications. */
75     val clearAllButton: FooterButtonViewModel =
76         FooterButtonViewModel(
77             labelId = flowOf(R.string.clear_all_notifications_text),
78             accessibilityDescriptionId = flowOf(R.string.accessibility_clear_all),
79             isVisible =
80                 clearAllButtonVisible
81                     .sample(
82                         // TODO(b/322167853): This check is currently duplicated in
83                         //  NotificationListViewModel, but instead it should be a field in
84                         //  ShadeAnimationInteractor.
85                         combine(
86                                 shadeInteractor.isShadeFullyExpanded,
87                                 shadeInteractor.isShadeTouchable,
88                                 ::Pair,
89                             )
90                             .onStart { emit(Pair(false, false)) }
91                     ) { clearAllButtonVisible, (isShadeFullyExpanded, animationsEnabled) ->
92                         val shouldAnimate = isShadeFullyExpanded && animationsEnabled
93                         AnimatableEvent(clearAllButtonVisible, shouldAnimate)
94                     }
95                     .toAnimatedValueFlow(),
96         )
97 
98     // Settings buttons are not visible when the message is.
99     val settingsButtonVisible: Flow<Boolean> = message.isVisible.map { !it }
100     val historyButtonVisible: Flow<Boolean> = message.isVisible.map { !it }
101 
102     val manageButtonShouldLaunchHistory =
103         notificationSettingsInteractor.isNotificationHistoryEnabled
104 
105     val manageOrHistoryButtonClick: Flow<SettingsIntent> by lazy {
106         if (ModesEmptyShadeFix.isUnexpectedlyInLegacyMode()) {
107             flowOf(SettingsIntent(Intent(Settings.ACTION_NOTIFICATION_SETTINGS)))
108         } else {
109             notificationSettingsInteractor.isNotificationHistoryEnabled.map {
110                 isNotificationHistoryEnabled ->
111                 if (isNotificationHistoryEnabled) {
112                     SettingsIntent.forNotificationHistory(
113                         cujType = InteractionJankMonitor.CUJ_SHADE_APP_LAUNCH_FROM_HISTORY_BUTTON
114                     )
115                 } else {
116                     SettingsIntent.forNotificationSettings(
117                         cujType = InteractionJankMonitor.CUJ_SHADE_APP_LAUNCH_FROM_HISTORY_BUTTON
118                     )
119                 }
120             }
121         }
122     }
123 
124     val isBlurSupported = windowRootViewBlurInteractor.isBlurCurrentlySupported
125 
126     private val manageOrHistoryButtonText: Flow<Int> =
127         notificationSettingsInteractor.isNotificationHistoryEnabled.map { shouldLaunchHistory ->
128             if (shouldLaunchHistory) R.string.manage_notifications_history_text
129             else R.string.manage_notifications_text
130         }
131 
132     /**
133      * The button for managing notification settings or opening notification history. This is
134      * replaced by two separate buttons in the redesign. These are currently static, and therefore
135      * not modeled here, but if that changes we can also add them as FooterButtonViewModels.
136      */
137     val manageOrHistoryButton: FooterButtonViewModel =
138         FooterButtonViewModel(
139             labelId = manageOrHistoryButtonText,
140             accessibilityDescriptionId = manageOrHistoryButtonText,
141             isVisible =
142                 // Hide the manage button if the message is visible
143                 message.isVisible.map { messageVisible ->
144                     AnimatedValue.NotAnimating(!messageVisible)
145                 },
146         )
147 
148     @AssistedFactory
149     interface Factory {
150         fun create(): FooterViewModel
151     }
152 }
153