diff --git a/Core b/Core index b17b7106eb..0cfb6ebaf6 160000 --- a/Core +++ b/Core @@ -1 +1 @@ -Subproject commit b17b7106eb973b25484fcce181d2ad28e8f1f6be +Subproject commit 0cfb6ebaf64061ddc86b23579dc33676a18778fd diff --git a/app/src/main/java/com/infomaniak/mail/ui/MainActivity.kt b/app/src/main/java/com/infomaniak/mail/ui/MainActivity.kt index 83ee34d535..a6b13ba5ae 100644 --- a/app/src/main/java/com/infomaniak/mail/ui/MainActivity.kt +++ b/app/src/main/java/com/infomaniak/mail/ui/MainActivity.kt @@ -31,19 +31,15 @@ import androidx.annotation.FloatRange import androidx.core.view.isVisible import androidx.drawerlayout.widget.DrawerLayout import androidx.lifecycle.Lifecycle.State -import androidx.lifecycle.distinctUntilChanged import androidx.lifecycle.lifecycleScope import androidx.lifecycle.repeatOnLifecycle import androidx.navigation.NavDestination import androidx.navigation.fragment.NavHostFragment import androidx.work.Data import com.infomaniak.lib.core.MatomoCore.TrackerAction -import com.infomaniak.lib.core.networking.LiveDataNetworkStatus -import com.infomaniak.lib.core.utils.SentryLog +import com.infomaniak.lib.core.utils.* import com.infomaniak.lib.core.utils.Utils import com.infomaniak.lib.core.utils.Utils.toEnumOrThrow -import com.infomaniak.lib.core.utils.hasPermissions -import com.infomaniak.lib.core.utils.year import com.infomaniak.lib.stores.StoreUtils import com.infomaniak.lib.stores.StoreUtils.checkUpdateIsRequired import com.infomaniak.lib.stores.reviewmanagers.InAppReviewManager @@ -197,7 +193,6 @@ class MainActivity : BaseActivity() { localSettings.accentColor.theme, ) - observeNetworkStatus() observeDeletedMessages() observeDeleteThreadTrigger() observeDraftWorkerResults() @@ -228,21 +223,6 @@ class MainActivity : BaseActivity() { ) } - private fun observeNetworkStatus() { - LiveDataNetworkStatus(context = this).distinctUntilChanged().observe(this) { isAvailable -> - - SentryLog.d("Internet availability", if (isAvailable) "Available" else "Unavailable") - - Breadcrumb().apply { - category = "Network" - message = "Internet access is available : $isAvailable" - level = if (isAvailable) SentryLevel.INFO else SentryLevel.WARNING - }.also(Sentry::addBreadcrumb) - - mainViewModel.isInternetAvailable.value = isAvailable - } - } - private fun observeDeletedMessages() = with(mainViewModel) { deletedMessages.observe(owner = this@MainActivity) { if (it.isNotEmpty()) { 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 47b312767a..6c8d03d383 100644 --- a/app/src/main/java/com/infomaniak/mail/ui/MainViewModel.kt +++ b/app/src/main/java/com/infomaniak/mail/ui/MainViewModel.kt @@ -20,6 +20,7 @@ package com.infomaniak.mail.ui import android.app.Application import androidx.lifecycle.* import com.infomaniak.lib.core.models.ApiResponse +import com.infomaniak.lib.core.networking.NetworkAvailability import com.infomaniak.lib.core.utils.ApiErrorCode.Companion.translateError import com.infomaniak.lib.core.utils.DownloadManagerUtils import com.infomaniak.lib.core.utils.SentryLog @@ -100,7 +101,6 @@ class MainViewModel @Inject constructor( // First boolean is the download status, second boolean is if the LoadMore button should be displayed val isDownloadingChanges: MutableLiveData = MutableLiveData(false) - val isInternetAvailable = MutableLiveData() val isMovedToNewFolder = SingleLiveEvent() val toggleLightThemeForMessage = SingleLiveEvent() val deletedMessages = SingleLiveEvent>() @@ -172,6 +172,17 @@ class MainViewModel @Inject constructor( val currentThreadsLive = MutableLiveData>() + val isNetworkAvailable = NetworkAvailability(appContext).isNetworkAvailable + .mapLatest { + SentryLog.d("Internet availability", if (it) "Available" else "Unavailable") + it + } + .stateIn( + scope = viewModelScope, + started = SharingStarted.Eagerly, + initialValue = null + ) + private var currentThreadsLiveJob: Job? = null fun reassignCurrentThreadsLive() { @@ -1080,5 +1091,6 @@ class MainViewModel @Inject constructor( private val DEFAULT_SELECTED_FOLDER = FolderRole.INBOX private const val REFRESH_DELAY = 2_000L // We add this delay because `etop` isn't always big enough. private const val MAX_REFRESH_DELAY = 6_000L + private const val TIMEOUT_MS_NETWORK_AVAILABILITY_MS = 500L } } diff --git a/app/src/main/java/com/infomaniak/mail/ui/main/folder/ThreadListFragment.kt b/app/src/main/java/com/infomaniak/mail/ui/main/folder/ThreadListFragment.kt index b2a0a530f1..7f3f1f45cc 100644 --- a/app/src/main/java/com/infomaniak/mail/ui/main/folder/ThreadListFragment.kt +++ b/app/src/main/java/com/infomaniak/mail/ui/main/folder/ThreadListFragment.kt @@ -34,6 +34,7 @@ import androidx.core.view.isVisible import androidx.fragment.app.FragmentContainerView import androidx.fragment.app.viewModels import androidx.lifecycle.distinctUntilChanged +import androidx.lifecycle.lifecycleScope import androidx.navigation.fragment.findNavController import androidx.navigation.fragment.navArgs import androidx.recyclerview.widget.LinearLayoutManager @@ -47,7 +48,12 @@ import com.ernestoyaquello.dragdropswiperecyclerview.listener.OnListScrollListen import com.ernestoyaquello.dragdropswiperecyclerview.listener.OnListScrollListener.ScrollDirection import com.ernestoyaquello.dragdropswiperecyclerview.listener.OnListScrollListener.ScrollState import com.infomaniak.lib.core.MatomoCore.TrackerAction -import com.infomaniak.lib.core.utils.* +import com.infomaniak.lib.core.utils.SentryLog +import com.infomaniak.lib.core.utils.context +import com.infomaniak.lib.core.utils.goToPlayStore +import com.infomaniak.lib.core.utils.isToday +import com.infomaniak.lib.core.utils.safeNavigate +import com.infomaniak.lib.core.utils.setPaddingRelative import com.infomaniak.lib.stores.updatemanagers.InAppUpdateManager import com.infomaniak.mail.MatomoMail.trackEvent import com.infomaniak.mail.MatomoMail.trackMenuDrawerEvent @@ -77,10 +83,20 @@ import com.infomaniak.mail.utils.UiUtils.formatUnreadCount import com.infomaniak.mail.utils.Utils import com.infomaniak.mail.utils.Utils.isPermanentDeleteFolder import com.infomaniak.mail.utils.Utils.runCatchingRealm -import com.infomaniak.mail.utils.extensions.* +import com.infomaniak.mail.utils.extensions.addStickyDateDecoration +import com.infomaniak.mail.utils.extensions.animatedNavigation +import com.infomaniak.mail.utils.extensions.bindAlertToViewLifecycle +import com.infomaniak.mail.utils.extensions.deleteWithConfirmationPopup +import com.infomaniak.mail.utils.extensions.getAnimatedNavOptions +import com.infomaniak.mail.utils.extensions.notYetImplemented +import com.infomaniak.mail.utils.extensions.observeNotNull +import com.infomaniak.mail.utils.extensions.safeNavigateToNewMessageActivity +import com.infomaniak.mail.utils.extensions.setSystemBarsColors +import com.infomaniak.mail.utils.extensions.toDate import dagger.hilt.android.AndroidEntryPoint import io.sentry.Sentry import io.sentry.SentryLevel +import kotlinx.coroutines.launch import java.util.Date import javax.inject.Inject import com.infomaniak.lib.core.R as RCore @@ -495,8 +511,7 @@ class ThreadListFragment : TwoPaneFragment(), SwipeRefreshLayout.OnRefreshListen SwipeAction.NONE -> throw IllegalStateException("Cannot swipe on an action which is not set") } - val shouldKeepItemBecauseOfNoConnection = isInternetAvailable.value == false - + val shouldKeepItemBecauseOfNoConnection = mainViewModel.isNetworkAvailable.value == false return shouldKeepItemBecauseOfAction || shouldKeepItemBecauseOfNoConnection } @@ -529,10 +544,14 @@ class ThreadListFragment : TwoPaneFragment(), SwipeRefreshLayout.OnRefreshListen } private fun observeNetworkStatus() { - mainViewModel.isInternetAvailable.observe(viewLifecycleOwner) { isAvailable -> - TransitionManager.beginDelayedTransition(binding.root) - binding.noNetwork.isGone = isAvailable - if (!isAvailable) updateThreadsVisibility() + lifecycleScope.launch { + mainViewModel.isNetworkAvailable.collect { isNetworkAvailable -> + if (isNetworkAvailable != null) { + TransitionManager.beginDelayedTransition(binding.root) + binding.noNetwork.isGone = isNetworkAvailable + if (!isNetworkAvailable) updateThreadsVisibility() + } + } } } @@ -722,7 +741,7 @@ class ThreadListFragment : TwoPaneFragment(), SwipeRefreshLayout.OnRefreshListen val areThereThreads = (currentThreadsCount ?: 0) > 0 val isFilterEnabled = mainViewModel.currentFilter.value != ThreadFilter.ALL val isCursorNull = currentFolderCursor == null - val isNetworkConnected = mainViewModel.isInternetAvailable.value == true + val isNetworkConnected = mainViewModel.isNetworkAvailable.value ?: true val isBooting = currentThreadsCount == null && !isCursorNull && isNetworkConnected val shouldDisplayThreadsView = isBooting || areThereThreads || isFilterEnabled || (isCursorNull && isNetworkConnected) diff --git a/app/src/main/java/com/infomaniak/mail/ui/main/thread/actions/DownloadAttachmentProgressDialog.kt b/app/src/main/java/com/infomaniak/mail/ui/main/thread/actions/DownloadAttachmentProgressDialog.kt index 7a9055d1b7..812988f21a 100644 --- a/app/src/main/java/com/infomaniak/mail/ui/main/thread/actions/DownloadAttachmentProgressDialog.kt +++ b/app/src/main/java/com/infomaniak/mail/ui/main/thread/actions/DownloadAttachmentProgressDialog.kt @@ -24,6 +24,7 @@ import androidx.appcompat.content.res.AppCompatResources import androidx.fragment.app.DialogFragment import androidx.fragment.app.activityViewModels import androidx.fragment.app.viewModels +import androidx.lifecycle.lifecycleScope import androidx.navigation.fragment.findNavController import androidx.navigation.fragment.navArgs import com.google.android.material.dialog.MaterialAlertDialogBuilder @@ -35,6 +36,8 @@ import com.infomaniak.mail.ui.MainViewModel import com.infomaniak.mail.utils.extensions.AttachmentExtensions import com.infomaniak.mail.utils.extensions.AttachmentExtensions.getIntentOrGoToPlayStore import dagger.hilt.android.AndroidEntryPoint +import kotlinx.coroutines.flow.first +import kotlinx.coroutines.launch @AndroidEntryPoint class DownloadAttachmentProgressDialog : DialogFragment() { @@ -76,8 +79,14 @@ class DownloadAttachmentProgressDialog : DialogFragment() { } private fun popBackStackWithError() { - val title = if (mainViewModel.isInternetAvailable.value == true) R.string.anErrorHasOccurred else R.string.noConnection - showSnackbar(title) - findNavController().popBackStack() + // We have to use the Flow instead of the LiveData here since the mainViewModel is not the same instance as the one in the + // MainActivity. So, instead of observing the LiveData in order to make it "active", we use the Flow. + lifecycleScope.launch { + with(mainViewModel.isNetworkAvailable.first { it != null }) { + val title = if (this!!) R.string.anErrorHasOccurred else R.string.noConnection + showSnackbar(title) + findNavController().popBackStack() + } + } } } diff --git a/app/src/main/java/com/infomaniak/mail/ui/newMessage/NewMessageActivity.kt b/app/src/main/java/com/infomaniak/mail/ui/newMessage/NewMessageActivity.kt index 29aa7a166f..2be99d0b11 100644 --- a/app/src/main/java/com/infomaniak/mail/ui/newMessage/NewMessageActivity.kt +++ b/app/src/main/java/com/infomaniak/mail/ui/newMessage/NewMessageActivity.kt @@ -34,6 +34,7 @@ import com.infomaniak.mail.data.models.draft.Draft.DraftAction import com.infomaniak.mail.databinding.ActivityNewMessageBinding import com.infomaniak.mail.ui.BaseActivity import com.infomaniak.mail.ui.LaunchActivity +import com.infomaniak.mail.ui.MainViewModel import com.infomaniak.mail.ui.main.SnackbarManager import com.infomaniak.mail.utils.AccountUtils import com.infomaniak.mail.utils.SentryDebug