commit 9663bdfc9d264723a6815154dd1865d70de5b025 parent c58d5b4ce1e0fb6f7efaecd1b2dae6e932ec313c Author: Mugurell <Mugurell@users.noreply.github.com> Date: Wed, 17 Dec 2025 16:06:38 +0000 Bug 1988730 - part 5 - Update browser paddings when the keyboard is shown or hidden r=android-reviewers,moyin Complex code showing that we have room for improvement on the layout structure on the browser screen but on manual testing on wikipedia and https://interop-2022-viewport.netlify.app/combined/viewport-units/ this rules should cover all scenarios of bottom & top toolbars. Differential Revision: https://phabricator.services.mozilla.com/D275993 Diffstat:
10 files changed, 336 insertions(+), 37 deletions(-)
diff --git a/mobile/android/android-components/components/browser/state/src/main/java/mozilla/components/browser/state/ext/CustomTabSessionState.kt b/mobile/android/android-components/components/browser/state/src/main/java/mozilla/components/browser/state/ext/CustomTabSessionState.kt @@ -5,8 +5,22 @@ package mozilla.components.browser.state.ext import mozilla.components.browser.state.state.CustomTabSessionState +import mozilla.components.browser.state.state.ExternalAppType.PROGRESSIVE_WEB_APP +import mozilla.components.browser.state.state.ExternalAppType.TRUSTED_WEB_ACTIVITY import mozilla.components.browser.state.state.TabSessionState +/** + * Whether this custom tab is showing a Progressive Web Application. + */ +val CustomTabSessionState?.isPWA: Boolean + get() = this?.config?.externalAppType == PROGRESSIVE_WEB_APP + +/** + * Whether this custom tab is showing a Trusted Web Activity. + */ +val CustomTabSessionState?.isTWA: Boolean + get() = this?.config?.externalAppType == TRUSTED_WEB_ACTIVITY + internal fun CustomTabSessionState.toTab(): TabSessionState { return TabSessionState( id = id, diff --git a/mobile/android/android-components/components/compose/browser-toolbar/src/main/java/mozilla/components/compose/browser/toolbar/ui/MinimalDisplayToolbar.kt b/mobile/android/android-components/components/compose/browser-toolbar/src/main/java/mozilla/components/compose/browser/toolbar/ui/MinimalDisplayToolbar.kt @@ -18,6 +18,7 @@ import androidx.compose.ui.focus.FocusRequester import androidx.compose.ui.input.pointer.pointerInput import androidx.compose.ui.platform.LocalSoftwareKeyboardController import androidx.compose.ui.platform.testTag +import androidx.compose.ui.res.dimensionResource import androidx.compose.ui.res.stringResource import androidx.compose.ui.semantics.clearAndSetSemantics import androidx.compose.ui.semantics.contentDescription @@ -25,7 +26,6 @@ import androidx.compose.ui.semantics.testTag import androidx.compose.ui.semantics.testTagsAsResourceId import androidx.compose.ui.tooling.preview.PreviewLightDark import androidx.compose.ui.tooling.preview.PreviewParameter -import androidx.compose.ui.unit.dp import mozilla.components.compose.base.theme.AcornTheme import mozilla.components.compose.browser.toolbar.ActionContainer import mozilla.components.compose.browser.toolbar.R @@ -41,8 +41,6 @@ import mozilla.components.compose.browser.toolbar.utils.DisplayToolbarDataProvid import mozilla.components.compose.browser.toolbar.utils.DisplayToolbarPreviewModel import mozilla.components.support.ktx.kotlin.getRegistrableDomainIndexRange -private const val MINIMAL_TOOLBAR_HEIGHT_DP = 32 - @Composable internal fun MinimalDisplayToolbar( pageOrigin: PageOrigin, @@ -69,7 +67,7 @@ internal fun MinimalDisplayToolbar( Box { Row( modifier = modifier - .requiredHeight(MINIMAL_TOOLBAR_HEIGHT_DP.dp) + .requiredHeight(dimensionResource(R.dimen.mozac_minimal_display_toolbar_height)) .clearAndSetSemantics { this.contentDescription = contentDescription testTagsAsResourceId = true diff --git a/mobile/android/android-components/components/compose/browser-toolbar/src/main/res/values/dimens.xml b/mobile/android/android-components/components/compose/browser-toolbar/src/main/res/values/dimens.xml @@ -0,0 +1,7 @@ +<?xml version="1.0" encoding="utf-8"?> +<!-- 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/. --> +<resources> + <dimen name="mozac_minimal_display_toolbar_height">32dp</dimen> +</resources> diff --git a/mobile/android/android-components/components/feature/pwa/src/main/java/mozilla/components/feature/pwa/feature/WebAppHideToolbarFeature.kt b/mobile/android/android-components/components/feature/pwa/src/main/java/mozilla/components/feature/pwa/feature/WebAppHideToolbarFeature.kt @@ -25,6 +25,7 @@ import mozilla.components.feature.pwa.ext.trustedOrigins import mozilla.components.lib.state.ext.flow import mozilla.components.support.base.feature.LifecycleAwareFeature import mozilla.components.support.ktx.android.net.isInScope +import kotlin.properties.Delegates /** * Hides a custom tab toolbar for Progressive Web Apps and Trusted Web Activities. @@ -51,6 +52,15 @@ class WebAppHideToolbarFeature( private val setToolbarVisibility: (Boolean) -> Unit, ) : LifecycleAwareFeature { + private var _shouldToolbarsBeVisible: Boolean by Delegates.observable(false) { _, _, newValue -> + setToolbarVisibility(newValue) + } + + /** + * Whether the toolbar should be visible for the current tab. + */ + val shouldToolbarsBeVisible = _shouldToolbarsBeVisible + private val manifestScope = listOfNotNull(manifest?.getTrustedScope()) private var scope: CoroutineScope? = null @@ -58,7 +68,7 @@ class WebAppHideToolbarFeature( // Hide the toolbar by default to prevent a flash. val tab = store.state.findTabOrCustomTabOrSelectedTab(tabId) val customTabState = customTabsStore.state.getCustomTabStateForTab(tab) - setToolbarVisibility(shouldToolbarBeVisible(tab, customTabState)) + _shouldToolbarsBeVisible = shouldToolbarBeVisible(tab, customTabState) } override fun start() { @@ -78,7 +88,7 @@ class WebAppHideToolbarFeature( .map { (tab, customTabState) -> shouldToolbarBeVisible(tab, customTabState) } .distinctUntilChanged() .collect { toolbarVisible -> - setToolbarVisibility(toolbarVisible) + _shouldToolbarsBeVisible = toolbarVisible } } } diff --git a/mobile/android/fenix/app/src/main/java/org/mozilla/fenix/browser/BaseBrowserFragment.kt b/mobile/android/fenix/app/src/main/java/org/mozilla/fenix/browser/BaseBrowserFragment.kt @@ -110,6 +110,7 @@ import mozilla.components.feature.prompts.login.LoginSelectBar import mozilla.components.feature.prompts.login.SuggestStrongPasswordBar import mozilla.components.feature.prompts.login.SuggestStrongPasswordDelegate import mozilla.components.feature.prompts.share.ShareDelegate +import mozilla.components.feature.pwa.feature.WebAppHideToolbarFeature import mozilla.components.feature.readerview.ReaderViewFeature import mozilla.components.feature.search.SearchFeature import mozilla.components.feature.session.FullScreenFeature @@ -189,6 +190,7 @@ import org.mozilla.fenix.components.toolbar.FenixBrowserToolbarView import org.mozilla.fenix.components.toolbar.ToolbarContainerView import org.mozilla.fenix.components.toolbar.ToolbarIntegration import org.mozilla.fenix.components.toolbar.ToolbarPosition +import org.mozilla.fenix.components.toolbar.ToolbarsIntegration import org.mozilla.fenix.components.toolbar.interactor.BrowserToolbarInteractor import org.mozilla.fenix.components.toolbar.interactor.DefaultBrowserToolbarInteractor import org.mozilla.fenix.compose.core.Action @@ -326,9 +328,12 @@ abstract class BaseBrowserFragment : @VisibleForTesting internal val findInPageIntegration = ViewBoundFeatureWrapper<FindInPageIntegration>() private val toolbarIntegration = ViewBoundFeatureWrapper<ToolbarIntegration>() + private val toolbarsIntegration = ViewBoundFeatureWrapper<ToolbarsIntegration>() private val bottomToolbarContainerIntegration = ViewBoundFeatureWrapper<BottomToolbarContainerIntegration>() private val sitePermissionsFeature = ViewBoundFeatureWrapper<SitePermissionsFeature>() private val fullScreenFeature = ViewBoundFeatureWrapper<FullScreenFeature>() + protected val hideToolbarFeature = ViewBoundFeatureWrapper<WebAppHideToolbarFeature>() + private val swipeRefreshFeature = ViewBoundFeatureWrapper<SwipeRefreshFeature>() private val webchannelIntegration = ViewBoundFeatureWrapper<FxaWebChannelIntegration>() private val sitePermissionWifiIntegration = @@ -647,6 +652,28 @@ abstract class BaseBrowserFragment : ) } + if (context.settings().shouldUseComposableToolbar) { + toolbarsIntegration.set( + feature = ToolbarsIntegration( + fullScreenFeature = { fullScreenFeature.get() }, + webAppHideToolbarFeature = { hideToolbarFeature.get() }, + settings = context.settings(), + browserLayout = getSwipeRefreshLayout(), + engineView = getEngineView(), + toolbar = browserToolbarView, + navbar = browserNavigationBar, + topToolbarHeight = { + getTopToolbarHeight( + includeTabStripIfAvailable = customTabSessionId == null, + ) + }, + onToolbarsReset = ::collapseBrowserView, + ), + owner = this, + view = view, + ) + } + findInPageBinding.set( feature = FindInPageBinding( appStore = context.components.appStore, diff --git a/mobile/android/fenix/app/src/main/java/org/mozilla/fenix/components/toolbar/FenixBrowserToolbarView.kt b/mobile/android/fenix/app/src/main/java/org/mozilla/fenix/components/toolbar/FenixBrowserToolbarView.kt @@ -7,9 +7,6 @@ package org.mozilla.fenix.components.toolbar import android.view.View import android.view.ViewGroup import androidx.annotation.VisibleForTesting -import androidx.compose.runtime.LaunchedEffect -import androidx.compose.runtime.getValue -import androidx.compose.ui.platform.ComposeView import androidx.coordinatorlayout.widget.CoordinatorLayout import androidx.core.view.isVisible import mozilla.components.browser.state.state.CustomTabSessionState @@ -17,9 +14,7 @@ import mozilla.components.browser.state.state.ExternalAppType import mozilla.components.concept.engine.EngineView import mozilla.components.concept.toolbar.ScrollableToolbar import mozilla.components.support.ktx.android.view.findViewInHierarchy -import mozilla.components.support.utils.KeyboardState import mozilla.components.support.utils.ext.isKeyboardVisible -import mozilla.components.support.utils.keyboardAsState import mozilla.components.ui.widgets.behavior.DependencyGravity.Bottom import mozilla.components.ui.widgets.behavior.DependencyGravity.Top import mozilla.components.ui.widgets.behavior.EngineViewScrollingBehavior @@ -39,10 +34,6 @@ abstract class FenixBrowserToolbarView( private val customTabSession: CustomTabSessionState?, ) : ScrollableToolbar { - init { - setupToolbarBehaviorBasedOnKeyboardState() - } - abstract val layout: View @VisibleForTesting @@ -165,25 +156,4 @@ abstract class FenixBrowserToolbarView( } protected fun shouldShowTabStrip() = customTabSession == null && settings.isTabStripEnabled - - private fun setupToolbarBehaviorBasedOnKeyboardState() { - parent.addView( - ComposeView(parent.context).apply { - setContent { - val keyboardState by keyboardAsState() - LaunchedEffect(keyboardState) { - when (keyboardState) { - KeyboardState.Closed -> { - enableScrolling() - } - KeyboardState.Opened -> { - disableScrolling() - expand() - } - } - } - } - }, - ) - } } diff --git a/mobile/android/fenix/app/src/main/java/org/mozilla/fenix/components/toolbar/ToolbarsIntegration.kt b/mobile/android/fenix/app/src/main/java/org/mozilla/fenix/components/toolbar/ToolbarsIntegration.kt @@ -0,0 +1,118 @@ +/* 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.view.ViewGroup +import android.view.ViewTreeObserver +import androidx.annotation.VisibleForTesting +import androidx.coordinatorlayout.widget.CoordinatorLayout +import mozilla.components.concept.engine.EngineView +import mozilla.components.feature.pwa.feature.WebAppHideToolbarFeature +import mozilla.components.feature.session.FullScreenFeature +import mozilla.components.support.base.feature.LifecycleAwareFeature +import mozilla.components.support.utils.ext.isKeyboardVisible +import org.mozilla.fenix.ext.pixelSizeFor +import org.mozilla.fenix.utils.Settings +import kotlin.math.roundToInt +import mozilla.components.compose.browser.toolbar.R as toolbarR + +/** + * Helper for ensuring the top and bottom toolbars are correctly configured depending on + * whether the IME is shown or not and will dynamically adapt all related properties when + * the IME visibility changes. + * This will also handle the scenarios in which the toolbars should not be visible as it + * happens when the engine view is shown in fullscreen. + * + * @param fullScreenFeature The [FullScreenFeature] instance used to check when the website is in fullscreen. + * @param webAppHideToolbarFeature The [WebAppHideToolbarFeature] instance used to check when the + * toolbars should be hidden for the current PWA configuration. + * @param settings [Settings] object to get the toolbar position and other settings. + * @param browserLayout The root layout of the engine view. + * @param engineView The engine View rendering web content. + * @param toolbar The top toolbar layout. + * @param navbar The bottom navigation bar layout. + * @param topToolbarHeight The height of the top toolbar. + * @param onToolbarsReset Callback to be invoked when the toolbars need to be reset. + */ +@Suppress("LongParameterList") +class ToolbarsIntegration( + private val fullScreenFeature: () -> FullScreenFeature?, + private val webAppHideToolbarFeature: () -> WebAppHideToolbarFeature?, + private val settings: Settings, + private val browserLayout: ViewGroup, + private val engineView: EngineView, + private val toolbar: FenixBrowserToolbarView, + private val navbar: BrowserNavigationBar?, + private val topToolbarHeight: () -> Int, + private val onToolbarsReset: () -> Unit, +) : LifecycleAwareFeature { + private var layoutListener: ViewTreeObserver.OnGlobalLayoutListener? = null + + override fun start() { + var wasKeyboardShown = false + + layoutListener = ViewTreeObserver.OnGlobalLayoutListener { + val isKeyboardShown = browserLayout.isKeyboardVisible() + if (wasKeyboardShown != isKeyboardShown) { + wasKeyboardShown = isKeyboardShown + onKeyboardShown(isKeyboardShown) + } + } + browserLayout.viewTreeObserver.addOnGlobalLayoutListener(layoutListener) + } + + override fun stop() { + browserLayout.viewTreeObserver.removeOnGlobalLayoutListener(layoutListener) + } + + @VisibleForTesting + internal fun onKeyboardShown(isKeyboardShown: Boolean) { + // Ignore any updates relating to the toolbar if the toolbar should not be visible. + if (fullScreenFeature()?.isFullScreen == true || + webAppHideToolbarFeature()?.shouldToolbarsBeVisible == false + ) { + return + } + + val browserLayoutParams = browserLayout.layoutParams as CoordinatorLayout.LayoutParams + when (isKeyboardShown) { + true -> { + toolbar.disableScrolling() + navbar?.disableScrolling() + toolbar.expand() + navbar?.expand() + + val isUsingBottomToolbar = settings.shouldUseBottomToolbar + val isTopToolbarShown = browserLayout.translationY.roundToInt() > 0 + + browserLayoutParams.behavior = null + browserLayoutParams.topMargin = when { + isTopToolbarShown -> 0 + else -> topToolbarHeight() + } + browserLayoutParams.bottomMargin = when { + isUsingBottomToolbar -> browserLayout.pixelSizeFor( + toolbarR.dimen.mozac_minimal_display_toolbar_height, + ) + isTopToolbarShown -> topToolbarHeight() + else -> 0 + } + + engineView.apply { + setDynamicToolbarMaxHeight(0) + setVerticalClipping(0) + } + } + false -> { + toolbar.enableScrolling() + navbar?.enableScrolling() + + browserLayoutParams.topMargin = 0 + browserLayoutParams.bottomMargin = 0 + onToolbarsReset() + } + } + } +} diff --git a/mobile/android/fenix/app/src/main/java/org/mozilla/fenix/customtabs/ExternalAppBrowserFragment.kt b/mobile/android/fenix/app/src/main/java/org/mozilla/fenix/customtabs/ExternalAppBrowserFragment.kt @@ -51,7 +51,6 @@ class ExternalAppBrowserFragment : BaseBrowserFragment() { private val customTabsIntegration = ViewBoundFeatureWrapper<CustomTabsIntegration>() private val customTabColorsBinding = ViewBoundFeatureWrapper<CustomTabColorsBinding>() private val windowFeature = ViewBoundFeatureWrapper<CustomTabWindowFeature>() - private val hideToolbarFeature = ViewBoundFeatureWrapper<WebAppHideToolbarFeature>() @Suppress("LongMethod") override fun initializeUI(view: View, tab: SessionState) { diff --git a/mobile/android/fenix/app/src/test/java/org/mozilla/fenix/components/AppStoreTest.kt b/mobile/android/fenix/app/src/test/java/org/mozilla/fenix/components/AppStoreTest.kt @@ -5,6 +5,7 @@ package org.mozilla.fenix.components import android.content.Context +import androidx.test.ext.junit.runners.AndroidJUnit4 import io.mockk.every import io.mockk.mockk import kotlinx.coroutines.test.runTest @@ -28,6 +29,7 @@ import org.junit.Assert.assertFalse import org.junit.Assert.assertTrue import org.junit.Before import org.junit.Test +import org.junit.runner.RunWith import org.mozilla.fenix.R import org.mozilla.fenix.browser.browsingmode.BrowsingMode import org.mozilla.fenix.browser.browsingmode.BrowsingModeManager @@ -54,6 +56,7 @@ import org.mozilla.fenix.home.recentvisits.RecentlyVisitedItem.RecentHistoryHigh import org.mozilla.fenix.messaging.FenixMessageSurfaceId import org.mozilla.fenix.onboarding.FenixOnboarding +@RunWith(AndroidJUnit4::class) class AppStoreTest { private lateinit var context: Context private lateinit var accountManager: FxaAccountManager diff --git a/mobile/android/fenix/app/src/test/java/org/mozilla/fenix/components/toolbar/ToolbarsIntegrationTest.kt b/mobile/android/fenix/app/src/test/java/org/mozilla/fenix/components/toolbar/ToolbarsIntegrationTest.kt @@ -0,0 +1,153 @@ +/* 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.view.ViewGroup +import android.view.ViewTreeObserver +import androidx.coordinatorlayout.widget.CoordinatorLayout +import io.mockk.every +import io.mockk.mockk +import io.mockk.verify +import junit.framework.TestCase.assertEquals +import mozilla.components.concept.engine.EngineView +import mozilla.components.feature.pwa.feature.WebAppHideToolbarFeature +import mozilla.components.feature.session.FullScreenFeature +import org.junit.Before +import org.junit.Test +import org.mozilla.fenix.utils.Settings +import mozilla.components.compose.browser.toolbar.R as toolbarR + +class ToolbarsIntegrationTest { + private val fullScreenFeature: FullScreenFeature = mockk(relaxed = true) + private val webAppHideToolbarFeature: WebAppHideToolbarFeature = mockk(relaxed = true) + private val settings: Settings = mockk(relaxed = true) + + private val context: Context = mockk(relaxed = true) + private val browserLayout: ViewGroup = mockk(relaxed = true) + private val engineView: EngineView = mockk(relaxed = true) + private val toolbar: FenixBrowserToolbarView = mockk(relaxed = true) + private val navbar: BrowserNavigationBar = mockk(relaxed = true) + private val onToolbarsReset: () -> Unit = mockk(relaxed = true) + + private val topToolbarHeight = 150 + private val minimalBottomToolbarHeight = 32 + private val layoutParams: CoordinatorLayout.LayoutParams = mockk(relaxed = true) + private val viewTreeObserver: ViewTreeObserver = mockk(relaxed = true) + + private lateinit var toolbarsIntegration: ToolbarsIntegration + + @Before + fun setUp() { + every { browserLayout.context } returns context + every { browserLayout.layoutParams } returns layoutParams + every { browserLayout.viewTreeObserver } returns viewTreeObserver + every { browserLayout.resources } returns mockk(relaxed = true) { + every { + getDimensionPixelSize(toolbarR.dimen.mozac_minimal_display_toolbar_height) + } returns minimalBottomToolbarHeight + } + layoutParams.behavior = mockk(relaxed = true) + + toolbarsIntegration = ToolbarsIntegration( + fullScreenFeature = { fullScreenFeature }, + webAppHideToolbarFeature = { webAppHideToolbarFeature }, + settings = settings, + browserLayout = browserLayout, + engineView = engineView, + toolbar = toolbar, + navbar = navbar, + topToolbarHeight = { topToolbarHeight }, + onToolbarsReset = onToolbarsReset, + ) + } + + @Test + fun `GIVEN fullscreen is active WHEN keyboard is shown THEN do nothing`() { + every { fullScreenFeature.isFullScreen } returns true + // Set some initial margins to then check they are not changed. + layoutParams.topMargin = 23 + layoutParams.bottomMargin = 32 + + toolbarsIntegration.onKeyboardShown(isKeyboardShown = true) + + verify(exactly = 0) { onToolbarsReset() } + verify(exactly = 0) { toolbar.enableScrolling() } + verify(exactly = 0) { navbar.enableScrolling() } + assertEquals(23, layoutParams.topMargin) + assertEquals(32, layoutParams.bottomMargin) + } + + @Test + fun `GIVEN web app toolbar should be hidden WHEN keyboard is shown THEN do nothing`() { + every { webAppHideToolbarFeature.shouldToolbarsBeVisible } returns false + // Set some initial margins to then check they are not changed. + layoutParams.topMargin = 34 + layoutParams.bottomMargin = 45 + + toolbarsIntegration.onKeyboardShown(isKeyboardShown = true) + + verify(exactly = 0) { onToolbarsReset() } + assertEquals(34, layoutParams.topMargin) + assertEquals(45, layoutParams.bottomMargin) + } + + @Test + fun `GIVEN a normal state WHEN keyboard is hidden THEN margins should be zeroed and toolbars reset`() { + every { fullScreenFeature.isFullScreen } returns false + every { webAppHideToolbarFeature.shouldToolbarsBeVisible } returns true + // Set initial margins to non-zero values to ensure they are changed + layoutParams.topMargin = 100 + layoutParams.bottomMargin = 100 + + toolbarsIntegration.onKeyboardShown(isKeyboardShown = false) + + assertEquals(0, layoutParams.topMargin) + assertEquals(0, layoutParams.bottomMargin) + verify { onToolbarsReset() } + verify { toolbar.enableScrolling() } + verify { navbar.enableScrolling() } + } + + @Test + fun `GIVEN a top toolbar WHEN keyboard is shown THEN restore margins for top toolbar`() { + every { fullScreenFeature.isFullScreen } returns false + every { webAppHideToolbarFeature.shouldToolbarsBeVisible } returns true + every { settings.shouldUseBottomToolbar } returns false + every { browserLayout.translationY } returns 1f + + toolbarsIntegration.onKeyboardShown(isKeyboardShown = true) + + verify { layoutParams.behavior = null } + assertEquals(0, layoutParams.topMargin) + assertEquals(topToolbarHeight, layoutParams.bottomMargin) + verify { engineView.setDynamicToolbarMaxHeight(0) } + verify { engineView.setVerticalClipping(0) } + verify { toolbar.disableScrolling() } + verify { toolbar.expand() } + verify { navbar.disableScrolling() } + verify { navbar.expand() } + } + + @Test + fun `GIVEN a bottom toolbar WHEN keyboard is shown THEN restore margins for bottom toolbar`() { + every { fullScreenFeature.isFullScreen } returns false + every { webAppHideToolbarFeature.shouldToolbarsBeVisible } returns true + every { settings.shouldUseBottomToolbar } returns true + every { browserLayout.translationY } returns 1f + + toolbarsIntegration.onKeyboardShown(isKeyboardShown = true) + + verify { layoutParams.behavior = null } + assertEquals(0, layoutParams.topMargin) + assertEquals(minimalBottomToolbarHeight, layoutParams.bottomMargin) + verify { engineView.setDynamicToolbarMaxHeight(0) } + verify { engineView.setVerticalClipping(0) } + verify { toolbar.disableScrolling() } + verify { toolbar.expand() } + verify { navbar.disableScrolling() } + verify { navbar.expand() } + } +}