Merge "[SB Refactor] Implement the TunerService callback for the icon hide list and pipe it through to the wifi icon." into tm-qpr-dev
This commit is contained in:
committed by
Android (Google) Code Review
commit
e04e312e98
@@ -18,6 +18,8 @@ package com.android.systemui.statusbar.pipeline.dagger
|
|||||||
|
|
||||||
import com.android.systemui.CoreStartable
|
import com.android.systemui.CoreStartable
|
||||||
import com.android.systemui.statusbar.pipeline.ConnectivityInfoProcessor
|
import com.android.systemui.statusbar.pipeline.ConnectivityInfoProcessor
|
||||||
|
import com.android.systemui.statusbar.pipeline.shared.data.repository.ConnectivityRepository
|
||||||
|
import com.android.systemui.statusbar.pipeline.shared.data.repository.ConnectivityRepositoryImpl
|
||||||
import com.android.systemui.statusbar.pipeline.wifi.data.repository.WifiRepository
|
import com.android.systemui.statusbar.pipeline.wifi.data.repository.WifiRepository
|
||||||
import com.android.systemui.statusbar.pipeline.wifi.data.repository.WifiRepositoryImpl
|
import com.android.systemui.statusbar.pipeline.wifi.data.repository.WifiRepositoryImpl
|
||||||
import dagger.Binds
|
import dagger.Binds
|
||||||
@@ -33,6 +35,9 @@ abstract class StatusBarPipelineModule {
|
|||||||
@ClassKey(ConnectivityInfoProcessor::class)
|
@ClassKey(ConnectivityInfoProcessor::class)
|
||||||
abstract fun bindConnectivityInfoProcessor(cip: ConnectivityInfoProcessor): CoreStartable
|
abstract fun bindConnectivityInfoProcessor(cip: ConnectivityInfoProcessor): CoreStartable
|
||||||
|
|
||||||
|
@Binds
|
||||||
|
abstract fun connectivityRepository(impl: ConnectivityRepositoryImpl): ConnectivityRepository
|
||||||
|
|
||||||
@Binds
|
@Binds
|
||||||
abstract fun wifiRepository(impl: WifiRepositoryImpl): WifiRepository
|
abstract fun wifiRepository(impl: WifiRepositoryImpl): WifiRepository
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -34,7 +34,7 @@ class ConnectivityPipelineLogger @Inject constructor(
|
|||||||
/**
|
/**
|
||||||
* Logs a change in one of the **raw inputs** to the connectivity pipeline.
|
* Logs a change in one of the **raw inputs** to the connectivity pipeline.
|
||||||
*/
|
*/
|
||||||
fun logInputChange(callbackName: String, changeInfo: String) {
|
fun logInputChange(callbackName: String, changeInfo: String?) {
|
||||||
buffer.log(
|
buffer.log(
|
||||||
SB_LOGGING_TAG,
|
SB_LOGGING_TAG,
|
||||||
LogLevel.INFO,
|
LogLevel.INFO,
|
||||||
|
|||||||
@@ -0,0 +1,56 @@
|
|||||||
|
/*
|
||||||
|
* 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.shared.data.model
|
||||||
|
|
||||||
|
import android.content.Context
|
||||||
|
import com.android.internal.R
|
||||||
|
import com.android.systemui.dagger.SysUISingleton
|
||||||
|
import javax.inject.Inject
|
||||||
|
|
||||||
|
/**
|
||||||
|
* A container for all the different types of connectivity slots: wifi, mobile, etc.
|
||||||
|
*/
|
||||||
|
@SysUISingleton
|
||||||
|
class ConnectivitySlots @Inject constructor(context: Context) {
|
||||||
|
private val airplaneSlot: String = context.getString(R.string.status_bar_airplane)
|
||||||
|
private val mobileSlot: String = context.getString(R.string.status_bar_mobile)
|
||||||
|
private val wifiSlot: String = context.getString(R.string.status_bar_wifi)
|
||||||
|
private val ethernetSlot: String = context.getString(R.string.status_bar_ethernet)
|
||||||
|
|
||||||
|
private val slotByName: Map<String, ConnectivitySlot> = mapOf(
|
||||||
|
airplaneSlot to ConnectivitySlot.AIRPLANE,
|
||||||
|
mobileSlot to ConnectivitySlot.MOBILE,
|
||||||
|
wifiSlot to ConnectivitySlot.WIFI,
|
||||||
|
ethernetSlot to ConnectivitySlot.ETHERNET
|
||||||
|
)
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Given a string name of a slot, returns the instance of [ConnectivitySlot] that it corresponds
|
||||||
|
* to, or null if we couldn't find that slot name.
|
||||||
|
*/
|
||||||
|
fun getSlotFromName(slotName: String): ConnectivitySlot? {
|
||||||
|
return slotByName[slotName]
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/** The different types of connectivity slots. */
|
||||||
|
enum class ConnectivitySlot {
|
||||||
|
AIRPLANE,
|
||||||
|
ETHERNET,
|
||||||
|
MOBILE,
|
||||||
|
WIFI,
|
||||||
|
}
|
||||||
@@ -0,0 +1,120 @@
|
|||||||
|
/*
|
||||||
|
* 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.shared.data.repository
|
||||||
|
|
||||||
|
import android.content.Context
|
||||||
|
import androidx.annotation.ArrayRes
|
||||||
|
import androidx.annotation.VisibleForTesting
|
||||||
|
import com.android.systemui.Dumpable
|
||||||
|
import com.android.systemui.R
|
||||||
|
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.dump.DumpManager
|
||||||
|
import com.android.systemui.statusbar.phone.StatusBarIconController
|
||||||
|
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.data.model.ConnectivitySlot
|
||||||
|
import com.android.systemui.statusbar.pipeline.shared.data.model.ConnectivitySlots
|
||||||
|
import com.android.systemui.tuner.TunerService
|
||||||
|
import java.io.PrintWriter
|
||||||
|
import javax.inject.Inject
|
||||||
|
import kotlinx.coroutines.CoroutineScope
|
||||||
|
import kotlinx.coroutines.ExperimentalCoroutinesApi
|
||||||
|
import kotlinx.coroutines.channels.awaitClose
|
||||||
|
import kotlinx.coroutines.flow.SharingStarted
|
||||||
|
import kotlinx.coroutines.flow.StateFlow
|
||||||
|
import kotlinx.coroutines.flow.stateIn
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Provides data related to the connectivity state that needs to be shared across multiple different
|
||||||
|
* types of connectivity (wifi, mobile, ethernet, etc.)
|
||||||
|
*/
|
||||||
|
interface ConnectivityRepository {
|
||||||
|
/**
|
||||||
|
* Observable for the current set of connectivity icons that should be force-hidden.
|
||||||
|
*/
|
||||||
|
val forceHiddenSlots: StateFlow<Set<ConnectivitySlot>>
|
||||||
|
}
|
||||||
|
|
||||||
|
@OptIn(ExperimentalCoroutinesApi::class)
|
||||||
|
@SysUISingleton
|
||||||
|
class ConnectivityRepositoryImpl @Inject constructor(
|
||||||
|
private val connectivitySlots: ConnectivitySlots,
|
||||||
|
context: Context,
|
||||||
|
dumpManager: DumpManager,
|
||||||
|
logger: ConnectivityPipelineLogger,
|
||||||
|
@Application scope: CoroutineScope,
|
||||||
|
tunerService: TunerService,
|
||||||
|
) : ConnectivityRepository, Dumpable {
|
||||||
|
init {
|
||||||
|
dumpManager.registerDumpable("$SB_LOGGING_TAG:ConnectivityRepository", this)
|
||||||
|
}
|
||||||
|
|
||||||
|
// The default set of hidden icons to use if we don't get any from [TunerService].
|
||||||
|
private val defaultHiddenIcons: Set<ConnectivitySlot> =
|
||||||
|
context.resources.getStringArray(DEFAULT_HIDDEN_ICONS_RESOURCE)
|
||||||
|
.asList()
|
||||||
|
.toSlotSet(connectivitySlots)
|
||||||
|
|
||||||
|
override val forceHiddenSlots: StateFlow<Set<ConnectivitySlot>> = conflatedCallbackFlow {
|
||||||
|
val callback = object : TunerService.Tunable {
|
||||||
|
override fun onTuningChanged(key: String, newHideList: String?) {
|
||||||
|
if (key != HIDDEN_ICONS_TUNABLE_KEY) {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
logger.logInputChange("onTuningChanged", newHideList)
|
||||||
|
|
||||||
|
val outputList = newHideList?.split(",")?.toSlotSet(connectivitySlots)
|
||||||
|
?: defaultHiddenIcons
|
||||||
|
trySend(outputList)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
tunerService.addTunable(callback, HIDDEN_ICONS_TUNABLE_KEY)
|
||||||
|
|
||||||
|
awaitClose { tunerService.removeTunable(callback) }
|
||||||
|
}
|
||||||
|
.stateIn(
|
||||||
|
scope,
|
||||||
|
started = SharingStarted.WhileSubscribed(),
|
||||||
|
initialValue = defaultHiddenIcons
|
||||||
|
)
|
||||||
|
|
||||||
|
override fun dump(pw: PrintWriter, args: Array<out String>) {
|
||||||
|
pw.apply {
|
||||||
|
println("defaultHiddenIcons=$defaultHiddenIcons")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
companion object {
|
||||||
|
@VisibleForTesting
|
||||||
|
internal const val HIDDEN_ICONS_TUNABLE_KEY = StatusBarIconController.ICON_HIDE_LIST
|
||||||
|
@VisibleForTesting
|
||||||
|
@ArrayRes
|
||||||
|
internal val DEFAULT_HIDDEN_ICONS_RESOURCE = R.array.config_statusBarIconsToExclude
|
||||||
|
|
||||||
|
/** Converts a list of string slot names to a set of [ConnectivitySlot] instances. */
|
||||||
|
private fun List<String>.toSlotSet(
|
||||||
|
connectivitySlots: ConnectivitySlots
|
||||||
|
): Set<ConnectivitySlot> {
|
||||||
|
return this
|
||||||
|
.filter { it.isNotBlank() }
|
||||||
|
.mapNotNull { connectivitySlots.getSlotFromName(it) }
|
||||||
|
.toSet()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -18,6 +18,8 @@ package com.android.systemui.statusbar.pipeline.wifi.domain.interactor
|
|||||||
|
|
||||||
import android.net.wifi.WifiManager
|
import android.net.wifi.WifiManager
|
||||||
import com.android.systemui.dagger.SysUISingleton
|
import com.android.systemui.dagger.SysUISingleton
|
||||||
|
import com.android.systemui.statusbar.pipeline.shared.data.model.ConnectivitySlot
|
||||||
|
import com.android.systemui.statusbar.pipeline.shared.data.repository.ConnectivityRepository
|
||||||
import com.android.systemui.statusbar.pipeline.wifi.data.model.WifiNetworkModel
|
import com.android.systemui.statusbar.pipeline.wifi.data.model.WifiNetworkModel
|
||||||
import com.android.systemui.statusbar.pipeline.wifi.data.repository.WifiRepository
|
import com.android.systemui.statusbar.pipeline.wifi.data.repository.WifiRepository
|
||||||
import javax.inject.Inject
|
import javax.inject.Inject
|
||||||
@@ -33,9 +35,10 @@ import kotlinx.coroutines.flow.map
|
|||||||
*/
|
*/
|
||||||
@SysUISingleton
|
@SysUISingleton
|
||||||
class WifiInteractor @Inject constructor(
|
class WifiInteractor @Inject constructor(
|
||||||
repository: WifiRepository,
|
connectivityRepository: ConnectivityRepository,
|
||||||
|
wifiRepository: WifiRepository,
|
||||||
) {
|
) {
|
||||||
private val ssid: Flow<String?> = repository.wifiNetwork.map { info ->
|
private val ssid: Flow<String?> = wifiRepository.wifiNetwork.map { info ->
|
||||||
when (info) {
|
when (info) {
|
||||||
is WifiNetworkModel.Inactive -> null
|
is WifiNetworkModel.Inactive -> null
|
||||||
is WifiNetworkModel.CarrierMerged -> null
|
is WifiNetworkModel.CarrierMerged -> null
|
||||||
@@ -49,10 +52,16 @@ class WifiInteractor @Inject constructor(
|
|||||||
}
|
}
|
||||||
|
|
||||||
/** Our current wifi network. See [WifiNetworkModel]. */
|
/** Our current wifi network. See [WifiNetworkModel]. */
|
||||||
val wifiNetwork: Flow<WifiNetworkModel> = repository.wifiNetwork
|
val wifiNetwork: Flow<WifiNetworkModel> = wifiRepository.wifiNetwork
|
||||||
|
|
||||||
|
/** True if we're configured to force-hide the wifi icon and false otherwise. */
|
||||||
|
val isForceHidden: Flow<Boolean> = connectivityRepository.forceHiddenSlots.map {
|
||||||
|
it.contains(ConnectivitySlot.WIFI)
|
||||||
|
}
|
||||||
|
|
||||||
/** True if our wifi network has activity in (download), and false otherwise. */
|
/** 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(wifiRepository.wifiActivity, ssid) { activity, ssid ->
|
||||||
activity.hasActivityIn && ssid != null
|
activity.hasActivityIn && ssid != null
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -24,6 +24,7 @@ import androidx.core.view.isVisible
|
|||||||
import androidx.lifecycle.Lifecycle
|
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.common.ui.binder.IconViewBinder
|
||||||
import com.android.systemui.lifecycle.repeatWhenAttached
|
import com.android.systemui.lifecycle.repeatWhenAttached
|
||||||
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
|
||||||
@@ -54,14 +55,15 @@ object WifiViewBinder {
|
|||||||
view.repeatWhenAttached {
|
view.repeatWhenAttached {
|
||||||
repeatOnLifecycle(Lifecycle.State.STARTED) {
|
repeatOnLifecycle(Lifecycle.State.STARTED) {
|
||||||
launch {
|
launch {
|
||||||
viewModel.wifiIconResId.distinctUntilChanged().collect { iconResId ->
|
viewModel.wifiIcon.distinctUntilChanged().collect { wifiIcon ->
|
||||||
iconView.setImageDrawable(
|
// TODO(b/238425913): Right now, if !isVisible, there's just an empty space
|
||||||
if (iconResId != null && iconResId > 0) {
|
// where the wifi icon would be. We need to pipe isVisible through to
|
||||||
iconView.context.getDrawable(iconResId)
|
// [ModernStatusBarWifiView.isIconVisible], which is what actually makes
|
||||||
} else {
|
// the view GONE.
|
||||||
null
|
view.isVisible = wifiIcon != null
|
||||||
}
|
wifiIcon?.let {
|
||||||
)
|
IconViewBinder.bind(wifiIcon, iconView)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -18,6 +18,7 @@ package com.android.systemui.statusbar.pipeline.wifi.ui.viewmodel
|
|||||||
|
|
||||||
import android.graphics.Color
|
import android.graphics.Color
|
||||||
import androidx.annotation.DrawableRes
|
import androidx.annotation.DrawableRes
|
||||||
|
import com.android.systemui.common.shared.model.Icon
|
||||||
import com.android.systemui.statusbar.connectivity.WifiIcons.WIFI_FULL_ICONS
|
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_INTERNET_ICONS
|
||||||
import com.android.systemui.statusbar.connectivity.WifiIcons.WIFI_NO_NETWORK
|
import com.android.systemui.statusbar.connectivity.WifiIcons.WIFI_NO_NETWORK
|
||||||
@@ -29,6 +30,7 @@ import com.android.systemui.statusbar.pipeline.wifi.domain.interactor.WifiIntera
|
|||||||
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.combine
|
||||||
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
|
import kotlinx.coroutines.flow.map
|
||||||
@@ -46,7 +48,7 @@ class WifiViewModel @Inject constructor(
|
|||||||
* The drawable resource ID to use for the wifi icon. Null if we shouldn't display any icon.
|
* The drawable resource ID to use for the wifi icon. Null if we shouldn't display any icon.
|
||||||
*/
|
*/
|
||||||
@DrawableRes
|
@DrawableRes
|
||||||
val wifiIconResId: Flow<Int?> = interactor.wifiNetwork.map {
|
private val iconResId: Flow<Int?> = interactor.wifiNetwork.map {
|
||||||
when (it) {
|
when (it) {
|
||||||
is WifiNetworkModel.CarrierMerged -> null
|
is WifiNetworkModel.CarrierMerged -> null
|
||||||
is WifiNetworkModel.Inactive -> WIFI_NO_NETWORK
|
is WifiNetworkModel.Inactive -> WIFI_NO_NETWORK
|
||||||
@@ -59,6 +61,24 @@ class WifiViewModel @Inject constructor(
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* The wifi icon that should be displayed. Null if we shouldn't display any icon.
|
||||||
|
*/
|
||||||
|
val wifiIcon: Flow<Icon?> = combine(
|
||||||
|
interactor.isForceHidden,
|
||||||
|
iconResId
|
||||||
|
) { isForceHidden, iconResId ->
|
||||||
|
when {
|
||||||
|
isForceHidden ||
|
||||||
|
iconResId == null ||
|
||||||
|
iconResId <= 0 -> null
|
||||||
|
else -> Icon.Resource(iconResId)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* True if the activity in icon should be displayed and false otherwise.
|
||||||
|
*/
|
||||||
val isActivityInVisible: Flow<Boolean>
|
val isActivityInVisible: Flow<Boolean>
|
||||||
get() =
|
get() =
|
||||||
if (!constants.shouldShowActivityConfig) {
|
if (!constants.shouldShowActivityConfig) {
|
||||||
|
|||||||
@@ -0,0 +1,295 @@
|
|||||||
|
/*
|
||||||
|
* 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.shared.data.repository
|
||||||
|
|
||||||
|
import androidx.test.filters.SmallTest
|
||||||
|
import com.android.systemui.SysuiTestCase
|
||||||
|
import com.android.systemui.dump.DumpManager
|
||||||
|
import com.android.systemui.statusbar.pipeline.shared.ConnectivityPipelineLogger
|
||||||
|
import com.android.systemui.statusbar.pipeline.shared.data.model.ConnectivitySlot
|
||||||
|
import com.android.systemui.statusbar.pipeline.shared.data.model.ConnectivitySlots
|
||||||
|
import com.android.systemui.statusbar.pipeline.shared.data.repository.ConnectivityRepositoryImpl.Companion.DEFAULT_HIDDEN_ICONS_RESOURCE
|
||||||
|
import com.android.systemui.statusbar.pipeline.shared.data.repository.ConnectivityRepositoryImpl.Companion.HIDDEN_ICONS_TUNABLE_KEY
|
||||||
|
import com.android.systemui.tuner.TunerService
|
||||||
|
import com.android.systemui.util.mockito.any
|
||||||
|
import com.android.systemui.util.mockito.argumentCaptor
|
||||||
|
import com.google.common.truth.Truth.assertThat
|
||||||
|
import kotlinx.coroutines.CoroutineScope
|
||||||
|
import kotlinx.coroutines.Dispatchers
|
||||||
|
import kotlinx.coroutines.ExperimentalCoroutinesApi
|
||||||
|
import kotlinx.coroutines.cancel
|
||||||
|
import kotlinx.coroutines.flow.launchIn
|
||||||
|
import kotlinx.coroutines.flow.onEach
|
||||||
|
import kotlinx.coroutines.runBlocking
|
||||||
|
import kotlinx.coroutines.yield
|
||||||
|
import org.junit.After
|
||||||
|
import org.junit.Before
|
||||||
|
import org.junit.Test
|
||||||
|
import org.mockito.Mock
|
||||||
|
import org.mockito.Mockito
|
||||||
|
import org.mockito.Mockito.`when` as whenever
|
||||||
|
import org.mockito.MockitoAnnotations
|
||||||
|
|
||||||
|
@OptIn(ExperimentalCoroutinesApi::class)
|
||||||
|
@SmallTest
|
||||||
|
class ConnectivityRepositoryImplTest : SysuiTestCase() {
|
||||||
|
|
||||||
|
private lateinit var underTest: ConnectivityRepositoryImpl
|
||||||
|
|
||||||
|
@Mock private lateinit var connectivitySlots: ConnectivitySlots
|
||||||
|
@Mock private lateinit var dumpManager: DumpManager
|
||||||
|
@Mock private lateinit var logger: ConnectivityPipelineLogger
|
||||||
|
private lateinit var scope: CoroutineScope
|
||||||
|
@Mock private lateinit var tunerService: TunerService
|
||||||
|
|
||||||
|
@Before
|
||||||
|
fun setUp() {
|
||||||
|
MockitoAnnotations.initMocks(this)
|
||||||
|
scope = CoroutineScope(IMMEDIATE)
|
||||||
|
|
||||||
|
underTest = ConnectivityRepositoryImpl(
|
||||||
|
connectivitySlots,
|
||||||
|
context,
|
||||||
|
dumpManager,
|
||||||
|
logger,
|
||||||
|
scope,
|
||||||
|
tunerService,
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
@After
|
||||||
|
fun tearDown() {
|
||||||
|
scope.cancel()
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
fun forceHiddenSlots_initiallyGetsDefault() = runBlocking(IMMEDIATE) {
|
||||||
|
setUpEthernetWifiMobileSlotNames()
|
||||||
|
context.getOrCreateTestableResources().addOverride(
|
||||||
|
DEFAULT_HIDDEN_ICONS_RESOURCE,
|
||||||
|
arrayOf(SLOT_WIFI, SLOT_ETHERNET)
|
||||||
|
)
|
||||||
|
// Re-create our [ConnectivityRepositoryImpl], since it fetches
|
||||||
|
// config_statusBarIconsToExclude when it's first constructed
|
||||||
|
underTest = ConnectivityRepositoryImpl(
|
||||||
|
connectivitySlots,
|
||||||
|
context,
|
||||||
|
dumpManager,
|
||||||
|
logger,
|
||||||
|
scope,
|
||||||
|
tunerService,
|
||||||
|
)
|
||||||
|
|
||||||
|
var latest: Set<ConnectivitySlot>? = null
|
||||||
|
val job = underTest
|
||||||
|
.forceHiddenSlots
|
||||||
|
.onEach { latest = it }
|
||||||
|
.launchIn(this)
|
||||||
|
|
||||||
|
assertThat(latest).containsExactly(ConnectivitySlot.ETHERNET, ConnectivitySlot.WIFI)
|
||||||
|
|
||||||
|
job.cancel()
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
fun forceHiddenSlots_slotNamesAdded_flowHasSlots() = runBlocking(IMMEDIATE) {
|
||||||
|
setUpEthernetWifiMobileSlotNames()
|
||||||
|
|
||||||
|
var latest: Set<ConnectivitySlot>? = null
|
||||||
|
val job = underTest
|
||||||
|
.forceHiddenSlots
|
||||||
|
.onEach { latest = it }
|
||||||
|
.launchIn(this)
|
||||||
|
|
||||||
|
getTunable().onTuningChanged(HIDDEN_ICONS_TUNABLE_KEY, SLOT_MOBILE)
|
||||||
|
|
||||||
|
assertThat(latest).containsExactly(ConnectivitySlot.MOBILE)
|
||||||
|
|
||||||
|
job.cancel()
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
fun forceHiddenSlots_wrongKey_doesNotUpdate() = runBlocking(IMMEDIATE) {
|
||||||
|
setUpEthernetWifiMobileSlotNames()
|
||||||
|
|
||||||
|
var latest: Set<ConnectivitySlot>? = null
|
||||||
|
val job = underTest
|
||||||
|
.forceHiddenSlots
|
||||||
|
.onEach { latest = it }
|
||||||
|
.launchIn(this)
|
||||||
|
|
||||||
|
getTunable().onTuningChanged(HIDDEN_ICONS_TUNABLE_KEY, SLOT_MOBILE)
|
||||||
|
|
||||||
|
// WHEN onTuningChanged with the wrong key
|
||||||
|
getTunable().onTuningChanged("wrongKey", SLOT_WIFI)
|
||||||
|
yield()
|
||||||
|
|
||||||
|
// THEN we didn't update our value and still have the old one
|
||||||
|
assertThat(latest).containsExactly(ConnectivitySlot.MOBILE)
|
||||||
|
|
||||||
|
job.cancel()
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
fun forceHiddenSlots_slotNamesAddedThenNull_flowHasDefault() = runBlocking(IMMEDIATE) {
|
||||||
|
setUpEthernetWifiMobileSlotNames()
|
||||||
|
context.getOrCreateTestableResources().addOverride(
|
||||||
|
DEFAULT_HIDDEN_ICONS_RESOURCE,
|
||||||
|
arrayOf(SLOT_WIFI, SLOT_ETHERNET)
|
||||||
|
)
|
||||||
|
// Re-create our [ConnectivityRepositoryImpl], since it fetches
|
||||||
|
// config_statusBarIconsToExclude when it's first constructed
|
||||||
|
underTest = ConnectivityRepositoryImpl(
|
||||||
|
connectivitySlots,
|
||||||
|
context,
|
||||||
|
dumpManager,
|
||||||
|
logger,
|
||||||
|
scope,
|
||||||
|
tunerService,
|
||||||
|
)
|
||||||
|
|
||||||
|
var latest: Set<ConnectivitySlot>? = null
|
||||||
|
val job = underTest
|
||||||
|
.forceHiddenSlots
|
||||||
|
.onEach { latest = it }
|
||||||
|
.launchIn(this)
|
||||||
|
|
||||||
|
// First, update the slots
|
||||||
|
getTunable().onTuningChanged(HIDDEN_ICONS_TUNABLE_KEY, SLOT_MOBILE)
|
||||||
|
assertThat(latest).containsExactly(ConnectivitySlot.MOBILE)
|
||||||
|
|
||||||
|
// WHEN we update to a null value
|
||||||
|
getTunable().onTuningChanged(HIDDEN_ICONS_TUNABLE_KEY, null)
|
||||||
|
yield()
|
||||||
|
|
||||||
|
// THEN we go back to our default value
|
||||||
|
assertThat(latest).containsExactly(ConnectivitySlot.ETHERNET, ConnectivitySlot.WIFI)
|
||||||
|
|
||||||
|
job.cancel()
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
fun forceHiddenSlots_someInvalidSlotNames_flowHasValidSlotsOnly() = runBlocking(IMMEDIATE) {
|
||||||
|
var latest: Set<ConnectivitySlot>? = null
|
||||||
|
val job = underTest
|
||||||
|
.forceHiddenSlots
|
||||||
|
.onEach { latest = it }
|
||||||
|
.launchIn(this)
|
||||||
|
|
||||||
|
whenever(connectivitySlots.getSlotFromName(SLOT_WIFI))
|
||||||
|
.thenReturn(ConnectivitySlot.WIFI)
|
||||||
|
whenever(connectivitySlots.getSlotFromName(SLOT_MOBILE)).thenReturn(null)
|
||||||
|
|
||||||
|
getTunable().onTuningChanged(HIDDEN_ICONS_TUNABLE_KEY, "$SLOT_WIFI,$SLOT_MOBILE")
|
||||||
|
|
||||||
|
assertThat(latest).containsExactly(ConnectivitySlot.WIFI)
|
||||||
|
|
||||||
|
job.cancel()
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
fun forceHiddenSlots_someEmptySlotNames_flowHasValidSlotsOnly() = runBlocking(IMMEDIATE) {
|
||||||
|
setUpEthernetWifiMobileSlotNames()
|
||||||
|
|
||||||
|
var latest: Set<ConnectivitySlot>? = null
|
||||||
|
val job = underTest
|
||||||
|
.forceHiddenSlots
|
||||||
|
.onEach { latest = it }
|
||||||
|
.launchIn(this)
|
||||||
|
|
||||||
|
// WHEN there's empty and blank slot names
|
||||||
|
getTunable().onTuningChanged(
|
||||||
|
HIDDEN_ICONS_TUNABLE_KEY, "$SLOT_MOBILE, ,,$SLOT_WIFI"
|
||||||
|
)
|
||||||
|
|
||||||
|
// THEN we skip that slot but still process the other ones
|
||||||
|
assertThat(latest).containsExactly(ConnectivitySlot.WIFI, ConnectivitySlot.MOBILE)
|
||||||
|
|
||||||
|
job.cancel()
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
fun forceHiddenSlots_allInvalidOrEmptySlotNames_flowHasEmpty() = runBlocking(IMMEDIATE) {
|
||||||
|
var latest: Set<ConnectivitySlot>? = null
|
||||||
|
val job = underTest
|
||||||
|
.forceHiddenSlots
|
||||||
|
.onEach { latest = it }
|
||||||
|
.launchIn(this)
|
||||||
|
|
||||||
|
whenever(connectivitySlots.getSlotFromName(SLOT_WIFI)).thenReturn(null)
|
||||||
|
whenever(connectivitySlots.getSlotFromName(SLOT_ETHERNET)).thenReturn(null)
|
||||||
|
whenever(connectivitySlots.getSlotFromName(SLOT_MOBILE)).thenReturn(null)
|
||||||
|
|
||||||
|
getTunable().onTuningChanged(
|
||||||
|
HIDDEN_ICONS_TUNABLE_KEY, "$SLOT_MOBILE,,$SLOT_WIFI,$SLOT_ETHERNET,,,"
|
||||||
|
)
|
||||||
|
|
||||||
|
assertThat(latest).isEmpty()
|
||||||
|
|
||||||
|
job.cancel()
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
fun forceHiddenSlots_newSubscriberGetsCurrentValue() = runBlocking(IMMEDIATE) {
|
||||||
|
setUpEthernetWifiMobileSlotNames()
|
||||||
|
|
||||||
|
var latest1: Set<ConnectivitySlot>? = null
|
||||||
|
val job1 = underTest
|
||||||
|
.forceHiddenSlots
|
||||||
|
.onEach { latest1 = it }
|
||||||
|
.launchIn(this)
|
||||||
|
|
||||||
|
getTunable().onTuningChanged(HIDDEN_ICONS_TUNABLE_KEY, "$SLOT_WIFI,$SLOT_ETHERNET")
|
||||||
|
|
||||||
|
assertThat(latest1).containsExactly(ConnectivitySlot.WIFI, ConnectivitySlot.ETHERNET)
|
||||||
|
|
||||||
|
// WHEN we add a second subscriber after having already emitted a value
|
||||||
|
var latest2: Set<ConnectivitySlot>? = null
|
||||||
|
val job2 = underTest
|
||||||
|
.forceHiddenSlots
|
||||||
|
.onEach { latest2 = it }
|
||||||
|
.launchIn(this)
|
||||||
|
|
||||||
|
// THEN the second subscribe receives the already-emitted value
|
||||||
|
assertThat(latest2).containsExactly(ConnectivitySlot.WIFI, ConnectivitySlot.ETHERNET)
|
||||||
|
|
||||||
|
job1.cancel()
|
||||||
|
job2.cancel()
|
||||||
|
}
|
||||||
|
|
||||||
|
private fun getTunable(): TunerService.Tunable {
|
||||||
|
val callbackCaptor = argumentCaptor<TunerService.Tunable>()
|
||||||
|
Mockito.verify(tunerService).addTunable(callbackCaptor.capture(), any())
|
||||||
|
return callbackCaptor.value!!
|
||||||
|
}
|
||||||
|
|
||||||
|
private fun setUpEthernetWifiMobileSlotNames() {
|
||||||
|
whenever(connectivitySlots.getSlotFromName(SLOT_ETHERNET))
|
||||||
|
.thenReturn(ConnectivitySlot.ETHERNET)
|
||||||
|
whenever(connectivitySlots.getSlotFromName(SLOT_WIFI))
|
||||||
|
.thenReturn(ConnectivitySlot.WIFI)
|
||||||
|
whenever(connectivitySlots.getSlotFromName(SLOT_MOBILE))
|
||||||
|
.thenReturn(ConnectivitySlot.MOBILE)
|
||||||
|
}
|
||||||
|
|
||||||
|
companion object {
|
||||||
|
private const val SLOT_ETHERNET = "ethernet"
|
||||||
|
private const val SLOT_WIFI = "wifi"
|
||||||
|
private const val SLOT_MOBILE = "mobile"
|
||||||
|
private val IMMEDIATE = Dispatchers.Main.immediate
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,32 @@
|
|||||||
|
/*
|
||||||
|
* 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.shared.data.repository
|
||||||
|
|
||||||
|
import com.android.systemui.statusbar.pipeline.shared.data.model.ConnectivitySlot
|
||||||
|
import kotlinx.coroutines.flow.MutableStateFlow
|
||||||
|
import kotlinx.coroutines.flow.StateFlow
|
||||||
|
|
||||||
|
/** Fake implementation of [ConnectivityRepository] exposing set methods for all the flows. */
|
||||||
|
class FakeConnectivityRepository : ConnectivityRepository {
|
||||||
|
private val _forceHiddenIcons: MutableStateFlow<Set<ConnectivitySlot>> =
|
||||||
|
MutableStateFlow(emptySet())
|
||||||
|
override val forceHiddenSlots: StateFlow<Set<ConnectivitySlot>> = _forceHiddenIcons
|
||||||
|
|
||||||
|
fun setForceHiddenIcons(hiddenIcons: Set<ConnectivitySlot>) {
|
||||||
|
_forceHiddenIcons.value = hiddenIcons
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -18,6 +18,8 @@ package com.android.systemui.statusbar.pipeline.wifi.domain.interactor
|
|||||||
|
|
||||||
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.pipeline.shared.data.model.ConnectivitySlot
|
||||||
|
import com.android.systemui.statusbar.pipeline.shared.data.repository.FakeConnectivityRepository
|
||||||
import com.android.systemui.statusbar.pipeline.wifi.data.model.WifiActivityModel
|
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 com.android.systemui.statusbar.pipeline.wifi.data.repository.FakeWifiRepository
|
import com.android.systemui.statusbar.pipeline.wifi.data.repository.FakeWifiRepository
|
||||||
@@ -37,18 +39,22 @@ class WifiInteractorTest : SysuiTestCase() {
|
|||||||
|
|
||||||
private lateinit var underTest: WifiInteractor
|
private lateinit var underTest: WifiInteractor
|
||||||
|
|
||||||
private lateinit var repository: FakeWifiRepository
|
private lateinit var connectivityRepository: FakeConnectivityRepository
|
||||||
|
private lateinit var wifiRepository: FakeWifiRepository
|
||||||
|
|
||||||
@Before
|
@Before
|
||||||
fun setUp() {
|
fun setUp() {
|
||||||
repository = FakeWifiRepository()
|
connectivityRepository = FakeConnectivityRepository()
|
||||||
underTest = WifiInteractor(repository)
|
wifiRepository = FakeWifiRepository()
|
||||||
|
underTest = WifiInteractor(connectivityRepository, wifiRepository)
|
||||||
}
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
fun hasActivityIn_noInOrOut_outputsFalse() = runBlocking(IMMEDIATE) {
|
fun hasActivityIn_noInOrOut_outputsFalse() = runBlocking(IMMEDIATE) {
|
||||||
repository.setWifiNetwork(VALID_WIFI_NETWORK_MODEL)
|
wifiRepository.setWifiNetwork(VALID_WIFI_NETWORK_MODEL)
|
||||||
repository.setWifiActivity(WifiActivityModel(hasActivityIn = false, hasActivityOut = false))
|
wifiRepository.setWifiActivity(
|
||||||
|
WifiActivityModel(hasActivityIn = false, hasActivityOut = false)
|
||||||
|
)
|
||||||
|
|
||||||
var latest: Boolean? = null
|
var latest: Boolean? = null
|
||||||
val job = underTest
|
val job = underTest
|
||||||
@@ -63,8 +69,10 @@ class WifiInteractorTest : SysuiTestCase() {
|
|||||||
|
|
||||||
@Test
|
@Test
|
||||||
fun hasActivityIn_onlyOut_outputsFalse() = runBlocking(IMMEDIATE) {
|
fun hasActivityIn_onlyOut_outputsFalse() = runBlocking(IMMEDIATE) {
|
||||||
repository.setWifiNetwork(VALID_WIFI_NETWORK_MODEL)
|
wifiRepository.setWifiNetwork(VALID_WIFI_NETWORK_MODEL)
|
||||||
repository.setWifiActivity(WifiActivityModel(hasActivityIn = false, hasActivityOut = true))
|
wifiRepository.setWifiActivity(
|
||||||
|
WifiActivityModel(hasActivityIn = false, hasActivityOut = true)
|
||||||
|
)
|
||||||
|
|
||||||
var latest: Boolean? = null
|
var latest: Boolean? = null
|
||||||
val job = underTest
|
val job = underTest
|
||||||
@@ -79,8 +87,10 @@ class WifiInteractorTest : SysuiTestCase() {
|
|||||||
|
|
||||||
@Test
|
@Test
|
||||||
fun hasActivityIn_onlyIn_outputsTrue() = runBlocking(IMMEDIATE) {
|
fun hasActivityIn_onlyIn_outputsTrue() = runBlocking(IMMEDIATE) {
|
||||||
repository.setWifiNetwork(VALID_WIFI_NETWORK_MODEL)
|
wifiRepository.setWifiNetwork(VALID_WIFI_NETWORK_MODEL)
|
||||||
repository.setWifiActivity(WifiActivityModel(hasActivityIn = true, hasActivityOut = false))
|
wifiRepository.setWifiActivity(
|
||||||
|
WifiActivityModel(hasActivityIn = true, hasActivityOut = false)
|
||||||
|
)
|
||||||
|
|
||||||
var latest: Boolean? = null
|
var latest: Boolean? = null
|
||||||
val job = underTest
|
val job = underTest
|
||||||
@@ -95,8 +105,10 @@ class WifiInteractorTest : SysuiTestCase() {
|
|||||||
|
|
||||||
@Test
|
@Test
|
||||||
fun hasActivityIn_inAndOut_outputsTrue() = runBlocking(IMMEDIATE) {
|
fun hasActivityIn_inAndOut_outputsTrue() = runBlocking(IMMEDIATE) {
|
||||||
repository.setWifiNetwork(VALID_WIFI_NETWORK_MODEL)
|
wifiRepository.setWifiNetwork(VALID_WIFI_NETWORK_MODEL)
|
||||||
repository.setWifiActivity(WifiActivityModel(hasActivityIn = true, hasActivityOut = true))
|
wifiRepository.setWifiActivity(
|
||||||
|
WifiActivityModel(hasActivityIn = true, hasActivityOut = true)
|
||||||
|
)
|
||||||
|
|
||||||
var latest: Boolean? = null
|
var latest: Boolean? = null
|
||||||
val job = underTest
|
val job = underTest
|
||||||
@@ -111,8 +123,10 @@ class WifiInteractorTest : SysuiTestCase() {
|
|||||||
|
|
||||||
@Test
|
@Test
|
||||||
fun hasActivityIn_ssidNull_outputsFalse() = runBlocking(IMMEDIATE) {
|
fun hasActivityIn_ssidNull_outputsFalse() = runBlocking(IMMEDIATE) {
|
||||||
repository.setWifiNetwork(WifiNetworkModel.Active(networkId = 1, ssid = null))
|
wifiRepository.setWifiNetwork(WifiNetworkModel.Active(networkId = 1, ssid = null))
|
||||||
repository.setWifiActivity(WifiActivityModel(hasActivityIn = true, hasActivityOut = true))
|
wifiRepository.setWifiActivity(
|
||||||
|
WifiActivityModel(hasActivityIn = true, hasActivityOut = true)
|
||||||
|
)
|
||||||
|
|
||||||
var latest: Boolean? = null
|
var latest: Boolean? = null
|
||||||
val job = underTest
|
val job = underTest
|
||||||
@@ -127,8 +141,10 @@ class WifiInteractorTest : SysuiTestCase() {
|
|||||||
|
|
||||||
@Test
|
@Test
|
||||||
fun hasActivityIn_inactiveNetwork_outputsFalse() = runBlocking(IMMEDIATE) {
|
fun hasActivityIn_inactiveNetwork_outputsFalse() = runBlocking(IMMEDIATE) {
|
||||||
repository.setWifiNetwork(WifiNetworkModel.Inactive)
|
wifiRepository.setWifiNetwork(WifiNetworkModel.Inactive)
|
||||||
repository.setWifiActivity(WifiActivityModel(hasActivityIn = true, hasActivityOut = true))
|
wifiRepository.setWifiActivity(
|
||||||
|
WifiActivityModel(hasActivityIn = true, hasActivityOut = true)
|
||||||
|
)
|
||||||
|
|
||||||
var latest: Boolean? = null
|
var latest: Boolean? = null
|
||||||
val job = underTest
|
val job = underTest
|
||||||
@@ -143,8 +159,10 @@ class WifiInteractorTest : SysuiTestCase() {
|
|||||||
|
|
||||||
@Test
|
@Test
|
||||||
fun hasActivityIn_carrierMergedNetwork_outputsFalse() = runBlocking(IMMEDIATE) {
|
fun hasActivityIn_carrierMergedNetwork_outputsFalse() = runBlocking(IMMEDIATE) {
|
||||||
repository.setWifiNetwork(WifiNetworkModel.CarrierMerged)
|
wifiRepository.setWifiNetwork(WifiNetworkModel.CarrierMerged)
|
||||||
repository.setWifiActivity(WifiActivityModel(hasActivityIn = true, hasActivityOut = true))
|
wifiRepository.setWifiActivity(
|
||||||
|
WifiActivityModel(hasActivityIn = true, hasActivityOut = true)
|
||||||
|
)
|
||||||
|
|
||||||
var latest: Boolean? = null
|
var latest: Boolean? = null
|
||||||
val job = underTest
|
val job = underTest
|
||||||
@@ -159,7 +177,7 @@ class WifiInteractorTest : SysuiTestCase() {
|
|||||||
|
|
||||||
@Test
|
@Test
|
||||||
fun hasActivityIn_multipleChanges_multipleOutputChanges() = runBlocking(IMMEDIATE) {
|
fun hasActivityIn_multipleChanges_multipleOutputChanges() = runBlocking(IMMEDIATE) {
|
||||||
repository.setWifiNetwork(VALID_WIFI_NETWORK_MODEL)
|
wifiRepository.setWifiNetwork(VALID_WIFI_NETWORK_MODEL)
|
||||||
|
|
||||||
var latest: Boolean? = null
|
var latest: Boolean? = null
|
||||||
val job = underTest
|
val job = underTest
|
||||||
@@ -168,23 +186,33 @@ class WifiInteractorTest : SysuiTestCase() {
|
|||||||
.launchIn(this)
|
.launchIn(this)
|
||||||
|
|
||||||
// Conduct a series of changes and verify we catch each of them in succession
|
// Conduct a series of changes and verify we catch each of them in succession
|
||||||
repository.setWifiActivity(WifiActivityModel(hasActivityIn = true, hasActivityOut = false))
|
wifiRepository.setWifiActivity(
|
||||||
|
WifiActivityModel(hasActivityIn = true, hasActivityOut = false)
|
||||||
|
)
|
||||||
yield()
|
yield()
|
||||||
assertThat(latest).isTrue()
|
assertThat(latest).isTrue()
|
||||||
|
|
||||||
repository.setWifiActivity(WifiActivityModel(hasActivityIn = false, hasActivityOut = true))
|
wifiRepository.setWifiActivity(
|
||||||
|
WifiActivityModel(hasActivityIn = false, hasActivityOut = true)
|
||||||
|
)
|
||||||
yield()
|
yield()
|
||||||
assertThat(latest).isFalse()
|
assertThat(latest).isFalse()
|
||||||
|
|
||||||
repository.setWifiActivity(WifiActivityModel(hasActivityIn = true, hasActivityOut = true))
|
wifiRepository.setWifiActivity(
|
||||||
|
WifiActivityModel(hasActivityIn = true, hasActivityOut = true)
|
||||||
|
)
|
||||||
yield()
|
yield()
|
||||||
assertThat(latest).isTrue()
|
assertThat(latest).isTrue()
|
||||||
|
|
||||||
repository.setWifiActivity(WifiActivityModel(hasActivityIn = true, hasActivityOut = false))
|
wifiRepository.setWifiActivity(
|
||||||
|
WifiActivityModel(hasActivityIn = true, hasActivityOut = false)
|
||||||
|
)
|
||||||
yield()
|
yield()
|
||||||
assertThat(latest).isTrue()
|
assertThat(latest).isTrue()
|
||||||
|
|
||||||
repository.setWifiActivity(WifiActivityModel(hasActivityIn = false, hasActivityOut = false))
|
wifiRepository.setWifiActivity(
|
||||||
|
WifiActivityModel(hasActivityIn = false, hasActivityOut = false)
|
||||||
|
)
|
||||||
yield()
|
yield()
|
||||||
assertThat(latest).isFalse()
|
assertThat(latest).isFalse()
|
||||||
|
|
||||||
@@ -200,7 +228,7 @@ class WifiInteractorTest : SysuiTestCase() {
|
|||||||
ssid = "AB",
|
ssid = "AB",
|
||||||
passpointProviderFriendlyName = "friendly"
|
passpointProviderFriendlyName = "friendly"
|
||||||
)
|
)
|
||||||
repository.setWifiNetwork(wifiNetwork)
|
wifiRepository.setWifiNetwork(wifiNetwork)
|
||||||
|
|
||||||
var latest: WifiNetworkModel? = null
|
var latest: WifiNetworkModel? = null
|
||||||
val job = underTest
|
val job = underTest
|
||||||
@@ -213,6 +241,36 @@ class WifiInteractorTest : SysuiTestCase() {
|
|||||||
job.cancel()
|
job.cancel()
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
fun isForceHidden_repoHasWifiHidden_outputsTrue() = runBlocking(IMMEDIATE) {
|
||||||
|
connectivityRepository.setForceHiddenIcons(setOf(ConnectivitySlot.WIFI))
|
||||||
|
|
||||||
|
var latest: Boolean? = null
|
||||||
|
val job = underTest
|
||||||
|
.isForceHidden
|
||||||
|
.onEach { latest = it }
|
||||||
|
.launchIn(this)
|
||||||
|
|
||||||
|
assertThat(latest).isTrue()
|
||||||
|
|
||||||
|
job.cancel()
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
fun isForceHidden_repoDoesNotHaveWifiHidden_outputsFalse() = runBlocking(IMMEDIATE) {
|
||||||
|
connectivityRepository.setForceHiddenIcons(setOf())
|
||||||
|
|
||||||
|
var latest: Boolean? = null
|
||||||
|
val job = underTest
|
||||||
|
.isForceHidden
|
||||||
|
.onEach { latest = it }
|
||||||
|
.launchIn(this)
|
||||||
|
|
||||||
|
assertThat(latest).isFalse()
|
||||||
|
|
||||||
|
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")
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -18,11 +18,14 @@ 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.common.shared.model.Icon
|
||||||
import com.android.systemui.statusbar.connectivity.WifiIcons.WIFI_FULL_ICONS
|
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_INTERNET_ICONS
|
||||||
import com.android.systemui.statusbar.connectivity.WifiIcons.WIFI_NO_NETWORK
|
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.data.model.ConnectivitySlot
|
||||||
|
import com.android.systemui.statusbar.pipeline.shared.data.repository.FakeConnectivityRepository
|
||||||
import com.android.systemui.statusbar.pipeline.wifi.data.model.WifiActivityModel
|
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 com.android.systemui.statusbar.pipeline.wifi.data.repository.FakeWifiRepository
|
import com.android.systemui.statusbar.pipeline.wifi.data.repository.FakeWifiRepository
|
||||||
@@ -50,14 +53,16 @@ class WifiViewModelTest : SysuiTestCase() {
|
|||||||
@Mock private lateinit var statusBarPipelineFlags: StatusBarPipelineFlags
|
@Mock private lateinit var statusBarPipelineFlags: StatusBarPipelineFlags
|
||||||
@Mock private lateinit var logger: ConnectivityPipelineLogger
|
@Mock private lateinit var logger: ConnectivityPipelineLogger
|
||||||
@Mock private lateinit var constants: WifiConstants
|
@Mock private lateinit var constants: WifiConstants
|
||||||
private lateinit var repository: FakeWifiRepository
|
private lateinit var connectivityRepository: FakeConnectivityRepository
|
||||||
|
private lateinit var wifiRepository: FakeWifiRepository
|
||||||
private lateinit var interactor: WifiInteractor
|
private lateinit var interactor: WifiInteractor
|
||||||
|
|
||||||
@Before
|
@Before
|
||||||
fun setUp() {
|
fun setUp() {
|
||||||
MockitoAnnotations.initMocks(this)
|
MockitoAnnotations.initMocks(this)
|
||||||
repository = FakeWifiRepository()
|
connectivityRepository = FakeConnectivityRepository()
|
||||||
interactor = WifiInteractor(repository)
|
wifiRepository = FakeWifiRepository()
|
||||||
|
interactor = WifiInteractor(connectivityRepository, wifiRepository)
|
||||||
|
|
||||||
underTest = WifiViewModel(
|
underTest = WifiViewModel(
|
||||||
statusBarPipelineFlags,
|
statusBarPipelineFlags,
|
||||||
@@ -68,42 +73,13 @@ class WifiViewModelTest : SysuiTestCase() {
|
|||||||
}
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
fun wifiIconResId_inactiveNetwork_outputsNoNetworkIcon() = runBlocking(IMMEDIATE) {
|
fun wifiIcon_forceHidden_outputsNull() = runBlocking(IMMEDIATE) {
|
||||||
repository.setWifiNetwork(WifiNetworkModel.Inactive)
|
connectivityRepository.setForceHiddenIcons(setOf(ConnectivitySlot.WIFI))
|
||||||
|
wifiRepository.setWifiNetwork(WifiNetworkModel.Active(NETWORK_ID, level = 2))
|
||||||
|
|
||||||
var latest: Int? = null
|
var latest: Icon? = null
|
||||||
val job = underTest
|
val job = underTest
|
||||||
.wifiIconResId
|
.wifiIcon
|
||||||
.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 }
|
.onEach { latest = it }
|
||||||
.launchIn(this)
|
.launchIn(this)
|
||||||
|
|
||||||
@@ -113,10 +89,72 @@ class WifiViewModelTest : SysuiTestCase() {
|
|||||||
}
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
fun wifiIconResId_isActiveAndValidated_level1_outputsFull1Icon() = runBlocking(IMMEDIATE) {
|
fun wifiIcon_notForceHidden_outputsVisible() = runBlocking(IMMEDIATE) {
|
||||||
|
connectivityRepository.setForceHiddenIcons(setOf())
|
||||||
|
wifiRepository.setWifiNetwork(WifiNetworkModel.Active(NETWORK_ID, level = 2))
|
||||||
|
|
||||||
|
var latest: Icon? = null
|
||||||
|
val job = underTest
|
||||||
|
.wifiIcon
|
||||||
|
.onEach { latest = it }
|
||||||
|
.launchIn(this)
|
||||||
|
|
||||||
|
assertThat(latest).isInstanceOf(Icon.Resource::class.java)
|
||||||
|
|
||||||
|
job.cancel()
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
fun wifiIcon_inactiveNetwork_outputsNoNetworkIcon() = runBlocking(IMMEDIATE) {
|
||||||
|
wifiRepository.setWifiNetwork(WifiNetworkModel.Inactive)
|
||||||
|
|
||||||
|
var latest: Icon? = null
|
||||||
|
val job = underTest
|
||||||
|
.wifiIcon
|
||||||
|
.onEach { latest = it }
|
||||||
|
.launchIn(this)
|
||||||
|
|
||||||
|
assertThat(latest).isInstanceOf(Icon.Resource::class.java)
|
||||||
|
assertThat((latest as Icon.Resource).res).isEqualTo(WIFI_NO_NETWORK)
|
||||||
|
|
||||||
|
job.cancel()
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
fun wifiIcon_carrierMergedNetwork_outputsNull() = runBlocking(IMMEDIATE) {
|
||||||
|
wifiRepository.setWifiNetwork(WifiNetworkModel.CarrierMerged)
|
||||||
|
|
||||||
|
var latest: Icon? = null
|
||||||
|
val job = underTest
|
||||||
|
.wifiIcon
|
||||||
|
.onEach { latest = it }
|
||||||
|
.launchIn(this)
|
||||||
|
|
||||||
|
assertThat(latest).isNull()
|
||||||
|
|
||||||
|
job.cancel()
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
fun wifiIcon_isActiveNullLevel_outputsNull() = runBlocking(IMMEDIATE) {
|
||||||
|
wifiRepository.setWifiNetwork(WifiNetworkModel.Active(NETWORK_ID, level = null))
|
||||||
|
|
||||||
|
var latest: Icon? = null
|
||||||
|
val job = underTest
|
||||||
|
.wifiIcon
|
||||||
|
.onEach { latest = it }
|
||||||
|
.launchIn(this)
|
||||||
|
|
||||||
|
assertThat(latest).isNull()
|
||||||
|
|
||||||
|
job.cancel()
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
fun wifiIcon_isActiveAndValidated_level1_outputsFull1Icon() = runBlocking(IMMEDIATE) {
|
||||||
val level = 1
|
val level = 1
|
||||||
|
|
||||||
repository.setWifiNetwork(
|
wifiRepository.setWifiNetwork(
|
||||||
WifiNetworkModel.Active(
|
WifiNetworkModel.Active(
|
||||||
NETWORK_ID,
|
NETWORK_ID,
|
||||||
isValidated = true,
|
isValidated = true,
|
||||||
@@ -124,22 +162,23 @@ class WifiViewModelTest : SysuiTestCase() {
|
|||||||
)
|
)
|
||||||
)
|
)
|
||||||
|
|
||||||
var latest: Int? = null
|
var latest: Icon? = null
|
||||||
val job = underTest
|
val job = underTest
|
||||||
.wifiIconResId
|
.wifiIcon
|
||||||
.onEach { latest = it }
|
.onEach { latest = it }
|
||||||
.launchIn(this)
|
.launchIn(this)
|
||||||
|
|
||||||
assertThat(latest).isEqualTo(WIFI_FULL_ICONS[level])
|
assertThat(latest).isInstanceOf(Icon.Resource::class.java)
|
||||||
|
assertThat((latest as Icon.Resource).res).isEqualTo(WIFI_FULL_ICONS[level])
|
||||||
|
|
||||||
job.cancel()
|
job.cancel()
|
||||||
}
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
fun wifiIconResId_isActiveAndNotValidated_level4_outputsEmpty4Icon() = runBlocking(IMMEDIATE) {
|
fun wifiIcon_isActiveAndNotValidated_level4_outputsEmpty4Icon() = runBlocking(IMMEDIATE) {
|
||||||
val level = 4
|
val level = 4
|
||||||
|
|
||||||
repository.setWifiNetwork(
|
wifiRepository.setWifiNetwork(
|
||||||
WifiNetworkModel.Active(
|
WifiNetworkModel.Active(
|
||||||
NETWORK_ID,
|
NETWORK_ID,
|
||||||
isValidated = false,
|
isValidated = false,
|
||||||
@@ -147,13 +186,14 @@ class WifiViewModelTest : SysuiTestCase() {
|
|||||||
)
|
)
|
||||||
)
|
)
|
||||||
|
|
||||||
var latest: Int? = null
|
var latest: Icon? = null
|
||||||
val job = underTest
|
val job = underTest
|
||||||
.wifiIconResId
|
.wifiIcon
|
||||||
.onEach { latest = it }
|
.onEach { latest = it }
|
||||||
.launchIn(this)
|
.launchIn(this)
|
||||||
|
|
||||||
assertThat(latest).isEqualTo(WIFI_NO_INTERNET_ICONS[level])
|
assertThat(latest).isInstanceOf(Icon.Resource::class.java)
|
||||||
|
assertThat((latest as Icon.Resource).res).isEqualTo(WIFI_NO_INTERNET_ICONS[level])
|
||||||
|
|
||||||
job.cancel()
|
job.cancel()
|
||||||
}
|
}
|
||||||
@@ -161,7 +201,7 @@ class WifiViewModelTest : SysuiTestCase() {
|
|||||||
@Test
|
@Test
|
||||||
fun activityInVisible_showActivityConfigFalse_outputsFalse() = runBlocking(IMMEDIATE) {
|
fun activityInVisible_showActivityConfigFalse_outputsFalse() = runBlocking(IMMEDIATE) {
|
||||||
whenever(constants.shouldShowActivityConfig).thenReturn(false)
|
whenever(constants.shouldShowActivityConfig).thenReturn(false)
|
||||||
repository.setWifiNetwork(ACTIVE_VALID_WIFI_NETWORK)
|
wifiRepository.setWifiNetwork(ACTIVE_VALID_WIFI_NETWORK)
|
||||||
|
|
||||||
var latest: Boolean? = null
|
var latest: Boolean? = null
|
||||||
val job = underTest
|
val job = underTest
|
||||||
@@ -178,7 +218,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)
|
wifiRepository.setWifiNetwork(ACTIVE_VALID_WIFI_NETWORK)
|
||||||
|
|
||||||
var latest: Boolean? = null
|
var latest: Boolean? = null
|
||||||
val job = underTest
|
val job = underTest
|
||||||
@@ -187,7 +227,9 @@ class WifiViewModelTest : SysuiTestCase() {
|
|||||||
.launchIn(this)
|
.launchIn(this)
|
||||||
|
|
||||||
// Update the repo to have activityIn
|
// Update the repo to have activityIn
|
||||||
repository.setWifiActivity(WifiActivityModel(hasActivityIn = true, hasActivityOut = false))
|
wifiRepository.setWifiActivity(
|
||||||
|
WifiActivityModel(hasActivityIn = true, hasActivityOut = false)
|
||||||
|
)
|
||||||
yield()
|
yield()
|
||||||
|
|
||||||
// Verify that we didn't update to activityIn=true (because our config is false)
|
// Verify that we didn't update to activityIn=true (because our config is false)
|
||||||
@@ -199,7 +241,7 @@ class WifiViewModelTest : SysuiTestCase() {
|
|||||||
@Test
|
@Test
|
||||||
fun activityInVisible_showActivityConfigTrue_outputsUpdate() = runBlocking(IMMEDIATE) {
|
fun activityInVisible_showActivityConfigTrue_outputsUpdate() = runBlocking(IMMEDIATE) {
|
||||||
whenever(constants.shouldShowActivityConfig).thenReturn(true)
|
whenever(constants.shouldShowActivityConfig).thenReturn(true)
|
||||||
repository.setWifiNetwork(ACTIVE_VALID_WIFI_NETWORK)
|
wifiRepository.setWifiNetwork(ACTIVE_VALID_WIFI_NETWORK)
|
||||||
|
|
||||||
var latest: Boolean? = null
|
var latest: Boolean? = null
|
||||||
val job = underTest
|
val job = underTest
|
||||||
@@ -208,7 +250,9 @@ class WifiViewModelTest : SysuiTestCase() {
|
|||||||
.launchIn(this)
|
.launchIn(this)
|
||||||
|
|
||||||
// Update the repo to have activityIn
|
// Update the repo to have activityIn
|
||||||
repository.setWifiActivity(WifiActivityModel(hasActivityIn = true, hasActivityOut = false))
|
wifiRepository.setWifiActivity(
|
||||||
|
WifiActivityModel(hasActivityIn = true, hasActivityOut = false)
|
||||||
|
)
|
||||||
yield()
|
yield()
|
||||||
|
|
||||||
// Verify that we updated to activityIn=true
|
// Verify that we updated to activityIn=true
|
||||||
|
|||||||
Reference in New Issue
Block a user