Add Flow.throttleLatest extension.
This commit is contained in:
parent
6b9e921888
commit
c36c6e62e2
6 changed files with 184 additions and 6 deletions
|
@ -10,17 +10,14 @@ import androidx.compose.runtime.Composable
|
|||
import androidx.lifecycle.LifecycleOwner
|
||||
import androidx.lifecycle.flowWithLifecycle
|
||||
import kotlinx.coroutines.Dispatchers
|
||||
import kotlinx.coroutines.channels.BufferOverflow
|
||||
import kotlinx.coroutines.channels.awaitClose
|
||||
import kotlinx.coroutines.delay
|
||||
import kotlinx.coroutines.flow.Flow
|
||||
import kotlinx.coroutines.flow.buffer
|
||||
import kotlinx.coroutines.flow.callbackFlow
|
||||
import kotlinx.coroutines.flow.flow
|
||||
import kotlinx.coroutines.flow.flowOn
|
||||
import kotlinx.coroutines.flow.map
|
||||
import kotlinx.coroutines.flow.onEach
|
||||
import kotlinx.coroutines.withContext
|
||||
import org.signal.core.util.throttleLatest
|
||||
import org.thoughtcrime.securesms.backup.v2.ui.status.BackupStatus
|
||||
import org.thoughtcrime.securesms.backup.v2.ui.status.BackupStatusData
|
||||
import org.thoughtcrime.securesms.banner.Banner
|
||||
|
@ -67,8 +64,7 @@ class MediaRestoreProgressBanner(private val data: MediaRestoreEvent) : Banner()
|
|||
|
||||
return flow
|
||||
.flowWithLifecycle(lifecycleOwner.lifecycle)
|
||||
.buffer(1, BufferOverflow.DROP_OLDEST)
|
||||
.onEach { delay(1.seconds) }
|
||||
.throttleLatest(1.seconds)
|
||||
.map { MediaRestoreProgressBanner(loadData()) }
|
||||
.flowOn(Dispatchers.IO)
|
||||
}
|
||||
|
|
|
@ -25,7 +25,11 @@ kotlin {
|
|||
|
||||
dependencies {
|
||||
implementation(libs.kotlin.reflect)
|
||||
implementation(libs.kotlinx.coroutines.core)
|
||||
implementation(libs.kotlinx.coroutines.core.jvm)
|
||||
|
||||
testImplementation(testLibs.junit.junit)
|
||||
testImplementation(testLibs.assertj.core)
|
||||
testImplementation(testLibs.junit.junit)
|
||||
testImplementation(testLibs.kotlinx.coroutines.test)
|
||||
}
|
||||
|
|
|
@ -0,0 +1,24 @@
|
|||
/*
|
||||
* Copyright 2024 Signal Messenger, LLC
|
||||
* SPDX-License-Identifier: AGPL-3.0-only
|
||||
*/
|
||||
|
||||
package org.signal.core.util
|
||||
|
||||
import kotlinx.coroutines.delay
|
||||
import kotlinx.coroutines.flow.Flow
|
||||
import kotlinx.coroutines.flow.conflate
|
||||
import kotlinx.coroutines.flow.onEach
|
||||
import kotlin.time.Duration
|
||||
|
||||
/**
|
||||
* Throttles the flow so that at most one value is emitted every [timeout]. The latest value is always emitted.
|
||||
*
|
||||
* You can think of this like debouncing, but with "checkpoints" so that even if you have a constant stream of values,
|
||||
* you'll still get an emission every [timeout] (unlike debouncing, which will only emit once the stream settles down).
|
||||
*/
|
||||
fun <T> Flow<T>.throttleLatest(timeout: Duration): Flow<T> {
|
||||
return this
|
||||
.conflate()
|
||||
.onEach { delay(timeout) }
|
||||
}
|
|
@ -0,0 +1,63 @@
|
|||
/*
|
||||
* Copyright 2024 Signal Messenger, LLC
|
||||
* SPDX-License-Identifier: AGPL-3.0-only
|
||||
*/
|
||||
|
||||
package org.signal.core.util
|
||||
|
||||
import kotlinx.coroutines.delay
|
||||
import kotlinx.coroutines.flow.flow
|
||||
import kotlinx.coroutines.flow.toList
|
||||
import kotlinx.coroutines.test.runTest
|
||||
import org.junit.Assert.assertEquals
|
||||
import org.junit.Test
|
||||
import kotlin.time.Duration.Companion.milliseconds
|
||||
|
||||
class FlowExtensionsTests {
|
||||
|
||||
@Test
|
||||
fun `throttleLatest - always emits first value`() = runTest {
|
||||
val testFlow = flow {
|
||||
delay(10)
|
||||
emit(1)
|
||||
}
|
||||
|
||||
val output = testFlow
|
||||
.throttleLatest(100.milliseconds)
|
||||
.toList()
|
||||
|
||||
assertEquals(listOf(1), output)
|
||||
}
|
||||
|
||||
@Test
|
||||
fun `throttleLatest - always emits last value`() = runTest {
|
||||
val testFlow = flow {
|
||||
delay(10)
|
||||
emit(1)
|
||||
delay(30)
|
||||
emit(2)
|
||||
}
|
||||
|
||||
val output = testFlow
|
||||
.throttleLatest(20.milliseconds)
|
||||
.toList()
|
||||
|
||||
assertEquals(listOf(1, 2), output)
|
||||
}
|
||||
|
||||
@Test
|
||||
fun `throttleLatest - skips intermediate values`() = runTest {
|
||||
val testFlow = flow {
|
||||
for (i in 1..30) {
|
||||
emit(i)
|
||||
delay(10)
|
||||
}
|
||||
}
|
||||
|
||||
val output = testFlow
|
||||
.throttleLatest(50.milliseconds)
|
||||
.toList()
|
||||
|
||||
assertEquals(listOf(1, 5, 10, 15, 20, 25, 30), output)
|
||||
}
|
||||
}
|
|
@ -47,6 +47,8 @@ dependencyResolutionManagement {
|
|||
library("kotlin-reflect", "org.jetbrains.kotlin", "kotlin-reflect").versionRef("kotlin")
|
||||
library("kotlin-gradle-plugin", "org.jetbrains.kotlin", "kotlin-gradle-plugin").versionRef("kotlin")
|
||||
library("ktlint", "org.jlleitschuh.gradle:ktlint-gradle:12.1.1")
|
||||
library("kotlinx-coroutines-core", "org.jetbrains.kotlinx:kotlinx-coroutines-core:1.9.0")
|
||||
library("kotlinx-coroutines-core-jvm", "org.jetbrains.kotlinx:kotlinx-coroutines-core-jvm:1.9.0")
|
||||
library("kotlinx-coroutines-play-services", "org.jetbrains.kotlinx:kotlinx-coroutines-play-services:1.8.1")
|
||||
library("kotlinx-coroutines-rx3", "org.jetbrains.kotlinx:kotlinx-coroutines-rx3:1.3.9")
|
||||
|
||||
|
@ -196,6 +198,7 @@ dependencyResolutionManagement {
|
|||
library("androidx-test-orchestrator", "androidx.test:orchestrator:1.4.1")
|
||||
library("androidx-test-runner", "androidx.test", "runner").versionRef("androidx-test")
|
||||
library("espresso-core", "androidx.test.espresso", "espresso-core").versionRef("espresso")
|
||||
library("kotlinx-coroutines-test", "org.jetbrains.kotlinx:kotlinx-coroutines-test:1.9.0")
|
||||
library("mockito-core", "org.mockito:mockito-inline:4.6.1")
|
||||
library("mockito-kotlin", "org.mockito.kotlin:mockito-kotlin:4.0.0")
|
||||
library("mockito-android", "org.mockito:mockito-android:4.6.1")
|
||||
|
|
|
@ -7968,6 +7968,17 @@ https://docs.gradle.org/current/userguide/dependency_verification.html
|
|||
<sha256 value="f482314b5079c1455f6fb0d4257a745d101c6124ce961522ba86f9dc90901e47" origin="Generated by Gradle"/>
|
||||
</artifact>
|
||||
</component>
|
||||
<component group="org.jetbrains.kotlin" name="kotlin-stdlib" version="2.0.0">
|
||||
<artifact name="kotlin-stdlib-2.0.0-all.jar">
|
||||
<sha256 value="30e05222bc067fffb896a3276b5f1e3f29450bf1d9461d27a19ce0efc793b5cd" origin="Generated by Gradle"/>
|
||||
</artifact>
|
||||
<artifact name="kotlin-stdlib-2.0.0.jar">
|
||||
<sha256 value="240938c4aab8e73e888703e3e7d3f87383ffe5bd536d6d5e3c100d4cd0379fcf" origin="Generated by Gradle"/>
|
||||
</artifact>
|
||||
<artifact name="kotlin-stdlib-2.0.0.module">
|
||||
<sha256 value="64f96ea8e7b9896731052241ffd3a265f8274d761e5fe9dc088ac45b31718341" origin="Generated by Gradle"/>
|
||||
</artifact>
|
||||
</component>
|
||||
<component group="org.jetbrains.kotlin" name="kotlin-stdlib-common" version="1.4.21">
|
||||
<artifact name="kotlin-stdlib-common-1.4.21.jar">
|
||||
<sha256 value="812cf197d9c4c67e1f47f95e2d72a9b600f0d1124560617bfe9850773eccbcff" origin="Generated by Gradle"/>
|
||||
|
@ -8043,6 +8054,11 @@ https://docs.gradle.org/current/userguide/dependency_verification.html
|
|||
<sha256 value="f93c9e9abf8d52d8e8fd8e851aa802ecec55132161c4aeee7d3cd924bf794246" origin="Generated by Gradle"/>
|
||||
</artifact>
|
||||
</component>
|
||||
<component group="org.jetbrains.kotlin" name="kotlin-stdlib-common" version="2.0.0">
|
||||
<artifact name="kotlin-stdlib-common-2.0.0.module">
|
||||
<sha256 value="2335187440c51d0d69e1b906fefc31f6169691c8598177b0e610c9b9a92ce6b5" origin="Generated by Gradle"/>
|
||||
</artifact>
|
||||
</component>
|
||||
<component group="org.jetbrains.kotlin" name="kotlin-stdlib-jdk7" version="1.4.10">
|
||||
<artifact name="kotlin-stdlib-jdk7-1.4.10.jar">
|
||||
<sha256 value="f9566380c08722c780ce33ceee23e98ddf765ca98fabd3e2fabae7975c8d232b" origin="Generated by Gradle"/>
|
||||
|
@ -8291,6 +8307,14 @@ https://docs.gradle.org/current/userguide/dependency_verification.html
|
|||
<sha256 value="f00af0920cb7dc4611205ff592eec5aa0071bc959b3797026abfe43cf14bfad4" origin="Generated by Gradle"/>
|
||||
</artifact>
|
||||
</component>
|
||||
<component group="org.jetbrains.kotlinx" name="kotlinx-coroutines-android" version="1.9.0">
|
||||
<artifact name="kotlinx-coroutines-android-1.9.0.jar">
|
||||
<sha256 value="bd783acd2f9738845d58380f46f45b5cde95d0cb03027d56725338b26bfc4d72" origin="Generated by Gradle"/>
|
||||
</artifact>
|
||||
<artifact name="kotlinx-coroutines-android-1.9.0.module">
|
||||
<sha256 value="7df1e04d77f0cc411890d999a9449b0e3bcd4f159a46c3060b110204ca607d43" origin="Generated by Gradle"/>
|
||||
</artifact>
|
||||
</component>
|
||||
<component group="org.jetbrains.kotlinx" name="kotlinx-coroutines-core" version="1.5.0">
|
||||
<artifact name="kotlinx-coroutines-core-1.5.0.module">
|
||||
<sha256 value="d8a26a896da32fb1f8c3f13fe41cb798a612a1c1ddf3a555d82ee1ff16ef13d3" origin="Generated by Gradle"/>
|
||||
|
@ -8346,6 +8370,14 @@ https://docs.gradle.org/current/userguide/dependency_verification.html
|
|||
<sha256 value="daf50f1c9404b224a1d6dd5286f8e8ee7d63fe807f78ea98f71795c183b6025f" origin="Generated by Gradle"/>
|
||||
</artifact>
|
||||
</component>
|
||||
<component group="org.jetbrains.kotlinx" name="kotlinx-coroutines-core" version="1.9.0">
|
||||
<artifact name="kotlinx-coroutines-core-1.9.0.module">
|
||||
<sha256 value="ad534034a953b4e12cbeeb874c66adf8b1ca14df15fe0d2e6547aa34e86dfeca" origin="Generated by Gradle"/>
|
||||
</artifact>
|
||||
<artifact name="kotlinx-coroutines-core-metadata-1.9.0.jar">
|
||||
<sha256 value="7089c33c145865020760d3dbca5e4634133cc3dd7feb926e830f6de6ede28ac6" origin="Generated by Gradle"/>
|
||||
</artifact>
|
||||
</component>
|
||||
<component group="org.jetbrains.kotlinx" name="kotlinx-coroutines-core-jvm" version="1.5.0">
|
||||
<artifact name="kotlinx-coroutines-core-jvm-1.5.0.jar">
|
||||
<sha256 value="78d6cc7135f84d692ff3752fcfd1fa1bbe0940d7df70652e4f1eaeec0c78afbb" origin="Generated by Gradle"/>
|
||||
|
@ -8399,6 +8431,14 @@ https://docs.gradle.org/current/userguide/dependency_verification.html
|
|||
<sha256 value="09b81c9d11c2deebf133ad87b707927c1f04099cb611ef008c7725b3eb308329" origin="Generated by Gradle"/>
|
||||
</artifact>
|
||||
</component>
|
||||
<component group="org.jetbrains.kotlinx" name="kotlinx-coroutines-core-jvm" version="1.9.0">
|
||||
<artifact name="kotlinx-coroutines-core-jvm-1.9.0.jar">
|
||||
<sha256 value="ad89c2892235e670f222d819cb3d81188143cb19a05b59df9889ae4269f5c70a" origin="Generated by Gradle"/>
|
||||
</artifact>
|
||||
<artifact name="kotlinx-coroutines-core-jvm-1.9.0.module">
|
||||
<sha256 value="b321a899e40d3ce345707aa2cfda9983ad0dcc69fea74a9b8bf906a16c1cf8a9" origin="Generated by Gradle"/>
|
||||
</artifact>
|
||||
</component>
|
||||
<component group="org.jetbrains.kotlinx" name="kotlinx-coroutines-play-services" version="1.8.1">
|
||||
<artifact name="kotlinx-coroutines-play-services-1.8.1.jar">
|
||||
<sha256 value="f90c2c76cab2fb3db89bf8a742bb4f36cba43e52472aeff3919e9143bd5ec072" origin="Generated by Gradle"/>
|
||||
|
@ -8407,6 +8447,14 @@ https://docs.gradle.org/current/userguide/dependency_verification.html
|
|||
<sha256 value="2eb0b5c00791576c33079a0750ef3ded335c180f60043d51a19c799f8b737bc5" origin="Generated by Gradle"/>
|
||||
</artifact>
|
||||
</component>
|
||||
<component group="org.jetbrains.kotlinx" name="kotlinx-coroutines-play-services" version="1.9.0">
|
||||
<artifact name="kotlinx-coroutines-play-services-1.9.0.jar">
|
||||
<sha256 value="4cb6b6321df1664b99fee756de63f6af65ccb4f68936f26cf76c211dea1b23ae" origin="Generated by Gradle"/>
|
||||
</artifact>
|
||||
<artifact name="kotlinx-coroutines-play-services-1.9.0.module">
|
||||
<sha256 value="93dcee9a831a2f9dcda9528bab28d724e15e8a0bfec4194ef81d4ec7e7301ff2" origin="Generated by Gradle"/>
|
||||
</artifact>
|
||||
</component>
|
||||
<component group="org.jetbrains.kotlinx" name="kotlinx-coroutines-reactive" version="1.3.9">
|
||||
<artifact name="kotlinx-coroutines-reactive-1.3.9.module">
|
||||
<sha256 value="afa2e53b31073b3888dc2bb526dc78cf09d78ba5993a1f12f71ff02d94b4abd0" origin="Generated by Gradle"/>
|
||||
|
@ -8420,6 +8468,14 @@ https://docs.gradle.org/current/userguide/dependency_verification.html
|
|||
<sha256 value="9090472f51918695de4084e8d6edf186b1d2f90babe063629f03d313e5d67e6b" origin="Generated by Gradle"/>
|
||||
</artifact>
|
||||
</component>
|
||||
<component group="org.jetbrains.kotlinx" name="kotlinx-coroutines-reactive" version="1.9.0">
|
||||
<artifact name="kotlinx-coroutines-reactive-1.9.0.jar">
|
||||
<sha256 value="4e889ac740e0ba9e6d3a429afc1a908c5a172649d0733cc56bdb6cafc59384d4" origin="Generated by Gradle"/>
|
||||
</artifact>
|
||||
<artifact name="kotlinx-coroutines-reactive-1.9.0.module">
|
||||
<sha256 value="7d2d62b887bd6fe9811cb4e553551e32f5954f58d46ce588e34e954946f60c5c" origin="Generated by Gradle"/>
|
||||
</artifact>
|
||||
</component>
|
||||
<component group="org.jetbrains.kotlinx" name="kotlinx-coroutines-rx3" version="1.3.9">
|
||||
<artifact name="kotlinx-coroutines-rx3-1.3.9.module">
|
||||
<sha256 value="717591b07c7875391cd592779cd60b2a2f87d2a7e556f2e94d3459fb1e413b1b" origin="Generated by Gradle"/>
|
||||
|
@ -8433,6 +8489,14 @@ https://docs.gradle.org/current/userguide/dependency_verification.html
|
|||
<sha256 value="c149e9405417ece9ab4207aad5990c3c6f42776fc750c49a27ea99e32a738264" origin="Generated by Gradle"/>
|
||||
</artifact>
|
||||
</component>
|
||||
<component group="org.jetbrains.kotlinx" name="kotlinx-coroutines-rx3" version="1.9.0">
|
||||
<artifact name="kotlinx-coroutines-rx3-1.9.0.jar">
|
||||
<sha256 value="f117db86962fa918ae20a8ef2668d5797123d957b1c2c5691ba375f7e5a8d7d9" origin="Generated by Gradle"/>
|
||||
</artifact>
|
||||
<artifact name="kotlinx-coroutines-rx3-1.9.0.module">
|
||||
<sha256 value="ed4c120c4904bbe73c38c431eab109a7ef8e5de1ababa8fd87445915b0ad59db" origin="Generated by Gradle"/>
|
||||
</artifact>
|
||||
</component>
|
||||
<component group="org.jetbrains.kotlinx" name="kotlinx-coroutines-swing" version="1.8.1">
|
||||
<artifact name="kotlinx-coroutines-swing-1.8.1.jar">
|
||||
<sha256 value="0c809e79dc64ab5bc9b790389fe491cc19d3574345a431276966fbc09994a659" origin="Generated by Gradle"/>
|
||||
|
@ -8441,6 +8505,30 @@ https://docs.gradle.org/current/userguide/dependency_verification.html
|
|||
<sha256 value="2915188bf6dde89157cccb480bfa1249f010917a8e1fad340c9dbef6d7e0229b" origin="Generated by Gradle"/>
|
||||
</artifact>
|
||||
</component>
|
||||
<component group="org.jetbrains.kotlinx" name="kotlinx-coroutines-swing" version="1.9.0">
|
||||
<artifact name="kotlinx-coroutines-swing-1.9.0.jar">
|
||||
<sha256 value="ce506d82f3642651a39fc706f3d2928d846772a64c92e749f018d7f0f216826f" origin="Generated by Gradle"/>
|
||||
</artifact>
|
||||
<artifact name="kotlinx-coroutines-swing-1.9.0.module">
|
||||
<sha256 value="6b04c3d5e7b1a4bfe2813fc3039393c4fe5dda7f03d75d703ca23efb79de79c7" origin="Generated by Gradle"/>
|
||||
</artifact>
|
||||
</component>
|
||||
<component group="org.jetbrains.kotlinx" name="kotlinx-coroutines-test" version="1.9.0">
|
||||
<artifact name="kotlinx-coroutines-test-1.9.0.module">
|
||||
<sha256 value="3a5f38700b2a4eb290e3507a0e3e4f9cc0bd63e70cca7f15e911dbf37c4f44ca" origin="Generated by Gradle"/>
|
||||
</artifact>
|
||||
<artifact name="kotlinx-coroutines-test-metadata-1.9.0.jar">
|
||||
<sha256 value="b9f0603718765fe9f93b4a139198ada254f11c7bf18e48c99629a3a73ce281d1" origin="Generated by Gradle"/>
|
||||
</artifact>
|
||||
</component>
|
||||
<component group="org.jetbrains.kotlinx" name="kotlinx-coroutines-test-jvm" version="1.9.0">
|
||||
<artifact name="kotlinx-coroutines-test-jvm-1.9.0.jar">
|
||||
<sha256 value="68b87fa90db3dab1e794ff6078364087c07f87e5e4a4d0c8d3272a222ee8fe7e" origin="Generated by Gradle"/>
|
||||
</artifact>
|
||||
<artifact name="kotlinx-coroutines-test-jvm-1.9.0.module">
|
||||
<sha256 value="e2364beb540720b3c117957853379d4ae222058ad88f73a2ac73026c836f839f" origin="Generated by Gradle"/>
|
||||
</artifact>
|
||||
</component>
|
||||
<component group="org.jetbrains.kotlinx" name="kotlinx-serialization-core" version="1.0.1">
|
||||
<artifact name="kotlinx-serialization-core-1.0.1.module">
|
||||
<sha256 value="9d0f1f6db25e394c89d58838eaebf3eabe360ef8ceb98aa3b9a4283532a54077" origin="Generated by Gradle"/>
|
||||
|
|
Loading…
Add table
Reference in a new issue