From 8bf1cae2cae7bb72fc406b89538914bd24e12d83 Mon Sep 17 00:00:00 2001 From: Mugurell Date: Wed, 27 Jan 2021 17:56:48 +0200 Subject: [PATCH] For #10686 - Use the AC custom behavior for both the top and bottom toolbars This comes to unify the experience (with improvements but also specific issues) for the url toolbar irrespective of it being placed at the bottom or at the top Going further this will ease development and ensure the best UX for users. --- .../fenix/browser/BaseBrowserFragment.kt | 65 ++++-- .../components/toolbar/BrowserToolbarView.kt | 99 ++++----- .../SwipeRefreshScrollingViewBehavior.kt | 50 ----- .../layout/component_browser_top_toolbar.xml | 10 +- app/src/main/res/layout/fragment_browser.xml | 3 +- .../fenix/browser/BaseBrowserFragmentTest.kt | 133 ++++++++++++ .../toolbar/BrowserToolbarViewTest.kt | 196 ++++++++++++++++++ 7 files changed, 421 insertions(+), 135 deletions(-) delete mode 100644 app/src/main/java/org/mozilla/fenix/components/toolbar/SwipeRefreshScrollingViewBehavior.kt create mode 100644 app/src/test/java/org/mozilla/fenix/browser/BaseBrowserFragmentTest.kt create mode 100644 app/src/test/java/org/mozilla/fenix/components/toolbar/BrowserToolbarViewTest.kt diff --git a/app/src/main/java/org/mozilla/fenix/browser/BaseBrowserFragment.kt b/app/src/main/java/org/mozilla/fenix/browser/BaseBrowserFragment.kt index 817a5ba2b8..c90ca97f46 100644 --- a/app/src/main/java/org/mozilla/fenix/browser/BaseBrowserFragment.kt +++ b/app/src/main/java/org/mozilla/fenix/browser/BaseBrowserFragment.kt @@ -70,7 +70,6 @@ import mozilla.components.feature.session.FullScreenFeature import mozilla.components.feature.session.PictureInPictureFeature import mozilla.components.feature.session.SessionFeature import mozilla.components.feature.session.SwipeRefreshFeature -import mozilla.components.feature.session.behavior.EngineViewBottomBehavior import mozilla.components.feature.sitepermissions.SitePermissions import mozilla.components.feature.sitepermissions.SitePermissionsFeature import mozilla.components.lib.state.ext.consumeFlow @@ -103,9 +102,7 @@ import org.mozilla.fenix.components.toolbar.BrowserToolbarView import org.mozilla.fenix.components.toolbar.BrowserToolbarViewInteractor import org.mozilla.fenix.components.toolbar.DefaultBrowserToolbarController import org.mozilla.fenix.components.toolbar.DefaultBrowserToolbarMenuController -import org.mozilla.fenix.components.toolbar.SwipeRefreshScrollingViewBehavior import org.mozilla.fenix.components.toolbar.ToolbarIntegration -import org.mozilla.fenix.components.toolbar.ToolbarPosition import org.mozilla.fenix.downloads.DownloadService import org.mozilla.fenix.downloads.DynamicDownloadDialog import org.mozilla.fenix.ext.accessibilityManager @@ -128,9 +125,11 @@ import org.mozilla.fenix.utils.allowUndo import org.mozilla.fenix.wifi.SitePermissionsWifiIntegration import java.lang.ref.WeakReference import mozilla.components.feature.media.fullscreen.MediaFullscreenOrientationFeature +import mozilla.components.feature.session.behavior.EngineViewBrowserToolbarBehavior import mozilla.components.feature.webauthn.WebAuthnFeature import mozilla.components.support.base.feature.ActivityResultHandler import org.mozilla.fenix.FeatureFlags.newMediaSessionApi +import mozilla.components.feature.session.behavior.ToolbarPosition as MozacToolbarPosition /** * Base fragment extended by [BrowserFragment]. @@ -838,34 +837,42 @@ abstract class BaseBrowserFragment : Fragment(), UserInteractionHandler, Activit !inFullScreen } - private fun initializeEngineView(toolbarHeight: Int) { + @VisibleForTesting + internal fun initializeEngineView(toolbarHeight: Int) { val context = requireContext() - if (context.settings().isDynamicToolbarEnabled) { - engineView.setDynamicToolbarMaxHeight(toolbarHeight) + // If there is an a11y service enabled and the user hasn't explicitly set bottom toolbar + val isTopToolbarForced = + !context.settings().shouldUseBottomToolbar && + context.settings().shouldUseFixedTopToolbar + + if (!isTopToolbarForced && context.settings().isDynamicToolbarEnabled) { + getEngineView().setDynamicToolbarMaxHeight(toolbarHeight) - val behavior = when (context.settings().toolbarPosition) { - // Set engineView dynamic vertical clipping depending on the toolbar position. - ToolbarPosition.BOTTOM -> EngineViewBottomBehavior(context, null) - // Set scroll flags depending on if if the browser or the website is doing the scroll. - ToolbarPosition.TOP -> SwipeRefreshScrollingViewBehavior( + val toolbarPosition = if (context.settings().shouldUseBottomToolbar) { + MozacToolbarPosition.BOTTOM + } else { + MozacToolbarPosition.TOP + } + (getSwipeRefreshLayout().layoutParams as CoordinatorLayout.LayoutParams).behavior = + EngineViewBrowserToolbarBehavior( context, null, - engineView, - browserToolbarView + getSwipeRefreshLayout(), + toolbarHeight, + toolbarPosition ) - } - - (swipeRefresh.layoutParams as CoordinatorLayout.LayoutParams).behavior = behavior } else { // Ensure webpage's bottom elements are aligned to the very bottom of the engineView. - engineView.setDynamicToolbarMaxHeight(0) + getEngineView().setDynamicToolbarMaxHeight(0) - // Effectively place the engineView on top of the toolbar if that is not dynamic. + // Effectively place the engineView on top/below of the toolbar if that is not dynamic. + val swipeRefreshParams = + getSwipeRefreshLayout().layoutParams as CoordinatorLayout.LayoutParams if (context.settings().shouldUseBottomToolbar) { - val browserEngine = swipeRefresh.layoutParams as CoordinatorLayout.LayoutParams - browserEngine.bottomMargin = - requireContext().resources.getDimensionPixelSize(R.dimen.browser_toolbar_height) + swipeRefreshParams.bottomMargin = toolbarHeight + } else { + swipeRefreshParams.topMargin = toolbarHeight } } } @@ -1251,6 +1258,8 @@ abstract class BaseBrowserFragment : Fragment(), UserInteractionHandler, Activit browserToolbarView.view.isVisible = false val browserEngine = swipeRefresh.layoutParams as CoordinatorLayout.LayoutParams browserEngine.bottomMargin = 0 + browserEngine.topMargin = 0 + swipeRefresh.translationY = 0f engineView.setDynamicToolbarMaxHeight(0) browserToolbarView.expand() @@ -1330,7 +1339,7 @@ abstract class BaseBrowserFragment : Fragment(), UserInteractionHandler, Activit override fun onAccessibilityStateChanged(enabled: Boolean) { if (_browserToolbarView != null) { - browserToolbarView.setScrollFlags(enabled) + browserToolbarView.setToolbarBehavior(enabled) } } @@ -1352,4 +1361,16 @@ abstract class BaseBrowserFragment : Fragment(), UserInteractionHandler, Activit } } } + + /** + * Convenience method for replacing EngineView (id/engineView) in unit tests. + */ + @VisibleForTesting + internal fun getEngineView() = engineView + + /** + * Convenience method for replacing SwipeRefreshLayout (id/swipeRefresh) in unit tests. + */ + @VisibleForTesting + internal fun getSwipeRefreshLayout() = swipeRefresh } diff --git a/app/src/main/java/org/mozilla/fenix/components/toolbar/BrowserToolbarView.kt b/app/src/main/java/org/mozilla/fenix/components/toolbar/BrowserToolbarView.kt index aff4b03d2c..af57953a5f 100644 --- a/app/src/main/java/org/mozilla/fenix/components/toolbar/BrowserToolbarView.kt +++ b/app/src/main/java/org/mozilla/fenix/components/toolbar/BrowserToolbarView.kt @@ -9,25 +9,18 @@ import android.view.LayoutInflater import android.view.View import android.view.ViewGroup import androidx.annotation.LayoutRes +import androidx.annotation.VisibleForTesting import androidx.coordinatorlayout.widget.CoordinatorLayout import androidx.core.content.ContextCompat -import androidx.core.view.updateLayoutParams import androidx.lifecycle.LifecycleOwner -import com.google.android.material.appbar.AppBarLayout -import com.google.android.material.appbar.AppBarLayout.LayoutParams.SCROLL_FLAG_ENTER_ALWAYS -import com.google.android.material.appbar.AppBarLayout.LayoutParams.SCROLL_FLAG_EXIT_UNTIL_COLLAPSED -import com.google.android.material.appbar.AppBarLayout.LayoutParams.SCROLL_FLAG_SCROLL -import com.google.android.material.appbar.AppBarLayout.LayoutParams.SCROLL_FLAG_SNAP import kotlinx.android.extensions.LayoutContainer -import kotlinx.android.synthetic.main.component_browser_top_toolbar.* -import kotlinx.android.synthetic.main.component_browser_top_toolbar.view.* import kotlinx.coroutines.ExperimentalCoroutinesApi import mozilla.components.browser.domains.autocomplete.ShippedDomainsProvider import mozilla.components.browser.session.Session import mozilla.components.browser.state.selector.selectedTab import mozilla.components.browser.state.state.ExternalAppType import mozilla.components.browser.toolbar.BrowserToolbar -import mozilla.components.browser.toolbar.behavior.BrowserToolbarBottomBehavior +import mozilla.components.browser.toolbar.behavior.BrowserToolbarBehavior import mozilla.components.browser.toolbar.display.DisplayToolbar import mozilla.components.support.utils.URLStringUtils import mozilla.components.ui.tabcounter.TabCounterMenu @@ -40,6 +33,7 @@ import org.mozilla.fenix.ext.settings import org.mozilla.fenix.theme.ThemeManager import org.mozilla.fenix.utils.ToolbarPopupWindow import java.lang.ref.WeakReference +import mozilla.components.browser.toolbar.behavior.ToolbarPosition as MozacToolbarPosition interface BrowserToolbarViewInteractor { fun onBrowserToolbarPaste(text: String) @@ -76,12 +70,14 @@ class BrowserToolbarView( private val layout = LayoutInflater.from(container.context) .inflate(toolbarLayout, container, true) - val view: BrowserToolbar = layout + @VisibleForTesting + internal var view: BrowserToolbar = layout .findViewById(R.id.toolbar) val toolbarIntegration: ToolbarIntegration - private val isPwaTabOrTwaTab: Boolean + @VisibleForTesting + internal val isPwaTabOrTwaTab: Boolean get() = customTabSession?.customTabConfig?.externalAppType == ExternalAppType.PROGRESSIVE_WEB_APP || customTabSession?.customTabConfig?.externalAppType == ExternalAppType.TRUSTED_WEB_ACTIVITY @@ -101,17 +97,8 @@ class BrowserToolbarView( with(container.context) { val isPinningSupported = components.useCases.webAppUseCases.isPinningSupported() - if (toolbarPosition == ToolbarPosition.TOP) { - val offsetChangedListener = - AppBarLayout.OnOffsetChangedListener { _: AppBarLayout?, verticalOffset: Int -> - interactor.onScrolled(verticalOffset) - } - - app_bar.addOnOffsetChangedListener(offsetChangedListener) - } - view.apply { - setScrollFlags() + setToolbarBehavior() elevation = resources.getDimension(R.dimen.browser_fragment_toolbar_elevation) @@ -222,16 +209,9 @@ class BrowserToolbarView( if (isPwaTabOrTwaTab) { return } - when (settings.toolbarPosition) { - ToolbarPosition.BOTTOM -> { - (view.layoutParams as? CoordinatorLayout.LayoutParams)?.apply { - // behavior can be null if the "Scroll to hide toolbar" setting is toggled off. - (behavior as? BrowserToolbarBottomBehavior)?.forceExpand(view) - } - } - ToolbarPosition.TOP -> { - layout.app_bar?.setExpanded(true) - } + + (view.layoutParams as? CoordinatorLayout.LayoutParams)?.apply { + (behavior as? BrowserToolbarBehavior)?.forceExpand(view) } } @@ -240,41 +220,54 @@ class BrowserToolbarView( } /** - * Dynamically sets scroll flags for the toolbar when the user does not have a screen reader enabled - * Note that the toolbar will have the flags set and be able to be hidden - * only if the user didn't disabled this behavior in app's settings. + * Sets whether the toolbar will have a dynamic behavior (to be scrolled) or not. + * + * This will intrinsically check and disable the dynamic behavior if + * - this is disabled in app settings + * - toolbar is placed at the bottom and tab shows a PWA or TWA + * + * Also if the user has not explicitly set a toolbar position and has a screen reader enabled + * the toolbar will be placed at the top and in a fixed position. + * + * @param shouldDisableScroll force disable of the dynamic behavior irrespective of the intrinsic checks. */ - fun setScrollFlags(shouldDisableScroll: Boolean = false) { + fun setToolbarBehavior(shouldDisableScroll: Boolean = false) { when (settings.toolbarPosition) { ToolbarPosition.BOTTOM -> { if (settings.isDynamicToolbarEnabled && !isPwaTabOrTwaTab) { - (view.layoutParams as? CoordinatorLayout.LayoutParams)?.apply { - behavior = BrowserToolbarBottomBehavior(view.context, null) - } + setDynamicToolbarBehavior(MozacToolbarPosition.BOTTOM) } else { - expand() + expandToolbarAndMakeItFixed() } } ToolbarPosition.TOP -> { - view.updateLayoutParams { - scrollFlags = - if (settings.shouldUseFixedTopToolbar || - !settings.isDynamicToolbarEnabled || - shouldDisableScroll) { - // Force expand the toolbar so the user is not stuck with a hidden toolbar - expand() - 0 - } else { - SCROLL_FLAG_SCROLL or - SCROLL_FLAG_ENTER_ALWAYS or - SCROLL_FLAG_SNAP or - SCROLL_FLAG_EXIT_UNTIL_COLLAPSED - } + if (settings.shouldUseFixedTopToolbar || + !settings.isDynamicToolbarEnabled || + shouldDisableScroll + ) { + expandToolbarAndMakeItFixed() + } else { + setDynamicToolbarBehavior(MozacToolbarPosition.TOP) } } } } + @VisibleForTesting + internal fun expandToolbarAndMakeItFixed() { + expand() + (view.layoutParams as? CoordinatorLayout.LayoutParams)?.apply { + behavior = null + } + } + + @VisibleForTesting + internal fun setDynamicToolbarBehavior(toolbarPosition: MozacToolbarPosition) { + (view.layoutParams as? CoordinatorLayout.LayoutParams)?.apply { + behavior = BrowserToolbarBehavior(view.context, null, toolbarPosition) + } + } + @Suppress("ComplexCondition") private fun ToolbarMenu.Item.performHapticIfNeeded(view: View) { if (this is ToolbarMenu.Item.Reload && this.bypassCache || diff --git a/app/src/main/java/org/mozilla/fenix/components/toolbar/SwipeRefreshScrollingViewBehavior.kt b/app/src/main/java/org/mozilla/fenix/components/toolbar/SwipeRefreshScrollingViewBehavior.kt deleted file mode 100644 index b69bc0f943..0000000000 --- a/app/src/main/java/org/mozilla/fenix/components/toolbar/SwipeRefreshScrollingViewBehavior.kt +++ /dev/null @@ -1,50 +0,0 @@ -/* 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.components.toolbar - -import android.content.Context -import android.util.AttributeSet -import android.view.View -import androidx.coordinatorlayout.widget.CoordinatorLayout -import com.google.android.material.appbar.AppBarLayout -import kotlinx.coroutines.ExperimentalCoroutinesApi -import mozilla.components.concept.engine.EngineView -import mozilla.components.concept.engine.EngineView.InputResult.INPUT_RESULT_UNHANDLED -import org.mozilla.fenix.ext.settings - -/** - * ScrollingViewBehavior that will setScrollFlags on BrowserToolbar based on EngineView touch handling - */ -@ExperimentalCoroutinesApi -class SwipeRefreshScrollingViewBehavior( - context: Context, - attrs: AttributeSet?, - private val engineView: EngineView, - private val browserToolbarView: BrowserToolbarView -) : AppBarLayout.ScrollingViewBehavior(context, attrs) { - override fun onStartNestedScroll( - coordinatorLayout: CoordinatorLayout, - child: View, - directTargetChild: View, - target: View, - axes: Int, - type: Int - ): Boolean { - - if (!browserToolbarView.view.context.settings().shouldUseBottomToolbar) { - val shouldDisable = engineView.getInputResult() == INPUT_RESULT_UNHANDLED - browserToolbarView.setScrollFlags(shouldDisable) - } - - return super.onStartNestedScroll( - coordinatorLayout, - child, - directTargetChild, - target, - axes, - type - ) - } -} diff --git a/app/src/main/res/layout/component_browser_top_toolbar.xml b/app/src/main/res/layout/component_browser_top_toolbar.xml index 479bc9dd91..77f460296b 100644 --- a/app/src/main/res/layout/component_browser_top_toolbar.xml +++ b/app/src/main/res/layout/component_browser_top_toolbar.xml @@ -2,12 +2,9 @@ - + - - diff --git a/app/src/main/res/layout/fragment_browser.xml b/app/src/main/res/layout/fragment_browser.xml index 63150e569b..32d2bd4c4e 100644 --- a/app/src/main/res/layout/fragment_browser.xml +++ b/app/src/main/res/layout/fragment_browser.xml @@ -25,8 +25,7 @@ + android:layout_height="match_parent"> (relaxed = true) + } + + @Test + fun `initializeEngineView should setDynamicToolbarMaxHeight to 0 if top toolbar is forced for a11y`() { + every { testContext.settings().shouldUseBottomToolbar } returns false + every { testContext.settings().shouldUseFixedTopToolbar } returns true + + fragment.initializeEngineView(13) + + verify { engineView.setDynamicToolbarMaxHeight(0) } + } + + @Test + fun `initializeEngineView should setDynamicToolbarMaxHeight to toolbar height if dynamic toolbar is enabled`() { + every { testContext.settings().shouldUseFixedTopToolbar } returns false + every { testContext.settings().isDynamicToolbarEnabled } returns true + + fragment.initializeEngineView(13) + + verify { engineView.setDynamicToolbarMaxHeight(13) } + } + + @Test + fun `initializeEngineView should setDynamicToolbarMaxHeight to 0 if dynamic toolbar is disabled`() { + every { testContext.settings().shouldUseFixedTopToolbar } returns false + every { testContext.settings().isDynamicToolbarEnabled } returns false + + fragment.initializeEngineView(13) + + verify { engineView.setDynamicToolbarMaxHeight(0) } + } + + @Test + fun `initializeEngineView should set EngineViewBrowserToolbarBehavior when dynamic toolbar is enabled`() { + every { testContext.settings().shouldUseFixedTopToolbar } returns false + every { testContext.settings().isDynamicToolbarEnabled } returns true + val params: CoordinatorLayout.LayoutParams = mockk(relaxed = true) + every { params.behavior } returns mockk(relaxed = true) + every { swipeRefreshLayout.layoutParams } returns params + val behavior = slot() + + fragment.initializeEngineView(13) + + // EngineViewBrowserToolbarBehavior constructor parameters are not properties, we cannot check them. + // Ensure just that the right behavior is set. + verify { params.behavior = capture(behavior) } + } + + @Test + fun `initializeEngineView should set toolbar height as EngineView parent's bottom margin when using bottom toolbar`() { + every { testContext.settings().isDynamicToolbarEnabled } returns false + every { testContext.settings().shouldUseBottomToolbar } returns true + + fragment.initializeEngineView(13) + + verify { (swipeRefreshLayout.layoutParams as CoordinatorLayout.LayoutParams).bottomMargin = 13 } + } + + @Test + fun `initializeEngineView should set toolbar height as EngineView parent's bottom margin if top toolbar is forced for a11y`() { + every { testContext.settings().shouldUseBottomToolbar } returns false + every { testContext.settings().shouldUseFixedTopToolbar } returns true + + fragment.initializeEngineView(13) + + verify { (swipeRefreshLayout.layoutParams as CoordinatorLayout.LayoutParams).bottomMargin = 13 } + } +} + +@ExperimentalCoroutinesApi +private class TestBaseBrowserFragment : BaseBrowserFragment() { + override fun getContextMenuCandidates( + context: Context, + view: View + ): List { + // no-op + return emptyList() + } + + override fun navToQuickSettingsSheet(tab: SessionState, sitePermissions: SitePermissions?) { + // no-op + } + + override fun navToTrackingProtectionPanel(tab: SessionState) { + // no-op + } +} diff --git a/app/src/test/java/org/mozilla/fenix/components/toolbar/BrowserToolbarViewTest.kt b/app/src/test/java/org/mozilla/fenix/components/toolbar/BrowserToolbarViewTest.kt new file mode 100644 index 0000000000..f75b1d448f --- /dev/null +++ b/app/src/test/java/org/mozilla/fenix/components/toolbar/BrowserToolbarViewTest.kt @@ -0,0 +1,196 @@ +/* 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.components.toolbar + +import androidx.coordinatorlayout.widget.CoordinatorLayout + +import io.mockk.every +import io.mockk.mockk +import io.mockk.spyk +import io.mockk.verify +import kotlinx.coroutines.ExperimentalCoroutinesApi +import mozilla.components.support.test.robolectric.testContext +import org.junit.Test +import org.junit.runner.RunWith +import mozilla.components.browser.toolbar.BrowserToolbar +import mozilla.components.browser.toolbar.behavior.BrowserToolbarBehavior +import org.junit.Assert.assertNotNull +import org.junit.Assert.assertNull +import org.junit.Before +import org.mozilla.fenix.ext.settings +import org.mozilla.fenix.helpers.FenixRobolectricTestRunner +import mozilla.components.browser.toolbar.behavior.ToolbarPosition as MozacToolbarPosition + +@ExperimentalCoroutinesApi +@RunWith(FenixRobolectricTestRunner::class) +class BrowserToolbarViewTest { + private lateinit var toolbarView: BrowserToolbarView + private lateinit var toolbar: BrowserToolbar + private lateinit var behavior: BrowserToolbarBehavior + + @Before + fun `setup`() { + toolbar = BrowserToolbar(testContext) + toolbar.layoutParams = CoordinatorLayout.LayoutParams(100, 100) + behavior = spyk(BrowserToolbarBehavior(testContext, null, MozacToolbarPosition.BOTTOM)) + (toolbar.layoutParams as CoordinatorLayout.LayoutParams).behavior = behavior + + toolbarView = BrowserToolbarView( + container = CoordinatorLayout(testContext), + toolbarPosition = ToolbarPosition.BOTTOM, + interactor = mockk(), + customTabSession = mockk(relaxed = true), + lifecycleOwner = mockk() + ) + + toolbarView.view = toolbar + } + + @Test + fun `setToolbarBehavior(false) should setDynamicToolbarBehavior if bottom toolbar is dynamic and the tab is not for a PWA or TWA`() { + val toolbarViewSpy = spyk(toolbarView) + every { testContext.settings().toolbarPosition } returns ToolbarPosition.BOTTOM + every { testContext.settings().isDynamicToolbarEnabled } returns true + every { toolbarViewSpy.isPwaTabOrTwaTab } returns false + + toolbarViewSpy.setToolbarBehavior(false) + + verify { toolbarViewSpy.setDynamicToolbarBehavior(MozacToolbarPosition.BOTTOM) } + } + + @Test + fun `setToolbarBehavior(false) should expandToolbarAndMakeItFixed if bottom toolbar is not set as dynamic`() { + val toolbarViewSpy = spyk(toolbarView) + every { testContext.settings().toolbarPosition } returns ToolbarPosition.BOTTOM + every { testContext.settings().isDynamicToolbarEnabled } returns false + + toolbarViewSpy.setToolbarBehavior(false) + + verify { toolbarViewSpy.expandToolbarAndMakeItFixed() } + } + + @Test + fun `setToolbarBehavior(false) should expandToolbarAndMakeItFixed if bottom toolbar is dynamic but the tab is for a PWA or TWA`() { + val toolbarViewSpy = spyk(toolbarView) + every { testContext.settings().toolbarPosition } returns ToolbarPosition.BOTTOM + every { testContext.settings().isDynamicToolbarEnabled } returns true + every { toolbarViewSpy.isPwaTabOrTwaTab } returns true + + toolbarViewSpy.setToolbarBehavior(false) + + verify { toolbarViewSpy.expandToolbarAndMakeItFixed() } + } + + @Test + fun `setToolbarBehavior(true) should setDynamicToolbarBehavior if bottom toolbar is dynamic and the tab is not for a PWA or TWA`() { + val toolbarViewSpy = spyk(toolbarView) + every { testContext.settings().toolbarPosition } returns ToolbarPosition.BOTTOM + every { testContext.settings().isDynamicToolbarEnabled } returns true + every { toolbarViewSpy.isPwaTabOrTwaTab } returns false + + toolbarViewSpy.setToolbarBehavior(false) + + verify { toolbarViewSpy.setDynamicToolbarBehavior(MozacToolbarPosition.BOTTOM) } + } + + @Test + fun `setToolbarBehavior(true) should expandToolbarAndMakeItFixed if bottom toolbar is not set as dynamic`() { + val toolbarViewSpy = spyk(toolbarView) + every { testContext.settings().toolbarPosition } returns ToolbarPosition.BOTTOM + every { testContext.settings().isDynamicToolbarEnabled } returns false + + toolbarViewSpy.setToolbarBehavior(false) + + verify { toolbarViewSpy.expandToolbarAndMakeItFixed() } + } + + @Test + fun `setToolbarBehavior(true) should expandToolbarAndMakeItFixed if bottom toolbar is dynamic but the tab is for a PWA or TWA`() { + val toolbarViewSpy = spyk(toolbarView) + every { testContext.settings().toolbarPosition } returns ToolbarPosition.BOTTOM + every { testContext.settings().isDynamicToolbarEnabled } returns true + every { toolbarViewSpy.isPwaTabOrTwaTab } returns true + + toolbarViewSpy.setToolbarBehavior(false) + + verify { toolbarViewSpy.expandToolbarAndMakeItFixed() } + } + + @Test + fun `setToolbarBehavior(true) should expandToolbarAndMakeItFixed for top toolbar if shouldUseFixedTopToolbar`() { + val toolbarViewSpy = spyk(toolbarView) + every { testContext.settings().toolbarPosition } returns ToolbarPosition.TOP + every { testContext.settings().shouldUseFixedTopToolbar } returns true + + toolbarViewSpy.setToolbarBehavior(true) + + verify { toolbarViewSpy.expandToolbarAndMakeItFixed() } + } + + @Test + fun `setToolbarBehavior(true) should expandToolbarAndMakeItFixed for top toolbar if it is not dynamic`() { + val toolbarViewSpy = spyk(toolbarView) + every { testContext.settings().toolbarPosition } returns ToolbarPosition.TOP + every { testContext.settings().isDynamicToolbarEnabled } returns false + + toolbarViewSpy.setToolbarBehavior(true) + + verify { toolbarViewSpy.expandToolbarAndMakeItFixed() } + } + + @Test + fun `setToolbarBehavior(true) should expandToolbarAndMakeItFixed for top toolbar if shouldDisableScroll`() { + val toolbarViewSpy = spyk(toolbarView) + every { testContext.settings().toolbarPosition } returns ToolbarPosition.TOP + + toolbarViewSpy.setToolbarBehavior(true) + + verify { toolbarViewSpy.expandToolbarAndMakeItFixed() } + } + + @Test + fun `setToolbarBehavior(false) should setDynamicToolbarBehavior for top toolbar`() { + val toolbarViewSpy = spyk(toolbarView) + every { testContext.settings().toolbarPosition } returns ToolbarPosition.TOP + every { testContext.settings().shouldUseFixedTopToolbar } returns true + every { testContext.settings().isDynamicToolbarEnabled } returns true + + toolbarViewSpy.setToolbarBehavior(true) + + verify { toolbarViewSpy.expandToolbarAndMakeItFixed() } + } + + @Test + fun `expandToolbarAndMakeItFixed should expand the toolbar and and disable the dynamic behavior`() { + val toolbarViewSpy = spyk(toolbarView) + + assertNotNull((toolbar.layoutParams as CoordinatorLayout.LayoutParams).behavior) + + toolbarViewSpy.expandToolbarAndMakeItFixed() + + verify { toolbarViewSpy.expand() } + assertNull((toolbar.layoutParams as CoordinatorLayout.LayoutParams).behavior) + } + + @Test + fun `setDynamicToolbarBehavior should set a BrowserToolbarBehavior for the bottom toolbar`() { + val toolbarViewSpy = spyk(toolbarView) + (toolbar.layoutParams as CoordinatorLayout.LayoutParams).behavior = null + + toolbarViewSpy.setDynamicToolbarBehavior(MozacToolbarPosition.BOTTOM) + + assertNotNull((toolbar.layoutParams as CoordinatorLayout.LayoutParams).behavior) + } + + @Test + fun `setDynamicToolbarBehavior should set a BrowserToolbarBehavior for the top toolbar`() { + val toolbarViewSpy = spyk(toolbarView) + (toolbar.layoutParams as CoordinatorLayout.LayoutParams).behavior = null + + toolbarViewSpy.setDynamicToolbarBehavior(MozacToolbarPosition.TOP) + + assertNotNull((toolbar.layoutParams as CoordinatorLayout.LayoutParams).behavior) + } +}