From 0a2cff92b5ca2ce385c62ac937d0808973b49b8f Mon Sep 17 00:00:00 2001 From: MohitMali Date: Fri, 28 Jul 2023 18:51:06 +0530 Subject: [PATCH] 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. --- .../library/OnlineLibraryFragment.kt | 32 ++++++--- .../libraryView/adapter/LibraryDelegate.kt | 8 ++- .../libraryView/adapter/LibraryViewHolder.kt | 16 ++++- .../main/res/drawable-night/ic_pause_24dp.xml | 9 +++ .../main/res/drawable-night/ic_play_24dp.xml | 9 +++ app/src/main/res/drawable/ic_pause_24dp.xml | 27 +++++++ app/src/main/res/drawable/ic_play_24dp.xml | 27 +++++++ app/src/main/res/layout/item_download.xml | 12 ++++ .../kiwixmobile/core/dao/FetchDownloadDao.kt | 11 +-- .../core/di/modules/DatabaseModule.kt | 6 +- .../core/downloader/DownloadRequester.kt | 1 + .../kiwixmobile/core/downloader/Downloader.kt | 1 + .../core/downloader/DownloaderImpl.kt | 4 ++ .../downloader/fetch/FetchDownloadMonitor.kt | 6 +- .../fetch/FetchDownloadRequester.kt | 14 ++-- .../downloader/model/CanceledDownloadModel.kt | 21 ------ .../core/utils/SharedPreferenceUtil.kt | 70 ------------------- 17 files changed, 146 insertions(+), 128 deletions(-) create mode 100644 app/src/main/res/drawable-night/ic_pause_24dp.xml create mode 100644 app/src/main/res/drawable-night/ic_play_24dp.xml create mode 100644 app/src/main/res/drawable/ic_pause_24dp.xml create mode 100644 app/src/main/res/drawable/ic_play_24dp.xml delete mode 100644 core/src/main/java/org/kiwix/kiwixmobile/core/downloader/model/CanceledDownloadModel.kt diff --git a/app/src/main/java/org/kiwix/kiwixmobile/nav/destination/library/OnlineLibraryFragment.kt b/app/src/main/java/org/kiwix/kiwixmobile/nav/destination/library/OnlineLibraryFragment.kt index 086423547..8451e6d84 100644 --- a/app/src/main/java/org/kiwix/kiwixmobile/nav/destination/library/OnlineLibraryFragment.kt +++ b/app/src/main/java/org/kiwix/kiwixmobile/nav/destination/library/OnlineLibraryFragment.kt @@ -113,20 +113,30 @@ class OnlineLibraryFragment : BaseFragment(), FragmentActivityExtensions { private val libraryAdapter: LibraryAdapter by lazy { LibraryAdapter( LibraryDelegate.BookDelegate(bookUtils, ::onBookItemClick, availableSpaceCalculator), - LibraryDelegate.DownloadDelegate { - if (it.currentDownloadState == Status.FAILED) { - if (isNotConnected) { - noInternetSnackbar() + LibraryDelegate.DownloadDelegate( + { + if (it.currentDownloadState == Status.FAILED) { + if (isNotConnected) { + noInternetSnackbar() + } else { + downloader.retryDownload(it.downloadId) + } } else { - downloader.retryDownload(it.downloadId) + dialogShower.show( + KiwixDialog.YesNoDialog.StopDownload, + { downloader.cancelDownload(it.downloadId) } + ) + } + }, + { + context?.let { context -> + downloader.pauseResumeDownload( + it.downloadId, + it.downloadState.toReadableState(context) == "Paused" + ) } - } else { - dialogShower.show( - KiwixDialog.YesNoDialog.StopDownload, - { downloader.cancelDownload(it.downloadId) } - ) } - }, + ), LibraryDelegate.DividerDelegate ) } diff --git a/app/src/main/java/org/kiwix/kiwixmobile/zimManager/libraryView/adapter/LibraryDelegate.kt b/app/src/main/java/org/kiwix/kiwixmobile/zimManager/libraryView/adapter/LibraryDelegate.kt index 0592b4673..42c8747b4 100644 --- a/app/src/main/java/org/kiwix/kiwixmobile/zimManager/libraryView/adapter/LibraryDelegate.kt +++ b/app/src/main/java/org/kiwix/kiwixmobile/zimManager/libraryView/adapter/LibraryDelegate.kt @@ -51,14 +51,18 @@ sealed class LibraryDelegate> ) } - class DownloadDelegate(private val clickAction: (LibraryDownloadItem) -> Unit) : + class DownloadDelegate( + private val clickAction: (LibraryDownloadItem) -> Unit, + private val pauseResumeClickAction: (LibraryDownloadItem) -> Unit + ) : LibraryDelegate() { override val itemClass = LibraryDownloadItem::class.java override fun createViewHolder(parent: ViewGroup) = DownloadViewHolder( parent.viewBinding(ItemDownloadBinding::inflate, false), - clickAction + clickAction, + pauseResumeClickAction ) } diff --git a/app/src/main/java/org/kiwix/kiwixmobile/zimManager/libraryView/adapter/LibraryViewHolder.kt b/app/src/main/java/org/kiwix/kiwixmobile/zimManager/libraryView/adapter/LibraryViewHolder.kt index b9e2a3e4d..45c371c5f 100644 --- a/app/src/main/java/org/kiwix/kiwixmobile/zimManager/libraryView/adapter/LibraryViewHolder.kt +++ b/app/src/main/java/org/kiwix/kiwixmobile/zimManager/libraryView/adapter/LibraryViewHolder.kt @@ -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(containerView: View) : class DownloadViewHolder( private val itemDownloadBinding: ItemDownloadBinding, - private val clickAction: (LibraryDownloadItem) -> Unit + private val clickAction: (LibraryDownloadItem) -> Unit, + private val pauseResumeClickAction: (LibraryDownloadItem) -> Unit ) : LibraryViewHolder(itemDownloadBinding.root) { @@ -100,8 +102,18 @@ sealed class LibraryViewHolder(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) } diff --git a/app/src/main/res/drawable-night/ic_pause_24dp.xml b/app/src/main/res/drawable-night/ic_pause_24dp.xml new file mode 100644 index 000000000..c90e3d0ef --- /dev/null +++ b/app/src/main/res/drawable-night/ic_pause_24dp.xml @@ -0,0 +1,9 @@ + + + diff --git a/app/src/main/res/drawable-night/ic_play_24dp.xml b/app/src/main/res/drawable-night/ic_play_24dp.xml new file mode 100644 index 000000000..92228b574 --- /dev/null +++ b/app/src/main/res/drawable-night/ic_play_24dp.xml @@ -0,0 +1,9 @@ + + + diff --git a/app/src/main/res/drawable/ic_pause_24dp.xml b/app/src/main/res/drawable/ic_pause_24dp.xml new file mode 100644 index 000000000..45116a2fc --- /dev/null +++ b/app/src/main/res/drawable/ic_pause_24dp.xml @@ -0,0 +1,27 @@ + + + + + diff --git a/app/src/main/res/drawable/ic_play_24dp.xml b/app/src/main/res/drawable/ic_play_24dp.xml new file mode 100644 index 000000000..1e922390e --- /dev/null +++ b/app/src/main/res/drawable/ic_play_24dp.xml @@ -0,0 +1,27 @@ + + + + + diff --git a/app/src/main/res/layout/item_download.xml b/app/src/main/res/layout/item_download.xml index 2441aa62d..1d04fec70 100644 --- a/app/src/main/res/layout/item_download.xml +++ b/app/src/main/res/layout/item_download.xml @@ -87,6 +87,18 @@ android:layout_height="match_parent" android:orientation="horizontal"> + + , - private val newBookDao: NewBookDao, - private val sharedPreferenceUtil: SharedPreferenceUtil + private val newBookDao: NewBookDao ) { fun downloads(): Flowable> = @@ -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() diff --git a/core/src/main/java/org/kiwix/kiwixmobile/core/di/modules/DatabaseModule.kt b/core/src/main/java/org/kiwix/kiwixmobile/core/di/modules/DatabaseModule.kt index b40bf796e..7de1934d4 100644 --- a/core/src/main/java/org/kiwix/kiwixmobile/core/di/modules/DatabaseModule.kt +++ b/core/src/main/java/org/kiwix/kiwixmobile/core/di/modules/DatabaseModule.kt @@ -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) } diff --git a/core/src/main/java/org/kiwix/kiwixmobile/core/downloader/DownloadRequester.kt b/core/src/main/java/org/kiwix/kiwixmobile/core/downloader/DownloadRequester.kt index 33c73c508..063bbf713 100644 --- a/core/src/main/java/org/kiwix/kiwixmobile/core/downloader/DownloadRequester.kt +++ b/core/src/main/java/org/kiwix/kiwixmobile/core/downloader/DownloadRequester.kt @@ -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) } diff --git a/core/src/main/java/org/kiwix/kiwixmobile/core/downloader/Downloader.kt b/core/src/main/java/org/kiwix/kiwixmobile/core/downloader/Downloader.kt index 4f9d09b50..06aa2eca9 100644 --- a/core/src/main/java/org/kiwix/kiwixmobile/core/downloader/Downloader.kt +++ b/core/src/main/java/org/kiwix/kiwixmobile/core/downloader/Downloader.kt @@ -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) } diff --git a/core/src/main/java/org/kiwix/kiwixmobile/core/downloader/DownloaderImpl.kt b/core/src/main/java/org/kiwix/kiwixmobile/core/downloader/DownloaderImpl.kt index 52dc10e27..4036bf6cf 100644 --- a/core/src/main/java/org/kiwix/kiwixmobile/core/downloader/DownloaderImpl.kt +++ b/core/src/main/java/org/kiwix/kiwixmobile/core/downloader/DownloaderImpl.kt @@ -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) + } } diff --git a/core/src/main/java/org/kiwix/kiwixmobile/core/downloader/fetch/FetchDownloadMonitor.kt b/core/src/main/java/org/kiwix/kiwixmobile/core/downloader/fetch/FetchDownloadMonitor.kt index ebccd4bf4..eef4bf97d 100644 --- a/core/src/main/java/org/kiwix/kiwixmobile/core/downloader/fetch/FetchDownloadMonitor.kt +++ b/core/src/main/java/org/kiwix/kiwixmobile/core/downloader/fetch/FetchDownloadMonitor.kt @@ -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) } } } diff --git a/core/src/main/java/org/kiwix/kiwixmobile/core/downloader/fetch/FetchDownloadRequester.kt b/core/src/main/java/org/kiwix/kiwixmobile/core/downloader/fetch/FetchDownloadRequester.kt index 3dbcd999e..13872f8e7 100644 --- a/core/src/main/java/org/kiwix/kiwixmobile/core/downloader/fetch/FetchDownloadRequester.kt +++ b/core/src/main/java/org/kiwix/kiwixmobile/core/downloader/fetch/FetchDownloadRequester.kt @@ -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) = diff --git a/core/src/main/java/org/kiwix/kiwixmobile/core/downloader/model/CanceledDownloadModel.kt b/core/src/main/java/org/kiwix/kiwixmobile/core/downloader/model/CanceledDownloadModel.kt deleted file mode 100644 index d0e745e27..000000000 --- a/core/src/main/java/org/kiwix/kiwixmobile/core/downloader/model/CanceledDownloadModel.kt +++ /dev/null @@ -1,21 +0,0 @@ -/* - * Kiwix Android - * Copyright (c) 2023 Kiwix - * 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 . - * - */ - -package org.kiwix.kiwixmobile.core.downloader.model - -data class CanceledDownloadModel(val downloadId: Long, val url: String) diff --git a/core/src/main/java/org/kiwix/kiwixmobile/core/utils/SharedPreferenceUtil.kt b/core/src/main/java/org/kiwix/kiwixmobile/core/utils/SharedPreferenceUtil.kt index 869e76815..f4cc2eb09 100644 --- a/core/src/main/java/org/kiwix/kiwixmobile/core/utils/SharedPreferenceUtil.kt +++ b/core/src/main/java/org/kiwix/kiwixmobile/core/utils/SharedPreferenceUtil.kt @@ -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 { - val savedCanceledDownloads = sharedPreferences.getString(DOWNLOAD_LIST, "") - - val canceledDownloadList = mutableListOf() - - 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" } }