Commit d30ebe0d authored by Mugurell's avatar Mugurell Committed by plingurar@mozilla.com
Browse files

Bug 2020810 - part 2 - New ToolbarVerticalGesturesHandler to open tabs tray...

Bug 2020810 - part 2 - New ToolbarVerticalGesturesHandler to open tabs tray when toolbar is swiped up/down r=android-reviewers,skhan

Differential Revision: https://phabricator.services.mozilla.com/D286667
parent 4ee1429f
Loading
Loading
Loading
Loading
+37 −20
Changes for mobile/android/fenix/app/src/main/java/org/mozilla/fenix/browser/BrowserFragment.kt: 37 added lines, 20 removed lines.
Original line number Diff line number Diff line
@@ -43,6 +43,7 @@ import mozilla.telemetry.glean.private.NoExtras
import org.mozilla.fenix.GleanMetrics.ReaderMode
import org.mozilla.fenix.R
import org.mozilla.fenix.browser.store.BrowserScreenAction.ReaderModeStatusUpdated
import org.mozilla.fenix.components.Components
import org.mozilla.fenix.components.LensFeature
import org.mozilla.fenix.components.QrScanFenixFeature
import org.mozilla.fenix.components.TabCollectionStorage
@@ -53,7 +54,8 @@ import org.mozilla.fenix.components.toolbar.BrowserToolbarComposable
import org.mozilla.fenix.components.toolbar.BrowserToolbarView
import org.mozilla.fenix.components.toolbar.FenixBrowserToolbarView
import org.mozilla.fenix.components.toolbar.ToolbarMenu
import org.mozilla.fenix.components.toolbar.gestures.ToolbarGestureHandler
import org.mozilla.fenix.components.toolbar.gestures.ToolbarHorizontalGesturesHandler
import org.mozilla.fenix.components.toolbar.gestures.ToolbarVerticalGesturesHandler
import org.mozilla.fenix.components.toolbar.ui.createShareBrowserAction
import org.mozilla.fenix.compose.snackbar.Snackbar
import org.mozilla.fenix.compose.snackbar.SnackbarState
@@ -80,6 +82,7 @@ import org.mozilla.fenix.telemetry.ACTION_SHARE_CLICKED
import org.mozilla.fenix.telemetry.SOURCE_ADDRESS_BAR
import org.mozilla.fenix.termsofuse.store.Surface
import org.mozilla.fenix.theme.ThemeManager
import org.mozilla.fenix.utils.Settings
import mozilla.components.ui.icons.R as iconsR
import org.mozilla.fenix.GleanMetrics.Toolbar as GleanMetricsToolbar

@@ -168,23 +171,9 @@ class BrowserFragment : BaseBrowserFragment(), UserInteractionHandler, SystemIns

        val context = requireContext()
        val components = context.components
        val settings = context.settings()

        if (!context.settings().isTabStripEnabled && context.settings().isSwipeToolbarToSwitchTabsEnabled) {
            binding.gestureLayout.addGestureListener(
                ToolbarGestureHandler(
                    activity = requireActivity(),
                    contentLayout = binding.browserLayout,
                    tabPreview = binding.tabPreview,
                    toolbarLayout = browserToolbarView.layout,
                    navBarLayout = browserNavigationBar?.layout,
                    store = components.core.store,
                    selectTabUseCase = components.useCases.tabsUseCases.selectTab,
                    onSwipeStarted = {
                        thumbnailsFeature.get()?.requestScreenshot()
                    },
                ),
            )
        }
        setupToolbarSwipeBehavior(settings, components)

        if (browserToolbarView is BrowserToolbarView) {
            updateBrowserToolbarLeadingAndNavigationActions(
@@ -214,17 +203,17 @@ class BrowserFragment : BaseBrowserFragment(), UserInteractionHandler, SystemIns
            view = view,
        )

        if (context.settings().shouldShowOpenInAppCfr) {
        if (settings.shouldShowOpenInAppCfr) {
            openInAppOnboardingObserver.set(
                feature = OpenInAppOnboardingObserver(
                    context = context,
                    store = context.components.core.store,
                    lifecycleOwner = this,
                    navController = findNavController(),
                    settings = context.settings(),
                    settings = settings,
                    appLinksUseCases = context.components.useCases.appLinksUseCases,
                    container = binding.browserLayout as ViewGroup,
                    shouldScrollWithTopToolbar = !context.settings().shouldUseBottomToolbar,
                    shouldScrollWithTopToolbar = !settings.shouldUseBottomToolbar,
                ),
                owner = this,
                view = view,
@@ -239,6 +228,34 @@ class BrowserFragment : BaseBrowserFragment(), UserInteractionHandler, SystemIns
        )
    }

    private fun setupToolbarSwipeBehavior(settings: Settings, components: Components) {
        if (!settings.isTabStripEnabled && settings.isSwipeToolbarToSwitchTabsEnabled) {
            binding.gestureLayout.addGestureListener(
                ToolbarHorizontalGesturesHandler(
                    activity = requireActivity(),
                    contentLayout = binding.browserLayout,
                    tabPreview = binding.tabPreview,
                    toolbarLayout = browserToolbarView.layout,
                    navBarLayout = browserNavigationBar?.layout,
                    store = components.core.store,
                    selectTabUseCase = components.useCases.tabsUseCases.selectTab,
                    onSwipeStarted = {
                        thumbnailsFeature.get()?.requestScreenshot()
                    },
                ),
            )
        }
        binding.gestureLayout.addGestureListener(
            ToolbarVerticalGesturesHandler(
                appStore = components.appStore,
                toolbarLayout = browserToolbarView.layout,
                navBarLayout = browserNavigationBar?.layout,
                toolbarPosition = settings.toolbarPosition,
                navController = findNavController(),
            ),
        )
    }

    private fun setupShakeDetection() {
        val shouldSetupShake = requireComponents.core.summarizeFeatureSettings.canShowFeature &&
                requireComponents.core.summarizationSettings.isGestureEnabled.value
+1 −1
Changes for mobile/android/fenix/app/src/main/java/org/mozilla/fenix/components/toolbar/gestures/ToolbarHorizontalGesturesHandler.kt: 1 added line, 1 removed line.
Original line number Diff line number Diff line
@@ -46,7 +46,7 @@ import kotlin.math.min
 * necessary animations.
 */
@Suppress("LargeClass", "TooManyFunctions", "LongParameterList")
class ToolbarGestureHandler(
class ToolbarHorizontalGesturesHandler(
    private val activity: Activity,
    private val contentLayout: View,
    private val tabPreview: TabPreview,
+143 −0
Changes for mobile/android/fenix/app/src/main/java/org/mozilla/fenix/components/toolbar/gestures/ToolbarVerticalGesturesHandler.kt: 143 added lines, 0 removed lines.
Original line number Diff line number Diff line
/* 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.gestures

import android.graphics.PointF
import android.view.View
import android.view.ViewConfiguration
import androidx.core.graphics.contains
import androidx.core.graphics.toPoint
import androidx.navigation.NavController
import mozilla.telemetry.glean.private.NoExtras
import org.mozilla.fenix.GleanMetrics.Events
import org.mozilla.fenix.NavGraphDirections
import org.mozilla.fenix.R
import org.mozilla.fenix.browser.SwipeGestureListener
import org.mozilla.fenix.browser.browsingmode.BrowsingMode.Normal
import org.mozilla.fenix.browser.browsingmode.BrowsingMode.Private
import org.mozilla.fenix.components.AppStore
import org.mozilla.fenix.components.toolbar.ToolbarPosition
import org.mozilla.fenix.components.toolbar.ToolbarPosition.BOTTOM
import org.mozilla.fenix.components.toolbar.ToolbarPosition.TOP
import org.mozilla.fenix.ext.getRectWithScreenLocation
import org.mozilla.fenix.ext.nav
import org.mozilla.fenix.tabstray.redux.state.Page
import kotlin.math.abs

private const val TOOLBAR_HEIGHT_MAXIMUM_SWIPE_FACTOR = 0.8f

/**
 * Toolbars (address bar + navigation bar) specific gesture handler that will
 * show the tabs tray for the appropriate swip up/down gesture.
 *
 * @param appStore The [AppStore] containing the application state.
 * @param toolbarLayout The address bar layout.
 * @param navBarLayout The navigation bar layout.
 * @param toolbarPosition Where the address bar is shown on the screen.
 * @param navController [NavController] used for navigation to the tabs tray.
 */
class ToolbarVerticalGesturesHandler(
    private val appStore: AppStore,
    private val toolbarLayout: View,
    private val navBarLayout: View?,
    private val toolbarPosition: ToolbarPosition,
    private val navController: NavController,
) : SwipeGestureListener {
    private val scaledTouchSlop = ViewConfiguration.get(toolbarLayout.context).scaledTouchSlop * 2
    private var currentSwipeXDistance = 0f
    private var currentSwipeYDistance = 0f
    private var startTouchPoint = PointF(0f, 0f)

    override fun onSwipeStarted(
        start: PointF,
        next: PointF,
    ): Boolean {
        startTouchPoint = start
        currentSwipeXDistance = next.x - start.x
        currentSwipeYDistance = next.y - start.y

        return maybeShowTabsOnSwipe()
    }

    override fun onSwipeUpdate(distanceX: Float, distanceY: Float) {
        currentSwipeXDistance -= distanceX
        currentSwipeYDistance -= distanceY

        maybeShowTabsOnSwipe()
    }

    override fun onSwipeFinished(velocityX: Float, velocityY: Float) {
        // no-op
    }

    private fun maybeShowTabsOnSwipe(): Boolean {
        val currentDestinationId = navController.currentDestination?.id
        // Avoid negative side effects of the race between navigation and swipe callbacks
        val isCurrentDestinationValid =
            currentDestinationId == R.id.browserFragment || currentDestinationId == R.id.homeFragment

        if (!isCurrentDestinationValid || !startTouchPoint.isSwipeValid(currentSwipeYDistance)) {
            return false
        }

        if (isSwipeValid()) {
            navController.nav(
                navController.currentDestination?.id,
                NavGraphDirections.actionGlobalTabManagementFragment(
                    page = when (appStore.state.mode) {
                        Normal -> Page.NormalTabs
                        Private -> Page.PrivateTabs
                    },
                ),
            )
            return false
        } else {
            return true
        }
    }

    /**
     * Check if a vertical swipe with the minimum accepted distance happened.
     */
    private fun isSwipeValid(): Boolean {
        val target = getTargetView() ?: return false

        // Ensure that the minimum swipe distance is still smaller than the toolbar height.
        val maximumToolbarSwipeNeeded = target.height / TOOLBAR_HEIGHT_MAXIMUM_SWIPE_FACTOR
        val minimumSwipeDistance = scaledTouchSlop.coerceAtMost(maximumToolbarSwipeNeeded.toInt())

        return abs(currentSwipeYDistance) >= minimumSwipeDistance &&
            abs(currentSwipeXDistance) < minimumSwipeDistance &&
            startTouchPoint.isSwipeValid(currentSwipeYDistance)
    }

    /**
     * Check if the swipe originated from the toolbar or navigation bar.
     */
    private fun PointF.isSwipeValid(distanceY: Float): Boolean {
        val isSwipeUpOverNavbar = distanceY.isSwipeUp && isInTarget(navBarLayout)
        if (isSwipeUpOverNavbar) return true

        val isToolbarSwipeDirectionValid = when (toolbarPosition) {
            TOP -> distanceY.isSwipeDown
            BOTTOM -> distanceY.isSwipeUp
        }
        return isToolbarSwipeDirectionValid && isInTarget(toolbarLayout)
    }

    private fun getTargetView() = when ((navBarLayout?.height ?: 0) > 0) {
        true -> navBarLayout
        else -> toolbarLayout
    }

    private fun PointF.isInTarget(target: View?) =
        target?.getRectWithScreenLocation()?.contains(toPoint()) == true

    private val Float.isSwipeUp
        get() = this < 0f

    private val Float.isSwipeDown
        get() = this > 0f
}
+594 −0

File added.

Preview size limit exceeded, changes collapsed.

+1 −1
Changes for mobile/android/fenix/config/detekt-baseline.xml: 1 added line, 1 removed line.
Original line number Diff line number Diff line
@@ -139,7 +139,7 @@
    <ID>ForbiddenSuppress:TabsTrayBanner.kt$@Suppress("LongParameterList", "LongMethod")</ID>
    <ID>ForbiddenSuppress:TelemetryMiddleware.kt$TelemetryMiddleware$@Suppress("TooGenericExceptionCaught", "CognitiveComplexMethod", "NestedBlockDepth", "LongMethod", "CyclomaticComplexMethod")</ID>
    <ID>ForbiddenSuppress:ThemeOnboardingPage.kt$@Suppress("LongMethod")</ID>
    <ID>ForbiddenSuppress:ToolbarGestureHandler.kt$ToolbarGestureHandler$@Suppress("CognitiveComplexMethod")</ID>
    <ID>ForbiddenSuppress:ToolbarHorizontalGesturesHandler.kt$ToolbarHorizontalGesturesHandler$@Suppress("CognitiveComplexMethod")</ID>
    <ID>ForbiddenSuppress:TopSites.kt$@Suppress( "LongMethod", "LongParameterList", "Deprecation", )</ID>
    <ID>ForbiddenSuppress:TrackingProtectionPanelView.kt$TrackingProtectionPanelView$@Suppress("CognitiveComplexMethod")</ID>
    <ID>ForbiddenSuppress:TranslationSettings.kt$@Suppress("LongMethod", "CognitiveComplexMethod")</ID>