From 2f35e069f3b62c8c2329aa21f85093b4ba4ca6c1 Mon Sep 17 00:00:00 2001 From: vincent Bourgmayer Date: Thu, 2 Nov 2023 11:40:51 +0100 Subject: [PATCH 01/33] Start to implement a worker to replace SyncrhonizationService also add a Transfert Class to extends onRemoteOperationlistener --- .../services/SynchronizationService.java | 10 +-- .../synchronization/SyncCoroutineWorker.kt | 15 ++++ .../e/drive/synchronization/SyncWorker.kt | 74 +++++++++++++++++++ .../e/drive/synchronization/Transfer.kt | 63 ++++++++++++++++ 4 files changed, 157 insertions(+), 5 deletions(-) create mode 100644 app/src/main/java/foundation/e/drive/synchronization/SyncCoroutineWorker.kt create mode 100644 app/src/main/java/foundation/e/drive/synchronization/SyncWorker.kt create mode 100644 app/src/main/java/foundation/e/drive/synchronization/Transfer.kt 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 42700dc3..8ba6bde0 100644 --- a/app/src/main/java/foundation/e/drive/services/SynchronizationService.java +++ b/app/src/main/java/foundation/e/drive/services/SynchronizationService.java @@ -71,7 +71,7 @@ public class SynchronizationService extends Service implements OnRemoteOperation public int onStartCommand(@NonNull Intent intent, int flags, int startId) { Timber.v("onStartCommand()"); - final SharedPreferences prefs = this.getSharedPreferences(AppConstants.SHARED_PREFERENCE_NAME, Context.MODE_PRIVATE); + /*final SharedPreferences prefs = this.getSharedPreferences(AppConstants.SHARED_PREFERENCE_NAME, Context.MODE_PRIVATE); final String accountName = prefs.getString(AccountManager.KEY_ACCOUNT_NAME, ""); final String accountType = getApplicationContext().getString(R.string.eelo_account_type); account = CommonUtils.getAccount(accountName, accountType, AccountManager.get(this)); @@ -81,7 +81,7 @@ public class SynchronizationService extends Service implements OnRemoteOperation syncManager.startListeningFiles(getApplication()); stopSelf(); return START_NOT_STICKY; - } + }*/ threadPool = new Thread[workerAmount]; @@ -218,14 +218,14 @@ public class SynchronizationService extends Service implements OnRemoteOperation final RemoteOperation wrapperOperation = wrapper.getRemoteOperation(); if (wrapperOperation != null && wrapperOperation.equals(callerOperation)) { wrapper.setRunning(false); - updateFailureCounter(wrapper.getRequest(), result.isSuccess()); + //updateFailureCounter(wrapper.getRequest(), result.isSuccess()); startWorker(keyValue.getKey()); break; } } } - private void logSyncResult(@NonNull RemoteOperationResult.ResultCode resultCode, @NonNull RemoteOperation callerOperation) { + /*private void logSyncResult(@NonNull RemoteOperationResult.ResultCode resultCode, @NonNull RemoteOperation callerOperation) { String fileName; String operationType; if (callerOperation instanceof UploadFileOperation) { @@ -258,7 +258,7 @@ public class SynchronizationService extends Service implements OnRemoteOperation } failedPref.saveFileSyncFailure(fileStateId); } - } + }*/ @Override public void onSyncDisabled(int threadId, boolean succeed) { diff --git a/app/src/main/java/foundation/e/drive/synchronization/SyncCoroutineWorker.kt b/app/src/main/java/foundation/e/drive/synchronization/SyncCoroutineWorker.kt new file mode 100644 index 00000000..e53f8d33 --- /dev/null +++ b/app/src/main/java/foundation/e/drive/synchronization/SyncCoroutineWorker.kt @@ -0,0 +1,15 @@ +package foundation.e.drive.synchronization + +import android.content.Context +import androidx.work.CoroutineWorker +import androidx.work.WorkerParameters + +class SyncCoroutineWorker( + context: Context, + params: WorkerParameters +) : CoroutineWorker(context, params) { + override suspend fun doWork(): Result { + TODO("Not yet implemented") + } + +} \ No newline at end of file diff --git a/app/src/main/java/foundation/e/drive/synchronization/SyncWorker.kt b/app/src/main/java/foundation/e/drive/synchronization/SyncWorker.kt new file mode 100644 index 00000000..7e9c58f2 --- /dev/null +++ b/app/src/main/java/foundation/e/drive/synchronization/SyncWorker.kt @@ -0,0 +1,74 @@ +package foundation.e.drive.synchronization + +import android.accounts.Account +import android.accounts.AccountManager +import android.content.Context + +import androidx.work.Worker +import androidx.work.WorkerParameters +import com.owncloud.android.lib.common.OwnCloudClient +import foundation.e.drive.EdriveApplication +import foundation.e.drive.R +import foundation.e.drive.utils.AppConstants +import foundation.e.drive.utils.CommonUtils +import foundation.e.drive.utils.DavClientProvider +import timber.log.Timber +import java.util.concurrent.LinkedBlockingQueue +import java.util.concurrent.ThreadPoolExecutor +import java.util.concurrent.TimeUnit + +class SyncWorker( + context: Context, + params: WorkerParameters +) : Worker(context, params) { + + companion object { + const val threadAmount = 2 + val threadPoolExecutor = ThreadPoolExecutor(2, 2, 2, TimeUnit.MINUTES, LinkedBlockingQueue()) + } + + + override fun doWork(): Result { + val syncManager = SyncProxy as SyncManager + val account = loadAccount() + if (account == null) { + syncManager.startListeningFiles(applicationContext as EdriveApplication) + //todo find how to exit + } + val ocClient = getOcClient(account!!) + if (ocClient == null) { + syncManager.startListeningFiles(applicationContext as EdriveApplication) + //todo find how to exit + } + + return Result.success() + } + + private fun loadAccount(): Account? { + val context = applicationContext + val prefs = context.getSharedPreferences(AppConstants.SHARED_PREFERENCE_NAME, Context.MODE_PRIVATE) + val accountName = prefs.getString(AccountManager.KEY_ACCOUNT_NAME, "") + val accountType = context.getString(R.string.eelo_account_type) + //for above: AS complaint about accountName as String? while it can't be... + return CommonUtils.getAccount(accountName!!, accountType, AccountManager.get(context)) + } + + private fun getOcClient(account: Account): OwnCloudClient? { + return DavClientProvider.getInstance().getClientInstance( + account, + applicationContext + ) + } + + private fun startWorker(threadIndex: Int) { + val syncManager = (SyncProxy as SyncManager) + val syncRequest = syncManager.pollSyncRequest() + if (syncRequest == null) { + Timber.d("There is no more syncRequest to poll") + + return; + } + } + + +} \ No newline at end of file diff --git a/app/src/main/java/foundation/e/drive/synchronization/Transfer.kt b/app/src/main/java/foundation/e/drive/synchronization/Transfer.kt new file mode 100644 index 00000000..9494f197 --- /dev/null +++ b/app/src/main/java/foundation/e/drive/synchronization/Transfer.kt @@ -0,0 +1,63 @@ +package foundation.e.drive.synchronization + +import android.content.Context +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 foundation.e.drive.database.FailedSyncPrefsManager +import foundation.e.drive.models.SyncRequest +import foundation.e.drive.operations.DownloadFileOperation +import foundation.e.drive.operations.UploadFileOperation +import foundation.e.drive.operations.UploadFileOperation.FILE_SIZE_FLOOR_FOR_CHUNKED +import timber.log.Timber +import java.io.File + +class Transfer(val context: Context): OnRemoteOperationListener { + + + + + override fun onRemoteOperationFinish(remoteOperation: RemoteOperation<*>, result: RemoteOperationResult<*>) { + Timber.v("onRemoteOperationFinish()") + + logSyncResult(result.code, remoteOperation) + + //updateFailureCounter() + } + + private fun logSyncResult( + code: RemoteOperationResult.ResultCode, + remoteOperation: RemoteOperation<*> + ) { + var fileName = "" + var operationType = "" + if (remoteOperation is UploadFileOperation) { + operationType = "Upload" + fileName = remoteOperation.syncedState.name + } else if (remoteOperation is DownloadFileOperation) { + operationType = "Download" + fileName = remoteOperation.remoteFilePath?: "" + } + Timber.d("$operationType operation for $fileName result in: $code") + } + + + private fun updateFailureCounter(request: SyncRequest, success: Boolean) { + val failedPrefs = FailedSyncPrefsManager.getInstance(context) + val fileState = request.syncedFileState + val fileStateId = fileState.id + + if (!success) { + if (request.operationType == SyncRequest.Type.UPLOAD) { + val filePath = fileState.localPath + if (filePath.isEmpty()) return; + val file = File(filePath) + if (file.length() >= FILE_SIZE_FLOOR_FOR_CHUNKED) return + } + failedPrefs.saveFileSyncFailure(fileStateId) + return + } + + failedPrefs.removeDataForFile(fileStateId) + } +} \ No newline at end of file -- GitLab From 72bfb4edf1562455a95a7e354dd389ad247561a4 Mon Sep 17 00:00:00 2001 From: vincent Bourgmayer Date: Mon, 6 Nov 2023 14:45:15 +0100 Subject: [PATCH 02/33] finish to implement class 'Transfer' as listener for upload/download & sync disabling --- app/build.gradle | 3 + .../services/SynchronizationService.java | 101 +----------------- .../e/drive/synchronization/Transfer.kt | 65 ++++++++++- 3 files changed, 64 insertions(+), 105 deletions(-) diff --git a/app/build.gradle b/app/build.gradle index 2073fe13..ec01d29c 100644 --- a/app/build.gradle +++ b/app/build.gradle @@ -105,8 +105,11 @@ dependencies { implementation 'com.github.bumptech.glide:glide:4.15.1' implementation 'com.github.bumptech.glide:annotations:4.15.1' implementation 'androidx.core:core-ktx:1.10.1' + implementation 'androidx.work:work-runtime-ktx:2.8.1' annotationProcessor 'com.github.bumptech.glide:compiler:4.15.1' implementation "androidx.work:work-runtime:2.8.1" + + // Kotlin implementation 'androidx.test:core:1.5.0' implementation 'com.jakewharton.timber:timber:5.0.1' implementation 'foundation.e:elib:0.0.1-alpha11' 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 8ba6bde0..fc1c5f54 100644 --- a/app/src/main/java/foundation/e/drive/services/SynchronizationService.java +++ b/app/src/main/java/foundation/e/drive/services/SynchronizationService.java @@ -10,11 +10,8 @@ package foundation.e.drive.services; import static foundation.e.drive.operations.UploadFileOperation.FILE_SIZE_FLOOR_FOR_CHUNKED; import android.accounts.Account; -import android.accounts.AccountManager; import android.app.Service; -import android.content.Context; import android.content.Intent; -import android.content.SharedPreferences; import android.os.Handler; import android.os.HandlerThread; import android.os.IBinder; @@ -22,24 +19,12 @@ import android.os.IBinder; import androidx.annotation.NonNull; import androidx.annotation.Nullable; 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.io.File; -import java.util.Map; - -import foundation.e.drive.R; -import foundation.e.drive.database.DbHelper; -import foundation.e.drive.database.FailedSyncPrefsManager; import foundation.e.drive.models.SyncRequest; import foundation.e.drive.models.SyncWrapper; -import foundation.e.drive.models.SyncedFileState; -import foundation.e.drive.operations.DownloadFileOperation; -import foundation.e.drive.operations.UploadFileOperation; import foundation.e.drive.synchronization.SyncManager; import foundation.e.drive.synchronization.SyncProxy; -import foundation.e.drive.utils.AppConstants; import foundation.e.drive.utils.CommonUtils; import foundation.e.drive.utils.DavClientProvider; import foundation.e.drive.work.FileSyncDisabler; @@ -48,7 +33,7 @@ import timber.log.Timber; /** * @author Vincent Bourgmayer */ -public class SynchronizationService extends Service implements OnRemoteOperationListener, FileSyncDisabler.FileSyncDisablingListener { +public class SynchronizationService extends Service { private final SyncManager syncManager = SyncProxy.INSTANCE; private Account account; @@ -187,90 +172,6 @@ public class SynchronizationService extends Service implements OnRemoteOperation return CommonUtils.haveNetworkConnection(getApplicationContext(), meteredNetworkAllowed); } - @Override - public void onRemoteOperationFinish(@NonNull RemoteOperation callerOperation, @NonNull RemoteOperationResult result) { - Timber.v("onRemoteOperationFinish()"); - - boolean isNetworkDisconnected = false; - - logSyncResult(result.getCode(), callerOperation); - - switch (result.getCode()) { - case UNKNOWN_ERROR: - case FORBIDDEN: - if (callerOperation instanceof UploadFileOperation) { - final int rowAffected = DbHelper.forceFoldertoBeRescan(((UploadFileOperation) callerOperation).getSyncedState().getId(), getApplicationContext()); - Timber.d("Force folder to be rescan next time (row affected) : %s", rowAffected); - } - break; - case NO_NETWORK_CONNECTION: - case WRONG_CONNECTION: - isNetworkDisconnected = true; - break; - } - - if (isNetworkDisconnected) { - syncManager.clearRequestQueue(); - } - - for (Map.Entry keyValue : syncManager.getStartedRequests().entrySet()) { - final SyncWrapper wrapper = keyValue.getValue(); - final RemoteOperation wrapperOperation = wrapper.getRemoteOperation(); - if (wrapperOperation != null && wrapperOperation.equals(callerOperation)) { - wrapper.setRunning(false); - //updateFailureCounter(wrapper.getRequest(), result.isSuccess()); - startWorker(keyValue.getKey()); - break; - } - } - } - - /*private void logSyncResult(@NonNull RemoteOperationResult.ResultCode resultCode, @NonNull RemoteOperation callerOperation) { - String fileName; - String operationType; - if (callerOperation instanceof UploadFileOperation) { - operationType = "Upload"; - fileName = ((UploadFileOperation) callerOperation).getSyncedState().getName(); - } else if (callerOperation instanceof DownloadFileOperation) { - operationType = "Download"; - fileName = ((DownloadFileOperation) callerOperation).getRemoteFilePath(); - } else return; - Timber.d("%s operation for %s result in: %s", operationType, fileName, resultCode.name()); - } - - private void updateFailureCounter(SyncRequest request, boolean success) { - final FailedSyncPrefsManager failedPref = FailedSyncPrefsManager.getInstance(getApplicationContext()); - final SyncedFileState fileState = request.getSyncedFileState(); - - final int fileStateId = fileState.getId(); - - if (success) { - failedPref.removeDataForFile(fileStateId); - } else { - - if (request.getOperationType().equals(SyncRequest.Type.UPLOAD)) { - if (fileState.getLocalPath() == null) return; - final File file = new File(fileState.getLocalPath()); - - if (file.length() >= FILE_SIZE_FLOOR_FOR_CHUNKED) { - return; //do not delay future synchronization when it's for a chunked upload - } - } - failedPref.saveFileSyncFailure(fileStateId); - } - }*/ - - @Override - public void onSyncDisabled(int threadId, boolean succeed) { - final SyncWrapper wrapper = syncManager.getStartedRequestOnThread(threadId); - if (wrapper != null) { - final SyncRequest request = wrapper.getRequest(); - Timber.d("%s sync disabled ? %s", request.getSyncedFileState().getLocalPath(), succeed); - wrapper.setRunning(false); - updateFailureCounter(request, succeed); - } - startWorker(threadId); - } @Nullable @Override diff --git a/app/src/main/java/foundation/e/drive/synchronization/Transfer.kt b/app/src/main/java/foundation/e/drive/synchronization/Transfer.kt index 9494f197..9b523ddf 100644 --- a/app/src/main/java/foundation/e/drive/synchronization/Transfer.kt +++ b/app/src/main/java/foundation/e/drive/synchronization/Transfer.kt @@ -4,27 +4,49 @@ import android.content.Context 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 com.owncloud.android.lib.common.operations.RemoteOperationResult.ResultCode.FORBIDDEN +import com.owncloud.android.lib.common.operations.RemoteOperationResult.ResultCode.NO_NETWORK_CONNECTION +import com.owncloud.android.lib.common.operations.RemoteOperationResult.ResultCode.UNKNOWN_ERROR +import com.owncloud.android.lib.common.operations.RemoteOperationResult.ResultCode.WRONG_CONNECTION +import foundation.e.drive.database.DbHelper import foundation.e.drive.database.FailedSyncPrefsManager import foundation.e.drive.models.SyncRequest +import foundation.e.drive.models.SyncWrapper import foundation.e.drive.operations.DownloadFileOperation import foundation.e.drive.operations.UploadFileOperation import foundation.e.drive.operations.UploadFileOperation.FILE_SIZE_FLOOR_FOR_CHUNKED +import foundation.e.drive.work.FileSyncDisabler.FileSyncDisablingListener import timber.log.Timber import java.io.File -class Transfer(val context: Context): OnRemoteOperationListener { - - - +class Transfer(val context: Context): OnRemoteOperationListener, FileSyncDisablingListener { + companion object { + private val syncManager = SyncProxy as SyncManager + } override fun onRemoteOperationFinish(remoteOperation: RemoteOperation<*>, result: RemoteOperationResult<*>) { Timber.v("onRemoteOperationFinish()") logSyncResult(result.code, remoteOperation) - //updateFailureCounter() + handleResultCode(result.code, remoteOperation) + + val startedRequest = getStartedRequest(remoteOperation) + if (startedRequest == null) { + Timber.d("Cannot retrieve startedRequest for remoteOperation") + return + } + val wrapper = startedRequest.value + wrapper.isRunning = false + + val request = wrapper.request + updateFailureCounter(request, result.isSuccess) + + val threadId = startedRequest.key + startAnotherTransfer(threadId) } + private fun logSyncResult( code: RemoteOperationResult.ResultCode, remoteOperation: RemoteOperation<*> @@ -41,6 +63,16 @@ class Transfer(val context: Context): OnRemoteOperationListener { Timber.d("$operationType operation for $fileName result in: $code") } + private fun handleResultCode(code: RemoteOperationResult.ResultCode, remoteOperation: RemoteOperation<*>) { + if (remoteOperation is UploadFileOperation && (code == UNKNOWN_ERROR || code == FORBIDDEN)) { + val rowAffected = DbHelper.forceFoldertoBeRescan(remoteOperation.syncedState.id, context) + Timber.d("Force folder to be rescan next time (row affected) : $rowAffected") + + } else if (code == WRONG_CONNECTION || code == NO_NETWORK_CONNECTION) { + syncManager.clearRequestQueue() + Timber.d("Network has been lost. SyncRequest queue has been cleared") + } + } private fun updateFailureCounter(request: SyncRequest, success: Boolean) { val failedPrefs = FailedSyncPrefsManager.getInstance(context) @@ -60,4 +92,27 @@ class Transfer(val context: Context): OnRemoteOperationListener { failedPrefs.removeDataForFile(fileStateId) } + + private fun getStartedRequest(callerOperation: RemoteOperation<*>): MutableMap.MutableEntry? { + val startedRequests = syncManager.getStartedRequests() + + return startedRequests.entries.stream() + .filter { (key, value) -> value.remoteOperation == callerOperation } + .findFirst().get() + } + + private fun startAnotherTransfer(threadId: Int) { + + } + + override fun onSyncDisabled(threadId: Int, succeed: Boolean) { + val syncWrapper = syncManager.getStartedRequestOnThread(threadId) + if (syncWrapper != null) { + val request = syncWrapper.request + Timber.d("${request.syncedFileState.localPath} sync disabled ? $succeed") + syncWrapper.isRunning = false + updateFailureCounter(request, succeed) + } + startAnotherTransfer(threadId) + } } \ No newline at end of file -- GitLab From 5eda7d4b01aefbd95f16780bac2f0a9934780b9c Mon Sep 17 00:00:00 2001 From: vincent Bourgmayer Date: Mon, 6 Nov 2023 17:38:17 +0100 Subject: [PATCH 03/33] move core function of SynchrinozationService into the worker --- .../services/SynchronizationService.java | 95 +--------------- .../e/drive/synchronization/SyncWorker.kt | 105 ++++++++++++++++-- 2 files changed, 95 insertions(+), 105 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 fc1c5f54..f327eea6 100644 --- a/app/src/main/java/foundation/e/drive/services/SynchronizationService.java +++ b/app/src/main/java/foundation/e/drive/services/SynchronizationService.java @@ -38,7 +38,6 @@ public class SynchronizationService extends Service { private Account account; private final int workerAmount = 2; - private Thread[] threadPool; @SuppressWarnings("DeprecatedIsStillUsed") @Deprecated private OwnCloudClient ocClient; @@ -56,25 +55,6 @@ public class SynchronizationService extends Service { public int onStartCommand(@NonNull Intent intent, int flags, int startId) { Timber.v("onStartCommand()"); - /*final SharedPreferences prefs = this.getSharedPreferences(AppConstants.SHARED_PREFERENCE_NAME, Context.MODE_PRIVATE); - final String accountName = prefs.getString(AccountManager.KEY_ACCOUNT_NAME, ""); - final String accountType = getApplicationContext().getString(R.string.eelo_account_type); - account = CommonUtils.getAccount(accountName, accountType, AccountManager.get(this)); - - if (account == null) { - Timber.d("No account available"); - syncManager.startListeningFiles(getApplication()); - stopSelf(); - return START_NOT_STICKY; - }*/ - - threadPool = new Thread[workerAmount]; - - ocClient = DavClientProvider.getInstance().getClientInstance(account, getApplicationContext()); - if (ocClient == null) { - Timber.w("ocClient is null"); - } - handlerThread = new HandlerThread("syncService_onResponse"); handlerThread.start(); handler = new Handler(handlerThread.getLooper()); @@ -96,83 +76,10 @@ public class SynchronizationService extends Service { public void startSynchronization(){ Timber.d("startAllThreads"); for(int i =-1; ++i < workerAmount;){ - this.startWorker(i); - } - } - - private void startWorker(int threadIndex) { - if (ocClient == null) { - Timber.w("Can't start sync thread: ocClient is null"); - return; - } - - if (!isNetworkAvailable()) { - Timber.d("No network available: Clear syncRequestQueue"); - syncManager.clearRequestQueue(); - return; - } - - if (!canStart(threadIndex)) { - Timber.d("Can't start thread #%s, thread is already running", threadIndex); - return; - } - - final SyncRequest request = this.syncManager.pollSyncRequest(); //return null if empty - if (request == null) { - Timber.d("Thread #%s: No more sync request to start.", threadIndex); - syncManager.removeStartedRequest(threadIndex); - if (!syncManager.isAnySyncRequestRunning()) { - syncManager.startListeningFiles(getApplication()); - } - return; + // this.startWorker(i); } - - if (!CommonUtils.isThisSyncAllowed(account, request.getSyncedFileState().isMediaType())) { - Timber.d("thread #%s won't start cause sync is not allowed by user setting", threadIndex); - return; - } - - final SyncWrapper syncWrapper = new SyncWrapper(request, account, getApplicationContext()); - if (request.getOperationType().equals(SyncRequest.Type.DISABLE_SYNCING)) { - - Timber.d(" starts 'sync disabling' for file : %s on thread #%s", request.getSyncedFileState().getName(), threadIndex); - final FileSyncDisabler fileSyncDisabler = new FileSyncDisabler(request.getSyncedFileState()); - threadPool[threadIndex] = new Thread(fileSyncDisabler.getRunnable(handler, threadIndex, getApplicationContext(), this)); - threadPool[threadIndex].start(); - syncManager.addStartedRequest(threadIndex,syncWrapper); - return; - } - - @SuppressWarnings("rawtypes") - final RemoteOperation operation = syncWrapper.getRemoteOperation(); - - if (operation == null ) return; - - CommonUtils.createNotificationChannel(this); - Timber.v(" starts %s on thread #%s for: %s",request.getOperationType().name(), - threadIndex, request.getSyncedFileState().getName()); - - //noinspection deprecation - threadPool[threadIndex] = operation.execute(ocClient, this, handler); - syncManager.addStartedRequest(threadIndex, syncWrapper); - } - - /** - * Check if conditions are met to run a new file transfer - * @param threadIndex index of thread on which we want to perform the transfer - * @return false if nogo - */ - private boolean canStart(int threadIndex) { - final SyncWrapper syncWrapper = syncManager.getStartedRequestOnThread(threadIndex); - return (syncWrapper == null || !syncWrapper.isRunning()); } - private boolean isNetworkAvailable() { - final boolean meteredNetworkAllowed = CommonUtils.isMeteredNetworkAllowed(account); - return CommonUtils.haveNetworkConnection(getApplicationContext(), meteredNetworkAllowed); - } - - @Nullable @Override public IBinder onBind(@Nullable Intent intent) { diff --git a/app/src/main/java/foundation/e/drive/synchronization/SyncWorker.kt b/app/src/main/java/foundation/e/drive/synchronization/SyncWorker.kt index 7e9c58f2..c6918aa1 100644 --- a/app/src/main/java/foundation/e/drive/synchronization/SyncWorker.kt +++ b/app/src/main/java/foundation/e/drive/synchronization/SyncWorker.kt @@ -3,15 +3,20 @@ package foundation.e.drive.synchronization import android.accounts.Account import android.accounts.AccountManager import android.content.Context +import android.os.Handler +import android.os.HandlerThread import androidx.work.Worker import androidx.work.WorkerParameters import com.owncloud.android.lib.common.OwnCloudClient import foundation.e.drive.EdriveApplication import foundation.e.drive.R +import foundation.e.drive.models.SyncRequest +import foundation.e.drive.models.SyncWrapper import foundation.e.drive.utils.AppConstants import foundation.e.drive.utils.CommonUtils import foundation.e.drive.utils.DavClientProvider +import foundation.e.drive.work.FileSyncDisabler import timber.log.Timber import java.util.concurrent.LinkedBlockingQueue import java.util.concurrent.ThreadPoolExecutor @@ -22,24 +27,36 @@ class SyncWorker( params: WorkerParameters ) : Worker(context, params) { + private lateinit var account: Account + private var ocClient: OwnCloudClient? = null + private val threadPool = Array(threadAmount) { _ -> null } + private val handlerThread = HandlerThread("sync_worker_onResponse") + private lateinit var handler: Handler companion object { const val threadAmount = 2 - val threadPoolExecutor = ThreadPoolExecutor(2, 2, 2, TimeUnit.MINUTES, LinkedBlockingQueue()) + //val threadPoolExecutor = ThreadPoolExecutor(2, 2, 2, TimeUnit.MINUTES, LinkedBlockingQueue()) + val syncManager = SyncProxy as SyncManager } - override fun doWork(): Result { - val syncManager = SyncProxy as SyncManager - val account = loadAccount() - if (account == null) { + val tmpAccount = loadAccount() + if (tmpAccount == null) { syncManager.startListeningFiles(applicationContext as EdriveApplication) //todo find how to exit + return Result.failure() } - val ocClient = getOcClient(account!!) - if (ocClient == null) { + this.account = tmpAccount + + val tmpOcClient = getOcClient(account) + if (tmpOcClient == null) { syncManager.startListeningFiles(applicationContext as EdriveApplication) //todo find how to exit + return Result.failure() } + ocClient = tmpOcClient + + handlerThread.start() + handler = Handler(handlerThread.looper) return Result.success() } @@ -60,15 +77,81 @@ class SyncWorker( ) } - private fun startWorker(threadIndex: Int) { - val syncManager = (SyncProxy as SyncManager) + private fun isNetworkAvailable(account: Account): Boolean { + val isMeteredNetworkAllowed = CommonUtils.isMeteredNetworkAllowed(account) + return CommonUtils.haveNetworkConnection(applicationContext, isMeteredNetworkAllowed) + } + + /** + * Check if conditions are met to run a new file transfer + * @param threadId index of thread on which we want to perform the transfer + * @return false if no go + */ + private fun canStartOnThread(threadId: Int): Boolean { + val syncWrapper = syncManager.getStartedRequestOnThread(threadId); + return (syncWrapper == null || !syncWrapper.isRunning) + } + + private fun startWorker(threadId: Int) { + if (ocClient == null) { + Timber.d("Can't start worker on thread $threadId, OwnCloudClient is null") + return + } + if (!isNetworkAvailable(account)) { + Timber.d("No network available: Clear syncRequestQueue") + syncManager.clearRequestQueue() + return + } + + if (!canStartOnThread(threadId)) { + Timber.d("Can't start thread $threadId, thread is already running") + return + } + val syncRequest = syncManager.pollSyncRequest() if (syncRequest == null) { - Timber.d("There is no more syncRequest to poll") + Timber.d("Thread $threadId: no more syncRequest to poll") + syncManager.removeStartedRequest(threadId) + + if (!syncManager.isAnySyncRequestRunning()) { + syncManager.startListeningFiles(applicationContext as EdriveApplication) + } + return + } - return; + val isSyncAllowed = CommonUtils.isThisSyncAllowed(account, syncRequest.syncedFileState.isMediaType) + if (!isSyncAllowed){ + Timber.d("Sync of the file is not allowed anymore") + return } + + val syncWrapper = SyncWrapper(syncRequest, account, applicationContext) + if( syncRequest.operationType == SyncRequest.Type.DISABLE_SYNCING) { + startSyncDisablingRequest(syncWrapper, threadId) + return + } + CommonUtils.createNotificationChannel(applicationContext) + startFileTransferRequest(syncWrapper, threadId) } + private fun startSyncDisablingRequest(syncWrapper: SyncWrapper, threadId: Int) { + val request = syncWrapper.request + Timber.d(" starts 'sync disabling' for file : ${request.syncedFileState.name} on thread #${threadId}") + + val listener = Transfer(applicationContext) + val fileSyncDisabler = FileSyncDisabler(syncWrapper.request.syncedFileState) + threadPool[threadId] = Thread(fileSyncDisabler.getRunnable(handler, threadId, applicationContext, listener)) + threadPool[threadId]!!.start() + syncManager.addStartedRequest(threadId, syncWrapper) + } + + private fun startFileTransferRequest(syncWrapper: SyncWrapper, threadId: Int) { + val remoteOperation = syncWrapper.remoteOperation ?: return + val request = syncWrapper.request + Timber.v(" starts ${request.operationType.name} on thread #$threadId for: ${request.syncedFileState.name}") + val listener = Transfer(applicationContext) + threadPool[threadId] = Thread(remoteOperation.execute(ocClient, listener, handler)) + syncManager.addStartedRequest(threadId, syncWrapper) + } } \ No newline at end of file -- GitLab From c2062c4dcdd603110e844700e122fe55601cdcd2 Mon Sep 17 00:00:00 2001 From: vincent Bourgmayer Date: Tue, 7 Nov 2023 15:54:16 +0100 Subject: [PATCH 04/33] Remove SynchronizationService (which is replaced by SyncWorker.kt) Remove SyncCoroutineWorker as it is not the solution Replace Transfer as Listener by SyncTask as synchronous Runnable SyncWorker now use ExecutorService to start all request --- app/src/main/AndroidManifest.xml | 1 - .../AccountRemoveCallbackReceiver.java | 9 -- .../e/drive/models/SyncWrapper.java | 4 +- .../services/SynchronizationService.java | 88 ----------- .../synchronization/SyncCoroutineWorker.kt | 15 -- .../e/drive/synchronization/SyncProxy.kt | 31 ++-- .../e/drive/synchronization/SyncTask.kt | 138 ++++++++++++++++++ .../e/drive/synchronization/SyncWorker.kt | 113 +++----------- .../e/drive/synchronization/Transfer.kt | 118 --------------- .../tasks}/DownloadFileOperation.java | 2 +- .../tasks}/UploadFileOperation.java | 2 +- .../e/drive/work/FileSyncDisabler.java | 55 ------- 12 files changed, 180 insertions(+), 396 deletions(-) delete mode 100644 app/src/main/java/foundation/e/drive/services/SynchronizationService.java delete mode 100644 app/src/main/java/foundation/e/drive/synchronization/SyncCoroutineWorker.kt create mode 100644 app/src/main/java/foundation/e/drive/synchronization/SyncTask.kt delete mode 100644 app/src/main/java/foundation/e/drive/synchronization/Transfer.kt rename app/src/main/java/foundation/e/drive/{operations => synchronization/tasks}/DownloadFileOperation.java (99%) rename app/src/main/java/foundation/e/drive/{operations => synchronization/tasks}/UploadFileOperation.java (99%) delete mode 100644 app/src/main/java/foundation/e/drive/work/FileSyncDisabler.java diff --git a/app/src/main/AndroidManifest.xml b/app/src/main/AndroidManifest.xml index f8c0bfe3..9c3f8b2c 100644 --- a/app/src/main/AndroidManifest.xml +++ b/app/src/main/AndroidManifest.xml @@ -83,7 +83,6 @@ android:exported="true" android:label="@string/account_setting_metered_network" tools:ignore="ExportedContentProvider" /> - + fun getStartedRequestOnThread(fileName: String): SyncWrapper? + fun getStartedRequests(): ConcurrentHashMap fun isAnySyncRequestRunning(): Boolean - fun removeStartedRequest(threadIndex: Int) + fun removeStartedRequest(filenName: String) fun startListeningFiles(application: Application) } @@ -57,7 +55,7 @@ interface SyncManager { */ object SyncProxy: SyncRequestCollector, SyncManager { private val syncRequestQueue: ConcurrentLinkedQueue = ConcurrentLinkedQueue() //could we use channel instead ? - private val startedRequest: ConcurrentHashMap = ConcurrentHashMap() + private val startedRequest: ConcurrentHashMap = ConcurrentHashMap() /** * Add a SyncRequest into waiting queue if it matches some conditions: @@ -136,8 +134,7 @@ object SyncProxy: SyncRequestCollector, SyncManager { } if (previousSyncState != SyncState.SYNCHRONIZING) { - val intent = Intent(context, SynchronizationService::class.java) - context.startService(intent) + // todo: start SyncWorker } } @@ -206,15 +203,15 @@ object SyncProxy: SyncRequestCollector, SyncManager { return syncRequestQueue.poll() } - override fun addStartedRequest(threadId: Int, syncWrapper: SyncWrapper) { - startedRequest[threadId] = syncWrapper + override fun addStartedRequest(fileName: String, syncWrapper: SyncWrapper) { + startedRequest[fileName] = syncWrapper } override fun isQueueEmpty(): Boolean { return syncRequestQueue.isEmpty() } - override fun getStartedRequests(): ConcurrentHashMap { + override fun getStartedRequests(): ConcurrentHashMap { return startedRequest } @@ -226,13 +223,13 @@ object SyncProxy: SyncRequestCollector, SyncManager { syncRequestQueue.clear() } - override fun getStartedRequestOnThread(threadIndex: Int): SyncWrapper? { - return startedRequest[threadIndex] + override fun getStartedRequestOnThread(fileName: String): SyncWrapper? { + return startedRequest[fileName] } - override fun removeStartedRequest(threadIndex: Int) { - if (startedRequest[threadIndex]?.isRunning == false) { - startedRequest.remove(threadIndex) + override fun removeStartedRequest(fileName: String) { + if (startedRequest[fileName]?.isRunning == false) { + startedRequest.remove(fileName) } } } \ No newline at end of file diff --git a/app/src/main/java/foundation/e/drive/synchronization/SyncTask.kt b/app/src/main/java/foundation/e/drive/synchronization/SyncTask.kt new file mode 100644 index 00000000..3d096709 --- /dev/null +++ b/app/src/main/java/foundation/e/drive/synchronization/SyncTask.kt @@ -0,0 +1,138 @@ +package foundation.e.drive.synchronization + +import android.accounts.Account +import android.content.Context +import com.owncloud.android.lib.common.OwnCloudClient +import com.owncloud.android.lib.common.operations.RemoteOperationResult +import foundation.e.drive.database.DbHelper +import foundation.e.drive.database.FailedSyncPrefsManager +import foundation.e.drive.models.DownloadRequest +import foundation.e.drive.models.SyncRequest +import foundation.e.drive.models.SyncRequest.Type.* +import foundation.e.drive.models.SyncWrapper +import foundation.e.drive.synchronization.tasks.UploadFileOperation +import foundation.e.drive.utils.CommonUtils +import timber.log.Timber +import java.io.File + +class SyncTask(val request: SyncRequest, val client: OwnCloudClient, val account: Account, val context: Context): Runnable { + private val fileName = getFileName() + companion object { + private val syncManager = SyncProxy as SyncManager + } + override fun run() { + val canStart = checkStartCondition() + if (!canStart) { + return + } + val wrapper = SyncWrapper(request, account, context) + Timber.d(" starts ${request.operationType.name} for: $fileName") + syncManager.addStartedRequest(fileName, wrapper) + val succeed = when (request.operationType) { + UPLOAD -> runUpload(wrapper) + DOWNLOAD -> runDownload(wrapper) + DISABLE_SYNCING -> runSyncDisabling() + } + updateFailureCounter(request, succeed) + syncManager.removeStartedRequest(fileName) + } + + private fun checkStartCondition(): Boolean { + if (client == null) { + Timber.d("Can't start thread: OwnCloudClient is null") + return false + } + + val isSyncAllowed = CommonUtils.isThisSyncAllowed(account, request.syncedFileState.isMediaType) + if (!isSyncAllowed){ + Timber.d("Sync of the file is not allowed anymore") + return false + } + return true + } + + + private fun runUpload(syncWrapper: SyncWrapper): Boolean { + val result = syncWrapper.remoteOperation?.execute(client) + if (result == null) { + Timber.d("Error: Upload result for $fileName is null") + return false + } + + val code = result.code + Timber.d("Upload operation for $fileName result in: ${result.code}") + + if (code == RemoteOperationResult.ResultCode.UNKNOWN_ERROR || code == RemoteOperationResult.ResultCode.FORBIDDEN) { + val operation = syncWrapper.remoteOperation as UploadFileOperation + val rowAffected = DbHelper.forceFoldertoBeRescan(operation.syncedState.id, context) + Timber.d("Force folder to be rescan next time (row affected) : $rowAffected") + return false + } + + if (isNetworkLost(code)) { + syncManager.clearRequestQueue() + Timber.d("Network has been lost. SyncRequest queue has been cleared") + return false + } + return result.isSuccess + } + + private fun runDownload(syncWrapper: SyncWrapper): Boolean { + val result = syncWrapper.remoteOperation?.execute(client) + if (result == null) { + Timber.d("Error: Download result for $fileName is null") + return false + } + val code = result.code + Timber.d("Download operation for $fileName result in: ${result.code}") + + if (isNetworkLost(code)) { + syncManager.clearRequestQueue() + Timber.d("Network has been lost. SyncRequest queue has been cleared") + return false + } + return result.isSuccess + } + + private fun runSyncDisabling(): Boolean { + val fileState = request.syncedFileState + + fileState.disableScanning() + if (DbHelper.manageSyncedFileStateDB(fileState, "UPDATE", context) <= 0) { + Timber.d("Failed to disable sync for $fileState.name from DB") + return false + } + return true + } + + private fun getFileName(): String { + return if (request is DownloadRequest ) { + request.remoteFile.remotePath //todo why not just use syncedFileStateName ? + } else + request.syncedFileState.name + } + + + private fun isNetworkLost(code: RemoteOperationResult.ResultCode): Boolean { + return (code == RemoteOperationResult.ResultCode.WRONG_CONNECTION || code == RemoteOperationResult.ResultCode.NO_NETWORK_CONNECTION) + } + + private fun updateFailureCounter(request: SyncRequest, success: Boolean) { + val failedPrefs = FailedSyncPrefsManager.getInstance(context) + val fileState = request.syncedFileState + val fileStateId = fileState.id + + if (!success) { + if (request.operationType == SyncRequest.Type.UPLOAD) { + val filePath = fileState.localPath + if (filePath.isEmpty()) return; + val file = File(filePath) + if (file.length() >= UploadFileOperation.FILE_SIZE_FLOOR_FOR_CHUNKED) return + } + failedPrefs.saveFileSyncFailure(fileStateId) + return + } + + failedPrefs.removeDataForFile(fileStateId) + } +} \ No newline at end of file diff --git a/app/src/main/java/foundation/e/drive/synchronization/SyncWorker.kt b/app/src/main/java/foundation/e/drive/synchronization/SyncWorker.kt index c6918aa1..130f9009 100644 --- a/app/src/main/java/foundation/e/drive/synchronization/SyncWorker.kt +++ b/app/src/main/java/foundation/e/drive/synchronization/SyncWorker.kt @@ -3,23 +3,17 @@ package foundation.e.drive.synchronization import android.accounts.Account import android.accounts.AccountManager import android.content.Context -import android.os.Handler -import android.os.HandlerThread import androidx.work.Worker import androidx.work.WorkerParameters import com.owncloud.android.lib.common.OwnCloudClient import foundation.e.drive.EdriveApplication import foundation.e.drive.R -import foundation.e.drive.models.SyncRequest -import foundation.e.drive.models.SyncWrapper import foundation.e.drive.utils.AppConstants import foundation.e.drive.utils.CommonUtils import foundation.e.drive.utils.DavClientProvider -import foundation.e.drive.work.FileSyncDisabler import timber.log.Timber -import java.util.concurrent.LinkedBlockingQueue -import java.util.concurrent.ThreadPoolExecutor +import java.util.concurrent.Executors import java.util.concurrent.TimeUnit class SyncWorker( @@ -28,13 +22,10 @@ class SyncWorker( ) : Worker(context, params) { private lateinit var account: Account - private var ocClient: OwnCloudClient? = null - private val threadPool = Array(threadAmount) { _ -> null } - private val handlerThread = HandlerThread("sync_worker_onResponse") - private lateinit var handler: Handler + private lateinit var ocClient: OwnCloudClient + companion object { const val threadAmount = 2 - //val threadPoolExecutor = ThreadPoolExecutor(2, 2, 2, TimeUnit.MINUTES, LinkedBlockingQueue()) val syncManager = SyncProxy as SyncManager } @@ -42,7 +33,6 @@ class SyncWorker( val tmpAccount = loadAccount() if (tmpAccount == null) { syncManager.startListeningFiles(applicationContext as EdriveApplication) - //todo find how to exit return Result.failure() } this.account = tmpAccount @@ -50,14 +40,32 @@ class SyncWorker( val tmpOcClient = getOcClient(account) if (tmpOcClient == null) { syncManager.startListeningFiles(applicationContext as EdriveApplication) - //todo find how to exit return Result.failure() } ocClient = tmpOcClient - handlerThread.start() - handler = Handler(handlerThread.looper) + if (!isNetworkAvailable(account)) { + Timber.d("No network available: Clear syncRequestQueue") + syncManager.clearRequestQueue() + return Result.failure() + } + + CommonUtils.createNotificationChannel(applicationContext) + val executor = Executors.newFixedThreadPool(threadAmount) + + while (!syncManager.isQueueEmpty()) { + val request = syncManager.pollSyncRequest()?: break + val task = SyncTask(request, ocClient, account, applicationContext) + /*val futur =*/ executor.submit(task) + } + + executor.shutdown() + while (!executor.isTerminated && !executor.isShutdown) { + executor.awaitTermination(30, TimeUnit.SECONDS) + } + + syncManager.startListeningFiles(applicationContext as EdriveApplication) return Result.success() } @@ -81,77 +89,4 @@ class SyncWorker( val isMeteredNetworkAllowed = CommonUtils.isMeteredNetworkAllowed(account) return CommonUtils.haveNetworkConnection(applicationContext, isMeteredNetworkAllowed) } - - /** - * Check if conditions are met to run a new file transfer - * @param threadId index of thread on which we want to perform the transfer - * @return false if no go - */ - private fun canStartOnThread(threadId: Int): Boolean { - val syncWrapper = syncManager.getStartedRequestOnThread(threadId); - return (syncWrapper == null || !syncWrapper.isRunning) - } - - private fun startWorker(threadId: Int) { - if (ocClient == null) { - Timber.d("Can't start worker on thread $threadId, OwnCloudClient is null") - return - } - if (!isNetworkAvailable(account)) { - Timber.d("No network available: Clear syncRequestQueue") - syncManager.clearRequestQueue() - return - } - - if (!canStartOnThread(threadId)) { - Timber.d("Can't start thread $threadId, thread is already running") - return - } - - val syncRequest = syncManager.pollSyncRequest() - if (syncRequest == null) { - Timber.d("Thread $threadId: no more syncRequest to poll") - syncManager.removeStartedRequest(threadId) - - if (!syncManager.isAnySyncRequestRunning()) { - syncManager.startListeningFiles(applicationContext as EdriveApplication) - } - return - } - - val isSyncAllowed = CommonUtils.isThisSyncAllowed(account, syncRequest.syncedFileState.isMediaType) - if (!isSyncAllowed){ - Timber.d("Sync of the file is not allowed anymore") - return - } - - val syncWrapper = SyncWrapper(syncRequest, account, applicationContext) - if( syncRequest.operationType == SyncRequest.Type.DISABLE_SYNCING) { - startSyncDisablingRequest(syncWrapper, threadId) - return - } - CommonUtils.createNotificationChannel(applicationContext) - startFileTransferRequest(syncWrapper, threadId) - } - - - private fun startSyncDisablingRequest(syncWrapper: SyncWrapper, threadId: Int) { - val request = syncWrapper.request - Timber.d(" starts 'sync disabling' for file : ${request.syncedFileState.name} on thread #${threadId}") - - val listener = Transfer(applicationContext) - val fileSyncDisabler = FileSyncDisabler(syncWrapper.request.syncedFileState) - threadPool[threadId] = Thread(fileSyncDisabler.getRunnable(handler, threadId, applicationContext, listener)) - threadPool[threadId]!!.start() - syncManager.addStartedRequest(threadId, syncWrapper) - } - - private fun startFileTransferRequest(syncWrapper: SyncWrapper, threadId: Int) { - val remoteOperation = syncWrapper.remoteOperation ?: return - val request = syncWrapper.request - Timber.v(" starts ${request.operationType.name} on thread #$threadId for: ${request.syncedFileState.name}") - val listener = Transfer(applicationContext) - threadPool[threadId] = Thread(remoteOperation.execute(ocClient, listener, handler)) - syncManager.addStartedRequest(threadId, syncWrapper) - } } \ No newline at end of file diff --git a/app/src/main/java/foundation/e/drive/synchronization/Transfer.kt b/app/src/main/java/foundation/e/drive/synchronization/Transfer.kt deleted file mode 100644 index 9b523ddf..00000000 --- a/app/src/main/java/foundation/e/drive/synchronization/Transfer.kt +++ /dev/null @@ -1,118 +0,0 @@ -package foundation.e.drive.synchronization - -import android.content.Context -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 com.owncloud.android.lib.common.operations.RemoteOperationResult.ResultCode.FORBIDDEN -import com.owncloud.android.lib.common.operations.RemoteOperationResult.ResultCode.NO_NETWORK_CONNECTION -import com.owncloud.android.lib.common.operations.RemoteOperationResult.ResultCode.UNKNOWN_ERROR -import com.owncloud.android.lib.common.operations.RemoteOperationResult.ResultCode.WRONG_CONNECTION -import foundation.e.drive.database.DbHelper -import foundation.e.drive.database.FailedSyncPrefsManager -import foundation.e.drive.models.SyncRequest -import foundation.e.drive.models.SyncWrapper -import foundation.e.drive.operations.DownloadFileOperation -import foundation.e.drive.operations.UploadFileOperation -import foundation.e.drive.operations.UploadFileOperation.FILE_SIZE_FLOOR_FOR_CHUNKED -import foundation.e.drive.work.FileSyncDisabler.FileSyncDisablingListener -import timber.log.Timber -import java.io.File - -class Transfer(val context: Context): OnRemoteOperationListener, FileSyncDisablingListener { - companion object { - private val syncManager = SyncProxy as SyncManager - } - - override fun onRemoteOperationFinish(remoteOperation: RemoteOperation<*>, result: RemoteOperationResult<*>) { - Timber.v("onRemoteOperationFinish()") - - logSyncResult(result.code, remoteOperation) - - handleResultCode(result.code, remoteOperation) - - val startedRequest = getStartedRequest(remoteOperation) - if (startedRequest == null) { - Timber.d("Cannot retrieve startedRequest for remoteOperation") - return - } - val wrapper = startedRequest.value - wrapper.isRunning = false - - val request = wrapper.request - updateFailureCounter(request, result.isSuccess) - - val threadId = startedRequest.key - startAnotherTransfer(threadId) - } - - - private fun logSyncResult( - code: RemoteOperationResult.ResultCode, - remoteOperation: RemoteOperation<*> - ) { - var fileName = "" - var operationType = "" - if (remoteOperation is UploadFileOperation) { - operationType = "Upload" - fileName = remoteOperation.syncedState.name - } else if (remoteOperation is DownloadFileOperation) { - operationType = "Download" - fileName = remoteOperation.remoteFilePath?: "" - } - Timber.d("$operationType operation for $fileName result in: $code") - } - - private fun handleResultCode(code: RemoteOperationResult.ResultCode, remoteOperation: RemoteOperation<*>) { - if (remoteOperation is UploadFileOperation && (code == UNKNOWN_ERROR || code == FORBIDDEN)) { - val rowAffected = DbHelper.forceFoldertoBeRescan(remoteOperation.syncedState.id, context) - Timber.d("Force folder to be rescan next time (row affected) : $rowAffected") - - } else if (code == WRONG_CONNECTION || code == NO_NETWORK_CONNECTION) { - syncManager.clearRequestQueue() - Timber.d("Network has been lost. SyncRequest queue has been cleared") - } - } - - private fun updateFailureCounter(request: SyncRequest, success: Boolean) { - val failedPrefs = FailedSyncPrefsManager.getInstance(context) - val fileState = request.syncedFileState - val fileStateId = fileState.id - - if (!success) { - if (request.operationType == SyncRequest.Type.UPLOAD) { - val filePath = fileState.localPath - if (filePath.isEmpty()) return; - val file = File(filePath) - if (file.length() >= FILE_SIZE_FLOOR_FOR_CHUNKED) return - } - failedPrefs.saveFileSyncFailure(fileStateId) - return - } - - failedPrefs.removeDataForFile(fileStateId) - } - - private fun getStartedRequest(callerOperation: RemoteOperation<*>): MutableMap.MutableEntry? { - val startedRequests = syncManager.getStartedRequests() - - return startedRequests.entries.stream() - .filter { (key, value) -> value.remoteOperation == callerOperation } - .findFirst().get() - } - - private fun startAnotherTransfer(threadId: Int) { - - } - - override fun onSyncDisabled(threadId: Int, succeed: Boolean) { - val syncWrapper = syncManager.getStartedRequestOnThread(threadId) - if (syncWrapper != null) { - val request = syncWrapper.request - Timber.d("${request.syncedFileState.localPath} sync disabled ? $succeed") - syncWrapper.isRunning = false - updateFailureCounter(request, succeed) - } - startAnotherTransfer(threadId) - } -} \ No newline at end of file diff --git a/app/src/main/java/foundation/e/drive/operations/DownloadFileOperation.java b/app/src/main/java/foundation/e/drive/synchronization/tasks/DownloadFileOperation.java similarity index 99% rename from app/src/main/java/foundation/e/drive/operations/DownloadFileOperation.java rename to app/src/main/java/foundation/e/drive/synchronization/tasks/DownloadFileOperation.java index 942fd11d..4f6b4573 100644 --- a/app/src/main/java/foundation/e/drive/operations/DownloadFileOperation.java +++ b/app/src/main/java/foundation/e/drive/synchronization/tasks/DownloadFileOperation.java @@ -7,7 +7,7 @@ * http://www.gnu.org/licenses/gpl.html */ -package foundation.e.drive.operations; +package foundation.e.drive.synchronization.tasks; import static com.owncloud.android.lib.common.operations.RemoteOperationResult.ResultCode.FILE_NOT_FOUND; import static com.owncloud.android.lib.common.operations.RemoteOperationResult.ResultCode.FORBIDDEN; diff --git a/app/src/main/java/foundation/e/drive/operations/UploadFileOperation.java b/app/src/main/java/foundation/e/drive/synchronization/tasks/UploadFileOperation.java similarity index 99% rename from app/src/main/java/foundation/e/drive/operations/UploadFileOperation.java rename to app/src/main/java/foundation/e/drive/synchronization/tasks/UploadFileOperation.java index 47cdd83e..ca723c10 100644 --- a/app/src/main/java/foundation/e/drive/operations/UploadFileOperation.java +++ b/app/src/main/java/foundation/e/drive/synchronization/tasks/UploadFileOperation.java @@ -7,7 +7,7 @@ * http://www.gnu.org/licenses/gpl.html */ -package foundation.e.drive.operations; +package foundation.e.drive.synchronization.tasks; import static foundation.e.drive.utils.FileUtils.getMimeType; diff --git a/app/src/main/java/foundation/e/drive/work/FileSyncDisabler.java b/app/src/main/java/foundation/e/drive/work/FileSyncDisabler.java deleted file mode 100644 index 76af0fd9..00000000 --- a/app/src/main/java/foundation/e/drive/work/FileSyncDisabler.java +++ /dev/null @@ -1,55 +0,0 @@ -/* - * Copyright © MURENA SAS 2022. - * All rights reserved. This program and the accompanying materials - * are made available under the terms of the GNU Public License v3.0 - * which accompanies this distribution, and is available at - * http://www.gnu.org/licenses/gpl.html - */ -package foundation.e.drive.work; - -import android.content.Context; -import android.os.Handler; - -import androidx.annotation.NonNull; - -import foundation.e.drive.database.DbHelper; -import foundation.e.drive.models.SyncedFileState; -import timber.log.Timber; - -/** - * New Goal: Disable eDrive Synchronization of the given file - * - * previous goal: This worker is called when a remote file has been detected as removed and that all the - * scanning process lead to choose to delete the local version too. - * - * @author vincent Bourgmayer - */ -public class FileSyncDisabler { - - public interface FileSyncDisablingListener { - void onSyncDisabled(int threadId, boolean succeed); - } - - private final SyncedFileState fileState; - - public FileSyncDisabler(@NonNull SyncedFileState syncedFileState) { - fileState = syncedFileState; - } - - @NonNull - public Runnable getRunnable(@NonNull final Handler handler, final int threadId, @NonNull final Context context, @NonNull final FileSyncDisablingListener listener) { - return () -> { - - fileState.disableScanning(); - if (DbHelper.manageSyncedFileStateDB(fileState, "UPDATE", context) <= 0) { - Timber.d("Failed to remove %s from DB", fileState.getName()); - } - - notifyCompletion(threadId, listener, true, handler); - }; - } - - private void notifyCompletion(final int threadId, final @NonNull FileSyncDisablingListener listener, final boolean success, @NonNull final Handler handler) { - handler.post(() -> listener.onSyncDisabled(threadId, success)); - } -} \ No newline at end of file -- GitLab From 48266ebba130846f4253cd810433e615ee961ded Mon Sep 17 00:00:00 2001 From: vincent Bourgmayer Date: Wed, 8 Nov 2023 09:52:24 +0100 Subject: [PATCH 05/33] remove 'isRunning' from syncWrapper --- .../e/drive/models/SyncWrapper.java | 10 ------- .../e/drive/synchronization/SyncProxy.kt | 15 +++++------ .../e/drive/synchronization/SyncWorker.kt | 27 +++++++++++-------- .../operations/UploadFileOperationTest.java | 1 + 4 files changed, 23 insertions(+), 30 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 c0159565..6bbf6221 100644 --- a/app/src/main/java/foundation/e/drive/models/SyncWrapper.java +++ b/app/src/main/java/foundation/e/drive/models/SyncWrapper.java @@ -26,7 +26,6 @@ import foundation.e.drive.synchronization.tasks.UploadFileOperation; public class SyncWrapper { private final SyncRequest request; private final RemoteOperation remoteOperation; - private boolean isRunning; /** * Build an instance of SyncThreadHolder for a file transfer @@ -36,7 +35,6 @@ public class SyncWrapper { public SyncWrapper(@NonNull final SyncRequest request, @Nullable final Account account, @NonNull final Context context) { this.request = request; remoteOperation = createRemoteOperation(request, account, context); - isRunning = true; } @Nullable @@ -44,14 +42,6 @@ public class SyncWrapper { return remoteOperation; } - public boolean isRunning() { - return isRunning; - } - - public synchronized void setRunning(boolean running) { - isRunning = running; - } - /** * Create the RemoteOperation (to perform file transfer) based on SyncRequest * @param request syncRequest for the file diff --git a/app/src/main/java/foundation/e/drive/synchronization/SyncProxy.kt b/app/src/main/java/foundation/e/drive/synchronization/SyncProxy.kt index 86c8b429..4d7e47b4 100644 --- a/app/src/main/java/foundation/e/drive/synchronization/SyncProxy.kt +++ b/app/src/main/java/foundation/e/drive/synchronization/SyncProxy.kt @@ -67,10 +67,9 @@ object SyncProxy: SyncRequestCollector, SyncManager { * @param context used to check previous failure of file sync */ override fun queueSyncRequest(request: SyncRequest, context: Context) { - for (syncWrapper in startedRequest.values) { - if (syncWrapper.isRunning && syncWrapper == request) { - return - } + if (startedRequest.contains(request.syncedFileState.name)) { + val syncWrapper = startedRequest[request.syncedFileState.name] + if (syncWrapper == request) return } if (skipBecauseOfPreviousFail(request, context)) return @@ -90,10 +89,8 @@ object SyncProxy: SyncRequestCollector, SyncManager { */ override fun queueSyncRequests(requests: MutableCollection, context: Context) { for (syncWrapper in startedRequest.values) { - if (syncWrapper.isRunning) { - requests.removeIf { obj: SyncRequest? -> - syncWrapper == obj - } + requests.removeIf { obj: SyncRequest? -> + syncWrapper == obj } } @@ -228,7 +225,7 @@ object SyncProxy: SyncRequestCollector, SyncManager { } override fun removeStartedRequest(fileName: String) { - if (startedRequest[fileName]?.isRunning == false) { + if (startedRequest.contains(fileName)) { startedRequest.remove(fileName) } } diff --git a/app/src/main/java/foundation/e/drive/synchronization/SyncWorker.kt b/app/src/main/java/foundation/e/drive/synchronization/SyncWorker.kt index 130f9009..b1168833 100644 --- a/app/src/main/java/foundation/e/drive/synchronization/SyncWorker.kt +++ b/app/src/main/java/foundation/e/drive/synchronization/SyncWorker.kt @@ -52,17 +52,8 @@ class SyncWorker( CommonUtils.createNotificationChannel(applicationContext) - val executor = Executors.newFixedThreadPool(threadAmount) - - while (!syncManager.isQueueEmpty()) { - val request = syncManager.pollSyncRequest()?: break - val task = SyncTask(request, ocClient, account, applicationContext) - /*val futur =*/ executor.submit(task) - } - - executor.shutdown() - while (!executor.isTerminated && !executor.isShutdown) { - executor.awaitTermination(30, TimeUnit.SECONDS) + while (!syncManager.isQueueEmpty()) { //new request may have been added during execution of others + executeRequests() } syncManager.startListeningFiles(applicationContext as EdriveApplication) @@ -89,4 +80,18 @@ class SyncWorker( val isMeteredNetworkAllowed = CommonUtils.isMeteredNetworkAllowed(account) return CommonUtils.haveNetworkConnection(applicationContext, isMeteredNetworkAllowed) } + + private fun executeRequests() { + val executor = Executors.newFixedThreadPool(threadAmount) + while (!syncManager.isQueueEmpty()) { + val request = syncManager.pollSyncRequest()?: break + val task = SyncTask(request, ocClient, account, applicationContext) + /*val futur =*/ executor.submit(task) + } + + executor.shutdown() + while (!executor.isTerminated && !executor.isShutdown) { + executor.awaitTermination(30, TimeUnit.SECONDS) + } + } } \ No newline at end of file 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 e57b6603..8bcb28ef 100644 --- a/app/src/test/java/foundation/e/drive/operations/UploadFileOperationTest.java +++ b/app/src/test/java/foundation/e/drive/operations/UploadFileOperationTest.java @@ -41,6 +41,7 @@ import foundation.e.drive.database.DbHelper; import foundation.e.drive.models.SyncedFileState; import foundation.e.drive.models.SyncedFolder; import foundation.e.drive.TestUtils; +import foundation.e.drive.synchronization.tasks.UploadFileOperation; import foundation.e.drive.utils.DavClientProvider; @SuppressWarnings("rawtypes") -- GitLab From 943d8b4c4a2ee347c18ad9c1518d0b4b7d6b2b57 Mon Sep 17 00:00:00 2001 From: vincent Bourgmayer Date: Wed, 8 Nov 2023 10:01:35 +0100 Subject: [PATCH 06/33] improve SyncTask --- .../e/drive/synchronization/SyncTask.kt | 25 +++++++++++-------- 1 file changed, 15 insertions(+), 10 deletions(-) diff --git a/app/src/main/java/foundation/e/drive/synchronization/SyncTask.kt b/app/src/main/java/foundation/e/drive/synchronization/SyncTask.kt index 3d096709..37f7588d 100644 --- a/app/src/main/java/foundation/e/drive/synchronization/SyncTask.kt +++ b/app/src/main/java/foundation/e/drive/synchronization/SyncTask.kt @@ -6,7 +6,6 @@ import com.owncloud.android.lib.common.OwnCloudClient import com.owncloud.android.lib.common.operations.RemoteOperationResult import foundation.e.drive.database.DbHelper import foundation.e.drive.database.FailedSyncPrefsManager -import foundation.e.drive.models.DownloadRequest import foundation.e.drive.models.SyncRequest import foundation.e.drive.models.SyncRequest.Type.* import foundation.e.drive.models.SyncWrapper @@ -16,7 +15,7 @@ import timber.log.Timber import java.io.File class SyncTask(val request: SyncRequest, val client: OwnCloudClient, val account: Account, val context: Context): Runnable { - private val fileName = getFileName() + private val fileName = request.syncedFileState.name companion object { private val syncManager = SyncProxy as SyncManager } @@ -38,16 +37,17 @@ class SyncTask(val request: SyncRequest, val client: OwnCloudClient, val account } private fun checkStartCondition(): Boolean { - if (client == null) { - Timber.d("Can't start thread: OwnCloudClient is null") - return false - } - val isSyncAllowed = CommonUtils.isThisSyncAllowed(account, request.syncedFileState.isMediaType) if (!isSyncAllowed){ Timber.d("Sync of the file is not allowed anymore") return false } + + + if (!isNetworkAvailable()) { + Timber.d("No network available. can't run syncTask") + return false + } return true } @@ -78,7 +78,7 @@ class SyncTask(val request: SyncRequest, val client: OwnCloudClient, val account } private fun runDownload(syncWrapper: SyncWrapper): Boolean { - val result = syncWrapper.remoteOperation?.execute(client) + @Suppress("DEPRECATION") val result = syncWrapper.remoteOperation?.execute(client) if (result == null) { Timber.d("Error: Download result for $fileName is null") return false @@ -105,13 +105,13 @@ class SyncTask(val request: SyncRequest, val client: OwnCloudClient, val account return true } - private fun getFileName(): String { + /*private fun getFileName(): String { return if (request is DownloadRequest ) { request.remoteFile.remotePath //todo why not just use syncedFileStateName ? } else request.syncedFileState.name } - +*/ private fun isNetworkLost(code: RemoteOperationResult.ResultCode): Boolean { return (code == RemoteOperationResult.ResultCode.WRONG_CONNECTION || code == RemoteOperationResult.ResultCode.NO_NETWORK_CONNECTION) @@ -135,4 +135,9 @@ class SyncTask(val request: SyncRequest, val client: OwnCloudClient, val account failedPrefs.removeDataForFile(fileStateId) } + + private fun isNetworkAvailable(): Boolean { + val isMeteredNetworkAllowed = CommonUtils.isMeteredNetworkAllowed(account) + return CommonUtils.haveNetworkConnection(context, isMeteredNetworkAllowed) + } } \ No newline at end of file -- GitLab From 56ecefe3dcbd5bbe71e3ad39f63f6dff9e1636ae Mon Sep 17 00:00:00 2001 From: vincent Bourgmayer Date: Wed, 8 Nov 2023 10:32:10 +0100 Subject: [PATCH 07/33] add code to trigger syncWorker --- .../e/drive/synchronization/SyncProxy.kt | 12 ++++++++++- .../e/drive/synchronization/SyncWorker.kt | 21 +++++++++++++++++-- 2 files changed, 30 insertions(+), 3 deletions(-) diff --git a/app/src/main/java/foundation/e/drive/synchronization/SyncProxy.kt b/app/src/main/java/foundation/e/drive/synchronization/SyncProxy.kt index 4d7e47b4..7efb903f 100644 --- a/app/src/main/java/foundation/e/drive/synchronization/SyncProxy.kt +++ b/app/src/main/java/foundation/e/drive/synchronization/SyncProxy.kt @@ -9,6 +9,12 @@ package foundation.e.drive.synchronization import android.app.Application import android.content.Context +import androidx.work.Constraints +import androidx.work.ExistingWorkPolicy +import androidx.work.OneTimeWorkRequestBuilder +import androidx.work.OutOfQuotaPolicy +import androidx.work.WorkManager +import androidx.work.WorkRequest import foundation.e.drive.EdriveApplication import foundation.e.drive.database.FailedSyncPrefsManager import foundation.e.drive.models.SyncRequest @@ -41,7 +47,7 @@ interface SyncManager { fun getStartedRequestOnThread(fileName: String): SyncWrapper? fun getStartedRequests(): ConcurrentHashMap fun isAnySyncRequestRunning(): Boolean - fun removeStartedRequest(filenName: String) + fun removeStartedRequest(fileName: String) fun startListeningFiles(application: Application) } @@ -132,6 +138,10 @@ object SyncProxy: SyncRequestCollector, SyncManager { if (previousSyncState != SyncState.SYNCHRONIZING) { // todo: start SyncWorker + val workManager = WorkManager.getInstance(context) + val workRequest = OneTimeWorkRequestBuilder() + + workManager.enqueueUniqueWork(SyncWorker.UNIQUE_WORK_NAME, ExistingWorkPolicy.KEEP, workRequest.build() ) } } diff --git a/app/src/main/java/foundation/e/drive/synchronization/SyncWorker.kt b/app/src/main/java/foundation/e/drive/synchronization/SyncWorker.kt index b1168833..2e9a6324 100644 --- a/app/src/main/java/foundation/e/drive/synchronization/SyncWorker.kt +++ b/app/src/main/java/foundation/e/drive/synchronization/SyncWorker.kt @@ -2,7 +2,10 @@ package foundation.e.drive.synchronization import android.accounts.Account import android.accounts.AccountManager +import android.app.Notification import android.content.Context +import androidx.core.app.NotificationCompat +import androidx.work.ForegroundInfo import androidx.work.Worker import androidx.work.WorkerParameters @@ -25,8 +28,9 @@ class SyncWorker( private lateinit var ocClient: OwnCloudClient companion object { - const val threadAmount = 2 - val syncManager = SyncProxy as SyncManager + const val UNIQUE_WORK_NAME = "syncWorker" + private const val threadAmount = 2 + private val syncManager = SyncProxy as SyncManager } override fun doWork(): Result { @@ -51,6 +55,7 @@ class SyncWorker( } CommonUtils.createNotificationChannel(applicationContext) + setForegroundAsync(createForegroundInfo()) while (!syncManager.isQueueEmpty()) { //new request may have been added during execution of others executeRequests() @@ -94,4 +99,16 @@ class SyncWorker( executor.awaitTermination(30, TimeUnit.SECONDS) } } + + private fun createForegroundInfo(): ForegroundInfo { + val notifChannelId="SyncWorkerChannelId" + val title="Synchronizing medias" //todo move to resources + val notificationId=2003004 + val notification = NotificationCompat.Builder(applicationContext, notifChannelId) + .setOngoing(true) + .setContentTitle(title) + .build() + + return ForegroundInfo(notificationId, notification) + } } \ No newline at end of file -- GitLab From f39171b3af17ffbe8ecc1c6df687a2bf3be8c7e8 Mon Sep 17 00:00:00 2001 From: vincent Bourgmayer Date: Wed, 8 Nov 2023 15:53:56 +0100 Subject: [PATCH 08/33] fix errors due to fileEventListener --- app/src/main/AndroidManifest.xml | 6 +++++ .../FileObservers/FileEventListener.java | 10 +++---- .../e/drive/synchronization/SyncProxy.kt | 27 +++++++++---------- .../e/drive/synchronization/SyncTask.kt | 9 +------ .../e/drive/synchronization/SyncWorker.kt | 26 ++++++++---------- 5 files changed, 36 insertions(+), 42 deletions(-) diff --git a/app/src/main/AndroidManifest.xml b/app/src/main/AndroidManifest.xml index 9c3f8b2c..42fe0846 100644 --- a/app/src/main/AndroidManifest.xml +++ b/app/src/main/AndroidManifest.xml @@ -119,5 +119,11 @@ + + + \ No newline at end of file diff --git a/app/src/main/java/foundation/e/drive/FileObservers/FileEventListener.java b/app/src/main/java/foundation/e/drive/FileObservers/FileEventListener.java index aa621e6f..1d5a9c50 100644 --- a/app/src/main/java/foundation/e/drive/FileObservers/FileEventListener.java +++ b/app/src/main/java/foundation/e/drive/FileObservers/FileEventListener.java @@ -105,12 +105,12 @@ public class FileEventListener { * @param request SyncRequest that should be executed asap */ private void sendSyncRequestToSynchronizationService(@NonNull SyncRequest request) { - Timber.d("Sending a SyncRequest for %s", request.getSyncedFileState().getName()); - final SyncRequestCollector syncManager = SyncProxy.INSTANCE; - syncManager.queueSyncRequest(request, appContext.getApplicationContext()); - syncManager.startSynchronization(appContext); - + final boolean requestAdded = syncManager.queueSyncRequest(request, appContext.getApplicationContext()); + if (requestAdded) { + Timber.d("Sending a SyncRequest for %s", request.getSyncedFileState().getName()); + syncManager.startSynchronization(appContext); + } } /** diff --git a/app/src/main/java/foundation/e/drive/synchronization/SyncProxy.kt b/app/src/main/java/foundation/e/drive/synchronization/SyncProxy.kt index 7efb903f..aeaf8551 100644 --- a/app/src/main/java/foundation/e/drive/synchronization/SyncProxy.kt +++ b/app/src/main/java/foundation/e/drive/synchronization/SyncProxy.kt @@ -9,12 +9,9 @@ package foundation.e.drive.synchronization import android.app.Application import android.content.Context -import androidx.work.Constraints import androidx.work.ExistingWorkPolicy import androidx.work.OneTimeWorkRequestBuilder -import androidx.work.OutOfQuotaPolicy import androidx.work.WorkManager -import androidx.work.WorkRequest import foundation.e.drive.EdriveApplication import foundation.e.drive.database.FailedSyncPrefsManager import foundation.e.drive.models.SyncRequest @@ -28,7 +25,7 @@ import java.util.concurrent.ConcurrentLinkedQueue * @author Vincent Bourgmayer */ interface SyncRequestCollector { - fun queueSyncRequest(request: SyncRequest, context: Context) + fun queueSyncRequest(request: SyncRequest, context: Context): Boolean fun queueSyncRequests(requests: MutableCollection, context: Context) fun startSynchronization(context: Context) fun onPeriodicScanStart(application: Application): Boolean @@ -53,7 +50,6 @@ interface SyncManager { /** * This class goals is to act as a proxy between file's change detection & performing synchronization - * todo 3. one improvement could be to handle file that has just been synced in order to prevent instant detection to trigger a syncRequest in reaction (i.e in case of a download) * * This object must allow concurrent access between (periodic | instant) file's change detection and synchronization * it holds the SyncRequest Queue and a list of running sync @@ -72,16 +68,17 @@ object SyncProxy: SyncRequestCollector, SyncManager { * @param request request to add to waiting queue * @param context used to check previous failure of file sync */ - override fun queueSyncRequest(request: SyncRequest, context: Context) { - if (startedRequest.contains(request.syncedFileState.name)) { - val syncWrapper = startedRequest[request.syncedFileState.name] - if (syncWrapper == request) return + override fun queueSyncRequest(request: SyncRequest, context: Context): Boolean { + if (startedRequest.containsKey(request.syncedFileState.name)) { + Timber.d("StartedRequest already contain one entry for ${request.syncedFileState.name}") + return false } - if (skipBecauseOfPreviousFail(request, context)) return + if (skipBecauseOfPreviousFail(request, context)) return false syncRequestQueue.remove(request) syncRequestQueue.add(request) + return true } /** @@ -95,8 +92,8 @@ object SyncProxy: SyncRequestCollector, SyncManager { */ override fun queueSyncRequests(requests: MutableCollection, context: Context) { for (syncWrapper in startedRequest.values) { - requests.removeIf { obj: SyncRequest? -> - syncWrapper == obj + requests.removeIf { obj: SyncRequest -> + startedRequest.containsKey(obj.syncedFileState.name) } } @@ -137,7 +134,6 @@ object SyncProxy: SyncRequestCollector, SyncManager { } if (previousSyncState != SyncState.SYNCHRONIZING) { - // todo: start SyncWorker val workManager = WorkManager.getInstance(context) val workRequest = OneTimeWorkRequestBuilder() @@ -211,7 +207,9 @@ object SyncProxy: SyncRequestCollector, SyncManager { } override fun addStartedRequest(fileName: String, syncWrapper: SyncWrapper) { + startedRequest[fileName] = syncWrapper + Timber.d("Added started Request for $fileName. Now contains: ${startedRequest.size}") } override fun isQueueEmpty(): Boolean { @@ -235,8 +233,9 @@ object SyncProxy: SyncRequestCollector, SyncManager { } override fun removeStartedRequest(fileName: String) { - if (startedRequest.contains(fileName)) { + if (startedRequest.containsKey(fileName)) { startedRequest.remove(fileName) + Timber.v("StartedRequest for $fileName removed. ${startedRequest.size} remaining") } } } \ No newline at end of file diff --git a/app/src/main/java/foundation/e/drive/synchronization/SyncTask.kt b/app/src/main/java/foundation/e/drive/synchronization/SyncTask.kt index 37f7588d..3b67e52e 100644 --- a/app/src/main/java/foundation/e/drive/synchronization/SyncTask.kt +++ b/app/src/main/java/foundation/e/drive/synchronization/SyncTask.kt @@ -34,6 +34,7 @@ class SyncTask(val request: SyncRequest, val client: OwnCloudClient, val account } updateFailureCounter(request, succeed) syncManager.removeStartedRequest(fileName) + Timber.d("${request.operationType.name} finished for $fileName") } private fun checkStartCondition(): Boolean { @@ -105,14 +106,6 @@ class SyncTask(val request: SyncRequest, val client: OwnCloudClient, val account return true } - /*private fun getFileName(): String { - return if (request is DownloadRequest ) { - request.remoteFile.remotePath //todo why not just use syncedFileStateName ? - } else - request.syncedFileState.name - } -*/ - private fun isNetworkLost(code: RemoteOperationResult.ResultCode): Boolean { return (code == RemoteOperationResult.ResultCode.WRONG_CONNECTION || code == RemoteOperationResult.ResultCode.NO_NETWORK_CONNECTION) } diff --git a/app/src/main/java/foundation/e/drive/synchronization/SyncWorker.kt b/app/src/main/java/foundation/e/drive/synchronization/SyncWorker.kt index 2e9a6324..0c7218b2 100644 --- a/app/src/main/java/foundation/e/drive/synchronization/SyncWorker.kt +++ b/app/src/main/java/foundation/e/drive/synchronization/SyncWorker.kt @@ -2,7 +2,6 @@ package foundation.e.drive.synchronization import android.accounts.Account import android.accounts.AccountManager -import android.app.Notification import android.content.Context import androidx.core.app.NotificationCompat import androidx.work.ForegroundInfo @@ -17,6 +16,7 @@ import foundation.e.drive.utils.CommonUtils import foundation.e.drive.utils.DavClientProvider import timber.log.Timber import java.util.concurrent.Executors +import java.util.concurrent.Future import java.util.concurrent.TimeUnit class SyncWorker( @@ -36,6 +36,7 @@ class SyncWorker( override fun doWork(): Result { val tmpAccount = loadAccount() if (tmpAccount == null) { + Timber.d("Warning : account is null") syncManager.startListeningFiles(applicationContext as EdriveApplication) return Result.failure() } @@ -43,17 +44,12 @@ class SyncWorker( val tmpOcClient = getOcClient(account) if (tmpOcClient == null) { + Timber.d("Warning : ocClient is null") syncManager.startListeningFiles(applicationContext as EdriveApplication) return Result.failure() } ocClient = tmpOcClient - if (!isNetworkAvailable(account)) { - Timber.d("No network available: Clear syncRequestQueue") - syncManager.clearRequestQueue() - return Result.failure() - } - CommonUtils.createNotificationChannel(applicationContext) setForegroundAsync(createForegroundInfo()) @@ -81,17 +77,17 @@ class SyncWorker( ) } - private fun isNetworkAvailable(account: Account): Boolean { - val isMeteredNetworkAllowed = CommonUtils.isMeteredNetworkAllowed(account) - return CommonUtils.haveNetworkConnection(applicationContext, isMeteredNetworkAllowed) - } - private fun executeRequests() { + val futureList = arrayListOf>() val executor = Executors.newFixedThreadPool(threadAmount) while (!syncManager.isQueueEmpty()) { val request = syncManager.pollSyncRequest()?: break val task = SyncTask(request, ocClient, account, applicationContext) - /*val futur =*/ executor.submit(task) + futureList.add(executor.submit(task)) + } + + for (future: Future<*> in futureList) { + future.get() } executor.shutdown() @@ -101,12 +97,12 @@ class SyncWorker( } private fun createForegroundInfo(): ForegroundInfo { - val notifChannelId="SyncWorkerChannelId" val title="Synchronizing medias" //todo move to resources val notificationId=2003004 - val notification = NotificationCompat.Builder(applicationContext, notifChannelId) + val notification = NotificationCompat.Builder(applicationContext, AppConstants.notificationChannelID) .setOngoing(true) .setContentTitle(title) + .setSmallIcon(R.drawable.ic_arrow_up) .build() return ForegroundInfo(notificationId, notification) -- GitLab From b2f3f76f16dbbb9858b9bcb3df54abc05edd5e5f Mon Sep 17 00:00:00 2001 From: vincent Bourgmayer Date: Wed, 8 Nov 2023 16:06:24 +0100 Subject: [PATCH 09/33] use file's local path as key for startedRequest instead of filename --- .../e/drive/synchronization/SyncProxy.kt | 36 ++++++++----------- .../e/drive/synchronization/SyncTask.kt | 7 ++-- 2 files changed, 19 insertions(+), 24 deletions(-) diff --git a/app/src/main/java/foundation/e/drive/synchronization/SyncProxy.kt b/app/src/main/java/foundation/e/drive/synchronization/SyncProxy.kt index aeaf8551..00bf7f0e 100644 --- a/app/src/main/java/foundation/e/drive/synchronization/SyncProxy.kt +++ b/app/src/main/java/foundation/e/drive/synchronization/SyncProxy.kt @@ -38,13 +38,12 @@ interface SyncRequestCollector { */ interface SyncManager { fun pollSyncRequest(): SyncRequest? - fun addStartedRequest(fileName: String, syncWrapper: SyncWrapper) + fun addStartedRequest(fileLocalPath: String, syncWrapper: SyncWrapper) fun isQueueEmpty(): Boolean fun clearRequestQueue() - fun getStartedRequestOnThread(fileName: String): SyncWrapper? fun getStartedRequests(): ConcurrentHashMap fun isAnySyncRequestRunning(): Boolean - fun removeStartedRequest(fileName: String) + fun removeStartedRequest(fileLocalPath: String) fun startListeningFiles(application: Application) } @@ -57,7 +56,7 @@ interface SyncManager { */ object SyncProxy: SyncRequestCollector, SyncManager { private val syncRequestQueue: ConcurrentLinkedQueue = ConcurrentLinkedQueue() //could we use channel instead ? - private val startedRequest: ConcurrentHashMap = ConcurrentHashMap() + private val startedRequests: ConcurrentHashMap = ConcurrentHashMap() /** * Add a SyncRequest into waiting queue if it matches some conditions: @@ -69,7 +68,7 @@ object SyncProxy: SyncRequestCollector, SyncManager { * @param context used to check previous failure of file sync */ override fun queueSyncRequest(request: SyncRequest, context: Context): Boolean { - if (startedRequest.containsKey(request.syncedFileState.name)) { + if (startedRequests.containsKey(request.syncedFileState.localPath)) { Timber.d("StartedRequest already contain one entry for ${request.syncedFileState.name}") return false } @@ -91,9 +90,9 @@ object SyncProxy: SyncRequestCollector, SyncManager { * @param context used to check previous failure of file sync */ override fun queueSyncRequests(requests: MutableCollection, context: Context) { - for (syncWrapper in startedRequest.values) { + for (syncWrapper in startedRequests.values) { requests.removeIf { obj: SyncRequest -> - startedRequest.containsKey(obj.syncedFileState.name) + startedRequests.containsKey(obj.syncedFileState.localPath) } } @@ -206,10 +205,9 @@ object SyncProxy: SyncRequestCollector, SyncManager { return syncRequestQueue.poll() } - override fun addStartedRequest(fileName: String, syncWrapper: SyncWrapper) { - - startedRequest[fileName] = syncWrapper - Timber.d("Added started Request for $fileName. Now contains: ${startedRequest.size}") + override fun addStartedRequest(fileLocalPath: String, syncWrapper: SyncWrapper) { + startedRequests[fileLocalPath] = syncWrapper + Timber.d("Added started Request for $fileLocalPath. Now contains: ${startedRequests.size}") } override fun isQueueEmpty(): Boolean { @@ -217,25 +215,21 @@ object SyncProxy: SyncRequestCollector, SyncManager { } override fun getStartedRequests(): ConcurrentHashMap { - return startedRequest + return startedRequests } override fun isAnySyncRequestRunning(): Boolean { - return startedRequest.isNotEmpty() + return startedRequests.isNotEmpty() } override fun clearRequestQueue() { syncRequestQueue.clear() } - override fun getStartedRequestOnThread(fileName: String): SyncWrapper? { - return startedRequest[fileName] - } - - override fun removeStartedRequest(fileName: String) { - if (startedRequest.containsKey(fileName)) { - startedRequest.remove(fileName) - Timber.v("StartedRequest for $fileName removed. ${startedRequest.size} remaining") + override fun removeStartedRequest(fileLocalPath: String) { + if (startedRequests.containsKey(fileLocalPath)) { + startedRequests.remove(fileLocalPath) + Timber.v("StartedRequest for $fileLocalPath removed. ${startedRequests.size} remaining") } } } \ No newline at end of file diff --git a/app/src/main/java/foundation/e/drive/synchronization/SyncTask.kt b/app/src/main/java/foundation/e/drive/synchronization/SyncTask.kt index 3b67e52e..ff8a221c 100644 --- a/app/src/main/java/foundation/e/drive/synchronization/SyncTask.kt +++ b/app/src/main/java/foundation/e/drive/synchronization/SyncTask.kt @@ -24,16 +24,17 @@ class SyncTask(val request: SyncRequest, val client: OwnCloudClient, val account if (!canStart) { return } - val wrapper = SyncWrapper(request, account, context) + val fileLocalPath = request.syncedFileState.localPath Timber.d(" starts ${request.operationType.name} for: $fileName") - syncManager.addStartedRequest(fileName, wrapper) + val wrapper = SyncWrapper(request, account, context) + syncManager.addStartedRequest(fileLocalPath, wrapper) val succeed = when (request.operationType) { UPLOAD -> runUpload(wrapper) DOWNLOAD -> runDownload(wrapper) DISABLE_SYNCING -> runSyncDisabling() } updateFailureCounter(request, succeed) - syncManager.removeStartedRequest(fileName) + syncManager.removeStartedRequest(fileLocalPath) Timber.d("${request.operationType.name} finished for $fileName") } -- GitLab From 7137a35a600d40ff29c4484a013c70343c95875d Mon Sep 17 00:00:00 2001 From: vincent Bourgmayer Date: Thu, 9 Nov 2023 09:53:54 +0100 Subject: [PATCH 10/33] move syncWorker request creation to WorkRequestFactory and remove useless method in SyncProxy --- .../e/drive/synchronization/SyncProxy.kt | 17 ++++------------- .../e/drive/work/WorkRequestFactory.java | 19 ++++++++++++++++++- 2 files changed, 22 insertions(+), 14 deletions(-) diff --git a/app/src/main/java/foundation/e/drive/synchronization/SyncProxy.kt b/app/src/main/java/foundation/e/drive/synchronization/SyncProxy.kt index 00bf7f0e..4055567b 100644 --- a/app/src/main/java/foundation/e/drive/synchronization/SyncProxy.kt +++ b/app/src/main/java/foundation/e/drive/synchronization/SyncProxy.kt @@ -10,12 +10,13 @@ package foundation.e.drive.synchronization import android.app.Application import android.content.Context import androidx.work.ExistingWorkPolicy -import androidx.work.OneTimeWorkRequestBuilder import androidx.work.WorkManager import foundation.e.drive.EdriveApplication import foundation.e.drive.database.FailedSyncPrefsManager import foundation.e.drive.models.SyncRequest import foundation.e.drive.models.SyncWrapper +import foundation.e.drive.work.WorkRequestFactory +import foundation.e.drive.work.WorkRequestFactory.WorkType.ONE_TIME_SYNC import timber.log.Timber import java.util.concurrent.ConcurrentHashMap import java.util.concurrent.ConcurrentLinkedQueue @@ -41,8 +42,6 @@ interface SyncManager { fun addStartedRequest(fileLocalPath: String, syncWrapper: SyncWrapper) fun isQueueEmpty(): Boolean fun clearRequestQueue() - fun getStartedRequests(): ConcurrentHashMap - fun isAnySyncRequestRunning(): Boolean fun removeStartedRequest(fileLocalPath: String) fun startListeningFiles(application: Application) } @@ -134,9 +133,9 @@ object SyncProxy: SyncRequestCollector, SyncManager { if (previousSyncState != SyncState.SYNCHRONIZING) { val workManager = WorkManager.getInstance(context) - val workRequest = OneTimeWorkRequestBuilder() + val workRequest = WorkRequestFactory.getOneTimeWorkRequest(ONE_TIME_SYNC, null) - workManager.enqueueUniqueWork(SyncWorker.UNIQUE_WORK_NAME, ExistingWorkPolicy.KEEP, workRequest.build() ) + workManager.enqueueUniqueWork(SyncWorker.UNIQUE_WORK_NAME, ExistingWorkPolicy.KEEP, workRequest) } } @@ -214,14 +213,6 @@ object SyncProxy: SyncRequestCollector, SyncManager { return syncRequestQueue.isEmpty() } - override fun getStartedRequests(): ConcurrentHashMap { - return startedRequests - } - - override fun isAnySyncRequestRunning(): Boolean { - return startedRequests.isNotEmpty() - } - override fun clearRequestQueue() { syncRequestQueue.clear() } diff --git a/app/src/main/java/foundation/e/drive/work/WorkRequestFactory.java b/app/src/main/java/foundation/e/drive/work/WorkRequestFactory.java index 324b5def..460ed576 100644 --- a/app/src/main/java/foundation/e/drive/work/WorkRequestFactory.java +++ b/app/src/main/java/foundation/e/drive/work/WorkRequestFactory.java @@ -40,6 +40,7 @@ import foundation.e.drive.models.SyncedFolder; import foundation.e.drive.periodicScan.FullScanWorker; import foundation.e.drive.periodicScan.ListAppsWorker; import foundation.e.drive.periodicScan.PeriodicScanWorker; +import foundation.e.drive.synchronization.SyncWorker; public class WorkRequestFactory { public enum WorkType { @@ -50,7 +51,8 @@ public class WorkRequestFactory { ONE_TIME_APP_LIST, ONE_TIME_USER_INFO, ONE_TIME_ROOT_FOLDER_SETUP, - ONE_TIME_FINISH_SETUP + ONE_TIME_FINISH_SETUP, + ONE_TIME_SYNC } private final static int PERIODIC_WORK_REPEAT_INTERVAL = 30; @@ -135,11 +137,26 @@ public class WorkRequestFactory { case ONE_TIME_ROOT_FOLDER_SETUP: if (syncedFolder == null) throw new NullPointerException("Synced folder is null"); return createRootFolderSetupWorkRequest(syncedFolder); + case ONE_TIME_SYNC: + return createSyncWorkRequest(); default: throw new InvalidParameterException("Unsupported Work Type: " + type); } } + private static OneTimeWorkRequest createSyncWorkRequest() { + final Constraints constraints = new Constraints.Builder() + .setRequiredNetworkType(NetworkType.CONNECTED) + .setRequiresBatteryNotLow(true) + .build(); + + final OneTimeWorkRequest.Builder builder = new OneTimeWorkRequest.Builder(SyncWorker.class); + + return builder.setBackoffCriteria(LINEAR, 2, MINUTES).addTag(WORK_GENERIC_TAG) + .setConstraints(constraints) + .build(); + } + /** * Create a workRequest to generate file which contains list of installed apps * @return the workRequest -- GitLab From fdeb4a1f8267a2d09fea4478e8fa58b2979c7882 Mon Sep 17 00:00:00 2001 From: vincent Bourgmayer Date: Thu, 9 Nov 2023 10:27:47 +0100 Subject: [PATCH 11/33] Create dedicated notification Channel for SyncWorker with min importance --- .../foundation/e/drive/EdriveApplication.java | 2 +- .../AccountRemoveCallbackReceiver.java | 11 +++++- .../e/drive/models/SyncedFileState.kt | 1 - .../e/drive/synchronization/SyncProxy.kt | 5 +++ .../e/drive/synchronization/SyncTask.kt | 7 ++++ .../e/drive/synchronization/SyncWorker.kt | 36 ++++++++++++++++--- .../foundation/e/drive/utils/CommonUtils.java | 2 +- app/src/main/res/values/strings.xml | 5 ++- 8 files changed, 59 insertions(+), 10 deletions(-) diff --git a/app/src/main/java/foundation/e/drive/EdriveApplication.java b/app/src/main/java/foundation/e/drive/EdriveApplication.java index fe9b303f..edf9a26f 100644 --- a/app/src/main/java/foundation/e/drive/EdriveApplication.java +++ b/app/src/main/java/foundation/e/drive/EdriveApplication.java @@ -43,7 +43,7 @@ public class EdriveApplication extends Application { setupLogging(); instantiateFileEventListener(); - CommonUtils.createNotificationChannel(getApplicationContext()); + CommonUtils.createQuotaNotificationChannel(getApplicationContext()); final SharedPreferences prefs = getSharedPreferences(AppConstants.SHARED_PREFERENCE_NAME, Context.MODE_PRIVATE); diff --git a/app/src/main/java/foundation/e/drive/account/receivers/AccountRemoveCallbackReceiver.java b/app/src/main/java/foundation/e/drive/account/receivers/AccountRemoveCallbackReceiver.java index 01ee8c2d..19a8e33c 100644 --- a/app/src/main/java/foundation/e/drive/account/receivers/AccountRemoveCallbackReceiver.java +++ b/app/src/main/java/foundation/e/drive/account/receivers/AccountRemoveCallbackReceiver.java @@ -13,6 +13,7 @@ import static foundation.e.drive.utils.AppConstants.SETUP_COMPLETED; import android.accounts.AccountManager; import android.annotation.SuppressLint; +import android.app.NotificationManager; import android.content.BroadcastReceiver; import android.content.Context; import android.content.Intent; @@ -26,6 +27,7 @@ import java.io.File; import foundation.e.drive.EdriveApplication; import foundation.e.drive.database.DbHelper; import foundation.e.drive.database.FailedSyncPrefsManager; +import foundation.e.drive.synchronization.SyncWorker; import foundation.e.drive.utils.AppConstants; import foundation.e.drive.utils.DavClientProvider; import foundation.e.drive.utils.ViewUtils; @@ -50,7 +52,7 @@ public class AccountRemoveCallbackReceiver extends BroadcastReceiver { deleteDatabase(applicationContext); cleanSharedPreferences(applicationContext, preferences); removeCachedFiles(applicationContext); - + deleteNotificationChannel(applicationContext); DavClientProvider.getInstance().cleanUp(); ViewUtils.updateWidgetView(applicationContext); @@ -135,4 +137,11 @@ public class AccountRemoveCallbackReceiver extends BroadcastReceiver { return dir.delete(); } + + private void deleteNotificationChannel(Context context) { + NotificationManager notificatioManager = (NotificationManager) context.getSystemService(Context.NOTIFICATION_SERVICE); + + notificatioManager.deleteNotificationChannel(AppConstants.notificationChannelID); + notificatioManager.deleteNotificationChannel(SyncWorker.NOTIF_CHANNEL_ID); + } } diff --git a/app/src/main/java/foundation/e/drive/models/SyncedFileState.kt b/app/src/main/java/foundation/e/drive/models/SyncedFileState.kt index e43e7cc5..e8b36cb0 100644 --- a/app/src/main/java/foundation/e/drive/models/SyncedFileState.kt +++ b/app/src/main/java/foundation/e/drive/models/SyncedFileState.kt @@ -10,7 +10,6 @@ package foundation.e.drive.models import android.os.Parcel import android.os.Parcelable - const val DO_NOT_SCAN = 0 const val SCAN_ON_CLOUD = 1 const val SCAN_ON_DEVICE = 2 diff --git a/app/src/main/java/foundation/e/drive/synchronization/SyncProxy.kt b/app/src/main/java/foundation/e/drive/synchronization/SyncProxy.kt index 4055567b..137aafc7 100644 --- a/app/src/main/java/foundation/e/drive/synchronization/SyncProxy.kt +++ b/app/src/main/java/foundation/e/drive/synchronization/SyncProxy.kt @@ -41,6 +41,7 @@ interface SyncManager { fun pollSyncRequest(): SyncRequest? fun addStartedRequest(fileLocalPath: String, syncWrapper: SyncWrapper) fun isQueueEmpty(): Boolean + fun getQueueSize(): Int fun clearRequestQueue() fun removeStartedRequest(fileLocalPath: String) fun startListeningFiles(application: Application) @@ -213,6 +214,10 @@ object SyncProxy: SyncRequestCollector, SyncManager { return syncRequestQueue.isEmpty() } + override fun getQueueSize(): Int { + return syncRequestQueue.size + } + override fun clearRequestQueue() { syncRequestQueue.clear() } diff --git a/app/src/main/java/foundation/e/drive/synchronization/SyncTask.kt b/app/src/main/java/foundation/e/drive/synchronization/SyncTask.kt index ff8a221c..f3d088e4 100644 --- a/app/src/main/java/foundation/e/drive/synchronization/SyncTask.kt +++ b/app/src/main/java/foundation/e/drive/synchronization/SyncTask.kt @@ -1,3 +1,10 @@ +/* + * Copyright © MURENA SAS 2023. + * All rights reserved. This program and the accompanying materials + * are made available under the terms of the GNU Public License v3.0 + * which accompanies this distribution, and is available at + * http://www.gnu.org/licenses/gpl.html + */ package foundation.e.drive.synchronization import android.accounts.Account diff --git a/app/src/main/java/foundation/e/drive/synchronization/SyncWorker.kt b/app/src/main/java/foundation/e/drive/synchronization/SyncWorker.kt index 0c7218b2..d293cb13 100644 --- a/app/src/main/java/foundation/e/drive/synchronization/SyncWorker.kt +++ b/app/src/main/java/foundation/e/drive/synchronization/SyncWorker.kt @@ -1,8 +1,18 @@ +/* + * Copyright © MURENA SAS 2023. + * All rights reserved. This program and the accompanying materials + * are made available under the terms of the GNU Public License v3.0 + * which accompanies this distribution, and is available at + * http://www.gnu.org/licenses/gpl.html + */ package foundation.e.drive.synchronization import android.accounts.Account import android.accounts.AccountManager +import android.app.NotificationChannel +import android.app.NotificationManager import android.content.Context +import android.content.Context.NOTIFICATION_SERVICE import androidx.core.app.NotificationCompat import androidx.work.ForegroundInfo @@ -29,6 +39,7 @@ class SyncWorker( companion object { const val UNIQUE_WORK_NAME = "syncWorker" + const val NOTIF_CHANNEL_ID = "syncChannelId" private const val threadAmount = 2 private val syncManager = SyncProxy as SyncManager } @@ -50,10 +61,11 @@ class SyncWorker( } ocClient = tmpOcClient - CommonUtils.createNotificationChannel(applicationContext) - setForegroundAsync(createForegroundInfo()) + createNotificationChannel() + while (!syncManager.isQueueEmpty()) { //new request may have been added during execution of others + setForegroundAsync(createForegroundInfo()) executeRequests() } @@ -97,14 +109,28 @@ class SyncWorker( } private fun createForegroundInfo(): ForegroundInfo { - val title="Synchronizing medias" //todo move to resources - val notificationId=2003004 - val notification = NotificationCompat.Builder(applicationContext, AppConstants.notificationChannelID) + val title = applicationContext.getString(R.string.notif_sync_is_running_title) + val text = applicationContext.getString(R.string.notif_sync_is_running_txt, syncManager.getQueueSize()) + + val notificationId = 2003004 + val notification = NotificationCompat.Builder(applicationContext, NOTIF_CHANNEL_ID) .setOngoing(true) .setContentTitle(title) + .setContentText(text) .setSmallIcon(R.drawable.ic_arrow_up) .build() return ForegroundInfo(notificationId, notification) } + + private fun createNotificationChannel() { + val channelName = applicationContext.getString(R.string.notif_sync_channel_name) + val importance = NotificationManager.IMPORTANCE_MIN + val channel = NotificationChannel(NOTIF_CHANNEL_ID, channelName, importance) + val channelDescription = applicationContext.getString(R.string.notif_sync_channel_description) + channel.description = channelDescription + + val notificationManager = applicationContext.getSystemService(NOTIFICATION_SERVICE) as NotificationManager + notificationManager.createNotificationChannel(channel) + } } \ No newline at end of file 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 a9fd0c00..e0aa1902 100644 --- a/app/src/main/java/foundation/e/drive/utils/CommonUtils.java +++ b/app/src/main/java/foundation/e/drive/utils/CommonUtils.java @@ -196,7 +196,7 @@ public abstract class CommonUtils { return String.format(Locale.ENGLISH, "%.1f %cB", value / 1024.0, ci.current()); } - public static void createNotificationChannel(@NonNull Context context) { + public static void createQuotaNotificationChannel(@NonNull Context context) { final CharSequence name = context.getString(R.string.notif_channel_name); final String description = context.getString(R.string.notif_channel_description); int importance = NotificationManager.IMPORTANCE_DEFAULT; diff --git a/app/src/main/res/values/strings.xml b/app/src/main/res/values/strings.xml index 4e8c1cc1..872d8255 100644 --- a/app/src/main/res/values/strings.xml +++ b/app/src/main/res/values/strings.xml @@ -31,7 +31,10 @@ 99% of your allotted cloud storage is used. Please take action. You\'ve filled your allotted cloud storage up to 90%. You\'ve filled your allotted cloud storage up to 80%. - + eDrive\'s is synchronizing content + %1$d file(s) to sync + Sync worker\'s channel + Notification about content being synchronized Copied %1$s to clipboard Settings Additional settings for account -- GitLab From 38f9f4181202f3d8d56c4af923bcf33f899f2714 Mon Sep 17 00:00:00 2001 From: vincent Bourgmayer Date: Thu, 9 Nov 2023 15:28:23 +0100 Subject: [PATCH 12/33] update log --- .../java/foundation/e/drive/synchronization/SyncProxy.kt | 4 +--- .../main/java/foundation/e/drive/synchronization/SyncTask.kt | 5 +++-- 2 files changed, 4 insertions(+), 5 deletions(-) diff --git a/app/src/main/java/foundation/e/drive/synchronization/SyncProxy.kt b/app/src/main/java/foundation/e/drive/synchronization/SyncProxy.kt index 137aafc7..ef829bda 100644 --- a/app/src/main/java/foundation/e/drive/synchronization/SyncProxy.kt +++ b/app/src/main/java/foundation/e/drive/synchronization/SyncProxy.kt @@ -69,7 +69,7 @@ object SyncProxy: SyncRequestCollector, SyncManager { */ override fun queueSyncRequest(request: SyncRequest, context: Context): Boolean { if (startedRequests.containsKey(request.syncedFileState.localPath)) { - Timber.d("StartedRequest already contain one entry for ${request.syncedFileState.name}") + Timber.d("A request is already performing for ${request.syncedFileState.name}") return false } @@ -207,7 +207,6 @@ object SyncProxy: SyncRequestCollector, SyncManager { override fun addStartedRequest(fileLocalPath: String, syncWrapper: SyncWrapper) { startedRequests[fileLocalPath] = syncWrapper - Timber.d("Added started Request for $fileLocalPath. Now contains: ${startedRequests.size}") } override fun isQueueEmpty(): Boolean { @@ -225,7 +224,6 @@ object SyncProxy: SyncRequestCollector, SyncManager { override fun removeStartedRequest(fileLocalPath: String) { if (startedRequests.containsKey(fileLocalPath)) { startedRequests.remove(fileLocalPath) - Timber.v("StartedRequest for $fileLocalPath removed. ${startedRequests.size} remaining") } } } \ No newline at end of file diff --git a/app/src/main/java/foundation/e/drive/synchronization/SyncTask.kt b/app/src/main/java/foundation/e/drive/synchronization/SyncTask.kt index f3d088e4..e98cad8a 100644 --- a/app/src/main/java/foundation/e/drive/synchronization/SyncTask.kt +++ b/app/src/main/java/foundation/e/drive/synchronization/SyncTask.kt @@ -23,6 +23,7 @@ import java.io.File class SyncTask(val request: SyncRequest, val client: OwnCloudClient, val account: Account, val context: Context): Runnable { private val fileName = request.syncedFileState.name + private val filePath = request.syncedFileState.localPath companion object { private val syncManager = SyncProxy as SyncManager } @@ -32,7 +33,7 @@ class SyncTask(val request: SyncRequest, val client: OwnCloudClient, val account return } val fileLocalPath = request.syncedFileState.localPath - Timber.d(" starts ${request.operationType.name} for: $fileName") + Timber.d(" starts ${request.operationType.name} for: $filePath") val wrapper = SyncWrapper(request, account, context) syncManager.addStartedRequest(fileLocalPath, wrapper) val succeed = when (request.operationType) { @@ -42,7 +43,7 @@ class SyncTask(val request: SyncRequest, val client: OwnCloudClient, val account } updateFailureCounter(request, succeed) syncManager.removeStartedRequest(fileLocalPath) - Timber.d("${request.operationType.name} finished for $fileName") + Timber.d("${request.operationType.name} finished for $filePath") } private fun checkStartCondition(): Boolean { -- GitLab From 7b7ed2e9e1ad422543452a31a86bc70a3fdd8188 Mon Sep 17 00:00:00 2001 From: vincent Bourgmayer Date: Thu, 9 Nov 2023 15:35:32 +0100 Subject: [PATCH 13/33] update strings.xml to fix linter --- app/src/main/res/values/strings.xml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/app/src/main/res/values/strings.xml b/app/src/main/res/values/strings.xml index 872d8255..ec174ecc 100644 --- a/app/src/main/res/values/strings.xml +++ b/app/src/main/res/values/strings.xml @@ -32,7 +32,7 @@ You\'ve filled your allotted cloud storage up to 90%. You\'ve filled your allotted cloud storage up to 80%. eDrive\'s is synchronizing content - %1$d file(s) to sync + %1$d files to sync Sync worker\'s channel Notification about content being synchronized Copied %1$s to clipboard -- GitLab From 2287513a3a8f8fc246df6f3a0279afb1d4b455b5 Mon Sep 17 00:00:00 2001 From: vincent Bourgmayer Date: Thu, 9 Nov 2023 17:04:15 +0100 Subject: [PATCH 14/33] clean code & fix some error Cutting network trigger HOST_NOT_AVAILABLE instead of NO_NETWORK_CONNECTION or WRONG_CONNECTION result code. Also remove calling StartListeningFiles when fullScannWorker is not allowed to start because it is not needed and may break state machine rules --- app/src/main/AndroidManifest.xml | 4 +--- .../java/foundation/e/drive/periodicScan/FullScanWorker.kt | 1 - .../main/java/foundation/e/drive/synchronization/SyncTask.kt | 4 +++- .../e/drive/synchronization/tasks/DownloadFileOperation.java | 5 ++++- .../e/drive/synchronization/tasks/UploadFileOperation.java | 1 + 5 files changed, 9 insertions(+), 6 deletions(-) diff --git a/app/src/main/AndroidManifest.xml b/app/src/main/AndroidManifest.xml index 42fe0846..eaf8aaa9 100644 --- a/app/src/main/AndroidManifest.xml +++ b/app/src/main/AndroidManifest.xml @@ -120,10 +120,8 @@ - - \ No newline at end of file diff --git a/app/src/main/java/foundation/e/drive/periodicScan/FullScanWorker.kt b/app/src/main/java/foundation/e/drive/periodicScan/FullScanWorker.kt index 0a4776ae..b5ae98f0 100644 --- a/app/src/main/java/foundation/e/drive/periodicScan/FullScanWorker.kt +++ b/app/src/main/java/foundation/e/drive/periodicScan/FullScanWorker.kt @@ -56,7 +56,6 @@ class FullScanWorker(private val context: Context, private val workerParams: Wor val startAllowed = checkStartConditions(account, prefs, requestCollector) if (!startAllowed) { Timber.d("Start Periodic Scan is not allowed") - requestCollector.startListeningFiles(applicationContext as Application) return Result.failure() } diff --git a/app/src/main/java/foundation/e/drive/synchronization/SyncTask.kt b/app/src/main/java/foundation/e/drive/synchronization/SyncTask.kt index e98cad8a..e051526e 100644 --- a/app/src/main/java/foundation/e/drive/synchronization/SyncTask.kt +++ b/app/src/main/java/foundation/e/drive/synchronization/SyncTask.kt @@ -116,7 +116,9 @@ class SyncTask(val request: SyncRequest, val client: OwnCloudClient, val account } private fun isNetworkLost(code: RemoteOperationResult.ResultCode): Boolean { - return (code == RemoteOperationResult.ResultCode.WRONG_CONNECTION || code == RemoteOperationResult.ResultCode.NO_NETWORK_CONNECTION) + return (code == RemoteOperationResult.ResultCode.WRONG_CONNECTION + || code == RemoteOperationResult.ResultCode.NO_NETWORK_CONNECTION + || code == RemoteOperationResult.ResultCode.HOST_NOT_AVAILABLE) } private fun updateFailureCounter(request: SyncRequest, success: Boolean) { diff --git a/app/src/main/java/foundation/e/drive/synchronization/tasks/DownloadFileOperation.java b/app/src/main/java/foundation/e/drive/synchronization/tasks/DownloadFileOperation.java index 4f6b4573..4874e460 100644 --- a/app/src/main/java/foundation/e/drive/synchronization/tasks/DownloadFileOperation.java +++ b/app/src/main/java/foundation/e/drive/synchronization/tasks/DownloadFileOperation.java @@ -11,6 +11,7 @@ package foundation.e.drive.synchronization.tasks; import static com.owncloud.android.lib.common.operations.RemoteOperationResult.ResultCode.FILE_NOT_FOUND; import static com.owncloud.android.lib.common.operations.RemoteOperationResult.ResultCode.FORBIDDEN; +import static com.owncloud.android.lib.common.operations.RemoteOperationResult.ResultCode.HOST_NOT_AVAILABLE; import static com.owncloud.android.lib.common.operations.RemoteOperationResult.ResultCode.INVALID_OVERWRITE; import static com.owncloud.android.lib.common.operations.RemoteOperationResult.ResultCode.NO_NETWORK_CONNECTION; import static com.owncloud.android.lib.common.operations.RemoteOperationResult.ResultCode.OK; @@ -129,7 +130,9 @@ public class DownloadFileOperation extends RemoteOperation { private boolean isNetworkDisconnected(@NonNull final RemoteOperationResult result) { RemoteOperationResult.ResultCode resultCode = result.getCode(); - return resultCode == NO_NETWORK_CONNECTION || resultCode == WRONG_CONNECTION; + return resultCode == NO_NETWORK_CONNECTION + || resultCode == WRONG_CONNECTION + || resultCode == HOST_NOT_AVAILABLE; } private RemoteOperationResult.ResultCode onDownloadSuccess(String tmpTargetFolderPath) { diff --git a/app/src/main/java/foundation/e/drive/synchronization/tasks/UploadFileOperation.java b/app/src/main/java/foundation/e/drive/synchronization/tasks/UploadFileOperation.java index ca723c10..abed3f95 100644 --- a/app/src/main/java/foundation/e/drive/synchronization/tasks/UploadFileOperation.java +++ b/app/src/main/java/foundation/e/drive/synchronization/tasks/UploadFileOperation.java @@ -131,6 +131,7 @@ public class UploadFileOperation extends RemoteOperation { handledResultCodes.add(ResultCode.QUOTA_EXCEEDED); handledResultCodes.add(ResultCode.WRONG_CONNECTION); handledResultCodes.add(ResultCode.NO_NETWORK_CONNECTION); + handledResultCodes.add(ResultCode.HOST_NOT_AVAILABLE); return handledResultCodes; } -- GitLab From e05c014bfd6b8f12ca7b10fbca609381ac9672dc Mon Sep 17 00:00:00 2001 From: vincent Bourgmayer Date: Thu, 9 Nov 2023 17:12:26 +0100 Subject: [PATCH 15/33] fix linter --- .../foundation/e/drive/synchronization/SyncTask.kt | 11 +++++++---- .../foundation/e/drive/synchronization/SyncWorker.kt | 2 +- .../synchronization/tasks/UploadFileOperation.java | 3 ++- app/src/main/res/values/strings.xml | 7 +++++-- 4 files changed, 15 insertions(+), 8 deletions(-) diff --git a/app/src/main/java/foundation/e/drive/synchronization/SyncTask.kt b/app/src/main/java/foundation/e/drive/synchronization/SyncTask.kt index e051526e..1a5315cf 100644 --- a/app/src/main/java/foundation/e/drive/synchronization/SyncTask.kt +++ b/app/src/main/java/foundation/e/drive/synchronization/SyncTask.kt @@ -11,6 +11,9 @@ import android.accounts.Account import android.content.Context import com.owncloud.android.lib.common.OwnCloudClient import com.owncloud.android.lib.common.operations.RemoteOperationResult +import com.owncloud.android.lib.common.operations.RemoteOperationResult.ResultCode.HOST_NOT_AVAILABLE +import com.owncloud.android.lib.common.operations.RemoteOperationResult.ResultCode.NO_NETWORK_CONNECTION +import com.owncloud.android.lib.common.operations.RemoteOperationResult.ResultCode.WRONG_CONNECTION import foundation.e.drive.database.DbHelper import foundation.e.drive.database.FailedSyncPrefsManager import foundation.e.drive.models.SyncRequest @@ -63,7 +66,7 @@ class SyncTask(val request: SyncRequest, val client: OwnCloudClient, val account private fun runUpload(syncWrapper: SyncWrapper): Boolean { - val result = syncWrapper.remoteOperation?.execute(client) + @Suppress("DEPRECATION") val result = syncWrapper.remoteOperation?.execute(client) if (result == null) { Timber.d("Error: Upload result for $fileName is null") return false @@ -116,9 +119,9 @@ class SyncTask(val request: SyncRequest, val client: OwnCloudClient, val account } private fun isNetworkLost(code: RemoteOperationResult.ResultCode): Boolean { - return (code == RemoteOperationResult.ResultCode.WRONG_CONNECTION - || code == RemoteOperationResult.ResultCode.NO_NETWORK_CONNECTION - || code == RemoteOperationResult.ResultCode.HOST_NOT_AVAILABLE) + return (code == WRONG_CONNECTION + || code == NO_NETWORK_CONNECTION + || code == HOST_NOT_AVAILABLE) } private fun updateFailureCounter(request: SyncRequest, success: Boolean) { diff --git a/app/src/main/java/foundation/e/drive/synchronization/SyncWorker.kt b/app/src/main/java/foundation/e/drive/synchronization/SyncWorker.kt index d293cb13..968b44f9 100644 --- a/app/src/main/java/foundation/e/drive/synchronization/SyncWorker.kt +++ b/app/src/main/java/foundation/e/drive/synchronization/SyncWorker.kt @@ -110,7 +110,7 @@ class SyncWorker( private fun createForegroundInfo(): ForegroundInfo { val title = applicationContext.getString(R.string.notif_sync_is_running_title) - val text = applicationContext.getString(R.string.notif_sync_is_running_txt, syncManager.getQueueSize()) + val text = applicationContext.resources.getQuantityString(R.plurals.notif_sync_is_running_txt, syncManager.getQueueSize()) val notificationId = 2003004 val notification = NotificationCompat.Builder(applicationContext, NOTIF_CHANNEL_ID) diff --git a/app/src/main/java/foundation/e/drive/synchronization/tasks/UploadFileOperation.java b/app/src/main/java/foundation/e/drive/synchronization/tasks/UploadFileOperation.java index abed3f95..40f283ce 100644 --- a/app/src/main/java/foundation/e/drive/synchronization/tasks/UploadFileOperation.java +++ b/app/src/main/java/foundation/e/drive/synchronization/tasks/UploadFileOperation.java @@ -214,7 +214,8 @@ public class UploadFileOperation extends RemoteOperation { public RemoteOperationResult checkAvailableSpace(@NonNull NextcloudClient client, long fileSize) { final RemoteOperationResult ocsResult = readUserInfo(client); final ResultCode resultCode; - final Quota quotas = ocsResult.getResultData().getQuota(); + //todo check ocsResult succeed first + final Quota quotas = ocsResult.getResultData().getQuota(); //todo trigger exception if request failed if (ocsResult.isSuccess() && quotas != null && quotas.getFree() < fileSize) { resultCode = ResultCode.QUOTA_EXCEEDED; diff --git a/app/src/main/res/values/strings.xml b/app/src/main/res/values/strings.xml index ec174ecc..5d6fa799 100644 --- a/app/src/main/res/values/strings.xml +++ b/app/src/main/res/values/strings.xml @@ -31,8 +31,11 @@ 99% of your allotted cloud storage is used. Please take action. You\'ve filled your allotted cloud storage up to 90%. You\'ve filled your allotted cloud storage up to 80%. - eDrive\'s is synchronizing content - %1$d files to sync + eDrive is synchronizing content + + %d file to sync + %d files to sync + Sync worker\'s channel Notification about content being synchronized Copied %1$s to clipboard -- GitLab From a175cf2b2c9e8ce09de0f48d619bfb9894869f11 Mon Sep 17 00:00:00 2001 From: vincent Bourgmayer Date: Fri, 10 Nov 2023 10:05:27 +0100 Subject: [PATCH 16/33] fix crash caused by UploadFileOperation --- .../tasks/UploadFileOperation.java | 17 +++++++++-------- 1 file changed, 9 insertions(+), 8 deletions(-) diff --git a/app/src/main/java/foundation/e/drive/synchronization/tasks/UploadFileOperation.java b/app/src/main/java/foundation/e/drive/synchronization/tasks/UploadFileOperation.java index 40f283ce..1d920a6c 100644 --- a/app/src/main/java/foundation/e/drive/synchronization/tasks/UploadFileOperation.java +++ b/app/src/main/java/foundation/e/drive/synchronization/tasks/UploadFileOperation.java @@ -213,16 +213,17 @@ public class UploadFileOperation extends RemoteOperation { @NonNull public RemoteOperationResult checkAvailableSpace(@NonNull NextcloudClient client, long fileSize) { final RemoteOperationResult ocsResult = readUserInfo(client); - final ResultCode resultCode; - //todo check ocsResult succeed first - final Quota quotas = ocsResult.getResultData().getQuota(); //todo trigger exception if request failed + if (!ocsResult.isSuccess()) { + return ocsResult; + } - if (ocsResult.isSuccess() && quotas != null && quotas.getFree() < fileSize) { - resultCode = ResultCode.QUOTA_EXCEEDED; - } else { - resultCode = ocsResult.getCode(); + final Quota quotas = ocsResult.getResultData().getQuota(); + + if (quotas != null && quotas.getFree() < fileSize) { + return new RemoteOperationResult(ResultCode.QUOTA_EXCEEDED); } - return new RemoteOperationResult(resultCode); + + return ocsResult; } /** -- GitLab From 15efda54472fbb5845b184d402fbbe87f9c4ccd1 Mon Sep 17 00:00:00 2001 From: vincent Bourgmayer Date: Fri, 10 Nov 2023 10:23:22 +0100 Subject: [PATCH 17/33] remove useless & unused change from the MR for easier review --- app/build.gradle | 3 --- app/src/main/java/foundation/e/drive/EdriveApplication.java | 2 +- app/src/main/java/foundation/e/drive/models/SyncedFileState.kt | 1 + app/src/main/java/foundation/e/drive/utils/CommonUtils.java | 2 +- 4 files changed, 3 insertions(+), 5 deletions(-) diff --git a/app/build.gradle b/app/build.gradle index ec01d29c..2073fe13 100644 --- a/app/build.gradle +++ b/app/build.gradle @@ -105,11 +105,8 @@ dependencies { implementation 'com.github.bumptech.glide:glide:4.15.1' implementation 'com.github.bumptech.glide:annotations:4.15.1' implementation 'androidx.core:core-ktx:1.10.1' - implementation 'androidx.work:work-runtime-ktx:2.8.1' annotationProcessor 'com.github.bumptech.glide:compiler:4.15.1' implementation "androidx.work:work-runtime:2.8.1" - - // Kotlin implementation 'androidx.test:core:1.5.0' implementation 'com.jakewharton.timber:timber:5.0.1' implementation 'foundation.e:elib:0.0.1-alpha11' diff --git a/app/src/main/java/foundation/e/drive/EdriveApplication.java b/app/src/main/java/foundation/e/drive/EdriveApplication.java index edf9a26f..fe9b303f 100644 --- a/app/src/main/java/foundation/e/drive/EdriveApplication.java +++ b/app/src/main/java/foundation/e/drive/EdriveApplication.java @@ -43,7 +43,7 @@ public class EdriveApplication extends Application { setupLogging(); instantiateFileEventListener(); - CommonUtils.createQuotaNotificationChannel(getApplicationContext()); + CommonUtils.createNotificationChannel(getApplicationContext()); final SharedPreferences prefs = getSharedPreferences(AppConstants.SHARED_PREFERENCE_NAME, Context.MODE_PRIVATE); diff --git a/app/src/main/java/foundation/e/drive/models/SyncedFileState.kt b/app/src/main/java/foundation/e/drive/models/SyncedFileState.kt index e8b36cb0..e43e7cc5 100644 --- a/app/src/main/java/foundation/e/drive/models/SyncedFileState.kt +++ b/app/src/main/java/foundation/e/drive/models/SyncedFileState.kt @@ -10,6 +10,7 @@ package foundation.e.drive.models import android.os.Parcel import android.os.Parcelable + const val DO_NOT_SCAN = 0 const val SCAN_ON_CLOUD = 1 const val SCAN_ON_DEVICE = 2 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 e0aa1902..a9fd0c00 100644 --- a/app/src/main/java/foundation/e/drive/utils/CommonUtils.java +++ b/app/src/main/java/foundation/e/drive/utils/CommonUtils.java @@ -196,7 +196,7 @@ public abstract class CommonUtils { return String.format(Locale.ENGLISH, "%.1f %cB", value / 1024.0, ci.current()); } - public static void createQuotaNotificationChannel(@NonNull Context context) { + public static void createNotificationChannel(@NonNull Context context) { final CharSequence name = context.getString(R.string.notif_channel_name); final String description = context.getString(R.string.notif_channel_description); int importance = NotificationManager.IMPORTANCE_DEFAULT; -- GitLab From dc50a26a859e04cc32666d7cf7e7331d31b9081b Mon Sep 17 00:00:00 2001 From: vincent Bourgmayer Date: Fri, 10 Nov 2023 11:37:48 +0100 Subject: [PATCH 18/33] fix Foreground Service's type in Manifest --- 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 eaf8aaa9..ac42e944 100644 --- a/app/src/main/AndroidManifest.xml +++ b/app/src/main/AndroidManifest.xml @@ -121,7 +121,7 @@ \ No newline at end of file -- GitLab From f41c0c33fcb743303dabddab83a1f738bb663dd3 Mon Sep 17 00:00:00 2001 From: vincent Bourgmayer Date: Fri, 10 Nov 2023 11:38:46 +0100 Subject: [PATCH 19/33] fix error in plurals declaration (default.strings.xml) add add a try catch when calling get() on future of SyncTask() to prevent CancelException() and unknown others --- .../AccountRemoveCallbackReceiver.java | 7 ++++--- .../e/drive/synchronization/SyncWorker.kt | 18 ++++++++++++------ app/src/main/res/values/strings.xml | 2 +- 3 files changed, 17 insertions(+), 10 deletions(-) diff --git a/app/src/main/java/foundation/e/drive/account/receivers/AccountRemoveCallbackReceiver.java b/app/src/main/java/foundation/e/drive/account/receivers/AccountRemoveCallbackReceiver.java index 19a8e33c..40e49ba8 100644 --- a/app/src/main/java/foundation/e/drive/account/receivers/AccountRemoveCallbackReceiver.java +++ b/app/src/main/java/foundation/e/drive/account/receivers/AccountRemoveCallbackReceiver.java @@ -139,9 +139,10 @@ public class AccountRemoveCallbackReceiver extends BroadcastReceiver { } private void deleteNotificationChannel(Context context) { - NotificationManager notificatioManager = (NotificationManager) context.getSystemService(Context.NOTIFICATION_SERVICE); + NotificationManager notificationManager = (NotificationManager) context.getSystemService(Context.NOTIFICATION_SERVICE); - notificatioManager.deleteNotificationChannel(AppConstants.notificationChannelID); - notificatioManager.deleteNotificationChannel(SyncWorker.NOTIF_CHANNEL_ID); + notificationManager.cancelAll(); + notificationManager.deleteNotificationChannel(AppConstants.notificationChannelID); + notificationManager.deleteNotificationChannel(SyncWorker.NOTIF_CHANNEL_ID); } } diff --git a/app/src/main/java/foundation/e/drive/synchronization/SyncWorker.kt b/app/src/main/java/foundation/e/drive/synchronization/SyncWorker.kt index 968b44f9..faa1ca24 100644 --- a/app/src/main/java/foundation/e/drive/synchronization/SyncWorker.kt +++ b/app/src/main/java/foundation/e/drive/synchronization/SyncWorker.kt @@ -36,12 +36,13 @@ class SyncWorker( private lateinit var account: Account private lateinit var ocClient: OwnCloudClient - + private val notificationManager = applicationContext.getSystemService(NOTIFICATION_SERVICE) as NotificationManager companion object { const val UNIQUE_WORK_NAME = "syncWorker" const val NOTIF_CHANNEL_ID = "syncChannelId" private const val threadAmount = 2 private val syncManager = SyncProxy as SyncManager + private val notificationId = 2003004 } override fun doWork(): Result { @@ -69,6 +70,7 @@ class SyncWorker( executeRequests() } + notificationManager.cancel(notificationId) syncManager.startListeningFiles(applicationContext as EdriveApplication) return Result.success() } @@ -98,8 +100,12 @@ class SyncWorker( futureList.add(executor.submit(task)) } - for (future: Future<*> in futureList) { - future.get() + try { + for (future: Future<*> in futureList) { + future.get() + } + } catch (exception: Exception) { + Timber.w("Waiting for syncTask to finish but caught exception: ${exception.message}") } executor.shutdown() @@ -110,9 +116,10 @@ class SyncWorker( private fun createForegroundInfo(): ForegroundInfo { val title = applicationContext.getString(R.string.notif_sync_is_running_title) - val text = applicationContext.resources.getQuantityString(R.plurals.notif_sync_is_running_txt, syncManager.getQueueSize()) + val requestCount = syncManager.getQueueSize() + val text = applicationContext.resources + .getQuantityString(R.plurals.notif_sync_is_running_txt, requestCount, requestCount) - val notificationId = 2003004 val notification = NotificationCompat.Builder(applicationContext, NOTIF_CHANNEL_ID) .setOngoing(true) .setContentTitle(title) @@ -130,7 +137,6 @@ class SyncWorker( val channelDescription = applicationContext.getString(R.string.notif_sync_channel_description) channel.description = channelDescription - val notificationManager = applicationContext.getSystemService(NOTIFICATION_SERVICE) as NotificationManager notificationManager.createNotificationChannel(channel) } } \ No newline at end of file diff --git a/app/src/main/res/values/strings.xml b/app/src/main/res/values/strings.xml index 5d6fa799..11f2e3c3 100644 --- a/app/src/main/res/values/strings.xml +++ b/app/src/main/res/values/strings.xml @@ -34,7 +34,7 @@ eDrive is synchronizing content %d file to sync - %d files to sync + %d files to sync Sync worker\'s channel Notification about content being synchronized -- GitLab From 79da04fb615837a3ee12a1483f8aaecc25a76ae5 Mon Sep 17 00:00:00 2001 From: vincent Bourgmayer Date: Fri, 10 Nov 2023 12:02:02 +0100 Subject: [PATCH 20/33] fix error in fullScanWorker that block detection of some local files --- .../java/foundation/e/drive/periodicScan/FullScanWorker.kt | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/app/src/main/java/foundation/e/drive/periodicScan/FullScanWorker.kt b/app/src/main/java/foundation/e/drive/periodicScan/FullScanWorker.kt index b5ae98f0..5c423254 100644 --- a/app/src/main/java/foundation/e/drive/periodicScan/FullScanWorker.kt +++ b/app/src/main/java/foundation/e/drive/periodicScan/FullScanWorker.kt @@ -59,18 +59,18 @@ class FullScanWorker(private val context: Context, private val workerParams: Wor return Result.failure() } - val syncFolders = loadSyncedFolders(account!!) + var syncFolders = loadSyncedFolders(account!!) if (syncFolders.isEmpty()) { requestCollector.startListeningFiles(applicationContext as Application) return Result.success() } - val remoteSyncRequests = scanRemoteFiles(account, syncFolders.toMutableList()) + val remoteSyncRequests = scanRemoteFiles(account, syncFolders) syncRequests.putAll(remoteSyncRequests) Timber.d("${remoteSyncRequests.size} request collected from cloud") - val localSyncRequests = scanLocalFiles(syncFolders.toMutableList()) + val localSyncRequests = scanLocalFiles(syncFolders) syncRequests.putAll(localSyncRequests) Timber.d("${localSyncRequests.size} request collected from device") -- GitLab From 737da45c14db48b4e5de8eac76d17c5f004ae9a5 Mon Sep 17 00:00:00 2001 From: vincent Bourgmayer Date: Fri, 10 Nov 2023 12:51:41 +0100 Subject: [PATCH 21/33] update Notification Title --- .../java/foundation/e/drive/synchronization/SyncWorker.kt | 6 +++--- app/src/main/res/values/strings.xml | 2 +- 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/app/src/main/java/foundation/e/drive/synchronization/SyncWorker.kt b/app/src/main/java/foundation/e/drive/synchronization/SyncWorker.kt index faa1ca24..d8c38a27 100644 --- a/app/src/main/java/foundation/e/drive/synchronization/SyncWorker.kt +++ b/app/src/main/java/foundation/e/drive/synchronization/SyncWorker.kt @@ -40,9 +40,9 @@ class SyncWorker( companion object { const val UNIQUE_WORK_NAME = "syncWorker" const val NOTIF_CHANNEL_ID = "syncChannelId" + const val NOTIFICATION_ID = 2003004 private const val threadAmount = 2 private val syncManager = SyncProxy as SyncManager - private val notificationId = 2003004 } override fun doWork(): Result { @@ -70,7 +70,7 @@ class SyncWorker( executeRequests() } - notificationManager.cancel(notificationId) + notificationManager.cancel(NOTIFICATION_ID) syncManager.startListeningFiles(applicationContext as EdriveApplication) return Result.success() } @@ -127,7 +127,7 @@ class SyncWorker( .setSmallIcon(R.drawable.ic_arrow_up) .build() - return ForegroundInfo(notificationId, notification) + return ForegroundInfo(NOTIFICATION_ID, notification) } private fun createNotificationChannel() { diff --git a/app/src/main/res/values/strings.xml b/app/src/main/res/values/strings.xml index 11f2e3c3..fda41306 100644 --- a/app/src/main/res/values/strings.xml +++ b/app/src/main/res/values/strings.xml @@ -31,7 +31,7 @@ 99% of your allotted cloud storage is used. Please take action. You\'ve filled your allotted cloud storage up to 90%. You\'ve filled your allotted cloud storage up to 80%. - eDrive is synchronizing content + File synchronization in progress %d file to sync %d files to sync -- GitLab From 96726edb2fabb44b1385934d9073181432e48804 Mon Sep 17 00:00:00 2001 From: Sayantan Roychowdhury Date: Mon, 13 Nov 2023 08:55:16 +0000 Subject: [PATCH 22/33] Apply 1 suggestion(s) to 1 file(s) --- .../java/foundation/e/drive/synchronization/SyncTask.kt | 7 ++++++- 1 file changed, 6 insertions(+), 1 deletion(-) diff --git a/app/src/main/java/foundation/e/drive/synchronization/SyncTask.kt b/app/src/main/java/foundation/e/drive/synchronization/SyncTask.kt index 1a5315cf..849e943a 100644 --- a/app/src/main/java/foundation/e/drive/synchronization/SyncTask.kt +++ b/app/src/main/java/foundation/e/drive/synchronization/SyncTask.kt @@ -24,7 +24,12 @@ import foundation.e.drive.utils.CommonUtils import timber.log.Timber import java.io.File -class SyncTask(val request: SyncRequest, val client: OwnCloudClient, val account: Account, val context: Context): Runnable { +class SyncTask( + val request: SyncRequest, + val client: OwnCloudClient, + val account: Account, + val context: Context, +) : Runnable { private val fileName = request.syncedFileState.name private val filePath = request.syncedFileState.localPath companion object { -- GitLab From e662700d5743f49ff395c27b46a9864d41bfd4c0 Mon Sep 17 00:00:00 2001 From: vincent Bourgmayer Date: Mon, 13 Nov 2023 10:06:17 +0100 Subject: [PATCH 23/33] apply Sayantan's suggestion --- .../account/receivers/AccountRemoveCallbackReceiver.java | 4 ++-- .../java/foundation/e/drive/periodicScan/FullScanWorker.kt | 2 +- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/app/src/main/java/foundation/e/drive/account/receivers/AccountRemoveCallbackReceiver.java b/app/src/main/java/foundation/e/drive/account/receivers/AccountRemoveCallbackReceiver.java index 40e49ba8..f311175d 100644 --- a/app/src/main/java/foundation/e/drive/account/receivers/AccountRemoveCallbackReceiver.java +++ b/app/src/main/java/foundation/e/drive/account/receivers/AccountRemoveCallbackReceiver.java @@ -52,7 +52,7 @@ public class AccountRemoveCallbackReceiver extends BroadcastReceiver { deleteDatabase(applicationContext); cleanSharedPreferences(applicationContext, preferences); removeCachedFiles(applicationContext); - deleteNotificationChannel(applicationContext); + deleteNotificationChannels(applicationContext); DavClientProvider.getInstance().cleanUp(); ViewUtils.updateWidgetView(applicationContext); @@ -138,7 +138,7 @@ public class AccountRemoveCallbackReceiver extends BroadcastReceiver { return dir.delete(); } - private void deleteNotificationChannel(Context context) { + private void deleteNotificationChannels(Context context) { NotificationManager notificationManager = (NotificationManager) context.getSystemService(Context.NOTIFICATION_SERVICE); notificationManager.cancelAll(); diff --git a/app/src/main/java/foundation/e/drive/periodicScan/FullScanWorker.kt b/app/src/main/java/foundation/e/drive/periodicScan/FullScanWorker.kt index 5c423254..49cc9c53 100644 --- a/app/src/main/java/foundation/e/drive/periodicScan/FullScanWorker.kt +++ b/app/src/main/java/foundation/e/drive/periodicScan/FullScanWorker.kt @@ -59,7 +59,7 @@ class FullScanWorker(private val context: Context, private val workerParams: Wor return Result.failure() } - var syncFolders = loadSyncedFolders(account!!) + val syncFolders = loadSyncedFolders(account!!) if (syncFolders.isEmpty()) { requestCollector.startListeningFiles(applicationContext as Application) return Result.success() -- GitLab From bac8d8920b61f41f02c6ebd4278e01b03cb10bb7 Mon Sep 17 00:00:00 2001 From: vincent Bourgmayer Date: Mon, 13 Nov 2023 11:07:13 +0100 Subject: [PATCH 24/33] add sync icon from Figma --- .../foundation/e/drive/synchronization/SyncWorker.kt | 2 +- app/src/main/res/drawable/ic_synchronization.xml | 9 +++++++++ 2 files changed, 10 insertions(+), 1 deletion(-) create mode 100644 app/src/main/res/drawable/ic_synchronization.xml diff --git a/app/src/main/java/foundation/e/drive/synchronization/SyncWorker.kt b/app/src/main/java/foundation/e/drive/synchronization/SyncWorker.kt index d8c38a27..5925cfdf 100644 --- a/app/src/main/java/foundation/e/drive/synchronization/SyncWorker.kt +++ b/app/src/main/java/foundation/e/drive/synchronization/SyncWorker.kt @@ -124,7 +124,7 @@ class SyncWorker( .setOngoing(true) .setContentTitle(title) .setContentText(text) - .setSmallIcon(R.drawable.ic_arrow_up) + .setSmallIcon(R.drawable.ic_synchronization) .build() return ForegroundInfo(NOTIFICATION_ID, notification) diff --git a/app/src/main/res/drawable/ic_synchronization.xml b/app/src/main/res/drawable/ic_synchronization.xml new file mode 100644 index 00000000..3140b2a2 --- /dev/null +++ b/app/src/main/res/drawable/ic_synchronization.xml @@ -0,0 +1,9 @@ + + + -- GitLab From 8cc18efe93612dc909d4193844d6a9a389b445c1 Mon Sep 17 00:00:00 2001 From: vincent Bourgmayer Date: Mon, 13 Nov 2023 14:26:25 +0100 Subject: [PATCH 25/33] fix accountRemoval --- .../AccountRemoveCallbackReceiver.java | 26 ++++++++++++++----- 1 file changed, 20 insertions(+), 6 deletions(-) diff --git a/app/src/main/java/foundation/e/drive/account/receivers/AccountRemoveCallbackReceiver.java b/app/src/main/java/foundation/e/drive/account/receivers/AccountRemoveCallbackReceiver.java index f311175d..d27a2fff 100644 --- a/app/src/main/java/foundation/e/drive/account/receivers/AccountRemoveCallbackReceiver.java +++ b/app/src/main/java/foundation/e/drive/account/receivers/AccountRemoveCallbackReceiver.java @@ -21,10 +21,12 @@ import android.content.SharedPreferences; import androidx.annotation.NonNull; import androidx.annotation.Nullable; +import androidx.work.WorkManager; import java.io.File; import foundation.e.drive.EdriveApplication; +import foundation.e.drive.R; import foundation.e.drive.database.DbHelper; import foundation.e.drive.database.FailedSyncPrefsManager; import foundation.e.drive.synchronization.SyncWorker; @@ -44,10 +46,11 @@ public class AccountRemoveCallbackReceiver extends BroadcastReceiver { final SharedPreferences preferences = applicationContext.getSharedPreferences(AppConstants.SHARED_PREFERENCE_NAME, Context.MODE_PRIVATE); - if (!shouldProceedWithRemoval(intent, preferences)) { + if (!shouldProceedWithRemoval(intent, preferences, applicationContext)) { return; } + cancelWorkers(applicationContext); stopRecursiveFileObserver(applicationContext); deleteDatabase(applicationContext); cleanSharedPreferences(applicationContext, preferences); @@ -58,6 +61,13 @@ public class AccountRemoveCallbackReceiver extends BroadcastReceiver { ViewUtils.updateWidgetView(applicationContext); } + + private void cancelWorkers(@NonNull Context context) { + final WorkManager workManager = WorkManager.getInstance(context); + workManager.cancelAllWorkByTag(AppConstants.WORK_GENERIC_TAG); + } + + private void deleteDatabase(@NonNull Context applicationContext) { final boolean result = applicationContext.deleteDatabase(DbHelper.DATABASE_NAME); Timber.d("Remove Database: %s", result); @@ -69,16 +79,16 @@ public class AccountRemoveCallbackReceiver extends BroadcastReceiver { } } - private boolean shouldProceedWithRemoval(@NonNull Intent intent, @NonNull SharedPreferences preferences) { + private boolean shouldProceedWithRemoval(@NonNull Intent intent, @NonNull SharedPreferences preferences, @NonNull Context context) { if (isInvalidAction(intent) || intent.getExtras() == null) { Timber.w("Invalid account removal request"); return false; } String currentAccount = preferences.getString(AccountManager.KEY_ACCOUNT_NAME, ""); - String currentAccountType = preferences.getString(AccountManager.KEY_ACCOUNT_TYPE, ""); + String currentAccountType = context.getString(R.string.eelo_account_type); - if (currentAccount.isEmpty() || currentAccountType.isEmpty()) { + if (currentAccount.isEmpty()) { Timber.d("No account set up, ignoring account removal"); return false; } @@ -142,7 +152,11 @@ public class AccountRemoveCallbackReceiver extends BroadcastReceiver { NotificationManager notificationManager = (NotificationManager) context.getSystemService(Context.NOTIFICATION_SERVICE); notificationManager.cancelAll(); - notificationManager.deleteNotificationChannel(AppConstants.notificationChannelID); - notificationManager.deleteNotificationChannel(SyncWorker.NOTIF_CHANNEL_ID); + try { + notificationManager.deleteNotificationChannel(AppConstants.notificationChannelID); + notificationManager.deleteNotificationChannel(SyncWorker.NOTIF_CHANNEL_ID); + } catch (Exception exception) { + Timber.d("Cannot delete notification Channel %s", exception.getMessage()); + } } } -- GitLab From 156a05f7d39f8c5c1a2eea4a2361e95b821f784a Mon Sep 17 00:00:00 2001 From: vincent Bourgmayer Date: Mon, 13 Nov 2023 17:49:16 +0100 Subject: [PATCH 26/33] stop sync when account is removed --- .../e/drive/synchronization/SyncWorker.kt | 71 ++++++++++++------- 1 file changed, 44 insertions(+), 27 deletions(-) diff --git a/app/src/main/java/foundation/e/drive/synchronization/SyncWorker.kt b/app/src/main/java/foundation/e/drive/synchronization/SyncWorker.kt index 5925cfdf..da313cd3 100644 --- a/app/src/main/java/foundation/e/drive/synchronization/SyncWorker.kt +++ b/app/src/main/java/foundation/e/drive/synchronization/SyncWorker.kt @@ -37,6 +37,9 @@ class SyncWorker( private lateinit var account: Account private lateinit var ocClient: OwnCloudClient private val notificationManager = applicationContext.getSystemService(NOTIFICATION_SERVICE) as NotificationManager + private var executor = Executors.newFixedThreadPool(threadAmount) + + companion object { const val UNIQUE_WORK_NAME = "syncWorker" const val NOTIF_CHANNEL_ID = "syncChannelId" @@ -45,33 +48,44 @@ class SyncWorker( private val syncManager = SyncProxy as SyncManager } + + override fun onStopped() { + super.onStopped() + Timber.d("SyncWorker has been stopped") + notificationManager.cancel(NOTIFICATION_ID) + executor.shutdownNow() + } override fun doWork(): Result { - val tmpAccount = loadAccount() - if (tmpAccount == null) { - Timber.d("Warning : account is null") - syncManager.startListeningFiles(applicationContext as EdriveApplication) - return Result.failure() - } - this.account = tmpAccount + try { + val tmpAccount = loadAccount() + if (tmpAccount == null) { + Timber.d("Warning : account is null") + syncManager.startListeningFiles(applicationContext as EdriveApplication) + return Result.failure() + } + this.account = tmpAccount - val tmpOcClient = getOcClient(account) - if (tmpOcClient == null) { - Timber.d("Warning : ocClient is null") - syncManager.startListeningFiles(applicationContext as EdriveApplication) - return Result.failure() - } - ocClient = tmpOcClient + val tmpOcClient = getOcClient(account) + if (tmpOcClient == null) { + Timber.d("Warning : ocClient is null") + syncManager.startListeningFiles(applicationContext as EdriveApplication) + return Result.failure() + } + ocClient = tmpOcClient - createNotificationChannel() + createNotificationChannel() - while (!syncManager.isQueueEmpty()) { //new request may have been added during execution of others - setForegroundAsync(createForegroundInfo()) - executeRequests() - } + while (!syncManager.isQueueEmpty()) { + setForegroundAsync(createForegroundInfo()) + executeRequests() + } - notificationManager.cancel(NOTIFICATION_ID) - syncManager.startListeningFiles(applicationContext as EdriveApplication) + notificationManager.cancel(NOTIFICATION_ID) + syncManager.startListeningFiles(applicationContext as EdriveApplication) + } catch (exception: Exception) { //for Sentry report + Timber.w(exception) + } return Result.success() } @@ -93,19 +107,22 @@ class SyncWorker( private fun executeRequests() { val futureList = arrayListOf>() - val executor = Executors.newFixedThreadPool(threadAmount) + if (executor.isShutdown || executor.isTerminated) { + executor = Executors.newFixedThreadPool(threadAmount) + } while (!syncManager.isQueueEmpty()) { val request = syncManager.pollSyncRequest()?: break val task = SyncTask(request, ocClient, account, applicationContext) - futureList.add(executor.submit(task)) + futureList.add(executor!!.submit(task)) } - try { - for (future: Future<*> in futureList) { + for (future: Future<*> in futureList) { + if (this.isStopped) { + Timber.d("Cancel task before execution") + future.cancel(true) + } else { future.get() } - } catch (exception: Exception) { - Timber.w("Waiting for syncTask to finish but caught exception: ${exception.message}") } executor.shutdown() -- GitLab From 76d3ec8e3f8c9edd81d3d889f637ea5f5f54b304 Mon Sep 17 00:00:00 2001 From: Sayantan Roychowdhury Date: Mon, 13 Nov 2023 16:54:24 +0000 Subject: [PATCH 27/33] Apply 1 suggestion(s) to 1 file(s) --- .../java/foundation/e/drive/synchronization/SyncWorker.kt | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/app/src/main/java/foundation/e/drive/synchronization/SyncWorker.kt b/app/src/main/java/foundation/e/drive/synchronization/SyncWorker.kt index da313cd3..02fbe33c 100644 --- a/app/src/main/java/foundation/e/drive/synchronization/SyncWorker.kt +++ b/app/src/main/java/foundation/e/drive/synchronization/SyncWorker.kt @@ -36,7 +36,9 @@ class SyncWorker( private lateinit var account: Account private lateinit var ocClient: OwnCloudClient - private val notificationManager = applicationContext.getSystemService(NOTIFICATION_SERVICE) as NotificationManager + private val notificationManager : NotificationManager by lazy { + applicationContext.getSystemService(NOTIFICATION_SERVICE) as NotificationManager + } private var executor = Executors.newFixedThreadPool(threadAmount) -- GitLab From eae917b0ef634a1844906b3b49d2616a50b64d3e Mon Sep 17 00:00:00 2001 From: Fahim Salam Chowdhury Date: Tue, 14 Nov 2023 08:45:56 +0000 Subject: [PATCH 28/33] Apply 10 suggestion from Fahim --- .../java/foundation/e/drive/synchronization/SyncTask.kt | 4 +++- .../foundation/e/drive/synchronization/SyncWorker.kt | 9 ++++++--- 2 files changed, 9 insertions(+), 4 deletions(-) diff --git a/app/src/main/java/foundation/e/drive/synchronization/SyncTask.kt b/app/src/main/java/foundation/e/drive/synchronization/SyncTask.kt index 849e943a..01f8238a 100644 --- a/app/src/main/java/foundation/e/drive/synchronization/SyncTask.kt +++ b/app/src/main/java/foundation/e/drive/synchronization/SyncTask.kt @@ -61,7 +61,6 @@ class SyncTask( return false } - if (!isNetworkAvailable()) { Timber.d("No network available. can't run syncTask") return false @@ -101,6 +100,7 @@ class SyncTask( Timber.d("Error: Download result for $fileName is null") return false } + val code = result.code Timber.d("Download operation for $fileName result in: ${result.code}") @@ -109,6 +109,7 @@ class SyncTask( Timber.d("Network has been lost. SyncRequest queue has been cleared") return false } + return result.isSuccess } @@ -120,6 +121,7 @@ class SyncTask( Timber.d("Failed to disable sync for $fileState.name from DB") return false } + return true } diff --git a/app/src/main/java/foundation/e/drive/synchronization/SyncWorker.kt b/app/src/main/java/foundation/e/drive/synchronization/SyncWorker.kt index 02fbe33c..c2f1c075 100644 --- a/app/src/main/java/foundation/e/drive/synchronization/SyncWorker.kt +++ b/app/src/main/java/foundation/e/drive/synchronization/SyncWorker.kt @@ -52,11 +52,13 @@ class SyncWorker( override fun onStopped() { - super.onStopped() Timber.d("SyncWorker has been stopped") notificationManager.cancel(NOTIFICATION_ID) executor.shutdownNow() + + super.onStopped() } + override fun doWork(): Result { try { val tmpAccount = loadAccount() @@ -77,7 +79,6 @@ class SyncWorker( createNotificationChannel() - while (!syncManager.isQueueEmpty()) { setForegroundAsync(createForegroundInfo()) executeRequests() @@ -85,9 +86,10 @@ class SyncWorker( notificationManager.cancel(NOTIFICATION_ID) syncManager.startListeningFiles(applicationContext as EdriveApplication) - } catch (exception: Exception) { //for Sentry report + } catch (exception: Exception) { Timber.w(exception) } + return Result.success() } @@ -112,6 +114,7 @@ class SyncWorker( if (executor.isShutdown || executor.isTerminated) { executor = Executors.newFixedThreadPool(threadAmount) } + while (!syncManager.isQueueEmpty()) { val request = syncManager.pollSyncRequest()?: break val task = SyncTask(request, ocClient, account, applicationContext) -- GitLab From 5f4b49f1f7cf731634e7b438323b3b20e35c0857 Mon Sep 17 00:00:00 2001 From: vincent Bourgmayer Date: Tue, 14 Nov 2023 09:53:41 +0100 Subject: [PATCH 29/33] apply another Batch of Fahim's suggestion --- .../e/drive/synchronization/SyncTask.kt | 20 +++++++++++-------- .../e/drive/synchronization/SyncWorker.kt | 12 ++++------- .../tasks/UploadFileOperation.java | 1 + 3 files changed, 17 insertions(+), 16 deletions(-) diff --git a/app/src/main/java/foundation/e/drive/synchronization/SyncTask.kt b/app/src/main/java/foundation/e/drive/synchronization/SyncTask.kt index 01f8238a..d37317e8 100644 --- a/app/src/main/java/foundation/e/drive/synchronization/SyncTask.kt +++ b/app/src/main/java/foundation/e/drive/synchronization/SyncTask.kt @@ -30,31 +30,35 @@ class SyncTask( val account: Account, val context: Context, ) : Runnable { - private val fileName = request.syncedFileState.name - private val filePath = request.syncedFileState.localPath companion object { private val syncManager = SyncProxy as SyncManager } + + private val fileName = request.syncedFileState.name + private val fileLocalPath = request.syncedFileState.localPath + override fun run() { - val canStart = checkStartCondition() - if (!canStart) { + if (!canStart()) { return } - val fileLocalPath = request.syncedFileState.localPath - Timber.d(" starts ${request.operationType.name} for: $filePath") + + Timber.d(" starts ${request.operationType.name} for: $fileLocalPath") + val wrapper = SyncWrapper(request, account, context) syncManager.addStartedRequest(fileLocalPath, wrapper) + val succeed = when (request.operationType) { UPLOAD -> runUpload(wrapper) DOWNLOAD -> runDownload(wrapper) DISABLE_SYNCING -> runSyncDisabling() } + updateFailureCounter(request, succeed) syncManager.removeStartedRequest(fileLocalPath) - Timber.d("${request.operationType.name} finished for $filePath") + Timber.d("${request.operationType.name} finished for $fileLocalPath") } - private fun checkStartCondition(): Boolean { + private fun canStart(): Boolean { val isSyncAllowed = CommonUtils.isThisSyncAllowed(account, request.syncedFileState.isMediaType) if (!isSyncAllowed){ Timber.d("Sync of the file is not allowed anymore") diff --git a/app/src/main/java/foundation/e/drive/synchronization/SyncWorker.kt b/app/src/main/java/foundation/e/drive/synchronization/SyncWorker.kt index c2f1c075..363d40fa 100644 --- a/app/src/main/java/foundation/e/drive/synchronization/SyncWorker.kt +++ b/app/src/main/java/foundation/e/drive/synchronization/SyncWorker.kt @@ -34,14 +34,6 @@ class SyncWorker( params: WorkerParameters ) : Worker(context, params) { - private lateinit var account: Account - private lateinit var ocClient: OwnCloudClient - private val notificationManager : NotificationManager by lazy { - applicationContext.getSystemService(NOTIFICATION_SERVICE) as NotificationManager - } - private var executor = Executors.newFixedThreadPool(threadAmount) - - companion object { const val UNIQUE_WORK_NAME = "syncWorker" const val NOTIF_CHANNEL_ID = "syncChannelId" @@ -50,6 +42,10 @@ class SyncWorker( private val syncManager = SyncProxy as SyncManager } + private lateinit var account: Account + private lateinit var ocClient: OwnCloudClient + private val notificationManager = applicationContext.getSystemService(NOTIFICATION_SERVICE) as NotificationManager + private var executor = Executors.newFixedThreadPool(threadAmount) override fun onStopped() { Timber.d("SyncWorker has been stopped") diff --git a/app/src/main/java/foundation/e/drive/synchronization/tasks/UploadFileOperation.java b/app/src/main/java/foundation/e/drive/synchronization/tasks/UploadFileOperation.java index 1d920a6c..e141198c 100644 --- a/app/src/main/java/foundation/e/drive/synchronization/tasks/UploadFileOperation.java +++ b/app/src/main/java/foundation/e/drive/synchronization/tasks/UploadFileOperation.java @@ -270,6 +270,7 @@ public class UploadFileOperation extends RemoteOperation { syncedState.getRemotePath(), mimeType, syncedState.getLastEtag(), timeStamp, false); + //noinspection deprecation return uploadOperation.execute(client); } -- GitLab From ff05b348467e21bf4c870c1a977ea54dd73fc2e5 Mon Sep 17 00:00:00 2001 From: vincent Bourgmayer Date: Tue, 14 Nov 2023 09:59:05 +0100 Subject: [PATCH 30/33] add missing licence header in drawable folder --- .../main/res/drawable/button_background.xml | 17 +++++++++++++++++ .../res/drawable/button_background_light.xml | 17 +++++++++++++++++ app/src/main/res/drawable/ic_arrow_up.xml | 18 ++++++++++++++++++ app/src/main/res/drawable/ic_clipboard.xml | 18 ++++++++++++++++++ app/src/main/res/drawable/ic_expand_less.xml | 18 ++++++++++++++++++ app/src/main/res/drawable/ic_expand_more.xml | 18 ++++++++++++++++++ .../res/drawable/ic_keyboard_arrow_down.xml | 18 ++++++++++++++++++ app/src/main/res/drawable/ic_murena_eel.xml | 1 + app/src/main/res/drawable/ic_settings.xml | 18 ++++++++++++++++++ .../main/res/drawable/ic_settings_light.xml | 18 ++++++++++++++++++ .../main/res/drawable/ic_synchronization.xml | 18 ++++++++++++++++++ .../main/res/drawable/widget_background.xml | 17 +++++++++++++++++ 12 files changed, 196 insertions(+) diff --git a/app/src/main/res/drawable/button_background.xml b/app/src/main/res/drawable/button_background.xml index 1c1a64ee..56a100fd 100644 --- a/app/src/main/res/drawable/button_background.xml +++ b/app/src/main/res/drawable/button_background.xml @@ -1,4 +1,21 @@ + + + diff --git a/app/src/main/res/drawable/button_background_light.xml b/app/src/main/res/drawable/button_background_light.xml index 6a146ee3..98394184 100644 --- a/app/src/main/res/drawable/button_background_light.xml +++ b/app/src/main/res/drawable/button_background_light.xml @@ -1,4 +1,21 @@ + + + diff --git a/app/src/main/res/drawable/ic_arrow_up.xml b/app/src/main/res/drawable/ic_arrow_up.xml index fa9dfe41..994851eb 100644 --- a/app/src/main/res/drawable/ic_arrow_up.xml +++ b/app/src/main/res/drawable/ic_arrow_up.xml @@ -1,3 +1,21 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + -- GitLab From 44cc1806f867e6ad850361c347ec85d33b79549e Mon Sep 17 00:00:00 2001 From: vincent Bourgmayer Date: Tue, 14 Nov 2023 11:38:02 +0100 Subject: [PATCH 31/33] replace lateinit var by nullable var as suggested by Fahim --- .../e/drive/synchronization/SyncWorker.kt | 17 ++++++++--------- 1 file changed, 8 insertions(+), 9 deletions(-) diff --git a/app/src/main/java/foundation/e/drive/synchronization/SyncWorker.kt b/app/src/main/java/foundation/e/drive/synchronization/SyncWorker.kt index 363d40fa..eda410b2 100644 --- a/app/src/main/java/foundation/e/drive/synchronization/SyncWorker.kt +++ b/app/src/main/java/foundation/e/drive/synchronization/SyncWorker.kt @@ -42,8 +42,8 @@ class SyncWorker( private val syncManager = SyncProxy as SyncManager } - private lateinit var account: Account - private lateinit var ocClient: OwnCloudClient + private var account: Account? = null + private var ocClient: OwnCloudClient? = null private val notificationManager = applicationContext.getSystemService(NOTIFICATION_SERVICE) as NotificationManager private var executor = Executors.newFixedThreadPool(threadAmount) @@ -57,21 +57,19 @@ class SyncWorker( override fun doWork(): Result { try { - val tmpAccount = loadAccount() - if (tmpAccount == null) { + account = loadAccount() + if (account == null) { Timber.d("Warning : account is null") syncManager.startListeningFiles(applicationContext as EdriveApplication) return Result.failure() } - this.account = tmpAccount - val tmpOcClient = getOcClient(account) - if (tmpOcClient == null) { + ocClient = getOcClient(account!!) + if (ocClient == null) { Timber.d("Warning : ocClient is null") syncManager.startListeningFiles(applicationContext as EdriveApplication) return Result.failure() } - ocClient = tmpOcClient createNotificationChannel() @@ -113,7 +111,7 @@ class SyncWorker( while (!syncManager.isQueueEmpty()) { val request = syncManager.pollSyncRequest()?: break - val task = SyncTask(request, ocClient, account, applicationContext) + val task = SyncTask(request, ocClient!!, account!!, applicationContext) futureList.add(executor!!.submit(task)) } @@ -143,6 +141,7 @@ class SyncWorker( .setContentTitle(title) .setContentText(text) .setSmallIcon(R.drawable.ic_synchronization) + .build() return ForegroundInfo(NOTIFICATION_ID, notification) -- GitLab From 7baa58dc138ee77c7f3241b0b397afce6d160bb5 Mon Sep 17 00:00:00 2001 From: Fahim Salam Chowdhury Date: Tue, 14 Nov 2023 10:55:12 +0000 Subject: [PATCH 32/33] Apply 1 suggestion(s) to 1 file(s) --- .../e/drive/account/receivers/AccountRemoveCallbackReceiver.java | 1 - 1 file changed, 1 deletion(-) diff --git a/app/src/main/java/foundation/e/drive/account/receivers/AccountRemoveCallbackReceiver.java b/app/src/main/java/foundation/e/drive/account/receivers/AccountRemoveCallbackReceiver.java index d27a2fff..2fa79a04 100644 --- a/app/src/main/java/foundation/e/drive/account/receivers/AccountRemoveCallbackReceiver.java +++ b/app/src/main/java/foundation/e/drive/account/receivers/AccountRemoveCallbackReceiver.java @@ -67,7 +67,6 @@ public class AccountRemoveCallbackReceiver extends BroadcastReceiver { workManager.cancelAllWorkByTag(AppConstants.WORK_GENERIC_TAG); } - private void deleteDatabase(@NonNull Context applicationContext) { final boolean result = applicationContext.deleteDatabase(DbHelper.DATABASE_NAME); Timber.d("Remove Database: %s", result); -- GitLab From 223f1628e742deaaf42ef07d87007c6f69e8d812 Mon Sep 17 00:00:00 2001 From: Fahim Salam Chowdhury Date: Tue, 14 Nov 2023 10:55:30 +0000 Subject: [PATCH 33/33] Apply 1 suggestion(s) to 1 file(s) --- .../drive/account/receivers/AccountRemoveCallbackReceiver.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/app/src/main/java/foundation/e/drive/account/receivers/AccountRemoveCallbackReceiver.java b/app/src/main/java/foundation/e/drive/account/receivers/AccountRemoveCallbackReceiver.java index 2fa79a04..668fb93d 100644 --- a/app/src/main/java/foundation/e/drive/account/receivers/AccountRemoveCallbackReceiver.java +++ b/app/src/main/java/foundation/e/drive/account/receivers/AccountRemoveCallbackReceiver.java @@ -155,7 +155,7 @@ public class AccountRemoveCallbackReceiver extends BroadcastReceiver { notificationManager.deleteNotificationChannel(AppConstants.notificationChannelID); notificationManager.deleteNotificationChannel(SyncWorker.NOTIF_CHANNEL_ID); } catch (Exception exception) { - Timber.d("Cannot delete notification Channel %s", exception.getMessage()); + Timber.e(exception, "Cannot delete notification Channel"); } } } -- GitLab