diff --git a/app-common/src/main/java/im/angry/openeuicc/service/EuiccChannelManagerService.kt b/app-common/src/main/java/im/angry/openeuicc/service/EuiccChannelManagerService.kt index 760f1af..ac5e0d7 100644 --- a/app-common/src/main/java/im/angry/openeuicc/service/EuiccChannelManagerService.kt +++ b/app-common/src/main/java/im/angry/openeuicc/service/EuiccChannelManagerService.kt @@ -11,6 +11,7 @@ import androidx.core.app.NotificationManagerCompat import androidx.lifecycle.LifecycleService import androidx.lifecycle.lifecycleScope import im.angry.openeuicc.common.R +import im.angry.openeuicc.core.EuiccChannel import im.angry.openeuicc.core.EuiccChannelManager import im.angry.openeuicc.util.* import kotlinx.coroutines.Dispatchers @@ -446,30 +447,30 @@ class EuiccChannelManagerService : LifecycleService(), OpenEuiccContextMarker { iccid: String, enable: Boolean, // Enable or disable the profile indicated in iccid reconnectTimeoutMillis: Long = 0 // 0 = do not wait for reconnect - ): ForegroundTaskSubscriberFlow = + ) = launchForegroundTask( getString(R.string.task_profile_switch), getString(R.string.task_profile_switch_failure), R.drawable.ic_task_switch ) { - euiccChannelManager.beginTrackedOperation(slotId, portId) { - val (res, refreshed) = euiccChannelManager.withEuiccChannel( - slotId, - portId - ) { channel -> - if (!channel.lpa.switchProfile(iccid, enable, refresh = true)) { - // Sometimes, we *can* enable or disable the profile, but we cannot - // send the refresh command to the modem because the profile somehow - // makes the modem "busy". In this case, we can still switch by setting - // refresh to false, but then the switch cannot take effect until the - // user resets the modem manually by toggling airplane mode or rebooting. - Pair(channel.lpa.switchProfile(iccid, enable, refresh = false), false) - } else { - Pair(true, true) - } - } + suspend fun onSwitch(channel: EuiccChannel): Pair { + val refresh = preferenceRepository.refreshAfterSwitchFlow.first() + val response = channel.lpa.switchProfile(iccid, enable, refresh) + if (response || !refresh) return Pair(response, refresh) + // refresh failed, but refresh was requested + // Sometimes, we *can* enable or disable the profile, but we cannot + // send the refresh command to the modem because the profile somehow + // makes the modem "busy". In this case, we can still switch by setting + // refresh to false, but then the switch cannot take effect until the + // user resets the modem manually by toggling airplane mode or rebooting. + return Pair(channel.lpa.switchProfile(iccid, enable, refresh = false), false) + } - if (!res) { + euiccChannelManager.beginTrackedOperation(slotId, portId) { + val (response, refreshed) = + euiccChannelManager.withEuiccChannel(slotId, portId, ::onSwitch) + + if (!response) { throw RuntimeException("Could not switch profile") } 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 fab680f..c54d6a1 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 @@ -122,7 +122,7 @@ open class SettingsFragment: PreferenceFragmentCompat() { return true } - private fun CheckBoxPreference.bindBooleanFlow(flow: PreferenceFlowWrapper) { + protected fun CheckBoxPreference.bindBooleanFlow(flow: PreferenceFlowWrapper) { lifecycleScope.launch { flow.collect { isChecked = it } } 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 f5e3ca2..b38eb5d 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 @@ -26,6 +26,7 @@ internal object PreferenceKeys { val NOTIFICATION_SWITCH = booleanPreferencesKey("notification_switch") // ---- Advanced ---- + val REFRESH_AFTER_SWITCH = booleanPreferencesKey("refresh_after_switch") val DISABLE_SAFEGUARD_REMOVABLE_ESIM = booleanPreferencesKey("disable_safeguard_removable_esim") val VERBOSE_LOGGING = booleanPreferencesKey("verbose_logging") @@ -47,6 +48,7 @@ class PreferenceRepository(private val context: Context) { val verboseLoggingFlow = bindFlow(PreferenceKeys.VERBOSE_LOGGING, false) // ---- Developer Options ---- + val refreshAfterSwitchFlow = bindFlow(PreferenceKeys.REFRESH_AFTER_SWITCH, true) val developerOptionsEnabledFlow = bindFlow(PreferenceKeys.DEVELOPER_OPTIONS_ENABLED, false) val unfilteredProfileListFlow = bindFlow(PreferenceKeys.UNFILTERED_PROFILE_LIST, false) val ignoreTLSCertificateFlow = bindFlow(PreferenceKeys.IGNORE_TLS_CERTIFICATE, false) @@ -58,15 +60,12 @@ class PreferenceRepository(private val context: Context) { class PreferenceFlowWrapper private constructor( private val context: Context, private val key: Preferences.Key, - inner: Flow + inner: Flow, ) : Flow by inner { - internal constructor(context: Context, key: Preferences.Key, defaultValue: T) : this( - context, - key, - context.dataStore.data.map { it[key] ?: defaultValue } - ) + internal constructor(context: Context, key: Preferences.Key, defaultValue: T) : + this(context, key, context.dataStore.data.map { it[key] ?: defaultValue }) suspend fun updatePreference(value: T) { context.dataStore.edit { it[key] = value } } -} \ No newline at end of file +} diff --git a/app-common/src/main/res/values/strings.xml b/app-common/src/main/res/values/strings.xml index a45ce1f..a0c2d98 100644 --- a/app-common/src/main/res/values/strings.xml +++ b/app-common/src/main/res/values/strings.xml @@ -162,6 +162,8 @@ Switching Send notifications for switching profiles\nNote that this type of notification is unreliable. Advanced + Refresh status after switching + If crash after switch, Please trying disable the function Allow Disabling / Deleting Active Profile By default, this app prevents you from disabling the active profile on a removable eSIM inserted in the device, because doing so may sometimes render it inaccessible.\nCheck this box to remove this safeguard. Verbose Logging diff --git a/app-common/src/main/res/xml/pref_settings.xml b/app-common/src/main/res/xml/pref_settings.xml index bb5bd50..9d2f189 100644 --- a/app-common/src/main/res/xml/pref_settings.xml +++ b/app-common/src/main/res/xml/pref_settings.xml @@ -60,12 +60,14 @@ diff --git a/app-unpriv/src/main/java/im/angry/openeuicc/ui/UnprivilegedSettingsFragment.kt b/app-unpriv/src/main/java/im/angry/openeuicc/ui/UnprivilegedSettingsFragment.kt index 23589a6..31c9a77 100644 --- a/app-unpriv/src/main/java/im/angry/openeuicc/ui/UnprivilegedSettingsFragment.kt +++ b/app-unpriv/src/main/java/im/angry/openeuicc/ui/UnprivilegedSettingsFragment.kt @@ -6,9 +6,11 @@ import android.content.pm.PackageManager import android.os.Build import android.os.Bundle import android.widget.Toast +import androidx.preference.CheckBoxPreference import androidx.preference.Preference import im.angry.easyeuicc.R import im.angry.openeuicc.util.encodeHex +import im.angry.openeuicc.util.preferenceRepository import java.security.MessageDigest class UnprivilegedSettingsFragment : SettingsFragment() { @@ -29,8 +31,12 @@ class UnprivilegedSettingsFragment : SettingsFragment() { override fun onCreatePreferences(savedInstanceState: Bundle?, rootKey: String?) { super.onCreatePreferences(savedInstanceState, rootKey) addPreferencesFromResource(R.xml.pref_unprivileged_settings) + mergePreferenceOverlay("pref_developer_overlay", "pref_developer") mergePreferenceOverlay("pref_info_overlay", "pref_info") + requirePreference("pref_developer_refreshed_after_switch") + .bindBooleanFlow(preferenceRepository.refreshAfterSwitchFlow) + requirePreference("pref_info_ara_m").apply { summary = firstSigner.encodeHex() setOnPreferenceClickListener { diff --git a/app-unpriv/src/main/res/xml/pref_unprivileged_settings.xml b/app-unpriv/src/main/res/xml/pref_unprivileged_settings.xml index 3281caf..df5bfee 100644 --- a/app-unpriv/src/main/res/xml/pref_unprivileged_settings.xml +++ b/app-unpriv/src/main/res/xml/pref_unprivileged_settings.xml @@ -1,5 +1,15 @@ + + +