Merge changes I1c9ade7e,I06365d19,I0945a29d,I8e798197,Ib3a4f14b, ... into tm-qpr-dev
* changes: [Sb refactor] [demo] Use better default for demo mode [Sb refactor] Support NOT_DEFAULT_DATA network type Partial revert of "[SB Refactor] Remove unused `isDefaultDataSubscription` flow." [Sb refactor] Implement 2s grace period for data switching [Sb refactor] Add the default network's connectivity to the view model [Sb refactor] Upgrade MobileIconsInteractorTest to use testScope
This commit is contained in:
@@ -18,6 +18,7 @@ package com.android.systemui.statusbar.pipeline.mobile.data.repository
|
|||||||
|
|
||||||
import android.provider.Settings
|
import android.provider.Settings
|
||||||
import android.telephony.CarrierConfigManager
|
import android.telephony.CarrierConfigManager
|
||||||
|
import android.telephony.SubscriptionManager
|
||||||
import com.android.settingslib.SignalIcon.MobileIconGroup
|
import com.android.settingslib.SignalIcon.MobileIconGroup
|
||||||
import com.android.settingslib.mobile.MobileMappings
|
import com.android.settingslib.mobile.MobileMappings
|
||||||
import com.android.settingslib.mobile.MobileMappings.Config
|
import com.android.settingslib.mobile.MobileMappings.Config
|
||||||
@@ -37,6 +38,15 @@ interface MobileConnectionsRepository {
|
|||||||
/** Observable for the subscriptionId of the current mobile data connection */
|
/** Observable for the subscriptionId of the current mobile data connection */
|
||||||
val activeMobileDataSubscriptionId: StateFlow<Int>
|
val activeMobileDataSubscriptionId: StateFlow<Int>
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Observable event for when the active data sim switches but the group stays the same. E.g.,
|
||||||
|
* CBRS switching would trigger this
|
||||||
|
*/
|
||||||
|
val activeSubChangedInGroupEvent: Flow<Unit>
|
||||||
|
|
||||||
|
/** Tracks [SubscriptionManager.getDefaultDataSubscriptionId] */
|
||||||
|
val defaultDataSubId: StateFlow<Int>
|
||||||
|
|
||||||
/** The current connectivity status for the default mobile network connection */
|
/** The current connectivity status for the default mobile network connection */
|
||||||
val defaultMobileNetworkConnectivity: StateFlow<MobileConnectivityModel>
|
val defaultMobileNetworkConnectivity: StateFlow<MobileConnectivityModel>
|
||||||
|
|
||||||
|
|||||||
@@ -124,6 +124,9 @@ constructor(
|
|||||||
realRepository.activeMobileDataSubscriptionId.value
|
realRepository.activeMobileDataSubscriptionId.value
|
||||||
)
|
)
|
||||||
|
|
||||||
|
override val activeSubChangedInGroupEvent: Flow<Unit> =
|
||||||
|
activeRepo.flatMapLatest { it.activeSubChangedInGroupEvent }
|
||||||
|
|
||||||
override val defaultDataSubRatConfig: StateFlow<MobileMappings.Config> =
|
override val defaultDataSubRatConfig: StateFlow<MobileMappings.Config> =
|
||||||
activeRepo
|
activeRepo
|
||||||
.flatMapLatest { it.defaultDataSubRatConfig }
|
.flatMapLatest { it.defaultDataSubRatConfig }
|
||||||
@@ -139,6 +142,11 @@ constructor(
|
|||||||
override val defaultMobileIconGroup: Flow<SignalIcon.MobileIconGroup> =
|
override val defaultMobileIconGroup: Flow<SignalIcon.MobileIconGroup> =
|
||||||
activeRepo.flatMapLatest { it.defaultMobileIconGroup }
|
activeRepo.flatMapLatest { it.defaultMobileIconGroup }
|
||||||
|
|
||||||
|
override val defaultDataSubId: StateFlow<Int> =
|
||||||
|
activeRepo
|
||||||
|
.flatMapLatest { it.defaultDataSubId }
|
||||||
|
.stateIn(scope, SharingStarted.WhileSubscribed(), realRepository.defaultDataSubId.value)
|
||||||
|
|
||||||
override val defaultMobileNetworkConnectivity: StateFlow<MobileConnectivityModel> =
|
override val defaultMobileNetworkConnectivity: StateFlow<MobileConnectivityModel> =
|
||||||
activeRepo
|
activeRepo
|
||||||
.flatMapLatest { it.defaultMobileNetworkConnectivity }
|
.flatMapLatest { it.defaultMobileNetworkConnectivity }
|
||||||
|
|||||||
@@ -48,6 +48,7 @@ import javax.inject.Inject
|
|||||||
import kotlinx.coroutines.CoroutineScope
|
import kotlinx.coroutines.CoroutineScope
|
||||||
import kotlinx.coroutines.ExperimentalCoroutinesApi
|
import kotlinx.coroutines.ExperimentalCoroutinesApi
|
||||||
import kotlinx.coroutines.Job
|
import kotlinx.coroutines.Job
|
||||||
|
import kotlinx.coroutines.flow.Flow
|
||||||
import kotlinx.coroutines.flow.MutableSharedFlow
|
import kotlinx.coroutines.flow.MutableSharedFlow
|
||||||
import kotlinx.coroutines.flow.MutableStateFlow
|
import kotlinx.coroutines.flow.MutableStateFlow
|
||||||
import kotlinx.coroutines.flow.SharingStarted
|
import kotlinx.coroutines.flow.SharingStarted
|
||||||
@@ -120,6 +121,9 @@ constructor(
|
|||||||
subscriptions.value.firstOrNull()?.subscriptionId ?: INVALID_SUBSCRIPTION_ID
|
subscriptions.value.firstOrNull()?.subscriptionId ?: INVALID_SUBSCRIPTION_ID
|
||||||
)
|
)
|
||||||
|
|
||||||
|
// TODO(b/261029387): consider adding a demo command for this
|
||||||
|
override val activeSubChangedInGroupEvent: Flow<Unit> = flowOf()
|
||||||
|
|
||||||
/** Demo mode doesn't currently support modifications to the mobile mappings */
|
/** Demo mode doesn't currently support modifications to the mobile mappings */
|
||||||
override val defaultDataSubRatConfig =
|
override val defaultDataSubRatConfig =
|
||||||
MutableStateFlow(MobileMappings.Config.readConfig(context))
|
MutableStateFlow(MobileMappings.Config.readConfig(context))
|
||||||
@@ -148,8 +152,12 @@ constructor(
|
|||||||
|
|
||||||
private fun <K, V> Map<K, V>.reverse() = entries.associateBy({ it.value }) { it.key }
|
private fun <K, V> Map<K, V>.reverse() = entries.associateBy({ it.value }) { it.key }
|
||||||
|
|
||||||
|
// TODO(b/261029387): add a command for this value
|
||||||
|
override val defaultDataSubId = MutableStateFlow(INVALID_SUBSCRIPTION_ID)
|
||||||
|
|
||||||
// TODO(b/261029387): not yet supported
|
// TODO(b/261029387): not yet supported
|
||||||
override val defaultMobileNetworkConnectivity = MutableStateFlow(MobileConnectivityModel())
|
override val defaultMobileNetworkConnectivity =
|
||||||
|
MutableStateFlow(MobileConnectivityModel(isConnected = true, isValidated = true))
|
||||||
|
|
||||||
override fun getRepoForSubId(subId: Int): DemoMobileConnectionRepository {
|
override fun getRepoForSubId(subId: Int): DemoMobileConnectionRepository {
|
||||||
val current = connectionRepoCache[subId]?.repo
|
val current = connectionRepoCache[subId]?.repo
|
||||||
@@ -229,6 +237,9 @@ constructor(
|
|||||||
val connection = getRepoForSubId(subId)
|
val connection = getRepoForSubId(subId)
|
||||||
connectionRepoCache[subId]?.lastMobileState = state
|
connectionRepoCache[subId]?.lastMobileState = state
|
||||||
|
|
||||||
|
// TODO(b/261029387): until we have a command, use the most recent subId
|
||||||
|
defaultDataSubId.value = subId
|
||||||
|
|
||||||
// This is always true here, because we split out disabled states at the data-source level
|
// This is always true here, because we split out disabled states at the data-source level
|
||||||
connection.dataEnabled.value = true
|
connection.dataEnabled.value = true
|
||||||
connection.networkName.value = NetworkNameModel.Derived(state.name)
|
connection.networkName.value = NetworkNameModel.Derived(state.name)
|
||||||
|
|||||||
@@ -35,6 +35,7 @@ import android.telephony.TelephonyCallback
|
|||||||
import android.telephony.TelephonyCallback.ActiveDataSubscriptionIdListener
|
import android.telephony.TelephonyCallback.ActiveDataSubscriptionIdListener
|
||||||
import android.telephony.TelephonyManager
|
import android.telephony.TelephonyManager
|
||||||
import androidx.annotation.VisibleForTesting
|
import androidx.annotation.VisibleForTesting
|
||||||
|
import com.android.internal.telephony.PhoneConstants
|
||||||
import com.android.settingslib.SignalIcon.MobileIconGroup
|
import com.android.settingslib.SignalIcon.MobileIconGroup
|
||||||
import com.android.settingslib.mobile.MobileMappings.Config
|
import com.android.settingslib.mobile.MobileMappings.Config
|
||||||
import com.android.systemui.R
|
import com.android.systemui.R
|
||||||
@@ -52,6 +53,7 @@ import com.android.systemui.statusbar.pipeline.shared.ConnectivityPipelineLogger
|
|||||||
import com.android.systemui.statusbar.pipeline.shared.ConnectivityPipelineLogger.Companion.logInputChange
|
import com.android.systemui.statusbar.pipeline.shared.ConnectivityPipelineLogger.Companion.logInputChange
|
||||||
import com.android.systemui.statusbar.pipeline.wifi.data.model.WifiNetworkModel
|
import com.android.systemui.statusbar.pipeline.wifi.data.model.WifiNetworkModel
|
||||||
import com.android.systemui.statusbar.pipeline.wifi.data.repository.WifiRepository
|
import com.android.systemui.statusbar.pipeline.wifi.data.repository.WifiRepository
|
||||||
|
import com.android.systemui.util.kotlin.pairwiseBy
|
||||||
import com.android.systemui.util.settings.GlobalSettings
|
import com.android.systemui.util.settings.GlobalSettings
|
||||||
import javax.inject.Inject
|
import javax.inject.Inject
|
||||||
import kotlinx.coroutines.CoroutineDispatcher
|
import kotlinx.coroutines.CoroutineDispatcher
|
||||||
@@ -60,9 +62,12 @@ import kotlinx.coroutines.ExperimentalCoroutinesApi
|
|||||||
import kotlinx.coroutines.asExecutor
|
import kotlinx.coroutines.asExecutor
|
||||||
import kotlinx.coroutines.channels.awaitClose
|
import kotlinx.coroutines.channels.awaitClose
|
||||||
import kotlinx.coroutines.flow.Flow
|
import kotlinx.coroutines.flow.Flow
|
||||||
|
import kotlinx.coroutines.flow.MutableSharedFlow
|
||||||
import kotlinx.coroutines.flow.SharingStarted
|
import kotlinx.coroutines.flow.SharingStarted
|
||||||
import kotlinx.coroutines.flow.StateFlow
|
import kotlinx.coroutines.flow.StateFlow
|
||||||
import kotlinx.coroutines.flow.distinctUntilChanged
|
import kotlinx.coroutines.flow.distinctUntilChanged
|
||||||
|
import kotlinx.coroutines.flow.flow
|
||||||
|
import kotlinx.coroutines.flow.flowOn
|
||||||
import kotlinx.coroutines.flow.map
|
import kotlinx.coroutines.flow.map
|
||||||
import kotlinx.coroutines.flow.mapLatest
|
import kotlinx.coroutines.flow.mapLatest
|
||||||
import kotlinx.coroutines.flow.merge
|
import kotlinx.coroutines.flow.merge
|
||||||
@@ -159,10 +164,24 @@ constructor(
|
|||||||
.logInputChange(logger, "onActiveDataSubscriptionIdChanged")
|
.logInputChange(logger, "onActiveDataSubscriptionIdChanged")
|
||||||
.stateIn(scope, started = SharingStarted.WhileSubscribed(), INVALID_SUBSCRIPTION_ID)
|
.stateIn(scope, started = SharingStarted.WhileSubscribed(), INVALID_SUBSCRIPTION_ID)
|
||||||
|
|
||||||
private val defaultDataSubIdChangedEvent =
|
private val defaultDataSubIdChangeEvent: MutableSharedFlow<Unit> =
|
||||||
|
MutableSharedFlow(extraBufferCapacity = 1)
|
||||||
|
|
||||||
|
override val defaultDataSubId: StateFlow<Int> =
|
||||||
broadcastDispatcher
|
broadcastDispatcher
|
||||||
.broadcastFlow(IntentFilter(TelephonyManager.ACTION_DEFAULT_DATA_SUBSCRIPTION_CHANGED))
|
.broadcastFlow(
|
||||||
|
IntentFilter(TelephonyManager.ACTION_DEFAULT_DATA_SUBSCRIPTION_CHANGED)
|
||||||
|
) { intent, _ ->
|
||||||
|
intent.getIntExtra(PhoneConstants.SUBSCRIPTION_KEY, INVALID_SUBSCRIPTION_ID)
|
||||||
|
}
|
||||||
|
.distinctUntilChanged()
|
||||||
.logInputChange(logger, "ACTION_DEFAULT_DATA_SUBSCRIPTION_CHANGED")
|
.logInputChange(logger, "ACTION_DEFAULT_DATA_SUBSCRIPTION_CHANGED")
|
||||||
|
.onEach { defaultDataSubIdChangeEvent.tryEmit(Unit) }
|
||||||
|
.stateIn(
|
||||||
|
scope,
|
||||||
|
SharingStarted.WhileSubscribed(),
|
||||||
|
SubscriptionManager.getDefaultDataSubscriptionId()
|
||||||
|
)
|
||||||
|
|
||||||
private val carrierConfigChangedEvent =
|
private val carrierConfigChangedEvent =
|
||||||
broadcastDispatcher
|
broadcastDispatcher
|
||||||
@@ -170,7 +189,7 @@ constructor(
|
|||||||
.logInputChange(logger, "ACTION_CARRIER_CONFIG_CHANGED")
|
.logInputChange(logger, "ACTION_CARRIER_CONFIG_CHANGED")
|
||||||
|
|
||||||
override val defaultDataSubRatConfig: StateFlow<Config> =
|
override val defaultDataSubRatConfig: StateFlow<Config> =
|
||||||
merge(defaultDataSubIdChangedEvent, carrierConfigChangedEvent)
|
merge(defaultDataSubIdChangeEvent, carrierConfigChangedEvent)
|
||||||
.mapLatest { Config.readConfig(context) }
|
.mapLatest { Config.readConfig(context) }
|
||||||
.distinctUntilChanged()
|
.distinctUntilChanged()
|
||||||
.logInputChange(logger, "defaultDataSubRatConfig")
|
.logInputChange(logger, "defaultDataSubRatConfig")
|
||||||
@@ -258,6 +277,35 @@ constructor(
|
|||||||
.logInputChange(logger, "defaultMobileNetworkConnectivity")
|
.logInputChange(logger, "defaultMobileNetworkConnectivity")
|
||||||
.stateIn(scope, SharingStarted.WhileSubscribed(), MobileConnectivityModel())
|
.stateIn(scope, SharingStarted.WhileSubscribed(), MobileConnectivityModel())
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Flow that tracks the active mobile data subscriptions. Emits `true` whenever the active data
|
||||||
|
* subscription Id changes but the subscription group remains the same. In these cases, we want
|
||||||
|
* to retain the previous subscription's validation status for up to 2s to avoid flickering the
|
||||||
|
* icon.
|
||||||
|
*
|
||||||
|
* TODO(b/265164432): we should probably expose all change events, not just same group
|
||||||
|
*/
|
||||||
|
@SuppressLint("MissingPermission")
|
||||||
|
override val activeSubChangedInGroupEvent =
|
||||||
|
flow {
|
||||||
|
activeMobileDataSubscriptionId.pairwiseBy { prevVal: Int, newVal: Int ->
|
||||||
|
if (!defaultMobileNetworkConnectivity.value.isValidated) {
|
||||||
|
return@pairwiseBy
|
||||||
|
}
|
||||||
|
val prevSub = subscriptionManager.getActiveSubscriptionInfo(prevVal)
|
||||||
|
val nextSub = subscriptionManager.getActiveSubscriptionInfo(newVal)
|
||||||
|
|
||||||
|
if (prevSub == null || nextSub == null) {
|
||||||
|
return@pairwiseBy
|
||||||
|
}
|
||||||
|
|
||||||
|
if (prevSub.groupUuid != null && prevSub.groupUuid == nextSub.groupUuid) {
|
||||||
|
emit(Unit)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
.flowOn(bgDispatcher)
|
||||||
|
|
||||||
private fun isValidSubId(subId: Int): Boolean {
|
private fun isValidSubId(subId: Int): Boolean {
|
||||||
subscriptions.value.forEach {
|
subscriptions.value.forEach {
|
||||||
if (it.subscriptionId == subId) {
|
if (it.subscriptionId == subId) {
|
||||||
|
|||||||
@@ -18,9 +18,11 @@ package com.android.systemui.statusbar.pipeline.mobile.domain.interactor
|
|||||||
|
|
||||||
import android.telephony.CarrierConfigManager
|
import android.telephony.CarrierConfigManager
|
||||||
import com.android.settingslib.SignalIcon.MobileIconGroup
|
import com.android.settingslib.SignalIcon.MobileIconGroup
|
||||||
|
import com.android.settingslib.mobile.TelephonyIcons.NOT_DEFAULT_DATA
|
||||||
import com.android.systemui.dagger.qualifiers.Application
|
import com.android.systemui.dagger.qualifiers.Application
|
||||||
import com.android.systemui.log.table.TableLogBuffer
|
import com.android.systemui.log.table.TableLogBuffer
|
||||||
import com.android.systemui.statusbar.pipeline.mobile.data.model.DataConnectionState.Connected
|
import com.android.systemui.statusbar.pipeline.mobile.data.model.DataConnectionState.Connected
|
||||||
|
import com.android.systemui.statusbar.pipeline.mobile.data.model.MobileConnectivityModel
|
||||||
import com.android.systemui.statusbar.pipeline.mobile.data.model.NetworkNameModel
|
import com.android.systemui.statusbar.pipeline.mobile.data.model.NetworkNameModel
|
||||||
import com.android.systemui.statusbar.pipeline.mobile.data.model.ResolvedNetworkType
|
import com.android.systemui.statusbar.pipeline.mobile.data.model.ResolvedNetworkType
|
||||||
import com.android.systemui.statusbar.pipeline.mobile.data.repository.MobileConnectionRepository
|
import com.android.systemui.statusbar.pipeline.mobile.data.repository.MobileConnectionRepository
|
||||||
@@ -41,12 +43,29 @@ interface MobileIconInteractor {
|
|||||||
/** The current mobile data activity */
|
/** The current mobile data activity */
|
||||||
val activity: Flow<DataActivityModel>
|
val activity: Flow<DataActivityModel>
|
||||||
|
|
||||||
|
/**
|
||||||
|
* This bit is meant to be `true` if and only if the default network capabilities (see
|
||||||
|
* [android.net.ConnectivityManager.registerDefaultNetworkCallback]) result in a network that
|
||||||
|
* has the [android.net.NetworkCapabilities.TRANSPORT_CELLULAR] represented.
|
||||||
|
*
|
||||||
|
* Note that this differs from [isDataConnected], which is tracked by telephony and has to do
|
||||||
|
* with the state of using this mobile connection for data as opposed to just voice. It is
|
||||||
|
* possible for a mobile subscription to be connected but not be in a connected data state, and
|
||||||
|
* thus we wouldn't want to show the network type icon.
|
||||||
|
*/
|
||||||
|
val isConnected: Flow<Boolean>
|
||||||
|
|
||||||
|
/**
|
||||||
|
* True when telephony tells us that the data state is CONNECTED. See
|
||||||
|
* [android.telephony.TelephonyCallback.DataConnectionStateListener] for more details. We
|
||||||
|
* consider this connection to be serving data, and thus want to show a network type icon, when
|
||||||
|
* data is connected. Other data connection states would typically cause us not to show the icon
|
||||||
|
*/
|
||||||
|
val isDataConnected: StateFlow<Boolean>
|
||||||
|
|
||||||
/** Only true if mobile is the default transport but is not validated, otherwise false */
|
/** Only true if mobile is the default transport but is not validated, otherwise false */
|
||||||
val isDefaultConnectionFailed: StateFlow<Boolean>
|
val isDefaultConnectionFailed: StateFlow<Boolean>
|
||||||
|
|
||||||
/** True when telephony tells us that the data state is CONNECTED */
|
|
||||||
val isDataConnected: StateFlow<Boolean>
|
|
||||||
|
|
||||||
/** True if we consider this connection to be in service, i.e. can make calls */
|
/** True if we consider this connection to be in service, i.e. can make calls */
|
||||||
val isInService: StateFlow<Boolean>
|
val isInService: StateFlow<Boolean>
|
||||||
|
|
||||||
@@ -100,8 +119,10 @@ class MobileIconInteractorImpl(
|
|||||||
defaultSubscriptionHasDataEnabled: StateFlow<Boolean>,
|
defaultSubscriptionHasDataEnabled: StateFlow<Boolean>,
|
||||||
override val alwaysShowDataRatIcon: StateFlow<Boolean>,
|
override val alwaysShowDataRatIcon: StateFlow<Boolean>,
|
||||||
override val alwaysUseCdmaLevel: StateFlow<Boolean>,
|
override val alwaysUseCdmaLevel: StateFlow<Boolean>,
|
||||||
|
defaultMobileConnectivity: StateFlow<MobileConnectivityModel>,
|
||||||
defaultMobileIconMapping: StateFlow<Map<String, MobileIconGroup>>,
|
defaultMobileIconMapping: StateFlow<Map<String, MobileIconGroup>>,
|
||||||
defaultMobileIconGroup: StateFlow<MobileIconGroup>,
|
defaultMobileIconGroup: StateFlow<MobileIconGroup>,
|
||||||
|
defaultDataSubId: StateFlow<Int>,
|
||||||
override val isDefaultConnectionFailed: StateFlow<Boolean>,
|
override val isDefaultConnectionFailed: StateFlow<Boolean>,
|
||||||
connectionRepository: MobileConnectionRepository,
|
connectionRepository: MobileConnectionRepository,
|
||||||
) : MobileIconInteractor {
|
) : MobileIconInteractor {
|
||||||
@@ -111,8 +132,19 @@ class MobileIconInteractorImpl(
|
|||||||
|
|
||||||
override val activity = connectionInfo.mapLatest { it.dataActivityDirection }
|
override val activity = connectionInfo.mapLatest { it.dataActivityDirection }
|
||||||
|
|
||||||
|
override val isConnected: Flow<Boolean> = defaultMobileConnectivity.mapLatest { it.isConnected }
|
||||||
|
|
||||||
override val isDataEnabled: StateFlow<Boolean> = connectionRepository.dataEnabled
|
override val isDataEnabled: StateFlow<Boolean> = connectionRepository.dataEnabled
|
||||||
|
|
||||||
|
private val isDefault =
|
||||||
|
defaultDataSubId
|
||||||
|
.mapLatest { connectionRepository.subId == it }
|
||||||
|
.stateIn(
|
||||||
|
scope,
|
||||||
|
SharingStarted.WhileSubscribed(),
|
||||||
|
connectionRepository.subId == defaultDataSubId.value
|
||||||
|
)
|
||||||
|
|
||||||
override val isDefaultDataEnabled = defaultSubscriptionHasDataEnabled
|
override val isDefaultDataEnabled = defaultSubscriptionHasDataEnabled
|
||||||
|
|
||||||
override val networkName =
|
override val networkName =
|
||||||
@@ -137,7 +169,12 @@ class MobileIconInteractorImpl(
|
|||||||
connectionInfo,
|
connectionInfo,
|
||||||
defaultMobileIconMapping,
|
defaultMobileIconMapping,
|
||||||
defaultMobileIconGroup,
|
defaultMobileIconGroup,
|
||||||
) { info, mapping, defaultGroup ->
|
isDefault,
|
||||||
|
) { info, mapping, defaultGroup, isDefault ->
|
||||||
|
if (!isDefault) {
|
||||||
|
return@combine NOT_DEFAULT_DATA
|
||||||
|
}
|
||||||
|
|
||||||
when (info.resolvedNetworkType) {
|
when (info.resolvedNetworkType) {
|
||||||
is ResolvedNetworkType.CarrierMergedNetworkType ->
|
is ResolvedNetworkType.CarrierMergedNetworkType ->
|
||||||
info.resolvedNetworkType.iconGroupOverride
|
info.resolvedNetworkType.iconGroupOverride
|
||||||
|
|||||||
@@ -23,6 +23,7 @@ import com.android.settingslib.SignalIcon.MobileIconGroup
|
|||||||
import com.android.settingslib.mobile.TelephonyIcons
|
import com.android.settingslib.mobile.TelephonyIcons
|
||||||
import com.android.systemui.dagger.SysUISingleton
|
import com.android.systemui.dagger.SysUISingleton
|
||||||
import com.android.systemui.dagger.qualifiers.Application
|
import com.android.systemui.dagger.qualifiers.Application
|
||||||
|
import com.android.systemui.statusbar.pipeline.mobile.data.model.MobileConnectivityModel
|
||||||
import com.android.systemui.statusbar.pipeline.mobile.data.model.SubscriptionModel
|
import com.android.systemui.statusbar.pipeline.mobile.data.model.SubscriptionModel
|
||||||
import com.android.systemui.statusbar.pipeline.mobile.data.repository.MobileConnectionRepository
|
import com.android.systemui.statusbar.pipeline.mobile.data.repository.MobileConnectionRepository
|
||||||
import com.android.systemui.statusbar.pipeline.mobile.data.repository.MobileConnectionsRepository
|
import com.android.systemui.statusbar.pipeline.mobile.data.repository.MobileConnectionsRepository
|
||||||
@@ -31,14 +32,17 @@ import com.android.systemui.util.CarrierConfigTracker
|
|||||||
import javax.inject.Inject
|
import javax.inject.Inject
|
||||||
import kotlinx.coroutines.CoroutineScope
|
import kotlinx.coroutines.CoroutineScope
|
||||||
import kotlinx.coroutines.ExperimentalCoroutinesApi
|
import kotlinx.coroutines.ExperimentalCoroutinesApi
|
||||||
|
import kotlinx.coroutines.delay
|
||||||
import kotlinx.coroutines.flow.Flow
|
import kotlinx.coroutines.flow.Flow
|
||||||
import kotlinx.coroutines.flow.SharingStarted
|
import kotlinx.coroutines.flow.SharingStarted
|
||||||
import kotlinx.coroutines.flow.StateFlow
|
import kotlinx.coroutines.flow.StateFlow
|
||||||
import kotlinx.coroutines.flow.combine
|
import kotlinx.coroutines.flow.combine
|
||||||
|
import kotlinx.coroutines.flow.filter
|
||||||
import kotlinx.coroutines.flow.flatMapLatest
|
import kotlinx.coroutines.flow.flatMapLatest
|
||||||
import kotlinx.coroutines.flow.flowOf
|
import kotlinx.coroutines.flow.flowOf
|
||||||
import kotlinx.coroutines.flow.mapLatest
|
import kotlinx.coroutines.flow.mapLatest
|
||||||
import kotlinx.coroutines.flow.stateIn
|
import kotlinx.coroutines.flow.stateIn
|
||||||
|
import kotlinx.coroutines.flow.transformLatest
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Business layer logic for the set of mobile subscription icons.
|
* Business layer logic for the set of mobile subscription icons.
|
||||||
@@ -62,6 +66,17 @@ interface MobileIconsInteractor {
|
|||||||
/** True if the CDMA level should be preferred over the primary level. */
|
/** True if the CDMA level should be preferred over the primary level. */
|
||||||
val alwaysUseCdmaLevel: StateFlow<Boolean>
|
val alwaysUseCdmaLevel: StateFlow<Boolean>
|
||||||
|
|
||||||
|
/** Tracks the subscriptionId set as the default for data connections */
|
||||||
|
val defaultDataSubId: StateFlow<Int>
|
||||||
|
|
||||||
|
/**
|
||||||
|
* The connectivity of the default mobile network. Note that this can differ from what is
|
||||||
|
* reported from [MobileConnectionsRepository] in some cases. E.g., when the active subscription
|
||||||
|
* changes but the groupUuid remains the same, we keep the old validation information for 2
|
||||||
|
* seconds to avoid icon flickering.
|
||||||
|
*/
|
||||||
|
val defaultMobileNetworkConnectivity: StateFlow<MobileConnectivityModel>
|
||||||
|
|
||||||
/** The icon mapping from network type to [MobileIconGroup] for the default subscription */
|
/** The icon mapping from network type to [MobileIconGroup] for the default subscription */
|
||||||
val defaultMobileIconMapping: StateFlow<Map<String, MobileIconGroup>>
|
val defaultMobileIconMapping: StateFlow<Map<String, MobileIconGroup>>
|
||||||
/** Fallback [MobileIconGroup] in the case where there is no icon in the mapping */
|
/** Fallback [MobileIconGroup] in the case where there is no icon in the mapping */
|
||||||
@@ -154,6 +169,48 @@ constructor(
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
override val defaultDataSubId = mobileConnectionsRepo.defaultDataSubId
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Copied from the old pipeline. We maintain a 2s period of time where we will keep the
|
||||||
|
* validated bit from the old active network (A) while data is changing to the new one (B).
|
||||||
|
*
|
||||||
|
* This condition only applies if
|
||||||
|
* 1. A and B are in the same subscription group (e.c. for CBRS data switching) and
|
||||||
|
* 2. A was validated before the switch
|
||||||
|
*
|
||||||
|
* The goal of this is to minimize the flickering in the UI of the cellular indicator
|
||||||
|
*/
|
||||||
|
private val forcingCellularValidation =
|
||||||
|
mobileConnectionsRepo.activeSubChangedInGroupEvent
|
||||||
|
.filter { mobileConnectionsRepo.defaultMobileNetworkConnectivity.value.isValidated }
|
||||||
|
.transformLatest {
|
||||||
|
emit(true)
|
||||||
|
delay(2000)
|
||||||
|
emit(false)
|
||||||
|
}
|
||||||
|
.stateIn(scope, SharingStarted.WhileSubscribed(), false)
|
||||||
|
|
||||||
|
override val defaultMobileNetworkConnectivity: StateFlow<MobileConnectivityModel> =
|
||||||
|
combine(
|
||||||
|
mobileConnectionsRepo.defaultMobileNetworkConnectivity,
|
||||||
|
forcingCellularValidation,
|
||||||
|
) { networkConnectivity, forceValidation ->
|
||||||
|
return@combine if (forceValidation) {
|
||||||
|
MobileConnectivityModel(
|
||||||
|
isValidated = true,
|
||||||
|
isConnected = networkConnectivity.isConnected
|
||||||
|
)
|
||||||
|
} else {
|
||||||
|
networkConnectivity
|
||||||
|
}
|
||||||
|
}
|
||||||
|
.stateIn(
|
||||||
|
scope,
|
||||||
|
SharingStarted.WhileSubscribed(),
|
||||||
|
mobileConnectionsRepo.defaultMobileNetworkConnectivity.value
|
||||||
|
)
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Mapping from network type to [MobileIconGroup] using the config generated for the default
|
* Mapping from network type to [MobileIconGroup] using the config generated for the default
|
||||||
* subscription Id. This mapping is the same for every subscription.
|
* subscription Id. This mapping is the same for every subscription.
|
||||||
@@ -207,8 +264,10 @@ constructor(
|
|||||||
activeDataConnectionHasDataEnabled,
|
activeDataConnectionHasDataEnabled,
|
||||||
alwaysShowDataRatIcon,
|
alwaysShowDataRatIcon,
|
||||||
alwaysUseCdmaLevel,
|
alwaysUseCdmaLevel,
|
||||||
|
defaultMobileNetworkConnectivity,
|
||||||
defaultMobileIconMapping,
|
defaultMobileIconMapping,
|
||||||
defaultMobileIconGroup,
|
defaultMobileIconGroup,
|
||||||
|
defaultDataSubId,
|
||||||
isDefaultConnectionFailed,
|
isDefaultConnectionFailed,
|
||||||
mobileConnectionsRepo.getRepoForSubId(subId),
|
mobileConnectionsRepo.getRepoForSubId(subId),
|
||||||
)
|
)
|
||||||
|
|||||||
@@ -102,24 +102,29 @@ constructor(
|
|||||||
.stateIn(scope, SharingStarted.WhileSubscribed(), initial)
|
.stateIn(scope, SharingStarted.WhileSubscribed(), initial)
|
||||||
}
|
}
|
||||||
|
|
||||||
override val networkTypeIcon: Flow<Icon?> =
|
private val showNetworkTypeIcon: Flow<Boolean> =
|
||||||
combine(
|
combine(
|
||||||
iconInteractor.networkTypeIconGroup,
|
|
||||||
iconInteractor.isDataConnected,
|
iconInteractor.isDataConnected,
|
||||||
iconInteractor.isDataEnabled,
|
iconInteractor.isDataEnabled,
|
||||||
iconInteractor.isDefaultConnectionFailed,
|
iconInteractor.isDefaultConnectionFailed,
|
||||||
iconInteractor.alwaysShowDataRatIcon,
|
iconInteractor.alwaysShowDataRatIcon,
|
||||||
) { networkTypeIconGroup, dataConnected, dataEnabled, failedConnection, alwaysShow ->
|
iconInteractor.isConnected,
|
||||||
|
) { dataConnected, dataEnabled, failedConnection, alwaysShow, connected ->
|
||||||
|
alwaysShow || (dataConnected && dataEnabled && !failedConnection && connected)
|
||||||
|
}
|
||||||
|
|
||||||
|
override val networkTypeIcon: Flow<Icon?> =
|
||||||
|
combine(
|
||||||
|
iconInteractor.networkTypeIconGroup,
|
||||||
|
showNetworkTypeIcon,
|
||||||
|
) { networkTypeIconGroup, shouldShow ->
|
||||||
val desc =
|
val desc =
|
||||||
if (networkTypeIconGroup.dataContentDescription != 0)
|
if (networkTypeIconGroup.dataContentDescription != 0)
|
||||||
ContentDescription.Resource(networkTypeIconGroup.dataContentDescription)
|
ContentDescription.Resource(networkTypeIconGroup.dataContentDescription)
|
||||||
else null
|
else null
|
||||||
val icon = Icon.Resource(networkTypeIconGroup.dataType, desc)
|
val icon = Icon.Resource(networkTypeIconGroup.dataType, desc)
|
||||||
return@combine when {
|
return@combine when {
|
||||||
alwaysShow -> icon
|
!shouldShow -> null
|
||||||
!dataConnected -> null
|
|
||||||
!dataEnabled -> null
|
|
||||||
failedConnection -> null
|
|
||||||
else -> icon
|
else -> icon
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -26,6 +26,7 @@ import com.android.systemui.log.table.TableLogBuffer
|
|||||||
import com.android.systemui.statusbar.pipeline.mobile.data.model.MobileConnectivityModel
|
import com.android.systemui.statusbar.pipeline.mobile.data.model.MobileConnectivityModel
|
||||||
import com.android.systemui.statusbar.pipeline.mobile.data.model.SubscriptionModel
|
import com.android.systemui.statusbar.pipeline.mobile.data.model.SubscriptionModel
|
||||||
import com.android.systemui.statusbar.pipeline.mobile.util.MobileMappingsProxy
|
import com.android.systemui.statusbar.pipeline.mobile.util.MobileMappingsProxy
|
||||||
|
import kotlinx.coroutines.flow.MutableSharedFlow
|
||||||
import kotlinx.coroutines.flow.MutableStateFlow
|
import kotlinx.coroutines.flow.MutableStateFlow
|
||||||
|
|
||||||
// TODO(b/261632894): remove this in favor of the real impl or DemoMobileConnectionsRepository
|
// TODO(b/261632894): remove this in favor of the real impl or DemoMobileConnectionsRepository
|
||||||
@@ -56,6 +57,10 @@ class FakeMobileConnectionsRepository(
|
|||||||
|
|
||||||
private val _activeMobileDataSubscriptionId = MutableStateFlow(INVALID_SUBSCRIPTION_ID)
|
private val _activeMobileDataSubscriptionId = MutableStateFlow(INVALID_SUBSCRIPTION_ID)
|
||||||
override val activeMobileDataSubscriptionId = _activeMobileDataSubscriptionId
|
override val activeMobileDataSubscriptionId = _activeMobileDataSubscriptionId
|
||||||
|
override val activeSubChangedInGroupEvent: MutableSharedFlow<Unit> = MutableSharedFlow()
|
||||||
|
|
||||||
|
private val _defaultDataSubId = MutableStateFlow(INVALID_SUBSCRIPTION_ID)
|
||||||
|
override val defaultDataSubId = _defaultDataSubId
|
||||||
|
|
||||||
private val _mobileConnectivity = MutableStateFlow(MobileConnectivityModel())
|
private val _mobileConnectivity = MutableStateFlow(MobileConnectivityModel())
|
||||||
override val defaultMobileNetworkConnectivity = _mobileConnectivity
|
override val defaultMobileNetworkConnectivity = _mobileConnectivity
|
||||||
@@ -81,6 +86,10 @@ class FakeMobileConnectionsRepository(
|
|||||||
_subscriptions.value = subs
|
_subscriptions.value = subs
|
||||||
}
|
}
|
||||||
|
|
||||||
|
fun setDefaultDataSubId(id: Int) {
|
||||||
|
_defaultDataSubId.value = id
|
||||||
|
}
|
||||||
|
|
||||||
fun setMobileConnectivity(model: MobileConnectivityModel) {
|
fun setMobileConnectivity(model: MobileConnectivityModel) {
|
||||||
_mobileConnectivity.value = model
|
_mobileConnectivity.value = model
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -89,6 +89,14 @@ class DemoMobileConnectionsRepositoryTest : SysuiTestCase() {
|
|||||||
underTest.startProcessingCommands()
|
underTest.startProcessingCommands()
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
fun `connectivity - defaults to connected and validated`() =
|
||||||
|
testScope.runTest {
|
||||||
|
val connectivity = underTest.defaultMobileNetworkConnectivity.value
|
||||||
|
assertThat(connectivity.isConnected).isTrue()
|
||||||
|
assertThat(connectivity.isValidated).isTrue()
|
||||||
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
fun `network event - create new subscription`() =
|
fun `network event - create new subscription`() =
|
||||||
testScope.runTest {
|
testScope.runTest {
|
||||||
|
|||||||
@@ -478,6 +478,35 @@ class MobileConnectionsRepositoryTest : SysuiTestCase() {
|
|||||||
job.cancel()
|
job.cancel()
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
fun testDefaultDataSubId_updatesOnBroadcast() =
|
||||||
|
runBlocking(IMMEDIATE) {
|
||||||
|
var latest: Int? = null
|
||||||
|
val job = underTest.defaultDataSubId.onEach { latest = it }.launchIn(this)
|
||||||
|
|
||||||
|
fakeBroadcastDispatcher.registeredReceivers.forEach { receiver ->
|
||||||
|
receiver.onReceive(
|
||||||
|
context,
|
||||||
|
Intent(TelephonyManager.ACTION_DEFAULT_DATA_SUBSCRIPTION_CHANGED)
|
||||||
|
.putExtra(PhoneConstants.SUBSCRIPTION_KEY, SUB_2_ID)
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
assertThat(latest).isEqualTo(SUB_2_ID)
|
||||||
|
|
||||||
|
fakeBroadcastDispatcher.registeredReceivers.forEach { receiver ->
|
||||||
|
receiver.onReceive(
|
||||||
|
context,
|
||||||
|
Intent(TelephonyManager.ACTION_DEFAULT_DATA_SUBSCRIPTION_CHANGED)
|
||||||
|
.putExtra(PhoneConstants.SUBSCRIPTION_KEY, SUB_1_ID)
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
assertThat(latest).isEqualTo(SUB_1_ID)
|
||||||
|
|
||||||
|
job.cancel()
|
||||||
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
fun mobileConnectivity_default() {
|
fun mobileConnectivity_default() {
|
||||||
assertThat(underTest.defaultMobileNetworkConnectivity.value)
|
assertThat(underTest.defaultMobileNetworkConnectivity.value)
|
||||||
|
|||||||
@@ -40,6 +40,8 @@ class FakeMobileIconInteractor(
|
|||||||
)
|
)
|
||||||
)
|
)
|
||||||
|
|
||||||
|
override val isConnected = MutableStateFlow(true)
|
||||||
|
|
||||||
private val _iconGroup = MutableStateFlow<SignalIcon.MobileIconGroup>(TelephonyIcons.THREE_G)
|
private val _iconGroup = MutableStateFlow<SignalIcon.MobileIconGroup>(TelephonyIcons.THREE_G)
|
||||||
override val networkTypeIconGroup = _iconGroup
|
override val networkTypeIconGroup = _iconGroup
|
||||||
|
|
||||||
|
|||||||
@@ -23,6 +23,7 @@ import android.telephony.TelephonyManager.NETWORK_TYPE_UMTS
|
|||||||
import com.android.settingslib.SignalIcon.MobileIconGroup
|
import com.android.settingslib.SignalIcon.MobileIconGroup
|
||||||
import com.android.settingslib.mobile.TelephonyIcons
|
import com.android.settingslib.mobile.TelephonyIcons
|
||||||
import com.android.systemui.log.table.TableLogBuffer
|
import com.android.systemui.log.table.TableLogBuffer
|
||||||
|
import com.android.systemui.statusbar.pipeline.mobile.data.model.MobileConnectivityModel
|
||||||
import com.android.systemui.statusbar.pipeline.mobile.data.model.SubscriptionModel
|
import com.android.systemui.statusbar.pipeline.mobile.data.model.SubscriptionModel
|
||||||
import com.android.systemui.statusbar.pipeline.mobile.util.MobileMappingsProxy
|
import com.android.systemui.statusbar.pipeline.mobile.util.MobileMappingsProxy
|
||||||
import kotlinx.coroutines.flow.MutableStateFlow
|
import kotlinx.coroutines.flow.MutableStateFlow
|
||||||
@@ -59,6 +60,9 @@ class FakeMobileIconsInteractor(
|
|||||||
override val alwaysShowDataRatIcon = MutableStateFlow(false)
|
override val alwaysShowDataRatIcon = MutableStateFlow(false)
|
||||||
|
|
||||||
override val alwaysUseCdmaLevel = MutableStateFlow(false)
|
override val alwaysUseCdmaLevel = MutableStateFlow(false)
|
||||||
|
override val defaultDataSubId = MutableStateFlow(DEFAULT_DATA_SUB_ID)
|
||||||
|
|
||||||
|
override val defaultMobileNetworkConnectivity = MutableStateFlow(MobileConnectivityModel())
|
||||||
|
|
||||||
private val _defaultMobileIconMapping = MutableStateFlow(TEST_MAPPING)
|
private val _defaultMobileIconMapping = MutableStateFlow(TEST_MAPPING)
|
||||||
override val defaultMobileIconMapping = _defaultMobileIconMapping
|
override val defaultMobileIconMapping = _defaultMobileIconMapping
|
||||||
@@ -77,6 +81,8 @@ class FakeMobileIconsInteractor(
|
|||||||
companion object {
|
companion object {
|
||||||
val DEFAULT_ICON = TelephonyIcons.G
|
val DEFAULT_ICON = TelephonyIcons.G
|
||||||
|
|
||||||
|
const val DEFAULT_DATA_SUB_ID = 1
|
||||||
|
|
||||||
// Use [MobileMappings] to define some simple definitions
|
// Use [MobileMappings] to define some simple definitions
|
||||||
const val THREE_G = NETWORK_TYPE_GSM
|
const val THREE_G = NETWORK_TYPE_GSM
|
||||||
const val LTE = NETWORK_TYPE_LTE
|
const val LTE = NETWORK_TYPE_LTE
|
||||||
|
|||||||
@@ -61,8 +61,10 @@ class MobileIconInteractorTest : SysuiTestCase() {
|
|||||||
mobileIconsInteractor.activeDataConnectionHasDataEnabled,
|
mobileIconsInteractor.activeDataConnectionHasDataEnabled,
|
||||||
mobileIconsInteractor.alwaysShowDataRatIcon,
|
mobileIconsInteractor.alwaysShowDataRatIcon,
|
||||||
mobileIconsInteractor.alwaysUseCdmaLevel,
|
mobileIconsInteractor.alwaysUseCdmaLevel,
|
||||||
|
mobileIconsInteractor.defaultMobileNetworkConnectivity,
|
||||||
mobileIconsInteractor.defaultMobileIconMapping,
|
mobileIconsInteractor.defaultMobileIconMapping,
|
||||||
mobileIconsInteractor.defaultMobileIconGroup,
|
mobileIconsInteractor.defaultMobileIconGroup,
|
||||||
|
mobileIconsInteractor.defaultDataSubId,
|
||||||
mobileIconsInteractor.isDefaultConnectionFailed,
|
mobileIconsInteractor.isDefaultConnectionFailed,
|
||||||
connectionRepository,
|
connectionRepository,
|
||||||
)
|
)
|
||||||
@@ -288,6 +290,30 @@ class MobileIconInteractorTest : SysuiTestCase() {
|
|||||||
job.cancel()
|
job.cancel()
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
fun `icon group - checks default data`() =
|
||||||
|
runBlocking(IMMEDIATE) {
|
||||||
|
mobileIconsInteractor.defaultDataSubId.value = SUB_1_ID
|
||||||
|
connectionRepository.setConnectionInfo(
|
||||||
|
MobileConnectionModel(
|
||||||
|
resolvedNetworkType = DefaultNetworkType(mobileMappingsProxy.toIconKey(THREE_G))
|
||||||
|
),
|
||||||
|
)
|
||||||
|
|
||||||
|
var latest: MobileIconGroup? = null
|
||||||
|
val job = underTest.networkTypeIconGroup.onEach { latest = it }.launchIn(this)
|
||||||
|
|
||||||
|
assertThat(latest).isEqualTo(TelephonyIcons.THREE_G)
|
||||||
|
|
||||||
|
// Default data sub id changes to something else
|
||||||
|
mobileIconsInteractor.defaultDataSubId.value = 123
|
||||||
|
yield()
|
||||||
|
|
||||||
|
assertThat(latest).isEqualTo(TelephonyIcons.NOT_DEFAULT_DATA)
|
||||||
|
|
||||||
|
job.cancel()
|
||||||
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
fun alwaysShowDataRatIcon_matchesParent() =
|
fun alwaysShowDataRatIcon_matchesParent() =
|
||||||
runBlocking(IMMEDIATE) {
|
runBlocking(IMMEDIATE) {
|
||||||
|
|||||||
@@ -31,11 +31,13 @@ import com.android.systemui.util.CarrierConfigTracker
|
|||||||
import com.android.systemui.util.mockito.whenever
|
import com.android.systemui.util.mockito.whenever
|
||||||
import com.android.systemui.util.time.FakeSystemClock
|
import com.android.systemui.util.time.FakeSystemClock
|
||||||
import com.google.common.truth.Truth.assertThat
|
import com.google.common.truth.Truth.assertThat
|
||||||
import kotlinx.coroutines.CoroutineScope
|
import kotlinx.coroutines.ExperimentalCoroutinesApi
|
||||||
import kotlinx.coroutines.Dispatchers
|
|
||||||
import kotlinx.coroutines.flow.launchIn
|
import kotlinx.coroutines.flow.launchIn
|
||||||
import kotlinx.coroutines.flow.onEach
|
import kotlinx.coroutines.flow.onEach
|
||||||
import kotlinx.coroutines.runBlocking
|
import kotlinx.coroutines.test.TestScope
|
||||||
|
import kotlinx.coroutines.test.UnconfinedTestDispatcher
|
||||||
|
import kotlinx.coroutines.test.advanceTimeBy
|
||||||
|
import kotlinx.coroutines.test.runTest
|
||||||
import kotlinx.coroutines.yield
|
import kotlinx.coroutines.yield
|
||||||
import org.junit.After
|
import org.junit.After
|
||||||
import org.junit.Before
|
import org.junit.Before
|
||||||
@@ -43,13 +45,16 @@ import org.junit.Test
|
|||||||
import org.mockito.Mock
|
import org.mockito.Mock
|
||||||
import org.mockito.MockitoAnnotations
|
import org.mockito.MockitoAnnotations
|
||||||
|
|
||||||
|
@OptIn(ExperimentalCoroutinesApi::class)
|
||||||
@SmallTest
|
@SmallTest
|
||||||
class MobileIconsInteractorTest : SysuiTestCase() {
|
class MobileIconsInteractorTest : SysuiTestCase() {
|
||||||
private lateinit var underTest: MobileIconsInteractor
|
private lateinit var underTest: MobileIconsInteractor
|
||||||
private lateinit var connectionsRepository: FakeMobileConnectionsRepository
|
private lateinit var connectionsRepository: FakeMobileConnectionsRepository
|
||||||
private val userSetupRepository = FakeUserSetupRepository()
|
private val userSetupRepository = FakeUserSetupRepository()
|
||||||
private val mobileMappingsProxy = FakeMobileMappingsProxy()
|
private val mobileMappingsProxy = FakeMobileMappingsProxy()
|
||||||
private val scope = CoroutineScope(IMMEDIATE)
|
|
||||||
|
private val testDispatcher = UnconfinedTestDispatcher()
|
||||||
|
private val testScope = TestScope(testDispatcher)
|
||||||
|
|
||||||
@Mock private lateinit var carrierConfigTracker: CarrierConfigTracker
|
@Mock private lateinit var carrierConfigTracker: CarrierConfigTracker
|
||||||
|
|
||||||
@@ -73,7 +78,7 @@ class MobileIconsInteractorTest : SysuiTestCase() {
|
|||||||
connectionsRepository,
|
connectionsRepository,
|
||||||
carrierConfigTracker,
|
carrierConfigTracker,
|
||||||
userSetupRepository,
|
userSetupRepository,
|
||||||
scope
|
testScope.backgroundScope,
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -81,7 +86,7 @@ class MobileIconsInteractorTest : SysuiTestCase() {
|
|||||||
|
|
||||||
@Test
|
@Test
|
||||||
fun filteredSubscriptions_default() =
|
fun filteredSubscriptions_default() =
|
||||||
runBlocking(IMMEDIATE) {
|
testScope.runTest {
|
||||||
var latest: List<SubscriptionModel>? = null
|
var latest: List<SubscriptionModel>? = null
|
||||||
val job = underTest.filteredSubscriptions.onEach { latest = it }.launchIn(this)
|
val job = underTest.filteredSubscriptions.onEach { latest = it }.launchIn(this)
|
||||||
|
|
||||||
@@ -92,7 +97,7 @@ class MobileIconsInteractorTest : SysuiTestCase() {
|
|||||||
|
|
||||||
@Test
|
@Test
|
||||||
fun filteredSubscriptions_nonOpportunistic_updatesWithMultipleSubs() =
|
fun filteredSubscriptions_nonOpportunistic_updatesWithMultipleSubs() =
|
||||||
runBlocking(IMMEDIATE) {
|
testScope.runTest {
|
||||||
connectionsRepository.setSubscriptions(listOf(SUB_1, SUB_2))
|
connectionsRepository.setSubscriptions(listOf(SUB_1, SUB_2))
|
||||||
|
|
||||||
var latest: List<SubscriptionModel>? = null
|
var latest: List<SubscriptionModel>? = null
|
||||||
@@ -105,7 +110,7 @@ class MobileIconsInteractorTest : SysuiTestCase() {
|
|||||||
|
|
||||||
@Test
|
@Test
|
||||||
fun filteredSubscriptions_bothOpportunistic_configFalse_showsActive_3() =
|
fun filteredSubscriptions_bothOpportunistic_configFalse_showsActive_3() =
|
||||||
runBlocking(IMMEDIATE) {
|
testScope.runTest {
|
||||||
connectionsRepository.setSubscriptions(listOf(SUB_3_OPP, SUB_4_OPP))
|
connectionsRepository.setSubscriptions(listOf(SUB_3_OPP, SUB_4_OPP))
|
||||||
connectionsRepository.setActiveMobileDataSubscriptionId(SUB_3_ID)
|
connectionsRepository.setActiveMobileDataSubscriptionId(SUB_3_ID)
|
||||||
whenever(carrierConfigTracker.alwaysShowPrimarySignalBarInOpportunisticNetworkDefault)
|
whenever(carrierConfigTracker.alwaysShowPrimarySignalBarInOpportunisticNetworkDefault)
|
||||||
@@ -122,7 +127,7 @@ class MobileIconsInteractorTest : SysuiTestCase() {
|
|||||||
|
|
||||||
@Test
|
@Test
|
||||||
fun filteredSubscriptions_bothOpportunistic_configFalse_showsActive_4() =
|
fun filteredSubscriptions_bothOpportunistic_configFalse_showsActive_4() =
|
||||||
runBlocking(IMMEDIATE) {
|
testScope.runTest {
|
||||||
connectionsRepository.setSubscriptions(listOf(SUB_3_OPP, SUB_4_OPP))
|
connectionsRepository.setSubscriptions(listOf(SUB_3_OPP, SUB_4_OPP))
|
||||||
connectionsRepository.setActiveMobileDataSubscriptionId(SUB_4_ID)
|
connectionsRepository.setActiveMobileDataSubscriptionId(SUB_4_ID)
|
||||||
whenever(carrierConfigTracker.alwaysShowPrimarySignalBarInOpportunisticNetworkDefault)
|
whenever(carrierConfigTracker.alwaysShowPrimarySignalBarInOpportunisticNetworkDefault)
|
||||||
@@ -139,7 +144,7 @@ class MobileIconsInteractorTest : SysuiTestCase() {
|
|||||||
|
|
||||||
@Test
|
@Test
|
||||||
fun filteredSubscriptions_oneOpportunistic_configTrue_showsPrimary_active_1() =
|
fun filteredSubscriptions_oneOpportunistic_configTrue_showsPrimary_active_1() =
|
||||||
runBlocking(IMMEDIATE) {
|
testScope.runTest {
|
||||||
connectionsRepository.setSubscriptions(listOf(SUB_1, SUB_3_OPP))
|
connectionsRepository.setSubscriptions(listOf(SUB_1, SUB_3_OPP))
|
||||||
connectionsRepository.setActiveMobileDataSubscriptionId(SUB_1_ID)
|
connectionsRepository.setActiveMobileDataSubscriptionId(SUB_1_ID)
|
||||||
whenever(carrierConfigTracker.alwaysShowPrimarySignalBarInOpportunisticNetworkDefault)
|
whenever(carrierConfigTracker.alwaysShowPrimarySignalBarInOpportunisticNetworkDefault)
|
||||||
@@ -157,7 +162,7 @@ class MobileIconsInteractorTest : SysuiTestCase() {
|
|||||||
|
|
||||||
@Test
|
@Test
|
||||||
fun filteredSubscriptions_oneOpportunistic_configTrue_showsPrimary_nonActive_1() =
|
fun filteredSubscriptions_oneOpportunistic_configTrue_showsPrimary_nonActive_1() =
|
||||||
runBlocking(IMMEDIATE) {
|
testScope.runTest {
|
||||||
connectionsRepository.setSubscriptions(listOf(SUB_1, SUB_3_OPP))
|
connectionsRepository.setSubscriptions(listOf(SUB_1, SUB_3_OPP))
|
||||||
connectionsRepository.setActiveMobileDataSubscriptionId(SUB_3_ID)
|
connectionsRepository.setActiveMobileDataSubscriptionId(SUB_3_ID)
|
||||||
whenever(carrierConfigTracker.alwaysShowPrimarySignalBarInOpportunisticNetworkDefault)
|
whenever(carrierConfigTracker.alwaysShowPrimarySignalBarInOpportunisticNetworkDefault)
|
||||||
@@ -175,7 +180,7 @@ class MobileIconsInteractorTest : SysuiTestCase() {
|
|||||||
|
|
||||||
@Test
|
@Test
|
||||||
fun activeDataConnection_turnedOn() =
|
fun activeDataConnection_turnedOn() =
|
||||||
runBlocking(IMMEDIATE) {
|
testScope.runTest {
|
||||||
CONNECTION_1.setDataEnabled(true)
|
CONNECTION_1.setDataEnabled(true)
|
||||||
var latest: Boolean? = null
|
var latest: Boolean? = null
|
||||||
val job =
|
val job =
|
||||||
@@ -188,7 +193,7 @@ class MobileIconsInteractorTest : SysuiTestCase() {
|
|||||||
|
|
||||||
@Test
|
@Test
|
||||||
fun activeDataConnection_turnedOff() =
|
fun activeDataConnection_turnedOff() =
|
||||||
runBlocking(IMMEDIATE) {
|
testScope.runTest {
|
||||||
CONNECTION_1.setDataEnabled(true)
|
CONNECTION_1.setDataEnabled(true)
|
||||||
var latest: Boolean? = null
|
var latest: Boolean? = null
|
||||||
val job =
|
val job =
|
||||||
@@ -204,7 +209,7 @@ class MobileIconsInteractorTest : SysuiTestCase() {
|
|||||||
|
|
||||||
@Test
|
@Test
|
||||||
fun activeDataConnection_invalidSubId() =
|
fun activeDataConnection_invalidSubId() =
|
||||||
runBlocking(IMMEDIATE) {
|
testScope.runTest {
|
||||||
var latest: Boolean? = null
|
var latest: Boolean? = null
|
||||||
val job =
|
val job =
|
||||||
underTest.activeDataConnectionHasDataEnabled.onEach { latest = it }.launchIn(this)
|
underTest.activeDataConnectionHasDataEnabled.onEach { latest = it }.launchIn(this)
|
||||||
@@ -220,7 +225,7 @@ class MobileIconsInteractorTest : SysuiTestCase() {
|
|||||||
|
|
||||||
@Test
|
@Test
|
||||||
fun failedConnection_connected_validated_notFailed() =
|
fun failedConnection_connected_validated_notFailed() =
|
||||||
runBlocking(IMMEDIATE) {
|
testScope.runTest {
|
||||||
var latest: Boolean? = null
|
var latest: Boolean? = null
|
||||||
val job = underTest.isDefaultConnectionFailed.onEach { latest = it }.launchIn(this)
|
val job = underTest.isDefaultConnectionFailed.onEach { latest = it }.launchIn(this)
|
||||||
connectionsRepository.setMobileConnectivity(MobileConnectivityModel(true, true))
|
connectionsRepository.setMobileConnectivity(MobileConnectivityModel(true, true))
|
||||||
@@ -233,7 +238,7 @@ class MobileIconsInteractorTest : SysuiTestCase() {
|
|||||||
|
|
||||||
@Test
|
@Test
|
||||||
fun failedConnection_notConnected_notValidated_notFailed() =
|
fun failedConnection_notConnected_notValidated_notFailed() =
|
||||||
runBlocking(IMMEDIATE) {
|
testScope.runTest {
|
||||||
var latest: Boolean? = null
|
var latest: Boolean? = null
|
||||||
val job = underTest.isDefaultConnectionFailed.onEach { latest = it }.launchIn(this)
|
val job = underTest.isDefaultConnectionFailed.onEach { latest = it }.launchIn(this)
|
||||||
|
|
||||||
@@ -247,7 +252,7 @@ class MobileIconsInteractorTest : SysuiTestCase() {
|
|||||||
|
|
||||||
@Test
|
@Test
|
||||||
fun failedConnection_connected_notValidated_failed() =
|
fun failedConnection_connected_notValidated_failed() =
|
||||||
runBlocking(IMMEDIATE) {
|
testScope.runTest {
|
||||||
var latest: Boolean? = null
|
var latest: Boolean? = null
|
||||||
val job = underTest.isDefaultConnectionFailed.onEach { latest = it }.launchIn(this)
|
val job = underTest.isDefaultConnectionFailed.onEach { latest = it }.launchIn(this)
|
||||||
|
|
||||||
@@ -261,7 +266,7 @@ class MobileIconsInteractorTest : SysuiTestCase() {
|
|||||||
|
|
||||||
@Test
|
@Test
|
||||||
fun alwaysShowDataRatIcon_configHasTrue() =
|
fun alwaysShowDataRatIcon_configHasTrue() =
|
||||||
runBlocking(IMMEDIATE) {
|
testScope.runTest {
|
||||||
var latest: Boolean? = null
|
var latest: Boolean? = null
|
||||||
val job = underTest.alwaysShowDataRatIcon.onEach { latest = it }.launchIn(this)
|
val job = underTest.alwaysShowDataRatIcon.onEach { latest = it }.launchIn(this)
|
||||||
|
|
||||||
@@ -277,7 +282,7 @@ class MobileIconsInteractorTest : SysuiTestCase() {
|
|||||||
|
|
||||||
@Test
|
@Test
|
||||||
fun alwaysShowDataRatIcon_configHasFalse() =
|
fun alwaysShowDataRatIcon_configHasFalse() =
|
||||||
runBlocking(IMMEDIATE) {
|
testScope.runTest {
|
||||||
var latest: Boolean? = null
|
var latest: Boolean? = null
|
||||||
val job = underTest.alwaysShowDataRatIcon.onEach { latest = it }.launchIn(this)
|
val job = underTest.alwaysShowDataRatIcon.onEach { latest = it }.launchIn(this)
|
||||||
|
|
||||||
@@ -293,7 +298,7 @@ class MobileIconsInteractorTest : SysuiTestCase() {
|
|||||||
|
|
||||||
@Test
|
@Test
|
||||||
fun alwaysUseCdmaLevel_configHasTrue() =
|
fun alwaysUseCdmaLevel_configHasTrue() =
|
||||||
runBlocking(IMMEDIATE) {
|
testScope.runTest {
|
||||||
var latest: Boolean? = null
|
var latest: Boolean? = null
|
||||||
val job = underTest.alwaysUseCdmaLevel.onEach { latest = it }.launchIn(this)
|
val job = underTest.alwaysUseCdmaLevel.onEach { latest = it }.launchIn(this)
|
||||||
|
|
||||||
@@ -309,7 +314,7 @@ class MobileIconsInteractorTest : SysuiTestCase() {
|
|||||||
|
|
||||||
@Test
|
@Test
|
||||||
fun alwaysUseCdmaLevel_configHasFalse() =
|
fun alwaysUseCdmaLevel_configHasFalse() =
|
||||||
runBlocking(IMMEDIATE) {
|
testScope.runTest {
|
||||||
var latest: Boolean? = null
|
var latest: Boolean? = null
|
||||||
val job = underTest.alwaysUseCdmaLevel.onEach { latest = it }.launchIn(this)
|
val job = underTest.alwaysUseCdmaLevel.onEach { latest = it }.launchIn(this)
|
||||||
|
|
||||||
@@ -323,8 +328,286 @@ class MobileIconsInteractorTest : SysuiTestCase() {
|
|||||||
job.cancel()
|
job.cancel()
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
fun `default mobile connectivity - uses repo value`() =
|
||||||
|
testScope.runTest {
|
||||||
|
var latest: MobileConnectivityModel? = null
|
||||||
|
val job =
|
||||||
|
underTest.defaultMobileNetworkConnectivity.onEach { latest = it }.launchIn(this)
|
||||||
|
|
||||||
|
var expected = MobileConnectivityModel(isConnected = true, isValidated = true)
|
||||||
|
connectionsRepository.setMobileConnectivity(expected)
|
||||||
|
assertThat(latest).isEqualTo(expected)
|
||||||
|
|
||||||
|
expected = MobileConnectivityModel(isConnected = false, isValidated = true)
|
||||||
|
connectionsRepository.setMobileConnectivity(expected)
|
||||||
|
assertThat(latest).isEqualTo(expected)
|
||||||
|
|
||||||
|
expected = MobileConnectivityModel(isConnected = true, isValidated = false)
|
||||||
|
connectionsRepository.setMobileConnectivity(expected)
|
||||||
|
assertThat(latest).isEqualTo(expected)
|
||||||
|
|
||||||
|
expected = MobileConnectivityModel(isConnected = false, isValidated = false)
|
||||||
|
connectionsRepository.setMobileConnectivity(expected)
|
||||||
|
assertThat(latest).isEqualTo(expected)
|
||||||
|
|
||||||
|
job.cancel()
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
fun `data switch - in same group - validated matches previous value`() =
|
||||||
|
testScope.runTest {
|
||||||
|
var latest: MobileConnectivityModel? = null
|
||||||
|
val job =
|
||||||
|
underTest.defaultMobileNetworkConnectivity.onEach { latest = it }.launchIn(this)
|
||||||
|
|
||||||
|
connectionsRepository.setMobileConnectivity(
|
||||||
|
MobileConnectivityModel(
|
||||||
|
isConnected = true,
|
||||||
|
isValidated = true,
|
||||||
|
)
|
||||||
|
)
|
||||||
|
// Trigger a data change in the same subscription group
|
||||||
|
connectionsRepository.activeSubChangedInGroupEvent.emit(Unit)
|
||||||
|
connectionsRepository.setMobileConnectivity(
|
||||||
|
MobileConnectivityModel(
|
||||||
|
isConnected = false,
|
||||||
|
isValidated = false,
|
||||||
|
)
|
||||||
|
)
|
||||||
|
|
||||||
|
assertThat(latest)
|
||||||
|
.isEqualTo(
|
||||||
|
MobileConnectivityModel(
|
||||||
|
isConnected = false,
|
||||||
|
isValidated = true,
|
||||||
|
)
|
||||||
|
)
|
||||||
|
|
||||||
|
job.cancel()
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
fun `data switch - in same group - validated matches previous value - expires after 2s`() =
|
||||||
|
testScope.runTest {
|
||||||
|
var latest: MobileConnectivityModel? = null
|
||||||
|
val job =
|
||||||
|
underTest.defaultMobileNetworkConnectivity.onEach { latest = it }.launchIn(this)
|
||||||
|
|
||||||
|
connectionsRepository.setMobileConnectivity(
|
||||||
|
MobileConnectivityModel(
|
||||||
|
isConnected = true,
|
||||||
|
isValidated = true,
|
||||||
|
)
|
||||||
|
)
|
||||||
|
// Trigger a data change in the same subscription group
|
||||||
|
connectionsRepository.activeSubChangedInGroupEvent.emit(Unit)
|
||||||
|
connectionsRepository.setMobileConnectivity(
|
||||||
|
MobileConnectivityModel(
|
||||||
|
isConnected = false,
|
||||||
|
isValidated = false,
|
||||||
|
)
|
||||||
|
)
|
||||||
|
// After 1s, the force validation bit is still present
|
||||||
|
advanceTimeBy(1000)
|
||||||
|
assertThat(latest)
|
||||||
|
.isEqualTo(
|
||||||
|
MobileConnectivityModel(
|
||||||
|
isConnected = false,
|
||||||
|
isValidated = true,
|
||||||
|
)
|
||||||
|
)
|
||||||
|
|
||||||
|
// After 2s, the force validation expires
|
||||||
|
advanceTimeBy(1001)
|
||||||
|
|
||||||
|
assertThat(latest)
|
||||||
|
.isEqualTo(
|
||||||
|
MobileConnectivityModel(
|
||||||
|
isConnected = false,
|
||||||
|
isValidated = false,
|
||||||
|
)
|
||||||
|
)
|
||||||
|
|
||||||
|
job.cancel()
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
fun `data switch - in same group - not validated - uses new value immediately`() =
|
||||||
|
testScope.runTest {
|
||||||
|
var latest: MobileConnectivityModel? = null
|
||||||
|
val job =
|
||||||
|
underTest.defaultMobileNetworkConnectivity.onEach { latest = it }.launchIn(this)
|
||||||
|
|
||||||
|
connectionsRepository.setMobileConnectivity(
|
||||||
|
MobileConnectivityModel(
|
||||||
|
isConnected = true,
|
||||||
|
isValidated = false,
|
||||||
|
)
|
||||||
|
)
|
||||||
|
connectionsRepository.activeSubChangedInGroupEvent.emit(Unit)
|
||||||
|
connectionsRepository.setMobileConnectivity(
|
||||||
|
MobileConnectivityModel(
|
||||||
|
isConnected = false,
|
||||||
|
isValidated = false,
|
||||||
|
)
|
||||||
|
)
|
||||||
|
|
||||||
|
assertThat(latest)
|
||||||
|
.isEqualTo(
|
||||||
|
MobileConnectivityModel(
|
||||||
|
isConnected = false,
|
||||||
|
isValidated = false,
|
||||||
|
)
|
||||||
|
)
|
||||||
|
|
||||||
|
job.cancel()
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
fun `data switch - lose validation - then switch happens - clears forced bit`() =
|
||||||
|
testScope.runTest {
|
||||||
|
var latest: MobileConnectivityModel? = null
|
||||||
|
val job =
|
||||||
|
underTest.defaultMobileNetworkConnectivity.onEach { latest = it }.launchIn(this)
|
||||||
|
|
||||||
|
// GIVEN the network starts validated
|
||||||
|
connectionsRepository.setMobileConnectivity(
|
||||||
|
MobileConnectivityModel(
|
||||||
|
isConnected = true,
|
||||||
|
isValidated = true,
|
||||||
|
)
|
||||||
|
)
|
||||||
|
|
||||||
|
// WHEN a data change happens in the same group
|
||||||
|
connectionsRepository.activeSubChangedInGroupEvent.emit(Unit)
|
||||||
|
|
||||||
|
// WHEN the validation bit is lost
|
||||||
|
connectionsRepository.setMobileConnectivity(
|
||||||
|
MobileConnectivityModel(
|
||||||
|
isConnected = false,
|
||||||
|
isValidated = false,
|
||||||
|
)
|
||||||
|
)
|
||||||
|
|
||||||
|
// WHEN another data change happens in the same group
|
||||||
|
connectionsRepository.activeSubChangedInGroupEvent.emit(Unit)
|
||||||
|
|
||||||
|
// THEN the forced validation bit is still removed after 2s
|
||||||
|
assertThat(latest)
|
||||||
|
.isEqualTo(
|
||||||
|
MobileConnectivityModel(
|
||||||
|
isConnected = false,
|
||||||
|
isValidated = true,
|
||||||
|
)
|
||||||
|
)
|
||||||
|
|
||||||
|
advanceTimeBy(1000)
|
||||||
|
|
||||||
|
assertThat(latest)
|
||||||
|
.isEqualTo(
|
||||||
|
MobileConnectivityModel(
|
||||||
|
isConnected = false,
|
||||||
|
isValidated = true,
|
||||||
|
)
|
||||||
|
)
|
||||||
|
|
||||||
|
advanceTimeBy(1001)
|
||||||
|
|
||||||
|
assertThat(latest)
|
||||||
|
.isEqualTo(
|
||||||
|
MobileConnectivityModel(
|
||||||
|
isConnected = false,
|
||||||
|
isValidated = false,
|
||||||
|
)
|
||||||
|
)
|
||||||
|
|
||||||
|
job.cancel()
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
fun `data switch - while already forcing validation - resets clock`() =
|
||||||
|
testScope.runTest {
|
||||||
|
var latest: MobileConnectivityModel? = null
|
||||||
|
val job =
|
||||||
|
underTest.defaultMobileNetworkConnectivity.onEach { latest = it }.launchIn(this)
|
||||||
|
connectionsRepository.setMobileConnectivity(
|
||||||
|
MobileConnectivityModel(
|
||||||
|
isConnected = true,
|
||||||
|
isValidated = true,
|
||||||
|
)
|
||||||
|
)
|
||||||
|
|
||||||
|
connectionsRepository.activeSubChangedInGroupEvent.emit(Unit)
|
||||||
|
|
||||||
|
advanceTimeBy(1000)
|
||||||
|
|
||||||
|
// WHEN another change in same group event happens
|
||||||
|
connectionsRepository.activeSubChangedInGroupEvent.emit(Unit)
|
||||||
|
connectionsRepository.setMobileConnectivity(
|
||||||
|
MobileConnectivityModel(
|
||||||
|
isConnected = false,
|
||||||
|
isValidated = false,
|
||||||
|
)
|
||||||
|
)
|
||||||
|
|
||||||
|
// THEN the forced validation remains for exactly 2 more seconds from now
|
||||||
|
|
||||||
|
// 1.500s from second event
|
||||||
|
advanceTimeBy(1500)
|
||||||
|
assertThat(latest)
|
||||||
|
.isEqualTo(
|
||||||
|
MobileConnectivityModel(
|
||||||
|
isConnected = false,
|
||||||
|
isValidated = true,
|
||||||
|
)
|
||||||
|
)
|
||||||
|
|
||||||
|
// 2.001s from the second event
|
||||||
|
advanceTimeBy(501)
|
||||||
|
assertThat(latest)
|
||||||
|
.isEqualTo(
|
||||||
|
MobileConnectivityModel(
|
||||||
|
isConnected = false,
|
||||||
|
isValidated = false,
|
||||||
|
)
|
||||||
|
)
|
||||||
|
|
||||||
|
job.cancel()
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
fun `data switch - not in same group - uses new values`() =
|
||||||
|
testScope.runTest {
|
||||||
|
var latest: MobileConnectivityModel? = null
|
||||||
|
val job =
|
||||||
|
underTest.defaultMobileNetworkConnectivity.onEach { latest = it }.launchIn(this)
|
||||||
|
|
||||||
|
connectionsRepository.setMobileConnectivity(
|
||||||
|
MobileConnectivityModel(
|
||||||
|
isConnected = true,
|
||||||
|
isValidated = true,
|
||||||
|
)
|
||||||
|
)
|
||||||
|
connectionsRepository.setMobileConnectivity(
|
||||||
|
MobileConnectivityModel(
|
||||||
|
isConnected = false,
|
||||||
|
isValidated = false,
|
||||||
|
)
|
||||||
|
)
|
||||||
|
|
||||||
|
assertThat(latest)
|
||||||
|
.isEqualTo(
|
||||||
|
MobileConnectivityModel(
|
||||||
|
isConnected = false,
|
||||||
|
isValidated = false,
|
||||||
|
)
|
||||||
|
)
|
||||||
|
|
||||||
|
job.cancel()
|
||||||
|
}
|
||||||
|
|
||||||
companion object {
|
companion object {
|
||||||
private val IMMEDIATE = Dispatchers.Main.immediate
|
|
||||||
private val tableLogBuffer =
|
private val tableLogBuffer =
|
||||||
TableLogBuffer(8, "MobileIconsInteractorTest", FakeSystemClock())
|
TableLogBuffer(8, "MobileIconsInteractorTest", FakeSystemClock())
|
||||||
|
|
||||||
|
|||||||
@@ -273,6 +273,41 @@ class MobileIconViewModelTest : SysuiTestCase() {
|
|||||||
job.cancel()
|
job.cancel()
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
fun `network type - alwaysShow - shown when not connected`() =
|
||||||
|
testScope.runTest {
|
||||||
|
interactor.setIconGroup(THREE_G)
|
||||||
|
interactor.isConnected.value = false
|
||||||
|
interactor.alwaysShowDataRatIcon.value = true
|
||||||
|
|
||||||
|
var latest: Icon? = null
|
||||||
|
val job = underTest.networkTypeIcon.onEach { latest = it }.launchIn(this)
|
||||||
|
|
||||||
|
val expected =
|
||||||
|
Icon.Resource(
|
||||||
|
THREE_G.dataType,
|
||||||
|
ContentDescription.Resource(THREE_G.dataContentDescription)
|
||||||
|
)
|
||||||
|
assertThat(latest).isEqualTo(expected)
|
||||||
|
|
||||||
|
job.cancel()
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
fun `network type - not shown when not connected`() =
|
||||||
|
testScope.runTest {
|
||||||
|
interactor.setIconGroup(THREE_G)
|
||||||
|
interactor.isDataConnected.value = true
|
||||||
|
interactor.isConnected.value = false
|
||||||
|
|
||||||
|
var latest: Icon? = null
|
||||||
|
val job = underTest.networkTypeIcon.onEach { latest = it }.launchIn(this)
|
||||||
|
|
||||||
|
assertThat(latest).isNull()
|
||||||
|
|
||||||
|
job.cancel()
|
||||||
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
fun roaming() =
|
fun roaming() =
|
||||||
testScope.runTest {
|
testScope.runTest {
|
||||||
|
|||||||
Reference in New Issue
Block a user