diff --git a/app-common/src/main/java/im/angry/openeuicc/ui/EuiccManagementFragment.kt b/app-common/src/main/java/im/angry/openeuicc/ui/EuiccManagementFragment.kt index c547204..b232339 100644 --- a/app-common/src/main/java/im/angry/openeuicc/ui/EuiccManagementFragment.kt +++ b/app-common/src/main/java/im/angry/openeuicc/ui/EuiccManagementFragment.kt @@ -29,7 +29,9 @@ import im.angry.openeuicc.core.EuiccChannelManager import im.angry.openeuicc.util.* import kotlinx.coroutines.Dispatchers import kotlinx.coroutines.TimeoutCancellationException +import kotlinx.coroutines.flow.StateFlow import kotlinx.coroutines.flow.first +import kotlinx.coroutines.flow.stateIn import kotlinx.coroutines.launch import kotlinx.coroutines.withContext @@ -52,6 +54,10 @@ open class EuiccManagementFragment : Fragment(), EuiccProfilesChangedListener, // e.g. after a failed enable / disable operation private var invalid = false + // Subscribe to settings we care about outside of coroutine contexts while initializing + // This gives us access to the "latest" state without having to launch coroutines + private lateinit var disableSafeguardFlow: StateFlow + override fun onCreate(savedInstanceState: Bundle?) { super.onCreate(savedInstanceState) setHasOptionsMenu(true) @@ -115,6 +121,11 @@ open class EuiccManagementFragment : Fragment(), EuiccProfilesChangedListener, swipeRefresh.isRefreshing = true lifecycleScope.launch { + if (!this@EuiccManagementFragment::disableSafeguardFlow.isInitialized) { + disableSafeguardFlow = + preferenceRepository.disableSafeguardFlow.stateIn(lifecycleScope) + } + val profiles = withContext(Dispatchers.IO) { euiccChannelManager.notifyEuiccProfilesChanged(channel.logicalSlotId) channel.lpa.profiles @@ -219,8 +230,8 @@ open class EuiccManagementFragment : Fragment(), EuiccProfilesChangedListener, // We hide the disable option by default to avoid "bricking" some cards that won't get // recognized again by the phone's modem. However we don't have that worry if we are - // accessing it through a USB card reader - if (isUsb) { + // accessing it through a USB card reader, or when the user explicitly opted in + if (isUsb || disableSafeguardFlow.value) { popup.menu.findItem(R.id.disable).isVisible = true } } diff --git a/app-common/src/main/java/im/angry/openeuicc/ui/SettingsFragment.kt b/app-common/src/main/java/im/angry/openeuicc/ui/SettingsFragment.kt index e3336a0..d471efa 100644 --- a/app-common/src/main/java/im/angry/openeuicc/ui/SettingsFragment.kt +++ b/app-common/src/main/java/im/angry/openeuicc/ui/SettingsFragment.kt @@ -41,6 +41,9 @@ class SettingsFragment: PreferenceFragmentCompat() { findPreference("pref_notifications_switch") ?.bindBooleanFlow(preferenceRepository.notificationSwitchFlow, PreferenceKeys.NOTIFICATION_SWITCH) + + findPreference("pref_advanced_disable_safeguard_removable_esim") + ?.bindBooleanFlow(preferenceRepository.disableSafeguardFlow, PreferenceKeys.DISABLE_SAFEGUARD_REMOVABLE_ESIM) } private fun CheckBoxPreference.bindBooleanFlow(flow: Flow, key: Preferences.Key) { diff --git a/app-common/src/main/java/im/angry/openeuicc/util/PreferenceUtils.kt b/app-common/src/main/java/im/angry/openeuicc/util/PreferenceUtils.kt index e72301c..5315340 100644 --- a/app-common/src/main/java/im/angry/openeuicc/util/PreferenceUtils.kt +++ b/app-common/src/main/java/im/angry/openeuicc/util/PreferenceUtils.kt @@ -23,6 +23,7 @@ object PreferenceKeys { val NOTIFICATION_DOWNLOAD = booleanPreferencesKey("notification_download") val NOTIFICATION_DELETE = booleanPreferencesKey("notification_delete") val NOTIFICATION_SWITCH = booleanPreferencesKey("notification_switch") + val DISABLE_SAFEGUARD_REMOVABLE_ESIM = booleanPreferencesKey("disable_safeguard_removable_esim") } class PreferenceRepository(context: Context) { @@ -39,6 +40,10 @@ class PreferenceRepository(context: Context) { val notificationSwitchFlow: Flow = dataStore.data.map { it[PreferenceKeys.NOTIFICATION_SWITCH] ?: false } + // ---- Advanced ---- + val disableSafeguardFlow: Flow = + dataStore.data.map { it[PreferenceKeys.DISABLE_SAFEGUARD_REMOVABLE_ESIM] ?: false } + suspend fun updatePreference(key: Preferences.Key, value: T) { dataStore.edit { it[key] = value diff --git a/app-common/src/main/res/values/strings.xml b/app-common/src/main/res/values/strings.xml index 21b84d6..f859014 100644 --- a/app-common/src/main/res/values/strings.xml +++ b/app-common/src/main/res/values/strings.xml @@ -70,6 +70,8 @@ Switching Send notifications for switching profiles\nNote that this type of notification is unreliable. Advanced + Disable Safeguards for Removable eSIMs + By default, this app prevents you from disabling the active profile on a removable eSIM inserted in the device, because doing so may render it inaccessible.\nCheck this box to remove this safeguard. Logs View recent debug logs of the application Info diff --git a/app-common/src/main/res/xml/pref_settings.xml b/app-common/src/main/res/xml/pref_settings.xml index 34466fd..db348fc 100644 --- a/app-common/src/main/res/xml/pref_settings.xml +++ b/app-common/src/main/res/xml/pref_settings.xml @@ -24,6 +24,12 @@ + +