[Sb refactor] Mobile icon view model logging
Add logging to the mobile icon view models. In order to keep the logs from duplicating, this CL also adds: 1. The reuse of a single instance of the MobileIconsViewModel (top-level), which 2. Caches the individual MobileIconViewModel common implementations on a per-subscription basis. There is now a 1-1 correspondence of the common mobile icon view model implementations and the number of subscriptions. Also updated the MobileIconViewModelTest and LocationBasedMobileIconViewModelTest classes to use the TestScope method of running tests. Test: adb logcat | grep SbConnectivity Test: tests in tests/src/com/android/systemui/statusbar/pipeline/mobile/* Bug: 238425913 Change-Id: I5157cdcfb054d4629db0f9b11da8b583760ce471
This commit is contained in:
@@ -387,7 +387,7 @@ public interface StatusBarIconController {
|
||||
if (statusBarPipelineFlags.runNewMobileIconsBackend()) {
|
||||
// This starts the flow for the new pipeline, and will notify us of changes if
|
||||
// {@link StatusBarPipelineFlags#useNewMobileIcons} is also true.
|
||||
mMobileIconsViewModel = mobileUiAdapter.createMobileIconsViewModel();
|
||||
mMobileIconsViewModel = mobileUiAdapter.getMobileIconsViewModel();
|
||||
MobileIconsBinder.bind(mGroup, mMobileIconsViewModel);
|
||||
} else {
|
||||
mMobileIconsViewModel = null;
|
||||
|
||||
@@ -20,6 +20,7 @@ import android.telephony.SubscriptionInfo
|
||||
import android.telephony.SubscriptionManager
|
||||
import android.telephony.TelephonyCallback
|
||||
import android.telephony.TelephonyManager
|
||||
import com.android.systemui.log.table.TableLogBuffer
|
||||
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
|
||||
@@ -39,6 +40,13 @@ import kotlinx.coroutines.flow.StateFlow
|
||||
interface MobileConnectionRepository {
|
||||
/** The subscriptionId that this connection represents */
|
||||
val subId: Int
|
||||
|
||||
/**
|
||||
* The table log buffer created for this connection. Will have the name "MobileConnectionLog
|
||||
* [subId]"
|
||||
*/
|
||||
val tableLogBuffer: TableLogBuffer
|
||||
|
||||
/**
|
||||
* A flow that aggregates all necessary callbacks from [TelephonyCallback] into a single
|
||||
* listener + model.
|
||||
|
||||
@@ -24,6 +24,8 @@ import com.android.settingslib.SignalIcon
|
||||
import com.android.settingslib.mobile.MobileMappings
|
||||
import com.android.settingslib.mobile.TelephonyIcons
|
||||
import com.android.systemui.dagger.qualifiers.Application
|
||||
import com.android.systemui.log.table.TableLogBuffer
|
||||
import com.android.systemui.log.table.TableLogBufferFactory
|
||||
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
|
||||
@@ -60,6 +62,7 @@ constructor(
|
||||
private val dataSource: DemoModeMobileConnectionDataSource,
|
||||
@Application private val scope: CoroutineScope,
|
||||
context: Context,
|
||||
private val logFactory: TableLogBufferFactory,
|
||||
) : MobileConnectionsRepository {
|
||||
|
||||
private var demoCommandJob: Job? = null
|
||||
@@ -149,7 +152,16 @@ constructor(
|
||||
|
||||
override fun getRepoForSubId(subId: Int): DemoMobileConnectionRepository {
|
||||
return connectionRepoCache[subId]
|
||||
?: DemoMobileConnectionRepository(subId).also { connectionRepoCache[subId] = it }
|
||||
?: createDemoMobileConnectionRepo(subId).also { connectionRepoCache[subId] = it }
|
||||
}
|
||||
|
||||
private fun createDemoMobileConnectionRepo(subId: Int): DemoMobileConnectionRepository {
|
||||
val tableLogBuffer = logFactory.create("DemoMobileConnectionLog [$subId]", 100)
|
||||
|
||||
return DemoMobileConnectionRepository(
|
||||
subId,
|
||||
tableLogBuffer,
|
||||
)
|
||||
}
|
||||
|
||||
override val globalMobileDataSettingChangedEvent = MutableStateFlow(Unit)
|
||||
@@ -260,7 +272,10 @@ constructor(
|
||||
}
|
||||
}
|
||||
|
||||
class DemoMobileConnectionRepository(override val subId: Int) : MobileConnectionRepository {
|
||||
class DemoMobileConnectionRepository(
|
||||
override val subId: Int,
|
||||
override val tableLogBuffer: TableLogBuffer,
|
||||
) : MobileConnectionRepository {
|
||||
override val connectionInfo = MutableStateFlow(MobileConnectionModel())
|
||||
|
||||
override val dataEnabled = MutableStateFlow(true)
|
||||
|
||||
@@ -96,6 +96,8 @@ class MobileConnectionRepositoryImpl(
|
||||
|
||||
private val telephonyCallbackEvent = MutableSharedFlow<Unit>(extraBufferCapacity = 1)
|
||||
|
||||
override val tableLogBuffer: TableLogBuffer = mobileLogger
|
||||
|
||||
override val connectionInfo: StateFlow<MobileConnectionModel> = run {
|
||||
var state = MobileConnectionModel()
|
||||
conflatedCallbackFlow {
|
||||
|
||||
@@ -19,6 +19,7 @@ package com.android.systemui.statusbar.pipeline.mobile.domain.interactor
|
||||
import android.telephony.CarrierConfigManager
|
||||
import com.android.settingslib.SignalIcon.MobileIconGroup
|
||||
import com.android.systemui.dagger.qualifiers.Application
|
||||
import com.android.systemui.log.table.TableLogBuffer
|
||||
import com.android.systemui.statusbar.pipeline.mobile.data.model.DataConnectionState.Connected
|
||||
import com.android.systemui.statusbar.pipeline.mobile.data.model.NetworkNameModel
|
||||
import com.android.systemui.statusbar.pipeline.mobile.data.repository.MobileConnectionRepository
|
||||
@@ -35,6 +36,9 @@ import kotlinx.coroutines.flow.mapLatest
|
||||
import kotlinx.coroutines.flow.stateIn
|
||||
|
||||
interface MobileIconInteractor {
|
||||
/** The table log created for this connection */
|
||||
val tableLogBuffer: TableLogBuffer
|
||||
|
||||
/** The current mobile data activity */
|
||||
val activity: Flow<DataActivityModel>
|
||||
|
||||
@@ -97,6 +101,8 @@ class MobileIconInteractorImpl(
|
||||
) : MobileIconInteractor {
|
||||
private val connectionInfo = connectionRepository.connectionInfo
|
||||
|
||||
override val tableLogBuffer: TableLogBuffer = connectionRepository.tableLogBuffer
|
||||
|
||||
override val activity = connectionInfo.mapLatest { it.dataActivityDirection }
|
||||
|
||||
override val isDataEnabled: StateFlow<Boolean> = connectionRepository.dataEnabled
|
||||
|
||||
@@ -20,7 +20,6 @@ import com.android.systemui.CoreStartable
|
||||
import com.android.systemui.dagger.SysUISingleton
|
||||
import com.android.systemui.dagger.qualifiers.Application
|
||||
import com.android.systemui.statusbar.phone.StatusBarIconController
|
||||
import com.android.systemui.statusbar.phone.StatusBarIconController.IconManager
|
||||
import com.android.systemui.statusbar.pipeline.StatusBarPipelineFlags
|
||||
import com.android.systemui.statusbar.pipeline.mobile.domain.interactor.MobileIconsInteractor
|
||||
import com.android.systemui.statusbar.pipeline.mobile.ui.viewmodel.MobileIconsViewModel
|
||||
@@ -70,6 +69,9 @@ constructor(
|
||||
private val mobileSubIdsState: StateFlow<List<Int>> =
|
||||
mobileSubIds.stateIn(scope, SharingStarted.WhileSubscribed(), listOf())
|
||||
|
||||
/** In order to keep the logs tame, we will reuse the same top-level mobile icons view model */
|
||||
val mobileIconsViewModel = iconsViewModelFactory.create(mobileSubIdsState)
|
||||
|
||||
override fun start() {
|
||||
// Only notify the icon controller if we want to *render* the new icons.
|
||||
// Note that this flow may still run if
|
||||
@@ -81,12 +83,4 @@ constructor(
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Create a MobileIconsViewModel for a given [IconManager], and bind it to to the manager's
|
||||
* lifecycle. This will start collecting on [mobileSubIdsState] and link our new pipeline with
|
||||
* the old view system.
|
||||
*/
|
||||
fun createMobileIconsViewModel(): MobileIconsViewModel =
|
||||
iconsViewModelFactory.create(mobileSubIdsState)
|
||||
}
|
||||
|
||||
@@ -18,7 +18,10 @@ package com.android.systemui.statusbar.pipeline.mobile.ui.viewmodel
|
||||
|
||||
import android.graphics.Color
|
||||
import com.android.systemui.statusbar.phone.StatusBarLocation
|
||||
import com.android.systemui.statusbar.pipeline.shared.ConnectivityPipelineLogger
|
||||
import com.android.systemui.statusbar.pipeline.shared.ConnectivityPipelineLogger.Companion.logOutputChange
|
||||
import kotlinx.coroutines.flow.Flow
|
||||
import kotlinx.coroutines.flow.distinctUntilChanged
|
||||
import kotlinx.coroutines.flow.flowOf
|
||||
|
||||
/**
|
||||
@@ -30,34 +33,50 @@ import kotlinx.coroutines.flow.flowOf
|
||||
*/
|
||||
abstract class LocationBasedMobileViewModel(
|
||||
val commonImpl: MobileIconViewModelCommon,
|
||||
val logger: ConnectivityPipelineLogger,
|
||||
) : MobileIconViewModelCommon by commonImpl {
|
||||
abstract val tint: Flow<Int>
|
||||
|
||||
companion object {
|
||||
fun viewModelForLocation(
|
||||
commonImpl: MobileIconViewModelCommon,
|
||||
logger: ConnectivityPipelineLogger,
|
||||
loc: StatusBarLocation,
|
||||
): LocationBasedMobileViewModel =
|
||||
when (loc) {
|
||||
StatusBarLocation.HOME -> HomeMobileIconViewModel(commonImpl)
|
||||
StatusBarLocation.KEYGUARD -> KeyguardMobileIconViewModel(commonImpl)
|
||||
StatusBarLocation.QS -> QsMobileIconViewModel(commonImpl)
|
||||
StatusBarLocation.HOME -> HomeMobileIconViewModel(commonImpl, logger)
|
||||
StatusBarLocation.KEYGUARD -> KeyguardMobileIconViewModel(commonImpl, logger)
|
||||
StatusBarLocation.QS -> QsMobileIconViewModel(commonImpl, logger)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
class HomeMobileIconViewModel(
|
||||
commonImpl: MobileIconViewModelCommon,
|
||||
) : MobileIconViewModelCommon, LocationBasedMobileViewModel(commonImpl) {
|
||||
override val tint: Flow<Int> = flowOf(Color.CYAN)
|
||||
logger: ConnectivityPipelineLogger,
|
||||
) : MobileIconViewModelCommon, LocationBasedMobileViewModel(commonImpl, logger) {
|
||||
override val tint: Flow<Int> =
|
||||
flowOf(Color.CYAN)
|
||||
.distinctUntilChanged()
|
||||
.logOutputChange(logger, "HOME tint(${commonImpl.subscriptionId})")
|
||||
}
|
||||
|
||||
class QsMobileIconViewModel(commonImpl: MobileIconViewModelCommon) :
|
||||
MobileIconViewModelCommon, LocationBasedMobileViewModel(commonImpl) {
|
||||
override val tint: Flow<Int> = flowOf(Color.GREEN)
|
||||
class QsMobileIconViewModel(
|
||||
commonImpl: MobileIconViewModelCommon,
|
||||
logger: ConnectivityPipelineLogger,
|
||||
) : MobileIconViewModelCommon, LocationBasedMobileViewModel(commonImpl, logger) {
|
||||
override val tint: Flow<Int> =
|
||||
flowOf(Color.GREEN)
|
||||
.distinctUntilChanged()
|
||||
.logOutputChange(logger, "QS tint(${commonImpl.subscriptionId})")
|
||||
}
|
||||
|
||||
class KeyguardMobileIconViewModel(commonImpl: MobileIconViewModelCommon) :
|
||||
MobileIconViewModelCommon, LocationBasedMobileViewModel(commonImpl) {
|
||||
override val tint: Flow<Int> = flowOf(Color.MAGENTA)
|
||||
class KeyguardMobileIconViewModel(
|
||||
commonImpl: MobileIconViewModelCommon,
|
||||
logger: ConnectivityPipelineLogger,
|
||||
) : MobileIconViewModelCommon, LocationBasedMobileViewModel(commonImpl, logger) {
|
||||
override val tint: Flow<Int> =
|
||||
flowOf(Color.MAGENTA)
|
||||
.distinctUntilChanged()
|
||||
.logOutputChange(logger, "KEYGUARD tint(${commonImpl.subscriptionId})")
|
||||
}
|
||||
|
||||
@@ -16,23 +16,27 @@
|
||||
|
||||
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.log.table.logDiffsForTable
|
||||
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.CoroutineScope
|
||||
import kotlinx.coroutines.ExperimentalCoroutinesApi
|
||||
import kotlinx.coroutines.flow.Flow
|
||||
import kotlinx.coroutines.flow.SharingStarted
|
||||
import kotlinx.coroutines.flow.StateFlow
|
||||
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
|
||||
import kotlinx.coroutines.flow.onEach
|
||||
import kotlinx.coroutines.flow.stateIn
|
||||
|
||||
/** Common interface for all of the location-based mobile icon view models. */
|
||||
interface MobileIconViewModelCommon {
|
||||
@@ -53,7 +57,12 @@ interface MobileIconViewModelCommon {
|
||||
* subscription's information.
|
||||
*
|
||||
* There will be exactly one [MobileIconViewModel] per filtered subscription offered from
|
||||
* [MobileIconsInteractor.filteredSubscriptions]
|
||||
* [MobileIconsInteractor.filteredSubscriptions].
|
||||
*
|
||||
* For the sake of keeping log spam in check, every flow funding the [MobileIconViewModelCommon]
|
||||
* interface is implemented as a [StateFlow]. This ensures that each location-based mobile icon view
|
||||
* model gets the exact same information, as well as allows us to log that unified state only once
|
||||
* per icon.
|
||||
*/
|
||||
@Suppress("EXPERIMENTAL_IS_NOT_ENABLED")
|
||||
@OptIn(ExperimentalCoroutinesApi::class)
|
||||
@@ -63,12 +72,14 @@ constructor(
|
||||
iconInteractor: MobileIconInteractor,
|
||||
logger: ConnectivityPipelineLogger,
|
||||
constants: ConnectivityConstants,
|
||||
scope: CoroutineScope,
|
||||
) : MobileIconViewModelCommon {
|
||||
/** Whether or not to show the error state of [SignalDrawable] */
|
||||
private val showExclamationMark: Flow<Boolean> =
|
||||
iconInteractor.isDefaultDataEnabled.mapLatest { !it }
|
||||
|
||||
override val iconId: Flow<Int> =
|
||||
override val iconId: Flow<Int> = run {
|
||||
val initial = SignalDrawable.getEmptyState(iconInteractor.numberOfLevels.value)
|
||||
combine(iconInteractor.level, iconInteractor.numberOfLevels, showExclamationMark) {
|
||||
level,
|
||||
numberOfLevels,
|
||||
@@ -76,31 +87,56 @@ constructor(
|
||||
SignalDrawable.getState(level, numberOfLevels, showExclamationMark)
|
||||
}
|
||||
.distinctUntilChanged()
|
||||
.logOutputChange(logger, "iconId($subscriptionId)")
|
||||
.logDiffsForTable(
|
||||
iconInteractor.tableLogBuffer,
|
||||
columnPrefix = "",
|
||||
columnName = "iconId",
|
||||
initialValue = initial,
|
||||
)
|
||||
.stateIn(scope, SharingStarted.WhileSubscribed(), initial)
|
||||
}
|
||||
|
||||
override val networkTypeIcon: Flow<Icon?> =
|
||||
combine(
|
||||
iconInteractor.networkTypeIconGroup,
|
||||
iconInteractor.isDataConnected,
|
||||
iconInteractor.isDataEnabled,
|
||||
iconInteractor.isDefaultConnectionFailed,
|
||||
iconInteractor.alwaysShowDataRatIcon,
|
||||
) { networkTypeIconGroup, dataConnected, dataEnabled, failedConnection, alwaysShow ->
|
||||
val desc =
|
||||
if (networkTypeIconGroup.dataContentDescription != 0)
|
||||
ContentDescription.Resource(networkTypeIconGroup.dataContentDescription)
|
||||
else null
|
||||
val icon = Icon.Resource(networkTypeIconGroup.dataType, desc)
|
||||
return@combine when {
|
||||
alwaysShow -> icon
|
||||
!dataConnected -> null
|
||||
!dataEnabled -> null
|
||||
failedConnection -> null
|
||||
else -> icon
|
||||
iconInteractor.networkTypeIconGroup,
|
||||
iconInteractor.isDataConnected,
|
||||
iconInteractor.isDataEnabled,
|
||||
iconInteractor.isDefaultConnectionFailed,
|
||||
iconInteractor.alwaysShowDataRatIcon,
|
||||
) { networkTypeIconGroup, dataConnected, dataEnabled, failedConnection, alwaysShow ->
|
||||
val desc =
|
||||
if (networkTypeIconGroup.dataContentDescription != 0)
|
||||
ContentDescription.Resource(networkTypeIconGroup.dataContentDescription)
|
||||
else null
|
||||
val icon = Icon.Resource(networkTypeIconGroup.dataType, desc)
|
||||
return@combine when {
|
||||
alwaysShow -> icon
|
||||
!dataConnected -> null
|
||||
!dataEnabled -> null
|
||||
failedConnection -> null
|
||||
else -> icon
|
||||
}
|
||||
}
|
||||
}
|
||||
.distinctUntilChanged()
|
||||
.onEach {
|
||||
// This is done as an onEach side effect since Icon is not Diffable (yet)
|
||||
iconInteractor.tableLogBuffer.logChange(
|
||||
prefix = "",
|
||||
columnName = "networkTypeIcon",
|
||||
value = it.toString(),
|
||||
)
|
||||
}
|
||||
.stateIn(scope, SharingStarted.WhileSubscribed(), null)
|
||||
|
||||
override val roaming: Flow<Boolean> = iconInteractor.isRoaming
|
||||
override val roaming: StateFlow<Boolean> =
|
||||
iconInteractor.isRoaming
|
||||
.logDiffsForTable(
|
||||
iconInteractor.tableLogBuffer,
|
||||
columnPrefix = "",
|
||||
columnName = "roaming",
|
||||
initialValue = false,
|
||||
)
|
||||
.stateIn(scope, SharingStarted.WhileSubscribed(), false)
|
||||
|
||||
private val activity: Flow<DataActivityModel?> =
|
||||
if (!constants.shouldShowActivityConfig) {
|
||||
@@ -109,10 +145,39 @@ constructor(
|
||||
iconInteractor.activity
|
||||
}
|
||||
|
||||
override val activityInVisible: Flow<Boolean> = activity.map { it?.hasActivityIn ?: false }
|
||||
override val activityOutVisible: Flow<Boolean> = activity.map { it?.hasActivityOut ?: false }
|
||||
override val activityContainerVisible: Flow<Boolean> =
|
||||
activity.map { it != null && (it.hasActivityIn || it.hasActivityOut) }
|
||||
override val activityInVisible: Flow<Boolean> =
|
||||
activity
|
||||
.map { it?.hasActivityIn ?: false }
|
||||
.distinctUntilChanged()
|
||||
.logDiffsForTable(
|
||||
iconInteractor.tableLogBuffer,
|
||||
columnPrefix = "",
|
||||
columnName = "activityInVisible",
|
||||
initialValue = false,
|
||||
)
|
||||
.stateIn(scope, SharingStarted.WhileSubscribed(), false)
|
||||
|
||||
val tint: Flow<Int> = flowOf(Color.CYAN)
|
||||
override val activityOutVisible: Flow<Boolean> =
|
||||
activity
|
||||
.map { it?.hasActivityOut ?: false }
|
||||
.distinctUntilChanged()
|
||||
.logDiffsForTable(
|
||||
iconInteractor.tableLogBuffer,
|
||||
columnPrefix = "",
|
||||
columnName = "activityOutVisible",
|
||||
initialValue = false,
|
||||
)
|
||||
.stateIn(scope, SharingStarted.WhileSubscribed(), false)
|
||||
|
||||
override val activityContainerVisible: Flow<Boolean> =
|
||||
activity
|
||||
.map { it != null && (it.hasActivityIn || it.hasActivityOut) }
|
||||
.distinctUntilChanged()
|
||||
.logDiffsForTable(
|
||||
iconInteractor.tableLogBuffer,
|
||||
columnPrefix = "",
|
||||
columnName = "activityContainerVisible",
|
||||
initialValue = false,
|
||||
)
|
||||
.stateIn(scope, SharingStarted.WhileSubscribed(), false)
|
||||
}
|
||||
|
||||
@@ -14,18 +14,19 @@
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
@file:OptIn(InternalCoroutinesApi::class)
|
||||
|
||||
package com.android.systemui.statusbar.pipeline.mobile.ui.viewmodel
|
||||
|
||||
import androidx.annotation.VisibleForTesting
|
||||
import com.android.systemui.dagger.qualifiers.Application
|
||||
import com.android.systemui.statusbar.phone.StatusBarLocation
|
||||
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
|
||||
import kotlinx.coroutines.CoroutineScope
|
||||
import kotlinx.coroutines.flow.StateFlow
|
||||
import kotlinx.coroutines.launch
|
||||
|
||||
/**
|
||||
* View model for describing the system's current mobile cellular connections. The result is a list
|
||||
@@ -39,18 +40,32 @@ constructor(
|
||||
private val interactor: MobileIconsInteractor,
|
||||
private val logger: ConnectivityPipelineLogger,
|
||||
private val constants: ConnectivityConstants,
|
||||
@Application private val scope: CoroutineScope,
|
||||
) {
|
||||
/** TODO: do we need to cache these? */
|
||||
@VisibleForTesting val mobileIconSubIdCache = mutableMapOf<Int, MobileIconViewModel>()
|
||||
|
||||
init {
|
||||
scope.launch { subscriptionIdsFlow.collect { removeInvalidModelsFromCache(it) } }
|
||||
}
|
||||
|
||||
fun viewModelForSub(subId: Int, location: StatusBarLocation): LocationBasedMobileViewModel {
|
||||
val common =
|
||||
MobileIconViewModel(
|
||||
subId,
|
||||
interactor.createMobileConnectionInteractorForSubId(subId),
|
||||
logger,
|
||||
constants,
|
||||
)
|
||||
mobileIconSubIdCache[subId]
|
||||
?: MobileIconViewModel(
|
||||
subId,
|
||||
interactor.createMobileConnectionInteractorForSubId(subId),
|
||||
logger,
|
||||
constants,
|
||||
scope,
|
||||
)
|
||||
.also { mobileIconSubIdCache[subId] = it }
|
||||
|
||||
return LocationBasedMobileViewModel.viewModelForLocation(common, location)
|
||||
return LocationBasedMobileViewModel.viewModelForLocation(common, logger, location)
|
||||
}
|
||||
|
||||
private fun removeInvalidModelsFromCache(subIds: List<Int>) {
|
||||
val subIdsToRemove = mobileIconSubIdCache.keys.filter { !subIds.contains(it) }
|
||||
subIdsToRemove.forEach { mobileIconSubIdCache.remove(it) }
|
||||
}
|
||||
|
||||
class Factory
|
||||
@@ -59,6 +74,7 @@ constructor(
|
||||
private val interactor: MobileIconsInteractor,
|
||||
private val logger: ConnectivityPipelineLogger,
|
||||
private val constants: ConnectivityConstants,
|
||||
@Application private val scope: CoroutineScope,
|
||||
) {
|
||||
fun create(subscriptionIdsFlow: StateFlow<List<Int>>): MobileIconsViewModel {
|
||||
return MobileIconsViewModel(
|
||||
@@ -66,6 +82,7 @@ constructor(
|
||||
interactor,
|
||||
logger,
|
||||
constants,
|
||||
scope,
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
@@ -16,12 +16,16 @@
|
||||
|
||||
package com.android.systemui.statusbar.pipeline.mobile.data.repository
|
||||
|
||||
import com.android.systemui.log.table.TableLogBuffer
|
||||
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
|
||||
class FakeMobileConnectionRepository(override val subId: Int) : MobileConnectionRepository {
|
||||
class FakeMobileConnectionRepository(
|
||||
override val subId: Int,
|
||||
override val tableLogBuffer: TableLogBuffer,
|
||||
) : MobileConnectionRepository {
|
||||
private val _connectionInfo = MutableStateFlow(MobileConnectionModel())
|
||||
override val connectionInfo = _connectionInfo
|
||||
|
||||
|
||||
@@ -22,14 +22,17 @@ import android.telephony.TelephonyManager
|
||||
import com.android.settingslib.SignalIcon
|
||||
import com.android.settingslib.mobile.MobileMappings
|
||||
import com.android.settingslib.mobile.TelephonyIcons
|
||||
import com.android.systemui.log.table.TableLogBuffer
|
||||
import com.android.systemui.statusbar.pipeline.mobile.data.model.MobileConnectivityModel
|
||||
import com.android.systemui.statusbar.pipeline.mobile.data.model.SubscriptionModel
|
||||
import com.android.systemui.statusbar.pipeline.mobile.util.MobileMappingsProxy
|
||||
import kotlinx.coroutines.flow.MutableStateFlow
|
||||
|
||||
// TODO(b/261632894): remove this in favor of the real impl or DemoMobileConnectionsRepository
|
||||
class FakeMobileConnectionsRepository(mobileMappings: MobileMappingsProxy) :
|
||||
MobileConnectionsRepository {
|
||||
class FakeMobileConnectionsRepository(
|
||||
mobileMappings: MobileMappingsProxy,
|
||||
val tableLogBuffer: TableLogBuffer,
|
||||
) : MobileConnectionsRepository {
|
||||
val GSM_KEY = mobileMappings.toIconKey(GSM)
|
||||
val LTE_KEY = mobileMappings.toIconKey(LTE)
|
||||
val UMTS_KEY = mobileMappings.toIconKey(UMTS)
|
||||
@@ -63,7 +66,7 @@ class FakeMobileConnectionsRepository(mobileMappings: MobileMappingsProxy) :
|
||||
private val subIdRepos = mutableMapOf<Int, MobileConnectionRepository>()
|
||||
override fun getRepoForSubId(subId: Int): MobileConnectionRepository {
|
||||
return subIdRepos[subId]
|
||||
?: FakeMobileConnectionRepository(subId).also { subIdRepos[subId] = it }
|
||||
?: FakeMobileConnectionRepository(subId, tableLogBuffer).also { subIdRepos[subId] = it }
|
||||
}
|
||||
|
||||
private val _globalMobileDataSettingChangedEvent = MutableStateFlow(Unit)
|
||||
|
||||
@@ -24,6 +24,8 @@ import androidx.test.filters.SmallTest
|
||||
import com.android.systemui.SysuiTestCase
|
||||
import com.android.systemui.demomode.DemoMode
|
||||
import com.android.systemui.demomode.DemoModeController
|
||||
import com.android.systemui.dump.DumpManager
|
||||
import com.android.systemui.log.table.TableLogBufferFactory
|
||||
import com.android.systemui.statusbar.pipeline.mobile.data.model.SubscriptionModel
|
||||
import com.android.systemui.statusbar.pipeline.mobile.data.repository.demo.DemoMobileConnectionsRepository
|
||||
import com.android.systemui.statusbar.pipeline.mobile.data.repository.demo.DemoModeMobileConnectionDataSource
|
||||
@@ -37,6 +39,7 @@ import com.android.systemui.util.mockito.kotlinArgumentCaptor
|
||||
import com.android.systemui.util.mockito.mock
|
||||
import com.android.systemui.util.mockito.whenever
|
||||
import com.android.systemui.util.settings.FakeSettings
|
||||
import com.android.systemui.util.time.FakeSystemClock
|
||||
import com.google.common.truth.Truth.assertThat
|
||||
import kotlinx.coroutines.CoroutineScope
|
||||
import kotlinx.coroutines.Dispatchers
|
||||
@@ -69,12 +72,14 @@ class MobileRepositorySwitcherTest : SysuiTestCase() {
|
||||
private lateinit var realRepo: MobileConnectionsRepositoryImpl
|
||||
private lateinit var demoRepo: DemoMobileConnectionsRepository
|
||||
private lateinit var mockDataSource: DemoModeMobileConnectionDataSource
|
||||
private lateinit var logFactory: TableLogBufferFactory
|
||||
|
||||
@Mock private lateinit var connectivityManager: ConnectivityManager
|
||||
@Mock private lateinit var subscriptionManager: SubscriptionManager
|
||||
@Mock private lateinit var telephonyManager: TelephonyManager
|
||||
@Mock private lateinit var logger: ConnectivityPipelineLogger
|
||||
@Mock private lateinit var demoModeController: DemoModeController
|
||||
@Mock private lateinit var dumpManager: DumpManager
|
||||
|
||||
private val globalSettings = FakeSettings()
|
||||
private val fakeNetworkEventsFlow = MutableStateFlow<FakeNetworkEventModel?>(null)
|
||||
@@ -86,6 +91,8 @@ class MobileRepositorySwitcherTest : SysuiTestCase() {
|
||||
fun setUp() {
|
||||
MockitoAnnotations.initMocks(this)
|
||||
|
||||
logFactory = TableLogBufferFactory(dumpManager, FakeSystemClock())
|
||||
|
||||
// Never start in demo mode
|
||||
whenever(demoModeController.isInDemoMode).thenReturn(false)
|
||||
|
||||
@@ -114,6 +121,7 @@ class MobileRepositorySwitcherTest : SysuiTestCase() {
|
||||
dataSource = mockDataSource,
|
||||
scope = scope,
|
||||
context = context,
|
||||
logFactory = logFactory,
|
||||
)
|
||||
|
||||
underTest =
|
||||
|
||||
@@ -23,6 +23,7 @@ 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.log.table.TableLogBufferFactory
|
||||
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
|
||||
@@ -30,6 +31,7 @@ import com.android.systemui.statusbar.pipeline.mobile.data.repository.demo.model
|
||||
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.android.systemui.util.time.FakeSystemClock
|
||||
import com.google.common.truth.Truth.assertThat
|
||||
import kotlinx.coroutines.ExperimentalCoroutinesApi
|
||||
import kotlinx.coroutines.cancel
|
||||
@@ -54,6 +56,9 @@ import org.junit.runners.Parameterized.Parameters
|
||||
@RunWith(Parameterized::class)
|
||||
internal class DemoMobileConnectionParameterizedTest(private val testCase: TestCase) :
|
||||
SysuiTestCase() {
|
||||
|
||||
private val logFactory = TableLogBufferFactory(mock(), FakeSystemClock())
|
||||
|
||||
private val testDispatcher = UnconfinedTestDispatcher()
|
||||
private val testScope = TestScope(testDispatcher)
|
||||
|
||||
@@ -76,6 +81,7 @@ internal class DemoMobileConnectionParameterizedTest(private val testCase: TestC
|
||||
dataSource = mockDataSource,
|
||||
scope = testScope.backgroundScope,
|
||||
context = context,
|
||||
logFactory = logFactory,
|
||||
)
|
||||
|
||||
connectionsRepo.startProcessingCommands()
|
||||
|
||||
@@ -23,6 +23,8 @@ import androidx.test.filters.SmallTest
|
||||
import com.android.settingslib.SignalIcon
|
||||
import com.android.settingslib.mobile.TelephonyIcons.THREE_G
|
||||
import com.android.systemui.SysuiTestCase
|
||||
import com.android.systemui.dump.DumpManager
|
||||
import com.android.systemui.log.table.TableLogBufferFactory
|
||||
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
|
||||
@@ -32,6 +34,7 @@ import com.android.systemui.statusbar.pipeline.mobile.data.repository.demo.model
|
||||
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.android.systemui.util.time.FakeSystemClock
|
||||
import com.google.common.truth.Truth.assertThat
|
||||
import junit.framework.Assert
|
||||
import kotlinx.coroutines.ExperimentalCoroutinesApi
|
||||
@@ -47,6 +50,9 @@ import org.junit.Test
|
||||
@OptIn(ExperimentalCoroutinesApi::class)
|
||||
@SmallTest
|
||||
class DemoMobileConnectionsRepositoryTest : SysuiTestCase() {
|
||||
private val dumpManager: DumpManager = mock()
|
||||
private val logFactory = TableLogBufferFactory(dumpManager, FakeSystemClock())
|
||||
|
||||
private val testDispatcher = UnconfinedTestDispatcher()
|
||||
private val testScope = TestScope(testDispatcher)
|
||||
|
||||
@@ -68,6 +74,7 @@ class DemoMobileConnectionsRepositoryTest : SysuiTestCase() {
|
||||
dataSource = mockDataSource,
|
||||
scope = testScope.backgroundScope,
|
||||
context = context,
|
||||
logFactory = logFactory,
|
||||
)
|
||||
|
||||
underTest.startProcessingCommands()
|
||||
|
||||
@@ -88,6 +88,7 @@ import org.mockito.MockitoAnnotations
|
||||
@SmallTest
|
||||
class MobileConnectionRepositoryTest : SysuiTestCase() {
|
||||
private lateinit var underTest: MobileConnectionRepositoryImpl
|
||||
private lateinit var connectionsRepo: FakeMobileConnectionsRepository
|
||||
|
||||
@Mock private lateinit var telephonyManager: TelephonyManager
|
||||
@Mock private lateinit var logger: ConnectivityPipelineLogger
|
||||
@@ -96,7 +97,6 @@ class MobileConnectionRepositoryTest : SysuiTestCase() {
|
||||
private val scope = CoroutineScope(IMMEDIATE)
|
||||
private val mobileMappings = FakeMobileMappingsProxy()
|
||||
private val globalSettings = FakeSettings()
|
||||
private val connectionsRepo = FakeMobileConnectionsRepository(mobileMappings)
|
||||
|
||||
@Before
|
||||
fun setUp() {
|
||||
@@ -104,6 +104,8 @@ class MobileConnectionRepositoryTest : SysuiTestCase() {
|
||||
globalSettings.userId = UserHandle.USER_ALL
|
||||
whenever(telephonyManager.subscriptionId).thenReturn(SUB_1_ID)
|
||||
|
||||
connectionsRepo = FakeMobileConnectionsRepository(mobileMappings, tableLogger)
|
||||
|
||||
underTest =
|
||||
MobileConnectionRepositoryImpl(
|
||||
context,
|
||||
|
||||
@@ -19,11 +19,14 @@ 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.log.table.TableLogBuffer
|
||||
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 {
|
||||
class FakeMobileIconInteractor(
|
||||
override val tableLogBuffer: TableLogBuffer,
|
||||
) : MobileIconInteractor {
|
||||
override val alwaysShowDataRatIcon = MutableStateFlow(false)
|
||||
|
||||
override val activity =
|
||||
|
||||
@@ -22,12 +22,15 @@ 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.log.table.TableLogBuffer
|
||||
import com.android.systemui.statusbar.pipeline.mobile.data.model.SubscriptionModel
|
||||
import com.android.systemui.statusbar.pipeline.mobile.util.MobileMappingsProxy
|
||||
import kotlinx.coroutines.flow.Flow
|
||||
import kotlinx.coroutines.flow.MutableStateFlow
|
||||
|
||||
class FakeMobileIconsInteractor(mobileMappings: MobileMappingsProxy) : MobileIconsInteractor {
|
||||
class FakeMobileIconsInteractor(
|
||||
mobileMappings: MobileMappingsProxy,
|
||||
val tableLogBuffer: TableLogBuffer,
|
||||
) : MobileIconsInteractor {
|
||||
val THREE_G_KEY = mobileMappings.toIconKey(THREE_G)
|
||||
val LTE_KEY = mobileMappings.toIconKey(LTE)
|
||||
val FOUR_G_KEY = mobileMappings.toIconKey(FOUR_G)
|
||||
@@ -48,8 +51,7 @@ class FakeMobileIconsInteractor(mobileMappings: MobileMappingsProxy) : MobileIco
|
||||
|
||||
override val isDefaultConnectionFailed = MutableStateFlow(false)
|
||||
|
||||
private val _filteredSubscriptions = MutableStateFlow<List<SubscriptionModel>>(listOf())
|
||||
override val filteredSubscriptions: Flow<List<SubscriptionModel>> = _filteredSubscriptions
|
||||
override val filteredSubscriptions = MutableStateFlow<List<SubscriptionModel>>(listOf())
|
||||
|
||||
private val _activeDataConnectionHasDataEnabled = MutableStateFlow(false)
|
||||
override val activeDataConnectionHasDataEnabled = _activeDataConnectionHasDataEnabled
|
||||
@@ -67,7 +69,7 @@ class FakeMobileIconsInteractor(mobileMappings: MobileMappingsProxy) : MobileIco
|
||||
|
||||
/** Always returns a new fake interactor */
|
||||
override fun createMobileConnectionInteractorForSubId(subId: Int): MobileIconInteractor {
|
||||
return FakeMobileIconInteractor()
|
||||
return FakeMobileIconInteractor(tableLogBuffer)
|
||||
}
|
||||
|
||||
companion object {
|
||||
|
||||
@@ -49,8 +49,8 @@ import org.junit.Test
|
||||
class MobileIconInteractorTest : SysuiTestCase() {
|
||||
private lateinit var underTest: MobileIconInteractor
|
||||
private val mobileMappingsProxy = FakeMobileMappingsProxy()
|
||||
private val mobileIconsInteractor = FakeMobileIconsInteractor(mobileMappingsProxy)
|
||||
private val connectionRepository = FakeMobileConnectionRepository(SUB_1_ID)
|
||||
private val mobileIconsInteractor = FakeMobileIconsInteractor(mobileMappingsProxy, mock())
|
||||
private val connectionRepository = FakeMobileConnectionRepository(SUB_1_ID, mock())
|
||||
|
||||
private val scope = CoroutineScope(IMMEDIATE)
|
||||
|
||||
|
||||
@@ -20,6 +20,7 @@ import android.telephony.SubscriptionManager.INVALID_SUBSCRIPTION_ID
|
||||
import androidx.test.filters.SmallTest
|
||||
import com.android.settingslib.mobile.MobileMappings
|
||||
import com.android.systemui.SysuiTestCase
|
||||
import com.android.systemui.log.table.TableLogBuffer
|
||||
import com.android.systemui.statusbar.pipeline.mobile.data.model.MobileConnectivityModel
|
||||
import com.android.systemui.statusbar.pipeline.mobile.data.model.SubscriptionModel
|
||||
import com.android.systemui.statusbar.pipeline.mobile.data.repository.FakeMobileConnectionRepository
|
||||
@@ -28,6 +29,7 @@ import com.android.systemui.statusbar.pipeline.mobile.data.repository.FakeUserSe
|
||||
import com.android.systemui.statusbar.pipeline.mobile.util.FakeMobileMappingsProxy
|
||||
import com.android.systemui.util.CarrierConfigTracker
|
||||
import com.android.systemui.util.mockito.whenever
|
||||
import com.android.systemui.util.time.FakeSystemClock
|
||||
import com.google.common.truth.Truth.assertThat
|
||||
import kotlinx.coroutines.CoroutineScope
|
||||
import kotlinx.coroutines.Dispatchers
|
||||
@@ -44,9 +46,9 @@ import org.mockito.MockitoAnnotations
|
||||
@SmallTest
|
||||
class MobileIconsInteractorTest : SysuiTestCase() {
|
||||
private lateinit var underTest: MobileIconsInteractor
|
||||
private lateinit var connectionsRepository: FakeMobileConnectionsRepository
|
||||
private val userSetupRepository = FakeUserSetupRepository()
|
||||
private val mobileMappingsProxy = FakeMobileMappingsProxy()
|
||||
private val connectionsRepository = FakeMobileConnectionsRepository(mobileMappingsProxy)
|
||||
private val scope = CoroutineScope(IMMEDIATE)
|
||||
|
||||
@Mock private lateinit var carrierConfigTracker: CarrierConfigTracker
|
||||
@@ -55,6 +57,7 @@ class MobileIconsInteractorTest : SysuiTestCase() {
|
||||
fun setUp() {
|
||||
MockitoAnnotations.initMocks(this)
|
||||
|
||||
connectionsRepository = FakeMobileConnectionsRepository(mobileMappingsProxy, tableLogBuffer)
|
||||
connectionsRepository.setMobileConnectionRepositoryMap(
|
||||
mapOf(
|
||||
SUB_1_ID to CONNECTION_1,
|
||||
@@ -290,21 +293,23 @@ class MobileIconsInteractorTest : SysuiTestCase() {
|
||||
|
||||
companion object {
|
||||
private val IMMEDIATE = Dispatchers.Main.immediate
|
||||
private val tableLogBuffer =
|
||||
TableLogBuffer(8, "MobileIconsInteractorTest", FakeSystemClock())
|
||||
|
||||
private const val SUB_1_ID = 1
|
||||
private val SUB_1 = SubscriptionModel(subscriptionId = SUB_1_ID)
|
||||
private val CONNECTION_1 = FakeMobileConnectionRepository(SUB_1_ID)
|
||||
private val CONNECTION_1 = FakeMobileConnectionRepository(SUB_1_ID, tableLogBuffer)
|
||||
|
||||
private const val SUB_2_ID = 2
|
||||
private val SUB_2 = SubscriptionModel(subscriptionId = SUB_2_ID)
|
||||
private val CONNECTION_2 = FakeMobileConnectionRepository(SUB_2_ID)
|
||||
private val CONNECTION_2 = FakeMobileConnectionRepository(SUB_2_ID, tableLogBuffer)
|
||||
|
||||
private const val SUB_3_ID = 3
|
||||
private val SUB_3_OPP = SubscriptionModel(subscriptionId = SUB_3_ID, isOpportunistic = true)
|
||||
private val CONNECTION_3 = FakeMobileConnectionRepository(SUB_3_ID)
|
||||
private val CONNECTION_3 = FakeMobileConnectionRepository(SUB_3_ID, tableLogBuffer)
|
||||
|
||||
private const val SUB_4_ID = 4
|
||||
private val SUB_4_OPP = SubscriptionModel(subscriptionId = SUB_4_ID, isOpportunistic = true)
|
||||
private val CONNECTION_4 = FakeMobileConnectionRepository(SUB_4_ID)
|
||||
private val CONNECTION_4 = FakeMobileConnectionRepository(SUB_4_ID, tableLogBuffer)
|
||||
}
|
||||
}
|
||||
|
||||
@@ -19,6 +19,7 @@ package com.android.systemui.statusbar.pipeline.mobile.ui.viewmodel
|
||||
import androidx.test.filters.SmallTest
|
||||
import com.android.settingslib.mobile.TelephonyIcons
|
||||
import com.android.systemui.SysuiTestCase
|
||||
import com.android.systemui.log.table.TableLogBuffer
|
||||
import com.android.systemui.statusbar.pipeline.mobile.domain.interactor.FakeMobileIconInteractor
|
||||
import com.android.systemui.statusbar.pipeline.mobile.ui.viewmodel.MobileIconViewModelTest.Companion.defaultSignal
|
||||
import com.android.systemui.statusbar.pipeline.shared.ConnectivityConstants
|
||||
@@ -43,9 +44,10 @@ class LocationBasedMobileIconViewModelTest : SysuiTestCase() {
|
||||
private lateinit var homeIcon: HomeMobileIconViewModel
|
||||
private lateinit var qsIcon: QsMobileIconViewModel
|
||||
private lateinit var keyguardIcon: KeyguardMobileIconViewModel
|
||||
private val interactor = FakeMobileIconInteractor()
|
||||
private lateinit var interactor: FakeMobileIconInteractor
|
||||
@Mock private lateinit var logger: ConnectivityPipelineLogger
|
||||
@Mock private lateinit var constants: ConnectivityConstants
|
||||
@Mock private lateinit var tableLogBuffer: TableLogBuffer
|
||||
|
||||
private val testDispatcher = UnconfinedTestDispatcher()
|
||||
private val testScope = TestScope(testDispatcher)
|
||||
@@ -53,6 +55,7 @@ class LocationBasedMobileIconViewModelTest : SysuiTestCase() {
|
||||
@Before
|
||||
fun setUp() {
|
||||
MockitoAnnotations.initMocks(this)
|
||||
interactor = FakeMobileIconInteractor(tableLogBuffer)
|
||||
interactor.apply {
|
||||
setLevel(1)
|
||||
setIsDefaultDataEnabled(true)
|
||||
@@ -62,11 +65,12 @@ class LocationBasedMobileIconViewModelTest : SysuiTestCase() {
|
||||
setNumberOfLevels(4)
|
||||
isDataConnected.value = true
|
||||
}
|
||||
commonImpl = MobileIconViewModel(SUB_1_ID, interactor, logger, constants)
|
||||
commonImpl =
|
||||
MobileIconViewModel(SUB_1_ID, interactor, logger, constants, testScope.backgroundScope)
|
||||
|
||||
homeIcon = HomeMobileIconViewModel(commonImpl)
|
||||
qsIcon = QsMobileIconViewModel(commonImpl)
|
||||
keyguardIcon = KeyguardMobileIconViewModel(commonImpl)
|
||||
homeIcon = HomeMobileIconViewModel(commonImpl, logger)
|
||||
qsIcon = QsMobileIconViewModel(commonImpl, logger)
|
||||
keyguardIcon = KeyguardMobileIconViewModel(commonImpl, logger)
|
||||
}
|
||||
|
||||
@Test
|
||||
|
||||
@@ -22,32 +22,42 @@ import com.android.settingslib.mobile.TelephonyIcons.THREE_G
|
||||
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.log.table.TableLogBuffer
|
||||
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.ExperimentalCoroutinesApi
|
||||
import kotlinx.coroutines.flow.launchIn
|
||||
import kotlinx.coroutines.flow.onEach
|
||||
import kotlinx.coroutines.runBlocking
|
||||
import kotlinx.coroutines.test.TestScope
|
||||
import kotlinx.coroutines.test.UnconfinedTestDispatcher
|
||||
import kotlinx.coroutines.test.runTest
|
||||
import kotlinx.coroutines.yield
|
||||
import org.junit.Before
|
||||
import org.junit.Test
|
||||
import org.mockito.Mock
|
||||
import org.mockito.MockitoAnnotations
|
||||
|
||||
@Suppress("EXPERIMENTAL_IS_NOT_ENABLED")
|
||||
@OptIn(ExperimentalCoroutinesApi::class)
|
||||
@SmallTest
|
||||
class MobileIconViewModelTest : SysuiTestCase() {
|
||||
private lateinit var underTest: MobileIconViewModel
|
||||
private val interactor = FakeMobileIconInteractor()
|
||||
private lateinit var interactor: FakeMobileIconInteractor
|
||||
@Mock private lateinit var logger: ConnectivityPipelineLogger
|
||||
@Mock private lateinit var constants: ConnectivityConstants
|
||||
@Mock private lateinit var tableLogBuffer: TableLogBuffer
|
||||
|
||||
private val testDispatcher = UnconfinedTestDispatcher()
|
||||
private val testScope = TestScope(testDispatcher)
|
||||
|
||||
@Before
|
||||
fun setUp() {
|
||||
MockitoAnnotations.initMocks(this)
|
||||
interactor = FakeMobileIconInteractor(tableLogBuffer)
|
||||
interactor.apply {
|
||||
setLevel(1)
|
||||
setIsDefaultDataEnabled(true)
|
||||
@@ -57,12 +67,13 @@ class MobileIconViewModelTest : SysuiTestCase() {
|
||||
setNumberOfLevels(4)
|
||||
isDataConnected.value = true
|
||||
}
|
||||
underTest = MobileIconViewModel(SUB_1_ID, interactor, logger, constants)
|
||||
underTest =
|
||||
MobileIconViewModel(SUB_1_ID, interactor, logger, constants, testScope.backgroundScope)
|
||||
}
|
||||
|
||||
@Test
|
||||
fun iconId_correctLevel_notCutout() =
|
||||
runBlocking(IMMEDIATE) {
|
||||
testScope.runTest {
|
||||
var latest: Int? = null
|
||||
val job = underTest.iconId.onEach { latest = it }.launchIn(this)
|
||||
val expected = defaultSignal()
|
||||
@@ -74,7 +85,7 @@ class MobileIconViewModelTest : SysuiTestCase() {
|
||||
|
||||
@Test
|
||||
fun iconId_cutout_whenDefaultDataDisabled() =
|
||||
runBlocking(IMMEDIATE) {
|
||||
testScope.runTest {
|
||||
interactor.setIsDefaultDataEnabled(false)
|
||||
|
||||
var latest: Int? = null
|
||||
@@ -88,7 +99,7 @@ class MobileIconViewModelTest : SysuiTestCase() {
|
||||
|
||||
@Test
|
||||
fun networkType_dataEnabled_groupIsRepresented() =
|
||||
runBlocking(IMMEDIATE) {
|
||||
testScope.runTest {
|
||||
val expected =
|
||||
Icon.Resource(
|
||||
THREE_G.dataType,
|
||||
@@ -106,7 +117,7 @@ class MobileIconViewModelTest : SysuiTestCase() {
|
||||
|
||||
@Test
|
||||
fun networkType_nullWhenDisabled() =
|
||||
runBlocking(IMMEDIATE) {
|
||||
testScope.runTest {
|
||||
interactor.setIconGroup(THREE_G)
|
||||
interactor.setIsDataEnabled(false)
|
||||
var latest: Icon? = null
|
||||
@@ -119,7 +130,7 @@ class MobileIconViewModelTest : SysuiTestCase() {
|
||||
|
||||
@Test
|
||||
fun networkType_nullWhenFailedConnection() =
|
||||
runBlocking(IMMEDIATE) {
|
||||
testScope.runTest {
|
||||
interactor.setIconGroup(THREE_G)
|
||||
interactor.setIsDataEnabled(true)
|
||||
interactor.setIsFailedConnection(true)
|
||||
@@ -133,7 +144,7 @@ class MobileIconViewModelTest : SysuiTestCase() {
|
||||
|
||||
@Test
|
||||
fun networkType_nullWhenDataDisconnects() =
|
||||
runBlocking(IMMEDIATE) {
|
||||
testScope.runTest {
|
||||
val initial =
|
||||
Icon.Resource(
|
||||
THREE_G.dataType,
|
||||
@@ -157,7 +168,7 @@ class MobileIconViewModelTest : SysuiTestCase() {
|
||||
|
||||
@Test
|
||||
fun networkType_null_changeToDisabled() =
|
||||
runBlocking(IMMEDIATE) {
|
||||
testScope.runTest {
|
||||
val expected =
|
||||
Icon.Resource(
|
||||
THREE_G.dataType,
|
||||
@@ -180,7 +191,7 @@ class MobileIconViewModelTest : SysuiTestCase() {
|
||||
|
||||
@Test
|
||||
fun networkType_alwaysShow_shownEvenWhenDisabled() =
|
||||
runBlocking(IMMEDIATE) {
|
||||
testScope.runTest {
|
||||
interactor.setIconGroup(THREE_G)
|
||||
interactor.setIsDataEnabled(true)
|
||||
interactor.alwaysShowDataRatIcon.value = true
|
||||
@@ -200,7 +211,7 @@ class MobileIconViewModelTest : SysuiTestCase() {
|
||||
|
||||
@Test
|
||||
fun networkType_alwaysShow_shownEvenWhenDisconnected() =
|
||||
runBlocking(IMMEDIATE) {
|
||||
testScope.runTest {
|
||||
interactor.setIconGroup(THREE_G)
|
||||
interactor.isDataConnected.value = false
|
||||
interactor.alwaysShowDataRatIcon.value = true
|
||||
@@ -220,7 +231,7 @@ class MobileIconViewModelTest : SysuiTestCase() {
|
||||
|
||||
@Test
|
||||
fun networkType_alwaysShow_shownEvenWhenFailedConnection() =
|
||||
runBlocking(IMMEDIATE) {
|
||||
testScope.runTest {
|
||||
interactor.setIconGroup(THREE_G)
|
||||
interactor.setIsFailedConnection(true)
|
||||
interactor.alwaysShowDataRatIcon.value = true
|
||||
@@ -240,7 +251,7 @@ class MobileIconViewModelTest : SysuiTestCase() {
|
||||
|
||||
@Test
|
||||
fun roaming() =
|
||||
runBlocking(IMMEDIATE) {
|
||||
testScope.runTest {
|
||||
interactor.isRoaming.value = true
|
||||
var latest: Boolean? = null
|
||||
val job = underTest.roaming.onEach { latest = it }.launchIn(this)
|
||||
@@ -256,10 +267,17 @@ class MobileIconViewModelTest : SysuiTestCase() {
|
||||
|
||||
@Test
|
||||
fun `data activity - null when config is off`() =
|
||||
runBlocking(IMMEDIATE) {
|
||||
testScope.runTest {
|
||||
// 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)
|
||||
underTest =
|
||||
MobileIconViewModel(
|
||||
SUB_1_ID,
|
||||
interactor,
|
||||
logger,
|
||||
constants,
|
||||
testScope.backgroundScope,
|
||||
)
|
||||
|
||||
var inVisible: Boolean? = null
|
||||
val inJob = underTest.activityInVisible.onEach { inVisible = it }.launchIn(this)
|
||||
@@ -288,10 +306,17 @@ class MobileIconViewModelTest : SysuiTestCase() {
|
||||
|
||||
@Test
|
||||
fun `data activity - config on - test indicators`() =
|
||||
runBlocking(IMMEDIATE) {
|
||||
testScope.runTest {
|
||||
// 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)
|
||||
underTest =
|
||||
MobileIconViewModel(
|
||||
SUB_1_ID,
|
||||
interactor,
|
||||
logger,
|
||||
constants,
|
||||
testScope.backgroundScope,
|
||||
)
|
||||
|
||||
var inVisible: Boolean? = null
|
||||
val inJob = underTest.activityInVisible.onEach { inVisible = it }.launchIn(this)
|
||||
@@ -341,7 +366,6 @@ class MobileIconViewModelTest : SysuiTestCase() {
|
||||
}
|
||||
|
||||
companion object {
|
||||
private val IMMEDIATE = Dispatchers.Main.immediate
|
||||
private const val SUB_1_ID = 1
|
||||
|
||||
/** Convenience constructor for these tests */
|
||||
|
||||
@@ -0,0 +1,106 @@
|
||||
/*
|
||||
* 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.ui.viewmodel
|
||||
|
||||
import androidx.test.filters.SmallTest
|
||||
import com.android.systemui.SysuiTestCase
|
||||
import com.android.systemui.statusbar.phone.StatusBarLocation
|
||||
import com.android.systemui.statusbar.pipeline.mobile.data.model.SubscriptionModel
|
||||
import com.android.systemui.statusbar.pipeline.mobile.domain.interactor.FakeMobileIconsInteractor
|
||||
import com.android.systemui.statusbar.pipeline.mobile.util.FakeMobileMappingsProxy
|
||||
import com.android.systemui.statusbar.pipeline.shared.ConnectivityConstants
|
||||
import com.android.systemui.statusbar.pipeline.shared.ConnectivityPipelineLogger
|
||||
import com.android.systemui.util.mockito.mock
|
||||
import com.google.common.truth.Truth.assertThat
|
||||
import kotlinx.coroutines.ExperimentalCoroutinesApi
|
||||
import kotlinx.coroutines.flow.SharingStarted
|
||||
import kotlinx.coroutines.flow.map
|
||||
import kotlinx.coroutines.flow.stateIn
|
||||
import kotlinx.coroutines.test.TestScope
|
||||
import kotlinx.coroutines.test.UnconfinedTestDispatcher
|
||||
import kotlinx.coroutines.test.runTest
|
||||
import org.junit.Before
|
||||
import org.junit.Test
|
||||
import org.mockito.Mock
|
||||
import org.mockito.MockitoAnnotations
|
||||
|
||||
@Suppress("EXPERIMENTAL_IS_NOT_ENABLED")
|
||||
@OptIn(ExperimentalCoroutinesApi::class)
|
||||
@SmallTest
|
||||
class MobileIconsViewModelTest : SysuiTestCase() {
|
||||
private lateinit var underTest: MobileIconsViewModel
|
||||
private val interactor = FakeMobileIconsInteractor(FakeMobileMappingsProxy(), mock())
|
||||
|
||||
@Mock private lateinit var logger: ConnectivityPipelineLogger
|
||||
@Mock private lateinit var constants: ConnectivityConstants
|
||||
|
||||
private val testDispatcher = UnconfinedTestDispatcher()
|
||||
private val testScope = TestScope(testDispatcher)
|
||||
|
||||
@Before
|
||||
fun setUp() {
|
||||
MockitoAnnotations.initMocks(this)
|
||||
|
||||
val subscriptionIdsFlow =
|
||||
interactor.filteredSubscriptions
|
||||
.map { subs -> subs.map { it.subscriptionId } }
|
||||
.stateIn(testScope.backgroundScope, SharingStarted.WhileSubscribed(), listOf())
|
||||
|
||||
underTest =
|
||||
MobileIconsViewModel(
|
||||
subscriptionIdsFlow,
|
||||
interactor,
|
||||
logger,
|
||||
constants,
|
||||
testScope.backgroundScope,
|
||||
)
|
||||
|
||||
interactor.filteredSubscriptions.value = listOf(SUB_1, SUB_2)
|
||||
}
|
||||
|
||||
@Test
|
||||
fun `caching - mobile icon view model is reused for same sub id`() =
|
||||
testScope.runTest {
|
||||
val model1 = underTest.viewModelForSub(1, StatusBarLocation.HOME)
|
||||
val model2 = underTest.viewModelForSub(1, StatusBarLocation.QS)
|
||||
|
||||
assertThat(model1.commonImpl).isSameInstanceAs(model2.commonImpl)
|
||||
}
|
||||
|
||||
@Test
|
||||
fun `caching - invalid view models are removed from cache when sub disappears`() =
|
||||
testScope.runTest {
|
||||
// Retrieve models to trigger caching
|
||||
val model1 = underTest.viewModelForSub(1, StatusBarLocation.HOME)
|
||||
val model2 = underTest.viewModelForSub(2, StatusBarLocation.QS)
|
||||
|
||||
// Both impls are cached
|
||||
assertThat(underTest.mobileIconSubIdCache)
|
||||
.containsExactly(1, model1.commonImpl, 2, model2.commonImpl)
|
||||
|
||||
// SUB_1 is removed from the list...
|
||||
interactor.filteredSubscriptions.value = listOf(SUB_2)
|
||||
|
||||
// ... and dropped from the cache
|
||||
assertThat(underTest.mobileIconSubIdCache).containsExactly(2, model2.commonImpl)
|
||||
}
|
||||
|
||||
companion object {
|
||||
private val SUB_1 = SubscriptionModel(subscriptionId = 1, isOpportunistic = false)
|
||||
private val SUB_2 = SubscriptionModel(subscriptionId = 2, isOpportunistic = false)
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user