Skip to content

Instantly share code, notes, and snippets.

@AdamSHurwitz
Last active Jun 23, 2019
Embed
What would you like to do?
MediatorLiveData
class ContentViewModel(application: Application) : AndroidViewModel(application) {
...
fun processEvent(event: ContentViewEvent) {
when (event) {
is ContentViewEvent.PlayerLoad ->
_playerViewState.value = PlayerViewState(getContentPlayer(
event.contentId, event.filePath, event.previewImageUrl))
...
}
}
private fun getContentPlayer(contentId: String, filePath: String, imageUrl: String) =
//'getContentUri' and `bitmapToByteArray' return LiveData.
//LiveData is wrapped in an Event object to ensure data is only observed once, thus requiring 'peekEvent'
//to return data.
getContentUri(contentId, filePath).combineLiveData(bitmapToByteArray(imageUrl)) { a, b ->
Event(ContentResult.ContentPlayer(a.peekEvent().uri, b.peekEvent().image,
getLiveDataErrors(a, b)
))
}
/**
* Sets the value to the result of a function that is called when both `LiveData`s have data
* or when they receive updates after that.
*/
private fun <T, A, B> LiveData<A>.combineLiveData(other: LiveData<B>, onChange: (A, B) -> T) =
MediatorLiveData<T>().also { result ->
var source1emitted = false
var source2emitted = false
val mergeF = {
val source1Value = this.value
val source2Value = other.value
if (source1emitted && source2emitted)
result.value = onChange.invoke(source1Value!!, source2Value!!)
}
result.addSource(this) { source1emitted = true; mergeF.invoke() }
result.addSource(other) { source2emitted = true; mergeF.invoke() }
}
private fun getLiveDataErrors(a: Event<ContentResult.ContentUri>, b: Event<ContentResult.ContentBitmap>) =
a.peekEvent().errorMessage.apply { if (this.isNotEmpty()) this }.apply {
b.peekEvent().errorMessage.also {
if (it.isNotEmpty()) this.plus(" " + it)
}
}
...
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment