mirror of
https://github.com/kiwix/kiwix-android.git
synced 2025-08-04 03:06:41 -04:00
Refactored the ZimManageViewModel to fetch the library reactively from the network and laid the groundwork for applying filters in network requests.
This commit is contained in:
parent
d9a10b30a6
commit
8b02f96c1c
@ -57,6 +57,7 @@ import org.kiwix.kiwixmobile.core.R.string
|
|||||||
import org.kiwix.kiwixmobile.core.base.BaseActivity
|
import org.kiwix.kiwixmobile.core.base.BaseActivity
|
||||||
import org.kiwix.kiwixmobile.core.base.BaseFragment
|
import org.kiwix.kiwixmobile.core.base.BaseFragment
|
||||||
import org.kiwix.kiwixmobile.core.base.FragmentActivityExtensions
|
import org.kiwix.kiwixmobile.core.base.FragmentActivityExtensions
|
||||||
|
import org.kiwix.kiwixmobile.core.data.remote.KiwixService.Companion.ITEMS_PER_PAGE
|
||||||
import org.kiwix.kiwixmobile.core.downloader.Downloader
|
import org.kiwix.kiwixmobile.core.downloader.Downloader
|
||||||
import org.kiwix.kiwixmobile.core.downloader.downloadManager.ZERO
|
import org.kiwix.kiwixmobile.core.downloader.downloadManager.ZERO
|
||||||
import org.kiwix.kiwixmobile.core.extensions.ActivityExtensions.hasNotificationPermission
|
import org.kiwix.kiwixmobile.core.extensions.ActivityExtensions.hasNotificationPermission
|
||||||
@ -76,6 +77,7 @@ import org.kiwix.kiwixmobile.core.navigateToAppSettings
|
|||||||
import org.kiwix.kiwixmobile.core.navigateToSettings
|
import org.kiwix.kiwixmobile.core.navigateToSettings
|
||||||
import org.kiwix.kiwixmobile.core.page.SEARCH_ICON_TESTING_TAG
|
import org.kiwix.kiwixmobile.core.page.SEARCH_ICON_TESTING_TAG
|
||||||
import org.kiwix.kiwixmobile.core.ui.components.NavigationIcon
|
import org.kiwix.kiwixmobile.core.ui.components.NavigationIcon
|
||||||
|
import org.kiwix.kiwixmobile.core.ui.components.ONE
|
||||||
import org.kiwix.kiwixmobile.core.ui.components.rememberBottomNavigationVisibility
|
import org.kiwix.kiwixmobile.core.ui.components.rememberBottomNavigationVisibility
|
||||||
import org.kiwix.kiwixmobile.core.ui.models.ActionMenuItem
|
import org.kiwix.kiwixmobile.core.ui.models.ActionMenuItem
|
||||||
import org.kiwix.kiwixmobile.core.ui.models.IconItem
|
import org.kiwix.kiwixmobile.core.ui.models.IconItem
|
||||||
@ -89,11 +91,13 @@ import org.kiwix.kiwixmobile.core.utils.SharedPreferenceUtil
|
|||||||
import org.kiwix.kiwixmobile.core.utils.dialog.AlertDialogShower
|
import org.kiwix.kiwixmobile.core.utils.dialog.AlertDialogShower
|
||||||
import org.kiwix.kiwixmobile.core.utils.dialog.DialogHost
|
import org.kiwix.kiwixmobile.core.utils.dialog.DialogHost
|
||||||
import org.kiwix.kiwixmobile.core.utils.dialog.KiwixDialog
|
import org.kiwix.kiwixmobile.core.utils.dialog.KiwixDialog
|
||||||
|
import org.kiwix.kiwixmobile.core.utils.files.Log
|
||||||
import org.kiwix.kiwixmobile.core.zim_manager.NetworkState
|
import org.kiwix.kiwixmobile.core.zim_manager.NetworkState
|
||||||
import org.kiwix.kiwixmobile.main.KiwixMainActivity
|
import org.kiwix.kiwixmobile.main.KiwixMainActivity
|
||||||
import org.kiwix.kiwixmobile.storage.STORAGE_SELECT_STORAGE_TITLE_TEXTVIEW_SIZE
|
import org.kiwix.kiwixmobile.storage.STORAGE_SELECT_STORAGE_TITLE_TEXTVIEW_SIZE
|
||||||
import org.kiwix.kiwixmobile.storage.StorageSelectDialog
|
import org.kiwix.kiwixmobile.storage.StorageSelectDialog
|
||||||
import org.kiwix.kiwixmobile.zimManager.ZimManageViewModel
|
import org.kiwix.kiwixmobile.zimManager.ZimManageViewModel
|
||||||
|
import org.kiwix.kiwixmobile.zimManager.ZimManageViewModel.OnlineLibraryRequest
|
||||||
import org.kiwix.kiwixmobile.zimManager.libraryView.AvailableSpaceCalculator
|
import org.kiwix.kiwixmobile.zimManager.libraryView.AvailableSpaceCalculator
|
||||||
import org.kiwix.kiwixmobile.zimManager.libraryView.adapter.LibraryListItem
|
import org.kiwix.kiwixmobile.zimManager.libraryView.adapter.LibraryListItem
|
||||||
import javax.inject.Inject
|
import javax.inject.Inject
|
||||||
@ -138,11 +142,37 @@ class OnlineLibraryFragment : BaseFragment(), FragmentActivityExtensions {
|
|||||||
isSearchActive = false,
|
isSearchActive = false,
|
||||||
searchText = "",
|
searchText = "",
|
||||||
searchValueChangedListener = { onSearchValueChanged(it) },
|
searchValueChangedListener = { onSearchValueChanged(it) },
|
||||||
clearSearchButtonClickListener = { onSearchClear() }
|
clearSearchButtonClickListener = { onSearchClear() },
|
||||||
|
onLoadMore = { totalItemShowingCount ->
|
||||||
|
loadMoreBooksFromNetwork(totalItemShowingCount)
|
||||||
|
},
|
||||||
|
isLoadingMoreItem = false
|
||||||
)
|
)
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private fun loadMoreBooksFromNetwork(totalItemShowingCount: Int) {
|
||||||
|
val totalResults = zimManageViewModel.onlineLibraryManager.totalResult
|
||||||
|
val totalPages =
|
||||||
|
zimManageViewModel.onlineLibraryManager.calculateTotalPages(
|
||||||
|
totalResults,
|
||||||
|
ITEMS_PER_PAGE
|
||||||
|
)
|
||||||
|
val currentPage = totalItemShowingCount / ITEMS_PER_PAGE
|
||||||
|
val nextPage = currentPage + ONE
|
||||||
|
|
||||||
|
if (nextPage < totalPages) {
|
||||||
|
zimManageViewModel.updateOnlineLibraryFilters(
|
||||||
|
zimManageViewModel.onlineLibraryRequest.value.copy(
|
||||||
|
page = nextPage,
|
||||||
|
isLoadMoreItem = true
|
||||||
|
)
|
||||||
|
)
|
||||||
|
} else {
|
||||||
|
Log.d("OnlineLibrary", "All pages loaded")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
private fun onSearchClear() {
|
private fun onSearchClear() {
|
||||||
onlineLibraryScreenState.value.update {
|
onlineLibraryScreenState.value.update {
|
||||||
copy(searchText = "")
|
copy(searchText = "")
|
||||||
@ -250,9 +280,17 @@ class OnlineLibraryFragment : BaseFragment(), FragmentActivityExtensions {
|
|||||||
}
|
}
|
||||||
observeViewModelData()
|
observeViewModelData()
|
||||||
showPreviouslySearchedTextInSearchView()
|
showPreviouslySearchedTextInSearchView()
|
||||||
startDownloadingLibrary()
|
startDownloadingLibrary(getOnlineLibraryRequest())
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private fun getOnlineLibraryRequest(): OnlineLibraryRequest = OnlineLibraryRequest(
|
||||||
|
null,
|
||||||
|
null,
|
||||||
|
null,
|
||||||
|
false,
|
||||||
|
1
|
||||||
|
)
|
||||||
|
|
||||||
private fun observeViewModelData() {
|
private fun observeViewModelData() {
|
||||||
zimManageViewModel.apply {
|
zimManageViewModel.apply {
|
||||||
// Observe when library items changes.
|
// Observe when library items changes.
|
||||||
@ -265,12 +303,13 @@ class OnlineLibraryFragment : BaseFragment(), FragmentActivityExtensions {
|
|||||||
}
|
}
|
||||||
// Observe when online library downloading.
|
// Observe when online library downloading.
|
||||||
onlineLibraryDownloading
|
onlineLibraryDownloading
|
||||||
.onEach {
|
.onEach { (initialLibraryDownloading, loadingMoreItem) ->
|
||||||
if (it) {
|
if (initialLibraryDownloading) {
|
||||||
showProgressBarOfFetchingOnlineLibrary()
|
showProgressBarOfFetchingOnlineLibrary()
|
||||||
} else {
|
} else {
|
||||||
hideProgressBarOfFetchingOnlineLibrary()
|
hideProgressBarOfFetchingOnlineLibrary()
|
||||||
}
|
}
|
||||||
|
onlineLibraryScreenState.value.update { copy(isLoadingMoreItem = loadingMoreItem) }
|
||||||
}.launchIn(viewLifecycleOwner.lifecycleScope)
|
}.launchIn(viewLifecycleOwner.lifecycleScope)
|
||||||
// Observe when library list refreshing e.g. applying filters.
|
// Observe when library list refreshing e.g. applying filters.
|
||||||
libraryListIsRefreshing.observe(
|
libraryListIsRefreshing.observe(
|
||||||
@ -369,7 +408,7 @@ class OnlineLibraryFragment : BaseFragment(), FragmentActivityExtensions {
|
|||||||
// User allowed downloading over mobile data.
|
// User allowed downloading over mobile data.
|
||||||
// Since the download flow now triggers only when appropriate,
|
// Since the download flow now triggers only when appropriate,
|
||||||
// we start the library download explicitly after updating the preference.
|
// we start the library download explicitly after updating the preference.
|
||||||
startDownloadingLibrary(true)
|
startDownloadingLibrary(getOnlineLibraryRequest())
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
context.toast(
|
context.toast(
|
||||||
@ -456,18 +495,15 @@ class OnlineLibraryFragment : BaseFragment(), FragmentActivityExtensions {
|
|||||||
NetworkState.CONNECTED -> {
|
NetworkState.CONNECTED -> {
|
||||||
when {
|
when {
|
||||||
NetworkUtils.isWiFi(requireContext()) -> {
|
NetworkUtils.isWiFi(requireContext()) -> {
|
||||||
if (!zimManageViewModel.isOnlineLibraryDownloading) {
|
|
||||||
refreshFragment(false)
|
refreshFragment(false)
|
||||||
}
|
}
|
||||||
}
|
|
||||||
|
|
||||||
noWifiWithWifiOnlyPreferenceSet -> {
|
noWifiWithWifiOnlyPreferenceSet -> {
|
||||||
hideRecyclerviewAndShowSwipeDownForLibraryErrorText()
|
hideRecyclerviewAndShowSwipeDownForLibraryErrorText()
|
||||||
}
|
}
|
||||||
|
|
||||||
onlineLibraryScreenState.value.value.onlineLibraryList.isNullOrEmpty() &&
|
onlineLibraryScreenState.value.value.onlineLibraryList.isNullOrEmpty() -> {
|
||||||
!zimManageViewModel.isOnlineLibraryDownloading -> {
|
startDownloadingLibrary(getOnlineLibraryRequest())
|
||||||
startDownloadingLibrary(true)
|
|
||||||
showProgressBarOfFetchingOnlineLibrary()
|
showProgressBarOfFetchingOnlineLibrary()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -525,15 +561,15 @@ class OnlineLibraryFragment : BaseFragment(), FragmentActivityExtensions {
|
|||||||
if (isNotConnected) {
|
if (isNotConnected) {
|
||||||
showNoInternetConnectionError()
|
showNoInternetConnectionError()
|
||||||
} else {
|
} else {
|
||||||
startDownloadingLibrary(isExplicitRefresh)
|
startDownloadingLibrary(getOnlineLibraryRequest())
|
||||||
if (isExplicitRefresh) {
|
if (isExplicitRefresh) {
|
||||||
showRecyclerviewAndHideSwipeDownForLibraryErrorText()
|
showRecyclerviewAndHideSwipeDownForLibraryErrorText()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private fun startDownloadingLibrary(isExplicitRefresh: Boolean = false) {
|
private fun startDownloadingLibrary(onlineLibraryRequest: OnlineLibraryRequest) {
|
||||||
zimManageViewModel.requestOnlineLibraryIfNeeded(isExplicitRefresh)
|
zimManageViewModel.updateOnlineLibraryFilters(onlineLibraryRequest)
|
||||||
}
|
}
|
||||||
|
|
||||||
private fun downloadFile() {
|
private fun downloadFile() {
|
||||||
|
@ -30,6 +30,7 @@ import androidx.compose.foundation.layout.padding
|
|||||||
import androidx.compose.foundation.layout.size
|
import androidx.compose.foundation.layout.size
|
||||||
import androidx.compose.foundation.layout.width
|
import androidx.compose.foundation.layout.width
|
||||||
import androidx.compose.foundation.lazy.LazyColumn
|
import androidx.compose.foundation.lazy.LazyColumn
|
||||||
|
import androidx.compose.foundation.lazy.LazyListScope
|
||||||
import androidx.compose.foundation.lazy.LazyListState
|
import androidx.compose.foundation.lazy.LazyListState
|
||||||
import androidx.compose.foundation.lazy.items
|
import androidx.compose.foundation.lazy.items
|
||||||
import androidx.compose.foundation.rememberScrollState
|
import androidx.compose.foundation.rememberScrollState
|
||||||
@ -42,6 +43,9 @@ import androidx.compose.material3.Scaffold
|
|||||||
import androidx.compose.material3.Text
|
import androidx.compose.material3.Text
|
||||||
import androidx.compose.material3.TopAppBarDefaults
|
import androidx.compose.material3.TopAppBarDefaults
|
||||||
import androidx.compose.runtime.Composable
|
import androidx.compose.runtime.Composable
|
||||||
|
import androidx.compose.runtime.LaunchedEffect
|
||||||
|
import androidx.compose.runtime.derivedStateOf
|
||||||
|
import androidx.compose.runtime.snapshotFlow
|
||||||
import androidx.compose.ui.Alignment
|
import androidx.compose.ui.Alignment
|
||||||
import androidx.compose.ui.Modifier
|
import androidx.compose.ui.Modifier
|
||||||
import androidx.compose.ui.input.nestedscroll.nestedScroll
|
import androidx.compose.ui.input.nestedscroll.nestedScroll
|
||||||
@ -51,7 +55,11 @@ import androidx.compose.ui.semantics.semantics
|
|||||||
import androidx.compose.ui.semantics.testTag
|
import androidx.compose.ui.semantics.testTag
|
||||||
import androidx.compose.ui.text.font.FontWeight
|
import androidx.compose.ui.text.font.FontWeight
|
||||||
import androidx.compose.ui.text.style.TextAlign
|
import androidx.compose.ui.text.style.TextAlign
|
||||||
|
import kotlinx.coroutines.flow.distinctUntilChanged
|
||||||
|
import kotlinx.coroutines.flow.filter
|
||||||
import org.kiwix.kiwixmobile.core.R.string
|
import org.kiwix.kiwixmobile.core.R.string
|
||||||
|
import org.kiwix.kiwixmobile.core.downloader.downloadManager.FIVE
|
||||||
|
import org.kiwix.kiwixmobile.core.downloader.downloadManager.ZERO
|
||||||
import org.kiwix.kiwixmobile.core.extensions.hideKeyboardOnLazyColumnScroll
|
import org.kiwix.kiwixmobile.core.extensions.hideKeyboardOnLazyColumnScroll
|
||||||
import org.kiwix.kiwixmobile.core.main.reader.rememberScrollBehavior
|
import org.kiwix.kiwixmobile.core.main.reader.rememberScrollBehavior
|
||||||
import org.kiwix.kiwixmobile.core.ui.components.ContentLoadingProgressBar
|
import org.kiwix.kiwixmobile.core.ui.components.ContentLoadingProgressBar
|
||||||
@ -194,6 +202,46 @@ private fun OnlineLibraryList(state: OnlineLibraryScreenState, lazyListState: La
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
showLoadMoreProgressBar(state.isLoadingMoreItem)
|
||||||
|
}
|
||||||
|
|
||||||
|
LaunchedEffect(state.isLoadingMoreItem) {
|
||||||
|
if (state.isLoadingMoreItem) {
|
||||||
|
// Scroll to the last item (i.e., the loading spinner)
|
||||||
|
val lastItemIndex = state.onlineLibraryList?.size ?: 0
|
||||||
|
lazyListState.animateScrollToItem(lastItemIndex)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
LaunchedEffect(lazyListState) {
|
||||||
|
snapshotFlow {
|
||||||
|
derivedStateOf {
|
||||||
|
val layoutInfo = lazyListState.layoutInfo
|
||||||
|
val totalItems = layoutInfo.totalItemsCount
|
||||||
|
val lastVisibleItemIndex = layoutInfo.visibleItemsInfo.lastOrNull()?.index ?: ZERO
|
||||||
|
(totalItems > ZERO && lastVisibleItemIndex >= (totalItems - FIVE)) to totalItems
|
||||||
|
}.value
|
||||||
|
}
|
||||||
|
.distinctUntilChanged()
|
||||||
|
.filter { it.first }
|
||||||
|
.collect { (_, totalItems) ->
|
||||||
|
state.onLoadMore(totalItems)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private fun LazyListScope.showLoadMoreProgressBar(isLoadingMoreItem: Boolean) {
|
||||||
|
if (isLoadingMoreItem) {
|
||||||
|
item {
|
||||||
|
Box(
|
||||||
|
modifier = Modifier
|
||||||
|
.fillMaxWidth()
|
||||||
|
.padding(SIXTEEN_DP),
|
||||||
|
contentAlignment = Alignment.Center
|
||||||
|
) {
|
||||||
|
ContentLoadingProgressBar()
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -99,5 +99,13 @@ data class OnlineLibraryScreenState(
|
|||||||
/**
|
/**
|
||||||
* Triggers when clear button clicked.
|
* Triggers when clear button clicked.
|
||||||
*/
|
*/
|
||||||
val clearSearchButtonClickListener: () -> Unit
|
val clearSearchButtonClickListener: () -> Unit,
|
||||||
|
/**
|
||||||
|
* Triggers when user at the end of the online content.
|
||||||
|
*/
|
||||||
|
val onLoadMore: (Int) -> Unit,
|
||||||
|
/**
|
||||||
|
* Manages the showing of progressBar at the end of book list when more items is loading.
|
||||||
|
*/
|
||||||
|
val isLoadingMoreItem: Boolean
|
||||||
)
|
)
|
||||||
|
@ -20,6 +20,7 @@ package org.kiwix.kiwixmobile.zimManager
|
|||||||
|
|
||||||
import kotlinx.coroutines.Dispatchers
|
import kotlinx.coroutines.Dispatchers
|
||||||
import kotlinx.coroutines.withContext
|
import kotlinx.coroutines.withContext
|
||||||
|
import org.kiwix.kiwixmobile.core.data.remote.KiwixService.Companion.ITEMS_PER_PAGE
|
||||||
import org.kiwix.kiwixmobile.core.data.remote.KiwixService.Companion.OPDS_LIBRARY_ENDPOINT
|
import org.kiwix.kiwixmobile.core.data.remote.KiwixService.Companion.OPDS_LIBRARY_ENDPOINT
|
||||||
import org.kiwix.kiwixmobile.core.downloader.downloadManager.ZERO
|
import org.kiwix.kiwixmobile.core.downloader.downloadManager.ZERO
|
||||||
import org.kiwix.kiwixmobile.core.entity.LibkiwixBook
|
import org.kiwix.kiwixmobile.core.entity.LibkiwixBook
|
||||||
@ -44,7 +45,8 @@ class OnlineLibraryManager @Inject constructor(
|
|||||||
urlHost: String
|
urlHost: String
|
||||||
): ArrayList<LibkiwixBook>? =
|
): ArrayList<LibkiwixBook>? =
|
||||||
runCatching {
|
runCatching {
|
||||||
content?.let { totalResult = extractTotalResults(it) }
|
if (content == null) return null
|
||||||
|
totalResult = extractTotalResults(content)
|
||||||
val onlineBooksList = arrayListOf<LibkiwixBook>()
|
val onlineBooksList = arrayListOf<LibkiwixBook>()
|
||||||
val tempLibrary = Library()
|
val tempLibrary = Library()
|
||||||
val tempManager = Manager(tempLibrary)
|
val tempManager = Manager(tempLibrary)
|
||||||
@ -83,8 +85,8 @@ class OnlineLibraryManager @Inject constructor(
|
|||||||
*/
|
*/
|
||||||
fun buildLibraryUrl(
|
fun buildLibraryUrl(
|
||||||
baseUrl: String,
|
baseUrl: String,
|
||||||
start: Int = 0,
|
start: Int,
|
||||||
count: Int = 50,
|
count: Int,
|
||||||
query: String? = null,
|
query: String? = null,
|
||||||
lang: String? = null,
|
lang: String? = null,
|
||||||
category: String? = null
|
category: String? = null
|
||||||
|
@ -39,6 +39,7 @@ import kotlinx.coroutines.flow.catch
|
|||||||
import kotlinx.coroutines.flow.combine
|
import kotlinx.coroutines.flow.combine
|
||||||
import kotlinx.coroutines.flow.debounce
|
import kotlinx.coroutines.flow.debounce
|
||||||
import kotlinx.coroutines.flow.distinctUntilChanged
|
import kotlinx.coroutines.flow.distinctUntilChanged
|
||||||
|
import kotlinx.coroutines.flow.drop
|
||||||
import kotlinx.coroutines.flow.filter
|
import kotlinx.coroutines.flow.filter
|
||||||
import kotlinx.coroutines.flow.filterNotNull
|
import kotlinx.coroutines.flow.filterNotNull
|
||||||
import kotlinx.coroutines.flow.first
|
import kotlinx.coroutines.flow.first
|
||||||
@ -53,6 +54,7 @@ import kotlinx.coroutines.flow.merge
|
|||||||
import kotlinx.coroutines.flow.onEach
|
import kotlinx.coroutines.flow.onEach
|
||||||
import kotlinx.coroutines.flow.retry
|
import kotlinx.coroutines.flow.retry
|
||||||
import kotlinx.coroutines.flow.take
|
import kotlinx.coroutines.flow.take
|
||||||
|
import kotlinx.coroutines.flow.update
|
||||||
import kotlinx.coroutines.launch
|
import kotlinx.coroutines.launch
|
||||||
import okhttp3.OkHttpClient
|
import okhttp3.OkHttpClient
|
||||||
import okhttp3.Request
|
import okhttp3.Request
|
||||||
@ -71,7 +73,6 @@ import org.kiwix.kiwixmobile.core.dao.LibkiwixBookOnDisk
|
|||||||
import org.kiwix.kiwixmobile.core.data.DataSource
|
import org.kiwix.kiwixmobile.core.data.DataSource
|
||||||
import org.kiwix.kiwixmobile.core.data.remote.KiwixService
|
import org.kiwix.kiwixmobile.core.data.remote.KiwixService
|
||||||
import org.kiwix.kiwixmobile.core.data.remote.KiwixService.Companion.ITEMS_PER_PAGE
|
import org.kiwix.kiwixmobile.core.data.remote.KiwixService.Companion.ITEMS_PER_PAGE
|
||||||
import org.kiwix.kiwixmobile.core.data.remote.KiwixService.Companion.OPDS_LIBRARY_ENDPOINT
|
|
||||||
import org.kiwix.kiwixmobile.core.data.remote.ProgressResponseBody
|
import org.kiwix.kiwixmobile.core.data.remote.ProgressResponseBody
|
||||||
import org.kiwix.kiwixmobile.core.data.remote.UserAgentInterceptor
|
import org.kiwix.kiwixmobile.core.data.remote.UserAgentInterceptor
|
||||||
import org.kiwix.kiwixmobile.core.di.modules.CALL_TIMEOUT
|
import org.kiwix.kiwixmobile.core.di.modules.CALL_TIMEOUT
|
||||||
@ -123,6 +124,7 @@ import org.kiwix.kiwixmobile.zimManager.libraryView.adapter.LibraryListItem.Book
|
|||||||
import org.kiwix.kiwixmobile.zimManager.libraryView.adapter.LibraryListItem.DividerItem
|
import org.kiwix.kiwixmobile.zimManager.libraryView.adapter.LibraryListItem.DividerItem
|
||||||
import org.kiwix.kiwixmobile.zimManager.libraryView.adapter.LibraryListItem.LibraryDownloadItem
|
import org.kiwix.kiwixmobile.zimManager.libraryView.adapter.LibraryListItem.LibraryDownloadItem
|
||||||
import org.kiwix.libkiwix.Book
|
import org.kiwix.libkiwix.Book
|
||||||
|
import retrofit2.Response
|
||||||
import java.util.Locale
|
import java.util.Locale
|
||||||
import java.util.concurrent.TimeUnit.SECONDS
|
import java.util.concurrent.TimeUnit.SECONDS
|
||||||
import javax.inject.Inject
|
import javax.inject.Inject
|
||||||
@ -148,7 +150,7 @@ class ZimManageViewModel @Inject constructor(
|
|||||||
private val dataSource: DataSource,
|
private val dataSource: DataSource,
|
||||||
private val connectivityManager: ConnectivityManager,
|
private val connectivityManager: ConnectivityManager,
|
||||||
private val sharedPreferenceUtil: SharedPreferenceUtil,
|
private val sharedPreferenceUtil: SharedPreferenceUtil,
|
||||||
private val onlineLibraryManager: OnlineLibraryManager
|
val onlineLibraryManager: OnlineLibraryManager
|
||||||
) : ViewModel() {
|
) : ViewModel() {
|
||||||
sealed class FileSelectActions {
|
sealed class FileSelectActions {
|
||||||
data class RequestNavigateTo(val bookOnDisk: BookOnDisk) : FileSelectActions()
|
data class RequestNavigateTo(val bookOnDisk: BookOnDisk) : FileSelectActions()
|
||||||
@ -161,6 +163,19 @@ class ZimManageViewModel @Inject constructor(
|
|||||||
object UserClickedDownloadBooksButton : FileSelectActions()
|
object UserClickedDownloadBooksButton : FileSelectActions()
|
||||||
}
|
}
|
||||||
|
|
||||||
|
data class OnlineLibraryRequest(
|
||||||
|
val query: String?,
|
||||||
|
val category: String?,
|
||||||
|
val lang: String?,
|
||||||
|
val isLoadMoreItem: Boolean,
|
||||||
|
val page: Int
|
||||||
|
)
|
||||||
|
|
||||||
|
data class OnlineLibraryResult(
|
||||||
|
val onlineLibraryRequest: OnlineLibraryRequest,
|
||||||
|
val books: List<LibkiwixBook>
|
||||||
|
)
|
||||||
|
|
||||||
private var isUnitTestCase: Boolean = false
|
private var isUnitTestCase: Boolean = false
|
||||||
val sideEffects: MutableSharedFlow<SideEffect<*>> = MutableSharedFlow()
|
val sideEffects: MutableSharedFlow<SideEffect<*>> = MutableSharedFlow()
|
||||||
private val _libraryItems = MutableStateFlow<List<LibraryListItem>>(emptyList())
|
private val _libraryItems = MutableStateFlow<List<LibraryListItem>>(emptyList())
|
||||||
@ -168,20 +183,36 @@ class ZimManageViewModel @Inject constructor(
|
|||||||
val fileSelectListStates: MutableLiveData<FileSelectListState> = MutableLiveData()
|
val fileSelectListStates: MutableLiveData<FileSelectListState> = MutableLiveData()
|
||||||
val deviceListScanningProgress = MutableLiveData<Int>()
|
val deviceListScanningProgress = MutableLiveData<Int>()
|
||||||
val libraryListIsRefreshing = MutableLiveData<Boolean>()
|
val libraryListIsRefreshing = MutableLiveData<Boolean>()
|
||||||
val onlineLibraryDownloading = MutableStateFlow(false)
|
|
||||||
|
/**
|
||||||
|
* Manages the showing of downloading online library progress,
|
||||||
|
* and showing the progressBar at the end of content when loading more items.
|
||||||
|
*
|
||||||
|
* A [Pair] containing:
|
||||||
|
* - [Boolean]: When initial content is downloading.
|
||||||
|
* - [Boolean]: When loading more item.
|
||||||
|
*/
|
||||||
|
val onlineLibraryDownloading = MutableStateFlow(false to false)
|
||||||
val shouldShowWifiOnlyDialog = MutableLiveData<Boolean>()
|
val shouldShowWifiOnlyDialog = MutableLiveData<Boolean>()
|
||||||
val networkStates = MutableLiveData<NetworkState>()
|
val networkStates = MutableLiveData<NetworkState>()
|
||||||
val networkLibrary = MutableSharedFlow<List<LibkiwixBook>>(replay = 0)
|
val networkLibrary = MutableStateFlow<List<LibkiwixBook>>(emptyList())
|
||||||
val requestFileSystemCheck = MutableSharedFlow<Unit>(replay = 0)
|
val requestFileSystemCheck = MutableSharedFlow<Unit>(replay = 0)
|
||||||
val fileSelectActions = MutableSharedFlow<FileSelectActions>()
|
val fileSelectActions = MutableSharedFlow<FileSelectActions>()
|
||||||
private val requestDownloadLibrary = MutableSharedFlow<Unit>(
|
private val requestDownloadLibrary = MutableSharedFlow<OnlineLibraryRequest>(
|
||||||
replay = 0,
|
replay = 0,
|
||||||
extraBufferCapacity = 1,
|
extraBufferCapacity = 1,
|
||||||
onBufferOverflow = BufferOverflow.DROP_OLDEST
|
onBufferOverflow = BufferOverflow.DROP_OLDEST
|
||||||
)
|
)
|
||||||
|
val onlineLibraryRequest: MutableStateFlow<OnlineLibraryRequest> =
|
||||||
@Volatile
|
MutableStateFlow<OnlineLibraryRequest>(
|
||||||
var isOnlineLibraryDownloading = false
|
OnlineLibraryRequest(
|
||||||
|
query = null,
|
||||||
|
category = null,
|
||||||
|
lang = null,
|
||||||
|
isLoadMoreItem = false,
|
||||||
|
page = 0
|
||||||
|
)
|
||||||
|
)
|
||||||
val requestFiltering = MutableStateFlow("")
|
val requestFiltering = MutableStateFlow("")
|
||||||
val onlineBooksSearchedQuery = MutableLiveData<String>()
|
val onlineBooksSearchedQuery = MutableLiveData<String>()
|
||||||
private val coroutineJobs: MutableList<Job> = mutableListOf()
|
private val coroutineJobs: MutableList<Job> = mutableListOf()
|
||||||
@ -194,14 +225,6 @@ class ZimManageViewModel @Inject constructor(
|
|||||||
context.registerReceiver(connectivityBroadcastReceiver)
|
context.registerReceiver(connectivityBroadcastReceiver)
|
||||||
}
|
}
|
||||||
|
|
||||||
fun requestOnlineLibraryIfNeeded(isExplicitRefresh: Boolean) {
|
|
||||||
if (isOnlineLibraryDownloading && !isExplicitRefresh) return
|
|
||||||
isOnlineLibraryDownloading = true
|
|
||||||
viewModelScope.launch {
|
|
||||||
requestDownloadLibrary.tryEmit(Unit)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
fun setIsUnitTestCase() {
|
fun setIsUnitTestCase() {
|
||||||
isUnitTestCase = true
|
isUnitTestCase = true
|
||||||
}
|
}
|
||||||
@ -210,9 +233,18 @@ class ZimManageViewModel @Inject constructor(
|
|||||||
this.alertDialogShower = alertDialogShower
|
this.alertDialogShower = alertDialogShower
|
||||||
}
|
}
|
||||||
|
|
||||||
private fun createKiwixServiceWithProgressListener(): KiwixService {
|
private fun createKiwixServiceWithProgressListener(
|
||||||
|
baseUrl: String,
|
||||||
|
start: Int = ZERO,
|
||||||
|
count: Int = ITEMS_PER_PAGE,
|
||||||
|
query: String? = null,
|
||||||
|
lang: String? = null,
|
||||||
|
category: String? = null,
|
||||||
|
shouldTrackProgress: Boolean
|
||||||
|
): KiwixService {
|
||||||
if (isUnitTestCase) return kiwixService
|
if (isUnitTestCase) return kiwixService
|
||||||
val contentLength = getContentLengthOfLibraryXmlFile()
|
val contentLength =
|
||||||
|
getContentLengthOfLibraryXmlFile(baseUrl, start, count, query, lang, category)
|
||||||
val customOkHttpClient =
|
val customOkHttpClient =
|
||||||
OkHttpClient().newBuilder()
|
OkHttpClient().newBuilder()
|
||||||
.followRedirects(true)
|
.followRedirects(true)
|
||||||
@ -228,22 +260,19 @@ class ZimManageViewModel @Inject constructor(
|
|||||||
.addNetworkInterceptor(UserAgentInterceptor(USER_AGENT))
|
.addNetworkInterceptor(UserAgentInterceptor(USER_AGENT))
|
||||||
.addNetworkInterceptor { chain ->
|
.addNetworkInterceptor { chain ->
|
||||||
val originalResponse = chain.proceed(chain.request())
|
val originalResponse = chain.proceed(chain.request())
|
||||||
originalResponse.body?.let { responseBody ->
|
val body = originalResponse.body
|
||||||
|
if (shouldTrackProgress && body != null) {
|
||||||
originalResponse.newBuilder()
|
originalResponse.newBuilder()
|
||||||
.body(
|
.body(ProgressResponseBody(body, appProgressListener, contentLength))
|
||||||
ProgressResponseBody(
|
|
||||||
responseBody,
|
|
||||||
appProgressListener,
|
|
||||||
contentLength
|
|
||||||
)
|
|
||||||
)
|
|
||||||
.build()
|
.build()
|
||||||
} ?: originalResponse
|
} else {
|
||||||
|
originalResponse
|
||||||
|
}
|
||||||
}
|
}
|
||||||
.build()
|
.build()
|
||||||
return KiwixService.ServiceCreator.newHackListService(
|
return KiwixService.ServiceCreator.newHackListService(
|
||||||
customOkHttpClient,
|
customOkHttpClient,
|
||||||
KIWIX_OPDS_LIBRARY_URL
|
baseUrl
|
||||||
)
|
)
|
||||||
.also {
|
.also {
|
||||||
kiwixService = it
|
kiwixService = it
|
||||||
@ -252,10 +281,19 @@ class ZimManageViewModel @Inject constructor(
|
|||||||
|
|
||||||
private var appProgressListener: AppProgressListenerProvider? = AppProgressListenerProvider(this)
|
private var appProgressListener: AppProgressListenerProvider? = AppProgressListenerProvider(this)
|
||||||
|
|
||||||
private fun getContentLengthOfLibraryXmlFile(): Long {
|
private fun getContentLengthOfLibraryXmlFile(
|
||||||
|
baseUrl: String,
|
||||||
|
start: Int = ZERO,
|
||||||
|
count: Int = ITEMS_PER_PAGE,
|
||||||
|
query: String? = null,
|
||||||
|
lang: String? = null,
|
||||||
|
category: String? = null
|
||||||
|
): Long {
|
||||||
|
val requestUrl =
|
||||||
|
onlineLibraryManager.buildLibraryUrl(baseUrl, start, count, query, lang, category)
|
||||||
val headRequest =
|
val headRequest =
|
||||||
Request.Builder()
|
Request.Builder()
|
||||||
.url("$KIWIX_OPDS_LIBRARY_URL$OPDS_LIBRARY_ENDPOINT?count=$ITEMS_PER_PAGE")
|
.url(requestUrl)
|
||||||
.head()
|
.head()
|
||||||
.header("Accept-Encoding", "identity")
|
.header("Accept-Encoding", "identity")
|
||||||
.build()
|
.build()
|
||||||
@ -298,6 +336,7 @@ class ZimManageViewModel @Inject constructor(
|
|||||||
add(updateLanguagesInDao(networkLibrary, languages))
|
add(updateLanguagesInDao(networkLibrary, languages))
|
||||||
add(updateNetworkStates())
|
add(updateNetworkStates())
|
||||||
add(requestsAndConnectivityChangesToLibraryRequests(networkLibrary))
|
add(requestsAndConnectivityChangesToLibraryRequests(networkLibrary))
|
||||||
|
add(onlineLibraryRequest())
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -311,6 +350,25 @@ class ZimManageViewModel @Inject constructor(
|
|||||||
super.onCleared()
|
super.onCleared()
|
||||||
}
|
}
|
||||||
|
|
||||||
|
fun updateOnlineLibraryFilters(newRequest: OnlineLibraryRequest) {
|
||||||
|
onlineLibraryRequest.update { current ->
|
||||||
|
current.copy(
|
||||||
|
query = newRequest.query.takeUnless { it.isNullOrEmpty() } ?: current.query,
|
||||||
|
category = newRequest.category.takeUnless { it.isNullOrEmpty() } ?: current.category,
|
||||||
|
lang = newRequest.lang.takeUnless { it.isNullOrEmpty() } ?: current.lang,
|
||||||
|
page = newRequest.page,
|
||||||
|
isLoadMoreItem = newRequest.isLoadMoreItem
|
||||||
|
)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private fun onlineLibraryRequest() = onlineLibraryRequest
|
||||||
|
.drop(1)
|
||||||
|
.onEach { request ->
|
||||||
|
requestDownloadLibrary.tryEmit(request)
|
||||||
|
}
|
||||||
|
.launchIn(viewModelScope)
|
||||||
|
|
||||||
private fun scanBooksFromStorage(dispatcher: CoroutineDispatcher = Dispatchers.IO) =
|
private fun scanBooksFromStorage(dispatcher: CoroutineDispatcher = Dispatchers.IO) =
|
||||||
checkFileSystemForBooksOnRequest(books())
|
checkFileSystemForBooksOnRequest(books())
|
||||||
.catch { it.printStackTrace() }
|
.catch { it.printStackTrace() }
|
||||||
@ -392,85 +450,133 @@ class ZimManageViewModel @Inject constructor(
|
|||||||
return None
|
return None
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private fun updateDownloadState(isInitial: Boolean) {
|
||||||
|
onlineLibraryDownloading.tryEmit(isInitial to !isInitial)
|
||||||
|
}
|
||||||
|
|
||||||
|
private fun resetDownloadState() {
|
||||||
|
onlineLibraryDownloading.tryEmit(false to false)
|
||||||
|
}
|
||||||
|
|
||||||
@OptIn(ExperimentalCoroutinesApi::class)
|
@OptIn(ExperimentalCoroutinesApi::class)
|
||||||
private fun requestsAndConnectivityChangesToLibraryRequests(
|
private fun requestsAndConnectivityChangesToLibraryRequests(
|
||||||
library: MutableSharedFlow<List<LibkiwixBook>>,
|
library: MutableStateFlow<List<LibkiwixBook>>,
|
||||||
dispatcher: CoroutineDispatcher = Dispatchers.IO
|
dispatcher: CoroutineDispatcher = Dispatchers.IO
|
||||||
) = requestDownloadLibrary.flatMapConcat {
|
) = requestDownloadLibrary.flatMapConcat { onlineLibraryRequest ->
|
||||||
connectivityBroadcastReceiver.networkStates
|
connectivityBroadcastReceiver.networkStates
|
||||||
.filter { networkState -> networkState == CONNECTED }
|
.filter { networkState -> networkState == CONNECTED }
|
||||||
.take(1)
|
.take(1)
|
||||||
.flatMapConcat {
|
.flatMapConcat {
|
||||||
shouldProceedWithDownload()
|
updateDownloadState(onlineLibraryRequest.isLoadMoreItem.not())
|
||||||
|
shouldProceedWithDownload(onlineLibraryRequest)
|
||||||
.flatMapConcat { kiwixService ->
|
.flatMapConcat { kiwixService ->
|
||||||
downloadLibraryFlow(kiwixService).also {
|
downloadLibraryFlow(kiwixService, onlineLibraryRequest)
|
||||||
onlineLibraryDownloading.tryEmit(true)
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
.filterNotNull()
|
.filterNotNull()
|
||||||
.catch {
|
.catch {
|
||||||
it.printStackTrace().also {
|
it.printStackTrace().also {
|
||||||
isOnlineLibraryDownloading = false
|
resetDownloadState()
|
||||||
onlineLibraryDownloading.tryEmit(false)
|
|
||||||
library.emit(emptyList())
|
library.emit(emptyList())
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
.onEach {
|
.onEach { result ->
|
||||||
library.emit(it).also {
|
networkLibrary.value = if (result.onlineLibraryRequest.isLoadMoreItem) {
|
||||||
// Setting this to true because once library downloaded we don't need to download again
|
networkLibrary.value + result.books
|
||||||
// until user wants to refresh the online library.
|
} else {
|
||||||
isOnlineLibraryDownloading = true
|
result.books
|
||||||
onlineLibraryDownloading.tryEmit(false)
|
|
||||||
}
|
}
|
||||||
|
resetDownloadState()
|
||||||
}
|
}
|
||||||
.flowOn(dispatcher)
|
.flowOn(dispatcher)
|
||||||
.launchIn(viewModelScope)
|
.launchIn(viewModelScope)
|
||||||
|
|
||||||
private fun shouldProceedWithDownload(): Flow<KiwixService> {
|
private fun shouldProceedWithDownload(onlineLibraryRequest: OnlineLibraryRequest): Flow<KiwixService> {
|
||||||
|
val baseUrl = KIWIX_OPDS_LIBRARY_URL
|
||||||
|
val start =
|
||||||
|
onlineLibraryManager.getStartOffset(onlineLibraryRequest.page.minus(ONE), ITEMS_PER_PAGE)
|
||||||
|
val shouldTrackProgress = !onlineLibraryRequest.isLoadMoreItem
|
||||||
return if (connectivityManager.isWifi()) {
|
return if (connectivityManager.isWifi()) {
|
||||||
flowOf(createKiwixServiceWithProgressListener())
|
flowOf(
|
||||||
|
createKiwixServiceWithProgressListener(
|
||||||
|
baseUrl,
|
||||||
|
start,
|
||||||
|
ITEMS_PER_PAGE,
|
||||||
|
onlineLibraryRequest.query,
|
||||||
|
onlineLibraryRequest.lang,
|
||||||
|
onlineLibraryRequest.category,
|
||||||
|
shouldTrackProgress
|
||||||
|
)
|
||||||
|
)
|
||||||
} else {
|
} else {
|
||||||
flow {
|
flow {
|
||||||
val wifiOnly = sharedPreferenceUtil.prefWifiOnlys.first()
|
val wifiOnly = sharedPreferenceUtil.prefWifiOnlys.first()
|
||||||
if (wifiOnly) {
|
if (wifiOnly) {
|
||||||
|
onlineLibraryDownloading.emit(false to false)
|
||||||
shouldShowWifiOnlyDialog.postValue(true)
|
shouldShowWifiOnlyDialog.postValue(true)
|
||||||
// Don't emit anything — just return
|
// Don't emit anything — just return
|
||||||
return@flow
|
return@flow
|
||||||
}
|
}
|
||||||
emit(createKiwixServiceWithProgressListener())
|
emit(
|
||||||
|
createKiwixServiceWithProgressListener(
|
||||||
|
baseUrl,
|
||||||
|
start,
|
||||||
|
ITEMS_PER_PAGE,
|
||||||
|
onlineLibraryRequest.query,
|
||||||
|
onlineLibraryRequest.lang,
|
||||||
|
onlineLibraryRequest.category,
|
||||||
|
shouldTrackProgress
|
||||||
|
)
|
||||||
|
)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private fun downloadLibraryFlow(
|
private fun downloadLibraryFlow(
|
||||||
kiwixService: KiwixService
|
kiwixService: KiwixService,
|
||||||
): Flow<List<LibkiwixBook>> = flow {
|
request: OnlineLibraryRequest
|
||||||
downloadProgress.postValue(context.getString(R.string.starting_downloading_remote_library))
|
): Flow<OnlineLibraryResult> = flow {
|
||||||
// TODO get the filter from online library and pass it here to get the online content based on filters.
|
updateDownloadProgressIfNeeded(
|
||||||
|
request,
|
||||||
|
R.string.starting_downloading_remote_library
|
||||||
|
)
|
||||||
|
val start =
|
||||||
|
onlineLibraryManager.getStartOffset(request.page.minus(ONE), ITEMS_PER_PAGE)
|
||||||
val buildUrl = onlineLibraryManager.buildLibraryUrl(
|
val buildUrl = onlineLibraryManager.buildLibraryUrl(
|
||||||
KIWIX_OPDS_LIBRARY_URL,
|
KIWIX_OPDS_LIBRARY_URL,
|
||||||
|
start,
|
||||||
|
ITEMS_PER_PAGE,
|
||||||
|
request.query,
|
||||||
|
request.lang,
|
||||||
|
request.category,
|
||||||
)
|
)
|
||||||
val response = kiwixService.getLibraryPage(buildUrl)
|
val response = kiwixService.getLibraryPage(buildUrl)
|
||||||
val resolvedUrl = response.raw().networkResponse?.request?.url
|
val urlHost = response.getResolvedBaseUrl()
|
||||||
?: response.raw().request.url
|
updateDownloadProgressIfNeeded(
|
||||||
val baseHostUrl = "${resolvedUrl.scheme}://${resolvedUrl.host}"
|
request,
|
||||||
downloadProgress.postValue(context.getString(R.string.parsing_remote_library))
|
R.string.parsing_remote_library
|
||||||
val libraryXml = response.body()
|
|
||||||
val onlineBooks = onlineLibraryManager.parseOPDSStreamAndGetBooks(libraryXml, baseHostUrl)
|
|
||||||
emit(
|
|
||||||
if (onlineBooks.isNullOrEmpty()) {
|
|
||||||
emptyList()
|
|
||||||
} else {
|
|
||||||
onlineBooks
|
|
||||||
}
|
|
||||||
)
|
)
|
||||||
|
val libraryXml = response.body()
|
||||||
|
val onlineBooks =
|
||||||
|
onlineLibraryManager.parseOPDSStreamAndGetBooks(libraryXml, urlHost).orEmpty()
|
||||||
|
emit(OnlineLibraryResult(request, onlineBooks))
|
||||||
}
|
}
|
||||||
.retry(5)
|
.retry(5)
|
||||||
.catch { e ->
|
.catch { e ->
|
||||||
e.printStackTrace()
|
e.printStackTrace()
|
||||||
emit(emptyList())
|
emit(OnlineLibraryResult(request, emptyList()))
|
||||||
|
}
|
||||||
|
|
||||||
|
private fun updateDownloadProgressIfNeeded(request: OnlineLibraryRequest, messageResId: Int) {
|
||||||
|
if (!request.isLoadMoreItem) {
|
||||||
|
downloadProgress.postValue(context.getString(messageResId))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private fun Response<String>.getResolvedBaseUrl(): String {
|
||||||
|
val url = raw().networkResponse?.request?.url ?: raw().request.url
|
||||||
|
return "${url.scheme}://${url.host}"
|
||||||
}
|
}
|
||||||
|
|
||||||
private fun updateNetworkStates() = connectivityBroadcastReceiver.networkStates
|
private fun updateNetworkStates() = connectivityBroadcastReceiver.networkStates
|
||||||
@ -482,7 +588,7 @@ class ZimManageViewModel @Inject constructor(
|
|||||||
private fun updateLibraryItems(
|
private fun updateLibraryItems(
|
||||||
localBooksFromLibkiwix: Flow<List<Book>>,
|
localBooksFromLibkiwix: Flow<List<Book>>,
|
||||||
downloads: Flow<List<DownloadModel>>,
|
downloads: Flow<List<DownloadModel>>,
|
||||||
library: MutableSharedFlow<List<LibkiwixBook>>,
|
library: MutableStateFlow<List<LibkiwixBook>>,
|
||||||
languages: Flow<List<Language>>,
|
languages: Flow<List<Language>>,
|
||||||
dispatcher: CoroutineDispatcher = Dispatchers.IO
|
dispatcher: CoroutineDispatcher = Dispatchers.IO
|
||||||
) = viewModelScope.launch(dispatcher) {
|
) = viewModelScope.launch(dispatcher) {
|
||||||
@ -527,7 +633,7 @@ class ZimManageViewModel @Inject constructor(
|
|||||||
}
|
}
|
||||||
|
|
||||||
private fun updateLanguagesInDao(
|
private fun updateLanguagesInDao(
|
||||||
library: MutableSharedFlow<List<LibkiwixBook>>,
|
library: MutableStateFlow<List<LibkiwixBook>>,
|
||||||
languages: Flow<List<Language>>,
|
languages: Flow<List<Language>>,
|
||||||
dispatcher: CoroutineDispatcher = Dispatchers.IO
|
dispatcher: CoroutineDispatcher = Dispatchers.IO
|
||||||
) =
|
) =
|
||||||
|
Loading…
x
Reference in New Issue
Block a user