From a71d3a25765fdcb7ebc65b6cb566459e548b7d45 Mon Sep 17 00:00:00 2001 From: vincent Bourgmayer Date: Wed, 15 Mar 2023 16:11:42 +0100 Subject: [PATCH 01/20] Refactor InitializerService: - exTract some code from onStartCommand into checkStartConditions - Remove useless 'OwnCloudClient' field - Add extra space around '+' in String concat - Change for loop in getInitialSyncedFolders(...) --- .../e/drive/services/InitializerService.java | 116 +++++++++--------- 1 file changed, 61 insertions(+), 55 deletions(-) diff --git a/app/src/main/java/foundation/e/drive/services/InitializerService.java b/app/src/main/java/foundation/e/drive/services/InitializerService.java index 7c0e0738..89babe44 100644 --- a/app/src/main/java/foundation/e/drive/services/InitializerService.java +++ b/app/src/main/java/foundation/e/drive/services/InitializerService.java @@ -1,6 +1,6 @@ /* * Copyright © CLEUS SAS 2018-2019. - * Copyright © ECORP SAS 2022. + * 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 @@ -19,8 +19,6 @@ import android.os.Build; import android.os.Environment; import android.os.IBinder; -import com.owncloud.android.lib.common.OwnCloudClient; - import java.util.ArrayList; import java.util.Arrays; import java.util.List; @@ -28,7 +26,6 @@ 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 timber.log.Timber; import static com.owncloud.android.lib.resources.files.FileUtils.PATH_SEPARATOR; @@ -45,7 +42,6 @@ import androidx.work.WorkManager; */ public class InitializerService extends Service { private List syncedFolders; - private OwnCloudClient cloudClient; private Account account; @Override @@ -60,55 +56,69 @@ public class InitializerService extends Service { CommonUtils.setServiceUnCaughtExceptionHandler(this); //Get account - SharedPreferences prefs = this.getSharedPreferences(AppConstants.SHARED_PREFERENCE_NAME, Context.MODE_PRIVATE); + final SharedPreferences prefs = this.getSharedPreferences(AppConstants.SHARED_PREFERENCE_NAME, Context.MODE_PRIVATE); - if (prefs.getBoolean(AppConstants.INITIALIZATION_HAS_BEEN_DONE, false)) { - Timber.i("Initializer has already been done"); - } else { - String accountName = prefs.getString(AccountManager.KEY_ACCOUNT_NAME, ""); - String accountType = prefs.getString(AccountManager.KEY_ACCOUNT_TYPE, ""); + String accountName = prefs.getString(AccountManager.KEY_ACCOUNT_NAME, ""); + String accountType = prefs.getString(AccountManager.KEY_ACCOUNT_TYPE, ""); - if (accountName.isEmpty() && accountType.isEmpty() && intent.getExtras() != null) { + if (accountName.isEmpty() && accountType.isEmpty() && intent.getExtras() != null) { - accountName = intent.getExtras().getString(AccountManager.KEY_ACCOUNT_NAME, ""); - accountType = intent.getExtras().getString(AccountManager.KEY_ACCOUNT_TYPE, ""); + 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(); - } + prefs.edit().putString(AccountManager.KEY_ACCOUNT_NAME, accountName) + .putString(AccountManager.KEY_ACCOUNT_TYPE, accountType) + .apply(); + } - if (accountName.isEmpty()) { - Timber.d("Account's name not found"); - stopSelf(); - } else { - this.account = CommonUtils.getAccount(accountName, accountType, AccountManager.get(this)); - if (this.account != null) { - this.cloudClient = DavClientProvider.getInstance().getClientInstance(account, getApplicationContext()); - start(); - } else { - Timber.i("Got account is invalid"); - stopSelf(); - } - } + 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 + */ + public boolean checkStartConditions(final SharedPreferences prefs, final String accountName, final String accountType) { + if (!prefs.getBoolean(AppConstants.INITIALIZATION_HAS_BEEN_DONE, false)) { + Timber.d("Initialization has already been done"); + return false; + } + + if (accountName.isEmpty()) { + Timber.d("No account Name available"); + return false; + } + + account = CommonUtils.getAccount(accountName, accountType, AccountManager.get(this)); + if (account == null) { + Timber.d("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 + */ public void start() { Timber.d("start()"); - if (cloudClient == null) { - stopSelf(); - return; - } CommonUtils.registerPeriodicUserInfoChecking(WorkManager.getInstance(this)); - final List syncCategories = new ArrayList<>(); + final List syncCategories = new ArrayList<>(); syncCategories.addAll(Arrays.asList(MEDIA_SYNCABLE_CATEGORIES)); syncCategories.addAll(Arrays.asList(SETTINGS_SYNCABLE_CATEGORIES)); getInitialSyncedFolders(syncCategories); + CommonUtils.registerInitializationWorkers(syncedFolders, WorkManager.getInstance(getApplicationContext()) ); } @@ -121,45 +131,43 @@ public class InitializerService extends Service { this.syncedFolders = new ArrayList<>(); - for(int i=-1, size = categories.size(); ++i < size;) { - final String DEVICE_SPECIFIC_PATH = PATH_SEPARATOR+"Devices"+PATH_SEPARATOR+ Build.BRAND+"_"+ Build.MODEL+"_" - + Build.SERIAL; - switch (categories.get(i)) { + for(final String category : categories) { + switch (category) { case "Images": - syncedFolders.add(new SyncedFolder(categories.get(i), getExternalFolder(Environment.DIRECTORY_DCIM), + syncedFolders.add(new SyncedFolder(category, getExternalFolder(Environment.DIRECTORY_DCIM), "/Photos/", true)); - syncedFolders.add(new SyncedFolder(categories.get(i), getExternalFolder(Environment.DIRECTORY_PICTURES), + syncedFolders.add(new SyncedFolder(category, getExternalFolder(Environment.DIRECTORY_PICTURES), "/Pictures/", true)); break; case "Movies": - syncedFolders.add(new SyncedFolder(categories.get(i), getExternalFolder(Environment.DIRECTORY_MOVIES), + syncedFolders.add(new SyncedFolder(category, getExternalFolder(Environment.DIRECTORY_MOVIES), "/Movies/", true)); break; case "Music": - syncedFolders.add(new SyncedFolder(categories.get(i), getExternalFolder(Environment.DIRECTORY_MUSIC), + syncedFolders.add(new SyncedFolder(category, getExternalFolder(Environment.DIRECTORY_MUSIC), "/Music/", true)); break; case "Ringtones": - syncedFolders.add(new SyncedFolder(categories.get(i), getExternalFolder(Environment.DIRECTORY_RINGTONES), + syncedFolders.add(new SyncedFolder(category, getExternalFolder(Environment.DIRECTORY_RINGTONES), "/Ringtones/", true)); break; case "Documents": - syncedFolders.add(new SyncedFolder(categories.get(i), getExternalFolder(Environment.DIRECTORY_DOCUMENTS), + syncedFolders.add(new SyncedFolder(category, getExternalFolder(Environment.DIRECTORY_DOCUMENTS), "/Documents/", true)); break; case "Podcasts": - syncedFolders.add(new SyncedFolder(categories.get(i), getExternalFolder(Environment.DIRECTORY_PODCASTS), + syncedFolders.add(new SyncedFolder(category, getExternalFolder(Environment.DIRECTORY_PODCASTS), "/Podcasts/", true)); break; case "Rom settings": - - String remoteFolderPath = DEVICE_SPECIFIC_PATH+"/rom_settings/"; - syncedFolders.add(new SyncedFolder(categories.get(i), "/data/system/users/0/", remoteFolderPath, true, false, true, false)); + final String remoteFolderPath = PATH_SEPARATOR + "Devices" + PATH_SEPARATOR + Build.BRAND + "_"+ Build.MODEL + "_" + + Build.SERIAL + "/rom_settings/"; + syncedFolders.add(new SyncedFolder(category, "/data/system/users/0/", remoteFolderPath, true, false, true, false)); try{ syncedFolders.add(new SyncedFolder( - categories.get(i), - getFilesDir().getCanonicalPath()+PATH_SEPARATOR, - remoteFolderPath+"app_list/", + category, + getFilesDir().getCanonicalPath() + PATH_SEPARATOR, + remoteFolderPath + "app_list/", true, false, CommonUtils.isSettingsSyncEnabled(account), @@ -179,8 +187,6 @@ public class InitializerService extends Service { @Override public void onDestroy() { super.onDestroy(); - this.account = null; - this.cloudClient = null; if (this.syncedFolders != null) this.syncedFolders.clear(); this.syncedFolders = null; } -- GitLab From 14bfa481e9394072141893a641dd74f47201db8a Mon Sep 17 00:00:00 2001 From: vincent Bourgmayer Date: Wed, 15 Mar 2023 16:36:44 +0100 Subject: [PATCH 02/20] Create recycle bin in InitializerService with path defined in AppConstant --- .../foundation/e/drive/services/InitializerService.java | 7 ++++++- .../main/java/foundation/e/drive/utils/AppConstants.java | 7 ++++++- 2 files changed, 12 insertions(+), 2 deletions(-) diff --git a/app/src/main/java/foundation/e/drive/services/InitializerService.java b/app/src/main/java/foundation/e/drive/services/InitializerService.java index 89babe44..cc99232c 100644 --- a/app/src/main/java/foundation/e/drive/services/InitializerService.java +++ b/app/src/main/java/foundation/e/drive/services/InitializerService.java @@ -19,6 +19,7 @@ import android.os.Build; import android.os.Environment; import android.os.IBinder; +import java.io.File; import java.util.ArrayList; import java.util.Arrays; import java.util.List; @@ -107,6 +108,7 @@ public class InitializerService extends Service { * Set up base component for eDrive: * - Register basic worker * - build root folders to sync + * - Create Recycle bin for eDrive */ public void start() { Timber.d("start()"); @@ -119,6 +121,9 @@ public class InitializerService extends Service { getInitialSyncedFolders(syncCategories); + final boolean recycleBinCreated = new File(AppConstants.RECYCLE_BIN_PATH).mkdirs(); + if (!recycleBinCreated) Timber.d("Cannot create recycle bin"); + CommonUtils.registerInitializationWorkers(syncedFolders, WorkManager.getInstance(getApplicationContext()) ); } @@ -131,7 +136,7 @@ public class InitializerService extends Service { this.syncedFolders = new ArrayList<>(); - for(final String category : categories) { + for (final String category : categories) { switch (category) { case "Images": syncedFolders.add(new SyncedFolder(category, getExternalFolder(Environment.DIRECTORY_DCIM), diff --git a/app/src/main/java/foundation/e/drive/utils/AppConstants.java b/app/src/main/java/foundation/e/drive/utils/AppConstants.java index bad256e6..59a41be1 100644 --- a/app/src/main/java/foundation/e/drive/utils/AppConstants.java +++ b/app/src/main/java/foundation/e/drive/utils/AppConstants.java @@ -1,6 +1,6 @@ /* * Copyright © CLEUS SAS 2018-2019. - * Copyright © ECORP SAS 2022. + * 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 @@ -9,7 +9,10 @@ package foundation.e.drive.utils; +import static com.owncloud.android.lib.resources.files.FileUtils.PATH_SEPARATOR; + import android.os.Build; +import android.os.Environment; import java.text.SimpleDateFormat; import java.util.Locale; @@ -47,6 +50,8 @@ public abstract class AppConstants { public static final String WORK_INITIALIZATION_TAG = "eDrive-init"; public static final String USER_AGENT = "eos(" + getBuildTime() + ")-eDrive(" + BuildConfig.VERSION_NAME + ")"; + public static final String RECYCLE_BIN_PATH = Environment.getExternalStorageDirectory() + PATH_SEPARATOR + ".eDrive-recycle-bin"; + /** * Get a readable OS's build date String * -- GitLab From 8cf9fd4e973ffb6fd3e65dfbc688468a208f398e Mon Sep 17 00:00:00 2001 From: vincent Bourgmayer Date: Wed, 15 Mar 2023 16:41:22 +0100 Subject: [PATCH 03/20] Refactoring: set InitializerService's method private Commit to merge with one of the previous --- .../java/foundation/e/drive/services/InitializerService.java | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/app/src/main/java/foundation/e/drive/services/InitializerService.java b/app/src/main/java/foundation/e/drive/services/InitializerService.java index cc99232c..0d4f5a54 100644 --- a/app/src/main/java/foundation/e/drive/services/InitializerService.java +++ b/app/src/main/java/foundation/e/drive/services/InitializerService.java @@ -85,7 +85,7 @@ public class InitializerService extends Service { * - Account available * @return true if condition are met */ - public boolean checkStartConditions(final SharedPreferences prefs, final String accountName, final String accountType) { + private boolean checkStartConditions(final SharedPreferences prefs, final String accountName, final String accountType) { if (!prefs.getBoolean(AppConstants.INITIALIZATION_HAS_BEEN_DONE, false)) { Timber.d("Initialization has already been done"); return false; @@ -110,7 +110,7 @@ public class InitializerService extends Service { * - build root folders to sync * - Create Recycle bin for eDrive */ - public void start() { + private void start() { Timber.d("start()"); CommonUtils.registerPeriodicUserInfoChecking(WorkManager.getInstance(this)); -- GitLab From 9c305e5c472bc5a88652a8f0157ebe705623f1f1 Mon Sep 17 00:00:00 2001 From: vincent Bourgmayer Date: Wed, 15 Mar 2023 16:58:39 +0100 Subject: [PATCH 04/20] refactor LocalFileDeleter. make deleteFile(...) deprecated, and implement new method: moveToTrash(...) --- .../java/foundation/e/drive/RecycleBin.kt | 4 ++ .../e/drive/work/LocalFileDeleter.java | 51 +++++++++++++++---- 2 files changed, 45 insertions(+), 10 deletions(-) create mode 100644 app/src/main/java/foundation/e/drive/RecycleBin.kt diff --git a/app/src/main/java/foundation/e/drive/RecycleBin.kt b/app/src/main/java/foundation/e/drive/RecycleBin.kt new file mode 100644 index 00000000..93b068b7 --- /dev/null +++ b/app/src/main/java/foundation/e/drive/RecycleBin.kt @@ -0,0 +1,4 @@ +package foundation.e.drive + +class RecycleBin { +} \ No newline at end of file diff --git a/app/src/main/java/foundation/e/drive/work/LocalFileDeleter.java b/app/src/main/java/foundation/e/drive/work/LocalFileDeleter.java index df8e0ad8..89f7c69a 100644 --- a/app/src/main/java/foundation/e/drive/work/LocalFileDeleter.java +++ b/app/src/main/java/foundation/e/drive/work/LocalFileDeleter.java @@ -12,9 +12,13 @@ import android.os.Handler; import android.provider.MediaStore; import java.io.File; +import java.io.IOException; +import java.nio.file.Files; +import java.nio.file.Path; import foundation.e.drive.database.DbHelper; import foundation.e.drive.models.SyncedFileState; +import foundation.e.drive.utils.AppConstants; import foundation.e.drive.utils.CommonUtils; import timber.log.Timber; @@ -37,13 +41,26 @@ public class LocalFileDeleter { public Runnable getRunnable(final Handler handler, final int threadId, final Context context, final LocalFileDeletionListener listener) { return () -> { - final boolean succeed = deleteFile(fileState, context); + final File file = new File(fileState.getLocalPath()); + //final boolean succeed = deleteFile(file); + final boolean succeed = moveFileToTrash(file); + if (succeed) { + context.getContentResolver().delete(MediaStore.Files.getContentUri("external"), + MediaStore.Files.FileColumns.DATA + "=?", + new String[]{CommonUtils.getLocalPath(file)}); + + if (DbHelper.manageSyncedFileStateDB(fileState, "DELETE", context) <= 0) { + Timber.d("Failed to remove %s from DB", file.getName()); + } + } + notifyCompletion(threadId, listener, succeed, handler); }; } - private boolean deleteFile(SyncedFileState fileState, Context context) { - final File file = new File(fileState.getLocalPath()); + /* + @Deprecated + private boolean deleteFile(File file) { if (file.exists()) { try { if (!file.delete()) { @@ -54,17 +71,31 @@ public class LocalFileDeleter { Timber.e(exception); return false; } - - context.getContentResolver().delete(MediaStore.Files.getContentUri("external"), - MediaStore.Files.FileColumns.DATA + "=?", - new String[]{CommonUtils.getLocalPath(file)}); } + return true; + } */ + /** + * Move the given file in the Recycle bin + * @return + */ + private boolean moveFileToTrash(final File file) { + if (file == null || !file.exists()) { + Timber.d("Can't move %s to trashbin: it doesn't exist", file.getAbsolutePath()); + return false; + } - if (DbHelper.manageSyncedFileStateDB(fileState, "DELETE", context) <= 0) { - Timber.d("Failed to remove %s from DB", file.getName()); + final File recycleBin = new File(AppConstants.RECYCLE_BIN_PATH); + recycleBin.mkdirs(); //Create if not exists + try { + final Path moveResult = Files.move(file.toPath(), recycleBin.toPath()); + if (moveResult.toFile().exists()) { + return true; + } + } catch (IOException | SecurityException | NullPointerException exception) { + Timber.e(exception); } - return true; + return false; } private void notifyCompletion(final int threadId, final LocalFileDeletionListener listener, final boolean success, final Handler handler) { -- GitLab From 8f516e8ee14c68ac31f444b81c5a580eeca6976f Mon Sep 17 00:00:00 2001 From: vincent Bourgmayer Date: Thu, 16 Mar 2023 11:34:28 +0100 Subject: [PATCH 05/20] Implement a RecycleBin class in kotlin --- .../java/foundation/e/drive/RecycleBin.kt | 74 +++++++++++++++++++ .../e/drive/utils/AppConstants.java | 2 +- .../e/drive/work/LocalFileDeleter.java | 49 +----------- 3 files changed, 78 insertions(+), 47 deletions(-) diff --git a/app/src/main/java/foundation/e/drive/RecycleBin.kt b/app/src/main/java/foundation/e/drive/RecycleBin.kt index 93b068b7..dceeb007 100644 --- a/app/src/main/java/foundation/e/drive/RecycleBin.kt +++ b/app/src/main/java/foundation/e/drive/RecycleBin.kt @@ -1,4 +1,78 @@ package foundation.e.drive +import android.content.Context +import android.os.Environment +import foundation.e.drive.utils.AppConstants +import timber.log.Timber +import java.io.File +import java.io.IOException +import java.nio.file.Files +import kotlin.io.path.Path +import kotlin.time.Duration +import kotlin.time.DurationUnit +import kotlin.time.toDuration + class RecycleBin { + private val BIN_PATH = AppConstants.RECYCLE_BIN_PATH //TMP only, Need to find a way to get context + private val DELAY_FOR_DELETION = 30.toDuration(DurationUnit.DAYS); + + companion object Singleton { + private var instance: RecycleBin ? = null + + fun getInstance() = instance?: synchronized(this) { + instance ?: RecycleBin().also { instance = it } + } + } + + /** + * Remove files which are in recycle bin + * for more than DELAY_FOR_DELETION + */ + fun clearOldestFiles(): Boolean { + val binDir = File(BIN_PATH); + try { + val filesToRemove = binDir.listFiles { file -> + computeTimeInBin(file.lastModified()) >= DELAY_FOR_DELETION + } + + filesToRemove?.forEach { file -> file?.delete() } + } catch (exception: IOException) { + Timber.d(exception, "Catched exception when clearing oldest file in bin") + return false + } + return true + } + + /** + * Compute time from which file is in Bin + * and return it as a Duration in days + */ + private fun computeTimeInBin(fileLastModified : Long): Duration { + return (System.currentTimeMillis() - fileLastModified).toDuration(DurationUnit.DAYS) + } + + + /** + * Move a file into the bin + */ + fun putFile(file:File): Boolean { + File(BIN_PATH).mkdirs() //Assert that recycle bin exist + + if (file.exists()) { + try { + val moveResult = Files.move(file.toPath(), Path(BIN_PATH)) + if (moveResult.toFile().exists()) { + return true + } + } catch (exception: IOException) { + Timber.d(exception) + } catch (exception: SecurityException) { + Timber.d(exception) + } catch (exception: NullPointerException) { + Timber.d(exception) + } + } + Timber.d("Can't move %s to trashbin", file.getAbsolutePath()) + return false + } } \ No newline at end of file diff --git a/app/src/main/java/foundation/e/drive/utils/AppConstants.java b/app/src/main/java/foundation/e/drive/utils/AppConstants.java index 59a41be1..555ac004 100644 --- a/app/src/main/java/foundation/e/drive/utils/AppConstants.java +++ b/app/src/main/java/foundation/e/drive/utils/AppConstants.java @@ -50,7 +50,7 @@ public abstract class AppConstants { public static final String WORK_INITIALIZATION_TAG = "eDrive-init"; public static final String USER_AGENT = "eos(" + getBuildTime() + ")-eDrive(" + BuildConfig.VERSION_NAME + ")"; - public static final String RECYCLE_BIN_PATH = Environment.getExternalStorageDirectory() + PATH_SEPARATOR + ".eDrive-recycle-bin"; + public static final String RECYCLE_BIN_PATH = Environment.getExternalStorageDirectory() + PATH_SEPARATOR + "eDrive-recycle-bin"; /** * Get a readable OS's build date String diff --git a/app/src/main/java/foundation/e/drive/work/LocalFileDeleter.java b/app/src/main/java/foundation/e/drive/work/LocalFileDeleter.java index 89f7c69a..6ddc6d2d 100644 --- a/app/src/main/java/foundation/e/drive/work/LocalFileDeleter.java +++ b/app/src/main/java/foundation/e/drive/work/LocalFileDeleter.java @@ -12,13 +12,10 @@ import android.os.Handler; import android.provider.MediaStore; import java.io.File; -import java.io.IOException; -import java.nio.file.Files; -import java.nio.file.Path; +import foundation.e.drive.RecycleBin; import foundation.e.drive.database.DbHelper; import foundation.e.drive.models.SyncedFileState; -import foundation.e.drive.utils.AppConstants; import foundation.e.drive.utils.CommonUtils; import timber.log.Timber; @@ -42,8 +39,8 @@ public class LocalFileDeleter { public Runnable getRunnable(final Handler handler, final int threadId, final Context context, final LocalFileDeletionListener listener) { return () -> { final File file = new File(fileState.getLocalPath()); - //final boolean succeed = deleteFile(file); - final boolean succeed = moveFileToTrash(file); + final boolean succeed = RecycleBin.Singleton.getInstance().putFile(file); + if (succeed) { context.getContentResolver().delete(MediaStore.Files.getContentUri("external"), MediaStore.Files.FileColumns.DATA + "=?", @@ -58,46 +55,6 @@ public class LocalFileDeleter { }; } - /* - @Deprecated - private boolean deleteFile(File file) { - if (file.exists()) { - try { - if (!file.delete()) { - Timber.d("local file ( %s ) removal failed", file.getName()); - return false; - } - } catch (SecurityException exception) { - Timber.e(exception); - return false; - } - } - return true; - } */ - - /** - * Move the given file in the Recycle bin - * @return - */ - private boolean moveFileToTrash(final File file) { - if (file == null || !file.exists()) { - Timber.d("Can't move %s to trashbin: it doesn't exist", file.getAbsolutePath()); - return false; - } - - final File recycleBin = new File(AppConstants.RECYCLE_BIN_PATH); - recycleBin.mkdirs(); //Create if not exists - try { - final Path moveResult = Files.move(file.toPath(), recycleBin.toPath()); - if (moveResult.toFile().exists()) { - return true; - } - } catch (IOException | SecurityException | NullPointerException exception) { - Timber.e(exception); - } - return false; - } - private void notifyCompletion(final int threadId, final LocalFileDeletionListener listener, final boolean success, final Handler handler) { handler.post(() -> listener.onDeletionComplete(threadId, success)); } -- GitLab From d98b6aa772710ef42c1e9939f016c31af11ac4a6 Mon Sep 17 00:00:00 2001 From: vincent Bourgmayer Date: Thu, 16 Mar 2023 15:59:51 +0100 Subject: [PATCH 06/20] fix build failure due to kotlin code --- app/build.gradle | 6 ++++-- app/src/main/java/foundation/e/drive/RecycleBin.kt | 6 ++++-- build.gradle | 1 + 3 files changed, 9 insertions(+), 4 deletions(-) diff --git a/app/build.gradle b/app/build.gradle index 094951fb..1e7176c2 100644 --- a/app/build.gradle +++ b/app/build.gradle @@ -1,5 +1,6 @@ plugins { id 'com.android.application' + id 'org.jetbrains.kotlin.android' } @@ -35,11 +36,11 @@ def getSentryDsn = { -> } android { - compileSdk 31 + compileSdk 33 defaultConfig { applicationId "foundation.e.drive" minSdk 26 - targetSdk 31 + targetSdk 33 versionCode versionMajor * 1000000 + versionMinor * 1000 + versionPatch versionName "${versionMajor}.${versionMinor}.${versionPatch}" setProperty("archivesBaseName", "eDrive-$versionName") @@ -99,6 +100,7 @@ dependencies { implementation 'com.google.android.material:material:1.6.0' implementation 'com.github.bumptech.glide:glide:4.14.2' implementation 'com.github.bumptech.glide:annotations:4.14.2' + implementation 'androidx.core:core-ktx:+' annotationProcessor 'com.github.bumptech.glide:compiler:4.14.2' implementation "androidx.work:work-runtime:2.7.1" implementation 'androidx.test:core:1.4.0' diff --git a/app/src/main/java/foundation/e/drive/RecycleBin.kt b/app/src/main/java/foundation/e/drive/RecycleBin.kt index dceeb007..973ad532 100644 --- a/app/src/main/java/foundation/e/drive/RecycleBin.kt +++ b/app/src/main/java/foundation/e/drive/RecycleBin.kt @@ -1,7 +1,7 @@ +@file:JvmName("RecycleBin") + package foundation.e.drive -import android.content.Context -import android.os.Environment import foundation.e.drive.utils.AppConstants import timber.log.Timber import java.io.File @@ -17,8 +17,10 @@ class RecycleBin { private val DELAY_FOR_DELETION = 30.toDuration(DurationUnit.DAYS); companion object Singleton { + @JvmStatic private var instance: RecycleBin ? = null + @JvmStatic fun getInstance() = instance?: synchronized(this) { instance ?: RecycleBin().also { instance = it } } diff --git a/build.gradle b/build.gradle index 3858e872..2d560f5f 100644 --- a/build.gradle +++ b/build.gradle @@ -2,6 +2,7 @@ plugins { id 'com.android.application' version '7.1.3' apply false id 'com.android.library' version '7.1.3' apply false + id 'org.jetbrains.kotlin.android' version '1.8.20-RC' apply false } task clean(type: Delete) { -- GitLab From a257b8961ac705d7ff270de977552d6a6af71f2d Mon Sep 17 00:00:00 2001 From: vincent Bourgmayer Date: Tue, 21 Mar 2023 11:05:08 +0100 Subject: [PATCH 07/20] Implement periodic job for cleaning recycleBin --- .../java/foundation/e/drive/RecycleBin.kt | 23 +++++++++---- .../e/drive/work/FirstStartWorker.java | 5 +++ .../e/drive/work/LocalFileDeleter.java | 2 +- .../e/drive/work/RecycleBinCleaningWorker.kt | 32 +++++++++++++++++++ .../e/drive/work/WorkRequestFactory.java | 15 +++++++-- 5 files changed, 68 insertions(+), 9 deletions(-) create mode 100644 app/src/main/java/foundation/e/drive/work/RecycleBinCleaningWorker.kt diff --git a/app/src/main/java/foundation/e/drive/RecycleBin.kt b/app/src/main/java/foundation/e/drive/RecycleBin.kt index 973ad532..2d991326 100644 --- a/app/src/main/java/foundation/e/drive/RecycleBin.kt +++ b/app/src/main/java/foundation/e/drive/RecycleBin.kt @@ -1,3 +1,11 @@ +/* + * 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 + */ + @file:JvmName("RecycleBin") package foundation.e.drive @@ -12,11 +20,14 @@ import kotlin.time.Duration import kotlin.time.DurationUnit import kotlin.time.toDuration +/** + * This class contains method for trashing file & cleaning the trash + */ class RecycleBin { - private val BIN_PATH = AppConstants.RECYCLE_BIN_PATH //TMP only, Need to find a way to get context - private val DELAY_FOR_DELETION = 30.toDuration(DurationUnit.DAYS); - companion object Singleton { + private val DELAY_FOR_DELETION = 30.toDuration(DurationUnit.DAYS); + private val BIN_PATH = AppConstants.RECYCLE_BIN_PATH //TMP only, Need to find a way to get context + @JvmStatic private var instance: RecycleBin ? = null @@ -34,7 +45,7 @@ class RecycleBin { val binDir = File(BIN_PATH); try { val filesToRemove = binDir.listFiles { file -> - computeTimeInBin(file.lastModified()) >= DELAY_FOR_DELETION + computeTimeInBin(file.lastModified()) > DELAY_FOR_DELETION } filesToRemove?.forEach { file -> file?.delete() } @@ -55,9 +66,9 @@ class RecycleBin { /** - * Move a file into the bin + * put a file into the bin */ - fun putFile(file:File): Boolean { + fun trashFile(file:File): Boolean { File(BIN_PATH).mkdirs() //Assert that recycle bin exist if (file.exists()) { diff --git a/app/src/main/java/foundation/e/drive/work/FirstStartWorker.java b/app/src/main/java/foundation/e/drive/work/FirstStartWorker.java index 7cdbf4f1..894cb85d 100644 --- a/app/src/main/java/foundation/e/drive/work/FirstStartWorker.java +++ b/app/src/main/java/foundation/e/drive/work/FirstStartWorker.java @@ -10,6 +10,7 @@ package foundation.e.drive.work; import static foundation.e.drive.utils.AppConstants.INITIALFOLDERS_NUMBER; import static foundation.e.drive.work.WorkRequestFactory.WorkType.ONE_TIME_APP_LIST; +import static foundation.e.drive.work.WorkRequestFactory.WorkType.PERIODIC_CLEANING; import static foundation.e.drive.work.WorkRequestFactory.WorkType.PERIODIC_SCAN; import android.content.Context; @@ -74,5 +75,9 @@ public class FirstStartWorker extends Worker { workManager.enqueueUniquePeriodicWork(PeriodicWorker.UNIQUE_WORK_NAME, ExistingPeriodicWorkPolicy.KEEP, WorkRequestFactory.getPeriodicWorkRequest(PERIODIC_SCAN)); + + workManager.enqueueUniquePeriodicWork(RecycleBinCleaningWorkerKt.UNIQUE_NAME, + ExistingPeriodicWorkPolicy.KEEP, + WorkRequestFactory.getPeriodicWorkRequest(PERIODIC_CLEANING)); } } \ No newline at end of file diff --git a/app/src/main/java/foundation/e/drive/work/LocalFileDeleter.java b/app/src/main/java/foundation/e/drive/work/LocalFileDeleter.java index 6ddc6d2d..644a1996 100644 --- a/app/src/main/java/foundation/e/drive/work/LocalFileDeleter.java +++ b/app/src/main/java/foundation/e/drive/work/LocalFileDeleter.java @@ -39,7 +39,7 @@ public class LocalFileDeleter { public Runnable getRunnable(final Handler handler, final int threadId, final Context context, final LocalFileDeletionListener listener) { return () -> { final File file = new File(fileState.getLocalPath()); - final boolean succeed = RecycleBin.Singleton.getInstance().putFile(file); + final boolean succeed = RecycleBin.getInstance().trashFile(file); if (succeed) { context.getContentResolver().delete(MediaStore.Files.getContentUri("external"), diff --git a/app/src/main/java/foundation/e/drive/work/RecycleBinCleaningWorker.kt b/app/src/main/java/foundation/e/drive/work/RecycleBinCleaningWorker.kt new file mode 100644 index 00000000..23f815cd --- /dev/null +++ b/app/src/main/java/foundation/e/drive/work/RecycleBinCleaningWorker.kt @@ -0,0 +1,32 @@ +/* + * 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.work + +import android.content.Context +import androidx.work.Worker +import androidx.work.WorkerParameters +import foundation.e.drive.RecycleBin + +/** + * This class is the worker that call cleaning of eDrive's technical files + * and cleaning + * @author Vincent Bourgmayer + */ +const val UNIQUE_NAME = "binCleaner" +class RecycleBinCleaningWorker(context: Context, workerParams: WorkerParameters) : Worker(context, workerParams) { + + + override fun doWork(): Result { + val result = RecycleBin.getInstance().clearOldestFiles() + + if (result) + return Result.success() + else + return Result.failure() + } +} \ 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 52084c10..0eeebfff 100644 --- a/app/src/main/java/foundation/e/drive/work/WorkRequestFactory.java +++ b/app/src/main/java/foundation/e/drive/work/WorkRequestFactory.java @@ -26,7 +26,6 @@ import androidx.work.Constraints; import androidx.work.Data; import androidx.work.NetworkType; import androidx.work.OneTimeWorkRequest; -import androidx.work.OutOfQuotaPolicy; import androidx.work.PeriodicWorkRequest; import java.security.InvalidParameterException; @@ -39,6 +38,7 @@ public class WorkRequestFactory { public enum WorkType { PERIODIC_USER_INFO, PERIODIC_SCAN, + PERIODIC_CLEANING, ONE_TIME_FULL_SCAN, ONE_TIME_APP_LIST, ONE_TIME_USER_INFO, @@ -58,11 +58,23 @@ public class WorkRequestFactory { return createPeriodicScanWorkRequest(); case PERIODIC_USER_INFO: return createPeriodicGetUserInfoWorkRequest(); + case PERIODIC_CLEANING: + return createPeriodicCleaningWorkRequest(); default: throw new InvalidParameterException("Unsupported Work Type: " + type); } } + /** + * Create a periodic work request that clean the trash bin + * @return + */ + private static PeriodicWorkRequest createPeriodicCleaningWorkRequest() { + return new PeriodicWorkRequest.Builder(RecycleBinCleaningWorker.class, 1, TimeUnit.DAYS, 2, TimeUnit.HOURS) + .addTag(AppConstants.WORK_GENERIC_TAG) + .build(); + } + /** * Create a PeridocWorkRequest instance for * a Full scan with constraints on network (should @@ -125,7 +137,6 @@ public class WorkRequestFactory { /** * Create a workRequest to generate file which contains list of installed apps - * @param expedited if request is expedited * @return the workRequest */ private static OneTimeWorkRequest createOneTimeAppListGenerationWorkRequest() { -- GitLab From a67752056c5ac6682d74e66ff95ed247993ce4e4 Mon Sep 17 00:00:00 2001 From: vincent Bourgmayer Date: Tue, 21 Mar 2023 11:05:30 +0100 Subject: [PATCH 08/20] enable again local file deletion --- .../e/drive/contentScanner/RemoteContentScanner.java | 4 ---- .../java/foundation/e/drive/services/ObserverService.java | 1 - .../foundation/e/drive/services/SynchronizationService.java | 5 +---- 3 files changed, 1 insertion(+), 9 deletions(-) diff --git a/app/src/main/java/foundation/e/drive/contentScanner/RemoteContentScanner.java b/app/src/main/java/foundation/e/drive/contentScanner/RemoteContentScanner.java index 0e8d37ed..9ff7c18c 100644 --- a/app/src/main/java/foundation/e/drive/contentScanner/RemoteContentScanner.java +++ b/app/src/main/java/foundation/e/drive/contentScanner/RemoteContentScanner.java @@ -82,16 +82,12 @@ public class RemoteContentScanner extends AbstractContentScanner { @Override protected void onMissingFile(SyncedFileState fileState) { - //TODO: disabled file deletion feature for now to handle accidental file deletion. - //Uncomment the following block when we resolve the issue: https://gitlab.e.foundation/e/backlog/-/issues/6711 -/* if (!fileState.hasBeenSynchronizedOnce()) { return; } Timber.d("Add local deletion request for file: %s", fileState.getLocalPath()); this.syncRequests.put(fileState.getId(), new SyncRequest(fileState, LOCAL_DELETE)); -*/ } @Override 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 5664eb1c..a9b76105 100644 --- a/app/src/main/java/foundation/e/drive/services/ObserverService.java +++ b/app/src/main/java/foundation/e/drive/services/ObserverService.java @@ -222,7 +222,6 @@ public class ObserverService extends Service implements OnRemoteOperationListene /** * Clear cached file unused: - * remove each cached file which isn't in OperationManagerService.lockedSyncedFileState(); */ private void clearCachedFile(){ Timber.i("clearCachedFile()"); diff --git a/app/src/main/java/foundation/e/drive/services/SynchronizationService.java b/app/src/main/java/foundation/e/drive/services/SynchronizationService.java index 74946167..78c87b07 100644 --- a/app/src/main/java/foundation/e/drive/services/SynchronizationService.java +++ b/app/src/main/java/foundation/e/drive/services/SynchronizationService.java @@ -236,16 +236,13 @@ public class SynchronizationService extends Service implements OnRemoteOperation final SyncWrapper syncWrapper = new SyncWrapper(request, account, getApplicationContext()); if (request.getOperationType().equals(SyncRequest.Type.LOCAL_DELETE)) { - //TODO: disabled file deletion feature for now to handle accidental file deletion. - //Uncomment the following block when we resolve the issue: https://gitlab.e.foundation/e/backlog/-/issues/6711 -/* + Timber.v(" starts " + request.getSyncedFileState().getName() + " local deletion on thread " + threadIndex); final LocalFileDeleter fileDeleter = new LocalFileDeleter(request.getSyncedFileState()); threadPool[threadIndex] = new Thread(fileDeleter.getRunnable( handler, threadIndex, getApplicationContext(), this)); threadPool[threadIndex].start(); startedSync.put(threadIndex, syncWrapper); -*/ return; } -- GitLab From d23e5f77e47f005e2376aa3e63e55861f21cc824 Mon Sep 17 00:00:00 2001 From: vincent Bourgmayer Date: Tue, 21 Mar 2023 15:52:59 +0100 Subject: [PATCH 09/20] fix error in refactoring InitializerService to be merge with first commit --- .../java/foundation/e/drive/services/InitializerService.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/app/src/main/java/foundation/e/drive/services/InitializerService.java b/app/src/main/java/foundation/e/drive/services/InitializerService.java index 0d4f5a54..ca6a0e88 100644 --- a/app/src/main/java/foundation/e/drive/services/InitializerService.java +++ b/app/src/main/java/foundation/e/drive/services/InitializerService.java @@ -86,7 +86,7 @@ public class InitializerService extends Service { * @return true if condition are met */ private boolean checkStartConditions(final SharedPreferences prefs, final String accountName, final String accountType) { - if (!prefs.getBoolean(AppConstants.INITIALIZATION_HAS_BEEN_DONE, false)) { + if (prefs.getBoolean(AppConstants.INITIALIZATION_HAS_BEEN_DONE, false)) { Timber.d("Initialization has already been done"); return false; } -- GitLab From a4ba0b8bf142f1bfce42d5d4e0c841bbd52df8d7 Mon Sep 17 00:00:00 2001 From: vincent Bourgmayer Date: Tue, 21 Mar 2023 17:09:36 +0100 Subject: [PATCH 10/20] fix incorrect path for file move --- app/src/main/java/foundation/e/drive/RecycleBin.kt | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/app/src/main/java/foundation/e/drive/RecycleBin.kt b/app/src/main/java/foundation/e/drive/RecycleBin.kt index 2d991326..2653d6b9 100644 --- a/app/src/main/java/foundation/e/drive/RecycleBin.kt +++ b/app/src/main/java/foundation/e/drive/RecycleBin.kt @@ -71,9 +71,11 @@ class RecycleBin { fun trashFile(file:File): Boolean { File(BIN_PATH).mkdirs() //Assert that recycle bin exist + if (file.exists()) { + val targetPath = File(BIN_PATH, file.name).absolutePath try { - val moveResult = Files.move(file.toPath(), Path(BIN_PATH)) + val moveResult = Files.move(file.toPath(), Path(targetPath)) if (moveResult.toFile().exists()) { return true } -- GitLab From 0ecd167a1d0e4743493a624a06bb0a0b2cb545ff Mon Sep 17 00:00:00 2001 From: vincent Bourgmayer Date: Tue, 21 Mar 2023 17:43:05 +0100 Subject: [PATCH 11/20] change trash folder name --- app/src/main/java/foundation/e/drive/RecycleBin.kt | 1 - app/src/main/java/foundation/e/drive/utils/AppConstants.java | 2 +- 2 files changed, 1 insertion(+), 2 deletions(-) diff --git a/app/src/main/java/foundation/e/drive/RecycleBin.kt b/app/src/main/java/foundation/e/drive/RecycleBin.kt index 2653d6b9..f0ca77ed 100644 --- a/app/src/main/java/foundation/e/drive/RecycleBin.kt +++ b/app/src/main/java/foundation/e/drive/RecycleBin.kt @@ -71,7 +71,6 @@ class RecycleBin { fun trashFile(file:File): Boolean { File(BIN_PATH).mkdirs() //Assert that recycle bin exist - if (file.exists()) { val targetPath = File(BIN_PATH, file.name).absolutePath try { diff --git a/app/src/main/java/foundation/e/drive/utils/AppConstants.java b/app/src/main/java/foundation/e/drive/utils/AppConstants.java index 555ac004..cdf8d366 100644 --- a/app/src/main/java/foundation/e/drive/utils/AppConstants.java +++ b/app/src/main/java/foundation/e/drive/utils/AppConstants.java @@ -50,7 +50,7 @@ public abstract class AppConstants { public static final String WORK_INITIALIZATION_TAG = "eDrive-init"; public static final String USER_AGENT = "eos(" + getBuildTime() + ")-eDrive(" + BuildConfig.VERSION_NAME + ")"; - public static final String RECYCLE_BIN_PATH = Environment.getExternalStorageDirectory() + PATH_SEPARATOR + "eDrive-recycle-bin"; + public static final String RECYCLE_BIN_PATH = Environment.getExternalStorageDirectory() + PATH_SEPARATOR + "Sync_trash"; /** * Get a readable OS's build date String -- GitLab From 64e41d5ff5219b05e2afee20bf1ac47075e1d987 Mon Sep 17 00:00:00 2001 From: vincent Bourgmayer Date: Thu, 23 Mar 2023 06:11:58 +0100 Subject: [PATCH 12/20] check if bin exists in 'RecycleBin.clearOldestFiles()' --- app/src/main/java/foundation/e/drive/RecycleBin.kt | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/app/src/main/java/foundation/e/drive/RecycleBin.kt b/app/src/main/java/foundation/e/drive/RecycleBin.kt index f0ca77ed..c03ce9fd 100644 --- a/app/src/main/java/foundation/e/drive/RecycleBin.kt +++ b/app/src/main/java/foundation/e/drive/RecycleBin.kt @@ -40,9 +40,13 @@ class RecycleBin { /** * Remove files which are in recycle bin * for more than DELAY_FOR_DELETION + * @return false as soon as some files that should be removed is not removed */ fun clearOldestFiles(): Boolean { val binDir = File(BIN_PATH); + + if (!binDir.exists()) return true + try { val filesToRemove = binDir.listFiles { file -> computeTimeInBin(file.lastModified()) > DELAY_FOR_DELETION @@ -50,6 +54,7 @@ class RecycleBin { filesToRemove?.forEach { file -> file?.delete() } } catch (exception: IOException) { + //Note that some files might have already been removed Timber.d(exception, "Catched exception when clearing oldest file in bin") return false } -- GitLab From 94b2b2424c0c77702d3096577256cb78caa5b82a Mon Sep 17 00:00:00 2001 From: vincent Bourgmayer Date: Fri, 24 Mar 2023 09:49:13 +0100 Subject: [PATCH 13/20] change targetSdk to 31 --- app/build.gradle | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/app/build.gradle b/app/build.gradle index 1e7176c2..c8c8651f 100644 --- a/app/build.gradle +++ b/app/build.gradle @@ -40,7 +40,7 @@ android { defaultConfig { applicationId "foundation.e.drive" minSdk 26 - targetSdk 33 + targetSdk 31 versionCode versionMajor * 1000000 + versionMinor * 1000 + versionPatch versionName "${versionMajor}.${versionMinor}.${versionPatch}" setProperty("archivesBaseName", "eDrive-$versionName") -- GitLab From f9065916790d9bb6548bd30dd0ae3684395c6286 Mon Sep 17 00:00:00 2001 From: vincent Bourgmayer Date: Mon, 27 Mar 2023 09:08:22 +0200 Subject: [PATCH 14/20] Apply Jonathan's suggestion - Change RecycleBin's singleton implementation into kotlin 'Object' - Add @NonNull flag - Change logging level (debug to warning in Initializer service & debug to error in RecycleBin) - change wording of log in RecycleBin --- .../java/foundation/e/drive/RecycleBin.kt | 29 +++++++------------ .../e/drive/services/InitializerService.java | 11 +++---- .../e/drive/utils/AppConstants.java | 2 +- .../e/drive/work/LocalFileDeleter.java | 2 +- .../e/drive/work/RecycleBinCleaningWorker.kt | 3 +- 5 files changed, 19 insertions(+), 28 deletions(-) diff --git a/app/src/main/java/foundation/e/drive/RecycleBin.kt b/app/src/main/java/foundation/e/drive/RecycleBin.kt index c03ce9fd..dd893343 100644 --- a/app/src/main/java/foundation/e/drive/RecycleBin.kt +++ b/app/src/main/java/foundation/e/drive/RecycleBin.kt @@ -10,6 +10,7 @@ package foundation.e.drive +import androidx.annotation.NonNull import foundation.e.drive.utils.AppConstants import timber.log.Timber import java.io.File @@ -23,19 +24,9 @@ import kotlin.time.toDuration /** * This class contains method for trashing file & cleaning the trash */ -class RecycleBin { - companion object Singleton { - private val DELAY_FOR_DELETION = 30.toDuration(DurationUnit.DAYS); - private val BIN_PATH = AppConstants.RECYCLE_BIN_PATH //TMP only, Need to find a way to get context - - @JvmStatic - private var instance: RecycleBin ? = null - - @JvmStatic - fun getInstance() = instance?: synchronized(this) { - instance ?: RecycleBin().also { instance = it } - } - } +object RecycleBin { + private val DELAY_FOR_DELETION = 30.toDuration(DurationUnit.DAYS); + private val BIN_PATH = AppConstants.RECYCLE_BIN_PATH //TMP only, Need to find a way to get context /** * Remove files which are in recycle bin @@ -55,7 +46,7 @@ class RecycleBin { filesToRemove?.forEach { file -> file?.delete() } } catch (exception: IOException) { //Note that some files might have already been removed - Timber.d(exception, "Catched exception when clearing oldest file in bin") + Timber.e(exception, "Caught exception when clearing oldest file in bin") return false } return true @@ -65,7 +56,7 @@ class RecycleBin { * Compute time from which file is in Bin * and return it as a Duration in days */ - private fun computeTimeInBin(fileLastModified : Long): Duration { + private fun computeTimeInBin(@NonNull fileLastModified : Long): Duration { return (System.currentTimeMillis() - fileLastModified).toDuration(DurationUnit.DAYS) } @@ -73,7 +64,7 @@ class RecycleBin { /** * put a file into the bin */ - fun trashFile(file:File): Boolean { + fun trashFile(@NonNull file:File): Boolean { File(BIN_PATH).mkdirs() //Assert that recycle bin exist if (file.exists()) { @@ -84,11 +75,11 @@ class RecycleBin { return true } } catch (exception: IOException) { - Timber.d(exception) + Timber.e(exception) } catch (exception: SecurityException) { - Timber.d(exception) + Timber.e(exception) } catch (exception: NullPointerException) { - Timber.d(exception) + Timber.e(exception) } } Timber.d("Can't move %s to trashbin", file.getAbsolutePath()) diff --git a/app/src/main/java/foundation/e/drive/services/InitializerService.java b/app/src/main/java/foundation/e/drive/services/InitializerService.java index ca6a0e88..0df2d338 100644 --- a/app/src/main/java/foundation/e/drive/services/InitializerService.java +++ b/app/src/main/java/foundation/e/drive/services/InitializerService.java @@ -33,6 +33,7 @@ import static com.owncloud.android.lib.resources.files.FileUtils.PATH_SEPARATOR; import static foundation.e.drive.utils.AppConstants.MEDIA_SYNCABLE_CATEGORIES; import static foundation.e.drive.utils.AppConstants.SETTINGS_SYNCABLE_CATEGORIES; +import androidx.annotation.NonNull; import androidx.annotation.Nullable; import androidx.work.WorkManager; @@ -85,20 +86,20 @@ public class InitializerService extends Service { * - Account available * @return true if condition are met */ - private boolean checkStartConditions(final SharedPreferences prefs, final String accountName, final String accountType) { + private boolean checkStartConditions(@NonNull final SharedPreferences prefs, @NonNull final String accountName, @NonNull final String accountType) { if (prefs.getBoolean(AppConstants.INITIALIZATION_HAS_BEEN_DONE, false)) { - Timber.d("Initialization has already been done"); + Timber.w("Initialization has already been done"); return false; } if (accountName.isEmpty()) { - Timber.d("No account Name available"); + Timber.w("No account Name available"); return false; } account = CommonUtils.getAccount(accountName, accountType, AccountManager.get(this)); if (account == null) { - Timber.d("got Invalid %s account for username: %s ", accountType, accountName); + Timber.w("got Invalid %s account for username: %s ", accountType, accountName); return false; } return true; @@ -122,7 +123,7 @@ public class InitializerService extends Service { getInitialSyncedFolders(syncCategories); final boolean recycleBinCreated = new File(AppConstants.RECYCLE_BIN_PATH).mkdirs(); - if (!recycleBinCreated) Timber.d("Cannot create recycle bin"); + if (!recycleBinCreated) Timber.w("Cannot create recycle bin. It may be already existing"); CommonUtils.registerInitializationWorkers(syncedFolders, WorkManager.getInstance(getApplicationContext()) ); } diff --git a/app/src/main/java/foundation/e/drive/utils/AppConstants.java b/app/src/main/java/foundation/e/drive/utils/AppConstants.java index cdf8d366..0d907fc3 100644 --- a/app/src/main/java/foundation/e/drive/utils/AppConstants.java +++ b/app/src/main/java/foundation/e/drive/utils/AppConstants.java @@ -50,7 +50,7 @@ public abstract class AppConstants { public static final String WORK_INITIALIZATION_TAG = "eDrive-init"; public static final String USER_AGENT = "eos(" + getBuildTime() + ")-eDrive(" + BuildConfig.VERSION_NAME + ")"; - public static final String RECYCLE_BIN_PATH = Environment.getExternalStorageDirectory() + PATH_SEPARATOR + "Sync_trash"; + public static final String RECYCLE_BIN_PATH = Environment.getExternalStorageDirectory() + PATH_SEPARATOR + "Sync trash"; /** * Get a readable OS's build date String diff --git a/app/src/main/java/foundation/e/drive/work/LocalFileDeleter.java b/app/src/main/java/foundation/e/drive/work/LocalFileDeleter.java index 644a1996..bfbc1c35 100644 --- a/app/src/main/java/foundation/e/drive/work/LocalFileDeleter.java +++ b/app/src/main/java/foundation/e/drive/work/LocalFileDeleter.java @@ -39,7 +39,7 @@ public class LocalFileDeleter { public Runnable getRunnable(final Handler handler, final int threadId, final Context context, final LocalFileDeletionListener listener) { return () -> { final File file = new File(fileState.getLocalPath()); - final boolean succeed = RecycleBin.getInstance().trashFile(file); + final boolean succeed = RecycleBin.INSTANCE.trashFile(file); if (succeed) { context.getContentResolver().delete(MediaStore.Files.getContentUri("external"), diff --git a/app/src/main/java/foundation/e/drive/work/RecycleBinCleaningWorker.kt b/app/src/main/java/foundation/e/drive/work/RecycleBinCleaningWorker.kt index 23f815cd..e560ae7c 100644 --- a/app/src/main/java/foundation/e/drive/work/RecycleBinCleaningWorker.kt +++ b/app/src/main/java/foundation/e/drive/work/RecycleBinCleaningWorker.kt @@ -20,9 +20,8 @@ import foundation.e.drive.RecycleBin const val UNIQUE_NAME = "binCleaner" class RecycleBinCleaningWorker(context: Context, workerParams: WorkerParameters) : Worker(context, workerParams) { - override fun doWork(): Result { - val result = RecycleBin.getInstance().clearOldestFiles() + val result = RecycleBin.clearOldestFiles() if (result) return Result.success() -- GitLab From 6e700796ed7a09d0ec6c496957e8baf77425d07a Mon Sep 17 00:00:00 2001 From: vincent Bourgmayer Date: Mon, 27 Mar 2023 11:23:56 +0200 Subject: [PATCH 15/20] remove useless field in InitializerService --- .../e/drive/services/InitializerService.java | 15 ++++----------- 1 file changed, 4 insertions(+), 11 deletions(-) diff --git a/app/src/main/java/foundation/e/drive/services/InitializerService.java b/app/src/main/java/foundation/e/drive/services/InitializerService.java index 0df2d338..5e9f6086 100644 --- a/app/src/main/java/foundation/e/drive/services/InitializerService.java +++ b/app/src/main/java/foundation/e/drive/services/InitializerService.java @@ -43,7 +43,6 @@ import androidx.work.WorkManager; * @author Abhishek Aggarwal */ public class InitializerService extends Service { - private List syncedFolders; private Account account; @Override @@ -120,7 +119,7 @@ public class InitializerService extends Service { syncCategories.addAll(Arrays.asList(MEDIA_SYNCABLE_CATEGORIES)); syncCategories.addAll(Arrays.asList(SETTINGS_SYNCABLE_CATEGORIES)); - getInitialSyncedFolders(syncCategories); + final List syncedFolders = getInitialSyncedFolders(syncCategories); final boolean recycleBinCreated = new File(AppConstants.RECYCLE_BIN_PATH).mkdirs(); if (!recycleBinCreated) Timber.w("Cannot create recycle bin. It may be already existing"); @@ -132,10 +131,10 @@ public class InitializerService extends Service { * Return a list of SyncedFolder * @param categories categories indicating which syncedFolder to create */ - private void getInitialSyncedFolders(List categories) { + private ArrayList getInitialSyncedFolders(List categories) { Timber.d("getInitialSyncedFolders"); - this.syncedFolders = new ArrayList<>(); + final ArrayList syncedFolders = new ArrayList<>(); for (final String category : categories) { switch (category) { @@ -184,19 +183,13 @@ public class InitializerService extends Service { break; } } + return syncedFolders; } private String getExternalFolder(String directory) { return CommonUtils.getLocalPath(Environment.getExternalStoragePublicDirectory(directory))+ PATH_SEPARATOR; } - @Override - public void onDestroy() { - super.onDestroy(); - if (this.syncedFolders != null) this.syncedFolders.clear(); - this.syncedFolders = null; - } - @Nullable @Override public IBinder onBind(Intent intent) { -- GitLab From 1b838ae0f50965a5a25a53454d574de555c8341a Mon Sep 17 00:00:00 2001 From: Jonathan Klee Date: Mon, 27 Mar 2023 12:59:00 +0000 Subject: [PATCH 16/20] Apply 5 suggestion(s) to 1 file(s) --- app/src/main/java/foundation/e/drive/RecycleBin.kt | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/app/src/main/java/foundation/e/drive/RecycleBin.kt b/app/src/main/java/foundation/e/drive/RecycleBin.kt index dd893343..4252434c 100644 --- a/app/src/main/java/foundation/e/drive/RecycleBin.kt +++ b/app/src/main/java/foundation/e/drive/RecycleBin.kt @@ -25,7 +25,7 @@ import kotlin.time.toDuration * This class contains method for trashing file & cleaning the trash */ object RecycleBin { - private val DELAY_FOR_DELETION = 30.toDuration(DurationUnit.DAYS); + private val DELAY_FOR_DELETION = 30.toDuration(DurationUnit.DAYS) private val BIN_PATH = AppConstants.RECYCLE_BIN_PATH //TMP only, Need to find a way to get context /** @@ -34,7 +34,7 @@ object RecycleBin { * @return false as soon as some files that should be removed is not removed */ fun clearOldestFiles(): Boolean { - val binDir = File(BIN_PATH); + val binDir = File(BIN_PATH) if (!binDir.exists()) return true @@ -56,7 +56,7 @@ object RecycleBin { * Compute time from which file is in Bin * and return it as a Duration in days */ - private fun computeTimeInBin(@NonNull fileLastModified : Long): Duration { + private fun computeTimeInBin(fileLastModified : Long): Duration { return (System.currentTimeMillis() - fileLastModified).toDuration(DurationUnit.DAYS) } @@ -64,7 +64,7 @@ object RecycleBin { /** * put a file into the bin */ - fun trashFile(@NonNull file:File): Boolean { + fun trashFile(file: File): Boolean { File(BIN_PATH).mkdirs() //Assert that recycle bin exist if (file.exists()) { @@ -82,7 +82,7 @@ object RecycleBin { Timber.e(exception) } } - Timber.d("Can't move %s to trashbin", file.getAbsolutePath()) + Timber.d("Can't move %s to trashbin", file.absolutePath) return false } } \ No newline at end of file -- GitLab From cb6a7001b18e9311ff786df0f027dcaced04843d Mon Sep 17 00:00:00 2001 From: vincent Bourgmayer Date: Mon, 27 Mar 2023 16:04:14 +0200 Subject: [PATCH 17/20] fix Jonathan's suggestion #3 --- app/src/main/java/foundation/e/drive/RecycleBin.kt | 2 -- .../java/foundation/e/drive/services/InitializerService.java | 3 ++- .../java/foundation/e/drive/work/RecycleBinCleaningWorker.kt | 5 ++++- .../java/foundation/e/drive/work/WorkRequestFactory.java | 2 +- 4 files changed, 7 insertions(+), 5 deletions(-) diff --git a/app/src/main/java/foundation/e/drive/RecycleBin.kt b/app/src/main/java/foundation/e/drive/RecycleBin.kt index 4252434c..197fd2ba 100644 --- a/app/src/main/java/foundation/e/drive/RecycleBin.kt +++ b/app/src/main/java/foundation/e/drive/RecycleBin.kt @@ -10,7 +10,6 @@ package foundation.e.drive -import androidx.annotation.NonNull import foundation.e.drive.utils.AppConstants import timber.log.Timber import java.io.File @@ -60,7 +59,6 @@ object RecycleBin { return (System.currentTimeMillis() - fileLastModified).toDuration(DurationUnit.DAYS) } - /** * put a file into the bin */ diff --git a/app/src/main/java/foundation/e/drive/services/InitializerService.java b/app/src/main/java/foundation/e/drive/services/InitializerService.java index 5e9f6086..f320a900 100644 --- a/app/src/main/java/foundation/e/drive/services/InitializerService.java +++ b/app/src/main/java/foundation/e/drive/services/InitializerService.java @@ -85,7 +85,8 @@ public class InitializerService extends Service { * - Account available * @return true if condition are met */ - private boolean checkStartConditions(@NonNull final SharedPreferences prefs, @NonNull final String accountName, @NonNull final String accountType) { + private 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; diff --git a/app/src/main/java/foundation/e/drive/work/RecycleBinCleaningWorker.kt b/app/src/main/java/foundation/e/drive/work/RecycleBinCleaningWorker.kt index e560ae7c..45328ed3 100644 --- a/app/src/main/java/foundation/e/drive/work/RecycleBinCleaningWorker.kt +++ b/app/src/main/java/foundation/e/drive/work/RecycleBinCleaningWorker.kt @@ -17,8 +17,11 @@ import foundation.e.drive.RecycleBin * and cleaning * @author Vincent Bourgmayer */ -const val UNIQUE_NAME = "binCleaner" + class RecycleBinCleaningWorker(context: Context, workerParams: WorkerParameters) : Worker(context, workerParams) { + companion object { + const val UNIQUE_NAME = "binCleaner" + } override fun doWork(): Result { val result = RecycleBin.clearOldestFiles() 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 0eeebfff..3b6a0892 100644 --- a/app/src/main/java/foundation/e/drive/work/WorkRequestFactory.java +++ b/app/src/main/java/foundation/e/drive/work/WorkRequestFactory.java @@ -59,7 +59,7 @@ public class WorkRequestFactory { case PERIODIC_USER_INFO: return createPeriodicGetUserInfoWorkRequest(); case PERIODIC_CLEANING: - return createPeriodicCleaningWorkRequest(); + return createPeriodicCleaningWorkRequest(); default: throw new InvalidParameterException("Unsupported Work Type: " + type); } -- GitLab From ea214e37d1092ec12ad6b3cd195b3bc53d9f4a26 Mon Sep 17 00:00:00 2001 From: vincent Bourgmayer Date: Mon, 27 Mar 2023 16:24:41 +0200 Subject: [PATCH 18/20] fix build issue due to RecycleBinCleaningWorker's unique tag --- app/src/main/java/foundation/e/drive/work/FirstStartWorker.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/app/src/main/java/foundation/e/drive/work/FirstStartWorker.java b/app/src/main/java/foundation/e/drive/work/FirstStartWorker.java index 894cb85d..5890e103 100644 --- a/app/src/main/java/foundation/e/drive/work/FirstStartWorker.java +++ b/app/src/main/java/foundation/e/drive/work/FirstStartWorker.java @@ -76,7 +76,7 @@ public class FirstStartWorker extends Worker { ExistingPeriodicWorkPolicy.KEEP, WorkRequestFactory.getPeriodicWorkRequest(PERIODIC_SCAN)); - workManager.enqueueUniquePeriodicWork(RecycleBinCleaningWorkerKt.UNIQUE_NAME, + workManager.enqueueUniquePeriodicWork(RecycleBinCleaningWorker.UNIQUE_NAME, ExistingPeriodicWorkPolicy.KEEP, WorkRequestFactory.getPeriodicWorkRequest(PERIODIC_CLEANING)); } -- GitLab From caaeaf8095a44b224046de90f1111ec41a4077a1 Mon Sep 17 00:00:00 2001 From: vincent Bourgmayer Date: Tue, 28 Mar 2023 10:29:23 +0200 Subject: [PATCH 19/20] fix serial no --- app/src/main/AndroidManifest.xml | 1 + .../java/foundation/e/drive/services/InitializerService.java | 2 +- 2 files changed, 2 insertions(+), 1 deletion(-) diff --git a/app/src/main/AndroidManifest.xml b/app/src/main/AndroidManifest.xml index 1c47ced4..4cf129c0 100644 --- a/app/src/main/AndroidManifest.xml +++ b/app/src/main/AndroidManifest.xml @@ -5,6 +5,7 @@ + diff --git a/app/src/main/java/foundation/e/drive/services/InitializerService.java b/app/src/main/java/foundation/e/drive/services/InitializerService.java index f320a900..669860ce 100644 --- a/app/src/main/java/foundation/e/drive/services/InitializerService.java +++ b/app/src/main/java/foundation/e/drive/services/InitializerService.java @@ -167,7 +167,7 @@ public class InitializerService extends Service { break; case "Rom settings": final String remoteFolderPath = PATH_SEPARATOR + "Devices" + PATH_SEPARATOR + Build.BRAND + "_"+ Build.MODEL + "_" - + Build.SERIAL + "/rom_settings/"; + + Build.getSerial() + "/rom_settings/"; syncedFolders.add(new SyncedFolder(category, "/data/system/users/0/", remoteFolderPath, true, false, true, false)); try{ syncedFolders.add(new SyncedFolder( -- GitLab From 1f62b37a996e378436277c5cb129874cdc2a0781 Mon Sep 17 00:00:00 2001 From: Vincent Bourgmayer Date: Wed, 29 Mar 2023 13:13:08 +0000 Subject: [PATCH 20/20] Proposal of refactoring for InitializerService --- .../java/foundation/e/drive/RecycleBin.kt | 2 +- .../e/drive/services/InitializerService.java | 78 +----------- .../e/drive/utils/RootSyncedFolderProvider.kt | 118 ++++++++++++++++++ 3 files changed, 121 insertions(+), 77 deletions(-) create mode 100644 app/src/main/java/foundation/e/drive/utils/RootSyncedFolderProvider.kt diff --git a/app/src/main/java/foundation/e/drive/RecycleBin.kt b/app/src/main/java/foundation/e/drive/RecycleBin.kt index 197fd2ba..447106bb 100644 --- a/app/src/main/java/foundation/e/drive/RecycleBin.kt +++ b/app/src/main/java/foundation/e/drive/RecycleBin.kt @@ -55,7 +55,7 @@ object RecycleBin { * Compute time from which file is in Bin * and return it as a Duration in days */ - private fun computeTimeInBin(fileLastModified : Long): Duration { + private fun computeTimeInBin(fileLastModified: Long): Duration { return (System.currentTimeMillis() - fileLastModified).toDuration(DurationUnit.DAYS) } diff --git a/app/src/main/java/foundation/e/drive/services/InitializerService.java b/app/src/main/java/foundation/e/drive/services/InitializerService.java index 669860ce..c6438174 100644 --- a/app/src/main/java/foundation/e/drive/services/InitializerService.java +++ b/app/src/main/java/foundation/e/drive/services/InitializerService.java @@ -15,24 +15,17 @@ import android.app.Service; import android.content.Context; import android.content.Intent; import android.content.SharedPreferences; -import android.os.Build; -import android.os.Environment; import android.os.IBinder; import java.io.File; -import java.util.ArrayList; -import java.util.Arrays; 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.RootSyncedFolderProvider; import timber.log.Timber; -import static com.owncloud.android.lib.resources.files.FileUtils.PATH_SEPARATOR; -import static foundation.e.drive.utils.AppConstants.MEDIA_SYNCABLE_CATEGORIES; -import static foundation.e.drive.utils.AppConstants.SETTINGS_SYNCABLE_CATEGORIES; - import androidx.annotation.NonNull; import androidx.annotation.Nullable; import androidx.work.WorkManager; @@ -116,11 +109,7 @@ public class InitializerService extends Service { CommonUtils.registerPeriodicUserInfoChecking(WorkManager.getInstance(this)); - final List syncCategories = new ArrayList<>(); - syncCategories.addAll(Arrays.asList(MEDIA_SYNCABLE_CATEGORIES)); - syncCategories.addAll(Arrays.asList(SETTINGS_SYNCABLE_CATEGORIES)); - - final List syncedFolders = getInitialSyncedFolders(syncCategories); + final List syncedFolders = RootSyncedFolderProvider.INSTANCE.getSyncedFolderRoots(getApplicationContext()); final boolean recycleBinCreated = new File(AppConstants.RECYCLE_BIN_PATH).mkdirs(); if (!recycleBinCreated) Timber.w("Cannot create recycle bin. It may be already existing"); @@ -128,69 +117,6 @@ public class InitializerService extends Service { CommonUtils.registerInitializationWorkers(syncedFolders, WorkManager.getInstance(getApplicationContext()) ); } - /** - * Return a list of SyncedFolder - * @param categories categories indicating which syncedFolder to create - */ - private ArrayList getInitialSyncedFolders(List categories) { - Timber.d("getInitialSyncedFolders"); - - final ArrayList syncedFolders = new ArrayList<>(); - - for (final String category : categories) { - switch (category) { - case "Images": - syncedFolders.add(new SyncedFolder(category, getExternalFolder(Environment.DIRECTORY_DCIM), - "/Photos/", true)); - syncedFolders.add(new SyncedFolder(category, getExternalFolder(Environment.DIRECTORY_PICTURES), - "/Pictures/", true)); - break; - case "Movies": - syncedFolders.add(new SyncedFolder(category, getExternalFolder(Environment.DIRECTORY_MOVIES), - "/Movies/", true)); - break; - case "Music": - syncedFolders.add(new SyncedFolder(category, getExternalFolder(Environment.DIRECTORY_MUSIC), - "/Music/", true)); - break; - case "Ringtones": - syncedFolders.add(new SyncedFolder(category, getExternalFolder(Environment.DIRECTORY_RINGTONES), - "/Ringtones/", true)); - break; - case "Documents": - syncedFolders.add(new SyncedFolder(category, getExternalFolder(Environment.DIRECTORY_DOCUMENTS), - "/Documents/", true)); - break; - case "Podcasts": - syncedFolders.add(new SyncedFolder(category, getExternalFolder(Environment.DIRECTORY_PODCASTS), - "/Podcasts/", true)); - break; - case "Rom settings": - final String remoteFolderPath = PATH_SEPARATOR + "Devices" + PATH_SEPARATOR + Build.BRAND + "_"+ Build.MODEL + "_" - + Build.getSerial() + "/rom_settings/"; - syncedFolders.add(new SyncedFolder(category, "/data/system/users/0/", remoteFolderPath, true, false, true, false)); - try{ - syncedFolders.add(new SyncedFolder( - category, - getFilesDir().getCanonicalPath() + PATH_SEPARATOR, - remoteFolderPath + "app_list/", - true, - false, - CommonUtils.isSettingsSyncEnabled(account), - false)); - } catch (Exception exception) { - Timber.e(exception); - } - break; - } - } - return syncedFolders; - } - - private String getExternalFolder(String directory) { - return CommonUtils.getLocalPath(Environment.getExternalStoragePublicDirectory(directory))+ PATH_SEPARATOR; - } - @Nullable @Override public IBinder onBind(Intent intent) { diff --git a/app/src/main/java/foundation/e/drive/utils/RootSyncedFolderProvider.kt b/app/src/main/java/foundation/e/drive/utils/RootSyncedFolderProvider.kt new file mode 100644 index 00000000..2c3d46b9 --- /dev/null +++ b/app/src/main/java/foundation/e/drive/utils/RootSyncedFolderProvider.kt @@ -0,0 +1,118 @@ +/* + * 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.utils + +import android.content.Context +import android.os.Build +import android.os.Build.BRAND +import android.os.Build.MODEL +import android.os.Environment +import android.os.Environment.DIRECTORY_PODCASTS +import android.os.Environment.DIRECTORY_MUSIC +import android.os.Environment.DIRECTORY_DCIM +import android.os.Environment.DIRECTORY_DOCUMENTS +import android.os.Environment.DIRECTORY_PICTURES +import android.os.Environment.DIRECTORY_RINGTONES +import android.os.Environment.DIRECTORY_MOVIES +import com.owncloud.android.lib.resources.files.FileUtils.PATH_SEPARATOR +import foundation.e.drive.models.SyncedFolder + +object RootSyncedFolderProvider { + private const val CATEGORY_IMAGES = "Images" + private const val CATEGORY_MOVIES = "Movies" + private const val CATEGORY_MUSIC = "Music" + private const val CATEGORY_RINGTONES = "Ringtones" + private const val CATEGORY_DOCUMENTS = "Documents" + private const val CATEGORY_PODCASTS = "Podcasts" + private const val CATEGORY_ROM_SETTINGS = "Rom settings" + + private const val LOCAL_ROM_SETTINGS_PATH = "/data/system/users/0/" + private val REMOTE_ROM_SETTINGS_PATH = PATH_SEPARATOR + "Devices" + PATH_SEPARATOR + BRAND + "_" + MODEL + "_" + Build.getSerial() + "/rom_settings/" + private val REMOTE_APP_LIST_PATH = REMOTE_ROM_SETTINGS_PATH + "app_list/" + + fun getSyncedFolderRoots(context : Context): ArrayList { + val syncedFolders = ArrayList() + val categories = getSyncableCategories() + + categories.forEach { + when (it) { + CATEGORY_IMAGES -> { + syncedFolders.add(createPhotosSyncedFolder()) + syncedFolders.add(createPicturesSyncedFolder()) + } + CATEGORY_MOVIES -> syncedFolders.add(createMovieSyncedFolder()) + CATEGORY_DOCUMENTS -> syncedFolders.add(createDocumentsSyncedFolder()) + CATEGORY_MUSIC -> syncedFolders.add(createMusicsSyncedFolder()) + CATEGORY_PODCASTS -> syncedFolders.add(createPodcastsSyncedFolder()) + CATEGORY_RINGTONES -> syncedFolders.add(createRingtonesSyncedFolder()) + CATEGORY_ROM_SETTINGS -> { + syncedFolders.add(createRomSettingsSyncedFolder()) + syncedFolders.add(createAppListSyncedFolder(context)) + } + } + } + return syncedFolders + } + + private fun getSyncableCategories(): List { + return listOf(CATEGORY_IMAGES, + CATEGORY_MOVIES, + CATEGORY_MUSIC, + CATEGORY_RINGTONES, + CATEGORY_DOCUMENTS, + CATEGORY_PODCASTS, + CATEGORY_ROM_SETTINGS) + } + + private fun createPhotosSyncedFolder(): SyncedFolder { + return createMediaSyncedFolder(CATEGORY_IMAGES, DIRECTORY_DCIM, "/Photos/") + } + + private fun createPicturesSyncedFolder(): SyncedFolder { + return createMediaSyncedFolder(CATEGORY_IMAGES, DIRECTORY_PICTURES, "/Pictures/") + } + + private fun createMovieSyncedFolder(): SyncedFolder { + return createMediaSyncedFolder(CATEGORY_MOVIES, DIRECTORY_MOVIES, "/Movies/") + } + + private fun createDocumentsSyncedFolder(): SyncedFolder { + return createMediaSyncedFolder(CATEGORY_DOCUMENTS, DIRECTORY_DOCUMENTS, "/Documents/") + } + + private fun createMusicsSyncedFolder(): SyncedFolder { + return createMediaSyncedFolder(CATEGORY_MUSIC, DIRECTORY_MUSIC, "/Music/") + } + + private fun createRingtonesSyncedFolder(): SyncedFolder { + return createMediaSyncedFolder(CATEGORY_RINGTONES, DIRECTORY_RINGTONES, "/Ringtones/") + } + + private fun createPodcastsSyncedFolder(): SyncedFolder { + return createMediaSyncedFolder(CATEGORY_PODCASTS, DIRECTORY_PODCASTS, "/Podcasts/") + } + + private fun createRomSettingsSyncedFolder(): SyncedFolder { + return createSettingsSyncedFolder(CATEGORY_ROM_SETTINGS, LOCAL_ROM_SETTINGS_PATH, REMOTE_ROM_SETTINGS_PATH) + } + + private fun createAppListSyncedFolder(context: Context): SyncedFolder { + val localPath = context.filesDir.absolutePath + PATH_SEPARATOR + return createSettingsSyncedFolder(CATEGORY_ROM_SETTINGS, localPath, REMOTE_APP_LIST_PATH) + } + + private fun createMediaSyncedFolder(category: String, publicDirectoryType: String, remotePath: String): SyncedFolder { + val dirPath = CommonUtils.getLocalPath(Environment.getExternalStoragePublicDirectory(publicDirectoryType)) + val localPath = dirPath + PATH_SEPARATOR + return SyncedFolder(category, localPath, remotePath, true) + } + + private fun createSettingsSyncedFolder(category: String, localPath: String, remotePath: String): SyncedFolder { + return SyncedFolder(category, localPath, remotePath, true, false, true, false) + } +} \ No newline at end of file -- GitLab