Donate to e Foundation | Murena handsets with /e/OS | Own a part of Murena! Learn more

Commit 60417deb authored by Steve Elliott's avatar Steve Elliott
Browse files

Add optional emitFirstEvent param to setChanges()

There are situations where you really don't want to treat the first
event as a change; for these cases, you can use

    setChanges(emitFirstEvent = false)

Unfortunately, due to the usage of distinctUntilChanged() in the
definition of setChanges(), there is no (obvious) way to recover this
behavior on the client-side by dropping / filtering events, so we need
to add this option to the method signature.

Bug: 241121499
Test: atest SetChangesFlowTest
Change-Id: I65ce50c12fb6299fa490887555e40db69f0eebdf
parent 7b21fb6b
Loading
Loading
Loading
Loading
+22 −3
Original line number Diff line number Diff line
@@ -74,10 +74,19 @@ data class WithPrev<T>(val previousValue: T, val newValue: T)
/**
 * Returns a new [Flow] that combines the [Set] changes between each emission from [this] using
 * [transform].
 *
 * If [emitFirstEvent] is `true`, then the first [Set] emitted from the upstream [Flow] will cause
 * a change event to be emitted that contains no removals, and all elements from that first [Set]
 * as additions.
 *
 * If [emitFirstEvent] is `false`, then the first emission is ignored and no changes are emitted
 * until a second [Set] has been emitted from the upstream [Flow].
 */
fun <T, R> Flow<Set<T>>.setChangesBy(
    transform: suspend (removed: Set<T>, added: Set<T>) -> R,
): Flow<R> = onStart { emit(emptySet()) }.distinctUntilChanged()
    emitFirstEvent: Boolean = true,
): Flow<R> = (if (emitFirstEvent) onStart { emit(emptySet()) } else this)
    .distinctUntilChanged()
    .pairwiseBy { old: Set<T>, new: Set<T> ->
        // If an element was present in the old set, but not the new one, then it was removed
        val removed = old - new
@@ -86,8 +95,18 @@ fun <T, R> Flow<Set<T>>.setChangesBy(
        transform(removed, added)
    }

/** Returns a new [Flow] that produces the [Set] changes between each emission from [this]. */
fun <T> Flow<Set<T>>.setChanges(): Flow<SetChanges<T>> = setChangesBy(::SetChanges)
/**
 * Returns a new [Flow] that produces the [Set] changes between each emission from [this].
 *
 * If [emitFirstEvent] is `true`, then the first [Set] emitted from the upstream [Flow] will cause
 * a change event to be emitted that contains no removals, and all elements from that first [Set]
 * as additions.
 *
 * If [emitFirstEvent] is `false`, then the first emission is ignored and no changes are emitted
 * until a second [Set] has been emitted from the upstream [Flow].
 */
fun <T> Flow<Set<T>>.setChanges(emitFirstEvent: Boolean = true): Flow<SetChanges<T>> =
    setChangesBy(::SetChanges, emitFirstEvent)

/** Contains the difference in elements between two [Set]s. */
data class SetChanges<T>(
+11 −0
Original line number Diff line number Diff line
@@ -127,6 +127,17 @@ class SetChangesFlowTest : SysuiTestCase() {
                )
            )
    }

    @Test
    fun dontEmitFirstEvent() = runBlocking {
        assertThatFlow(flowOf(setOf(1, 2), setOf(2, 3)).setChanges(emitFirstEvent = false))
            .emitsExactly(
                SetChanges(
                    removed = setOf(1),
                    added = setOf(3),
                )
            )
    }
}

private fun <T> assertThatFlow(flow: Flow<T>) = object {