Loading packages/SystemUI/multivalentTests/src/com/android/systemui/growth/GrowthInteractorTest.kt +31 −7 Original line number Diff line number Diff line Loading @@ -17,6 +17,7 @@ package com.android.systemui.growth.domain.interactor import android.content.Intent import android.os.UserHandle import androidx.test.ext.junit.runners.AndroidJUnit4 import androidx.test.filters.SmallTest import com.android.compose.animation.scene.ObservableTransitionState Loading Loading @@ -66,6 +67,7 @@ class GrowthInteractorTest : SysuiTestCase() { private lateinit var underTest: GrowthInteractor @Captor private lateinit var intentArgumentCaptor: ArgumentCaptor<Intent> @Captor private lateinit var userHandleArgumentCaptor: ArgumentCaptor<UserHandle> @Before fun setUp() { Loading @@ -83,7 +85,12 @@ class GrowthInteractorTest : SysuiTestCase() { advanceTimeBy(GROWTH_BROADCAST_DELAY.plus(DURATION_50_MILLIS)) runCurrent() verify(broadcastSender, times(1)).sendBroadcast(capture(intentArgumentCaptor)) verify(broadcastSender, times(1)) .sendBroadcastAsUser( capture(intentArgumentCaptor), capture(userHandleArgumentCaptor) ) assertThat(userHandleArgumentCaptor.value).isEqualTo(UserHandle.CURRENT) assertThat(intentArgumentCaptor.value.action) .isEqualTo(GrowthInteractor.ACTION_DEVICE_ENTERED_DIRECTLY) assertThat(intentArgumentCaptor.value.`package`).isNull() Loading @@ -103,7 +110,12 @@ class GrowthInteractorTest : SysuiTestCase() { advanceTimeBy(GROWTH_BROADCAST_DELAY.plus(DURATION_50_MILLIS)) runCurrent() verify(broadcastSender, times(1)).sendBroadcast(capture(intentArgumentCaptor)) verify(broadcastSender, times(1)) .sendBroadcastAsUser( capture(intentArgumentCaptor), capture(userHandleArgumentCaptor) ) assertThat(userHandleArgumentCaptor.value).isEqualTo(UserHandle.CURRENT) assertThat(intentArgumentCaptor.value.action) .isEqualTo(GrowthInteractor.ACTION_DEVICE_ENTERED_DIRECTLY) assertThat(intentArgumentCaptor.value.`package`).isNull() Loading @@ -120,7 +132,12 @@ class GrowthInteractorTest : SysuiTestCase() { advanceTimeBy(GROWTH_BROADCAST_DELAY.plus(DURATION_50_MILLIS)) runCurrent() verify(broadcastSender, times(1)).sendBroadcast(capture(intentArgumentCaptor)) verify(broadcastSender, times(1)) .sendBroadcastAsUser( capture(intentArgumentCaptor), capture(userHandleArgumentCaptor) ) assertThat(userHandleArgumentCaptor.value).isEqualTo(UserHandle.CURRENT) assertThat(intentArgumentCaptor.value.action) .isEqualTo(GrowthInteractor.ACTION_DEVICE_ENTERED_DIRECTLY) assertThat(intentArgumentCaptor.value.`package`).isNull() Loading @@ -137,7 +154,12 @@ class GrowthInteractorTest : SysuiTestCase() { advanceTimeBy(GROWTH_BROADCAST_DELAY.plus(DURATION_50_MILLIS)) runCurrent() verify(broadcastSender, times(1)).sendBroadcast(capture(intentArgumentCaptor)) verify(broadcastSender, times(1)) .sendBroadcastAsUser( capture(intentArgumentCaptor), capture(userHandleArgumentCaptor) ) assertThat(userHandleArgumentCaptor.value).isEqualTo(UserHandle.CURRENT) assertThat(intentArgumentCaptor.value.action) .isEqualTo(GrowthInteractor.ACTION_DEVICE_ENTERED_DIRECTLY) assertThat(intentArgumentCaptor.value.`package`).isNull() Loading @@ -148,13 +170,15 @@ class GrowthInteractorTest : SysuiTestCase() { fun onDeviceNotEnteredDirectly_doNotSendBroadcast_onLockscreen() = kosmos.runTest { overrideResources(GROWTH_APP_PACKAGE_NAME, GROWTH_RECEIVER_CLASS_NAME) underTest = kosmos.growthInteractor underTest.activateIn(kosmos.testScope) setDeviceEntered() advanceTimeBy(GROWTH_BROADCAST_DELAY.plus(DURATION_50_MILLIS)) clearInvocations(broadcastSender) setScene(Scenes.Lockscreen) advanceTimeBy(GROWTH_BROADCAST_DELAY.plus(DURATION_50_MILLIS)) verify(broadcastSender, never()).sendBroadcast(any(), any()) verify(broadcastSender, never()).sendBroadcastAsUser(any(), any()) } @Test Loading @@ -166,12 +190,12 @@ class GrowthInteractorTest : SysuiTestCase() { setDeviceEntered() advanceTimeBy(DURATION_50_MILLIS) runCurrent() verify(broadcastSender, never()).sendBroadcast(any(), any()) verify(broadcastSender, never()).sendBroadcastAsUser(any(), any()) setScene(Scenes.Lockscreen) advanceTimeBy(GROWTH_BROADCAST_DELAY.plus(DURATION_50_MILLIS)) runCurrent() verify(broadcastSender, never()).sendBroadcast(any(), any()) verify(broadcastSender, never()).sendBroadcastAsUser(any(), any()) } private fun overrideResources(packageName: String, receiverClassName: String) { Loading packages/SystemUI/src/com/android/systemui/growth/domain/interactor/GrowthInteractor.kt +25 −20 Original line number Diff line number Diff line Loading @@ -19,6 +19,7 @@ package com.android.systemui.growth.domain.interactor import android.content.ComponentName import android.content.Intent import android.content.res.Resources import android.os.UserHandle import androidx.annotation.VisibleForTesting import com.android.systemui.broadcast.BroadcastSender import com.android.systemui.dagger.SysUISingleton Loading @@ -28,11 +29,9 @@ import com.android.systemui.lifecycle.ExclusiveActivatable import com.android.systemui.res.R import dagger.Lazy import javax.inject.Inject import kotlinx.coroutines.Job import kotlinx.coroutines.cancel import kotlinx.coroutines.coroutineScope import kotlinx.coroutines.ExperimentalCoroutinesApi import kotlinx.coroutines.delay import kotlinx.coroutines.launch import kotlinx.coroutines.flow.transformLatest /** Interactor to communicate with the growth app. */ @SysUISingleton Loading @@ -49,33 +48,39 @@ constructor( private val growthBroadcastDelayMillis = resources.getInteger(R.integer.config_growthBroadcastDelayMillis) private var sendBroadcastJob: Job? = null @OptIn(ExperimentalCoroutinesApi::class) override suspend fun onActivated(): Nothing { deviceEntryInteractor.get().isDeviceEnteredDirectly.collect { // Cancel any existing job before launching a new one. sendBroadcastJob?.cancel() sendBroadcastJob = null if (it) { // Launch the delayed task. sendBroadcastJob = coroutineScope { launch { deviceEntryInteractor // When the device is entered directly (`isEntered` is true), wait for a delay and then // emit. `transformLatest` will cancel the delay if `isDeviceEnteredDirectly` emits a // new value (e.g. `false` when the device is locked) before the delay is finished, // thus cancelling the broadcast. .get() .isDeviceEnteredDirectly .transformLatest { isEntered -> if (isEntered) { delay(growthBroadcastDelayMillis.toLong()) sendBroadcast() } emit(Unit) } } .collect { sendBroadcast() } // The underlying flow should never complete, so this line should not be reachable. throw IllegalStateException("isDeviceEnteredDirectly flow completed unexpectedly") } /** * Sends a broadcast to the growth app for the current user to notify that the device has been * entered directly. The broadcast is explicit if a package and receiver class are configured. */ private suspend fun sendBroadcast() { // Broadcast the device entered event. val intent = Intent().apply { setAction(ACTION_DEVICE_ENTERED_DIRECTLY) } if (growthAppPackageName.isNotEmpty() && growthReceiverClassName.isNotEmpty()) { intent.setComponent(ComponentName(growthAppPackageName, growthReceiverClassName)) } broadcastSender.sendBroadcast(intent) broadcastSender.sendBroadcastAsUser(intent, UserHandle.CURRENT) } companion object { Loading Loading
packages/SystemUI/multivalentTests/src/com/android/systemui/growth/GrowthInteractorTest.kt +31 −7 Original line number Diff line number Diff line Loading @@ -17,6 +17,7 @@ package com.android.systemui.growth.domain.interactor import android.content.Intent import android.os.UserHandle import androidx.test.ext.junit.runners.AndroidJUnit4 import androidx.test.filters.SmallTest import com.android.compose.animation.scene.ObservableTransitionState Loading Loading @@ -66,6 +67,7 @@ class GrowthInteractorTest : SysuiTestCase() { private lateinit var underTest: GrowthInteractor @Captor private lateinit var intentArgumentCaptor: ArgumentCaptor<Intent> @Captor private lateinit var userHandleArgumentCaptor: ArgumentCaptor<UserHandle> @Before fun setUp() { Loading @@ -83,7 +85,12 @@ class GrowthInteractorTest : SysuiTestCase() { advanceTimeBy(GROWTH_BROADCAST_DELAY.plus(DURATION_50_MILLIS)) runCurrent() verify(broadcastSender, times(1)).sendBroadcast(capture(intentArgumentCaptor)) verify(broadcastSender, times(1)) .sendBroadcastAsUser( capture(intentArgumentCaptor), capture(userHandleArgumentCaptor) ) assertThat(userHandleArgumentCaptor.value).isEqualTo(UserHandle.CURRENT) assertThat(intentArgumentCaptor.value.action) .isEqualTo(GrowthInteractor.ACTION_DEVICE_ENTERED_DIRECTLY) assertThat(intentArgumentCaptor.value.`package`).isNull() Loading @@ -103,7 +110,12 @@ class GrowthInteractorTest : SysuiTestCase() { advanceTimeBy(GROWTH_BROADCAST_DELAY.plus(DURATION_50_MILLIS)) runCurrent() verify(broadcastSender, times(1)).sendBroadcast(capture(intentArgumentCaptor)) verify(broadcastSender, times(1)) .sendBroadcastAsUser( capture(intentArgumentCaptor), capture(userHandleArgumentCaptor) ) assertThat(userHandleArgumentCaptor.value).isEqualTo(UserHandle.CURRENT) assertThat(intentArgumentCaptor.value.action) .isEqualTo(GrowthInteractor.ACTION_DEVICE_ENTERED_DIRECTLY) assertThat(intentArgumentCaptor.value.`package`).isNull() Loading @@ -120,7 +132,12 @@ class GrowthInteractorTest : SysuiTestCase() { advanceTimeBy(GROWTH_BROADCAST_DELAY.plus(DURATION_50_MILLIS)) runCurrent() verify(broadcastSender, times(1)).sendBroadcast(capture(intentArgumentCaptor)) verify(broadcastSender, times(1)) .sendBroadcastAsUser( capture(intentArgumentCaptor), capture(userHandleArgumentCaptor) ) assertThat(userHandleArgumentCaptor.value).isEqualTo(UserHandle.CURRENT) assertThat(intentArgumentCaptor.value.action) .isEqualTo(GrowthInteractor.ACTION_DEVICE_ENTERED_DIRECTLY) assertThat(intentArgumentCaptor.value.`package`).isNull() Loading @@ -137,7 +154,12 @@ class GrowthInteractorTest : SysuiTestCase() { advanceTimeBy(GROWTH_BROADCAST_DELAY.plus(DURATION_50_MILLIS)) runCurrent() verify(broadcastSender, times(1)).sendBroadcast(capture(intentArgumentCaptor)) verify(broadcastSender, times(1)) .sendBroadcastAsUser( capture(intentArgumentCaptor), capture(userHandleArgumentCaptor) ) assertThat(userHandleArgumentCaptor.value).isEqualTo(UserHandle.CURRENT) assertThat(intentArgumentCaptor.value.action) .isEqualTo(GrowthInteractor.ACTION_DEVICE_ENTERED_DIRECTLY) assertThat(intentArgumentCaptor.value.`package`).isNull() Loading @@ -148,13 +170,15 @@ class GrowthInteractorTest : SysuiTestCase() { fun onDeviceNotEnteredDirectly_doNotSendBroadcast_onLockscreen() = kosmos.runTest { overrideResources(GROWTH_APP_PACKAGE_NAME, GROWTH_RECEIVER_CLASS_NAME) underTest = kosmos.growthInteractor underTest.activateIn(kosmos.testScope) setDeviceEntered() advanceTimeBy(GROWTH_BROADCAST_DELAY.plus(DURATION_50_MILLIS)) clearInvocations(broadcastSender) setScene(Scenes.Lockscreen) advanceTimeBy(GROWTH_BROADCAST_DELAY.plus(DURATION_50_MILLIS)) verify(broadcastSender, never()).sendBroadcast(any(), any()) verify(broadcastSender, never()).sendBroadcastAsUser(any(), any()) } @Test Loading @@ -166,12 +190,12 @@ class GrowthInteractorTest : SysuiTestCase() { setDeviceEntered() advanceTimeBy(DURATION_50_MILLIS) runCurrent() verify(broadcastSender, never()).sendBroadcast(any(), any()) verify(broadcastSender, never()).sendBroadcastAsUser(any(), any()) setScene(Scenes.Lockscreen) advanceTimeBy(GROWTH_BROADCAST_DELAY.plus(DURATION_50_MILLIS)) runCurrent() verify(broadcastSender, never()).sendBroadcast(any(), any()) verify(broadcastSender, never()).sendBroadcastAsUser(any(), any()) } private fun overrideResources(packageName: String, receiverClassName: String) { Loading
packages/SystemUI/src/com/android/systemui/growth/domain/interactor/GrowthInteractor.kt +25 −20 Original line number Diff line number Diff line Loading @@ -19,6 +19,7 @@ package com.android.systemui.growth.domain.interactor import android.content.ComponentName import android.content.Intent import android.content.res.Resources import android.os.UserHandle import androidx.annotation.VisibleForTesting import com.android.systemui.broadcast.BroadcastSender import com.android.systemui.dagger.SysUISingleton Loading @@ -28,11 +29,9 @@ import com.android.systemui.lifecycle.ExclusiveActivatable import com.android.systemui.res.R import dagger.Lazy import javax.inject.Inject import kotlinx.coroutines.Job import kotlinx.coroutines.cancel import kotlinx.coroutines.coroutineScope import kotlinx.coroutines.ExperimentalCoroutinesApi import kotlinx.coroutines.delay import kotlinx.coroutines.launch import kotlinx.coroutines.flow.transformLatest /** Interactor to communicate with the growth app. */ @SysUISingleton Loading @@ -49,33 +48,39 @@ constructor( private val growthBroadcastDelayMillis = resources.getInteger(R.integer.config_growthBroadcastDelayMillis) private var sendBroadcastJob: Job? = null @OptIn(ExperimentalCoroutinesApi::class) override suspend fun onActivated(): Nothing { deviceEntryInteractor.get().isDeviceEnteredDirectly.collect { // Cancel any existing job before launching a new one. sendBroadcastJob?.cancel() sendBroadcastJob = null if (it) { // Launch the delayed task. sendBroadcastJob = coroutineScope { launch { deviceEntryInteractor // When the device is entered directly (`isEntered` is true), wait for a delay and then // emit. `transformLatest` will cancel the delay if `isDeviceEnteredDirectly` emits a // new value (e.g. `false` when the device is locked) before the delay is finished, // thus cancelling the broadcast. .get() .isDeviceEnteredDirectly .transformLatest { isEntered -> if (isEntered) { delay(growthBroadcastDelayMillis.toLong()) sendBroadcast() } emit(Unit) } } .collect { sendBroadcast() } // The underlying flow should never complete, so this line should not be reachable. throw IllegalStateException("isDeviceEnteredDirectly flow completed unexpectedly") } /** * Sends a broadcast to the growth app for the current user to notify that the device has been * entered directly. The broadcast is explicit if a package and receiver class are configured. */ private suspend fun sendBroadcast() { // Broadcast the device entered event. val intent = Intent().apply { setAction(ACTION_DEVICE_ENTERED_DIRECTLY) } if (growthAppPackageName.isNotEmpty() && growthReceiverClassName.isNotEmpty()) { intent.setComponent(ComponentName(growthAppPackageName, growthReceiverClassName)) } broadcastSender.sendBroadcast(intent) broadcastSender.sendBroadcastAsUser(intent, UserHandle.CURRENT) } companion object { Loading