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