Donate to e Foundation | Murena handsets with /e/OS | Own a part of Murena! Learn more

Commit 8dd270c0 authored by Chaohui Wang's avatar Chaohui Wang Committed by Android (Google) Code Review
Browse files

Merge changes Ie14c36f0,I73fc9334,Ic06b0e34 into main

* changes:
  InternetPreferenceController V2 (6/n)
  InternetPreferenceController V2 (5/n)
  InternetPreferenceController V2 (4/n)
parents 8e103fc4 52d15b6c
Loading
Loading
Loading
Loading
+10 −3
Original line number Diff line number Diff line
@@ -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))
                    }
            }
        }
    }
}
+58 −16
Original line number Diff line number Diff line
@@ -18,9 +18,11 @@ 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
import androidx.annotation.DrawableRes
import com.android.settings.R
import com.android.settings.network.telephony.DataSubscriptionRepository
import com.android.settings.wifi.WifiSummaryRepository
@@ -32,7 +34,9 @@ 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

@OptIn(ExperimentalCoroutinesApi::class)
@@ -47,43 +51,81 @@ class InternetPreferenceRepository(
        context.settingsGlobalBooleanFlow(Settings.Global.AIRPLANE_MODE_ON),
) {

    fun summaryFlow(): Flow<String> =
    data class DisplayInfo(
        val summary: String,
        @DrawableRes val iconResId: Int,
    )

    fun displayInfoFlow(): Flow<DisplayInfo> =
        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<String> {
    private fun NetworkCapabilities.displayInfoFlow(): Flow<DisplayInfo> {
        if (
            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 wifiSummaryRepository.summaryFlow()
                    NetworkCapabilities.TRANSPORT_CELLULAR ->
                        return dataSubscriptionRepository.dataSummaryFlow()
                    NetworkCapabilities.TRANSPORT_WIFI -> return wifiDisplayInfoFlow()
                    NetworkCapabilities.TRANSPORT_CELLULAR -> return cellularDisplayInfoFlow()
                    NetworkCapabilities.TRANSPORT_ETHERNET -> return ethernetDisplayInfoFlow()
                }
            }
        }
        return defaultDisplayInfoFlow()
    }
        return defaultSummaryFlow()

    private fun wifiDisplayInfoFlow() =
        wifiSummaryRepository.summaryFlow().map { summary ->
            DisplayInfo(
                summary = summary,
                iconResId = R.drawable.ic_wifi_signal_4,
            )
        }

    private fun defaultSummaryFlow(): Flow<String> =
    private fun cellularDisplayInfoFlow() =
        dataSubscriptionRepository.dataSummaryFlow().map { summary ->
            DisplayInfo(
                summary = summary,
                iconResId = R.drawable.ic_network_cell,
            )
        }

    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<DisplayInfo> =
        combine(
            airplaneModeOnFlow,
            wifiRepository.wifiStateFlow(),
        ) { airplaneModeOn: Boolean, wifiState: Int ->
            context.getString(
            if (airplaneModeOn && wifiState != WifiManager.WIFI_STATE_ENABLED) {
                    R.string.condition_airplane_title
                DisplayInfo(
                    summary = context.getString(R.string.condition_airplane_title),
                    iconResId = R.drawable.ic_no_internet_unavailable,
                )
            } else {
                    R.string.networks_available
                }
                DisplayInfo(
                    summary = context.getString(R.string.networks_available),
                    iconResId = R.drawable.ic_no_internet_available,
                )
            }
        }

    private companion object {
        private const val TAG = "InternetPreferenceRepo"
+100 −15
Original line number Diff line number Diff line
@@ -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
@@ -58,7 +60,7 @@ class InternetPreferenceRepositoryTest {
        )

    @Test
    fun summaryFlow_wifi() = runBlocking {
    fun displayInfoFlow_wifi() = runBlocking {
        val wifiNetworkCapabilities =
            NetworkCapabilities.Builder()
                .addTransportType(NetworkCapabilities.TRANSPORT_WIFI)
@@ -70,13 +72,49 @@ 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 displayInfoFlow_carrierMergedWifi_asCellular() = runBlocking {
        val wifiInfo =
            mock<WifiInfo> {
                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 summaryFlow_cellular() = runBlocking {
    fun displayInfoFlow_cellular() = runBlocking {
        val wifiNetworkCapabilities =
            NetworkCapabilities.Builder()
                .addTransportType(NetworkCapabilities.TRANSPORT_CELLULAR)
@@ -88,13 +126,42 @@ 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_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 {
            on { networkCapabilitiesFlow() } doReturn flowOf(NetworkCapabilities())
        }
@@ -103,13 +170,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 +191,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 +212,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 {