From 590e617c8a96b18de8e32537622eaf9ca276bd2b Mon Sep 17 00:00:00 2001 From: tobiasKaminsky Date: Mon, 9 Oct 2017 14:16:08 +0200 Subject: [PATCH] split up large method - part 1 --- .../android/files/services/FileUploader.java | 8 +- .../operations/UploadFileOperation.java | 369 ++++++++++-------- 2 files changed, 209 insertions(+), 168 deletions(-) diff --git a/src/main/java/com/owncloud/android/files/services/FileUploader.java b/src/main/java/com/owncloud/android/files/services/FileUploader.java index bbec886765..a9e9ddf158 100644 --- a/src/main/java/com/owncloud/android/files/services/FileUploader.java +++ b/src/main/java/com/owncloud/android/files/services/FileUploader.java @@ -615,8 +615,8 @@ public class FileUploader extends Service if (isCreateRemoteFolder) { newUpload.setRemoteFolderToBeCreated(); } - newUpload.addDatatransferProgressListener(this); - newUpload.addDatatransferProgressListener((FileUploaderBinder) mBinder); + newUpload.addDataTransferProgressListener(this); + newUpload.addDataTransferProgressListener((FileUploaderBinder) mBinder); newUpload.addRenameUploadListener(this); @@ -671,8 +671,8 @@ public class FileUploader extends Service whileChargingOnly ); - newUpload.addDatatransferProgressListener(this); - newUpload.addDatatransferProgressListener((FileUploaderBinder) mBinder); + newUpload.addDataTransferProgressListener(this); + newUpload.addDataTransferProgressListener((FileUploaderBinder) mBinder); newUpload.addRenameUploadListener(this); diff --git a/src/main/java/com/owncloud/android/operations/UploadFileOperation.java b/src/main/java/com/owncloud/android/operations/UploadFileOperation.java index dd5df72fa4..2bc238b6a3 100644 --- a/src/main/java/com/owncloud/android/operations/UploadFileOperation.java +++ b/src/main/java/com/owncloud/android/operations/UploadFileOperation.java @@ -1,4 +1,4 @@ -/** +/* * ownCloud Android client application * * @author David A. Velasco @@ -128,7 +128,7 @@ public class UploadFileOperation extends SyncOperation { * Local path to file which is to be uploaded (before any possible renaming or moving). */ private String mOriginalStoragePath = null; - private Set mDataTransferListeners = new HashSet(); + private Set mDataTransferListeners = new HashSet<>(); private OnRenameListener mRenameUploadListener; private final AtomicBoolean mCancellationRequested = new AtomicBoolean(false); @@ -309,7 +309,7 @@ public class UploadFileOperation extends SyncOperation { return mDataTransferListeners; } - public void addDatatransferProgressListener(OnDatatransferProgressListener listener) { + public void addDataTransferProgressListener(OnDatatransferProgressListener listener) { synchronized (mDataTransferListeners) { mDataTransferListeners.add(listener); } @@ -321,7 +321,7 @@ public class UploadFileOperation extends SyncOperation { } } - public void removeDatatransferProgressListener(OnDatatransferProgressListener listener) { + public void removeDataTransferProgressListener(OnDatatransferProgressListener listener) { synchronized (mDataTransferListeners) { mDataTransferListeners.remove(listener); } @@ -451,35 +451,10 @@ public class UploadFileOperation extends SyncOperation { try { - if (Device.getNetworkType(mContext).equals(JobRequest.NetworkType.ANY) || - ConnectivityUtils.isInternetWalled(mContext)) { - return new RemoteOperationResult(ResultCode.NO_NETWORK_CONNECTION); - } - - /// Check that connectivity conditions are met and delays the upload otherwise - if (mOnWifiOnly && !Device.getNetworkType(mContext).equals(JobRequest.NetworkType.UNMETERED)) { - Log_OC.d(TAG, "Upload delayed until WiFi is available: " + getRemotePath()); - return new RemoteOperationResult(ResultCode.DELAYED_FOR_WIFI); - } - - // Check if charging conditions are met and delays the upload otherwise - if (mWhileChargingOnly && (!Device.getBatteryStatus(mContext).isCharging() && Device.getBatteryStatus - (mContext).getBatteryPercent() < 1)) { - Log_OC.d(TAG, "Upload delayed until the device is charging: " + getRemotePath()); - return new RemoteOperationResult(ResultCode.DELAYED_FOR_CHARGING); - } - - // Check that device is not in power save mode - if (!mIgnoringPowerSaveMode && PowerUtils.isPowerSaveMode(mContext)) { - Log_OC.d(TAG, "Upload delayed because device is in power save mode: " + getRemotePath()); - return new RemoteOperationResult(ResultCode.DELAYED_IN_POWER_SAVE_MODE); - } - - /// check if the file continues existing before schedule the operation - if (!originalFile.exists()) { - Log_OC.d(TAG, mOriginalStoragePath + " not exists anymore"); - return new RemoteOperationResult(ResultCode.LOCAL_FILE_NOT_FOUND); - } + // check conditions + result = checkConditions (originalFile); + + /***** E2E *****/ // Lock folder LockFileOperation lockFileOperation = new LockFileOperation(parentFile.getLocalId()); @@ -525,48 +500,24 @@ public class UploadFileOperation extends SyncOperation { throw new Exception("something wrong"); } - /// automatic rename of file to upload in case of name collision in server - Log_OC.d(TAG, "Checking name collision in server"); - if (!mForceOverwrite) { - String remotePath = getAvailableRemotePath(client, mRemotePath, metadata, true); - mWasRenamed = !remotePath.equals(mRemotePath); - if (mWasRenamed) { - createNewOCFile(remotePath); - Log_OC.d(TAG, "File renamed as " + remotePath); - } - mRemotePath = remotePath; - mRenameUploadListener.onRenameUpload(); - } + /***** E2E *****/ - if (mCancellationRequested.get()) { - throw new OperationCancelledException(); - } + // check name collision + checkNameCollision(client); String expectedPath = FileStorageUtils.getDefaultSavePathFor(mAccount.name, mFile); expectedFile = new File(expectedPath); - /// copy the file locally before uploading - if (mLocalBehaviour == FileUploader.LOCAL_BEHAVIOUR_COPY && - !mOriginalStoragePath.equals(expectedPath)) { - - String temporalPath = FileStorageUtils.getTemporalPath(mAccount.name) + mFile.getRemotePath(); - mFile.setStoragePath(temporalPath); - temporalFile = new File(temporalPath); - - result = copy(originalFile, temporalFile); - if (result != null) { - return result; - } - } - - if (mCancellationRequested.get()) { - throw new OperationCancelledException(); + result = copyFile(originalFile, expectedPath); + if (result != null) { + return result; } // Get the last modification date of the file from the file system Long timeStampLong = originalFile.lastModified() / 1000; String timeStamp = timeStampLong.toString(); + /***** E2E *****/ // Key byte[] key = null; @@ -612,10 +563,48 @@ public class UploadFileOperation extends SyncOperation { fileOutputStream.write(encryptedFile.encryptedBytes); fileOutputStream.close(); + /***** E2E *****/ + + FileChannel channel = null; + try { + channel = new RandomAccessFile(mFile.getStoragePath(), "rw").getChannel(); + fileLock = channel.tryLock(); + } catch (FileNotFoundException e) { + // this basically means that the file is on SD card + // try to copy file to temporary dir if it doesn't exist + String temporalPath = FileStorageUtils.getTemporalPath(mAccount.name) + mFile.getRemotePath(); + mFile.setStoragePath(temporalPath); + temporalFile = new File(temporalPath); + + Files.deleteIfExists(Paths.get(temporalPath)); + result = copy(originalFile, temporalFile); + + if (result == null) { + if (temporalFile.length() == originalFile.length()) { + channel = new RandomAccessFile(temporalFile.getAbsolutePath(), "rw").getChannel(); + fileLock = channel.tryLock(); + } else { + result = new RemoteOperationResult(ResultCode.LOCK_FAILED); + } + } + } + + try { + size = channel.size(); + } catch (IOException e1) { + size = new File(mFile.getStoragePath()).length(); + } + + for (OCUpload ocUpload : uploadsStorageManager.getAllStoredUploads()) { + if (ocUpload.getUploadId() == getOCUploadId()) { + ocUpload.setFileSize(size); + uploadsStorageManager.updateUpload(ocUpload); + break; + } + } + /// perform the upload - if (mChunked && - (new File(mFile.getStoragePath())).length() > - ChunkedUploadRemoteFileOperation.CHUNK_SIZE) { + if (mChunked && (size > ChunkedUploadRemoteFileOperation.CHUNK_SIZE)) { mUploadOperation = new ChunkedUploadRemoteFileOperation(mContext, encryptedTempFile.getAbsolutePath(), mFile.getParentRemotePath() + encryptedFileName, mFile.getMimetype(), mFile.getEtagInConflict(), timeStamp); @@ -674,11 +663,14 @@ public class UploadFileOperation extends SyncOperation { // } result = mUploadOperation.execute(client); + if (result == null || result.isSuccess() && mUploadOperation != null) { + result = mUploadOperation.execute(client); - /// move local temporal file or original file to its corresponding - // location in the ownCloud local folder - if (!result.isSuccess() && result.getHttpCode() == HttpStatus.SC_PRECONDITION_FAILED) { - result = new RemoteOperationResult(ResultCode.SYNC_CONFLICT); + /// move local temporal file or original file to its corresponding + // location in the Nextcloud local folder + if (!result.isSuccess() && result.getHttpCode() == HttpStatus.SC_PRECONDITION_FAILED) { + result = new RemoteOperationResult(ResultCode.SYNC_CONFLICT); + } } if (result.isSuccess()) { @@ -725,7 +717,6 @@ public class UploadFileOperation extends SyncOperation { result = new RemoteOperationResult(ResultCode.LOCK_FAILED); } catch (Exception e) { result = new RemoteOperationResult(e); - } finally { mUploadStarted.set(false); @@ -753,6 +744,7 @@ public class UploadFileOperation extends SyncOperation { if (result == null) { result = new RemoteOperationResult(ResultCode.UNKNOWN_ERROR); } + if (result.isSuccess()) { Log_OC.i(TAG, "Upload of " + mFile.getStoragePath() + " to " + mFile.getRemotePath() + ": " + result.getLogMessage()); @@ -823,9 +815,51 @@ public class UploadFileOperation extends SyncOperation { break; } + // TODO +// if (result.isSuccess()) { +// handleSuccessfulUpload(temporalFile, expectedFile, originalFile, client); +// } else if (result.getCode() == ResultCode.SYNC_CONFLICT) { +// getStorageManager().saveConflict(mFile, mFile.getEtagInConflict()); +// } + return result; } + private RemoteOperationResult checkConditions(File originalFile) { + if (Device.getNetworkType(mContext).equals(JobRequest.NetworkType.ANY) || + ConnectivityUtils.isInternetWalled(mContext)) { + return new RemoteOperationResult(ResultCode.NO_NETWORK_CONNECTION); + } + + /// Check that connectivity conditions are met and delays the upload otherwise + // TODO verify if this can be deleted + if (mOnWifiOnly && !Device.getNetworkType(mContext).equals(JobRequest.NetworkType.UNMETERED)) { + Log_OC.d(TAG, "Upload delayed until WiFi is available: " + getRemotePath()); + return new RemoteOperationResult(ResultCode.DELAYED_FOR_WIFI); + } + + // TODO verify if this can be deleted + // Check if charging conditions are met and delays the upload otherwise + if (mWhileChargingOnly && !Device.isCharging(mContext)) { + Log_OC.d(TAG, "Upload delayed until the device is charging: " + getRemotePath()); + return new RemoteOperationResult(ResultCode.DELAYED_FOR_CHARGING); + } + + // Check that device is not in power save mode + if (!mIgnoringPowerSaveMode && UploadUtils.isPowerSaveMode(mContext)) { + Log_OC.d(TAG, "Upload delayed because device is in power save mode: " + getRemotePath()); + return new RemoteOperationResult(ResultCode.DELAYED_IN_POWER_SAVE_MODE); + } + + /// check if the file continues existing before schedule the operation + if (!originalFile.exists()) { + Log_OC.d(TAG, mOriginalStoragePath + " not exists anymore"); + return new RemoteOperationResult(ResultCode.LOCAL_FILE_NOT_FOUND); + } + + return null; + } + private RemoteOperationResult normalUpload(OwnCloudClient client) { RemoteOperationResult result = null; File temporalFile = null; @@ -835,60 +869,18 @@ public class UploadFileOperation extends SyncOperation { long size = 0; try { - /// Check that connectivity conditions are met and delays the upload otherwise - if (mOnWifiOnly && !Device.getNetworkType(mContext).equals(JobRequest.NetworkType.UNMETERED)) { - Log_OC.d(TAG, "Upload delayed until WiFi is available: " + getRemotePath()); - return new RemoteOperationResult(ResultCode.DELAYED_FOR_WIFI); - } + // check conditions + result = checkConditions(originalFile); - // Check if charging conditions are met and delays the upload otherwise - if (mWhileChargingOnly && !Device.isCharging(mContext)) { - Log_OC.d(TAG, "Upload delayed until the device is charging: " + getRemotePath()); - return new RemoteOperationResult(ResultCode.DELAYED_FOR_CHARGING); - } - - /// check if the file continues existing before schedule the operation - if (!originalFile.exists()) { - Log_OC.d(TAG, mOriginalStoragePath + " not exists anymore"); - return new RemoteOperationResult(ResultCode.LOCAL_FILE_NOT_FOUND); - } - - /// automatic rename of file to upload in case of name collision in server - Log_OC.d(TAG, "Checking name collision in server"); - if (!mForceOverwrite) { - String remotePath = getAvailableRemotePath(client, mRemotePath, null, false); - mWasRenamed = !remotePath.equals(mRemotePath); - if (mWasRenamed) { - createNewOCFile(remotePath); - Log_OC.d(TAG, "File renamed as " + remotePath); - } - mRemotePath = remotePath; - mRenameUploadListener.onRenameUpload(); - } - - if (mCancellationRequested.get()) { - throw new OperationCancelledException(); - } + // check name collision + checkNameCollision(client); String expectedPath = FileStorageUtils.getDefaultSavePathFor(mAccount.name, mFile); expectedFile = new File(expectedPath); - /// copy the file locally before uploading - if (mLocalBehaviour == FileUploader.LOCAL_BEHAVIOUR_COPY && - !mOriginalStoragePath.equals(expectedPath)) { - - String temporalPath = FileStorageUtils.getTemporalPath(mAccount.name) + mFile.getRemotePath(); - mFile.setStoragePath(temporalPath); - temporalFile = new File(temporalPath); - - result = copy(originalFile, temporalFile); - if (result != null) { - return result; - } - } - - if (mCancellationRequested.get()) { - throw new OperationCancelledException(); + result = copyFile(originalFile, expectedPath); + if (result != null) { + return result; } // Get the last modification date of the file from the file system @@ -933,13 +925,11 @@ public class UploadFileOperation extends SyncOperation { } } - /// perform the upload - if (mChunked && - (size > ChunkedUploadRemoteFileOperation.CHUNK_SIZE)) { + // perform the upload + if (mChunked && (size > ChunkedUploadRemoteFileOperation.CHUNK_SIZE)) { mUploadOperation = new ChunkedUploadRemoteFileOperation(mContext, mFile.getStoragePath(), mFile.getRemotePath(), mFile.getMimetype(), mFile.getEtagInConflict(), timeStamp); } else { - mUploadOperation = new UploadRemoteFileOperation(mFile.getStoragePath(), mFile.getRemotePath(), mFile.getMimetype(), mFile.getEtagInConflict(), timeStamp); } @@ -957,14 +947,11 @@ public class UploadFileOperation extends SyncOperation { result = mUploadOperation.execute(client); /// move local temporal file or original file to its corresponding - // location in the ownCloud local folder + // location in the Nextcloud local folder if (!result.isSuccess() && result.getHttpCode() == HttpStatus.SC_PRECONDITION_FAILED) { result = new RemoteOperationResult(ResultCode.SYNC_CONFLICT); } - } - - } catch (FileNotFoundException e) { Log_OC.d(TAG, mOriginalStoragePath + " not exists anymore"); result = new RemoteOperationResult(ResultCode.LOCAL_FILE_NOT_FOUND); @@ -973,7 +960,6 @@ public class UploadFileOperation extends SyncOperation { result = new RemoteOperationResult(ResultCode.LOCK_FAILED); } catch (Exception e) { result = new RemoteOperationResult(e); - } finally { mUploadStarted.set(false); @@ -992,6 +978,7 @@ public class UploadFileOperation extends SyncOperation { if (result == null) { result = new RemoteOperationResult(ResultCode.UNKNOWN_ERROR); } + if (result.isSuccess()) { Log_OC.i(TAG, "Upload of " + mOriginalStoragePath + " to " + mRemotePath + ": " + result.getLogMessage()); @@ -1004,7 +991,6 @@ public class UploadFileOperation extends SyncOperation { Log_OC.e(TAG, "Upload of " + mOriginalStoragePath + " to " + mRemotePath + ": " + result.getLogMessage(), result.getException()); } - } else { Log_OC.e(TAG, "Upload of " + mOriginalStoragePath + " to " + mRemotePath + ": " + result.getLogMessage()); @@ -1013,9 +999,58 @@ public class UploadFileOperation extends SyncOperation { } if (result.isSuccess()) { + handleSuccessfulUpload(temporalFile, expectedFile, originalFile, client); + } else if (result.getCode() == ResultCode.SYNC_CONFLICT) { + getStorageManager().saveConflict(mFile, mFile.getEtagInConflict()); + } - if (mLocalBehaviour == FileUploader.LOCAL_BEHAVIOUR_FORGET) { + return result; + } + private RemoteOperationResult copyFile(File originalFile, String expectedPath) throws OperationCancelledException, + IOException { + RemoteOperationResult result = null; + + if (mLocalBehaviour == FileUploader.LOCAL_BEHAVIOUR_COPY && !mOriginalStoragePath.equals(expectedPath)) { + String temporalPath = FileStorageUtils.getTemporalPath(mAccount.name) + mFile.getRemotePath(); + mFile.setStoragePath(temporalPath); + File temporalFile = new File(temporalPath); + + result = copy(originalFile, temporalFile); + } + + if (mCancellationRequested.get()) { + throw new OperationCancelledException(); + } + + return result; + } + + private void checkNameCollision(OwnCloudClient client) throws OperationCancelledException { + /// automatic rename of file to upload in case of name collision in server + Log_OC.d(TAG, "Checking name collision in server"); + if (!mForceOverwrite) { + // TODO encrypted always false? + String remotePath = getAvailableRemotePath(client, mRemotePath, null, false); + mWasRenamed = !remotePath.equals(mRemotePath); + if (mWasRenamed) { + createNewOCFile(remotePath); + Log_OC.d(TAG, "File renamed as " + remotePath); + } + mRemotePath = remotePath; + mRenameUploadListener.onRenameUpload(); + } + + if (mCancellationRequested.get()) { + throw new OperationCancelledException(); + } + } + + private void handleSuccessfulUpload(File temporalFile, File expectedFile, File originalFile, + OwnCloudClient client) { + switch (mLocalBehaviour) { + case FileUploader.LOCAL_BEHAVIOUR_FORGET: + default: String temporalPath = FileStorageUtils.getTemporalPath(mAccount.name) + mFile.getRemotePath(); if (mOriginalStoragePath.equals(temporalPath)) { // delete local file is was pre-copied in temporary folder (see .ui.helpers.UriUploader) @@ -1024,37 +1059,44 @@ public class UploadFileOperation extends SyncOperation { } mFile.setStoragePath(""); saveUploadedFile(client); + break; + case FileUploader.LOCAL_BEHAVIOUR_DELETE: + Log_OC.d(TAG, "Delete source file"); - } else if (mLocalBehaviour == FileUploader.LOCAL_BEHAVIOUR_DELETE) { originalFile.delete(); getStorageManager().deleteFileInMediaScan(originalFile.getAbsolutePath()); saveUploadedFile(client); - } else { + break; - if (temporalFile != null) { // FileUploader.LOCAL_BEHAVIOUR_COPY + case FileUploader.LOCAL_BEHAVIOUR_COPY: + if (temporalFile != null) { try { move(temporalFile, expectedFile); } catch (IOException e) { e.printStackTrace(); } - } else { // FileUploader.LOCAL_BEHAVIOUR_MOVE - try { - move(originalFile, expectedFile); - } catch (IOException e) { - e.printStackTrace(); - } - getStorageManager().deleteFileInMediaScan(originalFile.getAbsolutePath()); } mFile.setStoragePath(expectedFile.getAbsolutePath()); saveUploadedFile(client); FileDataStorageManager.triggerMediaScan(expectedFile.getAbsolutePath()); - } - } else if (result.getCode() == ResultCode.SYNC_CONFLICT) { - getStorageManager().saveConflict(mFile, mFile.getEtagInConflict()); - } + break; - return result; + case FileUploader.LOCAL_BEHAVIOUR_MOVE: + String expectedPath = FileStorageUtils.getDefaultSavePathFor(mAccount.name, mFile); + expectedFile = new File(expectedPath); + + try { + move(originalFile, expectedFile); + } catch (IOException e) { + e.printStackTrace(); + } + getStorageManager().deleteFileInMediaScan(originalFile.getAbsolutePath()); + mFile.setStoragePath(expectedFile.getAbsolutePath()); + saveUploadedFile(client); + FileDataStorageManager.triggerMediaScan(expectedFile.getAbsolutePath()); + break; + } } /** @@ -1138,20 +1180,20 @@ public class UploadFileOperation extends SyncOperation { * Checks if remotePath does not exist in the server and returns it, or adds * a suffix to it in order to avoid the server file is overwritten. * - * @param wc - * @param remotePath - * @param metadata - * @return + * @param client OwnCloud client + * @param remotePath remote path of the file + * @param metadata metadata of encrypted folder + * @return new remote path */ - private String getAvailableRemotePath(OwnCloudClient wc, String remotePath, DecryptedFolderMetadata metadata, + private String getAvailableRemotePath(OwnCloudClient client, String remotePath, DecryptedFolderMetadata metadata, boolean encrypted) { - boolean check = existsFile(wc, remotePath, metadata, encrypted); + boolean check = existsFile(client, remotePath, metadata, encrypted); if (!check) { return remotePath; } int pos = remotePath.lastIndexOf('.'); - String suffix = ""; + String suffix; String extension = ""; String remotePathWithoutExtension = ""; if (pos >= 0) { @@ -1162,9 +1204,9 @@ public class UploadFileOperation extends SyncOperation { do { suffix = " (" + count + ")"; if (pos >= 0) { - check = existsFile(wc, remotePathWithoutExtension + suffix + "." + extension, metadata, encrypted); + check = existsFile(client, remotePathWithoutExtension + suffix + "." + extension, metadata, encrypted); } else { - check = existsFile(wc, remotePath + suffix, metadata, encrypted); + check = existsFile(client, remotePath + suffix, metadata, encrypted); } count++; } while (check); @@ -1189,8 +1231,8 @@ public class UploadFileOperation extends SyncOperation { return false; } else { - ExistenceCheckRemoteOperation existsOperation = - new ExistenceCheckRemoteOperation(remotePath, mContext, false); + ExistenceCheckRemoteOperation existsOperation = new ExistenceCheckRemoteOperation(remotePath, mContext, + false); RemoteOperationResult result = existsOperation.execute(client); return result.isSuccess(); } @@ -1230,7 +1272,7 @@ public class UploadFileOperation extends SyncOperation { * @param sourceFile Source file to copy. * @param targetFile Target location to copy the file. * @return {@link RemoteOperationResult} - * @throws IOException + * @throws IOException exception if file cannot be accessed */ private RemoteOperationResult copy(File sourceFile, File targetFile) throws IOException { Log_OC.d(TAG, "Copying local file"); @@ -1315,13 +1357,13 @@ public class UploadFileOperation extends SyncOperation { /** * TODO rewrite with homogeneous fail handling, remove dependency on {@link RemoteOperationResult}, * TODO use Exceptions instead - * + *

* TODO refactor both this and 'copy' in a single method * * @param sourceFile Source file to move. * @param targetFile Target location to move the file. - * @return {@link RemoteOperationResult} - * @throws IOException + * @return {@link RemoteOperationResult} result from remote operation + * @throws IOException exception if file cannot be read/wrote */ private void move(File sourceFile, File targetFile) throws IOException { @@ -1360,11 +1402,10 @@ public class UploadFileOperation extends SyncOperation { /** * Saves a OC File after a successful upload. - *

+ *

* A PROPFIND is necessary to keep the props in the local database * synchronized with the server, specially the modification time and Etag * (where available) - *

*/ private void saveUploadedFile(OwnCloudClient client) { OCFile file = mFile;