Merge changes I1c9ade7e,I06365d19,I0945a29d,I8e798197,Ib3a4f14b, ... into tm-qpr-dev am: 74daba5ad9
Original change: https://googleplex-android-review.googlesource.com/c/platform/frameworks/base/+/20932924 Change-Id: Ibbafab8376e7c2bf43681c4148ccae673382c9e8 Signed-off-by: Automerger Merge Worker <android-build-automerger-merge-worker@system.gserviceaccount.com>
This commit is contained in:
@@ -18,6 +18,7 @@ package com.android.systemui.statusbar.pipeline.mobile.data.repository
|
||||
|
||||
import android.provider.Settings
|
||||
import android.telephony.CarrierConfigManager
|
||||
import android.telephony.SubscriptionManager
|
||||
import com.android.settingslib.SignalIcon.MobileIconGroup
|
||||
import com.android.settingslib.mobile.MobileMappings
|
||||
import com.android.settingslib.mobile.MobileMappings.Config
|
||||
@@ -37,6 +38,15 @@ interface MobileConnectionsRepository {
|
||||
/** Observable for the subscriptionId of the current mobile data connection */
|
||||
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 */
|
||||
val defaultMobileNetworkConnectivity: StateFlow<MobileConnectivityModel>
|
||||
|
||||
|
||||
@@ -124,6 +124,9 @@ constructor(
|
||||
realRepository.activeMobileDataSubscriptionId.value
|
||||
)
|
||||
|
||||
override val activeSubChangedInGroupEvent: Flow<Unit> =
|
||||
activeRepo.flatMapLatest { it.activeSubChangedInGroupEvent }
|
||||
|
||||
override val defaultDataSubRatConfig: StateFlow<MobileMappings.Config> =
|
||||
activeRepo
|
||||
.flatMapLatest { it.defaultDataSubRatConfig }
|
||||
@@ -139,6 +142,11 @@ constructor(
|
||||
override val defaultMobileIconGroup: Flow<SignalIcon.MobileIconGroup> =
|
||||
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> =
|
||||
activeRepo
|
||||
.flatMapLatest { it.defaultMobileNetworkConnectivity }
|
||||
|
||||
@@ -48,6 +48,7 @@ import javax.inject.Inject
|
||||
import kotlinx.coroutines.CoroutineScope
|
||||
import kotlinx.coroutines.ExperimentalCoroutinesApi
|
||||
import kotlinx.coroutines.Job
|
||||
import kotlinx.coroutines.flow.Flow
|
||||
import kotlinx.coroutines.flow.MutableSharedFlow
|
||||
import kotlinx.coroutines.flow.MutableStateFlow
|
||||
import kotlinx.coroutines.flow.SharingStarted
|
||||
@@ -120,6 +121,9 @@ constructor(
|
||||
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 */
|
||||
override val defaultDataSubRatConfig =
|
||||
MutableStateFlow(MobileMappings.Config.readConfig(context))
|
||||
@@ -148,8 +152,12 @@ constructor(
|
||||
|
||||
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
|
||||
override val defaultMobileNetworkConnectivity = MutableStateFlow(MobileConnectivityModel())
|
||||
override val defaultMobileNetworkConnectivity =
|
||||
MutableStateFlow(MobileConnectivityModel(isConnected = true, isValidated = true))
|
||||
|
||||
override fun getRepoForSubId(subId: Int): DemoMobileConnectionRepository {
|
||||
val current = connectionRepoCache[subId]?.repo
|
||||
@@ -229,6 +237,9 @@ constructor(
|
||||
val connection = getRepoForSubId(subId)
|
||||
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
|
||||
connection.dataEnabled.value = true
|
||||
connection.networkName.value = NetworkNameModel.Derived(state.name)
|
||||
|
||||
@@ -35,6 +35,7 @@ import android.telephony.TelephonyCallback
|
||||
import android.telephony.TelephonyCallback.ActiveDataSubscriptionIdListener
|
||||
import android.telephony.TelephonyManager
|
||||
import androidx.annotation.VisibleForTesting
|
||||
import com.android.internal.telephony.PhoneConstants
|
||||
import com.android.settingslib.SignalIcon.MobileIconGroup
|
||||
import com.android.settingslib.mobile.MobileMappings.Config
|
||||
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.wifi.data.model.WifiNetworkModel
|
||||
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 javax.inject.Inject
|
||||
import kotlinx.coroutines.CoroutineDispatcher
|
||||
@@ -60,9 +62,12 @@ import kotlinx.coroutines.ExperimentalCoroutinesApi
|
||||
import kotlinx.coroutines.asExecutor
|
||||
import kotlinx.coroutines.channels.awaitClose
|
||||
import kotlinx.coroutines.flow.Flow
|
||||
import kotlinx.coroutines.flow.MutableSharedFlow
|
||||
import kotlinx.coroutines.flow.SharingStarted
|
||||
import kotlinx.coroutines.flow.StateFlow
|
||||
import kotlinx.coroutines.flow.distinctUntilChanged
|
||||
import kotlinx.coroutines.flow.flow
|
||||
import kotlinx.coroutines.flow.flowOn
|
||||
import kotlinx.coroutines.flow.map
|
||||
import kotlinx.coroutines.flow.mapLatest
|
||||
import kotlinx.coroutines.flow.merge
|
||||
@@ -159,10 +164,24 @@ constructor(
|
||||
.logInputChange(logger, "onActiveDataSubscriptionIdChanged")
|
||||
.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
|
||||
.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")
|
||||
.onEach { defaultDataSubIdChangeEvent.tryEmit(Unit) }
|
||||
.stateIn(
|
||||
scope,
|
||||
SharingStarted.WhileSubscribed(),
|
||||
SubscriptionManager.getDefaultDataSubscriptionId()
|
||||
)
|
||||
|
||||
private val carrierConfigChangedEvent =
|
||||
broadcastDispatcher
|
||||
@@ -170,7 +189,7 @@ constructor(
|
||||
.logInputChange(logger, "ACTION_CARRIER_CONFIG_CHANGED")
|
||||
|
||||
override val defaultDataSubRatConfig: StateFlow<Config> =
|
||||
merge(defaultDataSubIdChangedEvent, carrierConfigChangedEvent)
|
||||
merge(defaultDataSubIdChangeEvent, carrierConfigChangedEvent)
|
||||
.mapLatest { Config.readConfig(context) }
|
||||
.distinctUntilChanged()
|
||||
.logInputChange(logger, "defaultDataSubRatConfig")
|
||||
@@ -258,6 +277,35 @@ constructor(
|
||||
.logInputChange(logger, "defaultMobileNetworkConnectivity")
|
||||
.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 {
|
||||
subscriptions.value.forEach {
|
||||
if (it.subscriptionId == subId) {
|
||||
|
||||
@@ -18,9 +18,11 @@ package com.android.systemui.statusbar.pipeline.mobile.domain.interactor
|
||||
|
||||
import android.telephony.CarrierConfigManager
|
||||
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.log.table.TableLogBuffer
|
||||
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.ResolvedNetworkType
|
||||
import com.android.systemui.statusbar.pipeline.mobile.data.repository.MobileConnectionRepository
|
||||
@@ -41,12 +43,29 @@ interface MobileIconInteractor {
|
||||
/** The current mobile data activity */
|
||||
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 */
|
||||
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 */
|
||||
val isInService: StateFlow<Boolean>
|
||||
|
||||
@@ -100,8 +119,10 @@ class MobileIconInteractorImpl(
|
||||
defaultSubscriptionHasDataEnabled: StateFlow<Boolean>,
|
||||
override val alwaysShowDataRatIcon: StateFlow<Boolean>,
|
||||
override val alwaysUseCdmaLevel: StateFlow<Boolean>,
|
||||
defaultMobileConnectivity: StateFlow<MobileConnectivityModel>,
|
||||
defaultMobileIconMapping: StateFlow<Map<String, MobileIconGroup>>,
|
||||
defaultMobileIconGroup: StateFlow<MobileIconGroup>,
|
||||
defaultDataSubId: StateFlow<Int>,
|
||||
override val isDefaultConnectionFailed: StateFlow<Boolean>,
|
||||
connectionRepository: MobileConnectionRepository,
|
||||
) : MobileIconInteractor {
|
||||
@@ -111,8 +132,19 @@ class MobileIconInteractorImpl(
|
||||
|
||||
override val activity = connectionInfo.mapLatest { it.dataActivityDirection }
|
||||
|
||||
override val isConnected: Flow<Boolean> = defaultMobileConnectivity.mapLatest { it.isConnected }
|
||||
|
||||
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 networkName =
|
||||
@@ -137,7 +169,12 @@ class MobileIconInteractorImpl(
|
||||
connectionInfo,
|
||||
defaultMobileIconMapping,
|
||||
defaultMobileIconGroup,
|
||||
) { info, mapping, defaultGroup ->
|
||||
isDefault,
|
||||
) { info, mapping, defaultGroup, isDefault ->
|
||||
if (!isDefault) {
|
||||
return@combine NOT_DEFAULT_DATA
|
||||
}
|
||||
|
||||
when (info.resolvedNetworkType) {
|
||||
is ResolvedNetworkType.CarrierMergedNetworkType ->
|
||||
info.resolvedNetworkType.iconGroupOverride
|
||||
|
||||
@@ -23,6 +23,7 @@ import com.android.settingslib.SignalIcon.MobileIconGroup
|
||||
import com.android.settingslib.mobile.TelephonyIcons
|
||||
import com.android.systemui.dagger.SysUISingleton
|
||||
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.repository.MobileConnectionRepository
|
||||
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 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.filter
|
||||
import kotlinx.coroutines.flow.flatMapLatest
|
||||
import kotlinx.coroutines.flow.flowOf
|
||||
import kotlinx.coroutines.flow.mapLatest
|
||||
import kotlinx.coroutines.flow.stateIn
|
||||
import kotlinx.coroutines.flow.transformLatest
|
||||
|
||||
/**
|
||||
* 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. */
|
||||
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 */
|
||||
val defaultMobileIconMapping: StateFlow<Map<String, MobileIconGroup>>
|
||||
/** 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
|
||||
* subscription Id. This mapping is the same for every subscription.
|
||||
@@ -207,8 +264,10 @@ constructor(
|
||||
activeDataConnectionHasDataEnabled,
|
||||
alwaysShowDataRatIcon,
|
||||
alwaysUseCdmaLevel,
|
||||
defaultMobileNetworkConnectivity,
|
||||
defaultMobileIconMapping,
|
||||
defaultMobileIconGroup,
|
||||
defaultDataSubId,
|
||||
isDefaultConnectionFailed,
|
||||
mobileConnectionsRepo.getRepoForSubId(subId),
|
||||
)
|
||||
|
||||
@@ -102,24 +102,29 @@ constructor(
|
||||
.stateIn(scope, SharingStarted.WhileSubscribed(), initial)
|
||||
}
|
||||
|
||||
private val showNetworkTypeIcon: Flow<Boolean> =
|
||||
combine(
|
||||
iconInteractor.isDataConnected,
|
||||
iconInteractor.isDataEnabled,
|
||||
iconInteractor.isDefaultConnectionFailed,
|
||||
iconInteractor.alwaysShowDataRatIcon,
|
||||
iconInteractor.isConnected,
|
||||
) { dataConnected, dataEnabled, failedConnection, alwaysShow, connected ->
|
||||
alwaysShow || (dataConnected && dataEnabled && !failedConnection && connected)
|
||||
}
|
||||
|
||||
override val networkTypeIcon: Flow<Icon?> =
|
||||
combine(
|
||||
iconInteractor.networkTypeIconGroup,
|
||||
iconInteractor.isDataConnected,
|
||||
iconInteractor.isDataEnabled,
|
||||
iconInteractor.isDefaultConnectionFailed,
|
||||
iconInteractor.alwaysShowDataRatIcon,
|
||||
) { networkTypeIconGroup, dataConnected, dataEnabled, failedConnection, alwaysShow ->
|
||||
showNetworkTypeIcon,
|
||||
) { networkTypeIconGroup, shouldShow ->
|
||||
val desc =
|
||||
if (networkTypeIconGroup.dataContentDescription != 0)
|
||||
ContentDescription.Resource(networkTypeIconGroup.dataContentDescription)
|
||||
else null
|
||||
val icon = Icon.Resource(networkTypeIconGroup.dataType, desc)
|
||||
return@combine when {
|
||||
alwaysShow -> icon
|
||||
!dataConnected -> null
|
||||
!dataEnabled -> null
|
||||
failedConnection -> null
|
||||
!shouldShow -> null
|
||||
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.SubscriptionModel
|
||||
import com.android.systemui.statusbar.pipeline.mobile.util.MobileMappingsProxy
|
||||
import kotlinx.coroutines.flow.MutableSharedFlow
|
||||
import kotlinx.coroutines.flow.MutableStateFlow
|
||||
|
||||
// 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)
|
||||
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())
|
||||
override val defaultMobileNetworkConnectivity = _mobileConnectivity
|
||||
@@ -81,6 +86,10 @@ class FakeMobileConnectionsRepository(
|
||||
_subscriptions.value = subs
|
||||
}
|
||||
|
||||
fun setDefaultDataSubId(id: Int) {
|
||||
_defaultDataSubId.value = id
|
||||
}
|
||||
|
||||
fun setMobileConnectivity(model: MobileConnectivityModel) {
|
||||
_mobileConnectivity.value = model
|
||||
}
|
||||
|
||||
@@ -89,6 +89,14 @@ class DemoMobileConnectionsRepositoryTest : SysuiTestCase() {
|
||||
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
|
||||
fun `network event - create new subscription`() =
|
||||
testScope.runTest {
|
||||
|
||||
@@ -478,6 +478,35 @@ class MobileConnectionsRepositoryTest : SysuiTestCase() {
|
||||
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
|
||||
fun mobileConnectivity_default() {
|
||||
assertThat(underTest.defaultMobileNetworkConnectivity.value)
|
||||
|
||||
@@ -40,6 +40,8 @@ class FakeMobileIconInteractor(
|
||||
)
|
||||
)
|
||||
|
||||
override val isConnected = MutableStateFlow(true)
|
||||
|
||||
private val _iconGroup = MutableStateFlow<SignalIcon.MobileIconGroup>(TelephonyIcons.THREE_G)
|
||||
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.mobile.TelephonyIcons
|
||||
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.util.MobileMappingsProxy
|
||||
import kotlinx.coroutines.flow.MutableStateFlow
|
||||
@@ -59,6 +60,9 @@ class FakeMobileIconsInteractor(
|
||||
override val alwaysShowDataRatIcon = 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)
|
||||
override val defaultMobileIconMapping = _defaultMobileIconMapping
|
||||
@@ -77,6 +81,8 @@ class FakeMobileIconsInteractor(
|
||||
companion object {
|
||||
val DEFAULT_ICON = TelephonyIcons.G
|
||||
|
||||
const val DEFAULT_DATA_SUB_ID = 1
|
||||
|
||||
// Use [MobileMappings] to define some simple definitions
|
||||
const val THREE_G = NETWORK_TYPE_GSM
|
||||
const val LTE = NETWORK_TYPE_LTE
|
||||
|
||||
@@ -61,8 +61,10 @@ class MobileIconInteractorTest : SysuiTestCase() {
|
||||
mobileIconsInteractor.activeDataConnectionHasDataEnabled,
|
||||
mobileIconsInteractor.alwaysShowDataRatIcon,
|
||||
mobileIconsInteractor.alwaysUseCdmaLevel,
|
||||
mobileIconsInteractor.defaultMobileNetworkConnectivity,
|
||||
mobileIconsInteractor.defaultMobileIconMapping,
|
||||
mobileIconsInteractor.defaultMobileIconGroup,
|
||||
mobileIconsInteractor.defaultDataSubId,
|
||||
mobileIconsInteractor.isDefaultConnectionFailed,
|
||||
connectionRepository,
|
||||
)
|
||||
@@ -288,6 +290,30 @@ class MobileIconInteractorTest : SysuiTestCase() {
|
||||
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
|
||||
fun alwaysShowDataRatIcon_matchesParent() =
|
||||
runBlocking(IMMEDIATE) {
|
||||
|
||||
@@ -31,11 +31,13 @@ import com.android.systemui.util.CarrierConfigTracker
|
||||
import com.android.systemui.util.mockito.whenever
|
||||
import com.android.systemui.util.time.FakeSystemClock
|
||||
import com.google.common.truth.Truth.assertThat
|
||||
import kotlinx.coroutines.CoroutineScope
|
||||
import kotlinx.coroutines.Dispatchers
|
||||
import kotlinx.coroutines.ExperimentalCoroutinesApi
|
||||
import kotlinx.coroutines.flow.launchIn
|
||||
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 org.junit.After
|
||||
import org.junit.Before
|
||||
@@ -43,13 +45,16 @@ import org.junit.Test
|
||||
import org.mockito.Mock
|
||||
import org.mockito.MockitoAnnotations
|
||||
|
||||
@OptIn(ExperimentalCoroutinesApi::class)
|
||||
@SmallTest
|
||||
class MobileIconsInteractorTest : SysuiTestCase() {
|
||||
private lateinit var underTest: MobileIconsInteractor
|
||||
private lateinit var connectionsRepository: FakeMobileConnectionsRepository
|
||||
private val userSetupRepository = FakeUserSetupRepository()
|
||||
private val mobileMappingsProxy = FakeMobileMappingsProxy()
|
||||
private val scope = CoroutineScope(IMMEDIATE)
|
||||
|
||||
private val testDispatcher = UnconfinedTestDispatcher()
|
||||
private val testScope = TestScope(testDispatcher)
|
||||
|
||||
@Mock private lateinit var carrierConfigTracker: CarrierConfigTracker
|
||||
|
||||
@@ -73,7 +78,7 @@ class MobileIconsInteractorTest : SysuiTestCase() {
|
||||
connectionsRepository,
|
||||
carrierConfigTracker,
|
||||
userSetupRepository,
|
||||
scope
|
||||
testScope.backgroundScope,
|
||||
)
|
||||
}
|
||||
|
||||
@@ -81,7 +86,7 @@ class MobileIconsInteractorTest : SysuiTestCase() {
|
||||
|
||||
@Test
|
||||
fun filteredSubscriptions_default() =
|
||||
runBlocking(IMMEDIATE) {
|
||||
testScope.runTest {
|
||||
var latest: List<SubscriptionModel>? = null
|
||||
val job = underTest.filteredSubscriptions.onEach { latest = it }.launchIn(this)
|
||||
|
||||
@@ -92,7 +97,7 @@ class MobileIconsInteractorTest : SysuiTestCase() {
|
||||
|
||||
@Test
|
||||
fun filteredSubscriptions_nonOpportunistic_updatesWithMultipleSubs() =
|
||||
runBlocking(IMMEDIATE) {
|
||||
testScope.runTest {
|
||||
connectionsRepository.setSubscriptions(listOf(SUB_1, SUB_2))
|
||||
|
||||
var latest: List<SubscriptionModel>? = null
|
||||
@@ -105,7 +110,7 @@ class MobileIconsInteractorTest : SysuiTestCase() {
|
||||
|
||||
@Test
|
||||
fun filteredSubscriptions_bothOpportunistic_configFalse_showsActive_3() =
|
||||
runBlocking(IMMEDIATE) {
|
||||
testScope.runTest {
|
||||
connectionsRepository.setSubscriptions(listOf(SUB_3_OPP, SUB_4_OPP))
|
||||
connectionsRepository.setActiveMobileDataSubscriptionId(SUB_3_ID)
|
||||
whenever(carrierConfigTracker.alwaysShowPrimarySignalBarInOpportunisticNetworkDefault)
|
||||
@@ -122,7 +127,7 @@ class MobileIconsInteractorTest : SysuiTestCase() {
|
||||
|
||||
@Test
|
||||
fun filteredSubscriptions_bothOpportunistic_configFalse_showsActive_4() =
|
||||
runBlocking(IMMEDIATE) {
|
||||
testScope.runTest {
|
||||
connectionsRepository.setSubscriptions(listOf(SUB_3_OPP, SUB_4_OPP))
|
||||
connectionsRepository.setActiveMobileDataSubscriptionId(SUB_4_ID)
|
||||
whenever(carrierConfigTracker.alwaysShowPrimarySignalBarInOpportunisticNetworkDefault)
|
||||
@@ -139,7 +144,7 @@ class MobileIconsInteractorTest : SysuiTestCase() {
|
||||
|
||||
@Test
|
||||
fun filteredSubscriptions_oneOpportunistic_configTrue_showsPrimary_active_1() =
|
||||
runBlocking(IMMEDIATE) {
|
||||
testScope.runTest {
|
||||
connectionsRepository.setSubscriptions(listOf(SUB_1, SUB_3_OPP))
|
||||
connectionsRepository.setActiveMobileDataSubscriptionId(SUB_1_ID)
|
||||
whenever(carrierConfigTracker.alwaysShowPrimarySignalBarInOpportunisticNetworkDefault)
|
||||
@@ -157,7 +162,7 @@ class MobileIconsInteractorTest : SysuiTestCase() {
|
||||
|
||||
@Test
|
||||
fun filteredSubscriptions_oneOpportunistic_configTrue_showsPrimary_nonActive_1() =
|
||||
runBlocking(IMMEDIATE) {
|
||||
testScope.runTest {
|
||||
connectionsRepository.setSubscriptions(listOf(SUB_1, SUB_3_OPP))
|
||||
connectionsRepository.setActiveMobileDataSubscriptionId(SUB_3_ID)
|
||||
whenever(carrierConfigTracker.alwaysShowPrimarySignalBarInOpportunisticNetworkDefault)
|
||||
@@ -175,7 +180,7 @@ class MobileIconsInteractorTest : SysuiTestCase() {
|
||||
|
||||
@Test
|
||||
fun activeDataConnection_turnedOn() =
|
||||
runBlocking(IMMEDIATE) {
|
||||
testScope.runTest {
|
||||
CONNECTION_1.setDataEnabled(true)
|
||||
var latest: Boolean? = null
|
||||
val job =
|
||||
@@ -188,7 +193,7 @@ class MobileIconsInteractorTest : SysuiTestCase() {
|
||||
|
||||
@Test
|
||||
fun activeDataConnection_turnedOff() =
|
||||
runBlocking(IMMEDIATE) {
|
||||
testScope.runTest {
|
||||
CONNECTION_1.setDataEnabled(true)
|
||||
var latest: Boolean? = null
|
||||
val job =
|
||||
@@ -204,7 +209,7 @@ class MobileIconsInteractorTest : SysuiTestCase() {
|
||||
|
||||
@Test
|
||||
fun activeDataConnection_invalidSubId() =
|
||||
runBlocking(IMMEDIATE) {
|
||||
testScope.runTest {
|
||||
var latest: Boolean? = null
|
||||
val job =
|
||||
underTest.activeDataConnectionHasDataEnabled.onEach { latest = it }.launchIn(this)
|
||||
@@ -220,7 +225,7 @@ class MobileIconsInteractorTest : SysuiTestCase() {
|
||||
|
||||
@Test
|
||||
fun failedConnection_connected_validated_notFailed() =
|
||||
runBlocking(IMMEDIATE) {
|
||||
testScope.runTest {
|
||||
var latest: Boolean? = null
|
||||
val job = underTest.isDefaultConnectionFailed.onEach { latest = it }.launchIn(this)
|
||||
connectionsRepository.setMobileConnectivity(MobileConnectivityModel(true, true))
|
||||
@@ -233,7 +238,7 @@ class MobileIconsInteractorTest : SysuiTestCase() {
|
||||
|
||||
@Test
|
||||
fun failedConnection_notConnected_notValidated_notFailed() =
|
||||
runBlocking(IMMEDIATE) {
|
||||
testScope.runTest {
|
||||
var latest: Boolean? = null
|
||||
val job = underTest.isDefaultConnectionFailed.onEach { latest = it }.launchIn(this)
|
||||
|
||||
@@ -247,7 +252,7 @@ class MobileIconsInteractorTest : SysuiTestCase() {
|
||||
|
||||
@Test
|
||||
fun failedConnection_connected_notValidated_failed() =
|
||||
runBlocking(IMMEDIATE) {
|
||||
testScope.runTest {
|
||||
var latest: Boolean? = null
|
||||
val job = underTest.isDefaultConnectionFailed.onEach { latest = it }.launchIn(this)
|
||||
|
||||
@@ -261,7 +266,7 @@ class MobileIconsInteractorTest : SysuiTestCase() {
|
||||
|
||||
@Test
|
||||
fun alwaysShowDataRatIcon_configHasTrue() =
|
||||
runBlocking(IMMEDIATE) {
|
||||
testScope.runTest {
|
||||
var latest: Boolean? = null
|
||||
val job = underTest.alwaysShowDataRatIcon.onEach { latest = it }.launchIn(this)
|
||||
|
||||
@@ -277,7 +282,7 @@ class MobileIconsInteractorTest : SysuiTestCase() {
|
||||
|
||||
@Test
|
||||
fun alwaysShowDataRatIcon_configHasFalse() =
|
||||
runBlocking(IMMEDIATE) {
|
||||
testScope.runTest {
|
||||
var latest: Boolean? = null
|
||||
val job = underTest.alwaysShowDataRatIcon.onEach { latest = it }.launchIn(this)
|
||||
|
||||
@@ -293,7 +298,7 @@ class MobileIconsInteractorTest : SysuiTestCase() {
|
||||
|
||||
@Test
|
||||
fun alwaysUseCdmaLevel_configHasTrue() =
|
||||
runBlocking(IMMEDIATE) {
|
||||
testScope.runTest {
|
||||
var latest: Boolean? = null
|
||||
val job = underTest.alwaysUseCdmaLevel.onEach { latest = it }.launchIn(this)
|
||||
|
||||
@@ -309,7 +314,7 @@ class MobileIconsInteractorTest : SysuiTestCase() {
|
||||
|
||||
@Test
|
||||
fun alwaysUseCdmaLevel_configHasFalse() =
|
||||
runBlocking(IMMEDIATE) {
|
||||
testScope.runTest {
|
||||
var latest: Boolean? = null
|
||||
val job = underTest.alwaysUseCdmaLevel.onEach { latest = it }.launchIn(this)
|
||||
|
||||
@@ -323,8 +328,286 @@ class MobileIconsInteractorTest : SysuiTestCase() {
|
||||
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 {
|
||||
private val IMMEDIATE = Dispatchers.Main.immediate
|
||||
private val tableLogBuffer =
|
||||
TableLogBuffer(8, "MobileIconsInteractorTest", FakeSystemClock())
|
||||
|
||||
|
||||
@@ -273,6 +273,41 @@ class MobileIconViewModelTest : SysuiTestCase() {
|
||||
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
|
||||
fun roaming() =
|
||||
testScope.runTest {
|
||||
|
||||
Reference in New Issue
Block a user