diff --git a/res/xml/mobile_network_settings.xml b/res/xml/mobile_network_settings.xml index eb9f4427533..51cbbe6b86f 100644 --- a/res/xml/mobile_network_settings.xml +++ b/res/xml/mobile_network_settings.xml @@ -85,13 +85,9 @@ android:summary="@string/auto_data_switch_summary" settings:controller="com.android.settings.network.telephony.AutoDataSwitchPreferenceController"/> - = - simSlotRepository.subIdInSimSlotFlow(simSlotIndex) + simSlotRepository + .subIdInSimSlotFlow(simSlotIndex) .flatMapLatest { subId -> if (SubscriptionManager.isValidSubscriptionId(subId)) { simStatusDialogInfoFlow(subId) @@ -99,22 +102,16 @@ class SimStatusDialogRepository @JvmOverloads constructor( } private fun showUpFlow(subId: Int) = flow { - val config = carrierConfigManager.safeGetConfig( - keys = listOf( - CarrierConfigManager.KEY_SHOW_SIGNAL_STRENGTH_IN_SIM_STATUS_BOOL, - CarrierConfigManager.KEY_SHOW_IMS_REGISTRATION_STATUS_BOOL, - ), - subId = subId, - ) - val visibility = SimStatusDialogVisibility( - signalStrengthShowUp = config.getBoolean( - CarrierConfigManager.KEY_SHOW_SIGNAL_STRENGTH_IN_SIM_STATUS_BOOL, - true, // by default we show the signal strength in sim status - ), - imsRegisteredShowUp = config.getBoolean( - CarrierConfigManager.KEY_SHOW_IMS_REGISTRATION_STATUS_BOOL - ), - ) + val visibility = + carrierConfigRepository.transformConfig(subId) { + SimStatusDialogVisibility( + signalStrengthShowUp = + getBoolean( + CarrierConfigManager.KEY_SHOW_SIGNAL_STRENGTH_IN_SIM_STATUS_BOOL), + imsRegisteredShowUp = + getBoolean(CarrierConfigManager.KEY_SHOW_IMS_REGISTRATION_STATUS_BOOL), + ) + } emit(visibility) } } diff --git a/src/com/android/settings/network/telephony/CarrierConfigManagerExt.kt b/src/com/android/settings/network/telephony/CarrierConfigManagerExt.kt index 05b4c07c53e..5408ab04e77 100644 --- a/src/com/android/settings/network/telephony/CarrierConfigManagerExt.kt +++ b/src/com/android/settings/network/telephony/CarrierConfigManagerExt.kt @@ -24,6 +24,7 @@ import androidx.core.os.persistableBundleOf /** * Gets the configuration values of the specified config keys applied. */ +@Deprecated("Use CarrierConfigRepository instead") fun CarrierConfigManager.safeGetConfig( keys: List, subId: Int = SubscriptionManager.getDefaultSubscriptionId(), diff --git a/src/com/android/settings/network/telephony/CarrierConfigRepository.kt b/src/com/android/settings/network/telephony/CarrierConfigRepository.kt new file mode 100644 index 00000000000..3ec529dbe82 --- /dev/null +++ b/src/com/android/settings/network/telephony/CarrierConfigRepository.kt @@ -0,0 +1,217 @@ +/* + * 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.telephony + +import android.content.Context +import android.os.PersistableBundle +import android.telephony.CarrierConfigManager +import android.telephony.SubscriptionManager +import android.util.Log +import androidx.annotation.VisibleForTesting +import java.util.concurrent.ConcurrentHashMap +import kotlinx.atomicfu.atomic +import kotlinx.coroutines.Dispatchers +import kotlinx.coroutines.asExecutor + +class CarrierConfigRepository(private val context: Context) { + + private val carrierConfigManager: CarrierConfigManager? = + context.getSystemService(CarrierConfigManager::class.java) + + private enum class KeyType { + BOOLEAN, + INT, + STRING + } + + interface CarrierConfigAccessor { + fun getBoolean(key: String): Boolean + + fun getInt(key: String): Int + + fun getString(key: String): String? + } + + private class Accessor(private val cache: ConfigCache) : CarrierConfigAccessor { + private val keysToRetrieve = mutableMapOf() + + override fun getBoolean(key: String): Boolean { + check(key.endsWith("_bool")) { "Boolean key should ends with _bool" } + val value = cache[key] + return if (value == null) { + keysToRetrieve += key to KeyType.BOOLEAN + DefaultConfig.getBoolean(key) + } else { + check(value is BooleanConfigValue) { "Boolean value type wrong" } + value.value + } + } + + override fun getInt(key: String): Int { + check(key.endsWith("_int")) { "Int key should ends with _int" } + val value = cache[key] + return if (value == null) { + keysToRetrieve += key to KeyType.INT + DefaultConfig.getInt(key) + } else { + check(value is IntConfigValue) { "Int value type wrong" } + value.value + } + } + + override fun getString(key: String): String? { + check(key.endsWith("_string")) { "String key should ends with _string" } + val value = cache[key] + return if (value == null) { + keysToRetrieve += key to KeyType.STRING + DefaultConfig.getString(key) + } else { + check(value is StringConfigValue) { "String value type wrong" } + value.value + } + } + + fun getKeysToRetrieve(): Map = keysToRetrieve + } + + /** + * Gets the configuration values for the given [subId]. + * + * Configuration values could be accessed in [block]. Note: [block] could be called multiple + * times, so it should be pure function without side effort. + */ + fun transformConfig(subId: Int, block: CarrierConfigAccessor.() -> T): T { + val perSubCache = getPerSubCache(subId) + val accessor = Accessor(perSubCache) + val result = accessor.block() + val keysToRetrieve = accessor.getKeysToRetrieve() + // If all keys found in the first pass, no need to collect again + if (keysToRetrieve.isEmpty()) return result + + perSubCache.update(subId, keysToRetrieve) + + return accessor.block() + } + + /** Gets the configuration boolean for the given [subId] and [key]. */ + fun getBoolean(subId: Int, key: String): Boolean = transformConfig(subId) { getBoolean(key) } + + /** Gets the configuration int for the given [subId] and [key]. */ + fun getInt(subId: Int, key: String): Int = transformConfig(subId) { getInt(key) } + + /** Gets the configuration string for the given [subId] and [key]. */ + fun getString(subId: Int, key: String): String? = transformConfig(subId) { getString(key) } + + private fun ConfigCache.update(subId: Int, keysToRetrieve: Map) { + val config = safeGetConfig(subId, keysToRetrieve.keys) ?: return + for ((key, type) in keysToRetrieve) { + when (type) { + KeyType.BOOLEAN -> this[key] = BooleanConfigValue(config.getBoolean(key)) + KeyType.INT -> this[key] = IntConfigValue(config.getInt(key)) + KeyType.STRING -> this[key] = StringConfigValue(config.getString(key)) + } + } + } + + /** Gets the configuration values of the specified config keys applied. */ + private fun safeGetConfig(subId: Int, keys: Collection): PersistableBundle? { + if (carrierConfigManager == null || !SubscriptionManager.isValidSubscriptionId(subId)) { + return null + } + tryRegisterListener(context) + return try { + carrierConfigManager.getConfigForSubId(subId, *keys.toTypedArray()) + } catch (e: Exception) { + Log.e(TAG, "safeGetConfig: exception", e) + // The CarrierConfigLoader (the service implemented the CarrierConfigManager) hasn't + // been initialized yet. This may occurs during very early phase of phone booting up + // or when Phone process has been restarted. + // Settings should not assume Carrier config loader (and any other system services + // as well) are always available. If not available, use default value instead. + null + } + } + + companion object { + private const val TAG = "CarrierConfigRepository" + + private val DefaultConfig = CarrierConfigManager.getDefaultConfig() + + /** Cache of config values for each subscription. */ + private val Cache = ConcurrentHashMap() + + private fun getPerSubCache(subId: Int) = + Cache.computeIfAbsent(subId) { ConcurrentHashMap() } + + /** To make sure the registerCarrierConfigChangeListener is only called once. */ + private val ListenerRegistered = atomic(false) + + private fun tryRegisterListener(context: Context) { + if (ListenerRegistered.compareAndSet(expect = false, update = true)) { + val carrierConfigManager = + context.applicationContext.getSystemService(CarrierConfigManager::class.java) + if (carrierConfigManager != null) { + carrierConfigManager.registerCarrierConfigChangeListener() + } else { + ListenerRegistered.getAndSet(false) + } + } + } + + private fun CarrierConfigManager.registerCarrierConfigChangeListener() { + val executor = Dispatchers.Default.asExecutor() + registerCarrierConfigChangeListener(executor) { _, subId, _, _ -> + Log.d(TAG, "[$subId] onCarrierConfigChanged") + Cache.remove(subId) + } + } + + @VisibleForTesting + fun resetForTest() { + Cache.clear() + ListenerRegistered.getAndSet(false) + } + + @VisibleForTesting + fun setBooleanForTest(subId: Int, key: String, value: Boolean) { + check(key.endsWith("_bool")) { "Boolean key should ends with _bool" } + getPerSubCache(subId)[key] = BooleanConfigValue(value) + } + + @VisibleForTesting + fun setIntForTest(subId: Int, key: String, value: Int) { + check(key.endsWith("_int")) { "Int key should ends with _int" } + getPerSubCache(subId)[key] = IntConfigValue(value) + } + + @VisibleForTesting + fun setStringForTest(subId: Int, key: String, value: String) { + check(key.endsWith("_string")) { "String key should ends with _string" } + getPerSubCache(subId)[key] = StringConfigValue(value) + } + } +} + +private sealed interface ConfigValue + +private data class BooleanConfigValue(val value: Boolean) : ConfigValue + +private data class IntConfigValue(val value: Int) : ConfigValue + +private data class StringConfigValue(val value: String?) : ConfigValue + +private typealias ConfigCache = ConcurrentHashMap diff --git a/src/com/android/settings/network/telephony/MobileDataRepository.kt b/src/com/android/settings/network/telephony/MobileDataRepository.kt index 46f814b60da..5a0dff70dd8 100644 --- a/src/com/android/settings/network/telephony/MobileDataRepository.kt +++ b/src/com/android/settings/network/telephony/MobileDataRepository.kt @@ -118,6 +118,18 @@ class MobileDataRepository( } } + /** Creates an instance of a cold Flow for whether data roaming is enabled of given [subId]. */ + fun isDataRoamingEnabledFlow(subId: Int): Flow { + if (!SubscriptionManager.isValidSubscriptionId(subId)) return flowOf(false) + val telephonyManager = context.telephonyManager(subId) + return mobileSettingsGlobalChangedFlow(Settings.Global.DATA_ROAMING, subId) + .map { telephonyManager.isDataRoamingEnabled } + .distinctUntilChanged() + .conflate() + .onEach { Log.d(TAG, "[$subId] isDataRoamingEnabledFlow: $it") } + .flowOn(Dispatchers.Default) + } + private companion object { private const val TAG = "MobileDataRepository" } diff --git a/src/com/android/settings/network/telephony/MobileNetworkSettings.java b/src/com/android/settings/network/telephony/MobileNetworkSettings.java index 8b927a9f1ca..34d2fbd5436 100644 --- a/src/com/android/settings/network/telephony/MobileNetworkSettings.java +++ b/src/com/android/settings/network/telephony/MobileNetworkSettings.java @@ -79,7 +79,6 @@ public class MobileNetworkSettings extends AbstractMobileNetworkSettings impleme @VisibleForTesting static final String KEY_CLICKED_PREF = "key_clicked_pref"; - private static final String KEY_ROAMING_PREF = "button_roaming_key"; private static final String KEY_CALLS_PREF = "calls_preference"; private static final String KEY_SMS_PREF = "sms_preference"; private static final String KEY_MOBILE_DATA_PREF = "mobile_data_enable"; @@ -178,8 +177,6 @@ public class MobileNetworkSettings extends AbstractMobileNetworkSettings impleme return Arrays.asList( new DataUsageSummaryPreferenceController(context, mSubId), - new RoamingPreferenceController(context, KEY_ROAMING_PREF, getSettingsLifecycle(), - this, mSubId), new CallsDefaultSubscriptionController(context, KEY_CALLS_PREF, getSettingsLifecycle(), this), new SmsDefaultSubscriptionController(context, KEY_SMS_PREF, getSettingsLifecycle(), @@ -263,8 +260,7 @@ public class MobileNetworkSettings extends AbstractMobileNetworkSettings impleme final RoamingPreferenceController roamingPreferenceController = use(RoamingPreferenceController.class); if (roamingPreferenceController != null) { - roamingPreferenceController.init(getFragmentManager(), mSubId, - mMobileNetworkInfoEntity); + roamingPreferenceController.init(getParentFragmentManager(), mSubId); } final SatelliteSettingPreferenceController satelliteSettingPreferenceController = use( SatelliteSettingPreferenceController.class); diff --git a/src/com/android/settings/network/telephony/RoamingPreferenceController.java b/src/com/android/settings/network/telephony/RoamingPreferenceController.java deleted file mode 100644 index bf02308be39..00000000000 --- a/src/com/android/settings/network/telephony/RoamingPreferenceController.java +++ /dev/null @@ -1,215 +0,0 @@ -/* - * Copyright (C) 2018 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.telephony; - -import static androidx.lifecycle.Lifecycle.Event.ON_START; -import static androidx.lifecycle.Lifecycle.Event.ON_STOP; - -import android.content.Context; -import android.os.PersistableBundle; -import android.telephony.CarrierConfigManager; -import android.telephony.SubscriptionManager; -import android.telephony.TelephonyManager; -import android.util.Log; - -import androidx.annotation.VisibleForTesting; -import androidx.fragment.app.FragmentManager; -import androidx.lifecycle.LifecycleObserver; -import androidx.lifecycle.LifecycleOwner; -import androidx.lifecycle.OnLifecycleEvent; -import androidx.preference.Preference; -import androidx.preference.PreferenceScreen; - -import com.android.settings.network.MobileNetworkRepository; -import com.android.settingslib.RestrictedSwitchPreference; -import com.android.settingslib.core.lifecycle.Lifecycle; -import com.android.settingslib.mobile.dataservice.MobileNetworkInfoEntity; - -import java.util.ArrayList; -import java.util.List; - -/** - * Preference controller for "Roaming" - */ -public class RoamingPreferenceController extends TelephonyTogglePreferenceController implements - LifecycleObserver, MobileNetworkRepository.MobileNetworkCallback { - private static final String TAG = "RoamingController"; - private static final String DIALOG_TAG = "MobileDataDialog"; - - private RestrictedSwitchPreference mSwitchPreference; - private TelephonyManager mTelephonyManager; - private CarrierConfigManager mCarrierConfigManager; - protected MobileNetworkRepository mMobileNetworkRepository; - protected LifecycleOwner mLifecycleOwner; - private List mMobileNetworkInfoEntityList = new ArrayList<>(); - - @VisibleForTesting - FragmentManager mFragmentManager; - MobileNetworkInfoEntity mMobileNetworkInfoEntity; - - public RoamingPreferenceController(Context context, String key, Lifecycle lifecycle, - LifecycleOwner lifecycleOwner, int subId) { - this(context, key); - mSubId = subId; - mLifecycleOwner = lifecycleOwner; - if (lifecycle != null) { - lifecycle.addObserver(this); - } - } - - public RoamingPreferenceController(Context context, String key) { - super(context, key); - mCarrierConfigManager = context.getSystemService(CarrierConfigManager.class); - mMobileNetworkRepository = MobileNetworkRepository.getInstance(context); - } - - @Override - public int getAvailabilityStatus() { - final PersistableBundle carrierConfig = mCarrierConfigManager.getConfigForSubId(mSubId); - if (carrierConfig != null && carrierConfig.getBoolean( - CarrierConfigManager.KEY_FORCE_HOME_NETWORK_BOOL)) { - return CONDITIONALLY_UNAVAILABLE; - } - return AVAILABLE; - } - - @OnLifecycleEvent(ON_START) - public void onStart() { - mMobileNetworkRepository.addRegister(mLifecycleOwner, this, mSubId); - mMobileNetworkRepository.updateEntity(); - } - - @OnLifecycleEvent(ON_STOP) - public void onStop() { - mMobileNetworkRepository.removeRegister(this); - } - - @Override - public void displayPreference(PreferenceScreen screen) { - super.displayPreference(screen); - mSwitchPreference = screen.findPreference(getPreferenceKey()); - } - - @Override - public int getAvailabilityStatus(int subId) { - return mSubId != SubscriptionManager.INVALID_SUBSCRIPTION_ID - ? AVAILABLE - : AVAILABLE_UNSEARCHABLE; - } - - @Override - public boolean setChecked(boolean isChecked) { - if (isDialogNeeded()) { - showDialog(); - } else { - // Update data directly if we don't need dialog - mTelephonyManager.setDataRoamingEnabled(isChecked); - return true; - } - - return false; - } - - @Override - public void updateState(Preference preference) { - super.updateState(preference); - mSwitchPreference = (RestrictedSwitchPreference) preference; - update(); - } - - private void update() { - if (mSwitchPreference == null) { - return; - } - if (!mSwitchPreference.isDisabledByAdmin()) { - mSwitchPreference.setEnabled(mSubId != SubscriptionManager.INVALID_SUBSCRIPTION_ID); - mSwitchPreference.setChecked(isChecked()); - } - } - - @VisibleForTesting - boolean isDialogNeeded() { - final boolean isRoamingEnabled = mMobileNetworkInfoEntity == null ? false - : mMobileNetworkInfoEntity.isDataRoamingEnabled; - final PersistableBundle carrierConfig = mCarrierConfigManager.getConfigForSubId( - mSubId); - // Need dialog if we need to turn on roaming and the roaming charge indication is allowed - if (!isRoamingEnabled && (carrierConfig == null || !carrierConfig.getBoolean( - CarrierConfigManager.KEY_DISABLE_CHARGE_INDICATION_BOOL))) { - return true; - } - return false; - } - - @Override - public boolean isChecked() { - return mMobileNetworkInfoEntity == null ? false - : mMobileNetworkInfoEntity.isDataRoamingEnabled; - } - - public void init(FragmentManager fragmentManager, int subId, MobileNetworkInfoEntity entity) { - mFragmentManager = fragmentManager; - mSubId = subId; - mMobileNetworkInfoEntity = entity; - mTelephonyManager = mContext.getSystemService(TelephonyManager.class); - if (mSubId == SubscriptionManager.INVALID_SUBSCRIPTION_ID) { - return; - } - final TelephonyManager telephonyManager = mTelephonyManager - .createForSubscriptionId(mSubId); - if (telephonyManager == null) { - Log.w(TAG, "fail to init in sub" + mSubId); - mSubId = SubscriptionManager.INVALID_SUBSCRIPTION_ID; - return; - } - mTelephonyManager = telephonyManager; - } - - private void showDialog() { - final RoamingDialogFragment dialogFragment = RoamingDialogFragment.newInstance(mSubId); - - dialogFragment.show(mFragmentManager, DIALOG_TAG); - } - - @VisibleForTesting - public void setMobileNetworkInfoEntity(MobileNetworkInfoEntity mobileNetworkInfoEntity) { - mMobileNetworkInfoEntity = mobileNetworkInfoEntity; - } - - @Override - public void onAllMobileNetworkInfoChanged( - List mobileNetworkInfoEntityList) { - mMobileNetworkInfoEntityList = mobileNetworkInfoEntityList; - mMobileNetworkInfoEntityList.forEach(entity -> { - if (Integer.parseInt(entity.subId) == mSubId) { - mMobileNetworkInfoEntity = entity; - update(); - refreshSummary(mSwitchPreference); - return; - } - }); - } - - @Override - public void onDataRoamingChanged(int subId, boolean enabled) { - if (subId != mSubId) { - Log.d(TAG, "onDataRoamingChanged - wrong subId : " + subId + " / " + enabled); - return; - } - update(); - } -} diff --git a/src/com/android/settings/network/telephony/RoamingPreferenceController.kt b/src/com/android/settings/network/telephony/RoamingPreferenceController.kt new file mode 100644 index 00000000000..2529d41324e --- /dev/null +++ b/src/com/android/settings/network/telephony/RoamingPreferenceController.kt @@ -0,0 +1,105 @@ +/* + * 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.telephony + +import android.content.Context +import android.os.UserManager +import android.telephony.CarrierConfigManager +import android.telephony.SubscriptionManager +import android.telephony.TelephonyManager +import androidx.annotation.VisibleForTesting +import androidx.compose.runtime.Composable +import androidx.compose.runtime.getValue +import androidx.compose.runtime.remember +import androidx.compose.ui.res.stringResource +import androidx.fragment.app.FragmentManager +import androidx.lifecycle.compose.collectAsStateWithLifecycle +import com.android.settings.R +import com.android.settings.spa.preference.ComposePreferenceController +import com.android.settingslib.spa.widget.preference.SwitchPreferenceModel +import com.android.settingslib.spaprivileged.model.enterprise.Restrictions +import com.android.settingslib.spaprivileged.template.preference.RestrictedSwitchPreference + +/** Preference controller for "Roaming" */ +class RoamingPreferenceController +@JvmOverloads +constructor( + context: Context, + key: String, + private val mobileDataRepository: MobileDataRepository = MobileDataRepository(context), +) : ComposePreferenceController(context, key) { + @VisibleForTesting var fragmentManager: FragmentManager? = null + private var subId = SubscriptionManager.INVALID_SUBSCRIPTION_ID + + private var telephonyManager = context.getSystemService(TelephonyManager::class.java)!! + private val carrierConfigRepository = CarrierConfigRepository(context) + + fun init(fragmentManager: FragmentManager, subId: Int) { + this.fragmentManager = fragmentManager + this.subId = subId + telephonyManager = telephonyManager.createForSubscriptionId(subId) + } + + override fun getAvailabilityStatus(): Int { + if (!SubscriptionManager.isValidSubscriptionId(subId)) return CONDITIONALLY_UNAVAILABLE + val isForceHomeNetwork = + carrierConfigRepository.getBoolean( + subId, CarrierConfigManager.KEY_FORCE_HOME_NETWORK_BOOL) + + return if (isForceHomeNetwork) CONDITIONALLY_UNAVAILABLE else AVAILABLE + } + + @Composable + override fun Content() { + val summary = stringResource(R.string.roaming_enable) + val isDataRoamingEnabled by + remember { mobileDataRepository.isDataRoamingEnabledFlow(subId) } + .collectAsStateWithLifecycle(null) + RestrictedSwitchPreference( + model = + object : SwitchPreferenceModel { + override val title = stringResource(R.string.roaming) + override val summary = { summary } + override val checked = { isDataRoamingEnabled } + override val onCheckedChange: (Boolean) -> Unit = { newChecked -> + if (newChecked && isDialogNeeded()) { + showDialog() + } else { + // Update data directly if we don't need dialog + telephonyManager.isDataRoamingEnabled = newChecked + } + } + }, + restrictions = Restrictions(keys = listOf(UserManager.DISALLOW_DATA_ROAMING)), + ) + } + + @VisibleForTesting + fun isDialogNeeded(): Boolean { + // Need dialog if we need to turn on roaming and the roaming charge indication is allowed + return !carrierConfigRepository.getBoolean( + subId, CarrierConfigManager.KEY_DISABLE_CHARGE_INDICATION_BOOL) + } + + private fun showDialog() { + fragmentManager?.let { RoamingDialogFragment.newInstance(subId).show(it, DIALOG_TAG) } + } + + companion object { + private const val DIALOG_TAG = "MobileDataDialog" + } +} diff --git a/tests/spa_unit/src/com/android/settings/deviceinfo/simstatus/SimStatusDialogRepositoryTest.kt b/tests/spa_unit/src/com/android/settings/deviceinfo/simstatus/SimStatusDialogRepositoryTest.kt index 01f32bfccf0..1c1d9df79ca 100644 --- a/tests/spa_unit/src/com/android/settings/deviceinfo/simstatus/SimStatusDialogRepositoryTest.kt +++ b/tests/spa_unit/src/com/android/settings/deviceinfo/simstatus/SimStatusDialogRepositoryTest.kt @@ -17,65 +17,65 @@ package com.android.settings.deviceinfo.simstatus import android.content.Context -import android.os.PersistableBundle import android.telephony.CarrierConfigManager import androidx.lifecycle.testing.TestLifecycleOwner import androidx.test.core.app.ApplicationProvider import androidx.test.ext.junit.runners.AndroidJUnit4 import com.android.settings.deviceinfo.simstatus.SimStatusDialogRepository.SimStatusDialogInfo +import com.android.settings.network.telephony.CarrierConfigRepository import com.android.settings.network.telephony.SimSlotRepository import com.android.settings.network.telephony.ims.ImsMmTelRepository import com.google.common.truth.Truth.assertThat import kotlinx.coroutines.delay import kotlinx.coroutines.flow.flowOf import kotlinx.coroutines.runBlocking +import org.junit.Before import org.junit.Test import org.junit.runner.RunWith -import org.mockito.kotlin.anyVararg import org.mockito.kotlin.doReturn -import org.mockito.kotlin.eq import org.mockito.kotlin.mock -import org.mockito.kotlin.spy @RunWith(AndroidJUnit4::class) class SimStatusDialogRepositoryTest { - private val carrierConfig = PersistableBundle().apply { - putBoolean(CarrierConfigManager.KEY_SHOW_IMS_REGISTRATION_STATUS_BOOL, true) - } + private val context: Context = ApplicationProvider.getApplicationContext() - private val mockCarrierConfigManager = mock { - on { getConfigForSubId(eq(SUB_ID), anyVararg()) } doReturn carrierConfig - } + private val mockSimSlotRepository = + mock { + on { subIdInSimSlotFlow(SIM_SLOT_INDEX) } doReturn flowOf(SUB_ID) + } - private val context: Context = spy(ApplicationProvider.getApplicationContext()) { - on { getSystemService(CarrierConfigManager::class.java) } doReturn mockCarrierConfigManager - } + private val mockSignalStrengthRepository = + mock { + on { signalStrengthDisplayFlow(SUB_ID) } doReturn flowOf(SIGNAL_STRENGTH) + } - private val mockSimSlotRepository = mock { - on { subIdInSimSlotFlow(SIM_SLOT_INDEX) } doReturn flowOf(SUB_ID) - } + private val mockImsMmTelRepository = + mock { on { imsRegisteredFlow() } doReturn flowOf(true) } - private val mockSignalStrengthRepository = mock { - on { signalStrengthDisplayFlow(SUB_ID) } doReturn flowOf(SIGNAL_STRENGTH) - } + private val controller = + SimStatusDialogRepository( + context = context, + simSlotRepository = mockSimSlotRepository, + signalStrengthRepository = mockSignalStrengthRepository, + imsMmTelRepositoryFactory = { subId -> + assertThat(subId).isEqualTo(SUB_ID) + mockImsMmTelRepository + }, + ) - private val mockImsMmTelRepository = mock { - on { imsRegisteredFlow() } doReturn flowOf(true) + @Before + fun setUp() { + CarrierConfigRepository.resetForTest() } - private val controller = SimStatusDialogRepository( - context = context, - simSlotRepository = mockSimSlotRepository, - signalStrengthRepository = mockSignalStrengthRepository, - imsMmTelRepositoryFactory = { subId -> - assertThat(subId).isEqualTo(SUB_ID) - mockImsMmTelRepository - }, - ) - @Test fun collectSimStatusDialogInfo() = runBlocking { + CarrierConfigRepository.setBooleanForTest( + subId = SUB_ID, + key = CarrierConfigManager.KEY_SHOW_IMS_REGISTRATION_STATUS_BOOL, + value = true, + ) var simStatusDialogInfo = SimStatusDialogInfo() controller.collectSimStatusDialogInfo(TestLifecycleOwner(), SIM_SLOT_INDEX) { @@ -83,19 +83,20 @@ class SimStatusDialogRepositoryTest { } delay(100) - assertThat(simStatusDialogInfo).isEqualTo( - SimStatusDialogInfo( - signalStrength = SIGNAL_STRENGTH, - imsRegistered = true, - ) - ) + assertThat(simStatusDialogInfo) + .isEqualTo( + SimStatusDialogInfo( + signalStrength = SIGNAL_STRENGTH, + imsRegistered = true, + )) } @Test fun collectSimStatusDialogInfo_doNotShowSignalStrength() = runBlocking { - carrierConfig.putBoolean( - CarrierConfigManager.KEY_SHOW_SIGNAL_STRENGTH_IN_SIM_STATUS_BOOL, - false + CarrierConfigRepository.setBooleanForTest( + subId = SUB_ID, + key = CarrierConfigManager.KEY_SHOW_SIGNAL_STRENGTH_IN_SIM_STATUS_BOOL, + value = false, ) var simStatusDialogInfo = SimStatusDialogInfo() @@ -109,7 +110,11 @@ class SimStatusDialogRepositoryTest { @Test fun collectSimStatusDialogInfo_doNotShowImsRegistration() = runBlocking { - carrierConfig.putBoolean(CarrierConfigManager.KEY_SHOW_IMS_REGISTRATION_STATUS_BOOL, false) + CarrierConfigRepository.setBooleanForTest( + subId = SUB_ID, + key = CarrierConfigManager.KEY_SHOW_IMS_REGISTRATION_STATUS_BOOL, + value = false, + ) var simStatusDialogInfo = SimStatusDialogInfo() controller.collectSimStatusDialogInfo(TestLifecycleOwner(), SIM_SLOT_INDEX) { diff --git a/tests/spa_unit/src/com/android/settings/network/telephony/CarrierConfigRepositoryTest.kt b/tests/spa_unit/src/com/android/settings/network/telephony/CarrierConfigRepositoryTest.kt new file mode 100644 index 00000000000..8c54751da35 --- /dev/null +++ b/tests/spa_unit/src/com/android/settings/network/telephony/CarrierConfigRepositoryTest.kt @@ -0,0 +1,138 @@ +/* + * 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.telephony + +import android.content.Context +import android.telephony.CarrierConfigManager +import androidx.core.os.persistableBundleOf +import androidx.test.ext.junit.runners.AndroidJUnit4 +import com.google.common.truth.Truth.assertThat +import org.junit.Before +import org.junit.Test +import org.junit.runner.RunWith +import org.mockito.kotlin.any +import org.mockito.kotlin.anyVararg +import org.mockito.kotlin.doReturn +import org.mockito.kotlin.doThrow +import org.mockito.kotlin.eq +import org.mockito.kotlin.mock +import org.mockito.kotlin.stub +import org.mockito.kotlin.times +import org.mockito.kotlin.verify + +@RunWith(AndroidJUnit4::class) +class CarrierConfigRepositoryTest { + + private val mockCarrierConfigManager = mock() + + private val context = + mock { + on { applicationContext } doReturn mock + on { getSystemService(CarrierConfigManager::class.java) } doReturn + mockCarrierConfigManager + } + + private val repository = CarrierConfigRepository(context) + + @Before + fun setUp() { + CarrierConfigRepository.resetForTest() + } + + @Test + fun getBoolean_returnValue() { + val key = CarrierConfigManager.KEY_CARRIER_CONFIG_APPLIED_BOOL + mockCarrierConfigManager.stub { + on { getConfigForSubId(any(), eq(key)) } doReturn persistableBundleOf(key to true) + } + + val value = repository.getBoolean(SUB_ID, key) + + assertThat(value).isTrue() + } + + @Test + fun getInt_returnValue() { + val key = CarrierConfigManager.KEY_GBA_MODE_INT + mockCarrierConfigManager.stub { + on { getConfigForSubId(any(), eq(key)) } doReturn persistableBundleOf(key to 99) + } + + val value = repository.getInt(SUB_ID, key) + + assertThat(value).isEqualTo(99) + } + + @Test + fun getString_returnValue() { + val key = CarrierConfigManager.KEY_CARRIER_NAME_STRING + mockCarrierConfigManager.stub { + on { getConfigForSubId(any(), eq(key)) } doReturn + persistableBundleOf(key to STRING_VALUE) + } + + val value = repository.getString(SUB_ID, key) + + assertThat(value).isEqualTo(STRING_VALUE) + } + + @Test + fun transformConfig_managerThrowIllegalStateException_returnDefaultValue() { + mockCarrierConfigManager.stub { + on { getConfigForSubId(any(), anyVararg()) } doThrow IllegalStateException() + } + + val carrierName = + repository.transformConfig(SUB_ID) { + getInt(CarrierConfigManager.KEY_CARRIER_DEFAULT_WFC_IMS_MODE_INT) + } + + assertThat(carrierName) + .isEqualTo( + CarrierConfigManager.getDefaultConfig() + .getInt(CarrierConfigManager.KEY_CARRIER_DEFAULT_WFC_IMS_MODE_INT)) + } + + @Test + fun transformConfig_getValueTwice_cached() { + val key = CarrierConfigManager.KEY_CARRIER_NAME_STRING + mockCarrierConfigManager.stub { + on { getConfigForSubId(any(), eq(key)) } doReturn + persistableBundleOf(key to STRING_VALUE) + } + + repository.transformConfig(SUB_ID) { getString(key) } + repository.transformConfig(SUB_ID) { getString(key) } + + verify(mockCarrierConfigManager, times(1)).getConfigForSubId(any(), anyVararg()) + } + + @Test + fun transformConfig_registerCarrierConfigChangeListener() { + val key = CarrierConfigManager.KEY_CARRIER_NAME_STRING + + repository.transformConfig(SUB_ID) { getString(key) } + repository.transformConfig(SUB_ID) { getString(key) } + + verify(mockCarrierConfigManager, times(1)).registerCarrierConfigChangeListener(any(), any()) + } + + private companion object { + const val SUB_ID = 123 + const val STRING_VALUE = "value" + } +} diff --git a/tests/spa_unit/src/com/android/settings/network/telephony/MobileDataRepositoryTest.kt b/tests/spa_unit/src/com/android/settings/network/telephony/MobileDataRepositoryTest.kt index 268be570048..fc762fa894a 100644 --- a/tests/spa_unit/src/com/android/settings/network/telephony/MobileDataRepositoryTest.kt +++ b/tests/spa_unit/src/com/android/settings/network/telephony/MobileDataRepositoryTest.kt @@ -162,6 +162,26 @@ class MobileDataRepositoryTest { assertThat(state.firstWithTimeoutOrNull()).isTrue() } + @Test + fun isDataRoamingEnabledFlow_invalidSub_returnFalse() = runBlocking { + val isDataRoamingEnabled = + repository + .isDataRoamingEnabledFlow(subId = SubscriptionManager.INVALID_SUBSCRIPTION_ID) + .firstWithTimeoutOrNull() + + assertThat(isDataRoamingEnabled).isFalse() + } + + @Test + fun isDataRoamingEnabledFlow_validSub_returnCurrentValue() = runBlocking { + mockTelephonyManager.stub { on { isDataRoamingEnabled } doReturn true } + + val isDataRoamingEnabled = + repository.isDataRoamingEnabledFlow(subId = SUB_ID).firstWithTimeoutOrNull() + + assertThat(isDataRoamingEnabled).isTrue() + } + private companion object { const val SUB_ID = 123 } diff --git a/tests/spa_unit/src/com/android/settings/network/telephony/RoamingPreferenceControllerTest.kt b/tests/spa_unit/src/com/android/settings/network/telephony/RoamingPreferenceControllerTest.kt new file mode 100644 index 00000000000..ee4cff684ca --- /dev/null +++ b/tests/spa_unit/src/com/android/settings/network/telephony/RoamingPreferenceControllerTest.kt @@ -0,0 +1,194 @@ +/* + * 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.telephony + +import android.content.Context +import android.telephony.CarrierConfigManager +import android.telephony.SubscriptionManager +import androidx.compose.runtime.CompositionLocalProvider +import androidx.compose.ui.platform.LocalContext +import androidx.compose.ui.test.assertIsDisplayed +import androidx.compose.ui.test.assertIsOff +import androidx.compose.ui.test.assertIsOn +import androidx.compose.ui.test.junit4.createComposeRule +import androidx.compose.ui.test.onNodeWithText +import androidx.fragment.app.FragmentManager +import androidx.test.core.app.ApplicationProvider +import androidx.test.ext.junit.runners.AndroidJUnit4 +import com.android.settings.R +import com.android.settings.core.BasePreferenceController.AVAILABLE +import com.android.settings.core.BasePreferenceController.CONDITIONALLY_UNAVAILABLE +import com.google.common.truth.Truth.assertThat +import kotlinx.coroutines.flow.flowOf +import org.junit.Before +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 RoamingPreferenceControllerTest { + @get:Rule val composeTestRule = createComposeRule() + + private val context: Context = ApplicationProvider.getApplicationContext() + + private val mockMobileDataRepository = + mock { + on { isDataRoamingEnabledFlow(SUB_ID) } doReturn flowOf(false) + } + + private val controller = + RoamingPreferenceController(context, TEST_KEY, mockMobileDataRepository) + + @Before + fun setUp() { + CarrierConfigRepository.resetForTest() + } + + @Test + fun getAvailabilityStatus_validSubId_returnAvailable() { + controller.init(mock(), SUB_ID) + + val availabilityStatus = controller.getAvailabilityStatus() + + assertThat(availabilityStatus).isEqualTo(AVAILABLE) + } + + @Test + fun getAvailabilityStatus_invalidSubId_returnConditionallyUnavailable() { + controller.init(mock(), SubscriptionManager.INVALID_SUBSCRIPTION_ID) + + val availabilityStatus = controller.getAvailabilityStatus() + + assertThat(availabilityStatus).isEqualTo(CONDITIONALLY_UNAVAILABLE) + } + + @Test + fun getAvailabilityStatus_forceHomeNetworkIsTrue_returnConditionallyUnavailable() { + CarrierConfigRepository.setBooleanForTest( + subId = SUB_ID, + key = CarrierConfigManager.KEY_FORCE_HOME_NETWORK_BOOL, + value = true, + ) + controller.init(mock(), SUB_ID) + + val availabilityStatus = controller.getAvailabilityStatus() + + assertThat(availabilityStatus).isEqualTo(CONDITIONALLY_UNAVAILABLE) + } + + @Test + fun getAvailabilityStatus_forceHomeNetworkIsFalse_returnAvailable() { + CarrierConfigRepository.setBooleanForTest( + subId = SUB_ID, + key = CarrierConfigManager.KEY_FORCE_HOME_NETWORK_BOOL, + value = false, + ) + controller.init(mock(), SUB_ID) + + val availabilityStatus = controller.getAvailabilityStatus() + + assertThat(availabilityStatus).isEqualTo(AVAILABLE) + } + + @Test + fun title_displayed() { + controller.init(mock(), SUB_ID) + + composeTestRule.setContent { + CompositionLocalProvider(LocalContext provides context) { controller.Content() } + } + + composeTestRule.onNodeWithText(context.getString(R.string.roaming)).assertIsDisplayed() + } + + @Test + fun summary_displayed() { + controller.init(mock(), SUB_ID) + + composeTestRule.setContent { + CompositionLocalProvider(LocalContext provides context) { controller.Content() } + } + + composeTestRule + .onNodeWithText(context.getString(R.string.roaming_enable)) + .assertIsDisplayed() + } + + @Test + fun isDialogNeeded_enableChargeIndication_returnTrue() { + CarrierConfigRepository.setBooleanForTest( + subId = SUB_ID, + key = CarrierConfigManager.KEY_DISABLE_CHARGE_INDICATION_BOOL, + value = false, + ) + controller.init(mock(), SUB_ID) + + val isDialogNeeded = controller.isDialogNeeded() + + assertThat(isDialogNeeded).isTrue() + } + + @Test + fun isDialogNeeded_disableChargeIndication_returnFalse() { + CarrierConfigRepository.setBooleanForTest( + subId = SUB_ID, + key = CarrierConfigManager.KEY_DISABLE_CHARGE_INDICATION_BOOL, + value = true, + ) + controller.init(mock(), SUB_ID) + + val isDialogNeeded = controller.isDialogNeeded() + + assertThat(isDialogNeeded).isFalse() + } + + @Test + fun checked_roamingEnabled_isOn() { + mockMobileDataRepository.stub { + on { isDataRoamingEnabledFlow(SUB_ID) } doReturn flowOf(true) + } + controller.init(mock(), SUB_ID) + + composeTestRule.setContent { + CompositionLocalProvider(LocalContext provides context) { controller.Content() } + } + + composeTestRule.onNodeWithText(context.getString(R.string.roaming)).assertIsOn() + } + + @Test + fun checked_roamingDisabled_isOff() { + mockMobileDataRepository.stub { + on { isDataRoamingEnabledFlow(SUB_ID) } doReturn flowOf(false) + } + controller.init(mock(), SUB_ID) + + composeTestRule.setContent { + CompositionLocalProvider(LocalContext provides context) { controller.Content() } + } + + composeTestRule.onNodeWithText(context.getString(R.string.roaming)).assertIsOff() + } + + private companion object { + const val TEST_KEY = "test_key" + const val SUB_ID = 2 + } +} diff --git a/tests/unit/src/com/android/settings/network/telephony/RoamingPreferenceControllerTest.java b/tests/unit/src/com/android/settings/network/telephony/RoamingPreferenceControllerTest.java deleted file mode 100644 index d221280ae48..00000000000 --- a/tests/unit/src/com/android/settings/network/telephony/RoamingPreferenceControllerTest.java +++ /dev/null @@ -1,234 +0,0 @@ -/* - * Copyright (C) 2020 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.telephony; - -import static com.android.settings.core.BasePreferenceController.AVAILABLE; -import static com.android.settings.core.BasePreferenceController.CONDITIONALLY_UNAVAILABLE; - -import static com.google.common.truth.Truth.assertThat; - -import static org.mockito.ArgumentMatchers.anyBoolean; -import static org.mockito.Mockito.doReturn; -import static org.mockito.Mockito.mock; -import static org.mockito.Mockito.never; -import static org.mockito.Mockito.spy; -import static org.mockito.Mockito.verify; -import static org.mockito.Mockito.when; - -import android.content.Context; -import android.os.Looper; -import android.os.PersistableBundle; -import android.telephony.CarrierConfigManager; -import android.telephony.SubscriptionManager; -import android.telephony.TelephonyManager; - -import androidx.fragment.app.FragmentManager; -import androidx.fragment.app.FragmentTransaction; -import androidx.lifecycle.LifecycleOwner; -import androidx.lifecycle.LifecycleRegistry; -import androidx.test.annotation.UiThreadTest; -import androidx.test.core.app.ApplicationProvider; -import androidx.test.ext.junit.runners.AndroidJUnit4; - -import com.android.settings.core.BasePreferenceController; -import com.android.settingslib.RestrictedSwitchPreference; -import com.android.settingslib.core.lifecycle.Lifecycle; -import com.android.settingslib.mobile.dataservice.MobileNetworkInfoEntity; - -import org.junit.Before; -import org.junit.Test; -import org.junit.runner.RunWith; -import org.mockito.Mock; -import org.mockito.MockitoAnnotations; - -@RunWith(AndroidJUnit4.class) -public class RoamingPreferenceControllerTest { - private static final int SUB_ID = 2; - - @Mock - private FragmentManager mFragmentManager; - @Mock - private TelephonyManager mTelephonyManager; - @Mock - private TelephonyManager mInvalidTelephonyManager; - @Mock - private SubscriptionManager mSubscriptionManager; - @Mock - private FragmentTransaction mFragmentTransaction; - @Mock - private CarrierConfigManager mCarrierConfigManager; - @Mock - private Lifecycle mLifecycle; - @Mock - private LifecycleOwner mLifecycleOwner; - - private LifecycleRegistry mLifecycleRegistry; - private RoamingPreferenceController mController; - private RestrictedSwitchPreference mPreference; - private Context mContext; - private MobileNetworkInfoEntity mMobileNetworkInfoEntity; - - @Before - public void setUp() { - MockitoAnnotations.initMocks(this); - - if (Looper.myLooper() == null) { - Looper.prepare(); - } - - mContext = spy(ApplicationProvider.getApplicationContext()); - doReturn(mTelephonyManager).when(mContext).getSystemService(Context.TELEPHONY_SERVICE); - doReturn(mSubscriptionManager).when(mContext).getSystemService( - Context.TELEPHONY_SUBSCRIPTION_SERVICE); - - doReturn(mCarrierConfigManager).when(mContext).getSystemService( - Context.CARRIER_CONFIG_SERVICE); - doReturn(mTelephonyManager).when(mTelephonyManager).createForSubscriptionId(SUB_ID); - doReturn(mInvalidTelephonyManager).when(mTelephonyManager).createForSubscriptionId( - SubscriptionManager.INVALID_SUBSCRIPTION_ID); - doReturn(mFragmentTransaction).when(mFragmentManager).beginTransaction(); - - mPreference = spy(new RestrictedSwitchPreference(mContext)); - mController = spy( - new RoamingPreferenceController(mContext, "roaming", mLifecycle, mLifecycleOwner, - SUB_ID)); - mLifecycleRegistry = new LifecycleRegistry(mLifecycleOwner); - when(mLifecycleOwner.getLifecycle()).thenReturn(mLifecycleRegistry); - mController.init(mFragmentManager, SUB_ID, mMobileNetworkInfoEntity); - mPreference.setKey(mController.getPreferenceKey()); - } - - private MobileNetworkInfoEntity setupMobileNetworkInfoEntity(String subId, - boolean isDataRoaming) { - return new MobileNetworkInfoEntity(subId, false, false, true, false, false, false, false, - false, false, false, isDataRoaming); - } - - @Test - public void getAvailabilityStatus_validSubId_returnAvailable() { - assertThat(mController.getAvailabilityStatus()).isEqualTo( - AVAILABLE); - } - - @Test - public void getAvailabilityStatus_invalidSubId_returnUnsearchable() { - mController.init(mFragmentManager, SubscriptionManager.INVALID_SUBSCRIPTION_ID, - mMobileNetworkInfoEntity); - - assertThat(mController.getAvailabilityStatus( - SubscriptionManager.INVALID_SUBSCRIPTION_ID)).isEqualTo( - BasePreferenceController.AVAILABLE_UNSEARCHABLE); - } - - @Test - public void isDialogNeeded_roamingDisabledWithoutFlag_returnTrue() { - final PersistableBundle bundle = new PersistableBundle(); - bundle.putBoolean(CarrierConfigManager.KEY_DISABLE_CHARGE_INDICATION_BOOL, false); - doReturn(bundle).when(mCarrierConfigManager).getConfigForSubId(SUB_ID); - mMobileNetworkInfoEntity = setupMobileNetworkInfoEntity(String.valueOf(SUB_ID), false); - mController.setMobileNetworkInfoEntity(mMobileNetworkInfoEntity); - - assertThat(mController.isDialogNeeded()).isTrue(); - } - - @Test - public void isDialogNeeded_roamingEnabled_returnFalse() { - mMobileNetworkInfoEntity = setupMobileNetworkInfoEntity(String.valueOf(SUB_ID), true); - mController.setMobileNetworkInfoEntity(mMobileNetworkInfoEntity); - - assertThat(mController.isDialogNeeded()).isFalse(); - } - - @Test - @UiThreadTest - public void setChecked_needDialog_showDialog() { - mMobileNetworkInfoEntity = setupMobileNetworkInfoEntity(String.valueOf(SUB_ID), false); - mController.setMobileNetworkInfoEntity(mMobileNetworkInfoEntity); - doReturn(null).when(mCarrierConfigManager).getConfigForSubId(SUB_ID); - - mController.setChecked(true); - - verify(mFragmentManager).beginTransaction(); - } - - @Test - public void updateState_invalidSubId_disabled() { - mMobileNetworkInfoEntity = setupMobileNetworkInfoEntity( - String.valueOf(SubscriptionManager.INVALID_SUBSCRIPTION_ID), false); - mController.setMobileNetworkInfoEntity(mMobileNetworkInfoEntity); - mController.init(mFragmentManager, SubscriptionManager.INVALID_SUBSCRIPTION_ID, - mMobileNetworkInfoEntity); - - mController.updateState(mPreference); - - assertThat(mPreference.isEnabled()).isFalse(); - } - - @Test - public void updateState_validSubId_enabled() { - mMobileNetworkInfoEntity = setupMobileNetworkInfoEntity(String.valueOf(SUB_ID), true); - mController.setMobileNetworkInfoEntity(mMobileNetworkInfoEntity); - - mController.updateState(mPreference); - - assertThat(mPreference.isEnabled()).isTrue(); - assertThat(mPreference.isChecked()).isTrue(); - } - - @Test - public void updateState_isNotDisabledByAdmin_shouldInvokeSetEnabled() { - when(mPreference.isDisabledByAdmin()).thenReturn(false); - - mController.updateState(mPreference); - - verify(mPreference).setEnabled(anyBoolean()); - } - - @Test - public void updateState_isDisabledByAdmin_shouldNotInvokeSetEnabled() { - when(mPreference.isDisabledByAdmin()).thenReturn(true); - - mController.updateState(mPreference); - - verify(mPreference, never()).setEnabled(anyBoolean()); - } - - @Test - public void getAvailabilityStatus_carrierConfigIsNull_shouldReturnAvailable() { - doReturn(null).when(mCarrierConfigManager).getConfigForSubId(SUB_ID); - - assertThat(mController.getAvailabilityStatus()).isEqualTo(AVAILABLE); - } - - @Test - public void getAvailabilityStatus_forceHomeNetworkIsFalse_shouldReturnAvailable() { - final PersistableBundle bundle = new PersistableBundle(); - bundle.putBoolean(CarrierConfigManager.KEY_FORCE_HOME_NETWORK_BOOL, false); - doReturn(bundle).when(mCarrierConfigManager).getConfigForSubId(SUB_ID); - - assertThat(mController.getAvailabilityStatus()).isEqualTo(AVAILABLE); - } - - @Test - public void getAvailabilityStatus_forceHomeNetworkIsTrue_shouldReturnConditionallyAvailable() { - final PersistableBundle bundle = new PersistableBundle(); - bundle.putBoolean(CarrierConfigManager.KEY_FORCE_HOME_NETWORK_BOOL, true); - doReturn(bundle).when(mCarrierConfigManager).getConfigForSubId(SUB_ID); - - assertThat(mController.getAvailabilityStatus()).isEqualTo(CONDITIONALLY_UNAVAILABLE); - } -}