nextcloud-android/src/main/java/com/nextcloud/client/files/downloader/DownloaderConnection.kt

123 lines
4.4 KiB
Kotlin

/**
* 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 android.content.Context
import android.content.Intent
import android.os.IBinder
import com.nextcloud.client.account.User
import com.nextcloud.client.core.LocalConnection
import com.owncloud.android.datamodel.OCFile
import java.util.UUID
class DownloaderConnection(context: Context, val user: User) : LocalConnection<DownloaderService>(context), Downloader {
private var downloadListeners: MutableSet<(Download) -> Unit> = mutableSetOf()
private var statusListeners: MutableSet<(Downloader.Status) -> Unit> = mutableSetOf()
private var binder: DownloaderService.Binder? = null
private val downloadsRequiringStatusRedelivery: MutableSet<UUID> = mutableSetOf()
override val isRunning: Boolean
get() = binder?.isRunning ?: false
override val status: Downloader.Status
get() = binder?.status ?: Downloader.Status.EMPTY
override fun getDownload(uuid: UUID): Download? = binder?.getDownload(uuid)
override fun getDownload(file: OCFile): Download? = binder?.getDownload(file)
override fun download(request: Request) {
val intent = DownloaderService.createDownloadIntent(context, request)
context.startService(intent)
if (!isConnected && downloadListeners.size > 0) {
downloadsRequiringStatusRedelivery.add(request.uuid)
}
}
override fun registerDownloadListener(listener: (Download) -> Unit) {
downloadListeners.add(listener)
binder?.registerDownloadListener(listener)
}
override fun removeDownloadListener(listener: (Download) -> Unit) {
downloadListeners.remove(listener)
binder?.removeDownloadListener(listener)
}
override fun registerStatusListener(listener: (Downloader.Status) -> Unit) {
statusListeners.add(listener)
binder?.registerStatusListener(listener)
}
override fun removeStatusListener(listener: (Downloader.Status) -> Unit) {
statusListeners.remove(listener)
binder?.removeStatusListener(listener)
}
override fun createBindIntent(): Intent {
return DownloaderService.createBindIntent(context, user)
}
override fun onBound(binder: IBinder) {
super.onBound(binder)
this.binder = binder as DownloaderService.Binder
downloadListeners.forEach { listener ->
binder.registerDownloadListener(listener)
}
statusListeners.forEach { listener ->
binder.registerStatusListener(listener)
}
deliverMissedUpdates()
}
/**
* Since binding and download start are both asynchronous and the order
* is not guaranteed, some downloads might already finish when service is bound,
* resulting in lost notifications.
*
* Deliver all updates for pending downloads that were scheduled
* before service has been bound.
*/
private fun deliverMissedUpdates() {
val downloadUpdates = downloadsRequiringStatusRedelivery.mapNotNull { uuid ->
binder?.getDownload(uuid)
}
downloadListeners.forEach { listener ->
downloadUpdates.forEach { update ->
listener.invoke(update)
}
}
downloadsRequiringStatusRedelivery.clear()
if (statusListeners.isNotEmpty()) {
binder?.status?.let { status ->
statusListeners.forEach { it.invoke(status) }
}
}
}
override fun onUnbind() {
super.onUnbind()
downloadListeners.forEach { binder?.removeDownloadListener(it) }
statusListeners.forEach { binder?.removeStatusListener(it) }
}
}