diff --git a/app/src/main/java/org/koitharu/kotatsu/base/ui/BaseViewModel.kt b/app/src/main/java/org/koitharu/kotatsu/base/ui/BaseViewModel.kt index f4904f8ed..f17e3aa9f 100644 --- a/app/src/main/java/org/koitharu/kotatsu/base/ui/BaseViewModel.kt +++ b/app/src/main/java/org/koitharu/kotatsu/base/ui/BaseViewModel.kt @@ -1,5 +1,6 @@ package org.koitharu.kotatsu.base.ui +import androidx.lifecycle.LiveData import androidx.lifecycle.ViewModel import androidx.lifecycle.viewModelScope import kotlin.coroutines.CoroutineContext @@ -11,8 +12,14 @@ import org.koitharu.kotatsu.utils.ext.printStackTraceDebug abstract class BaseViewModel : ViewModel() { - val onError = SingleLiveEvent() - val isLoading = CountedBooleanLiveData() + protected val loadingCounter = CountedBooleanLiveData() + protected val errorEvent = SingleLiveEvent() + + val onError: LiveData + get() = errorEvent + + val isLoading: LiveData + get() = loadingCounter protected fun launchJob( context: CoroutineContext = EmptyCoroutineContext, @@ -25,18 +32,18 @@ abstract class BaseViewModel : ViewModel() { start: CoroutineStart = CoroutineStart.DEFAULT, block: suspend CoroutineScope.() -> Unit ): Job = viewModelScope.launch(context + createErrorHandler(), start) { - isLoading.postValue(true) + loadingCounter.increment() try { block() } finally { - isLoading.postValue(false) + loadingCounter.decrement() } } private fun createErrorHandler() = CoroutineExceptionHandler { _, throwable -> throwable.printStackTraceDebug() if (throwable !is CancellationException) { - onError.postCall(throwable) + errorEvent.postCall(throwable) } } } \ No newline at end of file diff --git a/app/src/main/java/org/koitharu/kotatsu/base/ui/util/CountedBooleanLiveData.kt b/app/src/main/java/org/koitharu/kotatsu/base/ui/util/CountedBooleanLiveData.kt index cb54ef7db..d654e541d 100644 --- a/app/src/main/java/org/koitharu/kotatsu/base/ui/util/CountedBooleanLiveData.kt +++ b/app/src/main/java/org/koitharu/kotatsu/base/ui/util/CountedBooleanLiveData.kt @@ -1,20 +1,31 @@ package org.koitharu.kotatsu.base.ui.util -import androidx.lifecycle.MutableLiveData +import androidx.annotation.AnyThread +import androidx.lifecycle.LiveData +import java.util.concurrent.atomic.AtomicInteger -class CountedBooleanLiveData : MutableLiveData(false) { +class CountedBooleanLiveData : LiveData(false) { - private var counter = 0 + private val counter = AtomicInteger(0) - override fun setValue(value: Boolean) { - if (value) { - counter++ - } else { - counter-- + @AnyThread + fun increment() { + if (counter.getAndIncrement() == 0) { + postValue(true) } - val newValue = counter > 0 - if (newValue != this.value) { - super.setValue(newValue) + } + + @AnyThread + fun decrement() { + if (counter.decrementAndGet() == 0) { + postValue(false) + } + } + + @AnyThread + fun reset() { + if (counter.getAndSet(0) != 0) { + postValue(false) } } } \ No newline at end of file diff --git a/app/src/main/java/org/koitharu/kotatsu/history/ui/HistoryListViewModel.kt b/app/src/main/java/org/koitharu/kotatsu/history/ui/HistoryListViewModel.kt index 60190535b..1768c2a5f 100644 --- a/app/src/main/java/org/koitharu/kotatsu/history/ui/HistoryListViewModel.kt +++ b/app/src/main/java/org/koitharu/kotatsu/history/ui/HistoryListViewModel.kt @@ -2,10 +2,13 @@ package org.koitharu.kotatsu.history.ui import androidx.lifecycle.MutableLiveData import androidx.lifecycle.viewModelScope +import java.util.* +import java.util.concurrent.TimeUnit import kotlinx.coroutines.Dispatchers import kotlinx.coroutines.flow.catch import kotlinx.coroutines.flow.combine import kotlinx.coroutines.flow.onEach +import kotlinx.coroutines.flow.onStart import org.koitharu.kotatsu.R import org.koitharu.kotatsu.base.domain.ReversibleHandle import org.koitharu.kotatsu.base.domain.plus @@ -23,8 +26,6 @@ import org.koitharu.kotatsu.utils.SingleLiveEvent import org.koitharu.kotatsu.utils.ext.asLiveDataDistinct import org.koitharu.kotatsu.utils.ext.daysDiff import org.koitharu.kotatsu.utils.ext.onFirst -import java.util.* -import java.util.concurrent.TimeUnit class HistoryListViewModel( private val repository: HistoryRepository, @@ -55,8 +56,10 @@ class HistoryListViewModel( ) else -> mapList(list, grouped, mode) } + }.onStart { + loadingCounter.increment() }.onFirst { - isLoading.postValue(false) + loadingCounter.decrement() }.catch { it.toErrorState(canRetry = false) }.asLiveDataDistinct(viewModelScope.coroutineContext + Dispatchers.Default, listOf(LoadingState)) diff --git a/app/src/main/java/org/koitharu/kotatsu/remotelist/ui/RemoteListViewModel.kt b/app/src/main/java/org/koitharu/kotatsu/remotelist/ui/RemoteListViewModel.kt index 9c9922598..b57f0b30b 100644 --- a/app/src/main/java/org/koitharu/kotatsu/remotelist/ui/RemoteListViewModel.kt +++ b/app/src/main/java/org/koitharu/kotatsu/remotelist/ui/RemoteListViewModel.kt @@ -136,7 +136,7 @@ class RemoteListViewModel( e.printStackTraceDebug() listError.value = e if (!mangaList.value.isNullOrEmpty()) { - onError.postCall(e) + errorEvent.postCall(e) } } } diff --git a/app/src/main/java/org/koitharu/kotatsu/search/ui/global/GlobalSearchViewModel.kt b/app/src/main/java/org/koitharu/kotatsu/search/ui/global/GlobalSearchViewModel.kt index b516a026a..3511f3b3d 100644 --- a/app/src/main/java/org/koitharu/kotatsu/search/ui/global/GlobalSearchViewModel.kt +++ b/app/src/main/java/org/koitharu/kotatsu/search/ui/global/GlobalSearchViewModel.kt @@ -67,19 +67,19 @@ class GlobalSearchViewModel( searchJob = repository.globalSearch(query) .catch { e -> listError.value = e - isLoading.postValue(false) + loadingCounter.reset() }.onStart { mangaList.value = null listError.value = null - isLoading.postValue(true) + loadingCounter.increment() hasNextPage.value = true }.onEmpty { mangaList.value = emptyList() }.onCompletion { - isLoading.postValue(false) + loadingCounter.reset() hasNextPage.value = false }.onFirst { - isLoading.postValue(false) + loadingCounter.reset() }.onEach { mangaList.value = mangaList.value?.plus(it) ?: listOf(it) }.launchIn(viewModelScope + Dispatchers.Default) diff --git a/app/src/main/java/org/koitharu/kotatsu/suggestions/ui/SuggestionsViewModel.kt b/app/src/main/java/org/koitharu/kotatsu/suggestions/ui/SuggestionsViewModel.kt index 634eeb757..832922ea3 100644 --- a/app/src/main/java/org/koitharu/kotatsu/suggestions/ui/SuggestionsViewModel.kt +++ b/app/src/main/java/org/koitharu/kotatsu/suggestions/ui/SuggestionsViewModel.kt @@ -4,6 +4,7 @@ import androidx.lifecycle.viewModelScope import kotlinx.coroutines.Dispatchers import kotlinx.coroutines.flow.catch import kotlinx.coroutines.flow.combine +import kotlinx.coroutines.flow.onStart import org.koitharu.kotatsu.R import org.koitharu.kotatsu.core.prefs.AppSettings import org.koitharu.kotatsu.list.ui.MangaListViewModel @@ -37,8 +38,10 @@ class SuggestionsViewModel( list.toUi(this, mode) } } + }.onStart { + loadingCounter.increment() }.onFirst { - isLoading.postValue(false) + loadingCounter.decrement() }.catch { it.toErrorState(canRetry = false) }.asLiveDataDistinct(