Improved the design of showing the downloading progress of online library.

* Also, disabled the refresh layout so avoid unnecessary calls when a request is already in progress.
This commit is contained in:
MohitMaliFtechiz 2024-09-17 15:00:02 +05:30
parent 90f711b244
commit c5d0d8aeea
6 changed files with 128 additions and 53 deletions

View File

@ -41,6 +41,7 @@ import androidx.core.app.ActivityCompat
import androidx.core.content.ContextCompat import androidx.core.content.ContextCompat
import androidx.core.view.MenuHost import androidx.core.view.MenuHost
import androidx.core.view.MenuProvider import androidx.core.view.MenuProvider
import androidx.core.view.isVisible
import androidx.fragment.app.FragmentActivity import androidx.fragment.app.FragmentActivity
import androidx.lifecycle.Lifecycle import androidx.lifecycle.Lifecycle
import androidx.lifecycle.Observer import androidx.lifecycle.Observer
@ -86,6 +87,7 @@ import org.kiwix.kiwixmobile.core.utils.dialog.KiwixDialog
import org.kiwix.kiwixmobile.core.utils.dialog.KiwixDialog.YesNoDialog.WifiOnly import org.kiwix.kiwixmobile.core.utils.dialog.KiwixDialog.YesNoDialog.WifiOnly
import org.kiwix.kiwixmobile.databinding.FragmentDestinationDownloadBinding import org.kiwix.kiwixmobile.databinding.FragmentDestinationDownloadBinding
import org.kiwix.kiwixmobile.zimManager.NetworkState import org.kiwix.kiwixmobile.zimManager.NetworkState
import org.kiwix.kiwixmobile.zimManager.OnlineLibraryStatus
import org.kiwix.kiwixmobile.zimManager.ZimManageViewModel import org.kiwix.kiwixmobile.zimManager.ZimManageViewModel
import org.kiwix.kiwixmobile.zimManager.libraryView.AvailableSpaceCalculator import org.kiwix.kiwixmobile.zimManager.libraryView.AvailableSpaceCalculator
import org.kiwix.kiwixmobile.zimManager.libraryView.adapter.LibraryAdapter import org.kiwix.kiwixmobile.zimManager.libraryView.adapter.LibraryAdapter
@ -192,16 +194,11 @@ class OnlineLibraryFragment : BaseFragment(), FragmentActivityExtensions {
viewLifecycleOwner viewLifecycleOwner
) { ) {
if (it && !NetworkUtils.isWiFi(requireContext())) { if (it && !NetworkUtils.isWiFi(requireContext())) {
hideProgressBarOfFetchingOnlineLibrary()
showInternetAccessViaMobileNetworkDialog() showInternetAccessViaMobileNetworkDialog()
} }
} }
zimManageViewModel.downloadProgress.observe(viewLifecycleOwner) { progress -> zimManageViewModel.downloadProgress.observe(viewLifecycleOwner, ::onLibraryStatusChanged)
fragmentDestinationDownloadBinding?.onlineLibraryProgressBar?.progress = progress
}
zimManageViewModel.downloadStatus.observe(viewLifecycleOwner) { status ->
fragmentDestinationDownloadBinding?.libraryErrorText?.text = status
}
setupMenu() setupMenu()
// hides keyboard when scrolled // hides keyboard when scrolled
@ -254,13 +251,11 @@ class OnlineLibraryFragment : BaseFragment(), FragmentActivityExtensions {
dialogShower.show( dialogShower.show(
WifiOnly, WifiOnly,
{ {
onRefreshStateChange(true)
showRecyclerviewAndHideSwipeDownForLibraryErrorText() showRecyclerviewAndHideSwipeDownForLibraryErrorText()
sharedPreferenceUtil.putPrefWifiOnly(false) sharedPreferenceUtil.putPrefWifiOnly(false)
zimManageViewModel.shouldShowWifiOnlyDialog.value = false zimManageViewModel.shouldShowWifiOnlyDialog.value = false
}, },
{ {
onRefreshStateChange(false)
context.toast( context.toast(
resources.getString(string.denied_internet_permission_message), resources.getString(string.denied_internet_permission_message),
Toast.LENGTH_SHORT Toast.LENGTH_SHORT
@ -285,17 +280,32 @@ class OnlineLibraryFragment : BaseFragment(), FragmentActivityExtensions {
) )
libraryErrorText.visibility = View.VISIBLE libraryErrorText.visibility = View.VISIBLE
libraryList.visibility = View.GONE libraryList.visibility = View.GONE
onlineLibraryProgressBar.visibility = View.GONE
} }
hideProgressBarOfFetchingOnlineLibrary()
} }
private fun showProgressBarOfFetchingOnlineLibrary() { private fun showProgressBarOfFetchingOnlineLibrary() {
onRefreshStateChange(false)
fragmentDestinationDownloadBinding?.apply { fragmentDestinationDownloadBinding?.apply {
onlineLibraryProgressBar.visibility = View.VISIBLE librarySwipeRefresh.isEnabled = false
libraryErrorText.apply { onlineLibraryProgressLayout.visibility = View.VISIBLE
visibility = View.VISIBLE onlineLibraryProgressStatusText.setText(string.reaching_remote_library)
setText(string.reaching_remote_library) }
} }
private fun hideProgressBarOfFetchingOnlineLibrary() {
onRefreshStateChange(false)
fragmentDestinationDownloadBinding?.apply {
librarySwipeRefresh.isEnabled = true
onlineLibraryProgressLayout.visibility = View.GONE
onlineLibraryProgressStatusText.setText(string.reaching_remote_library)
}
}
private fun onLibraryStatusChanged(onlineLibraryStatus: OnlineLibraryStatus) {
fragmentDestinationDownloadBinding?.apply {
onlineLibraryProgressBar.progress = onlineLibraryStatus.progress
onlineLibraryProgressStatusText.text = onlineLibraryStatus.status
} }
} }
@ -312,17 +322,20 @@ class OnlineLibraryFragment : BaseFragment(), FragmentActivityExtensions {
} }
private fun onRefreshStateChange(isRefreshing: Boolean?) { private fun onRefreshStateChange(isRefreshing: Boolean?) {
fragmentDestinationDownloadBinding?.librarySwipeRefresh?.isRefreshing = isRefreshing == true var refreshing = isRefreshing == true
// do not show the refreshing when the online library is downloading
if (fragmentDestinationDownloadBinding?.onlineLibraryProgressLayout?.isVisible == true) {
refreshing = false
}
fragmentDestinationDownloadBinding?.librarySwipeRefresh?.isRefreshing = refreshing
} }
private fun onNetworkStateChange(networkState: NetworkState?) { private fun onNetworkStateChange(networkState: NetworkState?) {
when (networkState) { when (networkState) {
NetworkState.CONNECTED -> { NetworkState.CONNECTED -> {
if (NetworkUtils.isWiFi(requireContext())) { if (NetworkUtils.isWiFi(requireContext())) {
onRefreshStateChange(true)
refreshFragment() refreshFragment()
} else if (noWifiWithWifiOnlyPreferenceSet) { } else if (noWifiWithWifiOnlyPreferenceSet) {
onRefreshStateChange(false)
hideRecyclerviewAndShowSwipeDownForLibraryErrorText() hideRecyclerviewAndShowSwipeDownForLibraryErrorText()
} }
} }
@ -344,7 +357,7 @@ class OnlineLibraryFragment : BaseFragment(), FragmentActivityExtensions {
) )
fragmentDestinationDownloadBinding?.libraryErrorText?.visibility = View.VISIBLE fragmentDestinationDownloadBinding?.libraryErrorText?.visibility = View.VISIBLE
} }
fragmentDestinationDownloadBinding?.librarySwipeRefresh?.isRefreshing = false hideProgressBarOfFetchingOnlineLibrary()
} }
private fun noInternetSnackbar() { private fun noInternetSnackbar() {
@ -364,7 +377,7 @@ class OnlineLibraryFragment : BaseFragment(), FragmentActivityExtensions {
if (it != null) { if (it != null) {
libraryAdapter.items = it libraryAdapter.items = it
} }
fragmentDestinationDownloadBinding?.onlineLibraryProgressBar?.visibility = View.GONE hideProgressBarOfFetchingOnlineLibrary()
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

View File

@ -26,6 +26,11 @@ class AppProgressListenerProvider(
@Suppress("MagicNumber") @Suppress("MagicNumber")
override fun onProgress(bytesRead: Long, contentLength: Long, done: Boolean) { override fun onProgress(bytesRead: Long, contentLength: Long, done: Boolean) {
val progress = if (contentLength == -1L) 0 else (bytesRead * 100 / contentLength).toInt() val progress = if (contentLength == -1L) 0 else (bytesRead * 100 / contentLength).toInt()
zimManageViewModel.downloadProgress.postValue(progress) zimManageViewModel.downloadProgress.postValue(
OnlineLibraryStatus(
progress,
"Downloading online content"
)
)
} }
} }

View File

@ -0,0 +1,21 @@
/*
* 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
data class OnlineLibraryStatus(val progress: Int, val status: String)

View File

@ -140,8 +140,7 @@ 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 downloadProgress = MutableLiveData<OnlineLibraryStatus>()
val downloadStatus = MutableLiveData<String>()
init { init {
// add listener to retrofit to get updates of downloading online library // add listener to retrofit to get updates of downloading online library
@ -299,14 +298,6 @@ 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())
.flatMap { .flatMap {
@ -314,18 +305,32 @@ class ZimManageViewModel @Inject constructor(
.toFlowable() .toFlowable()
.retry(5) .retry(5)
.doOnSubscribe { .doOnSubscribe {
downloadStatus.postValue("Downloading library 0%") downloadProgress.postValue(OnlineLibraryStatus(0, "Downloading library 0%"))
} }
.map { response -> .map { response ->
downloadStatus.postValue("Downloading library... parsing response") downloadProgress.postValue(
OnlineLibraryStatus(
0,
"Downloading library... parsing response"
)
)
response response
} }
.doFinally { .doFinally {
downloadStatus.postValue("Remote library downloaded, parsing data") downloadProgress.postValue(
OnlineLibraryStatus(
0,
"Remote library downloaded, parsing data"
)
)
} }
.onErrorReturn { .onErrorReturn {
it.printStackTrace() it.printStackTrace()
downloadStatus.postValue("Failed to download the library") downloadProgress.postValue(
OnlineLibraryStatus(
0, "Failed to download the library"
)
)
LibraryNetworkEntity().apply { book = LinkedList() } LibraryNetworkEntity().apply { book = LinkedList() }
} }
} }

View File

@ -47,8 +47,8 @@
android:id="@+id/libraryList" android:id="@+id/libraryList"
android:layout_width="match_parent" android:layout_width="match_parent"
android:layout_height="match_parent" android:layout_height="match_parent"
android:scrollbars="vertical"
android:contentDescription="@string/library" android:contentDescription="@string/library"
android:scrollbars="vertical"
app:layout_behavior="@string/appbar_scrolling_view_behavior" app:layout_behavior="@string/appbar_scrolling_view_behavior"
tools:listitem="@layout/item_download" /> tools:listitem="@layout/item_download" />
@ -66,13 +66,44 @@
app:layout_constraintVertical_bias="0.45" app:layout_constraintVertical_bias="0.45"
tools:ignore="RequiredSize" /> tools:ignore="RequiredSize" />
<ProgressBar <androidx.cardview.widget.CardView
android:id="@+id/onlineLibraryProgressBar" android:id="@+id/progressCardView"
android:layout_width="wrap_content" android:layout_width="wrap_content"
android:layout_height="wrap_content" android:layout_height="wrap_content"
app:layout_constraintStart_toStartOf="parent" android:layout_margin="16dp"
android:visibility="visible"
app:cardCornerRadius="4dp"
app:cardElevation="4dp"
app:layout_constraintBottom_toBottomOf="parent"
app:layout_constraintEnd_toEndOf="parent" app:layout_constraintEnd_toEndOf="parent"
android:visibility="gone" app:layout_constraintStart_toStartOf="parent"
app:layout_constraintBottom_toTopOf="@+id/libraryErrorText" /> app:layout_constraintTop_toTopOf="parent">
<LinearLayout
android:id="@+id/onlineLibraryProgressLayout"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:gravity="center"
android:orientation="vertical"
android:padding="@dimen/activity_horizontal_margin"
android:visibility="gone">
<ProgressBar
android:id="@+id/onlineLibraryProgressBar"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_marginTop="@dimen/activity_horizontal_margin" />
<TextView
android:id="@+id/onlineLibraryProgressStatusText"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_marginTop="8dp"
android:layout_marginBottom="@dimen/activity_horizontal_margin"
android:gravity="center"
android:text="@string/reaching_remote_library"
android:textColor="@color/mine_shaft_gray900"
android:textSize="12sp" />
</LinearLayout>
</androidx.cardview.widget.CardView>
</androidx.constraintlayout.widget.ConstraintLayout> </androidx.constraintlayout.widget.ConstraintLayout>

View File

@ -32,18 +32,17 @@ class ProgressResponseBody(
private val progressListener: OnlineLibraryProgressListener private val progressListener: OnlineLibraryProgressListener
) : ResponseBody() { ) : ResponseBody() {
private var bufferedSource: BufferedSource? = null private lateinit var bufferedSource: BufferedSource
override fun contentType(): MediaType? = responseBody.contentType() override fun contentType(): MediaType? = responseBody.contentType()
override fun contentLength(): Long = responseBody.contentLength() override fun contentLength(): Long = responseBody.contentLength()
@Suppress("UnsafeCallOnNullableType")
override fun source(): BufferedSource { override fun source(): BufferedSource {
if (bufferedSource == null) { if (!::bufferedSource.isInitialized) {
bufferedSource = source(responseBody.source()).buffer() bufferedSource = source(responseBody.source()).buffer()
} }
return bufferedSource!! return bufferedSource
} }
private fun source(source: Source): Source { private fun source(source: Source): Source {
@ -57,14 +56,15 @@ class ProgressResponseBody(
totalBytesRead, totalBytesRead,
responseBody.contentLength(), responseBody.contentLength(),
isDone isDone
).also { )
Log.e( .also {
"PROGRESS", Log.e(
"onProgress: ${contentLength()} and byteRead = $totalBytesRead\n" + "PROGRESS",
" sink ${bytesRead == -1L} \n byteRead = $bytesRead " + "onProgress: ${contentLength()} and byteRead = $totalBytesRead\n" +
"\n bufferedSource = ${bufferedSource?.isOpen}" " sink ${bytesRead == -1L} \n byteRead = $bytesRead " +
) "\n bufferedSource = ${bufferedSource.isOpen}"
} )
}
return bytesRead return bytesRead
} }
} }