Donate to e Foundation | Murena handsets with /e/OS | Own a part of Murena! Learn more

Commit 686e0fd0 authored by Danny Burakov's avatar Danny Burakov Committed by Android (Google) Code Review
Browse files

Merge "[flexiglass] Refactor Shade Scene and QS Scene code, removing all StateFlows." into main

parents 3837387c c1ddd351
Loading
Loading
Loading
Loading
+1 −1
Original line number Diff line number Diff line
@@ -294,7 +294,7 @@ private fun ContentScope.QuickSettingsScene(
        }

        // ############# Media ###############
        val isMediaVisible by viewModel.isMediaVisible.collectAsStateWithLifecycle()
        val isMediaVisible = viewModel.isMediaVisible
        val mediaInRow = isMediaVisible && isLandscape()
        val mediaOffset by
            animateSceneDpAsState(value = InQS, key = MediaLandscapeTopOffset, canOverflow = false)
+9 −17
Original line number Diff line number Diff line
@@ -206,8 +206,7 @@ private fun ContentScope.ShadeScene(
    shadeSession: SaveableSession,
    usingCollapsedLandscapeMedia: Boolean,
) {
    val shadeMode by viewModel.shadeMode.collectAsStateWithLifecycle()
    when (shadeMode) {
    when (viewModel.shadeMode) {
        is ShadeMode.Single ->
            SingleShade(
                notificationStackScrollView = notificationStackScrollView,
@@ -261,15 +260,12 @@ private fun ContentScope.SingleShade(
            key = QuickSettings.SharedValues.TilesSquishiness,
            canOverflow = false,
        )
    val isEmptySpaceClickable by viewModel.isEmptySpaceClickable.collectAsStateWithLifecycle()
    val isMediaVisible by viewModel.isMediaVisible.collectAsStateWithLifecycle()
    val isQsEnabled by viewModel.isQsEnabled.collectAsStateWithLifecycle()

    val shouldPunchHoleBehindScrim =
        layoutState.isTransitioningBetween(Scenes.Gone, Scenes.Shade) ||
            layoutState.isTransitioning(from = Scenes.Lockscreen, to = Scenes.Shade)
    // Media is visible and we are in landscape on a small height screen
    val mediaInRow = isMediaVisible && isLandscape()
    val mediaInRow = viewModel.isMediaVisible && isLandscape()
    val mediaOffset by
        animateContentDpAsState(
            value = QuickSettings.SharedValues.MediaOffset.inQqs(mediaInRow),
@@ -322,7 +318,7 @@ private fun ContentScope.SingleShade(
        )
        Layout(
            modifier =
                Modifier.thenIf(isEmptySpaceClickable) {
                Modifier.thenIf(viewModel.isEmptySpaceClickable) {
                    Modifier.clickable { viewModel.onEmptySpaceClicked() }
                },
            content = {
@@ -348,7 +344,7 @@ private fun ContentScope.SingleShade(
                val qqsLayoutPaddingBottom =
                    dimensionResource(id = R.dimen.qqs_layout_padding_bottom)
                ShadeMediaCarousel(
                    isVisible = isMediaVisible,
                    isVisible = viewModel.isMediaVisible,
                    isInRow = mediaInRow,
                    mediaHost = mediaHost,
                    mediaOffsetProvider = mediaOffsetProvider,
@@ -364,7 +360,7 @@ private fun ContentScope.SingleShade(
                                Modifier.padding(bottom = qqsLayoutPaddingBottom)
                            },
                    usingCollapsedLandscapeMedia = usingCollapsedLandscapeMedia,
                    isQsEnabled = isQsEnabled,
                    isQsEnabled = viewModel.isQsEnabled,
                    isInSplitShade = false,
                )

@@ -379,7 +375,7 @@ private fun ContentScope.SingleShade(
                    stackBottomPadding = navBarHeight,
                    supportNestedScrolling = true,
                    onEmptySpaceClick =
                        viewModel::onEmptySpaceClicked.takeIf { isEmptySpaceClickable },
                        viewModel::onEmptySpaceClicked.takeIf { viewModel.isEmptySpaceClickable },
                    modifier =
                        Modifier.layoutId(SingleShadeMeasurePolicy.LayoutId.Notifications)
                            .padding(horizontal = shadeHorizontalPadding),
@@ -416,7 +412,6 @@ private fun ContentScope.SplitShade(
    jankMonitor: InteractionJankMonitor,
) {
    val isCustomizing by viewModel.qsSceneAdapter.isCustomizing.collectAsStateWithLifecycle()
    val isQsEnabled by viewModel.isQsEnabled.collectAsStateWithLifecycle()
    val isCustomizerShowing by
        viewModel.qsSceneAdapter.isCustomizerShowing.collectAsStateWithLifecycle()
    val customizingAnimationDuration by
@@ -472,9 +467,6 @@ private fun ContentScope.SplitShade(
        onDispose { notificationsPlaceholderViewModel.setAlphaForBrightnessMirror(1f) }
    }

    val isEmptySpaceClickable by viewModel.isEmptySpaceClickable.collectAsStateWithLifecycle()
    val isMediaVisible by viewModel.isMediaVisible.collectAsStateWithLifecycle()

    val brightnessMirrorShowingModifier = Modifier.graphicsLayer { alpha = contentAlpha }

    val mediaOffsetProvider = remember {
@@ -555,7 +547,7 @@ private fun ContentScope.SplitShade(
                            }

                            ShadeMediaCarousel(
                                isVisible = isMediaVisible,
                                isVisible = viewModel.isMediaVisible,
                                isInRow = false,
                                mediaHost = mediaHost,
                                mediaOffsetProvider = mediaOffsetProvider,
@@ -570,7 +562,7 @@ private fun ContentScope.SplitShade(
                                                dimensionResource(id = R.dimen.qs_horizontal_margin)
                                        ),
                                carouselController = mediaCarouselController,
                                isQsEnabled = isQsEnabled,
                                isQsEnabled = viewModel.isQsEnabled,
                                isInSplitShade = true,
                            )
                        }
@@ -598,7 +590,7 @@ private fun ContentScope.SplitShade(
                    shouldPunchHoleBehindScrim = false,
                    supportNestedScrolling = false,
                    onEmptySpaceClick =
                        viewModel::onEmptySpaceClicked.takeIf { isEmptySpaceClickable },
                        viewModel::onEmptySpaceClicked.takeIf { viewModel.isEmptySpaceClickable },
                    modifier =
                        Modifier.weight(1f)
                            .fillMaxHeight()
+9 −15
Original line number Diff line number Diff line
@@ -24,6 +24,7 @@ import com.android.systemui.SysuiTestCase
import com.android.systemui.coroutines.collectLastValue
import com.android.systemui.flags.EnableSceneContainer
import com.android.systemui.kosmos.testScope
import com.android.systemui.kosmos.useUnconfinedTestDispatcher
import com.android.systemui.lifecycle.activateIn
import com.android.systemui.media.controls.data.repository.mediaFilterRepository
import com.android.systemui.media.controls.domain.pipeline.interactor.mediaCarouselInteractor
@@ -35,8 +36,8 @@ import com.android.systemui.scene.domain.interactor.sceneInteractor
import com.android.systemui.scene.domain.startable.sceneContainerStartable
import com.android.systemui.scene.shared.model.Scenes
import com.android.systemui.settings.brightness.ui.viewmodel.brightnessMirrorViewModelFactory
import com.android.systemui.shade.data.repository.shadeRepository
import com.android.systemui.shade.domain.interactor.disableDualShade
import com.android.systemui.shade.domain.interactor.enableSplitShade
import com.android.systemui.shade.domain.interactor.shadeModeInteractor
import com.android.systemui.shade.ui.viewmodel.shadeHeaderViewModelFactory
import com.android.systemui.testKosmos
@@ -44,8 +45,6 @@ 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 kotlinx.coroutines.ExperimentalCoroutinesApi
import kotlinx.coroutines.test.runCurrent
import kotlinx.coroutines.test.runTest
import org.junit.Before
import org.junit.Test
@@ -53,14 +52,13 @@ import org.junit.runner.RunWith
import org.mockito.Mockito.times
import org.mockito.Mockito.verify

@OptIn(ExperimentalCoroutinesApi::class)
@SmallTest
@RunWith(AndroidJUnit4::class)
@RunWithLooper
@EnableSceneContainer
class QuickSettingsSceneContentViewModelTest : SysuiTestCase() {

    private val kosmos = testKosmos()
    private val kosmos = testKosmos().useUnconfinedTestDispatcher()
    private val testScope = kosmos.testScope
    private val qsFlexiglassAdapter = FakeQSSceneAdapter({ mock() })
    private val footerActionsViewModel = mock<FooterActionsViewModel>()
@@ -104,31 +102,29 @@ class QuickSettingsSceneContentViewModelTest : SysuiTestCase() {
    @Test
    fun addAndRemoveMedia_mediaVisibilityIsUpdated() =
        testScope.runTest {
            val isMediaVisible by collectLastValue(underTest.isMediaVisible)
            val userMedia = MediaData(active = true)

            assertThat(isMediaVisible).isFalse()
            assertThat(underTest.isMediaVisible).isFalse()

            kosmos.mediaFilterRepository.addCurrentUserMediaEntry(userMedia)

            assertThat(isMediaVisible).isTrue()
            assertThat(underTest.isMediaVisible).isTrue()

            kosmos.mediaFilterRepository.removeCurrentUserMediaEntry(userMedia.instanceId)

            assertThat(isMediaVisible).isFalse()
            assertThat(underTest.isMediaVisible).isFalse()
        }

    @Test
    fun addInactiveMedia_mediaVisibilityIsUpdated() =
        testScope.runTest {
            val isMediaVisible by collectLastValue(underTest.isMediaVisible)
            val userMedia = MediaData(active = false)

            assertThat(isMediaVisible).isFalse()
            assertThat(underTest.isMediaVisible).isFalse()

            kosmos.mediaFilterRepository.addCurrentUserMediaEntry(userMedia)

            assertThat(isMediaVisible).isTrue()
            assertThat(underTest.isMediaVisible).isTrue()
        }

    @Test
@@ -136,9 +132,7 @@ class QuickSettingsSceneContentViewModelTest : SysuiTestCase() {
        testScope.runTest {
            val scene by collectLastValue(sceneInteractor.currentScene)

            // switch to split shade
            kosmos.shadeRepository.setShadeLayoutWide(true)
            runCurrent()
            kosmos.enableSplitShade()

            assertThat(scene).isEqualTo(Scenes.Shade)
        }
+25 −45
Original line number Diff line number Diff line
@@ -33,26 +33,28 @@ 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.kosmos.useUnconfinedTestDispatcher
import com.android.systemui.lifecycle.activateIn
import com.android.systemui.media.controls.data.repository.mediaFilterRepository
import com.android.systemui.media.controls.shared.model.MediaData
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.shade.data.repository.shadeRepository
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.shared.model.ShadeMode
import com.android.systemui.statusbar.disableflags.data.repository.fakeDisableFlagsRepository
import com.android.systemui.testKosmos
import com.android.systemui.unfold.fakeUnfoldTransitionProgressProvider
import com.google.common.truth.Truth.assertThat
import java.util.Locale
import kotlinx.coroutines.flow.MutableStateFlow
import kotlinx.coroutines.flow.combine
import kotlinx.coroutines.flow.flowOf
import kotlinx.coroutines.flow.map
import kotlinx.coroutines.flow.update
import kotlinx.coroutines.test.TestScope
import kotlinx.coroutines.test.runCurrent
import kotlinx.coroutines.test.runTest
import org.junit.Before
import org.junit.Test
@@ -64,10 +66,8 @@ import org.junit.runner.RunWith
@EnableSceneContainer
class ShadeSceneContentViewModelTest : SysuiTestCase() {

    private val kosmos = testKosmos()
    private val kosmos = testKosmos().useUnconfinedTestDispatcher()
    private val testScope = kosmos.testScope
    private val sceneInteractor by lazy { kosmos.sceneInteractor }
    private val shadeRepository by lazy { kosmos.shadeRepository }

    private val underTest: ShadeSceneContentViewModel by lazy { kosmos.shadeSceneContentViewModel }

@@ -80,36 +80,31 @@ class ShadeSceneContentViewModelTest : SysuiTestCase() {
    @Test
    fun isEmptySpaceClickable_deviceUnlocked_false() =
        testScope.runTest {
            val isEmptySpaceClickable by collectLastValue(underTest.isEmptySpaceClickable)
            kosmos.fakeAuthenticationRepository.setAuthenticationMethod(
                AuthenticationMethodModel.Pin
            )
            setDeviceEntered(true)
            runCurrent()

            assertThat(isEmptySpaceClickable).isFalse()
            assertThat(underTest.isEmptySpaceClickable).isFalse()
        }

    @Test
    fun isEmptySpaceClickable_deviceLockedSecurely_true() =
        testScope.runTest {
            val isEmptySpaceClickable by collectLastValue(underTest.isEmptySpaceClickable)
            kosmos.fakeAuthenticationRepository.setAuthenticationMethod(
                AuthenticationMethodModel.Pin
            )
            runCurrent()

            assertThat(isEmptySpaceClickable).isTrue()
            assertThat(underTest.isEmptySpaceClickable).isTrue()
        }

    @Test
    fun onEmptySpaceClicked_deviceLockedSecurely_switchesToLockscreen() =
        testScope.runTest {
            val currentScene by collectLastValue(sceneInteractor.currentScene)
            val currentScene by collectLastValue(kosmos.sceneInteractor.currentScene)
            kosmos.fakeAuthenticationRepository.setAuthenticationMethod(
                AuthenticationMethodModel.Pin
            )
            runCurrent()

            underTest.onEmptySpaceClicked()

@@ -117,35 +112,32 @@ class ShadeSceneContentViewModelTest : SysuiTestCase() {
        }

    @Test
    fun addAndRemoveMedia_mediaVisibilityisUpdated() =
    fun addAndRemoveMedia_mediaVisibilityIsUpdated() =
        testScope.runTest {
            val isMediaVisible by collectLastValue(underTest.isMediaVisible)
            val userMedia = MediaData(active = true)

            assertThat(isMediaVisible).isFalse()
            assertThat(underTest.isMediaVisible).isFalse()

            kosmos.mediaFilterRepository.addCurrentUserMediaEntry(userMedia)

            assertThat(isMediaVisible).isTrue()
            assertThat(underTest.isMediaVisible).isTrue()

            kosmos.mediaFilterRepository.removeCurrentUserMediaEntry(userMedia.instanceId)

            assertThat(isMediaVisible).isFalse()
            assertThat(underTest.isMediaVisible).isFalse()
        }

    @Test
    fun shadeMode() =
        testScope.runTest {
            val shadeMode by collectLastValue(underTest.shadeMode)
            kosmos.enableSplitShade()
            assertThat(underTest.shadeMode).isEqualTo(ShadeMode.Split)

            shadeRepository.setShadeLayoutWide(true)
            assertThat(shadeMode).isEqualTo(ShadeMode.Split)
            kosmos.enableSingleShade()
            assertThat(underTest.shadeMode).isEqualTo(ShadeMode.Single)

            shadeRepository.setShadeLayoutWide(false)
            assertThat(shadeMode).isEqualTo(ShadeMode.Single)

            shadeRepository.setShadeLayoutWide(true)
            assertThat(shadeMode).isEqualTo(ShadeMode.Split)
            kosmos.enableDualShade()
            assertThat(underTest.shadeMode).isEqualTo(ShadeMode.Dual)
        }

    @Test
@@ -186,15 +178,13 @@ class ShadeSceneContentViewModelTest : SysuiTestCase() {
    @Test
    fun disable2QuickSettings_isQsEnabledIsFalse() =
        testScope.runTest {
            val isQsEnabled by collectLastValue(underTest.isQsEnabled)
            assertThat(isQsEnabled).isTrue()
            assertThat(underTest.isQsEnabled).isTrue()

            kosmos.fakeDisableFlagsRepository.disableFlags.update {
                it.copy(disable2 = DISABLE2_QUICK_SETTINGS)
            }
            runCurrent()

            assertThat(isQsEnabled).isFalse()
            assertThat(underTest.isQsEnabled).isFalse()
        }

    private fun prepareConfiguration(): Int {
@@ -219,25 +209,15 @@ class ShadeSceneContentViewModelTest : SysuiTestCase() {
            kosmos.fakeDeviceEntryFingerprintAuthRepository.setAuthenticationStatus(
                SuccessFingerprintAuthenticationStatus(0, true)
            )
            runCurrent()
            assertThat(isDeviceUnlocked).isTrue()
        }
        setScene(
            if (isEntered) {
                Scenes.Gone
            } else {
                Scenes.Lockscreen
            }
        )
        setScene(if (isEntered) Scenes.Gone else Scenes.Lockscreen)
        assertThat(kosmos.deviceEntryInteractor.isDeviceEntered.value).isEqualTo(isEntered)
    }

    private fun TestScope.setScene(key: SceneKey) {
        sceneInteractor.changeScene(key, "test")
        sceneInteractor.setTransitionState(
            MutableStateFlow<ObservableTransitionState>(ObservableTransitionState.Idle(key))
        )
        runCurrent()
    private fun setScene(key: SceneKey) {
        kosmos.sceneInteractor.changeScene(key, "test")
        kosmos.sceneInteractor.setTransitionState(flowOf(ObservableTransitionState.Idle(key)))
    }

    private data class Translations(val start: Float, val end: Float)
+7 −21
Original line number Diff line number Diff line
@@ -36,6 +36,7 @@ import com.android.systemui.keyguard.data.repository.fakeDeviceEntryFingerprintA
import com.android.systemui.keyguard.domain.interactor.keyguardEnabledInteractor
import com.android.systemui.keyguard.shared.model.SuccessFingerprintAuthenticationStatus
import com.android.systemui.kosmos.testScope
import com.android.systemui.kosmos.useUnconfinedTestDispatcher
import com.android.systemui.lifecycle.activateIn
import com.android.systemui.qs.ui.adapter.fakeQSSceneAdapter
import com.android.systemui.scene.domain.interactor.sceneInteractor
@@ -51,9 +52,7 @@ import com.android.systemui.shade.domain.startable.shadeStartable
import com.android.systemui.testKosmos
import com.google.common.truth.Truth.assertThat
import com.google.common.truth.Truth.assertWithMessage
import kotlinx.coroutines.flow.MutableStateFlow
import kotlinx.coroutines.test.TestScope
import kotlinx.coroutines.test.runCurrent
import kotlinx.coroutines.flow.flowOf
import kotlinx.coroutines.test.runTest
import org.junit.Before
import org.junit.Test
@@ -65,7 +64,7 @@ import org.junit.runner.RunWith
@EnableSceneContainer
class ShadeUserActionsViewModelTest : SysuiTestCase() {

    private val kosmos = testKosmos()
    private val kosmos = testKosmos().useUnconfinedTestDispatcher()
    private val testScope = kosmos.testScope
    private val sceneInteractor by lazy { kosmos.sceneInteractor }
    private val qsSceneAdapter by lazy { kosmos.fakeQSSceneAdapter }
@@ -148,7 +147,6 @@ class ShadeUserActionsViewModelTest : SysuiTestCase() {
            kosmos.fakeAuthenticationRepository.setAuthenticationMethod(
                AuthenticationMethodModel.None
            )
            runCurrent()
            sceneInteractor.changeScene(Scenes.Gone, "reason")

            assertThat((actions?.get(Swipe.Up) as? UserActionResult.ChangeScene)?.toScene)
@@ -161,7 +159,6 @@ class ShadeUserActionsViewModelTest : SysuiTestCase() {
        testScope.runTest {
            val actions by collectLastValue(underTest.actions)
            kosmos.enableSplitShade()
            runCurrent()

            assertThat(actions?.get(Swipe.Up)?.transitionKey).isEqualTo(ToSplitShade)
        }
@@ -171,7 +168,6 @@ class ShadeUserActionsViewModelTest : SysuiTestCase() {
        testScope.runTest {
            val actions by collectLastValue(underTest.actions)
            kosmos.enableSingleShade()
            runCurrent()

            assertThat(actions?.get(Swipe.Up)?.transitionKey).isNull()
        }
@@ -245,29 +241,19 @@ class ShadeUserActionsViewModelTest : SysuiTestCase() {
            }
        }

    private fun TestScope.setDeviceEntered(isEntered: Boolean) {
    private fun setDeviceEntered(isEntered: Boolean) {
        if (isEntered) {
            // Unlock the device marking the device has entered.
            kosmos.fakeDeviceEntryFingerprintAuthRepository.setAuthenticationStatus(
                SuccessFingerprintAuthenticationStatus(0, true)
            )
            runCurrent()
        }
        setScene(
            if (isEntered) {
                Scenes.Gone
            } else {
                Scenes.Lockscreen
            }
        )
        setScene(if (isEntered) Scenes.Gone else Scenes.Lockscreen)
        assertThat(kosmos.deviceEntryInteractor.isDeviceEntered.value).isEqualTo(isEntered)
    }

    private fun TestScope.setScene(key: SceneKey) {
    private fun setScene(key: SceneKey) {
        sceneInteractor.changeScene(key, "test")
        sceneInteractor.setTransitionState(
            MutableStateFlow<ObservableTransitionState>(ObservableTransitionState.Idle(key))
        )
        runCurrent()
        sceneInteractor.setTransitionState(flowOf(ObservableTransitionState.Idle(key)))
    }
}
Loading