diff --git a/app-common/src/main/java/im/angry/openeuicc/util/EuiccChannelFragmentUtils.kt b/app-common/src/main/java/im/angry/openeuicc/util/EuiccChannelFragmentUtils.kt index 582f86c..aff4154 100644 --- a/app-common/src/main/java/im/angry/openeuicc/util/EuiccChannelFragmentUtils.kt +++ b/app-common/src/main/java/im/angry/openeuicc/util/EuiccChannelFragmentUtils.kt @@ -1,10 +1,15 @@ package im.angry.openeuicc.util import android.os.Bundle +import android.util.Log import androidx.fragment.app.Fragment import im.angry.openeuicc.core.EuiccChannel import im.angry.openeuicc.core.EuiccChannelManager import im.angry.openeuicc.ui.BaseEuiccAccessActivity +import kotlinx.coroutines.Dispatchers +import kotlinx.coroutines.withContext + +private const val TAG = "EuiccChannelFragmentUtils" interface EuiccChannelFragmentMarker: OpenEuiccContextMarker @@ -32,9 +37,32 @@ val T.channel: EuiccChannel where T: Fragment, T: EuiccChannelFragmentMarker get() = euiccChannelManager.findEuiccChannelByPortBlocking(slotId, portId)!! +/* + * Begin a "tracked" operation where notifications may be generated by the eSIM + * Automatically handle any newly generated notification during the operation + * if the function "op" returns true. + */ +suspend fun T.beginTrackedOperation(op: suspend () -> Boolean) where T : Fragment, T : EuiccChannelFragmentMarker = + withContext(Dispatchers.IO) { + val latestSeq = channel.lpa.notifications.firstOrNull()?.seqNumber ?: 0 + Log.d(TAG, "Latest notification is $latestSeq before operation") + if (op()) { + Log.d(TAG, "Operation has requested notification handling") + try { + // Note that the exact instance of "channel" might have changed here if reconnected; + // so we MUST use the automatic getter for "channel" + channel.lpa.notifications.filter { it.seqNumber > latestSeq }.forEach { + Log.d(TAG, "Handling notification $it") + channel.lpa.handleNotification(it.seqNumber) + } + } catch (e: Exception) { + // Ignore any error during notification handling + e.printStackTrace() + } + } + Log.d(TAG, "Operation complete") + } + interface EuiccProfilesChangedListener { fun onEuiccProfilesChanged() -} - -suspend fun T.beginTrackedOperation(op: suspend () -> Boolean) where T: Fragment, T: EuiccChannelFragmentMarker = - channel.lpa.beginTrackedOperation(op) \ No newline at end of file +} \ No newline at end of file diff --git a/app-common/src/main/java/im/angry/openeuicc/util/LPAUtils.kt b/app-common/src/main/java/im/angry/openeuicc/util/LPAUtils.kt index 5d0c6ce..556320f 100644 --- a/app-common/src/main/java/im/angry/openeuicc/util/LPAUtils.kt +++ b/app-common/src/main/java/im/angry/openeuicc/util/LPAUtils.kt @@ -2,14 +2,9 @@ package im.angry.openeuicc.util import android.util.Log import im.angry.openeuicc.core.EuiccChannel -import kotlinx.coroutines.Dispatchers -import kotlinx.coroutines.runBlocking -import kotlinx.coroutines.withContext import net.typeblog.lpac_jni.LocalProfileAssistant import net.typeblog.lpac_jni.LocalProfileInfo -const val TAG = "LPAUtils" - val LocalProfileInfo.displayName: String get() = nickName.ifEmpty { name } @@ -31,7 +26,7 @@ val List.hasMultipleChips: Boolean */ fun LocalProfileAssistant.disableActiveProfile(refresh: Boolean): Boolean = profiles.find { it.isEnabled }?.let { - Log.i(TAG, "Disabling active profile ${it.iccid}") + Log.i("LPAUtils", "Disabling active profile ${it.iccid}") disableProfile(it.iccid, refresh) } ?: true @@ -45,34 +40,4 @@ fun LocalProfileAssistant.disableActiveProfileWithUndo(refreshOnDisable: Boolean profiles.find { it.isEnabled }?.let { disableProfile(it.iccid, refreshOnDisable) return { enableProfile(it.iccid) } - } ?: { } - -/** - * Begin a "tracked" operation where notifications may be generated by the eSIM - * Automatically handle any newly generated notification during the operation - * if the function "op" returns true. - */ -suspend fun LocalProfileAssistant.beginTrackedOperation(op: suspend () -> Boolean) = - withContext(Dispatchers.IO) { - beginTrackedOperationBlocking { op() } - } - -inline fun LocalProfileAssistant.beginTrackedOperationBlocking(op: () -> Boolean) { - val latestSeq = notifications.firstOrNull()?.seqNumber ?: 0 - Log.d(TAG, "Latest notification is $latestSeq before operation") - if (op()) { - Log.d(TAG, "Operation has requested notification handling") - try { - // Note that the exact instance of "channel" might have changed here if reconnected; - // so we MUST use the automatic getter for "channel" - notifications.filter { it.seqNumber > latestSeq }.forEach { - Log.d(TAG, "Handling notification $it") - handleNotification(it.seqNumber) - } - } catch (e: Exception) { - // Ignore any error during notification handling - e.printStackTrace() - } - } - Log.d(TAG, "Operation complete") -} \ No newline at end of file + } ?: { } \ No newline at end of file diff --git a/app/src/main/java/im/angry/openeuicc/service/OpenEuiccService.kt b/app/src/main/java/im/angry/openeuicc/service/OpenEuiccService.kt index 4b46164..74d2d75 100644 --- a/app/src/main/java/im/angry/openeuicc/service/OpenEuiccService.kt +++ b/app/src/main/java/im/angry/openeuicc/service/OpenEuiccService.kt @@ -12,8 +12,6 @@ import net.typeblog.lpac_jni.LocalProfileInfo import im.angry.openeuicc.core.EuiccChannel import im.angry.openeuicc.core.EuiccChannelManager import im.angry.openeuicc.util.* -import kotlinx.coroutines.flow.first -import kotlinx.coroutines.flow.single import kotlinx.coroutines.runBlocking import java.lang.IllegalStateException @@ -228,17 +226,11 @@ class OpenEuiccService : EuiccService(), OpenEuiccContextMarker { } } - channels[0].lpa.beginTrackedOperationBlocking { - if (channels[0].lpa.deleteProfile(iccid)) { - return RESULT_OK - } - - runBlocking { - preferenceRepository.notificationDeleteFlow.first() - } + return if (channels[0].lpa.deleteProfile(iccid)) { + RESULT_OK + } else { + RESULT_FIRST_USER } - - return RESULT_FIRST_USER } catch (e: Exception) { return RESULT_FIRST_USER } @@ -291,22 +283,14 @@ class OpenEuiccService : EuiccService(), OpenEuiccContextMarker { return RESULT_FIRST_USER } - channel.lpa.beginTrackedOperationBlocking { - if (iccid != null) { - // Disable any active profile first if present - channel.lpa.disableActiveProfile(false) - if (!channel.lpa.enableProfile(iccid)) { - return RESULT_FIRST_USER - } - } else { - if (!channel.lpa.disableActiveProfile(true)) { - return RESULT_FIRST_USER - } - } + // Disable any active profile first if present + if (!channel.lpa.disableActiveProfile(false)) { + return RESULT_FIRST_USER + } - runBlocking { - // TODO: The enable / disable operations should really be one - preferenceRepository.notificationEnableFlow.first() + if (iccid != null) { + if (!channel.lpa.enableProfile(iccid)) { + return RESULT_FIRST_USER } } diff --git a/libs/lpac-jni/src/main/java/net/typeblog/lpac_jni/LocalProfileAssistant.kt b/libs/lpac-jni/src/main/java/net/typeblog/lpac_jni/LocalProfileAssistant.kt index 7e0a31b..89df38d 100644 --- a/libs/lpac-jni/src/main/java/net/typeblog/lpac_jni/LocalProfileAssistant.kt +++ b/libs/lpac-jni/src/main/java/net/typeblog/lpac_jni/LocalProfileAssistant.kt @@ -10,8 +10,8 @@ interface LocalProfileAssistant { // All blocking functions in this class assume that they are executed on non-Main threads // The IO context in Kotlin's coroutine library is recommended. - fun enableProfile(iccid: String, refresh: Boolean = true): Boolean - fun disableProfile(iccid: String, refresh: Boolean = true): Boolean + fun enableProfile(iccid: String, refresh: Boolean = false): Boolean + fun disableProfile(iccid: String, refresh: Boolean = false): Boolean fun deleteProfile(iccid: String): Boolean fun downloadProfile(smdp: String, matchingId: String?, imei: String?,