Add the mobile data into new SIMs page
Bug: 329584989
Test: verify UI.
atest TelephonyRepositoryTest
Change-Id: Ia00ac287ffd0d15ba0c9350b731c3afc1a04b7a0
diff --git a/src/com/android/settings/network/SimOnboardingActivity.kt b/src/com/android/settings/network/SimOnboardingActivity.kt
index f97822a..80aa13c 100644
--- a/src/com/android/settings/network/SimOnboardingActivity.kt
+++ b/src/com/android/settings/network/SimOnboardingActivity.kt
@@ -45,15 +45,18 @@
import androidx.compose.runtime.rememberCoroutineScope
import androidx.compose.ui.Alignment
import androidx.compose.ui.Modifier
+import androidx.compose.ui.platform.LocalContext
import androidx.compose.ui.platform.LocalLifecycleOwner
import androidx.compose.ui.res.stringResource
import androidx.compose.ui.text.style.TextAlign
+import androidx.lifecycle.LifecycleRegistry
import com.android.settings.R
import com.android.settings.SidecarFragment
import com.android.settings.network.telephony.SubscriptionActionDialogActivity
import com.android.settings.network.telephony.ToggleSubscriptionDialogActivity
import com.android.settings.spa.SpaActivity.Companion.startSpaActivity
import com.android.settings.spa.network.SimOnboardingPageProvider.getRoute
+import com.android.settings.wifi.WifiPickerTrackerHelper
import com.android.settingslib.spa.SpaBaseDialogActivity
import com.android.settingslib.spa.framework.theme.SettingsDimension
import com.android.settingslib.spa.framework.util.collectLatestWithLifecycle
@@ -73,6 +76,8 @@
class SimOnboardingActivity : SpaBaseDialogActivity() {
lateinit var scope: CoroutineScope
+ lateinit var wifiPickerTrackerHelper: WifiPickerTrackerHelper
+ lateinit var context: Context
lateinit var showStartingDialog: MutableState<Boolean>
lateinit var showError: MutableState<ErrorType>
lateinit var showProgressDialog: MutableState<Boolean>
@@ -85,6 +90,7 @@
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
+
if (!this.userManager.isAdminUser) {
Log.e(TAG, "It is not the admin user. Unable to toggle subscription.")
finish()
@@ -151,7 +157,10 @@
CallbackType.CALLBACK_SETUP_PRIMARY_SIM -> {
scope.launch {
- onboardingService.startSetupPrimarySim(this@SimOnboardingActivity)
+ onboardingService.startSetupPrimarySim(
+ this@SimOnboardingActivity,
+ wifiPickerTrackerHelper
+ )
}
}
@@ -183,6 +192,12 @@
showDsdsProgressDialog = remember { mutableStateOf(false) }
showRestartDialog = remember { mutableStateOf(false) }
scope = rememberCoroutineScope()
+ context = LocalContext.current
+ val lifecycleOwner = LocalLifecycleOwner.current
+ wifiPickerTrackerHelper = WifiPickerTrackerHelper(
+ LifecycleRegistry(lifecycleOwner), context,
+ null /* WifiPickerTrackerCallback */
+ )
registerSidecarReceiverFlow()
diff --git a/src/com/android/settings/network/SimOnboardingService.kt b/src/com/android/settings/network/SimOnboardingService.kt
index b99f18d..b7df754 100644
--- a/src/com/android/settings/network/SimOnboardingService.kt
+++ b/src/com/android/settings/network/SimOnboardingService.kt
@@ -30,6 +30,7 @@
import com.android.settings.spa.network.setDefaultData
import com.android.settings.spa.network.setDefaultSms
import com.android.settings.spa.network.setDefaultVoice
+import com.android.settings.wifi.WifiPickerTrackerHelper
import com.android.settingslib.utils.ThreadUtils
import kotlinx.coroutines.Dispatchers
import kotlinx.coroutines.flow.MutableStateFlow
@@ -336,14 +337,17 @@
}
}
- suspend fun startSetupPrimarySim(context: Context) {
+ suspend fun startSetupPrimarySim(
+ context: Context,
+ wifiPickerTrackerHelper: WifiPickerTrackerHelper
+ ) {
withContext(Dispatchers.Default) {
setDefaultVoice(subscriptionManager, targetPrimarySimCalls)
setDefaultSms(subscriptionManager, targetPrimarySimTexts)
setDefaultData(
context,
subscriptionManager,
- null,
+ wifiPickerTrackerHelper,
targetPrimarySimMobileData
)
TelephonyRepository(context).setAutomaticData(
diff --git a/src/com/android/settings/network/telephony/TelephonyRepository.kt b/src/com/android/settings/network/telephony/TelephonyRepository.kt
index 18af621..cc9b53d 100644
--- a/src/com/android/settings/network/telephony/TelephonyRepository.kt
+++ b/src/com/android/settings/network/telephony/TelephonyRepository.kt
@@ -21,6 +21,8 @@
import android.telephony.TelephonyCallback
import android.telephony.TelephonyManager
import android.util.Log
+import com.android.settings.network.mobileDataEnabledFlow
+import com.android.settings.wifi.WifiPickerTrackerHelper
import kotlinx.coroutines.Dispatchers
import kotlinx.coroutines.asExecutor
import kotlinx.coroutines.channels.ProducerScope
@@ -62,6 +64,42 @@
telephonyManager.setMobileDataPolicyEnabled(policy, enabled)
}
+ fun isDataEnabled(
+ subId: Int,
+ ): Flow<Boolean> {
+ if (!SubscriptionManager.isValidSubscriptionId(subId)) return flowOf(false)
+
+ Log.d(TAG, "register mobileDataEnabledFlow: [$subId]")
+ return context.mobileDataEnabledFlow(subId)
+ .map {
+ Log.d(TAG, "mobileDataEnabledFlow: receive mobile data [$subId] start")
+ val telephonyManager = context.telephonyManager(subId)
+ telephonyManager.isDataEnabledForReason(TelephonyManager.DATA_ENABLED_REASON_USER)
+ .also { Log.d(TAG, "mobileDataEnabledFlow: [$subId] isDataEnabled(): $it") }
+ }
+ }
+
+ fun setMobileData(
+ subId: Int,
+ enabled: Boolean,
+ wifiPickerTrackerHelper: WifiPickerTrackerHelper? = null
+ ) {
+ if (!SubscriptionManager.isValidSubscriptionId(subId)) return
+
+ Log.d(TAG, "setMobileData: $enabled")
+ MobileNetworkUtils.setMobileDataEnabled(
+ context,
+ subId,
+ enabled /* enabled */,
+ true /* disableOtherSubscriptions */
+ )
+
+ if (wifiPickerTrackerHelper != null
+ && !wifiPickerTrackerHelper.isCarrierNetworkProvisionEnabled(subId)
+ ) {
+ wifiPickerTrackerHelper.setCarrierNetworkEnabled(enabled)
+ }
+ }
private companion object {
private const val TAG = "TelephonyRepository"
}
diff --git a/src/com/android/settings/spa/network/MobileDataSwitchingPreference.kt b/src/com/android/settings/spa/network/MobileDataSwitchingPreference.kt
new file mode 100644
index 0000000..8c382bd
--- /dev/null
+++ b/src/com/android/settings/spa/network/MobileDataSwitchingPreference.kt
@@ -0,0 +1,50 @@
+/*
+ * 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.spa.network
+
+import android.telephony.TelephonyManager
+import androidx.compose.runtime.Composable
+import androidx.compose.runtime.rememberCoroutineScope
+import androidx.compose.ui.res.stringResource
+import com.android.settings.R
+import com.android.settings.network.telephony.TelephonyRepository
+import com.android.settingslib.spa.widget.preference.SwitchPreference
+import com.android.settingslib.spa.widget.preference.SwitchPreferenceModel
+import kotlinx.coroutines.Dispatchers
+import kotlinx.coroutines.launch
+
+@Composable
+fun MobileDataSwitchingPreference(
+ isMobileDataEnabled: () -> Boolean?,
+ setMobileDataEnabled: (newEnabled: Boolean) -> Unit,
+) {
+ val mobileDataSummary = stringResource(id = R.string.primary_sim_automatic_data_msg)
+ val coroutineScope = rememberCoroutineScope()
+ SwitchPreference(
+ object : SwitchPreferenceModel {
+ override val title = stringResource(id = R.string.mobile_data_settings_title)
+ override val summary = { mobileDataSummary }
+ override val checked = { isMobileDataEnabled() }
+ override val onCheckedChange: (Boolean) -> Unit = { newEnabled ->
+ coroutineScope.launch(Dispatchers.Default) {
+ setMobileDataEnabled(newEnabled)
+ }
+ }
+ override val changeable:() -> Boolean = {true}
+ }
+ )
+}
diff --git a/src/com/android/settings/spa/network/NetworkCellularGroupProvider.kt b/src/com/android/settings/spa/network/NetworkCellularGroupProvider.kt
index 80be970..0ccedeb 100644
--- a/src/com/android/settings/spa/network/NetworkCellularGroupProvider.kt
+++ b/src/com/android/settings/spa/network/NetworkCellularGroupProvider.kt
@@ -38,11 +38,12 @@
import androidx.compose.ui.platform.LocalLifecycleOwner
import androidx.compose.ui.res.stringResource
import androidx.compose.ui.res.vectorResource
+import androidx.lifecycle.LifecycleOwner
+import androidx.lifecycle.LifecycleRegistry
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.MobileNetworkUtils
import com.android.settings.network.telephony.TelephonyRepository
import com.android.settings.spa.network.PrimarySimRepository.PrimarySimInfo
import com.android.settings.wifi.WifiPickerTrackerHelper
@@ -167,7 +168,7 @@
defaultVoiceSubId: MutableIntState,
defaultSmsSubId: MutableIntState,
defaultDataSubId: MutableIntState,
- nonDds: MutableIntState
+ nonDds: MutableIntState,
) {
val selectableSubscriptionInfoList by selectableSubscriptionInfoListFlow
.collectAsStateWithLifecycle(initialValue = emptyList())
@@ -175,22 +176,76 @@
val stringSims = stringResource(R.string.provider_network_settings_title)
RegularScaffold(title = stringSims) {
SimsSection(selectableSubscriptionInfoList)
+ MobileDataSectionImpl(defaultDataSubId,
+ nonDds,
+ )
+
PrimarySimSectionImpl(
selectableSubscriptionInfoListFlow,
defaultVoiceSubId,
defaultSmsSubId,
defaultDataSubId,
- nonDds
)
}
}
@Composable
+fun MobileDataSectionImpl(
+ mobileDataSelectedId: MutableIntState,
+ nonDds: MutableIntState,
+) {
+ val context = LocalContext.current
+ val localLifecycleOwner = LocalLifecycleOwner.current
+ val wifiPickerTrackerHelper = getWifiPickerTrackerHelper(context, localLifecycleOwner)
+
+ val subscriptionManager: SubscriptionManager? =
+ context.getSystemService(SubscriptionManager::class.java)
+
+ Category(title = stringResource(id = R.string.mobile_data_settings_title)) {
+ val isAutoDataEnabled by remember(nonDds.intValue) {
+ TelephonyRepository(context).isMobileDataPolicyEnabledFlow(
+ subId = nonDds.intValue,
+ policy = TelephonyManager.MOBILE_DATA_POLICY_AUTO_DATA_SWITCH
+ )
+ }.collectAsStateWithLifecycle(initialValue = null)
+
+ val mobileDataStateChanged by remember(mobileDataSelectedId.intValue) {
+ TelephonyRepository(context).isDataEnabled(mobileDataSelectedId.intValue)
+ }.collectAsStateWithLifecycle(initialValue = false)
+ val coroutineScope = rememberCoroutineScope()
+
+ MobileDataSwitchingPreference(
+ isMobileDataEnabled = { mobileDataStateChanged },
+ setMobileDataEnabled = { newEnabled ->
+ coroutineScope.launch {
+ setMobileData(
+ context,
+ subscriptionManager,
+ wifiPickerTrackerHelper,
+ mobileDataSelectedId.intValue,
+ newEnabled
+ )
+ }
+ },
+ )
+ if (nonDds.intValue != SubscriptionManager.INVALID_SUBSCRIPTION_ID) {
+ AutomaticDataSwitchingPreference(
+ isAutoDataEnabled = { isAutoDataEnabled },
+ setAutoDataEnabled = { newEnabled ->
+ TelephonyRepository(context).setAutomaticData(nonDds.intValue, newEnabled)
+ },
+ )
+ }
+ }
+}
+
+@Composable
fun PrimarySimImpl(
primarySimInfo: PrimarySimInfo,
callsSelectedId: MutableIntState,
textsSelectedId: MutableIntState,
mobileDataSelectedId: MutableIntState,
+ wifiPickerTrackerHelper: WifiPickerTrackerHelper? = null,
subscriptionManager: SubscriptionManager? =
LocalContext.current.getSystemService(SubscriptionManager::class.java),
coroutineScope: CoroutineScope = rememberCoroutineScope(),
@@ -208,20 +263,15 @@
}
},
actionSetMobileData: (Int) -> Unit = {
- mobileDataSelectedId.intValue = it
coroutineScope.launch {
- // TODO: to fix the WifiPickerTracker crash when create
- // the wifiPickerTrackerHelper
setDefaultData(
context,
subscriptionManager,
- null/*wifiPickerTrackerHelper*/,
+ wifiPickerTrackerHelper,
it
)
}
},
- isAutoDataEnabled: () -> Boolean?,
- setAutoDataEnabled: (newEnabled: Boolean) -> Unit,
) {
CreatePrimarySimListPreference(
stringResource(id = R.string.primary_sim_calls_title),
@@ -244,8 +294,6 @@
Icons.Outlined.DataUsage,
actionSetMobileData
)
-
- AutomaticDataSwitchingPreference(isAutoDataEnabled, setAutoDataEnabled)
}
@Composable
@@ -254,9 +302,11 @@
callsSelectedId: MutableIntState,
textsSelectedId: MutableIntState,
mobileDataSelectedId: MutableIntState,
- nonDds: MutableIntState,
) {
val context = LocalContext.current
+ val localLifecycleOwner = LocalLifecycleOwner.current
+ val wifiPickerTrackerHelper = getWifiPickerTrackerHelper(context, localLifecycleOwner)
+
val primarySimInfo = remember(subscriptionInfoListFlow) {
subscriptionInfoListFlow
.map { subscriptionInfoList ->
@@ -267,25 +317,25 @@
}.collectAsStateWithLifecycle(initialValue = null).value ?: return
Category(title = stringResource(id = R.string.primary_sim_title)) {
- val isAutoDataEnabled by remember(nonDds.intValue) {
- TelephonyRepository(context).isMobileDataPolicyEnabledFlow(
- subId = nonDds.intValue,
- policy = TelephonyManager.MOBILE_DATA_POLICY_AUTO_DATA_SWITCH
- )
- }.collectAsStateWithLifecycle(initialValue = null)
PrimarySimImpl(
primarySimInfo,
callsSelectedId,
textsSelectedId,
mobileDataSelectedId,
- isAutoDataEnabled = { isAutoDataEnabled },
- setAutoDataEnabled = { newEnabled ->
- TelephonyRepository(context).setAutomaticData(nonDds.intValue, newEnabled)
- },
+ wifiPickerTrackerHelper
)
}
}
+private fun getWifiPickerTrackerHelper(
+ context: Context,
+ lifecycleOwner: LifecycleOwner
+): WifiPickerTrackerHelper {
+ return WifiPickerTrackerHelper(
+ LifecycleRegistry(lifecycleOwner), context,
+ null /* WifiPickerTrackerCallback */
+ )
+}
private fun Context.defaultVoiceSubscriptionFlow(): Flow<Int> =
merge(
flowOf(null), // kick an initial value
@@ -335,18 +385,27 @@
wifiPickerTrackerHelper: WifiPickerTrackerHelper?,
subId: Int
): Unit =
+ setMobileData(
+ context,
+ subscriptionManager,
+ wifiPickerTrackerHelper,
+ subId,
+ true
+ )
+
+suspend fun setMobileData(
+ context: Context,
+ subscriptionManager: SubscriptionManager?,
+ wifiPickerTrackerHelper: WifiPickerTrackerHelper?,
+ subId: Int,
+ enabled: Boolean,
+): Unit =
withContext(Dispatchers.Default) {
- subscriptionManager?.setDefaultDataSubId(subId)
- Log.d(NetworkCellularGroupProvider.name, "setMobileDataEnabled: true")
- MobileNetworkUtils.setMobileDataEnabled(
- context,
- subId,
- true /* enabled */,
- true /* disableOtherSubscriptions */
- )
- if (wifiPickerTrackerHelper != null
- && !wifiPickerTrackerHelper.isCarrierNetworkProvisionEnabled(subId)
- ) {
- wifiPickerTrackerHelper.setCarrierNetworkEnabled(true)
+ Log.d(NetworkCellularGroupProvider.name, "setMobileData: $enabled")
+ if (enabled) {
+ Log.d(NetworkCellularGroupProvider.name, "setDefaultData: [$subId]")
+ subscriptionManager?.setDefaultDataSubId(subId)
}
- }
+ TelephonyRepository(context)
+ .setMobileData(subId, enabled, wifiPickerTrackerHelper)
+ }
\ No newline at end of file
diff --git a/src/com/android/settings/spa/network/SimOnboardingPrimarySim.kt b/src/com/android/settings/spa/network/SimOnboardingPrimarySim.kt
index 0306aad..1c96979 100644
--- a/src/com/android/settings/spa/network/SimOnboardingPrimarySim.kt
+++ b/src/com/android/settings/spa/network/SimOnboardingPrimarySim.kt
@@ -102,18 +102,21 @@
mobileDataSelectedId = mobileDataSelectedId,
actionSetCalls = {
callsSelectedId.intValue = it
- onboardingService.targetPrimarySimCalls = it},
+ onboardingService.targetPrimarySimCalls = it
+ },
actionSetTexts = {
textsSelectedId.intValue = it
- onboardingService.targetPrimarySimTexts = it},
+ onboardingService.targetPrimarySimTexts = it
+ },
actionSetMobileData = {
mobileDataSelectedId.intValue = it
- onboardingService.targetPrimarySimMobileData = it},
- isAutoDataEnabled = { isAutoDataEnabled },
+ onboardingService.targetPrimarySimMobileData = it
+ }
+ )
+ AutomaticDataSwitchingPreference(isAutoDataEnabled = { isAutoDataEnabled },
setAutoDataEnabled = { newEnabled ->
onboardingService.targetPrimarySimAutoDataSwitch.value = newEnabled
- },
- )
+ })
}
}
diff --git a/tests/spa_unit/src/com/android/settings/network/telephony/TelephonyRepositoryTest.kt b/tests/spa_unit/src/com/android/settings/network/telephony/TelephonyRepositoryTest.kt
index ce27ed4..6058935 100644
--- a/tests/spa_unit/src/com/android/settings/network/telephony/TelephonyRepositoryTest.kt
+++ b/tests/spa_unit/src/com/android/settings/network/telephony/TelephonyRepositoryTest.kt
@@ -92,6 +92,30 @@
}
@Test
+ fun isDataEnabled_invalidSub_returnFalse() = runBlocking {
+ val state = repository.isDataEnabled(
+ subId = SubscriptionManager.INVALID_SUBSCRIPTION_ID,
+ )
+
+ assertThat(state.firstWithTimeoutOrNull()).isFalse()
+ }
+
+ @Test
+ fun isDataEnabled_validSub_returnPolicyState() = runBlocking {
+ mockTelephonyManager.stub {
+ on {
+ isDataEnabledForReason(TelephonyManager.DATA_ENABLED_REASON_USER)
+ } doReturn true
+ }
+
+ val state = repository.isDataEnabled(
+ subId = SUB_ID,
+ )
+
+ assertThat(state.firstWithTimeoutOrNull()).isTrue()
+ }
+
+ @Test
fun telephonyCallbackFlow_callbackRegistered() = runBlocking {
val flow = context.telephonyCallbackFlow<Unit>(SUB_ID) {
object : TelephonyCallback() {}