From a17244e7fd55ef4ca71b1452cdd2cf9763d7feb2 Mon Sep 17 00:00:00 2001 From: vincent Bourgmayer Date: Thu, 10 Aug 2023 17:26:31 +0200 Subject: [PATCH 1/8] remove persistent flag to eDrive --- app/src/main/AndroidManifest.xml | 1 - 1 file changed, 1 deletion(-) diff --git a/app/src/main/AndroidManifest.xml b/app/src/main/AndroidManifest.xml index ec966a21..d0f189d3 100644 --- a/app/src/main/AndroidManifest.xml +++ b/app/src/main/AndroidManifest.xml @@ -33,7 +33,6 @@ android:allowBackup="true" android:icon="@drawable/ic_murena_eel" android:label="@string/app_name" - android:persistent="true" android:roundIcon="@drawable/ic_murena_eel" android:requestLegacyExternalStorage="true"> Date: Thu, 17 Aug 2023 11:45:17 +0200 Subject: [PATCH 2/8] remove InitializerService class --- .../e/drive/account/AccountSetupWorker.kt | 110 ++++++++++++++++ .../account/receivers/AccountAddedReceiver.kt | 63 +++++++++ .../AccountRemoveCallbackReceiver.java | 7 +- .../receivers/BootCompletedReceiver.java | 3 +- .../e/drive/services/InitializerService.java | 123 ------------------ .../e/drive/services/ObserverService.java | 4 +- .../services/InitializerServiceTest.java | 65 --------- .../e/drive/services/ObserverServiceTest.java | 6 +- 8 files changed, 182 insertions(+), 199 deletions(-) create mode 100644 app/src/main/java/foundation/e/drive/account/AccountSetupWorker.kt create mode 100644 app/src/main/java/foundation/e/drive/account/receivers/AccountAddedReceiver.kt delete mode 100644 app/src/main/java/foundation/e/drive/services/InitializerService.java delete mode 100644 app/src/test/java/foundation/e/drive/services/InitializerServiceTest.java diff --git a/app/src/main/java/foundation/e/drive/account/AccountSetupWorker.kt b/app/src/main/java/foundation/e/drive/account/AccountSetupWorker.kt new file mode 100644 index 00000000..005c5257 --- /dev/null +++ b/app/src/main/java/foundation/e/drive/account/AccountSetupWorker.kt @@ -0,0 +1,110 @@ +/* + * Copyright © MURENA SAS 2022-2023. + * All rights reserved. This program and the accompanying materials + * are made available under the terms of the GNU Public License v3.0 + * which accompanies this distribution, and is available at + * http://www.gnu.org/licenses/gpl.html + */ +package foundation.e.drive.setup + +import android.accounts.Account +import android.accounts.AccountManager +import android.content.Context +import android.content.SharedPreferences +import androidx.work.WorkManager +import androidx.work.Worker +import androidx.work.WorkerParameters +import foundation.e.drive.R +import foundation.e.drive.models.SyncedFolder +import foundation.e.drive.utils.AppConstants +import foundation.e.drive.utils.CommonUtils +import foundation.e.drive.utils.DavClientProvider +import foundation.e.drive.utils.RootSyncedFolderProvider.getSyncedFolderRoots +import timber.log.Timber + +const val accountNameKey ="accountName" +const val accountTypeKey ="accountType" +class AccountSetupWorker(private val context: Context, private val workerParams: WorkerParameters) : + Worker(context, workerParams) { + + private var account: Account? = null + + override fun doWork(): Result { + val accountName = workerParams.inputData.getString(accountNameKey) + val accountType = workerParams.inputData.getString(accountTypeKey) + + val prefs = context.getSharedPreferences( + AppConstants.SHARED_PREFERENCE_NAME, + Context.MODE_PRIVATE) + + if (checkStartConditions(accountName, accountType, prefs)) { + + startSyncWorkers() + + prefs.edit() + .putString(AccountManager.KEY_ACCOUNT_NAME, accountName) + .apply() + + } + + DavClientProvider.getInstance().cleanUp() + return Result.success() + } + + + /** + * Check that conditions to start are met: + * - Setup has not already been done or not achieved + * - AccountName is not empty + * - AccountType is /e/ account + * - the account is effectively available through accountManager + */ + private fun checkStartConditions(accountName:String?, accountType:String?, prefs:SharedPreferences): Boolean { + val alreadyStoredAccountName = prefs.getString(AccountManager.KEY_ACCOUNT_NAME, "") + if (isNotNullNorEmpty(alreadyStoredAccountName)) { //if no accountName found, previous setup cannot have been done + val setupAlreadyDone = + prefs.getBoolean(AppConstants.INITIALIZATION_HAS_BEEN_DONE, false) + if (setupAlreadyDone) return false + + if (accountName != alreadyStoredAccountName) { + Timber.w("An account was already added but not fully setup") + //todo: check with Aude or Jonathan + // what to do if alreadyStoredAccountName & accountName are different. + // Which one to choose ? + } + } + + if (!isValidAccountType(accountType)) return false + if (accountName.isNullOrEmpty()) return false + + account = CommonUtils.getAccount(accountName, AccountManager.get(context)) + if (account == null) { + Timber.w("got Invalid %s account for username: %s ", accountType, accountName) + return false + } + + return true + } + + private fun isNotNullNorEmpty(string: String?): Boolean { + return string != null && string.isNotEmpty() + } + + private fun isValidAccountType(accountType: String?): Boolean { + val expectedAccountType = context.getString(R.string.eelo_account_type) + return accountType != null && accountType == expectedAccountType + } + + private fun startSyncWorkers() { + CommonUtils.registerPeriodicUserInfoChecking(WorkManager.getInstance(context)) + + val syncedFolders: List = getSyncedFolderRoots( + applicationContext + ) + CommonUtils.registerInitializationWorkers( + syncedFolders, WorkManager.getInstance( + applicationContext + ) + ) + } +} \ No newline at end of file diff --git a/app/src/main/java/foundation/e/drive/account/receivers/AccountAddedReceiver.kt b/app/src/main/java/foundation/e/drive/account/receivers/AccountAddedReceiver.kt new file mode 100644 index 00000000..a35ffd37 --- /dev/null +++ b/app/src/main/java/foundation/e/drive/account/receivers/AccountAddedReceiver.kt @@ -0,0 +1,63 @@ +/* + * Copyright © MURENA SAS 2023. + * All rights reserved. This program and the accompanying materials + * are made available under the terms of the GNU Public License v3.0 + * which accompanies this distribution, and is available at + * http://www.gnu.org/licenses/gpl.html + */ +package foundation.e.drive.account + +import android.accounts.AccountManager +import android.content.BroadcastReceiver +import android.content.Context +import android.content.Intent +import android.os.Bundle +import androidx.work.Data +import androidx.work.OneTimeWorkRequestBuilder +import androidx.work.WorkManager +import foundation.e.drive.utils.AppConstants +import timber.log.Timber + +/** + * Entry point for eDrive + * @author Vincent Bourgmayer + */ +class AccountAddedReceiver() : BroadcastReceiver(){ + override fun onReceive(context: Context?, intent: Intent?) { + Timber.d("Received account added intent request") + + if (context == null || intent == null) return + + val data = prepareWorkerInputData(intent.extras) ?: return + + //register worker for setup + val workRequest = OneTimeWorkRequestBuilder() + .setInputData(data) + .addTag(AppConstants.WORK_GENERIC_TAG) + .build() + + WorkManager.getInstance(context).enqueue(workRequest) + /** + * as it is an experimentation right now it will be enough. But for final implementation we + * should answer the following design question + * 1. Shouldn't we rather use enqueueUniqueWork with replace/Ignore policy for older worker ? + * 2. Shouldn't we chain other worker required after account setup instead of triggering them from + * accountSetupWorker ? + */ + + + Timber.i("Account setup worker enqueued") + } + + private fun prepareWorkerInputData(extras: Bundle?): Data? { + val extras = extras ?: return null + + val accountName = extras.getString(AccountManager.KEY_ACCOUNT_NAME, "") + val accountType = extras.getString(AccountManager.KEY_ACCOUNT_TYPE, "") + + return Data.Builder() + .putString(AccountManager.KEY_ACCOUNT_NAME, accountName) + .putString(AccountManager.KEY_ACCOUNT_TYPE, accountType) + .build() + } +} \ No newline at end of file diff --git a/app/src/main/java/foundation/e/drive/receivers/AccountRemoveCallbackReceiver.java b/app/src/main/java/foundation/e/drive/receivers/AccountRemoveCallbackReceiver.java index fc7293f7..09d5a678 100644 --- a/app/src/main/java/foundation/e/drive/receivers/AccountRemoveCallbackReceiver.java +++ b/app/src/main/java/foundation/e/drive/receivers/AccountRemoveCallbackReceiver.java @@ -26,7 +26,6 @@ import java.io.File; import foundation.e.drive.EdriveApplication; import foundation.e.drive.database.DbHelper; import foundation.e.drive.database.FailedSyncPrefsManager; -import foundation.e.drive.services.InitializerService; import foundation.e.drive.services.ObserverService; import foundation.e.drive.services.SynchronizationService; import foundation.e.drive.utils.AppConstants; @@ -100,9 +99,9 @@ public class AccountRemoveCallbackReceiver extends BroadcastReceiver { boolean observerServiceStopResult = applicationContext.stopService(observerServiceIntent); Timber.d("stop ObserverService: %s", observerServiceStopResult); - Intent initializerServiceIntent = new Intent(applicationContext, InitializerService.class); - boolean initServiceStopResult = applicationContext.stopService(initializerServiceIntent); - Timber.d("stop InitializerService: %s", initServiceStopResult); + //Todo Intent initializerServiceIntent = new Intent(applicationContext, InitializerService.class); + //boolean initServiceStopResult = applicationContext.stopService(initializerServiceIntent); + //Timber.d("stop InitializerService: %s", initServiceStopResult); Intent synchronizationServiceIntent = new Intent(applicationContext, SynchronizationService.class); boolean syncServiceStopResult = applicationContext.stopService(synchronizationServiceIntent); diff --git a/app/src/main/java/foundation/e/drive/receivers/BootCompletedReceiver.java b/app/src/main/java/foundation/e/drive/receivers/BootCompletedReceiver.java index 12593bee..5616d55e 100644 --- a/app/src/main/java/foundation/e/drive/receivers/BootCompletedReceiver.java +++ b/app/src/main/java/foundation/e/drive/receivers/BootCompletedReceiver.java @@ -18,7 +18,6 @@ import androidx.annotation.NonNull; import foundation.e.drive.BuildConfig; import foundation.e.drive.database.DbHelper; -import foundation.e.drive.services.InitializerService; import foundation.e.drive.utils.AppConstants; import foundation.e.drive.utils.CommonUtils; import timber.log.Timber; @@ -72,7 +71,7 @@ public class BootCompletedReceiver extends BroadcastReceiver { .putBoolean(AppConstants.INITIALIZATION_HAS_BEEN_DONE, false) .apply(); - context.startService(new Intent(context, InitializerService.class)); + //Todo context.startService(new Intent(context, InitializerService.class)); forceDBUpdate(context); } diff --git a/app/src/main/java/foundation/e/drive/services/InitializerService.java b/app/src/main/java/foundation/e/drive/services/InitializerService.java deleted file mode 100644 index 74927399..00000000 --- a/app/src/main/java/foundation/e/drive/services/InitializerService.java +++ /dev/null @@ -1,123 +0,0 @@ -/* - * Copyright © CLEUS SAS 2018-2019. - * Copyright © ECORP SAS 2022-2023. - * All rights reserved. This program and the accompanying materials - * are made available under the terms of the GNU Public License v3.0 - * which accompanies this distribution, and is available at - * http://www.gnu.org/licenses/gpl.html - */ - -package foundation.e.drive.services; - -import android.accounts.Account; -import android.accounts.AccountManager; -import android.app.Service; -import android.content.Context; -import android.content.Intent; -import android.content.SharedPreferences; -import android.os.IBinder; - -import java.io.File; -import java.util.List; - -import foundation.e.drive.models.SyncedFolder; -import foundation.e.drive.utils.AppConstants; -import foundation.e.drive.utils.CommonUtils; -import foundation.e.drive.utils.DavClientProvider; -import foundation.e.drive.utils.RootSyncedFolderProvider; -import timber.log.Timber; - -import androidx.annotation.NonNull; -import androidx.annotation.Nullable; -import androidx.annotation.VisibleForTesting; -import androidx.work.WorkManager; - -/** - * @author Vincent Bourgmayer - * @author Jonathan Klee - * @author Abhishek Aggarwal - */ -public class InitializerService extends Service { - private Account account; - - @Override - public void onCreate() { - Timber.tag(InitializerService.class.getSimpleName()); - super.onCreate(); - } - - @Override - public int onStartCommand(@NonNull Intent intent, int flags, int startId) { - Timber.i("onStartCommand()"); - DavClientProvider.getInstance().cleanUp(); - CommonUtils.setServiceUnCaughtExceptionHandler(this); - - //Get account - final SharedPreferences prefs = this.getSharedPreferences(AppConstants.SHARED_PREFERENCE_NAME, Context.MODE_PRIVATE); - - String accountName = prefs.getString(AccountManager.KEY_ACCOUNT_NAME, ""); - String accountType = prefs.getString(AccountManager.KEY_ACCOUNT_TYPE, ""); - - if (accountName.isEmpty() && accountType.isEmpty() && intent.getExtras() != null) { - - accountName = intent.getExtras().getString(AccountManager.KEY_ACCOUNT_NAME, ""); - accountType = intent.getExtras().getString(AccountManager.KEY_ACCOUNT_TYPE, ""); - - prefs.edit().putString(AccountManager.KEY_ACCOUNT_NAME, accountName) - .putString(AccountManager.KEY_ACCOUNT_TYPE, accountType) - .apply(); - } - - if (checkStartConditions(prefs, accountName, accountType)) { - start(); - } - return super.onStartCommand(intent, flags, startId); - } - - /** - * Check if condition are present to start - * - Initialization not already done - * - AccountName is not empty - * - Account available - * @return true if condition are met - */ - @VisibleForTesting(otherwise = VisibleForTesting.PRIVATE) - public boolean checkStartConditions(@NonNull final SharedPreferences prefs, - @NonNull final String accountName, @NonNull final String accountType) { - if (prefs.getBoolean(AppConstants.INITIALIZATION_HAS_BEEN_DONE, false)) { - Timber.w("Initialization has already been done"); - return false; - } - - if (accountName.isEmpty()) { - Timber.w("No account Name available"); - return false; - } - - account = CommonUtils.getAccount(accountName, accountType, AccountManager.get(this)); - if (account == null) { - Timber.w("got Invalid %s account for username: %s ", accountType, accountName); - return false; - } - return true; - } - - /** - * Set up base component for eDrive: - * - Register basic worker - * - build root folders to sync - */ - private void start() { - Timber.d("start()"); - CommonUtils.registerPeriodicUserInfoChecking(WorkManager.getInstance(this)); - - final List syncedFolders = RootSyncedFolderProvider.INSTANCE.getSyncedFolderRoots(getApplicationContext()); - CommonUtils.registerInitializationWorkers(syncedFolders, WorkManager.getInstance(getApplicationContext()) ); - } - - @Nullable - @Override - public IBinder onBind(@Nullable Intent intent) { - return null; - } -} \ No newline at end of file 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 8b1d6747..de186c0b 100644 --- a/app/src/main/java/foundation/e/drive/services/ObserverService.java +++ b/app/src/main/java/foundation/e/drive/services/ObserverService.java @@ -152,8 +152,8 @@ public class ObserverService extends Service implements OnRemoteOperationListene if (!prefs.getBoolean(INITIALIZATION_HAS_BEEN_DONE, false)) { Timber.d("Initialization hasn't been done"); - Intent initializerIntent = new Intent(this, InitializerService.class); - startService(initializerIntent); + //Todo Intent initializerIntent = new Intent(this, InitializerService.class); + //startService(initializerIntent); return false; } diff --git a/app/src/test/java/foundation/e/drive/services/InitializerServiceTest.java b/app/src/test/java/foundation/e/drive/services/InitializerServiceTest.java deleted file mode 100644 index ca5febde..00000000 --- a/app/src/test/java/foundation/e/drive/services/InitializerServiceTest.java +++ /dev/null @@ -1,65 +0,0 @@ -package foundation.e.drive.services; - -import static org.junit.Assert.assertFalse; -import static org.junit.Assert.assertTrue; - -import android.accounts.AccountManager; -import android.app.job.JobScheduler; -import android.content.Context; -import android.net.ConnectivityManager; - -import androidx.test.core.app.ApplicationProvider; -import org.junit.Test; -import org.robolectric.Robolectric; - -import foundation.e.drive.TestUtils; -import foundation.e.drive.database.DbHelper; -import foundation.e.drive.utils.AppConstants; - -public class InitializerServiceTest extends AbstractServiceIT{ - - public InitializerServiceTest(){ - mServiceController = Robolectric.buildService(InitializerService.class); - mService = mServiceController.get(); - context = ApplicationProvider.getApplicationContext(); - accountManager = AccountManager.get(context); - jobScheduler = (JobScheduler) context.getSystemService(Context.JOB_SCHEDULER_SERVICE); - contentResolver = context.getContentResolver(); - sharedPreferences = context.getSharedPreferences( AppConstants.SHARED_PREFERENCE_NAME, Context.MODE_PRIVATE); - connectivityManager = (ConnectivityManager) context.getSystemService(Context.CONNECTIVITY_SERVICE); - dbHelper = new DbHelper(context); - TestUtils.initializeWorkmanager(context); - init_done = false; - TestUtils.loadServerCredentials(); - TestUtils.prepareValidAccount(accountManager); - } - - @Test - public void checkStartConditions_noAccountInIntent_NorInPrefs_falseExpected() { - registerSharedPref(); - final boolean canStart = mService.checkStartConditions(sharedPreferences, "", ""); - assertFalse("InitializerService.checkStartConditions without any available account info should return false but returned true", canStart); - } - - @Test - public void checkStartConditions_trueExpected() { - registerSharedPref(); - final boolean canStart = mService.checkStartConditions(sharedPreferences, TestUtils.TEST_ACCOUNT_NAME, TestUtils.TEST_ACCOUNT_TYPE); - assertTrue("InitializerService.checkStartConditions with invalid account should return false but returned true", canStart); - } - - @Test - public void checkStartConditions_withFakeAccountNameOrType_falseExpected() { - registerSharedPref(); - final boolean canStart = mService.checkStartConditions(sharedPreferences, "any", "any"); - assertFalse("InitializerService.checkStartConditions with invalid account should return false but returned true", canStart); - } - - @Test - public void checkStartConditions_withInitDone_falseExpected() { - registerSharedPref(); - sharedPreferences.edit().putBoolean(AppConstants.INITIALIZATION_HAS_BEEN_DONE, true).apply(); - final boolean canStart = mService.checkStartConditions(sharedPreferences, TestUtils.TEST_ACCOUNT_NAME, TestUtils.TEST_ACCOUNT_TYPE); - assertFalse("InitializerService.checkStartConditions with valid account but init already done should return false but returned true", canStart); - } -} diff --git a/app/src/test/java/foundation/e/drive/services/ObserverServiceTest.java b/app/src/test/java/foundation/e/drive/services/ObserverServiceTest.java index bb9ff1ad..2ee0142b 100644 --- a/app/src/test/java/foundation/e/drive/services/ObserverServiceTest.java +++ b/app/src/test/java/foundation/e/drive/services/ObserverServiceTest.java @@ -267,10 +267,10 @@ public class ObserverServiceTest extends AbstractServiceIT { mServiceController.create().startCommand(0, 0); //How to assert this... ? - Intent expectedIntent = new Intent(mService, InitializerService.class); - Intent actualIntent = shadowOf(RuntimeEnvironment.application).getNextStartedService(); + //Todo Intent expectedIntent = new Intent(mService, InitializerService.class); + //Intent actualIntent = shadowOf(RuntimeEnvironment.application).getNextStartedService(); - assertEquals("Checked intent not the expected one", expectedIntent.getComponent(), actualIntent.getComponent()); + //assertEquals("Checked intent not the expected one", expectedIntent.getComponent(), actualIntent.getComponent()); List logs = ShadowLog.getLogs(); ShadowLog.LogItem lastLog = logs.get(logs.size()-1); -- GitLab From a9d85b07f72ca44b51a348f11d4d5e8319cdaaef Mon Sep 17 00:00:00 2001 From: vincent Bourgmayer Date: Thu, 17 Aug 2023 11:46:04 +0200 Subject: [PATCH 3/8] add receivers to catch new account creation --- app/src/main/AndroidManifest.xml | 21 +++++++++++-------- .../account/receivers/AccountAddedReceiver.kt | 3 ++- 2 files changed, 14 insertions(+), 10 deletions(-) diff --git a/app/src/main/AndroidManifest.xml b/app/src/main/AndroidManifest.xml index d0f189d3..4de19709 100644 --- a/app/src/main/AndroidManifest.xml +++ b/app/src/main/AndroidManifest.xml @@ -28,6 +28,9 @@ android:protectionLevel="signature" tools:ignore="ReservedSystemPermission" /> + + - - - - - + @@ -114,6 +109,14 @@ + + + + + diff --git a/app/src/main/java/foundation/e/drive/account/receivers/AccountAddedReceiver.kt b/app/src/main/java/foundation/e/drive/account/receivers/AccountAddedReceiver.kt index a35ffd37..75716fa4 100644 --- a/app/src/main/java/foundation/e/drive/account/receivers/AccountAddedReceiver.kt +++ b/app/src/main/java/foundation/e/drive/account/receivers/AccountAddedReceiver.kt @@ -5,7 +5,7 @@ * which accompanies this distribution, and is available at * http://www.gnu.org/licenses/gpl.html */ -package foundation.e.drive.account +package foundation.e.drive.account.receivers import android.accounts.AccountManager import android.content.BroadcastReceiver @@ -15,6 +15,7 @@ import android.os.Bundle import androidx.work.Data import androidx.work.OneTimeWorkRequestBuilder import androidx.work.WorkManager +import foundation.e.drive.account.AccountSetupWorker import foundation.e.drive.utils.AppConstants import timber.log.Timber -- GitLab From 5f4a4e0950450d20c797f938f409c3b98f79b266 Mon Sep 17 00:00:00 2001 From: vincent Bourgmayer Date: Thu, 17 Aug 2023 11:46:55 +0200 Subject: [PATCH 4/8] add AccountSetupWorker to do InitializerService job --- .../java/foundation/e/drive/account/AccountSetupWorker.kt | 8 ++++++-- 1 file changed, 6 insertions(+), 2 deletions(-) diff --git a/app/src/main/java/foundation/e/drive/account/AccountSetupWorker.kt b/app/src/main/java/foundation/e/drive/account/AccountSetupWorker.kt index 005c5257..8eea9e87 100644 --- a/app/src/main/java/foundation/e/drive/account/AccountSetupWorker.kt +++ b/app/src/main/java/foundation/e/drive/account/AccountSetupWorker.kt @@ -1,11 +1,11 @@ /* - * Copyright © MURENA SAS 2022-2023. + * Copyright © MURENA SAS 2023. * All rights reserved. This program and the accompanying materials * are made available under the terms of the GNU Public License v3.0 * which accompanies this distribution, and is available at * http://www.gnu.org/licenses/gpl.html */ -package foundation.e.drive.setup +package foundation.e.drive.account import android.accounts.Account import android.accounts.AccountManager @@ -24,6 +24,10 @@ import timber.log.Timber const val accountNameKey ="accountName" const val accountTypeKey ="accountType" + +/** + * @author Vincent Bourgmayer + */ class AccountSetupWorker(private val context: Context, private val workerParams: WorkerParameters) : Worker(context, workerParams) { -- GitLab From 17e6589e393dd77fb1e1f34c3256bbc5f728a7d9 Mon Sep 17 00:00:00 2001 From: vincent Bourgmayer Date: Fri, 18 Aug 2023 15:23:19 +0200 Subject: [PATCH 5/8] fix androidManifest for add account receiver --- app/src/main/AndroidManifest.xml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/app/src/main/AndroidManifest.xml b/app/src/main/AndroidManifest.xml index 4de19709..e0236fb9 100644 --- a/app/src/main/AndroidManifest.xml +++ b/app/src/main/AndroidManifest.xml @@ -114,7 +114,7 @@ android:permission="foundation.e.permission.ADD_ACCOUNT" android:exported="true"> - + Date: Mon, 21 Aug 2023 15:54:55 +0600 Subject: [PATCH 6/8] fix accountName key mismatch issue in accountSetupWorker --- .../java/foundation/e/drive/account/AccountSetupWorker.kt | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/app/src/main/java/foundation/e/drive/account/AccountSetupWorker.kt b/app/src/main/java/foundation/e/drive/account/AccountSetupWorker.kt index 8eea9e87..bb16c720 100644 --- a/app/src/main/java/foundation/e/drive/account/AccountSetupWorker.kt +++ b/app/src/main/java/foundation/e/drive/account/AccountSetupWorker.kt @@ -22,8 +22,8 @@ import foundation.e.drive.utils.DavClientProvider import foundation.e.drive.utils.RootSyncedFolderProvider.getSyncedFolderRoots import timber.log.Timber -const val accountNameKey ="accountName" -const val accountTypeKey ="accountType" +const val accountNameKey = AccountManager.KEY_ACCOUNT_NAME +const val accountTypeKey = AccountManager.KEY_ACCOUNT_TYPE /** * @author Vincent Bourgmayer -- GitLab From 88e6a2ce0d03c753ae87794daeb31434b24d27ec Mon Sep 17 00:00:00 2001 From: vincent Bourgmayer Date: Wed, 23 Aug 2023 11:17:24 +0200 Subject: [PATCH 7/8] fix setUpAccountWorker --- .../foundation/e/drive/account/AccountSetupWorker.kt | 10 +++------- 1 file changed, 3 insertions(+), 7 deletions(-) diff --git a/app/src/main/java/foundation/e/drive/account/AccountSetupWorker.kt b/app/src/main/java/foundation/e/drive/account/AccountSetupWorker.kt index bb16c720..af4d331c 100644 --- a/app/src/main/java/foundation/e/drive/account/AccountSetupWorker.kt +++ b/app/src/main/java/foundation/e/drive/account/AccountSetupWorker.kt @@ -65,7 +65,7 @@ class AccountSetupWorker(private val context: Context, private val workerParams: */ private fun checkStartConditions(accountName:String?, accountType:String?, prefs:SharedPreferences): Boolean { val alreadyStoredAccountName = prefs.getString(AccountManager.KEY_ACCOUNT_NAME, "") - if (isNotNullNorEmpty(alreadyStoredAccountName)) { //if no accountName found, previous setup cannot have been done + if (!alreadyStoredAccountName.isNullOrEmpty()) { //if no accountName found, next setup cannot have been done val setupAlreadyDone = prefs.getBoolean(AppConstants.INITIALIZATION_HAS_BEEN_DONE, false) if (setupAlreadyDone) return false @@ -79,9 +79,9 @@ class AccountSetupWorker(private val context: Context, private val workerParams: } if (!isValidAccountType(accountType)) return false - if (accountName.isNullOrEmpty()) return false + if (accountName.isNullOrEmpty() || accountType.isNullOrEmpty()) return false - account = CommonUtils.getAccount(accountName, AccountManager.get(context)) + account = CommonUtils.getAccount(accountName, accountType, AccountManager.get(context)) if (account == null) { Timber.w("got Invalid %s account for username: %s ", accountType, accountName) return false @@ -90,10 +90,6 @@ class AccountSetupWorker(private val context: Context, private val workerParams: return true } - private fun isNotNullNorEmpty(string: String?): Boolean { - return string != null && string.isNotEmpty() - } - private fun isValidAccountType(accountType: String?): Boolean { val expectedAccountType = context.getString(R.string.eelo_account_type) return accountType != null && accountType == expectedAccountType -- GitLab From 16e4fe06e0f63cf5dee51f7b8a354084323ad445 Mon Sep 17 00:00:00 2001 From: vincent Bourgmayer Date: Tue, 29 Aug 2023 15:58:40 +0200 Subject: [PATCH 8/8] implement a worker to replace ObserverService --- .../contentScanner/PeriodicScanWorker.kt | 205 ++++++++++++++++++ 1 file changed, 205 insertions(+) create mode 100644 app/src/main/java/foundation/e/drive/contentScanner/PeriodicScanWorker.kt diff --git a/app/src/main/java/foundation/e/drive/contentScanner/PeriodicScanWorker.kt b/app/src/main/java/foundation/e/drive/contentScanner/PeriodicScanWorker.kt new file mode 100644 index 00000000..658381d5 --- /dev/null +++ b/app/src/main/java/foundation/e/drive/contentScanner/PeriodicScanWorker.kt @@ -0,0 +1,205 @@ +package foundation.e.drive.contentScanner + +import android.accounts.Account +import android.accounts.AccountManager +import android.content.ComponentName +import android.content.Context +import android.content.SharedPreferences +import android.os.IBinder +import androidx.work.Worker +import androidx.work.WorkerParameters +import com.owncloud.android.lib.common.operations.RemoteOperationResult +import com.owncloud.android.lib.resources.files.model.RemoteFile +import foundation.e.drive.R +import foundation.e.drive.database.DbHelper +import foundation.e.drive.models.SyncRequest +import foundation.e.drive.models.SyncedFolder +import foundation.e.drive.operations.ListFileRemoteOperation +import foundation.e.drive.utils.AppConstants +import foundation.e.drive.utils.CommonUtils +import foundation.e.drive.utils.DavClientProvider +import foundation.e.drive.utils.SynchronizationServiceConnection +import timber.log.Timber + +class PeriodicScanWorker(private val context: Context, private val workerParams: WorkerParameters) : + Worker(context, workerParams) { + + private val syncRequests = HashMap() + /**private val synchronizationServiceConnection: SynchronizationServiceConnection = + object : SynchronizationServiceConnection() { + override fun onServiceConnected(componentName: ComponentName?, iBinder: IBinder) { + super.onServiceConnected(componentName, iBinder) + val prefs = applicationContext.getSharedPreferences( + AppConstants.SHARED_PREFERENCE_NAME, + Context.MODE_PRIVATE + ) + if (!checkStartCondition(prefs, forcedSync)) { + stopSelf() + return + } + begin() + } + }**/ + + companion object { + private const val INTERSYNC_MINIMUM_DELAY = 900000 // min delay between two sync in ms. + const val ACTION_FORCED_SYNC_KEY = "forced_sync" + } + + override fun doWork(): Result { + //synchronizationServiceConnection.bindService(applicationContext) + + val prefs = context.getSharedPreferences(AppConstants.SHARED_PREFERENCE_NAME, Context.MODE_PRIVATE) + val account = loadAccount(prefs) + + if (account == null || !checkStartConditions(account, prefs)) return Result.failure() + + val syncFolders = loadSyncedFolders(account) + if (syncFolders.isEmpty()) return Result.success() + + val remoteSyncRequest = scanRemoteFiles(account, syncFolders.toMutableList()) + syncRequests.putAll(remoteSyncRequest) + + val localSyncRequest = scanLocalFiles(syncFolders.toMutableList()) + syncRequests.putAll(localSyncRequest) + + if (syncRequests.isNotEmpty()) { + Timber.d("syncRequests contains %s", syncRequests.size) + //todo send requests to synchronization service + } else { + Timber.i("There is no content to synchronized") + } + return Result.success() + } + + private fun loadAccount(prefs: SharedPreferences): Account? { + val accountName = prefs.getString(AccountManager.KEY_ACCOUNT_NAME, "") ?: return null + val accountType = context.getString(R.string.eelo_account_type) + + return CommonUtils.getAccount(accountName, accountType, AccountManager.get(context)) + } + + /** + * Check conditions to start periodic scan are respected + */ + private fun checkStartConditions(account: Account, prefs : SharedPreferences): Boolean { + Timber.d("PeriodicScanWorker.checkStartConditions()") + + if (isFileSyncDisabled(account)) return false //@todo could be replaced by checking list of sync folders not empty after loading them + + if (!isSetupDone(prefs)) return false + + val forcedSync = workerParams.inputData.getBoolean("ACTION_FORCED_SYNC_KEY", false) + + if (!forcedSync && !isMinimumDelayRespected(prefs)) return false + + if (!isConnectedToAllowedNetwork(account)) return false + + return true + } + + /** + * Indicates if user has disable both media and settings files synchronization + */ + private fun isFileSyncDisabled(account: Account) : Boolean { + return !CommonUtils.isMediaSyncEnabled(account) && !CommonUtils.isSettingsSyncEnabled(account) + } + + /** + * Indicates if account has been set up and Remote root folders created + */ + private fun isSetupDone(prefs: SharedPreferences): Boolean { + return prefs.getBoolean(AppConstants.INITIALIZATION_HAS_BEEN_DONE, false) + } + + /** + * Check minimum delay between two periodic sync is respected + */ + private fun isMinimumDelayRespected(prefs: SharedPreferences): Boolean { + val lastSyncTime = prefs.getLong(AppConstants.KEY_LAST_SYNC_TIME, 0L) + val currentTime = System.currentTimeMillis() + val deltaTime = currentTime - lastSyncTime + + return deltaTime >= INTERSYNC_MINIMUM_DELAY + } + + /** + * Check if device is connected to an allowed network type for synchronization + * note: because user can disable sync over metered network + */ + private fun isConnectedToAllowedNetwork(account: Account): Boolean { + val isMeteredNetworkAllowed = CommonUtils.isMeteredNetworkAllowed(account) + return CommonUtils.haveNetworkConnection(context, isMeteredNetworkAllowed) + } + + /** + * Fetch SyncedFolders list from Database + */ + private fun loadSyncedFolders(account: Account): List { + val isMediaSyncEnabled = CommonUtils.isMediaSyncEnabled(account) + val isSettingsSyncEnabled = CommonUtils.isSettingsSyncEnabled(account) + + return if (isMediaSyncEnabled && isSettingsSyncEnabled) DbHelper.getAllSyncedFolders(context) + else if (isMediaSyncEnabled) DbHelper.getSyncedFolderList(context, true) + else if (isSettingsSyncEnabled) DbHelper.getSyncedFolderList(context, false) + else ArrayList() + } + + + /** + * This method could be rewritten in a better way like: + * 1. Fetch remoteFiles + * 2. Fetch SyncedFileStates + * 3. use RemoteContentScanner to get SyncRequest + */ + private fun scanRemoteFiles(account: Account, syncedFolders: List): HashMap { + val ocClient = DavClientProvider.getInstance().getClientInstance(account, context)?: return HashMap() + val listRemoteFilesOperation = ListFileRemoteOperation(syncedFolders, context) + + try { + @Suppress("DEPRECATION") + val result = listRemoteFilesOperation.execute(ocClient) as RemoteOperationResult> + if (!result.isSuccess) { + Timber.d("Fails to check remote files") + } + + val syncedFoldersIds = listRemoteFilesOperation.syncedFoldersId + val syncedFileStates = DbHelper.getSyncedFileStatesByFolders(context, syncedFoldersIds) + val remoteFiles = result.resultData as List + + if (remoteFiles.isNotEmpty() && syncedFileStates.isNotEmpty()) { + val scanner = RemoteContentScanner(context, syncedFolders) + return scanner.scanContent(remoteFiles, syncedFileStates) + } + + } catch(exception: IllegalArgumentException) { + Timber.e(exception) + } + return HashMap() + } + + private fun scanLocalFiles(syncedFolders: List): HashMap { + val localFileLister = LocalFileLister(syncedFolders) + val isContentToScan = localFileLister.listContentToScan(context) + + if(!isContentToScan) { + return HashMap() + } + + val localFiles = localFileLister.contentToScan + val syncedFoldersIds = localFileLister.syncedFoldersId + val syncedFileStates = DbHelper.getSyncedFileStatesByFolders(context, syncedFoldersIds) + + if (localFiles.isNotEmpty() && syncedFileStates.isNotEmpty()) { + val scanner = LocalContentScanner(context, syncedFolders) + return scanner.scanContent(localFiles, syncedFileStates) + } + return HashMap() + } + + private fun passSyncRequestsToSynchronizationService() { + //val SynchronizatioNService = synchronizationServiceConnection.getSynchronizationService() + //todo find a way to send syncRequest to SynchronizationService + } + +} \ No newline at end of file -- GitLab