From 79518f880bd04258706ca562ce268e451a1063b7 Mon Sep 17 00:00:00 2001 From: Jeff Boek Date: Wed, 10 Apr 2019 16:35:30 -0700 Subject: [PATCH] [fenix] for https://github.com/mozilla-mobile/fenix/issues/1430 - Copy BottomToolbarBehavior to customize snackbar position --- .../mozilla/fenix/browser/BrowserFragment.kt | 1 - .../browser/BrowserToolbarBottomBehavior.kt | 123 ++++++++++++++++++ 2 files changed, 123 insertions(+), 1 deletion(-) create mode 100644 app/src/main/java/org/mozilla/fenix/browser/BrowserToolbarBottomBehavior.kt 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 accba6ae7..6e59a893d 100644 --- a/app/src/main/java/org/mozilla/fenix/browser/BrowserFragment.kt +++ b/app/src/main/java/org/mozilla/fenix/browser/BrowserFragment.kt @@ -32,7 +32,6 @@ import kotlinx.coroutines.Job import kotlinx.coroutines.launch import mozilla.appservices.places.BookmarkRoot import mozilla.components.browser.session.Session -import mozilla.components.browser.toolbar.behavior.BrowserToolbarBottomBehavior import mozilla.components.feature.contextmenu.ContextMenuCandidate import mozilla.components.feature.contextmenu.ContextMenuFeature import mozilla.components.feature.downloads.DownloadsFeature diff --git a/app/src/main/java/org/mozilla/fenix/browser/BrowserToolbarBottomBehavior.kt b/app/src/main/java/org/mozilla/fenix/browser/BrowserToolbarBottomBehavior.kt new file mode 100644 index 000000000..a76e3d56c --- /dev/null +++ b/app/src/main/java/org/mozilla/fenix/browser/BrowserToolbarBottomBehavior.kt @@ -0,0 +1,123 @@ +/* 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 + } + + 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 + } + } + + @Suppress("MagicNumber") + override fun onStopNestedScroll( + coordinatorLayout: CoordinatorLayout, + child: BrowserToolbar, + target: View, + type: Int + ) { + if (shouldSnapAfterScroll || type == TYPE_NON_TOUCH) { + if (child.translationY >= (child.height * 0.5f)) { + 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(child, 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() + } + + @Suppress("MagicNumber") + private fun positionSnackbar(child: View, 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 + } +} + +private enum class SnapDirection { + UP, + DOWN +}