diff --git a/app/src/main/java/com/infomaniak/mail/MainApplication.kt b/app/src/main/java/com/infomaniak/mail/MainApplication.kt index 70545683b1..34447d7258 100644 --- a/app/src/main/java/com/infomaniak/mail/MainApplication.kt +++ b/app/src/main/java/com/infomaniak/mail/MainApplication.kt @@ -36,7 +36,6 @@ import coil.ImageLoaderFactory import coil.decode.SvgDecoder import com.facebook.stetho.Stetho import com.infomaniak.lib.core.InfomaniakCore -import com.infomaniak.lib.core.api.ApiController import com.infomaniak.lib.core.auth.TokenInterceptorListener import com.infomaniak.lib.core.models.user.User import com.infomaniak.lib.core.networking.AccessTokenUsageInterceptor @@ -105,6 +104,9 @@ open class MainApplication : Application(), ImageLoaderFactory, DefaultLifecycle @Inject lateinit var appUpdateWorkerScheduler: AppUpdateScheduler + @Inject + lateinit var myKSuiteDataUtils: MyKSuiteDataUtils + @Inject @IoDispatcher lateinit var ioDispatcher: CoroutineDispatcher @@ -168,23 +170,37 @@ open class MainApplication : Application(), ImageLoaderFactory, DefaultLifecycle SentryAndroid.init(this) { options: SentryAndroidOptions -> // Register the callback as an option options.beforeSend = SentryOptions.BeforeSendCallback { event: SentryEvent, _: Any? -> - val exception = event.throwable - /** - * Reasons to discard Sentry events : - * - Application is in Debug mode - * - User deactivated Sentry tracking in DataManagement settings - * - The exception was an [ApiController.NetworkException], and we don't want to send them to Sentry - * - The exception was an [ApiErrorException] with an [ErrorCode.ACCESS_DENIED] or - * [ErrorCode.NOT_AUTHORIZED] error code, and we don't want to send them to Sentry - */ - when { - BuildConfig.DEBUG -> null - !localSettings.isSentryTrackingEnabled -> null - exception is ApiController.NetworkException -> null - exception is ApiErrorException && exception.errorCode == ErrorCode.ACCESS_DENIED -> null - exception is ApiErrorException && exception.errorCode == ErrorCode.NOT_AUTHORIZED -> null - else -> event - } + + val shouldLog = mutableListOf() + + // Sentry events are discarded if the app is in Debug mode + val isInReleaseMode = !BuildConfig.DEBUG + shouldLog.add(isInReleaseMode) + + // Sentry events are discarded if the user deactivated Sentry tracking in DataManagement settings + val isSentryTrackingEnabled = localSettings.isSentryTrackingEnabled + shouldLog.add(isSentryTrackingEnabled) + + // Network exceptions are discarded + // TODO: It doesn't work anymore :( + val isNetworkException = event.exceptions?.any { it.type == "ApiController\$NetworkException" } ?: false + shouldLog.add(!isNetworkException) + + // AccessDenied exceptions are discarded + val isAccessDeniedException = event.exceptions?.any { + // TODO: Check in Sentry if this `value.contains()` is the correct way to find this exception. + it.type == "ApiErrorException" && it.value?.contains("access_denied") == true + } ?: false + shouldLog.add(!isAccessDeniedException) + + // NotAuthorized exceptions are discarded + val isNotAuthorizedException = event.exceptions?.any { + // TODO: Check in Sentry if this `value.contains()` is the correct way to find this exception. + it.type == "ApiErrorException" && it.value?.contains("not_authorized") == true + } ?: false + shouldLog.add(!isNotAuthorizedException) + + if (shouldLog.all { true }) event else null } options.addIntegration( FragmentLifecycleIntegration( @@ -209,7 +225,7 @@ open class MainApplication : Application(), ImageLoaderFactory, DefaultLifecycle private fun configureRoomDatabases() { AccountUtils.init(this) - MyKSuiteDataUtils.initDatabase(this) + myKSuiteDataUtils.initDatabase(this) } private fun configureAppReloading() { diff --git a/app/src/main/java/com/infomaniak/mail/ui/MainViewModel.kt b/app/src/main/java/com/infomaniak/mail/ui/MainViewModel.kt index 43b4591933..eed9b0d016 100644 --- a/app/src/main/java/com/infomaniak/mail/ui/MainViewModel.kt +++ b/app/src/main/java/com/infomaniak/mail/ui/MainViewModel.kt @@ -92,6 +92,7 @@ class MainViewModel @Inject constructor( private val mailboxController: MailboxController, private val mergedContactController: MergedContactController, private val messageController: MessageController, + private val myKSuiteDataUtils: MyKSuiteDataUtils, private val notificationUtils: NotificationUtils, private val permissionsController: PermissionsController, private val quotasController: QuotasController, @@ -317,7 +318,7 @@ class MainViewModel @Inject constructor( AccountUtils.updateCurrentUser() // Refresh My kSuite asynchronously, because it's not required for the threads list display - launch { MyKSuiteDataUtils.fetchDataIfMyKSuite(mailboxController) } + launch { myKSuiteDataUtils.fetchData() } // Refresh Mailboxes SentryLog.d(TAG, "Refresh mailboxes from remote") diff --git a/app/src/main/java/com/infomaniak/mail/ui/main/settings/MykSuiteViewModel.kt b/app/src/main/java/com/infomaniak/mail/ui/main/settings/MykSuiteViewModel.kt index 6820c3cb57..0f6984b56e 100644 --- a/app/src/main/java/com/infomaniak/mail/ui/main/settings/MykSuiteViewModel.kt +++ b/app/src/main/java/com/infomaniak/mail/ui/main/settings/MykSuiteViewModel.kt @@ -36,6 +36,7 @@ import javax.inject.Inject class MykSuiteViewModel @Inject constructor( @IoDispatcher private val ioDispatcher: CoroutineDispatcher, private val mailboxController: MailboxController, + private val myKSuiteDataUtils: MyKSuiteDataUtils, ) : ViewModel() { private val ioCoroutineContext = viewModelScope.coroutineContext(ioDispatcher) @@ -48,6 +49,6 @@ class MykSuiteViewModel @Inject constructor( } fun refreshMyKSuite() = viewModelScope.launch(ioCoroutineContext) { - myKSuiteDataResult.postValue(MyKSuiteDataUtils.fetchDataIfMyKSuite(mailboxController)) + myKSuiteDataResult.postValue(myKSuiteDataUtils.fetchData()) } } diff --git a/app/src/main/java/com/infomaniak/mail/ui/main/settings/SettingsFragment.kt b/app/src/main/java/com/infomaniak/mail/ui/main/settings/SettingsFragment.kt index 27e8ca95bf..f16265f7f6 100644 --- a/app/src/main/java/com/infomaniak/mail/ui/main/settings/SettingsFragment.kt +++ b/app/src/main/java/com/infomaniak/mail/ui/main/settings/SettingsFragment.kt @@ -64,6 +64,9 @@ class SettingsFragment : Fragment() { @Inject lateinit var localSettings: LocalSettings + @Inject + lateinit var myKSuiteDataUtils: MyKSuiteDataUtils + override fun onCreate(savedInstanceState: Bundle?) { super.onCreate(savedInstanceState) saveFocusWhenNavigatingBack(getLayout = { binding.linearLayoutContainer }, lifecycle) @@ -87,8 +90,8 @@ class SettingsFragment : Fragment() { } private fun setupMyKSuite() { - binding.myKSuiteLayout.isGone = MyKSuiteDataUtils.myKSuite == null - MyKSuiteDataUtils.myKSuite?.let { setupMyKSuiteLayout(it) } ?: myKSuiteViewModel.refreshMyKSuite() + binding.myKSuiteLayout.isGone = myKSuiteDataUtils.myKSuite == null + myKSuiteDataUtils.myKSuite?.let { setupMyKSuiteLayout(it) } ?: myKSuiteViewModel.refreshMyKSuite() } private fun setupMyKSuiteLayout(myKSuiteData: MyKSuiteData) = with(binding) { @@ -149,7 +152,7 @@ class SettingsFragment : Fragment() { binding.mailboxesList.adapter = mailboxesAdapter mainViewModel.mailboxesLive.observe(viewLifecycleOwner) { mailboxes -> - mailboxesAdapter.setMailboxes(mailboxes.filterNot { it.mailboxId == MyKSuiteDataUtils.myKSuite?.mail?.mailboxId }) + mailboxesAdapter.setMailboxes(mailboxes.filterNot { it.mailboxId == myKSuiteDataUtils.myKSuite?.mail?.mailboxId }) } } diff --git a/app/src/main/java/com/infomaniak/mail/ui/main/user/SwitchUserViewModel.kt b/app/src/main/java/com/infomaniak/mail/ui/main/user/SwitchUserViewModel.kt index b3973f7779..4ab2a2f921 100644 --- a/app/src/main/java/com/infomaniak/mail/ui/main/user/SwitchUserViewModel.kt +++ b/app/src/main/java/com/infomaniak/mail/ui/main/user/SwitchUserViewModel.kt @@ -39,6 +39,7 @@ import javax.inject.Inject class SwitchUserViewModel @Inject constructor( application: Application, private val mailboxController: MailboxController, + private val myKSuiteDataUtils: MyKSuiteDataUtils, @IoDispatcher private val ioDispatcher: CoroutineDispatcher, ) : AndroidViewModel(application) { @@ -52,7 +53,7 @@ class SwitchUserViewModel @Inject constructor( if (user.id != AccountUtils.currentUserId) { appContext.trackAccountEvent("switch") RealmDatabase.backupPreviousRealms() - MyKSuiteDataUtils.myKSuite = null + myKSuiteDataUtils.myKSuite = null AccountUtils.currentUser = user AccountUtils.currentMailboxId = mailboxController.getFirstValidMailbox(user.id)?.mailboxId ?: AppSettings.DEFAULT_ID AccountUtils.reloadApp?.invoke() diff --git a/app/src/main/java/com/infomaniak/mail/utils/LogoutUser.kt b/app/src/main/java/com/infomaniak/mail/utils/LogoutUser.kt index f8f1243324..7ed05ba399 100644 --- a/app/src/main/java/com/infomaniak/mail/utils/LogoutUser.kt +++ b/app/src/main/java/com/infomaniak/mail/utils/LogoutUser.kt @@ -43,6 +43,7 @@ class LogoutUser @Inject constructor( private val globalCoroutineScope: CoroutineScope, private val localSettings: LocalSettings, private val mailboxController: MailboxController, + private val myKSuiteDataUtils: MyKSuiteDataUtils, private val playServicesUtils: PlayServicesUtils, private val storesSettingsRepository: StoresSettingsRepository, @IoDispatcher private val ioDispatcher: CoroutineDispatcher, @@ -52,7 +53,7 @@ class LogoutUser @Inject constructor( user.logoutToken() AccountUtils.removeUser(user) - MyKSuiteDataUtils.deleteData(user.id) + myKSuiteDataUtils.deleteData(user.id) RealmDatabase.removeUserData(appContext, user.id) mailboxController.deleteUserMailboxes(user.id) localSettings.removeRegisteredFirebaseUser(userId = user.id) diff --git a/app/src/main/java/com/infomaniak/mail/utils/MyKSuiteDataUtils.kt b/app/src/main/java/com/infomaniak/mail/utils/MyKSuiteDataUtils.kt index df840f0c4e..4ee31c2ad7 100644 --- a/app/src/main/java/com/infomaniak/mail/utils/MyKSuiteDataUtils.kt +++ b/app/src/main/java/com/infomaniak/mail/utils/MyKSuiteDataUtils.kt @@ -25,23 +25,25 @@ import com.infomaniak.mail.data.api.ApiRepository import com.infomaniak.mail.data.cache.mailboxInfo.MailboxController import kotlinx.serialization.ExperimentalSerializationApi import kotlinx.serialization.MissingFieldException +import javax.inject.Inject +import javax.inject.Singleton import kotlin.coroutines.cancellation.CancellationException -object MyKSuiteDataUtils : MyKSuiteDataManager() { - - private val TAG = MyKSuiteDataUtils::class.simpleName.toString() +@Singleton +class MyKSuiteDataUtils @Inject constructor(private val mailboxController: MailboxController) : MyKSuiteDataManager() { override val currentUserId get() = AccountUtils.currentUserId override var myKSuite: MyKSuiteData? = null - /** Only call this if you are sure that you want to do the call without checking if it's necessary or not - * To avoid useless call for account that does not have a my kSuite offer, use [fetchDataIfMyKSuite] */ override suspend fun fetchData(): MyKSuiteData? = runCatching { - MyKSuiteDataUtils.requestKSuiteData() + requestKSuiteData() + // Only fetch the Data if the current user has a my kSuite mailbox + if (mailboxController.getMyKSuiteMailboxCount(userId = AccountUtils.currentUserId) == 0L) return@runCatching null + val apiResponse = ApiRepository.getMyKSuiteData(HttpClient.okHttpClient) if (apiResponse.data != null) { - MyKSuiteDataUtils.upsertKSuiteData(apiResponse.data!!) + upsertKSuiteData(apiResponse.data!!) } else { @OptIn(ExperimentalSerializationApi::class) apiResponse.error?.exception?.let { @@ -56,8 +58,8 @@ object MyKSuiteDataUtils : MyKSuiteDataManager() { null } - suspend fun fetchDataIfMyKSuite(mailboxController: MailboxController): MyKSuiteData? { - // Only fetch the my kSuite Data if the current account has a my kSuite - return if (mailboxController.getMyKSuiteMailboxCount(userId = AccountUtils.currentUserId) != 0L) fetchData() else null + companion object { + + private val TAG = MyKSuiteDataUtils::class.simpleName.toString() } }