Loading app/src/main/java/foundation/e/drive/EdriveApplication.java +23 −5 Original line number Diff line number Diff line /* * 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 <https://www.gnu.org/licenses/>. */ package foundation.e.drive; Loading @@ -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; Loading Loading @@ -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() { Loading app/src/main/java/foundation/e/drive/account/AccountAdder.kt 0 → 100644 +104 −0 Original line number Diff line number Diff line /* * 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 <https://www.gnu.org/licenses/>. * */ 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 } } app/src/main/java/foundation/e/drive/account/AccountRemover.kt 0 → 100644 +116 −0 Original line number Diff line number Diff line /* * 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 <https://www.gnu.org/licenses/>. * */ 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") } } } app/src/main/java/foundation/e/drive/account/receivers/AccountAddedReceiver.kt +19 −70 Original line number Diff line number Diff line /* * 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 <https://www.gnu.org/licenses/>. */ package foundation.e.drive.account.receivers Loading @@ -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 /** Loading @@ -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") Loading @@ -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) } } app/src/main/java/foundation/e/drive/account/receivers/AccountRemoveCallbackReceiver.java +21 −93 Original line number Diff line number Diff line /* * 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 <https://www.gnu.org/licenses/>. */ 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) { Loading @@ -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"); Loading @@ -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()); } } Loading
app/src/main/java/foundation/e/drive/EdriveApplication.java +23 −5 Original line number Diff line number Diff line /* * 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 <https://www.gnu.org/licenses/>. */ package foundation.e.drive; Loading @@ -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; Loading Loading @@ -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() { Loading
app/src/main/java/foundation/e/drive/account/AccountAdder.kt 0 → 100644 +104 −0 Original line number Diff line number Diff line /* * 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 <https://www.gnu.org/licenses/>. * */ 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 } }
app/src/main/java/foundation/e/drive/account/AccountRemover.kt 0 → 100644 +116 −0 Original line number Diff line number Diff line /* * 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 <https://www.gnu.org/licenses/>. * */ 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") } } }
app/src/main/java/foundation/e/drive/account/receivers/AccountAddedReceiver.kt +19 −70 Original line number Diff line number Diff line /* * 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 <https://www.gnu.org/licenses/>. */ package foundation.e.drive.account.receivers Loading @@ -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 /** Loading @@ -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") Loading @@ -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) } }
app/src/main/java/foundation/e/drive/account/receivers/AccountRemoveCallbackReceiver.java +21 −93 Original line number Diff line number Diff line /* * 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 <https://www.gnu.org/licenses/>. */ 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) { Loading @@ -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"); Loading @@ -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()); } }