summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/notification/stack/ui/viewmodel/NotificationListViewModelTest.kt53
-rw-r--r--packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/StackScrollAlgorithm.java6
-rw-r--r--packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/ui/viewbinder/NotificationListViewBinder.kt25
-rw-r--r--packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/ui/viewmodel/NotificationListViewModel.kt73
4 files changed, 135 insertions, 22 deletions
diff --git a/packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/notification/stack/ui/viewmodel/NotificationListViewModelTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/notification/stack/ui/viewmodel/NotificationListViewModelTest.kt
index 840aa92548c8..26e1a4d9e961 100644
--- a/packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/notification/stack/ui/viewmodel/NotificationListViewModelTest.kt
+++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/statusbar/notification/stack/ui/viewmodel/NotificationListViewModelTest.kt
@@ -26,6 +26,7 @@ import androidx.test.filters.SmallTest
import com.android.settingslib.notification.data.repository.updateNotificationPolicy
import com.android.systemui.SysuiTestCase
import com.android.systemui.coroutines.collectLastValue
+import com.android.systemui.flags.DisableSceneContainer
import com.android.systemui.flags.EnableSceneContainer
import com.android.systemui.flags.Flags
import com.android.systemui.flags.andSceneContainer
@@ -36,6 +37,7 @@ import com.android.systemui.kosmos.testScope
import com.android.systemui.power.data.repository.fakePowerRepository
import com.android.systemui.power.shared.model.WakefulnessState
import com.android.systemui.res.R
+import com.android.systemui.scene.shared.flag.SceneContainerFlag
import com.android.systemui.shade.shadeTestUtil
import com.android.systemui.statusbar.data.repository.fakeRemoteInputRepository
import com.android.systemui.statusbar.notification.data.repository.FakeHeadsUpRowRepository
@@ -51,6 +53,7 @@ import com.android.systemui.util.ui.isAnimating
import com.android.systemui.util.ui.value
import com.google.common.truth.Truth.assertThat
import kotlinx.coroutines.ExperimentalCoroutinesApi
+import kotlinx.coroutines.test.TestScope
import kotlinx.coroutines.test.runCurrent
import kotlinx.coroutines.test.runTest
import org.junit.Before
@@ -153,7 +156,7 @@ class NotificationListViewModelTest(flags: FlagsParameterization) : SysuiTestCas
fun shouldShowEmptyShadeView_trueWhenNoNotifs() =
testScope.runTest {
val shouldShowEmptyShadeView by collectLastValue(underTest.shouldShowEmptyShadeView)
- val shouldIncludeFooterView by collectLastValue(underTest.shouldIncludeFooterView)
+ val shouldIncludeFooterView by collectFooterViewVisibility()
// WHEN has no notifs
activeNotificationListRepository.setActiveNotifs(count = 0)
@@ -196,7 +199,7 @@ class NotificationListViewModelTest(flags: FlagsParameterization) : SysuiTestCas
fun shouldShowEmptyShadeView_trueWhenQsExpandedInSplitShade() =
testScope.runTest {
val shouldShowEmptyShadeView by collectLastValue(underTest.shouldShowEmptyShadeView)
- val shouldIncludeFooterView by collectLastValue(underTest.shouldIncludeFooterView)
+ val shouldIncludeFooterView by collectFooterViewVisibility()
// WHEN has no notifs
activeNotificationListRepository.setActiveNotifs(count = 0)
@@ -217,7 +220,7 @@ class NotificationListViewModelTest(flags: FlagsParameterization) : SysuiTestCas
fun shouldShowEmptyShadeView_trueWhenLockedShade() =
testScope.runTest {
val shouldShowEmptyShadeView by collectLastValue(underTest.shouldShowEmptyShadeView)
- val shouldIncludeFooterView by collectLastValue(underTest.shouldIncludeFooterView)
+ val shouldIncludeFooterView by collectFooterViewVisibility()
// WHEN has no notifs
activeNotificationListRepository.setActiveNotifs(count = 0)
@@ -315,7 +318,7 @@ class NotificationListViewModelTest(flags: FlagsParameterization) : SysuiTestCas
@Test
fun shouldIncludeFooterView_trueWhenShade() =
testScope.runTest {
- val shouldIncludeFooterView by collectLastValue(underTest.shouldIncludeFooterView)
+ val shouldIncludeFooterView by collectFooterViewVisibility()
val shouldShowEmptyShadeView by collectLastValue(underTest.shouldShowEmptyShadeView)
// WHEN has notifs
@@ -333,7 +336,7 @@ class NotificationListViewModelTest(flags: FlagsParameterization) : SysuiTestCas
@Test
fun shouldIncludeFooterView_trueWhenLockedShade() =
testScope.runTest {
- val shouldIncludeFooterView by collectLastValue(underTest.shouldIncludeFooterView)
+ val shouldIncludeFooterView by collectFooterViewVisibility()
val shouldShowEmptyShadeView by collectLastValue(underTest.shouldShowEmptyShadeView)
// WHEN has notifs
@@ -351,7 +354,7 @@ class NotificationListViewModelTest(flags: FlagsParameterization) : SysuiTestCas
@Test
fun shouldIncludeFooterView_falseWhenKeyguard() =
testScope.runTest {
- val shouldInclude by collectLastValue(underTest.shouldIncludeFooterView)
+ val shouldInclude by collectFooterViewVisibility()
// WHEN has notifs
activeNotificationListRepository.setActiveNotifs(count = 2)
@@ -366,7 +369,7 @@ class NotificationListViewModelTest(flags: FlagsParameterization) : SysuiTestCas
@Test
fun shouldIncludeFooterView_falseWhenUserNotSetUp() =
testScope.runTest {
- val shouldInclude by collectLastValue(underTest.shouldIncludeFooterView)
+ val shouldInclude by collectFooterViewVisibility()
// WHEN has notifs
activeNotificationListRepository.setActiveNotifs(count = 2)
@@ -384,7 +387,7 @@ class NotificationListViewModelTest(flags: FlagsParameterization) : SysuiTestCas
@Test
fun shouldIncludeFooterView_falseWhenStartingToSleep() =
testScope.runTest {
- val shouldInclude by collectLastValue(underTest.shouldIncludeFooterView)
+ val shouldInclude by collectFooterViewVisibility()
// WHEN has notifs
activeNotificationListRepository.setActiveNotifs(count = 2)
@@ -402,7 +405,7 @@ class NotificationListViewModelTest(flags: FlagsParameterization) : SysuiTestCas
@Test
fun shouldIncludeFooterView_falseWhenQsExpandedDefault() =
testScope.runTest {
- val shouldInclude by collectLastValue(underTest.shouldIncludeFooterView)
+ val shouldInclude by collectFooterViewVisibility()
// WHEN has notifs
activeNotificationListRepository.setActiveNotifs(count = 2)
@@ -421,7 +424,7 @@ class NotificationListViewModelTest(flags: FlagsParameterization) : SysuiTestCas
@Test
fun shouldIncludeFooterView_trueWhenQsExpandedSplitShade() =
testScope.runTest {
- val shouldIncludeFooterView by collectLastValue(underTest.shouldIncludeFooterView)
+ val shouldIncludeFooterView by collectFooterViewVisibility()
val shouldShowEmptyShadeView by collectLastValue(underTest.shouldShowEmptyShadeView)
// WHEN has notifs
@@ -444,7 +447,7 @@ class NotificationListViewModelTest(flags: FlagsParameterization) : SysuiTestCas
@Test
fun shouldIncludeFooterView_falseWhenRemoteInputActive() =
testScope.runTest {
- val shouldInclude by collectLastValue(underTest.shouldIncludeFooterView)
+ val shouldInclude by collectFooterViewVisibility()
// WHEN has notifs
activeNotificationListRepository.setActiveNotifs(count = 2)
@@ -462,7 +465,7 @@ class NotificationListViewModelTest(flags: FlagsParameterization) : SysuiTestCas
@Test
fun shouldIncludeFooterView_animatesWhenShade() =
testScope.runTest {
- val shouldInclude by collectLastValue(underTest.shouldIncludeFooterView)
+ val shouldInclude by collectFooterViewVisibility()
// WHEN has notifs
activeNotificationListRepository.setActiveNotifs(count = 2)
@@ -478,7 +481,7 @@ class NotificationListViewModelTest(flags: FlagsParameterization) : SysuiTestCas
@Test
fun shouldIncludeFooterView_notAnimatingOnKeyguard() =
testScope.runTest {
- val shouldInclude by collectLastValue(underTest.shouldIncludeFooterView)
+ val shouldInclude by collectFooterViewVisibility()
// WHEN has notifs
activeNotificationListRepository.setActiveNotifs(count = 2)
@@ -492,6 +495,22 @@ class NotificationListViewModelTest(flags: FlagsParameterization) : SysuiTestCas
}
@Test
+ @EnableSceneContainer
+ fun shouldShowFooterView_falseWhenShadeIsClosed() =
+ testScope.runTest {
+ val shouldShow by collectLastValue(underTest.shouldShowFooterView)
+
+ // WHEN shade is closed
+ fakeKeyguardRepository.setStatusBarState(StatusBarState.SHADE)
+ shadeTestUtil.setShadeExpansion(0f)
+ runCurrent()
+
+ // THEN footer is hidden
+ assertThat(shouldShow?.value).isFalse()
+ }
+
+ @Test
+ @DisableSceneContainer
fun shouldHideFooterView_trueWhenShadeIsClosed() =
testScope.runTest {
val shouldHide by collectLastValue(underTest.shouldHideFooterView)
@@ -506,6 +525,7 @@ class NotificationListViewModelTest(flags: FlagsParameterization) : SysuiTestCas
}
@Test
+ @DisableSceneContainer
fun shouldHideFooterView_falseWhenShadeIsOpen() =
testScope.runTest {
val shouldHide by collectLastValue(underTest.shouldHideFooterView)
@@ -520,6 +540,7 @@ class NotificationListViewModelTest(flags: FlagsParameterization) : SysuiTestCas
}
@Test
+ @DisableSceneContainer
fun shouldHideFooterView_falseWhenQSPartiallyOpen() =
testScope.runTest {
val shouldHide by collectLastValue(underTest.shouldHideFooterView)
@@ -642,4 +663,10 @@ class NotificationListViewModelTest(flags: FlagsParameterization) : SysuiTestCas
assertThat(animationsEnabled).isTrue()
}
+
+ private fun TestScope.collectFooterViewVisibility() =
+ collectLastValue(
+ if (SceneContainerFlag.isEnabled) underTest.shouldShowFooterView
+ else underTest.shouldIncludeFooterView
+ )
}
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/StackScrollAlgorithm.java b/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/StackScrollAlgorithm.java
index ef1bcfc45879..cccac4b479dd 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/StackScrollAlgorithm.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/StackScrollAlgorithm.java
@@ -682,7 +682,10 @@ public class StackScrollAlgorithm {
// doesn't get updated quickly enough and can cause the footer to flash when
// closing the shade. As such, we temporarily also check the ambientState directly.
if (((FooterView) view).shouldBeHidden() || !ambientState.isShadeExpanded()) {
- viewState.hidden = true;
+ // Note: This is no longer necessary in flexiglass.
+ if (!SceneContainerFlag.isEnabled()) {
+ viewState.hidden = true;
+ }
} else {
final float footerEnd = algorithmState.mCurrentExpandedYPosition
+ view.getIntrinsicHeight();
@@ -691,7 +694,6 @@ public class StackScrollAlgorithm {
noSpaceForFooter || (ambientState.isClearAllInProgress()
&& !hasNonClearableNotifs(algorithmState));
}
-
} else {
final boolean shadeClosed = !ambientState.isShadeExpanded();
final boolean isShelfShowing = algorithmState.firstViewInShelf != null;
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/ui/viewbinder/NotificationListViewBinder.kt b/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/ui/viewbinder/NotificationListViewBinder.kt
index d770b2003f3b..dc9615c25ada 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/ui/viewbinder/NotificationListViewBinder.kt
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/ui/viewbinder/NotificationListViewBinder.kt
@@ -188,15 +188,26 @@ constructor(
.startHistoryIntent(view, /* showHistory= */ true)
},
)
- launch {
- viewModel.shouldIncludeFooterView.collect { animatedVisibility ->
- footerView.setVisible(
- /* visible = */ animatedVisibility.value,
- /* animate = */ animatedVisibility.isAnimating,
- )
+ if (SceneContainerFlag.isEnabled) {
+ launch {
+ viewModel.shouldShowFooterView.collect { animatedVisibility ->
+ footerView.setVisible(
+ /* visible = */ animatedVisibility.value,
+ /* animate = */ animatedVisibility.isAnimating,
+ )
+ }
+ }
+ } else {
+ launch {
+ viewModel.shouldIncludeFooterView.collect { animatedVisibility ->
+ footerView.setVisible(
+ /* visible = */ animatedVisibility.value,
+ /* animate = */ animatedVisibility.isAnimating,
+ )
+ }
}
+ launch { viewModel.shouldHideFooterView.collect { footerView.setShouldBeHidden(it) } }
}
- launch { viewModel.shouldHideFooterView.collect { footerView.setShouldBeHidden(it) } }
disposableHandle.awaitCancellationThenDispose()
}
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/ui/viewmodel/NotificationListViewModel.kt b/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/ui/viewmodel/NotificationListViewModel.kt
index e55492e67d02..4e2a46d78a5d 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/ui/viewmodel/NotificationListViewModel.kt
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/ui/viewmodel/NotificationListViewModel.kt
@@ -32,6 +32,7 @@ import com.android.systemui.statusbar.notification.stack.domain.interactor.Notif
import com.android.systemui.statusbar.policy.domain.interactor.UserSetupInteractor
import com.android.systemui.statusbar.policy.domain.interactor.ZenModeInteractor
import com.android.systemui.util.kotlin.FlowDumperImpl
+import com.android.systemui.util.kotlin.combine
import com.android.systemui.util.kotlin.sample
import com.android.systemui.util.ui.AnimatableEvent
import com.android.systemui.util.ui.AnimatedValue
@@ -120,6 +121,7 @@ constructor(
* This essentially corresponds to having the view set to INVISIBLE.
*/
val shouldHideFooterView: Flow<Boolean> by lazy {
+ SceneContainerFlag.assertInLegacyMode()
if (FooterViewRefactor.isUnexpectedlyInLegacyMode()) {
flowOf(false)
} else {
@@ -143,6 +145,7 @@ constructor(
* be hidden by another condition (see [shouldHideFooterView] above).
*/
val shouldIncludeFooterView: Flow<AnimatedValue<Boolean>> by lazy {
+ SceneContainerFlag.assertInLegacyMode()
if (FooterViewRefactor.isUnexpectedlyInLegacyMode()) {
flowOf(AnimatedValue.NotAnimating(false))
} else {
@@ -207,6 +210,76 @@ constructor(
}
}
+ // This flow replaces shouldHideFooterView+shouldIncludeFooterView in flexiglass.
+ val shouldShowFooterView: Flow<AnimatedValue<Boolean>> by lazy {
+ if (SceneContainerFlag.isUnexpectedlyInLegacyMode()) {
+ flowOf(AnimatedValue.NotAnimating(false))
+ } else {
+ combine(
+ activeNotificationsInteractor.areAnyNotificationsPresent,
+ userSetupInteractor.isUserSetUp,
+ notificationStackInteractor.isShowingOnLockscreen,
+ shadeInteractor.isQsFullscreen,
+ remoteInputInteractor.isRemoteInputActive,
+ shadeInteractor.shadeExpansion.map { it < 0.5f }.distinctUntilChanged(),
+ ) {
+ hasNotifications,
+ isUserSetUp,
+ isShowingOnLockscreen,
+ qsFullScreen,
+ isRemoteInputActive,
+ shadeLessThanHalfwayExpanded ->
+ when {
+ !hasNotifications -> VisibilityChange.DISAPPEAR_WITH_ANIMATION
+ // Hide the footer until the user setup is complete, to prevent access
+ // to settings (b/193149550).
+ !isUserSetUp -> VisibilityChange.DISAPPEAR_WITH_ANIMATION
+ // Do not show the footer if the lockscreen is visible (incl. AOD),
+ // except if the shade is opened on top. See also b/219680200.
+ // Do not animate, as that makes the footer appear briefly when
+ // transitioning between the shade and keyguard.
+ isShowingOnLockscreen -> VisibilityChange.DISAPPEAR_WITHOUT_ANIMATION
+ // Do not show the footer if quick settings are fully expanded (except
+ // for the foldable split shade view). See b/201427195 && b/222699879.
+ qsFullScreen -> VisibilityChange.DISAPPEAR_WITH_ANIMATION
+ // Hide the footer if remote input is active (i.e. user is replying to a
+ // notification). See b/75984847.
+ isRemoteInputActive -> VisibilityChange.DISAPPEAR_WITH_ANIMATION
+ // If the shade is not expanded enough, the footer shouldn't be visible.
+ shadeLessThanHalfwayExpanded -> VisibilityChange.DISAPPEAR_WITH_ANIMATION
+ else -> VisibilityChange.APPEAR_WITH_ANIMATION
+ }
+ }
+ .distinctUntilChanged(
+ // Equivalent unless visibility changes
+ areEquivalent = { a: VisibilityChange, b: VisibilityChange ->
+ a.visible == b.visible
+ }
+ )
+ // Should we animate the visibility change?
+ .sample(
+ // TODO(b/322167853): This check is currently duplicated in FooterViewModel,
+ // but instead it should be a field in ShadeAnimationInteractor.
+ combine(
+ shadeInteractor.isShadeFullyExpanded,
+ shadeInteractor.isShadeTouchable,
+ ::Pair
+ )
+ .onStart { emit(Pair(false, false)) }
+ ) { visibilityChange, (isShadeFullyExpanded, animationsEnabled) ->
+ // Animate if the shade is interactive, but NOT on the lockscreen. Having
+ // animations enabled while on the lockscreen makes the footer appear briefly
+ // when transitioning between the shade and keyguard.
+ val shouldAnimate =
+ isShadeFullyExpanded && animationsEnabled && visibilityChange.canAnimate
+ AnimatableEvent(visibilityChange.visible, shouldAnimate)
+ }
+ .toAnimatedValueFlow()
+ .dumpWhileCollecting("shouldShowFooterView")
+ .flowOn(bgDispatcher)
+ }
+ }
+
enum class VisibilityChange(val visible: Boolean, val canAnimate: Boolean) {
DISAPPEAR_WITHOUT_ANIMATION(visible = false, canAnimate = false),
DISAPPEAR_WITH_ANIMATION(visible = false, canAnimate = true),