From 0d758dfdb9f3f2a9c2f49a7723b9224e44471a51 Mon Sep 17 00:00:00 2001 From: Chaohui Wang Date: Wed, 12 Jun 2024 17:48:38 +0800 Subject: [PATCH 1/3] InternetPreferenceController V2 (4/n) Display different icon for different condition. Bug: 339884322 Flag: com.android.settings.flags.internet_preference_controller_v2 Test: manual - on Internet Test: unit test Change-Id: Ic06b0e349a284f8b4466bd0c19f318a6a0936a6e --- .../network/InternetPreferenceControllerV2.kt | 13 +++- .../network/InternetPreferenceRepository.kt | 58 +++++++++++++----- .../InternetPreferenceRepositoryTest.kt | 60 ++++++++++++++----- 3 files changed, 97 insertions(+), 34 deletions(-) diff --git a/src/com/android/settings/network/InternetPreferenceControllerV2.kt b/src/com/android/settings/network/InternetPreferenceControllerV2.kt index 351aca83526..a181abd2ef0 100644 --- a/src/com/android/settings/network/InternetPreferenceControllerV2.kt +++ b/src/com/android/settings/network/InternetPreferenceControllerV2.kt @@ -22,11 +22,13 @@ import androidx.preference.Preference import androidx.preference.PreferenceScreen import com.android.settings.R import com.android.settings.core.BasePreferenceController +import com.android.settingslib.Utils import com.android.settingslib.spa.framework.util.collectLatestWithLifecycle class InternetPreferenceControllerV2(context: Context, preferenceKey: String) : BasePreferenceController(context, preferenceKey) { + private val repository = InternetPreferenceRepository(mContext) private var preference: Preference? = null override fun getAvailabilityStatus() = @@ -39,9 +41,14 @@ class InternetPreferenceControllerV2(context: Context, preferenceKey: String) : } override fun onViewCreated(viewLifecycleOwner: LifecycleOwner) { - InternetPreferenceRepository(mContext).summaryFlow() - .collectLatestWithLifecycle(viewLifecycleOwner) { - preference?.summary = it + repository.displayInfoFlow().collectLatestWithLifecycle(viewLifecycleOwner) { displayInfo -> + preference?.apply { + summary = displayInfo.summary + icon = + mContext.getDrawable(displayInfo.iconResId)?.apply { + setTintList(Utils.getColorAttr(mContext, android.R.attr.colorControlNormal)) + } } + } } } diff --git a/src/com/android/settings/network/InternetPreferenceRepository.kt b/src/com/android/settings/network/InternetPreferenceRepository.kt index 6aa8db2c308..30b2f6a638e 100644 --- a/src/com/android/settings/network/InternetPreferenceRepository.kt +++ b/src/com/android/settings/network/InternetPreferenceRepository.kt @@ -21,6 +21,7 @@ import android.net.NetworkCapabilities import android.net.wifi.WifiManager import android.provider.Settings import android.util.Log +import androidx.annotation.DrawableRes import com.android.settings.R import com.android.settings.network.telephony.DataSubscriptionRepository import com.android.settings.wifi.WifiSummaryRepository @@ -33,6 +34,7 @@ import kotlinx.coroutines.flow.combine import kotlinx.coroutines.flow.conflate import kotlinx.coroutines.flow.flatMapLatest import kotlinx.coroutines.flow.flowOn +import kotlinx.coroutines.flow.map import kotlinx.coroutines.flow.onEach @OptIn(ExperimentalCoroutinesApi::class) @@ -47,42 +49,66 @@ class InternetPreferenceRepository( context.settingsGlobalBooleanFlow(Settings.Global.AIRPLANE_MODE_ON), ) { - fun summaryFlow(): Flow = + data class DisplayInfo( + val summary: String, + @DrawableRes val iconResId: Int, + ) + + fun displayInfoFlow(): Flow = connectivityRepository .networkCapabilitiesFlow() - .flatMapLatest { capabilities -> capabilities.summaryFlow() } - .onEach { Log.d(TAG, "summaryFlow: $it") } + .flatMapLatest { capabilities -> capabilities.displayInfoFlow() } + .onEach { Log.d(TAG, "displayInfoFlow: $it") } .conflate() .flowOn(Dispatchers.Default) - private fun NetworkCapabilities.summaryFlow(): Flow { + private fun NetworkCapabilities.displayInfoFlow(): Flow { if ( hasCapability(NetworkCapabilities.NET_CAPABILITY_INTERNET) && hasCapability(NetworkCapabilities.NET_CAPABILITY_VALIDATED) ) { for (transportType in transportTypes) { when (transportType) { - NetworkCapabilities.TRANSPORT_WIFI -> return wifiSummaryRepository.summaryFlow() - NetworkCapabilities.TRANSPORT_CELLULAR -> - return dataSubscriptionRepository.dataSummaryFlow() + NetworkCapabilities.TRANSPORT_WIFI -> return wifiDisplayInfoFlow() + NetworkCapabilities.TRANSPORT_CELLULAR -> return cellularDisplayInfoFlow() } } } - return defaultSummaryFlow() + return defaultDisplayInfoFlow() } - private fun defaultSummaryFlow(): Flow = + private fun wifiDisplayInfoFlow() = + wifiSummaryRepository.summaryFlow().map { summary -> + DisplayInfo( + summary = summary, + iconResId = R.drawable.ic_wifi_signal_4, + ) + } + + private fun cellularDisplayInfoFlow() = + dataSubscriptionRepository.dataSummaryFlow().map { summary -> + DisplayInfo( + summary = summary, + iconResId = R.drawable.ic_network_cell, + ) + } + + private fun defaultDisplayInfoFlow(): Flow = combine( airplaneModeOnFlow, wifiRepository.wifiStateFlow(), ) { airplaneModeOn: Boolean, wifiState: Int -> - context.getString( - if (airplaneModeOn && wifiState != WifiManager.WIFI_STATE_ENABLED) { - R.string.condition_airplane_title - } else { - R.string.networks_available - } - ) + if (airplaneModeOn && wifiState != WifiManager.WIFI_STATE_ENABLED) { + DisplayInfo( + summary = context.getString(R.string.condition_airplane_title), + iconResId = R.drawable.ic_no_internet_unavailable, + ) + } else { + DisplayInfo( + summary = context.getString(R.string.networks_available), + iconResId = R.drawable.ic_no_internet_available, + ) + } } private companion object { diff --git a/tests/spa_unit/src/com/android/settings/network/InternetPreferenceRepositoryTest.kt b/tests/spa_unit/src/com/android/settings/network/InternetPreferenceRepositoryTest.kt index aafd77f1a4a..58caea75a75 100644 --- a/tests/spa_unit/src/com/android/settings/network/InternetPreferenceRepositoryTest.kt +++ b/tests/spa_unit/src/com/android/settings/network/InternetPreferenceRepositoryTest.kt @@ -58,7 +58,7 @@ class InternetPreferenceRepositoryTest { ) @Test - fun summaryFlow_wifi() = runBlocking { + fun displayInfoFlow_wifi() = runBlocking { val wifiNetworkCapabilities = NetworkCapabilities.Builder() .addTransportType(NetworkCapabilities.TRANSPORT_WIFI) @@ -70,13 +70,19 @@ class InternetPreferenceRepositoryTest { } mockWifiSummaryRepository.stub { on { summaryFlow() } doReturn flowOf(SUMMARY) } - val summary = repository.summaryFlow().firstWithTimeoutOrNull() + val displayInfo = repository.displayInfoFlow().firstWithTimeoutOrNull() - assertThat(summary).isEqualTo(SUMMARY) + assertThat(displayInfo) + .isEqualTo( + InternetPreferenceRepository.DisplayInfo( + summary = SUMMARY, + iconResId = R.drawable.ic_wifi_signal_4, + ) + ) } @Test - fun summaryFlow_cellular() = runBlocking { + fun displayInfoFlow_cellular() = runBlocking { val wifiNetworkCapabilities = NetworkCapabilities.Builder() .addTransportType(NetworkCapabilities.TRANSPORT_CELLULAR) @@ -88,13 +94,19 @@ class InternetPreferenceRepositoryTest { } mockDataSubscriptionRepository.stub { on { dataSummaryFlow() } doReturn flowOf(SUMMARY) } - val summary = repository.summaryFlow().firstWithTimeoutOrNull() + val displayInfo = repository.displayInfoFlow().firstWithTimeoutOrNull() - assertThat(summary).isEqualTo(SUMMARY) + assertThat(displayInfo) + .isEqualTo( + InternetPreferenceRepository.DisplayInfo( + summary = SUMMARY, + iconResId = R.drawable.ic_network_cell, + ) + ) } @Test - fun summaryFlow_airplaneModeOnAndWifiOn() = runBlocking { + fun displayInfoFlow_airplaneModeOnAndWifiOn() = runBlocking { mockConnectivityRepository.stub { on { networkCapabilitiesFlow() } doReturn flowOf(NetworkCapabilities()) } @@ -103,13 +115,19 @@ class InternetPreferenceRepositoryTest { on { wifiStateFlow() } doReturn flowOf(WifiManager.WIFI_STATE_ENABLED) } - val summary = repository.summaryFlow().firstWithTimeoutOrNull() + val displayInfo = repository.displayInfoFlow().firstWithTimeoutOrNull() - assertThat(summary).isEqualTo(context.getString(R.string.networks_available)) + assertThat(displayInfo) + .isEqualTo( + InternetPreferenceRepository.DisplayInfo( + summary = context.getString(R.string.networks_available), + iconResId = R.drawable.ic_no_internet_available, + ) + ) } @Test - fun summaryFlow_airplaneModeOnAndWifiOff() = runBlocking { + fun displayInfoFlow_airplaneModeOnAndWifiOff() = runBlocking { mockConnectivityRepository.stub { on { networkCapabilitiesFlow() } doReturn flowOf(NetworkCapabilities()) } @@ -118,13 +136,19 @@ class InternetPreferenceRepositoryTest { on { wifiStateFlow() } doReturn flowOf(WifiManager.WIFI_STATE_DISABLED) } - val summary = repository.summaryFlow().firstWithTimeoutOrNull() + val displayInfo = repository.displayInfoFlow().firstWithTimeoutOrNull() - assertThat(summary).isEqualTo(context.getString(R.string.condition_airplane_title)) + assertThat(displayInfo) + .isEqualTo( + InternetPreferenceRepository.DisplayInfo( + summary = context.getString(R.string.condition_airplane_title), + iconResId = R.drawable.ic_no_internet_unavailable, + ) + ) } @Test - fun summaryFlow_airplaneModeOff() = runBlocking { + fun displayInfoFlow_airplaneModeOff() = runBlocking { mockConnectivityRepository.stub { on { networkCapabilitiesFlow() } doReturn flowOf(NetworkCapabilities()) } @@ -133,9 +157,15 @@ class InternetPreferenceRepositoryTest { on { wifiStateFlow() } doReturn flowOf(WifiManager.WIFI_STATE_DISABLED) } - val summary = repository.summaryFlow().firstWithTimeoutOrNull() + val displayInfo = repository.displayInfoFlow().firstWithTimeoutOrNull() - assertThat(summary).isEqualTo(context.getString(R.string.networks_available)) + assertThat(displayInfo) + .isEqualTo( + InternetPreferenceRepository.DisplayInfo( + summary = context.getString(R.string.networks_available), + iconResId = R.drawable.ic_no_internet_available, + ) + ) } private companion object { From c5cc30f0c39b21357a83c96735060b64f6388ebd Mon Sep 17 00:00:00 2001 From: Chaohui Wang Date: Wed, 12 Jun 2024 18:04:18 +0800 Subject: [PATCH 2/3] InternetPreferenceController V2 (5/n) Support ethernet. Bug: 339884322 Flag: com.android.settings.flags.internet_preference_controller_v2 Test: manual - on Internet Test: unit test Change-Id: I73fc9334379daa979f736fc9ff31c3d576a3381b --- .../network/InternetPreferenceRepository.kt | 10 ++++++++ .../InternetPreferenceRepositoryTest.kt | 23 +++++++++++++++++++ 2 files changed, 33 insertions(+) diff --git a/src/com/android/settings/network/InternetPreferenceRepository.kt b/src/com/android/settings/network/InternetPreferenceRepository.kt index 30b2f6a638e..229213b331e 100644 --- a/src/com/android/settings/network/InternetPreferenceRepository.kt +++ b/src/com/android/settings/network/InternetPreferenceRepository.kt @@ -33,6 +33,7 @@ import kotlinx.coroutines.flow.Flow import kotlinx.coroutines.flow.combine import kotlinx.coroutines.flow.conflate import kotlinx.coroutines.flow.flatMapLatest +import kotlinx.coroutines.flow.flowOf import kotlinx.coroutines.flow.flowOn import kotlinx.coroutines.flow.map import kotlinx.coroutines.flow.onEach @@ -71,6 +72,7 @@ class InternetPreferenceRepository( when (transportType) { NetworkCapabilities.TRANSPORT_WIFI -> return wifiDisplayInfoFlow() NetworkCapabilities.TRANSPORT_CELLULAR -> return cellularDisplayInfoFlow() + NetworkCapabilities.TRANSPORT_ETHERNET -> return ethernetDisplayInfoFlow() } } } @@ -93,6 +95,14 @@ class InternetPreferenceRepository( ) } + private fun ethernetDisplayInfoFlow() = + flowOf( + DisplayInfo( + summary = context.getString(R.string.to_switch_networks_disconnect_ethernet), + iconResId = R.drawable.ic_settings_ethernet, + ) + ) + private fun defaultDisplayInfoFlow(): Flow = combine( airplaneModeOnFlow, diff --git a/tests/spa_unit/src/com/android/settings/network/InternetPreferenceRepositoryTest.kt b/tests/spa_unit/src/com/android/settings/network/InternetPreferenceRepositoryTest.kt index 58caea75a75..5288202cfa0 100644 --- a/tests/spa_unit/src/com/android/settings/network/InternetPreferenceRepositoryTest.kt +++ b/tests/spa_unit/src/com/android/settings/network/InternetPreferenceRepositoryTest.kt @@ -105,6 +105,29 @@ class InternetPreferenceRepositoryTest { ) } + @Test + fun displayInfoFlow_ethernet() = runBlocking { + val wifiNetworkCapabilities = + NetworkCapabilities.Builder() + .addTransportType(NetworkCapabilities.TRANSPORT_ETHERNET) + .addCapability(NetworkCapabilities.NET_CAPABILITY_INTERNET) + .addCapability(NetworkCapabilities.NET_CAPABILITY_VALIDATED) + .build() + mockConnectivityRepository.stub { + on { networkCapabilitiesFlow() } doReturn flowOf(wifiNetworkCapabilities) + } + + val displayInfo = repository.displayInfoFlow().firstWithTimeoutOrNull() + + assertThat(displayInfo) + .isEqualTo( + InternetPreferenceRepository.DisplayInfo( + summary = context.getString(R.string.to_switch_networks_disconnect_ethernet), + iconResId = R.drawable.ic_settings_ethernet, + ) + ) + } + @Test fun displayInfoFlow_airplaneModeOnAndWifiOn() = runBlocking { mockConnectivityRepository.stub { From 52d15b6c3404f87e3612f347c24746c62bb18b55 Mon Sep 17 00:00:00 2001 From: Chaohui Wang Date: Wed, 12 Jun 2024 18:28:04 +0800 Subject: [PATCH 3/3] InternetPreferenceController V2 (6/n) Treat carrier merged Wi-Fi as cellular. Bug: 339884322 Flag: com.android.settings.flags.internet_preference_controller_v2 Test: manual - on Internet Test: unit test Change-Id: Ie14c36f0f22c332319c097150b06cfeec97e946f --- .../network/InternetPreferenceRepository.kt | 6 ++++ .../InternetPreferenceRepositoryTest.kt | 32 +++++++++++++++++++ 2 files changed, 38 insertions(+) diff --git a/src/com/android/settings/network/InternetPreferenceRepository.kt b/src/com/android/settings/network/InternetPreferenceRepository.kt index 229213b331e..41a2fccea5a 100644 --- a/src/com/android/settings/network/InternetPreferenceRepository.kt +++ b/src/com/android/settings/network/InternetPreferenceRepository.kt @@ -18,6 +18,7 @@ package com.android.settings.network import android.content.Context import android.net.NetworkCapabilities +import android.net.wifi.WifiInfo import android.net.wifi.WifiManager import android.provider.Settings import android.util.Log @@ -68,6 +69,11 @@ class InternetPreferenceRepository( hasCapability(NetworkCapabilities.NET_CAPABILITY_INTERNET) && hasCapability(NetworkCapabilities.NET_CAPABILITY_VALIDATED) ) { + val transportInfo = transportInfo + if (transportInfo is WifiInfo && transportInfo.isCarrierMerged) { + Log.i(TAG, "Detect a merged carrier Wi-Fi connected.") + return cellularDisplayInfoFlow() + } for (transportType in transportTypes) { when (transportType) { NetworkCapabilities.TRANSPORT_WIFI -> return wifiDisplayInfoFlow() diff --git a/tests/spa_unit/src/com/android/settings/network/InternetPreferenceRepositoryTest.kt b/tests/spa_unit/src/com/android/settings/network/InternetPreferenceRepositoryTest.kt index 5288202cfa0..a2542b59a84 100644 --- a/tests/spa_unit/src/com/android/settings/network/InternetPreferenceRepositoryTest.kt +++ b/tests/spa_unit/src/com/android/settings/network/InternetPreferenceRepositoryTest.kt @@ -18,6 +18,7 @@ package com.android.settings.network import android.content.Context import android.net.NetworkCapabilities +import android.net.wifi.WifiInfo import android.net.wifi.WifiManager import androidx.test.core.app.ApplicationProvider import androidx.test.ext.junit.runners.AndroidJUnit4 @@ -32,6 +33,7 @@ import kotlinx.coroutines.flow.flowOf import kotlinx.coroutines.runBlocking import org.junit.Test import org.junit.runner.RunWith +import org.mockito.kotlin.any import org.mockito.kotlin.doReturn import org.mockito.kotlin.mock import org.mockito.kotlin.stub @@ -81,6 +83,36 @@ class InternetPreferenceRepositoryTest { ) } + @Test + fun displayInfoFlow_carrierMergedWifi_asCellular() = runBlocking { + val wifiInfo = + mock { + on { isCarrierMerged } doReturn true + on { makeCopy(any()) } doReturn mock + } + val wifiNetworkCapabilities = + NetworkCapabilities.Builder() + .addTransportType(NetworkCapabilities.TRANSPORT_WIFI) + .addCapability(NetworkCapabilities.NET_CAPABILITY_INTERNET) + .addCapability(NetworkCapabilities.NET_CAPABILITY_VALIDATED) + .setTransportInfo(wifiInfo) + .build() + mockConnectivityRepository.stub { + on { networkCapabilitiesFlow() } doReturn flowOf(wifiNetworkCapabilities) + } + mockDataSubscriptionRepository.stub { on { dataSummaryFlow() } doReturn flowOf(SUMMARY) } + + val displayInfo = repository.displayInfoFlow().firstWithTimeoutOrNull() + + assertThat(displayInfo) + .isEqualTo( + InternetPreferenceRepository.DisplayInfo( + summary = SUMMARY, + iconResId = R.drawable.ic_network_cell, + ) + ) + } + @Test fun displayInfoFlow_cellular() = runBlocking { val wifiNetworkCapabilities =