diff --git a/app/src/main/AndroidManifest.xml b/app/src/main/AndroidManifest.xml
index 6bb8b5acc6d70b7060bcf2634d301ef100bd82d5..7639ff95c59e87023f25d8953b84e0818d3dbdfc 100644
--- a/app/src/main/AndroidManifest.xml
+++ b/app/src/main/AndroidManifest.xml
@@ -125,4 +125,4 @@
android:foregroundServiceType="dataSync"
tools:node="merge" />
-
\ No newline at end of file
+
diff --git a/app/src/main/java/foundation/e/drive/EdriveApplication.java b/app/src/main/java/foundation/e/drive/EdriveApplication.java
index 853607358fa03a933ba38ca35b9b328df0dcd970..ad9eb5c2b7bc959c381d7073892d36d371f70aa9 100644
--- a/app/src/main/java/foundation/e/drive/EdriveApplication.java
+++ b/app/src/main/java/foundation/e/drive/EdriveApplication.java
@@ -1,9 +1,19 @@
/*
- * Copyright © ECORP SAS 2022-2023.
- * All rights reserved. This program and the accompanying materials
- * are made available under the terms of the GNU Public License v3.0
- * which accompanies this distribution, and is available at
- * http://www.gnu.org/licenses/gpl.html
+ * Copyright (C) 2025 e Foundation
+ * Copyright (C) ECORP SAS 2022-2023
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation, either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program. If not, see .
*/
package foundation.e.drive;
@@ -20,6 +30,7 @@ import androidx.annotation.NonNull;
import foundation.e.drive.database.FailedSyncPrefsManager;
import foundation.e.drive.fileObservers.FileObserverManager;
+import foundation.e.drive.recovery.RecoveryManager;
import foundation.e.drive.utils.AppConstants;
import foundation.e.drive.utils.CommonUtils;
import foundation.e.drive.utils.ReleaseTree;
@@ -59,6 +70,13 @@ public class EdriveApplication extends Application {
}
FailedSyncPrefsManager.getInstance(getApplicationContext()).clearPreferences();
+
+ setupEdriveRecovery();
+ }
+
+ private void setupEdriveRecovery() {
+ RecoveryManager recoveryManager = new RecoveryManager(getApplicationContext());
+ recoveryManager.initiateRecovery();
}
synchronized public void startRecursiveFileObserver() {
diff --git a/app/src/main/java/foundation/e/drive/account/AccountAdder.kt b/app/src/main/java/foundation/e/drive/account/AccountAdder.kt
new file mode 100644
index 0000000000000000000000000000000000000000..0cbba53068d7bb02b3b1c3fa097be27e19c794d2
--- /dev/null
+++ b/app/src/main/java/foundation/e/drive/account/AccountAdder.kt
@@ -0,0 +1,104 @@
+/*
+ * Copyright (C) 2025 e Foundation
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation, either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program. If not, see .
+ *
+ */
+
+package foundation.e.drive.account
+
+import android.accounts.AccountManager
+import android.content.Context
+import android.content.SharedPreferences
+import foundation.e.drive.R
+import foundation.e.drive.utils.AppConstants
+import foundation.e.drive.utils.DavClientProvider
+import foundation.e.drive.work.WorkLauncher
+import timber.log.Timber
+
+class AccountAdder(private val context: Context) {
+
+ private val preferences: SharedPreferences by lazy {
+ context.applicationContext.getSharedPreferences(
+ AppConstants.SHARED_PREFERENCE_NAME, Context.MODE_PRIVATE
+ )
+ }
+
+ fun addAccount(name: String, type: String) {
+ if (!canStart(name, type, preferences, context)) return
+
+ updateAccountNameOnPreference(name)
+
+ val workLauncher = WorkLauncher.getInstance(context)
+ if (workLauncher.enqueueSetupWorkers(context)) {
+ DavClientProvider.getInstance().cleanUp()
+ workLauncher.enqueuePeriodicUserInfoFetching()
+ }
+ }
+
+ private fun updateAccountNameOnPreference(name: String) {
+ preferences.edit()
+ .putString(AccountManager.KEY_ACCOUNT_NAME, name)
+ .apply()
+ }
+
+ /**
+ * Check that conditions to start are met:
+ * - Setup has not already been done
+ * - AccountName is not empty
+ * - AccountType is /e/ account
+ * - the account is effectively available through accountManager
+ */
+ private fun canStart(
+ accountName: String,
+ accountType: String,
+ prefs: SharedPreferences,
+ context: Context
+ ): Boolean {
+ if (isSetupAlreadyDone(prefs)) {
+ Timber.w("Set up is already done, skipping account addition.")
+ return false
+ }
+
+ if (accountName.isEmpty()) {
+ Timber.w("Account name is empty, skipping account addition.")
+ return false
+ }
+
+ if (isInvalidAccountType(accountType, context)) {
+ Timber.w("Account type: $accountType is invalid, skipping account addition.")
+ return false
+ }
+
+ if (!isExistingAccount(accountName, context)) {
+ Timber.w("No account exists for $accountName, skipping account addition.")
+ return false
+ }
+
+ return true
+ }
+
+ private fun isSetupAlreadyDone(prefs: SharedPreferences): Boolean {
+ return prefs.getBoolean(AppConstants.SETUP_COMPLETED, false)
+ }
+
+ private fun isInvalidAccountType(accountType: String, context: Context): Boolean {
+ val validAccountType = context.getString(R.string.eelo_account_type)
+ return accountType != validAccountType
+ }
+
+ private fun isExistingAccount(accountName: String, context: Context): Boolean {
+ return AccountUtils.getAccount(accountName, context) != null
+ }
+}
diff --git a/app/src/main/java/foundation/e/drive/account/AccountRemover.kt b/app/src/main/java/foundation/e/drive/account/AccountRemover.kt
new file mode 100644
index 0000000000000000000000000000000000000000..fe6acc51c576be4469d5d8e9c5e3a32c1cf22315
--- /dev/null
+++ b/app/src/main/java/foundation/e/drive/account/AccountRemover.kt
@@ -0,0 +1,116 @@
+/*
+ * Copyright (C) 2025 e Foundation
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation, either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program. If not, see .
+ *
+ */
+
+package foundation.e.drive.account
+
+import android.accounts.AccountManager
+import android.app.Application
+import android.app.NotificationManager
+import android.content.Context
+import android.content.SharedPreferences
+import androidx.work.WorkManager
+import foundation.e.drive.database.DbHelper
+import foundation.e.drive.database.FailedSyncPrefsManager
+import foundation.e.drive.synchronization.SyncProxy
+import foundation.e.drive.utils.AppConstants
+import foundation.e.drive.utils.AppConstants.INITIAL_FOLDER_NUMBER
+import foundation.e.drive.utils.AppConstants.SETUP_COMPLETED
+import foundation.e.drive.utils.DavClientProvider
+import timber.log.Timber
+import java.io.File
+
+class AccountRemover(
+ private val context: Context,
+) {
+ private val preferences: SharedPreferences by lazy {
+ context.applicationContext.getSharedPreferences(
+ AppConstants.SHARED_PREFERENCE_NAME, Context.MODE_PRIVATE
+ )
+ }
+
+ fun removeAccount() {
+ cancelWorkers()
+ setSyncStateToIdle()
+ deleteDatabase()
+ cleanSharedPreferences()
+ removeCachedFiles()
+ deleteNotificationChannels()
+ cleanUpDavClient()
+ }
+
+ private fun cleanUpDavClient() {
+ DavClientProvider.getInstance().cleanUp()
+ }
+
+ private fun setSyncStateToIdle() {
+ SyncProxy.moveToIdle(context.applicationContext as Application)
+ }
+
+ private fun cancelWorkers() {
+ val workManager = WorkManager.getInstance(context)
+ workManager.cancelAllWorkByTag(AppConstants.WORK_GENERIC_TAG)
+ }
+
+ private fun deleteDatabase() {
+ val result = context.applicationContext.deleteDatabase(DbHelper.DATABASE_NAME)
+ Timber.d("Remove Database: %s", result)
+ }
+
+ private fun cleanSharedPreferences() {
+ if (!context.applicationContext.deleteSharedPreferences(AppConstants.SHARED_PREFERENCE_NAME)) {
+ //If removal failed, clear all data inside
+ preferences.edit().remove(AccountManager.KEY_ACCOUNT_NAME)
+ .remove(AccountManager.KEY_ACCOUNT_TYPE)
+ .remove(SETUP_COMPLETED)
+ .remove(INITIAL_FOLDER_NUMBER)
+ .remove(AppConstants.KEY_LAST_SCAN_TIME)
+ .apply()
+ }
+ context.applicationContext.deleteSharedPreferences(FailedSyncPrefsManager.PREF_NAME)
+ }
+
+ private fun removeCachedFiles() {
+ try {
+ deleteDir(context.applicationContext.externalCacheDir)
+ } catch (e: SecurityException) {
+ Timber.e(e, "failed to delete cached file on account removal call")
+ }
+ }
+
+ @Throws(SecurityException::class)
+ private fun deleteDir(dir: File?): Boolean {
+ if (dir == null) {
+ Timber.w("cache file returned null. preventing a NPE")
+ return false
+ }
+
+ return dir.deleteRecursively()
+ }
+
+ @Suppress("TooGenericExceptionCaught")
+ private fun deleteNotificationChannels() {
+ val notificationManager =
+ context.getSystemService(Context.NOTIFICATION_SERVICE) as NotificationManager
+
+ try {
+ notificationManager.cancelAll()
+ } catch (exception: RuntimeException) {
+ Timber.e(exception, "Cannot cancel all notifications")
+ }
+ }
+}
diff --git a/app/src/main/java/foundation/e/drive/account/receivers/AccountAddedReceiver.kt b/app/src/main/java/foundation/e/drive/account/receivers/AccountAddedReceiver.kt
index b2c51e5b84ea344b3570eb9140cf1692c7fd282f..7ed75c7a128516cd4b34bd9270be7f26628e87bc 100644
--- a/app/src/main/java/foundation/e/drive/account/receivers/AccountAddedReceiver.kt
+++ b/app/src/main/java/foundation/e/drive/account/receivers/AccountAddedReceiver.kt
@@ -1,9 +1,19 @@
/*
- * Copyright © MURENA SAS 2023-2024.
- * 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
+ * Copyright (C) 2025 e Foundation
+ * Copyright (C) MURENA SAS 2023-2024
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation, either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program. If not, see .
*/
package foundation.e.drive.account.receivers
@@ -11,12 +21,7 @@ import android.accounts.AccountManager
import android.content.BroadcastReceiver
import android.content.Context
import android.content.Intent
-import android.content.SharedPreferences
-import foundation.e.drive.R
-import foundation.e.drive.account.AccountUtils
-import foundation.e.drive.utils.AppConstants
-import foundation.e.drive.utils.DavClientProvider
-import foundation.e.drive.work.WorkLauncher
+import foundation.e.drive.account.AccountAdder
import timber.log.Timber
/**
@@ -24,7 +29,7 @@ import timber.log.Timber
* Triggered by AccountManager
* @author Vincent Bourgmayer
*/
-class AccountAddedReceiver() : BroadcastReceiver() {
+class AccountAddedReceiver : BroadcastReceiver() {
override fun onReceive(context: Context?, intent: Intent?) {
Timber.d("\"Account added\" intent received")
@@ -33,66 +38,10 @@ class AccountAddedReceiver() : BroadcastReceiver() {
val extras = intent.extras!!
val accountName = extras.getString(AccountManager.KEY_ACCOUNT_NAME, "")
val accountType = extras.getString(AccountManager.KEY_ACCOUNT_TYPE, "")
- val prefs = context.getSharedPreferences(AppConstants.SHARED_PREFERENCE_NAME,
- Context.MODE_PRIVATE)
Timber.d("AccountAddedReceiver.onReceive with name: %s and type: %s", accountName, accountType)
- if (!canStart(accountName, accountType, prefs, context)) return
-
- prefs.edit()
- .putString(AccountManager.KEY_ACCOUNT_NAME, accountName)
- .apply()
-
- val workLauncher = WorkLauncher.getInstance(context)
- if (workLauncher.enqueueSetupWorkers(context)) {
- DavClientProvider.getInstance().cleanUp()
- workLauncher.enqueuePeriodicUserInfoFetching()
- }
-}
-
- /**
- * Check that conditions to start are met:
- * - Setup has not already been done
- * - AccountName is not empty
- * - AccountType is /e/ account
- * - the account is effectively available through accountManager
- */
- private fun canStart(
- accountName: String,
- accountType: String,
- prefs: SharedPreferences,
- context: Context
- ): Boolean {
- if (isSetupAlreadyDone(prefs)) {
- return false
- }
-
- if (accountName.isEmpty()) {
- return false
- }
-
- if (isInvalidAccountType(accountType, context)) {
- return false
- }
-
- if (!isExistingAccount(accountName, context)) {
- Timber.w("No account exist for username: %s ", accountType, accountName)
- return false
- }
- return true
- }
-
- private fun isInvalidAccountType(accountType: String, context: Context): Boolean {
- val validAccountType = context.getString(R.string.eelo_account_type)
- return accountType != validAccountType
- }
-
- private fun isSetupAlreadyDone(prefs: SharedPreferences): Boolean {
- return prefs.getBoolean(AppConstants.SETUP_COMPLETED, false)
- }
-
- private fun isExistingAccount(accountName: String, context: Context): Boolean {
- return AccountUtils.getAccount(accountName, context) != null
+ val accountAdder = AccountAdder(context)
+ accountAdder.addAccount(accountName, accountType)
}
}
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 4ff1ed1ef1f24ec0d7156511b9d66f0387ac3431..c8f16a69000fcd2364b837f2a1e50d5caa6a1ede 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
@@ -1,42 +1,42 @@
/*
- * Copyright © ECORP 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
+ * Copyright (C) 2025 e Foundation
+ * Copyright (C) ECORP SAS 2023
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation, either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program. If not, see .
*/
package foundation.e.drive.account.receivers;
-import static foundation.e.drive.utils.AppConstants.INITIAL_FOLDER_NUMBER;
-import static foundation.e.drive.utils.AppConstants.SETUP_COMPLETED;
-
import android.accounts.AccountManager;
import android.annotation.SuppressLint;
-import android.app.Application;
-import android.app.NotificationManager;
import android.content.BroadcastReceiver;
import android.content.Context;
import android.content.Intent;
import android.content.SharedPreferences;
import androidx.annotation.NonNull;
-import androidx.annotation.Nullable;
-import androidx.work.WorkManager;
-
-import java.io.File;
import foundation.e.drive.R;
-import foundation.e.drive.database.DbHelper;
-import foundation.e.drive.database.FailedSyncPrefsManager;
-import foundation.e.drive.synchronization.SyncProxy;
+import foundation.e.drive.account.AccountRemover;
import foundation.e.drive.utils.AppConstants;
-import foundation.e.drive.utils.DavClientProvider;
import foundation.e.drive.utils.ViewUtils;
import timber.log.Timber;
public class AccountRemoveCallbackReceiver extends BroadcastReceiver {
+ private static final String ACTION_ACCOUNT_REMOVED = "android.accounts.action.ACCOUNT_REMOVED";
+
@SuppressLint("UnsafeProtectedBroadcastReceiver")
@Override
public void onReceive(@NonNull Context context, @NonNull Intent intent) {
@@ -50,29 +50,12 @@ public class AccountRemoveCallbackReceiver extends BroadcastReceiver {
return;
}
- cancelWorkers(applicationContext);
- SyncProxy.INSTANCE.moveToIdle((Application) applicationContext);
- deleteDatabase(applicationContext);
- cleanSharedPreferences(applicationContext, preferences);
- removeCachedFiles(applicationContext);
- deleteNotificationChannels(applicationContext);
-
- DavClientProvider.getInstance().cleanUp();
+ AccountRemover accountRemover = new AccountRemover(context);
+ accountRemover.removeAccount();
ViewUtils.updateWidgetView(applicationContext);
}
-
- private void cancelWorkers(@NonNull Context context) {
- final WorkManager workManager = WorkManager.getInstance(context);
- workManager.cancelAllWorkByTag(AppConstants.WORK_GENERIC_TAG);
- }
-
- private void deleteDatabase(@NonNull Context applicationContext) {
- final boolean result = applicationContext.deleteDatabase(DbHelper.DATABASE_NAME);
- Timber.d("Remove Database: %s", result);
- }
-
private boolean shouldProceedWithRemoval(@NonNull Intent intent, @NonNull SharedPreferences preferences, @NonNull Context context) {
if (isInvalidAction(intent) || intent.getExtras() == null) {
Timber.w("Invalid account removal request");
@@ -94,61 +77,6 @@ public class AccountRemoveCallbackReceiver extends BroadcastReceiver {
}
private boolean isInvalidAction(@NonNull Intent intent) {
- return !"android.accounts.action.ACCOUNT_REMOVED".equals(intent.getAction());
- }
-
- private void cleanSharedPreferences(@NonNull Context applicationContext, @NonNull SharedPreferences prefs) {
- if (!applicationContext.deleteSharedPreferences(AppConstants.SHARED_PREFERENCE_NAME)) {
- //If removal failed, clear all data inside
- prefs.edit().remove(AccountManager.KEY_ACCOUNT_NAME)
- .remove(AccountManager.KEY_ACCOUNT_TYPE)
- .remove(SETUP_COMPLETED)
- .remove(INITIAL_FOLDER_NUMBER)
- .remove(AppConstants.KEY_LAST_SCAN_TIME)
- .apply();
- }
-
- applicationContext.deleteSharedPreferences(FailedSyncPrefsManager.PREF_NAME);
- }
-
- private void removeCachedFiles(@NonNull Context applicationContext) {
- try {
- deleteDir(applicationContext.getExternalCacheDir());
- } catch (SecurityException e) {
- Timber.e(e, "failed to delete cached file on account removal call");
- }
- }
-
- private boolean deleteDir(@Nullable File dir) throws SecurityException {
- if (dir == null) {
- Timber.w("cache file returned null. preventing a NPE");
- return false;
- }
-
- if (dir.isDirectory()) {
- String[] children = dir.list();
- if (children == null) {
- return dir.delete();
- }
-
- for (String child : children) {
- boolean isSuccess = deleteDir(new File(dir, child));
- if (!isSuccess) {
- Timber.w("Failed to remove the cached file: %s", child);
- }
- }
- }
-
- return dir.delete();
- }
-
- private void deleteNotificationChannels(Context context) {
- NotificationManager notificationManager = (NotificationManager) context.getSystemService(Context.NOTIFICATION_SERVICE);
-
- try {
- notificationManager.cancelAll();
- } catch (Exception exception) {
- Timber.e(exception, "Cannot cancel all notifications");
- }
+ return !ACTION_ACCOUNT_REMOVED.equals(intent.getAction());
}
}
diff --git a/app/src/main/java/foundation/e/drive/recovery/RecoveryManager.kt b/app/src/main/java/foundation/e/drive/recovery/RecoveryManager.kt
new file mode 100644
index 0000000000000000000000000000000000000000..499ec7bae73540a9553c3d4d82fc4ee36968a665
--- /dev/null
+++ b/app/src/main/java/foundation/e/drive/recovery/RecoveryManager.kt
@@ -0,0 +1,74 @@
+/*
+ * Copyright (C) 2025 e Foundation
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation, either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program. If not, see .
+ */
+
+package foundation.e.drive.recovery
+
+import android.content.Context
+import foundation.e.drive.R
+import foundation.e.drive.account.AccountAdder
+import foundation.e.drive.account.AccountRemover
+import foundation.e.drive.account.AccountUtils.getAccount
+import foundation.e.drive.recovery.RecoveryPreferences.RecoveryStatus.RecoveryCompleted
+import foundation.e.drive.recovery.RecoveryPreferences.RecoveryStatus.RecoveryNeeded
+import timber.log.Timber
+
+class RecoveryManager(
+ private val context: Context,
+) {
+ private val accountRemover = AccountRemover(context)
+ private val accountAdder = AccountAdder(context)
+
+ fun initiateRecovery() {
+ if (isRecoveryNeeded()) {
+ val (accountName, accountType) = getUserAccountInfo(context)
+
+ if (accountName.isNotEmpty()) {
+ Timber.i("Initiating data recovery for eDrive.")
+ logoutUser()
+ loginUser(accountName, accountType)
+ updateRecoveryStatus(RecoveryCompleted)
+ }
+ }
+ }
+
+ private fun updateRecoveryStatus(status: RecoveryPreferences.RecoveryStatus) {
+ RecoveryPreferences.updateRecoveryStatus(context, status)
+ Timber.i("Updating eDrive recovery status to $status.")
+ }
+
+ private fun isRecoveryNeeded(): Boolean = when (RecoveryPreferences.isRecoveryNeeded(context)) {
+ RecoveryNeeded -> true
+ RecoveryCompleted -> false
+ }
+
+ private fun getUserAccountInfo(context: Context): Pair {
+ val accountName = getAccount(context.applicationContext)?.name ?: ""
+ val accountType: String = context.getString(R.string.eelo_account_type)
+
+ return Pair(accountName, accountType)
+ }
+
+ private fun loginUser(accountName: String, accountType: String) {
+ accountAdder.addAccount(accountName, accountType)
+ Timber.i("Added Account($accountName, $accountType) to eDrive.")
+ }
+
+ private fun logoutUser() {
+ accountRemover.removeAccount()
+ Timber.i("Account removed from eDrive.")
+ }
+}
diff --git a/app/src/main/java/foundation/e/drive/recovery/RecoveryPreferences.kt b/app/src/main/java/foundation/e/drive/recovery/RecoveryPreferences.kt
new file mode 100644
index 0000000000000000000000000000000000000000..8824e7786599bd5419bdbf91c6bd0d80b993365f
--- /dev/null
+++ b/app/src/main/java/foundation/e/drive/recovery/RecoveryPreferences.kt
@@ -0,0 +1,60 @@
+/*
+ * Copyright (C) 2025 e Foundation
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation, either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program. If not, see .
+ */
+
+package foundation.e.drive.recovery
+
+import android.content.Context
+import foundation.e.drive.recovery.RecoveryPreferences.RecoveryStatus.RecoveryCompleted
+import foundation.e.drive.recovery.RecoveryPreferences.RecoveryStatus.RecoveryNeeded
+
+object RecoveryPreferences {
+
+ private const val PREFERENCES_NAME = "edrive_recovery"
+ private const val KEY_IS_DATA_ALREADY_RECOVERED = "is_data_already_recovered"
+
+ @JvmStatic
+ fun isRecoveryNeeded(context: Context): RecoveryStatus {
+ val preferences = context.getSharedPreferences(PREFERENCES_NAME, Context.MODE_PRIVATE)
+ val isDataAlreadyRecovered = preferences.getBoolean(KEY_IS_DATA_ALREADY_RECOVERED, false)
+
+ return if (isDataAlreadyRecovered) {
+ RecoveryCompleted
+ } else {
+ RecoveryNeeded
+ }
+ }
+
+ @JvmStatic
+ fun updateRecoveryStatus(context: Context, status: RecoveryStatus) {
+ val preferences =
+ context.getSharedPreferences(PREFERENCES_NAME, Context.MODE_PRIVATE)
+ when (status) {
+ RecoveryCompleted -> {
+ preferences.edit().putBoolean(KEY_IS_DATA_ALREADY_RECOVERED, true).apply()
+ }
+
+ RecoveryNeeded -> {
+ preferences.edit().putBoolean(KEY_IS_DATA_ALREADY_RECOVERED, false).apply()
+ }
+ }
+ }
+
+ enum class RecoveryStatus {
+ RecoveryNeeded,
+ RecoveryCompleted
+ }
+}
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 57a107de508581d82275b4245004088a0169fece..7daadb73103b0780f947ae513a6f5a1022c0756e 100644
--- a/app/src/main/java/foundation/e/drive/utils/AppConstants.kt
+++ b/app/src/main/java/foundation/e/drive/utils/AppConstants.kt
@@ -1,10 +1,20 @@
/*
- * 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
+ * Copyright (C) 2025 e Foundation
+ * Copyright (C) MURENA SAS 2022-2023
+ * Copyright (C) CLEUS SAS 2018-2019
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation, either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program. If not, see .
*/
package foundation.e.drive.utils