mirror of
https://github.com/kiwix/kiwix-android.git
synced 2025-09-16 10:56:50 -04:00
Introduced pause/resume functionality.
* Added pause/resume button beside the stop button to perform pause/resume action on the downloading zim file. * Handling pause/resume via fetch library.
This commit is contained in:
parent
524c469d78
commit
0a2cff92b5
@ -113,7 +113,8 @@ class OnlineLibraryFragment : BaseFragment(), FragmentActivityExtensions {
|
||||
private val libraryAdapter: LibraryAdapter by lazy {
|
||||
LibraryAdapter(
|
||||
LibraryDelegate.BookDelegate(bookUtils, ::onBookItemClick, availableSpaceCalculator),
|
||||
LibraryDelegate.DownloadDelegate {
|
||||
LibraryDelegate.DownloadDelegate(
|
||||
{
|
||||
if (it.currentDownloadState == Status.FAILED) {
|
||||
if (isNotConnected) {
|
||||
noInternetSnackbar()
|
||||
@ -127,6 +128,15 @@ class OnlineLibraryFragment : BaseFragment(), FragmentActivityExtensions {
|
||||
)
|
||||
}
|
||||
},
|
||||
{
|
||||
context?.let { context ->
|
||||
downloader.pauseResumeDownload(
|
||||
it.downloadId,
|
||||
it.downloadState.toReadableState(context) == "Paused"
|
||||
)
|
||||
}
|
||||
}
|
||||
),
|
||||
LibraryDelegate.DividerDelegate
|
||||
)
|
||||
}
|
||||
|
@ -51,14 +51,18 @@ sealed class LibraryDelegate<I : LibraryListItem, out VH : LibraryViewHolder<I>>
|
||||
)
|
||||
}
|
||||
|
||||
class DownloadDelegate(private val clickAction: (LibraryDownloadItem) -> Unit) :
|
||||
class DownloadDelegate(
|
||||
private val clickAction: (LibraryDownloadItem) -> Unit,
|
||||
private val pauseResumeClickAction: (LibraryDownloadItem) -> Unit
|
||||
) :
|
||||
LibraryDelegate<LibraryDownloadItem, DownloadViewHolder>() {
|
||||
override val itemClass = LibraryDownloadItem::class.java
|
||||
|
||||
override fun createViewHolder(parent: ViewGroup) =
|
||||
DownloadViewHolder(
|
||||
parent.viewBinding(ItemDownloadBinding::inflate, false),
|
||||
clickAction
|
||||
clickAction,
|
||||
pauseResumeClickAction
|
||||
)
|
||||
}
|
||||
|
||||
|
@ -24,6 +24,7 @@ import org.kiwix.kiwixmobile.R
|
||||
import org.kiwix.kiwixmobile.core.base.adapter.BaseViewHolder
|
||||
import org.kiwix.kiwixmobile.core.downloader.model.Base64String
|
||||
import org.kiwix.kiwixmobile.core.extensions.setBitmap
|
||||
import org.kiwix.kiwixmobile.core.extensions.setImageDrawableCompat
|
||||
import org.kiwix.kiwixmobile.core.extensions.setTextAndVisibility
|
||||
import org.kiwix.kiwixmobile.core.extensions.toast
|
||||
import org.kiwix.kiwixmobile.core.utils.BookUtils
|
||||
@ -90,7 +91,8 @@ sealed class LibraryViewHolder<in T : LibraryListItem>(containerView: View) :
|
||||
|
||||
class DownloadViewHolder(
|
||||
private val itemDownloadBinding: ItemDownloadBinding,
|
||||
private val clickAction: (LibraryDownloadItem) -> Unit
|
||||
private val clickAction: (LibraryDownloadItem) -> Unit,
|
||||
private val pauseResumeClickAction: (LibraryDownloadItem) -> Unit
|
||||
) :
|
||||
LibraryViewHolder<LibraryDownloadItem>(itemDownloadBinding.root) {
|
||||
|
||||
@ -100,8 +102,18 @@ sealed class LibraryViewHolder<in T : LibraryListItem>(containerView: View) :
|
||||
itemDownloadBinding.libraryDownloadDescription.text = item.description
|
||||
itemDownloadBinding.downloadProgress.progress = item.progress
|
||||
itemDownloadBinding.stop.setOnClickListener { clickAction.invoke(item) }
|
||||
itemDownloadBinding.pauseResume.setOnClickListener {
|
||||
pauseResumeClickAction.invoke(item)
|
||||
}
|
||||
itemDownloadBinding.downloadState.text =
|
||||
item.downloadState.toReadableState(containerView.context)
|
||||
item.downloadState.toReadableState(containerView.context).also {
|
||||
val pauseResumeIconId = if (it == "Paused") {
|
||||
R.drawable.ic_play_24dp
|
||||
} else {
|
||||
R.drawable.ic_pause_24dp
|
||||
}
|
||||
itemDownloadBinding.pauseResume.setImageDrawableCompat(pauseResumeIconId)
|
||||
}
|
||||
if (item.currentDownloadState == Status.FAILED) {
|
||||
clickAction.invoke(item)
|
||||
}
|
||||
|
9
app/src/main/res/drawable-night/ic_pause_24dp.xml
Normal file
9
app/src/main/res/drawable-night/ic_pause_24dp.xml
Normal file
@ -0,0 +1,9 @@
|
||||
<vector xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
android:width="24dp"
|
||||
android:height="24dp"
|
||||
android:viewportWidth="24"
|
||||
android:viewportHeight="24">
|
||||
<path
|
||||
android:fillColor="@android:color/white"
|
||||
android:pathData="M6,19h4L10,5L6,5v14zM14,5v14h4L18,5h-4z" />
|
||||
</vector>
|
9
app/src/main/res/drawable-night/ic_play_24dp.xml
Normal file
9
app/src/main/res/drawable-night/ic_play_24dp.xml
Normal file
@ -0,0 +1,9 @@
|
||||
<vector xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
android:width="24dp"
|
||||
android:height="24dp"
|
||||
android:viewportWidth="24"
|
||||
android:viewportHeight="24">
|
||||
<path
|
||||
android:fillColor="#FFFFFF"
|
||||
android:pathData="M8,5v14l11,-7z" />
|
||||
</vector>
|
27
app/src/main/res/drawable/ic_pause_24dp.xml
Normal file
27
app/src/main/res/drawable/ic_pause_24dp.xml
Normal file
@ -0,0 +1,27 @@
|
||||
<!--
|
||||
~ Kiwix Android
|
||||
~ Copyright (c) 2023 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/>.
|
||||
~
|
||||
-->
|
||||
|
||||
<vector xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
android:width="24dp"
|
||||
android:height="24dp"
|
||||
android:viewportWidth="24"
|
||||
android:viewportHeight="24">
|
||||
<path
|
||||
android:fillColor="#000000"
|
||||
android:pathData="M6,19h4L10,5L6,5v14zM14,5v14h4L18,5h-4z" />
|
||||
</vector>
|
27
app/src/main/res/drawable/ic_play_24dp.xml
Normal file
27
app/src/main/res/drawable/ic_play_24dp.xml
Normal file
@ -0,0 +1,27 @@
|
||||
<!--
|
||||
~ Kiwix Android
|
||||
~ Copyright (c) 2023 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/>.
|
||||
~
|
||||
-->
|
||||
|
||||
<vector xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
android:width="24dp"
|
||||
android:height="24dp"
|
||||
android:viewportWidth="24"
|
||||
android:viewportHeight="24">
|
||||
<path
|
||||
android:fillColor="#000000"
|
||||
android:pathData="M8,5v14l11,-7z" />
|
||||
</vector>
|
@ -87,6 +87,18 @@
|
||||
android:layout_height="match_parent"
|
||||
android:orientation="horizontal">
|
||||
|
||||
<ImageView
|
||||
android:id="@+id/pauseResume"
|
||||
android:layout_width="0dp"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_gravity="center_vertical"
|
||||
android:layout_marginStart="@dimen/stop_horizontal_margin"
|
||||
android:layout_marginEnd="@dimen/stop_horizontal_margin"
|
||||
android:layout_weight="0.5"
|
||||
android:minWidth="@dimen/stop_min_width"
|
||||
android:minHeight="@dimen/stop_min_height"
|
||||
android:src="@drawable/ic_pause_24dp" />
|
||||
|
||||
<ImageView
|
||||
android:id="@+id/stop"
|
||||
android:layout_width="0dp"
|
||||
|
@ -31,14 +31,12 @@ import org.kiwix.kiwixmobile.core.downloader.DownloadRequester
|
||||
import org.kiwix.kiwixmobile.core.downloader.model.DownloadModel
|
||||
import org.kiwix.kiwixmobile.core.downloader.model.DownloadRequest
|
||||
import org.kiwix.kiwixmobile.core.entity.LibraryNetworkEntity.Book
|
||||
import org.kiwix.kiwixmobile.core.utils.SharedPreferenceUtil
|
||||
import org.kiwix.kiwixmobile.core.zim_manager.fileselect_view.adapter.BooksOnDiskListItem.BookOnDisk
|
||||
import javax.inject.Inject
|
||||
|
||||
class FetchDownloadDao @Inject constructor(
|
||||
private val box: Box<FetchDownloadEntity>,
|
||||
private val newBookDao: NewBookDao,
|
||||
private val sharedPreferenceUtil: SharedPreferenceUtil
|
||||
private val newBookDao: NewBookDao
|
||||
) {
|
||||
|
||||
fun downloads(): Flowable<List<DownloadModel>> =
|
||||
@ -54,8 +52,6 @@ class FetchDownloadDao @Inject constructor(
|
||||
box.store.callInTx {
|
||||
box.remove(it)
|
||||
newBookDao.insert(it.map(::BookOnDisk))
|
||||
// remove the canceled id from shared preference if exist
|
||||
it.map { sharedPreferenceUtil.removeCanceledDownload(it.downloadId) }
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -79,10 +75,7 @@ class FetchDownloadDao @Inject constructor(
|
||||
box.put(FetchDownloadEntity(downloadId, book))
|
||||
}
|
||||
|
||||
fun delete(download: Download, isDownloadCanceled: Boolean) {
|
||||
if (isDownloadCanceled) {
|
||||
sharedPreferenceUtil.addCanceledDownloadIfNotExist(download)
|
||||
}
|
||||
fun delete(download: Download) {
|
||||
box.query {
|
||||
equal(FetchDownloadEntity_.downloadId, download.id)
|
||||
}.remove()
|
||||
|
@ -31,7 +31,6 @@ import org.kiwix.kiwixmobile.core.dao.NewLanguagesDao
|
||||
import org.kiwix.kiwixmobile.core.dao.NewNoteDao
|
||||
import org.kiwix.kiwixmobile.core.dao.NewRecentSearchDao
|
||||
import org.kiwix.kiwixmobile.core.dao.entities.MyObjectBox
|
||||
import org.kiwix.kiwixmobile.core.utils.SharedPreferenceUtil
|
||||
import javax.inject.Singleton
|
||||
|
||||
@Module
|
||||
@ -71,8 +70,7 @@ open class DatabaseModule {
|
||||
|
||||
@Provides @Singleton fun providesFetchDownloadDao(
|
||||
boxStore: BoxStore,
|
||||
newBookDao: NewBookDao,
|
||||
sharedPreferenceUtil: SharedPreferenceUtil
|
||||
newBookDao: NewBookDao
|
||||
): FetchDownloadDao =
|
||||
FetchDownloadDao(boxStore.boxFor(), newBookDao, sharedPreferenceUtil)
|
||||
FetchDownloadDao(boxStore.boxFor(), newBookDao)
|
||||
}
|
||||
|
@ -23,4 +23,5 @@ interface DownloadRequester {
|
||||
fun enqueue(downloadRequest: DownloadRequest): Long
|
||||
fun cancel(downloadId: Long)
|
||||
fun retryDownload(downloadId: Long)
|
||||
fun pauseResumeDownload(downloadId: Long, isPause: Boolean)
|
||||
}
|
||||
|
@ -23,4 +23,5 @@ interface Downloader {
|
||||
fun download(book: LibraryNetworkEntity.Book)
|
||||
fun cancelDownload(downloadId: Long)
|
||||
fun retryDownload(downloadId: Long)
|
||||
fun pauseResumeDownload(downloadId: Long, isPause: Boolean)
|
||||
}
|
||||
|
@ -57,4 +57,8 @@ class DownloaderImpl @Inject constructor(
|
||||
override fun retryDownload(downloadId: Long) {
|
||||
downloadRequester.retryDownload(downloadId)
|
||||
}
|
||||
|
||||
override fun pauseResumeDownload(downloadId: Long, isPause: Boolean) {
|
||||
downloadRequester.pauseResumeDownload(downloadId, isPause)
|
||||
}
|
||||
}
|
||||
|
@ -37,7 +37,7 @@ class FetchDownloadMonitor @Inject constructor(fetch: Fetch, fetchDownloadDao: F
|
||||
override fun onAdded(download: Download) {}
|
||||
|
||||
override fun onCancelled(download: Download) {
|
||||
delete(download, true)
|
||||
delete(download)
|
||||
}
|
||||
|
||||
override fun onCompleted(download: Download) {
|
||||
@ -100,8 +100,8 @@ class FetchDownloadMonitor @Inject constructor(fetch: Fetch, fetchDownloadDao: F
|
||||
updater.onNext { fetchDownloadDao.update(download) }
|
||||
}
|
||||
|
||||
private fun delete(download: Download, isDownloadCanceled: Boolean = false) {
|
||||
updater.onNext { fetchDownloadDao.delete(download, isDownloadCanceled) }
|
||||
private fun delete(download: Download) {
|
||||
updater.onNext { fetchDownloadDao.delete(download) }
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -33,23 +33,25 @@ class FetchDownloadRequester @Inject constructor(
|
||||
) : DownloadRequester {
|
||||
|
||||
override fun enqueue(downloadRequest: DownloadRequest): Long {
|
||||
val canceledDownloadId = sharedPreferenceUtil.getDownloadIdIfExist("${downloadRequest.uri}")
|
||||
if (canceledDownloadId != 0L) {
|
||||
fetch.retry(canceledDownloadId.toInt())
|
||||
return canceledDownloadId
|
||||
}
|
||||
val request = downloadRequest.toFetchRequest(sharedPreferenceUtil)
|
||||
fetch.enqueue(request)
|
||||
return request.id.toLong()
|
||||
}
|
||||
|
||||
override fun cancel(downloadId: Long) {
|
||||
fetch.cancel(downloadId.toInt())
|
||||
fetch.delete(downloadId.toInt())
|
||||
}
|
||||
|
||||
override fun retryDownload(downloadId: Long) {
|
||||
fetch.retry(downloadId.toInt())
|
||||
}
|
||||
|
||||
override fun pauseResumeDownload(downloadId: Long, isPause: Boolean) {
|
||||
if (isPause)
|
||||
fetch.resume(downloadId.toInt())
|
||||
else
|
||||
fetch.pause(downloadId.toInt())
|
||||
}
|
||||
}
|
||||
|
||||
private fun DownloadRequest.toFetchRequest(sharedPreferenceUtil: SharedPreferenceUtil) =
|
||||
|
@ -1,21 +0,0 @@
|
||||
/*
|
||||
* Kiwix Android
|
||||
* Copyright (c) 2023 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.downloader.model
|
||||
|
||||
data class CanceledDownloadModel(val downloadId: Long, val url: String)
|
@ -24,15 +24,11 @@ import androidx.appcompat.app.AppCompatDelegate
|
||||
import androidx.core.content.ContextCompat.getExternalFilesDirs
|
||||
import androidx.core.content.edit
|
||||
import androidx.preference.PreferenceManager
|
||||
import com.tonyodev.fetch2.Download
|
||||
import io.reactivex.Flowable
|
||||
import io.reactivex.processors.PublishProcessor
|
||||
import org.json.JSONArray
|
||||
import org.json.JSONObject
|
||||
import org.kiwix.kiwixmobile.core.NightModeConfig
|
||||
import org.kiwix.kiwixmobile.core.NightModeConfig.Mode.Companion.from
|
||||
import org.kiwix.kiwixmobile.core.R
|
||||
import org.kiwix.kiwixmobile.core.downloader.model.CanceledDownloadModel
|
||||
import org.kiwix.kiwixmobile.core.extensions.isFileExist
|
||||
import java.io.File
|
||||
import java.util.Locale
|
||||
@ -96,11 +92,9 @@ class SharedPreferenceUtil @Inject constructor(val context: Context) {
|
||||
putPrefStorage(it)
|
||||
putStoragePosition(0)
|
||||
}
|
||||
|
||||
!File(storage).isFileExist() -> getPublicDirectoryPath(defaultStorage()).also {
|
||||
putStoragePosition(0)
|
||||
}
|
||||
|
||||
else -> storage
|
||||
}
|
||||
}
|
||||
@ -224,67 +218,6 @@ class SharedPreferenceUtil @Inject constructor(val context: Context) {
|
||||
fun isPlayStoreBuildWithAndroid11OrAbove(): Boolean =
|
||||
isPlayStoreBuild && Build.VERSION.SDK_INT >= Build.VERSION_CODES.R
|
||||
|
||||
fun addCanceledDownloadIfNotExist(download: Download) {
|
||||
if (getDownloadIdIfExist(download.url) == 0L) {
|
||||
val jsonArray = JSONArray()
|
||||
for (item in getCanceledDownloadItems()) {
|
||||
jsonArray.put(JSONObject().put(DOWNLOAD_ID, item.downloadId).put(DOWNLOAD_URL, item.url))
|
||||
}
|
||||
jsonArray.put(JSONObject().put(DOWNLOAD_ID, download.id).put(DOWNLOAD_URL, download.url))
|
||||
saveCanceledDownloads(jsonArray)
|
||||
}
|
||||
}
|
||||
|
||||
private fun saveCanceledDownloads(canceledJsonArray: JSONArray) {
|
||||
sharedPreferences.edit {
|
||||
putString(DOWNLOAD_LIST, "$canceledJsonArray")
|
||||
}
|
||||
}
|
||||
|
||||
private fun getCanceledDownloadItems(): MutableList<CanceledDownloadModel> {
|
||||
val savedCanceledDownloads = sharedPreferences.getString(DOWNLOAD_LIST, "")
|
||||
|
||||
val canceledDownloadList = mutableListOf<CanceledDownloadModel>()
|
||||
|
||||
if (!savedCanceledDownloads.isNullOrEmpty()) {
|
||||
val jsonArray = JSONArray(savedCanceledDownloads)
|
||||
(0 until jsonArray.length())
|
||||
.asSequence()
|
||||
.map(jsonArray::getJSONObject)
|
||||
.mapTo(canceledDownloadList) {
|
||||
CanceledDownloadModel(
|
||||
it.getInt(DOWNLOAD_ID).toLong(),
|
||||
it.getString(DOWNLOAD_URL)
|
||||
)
|
||||
}
|
||||
}
|
||||
return canceledDownloadList
|
||||
}
|
||||
|
||||
fun removeCanceledDownload(downloadId: Long) {
|
||||
val canceledList = getCanceledDownloadItems().apply {
|
||||
asSequence()
|
||||
.filter { it.downloadId == downloadId }
|
||||
.forEach(this::remove)
|
||||
}
|
||||
|
||||
// Save the updated list back to SharedPreferences
|
||||
val jsonArray = JSONArray()
|
||||
for (item in canceledList) {
|
||||
jsonArray.put(JSONObject().put(DOWNLOAD_ID, item.downloadId).put(DOWNLOAD_URL, item.url))
|
||||
}
|
||||
saveCanceledDownloads(jsonArray)
|
||||
}
|
||||
|
||||
fun getDownloadIdIfExist(url: String): Long {
|
||||
return getCanceledDownloadItems()
|
||||
.firstOrNull {
|
||||
it.url.substringAfterLast("/") == url.substringAfterLast("/")
|
||||
}
|
||||
?.downloadId
|
||||
?: 0L
|
||||
}
|
||||
|
||||
companion object {
|
||||
// Prefs
|
||||
const val PREF_LANG = "pref_language_chooser"
|
||||
@ -311,8 +244,5 @@ class SharedPreferenceUtil @Inject constructor(val context: Context) {
|
||||
private const val DEFAULT_ZOOM = 100
|
||||
const val PREF_MANAGE_EXTERNAL_FILES = "pref_manage_external_files"
|
||||
const val IS_PLAY_STORE_BUILD = "is_play_store_build"
|
||||
const val DOWNLOAD_ID = "download_id"
|
||||
const val DOWNLOAD_URL = "download_url"
|
||||
const val DOWNLOAD_LIST = "download_list"
|
||||
}
|
||||
}
|
||||
|
Loading…
x
Reference in New Issue
Block a user