fix(notifications): throttle dictionary progress reposts
A fast download reposted the ongoing notification several times per second. Publish stage changes immediately and rate-limit intermediate updates to the latest snapshot.
This commit is contained in:
1 parent
ae4bffe516
commit
2fdcae6ae3
1 file changed
+53
-3
+53
-3
@@ -1,37 +1,86 @@
|
||||
package com.example.research.common.progress
|
||||
|
||||
import android.os.SystemClock
|
||||
import kotlinx.coroutines.CoroutineDispatcher
|
||||
import kotlinx.coroutines.CoroutineScope
|
||||
import kotlinx.coroutines.Dispatchers
|
||||
import kotlinx.coroutines.Job
|
||||
import kotlinx.coroutines.delay
|
||||
import kotlinx.coroutines.flow.StateFlow
|
||||
import kotlinx.coroutines.launch
|
||||
import kotlin.time.Duration.Companion.milliseconds
|
||||
|
||||
class DictionaryProgressPresenter(
|
||||
scope: CoroutineScope,
|
||||
private val scope: CoroutineScope,
|
||||
private val progressSnapshot: StateFlow<DictionaryProgressModel.Snapshot?>,
|
||||
private val notifications: NotificationPort,
|
||||
mainDispatcher: CoroutineDispatcher = Dispatchers.Main,
|
||||
private val mainDispatcher: CoroutineDispatcher = Dispatchers.Main,
|
||||
) : PipelineTerminalUi {
|
||||
|
||||
private companion object {
|
||||
val MIN_NOTIFICATION_INTERVAL = 500.milliseconds
|
||||
}
|
||||
|
||||
@Volatile
|
||||
private var terminalActive = false
|
||||
|
||||
private var lastShownTitleRes: Int = 0
|
||||
private var lastEmitMs: Long = 0L
|
||||
private var pendingSnapshot: DictionaryProgressModel.Snapshot? = null
|
||||
private var flushJob: Job? = null
|
||||
|
||||
init {
|
||||
scope.launch(mainDispatcher) {
|
||||
progressSnapshot.collect { snapshot ->
|
||||
when {
|
||||
snapshot == null -> {
|
||||
resetThrottle()
|
||||
if (!terminalActive && notifications.hasActiveProgressNotification()) {
|
||||
notifications.cancelProgressNotification()
|
||||
}
|
||||
}
|
||||
!terminalActive -> notifications.showProgress(snapshot.percent, snapshot.titleRes)
|
||||
!terminalActive -> emitThrottled(snapshot)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private fun emitThrottled(snapshot: DictionaryProgressModel.Snapshot) {
|
||||
val now = SystemClock.elapsedRealtime()
|
||||
val stageChanged = snapshot.titleRes != lastShownTitleRes
|
||||
if (stageChanged || (now - lastEmitMs).milliseconds >= MIN_NOTIFICATION_INTERVAL) {
|
||||
flushJob?.cancel()
|
||||
pendingSnapshot = null
|
||||
publish(snapshot, now)
|
||||
} else {
|
||||
pendingSnapshot = snapshot
|
||||
if (flushJob?.isActive != true) {
|
||||
flushJob = scope.launch(mainDispatcher) {
|
||||
delay(MIN_NOTIFICATION_INTERVAL - (now - lastEmitMs).milliseconds)
|
||||
pendingSnapshot?.let { publish(it, SystemClock.elapsedRealtime()) }
|
||||
pendingSnapshot = null
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private fun publish(snapshot: DictionaryProgressModel.Snapshot, now: Long) {
|
||||
if (terminalActive) return
|
||||
lastShownTitleRes = snapshot.titleRes
|
||||
lastEmitMs = now
|
||||
notifications.showProgress(snapshot.percent, snapshot.titleRes)
|
||||
}
|
||||
|
||||
private fun resetThrottle() {
|
||||
flushJob?.cancel()
|
||||
pendingSnapshot = null
|
||||
lastShownTitleRes = 0
|
||||
lastEmitMs = 0L
|
||||
}
|
||||
|
||||
fun beginFlow() {
|
||||
terminalActive = false
|
||||
resetThrottle()
|
||||
notifications.cancelTerminalNotification()
|
||||
}
|
||||
|
||||
@@ -52,6 +101,7 @@ class DictionaryProgressPresenter(
|
||||
|
||||
override fun cancelProgress() {
|
||||
terminalActive = false
|
||||
resetThrottle()
|
||||
notifications.cancelProgressNotification()
|
||||
}
|
||||
}
|
||||
Reference in new issue
Block a user