Remove dependency on livedata libraries (#1453)

## Description
Refactors all usages of `LiveData` into flows in keeping with best
practices. This also includes refactoring several `ViewModel`s to
combine multiple `LiveData` into a new single `StateFlow`.

There are no user facing changes

### Related issues
None

### Testing
Emulator

## Screenshots
N/A

## AI or LLM usage
None
This commit is contained in:
Damontecres 2026-05-25 08:27:19 -04:00 • committed by GitHub
parent 982d9e6fe8
commit 0b450fc0ff
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
90 changed files with 2170 additions and 2283 deletions

View file

@ -1,27 +0,0 @@
package com.github.damontecres.wholphin.util
import androidx.lifecycle.MutableLiveData
/**
* A [MutableLiveData] that only notifies observers if the value does not equal the previous value
*/
class EqualityMutableLiveData<T> : MutableLiveData<T> {
constructor() : super()
constructor(value: T) : super(value)
override fun setValue(value: T?) {
if (value != getValue()) {
super.setValue(value)
}
}
fun setValueNoCheck(value: T?) {
super.setValue(value)
}
override fun postValue(value: T?) {
if (value != getValue()) {
super.postValue(value)
}
}
}

View file

@ -1,52 +0,0 @@
package com.github.damontecres.wholphin.util
import android.widget.Toast
import androidx.lifecycle.MutableLiveData
import com.github.damontecres.wholphin.WholphinApplication
import com.github.damontecres.wholphin.ui.showToast
import kotlinx.coroutines.CancellationException
import kotlinx.coroutines.CoroutineExceptionHandler
import kotlinx.coroutines.Dispatchers
import kotlinx.coroutines.runBlocking
import kotlinx.coroutines.withContext
import timber.log.Timber
import kotlin.coroutines.CoroutineContext
/**
* A [CoroutineExceptionHandler] that is aware of a [LoadingState] and will set it to error if an exception occurs in the coroutine.
*/
class LoadingExceptionHandler(
private val loadingState: MutableLiveData<LoadingState>,
private val errorMessage: String?,
private val autoToast: Boolean = false,
) : CoroutineExceptionHandler {
override val key: CoroutineContext.Key<*>
get() = CoroutineExceptionHandler
override fun handleException(
context: CoroutineContext,
exception: Throwable,
) {
if (exception is CancellationException) {
// Don't log/toast cancellations
return
}
Timber.e(exception, "Exception in coroutine")
runBlocking {
withContext(Dispatchers.Main) {
loadingState.value =
LoadingState.Error(
message = errorMessage,
exception = exception,
)
}
if (autoToast) {
showToast(
WholphinApplication.instance,
"Error: ${exception.message}",
Toast.LENGTH_LONG,
)
}
}
}
}

View file

@ -103,3 +103,5 @@ sealed interface DataLoadingState<out T> {
listOfNotNull(message, exception?.localizedMessage).joinToString(" - ")
}
}
val <T> DataLoadingState<T>.successValue: T? get() = (this as? DataLoadingState.Success<T>)?.data