diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/pipeline/dagger/StatusBarPipelineModule.kt b/packages/SystemUI/src/com/android/systemui/statusbar/pipeline/dagger/StatusBarPipelineModule.kt index 2aaa085645e4b..6c9ad6a2df7f0 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/pipeline/dagger/StatusBarPipelineModule.kt +++ b/packages/SystemUI/src/com/android/systemui/statusbar/pipeline/dagger/StatusBarPipelineModule.kt @@ -22,6 +22,10 @@ import com.android.systemui.statusbar.pipeline.mobile.data.repository.MobileSubs import com.android.systemui.statusbar.pipeline.mobile.data.repository.MobileSubscriptionRepositoryImpl import com.android.systemui.statusbar.pipeline.mobile.data.repository.UserSetupRepository import com.android.systemui.statusbar.pipeline.mobile.data.repository.UserSetupRepositoryImpl +import com.android.systemui.statusbar.pipeline.mobile.domain.interactor.MobileIconsInteractor +import com.android.systemui.statusbar.pipeline.mobile.domain.interactor.MobileIconsInteractorImpl +import com.android.systemui.statusbar.pipeline.mobile.util.MobileMappingsProxy +import com.android.systemui.statusbar.pipeline.mobile.util.MobileMappingsProxyImpl import com.android.systemui.statusbar.pipeline.shared.data.repository.ConnectivityRepository import com.android.systemui.statusbar.pipeline.shared.data.repository.ConnectivityRepositoryImpl import com.android.systemui.statusbar.pipeline.wifi.data.repository.WifiRepository @@ -47,4 +51,10 @@ abstract class StatusBarPipelineModule { @Binds abstract fun userSetupRepository(impl: UserSetupRepositoryImpl): UserSetupRepository + + @Binds + abstract fun mobileMappingsProxy(impl: MobileMappingsProxyImpl): MobileMappingsProxy + + @Binds + abstract fun mobileIconsInteractor(impl: MobileIconsInteractorImpl): MobileIconsInteractor } diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/pipeline/mobile/data/model/MobileSubscriptionModel.kt b/packages/SystemUI/src/com/android/systemui/statusbar/pipeline/mobile/data/model/MobileSubscriptionModel.kt index 46ccf32cc7f93..eaba0e93e7504 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/pipeline/mobile/data/model/MobileSubscriptionModel.kt +++ b/packages/SystemUI/src/com/android/systemui/statusbar/pipeline/mobile/data/model/MobileSubscriptionModel.kt @@ -27,6 +27,7 @@ import android.telephony.TelephonyCallback.ServiceStateListener import android.telephony.TelephonyCallback.SignalStrengthsListener import android.telephony.TelephonyDisplayInfo import android.telephony.TelephonyManager +import android.telephony.TelephonyManager.NETWORK_TYPE_UNKNOWN /** * Data class containing all of the relevant information for a particular line of service, known as @@ -57,6 +58,11 @@ data class MobileSubscriptionModel( /** From [CarrierNetworkListener.onCarrierNetworkChange] */ val carrierNetworkChangeActive: Boolean? = null, - /** From [DisplayInfoListener.onDisplayInfoChanged] */ - val displayInfo: TelephonyDisplayInfo? = null + /** + * 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 + */ + val resolvedNetworkType: ResolvedNetworkType = DefaultNetworkType(NETWORK_TYPE_UNKNOWN), ) diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/pipeline/mobile/data/model/ResolvedNetworkType.kt b/packages/SystemUI/src/com/android/systemui/statusbar/pipeline/mobile/data/model/ResolvedNetworkType.kt new file mode 100644 index 0000000000000..f385806c1b223 --- /dev/null +++ b/packages/SystemUI/src/com/android/systemui/statusbar/pipeline/mobile/data/model/ResolvedNetworkType.kt @@ -0,0 +1,33 @@ +/* + * 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.telephony.Annotation.NetworkType +import com.android.systemui.statusbar.pipeline.mobile.util.MobileMappingsProxy + +/** + * A SysUI type to represent the [NetworkType] that we pull out of [TelephonyDisplayInfo]. Depending + * on whether or not the display info contains an override type, we may have to call different + * methods on [MobileMappingsProxy] to generate an icon lookup key. + */ +sealed interface ResolvedNetworkType { + @NetworkType val type: Int +} + +data class DefaultNetworkType(@NetworkType override val type: Int) : ResolvedNetworkType + +data class OverrideNetworkType(@NetworkType override val type: Int) : ResolvedNetworkType diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/pipeline/mobile/data/repository/MobileSubscriptionRepository.kt b/packages/SystemUI/src/com/android/systemui/statusbar/pipeline/mobile/data/repository/MobileSubscriptionRepository.kt index 36de2a2541600..b6adc406cce99 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/pipeline/mobile/data/repository/MobileSubscriptionRepository.kt +++ b/packages/SystemUI/src/com/android/systemui/statusbar/pipeline/mobile/data/repository/MobileSubscriptionRepository.kt @@ -16,6 +16,9 @@ package com.android.systemui.statusbar.pipeline.mobile.data.repository +import android.content.Context +import android.content.IntentFilter +import android.telephony.CarrierConfigManager import android.telephony.CellSignalStrength import android.telephony.CellSignalStrengthCdma import android.telephony.ServiceState @@ -31,13 +34,21 @@ import android.telephony.TelephonyCallback.DisplayInfoListener import android.telephony.TelephonyCallback.ServiceStateListener import android.telephony.TelephonyCallback.SignalStrengthsListener import android.telephony.TelephonyDisplayInfo +import android.telephony.TelephonyDisplayInfo.OVERRIDE_NETWORK_TYPE_NONE import android.telephony.TelephonyManager import androidx.annotation.VisibleForTesting +import com.android.settingslib.mobile.MobileMappings +import com.android.settingslib.mobile.MobileMappings.Config +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.DefaultNetworkType import com.android.systemui.statusbar.pipeline.mobile.data.model.MobileSubscriptionModel +import com.android.systemui.statusbar.pipeline.mobile.data.model.OverrideNetworkType +import com.android.systemui.statusbar.pipeline.mobile.util.MobileMappingsProxy +import com.android.systemui.statusbar.pipeline.shared.ConnectivityPipelineLogger import javax.inject.Inject import kotlinx.coroutines.CoroutineDispatcher import kotlinx.coroutines.CoroutineScope @@ -47,7 +58,9 @@ import kotlinx.coroutines.channels.awaitClose import kotlinx.coroutines.flow.Flow import kotlinx.coroutines.flow.SharingStarted import kotlinx.coroutines.flow.StateFlow +import kotlinx.coroutines.flow.combine import kotlinx.coroutines.flow.mapLatest +import kotlinx.coroutines.flow.onEach import kotlinx.coroutines.flow.stateIn import kotlinx.coroutines.withContext @@ -62,6 +75,9 @@ interface MobileSubscriptionRepository { /** Observable for the subscriptionId of the current mobile data connection */ val activeMobileDataSubscriptionId: Flow + /** Observable for [MobileMappings.Config] tracking the defaults */ + val defaultDataSubRatConfig: StateFlow + /** Get or create an observable for the given subscription ID */ fun getFlowForSubId(subId: Int): Flow } @@ -74,6 +90,10 @@ class MobileSubscriptionRepositoryImpl constructor( private val subscriptionManager: SubscriptionManager, private val telephonyManager: TelephonyManager, + private val logger: ConnectivityPipelineLogger, + broadcastDispatcher: BroadcastDispatcher, + private val context: Context, + private val mobileMappings: MobileMappingsProxy, @Background private val bgDispatcher: CoroutineDispatcher, @Application private val scope: CoroutineScope, ) : MobileSubscriptionRepository { @@ -122,6 +142,36 @@ constructor( SubscriptionManager.INVALID_SUBSCRIPTION_ID ) + private val defaultDataSubChangedEvent = + broadcastDispatcher.broadcastFlow( + IntentFilter(TelephonyManager.ACTION_DEFAULT_DATA_SUBSCRIPTION_CHANGED) + ) + + private val carrierConfigChangedEvent = + broadcastDispatcher.broadcastFlow( + IntentFilter(CarrierConfigManager.ACTION_CARRIER_CONFIG_CHANGED) + ) + + /** + * [Config] is an object that tracks relevant configuration flags for a given subscription ID. + * In the case of [MobileMappings], it's hard-coded to check the default data subscription's + * config, so this will apply to every icon that we care about. + * + * Relevant bits in the config are things like + * [CarrierConfigManager.KEY_SHOW_4G_FOR_LTE_DATA_ICON_BOOL] + * + * This flow will produce whenever the default data subscription or the carrier config changes. + */ + override val defaultDataSubRatConfig: StateFlow = + combine(defaultDataSubChangedEvent, carrierConfigChangedEvent) { _, _ -> + Config.readConfig(context) + } + .stateIn( + scope, + SharingStarted.WhileSubscribed(), + initialValue = Config.readConfig(context) + ) + /** * Each mobile subscription needs its own flow, which comes from registering listeners on the * system. Use this method to create those flows and cache them for reuse @@ -151,6 +201,7 @@ constructor( state = state.copy(isEmergencyOnly = serviceState.isEmergencyOnly) trySend(state) } + override fun onSignalStrengthsChanged(signalStrength: SignalStrength) { val cdmaLevel = signalStrength @@ -173,6 +224,7 @@ constructor( ) trySend(state) } + override fun onDataConnectionStateChanged( dataState: Int, networkType: Int @@ -180,18 +232,31 @@ constructor( state = state.copy(dataConnectionState = dataState) trySend(state) } + override fun onDataActivity(direction: Int) { state = state.copy(dataActivityDirection = direction) trySend(state) } + override fun onCarrierNetworkChange(active: Boolean) { state = state.copy(carrierNetworkChangeActive = active) trySend(state) } + override fun onDisplayInfoChanged( telephonyDisplayInfo: TelephonyDisplayInfo ) { - state = state.copy(displayInfo = telephonyDisplayInfo) + val networkType = + if ( + telephonyDisplayInfo.overrideNetworkType == + OVERRIDE_NETWORK_TYPE_NONE + ) { + DefaultNetworkType(telephonyDisplayInfo.networkType) + } else { + OverrideNetworkType(telephonyDisplayInfo.overrideNetworkType) + } + + state = state.copy(resolvedNetworkType = networkType) trySend(state) } } @@ -202,6 +267,7 @@ constructor( subIdFlowCache.remove(subId) } } + .onEach { logger.logOutputChange("mobileSubscriptionModel", it.toString()) } .stateIn(scope, SharingStarted.WhileSubscribed(), state) } diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/pipeline/mobile/domain/interactor/MobileIconInteractor.kt b/packages/SystemUI/src/com/android/systemui/statusbar/pipeline/mobile/domain/interactor/MobileIconInteractor.kt index 40fe0f3e8fe09..e6d9993734d9c 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/pipeline/mobile/domain/interactor/MobileIconInteractor.kt +++ b/packages/SystemUI/src/com/android/systemui/statusbar/pipeline/mobile/domain/interactor/MobileIconInteractor.kt @@ -17,32 +17,56 @@ package com.android.systemui.statusbar.pipeline.mobile.domain.interactor import android.telephony.CarrierConfigManager -import com.android.settingslib.SignalIcon -import com.android.settingslib.mobile.TelephonyIcons +import com.android.settingslib.SignalIcon.MobileIconGroup +import com.android.systemui.statusbar.pipeline.mobile.data.model.DefaultNetworkType import com.android.systemui.statusbar.pipeline.mobile.data.model.MobileSubscriptionModel +import com.android.systemui.statusbar.pipeline.mobile.data.model.OverrideNetworkType +import com.android.systemui.statusbar.pipeline.mobile.util.MobileMappingsProxy import com.android.systemui.util.CarrierConfigTracker import kotlinx.coroutines.flow.Flow +import kotlinx.coroutines.flow.combine import kotlinx.coroutines.flow.flowOf import kotlinx.coroutines.flow.map interface MobileIconInteractor { - /** Identifier for RAT type indicator */ - val iconGroup: Flow + /** Observable for RAT type (network type) indicator */ + val networkTypeIconGroup: Flow + /** True if this line of service is emergency-only */ val isEmergencyOnly: Flow + /** Int describing the connection strength. 0-4 OR 1-5. See [numberOfLevels] */ val level: Flow + /** Based on [CarrierConfigManager.KEY_INFLATE_SIGNAL_STRENGTH_BOOL], either 4 or 5 */ val numberOfLevels: Flow + /** True when we want to draw an icon that makes room for the exclamation mark */ val cutOut: Flow } /** Interactor for a single mobile connection. This connection _should_ have one subscription ID */ class MobileIconInteractorImpl( + defaultMobileIconMapping: Flow>, + defaultMobileIconGroup: Flow, + mobileMappingsProxy: MobileMappingsProxy, mobileStatusInfo: Flow, ) : MobileIconInteractor { - override val iconGroup: Flow = flowOf(TelephonyIcons.THREE_G) + /** Observable for the current RAT indicator icon ([MobileIconGroup]) */ + override val networkTypeIconGroup: Flow = + combine( + mobileStatusInfo, + defaultMobileIconMapping, + defaultMobileIconGroup, + ) { info, mapping, defaultGroup -> + val lookupKey = + when (val resolved = info.resolvedNetworkType) { + is DefaultNetworkType -> mobileMappingsProxy.toIconKey(resolved.type) + is OverrideNetworkType -> mobileMappingsProxy.toIconKeyOverride(resolved.type) + } + mapping[lookupKey] ?: defaultGroup + } + override val isEmergencyOnly: Flow = mobileStatusInfo.map { it.isEmergencyOnly } override val level: Flow = diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/pipeline/mobile/domain/interactor/MobileIconsInteractor.kt b/packages/SystemUI/src/com/android/systemui/statusbar/pipeline/mobile/domain/interactor/MobileIconsInteractor.kt index 8e67e19f3e355..e401a9c78cf27 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/pipeline/mobile/domain/interactor/MobileIconsInteractor.kt +++ b/packages/SystemUI/src/com/android/systemui/statusbar/pipeline/mobile/domain/interactor/MobileIconsInteractor.kt @@ -19,29 +19,52 @@ package com.android.systemui.statusbar.pipeline.mobile.domain.interactor import android.telephony.CarrierConfigManager import android.telephony.SubscriptionInfo import android.telephony.SubscriptionManager +import com.android.settingslib.SignalIcon.MobileIconGroup +import com.android.settingslib.mobile.TelephonyIcons import com.android.systemui.dagger.SysUISingleton +import com.android.systemui.dagger.qualifiers.Application import com.android.systemui.statusbar.pipeline.mobile.data.model.MobileSubscriptionModel import com.android.systemui.statusbar.pipeline.mobile.data.repository.MobileSubscriptionRepository import com.android.systemui.statusbar.pipeline.mobile.data.repository.UserSetupRepository +import com.android.systemui.statusbar.pipeline.mobile.util.MobileMappingsProxy import com.android.systemui.util.CarrierConfigTracker import javax.inject.Inject +import kotlinx.coroutines.CoroutineScope import kotlinx.coroutines.flow.Flow +import kotlinx.coroutines.flow.SharingStarted +import kotlinx.coroutines.flow.StateFlow import kotlinx.coroutines.flow.combine +import kotlinx.coroutines.flow.map +import kotlinx.coroutines.flow.stateIn /** - * Business layer logic for mobile subscription icons + * Business layer logic for the set of mobile subscription icons. * - * Mobile indicators represent the UI for the (potentially filtered) list of [SubscriptionInfo]s - * that the system knows about. They obey policy that depends on OEM, carrier, and locale configs + * This interactor represents known set of mobile subscriptions (represented by [SubscriptionInfo]). + * The list of subscriptions is filtered based on the opportunistic flags on the infos. + * + * It provides the default mapping between the telephony display info and the icon group that + * represents each RAT (LTE, 3G, etc.), as well as can produce an interactor for each individual + * icon */ +interface MobileIconsInteractor { + val filteredSubscriptions: Flow> + val defaultMobileIconMapping: Flow> + val defaultMobileIconGroup: Flow + val isUserSetup: Flow + fun createMobileConnectionInteractorForSubId(subId: Int): MobileIconInteractor +} + @SysUISingleton -class MobileIconsInteractor +class MobileIconsInteractorImpl @Inject constructor( private val mobileSubscriptionRepo: MobileSubscriptionRepository, private val carrierConfigTracker: CarrierConfigTracker, + private val mobileMappingsProxy: MobileMappingsProxy, userSetupRepo: UserSetupRepository, -) { + @Application private val scope: CoroutineScope, +) : MobileIconsInteractor { private val activeMobileDataSubscriptionId = mobileSubscriptionRepo.activeMobileDataSubscriptionId @@ -61,7 +84,7 @@ constructor( * [CarrierConfigManager.KEY_ALWAYS_SHOW_PRIMARY_SIGNAL_BAR_IN_OPPORTUNISTIC_NETWORK_BOOLEAN], * and by checking which subscription is opportunistic, or which one is active. */ - val filteredSubscriptions: Flow> = + override val filteredSubscriptions: Flow> = combine(unfilteredSubscriptions, activeMobileDataSubscriptionId) { unfilteredSubs, activeId -> // Based on the old logic, @@ -92,11 +115,31 @@ constructor( } } - val isUserSetup: Flow = userSetupRepo.isUserSetupFlow + /** + * Mapping from network type to [MobileIconGroup] using the config generated for the default + * subscription Id. This mapping is the same for every subscription. + */ + override val defaultMobileIconMapping: StateFlow> = + mobileSubscriptionRepo.defaultDataSubRatConfig + .map { mobileMappingsProxy.mapIconSets(it) } + .stateIn(scope, SharingStarted.WhileSubscribed(), initialValue = mapOf()) + + /** If there is no mapping in [defaultMobileIconMapping], then use this default icon group */ + override val defaultMobileIconGroup: StateFlow = + mobileSubscriptionRepo.defaultDataSubRatConfig + .map { mobileMappingsProxy.getDefaultIcons(it) } + .stateIn(scope, SharingStarted.WhileSubscribed(), initialValue = TelephonyIcons.G) + + override val isUserSetup: Flow = userSetupRepo.isUserSetupFlow /** Vends out new [MobileIconInteractor] for a particular subId */ - fun createMobileConnectionInteractorForSubId(subId: Int): MobileIconInteractor = - MobileIconInteractorImpl(mobileSubscriptionFlowForSubId(subId)) + override fun createMobileConnectionInteractorForSubId(subId: Int): MobileIconInteractor = + MobileIconInteractorImpl( + defaultMobileIconMapping, + defaultMobileIconGroup, + mobileMappingsProxy, + mobileSubscriptionFlowForSubId(subId), + ) /** * Create a new flow for a given subscription ID, which usually maps 1:1 with mobile connections diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/pipeline/mobile/ui/binder/MobileIconBinder.kt b/packages/SystemUI/src/com/android/systemui/statusbar/pipeline/mobile/ui/binder/MobileIconBinder.kt index 1405b050234bb..67ea139271fcb 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/pipeline/mobile/ui/binder/MobileIconBinder.kt +++ b/packages/SystemUI/src/com/android/systemui/statusbar/pipeline/mobile/ui/binder/MobileIconBinder.kt @@ -17,6 +17,8 @@ package com.android.systemui.statusbar.pipeline.mobile.ui.binder import android.content.res.ColorStateList +import android.view.View.GONE +import android.view.View.VISIBLE import android.view.ViewGroup import android.widget.ImageView import androidx.core.view.isVisible @@ -24,6 +26,7 @@ import androidx.lifecycle.Lifecycle import androidx.lifecycle.repeatOnLifecycle import com.android.settingslib.graph.SignalDrawable import com.android.systemui.R +import com.android.systemui.common.ui.binder.IconViewBinder import com.android.systemui.lifecycle.repeatWhenAttached import com.android.systemui.statusbar.pipeline.mobile.ui.viewmodel.MobileIconViewModel import kotlinx.coroutines.flow.collect @@ -37,6 +40,7 @@ object MobileIconBinder { view: ViewGroup, viewModel: MobileIconViewModel, ) { + val networkTypeView = view.requireViewById(R.id.mobile_type) val iconView = view.requireViewById(R.id.mobile_signal) val mobileDrawable = SignalDrawable(view.context).also { iconView.setImageDrawable(it) } @@ -52,10 +56,20 @@ object MobileIconBinder { } } + // Set the network type icon + launch { + viewModel.networkTypeIcon.distinctUntilChanged().collect { dataTypeId -> + dataTypeId?.let { IconViewBinder.bind(dataTypeId, networkTypeView) } + networkTypeView.visibility = if (dataTypeId != null) VISIBLE else GONE + } + } + // Set the tint launch { viewModel.tint.collect { tint -> - iconView.imageTintList = ColorStateList.valueOf(tint) + val tintList = ColorStateList.valueOf(tint) + iconView.imageTintList = tintList + networkTypeView.imageTintList = tintList } } } diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/pipeline/mobile/ui/viewmodel/MobileIconViewModel.kt b/packages/SystemUI/src/com/android/systemui/statusbar/pipeline/mobile/ui/viewmodel/MobileIconViewModel.kt index cfabeba8432ce..cc8f6dd085853 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/pipeline/mobile/ui/viewmodel/MobileIconViewModel.kt +++ b/packages/SystemUI/src/com/android/systemui/statusbar/pipeline/mobile/ui/viewmodel/MobileIconViewModel.kt @@ -18,6 +18,8 @@ package com.android.systemui.statusbar.pipeline.mobile.ui.viewmodel import android.graphics.Color import com.android.settingslib.graph.SignalDrawable +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.ConnectivityPipelineLogger @@ -26,6 +28,7 @@ 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 /** * View model for the state of a single mobile icon. Each [MobileIconViewModel] will keep watch over @@ -54,5 +57,15 @@ constructor( .distinctUntilChanged() .logOutputChange(logger, "iconId($subscriptionId)") + /** The RAT icon (LTE, 3G, 5G, etc) to be displayed. Null if we shouldn't show anything */ + var networkTypeIcon: Flow = + iconInteractor.networkTypeIconGroup.map { + val desc = + if (it.dataContentDescription != 0) + ContentDescription.Resource(it.dataContentDescription) + else null + Icon.Resource(it.dataType, desc) + } + var tint: Flow = flowOf(Color.CYAN) } diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/pipeline/mobile/util/MobileMappings.kt b/packages/SystemUI/src/com/android/systemui/statusbar/pipeline/mobile/util/MobileMappings.kt new file mode 100644 index 0000000000000..60bd0383f8c72 --- /dev/null +++ b/packages/SystemUI/src/com/android/systemui/statusbar/pipeline/mobile/util/MobileMappings.kt @@ -0,0 +1,52 @@ +/* + * 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.util + +import android.telephony.Annotation.NetworkType +import android.telephony.TelephonyDisplayInfo +import com.android.settingslib.SignalIcon.MobileIconGroup +import com.android.settingslib.mobile.MobileMappings +import com.android.settingslib.mobile.MobileMappings.Config +import javax.inject.Inject + +/** + * [MobileMappings] owns the logic on creating the map from [TelephonyDisplayInfo] to + * [MobileIconGroup]. It creates that hash map and also manages the creation of lookup keys. This + * interface allows us to proxy those calls to the static java methods in SettingsLib and also fake + * them out in tests + */ +interface MobileMappingsProxy { + fun mapIconSets(config: Config): Map + fun getDefaultIcons(config: Config): MobileIconGroup + fun toIconKey(@NetworkType networkType: Int): String + fun toIconKeyOverride(@NetworkType networkType: Int): String +} + +/** Injectable wrapper class for [MobileMappings] */ +class MobileMappingsProxyImpl @Inject constructor() : MobileMappingsProxy { + override fun mapIconSets(config: Config): Map = + MobileMappings.mapIconSets(config) + + override fun getDefaultIcons(config: Config): MobileIconGroup = + MobileMappings.getDefaultIcons(config) + + override fun toIconKey(@NetworkType networkType: Int): String = + MobileMappings.toIconKey(networkType) + + override fun toIconKeyOverride(networkType: Int): String = + MobileMappings.toDisplayIconKey(networkType) +} diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/pipeline/mobile/data/repository/FakeMobileSubscriptionRepository.kt b/packages/SystemUI/tests/src/com/android/systemui/statusbar/pipeline/mobile/data/repository/FakeMobileSubscriptionRepository.kt index 0d15268830230..d5137a0e939d0 100644 --- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/pipeline/mobile/data/repository/FakeMobileSubscriptionRepository.kt +++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/pipeline/mobile/data/repository/FakeMobileSubscriptionRepository.kt @@ -18,6 +18,7 @@ package com.android.systemui.statusbar.pipeline.mobile.data.repository import android.telephony.SubscriptionInfo import android.telephony.SubscriptionManager +import com.android.settingslib.mobile.MobileMappings.Config import com.android.systemui.statusbar.pipeline.mobile.data.model.MobileSubscriptionModel import kotlinx.coroutines.flow.Flow import kotlinx.coroutines.flow.MutableStateFlow @@ -30,6 +31,9 @@ class FakeMobileSubscriptionRepository : MobileSubscriptionRepository { MutableStateFlow(SubscriptionManager.INVALID_SUBSCRIPTION_ID) override val activeMobileDataSubscriptionId = _activeMobileDataSubscriptionId + private val _defaultDataSubRatConfig = MutableStateFlow(Config()) + override val defaultDataSubRatConfig = _defaultDataSubRatConfig + private val subIdFlows = mutableMapOf>() override fun getFlowForSubId(subId: Int): Flow { return subIdFlows[subId] @@ -40,6 +44,10 @@ class FakeMobileSubscriptionRepository : MobileSubscriptionRepository { _subscriptionsFlow.value = subs } + fun setDefaultDataSubRatConfig(config: Config) { + _defaultDataSubRatConfig.value = config + } + fun setActiveMobileDataSubscriptionId(subId: Int) { _activeMobileDataSubscriptionId.value = subId } diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/pipeline/mobile/data/repository/MobileSubscriptionRepositoryTest.kt b/packages/SystemUI/tests/src/com/android/systemui/statusbar/pipeline/mobile/data/repository/MobileSubscriptionRepositoryTest.kt index 316b795ac9495..31203b3147eb1 100644 --- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/pipeline/mobile/data/repository/MobileSubscriptionRepositoryTest.kt +++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/pipeline/mobile/data/repository/MobileSubscriptionRepositoryTest.kt @@ -30,25 +30,36 @@ import android.telephony.TelephonyCallback.DisplayInfoListener import android.telephony.TelephonyCallback.ServiceStateListener import android.telephony.TelephonyCallback.SignalStrengthsListener import android.telephony.TelephonyDisplayInfo +import android.telephony.TelephonyDisplayInfo.OVERRIDE_NETWORK_TYPE_LTE_CA import android.telephony.TelephonyManager +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.broadcast.BroadcastDispatcher +import com.android.systemui.statusbar.pipeline.mobile.data.model.DefaultNetworkType import com.android.systemui.statusbar.pipeline.mobile.data.model.MobileSubscriptionModel +import com.android.systemui.statusbar.pipeline.mobile.data.model.OverrideNetworkType +import com.android.systemui.statusbar.pipeline.mobile.util.FakeMobileMappingsProxy +import com.android.systemui.statusbar.pipeline.shared.ConnectivityPipelineLogger import com.android.systemui.util.mockito.any import com.android.systemui.util.mockito.argumentCaptor import com.android.systemui.util.mockito.mock +import com.android.systemui.util.mockito.nullable import com.android.systemui.util.mockito.whenever import com.google.common.truth.Truth.assertThat import kotlinx.coroutines.CoroutineScope import kotlinx.coroutines.Dispatchers import kotlinx.coroutines.ExperimentalCoroutinesApi import kotlinx.coroutines.cancel +import kotlinx.coroutines.flow.flowOf import kotlinx.coroutines.flow.launchIn import kotlinx.coroutines.flow.onEach import kotlinx.coroutines.runBlocking import org.junit.After import org.junit.Before import org.junit.Test +import org.mockito.ArgumentMatchers import org.mockito.Mock import org.mockito.Mockito.verify import org.mockito.MockitoAnnotations @@ -61,16 +72,33 @@ class MobileSubscriptionRepositoryTest : SysuiTestCase() { @Mock private lateinit var subscriptionManager: SubscriptionManager @Mock private lateinit var telephonyManager: TelephonyManager + @Mock private lateinit var logger: ConnectivityPipelineLogger + @Mock private lateinit var broadcastDispatcher: BroadcastDispatcher + private val scope = CoroutineScope(IMMEDIATE) + private val mobileMappings = FakeMobileMappingsProxy() @Before fun setUp() { MockitoAnnotations.initMocks(this) + whenever( + broadcastDispatcher.broadcastFlow( + any(), + nullable(), + ArgumentMatchers.anyInt(), + nullable(), + ) + ) + .thenReturn(flowOf(Unit)) underTest = MobileSubscriptionRepositoryImpl( subscriptionManager, telephonyManager, + logger, + broadcastDispatcher, + context, + mobileMappings, IMMEDIATE, scope, ) @@ -266,7 +294,23 @@ class MobileSubscriptionRepositoryTest : SysuiTestCase() { } @Test - fun testFlowForSubId_displayInfo() = + fun testFlowForSubId_defaultNetworkType() = + runBlocking(IMMEDIATE) { + whenever(telephonyManager.createForSubscriptionId(any())).thenReturn(telephonyManager) + + var latest: MobileSubscriptionModel? = null + val job = underTest.getFlowForSubId(SUB_1_ID).onEach { latest = it }.launchIn(this) + + val type = NETWORK_TYPE_UNKNOWN + val expected = DefaultNetworkType(type) + + assertThat(latest?.resolvedNetworkType).isEqualTo(expected) + + job.cancel() + } + + @Test + fun testFlowForSubId_networkTypeUpdates_default() = runBlocking(IMMEDIATE) { whenever(telephonyManager.createForSubscriptionId(any())).thenReturn(telephonyManager) @@ -274,10 +318,34 @@ class MobileSubscriptionRepositoryTest : SysuiTestCase() { val job = underTest.getFlowForSubId(SUB_1_ID).onEach { latest = it }.launchIn(this) val callback = getTelephonyCallbackForType() - val ti = mock() + val type = NETWORK_TYPE_LTE + val expected = DefaultNetworkType(type) + val ti = mock().also { whenever(it.networkType).thenReturn(type) } callback.onDisplayInfoChanged(ti) - assertThat(latest?.displayInfo).isEqualTo(ti) + assertThat(latest?.resolvedNetworkType).isEqualTo(expected) + + job.cancel() + } + + @Test + fun testFlowForSubId_networkTypeUpdates_override() = + runBlocking(IMMEDIATE) { + whenever(telephonyManager.createForSubscriptionId(any())).thenReturn(telephonyManager) + + var latest: MobileSubscriptionModel? = null + val job = underTest.getFlowForSubId(SUB_1_ID).onEach { latest = it }.launchIn(this) + + val callback = getTelephonyCallbackForType() + val type = OVERRIDE_NETWORK_TYPE_LTE_CA + val expected = OverrideNetworkType(type) + val ti = + mock().also { + whenever(it.overrideNetworkType).thenReturn(type) + } + callback.onDisplayInfoChanged(ti) + + assertThat(latest?.resolvedNetworkType).isEqualTo(expected) job.cancel() } diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/pipeline/mobile/domain/interactor/FakeMobileIconInteractor.kt b/packages/SystemUI/tests/src/com/android/systemui/statusbar/pipeline/mobile/domain/interactor/FakeMobileIconInteractor.kt index 8ec68f36a837e..cd4dbebcc35c5 100644 --- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/pipeline/mobile/domain/interactor/FakeMobileIconInteractor.kt +++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/pipeline/mobile/domain/interactor/FakeMobileIconInteractor.kt @@ -23,7 +23,7 @@ import kotlinx.coroutines.flow.MutableStateFlow class FakeMobileIconInteractor : MobileIconInteractor { private val _iconGroup = MutableStateFlow(TelephonyIcons.UNKNOWN) - override val iconGroup = _iconGroup + override val networkTypeIconGroup = _iconGroup private val _isEmergencyOnly = MutableStateFlow(false) override val isEmergencyOnly = _isEmergencyOnly diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/pipeline/mobile/domain/interactor/FakeMobileIconsInteractor.kt b/packages/SystemUI/tests/src/com/android/systemui/statusbar/pipeline/mobile/domain/interactor/FakeMobileIconsInteractor.kt new file mode 100644 index 0000000000000..2bd228603cb01 --- /dev/null +++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/pipeline/mobile/domain/interactor/FakeMobileIconsInteractor.kt @@ -0,0 +1,75 @@ +/* + * 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.domain.interactor + +import android.telephony.SubscriptionInfo +import android.telephony.TelephonyDisplayInfo.OVERRIDE_NETWORK_TYPE_LTE_ADVANCED_PRO +import android.telephony.TelephonyManager.NETWORK_TYPE_GSM +import android.telephony.TelephonyManager.NETWORK_TYPE_LTE +import android.telephony.TelephonyManager.NETWORK_TYPE_UMTS +import com.android.settingslib.SignalIcon.MobileIconGroup +import com.android.settingslib.mobile.TelephonyIcons +import com.android.systemui.statusbar.pipeline.mobile.util.MobileMappingsProxy +import kotlinx.coroutines.flow.MutableStateFlow + +class FakeMobileIconsInteractor(private val mobileMappings: MobileMappingsProxy) : + MobileIconsInteractor { + val THREE_G_KEY = mobileMappings.toIconKey(THREE_G) + val LTE_KEY = mobileMappings.toIconKey(LTE) + val FOUR_G_KEY = mobileMappings.toIconKey(FOUR_G) + val FIVE_G_OVERRIDE_KEY = mobileMappings.toIconKeyOverride(FIVE_G_OVERRIDE) + + /** + * To avoid a reliance on [MobileMappings], we'll build a simpler map from network type to + * mobile icon. See TelephonyManager.NETWORK_TYPES for a list of types and [TelephonyIcons] for + * the exhaustive set of icons + */ + val TEST_MAPPING: Map = + mapOf( + THREE_G_KEY to TelephonyIcons.THREE_G, + LTE_KEY to TelephonyIcons.LTE, + FOUR_G_KEY to TelephonyIcons.FOUR_G, + FIVE_G_OVERRIDE_KEY to TelephonyIcons.NR_5G, + ) + + private val _filteredSubscriptions = MutableStateFlow>(listOf()) + override val filteredSubscriptions = _filteredSubscriptions + + private val _defaultMobileIconMapping = MutableStateFlow(TEST_MAPPING) + override val defaultMobileIconMapping = _defaultMobileIconMapping + + private val _defaultMobileIconGroup = MutableStateFlow(DEFAULT_ICON) + override val defaultMobileIconGroup = _defaultMobileIconGroup + + private val _isUserSetup = MutableStateFlow(true) + override val isUserSetup = _isUserSetup + + /** Always returns a new fake interactor */ + override fun createMobileConnectionInteractorForSubId(subId: Int): MobileIconInteractor { + return FakeMobileIconInteractor() + } + + companion object { + val DEFAULT_ICON = TelephonyIcons.G + + // Use [MobileMappings] to define some simple definitions + const val THREE_G = NETWORK_TYPE_GSM + const val LTE = NETWORK_TYPE_LTE + const val FOUR_G = NETWORK_TYPE_UMTS + const val FIVE_G_OVERRIDE = OVERRIDE_NETWORK_TYPE_LTE_ADVANCED_PRO + } +} diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/pipeline/mobile/domain/interactor/MobileIconInteractorTest.kt b/packages/SystemUI/tests/src/com/android/systemui/statusbar/pipeline/mobile/domain/interactor/MobileIconInteractorTest.kt index 2f07d9cb38312..99a7f6a2af7f1 100644 --- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/pipeline/mobile/domain/interactor/MobileIconInteractorTest.kt +++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/pipeline/mobile/domain/interactor/MobileIconInteractorTest.kt @@ -18,10 +18,19 @@ package com.android.systemui.statusbar.pipeline.mobile.domain.interactor import android.telephony.CellSignalStrength import android.telephony.SubscriptionInfo +import android.telephony.TelephonyManager.NETWORK_TYPE_UNKNOWN import androidx.test.filters.SmallTest +import com.android.settingslib.SignalIcon.MobileIconGroup +import com.android.settingslib.mobile.TelephonyIcons import com.android.systemui.SysuiTestCase +import com.android.systemui.statusbar.pipeline.mobile.data.model.DefaultNetworkType import com.android.systemui.statusbar.pipeline.mobile.data.model.MobileSubscriptionModel +import com.android.systemui.statusbar.pipeline.mobile.data.model.OverrideNetworkType import com.android.systemui.statusbar.pipeline.mobile.data.repository.FakeMobileSubscriptionRepository +import com.android.systemui.statusbar.pipeline.mobile.domain.interactor.FakeMobileIconsInteractor.Companion.FIVE_G_OVERRIDE +import com.android.systemui.statusbar.pipeline.mobile.domain.interactor.FakeMobileIconsInteractor.Companion.FOUR_G +import com.android.systemui.statusbar.pipeline.mobile.domain.interactor.FakeMobileIconsInteractor.Companion.THREE_G +import com.android.systemui.statusbar.pipeline.mobile.util.FakeMobileMappingsProxy import com.android.systemui.util.mockito.mock import com.android.systemui.util.mockito.whenever import com.google.common.truth.Truth.assertThat @@ -29,6 +38,7 @@ import kotlinx.coroutines.Dispatchers import kotlinx.coroutines.flow.launchIn import kotlinx.coroutines.flow.onEach import kotlinx.coroutines.runBlocking +import kotlinx.coroutines.yield import org.junit.Before import org.junit.Test @@ -36,11 +46,19 @@ import org.junit.Test class MobileIconInteractorTest : SysuiTestCase() { private lateinit var underTest: MobileIconInteractor private val mobileSubscriptionRepository = FakeMobileSubscriptionRepository() + private val mobileMappingsProxy = FakeMobileMappingsProxy() + private val mobileIconsInteractor = FakeMobileIconsInteractor(mobileMappingsProxy) private val sub1Flow = mobileSubscriptionRepository.getFlowForSubId(SUB_1_ID) @Before fun setUp() { - underTest = MobileIconInteractorImpl(sub1Flow) + underTest = + MobileIconInteractorImpl( + mobileIconsInteractor.defaultMobileIconMapping, + mobileIconsInteractor.defaultMobileIconGroup, + mobileMappingsProxy, + sub1Flow, + ) } @Test @@ -114,6 +132,80 @@ class MobileIconInteractorTest : SysuiTestCase() { job.cancel() } + @Test + fun iconGroup_three_g() = + runBlocking(IMMEDIATE) { + mobileSubscriptionRepository.setMobileSubscriptionModel( + MobileSubscriptionModel(resolvedNetworkType = DefaultNetworkType(THREE_G)), + SUB_1_ID + ) + + var latest: MobileIconGroup? = null + val job = underTest.networkTypeIconGroup.onEach { latest = it }.launchIn(this) + + assertThat(latest).isEqualTo(TelephonyIcons.THREE_G) + + job.cancel() + } + + @Test + fun iconGroup_updates_on_change() = + runBlocking(IMMEDIATE) { + mobileSubscriptionRepository.setMobileSubscriptionModel( + MobileSubscriptionModel(resolvedNetworkType = DefaultNetworkType(THREE_G)), + SUB_1_ID + ) + + var latest: MobileIconGroup? = null + val job = underTest.networkTypeIconGroup.onEach { latest = it }.launchIn(this) + + mobileSubscriptionRepository.setMobileSubscriptionModel( + MobileSubscriptionModel( + resolvedNetworkType = DefaultNetworkType(FOUR_G), + ), + SUB_1_ID + ) + yield() + + assertThat(latest).isEqualTo(TelephonyIcons.FOUR_G) + + job.cancel() + } + + @Test + fun iconGroup_5g_override_type() = + runBlocking(IMMEDIATE) { + mobileSubscriptionRepository.setMobileSubscriptionModel( + MobileSubscriptionModel(resolvedNetworkType = OverrideNetworkType(FIVE_G_OVERRIDE)), + SUB_1_ID + ) + + var latest: MobileIconGroup? = null + val job = underTest.networkTypeIconGroup.onEach { latest = it }.launchIn(this) + + assertThat(latest).isEqualTo(TelephonyIcons.NR_5G) + + job.cancel() + } + + @Test + fun iconGroup_default_if_no_lookup() = + runBlocking(IMMEDIATE) { + mobileSubscriptionRepository.setMobileSubscriptionModel( + MobileSubscriptionModel( + resolvedNetworkType = DefaultNetworkType(NETWORK_TYPE_UNKNOWN), + ), + SUB_1_ID + ) + + var latest: MobileIconGroup? = null + val job = underTest.networkTypeIconGroup.onEach { latest = it }.launchIn(this) + + assertThat(latest).isEqualTo(FakeMobileIconsInteractor.DEFAULT_ICON) + + job.cancel() + } + companion object { private val IMMEDIATE = Dispatchers.Main.immediate diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/pipeline/mobile/domain/interactor/MobileIconsInteractorTest.kt b/packages/SystemUI/tests/src/com/android/systemui/statusbar/pipeline/mobile/domain/interactor/MobileIconsInteractorTest.kt index 89ad9cb9e51e6..ff8c1e2862a42 100644 --- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/pipeline/mobile/domain/interactor/MobileIconsInteractorTest.kt +++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/pipeline/mobile/domain/interactor/MobileIconsInteractorTest.kt @@ -21,10 +21,12 @@ import androidx.test.filters.SmallTest import com.android.systemui.SysuiTestCase import com.android.systemui.statusbar.pipeline.mobile.data.repository.FakeMobileSubscriptionRepository import com.android.systemui.statusbar.pipeline.mobile.data.repository.FakeUserSetupRepository +import com.android.systemui.statusbar.pipeline.mobile.util.FakeMobileMappingsProxy import com.android.systemui.util.CarrierConfigTracker import com.android.systemui.util.mockito.mock import com.android.systemui.util.mockito.whenever import com.google.common.truth.Truth.assertThat +import kotlinx.coroutines.CoroutineScope import kotlinx.coroutines.Dispatchers import kotlinx.coroutines.flow.launchIn import kotlinx.coroutines.flow.onEach @@ -40,6 +42,8 @@ class MobileIconsInteractorTest : SysuiTestCase() { private lateinit var underTest: MobileIconsInteractor private val userSetupRepository = FakeUserSetupRepository() private val subscriptionsRepository = FakeMobileSubscriptionRepository() + private val mobileMappingsProxy = FakeMobileMappingsProxy() + private val scope = CoroutineScope(IMMEDIATE) @Mock private lateinit var carrierConfigTracker: CarrierConfigTracker @@ -47,10 +51,12 @@ class MobileIconsInteractorTest : SysuiTestCase() { fun setUp() { MockitoAnnotations.initMocks(this) underTest = - MobileIconsInteractor( + MobileIconsInteractorImpl( subscriptionsRepository, carrierConfigTracker, + mobileMappingsProxy, userSetupRepository, + scope ) } diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/pipeline/mobile/util/FakeMobileMappingsProxy.kt b/packages/SystemUI/tests/src/com/android/systemui/statusbar/pipeline/mobile/util/FakeMobileMappingsProxy.kt new file mode 100644 index 0000000000000..6d8d902615de1 --- /dev/null +++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/pipeline/mobile/util/FakeMobileMappingsProxy.kt @@ -0,0 +1,46 @@ +/* + * 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.util + +import com.android.settingslib.SignalIcon.MobileIconGroup +import com.android.settingslib.mobile.MobileMappings.Config +import com.android.settingslib.mobile.TelephonyIcons + +class FakeMobileMappingsProxy : MobileMappingsProxy { + private var iconMap = mapOf() + private var defaultIcons = TelephonyIcons.THREE_G + + fun setIconMap(map: Map) { + iconMap = map + } + override fun mapIconSets(config: Config): Map = iconMap + fun getIconMap() = iconMap + + fun setDefaultIcons(group: MobileIconGroup) { + defaultIcons = group + } + override fun getDefaultIcons(config: Config): MobileIconGroup = defaultIcons + fun getDefaultIcons(): MobileIconGroup = defaultIcons + + override fun toIconKey(networkType: Int): String { + return networkType.toString() + } + + override fun toIconKeyOverride(networkType: Int): String { + return toIconKey(networkType) + "_override" + } +}