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
Original file line number Diff line number Diff line change
Expand Up @@ -46,6 +46,8 @@
import com.owncloud.android.lib.common.operations.RemoteOperationResult;
import com.owncloud.android.lib.common.operations.RemoteOperationResult.ResultCode;
import com.owncloud.android.lib.common.utils.Log_OC;
import com.owncloud.android.lib.resources.files.Chunk;
import com.owncloud.android.lib.resources.files.ChunkUploadListener;
import com.owncloud.android.lib.resources.files.ChunkedFileUploadRemoteOperation;
import com.owncloud.android.lib.resources.files.ExistenceCheckRemoteOperation;
import com.owncloud.android.lib.resources.files.ReadFileRemoteOperation;
Expand Down Expand Up @@ -90,11 +92,13 @@
import java.security.cert.CertificateException;
import java.security.spec.InvalidParameterSpecException;
import java.util.ArrayList;
import java.util.Collections;
import java.util.HashMap;
import java.util.HashSet;
import java.util.List;
import java.util.Map;
import java.util.Set;
import java.util.concurrent.ConcurrentHashMap;
import java.util.concurrent.atomic.AtomicBoolean;

import javax.crypto.BadPaddingException;
Expand All @@ -103,14 +107,16 @@
import javax.crypto.NoSuchPaddingException;

import androidx.annotation.CheckResult;
import androidx.annotation.NonNull;
import androidx.annotation.Nullable;
import kotlin.Pair;
import kotlin.Triple;
import kotlin.Unit;

/**
* Operation performing the update in the ownCloud server of a file that was modified locally.
*/
public class UploadFileOperation extends SyncOperation {
public class UploadFileOperation extends SyncOperation implements ChunkUploadListener {

private static final String TAG = UploadFileOperation.class.getSimpleName();

Expand Down Expand Up @@ -746,7 +752,8 @@ private RemoteOperationResult performE2EUpload(E2EClientData data) throws Operat
mUploadOperation.addDataTransferProgressListener(mDataTransferListener);
}

if (mCancellationRequested.get()) {
if (shouldCancelUpload()) {
processCancellationIfRequested();
throw new OperationCancelledException();
}

Expand Down Expand Up @@ -1057,6 +1064,11 @@ private RemoteOperationResult normalUpload(OwnCloudClient client) {
mDisableRetries);
}

if (mUploadOperation instanceof ChunkedFileUploadRemoteOperation chunkedFileUploadRemoteOperation) {
chunkedFileUploadRemoteOperation.setChunkUploadListener(this);
}


/**
* Adds the onTransferProgress in FileUploadWorker
* {@link FileUploadWorker#onTransferProgress(long, long, long, String)()}
Expand All @@ -1065,7 +1077,8 @@ private RemoteOperationResult normalUpload(OwnCloudClient client) {
mUploadOperation.addDataTransferProgressListener(mDataTransferListener);
}

if (mCancellationRequested.get()) {
if (shouldCancelUpload()) {
processCancellationIfRequested();
throw new OperationCancelledException();
}

Expand Down Expand Up @@ -1131,6 +1144,10 @@ private RemoteOperationResult normalUpload(OwnCloudClient client) {
return result;
}

private boolean shouldCancelUpload() {
return mCancellationRequested.get() || (getMatchingCancellationRequest(mRemotePath, user.getAccountName()) != null);
}

private void updateSize(long size) {
OCUpload ocUpload = uploadsStorageManager.getUploadById(getOCUploadId());
if (ocUpload != null) {
Expand Down Expand Up @@ -1168,7 +1185,8 @@ private RemoteOperationResult copyFile(File originalFile, String expectedPath) t
return copy(originalFile, temporalFile);
}

if (mCancellationRequested.get()) {
if (shouldCancelUpload()) {
processCancellationIfRequested();
throw new OperationCancelledException();
}

Expand Down Expand Up @@ -1210,7 +1228,8 @@ private RemoteOperationResult checkNameCollision(OCFile parentFile,
}
}

if (mCancellationRequested.get()) {
if (shouldCancelUpload()) {
processCancellationIfRequested();
throw new OperationCancelledException();
}

Expand Down Expand Up @@ -1519,15 +1538,16 @@ private RemoteOperationResult copy(File sourceFile, File targetFile) throws IOEx
out = new FileOutputStream(targetFile);
int nRead;
byte[] buf = new byte[4096];
while (!mCancellationRequested.get() &&
while (!shouldCancelUpload() &&
(nRead = in.read(buf)) > -1) {
out.write(buf, 0, nRead);
}
out.flush();

} // else: weird but possible situation, nothing to copy

if (mCancellationRequested.get()) {
if (shouldCancelUpload()) {
processCancellationIfRequested();
return new RemoteOperationResult(new OperationCancelledException());
}
} catch (Exception e) {
Expand Down Expand Up @@ -1673,4 +1693,56 @@ public interface OnRenameListener {
void onRenameUpload();
}

/** Thread-safe set of pending cancellation requests. Each pair represents (remotePath, accountName). */
private final static Set<Pair<String, String>> pendingCancellationRequests = Collections.newSetFromMap(new ConcurrentHashMap<>());

/**
* Adds a cancellation request for a specific upload.
* <p>
* This does not immediately cancel the upload. It only marks it for cancellation.
* The upload will be cancelled later when {@link #processCancellationIfRequested()} is called.
*
* @param remotePath the remote path of the file being uploaded
* @param accountName the account name of the user who owns the upload
*/
public static void requestCancellation(String remotePath, String accountName) {
pendingCancellationRequests.add(new Pair<>(remotePath, accountName));
}

/**
* Checks if a cancellation has been requested for the current upload file operation, and cancels it if so.
* <p>
* If a matching request exists, the upload will be cancelled and the request removed
* from the pending set.
*
* @return true if the upload was cancelled due to a pending request
*/
public boolean processCancellationIfRequested() {
final var cancellationRequest = getMatchingCancellationRequest(mRemotePath, user.getAccountName());
if (cancellationRequest != null) {
cancel(ResultCode.USER_CANCELLED);
pendingCancellationRequests.remove(cancellationRequest);
return true;
}

return false;
}

public static Pair<String, String> getMatchingCancellationRequest(String remotePath, String accountName) {
for (Pair<String, String> cancelRequest : pendingCancellationRequests) {
if (remotePath.equals(cancelRequest.getFirst()) && accountName.equals(cancelRequest.getSecond())) {
return cancelRequest;
}
}
return null;
}

@Override
public boolean isCancelled() {
final var result = shouldCancelUpload();
if (result) {
processCancellationIfRequested();
}
return result;
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -46,6 +46,7 @@
import com.owncloud.android.lib.common.operations.OnRemoteOperationListener;
import com.owncloud.android.lib.common.utils.Log_OC;
import com.owncloud.android.operations.RefreshFolderOperation;
import com.owncloud.android.operations.UploadFileOperation;
import com.owncloud.android.ui.activity.ConflictsResolveActivity;
import com.owncloud.android.ui.activity.FileActivity;
import com.owncloud.android.ui.activity.FileDisplayActivity;
Expand All @@ -67,8 +68,6 @@
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 @@ -148,7 +147,7 @@ public void onBindHeaderViewHolder(SectionedViewHolder holder, int section, bool

for (OCUpload upload: group.items) {
uploadHelper.setStatusOfUploadToCancel(upload.getRemotePath());
FileUploadWorker.Companion.cancelCurrentUpload(upload.getRemotePath(), accountName, () -> Unit.INSTANCE);
UploadFileOperation.requestCancellation(upload.getRemotePath(), accountName);
}
loadUploadItemsFromDb();
}).start();
Expand Down Expand Up @@ -433,7 +432,7 @@ public void onBindViewHolder(SectionedViewHolder holder, int section, int relati
itemViewHolder.binding.uploadRightButton.setVisibility(View.VISIBLE);
itemViewHolder.binding.uploadRightButton.setOnClickListener(v -> {
uploadHelper.setStatusOfUploadToCancel(item.getRemotePath());
FileUploadWorker.Companion.cancelCurrentUpload(item.getRemotePath(), item.getAccountName(), () -> Unit.INSTANCE);
UploadFileOperation.requestCancellation(item.getRemotePath(), item.getAccountName());
loadUploadItemsFromDb();
});

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -53,6 +53,7 @@
import com.owncloud.android.lib.resources.shares.ShareType;
import com.owncloud.android.lib.resources.status.OCCapability;
import com.owncloud.android.operations.SynchronizeFileOperation;
import com.owncloud.android.operations.UploadFileOperation;
import com.owncloud.android.services.OperationsService;
import com.owncloud.android.ui.activity.ConflictsResolveActivity;
import com.owncloud.android.ui.activity.ExternalSiteWebView;
Expand Down Expand Up @@ -1019,6 +1020,7 @@ public void cancelTransference(OCFile file) {

final var fileUploadHelper = FileUploadHelper.Companion.instance();
if (fileUploadHelper.isUploading(file.getRemotePath(), currentUser.getAccountName())) {
UploadFileOperation.requestCancellation(file.getRemotePath(), currentUser.getAccountName());
FileUploadWorker.Companion.cancelCurrentUpload(file.getRemotePath(), currentUser.getAccountName(), () -> {
fileUploadHelper.setStatusOfUploadToCancel(file.getRemotePath());
return Unit.INSTANCE;
Expand Down
2 changes: 1 addition & 1 deletion build.gradle
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,7 @@
*/
buildscript {
ext {
androidLibraryVersion ="3546bd82fc"
androidLibraryVersion ="1dae051f17"
androidCommonLibraryVersion = "0.28.0"
androidPluginVersion = '8.13.0'
androidxMediaVersion = "1.5.1"
Expand Down
8 changes: 8 additions & 0 deletions gradle/verification-metadata.xml
Original file line number Diff line number Diff line change
Expand Up @@ -15990,6 +15990,14 @@
<sha256 value="e0c738e7c3648549631eda0f90d3ae028d6199ffe368324baf0503433535f33f" origin="Generated by Gradle" reason="Artifact is not signed"/>
</artifact>
</component>
<component group="com.github.nextcloud" name="android-library" version="1dae051f17">
<artifact name="android-library-1dae051f17.aar">
<sha256 value="5103bbb8d3ebab273efc3935cee785255739f76ae60a3871305b9fc4f50e2bbb" origin="Generated by Gradle" reason="Artifact is not signed"/>
</artifact>
<artifact name="android-library-1dae051f17.module">
<sha256 value="4159443ad49fd474a3845a34040ed9c494968f7768aa12946bb3eb4a2fecddf6" origin="Generated by Gradle" reason="Artifact is not signed"/>
</artifact>
</component>
<component group="com.github.nextcloud" name="android-library" version="26dc8477962f12356db840bb1a774f0186b38e4d">
<artifact name="android-library-26dc8477962f12356db840bb1a774f0186b38e4d.aar">
<sha256 value="57ab4fd7c922875a7e0b5feac20aa27ab5df0fd3b4e042f92ed727c0b6316e81" origin="Generated by Gradle" reason="Artifact is not signed"/>
Expand Down
Loading