Merge changes I04fa90bf,Id413d57d into tm-qpr-dev

* changes:
  [Sb refactor] Support NetworkName in new mobile pipeline
  [Sb refactor] show data activity in Mobile view
This commit is contained in:
Evan Laird
2022-12-21 15:11:36 +00:00
committed by Android (Google) Code Review
23 changed files with 576 additions and 33 deletions

View File

@@ -17,7 +17,6 @@
package com.android.systemui.statusbar.pipeline.mobile.data.model
import android.annotation.IntRange
import android.telephony.Annotation.DataActivityType
import android.telephony.CellSignalStrength
import android.telephony.TelephonyCallback.CarrierNetworkListener
import android.telephony.TelephonyCallback.DataActivityListener
@@ -28,6 +27,7 @@ import android.telephony.TelephonyCallback.SignalStrengthsListener
import android.telephony.TelephonyDisplayInfo
import android.telephony.TelephonyManager
import com.android.systemui.statusbar.pipeline.mobile.data.model.DataConnectionState.Disconnected
import com.android.systemui.statusbar.pipeline.shared.data.model.DataActivityModel
/**
* Data class containing all of the relevant information for a particular line of service, known as
@@ -39,29 +39,42 @@ import com.android.systemui.statusbar.pipeline.mobile.data.model.DataConnectionS
* threading complex system objects through the pipeline.
*/
data class MobileConnectionModel(
/** From [ServiceStateListener.onServiceStateChanged] */
/** Fields below are from [ServiceStateListener.onServiceStateChanged] */
val isEmergencyOnly: Boolean = false,
val isRoaming: Boolean = false,
/**
* See [android.telephony.ServiceState.getOperatorAlphaShort], this value is defined as the
* current registered operator name in short alphanumeric format. In some cases this name might
* be preferred over other methods of calculating the network name
*/
val operatorAlphaShort: String? = null,
/** From [SignalStrengthsListener.onSignalStrengthsChanged] */
/** Fields below from [SignalStrengthsListener.onSignalStrengthsChanged] */
val isGsm: Boolean = false,
@IntRange(from = 0, to = 4)
val cdmaLevel: Int = CellSignalStrength.SIGNAL_STRENGTH_NONE_OR_UNKNOWN,
@IntRange(from = 0, to = 4)
val primaryLevel: Int = CellSignalStrength.SIGNAL_STRENGTH_NONE_OR_UNKNOWN,
/** Mapped from [DataConnectionStateListener.onDataConnectionStateChanged] */
/** Fields below from [DataConnectionStateListener.onDataConnectionStateChanged] */
val dataConnectionState: DataConnectionState = Disconnected,
/** From [DataActivityListener.onDataActivity]. See [TelephonyManager] for the values */
@DataActivityType val dataActivityDirection: Int? = null,
/**
* Fields below from [DataActivityListener.onDataActivity]. See [TelephonyManager] for the
* values
*/
val dataActivityDirection: DataActivityModel =
DataActivityModel(
hasActivityIn = false,
hasActivityOut = false,
),
/** From [CarrierNetworkListener.onCarrierNetworkChange] */
/** Fields below from [CarrierNetworkListener.onCarrierNetworkChange] */
val carrierNetworkChangeActive: Boolean = false,
/** Fields below from [DisplayInfoListener.onDisplayInfoChanged]. */
/**
* From [DisplayInfoListener.onDisplayInfoChanged].
*
* [resolvedNetworkType] is the [TelephonyDisplayInfo.getOverrideNetworkType] if it exists or
* [TelephonyDisplayInfo.getNetworkType]. This is used to look up the proper network type icon
*/

View File

@@ -0,0 +1,62 @@
/*
* Copyright (C) 2022 The Android Open Source Project
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package com.android.systemui.statusbar.pipeline.mobile.data.model
import android.content.Intent
import android.telephony.TelephonyManager.EXTRA_DATA_SPN
import android.telephony.TelephonyManager.EXTRA_PLMN
import android.telephony.TelephonyManager.EXTRA_SHOW_PLMN
import android.telephony.TelephonyManager.EXTRA_SHOW_SPN
/**
* Encapsulates the data needed to show a network name for a mobile network. The data is parsed from
* the intent sent by [android.telephony.TelephonyManager.ACTION_SERVICE_PROVIDERS_UPDATED].
*/
sealed interface NetworkNameModel {
val name: String
/** The default name is read from [com.android.internal.R.string.lockscreen_carrier_default] */
data class Default(override val name: String) : NetworkNameModel
/**
* This name has been derived from telephony intents. see
* [android.telephony.TelephonyManager.ACTION_SERVICE_PROVIDERS_UPDATED]
*/
data class Derived(override val name: String) : NetworkNameModel
}
fun Intent.toNetworkNameModel(separator: String): NetworkNameModel? {
val showSpn = getBooleanExtra(EXTRA_SHOW_SPN, false)
val spn = getStringExtra(EXTRA_DATA_SPN)
val showPlmn = getBooleanExtra(EXTRA_SHOW_PLMN, false)
val plmn = getStringExtra(EXTRA_PLMN)
val str = StringBuilder()
val strData = StringBuilder()
if (showPlmn && plmn != null) {
str.append(plmn)
strData.append(plmn)
}
if (showSpn && spn != null) {
if (str.isNotEmpty()) {
str.append(separator)
}
str.append(spn)
}
return if (str.isNotEmpty()) NetworkNameModel.Derived(str.toString()) else null
}

View File

@@ -21,6 +21,7 @@ import android.telephony.SubscriptionManager
import android.telephony.TelephonyCallback
import android.telephony.TelephonyManager
import com.android.systemui.statusbar.pipeline.mobile.data.model.MobileConnectionModel
import com.android.systemui.statusbar.pipeline.mobile.data.model.NetworkNameModel
import kotlinx.coroutines.flow.Flow
import kotlinx.coroutines.flow.StateFlow
@@ -58,4 +59,7 @@ interface MobileConnectionRepository {
* True if the Enhanced Roaming Indicator (ERI) display number is not [TelephonyManager.ERI_OFF]
*/
val cdmaRoaming: StateFlow<Boolean>
/** The service provider name for this network connection, or the default name */
val networkName: StateFlow<NetworkNameModel>
}

View File

@@ -18,6 +18,7 @@ package com.android.systemui.statusbar.pipeline.mobile.data.repository.demo
import android.content.Context
import android.telephony.SubscriptionManager.INVALID_SUBSCRIPTION_ID
import android.telephony.TelephonyManager.DATA_ACTIVITY_NONE
import android.util.Log
import com.android.settingslib.SignalIcon
import com.android.settingslib.mobile.MobileMappings
@@ -26,6 +27,7 @@ import com.android.systemui.dagger.qualifiers.Application
import com.android.systemui.statusbar.pipeline.mobile.data.model.DataConnectionState
import com.android.systemui.statusbar.pipeline.mobile.data.model.MobileConnectionModel
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.model.ResolvedNetworkType.DefaultNetworkType
import com.android.systemui.statusbar.pipeline.mobile.data.model.SubscriptionModel
@@ -34,6 +36,7 @@ import com.android.systemui.statusbar.pipeline.mobile.data.repository.MobileConn
import com.android.systemui.statusbar.pipeline.mobile.data.repository.demo.model.FakeNetworkEventModel
import com.android.systemui.statusbar.pipeline.mobile.data.repository.demo.model.FakeNetworkEventModel.Mobile
import com.android.systemui.statusbar.pipeline.mobile.data.repository.demo.model.FakeNetworkEventModel.MobileDisabled
import com.android.systemui.statusbar.pipeline.shared.data.model.toMobileDataActivityModel
import javax.inject.Inject
import kotlinx.coroutines.CoroutineScope
import kotlinx.coroutines.ExperimentalCoroutinesApi
@@ -185,6 +188,7 @@ constructor(
// This is always true here, because we split out disabled states at the data-source level
connection.dataEnabled.value = true
connection.isDefaultDataSubscription.value = state.dataType != null
connection.networkName.value = NetworkNameModel.Derived(state.name)
connection.cdmaRoaming.value = state.roaming
connection.connectionInfo.value = state.toMobileConnectionModel()
@@ -236,7 +240,7 @@ constructor(
primaryLevel = level ?: 0,
dataConnectionState =
DataConnectionState.Connected, // TODO(b/261029387): not yet supported
dataActivityDirection = activity,
dataActivityDirection = (activity ?: DATA_ACTIVITY_NONE).toMobileDataActivityModel(),
carrierNetworkChangeActive = carrierNetworkChange,
resolvedNetworkType = dataType.toResolvedNetworkType()
)
@@ -264,4 +268,6 @@ class DemoMobileConnectionRepository(override val subId: Int) : MobileConnection
override val isDefaultDataSubscription = MutableStateFlow(true)
override val cdmaRoaming = MutableStateFlow(false)
override val networkName = MutableStateFlow(NetworkNameModel.Derived("demo network"))
}

View File

@@ -99,6 +99,7 @@ constructor(
val activity = getString("activity")?.toActivity()
val carrierNetworkChange = getString("carriernetworkchange") == "show"
val roaming = getString("roam") == "show"
val name = getString("networkname") ?: "demo mode"
return Mobile(
level = level,
@@ -109,6 +110,7 @@ constructor(
activity = activity,
carrierNetworkChange = carrierNetworkChange,
roaming = roaming,
name = name,
)
}
}

View File

@@ -35,6 +35,7 @@ sealed interface FakeNetworkEventModel {
@DataActivityType val activity: Int?,
val carrierNetworkChange: Boolean,
val roaming: Boolean,
val name: String,
) : FakeNetworkEventModel
data class MobileDisabled(

View File

@@ -17,30 +17,37 @@
package com.android.systemui.statusbar.pipeline.mobile.data.repository.prod
import android.content.Context
import android.content.IntentFilter
import android.database.ContentObserver
import android.provider.Settings.Global
import android.telephony.CellSignalStrength
import android.telephony.CellSignalStrengthCdma
import android.telephony.ServiceState
import android.telephony.SignalStrength
import android.telephony.SubscriptionManager.INVALID_SUBSCRIPTION_ID
import android.telephony.TelephonyCallback
import android.telephony.TelephonyDisplayInfo
import android.telephony.TelephonyDisplayInfo.OVERRIDE_NETWORK_TYPE_NONE
import android.telephony.TelephonyManager
import android.telephony.TelephonyManager.ERI_OFF
import android.telephony.TelephonyManager.EXTRA_SUBSCRIPTION_ID
import android.telephony.TelephonyManager.NETWORK_TYPE_UNKNOWN
import com.android.systemui.broadcast.BroadcastDispatcher
import com.android.systemui.common.coroutine.ConflatedCallbackFlow.conflatedCallbackFlow
import com.android.systemui.dagger.qualifiers.Application
import com.android.systemui.dagger.qualifiers.Background
import com.android.systemui.statusbar.pipeline.mobile.data.model.MobileConnectionModel
import com.android.systemui.statusbar.pipeline.mobile.data.model.NetworkNameModel
import com.android.systemui.statusbar.pipeline.mobile.data.model.ResolvedNetworkType.DefaultNetworkType
import com.android.systemui.statusbar.pipeline.mobile.data.model.ResolvedNetworkType.OverrideNetworkType
import com.android.systemui.statusbar.pipeline.mobile.data.model.ResolvedNetworkType.UnknownNetworkType
import com.android.systemui.statusbar.pipeline.mobile.data.model.toDataConnectionType
import com.android.systemui.statusbar.pipeline.mobile.data.model.toNetworkNameModel
import com.android.systemui.statusbar.pipeline.mobile.data.repository.MobileConnectionRepository
import com.android.systemui.statusbar.pipeline.mobile.util.MobileMappingsProxy
import com.android.systemui.statusbar.pipeline.shared.ConnectivityPipelineLogger
import com.android.systemui.statusbar.pipeline.shared.ConnectivityPipelineLogger.Companion.logOutputChange
import com.android.systemui.statusbar.pipeline.shared.data.model.toMobileDataActivityModel
import com.android.systemui.util.settings.GlobalSettings
import javax.inject.Inject
import kotlinx.coroutines.CoroutineDispatcher
@@ -62,8 +69,11 @@ import kotlinx.coroutines.flow.stateIn
class MobileConnectionRepositoryImpl(
private val context: Context,
override val subId: Int,
defaultNetworkName: NetworkNameModel,
networkNameSeparator: String,
private val telephonyManager: TelephonyManager,
private val globalSettings: GlobalSettings,
broadcastDispatcher: BroadcastDispatcher,
defaultDataSubId: StateFlow<Int>,
globalMobileDataSettingChangedEvent: Flow<Unit>,
mobileMappingsProxy: MobileMappingsProxy,
@@ -100,6 +110,7 @@ class MobileConnectionRepositoryImpl(
state.copy(
isEmergencyOnly = serviceState.isEmergencyOnly,
isRoaming = serviceState.roaming,
operatorAlphaShort = serviceState.operatorAlphaShort,
)
trySend(state)
}
@@ -137,7 +148,10 @@ class MobileConnectionRepositoryImpl(
}
override fun onDataActivity(direction: Int) {
state = state.copy(dataActivityDirection = direction)
state =
state.copy(
dataActivityDirection = direction.toMobileDataActivityModel()
)
trySend(state)
}
@@ -218,6 +232,19 @@ class MobileConnectionRepositoryImpl(
.mapLatest { telephonyManager.cdmaEnhancedRoamingIndicatorDisplayNumber != ERI_OFF }
.stateIn(scope, SharingStarted.WhileSubscribed(), false)
override val networkName: StateFlow<NetworkNameModel> =
broadcastDispatcher
.broadcastFlow(IntentFilter(TelephonyManager.ACTION_SERVICE_PROVIDERS_UPDATED)) {
intent,
_ ->
if (intent.getIntExtra(EXTRA_SUBSCRIPTION_ID, INVALID_SUBSCRIPTION_ID) != subId) {
defaultNetworkName
} else {
intent.toNetworkNameModel(networkNameSeparator) ?: defaultNetworkName
}
}
.stateIn(scope, SharingStarted.WhileSubscribed(), defaultNetworkName)
override val dataEnabled: StateFlow<Boolean> =
telephonyPollingEvent
.mapLatest { dataConnectionAllowed() }
@@ -233,6 +260,7 @@ class MobileConnectionRepositoryImpl(
class Factory
@Inject
constructor(
private val broadcastDispatcher: BroadcastDispatcher,
private val context: Context,
private val telephonyManager: TelephonyManager,
private val logger: ConnectivityPipelineLogger,
@@ -243,14 +271,19 @@ class MobileConnectionRepositoryImpl(
) {
fun build(
subId: Int,
defaultNetworkName: NetworkNameModel,
networkNameSeparator: String,
defaultDataSubId: StateFlow<Int>,
globalMobileDataSettingChangedEvent: Flow<Unit>,
): MobileConnectionRepository {
return MobileConnectionRepositoryImpl(
context,
subId,
defaultNetworkName,
networkNameSeparator,
telephonyManager.createForSubscriptionId(subId),
globalSettings,
broadcastDispatcher,
defaultDataSubId,
globalMobileDataSettingChangedEvent,
mobileMappingsProxy,

View File

@@ -38,12 +38,14 @@ 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
import com.android.systemui.broadcast.BroadcastDispatcher
import com.android.systemui.common.coroutine.ConflatedCallbackFlow.conflatedCallbackFlow
import com.android.systemui.dagger.SysUISingleton
import com.android.systemui.dagger.qualifiers.Application
import com.android.systemui.dagger.qualifiers.Background
import com.android.systemui.statusbar.pipeline.mobile.data.model.MobileConnectivityModel
import com.android.systemui.statusbar.pipeline.mobile.data.model.NetworkNameModel
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
@@ -88,6 +90,14 @@ constructor(
) : MobileConnectionsRepository {
private var subIdRepositoryCache: MutableMap<Int, MobileConnectionRepository> = mutableMapOf()
private val defaultNetworkName =
NetworkNameModel.Default(
context.getString(com.android.internal.R.string.lockscreen_carrier_default)
)
private val networkNameSeparator: String =
context.getString(R.string.status_bar_network_name_separator)
/**
* State flow that emits the set of mobile data subscriptions, each represented by its own
* [SubscriptionInfo]. We probably only need the [SubscriptionInfo.getSubscriptionId] of each
@@ -243,6 +253,8 @@ constructor(
private fun createRepositoryForSubId(subId: Int): MobileConnectionRepository {
return mobileConnectionRepositoryFactory.build(
subId,
defaultNetworkName,
networkNameSeparator,
defaultDataSubId,
globalMobileDataSettingChangedEvent,
)

View File

@@ -20,10 +20,13 @@ import android.telephony.CarrierConfigManager
import com.android.settingslib.SignalIcon.MobileIconGroup
import com.android.systemui.dagger.qualifiers.Application
import com.android.systemui.statusbar.pipeline.mobile.data.model.DataConnectionState.Connected
import com.android.systemui.statusbar.pipeline.mobile.data.model.NetworkNameModel
import com.android.systemui.statusbar.pipeline.mobile.data.repository.MobileConnectionRepository
import com.android.systemui.statusbar.pipeline.shared.data.model.DataActivityModel
import com.android.systemui.util.CarrierConfigTracker
import kotlinx.coroutines.CoroutineScope
import kotlinx.coroutines.ExperimentalCoroutinesApi
import kotlinx.coroutines.flow.Flow
import kotlinx.coroutines.flow.MutableStateFlow
import kotlinx.coroutines.flow.SharingStarted
import kotlinx.coroutines.flow.StateFlow
@@ -32,6 +35,9 @@ import kotlinx.coroutines.flow.mapLatest
import kotlinx.coroutines.flow.stateIn
interface MobileIconInteractor {
/** The current mobile data activity */
val activity: Flow<DataActivityModel>
/** Only true if mobile is the default transport but is not validated, otherwise false */
val isDefaultConnectionFailed: StateFlow<Boolean>
@@ -51,6 +57,15 @@ interface MobileIconInteractor {
/** Observable for RAT type (network type) indicator */
val networkTypeIconGroup: StateFlow<MobileIconGroup>
/**
* Provider name for this network connection. The name can be one of 3 values:
* 1. The default network name, if one is configured
* 2. A derived name based off of the intent [ACTION_SERVICE_PROVIDERS_UPDATED]
* 3. Or, in the case where the repository sends us the default network name, we check for an
* override in [connectionInfo.operatorAlphaShort], a value that is derived from [ServiceState]
*/
val networkName: StateFlow<NetworkNameModel>
/** True if this line of service is emergency-only */
val isEmergencyOnly: StateFlow<Boolean>
@@ -82,10 +97,28 @@ class MobileIconInteractorImpl(
) : MobileIconInteractor {
private val connectionInfo = connectionRepository.connectionInfo
override val activity = connectionInfo.mapLatest { it.dataActivityDirection }
override val isDataEnabled: StateFlow<Boolean> = connectionRepository.dataEnabled
override val isDefaultDataEnabled = defaultSubscriptionHasDataEnabled
override val networkName =
combine(connectionInfo, connectionRepository.networkName) { connection, networkName ->
if (
networkName is NetworkNameModel.Default && connection.operatorAlphaShort != null
) {
NetworkNameModel.Derived(connection.operatorAlphaShort)
} else {
networkName
}
}
.stateIn(
scope,
SharingStarted.WhileSubscribed(),
connectionRepository.networkName.value
)
/** Observable for the current RAT indicator icon ([MobileIconGroup]) */
override val networkTypeIconGroup: StateFlow<MobileIconGroup> =
combine(

View File

@@ -17,6 +17,7 @@
package com.android.systemui.statusbar.pipeline.mobile.ui.binder
import android.content.res.ColorStateList
import android.view.View
import android.view.View.GONE
import android.view.View.VISIBLE
import android.view.ViewGroup
@@ -40,6 +41,9 @@ object MobileIconBinder {
view: ViewGroup,
viewModel: MobileIconViewModel,
) {
val activityContainer = view.requireViewById<View>(R.id.inout_container)
val activityIn = view.requireViewById<ImageView>(R.id.mobile_in)
val activityOut = view.requireViewById<ImageView>(R.id.mobile_out)
val networkTypeView = view.requireViewById<ImageView>(R.id.mobile_type)
val iconView = view.requireViewById<ImageView>(R.id.mobile_signal)
val mobileDrawable = SignalDrawable(view.context).also { iconView.setImageDrawable(it) }
@@ -74,6 +78,15 @@ object MobileIconBinder {
}
}
// Set the activity indicators
launch { viewModel.activityInVisible.collect { activityIn.isVisible = it } }
launch { viewModel.activityOutVisible.collect { activityOut.isVisible = it } }
launch {
viewModel.activityContainerVisible.collect { activityContainer.isVisible = it }
}
// Set the tint
launch {
viewModel.tint.collect { tint ->
@@ -81,6 +94,8 @@ object MobileIconBinder {
iconView.imageTintList = tintList
networkTypeView.imageTintList = tintList
roamingView.imageTintList = tintList
activityIn.imageTintList = tintList
activityOut.imageTintList = tintList
}
}
}

View File

@@ -22,13 +22,16 @@ import com.android.systemui.common.shared.model.ContentDescription
import com.android.systemui.common.shared.model.Icon
import com.android.systemui.statusbar.pipeline.mobile.domain.interactor.MobileIconInteractor
import com.android.systemui.statusbar.pipeline.mobile.domain.interactor.MobileIconsInteractor
import com.android.systemui.statusbar.pipeline.shared.ConnectivityConstants
import com.android.systemui.statusbar.pipeline.shared.ConnectivityPipelineLogger
import com.android.systemui.statusbar.pipeline.shared.ConnectivityPipelineLogger.Companion.logOutputChange
import com.android.systemui.statusbar.pipeline.shared.data.model.DataActivityModel
import kotlinx.coroutines.ExperimentalCoroutinesApi
import kotlinx.coroutines.flow.Flow
import kotlinx.coroutines.flow.combine
import kotlinx.coroutines.flow.distinctUntilChanged
import kotlinx.coroutines.flow.flowOf
import kotlinx.coroutines.flow.map
import kotlinx.coroutines.flow.mapLatest
/**
@@ -48,6 +51,7 @@ constructor(
val subscriptionId: Int,
iconInteractor: MobileIconInteractor,
logger: ConnectivityPipelineLogger,
constants: ConnectivityConstants,
) {
/** Whether or not to show the error state of [SignalDrawable] */
private val showExclamationMark: Flow<Boolean> =
@@ -89,5 +93,17 @@ constructor(
val roaming: Flow<Boolean> = iconInteractor.isRoaming
private val activity: Flow<DataActivityModel?> =
if (!constants.shouldShowActivityConfig) {
flowOf(null)
} else {
iconInteractor.activity
}
val activityInVisible: Flow<Boolean> = activity.map { it?.hasActivityIn ?: false }
val activityOutVisible: Flow<Boolean> = activity.map { it?.hasActivityOut ?: false }
val activityContainerVisible: Flow<Boolean> =
activity.map { it != null && (it.hasActivityIn || it.hasActivityOut) }
val tint: Flow<Int> = flowOf(Color.CYAN)
}

View File

@@ -20,6 +20,7 @@ package com.android.systemui.statusbar.pipeline.mobile.ui.viewmodel
import com.android.systemui.statusbar.pipeline.mobile.domain.interactor.MobileIconsInteractor
import com.android.systemui.statusbar.pipeline.mobile.ui.view.ModernStatusBarMobileView
import com.android.systemui.statusbar.pipeline.shared.ConnectivityConstants
import com.android.systemui.statusbar.pipeline.shared.ConnectivityPipelineLogger
import javax.inject.Inject
import kotlinx.coroutines.InternalCoroutinesApi
@@ -36,13 +37,15 @@ constructor(
val subscriptionIdsFlow: StateFlow<List<Int>>,
private val interactor: MobileIconsInteractor,
private val logger: ConnectivityPipelineLogger,
private val constants: ConnectivityConstants,
) {
/** TODO: do we need to cache these? */
fun viewModelForSub(subId: Int): MobileIconViewModel =
MobileIconViewModel(
subId,
interactor.createMobileConnectionInteractorForSubId(subId),
logger
logger,
constants,
)
class Factory
@@ -50,12 +53,14 @@ constructor(
constructor(
private val interactor: MobileIconsInteractor,
private val logger: ConnectivityPipelineLogger,
private val constants: ConnectivityConstants,
) {
fun create(subscriptionIdsFlow: StateFlow<List<Int>>): MobileIconsViewModel {
return MobileIconsViewModel(
subscriptionIdsFlow,
interactor,
logger,
constants,
)
}
}

View File

@@ -16,8 +16,10 @@
package com.android.systemui.statusbar.pipeline.shared
import android.content.Context
import android.telephony.TelephonyManager
import com.android.systemui.Dumpable
import com.android.systemui.R
import com.android.systemui.dagger.SysUISingleton
import com.android.systemui.dump.DumpManager
import com.android.systemui.statusbar.pipeline.shared.ConnectivityPipelineLogger.Companion.SB_LOGGING_TAG
@@ -32,15 +34,25 @@ import javax.inject.Inject
@SysUISingleton
class ConnectivityConstants
@Inject
constructor(dumpManager: DumpManager, telephonyManager: TelephonyManager) : Dumpable {
constructor(
context: Context,
dumpManager: DumpManager,
telephonyManager: TelephonyManager,
) : Dumpable {
init {
dumpManager.registerDumpable("${SB_LOGGING_TAG}Constants", this)
dumpManager.registerNormalDumpable("${SB_LOGGING_TAG}Constants", this)
}
/** True if this device has the capability for data connections and false otherwise. */
val hasDataCapabilities = telephonyManager.isDataCapable
/** True if we should show the activityIn/activityOut icons and false otherwise */
val shouldShowActivityConfig = context.resources.getBoolean(R.bool.config_showActivity)
override fun dump(pw: PrintWriter, args: Array<out String>) {
pw.apply { println("hasDataCapabilities=$hasDataCapabilities") }
pw.apply {
println("hasDataCapabilities=$hasDataCapabilities")
println("shouldShowActivityConfig=$shouldShowActivityConfig")
}
}
}

View File

@@ -16,6 +16,9 @@
package com.android.systemui.statusbar.pipeline.shared.data.model
import android.net.wifi.WifiManager
import android.telephony.Annotation
import android.telephony.TelephonyManager
import com.android.systemui.log.table.Diffable
import com.android.systemui.log.table.TableRowLogger
@@ -44,3 +47,25 @@ data class DataActivityModel(
const val ACTIVITY_PREFIX = "dataActivity"
private const val COL_ACTIVITY_IN = "in"
private const val COL_ACTIVITY_OUT = "out"
fun @receiver:Annotation.DataActivityType Int.toMobileDataActivityModel(): DataActivityModel =
when (this) {
TelephonyManager.DATA_ACTIVITY_IN ->
DataActivityModel(hasActivityIn = true, hasActivityOut = false)
TelephonyManager.DATA_ACTIVITY_OUT ->
DataActivityModel(hasActivityIn = false, hasActivityOut = true)
TelephonyManager.DATA_ACTIVITY_INOUT ->
DataActivityModel(hasActivityIn = true, hasActivityOut = true)
else -> DataActivityModel(hasActivityIn = false, hasActivityOut = false)
}
fun Int.toWifiDataActivityModel(): DataActivityModel =
when (this) {
WifiManager.TrafficStateCallback.DATA_ACTIVITY_IN ->
DataActivityModel(hasActivityIn = true, hasActivityOut = false)
WifiManager.TrafficStateCallback.DATA_ACTIVITY_OUT ->
DataActivityModel(hasActivityIn = false, hasActivityOut = true)
WifiManager.TrafficStateCallback.DATA_ACTIVITY_INOUT ->
DataActivityModel(hasActivityIn = true, hasActivityOut = true)
else -> DataActivityModel(hasActivityIn = false, hasActivityOut = false)
}

View File

@@ -43,6 +43,7 @@ import com.android.systemui.statusbar.pipeline.shared.ConnectivityPipelineLogger
import com.android.systemui.statusbar.pipeline.shared.ConnectivityPipelineLogger.Companion.SB_LOGGING_TAG
import com.android.systemui.statusbar.pipeline.shared.ConnectivityPipelineLogger.Companion.logInputChange
import com.android.systemui.statusbar.pipeline.shared.data.model.DataActivityModel
import com.android.systemui.statusbar.pipeline.shared.data.model.toWifiDataActivityModel
import com.android.systemui.statusbar.pipeline.wifi.data.model.WifiNetworkModel
import java.util.concurrent.Executor
import javax.inject.Inject
@@ -237,7 +238,7 @@ class WifiRepositoryImpl @Inject constructor(
conflatedCallbackFlow {
val callback = TrafficStateCallback { state ->
logger.logInputChange("onTrafficStateChange", prettyPrintActivity(state))
trySend(trafficStateToDataActivityModel(state))
trySend(state.toWifiDataActivityModel())
}
wifiManager.registerTrafficStateCallback(mainExecutor, callback)
awaitClose { wifiManager.unregisterTrafficStateCallback(callback) }
@@ -266,15 +267,6 @@ class WifiRepositoryImpl @Inject constructor(
// NetworkCallback inside [wifiNetwork] for our wifi network information.
val WIFI_NETWORK_DEFAULT = WifiNetworkModel.Inactive
private fun trafficStateToDataActivityModel(state: Int): DataActivityModel {
return DataActivityModel(
hasActivityIn = state == TrafficStateCallback.DATA_ACTIVITY_IN ||
state == TrafficStateCallback.DATA_ACTIVITY_INOUT,
hasActivityOut = state == TrafficStateCallback.DATA_ACTIVITY_OUT ||
state == TrafficStateCallback.DATA_ACTIVITY_INOUT,
)
}
private fun networkCapabilitiesToWifiInfo(
networkCapabilities: NetworkCapabilities
): WifiInfo? {

View File

@@ -17,6 +17,7 @@
package com.android.systemui.statusbar.pipeline.mobile.data.repository
import com.android.systemui.statusbar.pipeline.mobile.data.model.MobileConnectionModel
import com.android.systemui.statusbar.pipeline.mobile.data.model.NetworkNameModel
import kotlinx.coroutines.flow.MutableStateFlow
// TODO(b/261632894): remove this in favor of the real impl or DemoMobileConnectionRepository
@@ -32,6 +33,9 @@ class FakeMobileConnectionRepository(override val subId: Int) : MobileConnection
override val cdmaRoaming = MutableStateFlow(false)
override val networkName =
MutableStateFlow<NetworkNameModel>(NetworkNameModel.Default("default"))
fun setConnectionInfo(model: MobileConnectionModel) {
_connectionInfo.value = model
}

View File

@@ -18,13 +18,16 @@ package com.android.systemui.statusbar.pipeline.mobile.data.repository.demo
import android.telephony.Annotation
import android.telephony.TelephonyManager
import android.telephony.TelephonyManager.DATA_ACTIVITY_NONE
import androidx.test.filters.SmallTest
import com.android.settingslib.SignalIcon
import com.android.settingslib.mobile.TelephonyIcons
import com.android.systemui.SysuiTestCase
import com.android.systemui.statusbar.pipeline.mobile.data.model.DataConnectionState
import com.android.systemui.statusbar.pipeline.mobile.data.model.MobileConnectionModel
import com.android.systemui.statusbar.pipeline.mobile.data.model.NetworkNameModel
import com.android.systemui.statusbar.pipeline.mobile.data.repository.demo.model.FakeNetworkEventModel
import com.android.systemui.statusbar.pipeline.shared.data.model.toMobileDataActivityModel
import com.android.systemui.util.mockito.mock
import com.android.systemui.util.mockito.whenever
import com.google.common.truth.Truth.assertThat
@@ -96,6 +99,7 @@ internal class DemoMobileConnectionParameterizedTest(private val testCase: TestC
activity = testCase.activity,
carrierNetworkChange = testCase.carrierNetworkChange,
roaming = testCase.roaming,
name = "demo name",
)
fakeNetworkEventFlow.value = networkModel
@@ -114,10 +118,12 @@ internal class DemoMobileConnectionParameterizedTest(private val testCase: TestC
assertThat(conn.subId).isEqualTo(model.subId)
assertThat(connectionInfo.cdmaLevel).isEqualTo(model.level)
assertThat(connectionInfo.primaryLevel).isEqualTo(model.level)
assertThat(connectionInfo.dataActivityDirection).isEqualTo(model.activity)
assertThat(connectionInfo.dataActivityDirection)
.isEqualTo((model.activity ?: DATA_ACTIVITY_NONE).toMobileDataActivityModel())
assertThat(connectionInfo.carrierNetworkChangeActive)
.isEqualTo(model.carrierNetworkChange)
assertThat(connectionInfo.isRoaming).isEqualTo(model.roaming)
assertThat(conn.networkName.value).isEqualTo(NetworkNameModel.Derived(model.name))
// TODO(b/261029387): check these once we start handling them
assertThat(connectionInfo.isEmergencyOnly).isFalse()
@@ -141,6 +147,7 @@ internal class DemoMobileConnectionParameterizedTest(private val testCase: TestC
@Annotation.DataActivityType val activity: Int,
val carrierNetworkChange: Boolean,
val roaming: Boolean,
val name: String,
) {
override fun toString(): String {
return "INPUT(level=$level, " +
@@ -150,7 +157,8 @@ internal class DemoMobileConnectionParameterizedTest(private val testCase: TestC
"inflateStrength=$inflateStrength, " +
"activity=$activity, " +
"carrierNetworkChange=$carrierNetworkChange, " +
"roaming=$roaming)"
"roaming=$roaming, " +
"name=$name)"
}
// Convenience for iterating test data and creating new cases
@@ -163,6 +171,7 @@ internal class DemoMobileConnectionParameterizedTest(private val testCase: TestC
@Annotation.DataActivityType activity: Int? = null,
carrierNetworkChange: Boolean? = null,
roaming: Boolean? = null,
name: String? = null,
): TestCase =
TestCase(
level = level ?: this.level,
@@ -173,6 +182,7 @@ internal class DemoMobileConnectionParameterizedTest(private val testCase: TestC
activity = activity ?: this.activity,
carrierNetworkChange = carrierNetworkChange ?: this.carrierNetworkChange,
roaming = roaming ?: this.roaming,
name = name ?: this.name,
)
}
@@ -201,6 +211,7 @@ internal class DemoMobileConnectionParameterizedTest(private val testCase: TestC
private val carrierNetworkChange = booleanList
// false first so the base case doesn't have roaming set (more common)
private val roaming = listOf(false, true)
private val names = listOf("name 1", "name 2")
@Parameters(name = "{0}") @JvmStatic fun data() = testData()
@@ -236,6 +247,7 @@ internal class DemoMobileConnectionParameterizedTest(private val testCase: TestC
activity.first(),
carrierNetworkChange.first(),
roaming.first(),
names.first(),
)
val tail =
@@ -246,7 +258,8 @@ internal class DemoMobileConnectionParameterizedTest(private val testCase: TestC
inflateStrength.map { baseCase.modifiedBy(inflateStrength = it) },
activity.map { baseCase.modifiedBy(activity = it) },
carrierNetworkChange.map { baseCase.modifiedBy(carrierNetworkChange = it) },
roaming.map { baseCase.modifiedBy(roaming = it) }
roaming.map { baseCase.modifiedBy(roaming = it) },
names.map { baseCase.modifiedBy(name = it) },
)
.flatten()

View File

@@ -17,6 +17,7 @@
package com.android.systemui.statusbar.pipeline.mobile.data.repository.demo
import android.telephony.TelephonyManager.DATA_ACTIVITY_INOUT
import android.telephony.TelephonyManager.DATA_ACTIVITY_NONE
import android.telephony.TelephonyManager.UNKNOWN_CARRIER_ID
import androidx.test.filters.SmallTest
import com.android.settingslib.SignalIcon
@@ -24,9 +25,11 @@ import com.android.settingslib.mobile.TelephonyIcons.THREE_G
import com.android.systemui.SysuiTestCase
import com.android.systemui.statusbar.pipeline.mobile.data.model.DataConnectionState
import com.android.systemui.statusbar.pipeline.mobile.data.model.MobileConnectionModel
import com.android.systemui.statusbar.pipeline.mobile.data.model.NetworkNameModel
import com.android.systemui.statusbar.pipeline.mobile.data.model.SubscriptionModel
import com.android.systemui.statusbar.pipeline.mobile.data.repository.demo.model.FakeNetworkEventModel
import com.android.systemui.statusbar.pipeline.mobile.data.repository.demo.model.FakeNetworkEventModel.MobileDisabled
import com.android.systemui.statusbar.pipeline.shared.data.model.toMobileDataActivityModel
import com.android.systemui.util.mockito.mock
import com.android.systemui.util.mockito.whenever
import com.google.common.truth.Truth.assertThat
@@ -289,10 +292,12 @@ class DemoMobileConnectionsRepositoryTest : SysuiTestCase() {
assertThat(conn.subId).isEqualTo(model.subId)
assertThat(connectionInfo.cdmaLevel).isEqualTo(model.level)
assertThat(connectionInfo.primaryLevel).isEqualTo(model.level)
assertThat(connectionInfo.dataActivityDirection).isEqualTo(model.activity)
assertThat(connectionInfo.dataActivityDirection)
.isEqualTo((model.activity ?: DATA_ACTIVITY_NONE).toMobileDataActivityModel())
assertThat(connectionInfo.carrierNetworkChangeActive)
.isEqualTo(model.carrierNetworkChange)
assertThat(connectionInfo.isRoaming).isEqualTo(model.roaming)
assertThat(conn.networkName.value).isEqualTo(NetworkNameModel.Derived(model.name))
// TODO(b/261029387) check these once we start handling them
assertThat(connectionInfo.isEmergencyOnly).isFalse()
@@ -325,4 +330,5 @@ fun validMobileEvent(
activity = activity,
carrierNetworkChange = carrierNetworkChange,
roaming = roaming,
name = "demo name",
)

View File

@@ -16,6 +16,7 @@
package com.android.systemui.statusbar.pipeline.mobile.data.repository.prod
import android.content.Intent
import android.os.UserHandle
import android.provider.Settings
import android.telephony.CellSignalStrengthCdma
@@ -23,10 +24,16 @@ import android.telephony.ServiceState
import android.telephony.SignalStrength
import android.telephony.SubscriptionInfo
import android.telephony.TelephonyCallback
import android.telephony.TelephonyCallback.DataActivityListener
import android.telephony.TelephonyCallback.ServiceStateListener
import android.telephony.TelephonyDisplayInfo
import android.telephony.TelephonyDisplayInfo.OVERRIDE_NETWORK_TYPE_LTE_CA
import android.telephony.TelephonyManager
import android.telephony.TelephonyManager.DATA_ACTIVITY_DORMANT
import android.telephony.TelephonyManager.DATA_ACTIVITY_IN
import android.telephony.TelephonyManager.DATA_ACTIVITY_INOUT
import android.telephony.TelephonyManager.DATA_ACTIVITY_NONE
import android.telephony.TelephonyManager.DATA_ACTIVITY_OUT
import android.telephony.TelephonyManager.DATA_CONNECTED
import android.telephony.TelephonyManager.DATA_CONNECTING
import android.telephony.TelephonyManager.DATA_DISCONNECTED
@@ -34,18 +41,27 @@ import android.telephony.TelephonyManager.DATA_DISCONNECTING
import android.telephony.TelephonyManager.DATA_UNKNOWN
import android.telephony.TelephonyManager.ERI_OFF
import android.telephony.TelephonyManager.ERI_ON
import android.telephony.TelephonyManager.EXTRA_PLMN
import android.telephony.TelephonyManager.EXTRA_SHOW_PLMN
import android.telephony.TelephonyManager.EXTRA_SHOW_SPN
import android.telephony.TelephonyManager.EXTRA_SPN
import android.telephony.TelephonyManager.EXTRA_SUBSCRIPTION_ID
import android.telephony.TelephonyManager.NETWORK_TYPE_LTE
import android.telephony.TelephonyManager.NETWORK_TYPE_UNKNOWN
import androidx.test.filters.SmallTest
import com.android.systemui.SysuiTestCase
import com.android.systemui.statusbar.pipeline.mobile.data.model.DataConnectionState
import com.android.systemui.statusbar.pipeline.mobile.data.model.MobileConnectionModel
import com.android.systemui.statusbar.pipeline.mobile.data.model.NetworkNameModel
import com.android.systemui.statusbar.pipeline.mobile.data.model.ResolvedNetworkType.DefaultNetworkType
import com.android.systemui.statusbar.pipeline.mobile.data.model.ResolvedNetworkType.OverrideNetworkType
import com.android.systemui.statusbar.pipeline.mobile.data.model.ResolvedNetworkType.UnknownNetworkType
import com.android.systemui.statusbar.pipeline.mobile.data.model.toNetworkNameModel
import com.android.systemui.statusbar.pipeline.mobile.data.repository.FakeMobileConnectionsRepository
import com.android.systemui.statusbar.pipeline.mobile.util.FakeMobileMappingsProxy
import com.android.systemui.statusbar.pipeline.shared.ConnectivityPipelineLogger
import com.android.systemui.statusbar.pipeline.shared.data.model.DataActivityModel
import com.android.systemui.statusbar.pipeline.shared.data.model.toMobileDataActivityModel
import com.android.systemui.util.mockito.any
import com.android.systemui.util.mockito.argumentCaptor
import com.android.systemui.util.mockito.mock
@@ -90,8 +106,11 @@ class MobileConnectionRepositoryTest : SysuiTestCase() {
MobileConnectionRepositoryImpl(
context,
SUB_1_ID,
DEFAULT_NAME,
SEP,
telephonyManager,
globalSettings,
fakeBroadcastDispatcher,
connectionsRepo.defaultDataSubId,
connectionsRepo.globalMobileDataSettingChangedEvent,
mobileMappings,
@@ -249,10 +268,11 @@ class MobileConnectionRepositoryTest : SysuiTestCase() {
var latest: MobileConnectionModel? = null
val job = underTest.connectionInfo.onEach { latest = it }.launchIn(this)
val callback = getTelephonyCallbackForType<TelephonyCallback.DataActivityListener>()
callback.onDataActivity(3)
val callback = getTelephonyCallbackForType<DataActivityListener>()
callback.onDataActivity(DATA_ACTIVITY_INOUT)
assertThat(latest?.dataActivityDirection).isEqualTo(3)
assertThat(latest?.dataActivityDirection)
.isEqualTo(DATA_ACTIVITY_INOUT.toMobileDataActivityModel())
job.cancel()
}
@@ -459,6 +479,112 @@ class MobileConnectionRepositoryTest : SysuiTestCase() {
job.cancel()
}
@Test
fun `activity - updates from callback`() =
runBlocking(IMMEDIATE) {
var latest: DataActivityModel? = null
val job =
underTest.connectionInfo.onEach { latest = it.dataActivityDirection }.launchIn(this)
assertThat(latest)
.isEqualTo(DataActivityModel(hasActivityIn = false, hasActivityOut = false))
val cb = getTelephonyCallbackForType<DataActivityListener>()
cb.onDataActivity(DATA_ACTIVITY_IN)
assertThat(latest)
.isEqualTo(DataActivityModel(hasActivityIn = true, hasActivityOut = false))
cb.onDataActivity(DATA_ACTIVITY_OUT)
assertThat(latest)
.isEqualTo(DataActivityModel(hasActivityIn = false, hasActivityOut = true))
cb.onDataActivity(DATA_ACTIVITY_INOUT)
assertThat(latest)
.isEqualTo(DataActivityModel(hasActivityIn = true, hasActivityOut = true))
cb.onDataActivity(DATA_ACTIVITY_NONE)
assertThat(latest)
.isEqualTo(DataActivityModel(hasActivityIn = false, hasActivityOut = false))
cb.onDataActivity(DATA_ACTIVITY_DORMANT)
assertThat(latest)
.isEqualTo(DataActivityModel(hasActivityIn = false, hasActivityOut = false))
cb.onDataActivity(1234)
assertThat(latest)
.isEqualTo(DataActivityModel(hasActivityIn = false, hasActivityOut = false))
job.cancel()
}
@Test
fun `network name - default`() =
runBlocking(IMMEDIATE) {
var latest: NetworkNameModel? = null
val job = underTest.networkName.onEach { latest = it }.launchIn(this)
assertThat(latest).isEqualTo(DEFAULT_NAME)
job.cancel()
}
@Test
fun `network name - uses broadcast info - returns derived`() =
runBlocking(IMMEDIATE) {
var latest: NetworkNameModel? = null
val job = underTest.networkName.onEach { latest = it }.launchIn(this)
val intent = spnIntent()
fakeBroadcastDispatcher.registeredReceivers.forEach { receiver ->
receiver.onReceive(context, intent)
}
assertThat(latest).isEqualTo(intent.toNetworkNameModel(SEP))
job.cancel()
}
@Test
fun `network name - broadcast not for this sub id - returns default`() =
runBlocking(IMMEDIATE) {
var latest: NetworkNameModel? = null
val job = underTest.networkName.onEach { latest = it }.launchIn(this)
val intent = spnIntent(subId = 101)
fakeBroadcastDispatcher.registeredReceivers.forEach { receiver ->
receiver.onReceive(context, intent)
}
assertThat(latest).isEqualTo(DEFAULT_NAME)
job.cancel()
}
@Test
fun `network name - operatorAlphaShort - tracked`() =
runBlocking(IMMEDIATE) {
var latest: String? = null
val job =
underTest.connectionInfo.onEach { latest = it.operatorAlphaShort }.launchIn(this)
val shortName = "short name"
val serviceState = ServiceState()
serviceState.setOperatorName(
/* longName */ "long name",
/* shortName */ shortName,
/* numeric */ "12345",
)
getTelephonyCallbackForType<ServiceStateListener>().onServiceStateChanged(serviceState)
assertThat(latest).isEqualTo(shortName)
job.cancel()
}
private fun getTelephonyCallbacks(): List<TelephonyCallback> {
val callbackCaptor = argumentCaptor<TelephonyCallback>()
Mockito.verify(telephonyManager).registerTelephonyCallback(any(), callbackCaptor.capture())
@@ -484,10 +610,31 @@ class MobileConnectionRepositoryTest : SysuiTestCase() {
return signalStrength
}
private fun spnIntent(
subId: Int = SUB_1_ID,
showSpn: Boolean = true,
spn: String = SPN,
showPlmn: Boolean = true,
plmn: String = PLMN,
): Intent =
Intent(TelephonyManager.ACTION_SERVICE_PROVIDERS_UPDATED).apply {
putExtra(EXTRA_SUBSCRIPTION_ID, subId)
putExtra(EXTRA_SHOW_SPN, showSpn)
putExtra(EXTRA_SPN, spn)
putExtra(EXTRA_SHOW_PLMN, showPlmn)
putExtra(EXTRA_PLMN, plmn)
}
companion object {
private val IMMEDIATE = Dispatchers.Main.immediate
private const val SUB_1_ID = 1
private val SUB_1 =
mock<SubscriptionInfo>().also { whenever(it.subscriptionId).thenReturn(SUB_1_ID) }
private val DEFAULT_NAME = NetworkNameModel.Default("default name")
private const val SEP = "-"
private const val SPN = "testSpn"
private const val PLMN = "testPlmn"
}
}

View File

@@ -91,6 +91,7 @@ class MobileConnectionsRepositoryTest : SysuiTestCase() {
connectionFactory =
MobileConnectionRepositoryImpl.Factory(
fakeBroadcastDispatcher,
context = context,
telephonyManager = telephonyManager,
bgDispatcher = IMMEDIATE,

View File

@@ -19,14 +19,26 @@ package com.android.systemui.statusbar.pipeline.mobile.domain.interactor
import android.telephony.CellSignalStrength
import com.android.settingslib.SignalIcon
import com.android.settingslib.mobile.TelephonyIcons
import com.android.systemui.statusbar.pipeline.mobile.data.model.NetworkNameModel
import com.android.systemui.statusbar.pipeline.shared.data.model.DataActivityModel
import kotlinx.coroutines.flow.MutableStateFlow
class FakeMobileIconInteractor : MobileIconInteractor {
override val alwaysShowDataRatIcon = MutableStateFlow(false)
override val activity =
MutableStateFlow(
DataActivityModel(
hasActivityIn = false,
hasActivityOut = false,
)
)
private val _iconGroup = MutableStateFlow<SignalIcon.MobileIconGroup>(TelephonyIcons.THREE_G)
override val networkTypeIconGroup = _iconGroup
override val networkName = MutableStateFlow(NetworkNameModel.Derived("demo mode"))
private val _isEmergencyOnly = MutableStateFlow(false)
override val isEmergencyOnly = _isEmergencyOnly

View File

@@ -25,6 +25,7 @@ import com.android.settingslib.mobile.TelephonyIcons
import com.android.systemui.SysuiTestCase
import com.android.systemui.statusbar.pipeline.mobile.data.model.DataConnectionState
import com.android.systemui.statusbar.pipeline.mobile.data.model.MobileConnectionModel
import com.android.systemui.statusbar.pipeline.mobile.data.model.NetworkNameModel
import com.android.systemui.statusbar.pipeline.mobile.data.model.ResolvedNetworkType.DefaultNetworkType
import com.android.systemui.statusbar.pipeline.mobile.data.model.ResolvedNetworkType.OverrideNetworkType
import com.android.systemui.statusbar.pipeline.mobile.data.repository.FakeMobileConnectionRepository
@@ -392,6 +393,41 @@ class MobileIconInteractorTest : SysuiTestCase() {
job.cancel()
}
@Test
fun `network name - uses operatorAlphaShot when non null and repo is default`() =
runBlocking(IMMEDIATE) {
var latest: NetworkNameModel? = null
val job = underTest.networkName.onEach { latest = it }.launchIn(this)
val testOperatorName = "operatorAlphaShort"
// Default network name, operator name is non-null, uses the operator name
connectionRepository.networkName.value = DEFAULT_NAME
connectionRepository.setConnectionInfo(
MobileConnectionModel(operatorAlphaShort = testOperatorName)
)
yield()
assertThat(latest).isEqualTo(NetworkNameModel.Derived(testOperatorName))
// Default network name, operator name is null, uses the default
connectionRepository.setConnectionInfo(MobileConnectionModel(operatorAlphaShort = null))
yield()
assertThat(latest).isEqualTo(DEFAULT_NAME)
// Derived network name, operator name non-null, uses the derived name
connectionRepository.networkName.value = DERIVED_NAME
connectionRepository.setConnectionInfo(
MobileConnectionModel(operatorAlphaShort = testOperatorName)
)
yield()
assertThat(latest).isEqualTo(DERIVED_NAME)
job.cancel()
}
companion object {
private val IMMEDIATE = Dispatchers.Main.immediate
@@ -401,5 +437,8 @@ class MobileIconInteractorTest : SysuiTestCase() {
private const val SUB_1_ID = 1
private val SUB_1 =
mock<SubscriptionInfo>().also { whenever(it.subscriptionId).thenReturn(SUB_1_ID) }
private val DEFAULT_NAME = NetworkNameModel.Default("test default name")
private val DERIVED_NAME = NetworkNameModel.Derived("test derived name")
}
}

View File

@@ -23,7 +23,10 @@ import com.android.systemui.SysuiTestCase
import com.android.systemui.common.shared.model.ContentDescription
import com.android.systemui.common.shared.model.Icon
import com.android.systemui.statusbar.pipeline.mobile.domain.interactor.FakeMobileIconInteractor
import com.android.systemui.statusbar.pipeline.shared.ConnectivityConstants
import com.android.systemui.statusbar.pipeline.shared.ConnectivityPipelineLogger
import com.android.systemui.statusbar.pipeline.shared.data.model.DataActivityModel
import com.android.systemui.util.mockito.whenever
import com.google.common.truth.Truth.assertThat
import kotlinx.coroutines.Dispatchers
import kotlinx.coroutines.flow.launchIn
@@ -40,6 +43,7 @@ class MobileIconViewModelTest : SysuiTestCase() {
private lateinit var underTest: MobileIconViewModel
private val interactor = FakeMobileIconInteractor()
@Mock private lateinit var logger: ConnectivityPipelineLogger
@Mock private lateinit var constants: ConnectivityConstants
@Before
fun setUp() {
@@ -53,7 +57,7 @@ class MobileIconViewModelTest : SysuiTestCase() {
setNumberOfLevels(4)
isDataConnected.value = true
}
underTest = MobileIconViewModel(SUB_1_ID, interactor, logger)
underTest = MobileIconViewModel(SUB_1_ID, interactor, logger, constants)
}
@Test
@@ -250,6 +254,92 @@ class MobileIconViewModelTest : SysuiTestCase() {
job.cancel()
}
@Test
fun `data activity - null when config is off`() =
runBlocking(IMMEDIATE) {
// Create a new view model here so the constants are properly read
whenever(constants.shouldShowActivityConfig).thenReturn(false)
underTest = MobileIconViewModel(SUB_1_ID, interactor, logger, constants)
var inVisible: Boolean? = null
val inJob = underTest.activityInVisible.onEach { inVisible = it }.launchIn(this)
var outVisible: Boolean? = null
val outJob = underTest.activityInVisible.onEach { outVisible = it }.launchIn(this)
var containerVisible: Boolean? = null
val containerJob =
underTest.activityInVisible.onEach { containerVisible = it }.launchIn(this)
interactor.activity.value =
DataActivityModel(
hasActivityIn = true,
hasActivityOut = true,
)
assertThat(inVisible).isFalse()
assertThat(outVisible).isFalse()
assertThat(containerVisible).isFalse()
inJob.cancel()
outJob.cancel()
containerJob.cancel()
}
@Test
fun `data activity - config on - test indicators`() =
runBlocking(IMMEDIATE) {
// Create a new view model here so the constants are properly read
whenever(constants.shouldShowActivityConfig).thenReturn(true)
underTest = MobileIconViewModel(SUB_1_ID, interactor, logger, constants)
var inVisible: Boolean? = null
val inJob = underTest.activityInVisible.onEach { inVisible = it }.launchIn(this)
var outVisible: Boolean? = null
val outJob = underTest.activityOutVisible.onEach { outVisible = it }.launchIn(this)
var containerVisible: Boolean? = null
val containerJob =
underTest.activityContainerVisible.onEach { containerVisible = it }.launchIn(this)
interactor.activity.value =
DataActivityModel(
hasActivityIn = true,
hasActivityOut = false,
)
yield()
assertThat(inVisible).isTrue()
assertThat(outVisible).isFalse()
assertThat(containerVisible).isTrue()
interactor.activity.value =
DataActivityModel(
hasActivityIn = false,
hasActivityOut = true,
)
assertThat(inVisible).isFalse()
assertThat(outVisible).isTrue()
assertThat(containerVisible).isTrue()
interactor.activity.value =
DataActivityModel(
hasActivityIn = false,
hasActivityOut = false,
)
assertThat(inVisible).isFalse()
assertThat(outVisible).isFalse()
assertThat(containerVisible).isFalse()
inJob.cancel()
outJob.cancel()
containerJob.cancel()
}
/** Convenience constructor for these tests */
private fun defaultSignal(
level: Int = 1,