[SB refactor] Add connectivity tracking to the mobile pipeline
This CL tracks 2 bits from ConnectivityManager for mobile networks: isConnected and isValidated. These bits are used to know whether or not cellular networks are the default transport, and whether or not connectivitymanager has validated that transport. Test: manual Test: atest packages/SystemUI/tests/src/com/android/systemui/statusbar/pipeline/mobile/* Bug: 240492102 Change-Id: I13890cdc59c198c1b5290ae92e7ec4ba491999e3
This commit is contained in:
@@ -0,0 +1,27 @@
|
||||
/*
|
||||
* Copyright (C) 2022 The Android Open Source Project
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
package com.android.systemui.statusbar.pipeline.mobile.data.model
|
||||
|
||||
import android.net.NetworkCapabilities
|
||||
|
||||
/** Provides information about a mobile network connection */
|
||||
data class MobileConnectivityModel(
|
||||
/** Whether mobile is the connected transport see [NetworkCapabilities.TRANSPORT_CELLULAR] */
|
||||
val isConnected: Boolean = false,
|
||||
/** Whether the mobile transport is validated [NetworkCapabilities.NET_CAPABILITY_VALIDATED] */
|
||||
val isValidated: Boolean = false,
|
||||
)
|
||||
@@ -16,9 +16,16 @@
|
||||
|
||||
package com.android.systemui.statusbar.pipeline.mobile.data.repository
|
||||
|
||||
import android.annotation.SuppressLint
|
||||
import android.content.Context
|
||||
import android.content.IntentFilter
|
||||
import android.database.ContentObserver
|
||||
import android.net.ConnectivityManager
|
||||
import android.net.ConnectivityManager.NetworkCallback
|
||||
import android.net.Network
|
||||
import android.net.NetworkCapabilities
|
||||
import android.net.NetworkCapabilities.NET_CAPABILITY_VALIDATED
|
||||
import android.net.NetworkCapabilities.TRANSPORT_CELLULAR
|
||||
import android.provider.Settings
|
||||
import android.provider.Settings.Global.MOBILE_DATA
|
||||
import android.telephony.CarrierConfigManager
|
||||
@@ -37,6 +44,7 @@ import com.android.systemui.common.coroutine.ConflatedCallbackFlow.conflatedCall
|
||||
import com.android.systemui.dagger.SysUISingleton
|
||||
import com.android.systemui.dagger.qualifiers.Application
|
||||
import com.android.systemui.dagger.qualifiers.Background
|
||||
import com.android.systemui.statusbar.pipeline.mobile.data.model.MobileConnectivityModel
|
||||
import com.android.systemui.statusbar.pipeline.shared.ConnectivityPipelineLogger
|
||||
import com.android.systemui.util.settings.GlobalSettings
|
||||
import javax.inject.Inject
|
||||
@@ -73,6 +81,9 @@ interface MobileConnectionsRepository {
|
||||
/** Tracks [SubscriptionManager.getDefaultDataSubscriptionId] */
|
||||
val defaultDataSubId: StateFlow<Int>
|
||||
|
||||
/** The current connectivity status for the default mobile network connection */
|
||||
val defaultMobileNetworkConnectivity: StateFlow<MobileConnectivityModel>
|
||||
|
||||
/** Get or create a repository for the line of service for the given subscription ID */
|
||||
fun getRepoForSubId(subId: Int): MobileConnectionRepository
|
||||
|
||||
@@ -86,6 +97,7 @@ interface MobileConnectionsRepository {
|
||||
class MobileConnectionsRepositoryImpl
|
||||
@Inject
|
||||
constructor(
|
||||
private val connectivityManager: ConnectivityManager,
|
||||
private val subscriptionManager: SubscriptionManager,
|
||||
private val telephonyManager: TelephonyManager,
|
||||
private val logger: ConnectivityPipelineLogger,
|
||||
@@ -212,6 +224,36 @@ constructor(
|
||||
awaitClose { context.contentResolver.unregisterContentObserver(observer) }
|
||||
}
|
||||
|
||||
@SuppressLint("MissingPermission")
|
||||
override val defaultMobileNetworkConnectivity: StateFlow<MobileConnectivityModel> =
|
||||
conflatedCallbackFlow {
|
||||
val callback =
|
||||
object : NetworkCallback(FLAG_INCLUDE_LOCATION_INFO) {
|
||||
override fun onLost(network: Network) {
|
||||
// Send a disconnected model when lost. Maybe should create a sealed
|
||||
// type or null here?
|
||||
trySend(MobileConnectivityModel())
|
||||
}
|
||||
|
||||
override fun onCapabilitiesChanged(
|
||||
network: Network,
|
||||
caps: NetworkCapabilities
|
||||
) {
|
||||
trySend(
|
||||
MobileConnectivityModel(
|
||||
isConnected = caps.hasTransport(TRANSPORT_CELLULAR),
|
||||
isValidated = caps.hasCapability(NET_CAPABILITY_VALIDATED),
|
||||
)
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
connectivityManager.registerDefaultNetworkCallback(callback)
|
||||
|
||||
awaitClose { connectivityManager.unregisterNetworkCallback(callback) }
|
||||
}
|
||||
.stateIn(scope, SharingStarted.WhileSubscribed(), MobileConnectivityModel())
|
||||
|
||||
private fun isValidSubId(subId: Int): Boolean {
|
||||
subscriptionsFlow.value.forEach {
|
||||
if (it.subscriptionId == subId) {
|
||||
|
||||
@@ -26,7 +26,6 @@ import com.android.systemui.statusbar.pipeline.mobile.util.MobileMappingsProxy
|
||||
import com.android.systemui.util.CarrierConfigTracker
|
||||
import kotlinx.coroutines.CoroutineScope
|
||||
import kotlinx.coroutines.ExperimentalCoroutinesApi
|
||||
import kotlinx.coroutines.flow.Flow
|
||||
import kotlinx.coroutines.flow.MutableStateFlow
|
||||
import kotlinx.coroutines.flow.SharingStarted
|
||||
import kotlinx.coroutines.flow.StateFlow
|
||||
@@ -35,6 +34,9 @@ import kotlinx.coroutines.flow.mapLatest
|
||||
import kotlinx.coroutines.flow.stateIn
|
||||
|
||||
interface MobileIconInteractor {
|
||||
/** Only true if mobile is the default transport but is not validated, otherwise false */
|
||||
val isDefaultConnectionFailed: StateFlow<Boolean>
|
||||
|
||||
// TODO(b/256839546): clarify naming of default vs active
|
||||
/** True if we want to consider the data connection enabled */
|
||||
val isDefaultDataEnabled: StateFlow<Boolean>
|
||||
@@ -63,6 +65,7 @@ class MobileIconInteractorImpl(
|
||||
defaultSubscriptionHasDataEnabled: StateFlow<Boolean>,
|
||||
defaultMobileIconMapping: StateFlow<Map<String, MobileIconGroup>>,
|
||||
defaultMobileIconGroup: StateFlow<MobileIconGroup>,
|
||||
override val isDefaultConnectionFailed: StateFlow<Boolean>,
|
||||
mobileMappingsProxy: MobileMappingsProxy,
|
||||
connectionRepository: MobileConnectionRepository,
|
||||
) : MobileIconInteractor {
|
||||
|
||||
@@ -60,6 +60,8 @@ interface MobileIconsInteractor {
|
||||
val defaultMobileIconMapping: StateFlow<Map<String, MobileIconGroup>>
|
||||
/** Fallback [MobileIconGroup] in the case where there is no icon in the mapping */
|
||||
val defaultMobileIconGroup: StateFlow<MobileIconGroup>
|
||||
/** True only if the default network is mobile, and validation also failed */
|
||||
val isDefaultConnectionFailed: StateFlow<Boolean>
|
||||
/** True once the user has been set up */
|
||||
val isUserSetup: StateFlow<Boolean>
|
||||
/**
|
||||
@@ -162,6 +164,21 @@ constructor(
|
||||
.mapLatest { mobileMappingsProxy.getDefaultIcons(it) }
|
||||
.stateIn(scope, SharingStarted.WhileSubscribed(), initialValue = TelephonyIcons.G)
|
||||
|
||||
/**
|
||||
* We want to show an error state when cellular has actually failed to validate, but not if some
|
||||
* other transport type is active, because then we expect there not to be validation.
|
||||
*/
|
||||
override val isDefaultConnectionFailed: StateFlow<Boolean> =
|
||||
mobileConnectionsRepo.defaultMobileNetworkConnectivity
|
||||
.mapLatest { connectivityModel ->
|
||||
if (!connectivityModel.isConnected) {
|
||||
false
|
||||
} else {
|
||||
!connectivityModel.isValidated
|
||||
}
|
||||
}
|
||||
.stateIn(scope, SharingStarted.WhileSubscribed(), false)
|
||||
|
||||
override val isUserSetup: StateFlow<Boolean> = userSetupRepo.isUserSetupFlow
|
||||
|
||||
/** Vends out new [MobileIconInteractor] for a particular subId */
|
||||
@@ -171,6 +188,7 @@ constructor(
|
||||
activeDataConnectionHasDataEnabled,
|
||||
defaultMobileIconMapping,
|
||||
defaultMobileIconGroup,
|
||||
isDefaultConnectionFailed,
|
||||
mobileMappingsProxy,
|
||||
mobileConnectionsRepo.getRepoForSubId(subId),
|
||||
)
|
||||
|
||||
@@ -66,10 +66,12 @@ constructor(
|
||||
|
||||
/** The RAT icon (LTE, 3G, 5G, etc) to be displayed. Null if we shouldn't show anything */
|
||||
val networkTypeIcon: Flow<Icon?> =
|
||||
combine(iconInteractor.networkTypeIconGroup, iconInteractor.isDataEnabled) {
|
||||
networkTypeIconGroup,
|
||||
isDataEnabled ->
|
||||
if (!isDataEnabled) {
|
||||
combine(
|
||||
iconInteractor.networkTypeIconGroup,
|
||||
iconInteractor.isDataEnabled,
|
||||
iconInteractor.isDefaultConnectionFailed
|
||||
) { networkTypeIconGroup, isDataEnabled, isFailedConnection ->
|
||||
if (!isDataEnabled || isFailedConnection) {
|
||||
null
|
||||
} else {
|
||||
val desc =
|
||||
|
||||
@@ -19,6 +19,7 @@ package com.android.systemui.statusbar.pipeline.mobile.data.repository
|
||||
import android.telephony.SubscriptionInfo
|
||||
import android.telephony.SubscriptionManager.INVALID_SUBSCRIPTION_ID
|
||||
import com.android.settingslib.mobile.MobileMappings.Config
|
||||
import com.android.systemui.statusbar.pipeline.mobile.data.model.MobileConnectivityModel
|
||||
import kotlinx.coroutines.flow.Flow
|
||||
import kotlinx.coroutines.flow.MutableStateFlow
|
||||
|
||||
@@ -35,6 +36,9 @@ class FakeMobileConnectionsRepository : MobileConnectionsRepository {
|
||||
private val _defaultDataSubId = MutableStateFlow(INVALID_SUBSCRIPTION_ID)
|
||||
override val defaultDataSubId = _defaultDataSubId
|
||||
|
||||
private val _mobileConnectivity = MutableStateFlow(MobileConnectivityModel())
|
||||
override val defaultMobileNetworkConnectivity = _mobileConnectivity
|
||||
|
||||
private val subIdRepos = mutableMapOf<Int, MobileConnectionRepository>()
|
||||
override fun getRepoForSubId(subId: Int): MobileConnectionRepository {
|
||||
return subIdRepos[subId] ?: FakeMobileConnectionRepository().also { subIdRepos[subId] = it }
|
||||
@@ -55,6 +59,10 @@ class FakeMobileConnectionsRepository : MobileConnectionsRepository {
|
||||
_defaultDataSubId.value = id
|
||||
}
|
||||
|
||||
fun setMobileConnectivity(model: MobileConnectivityModel) {
|
||||
_mobileConnectivity.value = model
|
||||
}
|
||||
|
||||
suspend fun triggerGlobalMobileDataSettingChangedEvent() {
|
||||
_globalMobileDataSettingChangedEvent.emit(Unit)
|
||||
}
|
||||
|
||||
@@ -17,6 +17,11 @@
|
||||
package com.android.systemui.statusbar.pipeline.mobile.data.repository
|
||||
|
||||
import android.content.Intent
|
||||
import android.net.ConnectivityManager
|
||||
import android.net.Network
|
||||
import android.net.NetworkCapabilities
|
||||
import android.net.NetworkCapabilities.NET_CAPABILITY_VALIDATED
|
||||
import android.net.NetworkCapabilities.TRANSPORT_CELLULAR
|
||||
import android.provider.Settings
|
||||
import android.telephony.SubscriptionInfo
|
||||
import android.telephony.SubscriptionManager
|
||||
@@ -26,6 +31,7 @@ import android.telephony.TelephonyManager
|
||||
import androidx.test.filters.SmallTest
|
||||
import com.android.internal.telephony.PhoneConstants
|
||||
import com.android.systemui.SysuiTestCase
|
||||
import com.android.systemui.statusbar.pipeline.mobile.data.model.MobileConnectivityModel
|
||||
import com.android.systemui.statusbar.pipeline.shared.ConnectivityPipelineLogger
|
||||
import com.android.systemui.util.mockito.any
|
||||
import com.android.systemui.util.mockito.argumentCaptor
|
||||
@@ -54,6 +60,7 @@ import org.mockito.MockitoAnnotations
|
||||
class MobileConnectionsRepositoryTest : SysuiTestCase() {
|
||||
private lateinit var underTest: MobileConnectionsRepositoryImpl
|
||||
|
||||
@Mock private lateinit var connectivityManager: ConnectivityManager
|
||||
@Mock private lateinit var subscriptionManager: SubscriptionManager
|
||||
@Mock private lateinit var telephonyManager: TelephonyManager
|
||||
@Mock private lateinit var logger: ConnectivityPipelineLogger
|
||||
@@ -67,6 +74,7 @@ class MobileConnectionsRepositoryTest : SysuiTestCase() {
|
||||
|
||||
underTest =
|
||||
MobileConnectionsRepositoryImpl(
|
||||
connectivityManager,
|
||||
subscriptionManager,
|
||||
telephonyManager,
|
||||
logger,
|
||||
@@ -235,6 +243,29 @@ class MobileConnectionsRepositoryTest : SysuiTestCase() {
|
||||
job.cancel()
|
||||
}
|
||||
|
||||
@Test
|
||||
fun mobileConnectivity_default() {
|
||||
assertThat(underTest.defaultMobileNetworkConnectivity.value)
|
||||
.isEqualTo(MobileConnectivityModel(isConnected = false, isValidated = false))
|
||||
}
|
||||
|
||||
@Test
|
||||
fun mobileConnectivity_isConnected_isValidated() =
|
||||
runBlocking(IMMEDIATE) {
|
||||
val caps = createCapabilities(connected = true, validated = true)
|
||||
|
||||
var latest: MobileConnectivityModel? = null
|
||||
val job =
|
||||
underTest.defaultMobileNetworkConnectivity.onEach { latest = it }.launchIn(this)
|
||||
|
||||
getDefaultNetworkCallback().onCapabilitiesChanged(NETWORK, caps)
|
||||
|
||||
assertThat(latest)
|
||||
.isEqualTo(MobileConnectivityModel(isConnected = true, isValidated = true))
|
||||
|
||||
job.cancel()
|
||||
}
|
||||
|
||||
@Test
|
||||
fun globalMobileDataSettingsChangedEvent_producesOnSettingChange() =
|
||||
runBlocking(IMMEDIATE) {
|
||||
@@ -253,6 +284,69 @@ class MobileConnectionsRepositoryTest : SysuiTestCase() {
|
||||
job.cancel()
|
||||
}
|
||||
|
||||
@Test
|
||||
fun mobileConnectivity_isConnected_isNotValidated() =
|
||||
runBlocking(IMMEDIATE) {
|
||||
val caps = createCapabilities(connected = true, validated = false)
|
||||
|
||||
var latest: MobileConnectivityModel? = null
|
||||
val job =
|
||||
underTest.defaultMobileNetworkConnectivity.onEach { latest = it }.launchIn(this)
|
||||
|
||||
getDefaultNetworkCallback().onCapabilitiesChanged(NETWORK, caps)
|
||||
|
||||
assertThat(latest)
|
||||
.isEqualTo(MobileConnectivityModel(isConnected = true, isValidated = false))
|
||||
|
||||
job.cancel()
|
||||
}
|
||||
|
||||
@Test
|
||||
fun mobileConnectivity_isNotConnected_isNotValidated() =
|
||||
runBlocking(IMMEDIATE) {
|
||||
val caps = createCapabilities(connected = false, validated = false)
|
||||
|
||||
var latest: MobileConnectivityModel? = null
|
||||
val job =
|
||||
underTest.defaultMobileNetworkConnectivity.onEach { latest = it }.launchIn(this)
|
||||
|
||||
getDefaultNetworkCallback().onCapabilitiesChanged(NETWORK, caps)
|
||||
|
||||
assertThat(latest)
|
||||
.isEqualTo(MobileConnectivityModel(isConnected = false, isValidated = false))
|
||||
|
||||
job.cancel()
|
||||
}
|
||||
|
||||
/** In practice, I don't think this state can ever happen (!connected, validated) */
|
||||
@Test
|
||||
fun mobileConnectivity_isNotConnected_isValidated() =
|
||||
runBlocking(IMMEDIATE) {
|
||||
val caps = createCapabilities(connected = false, validated = true)
|
||||
|
||||
var latest: MobileConnectivityModel? = null
|
||||
val job =
|
||||
underTest.defaultMobileNetworkConnectivity.onEach { latest = it }.launchIn(this)
|
||||
|
||||
getDefaultNetworkCallback().onCapabilitiesChanged(NETWORK, caps)
|
||||
|
||||
assertThat(latest).isEqualTo(MobileConnectivityModel(false, true))
|
||||
|
||||
job.cancel()
|
||||
}
|
||||
|
||||
private fun createCapabilities(connected: Boolean, validated: Boolean): NetworkCapabilities =
|
||||
mock<NetworkCapabilities>().also {
|
||||
whenever(it.hasTransport(TRANSPORT_CELLULAR)).thenReturn(connected)
|
||||
whenever(it.hasCapability(NET_CAPABILITY_VALIDATED)).thenReturn(validated)
|
||||
}
|
||||
|
||||
private fun getDefaultNetworkCallback(): ConnectivityManager.NetworkCallback {
|
||||
val callbackCaptor = argumentCaptor<ConnectivityManager.NetworkCallback>()
|
||||
verify(connectivityManager).registerDefaultNetworkCallback(callbackCaptor.capture())
|
||||
return callbackCaptor.value!!
|
||||
}
|
||||
|
||||
private fun getSubscriptionCallback(): SubscriptionManager.OnSubscriptionsChangedListener {
|
||||
val callbackCaptor = argumentCaptor<SubscriptionManager.OnSubscriptionsChangedListener>()
|
||||
verify(subscriptionManager)
|
||||
@@ -281,5 +375,8 @@ class MobileConnectionsRepositoryTest : SysuiTestCase() {
|
||||
private const val SUB_2_ID = 2
|
||||
private val SUB_2 =
|
||||
mock<SubscriptionInfo>().also { whenever(it.subscriptionId).thenReturn(SUB_2_ID) }
|
||||
|
||||
private const val NET_ID = 123
|
||||
private val NETWORK = mock<Network>().apply { whenever(getNetId()).thenReturn(NET_ID) }
|
||||
}
|
||||
}
|
||||
|
||||
@@ -28,6 +28,9 @@ class FakeMobileIconInteractor : MobileIconInteractor {
|
||||
private val _isEmergencyOnly = MutableStateFlow(false)
|
||||
override val isEmergencyOnly = _isEmergencyOnly
|
||||
|
||||
private val _isFailedConnection = MutableStateFlow(false)
|
||||
override val isDefaultConnectionFailed = _isFailedConnection
|
||||
|
||||
private val _isDataEnabled = MutableStateFlow(true)
|
||||
override val isDataEnabled = _isDataEnabled
|
||||
|
||||
@@ -56,6 +59,10 @@ class FakeMobileIconInteractor : MobileIconInteractor {
|
||||
_isDefaultDataEnabled.value = disabled
|
||||
}
|
||||
|
||||
fun setIsFailedConnection(failed: Boolean) {
|
||||
_isFailedConnection.value = failed
|
||||
}
|
||||
|
||||
fun setLevel(level: Int) {
|
||||
_level.value = level
|
||||
}
|
||||
|
||||
@@ -45,6 +45,8 @@ class FakeMobileIconsInteractor(mobileMappings: MobileMappingsProxy) : MobileIco
|
||||
FIVE_G_OVERRIDE_KEY to TelephonyIcons.NR_5G,
|
||||
)
|
||||
|
||||
override val isDefaultConnectionFailed = MutableStateFlow(false)
|
||||
|
||||
private val _filteredSubscriptions = MutableStateFlow<List<SubscriptionInfo>>(listOf())
|
||||
override val filteredSubscriptions = _filteredSubscriptions
|
||||
|
||||
|
||||
@@ -60,6 +60,7 @@ class MobileIconInteractorTest : SysuiTestCase() {
|
||||
mobileIconsInteractor.activeDataConnectionHasDataEnabled,
|
||||
mobileIconsInteractor.defaultMobileIconMapping,
|
||||
mobileIconsInteractor.defaultMobileIconGroup,
|
||||
mobileIconsInteractor.isDefaultConnectionFailed,
|
||||
mobileMappingsProxy,
|
||||
connectionRepository,
|
||||
)
|
||||
@@ -216,6 +217,20 @@ class MobileIconInteractorTest : SysuiTestCase() {
|
||||
job.cancel()
|
||||
}
|
||||
|
||||
@Test
|
||||
fun test_isDefaultConnectionFailed_matchedParent() =
|
||||
runBlocking(IMMEDIATE) {
|
||||
val job = underTest.isDefaultConnectionFailed.launchIn(this)
|
||||
|
||||
mobileIconsInteractor.isDefaultConnectionFailed.value = false
|
||||
assertThat(underTest.isDefaultConnectionFailed.value).isFalse()
|
||||
|
||||
mobileIconsInteractor.isDefaultConnectionFailed.value = true
|
||||
assertThat(underTest.isDefaultConnectionFailed.value).isTrue()
|
||||
|
||||
job.cancel()
|
||||
}
|
||||
|
||||
companion object {
|
||||
private val IMMEDIATE = Dispatchers.Main.immediate
|
||||
|
||||
|
||||
@@ -20,6 +20,7 @@ import android.telephony.SubscriptionInfo
|
||||
import android.telephony.SubscriptionManager.INVALID_SUBSCRIPTION_ID
|
||||
import androidx.test.filters.SmallTest
|
||||
import com.android.systemui.SysuiTestCase
|
||||
import com.android.systemui.statusbar.pipeline.mobile.data.model.MobileConnectivityModel
|
||||
import com.android.systemui.statusbar.pipeline.mobile.data.repository.FakeMobileConnectionRepository
|
||||
import com.android.systemui.statusbar.pipeline.mobile.data.repository.FakeMobileConnectionsRepository
|
||||
import com.android.systemui.statusbar.pipeline.mobile.data.repository.FakeUserSetupRepository
|
||||
@@ -215,6 +216,47 @@ class MobileIconsInteractorTest : SysuiTestCase() {
|
||||
job.cancel()
|
||||
}
|
||||
|
||||
@Test
|
||||
fun failedConnection_connected_validated_notFailed() =
|
||||
runBlocking(IMMEDIATE) {
|
||||
var latest: Boolean? = null
|
||||
val job = underTest.isDefaultConnectionFailed.onEach { latest = it }.launchIn(this)
|
||||
connectionsRepository.setMobileConnectivity(MobileConnectivityModel(true, true))
|
||||
yield()
|
||||
|
||||
assertThat(latest).isFalse()
|
||||
|
||||
job.cancel()
|
||||
}
|
||||
|
||||
@Test
|
||||
fun failedConnection_notConnected_notValidated_notFailed() =
|
||||
runBlocking(IMMEDIATE) {
|
||||
var latest: Boolean? = null
|
||||
val job = underTest.isDefaultConnectionFailed.onEach { latest = it }.launchIn(this)
|
||||
|
||||
connectionsRepository.setMobileConnectivity(MobileConnectivityModel(false, false))
|
||||
yield()
|
||||
|
||||
assertThat(latest).isFalse()
|
||||
|
||||
job.cancel()
|
||||
}
|
||||
|
||||
@Test
|
||||
fun failedConnection_connected_notValidated_failed() =
|
||||
runBlocking(IMMEDIATE) {
|
||||
var latest: Boolean? = null
|
||||
val job = underTest.isDefaultConnectionFailed.onEach { latest = it }.launchIn(this)
|
||||
|
||||
connectionsRepository.setMobileConnectivity(MobileConnectivityModel(true, false))
|
||||
yield()
|
||||
|
||||
assertThat(latest).isTrue()
|
||||
|
||||
job.cancel()
|
||||
}
|
||||
|
||||
companion object {
|
||||
private val IMMEDIATE = Dispatchers.Main.immediate
|
||||
|
||||
|
||||
@@ -47,6 +47,7 @@ class MobileIconViewModelTest : SysuiTestCase() {
|
||||
interactor.apply {
|
||||
setLevel(1)
|
||||
setIsDefaultDataEnabled(true)
|
||||
setIsFailedConnection(false)
|
||||
setIconGroup(THREE_G)
|
||||
setIsEmergencyOnly(false)
|
||||
setNumberOfLevels(4)
|
||||
@@ -111,6 +112,20 @@ class MobileIconViewModelTest : SysuiTestCase() {
|
||||
job.cancel()
|
||||
}
|
||||
|
||||
@Test
|
||||
fun networkType_nullWhenFailedConnection() =
|
||||
runBlocking(IMMEDIATE) {
|
||||
interactor.setIconGroup(THREE_G)
|
||||
interactor.setIsDataEnabled(true)
|
||||
interactor.setIsFailedConnection(true)
|
||||
var latest: Icon? = null
|
||||
val job = underTest.networkTypeIcon.onEach { latest = it }.launchIn(this)
|
||||
|
||||
assertThat(latest).isNull()
|
||||
|
||||
job.cancel()
|
||||
}
|
||||
|
||||
@Test
|
||||
fun networkType_null_changeToDisabled() =
|
||||
runBlocking(IMMEDIATE) {
|
||||
|
||||
Reference in New Issue
Block a user