mirror of
https://github.com/kiwix/kiwix-android.git
synced 2025-09-08 06:42:21 -04:00
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:
parent
3ab0dcd8b9
commit
222e86840a
@ -195,6 +195,13 @@ class OnlineLibraryFragment : BaseFragment(), FragmentActivityExtensions {
|
|||||||
showInternetAccessViaMobileNetworkDialog()
|
showInternetAccessViaMobileNetworkDialog()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
zimManageViewModel.downloadProgress.observe(viewLifecycleOwner) { progress ->
|
||||||
|
fragmentDestinationDownloadBinding?.onlineLibraryProgressBar?.progress = progress
|
||||||
|
}
|
||||||
|
|
||||||
|
zimManageViewModel.downloadStatus.observe(viewLifecycleOwner) { status ->
|
||||||
|
fragmentDestinationDownloadBinding?.libraryErrorText?.text = status
|
||||||
|
}
|
||||||
setupMenu()
|
setupMenu()
|
||||||
|
|
||||||
// hides keyboard when scrolled
|
// hides keyboard when scrolled
|
||||||
@ -268,6 +275,7 @@ class OnlineLibraryFragment : BaseFragment(), FragmentActivityExtensions {
|
|||||||
libraryErrorText.visibility = View.GONE
|
libraryErrorText.visibility = View.GONE
|
||||||
libraryList.visibility = View.VISIBLE
|
libraryList.visibility = View.VISIBLE
|
||||||
}
|
}
|
||||||
|
showProgressBarOfFetchingOnlineLibrary()
|
||||||
}
|
}
|
||||||
|
|
||||||
private fun hideRecyclerviewAndShowSwipeDownForLibraryErrorText() {
|
private fun hideRecyclerviewAndShowSwipeDownForLibraryErrorText() {
|
||||||
@ -277,6 +285,17 @@ class OnlineLibraryFragment : BaseFragment(), FragmentActivityExtensions {
|
|||||||
)
|
)
|
||||||
libraryErrorText.visibility = View.VISIBLE
|
libraryErrorText.visibility = View.VISIBLE
|
||||||
libraryList.visibility = View.GONE
|
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) {
|
if (it != null) {
|
||||||
libraryAdapter.items = it
|
libraryAdapter.items = it
|
||||||
}
|
}
|
||||||
|
fragmentDestinationDownloadBinding?.onlineLibraryProgressBar?.visibility = View.GONE
|
||||||
if (it?.isEmpty() == true) {
|
if (it?.isEmpty() == true) {
|
||||||
fragmentDestinationDownloadBinding?.libraryErrorText?.setText(
|
fragmentDestinationDownloadBinding?.libraryErrorText?.setText(
|
||||||
if (isNotConnected) string.no_network_connection
|
if (isNotConnected) string.no_network_connection
|
||||||
|
@ -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)
|
||||||
|
}
|
||||||
|
}
|
@ -33,6 +33,11 @@ import io.reactivex.plugins.RxJavaPlugins
|
|||||||
import io.reactivex.processors.BehaviorProcessor
|
import io.reactivex.processors.BehaviorProcessor
|
||||||
import io.reactivex.processors.PublishProcessor
|
import io.reactivex.processors.PublishProcessor
|
||||||
import io.reactivex.schedulers.Schedulers
|
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.R
|
||||||
import org.kiwix.kiwixmobile.core.StorageObserver
|
import org.kiwix.kiwixmobile.core.StorageObserver
|
||||||
import org.kiwix.kiwixmobile.core.base.SideEffect
|
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.dao.NewLanguagesDao
|
||||||
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.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.downloader.model.DownloadModel
|
||||||
import org.kiwix.kiwixmobile.core.entity.LibraryNetworkEntity
|
import org.kiwix.kiwixmobile.core.entity.LibraryNetworkEntity
|
||||||
import org.kiwix.kiwixmobile.core.entity.LibraryNetworkEntity.Book
|
import org.kiwix.kiwixmobile.core.entity.LibraryNetworkEntity.Book
|
||||||
@ -81,6 +93,7 @@ import java.io.IOException
|
|||||||
import java.util.LinkedList
|
import java.util.LinkedList
|
||||||
import java.util.Locale
|
import java.util.Locale
|
||||||
import java.util.concurrent.TimeUnit.MILLISECONDS
|
import java.util.concurrent.TimeUnit.MILLISECONDS
|
||||||
|
import java.util.concurrent.TimeUnit.SECONDS
|
||||||
import javax.inject.Inject
|
import javax.inject.Inject
|
||||||
|
|
||||||
const val DEFAULT_PROGRESS = 0
|
const val DEFAULT_PROGRESS = 0
|
||||||
@ -92,7 +105,7 @@ class ZimManageViewModel @Inject constructor(
|
|||||||
private val bookDao: NewBookDao,
|
private val bookDao: NewBookDao,
|
||||||
private val languageDao: NewLanguagesDao,
|
private val languageDao: NewLanguagesDao,
|
||||||
private val storageObserver: StorageObserver,
|
private val storageObserver: StorageObserver,
|
||||||
private val kiwixService: KiwixService,
|
private var kiwixService: KiwixService,
|
||||||
private val context: Application,
|
private val context: Application,
|
||||||
private val connectivityBroadcastReceiver: ConnectivityBroadcastReceiver,
|
private val connectivityBroadcastReceiver: ConnectivityBroadcastReceiver,
|
||||||
private val bookUtils: BookUtils,
|
private val bookUtils: BookUtils,
|
||||||
@ -127,12 +140,44 @@ class ZimManageViewModel @Inject constructor(
|
|||||||
val requestFiltering = BehaviorProcessor.createDefault("")
|
val requestFiltering = BehaviorProcessor.createDefault("")
|
||||||
|
|
||||||
private var compositeDisposable: CompositeDisposable? = CompositeDisposable()
|
private var compositeDisposable: CompositeDisposable? = CompositeDisposable()
|
||||||
|
val downloadProgress = MutableLiveData<Int>()
|
||||||
|
val downloadStatus = MutableLiveData<String>()
|
||||||
|
|
||||||
init {
|
init {
|
||||||
|
// add listener to retrofit to get updates of downloading online library
|
||||||
|
kiwixService = createKiwixServiceWithProgressListener()
|
||||||
compositeDisposable?.addAll(*disposables())
|
compositeDisposable?.addAll(*disposables())
|
||||||
context.registerReceiver(connectivityBroadcastReceiver)
|
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
|
@VisibleForTesting
|
||||||
fun onClearedExposed() {
|
fun onClearedExposed() {
|
||||||
onCleared()
|
onCleared()
|
||||||
@ -232,7 +277,7 @@ class ZimManageViewModel @Inject constructor(
|
|||||||
}
|
}
|
||||||
|
|
||||||
private fun requestsAndConnectivtyChangesToLibraryRequests(
|
private fun requestsAndConnectivtyChangesToLibraryRequests(
|
||||||
library: PublishProcessor<LibraryNetworkEntity>
|
library: PublishProcessor<LibraryNetworkEntity>,
|
||||||
) =
|
) =
|
||||||
Flowable.combineLatest(
|
Flowable.combineLatest(
|
||||||
requestDownloadLibrary,
|
requestDownloadLibrary,
|
||||||
@ -254,21 +299,37 @@ class ZimManageViewModel @Inject constructor(
|
|||||||
.map { }
|
.map { }
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
.doOnEach {
|
||||||
|
downloadStatus.postValue("Reaching remote library")
|
||||||
|
}
|
||||||
|
.switchMap {
|
||||||
|
Flowable.fromCallable {
|
||||||
|
downloadStatus.postValue("Starting download of the online library")
|
||||||
|
}
|
||||||
|
}
|
||||||
.subscribeOn(Schedulers.io())
|
.subscribeOn(Schedulers.io())
|
||||||
.observeOn(Schedulers.io())
|
.observeOn(Schedulers.io())
|
||||||
.subscribe(
|
.flatMap {
|
||||||
{
|
kiwixService.library
|
||||||
compositeDisposable?.add(
|
.toFlowable()
|
||||||
kiwixService.library
|
.retry(5)
|
||||||
.retry(5)
|
.doOnSubscribe {
|
||||||
.subscribe(library::onNext) {
|
downloadStatus.postValue("Downloading library 0%")
|
||||||
it.printStackTrace()
|
}
|
||||||
library.onNext(LibraryNetworkEntity().apply { book = LinkedList() })
|
.map { response ->
|
||||||
}
|
downloadStatus.postValue("Downloading library... parsing response")
|
||||||
)
|
response
|
||||||
},
|
}
|
||||||
Throwable::printStackTrace
|
.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() =
|
private fun updateNetworkStates() =
|
||||||
connectivityBroadcastReceiver.networkStates.subscribe(
|
connectivityBroadcastReceiver.networkStates.subscribe(
|
||||||
|
@ -66,4 +66,13 @@
|
|||||||
app:layout_constraintVertical_bias="0.45"
|
app:layout_constraintVertical_bias="0.45"
|
||||||
tools:ignore="RequiredSize" />
|
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>
|
</androidx.constraintlayout.widget.ConstraintLayout>
|
||||||
|
@ -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)
|
||||||
|
}
|
@ -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
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
@ -30,11 +30,11 @@ import org.kiwix.kiwixmobile.core.data.remote.UserAgentInterceptor
|
|||||||
import java.util.concurrent.TimeUnit.SECONDS
|
import java.util.concurrent.TimeUnit.SECONDS
|
||||||
import javax.inject.Singleton
|
import javax.inject.Singleton
|
||||||
|
|
||||||
private const val CONNECTION_TIMEOUT = 10L
|
const val CONNECTION_TIMEOUT = 10L
|
||||||
private const val READ_TIMEOUT = 60L
|
const val READ_TIMEOUT = 60L
|
||||||
private const val CALL_TIMEOUT = 60L
|
const val CALL_TIMEOUT = 60L
|
||||||
private const val USER_AGENT = "kiwix-android-version:${BuildConfig.VERSION_CODE}"
|
const val USER_AGENT = "kiwix-android-version:${BuildConfig.VERSION_CODE}"
|
||||||
private const val KIWIX_DOWNLOAD_URL = "https://mirror.download.kiwix.org/"
|
const val KIWIX_DOWNLOAD_URL = "https://mirror.download.kiwix.org/"
|
||||||
|
|
||||||
@Module
|
@Module
|
||||||
class NetworkModule {
|
class NetworkModule {
|
||||||
|
@ -348,6 +348,7 @@
|
|||||||
<string name="system_unable_to_grant_permission_message">System unable to grant permission!</string>
|
<string name="system_unable_to_grant_permission_message">System unable to grant permission!</string>
|
||||||
<string name="allow">Allow</string>
|
<string name="allow">Allow</string>
|
||||||
<string name="swipe_down_for_library">Swipe Down for Library</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">
|
<string-array name="pref_dark_modes_entries">
|
||||||
<item>@string/on</item>
|
<item>@string/on</item>
|
||||||
<item>@string/off</item>
|
<item>@string/off</item>
|
||||||
|
Loading…
x
Reference in New Issue
Block a user