Compare commits
2 commits
b2abe5ee84
...
f2c233fe1c
Author | SHA1 | Date | |
---|---|---|---|
f2c233fe1c | |||
3507c17834 |
1 changed files with 70 additions and 26 deletions
|
@ -15,10 +15,12 @@ import im.angry.openeuicc.core.EuiccChannelManager
|
||||||
import im.angry.openeuicc.util.*
|
import im.angry.openeuicc.util.*
|
||||||
import kotlinx.coroutines.Dispatchers
|
import kotlinx.coroutines.Dispatchers
|
||||||
import kotlinx.coroutines.NonCancellable
|
import kotlinx.coroutines.NonCancellable
|
||||||
|
import kotlinx.coroutines.channels.BufferOverflow
|
||||||
import kotlinx.coroutines.delay
|
import kotlinx.coroutines.delay
|
||||||
import kotlinx.coroutines.flow.Flow
|
import kotlinx.coroutines.flow.Flow
|
||||||
import kotlinx.coroutines.flow.MutableSharedFlow
|
import kotlinx.coroutines.flow.MutableSharedFlow
|
||||||
import kotlinx.coroutines.flow.MutableStateFlow
|
import kotlinx.coroutines.flow.MutableStateFlow
|
||||||
|
import kotlinx.coroutines.flow.buffer
|
||||||
import kotlinx.coroutines.flow.collect
|
import kotlinx.coroutines.flow.collect
|
||||||
import kotlinx.coroutines.flow.first
|
import kotlinx.coroutines.flow.first
|
||||||
import kotlinx.coroutines.flow.flow
|
import kotlinx.coroutines.flow.flow
|
||||||
|
@ -98,6 +100,25 @@ class EuiccChannelManagerService : LifecycleService(), OpenEuiccContextMarker {
|
||||||
private val foregroundTaskState: MutableStateFlow<ForegroundTaskState> =
|
private val foregroundTaskState: MutableStateFlow<ForegroundTaskState> =
|
||||||
MutableStateFlow(ForegroundTaskState.Idle)
|
MutableStateFlow(ForegroundTaskState.Idle)
|
||||||
|
|
||||||
|
/**
|
||||||
|
* A simple wrapper over a flow with taskId added.
|
||||||
|
*
|
||||||
|
* taskID is the exact millisecond-precision timestamp when the task is launched.
|
||||||
|
*/
|
||||||
|
class ForegroundTaskSubscriberFlow(val taskId: Long, inner: Flow<ForegroundTaskState>) :
|
||||||
|
Flow<ForegroundTaskState> by inner
|
||||||
|
|
||||||
|
/**
|
||||||
|
* A cache of subscribers to 5 recently-launched foreground tasks, identified by ID
|
||||||
|
*
|
||||||
|
* Only one can be run at the same time, but those that are done will be kept in this
|
||||||
|
* map for a little while -- because UI components may be stopped and recreated while
|
||||||
|
* tasks are running. Having this buffer allows the components to re-subscribe even if
|
||||||
|
* the task completes while they are being recreated.
|
||||||
|
*/
|
||||||
|
private val foregroundTaskSubscribers: MutableMap<Long, ForegroundTaskSubscriberFlow> =
|
||||||
|
mutableMapOf()
|
||||||
|
|
||||||
override fun onBind(intent: Intent): IBinder {
|
override fun onBind(intent: Intent): IBinder {
|
||||||
super.onBind(intent)
|
super.onBind(intent)
|
||||||
return LocalBinder()
|
return LocalBinder()
|
||||||
|
@ -194,7 +215,9 @@ class EuiccChannelManagerService : LifecycleService(), OpenEuiccContextMarker {
|
||||||
failureTitle: String,
|
failureTitle: String,
|
||||||
iconRes: Int,
|
iconRes: Int,
|
||||||
task: suspend EuiccChannelManagerService.() -> Unit
|
task: suspend EuiccChannelManagerService.() -> Unit
|
||||||
): Flow<ForegroundTaskState> {
|
): ForegroundTaskSubscriberFlow {
|
||||||
|
val taskID = System.currentTimeMillis()
|
||||||
|
|
||||||
// Atomically set the state to InProgress. If this returns true, we are
|
// Atomically set the state to InProgress. If this returns true, we are
|
||||||
// the only task currently in progress.
|
// the only task currently in progress.
|
||||||
if (!foregroundTaskState.compareAndSet(
|
if (!foregroundTaskState.compareAndSet(
|
||||||
|
@ -202,7 +225,9 @@ class EuiccChannelManagerService : LifecycleService(), OpenEuiccContextMarker {
|
||||||
ForegroundTaskState.InProgress(0)
|
ForegroundTaskState.InProgress(0)
|
||||||
)
|
)
|
||||||
) {
|
) {
|
||||||
return flow { emit(ForegroundTaskState.Done(IllegalStateException("There are tasks currently running"))) }
|
return ForegroundTaskSubscriberFlow(
|
||||||
|
taskID,
|
||||||
|
flow { emit(ForegroundTaskState.Done(IllegalStateException("There are tasks currently running"))) })
|
||||||
}
|
}
|
||||||
|
|
||||||
lifecycleScope.launch(Dispatchers.Main) {
|
lifecycleScope.launch(Dispatchers.Main) {
|
||||||
|
@ -249,7 +274,8 @@ class EuiccChannelManagerService : LifecycleService(), OpenEuiccContextMarker {
|
||||||
// Then, we complete the returned flow, but we also set the state back to Idle.
|
// Then, we complete the returned flow, but we also set the state back to Idle.
|
||||||
// The state update back to Idle won't show up in the returned stream, because
|
// The state update back to Idle won't show up in the returned stream, because
|
||||||
// it has been completed by that point.
|
// it has been completed by that point.
|
||||||
return foregroundTaskState.transformWhile {
|
val subscriberFlow = foregroundTaskState
|
||||||
|
.transformWhile {
|
||||||
// Also update our notification when we see an update
|
// Also update our notification when we see an update
|
||||||
// But ignore the first progress = 0 update -- that is the current value.
|
// But ignore the first progress = 0 update -- that is the current value.
|
||||||
// we need that to be handled by the main coroutine after it finishes.
|
// we need that to be handled by the main coroutine after it finishes.
|
||||||
|
@ -272,6 +298,24 @@ class EuiccChannelManagerService : LifecycleService(), OpenEuiccContextMarker {
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
}.onCompletion { foregroundTaskState.value = ForegroundTaskState.Idle }
|
}.onCompletion { foregroundTaskState.value = ForegroundTaskState.Idle }
|
||||||
|
// Buffer the returned flow by 2, so that if there is an error,
|
||||||
|
// we always get a copy of the last process update before completion.
|
||||||
|
// This also guarantees that our onCompletion callback is always run
|
||||||
|
// even if the returned flow isn't subscribed to
|
||||||
|
.buffer(capacity = 2, onBufferOverflow = BufferOverflow.DROP_OLDEST)
|
||||||
|
|
||||||
|
val ret = ForegroundTaskSubscriberFlow(taskID, subscriberFlow)
|
||||||
|
foregroundTaskSubscribers[taskID] = ret
|
||||||
|
|
||||||
|
if (foregroundTaskSubscribers.size > 5) {
|
||||||
|
// Remove enough elements so that the size is kept at 5
|
||||||
|
for (key in foregroundTaskSubscribers.keys.sorted()
|
||||||
|
.take(foregroundTaskSubscribers.size - 5)) {
|
||||||
|
foregroundTaskSubscribers.remove(key)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return ret
|
||||||
}
|
}
|
||||||
|
|
||||||
val isForegroundTaskRunning: Boolean
|
val isForegroundTaskRunning: Boolean
|
||||||
|
@ -289,7 +333,7 @@ class EuiccChannelManagerService : LifecycleService(), OpenEuiccContextMarker {
|
||||||
matchingId: String?,
|
matchingId: String?,
|
||||||
confirmationCode: String?,
|
confirmationCode: String?,
|
||||||
imei: String?
|
imei: String?
|
||||||
): Flow<ForegroundTaskState> =
|
): ForegroundTaskSubscriberFlow =
|
||||||
launchForegroundTask(
|
launchForegroundTask(
|
||||||
getString(R.string.task_profile_download),
|
getString(R.string.task_profile_download),
|
||||||
getString(R.string.task_profile_download_failure),
|
getString(R.string.task_profile_download_failure),
|
||||||
|
@ -325,7 +369,7 @@ class EuiccChannelManagerService : LifecycleService(), OpenEuiccContextMarker {
|
||||||
portId: Int,
|
portId: Int,
|
||||||
iccid: String,
|
iccid: String,
|
||||||
name: String
|
name: String
|
||||||
): Flow<ForegroundTaskState> =
|
): ForegroundTaskSubscriberFlow =
|
||||||
launchForegroundTask(
|
launchForegroundTask(
|
||||||
getString(R.string.task_profile_rename),
|
getString(R.string.task_profile_rename),
|
||||||
getString(R.string.task_profile_rename_failure),
|
getString(R.string.task_profile_rename_failure),
|
||||||
|
@ -347,7 +391,7 @@ class EuiccChannelManagerService : LifecycleService(), OpenEuiccContextMarker {
|
||||||
slotId: Int,
|
slotId: Int,
|
||||||
portId: Int,
|
portId: Int,
|
||||||
iccid: String
|
iccid: String
|
||||||
): Flow<ForegroundTaskState> =
|
): ForegroundTaskSubscriberFlow =
|
||||||
launchForegroundTask(
|
launchForegroundTask(
|
||||||
getString(R.string.task_profile_delete),
|
getString(R.string.task_profile_delete),
|
||||||
getString(R.string.task_profile_delete_failure),
|
getString(R.string.task_profile_delete_failure),
|
||||||
|
@ -370,7 +414,7 @@ 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, useful for USB readers
|
reconnectTimeoutMillis: Long = 0 // 0 = do not wait for reconnect, useful for USB readers
|
||||||
): Flow<ForegroundTaskState> =
|
): 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),
|
||||||
|
|
Loading…
Add table
Reference in a new issue