From f1ea4844258a61b0a939c5cfe967300db00aa8dc Mon Sep 17 00:00:00 2001 From: songferngwang Date: Mon, 5 Feb 2024 23:46:13 +0000 Subject: [PATCH] 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 --- AndroidManifest.xml | 5 + .../settings/network/SimOnboardingActivity.kt | 430 ++++++++++++++++++ .../settings/network/SimOnboardingService.kt | 189 +++++++- .../settings/network/SubscriptionUtil.java | 5 +- .../settings/network/UiccSlotUtil.java | 18 +- .../settings/sim/SimDialogActivity.java | 12 + .../network/NetworkCellularGroupProvider.kt | 148 +++--- .../spa/network/SimOnboardingLabelSim.kt | 4 +- .../spa/network/SimOnboardingPageProvider.kt | 29 +- .../spa/network/SimOnboardingPrimarySim.kt | 121 ++--- .../spa/network/SimOnboardingSelectSim.kt | 35 +- .../spa/network/SimOnboardingLabelSimTest.kt | 4 +- .../network/SimOnboardingPageProviderTest.kt | 1 + .../spa/network/SimOnboardingSelectSimTest.kt | 2 +- 14 files changed, 810 insertions(+), 193 deletions(-) create mode 100644 src/com/android/settings/network/SimOnboardingActivity.kt diff --git a/AndroidManifest.xml b/AndroidManifest.xml index 195c44ecd23..720b3587cc7 100644 --- a/AndroidManifest.xml +++ b/AndroidManifest.xml @@ -802,6 +802,11 @@ + + + lateinit var showError: MutableState + lateinit var showDialog: MutableState + + 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 = 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 1b3994e476e..86793851f71 100644 --- a/src/com/android/settings/network/SimOnboardingService.kt +++ b/src/com/android/settings/network/SimOnboardingService.kt @@ -23,11 +23,17 @@ import android.telephony.TelephonyManager 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 @@ class SimOnboardingService { var slotInfoList: List = listOf() var uiccCardInfoList: List = listOf() var selectedSubInfoList: MutableList = 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 = mutableMapOf() + var userSelectedSubInfoList : MutableList = 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 @@ class SimOnboardingService { 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 @@ class SimOnboardingService { 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 { + /** + * Return the subscriptionInfo list which has + * the target subscriptionInfo + active subscriptionInfo. + */ + fun getSelectableSubscriptionInfoList(): List { var list: MutableList = mutableListOf() list.addAll(activeSubInfoList) if (!list.contains(targetSubInfo)) { @@ -109,18 +190,102 @@ class SimOnboardingService { return list.toList() } + /** + * Return the user selected SubscriptionInfo list. + */ + fun getSelectedSubscriptionInfoList(): List { + 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 b6b433b7955..84e4e7543fd 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.SelectableSubscriptions; 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 @@ public class SubscriptionUtil { 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 5175c233cb5..df23f122e83 100644 --- a/src/com/android/settings/network/UiccSlotUtil.java +++ b/src/com/android/settings/network/UiccSlotUtil.java @@ -463,17 +463,27 @@ public class UiccSlotUtil { if (telMgr == null) { return false; } - ImmutableList slotInfos = UiccSlotUtil.getSlotInfos(telMgr); + List 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 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 d65b2d1f69c..4544e73b87d 100644 --- a/src/com/android/settings/sim/SimDialogActivity.java +++ b/src/com/android/settings/sim/SimDialogActivity.java @@ -41,6 +41,7 @@ import androidx.fragment.app.FragmentManager; 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 @@ public class SimDialogActivity extends FragmentActivity { 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 e746d4a79a0..b6d83f29976 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.framework.common.broadcastReceiverF 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 @@ object NetworkCellularGroupProvider : SettingsPageProvider { .map { it.subscriptionId } .firstOrNull() ?: SubscriptionManager.INVALID_SUBSCRIPTION_ID } + + Log.d(name, "defaultDataSubId: $defaultDataSubId, nonDds: $nonDds") } } @@ -195,7 +198,6 @@ fun PageImpl(selectableSubscriptionInfoList: List, selectableSubscriptionInfoList ) PrimarySimSectionImpl( - subscriptionManager, activeSubscriptionInfoList, defaultVoiceSubId, defaultSmsSubId, @@ -257,12 +259,49 @@ fun SimsSectionImpl( @Composable fun PrimarySimSectionImpl( - subscriptionManager: SubscriptionManager?, - activeSubscriptionInfoList: List, - callsSelectedId: MutableIntState, - textsSelectedId: MutableIntState, - mobileDataSelectedId: MutableIntState, - nonDds: MutableIntState + subscriptionInfoList: List, + 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 @@ fun PrimarySimSectionImpl( 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 @@ fun PrimarySimSectionImpl( } 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 @@ fun PrimarySimSectionImpl( } 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 @@ fun PrimarySimSectionImpl( 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 @@ private fun showEuiccSettings(context: Context): Boolean { 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 @@ private suspend fun setDefaultData(context: Context, 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 4cb04b69eac..e88c5c71347 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.mutableStateOf 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 @@ private fun labelSimBody(onboardingService: SimOnboardingService) { 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 e46dc2e6cc6..c60ac883637 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.app.Activity 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.composable 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 @@ object SimOnboardingPageProvider : SettingsPageProvider { 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 @@ object SimOnboardingPageProvider : SettingsPageProvider { @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 @@ private fun Context.getActivity(): Activity? = when (this) { @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 @@ fun PageImpl(onboardingService:SimOnboardingService,navHostController: NavHostCo ) { 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 5752a4f4f02..999abb486bd 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 @@ fun SimOnboardingPrimarySimImpl( 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, selectedId: MutableIntState, icon: ImageVector, - enable: Boolean = true, onIdSelected: (id: Int) -> Unit ) = ListPreference(remember { object : ListPreferenceModel { @@ -156,7 +119,5 @@ fun createPrimarySimListPreference( 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 1955d1335e4..5e71b12ed79 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 @@ private fun selectSimBody(onboardingService: SimOnboardingService) { 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 dace5e9acb2..8e12b20cd98 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 @@ class SimOnboardingLabelSimTest { 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 @@ class SimOnboardingLabelSimTest { 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 35f19682393..82dba76c7b7 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 @@ class SimOnboardingPageProviderTest { 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 5d7465f4d50..45667ef00ef 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 @@ class SimOnboardingSelectSimTest { 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,