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>
|
</intent-filter>
|
||||||
</activity>
|
</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"
|
<activity android:name=".network.telephony.ToggleSubscriptionDialogActivity"
|
||||||
android:exported="false"
|
android:exported="false"
|
||||||
android:permission="android.permission.WRITE_EMBEDDED_SUBSCRIPTIONS"
|
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.UiccCardInfo
|
||||||
import android.telephony.UiccSlotInfo
|
import android.telephony.UiccSlotInfo
|
||||||
import android.util.Log
|
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 com.android.settingslib.utils.ThreadUtils
|
||||||
|
import kotlinx.coroutines.Dispatchers
|
||||||
|
import kotlinx.coroutines.withContext
|
||||||
|
|
||||||
|
|
||||||
private const val TAG = "SimOnboardingService"
|
private const val TAG = "SimOnboardingService"
|
||||||
private const val INVALID = -1
|
private const val INVALID = SubscriptionManager.INVALID_SUBSCRIPTION_ID
|
||||||
|
|
||||||
class SimOnboardingService {
|
class SimOnboardingService {
|
||||||
var subscriptionManager:SubscriptionManager? = null
|
var subscriptionManager:SubscriptionManager? = null
|
||||||
@@ -40,19 +46,72 @@ class SimOnboardingService {
|
|||||||
var slotInfoList: List<UiccSlotInfo> = listOf()
|
var slotInfoList: List<UiccSlotInfo> = listOf()
|
||||||
var uiccCardInfoList: List<UiccCardInfo> = listOf()
|
var uiccCardInfoList: List<UiccCardInfo> = listOf()
|
||||||
var selectedSubInfoList: MutableList<SubscriptionInfo> = mutableListOf()
|
var selectedSubInfoList: MutableList<SubscriptionInfo> = mutableListOf()
|
||||||
var targetPrimarySimCalls: Int = -1
|
var targetPrimarySimCalls: Int = SubscriptionManager.INVALID_SUBSCRIPTION_ID
|
||||||
var targetPrimarySimTexts: Int = -1
|
var targetPrimarySimTexts: Int = SubscriptionManager.INVALID_SUBSCRIPTION_ID
|
||||||
var targetPrimarySimMobileData: Int = -1
|
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
|
var isMultipleEnabledProfilesSupported: Boolean = false
|
||||||
get() {
|
get() {
|
||||||
if (uiccCardInfoList.isEmpty()) {
|
if (uiccCardInfoList.isEmpty()) {
|
||||||
Log.w(TAG, "UICC cards info list is empty.")
|
Log.w(TAG, "UICC cards info list is empty.")
|
||||||
return false
|
return false
|
||||||
}
|
}
|
||||||
return uiccCardInfoList.stream()
|
return uiccCardInfoList.any { it.isMultipleEnabledProfilesSupported }
|
||||||
.anyMatch { cardInfo: UiccCardInfo -> cardInfo.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 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 {
|
fun isValid(): Boolean {
|
||||||
return targetSubId != INVALID
|
return targetSubId != INVALID
|
||||||
@@ -73,18 +132,27 @@ class SimOnboardingService {
|
|||||||
targetPrimarySimCalls = -1
|
targetPrimarySimCalls = -1
|
||||||
targetPrimarySimTexts = -1
|
targetPrimarySimTexts = -1
|
||||||
targetPrimarySimMobileData = -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
|
targetSubId = inputTargetSubId
|
||||||
subscriptionManager = context.getSystemService(SubscriptionManager::class.java)
|
subscriptionManager = context.getSystemService(SubscriptionManager::class.java)
|
||||||
telephonyManager = context.getSystemService(TelephonyManager::class.java)
|
telephonyManager = context.getSystemService(TelephonyManager::class.java)
|
||||||
|
Log.d(
|
||||||
|
TAG, "startInit: targetSubId:$targetSubId"
|
||||||
|
)
|
||||||
ThreadUtils.postOnBackgroundThread {
|
ThreadUtils.postOnBackgroundThread {
|
||||||
activeSubInfoList = SubscriptionUtil.getActiveSubscriptions(subscriptionManager)
|
activeSubInfoList = SubscriptionUtil.getActiveSubscriptions(subscriptionManager)
|
||||||
availableSubInfoList = SubscriptionUtil.getAvailableSubscriptions(context)
|
availableSubInfoList = SubscriptionUtil.getAvailableSubscriptions(context)
|
||||||
targetSubInfo = availableSubInfoList.find { subInfo -> subInfo.subscriptionId == targetSubId }
|
targetSubInfo = availableSubInfoList.find { subInfo -> subInfo.subscriptionId == targetSubId }
|
||||||
|
targetSubInfo?.let { userSelectedSubInfoList.add(it) }
|
||||||
Log.d(
|
Log.d(
|
||||||
TAG, "targetSubId: $targetSubId" + ", targetSubInfo: $targetSubInfo" +
|
TAG, "targetSubId: $targetSubId" + ", targetSubInfo: $targetSubInfo" +
|
||||||
". activeSubInfoList: $activeSubInfoList"
|
". activeSubInfoList: $activeSubInfoList"
|
||||||
@@ -94,11 +162,24 @@ class SimOnboardingService {
|
|||||||
uiccCardInfoList = telephonyManager?.uiccCardsInfo!!
|
uiccCardInfoList = telephonyManager?.uiccCardsInfo!!
|
||||||
Log.d(TAG, "uiccCardInfoList: $uiccCardInfoList")
|
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()
|
var list: MutableList<SubscriptionInfo> = mutableListOf()
|
||||||
list.addAll(activeSubInfoList)
|
list.addAll(activeSubInfoList)
|
||||||
if (!list.contains(targetSubInfo)) {
|
if (!list.contains(targetSubInfo)) {
|
||||||
@@ -109,18 +190,102 @@ class SimOnboardingService {
|
|||||||
return list.toList()
|
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) {
|
fun addItemForRenaming(subInfo: SubscriptionInfo, newName: String) {
|
||||||
if (subInfo.displayName == newName) {
|
if (subInfo.displayName == newName) {
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
renameMutableMap[subInfo.subscriptionId] = newName
|
renameMutableMap[subInfo.subscriptionId] = newName
|
||||||
|
Log.d(TAG, "renameMutableMap add ${subInfo.subscriptionId} & $newName into: $renameMutableMap")
|
||||||
}
|
}
|
||||||
|
|
||||||
fun getSubscriptionInfoDisplayName(subInfo: SubscriptionInfo): String {
|
fun getSubscriptionInfoDisplayName(subInfo: SubscriptionInfo): String {
|
||||||
return renameMutableMap[subInfo.subscriptionId] ?: subInfo.displayName.toString()
|
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
|
// 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.helper.SubscriptionAnnotation;
|
||||||
import com.android.settings.network.telephony.DeleteEuiccSubscriptionDialogActivity;
|
import com.android.settings.network.telephony.DeleteEuiccSubscriptionDialogActivity;
|
||||||
import com.android.settings.network.telephony.ToggleSubscriptionDialogActivity;
|
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.ArrayList;
|
||||||
import java.util.Collections;
|
import java.util.Collections;
|
||||||
@@ -546,8 +544,7 @@ public class SubscriptionUtil {
|
|||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
if (enable && Flags.isDualSimOnboardingEnabled()) {
|
if (enable && Flags.isDualSimOnboardingEnabled()) {
|
||||||
String route = SimOnboardingPageProvider.INSTANCE.getRoute(subId);
|
SimOnboardingActivity.startSimOnboardingActivity(context, subId);
|
||||||
SpaActivity.startSpaActivity(context, route);
|
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
context.startActivity(ToggleSubscriptionDialogActivity.getIntent(context, subId, enable));
|
context.startActivity(ToggleSubscriptionDialogActivity.getIntent(context, subId, enable));
|
||||||
|
@@ -463,15 +463,25 @@ public class UiccSlotUtil {
|
|||||||
if (telMgr == null) {
|
if (telMgr == null) {
|
||||||
return false;
|
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 =
|
boolean isRemovableSimEnabled =
|
||||||
slotInfos.stream()
|
slotInfos.stream()
|
||||||
.anyMatch(
|
.anyMatch(
|
||||||
slot -> slot != null
|
slot -> slot != null
|
||||||
&& slot.isRemovable()
|
&& slot.isRemovable()
|
||||||
&& !slot.getIsEuicc()
|
&& !slot.getIsEuicc()
|
||||||
&& slot.getPorts().stream().anyMatch(
|
&& slot.getPorts().stream()
|
||||||
port -> port.isActive())
|
.anyMatch(port -> port.isActive())
|
||||||
&& slot.getCardStateInfo()
|
&& slot.getCardStateInfo()
|
||||||
== UiccSlotInfo.CARD_STATE_INFO_PRESENT);
|
== UiccSlotInfo.CARD_STATE_INFO_PRESENT);
|
||||||
Log.i(TAG, "isRemovableSimEnabled: " + isRemovableSimEnabled);
|
Log.i(TAG, "isRemovableSimEnabled: " + isRemovableSimEnabled);
|
||||||
|
@@ -41,6 +41,7 @@ import androidx.fragment.app.FragmentManager;
|
|||||||
|
|
||||||
import com.android.internal.annotations.VisibleForTesting;
|
import com.android.internal.annotations.VisibleForTesting;
|
||||||
import com.android.settings.R;
|
import com.android.settings.R;
|
||||||
|
import com.android.settings.flags.Flags;
|
||||||
import com.android.settings.network.CarrierConfigCache;
|
import com.android.settings.network.CarrierConfigCache;
|
||||||
import com.android.settings.network.SubscriptionUtil;
|
import com.android.settings.network.SubscriptionUtil;
|
||||||
import com.android.settings.network.ims.WifiCallingQueryImsState;
|
import com.android.settings.network.ims.WifiCallingQueryImsState;
|
||||||
@@ -134,6 +135,17 @@ public class SimDialogActivity extends FragmentActivity {
|
|||||||
return;
|
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 String tag = Integer.toString(dialogType);
|
||||||
final FragmentManager fragmentManager = getSupportFragmentManager();
|
final FragmentManager fragmentManager = getSupportFragmentManager();
|
||||||
SimDialogFragment fragment = (SimDialogFragment) fragmentManager.findFragmentByTag(tag);
|
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.model.enterprise.Restrictions
|
||||||
import com.android.settingslib.spaprivileged.template.preference.RestrictedPreference
|
import com.android.settingslib.spaprivileged.template.preference.RestrictedPreference
|
||||||
|
import kotlinx.coroutines.CoroutineScope
|
||||||
import kotlinx.coroutines.Dispatchers
|
import kotlinx.coroutines.Dispatchers
|
||||||
import kotlinx.coroutines.flow.Flow
|
import kotlinx.coroutines.flow.Flow
|
||||||
import kotlinx.coroutines.flow.combine
|
import kotlinx.coroutines.flow.combine
|
||||||
@@ -171,6 +172,8 @@ object NetworkCellularGroupProvider : SettingsPageProvider {
|
|||||||
.map { it.subscriptionId }
|
.map { it.subscriptionId }
|
||||||
.firstOrNull() ?: SubscriptionManager.INVALID_SUBSCRIPTION_ID
|
.firstOrNull() ?: SubscriptionManager.INVALID_SUBSCRIPTION_ID
|
||||||
}
|
}
|
||||||
|
|
||||||
|
Log.d(name, "defaultDataSubId: $defaultDataSubId, nonDds: $nonDds")
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -195,7 +198,6 @@ fun PageImpl(selectableSubscriptionInfoList: List<SubscriptionInfo>,
|
|||||||
selectableSubscriptionInfoList
|
selectableSubscriptionInfoList
|
||||||
)
|
)
|
||||||
PrimarySimSectionImpl(
|
PrimarySimSectionImpl(
|
||||||
subscriptionManager,
|
|
||||||
activeSubscriptionInfoList,
|
activeSubscriptionInfoList,
|
||||||
defaultVoiceSubId,
|
defaultVoiceSubId,
|
||||||
defaultSmsSubId,
|
defaultSmsSubId,
|
||||||
@@ -257,12 +259,49 @@ fun SimsSectionImpl(
|
|||||||
|
|
||||||
@Composable
|
@Composable
|
||||||
fun PrimarySimSectionImpl(
|
fun PrimarySimSectionImpl(
|
||||||
subscriptionManager: SubscriptionManager?,
|
subscriptionInfoList: List<SubscriptionInfo>,
|
||||||
activeSubscriptionInfoList: List<SubscriptionInfo>,
|
|
||||||
callsSelectedId: MutableIntState,
|
callsSelectedId: MutableIntState,
|
||||||
textsSelectedId: MutableIntState,
|
textsSelectedId: MutableIntState,
|
||||||
mobileDataSelectedId: 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 state = rememberSaveable { mutableStateOf(false) }
|
||||||
var callsAndSmsList = remember {
|
var callsAndSmsList = remember {
|
||||||
@@ -272,11 +311,11 @@ fun PrimarySimSectionImpl(
|
|||||||
mutableListOf(ListPreferenceOption(id = -1, text = "Loading"))
|
mutableListOf(ListPreferenceOption(id = -1, text = "Loading"))
|
||||||
}
|
}
|
||||||
|
|
||||||
if (activeSubscriptionInfoList.size >= 2) {
|
if (subscriptionInfoList.size >= 2) {
|
||||||
state.value = true
|
state.value = true
|
||||||
callsAndSmsList.clear()
|
callsAndSmsList.clear()
|
||||||
dataList.clear()
|
dataList.clear()
|
||||||
for (info in activeSubscriptionInfoList) {
|
for (info in subscriptionInfoList) {
|
||||||
var item = ListPreferenceOption(
|
var item = ListPreferenceOption(
|
||||||
id = info.subscriptionId,
|
id = info.subscriptionId,
|
||||||
text = "${info.displayName}"
|
text = "${info.displayName}"
|
||||||
@@ -291,12 +330,10 @@ fun PrimarySimSectionImpl(
|
|||||||
} else {
|
} else {
|
||||||
// hide the primary sim
|
// hide the primary sim
|
||||||
state.value = false
|
state.value = false
|
||||||
Log.d("NetworkCellularGroupProvider", "Hide primary sim")
|
Log.d(NetworkCellularGroupProvider.name, "Hide primary sim")
|
||||||
}
|
}
|
||||||
|
|
||||||
if (state.value) {
|
if (state.value) {
|
||||||
val coroutineScope = rememberCoroutineScope()
|
|
||||||
var context = LocalContext.current
|
|
||||||
val telephonyManagerForNonDds: TelephonyManager? =
|
val telephonyManagerForNonDds: TelephonyManager? =
|
||||||
context.getSystemService(TelephonyManager::class.java)
|
context.getSystemService(TelephonyManager::class.java)
|
||||||
?.createForSubscriptionId(nonDds.intValue)
|
?.createForSubscriptionId(nonDds.intValue)
|
||||||
@@ -305,44 +342,27 @@ fun PrimarySimSectionImpl(
|
|||||||
}
|
}
|
||||||
|
|
||||||
Category(title = stringResource(id = R.string.primary_sim_title)) {
|
Category(title = stringResource(id = R.string.primary_sim_title)) {
|
||||||
createPrimarySimListPreference(
|
CreatePrimarySimListPreference(
|
||||||
stringResource(id = R.string.primary_sim_calls_title),
|
stringResource(id = R.string.primary_sim_calls_title),
|
||||||
callsAndSmsList,
|
callsAndSmsList,
|
||||||
callsSelectedId,
|
callsSelectedId,
|
||||||
ImageVector.vectorResource(R.drawable.ic_phone),
|
ImageVector.vectorResource(R.drawable.ic_phone),
|
||||||
) {
|
actionSetCalls
|
||||||
callsSelectedId.intValue = it
|
)
|
||||||
coroutineScope.launch {
|
CreatePrimarySimListPreference(
|
||||||
setDefaultVoice(subscriptionManager, it)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
createPrimarySimListPreference(
|
|
||||||
stringResource(id = R.string.primary_sim_texts_title),
|
stringResource(id = R.string.primary_sim_texts_title),
|
||||||
callsAndSmsList,
|
callsAndSmsList,
|
||||||
textsSelectedId,
|
textsSelectedId,
|
||||||
Icons.AutoMirrored.Outlined.Message,
|
Icons.AutoMirrored.Outlined.Message,
|
||||||
) {
|
actionSetTexts
|
||||||
textsSelectedId.intValue = it
|
)
|
||||||
coroutineScope.launch {
|
CreatePrimarySimListPreference(
|
||||||
setDefaultSms(subscriptionManager, it)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
createPrimarySimListPreference(
|
|
||||||
stringResource(id = R.string.mobile_data_settings_title),
|
stringResource(id = R.string.mobile_data_settings_title),
|
||||||
dataList,
|
dataList,
|
||||||
mobileDataSelectedId,
|
mobileDataSelectedId,
|
||||||
Icons.Outlined.DataUsage,
|
Icons.Outlined.DataUsage,
|
||||||
) {
|
actionSetMobileData
|
||||||
mobileDataSelectedId.intValue = it
|
)
|
||||||
coroutineScope.launch {
|
|
||||||
// TODO: to fix the WifiPickerTracker crash when create
|
|
||||||
// the wifiPickerTrackerHelper
|
|
||||||
setDefaultData(context,
|
|
||||||
subscriptionManager,
|
|
||||||
null/*wifiPickerTrackerHelper*/,
|
|
||||||
it)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
val autoDataTitle = stringResource(id = R.string.primary_sim_automatic_data_title)
|
val autoDataTitle = stringResource(id = R.string.primary_sim_automatic_data_title)
|
||||||
@@ -351,24 +371,17 @@ fun PrimarySimSectionImpl(
|
|||||||
object : SwitchPreferenceModel {
|
object : SwitchPreferenceModel {
|
||||||
override val title = autoDataTitle
|
override val title = autoDataTitle
|
||||||
override val summary = { autoDataSummary }
|
override val summary = { autoDataSummary }
|
||||||
override val changeable: () -> Boolean = {
|
|
||||||
nonDds.intValue != SubscriptionManager.INVALID_SUBSCRIPTION_ID
|
|
||||||
}
|
|
||||||
override val checked = {
|
override val checked = {
|
||||||
|
if (nonDds.intValue != SubscriptionManager.INVALID_SUBSCRIPTION_ID) {
|
||||||
coroutineScope.launch {
|
coroutineScope.launch {
|
||||||
withContext(Dispatchers.Default) {
|
automaticDataChecked.value = getAutomaticData(telephonyManagerForNonDds)
|
||||||
automaticDataChecked.value = telephonyManagerForNonDds != null
|
|
||||||
&& telephonyManagerForNonDds.isMobileDataPolicyEnabled(
|
|
||||||
TelephonyManager.MOBILE_DATA_POLICY_AUTO_DATA_SWITCH)
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
automaticDataChecked.value
|
automaticDataChecked.value
|
||||||
}
|
}
|
||||||
override val onCheckedChange: ((Boolean) -> Unit)? =
|
override val onCheckedChange: ((Boolean) -> Unit)? = {
|
||||||
{ newChecked: Boolean ->
|
automaticDataChecked.value = it
|
||||||
coroutineScope.launch {
|
actionSetAutoDataSwitch(it)
|
||||||
setAutomaticData(telephonyManagerForNonDds, newChecked)
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
@@ -428,19 +441,19 @@ private fun showEuiccSettings(context: Context): Boolean {
|
|||||||
return MobileNetworkUtils.showEuiccSettings(context)
|
return MobileNetworkUtils.showEuiccSettings(context)
|
||||||
}
|
}
|
||||||
|
|
||||||
private suspend fun setDefaultVoice(
|
suspend fun setDefaultVoice(
|
||||||
subscriptionManager: SubscriptionManager?,
|
subscriptionManager: SubscriptionManager?,
|
||||||
subId: Int): Unit = withContext(Dispatchers.Default) {
|
subId: Int): Unit = withContext(Dispatchers.Default) {
|
||||||
subscriptionManager?.setDefaultVoiceSubscriptionId(subId)
|
subscriptionManager?.setDefaultVoiceSubscriptionId(subId)
|
||||||
}
|
}
|
||||||
|
|
||||||
private suspend fun setDefaultSms(
|
suspend fun setDefaultSms(
|
||||||
subscriptionManager: SubscriptionManager?,
|
subscriptionManager: SubscriptionManager?,
|
||||||
subId: Int): Unit = withContext(Dispatchers.Default) {
|
subId: Int): Unit = withContext(Dispatchers.Default) {
|
||||||
subscriptionManager?.setDefaultSmsSubId(subId)
|
subscriptionManager?.setDefaultSmsSubId(subId)
|
||||||
}
|
}
|
||||||
|
|
||||||
private suspend fun setDefaultData(context: Context,
|
suspend fun setDefaultData(context: Context,
|
||||||
subscriptionManager: SubscriptionManager?,
|
subscriptionManager: SubscriptionManager?,
|
||||||
wifiPickerTrackerHelper: WifiPickerTrackerHelper?,
|
wifiPickerTrackerHelper: WifiPickerTrackerHelper?,
|
||||||
subId: Int): Unit = withContext(Dispatchers.Default) {
|
subId: Int): Unit = withContext(Dispatchers.Default) {
|
||||||
@@ -455,11 +468,22 @@ private suspend fun setDefaultData(context: Context,
|
|||||||
wifiPickerTrackerHelper.setCarrierNetworkEnabled(true)
|
wifiPickerTrackerHelper.setCarrierNetworkEnabled(true)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
suspend fun getAutomaticData(telephonyManagerForNonDds: TelephonyManager?): Boolean =
|
||||||
private suspend fun setAutomaticData(telephonyManager: TelephonyManager?, newState: Boolean): Unit =
|
|
||||||
withContext(Dispatchers.Default) {
|
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?.setMobileDataPolicyEnabled(
|
||||||
TelephonyManager.MOBILE_DATA_POLICY_AUTO_DATA_SWITCH,
|
TelephonyManager.MOBILE_DATA_POLICY_AUTO_DATA_SWITCH,
|
||||||
newState)
|
newState
|
||||||
|
)
|
||||||
//TODO: setup backup calling
|
//TODO: setup backup calling
|
||||||
}
|
}
|
@@ -27,11 +27,9 @@ import androidx.compose.runtime.mutableStateOf
|
|||||||
import androidx.compose.runtime.remember
|
import androidx.compose.runtime.remember
|
||||||
import androidx.compose.runtime.setValue
|
import androidx.compose.runtime.setValue
|
||||||
import androidx.compose.ui.Modifier
|
import androidx.compose.ui.Modifier
|
||||||
import androidx.compose.ui.platform.LocalContext
|
|
||||||
import androidx.compose.ui.res.stringResource
|
import androidx.compose.ui.res.stringResource
|
||||||
import com.android.settings.R
|
import com.android.settings.R
|
||||||
import com.android.settings.network.SimOnboardingService
|
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.framework.theme.SettingsDimension
|
||||||
import com.android.settingslib.spa.widget.dialog.AlertDialogButton
|
import com.android.settingslib.spa.widget.dialog.AlertDialogButton
|
||||||
import com.android.settingslib.spa.widget.dialog.rememberAlertDialogPresenter
|
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))
|
SettingsBody(stringResource(R.string.sim_onboarding_label_sim_msg))
|
||||||
}
|
}
|
||||||
|
|
||||||
for (subInfo in onboardingService.getSelectableSubscriptionInfo()) {
|
for (subInfo in onboardingService.getSelectableSubscriptionInfoList()) {
|
||||||
var titleSimName by remember {
|
var titleSimName by remember {
|
||||||
mutableStateOf(
|
mutableStateOf(
|
||||||
onboardingService.getSubscriptionInfoDisplayName(subInfo)
|
onboardingService.getSubscriptionInfoDisplayName(subInfo)
|
||||||
|
@@ -21,6 +21,7 @@ import android.app.Activity
|
|||||||
import android.content.Context
|
import android.content.Context
|
||||||
import android.content.ContextWrapper
|
import android.content.ContextWrapper
|
||||||
import android.os.Bundle
|
import android.os.Bundle
|
||||||
|
import android.util.Log
|
||||||
import androidx.annotation.VisibleForTesting
|
import androidx.annotation.VisibleForTesting
|
||||||
import androidx.compose.runtime.Composable
|
import androidx.compose.runtime.Composable
|
||||||
import androidx.compose.ui.platform.LocalContext
|
import androidx.compose.ui.platform.LocalContext
|
||||||
@@ -31,12 +32,12 @@ import androidx.navigation.compose.composable
|
|||||||
import androidx.navigation.compose.rememberNavController
|
import androidx.navigation.compose.rememberNavController
|
||||||
import androidx.navigation.navArgument
|
import androidx.navigation.navArgument
|
||||||
import com.android.settings.R
|
import com.android.settings.R
|
||||||
|
import com.android.settings.network.SimOnboardingActivity
|
||||||
import com.android.settings.network.SimOnboardingService
|
import com.android.settings.network.SimOnboardingService
|
||||||
import com.android.settingslib.spa.framework.common.SettingsEntryBuilder
|
import com.android.settingslib.spa.framework.common.SettingsEntryBuilder
|
||||||
import com.android.settingslib.spa.framework.common.SettingsPageProvider
|
import com.android.settingslib.spa.framework.common.SettingsPageProvider
|
||||||
import com.android.settingslib.spa.framework.common.createSettingsPage
|
import com.android.settingslib.spa.framework.common.createSettingsPage
|
||||||
import com.android.settingslib.spa.framework.compose.navigator
|
import com.android.settingslib.spa.framework.compose.navigator
|
||||||
|
|
||||||
import com.android.settingslib.spa.widget.preference.Preference
|
import com.android.settingslib.spa.widget.preference.Preference
|
||||||
import com.android.settingslib.spa.widget.preference.PreferenceModel
|
import com.android.settingslib.spa.widget.preference.PreferenceModel
|
||||||
|
|
||||||
@@ -59,7 +60,7 @@ object SimOnboardingPageProvider : SettingsPageProvider {
|
|||||||
|
|
||||||
private val owner = createSettingsPage()
|
private val owner = createSettingsPage()
|
||||||
@VisibleForTesting
|
@VisibleForTesting
|
||||||
var onboardingService: SimOnboardingService = SimOnboardingService()
|
var onboardingService: SimOnboardingService = SimOnboardingActivity.onboardingService
|
||||||
|
|
||||||
fun buildInjectEntry() = SettingsEntryBuilder.createInject(owner = owner)
|
fun buildInjectEntry() = SettingsEntryBuilder.createInject(owner = owner)
|
||||||
.setUiLayoutFn {
|
.setUiLayoutFn {
|
||||||
@@ -72,18 +73,12 @@ object SimOnboardingPageProvider : SettingsPageProvider {
|
|||||||
|
|
||||||
@Composable
|
@Composable
|
||||||
override fun Page(arguments: Bundle?) {
|
override fun Page(arguments: Bundle?) {
|
||||||
initServiceData(arguments!!.getInt(SUB_ID))
|
|
||||||
PageImpl(onboardingService,rememberNavController())
|
PageImpl(onboardingService,rememberNavController())
|
||||||
}
|
}
|
||||||
|
|
||||||
fun getRoute(
|
fun getRoute(
|
||||||
subId: Int
|
subId: Int
|
||||||
): String = "${name}/$subId"
|
): String = "${name}/$subId"
|
||||||
|
|
||||||
@Composable
|
|
||||||
fun initServiceData(targetSubId: Int) {
|
|
||||||
onboardingService.initData(targetSubId, LocalContext.current)
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
private fun Context.getActivity(): Activity? = when (this) {
|
private fun Context.getActivity(): Activity? = when (this) {
|
||||||
@@ -95,7 +90,10 @@ private fun Context.getActivity(): Activity? = when (this) {
|
|||||||
@Composable
|
@Composable
|
||||||
fun PageImpl(onboardingService:SimOnboardingService,navHostController: NavHostController) {
|
fun PageImpl(onboardingService:SimOnboardingService,navHostController: NavHostController) {
|
||||||
val context = LocalContext.current
|
val context = LocalContext.current
|
||||||
var previousPageOfOnboarding: () -> Unit = { context.getActivity()?.finish() }
|
var finishOnboarding: () -> Unit = {
|
||||||
|
context.getActivity()?.finish()
|
||||||
|
onboardingService.callback(SimOnboardingActivity.CALLBACK_FINISH)
|
||||||
|
}
|
||||||
|
|
||||||
NavHost(
|
NavHost(
|
||||||
navController = navHostController,
|
navController = navHostController,
|
||||||
@@ -103,31 +101,32 @@ fun PageImpl(onboardingService:SimOnboardingService,navHostController: NavHostCo
|
|||||||
) {
|
) {
|
||||||
composable(route = SimOnboardingScreen.LabelSim.name) {
|
composable(route = SimOnboardingScreen.LabelSim.name) {
|
||||||
val nextPage =
|
val nextPage =
|
||||||
// Adding more conditions
|
if (onboardingService.isMultipleEnabledProfilesSupported && onboardingService.isAllOfSlotAssigned) {
|
||||||
if (onboardingService.isMultipleEnabledProfilesSupported) {
|
|
||||||
SimOnboardingScreen.SelectSim.name
|
SimOnboardingScreen.SelectSim.name
|
||||||
} else {
|
} else {
|
||||||
|
onboardingService.addCurrentItemForSelectedSim()
|
||||||
SimOnboardingScreen.PrimarySim.name
|
SimOnboardingScreen.PrimarySim.name
|
||||||
}
|
}
|
||||||
SimOnboardingLabelSimImpl(
|
SimOnboardingLabelSimImpl(
|
||||||
nextAction = { navHostController.navigate(nextPage) },
|
nextAction = { navHostController.navigate(nextPage) },
|
||||||
cancelAction = previousPageOfOnboarding,
|
cancelAction = finishOnboarding,
|
||||||
onboardingService = onboardingService
|
onboardingService = onboardingService
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
composable(route = SimOnboardingScreen.PrimarySim.name) {
|
composable(route = SimOnboardingScreen.PrimarySim.name) {
|
||||||
SimOnboardingPrimarySimImpl(
|
SimOnboardingPrimarySimImpl(
|
||||||
nextAction = {
|
nextAction = {
|
||||||
//go back and activate sim
|
onboardingService.callback(SimOnboardingActivity.CALLBACK_ONBOARDING_COMPLETE)
|
||||||
|
context.getActivity()?.finish()
|
||||||
},
|
},
|
||||||
cancelAction = previousPageOfOnboarding,
|
cancelAction = finishOnboarding,
|
||||||
onboardingService = onboardingService
|
onboardingService = onboardingService
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
composable(route = SimOnboardingScreen.SelectSim.name) {
|
composable(route = SimOnboardingScreen.SelectSim.name) {
|
||||||
SimOnboardingSelectSimImpl(
|
SimOnboardingSelectSimImpl(
|
||||||
nextAction = { navHostController.navigate(SimOnboardingScreen.PrimarySim.name) },
|
nextAction = { navHostController.navigate(SimOnboardingScreen.PrimarySim.name) },
|
||||||
cancelAction = previousPageOfOnboarding,
|
cancelAction = finishOnboarding,
|
||||||
onboardingService = onboardingService
|
onboardingService = onboardingService
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
@@ -16,32 +16,25 @@
|
|||||||
|
|
||||||
package com.android.settings.spa.network
|
package com.android.settings.spa.network
|
||||||
|
|
||||||
|
import android.telephony.SubscriptionManager
|
||||||
import androidx.compose.foundation.layout.Column
|
import androidx.compose.foundation.layout.Column
|
||||||
import androidx.compose.foundation.layout.padding
|
import androidx.compose.foundation.layout.padding
|
||||||
import androidx.compose.material.icons.Icons
|
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.material.icons.outlined.SignalCellularAlt
|
||||||
import androidx.compose.runtime.Composable
|
import androidx.compose.runtime.Composable
|
||||||
import androidx.compose.runtime.MutableIntState
|
import androidx.compose.runtime.MutableIntState
|
||||||
import androidx.compose.runtime.getValue
|
|
||||||
import androidx.compose.runtime.mutableIntStateOf
|
import androidx.compose.runtime.mutableIntStateOf
|
||||||
import androidx.compose.runtime.mutableStateOf
|
|
||||||
import androidx.compose.runtime.remember
|
import androidx.compose.runtime.remember
|
||||||
import androidx.compose.runtime.saveable.rememberSaveable
|
import androidx.compose.runtime.saveable.rememberSaveable
|
||||||
import androidx.compose.runtime.setValue
|
|
||||||
import androidx.compose.ui.Modifier
|
import androidx.compose.ui.Modifier
|
||||||
import androidx.compose.ui.graphics.vector.ImageVector
|
import androidx.compose.ui.graphics.vector.ImageVector
|
||||||
import androidx.compose.ui.res.stringResource
|
import androidx.compose.ui.res.stringResource
|
||||||
import androidx.compose.ui.res.vectorResource
|
|
||||||
import com.android.settings.R
|
import com.android.settings.R
|
||||||
import com.android.settings.network.SimOnboardingService
|
import com.android.settings.network.SimOnboardingService
|
||||||
import com.android.settingslib.spa.framework.theme.SettingsDimension
|
import com.android.settingslib.spa.framework.theme.SettingsDimension
|
||||||
import com.android.settingslib.spa.widget.preference.ListPreference
|
import com.android.settingslib.spa.widget.preference.ListPreference
|
||||||
import com.android.settingslib.spa.widget.preference.ListPreferenceModel
|
import com.android.settingslib.spa.widget.preference.ListPreferenceModel
|
||||||
import com.android.settingslib.spa.widget.preference.ListPreferenceOption
|
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.BottomAppBarButton
|
||||||
import com.android.settingslib.spa.widget.scaffold.SuwScaffold
|
import com.android.settingslib.spa.widget.scaffold.SuwScaffold
|
||||||
import com.android.settingslib.spa.widget.ui.SettingsBody
|
import com.android.settingslib.spa.widget.ui.SettingsBody
|
||||||
@@ -68,84 +61,54 @@ fun SimOnboardingPrimarySimImpl(
|
|||||||
cancelAction
|
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)) {
|
Column(Modifier.padding(SettingsDimension.itemPadding)) {
|
||||||
SettingsBody(stringResource(id = R.string.sim_onboarding_primary_sim_msg))
|
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(
|
var selectedSubscriptionInfoList = onboardingService.getSelectedSubscriptionInfoList()
|
||||||
stringResource(id = R.string.mobile_data_settings_title),
|
callsSelectedId.intValue = onboardingService.targetPrimarySimCalls
|
||||||
list,
|
textsSelectedId.intValue = onboardingService.targetPrimarySimTexts
|
||||||
mobileDataSelectedId,
|
mobileDataSelectedId.intValue = onboardingService.targetPrimarySimMobileData
|
||||||
Icons.Outlined.DataUsage,
|
PrimarySimSectionImpl(
|
||||||
onIdSelected = { mobileDataSelectedId.intValue = it }
|
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
|
@Composable
|
||||||
fun createPrimarySimListPreference(
|
fun CreatePrimarySimListPreference(
|
||||||
title: String,
|
title: String,
|
||||||
list: List<ListPreferenceOption>,
|
list: List<ListPreferenceOption>,
|
||||||
selectedId: MutableIntState,
|
selectedId: MutableIntState,
|
||||||
icon: ImageVector,
|
icon: ImageVector,
|
||||||
enable: Boolean = true,
|
|
||||||
onIdSelected: (id: Int) -> Unit
|
onIdSelected: (id: Int) -> Unit
|
||||||
) = ListPreference(remember {
|
) = ListPreference(remember {
|
||||||
object : ListPreferenceModel {
|
object : ListPreferenceModel {
|
||||||
@@ -156,7 +119,5 @@ fun createPrimarySimListPreference(
|
|||||||
override val icon = @Composable {
|
override val icon = @Composable {
|
||||||
SettingsIcon(icon)
|
SettingsIcon(icon)
|
||||||
}
|
}
|
||||||
override val enabled: () -> Boolean
|
|
||||||
get() = { enable }
|
|
||||||
}
|
}
|
||||||
})
|
})
|
@@ -16,24 +16,23 @@
|
|||||||
|
|
||||||
package com.android.settings.spa.network
|
package com.android.settings.spa.network
|
||||||
|
|
||||||
|
import android.util.Log
|
||||||
import androidx.compose.foundation.layout.Column
|
import androidx.compose.foundation.layout.Column
|
||||||
import androidx.compose.foundation.layout.padding
|
import androidx.compose.foundation.layout.padding
|
||||||
import androidx.compose.material.icons.Icons
|
import androidx.compose.material.icons.Icons
|
||||||
import androidx.compose.material.icons.outlined.SignalCellularAlt
|
import androidx.compose.material.icons.outlined.SignalCellularAlt
|
||||||
import androidx.compose.runtime.Composable
|
import androidx.compose.runtime.Composable
|
||||||
import androidx.compose.runtime.getValue
|
|
||||||
import androidx.compose.runtime.mutableStateOf
|
import androidx.compose.runtime.mutableStateOf
|
||||||
import androidx.compose.runtime.remember
|
import androidx.compose.runtime.remember
|
||||||
import androidx.compose.runtime.saveable.rememberSaveable
|
import androidx.compose.runtime.saveable.rememberSaveable
|
||||||
import androidx.compose.runtime.setValue
|
|
||||||
import androidx.compose.ui.Modifier
|
import androidx.compose.ui.Modifier
|
||||||
import androidx.compose.ui.res.stringResource
|
import androidx.compose.ui.res.stringResource
|
||||||
import com.android.settings.R
|
import com.android.settings.R
|
||||||
import com.android.settings.network.SimOnboardingService
|
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.framework.theme.SettingsDimension
|
||||||
import com.android.settingslib.spa.widget.preference.CheckboxPreference
|
import com.android.settingslib.spa.widget.preference.CheckboxPreference
|
||||||
import com.android.settingslib.spa.widget.preference.CheckboxPreferenceModel
|
import com.android.settingslib.spa.widget.preference.CheckboxPreferenceModel
|
||||||
|
|
||||||
import com.android.settingslib.spa.widget.scaffold.BottomAppBarButton
|
import com.android.settingslib.spa.widget.scaffold.BottomAppBarButton
|
||||||
import com.android.settingslib.spa.widget.scaffold.SuwScaffold
|
import com.android.settingslib.spa.widget.scaffold.SuwScaffold
|
||||||
import com.android.settingslib.spa.widget.ui.SettingsBody
|
import com.android.settingslib.spa.widget.ui.SettingsBody
|
||||||
@@ -68,21 +67,37 @@ private fun selectSimBody(onboardingService: SimOnboardingService) {
|
|||||||
Column(Modifier.padding(SettingsDimension.itemPadding)) {
|
Column(Modifier.padding(SettingsDimension.itemPadding)) {
|
||||||
SettingsBody(stringResource(id = R.string.sim_onboarding_select_sim_msg))
|
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 title = onboardingService.getSubscriptionInfoDisplayName(subInfo)
|
||||||
var summaryNumber =
|
var summaryNumber =
|
||||||
subInfo.number // TODO using the SubscriptionUtil.getFormattedPhoneNumber
|
subInfo.number // TODO using the SubscriptionUtil.getFormattedPhoneNumber
|
||||||
var changeable = subInfo.isActive
|
var checked = rememberSaveable {
|
||||||
var checked by rememberSaveable { mutableStateOf(!subInfo.isActive) }
|
mutableStateOf(
|
||||||
|
onboardingService.getSelectedSubscriptionInfoList().contains(subInfo)
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
CheckboxPreference(remember {
|
CheckboxPreference(remember {
|
||||||
object : CheckboxPreferenceModel {
|
object : CheckboxPreferenceModel {
|
||||||
override val title = title
|
override val title = title
|
||||||
override val summary: () -> String
|
override val summary: () -> String
|
||||||
get() = { summaryNumber }
|
get() = { summaryNumber }
|
||||||
override val checked = { checked }
|
override val checked = { checked.value }
|
||||||
override val changeable = { changeable }
|
override val onCheckedChange = { newChecked: Boolean ->
|
||||||
override val onCheckedChange = { newChecked: Boolean -> checked = newChecked }
|
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 { targetSubInfo }.doReturn(SUB_INFO_1)
|
||||||
on { availableSubInfoList }.doReturn(listOf(SUB_INFO_1, SUB_INFO_2, SUB_INFO_3))
|
on { availableSubInfoList }.doReturn(listOf(SUB_INFO_1, SUB_INFO_2, SUB_INFO_3))
|
||||||
on { activeSubInfoList }.doReturn(listOf(SUB_INFO_2, SUB_INFO_3))
|
on { activeSubInfoList }.doReturn(listOf(SUB_INFO_2, SUB_INFO_3))
|
||||||
on { getSelectableSubscriptionInfo() }.doReturn(
|
on { getSelectableSubscriptionInfoList() }.doReturn(
|
||||||
listOf(
|
listOf(
|
||||||
SUB_INFO_1,
|
SUB_INFO_1,
|
||||||
SUB_INFO_2,
|
SUB_INFO_2,
|
||||||
@@ -139,7 +139,7 @@ class SimOnboardingLabelSimTest {
|
|||||||
on { targetSubInfo }.doReturn(SUB_INFO_1)
|
on { targetSubInfo }.doReturn(SUB_INFO_1)
|
||||||
on { availableSubInfoList }.doReturn(listOf(SUB_INFO_1, SUB_INFO_2, SUB_INFO_3))
|
on { availableSubInfoList }.doReturn(listOf(SUB_INFO_1, SUB_INFO_2, SUB_INFO_3))
|
||||||
on { activeSubInfoList }.doReturn(listOf(SUB_INFO_2, SUB_INFO_3))
|
on { activeSubInfoList }.doReturn(listOf(SUB_INFO_2, SUB_INFO_3))
|
||||||
on { getSelectableSubscriptionInfo() }.doReturn(
|
on { getSelectableSubscriptionInfoList() }.doReturn(
|
||||||
listOf(
|
listOf(
|
||||||
SUB_INFO_1,
|
SUB_INFO_1,
|
||||||
SUB_INFO_2,
|
SUB_INFO_2,
|
||||||
|
@@ -91,6 +91,7 @@ class SimOnboardingPageProviderTest {
|
|||||||
fun simOnboardingPage_nextAction_fromLabelSimToSelectSim() {
|
fun simOnboardingPage_nextAction_fromLabelSimToSelectSim() {
|
||||||
mockSimOnboardingService.stub {
|
mockSimOnboardingService.stub {
|
||||||
on { isMultipleEnabledProfilesSupported }.thenReturn(true)
|
on { isMultipleEnabledProfilesSupported }.thenReturn(true)
|
||||||
|
on { isAllOfSlotAssigned }.thenReturn(true)
|
||||||
}
|
}
|
||||||
|
|
||||||
composeTestRule.setContent {
|
composeTestRule.setContent {
|
||||||
|
@@ -108,7 +108,7 @@ class SimOnboardingSelectSimTest {
|
|||||||
on { targetSubInfo }.doReturn(SUB_INFO_1)
|
on { targetSubInfo }.doReturn(SUB_INFO_1)
|
||||||
on { availableSubInfoList }.doReturn(listOf(SUB_INFO_1, SUB_INFO_2, SUB_INFO_3))
|
on { availableSubInfoList }.doReturn(listOf(SUB_INFO_1, SUB_INFO_2, SUB_INFO_3))
|
||||||
on { activeSubInfoList }.doReturn(listOf(SUB_INFO_2, SUB_INFO_3))
|
on { activeSubInfoList }.doReturn(listOf(SUB_INFO_2, SUB_INFO_3))
|
||||||
on { getSelectableSubscriptionInfo() }.doReturn(
|
on { getSelectableSubscriptionInfoList() }.doReturn(
|
||||||
listOf(
|
listOf(
|
||||||
SUB_INFO_1,
|
SUB_INFO_1,
|
||||||
SUB_INFO_2,
|
SUB_INFO_2,
|
||||||
|
Reference in New Issue
Block a user