Add the UI of dual sim onboarding

This is the first version.

Bug: 318310357
Bug: 298898436
Bug: 298891941
Test: Build pass
atest SimOnboardingLabelSimTest
atest SimOnboardingPageProviderTest
atest SimOnboardingPrimarySimTest
atest SimOnboardingSelectSimTest

Change-Id: I195bd6b03ab0e881fdd67272c2476ef2b1fcd3ed
This commit is contained in:
SongFerngWang
2023-12-12 12:08:51 +08:00
committed by SongFerng Wang
parent 4e4b5f0876
commit 4713fd6baa
12 changed files with 1246 additions and 3 deletions

View File

@@ -11526,7 +11526,7 @@
<!-- Button on the dual sim onboarding to go to next page. [CHAR LIMIT=30] --> <!-- Button on the dual sim onboarding to go to next page. [CHAR LIMIT=30] -->
<string name="sim_onboarding_next">Next</string> <string name="sim_onboarding_next">Next</string>
<!-- Text on the progressbar of dual sim onboarding for turning sim on. [CHAR LIMIT=30] --> <!-- Text on the progressbar of dual sim onboarding for turning sim on. [CHAR LIMIT=30] -->
<string name="sim_onboarding_profressbar_turning_sim_on">Turning on <xliff:g id="carrier_name" example="Google Fi">%1$s</xliff:g>&#8230;</string> <string name="sim_onboarding_progressbar_turning_sim_on">Turning on <xliff:g id="carrier_name" example="Google Fi">%1$s</xliff:g>&#8230;</string>
<!-- Title of service provider name(SPN) at mobile network settings page. [CHAR LIMIT=30] --> <!-- Title of service provider name(SPN) at mobile network settings page. [CHAR LIMIT=30] -->
<string name="mobile_network_spn_title">Mobile network</string> <string name="mobile_network_spn_title">Mobile network</string>
<!-- Title of phone number at mobile network settings page. [CHAR LIMIT=30] --> <!-- Title of phone number at mobile network settings page. [CHAR LIMIT=30] -->

View File

@@ -0,0 +1,126 @@
/*
* Copyright (C) 2023 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.content.Context
import android.telephony.SubscriptionInfo
import android.telephony.SubscriptionManager
import android.telephony.TelephonyManager
import android.telephony.UiccCardInfo
import android.telephony.UiccSlotInfo
import android.util.Log
import com.android.settingslib.utils.ThreadUtils
private const val TAG = "SimOnboardingService"
private const val INVALID = -1
class SimOnboardingService {
var subscriptionManager:SubscriptionManager? = null
var telephonyManager:TelephonyManager? = null
var targetSubId: Int = INVALID
var targetSubInfo: SubscriptionInfo? = null
var availableSubInfoList: List<SubscriptionInfo> = listOf()
var activeSubInfoList: List<SubscriptionInfo> = listOf()
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 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 }
}
var renameMutableMap : MutableMap<Int, String> = mutableMapOf()
fun isValid(): Boolean {
return targetSubId != INVALID
&& targetSubInfo != null
&& activeSubInfoList.isNotEmpty()
&& slotInfoList.isNotEmpty()
&& selectedSubInfoList.isNotEmpty()
}
fun clear() {
targetSubId = -1
targetSubInfo = null
availableSubInfoList = listOf()
activeSubInfoList = listOf()
slotInfoList = listOf()
uiccCardInfoList = listOf()
selectedSubInfoList = mutableListOf()
targetPrimarySimCalls = -1
targetPrimarySimTexts = -1
targetPrimarySimMobileData = -1
renameMutableMap.clear()
}
fun initData(inputTargetSubId:Int,context: Context) {
targetSubId = inputTargetSubId
subscriptionManager = context.getSystemService(SubscriptionManager::class.java)
telephonyManager = context.getSystemService(TelephonyManager::class.java)
ThreadUtils.postOnBackgroundThread {
activeSubInfoList = SubscriptionUtil.getActiveSubscriptions(subscriptionManager)
availableSubInfoList = SubscriptionUtil.getAvailableSubscriptions(context)
targetSubInfo = availableSubInfoList.find { subInfo -> subInfo.subscriptionId == targetSubId }
Log.d(
TAG, "targetSubId: $targetSubId" + ", targetSubInfo: $targetSubInfo" +
". activeSubInfoList: $activeSubInfoList"
)
slotInfoList = telephonyManager?.uiccSlotsInfo?.toList() ?: listOf()
Log.d(TAG, "slotInfoList: $slotInfoList.")
uiccCardInfoList = telephonyManager?.uiccCardsInfo!!
Log.d(TAG, "uiccCardInfoList: $uiccCardInfoList")
Log.d(TAG, "isMultipleEnabledProfilesSupported: $isMultipleEnabledProfilesSupported")
}
}
fun getSelectableSubscriptionInfo(): List<SubscriptionInfo> {
var list: MutableList<SubscriptionInfo> = mutableListOf()
list.addAll(activeSubInfoList)
if (!list.contains(targetSubInfo)) {
targetSubInfo?.let { list.add(it) }
}
Log.d(TAG, "list: $list")
return list.toList()
}
fun addItemForRenaming(subInfo: SubscriptionInfo, newName: String) {
if (subInfo.displayName == newName) {
return
}
renameMutableMap[subInfo.subscriptionId] = newName
}
fun getSubscriptionInfoDisplayName(subInfo: SubscriptionInfo): String {
return renameMutableMap[subInfo.subscriptionId] ?: subInfo.displayName.toString()
}
fun startActivatingSim(callback:() -> Unit){
// TODO: start to activate sim
}
}

View File

@@ -42,12 +42,14 @@ import androidx.annotation.Nullable;
import androidx.annotation.VisibleForTesting; import androidx.annotation.VisibleForTesting;
import com.android.internal.telephony.MccTable; import com.android.internal.telephony.MccTable;
import com.android.internal.telephony.flags.Flags;
import com.android.settings.R; import com.android.settings.R;
import com.android.settings.flags.Flags;
import com.android.settings.network.helper.SelectableSubscriptions; 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;
@@ -535,6 +537,11 @@ public class SubscriptionUtil {
Log.i(TAG, "Unable to toggle subscription due to invalid subscription ID."); Log.i(TAG, "Unable to toggle subscription due to invalid subscription ID.");
return; return;
} }
if (enable && Flags.isDualSimOnboardingEnabled()) {
String route = SimOnboardingPageProvider.INSTANCE.getRoute(subId);
SpaActivity.startSpaActivity(context, route);
return;
}
context.startActivity(ToggleSubscriptionDialogActivity.getIntent(context, subId, enable)); context.startActivity(ToggleSubscriptionDialogActivity.getIntent(context, subId, enable));
} }
@@ -814,7 +821,7 @@ public class SubscriptionUtil {
private static boolean isEmbeddedSubscriptionVisible(@NonNull SubscriptionInfo subInfo) { private static boolean isEmbeddedSubscriptionVisible(@NonNull SubscriptionInfo subInfo) {
if (subInfo.isEmbedded() if (subInfo.isEmbedded()
&& (subInfo.getProfileClass() == PROFILE_CLASS_PROVISIONING && (subInfo.getProfileClass() == PROFILE_CLASS_PROVISIONING
|| (Flags.oemEnabledSatelliteFlag() || (com.android.internal.telephony.flags.Flags.oemEnabledSatelliteFlag()
&& subInfo.isOnlyNonTerrestrialNetwork()))) { && subInfo.isOnlyNonTerrestrialNetwork()))) {
return false; return false;
} }

View File

@@ -47,6 +47,7 @@ import com.android.settings.spa.development.UsageStatsPageProvider
import com.android.settings.spa.development.compat.PlatformCompatAppListPageProvider import com.android.settings.spa.development.compat.PlatformCompatAppListPageProvider
import com.android.settings.spa.home.HomePageProvider import com.android.settings.spa.home.HomePageProvider
import com.android.settings.spa.network.NetworkAndInternetPageProvider import com.android.settings.spa.network.NetworkAndInternetPageProvider
import com.android.settings.spa.network.SimOnboardingPageProvider
import com.android.settings.spa.notification.AppListNotificationsPageProvider import com.android.settings.spa.notification.AppListNotificationsPageProvider
import com.android.settings.spa.notification.NotificationMainPageProvider import com.android.settings.spa.notification.NotificationMainPageProvider
import com.android.settings.spa.system.AppLanguagesPageProvider import com.android.settings.spa.system.AppLanguagesPageProvider
@@ -114,6 +115,7 @@ open class SettingsSpaEnvironment(context: Context) : SpaEnvironment(context) {
StorageAppListPageProvider.Apps, StorageAppListPageProvider.Apps,
StorageAppListPageProvider.Games, StorageAppListPageProvider.Games,
ApnEditPageProvider, ApnEditPageProvider,
SimOnboardingPageProvider,
) )
override val logger = if (FeatureFlagUtils.isEnabled( override val logger = if (FeatureFlagUtils.isEnabled(

View File

@@ -0,0 +1,113 @@
/*
* Copyright (C) 2024 The Android Open Source Project
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package com.android.settings.spa.network
import 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.material3.Text
import androidx.compose.runtime.Composable
import androidx.compose.runtime.getValue
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
import com.android.settingslib.spa.widget.editor.SettingsOutlinedTextField
import com.android.settingslib.spa.widget.preference.Preference
import com.android.settingslib.spa.widget.preference.PreferenceModel
import com.android.settingslib.spa.widget.scaffold.BottomAppBarButton
import com.android.settingslib.spa.widget.scaffold.SuwScaffold
import com.android.settingslib.spa.widget.ui.SettingsBody
/**
* the sim onboarding label compose
*/
@Composable
fun SimOnboardingLabelSimImpl(
nextAction: () -> Unit,
cancelAction: () -> Unit,
onboardingService: SimOnboardingService
) {
SuwScaffold(
imageVector = Icons.Outlined.SignalCellularAlt,
title = stringResource(R.string.sim_onboarding_label_sim_title),
actionButton = BottomAppBarButton(
stringResource(R.string.sim_onboarding_next),
nextAction
),
dismissButton = BottomAppBarButton(
stringResource(R.string.cancel),
cancelAction
),
) {
labelSimBody(onboardingService)
}
}
@Composable
private fun labelSimBody(onboardingService: SimOnboardingService) {
Column(Modifier.padding(SettingsDimension.itemPadding)) {
SettingsBody(stringResource(R.string.sim_onboarding_label_sim_msg))
}
for (subInfo in onboardingService.getSelectableSubscriptionInfo()) {
var titleSimName by remember {
mutableStateOf(
onboardingService.getSubscriptionInfoDisplayName(subInfo)
)
}
var summaryNumber = subInfo.number
// TODO using the SubscriptionUtil.getFormattedPhoneNumber
val alertDialogPresenter = rememberAlertDialogPresenter(
confirmButton = AlertDialogButton(
stringResource(R.string.mobile_network_sim_name_rename)
) {
onboardingService.addItemForRenaming(subInfo, titleSimName)
},
dismissButton = AlertDialogButton(stringResource(R.string.cancel)) {
titleSimName = onboardingService.getSubscriptionInfoDisplayName(subInfo)
},
title = stringResource(R.string.sim_onboarding_label_sim_dialog_title),
text = {
Text(summaryNumber)
SettingsOutlinedTextField(
value = titleSimName,
label = stringResource(R.string.sim_onboarding_label_sim_dialog_label),
enabled = true
) {
titleSimName = it
}
},
)
Preference(object : PreferenceModel {
override val title = titleSimName
override val summary: () -> String
get() = { summaryNumber }
override val onClick = alertDialogPresenter::open
})
}
}

View File

@@ -0,0 +1,135 @@
/*
* Copyright (C) 2023 The Android Open Source Project
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package com.android.settings.spa.network
import android.app.Activity
import android.content.Context
import android.content.ContextWrapper
import android.os.Bundle
import androidx.annotation.VisibleForTesting
import androidx.compose.runtime.Composable
import androidx.compose.ui.platform.LocalContext
import androidx.navigation.NavHostController
import androidx.navigation.NavType
import androidx.navigation.compose.NavHost
import androidx.navigation.compose.composable
import androidx.navigation.compose.rememberNavController
import androidx.navigation.navArgument
import com.android.settings.R
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
const val SUB_ID = "subId"
enum class SimOnboardingScreen(val stringResId: Int) {
LabelSim(R.string.sim_onboarding_label_sim_title),
SelectSim(R.string.sim_onboarding_select_sim_title),
PrimarySim(R.string.sim_onboarding_primary_sim_title)
}
/**
* Showing the sim onboarding which is the process flow of sim switching on.
*/
object SimOnboardingPageProvider : SettingsPageProvider {
override val name = "SimOnboardingPageProvider"
override val parameter = listOf(
navArgument(SUB_ID) { type = NavType.IntType },
)
private val owner = createSettingsPage()
@VisibleForTesting
var onboardingService: SimOnboardingService = SimOnboardingService()
fun buildInjectEntry() = SettingsEntryBuilder.createInject(owner = owner)
.setUiLayoutFn {
// never using
Preference(object : PreferenceModel {
override val title = name
override val onClick = navigator(getRoute(-1))
})
}
@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) {
is Activity -> this
is ContextWrapper -> baseContext.getActivity()
else -> null
}
@Composable
fun PageImpl(onboardingService:SimOnboardingService,navHostController: NavHostController) {
val context = LocalContext.current
var previousPageOfOnboarding: () -> Unit = { context.getActivity()?.finish() }
NavHost(
navController = navHostController,
startDestination = SimOnboardingScreen.LabelSim.name
) {
composable(route = SimOnboardingScreen.LabelSim.name) {
val nextPage =
// Adding more conditions
if (onboardingService.isMultipleEnabledProfilesSupported) {
SimOnboardingScreen.SelectSim.name
} else {
SimOnboardingScreen.PrimarySim.name
}
SimOnboardingLabelSimImpl(
nextAction = { navHostController.navigate(nextPage) },
cancelAction = previousPageOfOnboarding,
onboardingService = onboardingService
)
}
composable(route = SimOnboardingScreen.PrimarySim.name) {
SimOnboardingPrimarySimImpl(
nextAction = {
//go back and activate sim
},
cancelAction = previousPageOfOnboarding,
onboardingService = onboardingService
)
}
composable(route = SimOnboardingScreen.SelectSim.name) {
SimOnboardingSelectSimImpl(
nextAction = { navHostController.navigate(SimOnboardingScreen.PrimarySim.name) },
cancelAction = previousPageOfOnboarding,
onboardingService = onboardingService
)
}
}
}

View File

@@ -0,0 +1,160 @@
/*
* Copyright (C) 2024 The Android Open Source Project
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package com.android.settings.spa.network
import 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
import com.android.settingslib.spa.widget.ui.SettingsIcon
/**
* the sim onboarding primary sim compose
*/
@Composable
fun SimOnboardingPrimarySimImpl(
nextAction: () -> Unit,
cancelAction: () -> Unit,
onboardingService: SimOnboardingService
) {
SuwScaffold(
imageVector = Icons.Outlined.SignalCellularAlt,
title = stringResource(id = R.string.sim_onboarding_primary_sim_title),
actionButton = BottomAppBarButton(
stringResource(id = R.string.done),
nextAction
),
dismissButton = BottomAppBarButton(
stringResource(id = R.string.cancel),
cancelAction
),
) {
primarySimBody(onboardingService)
}
}
@Composable
private fun primarySimBody(onboardingService: SimOnboardingService) {
//TODO: Load the status from the frameworks
var callsSelectedId = rememberSaveable { mutableIntStateOf(1) }
var textsSelectedId = rememberSaveable { mutableIntStateOf(1) }
var mobileDataSelectedId = rememberSaveable { mutableIntStateOf(1) }
var automaticDataChecked by rememberSaveable { mutableStateOf(true) }
Column(Modifier.padding(SettingsDimension.itemPadding)) {
SettingsBody(stringResource(id = R.string.sim_onboarding_primary_sim_msg))
}
var selectableSubscriptionInfo = onboardingService.getSelectableSubscriptionInfo()
var list = listOf(ListPreferenceOption(id = -1, text = "Loading"))
if (selectableSubscriptionInfo.size >= 2) {
list = listOf(
ListPreferenceOption(
id = selectableSubscriptionInfo[0].subscriptionId,
text = "${selectableSubscriptionInfo[0].displayName}"
),
ListPreferenceOption(
id = selectableSubscriptionInfo[1].subscriptionId,
text = "${selectableSubscriptionInfo[1].displayName}"
),
ListPreferenceOption(
id = -1,
text = stringResource(id = R.string.sim_calls_ask_first_prefs_title)
),
)
} else {
// set all of primary sim items' enable as false and showing that sim.
}
createPrimarySimListPreference(
stringResource(id = R.string.primary_sim_calls_title),
list,
callsSelectedId,
ImageVector.vectorResource(R.drawable.ic_phone),
true
)
createPrimarySimListPreference(
stringResource(id = R.string.primary_sim_texts_title),
list,
textsSelectedId,
Icons.AutoMirrored.Outlined.Message,
true
)
createPrimarySimListPreference(
stringResource(id = R.string.mobile_data_settings_title),
list,
mobileDataSelectedId,
Icons.Outlined.DataUsage,
true
)
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(
title: String,
list: List<ListPreferenceOption>,
selectedId: MutableIntState,
icon: ImageVector,
enable: Boolean
) = ListPreference(remember {
object : ListPreferenceModel {
override val title = title
override val options = list
override val selectedId = selectedId
override val onIdSelected: (id: Int) -> Unit = { selectedId.intValue = it }
override val icon = @Composable {
SettingsIcon(icon)
}
override val enabled: () -> Boolean
get() = { enable }
}
})

View File

@@ -0,0 +1,89 @@
/*
* Copyright (C) 2024 The Android Open Source Project
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package com.android.settings.spa.network
import 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.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
/**
* the sim onboarding select sim compose
*/
@Composable
fun SimOnboardingSelectSimImpl(
nextAction: () -> Unit,
cancelAction: () -> Unit,
onboardingService: SimOnboardingService
) {
SuwScaffold(
imageVector = Icons.Outlined.SignalCellularAlt,
title = stringResource(id = R.string.sim_onboarding_select_sim_title),
actionButton = BottomAppBarButton(
stringResource(id = R.string.sim_onboarding_next),
nextAction
),
dismissButton = BottomAppBarButton(
stringResource(id = R.string.cancel),
cancelAction
),
) {
selectSimBody(onboardingService)
}
}
@Composable
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 title = onboardingService.getSubscriptionInfoDisplayName(subInfo)
var summaryNumber =
subInfo.number // TODO using the SubscriptionUtil.getFormattedPhoneNumber
var changeable = subInfo.isActive
var checked by rememberSaveable { mutableStateOf(!subInfo.isActive) }
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 }
}
})
}
}

View File

@@ -0,0 +1,197 @@
/*
* Copyright (C) 2024 The Android Open Source Project
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package com.android.settings.spa.network
import android.content.Context
import android.telephony.SubscriptionInfo
import androidx.compose.ui.test.assertIsDisplayed
import androidx.compose.ui.test.junit4.createComposeRule
import androidx.compose.ui.test.onNodeWithText
import androidx.compose.ui.test.performClick
import androidx.test.core.app.ApplicationProvider
import androidx.test.ext.junit.runners.AndroidJUnit4
import com.android.settings.R
import com.android.settings.network.SimOnboardingService
import org.junit.Rule
import org.junit.Test
import org.junit.runner.RunWith
import org.mockito.kotlin.doReturn
import org.mockito.kotlin.mock
import org.mockito.kotlin.stub
import org.mockito.kotlin.verify
@RunWith(AndroidJUnit4::class)
class SimOnboardingLabelSimTest {
@get:Rule
val composeTestRule = createComposeRule()
private val context: Context = ApplicationProvider.getApplicationContext()
private var mockSimOnboardingService = mock<SimOnboardingService> {
on { targetSubId }.doReturn(-1)
on { targetSubInfo }.doReturn(null)
on { availableSubInfoList }.doReturn(listOf())
on { activeSubInfoList }.doReturn(listOf())
on { slotInfoList }.doReturn(listOf())
on { uiccCardInfoList }.doReturn(listOf())
on { selectedSubInfoList }.doReturn(mutableListOf())
on { targetPrimarySimCalls }.doReturn(PRIMARY_SIM_ASK_EVERY_TIME)
on { targetPrimarySimTexts }.doReturn(PRIMARY_SIM_ASK_EVERY_TIME)
on { targetPrimarySimMobileData }.doReturn(PRIMARY_SIM_ASK_EVERY_TIME)
}
private val nextAction: () -> Unit = mock()
private val cancelAction: () -> Unit = mock()
@Test
fun simOnboardingLabelSimImpl_showTitle() {
composeTestRule.setContent {
SimOnboardingLabelSimImpl(nextAction, cancelAction, mockSimOnboardingService)
}
composeTestRule.onNodeWithText(context.getString(R.string.sim_onboarding_label_sim_title))
.assertIsDisplayed()
}
@Test
fun simOnboardingLabelSimImpl_showSubTitle() {
composeTestRule.setContent {
SimOnboardingLabelSimImpl(nextAction, cancelAction, mockSimOnboardingService)
}
composeTestRule.onNodeWithText(context.getString(R.string.sim_onboarding_label_sim_msg))
.assertIsDisplayed()
}
@Test
fun simOnboardingLabelSimImpl_clickNextAction_verifyNextAction() {
composeTestRule.setContent {
SimOnboardingLabelSimImpl(nextAction, cancelAction, mockSimOnboardingService)
}
composeTestRule.onNodeWithText(context.getString(R.string.sim_onboarding_next))
.performClick()
verify(nextAction)
}
@Test
fun simOnboardingLabelSimImpl_clickCancelAction_verifyCancelAction() {
composeTestRule.setContent {
SimOnboardingLabelSimImpl(nextAction, cancelAction, mockSimOnboardingService)
}
composeTestRule.onNodeWithText(context.getString(R.string.cancel))
.performClick()
verify(cancelAction)
}
@Test
fun simOnboardingLabelSimImpl_showItem_show3Items() {
mockSimOnboardingService.stub {
on { targetSubId }.doReturn(SUB_ID_1)
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(
listOf(
SUB_INFO_1,
SUB_INFO_2,
SUB_INFO_3
)
)
on { getSubscriptionInfoDisplayName(SUB_INFO_1) }.doReturn(DISPLAY_NAME_1)
on { getSubscriptionInfoDisplayName(SUB_INFO_2) }.doReturn(DISPLAY_NAME_2)
on { getSubscriptionInfoDisplayName(SUB_INFO_3) }.doReturn(DISPLAY_NAME_3)
}
composeTestRule.setContent {
SimOnboardingLabelSimImpl(nextAction, cancelAction, mockSimOnboardingService)
}
composeTestRule.onNodeWithText(DISPLAY_NAME_1).assertIsDisplayed()
composeTestRule.onNodeWithText(NUMBER_1).assertIsDisplayed()
composeTestRule.onNodeWithText(DISPLAY_NAME_2).assertIsDisplayed()
composeTestRule.onNodeWithText(NUMBER_2).assertIsDisplayed()
composeTestRule.onNodeWithText(DISPLAY_NAME_3).assertIsDisplayed()
composeTestRule.onNodeWithText(NUMBER_3).assertIsDisplayed()
}
@Test
fun simOnboardingLabelSimImpl_showDialog_checkTitle() {
mockSimOnboardingService.stub {
on { targetSubId }.doReturn(SUB_ID_1)
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(
listOf(
SUB_INFO_1,
SUB_INFO_2,
SUB_INFO_3
)
)
on { getSubscriptionInfoDisplayName(SUB_INFO_1) }.doReturn(DISPLAY_NAME_1)
on { getSubscriptionInfoDisplayName(SUB_INFO_2) }.doReturn(DISPLAY_NAME_2)
on { getSubscriptionInfoDisplayName(SUB_INFO_3) }.doReturn(DISPLAY_NAME_3)
}
composeTestRule.setContent {
SimOnboardingLabelSimImpl(nextAction, cancelAction, mockSimOnboardingService)
}
composeTestRule.onNodeWithText(DISPLAY_NAME_1).performClick()
composeTestRule.onNodeWithText(
context.getString(R.string.sim_onboarding_label_sim_dialog_title)
)
.assertIsDisplayed()
}
private companion object {
const val SUB_ID_1 = 1
const val SUB_ID_2 = 2
const val SUB_ID_3 = 3
const val DISPLAY_NAME_1 = "Sub 1"
const val DISPLAY_NAME_2 = "Sub 2"
const val DISPLAY_NAME_3 = "Sub 3"
const val NUMBER_1 = "000000001"
const val NUMBER_2 = "000000002"
const val NUMBER_3 = "000000003"
const val PRIMARY_SIM_ASK_EVERY_TIME = -1
val SUB_INFO_1: SubscriptionInfo = SubscriptionInfo.Builder().apply {
setId(SUB_ID_1)
setDisplayName(DISPLAY_NAME_1)
setNumber(NUMBER_1)
}.build()
val SUB_INFO_2: SubscriptionInfo = SubscriptionInfo.Builder().apply {
setId(SUB_ID_2)
setDisplayName(DISPLAY_NAME_2)
setNumber(NUMBER_2)
}.build()
val SUB_INFO_3: SubscriptionInfo = SubscriptionInfo.Builder().apply {
setId(SUB_ID_3)
setDisplayName(DISPLAY_NAME_3)
setNumber(NUMBER_3)
}.build()
}
}

View File

@@ -0,0 +1,126 @@
/*
* Copyright (C) 2024 The Android Open Source Project
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package com.android.settings.spa.network
import android.content.Context
import androidx.compose.ui.test.assertIsDisplayed
import androidx.compose.ui.test.junit4.createComposeRule
import androidx.compose.ui.test.onNodeWithText
import androidx.compose.ui.test.performClick
import androidx.navigation.compose.rememberNavController
import androidx.test.core.app.ApplicationProvider
import androidx.test.ext.junit.runners.AndroidJUnit4
import com.android.settings.R
import com.android.settings.network.SimOnboardingService
import com.google.common.truth.Truth.assertThat
import org.junit.Rule
import org.junit.Test
import org.junit.runner.RunWith
import org.mockito.kotlin.doReturn
import org.mockito.kotlin.mock
import org.mockito.kotlin.stub
@RunWith(AndroidJUnit4::class)
class SimOnboardingPageProviderTest {
@get:Rule
val composeTestRule = createComposeRule()
private val context: Context = ApplicationProvider.getApplicationContext()
private var mockSimOnboardingService = mock<SimOnboardingService> {
on { targetSubId }.doReturn(SUB_ID)
on { targetSubInfo }.doReturn(null)
on { availableSubInfoList }.doReturn(listOf())
on { activeSubInfoList }.doReturn(listOf())
on { slotInfoList }.doReturn(listOf())
on { uiccCardInfoList }.doReturn(listOf())
on { selectedSubInfoList }.doReturn(mutableListOf())
on { targetPrimarySimCalls }.doReturn(PRIMARY_SIM_ASK_EVERY_TIME)
on { targetPrimarySimTexts }.doReturn(PRIMARY_SIM_ASK_EVERY_TIME)
on { targetPrimarySimMobileData }.doReturn(PRIMARY_SIM_ASK_EVERY_TIME)
}
@Test
fun simOnboardingPageProvider_name() {
assertThat(SimOnboardingPageProvider.name).isEqualTo("SimOnboardingPageProvider")
}
@Test
fun simOnboardingPage_labelSim() {
composeTestRule.setContent {
val navHostController = rememberNavController()
PageImpl(mockSimOnboardingService, navHostController)
}
composeTestRule.onNodeWithText(context.getString(R.string.sim_onboarding_label_sim_title))
.assertIsDisplayed()
}
@Test
fun simOnboardingPage_nextAction_fromLabelSimToPrimarySim() {
mockSimOnboardingService.stub {
on { isMultipleEnabledProfilesSupported }.thenReturn(false)
}
composeTestRule.setContent {
val navHostController = rememberNavController()
PageImpl(mockSimOnboardingService, navHostController)
}
composeTestRule.onNodeWithText(context.getString(R.string.sim_onboarding_next))
.performClick()
composeTestRule.onNodeWithText(context.getString(R.string.sim_onboarding_primary_sim_title))
.assertIsDisplayed()
}
@Test
fun simOnboardingPage_nextAction_fromLabelSimToSelectSim() {
mockSimOnboardingService.stub {
on { isMultipleEnabledProfilesSupported }.thenReturn(true)
}
composeTestRule.setContent {
val navHostController = rememberNavController()
PageImpl(mockSimOnboardingService, navHostController)
}
composeTestRule.onNodeWithText(context.getString(R.string.sim_onboarding_next))
.performClick()
composeTestRule.onNodeWithText(context.getString(R.string.sim_onboarding_select_sim_title))
.assertIsDisplayed()
}
@Test
fun simOnboardingPage_nextAction_fromSelectSimToPrimarySim() {
composeTestRule.setContent {
val navHostController = rememberNavController()
PageImpl(mockSimOnboardingService, navHostController)
}
composeTestRule.onNodeWithText(context.getString(R.string.sim_onboarding_next))
.performClick()
composeTestRule.onNodeWithText(context.getString(R.string.sim_onboarding_primary_sim_title))
.assertIsDisplayed()
}
private companion object {
const val SUB_ID = 1
const val PRIMARY_SIM_ASK_EVERY_TIME = -1
}
}

View File

@@ -0,0 +1,123 @@
/*
* Copyright (C) 2024 The Android Open Source Project
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package com.android.settings.spa.network
import android.content.Context
import android.telephony.SubscriptionInfo
import androidx.compose.ui.test.assertIsDisplayed
import androidx.compose.ui.test.junit4.createComposeRule
import androidx.compose.ui.test.onNodeWithText
import androidx.compose.ui.test.performClick
import androidx.test.core.app.ApplicationProvider
import androidx.test.ext.junit.runners.AndroidJUnit4
import com.android.settings.R
import com.android.settings.network.SimOnboardingService
import org.junit.Rule
import org.junit.Test
import org.junit.runner.RunWith
import org.mockito.kotlin.doReturn
import org.mockito.kotlin.mock
import org.mockito.kotlin.stub
import org.mockito.kotlin.verify
@RunWith(AndroidJUnit4::class)
class SimOnboardingPrimarySimTest {
@get:Rule
val composeTestRule = createComposeRule()
private val context: Context = ApplicationProvider.getApplicationContext()
private var mockSimOnboardingService = mock<SimOnboardingService> {
on { targetSubId }.doReturn(-1)
on { targetSubInfo }.doReturn(null)
on { availableSubInfoList }.doReturn(listOf())
on { activeSubInfoList }.doReturn(listOf())
on { slotInfoList }.doReturn(listOf())
on { uiccCardInfoList }.doReturn(listOf())
on { selectedSubInfoList }.doReturn(mutableListOf())
on { targetPrimarySimCalls }.doReturn(PRIMARY_SIM_ASK_EVERY_TIME)
on { targetPrimarySimTexts }.doReturn(PRIMARY_SIM_ASK_EVERY_TIME)
on { targetPrimarySimMobileData }.doReturn(PRIMARY_SIM_ASK_EVERY_TIME)
}
private val nextAction: () -> Unit = mock()
private val cancelAction: () -> Unit = mock()
@Test
fun simOnboardingPrimarySimImpl_showTitle() {
composeTestRule.setContent {
SimOnboardingPrimarySimImpl(nextAction, cancelAction, mockSimOnboardingService)
}
composeTestRule.onNodeWithText(context.getString(R.string.sim_onboarding_primary_sim_title))
.assertIsDisplayed()
}
@Test
fun simOnboardingPrimarySimImpl_showSubTitle() {
composeTestRule.setContent {
SimOnboardingPrimarySimImpl(nextAction, cancelAction, mockSimOnboardingService)
}
composeTestRule.onNodeWithText(context.getString(R.string.sim_onboarding_primary_sim_msg))
.assertIsDisplayed()
}
@Test
fun simOnboardingPrimarySimImpl_clickCancelAction_verifyCancelAction() {
composeTestRule.setContent {
SimOnboardingPrimarySimImpl(nextAction, cancelAction, mockSimOnboardingService)
}
composeTestRule.onNodeWithText(context.getString(R.string.cancel))
.performClick()
verify(cancelAction)
}
private companion object {
const val SUB_ID_1 = 1
const val SUB_ID_2 = 2
const val SUB_ID_3 = 3
const val DISPLAY_NAME_1 = "Sub 1"
const val DISPLAY_NAME_2 = "Sub 2"
const val DISPLAY_NAME_3 = "Sub 3"
const val NUMBER_1 = "000000001"
const val NUMBER_2 = "000000002"
const val NUMBER_3 = "000000003"
const val PRIMARY_SIM_ASK_EVERY_TIME = -1
val SUB_INFO_1: SubscriptionInfo = SubscriptionInfo.Builder().apply {
setId(SUB_ID_1)
setDisplayName(DISPLAY_NAME_1)
setNumber(NUMBER_1)
}.build()
val SUB_INFO_2: SubscriptionInfo = SubscriptionInfo.Builder().apply {
setId(SUB_ID_2)
setDisplayName(DISPLAY_NAME_2)
setNumber(NUMBER_2)
}.build()
val SUB_INFO_3: SubscriptionInfo = SubscriptionInfo.Builder().apply {
setId(SUB_ID_3)
setDisplayName(DISPLAY_NAME_3)
setNumber(NUMBER_3)
}.build()
}
}

View File

@@ -0,0 +1,165 @@
/*
* Copyright (C) 2024 The Android Open Source Project
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package com.android.settings.spa.network
import android.content.Context
import android.telephony.SubscriptionInfo
import androidx.compose.ui.test.assertIsDisplayed
import androidx.compose.ui.test.junit4.createComposeRule
import androidx.compose.ui.test.onNodeWithText
import androidx.compose.ui.test.performClick
import androidx.test.core.app.ApplicationProvider
import androidx.test.ext.junit.runners.AndroidJUnit4
import com.android.settings.R
import com.android.settings.network.SimOnboardingService
import org.junit.Rule
import org.junit.Test
import org.junit.runner.RunWith
import org.mockito.kotlin.doReturn
import org.mockito.kotlin.mock
import org.mockito.kotlin.stub
import org.mockito.kotlin.verify
@RunWith(AndroidJUnit4::class)
class SimOnboardingSelectSimTest {
@get:Rule
val composeTestRule = createComposeRule()
private val context: Context = ApplicationProvider.getApplicationContext()
private var mockSimOnboardingService = mock<SimOnboardingService> {
on { targetSubId }.doReturn(-1)
on { targetSubInfo }.doReturn(null)
on { availableSubInfoList }.doReturn(listOf())
on { activeSubInfoList }.doReturn(listOf())
on { slotInfoList }.doReturn(listOf())
on { uiccCardInfoList }.doReturn(listOf())
on { selectedSubInfoList }.doReturn(mutableListOf())
on { targetPrimarySimCalls }.doReturn(PRIMARY_SIM_ASK_EVERY_TIME)
on { targetPrimarySimTexts }.doReturn(PRIMARY_SIM_ASK_EVERY_TIME)
on { targetPrimarySimMobileData }.doReturn(PRIMARY_SIM_ASK_EVERY_TIME)
}
private val nextAction: () -> Unit = mock()
private val cancelAction: () -> Unit = mock()
@Test
fun simOnboardingSelectSimImpl_showTitle() {
composeTestRule.setContent {
SimOnboardingSelectSimImpl(nextAction, cancelAction, mockSimOnboardingService)
}
composeTestRule.onNodeWithText(context.getString(R.string.sim_onboarding_select_sim_title))
.assertIsDisplayed()
}
@Test
fun simOnboardingSelectSimImpl_showSubTitle() {
composeTestRule.setContent {
SimOnboardingSelectSimImpl(nextAction, cancelAction, mockSimOnboardingService)
}
composeTestRule.onNodeWithText(context.getString(R.string.sim_onboarding_select_sim_msg))
.assertIsDisplayed()
}
@Test
fun simOnboardingSelectSimImpl_clickNextAction_verifyNextAction() {
composeTestRule.setContent {
SimOnboardingSelectSimImpl(nextAction, cancelAction, mockSimOnboardingService)
}
composeTestRule.onNodeWithText(context.getString(R.string.sim_onboarding_next))
.performClick()
verify(nextAction)
}
@Test
fun simOnboardingSelectSimImpl_clickCancelAction_verifyCancelAction() {
composeTestRule.setContent {
SimOnboardingSelectSimImpl(nextAction, cancelAction, mockSimOnboardingService)
}
composeTestRule.onNodeWithText(context.getString(R.string.cancel))
.performClick()
verify(cancelAction)
}
@Test
fun simOnboardingSelectSimImpl_showItem_show3Items() {
mockSimOnboardingService.stub {
on { targetSubId }.doReturn(SUB_ID_1)
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(
listOf(
SUB_INFO_1,
SUB_INFO_2,
SUB_INFO_3
)
)
on { getSubscriptionInfoDisplayName(SUB_INFO_1) }.doReturn(DISPLAY_NAME_1)
on { getSubscriptionInfoDisplayName(SUB_INFO_2) }.doReturn(DISPLAY_NAME_2)
on { getSubscriptionInfoDisplayName(SUB_INFO_3) }.doReturn(DISPLAY_NAME_3)
}
composeTestRule.setContent {
SimOnboardingSelectSimImpl(nextAction, cancelAction, mockSimOnboardingService)
}
composeTestRule.onNodeWithText(DISPLAY_NAME_1).assertIsDisplayed()
composeTestRule.onNodeWithText(NUMBER_1).assertIsDisplayed()
composeTestRule.onNodeWithText(DISPLAY_NAME_2).assertIsDisplayed()
composeTestRule.onNodeWithText(NUMBER_2).assertIsDisplayed()
composeTestRule.onNodeWithText(DISPLAY_NAME_3).assertIsDisplayed()
composeTestRule.onNodeWithText(NUMBER_3).assertIsDisplayed()
}
private companion object {
const val SUB_ID_1 = 1
const val SUB_ID_2 = 2
const val SUB_ID_3 = 3
const val DISPLAY_NAME_1 = "Sub 1"
const val DISPLAY_NAME_2 = "Sub 2"
const val DISPLAY_NAME_3 = "Sub 3"
const val NUMBER_1 = "000000001"
const val NUMBER_2 = "000000002"
const val NUMBER_3 = "000000003"
const val PRIMARY_SIM_ASK_EVERY_TIME = -1
val SUB_INFO_1: SubscriptionInfo = SubscriptionInfo.Builder().apply {
setId(SUB_ID_1)
setDisplayName(DISPLAY_NAME_1)
setNumber(NUMBER_1)
}.build()
val SUB_INFO_2: SubscriptionInfo = SubscriptionInfo.Builder().apply {
setId(SUB_ID_2)
setDisplayName(DISPLAY_NAME_2)
setNumber(NUMBER_2)
}.build()
val SUB_INFO_3: SubscriptionInfo = SubscriptionInfo.Builder().apply {
setId(SUB_ID_3)
setDisplayName(DISPLAY_NAME_3)
setNumber(NUMBER_3)
}.build()
}
}