diff --git a/app/src/main/java/org/mozilla/fenix/tabtray/TabTrayController.kt b/app/src/main/java/org/mozilla/fenix/tabtray/TabTrayController.kt index 27ac903cd..d2383185d 100644 --- a/app/src/main/java/org/mozilla/fenix/tabtray/TabTrayController.kt +++ b/app/src/main/java/org/mozilla/fenix/tabtray/TabTrayController.kt @@ -117,6 +117,7 @@ class DefaultTabTrayController( } showUndoSnackbar(snackbarMessage, snapshot) + dismissTabTray() } @VisibleForTesting(otherwise = VisibleForTesting.PRIVATE) diff --git a/app/src/main/java/org/mozilla/fenix/tabtray/TabTrayDialogFragment.kt b/app/src/main/java/org/mozilla/fenix/tabtray/TabTrayDialogFragment.kt index b7340d1d4..e21e5e3be 100644 --- a/app/src/main/java/org/mozilla/fenix/tabtray/TabTrayDialogFragment.kt +++ b/app/src/main/java/org/mozilla/fenix/tabtray/TabTrayDialogFragment.kt @@ -38,6 +38,7 @@ import org.mozilla.fenix.components.FenixSnackbar import org.mozilla.fenix.components.TabCollectionStorage import org.mozilla.fenix.components.metrics.Event import org.mozilla.fenix.ext.components +import org.mozilla.fenix.ext.getRootView import org.mozilla.fenix.ext.requireComponents import org.mozilla.fenix.ext.settings import org.mozilla.fenix.utils.allowUndo @@ -204,19 +205,32 @@ class TabTrayDialogFragment : AppCompatDialogFragment() { getString(R.string.snackbar_tab_closed) } - view?.tabLayout?.let { - viewLifecycleOwner.lifecycleScope.allowUndo( - it, - snackbarMessage, - getString(R.string.snackbar_deleted_undo), - { - sessionManager.add(snapshot.session, isSelected, engineSessionState = state) - tabTrayView.scrollToTab(snapshot.session.id) - }, - operation = { }, - elevation = ELEVATION, - anchorView = snackbarAnchor - ) + // Check if this is the last tab of this session type + val isLastOpenTab = sessionManager.sessions.filter { snapshot.session.private }.size == 1 + val rootView = if (isLastOpenTab) { requireActivity().getRootView()!! } else { requireView().tabLayout } + val anchorView = if (isLastOpenTab) { null } else { snackbarAnchor } + + requireActivity().lifecycleScope.allowUndo( + rootView, + snackbarMessage, + getString(R.string.snackbar_deleted_undo), + { + sessionManager.add(snapshot.session, isSelected, engineSessionState = state) + _tabTrayView?.scrollToTab(snapshot.session.id) + }, + operation = { }, + elevation = ELEVATION, + paddedForBottomToolbar = isLastOpenTab, + anchorView = anchorView + ) + + dismissTabTrayIfNecessary() + } + + private fun dismissTabTrayIfNecessary() { + if (requireComponents.core.sessionManager.sessions.size == 1) { + findNavController().popBackStack(R.id.homeFragment, false) + dismissAllowingStateLoss() } } @@ -250,19 +264,20 @@ class TabTrayDialogFragment : AppCompatDialogFragment() { } private fun showUndoSnackbar(snackbarMessage: String, snapshot: SessionManager.Snapshot) { - view?.let { - viewLifecycleOwner.lifecycleScope.allowUndo( - it, - snackbarMessage, - getString(R.string.snackbar_deleted_undo), - { - context?.components?.core?.sessionManager?.restore(snapshot) - }, - operation = { }, - elevation = ELEVATION, - anchorView = snackbarAnchor - ) - } + // Warning: removing this definition and using it directly in the onCancel block will fail silently. + val sessionManager = view?.context?.components?.core?.sessionManager + + requireActivity().lifecycleScope.allowUndo( + requireActivity().getRootView()!!, + snackbarMessage, + getString(R.string.snackbar_deleted_undo), + { + sessionManager?.restore(snapshot) + }, + operation = { }, + elevation = ELEVATION, + paddedForBottomToolbar = true + ) } private fun showCollectionSnackbar(tabSize: Int, isNewCollection: Boolean = false) { diff --git a/app/src/main/java/org/mozilla/fenix/utils/Undo.kt b/app/src/main/java/org/mozilla/fenix/utils/Undo.kt index 90ab563ec..6e8997770 100644 --- a/app/src/main/java/org/mozilla/fenix/utils/Undo.kt +++ b/app/src/main/java/org/mozilla/fenix/utils/Undo.kt @@ -5,10 +5,12 @@ package org.mozilla.fenix.utils import android.view.View +import androidx.appcompat.widget.ContentFrameLayout import kotlinx.coroutines.CoroutineScope import kotlinx.coroutines.Dispatchers import kotlinx.coroutines.delay import kotlinx.coroutines.launch +import org.mozilla.fenix.R import org.mozilla.fenix.components.FenixSnackbar import org.mozilla.fenix.ext.settings import java.util.concurrent.atomic.AtomicBoolean @@ -37,7 +39,8 @@ fun CoroutineScope.allowUndo( onCancel: suspend () -> Unit = {}, operation: suspend () -> Unit, anchorView: View? = null, - elevation: Float? = null + elevation: Float? = null, + paddedForBottomToolbar: Boolean = false ) { // By using an AtomicBoolean, we achieve memory effects of reading and // writing a volatile variable. @@ -63,6 +66,30 @@ fun CoroutineScope.allowUndo( snackbar.view.elevation = it } + val shouldUseBottomToolbar = view.context.settings().shouldUseBottomToolbar + val toolbarHeight = view.context.resources + .getDimensionPixelSize(R.dimen.browser_toolbar_height) + + snackbar.view.setPadding( + 0, + 0, + 0, + if ( + paddedForBottomToolbar && + shouldUseBottomToolbar && + // If the view passed in is a ContentFrameLayout, it does not matter + // if the user has a dynamicBottomToolbar or not, as the Android system + // can't intelligently position the snackbar on the upper most view. + // Ideally we should not pass ContentFrameLayout in, but it's the only + // way to display snackbars through a fragment transition. + view is ContentFrameLayout + ) { + toolbarHeight + } else { + 0 + } + ) + snackbar.show() // Wait a bit, and if user didn't request cancellation, proceed with