Loading packages/SystemUI/multivalentTests/src/com/android/systemui/shade/domain/interactor/PanelExpansionInteractorImplTest.kt +189 −2 Original line number Diff line number Diff line Loading @@ -19,17 +19,24 @@ package com.android.systemui.shade.domain.interactor import androidx.test.ext.junit.runners.AndroidJUnit4 import androidx.test.filters.SmallTest import com.android.compose.animation.scene.ObservableTransitionState import com.android.compose.animation.scene.ObservableTransitionState.Transition.ShowOrHideOverlay import com.android.compose.animation.scene.OverlayKey import com.android.compose.animation.scene.SceneKey import com.android.systemui.SysuiTestCase import com.android.systemui.coroutines.collectLastValue import com.android.systemui.deviceentry.domain.interactor.deviceUnlockedInteractor import com.android.systemui.deviceentry.shared.model.DeviceUnlockSource import com.android.systemui.deviceentry.shared.model.DeviceUnlockStatus import com.android.systemui.flags.EnableSceneContainer import com.android.systemui.keyguard.data.repository.fakeDeviceEntryFingerprintAuthRepository import com.android.systemui.keyguard.shared.model.SuccessFingerprintAuthenticationStatus import com.android.systemui.kosmos.testScope import com.android.systemui.scene.domain.interactor.sceneInteractor import com.android.systemui.scene.shared.model.Overlays import com.android.systemui.scene.shared.model.Scenes import com.android.systemui.scene.shared.model.fakeSceneDataSource import com.android.systemui.testKosmos import com.google.common.truth.Truth.assertThat import kotlinx.coroutines.ExperimentalCoroutinesApi import kotlinx.coroutines.flow.MutableStateFlow import kotlinx.coroutines.flow.flowOf import kotlinx.coroutines.test.TestScope Loading @@ -39,13 +46,13 @@ import org.junit.Before import org.junit.Test import org.junit.runner.RunWith @OptIn(ExperimentalCoroutinesApi::class) @SmallTest @RunWith(AndroidJUnit4::class) class PanelExpansionInteractorImplTest : SysuiTestCase() { private val kosmos = testKosmos() private val testScope = kosmos.testScope private val deviceUnlockedInteractor by lazy { kosmos.deviceUnlockedInteractor } private val sceneInteractor by lazy { kosmos.sceneInteractor } private val shadeAnimationInteractor by lazy { kosmos.shadeAnimationInteractor } private val transitionState = Loading @@ -61,6 +68,118 @@ class PanelExpansionInteractorImplTest : SysuiTestCase() { sceneInteractor.setTransitionState(transitionState) } @Test @EnableSceneContainer fun legacyPanelExpansion_whenIdle_whenLocked() = testScope.runTest { underTest = kosmos.panelExpansionInteractorImpl val panelExpansion by collectLastValue(underTest.legacyPanelExpansion) changeScene(Scenes.Lockscreen) { assertThat(panelExpansion).isEqualTo(1f) } assertThat(panelExpansion).isEqualTo(1f) showOverlay(Overlays.Bouncer) { assertThat(panelExpansion).isEqualTo(1f) } assertThat(panelExpansion).isEqualTo(1f) changeScene(Scenes.Shade) { assertThat(panelExpansion).isEqualTo(1f) } assertThat(panelExpansion).isEqualTo(1f) changeScene(Scenes.QuickSettings) { assertThat(panelExpansion).isEqualTo(1f) } assertThat(panelExpansion).isEqualTo(1f) changeScene(Scenes.Communal) { assertThat(panelExpansion).isEqualTo(1f) } assertThat(panelExpansion).isEqualTo(1f) } @Test @EnableSceneContainer fun legacyPanelExpansion_whenIdle_whenUnlocked() = testScope.runTest { underTest = kosmos.panelExpansionInteractorImpl val unlockStatus by collectLastValue(deviceUnlockedInteractor.deviceUnlockStatus) kosmos.fakeDeviceEntryFingerprintAuthRepository.setAuthenticationStatus( SuccessFingerprintAuthenticationStatus(0, true) ) runCurrent() assertThat(unlockStatus) .isEqualTo(DeviceUnlockStatus(true, DeviceUnlockSource.Fingerprint)) val panelExpansion by collectLastValue(underTest.legacyPanelExpansion) changeScene(Scenes.Gone) { assertThat(panelExpansion).isEqualTo(0f) } assertThat(panelExpansion).isEqualTo(0f) changeScene(Scenes.Shade) { progress -> assertThat(panelExpansion).isEqualTo(progress) } assertThat(panelExpansion).isEqualTo(1f) changeScene(Scenes.QuickSettings) { // Shade's already expanded, so moving to QS should also be 1f. assertThat(panelExpansion).isEqualTo(1f) } assertThat(panelExpansion).isEqualTo(1f) changeScene(Scenes.Communal) { assertThat(panelExpansion).isEqualTo(1f) } assertThat(panelExpansion).isEqualTo(1f) } @Test @EnableSceneContainer fun legacyPanelExpansion_dualShade_whenIdle_whenLocked() = testScope.runTest { underTest = kosmos.panelExpansionInteractorImpl val panelExpansion by collectLastValue(underTest.legacyPanelExpansion) changeScene(Scenes.Lockscreen) { assertThat(panelExpansion).isEqualTo(1f) } assertThat(panelExpansion).isEqualTo(1f) showOverlay(Overlays.Bouncer) { assertThat(panelExpansion).isEqualTo(1f) } assertThat(panelExpansion).isEqualTo(1f) showOverlay(Overlays.NotificationsShade) { assertThat(panelExpansion).isEqualTo(1f) } assertThat(panelExpansion).isEqualTo(1f) showOverlay(Overlays.QuickSettingsShade) { assertThat(panelExpansion).isEqualTo(1f) } assertThat(panelExpansion).isEqualTo(1f) changeScene(Scenes.Communal) { assertThat(panelExpansion).isEqualTo(1f) } assertThat(panelExpansion).isEqualTo(1f) } @Test @EnableSceneContainer fun legacyPanelExpansion_dualShade_whenIdle_whenUnlocked() = testScope.runTest { underTest = kosmos.panelExpansionInteractorImpl val unlockStatus by collectLastValue(deviceUnlockedInteractor.deviceUnlockStatus) kosmos.fakeDeviceEntryFingerprintAuthRepository.setAuthenticationStatus( SuccessFingerprintAuthenticationStatus(0, true) ) runCurrent() assertThat(unlockStatus) .isEqualTo(DeviceUnlockStatus(true, DeviceUnlockSource.Fingerprint)) val panelExpansion by collectLastValue(underTest.legacyPanelExpansion) changeScene(Scenes.Gone) { assertThat(panelExpansion).isEqualTo(0f) } assertThat(panelExpansion).isEqualTo(0f) showOverlay(Overlays.NotificationsShade) { progress -> assertThat(panelExpansion).isEqualTo(progress) } assertThat(panelExpansion).isEqualTo(1f) showOverlay(Overlays.QuickSettingsShade) { // Notification shade is already expanded, so moving to QS shade should also be 1f. assertThat(panelExpansion).isEqualTo(1f) } assertThat(panelExpansion).isEqualTo(1f) changeScene(Scenes.Communal) { assertThat(panelExpansion).isEqualTo(1f) } assertThat(panelExpansion).isEqualTo(1f) } @Test @EnableSceneContainer fun shouldHideStatusBarIconsWhenExpanded_goneScene() = Loading Loading @@ -131,4 +250,72 @@ class PanelExpansionInteractorImplTest : SysuiTestCase() { assertThat(currentScene).isEqualTo(toScene) } private fun TestScope.showOverlay( toOverlay: OverlayKey, assertDuringProgress: ((progress: Float) -> Unit) = {}, ) { val currentScene by collectLastValue(sceneInteractor.currentScene) val currentOverlays by collectLastValue(sceneInteractor.currentOverlays) val progressFlow = MutableStateFlow(0f) transitionState.value = if (checkNotNull(currentOverlays).isEmpty()) { ShowOrHideOverlay( overlay = toOverlay, fromContent = checkNotNull(currentScene), toContent = toOverlay, currentScene = checkNotNull(currentScene), currentOverlays = flowOf(emptySet()), progress = progressFlow, isInitiatedByUserInput = true, isUserInputOngoing = flowOf(true), previewProgress = flowOf(0f), isInPreviewStage = flowOf(false), ) } else { ObservableTransitionState.Transition.ReplaceOverlay( fromOverlay = checkNotNull(currentOverlays).first(), toOverlay = toOverlay, currentScene = checkNotNull(currentScene), currentOverlays = flowOf(emptySet()), progress = progressFlow, isInitiatedByUserInput = true, isUserInputOngoing = flowOf(true), previewProgress = flowOf(0f), isInPreviewStage = flowOf(false), ) } runCurrent() assertDuringProgress(progressFlow.value) progressFlow.value = 0.2f runCurrent() assertDuringProgress(progressFlow.value) progressFlow.value = 0.6f runCurrent() assertDuringProgress(progressFlow.value) progressFlow.value = 1f runCurrent() assertDuringProgress(progressFlow.value) transitionState.value = ObservableTransitionState.Idle( currentScene = checkNotNull(currentScene), currentOverlays = setOf(toOverlay), ) if (checkNotNull(currentOverlays).isEmpty()) { fakeSceneDataSource.showOverlay(toOverlay) } else { fakeSceneDataSource.replaceOverlay( from = checkNotNull(currentOverlays).first(), to = toOverlay, ) } runCurrent() assertDuringProgress(progressFlow.value) assertThat(currentOverlays).containsExactly(toOverlay) } } packages/SystemUI/multivalentTests/src/com/android/systemui/shade/domain/startable/ShadeStartableTest.kt +20 −62 Original line number Diff line number Diff line Loading @@ -17,13 +17,13 @@ package com.android.systemui.shade.domain.startable import android.platform.test.flag.junit.FlagsParameterization import android.testing.TestableLooper.RunWithLooper import androidx.test.filters.SmallTest import com.android.compose.animation.scene.ObservableTransitionState import com.android.compose.animation.scene.SceneKey import com.android.systemui.SysuiTestCase import com.android.systemui.authentication.data.repository.fakeAuthenticationRepository import com.android.systemui.authentication.shared.model.AuthenticationMethodModel import com.android.systemui.common.ui.data.repository.fakeConfigurationRepository import com.android.systemui.coroutines.collectLastValue import com.android.systemui.display.data.repository.displayStateRepository import com.android.systemui.flags.EnableSceneContainer Loading @@ -31,33 +31,29 @@ import com.android.systemui.flags.parameterizeSceneContainerFlag import com.android.systemui.keyguard.data.repository.fakeDeviceEntryFingerprintAuthRepository import com.android.systemui.keyguard.shared.model.SuccessFingerprintAuthenticationStatus import com.android.systemui.kosmos.testScope import com.android.systemui.res.R import com.android.systemui.scene.domain.interactor.sceneInteractor import com.android.systemui.scene.shared.model.Scenes import com.android.systemui.scene.shared.model.fakeSceneDataSource import com.android.systemui.shade.ShadeExpansionChangeEvent import com.android.systemui.shade.ShadeExpansionListener import com.android.systemui.shade.domain.interactor.disableDualShade import com.android.systemui.shade.domain.interactor.enableDualShade import com.android.systemui.shade.domain.interactor.enableSingleShade import com.android.systemui.shade.domain.interactor.enableSplitShade import com.android.systemui.shade.domain.interactor.shadeMode import com.android.systemui.shade.domain.interactor.shadeModeInteractor import com.android.systemui.shade.shared.model.ShadeMode import com.android.systemui.statusbar.notification.stack.notificationStackScrollLayoutController import com.android.systemui.statusbar.notificationShadeDepthController import com.android.systemui.statusbar.phone.scrimController import com.android.systemui.testKosmos import com.android.systemui.util.mockito.any import com.android.systemui.util.mockito.mock import com.android.systemui.util.mockito.whenever import com.google.common.truth.Truth.assertThat import kotlin.math.max import kotlinx.coroutines.ExperimentalCoroutinesApi import kotlinx.coroutines.flow.MutableStateFlow import kotlinx.coroutines.flow.flowOf import kotlinx.coroutines.test.TestScope import kotlinx.coroutines.test.runCurrent import kotlinx.coroutines.test.runTest import org.junit.Before import org.junit.Test import org.junit.runner.RunWith import org.mockito.kotlin.verify Loading @@ -66,20 +62,16 @@ import platform.test.runner.parameterized.Parameters @OptIn(ExperimentalCoroutinesApi::class) @SmallTest @RunWithLooper(setAsMainLooper = true) @RunWith(ParameterizedAndroidJunit4::class) class ShadeStartableTest(flags: FlagsParameterization) : SysuiTestCase() { private val kosmos = testKosmos() private val testScope = kosmos.testScope private val sceneInteractor by lazy { kosmos.sceneInteractor } private val shadeExpansionStateManager by lazy { kosmos.shadeExpansionStateManager } private val fakeConfigurationRepository by lazy { kosmos.fakeConfigurationRepository } private val fakeSceneDataSource by lazy { kosmos.fakeSceneDataSource } private val shadeDepthController by lazy { kosmos.notificationShadeDepthController } private val shadeExpansionStateManager by lazy { kosmos.shadeExpansionStateManager.also { it.addExpansionListener(shadeDepthController) } } private lateinit var underTest: ShadeStartable private val underTest: ShadeStartable = kosmos.shadeStartable companion object { @JvmStatic Loading @@ -93,50 +85,43 @@ class ShadeStartableTest(flags: FlagsParameterization) : SysuiTestCase() { mSetFlagsRule.setFlagsParameterization(flags) } @Before fun setUp() { underTest = kosmos.shadeStartable } @Test fun hydrateShadeMode_dualShadeDisabled() = testScope.runTest { overrideResource(R.bool.config_use_split_notification_shade, false) kosmos.disableDualShade() val shadeMode by collectLastValue(kosmos.shadeMode) val isShadeLayoutWide by collectLastValue(kosmos.shadeModeInteractor.isShadeLayoutWide) underTest.start() kosmos.enableSingleShade() underTest.start() assertThat(shadeMode).isEqualTo(ShadeMode.Single) assertThat(isShadeLayoutWide).isFalse() kosmos.enableSplitShade() overrideResource(R.bool.config_use_split_notification_shade, true) fakeConfigurationRepository.onAnyConfigurationChange() assertThat(shadeMode).isEqualTo(ShadeMode.Split) assertThat(isShadeLayoutWide).isTrue() kosmos.enableSingleShade() overrideResource(R.bool.config_use_split_notification_shade, false) fakeConfigurationRepository.onAnyConfigurationChange() assertThat(shadeMode).isEqualTo(ShadeMode.Single) assertThat(isShadeLayoutWide).isFalse() } @Test @EnableSceneContainer fun hydrateShadeMode_dualShadeEnabled() = testScope.runTest { overrideResource(R.bool.config_use_split_notification_shade, false) kosmos.enableDualShade() val shadeMode by collectLastValue(kosmos.shadeMode) val isShadeLayoutWide by collectLastValue(kosmos.shadeModeInteractor.isShadeLayoutWide) underTest.start() kosmos.enableDualShade(wideLayout = false) underTest.start() assertThat(shadeMode).isEqualTo(ShadeMode.Dual) assertThat(isShadeLayoutWide).isFalse() kosmos.enableDualShade(wideLayout = true) overrideResource(R.bool.config_use_split_notification_shade, true) fakeConfigurationRepository.onAnyConfigurationChange() assertThat(shadeMode).isEqualTo(ShadeMode.Dual) assertThat(isShadeLayoutWide).isTrue() kosmos.enableDualShade(wideLayout = false) overrideResource(R.bool.config_use_split_notification_shade, false) fakeConfigurationRepository.onAnyConfigurationChange() assertThat(shadeMode).isEqualTo(ShadeMode.Dual) assertThat(isShadeLayoutWide).isFalse() } @Test Loading Loading @@ -178,34 +163,7 @@ class ShadeStartableTest(flags: FlagsParameterization) : SysuiTestCase() { changeScene(Scenes.Shade, transitionState) { progress -> assertThat(latestChangeEvent?.fraction).isEqualTo(progress) assertThat(shadeDepthController.qsPanelExpansion).isZero() assertThat(shadeDepthController.shadeExpansion).isEqualTo(progress) assertThat(shadeDepthController.transitionToFullShadeProgress).isEqualTo(progress) } assertThat(currentScene).isEqualTo(Scenes.Shade) changeScene(Scenes.QuickSettings, transitionState) { progress -> assertThat(latestChangeEvent?.fraction).isEqualTo(1 - progress) assertThat(shadeDepthController.qsPanelExpansion).isEqualTo(progress) assertThat(shadeDepthController.shadeExpansion).isEqualTo(1 - progress) assertThat(shadeDepthController.transitionToFullShadeProgress) .isEqualTo( max( shadeDepthController.qsPanelExpansion, shadeDepthController.shadeExpansion, ) ) } assertThat(currentScene).isEqualTo(Scenes.QuickSettings) changeScene(Scenes.Lockscreen, transitionState) { progress -> assertThat(latestChangeEvent?.fraction).isZero() assertThat(shadeDepthController.qsPanelExpansion).isEqualTo(1 - progress) assertThat(shadeDepthController.shadeExpansion).isZero() assertThat(shadeDepthController.transitionToFullShadeProgress) .isEqualTo(1 - progress) } assertThat(currentScene).isEqualTo(Scenes.Lockscreen) } @Test Loading packages/SystemUI/src/com/android/systemui/shade/NotificationPanelViewController.java +6 −0 Original line number Diff line number Diff line Loading @@ -210,6 +210,7 @@ import dagger.Lazy; import kotlin.Unit; import kotlinx.coroutines.CoroutineDispatcher; import kotlinx.coroutines.flow.Flow; import kotlinx.coroutines.flow.MutableStateFlow; import kotlinx.coroutines.flow.StateFlow; Loading Loading @@ -3122,6 +3123,11 @@ public final class NotificationPanelViewController implements return mShadeRepository.getUdfpsTransitionToFullShadeProgress(); } @Override public Flow<Float> getLegacyPanelExpansion() { return mShadeRepository.getLegacyShadeExpansion(); } @Override public boolean isFullyExpanded() { return mExpandedHeight >= getMaxPanelTransitionDistance(); Loading packages/SystemUI/src/com/android/systemui/shade/ShadeExpansionStateManager.kt +5 −1 Original line number Diff line number Diff line Loading @@ -29,7 +29,11 @@ import com.android.systemui.util.Compile import java.util.concurrent.CopyOnWriteArrayList import javax.inject.Inject /** A class responsible for managing the notification panel's current state. */ /** * A class responsible for managing the notification panel's current state. * * TODO(b/200063118): Make this class the one source of truth for the state of panel expansion. */ @SysUISingleton @Deprecated("Use ShadeInteractor instead") class ShadeExpansionStateManager @Inject constructor() { Loading packages/SystemUI/src/com/android/systemui/shade/ShadeViewControllerEmptyImpl.kt +3 −0 Original line number Diff line number Diff line Loading @@ -25,6 +25,7 @@ import com.android.systemui.statusbar.phone.HeadsUpAppearanceController import java.util.function.Consumer import javax.inject.Inject import kotlinx.coroutines.flow.MutableStateFlow import kotlinx.coroutines.flow.flowOf /** Empty implementation of ShadeViewController for variants with no shade. */ open class ShadeViewControllerEmptyImpl @Inject constructor() : Loading Loading @@ -110,6 +111,8 @@ open class ShadeViewControllerEmptyImpl @Inject constructor() : override val shadeHeadsUpTracker = ShadeHeadsUpTrackerEmptyImpl() override val shadeFoldAnimator = ShadeFoldAnimatorEmptyImpl() @Deprecated("Use SceneInteractor.currentScene instead.") override val legacyPanelExpansion = flowOf(0f) override val udfpsTransitionToFullShadeProgress = MutableStateFlow(0f) } Loading Loading
packages/SystemUI/multivalentTests/src/com/android/systemui/shade/domain/interactor/PanelExpansionInteractorImplTest.kt +189 −2 Original line number Diff line number Diff line Loading @@ -19,17 +19,24 @@ package com.android.systemui.shade.domain.interactor import androidx.test.ext.junit.runners.AndroidJUnit4 import androidx.test.filters.SmallTest import com.android.compose.animation.scene.ObservableTransitionState import com.android.compose.animation.scene.ObservableTransitionState.Transition.ShowOrHideOverlay import com.android.compose.animation.scene.OverlayKey import com.android.compose.animation.scene.SceneKey import com.android.systemui.SysuiTestCase import com.android.systemui.coroutines.collectLastValue import com.android.systemui.deviceentry.domain.interactor.deviceUnlockedInteractor import com.android.systemui.deviceentry.shared.model.DeviceUnlockSource import com.android.systemui.deviceentry.shared.model.DeviceUnlockStatus import com.android.systemui.flags.EnableSceneContainer import com.android.systemui.keyguard.data.repository.fakeDeviceEntryFingerprintAuthRepository import com.android.systemui.keyguard.shared.model.SuccessFingerprintAuthenticationStatus import com.android.systemui.kosmos.testScope import com.android.systemui.scene.domain.interactor.sceneInteractor import com.android.systemui.scene.shared.model.Overlays import com.android.systemui.scene.shared.model.Scenes import com.android.systemui.scene.shared.model.fakeSceneDataSource import com.android.systemui.testKosmos import com.google.common.truth.Truth.assertThat import kotlinx.coroutines.ExperimentalCoroutinesApi import kotlinx.coroutines.flow.MutableStateFlow import kotlinx.coroutines.flow.flowOf import kotlinx.coroutines.test.TestScope Loading @@ -39,13 +46,13 @@ import org.junit.Before import org.junit.Test import org.junit.runner.RunWith @OptIn(ExperimentalCoroutinesApi::class) @SmallTest @RunWith(AndroidJUnit4::class) class PanelExpansionInteractorImplTest : SysuiTestCase() { private val kosmos = testKosmos() private val testScope = kosmos.testScope private val deviceUnlockedInteractor by lazy { kosmos.deviceUnlockedInteractor } private val sceneInteractor by lazy { kosmos.sceneInteractor } private val shadeAnimationInteractor by lazy { kosmos.shadeAnimationInteractor } private val transitionState = Loading @@ -61,6 +68,118 @@ class PanelExpansionInteractorImplTest : SysuiTestCase() { sceneInteractor.setTransitionState(transitionState) } @Test @EnableSceneContainer fun legacyPanelExpansion_whenIdle_whenLocked() = testScope.runTest { underTest = kosmos.panelExpansionInteractorImpl val panelExpansion by collectLastValue(underTest.legacyPanelExpansion) changeScene(Scenes.Lockscreen) { assertThat(panelExpansion).isEqualTo(1f) } assertThat(panelExpansion).isEqualTo(1f) showOverlay(Overlays.Bouncer) { assertThat(panelExpansion).isEqualTo(1f) } assertThat(panelExpansion).isEqualTo(1f) changeScene(Scenes.Shade) { assertThat(panelExpansion).isEqualTo(1f) } assertThat(panelExpansion).isEqualTo(1f) changeScene(Scenes.QuickSettings) { assertThat(panelExpansion).isEqualTo(1f) } assertThat(panelExpansion).isEqualTo(1f) changeScene(Scenes.Communal) { assertThat(panelExpansion).isEqualTo(1f) } assertThat(panelExpansion).isEqualTo(1f) } @Test @EnableSceneContainer fun legacyPanelExpansion_whenIdle_whenUnlocked() = testScope.runTest { underTest = kosmos.panelExpansionInteractorImpl val unlockStatus by collectLastValue(deviceUnlockedInteractor.deviceUnlockStatus) kosmos.fakeDeviceEntryFingerprintAuthRepository.setAuthenticationStatus( SuccessFingerprintAuthenticationStatus(0, true) ) runCurrent() assertThat(unlockStatus) .isEqualTo(DeviceUnlockStatus(true, DeviceUnlockSource.Fingerprint)) val panelExpansion by collectLastValue(underTest.legacyPanelExpansion) changeScene(Scenes.Gone) { assertThat(panelExpansion).isEqualTo(0f) } assertThat(panelExpansion).isEqualTo(0f) changeScene(Scenes.Shade) { progress -> assertThat(panelExpansion).isEqualTo(progress) } assertThat(panelExpansion).isEqualTo(1f) changeScene(Scenes.QuickSettings) { // Shade's already expanded, so moving to QS should also be 1f. assertThat(panelExpansion).isEqualTo(1f) } assertThat(panelExpansion).isEqualTo(1f) changeScene(Scenes.Communal) { assertThat(panelExpansion).isEqualTo(1f) } assertThat(panelExpansion).isEqualTo(1f) } @Test @EnableSceneContainer fun legacyPanelExpansion_dualShade_whenIdle_whenLocked() = testScope.runTest { underTest = kosmos.panelExpansionInteractorImpl val panelExpansion by collectLastValue(underTest.legacyPanelExpansion) changeScene(Scenes.Lockscreen) { assertThat(panelExpansion).isEqualTo(1f) } assertThat(panelExpansion).isEqualTo(1f) showOverlay(Overlays.Bouncer) { assertThat(panelExpansion).isEqualTo(1f) } assertThat(panelExpansion).isEqualTo(1f) showOverlay(Overlays.NotificationsShade) { assertThat(panelExpansion).isEqualTo(1f) } assertThat(panelExpansion).isEqualTo(1f) showOverlay(Overlays.QuickSettingsShade) { assertThat(panelExpansion).isEqualTo(1f) } assertThat(panelExpansion).isEqualTo(1f) changeScene(Scenes.Communal) { assertThat(panelExpansion).isEqualTo(1f) } assertThat(panelExpansion).isEqualTo(1f) } @Test @EnableSceneContainer fun legacyPanelExpansion_dualShade_whenIdle_whenUnlocked() = testScope.runTest { underTest = kosmos.panelExpansionInteractorImpl val unlockStatus by collectLastValue(deviceUnlockedInteractor.deviceUnlockStatus) kosmos.fakeDeviceEntryFingerprintAuthRepository.setAuthenticationStatus( SuccessFingerprintAuthenticationStatus(0, true) ) runCurrent() assertThat(unlockStatus) .isEqualTo(DeviceUnlockStatus(true, DeviceUnlockSource.Fingerprint)) val panelExpansion by collectLastValue(underTest.legacyPanelExpansion) changeScene(Scenes.Gone) { assertThat(panelExpansion).isEqualTo(0f) } assertThat(panelExpansion).isEqualTo(0f) showOverlay(Overlays.NotificationsShade) { progress -> assertThat(panelExpansion).isEqualTo(progress) } assertThat(panelExpansion).isEqualTo(1f) showOverlay(Overlays.QuickSettingsShade) { // Notification shade is already expanded, so moving to QS shade should also be 1f. assertThat(panelExpansion).isEqualTo(1f) } assertThat(panelExpansion).isEqualTo(1f) changeScene(Scenes.Communal) { assertThat(panelExpansion).isEqualTo(1f) } assertThat(panelExpansion).isEqualTo(1f) } @Test @EnableSceneContainer fun shouldHideStatusBarIconsWhenExpanded_goneScene() = Loading Loading @@ -131,4 +250,72 @@ class PanelExpansionInteractorImplTest : SysuiTestCase() { assertThat(currentScene).isEqualTo(toScene) } private fun TestScope.showOverlay( toOverlay: OverlayKey, assertDuringProgress: ((progress: Float) -> Unit) = {}, ) { val currentScene by collectLastValue(sceneInteractor.currentScene) val currentOverlays by collectLastValue(sceneInteractor.currentOverlays) val progressFlow = MutableStateFlow(0f) transitionState.value = if (checkNotNull(currentOverlays).isEmpty()) { ShowOrHideOverlay( overlay = toOverlay, fromContent = checkNotNull(currentScene), toContent = toOverlay, currentScene = checkNotNull(currentScene), currentOverlays = flowOf(emptySet()), progress = progressFlow, isInitiatedByUserInput = true, isUserInputOngoing = flowOf(true), previewProgress = flowOf(0f), isInPreviewStage = flowOf(false), ) } else { ObservableTransitionState.Transition.ReplaceOverlay( fromOverlay = checkNotNull(currentOverlays).first(), toOverlay = toOverlay, currentScene = checkNotNull(currentScene), currentOverlays = flowOf(emptySet()), progress = progressFlow, isInitiatedByUserInput = true, isUserInputOngoing = flowOf(true), previewProgress = flowOf(0f), isInPreviewStage = flowOf(false), ) } runCurrent() assertDuringProgress(progressFlow.value) progressFlow.value = 0.2f runCurrent() assertDuringProgress(progressFlow.value) progressFlow.value = 0.6f runCurrent() assertDuringProgress(progressFlow.value) progressFlow.value = 1f runCurrent() assertDuringProgress(progressFlow.value) transitionState.value = ObservableTransitionState.Idle( currentScene = checkNotNull(currentScene), currentOverlays = setOf(toOverlay), ) if (checkNotNull(currentOverlays).isEmpty()) { fakeSceneDataSource.showOverlay(toOverlay) } else { fakeSceneDataSource.replaceOverlay( from = checkNotNull(currentOverlays).first(), to = toOverlay, ) } runCurrent() assertDuringProgress(progressFlow.value) assertThat(currentOverlays).containsExactly(toOverlay) } }
packages/SystemUI/multivalentTests/src/com/android/systemui/shade/domain/startable/ShadeStartableTest.kt +20 −62 Original line number Diff line number Diff line Loading @@ -17,13 +17,13 @@ package com.android.systemui.shade.domain.startable import android.platform.test.flag.junit.FlagsParameterization import android.testing.TestableLooper.RunWithLooper import androidx.test.filters.SmallTest import com.android.compose.animation.scene.ObservableTransitionState import com.android.compose.animation.scene.SceneKey import com.android.systemui.SysuiTestCase import com.android.systemui.authentication.data.repository.fakeAuthenticationRepository import com.android.systemui.authentication.shared.model.AuthenticationMethodModel import com.android.systemui.common.ui.data.repository.fakeConfigurationRepository import com.android.systemui.coroutines.collectLastValue import com.android.systemui.display.data.repository.displayStateRepository import com.android.systemui.flags.EnableSceneContainer Loading @@ -31,33 +31,29 @@ import com.android.systemui.flags.parameterizeSceneContainerFlag import com.android.systemui.keyguard.data.repository.fakeDeviceEntryFingerprintAuthRepository import com.android.systemui.keyguard.shared.model.SuccessFingerprintAuthenticationStatus import com.android.systemui.kosmos.testScope import com.android.systemui.res.R import com.android.systemui.scene.domain.interactor.sceneInteractor import com.android.systemui.scene.shared.model.Scenes import com.android.systemui.scene.shared.model.fakeSceneDataSource import com.android.systemui.shade.ShadeExpansionChangeEvent import com.android.systemui.shade.ShadeExpansionListener import com.android.systemui.shade.domain.interactor.disableDualShade import com.android.systemui.shade.domain.interactor.enableDualShade import com.android.systemui.shade.domain.interactor.enableSingleShade import com.android.systemui.shade.domain.interactor.enableSplitShade import com.android.systemui.shade.domain.interactor.shadeMode import com.android.systemui.shade.domain.interactor.shadeModeInteractor import com.android.systemui.shade.shared.model.ShadeMode import com.android.systemui.statusbar.notification.stack.notificationStackScrollLayoutController import com.android.systemui.statusbar.notificationShadeDepthController import com.android.systemui.statusbar.phone.scrimController import com.android.systemui.testKosmos import com.android.systemui.util.mockito.any import com.android.systemui.util.mockito.mock import com.android.systemui.util.mockito.whenever import com.google.common.truth.Truth.assertThat import kotlin.math.max import kotlinx.coroutines.ExperimentalCoroutinesApi import kotlinx.coroutines.flow.MutableStateFlow import kotlinx.coroutines.flow.flowOf import kotlinx.coroutines.test.TestScope import kotlinx.coroutines.test.runCurrent import kotlinx.coroutines.test.runTest import org.junit.Before import org.junit.Test import org.junit.runner.RunWith import org.mockito.kotlin.verify Loading @@ -66,20 +62,16 @@ import platform.test.runner.parameterized.Parameters @OptIn(ExperimentalCoroutinesApi::class) @SmallTest @RunWithLooper(setAsMainLooper = true) @RunWith(ParameterizedAndroidJunit4::class) class ShadeStartableTest(flags: FlagsParameterization) : SysuiTestCase() { private val kosmos = testKosmos() private val testScope = kosmos.testScope private val sceneInteractor by lazy { kosmos.sceneInteractor } private val shadeExpansionStateManager by lazy { kosmos.shadeExpansionStateManager } private val fakeConfigurationRepository by lazy { kosmos.fakeConfigurationRepository } private val fakeSceneDataSource by lazy { kosmos.fakeSceneDataSource } private val shadeDepthController by lazy { kosmos.notificationShadeDepthController } private val shadeExpansionStateManager by lazy { kosmos.shadeExpansionStateManager.also { it.addExpansionListener(shadeDepthController) } } private lateinit var underTest: ShadeStartable private val underTest: ShadeStartable = kosmos.shadeStartable companion object { @JvmStatic Loading @@ -93,50 +85,43 @@ class ShadeStartableTest(flags: FlagsParameterization) : SysuiTestCase() { mSetFlagsRule.setFlagsParameterization(flags) } @Before fun setUp() { underTest = kosmos.shadeStartable } @Test fun hydrateShadeMode_dualShadeDisabled() = testScope.runTest { overrideResource(R.bool.config_use_split_notification_shade, false) kosmos.disableDualShade() val shadeMode by collectLastValue(kosmos.shadeMode) val isShadeLayoutWide by collectLastValue(kosmos.shadeModeInteractor.isShadeLayoutWide) underTest.start() kosmos.enableSingleShade() underTest.start() assertThat(shadeMode).isEqualTo(ShadeMode.Single) assertThat(isShadeLayoutWide).isFalse() kosmos.enableSplitShade() overrideResource(R.bool.config_use_split_notification_shade, true) fakeConfigurationRepository.onAnyConfigurationChange() assertThat(shadeMode).isEqualTo(ShadeMode.Split) assertThat(isShadeLayoutWide).isTrue() kosmos.enableSingleShade() overrideResource(R.bool.config_use_split_notification_shade, false) fakeConfigurationRepository.onAnyConfigurationChange() assertThat(shadeMode).isEqualTo(ShadeMode.Single) assertThat(isShadeLayoutWide).isFalse() } @Test @EnableSceneContainer fun hydrateShadeMode_dualShadeEnabled() = testScope.runTest { overrideResource(R.bool.config_use_split_notification_shade, false) kosmos.enableDualShade() val shadeMode by collectLastValue(kosmos.shadeMode) val isShadeLayoutWide by collectLastValue(kosmos.shadeModeInteractor.isShadeLayoutWide) underTest.start() kosmos.enableDualShade(wideLayout = false) underTest.start() assertThat(shadeMode).isEqualTo(ShadeMode.Dual) assertThat(isShadeLayoutWide).isFalse() kosmos.enableDualShade(wideLayout = true) overrideResource(R.bool.config_use_split_notification_shade, true) fakeConfigurationRepository.onAnyConfigurationChange() assertThat(shadeMode).isEqualTo(ShadeMode.Dual) assertThat(isShadeLayoutWide).isTrue() kosmos.enableDualShade(wideLayout = false) overrideResource(R.bool.config_use_split_notification_shade, false) fakeConfigurationRepository.onAnyConfigurationChange() assertThat(shadeMode).isEqualTo(ShadeMode.Dual) assertThat(isShadeLayoutWide).isFalse() } @Test Loading Loading @@ -178,34 +163,7 @@ class ShadeStartableTest(flags: FlagsParameterization) : SysuiTestCase() { changeScene(Scenes.Shade, transitionState) { progress -> assertThat(latestChangeEvent?.fraction).isEqualTo(progress) assertThat(shadeDepthController.qsPanelExpansion).isZero() assertThat(shadeDepthController.shadeExpansion).isEqualTo(progress) assertThat(shadeDepthController.transitionToFullShadeProgress).isEqualTo(progress) } assertThat(currentScene).isEqualTo(Scenes.Shade) changeScene(Scenes.QuickSettings, transitionState) { progress -> assertThat(latestChangeEvent?.fraction).isEqualTo(1 - progress) assertThat(shadeDepthController.qsPanelExpansion).isEqualTo(progress) assertThat(shadeDepthController.shadeExpansion).isEqualTo(1 - progress) assertThat(shadeDepthController.transitionToFullShadeProgress) .isEqualTo( max( shadeDepthController.qsPanelExpansion, shadeDepthController.shadeExpansion, ) ) } assertThat(currentScene).isEqualTo(Scenes.QuickSettings) changeScene(Scenes.Lockscreen, transitionState) { progress -> assertThat(latestChangeEvent?.fraction).isZero() assertThat(shadeDepthController.qsPanelExpansion).isEqualTo(1 - progress) assertThat(shadeDepthController.shadeExpansion).isZero() assertThat(shadeDepthController.transitionToFullShadeProgress) .isEqualTo(1 - progress) } assertThat(currentScene).isEqualTo(Scenes.Lockscreen) } @Test Loading
packages/SystemUI/src/com/android/systemui/shade/NotificationPanelViewController.java +6 −0 Original line number Diff line number Diff line Loading @@ -210,6 +210,7 @@ import dagger.Lazy; import kotlin.Unit; import kotlinx.coroutines.CoroutineDispatcher; import kotlinx.coroutines.flow.Flow; import kotlinx.coroutines.flow.MutableStateFlow; import kotlinx.coroutines.flow.StateFlow; Loading Loading @@ -3122,6 +3123,11 @@ public final class NotificationPanelViewController implements return mShadeRepository.getUdfpsTransitionToFullShadeProgress(); } @Override public Flow<Float> getLegacyPanelExpansion() { return mShadeRepository.getLegacyShadeExpansion(); } @Override public boolean isFullyExpanded() { return mExpandedHeight >= getMaxPanelTransitionDistance(); Loading
packages/SystemUI/src/com/android/systemui/shade/ShadeExpansionStateManager.kt +5 −1 Original line number Diff line number Diff line Loading @@ -29,7 +29,11 @@ import com.android.systemui.util.Compile import java.util.concurrent.CopyOnWriteArrayList import javax.inject.Inject /** A class responsible for managing the notification panel's current state. */ /** * A class responsible for managing the notification panel's current state. * * TODO(b/200063118): Make this class the one source of truth for the state of panel expansion. */ @SysUISingleton @Deprecated("Use ShadeInteractor instead") class ShadeExpansionStateManager @Inject constructor() { Loading
packages/SystemUI/src/com/android/systemui/shade/ShadeViewControllerEmptyImpl.kt +3 −0 Original line number Diff line number Diff line Loading @@ -25,6 +25,7 @@ import com.android.systemui.statusbar.phone.HeadsUpAppearanceController import java.util.function.Consumer import javax.inject.Inject import kotlinx.coroutines.flow.MutableStateFlow import kotlinx.coroutines.flow.flowOf /** Empty implementation of ShadeViewController for variants with no shade. */ open class ShadeViewControllerEmptyImpl @Inject constructor() : Loading Loading @@ -110,6 +111,8 @@ open class ShadeViewControllerEmptyImpl @Inject constructor() : override val shadeHeadsUpTracker = ShadeHeadsUpTrackerEmptyImpl() override val shadeFoldAnimator = ShadeFoldAnimatorEmptyImpl() @Deprecated("Use SceneInteractor.currentScene instead.") override val legacyPanelExpansion = flowOf(0f) override val udfpsTransitionToFullShadeProgress = MutableStateFlow(0f) } Loading