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

Commit 57fc75ca authored by Matt Pietal's avatar Matt Pietal
Browse files

Make isInteractive use stateIn

To avoid registering multiple callbacks and to be able use retrieve
the value

Test: atest PowerRepositoryImplTest
Bug: 405159039
Flag: EXEMPT bugfix
Change-Id: I9362f5b42b254af2672f6c262c3125f3f6649eec
parent b521669e
Loading
Loading
Loading
Loading
+23 −54
Original line number Diff line number Diff line
@@ -27,7 +27,10 @@ import androidx.test.filters.SmallTest
import com.android.systemui.SysuiTestCase
import com.android.systemui.broadcast.BroadcastDispatcher
import com.android.systemui.concurrency.fakeExecutor
import com.android.systemui.coroutines.collectLastValue
import com.android.systemui.coroutines.collectValues
import com.android.systemui.keyguard.userActivityNotifier
import com.android.systemui.kosmos.testScope
import com.android.systemui.testKosmos
import com.android.systemui.util.mockito.any
import com.android.systemui.util.mockito.argumentCaptor
@@ -35,10 +38,8 @@ import com.android.systemui.util.mockito.capture
import com.android.systemui.util.mockito.eq
import com.android.systemui.util.time.FakeSystemClock
import com.google.common.truth.Truth.assertThat
import kotlinx.coroutines.Dispatchers
import kotlinx.coroutines.flow.launchIn
import kotlinx.coroutines.flow.onEach
import kotlinx.coroutines.runBlocking
import kotlinx.coroutines.test.runCurrent
import kotlinx.coroutines.test.runTest
import org.junit.Before
import org.junit.Test
import org.junit.runner.RunWith
@@ -55,6 +56,7 @@ import org.mockito.MockitoAnnotations
@RunWith(AndroidJUnit4::class)
class PowerRepositoryImplTest : SysuiTestCase() {
    private val kosmos = testKosmos()
    private val testScope = kosmos.testScope
    private val systemClock = FakeSystemClock()

    val manager: PowerManager = kosmos.powerManager
@@ -76,94 +78,66 @@ class PowerRepositoryImplTest : SysuiTestCase() {
            PowerRepositoryImpl(
                manager,
                context.applicationContext,
                kosmos.testScope.backgroundScope,
                systemClock,
                dispatcher,
                kosmos.userActivityNotifier,
            )
    }

    @Test
    fun isInteractive_registersForBroadcasts() =
        runBlocking(IMMEDIATE) {
            val job = underTest.isInteractive.onEach {}.launchIn(this)

            verifyRegistered()
            assertThat(filterCaptor.value.hasAction(Intent.ACTION_SCREEN_ON)).isTrue()
            assertThat(filterCaptor.value.hasAction(Intent.ACTION_SCREEN_OFF)).isTrue()

            job.cancel()
        }

    @Test
    fun isInteractive_unregistersFromBroadcasts() =
        runBlocking(IMMEDIATE) {
            val job = underTest.isInteractive.onEach {}.launchIn(this)
            verifyRegistered()

            job.cancel()

            verify(dispatcher).unregisterReceiver(receiverCaptor.value)
        }

    @Test
    fun isInteractive_emitsInitialTrueValueIfScreenWasOn() =
        runBlocking(IMMEDIATE) {
        testScope.runTest {
            isInteractive = true
            var value: Boolean? = null
            val job = underTest.isInteractive.onEach { value = it }.launchIn(this)

            val value by collectLastValue(underTest.isInteractive)
            runCurrent()
            verifyRegistered()

            assertThat(value).isTrue()
            job.cancel()
        }

    @Test
    fun isInteractive_emitsInitialFalseValueIfScreenWasOff() =
        runBlocking(IMMEDIATE) {
        testScope.runTest {
            isInteractive = false
            var value: Boolean? = null
            val job = underTest.isInteractive.onEach { value = it }.launchIn(this)

            val value by collectLastValue(underTest.isInteractive)
            runCurrent()
            verifyRegistered()

            assertThat(value).isFalse()
            job.cancel()
        }

    @Test
    fun isInteractive_emitsTrueWhenTheScreenTurnsOn() =
        runBlocking(IMMEDIATE) {
            var value: Boolean? = null
            val job = underTest.isInteractive.onEach { value = it }.launchIn(this)
        testScope.runTest {
            val value by collectLastValue(underTest.isInteractive)
            runCurrent()
            verifyRegistered()

            isInteractive = true
            receiverCaptor.value.onReceive(context, Intent(Intent.ACTION_SCREEN_ON))

            assertThat(value).isTrue()
            job.cancel()
        }

    @Test
    fun isInteractive_emitsFalseWhenTheScreenTurnsOff() =
        runBlocking(IMMEDIATE) {
            var value: Boolean? = null
            val job = underTest.isInteractive.onEach { value = it }.launchIn(this)
        testScope.runTest {
            val value by collectLastValue(underTest.isInteractive)
            runCurrent()
            verifyRegistered()

            isInteractive = false
            receiverCaptor.value.onReceive(context, Intent(Intent.ACTION_SCREEN_OFF))

            assertThat(value).isFalse()
            job.cancel()
        }

    @Test
    fun isInteractive_emitsCorrectlyOverTime() =
        runBlocking(IMMEDIATE) {
            val values = mutableListOf<Boolean>()
            val job = underTest.isInteractive.onEach(values::add).launchIn(this)
        testScope.runTest {
            val values by collectValues(underTest.isInteractive)
            runCurrent()
            verifyRegistered()

            isInteractive = false
@@ -173,8 +147,7 @@ class PowerRepositoryImplTest : SysuiTestCase() {
            isInteractive = false
            receiverCaptor.value.onReceive(context, Intent(Intent.ACTION_SCREEN_OFF))

            assertThat(values).isEqualTo(listOf(true, false, true, false))
            job.cancel()
            assertThat(values).isEqualTo(listOf(false, true, false))
        }

    @Test
@@ -247,8 +220,4 @@ class PowerRepositoryImplTest : SysuiTestCase() {
                isNull(),
            )
    }

    companion object {
        private val IMMEDIATE = Dispatchers.Main.immediate
    }
}
+28 −23
Original line number Diff line number Diff line
@@ -37,16 +37,18 @@ import com.android.systemui.power.shared.model.WakefulnessState
import com.android.systemui.util.time.SystemClock
import com.android.systemui.utils.coroutines.flow.conflatedCallbackFlow
import javax.inject.Inject
import kotlinx.coroutines.CoroutineScope
import kotlinx.coroutines.channels.awaitClose
import kotlinx.coroutines.flow.Flow
import kotlinx.coroutines.flow.MutableStateFlow
import kotlinx.coroutines.flow.SharingStarted
import kotlinx.coroutines.flow.StateFlow
import kotlinx.coroutines.flow.asStateFlow
import kotlinx.coroutines.flow.stateIn

/** Defines interface for classes that act as source of truth for power-related data. */
interface PowerRepository {
    /** Whether the device is interactive. Starts with the current state. */
    val isInteractive: Flow<Boolean>
    val isInteractive: StateFlow<Boolean>

    /**
     * Whether the device is awake or asleep. [WakefulnessState.AWAKE] means the screen is fully
@@ -103,6 +105,7 @@ class PowerRepositoryImpl
constructor(
    private val manager: PowerManager,
    @Application private val applicationContext: Context,
    @Application private val scope: CoroutineScope,
    private val systemClock: SystemClock,
    dispatcher: BroadcastDispatcher,
    private val userActivityNotifier: UserActivityNotifier,
@@ -110,7 +113,8 @@ constructor(

    override val dozeScreenState = MutableStateFlow(DozeScreenStateModel.UNKNOWN)

    override val isInteractive: Flow<Boolean> = conflatedCallbackFlow {
    override val isInteractive: StateFlow<Boolean> =
        conflatedCallbackFlow {
                fun send() {
                    trySendWithFailureLogging(manager.isInteractive, TAG)
                }
@@ -133,6 +137,7 @@ constructor(

                awaitClose { dispatcher.unregisterReceiver(receiver) }
            }
            .stateIn(scope, SharingStarted.Eagerly, false)

    private val _wakefulness = MutableStateFlow(WakefulnessModel())
    override val wakefulness = _wakefulness.asStateFlow()
+1 −2
Original line number Diff line number Diff line
@@ -34,7 +34,6 @@ import com.android.systemui.power.shared.model.WakefulnessState
import com.android.systemui.statusbar.phone.ScreenOffAnimationController
import javax.inject.Inject
import javax.inject.Provider
import kotlinx.coroutines.flow.Flow
import kotlinx.coroutines.flow.StateFlow
import kotlinx.coroutines.flow.asStateFlow
import kotlinx.coroutines.flow.collect
@@ -53,7 +52,7 @@ constructor(
    private val cameraGestureHelper: Provider<CameraGestureHelper?>,
) {
    /** Whether the screen is on or off. */
    val isInteractive: Flow<Boolean> = repository.isInteractive
    val isInteractive: StateFlow<Boolean> = repository.isInteractive

    /**
     * Whether we're awake or asleep, along with additional information about why we're awake/asleep
+2 −2
Original line number Diff line number Diff line
@@ -27,14 +27,14 @@ import com.android.systemui.power.shared.model.WakefulnessState
import dagger.Binds
import dagger.Module
import javax.inject.Inject
import kotlinx.coroutines.flow.Flow
import kotlinx.coroutines.flow.MutableStateFlow
import kotlinx.coroutines.flow.StateFlow
import kotlinx.coroutines.flow.asStateFlow

@SysUISingleton
class FakePowerRepository @Inject constructor() : PowerRepository {
    private val _isInteractive = MutableStateFlow(true)
    override val isInteractive: Flow<Boolean> = _isInteractive.asStateFlow()
    override val isInteractive: StateFlow<Boolean> = _isInteractive.asStateFlow()

    private val _wakefulness = MutableStateFlow(WakefulnessModel())
    override val wakefulness = _wakefulness.asStateFlow()