Merge changes I04fa90bf,Id413d57d into tm-qpr-dev am: 7f90cf447d
Original change: https://googleplex-android-review.googlesource.com/c/platform/frameworks/base/+/20680974 Change-Id: I5efb2c769773dea34bcbc50db1a4b38d0f9e30aa Signed-off-by: Automerger Merge Worker <android-build-automerger-merge-worker@system.gserviceaccount.com>
This commit is contained in:
@@ -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
|
||||
*/
|
||||
|
||||
@@ -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
|
||||
}
|
||||
@@ -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>
|
||||
}
|
||||
|
||||
@@ -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"))
|
||||
}
|
||||
|
||||
@@ -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,
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
@@ -35,6 +35,7 @@ sealed interface FakeNetworkEventModel {
|
||||
@DataActivityType val activity: Int?,
|
||||
val carrierNetworkChange: Boolean,
|
||||
val roaming: Boolean,
|
||||
val name: String,
|
||||
) : FakeNetworkEventModel
|
||||
|
||||
data class MobileDisabled(
|
||||
|
||||
@@ -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,
|
||||
|
||||
@@ -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,
|
||||
)
|
||||
|
||||
@@ -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(
|
||||
|
||||
@@ -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
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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)
|
||||
}
|
||||
|
||||
@@ -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,
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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")
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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)
|
||||
}
|
||||
|
||||
@@ -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? {
|
||||
|
||||
@@ -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
|
||||
}
|
||||
|
||||
@@ -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()
|
||||
|
||||
|
||||
@@ -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",
|
||||
)
|
||||
|
||||
@@ -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"
|
||||
}
|
||||
}
|
||||
|
||||
@@ -91,6 +91,7 @@ class MobileConnectionsRepositoryTest : SysuiTestCase() {
|
||||
|
||||
connectionFactory =
|
||||
MobileConnectionRepositoryImpl.Factory(
|
||||
fakeBroadcastDispatcher,
|
||||
context = context,
|
||||
telephonyManager = telephonyManager,
|
||||
bgDispatcher = IMMEDIATE,
|
||||
|
||||
@@ -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
|
||||
|
||||
|
||||
@@ -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")
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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,
|
||||
|
||||
Reference in New Issue
Block a user