diff --git a/app/src/main/java/com/duckduckgo/app/browser/BrowserTabFragment.kt b/app/src/main/java/com/duckduckgo/app/browser/BrowserTabFragment.kt index 6044f148dc2d..c5c806fffb57 100644 --- a/app/src/main/java/com/duckduckgo/app/browser/BrowserTabFragment.kt +++ b/app/src/main/java/com/duckduckgo/app/browser/BrowserTabFragment.kt @@ -834,8 +834,17 @@ class BrowserTabFragment : Fragment(), FindListener, CoroutineScope, DaxDialogLi } it.setEnableSwipeRefreshCallback { enable -> - swipeRefreshContainer?.isEnabled = enable + try { + activity?.runOnUiThread { + swipeRefreshContainer?.isEnabled = enable + } + } catch (e: Exception) { + Timber.e("ERROR: ${Thread.currentThread().name} $e") + } } + it.addJavascriptInterface(BrowserWebViewClient.WebAppInterface { enable -> + it.setContentAllowsSwipeToRefresh(enable) + }, "ddg_swipe_handler"); registerForContextMenu(it) diff --git a/app/src/main/java/com/duckduckgo/app/browser/BrowserWebViewClient.kt b/app/src/main/java/com/duckduckgo/app/browser/BrowserWebViewClient.kt index d3197171463d..d8df3e20361d 100644 --- a/app/src/main/java/com/duckduckgo/app/browser/BrowserWebViewClient.kt +++ b/app/src/main/java/com/duckduckgo/app/browser/BrowserWebViewClient.kt @@ -32,6 +32,7 @@ import kotlinx.coroutines.* import timber.log.Timber import java.net.URI + class BrowserWebViewClient( private val requestRewriter: RequestRewriter, private val specialUrlDetector: SpecialUrlDetector, @@ -137,6 +138,7 @@ class BrowserWebViewClient( val navigationList = webView.safeCopyBackForwardList() ?: return webViewClientListener?.navigationStateChanged(WebViewNavigationState(navigationList)) flushCookies() + detectOverscrollBehavior(webView) } catch (e: Throwable) { GlobalScope.launch { uncaughtExceptionRepository.recordUncaughtException(e, ON_PAGE_FINISHED) @@ -145,6 +147,16 @@ class BrowserWebViewClient( } } + private fun detectOverscrollBehavior(webView: WebView) { + webView.loadUrl( + """ + javascript:(function() { + ddg_swipe_handler.registerOverscrollBehavior(getComputedStyle(document.querySelector('body')).overscrollBehaviorY); + })(); + """ + ) + } + private fun flushCookies() { GlobalScope.launch(Dispatchers.IO) { cookieManager.flush() @@ -242,4 +254,11 @@ class BrowserWebViewClient( it.requiresAuthentication(request) } } + + class WebAppInterface internal constructor(private var enableSwipeRefresh: (Boolean) -> Unit) { + @JavascriptInterface + fun registerOverscrollBehavior(behavior: String) { + enableSwipeRefresh(behavior == "auto") + } + } } diff --git a/app/src/main/java/com/duckduckgo/app/browser/DuckDuckGoWebView.kt b/app/src/main/java/com/duckduckgo/app/browser/DuckDuckGoWebView.kt index ccc6bda1e092..0fb0cf107c7b 100644 --- a/app/src/main/java/com/duckduckgo/app/browser/DuckDuckGoWebView.kt +++ b/app/src/main/java/com/duckduckgo/app/browser/DuckDuckGoWebView.kt @@ -35,10 +35,12 @@ import androidx.core.view.ViewCompat * Originally based on https://github.com/takahirom/webview-in-coordinatorlayout for scrolling behaviour */ class DuckDuckGoWebView : WebView, NestedScrollingChild { - private var lastClampedY: Boolean = false + private var lastClampedTopY: Boolean = false + private var contentAllowsSwipeToRefresh: Boolean = true private var enableSwipeRefreshCallback: ((Boolean) -> Unit)? = null private var lastY: Int = 0 + private var lastDeltaY: Int = 0 private val scrollOffset = IntArray(2) private val scrollConsumed = IntArray(2) private var nestedOffsetY: Int = 0 @@ -78,7 +80,7 @@ class DuckDuckGoWebView : WebView, NestedScrollingChild { var deltaY = lastY - eventY if (deltaY > 0) { - lastClampedY = false + lastClampedTopY = false } if (dispatchNestedPreScroll(0, deltaY, scrollConsumed, scrollOffset)) { @@ -90,15 +92,18 @@ class DuckDuckGoWebView : WebView, NestedScrollingChild { returnValue = super.onTouchEvent(event) - if (scrollY == 0 && lastClampedY) { + if (scrollY == 0 && lastClampedTopY) { // we have reached the top and are clamped -> enable swipeRefresh (by default always disabled) enableSwipeRefresh(true) + stopNestedScroll() } else if (dispatchNestedScroll(0, scrollOffset[1], 0, deltaY, scrollOffset)) { event.offsetLocation(0f, scrollOffset[1].toFloat()) nestedOffsetY += scrollOffset[1] lastY -= scrollOffset[1] } + + lastDeltaY = deltaY } MotionEvent.ACTION_DOWN -> { @@ -146,19 +151,27 @@ class DuckDuckGoWebView : WebView, NestedScrollingChild { nestedScrollHelper.dispatchNestedPreFling(velocityX, velocityY) override fun onOverScrolled(scrollX: Int, scrollY: Int, clampedX: Boolean, clampedY: Boolean) { - lastClampedY = clampedY + // taking into account lastDeltaY since we are only interested whether we clamped at the top + lastClampedTopY = clampedY && lastDeltaY <= 0 enableSwipeRefresh(clampedY && scrollY == 0) super.onOverScrolled(scrollX, scrollY, clampedX, clampedY) } private fun enableSwipeRefresh(enable: Boolean) { - enableSwipeRefreshCallback?.invoke(enable) + enableSwipeRefreshCallback?.invoke(enable && contentAllowsSwipeToRefresh) } fun setEnableSwipeRefreshCallback(callback: (Boolean) -> Unit) { enableSwipeRefreshCallback = callback } + fun setContentAllowsSwipeToRefresh(allowed: Boolean) { + contentAllowsSwipeToRefresh = allowed + if (!allowed) { + enableSwipeRefresh(false) + } + } + companion object { /*