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.upstream-sync
parent
a3d401a3b7
commit
8bf1cae2ca
@ -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
|
||||
)
|
||||
}
|
||||
}
|
@ -0,0 +1,133 @@
|
||||
/* 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.browser
|
||||
|
||||
import android.content.Context
|
||||
import android.view.View
|
||||
import androidx.coordinatorlayout.widget.CoordinatorLayout
|
||||
import io.mockk.every
|
||||
import io.mockk.mockk
|
||||
import io.mockk.slot
|
||||
import io.mockk.spyk
|
||||
import io.mockk.verify
|
||||
import kotlinx.coroutines.ExperimentalCoroutinesApi
|
||||
import mozilla.components.browser.state.state.SessionState
|
||||
import mozilla.components.concept.engine.EngineView
|
||||
import mozilla.components.feature.contextmenu.ContextMenuCandidate
|
||||
import mozilla.components.feature.session.behavior.EngineViewBrowserToolbarBehavior
|
||||
import mozilla.components.feature.sitepermissions.SitePermissions
|
||||
import mozilla.components.support.test.robolectric.testContext
|
||||
import mozilla.components.ui.widgets.VerticalSwipeRefreshLayout
|
||||
import org.junit.Before
|
||||
import org.junit.Test
|
||||
import org.junit.runner.RunWith
|
||||
import org.mozilla.fenix.ext.settings
|
||||
import org.mozilla.fenix.helpers.FenixRobolectricTestRunner
|
||||
|
||||
@ExperimentalCoroutinesApi
|
||||
@RunWith(FenixRobolectricTestRunner::class)
|
||||
class BaseBrowserFragmentTest {
|
||||
private lateinit var fragment: TestBaseBrowserFragment
|
||||
private lateinit var swipeRefreshLayout: VerticalSwipeRefreshLayout
|
||||
private lateinit var engineView: EngineView
|
||||
|
||||
@Before
|
||||
fun setup() {
|
||||
fragment = spyk(TestBaseBrowserFragment())
|
||||
swipeRefreshLayout = mockk(relaxed = true)
|
||||
engineView = mockk(relaxed = true)
|
||||
every { fragment.isAdded } returns true
|
||||
every { fragment.activity } returns mockk()
|
||||
every { fragment.requireContext() } returns testContext
|
||||
every { fragment.getEngineView() } returns engineView
|
||||
every { fragment.getSwipeRefreshLayout() } returns swipeRefreshLayout
|
||||
every { swipeRefreshLayout.layoutParams } returns mockk<CoordinatorLayout.LayoutParams>(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<EngineViewBrowserToolbarBehavior>()
|
||||
|
||||
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<ContextMenuCandidate> {
|
||||
// no-op
|
||||
return emptyList()
|
||||
}
|
||||
|
||||
override fun navToQuickSettingsSheet(tab: SessionState, sitePermissions: SitePermissions?) {
|
||||
// no-op
|
||||
}
|
||||
|
||||
override fun navToTrackingProtectionPanel(tab: SessionState) {
|
||||
// no-op
|
||||
}
|
||||
}
|
@ -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)
|
||||
}
|
||||
}
|
Loading…
Reference in New Issue