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
This commit is contained in:
@@ -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"
|
||||
|
430
src/com/android/settings/network/SimOnboardingActivity.kt
Normal file
430
src/com/android/settings/network/SimOnboardingActivity.kt
Normal file
@@ -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
|
||||
}
|
||||
}
|
@@ -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<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 @@ 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<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 @@ class SimOnboardingService {
|
||||
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)
|
||||
}
|
||||
}
|
||||
}
|
@@ -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));
|
||||
|
@@ -463,15 +463,25 @@ public class UiccSlotUtil {
|
||||
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);
|
||||
Log.i(TAG, "isRemovableSimEnabled: " + isRemovableSimEnabled);
|
||||
|
@@ -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);
|
||||
|
@@ -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<SubscriptionInfo>,
|
||||
selectableSubscriptionInfoList
|
||||
)
|
||||
PrimarySimSectionImpl(
|
||||
subscriptionManager,
|
||||
activeSubscriptionInfoList,
|
||||
defaultVoiceSubId,
|
||||
defaultSmsSubId,
|
||||
@@ -257,12 +259,49 @@ fun SimsSectionImpl(
|
||||
|
||||
@Composable
|
||||
fun PrimarySimSectionImpl(
|
||||
subscriptionManager: SubscriptionManager?,
|
||||
activeSubscriptionInfoList: List<SubscriptionInfo>,
|
||||
subscriptionInfoList: List<SubscriptionInfo>,
|
||||
callsSelectedId: MutableIntState,
|
||||
textsSelectedId: MutableIntState,
|
||||
mobileDataSelectedId: MutableIntState,
|
||||
nonDds: 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,24 +371,17 @@ 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 = {
|
||||
if (nonDds.intValue != SubscriptionManager.INVALID_SUBSCRIPTION_ID) {
|
||||
coroutineScope.launch {
|
||||
withContext(Dispatchers.Default) {
|
||||
automaticDataChecked.value = telephonyManagerForNonDds != null
|
||||
&& telephonyManagerForNonDds.isMobileDataPolicyEnabled(
|
||||
TelephonyManager.MOBILE_DATA_POLICY_AUTO_DATA_SWITCH)
|
||||
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)
|
||||
}
|
||||
}
|
||||
|
||||
private suspend fun setAutomaticData(telephonyManager: TelephonyManager?, newState: Boolean): Unit =
|
||||
suspend fun getAutomaticData(telephonyManagerForNonDds: TelephonyManager?): Boolean =
|
||||
withContext(Dispatchers.Default) {
|
||||
telephonyManagerForNonDds != null
|
||||
&& telephonyManagerForNonDds.isMobileDataPolicyEnabled(
|
||||
TelephonyManager.MOBILE_DATA_POLICY_AUTO_DATA_SWITCH)
|
||||
}
|
||||
|
||||
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)
|
||||
newState
|
||||
)
|
||||
//TODO: setup backup calling
|
||||
}
|
@@ -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)
|
||||
|
@@ -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
|
||||
)
|
||||
}
|
||||
|
@@ -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)
|
||||
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)
|
||||
}
|
||||
}
|
||||
|
||||
@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 }
|
||||
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},
|
||||
)
|
||||
|
||||
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 }
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
@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 @@ fun createPrimarySimListPreference(
|
||||
override val icon = @Composable {
|
||||
SettingsIcon(icon)
|
||||
}
|
||||
override val enabled: () -> Boolean
|
||||
get() = { enable }
|
||||
}
|
||||
})
|
@@ -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,21 +67,37 @@ 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))
|
||||
}
|
||||
}
|
||||
})
|
||||
}
|
||||
|
@@ -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,
|
||||
|
@@ -91,6 +91,7 @@ class SimOnboardingPageProviderTest {
|
||||
fun simOnboardingPage_nextAction_fromLabelSimToSelectSim() {
|
||||
mockSimOnboardingService.stub {
|
||||
on { isMultipleEnabledProfilesSupported }.thenReturn(true)
|
||||
on { isAllOfSlotAssigned }.thenReturn(true)
|
||||
}
|
||||
|
||||
composeTestRule.setContent {
|
||||
|
@@ -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,
|
||||
|
Reference in New Issue
Block a user