From 4ba99372f298aa41f12f9673d07ed1e28c862bae Mon Sep 17 00:00:00 2001 From: Jonathan Almeida Date: Sun, 11 Apr 2021 17:24:51 -0400 Subject: [PATCH] [fenix] Issue https://github.com/mozilla-mobile/fenix/issues/18885: Dismiss FAB icon when tabs tray is closed --- .../fenix/tabstray/TabsTrayController.kt | 4 +- .../fenix/tabstray/TabsTrayFragment.kt | 32 ++++++---- .../tabstray/TraySheetBehaviorCallback.kt | 32 ++++++++++ .../tabstray/TraySheetBehaviorCallbackTest.kt | 63 +++++++++++++++++++ 4 files changed, 117 insertions(+), 14 deletions(-) create mode 100644 app/src/main/java/org/mozilla/fenix/tabstray/TraySheetBehaviorCallback.kt create mode 100644 app/src/test/java/org/mozilla/fenix/tabstray/TraySheetBehaviorCallbackTest.kt diff --git a/app/src/main/java/org/mozilla/fenix/tabstray/TabsTrayController.kt b/app/src/main/java/org/mozilla/fenix/tabstray/TabsTrayController.kt index 9ed44a68c3..507cfe1a7d 100644 --- a/app/src/main/java/org/mozilla/fenix/tabstray/TabsTrayController.kt +++ b/app/src/main/java/org/mozilla/fenix/tabstray/TabsTrayController.kt @@ -34,7 +34,7 @@ class DefaultTabsTrayController( private val browsingModeManager: BrowsingModeManager, private val navController: NavController, private val profiler: Profiler?, - private val dismissTabTray: () -> Unit, + private val navigationInteractor: NavigationInteractor, private val metrics: MetricController, private val ioScope: CoroutineScope, private val accountManager: FxaAccountManager @@ -44,7 +44,7 @@ class DefaultTabsTrayController( val startTime = profiler?.getProfilerTime() browsingModeManager.mode = BrowsingMode.fromBoolean(isPrivate) navController.navigate(TabTrayDialogFragmentDirections.actionGlobalHome(focusOnAddressBar = true)) - dismissTabTray() + navigationInteractor.onTabTrayDismissed() profiler?.addMarker( "DefaultTabTrayController.onNewTabTapped", startTime diff --git a/app/src/main/java/org/mozilla/fenix/tabstray/TabsTrayFragment.kt b/app/src/main/java/org/mozilla/fenix/tabstray/TabsTrayFragment.kt index ecc3efc700..a1ca35761e 100644 --- a/app/src/main/java/org/mozilla/fenix/tabstray/TabsTrayFragment.kt +++ b/app/src/main/java/org/mozilla/fenix/tabstray/TabsTrayFragment.kt @@ -91,11 +91,22 @@ class TabsTrayFragment : AppCompatDialogFragment(), TabsTrayInteractor { super.onViewCreated(view, savedInstanceState) val activity = activity as HomeActivity + val navigationInteractor = + DefaultNavigationInteractor( + tabsTrayStore = tabsTrayStore, + browserStore = requireComponents.core.store, + navController = findNavController(), + metrics = requireComponents.analytics.metrics, + dismissTabTray = ::dismissAllowingStateLoss, + dismissTabTrayAndNavigateHome = ::dismissTabTrayAndNavigateHome, + bookmarksUseCase = requireComponents.useCases.bookmarksUseCases + ) + tabsTrayController = DefaultTabsTrayController( store = tabsTrayStore, browsingModeManager = activity.browsingModeManager, navController = findNavController(), - dismissTabTray = ::dismissAllowingStateLoss, + navigationInteractor = navigationInteractor, profiler = requireComponents.core.engine.profiler, accountManager = requireComponents.backgroundServices.accountManager, metrics = requireComponents.analytics.metrics, @@ -111,17 +122,6 @@ class TabsTrayFragment : AppCompatDialogFragment(), TabsTrayInteractor { requireComponents.analytics.metrics ) - val navigationInteractor = - DefaultNavigationInteractor( - tabsTrayStore = tabsTrayStore, - browserStore = requireComponents.core.store, - navController = findNavController(), - metrics = requireComponents.analytics.metrics, - dismissTabTray = ::dismissAllowingStateLoss, - dismissTabTrayAndNavigateHome = ::dismissTabTrayAndNavigateHome, - bookmarksUseCase = requireComponents.useCases.bookmarksUseCases - ) - val syncedTabsTrayInteractor = SyncedTabsInteractor( requireComponents.analytics.metrics, requireActivity() as HomeActivity, @@ -138,6 +138,14 @@ class TabsTrayFragment : AppCompatDialogFragment(), TabsTrayInteractor { syncedTabsTrayInteractor ) + behavior.addBottomSheetCallback( + TraySheetBehaviorCallback( + behavior, + navigationInteractor, + requireComponents.analytics.metrics + ) + ) + tabsTrayCtaBinding.set( feature = TabsTrayInfoBannerBinding( context = view.context, diff --git a/app/src/main/java/org/mozilla/fenix/tabstray/TraySheetBehaviorCallback.kt b/app/src/main/java/org/mozilla/fenix/tabstray/TraySheetBehaviorCallback.kt new file mode 100644 index 0000000000..3c9f33807f --- /dev/null +++ b/app/src/main/java/org/mozilla/fenix/tabstray/TraySheetBehaviorCallback.kt @@ -0,0 +1,32 @@ +/* This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ + +package org.mozilla.fenix.tabstray + +import android.view.View +import androidx.constraintlayout.widget.ConstraintLayout +import com.google.android.material.bottomsheet.BottomSheetBehavior +import com.google.android.material.bottomsheet.BottomSheetBehavior.STATE_HIDDEN +import org.mozilla.fenix.components.metrics.Event +import org.mozilla.fenix.components.metrics.MetricController + +class TraySheetBehaviorCallback( + private val behavior: BottomSheetBehavior, + private val trayInteractor: NavigationInteractor, + private val metrics: MetricController +) : BottomSheetBehavior.BottomSheetCallback() { + + override fun onStateChanged(bottomSheet: View, newState: Int) { + if (newState == STATE_HIDDEN) { + metrics.track(Event.TabsTrayClosed) + trayInteractor.onTabTrayDismissed() + } else if (newState == BottomSheetBehavior.STATE_HALF_EXPANDED) { + // We only support expanded and collapsed states. + // But why?? + behavior.state = STATE_HIDDEN + } + } + + override fun onSlide(bottomSheet: View, slideOffset: Float) = Unit +} diff --git a/app/src/test/java/org/mozilla/fenix/tabstray/TraySheetBehaviorCallbackTest.kt b/app/src/test/java/org/mozilla/fenix/tabstray/TraySheetBehaviorCallbackTest.kt new file mode 100644 index 0000000000..35e147fc93 --- /dev/null +++ b/app/src/test/java/org/mozilla/fenix/tabstray/TraySheetBehaviorCallbackTest.kt @@ -0,0 +1,63 @@ +/* This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ + +package org.mozilla.fenix.tabstray + +import androidx.constraintlayout.widget.ConstraintLayout +import com.google.android.material.bottomsheet.BottomSheetBehavior +import com.google.android.material.bottomsheet.BottomSheetBehavior.STATE_HALF_EXPANDED +import com.google.android.material.bottomsheet.BottomSheetBehavior.STATE_HIDDEN +import com.google.android.material.bottomsheet.BottomSheetBehavior.STATE_COLLAPSED +import com.google.android.material.bottomsheet.BottomSheetBehavior.STATE_DRAGGING +import com.google.android.material.bottomsheet.BottomSheetBehavior.STATE_SETTLING +import com.google.android.material.bottomsheet.BottomSheetBehavior.STATE_EXPANDED +import io.mockk.Called +import io.mockk.mockk +import io.mockk.verify +import org.junit.Test +import org.mozilla.fenix.components.metrics.Event +import org.mozilla.fenix.components.metrics.MetricController + +class TraySheetBehaviorCallbackTest { + + @Test + fun `WHEN state is hidden THEN invoke interactor`() { + val interactor = mockk(relaxed = true) + val metrics = mockk(relaxed = true) + val callback = TraySheetBehaviorCallback(mockk(), interactor, metrics) + + callback.onStateChanged(mockk(), STATE_HIDDEN) + + verify { interactor.onTabTrayDismissed() } + verify { metrics.track(Event.TabsTrayClosed) } + } + + @Test + fun `WHEN state is half-expanded THEN close the tray`() { + val behavior = mockk>(relaxed = true) + val callback = TraySheetBehaviorCallback(behavior, mockk(), mockk()) + + callback.onStateChanged(mockk(), STATE_HALF_EXPANDED) + + verify { behavior.state = STATE_HIDDEN } + } + + @Test + fun `WHEN other states are invoked THEN do nothing`() { + val behavior = mockk>(relaxed = true) + val interactor = mockk(relaxed = true) + val metrics = mockk(relaxed = true) + + val callback = TraySheetBehaviorCallback(behavior, interactor, metrics) + + callback.onStateChanged(mockk(), STATE_COLLAPSED) + callback.onStateChanged(mockk(), STATE_DRAGGING) + callback.onStateChanged(mockk(), STATE_SETTLING) + callback.onStateChanged(mockk(), STATE_EXPANDED) + + verify { behavior wasNot Called } + verify { interactor wasNot Called } + verify { metrics wasNot Called } + } +}