Loading src/com/android/settings/wifi/WifiSummaryRepository.kt +29 −49 Original line number Diff line number Diff line Loading @@ -17,41 +17,42 @@ package com.android.settings.wifi import android.content.Context import android.content.IntentFilter import android.net.ConnectivityManager import android.net.NetworkScoreManager import android.net.wifi.WifiInfo import android.net.wifi.WifiManager import com.android.settings.wifi.repository.SharedConnectivityRepository import com.android.settings.wifi.repository.WifiPickerRepository import com.android.settings.wifi.repository.WifiStatusRepository import com.android.settingslib.R import com.android.settingslib.spaprivileged.framework.common.broadcastReceiverFlow import com.android.settingslib.wifi.WifiStatusTracker import com.android.wifitrackerlib.HotspotNetworkEntry import kotlinx.coroutines.Dispatchers import kotlinx.coroutines.channels.awaitClose import kotlinx.coroutines.flow.Flow import kotlinx.coroutines.flow.callbackFlow import kotlinx.coroutines.flow.combine import kotlinx.coroutines.flow.conflate import kotlinx.coroutines.flow.flowOn import kotlinx.coroutines.flow.launchIn import kotlinx.coroutines.flow.map import kotlinx.coroutines.flow.onEach /** * Repository that listeners to wifi callback and provide wifi summary flow to client. */ /** Repository that listeners to wifi callback and provide wifi summary flow to client. */ class WifiSummaryRepository( private val context: Context, private val wifiStatusTrackerFactory: (callback: Runnable) -> WifiStatusTracker = { callback -> WifiStatusTracker( context, context.getSystemService(WifiManager::class.java), context.getSystemService(NetworkScoreManager::class.java), context.getSystemService(ConnectivityManager::class.java), callback, ) }, private val wifiStatusRepository: WifiStatusRepository = WifiStatusRepository(context), private val wifiPickerRepository: WifiPickerRepository? = if (SharedConnectivityRepository.isDeviceConfigEnabled()) WifiPickerRepository(context) else null, ) { fun summaryFlow() = wifiStatusTrackerFlow() fun summaryFlow(): Flow<String> { if (wifiPickerRepository == null) return wifiStatusSummaryFlow() return combine( wifiStatusSummaryFlow(), wifiPickerRepository.connectedWifiEntryFlow(), ) { wifiStatusSummary, wifiEntry -> if (wifiEntry is HotspotNetworkEntry) wifiEntry.alternateSummary else wifiStatusSummary } } private fun wifiStatusSummaryFlow() = wifiStatusRepository .wifiStatusTrackerFlow() .map { wifiStatusTracker -> wifiStatusTracker.getSummary() } .conflate() .flowOn(Dispatchers.Default) Loading @@ -62,30 +63,9 @@ class WifiSummaryRepository( val sanitizedSsid = WifiInfo.sanitizeSsid(ssid) ?: "" if (statusLabel.isNullOrEmpty()) return sanitizedSsid return context.getString( R.string.preference_summary_default_combination, sanitizedSsid, statusLabel R.string.preference_summary_default_combination, sanitizedSsid, statusLabel, ) } private fun wifiStatusTrackerFlow(): Flow<WifiStatusTracker> = callbackFlow { var wifiStatusTracker: WifiStatusTracker? = null wifiStatusTracker = wifiStatusTrackerFactory { wifiStatusTracker?.let(::trySend) } context.broadcastReceiverFlow(INTENT_FILTER) .onEach { intent -> wifiStatusTracker.handleBroadcast(intent) } .launchIn(this) wifiStatusTracker.setListening(true) wifiStatusTracker.fetchInitialState() trySend(wifiStatusTracker) awaitClose { wifiStatusTracker.setListening(false) } }.conflate().flowOn(Dispatchers.Default) private companion object { val INTENT_FILTER = IntentFilter().apply { addAction(WifiManager.WIFI_STATE_CHANGED_ACTION) addAction(WifiManager.NETWORK_STATE_CHANGED_ACTION) addAction(WifiManager.RSSI_CHANGED_ACTION) } } } src/com/android/settings/wifi/repository/WifiPickerRepository.kt 0 → 100644 +101 −0 Original line number Diff line number Diff line /* * Copyright (C) 2024 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.settings.wifi.repository import android.content.Context import android.os.Handler import android.os.HandlerThread import android.os.Looper import android.os.Process import android.os.SystemClock import android.util.Log import com.android.settings.overlay.FeatureFactory.Companion.featureFactory import com.android.wifitrackerlib.WifiEntry import com.android.wifitrackerlib.WifiPickerTracker import kotlinx.coroutines.Dispatchers import kotlinx.coroutines.channels.awaitClose import kotlinx.coroutines.flow.Flow import kotlinx.coroutines.flow.callbackFlow import kotlinx.coroutines.flow.conflate import kotlinx.coroutines.flow.flowOn import kotlinx.coroutines.flow.onEach /** Repository that listeners to wifi picker callback and provide wifi picker flow to client. */ class WifiPickerRepository( private val context: Context, private val createWifiPickerTracker: ( workerThread: HandlerThread, callback: WifiPickerTracker.WifiPickerTrackerCallback ) -> WifiPickerTracker = { workerThread, callback -> featureFactory.wifiTrackerLibProvider.createWifiPickerTracker( null, context, Handler(Looper.getMainLooper()), workerThread.getThreadHandler(), SystemClock.elapsedRealtimeClock(), MAX_SCAN_AGE_MILLIS, SCAN_INTERVAL_MILLIS, callback, ) } ) { fun connectedWifiEntryFlow(): Flow<WifiEntry?> = callbackFlow { val workerThread = HandlerThread( /* name = */ "$TAG{${Integer.toHexString(System.identityHashCode(this))}}", /* priority = */ Process.THREAD_PRIORITY_BACKGROUND, ) workerThread.start() var tracker: WifiPickerTracker? = null val callback = object : WifiPickerTracker.WifiPickerTrackerCallback { override fun onWifiEntriesChanged() { trySend(tracker?.connectedWifiEntry) } override fun onWifiStateChanged() {} override fun onNumSavedNetworksChanged() {} override fun onNumSavedSubscriptionsChanged() {} } tracker = createWifiPickerTracker(workerThread, callback) tracker.onStart() awaitClose { tracker.onStop() tracker.onDestroy() workerThread.quit() } } .conflate() .onEach { Log.d(TAG, "connectedWifiEntryFlow: $it") } .flowOn(Dispatchers.Default) companion object { private const val TAG = "WifiPickerRepository" /** Max age of tracked WifiEntries */ private const val MAX_SCAN_AGE_MILLIS: Long = 15000 /** Interval between initiating WifiPickerTracker scans */ private const val SCAN_INTERVAL_MILLIS: Long = 10000 } } src/com/android/settings/wifi/repository/WifiStatusRepository.kt 0 → 100644 +75 −0 Original line number Diff line number Diff line /* * Copyright (C) 2024 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.settings.wifi.repository import android.content.Context import android.content.IntentFilter import android.net.ConnectivityManager import android.net.NetworkScoreManager import android.net.wifi.WifiManager import com.android.settingslib.spaprivileged.framework.common.broadcastReceiverFlow import com.android.settingslib.wifi.WifiStatusTracker import kotlinx.coroutines.Dispatchers import kotlinx.coroutines.channels.awaitClose import kotlinx.coroutines.flow.Flow import kotlinx.coroutines.flow.callbackFlow import kotlinx.coroutines.flow.conflate import kotlinx.coroutines.flow.flowOn import kotlinx.coroutines.flow.launchIn import kotlinx.coroutines.flow.onEach /** Repository that listeners to wifi callback and provide wifi status flow to client. */ class WifiStatusRepository( private val context: Context, private val wifiStatusTrackerFactory: (callback: Runnable) -> WifiStatusTracker = { callback -> WifiStatusTracker( context, context.getSystemService(WifiManager::class.java), context.getSystemService(NetworkScoreManager::class.java), context.getSystemService(ConnectivityManager::class.java), callback, ) }, ) { fun wifiStatusTrackerFlow(): Flow<WifiStatusTracker> = callbackFlow { var wifiStatusTracker: WifiStatusTracker? = null wifiStatusTracker = wifiStatusTrackerFactory { wifiStatusTracker?.let(::trySend) } context .broadcastReceiverFlow(INTENT_FILTER) .onEach { intent -> wifiStatusTracker.handleBroadcast(intent) } .launchIn(this) wifiStatusTracker.setListening(true) wifiStatusTracker.fetchInitialState() trySend(wifiStatusTracker) awaitClose { wifiStatusTracker.setListening(false) } } .conflate() .flowOn(Dispatchers.Default) private companion object { val INTENT_FILTER = IntentFilter().apply { addAction(WifiManager.WIFI_STATE_CHANGED_ACTION) addAction(WifiManager.NETWORK_STATE_CHANGED_ACTION) addAction(WifiManager.RSSI_CHANGED_ACTION) } } } tests/spa_unit/src/com/android/settings/wifi/WifiSummaryRepositoryTest.kt +56 −1 Original line number Diff line number Diff line Loading @@ -20,13 +20,19 @@ import android.content.Context import androidx.test.core.app.ApplicationProvider import androidx.test.ext.junit.runners.AndroidJUnit4 import com.android.settings.R import com.android.settings.wifi.repository.WifiPickerRepository import com.android.settings.wifi.repository.WifiStatusRepository import com.android.settingslib.spa.testutils.firstWithTimeoutOrNull import com.android.settingslib.wifi.WifiStatusTracker import com.android.wifitrackerlib.HotspotNetworkEntry import com.google.common.truth.Truth.assertThat import kotlinx.coroutines.flow.flowOf import kotlinx.coroutines.runBlocking import org.junit.Test import org.junit.runner.RunWith import org.mockito.kotlin.doReturn import org.mockito.kotlin.mock import org.mockito.kotlin.stub @RunWith(AndroidJUnit4::class) class WifiSummaryRepositoryTest { Loading @@ -35,11 +41,22 @@ class WifiSummaryRepositoryTest { private val context: Context = ApplicationProvider.getApplicationContext() private val repository = WifiSummaryRepository(context) { mockWifiStatusTracker } private val mockWifiStatusRepository = mock<WifiStatusRepository> { on { wifiStatusTrackerFlow() } doReturn flowOf(mockWifiStatusTracker) } private val mockWifiPickerRepository = mock<WifiPickerRepository>() @Test fun summaryFlow_wifiDisabled_returnOff() = runBlocking { mockWifiStatusTracker.enabled = false val repository = WifiSummaryRepository( context = context, wifiStatusRepository = mockWifiStatusRepository, wifiPickerRepository = null, ) val summary = repository.summaryFlow().firstWithTimeoutOrNull() Loading @@ -52,6 +69,12 @@ class WifiSummaryRepositoryTest { enabled = true connected = false } val repository = WifiSummaryRepository( context = context, wifiStatusRepository = mockWifiStatusRepository, wifiPickerRepository = null, ) val summary = repository.summaryFlow().firstWithTimeoutOrNull() Loading @@ -65,6 +88,12 @@ class WifiSummaryRepositoryTest { connected = true ssid = TEST_SSID } val repository = WifiSummaryRepository( context = context, wifiStatusRepository = mockWifiStatusRepository, wifiPickerRepository = null, ) val summary = repository.summaryFlow().firstWithTimeoutOrNull() Loading @@ -79,14 +108,40 @@ class WifiSummaryRepositoryTest { ssid = TEST_SSID statusLabel = STATUS_LABEL } val repository = WifiSummaryRepository( context = context, wifiStatusRepository = mockWifiStatusRepository, wifiPickerRepository = null, ) val summary = repository.summaryFlow().firstWithTimeoutOrNull() assertThat(summary).isEqualTo("$TEST_SSID / $STATUS_LABEL") } @Test fun summaryFlow_withWifiPickerRepository() = runBlocking { val hotspotNetworkEntry = mock<HotspotNetworkEntry> { on { alternateSummary } doReturn ALTERNATE_SUMMARY } mockWifiPickerRepository.stub { on { connectedWifiEntryFlow() } doReturn flowOf(hotspotNetworkEntry) } val repository = WifiSummaryRepository( context = context, wifiStatusRepository = mockWifiStatusRepository, wifiPickerRepository = mockWifiPickerRepository, ) val summary = repository.summaryFlow().firstWithTimeoutOrNull() assertThat(summary).isEqualTo(ALTERNATE_SUMMARY) } private companion object { const val TEST_SSID = "Test Ssid" const val STATUS_LABEL = "Very Fast" const val ALTERNATE_SUMMARY = "Alternate Summary" } } tests/spa_unit/src/com/android/settings/wifi/repository/WifiPickerRepositoryTest.kt 0 → 100644 +78 −0 Original line number Diff line number Diff line /* * Copyright (C) 2024 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.settings.wifi.repository import android.content.Context import androidx.test.core.app.ApplicationProvider import androidx.test.ext.junit.runners.AndroidJUnit4 import com.android.settingslib.spa.testutils.firstWithTimeoutOrNull import com.android.settingslib.spa.testutils.toListWithTimeout import com.android.wifitrackerlib.WifiEntry import com.android.wifitrackerlib.WifiPickerTracker import com.google.common.truth.Truth.assertThat import kotlinx.coroutines.async import kotlinx.coroutines.delay import kotlinx.coroutines.runBlocking import org.junit.Test import org.junit.runner.RunWith import org.mockito.kotlin.doReturn import org.mockito.kotlin.mock import org.mockito.kotlin.stub import org.mockito.kotlin.verify @RunWith(AndroidJUnit4::class) class WifiPickerRepositoryTest { private val context: Context = ApplicationProvider.getApplicationContext() private val mockWifiPickerTracker = mock<WifiPickerTracker>() private var callback: WifiPickerTracker.WifiPickerTrackerCallback? = null private val repository = WifiPickerRepository(context) { _, callback -> this.callback = callback mockWifiPickerTracker } @Test fun connectedWifiEntryFlow_callOnStartOnStopAndOnDestroy() = runBlocking { repository.connectedWifiEntryFlow().firstWithTimeoutOrNull() verify(mockWifiPickerTracker).onStart() verify(mockWifiPickerTracker).onStop() verify(mockWifiPickerTracker).onDestroy() } @Test fun connectedWifiEntryFlow_initial() = runBlocking { val wifiEntry = repository.connectedWifiEntryFlow().firstWithTimeoutOrNull() assertThat(wifiEntry).isNull() } @Test fun connectedWifiEntryFlow_onWifiEntriesChanged() = runBlocking { val listDeferred = async { repository.connectedWifiEntryFlow().toListWithTimeout() } delay(100) mockWifiPickerTracker.stub { on { connectedWifiEntry } doReturn mock<WifiEntry>() } callback?.onWifiEntriesChanged() assertThat(listDeferred.await().filterNotNull()).isNotEmpty() } } Loading
src/com/android/settings/wifi/WifiSummaryRepository.kt +29 −49 Original line number Diff line number Diff line Loading @@ -17,41 +17,42 @@ package com.android.settings.wifi import android.content.Context import android.content.IntentFilter import android.net.ConnectivityManager import android.net.NetworkScoreManager import android.net.wifi.WifiInfo import android.net.wifi.WifiManager import com.android.settings.wifi.repository.SharedConnectivityRepository import com.android.settings.wifi.repository.WifiPickerRepository import com.android.settings.wifi.repository.WifiStatusRepository import com.android.settingslib.R import com.android.settingslib.spaprivileged.framework.common.broadcastReceiverFlow import com.android.settingslib.wifi.WifiStatusTracker import com.android.wifitrackerlib.HotspotNetworkEntry import kotlinx.coroutines.Dispatchers import kotlinx.coroutines.channels.awaitClose import kotlinx.coroutines.flow.Flow import kotlinx.coroutines.flow.callbackFlow import kotlinx.coroutines.flow.combine import kotlinx.coroutines.flow.conflate import kotlinx.coroutines.flow.flowOn import kotlinx.coroutines.flow.launchIn import kotlinx.coroutines.flow.map import kotlinx.coroutines.flow.onEach /** * Repository that listeners to wifi callback and provide wifi summary flow to client. */ /** Repository that listeners to wifi callback and provide wifi summary flow to client. */ class WifiSummaryRepository( private val context: Context, private val wifiStatusTrackerFactory: (callback: Runnable) -> WifiStatusTracker = { callback -> WifiStatusTracker( context, context.getSystemService(WifiManager::class.java), context.getSystemService(NetworkScoreManager::class.java), context.getSystemService(ConnectivityManager::class.java), callback, ) }, private val wifiStatusRepository: WifiStatusRepository = WifiStatusRepository(context), private val wifiPickerRepository: WifiPickerRepository? = if (SharedConnectivityRepository.isDeviceConfigEnabled()) WifiPickerRepository(context) else null, ) { fun summaryFlow() = wifiStatusTrackerFlow() fun summaryFlow(): Flow<String> { if (wifiPickerRepository == null) return wifiStatusSummaryFlow() return combine( wifiStatusSummaryFlow(), wifiPickerRepository.connectedWifiEntryFlow(), ) { wifiStatusSummary, wifiEntry -> if (wifiEntry is HotspotNetworkEntry) wifiEntry.alternateSummary else wifiStatusSummary } } private fun wifiStatusSummaryFlow() = wifiStatusRepository .wifiStatusTrackerFlow() .map { wifiStatusTracker -> wifiStatusTracker.getSummary() } .conflate() .flowOn(Dispatchers.Default) Loading @@ -62,30 +63,9 @@ class WifiSummaryRepository( val sanitizedSsid = WifiInfo.sanitizeSsid(ssid) ?: "" if (statusLabel.isNullOrEmpty()) return sanitizedSsid return context.getString( R.string.preference_summary_default_combination, sanitizedSsid, statusLabel R.string.preference_summary_default_combination, sanitizedSsid, statusLabel, ) } private fun wifiStatusTrackerFlow(): Flow<WifiStatusTracker> = callbackFlow { var wifiStatusTracker: WifiStatusTracker? = null wifiStatusTracker = wifiStatusTrackerFactory { wifiStatusTracker?.let(::trySend) } context.broadcastReceiverFlow(INTENT_FILTER) .onEach { intent -> wifiStatusTracker.handleBroadcast(intent) } .launchIn(this) wifiStatusTracker.setListening(true) wifiStatusTracker.fetchInitialState() trySend(wifiStatusTracker) awaitClose { wifiStatusTracker.setListening(false) } }.conflate().flowOn(Dispatchers.Default) private companion object { val INTENT_FILTER = IntentFilter().apply { addAction(WifiManager.WIFI_STATE_CHANGED_ACTION) addAction(WifiManager.NETWORK_STATE_CHANGED_ACTION) addAction(WifiManager.RSSI_CHANGED_ACTION) } } }
src/com/android/settings/wifi/repository/WifiPickerRepository.kt 0 → 100644 +101 −0 Original line number Diff line number Diff line /* * Copyright (C) 2024 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.settings.wifi.repository import android.content.Context import android.os.Handler import android.os.HandlerThread import android.os.Looper import android.os.Process import android.os.SystemClock import android.util.Log import com.android.settings.overlay.FeatureFactory.Companion.featureFactory import com.android.wifitrackerlib.WifiEntry import com.android.wifitrackerlib.WifiPickerTracker import kotlinx.coroutines.Dispatchers import kotlinx.coroutines.channels.awaitClose import kotlinx.coroutines.flow.Flow import kotlinx.coroutines.flow.callbackFlow import kotlinx.coroutines.flow.conflate import kotlinx.coroutines.flow.flowOn import kotlinx.coroutines.flow.onEach /** Repository that listeners to wifi picker callback and provide wifi picker flow to client. */ class WifiPickerRepository( private val context: Context, private val createWifiPickerTracker: ( workerThread: HandlerThread, callback: WifiPickerTracker.WifiPickerTrackerCallback ) -> WifiPickerTracker = { workerThread, callback -> featureFactory.wifiTrackerLibProvider.createWifiPickerTracker( null, context, Handler(Looper.getMainLooper()), workerThread.getThreadHandler(), SystemClock.elapsedRealtimeClock(), MAX_SCAN_AGE_MILLIS, SCAN_INTERVAL_MILLIS, callback, ) } ) { fun connectedWifiEntryFlow(): Flow<WifiEntry?> = callbackFlow { val workerThread = HandlerThread( /* name = */ "$TAG{${Integer.toHexString(System.identityHashCode(this))}}", /* priority = */ Process.THREAD_PRIORITY_BACKGROUND, ) workerThread.start() var tracker: WifiPickerTracker? = null val callback = object : WifiPickerTracker.WifiPickerTrackerCallback { override fun onWifiEntriesChanged() { trySend(tracker?.connectedWifiEntry) } override fun onWifiStateChanged() {} override fun onNumSavedNetworksChanged() {} override fun onNumSavedSubscriptionsChanged() {} } tracker = createWifiPickerTracker(workerThread, callback) tracker.onStart() awaitClose { tracker.onStop() tracker.onDestroy() workerThread.quit() } } .conflate() .onEach { Log.d(TAG, "connectedWifiEntryFlow: $it") } .flowOn(Dispatchers.Default) companion object { private const val TAG = "WifiPickerRepository" /** Max age of tracked WifiEntries */ private const val MAX_SCAN_AGE_MILLIS: Long = 15000 /** Interval between initiating WifiPickerTracker scans */ private const val SCAN_INTERVAL_MILLIS: Long = 10000 } }
src/com/android/settings/wifi/repository/WifiStatusRepository.kt 0 → 100644 +75 −0 Original line number Diff line number Diff line /* * Copyright (C) 2024 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.settings.wifi.repository import android.content.Context import android.content.IntentFilter import android.net.ConnectivityManager import android.net.NetworkScoreManager import android.net.wifi.WifiManager import com.android.settingslib.spaprivileged.framework.common.broadcastReceiverFlow import com.android.settingslib.wifi.WifiStatusTracker import kotlinx.coroutines.Dispatchers import kotlinx.coroutines.channels.awaitClose import kotlinx.coroutines.flow.Flow import kotlinx.coroutines.flow.callbackFlow import kotlinx.coroutines.flow.conflate import kotlinx.coroutines.flow.flowOn import kotlinx.coroutines.flow.launchIn import kotlinx.coroutines.flow.onEach /** Repository that listeners to wifi callback and provide wifi status flow to client. */ class WifiStatusRepository( private val context: Context, private val wifiStatusTrackerFactory: (callback: Runnable) -> WifiStatusTracker = { callback -> WifiStatusTracker( context, context.getSystemService(WifiManager::class.java), context.getSystemService(NetworkScoreManager::class.java), context.getSystemService(ConnectivityManager::class.java), callback, ) }, ) { fun wifiStatusTrackerFlow(): Flow<WifiStatusTracker> = callbackFlow { var wifiStatusTracker: WifiStatusTracker? = null wifiStatusTracker = wifiStatusTrackerFactory { wifiStatusTracker?.let(::trySend) } context .broadcastReceiverFlow(INTENT_FILTER) .onEach { intent -> wifiStatusTracker.handleBroadcast(intent) } .launchIn(this) wifiStatusTracker.setListening(true) wifiStatusTracker.fetchInitialState() trySend(wifiStatusTracker) awaitClose { wifiStatusTracker.setListening(false) } } .conflate() .flowOn(Dispatchers.Default) private companion object { val INTENT_FILTER = IntentFilter().apply { addAction(WifiManager.WIFI_STATE_CHANGED_ACTION) addAction(WifiManager.NETWORK_STATE_CHANGED_ACTION) addAction(WifiManager.RSSI_CHANGED_ACTION) } } }
tests/spa_unit/src/com/android/settings/wifi/WifiSummaryRepositoryTest.kt +56 −1 Original line number Diff line number Diff line Loading @@ -20,13 +20,19 @@ import android.content.Context import androidx.test.core.app.ApplicationProvider import androidx.test.ext.junit.runners.AndroidJUnit4 import com.android.settings.R import com.android.settings.wifi.repository.WifiPickerRepository import com.android.settings.wifi.repository.WifiStatusRepository import com.android.settingslib.spa.testutils.firstWithTimeoutOrNull import com.android.settingslib.wifi.WifiStatusTracker import com.android.wifitrackerlib.HotspotNetworkEntry import com.google.common.truth.Truth.assertThat import kotlinx.coroutines.flow.flowOf import kotlinx.coroutines.runBlocking import org.junit.Test import org.junit.runner.RunWith import org.mockito.kotlin.doReturn import org.mockito.kotlin.mock import org.mockito.kotlin.stub @RunWith(AndroidJUnit4::class) class WifiSummaryRepositoryTest { Loading @@ -35,11 +41,22 @@ class WifiSummaryRepositoryTest { private val context: Context = ApplicationProvider.getApplicationContext() private val repository = WifiSummaryRepository(context) { mockWifiStatusTracker } private val mockWifiStatusRepository = mock<WifiStatusRepository> { on { wifiStatusTrackerFlow() } doReturn flowOf(mockWifiStatusTracker) } private val mockWifiPickerRepository = mock<WifiPickerRepository>() @Test fun summaryFlow_wifiDisabled_returnOff() = runBlocking { mockWifiStatusTracker.enabled = false val repository = WifiSummaryRepository( context = context, wifiStatusRepository = mockWifiStatusRepository, wifiPickerRepository = null, ) val summary = repository.summaryFlow().firstWithTimeoutOrNull() Loading @@ -52,6 +69,12 @@ class WifiSummaryRepositoryTest { enabled = true connected = false } val repository = WifiSummaryRepository( context = context, wifiStatusRepository = mockWifiStatusRepository, wifiPickerRepository = null, ) val summary = repository.summaryFlow().firstWithTimeoutOrNull() Loading @@ -65,6 +88,12 @@ class WifiSummaryRepositoryTest { connected = true ssid = TEST_SSID } val repository = WifiSummaryRepository( context = context, wifiStatusRepository = mockWifiStatusRepository, wifiPickerRepository = null, ) val summary = repository.summaryFlow().firstWithTimeoutOrNull() Loading @@ -79,14 +108,40 @@ class WifiSummaryRepositoryTest { ssid = TEST_SSID statusLabel = STATUS_LABEL } val repository = WifiSummaryRepository( context = context, wifiStatusRepository = mockWifiStatusRepository, wifiPickerRepository = null, ) val summary = repository.summaryFlow().firstWithTimeoutOrNull() assertThat(summary).isEqualTo("$TEST_SSID / $STATUS_LABEL") } @Test fun summaryFlow_withWifiPickerRepository() = runBlocking { val hotspotNetworkEntry = mock<HotspotNetworkEntry> { on { alternateSummary } doReturn ALTERNATE_SUMMARY } mockWifiPickerRepository.stub { on { connectedWifiEntryFlow() } doReturn flowOf(hotspotNetworkEntry) } val repository = WifiSummaryRepository( context = context, wifiStatusRepository = mockWifiStatusRepository, wifiPickerRepository = mockWifiPickerRepository, ) val summary = repository.summaryFlow().firstWithTimeoutOrNull() assertThat(summary).isEqualTo(ALTERNATE_SUMMARY) } private companion object { const val TEST_SSID = "Test Ssid" const val STATUS_LABEL = "Very Fast" const val ALTERNATE_SUMMARY = "Alternate Summary" } }
tests/spa_unit/src/com/android/settings/wifi/repository/WifiPickerRepositoryTest.kt 0 → 100644 +78 −0 Original line number Diff line number Diff line /* * Copyright (C) 2024 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.settings.wifi.repository import android.content.Context import androidx.test.core.app.ApplicationProvider import androidx.test.ext.junit.runners.AndroidJUnit4 import com.android.settingslib.spa.testutils.firstWithTimeoutOrNull import com.android.settingslib.spa.testutils.toListWithTimeout import com.android.wifitrackerlib.WifiEntry import com.android.wifitrackerlib.WifiPickerTracker import com.google.common.truth.Truth.assertThat import kotlinx.coroutines.async import kotlinx.coroutines.delay import kotlinx.coroutines.runBlocking import org.junit.Test import org.junit.runner.RunWith import org.mockito.kotlin.doReturn import org.mockito.kotlin.mock import org.mockito.kotlin.stub import org.mockito.kotlin.verify @RunWith(AndroidJUnit4::class) class WifiPickerRepositoryTest { private val context: Context = ApplicationProvider.getApplicationContext() private val mockWifiPickerTracker = mock<WifiPickerTracker>() private var callback: WifiPickerTracker.WifiPickerTrackerCallback? = null private val repository = WifiPickerRepository(context) { _, callback -> this.callback = callback mockWifiPickerTracker } @Test fun connectedWifiEntryFlow_callOnStartOnStopAndOnDestroy() = runBlocking { repository.connectedWifiEntryFlow().firstWithTimeoutOrNull() verify(mockWifiPickerTracker).onStart() verify(mockWifiPickerTracker).onStop() verify(mockWifiPickerTracker).onDestroy() } @Test fun connectedWifiEntryFlow_initial() = runBlocking { val wifiEntry = repository.connectedWifiEntryFlow().firstWithTimeoutOrNull() assertThat(wifiEntry).isNull() } @Test fun connectedWifiEntryFlow_onWifiEntriesChanged() = runBlocking { val listDeferred = async { repository.connectedWifiEntryFlow().toListWithTimeout() } delay(100) mockWifiPickerTracker.stub { on { connectedWifiEntry } doReturn mock<WifiEntry>() } callback?.onWifiEntriesChanged() assertThat(listDeferred.await().filterNotNull()).isNotEmpty() } }