1 /*
<lambda>null2  * Copyright (C) 2024 The Android Open Source Project
3  *
4  * Licensed under the Apache License, Version 2.0 (the "License");
5  * you may not use this file except in compliance with the License.
6  * You may obtain a copy of the License at
7  *
8  *      http://www.apache.org/licenses/LICENSE-2.0
9  *
10  * Unless required by applicable law or agreed to in writing, software
11  * distributed under the License is distributed on an "AS IS" BASIS,
12  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13  * See the License for the specific language governing permissions and
14  * limitations under the License.
15  */
16 
17 package com.android.settings.network
18 
19 import android.content.Context
20 import android.net.NetworkCapabilities
21 import android.net.wifi.WifiInfo
22 import android.net.wifi.WifiManager
23 import android.provider.Settings
24 import android.util.Log
25 import androidx.annotation.DrawableRes
26 import com.android.settings.R
27 import com.android.settings.network.telephony.DataSubscriptionRepository
28 import com.android.settings.wifi.WifiSummaryRepository
29 import com.android.settings.wifi.repository.WifiRepository
30 import com.android.settingslib.spaprivileged.settingsprovider.settingsGlobalBooleanFlow
31 import kotlinx.coroutines.Dispatchers
32 import kotlinx.coroutines.ExperimentalCoroutinesApi
33 import kotlinx.coroutines.flow.Flow
34 import kotlinx.coroutines.flow.combine
35 import kotlinx.coroutines.flow.conflate
36 import kotlinx.coroutines.flow.flatMapLatest
37 import kotlinx.coroutines.flow.flowOf
38 import kotlinx.coroutines.flow.flowOn
39 import kotlinx.coroutines.flow.map
40 import kotlinx.coroutines.flow.onEach
41 
42 @OptIn(ExperimentalCoroutinesApi::class)
43 class InternetPreferenceRepository(
44     private val context: Context,
45     private val connectivityRepository: ConnectivityRepository = ConnectivityRepository(context),
46     private val wifiSummaryRepository: WifiSummaryRepository = WifiSummaryRepository(context),
47     private val dataSubscriptionRepository: DataSubscriptionRepository =
48         DataSubscriptionRepository(context),
49     private val wifiRepository: WifiRepository = WifiRepository(context),
50     private val airplaneModeOnFlow: Flow<Boolean> =
51         context.settingsGlobalBooleanFlow(Settings.Global.AIRPLANE_MODE_ON),
52 ) {
53 
54     data class DisplayInfo(
55         val summary: String,
56         @DrawableRes val iconResId: Int,
57     )
58 
59     fun displayInfoFlow(): Flow<DisplayInfo> =
60         connectivityRepository
61             .networkCapabilitiesFlow()
62             .flatMapLatest { capabilities -> capabilities.displayInfoFlow() }
63             .onEach { Log.d(TAG, "displayInfoFlow: $it") }
64             .conflate()
65             .flowOn(Dispatchers.Default)
66 
67     private fun NetworkCapabilities.displayInfoFlow(): Flow<DisplayInfo> {
68         if (
69             hasCapability(NetworkCapabilities.NET_CAPABILITY_INTERNET) &&
70                 hasCapability(NetworkCapabilities.NET_CAPABILITY_VALIDATED)
71         ) {
72             val transportInfo = transportInfo
73             if (transportInfo is WifiInfo && transportInfo.isCarrierMerged) {
74                 Log.i(TAG, "Detect a merged carrier Wi-Fi connected.")
75                 return cellularDisplayInfoFlow()
76             }
77             for (transportType in transportTypes) {
78                 when (transportType) {
79                     NetworkCapabilities.TRANSPORT_WIFI -> return wifiDisplayInfoFlow()
80                     NetworkCapabilities.TRANSPORT_CELLULAR -> return cellularDisplayInfoFlow()
81                     NetworkCapabilities.TRANSPORT_ETHERNET -> return ethernetDisplayInfoFlow()
82                 }
83             }
84         }
85         return defaultDisplayInfoFlow()
86     }
87 
88     private fun wifiDisplayInfoFlow() =
89         wifiSummaryRepository.summaryFlow().map { summary ->
90             DisplayInfo(
91                 summary = summary,
92                 iconResId = R.drawable.ic_wifi_signal_4,
93             )
94         }
95 
96     private fun cellularDisplayInfoFlow() =
97         dataSubscriptionRepository.dataSummaryFlow().map { summary ->
98             DisplayInfo(
99                 summary = summary,
100                 iconResId = R.drawable.ic_network_cell,
101             )
102         }
103 
104     private fun ethernetDisplayInfoFlow() =
105         flowOf(
106             DisplayInfo(
107                 summary = context.getString(R.string.to_switch_networks_disconnect_ethernet),
108                 iconResId = R.drawable.ic_settings_ethernet,
109             )
110         )
111 
112     private fun defaultDisplayInfoFlow(): Flow<DisplayInfo> =
113         combine(
114             airplaneModeOnFlow,
115             wifiRepository.wifiStateFlow(),
116         ) { airplaneModeOn: Boolean, wifiState: Int ->
117             if (airplaneModeOn && wifiState != WifiManager.WIFI_STATE_ENABLED) {
118                 DisplayInfo(
119                     summary = context.getString(R.string.condition_airplane_title),
120                     iconResId = R.drawable.ic_no_internet_unavailable,
121                 )
122             } else {
123                 DisplayInfo(
124                     summary = context.getString(R.string.networks_available),
125                     iconResId = R.drawable.ic_no_internet_available,
126                 )
127             }
128         }
129 
130     private companion object {
131         private const val TAG = "InternetPreferenceRepo"
132     }
133 }
134