Loading packages/SystemUI/src/com/android/systemui/statusbar/notification/footer/ui/viewbinder/FooterViewBinder.kt +17 −13 Original line number Diff line number Diff line Loading @@ -34,34 +34,38 @@ object FooterViewBinder { viewModel: FooterViewModel, clearAllNotifications: View.OnClickListener, ): DisposableHandle { // Listen for changes when the view is attached. // Bind the resource IDs footer.setMessageString(viewModel.message.messageId) footer.setMessageIcon(viewModel.message.iconId) footer.setClearAllButtonText(viewModel.clearAllButton.labelId) footer.setClearAllButtonDescription(viewModel.clearAllButton.accessibilityDescriptionId) // Bind the click listeners footer.setClearAllButtonClickListener(clearAllNotifications) // Listen for visibility changes when the view is attached. return footer.repeatWhenAttached { lifecycleScope.launch { viewModel.clearAllButton.collect { button -> if (button.isVisible.isAnimating) { viewModel.clearAllButton.isVisible.collect { isVisible -> if (isVisible.isAnimating) { footer.setClearAllButtonVisible( button.isVisible.value, isVisible.value, /* animate = */ true, ) { _ -> button.isVisible.stopAnimating() isVisible.stopAnimating() } } else { footer.setClearAllButtonVisible( button.isVisible.value, isVisible.value, /* animate = */ false, ) } footer.setClearAllButtonText(button.labelId) footer.setClearAllButtonDescription(button.accessibilityDescriptionId) footer.setClearAllButtonClickListener(clearAllNotifications) } } lifecycleScope.launch { viewModel.message.collect { message -> footer.setFooterLabelVisible(message.visible) footer.setMessageString(message.messageId) footer.setMessageIcon(message.iconId) viewModel.message.isVisible.collect { visible -> footer.setFooterLabelVisible(visible) } } } Loading packages/SystemUI/src/com/android/systemui/statusbar/notification/footer/ui/viewmodel/FooterButtonViewModel.kt +2 −1 Original line number Diff line number Diff line Loading @@ -18,9 +18,10 @@ package com.android.systemui.statusbar.notification.footer.ui.viewmodel import android.annotation.StringRes import com.android.systemui.util.ui.AnimatedValue import kotlinx.coroutines.flow.Flow data class FooterButtonViewModel( @StringRes val labelId: Int, @StringRes val accessibilityDescriptionId: Int, val isVisible: AnimatedValue<Boolean>, val isVisible: Flow<AnimatedValue<Boolean>>, ) packages/SystemUI/src/com/android/systemui/statusbar/notification/footer/ui/viewmodel/FooterMessageViewModel.kt +2 −1 Original line number Diff line number Diff line Loading @@ -18,10 +18,11 @@ package com.android.systemui.statusbar.notification.footer.ui.viewmodel import android.annotation.DrawableRes import android.annotation.StringRes import kotlinx.coroutines.flow.StateFlow /** A ViewModel for the string message that can be shown in the footer. */ data class FooterMessageViewModel( @StringRes val messageId: Int, @DrawableRes val iconId: Int, val visible: Boolean, val isVisible: StateFlow<Boolean>, ) packages/SystemUI/src/com/android/systemui/statusbar/notification/footer/ui/viewmodel/FooterViewModel.kt +25 −31 Original line number Diff line number Diff line Loading @@ -30,9 +30,7 @@ import dagger.Module import dagger.Provides import java.util.Optional import javax.inject.Provider import kotlinx.coroutines.flow.Flow import kotlinx.coroutines.flow.combine import kotlinx.coroutines.flow.map import kotlinx.coroutines.flow.onStart /** ViewModel for [FooterView]. */ Loading @@ -41,7 +39,11 @@ class FooterViewModel( seenNotificationsInteractor: SeenNotificationsInteractor, shadeInteractor: ShadeInteractor, ) { val clearAllButton: Flow<FooterButtonViewModel> = val clearAllButton: FooterButtonViewModel = FooterButtonViewModel( labelId = R.string.clear_all_notifications_text, accessibilityDescriptionId = R.string.accessibility_clear_all, isVisible = activeNotificationsInteractor.hasClearableNotifications .sample( combine( Loading @@ -54,24 +56,16 @@ class FooterViewModel( val shouldAnimate = isShadeFullyExpanded && animationsEnabled AnimatableEvent(hasClearableNotifications, shouldAnimate) } .toAnimatedValueFlow() .map { visible -> FooterButtonViewModel( labelId = R.string.clear_all_notifications_text, accessibilityDescriptionId = R.string.accessibility_clear_all, isVisible = visible, .toAnimatedValueFlow(), ) } val message: Flow<FooterMessageViewModel> = seenNotificationsInteractor.hasFilteredOutSeenNotifications.map { hasFilteredOutNotifs -> val message: FooterMessageViewModel = FooterMessageViewModel( messageId = R.string.unlock_to_see_notif_text, iconId = R.drawable.ic_friction_lock_closed, visible = hasFilteredOutNotifs, isVisible = seenNotificationsInteractor.hasFilteredOutSeenNotifications, ) } } @Module object FooterViewModelModule { Loading packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/footer/ui/viewmodel/FooterViewModelTest.kt +12 −12 Original line number Diff line number Diff line Loading @@ -116,27 +116,27 @@ class FooterViewModelTest : SysuiTestCase() { @Test fun testMessageVisible_whenFilteredNotifications() = testComponent.runTest { val message by collectLastValue(footerViewModel.message) val visible by collectLastValue(footerViewModel.message.isVisible) activeNotificationListRepository.hasFilteredOutSeenNotifications.value = true assertThat(message?.visible).isTrue() assertThat(visible).isTrue() } @Test fun testMessageVisible_whenNoFilteredNotifications() = testComponent.runTest { val message by collectLastValue(footerViewModel.message) val visible by collectLastValue(footerViewModel.message.isVisible) activeNotificationListRepository.hasFilteredOutSeenNotifications.value = false assertThat(message?.visible).isFalse() assertThat(visible).isFalse() } @Test fun testClearAllButtonVisible_whenHasClearableNotifs() = testComponent.runTest { val button by collectLastValue(footerViewModel.clearAllButton) val visible by collectLastValue(footerViewModel.clearAllButton.isVisible) activeNotificationListRepository.notifStats.value = NotifStats( Loading @@ -148,13 +148,13 @@ class FooterViewModelTest : SysuiTestCase() { ) runCurrent() assertThat(button?.isVisible?.value).isTrue() assertThat(visible?.value).isTrue() } @Test fun testClearAllButtonVisible_whenHasNoClearableNotifs() = testComponent.runTest { val button by collectLastValue(footerViewModel.clearAllButton) val visible by collectLastValue(footerViewModel.clearAllButton.isVisible) activeNotificationListRepository.notifStats.value = NotifStats( Loading @@ -166,13 +166,13 @@ class FooterViewModelTest : SysuiTestCase() { ) runCurrent() assertThat(button?.isVisible?.value).isFalse() assertThat(visible?.value).isFalse() } @Test fun testClearAllButtonAnimating_whenShadeExpandedAndTouchable() = testComponent.runTest { val button by collectLastValue(footerViewModel.clearAllButton) val visible by collectLastValue(footerViewModel.clearAllButton.isVisible) runCurrent() // WHEN shade is expanded Loading Loading @@ -200,13 +200,13 @@ class FooterViewModelTest : SysuiTestCase() { runCurrent() // THEN button visibility should animate assertThat(button?.isVisible?.isAnimating).isTrue() assertThat(visible?.isAnimating).isTrue() } @Test fun testClearAllButtonAnimating_whenShadeNotExpanded() = testComponent.runTest { val button by collectLastValue(footerViewModel.clearAllButton) val visible by collectLastValue(footerViewModel.clearAllButton.isVisible) runCurrent() // WHEN shade is collapsed Loading Loading @@ -234,6 +234,6 @@ class FooterViewModelTest : SysuiTestCase() { runCurrent() // THEN button visibility should not animate assertThat(button?.isVisible?.isAnimating).isFalse() assertThat(visible?.isAnimating).isFalse() } } Loading
packages/SystemUI/src/com/android/systemui/statusbar/notification/footer/ui/viewbinder/FooterViewBinder.kt +17 −13 Original line number Diff line number Diff line Loading @@ -34,34 +34,38 @@ object FooterViewBinder { viewModel: FooterViewModel, clearAllNotifications: View.OnClickListener, ): DisposableHandle { // Listen for changes when the view is attached. // Bind the resource IDs footer.setMessageString(viewModel.message.messageId) footer.setMessageIcon(viewModel.message.iconId) footer.setClearAllButtonText(viewModel.clearAllButton.labelId) footer.setClearAllButtonDescription(viewModel.clearAllButton.accessibilityDescriptionId) // Bind the click listeners footer.setClearAllButtonClickListener(clearAllNotifications) // Listen for visibility changes when the view is attached. return footer.repeatWhenAttached { lifecycleScope.launch { viewModel.clearAllButton.collect { button -> if (button.isVisible.isAnimating) { viewModel.clearAllButton.isVisible.collect { isVisible -> if (isVisible.isAnimating) { footer.setClearAllButtonVisible( button.isVisible.value, isVisible.value, /* animate = */ true, ) { _ -> button.isVisible.stopAnimating() isVisible.stopAnimating() } } else { footer.setClearAllButtonVisible( button.isVisible.value, isVisible.value, /* animate = */ false, ) } footer.setClearAllButtonText(button.labelId) footer.setClearAllButtonDescription(button.accessibilityDescriptionId) footer.setClearAllButtonClickListener(clearAllNotifications) } } lifecycleScope.launch { viewModel.message.collect { message -> footer.setFooterLabelVisible(message.visible) footer.setMessageString(message.messageId) footer.setMessageIcon(message.iconId) viewModel.message.isVisible.collect { visible -> footer.setFooterLabelVisible(visible) } } } Loading
packages/SystemUI/src/com/android/systemui/statusbar/notification/footer/ui/viewmodel/FooterButtonViewModel.kt +2 −1 Original line number Diff line number Diff line Loading @@ -18,9 +18,10 @@ package com.android.systemui.statusbar.notification.footer.ui.viewmodel import android.annotation.StringRes import com.android.systemui.util.ui.AnimatedValue import kotlinx.coroutines.flow.Flow data class FooterButtonViewModel( @StringRes val labelId: Int, @StringRes val accessibilityDescriptionId: Int, val isVisible: AnimatedValue<Boolean>, val isVisible: Flow<AnimatedValue<Boolean>>, )
packages/SystemUI/src/com/android/systemui/statusbar/notification/footer/ui/viewmodel/FooterMessageViewModel.kt +2 −1 Original line number Diff line number Diff line Loading @@ -18,10 +18,11 @@ package com.android.systemui.statusbar.notification.footer.ui.viewmodel import android.annotation.DrawableRes import android.annotation.StringRes import kotlinx.coroutines.flow.StateFlow /** A ViewModel for the string message that can be shown in the footer. */ data class FooterMessageViewModel( @StringRes val messageId: Int, @DrawableRes val iconId: Int, val visible: Boolean, val isVisible: StateFlow<Boolean>, )
packages/SystemUI/src/com/android/systemui/statusbar/notification/footer/ui/viewmodel/FooterViewModel.kt +25 −31 Original line number Diff line number Diff line Loading @@ -30,9 +30,7 @@ import dagger.Module import dagger.Provides import java.util.Optional import javax.inject.Provider import kotlinx.coroutines.flow.Flow import kotlinx.coroutines.flow.combine import kotlinx.coroutines.flow.map import kotlinx.coroutines.flow.onStart /** ViewModel for [FooterView]. */ Loading @@ -41,7 +39,11 @@ class FooterViewModel( seenNotificationsInteractor: SeenNotificationsInteractor, shadeInteractor: ShadeInteractor, ) { val clearAllButton: Flow<FooterButtonViewModel> = val clearAllButton: FooterButtonViewModel = FooterButtonViewModel( labelId = R.string.clear_all_notifications_text, accessibilityDescriptionId = R.string.accessibility_clear_all, isVisible = activeNotificationsInteractor.hasClearableNotifications .sample( combine( Loading @@ -54,24 +56,16 @@ class FooterViewModel( val shouldAnimate = isShadeFullyExpanded && animationsEnabled AnimatableEvent(hasClearableNotifications, shouldAnimate) } .toAnimatedValueFlow() .map { visible -> FooterButtonViewModel( labelId = R.string.clear_all_notifications_text, accessibilityDescriptionId = R.string.accessibility_clear_all, isVisible = visible, .toAnimatedValueFlow(), ) } val message: Flow<FooterMessageViewModel> = seenNotificationsInteractor.hasFilteredOutSeenNotifications.map { hasFilteredOutNotifs -> val message: FooterMessageViewModel = FooterMessageViewModel( messageId = R.string.unlock_to_see_notif_text, iconId = R.drawable.ic_friction_lock_closed, visible = hasFilteredOutNotifs, isVisible = seenNotificationsInteractor.hasFilteredOutSeenNotifications, ) } } @Module object FooterViewModelModule { Loading
packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/footer/ui/viewmodel/FooterViewModelTest.kt +12 −12 Original line number Diff line number Diff line Loading @@ -116,27 +116,27 @@ class FooterViewModelTest : SysuiTestCase() { @Test fun testMessageVisible_whenFilteredNotifications() = testComponent.runTest { val message by collectLastValue(footerViewModel.message) val visible by collectLastValue(footerViewModel.message.isVisible) activeNotificationListRepository.hasFilteredOutSeenNotifications.value = true assertThat(message?.visible).isTrue() assertThat(visible).isTrue() } @Test fun testMessageVisible_whenNoFilteredNotifications() = testComponent.runTest { val message by collectLastValue(footerViewModel.message) val visible by collectLastValue(footerViewModel.message.isVisible) activeNotificationListRepository.hasFilteredOutSeenNotifications.value = false assertThat(message?.visible).isFalse() assertThat(visible).isFalse() } @Test fun testClearAllButtonVisible_whenHasClearableNotifs() = testComponent.runTest { val button by collectLastValue(footerViewModel.clearAllButton) val visible by collectLastValue(footerViewModel.clearAllButton.isVisible) activeNotificationListRepository.notifStats.value = NotifStats( Loading @@ -148,13 +148,13 @@ class FooterViewModelTest : SysuiTestCase() { ) runCurrent() assertThat(button?.isVisible?.value).isTrue() assertThat(visible?.value).isTrue() } @Test fun testClearAllButtonVisible_whenHasNoClearableNotifs() = testComponent.runTest { val button by collectLastValue(footerViewModel.clearAllButton) val visible by collectLastValue(footerViewModel.clearAllButton.isVisible) activeNotificationListRepository.notifStats.value = NotifStats( Loading @@ -166,13 +166,13 @@ class FooterViewModelTest : SysuiTestCase() { ) runCurrent() assertThat(button?.isVisible?.value).isFalse() assertThat(visible?.value).isFalse() } @Test fun testClearAllButtonAnimating_whenShadeExpandedAndTouchable() = testComponent.runTest { val button by collectLastValue(footerViewModel.clearAllButton) val visible by collectLastValue(footerViewModel.clearAllButton.isVisible) runCurrent() // WHEN shade is expanded Loading Loading @@ -200,13 +200,13 @@ class FooterViewModelTest : SysuiTestCase() { runCurrent() // THEN button visibility should animate assertThat(button?.isVisible?.isAnimating).isTrue() assertThat(visible?.isAnimating).isTrue() } @Test fun testClearAllButtonAnimating_whenShadeNotExpanded() = testComponent.runTest { val button by collectLastValue(footerViewModel.clearAllButton) val visible by collectLastValue(footerViewModel.clearAllButton.isVisible) runCurrent() // WHEN shade is collapsed Loading Loading @@ -234,6 +234,6 @@ class FooterViewModelTest : SysuiTestCase() { runCurrent() // THEN button visibility should not animate assertThat(button?.isVisible?.isAnimating).isFalse() assertThat(visible?.isAnimating).isFalse() } }