Commit 311864c2 authored by MozLando's avatar MozLando
Browse files

Merge #6244 #6343



6244: For #6242: Updates nestedScrolling handling r=pocmo a=sblatz



6343: Fix build of ui-test docker image r=rpappalax a=JohanLorenzo
Co-authored-by: default avatarSawyer Blatz <sdblatz@gmail.com>
Co-authored-by: default avatarJohan Lorenzo <jlorenzo@mozilla.com>
......@@ -11,6 +11,8 @@ import androidx.core.view.NestedScrollingChild
import androidx.core.view.NestedScrollingChildHelper
import androidx.core.view.ViewCompat
import org.mozilla.geckoview.GeckoView
import org.mozilla.geckoview.PanZoomController.INPUT_RESULT_HANDLED_CONTENT
import org.mozilla.geckoview.PanZoomController.INPUT_RESULT_UNHANDLED
/**
* geckoView that supports nested scrolls (for using in a CoordinatorLayout).
......@@ -23,6 +25,7 @@ import org.mozilla.geckoview.GeckoView
* https://github.com/takahirom/webview-in-coordinatorlayout
*/
@Suppress("TooManyFunctions")
open class NestedGeckoView(context: Context) : GeckoView(context), NestedScrollingChild {
@VisibleForTesting
......@@ -39,10 +42,14 @@ open class NestedGeckoView(context: Context) : GeckoView(context), NestedScrolli
@VisibleForTesting
internal var childHelper: NestedScrollingChildHelper = NestedScrollingChildHelper(this)
@VisibleForTesting
internal var shouldScroll = true
init {
isNestedScrollingEnabled = true
}
@Suppress("ComplexMethod")
override fun onTouchEventForResult(ev: MotionEvent): Int {
val event = MotionEvent.obtain(ev)
val action = ev.actionMasked
......@@ -52,43 +59,59 @@ open class NestedGeckoView(context: Context) : GeckoView(context), NestedScrolli
nestedOffsetY = 0
}
// Execute event handler from parent class in all cases
val eventHandled = handleEvent(event)
when (action) {
MotionEvent.ACTION_MOVE -> {
val allowScroll = !shouldPinOnScreen()
var deltaY = lastY - eventY
if (allowScroll && dispatchNestedPreScroll(0, deltaY, scrollConsumed, scrollOffset)) {
deltaY -= scrollConsumed[1]
event.offsetLocation(0f, (-scrollOffset[1]).toFloat())
nestedOffsetY += scrollOffset[1]
}
lastY = eventY - scrollOffset[1]
if (allowScroll && dispatchNestedScroll(0, scrollOffset[1], 0, deltaY, scrollOffset)) {
lastY -= scrollOffset[1]
event.offsetLocation(0f, scrollOffset[1].toFloat())
nestedOffsetY += scrollOffset[1]
if (shouldScroll) {
val allowScroll = !shouldPinOnScreen()
var deltaY = lastY - eventY
if (allowScroll && dispatchNestedPreScroll(0, deltaY, scrollConsumed, scrollOffset)) {
deltaY -= scrollConsumed[1]
event.offsetLocation(0f, (-scrollOffset[1]).toFloat())
nestedOffsetY += scrollOffset[1]
}
lastY = eventY - scrollOffset[1]
if (allowScroll && dispatchNestedScroll(0, scrollOffset[1], 0, deltaY, scrollOffset)) {
lastY -= scrollOffset[1]
event.offsetLocation(0f, scrollOffset[1].toFloat())
nestedOffsetY += scrollOffset[1]
}
}
}
MotionEvent.ACTION_DOWN -> {
lastY = eventY
startNestedScroll(ViewCompat.SCROLL_AXIS_VERTICAL)
// Only scroll for certain eventHandled responses
// See https://github.com/mozilla-mobile/fenix/issues/8768#issuecomment-592718468
shouldScroll = (eventHandled != INPUT_RESULT_HANDLED_CONTENT && eventHandled != INPUT_RESULT_UNHANDLED)
if (shouldScroll) {
lastY = eventY
startNestedScroll(ViewCompat.SCROLL_AXIS_VERTICAL)
} else {
stopNestedScroll()
}
}
// We don't care about other touch events
MotionEvent.ACTION_UP, MotionEvent.ACTION_CANCEL -> stopNestedScroll()
}
// Execute event handler from parent class in all cases
val eventHandled = super.onTouchEventForResult(event)
// Recycle previously obtained event
event.recycle()
return eventHandled
}
// Helper function to make testing of this method easier
internal fun handleEvent(event: MotionEvent): Int {
return super.onTouchEventForResult(event)
}
override fun setNestedScrollingEnabled(enabled: Boolean) {
childHelper.isNestedScrollingEnabled = enabled
}
......
......@@ -13,17 +13,21 @@ import android.view.MotionEvent.ACTION_UP
import androidx.core.view.NestedScrollingChildHelper
import androidx.core.view.ViewCompat.SCROLL_AXIS_VERTICAL
import androidx.test.ext.junit.runners.AndroidJUnit4
import mozilla.components.support.test.any
import mozilla.components.support.test.mock
import mozilla.components.support.test.mockMotionEvent
import org.junit.Assert.assertEquals
import org.junit.Assert.assertTrue
import org.junit.Test
import org.junit.runner.RunWith
import org.mockito.Mockito.any
import org.mockito.Mockito
import org.mockito.Mockito.anyInt
import org.mockito.Mockito.doReturn
import org.mockito.Mockito.times
import org.mockito.Mockito.verify
import org.mozilla.geckoview.PanZoomController.INPUT_RESULT_HANDLED
import org.mozilla.geckoview.PanZoomController.INPUT_RESULT_HANDLED_CONTENT
import org.mozilla.geckoview.PanZoomController.INPUT_RESULT_UNHANDLED
import org.robolectric.Robolectric.buildActivity
@RunWith(AndroidJUnit4::class)
......@@ -70,15 +74,44 @@ class NestedGeckoViewTest {
}
@Test
fun `verify onTouchEvent when ACTION_DOWN`() {
val nestedWebView = NestedGeckoView(context)
fun `verify onTouchEvent when ACTION_DOWN and INPUT_RESULT_HANDLED`() {
val nestedWebView = Mockito.spy(NestedGeckoView(context))
val mockChildHelper: NestedScrollingChildHelper = mock()
nestedWebView.childHelper = mockChildHelper
doReturn(INPUT_RESULT_HANDLED).`when`(nestedWebView).handleEvent(any())
nestedWebView.onTouchEvent(mockMotionEvent(ACTION_DOWN))
verify(mockChildHelper).startNestedScroll(SCROLL_AXIS_VERTICAL)
}
@Test
fun `verify onTouchEvent when ACTION_DOWN and INPUT_RESULT_HANDLED_CONTENT`() {
val nestedWebView = Mockito.spy(NestedGeckoView(context))
val mockChildHelper: NestedScrollingChildHelper = mock()
nestedWebView.childHelper = mockChildHelper
doReturn(INPUT_RESULT_HANDLED_CONTENT).`when`(nestedWebView).handleEvent(any())
nestedWebView.onTouchEvent(mockMotionEvent(ACTION_DOWN))
verify(mockChildHelper).stopNestedScroll()
}
@Test
fun `verify onTouchEvent when ACTION_DOWN and INPUT_RESULT_UNHANDLED`() {
val nestedWebView = Mockito.spy(NestedGeckoView(context))
val mockChildHelper: NestedScrollingChildHelper = mock()
nestedWebView.childHelper = mockChildHelper
doReturn(INPUT_RESULT_UNHANDLED).`when`(nestedWebView).handleEvent(any())
nestedWebView.onTouchEvent(mockMotionEvent(ACTION_DOWN))
verify(mockChildHelper).stopNestedScroll()
}
@Test
fun `verify onTouchEvent when ACTION_MOVE`() {
val nestedWebView = NestedGeckoView(context)
......
......@@ -11,6 +11,8 @@ import androidx.core.view.NestedScrollingChild
import androidx.core.view.NestedScrollingChildHelper
import androidx.core.view.ViewCompat
import org.mozilla.geckoview.GeckoView
import org.mozilla.geckoview.PanZoomController.INPUT_RESULT_HANDLED_CONTENT
import org.mozilla.geckoview.PanZoomController.INPUT_RESULT_UNHANDLED
/**
* geckoView that supports nested scrolls (for using in a CoordinatorLayout).
......@@ -23,6 +25,7 @@ import org.mozilla.geckoview.GeckoView
* https://github.com/takahirom/webview-in-coordinatorlayout
*/
@Suppress("TooManyFunctions")
open class NestedGeckoView(context: Context) : GeckoView(context), NestedScrollingChild {
@VisibleForTesting
......@@ -39,10 +42,14 @@ open class NestedGeckoView(context: Context) : GeckoView(context), NestedScrolli
@VisibleForTesting
internal var childHelper: NestedScrollingChildHelper = NestedScrollingChildHelper(this)
@VisibleForTesting
internal var shouldScroll = true
init {
isNestedScrollingEnabled = true
}
@Suppress("ComplexMethod")
override fun onTouchEventForResult(ev: MotionEvent): Int {
val event = MotionEvent.obtain(ev)
val action = ev.actionMasked
......@@ -52,43 +59,59 @@ open class NestedGeckoView(context: Context) : GeckoView(context), NestedScrolli
nestedOffsetY = 0
}
// Execute event handler from parent class in all cases
val eventHandled = handleEvent(event)
when (action) {
MotionEvent.ACTION_MOVE -> {
val allowScroll = !shouldPinOnScreen()
var deltaY = lastY - eventY
if (allowScroll && dispatchNestedPreScroll(0, deltaY, scrollConsumed, scrollOffset)) {
deltaY -= scrollConsumed[1]
event.offsetLocation(0f, (-scrollOffset[1]).toFloat())
nestedOffsetY += scrollOffset[1]
}
lastY = eventY - scrollOffset[1]
if (allowScroll && dispatchNestedScroll(0, scrollOffset[1], 0, deltaY, scrollOffset)) {
lastY -= scrollOffset[1]
event.offsetLocation(0f, scrollOffset[1].toFloat())
nestedOffsetY += scrollOffset[1]
if (shouldScroll) {
val allowScroll = !shouldPinOnScreen()
var deltaY = lastY - eventY
if (allowScroll && dispatchNestedPreScroll(0, deltaY, scrollConsumed, scrollOffset)) {
deltaY -= scrollConsumed[1]
event.offsetLocation(0f, (-scrollOffset[1]).toFloat())
nestedOffsetY += scrollOffset[1]
}
lastY = eventY - scrollOffset[1]
if (allowScroll && dispatchNestedScroll(0, scrollOffset[1], 0, deltaY, scrollOffset)) {
lastY -= scrollOffset[1]
event.offsetLocation(0f, scrollOffset[1].toFloat())
nestedOffsetY += scrollOffset[1]
}
}
}
MotionEvent.ACTION_DOWN -> {
lastY = eventY
startNestedScroll(ViewCompat.SCROLL_AXIS_VERTICAL)
// Only scroll for certain eventHandled responses
// See https://github.com/mozilla-mobile/fenix/issues/8768#issuecomment-592718468
shouldScroll = (eventHandled != INPUT_RESULT_HANDLED_CONTENT && eventHandled != INPUT_RESULT_UNHANDLED)
if (shouldScroll) {
lastY = eventY
startNestedScroll(ViewCompat.SCROLL_AXIS_VERTICAL)
} else {
stopNestedScroll()
}
}
// We don't care about other touch events
MotionEvent.ACTION_UP, MotionEvent.ACTION_CANCEL -> stopNestedScroll()
}
// Execute event handler from parent class in all cases
val eventHandled = super.onTouchEventForResult(event)
// Recycle previously obtained event
event.recycle()
return eventHandled
}
// Helper function to make testing of this method easier
internal fun handleEvent(event: MotionEvent): Int {
return super.onTouchEventForResult(event)
}
override fun setNestedScrollingEnabled(enabled: Boolean) {
childHelper.isNestedScrollingEnabled = enabled
}
......
......@@ -13,17 +13,21 @@ import android.view.MotionEvent.ACTION_UP
import androidx.core.view.NestedScrollingChildHelper
import androidx.core.view.ViewCompat.SCROLL_AXIS_VERTICAL
import androidx.test.ext.junit.runners.AndroidJUnit4
import mozilla.components.support.test.any
import mozilla.components.support.test.mock
import mozilla.components.support.test.mockMotionEvent
import org.junit.Assert.assertEquals
import org.junit.Assert.assertTrue
import org.junit.Test
import org.junit.runner.RunWith
import org.mockito.Mockito.any
import org.mockito.Mockito
import org.mockito.Mockito.anyInt
import org.mockito.Mockito.doReturn
import org.mockito.Mockito.times
import org.mockito.Mockito.verify
import org.mozilla.geckoview.PanZoomController.INPUT_RESULT_HANDLED
import org.mozilla.geckoview.PanZoomController.INPUT_RESULT_HANDLED_CONTENT
import org.mozilla.geckoview.PanZoomController.INPUT_RESULT_UNHANDLED
import org.robolectric.Robolectric.buildActivity
@RunWith(AndroidJUnit4::class)
......@@ -70,15 +74,44 @@ class NestedGeckoViewTest {
}
@Test
fun `verify onTouchEvent when ACTION_DOWN`() {
val nestedWebView = NestedGeckoView(context)
fun `verify onTouchEvent when ACTION_DOWN and INPUT_RESULT_HANDLED`() {
val nestedWebView = Mockito.spy(NestedGeckoView(context))
val mockChildHelper: NestedScrollingChildHelper = mock()
nestedWebView.childHelper = mockChildHelper
doReturn(INPUT_RESULT_HANDLED).`when`(nestedWebView).handleEvent(any())
nestedWebView.onTouchEvent(mockMotionEvent(ACTION_DOWN))
verify(mockChildHelper).startNestedScroll(SCROLL_AXIS_VERTICAL)
}
@Test
fun `verify onTouchEvent when ACTION_DOWN and INPUT_RESULT_HANDLED_CONTENT`() {
val nestedWebView = Mockito.spy(NestedGeckoView(context))
val mockChildHelper: NestedScrollingChildHelper = mock()
nestedWebView.childHelper = mockChildHelper
doReturn(INPUT_RESULT_HANDLED_CONTENT).`when`(nestedWebView).handleEvent(any())
nestedWebView.onTouchEvent(mockMotionEvent(ACTION_DOWN))
verify(mockChildHelper).stopNestedScroll()
}
@Test
fun `verify onTouchEvent when ACTION_DOWN and INPUT_RESULT_UNHANDLED`() {
val nestedWebView = Mockito.spy(NestedGeckoView(context))
val mockChildHelper: NestedScrollingChildHelper = mock()
nestedWebView.childHelper = mockChildHelper
doReturn(INPUT_RESULT_UNHANDLED).`when`(nestedWebView).handleEvent(any())
nestedWebView.onTouchEvent(mockMotionEvent(ACTION_DOWN))
verify(mockChildHelper).stopNestedScroll()
}
@Test
fun `verify onTouchEvent when ACTION_MOVE`() {
val nestedWebView = NestedGeckoView(context)
......
......@@ -11,6 +11,8 @@ import androidx.core.view.NestedScrollingChild
import androidx.core.view.NestedScrollingChildHelper
import androidx.core.view.ViewCompat
import org.mozilla.geckoview.GeckoView
import org.mozilla.geckoview.PanZoomController.INPUT_RESULT_HANDLED_CONTENT
import org.mozilla.geckoview.PanZoomController.INPUT_RESULT_UNHANDLED
/**
* geckoView that supports nested scrolls (for using in a CoordinatorLayout).
......@@ -23,6 +25,7 @@ import org.mozilla.geckoview.GeckoView
* https://github.com/takahirom/webview-in-coordinatorlayout
*/
@Suppress("TooManyFunctions")
open class NestedGeckoView(context: Context) : GeckoView(context), NestedScrollingChild {
@VisibleForTesting
......@@ -39,10 +42,14 @@ open class NestedGeckoView(context: Context) : GeckoView(context), NestedScrolli
@VisibleForTesting
internal var childHelper: NestedScrollingChildHelper = NestedScrollingChildHelper(this)
@VisibleForTesting
internal var shouldScroll = true
init {
isNestedScrollingEnabled = true
}
@Suppress("ComplexMethod")
override fun onTouchEventForResult(ev: MotionEvent): Int {
val event = MotionEvent.obtain(ev)
val action = ev.actionMasked
......@@ -52,43 +59,59 @@ open class NestedGeckoView(context: Context) : GeckoView(context), NestedScrolli
nestedOffsetY = 0
}
// Execute event handler from parent class in all cases
val eventHandled = handleEvent(event)
when (action) {
MotionEvent.ACTION_MOVE -> {
val allowScroll = !shouldPinOnScreen()
var deltaY = lastY - eventY
if (allowScroll && dispatchNestedPreScroll(0, deltaY, scrollConsumed, scrollOffset)) {
deltaY -= scrollConsumed[1]
event.offsetLocation(0f, (-scrollOffset[1]).toFloat())
nestedOffsetY += scrollOffset[1]
}
lastY = eventY - scrollOffset[1]
if (allowScroll && dispatchNestedScroll(0, scrollOffset[1], 0, deltaY, scrollOffset)) {
lastY -= scrollOffset[1]
event.offsetLocation(0f, scrollOffset[1].toFloat())
nestedOffsetY += scrollOffset[1]
if (shouldScroll) {
val allowScroll = !shouldPinOnScreen()
var deltaY = lastY - eventY
if (allowScroll && dispatchNestedPreScroll(0, deltaY, scrollConsumed, scrollOffset)) {
deltaY -= scrollConsumed[1]
event.offsetLocation(0f, (-scrollOffset[1]).toFloat())
nestedOffsetY += scrollOffset[1]
}
lastY = eventY - scrollOffset[1]
if (allowScroll && dispatchNestedScroll(0, scrollOffset[1], 0, deltaY, scrollOffset)) {
lastY -= scrollOffset[1]
event.offsetLocation(0f, scrollOffset[1].toFloat())
nestedOffsetY += scrollOffset[1]
}
}
}
MotionEvent.ACTION_DOWN -> {
lastY = eventY
startNestedScroll(ViewCompat.SCROLL_AXIS_VERTICAL)
// Only scroll for certain eventHandled responses
// See https://github.com/mozilla-mobile/fenix/issues/8768#issuecomment-592718468
shouldScroll = (eventHandled != INPUT_RESULT_HANDLED_CONTENT && eventHandled != INPUT_RESULT_UNHANDLED)
if (shouldScroll) {
lastY = eventY
startNestedScroll(ViewCompat.SCROLL_AXIS_VERTICAL)
} else {
stopNestedScroll()
}
}
// We don't care about other touch events
MotionEvent.ACTION_UP, MotionEvent.ACTION_CANCEL -> stopNestedScroll()
}
// Execute event handler from parent class in all cases
val eventHandled = super.onTouchEventForResult(event)
// Recycle previously obtained event
event.recycle()
return eventHandled
}
// Helper function to make testing of this method easier
internal fun handleEvent(event: MotionEvent): Int {
return super.onTouchEventForResult(event)
}
override fun setNestedScrollingEnabled(enabled: Boolean) {
childHelper.isNestedScrollingEnabled = enabled
}
......
......@@ -13,17 +13,21 @@ import android.view.MotionEvent.ACTION_UP
import androidx.core.view.NestedScrollingChildHelper
import androidx.core.view.ViewCompat.SCROLL_AXIS_VERTICAL
import androidx.test.ext.junit.runners.AndroidJUnit4
import mozilla.components.support.test.any
import mozilla.components.support.test.mock
import mozilla.components.support.test.mockMotionEvent
import org.junit.Assert.assertEquals
import org.junit.Assert.assertTrue
import org.junit.Test
import org.junit.runner.RunWith
import org.mockito.Mockito.any
import org.mockito.Mockito
import org.mockito.Mockito.anyInt
import org.mockito.Mockito.doReturn
import org.mockito.Mockito.times
import org.mockito.Mockito.verify
import org.mozilla.geckoview.PanZoomController.INPUT_RESULT_HANDLED
import org.mozilla.geckoview.PanZoomController.INPUT_RESULT_HANDLED_CONTENT
import org.mozilla.geckoview.PanZoomController.INPUT_RESULT_UNHANDLED
import org.robolectric.Robolectric.buildActivity
@RunWith(AndroidJUnit4::class)
......@@ -70,15 +74,44 @@ class NestedGeckoViewTest {
}
@Test
fun `verify onTouchEvent when ACTION_DOWN`() {
val nestedWebView = NestedGeckoView(context)
fun `verify onTouchEvent when ACTION_DOWN and INPUT_RESULT_HANDLED`() {
val nestedWebView = Mockito.spy(NestedGeckoView(context))
val mockChildHelper: NestedScrollingChildHelper = mock()
nestedWebView.childHelper = mockChildHelper
doReturn(INPUT_RESULT_HANDLED).`when`(nestedWebView).handleEvent(any())
nestedWebView.onTouchEvent(mockMotionEvent(ACTION_DOWN))
verify(mockChildHelper).startNestedScroll(SCROLL_AXIS_VERTICAL)
}
@Test
fun `verify onTouchEvent when ACTION_DOWN and INPUT_RESULT_HANDLED_CONTENT`() {
val nestedWebView = Mockito.spy(NestedGeckoView(context))
val mockChildHelper: NestedScrollingChildHelper = mock()
nestedWebView.childHelper = mockChildHelper
doReturn(INPUT_RESULT_HANDLED_CONTENT).`when`(nestedWebView).handleEvent(any())
nestedWebView.onTouchEvent(mockMotionEvent(ACTION_DOWN))
verify(mockChildHelper).stopNestedScroll()
}
@Test
fun `verify onTouchEvent when ACTION_DOWN and INPUT_RESULT_UNHANDLED`() {
val nestedWebView = Mockito.spy(NestedGeckoView(context))
val mockChildHelper: NestedScrollingChildHelper = mock()
nestedWebView.childHelper = mockChildHelper
doReturn(INPUT_RESULT_UNHANDLED).`when`(nestedWebView).handleEvent(any())
nestedWebView.onTouchEvent(mockMotionEvent(ACTION_DOWN))
verify(mockChildHelper).stopNestedScroll()
}
@Test
fun `verify onTouchEvent when ACTION_MOVE`() {
val nestedWebView = NestedGeckoView(context)
......
......@@ -21,6 +21,7 @@ WORKDIR /builds/worker/
ENV ANDROID_SDK_VERSION='3859397' \
ANDROID_SDK_ROOT='/builds/worker/android-sdk-linux' \
CURL='curl --location --retry 5' \
GRADLE_OPTS='-Xmx4096m -Dorg.gradle.daemon=false' \
LANG='en_US.UTF-8' \
TERM='dumb' \
......@@ -35,7 +36,6 @@ RUN apt-get update -qq \
# which we cannot navigate while building the Docker image.
&& apt-get install -y tzdata \
&& apt-get install -y openjdk-8-jdk \
wget \
expect \
git \
curl \
......@@ -52,7 +52,7 @@ RUN pip install taskcluster