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

Commit 6c1b577a authored by Treehugger Robot's avatar Treehugger Robot Committed by Android (Google) Code Review
Browse files

Merge "Make mapElementsLazily thread-safe" into main

parents 7bce80b3 d24a9b14
Loading
Loading
Loading
Loading
+29 −17
Original line number Diff line number Diff line
@@ -47,7 +47,6 @@ import kotlinx.coroutines.flow.distinctUntilChanged
import kotlinx.coroutines.flow.filterIsInstance
import kotlinx.coroutines.flow.flowOn
import kotlinx.coroutines.flow.map
import kotlinx.coroutines.flow.onEach
import kotlinx.coroutines.flow.onStart
import kotlinx.coroutines.flow.scan
import kotlinx.coroutines.flow.shareIn
@@ -155,10 +154,11 @@ constructor(
                            is DisplayEvent.Changed -> previousIds + id
                        }
                    }
                    .shareIn(
                    .distinctUntilChanged()
                    .stateIn(
                        bgApplicationScope,
                        started = SharingStarted.WhileSubscribed(),
                        replay = 1
                        SharingStarted.WhileSubscribed(),
                        emptySet(),
                    )
            } else {
                oldEnabledDisplays.map { enabledDisplaysSet ->
@@ -177,7 +177,8 @@ constructor(
            enabledDisplayIds
                .mapElementsLazily { displayId -> getDisplay(displayId) }
                .flowOn(backgroundCoroutineDispatcher)
                .debugLog("enabledDisplayIds")
                .debugLog("enabledDisplays")
                .stateIn(bgApplicationScope, SharingStarted.WhileSubscribed(), emptySet())
        } else {
            oldEnabledDisplays
        }
@@ -355,20 +356,31 @@ constructor(
     * [createValue] returns a null element, it will not be added in the output set.
     */
    private fun <T, V> Flow<Set<T>>.mapElementsLazily(createValue: (T) -> V?): Flow<Set<V>> {
        var initialSet = emptySet<T>()
        val currentMap = mutableMapOf<T, V>()
        var resultSet = emptySet<V>()
        return onEach { currentSet ->
                val removed = initialSet - currentSet
                val added = currentSet - initialSet
                if (added.isNotEmpty() || removed.isNotEmpty()) {
                    added.forEach { key: T -> createValue(key)?.let { currentMap[key] = it } }
                    removed.forEach { key: T -> currentMap.remove(key) }
                    resultSet = currentMap.values.toSet() // Creates a **copy** of values
                }
                initialSet = currentSet
            }
            .map { resultSet }
        data class State<T, V>(
            val previousSet: Set<T>,
            // Caches T values from the previousSet that were already converted to V
            val valueMap: Map<T, V>,
            val resultSet: Set<V>
        )

        val initialState = State(emptySet<T>(), emptyMap(), emptySet<V>())

        return this.scan(initialState) { state, currentSet ->
                if (currentSet == state.previousSet) {
                    state
                } else {
                    val removed = state.previousSet - currentSet
                    val added = currentSet - state.previousSet
                    val newMap = state.valueMap.toMutableMap()

                    added.forEach { key -> createValue(key)?.let { newMap[key] = it } }
                    removed.forEach { key -> newMap.remove(key) }

                    val resultSet = newMap.values.toSet()
                    State(currentSet, newMap, resultSet)
                }
            }
            .map { it.resultSet }
    }

    private companion object {