Loading src/com/android/settings/network/telephony/CarrierConfigRepository.kt +56 −7 Original line number Diff line number Diff line Loading @@ -17,6 +17,7 @@ package com.android.settings.network.telephony import android.content.Context import android.os.Build import android.os.PersistableBundle import android.telephony.CarrierConfigManager import android.telephony.SubscriptionManager Loading @@ -35,7 +36,8 @@ class CarrierConfigRepository(private val context: Context) { private enum class KeyType { BOOLEAN, INT, STRING INT_ARRAY, STRING, } interface CarrierConfigAccessor { Loading @@ -43,17 +45,20 @@ class CarrierConfigRepository(private val context: Context) { fun getInt(key: String): Int fun getIntArray(key: String): IntArray? fun getString(key: String): String? } private class Accessor(private val cache: ConfigCache) : CarrierConfigAccessor { private val keysToRetrieve = mutableMapOf<String, KeyType>() private var isKeysToRetrieveFrozen = false override fun getBoolean(key: String): Boolean { checkBooleanKey(key) val value = cache[key] return if (value == null) { keysToRetrieve += key to KeyType.BOOLEAN addKeyToRetrieve(key, KeyType.BOOLEAN) DefaultConfig.getBoolean(key) } else { check(value is BooleanConfigValue) { "Boolean value type wrong" } Loading @@ -65,7 +70,7 @@ class CarrierConfigRepository(private val context: Context) { check(key.endsWith("_int")) { "Int key should ends with _int" } val value = cache[key] return if (value == null) { keysToRetrieve += key to KeyType.INT addKeyToRetrieve(key, KeyType.INT) DefaultConfig.getInt(key) } else { check(value is IntConfigValue) { "Int value type wrong" } Loading @@ -73,11 +78,23 @@ class CarrierConfigRepository(private val context: Context) { } } override fun getIntArray(key: String): IntArray? { checkIntArrayKey(key) val value = cache[key] return if (value == null) { addKeyToRetrieve(key, KeyType.INT_ARRAY) DefaultConfig.getIntArray(key) } else { check(value is IntArrayConfigValue) { "Int array value type wrong" } value.value } } override fun getString(key: String): String? { check(key.endsWith("_string")) { "String key should ends with _string" } val value = cache[key] return if (value == null) { keysToRetrieve += key to KeyType.STRING addKeyToRetrieve(key, KeyType.STRING) DefaultConfig.getString(key) } else { check(value is StringConfigValue) { "String value type wrong" } Loading @@ -85,20 +102,35 @@ class CarrierConfigRepository(private val context: Context) { } } fun getKeysToRetrieve(): Map<String, KeyType> = keysToRetrieve private fun addKeyToRetrieve(key: String, type: KeyType) { if (keysToRetrieve.put(key, type) == null && Build.IS_DEBUGGABLE) { check(!isKeysToRetrieveFrozen) { "implement error for key $key" } } } /** * Gets the keys to retrieve. * * After this function is called, the keys to retrieve is frozen. */ fun getAndFrozeKeysToRetrieve(): Map<String, KeyType> { isKeysToRetrieveFrozen = true return keysToRetrieve } } /** * Gets the configuration values for the given [subId]. * * Configuration values could be accessed in [block]. Note: [block] could be called multiple * times, so it should be pure function without side effort. * times, so it should be pure function without side effort. Please also make sure every key is * retrieved every time, for example, we need avoid expression shortcut. */ fun <T> transformConfig(subId: Int, block: CarrierConfigAccessor.() -> T): T { val perSubCache = getPerSubCache(subId) val accessor = Accessor(perSubCache) val result = accessor.block() val keysToRetrieve = accessor.getKeysToRetrieve() val keysToRetrieve = accessor.getAndFrozeKeysToRetrieve() // If all keys found in the first pass, no need to collect again if (keysToRetrieve.isEmpty()) return result Loading @@ -113,6 +145,10 @@ class CarrierConfigRepository(private val context: Context) { /** Gets the configuration int for the given [subId] and [key]. */ fun getInt(subId: Int, key: String): Int = transformConfig(subId) { getInt(key) } /** Gets the configuration int array for the given [subId] and [key]. */ fun getIntArray(subId: Int, key: String): IntArray? = transformConfig(subId) { getIntArray(key) } /** Gets the configuration string for the given [subId] and [key]. */ fun getString(subId: Int, key: String): String? = transformConfig(subId) { getString(key) } Loading @@ -122,6 +158,7 @@ class CarrierConfigRepository(private val context: Context) { when (type) { KeyType.BOOLEAN -> this[key] = BooleanConfigValue(config.getBoolean(key)) KeyType.INT -> this[key] = IntConfigValue(config.getInt(key)) KeyType.INT_ARRAY -> this[key] = IntArrayConfigValue(config.getIntArray(key)) KeyType.STRING -> this[key] = StringConfigValue(config.getString(key)) } } Loading Loading @@ -195,6 +232,10 @@ class CarrierConfigRepository(private val context: Context) { } } private fun checkIntArrayKey(key: String) { check(key.endsWith("_int_array")) { "Int array key should ends with _int_array" } } @VisibleForTesting fun setBooleanForTest(subId: Int, key: String, value: Boolean) { checkBooleanKey(key) Loading @@ -207,6 +248,12 @@ class CarrierConfigRepository(private val context: Context) { getPerSubCache(subId)[key] = IntConfigValue(value) } @VisibleForTesting fun setIntArrayForTest(subId: Int, key: String, value: IntArray?) { checkIntArrayKey(key) getPerSubCache(subId)[key] = IntArrayConfigValue(value) } @VisibleForTesting fun setStringForTest(subId: Int, key: String, value: String?) { check(key.endsWith("_string")) { "String key should ends with _string" } Loading @@ -221,6 +268,8 @@ private data class BooleanConfigValue(val value: Boolean) : ConfigValue private data class IntConfigValue(val value: Int) : ConfigValue private class IntArrayConfigValue(val value: IntArray?) : ConfigValue private data class StringConfigValue(val value: String?) : ConfigValue private typealias ConfigCache = ConcurrentHashMap<String, ConfigValue> src/com/android/settings/network/telephony/Enhanced4gBasePreferenceController.java +1 −12 Original line number Diff line number Diff line Loading @@ -34,7 +34,6 @@ import androidx.preference.PreferenceScreen; import androidx.preference.TwoStatePreference; import com.android.internal.telephony.flags.Flags; import com.android.internal.telephony.util.ArrayUtils; import com.android.settings.R; import com.android.settings.network.ims.VolteQueryImsState; import com.android.settingslib.core.lifecycle.LifecycleObserver; Loading @@ -56,8 +55,6 @@ public class Enhanced4gBasePreferenceController extends TelephonyTogglePreferenc Preference mPreference; private PhoneCallStateTelephonyCallback mTelephonyCallback; private boolean mShow5gLimitedDialog; boolean mIsNrEnabledFromCarrierConfig; private boolean mHas5gCapability; private Integer mCallState; private final List<On4gLteUpdateListener> m4gLteListeners; Loading Loading @@ -94,9 +91,6 @@ public class Enhanced4gBasePreferenceController extends TelephonyTogglePreferenc mShow5gLimitedDialog = carrierConfig.getBoolean( CarrierConfigManager.KEY_VOLTE_5G_LIMITED_ALERT_DIALOG_BOOL); int[] nrAvailabilities = carrierConfig.getIntArray( CarrierConfigManager.KEY_CARRIER_NR_AVAILABILITIES_INT_ARRAY); mIsNrEnabledFromCarrierConfig = !ArrayUtils.isEmpty(nrAvailabilities); return this; } Loading Loading @@ -247,10 +241,6 @@ public class Enhanced4gBasePreferenceController extends TelephonyTogglePreferenc } mTelephonyManager.registerTelephonyCallback( mContext.getMainExecutor(), mTelephonyCallback); final long supportedRadioBitmask = mTelephonyManager.getSupportedRadioAccessFamily(); mHas5gCapability = (supportedRadioBitmask & TelephonyManager.NETWORK_TYPE_BITMASK_NR) > 0; } public void unregister() { Loading @@ -269,8 +259,7 @@ public class Enhanced4gBasePreferenceController extends TelephonyTogglePreferenc } private boolean isDialogNeeded() { Log.d(TAG, "Has5gCapability:" + mHas5gCapability); return mShow5gLimitedDialog && mHas5gCapability && mIsNrEnabledFromCarrierConfig; return mShow5gLimitedDialog && new NrRepository(mContext).isNrAvailable(mSubId); } private void show5gLimitedDialog(ImsMmTelManager imsMmTelManager) { Loading src/com/android/settings/network/telephony/NrAdvancedCallingPreferenceController.kt +25 −26 Original line number Diff line number Diff line Loading @@ -25,31 +25,28 @@ import androidx.compose.runtime.rememberCoroutineScope import androidx.compose.ui.res.stringResource import androidx.lifecycle.compose.collectAsStateWithLifecycle import com.android.settings.R import com.android.settings.network.telephony.MobileNetworkSettingsSearchIndex.MobileNetworkSettingsSearchResult import com.android.settings.network.telephony.MobileNetworkSettingsSearchIndex.MobileNetworkSettingsSearchItem import com.android.settings.network.telephony.MobileNetworkSettingsSearchIndex.MobileNetworkSettingsSearchResult import com.android.settings.spa.preference.ComposePreferenceController import com.android.settingslib.spa.widget.preference.SwitchPreference import com.android.settingslib.spa.widget.preference.SwitchPreferenceModel import kotlinx.coroutines.flow.flowOf import kotlinx.coroutines.launch /** * Preference controller for "Voice over NR". */ class NrAdvancedCallingPreferenceController @JvmOverloads constructor( /** Preference controller for "Voice over NR". */ class NrAdvancedCallingPreferenceController @JvmOverloads constructor( context: Context, key: String, private val voNrRepository: VoNrRepository = VoNrRepository(context), private val callStateRepository: CallStateRepository = CallStateRepository(context), ) : ComposePreferenceController(context, key) { private var subId: Int = SubscriptionManager.INVALID_SUBSCRIPTION_ID private var repository: VoNrRepository? = null private val searchItem = NrAdvancedCallingSearchItem(context) /** Initial this PreferenceController. */ @JvmOverloads fun init(subId: Int, repository: VoNrRepository = VoNrRepository(mContext, subId)) { fun init(subId: Int) { this.subId = subId this.repository = repository } override fun getAvailabilityStatus() = Loading @@ -58,30 +55,32 @@ class NrAdvancedCallingPreferenceController @JvmOverloads constructor( @Composable override fun Content() { val summary = stringResource(R.string.nr_advanced_calling_summary) val isInCall by remember { callStateRepository.isInCallFlow() } val isInCall by remember { callStateRepository.isInCallFlow() } .collectAsStateWithLifecycle(initialValue = false) val isVoNrEnabled by remember { voNrRepository.isVoNrEnabledFlow(subId) } .collectAsStateWithLifecycle(initialValue = false) val isEnabled by remember { repository?.isVoNrEnabledFlow() ?: flowOf(false) }.collectAsStateWithLifecycle(initialValue = false) val coroutineScope = rememberCoroutineScope() SwitchPreference(object : SwitchPreferenceModel { SwitchPreference( object : SwitchPreferenceModel { override val title = stringResource(R.string.nr_advanced_calling_title) override val summary = { summary } override val changeable = { !isInCall } override val checked = { isEnabled } override val checked = { isVoNrEnabled } override val onCheckedChange: (Boolean) -> Unit = { newChecked -> coroutineScope.launch { repository?.setVoNrEnabled(newChecked) coroutineScope.launch { voNrRepository.setVoNrEnabled(subId, newChecked) } } } }) ) } companion object { class NrAdvancedCallingSearchItem(private val context: Context) : MobileNetworkSettingsSearchItem { private val voNrRepository = VoNrRepository(context) fun isAvailable(subId: Int): Boolean = VoNrRepository(context, subId).isVoNrAvailable() fun isAvailable(subId: Int): Boolean = voNrRepository.isVoNrAvailable(subId) override fun getSearchResult(subId: Int): MobileNetworkSettingsSearchResult? { if (!isAvailable(subId)) return null Loading src/com/android/settings/network/telephony/NrRepository.kt 0 → 100644 +50 −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.telephony.CarrierConfigManager import android.telephony.SubscriptionManager import android.telephony.TelephonyManager import android.util.Log class NrRepository(private val context: Context) { private val carrierConfigRepository = CarrierConfigRepository(context) fun isNrAvailable(subId: Int): Boolean { if (!SubscriptionManager.isValidSubscriptionId(subId) || !has5gCapability(subId)) { return false } val carrierNrAvailabilities = carrierConfigRepository.getIntArray( subId = subId, key = CarrierConfigManager.KEY_CARRIER_NR_AVAILABILITIES_INT_ARRAY, ) return carrierNrAvailabilities?.isNotEmpty() ?: false } private fun has5gCapability(subId: Int): Boolean { val telephonyManager = context.telephonyManager(subId) return (telephonyManager.supportedRadioAccessFamily and TelephonyManager.NETWORK_TYPE_BITMASK_NR > 0) .also { Log.d(TAG, "[$subId] has5gCapability: $it") } } private companion object { private const val TAG = "NrRepository" } } src/com/android/settings/network/telephony/VoNrRepository.kt +32 −33 Original line number Diff line number Diff line Loading @@ -19,7 +19,6 @@ package com.android.settings.network.telephony import android.content.Context import android.telephony.CarrierConfigManager import android.telephony.SubscriptionManager import android.telephony.TelephonyManager import android.util.Log import kotlinx.coroutines.Dispatchers import kotlinx.coroutines.flow.Flow Loading @@ -29,40 +28,40 @@ import kotlinx.coroutines.flow.map import kotlinx.coroutines.flow.onEach import kotlinx.coroutines.withContext class VoNrRepository(private val context: Context, private val subId: Int) { private val telephonyManager = context.telephonyManager(subId) private val carrierConfigManager = context.getSystemService(CarrierConfigManager::class.java)!! class VoNrRepository( private val context: Context, private val nrRepository: NrRepository = NrRepository(context), ) { private val carrierConfigRepository = CarrierConfigRepository(context) fun isVoNrAvailable(): Boolean { if (!SubscriptionManager.isValidSubscriptionId(subId) || !has5gCapability()) return false val carrierConfig = carrierConfigManager.safeGetConfig( keys = listOf( CarrierConfigManager.KEY_VONR_ENABLED_BOOL, CarrierConfigManager.KEY_VONR_SETTING_VISIBILITY_BOOL, CarrierConfigManager.KEY_CARRIER_NR_AVAILABILITIES_INT_ARRAY, ), subId = subId, fun isVoNrAvailable(subId: Int): Boolean { if (!nrRepository.isNrAvailable(subId)) return false data class Config(val isVoNrEnabled: Boolean, val isVoNrSettingVisibility: Boolean) val carrierConfig = carrierConfigRepository.transformConfig(subId) { Config( isVoNrEnabled = getBoolean(CarrierConfigManager.KEY_VONR_ENABLED_BOOL), isVoNrSettingVisibility = getBoolean(CarrierConfigManager.KEY_VONR_SETTING_VISIBILITY_BOOL), ) return carrierConfig.getBoolean(CarrierConfigManager.KEY_VONR_ENABLED_BOOL) && carrierConfig.getBoolean(CarrierConfigManager.KEY_VONR_SETTING_VISIBILITY_BOOL) && (carrierConfig.getIntArray(CarrierConfigManager.KEY_CARRIER_NR_AVAILABILITIES_INT_ARRAY) ?.isNotEmpty() ?: false) } return carrierConfig.isVoNrEnabled && carrierConfig.isVoNrSettingVisibility } private fun has5gCapability() = ((telephonyManager.supportedRadioAccessFamily and TelephonyManager.NETWORK_TYPE_BITMASK_NR) > 0) .also { Log.d(TAG, "[$subId] has5gCapability: $it") } fun isVoNrEnabledFlow(): Flow<Boolean> = context.subscriptionsChangedFlow() fun isVoNrEnabledFlow(subId: Int): Flow<Boolean> { val telephonyManager = context.telephonyManager(subId) return context .subscriptionsChangedFlow() .map { telephonyManager.isVoNrEnabled } .conflate() .onEach { Log.d(TAG, "[$subId] isVoNrEnabled: $it") } .flowOn(Dispatchers.Default) } suspend fun setVoNrEnabled(enabled: Boolean) = withContext(Dispatchers.Default) { suspend fun setVoNrEnabled(subId: Int, enabled: Boolean) = withContext(Dispatchers.Default) { if (!SubscriptionManager.isValidSubscriptionId(subId)) return@withContext val result = telephonyManager.setVoNrEnabled(enabled) val result = context.telephonyManager(subId).setVoNrEnabled(enabled) Log.d(TAG, "[$subId] setVoNrEnabled: $enabled, result: $result") } Loading Loading
src/com/android/settings/network/telephony/CarrierConfigRepository.kt +56 −7 Original line number Diff line number Diff line Loading @@ -17,6 +17,7 @@ package com.android.settings.network.telephony import android.content.Context import android.os.Build import android.os.PersistableBundle import android.telephony.CarrierConfigManager import android.telephony.SubscriptionManager Loading @@ -35,7 +36,8 @@ class CarrierConfigRepository(private val context: Context) { private enum class KeyType { BOOLEAN, INT, STRING INT_ARRAY, STRING, } interface CarrierConfigAccessor { Loading @@ -43,17 +45,20 @@ class CarrierConfigRepository(private val context: Context) { fun getInt(key: String): Int fun getIntArray(key: String): IntArray? fun getString(key: String): String? } private class Accessor(private val cache: ConfigCache) : CarrierConfigAccessor { private val keysToRetrieve = mutableMapOf<String, KeyType>() private var isKeysToRetrieveFrozen = false override fun getBoolean(key: String): Boolean { checkBooleanKey(key) val value = cache[key] return if (value == null) { keysToRetrieve += key to KeyType.BOOLEAN addKeyToRetrieve(key, KeyType.BOOLEAN) DefaultConfig.getBoolean(key) } else { check(value is BooleanConfigValue) { "Boolean value type wrong" } Loading @@ -65,7 +70,7 @@ class CarrierConfigRepository(private val context: Context) { check(key.endsWith("_int")) { "Int key should ends with _int" } val value = cache[key] return if (value == null) { keysToRetrieve += key to KeyType.INT addKeyToRetrieve(key, KeyType.INT) DefaultConfig.getInt(key) } else { check(value is IntConfigValue) { "Int value type wrong" } Loading @@ -73,11 +78,23 @@ class CarrierConfigRepository(private val context: Context) { } } override fun getIntArray(key: String): IntArray? { checkIntArrayKey(key) val value = cache[key] return if (value == null) { addKeyToRetrieve(key, KeyType.INT_ARRAY) DefaultConfig.getIntArray(key) } else { check(value is IntArrayConfigValue) { "Int array value type wrong" } value.value } } override fun getString(key: String): String? { check(key.endsWith("_string")) { "String key should ends with _string" } val value = cache[key] return if (value == null) { keysToRetrieve += key to KeyType.STRING addKeyToRetrieve(key, KeyType.STRING) DefaultConfig.getString(key) } else { check(value is StringConfigValue) { "String value type wrong" } Loading @@ -85,20 +102,35 @@ class CarrierConfigRepository(private val context: Context) { } } fun getKeysToRetrieve(): Map<String, KeyType> = keysToRetrieve private fun addKeyToRetrieve(key: String, type: KeyType) { if (keysToRetrieve.put(key, type) == null && Build.IS_DEBUGGABLE) { check(!isKeysToRetrieveFrozen) { "implement error for key $key" } } } /** * Gets the keys to retrieve. * * After this function is called, the keys to retrieve is frozen. */ fun getAndFrozeKeysToRetrieve(): Map<String, KeyType> { isKeysToRetrieveFrozen = true return keysToRetrieve } } /** * Gets the configuration values for the given [subId]. * * Configuration values could be accessed in [block]. Note: [block] could be called multiple * times, so it should be pure function without side effort. * times, so it should be pure function without side effort. Please also make sure every key is * retrieved every time, for example, we need avoid expression shortcut. */ fun <T> transformConfig(subId: Int, block: CarrierConfigAccessor.() -> T): T { val perSubCache = getPerSubCache(subId) val accessor = Accessor(perSubCache) val result = accessor.block() val keysToRetrieve = accessor.getKeysToRetrieve() val keysToRetrieve = accessor.getAndFrozeKeysToRetrieve() // If all keys found in the first pass, no need to collect again if (keysToRetrieve.isEmpty()) return result Loading @@ -113,6 +145,10 @@ class CarrierConfigRepository(private val context: Context) { /** Gets the configuration int for the given [subId] and [key]. */ fun getInt(subId: Int, key: String): Int = transformConfig(subId) { getInt(key) } /** Gets the configuration int array for the given [subId] and [key]. */ fun getIntArray(subId: Int, key: String): IntArray? = transformConfig(subId) { getIntArray(key) } /** Gets the configuration string for the given [subId] and [key]. */ fun getString(subId: Int, key: String): String? = transformConfig(subId) { getString(key) } Loading @@ -122,6 +158,7 @@ class CarrierConfigRepository(private val context: Context) { when (type) { KeyType.BOOLEAN -> this[key] = BooleanConfigValue(config.getBoolean(key)) KeyType.INT -> this[key] = IntConfigValue(config.getInt(key)) KeyType.INT_ARRAY -> this[key] = IntArrayConfigValue(config.getIntArray(key)) KeyType.STRING -> this[key] = StringConfigValue(config.getString(key)) } } Loading Loading @@ -195,6 +232,10 @@ class CarrierConfigRepository(private val context: Context) { } } private fun checkIntArrayKey(key: String) { check(key.endsWith("_int_array")) { "Int array key should ends with _int_array" } } @VisibleForTesting fun setBooleanForTest(subId: Int, key: String, value: Boolean) { checkBooleanKey(key) Loading @@ -207,6 +248,12 @@ class CarrierConfigRepository(private val context: Context) { getPerSubCache(subId)[key] = IntConfigValue(value) } @VisibleForTesting fun setIntArrayForTest(subId: Int, key: String, value: IntArray?) { checkIntArrayKey(key) getPerSubCache(subId)[key] = IntArrayConfigValue(value) } @VisibleForTesting fun setStringForTest(subId: Int, key: String, value: String?) { check(key.endsWith("_string")) { "String key should ends with _string" } Loading @@ -221,6 +268,8 @@ private data class BooleanConfigValue(val value: Boolean) : ConfigValue private data class IntConfigValue(val value: Int) : ConfigValue private class IntArrayConfigValue(val value: IntArray?) : ConfigValue private data class StringConfigValue(val value: String?) : ConfigValue private typealias ConfigCache = ConcurrentHashMap<String, ConfigValue>
src/com/android/settings/network/telephony/Enhanced4gBasePreferenceController.java +1 −12 Original line number Diff line number Diff line Loading @@ -34,7 +34,6 @@ import androidx.preference.PreferenceScreen; import androidx.preference.TwoStatePreference; import com.android.internal.telephony.flags.Flags; import com.android.internal.telephony.util.ArrayUtils; import com.android.settings.R; import com.android.settings.network.ims.VolteQueryImsState; import com.android.settingslib.core.lifecycle.LifecycleObserver; Loading @@ -56,8 +55,6 @@ public class Enhanced4gBasePreferenceController extends TelephonyTogglePreferenc Preference mPreference; private PhoneCallStateTelephonyCallback mTelephonyCallback; private boolean mShow5gLimitedDialog; boolean mIsNrEnabledFromCarrierConfig; private boolean mHas5gCapability; private Integer mCallState; private final List<On4gLteUpdateListener> m4gLteListeners; Loading Loading @@ -94,9 +91,6 @@ public class Enhanced4gBasePreferenceController extends TelephonyTogglePreferenc mShow5gLimitedDialog = carrierConfig.getBoolean( CarrierConfigManager.KEY_VOLTE_5G_LIMITED_ALERT_DIALOG_BOOL); int[] nrAvailabilities = carrierConfig.getIntArray( CarrierConfigManager.KEY_CARRIER_NR_AVAILABILITIES_INT_ARRAY); mIsNrEnabledFromCarrierConfig = !ArrayUtils.isEmpty(nrAvailabilities); return this; } Loading Loading @@ -247,10 +241,6 @@ public class Enhanced4gBasePreferenceController extends TelephonyTogglePreferenc } mTelephonyManager.registerTelephonyCallback( mContext.getMainExecutor(), mTelephonyCallback); final long supportedRadioBitmask = mTelephonyManager.getSupportedRadioAccessFamily(); mHas5gCapability = (supportedRadioBitmask & TelephonyManager.NETWORK_TYPE_BITMASK_NR) > 0; } public void unregister() { Loading @@ -269,8 +259,7 @@ public class Enhanced4gBasePreferenceController extends TelephonyTogglePreferenc } private boolean isDialogNeeded() { Log.d(TAG, "Has5gCapability:" + mHas5gCapability); return mShow5gLimitedDialog && mHas5gCapability && mIsNrEnabledFromCarrierConfig; return mShow5gLimitedDialog && new NrRepository(mContext).isNrAvailable(mSubId); } private void show5gLimitedDialog(ImsMmTelManager imsMmTelManager) { Loading
src/com/android/settings/network/telephony/NrAdvancedCallingPreferenceController.kt +25 −26 Original line number Diff line number Diff line Loading @@ -25,31 +25,28 @@ import androidx.compose.runtime.rememberCoroutineScope import androidx.compose.ui.res.stringResource import androidx.lifecycle.compose.collectAsStateWithLifecycle import com.android.settings.R import com.android.settings.network.telephony.MobileNetworkSettingsSearchIndex.MobileNetworkSettingsSearchResult import com.android.settings.network.telephony.MobileNetworkSettingsSearchIndex.MobileNetworkSettingsSearchItem import com.android.settings.network.telephony.MobileNetworkSettingsSearchIndex.MobileNetworkSettingsSearchResult import com.android.settings.spa.preference.ComposePreferenceController import com.android.settingslib.spa.widget.preference.SwitchPreference import com.android.settingslib.spa.widget.preference.SwitchPreferenceModel import kotlinx.coroutines.flow.flowOf import kotlinx.coroutines.launch /** * Preference controller for "Voice over NR". */ class NrAdvancedCallingPreferenceController @JvmOverloads constructor( /** Preference controller for "Voice over NR". */ class NrAdvancedCallingPreferenceController @JvmOverloads constructor( context: Context, key: String, private val voNrRepository: VoNrRepository = VoNrRepository(context), private val callStateRepository: CallStateRepository = CallStateRepository(context), ) : ComposePreferenceController(context, key) { private var subId: Int = SubscriptionManager.INVALID_SUBSCRIPTION_ID private var repository: VoNrRepository? = null private val searchItem = NrAdvancedCallingSearchItem(context) /** Initial this PreferenceController. */ @JvmOverloads fun init(subId: Int, repository: VoNrRepository = VoNrRepository(mContext, subId)) { fun init(subId: Int) { this.subId = subId this.repository = repository } override fun getAvailabilityStatus() = Loading @@ -58,30 +55,32 @@ class NrAdvancedCallingPreferenceController @JvmOverloads constructor( @Composable override fun Content() { val summary = stringResource(R.string.nr_advanced_calling_summary) val isInCall by remember { callStateRepository.isInCallFlow() } val isInCall by remember { callStateRepository.isInCallFlow() } .collectAsStateWithLifecycle(initialValue = false) val isVoNrEnabled by remember { voNrRepository.isVoNrEnabledFlow(subId) } .collectAsStateWithLifecycle(initialValue = false) val isEnabled by remember { repository?.isVoNrEnabledFlow() ?: flowOf(false) }.collectAsStateWithLifecycle(initialValue = false) val coroutineScope = rememberCoroutineScope() SwitchPreference(object : SwitchPreferenceModel { SwitchPreference( object : SwitchPreferenceModel { override val title = stringResource(R.string.nr_advanced_calling_title) override val summary = { summary } override val changeable = { !isInCall } override val checked = { isEnabled } override val checked = { isVoNrEnabled } override val onCheckedChange: (Boolean) -> Unit = { newChecked -> coroutineScope.launch { repository?.setVoNrEnabled(newChecked) coroutineScope.launch { voNrRepository.setVoNrEnabled(subId, newChecked) } } } }) ) } companion object { class NrAdvancedCallingSearchItem(private val context: Context) : MobileNetworkSettingsSearchItem { private val voNrRepository = VoNrRepository(context) fun isAvailable(subId: Int): Boolean = VoNrRepository(context, subId).isVoNrAvailable() fun isAvailable(subId: Int): Boolean = voNrRepository.isVoNrAvailable(subId) override fun getSearchResult(subId: Int): MobileNetworkSettingsSearchResult? { if (!isAvailable(subId)) return null Loading
src/com/android/settings/network/telephony/NrRepository.kt 0 → 100644 +50 −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.telephony.CarrierConfigManager import android.telephony.SubscriptionManager import android.telephony.TelephonyManager import android.util.Log class NrRepository(private val context: Context) { private val carrierConfigRepository = CarrierConfigRepository(context) fun isNrAvailable(subId: Int): Boolean { if (!SubscriptionManager.isValidSubscriptionId(subId) || !has5gCapability(subId)) { return false } val carrierNrAvailabilities = carrierConfigRepository.getIntArray( subId = subId, key = CarrierConfigManager.KEY_CARRIER_NR_AVAILABILITIES_INT_ARRAY, ) return carrierNrAvailabilities?.isNotEmpty() ?: false } private fun has5gCapability(subId: Int): Boolean { val telephonyManager = context.telephonyManager(subId) return (telephonyManager.supportedRadioAccessFamily and TelephonyManager.NETWORK_TYPE_BITMASK_NR > 0) .also { Log.d(TAG, "[$subId] has5gCapability: $it") } } private companion object { private const val TAG = "NrRepository" } }
src/com/android/settings/network/telephony/VoNrRepository.kt +32 −33 Original line number Diff line number Diff line Loading @@ -19,7 +19,6 @@ package com.android.settings.network.telephony import android.content.Context import android.telephony.CarrierConfigManager import android.telephony.SubscriptionManager import android.telephony.TelephonyManager import android.util.Log import kotlinx.coroutines.Dispatchers import kotlinx.coroutines.flow.Flow Loading @@ -29,40 +28,40 @@ import kotlinx.coroutines.flow.map import kotlinx.coroutines.flow.onEach import kotlinx.coroutines.withContext class VoNrRepository(private val context: Context, private val subId: Int) { private val telephonyManager = context.telephonyManager(subId) private val carrierConfigManager = context.getSystemService(CarrierConfigManager::class.java)!! class VoNrRepository( private val context: Context, private val nrRepository: NrRepository = NrRepository(context), ) { private val carrierConfigRepository = CarrierConfigRepository(context) fun isVoNrAvailable(): Boolean { if (!SubscriptionManager.isValidSubscriptionId(subId) || !has5gCapability()) return false val carrierConfig = carrierConfigManager.safeGetConfig( keys = listOf( CarrierConfigManager.KEY_VONR_ENABLED_BOOL, CarrierConfigManager.KEY_VONR_SETTING_VISIBILITY_BOOL, CarrierConfigManager.KEY_CARRIER_NR_AVAILABILITIES_INT_ARRAY, ), subId = subId, fun isVoNrAvailable(subId: Int): Boolean { if (!nrRepository.isNrAvailable(subId)) return false data class Config(val isVoNrEnabled: Boolean, val isVoNrSettingVisibility: Boolean) val carrierConfig = carrierConfigRepository.transformConfig(subId) { Config( isVoNrEnabled = getBoolean(CarrierConfigManager.KEY_VONR_ENABLED_BOOL), isVoNrSettingVisibility = getBoolean(CarrierConfigManager.KEY_VONR_SETTING_VISIBILITY_BOOL), ) return carrierConfig.getBoolean(CarrierConfigManager.KEY_VONR_ENABLED_BOOL) && carrierConfig.getBoolean(CarrierConfigManager.KEY_VONR_SETTING_VISIBILITY_BOOL) && (carrierConfig.getIntArray(CarrierConfigManager.KEY_CARRIER_NR_AVAILABILITIES_INT_ARRAY) ?.isNotEmpty() ?: false) } return carrierConfig.isVoNrEnabled && carrierConfig.isVoNrSettingVisibility } private fun has5gCapability() = ((telephonyManager.supportedRadioAccessFamily and TelephonyManager.NETWORK_TYPE_BITMASK_NR) > 0) .also { Log.d(TAG, "[$subId] has5gCapability: $it") } fun isVoNrEnabledFlow(): Flow<Boolean> = context.subscriptionsChangedFlow() fun isVoNrEnabledFlow(subId: Int): Flow<Boolean> { val telephonyManager = context.telephonyManager(subId) return context .subscriptionsChangedFlow() .map { telephonyManager.isVoNrEnabled } .conflate() .onEach { Log.d(TAG, "[$subId] isVoNrEnabled: $it") } .flowOn(Dispatchers.Default) } suspend fun setVoNrEnabled(enabled: Boolean) = withContext(Dispatchers.Default) { suspend fun setVoNrEnabled(subId: Int, enabled: Boolean) = withContext(Dispatchers.Default) { if (!SubscriptionManager.isValidSubscriptionId(subId)) return@withContext val result = telephonyManager.setVoNrEnabled(enabled) val result = context.telephonyManager(subId).setVoNrEnabled(enabled) Log.d(TAG, "[$subId] setVoNrEnabled: $enabled, result: $result") } Loading