105 lines
3.1 KiB
Kotlin
105 lines
3.1 KiB
Kotlin
package com.m57.hermescontrol
|
|
|
|
import android.os.Handler
|
|
import android.os.Looper
|
|
|
|
/** Coordinates background reply handling and a bounded connection lease for external activities. */
|
|
object ExternalActivityLifecycleGuard {
|
|
private const val CONNECTION_LEASE_TIMEOUT_MS = 10 * 60 * 1_000L
|
|
|
|
private val lock = Any()
|
|
private var generation = 0L
|
|
private var externalActivityInFlight = false
|
|
private var resultDelivered = false
|
|
private var hostResumed = true
|
|
private var releaseConnectionLease: (() -> Unit)? = null
|
|
|
|
fun launchExternalActivity(
|
|
acquireConnectionLease: () -> Unit,
|
|
releaseConnectionLease: () -> Unit,
|
|
prepareForBackground: () -> Unit,
|
|
cleanupAfterLaunchFailure: () -> Unit,
|
|
scheduleTimeout: (Long, () -> Unit) -> Unit = ::scheduleTimeout,
|
|
launch: () -> Unit,
|
|
) {
|
|
val launchGeneration =
|
|
synchronized(lock) {
|
|
generation += 1
|
|
externalActivityInFlight = true
|
|
resultDelivered = false
|
|
this.releaseConnectionLease = releaseConnectionLease
|
|
generation
|
|
}
|
|
|
|
try {
|
|
acquireConnectionLease()
|
|
scheduleTimeout(CONNECTION_LEASE_TIMEOUT_MS) {
|
|
finishExternalActivity(launchGeneration)
|
|
}
|
|
prepareForBackground()
|
|
launch()
|
|
} catch (error: Throwable) {
|
|
cleanupAfterLaunchFailure()
|
|
finishExternalActivity(launchGeneration)
|
|
throw error
|
|
}
|
|
}
|
|
|
|
fun onHostPaused() {
|
|
synchronized(lock) {
|
|
hostResumed = false
|
|
}
|
|
}
|
|
|
|
fun onHostResumed() {
|
|
val release =
|
|
synchronized(lock) {
|
|
hostResumed = true
|
|
if (externalActivityInFlight && resultDelivered) finishExternalActivityLocked() else null
|
|
}
|
|
release?.invoke()
|
|
}
|
|
|
|
fun externalActivityReturned() {
|
|
val release =
|
|
synchronized(lock) {
|
|
if (!externalActivityInFlight) return
|
|
resultDelivered = true
|
|
if (hostResumed) finishExternalActivityLocked() else null
|
|
}
|
|
release?.invoke()
|
|
}
|
|
|
|
internal fun resetForTest() {
|
|
synchronized(lock) {
|
|
generation += 1
|
|
externalActivityInFlight = false
|
|
resultDelivered = false
|
|
hostResumed = true
|
|
releaseConnectionLease = null
|
|
}
|
|
}
|
|
|
|
private fun finishExternalActivity(expectedGeneration: Long) {
|
|
val release =
|
|
synchronized(lock) {
|
|
if (!externalActivityInFlight || generation != expectedGeneration) return
|
|
finishExternalActivityLocked()
|
|
}
|
|
release?.invoke()
|
|
}
|
|
|
|
private fun finishExternalActivityLocked(): (() -> Unit)? {
|
|
externalActivityInFlight = false
|
|
resultDelivered = false
|
|
return releaseConnectionLease.also { releaseConnectionLease = null }
|
|
}
|
|
|
|
private fun scheduleTimeout(
|
|
delayMs: Long,
|
|
action: () -> Unit,
|
|
) {
|
|
Handler(Looper.getMainLooper()).postDelayed(action, delayMs)
|
|
}
|
|
}
|