diff options
10 files changed, 422 insertions, 43 deletions
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/connectivity/WifiIcons.java b/packages/SystemUI/src/com/android/systemui/statusbar/connectivity/WifiIcons.java index 7097568c4adf..e582a01ea88b 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/connectivity/WifiIcons.java +++ b/packages/SystemUI/src/com/android/systemui/statusbar/connectivity/WifiIcons.java @@ -31,7 +31,7 @@ public class WifiIcons { com.android.internal.R.drawable.ic_wifi_signal_4 }; - private static final int[] WIFI_NO_INTERNET_ICONS = { + public static final int[] WIFI_NO_INTERNET_ICONS = { R.drawable.ic_no_internet_wifi_signal_0, R.drawable.ic_no_internet_wifi_signal_1, R.drawable.ic_no_internet_wifi_signal_2, @@ -48,7 +48,7 @@ public class WifiIcons { public static final int QS_WIFI_DISABLED = com.android.internal.R.drawable.ic_wifi_signal_0; public static final int QS_WIFI_NO_NETWORK = com.android.internal.R.drawable.ic_wifi_signal_0; - static final int WIFI_NO_NETWORK = QS_WIFI_NO_NETWORK; + public static final int WIFI_NO_NETWORK = QS_WIFI_NO_NETWORK; static final int WIFI_LEVEL_COUNT = WIFI_SIGNAL_STRENGTH[0].length; diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/pipeline/wifi/data/model/WifiNetworkModel.kt b/packages/SystemUI/src/com/android/systemui/statusbar/pipeline/wifi/data/model/WifiNetworkModel.kt index 5566fa6b5835..c96faabcda6c 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/pipeline/wifi/data/model/WifiNetworkModel.kt +++ b/packages/SystemUI/src/com/android/systemui/statusbar/pipeline/wifi/data/model/WifiNetworkModel.kt @@ -16,13 +16,23 @@ package com.android.systemui.statusbar.pipeline.wifi.data.model +import androidx.annotation.VisibleForTesting + /** Provides information about the current wifi network. */ sealed class WifiNetworkModel { /** A model representing that we have no active wifi network. */ object Inactive : WifiNetworkModel() + /** + * A model representing that our wifi network is actually a carrier merged network, meaning it's + * treated as more of a mobile network. + * + * See [android.net.wifi.WifiInfo.isCarrierMerged] for more information. + */ + object CarrierMerged : WifiNetworkModel() + /** Provides information about an active wifi network. */ - class Active( + data class Active( /** * The [android.net.Network.netId] we received from * [android.net.ConnectivityManager.NetworkCallback] in association with this wifi network. @@ -31,6 +41,19 @@ sealed class WifiNetworkModel { */ val networkId: Int, + /** See [android.net.NetworkCapabilities.NET_CAPABILITY_VALIDATED]. */ + val isValidated: Boolean = false, + + /** + * The wifi signal level, guaranteed to be 0 <= level <= 4. + * + * Null if we couldn't fetch the level for some reason. + * + * TODO(b/238425913): The level will only be null if we have a null WifiManager. Is there a + * way we can guarantee a non-null WifiManager? + */ + val level: Int? = null, + /** See [android.net.wifi.WifiInfo.ssid]. */ val ssid: String? = null, @@ -42,5 +65,18 @@ sealed class WifiNetworkModel { /** See [android.net.wifi.WifiInfo.passpointProviderFriendlyName]. */ val passpointProviderFriendlyName: String? = null, - ) : WifiNetworkModel() + ) : WifiNetworkModel() { + init { + require(level == null || level in MIN_VALID_LEVEL..MAX_VALID_LEVEL) { + "0 <= wifi level <= 4 required; level was $level" + } + } + + companion object { + @VisibleForTesting + internal const val MIN_VALID_LEVEL = 0 + @VisibleForTesting + internal const val MAX_VALID_LEVEL = 4 + } + } } diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/pipeline/wifi/data/repository/WifiRepository.kt b/packages/SystemUI/src/com/android/systemui/statusbar/pipeline/wifi/data/repository/WifiRepository.kt index 43fbabca823f..77654273bde4 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/pipeline/wifi/data/repository/WifiRepository.kt +++ b/packages/SystemUI/src/com/android/systemui/statusbar/pipeline/wifi/data/repository/WifiRepository.kt @@ -21,6 +21,7 @@ import android.net.ConnectivityManager import android.net.Network import android.net.NetworkCapabilities import android.net.NetworkCapabilities.NET_CAPABILITY_NOT_VPN +import android.net.NetworkCapabilities.NET_CAPABILITY_VALIDATED import android.net.NetworkCapabilities.TRANSPORT_CELLULAR import android.net.NetworkCapabilities.TRANSPORT_WIFI import android.net.NetworkRequest @@ -31,6 +32,7 @@ import android.util.Log import com.android.settingslib.Utils 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.Main import com.android.systemui.statusbar.pipeline.shared.ConnectivityPipelineLogger import com.android.systemui.statusbar.pipeline.shared.ConnectivityPipelineLogger.Companion.SB_LOGGING_TAG @@ -38,10 +40,13 @@ import com.android.systemui.statusbar.pipeline.wifi.data.model.WifiActivityModel import com.android.systemui.statusbar.pipeline.wifi.data.model.WifiNetworkModel import java.util.concurrent.Executor import javax.inject.Inject +import kotlinx.coroutines.CoroutineScope import kotlinx.coroutines.ExperimentalCoroutinesApi import kotlinx.coroutines.channels.awaitClose import kotlinx.coroutines.flow.Flow +import kotlinx.coroutines.flow.SharingStarted import kotlinx.coroutines.flow.flowOf +import kotlinx.coroutines.flow.shareIn /** * Provides data related to the wifi state. @@ -63,10 +68,11 @@ interface WifiRepository { @SysUISingleton @SuppressLint("MissingPermission") class WifiRepositoryImpl @Inject constructor( - connectivityManager: ConnectivityManager, - wifiManager: WifiManager?, - @Main mainExecutor: Executor, - logger: ConnectivityPipelineLogger, + connectivityManager: ConnectivityManager, + logger: ConnectivityPipelineLogger, + @Main mainExecutor: Executor, + @Application scope: CoroutineScope, + wifiManager: WifiManager?, ) : WifiRepository { override val wifiNetwork: Flow<WifiNetworkModel> = conflatedCallbackFlow { var currentWifi: WifiNetworkModel = WIFI_NETWORK_DEFAULT @@ -80,7 +86,12 @@ class WifiRepositoryImpl @Inject constructor( val wifiInfo = networkCapabilitiesToWifiInfo(networkCapabilities) if (wifiInfo?.isPrimary == true) { - val wifiNetworkModel = wifiInfoToModel(wifiInfo, network.getNetId()) + val wifiNetworkModel = createWifiNetworkModel( + wifiInfo, + network, + networkCapabilities, + wifiManager, + ) logger.logTransformation( WIFI_NETWORK_CALLBACK_NAME, oldValue = currentWifi, @@ -112,6 +123,7 @@ class WifiRepositoryImpl @Inject constructor( awaitClose { connectivityManager.unregisterNetworkCallback(callback) } } + .shareIn(scope, started = SharingStarted.WhileSubscribed()) override val wifiActivity: Flow<WifiActivityModel> = if (wifiManager == null) { @@ -164,14 +176,25 @@ class WifiRepositoryImpl @Inject constructor( } } - private fun wifiInfoToModel(wifiInfo: WifiInfo, networkId: Int): WifiNetworkModel { - return WifiNetworkModel.Active( - networkId, - wifiInfo.ssid, - wifiInfo.isPasspointAp, - wifiInfo.isOsuAp, - wifiInfo.passpointProviderFriendlyName - ) + private fun createWifiNetworkModel( + wifiInfo: WifiInfo, + network: Network, + networkCapabilities: NetworkCapabilities, + wifiManager: WifiManager?, + ): WifiNetworkModel { + return if (wifiInfo.isCarrierMerged) { + WifiNetworkModel.CarrierMerged + } else { + WifiNetworkModel.Active( + network.getNetId(), + isValidated = networkCapabilities.hasCapability(NET_CAPABILITY_VALIDATED), + level = wifiManager?.calculateSignalLevel(wifiInfo.rssi), + wifiInfo.ssid, + wifiInfo.isPasspointAp, + wifiInfo.isOsuAp, + wifiInfo.passpointProviderFriendlyName + ) + } } private fun prettyPrintActivity(activity: Int): String { diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/pipeline/wifi/domain/interactor/WifiInteractor.kt b/packages/SystemUI/src/com/android/systemui/statusbar/pipeline/wifi/domain/interactor/WifiInteractor.kt index a9da63b43aa4..afe19afff55f 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/pipeline/wifi/domain/interactor/WifiInteractor.kt +++ b/packages/SystemUI/src/com/android/systemui/statusbar/pipeline/wifi/domain/interactor/WifiInteractor.kt @@ -33,11 +33,12 @@ import kotlinx.coroutines.flow.map */ @SysUISingleton class WifiInteractor @Inject constructor( - repository: WifiRepository, + repository: WifiRepository, ) { private val ssid: Flow<String?> = repository.wifiNetwork.map { info -> when (info) { is WifiNetworkModel.Inactive -> null + is WifiNetworkModel.CarrierMerged -> null is WifiNetworkModel.Active -> when { info.isPasspointAccessPoint || info.isOnlineSignUpForPasspointAccessPoint -> info.passpointProviderFriendlyName @@ -47,6 +48,10 @@ class WifiInteractor @Inject constructor( } } + /** Our current wifi network. See [WifiNetworkModel]. */ + val wifiNetwork: Flow<WifiNetworkModel> = repository.wifiNetwork + + /** True if our wifi network has activity in (download), and false otherwise. */ val hasActivityIn: Flow<Boolean> = combine(repository.wifiActivity, ssid) { activity, ssid -> activity.hasActivityIn && ssid != null } diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/pipeline/wifi/ui/binder/WifiViewBinder.kt b/packages/SystemUI/src/com/android/systemui/statusbar/pipeline/wifi/ui/binder/WifiViewBinder.kt index b06aaf441f5a..7607ddf4fe8c 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/pipeline/wifi/ui/binder/WifiViewBinder.kt +++ b/packages/SystemUI/src/com/android/systemui/statusbar/pipeline/wifi/ui/binder/WifiViewBinder.kt @@ -25,10 +25,10 @@ import androidx.lifecycle.Lifecycle import androidx.lifecycle.repeatOnLifecycle import com.android.systemui.R import com.android.systemui.lifecycle.repeatWhenAttached -import com.android.systemui.statusbar.connectivity.WifiIcons.WIFI_FULL_ICONS import com.android.systemui.statusbar.pipeline.wifi.ui.viewmodel.WifiViewModel import kotlinx.coroutines.InternalCoroutinesApi import kotlinx.coroutines.flow.collect +import kotlinx.coroutines.flow.distinctUntilChanged import kotlinx.coroutines.launch /** @@ -50,11 +50,22 @@ object WifiViewBinder { view.isVisible = true iconView.isVisible = true - iconView.setImageDrawable(view.context.getDrawable(WIFI_FULL_ICONS[2])) view.repeatWhenAttached { repeatOnLifecycle(Lifecycle.State.STARTED) { launch { + viewModel.wifiIconResId.distinctUntilChanged().collect { iconResId -> + iconView.setImageDrawable( + if (iconResId != null && iconResId > 0) { + iconView.context.getDrawable(iconResId) + } else { + null + } + ) + } + } + + launch { viewModel.tint.collect { tint -> iconView.imageTintList = ColorStateList.valueOf(tint) } diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/pipeline/wifi/ui/viewmodel/WifiViewModel.kt b/packages/SystemUI/src/com/android/systemui/statusbar/pipeline/wifi/ui/viewmodel/WifiViewModel.kt index 7a262605681d..4fdcc44f1802 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/pipeline/wifi/ui/viewmodel/WifiViewModel.kt +++ b/packages/SystemUI/src/com/android/systemui/statusbar/pipeline/wifi/ui/viewmodel/WifiViewModel.kt @@ -17,20 +17,24 @@ package com.android.systemui.statusbar.pipeline.wifi.ui.viewmodel import android.graphics.Color +import androidx.annotation.DrawableRes +import com.android.systemui.statusbar.connectivity.WifiIcons.WIFI_FULL_ICONS +import com.android.systemui.statusbar.connectivity.WifiIcons.WIFI_NO_INTERNET_ICONS +import com.android.systemui.statusbar.connectivity.WifiIcons.WIFI_NO_NETWORK import com.android.systemui.statusbar.pipeline.StatusBarPipelineFlags import com.android.systemui.statusbar.pipeline.shared.ConnectivityPipelineLogger import com.android.systemui.statusbar.pipeline.shared.ConnectivityPipelineLogger.Companion.logOutputChange +import com.android.systemui.statusbar.pipeline.wifi.data.model.WifiNetworkModel import com.android.systemui.statusbar.pipeline.wifi.domain.interactor.WifiInteractor import com.android.systemui.statusbar.pipeline.wifi.shared.WifiConstants import javax.inject.Inject import kotlinx.coroutines.flow.Flow import kotlinx.coroutines.flow.emptyFlow import kotlinx.coroutines.flow.flowOf +import kotlinx.coroutines.flow.map /** * Models the UI state for the status bar wifi icon. - * - * TODO(b/238425913): Hook this up to the real status bar wifi view using a view binder. */ class WifiViewModel @Inject constructor( statusBarPipelineFlags: StatusBarPipelineFlags, @@ -38,6 +42,23 @@ class WifiViewModel @Inject constructor( private val logger: ConnectivityPipelineLogger, private val interactor: WifiInteractor, ) { + /** + * The drawable resource ID to use for the wifi icon. Null if we shouldn't display any icon. + */ + @DrawableRes + val wifiIconResId: Flow<Int?> = interactor.wifiNetwork.map { + when (it) { + is WifiNetworkModel.CarrierMerged -> null + is WifiNetworkModel.Inactive -> WIFI_NO_NETWORK + is WifiNetworkModel.Active -> + when { + it.level == null -> null + it.isValidated -> WIFI_FULL_ICONS[it.level] + else -> WIFI_NO_INTERNET_ICONS[it.level] + } + } + } + val isActivityInVisible: Flow<Boolean> get() = if (!constants.shouldShowActivityConfig) { diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/pipeline/wifi/data/model/WifiNetworkModelTest.kt b/packages/SystemUI/tests/src/com/android/systemui/statusbar/pipeline/wifi/data/model/WifiNetworkModelTest.kt new file mode 100644 index 000000000000..3d29d2b2c3c8 --- /dev/null +++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/pipeline/wifi/data/model/WifiNetworkModelTest.kt @@ -0,0 +1,54 @@ +/* + * 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.wifi.data.model + +import androidx.test.filters.SmallTest +import com.android.systemui.SysuiTestCase +import com.android.systemui.statusbar.pipeline.wifi.data.model.WifiNetworkModel.Active.Companion.MAX_VALID_LEVEL +import com.android.systemui.statusbar.pipeline.wifi.data.model.WifiNetworkModel.Active.Companion.MIN_VALID_LEVEL +import org.junit.Test + +@SmallTest +class WifiNetworkModelTest : SysuiTestCase() { + @Test + fun active_levelsInValidRange_noException() { + (MIN_VALID_LEVEL..MAX_VALID_LEVEL).forEach { level -> + WifiNetworkModel.Active(NETWORK_ID, level = level) + // No assert, just need no crash + } + } + + @Test + fun active_levelNull_noException() { + WifiNetworkModel.Active(NETWORK_ID, level = null) + // No assert, just need no crash + } + + @Test(expected = IllegalArgumentException::class) + fun active_levelNegative_exceptionThrown() { + WifiNetworkModel.Active(NETWORK_ID, level = MIN_VALID_LEVEL - 1) + } + + @Test(expected = IllegalArgumentException::class) + fun active_levelTooHigh_exceptionThrown() { + WifiNetworkModel.Active(NETWORK_ID, level = MAX_VALID_LEVEL + 1) + } + + companion object { + private const val NETWORK_ID = 2 + } +} diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/pipeline/wifi/data/repository/WifiRepositoryImplTest.kt b/packages/SystemUI/tests/src/com/android/systemui/statusbar/pipeline/wifi/data/repository/WifiRepositoryImplTest.kt index d0a38084af76..982927148a2e 100644 --- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/pipeline/wifi/data/repository/WifiRepositoryImplTest.kt +++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/pipeline/wifi/data/repository/WifiRepositoryImplTest.kt @@ -19,6 +19,7 @@ package com.android.systemui.statusbar.pipeline.wifi.data.repository import android.net.ConnectivityManager import android.net.Network import android.net.NetworkCapabilities +import android.net.NetworkCapabilities.NET_CAPABILITY_VALIDATED import android.net.NetworkCapabilities.TRANSPORT_CELLULAR import android.net.NetworkCapabilities.TRANSPORT_WIFI import android.net.vcn.VcnTransportInfo @@ -39,11 +40,14 @@ import com.android.systemui.util.mockito.mock import com.android.systemui.util.time.FakeSystemClock import com.google.common.truth.Truth.assertThat import java.util.concurrent.Executor +import kotlinx.coroutines.CoroutineScope import kotlinx.coroutines.Dispatchers import kotlinx.coroutines.ExperimentalCoroutinesApi +import kotlinx.coroutines.cancel 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.Mock @@ -61,20 +65,28 @@ class WifiRepositoryImplTest : SysuiTestCase() { @Mock private lateinit var connectivityManager: ConnectivityManager @Mock private lateinit var wifiManager: WifiManager private lateinit var executor: Executor + private lateinit var scope: CoroutineScope @Before fun setUp() { MockitoAnnotations.initMocks(this) executor = FakeExecutor(FakeSystemClock()) + scope = CoroutineScope(IMMEDIATE) underTest = WifiRepositoryImpl( connectivityManager, - wifiManager, - executor, logger, + executor, + scope, + wifiManager, ) } + @After + fun tearDown() { + scope.cancel() + } + @Test fun wifiNetwork_initiallyGetsDefault() = runBlocking(IMMEDIATE) { var latest: WifiNetworkModel? = null @@ -115,6 +127,61 @@ class WifiRepositoryImplTest : SysuiTestCase() { } @Test + fun wifiNetwork_isCarrierMerged_flowHasCarrierMerged() = runBlocking(IMMEDIATE) { + var latest: WifiNetworkModel? = null + val job = underTest + .wifiNetwork + .onEach { latest = it } + .launchIn(this) + + val wifiInfo = mock<WifiInfo>().apply { + whenever(this.ssid).thenReturn(SSID) + whenever(this.isPrimary).thenReturn(true) + whenever(this.isCarrierMerged).thenReturn(true) + } + + getNetworkCallback().onCapabilitiesChanged(NETWORK, createWifiNetworkCapabilities(wifiInfo)) + + assertThat(latest is WifiNetworkModel.CarrierMerged).isTrue() + + job.cancel() + } + + @Test + fun wifiNetwork_notValidated_networkNotValidated() = runBlocking(IMMEDIATE) { + var latest: WifiNetworkModel? = null + val job = underTest + .wifiNetwork + .onEach { latest = it } + .launchIn(this) + + getNetworkCallback().onCapabilitiesChanged( + NETWORK, createWifiNetworkCapabilities(PRIMARY_WIFI_INFO, isValidated = false) + ) + + assertThat((latest as WifiNetworkModel.Active).isValidated).isFalse() + + job.cancel() + } + + @Test + fun wifiNetwork_validated_networkValidated() = runBlocking(IMMEDIATE) { + var latest: WifiNetworkModel? = null + val job = underTest + .wifiNetwork + .onEach { latest = it } + .launchIn(this) + + getNetworkCallback().onCapabilitiesChanged( + NETWORK, createWifiNetworkCapabilities(PRIMARY_WIFI_INFO, isValidated = true) + ) + + assertThat((latest as WifiNetworkModel.Active).isValidated).isTrue() + + job.cancel() + } + + @Test fun wifiNetwork_nonPrimaryWifiNetworkAdded_flowHasNoNetwork() = runBlocking(IMMEDIATE) { var latest: WifiNetworkModel? = null val job = underTest @@ -282,13 +349,14 @@ class WifiRepositoryImplTest : SysuiTestCase() { .launchIn(this) val wifiInfo = mock<WifiInfo>().apply { - whenever(this.ssid).thenReturn(SSID) - whenever(this.isPrimary).thenReturn(true) - } + whenever(this.ssid).thenReturn(SSID) + whenever(this.isPrimary).thenReturn(true) + } // Start with the original network - getNetworkCallback() - .onCapabilitiesChanged(NETWORK, createWifiNetworkCapabilities(wifiInfo)) + getNetworkCallback().onCapabilitiesChanged( + NETWORK, createWifiNetworkCapabilities(wifiInfo, isValidated = true) + ) // WHEN we keep the same network ID but change the SSID val newSsid = "CD" @@ -297,14 +365,16 @@ class WifiRepositoryImplTest : SysuiTestCase() { whenever(this.isPrimary).thenReturn(true) } - getNetworkCallback() - .onCapabilitiesChanged(NETWORK, createWifiNetworkCapabilities(newWifiInfo)) + getNetworkCallback().onCapabilitiesChanged( + NETWORK, createWifiNetworkCapabilities(newWifiInfo, isValidated = false) + ) // THEN we've updated to the new SSID assertThat(latest is WifiNetworkModel.Active).isTrue() val latestActive = latest as WifiNetworkModel.Active assertThat(latestActive.networkId).isEqualTo(NETWORK_ID) assertThat(latestActive.ssid).isEqualTo(newSsid) + assertThat(latestActive.isValidated).isFalse() job.cancel() } @@ -406,10 +476,11 @@ class WifiRepositoryImplTest : SysuiTestCase() { @Test fun wifiActivity_nullWifiManager_receivesDefault() = runBlocking(IMMEDIATE) { underTest = WifiRepositoryImpl( - connectivityManager, - wifiManager = null, - executor, - logger, + connectivityManager, + logger, + executor, + scope, + wifiManager = null, ) var latest: WifiActivityModel? = null @@ -501,11 +572,16 @@ class WifiRepositoryImplTest : SysuiTestCase() { return callbackCaptor.value!! } - private fun createWifiNetworkCapabilities(wifiInfo: WifiInfo) = - mock<NetworkCapabilities>().apply { - whenever(this.hasTransport(TRANSPORT_WIFI)).thenReturn(true) - whenever(this.transportInfo).thenReturn(wifiInfo) + private fun createWifiNetworkCapabilities( + wifiInfo: WifiInfo, + isValidated: Boolean = true, + ): NetworkCapabilities { + return mock<NetworkCapabilities>().also { + whenever(it.hasTransport(TRANSPORT_WIFI)).thenReturn(true) + whenever(it.transportInfo).thenReturn(wifiInfo) + whenever(it.hasCapability(NET_CAPABILITY_VALIDATED)).thenReturn(isValidated) } + } private companion object { const val NETWORK_ID = 45 diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/pipeline/wifi/domain/interactor/WifiInteractorTest.kt b/packages/SystemUI/tests/src/com/android/systemui/statusbar/pipeline/wifi/domain/interactor/WifiInteractorTest.kt index 5f1b1dbb19dc..9d8b4bcabc8a 100644 --- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/pipeline/wifi/domain/interactor/WifiInteractorTest.kt +++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/pipeline/wifi/domain/interactor/WifiInteractorTest.kt @@ -126,6 +126,38 @@ class WifiInteractorTest : SysuiTestCase() { } @Test + fun hasActivityIn_inactiveNetwork_outputsFalse() = runBlocking(IMMEDIATE) { + repository.setWifiNetwork(WifiNetworkModel.Inactive) + repository.setWifiActivity(WifiActivityModel(hasActivityIn = true, hasActivityOut = true)) + + var latest: Boolean? = null + val job = underTest + .hasActivityIn + .onEach { latest = it } + .launchIn(this) + + assertThat(latest).isFalse() + + job.cancel() + } + + @Test + fun hasActivityIn_carrierMergedNetwork_outputsFalse() = runBlocking(IMMEDIATE) { + repository.setWifiNetwork(WifiNetworkModel.CarrierMerged) + repository.setWifiActivity(WifiActivityModel(hasActivityIn = true, hasActivityOut = true)) + + var latest: Boolean? = null + val job = underTest + .hasActivityIn + .onEach { latest = it } + .launchIn(this) + + assertThat(latest).isFalse() + + job.cancel() + } + + @Test fun hasActivityIn_multipleChanges_multipleOutputChanges() = runBlocking(IMMEDIATE) { repository.setWifiNetwork(VALID_WIFI_NETWORK_MODEL) @@ -159,6 +191,28 @@ class WifiInteractorTest : SysuiTestCase() { job.cancel() } + @Test + fun wifiNetwork_matchesRepoWifiNetwork() = runBlocking(IMMEDIATE) { + val wifiNetwork = WifiNetworkModel.Active( + networkId = 45, + isValidated = true, + level = 3, + ssid = "AB", + passpointProviderFriendlyName = "friendly" + ) + repository.setWifiNetwork(wifiNetwork) + + var latest: WifiNetworkModel? = null + val job = underTest + .wifiNetwork + .onEach { latest = it } + .launchIn(this) + + assertThat(latest).isEqualTo(wifiNetwork) + + job.cancel() + } + companion object { val VALID_WIFI_NETWORK_MODEL = WifiNetworkModel.Active(networkId = 1, ssid = "AB") } diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/pipeline/wifi/ui/viewmodel/WifiViewModelTest.kt b/packages/SystemUI/tests/src/com/android/systemui/statusbar/pipeline/wifi/ui/viewmodel/WifiViewModelTest.kt index c79073409883..f0a775b52297 100644 --- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/pipeline/wifi/ui/viewmodel/WifiViewModelTest.kt +++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/pipeline/wifi/ui/viewmodel/WifiViewModelTest.kt @@ -18,6 +18,9 @@ package com.android.systemui.statusbar.pipeline.wifi.ui.viewmodel import androidx.test.filters.SmallTest import com.android.systemui.SysuiTestCase +import com.android.systemui.statusbar.connectivity.WifiIcons.WIFI_FULL_ICONS +import com.android.systemui.statusbar.connectivity.WifiIcons.WIFI_NO_INTERNET_ICONS +import com.android.systemui.statusbar.connectivity.WifiIcons.WIFI_NO_NETWORK import com.android.systemui.statusbar.pipeline.StatusBarPipelineFlags import com.android.systemui.statusbar.pipeline.shared.ConnectivityPipelineLogger import com.android.systemui.statusbar.pipeline.wifi.data.model.WifiActivityModel @@ -62,14 +65,103 @@ class WifiViewModelTest : SysuiTestCase() { logger, interactor ) + } + + @Test + fun wifiIconResId_inactiveNetwork_outputsNoNetworkIcon() = runBlocking(IMMEDIATE) { + repository.setWifiNetwork(WifiNetworkModel.Inactive) + + var latest: Int? = null + val job = underTest + .wifiIconResId + .onEach { latest = it } + .launchIn(this) + + assertThat(latest).isEqualTo(WIFI_NO_NETWORK) + + job.cancel() + } + + @Test + fun wifiIconResId_carrierMergedNetwork_outputsNull() = runBlocking(IMMEDIATE) { + repository.setWifiNetwork(WifiNetworkModel.CarrierMerged) - // Set up with a valid SSID - repository.setWifiNetwork(WifiNetworkModel.Active(networkId = 1, ssid = "AB")) + var latest: Int? = null + val job = underTest + .wifiIconResId + .onEach { latest = it } + .launchIn(this) + + assertThat(latest).isNull() + + job.cancel() } @Test - fun activityInVisible_showActivityConfigFalse_receivesFalse() = runBlocking(IMMEDIATE) { + fun wifiIconResId_isActiveNullLevel_outputsNull() = runBlocking(IMMEDIATE) { + repository.setWifiNetwork(WifiNetworkModel.Active(NETWORK_ID, level = null)) + + var latest: Int? = null + val job = underTest + .wifiIconResId + .onEach { latest = it } + .launchIn(this) + + assertThat(latest).isNull() + + job.cancel() + } + + @Test + fun wifiIconResId_isActiveAndValidated_level1_outputsFull1Icon() = runBlocking(IMMEDIATE) { + val level = 1 + + repository.setWifiNetwork( + WifiNetworkModel.Active( + NETWORK_ID, + isValidated = true, + level = level + ) + ) + + var latest: Int? = null + val job = underTest + .wifiIconResId + .onEach { latest = it } + .launchIn(this) + + assertThat(latest).isEqualTo(WIFI_FULL_ICONS[level]) + + job.cancel() + } + + @Test + fun wifiIconResId_isActiveAndNotValidated_level4_outputsEmpty4Icon() = runBlocking(IMMEDIATE) { + val level = 4 + + repository.setWifiNetwork( + WifiNetworkModel.Active( + NETWORK_ID, + isValidated = false, + level = level + ) + ) + + var latest: Int? = null + val job = underTest + .wifiIconResId + .onEach { latest = it } + .launchIn(this) + + assertThat(latest).isEqualTo(WIFI_NO_INTERNET_ICONS[level]) + + job.cancel() + } + + @Test + fun activityInVisible_showActivityConfigFalse_outputsFalse() = runBlocking(IMMEDIATE) { whenever(constants.shouldShowActivityConfig).thenReturn(false) + repository.setWifiNetwork(ACTIVE_VALID_WIFI_NETWORK) var latest: Boolean? = null val job = underTest @@ -86,6 +178,7 @@ class WifiViewModelTest : SysuiTestCase() { @Test fun activityInVisible_showActivityConfigFalse_noUpdatesReceived() = runBlocking(IMMEDIATE) { whenever(constants.shouldShowActivityConfig).thenReturn(false) + repository.setWifiNetwork(ACTIVE_VALID_WIFI_NETWORK) var latest: Boolean? = null val job = underTest @@ -104,8 +197,9 @@ class WifiViewModelTest : SysuiTestCase() { } @Test - fun activityInVisible_showActivityConfigTrue_receivesUpdate() = runBlocking(IMMEDIATE) { + fun activityInVisible_showActivityConfigTrue_outputsUpdate() = runBlocking(IMMEDIATE) { whenever(constants.shouldShowActivityConfig).thenReturn(true) + repository.setWifiNetwork(ACTIVE_VALID_WIFI_NETWORK) var latest: Boolean? = null val job = underTest @@ -122,6 +216,11 @@ class WifiViewModelTest : SysuiTestCase() { job.cancel() } + + companion object { + private const val NETWORK_ID = 2 + private val ACTIVE_VALID_WIFI_NETWORK = WifiNetworkModel.Active(NETWORK_ID, ssid = "AB") + } } private val IMMEDIATE = Dispatchers.Main.immediate |