Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
18 changes: 18 additions & 0 deletions app/src/main/java/com/nextcloud/client/database/dao/UploadDao.kt
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,8 @@
package com.nextcloud.client.database.dao

import androidx.room.Dao
import androidx.room.Insert
import androidx.room.OnConflictStrategy
import androidx.room.Query
import com.nextcloud.client.database.entity.UploadEntity
import com.owncloud.android.db.ProviderMeta.ProviderTableMeta
Expand All @@ -27,4 +29,20 @@ interface UploadDao {
ProviderTableMeta.UPLOADS_ACCOUNT_NAME + " = :accountName"
)
fun getUploadsByIds(ids: LongArray, accountName: String): List<UploadEntity>

@Query(
"SELECT * FROM ${ProviderTableMeta.UPLOADS_TABLE_NAME} " +
"WHERE ${ProviderTableMeta.UPLOADS_REMOTE_PATH} = :remotePath LIMIT 1"
)
fun getByRemotePath(remotePath: String): UploadEntity?

@Insert(onConflict = OnConflictStrategy.REPLACE)
fun update(upload: UploadEntity)

@Query(
"DELETE FROM ${ProviderTableMeta.UPLOADS_TABLE_NAME} " +
"WHERE ${ProviderTableMeta.UPLOADS_ACCOUNT_NAME} = :accountName " +
"AND ${ProviderTableMeta.UPLOADS_REMOTE_PATH} = :remotePath"
)
fun deleteByAccountAndRemotePath(accountName: String, remotePath: String)
}
Original file line number Diff line number Diff line change
Expand Up @@ -32,7 +32,7 @@ data class UploadEntity(
@ColumnInfo(name = ProviderTableMeta.UPLOADS_FILE_SIZE)
val fileSize: Long?,
@ColumnInfo(name = ProviderTableMeta.UPLOADS_STATUS)
val status: Int?,
var status: Int?,
@ColumnInfo(name = ProviderTableMeta.UPLOADS_LOCAL_BEHAVIOUR)
val localBehaviour: Int?,
@ColumnInfo(name = ProviderTableMeta.UPLOADS_UPLOAD_TIME)
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -143,7 +143,7 @@ interface BackgroundJobManager {
fun startFilesUploadJob(user: User, uploadIds: LongArray, showSameFileAlreadyExistsNotification: Boolean)
fun getFileUploads(user: User): LiveData<List<JobInfo>>
fun cancelFilesUploadJob(user: User)
fun isStartFileUploadJobScheduled(user: User): Boolean
fun isStartFileUploadJobScheduled(accountName: String): Boolean

fun cancelFilesDownloadJob(user: User, fileId: Long)

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -635,10 +635,10 @@ internal class BackgroundJobManagerImpl(
workManager.enqueue(request)
}

private fun startFileUploadJobTag(user: User): String = JOB_FILES_UPLOAD + user.accountName
private fun startFileUploadJobTag(accountName: String): String = JOB_FILES_UPLOAD + accountName

override fun isStartFileUploadJobScheduled(user: User): Boolean =
workManager.isWorkScheduled(startFileUploadJobTag(user))
override fun isStartFileUploadJobScheduled(accountName: String): Boolean =
workManager.isWorkScheduled(startFileUploadJobTag(accountName))

/**
* This method supports initiating uploads for various scenarios, including:
Expand All @@ -656,7 +656,7 @@ internal class BackgroundJobManagerImpl(
defaultDispatcherScope.launch {
val batchSize = FileUploadHelper.MAX_FILE_COUNT
val batches = uploadIds.toList().chunked(batchSize)
val tag = startFileUploadJobTag(user)
val tag = startFileUploadJobTag(user.accountName)

val constraints = Constraints.Builder()
.setRequiredNetworkType(NetworkType.CONNECTED)
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -40,8 +40,6 @@ import kotlinx.coroutines.CoroutineScope
import kotlinx.coroutines.Dispatchers
import kotlinx.coroutines.launch
import java.io.File
import java.util.concurrent.CompletableFuture
import java.util.concurrent.ExecutionException
import java.util.concurrent.Semaphore
import javax.inject.Inject

Expand Down Expand Up @@ -220,44 +218,20 @@ class FileUploadHelper {
}

fun removeFileUpload(remotePath: String, accountName: String) {
try {
val user = accountManager.getUser(accountName).get()

// need to update now table in mUploadsStorageManager,
// since the operation will not get to be run by FileUploader#uploadFile
uploadsStorageManager.removeUpload(accountName, remotePath)
val uploadIds = uploadsStorageManager.getCurrentUploadIds(user.accountName)
cancelAndRestartUploadJob(user, uploadIds)
} catch (e: NoSuchElementException) {
Log_OC.e(TAG, "Error cancelling current upload because user does not exist!: " + e.message)
}
uploadsStorageManager.uploadDao.deleteByAccountAndRemotePath(accountName, remotePath)
}

fun cancelFileUpload(remotePath: String, accountName: String) {
fun setStatusOfUploadToCancel(remotePath: String) {
ioScope.launch {
val upload = uploadsStorageManager.getUploadByRemotePath(remotePath)
if (upload != null) {
cancelFileUploads(listOf(upload), accountName)
} else {
Log_OC.e(TAG, "Error cancelling current upload because upload does not exist!")
uploadsStorageManager.run {
uploadDao.getByRemotePath(remotePath)?.let { entity ->
entity.status = UploadStatus.UPLOAD_CANCELLED.value
uploadDao.update(entity)
}
}
}
}

fun cancelFileUploads(uploads: List<OCUpload>, accountName: String) {
for (upload in uploads) {
upload.uploadStatus = UploadStatus.UPLOAD_CANCELLED
uploadsStorageManager.updateUpload(upload)
}

try {
val user = accountManager.getUser(accountName).get()
cancelAndRestartUploadJob(user, uploads.getUploadIds())
} catch (e: NoSuchElementException) {
Log_OC.e(TAG, "Error restarting upload job because user does not exist!: " + e.message)
}
}

fun cancelAndRestartUploadJob(user: User, uploadIds: LongArray) {
backgroundJobManager.run {
cancelFilesUploadJob(user)
Expand All @@ -266,26 +240,16 @@ class FileUploadHelper {
}

@Suppress("ReturnCount")
fun isUploading(user: User?, file: OCFile?): Boolean {
if (user == null || file == null || !backgroundJobManager.isStartFileUploadJobScheduled(user)) {
fun isUploading(remotePath: String?, accountName: String?): Boolean {
accountName ?: return false
if (!backgroundJobManager.isStartFileUploadJobScheduled(accountName)) {
return false
}

val uploadCompletableFuture = CompletableFuture.supplyAsync {
uploadsStorageManager.getUploadByRemotePath(file.remotePath)
}
return try {
val upload = uploadCompletableFuture.get()
if (upload != null) {
upload.uploadStatus == UploadStatus.UPLOAD_IN_PROGRESS
} else {
false
}
} catch (e: ExecutionException) {
false
} catch (e: InterruptedException) {
false
}
remotePath ?: return false
val upload = uploadsStorageManager.uploadDao.getByRemotePath(remotePath)
return upload?.status == UploadStatus.UPLOAD_IN_PROGRESS.value ||
FileUploadWorker.isUploading(remotePath, accountName)
}

private fun checkConnectivity(connectivityService: ConnectivityService): Boolean {
Expand Down Expand Up @@ -474,7 +438,9 @@ class FileUploadHelper {
return
}

instance().cancelFileUpload(remotePath, accountName)
FileUploadWorker.cancelCurrentUpload(remotePath, accountName, onCompleted = {
instance().setStatusOfUploadToCancel(remotePath)
})
}
}
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -91,6 +91,23 @@ class FileUploadWorker(
fun getUploadStartMessage(): String = FileUploadWorker::class.java.name + UPLOAD_START_MESSAGE

fun getUploadFinishMessage(): String = FileUploadWorker::class.java.name + UPLOAD_FINISH_MESSAGE

fun cancelCurrentUpload(remotePath: String, accountName: String, onCompleted: () -> Unit) {
currentUploadFileOperation?.let {
if (it.remotePath == remotePath && it.user.accountName == accountName) {
it.cancel(ResultCode.USER_CANCELLED)
onCompleted()
}
}
}

fun isUploading(remotePath: String?, accountName: String?): Boolean {
currentUploadFileOperation?.let {
return it.remotePath == remotePath && it.user.accountName == accountName
}

return false
}
}

private var lastPercent = 0
Expand Down Expand Up @@ -346,6 +363,10 @@ class FileUploadWorker(
return
}

if (uploadResult.code == ResultCode.USER_CANCELLED) {
return
}

notificationManager.run {
val errorMessage = ErrorMessageAdapter.getErrorCauseMessage(
uploadResult,
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -414,6 +414,7 @@ public OCUpload getPendingCurrentOrFailedUpload(OCUpload upload) {
return null;
}

@Nullable
public OCUpload getUploadByRemotePath(String remotePath) {
OCUpload result = null;
try (Cursor cursor = getDB().query(
Expand Down Expand Up @@ -797,7 +798,7 @@ public void updateDatabaseUploadResult(RemoteOperationResult uploadResult, Uploa
upload.getRemotePath(),
localPath
);
} else {
} else if (uploadResult.getCode() != RemoteOperationResult.ResultCode.USER_CANCELLED){
updateUploadStatus(
upload.getOCUploadId(),
UploadStatus.UPLOAD_FAILED,
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -434,7 +434,7 @@ private boolean anyFileDownloading() {

private boolean anyFileUploading() {
for (OCFile file : files) {
if (FileUploadHelper.Companion.instance().isUploading(user, file)) {
if (FileUploadHelper.Companion.instance().isUploading(file.getRemotePath(), user.getAccountName())) {
return true;
}
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -79,12 +79,11 @@
import java.io.InputStream;
import java.io.OutputStream;
import java.io.RandomAccessFile;
import java.nio.file.Files;
import java.nio.file.Path;
import java.nio.file.Paths;
import java.nio.channels.FileChannel;
import java.nio.channels.FileLock;
import java.nio.channels.OverlappingFileLockException;
import java.nio.file.Files;
import java.nio.file.Paths;
import java.security.InvalidAlgorithmParameterException;
import java.security.InvalidKeyException;
import java.security.NoSuchAlgorithmException;
Expand Down Expand Up @@ -1435,21 +1434,37 @@ private static boolean existsFile(OwnCloudClient client,
}

/**
* Allows to cancel the actual upload operation. If actual upload operating is in progress it is cancelled, if
* upload preparation is being performed upload will not take place.
* Cancels the current upload process.
*
* <p>
* Behavior depends on the current state of the upload:
* <ul>
* <li><b>Upload in preparation:</b> Upload will not start and a cancellation flag is set.</li>
* <li><b>Upload in progress:</b> The ongoing upload operation is cancelled via
* {@link UploadFileRemoteOperation#cancel(ResultCode)}.</li>
* <li><b>No upload operation:</b> A cancellation flag is still set, but this situation is unexpected
* and logged as an error.</li>
* </ul>
*
* <p>
* Once cancelled, the database will be updated through
* {@link UploadsStorageManager#updateDatabaseUploadResult(RemoteOperationResult, UploadFileOperation)}.
*
* @param cancellationReason the reason for cancellation
*/
public void cancel(ResultCode cancellationReason) {
if (mUploadOperation == null) {
if (mUploadOperation != null) {
// Cancel an active upload
Log_OC.d(TAG, "Cancelling upload during actual upload operation.");
mUploadOperation.cancel(cancellationReason);
} else {
// Cancel while preparing or when no upload exists
mCancellationRequested.set(true);
if (mUploadStarted.get()) {
Log_OC.d(TAG, "Cancelling upload during upload preparations.");
mCancellationRequested.set(true);
Log_OC.d(TAG, "Cancelling upload during preparation.");
} else {
mCancellationRequested.set(true);
Log_OC.e(TAG, "No upload in progress. This should not happen.");
}
} else {
Log_OC.d(TAG, "Cancelling upload during actual upload operation.");
mUploadOperation.cancel(cancellationReason);
}
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -380,7 +380,7 @@ class OCFileListDelegate(

return operationsServiceBinder?.isSynchronizing(user, file) == true ||
fileDownloadHelper.isDownloading(user, file) ||
fileUploadHelper.isUploading(user, file)
fileUploadHelper.isUploading(file.remotePath, user.accountName)
}

private fun showLocalFileIndicator(file: OCFile, holder: ListViewHolder) {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -67,6 +67,8 @@
import java.util.concurrent.TimeUnit;

import androidx.annotation.NonNull;
import kotlin.Unit;
import kotlin.jvm.functions.Function0;

/**
* This Adapter populates a ListView with following types of uploads: pending, active, completed. Filtering possible.
Expand Down Expand Up @@ -144,7 +146,10 @@ public void onBindHeaderViewHolder(SectionedViewHolder holder, int section, bool
return;
}

uploadHelper.cancelFileUploads(Arrays.asList(group.items), accountName);
for (OCUpload upload: group.items) {
uploadHelper.setStatusOfUploadToCancel(upload.getRemotePath());
FileUploadWorker.Companion.cancelCurrentUpload(upload.getRemotePath(), accountName, () -> Unit.INSTANCE);
}
loadUploadItemsFromDb();
}).start();
case FINISHED -> {
Expand Down Expand Up @@ -427,7 +432,8 @@ public void onBindViewHolder(SectionedViewHolder holder, int section, int relati
itemViewHolder.binding.uploadRightButton.setImageResource(R.drawable.ic_action_cancel_grey);
itemViewHolder.binding.uploadRightButton.setVisibility(View.VISIBLE);
itemViewHolder.binding.uploadRightButton.setOnClickListener(v -> {
uploadHelper.cancelFileUpload(item.getRemotePath(), item.getAccountName());
uploadHelper.setStatusOfUploadToCancel(item.getRemotePath());
FileUploadWorker.Companion.cancelCurrentUpload(item.getRemotePath(), item.getAccountName(), () -> Unit.INSTANCE);
loadUploadItemsFromDb();
});

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -574,7 +574,7 @@ public void updateFileDetails(boolean transferring, boolean refresh) {
// configure UI for depending upon local state of the file
if (transferring
|| (FileDownloadHelper.Companion.instance().isDownloading(user, file))
|| (FileUploadHelper.Companion.instance().isUploading(user, file))) {
|| (FileUploadHelper.Companion.instance().isUploading(file.getRemotePath(), user.getAccountName()))) {
setButtonsForTransferring();

} else if (file.isDown()) {
Expand Down Expand Up @@ -736,7 +736,7 @@ private void setButtonsForTransferring() {
if (FileDownloadHelper.Companion.instance().isDownloading(user, getFile())) {
binding.progressText.setText(R.string.downloader_download_in_progress_ticker);
} else {
if (FileUploadHelper.Companion.instance().isUploading(user, getFile())) {
if (FileUploadHelper.Companion.instance().isUploading(getFile().getRemotePath(), user.getAccountName())) {
binding.progressText.setText(R.string.uploader_upload_in_progress_ticker);
}
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -34,6 +34,7 @@
import com.nextcloud.client.jobs.BackgroundJobManager;
import com.nextcloud.client.jobs.download.FileDownloadHelper;
import com.nextcloud.client.jobs.upload.FileUploadHelper;
import com.nextcloud.client.jobs.upload.FileUploadWorker;
import com.nextcloud.client.network.ConnectivityService;
import com.nextcloud.utils.EditorUtils;
import com.owncloud.android.MainApp;
Expand Down Expand Up @@ -97,6 +98,8 @@
import androidx.core.content.FileProvider;
import androidx.fragment.app.FragmentManager;
import androidx.fragment.app.FragmentTransaction;
import kotlin.Unit;
import kotlin.jvm.functions.Function0;

/**
* Helper implementation for file operations locally and remote.
Expand Down Expand Up @@ -1011,12 +1014,11 @@ public void cancelTransference(OCFile file) {
FileDownloadHelper.Companion.instance().cancelPendingOrCurrentDownloads(currentUser, files);
}

if (FileUploadHelper.Companion.instance().isUploading(currentUser, file)) {
try {
FileUploadHelper.Companion.instance().cancelFileUpload(file.getRemotePath(), currentUser.getAccountName());
} catch (NoSuchElementException e) {
Log_OC.e(TAG, "Error cancelling current upload because user does not exist!");
}
if (FileUploadHelper.Companion.instance().isUploading(file.getRemotePath(), currentUser.getAccountName())) {
FileUploadWorker.Companion.cancelCurrentUpload(file.getRemotePath(), currentUser.getAccountName(), () -> {
FileUploadHelper.Companion.instance().setStatusOfUploadToCancel(file.getRemotePath());
return Unit.INSTANCE;
});
}
}

Expand Down
Loading
Loading