Skip to content
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -310,6 +310,8 @@ public open class ReactViewGroup public constructor(context: Context?) :
override fun onHoverEvent(event: MotionEvent): Boolean {
@Suppress("DEPRECATION")
if (ReactFeatureFlags.dispatchPointerEvents) {
// View's own handling sends the accessibility hover events explore-by-touch relies on
super.onHoverEvent(event)
// Match the logic from onTouchEvent if pointer events are enabled
return canBeTouchTarget(pointerEvents)
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -9,19 +9,24 @@ package com.facebook.react.views.view

import android.app.Activity
import android.content.Context
import android.view.MotionEvent
import android.view.View
import android.view.ViewGroup
import android.view.accessibility.AccessibilityEvent
import android.widget.FrameLayout
import com.facebook.react.internal.featureflags.ReactNativeFeatureFlags
import com.facebook.react.internal.featureflags.ReactNativeFeatureFlagsDefaults
import com.facebook.react.internal.featureflags.ReactNativeFeatureFlagsForTests
import com.facebook.react.uimanager.HasElevatedDescendantCache
import com.facebook.react.uimanager.PointerEvents
import org.assertj.core.api.Assertions.assertThat
import org.junit.After
import org.junit.Before
import org.junit.Test
import org.junit.runner.RunWith
import org.robolectric.Robolectric
import org.robolectric.RobolectricTestRunner
import org.robolectric.shadows.ShadowLooper

@RunWith(RobolectricTestRunner::class)
class ReactViewGroupTest {
Expand All @@ -34,6 +39,32 @@ class ReactViewGroupTest {
context = Robolectric.buildActivity(Activity::class.java).create().get()
}

@After
fun tearDown() {
setDispatchPointerEvents(false)
}

@Test
fun `hover with pointer events dispatched still sends the accessibility hover event`() {
setDispatchPointerEvents(true)
val rvg = attachForHover()
val sentEventTypes = recordSentAccessibilityEvents(rvg)

rvg.onHoverEvent(obtainHoverEvent(MotionEvent.ACTION_HOVER_ENTER))

assertThat(sentEventTypes).contains(AccessibilityEvent.TYPE_VIEW_HOVER_ENTER)
}

@Test
fun `hover with pointer events dispatched still answers whether the view is a touch target`() {
setDispatchPointerEvents(true)
val rvg = attachForHover()

assertThat(rvg.onHoverEvent(obtainHoverEvent(MotionEvent.ACTION_HOVER_MOVE))).isTrue()
rvg.pointerEvents = PointerEvents.NONE
assertThat(rvg.onHoverEvent(obtainHoverEvent(MotionEvent.ACTION_HOVER_MOVE))).isFalse()
}

@Test
fun `View clipping - ensure allChildren properly resizes when adding views in sequence`() {
val rvg = ReactViewGroup(context)
Expand Down Expand Up @@ -280,6 +311,37 @@ class ReactViewGroupTest {
}

private fun elevatedChild(): View = View(context).apply { elevation = 10f }

@Suppress("DEPRECATION")
private fun setDispatchPointerEvents(enabled: Boolean) {
com.facebook.react.config.ReactFeatureFlags.dispatchPointerEvents = enabled
}

// An accessibility hover event is sent only from a view attached to a window.
private fun attachForHover(): ReactViewGroup {
val activity = Robolectric.buildActivity(Activity::class.java).setup().get()
val rvg = ReactViewGroup(activity)
rvg.importantForAccessibility = View.IMPORTANT_FOR_ACCESSIBILITY_YES
activity.setContentView(rvg)
ShadowLooper.idleMainLooper()
rvg.layout(0, 0, 100, 100)
assertThat(rvg.isAttachedToWindow).isTrue()
return rvg
}

private fun recordSentAccessibilityEvents(view: View): List<Int> {
val sentEventTypes = mutableListOf<Int>()
view.accessibilityDelegate =
object : View.AccessibilityDelegate() {
override fun sendAccessibilityEvent(host: View, eventType: Int) {
sentEventTypes.add(eventType)
}
}
return sentEventTypes
}

private fun obtainHoverEvent(action: Int): MotionEvent =
MotionEvent.obtain(0L, 0L, action, 50f, 50f, 0)
}

class TestView(context: Context, yPos: Int) : View(context) {
Expand Down
Loading