123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144 |
- /*
- * Nextcloud Android client application
- *
- * @author Chris Narkiewicz
- * Copyright (C) 2020 Chris Narkiewicz <hello@ezaquarii.com>
- *
- * This program is free software: you can redistribute it and/or modify
- * it under the terms of the GNU Affero 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 Affero General Public License for more details.
- *
- * You should have received a copy of the GNU Affero General Public License
- * along with this program. If not, see <http://www.gnu.org/licenses/>.
- */
- package com.nextcloud.client.files.downloader
- import com.nextcloud.client.core.AsyncRunner
- import com.nextcloud.client.core.IsCancelled
- import com.nextcloud.client.core.OnProgressCallback
- import com.nextcloud.client.core.TaskFunction
- import com.owncloud.android.datamodel.OCFile
- import java.util.UUID
- /**
- * Per-user file downloader.
- *
- * All notifications are performed on main thread. All download processes are run
- * in the background.
- *
- * @param runner Background task runner. It is important to provide runner that is not shared with UI code.
- * @param taskFactory Download task factory
- * @param threads maximum number of concurrent download processes
- */
- @Suppress("LongParameterList") // download operations requires those resources
- class DownloaderImpl(
- private val runner: AsyncRunner,
- private val taskFactory: DownloadTask.Factory,
- threads: Int = 1
- ) : Downloader {
- companion object {
- const val PROGRESS_PERCENTAGE_MAX = 100
- const val PROGRESS_PERCENTAGE_MIN = 0
- const val TEST_DOWNLOAD_PROGRESS_UPDATE_PERIOD_MS = 200L
- }
- private val registry = Registry(
- onStartDownload = this::onStartDownload,
- onDownloadChanged = this::onDownloadUpdate,
- maxRunning = threads
- )
- private val downloadListeners: MutableSet<(Download) -> Unit> = mutableSetOf()
- private val statusListeners: MutableSet<(Downloader.Status) -> Unit> = mutableSetOf()
- override val isRunning: Boolean get() = registry.isRunning
- override val status: Downloader.Status
- get() = Downloader.Status(
- pending = registry.pending,
- running = registry.running,
- completed = registry.completed
- )
- override fun registerDownloadListener(listener: (Download) -> Unit) {
- downloadListeners.add(listener)
- }
- override fun removeDownloadListener(listener: (Download) -> Unit) {
- downloadListeners.remove(listener)
- }
- override fun registerStatusListener(listener: (Downloader.Status) -> Unit) {
- statusListeners.add(listener)
- }
- override fun removeStatusListener(listener: (Downloader.Status) -> Unit) {
- statusListeners.remove(listener)
- }
- override fun download(request: Request) {
- registry.add(request)
- registry.startNext()
- }
- override fun getDownload(uuid: UUID): Download? = registry.getDownload(uuid)
- override fun getDownload(file: OCFile): Download? = registry.getDownload(file)
- private fun onStartDownload(uuid: UUID, request: Request) {
- val downloadTask = createDownloadTask(request)
- runner.postTask(
- task = downloadTask,
- onProgress = { progress: Int -> registry.progress(uuid, progress) },
- onResult = { result -> registry.complete(uuid, result.success, result.file); registry.startNext() },
- onError = { registry.complete(uuid, false); registry.startNext() }
- )
- }
- private fun createDownloadTask(request: Request): TaskFunction<DownloadTask.Result, Int> {
- return if (request.test) {
- { progress: OnProgressCallback<Int>, isCancelled: IsCancelled ->
- testDownloadTask(request.file, progress, isCancelled)
- }
- } else {
- val downloadTask = taskFactory.create()
- val wrapper: TaskFunction<DownloadTask.Result, Int> = { progress: ((Int) -> Unit), isCancelled ->
- downloadTask.download(request, progress, isCancelled)
- }
- wrapper
- }
- }
- private fun onDownloadUpdate(download: Download) {
- downloadListeners.forEach { it.invoke(download) }
- if (statusListeners.isNotEmpty()) {
- val status = this.status
- statusListeners.forEach { it.invoke(status) }
- }
- }
- /**
- * Test download task is used only to simulate download process without
- * any network traffic. It is used for development.
- */
- private fun testDownloadTask(
- file: OCFile,
- onProgress: OnProgressCallback<Int>,
- isCancelled: IsCancelled
- ): DownloadTask.Result {
- for (i in PROGRESS_PERCENTAGE_MIN..PROGRESS_PERCENTAGE_MAX) {
- onProgress(i)
- if (isCancelled()) {
- return DownloadTask.Result(file, false)
- }
- Thread.sleep(TEST_DOWNLOAD_PROGRESS_UPDATE_PERIOD_MS)
- }
- return DownloadTask.Result(file, true)
- }
- }
|