Merge "[SB Refactor] Implement the wifi icon flow in the new pipeline." into tm-qpr-dev

This commit is contained in:
Caitlin Shkuratov
2022-08-29 13:51:06 +00:00
committed by Android (Google) Code Review
10 changed files with 423 additions and 44 deletions

View File

@@ -31,7 +31,7 @@ public class WifiIcons {
com.android.internal.R.drawable.ic_wifi_signal_4 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_0,
R.drawable.ic_no_internet_wifi_signal_1, R.drawable.ic_no_internet_wifi_signal_1,
R.drawable.ic_no_internet_wifi_signal_2, 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_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; 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; static final int WIFI_LEVEL_COUNT = WIFI_SIGNAL_STRENGTH[0].length;

View File

@@ -16,13 +16,23 @@
package com.android.systemui.statusbar.pipeline.wifi.data.model package com.android.systemui.statusbar.pipeline.wifi.data.model
import androidx.annotation.VisibleForTesting
/** Provides information about the current wifi network. */ /** Provides information about the current wifi network. */
sealed class WifiNetworkModel { sealed class WifiNetworkModel {
/** A model representing that we have no active wifi network. */ /** A model representing that we have no active wifi network. */
object Inactive : WifiNetworkModel() 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. */ /** Provides information about an active wifi network. */
class Active( data class Active(
/** /**
* The [android.net.Network.netId] we received from * The [android.net.Network.netId] we received from
* [android.net.ConnectivityManager.NetworkCallback] in association with this wifi network. * [android.net.ConnectivityManager.NetworkCallback] in association with this wifi network.
@@ -31,6 +41,19 @@ sealed class WifiNetworkModel {
*/ */
val networkId: Int, 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]. */ /** See [android.net.wifi.WifiInfo.ssid]. */
val ssid: String? = null, val ssid: String? = null,
@@ -42,5 +65,18 @@ sealed class WifiNetworkModel {
/** See [android.net.wifi.WifiInfo.passpointProviderFriendlyName]. */ /** See [android.net.wifi.WifiInfo.passpointProviderFriendlyName]. */
val passpointProviderFriendlyName: String? = null, 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
}
}
} }

View File

@@ -21,6 +21,7 @@ import android.net.ConnectivityManager
import android.net.Network import android.net.Network
import android.net.NetworkCapabilities import android.net.NetworkCapabilities
import android.net.NetworkCapabilities.NET_CAPABILITY_NOT_VPN 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_CELLULAR
import android.net.NetworkCapabilities.TRANSPORT_WIFI import android.net.NetworkCapabilities.TRANSPORT_WIFI
import android.net.NetworkRequest import android.net.NetworkRequest
@@ -31,6 +32,7 @@ import android.util.Log
import com.android.settingslib.Utils import com.android.settingslib.Utils
import com.android.systemui.common.coroutine.ConflatedCallbackFlow.conflatedCallbackFlow import com.android.systemui.common.coroutine.ConflatedCallbackFlow.conflatedCallbackFlow
import com.android.systemui.dagger.SysUISingleton import com.android.systemui.dagger.SysUISingleton
import com.android.systemui.dagger.qualifiers.Application
import com.android.systemui.dagger.qualifiers.Main import com.android.systemui.dagger.qualifiers.Main
import com.android.systemui.statusbar.pipeline.shared.ConnectivityPipelineLogger import com.android.systemui.statusbar.pipeline.shared.ConnectivityPipelineLogger
import com.android.systemui.statusbar.pipeline.shared.ConnectivityPipelineLogger.Companion.SB_LOGGING_TAG import com.android.systemui.statusbar.pipeline.shared.ConnectivityPipelineLogger.Companion.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 com.android.systemui.statusbar.pipeline.wifi.data.model.WifiNetworkModel
import java.util.concurrent.Executor import java.util.concurrent.Executor
import javax.inject.Inject import javax.inject.Inject
import kotlinx.coroutines.CoroutineScope
import kotlinx.coroutines.ExperimentalCoroutinesApi import kotlinx.coroutines.ExperimentalCoroutinesApi
import kotlinx.coroutines.channels.awaitClose import kotlinx.coroutines.channels.awaitClose
import kotlinx.coroutines.flow.Flow import kotlinx.coroutines.flow.Flow
import kotlinx.coroutines.flow.SharingStarted
import kotlinx.coroutines.flow.flowOf import kotlinx.coroutines.flow.flowOf
import kotlinx.coroutines.flow.shareIn
/** /**
* Provides data related to the wifi state. * Provides data related to the wifi state.
@@ -63,10 +68,11 @@ interface WifiRepository {
@SysUISingleton @SysUISingleton
@SuppressLint("MissingPermission") @SuppressLint("MissingPermission")
class WifiRepositoryImpl @Inject constructor( class WifiRepositoryImpl @Inject constructor(
connectivityManager: ConnectivityManager, connectivityManager: ConnectivityManager,
wifiManager: WifiManager?, logger: ConnectivityPipelineLogger,
@Main mainExecutor: Executor, @Main mainExecutor: Executor,
logger: ConnectivityPipelineLogger, @Application scope: CoroutineScope,
wifiManager: WifiManager?,
) : WifiRepository { ) : WifiRepository {
override val wifiNetwork: Flow<WifiNetworkModel> = conflatedCallbackFlow { override val wifiNetwork: Flow<WifiNetworkModel> = conflatedCallbackFlow {
var currentWifi: WifiNetworkModel = WIFI_NETWORK_DEFAULT var currentWifi: WifiNetworkModel = WIFI_NETWORK_DEFAULT
@@ -80,7 +86,12 @@ class WifiRepositoryImpl @Inject constructor(
val wifiInfo = networkCapabilitiesToWifiInfo(networkCapabilities) val wifiInfo = networkCapabilitiesToWifiInfo(networkCapabilities)
if (wifiInfo?.isPrimary == true) { if (wifiInfo?.isPrimary == true) {
val wifiNetworkModel = wifiInfoToModel(wifiInfo, network.getNetId()) val wifiNetworkModel = createWifiNetworkModel(
wifiInfo,
network,
networkCapabilities,
wifiManager,
)
logger.logTransformation( logger.logTransformation(
WIFI_NETWORK_CALLBACK_NAME, WIFI_NETWORK_CALLBACK_NAME,
oldValue = currentWifi, oldValue = currentWifi,
@@ -112,6 +123,7 @@ class WifiRepositoryImpl @Inject constructor(
awaitClose { connectivityManager.unregisterNetworkCallback(callback) } awaitClose { connectivityManager.unregisterNetworkCallback(callback) }
} }
.shareIn(scope, started = SharingStarted.WhileSubscribed())
override val wifiActivity: Flow<WifiActivityModel> = override val wifiActivity: Flow<WifiActivityModel> =
if (wifiManager == null) { if (wifiManager == null) {
@@ -164,14 +176,25 @@ class WifiRepositoryImpl @Inject constructor(
} }
} }
private fun wifiInfoToModel(wifiInfo: WifiInfo, networkId: Int): WifiNetworkModel { private fun createWifiNetworkModel(
return WifiNetworkModel.Active( wifiInfo: WifiInfo,
networkId, network: Network,
wifiInfo.ssid, networkCapabilities: NetworkCapabilities,
wifiInfo.isPasspointAp, wifiManager: WifiManager?,
wifiInfo.isOsuAp, ): WifiNetworkModel {
wifiInfo.passpointProviderFriendlyName 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 { private fun prettyPrintActivity(activity: Int): String {

View File

@@ -33,11 +33,12 @@ import kotlinx.coroutines.flow.map
*/ */
@SysUISingleton @SysUISingleton
class WifiInteractor @Inject constructor( class WifiInteractor @Inject constructor(
repository: WifiRepository, repository: WifiRepository,
) { ) {
private val ssid: Flow<String?> = repository.wifiNetwork.map { info -> private val ssid: Flow<String?> = repository.wifiNetwork.map { info ->
when (info) { when (info) {
is WifiNetworkModel.Inactive -> null is WifiNetworkModel.Inactive -> null
is WifiNetworkModel.CarrierMerged -> null
is WifiNetworkModel.Active -> when { is WifiNetworkModel.Active -> when {
info.isPasspointAccessPoint || info.isOnlineSignUpForPasspointAccessPoint -> info.isPasspointAccessPoint || info.isOnlineSignUpForPasspointAccessPoint ->
info.passpointProviderFriendlyName 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 -> val hasActivityIn: Flow<Boolean> = combine(repository.wifiActivity, ssid) { activity, ssid ->
activity.hasActivityIn && ssid != null activity.hasActivityIn && ssid != null
} }

View File

@@ -25,10 +25,10 @@ import androidx.lifecycle.Lifecycle
import androidx.lifecycle.repeatOnLifecycle import androidx.lifecycle.repeatOnLifecycle
import com.android.systemui.R import com.android.systemui.R
import com.android.systemui.lifecycle.repeatWhenAttached 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 com.android.systemui.statusbar.pipeline.wifi.ui.viewmodel.WifiViewModel
import kotlinx.coroutines.InternalCoroutinesApi import kotlinx.coroutines.InternalCoroutinesApi
import kotlinx.coroutines.flow.collect import kotlinx.coroutines.flow.collect
import kotlinx.coroutines.flow.distinctUntilChanged
import kotlinx.coroutines.launch import kotlinx.coroutines.launch
/** /**
@@ -50,10 +50,21 @@ object WifiViewBinder {
view.isVisible = true view.isVisible = true
iconView.isVisible = true iconView.isVisible = true
iconView.setImageDrawable(view.context.getDrawable(WIFI_FULL_ICONS[2]))
view.repeatWhenAttached { view.repeatWhenAttached {
repeatOnLifecycle(Lifecycle.State.STARTED) { repeatOnLifecycle(Lifecycle.State.STARTED) {
launch {
viewModel.wifiIconResId.distinctUntilChanged().collect { iconResId ->
iconView.setImageDrawable(
if (iconResId != null && iconResId > 0) {
iconView.context.getDrawable(iconResId)
} else {
null
}
)
}
}
launch { launch {
viewModel.tint.collect { tint -> viewModel.tint.collect { tint ->
iconView.imageTintList = ColorStateList.valueOf(tint) iconView.imageTintList = ColorStateList.valueOf(tint)

View File

@@ -17,20 +17,24 @@
package com.android.systemui.statusbar.pipeline.wifi.ui.viewmodel package com.android.systemui.statusbar.pipeline.wifi.ui.viewmodel
import android.graphics.Color 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.StatusBarPipelineFlags
import com.android.systemui.statusbar.pipeline.shared.ConnectivityPipelineLogger 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.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.domain.interactor.WifiInteractor
import com.android.systemui.statusbar.pipeline.wifi.shared.WifiConstants import com.android.systemui.statusbar.pipeline.wifi.shared.WifiConstants
import javax.inject.Inject import javax.inject.Inject
import kotlinx.coroutines.flow.Flow import kotlinx.coroutines.flow.Flow
import kotlinx.coroutines.flow.emptyFlow import kotlinx.coroutines.flow.emptyFlow
import kotlinx.coroutines.flow.flowOf import kotlinx.coroutines.flow.flowOf
import kotlinx.coroutines.flow.map
/** /**
* Models the UI state for the status bar wifi icon. * 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( class WifiViewModel @Inject constructor(
statusBarPipelineFlags: StatusBarPipelineFlags, statusBarPipelineFlags: StatusBarPipelineFlags,
@@ -38,6 +42,23 @@ class WifiViewModel @Inject constructor(
private val logger: ConnectivityPipelineLogger, private val logger: ConnectivityPipelineLogger,
private val interactor: WifiInteractor, 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> val isActivityInVisible: Flow<Boolean>
get() = get() =
if (!constants.shouldShowActivityConfig) { if (!constants.shouldShowActivityConfig) {

View File

@@ -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
}
}

View File

@@ -19,6 +19,7 @@ package com.android.systemui.statusbar.pipeline.wifi.data.repository
import android.net.ConnectivityManager import android.net.ConnectivityManager
import android.net.Network import android.net.Network
import android.net.NetworkCapabilities import android.net.NetworkCapabilities
import android.net.NetworkCapabilities.NET_CAPABILITY_VALIDATED
import android.net.NetworkCapabilities.TRANSPORT_CELLULAR import android.net.NetworkCapabilities.TRANSPORT_CELLULAR
import android.net.NetworkCapabilities.TRANSPORT_WIFI import android.net.NetworkCapabilities.TRANSPORT_WIFI
import android.net.vcn.VcnTransportInfo 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.android.systemui.util.time.FakeSystemClock
import com.google.common.truth.Truth.assertThat import com.google.common.truth.Truth.assertThat
import java.util.concurrent.Executor import java.util.concurrent.Executor
import kotlinx.coroutines.CoroutineScope
import kotlinx.coroutines.Dispatchers import kotlinx.coroutines.Dispatchers
import kotlinx.coroutines.ExperimentalCoroutinesApi import kotlinx.coroutines.ExperimentalCoroutinesApi
import kotlinx.coroutines.cancel
import kotlinx.coroutines.flow.launchIn import kotlinx.coroutines.flow.launchIn
import kotlinx.coroutines.flow.onEach import kotlinx.coroutines.flow.onEach
import kotlinx.coroutines.runBlocking import kotlinx.coroutines.runBlocking
import org.junit.After
import org.junit.Before import org.junit.Before
import org.junit.Test import org.junit.Test
import org.mockito.Mock import org.mockito.Mock
@@ -61,20 +65,28 @@ class WifiRepositoryImplTest : SysuiTestCase() {
@Mock private lateinit var connectivityManager: ConnectivityManager @Mock private lateinit var connectivityManager: ConnectivityManager
@Mock private lateinit var wifiManager: WifiManager @Mock private lateinit var wifiManager: WifiManager
private lateinit var executor: Executor private lateinit var executor: Executor
private lateinit var scope: CoroutineScope
@Before @Before
fun setUp() { fun setUp() {
MockitoAnnotations.initMocks(this) MockitoAnnotations.initMocks(this)
executor = FakeExecutor(FakeSystemClock()) executor = FakeExecutor(FakeSystemClock())
scope = CoroutineScope(IMMEDIATE)
underTest = WifiRepositoryImpl( underTest = WifiRepositoryImpl(
connectivityManager, connectivityManager,
wifiManager,
executor,
logger, logger,
executor,
scope,
wifiManager,
) )
} }
@After
fun tearDown() {
scope.cancel()
}
@Test @Test
fun wifiNetwork_initiallyGetsDefault() = runBlocking(IMMEDIATE) { fun wifiNetwork_initiallyGetsDefault() = runBlocking(IMMEDIATE) {
var latest: WifiNetworkModel? = null var latest: WifiNetworkModel? = null
@@ -114,6 +126,61 @@ class WifiRepositoryImplTest : SysuiTestCase() {
job.cancel() job.cancel()
} }
@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 @Test
fun wifiNetwork_nonPrimaryWifiNetworkAdded_flowHasNoNetwork() = runBlocking(IMMEDIATE) { fun wifiNetwork_nonPrimaryWifiNetworkAdded_flowHasNoNetwork() = runBlocking(IMMEDIATE) {
var latest: WifiNetworkModel? = null var latest: WifiNetworkModel? = null
@@ -282,13 +349,14 @@ class WifiRepositoryImplTest : SysuiTestCase() {
.launchIn(this) .launchIn(this)
val wifiInfo = mock<WifiInfo>().apply { val wifiInfo = mock<WifiInfo>().apply {
whenever(this.ssid).thenReturn(SSID) whenever(this.ssid).thenReturn(SSID)
whenever(this.isPrimary).thenReturn(true) whenever(this.isPrimary).thenReturn(true)
} }
// Start with the original network // Start with the original network
getNetworkCallback() getNetworkCallback().onCapabilitiesChanged(
.onCapabilitiesChanged(NETWORK, createWifiNetworkCapabilities(wifiInfo)) NETWORK, createWifiNetworkCapabilities(wifiInfo, isValidated = true)
)
// WHEN we keep the same network ID but change the SSID // WHEN we keep the same network ID but change the SSID
val newSsid = "CD" val newSsid = "CD"
@@ -297,14 +365,16 @@ class WifiRepositoryImplTest : SysuiTestCase() {
whenever(this.isPrimary).thenReturn(true) whenever(this.isPrimary).thenReturn(true)
} }
getNetworkCallback() getNetworkCallback().onCapabilitiesChanged(
.onCapabilitiesChanged(NETWORK, createWifiNetworkCapabilities(newWifiInfo)) NETWORK, createWifiNetworkCapabilities(newWifiInfo, isValidated = false)
)
// THEN we've updated to the new SSID // THEN we've updated to the new SSID
assertThat(latest is WifiNetworkModel.Active).isTrue() assertThat(latest is WifiNetworkModel.Active).isTrue()
val latestActive = latest as WifiNetworkModel.Active val latestActive = latest as WifiNetworkModel.Active
assertThat(latestActive.networkId).isEqualTo(NETWORK_ID) assertThat(latestActive.networkId).isEqualTo(NETWORK_ID)
assertThat(latestActive.ssid).isEqualTo(newSsid) assertThat(latestActive.ssid).isEqualTo(newSsid)
assertThat(latestActive.isValidated).isFalse()
job.cancel() job.cancel()
} }
@@ -406,10 +476,11 @@ class WifiRepositoryImplTest : SysuiTestCase() {
@Test @Test
fun wifiActivity_nullWifiManager_receivesDefault() = runBlocking(IMMEDIATE) { fun wifiActivity_nullWifiManager_receivesDefault() = runBlocking(IMMEDIATE) {
underTest = WifiRepositoryImpl( underTest = WifiRepositoryImpl(
connectivityManager, connectivityManager,
wifiManager = null, logger,
executor, executor,
logger, scope,
wifiManager = null,
) )
var latest: WifiActivityModel? = null var latest: WifiActivityModel? = null
@@ -501,11 +572,16 @@ class WifiRepositoryImplTest : SysuiTestCase() {
return callbackCaptor.value!! return callbackCaptor.value!!
} }
private fun createWifiNetworkCapabilities(wifiInfo: WifiInfo) = private fun createWifiNetworkCapabilities(
mock<NetworkCapabilities>().apply { wifiInfo: WifiInfo,
whenever(this.hasTransport(TRANSPORT_WIFI)).thenReturn(true) isValidated: Boolean = true,
whenever(this.transportInfo).thenReturn(wifiInfo) ): 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 { private companion object {
const val NETWORK_ID = 45 const val NETWORK_ID = 45

View File

@@ -125,6 +125,38 @@ class WifiInteractorTest : SysuiTestCase() {
job.cancel() job.cancel()
} }
@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 @Test
fun hasActivityIn_multipleChanges_multipleOutputChanges() = runBlocking(IMMEDIATE) { fun hasActivityIn_multipleChanges_multipleOutputChanges() = runBlocking(IMMEDIATE) {
repository.setWifiNetwork(VALID_WIFI_NETWORK_MODEL) repository.setWifiNetwork(VALID_WIFI_NETWORK_MODEL)
@@ -159,6 +191,28 @@ class WifiInteractorTest : SysuiTestCase() {
job.cancel() 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 { companion object {
val VALID_WIFI_NETWORK_MODEL = WifiNetworkModel.Active(networkId = 1, ssid = "AB") val VALID_WIFI_NETWORK_MODEL = WifiNetworkModel.Active(networkId = 1, ssid = "AB")
} }

View File

@@ -18,6 +18,9 @@ package com.android.systemui.statusbar.pipeline.wifi.ui.viewmodel
import androidx.test.filters.SmallTest import androidx.test.filters.SmallTest
import com.android.systemui.SysuiTestCase 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.StatusBarPipelineFlags
import com.android.systemui.statusbar.pipeline.shared.ConnectivityPipelineLogger import com.android.systemui.statusbar.pipeline.shared.ConnectivityPipelineLogger
import com.android.systemui.statusbar.pipeline.wifi.data.model.WifiActivityModel import com.android.systemui.statusbar.pipeline.wifi.data.model.WifiActivityModel
@@ -62,14 +65,103 @@ class WifiViewModelTest : SysuiTestCase() {
logger, logger,
interactor interactor
) )
// Set up with a valid SSID
repository.setWifiNetwork(WifiNetworkModel.Active(networkId = 1, ssid = "AB"))
} }
@Test @Test
fun activityInVisible_showActivityConfigFalse_receivesFalse() = runBlocking(IMMEDIATE) { 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)
var latest: Int? = null
val job = underTest
.wifiIconResId
.onEach { latest = it }
.launchIn(this)
assertThat(latest).isNull()
job.cancel()
}
@Test
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) whenever(constants.shouldShowActivityConfig).thenReturn(false)
repository.setWifiNetwork(ACTIVE_VALID_WIFI_NETWORK)
var latest: Boolean? = null var latest: Boolean? = null
val job = underTest val job = underTest
@@ -86,6 +178,7 @@ class WifiViewModelTest : SysuiTestCase() {
@Test @Test
fun activityInVisible_showActivityConfigFalse_noUpdatesReceived() = runBlocking(IMMEDIATE) { fun activityInVisible_showActivityConfigFalse_noUpdatesReceived() = runBlocking(IMMEDIATE) {
whenever(constants.shouldShowActivityConfig).thenReturn(false) whenever(constants.shouldShowActivityConfig).thenReturn(false)
repository.setWifiNetwork(ACTIVE_VALID_WIFI_NETWORK)
var latest: Boolean? = null var latest: Boolean? = null
val job = underTest val job = underTest
@@ -104,8 +197,9 @@ class WifiViewModelTest : SysuiTestCase() {
} }
@Test @Test
fun activityInVisible_showActivityConfigTrue_receivesUpdate() = runBlocking(IMMEDIATE) { fun activityInVisible_showActivityConfigTrue_outputsUpdate() = runBlocking(IMMEDIATE) {
whenever(constants.shouldShowActivityConfig).thenReturn(true) whenever(constants.shouldShowActivityConfig).thenReturn(true)
repository.setWifiNetwork(ACTIVE_VALID_WIFI_NETWORK)
var latest: Boolean? = null var latest: Boolean? = null
val job = underTest val job = underTest
@@ -122,6 +216,11 @@ class WifiViewModelTest : SysuiTestCase() {
job.cancel() 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 private val IMMEDIATE = Dispatchers.Main.immediate