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 65f0a7afe..1fd018008 100644 --- a/app/src/main/java/org/mozilla/fenix/browser/BaseBrowserFragment.kt +++ b/app/src/main/java/org/mozilla/fenix/browser/BaseBrowserFragment.kt @@ -21,6 +21,7 @@ import androidx.fragment.app.Fragment import androidx.lifecycle.lifecycleScope import androidx.navigation.NavDirections import androidx.navigation.fragment.findNavController +import com.google.android.material.appbar.AppBarLayout import com.google.android.material.snackbar.Snackbar import kotlinx.android.synthetic.main.fragment_browser.* import kotlinx.android.synthetic.main.fragment_browser.view.* @@ -50,6 +51,7 @@ import mozilla.components.feature.session.FullScreenFeature import mozilla.components.feature.session.SessionFeature import mozilla.components.feature.session.SessionUseCases 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.feature.sitepermissions.SitePermissionsRules @@ -152,6 +154,10 @@ abstract class BaseBrowserFragment : Fragment(), UserInteractionHandler, Session val sessionManager = context.components.core.sessionManager val store = context.components.core.store + val toolbarHeight = resources.getDimensionPixelSize(R.dimen.browser_toolbar_height) + + initializeEngineView(toolbarHeight) + return getSessionById()?.also { session -> val browserToolbarController = DefaultBrowserToolbarController( store = browserFragmentStore, @@ -382,14 +388,18 @@ abstract class BaseBrowserFragment : Fragment(), UserInteractionHandler, Session .show() activity?.enterToImmersiveMode() browserToolbarView.view.visibility = View.GONE + + // TODO We need to call force expand here to update verticalClipping #8697 + // Without this, fullscreen has a margin at the top. + engineView.setVerticalClipping(0) } else { activity?.exitImmersiveModeIfNeeded() (activity as? HomeActivity)?.let { activity -> activity.themeManager.applyStatusBarTheme(activity) } browserToolbarView.view.visibility = View.VISIBLE + engineView.setDynamicToolbarMaxHeight(toolbarHeight) } - updateLayoutMargins(inFullScreen) }, owner = this, view = view @@ -450,6 +460,18 @@ abstract class BaseBrowserFragment : Fragment(), UserInteractionHandler, Session } } + private fun initializeEngineView(toolbarHeight: Int) { + engineView.setDynamicToolbarMaxHeight(toolbarHeight) + + val behavior = if (requireContext().settings().shouldUseBottomToolbar) { + EngineViewBottomBehavior(context, null) + } else { + AppBarLayout.ScrollingViewBehavior(context, null) + } + + (swipeRefresh.layoutParams as CoordinatorLayout.LayoutParams).behavior = behavior + } + /** * Returns a list of context menu items [ContextMenuCandidate] for the context menu */ @@ -470,7 +492,7 @@ abstract class BaseBrowserFragment : Fragment(), UserInteractionHandler, Session // If the bitmap is null, the best we can do to reduce the flash is set transparent swipeRefresh.background = bitmap?.toDrawable(it.resources) ?: ColorDrawable(Color.TRANSPARENT) - + engineView.asView().visibility = View.GONE findNavController().nav(R.id.browserFragment, directions) } @@ -607,35 +629,12 @@ abstract class BaseBrowserFragment : Fragment(), UserInteractionHandler, Session protected abstract fun navToTrackingProtectionPanel(session: Session) - /** - * Returns the top and bottom margins. - */ - private fun getEngineMargins(): Pair = - if (context?.settings()?.shouldUseBottomToolbar == true) { - val toolbarSize = resources.getDimensionPixelSize(R.dimen.browser_toolbar_height) - 0 to toolbarSize - } else { - 0 to 0 - } - /** * Returns the layout [android.view.Gravity] for the quick settings and ETP dialog. */ protected fun getAppropriateLayoutGravity(): Int = if (context?.settings()?.shouldUseBottomToolbar == true) Gravity.BOTTOM else Gravity.TOP - protected fun updateLayoutMargins(inFullScreen: Boolean) { - view?.swipeRefresh?.apply { - val (topMargin, bottomMargin) = if (inFullScreen) 0 to 0 else getEngineMargins() - (layoutParams as CoordinatorLayout.LayoutParams).setMargins( - 0, - topMargin, - 0, - bottomMargin - ) - } - } - /** * Updates the site permissions rules based on user settings. */ diff --git a/app/src/main/java/org/mozilla/fenix/browser/BrowserFragment.kt b/app/src/main/java/org/mozilla/fenix/browser/BrowserFragment.kt index 0b4def492..782719c3a 100644 --- a/app/src/main/java/org/mozilla/fenix/browser/BrowserFragment.kt +++ b/app/src/main/java/org/mozilla/fenix/browser/BrowserFragment.kt @@ -9,10 +9,8 @@ import android.os.Bundle import android.view.LayoutInflater import android.view.View import android.view.ViewGroup -import androidx.coordinatorlayout.widget.CoordinatorLayout import androidx.transition.TransitionInflater import com.google.android.material.snackbar.Snackbar -import kotlinx.android.synthetic.main.fragment_browser.* import kotlinx.android.synthetic.main.fragment_browser.view.* import kotlinx.coroutines.ExperimentalCoroutinesApi import mozilla.components.browser.session.Session @@ -128,14 +126,6 @@ class BrowserFragment : BaseBrowserFragment(), UserInteractionHandler { } private fun updateEngineBottomMargin() { - val browserEngine = swipeRefresh.layoutParams as CoordinatorLayout.LayoutParams - - browserEngine.bottomMargin = if (requireContext().settings().shouldUseBottomToolbar) { - requireContext().resources.getDimensionPixelSize(R.dimen.browser_toolbar_height) - } else { - 0 - } - val toolbarSessionObserver = TrackingProtectionOverlay( context = requireContext(), settings = requireContext().settings() diff --git a/app/src/main/java/org/mozilla/fenix/browser/BrowserToolbarBottomBehavior.kt b/app/src/main/java/org/mozilla/fenix/browser/BrowserToolbarBottomBehavior.kt deleted file mode 100644 index 4f0375f8c..000000000 --- a/app/src/main/java/org/mozilla/fenix/browser/BrowserToolbarBottomBehavior.kt +++ /dev/null @@ -1,125 +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.browser - -import android.animation.ValueAnimator -import android.content.Context -import android.util.AttributeSet -import android.view.Gravity -import android.view.View -import android.view.View.SCROLL_AXIS_VERTICAL -import android.view.animation.DecelerateInterpolator -import androidx.coordinatorlayout.widget.CoordinatorLayout -import androidx.core.view.ViewCompat.TYPE_NON_TOUCH -import androidx.core.view.ViewCompat.TYPE_TOUCH -import com.google.android.material.snackbar.Snackbar -import mozilla.components.browser.toolbar.BrowserToolbar -import org.mozilla.fenix.R -import kotlin.math.max -import kotlin.math.min - -private const val SNAP_ANIMATION_DURATION = 150L - -/** - * A [CoordinatorLayout.Behavior] implementation to be used when placing [BrowserToolbar] at the bottom of the screen. - * - * This implementation will: - * - Show/Hide the [BrowserToolbar] automatically when scrolling vertically. - * - On showing a [Snackbar] position it above the [BrowserToolbar]. - * - Snap the [BrowserToolbar] to be hidden or visible when the user stops scrolling. - */ -class BrowserToolbarBottomBehavior( - context: Context?, - attrs: AttributeSet? -) : CoordinatorLayout.Behavior(context, attrs) { - // This implementation is heavily based on this blog article: - // https://android.jlelse.eu/scroll-your-bottom-navigation-view-away-with-10-lines-of-code-346f1ed40e9e - - internal var shouldSnapAfterScroll: Boolean = false - - internal var snapAnimator: ValueAnimator = ValueAnimator().apply { - interpolator = DecelerateInterpolator() - duration = SNAP_ANIMATION_DURATION - } - - fun forceExpand(view: View) { - animateSnap(view, SnapDirection.UP) - } - - override fun onStartNestedScroll( - coordinatorLayout: CoordinatorLayout, - child: BrowserToolbar, - directTargetChild: View, - target: View, - axes: Int, - type: Int - ): Boolean { - return if (axes == SCROLL_AXIS_VERTICAL) { - shouldSnapAfterScroll = type == TYPE_TOUCH - snapAnimator.cancel() - true - } else { - false - } - } - - override fun onStopNestedScroll( - coordinatorLayout: CoordinatorLayout, - child: BrowserToolbar, - target: View, - type: Int - ) { - if (shouldSnapAfterScroll || type == TYPE_NON_TOUCH) { - if (child.translationY >= (child.height / 2f)) { - animateSnap(child, SnapDirection.DOWN) - } else { - animateSnap(child, SnapDirection.UP) - } - } - } - - override fun onNestedPreScroll( - coordinatorLayout: CoordinatorLayout, - child: BrowserToolbar, - target: View, - dx: Int, - dy: Int, - consumed: IntArray, - type: Int - ) { - super.onNestedPreScroll(coordinatorLayout, child, target, dx, dy, consumed, type) - child.translationY = max(0f, min(child.height.toFloat(), child.translationY + dy)) - } - - override fun layoutDependsOn(parent: CoordinatorLayout, child: BrowserToolbar, dependency: View): Boolean { - if (dependency is Snackbar.SnackbarLayout) { - positionSnackbar(dependency) - } - - return super.layoutDependsOn(parent, child, dependency) - } - - private fun animateSnap(child: View, direction: SnapDirection) = with(snapAnimator) { - addUpdateListener { child.translationY = it.animatedValue as Float } - setFloatValues(child.translationY, if (direction == SnapDirection.UP) 0f else child.height.toFloat()) - start() - } - - private fun positionSnackbar(view: View) { - val params = view.layoutParams as CoordinatorLayout.LayoutParams - - // Position the snackbar above the toolbar so that it doesn't overlay the toolbar. - params.anchorId = R.id.quick_action_sheet - params.anchorGravity = Gravity.TOP or Gravity.CENTER_HORIZONTAL - params.gravity = Gravity.TOP or Gravity.CENTER_HORIZONTAL - - view.layoutParams = params - } -} - -enum class SnapDirection { - UP, - DOWN -} diff --git a/app/src/main/java/org/mozilla/fenix/browser/BrowserToolbarTopBehavior.kt b/app/src/main/java/org/mozilla/fenix/browser/BrowserToolbarTopBehavior.kt deleted file mode 100644 index c42d33979..000000000 --- a/app/src/main/java/org/mozilla/fenix/browser/BrowserToolbarTopBehavior.kt +++ /dev/null @@ -1,120 +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.browser - -import android.animation.ValueAnimator -import android.content.Context -import android.util.AttributeSet -import android.view.Gravity -import android.view.View -import android.view.View.SCROLL_AXIS_VERTICAL -import android.view.animation.DecelerateInterpolator -import androidx.coordinatorlayout.widget.CoordinatorLayout -import androidx.core.view.ViewCompat.TYPE_NON_TOUCH -import androidx.core.view.ViewCompat.TYPE_TOUCH -import com.google.android.material.snackbar.Snackbar -import mozilla.components.browser.toolbar.BrowserToolbar -import org.mozilla.fenix.R -import kotlin.math.max -import kotlin.math.min - -private const val SNAP_ANIMATION_DURATION = 150L - -/** - * A [CoordinatorLayout.Behavior] implementation to be used when placing [BrowserToolbar] at the top of the screen. - * - * This implementation will: - * - Show/Hide the [BrowserToolbar] automatically when scrolling vertically. - * - On showing a [Snackbar] position it above the [BrowserToolbar]. - * - Snap the [BrowserToolbar] to be hidden or visible when the user stops scrolling. - */ -class BrowserToolbarTopBehavior( - context: Context?, - attrs: AttributeSet? -) : CoordinatorLayout.Behavior(context, attrs) { - // This implementation is heavily based on this blog article: - // https://android.jlelse.eu/scroll-your-bottom-navigation-view-away-with-10-lines-of-code-346f1ed40e9e - - internal var shouldSnapAfterScroll: Boolean = false - - internal var snapAnimator: ValueAnimator = ValueAnimator().apply { - interpolator = DecelerateInterpolator() - duration = SNAP_ANIMATION_DURATION - } - - fun forceExpand(view: View) { - animateSnap(view, SnapDirection.DOWN) - } - - override fun onStartNestedScroll( - coordinatorLayout: CoordinatorLayout, - child: BrowserToolbar, - directTargetChild: View, - target: View, - axes: Int, - type: Int - ): Boolean { - return if (axes == SCROLL_AXIS_VERTICAL) { - shouldSnapAfterScroll = type == TYPE_TOUCH - snapAnimator.cancel() - true - } else { - false - } - } - - override fun onStopNestedScroll( - coordinatorLayout: CoordinatorLayout, - child: BrowserToolbar, - target: View, - type: Int - ) { - if (shouldSnapAfterScroll || type == TYPE_NON_TOUCH) { - if (child.translationY >= (-child.height / 2f)) { - animateSnap(child, SnapDirection.DOWN) - } else { - animateSnap(child, SnapDirection.UP) - } - } - } - - override fun onNestedPreScroll( - coordinatorLayout: CoordinatorLayout, - child: BrowserToolbar, - target: View, - dx: Int, - dy: Int, - consumed: IntArray, - type: Int - ) { - super.onNestedPreScroll(coordinatorLayout, child, target, dx, dy, consumed, type) - child.translationY = max(-child.height.toFloat(), min(0f, child.translationY - dy)) - } - - override fun layoutDependsOn(parent: CoordinatorLayout, child: BrowserToolbar, dependency: View): Boolean { - if (dependency is Snackbar.SnackbarLayout) { - positionSnackbar(dependency) - } - - return super.layoutDependsOn(parent, child, dependency) - } - - private fun animateSnap(child: View, direction: SnapDirection) = with(snapAnimator) { - addUpdateListener { child.translationY = it.animatedValue as Float } - setFloatValues(child.translationY, if (direction == SnapDirection.DOWN) 0f else -child.height.toFloat()) - start() - } - - private fun positionSnackbar(view: View) { - val params = view.layoutParams as CoordinatorLayout.LayoutParams - - // Position the snackbar below the toolbar so that it doesn't overlay the toolbar. - params.anchorId = R.id.toolbar - params.anchorGravity = Gravity.BOTTOM or Gravity.CENTER_HORIZONTAL - params.gravity = Gravity.BOTTOM or Gravity.CENTER_HORIZONTAL - - view.layoutParams = params - } -} diff --git a/app/src/main/java/org/mozilla/fenix/customtabs/CustomTabsIntegration.kt b/app/src/main/java/org/mozilla/fenix/customtabs/CustomTabsIntegration.kt index 396290f41..c561cbba2 100644 --- a/app/src/main/java/org/mozilla/fenix/customtabs/CustomTabsIntegration.kt +++ b/app/src/main/java/org/mozilla/fenix/customtabs/CustomTabsIntegration.kt @@ -5,8 +5,6 @@ package org.mozilla.fenix.customtabs import android.app.Activity -import android.view.View -import android.view.ViewGroup.MarginLayoutParams import androidx.appcompat.content.res.AppCompatResources import mozilla.components.browser.session.SessionManager import mozilla.components.browser.toolbar.BrowserToolbar @@ -23,7 +21,6 @@ class CustomTabsIntegration( toolbar: BrowserToolbar, sessionId: String, activity: Activity, - engineLayout: View, onItemTapped: (ToolbarMenu.Item) -> Unit = {}, shouldReverseItems: Boolean, isPrivate: Boolean @@ -33,14 +30,6 @@ class CustomTabsIntegration( // Remove toolbar shadow toolbar.elevation = 0f - // Reduce margin height of EngineView from the top for the toolbar - engineLayout.run { - (layoutParams as MarginLayoutParams).apply { - val toolbarHeight = resources.getDimension(R.dimen.browser_toolbar_height).toInt() - setMargins(0, toolbarHeight, 0, 0) - } - } - val uncoloredEtpShield = AppCompatResources.getDrawable( activity, R.drawable.ic_tracking_protection_enabled diff --git a/app/src/main/java/org/mozilla/fenix/customtabs/ExternalAppBrowserFragment.kt b/app/src/main/java/org/mozilla/fenix/customtabs/ExternalAppBrowserFragment.kt index 48308f763..33d05b322 100644 --- a/app/src/main/java/org/mozilla/fenix/customtabs/ExternalAppBrowserFragment.kt +++ b/app/src/main/java/org/mozilla/fenix/customtabs/ExternalAppBrowserFragment.kt @@ -8,7 +8,6 @@ import android.content.Context import android.view.View import androidx.navigation.fragment.navArgs import kotlinx.android.synthetic.main.component_browser_top_toolbar.* -import kotlinx.android.synthetic.main.fragment_browser.view.* import kotlinx.coroutines.ExperimentalCoroutinesApi import mozilla.components.browser.session.Session import mozilla.components.concept.engine.manifest.WebAppManifestParser @@ -67,7 +66,6 @@ class ExternalAppBrowserFragment : BaseBrowserFragment(), UserInteractionHandler toolbar = toolbar, sessionId = customTabSessionId, activity = activity, - engineLayout = view.swipeRefresh, onItemTapped = { browserInteractor.onBrowserToolbarMenuItemTapped(it) }, isPrivate = (activity as HomeActivity).browsingModeManager.mode.isPrivate, shouldReverseItems = !activity.settings().shouldUseBottomToolbar @@ -92,8 +90,7 @@ class ExternalAppBrowserFragment : BaseBrowserFragment(), UserInteractionHandler toolbar, customTabSessionId, trustedScopes - ) { toolbarVisible -> - updateLayoutMargins(inFullScreen = !toolbarVisible) + ) { }, owner = this, view = toolbar diff --git a/app/src/main/java/org/mozilla/fenix/search/toolbar/ToolbarView.kt b/app/src/main/java/org/mozilla/fenix/search/toolbar/ToolbarView.kt index b1ef25f92..8b9de9046 100644 --- a/app/src/main/java/org/mozilla/fenix/search/toolbar/ToolbarView.kt +++ b/app/src/main/java/org/mozilla/fenix/search/toolbar/ToolbarView.kt @@ -21,6 +21,7 @@ import com.google.android.material.appbar.AppBarLayout.LayoutParams.SCROLL_FLAG_ import kotlinx.android.extensions.LayoutContainer import mozilla.components.browser.domains.autocomplete.ShippedDomainsProvider import mozilla.components.browser.toolbar.BrowserToolbar +import mozilla.components.browser.toolbar.behavior.BrowserToolbarBottomBehavior import mozilla.components.concept.storage.HistoryStorage import mozilla.components.feature.toolbar.ToolbarAutocompleteFeature import mozilla.components.support.ktx.android.util.dpToPx @@ -180,6 +181,11 @@ class ToolbarView( fun BrowserToolbar.setScrollFlagsForTopToolbar() { // Don't set scroll flags for bottom toolbar if (context.settings().shouldUseBottomToolbar) { + if (layoutParams is CoordinatorLayout.LayoutParams) { + (layoutParams as CoordinatorLayout.LayoutParams).apply { + behavior = BrowserToolbarBottomBehavior(context, null) + } + } return } diff --git a/app/src/main/res/layout/fragment_browser.xml b/app/src/main/res/layout/fragment_browser.xml index 0f6f70d49..81238b6b8 100644 --- a/app/src/main/res/layout/fragment_browser.xml +++ b/app/src/main/res/layout/fragment_browser.xml @@ -14,7 +14,7 @@ android:id="@+id/swipeRefresh" android:layout_width="match_parent" android:layout_height="match_parent" - app:layout_behavior="@string/appbar_scrolling_view_behavior"> + app:layout_behavior="mozilla.components.feature.session.behavior.EngineViewBottomBehavior">