Loading packages/SystemUI/src/com/android/systemui/statusbar/pipeline/satellite/ui/viewmodel/DeviceBasedSatelliteViewModel.kt +42 −11 Original line number Diff line number Diff line Loading @@ -18,15 +18,22 @@ package com.android.systemui.statusbar.pipeline.satellite.ui.viewmodel import com.android.systemui.common.shared.model.Icon import com.android.systemui.dagger.qualifiers.Application import com.android.systemui.log.LogBuffer import com.android.systemui.log.core.LogLevel import com.android.systemui.statusbar.pipeline.airplane.data.repository.AirplaneModeRepository import com.android.systemui.statusbar.pipeline.dagger.OemSatelliteInputLog import com.android.systemui.statusbar.pipeline.satellite.domain.interactor.DeviceBasedSatelliteInteractor import com.android.systemui.statusbar.pipeline.satellite.ui.model.SatelliteIconModel import javax.inject.Inject import kotlin.time.Duration.Companion.seconds import kotlinx.coroutines.CoroutineScope import kotlinx.coroutines.ExperimentalCoroutinesApi import kotlinx.coroutines.delay import kotlinx.coroutines.flow.Flow import kotlinx.coroutines.flow.SharingStarted import kotlinx.coroutines.flow.StateFlow import kotlinx.coroutines.flow.combine import kotlinx.coroutines.flow.distinctUntilChanged import kotlinx.coroutines.flow.flatMapLatest import kotlinx.coroutines.flow.flowOf import kotlinx.coroutines.flow.stateIn Loading @@ -42,10 +49,10 @@ constructor( interactor: DeviceBasedSatelliteInteractor, @Application scope: CoroutineScope, airplaneModeRepository: AirplaneModeRepository, @OemSatelliteInputLog logBuffer: LogBuffer, ) { private val shouldShowIcon: StateFlow<Boolean> = interactor.areAllConnectionsOutOfService .flatMapLatest { allOos -> private val shouldShowIcon: Flow<Boolean> = interactor.areAllConnectionsOutOfService.flatMapLatest { allOos -> if (!allOos) { flowOf(false) } else { Loading @@ -56,11 +63,30 @@ constructor( } } } // This adds a 10 seconds delay before showing the icon private val shouldActuallyShowIcon: StateFlow<Boolean> = shouldShowIcon .distinctUntilChanged() .flatMapLatest { shouldShow -> if (shouldShow) { logBuffer.log( TAG, LogLevel.INFO, { long1 = DELAY_DURATION.inWholeSeconds }, { "Waiting $long1 seconds before showing the satellite icon" } ) delay(DELAY_DURATION) flowOf(true) } else { flowOf(false) } } .stateIn(scope, SharingStarted.WhileSubscribed(), false) val icon: StateFlow<Icon?> = combine( shouldShowIcon, shouldActuallyShowIcon, interactor.connectionState, interactor.signalStrength, ) { shouldShow, state, signalStrength -> Loading @@ -71,4 +97,9 @@ constructor( } } .stateIn(scope, SharingStarted.WhileSubscribed(), null) companion object { private const val TAG = "DeviceBasedSatelliteViewModel" private val DELAY_DURATION = 10.seconds } } packages/SystemUI/tests/src/com/android/systemui/statusbar/pipeline/satellite/ui/viewmodel/DeviceBasedSatelliteViewModelTest.kt +46 −2 Original line number Diff line number Diff line Loading @@ -20,6 +20,7 @@ import androidx.test.filters.SmallTest import com.android.systemui.SysuiTestCase import com.android.systemui.common.shared.model.Icon import com.android.systemui.coroutines.collectLastValue import com.android.systemui.log.core.FakeLogBuffer import com.android.systemui.statusbar.pipeline.airplane.data.repository.FakeAirplaneModeRepository import com.android.systemui.statusbar.pipeline.mobile.domain.interactor.FakeMobileIconsInteractor import com.android.systemui.statusbar.pipeline.mobile.util.FakeMobileMappingsProxy Loading @@ -28,7 +29,10 @@ import com.android.systemui.statusbar.pipeline.satellite.domain.interactor.Devic import com.android.systemui.util.mockito.mock import com.google.common.truth.Truth.assertThat import kotlin.test.Test import kotlin.time.Duration.Companion.seconds import kotlinx.coroutines.ExperimentalCoroutinesApi import kotlinx.coroutines.test.TestScope import kotlinx.coroutines.test.advanceTimeBy import kotlinx.coroutines.test.runTest import org.junit.Before import org.mockito.MockitoAnnotations Loading Loading @@ -61,6 +65,7 @@ class DeviceBasedSatelliteViewModelTest : SysuiTestCase() { interactor, testScope.backgroundScope, airplaneModeRepository, FakeLogBuffer.Factory.create(), ) } Loading Loading @@ -121,8 +126,9 @@ class DeviceBasedSatelliteViewModelTest : SysuiTestCase() { assertThat(latest).isNull() } @OptIn(ExperimentalCoroutinesApi::class) @Test fun icon_satelliteIsOff() = fun icon_satelliteIsOn() = testScope.runTest { val latest by collectLastValue(underTest.icon) Loading @@ -133,7 +139,45 @@ class DeviceBasedSatelliteViewModelTest : SysuiTestCase() { val i1 = mobileIconsInteractor.getMobileConnectionInteractorForSubId(1) i1.isInService.value = false // THEN icon is null because we have service // GIVEN apm is disabled airplaneModeRepository.setIsAirplaneMode(false) // Wait for delay to be completed advanceTimeBy(10.seconds) // THEN icon is set because we don't have service assertThat(latest).isInstanceOf(Icon::class.java) } @OptIn(ExperimentalCoroutinesApi::class) @Test fun icon_hysteresisWhenEnablingIcon() = testScope.runTest { val latest by collectLastValue(underTest.icon) // GIVEN satellite is allowed repo.isSatelliteAllowedForCurrentLocation.value = true // GIVEN all icons are OOS val i1 = mobileIconsInteractor.getMobileConnectionInteractorForSubId(1) i1.isInService.value = false // GIVEN apm is disabled airplaneModeRepository.setIsAirplaneMode(false) // THEN icon is null because of the hysteresis assertThat(latest).isNull() // Wait for delay to be completed advanceTimeBy(10.seconds) // THEN icon is set after the delay assertThat(latest).isInstanceOf(Icon::class.java) // GIVEN apm is enabled airplaneModeRepository.setIsAirplaneMode(true) // THEN icon is null immediately assertThat(latest).isNull() } } Loading
packages/SystemUI/src/com/android/systemui/statusbar/pipeline/satellite/ui/viewmodel/DeviceBasedSatelliteViewModel.kt +42 −11 Original line number Diff line number Diff line Loading @@ -18,15 +18,22 @@ package com.android.systemui.statusbar.pipeline.satellite.ui.viewmodel import com.android.systemui.common.shared.model.Icon import com.android.systemui.dagger.qualifiers.Application import com.android.systemui.log.LogBuffer import com.android.systemui.log.core.LogLevel import com.android.systemui.statusbar.pipeline.airplane.data.repository.AirplaneModeRepository import com.android.systemui.statusbar.pipeline.dagger.OemSatelliteInputLog import com.android.systemui.statusbar.pipeline.satellite.domain.interactor.DeviceBasedSatelliteInteractor import com.android.systemui.statusbar.pipeline.satellite.ui.model.SatelliteIconModel import javax.inject.Inject import kotlin.time.Duration.Companion.seconds import kotlinx.coroutines.CoroutineScope import kotlinx.coroutines.ExperimentalCoroutinesApi import kotlinx.coroutines.delay import kotlinx.coroutines.flow.Flow import kotlinx.coroutines.flow.SharingStarted import kotlinx.coroutines.flow.StateFlow import kotlinx.coroutines.flow.combine import kotlinx.coroutines.flow.distinctUntilChanged import kotlinx.coroutines.flow.flatMapLatest import kotlinx.coroutines.flow.flowOf import kotlinx.coroutines.flow.stateIn Loading @@ -42,10 +49,10 @@ constructor( interactor: DeviceBasedSatelliteInteractor, @Application scope: CoroutineScope, airplaneModeRepository: AirplaneModeRepository, @OemSatelliteInputLog logBuffer: LogBuffer, ) { private val shouldShowIcon: StateFlow<Boolean> = interactor.areAllConnectionsOutOfService .flatMapLatest { allOos -> private val shouldShowIcon: Flow<Boolean> = interactor.areAllConnectionsOutOfService.flatMapLatest { allOos -> if (!allOos) { flowOf(false) } else { Loading @@ -56,11 +63,30 @@ constructor( } } } // This adds a 10 seconds delay before showing the icon private val shouldActuallyShowIcon: StateFlow<Boolean> = shouldShowIcon .distinctUntilChanged() .flatMapLatest { shouldShow -> if (shouldShow) { logBuffer.log( TAG, LogLevel.INFO, { long1 = DELAY_DURATION.inWholeSeconds }, { "Waiting $long1 seconds before showing the satellite icon" } ) delay(DELAY_DURATION) flowOf(true) } else { flowOf(false) } } .stateIn(scope, SharingStarted.WhileSubscribed(), false) val icon: StateFlow<Icon?> = combine( shouldShowIcon, shouldActuallyShowIcon, interactor.connectionState, interactor.signalStrength, ) { shouldShow, state, signalStrength -> Loading @@ -71,4 +97,9 @@ constructor( } } .stateIn(scope, SharingStarted.WhileSubscribed(), null) companion object { private const val TAG = "DeviceBasedSatelliteViewModel" private val DELAY_DURATION = 10.seconds } }
packages/SystemUI/tests/src/com/android/systemui/statusbar/pipeline/satellite/ui/viewmodel/DeviceBasedSatelliteViewModelTest.kt +46 −2 Original line number Diff line number Diff line Loading @@ -20,6 +20,7 @@ import androidx.test.filters.SmallTest import com.android.systemui.SysuiTestCase import com.android.systemui.common.shared.model.Icon import com.android.systemui.coroutines.collectLastValue import com.android.systemui.log.core.FakeLogBuffer import com.android.systemui.statusbar.pipeline.airplane.data.repository.FakeAirplaneModeRepository import com.android.systemui.statusbar.pipeline.mobile.domain.interactor.FakeMobileIconsInteractor import com.android.systemui.statusbar.pipeline.mobile.util.FakeMobileMappingsProxy Loading @@ -28,7 +29,10 @@ import com.android.systemui.statusbar.pipeline.satellite.domain.interactor.Devic import com.android.systemui.util.mockito.mock import com.google.common.truth.Truth.assertThat import kotlin.test.Test import kotlin.time.Duration.Companion.seconds import kotlinx.coroutines.ExperimentalCoroutinesApi import kotlinx.coroutines.test.TestScope import kotlinx.coroutines.test.advanceTimeBy import kotlinx.coroutines.test.runTest import org.junit.Before import org.mockito.MockitoAnnotations Loading Loading @@ -61,6 +65,7 @@ class DeviceBasedSatelliteViewModelTest : SysuiTestCase() { interactor, testScope.backgroundScope, airplaneModeRepository, FakeLogBuffer.Factory.create(), ) } Loading Loading @@ -121,8 +126,9 @@ class DeviceBasedSatelliteViewModelTest : SysuiTestCase() { assertThat(latest).isNull() } @OptIn(ExperimentalCoroutinesApi::class) @Test fun icon_satelliteIsOff() = fun icon_satelliteIsOn() = testScope.runTest { val latest by collectLastValue(underTest.icon) Loading @@ -133,7 +139,45 @@ class DeviceBasedSatelliteViewModelTest : SysuiTestCase() { val i1 = mobileIconsInteractor.getMobileConnectionInteractorForSubId(1) i1.isInService.value = false // THEN icon is null because we have service // GIVEN apm is disabled airplaneModeRepository.setIsAirplaneMode(false) // Wait for delay to be completed advanceTimeBy(10.seconds) // THEN icon is set because we don't have service assertThat(latest).isInstanceOf(Icon::class.java) } @OptIn(ExperimentalCoroutinesApi::class) @Test fun icon_hysteresisWhenEnablingIcon() = testScope.runTest { val latest by collectLastValue(underTest.icon) // GIVEN satellite is allowed repo.isSatelliteAllowedForCurrentLocation.value = true // GIVEN all icons are OOS val i1 = mobileIconsInteractor.getMobileConnectionInteractorForSubId(1) i1.isInService.value = false // GIVEN apm is disabled airplaneModeRepository.setIsAirplaneMode(false) // THEN icon is null because of the hysteresis assertThat(latest).isNull() // Wait for delay to be completed advanceTimeBy(10.seconds) // THEN icon is set after the delay assertThat(latest).isInstanceOf(Icon::class.java) // GIVEN apm is enabled airplaneModeRepository.setIsAirplaneMode(true) // THEN icon is null immediately assertThat(latest).isNull() } }