SIM Onboarding flow completed
- Add the setup flow for switching sim and rename and setup primary sim
items
- Add the bottom sheet and progress dialog.
Bug: 318310357
Bug: 298898436
Bug: 298891941
Test: build pass. Will upload another cl for testing
Change-Id: Ie9680f0a67afe453c1449c0f2b59e98fd627e215
diff --git a/AndroidManifest.xml b/AndroidManifest.xml
index 195c44e..720b3587 100644
--- a/AndroidManifest.xml
+++ b/AndroidManifest.xml
@@ -802,6 +802,11 @@
</intent-filter>
</activity>
+ <activity android:name=".network.SimOnboardingActivity"
+ android:exported="false"
+ android:permission="android.permission.WRITE_EMBEDDED_SUBSCRIPTIONS"
+ android:theme="@style/Theme.SpaLib.BottomSheetDialog"/>
+
<activity android:name=".network.telephony.ToggleSubscriptionDialogActivity"
android:exported="false"
android:permission="android.permission.WRITE_EMBEDDED_SUBSCRIPTIONS"
diff --git a/src/com/android/settings/network/SimOnboardingActivity.kt b/src/com/android/settings/network/SimOnboardingActivity.kt
new file mode 100644
index 0000000..f5dc886
--- /dev/null
+++ b/src/com/android/settings/network/SimOnboardingActivity.kt
@@ -0,0 +1,430 @@
+/*
+ * 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
+
+import android.app.ProgressDialog
+import android.content.Context
+import android.content.Intent
+import android.os.Bundle
+import android.telephony.SubscriptionManager
+import android.util.Log
+import android.view.MotionEvent
+import androidx.activity.compose.setContent
+import androidx.compose.foundation.layout.Column
+import androidx.compose.foundation.layout.padding
+import androidx.compose.foundation.layout.size
+import androidx.compose.material.icons.Icons
+import androidx.compose.material.icons.outlined.SignalCellularAlt
+import androidx.compose.material3.Button
+import androidx.compose.material3.ExperimentalMaterial3Api
+import androidx.compose.material3.Icon
+import androidx.compose.material3.MaterialTheme
+import androidx.compose.material3.ModalBottomSheet
+import androidx.compose.material3.SheetState
+import androidx.compose.material3.Text
+import androidx.compose.material3.rememberModalBottomSheetState
+import androidx.compose.runtime.Composable
+import androidx.compose.runtime.LaunchedEffect
+import androidx.compose.runtime.MutableState
+import androidx.compose.runtime.mutableStateOf
+import androidx.compose.runtime.remember
+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.compose.ui.text.style.TextOverflow
+import com.android.settings.R
+import com.android.settings.SidecarFragment
+import com.android.settings.network.telephony.SubscriptionActionDialogActivity
+import com.android.settings.spa.SpaActivity.Companion.startSpaActivity
+import com.android.settings.spa.network.SimOnboardingPageProvider.getRoute
+import com.android.settingslib.spa.SpaBaseDialogActivity
+import com.android.settingslib.spa.framework.theme.SettingsDimension
+import com.android.settingslib.spa.framework.util.collectLatestWithLifecycle
+import com.android.settingslib.spa.widget.ui.SettingsTitle
+import com.android.settingslib.spaprivileged.framework.common.userManager
+import kotlinx.coroutines.CoroutineScope
+import kotlinx.coroutines.channels.awaitClose
+import kotlinx.coroutines.flow.Flow
+import kotlinx.coroutines.flow.callbackFlow
+import kotlinx.coroutines.flow.catch
+import kotlinx.coroutines.flow.conflate
+import kotlinx.coroutines.launch
+
+class SimOnboardingActivity : SpaBaseDialogActivity() {
+ lateinit var scope: CoroutineScope
+ lateinit var showBottomSheet: MutableState<Boolean>
+ lateinit var showError: MutableState<Boolean>
+ lateinit var showDialog: MutableState<Boolean>
+
+ private var switchToEuiccSubscriptionSidecar: SwitchToEuiccSubscriptionSidecar? = null
+ private var switchToRemovableSlotSidecar: SwitchToRemovableSlotSidecar? = null
+ private var enableMultiSimSidecar: EnableMultiSimSidecar? = null
+
+ 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()
+ return
+ }
+
+ var targetSubId = intent.getIntExtra(SUB_ID,SubscriptionManager.INVALID_SUBSCRIPTION_ID)
+ initServiceData(this, targetSubId, callbackListener)
+ if (!onboardingService.isUsableTargetSubscriptionId) {
+ Log.e(TAG, "The subscription id is not usable.")
+ finish()
+ return
+ }
+
+ switchToEuiccSubscriptionSidecar = SwitchToEuiccSubscriptionSidecar.get(fragmentManager)
+ switchToRemovableSlotSidecar = SwitchToRemovableSlotSidecar.get(fragmentManager)
+ enableMultiSimSidecar = EnableMultiSimSidecar.get(fragmentManager)
+
+ setContent {
+ Content()
+ }
+ }
+
+ override fun finish() {
+ setProgressDialog(false)
+ onboardingService.clear()
+ super.finish()
+ }
+
+ var callbackListener: (Int) -> Unit = {
+ Log.d(TAG, "Receive the CALLBACK: $it")
+ when (it) {
+ CALLBACK_ERROR -> {
+ setProgressDialog(false)
+ showError.value = true
+ }
+
+ CALLBACK_ONBOARDING_COMPLETE -> {
+ showBottomSheet.value = false
+ setProgressDialog(true)
+ scope.launch {
+ // TODO: refactor the Sidecar
+ // start to activate the sim
+ startSimSwitching()
+ }
+ }
+
+ CALLBACK_SETUP_NAME -> {
+ scope.launch {
+ onboardingService.startSetupName()
+ }
+ }
+
+ CALLBACK_SETUP_PRIMARY_SIM -> {
+ scope.launch {
+ onboardingService.startSetupPrimarySim(this@SimOnboardingActivity)
+ }
+ }
+
+ CALLBACK_FINISH -> {
+ finish()
+ }
+ }
+ }
+
+ fun setProgressDialog(enable: Boolean) {
+ showDialog.value = enable
+ val progressState = if (enable) {
+ SubscriptionActionDialogActivity.PROGRESS_IS_SHOWING
+ } else {
+ SubscriptionActionDialogActivity.PROGRESS_IS_NOT_SHOWING
+ }
+ setProgressState(progressState)
+ }
+
+ @OptIn(ExperimentalMaterial3Api::class)
+ @Composable
+ override fun Content() {
+ showBottomSheet = remember { mutableStateOf(true) }
+ showError = remember { mutableStateOf(false) }
+ showDialog = remember { mutableStateOf(false) }
+ scope = rememberCoroutineScope()
+
+ registerSidecarReceiverFlow()
+
+ if(showError.value){
+ // show error
+ return
+ }
+
+ if (showBottomSheet.value) {
+ var sheetState = rememberModalBottomSheetState()
+ BottomSheetImpl(
+ sheetState = sheetState,
+ nextAction = {
+ // TODO: if the phone is SS mode and the isDsdsConditionSatisfied is true, then
+ // enable the DSDS mode.
+ // case#1: the device need the reboot after enabling DSDS. Showing the confirm
+ // dialog to user whether reboot device or not.
+ // case#2: The device don't need the reboot. Enabling DSDS and then showing
+ // the SIM onboarding UI.
+
+ // case#2
+ val route = getRoute(onboardingService.targetSubId)
+ startSpaActivity(route)
+ },
+ cancelAction = { finish() },
+ )
+ } else {
+ ProgressDialogImpl()
+ }
+ }
+
+ @Composable
+ fun ProgressDialogImpl() {
+ // TODO: 1. Create the SPA's ProgressDialog and using SPA's widget
+ val dialog: ProgressDialog = object : ProgressDialog(this) {
+ override fun onTouchEvent(event: MotionEvent): Boolean {
+ return true
+ }
+ }
+ dialog.setMessage(
+ stringResource(
+ R.string.sim_onboarding_progressbar_turning_sim_on,
+ onboardingService.targetSubInfo?.displayName ?: ""
+ )
+ )
+ dialog.setCancelable(false)
+
+ if(showDialog.value) {
+ dialog.show()
+ }
+ }
+ @Composable
+ fun registerSidecarReceiverFlow(){
+ switchToEuiccSubscriptionSidecar?.sidecarReceiverFlow()
+ ?.collectLatestWithLifecycle(LocalLifecycleOwner.current) {
+ onStateChange(it)
+ }
+ switchToRemovableSlotSidecar?.sidecarReceiverFlow()
+ ?.collectLatestWithLifecycle(LocalLifecycleOwner.current) {
+ onStateChange(it)
+ }
+ enableMultiSimSidecar?.sidecarReceiverFlow()
+ ?.collectLatestWithLifecycle(LocalLifecycleOwner.current) {
+ onStateChange(it)
+ }
+ }
+
+ fun SidecarFragment.sidecarReceiverFlow(): Flow<SidecarFragment> = callbackFlow {
+ val broadcastReceiver = SidecarFragment.Listener {
+ Log.d(TAG, "onReceive: $it")
+ trySend(it)
+ }
+ addListener(broadcastReceiver)
+
+ awaitClose { removeListener(broadcastReceiver) }
+ }.catch { e ->
+ Log.e(TAG, "Error while sidecarReceiverFlow", e)
+ }.conflate()
+
+ fun startSimSwitching(){
+ Log.d(TAG, "startSimSwitching:")
+
+ var targetSubInfo = onboardingService.targetSubInfo
+ targetSubInfo?.let {
+ var removedSubInfo = onboardingService.getRemovedSim()
+ if (targetSubInfo.isEmbedded) {
+ switchToEuiccSubscriptionSidecar!!.run(
+ targetSubInfo.subscriptionId,
+ UiccSlotUtil.INVALID_PORT_ID,
+ removedSubInfo
+ )
+ return@let
+ }
+ switchToRemovableSlotSidecar!!.run(
+ UiccSlotUtil.INVALID_PHYSICAL_SLOT_ID,
+ removedSubInfo
+ )
+ } ?: run {
+ Log.e(TAG, "no target subInfo in onboardingService")
+ finish()
+ }
+ }
+
+ fun onStateChange(fragment: SidecarFragment?) {
+ if (fragment === switchToEuiccSubscriptionSidecar) {
+ handleSwitchToEuiccSubscriptionSidecarStateChange()
+ } else if (fragment === switchToRemovableSlotSidecar) {
+ handleSwitchToRemovableSlotSidecarStateChange()
+ } else if (fragment === enableMultiSimSidecar) {
+ handleEnableMultiSimSidecarStateChange()
+ }
+ }
+
+ fun handleSwitchToEuiccSubscriptionSidecarStateChange() {
+ when (switchToEuiccSubscriptionSidecar!!.state) {
+ SidecarFragment.State.SUCCESS -> {
+ Log.i(TAG, "Successfully enable the eSIM profile.")
+ switchToEuiccSubscriptionSidecar!!.reset()
+ callbackListener(CALLBACK_SETUP_NAME)
+ }
+
+ SidecarFragment.State.ERROR -> {
+ Log.i(TAG, "Failed to enable the eSIM profile.")
+ switchToEuiccSubscriptionSidecar!!.reset()
+ callbackListener(CALLBACK_ERROR)
+ // TODO: showErrorDialog and using privileged_action_disable_fail_title and
+ // privileged_action_disable_fail_text
+ }
+ }
+ }
+
+ fun handleSwitchToRemovableSlotSidecarStateChange() {
+ when (switchToRemovableSlotSidecar!!.state) {
+ SidecarFragment.State.SUCCESS -> {
+ Log.i(TAG, "Successfully switched to removable slot.")
+ switchToRemovableSlotSidecar!!.reset()
+ onboardingService.handleTogglePsimAction()
+ callbackListener(CALLBACK_SETUP_NAME)
+ }
+
+ SidecarFragment.State.ERROR -> {
+ Log.e(TAG, "Failed switching to removable slot.")
+ switchToRemovableSlotSidecar!!.reset()
+ callbackListener(CALLBACK_ERROR)
+ // TODO: showErrorDialog and using sim_action_enable_sim_fail_title and
+ // sim_action_enable_sim_fail_text
+ }
+ }
+ }
+
+ fun handleEnableMultiSimSidecarStateChange() {
+ when (enableMultiSimSidecar!!.state) {
+ SidecarFragment.State.SUCCESS -> {
+ enableMultiSimSidecar!!.reset()
+ Log.i(TAG, "Successfully switched to DSDS without reboot.")
+ handleEnableSubscriptionAfterEnablingDsds()
+ }
+
+ SidecarFragment.State.ERROR -> {
+ enableMultiSimSidecar!!.reset()
+ Log.i(TAG, "Failed to switch to DSDS without rebooting.")
+ callbackListener(CALLBACK_ERROR)
+ // TODO: showErrorDialog and using dsds_activation_failure_title and
+ // dsds_activation_failure_body_msg2
+ }
+ }
+ }
+
+ fun handleEnableSubscriptionAfterEnablingDsds() {
+ var targetSubInfo = onboardingService.targetSubInfo
+ if (targetSubInfo?.isEmbedded == true) {
+ Log.i(TAG,
+ "DSDS enabled, start to enable profile: " + targetSubInfo.getSubscriptionId()
+ )
+ // For eSIM operations, we simply switch to the selected eSIM profile.
+ switchToEuiccSubscriptionSidecar!!.run(
+ targetSubInfo.subscriptionId,
+ UiccSlotUtil.INVALID_PORT_ID,
+ null
+ )
+ return
+ }
+ Log.i(TAG, "DSDS enabled, start to enable pSIM profile.")
+ onboardingService.handleTogglePsimAction()
+ callbackListener(CALLBACK_FINISH)
+ }
+
+ @Composable
+ fun BottomSheetBody(nextAction: () -> Unit) {
+ Column(horizontalAlignment = Alignment.CenterHorizontally, modifier = Modifier.padding(bottom = SettingsDimension.itemPaddingVertical)) {
+ Icon(
+ imageVector = Icons.Outlined.SignalCellularAlt,
+ contentDescription = null,
+ modifier = Modifier
+ .size(SettingsDimension.iconLarge),
+ tint = MaterialTheme.colorScheme.primary,
+ )
+ SettingsTitle(stringResource(R.string.sim_onboarding_bottomsheets_title))
+ Column(Modifier.padding(SettingsDimension.itemPadding)) {
+ Text(
+ text = stringResource(R.string.sim_onboarding_bottomsheets_msg),
+ color = MaterialTheme.colorScheme.onSurfaceVariant,
+ style = MaterialTheme.typography.bodyMedium,
+ overflow = TextOverflow.Ellipsis,
+ textAlign = TextAlign.Center
+ )
+ }
+ Button(onClick = nextAction) {
+ Text(stringResource(R.string.sim_onboarding_setup))
+ }
+ }
+ }
+
+ @OptIn(ExperimentalMaterial3Api::class)
+ @Composable
+ fun BottomSheetImpl(
+ sheetState: SheetState,
+ nextAction: () -> Unit,
+ cancelAction: () -> Unit,
+ ) {
+ ModalBottomSheet(
+ onDismissRequest = cancelAction,
+ sheetState = sheetState,
+ ) {
+ BottomSheetBody(nextAction = nextAction)
+ }
+ LaunchedEffect(Unit) {
+ sheetState.show()
+ }
+ }
+
+ fun setProgressState(state: Int) {
+ val prefs = getSharedPreferences(
+ SubscriptionActionDialogActivity.SIM_ACTION_DIALOG_PREFS,
+ MODE_PRIVATE
+ )
+ prefs.edit().putInt(SubscriptionActionDialogActivity.KEY_PROGRESS_STATE, state).apply()
+ Log.i(TAG, "setProgressState:$state")
+ }
+
+ fun initServiceData(context: Context,targetSubId: Int, callback:(Int)->Unit) {
+ onboardingService.initData(targetSubId, context,callback)
+ }
+
+ companion object {
+ @JvmStatic
+ fun startSimOnboardingActivity(
+ context: Context,
+ subId: Int,
+ ) {
+ val intent = Intent(context, SimOnboardingActivity::class.java).apply {
+ putExtra(SUB_ID, subId)
+ }
+ context.startActivity(intent)
+ }
+
+ var onboardingService:SimOnboardingService = SimOnboardingService()
+ const val TAG = "SimOnboardingActivity"
+ const val SUB_ID = "sub_id"
+ const val CALLBACK_ERROR = -1
+ const val CALLBACK_ONBOARDING_COMPLETE = 1
+ const val CALLBACK_SETUP_NAME = 2
+ const val CALLBACK_SETUP_PRIMARY_SIM = 3
+ const val CALLBACK_FINISH = 4
+ }
+}
\ No newline at end of file
diff --git a/src/com/android/settings/network/SimOnboardingService.kt b/src/com/android/settings/network/SimOnboardingService.kt
index 1b3994e..8679385 100644
--- a/src/com/android/settings/network/SimOnboardingService.kt
+++ b/src/com/android/settings/network/SimOnboardingService.kt
@@ -23,11 +23,17 @@
import android.telephony.UiccCardInfo
import android.telephony.UiccSlotInfo
import android.util.Log
+import com.android.settings.spa.network.setAutomaticData
+import com.android.settings.spa.network.setDefaultData
+import com.android.settings.spa.network.setDefaultSms
+import com.android.settings.spa.network.setDefaultVoice
import com.android.settingslib.utils.ThreadUtils
+import kotlinx.coroutines.Dispatchers
+import kotlinx.coroutines.withContext
private const val TAG = "SimOnboardingService"
-private const val INVALID = -1
+private const val INVALID = SubscriptionManager.INVALID_SUBSCRIPTION_ID
class SimOnboardingService {
var subscriptionManager:SubscriptionManager? = null
@@ -40,19 +46,72 @@
var slotInfoList: List<UiccSlotInfo> = listOf()
var uiccCardInfoList: List<UiccCardInfo> = listOf()
var selectedSubInfoList: MutableList<SubscriptionInfo> = mutableListOf()
- var targetPrimarySimCalls: Int = -1
- var targetPrimarySimTexts: Int = -1
- var targetPrimarySimMobileData: Int = -1
+ var targetPrimarySimCalls: Int = SubscriptionManager.INVALID_SUBSCRIPTION_ID
+ var targetPrimarySimTexts: Int = SubscriptionManager.INVALID_SUBSCRIPTION_ID
+ var targetPrimarySimMobileData: Int = SubscriptionManager.INVALID_SUBSCRIPTION_ID
+ var targetPrimarySimAutoDataSwitch: Boolean = false
+ var targetNonDds: Int = SubscriptionManager.INVALID_SUBSCRIPTION_ID
+ get() {
+ if(targetPrimarySimMobileData == SubscriptionManager.INVALID_SUBSCRIPTION_ID) {
+ Log.w(TAG, "No DDS")
+ return SubscriptionManager.INVALID_SUBSCRIPTION_ID
+ }
+ return selectedSubInfoList
+ .filter { info ->
+ (info.simSlotIndex != -1) && (info.subscriptionId != targetPrimarySimMobileData)
+ }
+ .map { it.subscriptionId }
+ .firstOrNull() ?: SubscriptionManager.INVALID_SUBSCRIPTION_ID
+ }
+ var callback: (Int) -> Unit = {}
+
var isMultipleEnabledProfilesSupported: Boolean = false
get() {
if (uiccCardInfoList.isEmpty()) {
Log.w(TAG, "UICC cards info list is empty.")
return false
}
- return uiccCardInfoList.stream()
- .anyMatch { cardInfo: UiccCardInfo -> cardInfo.isMultipleEnabledProfilesSupported }
+ return uiccCardInfoList.any { it.isMultipleEnabledProfilesSupported }
}
+ var isRemovableSimEnabled: Boolean = false
+ get() {
+ if(slotInfoList.isEmpty()) {
+ Log.w(TAG, "UICC Slot info list is empty.")
+ return false
+ }
+ return UiccSlotUtil.isRemovableSimEnabled(slotInfoList)
+ }
+
+ var doesTargetSimHaveEsimOperation = false
+ get() {
+ return targetSubInfo?.isEmbedded ?: false
+ }
+
+ var isUsableTargetSubscriptionId = false
+ get() {
+ return SubscriptionManager.isUsableSubscriptionId(targetSubId)
+ }
+ var getActiveModemCount = 0
+ get() {
+ return telephonyManager?.getActiveModemCount() ?: 0
+ }
+
var renameMutableMap : MutableMap<Int, String> = mutableMapOf()
+ var userSelectedSubInfoList : MutableList<SubscriptionInfo> = mutableListOf()
+
+ var isSimSelectionFinished = false
+ get() {
+ return getActiveModemCount != 0 && userSelectedSubInfoList.size == getActiveModemCount
+ }
+
+ var isAllOfSlotAssigned = false
+ get() {
+ if(getActiveModemCount == 0){
+ Log.e(TAG, "isAllOfSlotAssigned: getActiveModemCount is 0")
+ return true
+ }
+ return getActiveModemCount != 0 && activeSubInfoList.size == getActiveModemCount
+ }
fun isValid(): Boolean {
return targetSubId != INVALID
@@ -73,18 +132,27 @@
targetPrimarySimCalls = -1
targetPrimarySimTexts = -1
targetPrimarySimMobileData = -1
- renameMutableMap.clear()
+ clearUserRecord()
}
- fun initData(inputTargetSubId:Int,context: Context) {
+ fun clearUserRecord(){
+ renameMutableMap.clear()
+ userSelectedSubInfoList.clear()
+ }
+
+ fun initData(inputTargetSubId:Int,context: Context, callback: (Int) -> Unit) {
+ this.callback = callback
targetSubId = inputTargetSubId
subscriptionManager = context.getSystemService(SubscriptionManager::class.java)
telephonyManager = context.getSystemService(TelephonyManager::class.java)
-
+ Log.d(
+ TAG, "startInit: targetSubId:$targetSubId"
+ )
ThreadUtils.postOnBackgroundThread {
activeSubInfoList = SubscriptionUtil.getActiveSubscriptions(subscriptionManager)
availableSubInfoList = SubscriptionUtil.getAvailableSubscriptions(context)
targetSubInfo = availableSubInfoList.find { subInfo -> subInfo.subscriptionId == targetSubId }
+ targetSubInfo?.let { userSelectedSubInfoList.add(it) }
Log.d(
TAG, "targetSubId: $targetSubId" + ", targetSubInfo: $targetSubInfo" +
". activeSubInfoList: $activeSubInfoList"
@@ -94,11 +162,24 @@
uiccCardInfoList = telephonyManager?.uiccCardsInfo!!
Log.d(TAG, "uiccCardInfoList: $uiccCardInfoList")
- Log.d(TAG, "isMultipleEnabledProfilesSupported: $isMultipleEnabledProfilesSupported")
+ targetPrimarySimCalls = SubscriptionManager.getDefaultVoiceSubscriptionId()
+ targetPrimarySimTexts = SubscriptionManager.getDefaultSmsSubscriptionId()
+ targetPrimarySimMobileData = SubscriptionManager.getDefaultDataSubscriptionId()
+ Log.d(
+ TAG,"doesTargetSimHaveEsimOperation: $doesTargetSimHaveEsimOperation" +
+ ", isRemovableSimEnabled: $isRemovableSimEnabled" +
+ ", isMultipleEnabledProfilesSupported: $isMultipleEnabledProfilesSupported" +
+ ", targetPrimarySimCalls: $targetPrimarySimCalls" +
+ ", targetPrimarySimTexts: $targetPrimarySimTexts" +
+ ", targetPrimarySimMobileData: $targetPrimarySimMobileData")
}
}
- fun getSelectableSubscriptionInfo(): List<SubscriptionInfo> {
+ /**
+ * Return the subscriptionInfo list which has
+ * the target subscriptionInfo + active subscriptionInfo.
+ */
+ fun getSelectableSubscriptionInfoList(): List<SubscriptionInfo> {
var list: MutableList<SubscriptionInfo> = mutableListOf()
list.addAll(activeSubInfoList)
if (!list.contains(targetSubInfo)) {
@@ -109,18 +190,102 @@
return list.toList()
}
+ /**
+ * Return the user selected SubscriptionInfo list.
+ */
+ fun getSelectedSubscriptionInfoList(): List<SubscriptionInfo> {
+ if (userSelectedSubInfoList.isEmpty()){
+ Log.d(TAG, "userSelectedSubInfoList is empty")
+ return activeSubInfoList
+ }
+ return userSelectedSubInfoList.toList()
+ }
+
fun addItemForRenaming(subInfo: SubscriptionInfo, newName: String) {
if (subInfo.displayName == newName) {
return
}
renameMutableMap[subInfo.subscriptionId] = newName
+ Log.d(TAG, "renameMutableMap add ${subInfo.subscriptionId} & $newName into: $renameMutableMap")
}
fun getSubscriptionInfoDisplayName(subInfo: SubscriptionInfo): String {
return renameMutableMap[subInfo.subscriptionId] ?: subInfo.displayName.toString()
}
- fun startActivatingSim(callback:() -> Unit){
+ fun addCurrentItemForSelectedSim(){
+ userSelectedSubInfoList.addAll(activeSubInfoList)
+ }
+
+ fun addItemForSelectedSim(selectedSubInfo: SubscriptionInfo) {
+ userSelectedSubInfoList.add(selectedSubInfo)
+ }
+
+ fun removeItemForSelectedSim(selectedSubInfo: SubscriptionInfo) {
+ if (userSelectedSubInfoList.contains(selectedSubInfo)) {
+ userSelectedSubInfoList.remove(selectedSubInfo)
+ }
+ }
+
+ /**
+ * Return the subscriptionInfo which will be removed in the slot during the sim onboarding.
+ * If return Null, then no subscriptionInfo will be removed in the slot.
+ */
+ fun getRemovedSim():SubscriptionInfo?{
+ return activeSubInfoList.find { !userSelectedSubInfoList.contains(it) }
+ }
+
+ fun handleTogglePsimAction() {
+ val canDisablePhysicalSubscription =
+ subscriptionManager?.canDisablePhysicalSubscription() == true
+ if (targetSubInfo != null && canDisablePhysicalSubscription) {
+ // TODO: to support disable case.
+ subscriptionManager?.setUiccApplicationsEnabled(
+ targetSubInfo!!.subscriptionId, /*enabled=*/true)
+ } else {
+ Log.i(TAG, "The device does not support toggling pSIM. It is enough to just "
+ + "enable the removable slot."
+ )
+ }
+ }
+
+ fun startActivatingSim(){
// TODO: start to activate sim
+ callback(SimOnboardingActivity.CALLBACK_FINISH)
+ }
+
+ suspend fun startSetupName() {
+ withContext(Dispatchers.Default) {
+ renameMutableMap.forEach {
+ subscriptionManager?.setDisplayName(
+ it.value, it.key,
+ SubscriptionManager.NAME_SOURCE_USER_INPUT
+ )
+ }
+ // next action is SETUP_PRIMARY_SIM
+ callback(SimOnboardingActivity.CALLBACK_SETUP_PRIMARY_SIM)
+ }
+ }
+
+ suspend fun startSetupPrimarySim(context: Context) {
+ withContext(Dispatchers.Default) {
+ setDefaultVoice(subscriptionManager,targetPrimarySimCalls)
+ setDefaultSms(subscriptionManager,targetPrimarySimTexts)
+ setDefaultData(
+ context,
+ subscriptionManager,
+ null,
+ targetPrimarySimMobileData
+ )
+
+
+ val telephonyManagerForNonDds: TelephonyManager? =
+ context.getSystemService(TelephonyManager::class.java)
+ ?.createForSubscriptionId(targetNonDds)
+ setAutomaticData(telephonyManagerForNonDds, targetPrimarySimAutoDataSwitch)
+
+ // no next action, send finish
+ callback(SimOnboardingActivity.CALLBACK_FINISH)
+ }
}
}
\ No newline at end of file
diff --git a/src/com/android/settings/network/SubscriptionUtil.java b/src/com/android/settings/network/SubscriptionUtil.java
index b6b433b..84e4e75 100644
--- a/src/com/android/settings/network/SubscriptionUtil.java
+++ b/src/com/android/settings/network/SubscriptionUtil.java
@@ -48,8 +48,6 @@
import com.android.settings.network.helper.SubscriptionAnnotation;
import com.android.settings.network.telephony.DeleteEuiccSubscriptionDialogActivity;
import com.android.settings.network.telephony.ToggleSubscriptionDialogActivity;
-import com.android.settings.spa.SpaActivity;
-import com.android.settings.spa.network.SimOnboardingPageProvider;
import java.util.ArrayList;
import java.util.Collections;
@@ -546,8 +544,7 @@
return;
}
if (enable && Flags.isDualSimOnboardingEnabled()) {
- String route = SimOnboardingPageProvider.INSTANCE.getRoute(subId);
- SpaActivity.startSpaActivity(context, route);
+ SimOnboardingActivity.startSimOnboardingActivity(context, subId);
return;
}
context.startActivity(ToggleSubscriptionDialogActivity.getIntent(context, subId, enable));
diff --git a/src/com/android/settings/network/UiccSlotUtil.java b/src/com/android/settings/network/UiccSlotUtil.java
index 5175c23..df23f12 100644
--- a/src/com/android/settings/network/UiccSlotUtil.java
+++ b/src/com/android/settings/network/UiccSlotUtil.java
@@ -463,17 +463,27 @@
if (telMgr == null) {
return false;
}
- ImmutableList<UiccSlotInfo> slotInfos = UiccSlotUtil.getSlotInfos(telMgr);
+ List<UiccSlotInfo> slotInfos = UiccSlotUtil.getSlotInfos(telMgr);
+ return isRemovableSimEnabled(slotInfos);
+ }
+
+ /**
+ * Return whether the removable psim is enabled.
+ *
+ * @param slotInfos is a List of UiccSlotInfo.
+ * @return whether the removable psim is enabled.
+ */
+ public static boolean isRemovableSimEnabled(List<UiccSlotInfo> slotInfos) {
boolean isRemovableSimEnabled =
slotInfos.stream()
.anyMatch(
slot -> slot != null
&& slot.isRemovable()
&& !slot.getIsEuicc()
- && slot.getPorts().stream().anyMatch(
- port -> port.isActive())
+ && slot.getPorts().stream()
+ .anyMatch(port -> port.isActive())
&& slot.getCardStateInfo()
- == UiccSlotInfo.CARD_STATE_INFO_PRESENT);
+ == UiccSlotInfo.CARD_STATE_INFO_PRESENT);
Log.i(TAG, "isRemovableSimEnabled: " + isRemovableSimEnabled);
return isRemovableSimEnabled;
}
diff --git a/src/com/android/settings/sim/SimDialogActivity.java b/src/com/android/settings/sim/SimDialogActivity.java
index d65b2d1..4544e73 100644
--- a/src/com/android/settings/sim/SimDialogActivity.java
+++ b/src/com/android/settings/sim/SimDialogActivity.java
@@ -41,6 +41,7 @@
import com.android.internal.annotations.VisibleForTesting;
import com.android.settings.R;
+import com.android.settings.flags.Flags;
import com.android.settings.network.CarrierConfigCache;
import com.android.settings.network.SubscriptionUtil;
import com.android.settings.network.ims.WifiCallingQueryImsState;
@@ -134,6 +135,17 @@
return;
}
+ if (Flags.isDualSimOnboardingEnabled()
+ && getProgressState() == SubscriptionActionDialogActivity.PROGRESS_IS_SHOWING
+ && (dialogType == PREFERRED_PICK
+ || dialogType == DATA_PICK
+ || dialogType == CALLS_PICK
+ || dialogType == SMS_PICK)) {
+ Log.d(TAG, "Finish the sim dialog since the sim onboarding is shown");
+ finish();
+ return;
+ }
+
final String tag = Integer.toString(dialogType);
final FragmentManager fragmentManager = getSupportFragmentManager();
SimDialogFragment fragment = (SimDialogFragment) fragmentManager.findFragmentByTag(tag);
diff --git a/src/com/android/settings/spa/network/NetworkCellularGroupProvider.kt b/src/com/android/settings/spa/network/NetworkCellularGroupProvider.kt
index e746d4a..b6d83f2 100644
--- a/src/com/android/settings/spa/network/NetworkCellularGroupProvider.kt
+++ b/src/com/android/settings/spa/network/NetworkCellularGroupProvider.kt
@@ -67,6 +67,7 @@
import com.android.settingslib.spaprivileged.model.enterprise.Restrictions
import com.android.settingslib.spaprivileged.template.preference.RestrictedPreference
+import kotlinx.coroutines.CoroutineScope
import kotlinx.coroutines.Dispatchers
import kotlinx.coroutines.flow.Flow
import kotlinx.coroutines.flow.combine
@@ -171,6 +172,8 @@
.map { it.subscriptionId }
.firstOrNull() ?: SubscriptionManager.INVALID_SUBSCRIPTION_ID
}
+
+ Log.d(name, "defaultDataSubId: $defaultDataSubId, nonDds: $nonDds")
}
}
@@ -195,7 +198,6 @@
selectableSubscriptionInfoList
)
PrimarySimSectionImpl(
- subscriptionManager,
activeSubscriptionInfoList,
defaultVoiceSubId,
defaultSmsSubId,
@@ -257,12 +259,49 @@
@Composable
fun PrimarySimSectionImpl(
- subscriptionManager: SubscriptionManager?,
- activeSubscriptionInfoList: List<SubscriptionInfo>,
- callsSelectedId: MutableIntState,
- textsSelectedId: MutableIntState,
- mobileDataSelectedId: MutableIntState,
- nonDds: MutableIntState
+ subscriptionInfoList: List<SubscriptionInfo>,
+ callsSelectedId: MutableIntState,
+ textsSelectedId: MutableIntState,
+ mobileDataSelectedId: MutableIntState,
+ nonDds: MutableIntState,
+ subscriptionManager: SubscriptionManager? =
+ LocalContext.current.getSystemService(SubscriptionManager::class.java),
+ coroutineScope: CoroutineScope = rememberCoroutineScope(),
+ context: Context = LocalContext.current,
+ actionSetCalls: (Int) -> Unit = {
+ callsSelectedId.intValue = it
+ coroutineScope.launch {
+ setDefaultVoice(subscriptionManager, it)
+ }
+ },
+ actionSetTexts: (Int) -> Unit = {
+ textsSelectedId.intValue = it
+ coroutineScope.launch {
+ setDefaultSms(subscriptionManager, it)
+ }
+ },
+ actionSetMobileData: (Int) -> Unit = {
+ mobileDataSelectedId.intValue = it
+ coroutineScope.launch {
+ // TODO: to fix the WifiPickerTracker crash when create
+ // the wifiPickerTrackerHelper
+ setDefaultData(
+ context,
+ subscriptionManager,
+ null/*wifiPickerTrackerHelper*/,
+ it
+ )
+ }
+ },
+ actionSetAutoDataSwitch: (Boolean) -> Unit = { newState ->
+ coroutineScope.launch {
+ val telephonyManagerForNonDds: TelephonyManager? =
+ context.getSystemService(TelephonyManager::class.java)
+ ?.createForSubscriptionId(nonDds.intValue)
+ Log.d(NetworkCellularGroupProvider.name, "NonDds:${nonDds.intValue} setAutomaticData")
+ setAutomaticData(telephonyManagerForNonDds, newState)
+ }
+ },
) {
var state = rememberSaveable { mutableStateOf(false) }
var callsAndSmsList = remember {
@@ -272,11 +311,11 @@
mutableListOf(ListPreferenceOption(id = -1, text = "Loading"))
}
- if (activeSubscriptionInfoList.size >= 2) {
+ if (subscriptionInfoList.size >= 2) {
state.value = true
callsAndSmsList.clear()
dataList.clear()
- for (info in activeSubscriptionInfoList) {
+ for (info in subscriptionInfoList) {
var item = ListPreferenceOption(
id = info.subscriptionId,
text = "${info.displayName}"
@@ -291,12 +330,10 @@
} else {
// hide the primary sim
state.value = false
- Log.d("NetworkCellularGroupProvider", "Hide primary sim")
+ Log.d(NetworkCellularGroupProvider.name, "Hide primary sim")
}
if (state.value) {
- val coroutineScope = rememberCoroutineScope()
- var context = LocalContext.current
val telephonyManagerForNonDds: TelephonyManager? =
context.getSystemService(TelephonyManager::class.java)
?.createForSubscriptionId(nonDds.intValue)
@@ -305,44 +342,27 @@
}
Category(title = stringResource(id = R.string.primary_sim_title)) {
- createPrimarySimListPreference(
+ CreatePrimarySimListPreference(
stringResource(id = R.string.primary_sim_calls_title),
callsAndSmsList,
callsSelectedId,
ImageVector.vectorResource(R.drawable.ic_phone),
- ) {
- callsSelectedId.intValue = it
- coroutineScope.launch {
- setDefaultVoice(subscriptionManager, it)
- }
- }
- createPrimarySimListPreference(
+ actionSetCalls
+ )
+ CreatePrimarySimListPreference(
stringResource(id = R.string.primary_sim_texts_title),
callsAndSmsList,
textsSelectedId,
Icons.AutoMirrored.Outlined.Message,
- ) {
- textsSelectedId.intValue = it
- coroutineScope.launch {
- setDefaultSms(subscriptionManager, it)
- }
- }
- createPrimarySimListPreference(
+ actionSetTexts
+ )
+ CreatePrimarySimListPreference(
stringResource(id = R.string.mobile_data_settings_title),
dataList,
mobileDataSelectedId,
Icons.Outlined.DataUsage,
- ) {
- mobileDataSelectedId.intValue = it
- coroutineScope.launch {
- // TODO: to fix the WifiPickerTracker crash when create
- // the wifiPickerTrackerHelper
- setDefaultData(context,
- subscriptionManager,
- null/*wifiPickerTrackerHelper*/,
- it)
- }
- }
+ actionSetMobileData
+ )
}
val autoDataTitle = stringResource(id = R.string.primary_sim_automatic_data_title)
@@ -351,25 +371,18 @@
object : SwitchPreferenceModel {
override val title = autoDataTitle
override val summary = { autoDataSummary }
- override val changeable: () -> Boolean = {
- nonDds.intValue != SubscriptionManager.INVALID_SUBSCRIPTION_ID
- }
override val checked = {
- coroutineScope.launch {
- withContext(Dispatchers.Default) {
- automaticDataChecked.value = telephonyManagerForNonDds != null
- && telephonyManagerForNonDds.isMobileDataPolicyEnabled(
- TelephonyManager.MOBILE_DATA_POLICY_AUTO_DATA_SWITCH)
+ if (nonDds.intValue != SubscriptionManager.INVALID_SUBSCRIPTION_ID) {
+ coroutineScope.launch {
+ automaticDataChecked.value = getAutomaticData(telephonyManagerForNonDds)
}
}
automaticDataChecked.value
}
- override val onCheckedChange: ((Boolean) -> Unit)? =
- { newChecked: Boolean ->
- coroutineScope.launch {
- setAutomaticData(telephonyManagerForNonDds, newChecked)
- }
- }
+ override val onCheckedChange: ((Boolean) -> Unit)? = {
+ automaticDataChecked.value = it
+ actionSetAutoDataSwitch(it)
+ }
}
})
}
@@ -428,19 +441,19 @@
return MobileNetworkUtils.showEuiccSettings(context)
}
-private suspend fun setDefaultVoice(
+suspend fun setDefaultVoice(
subscriptionManager: SubscriptionManager?,
subId: Int): Unit = withContext(Dispatchers.Default) {
subscriptionManager?.setDefaultVoiceSubscriptionId(subId)
}
-private suspend fun setDefaultSms(
+suspend fun setDefaultSms(
subscriptionManager: SubscriptionManager?,
subId: Int): Unit = withContext(Dispatchers.Default) {
subscriptionManager?.setDefaultSmsSubId(subId)
}
-private suspend fun setDefaultData(context: Context,
+suspend fun setDefaultData(context: Context,
subscriptionManager: SubscriptionManager?,
wifiPickerTrackerHelper: WifiPickerTrackerHelper?,
subId: Int): Unit = withContext(Dispatchers.Default) {
@@ -455,11 +468,22 @@
wifiPickerTrackerHelper.setCarrierNetworkEnabled(true)
}
}
+suspend fun getAutomaticData(telephonyManagerForNonDds: TelephonyManager?): Boolean =
+ withContext(Dispatchers.Default) {
+ telephonyManagerForNonDds != null
+ && telephonyManagerForNonDds.isMobileDataPolicyEnabled(
+ TelephonyManager.MOBILE_DATA_POLICY_AUTO_DATA_SWITCH)
+ }
-private suspend fun setAutomaticData(telephonyManager: TelephonyManager?, newState: Boolean): Unit =
- withContext(Dispatchers.Default) {
- telephonyManager?.setMobileDataPolicyEnabled(
- TelephonyManager.MOBILE_DATA_POLICY_AUTO_DATA_SWITCH,
- newState)
- //TODO: setup backup calling
- }
\ No newline at end of file
+suspend fun setAutomaticData(telephonyManager: TelephonyManager?, newState: Boolean): Unit =
+ withContext(Dispatchers.Default) {
+ Log.d(
+ "NetworkCellularGroupProvider",
+ "setAutomaticData: MOBILE_DATA_POLICY_AUTO_DATA_SWITCH as $newState"
+ )
+ telephonyManager?.setMobileDataPolicyEnabled(
+ TelephonyManager.MOBILE_DATA_POLICY_AUTO_DATA_SWITCH,
+ newState
+ )
+ //TODO: setup backup calling
+ }
\ No newline at end of file
diff --git a/src/com/android/settings/spa/network/SimOnboardingLabelSim.kt b/src/com/android/settings/spa/network/SimOnboardingLabelSim.kt
index 4cb04b6..e88c5c7 100644
--- a/src/com/android/settings/spa/network/SimOnboardingLabelSim.kt
+++ b/src/com/android/settings/spa/network/SimOnboardingLabelSim.kt
@@ -27,11 +27,9 @@
import androidx.compose.runtime.remember
import androidx.compose.runtime.setValue
import androidx.compose.ui.Modifier
-import androidx.compose.ui.platform.LocalContext
import androidx.compose.ui.res.stringResource
import com.android.settings.R
import com.android.settings.network.SimOnboardingService
-import com.android.settings.network.SubscriptionUtil
import com.android.settingslib.spa.framework.theme.SettingsDimension
import com.android.settingslib.spa.widget.dialog.AlertDialogButton
import com.android.settingslib.spa.widget.dialog.rememberAlertDialogPresenter
@@ -74,7 +72,7 @@
SettingsBody(stringResource(R.string.sim_onboarding_label_sim_msg))
}
- for (subInfo in onboardingService.getSelectableSubscriptionInfo()) {
+ for (subInfo in onboardingService.getSelectableSubscriptionInfoList()) {
var titleSimName by remember {
mutableStateOf(
onboardingService.getSubscriptionInfoDisplayName(subInfo)
diff --git a/src/com/android/settings/spa/network/SimOnboardingPageProvider.kt b/src/com/android/settings/spa/network/SimOnboardingPageProvider.kt
index e46dc2e..c60ac88 100644
--- a/src/com/android/settings/spa/network/SimOnboardingPageProvider.kt
+++ b/src/com/android/settings/spa/network/SimOnboardingPageProvider.kt
@@ -21,6 +21,7 @@
import android.content.Context
import android.content.ContextWrapper
import android.os.Bundle
+import android.util.Log
import androidx.annotation.VisibleForTesting
import androidx.compose.runtime.Composable
import androidx.compose.ui.platform.LocalContext
@@ -31,12 +32,12 @@
import androidx.navigation.compose.rememberNavController
import androidx.navigation.navArgument
import com.android.settings.R
+import com.android.settings.network.SimOnboardingActivity
import com.android.settings.network.SimOnboardingService
import com.android.settingslib.spa.framework.common.SettingsEntryBuilder
import com.android.settingslib.spa.framework.common.SettingsPageProvider
import com.android.settingslib.spa.framework.common.createSettingsPage
import com.android.settingslib.spa.framework.compose.navigator
-
import com.android.settingslib.spa.widget.preference.Preference
import com.android.settingslib.spa.widget.preference.PreferenceModel
@@ -59,7 +60,7 @@
private val owner = createSettingsPage()
@VisibleForTesting
- var onboardingService: SimOnboardingService = SimOnboardingService()
+ var onboardingService: SimOnboardingService = SimOnboardingActivity.onboardingService
fun buildInjectEntry() = SettingsEntryBuilder.createInject(owner = owner)
.setUiLayoutFn {
@@ -72,18 +73,12 @@
@Composable
override fun Page(arguments: Bundle?) {
- initServiceData(arguments!!.getInt(SUB_ID))
PageImpl(onboardingService,rememberNavController())
}
fun getRoute(
subId: Int
): String = "${name}/$subId"
-
- @Composable
- fun initServiceData(targetSubId: Int) {
- onboardingService.initData(targetSubId, LocalContext.current)
- }
}
private fun Context.getActivity(): Activity? = when (this) {
@@ -95,7 +90,10 @@
@Composable
fun PageImpl(onboardingService:SimOnboardingService,navHostController: NavHostController) {
val context = LocalContext.current
- var previousPageOfOnboarding: () -> Unit = { context.getActivity()?.finish() }
+ var finishOnboarding: () -> Unit = {
+ context.getActivity()?.finish()
+ onboardingService.callback(SimOnboardingActivity.CALLBACK_FINISH)
+ }
NavHost(
navController = navHostController,
@@ -103,31 +101,32 @@
) {
composable(route = SimOnboardingScreen.LabelSim.name) {
val nextPage =
- // Adding more conditions
- if (onboardingService.isMultipleEnabledProfilesSupported) {
+ if (onboardingService.isMultipleEnabledProfilesSupported && onboardingService.isAllOfSlotAssigned) {
SimOnboardingScreen.SelectSim.name
} else {
+ onboardingService.addCurrentItemForSelectedSim()
SimOnboardingScreen.PrimarySim.name
}
SimOnboardingLabelSimImpl(
nextAction = { navHostController.navigate(nextPage) },
- cancelAction = previousPageOfOnboarding,
+ cancelAction = finishOnboarding,
onboardingService = onboardingService
)
}
composable(route = SimOnboardingScreen.PrimarySim.name) {
SimOnboardingPrimarySimImpl(
nextAction = {
- //go back and activate sim
+ onboardingService.callback(SimOnboardingActivity.CALLBACK_ONBOARDING_COMPLETE)
+ context.getActivity()?.finish()
},
- cancelAction = previousPageOfOnboarding,
+ cancelAction = finishOnboarding,
onboardingService = onboardingService
)
}
composable(route = SimOnboardingScreen.SelectSim.name) {
SimOnboardingSelectSimImpl(
nextAction = { navHostController.navigate(SimOnboardingScreen.PrimarySim.name) },
- cancelAction = previousPageOfOnboarding,
+ cancelAction = finishOnboarding,
onboardingService = onboardingService
)
}
diff --git a/src/com/android/settings/spa/network/SimOnboardingPrimarySim.kt b/src/com/android/settings/spa/network/SimOnboardingPrimarySim.kt
index 5752a4f..999abb4 100644
--- a/src/com/android/settings/spa/network/SimOnboardingPrimarySim.kt
+++ b/src/com/android/settings/spa/network/SimOnboardingPrimarySim.kt
@@ -16,32 +16,25 @@
package com.android.settings.spa.network
+import android.telephony.SubscriptionManager
import androidx.compose.foundation.layout.Column
import androidx.compose.foundation.layout.padding
import androidx.compose.material.icons.Icons
-import androidx.compose.material.icons.automirrored.outlined.Message
-import androidx.compose.material.icons.outlined.DataUsage
import androidx.compose.material.icons.outlined.SignalCellularAlt
import androidx.compose.runtime.Composable
import androidx.compose.runtime.MutableIntState
-import androidx.compose.runtime.getValue
import androidx.compose.runtime.mutableIntStateOf
-import androidx.compose.runtime.mutableStateOf
import androidx.compose.runtime.remember
import androidx.compose.runtime.saveable.rememberSaveable
-import androidx.compose.runtime.setValue
import androidx.compose.ui.Modifier
import androidx.compose.ui.graphics.vector.ImageVector
import androidx.compose.ui.res.stringResource
-import androidx.compose.ui.res.vectorResource
import com.android.settings.R
import com.android.settings.network.SimOnboardingService
import com.android.settingslib.spa.framework.theme.SettingsDimension
import com.android.settingslib.spa.widget.preference.ListPreference
import com.android.settingslib.spa.widget.preference.ListPreferenceModel
import com.android.settingslib.spa.widget.preference.ListPreferenceOption
-import com.android.settingslib.spa.widget.preference.SwitchPreference
-import com.android.settingslib.spa.widget.preference.SwitchPreferenceModel
import com.android.settingslib.spa.widget.scaffold.BottomAppBarButton
import com.android.settingslib.spa.widget.scaffold.SuwScaffold
import com.android.settingslib.spa.widget.ui.SettingsBody
@@ -68,84 +61,54 @@
cancelAction
),
) {
- primarySimBody(onboardingService)
- }
-}
-
-@Composable
-private fun primarySimBody(onboardingService: SimOnboardingService) {
- //TODO: Load the status from the frameworks
- var callsSelectedId = rememberSaveable { mutableIntStateOf(1) }
- var textsSelectedId = rememberSaveable { mutableIntStateOf(1) }
- var mobileDataSelectedId = rememberSaveable { mutableIntStateOf(1) }
- var automaticDataChecked by rememberSaveable { mutableStateOf(true) }
-
- Column(Modifier.padding(SettingsDimension.itemPadding)) {
- SettingsBody(stringResource(id = R.string.sim_onboarding_primary_sim_msg))
- }
- var selectableSubscriptionInfo = onboardingService.getSelectableSubscriptionInfo()
- var list = listOf(ListPreferenceOption(id = -1, text = "Loading"))
- if (selectableSubscriptionInfo.size >= 2) {
- list = listOf(
- ListPreferenceOption(
- id = selectableSubscriptionInfo[0].subscriptionId,
- text = "${selectableSubscriptionInfo[0].displayName}"
- ),
- ListPreferenceOption(
- id = selectableSubscriptionInfo[1].subscriptionId,
- text = "${selectableSubscriptionInfo[1].displayName}"
- ),
- ListPreferenceOption(
- id = -1,
- text = stringResource(id = R.string.sim_calls_ask_first_prefs_title)
- ),
- )
- } else {
- // set all of primary sim items' enable as false and showing that sim.
- }
- createPrimarySimListPreference(
- stringResource(id = R.string.primary_sim_calls_title),
- list,
- callsSelectedId,
- ImageVector.vectorResource(R.drawable.ic_phone),
- onIdSelected = { callsSelectedId.intValue = it }
- )
- createPrimarySimListPreference(
- stringResource(id = R.string.primary_sim_texts_title),
- list,
- textsSelectedId,
- Icons.AutoMirrored.Outlined.Message,
- onIdSelected = { textsSelectedId.intValue = it }
- )
-
- createPrimarySimListPreference(
- stringResource(id = R.string.mobile_data_settings_title),
- list,
- mobileDataSelectedId,
- Icons.Outlined.DataUsage,
- onIdSelected = { mobileDataSelectedId.intValue = it }
- )
-
- val autoDataTitle = stringResource(id = R.string.primary_sim_automatic_data_title)
- val autoDataSummary = stringResource(id = R.string.primary_sim_automatic_data_msg)
- SwitchPreference(remember {
- object : SwitchPreferenceModel {
- override val title = autoDataTitle
- override val summary = { autoDataSummary }
- override val checked = { automaticDataChecked }
- override val onCheckedChange =
- { newChecked: Boolean -> automaticDataChecked = newChecked }
+ val callsSelectedId = rememberSaveable {
+ mutableIntStateOf(SubscriptionManager.INVALID_SUBSCRIPTION_ID)
}
- })
+ val textsSelectedId = rememberSaveable {
+ mutableIntStateOf(SubscriptionManager.INVALID_SUBSCRIPTION_ID)
+ }
+ val mobileDataSelectedId = rememberSaveable {
+ mutableIntStateOf(SubscriptionManager.INVALID_SUBSCRIPTION_ID)
+ }
+ val nonDdsRemember = rememberSaveable {
+ mutableIntStateOf(SubscriptionManager.INVALID_SUBSCRIPTION_ID)
+ }
+
+ Column(Modifier.padding(SettingsDimension.itemPadding)) {
+ SettingsBody(stringResource(id = R.string.sim_onboarding_primary_sim_msg))
+ }
+
+ var selectedSubscriptionInfoList = onboardingService.getSelectedSubscriptionInfoList()
+ callsSelectedId.intValue = onboardingService.targetPrimarySimCalls
+ textsSelectedId.intValue = onboardingService.targetPrimarySimTexts
+ mobileDataSelectedId.intValue = onboardingService.targetPrimarySimMobileData
+ PrimarySimSectionImpl(
+ subscriptionInfoList = selectedSubscriptionInfoList,
+ callsSelectedId = callsSelectedId,
+ textsSelectedId = textsSelectedId,
+ mobileDataSelectedId = mobileDataSelectedId,
+ nonDds = nonDdsRemember,
+ actionSetCalls = {
+ callsSelectedId.intValue = it
+ onboardingService.targetPrimarySimCalls = it},
+ actionSetTexts = {
+ textsSelectedId.intValue = it
+ onboardingService.targetPrimarySimTexts = it},
+ actionSetMobileData = {
+ mobileDataSelectedId.intValue = it
+ onboardingService.targetPrimarySimMobileData = it},
+ actionSetAutoDataSwitch = {
+ onboardingService.targetPrimarySimAutoDataSwitch = it},
+ )
+ }
}
@Composable
-fun createPrimarySimListPreference(
+fun CreatePrimarySimListPreference(
title: String,
list: List<ListPreferenceOption>,
selectedId: MutableIntState,
icon: ImageVector,
- enable: Boolean = true,
onIdSelected: (id: Int) -> Unit
) = ListPreference(remember {
object : ListPreferenceModel {
@@ -156,7 +119,5 @@
override val icon = @Composable {
SettingsIcon(icon)
}
- override val enabled: () -> Boolean
- get() = { enable }
}
})
\ No newline at end of file
diff --git a/src/com/android/settings/spa/network/SimOnboardingSelectSim.kt b/src/com/android/settings/spa/network/SimOnboardingSelectSim.kt
index 1955d13..5e71b12 100644
--- a/src/com/android/settings/spa/network/SimOnboardingSelectSim.kt
+++ b/src/com/android/settings/spa/network/SimOnboardingSelectSim.kt
@@ -16,24 +16,23 @@
package com.android.settings.spa.network
+import android.util.Log
import androidx.compose.foundation.layout.Column
import androidx.compose.foundation.layout.padding
import androidx.compose.material.icons.Icons
import androidx.compose.material.icons.outlined.SignalCellularAlt
import androidx.compose.runtime.Composable
-import androidx.compose.runtime.getValue
import androidx.compose.runtime.mutableStateOf
import androidx.compose.runtime.remember
import androidx.compose.runtime.saveable.rememberSaveable
-import androidx.compose.runtime.setValue
import androidx.compose.ui.Modifier
import androidx.compose.ui.res.stringResource
import com.android.settings.R
import com.android.settings.network.SimOnboardingService
+import com.android.settings.sim.SimDialogActivity
import com.android.settingslib.spa.framework.theme.SettingsDimension
import com.android.settingslib.spa.widget.preference.CheckboxPreference
import com.android.settingslib.spa.widget.preference.CheckboxPreferenceModel
-
import com.android.settingslib.spa.widget.scaffold.BottomAppBarButton
import com.android.settingslib.spa.widget.scaffold.SuwScaffold
import com.android.settingslib.spa.widget.ui.SettingsBody
@@ -68,22 +67,38 @@
Column(Modifier.padding(SettingsDimension.itemPadding)) {
SettingsBody(stringResource(id = R.string.sim_onboarding_select_sim_msg))
}
- for (subInfo in onboardingService.getSelectableSubscriptionInfo()) {
+ var isFinished = rememberSaveable { mutableStateOf(false) }
+ isFinished.value = onboardingService.isSimSelectionFinished
+ for (subInfo in onboardingService.getSelectableSubscriptionInfoList()) {
var title = onboardingService.getSubscriptionInfoDisplayName(subInfo)
var summaryNumber =
subInfo.number // TODO using the SubscriptionUtil.getFormattedPhoneNumber
- var changeable = subInfo.isActive
- var checked by rememberSaveable { mutableStateOf(!subInfo.isActive) }
+ var checked = rememberSaveable {
+ mutableStateOf(
+ onboardingService.getSelectedSubscriptionInfoList().contains(subInfo)
+ )
+ }
CheckboxPreference(remember {
object : CheckboxPreferenceModel {
override val title = title
override val summary: () -> String
get() = { summaryNumber }
- override val checked = { checked }
- override val changeable = { changeable }
- override val onCheckedChange = { newChecked: Boolean -> checked = newChecked }
+ override val checked = { checked.value }
+ override val onCheckedChange = { newChecked: Boolean ->
+ checked.value = newChecked
+ if (newChecked) {
+ onboardingService.addItemForSelectedSim(subInfo)
+ } else {
+ onboardingService.removeItemForSelectedSim(subInfo)
+ }
+ isFinished.value = onboardingService.isSimSelectionFinished
+ }
+ override val changeable = {
+ subInfo.isActive
+ && (!isFinished.value || (isFinished.value && checked.value))
+ }
}
})
}
-}
\ No newline at end of file
+}
diff --git a/tests/spa_unit/src/com/android/settings/spa/network/SimOnboardingLabelSimTest.kt b/tests/spa_unit/src/com/android/settings/spa/network/SimOnboardingLabelSimTest.kt
index dace5e9..8e12b20 100644
--- a/tests/spa_unit/src/com/android/settings/spa/network/SimOnboardingLabelSimTest.kt
+++ b/tests/spa_unit/src/com/android/settings/spa/network/SimOnboardingLabelSimTest.kt
@@ -108,7 +108,7 @@
on { targetSubInfo }.doReturn(SUB_INFO_1)
on { availableSubInfoList }.doReturn(listOf(SUB_INFO_1, SUB_INFO_2, SUB_INFO_3))
on { activeSubInfoList }.doReturn(listOf(SUB_INFO_2, SUB_INFO_3))
- on { getSelectableSubscriptionInfo() }.doReturn(
+ on { getSelectableSubscriptionInfoList() }.doReturn(
listOf(
SUB_INFO_1,
SUB_INFO_2,
@@ -139,7 +139,7 @@
on { targetSubInfo }.doReturn(SUB_INFO_1)
on { availableSubInfoList }.doReturn(listOf(SUB_INFO_1, SUB_INFO_2, SUB_INFO_3))
on { activeSubInfoList }.doReturn(listOf(SUB_INFO_2, SUB_INFO_3))
- on { getSelectableSubscriptionInfo() }.doReturn(
+ on { getSelectableSubscriptionInfoList() }.doReturn(
listOf(
SUB_INFO_1,
SUB_INFO_2,
diff --git a/tests/spa_unit/src/com/android/settings/spa/network/SimOnboardingPageProviderTest.kt b/tests/spa_unit/src/com/android/settings/spa/network/SimOnboardingPageProviderTest.kt
index 35f1968..82dba76 100644
--- a/tests/spa_unit/src/com/android/settings/spa/network/SimOnboardingPageProviderTest.kt
+++ b/tests/spa_unit/src/com/android/settings/spa/network/SimOnboardingPageProviderTest.kt
@@ -91,6 +91,7 @@
fun simOnboardingPage_nextAction_fromLabelSimToSelectSim() {
mockSimOnboardingService.stub {
on { isMultipleEnabledProfilesSupported }.thenReturn(true)
+ on { isAllOfSlotAssigned }.thenReturn(true)
}
composeTestRule.setContent {
diff --git a/tests/spa_unit/src/com/android/settings/spa/network/SimOnboardingSelectSimTest.kt b/tests/spa_unit/src/com/android/settings/spa/network/SimOnboardingSelectSimTest.kt
index 5d7465f..45667ef 100644
--- a/tests/spa_unit/src/com/android/settings/spa/network/SimOnboardingSelectSimTest.kt
+++ b/tests/spa_unit/src/com/android/settings/spa/network/SimOnboardingSelectSimTest.kt
@@ -108,7 +108,7 @@
on { targetSubInfo }.doReturn(SUB_INFO_1)
on { availableSubInfoList }.doReturn(listOf(SUB_INFO_1, SUB_INFO_2, SUB_INFO_3))
on { activeSubInfoList }.doReturn(listOf(SUB_INFO_2, SUB_INFO_3))
- on { getSelectableSubscriptionInfo() }.doReturn(
+ on { getSelectableSubscriptionInfoList() }.doReturn(
listOf(
SUB_INFO_1,
SUB_INFO_2,