diff --git a/app/src/main/AndroidManifest.xml b/app/src/main/AndroidManifest.xml index 177f5488b06b08e947ab429251ff9d7f6bdd5ff8..f8c0bfe3a27bf3c8676e74696719bd83647883ca 100644 --- a/app/src/main/AndroidManifest.xml +++ b/app/src/main/AndroidManifest.xml @@ -83,9 +83,6 @@ android:exported="true" android:label="@string/account_setting_metered_network" tools:ignore="ExportedContentProvider" /> - = ArrayList() for (folder in rootSyncedFolderList) { - val createRemoteFolderWorkRequest = getOneTimeWorkRequest( - WorkType.CREATE_REMOTE_DIR, + val rootFolderSetupWorkRequest = getOneTimeWorkRequest( + WorkType.ONE_TIME_ROOT_FOLDER_SETUP, folder ) - workRequests.add(createRemoteFolderWorkRequest) + workRequests.add(rootFolderSetupWorkRequest) } return workRequests } diff --git a/app/src/main/java/foundation/e/drive/account/receivers/AccountRemoveCallbackReceiver.java b/app/src/main/java/foundation/e/drive/account/receivers/AccountRemoveCallbackReceiver.java index 3363425bf2e82d044f602bf7b5cc1c3ecc6bb955..bcfd6b5e6b3588b39fbaa487de87f7e811b3f985 100644 --- a/app/src/main/java/foundation/e/drive/account/receivers/AccountRemoveCallbackReceiver.java +++ b/app/src/main/java/foundation/e/drive/account/receivers/AccountRemoveCallbackReceiver.java @@ -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.ObserverService; import foundation.e.drive.services.SynchronizationService; import foundation.e.drive.utils.AppConstants; import foundation.e.drive.utils.DavClientProvider; @@ -95,9 +94,6 @@ public class AccountRemoveCallbackReceiver extends BroadcastReceiver { } private void stopAllServices(@NonNull Context applicationContext) { - Intent observerServiceIntent = new Intent(applicationContext, ObserverService.class); - boolean observerServiceStopResult = applicationContext.stopService(observerServiceIntent); - Timber.d("stop ObserverService: %s", observerServiceStopResult); Intent synchronizationServiceIntent = new Intent(applicationContext, SynchronizationService.class); boolean syncServiceStopResult = applicationContext.stopService(synchronizationServiceIntent); @@ -111,7 +107,7 @@ public class AccountRemoveCallbackReceiver extends BroadcastReceiver { .remove(AccountManager.KEY_ACCOUNT_TYPE) .remove(SETUP_COMPLETED) .remove(INITIAL_FOLDER_NUMBER) - .remove(AppConstants.KEY_LAST_SYNC_TIME) + .remove(AppConstants.KEY_LAST_SCAN_TIME) .apply(); } diff --git a/app/src/main/java/foundation/e/drive/account/setup/FinishSetupWorker.java b/app/src/main/java/foundation/e/drive/account/setup/FinishSetupWorker.java new file mode 100644 index 0000000000000000000000000000000000000000..1563cf01cbf0b2af40f4bfb9af174522f328840d --- /dev/null +++ b/app/src/main/java/foundation/e/drive/account/setup/FinishSetupWorker.java @@ -0,0 +1,93 @@ +/* + * 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.account.setup; + +import static android.content.Context.MODE_PRIVATE; +import static foundation.e.drive.utils.AppConstants.INITIAL_FOLDER_NUMBER; +import static foundation.e.drive.utils.AppConstants.SHARED_PREFERENCE_NAME; +import static foundation.e.drive.work.WorkRequestFactory.WorkType.ONE_TIME_APP_LIST; +import static foundation.e.drive.work.WorkRequestFactory.WorkType.ONE_TIME_FORCED_FULL_SCAN; +import static foundation.e.drive.work.WorkRequestFactory.WorkType.PERIODIC_SCAN; + +import android.content.Context; + +import androidx.annotation.NonNull; +import androidx.work.ExistingPeriodicWorkPolicy; +import androidx.work.ExistingWorkPolicy; +import androidx.work.WorkManager; +import androidx.work.Worker; +import androidx.work.WorkerParameters; + +import foundation.e.drive.periodicScan.FullScanWorker; +import foundation.e.drive.periodicScan.PeriodicScanWorker; +import foundation.e.drive.utils.AppConstants; +import foundation.e.drive.work.WorkRequestFactory; +import timber.log.Timber; + +/** + * This class perform last task of setup + * It contains job to start FileObserver, periodic fullScan and Synchronization service + * for the first time + * @author Vincent Bourgmayer + */ +public class FinishSetupWorker extends Worker { + public FinishSetupWorker(@NonNull Context context, @NonNull WorkerParameters workerParams) { + super(context, workerParams); + } + + @NonNull + @Override + public Result doWork() { + Timber.v("FinishSetupWorker.doWork()"); + try { + final Context appContext = getApplicationContext(); + enqueueWorkers(appContext); + updateSharedPreferences(appContext); + + return Result.success(); + + } catch (Exception exception) { + Timber.e(exception); + } + return Result.retry(); + } + + private void updateSharedPreferences(Context appContext) { + appContext.getSharedPreferences(SHARED_PREFERENCE_NAME, MODE_PRIVATE) + .edit() + .putBoolean(AppConstants.SETUP_COMPLETED, true) + .putInt(INITIAL_FOLDER_NUMBER, 9) + .apply(); + } + + private void enqueueWorkers(@NonNull final Context appContext) { + final WorkManager workManager = WorkManager.getInstance(appContext); + enqueueAppListGenerationWorker(workManager); + enqueueFullScanWorker(workManager); + enqueuePeriodicFileScanWorker(workManager); + } + + private void enqueueFullScanWorker(@NonNull final WorkManager workManager) { + workManager.enqueueUniqueWork( + FullScanWorker.UNIQUE_WORK_NAME, + ExistingWorkPolicy.KEEP, + WorkRequestFactory.getOneTimeWorkRequest(ONE_TIME_FORCED_FULL_SCAN, null) + ); + } + + private void enqueueAppListGenerationWorker(@NonNull final WorkManager workManager) { + workManager.enqueue(WorkRequestFactory.getOneTimeWorkRequest(ONE_TIME_APP_LIST, null)); + } + + private void enqueuePeriodicFileScanWorker(@NonNull final WorkManager workManager) { + workManager.enqueueUniquePeriodicWork(PeriodicScanWorker.UNIQUE_WORK_NAME, + ExistingPeriodicWorkPolicy.KEEP, + WorkRequestFactory.getPeriodicWorkRequest(PERIODIC_SCAN)); + } +} \ No newline at end of file diff --git a/app/src/main/java/foundation/e/drive/work/RootFolderSetupWorker.java b/app/src/main/java/foundation/e/drive/account/setup/RootFolderSetupWorker.java similarity index 99% rename from app/src/main/java/foundation/e/drive/work/RootFolderSetupWorker.java rename to app/src/main/java/foundation/e/drive/account/setup/RootFolderSetupWorker.java index 8ff54f22354017e337a2c49519737a3b8c39f5a9..6488eeb54c74b1c487105bf1090d945cf72f8d29 100644 --- a/app/src/main/java/foundation/e/drive/work/RootFolderSetupWorker.java +++ b/app/src/main/java/foundation/e/drive/account/setup/RootFolderSetupWorker.java @@ -6,7 +6,7 @@ * http://www.gnu.org/licenses/gpl.html */ -package foundation.e.drive.work; +package foundation.e.drive.account.setup; import android.accounts.Account; import android.accounts.AccountManager; diff --git a/app/src/main/java/foundation/e/drive/periodicScan/FullScanWorker.kt b/app/src/main/java/foundation/e/drive/periodicScan/FullScanWorker.kt new file mode 100644 index 0000000000000000000000000000000000000000..0a4776aec98e1fdc537fe9b6552278250523b81d --- /dev/null +++ b/app/src/main/java/foundation/e/drive/periodicScan/FullScanWorker.kt @@ -0,0 +1,227 @@ +/* + * 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.periodicScan + +import android.accounts.Account +import android.accounts.AccountManager +import android.app.Application +import android.content.Context +import android.content.SharedPreferences +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.periodicScan.contentScanner.LocalContentScanner +import foundation.e.drive.periodicScan.contentScanner.LocalFileLister +import foundation.e.drive.periodicScan.contentScanner.RemoteContentScanner +import foundation.e.drive.synchronization.SyncProxy +import foundation.e.drive.synchronization.SyncRequestCollector +import foundation.e.drive.utils.AppConstants +import foundation.e.drive.utils.AppConstants.KEY_LAST_SCAN_TIME +import foundation.e.drive.utils.CommonUtils +import foundation.e.drive.utils.DavClientProvider +import timber.log.Timber + +class FullScanWorker(private val context: Context, private val workerParams: WorkerParameters) : + Worker(context, workerParams) +{ + + private val syncRequests = HashMap() + + companion object { + private const val SYNC_MINIMUM_DELAY = 900000 // min delay between two run in ms. + const val ACTION_FORCED_SYNC_KEY = "forced_sync" + const val UNIQUE_WORK_NAME = "fullScan" + } + + override fun doWork(): Result { + try { + val requestCollector: SyncRequestCollector = SyncProxy + + val prefs = context.getSharedPreferences( + AppConstants.SHARED_PREFERENCE_NAME, + Context.MODE_PRIVATE + ) + val account = loadAccount(prefs) + + val startAllowed = checkStartConditions(account, prefs, requestCollector) + if (!startAllowed) { + Timber.d("Start Periodic Scan is not allowed") + requestCollector.startListeningFiles(applicationContext as Application) + return Result.failure() + } + + val syncFolders = loadSyncedFolders(account!!) + if (syncFolders.isEmpty()) { + requestCollector.startListeningFiles(applicationContext as Application) + return Result.success() + } + + val remoteSyncRequests = scanRemoteFiles(account, syncFolders.toMutableList()) + syncRequests.putAll(remoteSyncRequests) + + Timber.d("${remoteSyncRequests.size} request collected from cloud") + + val localSyncRequests = scanLocalFiles(syncFolders.toMutableList()) + syncRequests.putAll(localSyncRequests) + Timber.d("${localSyncRequests.size} request collected from device") + + prefs.edit() + .putLong(KEY_LAST_SCAN_TIME, System.currentTimeMillis()) + .apply(); + + if (syncRequests.isEmpty()) { + Timber.d("Nothing to sync") + requestCollector.startListeningFiles(applicationContext as Application) + return Result.success() + } + + Timber.d("${syncRequests.size} files to sync") + requestCollector.queueSyncRequests(syncRequests.values, context) + requestCollector.startSynchronization(applicationContext) + return Result.success() + + } catch (exception: Exception) { + Timber.e(exception) + return Result.failure() + } + } + + private fun checkStartConditions(account: Account?, prefs : SharedPreferences, requestCollector: SyncRequestCollector): Boolean { + Timber.d("FullScanWorker.checkStartConditions()") + + if (account == null) return false + + if (!isSetupDone(prefs)) return false + + if (isFileSyncDisabled(account)) return false //@todo could be replaced by checking list of sync folders not empty after loading them + + val forcedSync = workerParams.inputData.getBoolean(ACTION_FORCED_SYNC_KEY, false) + + if (!forcedSync && !isMinimumDelayRespected(prefs)) return false + + if (!isConnectedToAllowedNetwork(account)) return false + + if (!requestCollector.onPeriodicScanStart(applicationContext as Application)) return false + + return true + } + + 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)) + } + + /** + * indicate if minimum delay between two periodic scan is respected + */ + private fun isMinimumDelayRespected(prefs: SharedPreferences): Boolean { + val lastSyncTime = prefs.getLong(KEY_LAST_SCAN_TIME, 0L) + val currentTime = System.currentTimeMillis() + val deltaTime = currentTime - lastSyncTime + + return deltaTime >= SYNC_MINIMUM_DELAY + } + + /** + * 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.SETUP_COMPLETED, false) + } + + /** + * 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() + } + + /** + * generate SyncRequest for files on the cloud + */ + 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 remoteFiles = result.resultData as List + + val syncedFoldersIds = listRemoteFilesOperation.syncedFoldersId + val syncedFileStates = DbHelper.getSyncedFileStatesByFolders(context, syncedFoldersIds) + + if (remoteFiles.isNotEmpty() || syncedFileStates.isNotEmpty()) { + val scanner = RemoteContentScanner(context, syncedFolders) + return scanner.scanContent(remoteFiles, syncedFileStates) + } + + } catch(exception: IllegalArgumentException) { + Timber.e(exception) + } + return HashMap() + } + + /** + * generate SyncRequest for files on the device + */ + 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() + } +} \ No newline at end of file diff --git a/app/src/main/java/foundation/e/drive/work/ListAppsWorker.java b/app/src/main/java/foundation/e/drive/periodicScan/ListAppsWorker.java similarity index 99% rename from app/src/main/java/foundation/e/drive/work/ListAppsWorker.java rename to app/src/main/java/foundation/e/drive/periodicScan/ListAppsWorker.java index 1309f6eeafbd62a3f9c6e8a6d1084926f6a52c66..6c6b9062f1efa9be72c633f7b855443c898bcfef 100644 --- a/app/src/main/java/foundation/e/drive/work/ListAppsWorker.java +++ b/app/src/main/java/foundation/e/drive/periodicScan/ListAppsWorker.java @@ -6,7 +6,7 @@ * http://www.gnu.org/licenses/gpl.html */ -package foundation.e.drive.work; +package foundation.e.drive.periodicScan; import static com.owncloud.android.lib.resources.files.FileUtils.PATH_SEPARATOR; diff --git a/app/src/main/java/foundation/e/drive/operations/ListFileRemoteOperation.java b/app/src/main/java/foundation/e/drive/periodicScan/ListFileRemoteOperation.java similarity index 85% rename from app/src/main/java/foundation/e/drive/operations/ListFileRemoteOperation.java rename to app/src/main/java/foundation/e/drive/periodicScan/ListFileRemoteOperation.java index c5aa5ec6f617a00d2fe3292224672590eda41e18..0e07ee51dadf2f24f5632110e2d2ea979bf3e950 100644 --- a/app/src/main/java/foundation/e/drive/operations/ListFileRemoteOperation.java +++ b/app/src/main/java/foundation/e/drive/periodicScan/ListFileRemoteOperation.java @@ -1,36 +1,31 @@ /* * Copyright © CLEUS SAS 2018-2019. - * Copyright © ECORP SAS 2022. + * 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.operations; +package foundation.e.drive.periodicScan; import android.content.Context; import androidx.annotation.NonNull; -import androidx.annotation.VisibleForTesting; import com.owncloud.android.lib.common.OwnCloudClient; import com.owncloud.android.lib.common.operations.RemoteOperation; import com.owncloud.android.lib.common.operations.RemoteOperationResult; -import com.owncloud.android.lib.resources.files.ReadFolderRemoteOperation; import com.owncloud.android.lib.resources.files.model.RemoteFile; -import java.io.File; + import java.util.ArrayList; import java.util.List; -import java.util.ListIterator; -import foundation.e.drive.contentScanner.RemoteFileLister; +import foundation.e.drive.periodicScan.contentScanner.RemoteFileLister; import foundation.e.drive.database.DbHelper; import foundation.e.drive.models.SyncedFolder; -import foundation.e.drive.utils.CommonUtils; import timber.log.Timber; - /** * /!\ Doesn't require NextcloudClient yet * @author Vincent Bourgmayer @@ -63,8 +58,8 @@ public class ListFileRemoteOperation extends RemoteOperation(RemoteOperationResult.ResultCode.OK); if (isContentToScan) { DbHelper.updateSyncedFolders(this.syncedFolders, this.context); - result.setResultData(fileLister.getContentToScan()); } + result.setResultData(fileLister.getContentToScan()); updatedSyncedFoldersId.addAll(fileLister.getSyncedFoldersId()); return result; diff --git a/app/src/main/java/foundation/e/drive/work/PeriodicWorker.java b/app/src/main/java/foundation/e/drive/periodicScan/PeriodicScanWorker.java similarity index 85% rename from app/src/main/java/foundation/e/drive/work/PeriodicWorker.java rename to app/src/main/java/foundation/e/drive/periodicScan/PeriodicScanWorker.java index 560ef9f90ddf6382a7e1ea9b1aa6fb4bef3ff7e4..ffaf89c664679dfe4d7dfe296b43381fbba9a216 100644 --- a/app/src/main/java/foundation/e/drive/work/PeriodicWorker.java +++ b/app/src/main/java/foundation/e/drive/periodicScan/PeriodicScanWorker.java @@ -6,7 +6,7 @@ * http://www.gnu.org/licenses/gpl.html */ -package foundation.e.drive.work; +package foundation.e.drive.periodicScan; import static foundation.e.drive.work.WorkRequestFactory.WorkType.ONE_TIME_APP_LIST; import static foundation.e.drive.work.WorkRequestFactory.WorkType.ONE_TIME_FULL_SCAN; @@ -23,6 +23,7 @@ import androidx.work.WorkerParameters; import java.util.ArrayList; import java.util.List; +import foundation.e.drive.work.WorkRequestFactory; import timber.log.Timber; /** @@ -31,9 +32,9 @@ import timber.log.Timber; * at the moment * @author vincent Bourgmayer */ -public class PeriodicWorker extends Worker { - public final static String UNIQUE_WORK_NAME = "periodicWork"; - public PeriodicWorker(@NonNull Context context, @NonNull WorkerParameters workerParams) { +public class PeriodicScanWorker extends Worker { + public final static String UNIQUE_WORK_NAME = "periodicScanWork"; + public PeriodicScanWorker(@NonNull Context context, @NonNull WorkerParameters workerParams) { super(context, workerParams); } diff --git a/app/src/main/java/foundation/e/drive/contentScanner/AbstractContentScanner.java b/app/src/main/java/foundation/e/drive/periodicScan/contentScanner/AbstractContentScanner.java similarity index 98% rename from app/src/main/java/foundation/e/drive/contentScanner/AbstractContentScanner.java rename to app/src/main/java/foundation/e/drive/periodicScan/contentScanner/AbstractContentScanner.java index c8b843c9d76bf22cb1db17c1b3765d86833eb883..6bb3deb25f3cecd27c15567c9034599945340bb9 100644 --- a/app/src/main/java/foundation/e/drive/contentScanner/AbstractContentScanner.java +++ b/app/src/main/java/foundation/e/drive/periodicScan/contentScanner/AbstractContentScanner.java @@ -5,7 +5,7 @@ * which accompanies this distribution, and is available at * http://www.gnu.org/licenses/gpl.html */ -package foundation.e.drive.contentScanner; +package foundation.e.drive.periodicScan.contentScanner; import android.content.Context; diff --git a/app/src/main/java/foundation/e/drive/contentScanner/AbstractFileLister.java b/app/src/main/java/foundation/e/drive/periodicScan/contentScanner/AbstractFileLister.java similarity index 99% rename from app/src/main/java/foundation/e/drive/contentScanner/AbstractFileLister.java rename to app/src/main/java/foundation/e/drive/periodicScan/contentScanner/AbstractFileLister.java index 0e45cdcdc6ea30967fd96977c05db5212487d9cb..da8a6cb7543f40a74342b869f60c96119681dc20 100644 --- a/app/src/main/java/foundation/e/drive/contentScanner/AbstractFileLister.java +++ b/app/src/main/java/foundation/e/drive/periodicScan/contentScanner/AbstractFileLister.java @@ -5,7 +5,7 @@ * which accompanies this distribution, and is available at * http://www.gnu.org/licenses/gpl.html */ -package foundation.e.drive.contentScanner; +package foundation.e.drive.periodicScan.contentScanner; import android.content.Context; diff --git a/app/src/main/java/foundation/e/drive/contentScanner/FileDiffUtils.kt b/app/src/main/java/foundation/e/drive/periodicScan/contentScanner/FileDiffUtils.kt similarity index 98% rename from app/src/main/java/foundation/e/drive/contentScanner/FileDiffUtils.kt rename to app/src/main/java/foundation/e/drive/periodicScan/contentScanner/FileDiffUtils.kt index 273dfe5c7e5d6a2e0798dc54ed480ea736cc7dd5..41e34693a48ec0db9eaecad4105ac9f66bdb97b6 100644 --- a/app/src/main/java/foundation/e/drive/contentScanner/FileDiffUtils.kt +++ b/app/src/main/java/foundation/e/drive/periodicScan/contentScanner/FileDiffUtils.kt @@ -5,7 +5,7 @@ * which accompanies this distribution, and is available at * http://www.gnu.org/licenses/gpl.html */ -package foundation.e.drive.contentScanner +package foundation.e.drive.periodicScan.contentScanner import androidx.annotation.VisibleForTesting import com.owncloud.android.lib.resources.files.model.RemoteFile diff --git a/app/src/main/java/foundation/e/drive/contentScanner/FolderWrapper.java b/app/src/main/java/foundation/e/drive/periodicScan/contentScanner/FolderWrapper.java similarity index 97% rename from app/src/main/java/foundation/e/drive/contentScanner/FolderWrapper.java rename to app/src/main/java/foundation/e/drive/periodicScan/contentScanner/FolderWrapper.java index 49a5ee37b32a3e2b59d5ef92f5b187847b348c75..4c9bb0bcada6c0522408362ff2f9fbd320dcf060 100644 --- a/app/src/main/java/foundation/e/drive/contentScanner/FolderWrapper.java +++ b/app/src/main/java/foundation/e/drive/periodicScan/contentScanner/FolderWrapper.java @@ -5,7 +5,7 @@ * which accompanies this distribution, and is available at * http://www.gnu.org/licenses/gpl.html */ -package foundation.e.drive.contentScanner; +package foundation.e.drive.periodicScan.contentScanner; import androidx.annotation.NonNull; diff --git a/app/src/main/java/foundation/e/drive/contentScanner/LocalContentScanner.java b/app/src/main/java/foundation/e/drive/periodicScan/contentScanner/LocalContentScanner.java similarity index 98% rename from app/src/main/java/foundation/e/drive/contentScanner/LocalContentScanner.java rename to app/src/main/java/foundation/e/drive/periodicScan/contentScanner/LocalContentScanner.java index 6e62d02d61f0a0a7f8f44fb62921f8c4d4552658..83a656fa6e5380c4bf0743986f439c1ff145d30f 100644 --- a/app/src/main/java/foundation/e/drive/contentScanner/LocalContentScanner.java +++ b/app/src/main/java/foundation/e/drive/periodicScan/contentScanner/LocalContentScanner.java @@ -5,7 +5,7 @@ * which accompanies this distribution, and is available at * http://www.gnu.org/licenses/gpl.html */ -package foundation.e.drive.contentScanner; +package foundation.e.drive.periodicScan.contentScanner; import static foundation.e.drive.models.SyncedFileStateKt.DO_NOT_SCAN; import static foundation.e.drive.models.SyncedFileStateKt.SCAN_ON_CLOUD; diff --git a/app/src/main/java/foundation/e/drive/contentScanner/LocalFileLister.java b/app/src/main/java/foundation/e/drive/periodicScan/contentScanner/LocalFileLister.java similarity index 98% rename from app/src/main/java/foundation/e/drive/contentScanner/LocalFileLister.java rename to app/src/main/java/foundation/e/drive/periodicScan/contentScanner/LocalFileLister.java index 80a9fa8e3796d08d29c3e139540b0c66b72b40f1..11d2c58d891490445fbf7b8caf58c80caad28899 100644 --- a/app/src/main/java/foundation/e/drive/contentScanner/LocalFileLister.java +++ b/app/src/main/java/foundation/e/drive/periodicScan/contentScanner/LocalFileLister.java @@ -5,7 +5,7 @@ * which accompanies this distribution, and is available at * http://www.gnu.org/licenses/gpl.html */ -package foundation.e.drive.contentScanner; +package foundation.e.drive.periodicScan.contentScanner; import android.content.Context; diff --git a/app/src/main/java/foundation/e/drive/contentScanner/RemoteContentScanner.java b/app/src/main/java/foundation/e/drive/periodicScan/contentScanner/RemoteContentScanner.java similarity index 97% rename from app/src/main/java/foundation/e/drive/contentScanner/RemoteContentScanner.java rename to app/src/main/java/foundation/e/drive/periodicScan/contentScanner/RemoteContentScanner.java index 3848eb8931b81fdc3a4bbda4b09a3f0616cc5151..d4f22e7440504fc7a7b110a9d9a099ce11f276ef 100644 --- a/app/src/main/java/foundation/e/drive/contentScanner/RemoteContentScanner.java +++ b/app/src/main/java/foundation/e/drive/periodicScan/contentScanner/RemoteContentScanner.java @@ -5,13 +5,13 @@ * which accompanies this distribution, and is available at * http://www.gnu.org/licenses/gpl.html */ -package foundation.e.drive.contentScanner; +package foundation.e.drive.periodicScan.contentScanner; import static foundation.e.drive.models.SyncRequest.Type.DISABLE_SYNCING; import static foundation.e.drive.models.SyncedFileStateKt.DO_NOT_SCAN; import static foundation.e.drive.models.SyncedFileStateKt.SCAN_ON_CLOUD; import static foundation.e.drive.models.SyncedFileStateKt.SCAN_ON_DEVICE; -import static foundation.e.drive.contentScanner.FileDiffUtils.getActionForFileDiff; +import static foundation.e.drive.periodicScan.contentScanner.FileDiffUtils.getActionForFileDiff; import android.content.Context; diff --git a/app/src/main/java/foundation/e/drive/contentScanner/RemoteFileLister.java b/app/src/main/java/foundation/e/drive/periodicScan/contentScanner/RemoteFileLister.java similarity index 98% rename from app/src/main/java/foundation/e/drive/contentScanner/RemoteFileLister.java rename to app/src/main/java/foundation/e/drive/periodicScan/contentScanner/RemoteFileLister.java index 8c54a6492588d6c419edfe4583dbbf5752cf4cb7..de99d1c6a316d094c648c5ba79e2c1a7dec94d77 100644 --- a/app/src/main/java/foundation/e/drive/contentScanner/RemoteFileLister.java +++ b/app/src/main/java/foundation/e/drive/periodicScan/contentScanner/RemoteFileLister.java @@ -5,7 +5,7 @@ * which accompanies this distribution, and is available at * http://www.gnu.org/licenses/gpl.html */ -package foundation.e.drive.contentScanner; +package foundation.e.drive.periodicScan.contentScanner; import android.content.Context; diff --git a/app/src/main/java/foundation/e/drive/receivers/DebugCmdReceiver.java b/app/src/main/java/foundation/e/drive/receivers/DebugCmdReceiver.java index b5abb15948e84790e1fac10c2db5d3eadd44fd69..b4b6cf2956e2b002cc6f0c56f96b7809ab332c88 100644 --- a/app/src/main/java/foundation/e/drive/receivers/DebugCmdReceiver.java +++ b/app/src/main/java/foundation/e/drive/receivers/DebugCmdReceiver.java @@ -7,16 +7,21 @@ */ package foundation.e.drive.receivers; +import static foundation.e.drive.work.WorkRequestFactory.WorkType.ONE_TIME_FORCED_FULL_SCAN; + import android.content.BroadcastReceiver; import android.content.Context; import android.content.Intent; -import android.util.Log; import androidx.annotation.NonNull; +import androidx.work.ExistingWorkPolicy; +import androidx.work.OneTimeWorkRequest; +import androidx.work.WorkManager; import foundation.e.drive.database.DbHelper; -import foundation.e.drive.services.ObserverService; +import foundation.e.drive.periodicScan.FullScanWorker; import foundation.e.drive.utils.ReleaseTree; +import foundation.e.drive.work.WorkRequestFactory; import timber.log.Timber; /** @@ -35,7 +40,11 @@ public class DebugCmdReceiver extends BroadcastReceiver { switch (intent.getAction()) { case ACTION_FORCE_SYNC: Timber.d("Force Sync intent received"); - context.startService(new Intent(ACTION_FORCE_SYNC, null, context, ObserverService.class)); + final WorkManager workManager = WorkManager.getInstance(context); + final OneTimeWorkRequest fullScanWorkRequest = WorkRequestFactory.getOneTimeWorkRequest(ONE_TIME_FORCED_FULL_SCAN, null); + workManager.enqueueUniqueWork(FullScanWorker.UNIQUE_WORK_NAME, + ExistingWorkPolicy.KEEP, + fullScanWorkRequest); break; case ACTION_DUMP_DATABASE: Timber.d("Dump database intent received"); diff --git a/app/src/main/java/foundation/e/drive/services/ObserverService.java b/app/src/main/java/foundation/e/drive/services/ObserverService.java deleted file mode 100644 index fe8faf1adaf7d6ab3106e0828f16b3d732d5eb0b..0000000000000000000000000000000000000000 --- a/app/src/main/java/foundation/e/drive/services/ObserverService.java +++ /dev/null @@ -1,365 +0,0 @@ -/* - * Copyright © CLEUS SAS 2018-2019. - * 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.services; - -import static foundation.e.drive.utils.AppConstants.SETUP_COMPLETED; - -import android.accounts.Account; -import android.accounts.AccountManager; -import android.app.Service; -import android.content.Context; -import android.content.Intent; -import android.content.SharedPreferences; -import android.os.Handler; -import android.os.HandlerThread; -import android.os.IBinder; - -import androidx.annotation.NonNull; -import androidx.annotation.Nullable; - -import com.owncloud.android.lib.common.OwnCloudClient; -import com.owncloud.android.lib.common.operations.OnRemoteOperationListener; -import com.owncloud.android.lib.common.operations.RemoteOperation; -import com.owncloud.android.lib.common.operations.RemoteOperationResult; -import com.owncloud.android.lib.resources.files.model.RemoteFile; - -import java.io.File; -import java.util.ArrayList; -import java.util.HashMap; -import java.util.List; - -import foundation.e.drive.R; -import foundation.e.drive.contentScanner.LocalContentScanner; -import foundation.e.drive.contentScanner.LocalFileLister; -import foundation.e.drive.contentScanner.RemoteContentScanner; -import foundation.e.drive.database.DbHelper; -import foundation.e.drive.fileFilters.CrashlogsFileFilter; -import foundation.e.drive.fileFilters.OnlyFileFilter; -import foundation.e.drive.models.SyncRequest; -import foundation.e.drive.models.SyncedFileState; -import foundation.e.drive.models.SyncedFolder; -import foundation.e.drive.operations.ListFileRemoteOperation; -import foundation.e.drive.receivers.DebugCmdReceiver; -import foundation.e.drive.synchronization.SyncRequestCollector; -import foundation.e.drive.synchronization.SyncProxy; -import foundation.e.drive.utils.AppConstants; -import foundation.e.drive.utils.CommonUtils; -import foundation.e.drive.utils.DavClientProvider; -import foundation.e.drive.utils.ServiceExceptionHandler; -import timber.log.Timber; - -/** - * @author Vincent Bourgmayer - * @author Nicolas Gelot - * @author Jonathan Klee - * This service look for remote or looale file to synchronize - */ -public class ObserverService extends Service implements OnRemoteOperationListener{ - private final static int INTERSYNC_MINIMUM_DELAY = 900000; // min delay execution two sync in ms. - - private List mSyncedFolders; //List of synced folder - private Account mAccount; - private HashMap syncRequests; //integer is SyncedFileState id; Parcelable is the operation - - private final SyncRequestCollector syncManager = SyncProxy.INSTANCE; - - private Handler handler; - private HandlerThread handlerThread; - // protected to avoid SyntheticAccessor - protected boolean forcedSync = false; - - /* Lifecycle Methods */ - @Override - public void onDestroy(){ - Timber.v("onDestroy()"); - if (handlerThread != null) handlerThread.quitSafely(); - mSyncedFolders = null; - super.onDestroy(); - } - - @Override - public void onCreate() { - super.onCreate(); - Timber.tag(ObserverService.class.getSimpleName()); - } - - @Override - - public int onStartCommand(@Nullable Intent intent, int flags, int startId) { - Timber.v("onStartCommand(%s)", startId); - - CommonUtils.setServiceUnCaughtExceptionHandler(this); - - final SharedPreferences prefs = this.getSharedPreferences(AppConstants.SHARED_PREFERENCE_NAME, Context.MODE_PRIVATE); - final String accountName = prefs.getString(AccountManager.KEY_ACCOUNT_NAME, ""); - final String accountType = getApplicationContext().getString(R.string.eelo_account_type); - this.mAccount = CommonUtils.getAccount(accountName, accountType, AccountManager.get(this)); - - forcedSync = intent != null && DebugCmdReceiver.ACTION_FORCE_SYNC.equals(intent.getAction()); - - this.syncRequests = new HashMap<>(); - - if (!checkStartCondition(prefs, forcedSync)) { - syncManager.startListeningFiles(getApplication()); - stopSelf(); - return START_NOT_STICKY; - } - - begin(); - return START_STICKY; - } - - /** - * This method check that all condition are met - * to start ObserverService: - * - a valid account as been registered - * - Synchronization of media and/or settings is enabled - * - Initialization task has been done properly - * - Service isn't already running - * - Check minimum delay since last call if not forced sync - * - Check that network is available depending of metered network allowed or not - * - * It also display log depending of the failure and send intent for initialization if this has - * not been done - * @return false if at least one condition is false - */ - // protected to avoid SyntheticAccessor - protected boolean checkStartCondition(@NonNull final SharedPreferences prefs, final boolean forcedSync) { - Timber.v("checkStartCondition()"); - - if (mAccount == null) { - Timber.d("No account registered"); - return false; - } - - if (!CommonUtils.isMediaSyncEnabled(mAccount) && !CommonUtils.isSettingsSyncEnabled(mAccount)) { - Timber.d("Synchronization has been disabled in account's settings"); - return false; - } - - if (!prefs.getBoolean(SETUP_COMPLETED, false)) { - Timber.d("setup hasn't been done"); - return false; - } - - // Check minimum delay since last call & not forced sync - /*@todo is it really usefull to check time beetween to start as it is started by WorkManager? - it matters only if we want to consider forced sync */ - final long lastSyncTime = prefs.getLong(AppConstants.KEY_LAST_SYNC_TIME, 0L); - final long currentTime = System.currentTimeMillis(); - if (!forcedSync && (currentTime - lastSyncTime ) < INTERSYNC_MINIMUM_DELAY ) { - Timber.d("Delay between now and last call is too short"); - return false; - } - - final boolean meteredNetworkAllowed = CommonUtils.isMeteredNetworkAllowed(mAccount); - //check for the case where intent has been launched by initializerService - if (!CommonUtils.haveNetworkConnection(this, meteredNetworkAllowed)) { - Timber.d("There is no allowed internet connexion."); - return false; - } - - - final boolean startAllowed = syncManager.onPeriodicScanStart(getApplication()); - Timber.d("starting periodic scan is allowed ? %s", startAllowed); - return startAllowed; - } - - /* Common methods */ - /** - * Start to bind this service to OperationManagerService or start scan if binding is already set. - * Method to factorise code that is called from different place - */ - // protected to avoid SyntheticAccessor - protected void begin(){ - clearCachedFile(); - deleteOldestCrashlogs(); - startScan(true); - } - - /** - * This method remove all the crash-logs file - * in external dir that are 10 days or more old. - */ - private void deleteOldestCrashlogs(){ - Timber.i("deleteOldestCrashLogs()"); - final File externalFilesDir = getExternalFilesDir(ServiceExceptionHandler.CRASH_LOG_FOLDER); - if (externalFilesDir == null) { - Timber.d("getExternalFilesDir() returned null. Preventing a NPE"); - return; - } - - final File[] fileToRemove = externalFilesDir.listFiles(new CrashlogsFileFilter()); - if (fileToRemove == null) { - Timber.d("getExternalFilesDir() returned null. Preventing a NPE"); - return; - } - - for (File file : fileToRemove) { - try { - file.delete(); - } catch (SecurityException exception) { - Timber.e(exception); - } - } - } - - /** - * Clear cached file unused: - */ - private void clearCachedFile(){ - Timber.i("clearCachedFile()"); - final File[] fileToRemove = this.getApplicationContext().getExternalCacheDir().listFiles(new OnlyFileFilter() ); - if (fileToRemove == null) return; - - for (File file : fileToRemove) { - try { - file.delete(); - } catch (SecurityException exception) { - Timber.e(exception); - } - } - } - - /** - * Start scanning job - * Load operation from DB - * @param remote if true looks for remote change. If false, look for local change. - **/ - - private void startScan(boolean remote) { - Timber.i("startScan(%s)", remote); - this.mSyncedFolders = loadSyncedFolders(); - - if (mSyncedFolders.isEmpty()) { - Timber.d("List of synced folders is empty"); - this.stopSelf(); - return; - } - - if (remote) { - final OwnCloudClient client = DavClientProvider.getInstance().getClientInstance(mAccount, getApplicationContext()); - if (client == null) { - Timber.d("OwnCloudClient is null"); - return; - } - - try { - handlerThread = new HandlerThread("syncService_onResponse"); - handlerThread.start(); - handler = new Handler(handlerThread.getLooper()); - final ListFileRemoteOperation loadOperation = new ListFileRemoteOperation(this.mSyncedFolders, this); - loadOperation.execute(client, this, handler); - } catch (IllegalArgumentException exception) { - Timber.e(exception); - } - } else { - scanLocalFiles(); - } - } - - /** - * Get list of synced folder depending of if media and setting sync are enabled. - * @return - */ - private List loadSyncedFolders(){ - final boolean mediaSyncEnabled = CommonUtils.isMediaSyncEnabled(mAccount); - final boolean settingsSyncedEnabled = CommonUtils.isSettingsSyncEnabled(mAccount); - - if (mediaSyncEnabled && settingsSyncedEnabled) { - return DbHelper.getAllSyncedFolders(this); - } else if (mediaSyncEnabled) { - return DbHelper.getSyncedFolderList(this, true); - } else if (settingsSyncedEnabled) { - return DbHelper.getSyncedFolderList(this, false); - } else { - return new ArrayList<>(); - } - } - - /** - * Handle end of remote Operation - * @param operation The RemoteOperation which ends and call this methods - * @param result The result of the remote Operation - */ - @Override - public void onRemoteOperationFinish(@Nullable RemoteOperation operation, @NonNull RemoteOperationResult result) { - Timber.d("onRemoteOperationFinish()"); - if (!(operation instanceof ListFileRemoteOperation)) return; - - if (!result.isSuccess()) { - Timber.d("ListRemoteFileOperation failed. Http code: %s", result.getHttpCode()); - } - - final List remoteFiles = ((RemoteOperationResult>)result).getResultData(); - - if (remoteFiles != null) { - final ListFileRemoteOperation listFileOperation = (ListFileRemoteOperation) operation; - final List syncedFoldersId = listFileOperation.getSyncedFoldersId(); - - final List syncedFileStates = DbHelper.getSyncedFileStatesByFolders(this, - syncedFoldersId); - - if (!remoteFiles.isEmpty() || !syncedFileStates.isEmpty()) { - final RemoteContentScanner scanner = new RemoteContentScanner(getApplicationContext(), mSyncedFolders); - syncRequests.putAll(scanner.scanContent(remoteFiles, syncedFileStates)); - } - } - - startScan(false); - - if (!syncRequests.isEmpty()) { - Timber.d("syncRequests contains %s", syncRequests.size()); - syncManager.queueSyncRequests(syncRequests.values(), getApplicationContext()); - syncManager.startSynchronization(getApplicationContext()); - } else { - Timber.i("There is no file to sync."); - getSharedPreferences(AppConstants.SHARED_PREFERENCE_NAME, Context.MODE_PRIVATE) - .edit() - .putLong(AppConstants.KEY_LAST_SYNC_TIME, System.currentTimeMillis()) - .apply(); - syncManager.startListeningFiles(getApplication()); - } - - this.stopSelf(); - } - - /** - * Prepare the list of files and SyncedFileState for synchronisation - */ - private void scanLocalFiles(){ - Timber.i("scanLocalFiles()"); - - final LocalFileLister fileLister = new LocalFileLister(mSyncedFolders); - - final boolean isContentToScan = fileLister.listContentToScan(getApplicationContext()); - - if (!isContentToScan) { - return; - } - - final List fileList = fileLister.getContentToScan(); - final List folderIdList = fileLister.getSyncedFoldersId(); - - final List syncedFileStates = DbHelper.getSyncedFileStatesByFolders(this, - folderIdList); - - if (!syncedFileStates.isEmpty() || !fileList.isEmpty() ) { - final LocalContentScanner scanner= new LocalContentScanner(getApplicationContext(), mSyncedFolders); - syncRequests.putAll(scanner.scanContent(fileList, syncedFileStates)); - } - } - - @Nullable - @Override - public IBinder onBind(@Nullable Intent intent) { - throw new UnsupportedOperationException(); - } -} \ No newline at end of file diff --git a/app/src/main/java/foundation/e/drive/utils/AppConstants.kt b/app/src/main/java/foundation/e/drive/utils/AppConstants.kt index 49d2b5d636b20375ac27eb63fd22fd116083d35c..f3721e603596a662ec46eb6bc5444160002f82bc 100644 --- a/app/src/main/java/foundation/e/drive/utils/AppConstants.kt +++ b/app/src/main/java/foundation/e/drive/utils/AppConstants.kt @@ -22,11 +22,11 @@ object AppConstants { const val SETTINGS_SYNC_PROVIDER_AUTHORITY = "foundation.e.drive.providers.SettingsSyncProvider" const val METERED_NETWORK_ALLOWED_AUTHORITY = "foundation.e.drive.providers.MeteredConnectionAllowedProvider" - const val SETUP_COMPLETED = "initService_has_run" + const val SETUP_COMPLETED = "setup_completed" + const val KEY_LAST_SCAN_TIME = "lastScanTimestamp" const val INITIAL_FOLDER_NUMBER = "initial_folder_number" const val APPLICATIONS_LIST_FILE_NAME = "packages_list.csv" const val SHARED_PREFERENCE_NAME = "preferences" - const val KEY_LAST_SYNC_TIME = "lastSyncTimestamp" const val ACCOUNT_DATA_NAME = "display_name" const val ACCOUNT_DATA_USED_QUOTA_KEY = "used_quota" const val ACCOUNT_DATA_TOTAL_QUOTA_KEY = "total_quota" @@ -37,7 +37,7 @@ object AppConstants { const val ACCOUNT_USER_ID_KEY = "USERID" const val notificationChannelID = "foundation.e.drive" const val WORK_GENERIC_TAG = "eDrive" - const val WORK_INITIALIZATION_TAG = "eDrive-init" + const val WORK_SETUP_TAG = "eDrive-init" const val CORRUPTED_TIMESTAMP_IN_SECOND = 4294967295L @JvmField 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 1d6ec907f01c140beead7dce0c18e6a17865cbf0..a9fd0c00e519b24d65f0f3e41dc179d78e0d7ea4 100644 --- a/app/src/main/java/foundation/e/drive/utils/CommonUtils.java +++ b/app/src/main/java/foundation/e/drive/utils/CommonUtils.java @@ -29,12 +29,9 @@ import java.lang.reflect.Method; import java.text.CharacterIterator; import java.text.StringCharacterIterator; import java.util.Locale; -import java.util.ArrayList; -import java.util.List; import foundation.e.drive.R; -import foundation.e.drive.models.SyncedFolder; -import foundation.e.drive.work.AccountUserInfoWorker; +import foundation.e.drive.account.AccountUserInfoWorker; import foundation.e.drive.work.WorkRequestFactory; import timber.log.Timber; @@ -45,7 +42,6 @@ import static foundation.e.drive.utils.AppConstants.SETTINGS_SYNC_PROVIDER_AUTHO import androidx.annotation.NonNull; import androidx.annotation.Nullable; import androidx.work.ExistingPeriodicWorkPolicy; -import androidx.work.OneTimeWorkRequest; import androidx.work.PeriodicWorkRequest; import androidx.work.WorkManager; diff --git a/app/src/main/java/foundation/e/drive/work/FirstStartWorker.java b/app/src/main/java/foundation/e/drive/work/FirstStartWorker.java deleted file mode 100644 index 0ebce10f2fa3c48ccfd0f327ff9280b3c2cb94a9..0000000000000000000000000000000000000000 --- a/app/src/main/java/foundation/e/drive/work/FirstStartWorker.java +++ /dev/null @@ -1,79 +0,0 @@ -/* - * 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.work; - -import static foundation.e.drive.utils.AppConstants.INITIAL_FOLDER_NUMBER; -import static foundation.e.drive.work.WorkRequestFactory.WorkType.ONE_TIME_APP_LIST; -import static foundation.e.drive.work.WorkRequestFactory.WorkType.PERIODIC_SCAN; - -import android.content.Context; -import android.content.Intent; - -import androidx.annotation.NonNull; -import androidx.work.ExistingPeriodicWorkPolicy; -import androidx.work.WorkManager; -import androidx.work.Worker; -import androidx.work.WorkerParameters; - -import foundation.e.drive.EdriveApplication; -import foundation.e.drive.utils.AppConstants; -import timber.log.Timber; - -/** - * This class start eDrive work after initialization. - * It contains job to start FileObserver, periodic fullScan and Synchronization service - * for the first time - * @author Vincent Bourgmayer - */ -public class FirstStartWorker extends Worker { - public FirstStartWorker(@NonNull Context context, @NonNull WorkerParameters workerParams) { - super(context, workerParams); - Timber.tag(FirstStartWorker.class.getSimpleName()); - } - - @NonNull - @Override - public Result doWork() { - Timber.v("FirstStartWorker.doWork()"); - try { - final Context appContext = getApplicationContext(); - - enqueueAppListGenerationWorkRequest(appContext); - - appContext.getSharedPreferences(AppConstants.SHARED_PREFERENCE_NAME, - Context.MODE_PRIVATE) - .edit() - .putBoolean(AppConstants.SETUP_COMPLETED, true) - .putInt(INITIAL_FOLDER_NUMBER, 9) - .apply(); - - enqueuePeriodicFileScanWorkRequest(appContext); - - getApplicationContext().startService(new Intent(getApplicationContext(), foundation.e.drive.services.ObserverService.class)); - - return Result.success(); - } catch (Exception exception) { - Timber.e(exception); - } - return Result.retry(); - } - - private void enqueueAppListGenerationWorkRequest(@NonNull final Context context) { - final WorkManager workManager = WorkManager.getInstance(context); - workManager.enqueue(WorkRequestFactory.getOneTimeWorkRequest(ONE_TIME_APP_LIST, null)); - } - - private void enqueuePeriodicFileScanWorkRequest(@NonNull final Context context) { - final WorkManager workManager = WorkManager.getInstance(context); - - workManager.enqueueUniquePeriodicWork(PeriodicWorker.UNIQUE_WORK_NAME, - ExistingPeriodicWorkPolicy.KEEP, - WorkRequestFactory.getPeriodicWorkRequest(PERIODIC_SCAN)); - } -} \ 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 deleted file mode 100644 index 156bcb7350dc3793cbf5ad3b176113df737c65dc..0000000000000000000000000000000000000000 --- a/app/src/main/java/foundation/e/drive/work/FullScanWorker.java +++ /dev/null @@ -1,68 +0,0 @@ -/* - * Copyright © ECORP SAS 2022. - * All rights reserved. This program and the accompanying materials - * are made available under the terms of the GNU Public License v3.0 - * which accompanies this distribution, and is available at - * http://www.gnu.org/licenses/gpl.html - */ - -package foundation.e.drive.work; - -import android.accounts.Account; -import android.accounts.AccountManager; -import android.content.Context; -import android.content.Intent; -import android.content.SharedPreferences; - -import androidx.annotation.NonNull; -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; -import timber.log.Timber; - -/** - * 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 { - public final static String UNIQUE_WORK_NAME = "FullScan"; - - public FullScanWorker(@NonNull Context context, @NonNull WorkerParameters workerParams) { - super(context, workerParams); - Timber.tag(FullScanWorker.class.getSimpleName()); - } - - @NonNull - @Override - public Result doWork() { - try { - Timber.v("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, ""); - - 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 { - Timber.d("Intent for ObserverService not send : account is null or \"settings sync\" & \"media sync\" settings are disabled"); - } - - return Result.success(); - } catch (Exception exception) { - Timber.e(exception); - return Result.retry(); - } - } -} \ No newline at end of file diff --git a/app/src/main/java/foundation/e/drive/work/WorkRequestFactory.java b/app/src/main/java/foundation/e/drive/work/WorkRequestFactory.java index 0bef2fd5cd524958f5861cea2d7f6e8c5a4fed01..324b5deffe149920cc984cd8b3a2f851be78f69e 100644 --- a/app/src/main/java/foundation/e/drive/work/WorkRequestFactory.java +++ b/app/src/main/java/foundation/e/drive/work/WorkRequestFactory.java @@ -8,20 +8,23 @@ package foundation.e.drive.work; -import static foundation.e.drive.work.RootFolderSetupWorker.DATA_KEY_ENABLE; -import static foundation.e.drive.work.RootFolderSetupWorker.DATA_KEY_ID; -import static foundation.e.drive.work.RootFolderSetupWorker.DATA_KEY_LAST_ETAG; -import static foundation.e.drive.work.RootFolderSetupWorker.DATA_KEY_LAST_MODIFIED; -import static foundation.e.drive.work.RootFolderSetupWorker.DATA_KEY_LIBELLE; -import static foundation.e.drive.work.RootFolderSetupWorker.DATA_KEY_LOCAL_PATH; -import static foundation.e.drive.work.RootFolderSetupWorker.DATA_KEY_MEDIATYPE; -import static foundation.e.drive.work.RootFolderSetupWorker.DATA_KEY_REMOTE_PATH; -import static foundation.e.drive.work.RootFolderSetupWorker.DATA_KEY_SCAN_LOCAL; -import static foundation.e.drive.work.RootFolderSetupWorker.DATA_KEY_SCAN_REMOTE; +import static androidx.work.BackoffPolicy.LINEAR; +import static java.util.concurrent.TimeUnit.MINUTES; +import static foundation.e.drive.account.setup.RootFolderSetupWorker.DATA_KEY_ENABLE; +import static foundation.e.drive.account.setup.RootFolderSetupWorker.DATA_KEY_ID; +import static foundation.e.drive.account.setup.RootFolderSetupWorker.DATA_KEY_LAST_ETAG; +import static foundation.e.drive.account.setup.RootFolderSetupWorker.DATA_KEY_LAST_MODIFIED; +import static foundation.e.drive.account.setup.RootFolderSetupWorker.DATA_KEY_LIBELLE; +import static foundation.e.drive.account.setup.RootFolderSetupWorker.DATA_KEY_LOCAL_PATH; +import static foundation.e.drive.account.setup.RootFolderSetupWorker.DATA_KEY_MEDIATYPE; +import static foundation.e.drive.account.setup.RootFolderSetupWorker.DATA_KEY_REMOTE_PATH; +import static foundation.e.drive.account.setup.RootFolderSetupWorker.DATA_KEY_SCAN_LOCAL; +import static foundation.e.drive.account.setup.RootFolderSetupWorker.DATA_KEY_SCAN_REMOTE; +import static foundation.e.drive.utils.AppConstants.WORK_GENERIC_TAG; +import static foundation.e.drive.utils.AppConstants.WORK_SETUP_TAG; import androidx.annotation.NonNull; import androidx.annotation.Nullable; -import androidx.work.BackoffPolicy; import androidx.work.Constraints; import androidx.work.Data; import androidx.work.NetworkType; @@ -29,22 +32,30 @@ import androidx.work.OneTimeWorkRequest; import androidx.work.PeriodicWorkRequest; import java.security.InvalidParameterException; -import java.util.concurrent.TimeUnit; +import foundation.e.drive.account.AccountUserInfoWorker; +import foundation.e.drive.account.setup.FinishSetupWorker; +import foundation.e.drive.account.setup.RootFolderSetupWorker; import foundation.e.drive.models.SyncedFolder; -import foundation.e.drive.utils.AppConstants; +import foundation.e.drive.periodicScan.FullScanWorker; +import foundation.e.drive.periodicScan.ListAppsWorker; +import foundation.e.drive.periodicScan.PeriodicScanWorker; public class WorkRequestFactory { public enum WorkType { PERIODIC_USER_INFO, PERIODIC_SCAN, ONE_TIME_FULL_SCAN, + ONE_TIME_FORCED_FULL_SCAN, ONE_TIME_APP_LIST, ONE_TIME_USER_INFO, - CREATE_REMOTE_DIR, - FIRST_START + ONE_TIME_ROOT_FOLDER_SETUP, + ONE_TIME_FINISH_SETUP } + private final static int PERIODIC_WORK_REPEAT_INTERVAL = 30; + private final static int PERIODIC_SCAN_FLEX_TIME = 5; + /** * Build an instance of PeriodicWorkRequest depending of the work type specified * @param type WorkType. Should be FULL_SCAN or PERIODIC_USER_INFO or PERIODIC_APP_LIST @@ -64,7 +75,7 @@ public class WorkRequestFactory { } /** - * Create a PeridocWorkRequest instance for + * Create a PeriodicWorkRequest instance for * a Full scan with constraints on network (should * be unmetered) and battery (shouldn't be low) * @return instance of PeriodicWorkRequest @@ -73,11 +84,14 @@ public class WorkRequestFactory { private static PeriodicWorkRequest createPeriodicScanWorkRequest() { final Constraints constraints = createUnmeteredNetworkAndHighBatteryConstraints(); - return new PeriodicWorkRequest.Builder(PeriodicWorker.class, - 26, TimeUnit.MINUTES, 5, TimeUnit.MINUTES) - .setConstraints(constraints) - .addTag(AppConstants.WORK_GENERIC_TAG) - .build(); + final PeriodicWorkRequest.Builder workRequestBuilder = new PeriodicWorkRequest.Builder( + PeriodicScanWorker.class, + PERIODIC_WORK_REPEAT_INTERVAL, MINUTES, + PERIODIC_SCAN_FLEX_TIME, MINUTES); + + return workRequestBuilder.setConstraints(constraints) + .addTag(WORK_GENERIC_TAG) + .build(); } /** @@ -90,11 +104,12 @@ public class WorkRequestFactory { .setRequiredNetworkType(NetworkType.CONNECTED) .build(); - return new PeriodicWorkRequest.Builder(AccountUserInfoWorker.class, - 30, TimeUnit.MINUTES) - .addTag(AppConstants.WORK_GENERIC_TAG) - .setConstraints(constraints) - .build(); + final PeriodicWorkRequest.Builder workRequestBuilder = new PeriodicWorkRequest.Builder(AccountUserInfoWorker.class, + PERIODIC_WORK_REPEAT_INTERVAL, MINUTES); + + return workRequestBuilder.addTag(WORK_GENERIC_TAG) + .setConstraints(constraints) + .build(); } /** @@ -108,16 +123,18 @@ public class WorkRequestFactory { public static OneTimeWorkRequest getOneTimeWorkRequest(@NonNull WorkType type, @Nullable SyncedFolder syncedFolder) { switch (type) { case ONE_TIME_APP_LIST: - return createOneTimeAppListGenerationWorkRequest(); + return createAppListGenerationWorkRequest(); case ONE_TIME_FULL_SCAN: - return createOneTimeFullScanWorkRequest(); + return createFullScanWorkRequest(false); + case ONE_TIME_FORCED_FULL_SCAN: + return createFullScanWorkRequest(true); case ONE_TIME_USER_INFO: - return createOneTimeGetUserInfoWorkRequest(); - case FIRST_START: - return createOneTimeFirstStartWorkRequest(); - case CREATE_REMOTE_DIR: + return createGetUserInfoWorkRequest(); + case ONE_TIME_FINISH_SETUP: + return createFinishSetupWorkRequest(); + case ONE_TIME_ROOT_FOLDER_SETUP: if (syncedFolder == null) throw new NullPointerException("Synced folder is null"); - return createOneTimeCreateRemoteFolderWorkRequest(syncedFolder); + return createRootFolderSetupWorkRequest(syncedFolder); default: throw new InvalidParameterException("Unsupported Work Type: " + type); } @@ -128,11 +145,11 @@ public class WorkRequestFactory { * @return the workRequest */ @NonNull - private static OneTimeWorkRequest createOneTimeAppListGenerationWorkRequest() { + private static OneTimeWorkRequest createAppListGenerationWorkRequest() { final OneTimeWorkRequest.Builder builder = new OneTimeWorkRequest.Builder(ListAppsWorker.class); - return builder.setBackoffCriteria(BackoffPolicy.LINEAR, 2, TimeUnit.MINUTES) - .addTag(AppConstants.WORK_GENERIC_TAG) + return builder.setBackoffCriteria(LINEAR, 2, MINUTES) + .addTag(WORK_GENERIC_TAG) .build(); } @@ -143,14 +160,19 @@ public class WorkRequestFactory { * @return instance of OneTimeWorkRequest */ @NonNull - private static OneTimeWorkRequest createOneTimeFullScanWorkRequest() { + private static OneTimeWorkRequest createFullScanWorkRequest(boolean forced) { final Constraints constraints = createUnmeteredNetworkAndHighBatteryConstraints(); final OneTimeWorkRequest.Builder builder = new OneTimeWorkRequest.Builder(FullScanWorker.class); - return builder.setBackoffCriteria(BackoffPolicy.LINEAR, 2, TimeUnit.MINUTES) + final Data data = new Data.Builder() + .putBoolean(FullScanWorker.ACTION_FORCED_SYNC_KEY, forced) + .build(); + + return builder.setBackoffCriteria(LINEAR, 2, MINUTES) .setConstraints(constraints) - .addTag(AppConstants.WORK_GENERIC_TAG) + .setInputData(data) + .addTag(WORK_GENERIC_TAG) .build(); } @@ -160,14 +182,14 @@ public class WorkRequestFactory { * @return instance of OneTimeWorkRequest */ @NonNull - private static OneTimeWorkRequest createOneTimeGetUserInfoWorkRequest() { + private static OneTimeWorkRequest createGetUserInfoWorkRequest() { final Constraints constraints = createUnmeteredNetworkAndHighBatteryConstraints(); final OneTimeWorkRequest.Builder builder = new OneTimeWorkRequest.Builder(AccountUserInfoWorker.class); - return builder.setBackoffCriteria(BackoffPolicy.LINEAR, 2, TimeUnit.MINUTES) - .addTag(AppConstants.WORK_GENERIC_TAG) - .addTag(AppConstants.WORK_INITIALIZATION_TAG) + return builder.setBackoffCriteria(LINEAR, 2, MINUTES) + .addTag(WORK_GENERIC_TAG) + .addTag(WORK_SETUP_TAG) .setConstraints(constraints) .build(); } @@ -179,30 +201,29 @@ public class WorkRequestFactory { * @return Instance OneTimeWorkRequest */ @NonNull - private static OneTimeWorkRequest createOneTimeCreateRemoteFolderWorkRequest(@NonNull SyncedFolder syncedFolder) { + private static OneTimeWorkRequest createRootFolderSetupWorkRequest(@NonNull SyncedFolder syncedFolder) { final Constraints constraints = createUnmeteredNetworkAndHighBatteryConstraints(); return new OneTimeWorkRequest.Builder( RootFolderSetupWorker.class) - .setBackoffCriteria(BackoffPolicy.LINEAR, 2, TimeUnit.MINUTES) + .setBackoffCriteria(LINEAR, 2, MINUTES) .setInputData(createDataFromSyncedFolder(syncedFolder)) - .addTag(AppConstants.WORK_GENERIC_TAG) - .addTag(AppConstants.WORK_INITIALIZATION_TAG) + .addTag(WORK_GENERIC_TAG) + .addTag(WORK_SETUP_TAG) .setConstraints(constraints) .build(); } /** - * Create a OneTime WorkRequest which start eDrive - * after initialization + * Create a OneTime WorkRequest which finish setup process * @return Instance of OneTimeWorkRequest */ @NonNull - private static OneTimeWorkRequest createOneTimeFirstStartWorkRequest() { - return new OneTimeWorkRequest.Builder(FirstStartWorker.class) - .setBackoffCriteria(BackoffPolicy.LINEAR, 2, TimeUnit.MINUTES) - .addTag(AppConstants.WORK_GENERIC_TAG) - .addTag(AppConstants.WORK_INITIALIZATION_TAG) + private static OneTimeWorkRequest createFinishSetupWorkRequest() { + return new OneTimeWorkRequest.Builder(FinishSetupWorker.class) + .setBackoffCriteria(LINEAR, 2, MINUTES) + .addTag(WORK_GENERIC_TAG) + .addTag(WORK_SETUP_TAG) .build(); } diff --git a/app/src/test/java/foundation/e/drive/contentScanner/FileDiffUtilsTest.kt b/app/src/test/java/foundation/e/drive/periodicScan/contentScanner/FileDiffUtilsTest.kt similarity index 99% rename from app/src/test/java/foundation/e/drive/contentScanner/FileDiffUtilsTest.kt rename to app/src/test/java/foundation/e/drive/periodicScan/contentScanner/FileDiffUtilsTest.kt index 8bae8a20bdb1d840fe044c581c88a25a2c76446a..f078df1d2bc0eef1bbd7e5df11527f216460ab4a 100644 --- a/app/src/test/java/foundation/e/drive/contentScanner/FileDiffUtilsTest.kt +++ b/app/src/test/java/foundation/e/drive/periodicScan/contentScanner/FileDiffUtilsTest.kt @@ -5,10 +5,11 @@ * which accompanies this distribution, and is available at * http://www.gnu.org/licenses/gpl.html */ -package foundation.e.drive.contentScanner +package foundation.e.drive.periodicScan.contentScanner import com.owncloud.android.lib.resources.files.model.RemoteFile import foundation.e.drive.models.SyncedFileState +import foundation.e.drive.periodicScan.contentScanner.FileDiffUtils import org.junit.Assert import org.junit.Test import org.mockito.Mockito diff --git a/app/src/test/java/foundation/e/drive/contentScanner/LocalContentScannerTest.java b/app/src/test/java/foundation/e/drive/periodicScan/contentScanner/LocalContentScannerTest.java similarity index 98% rename from app/src/test/java/foundation/e/drive/contentScanner/LocalContentScannerTest.java rename to app/src/test/java/foundation/e/drive/periodicScan/contentScanner/LocalContentScannerTest.java index e57efbf6383017b08e88d4e122eff293a0abab59..6468357ae399d682b4a208049dd3c3d3207966d3 100644 --- a/app/src/test/java/foundation/e/drive/contentScanner/LocalContentScannerTest.java +++ b/app/src/test/java/foundation/e/drive/periodicScan/contentScanner/LocalContentScannerTest.java @@ -6,7 +6,7 @@ * http://www.gnu.org/licenses/gpl.html */ -package foundation.e.drive.contentScanner; +package foundation.e.drive.periodicScan.contentScanner; import static org.mockito.Mockito.when; import static foundation.e.drive.models.SyncedFileStateKt.SCAN_EVERYWHERE; @@ -33,6 +33,7 @@ import java.util.List; import foundation.e.drive.models.SyncRequest; import foundation.e.drive.models.SyncedFileState; import foundation.e.drive.models.SyncedFolder; +import foundation.e.drive.periodicScan.contentScanner.LocalContentScanner; /** * @author vincent Bourgmayer diff --git a/app/src/test/java/foundation/e/drive/contentScanner/LocalFileListerTest.java b/app/src/test/java/foundation/e/drive/periodicScan/contentScanner/LocalFileListerTest.java similarity index 98% rename from app/src/test/java/foundation/e/drive/contentScanner/LocalFileListerTest.java rename to app/src/test/java/foundation/e/drive/periodicScan/contentScanner/LocalFileListerTest.java index 40e41d2ce12206ca56a59ea6ef965613e1c93482..38a6907050e3133747815328f1f07756e197e068 100644 --- a/app/src/test/java/foundation/e/drive/contentScanner/LocalFileListerTest.java +++ b/app/src/test/java/foundation/e/drive/periodicScan/contentScanner/LocalFileListerTest.java @@ -6,7 +6,7 @@ * http://www.gnu.org/licenses/gpl.html */ -package foundation.e.drive.contentScanner; +package foundation.e.drive.periodicScan.contentScanner; import static foundation.e.drive.models.SyncedFileStateKt.SCAN_EVERYWHERE; @@ -36,6 +36,8 @@ import foundation.e.drive.TestUtils; import foundation.e.drive.database.DbHelper; import foundation.e.drive.models.SyncedFileState; import foundation.e.drive.models.SyncedFolder; +import foundation.e.drive.periodicScan.contentScanner.FolderWrapper; +import foundation.e.drive.periodicScan.contentScanner.LocalFileLister; /** * @author vincent Bourgmayer diff --git a/app/src/test/java/foundation/e/drive/contentScanner/RemoteContentScannerTest.java b/app/src/test/java/foundation/e/drive/periodicScan/contentScanner/RemoteContentScannerTest.java similarity index 98% rename from app/src/test/java/foundation/e/drive/contentScanner/RemoteContentScannerTest.java rename to app/src/test/java/foundation/e/drive/periodicScan/contentScanner/RemoteContentScannerTest.java index 476d96166759c33677d5260be9506ebaae749674..a985db22d10da44ecaea06643c7da89e599e9365 100644 --- a/app/src/test/java/foundation/e/drive/contentScanner/RemoteContentScannerTest.java +++ b/app/src/test/java/foundation/e/drive/periodicScan/contentScanner/RemoteContentScannerTest.java @@ -5,7 +5,7 @@ * which accompanies this distribution, and is available at * http://www.gnu.org/licenses/gpl.html */ -package foundation.e.drive.contentScanner; +package foundation.e.drive.periodicScan.contentScanner; import static org.junit.Assert.assertEquals; @@ -36,6 +36,7 @@ import foundation.e.drive.database.DbHelper; import foundation.e.drive.models.SyncRequest; import foundation.e.drive.models.SyncedFileState; import foundation.e.drive.models.SyncedFolder; +import foundation.e.drive.periodicScan.contentScanner.RemoteContentScanner; /** * @author vincent Bourgmayer diff --git a/app/src/test/java/foundation/e/drive/contentScanner/RemoteFileListerTest.java b/app/src/test/java/foundation/e/drive/periodicScan/contentScanner/RemoteFileListerTest.java similarity index 99% rename from app/src/test/java/foundation/e/drive/contentScanner/RemoteFileListerTest.java rename to app/src/test/java/foundation/e/drive/periodicScan/contentScanner/RemoteFileListerTest.java index c421c8a72006aa8b044dc8eeb63c0605e3558198..2f4c9e7ad481f9f2d12afebdbad52a8122fe8404 100644 --- a/app/src/test/java/foundation/e/drive/contentScanner/RemoteFileListerTest.java +++ b/app/src/test/java/foundation/e/drive/periodicScan/contentScanner/RemoteFileListerTest.java @@ -6,7 +6,7 @@ * http://www.gnu.org/licenses/gpl.html */ -package foundation.e.drive.contentScanner; +package foundation.e.drive.periodicScan.contentScanner; import static org.mockito.Mockito.when; @@ -41,6 +41,7 @@ import foundation.e.drive.TestUtils; import foundation.e.drive.database.DbHelper; import foundation.e.drive.models.SyncedFileState; import foundation.e.drive.models.SyncedFolder; +import foundation.e.drive.periodicScan.contentScanner.RemoteFileLister; import foundation.e.drive.utils.DavClientProvider; /** diff --git a/app/src/test/java/foundation/e/drive/services/AbstractServiceIT.java b/app/src/test/java/foundation/e/drive/services/AbstractServiceIT.java deleted file mode 100644 index 998157602efeb7a6b112107d884f02aedf119506..0000000000000000000000000000000000000000 --- a/app/src/test/java/foundation/e/drive/services/AbstractServiceIT.java +++ /dev/null @@ -1,121 +0,0 @@ -package foundation.e.drive.services; - -import android.accounts.Account; -import android.accounts.AccountManager; -import android.app.Service; -import android.app.job.JobScheduler; -import android.content.ContentResolver; -import android.content.Context; -import android.content.SharedPreferences; -import android.net.ConnectivityManager; -import android.os.Build; - -import org.junit.BeforeClass; -import org.junit.runner.RunWith; -import org.robolectric.RobolectricTestRunner; -import org.robolectric.android.controller.ServiceController; -import org.robolectric.annotation.Config; -import org.robolectric.shadows.ShadowLog; - -import foundation.e.drive.TestUtils; -import foundation.e.drive.database.DbHelper; -import foundation.e.drive.utils.AppConstants; -import static foundation.e.drive.TestUtils.TEST_ACCOUNT_NAME; -import static foundation.e.drive.TestUtils.TEST_ACCOUNT_TYPE; -import static foundation.e.drive.utils.AppConstants.MEDIA_SYNC_PROVIDER_AUTHORITY; -import static foundation.e.drive.utils.AppConstants.SETTINGS_SYNC_PROVIDER_AUTHORITY; - -import com.nextcloud.common.NextcloudClient; - -@RunWith(RobolectricTestRunner.class) -@Config(sdk = Build.VERSION_CODES.O, manifest = Config.NONE) -public abstract class AbstractServiceIT { - - /** - * By making the below field static it is done once - * for all test class that extends this instead of - * one time by class. - */ - protected static Context context; - - protected T mService; - protected ServiceController mServiceController; - - protected AccountManager accountManager; - protected ContentResolver contentResolver; - protected SharedPreferences sharedPreferences; - protected ConnectivityManager connectivityManager; - protected JobScheduler jobScheduler; - protected DbHelper dbHelper; - protected NextcloudClient client; - protected int initial_folder_number=0; //number of folders to sync at initialization - protected long last_sync_time=0l; //Timestamp of the end of the last synchronisation - protected boolean init_done = true; //true if InitializerService did its job - protected boolean oms_running=false; //true if OperationManagerService is already running - - @BeforeClass - public static void beforeAll(){ - TestUtils.loadServerCredentials(); - ShadowLog.stream = System.out; //give access to log - } - - /** - * Create and register one validAccount - */ - protected void prepareValidAccount(){ - TestUtils.prepareValidAccount(accountManager); - } - - /** - * enable Media & settings sync - */ - protected void enableMediaAndSettingsSync(Account account){ //replace this by robolectric the contentResolver - contentResolver.setSyncAutomatically(account, MEDIA_SYNC_PROVIDER_AUTHORITY, true); - contentResolver.setSyncAutomatically(account, SETTINGS_SYNC_PROVIDER_AUTHORITY, true); - } - - /** - * disable Media & enable settings sync - */ - protected void disableMediaSync(Account account){ //replace this by robolectric the contentResolver - contentResolver.setSyncAutomatically(account, MEDIA_SYNC_PROVIDER_AUTHORITY, false); - contentResolver.setSyncAutomatically(account, SETTINGS_SYNC_PROVIDER_AUTHORITY, true); - } - - /** - * enable Media and disable settings sync - */ - protected void disableSettingsSync(Account account){ //replace this by robolectric the contentResolver - contentResolver.setSyncAutomatically(account, MEDIA_SYNC_PROVIDER_AUTHORITY, true); - contentResolver.setSyncAutomatically(account, SETTINGS_SYNC_PROVIDER_AUTHORITY, false); - } - - /** - * disable Media & settings sync - */ - protected void disableMediaAndSettingsSync(Account account){ //replace this by robolectric the contentResolver - contentResolver.setSyncAutomatically(account, MEDIA_SYNC_PROVIDER_AUTHORITY, false); - contentResolver.setSyncAutomatically(account, SETTINGS_SYNC_PROVIDER_AUTHORITY, false); - } - - /** - * Register sharedPreferences common to all services - * - OMS is working (bool) - * - Account name (String) - * - Account type (String) - * - initial folders number (int) - * - last sync time (long) - * - Initialization has been done (bool) - * - * Use default value of the instance field - * So update the field before to call this to store specific value - */ - protected void registerSharedPref(){ - sharedPreferences.edit().putBoolean( AppConstants.SETUP_COMPLETED, init_done) - .putString(AccountManager.KEY_ACCOUNT_NAME, TEST_ACCOUNT_NAME) - .putString(AccountManager.KEY_ACCOUNT_TYPE, TEST_ACCOUNT_TYPE) - .putInt(AppConstants.INITIAL_FOLDER_NUMBER, initial_folder_number) - .putLong(AppConstants.KEY_LAST_SYNC_TIME, last_sync_time) - .apply(); - } -}