Compare commits
3 commits
df9cece94b
...
790a5cf778
Author | SHA1 | Date | |
---|---|---|---|
790a5cf778 | |||
261ad6dbeb | |||
f73af48b59 |
4 changed files with 70 additions and 47 deletions
|
@ -1,15 +1,10 @@
|
|||
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
|
||||
|
||||
|
@ -37,32 +32,9 @@ val <T> 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> 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> T.beginTrackedOperation(op: suspend () -> Boolean) where T: Fragment, T: EuiccChannelFragmentMarker =
|
||||
channel.lpa.beginTrackedOperation(op)
|
|
@ -2,9 +2,14 @@ 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 }
|
||||
|
||||
|
@ -26,7 +31,7 @@ val List<EuiccChannel>.hasMultipleChips: Boolean
|
|||
*/
|
||||
fun LocalProfileAssistant.disableActiveProfile(refresh: Boolean): Boolean =
|
||||
profiles.find { it.isEnabled }?.let {
|
||||
Log.i("LPAUtils", "Disabling active profile ${it.iccid}")
|
||||
Log.i(TAG, "Disabling active profile ${it.iccid}")
|
||||
disableProfile(it.iccid, refresh)
|
||||
} ?: true
|
||||
|
||||
|
@ -40,4 +45,34 @@ 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")
|
||||
}
|
|
@ -12,6 +12,8 @@ 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
|
||||
|
||||
|
@ -226,11 +228,17 @@ class OpenEuiccService : EuiccService(), OpenEuiccContextMarker {
|
|||
}
|
||||
}
|
||||
|
||||
return if (channels[0].lpa.deleteProfile(iccid)) {
|
||||
RESULT_OK
|
||||
} else {
|
||||
RESULT_FIRST_USER
|
||||
channels[0].lpa.beginTrackedOperationBlocking {
|
||||
if (channels[0].lpa.deleteProfile(iccid)) {
|
||||
return RESULT_OK
|
||||
}
|
||||
|
||||
runBlocking {
|
||||
preferenceRepository.notificationDeleteFlow.first()
|
||||
}
|
||||
}
|
||||
|
||||
return RESULT_FIRST_USER
|
||||
} catch (e: Exception) {
|
||||
return RESULT_FIRST_USER
|
||||
}
|
||||
|
@ -283,14 +291,22 @@ class OpenEuiccService : EuiccService(), OpenEuiccContextMarker {
|
|||
return RESULT_FIRST_USER
|
||||
}
|
||||
|
||||
// Disable any active profile first if present
|
||||
if (!channel.lpa.disableActiveProfile(false)) {
|
||||
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
|
||||
}
|
||||
}
|
||||
|
||||
if (iccid != null) {
|
||||
if (!channel.lpa.enableProfile(iccid)) {
|
||||
return RESULT_FIRST_USER
|
||||
runBlocking {
|
||||
// TODO: The enable / disable operations should really be one
|
||||
preferenceRepository.notificationEnableFlow.first()
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -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 = false): Boolean
|
||||
fun disableProfile(iccid: String, refresh: Boolean = false): Boolean
|
||||
fun enableProfile(iccid: String, refresh: Boolean = true): Boolean
|
||||
fun disableProfile(iccid: String, refresh: Boolean = true): Boolean
|
||||
fun deleteProfile(iccid: String): Boolean
|
||||
|
||||
fun downloadProfile(smdp: String, matchingId: String?, imei: String?,
|
||||
|
|
Loading…
Add table
Reference in a new issue