From ef6f81f8ffcebaa69a408c3ac800d18d69664d2f Mon Sep 17 00:00:00 2001 From: vincent Bourgmayer Date: Mon, 30 May 2022 17:12:26 +0200 Subject: [PATCH 01/59] Update gradle file --build.gradle-- - Replace Jcenter() by MavenCentral() - Add Jetpack dependency --app/build.gradle-- - Add official NCLib dependency & apache http client instead - remove dependency to submodule --settings.gradle-- - remove sutff related to submodule --- app/build.gradle | 3 ++- build.gradle | 9 +++------ settings.gradle | 3 +-- 3 files changed, 6 insertions(+), 9 deletions(-) diff --git a/app/build.gradle b/app/build.gradle index 6af3f945..77cda802 100644 --- a/app/build.gradle +++ b/app/build.gradle @@ -63,7 +63,8 @@ android { dependencies { - api project(':NextcloudLib') + implementation 'com.github.nextcloud:android-library:2.10.1' + implementation "commons-httpclient:commons-httpclient:3.1@jar" implementation fileTree(include: ['*.jar'], dir: 'libs') api 'androidx.annotation:annotation:1.3.0' implementation 'androidx.core:core:1.6.0' diff --git a/build.gradle b/build.gradle index 3378f0cd..9bd02054 100644 --- a/build.gradle +++ b/build.gradle @@ -4,21 +4,18 @@ buildscript { repositories { google() - jcenter() + mavenCentral() } dependencies { classpath 'com.android.tools.build:gradle:4.1.3' - - - // NOTE: Do not place your application dependencies here; they belong - // in the individual module build.gradle files } } allprojects { repositories { google() - jcenter() + mavenCentral() + maven { url "https://jitpack.io" } } } diff --git a/settings.gradle b/settings.gradle index 19a2fc3d..9d495b34 100644 --- a/settings.gradle +++ b/settings.gradle @@ -1,2 +1 @@ -include ':app', ':NextcloudLib' -project(':NextcloudLib').projectDir = new File('nextcloud-android-lib') \ No newline at end of file +include ':app' \ No newline at end of file -- GitLab From 04e0e3f320a4b60b19438b45574ba1a9793b9bd8 Mon Sep 17 00:00:00 2001 From: vincent Bourgmayer Date: Mon, 30 May 2022 17:29:55 +0200 Subject: [PATCH 02/59] Remove submodule (custom fork of Nextcloud Android Library) - Update .idea/vcs.xml - remove nextcloud-lib submodule - remove .gitmodules - remove useless DownloadFileRemoteOperation.java from operations package --- .gitmodules | 3 - .idea/vcs.xml | 1 - .../DownloadFileRemoteOperation.java | 188 ------------------ nextcloud-android-lib | 1 - 4 files changed, 193 deletions(-) delete mode 100644 .gitmodules delete mode 100644 app/src/main/java/foundation/e/drive/operations/DownloadFileRemoteOperation.java delete mode 160000 nextcloud-android-lib diff --git a/.gitmodules b/.gitmodules deleted file mode 100644 index 34e74166..00000000 --- a/.gitmodules +++ /dev/null @@ -1,3 +0,0 @@ -[submodule "nextcloud-android-lib"] - path = nextcloud-android-lib - url = ../nextcloud-android-lib.git \ No newline at end of file diff --git a/.idea/vcs.xml b/.idea/vcs.xml index 0bda0242..35eb1ddf 100644 --- a/.idea/vcs.xml +++ b/.idea/vcs.xml @@ -2,6 +2,5 @@ - \ No newline at end of file diff --git a/app/src/main/java/foundation/e/drive/operations/DownloadFileRemoteOperation.java b/app/src/main/java/foundation/e/drive/operations/DownloadFileRemoteOperation.java deleted file mode 100644 index 107b6ff1..00000000 --- a/app/src/main/java/foundation/e/drive/operations/DownloadFileRemoteOperation.java +++ /dev/null @@ -1,188 +0,0 @@ -/* ownCloud Android Library is available under MIT license - * Copyright (C) 2015 ownCloud Inc. - * Copyright © CLEUS SAS 2018-2019. - * Copyright © ECORP SAS 2022. - * Permission is hereby granted, free of charge, to any person obtaining a copy - * of this software and associated documentation files (the "Software"), to deal - * in the Software without restriction, including without limitation the rights - * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell - * copies of the Software, and to permit persons to whom the Software is - * furnished to do so, subject to the following conditions: - * - * The above copyright notice and this permission notice shall be included in - * all copies or substantial portions of the Software. - * - * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, - * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF - * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND - * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS - * BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN - * ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN - * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN - * THE SOFTWARE. - * - */ -package foundation.e.drive.operations; - -import android.util.Log; - -import com.owncloud.android.lib.common.OwnCloudClient; -import com.owncloud.android.lib.common.network.WebdavUtils; -import com.owncloud.android.lib.common.operations.OperationCancelledException; -import com.owncloud.android.lib.common.operations.RemoteOperation; -import com.owncloud.android.lib.common.operations.RemoteOperationResult; -import com.owncloud.android.lib.common.utils.Log_OC; -import org.apache.commons.httpclient.Header; -import org.apache.commons.httpclient.HttpStatus; -import org.apache.commons.httpclient.methods.GetMethod; -import java.io.BufferedInputStream; -import java.io.File; -import java.io.FileOutputStream; -import java.io.IOException; -import java.util.Date; -import java.util.concurrent.atomic.AtomicBoolean; - -/** - * Remote operation performing the download of a remote file in the ownCloud server. - * - * @author David A. Velasco - * @author masensio - * @author Vincent Bourgmayer - */ -class DownloadFileRemoteOperation extends RemoteOperation { - private static final String TAG = DownloadFileRemoteOperation.class.getSimpleName(); - - private final AtomicBoolean mCancellationRequested = new AtomicBoolean(false); - private long mModificationTimestamp = 0; - private String mEtag = ""; - private GetMethod mGet; - private final String mRemotePath; - private final String mLocalFolderPath; - - /** - * Constructor - * @param remotePath Path of file on the server - * @param localFolderPath Path of file on the device - */ - public DownloadFileRemoteOperation(String remotePath, String localFolderPath) { - mRemotePath = remotePath; - mLocalFolderPath = localFolderPath; - } - - @Override - protected RemoteOperationResult run(OwnCloudClient client) { - RemoteOperationResult result = null; - /// download will be performed to a temporal file, then moved to the final location - File tmpFile = new File(getTmpPath()); - /// perform the download - try { - File parentFile = tmpFile.getParentFile(); - if (parentFile == null) { - Log.e(TAG, "getParentFile() returned null. Returning to prevent a NPE"); - return new RemoteOperationResult(RemoteOperationResult.ResultCode.UNKNOWN_ERROR); - } - - parentFile.mkdirs(); - int status = downloadFile(client, tmpFile); - result = new RemoteOperationResult(isSuccess(status), mGet); - Log_OC.i(TAG, "Download of " + mRemotePath + " to " + getTmpPath() + ": " + - result.getLogMessage()); - - } catch (Exception e) { - result = new RemoteOperationResult(e); - Log_OC.e(TAG, "Download of " + mRemotePath + " to " + getTmpPath() + ": " + - result.getLogMessage(), e); - } - - return result; - } - - private int downloadFile(OwnCloudClient client, File targetFile) throws IOException, OperationCancelledException { - int status = -1; - boolean savedFile = false; - mGet = new GetMethod(client.getWebdavUri() + WebdavUtils.encodePath(mRemotePath)); - - FileOutputStream fos = null; - try { - status = client.executeMethod(mGet); - if (isSuccess(status)) { - targetFile.createNewFile(); - BufferedInputStream bis = new BufferedInputStream(mGet.getResponseBodyAsStream()); - fos = new FileOutputStream(targetFile); - long transferred = 0; - - Header contentLength = mGet.getResponseHeader("Content-Length"); - long totalToTransfer = (contentLength != null && - contentLength.getValue().length() > 0) ? - Long.parseLong(contentLength.getValue()) : 0; - - byte[] bytes = new byte[4096]; - int readResult = 0; - while ((readResult = bis.read(bytes)) != -1) { - synchronized (mCancellationRequested) { - if (mCancellationRequested.get()) { - mGet.abort(); - throw new OperationCancelledException(); - } - } - fos.write(bytes, 0, readResult); - transferred += readResult; - } - // Check if the file is completed - // if transfer-encoding: chunked we cannot check if the file is complete - Header transferEncodingHeader = mGet.getResponseHeader("Transfer-Encoding"); - boolean transferEncoding = false; - - if (transferEncodingHeader != null) { - transferEncoding = transferEncodingHeader.getValue().equals("chunked"); - } - - if (transferred == totalToTransfer || transferEncoding) { - savedFile = true; - Header modificationTime = mGet.getResponseHeader("Last-Modified"); - if (modificationTime == null) { - modificationTime = mGet.getResponseHeader("last-modified"); - } - if (modificationTime != null) { - Date d = WebdavUtils.parseResponseDate(modificationTime.getValue()); - mModificationTimestamp = (d != null) ? d.getTime() : 0; - } else { - Log_OC.e(TAG, "Could not read modification time from response downloading " + mRemotePath); - } - - mEtag = WebdavUtils.getEtagFromResponse(mGet); - if (mEtag.length() == 0) { - Log_OC.e(TAG, "Could not read eTag from response downloading " + mRemotePath); - } - - } else { - client.exhaustResponse(mGet.getResponseBodyAsStream()); - // TODO some kind of error control! - } - } else { - client.exhaustResponse(mGet.getResponseBodyAsStream()); - } - } catch (Exception e) { - Log_OC.e(TAG, e.getMessage()); - } finally { - if (fos != null) fos.close(); - if (!savedFile && targetFile.exists()) { - targetFile.delete(); - } - mGet.releaseConnection(); // let the connection available for other methods - } - return status; - } - - private boolean isSuccess(int status) { - return (status == HttpStatus.SC_OK); - } - - /** - * IMPLEMENTATION DIFFER FROM NC IMPLEMENTATION - * @return - */ - private String getTmpPath() { - return mLocalFolderPath; - } -} diff --git a/nextcloud-android-lib b/nextcloud-android-lib deleted file mode 160000 index b043a510..00000000 --- a/nextcloud-android-lib +++ /dev/null @@ -1 +0,0 @@ -Subproject commit b043a5104962a841daf4ca3e59383216eebf7ef0 -- GitLab From 5115ba81a94146a4b28cfbd0420586d70f2a91bc Mon Sep 17 00:00:00 2001 From: vincent Bourgmayer Date: Mon, 30 May 2022 17:38:45 +0200 Subject: [PATCH 03/59] Update foundation.e.drive.work package --CreateRemoteFolderWorker.java-- - Add 'final' keyword to three variables in two method - Remove one parameter for CreateFolderRemoteOperation.execute(...) --AccountUserInfoWorker.java-- - Replace GetUserRemoteInfoOperation by GetUserInfoRemoteOperation - make three properties 'private' - Add 'final' keyword to one variable in 'doWork()' - Update fetchUserInfo() method to use stuff from official lib - update fetchAliases() method to use stuff from official lib --- .../e/drive/work/AccountUserInfoWorker.java | 59 +++++++++---------- .../drive/work/CreateRemoteFolderWorker.java | 8 +-- 2 files changed, 31 insertions(+), 36 deletions(-) diff --git a/app/src/main/java/foundation/e/drive/work/AccountUserInfoWorker.java b/app/src/main/java/foundation/e/drive/work/AccountUserInfoWorker.java index 700338d0..9817efc0 100644 --- a/app/src/main/java/foundation/e/drive/work/AccountUserInfoWorker.java +++ b/app/src/main/java/foundation/e/drive/work/AccountUserInfoWorker.java @@ -31,9 +31,10 @@ import androidx.work.WorkerParameters; import com.bumptech.glide.Glide; import com.bumptech.glide.load.engine.DiskCacheStrategy; import com.owncloud.android.lib.common.OwnCloudClient; +import com.owncloud.android.lib.common.Quota; import com.owncloud.android.lib.common.UserInfo; import com.owncloud.android.lib.common.operations.RemoteOperationResult; -import com.owncloud.android.lib.resources.users.GetRemoteUserInfoOperation; +import com.owncloud.android.lib.resources.users.GetUserInfoRemoteOperation; import java.util.ArrayList; @@ -50,9 +51,9 @@ import foundation.e.drive.widgets.EDriveWidget; */ public class AccountUserInfoWorker extends Worker { public static final String UNIQUE_WORK_NAME = "AccountUserInfoWorker"; - final AccountManager accountManager; - final GetRemoteUserInfoOperation getRemoteUserInfoOperation = new GetRemoteUserInfoOperation(); - final GetAliasOperation getAliasOperation = new GetAliasOperation(); + private final AccountManager accountManager; + private final GetUserInfoRemoteOperation GetUserInfoRemoteOperation = new GetUserInfoRemoteOperation(); + private final GetAliasOperation getAliasOperation = new GetAliasOperation(); private final Context mContext; private Account account; @@ -67,7 +68,7 @@ public class AccountUserInfoWorker extends Worker { public Result doWork() { account = CommonUtils.getAccount(mContext.getString(R.string.eelo_account_type), accountManager); - OwnCloudClient client = CommonUtils.getOwnCloudClient(account, mContext); + final OwnCloudClient client = CommonUtils.getOwnCloudClient(account, mContext); if (account != null && client != null) { if (fetchUserInfo(client) && fetchAliases(client)) { @@ -86,32 +87,23 @@ public class AccountUserInfoWorker extends Worker { } private boolean fetchUserInfo(final OwnCloudClient client) { - final RemoteOperationResult ocsResult = getRemoteUserInfoOperation.execute(client); + final RemoteOperationResult ocsResult = GetUserInfoRemoteOperation.execute(client); - if (ocsResult.isSuccess() && ocsResult.getData() != null && !ocsResult.getData().isEmpty()) { - final UserInfo userInfo = (UserInfo) ocsResult.getData().get(0); - - final String name; - if (userInfo.displayName == null || userInfo.displayName.isEmpty()) { - name = userInfo.alternateDisplayName; - } else { - name = userInfo.displayName; - } - final double relativeQuota = userInfo.getQuota().relative; - long totalQuota = userInfo.getQuota().total; + if (ocsResult.isSuccess()) { + final UserInfo userInfo = ocsResult.getResultData(); + final Quota userQuota = userInfo.getQuota(); + final double relativeQuota = userQuota.getRelative(); + long totalQuota = userQuota.getTotal(); if (totalQuota <= 0) { totalQuota = 0; } - final long usedQuota = userInfo.getQuota().used; - final String groups = String.join(",", userInfo.groups); - final String email = userInfo.id; - - accountManager.setUserData(account, ACCOUNT_DATA_NAME, name); - accountManager.setUserData(account, ACCOUNT_DATA_EMAIL, email); + final String groups = String.join(",", userInfo.getGroups()); + accountManager.setUserData(account, ACCOUNT_DATA_NAME, userInfo.getDisplayName()); + accountManager.setUserData(account, ACCOUNT_DATA_EMAIL, userInfo.getEmail()); accountManager.setUserData(account, ACCOUNT_DATA_GROUPS, groups); accountManager.setUserData(account, ACCOUNT_DATA_TOTAL_QUOTA_KEY, "" + totalQuota); accountManager.setUserData(account, ACCOUNT_DATA_RELATIVE_QUOTA_KEY, "" + relativeQuota); - accountManager.setUserData(account, ACCOUNT_DATA_USED_QUOTA_KEY, "" + usedQuota); + accountManager.setUserData(account, ACCOUNT_DATA_USED_QUOTA_KEY, "" + userQuota.getUsed()); addNotifAboutQuota(relativeQuota); return true; @@ -179,14 +171,17 @@ public class AccountUserInfoWorker extends Worker { } private boolean fetchAliases(final OwnCloudClient client) { - final RemoteOperationResult ocsResult = getAliasOperation.execute(client); - String aliases = ""; - if (ocsResult.isSuccess() && ocsResult.getData() != null && !ocsResult.getData().isEmpty()) { - ArrayList alias = new ArrayList<>(ocsResult.getData().size()); - for (Object object : ocsResult.getData()) { - alias.add(object.toString()); - } - aliases = String.join(",", alias); + final RemoteOperationResult> ocsResult = getAliasOperation.execute(client); + if (!ocsResult.isSuccess()) { + return false; + } + + final ArrayList aliasList = ocsResult.getResultData(); + final String aliases; + if (!aliasList.isEmpty()) { + aliases = String.join(",", aliasList); + } else { + aliases = ""; } accountManager.setUserData(account, ACCOUNT_DATA_ALIAS_KEY, aliases); return true; diff --git a/app/src/main/java/foundation/e/drive/work/CreateRemoteFolderWorker.java b/app/src/main/java/foundation/e/drive/work/CreateRemoteFolderWorker.java index d7c27ba3..e510fdaf 100644 --- a/app/src/main/java/foundation/e/drive/work/CreateRemoteFolderWorker.java +++ b/app/src/main/java/foundation/e/drive/work/CreateRemoteFolderWorker.java @@ -85,7 +85,7 @@ public class CreateRemoteFolderWorker extends Worker { new CreateFolderRemoteOperation(syncedFolder.getRemoteFolder(), true); try { - final RemoteOperationResult result = mkcolRequest.execute(client, true); + final RemoteOperationResult result = mkcolRequest.execute(client); if (result.isSuccess() || result.getCode() == RemoteOperationResult.ResultCode.FOLDER_ALREADY_EXISTS) { if(DbHelper.insertSyncedFolder(syncedFolder, context) >= 0 ) { Log.d(TAG, "Insertion in DB succeed"); @@ -99,8 +99,8 @@ public class CreateRemoteFolderWorker extends Worker { } private SyncedFolder getSyncedFolderFromData() { - Data data = getInputData(); - SyncedFolder result = new SyncedFolder( + final Data data = getInputData(); + final SyncedFolder result = new SyncedFolder( data.getString(DATA_KEY_LIBELLE), data.getString(DATA_KEY_LOCAL_PATH), data.getString(DATA_KEY_REMOTE_PATH), @@ -118,7 +118,7 @@ public class CreateRemoteFolderWorker extends Worker { private Account getAccount() { - SharedPreferences prefs = getApplicationContext().getSharedPreferences(AppConstants.SHARED_PREFERENCE_NAME, Context.MODE_PRIVATE); + final SharedPreferences prefs = getApplicationContext().getSharedPreferences(AppConstants.SHARED_PREFERENCE_NAME, Context.MODE_PRIVATE); final String accountName = prefs.getString(AccountManager.KEY_ACCOUNT_NAME, ""); final String accountType = prefs.getString(AccountManager.KEY_ACCOUNT_TYPE, ""); -- GitLab From 74bd7b0787fc3957b2061492742f4a26b57b1aa7 Mon Sep 17 00:00:00 2001 From: vincent Bourgmayer Date: Tue, 31 May 2022 10:16:38 +0200 Subject: [PATCH 04/59] Update UploadFileOperation.java & SynchronizationService.java --UploadFileOperation.java-- - Replace GetRemoteUserInfoOperation by GetUserInfoRemoteOperation - Add 'final' to some variable - Removed property 'available quota' because useless - Add getSyncedFileState() method - UploadFileOperation doesn't provide quota as result anymore - Rewrite some if statement to be cleaner & more readable - remove one parameter from buildUploadOperation(...) --SynchronizatioNService.java-- - Add 'final' to some variable - report change from UploadFileOperation on SynchronizationService --- .../drive/operations/UploadFileOperation.java | 95 +++++++------------ .../services/SynchronizationService.java | 27 ++---- 2 files changed, 40 insertions(+), 82 deletions(-) diff --git a/app/src/main/java/foundation/e/drive/operations/UploadFileOperation.java b/app/src/main/java/foundation/e/drive/operations/UploadFileOperation.java index c1eed28e..8a48dab2 100644 --- a/app/src/main/java/foundation/e/drive/operations/UploadFileOperation.java +++ b/app/src/main/java/foundation/e/drive/operations/UploadFileOperation.java @@ -21,10 +21,9 @@ import com.owncloud.android.lib.common.operations.RemoteOperationResult; import com.owncloud.android.lib.resources.files.CreateFolderRemoteOperation; import com.owncloud.android.lib.resources.files.FileUtils; import com.owncloud.android.lib.resources.files.UploadFileRemoteOperation; -import com.owncloud.android.lib.resources.users.GetRemoteUserInfoOperation; +import com.owncloud.android.lib.resources.users.GetUserInfoRemoteOperation; import com.owncloud.android.lib.common.operations.RemoteOperationResult.ResultCode; import java.io.File; -import java.util.ArrayList; import foundation.e.drive.database.DbHelper; import foundation.e.drive.models.SyncedFileState; import foundation.e.drive.utils.CommonUtils; @@ -40,7 +39,6 @@ public class UploadFileOperation extends RemoteOperation { private long previousLastModified; //get to restore real value if all trials fails private Context context; private SyncedFileState syncedState; - private long availableQuota = -1; /** * Construct an upload operation with an already known syncedFileState @@ -66,12 +64,7 @@ public class UploadFileOperation extends RemoteOperation { @Override protected RemoteOperationResult run(OwnCloudClient client ) { //as operation isn't executed immediatly, file might have been deleted since creation of operation - if (syncedState == null ) { - Log.e(TAG, "run(client): no syncedFileState or target path, can't perform upload operation"); - return new RemoteOperationResult(ResultCode.FORBIDDEN); - } - - File file = new File(syncedState.getLocalPath()); + final File file = new File(syncedState.getLocalPath()); if (file == null || !file.exists()) { Log.w(TAG, "Can't get the file. It might have been deleted"); return new RemoteOperationResult(ResultCode.FORBIDDEN); @@ -79,39 +72,34 @@ public class UploadFileOperation extends RemoteOperation { final String targetPath = syncedState.getRemotePath(); - //If an Etag is already Stored and LastModified from DB is the same as real file + //If file already up-to-date & synced if (syncedState.isLastEtagStored() && syncedState.getLocalLastModified() == file.lastModified()) { Log.d(TAG, "syncedState last modified: "+ syncedState.getLocalLastModified()+" <=> file last modified: "+file.lastModified() +": So return sync_conflict"); return new RemoteOperationResult(ResultCode.SYNC_CONFLICT); } - Float relativeQuotaBeforeFileUpload = 0.0f; - if (this.availableQuota == -1) { - RemoteOperationResult checkQuotaResult = checkAvailableSpace(client, file.length()); - if (checkQuotaResult.getCode() != ResultCode.OK) { - return new RemoteOperationResult(checkQuotaResult.getCode()); - } else { - relativeQuotaBeforeFileUpload = ((Double) checkQuotaResult.getSingleData()).floatValue(); - } + final RemoteOperationResult checkQuotaResult = checkAvailableSpace(client, file.length()); + if (checkQuotaResult.getCode() != ResultCode.OK) { + Log.e(TAG, "Impossible to check quota. Upload of " + syncedState.getLocalPath() + "cancelled"); + return new RemoteOperationResult(checkQuotaResult.getCode()); } if (!createRemoteFolder(targetPath, client)) { return new RemoteOperationResult(ResultCode.UNKNOWN_ERROR); } - final UploadFileRemoteOperation uploadOperation = buildUploadOperation(file, targetPath); + final UploadFileRemoteOperation uploadOperation = buildUploadOperation(file); // Execute UploadFileOperation - RemoteOperationResult uploadResult = uploadOperation.execute(client ); - ResultCode resultCode; + final RemoteOperationResult uploadResult = uploadOperation.execute(client); + final ResultCode resultCode; boolean mustRestart = true; - //if upload is a success if (uploadResult.isSuccess()) { - Object data = uploadResult.getSingleData(); - if (data != null) { - syncedState.setLastETAG((String) data); + final String etag = uploadResult.getResultData(); + if (etag != null) { + syncedState.setLastETAG(etag); } syncedState.setLocalLastModified(file.lastModified()); resultCode = uploadResult.getCode(); @@ -125,7 +113,6 @@ public class UploadFileOperation extends RemoteOperation { resultCode = ResultCode.QUOTA_EXCEEDED; mustRestart = false; } else { - //Upload failed Log.e(TAG, "UploadFileRemoteOperation for : " + file.getName() + " failed => code: " + uploadResult.getCode()); resultCode = ResultCode.UNKNOWN_ERROR; mustRestart = false; @@ -135,38 +122,27 @@ public class UploadFileOperation extends RemoteOperation { if (mustRestart) { if (this.restartCounter < 1) { this.restartCounter += 1; - //if we encounter more than one time same error, stop trying to upload. return this.run(client); } else { syncedState.setLocalLastModified(this.previousLastModified); //Revert syncFileState to its previous state } } - // updated syncedFile in DB DbHelper.manageSyncedFileStateDB(syncedState, "UPDATE", context); - - ArrayList datas = new ArrayList<>(); - datas.add(syncedState.getSyncedFolderId()); - datas.add(relativeQuotaBeforeFileUpload); - final RemoteOperationResult finalResult = new RemoteOperationResult(resultCode); - finalResult.setData(datas); - return finalResult; + return new RemoteOperationResult(resultCode); } /** * Build the operation to put the file on server * @return the operation to execute */ - private UploadFileRemoteOperation buildUploadOperation(File file, String targetPath) { - String timeStamp = ((Long) (file.lastModified() / 1000) ).toString() ; - - //create UploadFileOperation - UploadFileRemoteOperation uploadRemoteFileOperation = new UploadFileRemoteOperation(syncedState.getLocalPath(), - (targetPath != null ) ? targetPath : syncedState.getRemotePath(), - CommonUtils.getMimeType(file ), - (!this.syncedState.isMediaType() || syncedState.getLastETAG().isEmpty() )? null : syncedState.getLastETAG(), //If not null, This can cause error 412; that means remote file has change + private UploadFileRemoteOperation buildUploadOperation(File file) { + final String timeStamp = ((Long) (file.lastModified() / 1000) ).toString() ; + + return new UploadFileRemoteOperation(syncedState.getLocalPath(), + syncedState.getRemotePath(), + CommonUtils.getMimeType(file), + (!this.syncedState.isMediaType() || syncedState.getLastETAG().isEmpty())? null : syncedState.getLastETAG(), //If not null, This can cause error 412; that means remote file has change timeStamp ); - uploadRemoteFileOperation.askResultEtag(true); - return uploadRemoteFileOperation; } /** @@ -176,24 +152,17 @@ public class UploadFileOperation extends RemoteOperation { */ @VisibleForTesting(otherwise = VisibleForTesting.PRIVATE) public RemoteOperationResult checkAvailableSpace(OwnCloudClient client, long fileSize) { - GetRemoteUserInfoOperation getRemoteUserInfoOperation = new GetRemoteUserInfoOperation(); - RemoteOperationResult ocsResult = getRemoteUserInfoOperation.execute(client); - if (ocsResult.isSuccess() && ocsResult.getData() != null) { - UserInfo userInfo = (UserInfo) ocsResult.getData().get(0); - this.availableQuota = userInfo.getQuota().getFree(); - if (((UserInfo) ocsResult.getData().get(0)).getQuota().getFree() < fileSize ) { - Log.w(TAG, "quota exceeded!"); - return new RemoteOperationResult(ResultCode.QUOTA_EXCEEDED); - } else { - Log.d(TAG, "Quota Okay"); - RemoteOperationResult result = new RemoteOperationResult(ResultCode.OK); - result.setSingleData((Double) userInfo.getQuota().getRelative()); - return result; - } + final GetUserInfoRemoteOperation GetUserInfoRemoteOperation = new GetUserInfoRemoteOperation(); + final RemoteOperationResult ocsResult = GetUserInfoRemoteOperation.execute(client); + final UserInfo userInfo = ocsResult.getResultData(); + final RemoteOperationResult result; + if (ocsResult.isSuccess() && userInfo.getQuota().getFree() < fileSize) { + Log.w(TAG, "Not enough quota to upload the file"); + result = new RemoteOperationResult(ResultCode.QUOTA_EXCEEDED); } else { - Log.w(TAG, "getRemoteUserInfoOperation failed: "+ocsResult.getHttpCode() ); - return new RemoteOperationResult(ocsResult.getCode()); + result = new RemoteOperationResult(ocsResult.getCode()); } + return result; } @@ -217,4 +186,8 @@ public class UploadFileOperation extends RemoteOperation { } return false; } + + public SyncedFileState getSyncedState() { + return syncedState; + } } diff --git a/app/src/main/java/foundation/e/drive/services/SynchronizationService.java b/app/src/main/java/foundation/e/drive/services/SynchronizationService.java index da005cc9..c9d932c7 100644 --- a/app/src/main/java/foundation/e/drive/services/SynchronizationService.java +++ b/app/src/main/java/foundation/e/drive/services/SynchronizationService.java @@ -174,7 +174,6 @@ public class SynchronizationService extends Service implements OnRemoteOperation */ private boolean canStart(int threadIndex) { final boolean meteredNetworkAllowed = CommonUtils.isMeteredNetworkAllowed(account); - final SyncWrapper syncWrapper = startedSync.get(threadIndex); if ((syncWrapper != null && syncWrapper.isRunning()) @@ -201,19 +200,13 @@ public class SynchronizationService extends Service implements OnRemoteOperation updateFailureCounter(callerWrapper.getRequest(), result.isSuccess()); } - - if (callerOperation instanceof RemoveFileOperation){ + if (callerOperation instanceof RemoveFileOperation) { if ( result.isSuccess() ) { DbHelper.manageSyncedFileStateDB( ( ( RemoveFileOperation ) callerOperation ).getSyncedFileState(), "DELETE", this); } } else { - String operationClassName = callerOperation.getClass().getSimpleName(); - final ArrayList callerOperationResultData = result.getData(); - if (callerOperation instanceof UploadFileOperation && callerOperationResultData != null - && callerOperationResultData.size() > 1) { - final Float relativeQuota = (Float) result.getData().get(1); - } + final String operationClassName = callerOperation.getClass().getSimpleName(); switch (result.getCode()) { case OK: Log.d(TAG, operationClassName + " Succeed"); @@ -227,24 +220,16 @@ public class SynchronizationService extends Service implements OnRemoteOperation break; case UNKNOWN_ERROR: if (callerOperation instanceof UploadFileOperation) { - if (callerOperationResultData != null && callerOperationResultData.size() > 0) { - int rowAffected = DbHelper.forceFoldertoBeRescan(((Long) callerOperationResultData.get(0)).intValue(), getApplicationContext()); - Log.e(TAG, " Upload failed for unknown reason.\n Force folder to be rescan next time (row affected) :" + rowAffected); - } else { - Log.w(TAG, "result.getData() for UploadFileOperation returned null"); - } + final int rowAffected = DbHelper.forceFoldertoBeRescan(((UploadFileOperation) callerOperation).getSyncedState().getId(), getApplicationContext()); + Log.e(TAG, " Upload failed for unknown reason.\n Force folder to be rescan next time (row affected) :" + rowAffected); } else if (callerOperation instanceof DownloadFileOperation) { Log.e(TAG, " Download: Unknown_error : failed"); } break; case FORBIDDEN: if (callerOperation instanceof UploadFileOperation) { - if (callerOperationResultData != null && callerOperationResultData.size() > 0) { - int rowAffected = DbHelper.forceFoldertoBeRescan(((Long) callerOperationResultData.get(0)).intValue(), getApplicationContext()); - Log.e(TAG, " Upload: Forbidden : Can't get syncedFileState, no remote path defined. Force folder to be rescan next time (row affected) :" + rowAffected); - } else { - Log.w(TAG, "result.getData() for UploadFileOperation returned null"); - } + final int rowAffected = DbHelper.forceFoldertoBeRescan(((UploadFileOperation) callerOperation).getSyncedState().getId(), getApplicationContext()); + Log.e(TAG, " Upload: Forbidden : Can't get syncedFileState, no remote path defined. Force folder to be rescan next time (row affected) :" + rowAffected); } else if (callerOperation instanceof DownloadFileOperation) { Log.e(TAG, "Download : Forbidden: Can't get syncedFileState, no local path defined"); } -- GitLab From 0a7e00337a074a92dff47251244cb38a65713355 Mon Sep 17 00:00:00 2001 From: vincent Bourgmayer Date: Tue, 31 May 2022 10:26:55 +0200 Subject: [PATCH 05/59] Update ListFileRemoteOperation.java - Remove import of class from removed submodule - Add new method 'shouldSkipSyncedFolder(...)' to replace multiple if statement - Remove useless comment - rewrite run() method to clean lots of if/else statement - fix space before and after if, else, {, }, (, ) --- .../operations/ListFileRemoteOperation.java | 175 +++++++----------- 1 file changed, 65 insertions(+), 110 deletions(-) diff --git a/app/src/main/java/foundation/e/drive/operations/ListFileRemoteOperation.java b/app/src/main/java/foundation/e/drive/operations/ListFileRemoteOperation.java index b35bd137..133ea5d9 100644 --- a/app/src/main/java/foundation/e/drive/operations/ListFileRemoteOperation.java +++ b/app/src/main/java/foundation/e/drive/operations/ListFileRemoteOperation.java @@ -14,8 +14,8 @@ import android.util.Log; import com.owncloud.android.lib.common.OwnCloudClient; import com.owncloud.android.lib.common.operations.RemoteOperation; import com.owncloud.android.lib.common.operations.RemoteOperationResult; +import com.owncloud.android.lib.resources.files.ReadFolderRemoteOperation; import com.owncloud.android.lib.resources.files.model.RemoteFile; -import com.owncloud.android.lib.resources.files.LightReadFolderRemoteOperation; import java.io.File; import java.util.ArrayList; import java.util.List; @@ -24,12 +24,11 @@ import foundation.e.drive.database.DbHelper; import foundation.e.drive.models.SyncedFolder; import foundation.e.drive.utils.CommonUtils; -import static org.apache.jackrabbit.webdav.DavConstants.DEPTH_1; /** * * @author Vincent Bourgmayer - * Created by Vincent on 04/05/2018. + * Created on 04/05/2018. */ public class ListFileRemoteOperation extends RemoteOperation { private final String TAG = ListFileRemoteOperation.class.getSimpleName(); @@ -38,8 +37,7 @@ public class ListFileRemoteOperation extends RemoteOperation { private final Context mContext; private final int initialFolderNumber; - public ListFileRemoteOperation(List syncedFolders, Context context, int initialFolderNumber){ - Log.i(TAG, "Constructor of ListFileRemoteOperation"); + public ListFileRemoteOperation(List syncedFolders, Context context, int initialFolderNumber) { this.mSyncedFolders = syncedFolders; this.mContext = context; this.initialFolderNumber = initialFolderNumber; @@ -51,146 +49,103 @@ public class ListFileRemoteOperation extends RemoteOperation { * @return List containing remoteFolder followed by remote files */ @Override - protected RemoteOperationResult run(OwnCloudClient ownCloudClient){ + protected RemoteOperationResult run(OwnCloudClient ownCloudClient) { Log.i(TAG, "run()"); - ArrayList mRemoteFiles = new ArrayList<>(); + final ArrayList mRemoteFiles = new ArrayList<>(); RemoteOperationResult finalResult; boolean atLeastOneDirAsChanged = false; - ListIterator mSyncedFolderIterator = mSyncedFolders.listIterator(); + final ListIterator mSyncedFolderIterator = mSyncedFolders.listIterator(); - //Loop through list of SyncedFolder - while (mSyncedFolderIterator.hasNext() ){ - - //Get CurrentSyncedFolder - SyncedFolder syncedFolder = mSyncedFolderIterator.next(); - - //if folder is media type() && is an hidden folder then ignore it - String fileName = CommonUtils.getFileNameFromPath(syncedFolder.getRemoteFolder()); - if (fileName == null) { - Log.e(TAG, "getFileNameFromPath() returned null. Returning to prevent a NPE"); - return new RemoteOperationResult(RemoteOperationResult.ResultCode.UNKNOWN_ERROR); - } - - if(syncedFolder.isMediaType() - && fileName.startsWith(".")){ - mSyncedFolderIterator.remove(); - continue; + while (mSyncedFolderIterator.hasNext()) { + try { + Thread.sleep(150); + } catch (InterruptedException e) { + Log.w(TAG, "listFileRemoteOperation's sleep had been interrupted"); } - //If folder isn't to be scan remotly, ignore it - if(!syncedFolder.isScanRemote()) { + final SyncedFolder syncedFolder = mSyncedFolderIterator.next(); + if (shouldSkipSyncedFolder(syncedFolder)) { mSyncedFolderIterator.remove(); continue; } - if(syncedFolder.getId() == -1) { - //persist new syncedFolder - int syncedFolderId = (int) DbHelper.insertSyncedFolder(syncedFolder, mContext); + if (syncedFolder.getId() == -1) { + final int syncedFolderId = (int) DbHelper.insertSyncedFolder(syncedFolder, mContext); if (syncedFolderId > 0) { syncedFolder.setId(syncedFolderId); - }else{ + } else { mSyncedFolderIterator.remove(); - Log.w(TAG, "syncedFolder "+syncedFolder.getRemoteFolder()+" doesn't have a valid ID"); + Log.w(TAG, "syncedFolder " + syncedFolder.getRemoteFolder() + " doesn't have a valid ID"); continue; } } - //Create ReadRemoteOperation - LightReadFolderRemoteOperation operation = new LightReadFolderRemoteOperation(syncedFolder.getRemoteFolder(), DEPTH_1, false); - RemoteOperationResult result = operation.execute(ownCloudClient); - - if(result.isSuccess() ){ - //is success then data can't be null - int dataSize = result.getData().size(); - if(dataSize > 1){ //There is at least one subfiles - RemoteFile directory = (RemoteFile) result.getData().get(0); - if(!directory.getEtag().equals(syncedFolder.getLastEtag() )){ //if etag differs - List remoteFiles = result.getData().subList( 1, dataSize ); //get list of subfiles - - //loop through subelements - for (int i = -1, remoteFilesSize = remoteFiles.size(); ++i < remoteFilesSize; ){ - RemoteFile remoteFile = (RemoteFile) remoteFiles.get(i); - - //if remoteFile is in a "media" folder and its name start with "." - // then ignore it - fileName = CommonUtils.getFileNameFromPath(remoteFile.getRemotePath()); - if (fileName == null) { - Log.e(TAG, "getFileNameFromPath() returned null. Returning to prevent NPE"); - return new RemoteOperationResult(RemoteOperationResult.ResultCode.UNKNOWN_ERROR); - } - - if(syncedFolder.isMediaType() && fileName.startsWith(".") ){ - continue; - } - if( remoteFile.getMimeType().equals("DIR") ) { - String suffixPath = remoteFile.getRemotePath().substring( syncedFolder.getRemoteFolder().length() ); - - //but is it already known as SyncedFolder? - SyncedFolder subSyncedFolder = new SyncedFolder(syncedFolder, suffixPath, 0L, "" ); //need to set empty etag to allow it to be scan - mSyncedFolderIterator.add(subSyncedFolder); - mSyncedFolderIterator.previous(); - - }else { - //If it's a file just add it to mRemoteFiles. - mRemoteFiles.add(remoteFile); - } + final ReadFolderRemoteOperation operation = new ReadFolderRemoteOperation(syncedFolder.getRemoteFolder()); + final RemoteOperationResult result = operation.execute(ownCloudClient); + + if (result.isSuccess()) { + final int dataSize = result.getData().size(); + final RemoteFile directory = (RemoteFile) result.getData().get(0); + + if (directory.getEtag().equals(syncedFolder.getLastEtag())) { + continue; + } + syncedFolder.setLastEtag(directory.getEtag()).setToSync(true); + atLeastOneDirAsChanged = true; + + if (dataSize > 1) { + final List remoteFiles = result.getData().subList(1, dataSize); //get list of subfiles + + //loop through subelements + for (int i = -1, remoteFilesSize = remoteFiles.size(); ++i < remoteFilesSize; ) { + final RemoteFile remoteFile = (RemoteFile) remoteFiles.get(i); + + if (remoteFile.getMimeType().equals("DIR")) { + final String suffixPath = remoteFile.getRemotePath().substring(syncedFolder.getRemoteFolder().length()); + final SyncedFolder subSyncedFolder = new SyncedFolder(syncedFolder, suffixPath, 0L, ""); //need to set empty etag to allow it to be scan + mSyncedFolderIterator.add(subSyncedFolder); + mSyncedFolderIterator.previous(); + } else { + mRemoteFiles.add(remoteFile); } - syncedFolder.setLastEtag(directory.getEtag() ).setToSync(true); - atLeastOneDirAsChanged = true; - } - }else if(dataSize == 1){ //Empty folder - RemoteFile directory = (RemoteFile) result.getData().get(0); - if(!directory.getEtag().equals(syncedFolder.getLastEtag())) { - syncedFolder.setLastEtag(directory.getEtag()).setToSync(true); - atLeastOneDirAsChanged = true; } - }//Last else correspond to error 404 at LightReadFolderRemoteOperation (see below) - }else{ //Result isn't a success - if(result.getHttpCode() == 404){ //File not found + } + }else { //Result isn't a success + if (result.getHttpCode() == 404) { //File not found atLeastOneDirAsChanged = true; syncedFolder.setToSync(true); - //If there is no remote file, then try to delete local one if empty. Finally remove Synced Folder from DB. - File localFolder = new File(syncedFolder.getLocalFolder()); - if (localFolder.exists()) { - File[] arrayFiles = localFolder.listFiles(); - if (arrayFiles != null && arrayFiles.length == 0) { - localFolder.delete(); - } - } - if( !localFolder.exists() ) { - if (syncedFolder.getId() > this.initialFolderNumber/*-1*/) { //does the synced folder has been persisted? - //remove it from DB - int deleteResult = DbHelper.deleteSyncedFolder(syncedFolder.getId(), mContext); - Log.d(TAG, "syncedFolder Id: "+syncedFolder.getId() + " deletion from db return " + deleteResult + " row affected"); + final File localFolder = new File(syncedFolder.getLocalFolder()); + if (!localFolder.exists()) { + if (syncedFolder.getId() > this.initialFolderNumber) { + final int deleteResult = DbHelper.deleteSyncedFolder(syncedFolder.getId(), mContext); + Log.d(TAG, "syncedFolder Id: " + syncedFolder.getId() + " deletion from db return " + deleteResult + " row affected"); } mSyncedFolderIterator.remove(); + } else if (localFolder.listFiles().length == 0) { + localFolder.delete(); } } - Log.w(TAG, "LightReadFolderRemoteOperation failed : http " + result.getHttpCode() + ", " + result.getLogMessage()+" => Ignored"); - } - - - try{ - Thread.sleep(150); - }catch(InterruptedException e){ - Log.w(TAG, "listFileRemoteOperation's sleep had been interrupted"); + Log.w(TAG, "ReadFolderRemoteOperation failed : http " + result.getHttpCode() + ", " + result.getLogMessage() + " => Ignored"); } - - } //End of loop + } finalResult = new RemoteOperationResult(RemoteOperationResult.ResultCode.OK); - - if( atLeastOneDirAsChanged ) { + if (atLeastOneDirAsChanged) { DbHelper.updateSyncedFolders(this.mSyncedFolders, this.mContext); - finalResult.setData(new ArrayList(mRemoteFiles) ); + finalResult.setData(new ArrayList(mRemoteFiles)); } - Log.v(TAG, "end of run()"); return finalResult; } - /** + private boolean shouldSkipSyncedFolder(SyncedFolder syncedFolder) { + return (syncedFolder.isMediaType() + && CommonUtils.getFileNameFromPath(syncedFolder.getRemoteFolder()).startsWith(".")) + || !syncedFolder.isScanRemote(); + } + + /** * * @return list of syncedFolder */ -- GitLab From cc34838acb596bea29d1d7b07b87b75a7c43272c Mon Sep 17 00:00:00 2001 From: vincent Bourgmayer Date: Wed, 1 Jun 2022 09:53:19 +0200 Subject: [PATCH 06/59] Update GetAliasOperation.java - Add 'final' keyword where possible - Remove useless linebreak - Set RemoteOperationResult from run to be RemoteOperationResult> - Replace deprecated 'setResult(...)' of RemoteOperationResult class --- .../e/drive/operations/GetAliasOperation.java | 19 +++++++++---------- 1 file changed, 9 insertions(+), 10 deletions(-) diff --git a/app/src/main/java/foundation/e/drive/operations/GetAliasOperation.java b/app/src/main/java/foundation/e/drive/operations/GetAliasOperation.java index 4f67f76f..2173b4e8 100644 --- a/app/src/main/java/foundation/e/drive/operations/GetAliasOperation.java +++ b/app/src/main/java/foundation/e/drive/operations/GetAliasOperation.java @@ -35,14 +35,13 @@ public class GetAliasOperation extends RemoteOperation { private static final String NODE_DATA = "data"; private static final String NODE_ALIASES = "aliases"; - private boolean isSuccess(int status) { return (status == HttpStatus.SC_OK); } @Override - protected RemoteOperationResult run(OwnCloudClient client) { - RemoteOperationResult result; + protected RemoteOperationResult> run(OwnCloudClient client) { + RemoteOperationResult> result; GetMethod get = null; final String uri = client.getBaseUri() + ALIAS_PATH + client.getCredentials().getUsername(); @@ -52,25 +51,25 @@ public class GetAliasOperation extends RemoteOperation { get.setQueryString(new NameValuePair[]{new NameValuePair("format", "json")}); if (isSuccess(client.executeMethod(get))) { - String response = get.getResponseBodyAsString(); + final String response = get.getResponseBodyAsString(); // parse final JSONArray aliases = new JSONObject(response).getJSONObject(NODE_OCS) .getJSONObject(NODE_DATA).getJSONArray(NODE_ALIASES); - final ArrayList resultAliases = new ArrayList<>(); + final ArrayList resultAliases = new ArrayList<>(); for (int i = 0; i < aliases.length(); i++) { - resultAliases.add(aliases.get(i)); + resultAliases.add(aliases.get(i).toString()); } - result = new RemoteOperationResult(true, get); - result.setData(resultAliases); + result = new RemoteOperationResult<>(true, get); + result.setResultData(resultAliases); } else { - result = new RemoteOperationResult(false, get); + result = new RemoteOperationResult<>(false, get); } } catch (Exception e) { e.printStackTrace(); - result = new RemoteOperationResult(e); + result = new RemoteOperationResult<>(e); Log_OC.e(TAG, "Fetching aliases failed"); } finally { if (get != null) -- GitLab From 650122d65c640e53e3db0da74cbcd32f952cc68d Mon Sep 17 00:00:00 2001 From: vincent Bourgmayer Date: Wed, 1 Jun 2022 10:11:09 +0200 Subject: [PATCH 07/59] Update DownloadFileOperation.java - Add missing import of DownloadFileRemoteOperation from NC lib - Update log messages - remove useless codes about checking file parent's existence - set 4 fields of the class final --- .../operations/DownloadFileOperation.java | 46 +++++++------------ 1 file changed, 17 insertions(+), 29 deletions(-) diff --git a/app/src/main/java/foundation/e/drive/operations/DownloadFileOperation.java b/app/src/main/java/foundation/e/drive/operations/DownloadFileOperation.java index 5d8892d3..1dedacd1 100644 --- a/app/src/main/java/foundation/e/drive/operations/DownloadFileOperation.java +++ b/app/src/main/java/foundation/e/drive/operations/DownloadFileOperation.java @@ -14,6 +14,7 @@ import android.util.Log; import com.owncloud.android.lib.common.OwnCloudClient; import com.owncloud.android.lib.common.operations.RemoteOperation; import com.owncloud.android.lib.common.operations.RemoteOperationResult; +import com.owncloud.android.lib.resources.files.DownloadFileRemoteOperation; import com.owncloud.android.lib.resources.files.FileUtils; import com.owncloud.android.lib.resources.files.model.RemoteFile; import java.io.File; @@ -29,11 +30,11 @@ public class DownloadFileOperation extends RemoteOperation { private final static String TAG = DownloadFileOperation.class.getSimpleName(); private final RemoteFile remoteFile; - private Context context; - private String targetPath; + private final Context context; + private final String targetPath; private int restartCounter =0; - private SyncedFileState syncedFileState; - private String previousEtag; + private final SyncedFileState syncedFileState; + private final String previousEtag; /** * COnstructor of download operation where syncedFileState is already known @@ -51,17 +52,13 @@ public class DownloadFileOperation extends RemoteOperation { @Override protected RemoteOperationResult run(OwnCloudClient ownCloudClient) { Log.i(TAG, "run(ownCloudClient)"); - - //get or build synced file equivalent of this.mFile - if (syncedFileState == null || targetPath == null || targetPath.isEmpty()) { - Log.e(TAG, "syncedFileState or targetPath is empty or null. Can't Download in those conditions"); - return new RemoteOperationResult(RemoteOperationResult.ResultCode.FORBIDDEN); - } else if (syncedFileState.getId() == -1) { + if (syncedFileState.getId() == -1) { this.syncedFileState.setId(DbHelper.manageSyncedFileStateDB(this.syncedFileState, "INSERT", context)); } - if (syncedFileState.getLastETAG().equals(remoteFile.getEtag()) && syncedFileState.getLocalLastModified() > 0L) { - //Same etag and localLastModified not null mean the file is up to date + if (syncedFileState.getLastETAG().equals(remoteFile.getEtag()) + && syncedFileState.getLocalLastModified() > 0L) { + //file is up to date Log.w(TAG, "File already up-to-date"); return new RemoteOperationResult(RemoteOperationResult.ResultCode.ETAG_UNCHANGED); } @@ -86,40 +83,31 @@ public class DownloadFileOperation extends RemoteOperation { resultCode = RemoteOperationResult.ResultCode.INVALID_OVERWRITE; tmpLocalFile.delete(); - } else { - //file has been correctly download. + } else { //file has been correctly download. final File localFile = new File(targetPath); if (localFile.exists()) { localFile.delete(); } - //Check parentFolder existence and create if needed - final String parentFoldersPath = localFile.getParent(); - final File localParentFile = new File(parentFoldersPath); - if (!localParentFile.exists()) { - if (localParentFile.mkdirs()) - Log.d(TAG, "Created folders: "+parentFoldersPath); - else - Log.d(TAG, "Can't create folders: "+parentFoldersPath); - } - boolean renameResult = tmpLocalFile.renameTo(localFile); - if (!renameResult) - Log.d(TAG, "File hasn't been successfully moved at its place"); + if (!tmpLocalFile.renameTo(localFile)) { + Log.d(TAG, "Can't move " + tmpTargetPath + " to " + targetPath); + return new RemoteOperationResult(RemoteOperationResult.ResultCode.FORBIDDEN); + } syncedFileState.setLocalLastModified(localFile.lastModified()) .setLastETAG(remoteFile.getEtag()); + mustRestart = false; resultCode = RemoteOperationResult.ResultCode.OK; //needed to make Gallery show new image CommonUtils.doActionMediaScannerConnexionScanFile(context, syncedFileState.getLocalPath()); } - } else { - //If download failed + } else { //If download failed Log.e(TAG, "Download failed: "+downloadResult.getLogMessage()); resultCode = RemoteOperationResult.ResultCode.UNKNOWN_ERROR; } if (mustRestart) { - Log.w(TAG, restartCounter+" unsuccessfull trial.s of downloading file " + Log.w(TAG, restartCounter+" unsuccessfull trial.s of downloading" + remoteFile.getRemotePath()); syncedFileState.setLastETAG(this.previousEtag); if (this.restartCounter < 3) { -- GitLab From 8e78c32cc646300afda4f8c7a25628abb076f75b Mon Sep 17 00:00:00 2001 From: Aayush Gupta Date: Wed, 11 May 2022 09:40:16 +0530 Subject: [PATCH 08/59] eDrive: Bump to latest stable gradle Signed-off-by: Aayush Gupta --- app/build.gradle | 7 +++---- build.gradle | 2 +- gradle/wrapper/gradle-wrapper.properties | 2 +- 3 files changed, 5 insertions(+), 6 deletions(-) diff --git a/app/build.gradle b/app/build.gradle index 77cda802..97cff2f2 100644 --- a/app/build.gradle +++ b/app/build.gradle @@ -35,10 +35,6 @@ android { } } - lintOptions { - abortOnError false - } - testOptions { unitTests { @@ -58,6 +54,9 @@ android { sourceCompatibility JavaVersion.VERSION_1_8 targetCompatibility JavaVersion.VERSION_1_8 } + lint { + abortOnError false + } } diff --git a/build.gradle b/build.gradle index 9bd02054..967ffc3c 100644 --- a/build.gradle +++ b/build.gradle @@ -7,7 +7,7 @@ buildscript { mavenCentral() } dependencies { - classpath 'com.android.tools.build:gradle:4.1.3' + classpath 'com.android.tools.build:gradle:7.1.3' } } diff --git a/gradle/wrapper/gradle-wrapper.properties b/gradle/wrapper/gradle-wrapper.properties index 46b275d7..75a02d86 100644 --- a/gradle/wrapper/gradle-wrapper.properties +++ b/gradle/wrapper/gradle-wrapper.properties @@ -3,4 +3,4 @@ distributionBase=GRADLE_USER_HOME distributionPath=wrapper/dists zipStoreBase=GRADLE_USER_HOME zipStorePath=wrapper/dists -distributionUrl=https\://services.gradle.org/distributions/gradle-6.5-all.zip +distributionUrl=https\://services.gradle.org/distributions/gradle-7.2-all.zip -- GitLab From 9cbc0bea9310f0d4c7b7b05cae7106ecba2ee13e Mon Sep 17 00:00:00 2001 From: Aayush Gupta Date: Wed, 11 May 2022 09:41:55 +0530 Subject: [PATCH 09/59] eDrive: Migrate to new plugins block API Signed-off-by: Aayush Gupta --- app/build.gradle | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/app/build.gradle b/app/build.gradle index 97cff2f2..a1ddabe7 100644 --- a/app/build.gradle +++ b/app/build.gradle @@ -1,6 +1,8 @@ +plugins { + id 'com.android.application' +} import java.text.SimpleDateFormat; -apply plugin: 'com.android.application' def versionMajor = 1 def versionMinor = 0 def versionPatch = 0 -- GitLab From 372090c2dc3d3bad56d5312e2ef95fe3c974796f Mon Sep 17 00:00:00 2001 From: Aayush Gupta Date: Wed, 11 May 2022 09:49:20 +0530 Subject: [PATCH 10/59] eDrive: Migrate to new plugins and dependency system Signed-off-by: Aayush Gupta --- build.gradle | 21 +++------------------ settings.gradle | 16 ++++++++++++++++ 2 files changed, 19 insertions(+), 18 deletions(-) diff --git a/build.gradle b/build.gradle index 967ffc3c..3858e872 100644 --- a/build.gradle +++ b/build.gradle @@ -1,22 +1,7 @@ // Top-level build file where you can add configuration options common to all sub-projects/modules. - -buildscript { - - repositories { - google() - mavenCentral() - } - dependencies { - classpath 'com.android.tools.build:gradle:7.1.3' - } -} - -allprojects { - repositories { - google() - mavenCentral() - maven { url "https://jitpack.io" } - } +plugins { + id 'com.android.application' version '7.1.3' apply false + id 'com.android.library' version '7.1.3' apply false } task clean(type: Delete) { diff --git a/settings.gradle b/settings.gradle index 9d495b34..a18c791d 100644 --- a/settings.gradle +++ b/settings.gradle @@ -1 +1,17 @@ +pluginManagement { + repositories { + gradlePluginPortal() + google() + mavenCentral() + } +} +dependencyResolutionManagement { + repositoriesMode.set(RepositoriesMode.FAIL_ON_PROJECT_REPOS) + repositories { + google() + mavenCentral() + maven { url "https://jitpack.io" } + } +} +rootProject.name = "eDrive" include ':app' \ No newline at end of file -- GitLab From 6d286214b20743eec0478044cf797998c8243158 Mon Sep 17 00:00:00 2001 From: Aayush Gupta Date: Wed, 11 May 2022 09:52:39 +0530 Subject: [PATCH 11/59] eDrive: Bump to latest stable dependencies Signed-off-by: Aayush Gupta --- app/build.gradle | 22 ++++++++++------------ 1 file changed, 10 insertions(+), 12 deletions(-) diff --git a/app/build.gradle b/app/build.gradle index a1ddabe7..0e43dcb1 100644 --- a/app/build.gradle +++ b/app/build.gradle @@ -2,7 +2,7 @@ plugins { id 'com.android.application' } -import java.text.SimpleDateFormat; + def versionMajor = 1 def versionMinor = 0 def versionPatch = 0 @@ -68,26 +68,24 @@ dependencies { implementation "commons-httpclient:commons-httpclient:3.1@jar" implementation fileTree(include: ['*.jar'], dir: 'libs') api 'androidx.annotation:annotation:1.3.0' - implementation 'androidx.core:core:1.6.0' - implementation 'androidx.appcompat:appcompat:1.0.2' + implementation 'androidx.core:core:1.7.0' + implementation 'androidx.appcompat:appcompat:1.4.1' implementation "androidx.constraintlayout:constraintlayout:2.1.3" - implementation 'com.google.android.material:material:1.5.0' + implementation 'com.google.android.material:material:1.6.0' implementation 'com.github.bumptech.glide:glide:4.13.1' - - def work_version = "2.7.1" - // (Java only) - implementation "androidx.work:work-runtime:$work_version" + implementation "androidx.work:work-runtime:2.7.1" androidTestImplementation 'androidx.test:runner:1.4.0' androidTestImplementation 'androidx.test:rules:1.4.0' androidTestImplementation 'androidx.annotation:annotation:1.3.0' androidTestImplementation 'androidx.test.espresso:espresso-core:3.4.0' - androidTestImplementation 'junit:junit:4.12' + androidTestImplementation 'junit:junit:4.13.2' testImplementation 'androidx.test:runner:1.4.0' testImplementation 'androidx.test:rules:1.4.0' - testImplementation 'junit:junit:4.12' - testImplementation 'org.robolectric:robolectric:4.8.1' + + testImplementation 'junit:junit:4.13.2' + testImplementation 'org.robolectric:robolectric:4.4' testImplementation 'org.mockito:mockito-inline:3.4.0' - testImplementation "androidx.work:work-testing:$work_version" + testImplementation 'androidx.work:work-testing:2.7.1' } -- GitLab From 269d80b5371b8fe973a2441598bdbd1b635fc5c3 Mon Sep 17 00:00:00 2001 From: Aayush Gupta Date: Wed, 11 May 2022 09:53:05 +0530 Subject: [PATCH 12/59] eDrive: Enable Java 11 language features Signed-off-by: Aayush Gupta --- app/build.gradle | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/app/build.gradle b/app/build.gradle index 0e43dcb1..543c0cfc 100644 --- a/app/build.gradle +++ b/app/build.gradle @@ -53,8 +53,8 @@ android { viewBinding true } compileOptions { - sourceCompatibility JavaVersion.VERSION_1_8 - targetCompatibility JavaVersion.VERSION_1_8 + sourceCompatibility JavaVersion.VERSION_11 + targetCompatibility JavaVersion.VERSION_11 } lint { abortOnError false -- GitLab From 0f7c6398e4762eab59d092bf9e351e1dac01321f Mon Sep 17 00:00:00 2001 From: Aayush Gupta Date: Wed, 11 May 2022 09:58:08 +0530 Subject: [PATCH 13/59] eDrive: Remove extra translations without default locale Resolves lint errors: > Task :app:lintVitalRelease C:\Users\aayus\StudioProjects\eDrive\app\src\main\res\values-de\strings.xml:23: Error: "no_alias" is translated here but not found in default locale [ExtraTranslation] Keine Aliase! ~~~~~~~~~~~~~~~ C:\Users\aayus\StudioProjects\eDrive\app\src\main\res\values-de\strings.xml:25: Error: "notif_quota_execeeded_title" is translated here but not found in default locale [ExtraTranslation] Die H?chstquote des /e/-Kontos wurde erreicht ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ C:\Users\aayus\StudioProjects\eDrive\app\src\main\res\values-de\strings.xml:26: Error: "notif_quota_nearlyReached_title" is translated here but not found in default locale [ExtraTranslation] Die H?chstquote des /e/-Kontos ist fast erreicht ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ Explanation for issues of type "ExtraTranslation": If a string appears in a specific language translation file, but there is no corresponding string in the default locale, then this string is probably unused. (It's technically possible that your application is only intended to run in a specific locale, but it's still a good idea to provide a fallback.) Note that these strings can lead to crashes if the string is looked up on any locale not providing a translation, so it's important to clean them up. 3 errors, 0 warnings Signed-off-by: Aayush Gupta --- app/src/main/res/values-de/strings.xml | 3 --- 1 file changed, 3 deletions(-) diff --git a/app/src/main/res/values-de/strings.xml b/app/src/main/res/values-de/strings.xml index 1c55ebbb..8f504689 100644 --- a/app/src/main/res/values-de/strings.xml +++ b/app/src/main/res/values-de/strings.xml @@ -20,10 +20,7 @@ Anpassen Synchronisiere deine Fotos und \n Videos sicher mit der Cloud - Keine Aliase! eDrive Benachrichtigungskanal - Die Höchstquote des /e/-Kontos wurde erreicht - Die Höchstquote des /e/-Kontos ist fast erreicht Es ist nicht möglich, Dateien hochzuladen, die größer sind als der verbleibende Platz im Cloud-Speicher. Bitte unternehmen Sie etwas. 99 % der Höchstquote des Cloud-Speichers erreicht. Bitte unternimm etwas. Alias -- GitLab From ad7afcf3c828012f61c472016b67c84942f51f33 Mon Sep 17 00:00:00 2001 From: Aayush Gupta Date: Wed, 11 May 2022 09:59:56 +0530 Subject: [PATCH 14/59] eDrive: Drop lint block No errors happening anymore, nor its a good idea to supress them like this when they happen Signed-off-by: Aayush Gupta --- app/build.gradle | 3 --- 1 file changed, 3 deletions(-) diff --git a/app/build.gradle b/app/build.gradle index 543c0cfc..908bf1c5 100644 --- a/app/build.gradle +++ b/app/build.gradle @@ -56,9 +56,6 @@ android { sourceCompatibility JavaVersion.VERSION_11 targetCompatibility JavaVersion.VERSION_11 } - lint { - abortOnError false - } } -- GitLab From bc38eada5c1f9d0a0c87e6fc4f5b2a7bece6621a Mon Sep 17 00:00:00 2001 From: vincent Bourgmayer Date: Thu, 2 Jun 2022 11:05:59 +0200 Subject: [PATCH 15/59] eDrive: Reformat and apply Android Studio suggestions to gradle file --- app/build.gradle | 12 +++++++----- 1 file changed, 7 insertions(+), 5 deletions(-) diff --git a/app/build.gradle b/app/build.gradle index 908bf1c5..721b03c3 100644 --- a/app/build.gradle +++ b/app/build.gradle @@ -10,9 +10,9 @@ def versionPatch = 0 def getTestProp(String propName) { def result = "" - if(project.hasProperty(propName)){ + if (project.hasProperty(propName)) { result =project.property(propName).toString() - }else if(project.rootProject.file('local.properties').exists()){ + } else if(project.rootProject.file('local.properties').exists()) { Properties properties = new Properties() properties.load(project.rootProject.file('local.properties').newReader()) result = properties.getProperty(propName) @@ -22,13 +22,15 @@ def getTestProp(String propName) { android { - compileSdkVersion 31 + compileSdk 31 defaultConfig { applicationId "foundation.e.drive" - minSdkVersion 26 + minSdk 26 + targetSdk 31 versionCode versionMajor * 1000000 + versionMinor * 1000 + versionPatch versionName "${versionMajor}.${versionMinor}.${versionPatch}" setProperty("archivesBaseName", "eDrive-$versionName") + testInstrumentationRunner "androidx.test.runner.AndroidJUnitRunner" } buildTypes { release { @@ -76,7 +78,7 @@ dependencies { androidTestImplementation 'androidx.test:rules:1.4.0' androidTestImplementation 'androidx.annotation:annotation:1.3.0' androidTestImplementation 'androidx.test.espresso:espresso-core:3.4.0' - androidTestImplementation 'junit:junit:4.13.2' + androidTestImplementation 'junit:junit:4.13.2' testImplementation 'androidx.test:runner:1.4.0' testImplementation 'androidx.test:rules:1.4.0' -- GitLab From 09e9a3104396ab79fef5f53f27708d687a7c8b75 Mon Sep 17 00:00:00 2001 From: vincent Bourgmayer Date: Thu, 2 Jun 2022 11:08:40 +0200 Subject: [PATCH 16/59] eDrive: CI: Reformat and drop non-required commands --- .gitlab-ci.yml | 16 +++++++--------- 1 file changed, 7 insertions(+), 9 deletions(-) diff --git a/.gitlab-ci.yml b/.gitlab-ci.yml index c60fad30..cdb53b00 100644 --- a/.gitlab-ci.yml +++ b/.gitlab-ci.yml @@ -1,20 +1,18 @@ image: "registry.gitlab.e.foundation/e/os/docker-android-apps-cicd:latest" stages: -- test -- build + - test + - build before_script: -- git submodule sync -- git submodule update --init --recursive -- export GRADLE_USER_HOME=$(pwd)/.gradle -- chmod +x ./gradlew + - export GRADLE_USER_HOME=$(pwd)/.gradle + - chmod +x ./gradlew cache: key: ${CI_PROJECT_ID} paths: - - .gradle/ + - .gradle/ test: @@ -35,9 +33,9 @@ test: build: stage: build script: - - ./gradlew assemble + - ./gradlew assemble artifacts: paths: - - app/build/outputs/apk/ + - app/build/outputs/apk/ expire_in: 4 weeks -- GitLab From 74cda5ce0b99efc6cb32e8371db53aba68d4398a Mon Sep 17 00:00:00 2001 From: Aayush Gupta Date: Wed, 11 May 2022 10:19:38 +0530 Subject: [PATCH 17/59] eDrive: Export Broadcast Recievers and supress play permission warning Signed-off-by: Aayush Gupta --- app/src/main/AndroidManifest.xml | 15 ++++++++++----- 1 file changed, 10 insertions(+), 5 deletions(-) diff --git a/app/src/main/AndroidManifest.xml b/app/src/main/AndroidManifest.xml index dc5d04a4..dd1a2379 100644 --- a/app/src/main/AndroidManifest.xml +++ b/app/src/main/AndroidManifest.xml @@ -1,5 +1,6 @@ @@ -11,7 +12,9 @@ - + + android:exported="true" + android:label="@string/app_widget_description"> @@ -92,14 +95,16 @@ + android:enabled="true" + android:exported="true"> + android:enabled="true" + android:exported="true"> -- GitLab From a24403dc6fb4225aa582bbf25ac6f5701f5f6a3d Mon Sep 17 00:00:00 2001 From: vincent Bourgmayer Date: Mon, 30 May 2022 17:12:26 +0200 Subject: [PATCH 18/59] Update gradle file --build.gradle-- - Replace Jcenter() by MavenCentral() - Add Jetpack dependency --app/build.gradle-- - Add official NCLib dependency & apache http client instead - remove dependency to submodule --settings.gradle-- - remove sutff related to submodule --- app/build.gradle | 3 ++- build.gradle | 9 +++------ settings.gradle | 3 +-- 3 files changed, 6 insertions(+), 9 deletions(-) diff --git a/app/build.gradle b/app/build.gradle index 6af3f945..77cda802 100644 --- a/app/build.gradle +++ b/app/build.gradle @@ -63,7 +63,8 @@ android { dependencies { - api project(':NextcloudLib') + implementation 'com.github.nextcloud:android-library:2.10.1' + implementation "commons-httpclient:commons-httpclient:3.1@jar" implementation fileTree(include: ['*.jar'], dir: 'libs') api 'androidx.annotation:annotation:1.3.0' implementation 'androidx.core:core:1.6.0' diff --git a/build.gradle b/build.gradle index 3378f0cd..9bd02054 100644 --- a/build.gradle +++ b/build.gradle @@ -4,21 +4,18 @@ buildscript { repositories { google() - jcenter() + mavenCentral() } dependencies { classpath 'com.android.tools.build:gradle:4.1.3' - - - // NOTE: Do not place your application dependencies here; they belong - // in the individual module build.gradle files } } allprojects { repositories { google() - jcenter() + mavenCentral() + maven { url "https://jitpack.io" } } } diff --git a/settings.gradle b/settings.gradle index 19a2fc3d..9d495b34 100644 --- a/settings.gradle +++ b/settings.gradle @@ -1,2 +1 @@ -include ':app', ':NextcloudLib' -project(':NextcloudLib').projectDir = new File('nextcloud-android-lib') \ No newline at end of file +include ':app' \ No newline at end of file -- GitLab From d651cfea73650dcd8307a91026d36a59611ef7fa Mon Sep 17 00:00:00 2001 From: vincent Bourgmayer Date: Mon, 30 May 2022 17:29:55 +0200 Subject: [PATCH 19/59] Remove submodule (custom fork of Nextcloud Android Library) - Update .idea/vcs.xml - remove nextcloud-lib submodule - remove .gitmodules - remove useless DownloadFileRemoteOperation.java from operations package --- .gitmodules | 3 - .idea/vcs.xml | 1 - .../DownloadFileRemoteOperation.java | 188 ------------------ nextcloud-android-lib | 1 - 4 files changed, 193 deletions(-) delete mode 100644 .gitmodules delete mode 100644 app/src/main/java/foundation/e/drive/operations/DownloadFileRemoteOperation.java delete mode 160000 nextcloud-android-lib diff --git a/.gitmodules b/.gitmodules deleted file mode 100644 index 34e74166..00000000 --- a/.gitmodules +++ /dev/null @@ -1,3 +0,0 @@ -[submodule "nextcloud-android-lib"] - path = nextcloud-android-lib - url = ../nextcloud-android-lib.git \ No newline at end of file diff --git a/.idea/vcs.xml b/.idea/vcs.xml index 0bda0242..35eb1ddf 100644 --- a/.idea/vcs.xml +++ b/.idea/vcs.xml @@ -2,6 +2,5 @@ - \ No newline at end of file diff --git a/app/src/main/java/foundation/e/drive/operations/DownloadFileRemoteOperation.java b/app/src/main/java/foundation/e/drive/operations/DownloadFileRemoteOperation.java deleted file mode 100644 index 107b6ff1..00000000 --- a/app/src/main/java/foundation/e/drive/operations/DownloadFileRemoteOperation.java +++ /dev/null @@ -1,188 +0,0 @@ -/* ownCloud Android Library is available under MIT license - * Copyright (C) 2015 ownCloud Inc. - * Copyright © CLEUS SAS 2018-2019. - * Copyright © ECORP SAS 2022. - * Permission is hereby granted, free of charge, to any person obtaining a copy - * of this software and associated documentation files (the "Software"), to deal - * in the Software without restriction, including without limitation the rights - * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell - * copies of the Software, and to permit persons to whom the Software is - * furnished to do so, subject to the following conditions: - * - * The above copyright notice and this permission notice shall be included in - * all copies or substantial portions of the Software. - * - * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, - * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF - * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND - * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS - * BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN - * ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN - * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN - * THE SOFTWARE. - * - */ -package foundation.e.drive.operations; - -import android.util.Log; - -import com.owncloud.android.lib.common.OwnCloudClient; -import com.owncloud.android.lib.common.network.WebdavUtils; -import com.owncloud.android.lib.common.operations.OperationCancelledException; -import com.owncloud.android.lib.common.operations.RemoteOperation; -import com.owncloud.android.lib.common.operations.RemoteOperationResult; -import com.owncloud.android.lib.common.utils.Log_OC; -import org.apache.commons.httpclient.Header; -import org.apache.commons.httpclient.HttpStatus; -import org.apache.commons.httpclient.methods.GetMethod; -import java.io.BufferedInputStream; -import java.io.File; -import java.io.FileOutputStream; -import java.io.IOException; -import java.util.Date; -import java.util.concurrent.atomic.AtomicBoolean; - -/** - * Remote operation performing the download of a remote file in the ownCloud server. - * - * @author David A. Velasco - * @author masensio - * @author Vincent Bourgmayer - */ -class DownloadFileRemoteOperation extends RemoteOperation { - private static final String TAG = DownloadFileRemoteOperation.class.getSimpleName(); - - private final AtomicBoolean mCancellationRequested = new AtomicBoolean(false); - private long mModificationTimestamp = 0; - private String mEtag = ""; - private GetMethod mGet; - private final String mRemotePath; - private final String mLocalFolderPath; - - /** - * Constructor - * @param remotePath Path of file on the server - * @param localFolderPath Path of file on the device - */ - public DownloadFileRemoteOperation(String remotePath, String localFolderPath) { - mRemotePath = remotePath; - mLocalFolderPath = localFolderPath; - } - - @Override - protected RemoteOperationResult run(OwnCloudClient client) { - RemoteOperationResult result = null; - /// download will be performed to a temporal file, then moved to the final location - File tmpFile = new File(getTmpPath()); - /// perform the download - try { - File parentFile = tmpFile.getParentFile(); - if (parentFile == null) { - Log.e(TAG, "getParentFile() returned null. Returning to prevent a NPE"); - return new RemoteOperationResult(RemoteOperationResult.ResultCode.UNKNOWN_ERROR); - } - - parentFile.mkdirs(); - int status = downloadFile(client, tmpFile); - result = new RemoteOperationResult(isSuccess(status), mGet); - Log_OC.i(TAG, "Download of " + mRemotePath + " to " + getTmpPath() + ": " + - result.getLogMessage()); - - } catch (Exception e) { - result = new RemoteOperationResult(e); - Log_OC.e(TAG, "Download of " + mRemotePath + " to " + getTmpPath() + ": " + - result.getLogMessage(), e); - } - - return result; - } - - private int downloadFile(OwnCloudClient client, File targetFile) throws IOException, OperationCancelledException { - int status = -1; - boolean savedFile = false; - mGet = new GetMethod(client.getWebdavUri() + WebdavUtils.encodePath(mRemotePath)); - - FileOutputStream fos = null; - try { - status = client.executeMethod(mGet); - if (isSuccess(status)) { - targetFile.createNewFile(); - BufferedInputStream bis = new BufferedInputStream(mGet.getResponseBodyAsStream()); - fos = new FileOutputStream(targetFile); - long transferred = 0; - - Header contentLength = mGet.getResponseHeader("Content-Length"); - long totalToTransfer = (contentLength != null && - contentLength.getValue().length() > 0) ? - Long.parseLong(contentLength.getValue()) : 0; - - byte[] bytes = new byte[4096]; - int readResult = 0; - while ((readResult = bis.read(bytes)) != -1) { - synchronized (mCancellationRequested) { - if (mCancellationRequested.get()) { - mGet.abort(); - throw new OperationCancelledException(); - } - } - fos.write(bytes, 0, readResult); - transferred += readResult; - } - // Check if the file is completed - // if transfer-encoding: chunked we cannot check if the file is complete - Header transferEncodingHeader = mGet.getResponseHeader("Transfer-Encoding"); - boolean transferEncoding = false; - - if (transferEncodingHeader != null) { - transferEncoding = transferEncodingHeader.getValue().equals("chunked"); - } - - if (transferred == totalToTransfer || transferEncoding) { - savedFile = true; - Header modificationTime = mGet.getResponseHeader("Last-Modified"); - if (modificationTime == null) { - modificationTime = mGet.getResponseHeader("last-modified"); - } - if (modificationTime != null) { - Date d = WebdavUtils.parseResponseDate(modificationTime.getValue()); - mModificationTimestamp = (d != null) ? d.getTime() : 0; - } else { - Log_OC.e(TAG, "Could not read modification time from response downloading " + mRemotePath); - } - - mEtag = WebdavUtils.getEtagFromResponse(mGet); - if (mEtag.length() == 0) { - Log_OC.e(TAG, "Could not read eTag from response downloading " + mRemotePath); - } - - } else { - client.exhaustResponse(mGet.getResponseBodyAsStream()); - // TODO some kind of error control! - } - } else { - client.exhaustResponse(mGet.getResponseBodyAsStream()); - } - } catch (Exception e) { - Log_OC.e(TAG, e.getMessage()); - } finally { - if (fos != null) fos.close(); - if (!savedFile && targetFile.exists()) { - targetFile.delete(); - } - mGet.releaseConnection(); // let the connection available for other methods - } - return status; - } - - private boolean isSuccess(int status) { - return (status == HttpStatus.SC_OK); - } - - /** - * IMPLEMENTATION DIFFER FROM NC IMPLEMENTATION - * @return - */ - private String getTmpPath() { - return mLocalFolderPath; - } -} diff --git a/nextcloud-android-lib b/nextcloud-android-lib deleted file mode 160000 index b043a510..00000000 --- a/nextcloud-android-lib +++ /dev/null @@ -1 +0,0 @@ -Subproject commit b043a5104962a841daf4ca3e59383216eebf7ef0 -- GitLab From dc7a8b18cb7e37cc1b02eefa1ddd9b67ad088918 Mon Sep 17 00:00:00 2001 From: vincent Bourgmayer Date: Mon, 30 May 2022 17:38:45 +0200 Subject: [PATCH 20/59] Update foundation.e.drive.work package --CreateRemoteFolderWorker.java-- - Add 'final' keyword to three variables in two method - Remove one parameter for CreateFolderRemoteOperation.execute(...) --AccountUserInfoWorker.java-- - Replace GetUserRemoteInfoOperation by GetUserInfoRemoteOperation - make three properties 'private' - Add 'final' keyword to one variable in 'doWork()' - Update fetchUserInfo() method to use stuff from official lib - update fetchAliases() method to use stuff from official lib --- .../e/drive/work/AccountUserInfoWorker.java | 59 +++++++++---------- .../drive/work/CreateRemoteFolderWorker.java | 8 +-- 2 files changed, 31 insertions(+), 36 deletions(-) diff --git a/app/src/main/java/foundation/e/drive/work/AccountUserInfoWorker.java b/app/src/main/java/foundation/e/drive/work/AccountUserInfoWorker.java index 700338d0..9817efc0 100644 --- a/app/src/main/java/foundation/e/drive/work/AccountUserInfoWorker.java +++ b/app/src/main/java/foundation/e/drive/work/AccountUserInfoWorker.java @@ -31,9 +31,10 @@ import androidx.work.WorkerParameters; import com.bumptech.glide.Glide; import com.bumptech.glide.load.engine.DiskCacheStrategy; import com.owncloud.android.lib.common.OwnCloudClient; +import com.owncloud.android.lib.common.Quota; import com.owncloud.android.lib.common.UserInfo; import com.owncloud.android.lib.common.operations.RemoteOperationResult; -import com.owncloud.android.lib.resources.users.GetRemoteUserInfoOperation; +import com.owncloud.android.lib.resources.users.GetUserInfoRemoteOperation; import java.util.ArrayList; @@ -50,9 +51,9 @@ import foundation.e.drive.widgets.EDriveWidget; */ public class AccountUserInfoWorker extends Worker { public static final String UNIQUE_WORK_NAME = "AccountUserInfoWorker"; - final AccountManager accountManager; - final GetRemoteUserInfoOperation getRemoteUserInfoOperation = new GetRemoteUserInfoOperation(); - final GetAliasOperation getAliasOperation = new GetAliasOperation(); + private final AccountManager accountManager; + private final GetUserInfoRemoteOperation GetUserInfoRemoteOperation = new GetUserInfoRemoteOperation(); + private final GetAliasOperation getAliasOperation = new GetAliasOperation(); private final Context mContext; private Account account; @@ -67,7 +68,7 @@ public class AccountUserInfoWorker extends Worker { public Result doWork() { account = CommonUtils.getAccount(mContext.getString(R.string.eelo_account_type), accountManager); - OwnCloudClient client = CommonUtils.getOwnCloudClient(account, mContext); + final OwnCloudClient client = CommonUtils.getOwnCloudClient(account, mContext); if (account != null && client != null) { if (fetchUserInfo(client) && fetchAliases(client)) { @@ -86,32 +87,23 @@ public class AccountUserInfoWorker extends Worker { } private boolean fetchUserInfo(final OwnCloudClient client) { - final RemoteOperationResult ocsResult = getRemoteUserInfoOperation.execute(client); + final RemoteOperationResult ocsResult = GetUserInfoRemoteOperation.execute(client); - if (ocsResult.isSuccess() && ocsResult.getData() != null && !ocsResult.getData().isEmpty()) { - final UserInfo userInfo = (UserInfo) ocsResult.getData().get(0); - - final String name; - if (userInfo.displayName == null || userInfo.displayName.isEmpty()) { - name = userInfo.alternateDisplayName; - } else { - name = userInfo.displayName; - } - final double relativeQuota = userInfo.getQuota().relative; - long totalQuota = userInfo.getQuota().total; + if (ocsResult.isSuccess()) { + final UserInfo userInfo = ocsResult.getResultData(); + final Quota userQuota = userInfo.getQuota(); + final double relativeQuota = userQuota.getRelative(); + long totalQuota = userQuota.getTotal(); if (totalQuota <= 0) { totalQuota = 0; } - final long usedQuota = userInfo.getQuota().used; - final String groups = String.join(",", userInfo.groups); - final String email = userInfo.id; - - accountManager.setUserData(account, ACCOUNT_DATA_NAME, name); - accountManager.setUserData(account, ACCOUNT_DATA_EMAIL, email); + final String groups = String.join(",", userInfo.getGroups()); + accountManager.setUserData(account, ACCOUNT_DATA_NAME, userInfo.getDisplayName()); + accountManager.setUserData(account, ACCOUNT_DATA_EMAIL, userInfo.getEmail()); accountManager.setUserData(account, ACCOUNT_DATA_GROUPS, groups); accountManager.setUserData(account, ACCOUNT_DATA_TOTAL_QUOTA_KEY, "" + totalQuota); accountManager.setUserData(account, ACCOUNT_DATA_RELATIVE_QUOTA_KEY, "" + relativeQuota); - accountManager.setUserData(account, ACCOUNT_DATA_USED_QUOTA_KEY, "" + usedQuota); + accountManager.setUserData(account, ACCOUNT_DATA_USED_QUOTA_KEY, "" + userQuota.getUsed()); addNotifAboutQuota(relativeQuota); return true; @@ -179,14 +171,17 @@ public class AccountUserInfoWorker extends Worker { } private boolean fetchAliases(final OwnCloudClient client) { - final RemoteOperationResult ocsResult = getAliasOperation.execute(client); - String aliases = ""; - if (ocsResult.isSuccess() && ocsResult.getData() != null && !ocsResult.getData().isEmpty()) { - ArrayList alias = new ArrayList<>(ocsResult.getData().size()); - for (Object object : ocsResult.getData()) { - alias.add(object.toString()); - } - aliases = String.join(",", alias); + final RemoteOperationResult> ocsResult = getAliasOperation.execute(client); + if (!ocsResult.isSuccess()) { + return false; + } + + final ArrayList aliasList = ocsResult.getResultData(); + final String aliases; + if (!aliasList.isEmpty()) { + aliases = String.join(",", aliasList); + } else { + aliases = ""; } accountManager.setUserData(account, ACCOUNT_DATA_ALIAS_KEY, aliases); return true; diff --git a/app/src/main/java/foundation/e/drive/work/CreateRemoteFolderWorker.java b/app/src/main/java/foundation/e/drive/work/CreateRemoteFolderWorker.java index d7c27ba3..e510fdaf 100644 --- a/app/src/main/java/foundation/e/drive/work/CreateRemoteFolderWorker.java +++ b/app/src/main/java/foundation/e/drive/work/CreateRemoteFolderWorker.java @@ -85,7 +85,7 @@ public class CreateRemoteFolderWorker extends Worker { new CreateFolderRemoteOperation(syncedFolder.getRemoteFolder(), true); try { - final RemoteOperationResult result = mkcolRequest.execute(client, true); + final RemoteOperationResult result = mkcolRequest.execute(client); if (result.isSuccess() || result.getCode() == RemoteOperationResult.ResultCode.FOLDER_ALREADY_EXISTS) { if(DbHelper.insertSyncedFolder(syncedFolder, context) >= 0 ) { Log.d(TAG, "Insertion in DB succeed"); @@ -99,8 +99,8 @@ public class CreateRemoteFolderWorker extends Worker { } private SyncedFolder getSyncedFolderFromData() { - Data data = getInputData(); - SyncedFolder result = new SyncedFolder( + final Data data = getInputData(); + final SyncedFolder result = new SyncedFolder( data.getString(DATA_KEY_LIBELLE), data.getString(DATA_KEY_LOCAL_PATH), data.getString(DATA_KEY_REMOTE_PATH), @@ -118,7 +118,7 @@ public class CreateRemoteFolderWorker extends Worker { private Account getAccount() { - SharedPreferences prefs = getApplicationContext().getSharedPreferences(AppConstants.SHARED_PREFERENCE_NAME, Context.MODE_PRIVATE); + final SharedPreferences prefs = getApplicationContext().getSharedPreferences(AppConstants.SHARED_PREFERENCE_NAME, Context.MODE_PRIVATE); final String accountName = prefs.getString(AccountManager.KEY_ACCOUNT_NAME, ""); final String accountType = prefs.getString(AccountManager.KEY_ACCOUNT_TYPE, ""); -- GitLab From 2a20dba2d02f3861e3c3f1e207667106f0764abb Mon Sep 17 00:00:00 2001 From: vincent Bourgmayer Date: Tue, 31 May 2022 10:16:38 +0200 Subject: [PATCH 21/59] Update UploadFileOperation.java & SynchronizationService.java --UploadFileOperation.java-- - Replace GetRemoteUserInfoOperation by GetUserInfoRemoteOperation - Add 'final' to some variable - Removed property 'available quota' because useless - Add getSyncedFileState() method - UploadFileOperation doesn't provide quota as result anymore - Rewrite some if statement to be cleaner & more readable - remove one parameter from buildUploadOperation(...) --SynchronizatioNService.java-- - Add 'final' to some variable - report change from UploadFileOperation on SynchronizationService --- .../drive/operations/UploadFileOperation.java | 95 +++++++------------ .../services/SynchronizationService.java | 27 ++---- 2 files changed, 40 insertions(+), 82 deletions(-) diff --git a/app/src/main/java/foundation/e/drive/operations/UploadFileOperation.java b/app/src/main/java/foundation/e/drive/operations/UploadFileOperation.java index c1eed28e..8a48dab2 100644 --- a/app/src/main/java/foundation/e/drive/operations/UploadFileOperation.java +++ b/app/src/main/java/foundation/e/drive/operations/UploadFileOperation.java @@ -21,10 +21,9 @@ import com.owncloud.android.lib.common.operations.RemoteOperationResult; import com.owncloud.android.lib.resources.files.CreateFolderRemoteOperation; import com.owncloud.android.lib.resources.files.FileUtils; import com.owncloud.android.lib.resources.files.UploadFileRemoteOperation; -import com.owncloud.android.lib.resources.users.GetRemoteUserInfoOperation; +import com.owncloud.android.lib.resources.users.GetUserInfoRemoteOperation; import com.owncloud.android.lib.common.operations.RemoteOperationResult.ResultCode; import java.io.File; -import java.util.ArrayList; import foundation.e.drive.database.DbHelper; import foundation.e.drive.models.SyncedFileState; import foundation.e.drive.utils.CommonUtils; @@ -40,7 +39,6 @@ public class UploadFileOperation extends RemoteOperation { private long previousLastModified; //get to restore real value if all trials fails private Context context; private SyncedFileState syncedState; - private long availableQuota = -1; /** * Construct an upload operation with an already known syncedFileState @@ -66,12 +64,7 @@ public class UploadFileOperation extends RemoteOperation { @Override protected RemoteOperationResult run(OwnCloudClient client ) { //as operation isn't executed immediatly, file might have been deleted since creation of operation - if (syncedState == null ) { - Log.e(TAG, "run(client): no syncedFileState or target path, can't perform upload operation"); - return new RemoteOperationResult(ResultCode.FORBIDDEN); - } - - File file = new File(syncedState.getLocalPath()); + final File file = new File(syncedState.getLocalPath()); if (file == null || !file.exists()) { Log.w(TAG, "Can't get the file. It might have been deleted"); return new RemoteOperationResult(ResultCode.FORBIDDEN); @@ -79,39 +72,34 @@ public class UploadFileOperation extends RemoteOperation { final String targetPath = syncedState.getRemotePath(); - //If an Etag is already Stored and LastModified from DB is the same as real file + //If file already up-to-date & synced if (syncedState.isLastEtagStored() && syncedState.getLocalLastModified() == file.lastModified()) { Log.d(TAG, "syncedState last modified: "+ syncedState.getLocalLastModified()+" <=> file last modified: "+file.lastModified() +": So return sync_conflict"); return new RemoteOperationResult(ResultCode.SYNC_CONFLICT); } - Float relativeQuotaBeforeFileUpload = 0.0f; - if (this.availableQuota == -1) { - RemoteOperationResult checkQuotaResult = checkAvailableSpace(client, file.length()); - if (checkQuotaResult.getCode() != ResultCode.OK) { - return new RemoteOperationResult(checkQuotaResult.getCode()); - } else { - relativeQuotaBeforeFileUpload = ((Double) checkQuotaResult.getSingleData()).floatValue(); - } + final RemoteOperationResult checkQuotaResult = checkAvailableSpace(client, file.length()); + if (checkQuotaResult.getCode() != ResultCode.OK) { + Log.e(TAG, "Impossible to check quota. Upload of " + syncedState.getLocalPath() + "cancelled"); + return new RemoteOperationResult(checkQuotaResult.getCode()); } if (!createRemoteFolder(targetPath, client)) { return new RemoteOperationResult(ResultCode.UNKNOWN_ERROR); } - final UploadFileRemoteOperation uploadOperation = buildUploadOperation(file, targetPath); + final UploadFileRemoteOperation uploadOperation = buildUploadOperation(file); // Execute UploadFileOperation - RemoteOperationResult uploadResult = uploadOperation.execute(client ); - ResultCode resultCode; + final RemoteOperationResult uploadResult = uploadOperation.execute(client); + final ResultCode resultCode; boolean mustRestart = true; - //if upload is a success if (uploadResult.isSuccess()) { - Object data = uploadResult.getSingleData(); - if (data != null) { - syncedState.setLastETAG((String) data); + final String etag = uploadResult.getResultData(); + if (etag != null) { + syncedState.setLastETAG(etag); } syncedState.setLocalLastModified(file.lastModified()); resultCode = uploadResult.getCode(); @@ -125,7 +113,6 @@ public class UploadFileOperation extends RemoteOperation { resultCode = ResultCode.QUOTA_EXCEEDED; mustRestart = false; } else { - //Upload failed Log.e(TAG, "UploadFileRemoteOperation for : " + file.getName() + " failed => code: " + uploadResult.getCode()); resultCode = ResultCode.UNKNOWN_ERROR; mustRestart = false; @@ -135,38 +122,27 @@ public class UploadFileOperation extends RemoteOperation { if (mustRestart) { if (this.restartCounter < 1) { this.restartCounter += 1; - //if we encounter more than one time same error, stop trying to upload. return this.run(client); } else { syncedState.setLocalLastModified(this.previousLastModified); //Revert syncFileState to its previous state } } - // updated syncedFile in DB DbHelper.manageSyncedFileStateDB(syncedState, "UPDATE", context); - - ArrayList datas = new ArrayList<>(); - datas.add(syncedState.getSyncedFolderId()); - datas.add(relativeQuotaBeforeFileUpload); - final RemoteOperationResult finalResult = new RemoteOperationResult(resultCode); - finalResult.setData(datas); - return finalResult; + return new RemoteOperationResult(resultCode); } /** * Build the operation to put the file on server * @return the operation to execute */ - private UploadFileRemoteOperation buildUploadOperation(File file, String targetPath) { - String timeStamp = ((Long) (file.lastModified() / 1000) ).toString() ; - - //create UploadFileOperation - UploadFileRemoteOperation uploadRemoteFileOperation = new UploadFileRemoteOperation(syncedState.getLocalPath(), - (targetPath != null ) ? targetPath : syncedState.getRemotePath(), - CommonUtils.getMimeType(file ), - (!this.syncedState.isMediaType() || syncedState.getLastETAG().isEmpty() )? null : syncedState.getLastETAG(), //If not null, This can cause error 412; that means remote file has change + private UploadFileRemoteOperation buildUploadOperation(File file) { + final String timeStamp = ((Long) (file.lastModified() / 1000) ).toString() ; + + return new UploadFileRemoteOperation(syncedState.getLocalPath(), + syncedState.getRemotePath(), + CommonUtils.getMimeType(file), + (!this.syncedState.isMediaType() || syncedState.getLastETAG().isEmpty())? null : syncedState.getLastETAG(), //If not null, This can cause error 412; that means remote file has change timeStamp ); - uploadRemoteFileOperation.askResultEtag(true); - return uploadRemoteFileOperation; } /** @@ -176,24 +152,17 @@ public class UploadFileOperation extends RemoteOperation { */ @VisibleForTesting(otherwise = VisibleForTesting.PRIVATE) public RemoteOperationResult checkAvailableSpace(OwnCloudClient client, long fileSize) { - GetRemoteUserInfoOperation getRemoteUserInfoOperation = new GetRemoteUserInfoOperation(); - RemoteOperationResult ocsResult = getRemoteUserInfoOperation.execute(client); - if (ocsResult.isSuccess() && ocsResult.getData() != null) { - UserInfo userInfo = (UserInfo) ocsResult.getData().get(0); - this.availableQuota = userInfo.getQuota().getFree(); - if (((UserInfo) ocsResult.getData().get(0)).getQuota().getFree() < fileSize ) { - Log.w(TAG, "quota exceeded!"); - return new RemoteOperationResult(ResultCode.QUOTA_EXCEEDED); - } else { - Log.d(TAG, "Quota Okay"); - RemoteOperationResult result = new RemoteOperationResult(ResultCode.OK); - result.setSingleData((Double) userInfo.getQuota().getRelative()); - return result; - } + final GetUserInfoRemoteOperation GetUserInfoRemoteOperation = new GetUserInfoRemoteOperation(); + final RemoteOperationResult ocsResult = GetUserInfoRemoteOperation.execute(client); + final UserInfo userInfo = ocsResult.getResultData(); + final RemoteOperationResult result; + if (ocsResult.isSuccess() && userInfo.getQuota().getFree() < fileSize) { + Log.w(TAG, "Not enough quota to upload the file"); + result = new RemoteOperationResult(ResultCode.QUOTA_EXCEEDED); } else { - Log.w(TAG, "getRemoteUserInfoOperation failed: "+ocsResult.getHttpCode() ); - return new RemoteOperationResult(ocsResult.getCode()); + result = new RemoteOperationResult(ocsResult.getCode()); } + return result; } @@ -217,4 +186,8 @@ public class UploadFileOperation extends RemoteOperation { } return false; } + + public SyncedFileState getSyncedState() { + return syncedState; + } } diff --git a/app/src/main/java/foundation/e/drive/services/SynchronizationService.java b/app/src/main/java/foundation/e/drive/services/SynchronizationService.java index da005cc9..c9d932c7 100644 --- a/app/src/main/java/foundation/e/drive/services/SynchronizationService.java +++ b/app/src/main/java/foundation/e/drive/services/SynchronizationService.java @@ -174,7 +174,6 @@ public class SynchronizationService extends Service implements OnRemoteOperation */ private boolean canStart(int threadIndex) { final boolean meteredNetworkAllowed = CommonUtils.isMeteredNetworkAllowed(account); - final SyncWrapper syncWrapper = startedSync.get(threadIndex); if ((syncWrapper != null && syncWrapper.isRunning()) @@ -201,19 +200,13 @@ public class SynchronizationService extends Service implements OnRemoteOperation updateFailureCounter(callerWrapper.getRequest(), result.isSuccess()); } - - if (callerOperation instanceof RemoveFileOperation){ + if (callerOperation instanceof RemoveFileOperation) { if ( result.isSuccess() ) { DbHelper.manageSyncedFileStateDB( ( ( RemoveFileOperation ) callerOperation ).getSyncedFileState(), "DELETE", this); } } else { - String operationClassName = callerOperation.getClass().getSimpleName(); - final ArrayList callerOperationResultData = result.getData(); - if (callerOperation instanceof UploadFileOperation && callerOperationResultData != null - && callerOperationResultData.size() > 1) { - final Float relativeQuota = (Float) result.getData().get(1); - } + final String operationClassName = callerOperation.getClass().getSimpleName(); switch (result.getCode()) { case OK: Log.d(TAG, operationClassName + " Succeed"); @@ -227,24 +220,16 @@ public class SynchronizationService extends Service implements OnRemoteOperation break; case UNKNOWN_ERROR: if (callerOperation instanceof UploadFileOperation) { - if (callerOperationResultData != null && callerOperationResultData.size() > 0) { - int rowAffected = DbHelper.forceFoldertoBeRescan(((Long) callerOperationResultData.get(0)).intValue(), getApplicationContext()); - Log.e(TAG, " Upload failed for unknown reason.\n Force folder to be rescan next time (row affected) :" + rowAffected); - } else { - Log.w(TAG, "result.getData() for UploadFileOperation returned null"); - } + final int rowAffected = DbHelper.forceFoldertoBeRescan(((UploadFileOperation) callerOperation).getSyncedState().getId(), getApplicationContext()); + Log.e(TAG, " Upload failed for unknown reason.\n Force folder to be rescan next time (row affected) :" + rowAffected); } else if (callerOperation instanceof DownloadFileOperation) { Log.e(TAG, " Download: Unknown_error : failed"); } break; case FORBIDDEN: if (callerOperation instanceof UploadFileOperation) { - if (callerOperationResultData != null && callerOperationResultData.size() > 0) { - int rowAffected = DbHelper.forceFoldertoBeRescan(((Long) callerOperationResultData.get(0)).intValue(), getApplicationContext()); - Log.e(TAG, " Upload: Forbidden : Can't get syncedFileState, no remote path defined. Force folder to be rescan next time (row affected) :" + rowAffected); - } else { - Log.w(TAG, "result.getData() for UploadFileOperation returned null"); - } + final int rowAffected = DbHelper.forceFoldertoBeRescan(((UploadFileOperation) callerOperation).getSyncedState().getId(), getApplicationContext()); + Log.e(TAG, " Upload: Forbidden : Can't get syncedFileState, no remote path defined. Force folder to be rescan next time (row affected) :" + rowAffected); } else if (callerOperation instanceof DownloadFileOperation) { Log.e(TAG, "Download : Forbidden: Can't get syncedFileState, no local path defined"); } -- GitLab From 44eb36fffae22947a5a4e362563c6772e0a6d1ad Mon Sep 17 00:00:00 2001 From: vincent Bourgmayer Date: Tue, 31 May 2022 10:26:55 +0200 Subject: [PATCH 22/59] Update ListFileRemoteOperation.java - Remove import of class from removed submodule - Add new method 'shouldSkipSyncedFolder(...)' to replace multiple if statement - Remove useless comment - rewrite run() method to clean lots of if/else statement - fix space before and after if, else, {, }, (, ) --- .../operations/ListFileRemoteOperation.java | 175 +++++++----------- 1 file changed, 65 insertions(+), 110 deletions(-) diff --git a/app/src/main/java/foundation/e/drive/operations/ListFileRemoteOperation.java b/app/src/main/java/foundation/e/drive/operations/ListFileRemoteOperation.java index b35bd137..133ea5d9 100644 --- a/app/src/main/java/foundation/e/drive/operations/ListFileRemoteOperation.java +++ b/app/src/main/java/foundation/e/drive/operations/ListFileRemoteOperation.java @@ -14,8 +14,8 @@ import android.util.Log; import com.owncloud.android.lib.common.OwnCloudClient; import com.owncloud.android.lib.common.operations.RemoteOperation; import com.owncloud.android.lib.common.operations.RemoteOperationResult; +import com.owncloud.android.lib.resources.files.ReadFolderRemoteOperation; import com.owncloud.android.lib.resources.files.model.RemoteFile; -import com.owncloud.android.lib.resources.files.LightReadFolderRemoteOperation; import java.io.File; import java.util.ArrayList; import java.util.List; @@ -24,12 +24,11 @@ import foundation.e.drive.database.DbHelper; import foundation.e.drive.models.SyncedFolder; import foundation.e.drive.utils.CommonUtils; -import static org.apache.jackrabbit.webdav.DavConstants.DEPTH_1; /** * * @author Vincent Bourgmayer - * Created by Vincent on 04/05/2018. + * Created on 04/05/2018. */ public class ListFileRemoteOperation extends RemoteOperation { private final String TAG = ListFileRemoteOperation.class.getSimpleName(); @@ -38,8 +37,7 @@ public class ListFileRemoteOperation extends RemoteOperation { private final Context mContext; private final int initialFolderNumber; - public ListFileRemoteOperation(List syncedFolders, Context context, int initialFolderNumber){ - Log.i(TAG, "Constructor of ListFileRemoteOperation"); + public ListFileRemoteOperation(List syncedFolders, Context context, int initialFolderNumber) { this.mSyncedFolders = syncedFolders; this.mContext = context; this.initialFolderNumber = initialFolderNumber; @@ -51,146 +49,103 @@ public class ListFileRemoteOperation extends RemoteOperation { * @return List containing remoteFolder followed by remote files */ @Override - protected RemoteOperationResult run(OwnCloudClient ownCloudClient){ + protected RemoteOperationResult run(OwnCloudClient ownCloudClient) { Log.i(TAG, "run()"); - ArrayList mRemoteFiles = new ArrayList<>(); + final ArrayList mRemoteFiles = new ArrayList<>(); RemoteOperationResult finalResult; boolean atLeastOneDirAsChanged = false; - ListIterator mSyncedFolderIterator = mSyncedFolders.listIterator(); + final ListIterator mSyncedFolderIterator = mSyncedFolders.listIterator(); - //Loop through list of SyncedFolder - while (mSyncedFolderIterator.hasNext() ){ - - //Get CurrentSyncedFolder - SyncedFolder syncedFolder = mSyncedFolderIterator.next(); - - //if folder is media type() && is an hidden folder then ignore it - String fileName = CommonUtils.getFileNameFromPath(syncedFolder.getRemoteFolder()); - if (fileName == null) { - Log.e(TAG, "getFileNameFromPath() returned null. Returning to prevent a NPE"); - return new RemoteOperationResult(RemoteOperationResult.ResultCode.UNKNOWN_ERROR); - } - - if(syncedFolder.isMediaType() - && fileName.startsWith(".")){ - mSyncedFolderIterator.remove(); - continue; + while (mSyncedFolderIterator.hasNext()) { + try { + Thread.sleep(150); + } catch (InterruptedException e) { + Log.w(TAG, "listFileRemoteOperation's sleep had been interrupted"); } - //If folder isn't to be scan remotly, ignore it - if(!syncedFolder.isScanRemote()) { + final SyncedFolder syncedFolder = mSyncedFolderIterator.next(); + if (shouldSkipSyncedFolder(syncedFolder)) { mSyncedFolderIterator.remove(); continue; } - if(syncedFolder.getId() == -1) { - //persist new syncedFolder - int syncedFolderId = (int) DbHelper.insertSyncedFolder(syncedFolder, mContext); + if (syncedFolder.getId() == -1) { + final int syncedFolderId = (int) DbHelper.insertSyncedFolder(syncedFolder, mContext); if (syncedFolderId > 0) { syncedFolder.setId(syncedFolderId); - }else{ + } else { mSyncedFolderIterator.remove(); - Log.w(TAG, "syncedFolder "+syncedFolder.getRemoteFolder()+" doesn't have a valid ID"); + Log.w(TAG, "syncedFolder " + syncedFolder.getRemoteFolder() + " doesn't have a valid ID"); continue; } } - //Create ReadRemoteOperation - LightReadFolderRemoteOperation operation = new LightReadFolderRemoteOperation(syncedFolder.getRemoteFolder(), DEPTH_1, false); - RemoteOperationResult result = operation.execute(ownCloudClient); - - if(result.isSuccess() ){ - //is success then data can't be null - int dataSize = result.getData().size(); - if(dataSize > 1){ //There is at least one subfiles - RemoteFile directory = (RemoteFile) result.getData().get(0); - if(!directory.getEtag().equals(syncedFolder.getLastEtag() )){ //if etag differs - List remoteFiles = result.getData().subList( 1, dataSize ); //get list of subfiles - - //loop through subelements - for (int i = -1, remoteFilesSize = remoteFiles.size(); ++i < remoteFilesSize; ){ - RemoteFile remoteFile = (RemoteFile) remoteFiles.get(i); - - //if remoteFile is in a "media" folder and its name start with "." - // then ignore it - fileName = CommonUtils.getFileNameFromPath(remoteFile.getRemotePath()); - if (fileName == null) { - Log.e(TAG, "getFileNameFromPath() returned null. Returning to prevent NPE"); - return new RemoteOperationResult(RemoteOperationResult.ResultCode.UNKNOWN_ERROR); - } - - if(syncedFolder.isMediaType() && fileName.startsWith(".") ){ - continue; - } - if( remoteFile.getMimeType().equals("DIR") ) { - String suffixPath = remoteFile.getRemotePath().substring( syncedFolder.getRemoteFolder().length() ); - - //but is it already known as SyncedFolder? - SyncedFolder subSyncedFolder = new SyncedFolder(syncedFolder, suffixPath, 0L, "" ); //need to set empty etag to allow it to be scan - mSyncedFolderIterator.add(subSyncedFolder); - mSyncedFolderIterator.previous(); - - }else { - //If it's a file just add it to mRemoteFiles. - mRemoteFiles.add(remoteFile); - } + final ReadFolderRemoteOperation operation = new ReadFolderRemoteOperation(syncedFolder.getRemoteFolder()); + final RemoteOperationResult result = operation.execute(ownCloudClient); + + if (result.isSuccess()) { + final int dataSize = result.getData().size(); + final RemoteFile directory = (RemoteFile) result.getData().get(0); + + if (directory.getEtag().equals(syncedFolder.getLastEtag())) { + continue; + } + syncedFolder.setLastEtag(directory.getEtag()).setToSync(true); + atLeastOneDirAsChanged = true; + + if (dataSize > 1) { + final List remoteFiles = result.getData().subList(1, dataSize); //get list of subfiles + + //loop through subelements + for (int i = -1, remoteFilesSize = remoteFiles.size(); ++i < remoteFilesSize; ) { + final RemoteFile remoteFile = (RemoteFile) remoteFiles.get(i); + + if (remoteFile.getMimeType().equals("DIR")) { + final String suffixPath = remoteFile.getRemotePath().substring(syncedFolder.getRemoteFolder().length()); + final SyncedFolder subSyncedFolder = new SyncedFolder(syncedFolder, suffixPath, 0L, ""); //need to set empty etag to allow it to be scan + mSyncedFolderIterator.add(subSyncedFolder); + mSyncedFolderIterator.previous(); + } else { + mRemoteFiles.add(remoteFile); } - syncedFolder.setLastEtag(directory.getEtag() ).setToSync(true); - atLeastOneDirAsChanged = true; - } - }else if(dataSize == 1){ //Empty folder - RemoteFile directory = (RemoteFile) result.getData().get(0); - if(!directory.getEtag().equals(syncedFolder.getLastEtag())) { - syncedFolder.setLastEtag(directory.getEtag()).setToSync(true); - atLeastOneDirAsChanged = true; } - }//Last else correspond to error 404 at LightReadFolderRemoteOperation (see below) - }else{ //Result isn't a success - if(result.getHttpCode() == 404){ //File not found + } + }else { //Result isn't a success + if (result.getHttpCode() == 404) { //File not found atLeastOneDirAsChanged = true; syncedFolder.setToSync(true); - //If there is no remote file, then try to delete local one if empty. Finally remove Synced Folder from DB. - File localFolder = new File(syncedFolder.getLocalFolder()); - if (localFolder.exists()) { - File[] arrayFiles = localFolder.listFiles(); - if (arrayFiles != null && arrayFiles.length == 0) { - localFolder.delete(); - } - } - if( !localFolder.exists() ) { - if (syncedFolder.getId() > this.initialFolderNumber/*-1*/) { //does the synced folder has been persisted? - //remove it from DB - int deleteResult = DbHelper.deleteSyncedFolder(syncedFolder.getId(), mContext); - Log.d(TAG, "syncedFolder Id: "+syncedFolder.getId() + " deletion from db return " + deleteResult + " row affected"); + final File localFolder = new File(syncedFolder.getLocalFolder()); + if (!localFolder.exists()) { + if (syncedFolder.getId() > this.initialFolderNumber) { + final int deleteResult = DbHelper.deleteSyncedFolder(syncedFolder.getId(), mContext); + Log.d(TAG, "syncedFolder Id: " + syncedFolder.getId() + " deletion from db return " + deleteResult + " row affected"); } mSyncedFolderIterator.remove(); + } else if (localFolder.listFiles().length == 0) { + localFolder.delete(); } } - Log.w(TAG, "LightReadFolderRemoteOperation failed : http " + result.getHttpCode() + ", " + result.getLogMessage()+" => Ignored"); - } - - - try{ - Thread.sleep(150); - }catch(InterruptedException e){ - Log.w(TAG, "listFileRemoteOperation's sleep had been interrupted"); + Log.w(TAG, "ReadFolderRemoteOperation failed : http " + result.getHttpCode() + ", " + result.getLogMessage() + " => Ignored"); } - - } //End of loop + } finalResult = new RemoteOperationResult(RemoteOperationResult.ResultCode.OK); - - if( atLeastOneDirAsChanged ) { + if (atLeastOneDirAsChanged) { DbHelper.updateSyncedFolders(this.mSyncedFolders, this.mContext); - finalResult.setData(new ArrayList(mRemoteFiles) ); + finalResult.setData(new ArrayList(mRemoteFiles)); } - Log.v(TAG, "end of run()"); return finalResult; } - /** + private boolean shouldSkipSyncedFolder(SyncedFolder syncedFolder) { + return (syncedFolder.isMediaType() + && CommonUtils.getFileNameFromPath(syncedFolder.getRemoteFolder()).startsWith(".")) + || !syncedFolder.isScanRemote(); + } + + /** * * @return list of syncedFolder */ -- GitLab From 855ce899bc5ee85dd6a5e4ac6ba2566cf28c5931 Mon Sep 17 00:00:00 2001 From: vincent Bourgmayer Date: Wed, 1 Jun 2022 09:53:19 +0200 Subject: [PATCH 23/59] Update GetAliasOperation.java - Add 'final' keyword where possible - Remove useless linebreak - Set RemoteOperationResult from run to be RemoteOperationResult> - Replace deprecated 'setResult(...)' of RemoteOperationResult class --- .../e/drive/operations/GetAliasOperation.java | 19 +++++++++---------- 1 file changed, 9 insertions(+), 10 deletions(-) diff --git a/app/src/main/java/foundation/e/drive/operations/GetAliasOperation.java b/app/src/main/java/foundation/e/drive/operations/GetAliasOperation.java index 4f67f76f..2173b4e8 100644 --- a/app/src/main/java/foundation/e/drive/operations/GetAliasOperation.java +++ b/app/src/main/java/foundation/e/drive/operations/GetAliasOperation.java @@ -35,14 +35,13 @@ public class GetAliasOperation extends RemoteOperation { private static final String NODE_DATA = "data"; private static final String NODE_ALIASES = "aliases"; - private boolean isSuccess(int status) { return (status == HttpStatus.SC_OK); } @Override - protected RemoteOperationResult run(OwnCloudClient client) { - RemoteOperationResult result; + protected RemoteOperationResult> run(OwnCloudClient client) { + RemoteOperationResult> result; GetMethod get = null; final String uri = client.getBaseUri() + ALIAS_PATH + client.getCredentials().getUsername(); @@ -52,25 +51,25 @@ public class GetAliasOperation extends RemoteOperation { get.setQueryString(new NameValuePair[]{new NameValuePair("format", "json")}); if (isSuccess(client.executeMethod(get))) { - String response = get.getResponseBodyAsString(); + final String response = get.getResponseBodyAsString(); // parse final JSONArray aliases = new JSONObject(response).getJSONObject(NODE_OCS) .getJSONObject(NODE_DATA).getJSONArray(NODE_ALIASES); - final ArrayList resultAliases = new ArrayList<>(); + final ArrayList resultAliases = new ArrayList<>(); for (int i = 0; i < aliases.length(); i++) { - resultAliases.add(aliases.get(i)); + resultAliases.add(aliases.get(i).toString()); } - result = new RemoteOperationResult(true, get); - result.setData(resultAliases); + result = new RemoteOperationResult<>(true, get); + result.setResultData(resultAliases); } else { - result = new RemoteOperationResult(false, get); + result = new RemoteOperationResult<>(false, get); } } catch (Exception e) { e.printStackTrace(); - result = new RemoteOperationResult(e); + result = new RemoteOperationResult<>(e); Log_OC.e(TAG, "Fetching aliases failed"); } finally { if (get != null) -- GitLab From 295009927f9a4904746411c0fe5af017bddf5f52 Mon Sep 17 00:00:00 2001 From: vincent Bourgmayer Date: Wed, 1 Jun 2022 10:11:09 +0200 Subject: [PATCH 24/59] Update DownloadFileOperation.java - Add missing import of DownloadFileRemoteOperation from NC lib - Update log messages - remove useless codes about checking file parent's existence - set 4 fields of the class final --- .../operations/DownloadFileOperation.java | 46 +++++++------------ 1 file changed, 17 insertions(+), 29 deletions(-) diff --git a/app/src/main/java/foundation/e/drive/operations/DownloadFileOperation.java b/app/src/main/java/foundation/e/drive/operations/DownloadFileOperation.java index 5d8892d3..1dedacd1 100644 --- a/app/src/main/java/foundation/e/drive/operations/DownloadFileOperation.java +++ b/app/src/main/java/foundation/e/drive/operations/DownloadFileOperation.java @@ -14,6 +14,7 @@ import android.util.Log; import com.owncloud.android.lib.common.OwnCloudClient; import com.owncloud.android.lib.common.operations.RemoteOperation; import com.owncloud.android.lib.common.operations.RemoteOperationResult; +import com.owncloud.android.lib.resources.files.DownloadFileRemoteOperation; import com.owncloud.android.lib.resources.files.FileUtils; import com.owncloud.android.lib.resources.files.model.RemoteFile; import java.io.File; @@ -29,11 +30,11 @@ public class DownloadFileOperation extends RemoteOperation { private final static String TAG = DownloadFileOperation.class.getSimpleName(); private final RemoteFile remoteFile; - private Context context; - private String targetPath; + private final Context context; + private final String targetPath; private int restartCounter =0; - private SyncedFileState syncedFileState; - private String previousEtag; + private final SyncedFileState syncedFileState; + private final String previousEtag; /** * COnstructor of download operation where syncedFileState is already known @@ -51,17 +52,13 @@ public class DownloadFileOperation extends RemoteOperation { @Override protected RemoteOperationResult run(OwnCloudClient ownCloudClient) { Log.i(TAG, "run(ownCloudClient)"); - - //get or build synced file equivalent of this.mFile - if (syncedFileState == null || targetPath == null || targetPath.isEmpty()) { - Log.e(TAG, "syncedFileState or targetPath is empty or null. Can't Download in those conditions"); - return new RemoteOperationResult(RemoteOperationResult.ResultCode.FORBIDDEN); - } else if (syncedFileState.getId() == -1) { + if (syncedFileState.getId() == -1) { this.syncedFileState.setId(DbHelper.manageSyncedFileStateDB(this.syncedFileState, "INSERT", context)); } - if (syncedFileState.getLastETAG().equals(remoteFile.getEtag()) && syncedFileState.getLocalLastModified() > 0L) { - //Same etag and localLastModified not null mean the file is up to date + if (syncedFileState.getLastETAG().equals(remoteFile.getEtag()) + && syncedFileState.getLocalLastModified() > 0L) { + //file is up to date Log.w(TAG, "File already up-to-date"); return new RemoteOperationResult(RemoteOperationResult.ResultCode.ETAG_UNCHANGED); } @@ -86,40 +83,31 @@ public class DownloadFileOperation extends RemoteOperation { resultCode = RemoteOperationResult.ResultCode.INVALID_OVERWRITE; tmpLocalFile.delete(); - } else { - //file has been correctly download. + } else { //file has been correctly download. final File localFile = new File(targetPath); if (localFile.exists()) { localFile.delete(); } - //Check parentFolder existence and create if needed - final String parentFoldersPath = localFile.getParent(); - final File localParentFile = new File(parentFoldersPath); - if (!localParentFile.exists()) { - if (localParentFile.mkdirs()) - Log.d(TAG, "Created folders: "+parentFoldersPath); - else - Log.d(TAG, "Can't create folders: "+parentFoldersPath); - } - boolean renameResult = tmpLocalFile.renameTo(localFile); - if (!renameResult) - Log.d(TAG, "File hasn't been successfully moved at its place"); + if (!tmpLocalFile.renameTo(localFile)) { + Log.d(TAG, "Can't move " + tmpTargetPath + " to " + targetPath); + return new RemoteOperationResult(RemoteOperationResult.ResultCode.FORBIDDEN); + } syncedFileState.setLocalLastModified(localFile.lastModified()) .setLastETAG(remoteFile.getEtag()); + mustRestart = false; resultCode = RemoteOperationResult.ResultCode.OK; //needed to make Gallery show new image CommonUtils.doActionMediaScannerConnexionScanFile(context, syncedFileState.getLocalPath()); } - } else { - //If download failed + } else { //If download failed Log.e(TAG, "Download failed: "+downloadResult.getLogMessage()); resultCode = RemoteOperationResult.ResultCode.UNKNOWN_ERROR; } if (mustRestart) { - Log.w(TAG, restartCounter+" unsuccessfull trial.s of downloading file " + Log.w(TAG, restartCounter+" unsuccessfull trial.s of downloading" + remoteFile.getRemotePath()); syncedFileState.setLastETAG(this.previousEtag); if (this.restartCounter < 3) { -- GitLab From 26cfd5ca97105208df1fc4d2a5ed7e565247399f Mon Sep 17 00:00:00 2001 From: Aayush Gupta Date: Wed, 11 May 2022 09:40:16 +0530 Subject: [PATCH 25/59] eDrive: Bump to latest stable gradle Signed-off-by: Aayush Gupta --- app/build.gradle | 7 +++---- build.gradle | 2 +- gradle/wrapper/gradle-wrapper.properties | 2 +- 3 files changed, 5 insertions(+), 6 deletions(-) diff --git a/app/build.gradle b/app/build.gradle index 77cda802..97cff2f2 100644 --- a/app/build.gradle +++ b/app/build.gradle @@ -35,10 +35,6 @@ android { } } - lintOptions { - abortOnError false - } - testOptions { unitTests { @@ -58,6 +54,9 @@ android { sourceCompatibility JavaVersion.VERSION_1_8 targetCompatibility JavaVersion.VERSION_1_8 } + lint { + abortOnError false + } } diff --git a/build.gradle b/build.gradle index 9bd02054..967ffc3c 100644 --- a/build.gradle +++ b/build.gradle @@ -7,7 +7,7 @@ buildscript { mavenCentral() } dependencies { - classpath 'com.android.tools.build:gradle:4.1.3' + classpath 'com.android.tools.build:gradle:7.1.3' } } diff --git a/gradle/wrapper/gradle-wrapper.properties b/gradle/wrapper/gradle-wrapper.properties index 46b275d7..75a02d86 100644 --- a/gradle/wrapper/gradle-wrapper.properties +++ b/gradle/wrapper/gradle-wrapper.properties @@ -3,4 +3,4 @@ distributionBase=GRADLE_USER_HOME distributionPath=wrapper/dists zipStoreBase=GRADLE_USER_HOME zipStorePath=wrapper/dists -distributionUrl=https\://services.gradle.org/distributions/gradle-6.5-all.zip +distributionUrl=https\://services.gradle.org/distributions/gradle-7.2-all.zip -- GitLab From b5950d312c8b53d85ff332c7d14fec0cc0b22f7d Mon Sep 17 00:00:00 2001 From: Aayush Gupta Date: Wed, 11 May 2022 09:41:55 +0530 Subject: [PATCH 26/59] eDrive: Migrate to new plugins block API Signed-off-by: Aayush Gupta --- app/build.gradle | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/app/build.gradle b/app/build.gradle index 97cff2f2..a1ddabe7 100644 --- a/app/build.gradle +++ b/app/build.gradle @@ -1,6 +1,8 @@ +plugins { + id 'com.android.application' +} import java.text.SimpleDateFormat; -apply plugin: 'com.android.application' def versionMajor = 1 def versionMinor = 0 def versionPatch = 0 -- GitLab From eed956c5ef52a15def4ec49ed45cee35a5de2ddf Mon Sep 17 00:00:00 2001 From: Aayush Gupta Date: Wed, 11 May 2022 09:49:20 +0530 Subject: [PATCH 27/59] eDrive: Migrate to new plugins and dependency system Signed-off-by: Aayush Gupta --- build.gradle | 21 +++------------------ settings.gradle | 16 ++++++++++++++++ 2 files changed, 19 insertions(+), 18 deletions(-) diff --git a/build.gradle b/build.gradle index 967ffc3c..3858e872 100644 --- a/build.gradle +++ b/build.gradle @@ -1,22 +1,7 @@ // Top-level build file where you can add configuration options common to all sub-projects/modules. - -buildscript { - - repositories { - google() - mavenCentral() - } - dependencies { - classpath 'com.android.tools.build:gradle:7.1.3' - } -} - -allprojects { - repositories { - google() - mavenCentral() - maven { url "https://jitpack.io" } - } +plugins { + id 'com.android.application' version '7.1.3' apply false + id 'com.android.library' version '7.1.3' apply false } task clean(type: Delete) { diff --git a/settings.gradle b/settings.gradle index 9d495b34..a18c791d 100644 --- a/settings.gradle +++ b/settings.gradle @@ -1 +1,17 @@ +pluginManagement { + repositories { + gradlePluginPortal() + google() + mavenCentral() + } +} +dependencyResolutionManagement { + repositoriesMode.set(RepositoriesMode.FAIL_ON_PROJECT_REPOS) + repositories { + google() + mavenCentral() + maven { url "https://jitpack.io" } + } +} +rootProject.name = "eDrive" include ':app' \ No newline at end of file -- GitLab From 6044e2ba0fe4b20a0b33e1b65ab7506047883aa8 Mon Sep 17 00:00:00 2001 From: Aayush Gupta Date: Wed, 11 May 2022 09:52:39 +0530 Subject: [PATCH 28/59] eDrive: Bump to latest stable dependencies Signed-off-by: Aayush Gupta --- app/build.gradle | 22 ++++++++++------------ 1 file changed, 10 insertions(+), 12 deletions(-) diff --git a/app/build.gradle b/app/build.gradle index a1ddabe7..0e43dcb1 100644 --- a/app/build.gradle +++ b/app/build.gradle @@ -2,7 +2,7 @@ plugins { id 'com.android.application' } -import java.text.SimpleDateFormat; + def versionMajor = 1 def versionMinor = 0 def versionPatch = 0 @@ -68,26 +68,24 @@ dependencies { implementation "commons-httpclient:commons-httpclient:3.1@jar" implementation fileTree(include: ['*.jar'], dir: 'libs') api 'androidx.annotation:annotation:1.3.0' - implementation 'androidx.core:core:1.6.0' - implementation 'androidx.appcompat:appcompat:1.0.2' + implementation 'androidx.core:core:1.7.0' + implementation 'androidx.appcompat:appcompat:1.4.1' implementation "androidx.constraintlayout:constraintlayout:2.1.3" - implementation 'com.google.android.material:material:1.5.0' + implementation 'com.google.android.material:material:1.6.0' implementation 'com.github.bumptech.glide:glide:4.13.1' - - def work_version = "2.7.1" - // (Java only) - implementation "androidx.work:work-runtime:$work_version" + implementation "androidx.work:work-runtime:2.7.1" androidTestImplementation 'androidx.test:runner:1.4.0' androidTestImplementation 'androidx.test:rules:1.4.0' androidTestImplementation 'androidx.annotation:annotation:1.3.0' androidTestImplementation 'androidx.test.espresso:espresso-core:3.4.0' - androidTestImplementation 'junit:junit:4.12' + androidTestImplementation 'junit:junit:4.13.2' testImplementation 'androidx.test:runner:1.4.0' testImplementation 'androidx.test:rules:1.4.0' - testImplementation 'junit:junit:4.12' - testImplementation 'org.robolectric:robolectric:4.8.1' + + testImplementation 'junit:junit:4.13.2' + testImplementation 'org.robolectric:robolectric:4.4' testImplementation 'org.mockito:mockito-inline:3.4.0' - testImplementation "androidx.work:work-testing:$work_version" + testImplementation 'androidx.work:work-testing:2.7.1' } -- GitLab From a898efe25b52e50adf140ca6a625d2ced19c4f4e Mon Sep 17 00:00:00 2001 From: Aayush Gupta Date: Wed, 11 May 2022 09:53:05 +0530 Subject: [PATCH 29/59] eDrive: Enable Java 11 language features Signed-off-by: Aayush Gupta --- app/build.gradle | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/app/build.gradle b/app/build.gradle index 0e43dcb1..543c0cfc 100644 --- a/app/build.gradle +++ b/app/build.gradle @@ -53,8 +53,8 @@ android { viewBinding true } compileOptions { - sourceCompatibility JavaVersion.VERSION_1_8 - targetCompatibility JavaVersion.VERSION_1_8 + sourceCompatibility JavaVersion.VERSION_11 + targetCompatibility JavaVersion.VERSION_11 } lint { abortOnError false -- GitLab From c3cf885875a660a566e912bf96a9bc981f7c9651 Mon Sep 17 00:00:00 2001 From: Aayush Gupta Date: Wed, 11 May 2022 09:58:08 +0530 Subject: [PATCH 30/59] eDrive: Remove extra translations without default locale Resolves lint errors: > Task :app:lintVitalRelease C:\Users\aayus\StudioProjects\eDrive\app\src\main\res\values-de\strings.xml:23: Error: "no_alias" is translated here but not found in default locale [ExtraTranslation] Keine Aliase! ~~~~~~~~~~~~~~~ C:\Users\aayus\StudioProjects\eDrive\app\src\main\res\values-de\strings.xml:25: Error: "notif_quota_execeeded_title" is translated here but not found in default locale [ExtraTranslation] Die H?chstquote des /e/-Kontos wurde erreicht ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ C:\Users\aayus\StudioProjects\eDrive\app\src\main\res\values-de\strings.xml:26: Error: "notif_quota_nearlyReached_title" is translated here but not found in default locale [ExtraTranslation] Die H?chstquote des /e/-Kontos ist fast erreicht ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ Explanation for issues of type "ExtraTranslation": If a string appears in a specific language translation file, but there is no corresponding string in the default locale, then this string is probably unused. (It's technically possible that your application is only intended to run in a specific locale, but it's still a good idea to provide a fallback.) Note that these strings can lead to crashes if the string is looked up on any locale not providing a translation, so it's important to clean them up. 3 errors, 0 warnings Signed-off-by: Aayush Gupta --- app/src/main/res/values-de/strings.xml | 5 +---- 1 file changed, 1 insertion(+), 4 deletions(-) diff --git a/app/src/main/res/values-de/strings.xml b/app/src/main/res/values-de/strings.xml index cf03e547..6e670f3c 100644 --- a/app/src/main/res/values-de/strings.xml +++ b/app/src/main/res/values-de/strings.xml @@ -20,10 +20,7 @@ Anpassen Synchronisiere deine Fotos und \n Videos sicher mit der Cloud - Keine Aliase! - eDrive-Benachrichtigungskanal - Die Höchstquote des /e/-Kontos wurde erreicht - Die Höchstquote des /e/-Kontos ist fast erreicht + eDrive Benachrichtigungskanal Es ist nicht möglich, Dateien hochzuladen, die größer sind als der verbleibende Platz im Cloud-Speicher. Bitte unternehmen Sie etwas. 99 % der Höchstquote des Cloud-Speichers erreicht. Bitte unternimm etwas. Alias -- GitLab From 5e4d85aa4631b15b2727cab247d3a3b904dd3d2f Mon Sep 17 00:00:00 2001 From: Aayush Gupta Date: Wed, 11 May 2022 09:59:56 +0530 Subject: [PATCH 31/59] eDrive: Drop lint block No errors happening anymore, nor its a good idea to supress them like this when they happen Signed-off-by: Aayush Gupta --- app/build.gradle | 3 --- 1 file changed, 3 deletions(-) diff --git a/app/build.gradle b/app/build.gradle index 543c0cfc..908bf1c5 100644 --- a/app/build.gradle +++ b/app/build.gradle @@ -56,9 +56,6 @@ android { sourceCompatibility JavaVersion.VERSION_11 targetCompatibility JavaVersion.VERSION_11 } - lint { - abortOnError false - } } -- GitLab From ec9d9b7cabee3235fa495542cc37b00dc5047336 Mon Sep 17 00:00:00 2001 From: vincent Bourgmayer Date: Thu, 2 Jun 2022 11:05:59 +0200 Subject: [PATCH 32/59] eDrive: Reformat and apply Android Studio suggestions to gradle file --- app/build.gradle | 12 +++++++----- 1 file changed, 7 insertions(+), 5 deletions(-) diff --git a/app/build.gradle b/app/build.gradle index 908bf1c5..721b03c3 100644 --- a/app/build.gradle +++ b/app/build.gradle @@ -10,9 +10,9 @@ def versionPatch = 0 def getTestProp(String propName) { def result = "" - if(project.hasProperty(propName)){ + if (project.hasProperty(propName)) { result =project.property(propName).toString() - }else if(project.rootProject.file('local.properties').exists()){ + } else if(project.rootProject.file('local.properties').exists()) { Properties properties = new Properties() properties.load(project.rootProject.file('local.properties').newReader()) result = properties.getProperty(propName) @@ -22,13 +22,15 @@ def getTestProp(String propName) { android { - compileSdkVersion 31 + compileSdk 31 defaultConfig { applicationId "foundation.e.drive" - minSdkVersion 26 + minSdk 26 + targetSdk 31 versionCode versionMajor * 1000000 + versionMinor * 1000 + versionPatch versionName "${versionMajor}.${versionMinor}.${versionPatch}" setProperty("archivesBaseName", "eDrive-$versionName") + testInstrumentationRunner "androidx.test.runner.AndroidJUnitRunner" } buildTypes { release { @@ -76,7 +78,7 @@ dependencies { androidTestImplementation 'androidx.test:rules:1.4.0' androidTestImplementation 'androidx.annotation:annotation:1.3.0' androidTestImplementation 'androidx.test.espresso:espresso-core:3.4.0' - androidTestImplementation 'junit:junit:4.13.2' + androidTestImplementation 'junit:junit:4.13.2' testImplementation 'androidx.test:runner:1.4.0' testImplementation 'androidx.test:rules:1.4.0' -- GitLab From e72c19a9e5a00e043e1b071802de609669118f05 Mon Sep 17 00:00:00 2001 From: vincent Bourgmayer Date: Thu, 2 Jun 2022 11:08:40 +0200 Subject: [PATCH 33/59] eDrive: CI: Reformat and drop non-required commands --- .gitlab-ci.yml | 16 +++++++--------- 1 file changed, 7 insertions(+), 9 deletions(-) diff --git a/.gitlab-ci.yml b/.gitlab-ci.yml index c60fad30..cdb53b00 100644 --- a/.gitlab-ci.yml +++ b/.gitlab-ci.yml @@ -1,20 +1,18 @@ image: "registry.gitlab.e.foundation/e/os/docker-android-apps-cicd:latest" stages: -- test -- build + - test + - build before_script: -- git submodule sync -- git submodule update --init --recursive -- export GRADLE_USER_HOME=$(pwd)/.gradle -- chmod +x ./gradlew + - export GRADLE_USER_HOME=$(pwd)/.gradle + - chmod +x ./gradlew cache: key: ${CI_PROJECT_ID} paths: - - .gradle/ + - .gradle/ test: @@ -35,9 +33,9 @@ test: build: stage: build script: - - ./gradlew assemble + - ./gradlew assemble artifacts: paths: - - app/build/outputs/apk/ + - app/build/outputs/apk/ expire_in: 4 weeks -- GitLab From 641015a48c1f5ca54748603df62d9950b5341f5f Mon Sep 17 00:00:00 2001 From: Aayush Gupta Date: Wed, 11 May 2022 10:19:38 +0530 Subject: [PATCH 34/59] eDrive: Export Broadcast Recievers and supress play permission warning Signed-off-by: Aayush Gupta --- app/src/main/AndroidManifest.xml | 15 ++++++++++----- 1 file changed, 10 insertions(+), 5 deletions(-) diff --git a/app/src/main/AndroidManifest.xml b/app/src/main/AndroidManifest.xml index dc5d04a4..dd1a2379 100644 --- a/app/src/main/AndroidManifest.xml +++ b/app/src/main/AndroidManifest.xml @@ -1,5 +1,6 @@ @@ -11,7 +12,9 @@ - + + android:exported="true" + android:label="@string/app_widget_description"> @@ -92,14 +95,16 @@ + android:enabled="true" + android:exported="true"> + android:enabled="true" + android:exported="true"> -- GitLab From e521d37de5406642622b8bfb6d12a9d9b5f3167c Mon Sep 17 00:00:00 2001 From: vincent Bourgmayer Date: Thu, 2 Jun 2022 14:39:36 +0200 Subject: [PATCH 35/59] fix tests sources --- app/src/test/java/foundation/e/drive/TestUtils.java | 6 +++--- .../e/drive/operations/UploadFileOperationTest.java | 13 ++++++------- .../e/drive/services/InitializerServiceTest.java | 2 +- .../e/drive/services/ObserverServiceTest.java | 2 +- 4 files changed, 11 insertions(+), 12 deletions(-) diff --git a/app/src/test/java/foundation/e/drive/TestUtils.java b/app/src/test/java/foundation/e/drive/TestUtils.java index 542a9d39..309a8503 100644 --- a/app/src/test/java/foundation/e/drive/TestUtils.java +++ b/app/src/test/java/foundation/e/drive/TestUtils.java @@ -11,7 +11,7 @@ import com.owncloud.android.lib.common.network.CertificateCombinedException; import com.owncloud.android.lib.common.network.NetworkUtils; import com.owncloud.android.lib.common.operations.RemoteOperationResult; import com.owncloud.android.lib.common.utils.Log_OC; -import com.owncloud.android.lib.resources.status.GetRemoteStatusOperation; +import com.owncloud.android.lib.resources.users.GetStatusRemoteOperation; import java.io.File; import java.io.FileWriter; @@ -90,7 +90,7 @@ public abstract class TestUtils { IOException, InterruptedException{ - GetRemoteStatusOperation getStatus = new GetRemoteStatusOperation(context); + GetStatusRemoteOperation getStatus = new GetStatusRemoteOperation(); RemoteOperationResult result = getStatus.execute(client); @@ -107,7 +107,7 @@ public abstract class TestUtils { Thread.sleep(1000); // retry - getStatus = new GetRemoteStatusOperation(context); + getStatus = new GetStatusRemoteOperation(); result = getStatus.execute(client); if (!result.isSuccess()) { diff --git a/app/src/test/java/foundation/e/drive/operations/UploadFileOperationTest.java b/app/src/test/java/foundation/e/drive/operations/UploadFileOperationTest.java index 698d06be..025d7532 100644 --- a/app/src/test/java/foundation/e/drive/operations/UploadFileOperationTest.java +++ b/app/src/test/java/foundation/e/drive/operations/UploadFileOperationTest.java @@ -14,9 +14,8 @@ import android.os.Build; import com.owncloud.android.lib.common.OwnCloudClient; import com.owncloud.android.lib.common.UserInfo; import com.owncloud.android.lib.common.operations.RemoteOperationResult; -import com.owncloud.android.lib.resources.users.GetRemoteUserInfoOperation; +import com.owncloud.android.lib.resources.users.GetUserInfoRemoteOperation; -import org.junit.After; import org.junit.Before; import org.junit.Ignore; import org.junit.Test; @@ -50,7 +49,7 @@ public class UploadFileOperationTest { private long userFreeQuota; public UploadFileOperationTest() { - context = RuntimeEnvironment.getApplication(); + context = RuntimeEnvironment.systemContext; accountManager = AccountManager.get(context); ShadowLog.stream = System.out; TestUtils.loadServerCredentials(); @@ -136,11 +135,11 @@ public class UploadFileOperationTest { } private long getUserRemoteFreeQuota() { - final GetRemoteUserInfoOperation getRemoteUserInfoOperation = new GetRemoteUserInfoOperation(); - final RemoteOperationResult ocsResult = getRemoteUserInfoOperation.execute(client); + final GetUserInfoRemoteOperation GetUserInfoRemoteOperation = new GetUserInfoRemoteOperation(); + final RemoteOperationResult ocsResult = GetUserInfoRemoteOperation.execute(client); - if (ocsResult.isSuccess() && ocsResult.getData() != null) { - UserInfo userInfo = (UserInfo) ocsResult.getData().get(0); + if (ocsResult.isSuccess()) { + UserInfo userInfo = ocsResult.getResultData(); System.out.println("User free Quotas: "+userInfo.getQuota().getFree()); System.out.println("User Total Quotas: "+userInfo.getQuota().getTotal()); diff --git a/app/src/test/java/foundation/e/drive/services/InitializerServiceTest.java b/app/src/test/java/foundation/e/drive/services/InitializerServiceTest.java index 8d5bfb3d..66e48ea0 100644 --- a/app/src/test/java/foundation/e/drive/services/InitializerServiceTest.java +++ b/app/src/test/java/foundation/e/drive/services/InitializerServiceTest.java @@ -26,7 +26,7 @@ public class InitializerServiceTest extends AbstractServiceIT { syncService = syncServiceController.get(); mServiceController = Robolectric.buildService(ObserverService.class); mService = mServiceController.get(); - context = RuntimeEnvironment.getApplication(); + context = RuntimeEnvironment.systemContext; accountManager = AccountManager.get(context); jobScheduler = (JobScheduler) context.getSystemService(Context.JOB_SCHEDULER_SERVICE); contentResolver = context.getContentResolver(); -- GitLab From b0adb17d0fcaa76cad1e9bbf930e390c61a61ec9 Mon Sep 17 00:00:00 2001 From: vincent Bourgmayer Date: Mon, 6 Jun 2022 08:55:52 +0200 Subject: [PATCH 36/59] fix Context issue, client issue and add androidx.test.core as dependency --- app/build.gradle | 1 + .../java/foundation/e/drive/TestUtils.java | 11 +++++- .../operations/UploadFileOperationTest.java | 33 +++++++++------- .../e/drive/services/AbstractServiceIT.java | 4 +- .../services/InitializerServiceTest.java | 3 +- .../e/drive/services/ObserverServiceTest.java | 39 +++++++++---------- 6 files changed, 52 insertions(+), 39 deletions(-) diff --git a/app/build.gradle b/app/build.gradle index 721b03c3..56825fd8 100644 --- a/app/build.gradle +++ b/app/build.gradle @@ -73,6 +73,7 @@ dependencies { implementation 'com.google.android.material:material:1.6.0' implementation 'com.github.bumptech.glide:glide:4.13.1' implementation "androidx.work:work-runtime:2.7.1" + implementation 'androidx.test:core:1.4.0' androidTestImplementation 'androidx.test:runner:1.4.0' androidTestImplementation 'androidx.test:rules:1.4.0' diff --git a/app/src/test/java/foundation/e/drive/TestUtils.java b/app/src/test/java/foundation/e/drive/TestUtils.java index 309a8503..1da09d5d 100644 --- a/app/src/test/java/foundation/e/drive/TestUtils.java +++ b/app/src/test/java/foundation/e/drive/TestUtils.java @@ -3,10 +3,11 @@ package foundation.e.drive; import android.accounts.Account; import android.accounts.AccountManager; import android.content.Context; +import android.net.Uri; import android.os.Bundle; import android.util.Log; -import com.owncloud.android.lib.common.OwnCloudClient; +import com.nextcloud.common.NextcloudClient; import com.owncloud.android.lib.common.network.CertificateCombinedException; import com.owncloud.android.lib.common.network.NetworkUtils; import com.owncloud.android.lib.common.operations.RemoteOperationResult; @@ -62,6 +63,12 @@ public abstract class TestUtils { return validAccount; } + + public static NextcloudClient getNcClient(Context context) { + final Uri serverUri = Uri.parse(TEST_SERVER_URI); + return new NextcloudClient(serverUri, TEST_ACCOUNT_NAME, TEST_ACCOUNT_PASSWORD, context); + } + /** * register in accountManager an account with name, password, type and server url provided at build */ @@ -84,7 +91,7 @@ public abstract class TestUtils { * @throws IOException exception * @throws InterruptedException exception */ - public static void testConnection(final OwnCloudClient client, final Context context) throws KeyStoreException, + public static void testConnection(final NextcloudClient client, final Context context) throws KeyStoreException, CertificateException, NoSuchAlgorithmException, IOException, diff --git a/app/src/test/java/foundation/e/drive/operations/UploadFileOperationTest.java b/app/src/test/java/foundation/e/drive/operations/UploadFileOperationTest.java index 025d7532..f6fe43de 100644 --- a/app/src/test/java/foundation/e/drive/operations/UploadFileOperationTest.java +++ b/app/src/test/java/foundation/e/drive/operations/UploadFileOperationTest.java @@ -11,6 +11,9 @@ import android.accounts.AccountManager; import android.content.Context; import android.os.Build; +import androidx.test.core.app.ApplicationProvider; + +import com.nextcloud.common.NextcloudClient; import com.owncloud.android.lib.common.OwnCloudClient; import com.owncloud.android.lib.common.UserInfo; import com.owncloud.android.lib.common.operations.RemoteOperationResult; @@ -22,7 +25,6 @@ import org.junit.Test; import org.junit.runner.RunWith; import org.mockito.Mockito; import org.robolectric.RobolectricTestRunner; -import org.robolectric.RuntimeEnvironment; import org.robolectric.annotation.Config; import org.robolectric.shadows.ShadowLog; @@ -43,21 +45,22 @@ import foundation.e.drive.utils.CommonUtils; public class UploadFileOperationTest { private List syncedFileStates= new ArrayList<>(); - private final OwnCloudClient client; + private final OwnCloudClient ocClient; + private final NextcloudClient ncClient; private final AccountManager accountManager; private final Context context; private long userFreeQuota; public UploadFileOperationTest() { - context = RuntimeEnvironment.systemContext; + context = ApplicationProvider.getApplicationContext(); accountManager = AccountManager.get(context); ShadowLog.stream = System.out; TestUtils.loadServerCredentials(); TestUtils.prepareValidAccount(accountManager); - client = CommonUtils.getOwnCloudClient(CommonUtils.getAccount(TestUtils.TEST_ACCOUNT_NAME, TestUtils.TEST_ACCOUNT_TYPE, accountManager), context); - + ocClient = CommonUtils.getOwnCloudClient(CommonUtils.getAccount(TestUtils.TEST_ACCOUNT_NAME, TestUtils.TEST_ACCOUNT_TYPE, accountManager), context); + ncClient = TestUtils.getNcClient(context); try { - TestUtils.testConnection(client, context); + TestUtils.testConnection(ncClient, context); } catch (Exception e) { System.out.println("test connection failed: "+e.getMessage()); } @@ -67,7 +70,7 @@ public class UploadFileOperationTest { @Before public void setUp() { prepareDB(); //Create DB - assertNotNull("Client is null. unexpected!", client); + assertNotNull("Client is null. unexpected!", ocClient); } /** @@ -123,7 +126,7 @@ public class UploadFileOperationTest { final SyncedFileState sfs = syncedFileStates.get(0); if (sfs != null) { final RemoveFileOperation removeRemoteFileOp = new RemoveFileOperation(sfs); - removeRemoteFileOp.execute(client); + removeRemoteFileOp.execute(ocClient); } } @@ -136,7 +139,7 @@ public class UploadFileOperationTest { private long getUserRemoteFreeQuota() { final GetUserInfoRemoteOperation GetUserInfoRemoteOperation = new GetUserInfoRemoteOperation(); - final RemoteOperationResult ocsResult = GetUserInfoRemoteOperation.execute(client); + final RemoteOperationResult ocsResult = GetUserInfoRemoteOperation.execute(ncClient); if (ocsResult.isSuccess()) { UserInfo userInfo = ocsResult.getResultData(); @@ -163,7 +166,7 @@ public class UploadFileOperationTest { boolean checkEtag = false; UploadFileOperation testOperation = new UploadFileOperation(syncedFileStates.get(0), context); - final RemoteOperationResult result = testOperation.execute(client); + final RemoteOperationResult result = testOperation.execute(ocClient); String errorMsg = "The upload failed:\n http code: "+result.getHttpCode() +"\n, is success ?"+result.isSuccess() +"\n, log msg: "+result.getLogMessage() @@ -193,7 +196,7 @@ public class UploadFileOperationTest { // check https://gitlab.e.foundation/e/apps/eDrive/-/issues/120 final UploadFileOperation testOperation = new UploadFileOperation(syncedFileState, context); - RemoteOperationResult result = testOperation.execute(client); + RemoteOperationResult result = testOperation.execute(ocClient); assertEquals("Expected result code was FORBIDDEN but got: "+result.getCode().name(), RemoteOperationResult.ResultCode.FORBIDDEN, result.getCode()); } @@ -216,7 +219,7 @@ public class UploadFileOperationTest { final File smallFile = new File(sfs_fromDB.getLocalPath()); assertTrue("Local file deletion return false instead of true", smallFile.delete()); - final RemoteOperationResult result = testOperation.execute(client); + final RemoteOperationResult result = testOperation.execute(ocClient); assertEquals("Expected result code was FORBIDDEN but got: "+result.getCode().name(), RemoteOperationResult.ResultCode.FORBIDDEN, result.getCode()); } @@ -230,7 +233,7 @@ public class UploadFileOperationTest { assertFalse("Reading remote free quota fails"+userFreeQuota, -1 == userFreeQuota); //We don't care of parameter of UploadFileOperation's constructor final RemoteOperationResult actualResult = new UploadFileOperation(Mockito.mock(SyncedFileState.class), context) - .checkAvailableSpace(client, (userFreeQuota+1)); + .checkAvailableSpace(ocClient, (userFreeQuota+1)); assertEquals("Quota check ("+ userFreeQuota+"vs"+(userFreeQuota+1)+") failed", RemoteOperationResult.ResultCode.QUOTA_EXCEEDED, actualResult.getCode()); } @@ -244,7 +247,7 @@ public class UploadFileOperationTest { assertFalse("Reading remote free quota fails"+userFreeQuota, -1 == userFreeQuota); final RemoteOperationResult actualResult = new UploadFileOperation(Mockito.mock(SyncedFileState.class), context) - .checkAvailableSpace(client, userFreeQuota); + .checkAvailableSpace(ocClient, userFreeQuota); assertNotEquals("Quota check is Quota Exceeded ("+ userFreeQuota+" vs "+userFreeQuota+")", RemoteOperationResult.ResultCode.QUOTA_EXCEEDED, actualResult.getCode()); @@ -264,7 +267,7 @@ public class UploadFileOperationTest { assertFalse("Reading remote free quota fails "+userFreeQuota, -1 == userFreeQuota); final RemoteOperationResult actualResult = new UploadFileOperation(Mockito.mock(SyncedFileState.class), context) - .checkAvailableSpace(client, (userFreeQuota-1)); + .checkAvailableSpace(ocClient, (userFreeQuota-1)); assertEquals("Quota check ("+ userFreeQuota+" vs "+(userFreeQuota-1)+") failed", RemoteOperationResult.ResultCode.OK, actualResult.getCode()); diff --git a/app/src/test/java/foundation/e/drive/services/AbstractServiceIT.java b/app/src/test/java/foundation/e/drive/services/AbstractServiceIT.java index a1631078..314e1141 100644 --- a/app/src/test/java/foundation/e/drive/services/AbstractServiceIT.java +++ b/app/src/test/java/foundation/e/drive/services/AbstractServiceIT.java @@ -25,6 +25,8 @@ import static foundation.e.drive.TestUtils.TEST_ACCOUNT_TYPE; import static foundation.e.drive.utils.AppConstants.MEDIASYNC_PROVIDER_AUTHORITY; import static foundation.e.drive.utils.AppConstants.SETTINGSYNC_PROVIDER_AUTHORITY; +import com.nextcloud.common.NextcloudClient; + @RunWith(RobolectricTestRunner.class) @Config(sdk = Build.VERSION_CODES.O, manifest = Config.NONE) public abstract class AbstractServiceIT { @@ -45,7 +47,7 @@ public abstract class AbstractServiceIT { protected ConnectivityManager connectivityManager; protected JobScheduler jobScheduler; protected DbHelper dbHelper; - + protected NextcloudClient client; protected int initial_folder_number=0; //number of folders to sync at initialization protected long last_sync_time=0l; //Timestamp of the end of the last synchronisation protected boolean init_done = true; //true if InitializerService did its job diff --git a/app/src/test/java/foundation/e/drive/services/InitializerServiceTest.java b/app/src/test/java/foundation/e/drive/services/InitializerServiceTest.java index 66e48ea0..725d8bd8 100644 --- a/app/src/test/java/foundation/e/drive/services/InitializerServiceTest.java +++ b/app/src/test/java/foundation/e/drive/services/InitializerServiceTest.java @@ -9,6 +9,7 @@ import android.content.Context; import android.net.ConnectivityManager; +import androidx.test.core.app.ApplicationProvider; import androidx.work.WorkManager; import org.junit.Test; @@ -26,7 +27,7 @@ public class InitializerServiceTest extends AbstractServiceIT { private final ServiceController syncServiceController; private final SynchronizationService syncService; + public ObserverServiceTest(){ syncServiceController = Robolectric.buildService(SynchronizationService.class); syncService = syncServiceController.get(); mServiceController = Robolectric.buildService(ObserverService.class); mService = mServiceController.get(); - context = RuntimeEnvironment.systemContext; + context = ApplicationProvider.getApplicationContext(); accountManager = AccountManager.get(context); jobScheduler = (JobScheduler) context.getSystemService(Context.JOB_SCHEDULER_SERVICE); contentResolver = context.getContentResolver(); sharedPreferences = context.getSharedPreferences( AppConstants.SHARED_PREFERENCE_NAME, Context.MODE_PRIVATE); connectivityManager = (ConnectivityManager) context.getSystemService(Context.CONNECTIVITY_SERVICE); dbHelper = new DbHelper(context); + client = TestUtils.getNcClient(context); } /** * Set the network status to an available wifi */ - private void setWifiNetworkStatus(){ + private void setWifiNetworkStatus() { NetworkInfo netInfo = ShadowNetworkInfo.newInstance(null, ConnectivityManager.TYPE_WIFI, 0, true, NetworkInfo.State.CONNECTED); assertEquals("NetworkInfo type is invalid",ConnectivityManager.TYPE_WIFI,netInfo.getType()); @@ -72,7 +78,7 @@ public class ObserverServiceTest extends AbstractServiceIT { /** * Create a network status where no connection is available */ - private void setUnavailableWifiNetworkStatus(){ + private void setUnavailableWifiNetworkStatus() { NetworkInfo netInfo = ShadowNetworkInfo.newInstance(null, ConnectivityManager.TYPE_WIFI, 0, true, NetworkInfo.State.DISCONNECTED); assertEquals("NetworkInfo type is invalid",ConnectivityManager.TYPE_WIFI,netInfo.getType()); @@ -84,7 +90,7 @@ public class ObserverServiceTest extends AbstractServiceIT { * Create a single 'SyncedFolder' instance for 'eDrive-test' folder * @return SyncedFolder instance */ - private SyncedFolder createSingleTestSyncedFolder(){ + private SyncedFolder createSingleTestSyncedFolder() { final File folder = new File(TEST_LOCAL_ROOT_FOLDER_PATH); try{ folder.mkdirs(); @@ -101,9 +107,7 @@ public class ObserverServiceTest extends AbstractServiceIT { * Send request to server to create the remote folder of the given syncedFolder * @param folder the local folder metadata to create */ - private void createRemoteFolder(SyncedFolder folder){ - final OwnCloudClient client = CommonUtils.getOwnCloudClient(getValidAccount(), context); - + private void createRemoteFolder(SyncedFolder folder) { try { TestUtils.testConnection(client, context); }catch(Exception e){ @@ -151,12 +155,10 @@ public class ObserverServiceTest extends AbstractServiceIT { */ @Ignore("Ignore until a way to prepare unavailable Network has been founded") @Test - public void noNetwork_shouldStop(){ - //setWifiNetworkStatus(); Make the test to fail as expected - //setUnavailableWifiNetworkStatus(); //Doesn't give the expected result at the moment so the test fails! + public void noNetwork_shouldStop() { prepareValidAccount(); enableMediaAndSettingsSync(getValidAccount()); - //createRemoteSyncedFolder(createSingleTestSyncedFolder()); + registerSharedPref(); boolean haveNetworkConnexion = CommonUtils.haveNetworkConnection(RuntimeEnvironment.application, true); @@ -177,12 +179,11 @@ public class ObserverServiceTest extends AbstractServiceIT { */ @Ignore("Binding to synchronizationService make test fails") @Test - public void lastSyncWasLessThan15minAgo_shouldStop(){ + public void lastSyncWasLessThan15minAgo_shouldStop() { last_sync_time = System.currentTimeMillis() - 899900; setWifiNetworkStatus(); prepareValidAccount(); enableMediaAndSettingsSync(getValidAccount()); - //createRemoteSyncedFolder(createSingleTestSyncedFolder()); registerSharedPref(); //Start the service @@ -203,13 +204,12 @@ public class ObserverServiceTest extends AbstractServiceIT { */ @Ignore("Binding to synchronizationService make test fails") @Test - public void lastSync15minAnd30secAgo_shouldStart(){ + public void lastSync15minAnd30secAgo_shouldStart() { //decrease 15min and 30sec last_sync_time = System.currentTimeMillis() - 930000; setWifiNetworkStatus(); prepareValidAccount(); enableMediaAndSettingsSync(getValidAccount()); - //createRemoteSyncedFolder(createSingleTestSyncedFolder()); registerSharedPref(); syncServiceController.create().startCommand(0, 0); @@ -227,7 +227,7 @@ public class ObserverServiceTest extends AbstractServiceIT { */ @Ignore("Not yet implemented") @Test - public void syncAlreadyStarted_shouldStop(){ + public void syncAlreadyStarted_shouldStop() { //@TODO need to find how to access the "isRunning" private field inside the ObserverService for this test fail("Not yet implemented "); } @@ -238,7 +238,7 @@ public class ObserverServiceTest extends AbstractServiceIT { */ @Ignore("Binding to synchronizationService make test fails") @Test - public void noAccount_shouldStop(){ + public void noAccount_shouldStop() { mServiceController.create().startCommand(0, 0); @@ -255,13 +255,12 @@ public class ObserverServiceTest extends AbstractServiceIT { */ @Ignore("Binding to synchronizationService make test fails") @Test - public void InitializationNotDone_shouldStop(){ + public void InitializationNotDone_shouldStop() { init_done = false; //This is the key settings for this test setWifiNetworkStatus(); prepareValidAccount(); enableMediaAndSettingsSync(getValidAccount()); - //createRemoteSyncedFolder(createSingleTestSyncedFolder()); registerSharedPref(); -- GitLab From e7edb2edb71a79f735d537548d109d0236c0034f Mon Sep 17 00:00:00 2001 From: vincent Bourgmayer Date: Mon, 6 Jun 2022 10:36:08 +0200 Subject: [PATCH 37/59] Replace OwncloudClient by NextcloudClient - Add new field ncClientInstance in DAVClientProvider - Add new method getNcClientInstance in DAVClientProvider - Call this method in CreateRemoteFolderWorker.java & AccountUserInfoWorker.java --- .../services/SynchronizationService.java | 12 ++++--- .../foundation/e/drive/utils/CommonUtils.java | 3 +- .../e/drive/utils/DavClientProvider.java | 35 +++++++++++++++---- .../e/drive/work/AccountUserInfoWorker.java | 16 ++++----- .../drive/work/CreateRemoteFolderWorker.java | 20 +++++------ 5 files changed, 55 insertions(+), 31 deletions(-) diff --git a/app/src/main/java/foundation/e/drive/services/SynchronizationService.java b/app/src/main/java/foundation/e/drive/services/SynchronizationService.java index c9d932c7..a5ba992b 100644 --- a/app/src/main/java/foundation/e/drive/services/SynchronizationService.java +++ b/app/src/main/java/foundation/e/drive/services/SynchronizationService.java @@ -22,12 +22,12 @@ import android.util.Log; import androidx.annotation.Nullable; +import com.nextcloud.common.NextcloudClient; import com.owncloud.android.lib.common.OwnCloudClient; import com.owncloud.android.lib.common.operations.OnRemoteOperationListener; import com.owncloud.android.lib.common.operations.RemoteOperation; import com.owncloud.android.lib.common.operations.RemoteOperationResult; -import java.util.ArrayList; import java.util.Collection; import java.util.Map; import java.util.concurrent.ConcurrentHashMap; @@ -56,7 +56,9 @@ public class SynchronizationService extends Service implements OnRemoteOperation private Account account; private final int workerAmount = 4; private Thread[] threadPool; - private OwnCloudClient client; + @Deprecated + private OwnCloudClient ocClient; + private NextcloudClient ncClient; private Handler handler; @Override @@ -77,7 +79,8 @@ public class SynchronizationService extends Service implements OnRemoteOperation syncRequestQueue = new ConcurrentLinkedDeque<>(); startedSync = new ConcurrentHashMap<>(); threadPool = new Thread[workerAmount]; - client = DavClientProvider.getInstance().getClientInstance(account, getApplicationContext()); + ocClient = DavClientProvider.getInstance().getClientInstance(account, getApplicationContext()); + ncClient = DavClientProvider.getInstance().getNcClientInstance(account, getApplicationContext()); handler = new Handler(); return START_REDELIVER_INTENT; @@ -160,8 +163,7 @@ public class SynchronizationService extends Service implements OnRemoteOperation CommonUtils.createNotificationChannel(this); Log.v(TAG, " starts " + request.getSyncedFileState().getName() + " " + request.getOperationType().name() + " on thread " + threadIndex); - - threadPool[threadIndex] = operation.execute(client, this, handler); + threadPool[threadIndex] = operation.execute(ocClient, this, handler); startedSync.put(threadIndex, syncWrapper); } } diff --git a/app/src/main/java/foundation/e/drive/utils/CommonUtils.java b/app/src/main/java/foundation/e/drive/utils/CommonUtils.java index b9a12691..0ec51f48 100644 --- a/app/src/main/java/foundation/e/drive/utils/CommonUtils.java +++ b/app/src/main/java/foundation/e/drive/utils/CommonUtils.java @@ -30,6 +30,7 @@ import android.util.Log; import android.webkit.MimeTypeMap; import android.widget.Toast; +import com.nextcloud.common.NextcloudClient; import com.owncloud.android.lib.common.OwnCloudBasicCredentials; import com.owncloud.android.lib.common.OwnCloudClient; import com.owncloud.android.lib.common.OwnCloudClientFactory; @@ -188,7 +189,7 @@ public abstract class CommonUtils { */ public static OwnCloudClient getOwnCloudClient(Account account, Context context) { Log.i(TAG, "getOwnCloudClient()"); - Uri serverUri; + final Uri serverUri; OwnCloudClient oc; try { serverUri = Uri.parse(AccountUtils.getBaseUrlForAccount(context, account)); diff --git a/app/src/main/java/foundation/e/drive/utils/DavClientProvider.java b/app/src/main/java/foundation/e/drive/utils/DavClientProvider.java index 02cb9142..6d5fc6e7 100644 --- a/app/src/main/java/foundation/e/drive/utils/DavClientProvider.java +++ b/app/src/main/java/foundation/e/drive/utils/DavClientProvider.java @@ -10,24 +10,47 @@ package foundation.e.drive.utils; import android.accounts.Account; +import android.accounts.AccountManager; import android.content.Context; +import android.net.Uri; +import android.util.Log; +import com.nextcloud.common.NextcloudClient; import com.owncloud.android.lib.common.OwnCloudClient; +import com.owncloud.android.lib.common.accounts.AccountUtils; /** * @author Vincent Bourgmayer */ public class DavClientProvider { - + private static final String TAG = DavClientProvider.class.getSimpleName(); private DavClientProvider (){} - private OwnCloudClient clientInstance; + @Deprecated + private OwnCloudClient ocClientInstance; + private NextcloudClient ncClientInstance; + + public OwnCloudClient getClientInstance(final Account account, final Context ctx) { + if (ocClientInstance == null) { + this.ocClientInstance = CommonUtils.getOwnCloudClient(account, ctx); + } + return ocClientInstance; + } - public OwnCloudClient getClientInstance(Account account, Context ctx) { - if(clientInstance == null){ - this.clientInstance = CommonUtils.getOwnCloudClient(account, ctx); + public NextcloudClient getNcClientInstance(final Account account, final Context ctx) { + Log.i(TAG, "getNcClientInstance()"); + if (ncClientInstance == null) { + final Uri serverUri; + try { + serverUri = Uri.parse(AccountUtils.getBaseUrlForAccount(ctx, account)); + } catch (AccountUtils.AccountNotFoundException e) { + Log.e(TAG, "Can't get server URI for account: "+account.name+"\n"+e.getMessage()); + return null; + } + final String password = AccountManager.get(ctx).getPassword(account); + ncClientInstance = new NextcloudClient(serverUri, account.name, password, ctx); } - return clientInstance; + return ncClientInstance; } /** Holder */ diff --git a/app/src/main/java/foundation/e/drive/work/AccountUserInfoWorker.java b/app/src/main/java/foundation/e/drive/work/AccountUserInfoWorker.java index 9817efc0..02d7ad04 100644 --- a/app/src/main/java/foundation/e/drive/work/AccountUserInfoWorker.java +++ b/app/src/main/java/foundation/e/drive/work/AccountUserInfoWorker.java @@ -30,7 +30,7 @@ import androidx.work.WorkerParameters; import com.bumptech.glide.Glide; import com.bumptech.glide.load.engine.DiskCacheStrategy; -import com.owncloud.android.lib.common.OwnCloudClient; +import com.nextcloud.common.NextcloudClient; import com.owncloud.android.lib.common.Quota; import com.owncloud.android.lib.common.UserInfo; import com.owncloud.android.lib.common.operations.RemoteOperationResult; @@ -43,6 +43,7 @@ import foundation.e.drive.activity.AccountsActivity; import foundation.e.drive.operations.GetAliasOperation; import foundation.e.drive.utils.AppConstants; import foundation.e.drive.utils.CommonUtils; +import foundation.e.drive.utils.DavClientProvider; import foundation.e.drive.widgets.EDriveWidget; /** @@ -68,13 +69,12 @@ public class AccountUserInfoWorker extends Worker { public Result doWork() { account = CommonUtils.getAccount(mContext.getString(R.string.eelo_account_type), accountManager); - final OwnCloudClient client = CommonUtils.getOwnCloudClient(account, mContext); - - if (account != null && client != null) { + final NextcloudClient client = DavClientProvider.getInstance().getNcClientInstance(account, mContext); + if (client != null) { if (fetchUserInfo(client) && fetchAliases(client)) { Glide.with(mContext) .load(client.getBaseUri() + AccountsActivity.NON_OFFICIAL_AVATAR_PATH - + client.getCredentials().getUsername() + "/" + 300) + + client.getUserId() + "/" + 300) .diskCacheStrategy(DiskCacheStrategy.ALL) .preload(); updateWidget(mContext); @@ -86,7 +86,7 @@ public class AccountUserInfoWorker extends Worker { return Result.failure(); } - private boolean fetchUserInfo(final OwnCloudClient client) { + private boolean fetchUserInfo(final NextcloudClient client) { final RemoteOperationResult ocsResult = GetUserInfoRemoteOperation.execute(client); if (ocsResult.isSuccess()) { @@ -127,7 +127,7 @@ public class AccountUserInfoWorker extends Worker { } } - private boolean needToSendNotification(Context context) { + private boolean needToSendNotification(final Context context) { final String LAST_NOTIFICATION_TIMESTAMP_KEY = "last_notification_timestamp"; final long MILLI_SECONDS_IN_A_DAY = 24 * 60 * 60 * 1000; @@ -170,7 +170,7 @@ public class AccountUserInfoWorker extends Worker { manager.notify(0, builder.build()); } - private boolean fetchAliases(final OwnCloudClient client) { + private boolean fetchAliases(final NextcloudClient client) { final RemoteOperationResult> ocsResult = getAliasOperation.execute(client); if (!ocsResult.isSuccess()) { return false; diff --git a/app/src/main/java/foundation/e/drive/work/CreateRemoteFolderWorker.java b/app/src/main/java/foundation/e/drive/work/CreateRemoteFolderWorker.java index e510fdaf..d6b06f72 100644 --- a/app/src/main/java/foundation/e/drive/work/CreateRemoteFolderWorker.java +++ b/app/src/main/java/foundation/e/drive/work/CreateRemoteFolderWorker.java @@ -19,7 +19,7 @@ import androidx.work.Data; import androidx.work.Worker; import androidx.work.WorkerParameters; -import com.owncloud.android.lib.common.OwnCloudClient; +import com.nextcloud.common.NextcloudClient; import com.owncloud.android.lib.common.operations.RemoteOperationResult; import com.owncloud.android.lib.resources.files.CreateFolderRemoteOperation; @@ -29,6 +29,7 @@ import foundation.e.drive.database.DbHelper; import foundation.e.drive.models.SyncedFolder; import foundation.e.drive.utils.AppConstants; import foundation.e.drive.utils.CommonUtils; +import foundation.e.drive.utils.DavClientProvider; /** * Create folder on ecloud for a given local folder @@ -75,7 +76,7 @@ public class CreateRemoteFolderWorker extends Worker { syncedFolder.setLastModified(folder.lastModified()); } - final OwnCloudClient client = CommonUtils.getOwnCloudClient(account, context); + final NextcloudClient client = DavClientProvider.getInstance().getNcClientInstance(account, context); if (client == null) { Log.e(TAG, "Can't get OwnCloudClient."); return Result.retry(); @@ -84,17 +85,14 @@ public class CreateRemoteFolderWorker extends Worker { final CreateFolderRemoteOperation mkcolRequest = new CreateFolderRemoteOperation(syncedFolder.getRemoteFolder(), true); - try { - final RemoteOperationResult result = mkcolRequest.execute(client); - if (result.isSuccess() || result.getCode() == RemoteOperationResult.ResultCode.FOLDER_ALREADY_EXISTS) { - if(DbHelper.insertSyncedFolder(syncedFolder, context) >= 0 ) { - Log.d(TAG, "Insertion in DB succeed"); - } - return Result.success(); + final RemoteOperationResult result = mkcolRequest.execute(client); + if (result.isSuccess() || result.getCode() == RemoteOperationResult.ResultCode.FOLDER_ALREADY_EXISTS) { + if(DbHelper.insertSyncedFolder(syncedFolder, context) >= 0 ) { + Log.d(TAG, "Insertion in DB succeed"); } - } catch (Exception e) { - Log.e(TAG, "Exception: "+e.getClass().getSimpleName()); + return Result.success(); } + return Result.retry(); } -- GitLab From b0b0f4936ea6a1933fc348b32dc76a9dc0db243d Mon Sep 17 00:00:00 2001 From: vincent Bourgmayer Date: Tue, 7 Jun 2022 10:04:22 +0200 Subject: [PATCH 38/59] update ListFileRemoteOperation to provide output without deprecated method --- .../ObserverServiceInstrumentedTest.java | 100 ------------------ .../operations/ListFileRemoteOperation.java | 11 +- .../e/drive/services/ObserverService.java | 76 +++++++------ 3 files changed, 42 insertions(+), 145 deletions(-) delete mode 100644 app/src/androidTest/java/foundation/e/drive/services/ObserverServiceInstrumentedTest.java diff --git a/app/src/androidTest/java/foundation/e/drive/services/ObserverServiceInstrumentedTest.java b/app/src/androidTest/java/foundation/e/drive/services/ObserverServiceInstrumentedTest.java deleted file mode 100644 index bd5fe22c..00000000 --- a/app/src/androidTest/java/foundation/e/drive/services/ObserverServiceInstrumentedTest.java +++ /dev/null @@ -1,100 +0,0 @@ -package foundation.e.drive.services; - -import android.accounts.Account; -import android.accounts.AccountManager; -import android.app.Service; -import android.content.Context; -import android.content.Intent; -import android.os.Environment; -import android.support.test.InstrumentationRegistry; -import android.support.test.rule.ServiceTestRule; -import android.support.test.runner.AndroidJUnit4; - -import org.junit.After; -import org.junit.Before; -import org.junit.Rule; -import org.junit.Test; -import org.junit.runner.RunWith; - -import java.io.File; -import java.io.FileOutputStream; -import java.io.IOException; -import java.io.OutputStream; -import java.util.concurrent.TimeoutException; - -import static android.support.test.InstrumentationRegistry.getContext; -import static org.junit.Assert.*; - - - - -/** - * Instrumented test, which will execute on an Android device. - * - * @see Testing documentation - */ -@RunWith(AndroidJUnit4.class) -public class ObserverServiceInstrumentedTest { - private final static String NEW_SMALL_FILE_PATH = Environment.getExternalStorageDirectory() + File.separator + Environment.DIRECTORY_DOCUMENTS+File.separator+"test-small-file.txt"; - private final static String ACCOUNT_NAME= ""; - private final static String ACCOUNT_PASS=""; - private final static String ACCOUNT_TYPE=""; - - - @Rule - public final ServiceTestRule mServiceRule = new ServiceTestRule(); - - - @Before - public void RegisterAccount() throws Exception { - final Account account = new Account(ACCOUNT_NAME, ACCOUNT_TYPE); - AccountManager.get(getContext()).addAccountExplicitly(account, ACCOUNT_PASS, null); - - } - - - /** - * Souldn't it be more for a unit test ? - * @throws IOException - */ - @Before - public void createLocalSmallFile() throws IOException { - - File file = new File(NEW_SMALL_FILE_PATH); - file.createNewFile(); - String content = "this a very small content"; -//write the bytes in file - if(file.exists()) - { - OutputStream fo = new FileOutputStream(file); - fo.write(content.getBytes()); - fo.close(); - System.out.println("file created: "+file); - } - } - - - @After - public void deleteLocalSmallFile(){ - File file = new File(NEW_SMALL_FILE_PATH); - //deleting the file - file.delete(); - } - - - @Test - public void testWithStartedService() throws TimeoutException { - mServiceRule.startService( - new Intent(InstrumentationRegistry.getTargetContext(), ObserverService.class)); - //do something - } - - @Test - public void useAppContext() throws Exception { - // Context of the app under test. - Context appContext = InstrumentationRegistry.getTargetContext(); - - - assertEquals("foundation.e.drive", appContext.getPackageName()); - } -} diff --git a/app/src/main/java/foundation/e/drive/operations/ListFileRemoteOperation.java b/app/src/main/java/foundation/e/drive/operations/ListFileRemoteOperation.java index 133ea5d9..08709c24 100644 --- a/app/src/main/java/foundation/e/drive/operations/ListFileRemoteOperation.java +++ b/app/src/main/java/foundation/e/drive/operations/ListFileRemoteOperation.java @@ -30,7 +30,7 @@ import foundation.e.drive.utils.CommonUtils; * @author Vincent Bourgmayer * Created on 04/05/2018. */ -public class ListFileRemoteOperation extends RemoteOperation { +public class ListFileRemoteOperation extends RemoteOperation> { private final String TAG = ListFileRemoteOperation.class.getSimpleName(); private final List mSyncedFolders; @@ -49,9 +49,9 @@ public class ListFileRemoteOperation extends RemoteOperation { * @return List containing remoteFolder followed by remote files */ @Override - protected RemoteOperationResult run(OwnCloudClient ownCloudClient) { + protected RemoteOperationResult> run(OwnCloudClient ownCloudClient) { Log.i(TAG, "run()"); - final ArrayList mRemoteFiles = new ArrayList<>(); + final ArrayList mRemoteFiles = new ArrayList<>(); RemoteOperationResult finalResult; boolean atLeastOneDirAsChanged = false; @@ -130,12 +130,11 @@ public class ListFileRemoteOperation extends RemoteOperation { Log.w(TAG, "ReadFolderRemoteOperation failed : http " + result.getHttpCode() + ", " + result.getLogMessage() + " => Ignored"); } } - finalResult = new RemoteOperationResult(RemoteOperationResult.ResultCode.OK); + finalResult = new RemoteOperationResult<>(RemoteOperationResult.ResultCode.OK); if (atLeastOneDirAsChanged) { DbHelper.updateSyncedFolders(this.mSyncedFolders, this.mContext); - finalResult.setData(new ArrayList(mRemoteFiles)); + finalResult.setResultData(mRemoteFiles); } - Log.v(TAG, "end of run()"); return finalResult; } diff --git a/app/src/main/java/foundation/e/drive/services/ObserverService.java b/app/src/main/java/foundation/e/drive/services/ObserverService.java index 38cc3b5c..133abbab 100644 --- a/app/src/main/java/foundation/e/drive/services/ObserverService.java +++ b/app/src/main/java/foundation/e/drive/services/ObserverService.java @@ -91,7 +91,7 @@ public class ObserverService extends Service implements OnRemoteOperationListene CommonUtils.setServiceUnCaughtExceptionHandler(this); - SharedPreferences prefs = this.getSharedPreferences(AppConstants.SHARED_PREFERENCE_NAME, Context.MODE_PRIVATE); + final SharedPreferences prefs = this.getSharedPreferences(AppConstants.SHARED_PREFERENCE_NAME, Context.MODE_PRIVATE); String accountName = prefs.getString(AccountManager.KEY_ACCOUNT_NAME, ""); String accountType = prefs.getString(AccountManager.KEY_ACCOUNT_TYPE, ""); this.mAccount = CommonUtils.getAccount(accountName, accountType, AccountManager.get(this)); @@ -243,8 +243,7 @@ public class ObserverService extends Service implements OnRemoteOperationListene OwnCloudClient client = DavClientProvider.getInstance().getClientInstance(mAccount, getApplicationContext()); if (client != null) { try { - Log.d(TAG, "Going to scan remote files"); - ListFileRemoteOperation loadOperation = new ListFileRemoteOperation(this.mSyncedFolders, this, this.initialFolderCounter); + final ListFileRemoteOperation loadOperation = new ListFileRemoteOperation(this.mSyncedFolders, this, this.initialFolderCounter); loadOperation.execute(client, this, new Handler()); } catch (IllegalArgumentException e){ Log.e(TAG, e.toString() ); @@ -258,7 +257,6 @@ public class ObserverService extends Service implements OnRemoteOperationListene } } - /** * Get list of synced folder depending of if media and setting sync are enabled. * @return @@ -286,43 +284,44 @@ public class ObserverService extends Service implements OnRemoteOperationListene @Override public void onRemoteOperationFinish(RemoteOperation operation, RemoteOperationResult result ) { Log.i( TAG, "onRemoteOperationFinish()" ); - if ( operation instanceof ListFileRemoteOperation) { - if (result.isSuccess()) { - List resultDatas = result.getData(); - if (resultDatas != null) { - mSyncedFolders = ((ListFileRemoteOperation) operation).getSyncedFolderList(); - List syncedFileStateList = DbHelper.getSyncedFileStatesByFolders(this, - getIdsFromFolderToScan()); - - //At least one list is not empty - if (!resultDatas.isEmpty() || !syncedFileStateList.isEmpty() && CommonUtils.isMediaSyncEnabled(mAccount)) { - handleRemoteFiles(resultDatas, syncedFileStateList); - } else { - Log.v(TAG, "No remote files nor syncedFileStates. Go next step"); - } + if ( ! (operation instanceof ListFileRemoteOperation)) { return;} + if (result.isSuccess()) { + final List remoteFiles = ((RemoteOperationResult>)result).getResultData(); + if (remoteFiles != null) { + final ListFileRemoteOperation listFileOperation = (ListFileRemoteOperation) operation; + mSyncedFolders = listFileOperation.getSyncedFolderList(); + final List syncedFileStateList = DbHelper.getSyncedFileStatesByFolders(this, + getIdsFromFolderToScan()); + + //At least one list is not empty + if (!remoteFiles.isEmpty() || !syncedFileStateList.isEmpty() && CommonUtils.isMediaSyncEnabled(mAccount)) { + handleRemoteFiles(remoteFiles, syncedFileStateList); + } else { + Log.v(TAG, "No remote files nor syncedFileStates. Go next step"); } - } else { - Log.w(TAG, "ListRemoteFileOperation doesn't return a success: " + result.getHttpCode()); } - this.startScan(false); - - Log.v(TAG, "operationsForIntent contains " + syncRequests.size()); + } else { + Log.w(TAG, "ListRemoteFileOperation doesn't return a success: " + result.getHttpCode()); + } + this.startScan(false); + Log.v(TAG, "operationsForIntent contains " + syncRequests.size()); - //After everything has been scanned. Send Intent to OperationmanagerService with data in bundle - if (syncRequests != null && !syncRequests.isEmpty()) { - passSyncRequestsToSynchronizationService(); - } else { - Log.w(TAG, "There is no file to sync."); - getSharedPreferences(AppConstants.SHARED_PREFERENCE_NAME, Context.MODE_PRIVATE) - .edit() - .putLong(AppConstants.KEY_LAST_SYNC_TIME, System.currentTimeMillis()) - .apply(); - } - this.isWorking = false; - this.stopSelf(); - } + if (syncRequests != null && !syncRequests.isEmpty()) { + passSyncRequestsToSynchronizationService(); + } else { + Log.w(TAG, "There is no file to sync."); + getSharedPreferences(AppConstants.SHARED_PREFERENCE_NAME, Context.MODE_PRIVATE) + .edit() + .putLong(AppConstants.KEY_LAST_SYNC_TIME, System.currentTimeMillis()) + .apply(); } + this.isWorking = false; + this.stopSelf(); + } + /** + * Send all gathered SyncRequest to SynchronizationService + */ private void passSyncRequestsToSynchronizationService() { if (synchronizationServiceConnection.isBoundToSynchronizationService()) { synchronizationServiceConnection.getSynchronizationService().queueSyncRequests(syncRequests.values()); @@ -356,14 +355,14 @@ public class ObserverService extends Service implements OnRemoteOperationListene * @param remoteFiles Remote Files to inspect * @param syncedFileStates SyncedFileState to inspect */ - private void handleRemoteFiles(List remoteFiles, List syncedFileStates ){ + private void handleRemoteFiles(List remoteFiles, List syncedFileStates ){ Log.i(TAG, "handleRemoteFiles()"); Log.d(TAG, "start to loop through remoteFiles"); ListIterator syncedFileListIterator; for( int i =-1, size = remoteFiles.size(); ++i < size; ){ - final RemoteFile remoteFile = (RemoteFile) remoteFiles.get(i); + final RemoteFile remoteFile = remoteFiles.get(i); final String remoteFilePath = remoteFile.getRemotePath(); // hidden file from server has already been filtered in previous step @@ -735,7 +734,6 @@ public class ObserverService extends Service implements OnRemoteOperationListene } //end of test if folder path match with file's parent path }//end of loop over folder }//end of loop over local files - //Handle remaining file handleLocalRemainingSyncedFileState( syncedFileStates ); } -- GitLab From 2989149be085ad982a8c2b5bc4fee1d55cd612ac Mon Sep 17 00:00:00 2001 From: vincent Bourgmayer Date: Tue, 7 Jun 2022 11:25:39 +0200 Subject: [PATCH 39/59] Use nextcloudClient where required --- .../e/drive/models/SyncWrapper.java | 9 +++++---- .../operations/DownloadFileOperation.java | 1 + .../e/drive/operations/GetAliasOperation.java | 1 + .../operations/ListFileRemoteOperation.java | 2 +- .../drive/operations/UploadFileOperation.java | 20 +++++++++++-------- .../services/SynchronizationService.java | 2 +- .../drive/work/CreateRemoteFolderWorker.java | 9 ++++++--- 7 files changed, 27 insertions(+), 17 deletions(-) diff --git a/app/src/main/java/foundation/e/drive/models/SyncWrapper.java b/app/src/main/java/foundation/e/drive/models/SyncWrapper.java index 598f5182..f6bc9313 100644 --- a/app/src/main/java/foundation/e/drive/models/SyncWrapper.java +++ b/app/src/main/java/foundation/e/drive/models/SyncWrapper.java @@ -7,6 +7,7 @@ */ package foundation.e.drive.models; +import android.accounts.Account; import android.content.Context; import com.owncloud.android.lib.common.operations.RemoteOperation; @@ -29,9 +30,9 @@ public class SyncWrapper { * @param request SyncRequest at origin of the file transfer * @param context Application context, used to create RemoteOperation to run */ - public SyncWrapper(SyncRequest request, Context context) { + public SyncWrapper(final SyncRequest request, final Account account, final Context context) { this.request = request; - remoteOperation = createRemoteOperation(request, context); + remoteOperation = createRemoteOperation(request, account, context); isRunning = true; } @@ -53,12 +54,12 @@ public class SyncWrapper { * @param context App context to be passed to RemoteOperation's contructor * @return RemoteOperation for Upload/Download request or null */ - private static RemoteOperation createRemoteOperation(SyncRequest request, Context context) { + private static RemoteOperation createRemoteOperation(final SyncRequest request, final Account account, final Context context) { final RemoteOperation operation; switch (request.getOperationType()) { case UPLOAD: final SyncedFileState sfs = request.getSyncedFileState(); - operation = new UploadFileOperation(sfs, context); + operation = new UploadFileOperation(sfs, account, context); break; case DOWNLOAD: final DownloadRequest downloadRequest = (DownloadRequest) request; diff --git a/app/src/main/java/foundation/e/drive/operations/DownloadFileOperation.java b/app/src/main/java/foundation/e/drive/operations/DownloadFileOperation.java index 1dedacd1..e16fabac 100644 --- a/app/src/main/java/foundation/e/drive/operations/DownloadFileOperation.java +++ b/app/src/main/java/foundation/e/drive/operations/DownloadFileOperation.java @@ -25,6 +25,7 @@ import foundation.e.drive.utils.CommonUtils; /** * @author Vincent Bourgmayer * Encapsulate a global download process for a file + * /!\ Doesn't require NextcloudClient yet */ public class DownloadFileOperation extends RemoteOperation { private final static String TAG = DownloadFileOperation.class.getSimpleName(); diff --git a/app/src/main/java/foundation/e/drive/operations/GetAliasOperation.java b/app/src/main/java/foundation/e/drive/operations/GetAliasOperation.java index 2173b4e8..8ec08a52 100644 --- a/app/src/main/java/foundation/e/drive/operations/GetAliasOperation.java +++ b/app/src/main/java/foundation/e/drive/operations/GetAliasOperation.java @@ -23,6 +23,7 @@ import java.util.ArrayList; /** * @author TheScarastic + * /!\ Doesn't require NextcloudClient yet */ public class GetAliasOperation extends RemoteOperation { diff --git a/app/src/main/java/foundation/e/drive/operations/ListFileRemoteOperation.java b/app/src/main/java/foundation/e/drive/operations/ListFileRemoteOperation.java index 08709c24..29dd762b 100644 --- a/app/src/main/java/foundation/e/drive/operations/ListFileRemoteOperation.java +++ b/app/src/main/java/foundation/e/drive/operations/ListFileRemoteOperation.java @@ -26,7 +26,7 @@ import foundation.e.drive.utils.CommonUtils; /** - * + * /!\ Doesn't require NextcloudClient yet * @author Vincent Bourgmayer * Created on 04/05/2018. */ diff --git a/app/src/main/java/foundation/e/drive/operations/UploadFileOperation.java b/app/src/main/java/foundation/e/drive/operations/UploadFileOperation.java index 8a48dab2..d7fc1cd5 100644 --- a/app/src/main/java/foundation/e/drive/operations/UploadFileOperation.java +++ b/app/src/main/java/foundation/e/drive/operations/UploadFileOperation.java @@ -9,11 +9,13 @@ package foundation.e.drive.operations; +import android.accounts.Account; import android.content.Context; import android.util.Log; import androidx.annotation.VisibleForTesting; +import com.nextcloud.common.NextcloudClient; import com.owncloud.android.lib.common.OwnCloudClient; import com.owncloud.android.lib.common.UserInfo; import com.owncloud.android.lib.common.operations.RemoteOperation; @@ -27,6 +29,7 @@ import java.io.File; import foundation.e.drive.database.DbHelper; import foundation.e.drive.models.SyncedFileState; import foundation.e.drive.utils.CommonUtils; +import foundation.e.drive.utils.DavClientProvider; /** * @author Vincent Bourgmayer @@ -37,17 +40,19 @@ public class UploadFileOperation extends RemoteOperation { private int restartCounter =0; private long previousLastModified; //get to restore real value if all trials fails - private Context context; - private SyncedFileState syncedState; + private final Context context; + private final SyncedFileState syncedState; + private final Account account; // /!\ this is temporary because NC library doesn't use NextcloudClient for every operation yet /** * Construct an upload operation with an already known syncedFileState * @param syncedFileState syncedFileState corresponding to file. */ - public UploadFileOperation (SyncedFileState syncedFileState, Context context) { + public UploadFileOperation (final SyncedFileState syncedFileState, final Account account, final Context context) { this.syncedState = syncedFileState; this.previousLastModified = syncedState.getLocalLastModified(); this.context = context; + this.account = account; } /** @@ -63,7 +68,6 @@ public class UploadFileOperation extends RemoteOperation { */ @Override protected RemoteOperationResult run(OwnCloudClient client ) { - //as operation isn't executed immediatly, file might have been deleted since creation of operation final File file = new File(syncedState.getLocalPath()); if (file == null || !file.exists()) { Log.w(TAG, "Can't get the file. It might have been deleted"); @@ -78,8 +82,8 @@ public class UploadFileOperation extends RemoteOperation { Log.d(TAG, "syncedState last modified: "+ syncedState.getLocalLastModified()+" <=> file last modified: "+file.lastModified() +": So return sync_conflict"); return new RemoteOperationResult(ResultCode.SYNC_CONFLICT); } - - final RemoteOperationResult checkQuotaResult = checkAvailableSpace(client, file.length()); + final NextcloudClient ncClient = DavClientProvider.getInstance().getNcClientInstance(account, context); + final RemoteOperationResult checkQuotaResult = checkAvailableSpace(ncClient, file.length()); if (checkQuotaResult.getCode() != ResultCode.OK) { Log.e(TAG, "Impossible to check quota. Upload of " + syncedState.getLocalPath() + "cancelled"); return new RemoteOperationResult(checkQuotaResult.getCode()); @@ -151,7 +155,7 @@ public class UploadFileOperation extends RemoteOperation { * @return RemoteOperationResult */ @VisibleForTesting(otherwise = VisibleForTesting.PRIVATE) - public RemoteOperationResult checkAvailableSpace(OwnCloudClient client, long fileSize) { + public RemoteOperationResult checkAvailableSpace(NextcloudClient client, long fileSize) { final GetUserInfoRemoteOperation GetUserInfoRemoteOperation = new GetUserInfoRemoteOperation(); final RemoteOperationResult ocsResult = GetUserInfoRemoteOperation.execute(client); final UserInfo userInfo = ocsResult.getResultData(); @@ -169,7 +173,7 @@ public class UploadFileOperation extends RemoteOperation { /** * Create remote parent folder of the file if missing * @param targetPath - * @param client + * @param client still OwnCloudClient at the moment, but will be Nextcloud client in the futur * @return */ public boolean createRemoteFolder(String targetPath, OwnCloudClient client) { diff --git a/app/src/main/java/foundation/e/drive/services/SynchronizationService.java b/app/src/main/java/foundation/e/drive/services/SynchronizationService.java index a5ba992b..020429da 100644 --- a/app/src/main/java/foundation/e/drive/services/SynchronizationService.java +++ b/app/src/main/java/foundation/e/drive/services/SynchronizationService.java @@ -155,7 +155,7 @@ public class SynchronizationService extends Service implements OnRemoteOperation final SyncRequest request = this.syncRequestQueue.poll(); //return null if empty if (request == null) return; - final SyncWrapper syncWrapper = new SyncWrapper(request, getApplicationContext()); + final SyncWrapper syncWrapper = new SyncWrapper(request, account, getApplicationContext()); final RemoteOperation operation = syncWrapper.getRemoteOperation(); if (operation != null) { diff --git a/app/src/main/java/foundation/e/drive/work/CreateRemoteFolderWorker.java b/app/src/main/java/foundation/e/drive/work/CreateRemoteFolderWorker.java index d6b06f72..2d995c64 100644 --- a/app/src/main/java/foundation/e/drive/work/CreateRemoteFolderWorker.java +++ b/app/src/main/java/foundation/e/drive/work/CreateRemoteFolderWorker.java @@ -19,7 +19,8 @@ import androidx.work.Data; import androidx.work.Worker; import androidx.work.WorkerParameters; -import com.nextcloud.common.NextcloudClient; +//import com.nextcloud.common.NextcloudClient; //not yet supported +import com.owncloud.android.lib.common.OwnCloudClient; import com.owncloud.android.lib.common.operations.RemoteOperationResult; import com.owncloud.android.lib.resources.files.CreateFolderRemoteOperation; @@ -32,6 +33,7 @@ import foundation.e.drive.utils.CommonUtils; import foundation.e.drive.utils.DavClientProvider; /** + * /!\ Doesn't require NextcloudClient yet * Create folder on ecloud for a given local folder * @author Vincent Bourgmayer */ @@ -75,8 +77,9 @@ public class CreateRemoteFolderWorker extends Worker { folder.mkdirs(); syncedFolder.setLastModified(folder.lastModified()); } - - final NextcloudClient client = DavClientProvider.getInstance().getNcClientInstance(account, context); + //Not yet supported + //final NextcloudClient client = DavClientProvider.getInstance().getNcClientInstance(account, context); + final OwnCloudClient client = DavClientProvider.getInstance().getClientInstance(account, context); if (client == null) { Log.e(TAG, "Can't get OwnCloudClient."); return Result.retry(); -- GitLab From 31f0f4d439bf2c81ca50a63d132ef748f400cb3b Mon Sep 17 00:00:00 2001 From: vincent Bourgmayer Date: Tue, 7 Jun 2022 12:01:28 +0200 Subject: [PATCH 40/59] try to fix UploadFileOperationTest --- .../operations/UploadFileOperationTest.java | 24 ++++++++++--------- 1 file changed, 13 insertions(+), 11 deletions(-) diff --git a/app/src/test/java/foundation/e/drive/operations/UploadFileOperationTest.java b/app/src/test/java/foundation/e/drive/operations/UploadFileOperationTest.java index f6fe43de..e68cc842 100644 --- a/app/src/test/java/foundation/e/drive/operations/UploadFileOperationTest.java +++ b/app/src/test/java/foundation/e/drive/operations/UploadFileOperationTest.java @@ -7,6 +7,7 @@ import static org.junit.Assert.assertNotNull; import static org.junit.Assert.assertTrue; import static org.junit.Assert.fail; +import android.accounts.Account; import android.accounts.AccountManager; import android.content.Context; import android.os.Build; @@ -50,6 +51,7 @@ public class UploadFileOperationTest { private final AccountManager accountManager; private final Context context; private long userFreeQuota; + private final Account account; public UploadFileOperationTest() { context = ApplicationProvider.getApplicationContext(); @@ -57,6 +59,7 @@ public class UploadFileOperationTest { ShadowLog.stream = System.out; TestUtils.loadServerCredentials(); TestUtils.prepareValidAccount(accountManager); + account = TestUtils.getValidAccount(); ocClient = CommonUtils.getOwnCloudClient(CommonUtils.getAccount(TestUtils.TEST_ACCOUNT_NAME, TestUtils.TEST_ACCOUNT_TYPE, accountManager), context); ncClient = TestUtils.getNcClient(context); try { @@ -162,9 +165,8 @@ public class UploadFileOperationTest { final SyncedFileState sfs_fromDB = DbHelper.loadSyncedFile(context, syncedFileStates.get(0).getLocalPath(), true); assertTrue("SyncedFileState loaded from DB must have an empty Etag", sfs_fromDB.getLastETAG().isEmpty()); - boolean checkEtag = false; - UploadFileOperation testOperation = new UploadFileOperation(syncedFileStates.get(0), context); + UploadFileOperation testOperation = new UploadFileOperation(syncedFileStates.get(0), account, context); final RemoteOperationResult result = testOperation.execute(ocClient); String errorMsg = "The upload failed:\n http code: "+result.getHttpCode() @@ -194,7 +196,7 @@ public class UploadFileOperationTest { SyncedFileState syncedFileState = null; //Test fails at the moment because of UploadFileOperation's constructor not checking for syncedFileState is null) // check https://gitlab.e.foundation/e/apps/eDrive/-/issues/120 - final UploadFileOperation testOperation = new UploadFileOperation(syncedFileState, context); + final UploadFileOperation testOperation = new UploadFileOperation(syncedFileState, account, context); RemoteOperationResult result = testOperation.execute(ocClient); assertEquals("Expected result code was FORBIDDEN but got: "+result.getCode().name(), RemoteOperationResult.ResultCode.FORBIDDEN, result.getCode()); @@ -214,7 +216,7 @@ public class UploadFileOperationTest { assertTrue("SyncedFileState loaded from DB must have an empty Etag", sfs_fromDB.getLastETAG().isEmpty()); boolean checkEtag = false; - UploadFileOperation testOperation = new UploadFileOperation(syncedFileStates.get(0), context); + UploadFileOperation testOperation = new UploadFileOperation(syncedFileStates.get(0), account, context); final File smallFile = new File(sfs_fromDB.getLocalPath()); assertTrue("Local file deletion return false instead of true", smallFile.delete()); @@ -232,13 +234,13 @@ public class UploadFileOperationTest { //long freeQuota = getUserRemoteFreeQuota(); assertFalse("Reading remote free quota fails"+userFreeQuota, -1 == userFreeQuota); //We don't care of parameter of UploadFileOperation's constructor - final RemoteOperationResult actualResult = new UploadFileOperation(Mockito.mock(SyncedFileState.class), context) - .checkAvailableSpace(ocClient, (userFreeQuota+1)); + final RemoteOperationResult actualResult = new UploadFileOperation(Mockito.mock(SyncedFileState.class), account, context) + .checkAvailableSpace(ncClient, (userFreeQuota+1)); assertEquals("Quota check ("+ userFreeQuota+"vs"+(userFreeQuota+1)+") failed", RemoteOperationResult.ResultCode.QUOTA_EXCEEDED, actualResult.getCode()); } /** - * Assert that uploading a file which size is exactly the amount of free quota isn't allowed + * Assert that uploading a file which size is exactlyF the amount of free quota isn't allowed */ @Test public void fileSizeEqualToFreeQuota_shouldBeAllowed() { @@ -246,8 +248,8 @@ public class UploadFileOperationTest { //long freeQuota = getUserRemoteFreeQuota(); assertFalse("Reading remote free quota fails"+userFreeQuota, -1 == userFreeQuota); - final RemoteOperationResult actualResult = new UploadFileOperation(Mockito.mock(SyncedFileState.class), context) - .checkAvailableSpace(ocClient, userFreeQuota); + final RemoteOperationResult actualResult = new UploadFileOperation(Mockito.mock(SyncedFileState.class), account, context) + .checkAvailableSpace(ncClient, userFreeQuota); assertNotEquals("Quota check is Quota Exceeded ("+ userFreeQuota+" vs "+userFreeQuota+")", RemoteOperationResult.ResultCode.QUOTA_EXCEEDED, actualResult.getCode()); @@ -266,8 +268,8 @@ public class UploadFileOperationTest { //long freeQuota = getUserRemoteFreeQuota(); assertFalse("Reading remote free quota fails "+userFreeQuota, -1 == userFreeQuota); - final RemoteOperationResult actualResult = new UploadFileOperation(Mockito.mock(SyncedFileState.class), context) - .checkAvailableSpace(ocClient, (userFreeQuota-1)); + final RemoteOperationResult actualResult = new UploadFileOperation(Mockito.mock(SyncedFileState.class), account, context) + .checkAvailableSpace(ncClient, (userFreeQuota-1)); assertEquals("Quota check ("+ userFreeQuota+" vs "+(userFreeQuota-1)+") failed", RemoteOperationResult.ResultCode.OK, actualResult.getCode()); -- GitLab From 787fc0d3859d3f08e15a65f128ad09614c18c871 Mon Sep 17 00:00:00 2001 From: vincent Bourgmayer Date: Tue, 7 Jun 2022 17:38:30 +0200 Subject: [PATCH 41/59] fixed some test in UploadFileOperationTest by using Mockito and updating FileUploadOperation --- app/build.gradle | 3 +- .../drive/operations/UploadFileOperation.java | 63 ++++++++------- .../operations/UploadFileOperationTest.java | 76 +++++++++++-------- 3 files changed, 82 insertions(+), 60 deletions(-) diff --git a/app/build.gradle b/app/build.gradle index 56825fd8..90fad662 100644 --- a/app/build.gradle +++ b/app/build.gradle @@ -86,6 +86,7 @@ dependencies { testImplementation 'junit:junit:4.13.2' testImplementation 'org.robolectric:robolectric:4.4' - testImplementation 'org.mockito:mockito-inline:3.4.0' + testImplementation 'org.mockito:mockito-core:3.4.0' + //testImplementation 'org.mockito:mockito-inline:3.4.0' testImplementation 'androidx.work:work-testing:2.7.1' } diff --git a/app/src/main/java/foundation/e/drive/operations/UploadFileOperation.java b/app/src/main/java/foundation/e/drive/operations/UploadFileOperation.java index d7fc1cd5..4cfc10b4 100644 --- a/app/src/main/java/foundation/e/drive/operations/UploadFileOperation.java +++ b/app/src/main/java/foundation/e/drive/operations/UploadFileOperation.java @@ -93,13 +93,11 @@ public class UploadFileOperation extends RemoteOperation { return new RemoteOperationResult(ResultCode.UNKNOWN_ERROR); } - final UploadFileRemoteOperation uploadOperation = buildUploadOperation(file); - // Execute UploadFileOperation - final RemoteOperationResult uploadResult = uploadOperation.execute(client); final ResultCode resultCode; boolean mustRestart = true; + final RemoteOperationResult uploadResult = uploadFile(file, client); if (uploadResult.isSuccess()) { final String etag = uploadResult.getResultData(); if (etag != null) { @@ -135,20 +133,6 @@ public class UploadFileOperation extends RemoteOperation { return new RemoteOperationResult(resultCode); } - /** - * Build the operation to put the file on server - * @return the operation to execute - */ - private UploadFileRemoteOperation buildUploadOperation(File file) { - final String timeStamp = ((Long) (file.lastModified() / 1000) ).toString() ; - - return new UploadFileRemoteOperation(syncedState.getLocalPath(), - syncedState.getRemotePath(), - CommonUtils.getMimeType(file), - (!this.syncedState.isMediaType() || syncedState.getLastETAG().isEmpty())? null : syncedState.getLastETAG(), //If not null, This can cause error 412; that means remote file has change - timeStamp ); - } - /** * Perform an operation to check available space on server before to upload * @param client OwnCloudClient @@ -156,19 +140,46 @@ public class UploadFileOperation extends RemoteOperation { */ @VisibleForTesting(otherwise = VisibleForTesting.PRIVATE) public RemoteOperationResult checkAvailableSpace(NextcloudClient client, long fileSize) { - final GetUserInfoRemoteOperation GetUserInfoRemoteOperation = new GetUserInfoRemoteOperation(); - final RemoteOperationResult ocsResult = GetUserInfoRemoteOperation.execute(client); - final UserInfo userInfo = ocsResult.getResultData(); - final RemoteOperationResult result; - if (ocsResult.isSuccess() && userInfo.getQuota().getFree() < fileSize) { - Log.w(TAG, "Not enough quota to upload the file"); - result = new RemoteOperationResult(ResultCode.QUOTA_EXCEEDED); + final RemoteOperationResult ocsResult = readUserInfo(client); + final ResultCode resultCode; + if (ocsResult.isSuccess() && ocsResult.getResultData().getQuota().getFree() < fileSize) { + resultCode = ResultCode.QUOTA_EXCEEDED; } else { - result = new RemoteOperationResult(ocsResult.getCode()); + resultCode = ocsResult.getCode(); } - return result; + return new RemoteOperationResult(resultCode); + } + + /** + * Read user info to get Quota's data + * note: this has been extracted from checkAvailableSpace for + * testing purpose + * @param client client to run the method + * @return RemoteOperationResult + */ + @VisibleForTesting(otherwise = VisibleForTesting.PRIVATE) + public RemoteOperationResult readUserInfo(NextcloudClient client) { + final GetUserInfoRemoteOperation GetUserInfoRemoteOperation = new GetUserInfoRemoteOperation(); + return GetUserInfoRemoteOperation.execute(client); } + /** + * Effectively upload the file + * note: this has been extracted from run(...) for + * testing purpose + * @param client client to run the method + * @return RemoteOperationResult + */ + @VisibleForTesting(otherwise = VisibleForTesting.PRIVATE) + public RemoteOperationResult uploadFile(final File file, final OwnCloudClient client) { + final String timeStamp = ((Long) (file.lastModified() / 1000) ).toString() ; + final UploadFileRemoteOperation uploadOperation = new UploadFileRemoteOperation(syncedState.getLocalPath(), + syncedState.getRemotePath(), + CommonUtils.getMimeType(file), + (!this.syncedState.isMediaType() || syncedState.getLastETAG().isEmpty())? null : syncedState.getLastETAG(), //If not null, This can cause error 412; that means remote file has change + timeStamp ); + return uploadOperation.execute(client); + } /** * Create remote parent folder of the file if missing diff --git a/app/src/test/java/foundation/e/drive/operations/UploadFileOperationTest.java b/app/src/test/java/foundation/e/drive/operations/UploadFileOperationTest.java index e68cc842..b39bf4de 100644 --- a/app/src/test/java/foundation/e/drive/operations/UploadFileOperationTest.java +++ b/app/src/test/java/foundation/e/drive/operations/UploadFileOperationTest.java @@ -16,9 +16,9 @@ import androidx.test.core.app.ApplicationProvider; import com.nextcloud.common.NextcloudClient; import com.owncloud.android.lib.common.OwnCloudClient; +import com.owncloud.android.lib.common.Quota; import com.owncloud.android.lib.common.UserInfo; import com.owncloud.android.lib.common.operations.RemoteOperationResult; -import com.owncloud.android.lib.resources.users.GetUserInfoRemoteOperation; import org.junit.Before; import org.junit.Ignore; @@ -51,6 +51,9 @@ public class UploadFileOperationTest { private final AccountManager accountManager; private final Context context; private long userFreeQuota; + private long userTotalQuota; + private long userUsedQuota; + private double userRelativeQuota; private final Account account; public UploadFileOperationTest() { @@ -62,12 +65,10 @@ public class UploadFileOperationTest { account = TestUtils.getValidAccount(); ocClient = CommonUtils.getOwnCloudClient(CommonUtils.getAccount(TestUtils.TEST_ACCOUNT_NAME, TestUtils.TEST_ACCOUNT_TYPE, accountManager), context); ncClient = TestUtils.getNcClient(context); - try { - TestUtils.testConnection(ncClient, context); - } catch (Exception e) { - System.out.println("test connection failed: "+e.getMessage()); - } - userFreeQuota = getUserRemoteFreeQuota(); + userFreeQuota = 50; + userTotalQuota = 100; + userUsedQuota = 50; + userRelativeQuota = 50.0; } @Before @@ -104,10 +105,11 @@ public class UploadFileOperationTest { /** * Create a local small file to use for upload test */ - private void createSmallFile() { + private File createSmallFile() { final String smallDummyFilePath = TestUtils.TEST_LOCAL_ROOT_FOLDER_PATH+"small/dummy.txt"; + File file = null; try { - TestUtils.createFile(smallDummyFilePath, 2); + file = TestUtils.createFile(smallDummyFilePath, 2); } catch (IOException | SecurityException e ) { fail(e.getMessage()); } @@ -116,6 +118,7 @@ public class UploadFileOperationTest { sfs.setId(DbHelper.manageSyncedFileStateDB(sfs, "INSERT", context)); syncedFileStates.add(sfs); + return file; } /** @@ -140,20 +143,6 @@ public class UploadFileOperationTest { } else return true; } - private long getUserRemoteFreeQuota() { - final GetUserInfoRemoteOperation GetUserInfoRemoteOperation = new GetUserInfoRemoteOperation(); - final RemoteOperationResult ocsResult = GetUserInfoRemoteOperation.execute(ncClient); - - if (ocsResult.isSuccess()) { - UserInfo userInfo = ocsResult.getResultData(); - System.out.println("User free Quotas: "+userInfo.getQuota().getFree()); - System.out.println("User Total Quotas: "+userInfo.getQuota().getTotal()); - - return userInfo.getQuota().getFree(); - } - return -1; - } - /** * test upload of a file and check that it's ok */ @@ -161,14 +150,15 @@ public class UploadFileOperationTest { public void uploadNewSmallFile_shouldwork() { //tearDown removeSmallFile(); //clean the environnement - createSmallFile(); //preparation + final File file = createSmallFile(); //preparation final SyncedFileState sfs_fromDB = DbHelper.loadSyncedFile(context, syncedFileStates.get(0).getLocalPath(), true); assertTrue("SyncedFileState loaded from DB must have an empty Etag", sfs_fromDB.getLastETAG().isEmpty()); boolean checkEtag = false; - UploadFileOperation testOperation = new UploadFileOperation(syncedFileStates.get(0), account, context); - - final RemoteOperationResult result = testOperation.execute(ocClient); + final UploadFileOperation operation = Mockito.spy(new UploadFileOperation(syncedFileStates.get(0), account, context)); + mockUserInfoReading(operation); + mockSuccessfulFileUpload(operation, file); + final RemoteOperationResult result = operation.execute(ocClient); String errorMsg = "The upload failed:\n http code: "+result.getHttpCode() +"\n, is success ?"+result.isSuccess() +"\n, log msg: "+result.getLogMessage() @@ -234,8 +224,9 @@ public class UploadFileOperationTest { //long freeQuota = getUserRemoteFreeQuota(); assertFalse("Reading remote free quota fails"+userFreeQuota, -1 == userFreeQuota); //We don't care of parameter of UploadFileOperation's constructor - final RemoteOperationResult actualResult = new UploadFileOperation(Mockito.mock(SyncedFileState.class), account, context) - .checkAvailableSpace(ncClient, (userFreeQuota+1)); + final UploadFileOperation operation = Mockito.spy(new UploadFileOperation(Mockito.mock(SyncedFileState.class), account, context)); + mockUserInfoReading(operation); + final RemoteOperationResult actualResult = operation.checkAvailableSpace(ncClient, (userFreeQuota+1)); assertEquals("Quota check ("+ userFreeQuota+"vs"+(userFreeQuota+1)+") failed", RemoteOperationResult.ResultCode.QUOTA_EXCEEDED, actualResult.getCode()); } @@ -248,8 +239,10 @@ public class UploadFileOperationTest { //long freeQuota = getUserRemoteFreeQuota(); assertFalse("Reading remote free quota fails"+userFreeQuota, -1 == userFreeQuota); - final RemoteOperationResult actualResult = new UploadFileOperation(Mockito.mock(SyncedFileState.class), account, context) - .checkAvailableSpace(ncClient, userFreeQuota); + final UploadFileOperation operation = Mockito.spy(new UploadFileOperation(Mockito.mock(SyncedFileState.class), account, context)); + mockUserInfoReading(operation); + + final RemoteOperationResult actualResult = operation.checkAvailableSpace(ncClient, userFreeQuota); assertNotEquals("Quota check is Quota Exceeded ("+ userFreeQuota+" vs "+userFreeQuota+")", RemoteOperationResult.ResultCode.QUOTA_EXCEEDED, actualResult.getCode()); @@ -268,10 +261,27 @@ public class UploadFileOperationTest { //long freeQuota = getUserRemoteFreeQuota(); assertFalse("Reading remote free quota fails "+userFreeQuota, -1 == userFreeQuota); - final RemoteOperationResult actualResult = new UploadFileOperation(Mockito.mock(SyncedFileState.class), account, context) - .checkAvailableSpace(ncClient, (userFreeQuota-1)); + final UploadFileOperation operation = Mockito.spy(new UploadFileOperation(Mockito.mock(SyncedFileState.class), account, context)); + mockUserInfoReading(operation); + final RemoteOperationResult actualResult = operation.checkAvailableSpace(ncClient, (userFreeQuota-1)); assertEquals("Quota check ("+ userFreeQuota+" vs "+(userFreeQuota-1)+") failed", RemoteOperationResult.ResultCode.OK, actualResult.getCode()); } + + + private void mockUserInfoReading(UploadFileOperation instanceUnderTest) { + final Quota quota = new Quota(userFreeQuota, userUsedQuota, userTotalQuota, userRelativeQuota, 100); + final UserInfo uInfo = new UserInfo("id", true, "toto", "toto@toto.com", "+123456789", "adress", "", "", quota, null); + final RemoteOperationResult mockResponse = new RemoteOperationResult(RemoteOperationResult.ResultCode.OK); + mockResponse.setResultData(uInfo); + Mockito.doReturn(mockResponse).when(instanceUnderTest).readUserInfo(ncClient); + } + + private void mockSuccessfulFileUpload(final UploadFileOperation instanceUnderTest, final File file) { + final String eTag = "123456789"; + final RemoteOperationResult mockResponse = new RemoteOperationResult(RemoteOperationResult.ResultCode.OK); + mockResponse.setResultData(eTag); + Mockito.doReturn(mockResponse).when(instanceUnderTest).uploadFile(file, ocClient); + } } \ No newline at end of file -- GitLab From 80f96aa4283969e285e4fb335926561f2f313618 Mon Sep 17 00:00:00 2001 From: vincent Bourgmayer Date: Wed, 8 Jun 2022 10:03:20 +0200 Subject: [PATCH 42/59] desactivate unit test failing because of mockito --- .../e/drive/operations/UploadFileOperationTest.java | 7 +++++-- 1 file changed, 5 insertions(+), 2 deletions(-) diff --git a/app/src/test/java/foundation/e/drive/operations/UploadFileOperationTest.java b/app/src/test/java/foundation/e/drive/operations/UploadFileOperationTest.java index b39bf4de..bfab8982 100644 --- a/app/src/test/java/foundation/e/drive/operations/UploadFileOperationTest.java +++ b/app/src/test/java/foundation/e/drive/operations/UploadFileOperationTest.java @@ -131,8 +131,9 @@ public class UploadFileOperationTest { if (!syncedFileStates.isEmpty()) { final SyncedFileState sfs = syncedFileStates.get(0); if (sfs != null) { - final RemoveFileOperation removeRemoteFileOp = new RemoveFileOperation(sfs); - removeRemoteFileOp.execute(ocClient); + //final RemoveFileOperation removeRemoteFileOp = new RemoveFileOperation(sfs); + //removeRemoteFileOp.execute(ocClient); + syncedFileStates.remove(sfs); } } @@ -147,6 +148,7 @@ public class UploadFileOperationTest { * test upload of a file and check that it's ok */ @Test + @Ignore("Mocking file uploading doesn't work, and I don't find why; But this isn't related to method under test") public void uploadNewSmallFile_shouldwork() { //tearDown removeSmallFile(); //clean the environnement @@ -158,6 +160,7 @@ public class UploadFileOperationTest { final UploadFileOperation operation = Mockito.spy(new UploadFileOperation(syncedFileStates.get(0), account, context)); mockUserInfoReading(operation); mockSuccessfulFileUpload(operation, file); + final RemoteOperationResult result = operation.execute(ocClient); String errorMsg = "The upload failed:\n http code: "+result.getHttpCode() +"\n, is success ?"+result.isSuccess() -- GitLab From 98ef49c7f990814f33fadbe68e9a5ffb9fec1ddb Mon Sep 17 00:00:00 2001 From: vincent Bourgmayer Date: Thu, 9 Jun 2022 10:55:30 +0200 Subject: [PATCH 43/59] Fix missing userID in OwncloudClient --- app/src/main/java/foundation/e/drive/utils/CommonUtils.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/app/src/main/java/foundation/e/drive/utils/CommonUtils.java b/app/src/main/java/foundation/e/drive/utils/CommonUtils.java index 0ec51f48..e1514f6c 100644 --- a/app/src/main/java/foundation/e/drive/utils/CommonUtils.java +++ b/app/src/main/java/foundation/e/drive/utils/CommonUtils.java @@ -195,7 +195,7 @@ public abstract class CommonUtils { serverUri = Uri.parse(AccountUtils.getBaseUrlForAccount(context, account)); oc = OwnCloudClientFactory.createOwnCloudClient(serverUri, context, true); oc.setCredentials(new OwnCloudBasicCredentials(account.name, AccountManager.get(context).getPassword(account))); - + oc.setUserId(account.name); Log.d(TAG, "user agent: " + AppConstants.USER_AGENT); if (!AppConstants.USER_AGENT.equals(OwnCloudClientManagerFactory.getUserAgent())) { OwnCloudClientManagerFactory.setUserAgent(AppConstants.USER_AGENT); -- GitLab From 0d19b682d7960ac9e2f1f586db3713c3e72a2446 Mon Sep 17 00:00:00 2001 From: vincent Bourgmayer Date: Fri, 10 Jun 2022 11:30:07 +0200 Subject: [PATCH 44/59] try to get a new NextcloudClient like Android client of Nextcloud --- .../e/drive/utils/DavClientProvider.java | 24 +++++++++++++++---- 1 file changed, 20 insertions(+), 4 deletions(-) diff --git a/app/src/main/java/foundation/e/drive/utils/DavClientProvider.java b/app/src/main/java/foundation/e/drive/utils/DavClientProvider.java index 6d5fc6e7..0ab62b9d 100644 --- a/app/src/main/java/foundation/e/drive/utils/DavClientProvider.java +++ b/app/src/main/java/foundation/e/drive/utils/DavClientProvider.java @@ -11,13 +11,21 @@ package foundation.e.drive.utils; import android.accounts.Account; import android.accounts.AccountManager; +import android.accounts.AuthenticatorException; +import android.accounts.OperationCanceledException; import android.content.Context; import android.net.Uri; import android.util.Log; import com.nextcloud.common.NextcloudClient; +import com.owncloud.android.lib.common.OwnCloudAccount; import com.owncloud.android.lib.common.OwnCloudClient; +import com.owncloud.android.lib.common.OwnCloudClientFactory; +import com.owncloud.android.lib.common.OwnCloudClientManagerFactory; import com.owncloud.android.lib.common.accounts.AccountUtils; +import com.owncloud.android.lib.resources.users.GetUserInfoRemoteOperation; + +import java.io.IOException; /** * @author Vincent Bourgmayer @@ -40,15 +48,23 @@ public class DavClientProvider { public NextcloudClient getNcClientInstance(final Account account, final Context ctx) { Log.i(TAG, "getNcClientInstance()"); if (ncClientInstance == null) { - final Uri serverUri; try { - serverUri = Uri.parse(AccountUtils.getBaseUrlForAccount(ctx, account)); + final OwnCloudAccount ocAccount = new OwnCloudAccount(account, ctx); + ncClientInstance = OwnCloudClientManagerFactory.getDefaultSingleton() + .getNextcloudClientFor(ocAccount, ctx); } catch (AccountUtils.AccountNotFoundException e) { Log.e(TAG, "Can't get server URI for account: "+account.name+"\n"+e.getMessage()); return null; + } catch (OperationCanceledException e) { + //ignore + return null; + } catch (AuthenticatorException e) { + return null; + } catch (IOException e) { + return null; } - final String password = AccountManager.get(ctx).getPassword(account); - ncClientInstance = new NextcloudClient(serverUri, account.name, password, ctx); + ncClientInstance.setUserId(account.name); + } return ncClientInstance; } -- GitLab From 9680e7dd45fad7d07fc1017b7d9a95e9afacd993 Mon Sep 17 00:00:00 2001 From: vincent Bourgmayer Date: Mon, 13 Jun 2022 10:38:59 +0200 Subject: [PATCH 45/59] update DavClientProvider to fix NextcloudClient credential usage --- .../e/drive/utils/DavClientProvider.java | 20 +++++++++---------- 1 file changed, 10 insertions(+), 10 deletions(-) diff --git a/app/src/main/java/foundation/e/drive/utils/DavClientProvider.java b/app/src/main/java/foundation/e/drive/utils/DavClientProvider.java index 0ab62b9d..bf7cbdab 100644 --- a/app/src/main/java/foundation/e/drive/utils/DavClientProvider.java +++ b/app/src/main/java/foundation/e/drive/utils/DavClientProvider.java @@ -18,6 +18,7 @@ import android.net.Uri; import android.util.Log; import com.nextcloud.common.NextcloudClient; +import com.nextcloud.common.OkHttpCredentialsUtil; import com.owncloud.android.lib.common.OwnCloudAccount; import com.owncloud.android.lib.common.OwnCloudClient; import com.owncloud.android.lib.common.OwnCloudClientFactory; @@ -49,19 +50,13 @@ public class DavClientProvider { Log.i(TAG, "getNcClientInstance()"); if (ncClientInstance == null) { try { - final OwnCloudAccount ocAccount = new OwnCloudAccount(account, ctx); - ncClientInstance = OwnCloudClientManagerFactory.getDefaultSingleton() - .getNextcloudClientFor(ocAccount, ctx); + final Uri serverUri = Uri.parse(AccountUtils.getBaseUrlForAccount(ctx, account)); + final String credentials = OkHttpCredentialsUtil.basic(account.name, getAcountPwd(account, ctx)); + + ncClientInstance = OwnCloudClientFactory.createNextcloudClient(serverUri, account.name, credentials, ctx, true); } catch (AccountUtils.AccountNotFoundException e) { Log.e(TAG, "Can't get server URI for account: "+account.name+"\n"+e.getMessage()); return null; - } catch (OperationCanceledException e) { - //ignore - return null; - } catch (AuthenticatorException e) { - return null; - } catch (IOException e) { - return null; } ncClientInstance.setUserId(account.name); @@ -69,6 +64,11 @@ public class DavClientProvider { return ncClientInstance; } + + private static String getAcountPwd(Account account, Context ctx) throws AccountUtils.AccountNotFoundException { + return AccountManager.get(ctx).getPassword(account); + } + /** Holder */ private static class DavClientHolder { -- GitLab From 1ce2eb1a46a3047c6897236fe8f887e19ab9297f Mon Sep 17 00:00:00 2001 From: vincent Bourgmayer Date: Mon, 13 Jun 2022 10:50:54 +0200 Subject: [PATCH 46/59] replace CommonUtils.getOwncloudClient() by DavClientProvider.getInstance().getClientInstance() in InitializerService & AccountsActivity --- .../java/foundation/e/drive/activity/AccountsActivity.java | 3 ++- .../java/foundation/e/drive/services/InitializerService.java | 3 ++- 2 files changed, 4 insertions(+), 2 deletions(-) diff --git a/app/src/main/java/foundation/e/drive/activity/AccountsActivity.java b/app/src/main/java/foundation/e/drive/activity/AccountsActivity.java index f40413ca..109936ae 100644 --- a/app/src/main/java/foundation/e/drive/activity/AccountsActivity.java +++ b/app/src/main/java/foundation/e/drive/activity/AccountsActivity.java @@ -36,6 +36,7 @@ import com.owncloud.android.lib.common.OwnCloudClient; import foundation.e.drive.R; import foundation.e.drive.databinding.ActivityAccountsBinding; import foundation.e.drive.utils.CommonUtils; +import foundation.e.drive.utils.DavClientProvider; import foundation.e.drive.widgets.EDriveWidget; public class AccountsActivity extends AppCompatActivity { @@ -70,7 +71,7 @@ public class AccountsActivity extends AppCompatActivity { final AccountManager accountManager = AccountManager.get(this); final Account account = CommonUtils.getAccount(getString(R.string.eelo_account_type), accountManager); - final OwnCloudClient client = CommonUtils.getOwnCloudClient(account, this); + final OwnCloudClient client = DavClientProvider.getInstance().getClientInstance(account, this); final String usedQuota = accountManager.getUserData(account, ACCOUNT_DATA_USED_QUOTA_KEY); final String totalQuota = accountManager.getUserData(account, ACCOUNT_DATA_TOTAL_QUOTA_KEY); diff --git a/app/src/main/java/foundation/e/drive/services/InitializerService.java b/app/src/main/java/foundation/e/drive/services/InitializerService.java index 922e1990..b53fc320 100644 --- a/app/src/main/java/foundation/e/drive/services/InitializerService.java +++ b/app/src/main/java/foundation/e/drive/services/InitializerService.java @@ -29,6 +29,7 @@ import java.util.List; import foundation.e.drive.models.SyncedFolder; import foundation.e.drive.utils.AppConstants; import foundation.e.drive.utils.CommonUtils; +import foundation.e.drive.utils.DavClientProvider; import static com.owncloud.android.lib.resources.files.FileUtils.PATH_SEPARATOR; import static foundation.e.drive.utils.AppConstants.MEDIA_SYNCABLE_CATEGORIES; @@ -85,7 +86,7 @@ public class InitializerService extends Service { this.account = CommonUtils.getAccount(accountName, accountType, AccountManager.get(this)); //Get OwnCloudlient if (this.account != null) { - this.cloudClient = CommonUtils.getOwnCloudClient(this.account, getApplicationContext()); + this.cloudClient = DavClientProvider.getInstance().getClientInstance(account, getApplicationContext()); start(); } else { Log.w(TAG, "Got account is invalid."); -- GitLab From 35fef892effdc2cc04b85cb77f6369a9d12a499c Mon Sep 17 00:00:00 2001 From: vincent Bourgmayer Date: Mon, 13 Jun 2022 11:27:19 +0200 Subject: [PATCH 47/59] move OwncloudClient creation from Commonutils into DavClientProvider --- .../foundation/e/drive/utils/CommonUtils.java | 33 ------------------- .../e/drive/utils/DavClientProvider.java | 23 ++++++++----- .../operations/UploadFileOperationTest.java | 5 +-- 3 files changed, 18 insertions(+), 43 deletions(-) diff --git a/app/src/main/java/foundation/e/drive/utils/CommonUtils.java b/app/src/main/java/foundation/e/drive/utils/CommonUtils.java index e1514f6c..b43451fb 100644 --- a/app/src/main/java/foundation/e/drive/utils/CommonUtils.java +++ b/app/src/main/java/foundation/e/drive/utils/CommonUtils.java @@ -30,12 +30,6 @@ import android.util.Log; import android.webkit.MimeTypeMap; import android.widget.Toast; -import com.nextcloud.common.NextcloudClient; -import com.owncloud.android.lib.common.OwnCloudBasicCredentials; -import com.owncloud.android.lib.common.OwnCloudClient; -import com.owncloud.android.lib.common.OwnCloudClientFactory; -import com.owncloud.android.lib.common.OwnCloudClientManagerFactory; -import com.owncloud.android.lib.common.accounts.AccountUtils; import com.owncloud.android.lib.resources.files.FileUtils; import java.io.File; @@ -183,33 +177,6 @@ public abstract class CommonUtils { return ContentResolver.getSyncAutomatically(account, METERED_NETWORK_ALLOWED_AUTHORITY); } - /** - * @param context app context - * @return Owncloud client instance or null - */ - public static OwnCloudClient getOwnCloudClient(Account account, Context context) { - Log.i(TAG, "getOwnCloudClient()"); - final Uri serverUri; - OwnCloudClient oc; - try { - serverUri = Uri.parse(AccountUtils.getBaseUrlForAccount(context, account)); - oc = OwnCloudClientFactory.createOwnCloudClient(serverUri, context, true); - oc.setCredentials(new OwnCloudBasicCredentials(account.name, AccountManager.get(context).getPassword(account))); - oc.setUserId(account.name); - Log.d(TAG, "user agent: " + AppConstants.USER_AGENT); - if (!AppConstants.USER_AGENT.equals(OwnCloudClientManagerFactory.getUserAgent())) { - OwnCloudClientManagerFactory.setUserAgent(AppConstants.USER_AGENT); - } - - } catch (Exception e) { - Log.e(TAG, "Can\'t parse serverPath to Uri : " + e.toString()); - oc = null; - } - return oc; - - } - - /** methods relative to file **/ /** diff --git a/app/src/main/java/foundation/e/drive/utils/DavClientProvider.java b/app/src/main/java/foundation/e/drive/utils/DavClientProvider.java index bf7cbdab..bf0679a4 100644 --- a/app/src/main/java/foundation/e/drive/utils/DavClientProvider.java +++ b/app/src/main/java/foundation/e/drive/utils/DavClientProvider.java @@ -11,22 +11,17 @@ package foundation.e.drive.utils; import android.accounts.Account; import android.accounts.AccountManager; -import android.accounts.AuthenticatorException; -import android.accounts.OperationCanceledException; import android.content.Context; import android.net.Uri; import android.util.Log; import com.nextcloud.common.NextcloudClient; import com.nextcloud.common.OkHttpCredentialsUtil; -import com.owncloud.android.lib.common.OwnCloudAccount; +import com.owncloud.android.lib.common.OwnCloudBasicCredentials; import com.owncloud.android.lib.common.OwnCloudClient; import com.owncloud.android.lib.common.OwnCloudClientFactory; import com.owncloud.android.lib.common.OwnCloudClientManagerFactory; import com.owncloud.android.lib.common.accounts.AccountUtils; -import com.owncloud.android.lib.resources.users.GetUserInfoRemoteOperation; - -import java.io.IOException; /** * @author Vincent Bourgmayer @@ -40,7 +35,20 @@ public class DavClientProvider { public OwnCloudClient getClientInstance(final Account account, final Context ctx) { if (ocClientInstance == null) { - this.ocClientInstance = CommonUtils.getOwnCloudClient(account, ctx); + try { + //Not sure below if code is used... + if (!AppConstants.USER_AGENT.equals(OwnCloudClientManagerFactory.getUserAgent())) { + OwnCloudClientManagerFactory.setUserAgent(AppConstants.USER_AGENT); + } + final Uri serverUri = Uri.parse(AccountUtils.getBaseUrlForAccount(ctx, account)); + ocClientInstance = OwnCloudClientFactory.createOwnCloudClient(serverUri, ctx, true); + ocClientInstance.setCredentials(new OwnCloudBasicCredentials(account.name, getAcountPwd(account, ctx))); + ocClientInstance.setUserId(account.name); + + } catch (AccountUtils.AccountNotFoundException e) { + Log.e(TAG, "Can\'t parse serverPath to Uri : " + e.toString()); + ocClientInstance = null; + } } return ocClientInstance; } @@ -59,7 +67,6 @@ public class DavClientProvider { return null; } ncClientInstance.setUserId(account.name); - } return ncClientInstance; } diff --git a/app/src/test/java/foundation/e/drive/operations/UploadFileOperationTest.java b/app/src/test/java/foundation/e/drive/operations/UploadFileOperationTest.java index bfab8982..2ba3e269 100644 --- a/app/src/test/java/foundation/e/drive/operations/UploadFileOperationTest.java +++ b/app/src/test/java/foundation/e/drive/operations/UploadFileOperationTest.java @@ -39,6 +39,7 @@ import foundation.e.drive.models.SyncedFileState; import foundation.e.drive.models.SyncedFolder; import foundation.e.drive.TestUtils; import foundation.e.drive.utils.CommonUtils; +import foundation.e.drive.utils.DavClientProvider; @RunWith(RobolectricTestRunner.class) @@ -63,7 +64,7 @@ public class UploadFileOperationTest { TestUtils.loadServerCredentials(); TestUtils.prepareValidAccount(accountManager); account = TestUtils.getValidAccount(); - ocClient = CommonUtils.getOwnCloudClient(CommonUtils.getAccount(TestUtils.TEST_ACCOUNT_NAME, TestUtils.TEST_ACCOUNT_TYPE, accountManager), context); + ocClient = DavClientProvider.getInstance().getClientInstance(account, context); ncClient = TestUtils.getNcClient(context); userFreeQuota = 50; userTotalQuota = 100; @@ -148,7 +149,7 @@ public class UploadFileOperationTest { * test upload of a file and check that it's ok */ @Test - @Ignore("Mocking file uploading doesn't work, and I don't find why; But this isn't related to method under test") + //@Ignore("Mocking file uploading doesn't work, and I don't find why; But this isn't related to method under test") public void uploadNewSmallFile_shouldwork() { //tearDown removeSmallFile(); //clean the environnement -- GitLab From 26b4752f94b781f2f37944122b3fc2422c0b45fd Mon Sep 17 00:00:00 2001 From: vincent Bourgmayer Date: Mon, 13 Jun 2022 18:22:59 +0200 Subject: [PATCH 48/59] try to get userID at initialization --- .../java/foundation/e/drive/utils/AppConstants.java | 2 +- .../java/foundation/e/drive/utils/CommonUtils.java | 10 +++++++++- .../foundation/e/drive/utils/DavClientProvider.java | 9 +++++++-- .../e/drive/work/AccountUserInfoWorker.java | 13 ++++++++++--- 4 files changed, 27 insertions(+), 7 deletions(-) diff --git a/app/src/main/java/foundation/e/drive/utils/AppConstants.java b/app/src/main/java/foundation/e/drive/utils/AppConstants.java index 08fae5a1..441924e3 100644 --- a/app/src/main/java/foundation/e/drive/utils/AppConstants.java +++ b/app/src/main/java/foundation/e/drive/utils/AppConstants.java @@ -39,7 +39,7 @@ public abstract class AppConstants { public static final String ACCOUNT_DATA_GROUPS = "group"; public static final String ACCOUNT_DATA_ALIAS_KEY = "alias"; public static final String ACCOUNT_DATA_EMAIL = "email"; - + public static final String ACCOUNT_USER_ID_KEY = "USERID"; public final static String FAILED_TRANSFER_PREF = "failed_transfer"; public static final String[] MEDIA_SYNCABLE_CATEGORIES = new String[]{"Images", "Movies", "Music", "Ringtones", "Documents", "Podcasts"}; public static final String[] SETTINGS_SYNCABLE_CATEGORIES = new String[]{"Rom settings"}; diff --git a/app/src/main/java/foundation/e/drive/utils/CommonUtils.java b/app/src/main/java/foundation/e/drive/utils/CommonUtils.java index b43451fb..c909dc6d 100644 --- a/app/src/main/java/foundation/e/drive/utils/CommonUtils.java +++ b/app/src/main/java/foundation/e/drive/utils/CommonUtils.java @@ -366,6 +366,13 @@ public abstract class CommonUtils { .setRequiresBatteryNotLow(true) .build(); + final OneTimeWorkRequest getUserInfoRequest = new OneTimeWorkRequest.Builder(AccountUserInfoWorker.class) + .setBackoffCriteria(BackoffPolicy.LINEAR, 2, TimeUnit.MINUTES) + .addTag(AppConstants.WORK_GENERIC_TAG) + .addTag(AppConstants.WORK_INITIALIZATION_TAG) + .setConstraints(constraints) + .build(); + final List workRequests = new ArrayList<>(); for (SyncedFolder folder : syncedFolders) { @@ -387,7 +394,8 @@ public abstract class CommonUtils { .addTag(AppConstants.WORK_INITIALIZATION_TAG) .build(); - workManager.beginWith(workRequests) + workManager.beginWith(getUserInfoRequest) + .then(workRequests) .then(firstStartRequest) .enqueue(); } diff --git a/app/src/main/java/foundation/e/drive/utils/DavClientProvider.java b/app/src/main/java/foundation/e/drive/utils/DavClientProvider.java index bf0679a4..fa308593 100644 --- a/app/src/main/java/foundation/e/drive/utils/DavClientProvider.java +++ b/app/src/main/java/foundation/e/drive/utils/DavClientProvider.java @@ -41,9 +41,14 @@ public class DavClientProvider { OwnCloudClientManagerFactory.setUserAgent(AppConstants.USER_AGENT); } final Uri serverUri = Uri.parse(AccountUtils.getBaseUrlForAccount(ctx, account)); + ocClientInstance = OwnCloudClientFactory.createOwnCloudClient(serverUri, ctx, true); - ocClientInstance.setCredentials(new OwnCloudBasicCredentials(account.name, getAcountPwd(account, ctx))); - ocClientInstance.setUserId(account.name); + final String pwd = getAcountPwd(account, ctx); + Log.d(TAG, "pwd: "+pwd+"\nname: "+account.name+"\n"+AccountManager.get(ctx).getUserData(account, AccountUtils.Constants.KEY_USER_ID)); + + ocClientInstance.setCredentials(new OwnCloudBasicCredentials(account.name, pwd)); + final String userId = AccountManager.get(ctx).getUserData(account, AppConstants.ACCOUNT_USER_ID_KEY); + ocClientInstance.setUserId(userId); } catch (AccountUtils.AccountNotFoundException e) { Log.e(TAG, "Can\'t parse serverPath to Uri : " + e.toString()); diff --git a/app/src/main/java/foundation/e/drive/work/AccountUserInfoWorker.java b/app/src/main/java/foundation/e/drive/work/AccountUserInfoWorker.java index 02d7ad04..52651cb6 100644 --- a/app/src/main/java/foundation/e/drive/work/AccountUserInfoWorker.java +++ b/app/src/main/java/foundation/e/drive/work/AccountUserInfoWorker.java @@ -14,6 +14,7 @@ import static foundation.e.drive.utils.AppConstants.ACCOUNT_DATA_NAME; import static foundation.e.drive.utils.AppConstants.ACCOUNT_DATA_RELATIVE_QUOTA_KEY; import static foundation.e.drive.utils.AppConstants.ACCOUNT_DATA_TOTAL_QUOTA_KEY; import static foundation.e.drive.utils.AppConstants.ACCOUNT_DATA_USED_QUOTA_KEY; +import static foundation.e.drive.utils.AppConstants.ACCOUNT_USER_ID_KEY; import android.accounts.Account; import android.accounts.AccountManager; @@ -31,6 +32,7 @@ import androidx.work.WorkerParameters; import com.bumptech.glide.Glide; import com.bumptech.glide.load.engine.DiskCacheStrategy; import com.nextcloud.common.NextcloudClient; +import com.owncloud.android.lib.common.OwnCloudClient; import com.owncloud.android.lib.common.Quota; import com.owncloud.android.lib.common.UserInfo; import com.owncloud.android.lib.common.operations.RemoteOperationResult; @@ -70,8 +72,9 @@ public class AccountUserInfoWorker extends Worker { account = CommonUtils.getAccount(mContext.getString(R.string.eelo_account_type), accountManager); final NextcloudClient client = DavClientProvider.getInstance().getNcClientInstance(account, mContext); + final OwnCloudClient ocClient = DavClientProvider.getInstance().getClientInstance(account, mContext); if (client != null) { - if (fetchUserInfo(client) && fetchAliases(client)) { + if (fetchUserInfo(client) && fetchAliases(ocClient)) { Glide.with(mContext) .load(client.getBaseUri() + AccountsActivity.NON_OFFICIAL_AVATAR_PATH + client.getUserId() + "/" + 300) @@ -91,6 +94,11 @@ public class AccountUserInfoWorker extends Worker { if (ocsResult.isSuccess()) { final UserInfo userInfo = ocsResult.getResultData(); + + if (accountManager.getUserData(account, ACCOUNT_USER_ID_KEY) == null) { + final String userId = userInfo.getId(); + AccountManager.get(mContext).setUserData(account, ACCOUNT_USER_ID_KEY, userId); + } final Quota userQuota = userInfo.getQuota(); final double relativeQuota = userQuota.getRelative(); long totalQuota = userQuota.getTotal(); @@ -147,7 +155,6 @@ public class AccountUserInfoWorker extends Worker { .apply(); return true; } - return false; } @@ -170,7 +177,7 @@ public class AccountUserInfoWorker extends Worker { manager.notify(0, builder.build()); } - private boolean fetchAliases(final NextcloudClient client) { + private boolean fetchAliases(final OwnCloudClient client) { final RemoteOperationResult> ocsResult = getAliasOperation.execute(client); if (!ocsResult.isSuccess()) { return false; -- GitLab From 27c150c4fc4fc9a0d2399f06615ee86c670b10b4 Mon Sep 17 00:00:00 2001 From: vincent Bourgmayer Date: Tue, 14 Jun 2022 10:02:28 +0200 Subject: [PATCH 49/59] Get userID when adding the account --- .../e/drive/operations/GetAliasOperation.java | 31 +++++++++++++++---- .../e/drive/utils/DavClientProvider.java | 13 +++++--- .../e/drive/work/AccountUserInfoWorker.java | 13 +++++--- 3 files changed, 42 insertions(+), 15 deletions(-) diff --git a/app/src/main/java/foundation/e/drive/operations/GetAliasOperation.java b/app/src/main/java/foundation/e/drive/operations/GetAliasOperation.java index 8ec08a52..52a21245 100644 --- a/app/src/main/java/foundation/e/drive/operations/GetAliasOperation.java +++ b/app/src/main/java/foundation/e/drive/operations/GetAliasOperation.java @@ -17,6 +17,7 @@ import org.apache.commons.httpclient.HttpStatus; import org.apache.commons.httpclient.NameValuePair; import org.apache.commons.httpclient.methods.GetMethod; import org.json.JSONArray; +import org.json.JSONException; import org.json.JSONObject; import java.util.ArrayList; @@ -54,15 +55,16 @@ public class GetAliasOperation extends RemoteOperation { if (isSuccess(client.executeMethod(get))) { final String response = get.getResponseBodyAsString(); // parse - final JSONArray aliases = new JSONObject(response).getJSONObject(NODE_OCS) - .getJSONObject(NODE_DATA).getJSONArray(NODE_ALIASES); - final ArrayList resultAliases = new ArrayList<>(); - for (int i = 0; i < aliases.length(); i++) { - resultAliases.add(aliases.get(i).toString()); - } + final JSONArray aliases = parseResponse(response); + final ArrayList resultAliases = new ArrayList<>(); result = new RemoteOperationResult<>(true, get); + if (aliases != null) { + for (int i = 0; i < aliases.length(); i++) { + resultAliases.add(aliases.get(i).toString()); + } + } result.setResultData(resultAliases); } else { result = new RemoteOperationResult<>(false, get); @@ -79,4 +81,21 @@ public class GetAliasOperation extends RemoteOperation { return result; } + + + public JSONArray parseResponse(String response) { + JSONArray result = null; + try { + final JSONObject jsonResponse= new JSONObject(response).optJSONObject(NODE_OCS); + if (jsonResponse == null) return result; + + final JSONObject jsonData = jsonResponse.optJSONObject(NODE_DATA); + if (jsonData == null) return result; + + result = jsonData.optJSONArray(NODE_ALIASES); + } catch (JSONException e) { + e.printStackTrace(); + } + return result; + } } diff --git a/app/src/main/java/foundation/e/drive/utils/DavClientProvider.java b/app/src/main/java/foundation/e/drive/utils/DavClientProvider.java index fa308593..6d48cc40 100644 --- a/app/src/main/java/foundation/e/drive/utils/DavClientProvider.java +++ b/app/src/main/java/foundation/e/drive/utils/DavClientProvider.java @@ -47,14 +47,17 @@ public class DavClientProvider { Log.d(TAG, "pwd: "+pwd+"\nname: "+account.name+"\n"+AccountManager.get(ctx).getUserData(account, AccountUtils.Constants.KEY_USER_ID)); ocClientInstance.setCredentials(new OwnCloudBasicCredentials(account.name, pwd)); - final String userId = AccountManager.get(ctx).getUserData(account, AppConstants.ACCOUNT_USER_ID_KEY); - ocClientInstance.setUserId(userId); - } catch (AccountUtils.AccountNotFoundException e) { - Log.e(TAG, "Can\'t parse serverPath to Uri : " + e.toString()); - ocClientInstance = null; + Log.e(TAG, "Can't parse serverPath to Uri : " + e.toString()); + return null; } } + + if (ocClientInstance.getUserId() == null ) { + final String userId = AccountManager.get(ctx).getUserData(account, AppConstants.ACCOUNT_USER_ID_KEY); + ocClientInstance.setUserId(userId); + } + return ocClientInstance; } diff --git a/app/src/main/java/foundation/e/drive/work/AccountUserInfoWorker.java b/app/src/main/java/foundation/e/drive/work/AccountUserInfoWorker.java index 52651cb6..b136f320 100644 --- a/app/src/main/java/foundation/e/drive/work/AccountUserInfoWorker.java +++ b/app/src/main/java/foundation/e/drive/work/AccountUserInfoWorker.java @@ -23,6 +23,7 @@ import android.appwidget.AppWidgetManager; import android.content.Context; import android.content.Intent; import android.content.SharedPreferences; +import android.util.Log; import androidx.annotation.NonNull; import androidx.core.app.NotificationCompat; @@ -53,6 +54,7 @@ import foundation.e.drive.widgets.EDriveWidget; * @author TheScarastic */ public class AccountUserInfoWorker extends Worker { + private static final String TAG = AccountUserInfoWorker.class.getSimpleName(); public static final String UNIQUE_WORK_NAME = "AccountUserInfoWorker"; private final AccountManager accountManager; private final GetUserInfoRemoteOperation GetUserInfoRemoteOperation = new GetUserInfoRemoteOperation(); @@ -72,9 +74,8 @@ public class AccountUserInfoWorker extends Worker { account = CommonUtils.getAccount(mContext.getString(R.string.eelo_account_type), accountManager); final NextcloudClient client = DavClientProvider.getInstance().getNcClientInstance(account, mContext); - final OwnCloudClient ocClient = DavClientProvider.getInstance().getClientInstance(account, mContext); if (client != null) { - if (fetchUserInfo(client) && fetchAliases(ocClient)) { + if (fetchUserInfo(client) && fetchAliases()) { Glide.with(mContext) .load(client.getBaseUri() + AccountsActivity.NON_OFFICIAL_AVATAR_PATH + client.getUserId() + "/" + 300) @@ -90,6 +91,7 @@ public class AccountUserInfoWorker extends Worker { } private boolean fetchUserInfo(final NextcloudClient client) { + Log.d(TAG, "fetchUserInfo"); final RemoteOperationResult ocsResult = GetUserInfoRemoteOperation.execute(client); if (ocsResult.isSuccess()) { @@ -97,7 +99,9 @@ public class AccountUserInfoWorker extends Worker { if (accountManager.getUserData(account, ACCOUNT_USER_ID_KEY) == null) { final String userId = userInfo.getId(); + client.setUserId(userId); AccountManager.get(mContext).setUserData(account, ACCOUNT_USER_ID_KEY, userId); + Log.v(TAG, "userId "+userId+" saved for account"); } final Quota userQuota = userInfo.getQuota(); final double relativeQuota = userQuota.getRelative(); @@ -177,8 +181,9 @@ public class AccountUserInfoWorker extends Worker { manager.notify(0, builder.build()); } - private boolean fetchAliases(final OwnCloudClient client) { - final RemoteOperationResult> ocsResult = getAliasOperation.execute(client); + private boolean fetchAliases() { + final OwnCloudClient ocClient = DavClientProvider.getInstance().getClientInstance(account, mContext); + final RemoteOperationResult> ocsResult = getAliasOperation.execute(ocClient); if (!ocsResult.isSuccess()) { return false; } -- GitLab From 41626dbb00c322726b4f64202ff77351fab153f9 Mon Sep 17 00:00:00 2001 From: vincent Bourgmayer Date: Wed, 15 Jun 2022 15:34:22 +0200 Subject: [PATCH 50/59] fix files not listed in ObserverService: add entries in AndroidManifest --- app/src/main/AndroidManifest.xml | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/app/src/main/AndroidManifest.xml b/app/src/main/AndroidManifest.xml index dd1a2379..1553ffe8 100644 --- a/app/src/main/AndroidManifest.xml +++ b/app/src/main/AndroidManifest.xml @@ -6,6 +6,7 @@ + @@ -28,7 +29,8 @@ android:icon="@mipmap/ic_eelo" android:label="@string/app_name" android:persistent="true" - android:roundIcon="@mipmap/ic_eelo_round"> + android:roundIcon="@mipmap/ic_eelo_round" + android:requestLegacyExternalStorage="true"> Date: Thu, 16 Jun 2022 10:32:25 +0200 Subject: [PATCH 51/59] mend --- app/src/main/AndroidManifest.xml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/app/src/main/AndroidManifest.xml b/app/src/main/AndroidManifest.xml index 1553ffe8..4b674380 100644 --- a/app/src/main/AndroidManifest.xml +++ b/app/src/main/AndroidManifest.xml @@ -30,7 +30,7 @@ android:label="@string/app_name" android:persistent="true" android:roundIcon="@mipmap/ic_eelo_round" - android:requestLegacyExternalStorage="true"> + android:requestLegacyExternalStorage="true"> Date: Wed, 6 Jul 2022 17:05:32 +0200 Subject: [PATCH 52/59] Fix issues in file downloading: - create local path when parent directories doesn't exist. Without it moving temporary file to correct place fails. - Change temporary folder path to be more coherent and use good path to move temporary file to correct place. --- .../operations/DownloadFileOperation.java | 33 +++++++++++-------- 1 file changed, 19 insertions(+), 14 deletions(-) diff --git a/app/src/main/java/foundation/e/drive/operations/DownloadFileOperation.java b/app/src/main/java/foundation/e/drive/operations/DownloadFileOperation.java index e16fabac..cc4115ee 100644 --- a/app/src/main/java/foundation/e/drive/operations/DownloadFileOperation.java +++ b/app/src/main/java/foundation/e/drive/operations/DownloadFileOperation.java @@ -52,7 +52,7 @@ public class DownloadFileOperation extends RemoteOperation { @Override protected RemoteOperationResult run(OwnCloudClient ownCloudClient) { - Log.i(TAG, "run(ownCloudClient)"); + Log.i(TAG, "run(client) for "+remoteFile.getRemotePath()); if (syncedFileState.getId() == -1) { this.syncedFileState.setId(DbHelper.manageSyncedFileStateDB(this.syncedFileState, "INSERT", context)); } @@ -63,37 +63,42 @@ public class DownloadFileOperation extends RemoteOperation { Log.w(TAG, "File already up-to-date"); return new RemoteOperationResult(RemoteOperationResult.ResultCode.ETAG_UNCHANGED); } - - final String tmpTargetPath = context.getExternalCacheDir()+ FileUtils.PATH_SEPARATOR+ syncedFileState.getName(); + + final String tmpTargetFolderPath = context.getExternalCacheDir().getAbsolutePath(); final DownloadFileRemoteOperation downloadOperation = new DownloadFileRemoteOperation(remoteFile.getRemotePath(), - tmpTargetPath); + tmpTargetFolderPath); final RemoteOperationResult downloadResult = downloadOperation.execute(ownCloudClient); RemoteOperationResult.ResultCode resultCode; boolean mustRestart = true; + if (downloadResult.isSuccess()) { - final File tmpLocalFile = new File(tmpTargetPath); - if (!tmpLocalFile.exists()) { + final String tmpFilePath = tmpTargetFolderPath+remoteFile.getRemotePath(); + final File tmpFile = new File(tmpFilePath); + + if (!tmpFile.exists()) { Log.e(TAG, "Downloaded file doesn't exist or is null"); resultCode = RemoteOperationResult.ResultCode.FILE_NOT_FOUND; - } else if (tmpLocalFile.length() != remoteFile.getLength()) { - - Log.e(TAG, "Local and remote file doesn't have the same size."); + } else if (tmpFile.length() != remoteFile.getLength()) { + Log.e(TAG, "Local and remote file doesn't have the same size"); resultCode = RemoteOperationResult.ResultCode.INVALID_OVERWRITE; - tmpLocalFile.delete(); - + tmpFile.delete(); } else { //file has been correctly download. + final File localFile = new File(targetPath); - if (localFile.exists()) { + if (!localFile.getParentFile().exists()) { + localFile.getParentFile().mkdirs(); + } else if (localFile.exists()) { localFile.delete(); } - if (!tmpLocalFile.renameTo(localFile)) { - Log.d(TAG, "Can't move " + tmpTargetPath + " to " + targetPath); + if (!tmpFile.renameTo(localFile)) { + Log.d(TAG, "failed to move " + tmpFile.getAbsolutePath() + " to " + targetPath); return new RemoteOperationResult(RemoteOperationResult.ResultCode.FORBIDDEN); } + Log.d(TAG, "File moved to: "+localFile.getAbsolutePath()); syncedFileState.setLocalLastModified(localFile.lastModified()) .setLastETAG(remoteFile.getEtag()); -- GitLab From 8c583ce5549c7d609d91c65483d0d3fc27ba678b Mon Sep 17 00:00:00 2001 From: vincent Bourgmayer Date: Mon, 11 Jul 2022 09:51:48 +0200 Subject: [PATCH 53/59] Remove printing of password in logcat --- .../main/java/foundation/e/drive/utils/DavClientProvider.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/app/src/main/java/foundation/e/drive/utils/DavClientProvider.java b/app/src/main/java/foundation/e/drive/utils/DavClientProvider.java index 6d48cc40..4ca2ce5d 100644 --- a/app/src/main/java/foundation/e/drive/utils/DavClientProvider.java +++ b/app/src/main/java/foundation/e/drive/utils/DavClientProvider.java @@ -44,7 +44,7 @@ public class DavClientProvider { ocClientInstance = OwnCloudClientFactory.createOwnCloudClient(serverUri, ctx, true); final String pwd = getAcountPwd(account, ctx); - Log.d(TAG, "pwd: "+pwd+"\nname: "+account.name+"\n"+AccountManager.get(ctx).getUserData(account, AccountUtils.Constants.KEY_USER_ID)); + Log.d(TAG, "name: "+account.name+"\n"+AccountManager.get(ctx).getUserData(account, AccountUtils.Constants.KEY_USER_ID)); ocClientInstance.setCredentials(new OwnCloudBasicCredentials(account.name, pwd)); } catch (AccountUtils.AccountNotFoundException e) { -- GitLab From 5fc1c6e4cc77f4ac1e3e521b8398beb1c5e96e15 Mon Sep 17 00:00:00 2001 From: vincent Bourgmayer Date: Mon, 11 Jul 2022 14:58:45 +0200 Subject: [PATCH 54/59] Fix blockin situation : apply Abhishek suggestion for fetchAliases() in AccountUserInfoWorker --- .../e/drive/work/AccountUserInfoWorker.java | 21 +++++++++---------- 1 file changed, 10 insertions(+), 11 deletions(-) diff --git a/app/src/main/java/foundation/e/drive/work/AccountUserInfoWorker.java b/app/src/main/java/foundation/e/drive/work/AccountUserInfoWorker.java index b136f320..4db98892 100644 --- a/app/src/main/java/foundation/e/drive/work/AccountUserInfoWorker.java +++ b/app/src/main/java/foundation/e/drive/work/AccountUserInfoWorker.java @@ -91,7 +91,6 @@ public class AccountUserInfoWorker extends Worker { } private boolean fetchUserInfo(final NextcloudClient client) { - Log.d(TAG, "fetchUserInfo"); final RemoteOperationResult ocsResult = GetUserInfoRemoteOperation.execute(client); if (ocsResult.isSuccess()) { @@ -101,7 +100,7 @@ public class AccountUserInfoWorker extends Worker { final String userId = userInfo.getId(); client.setUserId(userId); AccountManager.get(mContext).setUserData(account, ACCOUNT_USER_ID_KEY, userId); - Log.v(TAG, "userId "+userId+" saved for account"); + Log.v(TAG, "UserId "+userId+" saved for account"); } final Quota userQuota = userInfo.getQuota(); final double relativeQuota = userQuota.getRelative(); @@ -118,8 +117,10 @@ public class AccountUserInfoWorker extends Worker { accountManager.setUserData(account, ACCOUNT_DATA_USED_QUOTA_KEY, "" + userQuota.getUsed()); addNotifAboutQuota(relativeQuota); + Log.d(TAG+"fetchUserInfo()", "Success"); return true; } + Log.d(TAG+"fetchUserInfo()", "Failure"); return false; } @@ -184,18 +185,16 @@ public class AccountUserInfoWorker extends Worker { private boolean fetchAliases() { final OwnCloudClient ocClient = DavClientProvider.getInstance().getClientInstance(account, mContext); final RemoteOperationResult> ocsResult = getAliasOperation.execute(ocClient); - if (!ocsResult.isSuccess()) { - return false; - } + String aliases = ""; - final ArrayList aliasList = ocsResult.getResultData(); - final String aliases; - if (!aliasList.isEmpty()) { - aliases = String.join(",", aliasList); - } else { - aliases = ""; + if (ocsResult.isSuccess()) { + final ArrayList aliasList = ocsResult.getResultData(); + if (!aliasList.isEmpty()) { + aliases = String.join(",", aliasList); + } } accountManager.setUserData(account, ACCOUNT_DATA_ALIAS_KEY, aliases); + Log.d(TAG+"fetchAliases()", "Success"); return true; } -- GitLab From 4763dee700460fdeb7fced35b47e6fb35a22598b Mon Sep 17 00:00:00 2001 From: vincent Bourgmayer Date: Tue, 12 Jul 2022 13:26:35 +0200 Subject: [PATCH 55/59] - generate ChunkedfileUploadOperation in SyncWrapper.java - Change version number to 1.2.0 --- app/build.gradle | 2 +- .../e/drive/models/SyncWrapper.java | 21 ++++++++++++++++++- 2 files changed, 21 insertions(+), 2 deletions(-) diff --git a/app/build.gradle b/app/build.gradle index 90fad662..163b8930 100644 --- a/app/build.gradle +++ b/app/build.gradle @@ -4,7 +4,7 @@ plugins { def versionMajor = 1 -def versionMinor = 0 +def versionMinor = 2 def versionPatch = 0 diff --git a/app/src/main/java/foundation/e/drive/models/SyncWrapper.java b/app/src/main/java/foundation/e/drive/models/SyncWrapper.java index f6bc9313..36d14d34 100644 --- a/app/src/main/java/foundation/e/drive/models/SyncWrapper.java +++ b/app/src/main/java/foundation/e/drive/models/SyncWrapper.java @@ -9,11 +9,16 @@ package foundation.e.drive.models; import android.accounts.Account; import android.content.Context; +import android.util.Log; import com.owncloud.android.lib.common.operations.RemoteOperation; +import com.owncloud.android.lib.resources.files.ChunkedFileUploadRemoteOperation; + +import java.io.File; import foundation.e.drive.operations.DownloadFileOperation; import foundation.e.drive.operations.UploadFileOperation; +import foundation.e.drive.utils.CommonUtils; /** * This class encapsulates data, for SynchronizationService, about a thread which run a RemoteOperation @@ -50,6 +55,7 @@ public class SyncWrapper { /** * Create the RemoteOperation (to perform file transfer) based on SyncRequest + * @todo: move this method outside of a model object... * @param request syncRequest for the file * @param context App context to be passed to RemoteOperation's contructor * @return RemoteOperation for Upload/Download request or null @@ -59,7 +65,20 @@ public class SyncWrapper { switch (request.getOperationType()) { case UPLOAD: final SyncedFileState sfs = request.getSyncedFileState(); - operation = new UploadFileOperation(sfs, account, context); + final File file = new File(sfs.getLocalPath()); + final int fileSizeFloorforChunked = 3072000; //3MB + if (!file.exists()) { + operation = null; + Log.w(SyncWrapper.class.getSimpleName(), "createRemoteOperation: local file doesn't exist for upload"); + } else { + if (file.length() >= fileSizeFloorforChunked) { + Log.w(SyncWrapper.class.getSimpleName(), "createRemoteOperation: create a chunkedFileUploadRemoteOperation"); + final String mimeType = CommonUtils.getMimeType(file); + operation = new ChunkedFileUploadRemoteOperation(sfs.getLocalPath(), sfs.getRemotePath(), mimeType, sfs.getLastETAG(), sfs.getLocalLastModified()+"", false); + } else { + operation = new UploadFileOperation(sfs, account, context); + } + } break; case DOWNLOAD: final DownloadRequest downloadRequest = (DownloadRequest) request; -- GitLab From 4f78cf8008417440140315c379b76412a713c859 Mon Sep 17 00:00:00 2001 From: vincent Bourgmayer Date: Wed, 13 Jul 2022 11:31:25 +0200 Subject: [PATCH 56/59] update version number to v1.1.0 --- app/build.gradle | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/app/build.gradle b/app/build.gradle index 90fad662..52403e27 100644 --- a/app/build.gradle +++ b/app/build.gradle @@ -4,7 +4,7 @@ plugins { def versionMajor = 1 -def versionMinor = 0 +def versionMinor = 1 def versionPatch = 0 -- GitLab From 94f1f7a9772d1ff2d76b06ce3b6e074aac2f2ef5 Mon Sep 17 00:00:00 2001 From: vincent Bourgmayer Date: Tue, 12 Jul 2022 13:26:35 +0200 Subject: [PATCH 57/59] - generate ChunkedfileUploadOperation in SyncWrapper.java - Change version number to 1.2.0 --- app/build.gradle | 2 +- .../e/drive/models/SyncWrapper.java | 21 ++++++++++++++++++- 2 files changed, 21 insertions(+), 2 deletions(-) diff --git a/app/build.gradle b/app/build.gradle index bbcd73a2..b7c9e8a5 100644 --- a/app/build.gradle +++ b/app/build.gradle @@ -4,7 +4,7 @@ plugins { def versionMajor = 1 -def versionMinor = 1 +def versionMinor = 2 def versionPatch = 0 diff --git a/app/src/main/java/foundation/e/drive/models/SyncWrapper.java b/app/src/main/java/foundation/e/drive/models/SyncWrapper.java index f6bc9313..36d14d34 100644 --- a/app/src/main/java/foundation/e/drive/models/SyncWrapper.java +++ b/app/src/main/java/foundation/e/drive/models/SyncWrapper.java @@ -9,11 +9,16 @@ package foundation.e.drive.models; import android.accounts.Account; import android.content.Context; +import android.util.Log; import com.owncloud.android.lib.common.operations.RemoteOperation; +import com.owncloud.android.lib.resources.files.ChunkedFileUploadRemoteOperation; + +import java.io.File; import foundation.e.drive.operations.DownloadFileOperation; import foundation.e.drive.operations.UploadFileOperation; +import foundation.e.drive.utils.CommonUtils; /** * This class encapsulates data, for SynchronizationService, about a thread which run a RemoteOperation @@ -50,6 +55,7 @@ public class SyncWrapper { /** * Create the RemoteOperation (to perform file transfer) based on SyncRequest + * @todo: move this method outside of a model object... * @param request syncRequest for the file * @param context App context to be passed to RemoteOperation's contructor * @return RemoteOperation for Upload/Download request or null @@ -59,7 +65,20 @@ public class SyncWrapper { switch (request.getOperationType()) { case UPLOAD: final SyncedFileState sfs = request.getSyncedFileState(); - operation = new UploadFileOperation(sfs, account, context); + final File file = new File(sfs.getLocalPath()); + final int fileSizeFloorforChunked = 3072000; //3MB + if (!file.exists()) { + operation = null; + Log.w(SyncWrapper.class.getSimpleName(), "createRemoteOperation: local file doesn't exist for upload"); + } else { + if (file.length() >= fileSizeFloorforChunked) { + Log.w(SyncWrapper.class.getSimpleName(), "createRemoteOperation: create a chunkedFileUploadRemoteOperation"); + final String mimeType = CommonUtils.getMimeType(file); + operation = new ChunkedFileUploadRemoteOperation(sfs.getLocalPath(), sfs.getRemotePath(), mimeType, sfs.getLastETAG(), sfs.getLocalLastModified()+"", false); + } else { + operation = new UploadFileOperation(sfs, account, context); + } + } break; case DOWNLOAD: final DownloadRequest downloadRequest = (DownloadRequest) request; -- GitLab From af880d5ce3487ae1fbc1b82341aefcc481f5c9e6 Mon Sep 17 00:00:00 2001 From: vincent Bourgmayer Date: Fri, 15 Jul 2022 10:22:20 +0200 Subject: [PATCH 58/59] move chunkedUpload stuff from SyncWrapper to UploadFileOperation --- .../e/drive/models/SyncWrapper.java | 9 +--- .../drive/operations/UploadFileOperation.java | 53 +++++++++++++++++-- 2 files changed, 51 insertions(+), 11 deletions(-) diff --git a/app/src/main/java/foundation/e/drive/models/SyncWrapper.java b/app/src/main/java/foundation/e/drive/models/SyncWrapper.java index 36d14d34..9e3becdf 100644 --- a/app/src/main/java/foundation/e/drive/models/SyncWrapper.java +++ b/app/src/main/java/foundation/e/drive/models/SyncWrapper.java @@ -66,18 +66,11 @@ public class SyncWrapper { case UPLOAD: final SyncedFileState sfs = request.getSyncedFileState(); final File file = new File(sfs.getLocalPath()); - final int fileSizeFloorforChunked = 3072000; //3MB if (!file.exists()) { operation = null; Log.w(SyncWrapper.class.getSimpleName(), "createRemoteOperation: local file doesn't exist for upload"); } else { - if (file.length() >= fileSizeFloorforChunked) { - Log.w(SyncWrapper.class.getSimpleName(), "createRemoteOperation: create a chunkedFileUploadRemoteOperation"); - final String mimeType = CommonUtils.getMimeType(file); - operation = new ChunkedFileUploadRemoteOperation(sfs.getLocalPath(), sfs.getRemotePath(), mimeType, sfs.getLastETAG(), sfs.getLocalLastModified()+"", false); - } else { - operation = new UploadFileOperation(sfs, account, context); - } + operation = new UploadFileOperation(sfs, account, context); } break; case DOWNLOAD: diff --git a/app/src/main/java/foundation/e/drive/operations/UploadFileOperation.java b/app/src/main/java/foundation/e/drive/operations/UploadFileOperation.java index 4cfc10b4..51c32c16 100644 --- a/app/src/main/java/foundation/e/drive/operations/UploadFileOperation.java +++ b/app/src/main/java/foundation/e/drive/operations/UploadFileOperation.java @@ -20,12 +20,17 @@ import com.owncloud.android.lib.common.OwnCloudClient; import com.owncloud.android.lib.common.UserInfo; import com.owncloud.android.lib.common.operations.RemoteOperation; import com.owncloud.android.lib.common.operations.RemoteOperationResult; +import com.owncloud.android.lib.resources.files.ChunkedFileUploadRemoteOperation; import com.owncloud.android.lib.resources.files.CreateFolderRemoteOperation; import com.owncloud.android.lib.resources.files.FileUtils; +import com.owncloud.android.lib.resources.files.ReadFileRemoteOperation; import com.owncloud.android.lib.resources.files.UploadFileRemoteOperation; +import com.owncloud.android.lib.resources.files.model.RemoteFile; import com.owncloud.android.lib.resources.users.GetUserInfoRemoteOperation; import com.owncloud.android.lib.common.operations.RemoteOperationResult.ResultCode; import java.io.File; +import java.util.ArrayList; + import foundation.e.drive.database.DbHelper; import foundation.e.drive.models.SyncedFileState; import foundation.e.drive.utils.CommonUtils; @@ -74,14 +79,13 @@ public class UploadFileOperation extends RemoteOperation { return new RemoteOperationResult(ResultCode.FORBIDDEN); } - final String targetPath = syncedState.getRemotePath(); - //If file already up-to-date & synced if (syncedState.isLastEtagStored() && syncedState.getLocalLastModified() == file.lastModified()) { Log.d(TAG, "syncedState last modified: "+ syncedState.getLocalLastModified()+" <=> file last modified: "+file.lastModified() +": So return sync_conflict"); return new RemoteOperationResult(ResultCode.SYNC_CONFLICT); } + final NextcloudClient ncClient = DavClientProvider.getInstance().getNcClientInstance(account, context); final RemoteOperationResult checkQuotaResult = checkAvailableSpace(ncClient, file.length()); if (checkQuotaResult.getCode() != ResultCode.OK) { @@ -89,6 +93,7 @@ public class UploadFileOperation extends RemoteOperation { return new RemoteOperationResult(checkQuotaResult.getCode()); } + final String targetPath = syncedState.getRemotePath(); if (!createRemoteFolder(targetPath, client)) { return new RemoteOperationResult(ResultCode.UNKNOWN_ERROR); } @@ -96,8 +101,25 @@ public class UploadFileOperation extends RemoteOperation { final ResultCode resultCode; boolean mustRestart = true; + final int fileSizeFloorForChunked = 3072000; //3MB + final RemoteOperationResult uploadResult; + + if (file.length() >= fileSizeFloorForChunked) { + uploadResult = uploadChunkedFile(file, client); + + if (uploadResult.isSuccess()) { + final RemoteOperationResult result = readRemoteFile(syncedState.getRemotePath(), client); + ArrayList resultData = result.getData(); + if (result.isSuccess() && resultData != null && !resultData.isEmpty()) { + final String latestETag = ((RemoteFile) resultData.get(0)).getEtag(); + uploadResult.setResultData(latestETag); + } + } + + } else { + uploadResult = uploadFile(file, client); + } - final RemoteOperationResult uploadResult = uploadFile(file, client); if (uploadResult.isSuccess()) { final String etag = uploadResult.getResultData(); if (etag != null) { @@ -181,6 +203,31 @@ public class UploadFileOperation extends RemoteOperation { return uploadOperation.execute(client); } + + /** + * Upload a chunked file + * Used for file bigger than 3MB + * @param file + * @param client + * @return + */ + @VisibleForTesting(otherwise = VisibleForTesting.PRIVATE) + public RemoteOperationResult uploadChunkedFile(final File file, final OwnCloudClient client) { + final String mimeType = CommonUtils.getMimeType(file); + final ChunkedFileUploadRemoteOperation uploadOperation = new ChunkedFileUploadRemoteOperation(syncedState.getLocalPath(), + syncedState.getRemotePath(), + mimeType, syncedState.getLastETAG(), + syncedState.getLocalLastModified()+"", false); + return uploadOperation.execute(client); + } + + + @VisibleForTesting(otherwise = VisibleForTesting.PRIVATE) + public RemoteOperationResult readRemoteFile(final String remotePath, final OwnCloudClient client) { + final ReadFileRemoteOperation readRemoteFile = new ReadFileRemoteOperation(remotePath); + return readRemoteFile.execute(client); + } + /** * Create remote parent folder of the file if missing * @param targetPath -- GitLab From 9eb8bef5c23fb0dd7739ad3c606c865ca111f9d8 Mon Sep 17 00:00:00 2001 From: vincent Bourgmayer Date: Fri, 15 Jul 2022 10:22:20 +0200 Subject: [PATCH 59/59] move chunkedUpload stuff from SyncWrapper to UploadFileOperation --- .../e/drive/models/SyncWrapper.java | 9 +-- .../drive/operations/UploadFileOperation.java | 55 +++++++++++++++++-- 2 files changed, 52 insertions(+), 12 deletions(-) diff --git a/app/src/main/java/foundation/e/drive/models/SyncWrapper.java b/app/src/main/java/foundation/e/drive/models/SyncWrapper.java index 36d14d34..9e3becdf 100644 --- a/app/src/main/java/foundation/e/drive/models/SyncWrapper.java +++ b/app/src/main/java/foundation/e/drive/models/SyncWrapper.java @@ -66,18 +66,11 @@ public class SyncWrapper { case UPLOAD: final SyncedFileState sfs = request.getSyncedFileState(); final File file = new File(sfs.getLocalPath()); - final int fileSizeFloorforChunked = 3072000; //3MB if (!file.exists()) { operation = null; Log.w(SyncWrapper.class.getSimpleName(), "createRemoteOperation: local file doesn't exist for upload"); } else { - if (file.length() >= fileSizeFloorforChunked) { - Log.w(SyncWrapper.class.getSimpleName(), "createRemoteOperation: create a chunkedFileUploadRemoteOperation"); - final String mimeType = CommonUtils.getMimeType(file); - operation = new ChunkedFileUploadRemoteOperation(sfs.getLocalPath(), sfs.getRemotePath(), mimeType, sfs.getLastETAG(), sfs.getLocalLastModified()+"", false); - } else { - operation = new UploadFileOperation(sfs, account, context); - } + operation = new UploadFileOperation(sfs, account, context); } break; case DOWNLOAD: diff --git a/app/src/main/java/foundation/e/drive/operations/UploadFileOperation.java b/app/src/main/java/foundation/e/drive/operations/UploadFileOperation.java index 4cfc10b4..efd8bfd4 100644 --- a/app/src/main/java/foundation/e/drive/operations/UploadFileOperation.java +++ b/app/src/main/java/foundation/e/drive/operations/UploadFileOperation.java @@ -20,12 +20,17 @@ import com.owncloud.android.lib.common.OwnCloudClient; import com.owncloud.android.lib.common.UserInfo; import com.owncloud.android.lib.common.operations.RemoteOperation; import com.owncloud.android.lib.common.operations.RemoteOperationResult; +import com.owncloud.android.lib.resources.files.ChunkedFileUploadRemoteOperation; import com.owncloud.android.lib.resources.files.CreateFolderRemoteOperation; import com.owncloud.android.lib.resources.files.FileUtils; +import com.owncloud.android.lib.resources.files.ReadFileRemoteOperation; import com.owncloud.android.lib.resources.files.UploadFileRemoteOperation; +import com.owncloud.android.lib.resources.files.model.RemoteFile; import com.owncloud.android.lib.resources.users.GetUserInfoRemoteOperation; import com.owncloud.android.lib.common.operations.RemoteOperationResult.ResultCode; import java.io.File; +import java.util.ArrayList; + import foundation.e.drive.database.DbHelper; import foundation.e.drive.models.SyncedFileState; import foundation.e.drive.utils.CommonUtils; @@ -74,14 +79,13 @@ public class UploadFileOperation extends RemoteOperation { return new RemoteOperationResult(ResultCode.FORBIDDEN); } - final String targetPath = syncedState.getRemotePath(); - //If file already up-to-date & synced if (syncedState.isLastEtagStored() && syncedState.getLocalLastModified() == file.lastModified()) { Log.d(TAG, "syncedState last modified: "+ syncedState.getLocalLastModified()+" <=> file last modified: "+file.lastModified() +": So return sync_conflict"); return new RemoteOperationResult(ResultCode.SYNC_CONFLICT); } + final NextcloudClient ncClient = DavClientProvider.getInstance().getNcClientInstance(account, context); final RemoteOperationResult checkQuotaResult = checkAvailableSpace(ncClient, file.length()); if (checkQuotaResult.getCode() != ResultCode.OK) { @@ -89,15 +93,33 @@ public class UploadFileOperation extends RemoteOperation { return new RemoteOperationResult(checkQuotaResult.getCode()); } + final String targetPath = syncedState.getRemotePath(); if (!createRemoteFolder(targetPath, client)) { return new RemoteOperationResult(ResultCode.UNKNOWN_ERROR); } - final ResultCode resultCode; boolean mustRestart = true; + final int fileSizeFloorForChunked = 3072000; //3MB + final RemoteOperationResult uploadResult; + + if (file.length() >= fileSizeFloorForChunked) { + Log.d(TAG, "upload "+file.getName()+" as chunked file"); + uploadResult = uploadChunkedFile(file, client); + + if (uploadResult.isSuccess()) { + final RemoteOperationResult result = readRemoteFile(syncedState.getRemotePath(), client); + ArrayList resultData = result.getData(); + if (result.isSuccess() && resultData != null && !resultData.isEmpty()) { + final String latestETag = ((RemoteFile) resultData.get(0)).getEtag(); + uploadResult.setResultData(latestETag); + } + } + + } else { + uploadResult = uploadFile(file, client); + } - final RemoteOperationResult uploadResult = uploadFile(file, client); if (uploadResult.isSuccess()) { final String etag = uploadResult.getResultData(); if (etag != null) { @@ -181,6 +203,31 @@ public class UploadFileOperation extends RemoteOperation { return uploadOperation.execute(client); } + + /** + * Upload a chunked file + * Used for file bigger than 3MB + * @param file + * @param client + * @return + */ + @VisibleForTesting(otherwise = VisibleForTesting.PRIVATE) + public RemoteOperationResult uploadChunkedFile(final File file, final OwnCloudClient client) { + final String mimeType = CommonUtils.getMimeType(file); + final ChunkedFileUploadRemoteOperation uploadOperation = new ChunkedFileUploadRemoteOperation(syncedState.getLocalPath(), + syncedState.getRemotePath(), + mimeType, syncedState.getLastETAG(), + syncedState.getLocalLastModified()+"", false); + return uploadOperation.execute(client); + } + + + @VisibleForTesting(otherwise = VisibleForTesting.PRIVATE) + public RemoteOperationResult readRemoteFile(final String remotePath, final OwnCloudClient client) { + final ReadFileRemoteOperation readRemoteFile = new ReadFileRemoteOperation(remotePath); + return readRemoteFile.execute(client); + } + /** * Create remote parent folder of the file if missing * @param targetPath -- GitLab