diff --git a/src/com/android/settings/network/telephony/MobileNetworkSwitchController.kt b/src/com/android/settings/network/telephony/MobileNetworkSwitchController.kt index 6c5127f90eb..63364f9d990 100644 --- a/src/com/android/settings/network/telephony/MobileNetworkSwitchController.kt +++ b/src/com/android/settings/network/telephony/MobileNetworkSwitchController.kt @@ -21,14 +21,15 @@ import android.telephony.SubscriptionManager import androidx.compose.runtime.Composable import androidx.compose.runtime.getValue import androidx.compose.runtime.remember +import androidx.compose.runtime.rememberCoroutineScope import androidx.compose.ui.platform.LocalContext import androidx.compose.ui.res.stringResource import androidx.lifecycle.compose.collectAsStateWithLifecycle import com.android.settings.R -import com.android.settings.network.SubscriptionUtil import com.android.settings.spa.preference.ComposePreferenceController import com.android.settingslib.spa.widget.preference.MainSwitchPreference import com.android.settingslib.spa.widget.preference.SwitchPreferenceModel +import kotlinx.coroutines.launch class MobileNetworkSwitchController @JvmOverloads constructor( context: Context, @@ -56,12 +57,15 @@ class MobileNetworkSwitchController @JvmOverloads constructor( val changeable by remember { subscriptionActivationRepository.isActivationChangeableFlow() }.collectAsStateWithLifecycle(initialValue = true) + val coroutineScope = rememberCoroutineScope() MainSwitchPreference(model = object : SwitchPreferenceModel { override val title = stringResource(R.string.mobile_network_use_sim_on) override val changeable = { changeable } override val checked = { checked } - override val onCheckedChange = { newChecked: Boolean -> - SubscriptionUtil.startToggleSubscriptionDialogActivity(mContext, subId, newChecked) + override val onCheckedChange: (Boolean) -> Unit = { newChecked -> + coroutineScope.launch { + subscriptionActivationRepository.setActive(subId, newChecked) + } } }) } diff --git a/src/com/android/settings/network/telephony/SubscriptionActivationRepository.kt b/src/com/android/settings/network/telephony/SubscriptionActivationRepository.kt index 416dda19a2c..185af0c3d09 100644 --- a/src/com/android/settings/network/telephony/SubscriptionActivationRepository.kt +++ b/src/com/android/settings/network/telephony/SubscriptionActivationRepository.kt @@ -17,9 +17,18 @@ package com.android.settings.network.telephony import android.content.Context +import android.content.Intent +import android.telephony.SubscriptionManager +import android.telephony.TelephonyManager.ACTION_SHOW_NOTICE_ECM_BLOCK_OTHERS +import android.util.Log +import com.android.settings.Utils +import com.android.settings.flags.Flags import com.android.settings.network.SatelliteRepository +import com.android.settings.network.SimOnboardingActivity.Companion.startSimOnboardingActivity +import kotlinx.coroutines.Dispatchers import kotlinx.coroutines.flow.Flow import kotlinx.coroutines.flow.combine +import kotlinx.coroutines.withContext class SubscriptionActivationRepository( private val context: Context, @@ -32,4 +41,36 @@ class SubscriptionActivationRepository( ) { isInCall, isSatelliteModemEnabled -> !isInCall && !isSatelliteModemEnabled } + + /** + * Starts a dialog activity to handle SIM enabling / disabling. + * @param subId The id of subscription need to be enabled or disabled. + * @param active Whether the subscription with [subId] should be enabled or disabled. + */ + suspend fun setActive(subId: Int, active: Boolean) { + if (!SubscriptionManager.isUsableSubscriptionId(subId)) { + Log.i(TAG, "Unable to toggle subscription due to unusable subscription ID.") + return + } + if (!active && isEmergencyCallbackMode(subId)) { + val intent = Intent(ACTION_SHOW_NOTICE_ECM_BLOCK_OTHERS).apply { + setPackage(Utils.PHONE_PACKAGE_NAME) + } + context.startActivity(intent) + return + } + if (active && Flags.isDualSimOnboardingEnabled()) { + startSimOnboardingActivity(context, subId) + return + } + context.startActivity(ToggleSubscriptionDialogActivity.getIntent(context, subId, active)) + } + + private suspend fun isEmergencyCallbackMode(subId: Int) = withContext(Dispatchers.Default) { + context.telephonyManager(subId).emergencyCallbackMode + } + + private companion object { + private const val TAG = "SubscriptionActivationR" + } } diff --git a/src/com/android/settings/spa/network/SimsSection.kt b/src/com/android/settings/spa/network/SimsSection.kt index 07da034b33b..842656e28bd 100644 --- a/src/com/android/settings/spa/network/SimsSection.kt +++ b/src/com/android/settings/spa/network/SimsSection.kt @@ -30,6 +30,7 @@ import androidx.compose.runtime.Composable import androidx.compose.runtime.State import androidx.compose.runtime.getValue import androidx.compose.runtime.remember +import androidx.compose.runtime.rememberCoroutineScope import androidx.compose.ui.platform.LocalContext import androidx.compose.ui.res.stringResource import androidx.lifecycle.compose.collectAsStateWithLifecycle @@ -47,6 +48,7 @@ import com.android.settingslib.spaprivileged.model.enterprise.Restrictions import com.android.settingslib.spaprivileged.template.preference.RestrictedPreference import com.android.settingslib.spaprivileged.template.preference.RestrictedTwoTargetSwitchPreference import kotlinx.coroutines.flow.flow +import kotlinx.coroutines.launch @Composable fun SimsSection(subscriptionInfoList: List) { @@ -71,9 +73,11 @@ private fun SimPreference(subInfo: SubscriptionInfo) { emit(SubscriptionUtil.isConvertedPsimSubscription(subInfo)) } }.collectAsStateWithLifecycle(initialValue = false) + val subscriptionActivationRepository = remember { SubscriptionActivationRepository(context) } val isActivationChangeable by remember { - SubscriptionActivationRepository(context).isActivationChangeableFlow() + subscriptionActivationRepository.isActivationChangeableFlow() }.collectAsStateWithLifecycle(initialValue = false) + val coroutineScope = rememberCoroutineScope() RestrictedTwoTargetSwitchPreference( model = object : SwitchPreferenceModel { override val title = subInfo.displayName.toString() @@ -87,12 +91,10 @@ private fun SimPreference(subInfo: SubscriptionInfo) { override val icon = @Composable { SimIcon(subInfo.isEmbedded) } override val changeable = { isActivationChangeable && !isConvertedPsim } override val checked = { checked.value } - override val onCheckedChange = { newChecked: Boolean -> - SubscriptionUtil.startToggleSubscriptionDialogActivity( - context, - subInfo.subscriptionId, - newChecked, - ) + override val onCheckedChange: (Boolean) -> Unit = { newChecked -> + coroutineScope.launch { + subscriptionActivationRepository.setActive(subInfo.subscriptionId, newChecked) + } } }, restrictions = Restrictions(keys = listOf(UserManager.DISALLOW_CONFIG_MOBILE_NETWORKS)), diff --git a/tests/spa_unit/src/com/android/settings/network/telephony/SubscriptionActivationRepositoryTest.kt b/tests/spa_unit/src/com/android/settings/network/telephony/SubscriptionActivationRepositoryTest.kt index dd9c505635b..427ab7b1685 100644 --- a/tests/spa_unit/src/com/android/settings/network/telephony/SubscriptionActivationRepositoryTest.kt +++ b/tests/spa_unit/src/com/android/settings/network/telephony/SubscriptionActivationRepositoryTest.kt @@ -17,6 +17,9 @@ package com.android.settings.network.telephony import android.content.Context +import android.telephony.SubscriptionManager +import android.telephony.TelephonyManager +import android.telephony.TelephonyManager.ACTION_SHOW_NOTICE_ECM_BLOCK_OTHERS import androidx.test.core.app.ApplicationProvider import androidx.test.ext.junit.runners.AndroidJUnit4 import com.android.settings.network.SatelliteRepository @@ -26,14 +29,29 @@ import kotlinx.coroutines.flow.flowOf import kotlinx.coroutines.runBlocking import org.junit.Test import org.junit.runner.RunWith +import org.mockito.kotlin.any +import org.mockito.kotlin.argThat +import org.mockito.kotlin.doNothing import org.mockito.kotlin.doReturn import org.mockito.kotlin.mock +import org.mockito.kotlin.never +import org.mockito.kotlin.spy import org.mockito.kotlin.stub +import org.mockito.kotlin.verify +import org.mockito.kotlin.whenever @RunWith(AndroidJUnit4::class) class SubscriptionActivationRepositoryTest { - private val context: Context = ApplicationProvider.getApplicationContext() + private val mockTelephonyManager = mock { + on { createForSubscriptionId(SUB_ID) } doReturn mock + } + + private val context: Context = spy(ApplicationProvider.getApplicationContext()) { + doNothing().whenever(mock).startActivity(any()) + on { getSystemService(TelephonyManager::class.java) } doReturn mockTelephonyManager + } + private val mockCallStateRepository = mock() private val mockSatelliteRepository = mock() @@ -81,4 +99,39 @@ class SubscriptionActivationRepositoryTest { assertThat(changeable).isFalse() } + + @Test + fun setActive_defaultSubId_doNothing() = runBlocking { + repository.setActive(subId = SubscriptionManager.DEFAULT_SUBSCRIPTION_ID, active = true) + + verify(context, never()).startActivity(any()) + } + + @Test + fun setActive_turnOffAndIsEmergencyCallbackMode() = runBlocking { + mockTelephonyManager.stub { + on { emergencyCallbackMode } doReturn true + } + + repository.setActive(subId = SUB_ID, active = false) + + verify(context).startActivity(argThat { action == ACTION_SHOW_NOTICE_ECM_BLOCK_OTHERS }) + } + + @Test + fun setActive_turnOffAndNotEmergencyCallbackMode() = runBlocking { + mockTelephonyManager.stub { + on { emergencyCallbackMode } doReturn false + } + + repository.setActive(subId = SUB_ID, active = false) + + verify(context).startActivity(argThat { + component?.className == ToggleSubscriptionDialogActivity::class.qualifiedName + }) + } + + private companion object { + const val SUB_ID = 1 + } }