From 0e4153cccd9e8d1eef949d4ca7fc2f34bfcc2abf Mon Sep 17 00:00:00 2001 From: Fahim Masud Choudhury Date: Fri, 24 Jan 2025 19:43:22 +0600 Subject: [PATCH 1/8] feat: Implement device to cloud push for all the files needed for Murena.io cloud recovery eDrive checks if a recovery is needed and logs out and logs in the user to delete and recreate its database. It uses two broadcasts to trigger account removed and account added receivers. Because of the recreation of the database, all the files and (syncable) folders are pushed to cloud storage. When the process completes, a separate SharedPreference is used to track if recovery is needed or not. It is done so because if user logs out and logs in from AccountManager, eDrive's initial preference is cleared. However, this recovery preference will not be cleared unless user explicitly clears eDrive's data. --- app/src/main/AndroidManifest.xml | 5 +- .../foundation/e/drive/EdriveApplication.java | 28 +++++- .../account/receivers/AccountAddedReceiver.kt | 30 +++++- .../AccountRemoveCallbackReceiver.java | 32 ++++-- .../e/drive/recovery/RecoveryManager.kt | 97 +++++++++++++++++++ .../e/drive/recovery/RecoveryPreferences.kt | 60 ++++++++++++ .../foundation/e/drive/utils/AppConstants.kt | 21 ++-- 7 files changed, 250 insertions(+), 23 deletions(-) create mode 100644 app/src/main/java/foundation/e/drive/recovery/RecoveryManager.kt create mode 100644 app/src/main/java/foundation/e/drive/recovery/RecoveryPreferences.kt diff --git a/app/src/main/AndroidManifest.xml b/app/src/main/AndroidManifest.xml index 6bb8b5ac..23b9d8e2 100644 --- a/app/src/main/AndroidManifest.xml +++ b/app/src/main/AndroidManifest.xml @@ -110,6 +110,9 @@ + + + - \ 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 85360735..fcf82f0c 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) 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; @@ -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/receivers/AccountAddedReceiver.kt b/app/src/main/java/foundation/e/drive/account/receivers/AccountAddedReceiver.kt index b2c51e5b..974e747d 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 @@ -14,6 +24,8 @@ import android.content.Intent import android.content.SharedPreferences import foundation.e.drive.R import foundation.e.drive.account.AccountUtils +import foundation.e.drive.recovery.RecoveryManager +import foundation.e.drive.recovery.RecoveryPreferences.RecoveryStatus.RecoveryCompleted import foundation.e.drive.utils.AppConstants import foundation.e.drive.utils.DavClientProvider import foundation.e.drive.work.WorkLauncher @@ -48,9 +60,17 @@ class AccountAddedReceiver() : BroadcastReceiver() { if (workLauncher.enqueueSetupWorkers(context)) { DavClientProvider.getInstance().cleanUp() workLauncher.enqueuePeriodicUserInfoFetching() + handleDataRecovery(context) } } + private fun handleDataRecovery(context: Context) { + val recoveryManager = RecoveryManager(context) + if (recoveryManager.isRecoveryNeeded()) { + recoveryManager.updateRecoveryStatus(RecoveryCompleted) + } + } + /** * Check that conditions to start are met: * - Setup has not already been done 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 4ff1ed1e..0079d2eb 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,9 +1,19 @@ /* - * 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) 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; @@ -25,6 +35,8 @@ import androidx.annotation.Nullable; import androidx.work.WorkManager; import java.io.File; +import java.util.ArrayList; +import java.util.List; import foundation.e.drive.R; import foundation.e.drive.database.DbHelper; @@ -94,7 +106,15 @@ public class AccountRemoveCallbackReceiver extends BroadcastReceiver { } private boolean isInvalidAction(@NonNull Intent intent) { - return !"android.accounts.action.ACCOUNT_REMOVED".equals(intent.getAction()); + List actions = new ArrayList<>(); + actions.add("android.accounts.action.ACCOUNT_REMOVED"); + actions.add("foundation.e.drive.action.ACCOUNT_REMOVED"); + + String intentAction = intent.getAction(); + + boolean hasNecessaryIntentAction = actions.contains(intentAction); + + return !hasNecessaryIntentAction; } private void cleanSharedPreferences(@NonNull Context applicationContext, @NonNull SharedPreferences prefs) { 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 00000000..fffd000b --- /dev/null +++ b/app/src/main/java/foundation/e/drive/recovery/RecoveryManager.kt @@ -0,0 +1,97 @@ +/* + * 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.accounts.AccountManager +import android.content.ComponentName +import android.content.Context +import android.content.Intent +import foundation.e.drive.R +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, +) { + + fun initiateRecovery() { + if (isRecoveryNeeded()) { + val (accountName, accountType) = getUserAccountInfo(context) + + if (accountName.isNotEmpty()) { + Timber.d("Initiating data recovery for eDrive.") + logoutUser(accountName, accountType) + loginUser(accountName, accountType) + } + } + } + + fun updateRecoveryStatus(status: RecoveryPreferences.RecoveryStatus) { + RecoveryPreferences.updateRecoveryStatus(context, status) + Timber.d("Updating recovery status to $status.") + } + + 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) { + val accountAddedIntent = Intent("foundation.e.drive.action.ADD_ACCOUNT").apply { + setComponent( + ComponentName( + "foundation.e.drive", + "foundation.e.drive.account.receivers.AccountAddedReceiver" + ) + ) + putExtra(AccountManager.KEY_ACCOUNT_NAME, accountName) + putExtra(AccountManager.KEY_ACCOUNT_TYPE, accountType) + } + context.applicationContext.sendBroadcast(accountAddedIntent) + + Timber.d("Sending foundation.e.drive.action.ADD_ACCOUNT for eDrive data recovery.") + Timber.d("Account name = $accountName, account type = $accountType") + + } + + private fun logoutUser(accountName: String, accountType: String) { + val accountRemovedIntent = Intent("foundation.e.drive.action.ACCOUNT_REMOVED").apply { + setComponent( + ComponentName( + "foundation.e.drive", + "foundation.e.drive.account.receivers.AccountRemoveCallbackReceiver" + ) + ) + putExtra(AccountManager.KEY_ACCOUNT_NAME, accountName) + putExtra(AccountManager.KEY_ACCOUNT_TYPE, accountType) + } + context.applicationContext.sendBroadcast(accountRemovedIntent) + + Timber.d("Sending foundation.e.drive.action.ACCOUNT_REMOVED for eDrive data recovery.") + Timber.d("Account name = $accountName, account type = $accountType") + } +} 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 00000000..8824e778 --- /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 57a107de..8e361cd6 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,19 @@ /* - * 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 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.utils -- GitLab From b434f5a76aee980fb971336afb9bea9ed1405842 Mon Sep 17 00:00:00 2001 From: Fahim Masud Choudhury Date: Wed, 19 Feb 2025 12:07:51 +0600 Subject: [PATCH 2/8] refactor: Update copyright --- app/src/main/java/foundation/e/drive/EdriveApplication.java | 2 +- .../drive/account/receivers/AccountRemoveCallbackReceiver.java | 2 +- app/src/main/java/foundation/e/drive/utils/AppConstants.kt | 3 ++- 3 files changed, 4 insertions(+), 3 deletions(-) diff --git a/app/src/main/java/foundation/e/drive/EdriveApplication.java b/app/src/main/java/foundation/e/drive/EdriveApplication.java index fcf82f0c..ad9eb5c2 100644 --- a/app/src/main/java/foundation/e/drive/EdriveApplication.java +++ b/app/src/main/java/foundation/e/drive/EdriveApplication.java @@ -1,6 +1,6 @@ /* * Copyright (C) 2025 e Foundation - * Copyright (C) MURENA SAS 2023-2024 + * 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 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 0079d2eb..5c39c6dd 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,6 +1,6 @@ /* * Copyright (C) 2025 e Foundation - * Copyright (C) MURENA SAS 2023-2024 + * 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 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 8e361cd6..7daadb73 100644 --- a/app/src/main/java/foundation/e/drive/utils/AppConstants.kt +++ b/app/src/main/java/foundation/e/drive/utils/AppConstants.kt @@ -1,6 +1,7 @@ /* * Copyright (C) 2025 e Foundation - * Copyright (C) MURENA SAS 2023-2024 + * 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 -- GitLab From b02f9a9d8d7db394872db8cbd5da9d2b3537b71c Mon Sep 17 00:00:00 2001 From: Fahim Masud Choudhury Date: Wed, 19 Feb 2025 18:22:25 +0600 Subject: [PATCH 3/8] refactor: extract account addition and removal logic to separate classes --- app/src/main/AndroidManifest.xml | 3 - .../e/drive/account/AccountAdditionHelper.kt | 100 ++++++++++++++ .../e/drive/account/AccountRemovalHelper.kt | 126 ++++++++++++++++++ .../account/receivers/AccountAddedReceiver.kt | 79 +---------- .../AccountRemoveCallbackReceiver.java | 90 +------------ .../e/drive/recovery/RecoveryManager.kt | 53 +++----- 6 files changed, 248 insertions(+), 203 deletions(-) create mode 100644 app/src/main/java/foundation/e/drive/account/AccountAdditionHelper.kt create mode 100644 app/src/main/java/foundation/e/drive/account/AccountRemovalHelper.kt diff --git a/app/src/main/AndroidManifest.xml b/app/src/main/AndroidManifest.xml index 23b9d8e2..7639ff95 100644 --- a/app/src/main/AndroidManifest.xml +++ b/app/src/main/AndroidManifest.xml @@ -110,9 +110,6 @@ - - - . + * + */ + +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 AccountAdditionHelper(private val context: Context) { + + private val preferences: SharedPreferences by lazy { + context.applicationContext.getSharedPreferences( + AppConstants.SHARED_PREFERENCE_NAME, Context.MODE_PRIVATE + ) + } + + fun addAccount(accountName: String, accountType: String) { + if (!canStart(accountName, accountType, preferences, context)) return + + updateAccountNameOnPreference(accountName) + + val workLauncher = WorkLauncher.getInstance(context) + if (workLauncher.enqueueSetupWorkers(context)) { + DavClientProvider.getInstance().cleanUp() + workLauncher.enqueuePeriodicUserInfoFetching() + } + } + + private fun updateAccountNameOnPreference(accountName: String) { + preferences.edit() + .putString(AccountManager.KEY_ACCOUNT_NAME, accountName) + .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)) { + 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 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/AccountRemovalHelper.kt b/app/src/main/java/foundation/e/drive/account/AccountRemovalHelper.kt new file mode 100644 index 00000000..6af50905 --- /dev/null +++ b/app/src/main/java/foundation/e/drive/account/AccountRemovalHelper.kt @@ -0,0 +1,126 @@ +/* + * 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 AccountRemovalHelper( + 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 + } + + if (dir.isDirectory) { + val children = dir.list() ?: return dir.delete() + + for (child in children) { + val isSuccess = deleteDir(File(dir, child)) + if (!isSuccess) { + Timber.w("Failed to remove the cached file: %s", child) + } + } + } + + return dir.delete() + } + + private fun deleteNotificationChannels() { + val notificationManager = + context.getSystemService(Context.NOTIFICATION_SERVICE) as NotificationManager + + try { + notificationManager.cancelAll() + } catch (exception: Exception) { + 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 974e747d..9ff013ff 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 @@ -21,14 +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.recovery.RecoveryManager -import foundation.e.drive.recovery.RecoveryPreferences.RecoveryStatus.RecoveryCompleted -import foundation.e.drive.utils.AppConstants -import foundation.e.drive.utils.DavClientProvider -import foundation.e.drive.work.WorkLauncher +import foundation.e.drive.account.AccountAdditionHelper import timber.log.Timber /** @@ -36,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") @@ -45,74 +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() - handleDataRecovery(context) - } -} - - private fun handleDataRecovery(context: Context) { - val recoveryManager = RecoveryManager(context) - if (recoveryManager.isRecoveryNeeded()) { - recoveryManager.updateRecoveryStatus(RecoveryCompleted) - } - } - - /** - * 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 accountAdditionHelper = AccountAdditionHelper(context) + accountAdditionHelper.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 5c39c6dd..7c9f4676 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 @@ -18,32 +18,21 @@ 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 java.util.ArrayList; import java.util.List; 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.AccountRemovalHelper; import foundation.e.drive.utils.AppConstants; -import foundation.e.drive.utils.DavClientProvider; import foundation.e.drive.utils.ViewUtils; import timber.log.Timber; @@ -62,29 +51,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(); + AccountRemovalHelper accountRemovalHelper = new AccountRemovalHelper(context); + accountRemovalHelper.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"); @@ -108,7 +80,6 @@ public class AccountRemoveCallbackReceiver extends BroadcastReceiver { private boolean isInvalidAction(@NonNull Intent intent) { List actions = new ArrayList<>(); actions.add("android.accounts.action.ACCOUNT_REMOVED"); - actions.add("foundation.e.drive.action.ACCOUNT_REMOVED"); String intentAction = intent.getAction(); @@ -116,59 +87,4 @@ public class AccountRemoveCallbackReceiver extends BroadcastReceiver { return !hasNecessaryIntentAction; } - - 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"); - } - } } diff --git a/app/src/main/java/foundation/e/drive/recovery/RecoveryManager.kt b/app/src/main/java/foundation/e/drive/recovery/RecoveryManager.kt index fffd000b..66e1510d 100644 --- a/app/src/main/java/foundation/e/drive/recovery/RecoveryManager.kt +++ b/app/src/main/java/foundation/e/drive/recovery/RecoveryManager.kt @@ -17,11 +17,10 @@ package foundation.e.drive.recovery -import android.accounts.AccountManager -import android.content.ComponentName import android.content.Context -import android.content.Intent import foundation.e.drive.R +import foundation.e.drive.account.AccountAdditionHelper +import foundation.e.drive.account.AccountRemovalHelper import foundation.e.drive.account.AccountUtils.getAccount import foundation.e.drive.recovery.RecoveryPreferences.RecoveryStatus.RecoveryCompleted import foundation.e.drive.recovery.RecoveryPreferences.RecoveryStatus.RecoveryNeeded @@ -30,25 +29,28 @@ import timber.log.Timber class RecoveryManager( private val context: Context, ) { + private val accountRemovalHelper = AccountRemovalHelper(context) + private val accountAdditionHelper = AccountAdditionHelper(context) fun initiateRecovery() { if (isRecoveryNeeded()) { val (accountName, accountType) = getUserAccountInfo(context) if (accountName.isNotEmpty()) { - Timber.d("Initiating data recovery for eDrive.") - logoutUser(accountName, accountType) + Timber.i("Initiating data recovery for eDrive.") + logoutUser() loginUser(accountName, accountType) + updateRecoveryStatus(RecoveryCompleted) } } } - fun updateRecoveryStatus(status: RecoveryPreferences.RecoveryStatus) { + private fun updateRecoveryStatus(status: RecoveryPreferences.RecoveryStatus) { RecoveryPreferences.updateRecoveryStatus(context, status) - Timber.d("Updating recovery status to $status.") + Timber.i("Updating eDrive recovery status to $status.") } - fun isRecoveryNeeded(): Boolean = when (RecoveryPreferences.isRecoveryNeeded(context)) { + private fun isRecoveryNeeded(): Boolean = when (RecoveryPreferences.isRecoveryNeeded(context)) { RecoveryNeeded -> true RecoveryCompleted -> false } @@ -61,37 +63,12 @@ class RecoveryManager( } private fun loginUser(accountName: String, accountType: String) { - val accountAddedIntent = Intent("foundation.e.drive.action.ADD_ACCOUNT").apply { - setComponent( - ComponentName( - "foundation.e.drive", - "foundation.e.drive.account.receivers.AccountAddedReceiver" - ) - ) - putExtra(AccountManager.KEY_ACCOUNT_NAME, accountName) - putExtra(AccountManager.KEY_ACCOUNT_TYPE, accountType) - } - context.applicationContext.sendBroadcast(accountAddedIntent) - - Timber.d("Sending foundation.e.drive.action.ADD_ACCOUNT for eDrive data recovery.") - Timber.d("Account name = $accountName, account type = $accountType") - + accountAdditionHelper.addAccount(accountName, accountType) + Timber.i("Added Account($accountName, $accountType) to eDrive.") } - private fun logoutUser(accountName: String, accountType: String) { - val accountRemovedIntent = Intent("foundation.e.drive.action.ACCOUNT_REMOVED").apply { - setComponent( - ComponentName( - "foundation.e.drive", - "foundation.e.drive.account.receivers.AccountRemoveCallbackReceiver" - ) - ) - putExtra(AccountManager.KEY_ACCOUNT_NAME, accountName) - putExtra(AccountManager.KEY_ACCOUNT_TYPE, accountType) - } - context.applicationContext.sendBroadcast(accountRemovedIntent) - - Timber.d("Sending foundation.e.drive.action.ACCOUNT_REMOVED for eDrive data recovery.") - Timber.d("Account name = $accountName, account type = $accountType") + private fun logoutUser() { + accountRemovalHelper.removeAccount() + Timber.i("Account removed from eDrive.") } } -- GitLab From eab6745646c3dbe5858a24d7deec61783e74feaf Mon Sep 17 00:00:00 2001 From: Fahim Masud Choudhury Date: Wed, 19 Feb 2025 18:41:43 +0600 Subject: [PATCH 4/8] chore: Suppress detekt at method while handling exception --- .../java/foundation/e/drive/account/AccountRemovalHelper.kt | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/app/src/main/java/foundation/e/drive/account/AccountRemovalHelper.kt b/app/src/main/java/foundation/e/drive/account/AccountRemovalHelper.kt index 6af50905..8c2d6b49 100644 --- a/app/src/main/java/foundation/e/drive/account/AccountRemovalHelper.kt +++ b/app/src/main/java/foundation/e/drive/account/AccountRemovalHelper.kt @@ -19,6 +19,7 @@ package foundation.e.drive.account import android.accounts.AccountManager +import android.annotation.SuppressLint import android.app.Application import android.app.NotificationManager import android.content.Context @@ -113,13 +114,14 @@ class AccountRemovalHelper( return dir.delete() } + @Suppress("TooGenericExceptionCaught") private fun deleteNotificationChannels() { val notificationManager = context.getSystemService(Context.NOTIFICATION_SERVICE) as NotificationManager try { notificationManager.cancelAll() - } catch (exception: Exception) { + } catch (exception: RuntimeException) { Timber.e(exception, "Cannot cancel all notifications") } } -- GitLab From 3472fd2a8ad16fdc20ecbba36b8d5b0da161e922 Mon Sep 17 00:00:00 2001 From: Fahim Masud Choudhury Date: Thu, 20 Feb 2025 18:10:41 +0600 Subject: [PATCH 5/8] refactor: Resolve MR feedbacks --- .../{AccountAdditionHelper.kt => AccountAdder.kt} | 12 ++++++------ .../{AccountRemovalHelper.kt => AccountRemover.kt} | 3 +-- .../drive/account/receivers/AccountAddedReceiver.kt | 6 +++--- .../receivers/AccountRemoveCallbackReceiver.java | 6 +++--- .../foundation/e/drive/recovery/RecoveryManager.kt | 12 ++++++------ 5 files changed, 19 insertions(+), 20 deletions(-) rename app/src/main/java/foundation/e/drive/account/{AccountAdditionHelper.kt => AccountAdder.kt} (88%) rename app/src/main/java/foundation/e/drive/account/{AccountRemovalHelper.kt => AccountRemover.kt} (98%) diff --git a/app/src/main/java/foundation/e/drive/account/AccountAdditionHelper.kt b/app/src/main/java/foundation/e/drive/account/AccountAdder.kt similarity index 88% rename from app/src/main/java/foundation/e/drive/account/AccountAdditionHelper.kt rename to app/src/main/java/foundation/e/drive/account/AccountAdder.kt index 19bf7f39..209700a0 100644 --- a/app/src/main/java/foundation/e/drive/account/AccountAdditionHelper.kt +++ b/app/src/main/java/foundation/e/drive/account/AccountAdder.kt @@ -27,7 +27,7 @@ import foundation.e.drive.utils.DavClientProvider import foundation.e.drive.work.WorkLauncher import timber.log.Timber -class AccountAdditionHelper(private val context: Context) { +class AccountAdder(private val context: Context) { private val preferences: SharedPreferences by lazy { context.applicationContext.getSharedPreferences( @@ -35,10 +35,10 @@ class AccountAdditionHelper(private val context: Context) { ) } - fun addAccount(accountName: String, accountType: String) { - if (!canStart(accountName, accountType, preferences, context)) return + fun addAccount(name: String, type: String) { + if (!canStart(name, type, preferences, context)) return - updateAccountNameOnPreference(accountName) + updateAccountNameOnPreference(name) val workLauncher = WorkLauncher.getInstance(context) if (workLauncher.enqueueSetupWorkers(context)) { @@ -47,9 +47,9 @@ class AccountAdditionHelper(private val context: Context) { } } - private fun updateAccountNameOnPreference(accountName: String) { + private fun updateAccountNameOnPreference(name: String) { preferences.edit() - .putString(AccountManager.KEY_ACCOUNT_NAME, accountName) + .putString(AccountManager.KEY_ACCOUNT_NAME, name) .apply() } diff --git a/app/src/main/java/foundation/e/drive/account/AccountRemovalHelper.kt b/app/src/main/java/foundation/e/drive/account/AccountRemover.kt similarity index 98% rename from app/src/main/java/foundation/e/drive/account/AccountRemovalHelper.kt rename to app/src/main/java/foundation/e/drive/account/AccountRemover.kt index 8c2d6b49..cca1c455 100644 --- a/app/src/main/java/foundation/e/drive/account/AccountRemovalHelper.kt +++ b/app/src/main/java/foundation/e/drive/account/AccountRemover.kt @@ -19,7 +19,6 @@ package foundation.e.drive.account import android.accounts.AccountManager -import android.annotation.SuppressLint import android.app.Application import android.app.NotificationManager import android.content.Context @@ -35,7 +34,7 @@ import foundation.e.drive.utils.DavClientProvider import timber.log.Timber import java.io.File -class AccountRemovalHelper( +class AccountRemover( private val context: Context, ) { private val preferences: SharedPreferences by lazy { 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 9ff013ff..7ed75c7a 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 @@ -21,7 +21,7 @@ import android.accounts.AccountManager import android.content.BroadcastReceiver import android.content.Context import android.content.Intent -import foundation.e.drive.account.AccountAdditionHelper +import foundation.e.drive.account.AccountAdder import timber.log.Timber /** @@ -41,7 +41,7 @@ class AccountAddedReceiver : BroadcastReceiver() { Timber.d("AccountAddedReceiver.onReceive with name: %s and type: %s", accountName, accountType) - val accountAdditionHelper = AccountAdditionHelper(context) - accountAdditionHelper.addAccount(accountName, accountType) + 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 7c9f4676..c5317c29 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 @@ -31,7 +31,7 @@ import java.util.ArrayList; import java.util.List; import foundation.e.drive.R; -import foundation.e.drive.account.AccountRemovalHelper; +import foundation.e.drive.account.AccountRemover; import foundation.e.drive.utils.AppConstants; import foundation.e.drive.utils.ViewUtils; import timber.log.Timber; @@ -51,8 +51,8 @@ public class AccountRemoveCallbackReceiver extends BroadcastReceiver { return; } - AccountRemovalHelper accountRemovalHelper = new AccountRemovalHelper(context); - accountRemovalHelper.removeAccount(); + AccountRemover accountRemover = new AccountRemover(context); + accountRemover.removeAccount(); ViewUtils.updateWidgetView(applicationContext); } diff --git a/app/src/main/java/foundation/e/drive/recovery/RecoveryManager.kt b/app/src/main/java/foundation/e/drive/recovery/RecoveryManager.kt index 66e1510d..499ec7ba 100644 --- a/app/src/main/java/foundation/e/drive/recovery/RecoveryManager.kt +++ b/app/src/main/java/foundation/e/drive/recovery/RecoveryManager.kt @@ -19,8 +19,8 @@ package foundation.e.drive.recovery import android.content.Context import foundation.e.drive.R -import foundation.e.drive.account.AccountAdditionHelper -import foundation.e.drive.account.AccountRemovalHelper +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 @@ -29,8 +29,8 @@ import timber.log.Timber class RecoveryManager( private val context: Context, ) { - private val accountRemovalHelper = AccountRemovalHelper(context) - private val accountAdditionHelper = AccountAdditionHelper(context) + private val accountRemover = AccountRemover(context) + private val accountAdder = AccountAdder(context) fun initiateRecovery() { if (isRecoveryNeeded()) { @@ -63,12 +63,12 @@ class RecoveryManager( } private fun loginUser(accountName: String, accountType: String) { - accountAdditionHelper.addAccount(accountName, accountType) + accountAdder.addAccount(accountName, accountType) Timber.i("Added Account($accountName, $accountType) to eDrive.") } private fun logoutUser() { - accountRemovalHelper.removeAccount() + accountRemover.removeAccount() Timber.i("Account removed from eDrive.") } } -- GitLab From b4e5d09e4d36b8e1602c0ea34c9d31170fb1fa3b Mon Sep 17 00:00:00 2001 From: Fahim Masud Choudhury Date: Fri, 21 Feb 2025 11:47:36 +0600 Subject: [PATCH 6/8] refactor: Use deleteRecursively() method from kotlin.io package --- .../foundation/e/drive/account/AccountRemover.kt | 13 +------------ 1 file changed, 1 insertion(+), 12 deletions(-) diff --git a/app/src/main/java/foundation/e/drive/account/AccountRemover.kt b/app/src/main/java/foundation/e/drive/account/AccountRemover.kt index cca1c455..fe6acc51 100644 --- a/app/src/main/java/foundation/e/drive/account/AccountRemover.kt +++ b/app/src/main/java/foundation/e/drive/account/AccountRemover.kt @@ -99,18 +99,7 @@ class AccountRemover( return false } - if (dir.isDirectory) { - val children = dir.list() ?: return dir.delete() - - for (child in children) { - val isSuccess = deleteDir(File(dir, child)) - if (!isSuccess) { - Timber.w("Failed to remove the cached file: %s", child) - } - } - } - - return dir.delete() + return dir.deleteRecursively() } @Suppress("TooGenericExceptionCaught") -- GitLab From 463fb5679601aa07331fd90f076865bf0882b2ce Mon Sep 17 00:00:00 2001 From: Fahim Masud Choudhury Date: Fri, 21 Feb 2025 11:57:35 +0600 Subject: [PATCH 7/8] chore: add debug logs to AccountAdder.kt The commit adds debug logs to the `AccountAdder.kt` file to provide more information when skipping account addition. Logs are added for cases where setup is already done, account name is empty, account type is invalid, or no account exists for the given name. --- .../main/java/foundation/e/drive/account/AccountAdder.kt | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/app/src/main/java/foundation/e/drive/account/AccountAdder.kt b/app/src/main/java/foundation/e/drive/account/AccountAdder.kt index 209700a0..0cbba530 100644 --- a/app/src/main/java/foundation/e/drive/account/AccountAdder.kt +++ b/app/src/main/java/foundation/e/drive/account/AccountAdder.kt @@ -67,21 +67,25 @@ class AccountAdder(private val context: Context) { 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 exist for username: %s ", accountType, accountName) + Timber.w("No account exists for $accountName, skipping account addition.") return false } + return true } -- GitLab From f7bc19faf9d7cf537b0a5fb3fb185f2a4fb1e257 Mon Sep 17 00:00:00 2001 From: Fahim Masud Choudhury Date: Fri, 21 Feb 2025 19:19:27 +0600 Subject: [PATCH 8/8] feat: handle account removal with android.accounts.action.ACCOUNT_REMOVED This commit introduces the handling of the `android.accounts.action.ACCOUNT_REMOVED` action. The `AccountRemoveCallbackReceiver` now specifically checks for this action to ensure it's handling the correct intent. The `isInvalidAction` method has been updated to use a direct string comparison against the constant `ACTION_ACCOUNT_REMOVED`, making the check more concise. --- .../receivers/AccountRemoveCallbackReceiver.java | 14 +++----------- 1 file changed, 3 insertions(+), 11 deletions(-) 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 c5317c29..c8f16a69 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 @@ -27,9 +27,6 @@ import android.content.SharedPreferences; import androidx.annotation.NonNull; -import java.util.ArrayList; -import java.util.List; - import foundation.e.drive.R; import foundation.e.drive.account.AccountRemover; import foundation.e.drive.utils.AppConstants; @@ -38,6 +35,8 @@ 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) { @@ -78,13 +77,6 @@ public class AccountRemoveCallbackReceiver extends BroadcastReceiver { } private boolean isInvalidAction(@NonNull Intent intent) { - List actions = new ArrayList<>(); - actions.add("android.accounts.action.ACCOUNT_REMOVED"); - - String intentAction = intent.getAction(); - - boolean hasNecessaryIntentAction = actions.contains(intentAction); - - return !hasNecessaryIntentAction; + return !ACTION_ACCOUNT_REMOVED.equals(intent.getAction()); } } -- GitLab