Added UI to show the progress on fetching online content.

* Refactored the viewModel code to show the downloading updates, e.g. content is fetching, downloading the online content, etc.
This commit is contained in:
MohitMaliFtechiz 2024-09-16 18:48:34 +05:30
parent 3ab0dcd8b9
commit 222e86840a
8 changed files with 237 additions and 20 deletions

View File

@ -195,6 +195,13 @@ class OnlineLibraryFragment : BaseFragment(), FragmentActivityExtensions {
showInternetAccessViaMobileNetworkDialog()
}
}
zimManageViewModel.downloadProgress.observe(viewLifecycleOwner) { progress ->
fragmentDestinationDownloadBinding?.onlineLibraryProgressBar?.progress = progress
}
zimManageViewModel.downloadStatus.observe(viewLifecycleOwner) { status ->
fragmentDestinationDownloadBinding?.libraryErrorText?.text = status
}
setupMenu()
// hides keyboard when scrolled
@ -268,6 +275,7 @@ class OnlineLibraryFragment : BaseFragment(), FragmentActivityExtensions {
libraryErrorText.visibility = View.GONE
libraryList.visibility = View.VISIBLE
}
showProgressBarOfFetchingOnlineLibrary()
}
private fun hideRecyclerviewAndShowSwipeDownForLibraryErrorText() {
@ -277,6 +285,17 @@ class OnlineLibraryFragment : BaseFragment(), FragmentActivityExtensions {
)
libraryErrorText.visibility = View.VISIBLE
libraryList.visibility = View.GONE
onlineLibraryProgressBar.visibility = View.GONE
}
}
private fun showProgressBarOfFetchingOnlineLibrary() {
fragmentDestinationDownloadBinding?.apply {
onlineLibraryProgressBar.visibility = View.VISIBLE
libraryErrorText.apply {
visibility = View.VISIBLE
setText(string.reaching_remote_library)
}
}
}
@ -345,6 +364,7 @@ class OnlineLibraryFragment : BaseFragment(), FragmentActivityExtensions {
if (it != null) {
libraryAdapter.items = it
}
fragmentDestinationDownloadBinding?.onlineLibraryProgressBar?.visibility = View.GONE
if (it?.isEmpty() == true) {
fragmentDestinationDownloadBinding?.libraryErrorText?.setText(
if (isNotConnected) string.no_network_connection

View File

@ -0,0 +1,31 @@
/*
* Kiwix Android
* Copyright (c) 2024 Kiwix <android.kiwix.org>
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*
*/
package org.kiwix.kiwixmobile.zimManager
import org.kiwix.kiwixmobile.core.data.remote.OnlineLibraryProgressListener
class AppProgressListenerProvider(
private val zimManageViewModel: ZimManageViewModel
) : OnlineLibraryProgressListener {
@Suppress("MagicNumber")
override fun onProgress(bytesRead: Long, contentLength: Long, done: Boolean) {
val progress = if (contentLength == -1L) 0 else (bytesRead * 100 / contentLength).toInt()
zimManageViewModel.downloadProgress.postValue(progress)
}
}

View File

@ -33,6 +33,11 @@ import io.reactivex.plugins.RxJavaPlugins
import io.reactivex.processors.BehaviorProcessor
import io.reactivex.processors.PublishProcessor
import io.reactivex.schedulers.Schedulers
import okhttp3.OkHttpClient
import okhttp3.logging.HttpLoggingInterceptor
import okhttp3.logging.HttpLoggingInterceptor.Level.BASIC
import okhttp3.logging.HttpLoggingInterceptor.Level.NONE
import org.kiwix.kiwixmobile.BuildConfig.DEBUG
import org.kiwix.kiwixmobile.core.R
import org.kiwix.kiwixmobile.core.StorageObserver
import org.kiwix.kiwixmobile.core.base.SideEffect
@ -42,6 +47,13 @@ import org.kiwix.kiwixmobile.core.dao.NewBookDao
import org.kiwix.kiwixmobile.core.dao.NewLanguagesDao
import org.kiwix.kiwixmobile.core.data.DataSource
import org.kiwix.kiwixmobile.core.data.remote.KiwixService
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
import org.kiwix.kiwixmobile.core.di.modules.CONNECTION_TIMEOUT
import org.kiwix.kiwixmobile.core.di.modules.KIWIX_DOWNLOAD_URL
import org.kiwix.kiwixmobile.core.di.modules.READ_TIMEOUT
import org.kiwix.kiwixmobile.core.di.modules.USER_AGENT
import org.kiwix.kiwixmobile.core.downloader.model.DownloadModel
import org.kiwix.kiwixmobile.core.entity.LibraryNetworkEntity
import org.kiwix.kiwixmobile.core.entity.LibraryNetworkEntity.Book
@ -81,6 +93,7 @@ import java.io.IOException
import java.util.LinkedList
import java.util.Locale
import java.util.concurrent.TimeUnit.MILLISECONDS
import java.util.concurrent.TimeUnit.SECONDS
import javax.inject.Inject
const val DEFAULT_PROGRESS = 0
@ -92,7 +105,7 @@ class ZimManageViewModel @Inject constructor(
private val bookDao: NewBookDao,
private val languageDao: NewLanguagesDao,
private val storageObserver: StorageObserver,
private val kiwixService: KiwixService,
private var kiwixService: KiwixService,
private val context: Application,
private val connectivityBroadcastReceiver: ConnectivityBroadcastReceiver,
private val bookUtils: BookUtils,
@ -127,12 +140,44 @@ class ZimManageViewModel @Inject constructor(
val requestFiltering = BehaviorProcessor.createDefault("")
private var compositeDisposable: CompositeDisposable? = CompositeDisposable()
val downloadProgress = MutableLiveData<Int>()
val downloadStatus = MutableLiveData<String>()
init {
// add listener to retrofit to get updates of downloading online library
kiwixService = createKiwixServiceWithProgressListener()
compositeDisposable?.addAll(*disposables())
context.registerReceiver(connectivityBroadcastReceiver)
}
private fun createKiwixServiceWithProgressListener(): KiwixService {
val customOkHttpClient = OkHttpClient().newBuilder()
.followRedirects(true)
.followSslRedirects(true)
.connectTimeout(CONNECTION_TIMEOUT, SECONDS)
.readTimeout(READ_TIMEOUT, SECONDS)
.callTimeout(CALL_TIMEOUT, SECONDS)
.addNetworkInterceptor(
HttpLoggingInterceptor().apply {
level = if (DEBUG) BASIC else NONE
}
)
.addNetworkInterceptor(UserAgentInterceptor(USER_AGENT))
.addNetworkInterceptor { chain ->
val originalResponse = chain.proceed(chain.request())
originalResponse.newBuilder()
.body(
ProgressResponseBody(
originalResponse.body!!,
AppProgressListenerProvider(this)
)
)
.build()
}
.build()
return KiwixService.ServiceCreator.newHackListService(customOkHttpClient, KIWIX_DOWNLOAD_URL)
}
@VisibleForTesting
fun onClearedExposed() {
onCleared()
@ -232,7 +277,7 @@ class ZimManageViewModel @Inject constructor(
}
private fun requestsAndConnectivtyChangesToLibraryRequests(
library: PublishProcessor<LibraryNetworkEntity>
library: PublishProcessor<LibraryNetworkEntity>,
) =
Flowable.combineLatest(
requestDownloadLibrary,
@ -254,21 +299,37 @@ class ZimManageViewModel @Inject constructor(
.map { }
}
}
.doOnEach {
downloadStatus.postValue("Reaching remote library")
}
.switchMap {
Flowable.fromCallable {
downloadStatus.postValue("Starting download of the online library")
}
}
.subscribeOn(Schedulers.io())
.observeOn(Schedulers.io())
.subscribe(
{
compositeDisposable?.add(
kiwixService.library
.retry(5)
.subscribe(library::onNext) {
it.printStackTrace()
library.onNext(LibraryNetworkEntity().apply { book = LinkedList() })
}
)
},
Throwable::printStackTrace
)
.flatMap {
kiwixService.library
.toFlowable()
.retry(5)
.doOnSubscribe {
downloadStatus.postValue("Downloading library 0%")
}
.map { response ->
downloadStatus.postValue("Downloading library... parsing response")
response
}
.doFinally {
downloadStatus.postValue("Remote library downloaded, parsing data")
}
.onErrorReturn {
it.printStackTrace()
downloadStatus.postValue("Failed to download the library")
LibraryNetworkEntity().apply { book = LinkedList() }
}
}
.subscribe(library::onNext, Throwable::printStackTrace)
private fun updateNetworkStates() =
connectivityBroadcastReceiver.networkStates.subscribe(

View File

@ -66,4 +66,13 @@
app:layout_constraintVertical_bias="0.45"
tools:ignore="RequiredSize" />
<ProgressBar
android:id="@+id/onlineLibraryProgressBar"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintEnd_toEndOf="parent"
android:visibility="gone"
app:layout_constraintBottom_toTopOf="@+id/libraryErrorText" />
</androidx.constraintlayout.widget.ConstraintLayout>

View File

@ -0,0 +1,23 @@
/*
* Kiwix Android
* Copyright (c) 2024 Kiwix <android.kiwix.org>
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*
*/
package org.kiwix.kiwixmobile.core.data.remote
interface OnlineLibraryProgressListener {
fun onProgress(bytesRead: Long, contentLength: Long, done: Boolean)
}

View File

@ -0,0 +1,72 @@
/*
* Kiwix Android
* Copyright (c) 2024 Kiwix <android.kiwix.org>
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*
*/
package org.kiwix.kiwixmobile.core.data.remote
import okhttp3.MediaType
import okhttp3.ResponseBody
import okio.Buffer
import okio.BufferedSource
import okio.ForwardingSource
import okio.Source
import okio.buffer
import org.kiwix.kiwixmobile.core.utils.files.Log
class ProgressResponseBody(
private val responseBody: ResponseBody,
private val progressListener: OnlineLibraryProgressListener
) : ResponseBody() {
private var bufferedSource: BufferedSource? = null
override fun contentType(): MediaType? = responseBody.contentType()
override fun contentLength(): Long = responseBody.contentLength()
@Suppress("UnsafeCallOnNullableType")
override fun source(): BufferedSource {
if (bufferedSource == null) {
bufferedSource = source(responseBody.source()).buffer()
}
return bufferedSource!!
}
private fun source(source: Source): Source {
return object : ForwardingSource(source) {
var totalBytesRead = 0L
override fun read(sink: Buffer, byteCount: Long): Long {
val bytesRead = super.read(sink, byteCount)
totalBytesRead += if (bytesRead != -1L) bytesRead else 0
val isDone = bytesRead == -1L
progressListener.onProgress(
totalBytesRead,
responseBody.contentLength(),
isDone
).also {
Log.e(
"PROGRESS",
"onProgress: ${contentLength()} and byteRead = $totalBytesRead\n" +
" sink ${bytesRead == -1L} \n byteRead = $bytesRead " +
"\n bufferedSource = ${bufferedSource?.isOpen}"
)
}
return bytesRead
}
}
}
}

View File

@ -30,11 +30,11 @@ import org.kiwix.kiwixmobile.core.data.remote.UserAgentInterceptor
import java.util.concurrent.TimeUnit.SECONDS
import javax.inject.Singleton
private const val CONNECTION_TIMEOUT = 10L
private const val READ_TIMEOUT = 60L
private const val CALL_TIMEOUT = 60L
private const val USER_AGENT = "kiwix-android-version:${BuildConfig.VERSION_CODE}"
private const val KIWIX_DOWNLOAD_URL = "https://mirror.download.kiwix.org/"
const val CONNECTION_TIMEOUT = 10L
const val READ_TIMEOUT = 60L
const val CALL_TIMEOUT = 60L
const val USER_AGENT = "kiwix-android-version:${BuildConfig.VERSION_CODE}"
const val KIWIX_DOWNLOAD_URL = "https://mirror.download.kiwix.org/"
@Module
class NetworkModule {

View File

@ -348,6 +348,7 @@
<string name="system_unable_to_grant_permission_message">System unable to grant permission!</string>
<string name="allow">Allow</string>
<string name="swipe_down_for_library">Swipe Down for Library</string>
<string name="reaching_remote_library">Reaching remote library</string>
<string-array name="pref_dark_modes_entries">
<item>@string/on</item>
<item>@string/off</item>