Bug 1868261 - Add hiding on scroll behaviour to navbar

fenix/125.0
AndiAJ 3 months ago committed by mergify[bot]
parent 552a96e5ae
commit d42aca60dc

@ -131,7 +131,6 @@ import org.mozilla.fenix.components.FenixSnackbar
import org.mozilla.fenix.components.FindInPageIntegration
import org.mozilla.fenix.components.StoreProvider
import org.mozilla.fenix.components.metrics.MetricsUtils
import org.mozilla.fenix.components.toolbar.BottomToolbarContainerView
import org.mozilla.fenix.components.toolbar.BrowserFragmentState
import org.mozilla.fenix.components.toolbar.BrowserFragmentStore
import org.mozilla.fenix.components.toolbar.BrowserToolbarView
@ -142,6 +141,8 @@ import org.mozilla.fenix.components.toolbar.ToolbarIntegration
import org.mozilla.fenix.components.toolbar.ToolbarPosition
import org.mozilla.fenix.components.toolbar.interactor.BrowserToolbarInteractor
import org.mozilla.fenix.components.toolbar.interactor.DefaultBrowserToolbarInteractor
import org.mozilla.fenix.components.toolbar.navbar.BottomToolbarContainerView
import org.mozilla.fenix.components.toolbar.navbar.NavbarIntegration
import org.mozilla.fenix.crashes.CrashContentIntegration
import org.mozilla.fenix.customtabs.ExternalAppBrowserActivity
import org.mozilla.fenix.databinding.FragmentBrowserBinding
@ -223,6 +224,7 @@ abstract class BaseBrowserFragment :
private val promptsFeature = ViewBoundFeatureWrapper<PromptFeature>()
private val findInPageIntegration = ViewBoundFeatureWrapper<FindInPageIntegration>()
private val toolbarIntegration = ViewBoundFeatureWrapper<ToolbarIntegration>()
private val navbarIntegration = ViewBoundFeatureWrapper<NavbarIntegration>()
private val sitePermissionsFeature = ViewBoundFeatureWrapper<SitePermissionsFeature>()
private val fullScreenFeature = ViewBoundFeatureWrapper<FullScreenFeature>()
private val swipeRefreshFeature = ViewBoundFeatureWrapper<SwipeRefreshFeature>()
@ -467,11 +469,17 @@ abstract class BaseBrowserFragment :
BottomToolbarContainerView(
context = context,
container = binding.browserLayout,
parent = binding.browserLayout,
androidToolbarView = if (isToolbarAtBottom) browserToolbar else null,
menuButton = menuButton,
browsingModeManager = activity.browsingModeManager,
)
).also {
navbarIntegration.set(
feature = it.navbarIntegration,
owner = this,
view = view,
)
}
}
toolbarIntegration.set(

@ -2,12 +2,14 @@
* 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
package org.mozilla.fenix.components.toolbar.navbar
import android.content.Context
import android.util.AttributeSet
import android.view.Gravity
import android.view.View
import android.view.ViewGroup
import android.widget.LinearLayout
import androidx.compose.foundation.layout.Column
import androidx.compose.ui.platform.ComposeView
import androidx.compose.ui.viewinterop.AndroidView
@ -15,7 +17,10 @@ import androidx.coordinatorlayout.widget.CoordinatorLayout
import mozilla.components.browser.menu.view.MenuButton
import mozilla.components.browser.state.selector.normalTabs
import mozilla.components.browser.state.selector.privateTabs
import mozilla.components.concept.toolbar.ScrollableToolbar
import mozilla.components.lib.state.ext.observeAsState
import mozilla.components.ui.widgets.behavior.EngineViewScrollingBehavior
import mozilla.components.ui.widgets.behavior.ViewPosition
import org.mozilla.fenix.browser.browsingmode.BrowsingMode
import org.mozilla.fenix.browser.browsingmode.BrowsingModeManager
import org.mozilla.fenix.compose.Divider
@ -26,25 +31,31 @@ import org.mozilla.fenix.theme.FirefoxTheme
* A helper class to add NavigationBar composable to a [ViewGroup].
*
* @param context The Context the view is running in.
* @param container The ViewGroup into which the NavigationBar composable will be added.
* @param parent The ViewGroup into which the NavigationBar composable will be added.
* @param navigationItems A list of [ActionItem] objects representing the items to be displayed in the navigation bar.
* @param androidToolbarView An option toolbar view that will be added atop of the navigation bar.
* @param menuButton A [MenuButton] to be used for [ItemType.MENU].
* @param browsingModeManager A helper class that provides access to the current [BrowsingMode].
* @param customTabSessionId Custom tab session ID.
*
* Defaults to [NavigationItems.defaultItems] which provides a standard set of navigation items.
*/
class BottomToolbarContainerView(
context: Context,
container: ViewGroup,
parent: ViewGroup,
navigationItems: List<ActionItem> = NavigationItems.defaultItems,
androidToolbarView: View? = null,
menuButton: MenuButton,
browsingModeManager: BrowsingModeManager,
customTabSessionId: String? = null,
) {
private val toolbarContainerView = ToolbarContainerView(context)
val navbarIntegration =
NavbarIntegration(toolbarContainerView, parent.context.components.core.store, customTabSessionId)
init {
val composeView = ComposeView(context).apply {
ComposeView(parent.context).apply {
setContent {
val isPrivate = browsingModeManager.mode.isPrivate
val tabCount = context.components.core.store.observeAsState(initialValue = 0) { browserState ->
@ -71,16 +82,52 @@ class BottomToolbarContainerView(
}
}
}
toolbarContainerView.addView(this)
}
val layoutParams = CoordinatorLayout.LayoutParams(
toolbarContainerView.layoutParams = CoordinatorLayout.LayoutParams(
CoordinatorLayout.LayoutParams.MATCH_PARENT,
CoordinatorLayout.LayoutParams.WRAP_CONTENT,
).apply {
gravity = Gravity.BOTTOM
behavior = EngineViewScrollingBehavior(parent.context, null, ViewPosition.BOTTOM)
}
parent.addView(toolbarContainerView)
}
}
/**
* A container view that hosts a navigation bar and, possibly, a toolbar.
* Facilitates hide-on-scroll behavior.
*/
class ToolbarContainerView @JvmOverloads constructor(
context: Context,
attrs: AttributeSet? = null,
defStyleAttr: Int = 0,
) : LinearLayout(context, attrs, defStyleAttr), ScrollableToolbar {
override fun enableScrolling() {
(layoutParams as? CoordinatorLayout.LayoutParams)?.apply {
(behavior as? EngineViewScrollingBehavior)?.enableScrolling()
}
}
override fun disableScrolling() {
(layoutParams as? CoordinatorLayout.LayoutParams)?.apply {
(behavior as? EngineViewScrollingBehavior)?.disableScrolling()
}
}
composeView.layoutParams = layoutParams
container.addView(composeView)
override fun expand() {
(layoutParams as? CoordinatorLayout.LayoutParams)?.apply {
(behavior as? EngineViewScrollingBehavior)?.forceExpand(this@ToolbarContainerView)
}
}
override fun collapse() {
(layoutParams as? CoordinatorLayout.LayoutParams)?.apply {
(behavior as? EngineViewScrollingBehavior)?.forceCollapse(this@ToolbarContainerView)
}
}
}

@ -0,0 +1,34 @@
/* 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.navbar
import androidx.annotation.VisibleForTesting
import mozilla.components.browser.state.store.BrowserStore
import mozilla.components.concept.toolbar.ScrollableToolbar
import mozilla.components.feature.toolbar.ToolbarBehaviorController
import mozilla.components.support.base.feature.LifecycleAwareFeature
/**
* The feature responsible for scrolling behaviour of the navigation bar.
* When the content of a tab is being scrolled, the nav bar will react
* to the user interactions.
*/
class NavbarIntegration(
val toolbar: ScrollableToolbar,
val store: BrowserStore,
sessionId: String?,
) : LifecycleAwareFeature {
@VisibleForTesting(otherwise = VisibleForTesting.PRIVATE)
var toolbarController = ToolbarBehaviorController(toolbar, store, sessionId)
override fun start() {
toolbarController.start()
}
override fun stop() {
toolbarController.stop()
}
}

@ -2,7 +2,7 @@
* 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
package org.mozilla.fenix.components.toolbar.navbar
import androidx.compose.foundation.background
import androidx.compose.foundation.layout.Arrangement
@ -26,8 +26,8 @@ import androidx.compose.ui.unit.dp
import androidx.compose.ui.viewinterop.AndroidView
import mozilla.components.browser.menu.view.MenuButton
import org.mozilla.fenix.R
import org.mozilla.fenix.components.toolbar.ItemType.STANDARD
import org.mozilla.fenix.components.toolbar.ItemType.TAB_COUNTER
import org.mozilla.fenix.components.toolbar.navbar.ItemType.STANDARD
import org.mozilla.fenix.components.toolbar.navbar.ItemType.TAB_COUNTER
import org.mozilla.fenix.compose.TabCounter
import org.mozilla.fenix.compose.annotation.LightDarkPreview
import org.mozilla.fenix.theme.FirefoxTheme
@ -153,7 +153,7 @@ object NavigationItems {
val tabs = ActionItem(
iconId = R.drawable.mozac_ui_tabcounter_box,
descriptionResourceId = R.string.mozac_tab_counter_content_description,
type = ItemType.TAB_COUNTER,
type = TAB_COUNTER,
)
val defaultItems = listOf(back, forward, home, tabs, menu)

@ -92,9 +92,9 @@ import org.mozilla.fenix.components.FenixSnackbar
import org.mozilla.fenix.components.PrivateShortcutCreateManager
import org.mozilla.fenix.components.TabCollectionStorage
import org.mozilla.fenix.components.appstate.AppAction
import org.mozilla.fenix.components.toolbar.BottomToolbarContainerView
import org.mozilla.fenix.components.toolbar.IncompleteRedesignToolbarFeature
import org.mozilla.fenix.components.toolbar.ToolbarPosition
import org.mozilla.fenix.components.toolbar.navbar.BottomToolbarContainerView
import org.mozilla.fenix.databinding.FragmentHomeBinding
import org.mozilla.fenix.ext.components
import org.mozilla.fenix.ext.containsQueryParameters
@ -458,7 +458,7 @@ class HomeFragment : Fragment() {
BottomToolbarContainerView(
context = requireContext(),
container = binding.homeLayout,
parent = binding.homeLayout,
androidToolbarView = if (isToolbarAtBottom) binding.toolbarLayout else null,
menuButton = menuButton,
browsingModeManager = browsingModeManager,

@ -0,0 +1,43 @@
/* 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 io.mockk.mockk
import io.mockk.verify
import org.junit.Before
import org.junit.Test
import org.junit.runner.RunWith
import org.mozilla.fenix.components.toolbar.navbar.NavbarIntegration
import org.mozilla.fenix.helpers.FenixRobolectricTestRunner
@RunWith(FenixRobolectricTestRunner::class)
class NavbarIntegrationTest {
private lateinit var feature: NavbarIntegration
@Before
fun setup() {
feature = NavbarIntegration(
toolbar = mockk(),
store = mockk(),
sessionId = null,
).apply {
toolbarController = mockk(relaxed = true)
}
}
@Test
fun `WHEN the feature starts THEN toolbar controllers starts as well`() {
feature.start()
verify { feature.toolbarController.start() }
}
@Test
fun `WHEN the feature stops THEN toolbar controllers stops as well`() {
feature.stop()
verify { feature.toolbarController.stop() }
}
}
Loading…
Cancel
Save