1 /* <lambda>null2 * Copyright (C) 2025 The Android Open Source Project 3 * 4 * Licensed under the Apache License, Version 2.0 (the "License"); 5 * you may not use this file except in compliance with the License. 6 * You may obtain a copy of the License at 7 * 8 * http://www.apache.org/licenses/LICENSE-2.0 9 * 10 * Unless required by applicable law or agreed to in writing, software 11 * distributed under the License is distributed on an "AS IS" BASIS, 12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 * See the License for the specific language governing permissions and 14 * limitations under the License. 15 */ 16 17 package com.android.systemui.statusbar.pipeline.mobile.domain.interactor 18 19 import androidx.test.ext.junit.runners.AndroidJUnit4 20 import androidx.test.filters.SmallTest 21 import com.android.settingslib.SignalIcon 22 import com.android.settingslib.mobile.MobileMappings 23 import com.android.settingslib.mobile.TelephonyIcons 24 import com.android.systemui.KairosBuilder 25 import com.android.systemui.flags.featureFlagsClassic 26 import com.android.systemui.kairos.BuildScope 27 import com.android.systemui.kairos.Events 28 import com.android.systemui.kairos.ExperimentalKairosApi 29 import com.android.systemui.kairos.Incremental 30 import com.android.systemui.kairos.State 31 import com.android.systemui.kairos.activateKairosActivatable 32 import com.android.systemui.kairos.asIncremental 33 import com.android.systemui.kairos.buildSpec 34 import com.android.systemui.kairos.kairos 35 import com.android.systemui.kairos.map 36 import com.android.systemui.kairos.mapValues 37 import com.android.systemui.kairos.stateOf 38 import com.android.systemui.kairosBuilder 39 import com.android.systemui.kosmos.Kosmos 40 import com.android.systemui.kosmos.applicationCoroutineScope 41 import com.android.systemui.kosmos.runCurrent 42 import com.android.systemui.log.table.TableLogBuffer 43 import com.android.systemui.log.table.tableLogBufferFactory 44 import com.android.systemui.statusbar.pipeline.mobile.data.model.DataConnectionState 45 import com.android.systemui.statusbar.pipeline.mobile.data.model.NetworkNameModel 46 import com.android.systemui.statusbar.pipeline.mobile.data.model.ResolvedNetworkType 47 import com.android.systemui.statusbar.pipeline.mobile.data.model.SubscriptionModel 48 import com.android.systemui.statusbar.pipeline.mobile.data.repository.MobileConnectionRepository 49 import com.android.systemui.statusbar.pipeline.mobile.data.repository.MobileConnectionRepositoryKairos 50 import com.android.systemui.statusbar.pipeline.mobile.data.repository.MobileConnectionsRepository 51 import com.android.systemui.statusbar.pipeline.mobile.data.repository.MobileConnectionsRepositoryKairos 52 import com.android.systemui.statusbar.pipeline.mobile.data.repository.mobileMappingsProxy 53 import com.android.systemui.statusbar.pipeline.shared.data.model.DataActivityModel 54 import com.android.systemui.statusbar.pipeline.shared.data.repository.connectivityRepository 55 import com.android.systemui.statusbar.policy.data.repository.FakeUserSetupRepository 56 import org.junit.runner.RunWith 57 import org.mockito.kotlin.mock 58 59 @OptIn(ExperimentalKairosApi::class) 60 @SmallTest 61 @RunWith(AndroidJUnit4::class) 62 class MobileIconsInteractorKairosAdapterTest : MobileIconsInteractorTestBase() { 63 override fun Kosmos.createInteractor(): MobileIconsInteractor { 64 val userSetupRepo = FakeUserSetupRepository() 65 val repoK = 66 MobileConnectionsRepoWrapper(connectionsRepository).also { 67 activateKairosActivatable(it) 68 } 69 val kairosInteractor = 70 MobileIconsInteractorKairosImpl( 71 mobileConnectionsRepo = repoK, 72 carrierConfigTracker = carrierConfigTracker, 73 tableLogger = mock(), 74 connectivityRepository = connectivityRepository, 75 userSetupRepo = userSetupRepo, 76 context = context, 77 featureFlagsClassic = featureFlagsClassic, 78 ) 79 .also { activateKairosActivatable(it) } 80 return MobileIconsInteractorKairosAdapter( 81 kairosInteractor = kairosInteractor, 82 repo = connectionsRepository, 83 repoK = repoK, 84 kairosNetwork = kairos, 85 scope = applicationCoroutineScope, 86 context = context, 87 mobileMappingsProxy = mobileMappingsProxy, 88 userSetupRepo = userSetupRepo, 89 logFactory = tableLogBufferFactory, 90 ) 91 .also { 92 activateKairosActivatable(it) 93 runCurrent() 94 } 95 } 96 97 /** Allows us to wrap a (likely fake) MobileConnectionsRepository into a Kairos version. */ 98 private class MobileConnectionsRepoWrapper(val unwrapped: MobileConnectionsRepository) : 99 MobileConnectionsRepositoryKairos, KairosBuilder by kairosBuilder() { 100 101 override val mobileConnectionsBySubId: Incremental<Int, MobileConnectionRepositoryKairos> = 102 buildIncremental { 103 unwrapped.subscriptions 104 .toState() 105 .map { it.associate { it.subscriptionId to Unit } } 106 .asIncremental() 107 .mapValues { (subId, _) -> 108 buildSpec { wrapRepo(unwrapped.getRepoForSubId(subId)) } 109 } 110 .applyLatestSpecForKey() 111 } 112 override val subscriptions: State<Collection<SubscriptionModel>> = buildState { 113 unwrapped.subscriptions.toState() 114 } 115 override val activeMobileDataSubscriptionId: State<Int?> = buildState { 116 unwrapped.activeMobileDataSubscriptionId.toState() 117 } 118 override val activeMobileDataRepository: State<MobileConnectionRepositoryKairos?> = 119 buildState { 120 unwrapped.activeMobileDataRepository.toState().mapLatestBuild { 121 it?.let { wrapRepo(it) } 122 } 123 } 124 override val activeSubChangedInGroupEvent: Events<Unit> = buildEvents { 125 unwrapped.activeSubChangedInGroupEvent.toEvents() 126 } 127 override val defaultDataSubId: State<Int?> = buildState { 128 unwrapped.defaultDataSubId.toState() 129 } 130 override val mobileIsDefault: State<Boolean> = buildState { 131 unwrapped.mobileIsDefault.toState() 132 } 133 override val hasCarrierMergedConnection: State<Boolean> = buildState { 134 unwrapped.hasCarrierMergedConnection.toState(false) 135 } 136 override val defaultConnectionIsValidated: State<Boolean> = buildState { 137 unwrapped.defaultConnectionIsValidated.toState() 138 } 139 override val defaultDataSubRatConfig: State<MobileMappings.Config> = buildState { 140 unwrapped.defaultDataSubRatConfig.toState() 141 } 142 override val defaultMobileIconMapping: State<Map<String, SignalIcon.MobileIconGroup>> = 143 buildState { 144 unwrapped.defaultMobileIconMapping.toState(emptyMap()) 145 } 146 override val defaultMobileIconGroup: State<SignalIcon.MobileIconGroup> = buildState { 147 unwrapped.defaultMobileIconGroup.toState(TelephonyIcons.THREE_G) 148 } 149 override val isDeviceEmergencyCallCapable: State<Boolean> = buildState { 150 unwrapped.isDeviceEmergencyCallCapable.toState() 151 } 152 override val isAnySimSecure: State<Boolean> = buildState { 153 unwrapped.isDeviceEmergencyCallCapable.toState() 154 } 155 override val isInEcmMode: State<Boolean> = stateOf(false) 156 } 157 158 private class MobileConnectionRepoWrapper( 159 override val subId: Int, 160 override val carrierId: State<Int>, 161 override val inflateSignalStrength: State<Boolean>, 162 override val allowNetworkSliceIndicator: State<Boolean>, 163 override val tableLogBuffer: TableLogBuffer, 164 override val isEmergencyOnly: State<Boolean>, 165 override val isRoaming: State<Boolean>, 166 override val operatorAlphaShort: State<String?>, 167 override val isInService: State<Boolean>, 168 override val isNonTerrestrial: State<Boolean>, 169 override val isGsm: State<Boolean>, 170 override val cdmaLevel: State<Int>, 171 override val primaryLevel: State<Int>, 172 override val satelliteLevel: State<Int>, 173 override val dataConnectionState: State<DataConnectionState>, 174 override val dataActivityDirection: State<DataActivityModel>, 175 override val carrierNetworkChangeActive: State<Boolean>, 176 override val resolvedNetworkType: State<ResolvedNetworkType>, 177 override val numberOfLevels: State<Int>, 178 override val dataEnabled: State<Boolean>, 179 override val cdmaRoaming: State<Boolean>, 180 override val networkName: State<NetworkNameModel>, 181 override val carrierName: State<NetworkNameModel>, 182 override val isAllowedDuringAirplaneMode: State<Boolean>, 183 override val hasPrioritizedNetworkCapabilities: State<Boolean>, 184 override val isInEcmMode: State<Boolean>, 185 ) : MobileConnectionRepositoryKairos 186 187 companion object { 188 /** Allows us to wrap a (likely fake) MobileConnectionRepository into a Kairos version. */ 189 fun BuildScope.wrapRepo( 190 conn: MobileConnectionRepository 191 ): MobileConnectionRepositoryKairos = 192 with(conn) { 193 MobileConnectionRepoWrapper( 194 subId = subId, 195 carrierId = carrierId.toState(), 196 inflateSignalStrength = inflateSignalStrength.toState(), 197 allowNetworkSliceIndicator = allowNetworkSliceIndicator.toState(), 198 tableLogBuffer = tableLogBuffer, 199 isEmergencyOnly = isEmergencyOnly.toState(), 200 isRoaming = isRoaming.toState(), 201 operatorAlphaShort = operatorAlphaShort.toState(), 202 isInService = isInService.toState(), 203 isNonTerrestrial = isNonTerrestrial.toState(), 204 isGsm = isGsm.toState(), 205 cdmaLevel = cdmaLevel.toState(), 206 primaryLevel = primaryLevel.toState(), 207 satelliteLevel = satelliteLevel.toState(), 208 dataConnectionState = dataConnectionState.toState(), 209 dataActivityDirection = dataActivityDirection.toState(), 210 carrierNetworkChangeActive = carrierNetworkChangeActive.toState(), 211 resolvedNetworkType = resolvedNetworkType.toState(), 212 numberOfLevels = numberOfLevels.toState(), 213 dataEnabled = dataEnabled.toState(), 214 cdmaRoaming = cdmaRoaming.toState(), 215 networkName = networkName.toState(), 216 carrierName = carrierName.toState(), 217 isAllowedDuringAirplaneMode = isAllowedDuringAirplaneMode.toState(), 218 hasPrioritizedNetworkCapabilities = hasPrioritizedNetworkCapabilities.toState(), 219 isInEcmMode = stateOf(false), 220 ) 221 } 222 } 223 } 224