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:
MohitMaliFtechiz 2025-07-04 00:47:20 +05:30
parent d9a10b30a6
commit 8b02f96c1c
5 changed files with 284 additions and 84 deletions

View File

@ -57,6 +57,7 @@ import org.kiwix.kiwixmobile.core.R.string
import org.kiwix.kiwixmobile.core.base.BaseActivity
import org.kiwix.kiwixmobile.core.base.BaseFragment
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.downloadManager.ZERO
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.page.SEARCH_ICON_TESTING_TAG
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.models.ActionMenuItem
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.DialogHost
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.main.KiwixMainActivity
import org.kiwix.kiwixmobile.storage.STORAGE_SELECT_STORAGE_TITLE_TEXTVIEW_SIZE
import org.kiwix.kiwixmobile.storage.StorageSelectDialog
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.adapter.LibraryListItem
import javax.inject.Inject
@ -138,11 +142,37 @@ class OnlineLibraryFragment : BaseFragment(), FragmentActivityExtensions {
isSearchActive = false,
searchText = "",
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() {
onlineLibraryScreenState.value.update {
copy(searchText = "")
@ -250,9 +280,17 @@ class OnlineLibraryFragment : BaseFragment(), FragmentActivityExtensions {
}
observeViewModelData()
showPreviouslySearchedTextInSearchView()
startDownloadingLibrary()
startDownloadingLibrary(getOnlineLibraryRequest())
}
private fun getOnlineLibraryRequest(): OnlineLibraryRequest = OnlineLibraryRequest(
null,
null,
null,
false,
1
)
private fun observeViewModelData() {
zimManageViewModel.apply {
// Observe when library items changes.
@ -265,12 +303,13 @@ class OnlineLibraryFragment : BaseFragment(), FragmentActivityExtensions {
}
// Observe when online library downloading.
onlineLibraryDownloading
.onEach {
if (it) {
.onEach { (initialLibraryDownloading, loadingMoreItem) ->
if (initialLibraryDownloading) {
showProgressBarOfFetchingOnlineLibrary()
} else {
hideProgressBarOfFetchingOnlineLibrary()
}
onlineLibraryScreenState.value.update { copy(isLoadingMoreItem = loadingMoreItem) }
}.launchIn(viewLifecycleOwner.lifecycleScope)
// Observe when library list refreshing e.g. applying filters.
libraryListIsRefreshing.observe(
@ -369,7 +408,7 @@ class OnlineLibraryFragment : BaseFragment(), FragmentActivityExtensions {
// User allowed downloading over mobile data.
// Since the download flow now triggers only when appropriate,
// we start the library download explicitly after updating the preference.
startDownloadingLibrary(true)
startDownloadingLibrary(getOnlineLibraryRequest())
},
{
context.toast(
@ -456,18 +495,15 @@ class OnlineLibraryFragment : BaseFragment(), FragmentActivityExtensions {
NetworkState.CONNECTED -> {
when {
NetworkUtils.isWiFi(requireContext()) -> {
if (!zimManageViewModel.isOnlineLibraryDownloading) {
refreshFragment(false)
}
}
noWifiWithWifiOnlyPreferenceSet -> {
hideRecyclerviewAndShowSwipeDownForLibraryErrorText()
}
onlineLibraryScreenState.value.value.onlineLibraryList.isNullOrEmpty() &&
!zimManageViewModel.isOnlineLibraryDownloading -> {
startDownloadingLibrary(true)
onlineLibraryScreenState.value.value.onlineLibraryList.isNullOrEmpty() -> {
startDownloadingLibrary(getOnlineLibraryRequest())
showProgressBarOfFetchingOnlineLibrary()
}
}
@ -525,15 +561,15 @@ class OnlineLibraryFragment : BaseFragment(), FragmentActivityExtensions {
if (isNotConnected) {
showNoInternetConnectionError()
} else {
startDownloadingLibrary(isExplicitRefresh)
startDownloadingLibrary(getOnlineLibraryRequest())
if (isExplicitRefresh) {
showRecyclerviewAndHideSwipeDownForLibraryErrorText()
}
}
}
private fun startDownloadingLibrary(isExplicitRefresh: Boolean = false) {
zimManageViewModel.requestOnlineLibraryIfNeeded(isExplicitRefresh)
private fun startDownloadingLibrary(onlineLibraryRequest: OnlineLibraryRequest) {
zimManageViewModel.updateOnlineLibraryFilters(onlineLibraryRequest)
}
private fun downloadFile() {

View File

@ -30,6 +30,7 @@ import androidx.compose.foundation.layout.padding
import androidx.compose.foundation.layout.size
import androidx.compose.foundation.layout.width
import androidx.compose.foundation.lazy.LazyColumn
import androidx.compose.foundation.lazy.LazyListScope
import androidx.compose.foundation.lazy.LazyListState
import androidx.compose.foundation.lazy.items
import androidx.compose.foundation.rememberScrollState
@ -42,6 +43,9 @@ import androidx.compose.material3.Scaffold
import androidx.compose.material3.Text
import androidx.compose.material3.TopAppBarDefaults
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.Modifier
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.text.font.FontWeight
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.downloader.downloadManager.FIVE
import org.kiwix.kiwixmobile.core.downloader.downloadManager.ZERO
import org.kiwix.kiwixmobile.core.extensions.hideKeyboardOnLazyColumnScroll
import org.kiwix.kiwixmobile.core.main.reader.rememberScrollBehavior
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()
}
}
}
}

View File

@ -99,5 +99,13 @@ data class OnlineLibraryScreenState(
/**
* 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
)

View File

@ -20,6 +20,7 @@ package org.kiwix.kiwixmobile.zimManager
import kotlinx.coroutines.Dispatchers
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.downloader.downloadManager.ZERO
import org.kiwix.kiwixmobile.core.entity.LibkiwixBook
@ -44,7 +45,8 @@ class OnlineLibraryManager @Inject constructor(
urlHost: String
): ArrayList<LibkiwixBook>? =
runCatching {
content?.let { totalResult = extractTotalResults(it) }
if (content == null) return null
totalResult = extractTotalResults(content)
val onlineBooksList = arrayListOf<LibkiwixBook>()
val tempLibrary = Library()
val tempManager = Manager(tempLibrary)
@ -83,8 +85,8 @@ class OnlineLibraryManager @Inject constructor(
*/
fun buildLibraryUrl(
baseUrl: String,
start: Int = 0,
count: Int = 50,
start: Int,
count: Int,
query: String? = null,
lang: String? = null,
category: String? = null

View File

@ -39,6 +39,7 @@ import kotlinx.coroutines.flow.catch
import kotlinx.coroutines.flow.combine
import kotlinx.coroutines.flow.debounce
import kotlinx.coroutines.flow.distinctUntilChanged
import kotlinx.coroutines.flow.drop
import kotlinx.coroutines.flow.filter
import kotlinx.coroutines.flow.filterNotNull
import kotlinx.coroutines.flow.first
@ -53,6 +54,7 @@ import kotlinx.coroutines.flow.merge
import kotlinx.coroutines.flow.onEach
import kotlinx.coroutines.flow.retry
import kotlinx.coroutines.flow.take
import kotlinx.coroutines.flow.update
import kotlinx.coroutines.launch
import okhttp3.OkHttpClient
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.remote.KiwixService
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.UserAgentInterceptor
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.LibraryDownloadItem
import org.kiwix.libkiwix.Book
import retrofit2.Response
import java.util.Locale
import java.util.concurrent.TimeUnit.SECONDS
import javax.inject.Inject
@ -148,7 +150,7 @@ class ZimManageViewModel @Inject constructor(
private val dataSource: DataSource,
private val connectivityManager: ConnectivityManager,
private val sharedPreferenceUtil: SharedPreferenceUtil,
private val onlineLibraryManager: OnlineLibraryManager
val onlineLibraryManager: OnlineLibraryManager
) : ViewModel() {
sealed class FileSelectActions {
data class RequestNavigateTo(val bookOnDisk: BookOnDisk) : FileSelectActions()
@ -161,6 +163,19 @@ class ZimManageViewModel @Inject constructor(
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
val sideEffects: MutableSharedFlow<SideEffect<*>> = MutableSharedFlow()
private val _libraryItems = MutableStateFlow<List<LibraryListItem>>(emptyList())
@ -168,20 +183,36 @@ class ZimManageViewModel @Inject constructor(
val fileSelectListStates: MutableLiveData<FileSelectListState> = MutableLiveData()
val deviceListScanningProgress = MutableLiveData<Int>()
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 networkStates = MutableLiveData<NetworkState>()
val networkLibrary = MutableSharedFlow<List<LibkiwixBook>>(replay = 0)
val networkLibrary = MutableStateFlow<List<LibkiwixBook>>(emptyList())
val requestFileSystemCheck = MutableSharedFlow<Unit>(replay = 0)
val fileSelectActions = MutableSharedFlow<FileSelectActions>()
private val requestDownloadLibrary = MutableSharedFlow<Unit>(
private val requestDownloadLibrary = MutableSharedFlow<OnlineLibraryRequest>(
replay = 0,
extraBufferCapacity = 1,
onBufferOverflow = BufferOverflow.DROP_OLDEST
)
@Volatile
var isOnlineLibraryDownloading = false
val onlineLibraryRequest: MutableStateFlow<OnlineLibraryRequest> =
MutableStateFlow<OnlineLibraryRequest>(
OnlineLibraryRequest(
query = null,
category = null,
lang = null,
isLoadMoreItem = false,
page = 0
)
)
val requestFiltering = MutableStateFlow("")
val onlineBooksSearchedQuery = MutableLiveData<String>()
private val coroutineJobs: MutableList<Job> = mutableListOf()
@ -194,14 +225,6 @@ class ZimManageViewModel @Inject constructor(
context.registerReceiver(connectivityBroadcastReceiver)
}
fun requestOnlineLibraryIfNeeded(isExplicitRefresh: Boolean) {
if (isOnlineLibraryDownloading && !isExplicitRefresh) return
isOnlineLibraryDownloading = true
viewModelScope.launch {
requestDownloadLibrary.tryEmit(Unit)
}
}
fun setIsUnitTestCase() {
isUnitTestCase = true
}
@ -210,9 +233,18 @@ class ZimManageViewModel @Inject constructor(
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
val contentLength = getContentLengthOfLibraryXmlFile()
val contentLength =
getContentLengthOfLibraryXmlFile(baseUrl, start, count, query, lang, category)
val customOkHttpClient =
OkHttpClient().newBuilder()
.followRedirects(true)
@ -228,22 +260,19 @@ class ZimManageViewModel @Inject constructor(
.addNetworkInterceptor(UserAgentInterceptor(USER_AGENT))
.addNetworkInterceptor { chain ->
val originalResponse = chain.proceed(chain.request())
originalResponse.body?.let { responseBody ->
val body = originalResponse.body
if (shouldTrackProgress && body != null) {
originalResponse.newBuilder()
.body(
ProgressResponseBody(
responseBody,
appProgressListener,
contentLength
)
)
.body(ProgressResponseBody(body, appProgressListener, contentLength))
.build()
} ?: originalResponse
} else {
originalResponse
}
}
.build()
return KiwixService.ServiceCreator.newHackListService(
customOkHttpClient,
KIWIX_OPDS_LIBRARY_URL
baseUrl
)
.also {
kiwixService = it
@ -252,10 +281,19 @@ class ZimManageViewModel @Inject constructor(
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 =
Request.Builder()
.url("$KIWIX_OPDS_LIBRARY_URL$OPDS_LIBRARY_ENDPOINT?count=$ITEMS_PER_PAGE")
.url(requestUrl)
.head()
.header("Accept-Encoding", "identity")
.build()
@ -298,6 +336,7 @@ class ZimManageViewModel @Inject constructor(
add(updateLanguagesInDao(networkLibrary, languages))
add(updateNetworkStates())
add(requestsAndConnectivityChangesToLibraryRequests(networkLibrary))
add(onlineLibraryRequest())
}
}
@ -311,6 +350,25 @@ class ZimManageViewModel @Inject constructor(
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) =
checkFileSystemForBooksOnRequest(books())
.catch { it.printStackTrace() }
@ -392,85 +450,133 @@ class ZimManageViewModel @Inject constructor(
return None
}
private fun updateDownloadState(isInitial: Boolean) {
onlineLibraryDownloading.tryEmit(isInitial to !isInitial)
}
private fun resetDownloadState() {
onlineLibraryDownloading.tryEmit(false to false)
}
@OptIn(ExperimentalCoroutinesApi::class)
private fun requestsAndConnectivityChangesToLibraryRequests(
library: MutableSharedFlow<List<LibkiwixBook>>,
library: MutableStateFlow<List<LibkiwixBook>>,
dispatcher: CoroutineDispatcher = Dispatchers.IO
) = requestDownloadLibrary.flatMapConcat {
) = requestDownloadLibrary.flatMapConcat { onlineLibraryRequest ->
connectivityBroadcastReceiver.networkStates
.filter { networkState -> networkState == CONNECTED }
.take(1)
.flatMapConcat {
shouldProceedWithDownload()
updateDownloadState(onlineLibraryRequest.isLoadMoreItem.not())
shouldProceedWithDownload(onlineLibraryRequest)
.flatMapConcat { kiwixService ->
downloadLibraryFlow(kiwixService).also {
onlineLibraryDownloading.tryEmit(true)
}
downloadLibraryFlow(kiwixService, onlineLibraryRequest)
}
}
}
.filterNotNull()
.catch {
it.printStackTrace().also {
isOnlineLibraryDownloading = false
onlineLibraryDownloading.tryEmit(false)
resetDownloadState()
library.emit(emptyList())
}
}
.onEach {
library.emit(it).also {
// Setting this to true because once library downloaded we don't need to download again
// until user wants to refresh the online library.
isOnlineLibraryDownloading = true
onlineLibraryDownloading.tryEmit(false)
.onEach { result ->
networkLibrary.value = if (result.onlineLibraryRequest.isLoadMoreItem) {
networkLibrary.value + result.books
} else {
result.books
}
resetDownloadState()
}
.flowOn(dispatcher)
.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()) {
flowOf(createKiwixServiceWithProgressListener())
flowOf(
createKiwixServiceWithProgressListener(
baseUrl,
start,
ITEMS_PER_PAGE,
onlineLibraryRequest.query,
onlineLibraryRequest.lang,
onlineLibraryRequest.category,
shouldTrackProgress
)
)
} else {
flow {
val wifiOnly = sharedPreferenceUtil.prefWifiOnlys.first()
if (wifiOnly) {
onlineLibraryDownloading.emit(false to false)
shouldShowWifiOnlyDialog.postValue(true)
// Don't emit anything — just return
return@flow
}
emit(createKiwixServiceWithProgressListener())
emit(
createKiwixServiceWithProgressListener(
baseUrl,
start,
ITEMS_PER_PAGE,
onlineLibraryRequest.query,
onlineLibraryRequest.lang,
onlineLibraryRequest.category,
shouldTrackProgress
)
)
}
}
}
private fun downloadLibraryFlow(
kiwixService: KiwixService
): Flow<List<LibkiwixBook>> = flow {
downloadProgress.postValue(context.getString(R.string.starting_downloading_remote_library))
// TODO get the filter from online library and pass it here to get the online content based on filters.
kiwixService: KiwixService,
request: OnlineLibraryRequest
): Flow<OnlineLibraryResult> = flow {
updateDownloadProgressIfNeeded(
request,
R.string.starting_downloading_remote_library
)
val start =
onlineLibraryManager.getStartOffset(request.page.minus(ONE), ITEMS_PER_PAGE)
val buildUrl = onlineLibraryManager.buildLibraryUrl(
KIWIX_OPDS_LIBRARY_URL,
start,
ITEMS_PER_PAGE,
request.query,
request.lang,
request.category,
)
val response = kiwixService.getLibraryPage(buildUrl)
val resolvedUrl = response.raw().networkResponse?.request?.url
?: response.raw().request.url
val baseHostUrl = "${resolvedUrl.scheme}://${resolvedUrl.host}"
downloadProgress.postValue(context.getString(R.string.parsing_remote_library))
val libraryXml = response.body()
val onlineBooks = onlineLibraryManager.parseOPDSStreamAndGetBooks(libraryXml, baseHostUrl)
emit(
if (onlineBooks.isNullOrEmpty()) {
emptyList()
} else {
onlineBooks
}
val urlHost = response.getResolvedBaseUrl()
updateDownloadProgressIfNeeded(
request,
R.string.parsing_remote_library
)
val libraryXml = response.body()
val onlineBooks =
onlineLibraryManager.parseOPDSStreamAndGetBooks(libraryXml, urlHost).orEmpty()
emit(OnlineLibraryResult(request, onlineBooks))
}
.retry(5)
.catch { e ->
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
@ -482,7 +588,7 @@ class ZimManageViewModel @Inject constructor(
private fun updateLibraryItems(
localBooksFromLibkiwix: Flow<List<Book>>,
downloads: Flow<List<DownloadModel>>,
library: MutableSharedFlow<List<LibkiwixBook>>,
library: MutableStateFlow<List<LibkiwixBook>>,
languages: Flow<List<Language>>,
dispatcher: CoroutineDispatcher = Dispatchers.IO
) = viewModelScope.launch(dispatcher) {
@ -527,7 +633,7 @@ class ZimManageViewModel @Inject constructor(
}
private fun updateLanguagesInDao(
library: MutableSharedFlow<List<LibkiwixBook>>,
library: MutableStateFlow<List<LibkiwixBook>>,
languages: Flow<List<Language>>,
dispatcher: CoroutineDispatcher = Dispatchers.IO
) =