diff --git a/owncloudApp/src/main/java/com/owncloud/android/dependecyinjection/UseCaseModule.kt b/owncloudApp/src/main/java/com/owncloud/android/dependecyinjection/UseCaseModule.kt index b2b1a442424..3a196da83d5 100644 --- a/owncloudApp/src/main/java/com/owncloud/android/dependecyinjection/UseCaseModule.kt +++ b/owncloudApp/src/main/java/com/owncloud/android/dependecyinjection/UseCaseModule.kt @@ -67,6 +67,10 @@ import com.owncloud.android.domain.sharing.shares.usecases.EditPublicShareAsyncU import com.owncloud.android.domain.sharing.shares.usecases.GetShareAsLiveDataUseCase import com.owncloud.android.domain.sharing.shares.usecases.GetSharesAsLiveDataUseCase import com.owncloud.android.domain.sharing.shares.usecases.RefreshSharesFromServerAsyncUseCase +import com.owncloud.android.domain.transfers.usecases.ClearFailedTransfersUseCase +import com.owncloud.android.domain.transfers.usecases.ClearSuccessfulTransfersUseCase +import com.owncloud.android.domain.transfers.usecases.DeleteTransferWithIdUseCase +import com.owncloud.android.domain.transfers.usecases.GetAllTransfersAsLiveDataUseCase import com.owncloud.android.domain.user.usecases.GetStoredQuotaUseCase import com.owncloud.android.domain.user.usecases.GetUserAvatarAsyncUseCase import com.owncloud.android.domain.user.usecases.GetUserInfoAsyncUseCase @@ -79,9 +83,14 @@ import com.owncloud.android.usecases.transfers.downloads.DownloadFileUseCase import com.owncloud.android.usecases.transfers.downloads.GetLiveDataForDownloadingFileUseCase import com.owncloud.android.usecases.transfers.downloads.GetLiveDataForFinishedDownloadsFromAccountUseCase import com.owncloud.android.usecases.transfers.uploads.CancelUploadForFileUseCase +import com.owncloud.android.usecases.transfers.uploads.CancelUploadWithIdUseCase +import com.owncloud.android.usecases.transfers.uploads.RetryFailedUploadsUseCase +import com.owncloud.android.usecases.transfers.uploads.RetryUploadFromContentUriUseCase import com.owncloud.android.usecases.transfers.uploads.RetryUploadFromSystemUseCase +import com.owncloud.android.usecases.transfers.uploads.UploadFileFromContentUriUseCase +import com.owncloud.android.usecases.transfers.uploads.UploadFileFromSystemUseCase import com.owncloud.android.usecases.transfers.uploads.UploadFileInConflictUseCase -import com.owncloud.android.usecases.transfers.uploads.UploadFilesFromSAFUseCase +import com.owncloud.android.usecases.transfers.uploads.UploadFilesFromContentUriUseCase import com.owncloud.android.usecases.transfers.uploads.UploadFilesFromSystemUseCase import org.koin.dsl.module @@ -143,11 +152,20 @@ val useCaseModule = module { factory { DownloadFileUseCase(get()) } factory { GetLiveDataForDownloadingFileUseCase(get()) } factory { GetLiveDataForFinishedDownloadsFromAccountUseCase(get()) } - factory { UploadFilesFromSAFUseCase(get(), get()) } + factory { UploadFileFromSystemUseCase(get()) } + factory { UploadFileFromContentUriUseCase(get()) } + factory { UploadFilesFromContentUriUseCase(get(), get()) } factory { UploadFilesFromSystemUseCase(get(), get()) } factory { UploadFileInConflictUseCase(get(), get()) } factory { CancelUploadForFileUseCase(get(), get()) } - factory { RetryUploadFromSystemUseCase(get(), get()) } + factory { RetryUploadFromSystemUseCase(get(), get(), get()) } + factory { RetryUploadFromContentUriUseCase(get(), get(), get()) } + factory { GetAllTransfersAsLiveDataUseCase(get()) } + factory { CancelUploadWithIdUseCase(get(), get()) } + factory { DeleteTransferWithIdUseCase(get()) } + factory { ClearFailedTransfersUseCase(get()) } + factory { RetryFailedUploadsUseCase(get(), get(), get(), get()) } + factory { ClearSuccessfulTransfersUseCase(get()) } // User factory { GetStoredQuotaUseCase(get()) } diff --git a/owncloudApp/src/main/java/com/owncloud/android/dependecyinjection/ViewModelModule.kt b/owncloudApp/src/main/java/com/owncloud/android/dependecyinjection/ViewModelModule.kt index ee67335e06a..6185f2806b7 100644 --- a/owncloudApp/src/main/java/com/owncloud/android/dependecyinjection/ViewModelModule.kt +++ b/owncloudApp/src/main/java/com/owncloud/android/dependecyinjection/ViewModelModule.kt @@ -88,5 +88,5 @@ val viewModelModule = module { viewModel { FileDetailsViewModel(get(), get(), get(), get(), get()) } viewModel { FileOperationsViewModel(get(), get(), get(), get(), get(), get(), get(), get(), get(), get()) } viewModel { MainFileListViewModel(get(), get(), get(), get(), get(), get(), get(), get(), get(), get()) } - viewModel { TransfersViewModel(get(), get(), get()) } + viewModel { TransfersViewModel(get(), get(), get(), get(), get(), get(), get(), get(), get(), get(), get(), get()) } } diff --git a/owncloudApp/src/main/java/com/owncloud/android/extensions/OCTransferExt.kt b/owncloudApp/src/main/java/com/owncloud/android/extensions/OCTransferExt.kt new file mode 100644 index 00000000000..d54a936f483 --- /dev/null +++ b/owncloudApp/src/main/java/com/owncloud/android/extensions/OCTransferExt.kt @@ -0,0 +1,63 @@ +/** + * ownCloud Android client application + * + * @author Juan Carlos Garrote Gascón + * + * Copyright (C) 2022 ownCloud GmbH. + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2, + * as published by the Free Software Foundation. + * + * 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 com.owncloud.android.extensions + +import androidx.annotation.StringRes +import com.owncloud.android.R +import com.owncloud.android.domain.transfers.model.OCTransfer +import com.owncloud.android.domain.transfers.model.TransferResult +import com.owncloud.android.domain.transfers.model.TransferStatus + +@StringRes +fun OCTransfer.statusToStringRes(): Int { + return when (status) { + TransferStatus.TRANSFER_IN_PROGRESS -> R.string.uploader_upload_in_progress_ticker + TransferStatus.TRANSFER_SUCCEEDED -> R.string.uploads_view_upload_status_succeeded + TransferStatus.TRANSFER_QUEUED -> R.string.uploads_view_upload_status_queued + TransferStatus.TRANSFER_FAILED -> when (lastResult) { + TransferResult.CREDENTIAL_ERROR -> R.string.uploads_view_upload_status_failed_credentials_error + TransferResult.FOLDER_ERROR -> R.string.uploads_view_upload_status_failed_folder_error + TransferResult.FILE_NOT_FOUND -> R.string.uploads_view_upload_status_failed_localfile_error + TransferResult.FILE_ERROR -> R.string.uploads_view_upload_status_failed_file_error + TransferResult.PRIVILEGES_ERROR -> R.string.uploads_view_upload_status_failed_permission_error + TransferResult.NETWORK_CONNECTION -> R.string.uploads_view_upload_status_failed_connection_error + TransferResult.DELAYED_FOR_WIFI -> R.string.uploads_view_upload_status_waiting_for_wifi + TransferResult.CONFLICT_ERROR -> R.string.uploads_view_upload_status_conflict + TransferResult.SERVICE_INTERRUPTED -> R.string.uploads_view_upload_status_service_interrupted + TransferResult.SERVICE_UNAVAILABLE -> R.string.service_unavailable + TransferResult.QUOTA_EXCEEDED -> R.string.failed_upload_quota_exceeded_text + TransferResult.SSL_RECOVERABLE_PEER_UNVERIFIED -> R.string.ssl_certificate_not_trusted + TransferResult.UNKNOWN -> R.string.uploads_view_upload_status_unknown_fail + // Should not get here; cancelled uploads should be wiped out + TransferResult.CANCELLED -> R.string.uploads_view_upload_status_cancelled + // Should not get here; status should be UPLOAD_SUCCESS + TransferResult.UPLOADED -> R.string.uploads_view_upload_status_succeeded + // We don't know the specific forbidden error message because it is not being saved in transfers storage + TransferResult.SPECIFIC_FORBIDDEN -> R.string.uploader_upload_forbidden + // We don't know the specific unavailable service error message because it is not being saved in transfers storage + TransferResult.SPECIFIC_SERVICE_UNAVAILABLE -> R.string.service_unavailable + // We don't know the specific unsupported media type error message because it is not being saved in transfers storage + TransferResult.SPECIFIC_UNSUPPORTED_MEDIA_TYPE -> R.string.uploads_view_unsupported_media_type + // Should not get here; status should be not null + null -> R.string.uploads_view_upload_status_unknown_fail + } + } +} diff --git a/owncloudApp/src/main/java/com/owncloud/android/extensions/WorkManagerExt.kt b/owncloudApp/src/main/java/com/owncloud/android/extensions/WorkManagerExt.kt index 933dff1610f..6e1657484ed 100644 --- a/owncloudApp/src/main/java/com/owncloud/android/extensions/WorkManagerExt.kt +++ b/owncloudApp/src/main/java/com/owncloud/android/extensions/WorkManagerExt.kt @@ -2,7 +2,9 @@ * ownCloud Android client application * * @author Abel García de Prada - * Copyright (C) 2021 ownCloud GmbH. + * @author Juan Carlos Garrote Gascón + * + * Copyright (C) 2022 ownCloud GmbH. * * This program is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License version 2, @@ -16,9 +18,11 @@ * You should have received a copy of the GNU General Public License * along with this program. If not, see . */ + package com.owncloud.android.extensions import android.accounts.Account +import androidx.lifecycle.LiveData import androidx.work.WorkInfo import androidx.work.WorkManager import androidx.work.WorkQuery @@ -35,6 +39,13 @@ val FINISHED_WORK_STATUS = listOf(WorkInfo.State.SUCCEEDED, WorkInfo.State.FAILE fun WorkManager.getWorkInfoByTags(tags: List): List = this.getWorkInfos(buildWorkQuery(tags = tags)).get().filter { it.tags.containsAll(tags) } +/** + * Get a list of WorkInfo of running workers as LiveData that matches at least one of the tags. + */ +fun WorkManager.getRunningUploadsWorkInfosLiveData(tags: List): LiveData> { + return getWorkInfosLiveData(buildWorkQuery(tags = tags, states = listOf(WorkInfo.State.RUNNING))) +} + /** * Check if a download is pending. It could be enqueued, downloading or blocked. * @param account - Owner of the file diff --git a/owncloudApp/src/main/java/com/owncloud/android/operations/SynchronizeFileOperation.java b/owncloudApp/src/main/java/com/owncloud/android/operations/SynchronizeFileOperation.java index da006f6a0cc..400b9672b00 100644 --- a/owncloudApp/src/main/java/com/owncloud/android/operations/SynchronizeFileOperation.java +++ b/owncloudApp/src/main/java/com/owncloud/android/operations/SynchronizeFileOperation.java @@ -27,10 +27,6 @@ import android.accounts.Account; import android.content.Context; -import androidx.work.WorkManager; -import com.owncloud.android.data.OwncloudDatabase; -import com.owncloud.android.data.transfers.datasources.implementation.OCLocalTransferDataSource; -import com.owncloud.android.data.transfers.repository.OCTransferRepository; import com.owncloud.android.domain.files.model.OCFile; import com.owncloud.android.lib.common.OwnCloudClient; import com.owncloud.android.lib.common.operations.RemoteOperationResult; @@ -231,10 +227,8 @@ protected RemoteOperationResult run(OwnCloudClient client) { * @param file OCFile object representing the file to upload */ private void requestForUpload(OCFile file) { - WorkManager workManager = WorkManager.getInstance(mContext); - // Workaround... should be removed as soon as possible - OCTransferRepository transferRepository = new OCTransferRepository(new OCLocalTransferDataSource(OwncloudDatabase.Companion.getDatabase(mContext).transferDao())); - UploadFileInConflictUseCase uploadFileInConflictUseCase = new UploadFileInConflictUseCase(workManager, transferRepository); + @NotNull Lazy uploadFileInConflictUseCaseLazy = inject(UploadFileInConflictUseCase.class); + UploadFileInConflictUseCase uploadFileInConflictUseCase = uploadFileInConflictUseCaseLazy.getValue(); UploadFileInConflictUseCase.Params params = new UploadFileInConflictUseCase.Params( file.getOwner(), file.getStoragePath(), diff --git a/owncloudApp/src/main/java/com/owncloud/android/presentation/adapters/transfers/TransfersAdapter.kt b/owncloudApp/src/main/java/com/owncloud/android/presentation/adapters/transfers/TransfersAdapter.kt new file mode 100644 index 00000000000..613e9288699 --- /dev/null +++ b/owncloudApp/src/main/java/com/owncloud/android/presentation/adapters/transfers/TransfersAdapter.kt @@ -0,0 +1,278 @@ +/** + * ownCloud Android client application + * + * @author Juan Carlos Garrote Gascón + * + * Copyright (C) 2022 ownCloud GmbH. + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2, + * as published by the Free Software Foundation. + * + * 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 com.owncloud.android.presentation.adapters.transfers + +import android.text.format.DateUtils +import android.view.LayoutInflater +import android.view.View +import android.view.ViewGroup +import androidx.core.view.isVisible +import androidx.recyclerview.widget.DiffUtil +import androidx.recyclerview.widget.RecyclerView +import androidx.work.WorkInfo +import com.bumptech.glide.Glide +import com.bumptech.glide.load.engine.DiskCacheStrategy +import com.owncloud.android.R +import com.owncloud.android.authentication.AccountUtils +import com.owncloud.android.databinding.UploadListGroupBinding +import com.owncloud.android.databinding.UploadListItemBinding +import com.owncloud.android.domain.transfers.model.OCTransfer +import com.owncloud.android.domain.transfers.model.TransferStatus +import com.owncloud.android.extensions.statusToStringRes +import com.owncloud.android.lib.common.OwnCloudAccount +import com.owncloud.android.presentation.adapters.transfers.TransfersAdapter.TransferRecyclerItem.* +import com.owncloud.android.presentation.diffutils.TransfersDiffUtil +import com.owncloud.android.utils.DisplayUtils +import com.owncloud.android.utils.MimetypeIconUtil +import com.owncloud.android.utils.PreferenceUtils +import com.owncloud.android.workers.DownloadFileWorker +import timber.log.Timber +import java.io.File + +class TransfersAdapter( + val cancel: (Long) -> Unit, + val delete: (Long) -> Unit, + val retry: (OCTransfer) -> Unit, + val clearFailed: () -> Unit, + val retryFailed: () -> Unit, + val clearSuccessful: () -> Unit, +) : RecyclerView.Adapter() { + + private val transferItemsList = mutableListOf() + + override fun onCreateViewHolder(parent: ViewGroup, viewType: Int): RecyclerView.ViewHolder { + val inflater = LayoutInflater.from(parent.context) + return if (viewType == TransferRecyclerItemViewType.ITEM_VIEW_TRANSFER.ordinal) { + val view = inflater.inflate(R.layout.upload_list_item, parent, false) + view.filterTouchesWhenObscured = PreferenceUtils.shouldDisallowTouchesWithOtherVisibleWindows(parent.context) + TransferItemViewHolder(view) + } else { + val view = inflater.inflate(R.layout.upload_list_group, parent, false) + view.filterTouchesWhenObscured = PreferenceUtils.shouldDisallowTouchesWithOtherVisibleWindows(parent.context) + HeaderItemViewHolder(view) + } + + } + + override fun onBindViewHolder(holder: RecyclerView.ViewHolder, position: Int) { + when (holder) { + is TransferItemViewHolder -> { + val transferItem = getItem(position) as TransferItem + holder.binding.apply { + val remoteFile = File(transferItem.transfer.remotePath) + + var fileName = remoteFile.name + if (fileName.isEmpty()) { + fileName = File.separator + } + uploadName.text = fileName + + uploadRemotePath.text = holder.itemView.context.getString(R.string.app_name) + remoteFile.parent + + uploadFileSize.text = DisplayUtils.bytesToHumanReadable(transferItem.transfer.fileSize, holder.itemView.context) + + uploadDate.isVisible = + transferItem.transfer.transferEndTimestamp != null && transferItem.transfer.status != TransferStatus.TRANSFER_FAILED + transferItem.transfer.transferEndTimestamp?.let { + val dateString = DisplayUtils.getRelativeDateTimeString( + holder.itemView.context, + it, + DateUtils.SECOND_IN_MILLIS, + DateUtils.WEEK_IN_MILLIS, + 0 + ) + uploadDate.text = ", $dateString" + } + + try { + val account = AccountUtils.getOwnCloudAccountByName(holder.itemView.context, transferItem.transfer.accountName) + val oca = OwnCloudAccount(account, holder.itemView.context) + val accountName = oca.displayName + " @ " + + DisplayUtils.convertIdn(account.name.substring(account.name.lastIndexOf("@") + 1), false) + uploadAccount.text = accountName + } catch (e: Exception) { + Timber.w("Couldn't get display name for account, using old style") + uploadAccount.text = transferItem.transfer.accountName + } + + uploadStatus.isVisible = transferItem.transfer.status != TransferStatus.TRANSFER_SUCCEEDED + uploadStatus.text = " — " + holder.itemView.context.getString(transferItem.transfer.statusToStringRes()) + + Glide.with(holder.itemView) + .load(transferItem.transfer.localPath) + .diskCacheStrategy(DiskCacheStrategy.ALL) + .placeholder(MimetypeIconUtil.getFileTypeIconId(MimetypeIconUtil.getBestMimeTypeByFilename(transferItem.transfer.localPath), fileName)) + .into(thumbnail) + + uploadRightButton.isVisible = transferItem.transfer.status != TransferStatus.TRANSFER_SUCCEEDED + + uploadProgressBar.isVisible = transferItem.transfer.status == TransferStatus.TRANSFER_IN_PROGRESS + + holder.itemView.setOnClickListener(null) + + when (transferItem.transfer.status) { + TransferStatus.TRANSFER_IN_PROGRESS, TransferStatus.TRANSFER_QUEUED -> { + uploadRightButton.apply { + setImageResource(R.drawable.ic_action_cancel_grey) + setOnClickListener { + cancel(transferItem.transfer.id!!) + } + } + } + TransferStatus.TRANSFER_FAILED -> { + uploadRightButton.apply { + setImageResource(R.drawable.ic_action_delete_grey) + setOnClickListener { + delete(transferItem.transfer.id!!) + } + } + holder.itemView.setOnClickListener { + retry(transferItem.transfer) + } + holder.binding.ListItemLayout.isClickable = true + holder.binding.ListItemLayout.isFocusable = true + } + TransferStatus.TRANSFER_SUCCEEDED -> { + // Nothing to do + } + } + } + } + is HeaderItemViewHolder -> { + val headerItem = getItem(position) as HeaderItem + holder.binding.apply { + uploadListGroupName.text = holder.itemView.context.getString(headerTitleStringRes(headerItem.status)) + + val stringResFileCount = + if (headerItem.numberTransfers == 1) R.string.uploads_view_group_file_count_single else R.string.uploads_view_group_file_count + val fileCountText: String = String.format(holder.itemView.context.getString(stringResFileCount), headerItem.numberTransfers) + textViewFileCount.text = fileCountText + + uploadListGroupButtonClear.isVisible = headerItem.status == TransferStatus.TRANSFER_FAILED || + headerItem.status == TransferStatus.TRANSFER_SUCCEEDED + uploadListGroupButtonRetry.isVisible = headerItem.status == TransferStatus.TRANSFER_FAILED + + when (headerItem.status) { + TransferStatus.TRANSFER_FAILED -> { + uploadListGroupButtonClear.setOnClickListener { + clearFailed() + } + uploadListGroupButtonRetry.setOnClickListener { + retryFailed() + } + } + TransferStatus.TRANSFER_SUCCEEDED -> { + uploadListGroupButtonClear.setOnClickListener { + clearSuccessful() + } + } + TransferStatus.TRANSFER_QUEUED, TransferStatus.TRANSFER_IN_PROGRESS -> { + // Nothing to do + } + } + } + } + } + + } + + override fun onBindViewHolder(holder: RecyclerView.ViewHolder, position: Int, payloads: MutableList) { + if (payloads.isEmpty()) { + super.onBindViewHolder(holder, position, payloads) + } else { + (holder as TransferItemViewHolder).binding.apply { + if (payloads[0] is Int) { + uploadProgressBar.progress = payloads[0] as Int + } + } + } + + } + + private fun headerTitleStringRes(status: TransferStatus): Int { + return when (status) { + TransferStatus.TRANSFER_IN_PROGRESS -> R.string.uploads_view_group_current_uploads + TransferStatus.TRANSFER_FAILED -> R.string.uploads_view_group_failed_uploads + TransferStatus.TRANSFER_SUCCEEDED -> R.string.uploads_view_group_finished_uploads + TransferStatus.TRANSFER_QUEUED -> R.string.uploads_view_group_queued_uploads + } + } + + fun setData(transfers: List) { + val transfersGroupedByStatus = transfers.groupBy { it.status } + val newTransferItemsList = mutableListOf() + transfersGroupedByStatus.forEach { transferMap -> + val headerItem = HeaderItem(transferMap.key, transferMap.value.size) + newTransferItemsList.add(headerItem) + val transferItems = transferMap.value.sortedByDescending { it.transferEndTimestamp ?: it.id }.map(::TransferItem) + newTransferItemsList.addAll(transferItems) + } + val diffCallback = TransfersDiffUtil(transferItemsList, newTransferItemsList) + val diffResult = DiffUtil.calculateDiff(diffCallback) + transferItemsList.clear() + transferItemsList.addAll(newTransferItemsList) + diffResult.dispatchUpdatesTo(this) + } + + fun updateTransferProgress(workInfo: WorkInfo) { + var updated = false + var index = 0 + while (!updated && index < transferItemsList.size) { + val item = transferItemsList[index] + if (item is TransferItem && workInfo.tags.contains(item.transfer.id.toString())) { + notifyItemChanged(index, workInfo.progress.getInt(DownloadFileWorker.WORKER_KEY_PROGRESS, -1)) + updated = true + } + index += 1 + } + } + + override fun getItemCount(): Int = transferItemsList.size + + override fun getItemViewType(position: Int): Int { + return when (getItem(position)) { + is TransferItem -> TransferRecyclerItemViewType.ITEM_VIEW_TRANSFER.ordinal + is HeaderItem -> TransferRecyclerItemViewType.ITEM_VIEW_HEADER.ordinal + } + } + + fun getItem(position: Int) = transferItemsList[position] + + sealed class TransferRecyclerItem { + data class TransferItem(val transfer: OCTransfer) : TransferRecyclerItem() + data class HeaderItem( + val status: TransferStatus, + val numberTransfers: Int, + ) : TransferRecyclerItem() + } + + class TransferItemViewHolder(itemView: View) : RecyclerView.ViewHolder(itemView) { + val binding = UploadListItemBinding.bind(itemView) + } + + class HeaderItemViewHolder(itemView: View) : RecyclerView.ViewHolder(itemView) { + val binding = UploadListGroupBinding.bind(itemView) + } + + enum class TransferRecyclerItemViewType { + ITEM_VIEW_TRANSFER, ITEM_VIEW_HEADER + } +} diff --git a/owncloudApp/src/main/java/com/owncloud/android/presentation/diffutils/TransfersDiffUtil.kt b/owncloudApp/src/main/java/com/owncloud/android/presentation/diffutils/TransfersDiffUtil.kt new file mode 100644 index 00000000000..00b2814a635 --- /dev/null +++ b/owncloudApp/src/main/java/com/owncloud/android/presentation/diffutils/TransfersDiffUtil.kt @@ -0,0 +1,52 @@ +/** + * ownCloud Android client application + * + * @author Juan Carlos Garrote Gascón + * + * Copyright (C) 2022 ownCloud GmbH. + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2, + * as published by the Free Software Foundation. + * + * 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 com.owncloud.android.presentation.diffutils + +import androidx.recyclerview.widget.DiffUtil +import com.owncloud.android.presentation.adapters.transfers.TransfersAdapter + +class TransfersDiffUtil( + private val oldList: List, + private val newList: List +) : DiffUtil.Callback() { + override fun getOldListSize(): Int = oldList.size + + override fun getNewListSize(): Int = newList.size + + override fun areItemsTheSame(oldItemPosition: Int, newItemPosition: Int): Boolean { + val oldItem = oldList[oldItemPosition] + val newItem = newList[newItemPosition] + + if (oldItem is TransfersAdapter.TransferRecyclerItem.TransferItem && newItem is TransfersAdapter.TransferRecyclerItem.TransferItem) { + return oldItem.transfer.id == newItem.transfer.id + } + + if (oldItem is TransfersAdapter.TransferRecyclerItem.HeaderItem && newItem is TransfersAdapter.TransferRecyclerItem.HeaderItem) { + return oldItem.status == newItem.status + } + + return false + } + + override fun areContentsTheSame(oldItemPosition: Int, newItemPosition: Int): Boolean = + oldList[oldItemPosition] === newList[newItemPosition] + +} diff --git a/owncloudApp/src/main/java/com/owncloud/android/presentation/ui/transfers/TransferListFragment.kt b/owncloudApp/src/main/java/com/owncloud/android/presentation/ui/transfers/TransferListFragment.kt new file mode 100644 index 00000000000..c20f04d01cf --- /dev/null +++ b/owncloudApp/src/main/java/com/owncloud/android/presentation/ui/transfers/TransferListFragment.kt @@ -0,0 +1,130 @@ +/** + * ownCloud Android client application + * + * @author Juan Carlos Garrote Gascón + * + * Copyright (C) 2022 ownCloud GmbH. + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2, + * as published by the Free Software Foundation. + * + * 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 com.owncloud.android.presentation.ui.transfers + +import android.net.Uri +import android.os.Bundle +import android.view.LayoutInflater +import android.view.View +import android.view.ViewGroup +import androidx.core.view.isVisible +import androidx.documentfile.provider.DocumentFile +import androidx.fragment.app.Fragment +import androidx.recyclerview.widget.DefaultItemAnimator +import androidx.recyclerview.widget.DividerItemDecoration +import androidx.recyclerview.widget.LinearLayoutManager +import com.google.android.material.snackbar.Snackbar +import com.owncloud.android.R +import com.owncloud.android.authentication.AccountUtils +import com.owncloud.android.databinding.FragmentTransferListBinding +import com.owncloud.android.domain.transfers.model.OCTransfer +import com.owncloud.android.domain.transfers.model.TransferResult +import com.owncloud.android.presentation.adapters.transfers.TransfersAdapter +import com.owncloud.android.presentation.viewmodels.transfers.TransfersViewModel +import com.owncloud.android.ui.activity.FileActivity +import org.koin.androidx.viewmodel.ext.android.viewModel +import java.io.File + +class TransferListFragment : Fragment() { + + private val transfersViewModel by viewModel() + + private var _binding: FragmentTransferListBinding? = null + val binding get() = _binding!! + + private lateinit var transfersAdapter: TransfersAdapter + + override fun onCreateView(inflater: LayoutInflater, container: ViewGroup?, savedInstanceState: Bundle?): View { + _binding = FragmentTransferListBinding.inflate(inflater, container, false) + return binding.root + } + + override fun onViewCreated(view: View, savedInstanceState: Bundle?) { + super.onViewCreated(view, savedInstanceState) + transfersAdapter = TransfersAdapter( + cancel = { id -> + transfersViewModel.cancelTransferWithId(id) + }, + delete = { id -> + transfersViewModel.deleteTransferWithId(id) + }, + retry = { transfer: OCTransfer -> + if (transfer.lastResult == TransferResult.CREDENTIAL_ERROR) { + val parentActivity = requireActivity() as FileActivity + val account = AccountUtils.getOwnCloudAccountByName(requireContext(), transfer.accountName) + parentActivity.fileOperationsHelper.checkCurrentCredentials(account) + } else { + val file = File(transfer.localPath) + if (file.exists()) { + transfersViewModel.retryUploadFromSystem(transfer.id!!) + } else if (DocumentFile.isDocumentUri(requireContext(), Uri.parse(transfer.localPath))) { + transfersViewModel.retryUploadFromContentUri(transfer.id!!) + } else { + Snackbar.make( + view, + getString(R.string.local_file_not_found_toast), + Snackbar.LENGTH_LONG + ).show() + } + } + }, + clearFailed = { + transfersViewModel.clearFailedTransfers() + }, + retryFailed = { + transfersViewModel.retryFailedTransfers() + }, + clearSuccessful = { + transfersViewModel.clearSuccessfulTransfers() + } + ) + binding.transfersRecyclerView.apply { + layoutManager = LinearLayoutManager(context) + itemAnimator = DefaultItemAnimator() + adapter = transfersAdapter + addItemDecoration(DividerItemDecoration(context, DividerItemDecoration.VERTICAL)) + } + + transfersViewModel.transfersListLiveData.observe(viewLifecycleOwner) { transfers -> + val recyclerViewState = binding.transfersRecyclerView.layoutManager?.onSaveInstanceState() + setData(transfers) + binding.transfersRecyclerView.layoutManager?.onRestoreInstanceState(recyclerViewState) + } + + transfersViewModel.workInfosListLiveData.observe(viewLifecycleOwner) { workInfos -> + workInfos.forEach { workInfo -> + transfersAdapter.updateTransferProgress(workInfo) + } + } + } + + override fun onDestroy() { + super.onDestroy() + _binding = null + } + + private fun setData(items: List) { + binding.transfersRecyclerView.isVisible = items.isNotEmpty() + binding.emptyListText.isVisible = items.isEmpty() + + transfersAdapter.setData(items) + } +} diff --git a/owncloudApp/src/main/java/com/owncloud/android/presentation/viewmodels/transfers/TransfersViewModel.kt b/owncloudApp/src/main/java/com/owncloud/android/presentation/viewmodels/transfers/TransfersViewModel.kt index b6366e611c0..5d652b513fb 100644 --- a/owncloudApp/src/main/java/com/owncloud/android/presentation/viewmodels/transfers/TransfersViewModel.kt +++ b/owncloudApp/src/main/java/com/owncloud/android/presentation/viewmodels/transfers/TransfersViewModel.kt @@ -21,26 +21,70 @@ package com.owncloud.android.presentation.viewmodels.transfers import android.net.Uri +import androidx.lifecycle.LiveData +import androidx.lifecycle.MediatorLiveData import androidx.lifecycle.ViewModel import androidx.lifecycle.viewModelScope +import androidx.work.WorkInfo +import com.owncloud.android.domain.transfers.model.OCTransfer +import com.owncloud.android.domain.transfers.usecases.ClearFailedTransfersUseCase +import com.owncloud.android.domain.transfers.usecases.ClearSuccessfulTransfersUseCase +import com.owncloud.android.domain.transfers.usecases.DeleteTransferWithIdUseCase +import com.owncloud.android.domain.transfers.usecases.GetAllTransfersAsLiveDataUseCase import com.owncloud.android.providers.CoroutinesDispatcherProvider -import com.owncloud.android.usecases.transfers.uploads.UploadFilesFromSAFUseCase +import com.owncloud.android.providers.WorkManagerProvider +import com.owncloud.android.usecases.transfers.uploads.CancelUploadWithIdUseCase +import com.owncloud.android.usecases.transfers.uploads.RetryFailedUploadsUseCase +import com.owncloud.android.usecases.transfers.uploads.RetryUploadFromContentUriUseCase +import com.owncloud.android.usecases.transfers.uploads.RetryUploadFromSystemUseCase +import com.owncloud.android.usecases.transfers.uploads.UploadFilesFromContentUriUseCase import com.owncloud.android.usecases.transfers.uploads.UploadFilesFromSystemUseCase import kotlinx.coroutines.launch class TransfersViewModel( - private val uploadFilesFromSAFUseCase: UploadFilesFromSAFUseCase, + private val uploadFilesFromContentUriUseCase: UploadFilesFromContentUriUseCase, private val uploadFilesFromSystemUseCase: UploadFilesFromSystemUseCase, + private val cancelUploadWithIdUseCase: CancelUploadWithIdUseCase, + private val deleteTransferWithIdUseCase: DeleteTransferWithIdUseCase, + private val retryUploadFromSystemUseCase: RetryUploadFromSystemUseCase, + private val retryUploadFromContentUriUseCase: RetryUploadFromContentUriUseCase, + private val clearFailedTransfersUseCase: ClearFailedTransfersUseCase, + private val retryFailedUploadsUseCase: RetryFailedUploadsUseCase, + private val clearSuccessfulTransfersUseCase: ClearSuccessfulTransfersUseCase, + getAllTransfersAsLiveDataUseCase: GetAllTransfersAsLiveDataUseCase, private val coroutinesDispatcherProvider: CoroutinesDispatcherProvider, + workManagerProvider: WorkManagerProvider, ) : ViewModel() { - fun uploadFilesFromSAF( + + private val _transfersListLiveData = MediatorLiveData>() + val transfersListLiveData: LiveData> + get() = _transfersListLiveData + + private val _workInfosListLiveData = MediatorLiveData>() + val workInfosListLiveData: LiveData> + get() = _workInfosListLiveData + + private var transfersLiveData = getAllTransfersAsLiveDataUseCase.execute(Unit) + + private var workInfosLiveData = workManagerProvider.getRunningUploadsWorkInfosLiveData() + + init { + _transfersListLiveData.addSource(transfersLiveData) { transfers -> + _transfersListLiveData.postValue(transfers) + } + _workInfosListLiveData.addSource(workInfosLiveData) { workInfos -> + _workInfosListLiveData.postValue(workInfos) + } + } + + fun uploadFilesFromContentUri( accountName: String, listOfContentUris: List, uploadFolderPath: String ) { viewModelScope.launch(coroutinesDispatcherProvider.io) { - uploadFilesFromSAFUseCase.execute( - UploadFilesFromSAFUseCase.Params( + uploadFilesFromContentUriUseCase.execute( + UploadFilesFromContentUriUseCase.Params( accountName = accountName, listOfContentUris = listOfContentUris, uploadFolderPath = uploadFolderPath @@ -64,4 +108,54 @@ class TransfersViewModel( ) } } + + fun cancelTransferWithId(id: Long) { + viewModelScope.launch(coroutinesDispatcherProvider.io) { + cancelUploadWithIdUseCase.execute( + CancelUploadWithIdUseCase.Params(uploadId = id) + ) + } + } + + fun deleteTransferWithId(id: Long) { + viewModelScope.launch(coroutinesDispatcherProvider.io) { + deleteTransferWithIdUseCase.execute( + DeleteTransferWithIdUseCase.Params(id = id) + ) + } + } + + fun retryUploadFromSystem(id: Long) { + viewModelScope.launch(coroutinesDispatcherProvider.io) { + retryUploadFromSystemUseCase.execute( + RetryUploadFromSystemUseCase.Params(uploadIdInStorageManager = id) + ) + } + } + + fun retryUploadFromContentUri(id: Long) { + viewModelScope.launch(coroutinesDispatcherProvider.io) { + retryUploadFromContentUriUseCase.execute( + RetryUploadFromContentUriUseCase.Params(uploadIdInStorageManager = id) + ) + } + } + + fun clearFailedTransfers() { + viewModelScope.launch(coroutinesDispatcherProvider.io) { + clearFailedTransfersUseCase.execute(Unit) + } + } + + fun retryFailedTransfers() { + viewModelScope.launch(coroutinesDispatcherProvider.io) { + retryFailedUploadsUseCase.execute(Unit) + } + } + + fun clearSuccessfulTransfers() { + viewModelScope.launch(coroutinesDispatcherProvider.io) { + clearSuccessfulTransfersUseCase.execute(Unit) + } + } } diff --git a/owncloudApp/src/main/java/com/owncloud/android/providers/WorkManagerProvider.kt b/owncloudApp/src/main/java/com/owncloud/android/providers/WorkManagerProvider.kt index 70ba84bce55..26c5ee80bf9 100644 --- a/owncloudApp/src/main/java/com/owncloud/android/providers/WorkManagerProvider.kt +++ b/owncloudApp/src/main/java/com/owncloud/android/providers/WorkManagerProvider.kt @@ -20,15 +20,20 @@ package com.owncloud.android.providers import android.content.Context +import androidx.lifecycle.LiveData import androidx.work.Constraints import androidx.work.ExistingPeriodicWorkPolicy import androidx.work.NetworkType import androidx.work.PeriodicWorkRequestBuilder +import androidx.work.WorkInfo import androidx.work.WorkManager +import com.owncloud.android.extensions.getRunningUploadsWorkInfosLiveData import com.owncloud.android.workers.AvailableOfflinePeriodicWorker import com.owncloud.android.workers.AvailableOfflinePeriodicWorker.Companion.AVAILABLE_OFFLINE_PERIODIC_WORKER import com.owncloud.android.workers.CameraUploadsWorker import com.owncloud.android.workers.OldLogsCollectorWorker +import com.owncloud.android.workers.UploadFileFromContentUriWorker +import com.owncloud.android.workers.UploadFileFromFileSystemWorker class WorkManagerProvider( val context: Context @@ -76,4 +81,13 @@ class WorkManagerProvider( WorkManager.getInstance(context) .enqueueUniquePeriodicWork(AVAILABLE_OFFLINE_PERIODIC_WORKER, ExistingPeriodicWorkPolicy.KEEP, availableOfflinePeriodicWorker) } + + fun getRunningUploadsWorkInfosLiveData(): LiveData> { + return WorkManager.getInstance(context).getRunningUploadsWorkInfosLiveData( + listOf( + UploadFileFromContentUriWorker::class.java.name, + UploadFileFromFileSystemWorker::class.java.name + ) + ) + } } diff --git a/owncloudApp/src/main/java/com/owncloud/android/ui/activity/ConflictsResolveActivity.java b/owncloudApp/src/main/java/com/owncloud/android/ui/activity/ConflictsResolveActivity.java index 2a17bdc14bf..91421513e85 100644 --- a/owncloudApp/src/main/java/com/owncloud/android/ui/activity/ConflictsResolveActivity.java +++ b/owncloudApp/src/main/java/com/owncloud/android/ui/activity/ConflictsResolveActivity.java @@ -24,9 +24,6 @@ import android.os.Bundle; import androidx.work.WorkManager; -import com.owncloud.android.data.OwncloudDatabase; -import com.owncloud.android.data.transfers.datasources.implementation.OCLocalTransferDataSource; -import com.owncloud.android.data.transfers.repository.OCTransferRepository; import com.owncloud.android.domain.files.model.OCFile; import com.owncloud.android.ui.dialog.ConflictsResolveDialog; import com.owncloud.android.ui.dialog.ConflictsResolveDialog.Decision; @@ -46,7 +43,7 @@ * Wrapper activity which will be launched if keep-in-sync file will be modified by external * application. */ -public class ConflictsResolveActivity extends FileActivity implements OnConflictDecisionMadeListener { + public class ConflictsResolveActivity extends FileActivity implements OnConflictDecisionMadeListener { @Override protected void onCreate(Bundle savedInstanceState) { @@ -82,9 +79,8 @@ public void conflictDecisionMade(Decision decision) { WorkManager workManager = WorkManager.getInstance(getApplicationContext()); if (forceOverwrite) { - // Workaround... should be removed as soon as possible - OCTransferRepository transferRepository = new OCTransferRepository(new OCLocalTransferDataSource(OwncloudDatabase.Companion.getDatabase(this).transferDao())); - UploadFileInConflictUseCase uploadFileInConflictUseCase = new UploadFileInConflictUseCase(workManager, transferRepository); + @NotNull Lazy uploadFileInConflictUseCaseLazy = inject(UploadFileInConflictUseCase.class); + UploadFileInConflictUseCase uploadFileInConflictUseCase = uploadFileInConflictUseCaseLazy.getValue(); UploadFileInConflictUseCase.Params params = new UploadFileInConflictUseCase.Params( getFile().getOwner(), getFile().getStoragePath(), @@ -92,9 +88,8 @@ public void conflictDecisionMade(Decision decision) { ); uploadFileInConflictUseCase.execute(params); } else { - // Workaround... should be removed as soon as possible - OCTransferRepository transferRepository = new OCTransferRepository(new OCLocalTransferDataSource(OwncloudDatabase.Companion.getDatabase(this).transferDao())); - UploadFilesFromSystemUseCase uploadFilesFromSystemUseCase = new UploadFilesFromSystemUseCase(workManager, transferRepository); + @NotNull Lazy uploadFilesFromSystemUseCaseLazy = inject(UploadFilesFromSystemUseCase.class); + UploadFilesFromSystemUseCase uploadFilesFromSystemUseCase = uploadFilesFromSystemUseCaseLazy.getValue(); ArrayList listOfPaths = new ArrayList<>(); listOfPaths.add(getFile().getStoragePath()); UploadFilesFromSystemUseCase.Params params = new UploadFilesFromSystemUseCase.Params( diff --git a/owncloudApp/src/main/java/com/owncloud/android/ui/activity/FileDisplayActivity.kt b/owncloudApp/src/main/java/com/owncloud/android/ui/activity/FileDisplayActivity.kt index 0a87c5802ac..b2cac2e16ae 100644 --- a/owncloudApp/src/main/java/com/owncloud/android/ui/activity/FileDisplayActivity.kt +++ b/owncloudApp/src/main/java/com/owncloud/android/ui/activity/FileDisplayActivity.kt @@ -91,7 +91,6 @@ import com.owncloud.android.ui.preview.PreviewVideoFragment import com.owncloud.android.usecases.synchronization.SynchronizeFileUseCase import com.owncloud.android.usecases.transfers.DOWNLOAD_FINISH_MESSAGE import com.owncloud.android.usecases.transfers.downloads.DownloadFileUseCase -import com.owncloud.android.usecases.transfers.uploads.UploadFilesFromSAFUseCase import com.owncloud.android.utils.Extras import com.owncloud.android.utils.PreferenceUtils import kotlinx.coroutines.CoroutineScope @@ -553,8 +552,6 @@ class FileDisplayActivity : FileActivity(), } private fun requestUploadOfContentFromApps(contentIntent: Intent?, resultCode: Int) { - val uploadFileUseCase by inject() - val streamsToUpload = ArrayList() if (contentIntent!!.clipData != null && contentIntent.clipData!!.itemCount > 0) { @@ -568,7 +565,7 @@ class FileDisplayActivity : FileActivity(), val currentDir = currentDir val remotePath = currentDir?.remotePath ?: OCFile.ROOT_PATH - transfersViewModel.uploadFilesFromSAF( + transfersViewModel.uploadFilesFromContentUri( accountName = account.name, listOfContentUris = streamsToUpload, uploadFolderPath = remotePath, diff --git a/owncloudApp/src/main/java/com/owncloud/android/ui/activity/ManageAccountsActivity.java b/owncloudApp/src/main/java/com/owncloud/android/ui/activity/ManageAccountsActivity.java index 726d4f11be6..c10af32f4bc 100644 --- a/owncloudApp/src/main/java/com/owncloud/android/ui/activity/ManageAccountsActivity.java +++ b/owncloudApp/src/main/java/com/owncloud/android/ui/activity/ManageAccountsActivity.java @@ -38,13 +38,9 @@ import androidx.core.content.ContextCompat; import androidx.core.graphics.drawable.DrawableCompat; -import androidx.work.WorkManager; import com.owncloud.android.MainApp; import com.owncloud.android.R; import com.owncloud.android.authentication.AccountUtils; -import com.owncloud.android.data.OwncloudDatabase; -import com.owncloud.android.data.transfers.datasources.implementation.OCLocalTransferDataSource; -import com.owncloud.android.data.transfers.repository.OCTransferRepository; import com.owncloud.android.datamodel.FileDataStorageManager; import com.owncloud.android.presentation.ui.authentication.AuthenticatorConstants; import com.owncloud.android.presentation.ui.authentication.LoginActivity; @@ -297,15 +293,12 @@ public void run(AccountManagerFuture future) { if (future != null && future.isDone()) { Account account = new Account(mAccountBeingRemoved, MainApp.Companion.getAccountType()); if (!AccountUtils.exists(account.name, MainApp.Companion.getAppContext())) { - // Workaround... should be removed as soon as possible - OCTransferRepository transferRepository = new OCTransferRepository(new OCLocalTransferDataSource(OwncloudDatabase.Companion.getDatabase(this).transferDao())); // Cancel transfers of the removed account - CancelUploadsFromAccountUseCase cancelUploadsFromAccountUseCase = - new CancelUploadsFromAccountUseCase(WorkManager.getInstance(getBaseContext()), transferRepository); + @NotNull Lazy cancelUploadsFromAccountUseCaseLazy = inject(CancelUploadsFromAccountUseCase.class); + @NotNull Lazy cancelDownloadsForAccountUseCaseLazy = inject(CancelDownloadsForAccountUseCase.class); + CancelUploadsFromAccountUseCase cancelUploadsFromAccountUseCase = cancelUploadsFromAccountUseCaseLazy.getValue(); cancelUploadsFromAccountUseCase.execute(new CancelUploadsFromAccountUseCase.Params(account.name)); - - CancelDownloadsForAccountUseCase cancelDownloadsForAccountUseCase = - new CancelDownloadsForAccountUseCase(WorkManager.getInstance(getBaseContext())); + CancelDownloadsForAccountUseCase cancelDownloadsForAccountUseCase = cancelDownloadsForAccountUseCaseLazy.getValue(); cancelDownloadsForAccountUseCase.execute(new CancelDownloadsForAccountUseCase.Params(account)); } diff --git a/owncloudApp/src/main/java/com/owncloud/android/ui/activity/UploadListActivity.java b/owncloudApp/src/main/java/com/owncloud/android/ui/activity/UploadListActivity.java index 3969893cd8c..625d50287e6 100755 --- a/owncloudApp/src/main/java/com/owncloud/android/ui/activity/UploadListActivity.java +++ b/owncloudApp/src/main/java/com/owncloud/android/ui/activity/UploadListActivity.java @@ -1,11 +1,13 @@ -/** +/* * ownCloud Android client application * * @author LukeOwncloud * @author David A. Velasco * @author masensio * @author Christian Schabesberger - * Copyright (C) 2020 ownCloud GmbH. + * @author Juan Carlos Garrote Gascón + * + * Copyright (C) 2022 ownCloud GmbH. *

* This program is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License version 2, @@ -19,6 +21,7 @@ * You should have received a copy of the GNU General Public License * along with this program. If not, see . */ + package com.owncloud.android.ui.activity; import android.accounts.Account; @@ -30,27 +33,25 @@ import android.view.View; import androidx.fragment.app.FragmentTransaction; -import androidx.work.WorkManager; import com.owncloud.android.R; import com.owncloud.android.authentication.AccountUtils; -import com.owncloud.android.data.OwncloudDatabase; -import com.owncloud.android.data.transfers.datasources.implementation.OCLocalTransferDataSource; -import com.owncloud.android.data.transfers.repository.OCTransferRepository; import com.owncloud.android.datamodel.OCUpload; import com.owncloud.android.datamodel.UploadsStorageManager; import com.owncloud.android.lib.common.operations.RemoteOperation; import com.owncloud.android.lib.common.operations.RemoteOperationResult; import com.owncloud.android.operations.CheckCurrentCredentialsOperation; +import com.owncloud.android.presentation.ui.transfers.TransferListFragment; import com.owncloud.android.ui.fragment.UploadListFragment; import com.owncloud.android.usecases.transfers.uploads.RetryFailedUploadsForAccountUseCase; -import com.owncloud.android.usecases.transfers.uploads.RetryUploadFromContentUriUseCase; -import com.owncloud.android.usecases.transfers.uploads.RetryUploadFromSystemUseCase; -import com.owncloud.android.usecases.transfers.uploads.UploadFilesFromSystemUseCase; import com.owncloud.android.utils.MimetypeIconUtil; +import kotlin.Lazy; +import org.jetbrains.annotations.NotNull; import timber.log.Timber; import java.io.File; +import static org.koin.java.KoinJavaComponent.inject; + /** * Activity listing pending, active, and completed uploads. User can delete * completed uploads from view. Content of this list of coming from @@ -90,7 +91,8 @@ protected void onCreate(Bundle savedInstanceState) { } private void createUploadListFragment() { - UploadListFragment uploadList = new UploadListFragment(); + //UploadListFragment uploadList = new UploadListFragment(); + TransferListFragment uploadList = new TransferListFragment(); FragmentTransaction transaction = getSupportFragmentManager().beginTransaction(); transaction.add(R.id.left_fragment_container, uploadList, TAG_UPLOAD_LIST_FRAGMENT); transaction.commit(); @@ -148,13 +150,8 @@ protected void onActivityResult(int requestCode, int resultCode, Intent data) { if (account == null) { return; } - // Workaround... should be removed as soon as possible - OCTransferRepository transferRepository = new OCTransferRepository(new OCLocalTransferDataSource(OwncloudDatabase.Companion.getDatabase(this).transferDao())); - RetryUploadFromContentUriUseCase retryUploadFromContentUriUseCase = new RetryUploadFromContentUriUseCase(this, transferRepository); - WorkManager workManager = WorkManager.getInstance(this); - UploadFilesFromSystemUseCase uploadFilesFromSystemUseCase = new UploadFilesFromSystemUseCase(workManager, transferRepository); - RetryUploadFromSystemUseCase retryUploadFromSystemUseCase = new RetryUploadFromSystemUseCase(uploadFilesFromSystemUseCase, transferRepository); - RetryFailedUploadsForAccountUseCase retryFailedUploadsForAccountUseCase = new RetryFailedUploadsForAccountUseCase(this, retryUploadFromContentUriUseCase, retryUploadFromSystemUseCase, transferRepository); + @NotNull Lazy retryFailedUploadsForAccountUseCaseLazy = inject(RetryFailedUploadsForAccountUseCase.class); + RetryFailedUploadsForAccountUseCase retryFailedUploadsForAccountUseCase = retryFailedUploadsForAccountUseCaseLazy.getValue(); retryFailedUploadsForAccountUseCase.execute(new RetryFailedUploadsForAccountUseCase.Params(account.name)); } } @@ -176,13 +173,8 @@ public void onRemoteOperationFinish(RemoteOperation operation, RemoteOperationRe } else { // already updated -> just retry! - // Workaround... should be removed as soon as possible - OCTransferRepository transferRepository = new OCTransferRepository(new OCLocalTransferDataSource(OwncloudDatabase.Companion.getDatabase(this).transferDao())); - RetryUploadFromContentUriUseCase retryUploadFromContentUriUseCase = new RetryUploadFromContentUriUseCase(this, transferRepository); - WorkManager workManager = WorkManager.getInstance(this); - UploadFilesFromSystemUseCase uploadFilesFromSystemUseCase = new UploadFilesFromSystemUseCase(workManager, transferRepository); - RetryUploadFromSystemUseCase retryUploadFromSystemUseCase = new RetryUploadFromSystemUseCase(uploadFilesFromSystemUseCase, transferRepository); - RetryFailedUploadsForAccountUseCase retryFailedUploadsForAccountUseCase = new RetryFailedUploadsForAccountUseCase(this, retryUploadFromContentUriUseCase, retryUploadFromSystemUseCase, transferRepository); + @NotNull Lazy retryFailedUploadsForAccountUseCaseLazy = inject(RetryFailedUploadsForAccountUseCase.class); + RetryFailedUploadsForAccountUseCase retryFailedUploadsForAccountUseCase = retryFailedUploadsForAccountUseCaseLazy.getValue(); retryFailedUploadsForAccountUseCase.execute(new RetryFailedUploadsForAccountUseCase.Params(account.name)); } diff --git a/owncloudApp/src/main/java/com/owncloud/android/ui/adapter/ExpandableUploadListAdapter.java b/owncloudApp/src/main/java/com/owncloud/android/ui/adapter/ExpandableUploadListAdapter.java index cc0878f7449..821a0eed983 100755 --- a/owncloudApp/src/main/java/com/owncloud/android/ui/adapter/ExpandableUploadListAdapter.java +++ b/owncloudApp/src/main/java/com/owncloud/android/ui/adapter/ExpandableUploadListAdapter.java @@ -40,19 +40,16 @@ import androidx.appcompat.widget.AppCompatButton; import androidx.documentfile.provider.DocumentFile; -import androidx.work.WorkManager; import com.google.android.material.snackbar.Snackbar; import com.owncloud.android.R; import com.owncloud.android.authentication.AccountUtils; -import com.owncloud.android.data.OwncloudDatabase; -import com.owncloud.android.data.transfers.datasources.implementation.OCLocalTransferDataSource; -import com.owncloud.android.data.transfers.repository.OCTransferRepository; import com.owncloud.android.datamodel.OCUpload; import com.owncloud.android.datamodel.ThumbnailsCacheManager; import com.owncloud.android.datamodel.UploadsStorageManager; import com.owncloud.android.datamodel.UploadsStorageManager.UploadStatus; import com.owncloud.android.db.UploadResult; import com.owncloud.android.domain.files.model.OCFile; +import com.owncloud.android.domain.transfers.TransferRepository; import com.owncloud.android.lib.common.OwnCloudAccount; import com.owncloud.android.lib.common.network.OnDatatransferProgressListener; import com.owncloud.android.ui.activity.FileActivity; @@ -61,10 +58,11 @@ import com.owncloud.android.usecases.transfers.uploads.CancelUploadWithIdUseCase; import com.owncloud.android.usecases.transfers.uploads.RetryUploadFromContentUriUseCase; import com.owncloud.android.usecases.transfers.uploads.RetryUploadFromSystemUseCase; -import com.owncloud.android.usecases.transfers.uploads.UploadFilesFromSystemUseCase; import com.owncloud.android.utils.DisplayUtils; import com.owncloud.android.utils.MimetypeIconUtil; import com.owncloud.android.utils.PreferenceUtils; +import kotlin.Lazy; +import org.jetbrains.annotations.NotNull; import timber.log.Timber; import java.io.File; @@ -75,6 +73,7 @@ import java.util.Observer; import static com.owncloud.android.db.PreferenceManager.PREF__CAMERA_UPLOADS_DEFAULT_PATH; +import static org.koin.java.KoinJavaComponent.inject; /** * This Adapter populates a ListView with following types of uploads: pending, @@ -312,8 +311,8 @@ private View getView(OCUpload[] uploadsItems, int position, View convertView, Vi rightButton.setImageResource(R.drawable.ic_action_cancel_grey); rightButton.setVisibility(View.VISIBLE); rightButton.setOnClickListener(v -> { - OCTransferRepository transferRepository = new OCTransferRepository(new OCLocalTransferDataSource(OwncloudDatabase.Companion.getDatabase(v.getContext()).transferDao())); - CancelUploadWithIdUseCase cancelUploadWithIdUseCase = new CancelUploadWithIdUseCase(WorkManager.getInstance(parent.getContext()), transferRepository); + @NotNull Lazy cancelUploadWithIdUseCaseLazy = inject(CancelUploadWithIdUseCase.class); + CancelUploadWithIdUseCase cancelUploadWithIdUseCase = cancelUploadWithIdUseCaseLazy.getValue(); cancelUploadWithIdUseCase.execute(new CancelUploadWithIdUseCase.Params(upload.getUploadId())); refreshView(); }); @@ -323,7 +322,8 @@ private View getView(OCUpload[] uploadsItems, int position, View convertView, Vi rightButton.setImageResource(R.drawable.ic_action_delete_grey); rightButton.setVisibility(View.VISIBLE); rightButton.setOnClickListener(v -> { - OCTransferRepository transferRepository = new OCTransferRepository(new OCLocalTransferDataSource(OwncloudDatabase.Companion.getDatabase(v.getContext()).transferDao())); + @NotNull Lazy transferRepositoryLazy = inject(TransferRepository.class); + TransferRepository transferRepository = transferRepositoryLazy.getValue(); transferRepository.removeTransferById(upload.getUploadId()); refreshView(); }); @@ -346,18 +346,14 @@ private View getView(OCUpload[] uploadsItems, int position, View convertView, Vi public void onClick(View v) { File file = new File(upload.getLocalPath()); if (file.exists()) { - OCTransferRepository transferRepository = new OCTransferRepository(new OCLocalTransferDataSource(OwncloudDatabase.Companion.getDatabase(v.getContext()).transferDao())); - WorkManager workManager = WorkManager.getInstance(v.getContext()); - UploadFilesFromSystemUseCase uploadFilesFromSystemUseCase = new UploadFilesFromSystemUseCase(workManager, transferRepository); - RetryUploadFromSystemUseCase retryUploadFromSystemUseCase = new RetryUploadFromSystemUseCase(uploadFilesFromSystemUseCase, transferRepository); + @NotNull Lazy retryUploadFromSystemUseCaseLazy = inject(RetryUploadFromSystemUseCase.class); + RetryUploadFromSystemUseCase retryUploadFromSystemUseCase = retryUploadFromSystemUseCaseLazy.getValue(); RetryUploadFromSystemUseCase.Params useCaseParams = new RetryUploadFromSystemUseCase.Params(upload.getUploadId()); retryUploadFromSystemUseCase.execute(useCaseParams); refreshView(); } else if (DocumentFile.isDocumentUri(v.getContext(), Uri.parse(upload.getLocalPath()))) { - // Workaround... should be removed as soon as possible - OCTransferRepository transferRepository = new OCTransferRepository(new OCLocalTransferDataSource(OwncloudDatabase.Companion.getDatabase(v.getContext()).transferDao())); - RetryUploadFromContentUriUseCase retryUploadFromContentUriUseCase = - new RetryUploadFromContentUriUseCase(v.getContext(), transferRepository); + @NotNull Lazy retryUploadFromContentUriUseCaseLazy = inject(RetryUploadFromContentUriUseCase.class); + RetryUploadFromContentUriUseCase retryUploadFromContentUriUseCase = retryUploadFromContentUriUseCaseLazy.getValue(); RetryUploadFromContentUriUseCase.Params useCaseParams = new RetryUploadFromContentUriUseCase.Params( upload.getUploadId() ); diff --git a/owncloudApp/src/main/java/com/owncloud/android/ui/asynctasks/CopyAndUploadContentUrisTask.java b/owncloudApp/src/main/java/com/owncloud/android/ui/asynctasks/CopyAndUploadContentUrisTask.java index 12004270846..3cbbf35d161 100644 --- a/owncloudApp/src/main/java/com/owncloud/android/ui/asynctasks/CopyAndUploadContentUrisTask.java +++ b/owncloudApp/src/main/java/com/owncloud/android/ui/asynctasks/CopyAndUploadContentUrisTask.java @@ -28,16 +28,13 @@ import android.os.AsyncTask; import android.widget.Toast; -import androidx.work.WorkManager; import com.owncloud.android.R; -import com.owncloud.android.data.OwncloudDatabase; -import com.owncloud.android.data.transfers.datasources.implementation.OCLocalTransferDataSource; -import com.owncloud.android.data.transfers.db.TransferDao; -import com.owncloud.android.data.transfers.repository.OCTransferRepository; import com.owncloud.android.lib.common.operations.RemoteOperationResult.ResultCode; import com.owncloud.android.usecases.transfers.uploads.UploadFilesFromSystemUseCase; import com.owncloud.android.utils.FileStorageUtils; import com.owncloud.android.utils.UriUtils; +import kotlin.Lazy; +import org.jetbrains.annotations.NotNull; import timber.log.Timber; import java.io.File; @@ -47,6 +44,8 @@ import java.lang.ref.WeakReference; import java.util.ArrayList; +import static org.koin.java.KoinJavaComponent.inject; + /** * AsyncTask to copy a file from a uri in a temporal file */ @@ -162,10 +161,8 @@ protected ResultCode doInBackground(Object[] params) { } filesToUpload.add(fullTempPath); - WorkManager workManager = WorkManager.getInstance(mAppContext); - // Workaround... should be removed as soon as possible - OCTransferRepository transferRepository = new OCTransferRepository(new OCLocalTransferDataSource(OwncloudDatabase.Companion.getDatabase(mAppContext).transferDao())); - UploadFilesFromSystemUseCase uploadFilesFromSystemUseCase = new UploadFilesFromSystemUseCase(workManager, transferRepository); + @NotNull Lazy uploadFilesFromSystemUseCaseLazy = inject(UploadFilesFromSystemUseCase.class); + UploadFilesFromSystemUseCase uploadFilesFromSystemUseCase = uploadFilesFromSystemUseCaseLazy.getValue(); UploadFilesFromSystemUseCase.Params useCaseParams = new UploadFilesFromSystemUseCase.Params( account.name, filesToUpload, diff --git a/owncloudApp/src/main/java/com/owncloud/android/ui/fragment/UploadListFragment.java b/owncloudApp/src/main/java/com/owncloud/android/ui/fragment/UploadListFragment.java index 48b4ed47d5f..85f9159ceac 100755 --- a/owncloudApp/src/main/java/com/owncloud/android/ui/fragment/UploadListFragment.java +++ b/owncloudApp/src/main/java/com/owncloud/android/ui/fragment/UploadListFragment.java @@ -26,22 +26,19 @@ import android.widget.ExpandableListView; import android.widget.ListView; -import androidx.work.WorkManager; import com.owncloud.android.R; -import com.owncloud.android.data.OwncloudDatabase; -import com.owncloud.android.data.transfers.datasources.implementation.OCLocalTransferDataSource; -import com.owncloud.android.data.transfers.repository.OCTransferRepository; import com.owncloud.android.datamodel.OCUpload; import com.owncloud.android.datamodel.UploadsStorageManager; import com.owncloud.android.ui.activity.FileActivity; import com.owncloud.android.ui.adapter.ExpandableUploadListAdapter; import com.owncloud.android.usecases.transfers.uploads.RetryFailedUploadsUseCase; -import com.owncloud.android.usecases.transfers.uploads.RetryUploadFromContentUriUseCase; -import com.owncloud.android.usecases.transfers.uploads.RetryUploadFromSystemUseCase; -import com.owncloud.android.usecases.transfers.uploads.UploadFilesFromSystemUseCase; +import kotlin.Lazy; import kotlin.Unit; +import org.jetbrains.annotations.NotNull; import timber.log.Timber; +import static org.koin.java.KoinJavaComponent.inject; + /** * A Fragment that lists all files and folders in a given LOCAL path. * @@ -124,13 +121,8 @@ public void onClick(OptionsInUploadList option) { switch (option) { case RETRY_FAILED: - // Workaround... should be removed as soon as possible - OCTransferRepository transferRepository = new OCTransferRepository(new OCLocalTransferDataSource(OwncloudDatabase.Companion.getDatabase(requireContext()).transferDao())); - RetryUploadFromContentUriUseCase retryUploadFromContentUriUseCase = new RetryUploadFromContentUriUseCase(requireContext(), transferRepository); - WorkManager workManager = WorkManager.getInstance(requireContext()); - UploadFilesFromSystemUseCase uploadFilesFromSystemUseCase = new UploadFilesFromSystemUseCase(workManager, transferRepository); - RetryUploadFromSystemUseCase retryUploadFromSystemUseCase = new RetryUploadFromSystemUseCase(uploadFilesFromSystemUseCase, transferRepository); - RetryFailedUploadsUseCase retryFailedUploadsUseCase = new RetryFailedUploadsUseCase(requireContext(), retryUploadFromContentUriUseCase, retryUploadFromSystemUseCase, transferRepository); + @NotNull Lazy retryFailedUploadsUseCaseLazy = inject(RetryFailedUploadsUseCase.class); + RetryFailedUploadsUseCase retryFailedUploadsUseCase = retryFailedUploadsUseCaseLazy.getValue(); retryFailedUploadsUseCase.execute(Unit.INSTANCE); break; case CLEAR_FAILED: diff --git a/owncloudApp/src/main/java/com/owncloud/android/usecases/transfers/uploads/RetryUploadFromContentUriUseCase.kt b/owncloudApp/src/main/java/com/owncloud/android/usecases/transfers/uploads/RetryUploadFromContentUriUseCase.kt index 23a8f3b8f68..347f12734fd 100644 --- a/owncloudApp/src/main/java/com/owncloud/android/usecases/transfers/uploads/RetryUploadFromContentUriUseCase.kt +++ b/owncloudApp/src/main/java/com/owncloud/android/usecases/transfers/uploads/RetryUploadFromContentUriUseCase.kt @@ -21,15 +21,18 @@ package com.owncloud.android.usecases.transfers.uploads -import android.content.Context import androidx.core.net.toUri +import androidx.work.WorkInfo import androidx.work.WorkManager import com.owncloud.android.domain.BaseUseCase import com.owncloud.android.domain.camerauploads.model.UploadBehavior import com.owncloud.android.domain.transfers.TransferRepository +import com.owncloud.android.extensions.getWorkInfoByTags +import com.owncloud.android.workers.UploadFileFromContentUriWorker class RetryUploadFromContentUriUseCase( - private val context: Context, + private val workManager: WorkManager, + private val uploadFileFromContentUriUseCase: UploadFileFromContentUriUseCase, private val transferRepository: TransferRepository, ) : BaseUseCase() { @@ -38,19 +41,30 @@ class RetryUploadFromContentUriUseCase( uploadToRetry ?: return - val workManager = WorkManager.getInstance(context) - UploadFileFromContentUriUseCase(workManager).execute( - UploadFileFromContentUriUseCase.Params( - accountName = uploadToRetry.accountName, - contentUri = uploadToRetry.localPath.toUri(), - lastModifiedInSeconds = (uploadToRetry.transferEndTimestamp?.div(1000)).toString(), - behavior = UploadBehavior.fromLegacyLocalBehavior(uploadToRetry.localBehaviour).name, - uploadPath = uploadToRetry.remotePath, - uploadIdInStorageManager = uploadToRetry.id!!, - wifiOnly = false, - chargingOnly = false + transferRepository.updateTransferStatusToEnqueuedById(params.uploadIdInStorageManager) + + val workInfo = workManager.getWorkInfoByTags( + listOf( + params.uploadIdInStorageManager.toString(), + uploadToRetry.accountName, + UploadFileFromContentUriWorker::class.java.name ) ) + + if (workInfo.firstOrNull()?.state == WorkInfo.State.FAILED) { + uploadFileFromContentUriUseCase.execute( + UploadFileFromContentUriUseCase.Params( + accountName = uploadToRetry.accountName, + contentUri = uploadToRetry.localPath.toUri(), + lastModifiedInSeconds = (uploadToRetry.transferEndTimestamp?.div(1000)).toString(), + behavior = UploadBehavior.fromLegacyLocalBehavior(uploadToRetry.localBehaviour).name, + uploadPath = uploadToRetry.remotePath, + uploadIdInStorageManager = params.uploadIdInStorageManager, + wifiOnly = false, + chargingOnly = false + ) + ) + } } data class Params( diff --git a/owncloudApp/src/main/java/com/owncloud/android/usecases/transfers/uploads/RetryUploadFromSystemUseCase.kt b/owncloudApp/src/main/java/com/owncloud/android/usecases/transfers/uploads/RetryUploadFromSystemUseCase.kt index e1e898a0326..88328876c2f 100644 --- a/owncloudApp/src/main/java/com/owncloud/android/usecases/transfers/uploads/RetryUploadFromSystemUseCase.kt +++ b/owncloudApp/src/main/java/com/owncloud/android/usecases/transfers/uploads/RetryUploadFromSystemUseCase.kt @@ -21,12 +21,17 @@ package com.owncloud.android.usecases.transfers.uploads +import androidx.work.WorkInfo +import androidx.work.WorkManager import com.owncloud.android.domain.BaseUseCase -import com.owncloud.android.domain.files.model.OCFile.Companion.PATH_SEPARATOR +import com.owncloud.android.domain.camerauploads.model.UploadBehavior import com.owncloud.android.domain.transfers.TransferRepository +import com.owncloud.android.extensions.getWorkInfoByTags +import com.owncloud.android.workers.UploadFileFromFileSystemWorker class RetryUploadFromSystemUseCase( - private val uploadFilesFromSystemUseCase: UploadFilesFromSystemUseCase, + private val workManager: WorkManager, + private val uploadFileFromSystemUseCase: UploadFileFromSystemUseCase, private val transferRepository: TransferRepository, ) : BaseUseCase() { @@ -35,13 +40,28 @@ class RetryUploadFromSystemUseCase( uploadToRetry ?: return - uploadFilesFromSystemUseCase.execute( - UploadFilesFromSystemUseCase.Params( - accountName = uploadToRetry.accountName, - listOfLocalPaths = listOf(uploadToRetry.localPath), - uploadFolderPath = uploadToRetry.remotePath.trimEnd(PATH_SEPARATOR), + transferRepository.updateTransferStatusToEnqueuedById(params.uploadIdInStorageManager) + + val workInfo = workManager.getWorkInfoByTags( + listOf( + params.uploadIdInStorageManager.toString(), + uploadToRetry.accountName, + UploadFileFromFileSystemWorker::class.java.name ) ) + + if (workInfo.firstOrNull()?.state == WorkInfo.State.FAILED) { + uploadFileFromSystemUseCase.execute( + UploadFileFromSystemUseCase.Params( + accountName = uploadToRetry.accountName, + localPath = uploadToRetry.localPath, + lastModifiedInSeconds = (uploadToRetry.transferEndTimestamp?.div(1000)).toString(), + behavior = UploadBehavior.fromLegacyLocalBehavior(uploadToRetry.localBehaviour).name, + uploadPath = uploadToRetry.remotePath, + uploadIdInStorageManager = params.uploadIdInStorageManager + ) + ) + } } data class Params( diff --git a/owncloudApp/src/main/java/com/owncloud/android/usecases/transfers/uploads/UploadFileFromContentUriUseCase.kt b/owncloudApp/src/main/java/com/owncloud/android/usecases/transfers/uploads/UploadFileFromContentUriUseCase.kt index bedafea4d4b..9c2f39d0bc7 100644 --- a/owncloudApp/src/main/java/com/owncloud/android/usecases/transfers/uploads/UploadFileFromContentUriUseCase.kt +++ b/owncloudApp/src/main/java/com/owncloud/android/usecases/transfers/uploads/UploadFileFromContentUriUseCase.kt @@ -2,7 +2,9 @@ * ownCloud Android client application * * @author Abel García de Prada - * Copyright (C) 2021 ownCloud GmbH. + * @author Juan Carlos Garrote Gascón + * + * Copyright (C) 2022 ownCloud GmbH. *

* This program is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License version 2, @@ -16,6 +18,7 @@ * You should have received a copy of the GNU General Public License * along with this program. If not, see . */ + package com.owncloud.android.usecases.transfers.uploads import android.net.Uri @@ -33,19 +36,6 @@ class UploadFileFromContentUriUseCase( ) : BaseUseCase() { override fun run(params: Params) { - /** - * There will be different ways to upload files. - * - * PLAIN, CHUNKS(not implemented yet), TUS(not implemented yet) - * - * We will choose the way to upload the file depending on capabilities and file size. - * This check should be done in this use case. - * - */ - enqueuePlainUpload(params) - } - - private fun enqueuePlainUpload(params: Params) { val inputData = workDataOf( UploadFileFromContentUriWorker.KEY_PARAM_ACCOUNT_NAME to params.accountName, UploadFileFromContentUriWorker.KEY_PARAM_BEHAVIOR to params.behavior, diff --git a/owncloudApp/src/main/java/com/owncloud/android/usecases/transfers/uploads/UploadFileFromSystemUseCase.kt b/owncloudApp/src/main/java/com/owncloud/android/usecases/transfers/uploads/UploadFileFromSystemUseCase.kt new file mode 100644 index 00000000000..6560b44b713 --- /dev/null +++ b/owncloudApp/src/main/java/com/owncloud/android/usecases/transfers/uploads/UploadFileFromSystemUseCase.kt @@ -0,0 +1,69 @@ +/** + * ownCloud Android client application + * + * @author Juan Carlos Garrote Gascón + * + * Copyright (C) 2022 ownCloud GmbH. + *

+ * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2, + * as published by the Free Software Foundation. + *

+ * 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 com.owncloud.android.usecases.transfers.uploads + +import androidx.work.Constraints +import androidx.work.NetworkType +import androidx.work.OneTimeWorkRequestBuilder +import androidx.work.WorkManager +import androidx.work.workDataOf +import com.owncloud.android.domain.BaseUseCase +import com.owncloud.android.workers.UploadFileFromFileSystemWorker +import timber.log.Timber + +class UploadFileFromSystemUseCase( + private val workManager: WorkManager +) : BaseUseCase() { + + override fun run(params: Params) { + val inputData = workDataOf( + UploadFileFromFileSystemWorker.KEY_PARAM_ACCOUNT_NAME to params.accountName, + UploadFileFromFileSystemWorker.KEY_PARAM_BEHAVIOR to params.behavior, + UploadFileFromFileSystemWorker.KEY_PARAM_LOCAL_PATH to params.localPath, + UploadFileFromFileSystemWorker.KEY_PARAM_LAST_MODIFIED to params.lastModifiedInSeconds, + UploadFileFromFileSystemWorker.KEY_PARAM_UPLOAD_PATH to params.uploadPath, + UploadFileFromFileSystemWorker.KEY_PARAM_UPLOAD_ID to params.uploadIdInStorageManager + ) + + val constraints = Constraints.Builder() + .setRequiredNetworkType(NetworkType.CONNECTED) + .build() + + val uploadFileFromSystemWorker = OneTimeWorkRequestBuilder() + .setInputData(inputData) + .setConstraints(constraints) + .addTag(params.accountName) + .addTag(params.uploadIdInStorageManager.toString()) + .build() + + workManager.enqueue(uploadFileFromSystemWorker) + Timber.i("Plain upload of ${params.localPath} has been enqueued.") + } + + data class Params( + val accountName: String, + val localPath: String, + val lastModifiedInSeconds: String, + val behavior: String, + val uploadPath: String, + val uploadIdInStorageManager: Long, + ) +} diff --git a/owncloudApp/src/main/java/com/owncloud/android/usecases/transfers/uploads/UploadFileInConflictUseCase.kt b/owncloudApp/src/main/java/com/owncloud/android/usecases/transfers/uploads/UploadFileInConflictUseCase.kt index 2a4fc50f384..1503528270f 100644 --- a/owncloudApp/src/main/java/com/owncloud/android/usecases/transfers/uploads/UploadFileInConflictUseCase.kt +++ b/owncloudApp/src/main/java/com/owncloud/android/usecases/transfers/uploads/UploadFileInConflictUseCase.kt @@ -103,7 +103,7 @@ class UploadFileInConflictUseCase( val inputData = workDataOf( UploadFileFromFileSystemWorker.KEY_PARAM_ACCOUNT_NAME to accountName, UploadFileFromFileSystemWorker.KEY_PARAM_BEHAVIOR to UploadBehavior.COPY.name, - UploadFileFromFileSystemWorker.KEY_PARAM_CONTENT_URI to localPath, + UploadFileFromFileSystemWorker.KEY_PARAM_LOCAL_PATH to localPath, UploadFileFromFileSystemWorker.KEY_PARAM_LAST_MODIFIED to lastModifiedInSeconds, UploadFileFromFileSystemWorker.KEY_PARAM_UPLOAD_PATH to uploadPath, UploadFileFromFileSystemWorker.KEY_PARAM_UPLOAD_ID to uploadIdInStorageManager, diff --git a/owncloudApp/src/main/java/com/owncloud/android/usecases/transfers/uploads/UploadFilesFromSAFUseCase.kt b/owncloudApp/src/main/java/com/owncloud/android/usecases/transfers/uploads/UploadFilesFromContentUriUseCase.kt similarity index 94% rename from owncloudApp/src/main/java/com/owncloud/android/usecases/transfers/uploads/UploadFilesFromSAFUseCase.kt rename to owncloudApp/src/main/java/com/owncloud/android/usecases/transfers/uploads/UploadFilesFromContentUriUseCase.kt index cbf5d212a55..42234549956 100644 --- a/owncloudApp/src/main/java/com/owncloud/android/usecases/transfers/uploads/UploadFilesFromSAFUseCase.kt +++ b/owncloudApp/src/main/java/com/owncloud/android/usecases/transfers/uploads/UploadFilesFromContentUriUseCase.kt @@ -23,7 +23,6 @@ package com.owncloud.android.usecases.transfers.uploads import android.net.Uri import androidx.documentfile.provider.DocumentFile -import androidx.work.WorkManager import com.owncloud.android.MainApp import com.owncloud.android.domain.BaseUseCase import com.owncloud.android.domain.camerauploads.model.UploadBehavior @@ -40,10 +39,10 @@ import java.io.File * * It stores the upload in the database and then enqueue a new worker to upload the single file */ -class UploadFilesFromSAFUseCase( - private val workManager: WorkManager, +class UploadFilesFromContentUriUseCase( + private val uploadFileFromContentUriUseCase: UploadFileFromContentUriUseCase, private val transferRepository: TransferRepository, -) : BaseUseCase() { +) : BaseUseCase() { override fun run(params: Params) { params.listOfContentUris.forEach { contentUri -> @@ -98,7 +97,6 @@ class UploadFilesFromSAFUseCase( uploadIdInStorageManager: Long, uploadPath: String, ) { - val uploadFileFromContentUriUseCase = UploadFileFromContentUriUseCase(workManager) val uploadFileParams = UploadFileFromContentUriUseCase.Params( contentUri = contentUri, uploadPath = uploadPath, diff --git a/owncloudApp/src/main/java/com/owncloud/android/usecases/transfers/uploads/UploadFilesFromSystemUseCase.kt b/owncloudApp/src/main/java/com/owncloud/android/usecases/transfers/uploads/UploadFilesFromSystemUseCase.kt index c9912a1d152..c8fa0abbfbf 100644 --- a/owncloudApp/src/main/java/com/owncloud/android/usecases/transfers/uploads/UploadFilesFromSystemUseCase.kt +++ b/owncloudApp/src/main/java/com/owncloud/android/usecases/transfers/uploads/UploadFilesFromSystemUseCase.kt @@ -21,17 +21,11 @@ package com.owncloud.android.usecases.transfers.uploads -import androidx.work.Constraints -import androidx.work.NetworkType -import androidx.work.OneTimeWorkRequestBuilder -import androidx.work.WorkManager -import androidx.work.workDataOf import com.owncloud.android.domain.BaseUseCase import com.owncloud.android.domain.camerauploads.model.UploadBehavior import com.owncloud.android.domain.transfers.TransferRepository import com.owncloud.android.domain.transfers.model.OCTransfer import com.owncloud.android.domain.transfers.model.TransferStatus -import com.owncloud.android.workers.UploadFileFromFileSystemWorker import timber.log.Timber import java.io.File @@ -48,7 +42,7 @@ import java.io.File * It stores the upload in the database and then enqueue a new worker to upload the single file */ class UploadFilesFromSystemUseCase( - private val workManager: WorkManager, + private val uploadFileFromSystemUseCase: UploadFileFromSystemUseCase, private val transferRepository: TransferRepository, ) : BaseUseCase() { @@ -105,27 +99,15 @@ class UploadFilesFromSystemUseCase( uploadIdInStorageManager: Long, uploadPath: String, ) { - val inputData = workDataOf( - UploadFileFromFileSystemWorker.KEY_PARAM_ACCOUNT_NAME to accountName, - UploadFileFromFileSystemWorker.KEY_PARAM_BEHAVIOR to UploadBehavior.MOVE.name, - UploadFileFromFileSystemWorker.KEY_PARAM_CONTENT_URI to localPath, - UploadFileFromFileSystemWorker.KEY_PARAM_LAST_MODIFIED to lastModifiedInSeconds, - UploadFileFromFileSystemWorker.KEY_PARAM_UPLOAD_PATH to uploadPath, - UploadFileFromFileSystemWorker.KEY_PARAM_UPLOAD_ID to uploadIdInStorageManager + val uploadFileParams = UploadFileFromSystemUseCase.Params( + accountName = accountName, + localPath = localPath, + lastModifiedInSeconds = lastModifiedInSeconds, + behavior = UploadBehavior.MOVE.toString(), + uploadPath = uploadPath, + uploadIdInStorageManager = uploadIdInStorageManager ) - - val constraints = Constraints.Builder() - .setRequiredNetworkType(NetworkType.CONNECTED) - .build() - - val uploadFileFromContentUriWorker = OneTimeWorkRequestBuilder() - .setInputData(inputData) - .setConstraints(constraints) - .addTag(accountName) - .build() - - workManager.enqueue(uploadFileFromContentUriWorker) - Timber.i("Plain upload of $localPath has been enqueued.") + uploadFileFromSystemUseCase.execute(uploadFileParams) } data class Params( diff --git a/owncloudApp/src/main/java/com/owncloud/android/workers/UploadFileFromFileSystemWorker.kt b/owncloudApp/src/main/java/com/owncloud/android/workers/UploadFileFromFileSystemWorker.kt index 30eec9b1f7d..9060ea52570 100644 --- a/owncloudApp/src/main/java/com/owncloud/android/workers/UploadFileFromFileSystemWorker.kt +++ b/owncloudApp/src/main/java/com/owncloud/android/workers/UploadFileFromFileSystemWorker.kt @@ -144,7 +144,7 @@ class UploadFileFromFileSystemWorker( val paramUploadPath = workerParameters.inputData.getString(KEY_PARAM_UPLOAD_PATH) val paramLastModified = workerParameters.inputData.getString(KEY_PARAM_LAST_MODIFIED) val paramBehavior = workerParameters.inputData.getString(KEY_PARAM_BEHAVIOR) - val paramFileSystemUri = workerParameters.inputData.getString(KEY_PARAM_CONTENT_URI) + val paramFileSystemUri = workerParameters.inputData.getString(KEY_PARAM_LOCAL_PATH) val paramUploadId = workerParameters.inputData.getLong(KEY_PARAM_UPLOAD_ID, -1) account = AccountUtils.getOwnCloudAccountByName(appContext, paramAccountName) ?: return false @@ -353,7 +353,7 @@ class UploadFileFromFileSystemWorker( companion object { const val KEY_PARAM_ACCOUNT_NAME = "KEY_PARAM_ACCOUNT_NAME" const val KEY_PARAM_BEHAVIOR = "KEY_PARAM_BEHAVIOR" - const val KEY_PARAM_CONTENT_URI = "KEY_PARAM_CONTENT_URI" + const val KEY_PARAM_LOCAL_PATH = "KEY_PARAM_LOCAL_PATH" const val KEY_PARAM_LAST_MODIFIED = "KEY_PARAM_LAST_MODIFIED" const val KEY_PARAM_UPLOAD_PATH = "KEY_PARAM_UPLOAD_PATH" const val KEY_PARAM_UPLOAD_ID = "KEY_PARAM_UPLOAD_ID" diff --git a/owncloudApp/src/main/res/layout/fragment_transfer_list.xml b/owncloudApp/src/main/res/layout/fragment_transfer_list.xml new file mode 100644 index 00000000000..ed329bbd67d --- /dev/null +++ b/owncloudApp/src/main/res/layout/fragment_transfer_list.xml @@ -0,0 +1,26 @@ + + + + + + + + diff --git a/owncloudApp/src/main/res/layout/upload_list_group.xml b/owncloudApp/src/main/res/layout/upload_list_group.xml index 0f4f43cb456..9e6ed4b8063 100755 --- a/owncloudApp/src/main/res/layout/upload_list_group.xml +++ b/owncloudApp/src/main/res/layout/upload_list_group.xml @@ -41,9 +41,10 @@ android:layout_width="wrap_content" android:layout_height="0dp" android:visibility="gone" + android:layout_marginStart="13dp" app:layout_constraintStart_toStartOf="parent" app:layout_constraintTop_toBottomOf="@id/uploadListGroupName" - tools:text="@string/action_upload_clear" + android:text="@string/action_upload_clear" tools:visibility="visible" /> diff --git a/owncloudApp/src/main/res/layout/upload_list_item.xml b/owncloudApp/src/main/res/layout/upload_list_item.xml index 394f3c73abc..72f5803be2e 100755 --- a/owncloudApp/src/main/res/layout/upload_list_item.xml +++ b/owncloudApp/src/main/res/layout/upload_list_item.xml @@ -1,14 +1,13 @@ - + android:filterTouchesWhenObscured="true"> + android:scaleType="centerCrop" + tools:src="@drawable/ic_menu_archive" /> @@ -41,7 +41,7 @@ android:ellipsize="middle" android:singleLine="true" android:textColor="@color/textColor" - android:text="@string/placeholder_filename" + tools:text="@string/placeholder_filename" android:textSize="16sp" /> @@ -92,7 +90,7 @@ android:layout_height="wrap_content" android:textColor="@color/list_item_lastmod_and_filesize_text" android:singleLine="true" - android:text="@string/auth_username" + tools:text="@string/auth_username" android:textSize="12dip" /> Current Failed (tap to retry) Uploaded + Enqueued %d files %d file Completed @@ -234,6 +235,7 @@ App was terminated Unknown error Waiting for wifi connectivity + Enqueued Waiting to upload Unsupported media type Download cancelled diff --git a/owncloudData/src/main/java/com/owncloud/android/data/transfers/datasources/LocalTransferDataSource.kt b/owncloudData/src/main/java/com/owncloud/android/data/transfers/datasources/LocalTransferDataSource.kt index c24bb4b929c..b98e7ac3714 100644 --- a/owncloudData/src/main/java/com/owncloud/android/data/transfers/datasources/LocalTransferDataSource.kt +++ b/owncloudData/src/main/java/com/owncloud/android/data/transfers/datasources/LocalTransferDataSource.kt @@ -20,6 +20,7 @@ package com.owncloud.android.data.transfers.datasources +import androidx.lifecycle.LiveData import com.owncloud.android.domain.transfers.model.OCTransfer import com.owncloud.android.domain.transfers.model.TransferResult import com.owncloud.android.domain.transfers.model.TransferStatus @@ -28,6 +29,7 @@ interface LocalTransferDataSource { fun storeTransfer(transfer: OCTransfer): Long fun updateTransfer(transfer: OCTransfer) fun updateTransferStatusToInProgressById(id: Long) + fun updateTransferStatusToEnqueuedById(id: Long) fun updateTransferWhenFinished( id: Long, status: TransferStatus, @@ -37,7 +39,7 @@ interface LocalTransferDataSource { fun removeTransferById(id: Long) fun removeAllTransfersFromAccount(accountName: String) fun getTransferById(id: Long): OCTransfer? - fun getAllTransfers(): List + fun getAllTransfersAsLiveData(): LiveData> fun getLastTransferFor(remotePath: String, accountName: String): OCTransfer? fun getCurrentAndPendingTransfers(): List fun getFailedTransfers(): List diff --git a/owncloudData/src/main/java/com/owncloud/android/data/transfers/datasources/implementation/OCLocalTransferDataSource.kt b/owncloudData/src/main/java/com/owncloud/android/data/transfers/datasources/implementation/OCLocalTransferDataSource.kt index fb5230441c3..6c1232dae32 100644 --- a/owncloudData/src/main/java/com/owncloud/android/data/transfers/datasources/implementation/OCLocalTransferDataSource.kt +++ b/owncloudData/src/main/java/com/owncloud/android/data/transfers/datasources/implementation/OCLocalTransferDataSource.kt @@ -20,6 +20,8 @@ package com.owncloud.android.data.transfers.datasources.implementation +import androidx.lifecycle.LiveData +import androidx.lifecycle.Transformations import com.owncloud.android.data.transfers.datasources.LocalTransferDataSource import com.owncloud.android.data.transfers.db.OCTransferEntity import com.owncloud.android.data.transfers.db.TransferDao @@ -42,6 +44,10 @@ class OCLocalTransferDataSource( transferDao.updateTransferStatusWithId(id, TransferStatus.TRANSFER_IN_PROGRESS.value) } + override fun updateTransferStatusToEnqueuedById(id: Long) { + transferDao.updateTransferStatusWithId(id, TransferStatus.TRANSFER_QUEUED.value) + } + override fun updateTransferWhenFinished( id: Long, status: TransferStatus, @@ -63,8 +69,28 @@ class OCLocalTransferDataSource( return transferDao.getTransferWithId(id)?.toModel() } - override fun getAllTransfers(): List { - return transferDao.getAllTransfers().map { it.toModel() } + override fun getAllTransfersAsLiveData(): LiveData> { + return Transformations.map(transferDao.getAllTransfersAsLiveData()) { transferEntitiesList -> + val transfers = transferEntitiesList.map { transferEntity -> + transferEntity.toModel() + } + val transfersGroupedByStatus = transfers.groupBy { it.status } + val transfersGroupedByStatusOrdered = Array>(4) { emptyList() } + val newTransfersList = mutableListOf() + transfersGroupedByStatus.forEach { transferMap -> + val order = when (transferMap.key) { + TransferStatus.TRANSFER_IN_PROGRESS -> 0 + TransferStatus.TRANSFER_QUEUED -> 1 + TransferStatus.TRANSFER_FAILED -> 2 + TransferStatus.TRANSFER_SUCCEEDED -> 3 + } + transfersGroupedByStatusOrdered[order] = transferMap.value + } + for (items in transfersGroupedByStatusOrdered) { + newTransfersList.addAll(items) + } + newTransfersList + } } override fun getLastTransferFor(remotePath: String, accountName: String): OCTransfer? { diff --git a/owncloudData/src/main/java/com/owncloud/android/data/transfers/db/TransferDao.kt b/owncloudData/src/main/java/com/owncloud/android/data/transfers/db/TransferDao.kt index 9641d358592..86939a09bf5 100644 --- a/owncloudData/src/main/java/com/owncloud/android/data/transfers/db/TransferDao.kt +++ b/owncloudData/src/main/java/com/owncloud/android/data/transfers/db/TransferDao.kt @@ -20,6 +20,7 @@ package com.owncloud.android.data.transfers.db +import androidx.lifecycle.LiveData import androidx.room.Dao import androidx.room.Insert import androidx.room.OnConflictStrategy @@ -38,7 +39,7 @@ abstract class TransferDao { abstract fun getTransfersWithStatus(status: List): List @Query(SELECT_ALL_TRANSFERS) - abstract fun getAllTransfers(): List + abstract fun getAllTransfersAsLiveData(): LiveData> @Insert(onConflict = OnConflictStrategy.REPLACE) abstract fun insert(ocTransferEntity: OCTransferEntity): Long diff --git a/owncloudData/src/main/java/com/owncloud/android/data/transfers/repository/OCTransferRepository.kt b/owncloudData/src/main/java/com/owncloud/android/data/transfers/repository/OCTransferRepository.kt index 16103c337d5..9a881859ba4 100644 --- a/owncloudData/src/main/java/com/owncloud/android/data/transfers/repository/OCTransferRepository.kt +++ b/owncloudData/src/main/java/com/owncloud/android/data/transfers/repository/OCTransferRepository.kt @@ -20,6 +20,7 @@ package com.owncloud.android.data.transfers.repository +import androidx.lifecycle.LiveData import com.owncloud.android.data.transfers.datasources.LocalTransferDataSource import com.owncloud.android.domain.transfers.TransferRepository import com.owncloud.android.domain.transfers.model.OCTransfer @@ -39,6 +40,10 @@ class OCTransferRepository( localTransferDataSource.updateTransferStatusToInProgressById(id = id) } + override fun updateTransferStatusToEnqueuedById(id: Long) { + localTransferDataSource.updateTransferStatusToEnqueuedById(id = id) + } + override fun updateTransferWhenFinished( id: Long, status: TransferStatus, @@ -62,8 +67,8 @@ class OCTransferRepository( override fun getTransferById(id: Long): OCTransfer? = localTransferDataSource.getTransferById(id = id) - override fun getAllTransfers(): List = - localTransferDataSource.getAllTransfers() + override fun getAllTransfersAsLiveData(): LiveData> = + localTransferDataSource.getAllTransfersAsLiveData() override fun getLastTransferFor(remotePath: String, accountName: String) = localTransferDataSource.getLastTransferFor(remotePath = remotePath, accountName = accountName) diff --git a/owncloudDomain/src/main/java/com/owncloud/android/domain/transfers/TransferRepository.kt b/owncloudDomain/src/main/java/com/owncloud/android/domain/transfers/TransferRepository.kt index 9ca4238ae1c..7dcf53b72e8 100644 --- a/owncloudDomain/src/main/java/com/owncloud/android/domain/transfers/TransferRepository.kt +++ b/owncloudDomain/src/main/java/com/owncloud/android/domain/transfers/TransferRepository.kt @@ -20,6 +20,7 @@ package com.owncloud.android.domain.transfers +import androidx.lifecycle.LiveData import com.owncloud.android.domain.transfers.model.OCTransfer import com.owncloud.android.domain.transfers.model.TransferResult import com.owncloud.android.domain.transfers.model.TransferStatus @@ -28,6 +29,7 @@ interface TransferRepository { fun storeTransfer(transfer: OCTransfer): Long fun updateTransfer(transfer: OCTransfer) fun updateTransferStatusToInProgressById(id: Long) + fun updateTransferStatusToEnqueuedById(id: Long) fun updateTransferWhenFinished( id: Long, status: TransferStatus, @@ -37,7 +39,7 @@ interface TransferRepository { fun removeTransferById(id: Long) fun removeAllTransfersFromAccount(accountName: String) fun getTransferById(id: Long): OCTransfer? - fun getAllTransfers(): List + fun getAllTransfersAsLiveData(): LiveData> fun getLastTransferFor(remotePath: String, accountName: String): OCTransfer? fun getCurrentAndPendingTransfers(): List fun getFailedTransfers(): List diff --git a/owncloudDomain/src/main/java/com/owncloud/android/domain/transfers/usecases/ClearFailedTransfersUseCase.kt b/owncloudDomain/src/main/java/com/owncloud/android/domain/transfers/usecases/ClearFailedTransfersUseCase.kt new file mode 100644 index 00000000000..a9e767eabe8 --- /dev/null +++ b/owncloudDomain/src/main/java/com/owncloud/android/domain/transfers/usecases/ClearFailedTransfersUseCase.kt @@ -0,0 +1,32 @@ +/** + * ownCloud Android client application + * + * @author Juan Carlos Garrote Gascón + * + * Copyright (C) 2022 ownCloud GmbH. + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2, + * as published by the Free Software Foundation. + * + * 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 com.owncloud.android.domain.transfers.usecases + +import com.owncloud.android.domain.BaseUseCase +import com.owncloud.android.domain.transfers.TransferRepository + +class ClearFailedTransfersUseCase( + private val transferRepository: TransferRepository, +) : BaseUseCase() { + override fun run(params: Unit): Unit = + transferRepository.clearFailedTransfers() + +} diff --git a/owncloudDomain/src/main/java/com/owncloud/android/domain/transfers/usecases/ClearSuccessfulTransfersUseCase.kt b/owncloudDomain/src/main/java/com/owncloud/android/domain/transfers/usecases/ClearSuccessfulTransfersUseCase.kt new file mode 100644 index 00000000000..87450a33c9c --- /dev/null +++ b/owncloudDomain/src/main/java/com/owncloud/android/domain/transfers/usecases/ClearSuccessfulTransfersUseCase.kt @@ -0,0 +1,32 @@ +/** + * ownCloud Android client application + * + * @author Juan Carlos Garrote Gascón + * + * Copyright (C) 2022 ownCloud GmbH. + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2, + * as published by the Free Software Foundation. + * + * 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 com.owncloud.android.domain.transfers.usecases + +import com.owncloud.android.domain.BaseUseCase +import com.owncloud.android.domain.transfers.TransferRepository + +class ClearSuccessfulTransfersUseCase( + private val transferRepository: TransferRepository, +) : BaseUseCase() { + override fun run(params: Unit): Unit = + transferRepository.clearSuccessfulTransfers() + +} diff --git a/owncloudDomain/src/main/java/com/owncloud/android/domain/transfers/usecases/DeleteTransferWithIdUseCase.kt b/owncloudDomain/src/main/java/com/owncloud/android/domain/transfers/usecases/DeleteTransferWithIdUseCase.kt new file mode 100644 index 00000000000..3e22aab3571 --- /dev/null +++ b/owncloudDomain/src/main/java/com/owncloud/android/domain/transfers/usecases/DeleteTransferWithIdUseCase.kt @@ -0,0 +1,33 @@ +/** + * ownCloud Android client application + * + * @author Juan Carlos Garrote Gascón + * + * Copyright (C) 2022 ownCloud GmbH. + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2, + * as published by the Free Software Foundation. + * + * 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 com.owncloud.android.domain.transfers.usecases + +import com.owncloud.android.domain.BaseUseCase +import com.owncloud.android.domain.transfers.TransferRepository + +class DeleteTransferWithIdUseCase( + private val transferRepository: TransferRepository, +) : BaseUseCase() { + override fun run(params: Params) = + transferRepository.removeTransferById(params.id) + + data class Params(val id: Long) +} diff --git a/owncloudDomain/src/main/java/com/owncloud/android/domain/transfers/usecases/GetAllTransfersAsLiveDataUseCase.kt b/owncloudDomain/src/main/java/com/owncloud/android/domain/transfers/usecases/GetAllTransfersAsLiveDataUseCase.kt new file mode 100644 index 00000000000..61f1e52241f --- /dev/null +++ b/owncloudDomain/src/main/java/com/owncloud/android/domain/transfers/usecases/GetAllTransfersAsLiveDataUseCase.kt @@ -0,0 +1,34 @@ +/** + * ownCloud Android client application + * + * @author Juan Carlos Garrote Gascón + * + * Copyright (C) 2022 ownCloud GmbH. + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2, + * as published by the Free Software Foundation. + * + * 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 com.owncloud.android.domain.transfers.usecases + +import androidx.lifecycle.LiveData +import com.owncloud.android.domain.BaseUseCase +import com.owncloud.android.domain.transfers.TransferRepository +import com.owncloud.android.domain.transfers.model.OCTransfer + +class GetAllTransfersAsLiveDataUseCase( + private val transferRepository: TransferRepository, +) : BaseUseCase>, Unit>() { + override fun run(params: Unit): LiveData> = + transferRepository.getAllTransfersAsLiveData() + +}