From 48659b570d416c90520519e419d98008a6e0f255 Mon Sep 17 00:00:00 2001 From: vincent Bourgmayer Date: Wed, 2 Mar 2022 09:28:26 +0100 Subject: [PATCH 01/68] Update compileSDK in app/build.gradle and remove dependency - updated compileSdkVersion from 28 to 31 to allow WorkManager API support - removed dependency: implementation 'com.android.support:appcompat-v7:26.1.0' because it is useless in our scope. As stated at https://developer.android.com/topic/libraries/support-library/packages#v7-appcompat it only add ActionBar relative code. But eDrive doesn't use it for a while. --- app/build.gradle | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/app/build.gradle b/app/build.gradle index c65b01d7..44a0e1de 100644 --- a/app/build.gradle +++ b/app/build.gradle @@ -23,7 +23,7 @@ def getTestProp(String propName) { android { - compileSdkVersion 28 + compileSdkVersion 31 defaultConfig { applicationId "foundation.e.drive" minSdkVersion 26 @@ -59,7 +59,7 @@ android { dependencies { implementation fileTree(include: ['*.jar'], dir: 'libs') - implementation 'com.android.support:appcompat-v7:26.1.0' + //implementation 'com.android.support:appcompat-v7:26.1.0' androidTestImplementation 'com.android.support.test:runner:1.0.2' androidTestImplementation 'com.android.support.test:rules:1.0.2' -- GitLab From 042512c0032f3d1a8a298bdb0577dd16779934d8 Mon Sep 17 00:00:00 2001 From: vincent Bourgmayer Date: Wed, 2 Mar 2022 09:43:04 +0100 Subject: [PATCH 02/68] clean dependencies in app/build.gradle - replace all androidTestImplementation and testImplementation library into androidx - reorganize dependencies to make them cleaner --- app/build.gradle | 28 ++++++++++------------------ 1 file changed, 10 insertions(+), 18 deletions(-) diff --git a/app/build.gradle b/app/build.gradle index 44a0e1de..e2bfaed3 100644 --- a/app/build.gradle +++ b/app/build.gradle @@ -58,27 +58,19 @@ android { dependencies { + api project(':NextcloudLib') implementation fileTree(include: ['*.jar'], dir: 'libs') - //implementation 'com.android.support:appcompat-v7:26.1.0' - - androidTestImplementation 'com.android.support.test:runner:1.0.2' - androidTestImplementation 'com.android.support.test:rules:1.0.2' - androidTestImplementation 'androidx.annotation:annotation:1.3.0' - androidTestImplementation 'com.android.support.test.espresso:espresso-core:3.0.1' api 'androidx.annotation:annotation:1.3.0' - api project(':NextcloudLib') - - //start to add lib for test - 1/4/21 - //@TODO: add junit runner as lib for testImplementation + + androidTestImplementation 'androidx.test:runner:1.4.0' + androidTestImplementation 'androidx.test:rules:1.4.0 ' + androidTestImplementation 'androidx.annotation:annotation:1.3.0' + androidTestImplementation 'androidx.test.espresso:espresso-core:3.4.0' + androidTestImplementation 'junit:junit:4.12' - testImplementation 'com.android.support.test:runner:1.0.2' - testImplementation 'com.android.support.test:rules:1.0.2' + testImplementation 'androidx.test:runner:1.4.0' + testImplementation 'androidx.test:rules:1.4.0 ' testImplementation 'junit:junit:4.12' - //testImplementation 'org.robolectric:robolectric:4.4' //need AndroidX - testImplementation "org.robolectric:robolectric:3.8" + testImplementation 'org.robolectric:robolectric:4.4' testImplementation('org.mockito:mockito-inline:3.4.0') - - //testImplementation Libs.AndroidX.Test.archCoreTesting //TODO: replace by not android X version - //implementation 'androidx.localbroadcastmanager:localbroadcastmanager:1.0.0' - androidTestImplementation 'junit:junit:4.12' } -- GitLab From 2af8a5bc19e24b133060ef95ff68666630f08e6e Mon Sep 17 00:00:00 2001 From: vincent Bourgmayer Date: Wed, 2 Mar 2022 09:44:47 +0100 Subject: [PATCH 03/68] add jetpack's work dependency --- app/build.gradle | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/app/build.gradle b/app/build.gradle index e2bfaed3..510784e9 100644 --- a/app/build.gradle +++ b/app/build.gradle @@ -61,6 +61,10 @@ dependencies { api project(':NextcloudLib') implementation fileTree(include: ['*.jar'], dir: 'libs') api 'androidx.annotation:annotation:1.3.0' + + def work_version = "2.7.1" + // (Java only) + implementation "androidx.work:work-runtime:$work_version" androidTestImplementation 'androidx.test:runner:1.4.0' androidTestImplementation 'androidx.test:rules:1.4.0 ' -- GitLab From 8cf7652674a9170f4f53457dd91d4a0ea0319f1b Mon Sep 17 00:00:00 2001 From: vincent Bourgmayer Date: Wed, 2 Mar 2022 09:50:16 +0100 Subject: [PATCH 04/68] fix typo for 'androidx.test:rules' dependencies --- app/build.gradle | 7 +++---- 1 file changed, 3 insertions(+), 4 deletions(-) diff --git a/app/build.gradle b/app/build.gradle index 510784e9..018d91fd 100644 --- a/app/build.gradle +++ b/app/build.gradle @@ -53,7 +53,6 @@ android { //includeAndroidResources = true } } - } @@ -65,15 +64,15 @@ dependencies { def work_version = "2.7.1" // (Java only) implementation "androidx.work:work-runtime:$work_version" - + androidTestImplementation 'androidx.test:runner:1.4.0' - androidTestImplementation 'androidx.test:rules:1.4.0 ' + androidTestImplementation 'androidx.test:rules:1.4.0' androidTestImplementation 'androidx.annotation:annotation:1.3.0' androidTestImplementation 'androidx.test.espresso:espresso-core:3.4.0' androidTestImplementation 'junit:junit:4.12' testImplementation 'androidx.test:runner:1.4.0' - testImplementation 'androidx.test:rules:1.4.0 ' + testImplementation 'androidx.test:rules:1.4.0' testImplementation 'junit:junit:4.12' testImplementation 'org.robolectric:robolectric:4.4' testImplementation('org.mockito:mockito-inline:3.4.0') -- GitLab From e7912568a5fe88ebac21e9aacfbedf906cf09970 Mon Sep 17 00:00:00 2001 From: vincent Bourgmayer Date: Wed, 2 Mar 2022 10:30:05 +0100 Subject: [PATCH 05/68] Remove BatteryStateReceiver - Removed BatteryStateReceiver.java from foundation.e.drive.receivers package - removed BatteryStateReceiver entry in AndroidManifest.xml --- app/src/main/AndroidManifest.xml | 8 ---- .../drive/receivers/BatteryStateReceiver.java | 41 ------------------- 2 files changed, 49 deletions(-) delete mode 100644 app/src/main/java/foundation/e/drive/receivers/BatteryStateReceiver.java diff --git a/app/src/main/AndroidManifest.xml b/app/src/main/AndroidManifest.xml index be871995..e07511c5 100644 --- a/app/src/main/AndroidManifest.xml +++ b/app/src/main/AndroidManifest.xml @@ -69,14 +69,6 @@ http://www.gnu.org/licenses/gpl.html android:enabled="true" /> - - - - - - diff --git a/app/src/main/java/foundation/e/drive/receivers/BatteryStateReceiver.java b/app/src/main/java/foundation/e/drive/receivers/BatteryStateReceiver.java deleted file mode 100644 index 2fd661d1..00000000 --- a/app/src/main/java/foundation/e/drive/receivers/BatteryStateReceiver.java +++ /dev/null @@ -1,41 +0,0 @@ -/* - * Copyright © Vincent Bourgmayer (/e/ foundation). - * 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.receivers; - -import android.content.BroadcastReceiver; -import android.content.Context; -import android.content.Intent; -import android.util.Log; -import foundation.e.drive.utils.JobUtils; - -/** - * @author Vincent Bourgmayer - */ -public class BatteryStateReceiver extends BroadcastReceiver { - private final static String TAG = BatteryStateReceiver.class.getSimpleName(); - - @Override - public void onReceive(Context context, Intent intent) { - Log.i(TAG, "onReceive"); - - String intentAction = intent.getAction(); - if(intentAction == null) { - Log.e(TAG, "intent Action is null"); - } else if ( intentAction.equals(Intent.ACTION_BATTERY_OKAY) ) { - JobUtils.scheduleScannerJob(context); - }else if(intentAction.equals(Intent.ACTION_BATTERY_LOW)){ - JobUtils.stopScheduledJob(context, JobUtils.ScannerJobId); - try { - context.unregisterReceiver(ScreenOffReceiver.getInstance()); - }catch(Exception e){ - Log.e(TAG, e.toString() ); - } - } - } -} -- GitLab From 2b12b82d1d0ed70846391f746695778fefac1e95 Mon Sep 17 00:00:00 2001 From: vincent Bourgmayer Date: Wed, 2 Mar 2022 10:35:57 +0100 Subject: [PATCH 06/68] Remove ScreenOffReceiver - Remove ScreenOffReceiver.java in foundation.e.drive.receivers package - Remove "unregisterScreenOffReceiver()" in CommonUtils - Remove registration code for screenOffReceiver in InitializerService.java and scannerJob.java --- .../foundation/e/drive/jobs/ScannerJob.java | 8 +-- .../e/drive/receivers/ScreenOffReceiver.java | 51 ------------------- .../e/drive/services/InitializerService.java | 10 ---- .../foundation/e/drive/utils/CommonUtils.java | 20 -------- 4 files changed, 1 insertion(+), 88 deletions(-) delete mode 100644 app/src/main/java/foundation/e/drive/receivers/ScreenOffReceiver.java diff --git a/app/src/main/java/foundation/e/drive/jobs/ScannerJob.java b/app/src/main/java/foundation/e/drive/jobs/ScannerJob.java index 878f554b..50bfa641 100644 --- a/app/src/main/java/foundation/e/drive/jobs/ScannerJob.java +++ b/app/src/main/java/foundation/e/drive/jobs/ScannerJob.java @@ -11,9 +11,8 @@ package foundation.e.drive.jobs; import android.app.job.JobParameters; import android.app.job.JobService; import android.content.Intent; -import android.content.IntentFilter; import android.util.Log; -import foundation.e.drive.receivers.ScreenOffReceiver; + import foundation.e.drive.services.ObserverService; import foundation.e.drive.utils.CommonUtils; @@ -27,11 +26,6 @@ public class ScannerJob extends JobService { public boolean onStartJob(JobParameters params) { Log.i(TAG, "onStartJob()"); - Log.d(TAG, "RegisterReceiver: screenOffReceiver"); - IntentFilter filter = new IntentFilter(Intent.ACTION_SCREEN_ON); - filter.addAction(Intent.ACTION_SCREEN_OFF); - getApplicationContext().registerReceiver(ScreenOffReceiver.getInstance(), filter); - Intent observerServiceIntent = new Intent(this, ObserverService.class); this.startService(observerServiceIntent); jobFinished(params, false); diff --git a/app/src/main/java/foundation/e/drive/receivers/ScreenOffReceiver.java b/app/src/main/java/foundation/e/drive/receivers/ScreenOffReceiver.java deleted file mode 100644 index 27d48959..00000000 --- a/app/src/main/java/foundation/e/drive/receivers/ScreenOffReceiver.java +++ /dev/null @@ -1,51 +0,0 @@ -/* - * Copyright © Vincent Bourgmayer (/e/ foundation). - * 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.receivers; - -import android.content.BroadcastReceiver; -import android.content.Context; -import android.content.Intent; -import android.util.Log; -import foundation.e.drive.services.ObserverService; -import foundation.e.drive.utils.CommonUtils; - -/** - * @author Vincent Bourgmayer - * This is a broadcast receiver which catch "ACTION_SCREEN_OFF" to start scanning at a moment - * where the user won't need battery or network. - */ -public class ScreenOffReceiver extends BroadcastReceiver { - private final String TAG = ScreenOffReceiver.class.getSimpleName(); - private static ScreenOffReceiver instance; - - public static ScreenOffReceiver getInstance(){ - if(instance == null) - instance = new ScreenOffReceiver(); - return instance; - } - - /** - * Private constructor - */ - private ScreenOffReceiver(){} - - @Override - public void onReceive(Context context, Intent intent) { - Log.i(TAG, "onReceive"); - String intentAction = intent.getAction(); - if(intentAction == null){ - Log.e(TAG, "intent Action is null"); - } else if ( intent.getAction().equals(Intent.ACTION_SCREEN_OFF) - && CommonUtils.haveNetworkConnexion( context ) ) { - Log.d(TAG, "onReceive: ACTION_SCREEN_OFF"); - Intent cloudIntent = new Intent(context, ObserverService.class); - context.startService(cloudIntent); - } - } -} diff --git a/app/src/main/java/foundation/e/drive/services/InitializerService.java b/app/src/main/java/foundation/e/drive/services/InitializerService.java index 289229b2..273715b5 100644 --- a/app/src/main/java/foundation/e/drive/services/InitializerService.java +++ b/app/src/main/java/foundation/e/drive/services/InitializerService.java @@ -13,7 +13,6 @@ import android.accounts.AccountManager; import android.app.Service; import android.content.Context; import android.content.Intent; -import android.content.IntentFilter; import android.content.SharedPreferences; import android.os.Build; import android.os.Environment; @@ -32,11 +31,9 @@ import java.util.List; import foundation.e.drive.models.SyncedFolder; import foundation.e.drive.operations.CreateInitialFolderRemoteOperation; -import foundation.e.drive.receivers.ScreenOffReceiver; import foundation.e.drive.utils.AppConstants; import foundation.e.drive.utils.CommonUtils; import foundation.e.drive.utils.JobUtils; -import foundation.e.drive.utils.ServiceExceptionHandler; import static com.owncloud.android.lib.resources.files.FileUtils.PATH_SEPARATOR; import static foundation.e.drive.utils.AppConstants.INITIALFOLDERS_NUMBER; @@ -288,18 +285,11 @@ public class InitializerService extends Service implements OnRemoteOperationList //JobUtils.stopScheduledJob(appContext, JobUtils.InitializerJobId); JobUtils.scheduleScannerJob(appContext); - Log.d(TAG, "RegisterReceiver: screenOffReceiver"); - IntentFilter filter = new IntentFilter(Intent.ACTION_SCREEN_ON); - filter.addAction(Intent.ACTION_SCREEN_OFF); - getApplicationContext().registerReceiver(ScreenOffReceiver.getInstance(), filter); - //Immediatly start ObserverService to not have to wait 30 minutes. Intent observersServiceIntent = new Intent(getApplicationContext(), foundation.e.drive.services.ObserverService.class); startService(observersServiceIntent); - stopSelf(); - } @Override 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 e0f762fc..d14f1b2a 100644 --- a/app/src/main/java/foundation/e/drive/utils/CommonUtils.java +++ b/app/src/main/java/foundation/e/drive/utils/CommonUtils.java @@ -17,7 +17,6 @@ import android.content.ContentResolver; import android.content.Context; import android.media.MediaScannerConnection; import android.net.ConnectivityManager; -import android.net.Network; import android.net.NetworkInfo; import android.net.Uri; import android.util.Log; @@ -32,8 +31,6 @@ import com.owncloud.android.lib.resources.files.FileUtils; import java.io.File; -import foundation.e.drive.receivers.ScreenOffReceiver; - import static foundation.e.drive.utils.AppConstants.MEDIASYNC_PROVIDER_AUTHORITY; import static foundation.e.drive.utils.AppConstants.SETTINGSYNC_PROVIDER_AUTHORITY; @@ -64,23 +61,6 @@ public abstract class CommonUtils { } } - /** - * Unregister from screeOffReceiver component - * - * @param context app context - * @return true if unregistration was successful or false if it encounter an exception - */ - public static boolean unregisterScreenOff(Context context) { - try { - Log.d("TAG", "unregisterReceiver(screenOffReceiver)"); - context.unregisterReceiver(ScreenOffReceiver.getInstance()); - } catch (IllegalArgumentException e) { - Log.w(TAG, "Can't unregister screenOffReceiver "); - return false; - } - return true; - } - /** * This method retrieve Account corresponding to account's name and type -- GitLab From 8ecff71e1c5b15786a7d54a5e48fd7b7f52de7a6 Mon Sep 17 00:00:00 2001 From: vincent Bourgmayer Date: Wed, 2 Mar 2022 10:59:13 +0100 Subject: [PATCH 07/68] Remove remaining call of Commonutils.unregisterSceenOff() - remove call of the method in ResetService.java.onStartCommand() - remove call of the method in ScannerJob.java.onStopJob() --- app/src/main/java/foundation/e/drive/jobs/ScannerJob.java | 1 - .../main/java/foundation/e/drive/services/ResetService.java | 5 +---- 2 files changed, 1 insertion(+), 5 deletions(-) diff --git a/app/src/main/java/foundation/e/drive/jobs/ScannerJob.java b/app/src/main/java/foundation/e/drive/jobs/ScannerJob.java index 50bfa641..b62738bb 100644 --- a/app/src/main/java/foundation/e/drive/jobs/ScannerJob.java +++ b/app/src/main/java/foundation/e/drive/jobs/ScannerJob.java @@ -40,7 +40,6 @@ public class ScannerJob extends JobService { @Override public boolean onStopJob(JobParameters params) { Log.i(TAG, "onStopJob"); - boolean unregisteredReceiver = CommonUtils.unregisterScreenOff(getApplicationContext()); Intent observerServiceIntent = new Intent(this, ObserverService.class); this.stopService(observerServiceIntent); return false; diff --git a/app/src/main/java/foundation/e/drive/services/ResetService.java b/app/src/main/java/foundation/e/drive/services/ResetService.java index dad3cb20..447ab2c8 100644 --- a/app/src/main/java/foundation/e/drive/services/ResetService.java +++ b/app/src/main/java/foundation/e/drive/services/ResetService.java @@ -86,11 +86,8 @@ public class ResetService extends Service { .apply(); } - //5. Unregister screenOffReceiver - result = CommonUtils.unregisterScreenOff(getApplicationContext()); - Log.d(TAG, "Unregistered ScreenOffReceiver: "+result); - //6. Remove Cached File + //5. Remove Cached File File[] cachedFiles = this.getApplicationContext().getExternalCacheDir().listFiles(); for(File f : cachedFiles){ f.delete(); -- GitLab From acde5516354a73bd309dc1698b3d3fd22fe285dd Mon Sep 17 00:00:00 2001 From: vincent Bourgmayer Date: Wed, 2 Mar 2022 09:43:04 +0100 Subject: [PATCH 08/68] clean dependencies in app/build.gradle - replace all androidTestImplementation and testImplementation library into androidx - reorganize dependencies to make them cleaner fix typo for 'androidx.test:rules' dependencies --- app/build.gradle | 27 +++++++++------------------ 1 file changed, 9 insertions(+), 18 deletions(-) diff --git a/app/build.gradle b/app/build.gradle index 44a0e1de..290ae60d 100644 --- a/app/build.gradle +++ b/app/build.gradle @@ -53,32 +53,23 @@ android { //includeAndroidResources = true } } - } dependencies { + api project(':NextcloudLib') implementation fileTree(include: ['*.jar'], dir: 'libs') - //implementation 'com.android.support:appcompat-v7:26.1.0' - - androidTestImplementation 'com.android.support.test:runner:1.0.2' - androidTestImplementation 'com.android.support.test:rules:1.0.2' - androidTestImplementation 'androidx.annotation:annotation:1.3.0' - androidTestImplementation 'com.android.support.test.espresso:espresso-core:3.0.1' api 'androidx.annotation:annotation:1.3.0' - api project(':NextcloudLib') - //start to add lib for test - 1/4/21 - //@TODO: add junit runner as lib for testImplementation + androidTestImplementation 'androidx.test:runner:1.4.0' + androidTestImplementation 'androidx.test:rules:1.4.0' + androidTestImplementation 'androidx.annotation:annotation:1.3.0' + androidTestImplementation 'androidx.test.espresso:espresso-core:3.4.0' + androidTestImplementation 'junit:junit:4.12' - testImplementation 'com.android.support.test:runner:1.0.2' - testImplementation 'com.android.support.test:rules:1.0.2' + testImplementation 'androidx.test:runner:1.4.0' + testImplementation 'androidx.test:rules:1.4.0' testImplementation 'junit:junit:4.12' - //testImplementation 'org.robolectric:robolectric:4.4' //need AndroidX - testImplementation "org.robolectric:robolectric:3.8" + testImplementation 'org.robolectric:robolectric:4.4' testImplementation('org.mockito:mockito-inline:3.4.0') - - //testImplementation Libs.AndroidX.Test.archCoreTesting //TODO: replace by not android X version - //implementation 'androidx.localbroadcastmanager:localbroadcastmanager:1.0.0' - androidTestImplementation 'junit:junit:4.12' } -- GitLab From e3cb7f0198e68b3f24ca761a6f17dce0d9939ac3 Mon Sep 17 00:00:00 2001 From: vincent Bourgmayer Date: Wed, 2 Mar 2022 09:44:47 +0100 Subject: [PATCH 09/68] add jetpack's work dependency --- app/build.gradle | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/app/build.gradle b/app/build.gradle index 290ae60d..018d91fd 100644 --- a/app/build.gradle +++ b/app/build.gradle @@ -61,6 +61,10 @@ dependencies { implementation fileTree(include: ['*.jar'], dir: 'libs') api 'androidx.annotation:annotation:1.3.0' + def work_version = "2.7.1" + // (Java only) + implementation "androidx.work:work-runtime:$work_version" + androidTestImplementation 'androidx.test:runner:1.4.0' androidTestImplementation 'androidx.test:rules:1.4.0' androidTestImplementation 'androidx.annotation:annotation:1.3.0' -- GitLab From 5aa1a30996708a7562d833c4f34c1810fef36535 Mon Sep 17 00:00:00 2001 From: vincent Bourgmayer Date: Thu, 3 Mar 2022 10:13:03 +0100 Subject: [PATCH 10/68] Add new foundation.e.drive.work.FullScanWorker.java - Add new class FullScanWorker.java to be used as replacement of jobs.ScannerJob --- .../e/drive/work/FullScanWorker.java | 45 +++++++++++++++++++ 1 file changed, 45 insertions(+) create mode 100644 app/src/main/java/foundation/e/drive/work/FullScanWorker.java diff --git a/app/src/main/java/foundation/e/drive/work/FullScanWorker.java b/app/src/main/java/foundation/e/drive/work/FullScanWorker.java new file mode 100644 index 00000000..dd538e1e --- /dev/null +++ b/app/src/main/java/foundation/e/drive/work/FullScanWorker.java @@ -0,0 +1,45 @@ +/* + * Copyright © Vincent Bourgmayer (/e/ foundation). + * 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.content.Intent; +import android.util.Log; + +import androidx.annotation.NonNull; +import androidx.work.Worker; +import androidx.work.WorkerParameters; + +import foundation.e.drive.services.ObserverService; + +/** + * As a first step, this class must replace foundation.e.drive.jobs.ScannerJob + * in order to allow to use Jetpack Work API + * + * In further development it will be a part of Workers that will replace ObserverService + * I will update this header accordingly + * + * @author Vincent Bourgmayer + */ +public class FullScanWorker extends Worker { + final private static String TAG = FullScanWorker.class.getSimpleName(); + + public FullScanWorker(@NonNull Context context, @NonNull WorkerParameters workerParams) { + super(context, workerParams); + } + + @NonNull + @Override + public Result doWork() { + Log.d(TAG, "doWork(): going to send intent to ObserverService"); + Intent observerServiceIntent = new Intent(this.getApplicationContext(), ObserverService.class); + this.getApplicationContext().startService(observerServiceIntent); + return Result.success(); + } +} -- GitLab From 18d4cd547caad20c7cfdaeeae254e9f98c090c30 Mon Sep 17 00:00:00 2001 From: vincent Bourgmayer Date: Thu, 3 Mar 2022 10:20:53 +0100 Subject: [PATCH 11/68] add generic tag for worker in utils.AppConstants.java --- app/src/main/java/foundation/e/drive/utils/AppConstants.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/app/src/main/java/foundation/e/drive/utils/AppConstants.java b/app/src/main/java/foundation/e/drive/utils/AppConstants.java index 1e180424..7506a525 100644 --- a/app/src/main/java/foundation/e/drive/utils/AppConstants.java +++ b/app/src/main/java/foundation/e/drive/utils/AppConstants.java @@ -40,7 +40,7 @@ public abstract class AppConstants { public final static String notificationChannelID ="3310"; public final static String notificationChannelName="eDrive channel"; - + public final static String WORK_GENERIC_TAG="eDrive"; public final static String USER_AGENT = "eos("+getBuildTime()+")-eDrive("+ BuildConfig.VERSION_NAME +")"; -- GitLab From 4ee407908cddfb09133922798552057c015a5920 Mon Sep 17 00:00:00 2001 From: vincent Bourgmayer Date: Thu, 3 Mar 2022 10:26:03 +0100 Subject: [PATCH 12/68] clean InitializerService.java - remove useless comment - remove useless import --- .../e/drive/services/InitializerService.java | 13 +++---------- 1 file changed, 3 insertions(+), 10 deletions(-) diff --git a/app/src/main/java/foundation/e/drive/services/InitializerService.java b/app/src/main/java/foundation/e/drive/services/InitializerService.java index 289229b2..89ae39f4 100644 --- a/app/src/main/java/foundation/e/drive/services/InitializerService.java +++ b/app/src/main/java/foundation/e/drive/services/InitializerService.java @@ -36,7 +36,6 @@ import foundation.e.drive.receivers.ScreenOffReceiver; import foundation.e.drive.utils.AppConstants; import foundation.e.drive.utils.CommonUtils; import foundation.e.drive.utils.JobUtils; -import foundation.e.drive.utils.ServiceExceptionHandler; import static com.owncloud.android.lib.resources.files.FileUtils.PATH_SEPARATOR; import static foundation.e.drive.utils.AppConstants.INITIALFOLDERS_NUMBER; @@ -50,9 +49,9 @@ import androidx.annotation.Nullable; */ public class InitializerService extends Service implements OnRemoteOperationListener { final private String TAG = InitializerService.class.getSimpleName(); - //Complex properties - private int existingRemoteFolderCounter; //@dev-only; Temporarily used to know if all remotePath exist - private List mSyncedFolders; //syncedFolders + + private int existingRemoteFolderCounter; //Temporarily used to know if all remotePath exist + private List mSyncedFolders; private OwnCloudClient mCloudClient; private Handler mHandler; private Account mAccount; @@ -63,7 +62,6 @@ public class InitializerService extends Service implements OnRemoteOperationList Log.i(TAG, "onCreate()"); super.onCreate(); this.existingRemoteFolderCounter = 0; - //JobUtils.scheduleInitializerJob(getApplicationContext()); } @Override @@ -86,7 +84,6 @@ public class InitializerService extends Service implements OnRemoteOperationList accountName = intent.getExtras().getString( AccountManager.KEY_ACCOUNT_NAME, "" ); accountType = intent.getExtras().getString( AccountManager.KEY_ACCOUNT_TYPE, "" ); - //If data come from intent, store them into pref because there aren't stored prefs.edit().putString( AccountManager.KEY_ACCOUNT_NAME, accountName ) .putString( AccountManager.KEY_ACCOUNT_TYPE, accountType ) .apply(); @@ -94,7 +91,6 @@ public class InitializerService extends Service implements OnRemoteOperationList if(accountName.isEmpty() ) { Log.w(TAG, "Account's name not found. Neither in shared prefs nor in intent's extras"); - //JobUtils.stopScheduledJob(getApplicationContext(), JobUtils.InitializerJobId); stopSelf(); }else{ this.mAccount = CommonUtils.getAccount( accountName, accountType, AccountManager.get(this) ); @@ -123,7 +119,6 @@ public class InitializerService extends Service implements OnRemoteOperationList return; } - //Get categories of element to sync List syncCategories = new ArrayList<>(); if (CommonUtils.isMediaSyncEnabled(mAccount)) { @@ -134,7 +129,6 @@ public class InitializerService extends Service implements OnRemoteOperationList syncCategories.addAll(Arrays.asList(SETTINGS_SYNCABLE_CATEGORIES)); } - //Get SyncedFolders getInitialSyncedFolders(syncCategories); this.existingRemoteFolderCounter = 0; @@ -213,7 +207,6 @@ public class InitializerService extends Service implements OnRemoteOperationList this.restartFolderCreationCounter = 0; if( this.mSyncedFolders == null || this.mSyncedFolders.isEmpty() ){ - //JobUtils.stopScheduledJob(getApplicationContext(), JobUtils.InitializerJobId); this.stopSelf(); } -- GitLab From fabf2ff8aa753e162d83dfb58ae49384f784e131 Mon Sep 17 00:00:00 2001 From: vincent Bourgmayer Date: Thu, 3 Mar 2022 10:27:22 +0100 Subject: [PATCH 13/68] Add usage of WorkManager to enqueue FullScanWorker in InitializerService - Instanciate Constraint for the workRequest (Battery not low, and network available) - Instanciate PeriodicWorkRequest to start FullScanWorker job every 31 minutes with 5 minutes as flex time - Enqueue the workRequest into WorkManager - Add the generic Generic work Tag into the work request - Comment "JobUtils.scheduleScannerJob()" to prevent JobScheduler to run ScannerJob periodically --- .../e/drive/services/InitializerService.java | 24 ++++++++++++++++++- 1 file changed, 23 insertions(+), 1 deletion(-) diff --git a/app/src/main/java/foundation/e/drive/services/InitializerService.java b/app/src/main/java/foundation/e/drive/services/InitializerService.java index 89ae39f4..e3cd4390 100644 --- a/app/src/main/java/foundation/e/drive/services/InitializerService.java +++ b/app/src/main/java/foundation/e/drive/services/InitializerService.java @@ -29,6 +29,7 @@ import com.owncloud.android.lib.common.operations.RemoteOperationResult; import java.util.ArrayList; import java.util.Arrays; import java.util.List; +import java.util.concurrent.TimeUnit; import foundation.e.drive.models.SyncedFolder; import foundation.e.drive.operations.CreateInitialFolderRemoteOperation; @@ -36,6 +37,7 @@ import foundation.e.drive.receivers.ScreenOffReceiver; import foundation.e.drive.utils.AppConstants; import foundation.e.drive.utils.CommonUtils; import foundation.e.drive.utils.JobUtils; +import foundation.e.drive.work.FullScanWorker; import static com.owncloud.android.lib.resources.files.FileUtils.PATH_SEPARATOR; import static foundation.e.drive.utils.AppConstants.INITIALFOLDERS_NUMBER; @@ -43,6 +45,11 @@ import static foundation.e.drive.utils.AppConstants.MEDIA_SYNCABLE_CATEGORIES; import static foundation.e.drive.utils.AppConstants.SETTINGS_SYNCABLE_CATEGORIES; import androidx.annotation.Nullable; +import androidx.work.Constraints; +import androidx.work.NetworkType; +import androidx.work.PeriodicWorkRequest; +import androidx.work.WorkManager; +import androidx.work.WorkRequest; /** * @author Vincent Bourgmayer @@ -277,9 +284,24 @@ public class InitializerService extends Service implements OnRemoteOperationList .putInt( INITIALFOLDERS_NUMBER, mSyncedFolders.size() ) .apply(); + Constraints constraints = new Constraints.Builder() + .setRequiredNetworkType(NetworkType.CONNECTED) + .setRequiresBatteryNotLow(true) + .build(); + + WorkRequest periodicFullScanRequest = + new PeriodicWorkRequest.Builder(FullScanWorker.class, + 31, TimeUnit.MINUTES, + 5, TimeUnit.MINUTES) + .setConstraints(constraints) + .addTag(AppConstants.WORK_GENERIC_TAG) + .build(); + + WorkManager.getInstance(this).enqueue(periodicFullScanRequest); + //all folder have been created //JobUtils.stopScheduledJob(appContext, JobUtils.InitializerJobId); - JobUtils.scheduleScannerJob(appContext); + //JobUtils.scheduleScannerJob(appContext); Log.d(TAG, "RegisterReceiver: screenOffReceiver"); IntentFilter filter = new IntentFilter(Intent.ACTION_SCREEN_ON); -- GitLab From b10255e0ef757b04fb7f59322231bf4d57ba4a7e Mon Sep 17 00:00:00 2001 From: vincent Bourgmayer Date: Thu, 3 Mar 2022 10:43:34 +0100 Subject: [PATCH 14/68] Cancel all Work Enqueued when user account is removed and optimize WorkRequest registration - Cancel all work enqueued using the work generic tag in ResetService.java - Add method "RegisterPeriodicFullScanWorker() in CommonUtils - Replace code in InitializerService by CommonUtils.RegisterPeriodicFullScanWorker - Remove Remove unused import --- .../e/drive/services/InitializerService.java | 28 ++++-------------- .../e/drive/services/ResetService.java | 4 +++ .../foundation/e/drive/utils/CommonUtils.java | 29 ++++++++++++++++++- .../e/drive/work/FullScanWorker.java | 1 + 4 files changed, 38 insertions(+), 24 deletions(-) diff --git a/app/src/main/java/foundation/e/drive/services/InitializerService.java b/app/src/main/java/foundation/e/drive/services/InitializerService.java index e3cd4390..11c48174 100644 --- a/app/src/main/java/foundation/e/drive/services/InitializerService.java +++ b/app/src/main/java/foundation/e/drive/services/InitializerService.java @@ -11,10 +11,12 @@ package foundation.e.drive.services; import android.accounts.Account; import android.accounts.AccountManager; import android.app.Service; +import android.content.ContentResolver; import android.content.Context; import android.content.Intent; import android.content.IntentFilter; import android.content.SharedPreferences; +import android.net.Uri; import android.os.Build; import android.os.Environment; import android.os.Handler; @@ -26,10 +28,10 @@ import com.owncloud.android.lib.common.operations.OnRemoteOperationListener; import com.owncloud.android.lib.common.operations.RemoteOperation; import com.owncloud.android.lib.common.operations.RemoteOperationResult; + import java.util.ArrayList; import java.util.Arrays; import java.util.List; -import java.util.concurrent.TimeUnit; import foundation.e.drive.models.SyncedFolder; import foundation.e.drive.operations.CreateInitialFolderRemoteOperation; @@ -37,7 +39,6 @@ import foundation.e.drive.receivers.ScreenOffReceiver; import foundation.e.drive.utils.AppConstants; import foundation.e.drive.utils.CommonUtils; import foundation.e.drive.utils.JobUtils; -import foundation.e.drive.work.FullScanWorker; import static com.owncloud.android.lib.resources.files.FileUtils.PATH_SEPARATOR; import static foundation.e.drive.utils.AppConstants.INITIALFOLDERS_NUMBER; @@ -45,11 +46,7 @@ import static foundation.e.drive.utils.AppConstants.MEDIA_SYNCABLE_CATEGORIES; import static foundation.e.drive.utils.AppConstants.SETTINGS_SYNCABLE_CATEGORIES; import androidx.annotation.Nullable; -import androidx.work.Constraints; -import androidx.work.NetworkType; -import androidx.work.PeriodicWorkRequest; import androidx.work.WorkManager; -import androidx.work.WorkRequest; /** * @author Vincent Bourgmayer @@ -275,8 +272,7 @@ public class InitializerService extends Service implements OnRemoteOperationList **/ private void doLastStep(){ Log.i(TAG, "doLastStep()"); - - Context appContext = getApplicationContext(); + final Context appContext = getApplicationContext(); appContext.getSharedPreferences( AppConstants.SHARED_PREFERENCE_NAME, Context.MODE_PRIVATE ) .edit() @@ -284,20 +280,7 @@ public class InitializerService extends Service implements OnRemoteOperationList .putInt( INITIALFOLDERS_NUMBER, mSyncedFolders.size() ) .apply(); - Constraints constraints = new Constraints.Builder() - .setRequiredNetworkType(NetworkType.CONNECTED) - .setRequiresBatteryNotLow(true) - .build(); - - WorkRequest periodicFullScanRequest = - new PeriodicWorkRequest.Builder(FullScanWorker.class, - 31, TimeUnit.MINUTES, - 5, TimeUnit.MINUTES) - .setConstraints(constraints) - .addTag(AppConstants.WORK_GENERIC_TAG) - .build(); - - WorkManager.getInstance(this).enqueue(periodicFullScanRequest); + CommonUtils.registerPeriodicFullScanWorker(WorkManager.getInstance(appContext)); //all folder have been created //JobUtils.stopScheduledJob(appContext, JobUtils.InitializerJobId); @@ -314,7 +297,6 @@ public class InitializerService extends Service implements OnRemoteOperationList stopSelf(); - } @Override diff --git a/app/src/main/java/foundation/e/drive/services/ResetService.java b/app/src/main/java/foundation/e/drive/services/ResetService.java index dad3cb20..e0e1bb53 100644 --- a/app/src/main/java/foundation/e/drive/services/ResetService.java +++ b/app/src/main/java/foundation/e/drive/services/ResetService.java @@ -27,6 +27,7 @@ import static foundation.e.drive.utils.AppConstants.INITIALFOLDERS_NUMBER; import static foundation.e.drive.utils.AppConstants.INITIALIZATION_HAS_BEEN_DONE; import androidx.annotation.Nullable; +import androidx.work.WorkManager; /** * @author Vincent Bourgmayer @@ -67,6 +68,9 @@ public class ResetService extends Service { //2. Stop the scheduledJob JobUtils.stopScheduledJob(this, JobUtils.ScannerJobId); //JobUtils.stopScheduledJob(this, JobUtils.InitializerJobId); + + WorkManager.getInstance(this).cancelAllWorkByTag(AppConstants.WORK_GENERIC_TAG); + //3. delete DB result = this.deleteDatabase(DbHelper.DATABASE_NAME); Log.d(TAG, "Remove Database: "+result); 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 e0f762fc..59406ac8 100644 --- a/app/src/main/java/foundation/e/drive/utils/CommonUtils.java +++ b/app/src/main/java/foundation/e/drive/utils/CommonUtils.java @@ -17,7 +17,6 @@ import android.content.ContentResolver; import android.content.Context; import android.media.MediaScannerConnection; import android.net.ConnectivityManager; -import android.net.Network; import android.net.NetworkInfo; import android.net.Uri; import android.util.Log; @@ -31,13 +30,20 @@ import com.owncloud.android.lib.common.accounts.AccountUtils; import com.owncloud.android.lib.resources.files.FileUtils; import java.io.File; +import java.util.concurrent.TimeUnit; import foundation.e.drive.receivers.ScreenOffReceiver; +import foundation.e.drive.work.FullScanWorker; import static foundation.e.drive.utils.AppConstants.MEDIASYNC_PROVIDER_AUTHORITY; import static foundation.e.drive.utils.AppConstants.SETTINGSYNC_PROVIDER_AUTHORITY; import androidx.annotation.NonNull; +import androidx.work.Constraints; +import androidx.work.ExistingPeriodicWorkPolicy; +import androidx.work.NetworkType; +import androidx.work.PeriodicWorkRequest; +import androidx.work.WorkManager; /** @@ -293,4 +299,25 @@ public abstract class CommonUtils { + "\n File can be read?: " + f.canRead() + "\n File can be written?: " + f.canWrite(); } + + /** + * Enqueue a unique periodic worker to look for file to be synchronized (remote files + local files + * @param workManager the instance of workManager + */ + public static void registerPeriodicFullScanWorker(WorkManager workManager){ + final Constraints constraints = new Constraints.Builder() + .setRequiredNetworkType(NetworkType.CONNECTED) + .setRequiresBatteryNotLow(true) + .build(); + + final PeriodicWorkRequest periodicFullScanRequest = + new PeriodicWorkRequest.Builder(FullScanWorker.class, + 31, TimeUnit.MINUTES, + 5, TimeUnit.MINUTES) + .setConstraints(constraints) + .addTag(AppConstants.WORK_GENERIC_TAG) + .build(); + + workManager.enqueueUniquePeriodicWork(FullScanWorker.UNIQUE_WORK_NAME , ExistingPeriodicWorkPolicy.KEEP, periodicFullScanRequest); + } } \ No newline at end of file diff --git a/app/src/main/java/foundation/e/drive/work/FullScanWorker.java b/app/src/main/java/foundation/e/drive/work/FullScanWorker.java index dd538e1e..d05b7b6c 100644 --- a/app/src/main/java/foundation/e/drive/work/FullScanWorker.java +++ b/app/src/main/java/foundation/e/drive/work/FullScanWorker.java @@ -29,6 +29,7 @@ import foundation.e.drive.services.ObserverService; */ public class FullScanWorker extends Worker { final private static String TAG = FullScanWorker.class.getSimpleName(); + final public static String UNIQUE_WORK_NAME="FullScan"; public FullScanWorker(@NonNull Context context, @NonNull WorkerParameters workerParams) { super(context, workerParams); -- GitLab From b270cfa146bc3e07e836a39e4187cf21e25a6cdc Mon Sep 17 00:00:00 2001 From: vincent Bourgmayer Date: Thu, 3 Mar 2022 17:57:29 +0100 Subject: [PATCH 15/68] FullScanWorker doesn't send intent to ObserverService when synchronization is disabled --- .../e/drive/work/FullScanWorker.java | 21 +++++++++++++++++-- 1 file changed, 19 insertions(+), 2 deletions(-) diff --git a/app/src/main/java/foundation/e/drive/work/FullScanWorker.java b/app/src/main/java/foundation/e/drive/work/FullScanWorker.java index d05b7b6c..b67151c8 100644 --- a/app/src/main/java/foundation/e/drive/work/FullScanWorker.java +++ b/app/src/main/java/foundation/e/drive/work/FullScanWorker.java @@ -8,8 +8,11 @@ package foundation.e.drive.work; +import android.accounts.Account; +import android.accounts.AccountManager; import android.content.Context; import android.content.Intent; +import android.content.SharedPreferences; import android.util.Log; import androidx.annotation.NonNull; @@ -17,6 +20,8 @@ import androidx.work.Worker; import androidx.work.WorkerParameters; import foundation.e.drive.services.ObserverService; +import foundation.e.drive.utils.AppConstants; +import foundation.e.drive.utils.CommonUtils; /** * As a first step, this class must replace foundation.e.drive.jobs.ScannerJob @@ -39,8 +44,20 @@ public class FullScanWorker extends Worker { @Override public Result doWork() { Log.d(TAG, "doWork(): going to send intent to ObserverService"); - Intent observerServiceIntent = new Intent(this.getApplicationContext(), ObserverService.class); - this.getApplicationContext().startService(observerServiceIntent); + final SharedPreferences prefs = getApplicationContext().getSharedPreferences(AppConstants.SHARED_PREFERENCE_NAME, + Context.MODE_PRIVATE ); + final String accountName = prefs.getString( AccountManager.KEY_ACCOUNT_NAME, "" ); + final String accountType = prefs.getString( AccountManager.KEY_ACCOUNT_TYPE, "" ); + + final Account mAccount = CommonUtils.getAccount( accountName, accountType, AccountManager.get(this.getApplicationContext()) ); + + if(mAccount != null && CommonUtils.isSettingsSyncEnabled(mAccount) && CommonUtils.isMediaSyncEnabled(mAccount)) { + final Intent observerServiceIntent = new Intent(this.getApplicationContext(), ObserverService.class); + this.getApplicationContext().startService(observerServiceIntent); + }else{ + Log.w(TAG, "Intent for ObserverService not send : account is null or \"settings sync\" & \"media sync\" settings are disabled"); + } + return Result.success(); } } -- GitLab From 840bf519279aa532c8e8405ef6af89cf0d8d7902 Mon Sep 17 00:00:00 2001 From: vincent Bourgmayer Date: Fri, 4 Mar 2022 10:07:26 +0100 Subject: [PATCH 16/68] fix spacing in if/else statement and fix 'final' word placement --- .../e/drive/services/InitializerService.java | 38 +++++++++---------- .../foundation/e/drive/utils/CommonUtils.java | 4 +- .../e/drive/work/FullScanWorker.java | 10 ++--- 3 files changed, 25 insertions(+), 27 deletions(-) diff --git a/app/src/main/java/foundation/e/drive/services/InitializerService.java b/app/src/main/java/foundation/e/drive/services/InitializerService.java index 11c48174..8939223c 100644 --- a/app/src/main/java/foundation/e/drive/services/InitializerService.java +++ b/app/src/main/java/foundation/e/drive/services/InitializerService.java @@ -11,12 +11,10 @@ package foundation.e.drive.services; import android.accounts.Account; import android.accounts.AccountManager; import android.app.Service; -import android.content.ContentResolver; import android.content.Context; import android.content.Intent; import android.content.IntentFilter; import android.content.SharedPreferences; -import android.net.Uri; import android.os.Build; import android.os.Environment; import android.os.Handler; @@ -52,7 +50,7 @@ import androidx.work.WorkManager; * @author Vincent Bourgmayer */ public class InitializerService extends Service implements OnRemoteOperationListener { - final private String TAG = InitializerService.class.getSimpleName(); + private final String TAG = InitializerService.class.getSimpleName(); private int existingRemoteFolderCounter; //Temporarily used to know if all remotePath exist private List mSyncedFolders; @@ -76,10 +74,10 @@ public class InitializerService extends Service implements OnRemoteOperationList //Get account SharedPreferences prefs = this.getSharedPreferences( AppConstants.SHARED_PREFERENCE_NAME, Context.MODE_PRIVATE ); - if( prefs.getBoolean( AppConstants.INITIALIZATION_HAS_BEEN_DONE, false ) ) { + if (prefs.getBoolean( AppConstants.INITIALIZATION_HAS_BEEN_DONE, false ) ) { JobUtils.scheduleScannerJob(this); Log.w(TAG, "Initializer has already been run"); - }else{ + } else { String accountName = prefs.getString( AccountManager.KEY_ACCOUNT_NAME, "" ); String accountType = prefs.getString( AccountManager.KEY_ACCOUNT_TYPE, "" ); @@ -93,16 +91,16 @@ public class InitializerService extends Service implements OnRemoteOperationList .apply(); } - if(accountName.isEmpty() ) { + if (accountName.isEmpty() ) { Log.w(TAG, "Account's name not found. Neither in shared prefs nor in intent's extras"); stopSelf(); - }else{ + } else { this.mAccount = CommonUtils.getAccount( accountName, accountType, AccountManager.get(this) ); //Get OwnCloudlient if (this.mAccount != null) { this.mCloudClient = CommonUtils.getOwnCloudClient( this.mAccount, getApplicationContext()); start(); - }else { + } else { Log.w(TAG, "Got account is invalid."); stopSelf(); } @@ -193,7 +191,7 @@ public class InitializerService extends Service implements OnRemoteOperationList false, true, false) ); - }catch (Exception e){ Log.e(TAG, e.toString()); } + } catch (Exception e){ Log.e(TAG, e.toString()); } break; } } @@ -210,14 +208,14 @@ public class InitializerService extends Service implements OnRemoteOperationList Log.i(TAG, "createNextRemoteFolder()"); this.restartFolderCreationCounter = 0; - if( this.mSyncedFolders == null || this.mSyncedFolders.isEmpty() ){ + if (this.mSyncedFolders == null || this.mSyncedFolders.isEmpty() ){ this.stopSelf(); } //It means that there are still folders to create - if( this.existingRemoteFolderCounter < this.mSyncedFolders.size() ){ + if (this.existingRemoteFolderCounter < this.mSyncedFolders.size() ){ - if( this.mHandler == null ) this.mHandler = new Handler(); + if (this.mHandler == null ) this.mHandler = new Handler(); CreateInitialFolderRemoteOperation createFolderOperation = new CreateInitialFolderRemoteOperation( @@ -227,11 +225,11 @@ public class InitializerService extends Service implements OnRemoteOperationList createFolderOperation.execute(this.mCloudClient, this, this.mHandler); - }else if(this.existingRemoteFolderCounter == this.mSyncedFolders.size() ){ + } else if (this.existingRemoteFolderCounter == this.mSyncedFolders.size() ){ doLastStep(); - }else{ + } else { Log.e(TAG, "this.existingRemoteFolderCounter : "+this.existingRemoteFolderCounter+" > this.mSyncedFolders.size() : "+this.mSyncedFolders.size() ); this.stopSelf(); } @@ -240,26 +238,26 @@ public class InitializerService extends Service implements OnRemoteOperationList @Override public void onRemoteOperationFinish(RemoteOperation operation, RemoteOperationResult result) { Log.i(TAG, "onRemoteOperationFinish()"); - if(operation instanceof CreateInitialFolderRemoteOperation){ + if (operation instanceof CreateInitialFolderRemoteOperation){ - if(result.isSuccess() || result.getCode() == RemoteOperationResult.ResultCode.FOLDER_ALREADY_EXISTS){ + if (result.isSuccess() || result.getCode() == RemoteOperationResult.ResultCode.FOLDER_ALREADY_EXISTS){ this.existingRemoteFolderCounter+=1; CreateNextRemoteFolder(); - }else if( result.getHttpCode() == 423 || result.getHttpCode() == 409){//file locked or conflict in result + } else if (result.getHttpCode() == 423 || result.getHttpCode() == 409){//file locked or conflict in result Log.e( TAG, result.getLogMessage() ); - if( this.restartFolderCreationCounter < 3) { + if (this.restartFolderCreationCounter < 3) { Log.w( TAG, " restart operation" ); operation.execute( this.mCloudClient, this, this.mHandler ); this.restartFolderCreationCounter+=1; - }else{ + } else { Log.e(TAG, "Remote folder's creation failed due to conflict with server"); stopSelf(); } - }else{ + } else { Log.e(TAG, result.getLogMessage()+" "+result.getHttpCode() ); stopSelf(); } 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 59406ac8..5ba7a072 100644 --- a/app/src/main/java/foundation/e/drive/utils/CommonUtils.java +++ b/app/src/main/java/foundation/e/drive/utils/CommonUtils.java @@ -51,7 +51,7 @@ import androidx.work.WorkManager; */ public abstract class CommonUtils { - final private static String TAG = CommonUtils.class.getSimpleName(); + private final static String TAG = CommonUtils.class.getSimpleName(); /** * Set ServiceUncaughtExceptionHandler to be the MainThread Exception Handler @@ -169,7 +169,7 @@ public abstract class CommonUtils { oc.setCredentials(new OwnCloudBasicCredentials(account.name, AccountManager.get(context).getPassword(account))); Log.d(TAG, "user agent: "+AppConstants.USER_AGENT); - if(!AppConstants.USER_AGENT.equals(OwnCloudClientManagerFactory.getUserAgent())) { + if (!AppConstants.USER_AGENT.equals(OwnCloudClientManagerFactory.getUserAgent()) ){ OwnCloudClientManagerFactory.setUserAgent(AppConstants.USER_AGENT); } diff --git a/app/src/main/java/foundation/e/drive/work/FullScanWorker.java b/app/src/main/java/foundation/e/drive/work/FullScanWorker.java index b67151c8..08d5b875 100644 --- a/app/src/main/java/foundation/e/drive/work/FullScanWorker.java +++ b/app/src/main/java/foundation/e/drive/work/FullScanWorker.java @@ -33,8 +33,8 @@ import foundation.e.drive.utils.CommonUtils; * @author Vincent Bourgmayer */ public class FullScanWorker extends Worker { - final private static String TAG = FullScanWorker.class.getSimpleName(); - final public static String UNIQUE_WORK_NAME="FullScan"; + private final static String TAG = FullScanWorker.class.getSimpleName(); + public final static String UNIQUE_WORK_NAME="FullScan"; public FullScanWorker(@NonNull Context context, @NonNull WorkerParameters workerParams) { super(context, workerParams); @@ -51,13 +51,13 @@ public class FullScanWorker extends Worker { final Account mAccount = CommonUtils.getAccount( accountName, accountType, AccountManager.get(this.getApplicationContext()) ); - if(mAccount != null && CommonUtils.isSettingsSyncEnabled(mAccount) && CommonUtils.isMediaSyncEnabled(mAccount)) { + if (mAccount != null && CommonUtils.isSettingsSyncEnabled(mAccount) && CommonUtils.isMediaSyncEnabled(mAccount)) { final Intent observerServiceIntent = new Intent(this.getApplicationContext(), ObserverService.class); this.getApplicationContext().startService(observerServiceIntent); - }else{ + } else { Log.w(TAG, "Intent for ObserverService not send : account is null or \"settings sync\" & \"media sync\" settings are disabled"); } return Result.success(); } -} +} \ No newline at end of file -- GitLab From 4d67e91938243ed3b3000888c1433348051520a2 Mon Sep 17 00:00:00 2001 From: Vincent Bourgmayer Date: Fri, 4 Mar 2022 10:05:24 +0000 Subject: [PATCH 17/68] Remove JobUtils.java --- .../foundation/e/drive/EdriveApplication.java | 10 +-- .../drive/receivers/BatteryStateReceiver.java | 4 +- .../e/drive/services/InitializerService.java | 5 +- .../e/drive/services/ObserverService.java | 2 - .../e/drive/services/ResetService.java | 5 +- .../foundation/e/drive/utils/JobUtils.java | 70 ------------------- 6 files changed, 4 insertions(+), 92 deletions(-) delete mode 100644 app/src/main/java/foundation/e/drive/utils/JobUtils.java diff --git a/app/src/main/java/foundation/e/drive/EdriveApplication.java b/app/src/main/java/foundation/e/drive/EdriveApplication.java index de7bdcf7..b6f6ae35 100644 --- a/app/src/main/java/foundation/e/drive/EdriveApplication.java +++ b/app/src/main/java/foundation/e/drive/EdriveApplication.java @@ -17,7 +17,6 @@ import android.util.Log; import foundation.e.drive.utils.AppConstants; import foundation.e.drive.utils.CommonUtils; -import foundation.e.drive.utils.JobUtils; /** * Class representing the eDrive application. @@ -36,7 +35,7 @@ public class EdriveApplication extends Application { SharedPreferences prefs = getSharedPreferences(AppConstants.SHARED_PREFERENCE_NAME, Context.MODE_PRIVATE); if (prefs.getString(AccountManager.KEY_ACCOUNT_NAME, null) != null) { - scheduleScannerJob(); + Log.d(TAG, "Account already registered"); } else { Account mAccount = CommonUtils.getAccount(getString(R.string.eelo_account_type), AccountManager.get(this)); if (mAccount != null) { @@ -47,17 +46,10 @@ public class EdriveApplication extends Application { .putString(AccountManager.KEY_ACCOUNT_TYPE, accountType) .apply(); - scheduleScannerJob(); } } } - private void scheduleScannerJob() { - if (!JobUtils.isScannerJobRegistered(this)) { - JobUtils.scheduleScannerJob(this); - } - } - private void resetOperationManagerSetting() { getSharedPreferences(AppConstants.SHARED_PREFERENCE_NAME, Context.MODE_PRIVATE).edit() .putBoolean(AppConstants.KEY_OMS_IS_WORKING, false) diff --git a/app/src/main/java/foundation/e/drive/receivers/BatteryStateReceiver.java b/app/src/main/java/foundation/e/drive/receivers/BatteryStateReceiver.java index 2fd661d1..a634635b 100644 --- a/app/src/main/java/foundation/e/drive/receivers/BatteryStateReceiver.java +++ b/app/src/main/java/foundation/e/drive/receivers/BatteryStateReceiver.java @@ -12,7 +12,6 @@ import android.content.BroadcastReceiver; import android.content.Context; import android.content.Intent; import android.util.Log; -import foundation.e.drive.utils.JobUtils; /** * @author Vincent Bourgmayer @@ -28,9 +27,8 @@ public class BatteryStateReceiver extends BroadcastReceiver { if(intentAction == null) { Log.e(TAG, "intent Action is null"); } else if ( intentAction.equals(Intent.ACTION_BATTERY_OKAY) ) { - JobUtils.scheduleScannerJob(context); + Log.d(TAG, "BATTERY is Okay. Synchronization job isn't reschedule anymore. Expected behaviour"); }else if(intentAction.equals(Intent.ACTION_BATTERY_LOW)){ - JobUtils.stopScheduledJob(context, JobUtils.ScannerJobId); try { context.unregisterReceiver(ScreenOffReceiver.getInstance()); }catch(Exception e){ diff --git a/app/src/main/java/foundation/e/drive/services/InitializerService.java b/app/src/main/java/foundation/e/drive/services/InitializerService.java index 289229b2..134063f2 100644 --- a/app/src/main/java/foundation/e/drive/services/InitializerService.java +++ b/app/src/main/java/foundation/e/drive/services/InitializerService.java @@ -35,7 +35,6 @@ import foundation.e.drive.operations.CreateInitialFolderRemoteOperation; import foundation.e.drive.receivers.ScreenOffReceiver; import foundation.e.drive.utils.AppConstants; import foundation.e.drive.utils.CommonUtils; -import foundation.e.drive.utils.JobUtils; import foundation.e.drive.utils.ServiceExceptionHandler; import static com.owncloud.android.lib.resources.files.FileUtils.PATH_SEPARATOR; @@ -75,7 +74,6 @@ public class InitializerService extends Service implements OnRemoteOperationList SharedPreferences prefs = this.getSharedPreferences( AppConstants.SHARED_PREFERENCE_NAME, Context.MODE_PRIVATE ); if( prefs.getBoolean( AppConstants.INITIALIZATION_HAS_BEEN_DONE, false ) ) { - JobUtils.scheduleScannerJob(this); Log.w(TAG, "Initializer has already been run"); }else{ String accountName = prefs.getString( AccountManager.KEY_ACCOUNT_NAME, "" ); @@ -285,8 +283,7 @@ public class InitializerService extends Service implements OnRemoteOperationList .apply(); //all folder have been created - //JobUtils.stopScheduledJob(appContext, JobUtils.InitializerJobId); - JobUtils.scheduleScannerJob(appContext); + Log.d(TAG, "RegisterReceiver: screenOffReceiver"); IntentFilter filter = new IntentFilter(Intent.ACTION_SCREEN_ON); diff --git a/app/src/main/java/foundation/e/drive/services/ObserverService.java b/app/src/main/java/foundation/e/drive/services/ObserverService.java index c90b4167..a0e8c7eb 100644 --- a/app/src/main/java/foundation/e/drive/services/ObserverService.java +++ b/app/src/main/java/foundation/e/drive/services/ObserverService.java @@ -49,7 +49,6 @@ import foundation.e.drive.receivers.ForceSyncReceiver; import foundation.e.drive.utils.AppConstants; import foundation.e.drive.utils.CommonUtils; import foundation.e.drive.utils.DavClientProvider; -import foundation.e.drive.utils.JobUtils; import foundation.e.drive.utils.ServiceExceptionHandler; import static com.owncloud.android.lib.resources.files.FileUtils.PATH_SEPARATOR; @@ -95,7 +94,6 @@ public class ObserverService extends Service implements OnRemoteOperationListene // Check if account is invalid if(this.mAccount == null){ Log.w(TAG, "No account registered"); - JobUtils.stopScheduledJob(this, JobUtils.ScannerJobId); //If no account return super.onStartCommand(intent, flags, startId); } diff --git a/app/src/main/java/foundation/e/drive/services/ResetService.java b/app/src/main/java/foundation/e/drive/services/ResetService.java index dad3cb20..204f5341 100644 --- a/app/src/main/java/foundation/e/drive/services/ResetService.java +++ b/app/src/main/java/foundation/e/drive/services/ResetService.java @@ -21,7 +21,6 @@ import java.io.File; import foundation.e.drive.database.DbHelper; import foundation.e.drive.utils.AppConstants; import foundation.e.drive.utils.CommonUtils; -import foundation.e.drive.utils.JobUtils; import static foundation.e.drive.utils.AppConstants.INITIALFOLDERS_NUMBER; import static foundation.e.drive.utils.AppConstants.INITIALIZATION_HAS_BEEN_DONE; @@ -64,9 +63,7 @@ public class ResetService extends Service { result = getApplicationContext().stopService( stopperIntent ); Log.d(TAG, "stop OperationManagerService: "+result); - //2. Stop the scheduledJob - JobUtils.stopScheduledJob(this, JobUtils.ScannerJobId); - //JobUtils.stopScheduledJob(this, JobUtils.InitializerJobId); + //3. delete DB result = this.deleteDatabase(DbHelper.DATABASE_NAME); Log.d(TAG, "Remove Database: "+result); diff --git a/app/src/main/java/foundation/e/drive/utils/JobUtils.java b/app/src/main/java/foundation/e/drive/utils/JobUtils.java deleted file mode 100644 index 790427eb..00000000 --- a/app/src/main/java/foundation/e/drive/utils/JobUtils.java +++ /dev/null @@ -1,70 +0,0 @@ -/* - * Copyright © Vincent Bourgmayer (/e/ foundation). - * 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.utils; - -import android.app.job.JobInfo; -import android.app.job.JobScheduler; -import android.content.ComponentName; -import android.content.Context; -import android.util.Log; -import foundation.e.drive.jobs.ScannerJob; - - -/** - * @author Vincent Bourgmayer - */ - - -public abstract class JobUtils { - final private static String TAG = JobUtils.class.getSimpleName(); //Tag for log - public final static int ScannerJobId = 3310; - //public final static int InitializerJobId = 3311; - - /** - * Start the scheduledJob for observing remote's folder - * @param context app or service context - */ - public static void scheduleScannerJob(Context context){ - Log.i(TAG, "scheduleJob"); - /* I. Start periodic checkup */ - ComponentName jobService = new ComponentName( context, ScannerJob.class ); - - JobInfo job = new JobInfo.Builder(ScannerJobId, jobService ) - .setPeriodic(1860000, 30000) //31git minutes and 30 secondes - .setPersisted(true) - .setRequiredNetworkType( JobInfo.NETWORK_TYPE_ANY ) - .build(); - - JobScheduler jobScheduler = context.getSystemService( JobScheduler.class ); - - if ( jobScheduler.schedule( job ) == JobScheduler.RESULT_SUCCESS ) { - Log.d(TAG, "Scheduled job created"); - } else { - Log.e(TAG, "Scheduled job not created"); - } - } - - /** - * Try to stop the scheduledJob - * @param context - */ - public static void stopScheduledJob(Context context, int jobId){ - context.getSystemService( JobScheduler.class ).cancel(jobId); - } - - /** - * tell if the scannerJob is already registered or not - * @param context - * @return true if is registered, false either - */ - public static boolean isScannerJobRegistered(Context context){ - return (context.getSystemService( JobScheduler.class).getPendingJob(ScannerJobId) != null); - } - -} -- GitLab From c84bd3b8ced82c0c05b3324921579bd2df05566f Mon Sep 17 00:00:00 2001 From: vincent Bourgmayer Date: Wed, 2 Mar 2022 09:28:26 +0100 Subject: [PATCH 18/68] Update compileSDK in app/build.gradle and remove dependency - updated compileSdkVersion from 28 to 31 to allow WorkManager API support - removed dependency: implementation 'com.android.support:appcompat-v7:26.1.0' because it is useless in our scope. As stated at https://developer.android.com/topic/libraries/support-library/packages#v7-appcompat it only add ActionBar relative code. But eDrive doesn't use it for a while. --- app/build.gradle | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/app/build.gradle b/app/build.gradle index c65b01d7..44a0e1de 100644 --- a/app/build.gradle +++ b/app/build.gradle @@ -23,7 +23,7 @@ def getTestProp(String propName) { android { - compileSdkVersion 28 + compileSdkVersion 31 defaultConfig { applicationId "foundation.e.drive" minSdkVersion 26 @@ -59,7 +59,7 @@ android { dependencies { implementation fileTree(include: ['*.jar'], dir: 'libs') - implementation 'com.android.support:appcompat-v7:26.1.0' + //implementation 'com.android.support:appcompat-v7:26.1.0' androidTestImplementation 'com.android.support.test:runner:1.0.2' androidTestImplementation 'com.android.support.test:rules:1.0.2' -- GitLab From 49bbf04fa48585018c563db04e8a8bb8f8c6b2db Mon Sep 17 00:00:00 2001 From: vincent Bourgmayer Date: Wed, 2 Mar 2022 09:43:04 +0100 Subject: [PATCH 19/68] clean dependencies in app/build.gradle - replace all androidTestImplementation and testImplementation library into androidx - reorganize dependencies to make them cleaner --- app/build.gradle | 28 ++++++++++------------------ 1 file changed, 10 insertions(+), 18 deletions(-) diff --git a/app/build.gradle b/app/build.gradle index 44a0e1de..e2bfaed3 100644 --- a/app/build.gradle +++ b/app/build.gradle @@ -58,27 +58,19 @@ android { dependencies { + api project(':NextcloudLib') implementation fileTree(include: ['*.jar'], dir: 'libs') - //implementation 'com.android.support:appcompat-v7:26.1.0' - - androidTestImplementation 'com.android.support.test:runner:1.0.2' - androidTestImplementation 'com.android.support.test:rules:1.0.2' - androidTestImplementation 'androidx.annotation:annotation:1.3.0' - androidTestImplementation 'com.android.support.test.espresso:espresso-core:3.0.1' api 'androidx.annotation:annotation:1.3.0' - api project(':NextcloudLib') - - //start to add lib for test - 1/4/21 - //@TODO: add junit runner as lib for testImplementation + + androidTestImplementation 'androidx.test:runner:1.4.0' + androidTestImplementation 'androidx.test:rules:1.4.0 ' + androidTestImplementation 'androidx.annotation:annotation:1.3.0' + androidTestImplementation 'androidx.test.espresso:espresso-core:3.4.0' + androidTestImplementation 'junit:junit:4.12' - testImplementation 'com.android.support.test:runner:1.0.2' - testImplementation 'com.android.support.test:rules:1.0.2' + testImplementation 'androidx.test:runner:1.4.0' + testImplementation 'androidx.test:rules:1.4.0 ' testImplementation 'junit:junit:4.12' - //testImplementation 'org.robolectric:robolectric:4.4' //need AndroidX - testImplementation "org.robolectric:robolectric:3.8" + testImplementation 'org.robolectric:robolectric:4.4' testImplementation('org.mockito:mockito-inline:3.4.0') - - //testImplementation Libs.AndroidX.Test.archCoreTesting //TODO: replace by not android X version - //implementation 'androidx.localbroadcastmanager:localbroadcastmanager:1.0.0' - androidTestImplementation 'junit:junit:4.12' } -- GitLab From 8cfd1537a7392fc93150b018eb8fb5086eb66f3f Mon Sep 17 00:00:00 2001 From: vincent Bourgmayer Date: Wed, 2 Mar 2022 09:44:47 +0100 Subject: [PATCH 20/68] add jetpack's work dependency --- app/build.gradle | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/app/build.gradle b/app/build.gradle index e2bfaed3..510784e9 100644 --- a/app/build.gradle +++ b/app/build.gradle @@ -61,6 +61,10 @@ dependencies { api project(':NextcloudLib') implementation fileTree(include: ['*.jar'], dir: 'libs') api 'androidx.annotation:annotation:1.3.0' + + def work_version = "2.7.1" + // (Java only) + implementation "androidx.work:work-runtime:$work_version" androidTestImplementation 'androidx.test:runner:1.4.0' androidTestImplementation 'androidx.test:rules:1.4.0 ' -- GitLab From 32843030a820568f8f7a22222389a4429c025534 Mon Sep 17 00:00:00 2001 From: vincent Bourgmayer Date: Wed, 2 Mar 2022 09:50:16 +0100 Subject: [PATCH 21/68] fix typo for 'androidx.test:rules' dependencies --- app/build.gradle | 7 +++---- 1 file changed, 3 insertions(+), 4 deletions(-) diff --git a/app/build.gradle b/app/build.gradle index 510784e9..018d91fd 100644 --- a/app/build.gradle +++ b/app/build.gradle @@ -53,7 +53,6 @@ android { //includeAndroidResources = true } } - } @@ -65,15 +64,15 @@ dependencies { def work_version = "2.7.1" // (Java only) implementation "androidx.work:work-runtime:$work_version" - + androidTestImplementation 'androidx.test:runner:1.4.0' - androidTestImplementation 'androidx.test:rules:1.4.0 ' + androidTestImplementation 'androidx.test:rules:1.4.0' androidTestImplementation 'androidx.annotation:annotation:1.3.0' androidTestImplementation 'androidx.test.espresso:espresso-core:3.4.0' androidTestImplementation 'junit:junit:4.12' testImplementation 'androidx.test:runner:1.4.0' - testImplementation 'androidx.test:rules:1.4.0 ' + testImplementation 'androidx.test:rules:1.4.0' testImplementation 'junit:junit:4.12' testImplementation 'org.robolectric:robolectric:4.4' testImplementation('org.mockito:mockito-inline:3.4.0') -- GitLab From ffa4eaa907f41cec3847c4d688202d6c05246e24 Mon Sep 17 00:00:00 2001 From: vincent Bourgmayer Date: Wed, 2 Mar 2022 09:43:04 +0100 Subject: [PATCH 22/68] clean dependencies in app/build.gradle - replace all androidTestImplementation and testImplementation library into androidx - reorganize dependencies to make them cleaner fix typo for 'androidx.test:rules' dependencies --- app/build.gradle | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/app/build.gradle b/app/build.gradle index 018d91fd..60825e04 100644 --- a/app/build.gradle +++ b/app/build.gradle @@ -60,7 +60,7 @@ dependencies { api project(':NextcloudLib') implementation fileTree(include: ['*.jar'], dir: 'libs') api 'androidx.annotation:annotation:1.3.0' - + def work_version = "2.7.1" // (Java only) implementation "androidx.work:work-runtime:$work_version" -- GitLab From 5a537ca0e8c7f34ac98a629e5a93005895ecd651 Mon Sep 17 00:00:00 2001 From: vincent Bourgmayer Date: Wed, 2 Mar 2022 09:44:47 +0100 Subject: [PATCH 23/68] add jetpack's work dependency --- app/build.gradle | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/app/build.gradle b/app/build.gradle index 60825e04..63d73424 100644 --- a/app/build.gradle +++ b/app/build.gradle @@ -65,6 +65,10 @@ dependencies { // (Java only) implementation "androidx.work:work-runtime:$work_version" + def work_version = "2.7.1" + // (Java only) + implementation "androidx.work:work-runtime:$work_version" + androidTestImplementation 'androidx.test:runner:1.4.0' androidTestImplementation 'androidx.test:rules:1.4.0' androidTestImplementation 'androidx.annotation:annotation:1.3.0' -- GitLab From 01ae192b09ffcaad3a5728d783c0994c3dead7b4 Mon Sep 17 00:00:00 2001 From: vincent Bourgmayer Date: Thu, 3 Mar 2022 10:13:03 +0100 Subject: [PATCH 24/68] Add new foundation.e.drive.work.FullScanWorker.java - Add new class FullScanWorker.java to be used as replacement of jobs.ScannerJob --- .../e/drive/work/FullScanWorker.java | 45 +++++++++++++++++++ 1 file changed, 45 insertions(+) create mode 100644 app/src/main/java/foundation/e/drive/work/FullScanWorker.java diff --git a/app/src/main/java/foundation/e/drive/work/FullScanWorker.java b/app/src/main/java/foundation/e/drive/work/FullScanWorker.java new file mode 100644 index 00000000..dd538e1e --- /dev/null +++ b/app/src/main/java/foundation/e/drive/work/FullScanWorker.java @@ -0,0 +1,45 @@ +/* + * Copyright © Vincent Bourgmayer (/e/ foundation). + * 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.content.Intent; +import android.util.Log; + +import androidx.annotation.NonNull; +import androidx.work.Worker; +import androidx.work.WorkerParameters; + +import foundation.e.drive.services.ObserverService; + +/** + * As a first step, this class must replace foundation.e.drive.jobs.ScannerJob + * in order to allow to use Jetpack Work API + * + * In further development it will be a part of Workers that will replace ObserverService + * I will update this header accordingly + * + * @author Vincent Bourgmayer + */ +public class FullScanWorker extends Worker { + final private static String TAG = FullScanWorker.class.getSimpleName(); + + public FullScanWorker(@NonNull Context context, @NonNull WorkerParameters workerParams) { + super(context, workerParams); + } + + @NonNull + @Override + public Result doWork() { + Log.d(TAG, "doWork(): going to send intent to ObserverService"); + Intent observerServiceIntent = new Intent(this.getApplicationContext(), ObserverService.class); + this.getApplicationContext().startService(observerServiceIntent); + return Result.success(); + } +} -- GitLab From 6144d553c8cd075580421f6760e97b9138ecb7d3 Mon Sep 17 00:00:00 2001 From: vincent Bourgmayer Date: Thu, 3 Mar 2022 10:20:53 +0100 Subject: [PATCH 25/68] add generic tag for worker in utils.AppConstants.java --- app/src/main/java/foundation/e/drive/utils/AppConstants.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/app/src/main/java/foundation/e/drive/utils/AppConstants.java b/app/src/main/java/foundation/e/drive/utils/AppConstants.java index 1e180424..7506a525 100644 --- a/app/src/main/java/foundation/e/drive/utils/AppConstants.java +++ b/app/src/main/java/foundation/e/drive/utils/AppConstants.java @@ -40,7 +40,7 @@ public abstract class AppConstants { public final static String notificationChannelID ="3310"; public final static String notificationChannelName="eDrive channel"; - + public final static String WORK_GENERIC_TAG="eDrive"; public final static String USER_AGENT = "eos("+getBuildTime()+")-eDrive("+ BuildConfig.VERSION_NAME +")"; -- GitLab From 50a21cb2abf57c6a0975a0d4c3e787d634399f58 Mon Sep 17 00:00:00 2001 From: vincent Bourgmayer Date: Thu, 3 Mar 2022 10:26:03 +0100 Subject: [PATCH 26/68] clean InitializerService.java - remove useless comment - remove useless import --- .../e/drive/services/InitializerService.java | 12 +++--------- 1 file changed, 3 insertions(+), 9 deletions(-) diff --git a/app/src/main/java/foundation/e/drive/services/InitializerService.java b/app/src/main/java/foundation/e/drive/services/InitializerService.java index 134063f2..60f09fe6 100644 --- a/app/src/main/java/foundation/e/drive/services/InitializerService.java +++ b/app/src/main/java/foundation/e/drive/services/InitializerService.java @@ -49,9 +49,9 @@ import androidx.annotation.Nullable; */ public class InitializerService extends Service implements OnRemoteOperationListener { final private String TAG = InitializerService.class.getSimpleName(); - //Complex properties - private int existingRemoteFolderCounter; //@dev-only; Temporarily used to know if all remotePath exist - private List mSyncedFolders; //syncedFolders + + private int existingRemoteFolderCounter; //Temporarily used to know if all remotePath exist + private List mSyncedFolders; private OwnCloudClient mCloudClient; private Handler mHandler; private Account mAccount; @@ -62,7 +62,6 @@ public class InitializerService extends Service implements OnRemoteOperationList Log.i(TAG, "onCreate()"); super.onCreate(); this.existingRemoteFolderCounter = 0; - //JobUtils.scheduleInitializerJob(getApplicationContext()); } @Override @@ -84,7 +83,6 @@ public class InitializerService extends Service implements OnRemoteOperationList accountName = intent.getExtras().getString( AccountManager.KEY_ACCOUNT_NAME, "" ); accountType = intent.getExtras().getString( AccountManager.KEY_ACCOUNT_TYPE, "" ); - //If data come from intent, store them into pref because there aren't stored prefs.edit().putString( AccountManager.KEY_ACCOUNT_NAME, accountName ) .putString( AccountManager.KEY_ACCOUNT_TYPE, accountType ) .apply(); @@ -92,7 +90,6 @@ public class InitializerService extends Service implements OnRemoteOperationList if(accountName.isEmpty() ) { Log.w(TAG, "Account's name not found. Neither in shared prefs nor in intent's extras"); - //JobUtils.stopScheduledJob(getApplicationContext(), JobUtils.InitializerJobId); stopSelf(); }else{ this.mAccount = CommonUtils.getAccount( accountName, accountType, AccountManager.get(this) ); @@ -121,7 +118,6 @@ public class InitializerService extends Service implements OnRemoteOperationList return; } - //Get categories of element to sync List syncCategories = new ArrayList<>(); if (CommonUtils.isMediaSyncEnabled(mAccount)) { @@ -132,7 +128,6 @@ public class InitializerService extends Service implements OnRemoteOperationList syncCategories.addAll(Arrays.asList(SETTINGS_SYNCABLE_CATEGORIES)); } - //Get SyncedFolders getInitialSyncedFolders(syncCategories); this.existingRemoteFolderCounter = 0; @@ -211,7 +206,6 @@ public class InitializerService extends Service implements OnRemoteOperationList this.restartFolderCreationCounter = 0; if( this.mSyncedFolders == null || this.mSyncedFolders.isEmpty() ){ - //JobUtils.stopScheduledJob(getApplicationContext(), JobUtils.InitializerJobId); this.stopSelf(); } -- GitLab From e66b6083b874e4c2ab4aa35365fd8218421f03cb Mon Sep 17 00:00:00 2001 From: vincent Bourgmayer Date: Thu, 3 Mar 2022 10:27:22 +0100 Subject: [PATCH 27/68] Add usage of WorkManager to enqueue FullScanWorker in InitializerService - Instanciate Constraint for the workRequest (Battery not low, and network available) - Instanciate PeriodicWorkRequest to start FullScanWorker job every 31 minutes with 5 minutes as flex time - Enqueue the workRequest into WorkManager - Add the generic Generic work Tag into the work request - Comment "JobUtils.scheduleScannerJob()" to prevent JobScheduler to run ScannerJob periodically --- .../e/drive/services/InitializerService.java | 25 +++++++++++++++++-- 1 file changed, 23 insertions(+), 2 deletions(-) diff --git a/app/src/main/java/foundation/e/drive/services/InitializerService.java b/app/src/main/java/foundation/e/drive/services/InitializerService.java index 60f09fe6..2235a05f 100644 --- a/app/src/main/java/foundation/e/drive/services/InitializerService.java +++ b/app/src/main/java/foundation/e/drive/services/InitializerService.java @@ -29,13 +29,15 @@ import com.owncloud.android.lib.common.operations.RemoteOperationResult; import java.util.ArrayList; import java.util.Arrays; import java.util.List; +import java.util.concurrent.TimeUnit; import foundation.e.drive.models.SyncedFolder; import foundation.e.drive.operations.CreateInitialFolderRemoteOperation; import foundation.e.drive.receivers.ScreenOffReceiver; import foundation.e.drive.utils.AppConstants; import foundation.e.drive.utils.CommonUtils; -import foundation.e.drive.utils.ServiceExceptionHandler; + +import foundation.e.drive.work.FullScanWorker; import static com.owncloud.android.lib.resources.files.FileUtils.PATH_SEPARATOR; import static foundation.e.drive.utils.AppConstants.INITIALFOLDERS_NUMBER; @@ -43,6 +45,11 @@ import static foundation.e.drive.utils.AppConstants.MEDIA_SYNCABLE_CATEGORIES; import static foundation.e.drive.utils.AppConstants.SETTINGS_SYNCABLE_CATEGORIES; import androidx.annotation.Nullable; +import androidx.work.Constraints; +import androidx.work.NetworkType; +import androidx.work.PeriodicWorkRequest; +import androidx.work.WorkManager; +import androidx.work.WorkRequest; /** * @author Vincent Bourgmayer @@ -276,8 +283,22 @@ public class InitializerService extends Service implements OnRemoteOperationList .putInt( INITIALFOLDERS_NUMBER, mSyncedFolders.size() ) .apply(); - //all folder have been created + Constraints constraints = new Constraints.Builder() + .setRequiredNetworkType(NetworkType.CONNECTED) + .setRequiresBatteryNotLow(true) + .build(); + + WorkRequest periodicFullScanRequest = + new PeriodicWorkRequest.Builder(FullScanWorker.class, + 31, TimeUnit.MINUTES, + 5, TimeUnit.MINUTES) + .setConstraints(constraints) + .addTag(AppConstants.WORK_GENERIC_TAG) + .build(); + WorkManager.getInstance(this).enqueue(periodicFullScanRequest); + + //all folder have been created Log.d(TAG, "RegisterReceiver: screenOffReceiver"); IntentFilter filter = new IntentFilter(Intent.ACTION_SCREEN_ON); -- GitLab From 2ed296caa21f3c99dfb9a47c8d6d37caa0b685e9 Mon Sep 17 00:00:00 2001 From: vincent Bourgmayer Date: Thu, 3 Mar 2022 10:43:34 +0100 Subject: [PATCH 28/68] Cancel all Work Enqueued when user account is removed and optimize WorkRequest registration - Cancel all work enqueued using the work generic tag in ResetService.java - Add method "RegisterPeriodicFullScanWorker() in CommonUtils - Replace code in InitializerService by CommonUtils.RegisterPeriodicFullScanWorker - Remove Remove unused import --- .../e/drive/services/InitializerService.java | 28 ++++-------------- .../e/drive/services/ResetService.java | 2 ++ .../foundation/e/drive/utils/CommonUtils.java | 29 ++++++++++++++++++- .../e/drive/work/FullScanWorker.java | 1 + 4 files changed, 36 insertions(+), 24 deletions(-) diff --git a/app/src/main/java/foundation/e/drive/services/InitializerService.java b/app/src/main/java/foundation/e/drive/services/InitializerService.java index 2235a05f..0e040150 100644 --- a/app/src/main/java/foundation/e/drive/services/InitializerService.java +++ b/app/src/main/java/foundation/e/drive/services/InitializerService.java @@ -11,10 +11,12 @@ package foundation.e.drive.services; import android.accounts.Account; import android.accounts.AccountManager; import android.app.Service; +import android.content.ContentResolver; import android.content.Context; import android.content.Intent; import android.content.IntentFilter; import android.content.SharedPreferences; +import android.net.Uri; import android.os.Build; import android.os.Environment; import android.os.Handler; @@ -26,17 +28,16 @@ import com.owncloud.android.lib.common.operations.OnRemoteOperationListener; import com.owncloud.android.lib.common.operations.RemoteOperation; import com.owncloud.android.lib.common.operations.RemoteOperationResult; + import java.util.ArrayList; import java.util.Arrays; import java.util.List; -import java.util.concurrent.TimeUnit; import foundation.e.drive.models.SyncedFolder; import foundation.e.drive.operations.CreateInitialFolderRemoteOperation; import foundation.e.drive.receivers.ScreenOffReceiver; import foundation.e.drive.utils.AppConstants; import foundation.e.drive.utils.CommonUtils; - import foundation.e.drive.work.FullScanWorker; import static com.owncloud.android.lib.resources.files.FileUtils.PATH_SEPARATOR; @@ -45,11 +46,7 @@ import static foundation.e.drive.utils.AppConstants.MEDIA_SYNCABLE_CATEGORIES; import static foundation.e.drive.utils.AppConstants.SETTINGS_SYNCABLE_CATEGORIES; import androidx.annotation.Nullable; -import androidx.work.Constraints; -import androidx.work.NetworkType; -import androidx.work.PeriodicWorkRequest; import androidx.work.WorkManager; -import androidx.work.WorkRequest; /** * @author Vincent Bourgmayer @@ -274,8 +271,7 @@ public class InitializerService extends Service implements OnRemoteOperationList **/ private void doLastStep(){ Log.i(TAG, "doLastStep()"); - - Context appContext = getApplicationContext(); + final Context appContext = getApplicationContext(); appContext.getSharedPreferences( AppConstants.SHARED_PREFERENCE_NAME, Context.MODE_PRIVATE ) .edit() @@ -283,20 +279,7 @@ public class InitializerService extends Service implements OnRemoteOperationList .putInt( INITIALFOLDERS_NUMBER, mSyncedFolders.size() ) .apply(); - Constraints constraints = new Constraints.Builder() - .setRequiredNetworkType(NetworkType.CONNECTED) - .setRequiresBatteryNotLow(true) - .build(); - - WorkRequest periodicFullScanRequest = - new PeriodicWorkRequest.Builder(FullScanWorker.class, - 31, TimeUnit.MINUTES, - 5, TimeUnit.MINUTES) - .setConstraints(constraints) - .addTag(AppConstants.WORK_GENERIC_TAG) - .build(); - - WorkManager.getInstance(this).enqueue(periodicFullScanRequest); + CommonUtils.registerPeriodicFullScanWorker(WorkManager.getInstance(appContext)); //all folder have been created @@ -311,7 +294,6 @@ public class InitializerService extends Service implements OnRemoteOperationList stopSelf(); - } @Override diff --git a/app/src/main/java/foundation/e/drive/services/ResetService.java b/app/src/main/java/foundation/e/drive/services/ResetService.java index 204f5341..2144c935 100644 --- a/app/src/main/java/foundation/e/drive/services/ResetService.java +++ b/app/src/main/java/foundation/e/drive/services/ResetService.java @@ -26,6 +26,7 @@ import static foundation.e.drive.utils.AppConstants.INITIALFOLDERS_NUMBER; import static foundation.e.drive.utils.AppConstants.INITIALIZATION_HAS_BEEN_DONE; import androidx.annotation.Nullable; +import androidx.work.WorkManager; /** * @author Vincent Bourgmayer @@ -63,6 +64,7 @@ public class ResetService extends Service { result = getApplicationContext().stopService( stopperIntent ); Log.d(TAG, "stop OperationManagerService: "+result); + WorkManager.getInstance(this).cancelAllWorkByTag(AppConstants.WORK_GENERIC_TAG); //3. delete DB result = this.deleteDatabase(DbHelper.DATABASE_NAME); 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 e0f762fc..59406ac8 100644 --- a/app/src/main/java/foundation/e/drive/utils/CommonUtils.java +++ b/app/src/main/java/foundation/e/drive/utils/CommonUtils.java @@ -17,7 +17,6 @@ import android.content.ContentResolver; import android.content.Context; import android.media.MediaScannerConnection; import android.net.ConnectivityManager; -import android.net.Network; import android.net.NetworkInfo; import android.net.Uri; import android.util.Log; @@ -31,13 +30,20 @@ import com.owncloud.android.lib.common.accounts.AccountUtils; import com.owncloud.android.lib.resources.files.FileUtils; import java.io.File; +import java.util.concurrent.TimeUnit; import foundation.e.drive.receivers.ScreenOffReceiver; +import foundation.e.drive.work.FullScanWorker; import static foundation.e.drive.utils.AppConstants.MEDIASYNC_PROVIDER_AUTHORITY; import static foundation.e.drive.utils.AppConstants.SETTINGSYNC_PROVIDER_AUTHORITY; import androidx.annotation.NonNull; +import androidx.work.Constraints; +import androidx.work.ExistingPeriodicWorkPolicy; +import androidx.work.NetworkType; +import androidx.work.PeriodicWorkRequest; +import androidx.work.WorkManager; /** @@ -293,4 +299,25 @@ public abstract class CommonUtils { + "\n File can be read?: " + f.canRead() + "\n File can be written?: " + f.canWrite(); } + + /** + * Enqueue a unique periodic worker to look for file to be synchronized (remote files + local files + * @param workManager the instance of workManager + */ + public static void registerPeriodicFullScanWorker(WorkManager workManager){ + final Constraints constraints = new Constraints.Builder() + .setRequiredNetworkType(NetworkType.CONNECTED) + .setRequiresBatteryNotLow(true) + .build(); + + final PeriodicWorkRequest periodicFullScanRequest = + new PeriodicWorkRequest.Builder(FullScanWorker.class, + 31, TimeUnit.MINUTES, + 5, TimeUnit.MINUTES) + .setConstraints(constraints) + .addTag(AppConstants.WORK_GENERIC_TAG) + .build(); + + workManager.enqueueUniquePeriodicWork(FullScanWorker.UNIQUE_WORK_NAME , ExistingPeriodicWorkPolicy.KEEP, periodicFullScanRequest); + } } \ No newline at end of file diff --git a/app/src/main/java/foundation/e/drive/work/FullScanWorker.java b/app/src/main/java/foundation/e/drive/work/FullScanWorker.java index dd538e1e..d05b7b6c 100644 --- a/app/src/main/java/foundation/e/drive/work/FullScanWorker.java +++ b/app/src/main/java/foundation/e/drive/work/FullScanWorker.java @@ -29,6 +29,7 @@ import foundation.e.drive.services.ObserverService; */ public class FullScanWorker extends Worker { final private static String TAG = FullScanWorker.class.getSimpleName(); + final public static String UNIQUE_WORK_NAME="FullScan"; public FullScanWorker(@NonNull Context context, @NonNull WorkerParameters workerParams) { super(context, workerParams); -- GitLab From 07bf89b059e288ce5cdbda5fc6d15d7b80311c77 Mon Sep 17 00:00:00 2001 From: vincent Bourgmayer Date: Thu, 3 Mar 2022 17:57:29 +0100 Subject: [PATCH 29/68] FullScanWorker doesn't send intent to ObserverService when synchronization is disabled --- .../e/drive/work/FullScanWorker.java | 21 +++++++++++++++++-- 1 file changed, 19 insertions(+), 2 deletions(-) diff --git a/app/src/main/java/foundation/e/drive/work/FullScanWorker.java b/app/src/main/java/foundation/e/drive/work/FullScanWorker.java index d05b7b6c..b67151c8 100644 --- a/app/src/main/java/foundation/e/drive/work/FullScanWorker.java +++ b/app/src/main/java/foundation/e/drive/work/FullScanWorker.java @@ -8,8 +8,11 @@ package foundation.e.drive.work; +import android.accounts.Account; +import android.accounts.AccountManager; import android.content.Context; import android.content.Intent; +import android.content.SharedPreferences; import android.util.Log; import androidx.annotation.NonNull; @@ -17,6 +20,8 @@ import androidx.work.Worker; import androidx.work.WorkerParameters; import foundation.e.drive.services.ObserverService; +import foundation.e.drive.utils.AppConstants; +import foundation.e.drive.utils.CommonUtils; /** * As a first step, this class must replace foundation.e.drive.jobs.ScannerJob @@ -39,8 +44,20 @@ public class FullScanWorker extends Worker { @Override public Result doWork() { Log.d(TAG, "doWork(): going to send intent to ObserverService"); - Intent observerServiceIntent = new Intent(this.getApplicationContext(), ObserverService.class); - this.getApplicationContext().startService(observerServiceIntent); + final SharedPreferences prefs = getApplicationContext().getSharedPreferences(AppConstants.SHARED_PREFERENCE_NAME, + Context.MODE_PRIVATE ); + final String accountName = prefs.getString( AccountManager.KEY_ACCOUNT_NAME, "" ); + final String accountType = prefs.getString( AccountManager.KEY_ACCOUNT_TYPE, "" ); + + final Account mAccount = CommonUtils.getAccount( accountName, accountType, AccountManager.get(this.getApplicationContext()) ); + + if(mAccount != null && CommonUtils.isSettingsSyncEnabled(mAccount) && CommonUtils.isMediaSyncEnabled(mAccount)) { + final Intent observerServiceIntent = new Intent(this.getApplicationContext(), ObserverService.class); + this.getApplicationContext().startService(observerServiceIntent); + }else{ + Log.w(TAG, "Intent for ObserverService not send : account is null or \"settings sync\" & \"media sync\" settings are disabled"); + } + return Result.success(); } } -- GitLab From 07feb40b634eaf1f619cf2c3b68d10a046700edb Mon Sep 17 00:00:00 2001 From: vincent Bourgmayer Date: Fri, 4 Mar 2022 10:07:26 +0100 Subject: [PATCH 30/68] fix spacing in if/else statement and fix 'final' word placement --- .../e/drive/services/InitializerService.java | 40 +++++++++---------- .../foundation/e/drive/utils/CommonUtils.java | 4 +- .../e/drive/work/FullScanWorker.java | 10 ++--- 3 files changed, 27 insertions(+), 27 deletions(-) diff --git a/app/src/main/java/foundation/e/drive/services/InitializerService.java b/app/src/main/java/foundation/e/drive/services/InitializerService.java index 0e040150..742fa548 100644 --- a/app/src/main/java/foundation/e/drive/services/InitializerService.java +++ b/app/src/main/java/foundation/e/drive/services/InitializerService.java @@ -11,12 +11,10 @@ package foundation.e.drive.services; import android.accounts.Account; import android.accounts.AccountManager; import android.app.Service; -import android.content.ContentResolver; import android.content.Context; import android.content.Intent; import android.content.IntentFilter; import android.content.SharedPreferences; -import android.net.Uri; import android.os.Build; import android.os.Environment; import android.os.Handler; @@ -52,7 +50,7 @@ import androidx.work.WorkManager; * @author Vincent Bourgmayer */ public class InitializerService extends Service implements OnRemoteOperationListener { - final private String TAG = InitializerService.class.getSimpleName(); + private final String TAG = InitializerService.class.getSimpleName(); private int existingRemoteFolderCounter; //Temporarily used to know if all remotePath exist private List mSyncedFolders; @@ -76,9 +74,11 @@ public class InitializerService extends Service implements OnRemoteOperationList //Get account SharedPreferences prefs = this.getSharedPreferences( AppConstants.SHARED_PREFERENCE_NAME, Context.MODE_PRIVATE ); - if( prefs.getBoolean( AppConstants.INITIALIZATION_HAS_BEEN_DONE, false ) ) { + + if (prefs.getBoolean( AppConstants.INITIALIZATION_HAS_BEEN_DONE, false ) ) { + Log.w(TAG, "Initializer has already been run"); - }else{ + } else { String accountName = prefs.getString( AccountManager.KEY_ACCOUNT_NAME, "" ); String accountType = prefs.getString( AccountManager.KEY_ACCOUNT_TYPE, "" ); @@ -92,16 +92,16 @@ public class InitializerService extends Service implements OnRemoteOperationList .apply(); } - if(accountName.isEmpty() ) { + if (accountName.isEmpty() ) { Log.w(TAG, "Account's name not found. Neither in shared prefs nor in intent's extras"); stopSelf(); - }else{ + } else { this.mAccount = CommonUtils.getAccount( accountName, accountType, AccountManager.get(this) ); //Get OwnCloudlient if (this.mAccount != null) { this.mCloudClient = CommonUtils.getOwnCloudClient( this.mAccount, getApplicationContext()); start(); - }else { + } else { Log.w(TAG, "Got account is invalid."); stopSelf(); } @@ -192,7 +192,7 @@ public class InitializerService extends Service implements OnRemoteOperationList false, true, false) ); - }catch (Exception e){ Log.e(TAG, e.toString()); } + } catch (Exception e){ Log.e(TAG, e.toString()); } break; } } @@ -209,14 +209,14 @@ public class InitializerService extends Service implements OnRemoteOperationList Log.i(TAG, "createNextRemoteFolder()"); this.restartFolderCreationCounter = 0; - if( this.mSyncedFolders == null || this.mSyncedFolders.isEmpty() ){ + if (this.mSyncedFolders == null || this.mSyncedFolders.isEmpty() ){ this.stopSelf(); } //It means that there are still folders to create - if( this.existingRemoteFolderCounter < this.mSyncedFolders.size() ){ + if (this.existingRemoteFolderCounter < this.mSyncedFolders.size() ){ - if( this.mHandler == null ) this.mHandler = new Handler(); + if (this.mHandler == null ) this.mHandler = new Handler(); CreateInitialFolderRemoteOperation createFolderOperation = new CreateInitialFolderRemoteOperation( @@ -226,11 +226,11 @@ public class InitializerService extends Service implements OnRemoteOperationList createFolderOperation.execute(this.mCloudClient, this, this.mHandler); - }else if(this.existingRemoteFolderCounter == this.mSyncedFolders.size() ){ + } else if (this.existingRemoteFolderCounter == this.mSyncedFolders.size() ){ doLastStep(); - }else{ + } else { Log.e(TAG, "this.existingRemoteFolderCounter : "+this.existingRemoteFolderCounter+" > this.mSyncedFolders.size() : "+this.mSyncedFolders.size() ); this.stopSelf(); } @@ -239,26 +239,26 @@ public class InitializerService extends Service implements OnRemoteOperationList @Override public void onRemoteOperationFinish(RemoteOperation operation, RemoteOperationResult result) { Log.i(TAG, "onRemoteOperationFinish()"); - if(operation instanceof CreateInitialFolderRemoteOperation){ + if (operation instanceof CreateInitialFolderRemoteOperation){ - if(result.isSuccess() || result.getCode() == RemoteOperationResult.ResultCode.FOLDER_ALREADY_EXISTS){ + if (result.isSuccess() || result.getCode() == RemoteOperationResult.ResultCode.FOLDER_ALREADY_EXISTS){ this.existingRemoteFolderCounter+=1; CreateNextRemoteFolder(); - }else if( result.getHttpCode() == 423 || result.getHttpCode() == 409){//file locked or conflict in result + } else if (result.getHttpCode() == 423 || result.getHttpCode() == 409){//file locked or conflict in result Log.e( TAG, result.getLogMessage() ); - if( this.restartFolderCreationCounter < 3) { + if (this.restartFolderCreationCounter < 3) { Log.w( TAG, " restart operation" ); operation.execute( this.mCloudClient, this, this.mHandler ); this.restartFolderCreationCounter+=1; - }else{ + } else { Log.e(TAG, "Remote folder's creation failed due to conflict with server"); stopSelf(); } - }else{ + } else { Log.e(TAG, result.getLogMessage()+" "+result.getHttpCode() ); stopSelf(); } 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 59406ac8..5ba7a072 100644 --- a/app/src/main/java/foundation/e/drive/utils/CommonUtils.java +++ b/app/src/main/java/foundation/e/drive/utils/CommonUtils.java @@ -51,7 +51,7 @@ import androidx.work.WorkManager; */ public abstract class CommonUtils { - final private static String TAG = CommonUtils.class.getSimpleName(); + private final static String TAG = CommonUtils.class.getSimpleName(); /** * Set ServiceUncaughtExceptionHandler to be the MainThread Exception Handler @@ -169,7 +169,7 @@ public abstract class CommonUtils { oc.setCredentials(new OwnCloudBasicCredentials(account.name, AccountManager.get(context).getPassword(account))); Log.d(TAG, "user agent: "+AppConstants.USER_AGENT); - if(!AppConstants.USER_AGENT.equals(OwnCloudClientManagerFactory.getUserAgent())) { + if (!AppConstants.USER_AGENT.equals(OwnCloudClientManagerFactory.getUserAgent()) ){ OwnCloudClientManagerFactory.setUserAgent(AppConstants.USER_AGENT); } diff --git a/app/src/main/java/foundation/e/drive/work/FullScanWorker.java b/app/src/main/java/foundation/e/drive/work/FullScanWorker.java index b67151c8..08d5b875 100644 --- a/app/src/main/java/foundation/e/drive/work/FullScanWorker.java +++ b/app/src/main/java/foundation/e/drive/work/FullScanWorker.java @@ -33,8 +33,8 @@ import foundation.e.drive.utils.CommonUtils; * @author Vincent Bourgmayer */ public class FullScanWorker extends Worker { - final private static String TAG = FullScanWorker.class.getSimpleName(); - final public static String UNIQUE_WORK_NAME="FullScan"; + private final static String TAG = FullScanWorker.class.getSimpleName(); + public final static String UNIQUE_WORK_NAME="FullScan"; public FullScanWorker(@NonNull Context context, @NonNull WorkerParameters workerParams) { super(context, workerParams); @@ -51,13 +51,13 @@ public class FullScanWorker extends Worker { final Account mAccount = CommonUtils.getAccount( accountName, accountType, AccountManager.get(this.getApplicationContext()) ); - if(mAccount != null && CommonUtils.isSettingsSyncEnabled(mAccount) && CommonUtils.isMediaSyncEnabled(mAccount)) { + if (mAccount != null && CommonUtils.isSettingsSyncEnabled(mAccount) && CommonUtils.isMediaSyncEnabled(mAccount)) { final Intent observerServiceIntent = new Intent(this.getApplicationContext(), ObserverService.class); this.getApplicationContext().startService(observerServiceIntent); - }else{ + } else { Log.w(TAG, "Intent for ObserverService not send : account is null or \"settings sync\" & \"media sync\" settings are disabled"); } return Result.success(); } -} +} \ No newline at end of file -- GitLab From fcf3b8172d5f467e80ea9af534607e7e5a6dc67a Mon Sep 17 00:00:00 2001 From: vincent Bourgmayer Date: Wed, 2 Mar 2022 10:30:05 +0100 Subject: [PATCH 31/68] Remove BatteryStateReceiver - Removed BatteryStateReceiver.java from foundation.e.drive.receivers package - removed BatteryStateReceiver entry in AndroidManifest.xml --- app/src/main/AndroidManifest.xml | 8 ---- .../drive/receivers/BatteryStateReceiver.java | 39 ------------------- 2 files changed, 47 deletions(-) delete mode 100644 app/src/main/java/foundation/e/drive/receivers/BatteryStateReceiver.java diff --git a/app/src/main/AndroidManifest.xml b/app/src/main/AndroidManifest.xml index be871995..e07511c5 100644 --- a/app/src/main/AndroidManifest.xml +++ b/app/src/main/AndroidManifest.xml @@ -69,14 +69,6 @@ http://www.gnu.org/licenses/gpl.html android:enabled="true" /> - - - - - - diff --git a/app/src/main/java/foundation/e/drive/receivers/BatteryStateReceiver.java b/app/src/main/java/foundation/e/drive/receivers/BatteryStateReceiver.java deleted file mode 100644 index a634635b..00000000 --- a/app/src/main/java/foundation/e/drive/receivers/BatteryStateReceiver.java +++ /dev/null @@ -1,39 +0,0 @@ -/* - * Copyright © Vincent Bourgmayer (/e/ foundation). - * 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.receivers; - -import android.content.BroadcastReceiver; -import android.content.Context; -import android.content.Intent; -import android.util.Log; - -/** - * @author Vincent Bourgmayer - */ -public class BatteryStateReceiver extends BroadcastReceiver { - private final static String TAG = BatteryStateReceiver.class.getSimpleName(); - - @Override - public void onReceive(Context context, Intent intent) { - Log.i(TAG, "onReceive"); - - String intentAction = intent.getAction(); - if(intentAction == null) { - Log.e(TAG, "intent Action is null"); - } else if ( intentAction.equals(Intent.ACTION_BATTERY_OKAY) ) { - Log.d(TAG, "BATTERY is Okay. Synchronization job isn't reschedule anymore. Expected behaviour"); - }else if(intentAction.equals(Intent.ACTION_BATTERY_LOW)){ - try { - context.unregisterReceiver(ScreenOffReceiver.getInstance()); - }catch(Exception e){ - Log.e(TAG, e.toString() ); - } - } - } -} -- GitLab From a5a5ff6ad5a625b97ff11077e55ff10289928d7a Mon Sep 17 00:00:00 2001 From: vincent Bourgmayer Date: Wed, 2 Mar 2022 10:35:57 +0100 Subject: [PATCH 32/68] Remove ScreenOffReceiver - Remove ScreenOffReceiver.java in foundation.e.drive.receivers package - Remove "unregisterScreenOffReceiver()" in CommonUtils - Remove registration code for screenOffReceiver in InitializerService.java and scannerJob.java --- .../foundation/e/drive/jobs/ScannerJob.java | 8 +-- .../e/drive/receivers/ScreenOffReceiver.java | 51 ------------------- .../e/drive/services/InitializerService.java | 8 --- .../foundation/e/drive/utils/CommonUtils.java | 19 +------ 4 files changed, 2 insertions(+), 84 deletions(-) delete mode 100644 app/src/main/java/foundation/e/drive/receivers/ScreenOffReceiver.java diff --git a/app/src/main/java/foundation/e/drive/jobs/ScannerJob.java b/app/src/main/java/foundation/e/drive/jobs/ScannerJob.java index 878f554b..50bfa641 100644 --- a/app/src/main/java/foundation/e/drive/jobs/ScannerJob.java +++ b/app/src/main/java/foundation/e/drive/jobs/ScannerJob.java @@ -11,9 +11,8 @@ package foundation.e.drive.jobs; import android.app.job.JobParameters; import android.app.job.JobService; import android.content.Intent; -import android.content.IntentFilter; import android.util.Log; -import foundation.e.drive.receivers.ScreenOffReceiver; + import foundation.e.drive.services.ObserverService; import foundation.e.drive.utils.CommonUtils; @@ -27,11 +26,6 @@ public class ScannerJob extends JobService { public boolean onStartJob(JobParameters params) { Log.i(TAG, "onStartJob()"); - Log.d(TAG, "RegisterReceiver: screenOffReceiver"); - IntentFilter filter = new IntentFilter(Intent.ACTION_SCREEN_ON); - filter.addAction(Intent.ACTION_SCREEN_OFF); - getApplicationContext().registerReceiver(ScreenOffReceiver.getInstance(), filter); - Intent observerServiceIntent = new Intent(this, ObserverService.class); this.startService(observerServiceIntent); jobFinished(params, false); diff --git a/app/src/main/java/foundation/e/drive/receivers/ScreenOffReceiver.java b/app/src/main/java/foundation/e/drive/receivers/ScreenOffReceiver.java deleted file mode 100644 index 27d48959..00000000 --- a/app/src/main/java/foundation/e/drive/receivers/ScreenOffReceiver.java +++ /dev/null @@ -1,51 +0,0 @@ -/* - * Copyright © Vincent Bourgmayer (/e/ foundation). - * 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.receivers; - -import android.content.BroadcastReceiver; -import android.content.Context; -import android.content.Intent; -import android.util.Log; -import foundation.e.drive.services.ObserverService; -import foundation.e.drive.utils.CommonUtils; - -/** - * @author Vincent Bourgmayer - * This is a broadcast receiver which catch "ACTION_SCREEN_OFF" to start scanning at a moment - * where the user won't need battery or network. - */ -public class ScreenOffReceiver extends BroadcastReceiver { - private final String TAG = ScreenOffReceiver.class.getSimpleName(); - private static ScreenOffReceiver instance; - - public static ScreenOffReceiver getInstance(){ - if(instance == null) - instance = new ScreenOffReceiver(); - return instance; - } - - /** - * Private constructor - */ - private ScreenOffReceiver(){} - - @Override - public void onReceive(Context context, Intent intent) { - Log.i(TAG, "onReceive"); - String intentAction = intent.getAction(); - if(intentAction == null){ - Log.e(TAG, "intent Action is null"); - } else if ( intent.getAction().equals(Intent.ACTION_SCREEN_OFF) - && CommonUtils.haveNetworkConnexion( context ) ) { - Log.d(TAG, "onReceive: ACTION_SCREEN_OFF"); - Intent cloudIntent = new Intent(context, ObserverService.class); - context.startService(cloudIntent); - } - } -} diff --git a/app/src/main/java/foundation/e/drive/services/InitializerService.java b/app/src/main/java/foundation/e/drive/services/InitializerService.java index aad54992..ad682250 100644 --- a/app/src/main/java/foundation/e/drive/services/InitializerService.java +++ b/app/src/main/java/foundation/e/drive/services/InitializerService.java @@ -13,7 +13,6 @@ import android.accounts.AccountManager; import android.app.Service; import android.content.Context; import android.content.Intent; -import android.content.IntentFilter; import android.content.SharedPreferences; import android.os.Build; import android.os.Environment; @@ -33,7 +32,6 @@ import java.util.List; import foundation.e.drive.models.SyncedFolder; import foundation.e.drive.operations.CreateInitialFolderRemoteOperation; -import foundation.e.drive.receivers.ScreenOffReceiver; import foundation.e.drive.utils.AppConstants; import foundation.e.drive.utils.CommonUtils; @@ -284,16 +282,10 @@ public class InitializerService extends Service implements OnRemoteOperationList //all folder have been created - Log.d(TAG, "RegisterReceiver: screenOffReceiver"); - IntentFilter filter = new IntentFilter(Intent.ACTION_SCREEN_ON); - filter.addAction(Intent.ACTION_SCREEN_OFF); - getApplicationContext().registerReceiver(ScreenOffReceiver.getInstance(), filter); - //Immediatly start ObserverService to not have to wait 30 minutes. Intent observersServiceIntent = new Intent(getApplicationContext(), foundation.e.drive.services.ObserverService.class); startService(observersServiceIntent); - stopSelf(); } 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 5ba7a072..6cfb785a 100644 --- a/app/src/main/java/foundation/e/drive/utils/CommonUtils.java +++ b/app/src/main/java/foundation/e/drive/utils/CommonUtils.java @@ -32,7 +32,7 @@ import com.owncloud.android.lib.resources.files.FileUtils; import java.io.File; import java.util.concurrent.TimeUnit; -import foundation.e.drive.receivers.ScreenOffReceiver; + import foundation.e.drive.work.FullScanWorker; import static foundation.e.drive.utils.AppConstants.MEDIASYNC_PROVIDER_AUTHORITY; @@ -70,23 +70,6 @@ public abstract class CommonUtils { } } - /** - * Unregister from screeOffReceiver component - * - * @param context app context - * @return true if unregistration was successful or false if it encounter an exception - */ - public static boolean unregisterScreenOff(Context context) { - try { - Log.d("TAG", "unregisterReceiver(screenOffReceiver)"); - context.unregisterReceiver(ScreenOffReceiver.getInstance()); - } catch (IllegalArgumentException e) { - Log.w(TAG, "Can't unregister screenOffReceiver "); - return false; - } - return true; - } - /** * This method retrieve Account corresponding to account's name and type -- GitLab From 138ca83ed90f42f63721b6ce18adcfaa226f869d Mon Sep 17 00:00:00 2001 From: vincent Bourgmayer Date: Wed, 2 Mar 2022 10:59:13 +0100 Subject: [PATCH 33/68] Remove remaining call of Commonutils.unregisterSceenOff() - remove call of the method in ResetService.java.onStartCommand() - remove call of the method in ScannerJob.java.onStopJob() --- app/src/main/java/foundation/e/drive/jobs/ScannerJob.java | 1 - .../main/java/foundation/e/drive/services/ResetService.java | 5 +---- 2 files changed, 1 insertion(+), 5 deletions(-) diff --git a/app/src/main/java/foundation/e/drive/jobs/ScannerJob.java b/app/src/main/java/foundation/e/drive/jobs/ScannerJob.java index 50bfa641..b62738bb 100644 --- a/app/src/main/java/foundation/e/drive/jobs/ScannerJob.java +++ b/app/src/main/java/foundation/e/drive/jobs/ScannerJob.java @@ -40,7 +40,6 @@ public class ScannerJob extends JobService { @Override public boolean onStopJob(JobParameters params) { Log.i(TAG, "onStopJob"); - boolean unregisteredReceiver = CommonUtils.unregisterScreenOff(getApplicationContext()); Intent observerServiceIntent = new Intent(this, ObserverService.class); this.stopService(observerServiceIntent); return false; diff --git a/app/src/main/java/foundation/e/drive/services/ResetService.java b/app/src/main/java/foundation/e/drive/services/ResetService.java index 2144c935..7b7c9d4e 100644 --- a/app/src/main/java/foundation/e/drive/services/ResetService.java +++ b/app/src/main/java/foundation/e/drive/services/ResetService.java @@ -85,11 +85,8 @@ public class ResetService extends Service { .apply(); } - //5. Unregister screenOffReceiver - result = CommonUtils.unregisterScreenOff(getApplicationContext()); - Log.d(TAG, "Unregistered ScreenOffReceiver: "+result); - //6. Remove Cached File + //5. Remove Cached File File[] cachedFiles = this.getApplicationContext().getExternalCacheDir().listFiles(); for(File f : cachedFiles){ f.delete(); -- GitLab From 389ee9c408e2e284620675a7a9fff22287027b1f Mon Sep 17 00:00:00 2001 From: vincent Bourgmayer Date: Fri, 4 Mar 2022 17:36:19 +0100 Subject: [PATCH 34/68] Remove ScannerJob.java - Remove foundation.e.drive.job.ScannerJob.java - Remove entry in AndroidManifest.xml about Service for ScannerJob - Remove job package in foundation.e.drive --- app/src/main/AndroidManifest.xml | 3 -- .../foundation/e/drive/jobs/ScannerJob.java | 47 ------------------- 2 files changed, 50 deletions(-) delete mode 100644 app/src/main/java/foundation/e/drive/jobs/ScannerJob.java diff --git a/app/src/main/AndroidManifest.xml b/app/src/main/AndroidManifest.xml index e07511c5..7337a890 100644 --- a/app/src/main/AndroidManifest.xml +++ b/app/src/main/AndroidManifest.xml @@ -61,9 +61,6 @@ http://www.gnu.org/licenses/gpl.html - diff --git a/app/src/main/java/foundation/e/drive/jobs/ScannerJob.java b/app/src/main/java/foundation/e/drive/jobs/ScannerJob.java deleted file mode 100644 index b62738bb..00000000 --- a/app/src/main/java/foundation/e/drive/jobs/ScannerJob.java +++ /dev/null @@ -1,47 +0,0 @@ -/* - * Copyright © Vincent Bourgmayer (/e/ foundation). - * 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.jobs; - -import android.app.job.JobParameters; -import android.app.job.JobService; -import android.content.Intent; -import android.util.Log; - -import foundation.e.drive.services.ObserverService; -import foundation.e.drive.utils.CommonUtils; - -/** - * @author Vincent Bourgmayer - */ -public class ScannerJob extends JobService { - final private String TAG = ScannerJob.class.getSimpleName(); //Tag for log - - @Override - public boolean onStartJob(JobParameters params) { - Log.i(TAG, "onStartJob()"); - - Intent observerServiceIntent = new Intent(this, ObserverService.class); - this.startService(observerServiceIntent); - jobFinished(params, false); - return true; - } - - /** - * - * @param params - * @return default return... ? - */ - @Override - public boolean onStopJob(JobParameters params) { - Log.i(TAG, "onStopJob"); - Intent observerServiceIntent = new Intent(this, ObserverService.class); - this.stopService(observerServiceIntent); - return false; - } -} -- GitLab From 8b4368988766feaa94d7b9e7ec2f80ae2348f17b Mon Sep 17 00:00:00 2001 From: vincent Bourgmayer Date: Mon, 7 Mar 2022 18:41:33 +0100 Subject: [PATCH 35/68] Add DeviceIdle constraint to start FullScan (ObserverService) --- app/src/main/java/foundation/e/drive/utils/CommonUtils.java | 1 + 1 file changed, 1 insertion(+) 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 6cfb785a..6cffadcc 100644 --- a/app/src/main/java/foundation/e/drive/utils/CommonUtils.java +++ b/app/src/main/java/foundation/e/drive/utils/CommonUtils.java @@ -291,6 +291,7 @@ public abstract class CommonUtils { final Constraints constraints = new Constraints.Builder() .setRequiredNetworkType(NetworkType.CONNECTED) .setRequiresBatteryNotLow(true) + .setRequiresDeviceIdle(true) .build(); final PeriodicWorkRequest periodicFullScanRequest = -- GitLab From 69fdbeea99d9c4d251d242872ee0ef322e685c6e Mon Sep 17 00:00:00 2001 From: vincent Bourgmayer Date: Mon, 7 Mar 2022 18:41:33 +0100 Subject: [PATCH 36/68] Add DeviceIdle constraint to start FullScan (ObserverService) --- app/src/main/java/foundation/e/drive/utils/CommonUtils.java | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/app/src/main/java/foundation/e/drive/utils/CommonUtils.java b/app/src/main/java/foundation/e/drive/utils/CommonUtils.java index 6cfb785a..7c1896c5 100644 --- a/app/src/main/java/foundation/e/drive/utils/CommonUtils.java +++ b/app/src/main/java/foundation/e/drive/utils/CommonUtils.java @@ -291,6 +291,7 @@ public abstract class CommonUtils { final Constraints constraints = new Constraints.Builder() .setRequiredNetworkType(NetworkType.CONNECTED) .setRequiresBatteryNotLow(true) + .setRequiresDeviceIdle(true) .build(); final PeriodicWorkRequest periodicFullScanRequest = @@ -301,6 +302,6 @@ public abstract class CommonUtils { .addTag(AppConstants.WORK_GENERIC_TAG) .build(); - workManager.enqueueUniquePeriodicWork(FullScanWorker.UNIQUE_WORK_NAME , ExistingPeriodicWorkPolicy.KEEP, periodicFullScanRequest); + workManager.enqueueUniquePeriodicWork(FullScanWorker.UNIQUE_WORK_NAME, ExistingPeriodicWorkPolicy.KEEP, periodicFullScanRequest); } } \ No newline at end of file -- GitLab From 9e8dd34fdf4973a706aad2e609026d1c3f7d4aaa Mon Sep 17 00:00:00 2001 From: vincent Bourgmayer Date: Wed, 9 Mar 2022 09:21:23 +0100 Subject: [PATCH 37/68] fixed coding style according to suggestion --- .../java/foundation/e/drive/work/FullScanWorker.java | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/app/src/main/java/foundation/e/drive/work/FullScanWorker.java b/app/src/main/java/foundation/e/drive/work/FullScanWorker.java index 08d5b875..a2424a07 100644 --- a/app/src/main/java/foundation/e/drive/work/FullScanWorker.java +++ b/app/src/main/java/foundation/e/drive/work/FullScanWorker.java @@ -34,7 +34,7 @@ import foundation.e.drive.utils.CommonUtils; */ public class FullScanWorker extends Worker { private final static String TAG = FullScanWorker.class.getSimpleName(); - public final static String UNIQUE_WORK_NAME="FullScan"; + public final static String UNIQUE_WORK_NAME = "FullScan"; public FullScanWorker(@NonNull Context context, @NonNull WorkerParameters workerParams) { super(context, workerParams); @@ -45,11 +45,11 @@ public class FullScanWorker extends Worker { public Result doWork() { Log.d(TAG, "doWork(): going to send intent to ObserverService"); final SharedPreferences prefs = getApplicationContext().getSharedPreferences(AppConstants.SHARED_PREFERENCE_NAME, - Context.MODE_PRIVATE ); - final String accountName = prefs.getString( AccountManager.KEY_ACCOUNT_NAME, "" ); - final String accountType = prefs.getString( AccountManager.KEY_ACCOUNT_TYPE, "" ); + Context.MODE_PRIVATE); + final String accountName = prefs.getString(AccountManager.KEY_ACCOUNT_NAME, ""); + final String accountType = prefs.getString(AccountManager.KEY_ACCOUNT_TYPE, ""); - final Account mAccount = CommonUtils.getAccount( accountName, accountType, AccountManager.get(this.getApplicationContext()) ); + final Account mAccount = CommonUtils.getAccount(accountName, accountType, AccountManager.get(this.getApplicationContext())); if (mAccount != null && CommonUtils.isSettingsSyncEnabled(mAccount) && CommonUtils.isMediaSyncEnabled(mAccount)) { final Intent observerServiceIntent = new Intent(this.getApplicationContext(), ObserverService.class); -- GitLab From ce8d8f7397d5865342aeb5fa3856465b0407939f Mon Sep 17 00:00:00 2001 From: Vincent Bourgmayer Date: Wed, 9 Mar 2022 10:04:56 +0000 Subject: [PATCH 38/68] Update .gitlab-ci.yml --- .gitlab-ci.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.gitlab-ci.yml b/.gitlab-ci.yml index d475c315..18dd3f0e 100644 --- a/.gitlab-ci.yml +++ b/.gitlab-ci.yml @@ -1,4 +1,4 @@ -image: "registry.gitlab.e.foundation:5000/e/apps/docker-android-apps-cicd:latest" +image: "registry.gitlab.e.foundation:5000/e/os/docker-android-apps-cicd:latest" stages: - test -- GitLab From 09a63c5ae3935e2bea2f9beefddad2d5252152f6 Mon Sep 17 00:00:00 2001 From: Vincent Bourgmayer Date: Wed, 9 Mar 2022 10:06:37 +0000 Subject: [PATCH 39/68] Update .gitlab-ci.yml --- .gitlab-ci.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.gitlab-ci.yml b/.gitlab-ci.yml index 18dd3f0e..30859edd 100644 --- a/.gitlab-ci.yml +++ b/.gitlab-ci.yml @@ -1,4 +1,4 @@ -image: "registry.gitlab.e.foundation:5000/e/os/docker-android-apps-cicd:latest" +image: "registry.gitlab.e.foundation/e/os/docker-android-apps-cicd:latest" stages: - test -- GitLab From 335c93518ff31a98bbcf41c51fbef16eb9fbe5c2 Mon Sep 17 00:00:00 2001 From: vincent Bourgmayer Date: Mon, 7 Mar 2022 11:58:49 +0100 Subject: [PATCH 40/68] Add FileObserver classes - Add "FileObservers" Package - Add RecursiveFileObserver into FileObservers package. (Narinder's code) - Add FileEventListener interface into FileObservers package. (Narinder's code) - refactor some details in RecursiveFileObserver from Narinder's latest source code: - replace 'watch(File file)' method by private final static FileFilter - Remove a synchronized bloc that where duplicated - Add a "FileObserver" class in models Package based on Narinder's work. --- .../FileObservers/FileEventListener.java | 18 ++ .../FileObservers/RecursiveFileObserver.java | 168 ++++++++++++++++++ .../e/drive/models/FileObserver.java | 33 ++++ 3 files changed, 219 insertions(+) create mode 100644 app/src/main/java/foundation/e/drive/FileObservers/FileEventListener.java create mode 100644 app/src/main/java/foundation/e/drive/FileObservers/RecursiveFileObserver.java create mode 100644 app/src/main/java/foundation/e/drive/models/FileObserver.java diff --git a/app/src/main/java/foundation/e/drive/FileObservers/FileEventListener.java b/app/src/main/java/foundation/e/drive/FileObservers/FileEventListener.java new file mode 100644 index 00000000..de451f29 --- /dev/null +++ b/app/src/main/java/foundation/e/drive/FileObservers/FileEventListener.java @@ -0,0 +1,18 @@ +/* + * Copyright © Narinder Rana (/e/ foundation). + * 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.FileObservers; + +import java.io.File; + +/** + * @author Narinder Rana + */ +public interface FileEventListener { + void onEvent(int event, File file); +} diff --git a/app/src/main/java/foundation/e/drive/FileObservers/RecursiveFileObserver.java b/app/src/main/java/foundation/e/drive/FileObservers/RecursiveFileObserver.java new file mode 100644 index 00000000..6f506353 --- /dev/null +++ b/app/src/main/java/foundation/e/drive/FileObservers/RecursiveFileObserver.java @@ -0,0 +1,168 @@ +/* + * Copyright © Narinder Rana (/e/ foundation). + * 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.FileObservers; + +import android.content.Context; +import android.os.FileObserver; + +import java.io.File; +import java.io.FileFilter; +import java.util.HashMap; +import java.util.List; +import java.util.Stack; + +import foundation.e.drive.database.DbHelper; +import foundation.e.drive.models.SyncedFolder; + +/** + * @author Narinder Rana + * @author Vincent Bourgmayer + */ +public class RecursiveFileObserver extends FileObserver { + private final HashMap mObservers = new HashMap<>(); + private final static FileFilter watchableDirectoriesFilter = new FileFilter() { + @Override + public boolean accept(File file) { + return file.isDirectory() && !file.getName().equals(".") && !file.getName().equals(".."); + } + }; + + + private final Context applicationContext; + private String mPath; + private int mMask; + private FileEventListener mListener; + + public RecursiveFileObserver(Context applicationContext, String path, FileEventListener listener) { + this(applicationContext, path, ALL_EVENTS, listener); + + } + + public RecursiveFileObserver(Context applicationContext, String path, int mask, FileEventListener listener) { + super(path, mask); + mPath = path; + mMask = mask | FileObserver.CREATE | FileObserver.DELETE_SELF; + mListener = listener; + this.applicationContext=applicationContext; + } + + + @Override + public void onEvent(int event, String path) { + File file; + if (path == null) { + file = new File(mPath); + } else { + file = new File(mPath, path); + } + notify(event, file); + } + + private void notify(int event, File file) { + if (mListener != null) { + mListener.onEvent(event & FileObserver.ALL_EVENTS, file); + } + } + + @Override + public void startWatching() { + Stack stack = new Stack<>(); + + List mSyncedFolders = DbHelper.getAllSyncedFolders(applicationContext); + if(!mSyncedFolders.isEmpty()){ + for(SyncedFolder syncedFolder:mSyncedFolders){ + stack.push(syncedFolder.getLocalFolder()); + stack.push(syncedFolder.getRemoteFolder()); + } + } + + // Recursively watch all child directories + while (!stack.empty()) { + String parent = stack.pop(); + startWatching(parent); + + File path = new File(parent); + File[] files = path.listFiles(watchableDirectoriesFilter); + if (files != null) { + for (File file : files) { + stack.push(file.getAbsolutePath()); + } + } + } + } + + /** + * Start watching a single file + * @param path + */ + private void startWatching(String path) { + synchronized (mObservers) { + FileObserver observer = mObservers.remove(path); + if (observer != null) { + observer.stopWatching(); + } + observer = new SingleFileObserver(path, mMask); + observer.startWatching(); + mObservers.put(path, observer); + } + } + + @Override + public void stopWatching() { + for (FileObserver observer : mObservers.values()) { + observer.stopWatching(); + } + mObservers.clear(); + } + + /** + * Stop watching a single file + * @param path + */ + private void stopWatching(String path) { + synchronized (mObservers) { + FileObserver observer = mObservers.remove(path); + if (observer != null) { + observer.stopWatching(); + } + } + } + + private class SingleFileObserver extends FileObserver { + private String filePath; + + public SingleFileObserver(String path, int mask) { + super(path, mask); + filePath = path; + } + + @Override + public void onEvent(int event, String path) { + File file; + if (path == null) { + file = new File(filePath); + } else { + file = new File(filePath, path); + } + + switch (event & FileObserver.ALL_EVENTS) { + case DELETE_SELF: + RecursiveFileObserver.this.stopWatching(filePath); + break; + case CREATE: + if (watchableDirectoriesFilter.accept(file)) { + RecursiveFileObserver.this.startWatching(file.getAbsolutePath()); + } + break; + } + + RecursiveFileObserver.this.notify(event, file); + } + } +} diff --git a/app/src/main/java/foundation/e/drive/models/FileObserver.java b/app/src/main/java/foundation/e/drive/models/FileObserver.java new file mode 100644 index 00000000..6407b8a6 --- /dev/null +++ b/app/src/main/java/foundation/e/drive/models/FileObserver.java @@ -0,0 +1,33 @@ +/* + * Copyright © Narinder Rana (/e/ foundation). + * 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.models; + +import java.io.File; +import java.io.Serializable; +import java.util.List; + +/** + * @author Narinder Rana + */ +public class FileObserver implements Serializable { + + private List files; + public FileObserver(List files) { + this.files = files; + + } + + public List getFiles() { + return files; + } + + public void setFiles(List files) { + this.files = files; + } +} -- GitLab From 717c2f2417a9a479b103ee4dcada9cb44d222eb6 Mon Sep 17 00:00:00 2001 From: vincent Bourgmayer Date: Mon, 7 Mar 2022 14:47:57 +0100 Subject: [PATCH 41/68] Transform FileEventListener interface into class - Get FileEventListener implementation from Narinder's work branch and merge it merge it with FileEventListener interface. --- .../e/drive/FileObservers/FileEventListener.java | 16 ++++++++++++++-- 1 file changed, 14 insertions(+), 2 deletions(-) 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 de451f29..561cf5d6 100644 --- a/app/src/main/java/foundation/e/drive/FileObservers/FileEventListener.java +++ b/app/src/main/java/foundation/e/drive/FileObservers/FileEventListener.java @@ -8,11 +8,23 @@ package foundation.e.drive.FileObservers; +import android.os.FileObserver; +import android.util.Log; + import java.io.File; /** * @author Narinder Rana */ -public interface FileEventListener { - void onEvent(int event, File file); +public class FileEventListener { + private final static String TAG = FileEventListener.class.getSimpleName(); + + public void onEvent(int event, File file){ + if (event== FileObserver.CLOSE_WRITE){ //Event triggered after modification/creation + Log.d(TAG, "CLOSE_WRITE event for :"+file.getName()); + } + if (event== FileObserver.DELETE_SELF ){ + Log.d(TAG, "DELETE_SELF event for :"+file.getName()); + } + } } -- GitLab From dba13166b9f023bd928889cecb5202b0c56e35d7 Mon Sep 17 00:00:00 2001 From: vincent Bourgmayer Date: Mon, 7 Mar 2022 18:34:10 +0100 Subject: [PATCH 42/68] Add FileObserverService to start FileObserver - Add services.FileObserverService - Update FileEventListener to handle DELETE event instead of DELETE_SELF (not detected in test) - Add entry in AndroidManifest for FileObserverService - Start FileObserverService at the end of InitializerService's job --- app/src/main/AndroidManifest.xml | 8 ++++ .../FileObservers/FileEventListener.java | 5 +- .../FileObservers/RecursiveFileObserver.java | 4 +- .../e/drive/services/FileObserverService.java | 46 +++++++++++++++++++ .../e/drive/services/InitializerService.java | 3 +- 5 files changed, 61 insertions(+), 5 deletions(-) create mode 100644 app/src/main/java/foundation/e/drive/services/FileObserverService.java diff --git a/app/src/main/AndroidManifest.xml b/app/src/main/AndroidManifest.xml index 7337a890..fa37c77d 100644 --- a/app/src/main/AndroidManifest.xml +++ b/app/src/main/AndroidManifest.xml @@ -53,6 +53,14 @@ http://www.gnu.org/licenses/gpl.html + + + + + stack = new Stack<>(); List mSyncedFolders = DbHelper.getAllSyncedFolders(applicationContext); - if(!mSyncedFolders.isEmpty()){ - for(SyncedFolder syncedFolder:mSyncedFolders){ + if (!mSyncedFolders.isEmpty()){ + for (SyncedFolder syncedFolder:mSyncedFolders){ stack.push(syncedFolder.getLocalFolder()); stack.push(syncedFolder.getRemoteFolder()); } diff --git a/app/src/main/java/foundation/e/drive/services/FileObserverService.java b/app/src/main/java/foundation/e/drive/services/FileObserverService.java new file mode 100644 index 00000000..6ba63ba1 --- /dev/null +++ b/app/src/main/java/foundation/e/drive/services/FileObserverService.java @@ -0,0 +1,46 @@ +package foundation.e.drive.services; + +import android.app.Service; +import android.content.Intent; +import android.os.Environment; +import android.os.IBinder; +import android.util.Log; + +import androidx.annotation.Nullable; + +import java.io.File; +import java.util.List; +import java.util.concurrent.CopyOnWriteArrayList; + +import foundation.e.drive.FileObservers.FileEventListener; +import foundation.e.drive.FileObservers.RecursiveFileObserver; + +public class FileObserverService extends Service { + private final static String TAG = FileObserverService.class.getSimpleName(); + private RecursiveFileObserver mFileObserver = null; + + @Override + public int onStartCommand(Intent intent, int flags, int startId) { + Log.d(TAG, "started"); + super.onStartCommand(intent, flags, startId); + + String path = Environment.getExternalStorageDirectory().getAbsolutePath(); + Log.d(TAG, "Added RecursiveFileObserver on root :"+path); + + + mFileObserver = new RecursiveFileObserver(getApplicationContext(), path, new FileEventListener()); + + mFileObserver.startWatching(); + return START_STICKY; + } + + + + + + @Nullable + @Override + public IBinder onBind(Intent intent) { + return null; + } +} diff --git a/app/src/main/java/foundation/e/drive/services/InitializerService.java b/app/src/main/java/foundation/e/drive/services/InitializerService.java index ad682250..efcfdba8 100644 --- a/app/src/main/java/foundation/e/drive/services/InitializerService.java +++ b/app/src/main/java/foundation/e/drive/services/InitializerService.java @@ -280,7 +280,8 @@ public class InitializerService extends Service implements OnRemoteOperationList CommonUtils.registerPeriodicFullScanWorker(WorkManager.getInstance(appContext)); //all folder have been created - + Intent FileObserverService = new Intent(getApplicationContext(), foundation.e.drive.services.FileObserverService.class); + startService(FileObserverService); //Immediatly start ObserverService to not have to wait 30 minutes. Intent observersServiceIntent = new Intent(getApplicationContext(), foundation.e.drive.services.ObserverService.class); -- GitLab From a9bea5235076d672fbeb8b6fb64615ade62050f0 Mon Sep 17 00:00:00 2001 From: vincent Bourgmayer Date: Tue, 8 Mar 2022 18:22:53 +0100 Subject: [PATCH 43/68] Trigger FileObserver from EDriveApplication instead of FileObserverService - Remove FileObserverService because useless - Add "StartRecursiveFileObserver()" and "StopRecursiveFileObserver()" method in EDriveApplication. To allow services to stop/start the FileObserver. - Replace intent for FileObserverService by call in InitializerService by EDriveApplication.startRecursiveFileObserver()" - Add a call to EdriveApplication.StopRecursiveFileObserver() method in ResetService - Remove FileObserverService entry in AndroidManifest.xml - Add a "watching" boolean field in RecursiveFileObserver and "isWatching()" public method --- app/src/main/AndroidManifest.xml | 8 ---- .../foundation/e/drive/EdriveApplication.java | 29 ++++++++++++ .../FileObservers/RecursiveFileObserver.java | 9 +++- .../e/drive/services/FileObserverService.java | 46 ------------------- .../e/drive/services/InitializerService.java | 7 ++- .../e/drive/services/ResetService.java | 3 ++ 6 files changed, 45 insertions(+), 57 deletions(-) delete mode 100644 app/src/main/java/foundation/e/drive/services/FileObserverService.java diff --git a/app/src/main/AndroidManifest.xml b/app/src/main/AndroidManifest.xml index fa37c77d..7337a890 100644 --- a/app/src/main/AndroidManifest.xml +++ b/app/src/main/AndroidManifest.xml @@ -53,14 +53,6 @@ http://www.gnu.org/licenses/gpl.html - - - - - Date: Wed, 9 Mar 2022 11:39:18 +0100 Subject: [PATCH 44/68] update FileFilter in RecursiveFileObserver --- .../e/drive/FileObservers/RecursiveFileObserver.java | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/app/src/main/java/foundation/e/drive/FileObservers/RecursiveFileObserver.java b/app/src/main/java/foundation/e/drive/FileObservers/RecursiveFileObserver.java index 3b06faab..282789b1 100644 --- a/app/src/main/java/foundation/e/drive/FileObservers/RecursiveFileObserver.java +++ b/app/src/main/java/foundation/e/drive/FileObservers/RecursiveFileObserver.java @@ -29,7 +29,8 @@ public class RecursiveFileObserver extends FileObserver { private final static FileFilter watchableDirectoriesFilter = new FileFilter() { @Override public boolean accept(File file) { - return file.isDirectory() && !file.getName().equals(".") && !file.getName().equals(".."); + return file.isDirectory() && !file.getName().startsWith("."); + } }; @@ -61,6 +62,7 @@ public class RecursiveFileObserver extends FileObserver { } else { file = new File(mPath, path); } + notify(event, file); } -- GitLab From 277bb13cf17a9f48e3aa8af5e8369a0593982b55 Mon Sep 17 00:00:00 2001 From: vincent Bourgmayer Date: Mon, 7 Mar 2022 11:58:49 +0100 Subject: [PATCH 45/68] Add FileObserver classes - Add "FileObservers" Package - Add RecursiveFileObserver into FileObservers package. (Narinder's code) - Add FileEventListener interface into FileObservers package. (Narinder's code) - refactor some details in RecursiveFileObserver from Narinder's latest source code: - replace 'watch(File file)' method by private final static FileFilter - Remove a synchronized bloc that where duplicated - Add a "FileObserver" class in models Package based on Narinder's work. --- .../FileObservers/FileEventListener.java | 18 ++ .../FileObservers/RecursiveFileObserver.java | 168 ++++++++++++++++++ .../e/drive/models/FileObserver.java | 33 ++++ 3 files changed, 219 insertions(+) create mode 100644 app/src/main/java/foundation/e/drive/FileObservers/FileEventListener.java create mode 100644 app/src/main/java/foundation/e/drive/FileObservers/RecursiveFileObserver.java create mode 100644 app/src/main/java/foundation/e/drive/models/FileObserver.java diff --git a/app/src/main/java/foundation/e/drive/FileObservers/FileEventListener.java b/app/src/main/java/foundation/e/drive/FileObservers/FileEventListener.java new file mode 100644 index 00000000..de451f29 --- /dev/null +++ b/app/src/main/java/foundation/e/drive/FileObservers/FileEventListener.java @@ -0,0 +1,18 @@ +/* + * Copyright © Narinder Rana (/e/ foundation). + * 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.FileObservers; + +import java.io.File; + +/** + * @author Narinder Rana + */ +public interface FileEventListener { + void onEvent(int event, File file); +} diff --git a/app/src/main/java/foundation/e/drive/FileObservers/RecursiveFileObserver.java b/app/src/main/java/foundation/e/drive/FileObservers/RecursiveFileObserver.java new file mode 100644 index 00000000..6f506353 --- /dev/null +++ b/app/src/main/java/foundation/e/drive/FileObservers/RecursiveFileObserver.java @@ -0,0 +1,168 @@ +/* + * Copyright © Narinder Rana (/e/ foundation). + * 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.FileObservers; + +import android.content.Context; +import android.os.FileObserver; + +import java.io.File; +import java.io.FileFilter; +import java.util.HashMap; +import java.util.List; +import java.util.Stack; + +import foundation.e.drive.database.DbHelper; +import foundation.e.drive.models.SyncedFolder; + +/** + * @author Narinder Rana + * @author Vincent Bourgmayer + */ +public class RecursiveFileObserver extends FileObserver { + private final HashMap mObservers = new HashMap<>(); + private final static FileFilter watchableDirectoriesFilter = new FileFilter() { + @Override + public boolean accept(File file) { + return file.isDirectory() && !file.getName().equals(".") && !file.getName().equals(".."); + } + }; + + + private final Context applicationContext; + private String mPath; + private int mMask; + private FileEventListener mListener; + + public RecursiveFileObserver(Context applicationContext, String path, FileEventListener listener) { + this(applicationContext, path, ALL_EVENTS, listener); + + } + + public RecursiveFileObserver(Context applicationContext, String path, int mask, FileEventListener listener) { + super(path, mask); + mPath = path; + mMask = mask | FileObserver.CREATE | FileObserver.DELETE_SELF; + mListener = listener; + this.applicationContext=applicationContext; + } + + + @Override + public void onEvent(int event, String path) { + File file; + if (path == null) { + file = new File(mPath); + } else { + file = new File(mPath, path); + } + notify(event, file); + } + + private void notify(int event, File file) { + if (mListener != null) { + mListener.onEvent(event & FileObserver.ALL_EVENTS, file); + } + } + + @Override + public void startWatching() { + Stack stack = new Stack<>(); + + List mSyncedFolders = DbHelper.getAllSyncedFolders(applicationContext); + if(!mSyncedFolders.isEmpty()){ + for(SyncedFolder syncedFolder:mSyncedFolders){ + stack.push(syncedFolder.getLocalFolder()); + stack.push(syncedFolder.getRemoteFolder()); + } + } + + // Recursively watch all child directories + while (!stack.empty()) { + String parent = stack.pop(); + startWatching(parent); + + File path = new File(parent); + File[] files = path.listFiles(watchableDirectoriesFilter); + if (files != null) { + for (File file : files) { + stack.push(file.getAbsolutePath()); + } + } + } + } + + /** + * Start watching a single file + * @param path + */ + private void startWatching(String path) { + synchronized (mObservers) { + FileObserver observer = mObservers.remove(path); + if (observer != null) { + observer.stopWatching(); + } + observer = new SingleFileObserver(path, mMask); + observer.startWatching(); + mObservers.put(path, observer); + } + } + + @Override + public void stopWatching() { + for (FileObserver observer : mObservers.values()) { + observer.stopWatching(); + } + mObservers.clear(); + } + + /** + * Stop watching a single file + * @param path + */ + private void stopWatching(String path) { + synchronized (mObservers) { + FileObserver observer = mObservers.remove(path); + if (observer != null) { + observer.stopWatching(); + } + } + } + + private class SingleFileObserver extends FileObserver { + private String filePath; + + public SingleFileObserver(String path, int mask) { + super(path, mask); + filePath = path; + } + + @Override + public void onEvent(int event, String path) { + File file; + if (path == null) { + file = new File(filePath); + } else { + file = new File(filePath, path); + } + + switch (event & FileObserver.ALL_EVENTS) { + case DELETE_SELF: + RecursiveFileObserver.this.stopWatching(filePath); + break; + case CREATE: + if (watchableDirectoriesFilter.accept(file)) { + RecursiveFileObserver.this.startWatching(file.getAbsolutePath()); + } + break; + } + + RecursiveFileObserver.this.notify(event, file); + } + } +} diff --git a/app/src/main/java/foundation/e/drive/models/FileObserver.java b/app/src/main/java/foundation/e/drive/models/FileObserver.java new file mode 100644 index 00000000..6407b8a6 --- /dev/null +++ b/app/src/main/java/foundation/e/drive/models/FileObserver.java @@ -0,0 +1,33 @@ +/* + * Copyright © Narinder Rana (/e/ foundation). + * 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.models; + +import java.io.File; +import java.io.Serializable; +import java.util.List; + +/** + * @author Narinder Rana + */ +public class FileObserver implements Serializable { + + private List files; + public FileObserver(List files) { + this.files = files; + + } + + public List getFiles() { + return files; + } + + public void setFiles(List files) { + this.files = files; + } +} -- GitLab From 1292ab628c68b0964fbe68ee583c497302bb35fd Mon Sep 17 00:00:00 2001 From: vincent Bourgmayer Date: Mon, 7 Mar 2022 14:47:57 +0100 Subject: [PATCH 46/68] Transform FileEventListener interface into class - Get FileEventListener implementation from Narinder's work branch and merge it merge it with FileEventListener interface. --- .../e/drive/FileObservers/FileEventListener.java | 16 ++++++++++++++-- 1 file changed, 14 insertions(+), 2 deletions(-) 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 de451f29..561cf5d6 100644 --- a/app/src/main/java/foundation/e/drive/FileObservers/FileEventListener.java +++ b/app/src/main/java/foundation/e/drive/FileObservers/FileEventListener.java @@ -8,11 +8,23 @@ package foundation.e.drive.FileObservers; +import android.os.FileObserver; +import android.util.Log; + import java.io.File; /** * @author Narinder Rana */ -public interface FileEventListener { - void onEvent(int event, File file); +public class FileEventListener { + private final static String TAG = FileEventListener.class.getSimpleName(); + + public void onEvent(int event, File file){ + if (event== FileObserver.CLOSE_WRITE){ //Event triggered after modification/creation + Log.d(TAG, "CLOSE_WRITE event for :"+file.getName()); + } + if (event== FileObserver.DELETE_SELF ){ + Log.d(TAG, "DELETE_SELF event for :"+file.getName()); + } + } } -- GitLab From fa8555a73d6bfdaf99cbf6f637063f9fe324af94 Mon Sep 17 00:00:00 2001 From: vincent Bourgmayer Date: Mon, 7 Mar 2022 18:34:10 +0100 Subject: [PATCH 47/68] Add FileObserverService to start FileObserver - Add services.FileObserverService - Update FileEventListener to handle DELETE event instead of DELETE_SELF (not detected in test) - Add entry in AndroidManifest for FileObserverService - Start FileObserverService at the end of InitializerService's job --- app/src/main/AndroidManifest.xml | 8 ++++ .../FileObservers/FileEventListener.java | 5 +- .../FileObservers/RecursiveFileObserver.java | 4 +- .../e/drive/services/FileObserverService.java | 46 +++++++++++++++++++ .../e/drive/services/InitializerService.java | 3 +- 5 files changed, 61 insertions(+), 5 deletions(-) create mode 100644 app/src/main/java/foundation/e/drive/services/FileObserverService.java diff --git a/app/src/main/AndroidManifest.xml b/app/src/main/AndroidManifest.xml index 7337a890..fa37c77d 100644 --- a/app/src/main/AndroidManifest.xml +++ b/app/src/main/AndroidManifest.xml @@ -53,6 +53,14 @@ http://www.gnu.org/licenses/gpl.html + + + + + stack = new Stack<>(); List mSyncedFolders = DbHelper.getAllSyncedFolders(applicationContext); - if(!mSyncedFolders.isEmpty()){ - for(SyncedFolder syncedFolder:mSyncedFolders){ + if (!mSyncedFolders.isEmpty()){ + for (SyncedFolder syncedFolder:mSyncedFolders){ stack.push(syncedFolder.getLocalFolder()); stack.push(syncedFolder.getRemoteFolder()); } diff --git a/app/src/main/java/foundation/e/drive/services/FileObserverService.java b/app/src/main/java/foundation/e/drive/services/FileObserverService.java new file mode 100644 index 00000000..6ba63ba1 --- /dev/null +++ b/app/src/main/java/foundation/e/drive/services/FileObserverService.java @@ -0,0 +1,46 @@ +package foundation.e.drive.services; + +import android.app.Service; +import android.content.Intent; +import android.os.Environment; +import android.os.IBinder; +import android.util.Log; + +import androidx.annotation.Nullable; + +import java.io.File; +import java.util.List; +import java.util.concurrent.CopyOnWriteArrayList; + +import foundation.e.drive.FileObservers.FileEventListener; +import foundation.e.drive.FileObservers.RecursiveFileObserver; + +public class FileObserverService extends Service { + private final static String TAG = FileObserverService.class.getSimpleName(); + private RecursiveFileObserver mFileObserver = null; + + @Override + public int onStartCommand(Intent intent, int flags, int startId) { + Log.d(TAG, "started"); + super.onStartCommand(intent, flags, startId); + + String path = Environment.getExternalStorageDirectory().getAbsolutePath(); + Log.d(TAG, "Added RecursiveFileObserver on root :"+path); + + + mFileObserver = new RecursiveFileObserver(getApplicationContext(), path, new FileEventListener()); + + mFileObserver.startWatching(); + return START_STICKY; + } + + + + + + @Nullable + @Override + public IBinder onBind(Intent intent) { + return null; + } +} diff --git a/app/src/main/java/foundation/e/drive/services/InitializerService.java b/app/src/main/java/foundation/e/drive/services/InitializerService.java index ad682250..efcfdba8 100644 --- a/app/src/main/java/foundation/e/drive/services/InitializerService.java +++ b/app/src/main/java/foundation/e/drive/services/InitializerService.java @@ -280,7 +280,8 @@ public class InitializerService extends Service implements OnRemoteOperationList CommonUtils.registerPeriodicFullScanWorker(WorkManager.getInstance(appContext)); //all folder have been created - + Intent FileObserverService = new Intent(getApplicationContext(), foundation.e.drive.services.FileObserverService.class); + startService(FileObserverService); //Immediatly start ObserverService to not have to wait 30 minutes. Intent observersServiceIntent = new Intent(getApplicationContext(), foundation.e.drive.services.ObserverService.class); -- GitLab From dcecf591a3f053f4da8a5ee47d31d9a68131ddae Mon Sep 17 00:00:00 2001 From: vincent Bourgmayer Date: Tue, 8 Mar 2022 18:22:53 +0100 Subject: [PATCH 48/68] Trigger FileObserver from EDriveApplication instead of FileObserverService - Remove FileObserverService because useless - Add "StartRecursiveFileObserver()" and "StopRecursiveFileObserver()" method in EDriveApplication. To allow services to stop/start the FileObserver. - Replace intent for FileObserverService by call in InitializerService by EDriveApplication.startRecursiveFileObserver()" - Add a call to EdriveApplication.StopRecursiveFileObserver() method in ResetService - Remove FileObserverService entry in AndroidManifest.xml - Add a "watching" boolean field in RecursiveFileObserver and "isWatching()" public method --- app/src/main/AndroidManifest.xml | 8 ---- .../foundation/e/drive/EdriveApplication.java | 29 ++++++++++++ .../FileObservers/RecursiveFileObserver.java | 9 +++- .../e/drive/services/FileObserverService.java | 46 ------------------- .../e/drive/services/InitializerService.java | 7 ++- .../e/drive/services/ResetService.java | 3 ++ 6 files changed, 45 insertions(+), 57 deletions(-) delete mode 100644 app/src/main/java/foundation/e/drive/services/FileObserverService.java diff --git a/app/src/main/AndroidManifest.xml b/app/src/main/AndroidManifest.xml index fa37c77d..7337a890 100644 --- a/app/src/main/AndroidManifest.xml +++ b/app/src/main/AndroidManifest.xml @@ -53,14 +53,6 @@ http://www.gnu.org/licenses/gpl.html - - - - - Date: Wed, 9 Mar 2022 11:39:18 +0100 Subject: [PATCH 49/68] update FileFilter in RecursiveFileObserver --- .../e/drive/FileObservers/RecursiveFileObserver.java | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/app/src/main/java/foundation/e/drive/FileObservers/RecursiveFileObserver.java b/app/src/main/java/foundation/e/drive/FileObservers/RecursiveFileObserver.java index 3b06faab..282789b1 100644 --- a/app/src/main/java/foundation/e/drive/FileObservers/RecursiveFileObserver.java +++ b/app/src/main/java/foundation/e/drive/FileObservers/RecursiveFileObserver.java @@ -29,7 +29,8 @@ public class RecursiveFileObserver extends FileObserver { private final static FileFilter watchableDirectoriesFilter = new FileFilter() { @Override public boolean accept(File file) { - return file.isDirectory() && !file.getName().equals(".") && !file.getName().equals(".."); + return file.isDirectory() && !file.getName().startsWith("."); + } }; @@ -61,6 +62,7 @@ public class RecursiveFileObserver extends FileObserver { } else { file = new File(mPath, path); } + notify(event, file); } -- GitLab From e3bbb271434c73203207389f3954cccb4fd1c1c8 Mon Sep 17 00:00:00 2001 From: vincent Bourgmayer Date: Wed, 9 Mar 2022 16:53:23 +0100 Subject: [PATCH 50/68] OperationManagerService provide binder on 'OnBind()' method - Add a private final Binder implementation as class's property - Return the binder instance on 'onBind(Intent intent)' method --- .../e/drive/services/OperationManagerService.java | 10 +++++++++- 1 file changed, 9 insertions(+), 1 deletion(-) diff --git a/app/src/main/java/foundation/e/drive/services/OperationManagerService.java b/app/src/main/java/foundation/e/drive/services/OperationManagerService.java index c523f54c..343a462a 100644 --- a/app/src/main/java/foundation/e/drive/services/OperationManagerService.java +++ b/app/src/main/java/foundation/e/drive/services/OperationManagerService.java @@ -15,6 +15,7 @@ import android.app.PendingIntent; import android.app.Service; import android.content.Context; import android.content.Intent; +import android.os.Binder; import android.os.Bundle; import android.os.Handler; import android.os.IBinder; @@ -49,6 +50,13 @@ import foundation.e.drive.utils.ServiceExceptionHandler; */ public class OperationManagerService extends Service implements OnRemoteOperationListener{ private final static String TAG = OperationManagerService.class.getSimpleName(); + private final Binder binder = new Binder(){ + OperationManagerService getService(){ + return OperationManagerService.this; + } + }; + + private int workerAmount = 0; //Number of thread available to execute RemoteOperation private boolean[] mThreadWorkingState; //State of the threads; true mean the thread is working @@ -313,7 +321,7 @@ public class OperationManagerService extends Service implements OnRemoteOperatio @Nullable @Override public IBinder onBind(Intent intent) { - throw new UnsupportedOperationException(); + return binder; } @Override -- GitLab From a49b3ffc805b6b04b8725b8a3c09ae89875813c7 Mon Sep 17 00:00:00 2001 From: vincent Bourgmayer Date: Thu, 10 Mar 2022 16:30:21 +0100 Subject: [PATCH 51/68] Add SynchronizationService This service will replace OperationManagerService in next modification. It is a bound and started Service which will run undefinitively - Add new services.SynchronizationService - Update EdriveApplication.java to start SynchronizationService when device reboot (if all condition are met) - Update services.ResetService.java to stop SynchronizationService when account is removed - Update AndroidManifest.xml to replace OperationManagerService by SynchronizationService - Update InitializerService to start SynchronizationService when its jobs is over --- app/src/main/AndroidManifest.xml | 2 +- .../foundation/e/drive/EdriveApplication.java | 19 +- .../e/drive/services/InitializerService.java | 4 +- .../e/drive/services/ResetService.java | 7 +- .../services/SynchronizationService.java | 231 ++++++++++++++++++ 5 files changed, 249 insertions(+), 14 deletions(-) create mode 100644 app/src/main/java/foundation/e/drive/services/SynchronizationService.java diff --git a/app/src/main/AndroidManifest.xml b/app/src/main/AndroidManifest.xml index 7337a890..29b9ba12 100644 --- a/app/src/main/AndroidManifest.xml +++ b/app/src/main/AndroidManifest.xml @@ -64,7 +64,7 @@ http://www.gnu.org/licenses/gpl.html - + operationsQueue; + private Hashtable startedOperations; //Operations which are running + private Account account; + private final int workerAmount = 4; + private boolean[] threadWorkingState; //State of the threads; true mean the thread is working + private Thread[] threadPool; + private OwnCloudClient client; + private OperationHandler handler; + + @Override + public int onStartCommand(Intent intent, int flags, int startId) { + Log.d(TAG, "onStartCommand()"); + + final SharedPreferences prefs = getSharedPreferences(AppConstants.SHARED_PREFERENCE_NAME, Context.MODE_PRIVATE); + + if (prefs.getString(AccountManager.KEY_ACCOUNT_NAME, null) != null) { + Log.w(TAG, "No account available. Stop SynchronizationService"); + stopSelf(); + return START_NOT_STICKY; + } + + + account = (Account) intent.getParcelableExtra("account"); + operationsQueue = new ConcurrentLinkedDeque<>(); + threadPool = new Thread[workerAmount]; + threadWorkingState = new boolean[workerAmount]; + client = DavClientProvider.getInstance().getClientInstance(account, getApplicationContext()); + handler = new OperationHandler(this); + + return START_REDELIVER_INTENT; + } + + @Nullable + @Override + public IBinder onBind(Intent intent) { + return binder; + } + + @Override + public void onLowMemory() { + Log.w(TAG, "System is low on memory. Service might get killed. Setting KEY_OMS_IS_WORKING to false"); + getSharedPreferences(AppConstants.SHARED_PREFERENCE_NAME, Context.MODE_PRIVATE).edit() + .putBoolean(AppConstants.KEY_OMS_IS_WORKING, false) + .apply(); + } + + public boolean queueOperation(ComparableOperation operation){ + return operationsQueue.add(operation); + } + + public boolean queueOperations(List operations){ + return operationsQueue.addAll(operations); + } + + public void startSynchronization(){ + Log.d(TAG, "startAllThreads"); + for(int i =-1; ++i < workerAmount;){ + this.startWorker(i); + } + } + + private void startWorker(int threadIndex){ + if (operationsQueue.isEmpty()) return; + if (!threadWorkingState[threadIndex] && CommonUtils.haveNetworkConnexion(getApplicationContext())) { //check if the thread corresponding to threadIndex isn't already working + + ComparableOperation operation = this.operationsQueue.poll(); //return null if deque is empty + if (operation != null) { + Log.v(TAG, " an operation has been poll from queue"); + + if (CommonUtils.isThisSyncAllowed(account, operation.isMediaType())) { + startedOperations.put(operation.toRemoteOperation(), threadIndex); + threadPool[threadIndex] = operation.toRemoteOperation().execute(client, this, handler); + threadWorkingState[threadIndex] = true; + } + } + } + } + + @Override + public void onRemoteOperationFinish(RemoteOperation callerOperation, RemoteOperationResult result) { + Log.d(TAG, "onRemoteOperationFinish()"); + Integer threadIndex = this.startedOperations.remove(callerOperation); + if (threadIndex != null) { + this.threadWorkingState[threadIndex] = false; + this.startWorker(threadIndex); + } + + if (callerOperation instanceof RemoveFileOperation){ + if ( result.isSuccess() ) { + DbHelper.manageSyncedFileStateDB( ( ( RemoveFileOperation ) callerOperation ).getSyncedFileState(), + "DELETE", this); + } + } else { + String operationClassName = callerOperation.getClass().getSimpleName(); + switch (result.getCode()) { + case OK: + Log.d(TAG, operationClassName + " Succeed"); + break; + case SYNC_CONFLICT: + //Case specific to UploadFileOperation + Log.e(TAG, operationClassName+" : Sync_conflict : File is already up to date"); + break; + case INVALID_OVERWRITE: + Log.e(TAG, operationClassName + " => invalid_overwrite :\n remote file and local file doesn't have the same size"); + break; + case UNKNOWN_ERROR: + if (callerOperation instanceof UploadFileOperation) { + if(result.getData() != null) { + int rowAffected = DbHelper.forceFoldertoBeRescan(((Long) result.getData().get(0)).intValue(), getApplicationContext()); + Log.e(TAG, " Upload failed for unknown reason.\n Force folder to be rescan next time (row affected) :" + rowAffected); + } else { + Log.w(TAG, "result.getData() for UploadFileOperation returned null"); + } + } else if (callerOperation instanceof DownloadFileOperation) { + Log.e(TAG, " Download: Unknown_error : failed"); + } + break; + case FORBIDDEN: + if (callerOperation instanceof UploadFileOperation) { + if (result.getData() != null) { + int rowAffected = DbHelper.forceFoldertoBeRescan(((Long) result.getData().get(0)).intValue(), getApplicationContext()); + Log.e(TAG, " Upload: Forbidden : Can't get syncedFileState, no remote path defined. Force folder to be rescan next time (row affected) :" + rowAffected); + } else { + Log.w(TAG, "result.getData() for UploadFileOperation returned null"); + } + } else if (callerOperation instanceof DownloadFileOperation) { + Log.e(TAG, "Download : Forbidden: Can't get syncedFileState, no local path defined"); + } + break; + case QUOTA_EXCEEDED: + //Case specific to UploadFileOperation + Log.w(TAG, "Quota_EXCEEDED"); + + NotificationManager nM = (NotificationManager)getSystemService(Context.NOTIFICATION_SERVICE); + + Notification notif = new Notification.Builder(this, AppConstants.notificationChannelID) + .setContentIntent(PendingIntent.getActivity(getApplicationContext(), + 0, + new Intent(Intent.ACTION_VIEW, client.getBaseUri()), + 0)) + .setContentText("Your drive lacks of space. Tap to check " + client.getBaseUri()) + .setSmallIcon(android.R.drawable.stat_sys_warning) + .build(); + + nM.notify(1,notif ); + break; + case FILE_NOT_FOUND: + //Case specific to DownloadFileOperation + Log.e(TAG, operationClassName+" : File_not_found: File not found after download"); + break; + case ETAG_UNCHANGED: + //Case specific to DownloadFileOperation + Log.e(TAG, operationClassName+" : Sync_conflict: File is already up to date"); + break; + + } //Close switch + } //Close else + } + + /** + * Handler for the class + */ + static class OperationHandler extends Handler { + private final String TAG = SynchronizationService.OperationHandler.class.getSimpleName(); + + private final WeakReference serviceWeakRef; + + OperationHandler(SynchronizationService mOperationService){ + serviceWeakRef = new WeakReference<>(mOperationService); + } + + @Override + public void handleMessage(Message msg) { + Log.i(TAG, "handler.handleMessage()"); + Bundle data = msg.getData(); + + serviceWeakRef.get() + .threadWorkingState[data.getInt("thread index")] = data.getBoolean("mThreadWorkingState"); + } + } +} -- GitLab From 7d596aeb72c2806211ac302fa97b80d5029b64d0 Mon Sep 17 00:00:00 2001 From: vincent Bourgmayer Date: Thu, 10 Mar 2022 17:43:17 +0100 Subject: [PATCH 52/68] Implement ObserverService binding to SynchronizationService - Update SynchronizationService.enqueueOperations() parameters: List is now Collections - Implement Binding component into ObserverService - Replace intent for OperationManagerService by binding usage to pass list of operations to perform into SynchronizationService - Fixed coding style: add spaces around if/else statement in InitializerService --- .../e/drive/services/ObserverService.java | 124 ++++++++++-------- .../services/SynchronizationService.java | 15 ++- 2 files changed, 81 insertions(+), 58 deletions(-) diff --git a/app/src/main/java/foundation/e/drive/services/ObserverService.java b/app/src/main/java/foundation/e/drive/services/ObserverService.java index a0e8c7eb..f895ca28 100644 --- a/app/src/main/java/foundation/e/drive/services/ObserverService.java +++ b/app/src/main/java/foundation/e/drive/services/ObserverService.java @@ -11,8 +11,10 @@ package foundation.e.drive.services; import android.accounts.Account; import android.accounts.AccountManager; import android.app.Service; +import android.content.ComponentName; import android.content.Context; import android.content.Intent; +import android.content.ServiceConnection; import android.content.SharedPreferences; import android.content.pm.PackageInfo; import android.os.Handler; @@ -41,6 +43,7 @@ import foundation.e.drive.fileFilters.FileFilterFactory; import foundation.e.drive.fileFilters.OnlyFileFilter; import foundation.e.drive.models.SyncedFolder; import foundation.e.drive.models.SyncedFileState; +import foundation.e.drive.operations.ComparableOperation; import foundation.e.drive.operations.DownloadFileOperation; import foundation.e.drive.operations.ListFileRemoteOperation; import foundation.e.drive.operations.RemoveFileOperation; @@ -69,9 +72,27 @@ public class ObserverService extends Service implements OnRemoteOperationListene private boolean isWorking = false; private int initialFolderCounter; private Account mAccount; - private HashMap operationsForIntent; - /* Lifecycle Methods */ + private HashMap operationsForIntent; //integer is SyncedFileState id; Parcelable is the operation + + private SynchronizationService synchronizationService; + private boolean boundToSynchronizationService = false; + private ServiceConnection SynchronizationServiceConnection = new ServiceConnection() { + @Override + public void onServiceConnected(ComponentName componentName, IBinder iBinder) { + SynchronizationService.SynchronizationBinder binder = (SynchronizationService.SynchronizationBinder) iBinder; + synchronizationService = binder.getService(); + boundToSynchronizationService = true; + } + + @Override + public void onServiceDisconnected(ComponentName componentName) { + Log.e(TAG, "onServiceDisconnected"); + boundToSynchronizationService = false; + } + }; + + /* Lifecycle Methods */ @Override public void onDestroy(){ Log.i(TAG, "onDestroy()"); @@ -92,13 +113,13 @@ public class ObserverService extends Service implements OnRemoteOperationListene initialFolderCounter = prefs.getInt(AppConstants.INITIALFOLDERS_NUMBER, 0); // Check if account is invalid - if(this.mAccount == null){ + if (this.mAccount == null){ Log.w(TAG, "No account registered"); return super.onStartCommand(intent, flags, startId); } //check if user have disable eDrive's sync in account's settings - if(!CommonUtils.isMediaSyncEnabled(mAccount) && !CommonUtils.isSettingsSyncEnabled(mAccount) ){ + if (!CommonUtils.isMediaSyncEnabled(mAccount) && !CommonUtils.isSettingsSyncEnabled(mAccount) ){ Log.w(TAG, "eDrive syncing has been disabled in /e/ account's settings"); return super.onStartCommand(intent, flags, startId); } @@ -112,13 +133,13 @@ public class ObserverService extends Service implements OnRemoteOperationListene } //Check this service isn't already working - if(isWorking){ + if (isWorking){ Log.w(TAG, "ObserverService is already working"); return super.onStartCommand(intent,flags,startId); } //check OperationManagerService isn't working - if(prefs.getBoolean(AppConstants.KEY_OMS_IS_WORKING, false)){ + if (prefs.getBoolean(AppConstants.KEY_OMS_IS_WORKING, false)){ Log.w(TAG, "OperationManagerService is still performing some operation"); return super.onStartCommand(intent,flags, startId); } @@ -143,6 +164,10 @@ public class ObserverService extends Service implements OnRemoteOperationListene return super.onStartCommand( intent, flags, startId ); } this.operationsForIntent = new HashMap<>(); + + Intent SynchronizationServiceIntent = new Intent(this.getApplicationContext(), SynchronizationService.class); + bindService(SynchronizationServiceIntent, SynchronizationServiceConnection, Context.BIND_AUTO_CREATE); + begin(); return super.onStartCommand( intent, flags, startId ); } @@ -191,7 +216,7 @@ public class ObserverService extends Service implements OnRemoteOperationListene Log.i(TAG, "clearCachedFile()"); //Load subfiles into external cache file File[] fileArray = this.getApplicationContext().getExternalCacheDir().listFiles(new OnlyFileFilter() ); - if(fileArray != null) { + if (fileArray != null) { boolean toRemove; for (int i = -1, size = fileArray.length; ++i < size; ) { toRemove = true; @@ -200,7 +225,7 @@ public class ObserverService extends Service implements OnRemoteOperationListene Log.v(TAG+"_handleCachedFile()", "Deletion of cached file: " + deleteResult); } } - }else{ + } else { Log.e(TAG+"_handleCachedFile()", "Array of cached file is null"); } } @@ -215,7 +240,7 @@ public class ObserverService extends Service implements OnRemoteOperationListene Log.i(TAG, "startScan("+remote+")"); this.mSyncedFolders = loadSyncedFolders(); - if(mSyncedFolders.isEmpty() ){ + if (mSyncedFolders.isEmpty() ){ Log.w(TAG, "List of synced folders is empty"); this.stopSelf(); return; @@ -238,7 +263,7 @@ public class ObserverService extends Service implements OnRemoteOperationListene } catch (IllegalArgumentException e){ Log.e(TAG, e.toString() ); } - }else{ + } else { Log.w(TAG, "OwnCloudClient is null"); return; } @@ -256,13 +281,13 @@ public class ObserverService extends Service implements OnRemoteOperationListene boolean mediaSyncEnabled = CommonUtils.isMediaSyncEnabled(mAccount); boolean settingsSyncedEnabled = CommonUtils.isSettingsSyncEnabled(mAccount); - if(mediaSyncEnabled && settingsSyncedEnabled){ + if (mediaSyncEnabled && settingsSyncedEnabled){ return DbHelper.getAllSyncedFolders(this); - }else if(mediaSyncEnabled){ + } else if (mediaSyncEnabled){ return DbHelper.getSyncedFolderList(this, true); - }else if(settingsSyncedEnabled){ + } else if (settingsSyncedEnabled){ return DbHelper.getSyncedFolderList(this, false); - }else{ + } else { return new ArrayList(); } } @@ -280,7 +305,7 @@ public class ObserverService extends Service implements OnRemoteOperationListene @Override public void onRemoteOperationFinish(RemoteOperation operation, RemoteOperationResult result ) { Log.i( TAG, "onRemoteOperationFinish()" ); - if( operation instanceof ListFileRemoteOperation) { + if ( operation instanceof ListFileRemoteOperation) { if (result.isSuccess()) { List resultDatas = result.getData(); if (resultDatas != null) { @@ -303,15 +328,9 @@ public class ObserverService extends Service implements OnRemoteOperationListene Log.v(TAG, "operationsForIntent contains "+ operationsForIntent.size() ); //After everything has been scanned. Send Intent to OperationmanagerService with data in bundle - if(operationsForIntent != null && !operationsForIntent.isEmpty()) { - Intent OMSIntent = new Intent(this, OperationManagerService.class); - for(Map.Entry entry: operationsForIntent.entrySet()){ - OMSIntent.putExtra(entry.getKey()+"", entry.getValue()); - } - - OMSIntent.putExtra("account", mAccount); - startService(OMSIntent); - }else{ + if (operationsForIntent != null && !operationsForIntent.isEmpty()) { + this.synchronizationService.queueOperations(operationsForIntent.values()); + } else { Log.w(TAG, "There is no file to sync."); getSharedPreferences(AppConstants.SHARED_PREFERENCE_NAME, Context.MODE_PRIVATE) .edit() @@ -321,6 +340,7 @@ public class ObserverService extends Service implements OnRemoteOperationListene } this.isWorking = false; + unbindService(SynchronizationServiceConnection); this.stopSelf(); } } @@ -333,7 +353,7 @@ public class ObserverService extends Service implements OnRemoteOperationListene List result = new ArrayList<>(); for(int i = -1, size = this.mSyncedFolders.size(); ++i < size;){ SyncedFolder syncedFolder = this.mSyncedFolders.get(i); - if(syncedFolder.isToSync() ){ + if (syncedFolder.isToSync() ){ result.add( (long) syncedFolder.getId() ); } } @@ -367,7 +387,7 @@ public class ObserverService extends Service implements OnRemoteOperationListene SyncedFileState syncedFileState = syncedFileListIterator.next(); //ignore hidden file from db - if(syncedFileState.isMediaType() && syncedFileState.getName().startsWith(".")){ + if (syncedFileState.isMediaType() && syncedFileState.getName().startsWith(".")){ syncedFileListIterator.remove(); continue; } @@ -392,7 +412,7 @@ public class ObserverService extends Service implements OnRemoteOperationListene syncedFileState.setLastETAG(remoteFile.getEtag()); int affectedRows = DbHelper.manageSyncedFileStateDB(syncedFileState, "UPDATE", this); Log.v(TAG, affectedRows + " syncedFileState.s row in DB has been updated."); - }else { + } else { Log.i(TAG, "Add download operation for file "+syncedFileState.getId()); DownloadFileOperation downloadFileOperation = new DownloadFileOperation(remoteFile, syncedFileState); this.operationsForIntent.put(syncedFileState.getId(), downloadFileOperation); @@ -403,7 +423,7 @@ public class ObserverService extends Service implements OnRemoteOperationListene } } - if( correspondant_found )continue; + if ( correspondant_found )continue; //If we get here, RemoteFile is a new file to download Log.v(TAG, "SyncedFileState corresponding to remoteFile not found."); @@ -458,20 +478,20 @@ public class ObserverService extends Service implements OnRemoteOperationListene //Loop through remaining file state for(int i = -1, size = syncedFileStates.size(); ++i < size; ){ SyncedFileState syncedFileState = syncedFileStates.get(i); - if( !CommonUtils.isThisSyncAllowed( mAccount, syncedFileState.isMediaType() ) ){ + if ( !CommonUtils.isThisSyncAllowed( mAccount, syncedFileState.isMediaType() ) ){ Log.d(TAG, "Sync of current file: "+syncedFileState.getName()+" isn't allowed"); continue; } //Check that file has already been synced fully - if( syncedFileState.isLastEtagStored() && syncedFileState.getLocalLastModified() > 0L) { + if ( syncedFileState.isLastEtagStored() && syncedFileState.getLocalLastModified() > 0L) { //Get local file File file = new File( syncedFileStates.get(i).getLocalPath() ); //Try to remove local file boolean fileExists = file.exists(); - if( fileExists) { + if ( fileExists) { Log.d(TAG, file.getName()+" exists *1"); //delete file int rowAffected = getContentResolver().delete(MediaStore.Files.getContentUri("external"), @@ -483,11 +503,11 @@ public class ObserverService extends Service implements OnRemoteOperationListene } //if it succeed, remove syncedFileState in DB - if(! fileExists ) { + if (! fileExists ) { //It means that file has been correctly deleted from device. So update DB. if (DbHelper.manageSyncedFileStateDB(syncedFileState, "DELETE", this) <= 0) Log.e(TAG, "SyncedFileState row hasn't been deleted from DB"); - }else + } else Log.w(TAG, "local file:"+file.getName()+" still exist and can't be remove"); } } @@ -538,7 +558,7 @@ public class ObserverService extends Service implements OnRemoteOperationListene List folderIdList= new ArrayList<>(); boolean contentToSyncFound = false; //Regenere list of application's package - if(CommonUtils.isSettingsSyncEnabled(mAccount)) generateAppListFile(); + if (CommonUtils.isSettingsSyncEnabled(mAccount)) generateAppListFile(); ListIterator iterator = mSyncedFolders.listIterator() ; @@ -548,13 +568,13 @@ public class ObserverService extends Service implements OnRemoteOperationListene Log.d(TAG, "SyncedFolder :"+syncedFolder.getLibelle()+", "+syncedFolder.getLocalFolder()+", "+syncedFolder.getLastModified()+", "+syncedFolder.isScanLocal()+", "+syncedFolder.getId() ); //Check it's not a hidden file - if(syncedFolder.isMediaType() && CommonUtils.getFileNameFromPath(syncedFolder.getLocalFolder()).startsWith(".")){ + if (syncedFolder.isMediaType() && CommonUtils.getFileNameFromPath(syncedFolder.getLocalFolder()).startsWith(".")){ iterator.remove(); continue; } //Check it can be scann from local - if(!syncedFolder.isScanLocal()){ + if (!syncedFolder.isScanLocal()){ iterator.remove(); continue; } @@ -567,7 +587,7 @@ public class ObserverService extends Service implements OnRemoteOperationListene if (syncedFolder_id > 0) { Log.v(TAG, "Folder has been registered in DB"); syncedFolder.setId(syncedFolder_id); - }else { + } else { Log.w(TAG, "syncedFolder " + syncedFolder.getLocalFolder() + " remove iterator because it hasn't been registered in DB or already stored"); iterator.remove(); continue; @@ -578,7 +598,7 @@ public class ObserverService extends Service implements OnRemoteOperationListene Log.d(TAG, "Local Folder (last modified / exists): "+localFolder.lastModified()+", "+localFolder.exists() ); //Check if local folder exists - if(!localFolder.exists()){ + if (!localFolder.exists()){ Log.v(TAG, "local folder doesn't exist anymore . So content has change"); contentToSyncFound = true; folderIdList.add( (long) syncedFolder.getId() ); @@ -588,7 +608,7 @@ public class ObserverService extends Service implements OnRemoteOperationListene boolean folderHasChange = false; //consider by default that file hadn't change //Check if folder had change - if(localFolder.lastModified() > syncedFolder.getLastModified() ) { //compare last modified date + if (localFolder.lastModified() > syncedFolder.getLastModified() ) { //compare last modified date Log.v(TAG, "local folder has changed"); syncedFolder.setLastModified( localFolder.lastModified() ); //@Todo: it would be better to set it after all it's content has been synced contentToSyncFound = true; //at least one dir has changed @@ -603,7 +623,7 @@ public class ObserverService extends Service implements OnRemoteOperationListene Log.v(TAG, "loop through subfiles"); for (int i = -1, subEltSize = (subElements != null)? subElements.length: 0; ++i < subEltSize; ) { File subElt = subElements[i]; - if(subElt == null) continue; + if (subElt == null) continue; if (subElt.isDirectory()) { //if its a subfolder add it to syncedFolder list //if a subfolder is found, add it to syncedFolder list Log.v(TAG, "subfile "+subElt.getAbsolutePath()+" is a directory."); @@ -621,12 +641,12 @@ public class ObserverService extends Service implements OnRemoteOperationListene } //end of iterator loop - if(contentToSyncFound) { + if (contentToSyncFound) { DbHelper.updateSyncedFolders(mSyncedFolders, this); //@ToDo: maybe do this when all contents will be synced. List syncedFileStates = DbHelper.getSyncedFileStatesByFolders(this, folderIdList); - if(!syncedFileStates.isEmpty() || !fileList.isEmpty() ) { + if (!syncedFileStates.isEmpty() || !fileList.isEmpty() ) { handleLocalFiles(fileList, syncedFileStates); } } @@ -662,7 +682,7 @@ public class ObserverService extends Service implements OnRemoteOperationListene SyncedFileState syncedFileState = syncedFileListIterator.next(); //Ignore hidden media file store in DB - if(syncedFileState.isMediaType() && syncedFileState.getName().startsWith(".")){ + if (syncedFileState.isMediaType() && syncedFileState.getName().startsWith(".")){ syncedFileListIterator.remove(); continue; } @@ -670,11 +690,11 @@ public class ObserverService extends Service implements OnRemoteOperationListene Log.v(TAG, syncedFileState.getLocalPath()+", "+syncedFileState.getId()+", "+syncedFileState.getLocalLastModified()); //if syncedFileState correspond to local file - if( syncedFileState.getLocalPath().equals( filePath ) ){ + if ( syncedFileState.getLocalPath().equals( filePath ) ){ correspondant_found = true; //If no etag is stored in sfs, the file hasn't been sync up to server. then do upload - if( syncedFileState.getLocalLastModified() < localFile.lastModified() || !syncedFileState.isLastEtagStored()){ + if ( syncedFileState.getLocalLastModified() < localFile.lastModified() || !syncedFileState.isLastEtagStored()){ Log.d(TAG+"_handleLocalFiles()", syncedFileState.getName()+" file has been modified or never sync" ); boolean checkEtag = false; @@ -697,7 +717,7 @@ public class ObserverService extends Service implements OnRemoteOperationListene break; } } - if( correspondant_found ) continue; + if ( correspondant_found ) continue; //if no correspondance, then it is a new file Log.v(TAG, "this is a new file to sync"); @@ -707,19 +727,19 @@ public class ObserverService extends Service implements OnRemoteOperationListene //look into synced folders if folder path exist for(SyncedFolder syncedFolder : mSyncedFolders){ - if(syncedFolder.getLocalFolder().equals(parentPath)){ + if (syncedFolder.getLocalFolder().equals(parentPath)){ //create the syncedFile State SyncedFileState newSyncedFileState = new SyncedFileState(-1, localFile.getName(), filePath, syncedFolder.getRemoteFolder() + localFile.getName(), "", 0, syncedFolder.getId(), syncedFolder.isMediaType()); //Store it in DB int storedId = DbHelper.manageSyncedFileStateDB(newSyncedFileState, "INSERT", this); - if(storedId > 0){ + if (storedId > 0){ newSyncedFileState.setId( storedId ); Log.i(TAG, "Add upload operation for new file "+storedId); //create UploadOperation and add it into bundle UploadFileOperation uploadOperation = new UploadFileOperation(newSyncedFileState, syncedFolder.isScanRemote()); this.operationsForIntent.put(storedId, uploadOperation); - }else{ + } else { Log.w(TAG, "The new file to synced cannot be store in DB. Ignore it"); } break; @@ -738,13 +758,13 @@ public class ObserverService extends Service implements OnRemoteOperationListene Log.i(TAG, "handleLocalRemainingSyncedFileState(...)"); //Loop through remaining SyncedFileState for(SyncedFileState fileState : syncedFileStates){ - if(fileState.isLastEtagStored() && fileState.getLocalLastModified() > 0L){ + if (fileState.isLastEtagStored() && fileState.getLocalLastModified() > 0L){ //try to get File File file = new File(fileState.getLocalPath()); Log.v(TAG, "File : "+file.getAbsolutePath()+","+file.exists()); - if(file.exists()){ + if (file.exists()){ Log.w(TAG, "The file still exist. There is a problem!"); - }else{ + } else { Log.i(TAG, "Add remove operation for file "+fileState.getId()); RemoveFileOperation removeOperation = new RemoveFileOperation(fileState); this.operationsForIntent.put(fileState.getId(), removeOperation); 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 1b6a6b23..9da67bd6 100644 --- a/app/src/main/java/foundation/e/drive/services/SynchronizationService.java +++ b/app/src/main/java/foundation/e/drive/services/SynchronizationService.java @@ -24,6 +24,7 @@ import com.owncloud.android.lib.common.operations.RemoteOperation; import com.owncloud.android.lib.common.operations.RemoteOperationResult; import java.lang.ref.WeakReference; +import java.util.Collection; import java.util.Hashtable; import java.util.List; import java.util.concurrent.ConcurrentLinkedDeque; @@ -42,11 +43,7 @@ import foundation.e.drive.utils.DavClientProvider; */ public class SynchronizationService extends Service implements OnRemoteOperationListener { private final static String TAG = SynchronizationService.class.getSimpleName(); - private final Binder binder = new Binder(){ - SynchronizationService getService(){ - return SynchronizationService.this; - } - }; + private final SynchronizationBinder binder = new SynchronizationBinder(); private ConcurrentLinkedDeque operationsQueue; private Hashtable startedOperations; //Operations which are running @@ -98,7 +95,7 @@ public class SynchronizationService extends Service implements OnRemoteOperation return operationsQueue.add(operation); } - public boolean queueOperations(List operations){ + public boolean queueOperations(Collection operations){ return operationsQueue.addAll(operations); } @@ -228,4 +225,10 @@ public class SynchronizationService extends Service implements OnRemoteOperation .threadWorkingState[data.getInt("thread index")] = data.getBoolean("mThreadWorkingState"); } } + + public class SynchronizationBinder extends Binder{ + SynchronizationService getService(){ + return SynchronizationService.this; + } + } } -- GitLab From 67c7a85b16f3609e21f789fa0f8d49524e4dbde9 Mon Sep 17 00:00:00 2001 From: vincent Bourgmayer Date: Thu, 10 Mar 2022 17:50:08 +0100 Subject: [PATCH 53/68] Remove OperationManagerService - Remove OperationManagerService.java - Remove OperationManagerServiceTest.java - Update ServiceExceptionHandler to remove code related to OperationManagerService --- .../services/OperationManagerService.java | 334 ------------------ .../drive/utils/ServiceExceptionHandler.java | 10 - .../services/OperationManagerServiceTest.java | 108 ------ 3 files changed, 452 deletions(-) delete mode 100644 app/src/main/java/foundation/e/drive/services/OperationManagerService.java delete mode 100644 app/src/test/java/foundation/e/drive/services/OperationManagerServiceTest.java diff --git a/app/src/main/java/foundation/e/drive/services/OperationManagerService.java b/app/src/main/java/foundation/e/drive/services/OperationManagerService.java deleted file mode 100644 index 343a462a..00000000 --- a/app/src/main/java/foundation/e/drive/services/OperationManagerService.java +++ /dev/null @@ -1,334 +0,0 @@ -/* - * Copyright © Vincent Bourgmayer (/e/ foundation). - * 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.services; - -import android.accounts.Account; -import android.app.Notification; -import android.app.NotificationManager; -import android.app.PendingIntent; -import android.app.Service; -import android.content.Context; -import android.content.Intent; -import android.os.Binder; -import android.os.Bundle; -import android.os.Handler; -import android.os.IBinder; -import android.os.Message; -import android.os.Parcelable; -import android.util.Log; - -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.lang.ref.WeakReference; -import java.util.Hashtable; -import java.util.concurrent.ConcurrentLinkedDeque; - -import foundation.e.drive.database.DbHelper; -import foundation.e.drive.operations.ComparableOperation; -import foundation.e.drive.operations.DownloadFileOperation; -import foundation.e.drive.operations.RemoveFileOperation; -import foundation.e.drive.operations.UploadFileOperation; -import foundation.e.drive.utils.AppConstants; -import foundation.e.drive.utils.CommonUtils; -import foundation.e.drive.utils.DavClientProvider; -import foundation.e.drive.utils.ServiceExceptionHandler; - -/** - * @author Vincent Bourgmayer - * Service to do upload, remove and download operation. - */ -public class OperationManagerService extends Service implements OnRemoteOperationListener{ - private final static String TAG = OperationManagerService.class.getSimpleName(); - private final Binder binder = new Binder(){ - OperationManagerService getService(){ - return OperationManagerService.this; - } - }; - - - - private int workerAmount = 0; //Number of thread available to execute RemoteOperation - private boolean[] mThreadWorkingState; //State of the threads; true mean the thread is working - private Thread[] mThreadPool; //The threads to use - - private ConcurrentLinkedDeque mOperationsQueue; // Queue of Operation - private Hashtable mStartedOperations; //Operations which are running - - private OperationManagerHandler mHandler; - private OwnCloudClient mClient; //ClientObject - private Account mAccount; //Will be used soon - - @Override - public void onDestroy() { - Log.i(TAG, "onDestroy()"); - super.onDestroy(); - } - - - /** - * Start to run all threads - */ - private void startAllThreads(){ - Log.i(TAG, "startAllThreads"); - for(int i =-1; ++i < workerAmount;){ - this.startWork(i); - } - } - - /** - * retrieve an operation from queue and execute it if a thread is available. - * @param threadIndex index of thread which execute job. - */ - private synchronized void startWork( int threadIndex ){ - Log.i(TAG, "startWork("+threadIndex+")" ); - - //Check if operation queue is empty - if(mOperationsQueue.isEmpty()){ - boolean stillWorking = false; - //check that all other thread has finished. - for(boolean a : this.mThreadWorkingState){ - if(a){ - stillWorking = a; - break; - } - } - - if(!stillWorking) { - Log.i(TAG, "Operation queue is empty. all jobs Done. The End"); - //Register timestamp to allow to calculate min delay between two sync - getSharedPreferences(AppConstants.SHARED_PREFERENCE_NAME, Context.MODE_PRIVATE) - .edit() - .putLong(AppConstants.KEY_LAST_SYNC_TIME, System.currentTimeMillis()) - .putBoolean(AppConstants.KEY_OMS_IS_WORKING, false) - .apply(); - - stopSelf(); - return; - } - } - //start the new Job - if( !mThreadWorkingState[threadIndex] && CommonUtils.haveNetworkConnexion( getApplicationContext() ) ) { //check if the thread corresponding to threadIndex isn't already working - - ComparableOperation operation = this.mOperationsQueue.poll(); //return null if deque is empty - if (operation != null) { - Log.v(TAG, " an operation has been poll from queue"); - - if( CommonUtils.isThisSyncAllowed(mAccount, operation.isMediaType() ) ) { - mStartedOperations.put(operation.toRemoteOperation(), threadIndex); - this.mThreadPool[threadIndex] = operation.toRemoteOperation().execute(mClient, this, this.mHandler); - this.mThreadWorkingState[threadIndex] = true; - } - } - } //else : thread is already running or no network connexion - } - - /** - * Called when a remoteOperation finish - * @param operation the operation which finished - * @param result the result of the operation - */ - @Override - public void onRemoteOperationFinish(RemoteOperation operation, RemoteOperationResult result) { - Log.i(TAG, "onRemoteOperationFinish()"); - - // Start an another operation with the thread which has run this one - Integer threadIndex = this.mStartedOperations.remove(operation); - if(threadIndex != null) { - this.mThreadWorkingState[threadIndex] = false; - this.startWork(threadIndex); - } - - if(operation instanceof RemoveFileOperation){ - if( result.isSuccess() ) { - DbHelper.manageSyncedFileStateDB( ( ( RemoveFileOperation ) operation ).getSyncedFileState(), - "DELETE", this); - } - }else { - String operationClassName = operation.getClass().getSimpleName(); - switch (result.getCode()) { - case OK: - Log.d(TAG, operationClassName + " Succeed"); - break; - case SYNC_CONFLICT: - //Case specific to UploadFileOperation - Log.e(TAG, operationClassName+" : Sync_conflict : File is already up to date"); - break; - case INVALID_OVERWRITE: - Log.e(TAG, operationClassName + " => invalid_overwrite :\n remote file and local file doesn't have the same size"); - break; - case UNKNOWN_ERROR: - if (operation instanceof UploadFileOperation) { - if(result.getData() != null) { - int rowAffected = DbHelper.forceFoldertoBeRescan(((Long) result.getData().get(0)).intValue(), getApplicationContext()); - Log.e(TAG, " Upload failed for unknown reason.\n Force folder to be rescan next time (row affected) :" + rowAffected); - }else{ - Log.w(TAG, "result.getData() for UploadFileOperation returned null"); - } - } else if (operation instanceof DownloadFileOperation) { - Log.e(TAG, " Download: Unknown_error : failed"); - } - break; - case FORBIDDEN: - if (operation instanceof UploadFileOperation) { - - if(result.getData() != null) { - int rowAffected = DbHelper.forceFoldertoBeRescan(((Long) result.getData().get(0)).intValue(), getApplicationContext()); - Log.e(TAG, " Upload: Forbidden : Can't get syncedFileState, no remote path defined. Force folder to be rescan next time (row affected) :" + rowAffected); - }else{ - Log.w(TAG, "result.getData() for UploadFileOperation returned null"); - } - } else if (operation instanceof DownloadFileOperation) { - Log.e(TAG, "Download : Forbidden: Can't get syncedFileState, no local path defined"); - } - break; - case QUOTA_EXCEEDED: - //Case specific to UploadFileOperation - Log.w(TAG, "Quota_EXCEEDED"); - - NotificationManager nM = (NotificationManager)getSystemService(Context.NOTIFICATION_SERVICE); - - Notification notif = new Notification.Builder(this, AppConstants.notificationChannelID) - .setContentIntent(PendingIntent.getActivity(getApplicationContext(), - 0, - new Intent(Intent.ACTION_VIEW, mClient.getBaseUri()), - 0)) - .setContentText("Your drive lacks of space. Tap to check " + mClient.getBaseUri()) - .setSmallIcon(android.R.drawable.stat_sys_warning) - .build(); - - nM.notify(1,notif ); - break; - case FILE_NOT_FOUND: - //Case specific to DownloadFileOperation - Log.e(TAG, operationClassName+" : File_not_found: File not found after download"); - break; - case ETAG_UNCHANGED: - //Case specific to DownloadFileOperation - Log.e(TAG, operationClassName+" : Sync_conflict: File is already up to date"); - break; - - } //Close switch - } //Close else - } - - /** - * Handler for the class - */ - static class OperationManagerHandler extends Handler { - private final String TAG = OperationManagerHandler.class.getSimpleName(); - - private final WeakReference mOperationServiceWeakRef; - - OperationManagerHandler(OperationManagerService mOperationService){ - this.mOperationServiceWeakRef = new WeakReference<>(mOperationService); - } - - @Override - public void handleMessage(Message msg) { - Log.i(TAG, "handler.handleMessage()"); - Bundle data = msg.getData(); - - mOperationServiceWeakRef.get() - .mThreadWorkingState[data.getInt("thread index")] = data.getBoolean("mThreadWorkingState"); - } - } - - @Override - public int onStartCommand(Intent intent, int flags, int startId) { - Log.i(TAG, "onStartCommand()"); - - try{ - CommonUtils.setServiceUnCaughtExceptionHandler(this); - - if (null == intent || null == intent.getAction ()) { - String source = null == intent ? "intent" : "action"; - Log.e (TAG, source + " was null, flags=" + flags + " bits=" + Integer.toBinaryString (flags)); - //return START_STICKY; - } - - Bundle extras = intent.getExtras(); - Log.d(TAG, "OperationManagerService recieved "+(extras == null ? "null extras": extras.size()+" operations to perform") ); - - if(extras != null) { - //Load operation from intent - this.mOperationsQueue = new ConcurrentLinkedDeque<>(); - for (String key : extras.keySet()) { - - Parcelable parcelableObject = extras.getParcelable(key); - if (key.equals("account")) { - this.mAccount = (Account) parcelableObject; - } else if (parcelableObject.getClass().getName().equals(DownloadFileOperation.class.getName())) { - DownloadFileOperation download = (DownloadFileOperation) parcelableObject; - download.setContext(getApplicationContext()); - mOperationsQueue.add(download); - } else if (parcelableObject.getClass().getName().equals(UploadFileOperation.class.getName())) { - UploadFileOperation upload = (UploadFileOperation) parcelableObject; - upload.setContext(getApplicationContext()); - mOperationsQueue.add(upload); - } else if (parcelableObject.getClass().getName().equals(RemoveFileOperation.class.getName())) { - mOperationsQueue.add((RemoveFileOperation) parcelableObject); - } - } - - if(mAccount == null || mOperationsQueue.isEmpty()){ - Log.w(TAG, "No account or Operation queue is empty"); - return super.onStartCommand(intent, flags, startId); - } - - //Initialize class's field - this.workerAmount = 4; //This variable could be replace later by an option in settings - - this.mThreadPool = new Thread[workerAmount]; - this.mThreadWorkingState = new boolean[workerAmount]; - this.mHandler = new OperationManagerHandler(this); - this.mStartedOperations = new Hashtable(); - - mClient = DavClientProvider.getInstance().getClientInstance(mAccount, getApplicationContext()); - if (mClient != null) { - getSharedPreferences(AppConstants.SHARED_PREFERENCE_NAME, Context.MODE_PRIVATE) - .edit() - .putBoolean(AppConstants.KEY_OMS_IS_WORKING, true) - .apply(); - - startAllThreads(); - } else { - Log.w(TAG, "No Client, Can't Work!"); - stopSelf(); - } - }else{ - Log.w(TAG, "Intent's extras is null."); - } - }catch (Exception ex){ - Log.e("Exception", ex.getMessage()); - ex.printStackTrace(); - } - - return super.onStartCommand(intent, flags, startId); - } - - - @Nullable - @Override - public IBinder onBind(Intent intent) { - return binder; - } - - @Override - public void onLowMemory() { - Log.w(TAG, "System is low on memory. Service might get killed. Setting KEY_OMS_IS_WORKING to false"); - getSharedPreferences(AppConstants.SHARED_PREFERENCE_NAME, Context.MODE_PRIVATE).edit() - .putBoolean(AppConstants.KEY_OMS_IS_WORKING, false) - .apply(); - } -} diff --git a/app/src/main/java/foundation/e/drive/utils/ServiceExceptionHandler.java b/app/src/main/java/foundation/e/drive/utils/ServiceExceptionHandler.java index 250e791b..d94e1c21 100644 --- a/app/src/main/java/foundation/e/drive/utils/ServiceExceptionHandler.java +++ b/app/src/main/java/foundation/e/drive/utils/ServiceExceptionHandler.java @@ -7,7 +7,6 @@ */ package foundation.e.drive.utils; import android.app.Service; -import android.content.Context; import android.os.Environment; import android.util.Log; @@ -18,8 +17,6 @@ import java.io.PrintWriter; import java.io.StringWriter; import java.lang.Thread.UncaughtExceptionHandler; -import foundation.e.drive.services.OperationManagerService; - /** * @author Vincent Bourgmayer */ @@ -48,13 +45,6 @@ public class ServiceExceptionHandler implements UncaughtExceptionHandler{ @Override public void uncaughtException(Thread t, Throwable e) { Log.d(TAG, "Service class: "+service.getClass().getSimpleName()); - //IF OMS is crashing, set settings that it runs to false; - if(service.getClass().getSimpleName().equals(OperationManagerService.class.getSimpleName())){ - service.getSharedPreferences(AppConstants.SHARED_PREFERENCE_NAME, Context.MODE_PRIVATE) - .edit() - .putBoolean(AppConstants.KEY_OMS_IS_WORKING, false) - .apply(); - } if(isExternalStorageAvailable() && !isExternalStorageReadOnly()){ //Get TimeStamp diff --git a/app/src/test/java/foundation/e/drive/services/OperationManagerServiceTest.java b/app/src/test/java/foundation/e/drive/services/OperationManagerServiceTest.java deleted file mode 100644 index 0a2fb8f7..00000000 --- a/app/src/test/java/foundation/e/drive/services/OperationManagerServiceTest.java +++ /dev/null @@ -1,108 +0,0 @@ -package foundation.e.drive.services; - -import android.accounts.Account; -import android.accounts.AccountManager; -import android.app.job.JobScheduler; -import android.content.Context; -import android.content.Intent; -import android.net.ConnectivityManager; - -import com.owncloud.android.lib.resources.files.model.RemoteFile; - -import junit.framework.Assert; - -import org.junit.Test; -import org.robolectric.Robolectric; -import org.robolectric.RuntimeEnvironment; -import org.robolectric.shadows.ShadowLog; - -import java.util.List; - -import foundation.e.drive.TestUtils; -import foundation.e.drive.database.DbHelper; -import foundation.e.drive.models.SyncedFileState; -import foundation.e.drive.operations.DownloadFileOperation; -import foundation.e.drive.utils.AppConstants; - - -public class OperationManagerServiceTest extends AbstractServiceIT{ - - public OperationManagerServiceTest(){ - mServiceController = Robolectric.buildService(OperationManagerService.class); - mService = mServiceController.get(); - context = RuntimeEnvironment.application; - accountManager = AccountManager.get(context); - jobScheduler = (JobScheduler) context.getSystemService(Context.JOB_SCHEDULER_SERVICE); - contentResolver = context.getContentResolver(); - sharedPreferences = context.getSharedPreferences( AppConstants.SHARED_PREFERENCE_NAME, Context.MODE_PRIVATE); - connectivityManager = (ConnectivityManager) context.getSystemService(Context.CONNECTIVITY_SERVICE); - dbHelper = new DbHelper(context); - } - - - /** - * Check that the service stop quickly if no intent is provided - */ - @Test - public void noIntent_shouldStop(){ - mServiceController.create(); - mService.onStartCommand(null,0, 0); - - List logs = ShadowLog.getLogs(); - ShadowLog.LogItem lastLog = logs.get(logs.size()-2); - - Assert.assertEquals("Expected last log was: 'intent was null, flags=0 bits=0' but got:"+lastLog.msg, "intent was null, flags=0 bits=0", lastLog.msg); - } - - - /** - * Start the service with an intent that doesn't contains list of extra data - * (meaning no account and no synchronisation to perform) - */ - @Test - public void intentWithoutExtras_shouldStop(){ - mServiceController.create(); - mService.onStartCommand(new Intent(), 0, 0); - - List logs = ShadowLog.getLogs(); - ShadowLog.LogItem lastLog = logs.get(logs.size()-1); - - Assert.assertEquals("Expected last log was: 'Intent's extras is null.' but got:"+lastLog.msg, "Intent's extras is null.", lastLog.msg); - } - - - /** - * Start the OperationmanagerService with File to upload but no account provided - * in the intent. - * Failure is expected - */ - @Test - public void noAccount_shouldFail(){ - prepareValidAccount(); - final Account account = TestUtils.getValidAccount(); - final Intent intent = new Intent("dummyAction"); - - intent.putExtra("0", - new DownloadFileOperation( - new RemoteFile("/eDrive-test/coco.txt"), - new SyncedFileState(3, "coco.txt", "/tmp/eDrive-test/", "/eDrive-test/coco.txt","", -1, 1, true ) - ) - ); - - - intent.putExtra("account", account); - - final boolean accountRemoved = accountManager.removeAccountExplicitly(account); - Assert.assertTrue("Account removal should return true but returned false", accountRemoved); - - mServiceController.create(); - mService.onStartCommand(intent, 0,0); - - List logs = ShadowLog.getLogs(); - ShadowLog.LogItem lastLog = logs.get(logs.size()-1); - Assert.assertEquals("Expected last log was: 'No Client, Can't Work!' but got:"+lastLog.msg, "No Client, Can't Work!", lastLog.msg); - - } - - -} -- GitLab From f46be3caf0a969b2a151a7743c20058c0b616850 Mon Sep 17 00:00:00 2001 From: vincent Bourgmayer Date: Fri, 11 Mar 2022 13:27:49 +0100 Subject: [PATCH 54/68] Implement new class SyncRequest This class and subclasses will be passed from ObserverService and FileObserver to SynchronizationService instead of RemoteOperation (UploadFileOperation, RemoveFileOperation, DownloadFileOperation) The naming is more clear, and ObserverService doesn't have to create the RemoteOperation wich is out of its scope. The design isn't yet full complete so this Commit might be updated As it is now, the implementation isn't complete so the build won't succeed. Changes: - Add SyncRequest class into models package - Add UploadRequest as subclass of SyncRequest - Add DownloadRequest as Subclass of Syncrequest - Replace ComparableOperation in ObserverService and in SynchronizationService by SyncRequest - Add a method "CreateRemoteOperation(SyncRequest request" in SynchronizationService --- .../e/drive/models/DownloadRequest.java | 16 ++++++++ .../e/drive/models/SyncRequest.java | 31 +++++++++++++++ .../e/drive/models/UploadRequest.java | 14 +++++++ .../e/drive/services/ObserverService.java | 26 ++++++------- .../services/SynchronizationService.java | 39 ++++++++++++++----- 5 files changed, 103 insertions(+), 23 deletions(-) create mode 100644 app/src/main/java/foundation/e/drive/models/DownloadRequest.java create mode 100644 app/src/main/java/foundation/e/drive/models/SyncRequest.java create mode 100644 app/src/main/java/foundation/e/drive/models/UploadRequest.java diff --git a/app/src/main/java/foundation/e/drive/models/DownloadRequest.java b/app/src/main/java/foundation/e/drive/models/DownloadRequest.java new file mode 100644 index 00000000..546aa67f --- /dev/null +++ b/app/src/main/java/foundation/e/drive/models/DownloadRequest.java @@ -0,0 +1,16 @@ +package foundation.e.drive.models; + +import com.owncloud.android.lib.resources.files.model.RemoteFile; + +public class DownloadRequest extends SyncRequest{ + private RemoteFile remoteFile; + + public DownloadRequest (RemoteFile remoteFile, int syncedFileStateId){ + super(syncedFileStateId, Type.DOWNLOAD); + this.remoteFile = remoteFile; + } + + public RemoteFile getRemoteFile() { + return remoteFile; + } +} diff --git a/app/src/main/java/foundation/e/drive/models/SyncRequest.java b/app/src/main/java/foundation/e/drive/models/SyncRequest.java new file mode 100644 index 00000000..80a8721d --- /dev/null +++ b/app/src/main/java/foundation/e/drive/models/SyncRequest.java @@ -0,0 +1,31 @@ +package foundation.e.drive.models; + +import androidx.annotation.Nullable; + +public class SyncRequest { + public enum Type { UPLOAD, DOWNLOAD, REMOTE_DELETE}; + + final private int syncedFileStateId; + final private Type operationType; + + public SyncRequest(int syncedFileStateId, Type operationType){ + this.syncedFileStateId = syncedFileStateId; + this.operationType = operationType; + } + + public Type getOperationType(){ + return operationType; + } + + public int getSyncedFileStateId(){ + return syncedFileStateId; + } + + @Override + public boolean equals(@Nullable Object obj) { + if(obj instanceof SyncRequest){ + return (syncedFileStateId == ((SyncRequest) obj).syncedFileStateId ); + } + return super.equals(obj); + } +} diff --git a/app/src/main/java/foundation/e/drive/models/UploadRequest.java b/app/src/main/java/foundation/e/drive/models/UploadRequest.java new file mode 100644 index 00000000..dc0c6185 --- /dev/null +++ b/app/src/main/java/foundation/e/drive/models/UploadRequest.java @@ -0,0 +1,14 @@ +package foundation.e.drive.models; + +public class UploadRequest extends SyncRequest{ + private boolean checkEtag; + + public UploadRequest(boolean checkEtag, int syncedFileStateId){ + super(syncedFileStateId, Type.UPLOAD); + this.checkEtag = checkEtag; + } + + public boolean mustCheckEtag() { + return checkEtag; + } +} diff --git a/app/src/main/java/foundation/e/drive/services/ObserverService.java b/app/src/main/java/foundation/e/drive/services/ObserverService.java index f895ca28..5aa5bab2 100644 --- a/app/src/main/java/foundation/e/drive/services/ObserverService.java +++ b/app/src/main/java/foundation/e/drive/services/ObserverService.java @@ -19,7 +19,6 @@ import android.content.SharedPreferences; import android.content.pm.PackageInfo; import android.os.Handler; import android.os.IBinder; -import android.os.Parcelable; import android.provider.MediaStore; import android.util.Log; import com.owncloud.android.lib.common.OwnCloudClient; @@ -35,15 +34,14 @@ import java.util.ArrayList; import java.util.HashMap; import java.util.List; import java.util.ListIterator; -import java.util.Map; import foundation.e.drive.database.DbHelper; import foundation.e.drive.fileFilters.CrashlogsFileFilter; import foundation.e.drive.fileFilters.FileFilterFactory; import foundation.e.drive.fileFilters.OnlyFileFilter; +import foundation.e.drive.models.SyncRequest; import foundation.e.drive.models.SyncedFolder; import foundation.e.drive.models.SyncedFileState; -import foundation.e.drive.operations.ComparableOperation; import foundation.e.drive.operations.DownloadFileOperation; import foundation.e.drive.operations.ListFileRemoteOperation; import foundation.e.drive.operations.RemoveFileOperation; @@ -72,7 +70,7 @@ public class ObserverService extends Service implements OnRemoteOperationListene private boolean isWorking = false; private int initialFolderCounter; private Account mAccount; - private HashMap operationsForIntent; //integer is SyncedFileState id; Parcelable is the operation + private HashMap syncRequests; //integer is SyncedFileState id; Parcelable is the operation private SynchronizationService synchronizationService; private boolean boundToSynchronizationService = false; @@ -163,7 +161,7 @@ public class ObserverService extends Service implements OnRemoteOperationListene Log.w(TAG, "There is no Internet connexion."); return super.onStartCommand( intent, flags, startId ); } - this.operationsForIntent = new HashMap<>(); + this.syncRequests = new HashMap<>(); Intent SynchronizationServiceIntent = new Intent(this.getApplicationContext(), SynchronizationService.class); bindService(SynchronizationServiceIntent, SynchronizationServiceConnection, Context.BIND_AUTO_CREATE); @@ -325,11 +323,11 @@ public class ObserverService extends Service implements OnRemoteOperationListene } this.startScan(false); - Log.v(TAG, "operationsForIntent contains "+ operationsForIntent.size() ); + Log.v(TAG, "operationsForIntent contains "+ syncRequests.size() ); //After everything has been scanned. Send Intent to OperationmanagerService with data in bundle - if (operationsForIntent != null && !operationsForIntent.isEmpty()) { - this.synchronizationService.queueOperations(operationsForIntent.values()); + if (syncRequests != null && !syncRequests.isEmpty()) { + this.synchronizationService.queueOperations(syncRequests.values()); } else { Log.w(TAG, "There is no file to sync."); getSharedPreferences(AppConstants.SHARED_PREFERENCE_NAME, Context.MODE_PRIVATE) @@ -414,8 +412,10 @@ public class ObserverService extends Service implements OnRemoteOperationListene Log.v(TAG, affectedRows + " syncedFileState.s row in DB has been updated."); } else { Log.i(TAG, "Add download operation for file "+syncedFileState.getId()); + + DownloadFileOperation downloadFileOperation = new DownloadFileOperation(remoteFile, syncedFileState); - this.operationsForIntent.put(syncedFileState.getId(), downloadFileOperation); + this.syncRequests.put(syncedFileState.getId(), downloadFileOperation); } } syncedFileListIterator.remove(); //we can delete syncedFile from list because its correspondant has already been found and handled @@ -448,7 +448,7 @@ public class ObserverService extends Service implements OnRemoteOperationListene newRemoteFile.setId(storedId); Log.i(TAG, "Add download operation for new file "+storedId); //Create Download operation and add it into Bundle - this.operationsForIntent.put(storedId, new DownloadFileOperation(remoteFile, newRemoteFile)); + this.syncRequests.put(storedId, new DownloadFileOperation(remoteFile, newRemoteFile)); } else { Log.w(TAG, "Can't save new remote File in DB. Ignore file."); @@ -710,7 +710,7 @@ public class ObserverService extends Service implements OnRemoteOperationListene } Log.i(TAG, "Add upload operation for file "+syncedFileState.getId()); UploadFileOperation uploadFileOperation = new UploadFileOperation(syncedFileState, checkEtag ); - this.operationsForIntent.put(syncedFileState.getId(), uploadFileOperation); + this.syncRequests.put(syncedFileState.getId(), uploadFileOperation); } // No need to reloop on it. syncedFileListIterator.remove(); @@ -738,7 +738,7 @@ public class ObserverService extends Service implements OnRemoteOperationListene Log.i(TAG, "Add upload operation for new file "+storedId); //create UploadOperation and add it into bundle UploadFileOperation uploadOperation = new UploadFileOperation(newSyncedFileState, syncedFolder.isScanRemote()); - this.operationsForIntent.put(storedId, uploadOperation); + this.syncRequests.put(storedId, uploadOperation); } else { Log.w(TAG, "The new file to synced cannot be store in DB. Ignore it"); } @@ -767,7 +767,7 @@ public class ObserverService extends Service implements OnRemoteOperationListene } else { Log.i(TAG, "Add remove operation for file "+fileState.getId()); RemoveFileOperation removeOperation = new RemoveFileOperation(fileState); - this.operationsForIntent.put(fileState.getId(), removeOperation); + this.syncRequests.put(fileState.getId(), removeOperation); } } } 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 9da67bd6..92ebe613 100644 --- a/app/src/main/java/foundation/e/drive/services/SynchronizationService.java +++ b/app/src/main/java/foundation/e/drive/services/SynchronizationService.java @@ -26,11 +26,10 @@ import com.owncloud.android.lib.common.operations.RemoteOperationResult; import java.lang.ref.WeakReference; import java.util.Collection; import java.util.Hashtable; -import java.util.List; import java.util.concurrent.ConcurrentLinkedDeque; import foundation.e.drive.database.DbHelper; -import foundation.e.drive.operations.ComparableOperation; +import foundation.e.drive.models.SyncRequest; import foundation.e.drive.operations.DownloadFileOperation; import foundation.e.drive.operations.RemoveFileOperation; import foundation.e.drive.operations.UploadFileOperation; @@ -45,7 +44,7 @@ public class SynchronizationService extends Service implements OnRemoteOperation private final static String TAG = SynchronizationService.class.getSimpleName(); private final SynchronizationBinder binder = new SynchronizationBinder(); - private ConcurrentLinkedDeque operationsQueue; + private ConcurrentLinkedDeque syncedRequestQueue; private Hashtable startedOperations; //Operations which are running private Account account; private final int workerAmount = 4; @@ -68,7 +67,7 @@ public class SynchronizationService extends Service implements OnRemoteOperation account = (Account) intent.getParcelableExtra("account"); - operationsQueue = new ConcurrentLinkedDeque<>(); + syncedRequestQueue = new ConcurrentLinkedDeque<>(); threadPool = new Thread[workerAmount]; threadWorkingState = new boolean[workerAmount]; client = DavClientProvider.getInstance().getClientInstance(account, getApplicationContext()); @@ -91,12 +90,12 @@ public class SynchronizationService extends Service implements OnRemoteOperation .apply(); } - public boolean queueOperation(ComparableOperation operation){ - return operationsQueue.add(operation); + public boolean queueOperation(SyncRequest request){ + return syncedRequestQueue.add(request); } - public boolean queueOperations(Collection operations){ - return operationsQueue.addAll(operations); + public boolean queueOperations(Collection requests){ + return syncedRequestQueue.addAll(requests); } public void startSynchronization(){ @@ -107,10 +106,14 @@ public class SynchronizationService extends Service implements OnRemoteOperation } private void startWorker(int threadIndex){ - if (operationsQueue.isEmpty()) return; + if (syncedRequestQueue.isEmpty()) return; if (!threadWorkingState[threadIndex] && CommonUtils.haveNetworkConnexion(getApplicationContext())) { //check if the thread corresponding to threadIndex isn't already working - ComparableOperation operation = this.operationsQueue.poll(); //return null if deque is empty + SyncRequest request = this.syncedRequestQueue.poll(); //return null if deque is empty + + RemoteOperation operation = this.createRemoteOperation(request); + + if (operation != null) { Log.v(TAG, " an operation has been poll from queue"); @@ -204,6 +207,22 @@ public class SynchronizationService extends Service implements OnRemoteOperation } //Close else } + private RemoteOperation createRemoteOperation(SyncRequest request){ + RemoteOperation operation; + switch (request.getOperationType()){ + case UPLOAD: + operation = new UploadFileOperation(); + break; + case DOWNLOAD: + operation = new DownloadFileOperation(); + break; + case REMOTE_DELETE: + operation = new RemoveFileOperation(); + break; + } + } + + /** * Handler for the class */ -- GitLab From 20469cc6791704039844e54bb57f21aa2cad3694 Mon Sep 17 00:00:00 2001 From: vincent Bourgmayer Date: Mon, 14 Mar 2022 17:53:25 +0100 Subject: [PATCH 55/68] Replace SyncRequest syncedFileStateId by SyncedFileState and remove UploadRequest - Remove UploadRequest.java because I found How to get 'checkEtag' from SyncedFileState - Replace SyncedFileStateId's field by SyncedFileState instance in SyncRequest.java - Update DownloadRequest's constructor to fit with SyncRequest's changes. --- .../foundation/e/drive/models/DownloadRequest.java | 4 ++-- .../foundation/e/drive/models/SyncRequest.java | 12 ++++++------ .../foundation/e/drive/models/UploadRequest.java | 14 -------------- 3 files changed, 8 insertions(+), 22 deletions(-) delete mode 100644 app/src/main/java/foundation/e/drive/models/UploadRequest.java diff --git a/app/src/main/java/foundation/e/drive/models/DownloadRequest.java b/app/src/main/java/foundation/e/drive/models/DownloadRequest.java index 546aa67f..554010bb 100644 --- a/app/src/main/java/foundation/e/drive/models/DownloadRequest.java +++ b/app/src/main/java/foundation/e/drive/models/DownloadRequest.java @@ -5,8 +5,8 @@ import com.owncloud.android.lib.resources.files.model.RemoteFile; public class DownloadRequest extends SyncRequest{ private RemoteFile remoteFile; - public DownloadRequest (RemoteFile remoteFile, int syncedFileStateId){ - super(syncedFileStateId, Type.DOWNLOAD); + public DownloadRequest (RemoteFile remoteFile, SyncedFileState syncedFileState){ + super(syncedFileState, Type.DOWNLOAD); this.remoteFile = remoteFile; } diff --git a/app/src/main/java/foundation/e/drive/models/SyncRequest.java b/app/src/main/java/foundation/e/drive/models/SyncRequest.java index 80a8721d..03514629 100644 --- a/app/src/main/java/foundation/e/drive/models/SyncRequest.java +++ b/app/src/main/java/foundation/e/drive/models/SyncRequest.java @@ -5,11 +5,11 @@ import androidx.annotation.Nullable; public class SyncRequest { public enum Type { UPLOAD, DOWNLOAD, REMOTE_DELETE}; - final private int syncedFileStateId; + final private SyncedFileState syncedFileState; final private Type operationType; - public SyncRequest(int syncedFileStateId, Type operationType){ - this.syncedFileStateId = syncedFileStateId; + public SyncRequest(SyncedFileState syncedFileState, Type operationType){ + this.syncedFileState = syncedFileState; this.operationType = operationType; } @@ -17,14 +17,14 @@ public class SyncRequest { return operationType; } - public int getSyncedFileStateId(){ - return syncedFileStateId; + public SyncedFileState getSyncedFileState(){ + return syncedFileState; } @Override public boolean equals(@Nullable Object obj) { if(obj instanceof SyncRequest){ - return (syncedFileStateId == ((SyncRequest) obj).syncedFileStateId ); + return (syncedFileState.getId() == ((SyncRequest) obj).syncedFileState.getId() ); } return super.equals(obj); } diff --git a/app/src/main/java/foundation/e/drive/models/UploadRequest.java b/app/src/main/java/foundation/e/drive/models/UploadRequest.java deleted file mode 100644 index dc0c6185..00000000 --- a/app/src/main/java/foundation/e/drive/models/UploadRequest.java +++ /dev/null @@ -1,14 +0,0 @@ -package foundation.e.drive.models; - -public class UploadRequest extends SyncRequest{ - private boolean checkEtag; - - public UploadRequest(boolean checkEtag, int syncedFileStateId){ - super(syncedFileStateId, Type.UPLOAD); - this.checkEtag = checkEtag; - } - - public boolean mustCheckEtag() { - return checkEtag; - } -} -- GitLab From 1afb977c66e5697ef40c5e225fee238ca21d3f09 Mon Sep 17 00:00:00 2001 From: vincent Bourgmayer Date: Mon, 14 Mar 2022 17:58:55 +0100 Subject: [PATCH 56/68] Replace instanciation of UploadFileOperation by SyncRequest instanciation - Update ObserverService: replace new UploadFileOperation(...) by new SyncedRequest(...) - Update ObserverService: Remove loop over SyncedFolder to check isScanRemote as SyncedFileState.isMediaType() give the same - Update SynchronizerService to instanciate UploadFileOperation from SyncRequest of Upload Type --- .../e/drive/services/ObserverService.java | 23 ++++--------------- .../services/SynchronizationService.java | 8 ++++++- 2 files changed, 11 insertions(+), 20 deletions(-) diff --git a/app/src/main/java/foundation/e/drive/services/ObserverService.java b/app/src/main/java/foundation/e/drive/services/ObserverService.java index 5aa5bab2..318e412e 100644 --- a/app/src/main/java/foundation/e/drive/services/ObserverService.java +++ b/app/src/main/java/foundation/e/drive/services/ObserverService.java @@ -695,22 +695,8 @@ public class ObserverService extends Service implements OnRemoteOperationListene //If no etag is stored in sfs, the file hasn't been sync up to server. then do upload if ( syncedFileState.getLocalLastModified() < localFile.lastModified() || !syncedFileState.isLastEtagStored()){ - Log.d(TAG+"_handleLocalFiles()", syncedFileState.getName()+" file has been modified or never sync" ); - boolean checkEtag = false; - - //Look for folder to know if the folder can be scan remotly. - for(int folderIndex =-1, size = mSyncedFolders.size();++folderIndex < size;) { - - final SyncedFolder syncedFolder = mSyncedFolders.get(folderIndex); - if (syncedFolder.getId() == syncedFileState.getSyncedFolderId()) { - //Parent folder has been found - checkEtag = syncedFolder.isScanRemote(); - break; - } - } - Log.i(TAG, "Add upload operation for file "+syncedFileState.getId()); - UploadFileOperation uploadFileOperation = new UploadFileOperation(syncedFileState, checkEtag ); - this.syncRequests.put(syncedFileState.getId(), uploadFileOperation); + Log.i(TAG, "Add upload request for file "+syncedFileState.getId()); + this.syncRequests.put(syncedFileState.getId(), new SyncRequest(syncedFileState, SyncRequest.Type.UPLOAD)); } // No need to reloop on it. syncedFileListIterator.remove(); @@ -736,9 +722,8 @@ public class ObserverService extends Service implements OnRemoteOperationListene if (storedId > 0){ newSyncedFileState.setId( storedId ); Log.i(TAG, "Add upload operation for new file "+storedId); - //create UploadOperation and add it into bundle - UploadFileOperation uploadOperation = new UploadFileOperation(newSyncedFileState, syncedFolder.isScanRemote()); - this.syncRequests.put(storedId, uploadOperation); + + this.syncRequests.put(storedId, new SyncRequest(newSyncedFileState, SyncRequest.Type.UPLOAD)); } else { Log.w(TAG, "The new file to synced cannot be store in DB. Ignore it"); } 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 92ebe613..0e561041 100644 --- a/app/src/main/java/foundation/e/drive/services/SynchronizationService.java +++ b/app/src/main/java/foundation/e/drive/services/SynchronizationService.java @@ -30,6 +30,7 @@ import java.util.concurrent.ConcurrentLinkedDeque; import foundation.e.drive.database.DbHelper; import foundation.e.drive.models.SyncRequest; +import foundation.e.drive.models.SyncedFileState; import foundation.e.drive.operations.DownloadFileOperation; import foundation.e.drive.operations.RemoveFileOperation; import foundation.e.drive.operations.UploadFileOperation; @@ -211,7 +212,8 @@ public class SynchronizationService extends Service implements OnRemoteOperation RemoteOperation operation; switch (request.getOperationType()){ case UPLOAD: - operation = new UploadFileOperation(); + final SyncedFileState sfs = request.getSyncedFileState(); + operation = new UploadFileOperation(sfs, sfs.isMediaType()); break; case DOWNLOAD: operation = new DownloadFileOperation(); @@ -219,7 +221,11 @@ public class SynchronizationService extends Service implements OnRemoteOperation case REMOTE_DELETE: operation = new RemoveFileOperation(); break; + default: + operation = null; + break; } + return operation; } -- GitLab From 1d4e044f602f4c5580593ec8dd3973b95143f80a Mon Sep 17 00:00:00 2001 From: vincent Bourgmayer Date: Mon, 14 Mar 2022 18:12:04 +0100 Subject: [PATCH 57/68] Fix coding style in SyncRequest and set RemoteFile as final in DownloadRequest --- .../main/java/foundation/e/drive/models/DownloadRequest.java | 2 +- app/src/main/java/foundation/e/drive/models/SyncRequest.java | 5 +++-- 2 files changed, 4 insertions(+), 3 deletions(-) diff --git a/app/src/main/java/foundation/e/drive/models/DownloadRequest.java b/app/src/main/java/foundation/e/drive/models/DownloadRequest.java index 554010bb..34dc97fa 100644 --- a/app/src/main/java/foundation/e/drive/models/DownloadRequest.java +++ b/app/src/main/java/foundation/e/drive/models/DownloadRequest.java @@ -3,7 +3,7 @@ package foundation.e.drive.models; import com.owncloud.android.lib.resources.files.model.RemoteFile; public class DownloadRequest extends SyncRequest{ - private RemoteFile remoteFile; + private final RemoteFile remoteFile; public DownloadRequest (RemoteFile remoteFile, SyncedFileState syncedFileState){ super(syncedFileState, Type.DOWNLOAD); diff --git a/app/src/main/java/foundation/e/drive/models/SyncRequest.java b/app/src/main/java/foundation/e/drive/models/SyncRequest.java index 03514629..251c2190 100644 --- a/app/src/main/java/foundation/e/drive/models/SyncRequest.java +++ b/app/src/main/java/foundation/e/drive/models/SyncRequest.java @@ -5,8 +5,9 @@ import androidx.annotation.Nullable; public class SyncRequest { public enum Type { UPLOAD, DOWNLOAD, REMOTE_DELETE}; - final private SyncedFileState syncedFileState; - final private Type operationType; + private final SyncedFileState syncedFileState; + + private final Type operationType; public SyncRequest(SyncedFileState syncedFileState, Type operationType){ this.syncedFileState = syncedFileState; -- GitLab From 0eff79bb8b5394343eb03c073d430f4788bebe37 Mon Sep 17 00:00:00 2001 From: vincent Bourgmayer Date: Mon, 14 Mar 2022 18:23:14 +0100 Subject: [PATCH 58/68] Use DownloadRequest and syncRequest - ObserverService: Replace DownloadFileOperation instanciation by DownloadRequest - ObserverService: Replace RemoveFileOperation instanciation by SyncRequest - OperationManagerService: Create DownloadFileOperation from DownloadRequest - OperationManagerService: skip Remote deletion request for now accordingly to discussion with PO --- .../e/drive/services/ObserverService.java | 14 +++++--------- .../drive/services/SynchronizationService.java | 18 +++++++++--------- 2 files changed, 14 insertions(+), 18 deletions(-) diff --git a/app/src/main/java/foundation/e/drive/services/ObserverService.java b/app/src/main/java/foundation/e/drive/services/ObserverService.java index 318e412e..c88b4a3e 100644 --- a/app/src/main/java/foundation/e/drive/services/ObserverService.java +++ b/app/src/main/java/foundation/e/drive/services/ObserverService.java @@ -39,13 +39,12 @@ import foundation.e.drive.database.DbHelper; import foundation.e.drive.fileFilters.CrashlogsFileFilter; import foundation.e.drive.fileFilters.FileFilterFactory; import foundation.e.drive.fileFilters.OnlyFileFilter; +import foundation.e.drive.models.DownloadRequest; import foundation.e.drive.models.SyncRequest; import foundation.e.drive.models.SyncedFolder; import foundation.e.drive.models.SyncedFileState; -import foundation.e.drive.operations.DownloadFileOperation; import foundation.e.drive.operations.ListFileRemoteOperation; import foundation.e.drive.operations.RemoveFileOperation; -import foundation.e.drive.operations.UploadFileOperation; import foundation.e.drive.receivers.ForceSyncReceiver; import foundation.e.drive.utils.AppConstants; import foundation.e.drive.utils.CommonUtils; @@ -413,9 +412,7 @@ public class ObserverService extends Service implements OnRemoteOperationListene } else { Log.i(TAG, "Add download operation for file "+syncedFileState.getId()); - - DownloadFileOperation downloadFileOperation = new DownloadFileOperation(remoteFile, syncedFileState); - this.syncRequests.put(syncedFileState.getId(), downloadFileOperation); + this.syncRequests.put(syncedFileState.getId(), new DownloadRequest(remoteFile, syncedFileState)); } } syncedFileListIterator.remove(); //we can delete syncedFile from list because its correspondant has already been found and handled @@ -448,7 +445,7 @@ public class ObserverService extends Service implements OnRemoteOperationListene newRemoteFile.setId(storedId); Log.i(TAG, "Add download operation for new file "+storedId); //Create Download operation and add it into Bundle - this.syncRequests.put(storedId, new DownloadFileOperation(remoteFile, newRemoteFile)); + this.syncRequests.put(storedId, new DownloadRequest(remoteFile, newRemoteFile)); } else { Log.w(TAG, "Can't save new remote File in DB. Ignore file."); @@ -750,9 +747,8 @@ public class ObserverService extends Service implements OnRemoteOperationListene if (file.exists()){ Log.w(TAG, "The file still exist. There is a problem!"); } else { - Log.i(TAG, "Add remove operation for file "+fileState.getId()); - RemoveFileOperation removeOperation = new RemoveFileOperation(fileState); - this.syncRequests.put(fileState.getId(), removeOperation); + Log.i(TAG, "Add remote remove request for file "+fileState.getId()); + this.syncRequests.put(fileState.getId(), new SyncRequest(fileState, SyncRequest.Type.REMOTE_DELETE)); } } } 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 0e561041..b3453e2b 100644 --- a/app/src/main/java/foundation/e/drive/services/SynchronizationService.java +++ b/app/src/main/java/foundation/e/drive/services/SynchronizationService.java @@ -29,6 +29,7 @@ import java.util.Hashtable; import java.util.concurrent.ConcurrentLinkedDeque; import foundation.e.drive.database.DbHelper; +import foundation.e.drive.models.DownloadRequest; import foundation.e.drive.models.SyncRequest; import foundation.e.drive.models.SyncedFileState; import foundation.e.drive.operations.DownloadFileOperation; @@ -110,17 +111,16 @@ public class SynchronizationService extends Service implements OnRemoteOperation if (syncedRequestQueue.isEmpty()) return; if (!threadWorkingState[threadIndex] && CommonUtils.haveNetworkConnexion(getApplicationContext())) { //check if the thread corresponding to threadIndex isn't already working - SyncRequest request = this.syncedRequestQueue.poll(); //return null if deque is empty - - RemoteOperation operation = this.createRemoteOperation(request); + final SyncRequest request = this.syncedRequestQueue.poll(); //return null if deque is empty + final RemoteOperation operation = this.createRemoteOperation(request); if (operation != null) { Log.v(TAG, " an operation has been poll from queue"); - if (CommonUtils.isThisSyncAllowed(account, operation.isMediaType())) { - startedOperations.put(operation.toRemoteOperation(), threadIndex); - threadPool[threadIndex] = operation.toRemoteOperation().execute(client, this, handler); + if (CommonUtils.isThisSyncAllowed(account, request.getSyncedFileState().isMediaType())) { + startedOperations.put(operation, threadIndex); + threadPool[threadIndex] = operation.execute(client, this, handler); threadWorkingState[threadIndex] = true; } } @@ -216,11 +216,11 @@ public class SynchronizationService extends Service implements OnRemoteOperation operation = new UploadFileOperation(sfs, sfs.isMediaType()); break; case DOWNLOAD: - operation = new DownloadFileOperation(); + final DownloadRequest downloadRequest = (DownloadRequest) request; + operation = new DownloadFileOperation(downloadRequest.getRemoteFile(), downloadRequest.getSyncedFileState()); break; case REMOTE_DELETE: - operation = new RemoveFileOperation(); - break; + //operation = new RemoveFileOperation(); default: operation = null; break; -- GitLab From a52a45ccfc215edccc0dfbd020fe6c759b337099 Mon Sep 17 00:00:00 2001 From: Jonathan Klee Date: Wed, 16 Mar 2022 12:46:36 +0000 Subject: [PATCH 59/68] Apply 1 suggestion(s) to 1 file(s) --- .../main/java/foundation/e/drive/models/DownloadRequest.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/app/src/main/java/foundation/e/drive/models/DownloadRequest.java b/app/src/main/java/foundation/e/drive/models/DownloadRequest.java index 34dc97fa..91de9fb3 100644 --- a/app/src/main/java/foundation/e/drive/models/DownloadRequest.java +++ b/app/src/main/java/foundation/e/drive/models/DownloadRequest.java @@ -2,7 +2,7 @@ package foundation.e.drive.models; import com.owncloud.android.lib.resources.files.model.RemoteFile; -public class DownloadRequest extends SyncRequest{ +public class DownloadRequest extends SyncRequest { private final RemoteFile remoteFile; public DownloadRequest (RemoteFile remoteFile, SyncedFileState syncedFileState){ -- GitLab From 33408879cca9784c96247c454995998cdf4ad760 Mon Sep 17 00:00:00 2001 From: Jonathan Klee Date: Wed, 16 Mar 2022 12:49:43 +0000 Subject: [PATCH 60/68] Apply 1 suggestion(s) to 1 file(s) --- .../main/java/foundation/e/drive/models/DownloadRequest.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/app/src/main/java/foundation/e/drive/models/DownloadRequest.java b/app/src/main/java/foundation/e/drive/models/DownloadRequest.java index 91de9fb3..c59668a3 100644 --- a/app/src/main/java/foundation/e/drive/models/DownloadRequest.java +++ b/app/src/main/java/foundation/e/drive/models/DownloadRequest.java @@ -5,7 +5,7 @@ import com.owncloud.android.lib.resources.files.model.RemoteFile; public class DownloadRequest extends SyncRequest { private final RemoteFile remoteFile; - public DownloadRequest (RemoteFile remoteFile, SyncedFileState syncedFileState){ + public DownloadRequest (RemoteFile remoteFile, SyncedFileState syncedFileState) { super(syncedFileState, Type.DOWNLOAD); this.remoteFile = remoteFile; } -- GitLab From c0929c61ba4bec2d3fe8416818e4c273db782dba Mon Sep 17 00:00:00 2001 From: Jonathan Klee Date: Wed, 16 Mar 2022 12:49:50 +0000 Subject: [PATCH 61/68] Apply 1 suggestion(s) to 1 file(s) --- app/src/main/java/foundation/e/drive/models/SyncRequest.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/app/src/main/java/foundation/e/drive/models/SyncRequest.java b/app/src/main/java/foundation/e/drive/models/SyncRequest.java index 251c2190..e8ab3ca6 100644 --- a/app/src/main/java/foundation/e/drive/models/SyncRequest.java +++ b/app/src/main/java/foundation/e/drive/models/SyncRequest.java @@ -9,7 +9,7 @@ public class SyncRequest { private final Type operationType; - public SyncRequest(SyncedFileState syncedFileState, Type operationType){ + public SyncRequest(SyncedFileState syncedFileState, Type operationType) { this.syncedFileState = syncedFileState; this.operationType = operationType; } -- GitLab From 1dcd8ef7f90b77ae3be0d58412d3d806dca71516 Mon Sep 17 00:00:00 2001 From: Jonathan Klee Date: Wed, 16 Mar 2022 12:50:00 +0000 Subject: [PATCH 62/68] Apply 1 suggestion(s) to 1 file(s) --- app/src/main/java/foundation/e/drive/models/SyncRequest.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/app/src/main/java/foundation/e/drive/models/SyncRequest.java b/app/src/main/java/foundation/e/drive/models/SyncRequest.java index e8ab3ca6..6453856f 100644 --- a/app/src/main/java/foundation/e/drive/models/SyncRequest.java +++ b/app/src/main/java/foundation/e/drive/models/SyncRequest.java @@ -14,7 +14,7 @@ public class SyncRequest { this.operationType = operationType; } - public Type getOperationType(){ + public Type getOperationType() { return operationType; } -- GitLab From c17a2ab03eaf5670a096f735a681c3d081081815 Mon Sep 17 00:00:00 2001 From: Jonathan Klee Date: Wed, 16 Mar 2022 12:50:11 +0000 Subject: [PATCH 63/68] Apply 1 suggestion(s) to 1 file(s) --- app/src/main/java/foundation/e/drive/models/SyncRequest.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/app/src/main/java/foundation/e/drive/models/SyncRequest.java b/app/src/main/java/foundation/e/drive/models/SyncRequest.java index 6453856f..5d01ce73 100644 --- a/app/src/main/java/foundation/e/drive/models/SyncRequest.java +++ b/app/src/main/java/foundation/e/drive/models/SyncRequest.java @@ -18,7 +18,7 @@ public class SyncRequest { return operationType; } - public SyncedFileState getSyncedFileState(){ + public SyncedFileState getSyncedFileState() { return syncedFileState; } -- GitLab From 254b43476de25a4041aeb0aea5bc679db2e3de1a Mon Sep 17 00:00:00 2001 From: Jonathan Klee Date: Wed, 16 Mar 2022 12:50:26 +0000 Subject: [PATCH 64/68] Apply 1 suggestion(s) to 1 file(s) --- app/src/main/java/foundation/e/drive/models/SyncRequest.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/app/src/main/java/foundation/e/drive/models/SyncRequest.java b/app/src/main/java/foundation/e/drive/models/SyncRequest.java index 5d01ce73..c455dace 100644 --- a/app/src/main/java/foundation/e/drive/models/SyncRequest.java +++ b/app/src/main/java/foundation/e/drive/models/SyncRequest.java @@ -24,7 +24,7 @@ public class SyncRequest { @Override public boolean equals(@Nullable Object obj) { - if(obj instanceof SyncRequest){ + if (obj instanceof SyncRequest) { return (syncedFileState.getId() == ((SyncRequest) obj).syncedFileState.getId() ); } return super.equals(obj); -- GitLab From 6b2158f5490647d8080fb49af99e3b0f567cc1f8 Mon Sep 17 00:00:00 2001 From: vincent Bourgmayer Date: Wed, 16 Mar 2022 14:14:42 +0100 Subject: [PATCH 65/68] add missing licence header and and remove useless comment and commented code --- .../foundation/e/drive/models/DownloadRequest.java | 10 ++++++++++ .../java/foundation/e/drive/models/SyncRequest.java | 7 +++++++ .../e/drive/services/SynchronizationService.java | 12 +++++++++--- 3 files changed, 26 insertions(+), 3 deletions(-) diff --git a/app/src/main/java/foundation/e/drive/models/DownloadRequest.java b/app/src/main/java/foundation/e/drive/models/DownloadRequest.java index c59668a3..7403eea5 100644 --- a/app/src/main/java/foundation/e/drive/models/DownloadRequest.java +++ b/app/src/main/java/foundation/e/drive/models/DownloadRequest.java @@ -1,7 +1,17 @@ +/* + * Copyright © Vincent Bourgmayer (/e/ foundation). + * 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.models; import com.owncloud.android.lib.resources.files.model.RemoteFile; +/** + * @author vincent Bourgmayer + */ public class DownloadRequest extends SyncRequest { private final RemoteFile remoteFile; diff --git a/app/src/main/java/foundation/e/drive/models/SyncRequest.java b/app/src/main/java/foundation/e/drive/models/SyncRequest.java index c455dace..e2412cdf 100644 --- a/app/src/main/java/foundation/e/drive/models/SyncRequest.java +++ b/app/src/main/java/foundation/e/drive/models/SyncRequest.java @@ -1,3 +1,10 @@ +/* + * Copyright © Vincent Bourgmayer (/e/ foundation). + * 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.models; import androidx.annotation.Nullable; 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 b3453e2b..7cfcfc29 100644 --- a/app/src/main/java/foundation/e/drive/services/SynchronizationService.java +++ b/app/src/main/java/foundation/e/drive/services/SynchronizationService.java @@ -1,3 +1,10 @@ +/* + * Copyright © Vincent Bourgmayer (/e/ foundation). + * 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.services; import android.accounts.Account; @@ -204,8 +211,8 @@ public class SynchronizationService extends Service implements OnRemoteOperation Log.e(TAG, operationClassName+" : Sync_conflict: File is already up to date"); break; - } //Close switch - } //Close else + } + } } private RemoteOperation createRemoteOperation(SyncRequest request){ @@ -220,7 +227,6 @@ public class SynchronizationService extends Service implements OnRemoteOperation operation = new DownloadFileOperation(downloadRequest.getRemoteFile(), downloadRequest.getSyncedFileState()); break; case REMOTE_DELETE: - //operation = new RemoveFileOperation(); default: operation = null; break; -- GitLab From d6588c31ab1789e84bb90278504c0040b8bfccb0 Mon Sep 17 00:00:00 2001 From: vincent Bourgmayer Date: Thu, 17 Mar 2022 18:02:14 +0100 Subject: [PATCH 66/68] fix inversed if statement in SynchronizationService --- .../foundation/e/drive/services/SynchronizationService.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) 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 9da67bd6..44c4b749 100644 --- a/app/src/main/java/foundation/e/drive/services/SynchronizationService.java +++ b/app/src/main/java/foundation/e/drive/services/SynchronizationService.java @@ -60,7 +60,7 @@ public class SynchronizationService extends Service implements OnRemoteOperation final SharedPreferences prefs = getSharedPreferences(AppConstants.SHARED_PREFERENCE_NAME, Context.MODE_PRIVATE); - if (prefs.getString(AccountManager.KEY_ACCOUNT_NAME, null) != null) { + if (prefs.getString(AccountManager.KEY_ACCOUNT_NAME, null) == null) { Log.w(TAG, "No account available. Stop SynchronizationService"); stopSelf(); return START_NOT_STICKY; -- GitLab From fcc84c7347d0bae4d248e3b4e797c5aaa6ebd04b Mon Sep 17 00:00:00 2001 From: vincent Bourgmayer Date: Thu, 17 Mar 2022 18:56:05 +0100 Subject: [PATCH 67/68] fix uninstanciate HashTable in SynchronizationService.java --- .../java/foundation/e/drive/services/SynchronizationService.java | 1 + 1 file changed, 1 insertion(+) 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 7cfcfc29..4c1df6b8 100644 --- a/app/src/main/java/foundation/e/drive/services/SynchronizationService.java +++ b/app/src/main/java/foundation/e/drive/services/SynchronizationService.java @@ -77,6 +77,7 @@ public class SynchronizationService extends Service implements OnRemoteOperation account = (Account) intent.getParcelableExtra("account"); syncedRequestQueue = new ConcurrentLinkedDeque<>(); + startedOperations = new Hashtable<>(); threadPool = new Thread[workerAmount]; threadWorkingState = new boolean[workerAmount]; client = DavClientProvider.getInstance().getClientInstance(account, getApplicationContext()); -- GitLab From 68f9cc8173f68face15d4bd20b40833df39262f1 Mon Sep 17 00:00:00 2001 From: Vincent Bourgmayer Date: Fri, 18 Mar 2022 08:43:01 +0000 Subject: [PATCH 68/68] Remove useless ComparableOperation --- .../drive/operations/ComparableOperation.java | 18 ------------- .../operations/DownloadFileOperation.java | 16 +----------- .../drive/operations/RemoveFileOperation.java | 16 +----------- .../drive/operations/UploadFileOperation.java | 26 +++---------------- .../e/drive/services/ObserverService.java | 5 ---- .../services/SynchronizationService.java | 2 +- .../operations/UploadFileOperationTest.java | 13 +++++----- 7 files changed, 13 insertions(+), 83 deletions(-) delete mode 100644 app/src/main/java/foundation/e/drive/operations/ComparableOperation.java diff --git a/app/src/main/java/foundation/e/drive/operations/ComparableOperation.java b/app/src/main/java/foundation/e/drive/operations/ComparableOperation.java deleted file mode 100644 index 02b8308c..00000000 --- a/app/src/main/java/foundation/e/drive/operations/ComparableOperation.java +++ /dev/null @@ -1,18 +0,0 @@ -package foundation.e.drive.operations; - -import com.owncloud.android.lib.common.operations.RemoteOperation; - -public interface ComparableOperation { - - /** - * Say if File affected by operation is a media or a settings - * @return - */ - public boolean isMediaType(); - - /** - * Cast instance to generic remoteOperation Type - * @return RemoteOperation this instance - */ - public RemoteOperation toRemoteOperation(); -} diff --git a/app/src/main/java/foundation/e/drive/operations/DownloadFileOperation.java b/app/src/main/java/foundation/e/drive/operations/DownloadFileOperation.java index cfcb8a79..c8d17bb8 100644 --- a/app/src/main/java/foundation/e/drive/operations/DownloadFileOperation.java +++ b/app/src/main/java/foundation/e/drive/operations/DownloadFileOperation.java @@ -26,7 +26,7 @@ import foundation.e.drive.utils.CommonUtils; * @author Vincent Bourgmayer * Encapsulate a global download process for a file */ -public class DownloadFileOperation extends RemoteOperation implements ComparableOperation, Parcelable { +public class DownloadFileOperation extends RemoteOperation implements Parcelable { private final static String TAG = DownloadFileOperation.class.getSimpleName(); private final RemoteFile mRFile; @@ -171,20 +171,6 @@ public class DownloadFileOperation extends RemoteOperation implements Comparable return new RemoteOperationResult( mResultCode ); } - /** - * tell what type of operation is it - * @return true if it is an operation for media's element, then false mean its a settings operation - */ - @Override - public boolean isMediaType(){ - return this.mSyncedState.isMediaType(); - } - - @Override - public RemoteOperation toRemoteOperation() { - return this; - } - @Override public int describeContents() { return 0; diff --git a/app/src/main/java/foundation/e/drive/operations/RemoveFileOperation.java b/app/src/main/java/foundation/e/drive/operations/RemoveFileOperation.java index 6b2132b2..e70841c9 100644 --- a/app/src/main/java/foundation/e/drive/operations/RemoveFileOperation.java +++ b/app/src/main/java/foundation/e/drive/operations/RemoveFileOperation.java @@ -21,7 +21,7 @@ import foundation.e.drive.models.SyncedFileState; * Created by Vincent on 19/06/2018. * Class to be able to wrap concerned SyncedFileState in operation, so it can be retrieve at the end */ -public class RemoveFileOperation extends RemoveFileRemoteOperation implements ComparableOperation, Parcelable { +public class RemoveFileOperation extends RemoveFileRemoteOperation implements Parcelable { private SyncedFileState mSyncedFileState; @@ -61,18 +61,4 @@ public class RemoveFileOperation extends RemoveFileRemoteOperation implements Co public SyncedFileState getSyncedFileState() { return mSyncedFileState; } - - /** - * tell what type of element to sync it is - * @return true if it is an operation for media's element, then false mean its a settings operation - */ - @Override - public boolean isMediaType(){ - return this.mSyncedFileState.isMediaType(); - } - - @Override - public RemoteOperation toRemoteOperation() { - return this; - } } diff --git a/app/src/main/java/foundation/e/drive/operations/UploadFileOperation.java b/app/src/main/java/foundation/e/drive/operations/UploadFileOperation.java index d4d685d0..f4ec50ae 100644 --- a/app/src/main/java/foundation/e/drive/operations/UploadFileOperation.java +++ b/app/src/main/java/foundation/e/drive/operations/UploadFileOperation.java @@ -34,13 +34,12 @@ import foundation.e.drive.utils.CommonUtils; * @author Vincent Bourgmayer * High level Operation which upload a local file to a remote cloud storage */ -public class UploadFileOperation extends RemoteOperation implements ComparableOperation, Parcelable { +public class UploadFileOperation extends RemoteOperation implements Parcelable { private final static String TAG = UploadFileOperation.class.getSimpleName(); private int restartCounter =0; private long previousLastModified; //get to restore real value if all trials fails - private boolean checkEtag; private Context mContext; private SyncedFileState mSyncedState; @@ -49,7 +48,6 @@ public class UploadFileOperation extends RemoteOperation implements ComparableOp protected UploadFileOperation(Parcel in) { restartCounter = in.readInt(); previousLastModified = in.readLong(); - checkEtag = in.readByte() != 0; mSyncedState = in.readParcelable(SyncedFileState.class.getClassLoader()); availableQuota = in.readLong(); } @@ -66,21 +64,13 @@ public class UploadFileOperation extends RemoteOperation implements ComparableOp } }; - @Override - public RemoteOperation toRemoteOperation() { - return this; - } - - /** * Construct an upload operation with an already known syncedFileState * @param syncedFileState syncedFileState corresponding to file. - * @param checkEtag if we should use IF MATCH header with etag */ - public UploadFileOperation (SyncedFileState syncedFileState, boolean checkEtag){ + public UploadFileOperation (SyncedFileState syncedFileState){ this.mSyncedState = syncedFileState; this.previousLastModified = mSyncedState.getLocalLastModified(); - this.checkEtag = checkEtag; } public void setContext(Context context){ @@ -209,7 +199,7 @@ public class UploadFileOperation extends RemoteOperation implements ComparableOp UploadFileRemoteOperation uploadRemoteFileOperation = new UploadFileRemoteOperation( mSyncedState.getLocalPath(), ( targetPath != null ) ? targetPath : mSyncedState.getRemotePath(), CommonUtils.getMimeType( file ), - ( !checkEtag || mSyncedState.getLastETAG().isEmpty() )? null : mSyncedState.getLastETAG(), //If not null, This can cause error 412; that means remote file has change + ( !this.mSyncedState.isMediaType() || mSyncedState.getLastETAG().isEmpty() )? null : mSyncedState.getLastETAG(), //If not null, This can cause error 412; that means remote file has change timeStamp ); uploadRemoteFileOperation.askResultEtag(true); return uploadRemoteFileOperation; @@ -240,15 +230,6 @@ public class UploadFileOperation extends RemoteOperation implements ComparableOp } } - /** - * tell what type of operation is it - * @return true if it is an operation for media's element, then false mean its a settings operation - */ - @Override - public boolean isMediaType(){ - return this.mSyncedState.isMediaType(); - } - @Override public int describeContents() { return 0; @@ -258,7 +239,6 @@ public class UploadFileOperation extends RemoteOperation implements ComparableOp public void writeToParcel(Parcel dest, int flags) { dest.writeInt(restartCounter); dest.writeLong(previousLastModified); - dest.writeByte((byte) (checkEtag ? 1: 0) ); //No method to write boolean dest.writeParcelable(mSyncedState, flags); dest.writeLong(availableQuota); } diff --git a/app/src/main/java/foundation/e/drive/services/ObserverService.java b/app/src/main/java/foundation/e/drive/services/ObserverService.java index c88b4a3e..632d5b8a 100644 --- a/app/src/main/java/foundation/e/drive/services/ObserverService.java +++ b/app/src/main/java/foundation/e/drive/services/ObserverService.java @@ -289,11 +289,6 @@ public class ObserverService extends Service implements OnRemoteOperationListene } } - - - - - /** * Handle end of remote Operation * @param operation The RemoteOperation which ends and call this methods 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 1ff33293..d532786e 100644 --- a/app/src/main/java/foundation/e/drive/services/SynchronizationService.java +++ b/app/src/main/java/foundation/e/drive/services/SynchronizationService.java @@ -221,7 +221,7 @@ public class SynchronizationService extends Service implements OnRemoteOperation switch (request.getOperationType()){ case UPLOAD: final SyncedFileState sfs = request.getSyncedFileState(); - operation = new UploadFileOperation(sfs, sfs.isMediaType()); + operation = new UploadFileOperation(sfs); break; case DOWNLOAD: final DownloadRequest downloadRequest = (DownloadRequest) request; 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 1c01da6f..fbf30d4f 100644 --- a/app/src/test/java/foundation/e/drive/operations/UploadFileOperationTest.java +++ b/app/src/test/java/foundation/e/drive/operations/UploadFileOperationTest.java @@ -161,7 +161,7 @@ public class UploadFileOperationTest { boolean checkEtag = false; - UploadFileOperation testOperation = new UploadFileOperation(syncedFileStates.get(0), checkEtag); + UploadFileOperation testOperation = new UploadFileOperation(syncedFileStates.get(0)); testOperation.setContext(RuntimeEnvironment.application); //Without it, it won't update database RemoteOperationResult result = testOperation.execute(client); @@ -189,9 +189,10 @@ public class UploadFileOperationTest { @Test public void nullSyncedFileState_shouldFail(){ + SyncedFileState syncedFileState = null; //Test fails at the moment because of UploadFileOperation's constructor not checking for syncedFileState is null) // check https://gitlab.e.foundation/e/apps/eDrive/-/issues/120 - UploadFileOperation testOperation = new UploadFileOperation(null, false); + UploadFileOperation testOperation = new UploadFileOperation(syncedFileState); testOperation.setContext(RuntimeEnvironment.application); RemoteOperationResult result = testOperation.execute(client); @@ -212,7 +213,7 @@ public class UploadFileOperationTest { Assert.assertTrue("SyncedFileState loaded from DB must have an empty Etag", sfs_fromDB.getLastETAG().isEmpty()); boolean checkEtag = false; - UploadFileOperation testOperation = new UploadFileOperation(syncedFileStates.get(0), checkEtag); + UploadFileOperation testOperation = new UploadFileOperation(syncedFileStates.get(0)); File smallFile = new File(sfs_fromDB.getLocalPath()); Assert.assertTrue("Local file deletion return false instead of true", smallFile.delete()); @@ -230,7 +231,7 @@ public class UploadFileOperationTest { //long freeQuota = getUserRemoteFreeQuota(); Assert.assertFalse("Reading remote free quota fails"+userFreeQuota, -1 == userFreeQuota); //We don't care of parameter of UploadFileOperation's constructor - RemoteOperationResult actualResult = new UploadFileOperation(Mockito.mock(SyncedFileState.class), false) + RemoteOperationResult actualResult = new UploadFileOperation(Mockito.mock(SyncedFileState.class)) .checkAvailableSpace(client, (userFreeQuota+1)); Assert.assertEquals("Quota check ("+ userFreeQuota+"vs"+(userFreeQuota+1)+") failed", RemoteOperationResult.ResultCode.QUOTA_EXCEEDED, actualResult.getCode()); } @@ -245,7 +246,7 @@ public class UploadFileOperationTest { //long freeQuota = getUserRemoteFreeQuota(); Assert.assertFalse("Reading remote free quota fails"+userFreeQuota, -1 == userFreeQuota); - RemoteOperationResult actualResult = new UploadFileOperation(Mockito.mock(SyncedFileState.class), false) + RemoteOperationResult actualResult = new UploadFileOperation(Mockito.mock(SyncedFileState.class)) .checkAvailableSpace(client, userFreeQuota); Assert.assertEquals("Quota check ("+ userFreeQuota+" vs "+userFreeQuota+") failed", RemoteOperationResult.ResultCode.QUOTA_EXCEEDED, @@ -262,7 +263,7 @@ public class UploadFileOperationTest { //long freeQuota = getUserRemoteFreeQuota(); Assert.assertFalse("Reading remote free quota fails "+userFreeQuota, -1 == userFreeQuota); - RemoteOperationResult actualResult = new UploadFileOperation(Mockito.mock(SyncedFileState.class), false) + RemoteOperationResult actualResult = new UploadFileOperation(Mockito.mock(SyncedFileState.class)) .checkAvailableSpace(client, (userFreeQuota-1)); Assert.assertEquals("Quota check ("+ userFreeQuota+" vs "+(userFreeQuota-1)+") failed", RemoteOperationResult.ResultCode.OK, -- GitLab