Loading src/com/android/settings/network/InternetPreferenceRepository.kt +30 −21 Original line number Diff line number Diff line Loading @@ -22,6 +22,7 @@ import android.net.wifi.WifiManager import android.provider.Settings import android.util.Log import com.android.settings.R import com.android.settings.network.telephony.DataSubscriptionRepository import com.android.settings.wifi.WifiSummaryRepository import com.android.settings.wifi.repository.WifiRepository import com.android.settingslib.spaprivileged.settingsprovider.settingsGlobalBooleanFlow Loading @@ -39,31 +40,39 @@ class InternetPreferenceRepository( private val context: Context, private val connectivityRepository: ConnectivityRepository = ConnectivityRepository(context), private val wifiSummaryRepository: WifiSummaryRepository = WifiSummaryRepository(context), private val dataSubscriptionRepository: DataSubscriptionRepository = DataSubscriptionRepository(context), private val wifiRepository: WifiRepository = WifiRepository(context), private val airplaneModeOnFlow: Flow<Boolean> = context.settingsGlobalBooleanFlow(Settings.Global.AIRPLANE_MODE_ON), ) { fun summaryFlow(): Flow<String> = connectivityRepository.networkCapabilitiesFlow() fun summaryFlow(): Flow<String> = connectivityRepository .networkCapabilitiesFlow() .flatMapLatest { capabilities -> capabilities.summaryFlow() } .onEach { Log.d(TAG, "summaryFlow: $it") } .conflate() .flowOn(Dispatchers.Default) private fun NetworkCapabilities.summaryFlow(): Flow<String> { if (hasCapability(NetworkCapabilities.NET_CAPABILITY_INTERNET) && if ( hasCapability(NetworkCapabilities.NET_CAPABILITY_INTERNET) && hasCapability(NetworkCapabilities.NET_CAPABILITY_VALIDATED) ) { for (transportType in transportTypes) { if (transportType == NetworkCapabilities.TRANSPORT_WIFI) { return wifiSummaryRepository.summaryFlow() when (transportType) { NetworkCapabilities.TRANSPORT_WIFI -> return wifiSummaryRepository.summaryFlow() NetworkCapabilities.TRANSPORT_CELLULAR -> return dataSubscriptionRepository.dataSummaryFlow() } } } return defaultSummaryFlow() } private fun defaultSummaryFlow(): Flow<String> = combine( private fun defaultSummaryFlow(): Flow<String> = combine( airplaneModeOnFlow, wifiRepository.wifiStateFlow(), ) { airplaneModeOn: Boolean, wifiState: Int -> Loading src/com/android/settings/network/SubscriptionUtil.java +0 −1 Original line number Diff line number Diff line Loading @@ -408,7 +408,6 @@ public class SubscriptionUtil { * * @return map of active subscription ids to display names. */ @VisibleForTesting public static CharSequence getUniqueSubscriptionDisplayName( Integer subscriptionId, Context context) { final Map<Integer, CharSequence> displayNames = getUniqueSubscriptionDisplayNames(context); Loading src/com/android/settings/network/telephony/DataSubscriptionRepository.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.network.telephony import android.content.Context import android.content.IntentFilter import android.telephony.SubscriptionManager import android.telephony.TelephonyCallback import android.telephony.TelephonyManager import android.util.Log import androidx.annotation.VisibleForTesting import com.android.settings.R import com.android.settings.network.SubscriptionUtil import com.android.settingslib.spaprivileged.framework.common.broadcastReceiverFlow import kotlinx.coroutines.Dispatchers import kotlinx.coroutines.flow.Flow import kotlinx.coroutines.flow.combine import kotlinx.coroutines.flow.conflate import kotlinx.coroutines.flow.distinctUntilChanged import kotlinx.coroutines.flow.flowOn import kotlinx.coroutines.flow.map import kotlinx.coroutines.flow.onStart class DataSubscriptionRepository( private val context: Context, private val getDisplayName: (subId: Int) -> String = { subId -> SubscriptionUtil.getUniqueSubscriptionDisplayName(subId, context).toString() }, ) { private val telephonyManager = context.getSystemService(TelephonyManager::class.java)!! private val subscriptionManager = context.requireSubscriptionManager() fun defaultDataSubscriptionIdFlow(): Flow<Int> = context .broadcastReceiverFlow( IntentFilter(TelephonyManager.ACTION_DEFAULT_DATA_SUBSCRIPTION_CHANGED) ) .map { it.getIntExtra(SUBSCRIPTION_KEY, SubscriptionManager.INVALID_SUBSCRIPTION_ID) } .onStart { emit(SubscriptionManager.getDefaultDataSubscriptionId()) } .conflate() .flowOn(Dispatchers.Default) fun activeDataSubscriptionIdFlow(): Flow<Int> = telephonyManager.telephonyCallbackFlow { object : TelephonyCallback(), TelephonyCallback.ActiveDataSubscriptionIdListener { override fun onActiveDataSubscriptionIdChanged(subId: Int) { trySend(subId) Log.d(TAG, "activeDataSubscriptionIdFlow: $subId") } } } fun dataSummaryFlow(): Flow<String> = combine(defaultDataSubscriptionIdFlow(), activeDataSubscriptionIdFlow()) { defaultSubId, activeSubId -> DataSubscriptionIds(defaultSubId, activeSubId) } .distinctUntilChanged() .map { it.getDataSummary() } .conflate() .flowOn(Dispatchers.Default) private data class DataSubscriptionIds( val defaultSubId: Int, val activeSubId: Int, ) private fun DataSubscriptionIds.getDataSummary(): String { val activeSubInfo = subscriptionManager.getActiveSubscriptionInfo(activeSubId) ?: return "" if (!SubscriptionUtil.isSubscriptionVisible(subscriptionManager, context, activeSubInfo)) { return getDisplayName(defaultSubId) } val uniqueName = getDisplayName(activeSubId) return if (activeSubId == defaultSubId) { uniqueName } else { context.getString(R.string.mobile_data_temp_using, uniqueName) } } companion object { private const val TAG = "DataSubscriptionRepo" @VisibleForTesting const val SUBSCRIPTION_KEY = "subscription" } } src/com/android/settings/network/telephony/TelephonyRepository.kt +7 −4 Original line number Diff line number Diff line Loading @@ -114,14 +114,17 @@ class TelephonyRepository( fun <T> Context.telephonyCallbackFlow( subId: Int, block: ProducerScope<T>.() -> TelephonyCallback, ): Flow<T> = callbackFlow { val telephonyManager = telephonyManager(subId) ): Flow<T> = telephonyManager(subId).telephonyCallbackFlow(block) /** Creates an instance of a cold Flow for Telephony callback. */ fun <T> TelephonyManager.telephonyCallbackFlow( block: ProducerScope<T>.() -> TelephonyCallback, ): Flow<T> = callbackFlow { val callback = block() telephonyManager.registerTelephonyCallback(Dispatchers.Default.asExecutor(), callback) registerTelephonyCallback(Dispatchers.Default.asExecutor(), callback) awaitClose { telephonyManager.unregisterTelephonyCallback(callback) } awaitClose { unregisterTelephonyCallback(callback) } }.conflate().flowOn(Dispatchers.Default) fun Context.telephonyManager(subId: Int): TelephonyManager = Loading src/com/android/settings/spa/network/NetworkCellularGroupProvider.kt +2 −10 Original line number Diff line number Diff line Loading @@ -46,6 +46,7 @@ import androidx.lifecycle.compose.collectAsStateWithLifecycle import androidx.lifecycle.viewmodel.compose.viewModel import com.android.settings.R import com.android.settings.network.SubscriptionInfoListViewModel import com.android.settings.network.telephony.DataSubscriptionRepository import com.android.settings.network.telephony.TelephonyRepository import com.android.settings.spa.network.PrimarySimRepository.PrimarySimInfo import com.android.settings.wifi.WifiPickerTrackerHelper Loading Loading @@ -158,7 +159,7 @@ open class NetworkCellularGroupProvider : SettingsPageProvider { selectableSubscriptionInfoListFlow, context.defaultVoiceSubscriptionFlow(), context.defaultSmsSubscriptionFlow(), context.defaultDefaultDataSubscriptionFlow(), DataSubscriptionRepository(context).defaultDataSubscriptionIdFlow(), this::refreshUiStates, ).flowOn(Dispatchers.Default) Loading Loading @@ -370,15 +371,6 @@ private fun Context.defaultSmsSubscriptionFlow(): Flow<Int> = ).map { SubscriptionManager.getDefaultSmsSubscriptionId() } .conflate().flowOn(Dispatchers.Default) private fun Context.defaultDefaultDataSubscriptionFlow(): Flow<Int> = merge( flowOf(null), // kick an initial value broadcastReceiverFlow( IntentFilter(TelephonyManager.ACTION_DEFAULT_DATA_SUBSCRIPTION_CHANGED) ), ).map { SubscriptionManager.getDefaultDataSubscriptionId() } .conflate().flowOn(Dispatchers.Default) suspend fun setDefaultVoice( subscriptionManager: SubscriptionManager?, subId: Int Loading Loading
src/com/android/settings/network/InternetPreferenceRepository.kt +30 −21 Original line number Diff line number Diff line Loading @@ -22,6 +22,7 @@ import android.net.wifi.WifiManager import android.provider.Settings import android.util.Log import com.android.settings.R import com.android.settings.network.telephony.DataSubscriptionRepository import com.android.settings.wifi.WifiSummaryRepository import com.android.settings.wifi.repository.WifiRepository import com.android.settingslib.spaprivileged.settingsprovider.settingsGlobalBooleanFlow Loading @@ -39,31 +40,39 @@ class InternetPreferenceRepository( private val context: Context, private val connectivityRepository: ConnectivityRepository = ConnectivityRepository(context), private val wifiSummaryRepository: WifiSummaryRepository = WifiSummaryRepository(context), private val dataSubscriptionRepository: DataSubscriptionRepository = DataSubscriptionRepository(context), private val wifiRepository: WifiRepository = WifiRepository(context), private val airplaneModeOnFlow: Flow<Boolean> = context.settingsGlobalBooleanFlow(Settings.Global.AIRPLANE_MODE_ON), ) { fun summaryFlow(): Flow<String> = connectivityRepository.networkCapabilitiesFlow() fun summaryFlow(): Flow<String> = connectivityRepository .networkCapabilitiesFlow() .flatMapLatest { capabilities -> capabilities.summaryFlow() } .onEach { Log.d(TAG, "summaryFlow: $it") } .conflate() .flowOn(Dispatchers.Default) private fun NetworkCapabilities.summaryFlow(): Flow<String> { if (hasCapability(NetworkCapabilities.NET_CAPABILITY_INTERNET) && if ( hasCapability(NetworkCapabilities.NET_CAPABILITY_INTERNET) && hasCapability(NetworkCapabilities.NET_CAPABILITY_VALIDATED) ) { for (transportType in transportTypes) { if (transportType == NetworkCapabilities.TRANSPORT_WIFI) { return wifiSummaryRepository.summaryFlow() when (transportType) { NetworkCapabilities.TRANSPORT_WIFI -> return wifiSummaryRepository.summaryFlow() NetworkCapabilities.TRANSPORT_CELLULAR -> return dataSubscriptionRepository.dataSummaryFlow() } } } return defaultSummaryFlow() } private fun defaultSummaryFlow(): Flow<String> = combine( private fun defaultSummaryFlow(): Flow<String> = combine( airplaneModeOnFlow, wifiRepository.wifiStateFlow(), ) { airplaneModeOn: Boolean, wifiState: Int -> Loading
src/com/android/settings/network/SubscriptionUtil.java +0 −1 Original line number Diff line number Diff line Loading @@ -408,7 +408,6 @@ public class SubscriptionUtil { * * @return map of active subscription ids to display names. */ @VisibleForTesting public static CharSequence getUniqueSubscriptionDisplayName( Integer subscriptionId, Context context) { final Map<Integer, CharSequence> displayNames = getUniqueSubscriptionDisplayNames(context); Loading
src/com/android/settings/network/telephony/DataSubscriptionRepository.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.network.telephony import android.content.Context import android.content.IntentFilter import android.telephony.SubscriptionManager import android.telephony.TelephonyCallback import android.telephony.TelephonyManager import android.util.Log import androidx.annotation.VisibleForTesting import com.android.settings.R import com.android.settings.network.SubscriptionUtil import com.android.settingslib.spaprivileged.framework.common.broadcastReceiverFlow import kotlinx.coroutines.Dispatchers import kotlinx.coroutines.flow.Flow import kotlinx.coroutines.flow.combine import kotlinx.coroutines.flow.conflate import kotlinx.coroutines.flow.distinctUntilChanged import kotlinx.coroutines.flow.flowOn import kotlinx.coroutines.flow.map import kotlinx.coroutines.flow.onStart class DataSubscriptionRepository( private val context: Context, private val getDisplayName: (subId: Int) -> String = { subId -> SubscriptionUtil.getUniqueSubscriptionDisplayName(subId, context).toString() }, ) { private val telephonyManager = context.getSystemService(TelephonyManager::class.java)!! private val subscriptionManager = context.requireSubscriptionManager() fun defaultDataSubscriptionIdFlow(): Flow<Int> = context .broadcastReceiverFlow( IntentFilter(TelephonyManager.ACTION_DEFAULT_DATA_SUBSCRIPTION_CHANGED) ) .map { it.getIntExtra(SUBSCRIPTION_KEY, SubscriptionManager.INVALID_SUBSCRIPTION_ID) } .onStart { emit(SubscriptionManager.getDefaultDataSubscriptionId()) } .conflate() .flowOn(Dispatchers.Default) fun activeDataSubscriptionIdFlow(): Flow<Int> = telephonyManager.telephonyCallbackFlow { object : TelephonyCallback(), TelephonyCallback.ActiveDataSubscriptionIdListener { override fun onActiveDataSubscriptionIdChanged(subId: Int) { trySend(subId) Log.d(TAG, "activeDataSubscriptionIdFlow: $subId") } } } fun dataSummaryFlow(): Flow<String> = combine(defaultDataSubscriptionIdFlow(), activeDataSubscriptionIdFlow()) { defaultSubId, activeSubId -> DataSubscriptionIds(defaultSubId, activeSubId) } .distinctUntilChanged() .map { it.getDataSummary() } .conflate() .flowOn(Dispatchers.Default) private data class DataSubscriptionIds( val defaultSubId: Int, val activeSubId: Int, ) private fun DataSubscriptionIds.getDataSummary(): String { val activeSubInfo = subscriptionManager.getActiveSubscriptionInfo(activeSubId) ?: return "" if (!SubscriptionUtil.isSubscriptionVisible(subscriptionManager, context, activeSubInfo)) { return getDisplayName(defaultSubId) } val uniqueName = getDisplayName(activeSubId) return if (activeSubId == defaultSubId) { uniqueName } else { context.getString(R.string.mobile_data_temp_using, uniqueName) } } companion object { private const val TAG = "DataSubscriptionRepo" @VisibleForTesting const val SUBSCRIPTION_KEY = "subscription" } }
src/com/android/settings/network/telephony/TelephonyRepository.kt +7 −4 Original line number Diff line number Diff line Loading @@ -114,14 +114,17 @@ class TelephonyRepository( fun <T> Context.telephonyCallbackFlow( subId: Int, block: ProducerScope<T>.() -> TelephonyCallback, ): Flow<T> = callbackFlow { val telephonyManager = telephonyManager(subId) ): Flow<T> = telephonyManager(subId).telephonyCallbackFlow(block) /** Creates an instance of a cold Flow for Telephony callback. */ fun <T> TelephonyManager.telephonyCallbackFlow( block: ProducerScope<T>.() -> TelephonyCallback, ): Flow<T> = callbackFlow { val callback = block() telephonyManager.registerTelephonyCallback(Dispatchers.Default.asExecutor(), callback) registerTelephonyCallback(Dispatchers.Default.asExecutor(), callback) awaitClose { telephonyManager.unregisterTelephonyCallback(callback) } awaitClose { unregisterTelephonyCallback(callback) } }.conflate().flowOn(Dispatchers.Default) fun Context.telephonyManager(subId: Int): TelephonyManager = Loading
src/com/android/settings/spa/network/NetworkCellularGroupProvider.kt +2 −10 Original line number Diff line number Diff line Loading @@ -46,6 +46,7 @@ import androidx.lifecycle.compose.collectAsStateWithLifecycle import androidx.lifecycle.viewmodel.compose.viewModel import com.android.settings.R import com.android.settings.network.SubscriptionInfoListViewModel import com.android.settings.network.telephony.DataSubscriptionRepository import com.android.settings.network.telephony.TelephonyRepository import com.android.settings.spa.network.PrimarySimRepository.PrimarySimInfo import com.android.settings.wifi.WifiPickerTrackerHelper Loading Loading @@ -158,7 +159,7 @@ open class NetworkCellularGroupProvider : SettingsPageProvider { selectableSubscriptionInfoListFlow, context.defaultVoiceSubscriptionFlow(), context.defaultSmsSubscriptionFlow(), context.defaultDefaultDataSubscriptionFlow(), DataSubscriptionRepository(context).defaultDataSubscriptionIdFlow(), this::refreshUiStates, ).flowOn(Dispatchers.Default) Loading Loading @@ -370,15 +371,6 @@ private fun Context.defaultSmsSubscriptionFlow(): Flow<Int> = ).map { SubscriptionManager.getDefaultSmsSubscriptionId() } .conflate().flowOn(Dispatchers.Default) private fun Context.defaultDefaultDataSubscriptionFlow(): Flow<Int> = merge( flowOf(null), // kick an initial value broadcastReceiverFlow( IntentFilter(TelephonyManager.ACTION_DEFAULT_DATA_SUBSCRIPTION_CHANGED) ), ).map { SubscriptionManager.getDefaultDataSubscriptionId() } .conflate().flowOn(Dispatchers.Default) suspend fun setDefaultVoice( subscriptionManager: SubscriptionManager?, subId: Int Loading