FileTransferService.kt 6.2 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185
  1. /*
  2. * Nextcloud Android client application
  3. *
  4. * @author Chris Narkiewicz
  5. * Copyright (C) 2021 Chris Narkiewicz <hello@ezaquarii.com>
  6. *
  7. * This program is free software: you can redistribute it and/or modify
  8. * it under the terms of the GNU Affero General Public License as published by
  9. * the Free Software Foundation, either version 3 of the License, or
  10. * (at your option) any later version.
  11. *
  12. * This program is distributed in the hope that it will be useful,
  13. * but WITHOUT ANY WARRANTY; without even the implied warranty of
  14. * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
  15. * GNU Affero General Public License for more details.
  16. *
  17. * You should have received a copy of the GNU Affero General Public License
  18. * along with this program. If not, see <http://www.gnu.org/licenses/>.
  19. */
  20. package com.nextcloud.client.files.downloader
  21. import android.app.Service
  22. import android.content.Context
  23. import android.content.Intent
  24. import android.os.IBinder
  25. import com.nextcloud.client.account.User
  26. import com.nextcloud.client.core.AsyncRunner
  27. import com.nextcloud.client.core.LocalBinder
  28. import com.nextcloud.client.device.PowerManagementService
  29. import com.nextcloud.client.logger.Logger
  30. import com.nextcloud.client.network.ClientFactory
  31. import com.nextcloud.client.network.ConnectivityService
  32. import com.nextcloud.client.notifications.AppNotificationManager
  33. import com.owncloud.android.datamodel.FileDataStorageManager
  34. import com.owncloud.android.datamodel.UploadsStorageManager
  35. import dagger.android.AndroidInjection
  36. import javax.inject.Inject
  37. import javax.inject.Named
  38. class FileTransferService : Service() {
  39. companion object {
  40. const val TAG = "DownloaderService"
  41. const val ACTION_TRANSFER = "transfer"
  42. const val EXTRA_REQUEST = "request"
  43. const val EXTRA_USER = "user"
  44. fun createBindIntent(context: Context, user: User): Intent {
  45. return Intent(context, FileTransferService::class.java).apply {
  46. putExtra(EXTRA_USER, user)
  47. }
  48. }
  49. fun createTransferRequestIntent(context: Context, request: Request): Intent {
  50. return Intent(context, FileTransferService::class.java).apply {
  51. action = ACTION_TRANSFER
  52. putExtra(EXTRA_REQUEST, request)
  53. }
  54. }
  55. }
  56. /**
  57. * Binder forwards [TransferManager] API calls to selected instance of downloader.
  58. */
  59. class Binder(
  60. downloader: TransferManagerImpl,
  61. service: FileTransferService
  62. ) : LocalBinder<FileTransferService>(service),
  63. TransferManager by downloader
  64. @Inject
  65. lateinit var notificationsManager: AppNotificationManager
  66. @Inject
  67. lateinit var clientFactory: ClientFactory
  68. @Inject
  69. @Named("io")
  70. lateinit var runner: AsyncRunner
  71. @Inject
  72. lateinit var logger: Logger
  73. @Inject
  74. lateinit var uploadsStorageManager: UploadsStorageManager
  75. @Inject
  76. lateinit var connectivityService: ConnectivityService
  77. @Inject
  78. lateinit var powerManagementService: PowerManagementService
  79. @Inject
  80. lateinit var fileDataStorageManager: FileDataStorageManager
  81. val isRunning: Boolean get() = downloaders.any { it.value.isRunning }
  82. private val downloaders: MutableMap<String, TransferManagerImpl> = mutableMapOf()
  83. override fun onCreate() {
  84. AndroidInjection.inject(this)
  85. }
  86. override fun onStartCommand(intent: Intent, flags: Int, startId: Int): Int {
  87. if (intent.action != ACTION_TRANSFER) {
  88. return START_NOT_STICKY
  89. }
  90. if (!isRunning) {
  91. startForeground(
  92. AppNotificationManager.TRANSFER_NOTIFICATION_ID,
  93. notificationsManager.buildDownloadServiceForegroundNotification()
  94. )
  95. }
  96. val request = intent.getParcelableExtra(EXTRA_REQUEST) as Request
  97. val transferManager = getTransferManager(request.user)
  98. transferManager.enqueue(request)
  99. logger.d(TAG, "Enqueued new transfer: ${request.uuid} ${request.file.remotePath}")
  100. return START_NOT_STICKY
  101. }
  102. override fun onBind(intent: Intent?): IBinder? {
  103. val user = intent?.getParcelableExtra<User>(EXTRA_USER)
  104. if (user != null) {
  105. return Binder(getTransferManager(user), this)
  106. } else {
  107. return null
  108. }
  109. }
  110. private fun onTransferUpdate(transfer: Transfer) {
  111. if (!isRunning) {
  112. logger.d(TAG, "All downloads completed")
  113. notificationsManager.cancelTransferNotification()
  114. stopForeground(true)
  115. stopSelf()
  116. } else if (transfer.direction == Direction.DOWNLOAD) {
  117. notificationsManager.postDownloadTransferProgress(
  118. fileOwner = transfer.request.user,
  119. file = transfer.request.file,
  120. progress = transfer.progress,
  121. allowPreview = !transfer.request.test
  122. )
  123. } else if (transfer.direction == Direction.UPLOAD) {
  124. notificationsManager.postUploadTransferProgress(
  125. fileOwner = transfer.request.user,
  126. file = transfer.request.file,
  127. progress = transfer.progress
  128. )
  129. }
  130. }
  131. override fun onDestroy() {
  132. super.onDestroy()
  133. logger.d(TAG, "Stopping downloader service")
  134. }
  135. private fun getTransferManager(user: User): TransferManagerImpl {
  136. val existingDownloader = downloaders[user.accountName]
  137. return if (existingDownloader != null) {
  138. existingDownloader
  139. } else {
  140. val downloadTaskFactory = DownloadTask.Factory(
  141. applicationContext,
  142. { clientFactory.create(user) },
  143. contentResolver
  144. )
  145. val uploadTaskFactory = UploadTask.Factory(
  146. applicationContext,
  147. uploadsStorageManager,
  148. connectivityService,
  149. powerManagementService,
  150. { clientFactory.create(user) },
  151. fileDataStorageManager
  152. )
  153. val newDownloader = TransferManagerImpl(runner, downloadTaskFactory, uploadTaskFactory)
  154. newDownloader.registerTransferListener(this::onTransferUpdate)
  155. downloaders[user.accountName] = newDownloader
  156. newDownloader
  157. }
  158. }
  159. }