Merge "Add sample() Flow utility function" into tm-qpr-dev
This commit is contained in:
committed by
Android (Google) Code Review
commit
794fb27762
@@ -16,11 +16,15 @@
|
|||||||
|
|
||||||
package com.android.systemui.util.kotlin
|
package com.android.systemui.util.kotlin
|
||||||
|
|
||||||
|
import java.util.concurrent.atomic.AtomicReference
|
||||||
|
import kotlinx.coroutines.Dispatchers
|
||||||
|
import kotlinx.coroutines.coroutineScope
|
||||||
import kotlinx.coroutines.flow.Flow
|
import kotlinx.coroutines.flow.Flow
|
||||||
import kotlinx.coroutines.flow.collect
|
import kotlinx.coroutines.flow.collect
|
||||||
import kotlinx.coroutines.flow.distinctUntilChanged
|
import kotlinx.coroutines.flow.distinctUntilChanged
|
||||||
import kotlinx.coroutines.flow.flow
|
import kotlinx.coroutines.flow.flow
|
||||||
import kotlinx.coroutines.flow.onStart
|
import kotlinx.coroutines.flow.onStart
|
||||||
|
import kotlinx.coroutines.launch
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Returns a new [Flow] that combines the two most recent emissions from [this] using [transform].
|
* Returns a new [Flow] that combines the two most recent emissions from [this] using [transform].
|
||||||
@@ -116,3 +120,35 @@ data class SetChanges<T>(
|
|||||||
/** Elements that are present in the second [Set] but not in the first. */
|
/** Elements that are present in the second [Set] but not in the first. */
|
||||||
val added: Set<T>,
|
val added: Set<T>,
|
||||||
)
|
)
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Returns a new [Flow] that emits at the same rate as [this], but combines the emitted value with
|
||||||
|
* the most recent emission from [other] using [transform].
|
||||||
|
*
|
||||||
|
* Note that the returned Flow will not emit anything until [other] has emitted at least one value.
|
||||||
|
*/
|
||||||
|
fun <A, B, C> Flow<A>.sample(other: Flow<B>, transform: suspend (A, B) -> C): Flow<C> = flow {
|
||||||
|
coroutineScope {
|
||||||
|
val noVal = Any()
|
||||||
|
val sampledRef = AtomicReference(noVal)
|
||||||
|
val job = launch(Dispatchers.Unconfined) {
|
||||||
|
other.collect { sampledRef.set(it) }
|
||||||
|
}
|
||||||
|
collect {
|
||||||
|
val sampled = sampledRef.get()
|
||||||
|
if (sampled != noVal) {
|
||||||
|
@Suppress("UNCHECKED_CAST")
|
||||||
|
emit(transform(it, sampled as B))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
job.cancel()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Returns a new [Flow] that emits at the same rate as [this], but emits the most recently emitted
|
||||||
|
* value from [other] instead.
|
||||||
|
*
|
||||||
|
* Note that the returned Flow will not emit anything until [other] has emitted at least one value.
|
||||||
|
*/
|
||||||
|
fun <A> Flow<*>.sample(other: Flow<A>): Flow<A> = sample(other) { _, a -> a }
|
||||||
|
|||||||
@@ -28,12 +28,14 @@ import kotlinx.coroutines.flow.MutableStateFlow
|
|||||||
import kotlinx.coroutines.flow.asFlow
|
import kotlinx.coroutines.flow.asFlow
|
||||||
import kotlinx.coroutines.flow.emptyFlow
|
import kotlinx.coroutines.flow.emptyFlow
|
||||||
import kotlinx.coroutines.flow.filterIsInstance
|
import kotlinx.coroutines.flow.filterIsInstance
|
||||||
|
import kotlinx.coroutines.flow.flow
|
||||||
import kotlinx.coroutines.flow.flowOf
|
import kotlinx.coroutines.flow.flowOf
|
||||||
import kotlinx.coroutines.flow.merge
|
import kotlinx.coroutines.flow.merge
|
||||||
import kotlinx.coroutines.flow.takeWhile
|
import kotlinx.coroutines.flow.takeWhile
|
||||||
import kotlinx.coroutines.flow.toList
|
import kotlinx.coroutines.flow.toList
|
||||||
import kotlinx.coroutines.launch
|
import kotlinx.coroutines.launch
|
||||||
import kotlinx.coroutines.runBlocking
|
import kotlinx.coroutines.runBlocking
|
||||||
|
import kotlinx.coroutines.yield
|
||||||
import org.junit.Test
|
import org.junit.Test
|
||||||
import org.junit.runner.RunWith
|
import org.junit.runner.RunWith
|
||||||
|
|
||||||
@@ -140,6 +142,42 @@ class SetChangesFlowTest : SysuiTestCase() {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@SmallTest
|
||||||
|
@RunWith(AndroidTestingRunner::class)
|
||||||
|
class SampleFlowTest : SysuiTestCase() {
|
||||||
|
@Test
|
||||||
|
fun simple() = runBlocking {
|
||||||
|
assertThatFlow(flow { yield(); emit(1) }.sample(flowOf(2)) { a, b -> a to b })
|
||||||
|
.emitsExactly(1 to 2)
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
fun otherFlowNoValueYet() = runBlocking {
|
||||||
|
assertThatFlow(flowOf(1).sample(emptyFlow<Unit>()))
|
||||||
|
.emitsNothing()
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
fun multipleSamples() = runBlocking {
|
||||||
|
val samplee = MutableSharedFlow<Int>()
|
||||||
|
val sampler = flow {
|
||||||
|
emit(1)
|
||||||
|
samplee.emit(1)
|
||||||
|
emit(2)
|
||||||
|
samplee.emit(2)
|
||||||
|
samplee.emit(3)
|
||||||
|
emit(3)
|
||||||
|
emit(4)
|
||||||
|
}
|
||||||
|
assertThatFlow(sampler.sample(samplee) { a, b -> a to b })
|
||||||
|
.emitsExactly(
|
||||||
|
2 to 1,
|
||||||
|
3 to 3,
|
||||||
|
4 to 3,
|
||||||
|
)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
private fun <T> assertThatFlow(flow: Flow<T>) = object {
|
private fun <T> assertThatFlow(flow: Flow<T>) = object {
|
||||||
suspend fun emitsExactly(vararg emissions: T) =
|
suspend fun emitsExactly(vararg emissions: T) =
|
||||||
assertThat(flow.toList()).containsExactly(*emissions).inOrder()
|
assertThat(flow.toList()).containsExactly(*emissions).inOrder()
|
||||||
|
|||||||
Reference in New Issue
Block a user