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
|
package im.angry.openeuicc.util
|
||||||
|
|
||||||
import android.os.Bundle
|
import android.os.Bundle
|
||||||
import android.util.Log
|
|
||||||
import androidx.fragment.app.Fragment
|
import androidx.fragment.app.Fragment
|
||||||
import im.angry.openeuicc.core.EuiccChannel
|
import im.angry.openeuicc.core.EuiccChannel
|
||||||
import im.angry.openeuicc.core.EuiccChannelManager
|
import im.angry.openeuicc.core.EuiccChannelManager
|
||||||
import im.angry.openeuicc.ui.BaseEuiccAccessActivity
|
import im.angry.openeuicc.ui.BaseEuiccAccessActivity
|
||||||
import kotlinx.coroutines.Dispatchers
|
|
||||||
import kotlinx.coroutines.withContext
|
|
||||||
|
|
||||||
private const val TAG = "EuiccChannelFragmentUtils"
|
|
||||||
|
|
||||||
interface EuiccChannelFragmentMarker: OpenEuiccContextMarker
|
interface EuiccChannelFragmentMarker: OpenEuiccContextMarker
|
||||||
|
|
||||||
|
@ -37,32 +32,9 @@ val <T> T.channel: EuiccChannel where T: Fragment, T: EuiccChannelFragmentMarker
|
||||||
get() =
|
get() =
|
||||||
euiccChannelManager.findEuiccChannelByPortBlocking(slotId, portId)!!
|
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 {
|
interface EuiccProfilesChangedListener {
|
||||||
fun onEuiccProfilesChanged()
|
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 android.util.Log
|
||||||
import im.angry.openeuicc.core.EuiccChannel
|
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.LocalProfileAssistant
|
||||||
import net.typeblog.lpac_jni.LocalProfileInfo
|
import net.typeblog.lpac_jni.LocalProfileInfo
|
||||||
|
|
||||||
|
const val TAG = "LPAUtils"
|
||||||
|
|
||||||
val LocalProfileInfo.displayName: String
|
val LocalProfileInfo.displayName: String
|
||||||
get() = nickName.ifEmpty { name }
|
get() = nickName.ifEmpty { name }
|
||||||
|
|
||||||
|
@ -26,7 +31,7 @@ val List<EuiccChannel>.hasMultipleChips: Boolean
|
||||||
*/
|
*/
|
||||||
fun LocalProfileAssistant.disableActiveProfile(refresh: Boolean): Boolean =
|
fun LocalProfileAssistant.disableActiveProfile(refresh: Boolean): Boolean =
|
||||||
profiles.find { it.isEnabled }?.let {
|
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)
|
disableProfile(it.iccid, refresh)
|
||||||
} ?: true
|
} ?: true
|
||||||
|
|
||||||
|
@ -41,3 +46,33 @@ fun LocalProfileAssistant.disableActiveProfileWithUndo(refreshOnDisable: Boolean
|
||||||
disableProfile(it.iccid, refreshOnDisable)
|
disableProfile(it.iccid, refreshOnDisable)
|
||||||
return { enableProfile(it.iccid) }
|
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.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.flow.first
|
||||||
|
import kotlinx.coroutines.flow.single
|
||||||
import kotlinx.coroutines.runBlocking
|
import kotlinx.coroutines.runBlocking
|
||||||
import java.lang.IllegalStateException
|
import java.lang.IllegalStateException
|
||||||
|
|
||||||
|
@ -226,11 +228,17 @@ class OpenEuiccService : EuiccService(), OpenEuiccContextMarker {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
return if (channels[0].lpa.deleteProfile(iccid)) {
|
channels[0].lpa.beginTrackedOperationBlocking {
|
||||||
RESULT_OK
|
if (channels[0].lpa.deleteProfile(iccid)) {
|
||||||
} else {
|
return RESULT_OK
|
||||||
RESULT_FIRST_USER
|
}
|
||||||
|
|
||||||
|
runBlocking {
|
||||||
|
preferenceRepository.notificationDeleteFlow.first()
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
return RESULT_FIRST_USER
|
||||||
} catch (e: Exception) {
|
} catch (e: Exception) {
|
||||||
return RESULT_FIRST_USER
|
return RESULT_FIRST_USER
|
||||||
}
|
}
|
||||||
|
@ -283,14 +291,22 @@ class OpenEuiccService : EuiccService(), OpenEuiccContextMarker {
|
||||||
return RESULT_FIRST_USER
|
return RESULT_FIRST_USER
|
||||||
}
|
}
|
||||||
|
|
||||||
// Disable any active profile first if present
|
channel.lpa.beginTrackedOperationBlocking {
|
||||||
if (!channel.lpa.disableActiveProfile(false)) {
|
if (iccid != null) {
|
||||||
return RESULT_FIRST_USER
|
// 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) {
|
runBlocking {
|
||||||
if (!channel.lpa.enableProfile(iccid)) {
|
// TODO: The enable / disable operations should really be one
|
||||||
return RESULT_FIRST_USER
|
preferenceRepository.notificationEnableFlow.first()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -10,8 +10,8 @@ interface LocalProfileAssistant {
|
||||||
|
|
||||||
// All blocking functions in this class assume that they are executed on non-Main threads
|
// 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.
|
// The IO context in Kotlin's coroutine library is recommended.
|
||||||
fun enableProfile(iccid: String, refresh: Boolean = false): Boolean
|
fun enableProfile(iccid: String, refresh: Boolean = true): Boolean
|
||||||
fun disableProfile(iccid: String, refresh: Boolean = false): Boolean
|
fun disableProfile(iccid: String, refresh: Boolean = true): Boolean
|
||||||
fun deleteProfile(iccid: String): Boolean
|
fun deleteProfile(iccid: String): Boolean
|
||||||
|
|
||||||
fun downloadProfile(smdp: String, matchingId: String?, imei: String?,
|
fun downloadProfile(smdp: String, matchingId: String?, imei: String?,
|
||||||
|
|
Loading…
Add table
Reference in a new issue