compose: add StateFlow/LiveData collectForced helpers
Bridges Flows whose values have broken equals() (the gplayapi StreamBundle/StreamCluster id-only-equals quirk) into Compose state by forcing recomposition via neverEqualPolicy.
This commit is contained in:
@@ -0,0 +1,74 @@
|
|||||||
|
/*
|
||||||
|
* SPDX-FileCopyrightText: 2026 Aurora OSS
|
||||||
|
* SPDX-License-Identifier: GPL-3.0-or-later
|
||||||
|
*/
|
||||||
|
|
||||||
|
package com.aurora.store.compose.composition
|
||||||
|
|
||||||
|
import androidx.compose.runtime.Composable
|
||||||
|
import androidx.compose.runtime.DisposableEffect
|
||||||
|
import androidx.compose.runtime.LaunchedEffect
|
||||||
|
import androidx.compose.runtime.MutableState
|
||||||
|
import androidx.compose.runtime.State
|
||||||
|
import androidx.compose.runtime.mutableStateOf
|
||||||
|
import androidx.compose.runtime.neverEqualPolicy
|
||||||
|
import androidx.compose.runtime.remember
|
||||||
|
import androidx.lifecycle.Lifecycle
|
||||||
|
import androidx.lifecycle.LiveData
|
||||||
|
import androidx.lifecycle.Observer
|
||||||
|
import androidx.lifecycle.compose.LocalLifecycleOwner
|
||||||
|
import androidx.lifecycle.repeatOnLifecycle
|
||||||
|
import kotlinx.coroutines.flow.Flow
|
||||||
|
import kotlinx.coroutines.flow.StateFlow
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Collects a [StateFlow] as [State], always triggering recomposition on every emission
|
||||||
|
* regardless of structural equality, by using [neverEqualPolicy].
|
||||||
|
*/
|
||||||
|
@Composable
|
||||||
|
internal fun <T> StateFlow<T>.collectForced(): State<T> {
|
||||||
|
val lifecycleOwner = LocalLifecycleOwner.current
|
||||||
|
val state: MutableState<T> = remember(this) { mutableStateOf(value, neverEqualPolicy()) }
|
||||||
|
LaunchedEffect(this, lifecycleOwner) {
|
||||||
|
lifecycleOwner.repeatOnLifecycle(Lifecycle.State.STARTED) {
|
||||||
|
collect { state.value = it }
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return state
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Collects a [Flow] as [State] starting from [initial], always triggering recomposition on
|
||||||
|
* every emission regardless of structural equality, by using [neverEqualPolicy].
|
||||||
|
*
|
||||||
|
* Useful for non-state flows (e.g. [kotlinx.coroutines.flow.SharedFlow]) and for flows whose
|
||||||
|
* values have broken `equals` implementations that would otherwise be conflated upstream.
|
||||||
|
*/
|
||||||
|
@Composable
|
||||||
|
internal fun <T> Flow<T>.collectForced(initial: T): State<T> {
|
||||||
|
val lifecycleOwner = LocalLifecycleOwner.current
|
||||||
|
val state: MutableState<T> = remember(this) { mutableStateOf(initial, neverEqualPolicy()) }
|
||||||
|
LaunchedEffect(this, lifecycleOwner) {
|
||||||
|
lifecycleOwner.repeatOnLifecycle(Lifecycle.State.STARTED) {
|
||||||
|
collect { state.value = it }
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return state
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Observes a [LiveData] as [State], always triggering recomposition on every emission
|
||||||
|
* regardless of structural equality, by using [neverEqualPolicy].
|
||||||
|
*/
|
||||||
|
@Suppress("UNCHECKED_CAST")
|
||||||
|
@Composable
|
||||||
|
internal fun <T> LiveData<T>.observeForced(): State<T?> {
|
||||||
|
val lifecycleOwner = LocalLifecycleOwner.current
|
||||||
|
val state: MutableState<T?> = remember { mutableStateOf(value, neverEqualPolicy()) }
|
||||||
|
DisposableEffect(this, lifecycleOwner) {
|
||||||
|
val observer = Observer<T> { state.value = it }
|
||||||
|
observe(lifecycleOwner, observer)
|
||||||
|
onDispose { removeObserver(observer) }
|
||||||
|
}
|
||||||
|
return state
|
||||||
|
}
|
||||||
Reference in New Issue
Block a user