Compare commits

...

5 commits

5 changed files with 36 additions and 25 deletions

View file

@ -11,6 +11,7 @@ import androidx.core.app.NotificationManagerCompat
import androidx.lifecycle.LifecycleService import androidx.lifecycle.LifecycleService
import androidx.lifecycle.lifecycleScope import androidx.lifecycle.lifecycleScope
import im.angry.openeuicc.common.R import im.angry.openeuicc.common.R
import im.angry.openeuicc.core.EuiccChannel
import im.angry.openeuicc.core.EuiccChannelManager import im.angry.openeuicc.core.EuiccChannelManager
import im.angry.openeuicc.util.* import im.angry.openeuicc.util.*
import kotlinx.coroutines.Dispatchers import kotlinx.coroutines.Dispatchers
@ -446,30 +447,30 @@ class EuiccChannelManagerService : LifecycleService(), OpenEuiccContextMarker {
iccid: String, iccid: String,
enable: Boolean, // Enable or disable the profile indicated in iccid enable: Boolean, // Enable or disable the profile indicated in iccid
reconnectTimeoutMillis: Long = 0 // 0 = do not wait for reconnect reconnectTimeoutMillis: Long = 0 // 0 = do not wait for reconnect
): ForegroundTaskSubscriberFlow = ) =
launchForegroundTask( launchForegroundTask(
getString(R.string.task_profile_switch), getString(R.string.task_profile_switch),
getString(R.string.task_profile_switch_failure), getString(R.string.task_profile_switch_failure),
R.drawable.ic_task_switch R.drawable.ic_task_switch
) { ) {
euiccChannelManager.beginTrackedOperation(slotId, portId) { suspend fun onSwitch(channel: EuiccChannel): Pair<Boolean, Boolean> {
val (res, refreshed) = euiccChannelManager.withEuiccChannel( val refresh = preferenceRepository.refreshAfterSwitchFlow.first()
slotId, val response = channel.lpa.switchProfile(iccid, enable, refresh)
portId if (response || !refresh) return Pair(response, refresh)
) { channel -> // refresh failed, but refresh was requested
if (!channel.lpa.switchProfile(iccid, enable, refresh = true)) { // Sometimes, we *can* enable or disable the profile, but we cannot
// Sometimes, we *can* enable or disable the profile, but we cannot // send the refresh command to the modem because the profile somehow
// send the refresh command to the modem because the profile somehow // makes the modem "busy". In this case, we can still switch by setting
// 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
// refresh to false, but then the switch cannot take effect until the // user resets the modem manually by toggling airplane mode or rebooting.
// user resets the modem manually by toggling airplane mode or rebooting. return Pair(channel.lpa.switchProfile(iccid, enable, refresh = false), false)
Pair(channel.lpa.switchProfile(iccid, enable, refresh = false), false) }
} else {
Pair(true, true)
}
}
if (!res) { euiccChannelManager.beginTrackedOperation(slotId, portId) {
val (response, refreshed) =
euiccChannelManager.withEuiccChannel(slotId, portId, ::onSwitch)
if (!response) {
throw RuntimeException("Could not switch profile") throw RuntimeException("Could not switch profile")
} }

View file

@ -78,6 +78,9 @@ open class SettingsFragment: PreferenceFragmentCompat() {
requirePreference<CheckBoxPreference>("pref_developer_ignore_tls_certificate") requirePreference<CheckBoxPreference>("pref_developer_ignore_tls_certificate")
.bindBooleanFlow(preferenceRepository.ignoreTLSCertificateFlow) .bindBooleanFlow(preferenceRepository.ignoreTLSCertificateFlow)
requirePreference<CheckBoxPreference>("pref_developer_refresh_after_switch")
.bindBooleanFlow(preferenceRepository.refreshAfterSwitchFlow)
requirePreference<CheckBoxPreference>("pref_developer_euicc_memory_reset") requirePreference<CheckBoxPreference>("pref_developer_euicc_memory_reset")
.bindBooleanFlow(preferenceRepository.euiccMemoryResetFlow) .bindBooleanFlow(preferenceRepository.euiccMemoryResetFlow)
} }

View file

@ -31,6 +31,7 @@ internal object PreferenceKeys {
// ---- Developer Options ---- // ---- Developer Options ----
val DEVELOPER_OPTIONS_ENABLED = booleanPreferencesKey("developer_options_enabled") val DEVELOPER_OPTIONS_ENABLED = booleanPreferencesKey("developer_options_enabled")
val REFRESH_AFTER_SWITCH = booleanPreferencesKey("refresh_after_switch")
val UNFILTERED_PROFILE_LIST = booleanPreferencesKey("unfiltered_profile_list") val UNFILTERED_PROFILE_LIST = booleanPreferencesKey("unfiltered_profile_list")
val IGNORE_TLS_CERTIFICATE = booleanPreferencesKey("ignore_tls_certificate") val IGNORE_TLS_CERTIFICATE = booleanPreferencesKey("ignore_tls_certificate")
val EUICC_MEMORY_RESET = booleanPreferencesKey("euicc_memory_reset") val EUICC_MEMORY_RESET = booleanPreferencesKey("euicc_memory_reset")
@ -48,6 +49,7 @@ open class PreferenceRepository(private val context: Context) {
val verboseLoggingFlow = bindFlow(PreferenceKeys.VERBOSE_LOGGING, false) val verboseLoggingFlow = bindFlow(PreferenceKeys.VERBOSE_LOGGING, false)
// ---- Developer Options ---- // ---- Developer Options ----
val refreshAfterSwitchFlow = bindFlow(PreferenceKeys.REFRESH_AFTER_SWITCH, true)
val developerOptionsEnabledFlow = bindFlow(PreferenceKeys.DEVELOPER_OPTIONS_ENABLED, false) val developerOptionsEnabledFlow = bindFlow(PreferenceKeys.DEVELOPER_OPTIONS_ENABLED, false)
val unfilteredProfileListFlow = bindFlow(PreferenceKeys.UNFILTERED_PROFILE_LIST, false) val unfilteredProfileListFlow = bindFlow(PreferenceKeys.UNFILTERED_PROFILE_LIST, false)
val ignoreTLSCertificateFlow = bindFlow(PreferenceKeys.IGNORE_TLS_CERTIFICATE, false) val ignoreTLSCertificateFlow = bindFlow(PreferenceKeys.IGNORE_TLS_CERTIFICATE, false)
@ -60,13 +62,10 @@ open class PreferenceRepository(private val context: Context) {
class PreferenceFlowWrapper<T> private constructor( class PreferenceFlowWrapper<T> private constructor(
private val context: Context, private val context: Context,
private val key: Preferences.Key<T>, private val key: Preferences.Key<T>,
inner: Flow<T> inner: Flow<T>,
) : Flow<T> by inner { ) : Flow<T> by inner {
internal constructor(context: Context, key: Preferences.Key<T>, defaultValue: T) : this( internal constructor(context: Context, key: Preferences.Key<T>, defaultValue: T) :
context, this(context, key, context.dataStore.data.map { it[key] ?: defaultValue })
key,
context.dataStore.data.map { it[key] ?: defaultValue }
)
suspend fun updatePreference(value: T) { suspend fun updatePreference(value: T) {
context.dataStore.edit { it[key] = value } context.dataStore.edit { it[key] = value }

View file

@ -181,6 +181,8 @@
<string name="pref_advanced_logs">Logs</string> <string name="pref_advanced_logs">Logs</string>
<string name="pref_advanced_logs_desc">View recent debug logs of the application</string> <string name="pref_advanced_logs_desc">View recent debug logs of the application</string>
<string name="pref_developer">Developer Options</string> <string name="pref_developer">Developer Options</string>
<string name="pref_developer_refresh_after_switch">Refresh status after switching</string>
<string name="pref_developer_refresh_after_switch_desc">If crash after switch, Please trying disable the function</string>
<string name="pref_developer_unfiltered_profile_list">Show unfiltered profile list</string> <string name="pref_developer_unfiltered_profile_list">Show unfiltered profile list</string>
<string name="pref_developer_unfiltered_profile_list_desc">Include non-production profiles in the list</string> <string name="pref_developer_unfiltered_profile_list_desc">Include non-production profiles in the list</string>
<string name="pref_developer_ignore_tls_certificate">Ignore SM-DP+ TLS certificate</string> <string name="pref_developer_ignore_tls_certificate">Ignore SM-DP+ TLS certificate</string>

View file

@ -57,6 +57,12 @@
app:title="@string/pref_developer" app:title="@string/pref_developer"
app:iconSpaceReserved="false"> app:iconSpaceReserved="false">
<CheckBoxPreference
app:iconSpaceReserved="false"
app:key="pref_developer_refresh_after_switch"
app:summary="@string/pref_developer_refresh_after_switch_desc"
app:title="@string/pref_developer_refresh_after_switch" />
<CheckBoxPreference <CheckBoxPreference
app:iconSpaceReserved="false" app:iconSpaceReserved="false"
app:key="pref_developer_unfiltered_profile_list" app:key="pref_developer_unfiltered_profile_list"