Skip to content
This repository has been archived by the owner on Jun 17, 2024. It is now read-only.

Commit

Permalink
Bug 1879370 - Add a ClippingBehavior supporting multiple toolbars
Browse files Browse the repository at this point in the history
  • Loading branch information
mike a committed Mar 13, 2024
1 parent 51cd041 commit 73d5f32
Show file tree
Hide file tree
Showing 13 changed files with 571 additions and 47 deletions.
1 change: 1 addition & 0 deletions fenix/.buildconfig.yml
Original file line number Diff line number Diff line change
Expand Up @@ -83,6 +83,7 @@ projects:
- support-rusthttp
- support-rustlog
- support-test
- support-test-fakes
- support-test-libstate
- support-utils
- support-webextensions
Expand Down
1 change: 1 addition & 0 deletions fenix/app/build.gradle
Original file line number Diff line number Diff line change
Expand Up @@ -621,6 +621,7 @@ dependencies {
implementation project(':lib-push-firebase')
implementation project(':lib-state')
implementation project(':lib-dataprotect')
testImplementation project(':support-test-fakes')

debugImplementation ComponentsDependencies.leakcanary
debugImplementation ComponentsDependencies.androidx_compose_ui_tooling
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -108,7 +108,7 @@ abstract class AddonPopupBaseFragment : Fragment(), EngineSession.Observer, User
tryAgain = downloadsFeature.get()!!::tryAgain,
onCannotOpenFile = onCannotOpenFile,
binding = getDownloadDialogLayoutBinding(),
toolbarHeight = 0,
bottomToolbarHeight = 0,
) {}.show()
},
)
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -62,7 +62,9 @@ import mozilla.components.browser.state.state.TabSessionState
import mozilla.components.browser.state.state.content.DownloadState
import mozilla.components.browser.state.store.BrowserStore
import mozilla.components.browser.thumbnails.BrowserThumbnails
import mozilla.components.browser.toolbar.BrowserToolbar
import mozilla.components.concept.base.crash.Breadcrumb
import mozilla.components.concept.engine.EngineView
import mozilla.components.concept.engine.permission.SitePermissions
import mozilla.components.concept.engine.prompt.ShareData
import mozilla.components.concept.storage.LoginEntry
Expand Down Expand Up @@ -113,7 +115,6 @@ import mozilla.components.support.ktx.android.view.hideKeyboard
import mozilla.components.support.ktx.kotlin.getOrigin
import mozilla.components.support.ktx.kotlinx.coroutines.flow.ifAnyChanged
import mozilla.components.support.locale.ActivityContextWrapper
import mozilla.components.ui.widgets.behavior.EngineViewClippingBehavior
import mozilla.components.ui.widgets.withCenterAlignedButtons
import org.mozilla.fenix.BuildConfig
import org.mozilla.fenix.FeatureFlags
Expand Down Expand Up @@ -141,11 +142,15 @@ import org.mozilla.fenix.components.toolbar.IncompleteRedesignToolbarFeature
import org.mozilla.fenix.components.toolbar.ToolbarIntegration
import org.mozilla.fenix.components.toolbar.ToolbarMenu
import org.mozilla.fenix.components.toolbar.ToolbarPosition
import org.mozilla.fenix.components.toolbar.getBottomToolbarHeight
import org.mozilla.fenix.components.toolbar.getTopToolbarHeight
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.BrowserNavBar
import org.mozilla.fenix.components.toolbar.navbar.EngineViewClippingBehavior
import org.mozilla.fenix.components.toolbar.navbar.NavbarIntegration
import org.mozilla.fenix.components.toolbar.navbar.ToolbarContainerView
import org.mozilla.fenix.compose.Divider
import org.mozilla.fenix.crashes.CrashContentIntegration
import org.mozilla.fenix.customtabs.ExternalAppBrowserActivity
Expand Down Expand Up @@ -181,7 +186,6 @@ import org.mozilla.fenix.utils.allowUndo
import org.mozilla.fenix.wifi.SitePermissionsWifiIntegration
import java.lang.ref.WeakReference
import kotlin.coroutines.cancellation.CancellationException
import mozilla.components.ui.widgets.behavior.ToolbarPosition as MozacToolbarPosition

/**
* Base fragment extended by [BrowserFragment].
Expand Down Expand Up @@ -363,8 +367,6 @@ abstract class BaseBrowserFragment :
val store = context.components.core.store
val activity = requireActivity() as HomeActivity

val toolbarHeight = resources.getDimensionPixelSize(R.dimen.browser_toolbar_height)

browserAnimator = BrowserAnimator(
fragment = WeakReference(this),
engineView = WeakReference(binding.engineView),
Expand Down Expand Up @@ -476,9 +478,12 @@ abstract class BaseBrowserFragment :
)
}

val shouldHideOnScroll =
!context.settings().shouldUseFixedTopToolbar && context.settings().isDynamicToolbarEnabled
_bottomToolbarContainerView = BottomToolbarContainerView(
context = context,
parent = binding.browserLayout,
hideOnScroll = shouldHideOnScroll,
composableContent = {
FirefoxTheme {
Column {
Expand Down Expand Up @@ -698,6 +703,8 @@ abstract class BaseBrowserFragment :
},
)

val bottomToolbarHeight = context.getBottomToolbarHeight()

downloadFeature.onDownloadStopped = { downloadState, _, downloadJobStatus ->
val onCannotOpenFile: (DownloadState) -> Unit = {
FenixSnackbar.make(
Expand Down Expand Up @@ -732,7 +739,7 @@ abstract class BaseBrowserFragment :
tryAgain = downloadFeature::tryAgain,
onCannotOpenFile = onCannotOpenFile,
binding = binding.viewDynamicDownloadDialog,
toolbarHeight = toolbarHeight,
bottomToolbarHeight = bottomToolbarHeight,
) {
sharedViewModel.downloadDialogState.remove(downloadState.sessionId)
}.show()
Expand All @@ -745,7 +752,7 @@ abstract class BaseBrowserFragment :
getCurrentTab()?.id,
store,
context,
toolbarHeight,
bottomToolbarHeight,
)

shareDownloadsFeature.set(
Expand Down Expand Up @@ -1078,7 +1085,11 @@ abstract class BaseBrowserFragment :
owner = this,
view = view,
)
initializeEngineView(toolbarHeight)

initializeEngineView(
topToolbarHeight = context.getTopToolbarHeight(),
bottomToolbarHeight = bottomToolbarHeight,
)
}

protected fun showUndoSnackbar(message: String) {
Expand Down Expand Up @@ -1214,7 +1225,7 @@ abstract class BaseBrowserFragment :
sessionId: String?,
store: BrowserStore,
context: Context,
toolbarHeight: Int,
bottomToolbarHeight: Int,
) {
val savedDownloadState =
sharedViewModel.downloadDialogState[sessionId]
Expand Down Expand Up @@ -1252,7 +1263,7 @@ abstract class BaseBrowserFragment :
.show()
},
binding = binding.viewDynamicDownloadDialog,
toolbarHeight = toolbarHeight,
bottomToolbarHeight = bottomToolbarHeight,
onDismiss = onDismiss,
).show()

Expand All @@ -1266,37 +1277,48 @@ abstract class BaseBrowserFragment :
!inFullScreen
}

/**
* Sets up necessary layout configurations for the engine view.
*
* If the toolbar is dynamic, sets a [CoordinatorLayout.Behavior] that will adjust the top/bottom paddings when the
* content of the page is being scrolled. When the top toolbar is translating up, we have to adjust the top margin
* of the [EngineView] parent so it would be displayed right below the top toolbar. It would also be responsible
* for adjusting [EngineView.setVerticalClipping] when the bottom toolbar is translating down – the content of the
* page like banners or webpage toolbars should be displayed right above the app's toolbar.
*
* If the toolbar is not dynamic, it simple sets the top and bottom margins so that content is always above/below
* respectable toolbars.
*
* @param topToolbarHeight The height of the top toolbar. Could be zero if toolbar position at the bottom or be
* equal to the height of [BrowserToolbar].
* @param bottomToolbarHeight The height of the bottom toolbar. Could be equal to the height of [BrowserToolbar] or
* [ToolbarContainerView], or be zero if the toolbar is positioned at top without a navigation bar.
*/
@VisibleForTesting
internal fun initializeEngineView(toolbarHeight: Int) {
internal fun initializeEngineView(
topToolbarHeight: Int,
bottomToolbarHeight: Int,
) {
val context = requireContext()

if (!context.settings().shouldUseFixedTopToolbar && context.settings().isDynamicToolbarEnabled) {
getEngineView().setDynamicToolbarMaxHeight(toolbarHeight)
getEngineView().setDynamicToolbarMaxHeight(topToolbarHeight + bottomToolbarHeight)

val toolbarPosition = when (context.settings().toolbarPosition) {
ToolbarPosition.BOTTOM -> MozacToolbarPosition.BOTTOM
ToolbarPosition.TOP -> MozacToolbarPosition.TOP
}
(getSwipeRefreshLayout().layoutParams as CoordinatorLayout.LayoutParams).behavior =
EngineViewClippingBehavior(
context,
null,
getSwipeRefreshLayout(),
toolbarHeight,
toolbarPosition,
context = context,
attrs = null,
engineViewParent = getSwipeRefreshLayout(),
topToolbarHeight = topToolbarHeight,
)
} else {
// Ensure webpage's bottom elements are aligned to the very bottom of the engineView.
getEngineView().setDynamicToolbarMaxHeight(0)

// 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().toolbarPosition == ToolbarPosition.TOP) {
swipeRefreshParams.topMargin = toolbarHeight
} else {
swipeRefreshParams.bottomMargin = toolbarHeight
}
// Effectively place the engineView on top/below of the toolbars if that is not dynamic.
val swipeRefreshParams = getSwipeRefreshLayout().layoutParams as CoordinatorLayout.LayoutParams
swipeRefreshParams.topMargin = topToolbarHeight
swipeRefreshParams.bottomMargin = bottomToolbarHeight
}
}

Expand Down Expand Up @@ -1374,9 +1396,9 @@ abstract class BaseBrowserFragment :
fullScreenChanged(false)
browserToolbarView.expand()

val toolbarHeight = resources.getDimensionPixelSize(R.dimen.browser_toolbar_height)
val context = requireContext()
resumeDownloadDialogState(selectedTab.id, context.components.core.store, context, toolbarHeight)
val bottomToolbarHeight = context.getBottomToolbarHeight()
resumeDownloadDialogState(selectedTab.id, context.components.core.store, context, bottomToolbarHeight)
it.announceForAccessibility(selectedTab.toDisplayTitle())
}
} else {
Expand Down Expand Up @@ -1699,8 +1721,10 @@ abstract class BaseBrowserFragment :
}
if (webAppToolbarShouldBeVisible) {
browserToolbarView.view.isVisible = true
val toolbarHeight = resources.getDimensionPixelSize(R.dimen.browser_toolbar_height)
initializeEngineView(toolbarHeight)
initializeEngineView(
topToolbarHeight = requireContext().getTopToolbarHeight(),
bottomToolbarHeight = requireContext().getBottomToolbarHeight(),
)
browserToolbarView.expand()
}
if (customTabSessionId == null && requireContext().settings().isTabletAndTabStripEnabled) {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,9 @@

package org.mozilla.fenix.components.toolbar

import android.content.Context
import org.mozilla.fenix.R
import org.mozilla.fenix.ext.settings
import org.mozilla.fenix.utils.Settings

/**
Expand Down Expand Up @@ -40,3 +43,37 @@ class IncompleteRedesignToolbarFeature(
override val isEnabled: Boolean
get() = settings.enableIncompleteToolbarRedesign
}

/**
* Returns the height of the bottom toolbar.
*
* The bottom toolbar can consist of a navigation bar,
* a combination of a navigation and address bar, or be absent.
*/
fun Context.getBottomToolbarHeight(): Int {
val isNavBarEnabled = IncompleteRedesignToolbarFeature(settings()).isEnabled
val isToolbarAtBottom = settings().shouldUseBottomToolbar
val toolbarHeight = resources.getDimensionPixelSize(R.dimen.browser_toolbar_height)
val navbarHeight = resources.getDimensionPixelSize(R.dimen.browser_navbar_height)

return when {
isNavBarEnabled && isToolbarAtBottom -> toolbarHeight + navbarHeight
isNavBarEnabled -> navbarHeight
isToolbarAtBottom -> toolbarHeight
else -> 0
}
}

/**
* Returns the height of the top toolbar.
*/
fun Context.getTopToolbarHeight(): Int {
val isToolbarAtTop = !settings().shouldUseBottomToolbar
val toolbarHeight = resources.getDimensionPixelSize(R.dimen.browser_toolbar_height)

return if (isToolbarAtTop) {
toolbarHeight
} else {
0
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -21,11 +21,13 @@ import mozilla.components.ui.widgets.behavior.ViewPosition
*
* @param context The Context the view is running in.
* @param parent The ViewGroup into which the NavigationBar composable will be added.
* @param hideOnScroll If the container should react to the [EngineView] content being scrolled.
* @param composableContent
*/
class BottomToolbarContainerView(
context: Context,
parent: ViewGroup,
hideOnScroll: Boolean = false,
composableContent: @Composable () -> Unit,
) {

Expand All @@ -46,7 +48,9 @@ class BottomToolbarContainerView(
CoordinatorLayout.LayoutParams.WRAP_CONTENT,
).apply {
gravity = Gravity.BOTTOM
behavior = EngineViewScrollingBehavior(parent.context, null, ViewPosition.BOTTOM)
if (hideOnScroll) {
behavior = EngineViewScrollingBehavior(parent.context, null, ViewPosition.BOTTOM)
}
}

parent.addView(toolbarContainerView)
Expand Down
Loading

0 comments on commit 73d5f32

Please sign in to comment.