Merge "[Sb Refactor] Define an initial data/domain/ui layer for the status bar connectivity architecture for wifi." into tm-qpr-dev
This commit is contained in:
committed by
Android (Google) Code Review
commit
9dce321437
@@ -270,7 +270,7 @@ public class LogModule {
|
|||||||
@SysUISingleton
|
@SysUISingleton
|
||||||
@StatusBarConnectivityLog
|
@StatusBarConnectivityLog
|
||||||
public static LogBuffer provideStatusBarConnectivityBuffer(LogBufferFactory factory) {
|
public static LogBuffer provideStatusBarConnectivityBuffer(LogBufferFactory factory) {
|
||||||
return factory.create("StatusBarConnectivityLog", 64);
|
return factory.create("SbConnectivity", 64);
|
||||||
}
|
}
|
||||||
|
|
||||||
/** Allows logging buffers to be tweaked via adb on debug builds but not on prod builds. */
|
/** Allows logging buffers to be tweaked via adb on debug builds but not on prod builds. */
|
||||||
|
|||||||
@@ -16,7 +16,7 @@
|
|||||||
|
|
||||||
package com.android.systemui.statusbar.pipeline
|
package com.android.systemui.statusbar.pipeline
|
||||||
|
|
||||||
import com.android.systemui.statusbar.pipeline.repository.NetworkCapabilityInfo
|
import com.android.systemui.statusbar.pipeline.wifi.data.repository.NetworkCapabilityInfo
|
||||||
import kotlinx.coroutines.flow.StateFlow
|
import kotlinx.coroutines.flow.StateFlow
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
|||||||
@@ -18,9 +18,9 @@ package com.android.systemui.statusbar.pipeline
|
|||||||
|
|
||||||
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.Application
|
||||||
import com.android.systemui.statusbar.pipeline.repository.NetworkCapabilitiesRepo
|
import com.android.systemui.statusbar.pipeline.wifi.data.repository.NetworkCapabilitiesRepo
|
||||||
import kotlinx.coroutines.CoroutineScope
|
|
||||||
import javax.inject.Inject
|
import javax.inject.Inject
|
||||||
|
import kotlinx.coroutines.CoroutineScope
|
||||||
import kotlinx.coroutines.flow.SharingStarted.Companion.Lazily
|
import kotlinx.coroutines.flow.SharingStarted.Companion.Lazily
|
||||||
import kotlinx.coroutines.flow.StateFlow
|
import kotlinx.coroutines.flow.StateFlow
|
||||||
import kotlinx.coroutines.flow.map
|
import kotlinx.coroutines.flow.map
|
||||||
|
|||||||
@@ -20,14 +20,18 @@ import android.content.Context
|
|||||||
import com.android.systemui.CoreStartable
|
import com.android.systemui.CoreStartable
|
||||||
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.Application
|
||||||
import com.android.systemui.statusbar.pipeline.repository.NetworkCapabilityInfo
|
import com.android.systemui.statusbar.pipeline.wifi.data.repository.NetworkCapabilityInfo
|
||||||
|
import com.android.systemui.statusbar.pipeline.wifi.ui.viewmodel.WifiViewModel
|
||||||
import javax.inject.Inject
|
import javax.inject.Inject
|
||||||
|
import javax.inject.Provider
|
||||||
import kotlinx.coroutines.CoroutineScope
|
import kotlinx.coroutines.CoroutineScope
|
||||||
import kotlinx.coroutines.flow.Flow
|
import kotlinx.coroutines.flow.Flow
|
||||||
import kotlinx.coroutines.flow.SharingStarted.Companion.Lazily
|
import kotlinx.coroutines.flow.SharingStarted.Companion.Lazily
|
||||||
|
import kotlinx.coroutines.flow.collect
|
||||||
import kotlinx.coroutines.flow.emptyFlow
|
import kotlinx.coroutines.flow.emptyFlow
|
||||||
import kotlinx.coroutines.flow.map
|
import kotlinx.coroutines.flow.map
|
||||||
import kotlinx.coroutines.flow.stateIn
|
import kotlinx.coroutines.flow.stateIn
|
||||||
|
import kotlinx.coroutines.launch
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* A processor that transforms raw connectivity information that we get from callbacks and turns it
|
* A processor that transforms raw connectivity information that we get from callbacks and turns it
|
||||||
@@ -42,12 +46,16 @@ import kotlinx.coroutines.flow.stateIn
|
|||||||
class ConnectivityInfoProcessor @Inject constructor(
|
class ConnectivityInfoProcessor @Inject constructor(
|
||||||
connectivityInfoCollector: ConnectivityInfoCollector,
|
connectivityInfoCollector: ConnectivityInfoCollector,
|
||||||
context: Context,
|
context: Context,
|
||||||
|
// TODO(b/238425913): Don't use the application scope; instead, use the status bar view's
|
||||||
|
// scope so we only do work when there's UI that cares about it.
|
||||||
@Application private val scope: CoroutineScope,
|
@Application private val scope: CoroutineScope,
|
||||||
statusBarPipelineFlags: StatusBarPipelineFlags,
|
private val statusBarPipelineFlags: StatusBarPipelineFlags,
|
||||||
|
private val wifiViewModelProvider: Provider<WifiViewModel>,
|
||||||
) : CoreStartable(context) {
|
) : CoreStartable(context) {
|
||||||
// Note: This flow will not start running until a client calls `collect` on it, which means that
|
// Note: This flow will not start running until a client calls `collect` on it, which means that
|
||||||
// [connectivityInfoCollector]'s flow will also not start anything until that `collect` call
|
// [connectivityInfoCollector]'s flow will also not start anything until that `collect` call
|
||||||
// happens.
|
// happens.
|
||||||
|
// TODO(b/238425913): Delete this.
|
||||||
val processedInfoFlow: Flow<ProcessedConnectivityInfo> =
|
val processedInfoFlow: Flow<ProcessedConnectivityInfo> =
|
||||||
if (!statusBarPipelineFlags.isNewPipelineEnabled())
|
if (!statusBarPipelineFlags.isNewPipelineEnabled())
|
||||||
emptyFlow()
|
emptyFlow()
|
||||||
@@ -60,6 +68,14 @@ class ConnectivityInfoProcessor @Inject constructor(
|
|||||||
)
|
)
|
||||||
|
|
||||||
override fun start() {
|
override fun start() {
|
||||||
|
if (!statusBarPipelineFlags.isNewPipelineEnabled()) {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
// TODO(b/238425913): The view binder should do this instead. For now, do it here so we can
|
||||||
|
// see the logs.
|
||||||
|
scope.launch {
|
||||||
|
wifiViewModelProvider.get().isActivityInVisible.collect { }
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private fun RawConnectivityInfo.process(): ProcessedConnectivityInfo {
|
private fun RawConnectivityInfo.process(): ProcessedConnectivityInfo {
|
||||||
|
|||||||
@@ -1,59 +0,0 @@
|
|||||||
/*
|
|
||||||
* 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
|
|
||||||
|
|
||||||
import android.net.Network
|
|
||||||
import android.net.NetworkCapabilities
|
|
||||||
import com.android.systemui.dagger.SysUISingleton
|
|
||||||
import com.android.systemui.log.LogBuffer
|
|
||||||
import com.android.systemui.log.LogLevel
|
|
||||||
import com.android.systemui.log.dagger.StatusBarConnectivityLog
|
|
||||||
import javax.inject.Inject
|
|
||||||
|
|
||||||
@SysUISingleton
|
|
||||||
class ConnectivityPipelineLogger @Inject constructor(
|
|
||||||
@StatusBarConnectivityLog private val buffer: LogBuffer,
|
|
||||||
) {
|
|
||||||
fun logOnCapabilitiesChanged(network: Network, networkCapabilities: NetworkCapabilities) {
|
|
||||||
buffer.log(
|
|
||||||
TAG,
|
|
||||||
LogLevel.INFO,
|
|
||||||
{
|
|
||||||
int1 = network.getNetId()
|
|
||||||
str1 = networkCapabilities.toString()
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"onCapabilitiesChanged: net=$int1 capabilities=$str1"
|
|
||||||
}
|
|
||||||
)
|
|
||||||
}
|
|
||||||
|
|
||||||
fun logOnLost(network: Network) {
|
|
||||||
buffer.log(
|
|
||||||
TAG,
|
|
||||||
LogLevel.INFO,
|
|
||||||
{
|
|
||||||
int1 = network.getNetId()
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"onLost: net=$int1"
|
|
||||||
}
|
|
||||||
)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
private const val TAG = "SbConnectivityPipeline"
|
|
||||||
@@ -20,6 +20,8 @@ import com.android.systemui.CoreStartable
|
|||||||
import com.android.systemui.statusbar.pipeline.ConnectivityInfoCollector
|
import com.android.systemui.statusbar.pipeline.ConnectivityInfoCollector
|
||||||
import com.android.systemui.statusbar.pipeline.ConnectivityInfoCollectorImpl
|
import com.android.systemui.statusbar.pipeline.ConnectivityInfoCollectorImpl
|
||||||
import com.android.systemui.statusbar.pipeline.ConnectivityInfoProcessor
|
import com.android.systemui.statusbar.pipeline.ConnectivityInfoProcessor
|
||||||
|
import com.android.systemui.statusbar.pipeline.wifi.data.repository.WifiRepository
|
||||||
|
import com.android.systemui.statusbar.pipeline.wifi.data.repository.WifiRepositoryImpl
|
||||||
import dagger.Binds
|
import dagger.Binds
|
||||||
import dagger.Module
|
import dagger.Module
|
||||||
import dagger.multibindings.ClassKey
|
import dagger.multibindings.ClassKey
|
||||||
@@ -37,4 +39,7 @@ abstract class StatusBarPipelineModule {
|
|||||||
abstract fun provideConnectivityInfoCollector(
|
abstract fun provideConnectivityInfoCollector(
|
||||||
impl: ConnectivityInfoCollectorImpl
|
impl: ConnectivityInfoCollectorImpl
|
||||||
): ConnectivityInfoCollector
|
): ConnectivityInfoCollector
|
||||||
|
|
||||||
|
@Binds
|
||||||
|
abstract fun wifiRepository(impl: WifiRepositoryImpl): WifiRepository
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -0,0 +1,106 @@
|
|||||||
|
/*
|
||||||
|
* Copyright (C) 2022 The Android Open Source Project
|
||||||
|
*
|
||||||
|
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
|
* you may not use this file except in compliance with the License.
|
||||||
|
* You may obtain a copy of the License at
|
||||||
|
*
|
||||||
|
* http://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
*
|
||||||
|
* Unless required by applicable law or agreed to in writing, software
|
||||||
|
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||||
|
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||||
|
* See the License for the specific language governing permissions and
|
||||||
|
* limitations under the License.
|
||||||
|
*/
|
||||||
|
|
||||||
|
package com.android.systemui.statusbar.pipeline.shared
|
||||||
|
|
||||||
|
import android.net.Network
|
||||||
|
import android.net.NetworkCapabilities
|
||||||
|
import com.android.systemui.dagger.SysUISingleton
|
||||||
|
import com.android.systemui.log.LogBuffer
|
||||||
|
import com.android.systemui.log.LogLevel
|
||||||
|
import com.android.systemui.log.dagger.StatusBarConnectivityLog
|
||||||
|
import com.android.systemui.statusbar.pipeline.shared.ConnectivityPipelineLogger.Companion.toString
|
||||||
|
import javax.inject.Inject
|
||||||
|
import kotlinx.coroutines.flow.Flow
|
||||||
|
import kotlinx.coroutines.flow.onEach
|
||||||
|
|
||||||
|
@SysUISingleton
|
||||||
|
class ConnectivityPipelineLogger @Inject constructor(
|
||||||
|
@StatusBarConnectivityLog private val buffer: LogBuffer,
|
||||||
|
) {
|
||||||
|
fun logInputChange(callbackName: String, changeInfo: String) {
|
||||||
|
buffer.log(
|
||||||
|
SB_LOGGING_TAG,
|
||||||
|
LogLevel.INFO,
|
||||||
|
{
|
||||||
|
str1 = callbackName
|
||||||
|
str2 = changeInfo
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"Input: $str1: $str2"
|
||||||
|
}
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
fun logOutputChange(outputParamName: String, changeInfo: String) {
|
||||||
|
buffer.log(
|
||||||
|
SB_LOGGING_TAG,
|
||||||
|
LogLevel.INFO,
|
||||||
|
{
|
||||||
|
str1 = outputParamName
|
||||||
|
str2 = changeInfo
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"Output: $str1: $str2"
|
||||||
|
}
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
fun logOnCapabilitiesChanged(network: Network, networkCapabilities: NetworkCapabilities) {
|
||||||
|
buffer.log(
|
||||||
|
SB_LOGGING_TAG,
|
||||||
|
LogLevel.INFO,
|
||||||
|
{
|
||||||
|
int1 = network.getNetId()
|
||||||
|
str1 = networkCapabilities.toString()
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"onCapabilitiesChanged: net=$int1 capabilities=$str1"
|
||||||
|
}
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
fun logOnLost(network: Network) {
|
||||||
|
buffer.log(
|
||||||
|
SB_LOGGING_TAG,
|
||||||
|
LogLevel.INFO,
|
||||||
|
{
|
||||||
|
int1 = network.getNetId()
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"onLost: net=$int1"
|
||||||
|
}
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
companion object {
|
||||||
|
const val SB_LOGGING_TAG = "SbConnectivity"
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Log a change in one of the **outputs** to the connectivity pipeline.
|
||||||
|
*
|
||||||
|
* @param prettyPrint an optional function to transform the value into a readable string.
|
||||||
|
* [toString] is used if no custom function is provided.
|
||||||
|
*/
|
||||||
|
fun <T : Any> Flow<T>.logOutputChange(
|
||||||
|
logger: ConnectivityPipelineLogger,
|
||||||
|
outputParamName: String,
|
||||||
|
prettyPrint: (T) -> String = { it.toString() }
|
||||||
|
): Flow<T> {
|
||||||
|
return this.onEach { logger.logOutputChange(outputParamName, prettyPrint(it)) }
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,27 @@
|
|||||||
|
/*
|
||||||
|
* 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
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Provides information on the current wifi activity.
|
||||||
|
*/
|
||||||
|
data class WifiActivityModel(
|
||||||
|
/** True if the wifi has activity in (download). */
|
||||||
|
val hasActivityIn: Boolean,
|
||||||
|
/** True if the wifi has activity out (upload). */
|
||||||
|
val hasActivityOut: Boolean,
|
||||||
|
)
|
||||||
@@ -0,0 +1,29 @@
|
|||||||
|
/*
|
||||||
|
* 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
|
||||||
|
|
||||||
|
/** Provides information about the current wifi state. */
|
||||||
|
data class WifiModel(
|
||||||
|
/** See [android.net.wifi.WifiInfo.ssid]. */
|
||||||
|
val ssid: String? = null,
|
||||||
|
/** See [android.net.wifi.WifiInfo.isPasspointAp]. */
|
||||||
|
val isPasspointAccessPoint: Boolean = false,
|
||||||
|
/** See [android.net.wifi.WifiInfo.isOsuAp]. */
|
||||||
|
val isOnlineSignUpForPasspointAccessPoint: Boolean = false,
|
||||||
|
/** See [android.net.wifi.WifiInfo.passpointProviderFriendlyName]. */
|
||||||
|
val passpointProviderFriendlyName: String? = null,
|
||||||
|
)
|
||||||
@@ -16,7 +16,7 @@
|
|||||||
|
|
||||||
@file:OptIn(ExperimentalCoroutinesApi::class)
|
@file:OptIn(ExperimentalCoroutinesApi::class)
|
||||||
|
|
||||||
package com.android.systemui.statusbar.pipeline.repository
|
package com.android.systemui.statusbar.pipeline.wifi.data.repository
|
||||||
|
|
||||||
import android.annotation.SuppressLint
|
import android.annotation.SuppressLint
|
||||||
import android.net.ConnectivityManager
|
import android.net.ConnectivityManager
|
||||||
@@ -25,7 +25,7 @@ import android.net.NetworkCapabilities
|
|||||||
import android.net.NetworkRequest
|
import android.net.NetworkRequest
|
||||||
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.Application
|
||||||
import com.android.systemui.statusbar.pipeline.ConnectivityPipelineLogger
|
import com.android.systemui.statusbar.pipeline.shared.ConnectivityPipelineLogger
|
||||||
import javax.inject.Inject
|
import javax.inject.Inject
|
||||||
import kotlinx.coroutines.CoroutineScope
|
import kotlinx.coroutines.CoroutineScope
|
||||||
import kotlinx.coroutines.ExperimentalCoroutinesApi
|
import kotlinx.coroutines.ExperimentalCoroutinesApi
|
||||||
@@ -35,7 +35,11 @@ import kotlinx.coroutines.flow.StateFlow
|
|||||||
import kotlinx.coroutines.flow.callbackFlow
|
import kotlinx.coroutines.flow.callbackFlow
|
||||||
import kotlinx.coroutines.flow.stateIn
|
import kotlinx.coroutines.flow.stateIn
|
||||||
|
|
||||||
/** Repository that contains all relevant [NetworkCapabilites] for the current networks */
|
/**
|
||||||
|
* Repository that contains all relevant [NetworkCapabilities] for the current networks.
|
||||||
|
*
|
||||||
|
* TODO(b/238425913): Figure out how to merge this with [WifiRepository].
|
||||||
|
*/
|
||||||
@SysUISingleton
|
@SysUISingleton
|
||||||
class NetworkCapabilitiesRepo @Inject constructor(
|
class NetworkCapabilitiesRepo @Inject constructor(
|
||||||
connectivityManager: ConnectivityManager,
|
connectivityManager: ConnectivityManager,
|
||||||
@@ -88,5 +92,3 @@ data class NetworkCapabilityInfo(
|
|||||||
val network: Network,
|
val network: Network,
|
||||||
val capabilities: NetworkCapabilities,
|
val capabilities: NetworkCapabilities,
|
||||||
)
|
)
|
||||||
|
|
||||||
private const val TAG = "ConnectivityRepository"
|
|
||||||
@@ -0,0 +1,103 @@
|
|||||||
|
/*
|
||||||
|
* 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.repository
|
||||||
|
|
||||||
|
import android.net.wifi.WifiManager
|
||||||
|
import android.net.wifi.WifiManager.TrafficStateCallback
|
||||||
|
import android.util.Log
|
||||||
|
import com.android.systemui.common.coroutine.ConflatedCallbackFlow.conflatedCallbackFlow
|
||||||
|
import com.android.systemui.dagger.SysUISingleton
|
||||||
|
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
|
||||||
|
import com.android.systemui.statusbar.pipeline.wifi.data.model.WifiActivityModel
|
||||||
|
import com.android.systemui.statusbar.pipeline.wifi.data.model.WifiModel
|
||||||
|
import java.util.concurrent.Executor
|
||||||
|
import javax.inject.Inject
|
||||||
|
import kotlinx.coroutines.ExperimentalCoroutinesApi
|
||||||
|
import kotlinx.coroutines.channels.awaitClose
|
||||||
|
import kotlinx.coroutines.flow.Flow
|
||||||
|
import kotlinx.coroutines.flow.flowOf
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Provides data related to the wifi state.
|
||||||
|
*/
|
||||||
|
interface WifiRepository {
|
||||||
|
/**
|
||||||
|
* Observable for the current state of wifi; `null` when there is no active wifi.
|
||||||
|
*/
|
||||||
|
val wifiModel: Flow<WifiModel?>
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Observable for the current wifi network activity.
|
||||||
|
*/
|
||||||
|
val wifiActivity: Flow<WifiActivityModel>
|
||||||
|
}
|
||||||
|
|
||||||
|
/** Real implementation of [WifiRepository]. */
|
||||||
|
@OptIn(ExperimentalCoroutinesApi::class)
|
||||||
|
@SysUISingleton
|
||||||
|
class WifiRepositoryImpl @Inject constructor(
|
||||||
|
wifiManager: WifiManager?,
|
||||||
|
@Main mainExecutor: Executor,
|
||||||
|
logger: ConnectivityPipelineLogger,
|
||||||
|
) : WifiRepository {
|
||||||
|
|
||||||
|
// TODO(b/238425913): Actually implement the wifiModel flow.
|
||||||
|
override val wifiModel: Flow<WifiModel?> = flowOf(WifiModel(ssid = "AB"))
|
||||||
|
|
||||||
|
override val wifiActivity: Flow<WifiActivityModel> =
|
||||||
|
if (wifiManager == null) {
|
||||||
|
Log.w(SB_LOGGING_TAG, "Null WifiManager; skipping activity callback")
|
||||||
|
flowOf(ACTIVITY_DEFAULT)
|
||||||
|
} else {
|
||||||
|
conflatedCallbackFlow {
|
||||||
|
val callback = TrafficStateCallback { state ->
|
||||||
|
logger.logInputChange("onTrafficStateChange", prettyPrintActivity(state))
|
||||||
|
trySend(trafficStateToWifiActivityModel(state))
|
||||||
|
}
|
||||||
|
|
||||||
|
wifiManager.registerTrafficStateCallback(mainExecutor, callback)
|
||||||
|
trySend(ACTIVITY_DEFAULT)
|
||||||
|
|
||||||
|
awaitClose { wifiManager.unregisterTrafficStateCallback(callback) }
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
companion object {
|
||||||
|
val ACTIVITY_DEFAULT = WifiActivityModel(hasActivityIn = false, hasActivityOut = false)
|
||||||
|
|
||||||
|
private fun trafficStateToWifiActivityModel(state: Int): WifiActivityModel {
|
||||||
|
return WifiActivityModel(
|
||||||
|
hasActivityIn = state == TrafficStateCallback.DATA_ACTIVITY_IN ||
|
||||||
|
state == TrafficStateCallback.DATA_ACTIVITY_INOUT,
|
||||||
|
hasActivityOut = state == TrafficStateCallback.DATA_ACTIVITY_OUT ||
|
||||||
|
state == TrafficStateCallback.DATA_ACTIVITY_INOUT,
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
private fun prettyPrintActivity(activity: Int): String {
|
||||||
|
return when (activity) {
|
||||||
|
TrafficStateCallback.DATA_ACTIVITY_NONE -> "NONE"
|
||||||
|
TrafficStateCallback.DATA_ACTIVITY_IN -> "IN"
|
||||||
|
TrafficStateCallback.DATA_ACTIVITY_OUT -> "OUT"
|
||||||
|
TrafficStateCallback.DATA_ACTIVITY_INOUT -> "INOUT"
|
||||||
|
else -> "INVALID"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,50 @@
|
|||||||
|
/*
|
||||||
|
* 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.domain.interactor
|
||||||
|
|
||||||
|
import android.net.wifi.WifiManager
|
||||||
|
import com.android.systemui.dagger.SysUISingleton
|
||||||
|
import com.android.systemui.statusbar.pipeline.wifi.data.repository.WifiRepository
|
||||||
|
import javax.inject.Inject
|
||||||
|
import kotlinx.coroutines.flow.Flow
|
||||||
|
import kotlinx.coroutines.flow.combine
|
||||||
|
import kotlinx.coroutines.flow.map
|
||||||
|
|
||||||
|
/**
|
||||||
|
* The business logic layer for the wifi icon.
|
||||||
|
*
|
||||||
|
* This interactor processes information from our data layer into information that the UI layer can
|
||||||
|
* use.
|
||||||
|
*/
|
||||||
|
@SysUISingleton
|
||||||
|
class WifiInteractor @Inject constructor(
|
||||||
|
repository: WifiRepository,
|
||||||
|
) {
|
||||||
|
private val ssid: Flow<String?> = repository.wifiModel.map { info ->
|
||||||
|
when {
|
||||||
|
info == null -> null
|
||||||
|
info.isPasspointAccessPoint || info.isOnlineSignUpForPasspointAccessPoint ->
|
||||||
|
info.passpointProviderFriendlyName
|
||||||
|
info.ssid != WifiManager.UNKNOWN_SSID -> info.ssid
|
||||||
|
else -> null
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
val hasActivityIn: Flow<Boolean> = combine(repository.wifiActivity, ssid) { activity, ssid ->
|
||||||
|
activity.hasActivityIn && ssid != null
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,49 @@
|
|||||||
|
/*
|
||||||
|
* 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.shared
|
||||||
|
|
||||||
|
import android.content.Context
|
||||||
|
import com.android.systemui.Dumpable
|
||||||
|
import com.android.systemui.R
|
||||||
|
import com.android.systemui.dagger.SysUISingleton
|
||||||
|
import com.android.systemui.dump.DumpManager
|
||||||
|
import com.android.systemui.statusbar.pipeline.shared.ConnectivityPipelineLogger.Companion.SB_LOGGING_TAG
|
||||||
|
import java.io.PrintWriter
|
||||||
|
import javax.inject.Inject
|
||||||
|
|
||||||
|
/**
|
||||||
|
* An object storing constants that we use for calculating the wifi icon. Stored in a class for
|
||||||
|
* logging purposes.
|
||||||
|
*/
|
||||||
|
@SysUISingleton
|
||||||
|
class WifiConstants @Inject constructor(
|
||||||
|
context: Context,
|
||||||
|
dumpManager: DumpManager,
|
||||||
|
) : Dumpable {
|
||||||
|
init {
|
||||||
|
dumpManager.registerDumpable("$SB_LOGGING_TAG:WifiConstants", this)
|
||||||
|
}
|
||||||
|
|
||||||
|
/** True if we should show the activityIn/activityOut icons and false otherwise. */
|
||||||
|
val shouldShowActivityConfig = context.resources.getBoolean(R.bool.config_showActivity)
|
||||||
|
|
||||||
|
override fun dump(pw: PrintWriter, args: Array<out String>) {
|
||||||
|
pw.apply {
|
||||||
|
println("shouldShowActivityConfig=$shouldShowActivityConfig")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,45 @@
|
|||||||
|
/*
|
||||||
|
* 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.ui.viewmodel
|
||||||
|
|
||||||
|
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.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.flowOf
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 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(
|
||||||
|
private val constants: WifiConstants,
|
||||||
|
private val logger: ConnectivityPipelineLogger,
|
||||||
|
private val interactor: WifiInteractor,
|
||||||
|
) {
|
||||||
|
val isActivityInVisible: Flow<Boolean>
|
||||||
|
get() =
|
||||||
|
if (!constants.shouldShowActivityConfig) {
|
||||||
|
flowOf(false)
|
||||||
|
} else {
|
||||||
|
interactor.hasActivityIn
|
||||||
|
}
|
||||||
|
.logOutputChange(logger, "activityInVisible")
|
||||||
|
}
|
||||||
@@ -20,7 +20,7 @@ import android.net.NetworkCapabilities
|
|||||||
import android.testing.AndroidTestingRunner
|
import android.testing.AndroidTestingRunner
|
||||||
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.repository.NetworkCapabilityInfo
|
import com.android.systemui.statusbar.pipeline.wifi.data.repository.NetworkCapabilityInfo
|
||||||
import com.android.systemui.util.mockito.mock
|
import com.android.systemui.util.mockito.mock
|
||||||
import com.google.common.truth.Truth.assertThat
|
import com.google.common.truth.Truth.assertThat
|
||||||
import kotlinx.coroutines.CoroutineScope
|
import kotlinx.coroutines.CoroutineScope
|
||||||
@@ -59,6 +59,7 @@ class ConnectivityInfoProcessorTest : SysuiTestCase() {
|
|||||||
context,
|
context,
|
||||||
scope,
|
scope,
|
||||||
statusBarPipelineFlags,
|
statusBarPipelineFlags,
|
||||||
|
mock(),
|
||||||
)
|
)
|
||||||
|
|
||||||
var mostRecentValue: ProcessedConnectivityInfo? = null
|
var mostRecentValue: ProcessedConnectivityInfo? = null
|
||||||
|
|||||||
@@ -14,7 +14,7 @@
|
|||||||
* limitations under the License.
|
* limitations under the License.
|
||||||
*/
|
*/
|
||||||
|
|
||||||
package com.android.systemui.statusbar.pipeline
|
package com.android.systemui.statusbar.pipeline.shared
|
||||||
|
|
||||||
import android.net.Network
|
import android.net.Network
|
||||||
import android.net.NetworkCapabilities
|
import android.net.NetworkCapabilities
|
||||||
@@ -0,0 +1,40 @@
|
|||||||
|
/*
|
||||||
|
* 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.repository
|
||||||
|
|
||||||
|
import com.android.systemui.statusbar.pipeline.wifi.data.model.WifiActivityModel
|
||||||
|
import com.android.systemui.statusbar.pipeline.wifi.data.model.WifiModel
|
||||||
|
import com.android.systemui.statusbar.pipeline.wifi.data.repository.WifiRepositoryImpl.Companion.ACTIVITY_DEFAULT
|
||||||
|
import kotlinx.coroutines.flow.Flow
|
||||||
|
import kotlinx.coroutines.flow.MutableStateFlow
|
||||||
|
|
||||||
|
/** Fake implementation of [WifiRepository] exposing set methods for all the flows. */
|
||||||
|
class FakeWifiRepository : WifiRepository {
|
||||||
|
private val _wifiModel: MutableStateFlow<WifiModel?> = MutableStateFlow(null)
|
||||||
|
override val wifiModel: Flow<WifiModel?> = _wifiModel
|
||||||
|
|
||||||
|
private val _wifiActivity = MutableStateFlow(ACTIVITY_DEFAULT)
|
||||||
|
override val wifiActivity: Flow<WifiActivityModel> = _wifiActivity
|
||||||
|
|
||||||
|
fun setWifiModel(wifiModel: WifiModel?) {
|
||||||
|
_wifiModel.value = wifiModel
|
||||||
|
}
|
||||||
|
|
||||||
|
fun setWifiActivity(activity: WifiActivityModel) {
|
||||||
|
_wifiActivity.value = activity
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -14,7 +14,7 @@
|
|||||||
* limitations under the License.
|
* limitations under the License.
|
||||||
*/
|
*/
|
||||||
|
|
||||||
package com.android.systemui.statusbar.pipeline.repository
|
package com.android.systemui.statusbar.pipeline.wifi.data.repository
|
||||||
|
|
||||||
import android.net.ConnectivityManager
|
import android.net.ConnectivityManager
|
||||||
import android.net.ConnectivityManager.NetworkCallback
|
import android.net.ConnectivityManager.NetworkCallback
|
||||||
@@ -28,7 +28,7 @@ import android.net.NetworkRequest
|
|||||||
import android.test.suitebuilder.annotation.SmallTest
|
import android.test.suitebuilder.annotation.SmallTest
|
||||||
import android.testing.AndroidTestingRunner
|
import android.testing.AndroidTestingRunner
|
||||||
import com.android.systemui.SysuiTestCase
|
import com.android.systemui.SysuiTestCase
|
||||||
import com.android.systemui.statusbar.pipeline.ConnectivityPipelineLogger
|
import com.android.systemui.statusbar.pipeline.shared.ConnectivityPipelineLogger
|
||||||
import com.android.systemui.util.mockito.mock
|
import com.android.systemui.util.mockito.mock
|
||||||
import com.android.systemui.util.mockito.withArgCaptor
|
import com.android.systemui.util.mockito.withArgCaptor
|
||||||
import com.google.common.truth.Truth.assertThat
|
import com.google.common.truth.Truth.assertThat
|
||||||
@@ -0,0 +1,175 @@
|
|||||||
|
/*
|
||||||
|
* 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.repository
|
||||||
|
|
||||||
|
import android.net.wifi.WifiManager
|
||||||
|
import android.net.wifi.WifiManager.TrafficStateCallback
|
||||||
|
import androidx.test.filters.SmallTest
|
||||||
|
import com.android.systemui.SysuiTestCase
|
||||||
|
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.repository.WifiRepositoryImpl.Companion.ACTIVITY_DEFAULT
|
||||||
|
import com.android.systemui.util.concurrency.FakeExecutor
|
||||||
|
import com.android.systemui.util.mockito.any
|
||||||
|
import com.android.systemui.util.mockito.argumentCaptor
|
||||||
|
import com.android.systemui.util.time.FakeSystemClock
|
||||||
|
import com.google.common.truth.Truth.assertThat
|
||||||
|
import java.util.concurrent.Executor
|
||||||
|
import kotlinx.coroutines.Dispatchers
|
||||||
|
import kotlinx.coroutines.ExperimentalCoroutinesApi
|
||||||
|
import kotlinx.coroutines.flow.launchIn
|
||||||
|
import kotlinx.coroutines.flow.onEach
|
||||||
|
import kotlinx.coroutines.runBlocking
|
||||||
|
import org.junit.Before
|
||||||
|
import org.junit.Test
|
||||||
|
import org.mockito.Mock
|
||||||
|
import org.mockito.Mockito.verify
|
||||||
|
import org.mockito.MockitoAnnotations
|
||||||
|
|
||||||
|
@OptIn(ExperimentalCoroutinesApi::class)
|
||||||
|
@SmallTest
|
||||||
|
class WifiRepositoryImplTest : SysuiTestCase() {
|
||||||
|
|
||||||
|
private lateinit var underTest: WifiRepositoryImpl
|
||||||
|
|
||||||
|
@Mock private lateinit var logger: ConnectivityPipelineLogger
|
||||||
|
@Mock private lateinit var wifiManager: WifiManager
|
||||||
|
private lateinit var executor: Executor
|
||||||
|
|
||||||
|
@Before
|
||||||
|
fun setUp() {
|
||||||
|
MockitoAnnotations.initMocks(this)
|
||||||
|
executor = FakeExecutor(FakeSystemClock())
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
fun wifiActivity_nullWifiManager_receivesDefault() = runBlocking(IMMEDIATE) {
|
||||||
|
underTest = WifiRepositoryImpl(
|
||||||
|
wifiManager = null,
|
||||||
|
executor,
|
||||||
|
logger,
|
||||||
|
)
|
||||||
|
|
||||||
|
var latest: WifiActivityModel? = null
|
||||||
|
val job = underTest
|
||||||
|
.wifiActivity
|
||||||
|
.onEach { latest = it }
|
||||||
|
.launchIn(this)
|
||||||
|
|
||||||
|
assertThat(latest).isEqualTo(ACTIVITY_DEFAULT)
|
||||||
|
|
||||||
|
job.cancel()
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
fun wifiActivity_callbackGivesNone_activityFlowHasNone() = runBlocking(IMMEDIATE) {
|
||||||
|
underTest = WifiRepositoryImpl(
|
||||||
|
wifiManager,
|
||||||
|
executor,
|
||||||
|
logger,
|
||||||
|
)
|
||||||
|
|
||||||
|
var latest: WifiActivityModel? = null
|
||||||
|
val job = underTest
|
||||||
|
.wifiActivity
|
||||||
|
.onEach { latest = it }
|
||||||
|
.launchIn(this)
|
||||||
|
|
||||||
|
getTrafficStateCallback().onStateChanged(TrafficStateCallback.DATA_ACTIVITY_NONE)
|
||||||
|
|
||||||
|
assertThat(latest).isEqualTo(
|
||||||
|
WifiActivityModel(hasActivityIn = false, hasActivityOut = false)
|
||||||
|
)
|
||||||
|
|
||||||
|
job.cancel()
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
fun wifiActivity_callbackGivesIn_activityFlowHasIn() = runBlocking(IMMEDIATE) {
|
||||||
|
underTest = WifiRepositoryImpl(
|
||||||
|
wifiManager,
|
||||||
|
executor,
|
||||||
|
logger,
|
||||||
|
)
|
||||||
|
|
||||||
|
var latest: WifiActivityModel? = null
|
||||||
|
val job = underTest
|
||||||
|
.wifiActivity
|
||||||
|
.onEach { latest = it }
|
||||||
|
.launchIn(this)
|
||||||
|
|
||||||
|
getTrafficStateCallback().onStateChanged(TrafficStateCallback.DATA_ACTIVITY_IN)
|
||||||
|
|
||||||
|
assertThat(latest).isEqualTo(
|
||||||
|
WifiActivityModel(hasActivityIn = true, hasActivityOut = false)
|
||||||
|
)
|
||||||
|
|
||||||
|
job.cancel()
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
fun wifiActivity_callbackGivesOut_activityFlowHasOut() = runBlocking(IMMEDIATE) {
|
||||||
|
underTest = WifiRepositoryImpl(
|
||||||
|
wifiManager,
|
||||||
|
executor,
|
||||||
|
logger,
|
||||||
|
)
|
||||||
|
|
||||||
|
var latest: WifiActivityModel? = null
|
||||||
|
val job = underTest
|
||||||
|
.wifiActivity
|
||||||
|
.onEach { latest = it }
|
||||||
|
.launchIn(this)
|
||||||
|
|
||||||
|
getTrafficStateCallback().onStateChanged(TrafficStateCallback.DATA_ACTIVITY_OUT)
|
||||||
|
|
||||||
|
assertThat(latest).isEqualTo(
|
||||||
|
WifiActivityModel(hasActivityIn = false, hasActivityOut = true)
|
||||||
|
)
|
||||||
|
|
||||||
|
job.cancel()
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
fun wifiActivity_callbackGivesInout_activityFlowHasInAndOut() = runBlocking(IMMEDIATE) {
|
||||||
|
underTest = WifiRepositoryImpl(
|
||||||
|
wifiManager,
|
||||||
|
executor,
|
||||||
|
logger,
|
||||||
|
)
|
||||||
|
|
||||||
|
var latest: WifiActivityModel? = null
|
||||||
|
val job = underTest
|
||||||
|
.wifiActivity
|
||||||
|
.onEach { latest = it }
|
||||||
|
.launchIn(this)
|
||||||
|
|
||||||
|
getTrafficStateCallback().onStateChanged(TrafficStateCallback.DATA_ACTIVITY_INOUT)
|
||||||
|
|
||||||
|
assertThat(latest).isEqualTo(WifiActivityModel(hasActivityIn = true, hasActivityOut = true))
|
||||||
|
|
||||||
|
job.cancel()
|
||||||
|
}
|
||||||
|
|
||||||
|
private fun getTrafficStateCallback(): TrafficStateCallback {
|
||||||
|
val callbackCaptor = argumentCaptor<TrafficStateCallback>()
|
||||||
|
verify(wifiManager).registerTrafficStateCallback(any(), callbackCaptor.capture())
|
||||||
|
return callbackCaptor.value!!
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private val IMMEDIATE = Dispatchers.Main.immediate
|
||||||
@@ -0,0 +1,163 @@
|
|||||||
|
/*
|
||||||
|
* 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.domain.interactor
|
||||||
|
|
||||||
|
import androidx.test.filters.SmallTest
|
||||||
|
import com.android.systemui.SysuiTestCase
|
||||||
|
import com.android.systemui.statusbar.pipeline.wifi.data.model.WifiActivityModel
|
||||||
|
import com.android.systemui.statusbar.pipeline.wifi.data.model.WifiModel
|
||||||
|
import com.android.systemui.statusbar.pipeline.wifi.data.repository.FakeWifiRepository
|
||||||
|
import com.google.common.truth.Truth.assertThat
|
||||||
|
import kotlinx.coroutines.Dispatchers
|
||||||
|
import kotlinx.coroutines.ExperimentalCoroutinesApi
|
||||||
|
import kotlinx.coroutines.flow.launchIn
|
||||||
|
import kotlinx.coroutines.flow.onEach
|
||||||
|
import kotlinx.coroutines.runBlocking
|
||||||
|
import kotlinx.coroutines.yield
|
||||||
|
import org.junit.Before
|
||||||
|
import org.junit.Test
|
||||||
|
|
||||||
|
@OptIn(ExperimentalCoroutinesApi::class)
|
||||||
|
@SmallTest
|
||||||
|
class WifiInteractorTest : SysuiTestCase() {
|
||||||
|
|
||||||
|
private lateinit var underTest: WifiInteractor
|
||||||
|
|
||||||
|
private lateinit var repository: FakeWifiRepository
|
||||||
|
|
||||||
|
@Before
|
||||||
|
fun setUp() {
|
||||||
|
repository = FakeWifiRepository()
|
||||||
|
underTest = WifiInteractor(repository)
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
fun hasActivityIn_noInOrOut_outputsFalse() = runBlocking(IMMEDIATE) {
|
||||||
|
repository.setWifiModel(WifiModel(ssid = "AB"))
|
||||||
|
repository.setWifiActivity(WifiActivityModel(hasActivityIn = false, hasActivityOut = false))
|
||||||
|
|
||||||
|
var latest: Boolean? = null
|
||||||
|
val job = underTest
|
||||||
|
.hasActivityIn
|
||||||
|
.onEach { latest = it }
|
||||||
|
.launchIn(this)
|
||||||
|
|
||||||
|
assertThat(latest).isFalse()
|
||||||
|
|
||||||
|
job.cancel()
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
fun hasActivityIn_onlyOut_outputsFalse() = runBlocking(IMMEDIATE) {
|
||||||
|
repository.setWifiModel(WifiModel(ssid = "AB"))
|
||||||
|
repository.setWifiActivity(WifiActivityModel(hasActivityIn = false, hasActivityOut = true))
|
||||||
|
|
||||||
|
var latest: Boolean? = null
|
||||||
|
val job = underTest
|
||||||
|
.hasActivityIn
|
||||||
|
.onEach { latest = it }
|
||||||
|
.launchIn(this)
|
||||||
|
|
||||||
|
assertThat(latest).isFalse()
|
||||||
|
|
||||||
|
job.cancel()
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
fun hasActivityIn_onlyIn_outputsTrue() = runBlocking(IMMEDIATE) {
|
||||||
|
repository.setWifiModel(WifiModel(ssid = "AB"))
|
||||||
|
repository.setWifiActivity(WifiActivityModel(hasActivityIn = true, hasActivityOut = false))
|
||||||
|
|
||||||
|
var latest: Boolean? = null
|
||||||
|
val job = underTest
|
||||||
|
.hasActivityIn
|
||||||
|
.onEach { latest = it }
|
||||||
|
.launchIn(this)
|
||||||
|
|
||||||
|
assertThat(latest).isTrue()
|
||||||
|
|
||||||
|
job.cancel()
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
fun hasActivityIn_inAndOut_outputsTrue() = runBlocking(IMMEDIATE) {
|
||||||
|
repository.setWifiModel(WifiModel(ssid = "AB"))
|
||||||
|
repository.setWifiActivity(WifiActivityModel(hasActivityIn = true, hasActivityOut = true))
|
||||||
|
|
||||||
|
var latest: Boolean? = null
|
||||||
|
val job = underTest
|
||||||
|
.hasActivityIn
|
||||||
|
.onEach { latest = it }
|
||||||
|
.launchIn(this)
|
||||||
|
|
||||||
|
assertThat(latest).isTrue()
|
||||||
|
|
||||||
|
job.cancel()
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
fun hasActivityIn_ssidNull_outputsFalse() = runBlocking(IMMEDIATE) {
|
||||||
|
repository.setWifiModel(WifiModel(ssid = null))
|
||||||
|
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.setWifiModel(WifiModel(ssid = "AB"))
|
||||||
|
|
||||||
|
var latest: Boolean? = null
|
||||||
|
val job = underTest
|
||||||
|
.hasActivityIn
|
||||||
|
.onEach { latest = it }
|
||||||
|
.launchIn(this)
|
||||||
|
|
||||||
|
// Conduct a series of changes and verify we catch each of them in succession
|
||||||
|
repository.setWifiActivity(WifiActivityModel(hasActivityIn = true, hasActivityOut = false))
|
||||||
|
yield()
|
||||||
|
assertThat(latest).isTrue()
|
||||||
|
|
||||||
|
repository.setWifiActivity(WifiActivityModel(hasActivityIn = false, hasActivityOut = true))
|
||||||
|
yield()
|
||||||
|
assertThat(latest).isFalse()
|
||||||
|
|
||||||
|
repository.setWifiActivity(WifiActivityModel(hasActivityIn = true, hasActivityOut = true))
|
||||||
|
yield()
|
||||||
|
assertThat(latest).isTrue()
|
||||||
|
|
||||||
|
repository.setWifiActivity(WifiActivityModel(hasActivityIn = true, hasActivityOut = false))
|
||||||
|
yield()
|
||||||
|
assertThat(latest).isTrue()
|
||||||
|
|
||||||
|
repository.setWifiActivity(WifiActivityModel(hasActivityIn = false, hasActivityOut = false))
|
||||||
|
yield()
|
||||||
|
assertThat(latest).isFalse()
|
||||||
|
|
||||||
|
job.cancel()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private val IMMEDIATE = Dispatchers.Main.immediate
|
||||||
@@ -0,0 +1,124 @@
|
|||||||
|
/*
|
||||||
|
* 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.ui.viewmodel
|
||||||
|
|
||||||
|
import androidx.test.filters.SmallTest
|
||||||
|
import com.android.systemui.SysuiTestCase
|
||||||
|
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.WifiModel
|
||||||
|
import com.android.systemui.statusbar.pipeline.wifi.data.repository.FakeWifiRepository
|
||||||
|
import com.android.systemui.statusbar.pipeline.wifi.domain.interactor.WifiInteractor
|
||||||
|
import com.android.systemui.statusbar.pipeline.wifi.shared.WifiConstants
|
||||||
|
import com.google.common.truth.Truth.assertThat
|
||||||
|
import kotlinx.coroutines.Dispatchers
|
||||||
|
import kotlinx.coroutines.ExperimentalCoroutinesApi
|
||||||
|
import kotlinx.coroutines.flow.launchIn
|
||||||
|
import kotlinx.coroutines.flow.onEach
|
||||||
|
import kotlinx.coroutines.runBlocking
|
||||||
|
import kotlinx.coroutines.yield
|
||||||
|
import org.junit.Before
|
||||||
|
import org.junit.Test
|
||||||
|
import org.mockito.Mock
|
||||||
|
import org.mockito.Mockito.`when` as whenever
|
||||||
|
import org.mockito.MockitoAnnotations
|
||||||
|
|
||||||
|
@OptIn(ExperimentalCoroutinesApi::class)
|
||||||
|
@SmallTest
|
||||||
|
class WifiViewModelTest : SysuiTestCase() {
|
||||||
|
|
||||||
|
private lateinit var underTest: WifiViewModel
|
||||||
|
|
||||||
|
@Mock private lateinit var logger: ConnectivityPipelineLogger
|
||||||
|
@Mock private lateinit var constants: WifiConstants
|
||||||
|
private lateinit var repository: FakeWifiRepository
|
||||||
|
private lateinit var interactor: WifiInteractor
|
||||||
|
|
||||||
|
@Before
|
||||||
|
fun setUp() {
|
||||||
|
MockitoAnnotations.initMocks(this)
|
||||||
|
repository = FakeWifiRepository()
|
||||||
|
interactor = WifiInteractor(repository)
|
||||||
|
|
||||||
|
underTest = WifiViewModel(
|
||||||
|
constants,
|
||||||
|
logger,
|
||||||
|
interactor
|
||||||
|
)
|
||||||
|
|
||||||
|
// Set up with a valid SSID
|
||||||
|
repository.setWifiModel(WifiModel(ssid = "AB"))
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
fun activityInVisible_showActivityConfigFalse_receivesFalse() = runBlocking(IMMEDIATE) {
|
||||||
|
whenever(constants.shouldShowActivityConfig).thenReturn(false)
|
||||||
|
|
||||||
|
var latest: Boolean? = null
|
||||||
|
val job = underTest
|
||||||
|
.isActivityInVisible
|
||||||
|
.onEach { latest = it }
|
||||||
|
.launchIn(this)
|
||||||
|
|
||||||
|
// Verify that on launch, we receive a false.
|
||||||
|
assertThat(latest).isFalse()
|
||||||
|
|
||||||
|
job.cancel()
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
fun activityInVisible_showActivityConfigFalse_noUpdatesReceived() = runBlocking(IMMEDIATE) {
|
||||||
|
whenever(constants.shouldShowActivityConfig).thenReturn(false)
|
||||||
|
|
||||||
|
var latest: Boolean? = null
|
||||||
|
val job = underTest
|
||||||
|
.isActivityInVisible
|
||||||
|
.onEach { latest = it }
|
||||||
|
.launchIn(this)
|
||||||
|
|
||||||
|
// Update the repo to have activityIn
|
||||||
|
repository.setWifiActivity(WifiActivityModel(hasActivityIn = true, hasActivityOut = false))
|
||||||
|
yield()
|
||||||
|
|
||||||
|
// Verify that we didn't update to activityIn=true (because our config is false)
|
||||||
|
assertThat(latest).isFalse()
|
||||||
|
|
||||||
|
job.cancel()
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
fun activityInVisible_showActivityConfigTrue_receivesUpdate() = runBlocking(IMMEDIATE) {
|
||||||
|
whenever(constants.shouldShowActivityConfig).thenReturn(true)
|
||||||
|
|
||||||
|
var latest: Boolean? = null
|
||||||
|
val job = underTest
|
||||||
|
.isActivityInVisible
|
||||||
|
.onEach { latest = it }
|
||||||
|
.launchIn(this)
|
||||||
|
|
||||||
|
// Update the repo to have activityIn
|
||||||
|
repository.setWifiActivity(WifiActivityModel(hasActivityIn = true, hasActivityOut = false))
|
||||||
|
yield()
|
||||||
|
|
||||||
|
// Verify that we updated to activityIn=true
|
||||||
|
assertThat(latest).isTrue()
|
||||||
|
|
||||||
|
job.cancel()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private val IMMEDIATE = Dispatchers.Main.immediate
|
||||||
Reference in New Issue
Block a user