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

Commit b499a105 authored by Matt Pietal's avatar Matt Pietal
Browse files

Use shared flow for transition animations

There was state incorrectly being shared between multiple
flows. Convert to a SharedFlow instead, to more efficiently reuse.

Also add optional animation logging in a new logbuffer.

Also convert all transition tests to use Kosmos.

Bug: 296373465
Test: atest com.android.keyguard.systemui.keyguard.ui.viewmodel
Flag: N/A
Change-Id: Ic0f507ad90b5c8a4643ab31cd24aa65a6686ecc0
parent 30c11d69
Loading
Loading
Loading
Loading
+130 −159
Original line number Diff line number Diff line
@@ -19,14 +19,11 @@ package com.android.systemui.keyguard.ui.viewmodel
import androidx.test.ext.junit.runners.AndroidJUnit4
import androidx.test.filters.SmallTest
import com.android.systemui.SysuiTestCase
import com.android.systemui.biometrics.data.repository.FakeFingerprintPropertyRepository
import com.android.systemui.biometrics.data.repository.fingerprintPropertyRepository
import com.android.systemui.biometrics.shared.model.FingerprintSensorType
import com.android.systemui.biometrics.shared.model.SensorStrength
import com.android.systemui.deviceentry.domain.interactor.DeviceEntryUdfpsInteractor
import com.android.systemui.keyguard.data.repository.FakeBiometricSettingsRepository
import com.android.systemui.keyguard.data.repository.FakeDeviceEntryFingerprintAuthRepository
import com.android.systemui.keyguard.data.repository.FakeKeyguardTransitionRepository
import com.android.systemui.keyguard.domain.interactor.KeyguardTransitionInteractorFactory
import com.android.systemui.coroutines.collectValues
import com.android.systemui.keyguard.data.repository.fakeKeyguardTransitionRepository
import com.android.systemui.keyguard.shared.model.KeyguardState.AOD
import com.android.systemui.keyguard.shared.model.KeyguardState.DOZING
import com.android.systemui.keyguard.shared.model.KeyguardState.DREAMING
@@ -38,16 +35,12 @@ import com.android.systemui.keyguard.shared.model.TransitionState.FINISHED
import com.android.systemui.keyguard.shared.model.TransitionState.RUNNING
import com.android.systemui.keyguard.shared.model.TransitionState.STARTED
import com.android.systemui.keyguard.shared.model.TransitionStep
import com.android.systemui.util.mockito.mock
import com.android.systemui.kosmos.testScope
import com.android.systemui.testKosmos
import com.google.common.collect.Range
import com.google.common.truth.Truth.assertThat
import kotlinx.coroutines.ExperimentalCoroutinesApi
import kotlinx.coroutines.flow.launchIn
import kotlinx.coroutines.flow.onEach
import kotlinx.coroutines.test.TestScope
import kotlinx.coroutines.test.UnconfinedTestDispatcher
import kotlinx.coroutines.test.runTest
import org.junit.Before
import org.junit.Test
import org.junit.runner.RunWith

@@ -55,223 +48,201 @@ import org.junit.runner.RunWith
@SmallTest
@RunWith(AndroidJUnit4::class)
class DreamingToLockscreenTransitionViewModelTest : SysuiTestCase() {
    private lateinit var underTest: DreamingToLockscreenTransitionViewModel
    private lateinit var repository: FakeKeyguardTransitionRepository
    private lateinit var fingerprintPropertyRepository: FakeFingerprintPropertyRepository

    @Before
    fun setUp() {
        repository = FakeKeyguardTransitionRepository()
        fingerprintPropertyRepository = FakeFingerprintPropertyRepository()
        val interactor =
            KeyguardTransitionInteractorFactory.create(
                    scope = TestScope().backgroundScope,
                    repository = repository,
                )
                .keyguardTransitionInteractor
        underTest =
            DreamingToLockscreenTransitionViewModel(
                interactor,
                mock(),
                DeviceEntryUdfpsInteractor(
                    fingerprintPropertyRepository = fingerprintPropertyRepository,
                    fingerprintAuthRepository = FakeDeviceEntryFingerprintAuthRepository(),
                    biometricSettingsRepository = FakeBiometricSettingsRepository(),
                ),
            )
    }
    private val kosmos = testKosmos()
    private val testScope = kosmos.testScope
    private val keyguardTransitionRepository = kosmos.fakeKeyguardTransitionRepository
    private val fingerprintPropertyRepository = kosmos.fingerprintPropertyRepository
    private val underTest = kosmos.dreamingToLockscreenTransitionViewModel

    @Test
    fun dreamOverlayTranslationY() =
        runTest(UnconfinedTestDispatcher()) {
            val values = mutableListOf<Float>()

        testScope.runTest {
            val pixels = 100
            val job =
                underTest.dreamOverlayTranslationY(pixels).onEach { values.add(it) }.launchIn(this)

            repository.sendTransitionStep(step(0f, STARTED))
            repository.sendTransitionStep(step(0f))
            repository.sendTransitionStep(step(0.3f))
            repository.sendTransitionStep(step(0.5f))
            repository.sendTransitionStep(step(0.6f))
            repository.sendTransitionStep(step(0.8f))
            repository.sendTransitionStep(step(1f))
            val values by collectValues(underTest.dreamOverlayTranslationY(pixels))

            keyguardTransitionRepository.sendTransitionSteps(
                listOf(
                    step(0f, TransitionState.STARTED),
                    step(0f),
                    step(0.3f),
                    step(0.5f),
                    step(0.6f),
                    step(0.8f),
                    step(1f),
                ),
                testScope,
            )

            assertThat(values.size).isEqualTo(7)
            values.forEach { assertThat(it).isIn(Range.closed(0f, 100f)) }

            job.cancel()
        }

    @Test
    fun dreamOverlayFadeOut() =
        runTest(UnconfinedTestDispatcher()) {
            val values = mutableListOf<Float>()

            val job = underTest.dreamOverlayAlpha.onEach { values.add(it) }.launchIn(this)
        testScope.runTest {
            val values by collectValues(underTest.dreamOverlayAlpha)

            keyguardTransitionRepository.sendTransitionSteps(
                listOf(
                    // Should start running here...
            repository.sendTransitionStep(step(0f, STARTED))
            repository.sendTransitionStep(step(0f))
            repository.sendTransitionStep(step(0.1f))
            repository.sendTransitionStep(step(0.5f))
                    step(0f, TransitionState.STARTED),
                    step(0f),
                    step(0.1f),
                    step(0.5f),
                    // ...up to here
            repository.sendTransitionStep(step(1f))
                    step(1f),
                ),
                testScope,
            )

            // Only two values should be present, since the dream overlay runs for a small fraction
            // of the overall animation time
            assertThat(values.size).isEqualTo(4)
            values.forEach { assertThat(it).isIn(Range.closed(0f, 1f)) }

            job.cancel()
        }

    @Test
    fun lockscreenFadeIn() =
        runTest(UnconfinedTestDispatcher()) {
            val values = mutableListOf<Float>()

            val job = underTest.lockscreenAlpha.onEach { values.add(it) }.launchIn(this)

            repository.sendTransitionStep(step(0f, STARTED))
            repository.sendTransitionStep(step(0f))
            repository.sendTransitionStep(step(0.1f))
            repository.sendTransitionStep(step(0.2f))
            repository.sendTransitionStep(step(0.3f))
            repository.sendTransitionStep(step(1f))
        testScope.runTest {
            val values by collectValues(underTest.lockscreenAlpha)

            keyguardTransitionRepository.sendTransitionSteps(
                listOf(
                    step(0f, TransitionState.STARTED),
                    step(0f),
                    step(0.1f),
                    step(0.2f),
                    step(0.3f),
                    step(1f),
                ),
                testScope,
            )

            assertThat(values.size).isEqualTo(4)
            values.forEach { assertThat(it).isIn(Range.closed(0f, 1f)) }

            job.cancel()
        }

    @Test
    fun deviceEntryParentViewFadeIn() =
        runTest(UnconfinedTestDispatcher()) {
            val values = mutableListOf<Float>()

            val job = underTest.deviceEntryParentViewAlpha.onEach { values.add(it) }.launchIn(this)

            repository.sendTransitionStep(step(0f, STARTED))
            repository.sendTransitionStep(step(0f))
            repository.sendTransitionStep(step(0.1f))
            repository.sendTransitionStep(step(0.2f))
            repository.sendTransitionStep(step(0.3f))
            repository.sendTransitionStep(step(1f))
        testScope.runTest {
            val values by collectValues(underTest.deviceEntryParentViewAlpha)

            keyguardTransitionRepository.sendTransitionSteps(
                listOf(
                    step(0f, TransitionState.STARTED),
                    step(0f),
                    step(0.1f),
                    step(0.2f),
                    step(0.3f),
                    step(1f),
                ),
                testScope,
            )

            assertThat(values.size).isEqualTo(4)
            values.forEach { assertThat(it).isIn(Range.closed(0f, 1f)) }

            job.cancel()
        }

    @Test
    fun deviceEntryBackgroundViewAppear() =
        runTest(UnconfinedTestDispatcher()) {
        testScope.runTest {
            fingerprintPropertyRepository.setProperties(
                sensorId = 0,
                strength = SensorStrength.STRONG,
                sensorType = FingerprintSensorType.UDFPS_OPTICAL,
                sensorLocations = emptyMap(),
            )
            val values = mutableListOf<Float>()

            val job =
                underTest.deviceEntryBackgroundViewAlpha.onEach { values.add(it) }.launchIn(this)

            repository.sendTransitionStep(step(0f, STARTED))
            repository.sendTransitionStep(step(0f))
            repository.sendTransitionStep(step(0.1f))
            repository.sendTransitionStep(step(0.2f))
            repository.sendTransitionStep(step(0.3f))
            repository.sendTransitionStep(step(1f))
            val values by collectValues(underTest.deviceEntryBackgroundViewAlpha)

            keyguardTransitionRepository.sendTransitionSteps(
                listOf(
                    step(0f, TransitionState.STARTED),
                    step(0f),
                    step(0.1f),
                    step(0.2f),
                    step(0.3f),
                    step(1f),
                ),
                testScope,
            )

            values.forEach { assertThat(it).isEqualTo(1f) }

            job.cancel()
        }

    @Test
    fun deviceEntryBackground_noUdfps_noUpdates() =
        runTest(UnconfinedTestDispatcher()) {
        testScope.runTest {
            fingerprintPropertyRepository.setProperties(
                sensorId = 0,
                strength = SensorStrength.STRONG,
                sensorType = FingerprintSensorType.REAR,
                sensorLocations = emptyMap(),
            )
            val values = mutableListOf<Float>()

            val job =
                underTest.deviceEntryBackgroundViewAlpha.onEach { values.add(it) }.launchIn(this)

            repository.sendTransitionStep(step(0f, STARTED))
            repository.sendTransitionStep(step(0f))
            repository.sendTransitionStep(step(0.1f))
            repository.sendTransitionStep(step(0.2f))
            repository.sendTransitionStep(step(0.3f))
            repository.sendTransitionStep(step(1f))
            val values by collectValues(underTest.deviceEntryBackgroundViewAlpha)

            keyguardTransitionRepository.sendTransitionSteps(
                listOf(
                    step(0f, TransitionState.STARTED),
                    step(0f),
                    step(0.1f),
                    step(0.2f),
                    step(0.3f),
                    step(1f),
                ),
                testScope,
            )

            assertThat(values.size).isEqualTo(0) // no updates

            job.cancel()
        }

    @Test
    fun lockscreenTranslationY() =
        runTest(UnconfinedTestDispatcher()) {
            val values = mutableListOf<Float>()

        testScope.runTest {
            val pixels = 100
            val job =
                underTest.lockscreenTranslationY(pixels).onEach { values.add(it) }.launchIn(this)

            repository.sendTransitionStep(step(0f, STARTED))
            repository.sendTransitionStep(step(0f))
            repository.sendTransitionStep(step(0.3f))
            repository.sendTransitionStep(step(0.5f))
            repository.sendTransitionStep(step(1f))
            val values by collectValues(underTest.lockscreenTranslationY(pixels))

            keyguardTransitionRepository.sendTransitionSteps(
                listOf(
                    step(0f, TransitionState.STARTED),
                    step(0f),
                    step(0.3f),
                    step(0.5f),
                    step(1f),
                ),
                testScope,
            )

            assertThat(values.size).isEqualTo(5)
            values.forEach { assertThat(it).isIn(Range.closed(-100f, 0f)) }

            job.cancel()
        }

    @Test
    fun transitionEnded() =
        runTest(UnconfinedTestDispatcher()) {
            val values = mutableListOf<TransitionStep>()

            val job = underTest.transitionEnded.onEach { values.add(it) }.launchIn(this)

            repository.sendTransitionStep(TransitionStep(DOZING, DREAMING, 0.0f, STARTED))
            repository.sendTransitionStep(TransitionStep(DOZING, DREAMING, 1.0f, FINISHED))

            repository.sendTransitionStep(TransitionStep(DREAMING, LOCKSCREEN, 0.0f, STARTED))
            repository.sendTransitionStep(TransitionStep(DREAMING, LOCKSCREEN, 0.1f, RUNNING))
            repository.sendTransitionStep(TransitionStep(DREAMING, LOCKSCREEN, 1.0f, FINISHED))

            repository.sendTransitionStep(TransitionStep(LOCKSCREEN, DREAMING, 0.0f, STARTED))
            repository.sendTransitionStep(TransitionStep(LOCKSCREEN, DREAMING, 0.5f, RUNNING))
            repository.sendTransitionStep(TransitionStep(LOCKSCREEN, DREAMING, 1.0f, FINISHED))

            repository.sendTransitionStep(TransitionStep(DREAMING, GONE, 0.0f, STARTED))
            repository.sendTransitionStep(TransitionStep(DREAMING, GONE, 0.5f, RUNNING))
            repository.sendTransitionStep(TransitionStep(DREAMING, GONE, 1.0f, CANCELED))

            repository.sendTransitionStep(TransitionStep(DREAMING, AOD, 0.0f, STARTED))
            repository.sendTransitionStep(TransitionStep(DREAMING, AOD, 1.0f, FINISHED))
        testScope.runTest {
            val values by collectValues(underTest.transitionEnded)

            keyguardTransitionRepository.sendTransitionSteps(
                listOf(
                    TransitionStep(DOZING, DREAMING, 0.0f, STARTED),
                    TransitionStep(DOZING, DREAMING, 1.0f, FINISHED),
                    TransitionStep(DREAMING, LOCKSCREEN, 0.0f, STARTED),
                    TransitionStep(DREAMING, LOCKSCREEN, 0.1f, RUNNING),
                    TransitionStep(DREAMING, LOCKSCREEN, 1.0f, FINISHED),
                    TransitionStep(LOCKSCREEN, DREAMING, 0.0f, STARTED),
                    TransitionStep(LOCKSCREEN, DREAMING, 0.5f, RUNNING),
                    TransitionStep(LOCKSCREEN, DREAMING, 1.0f, FINISHED),
                    TransitionStep(DREAMING, GONE, 0.0f, STARTED),
                    TransitionStep(DREAMING, GONE, 0.5f, RUNNING),
                    TransitionStep(DREAMING, GONE, 1.0f, CANCELED),
                    TransitionStep(DREAMING, AOD, 0.0f, STARTED),
                    TransitionStep(DREAMING, AOD, 1.0f, FINISHED),
                ),
                testScope,
            )

            assertThat(values.size).isEqualTo(3)
            values.forEach {
                assertThat(it.transitionState == FINISHED || it.transitionState == CANCELED)
                    .isTrue()
            }

            job.cancel()
        }

    private fun step(value: Float, state: TransitionState = RUNNING): TransitionStep {
+38 −50
Original line number Diff line number Diff line
@@ -19,85 +19,73 @@ package com.android.systemui.keyguard.ui.viewmodel
import androidx.test.ext.junit.runners.AndroidJUnit4
import androidx.test.filters.SmallTest
import com.android.systemui.SysuiTestCase
import com.android.systemui.keyguard.data.repository.FakeKeyguardTransitionRepository
import com.android.systemui.keyguard.domain.interactor.KeyguardTransitionInteractorFactory
import com.android.systemui.coroutines.collectValues
import com.android.systemui.keyguard.data.repository.fakeKeyguardTransitionRepository
import com.android.systemui.keyguard.shared.model.KeyguardState
import com.android.systemui.keyguard.shared.model.TransitionState
import com.android.systemui.keyguard.shared.model.TransitionStep
import com.android.systemui.kosmos.testScope
import com.android.systemui.testKosmos
import com.google.common.collect.Range
import com.google.common.truth.Truth.assertThat
import kotlinx.coroutines.flow.launchIn
import kotlinx.coroutines.flow.onEach
import kotlinx.coroutines.test.TestScope
import kotlinx.coroutines.test.UnconfinedTestDispatcher
import kotlinx.coroutines.test.runTest
import org.junit.Before
import org.junit.Test
import org.junit.runner.RunWith

@SmallTest
@RunWith(AndroidJUnit4::class)
class GoneToDreamingTransitionViewModelTest : SysuiTestCase() {
    private lateinit var underTest: GoneToDreamingTransitionViewModel
    private lateinit var repository: FakeKeyguardTransitionRepository

    @Before
    fun setUp() {
        repository = FakeKeyguardTransitionRepository()
        val interactor =
            KeyguardTransitionInteractorFactory.create(
                    scope = TestScope().backgroundScope,
                    repository = repository,
                )
                .keyguardTransitionInteractor
        underTest = GoneToDreamingTransitionViewModel(interactor)
    }
    private val kosmos = testKosmos()
    private val testScope = kosmos.testScope
    private val repository = kosmos.fakeKeyguardTransitionRepository
    private val underTest = kosmos.goneToDreamingTransitionViewModel

    @Test
    fun lockscreenFadeOut() =
        runTest(UnconfinedTestDispatcher()) {
            val values = mutableListOf<Float>()

            val job = underTest.lockscreenAlpha.onEach { values.add(it) }.launchIn(this)
    fun runTest() =
        testScope.runTest {
            val values by collectValues(underTest.lockscreenAlpha)

            repository.sendTransitionSteps(
                listOf(
                    // Should start running here...
            repository.sendTransitionStep(step(0f, TransitionState.STARTED))
            repository.sendTransitionStep(step(0f))
            repository.sendTransitionStep(step(0.1f))
            repository.sendTransitionStep(step(0.2f))
            repository.sendTransitionStep(step(0.3f))
                    step(0f, TransitionState.STARTED),
                    step(0f),
                    step(0.1f),
                    step(0.2f),
                    step(0.3f),
                    // ...up to here
            repository.sendTransitionStep(step(1f))
                    step(1f),
                ),
                testScope,
            )

            // Only three values should be present, since the dream overlay runs for a small
            // fraction of the overall animation time
            assertThat(values.size).isEqualTo(5)
            values.forEach { assertThat(it).isIn(Range.closed(0f, 1f)) }

            job.cancel()
        }

    @Test
    fun lockscreenTranslationY() =
        runTest(UnconfinedTestDispatcher()) {
            val values = mutableListOf<Float>()

        testScope.runTest {
            val pixels = 100
            val job =
                underTest.lockscreenTranslationY(pixels).onEach { values.add(it) }.launchIn(this)
            val values by collectValues(underTest.lockscreenTranslationY(pixels))

            repository.sendTransitionSteps(
                listOf(
                    // Should start running here...
            repository.sendTransitionStep(step(0f, TransitionState.STARTED))
            repository.sendTransitionStep(step(0f))
            repository.sendTransitionStep(step(0.3f))
            repository.sendTransitionStep(step(0.5f))
                    step(0f, TransitionState.STARTED),
                    step(0f),
                    step(0.3f),
                    step(0.5f),
                    // And a final reset event on CANCEL
            repository.sendTransitionStep(step(0.8f, TransitionState.CANCELED))
                    step(0.8f, TransitionState.CANCELED)
                ),
                testScope,
            )

            assertThat(values.size).isEqualTo(5)
            values.forEach { assertThat(it).isIn(Range.closed(0f, 100f)) }

            job.cancel()
        }

    private fun step(
+1 −6
Original line number Diff line number Diff line
@@ -27,7 +27,6 @@ import com.android.systemui.flags.Flags
import com.android.systemui.flags.featureFlagsClassic
import com.android.systemui.keyguard.data.repository.fakeKeyguardRepository
import com.android.systemui.keyguard.data.repository.fakeKeyguardTransitionRepository
import com.android.systemui.keyguard.domain.interactor.keyguardTransitionInteractor
import com.android.systemui.keyguard.shared.model.KeyguardState
import com.android.systemui.keyguard.shared.model.StatusBarState
import com.android.systemui.keyguard.shared.model.TransitionState
@@ -55,11 +54,7 @@ class LockscreenToDreamingTransitionViewModelTest : SysuiTestCase() {
    private val repository = kosmos.fakeKeyguardTransitionRepository
    private val shadeRepository = kosmos.shadeRepository
    private val keyguardRepository = kosmos.fakeKeyguardRepository
    private val underTest =
        LockscreenToDreamingTransitionViewModel(
            interactor = kosmos.keyguardTransitionInteractor,
            shadeDependentFlows = kosmos.shadeDependentFlows,
        )
    private val underTest = kosmos.lockscreenToDreamingTransitionViewModel

    @Test
    fun lockscreenFadeOut() =
+14 −11
Original line number Diff line number Diff line
@@ -21,19 +21,19 @@ package com.android.systemui.keyguard.ui.viewmodel
import androidx.test.ext.junit.runners.AndroidJUnit4
import androidx.test.filters.SmallTest
import com.android.systemui.SysuiTestCase
import com.android.systemui.common.ui.data.repository.fakeConfigurationRepository
import com.android.systemui.coroutines.collectLastValue
import com.android.systemui.coroutines.collectValues
import com.android.systemui.common.ui.domain.interactor.ConfigurationInteractor
import com.android.systemui.flags.Flags
import com.android.systemui.flags.featureFlagsClassic
import com.android.systemui.keyguard.data.repository.fakeKeyguardRepository
import com.android.systemui.keyguard.data.repository.fakeKeyguardTransitionRepository
import com.android.systemui.keyguard.domain.interactor.keyguardTransitionInteractor
import com.android.systemui.keyguard.shared.model.KeyguardState
import com.android.systemui.keyguard.shared.model.StatusBarState
import com.android.systemui.keyguard.shared.model.TransitionState
import com.android.systemui.keyguard.shared.model.TransitionStep
import com.android.systemui.kosmos.testScope
import com.android.systemui.res.R
import com.android.systemui.shade.data.repository.shadeRepository
import com.android.systemui.testKosmos
import com.google.common.collect.Range
@@ -55,11 +55,8 @@ class LockscreenToOccludedTransitionViewModelTest : SysuiTestCase() {
    private val repository = kosmos.fakeKeyguardTransitionRepository
    private val shadeRepository = kosmos.shadeRepository
    private val keyguardRepository = kosmos.fakeKeyguardRepository
    private val underTest =
        LockscreenToOccludedTransitionViewModel(
            interactor = kosmos.keyguardTransitionInteractor,
            shadeDependentFlows = kosmos.shadeDependentFlows,
        )
    private val configurationRepository = kosmos.fakeConfigurationRepository
    private val underTest = kosmos.lockscreenToOccludedTransitionViewModel

    @Test
    fun lockscreenFadeOut() =
@@ -86,8 +83,11 @@ class LockscreenToOccludedTransitionViewModelTest : SysuiTestCase() {
    @Test
    fun lockscreenTranslationY() =
        testScope.runTest {
            val pixels = 100
            val values by collectValues(underTest.lockscreenTranslationY(pixels))
            configurationRepository.setDimensionPixelSize(
                R.dimen.lockscreen_to_occluded_transition_lockscreen_translation_y,
                100
            )
            val values by collectValues(underTest.lockscreenTranslationY)
            repository.sendTransitionSteps(
                steps =
                    listOf(
@@ -106,8 +106,11 @@ class LockscreenToOccludedTransitionViewModelTest : SysuiTestCase() {
    @Test
    fun lockscreenTranslationYIsCanceled() =
        testScope.runTest {
            val pixels = 100
            val values by collectValues(underTest.lockscreenTranslationY(pixels))
            configurationRepository.setDimensionPixelSize(
                R.dimen.lockscreen_to_occluded_transition_lockscreen_translation_y,
                100
            )
            val values by collectValues(underTest.lockscreenTranslationY)
            repository.sendTransitionSteps(
                steps =
                    listOf(
+96 −132

File changed.

Preview size limit exceeded, changes collapsed.

Loading