Skip to content

Commit

Permalink
Fix Android native buttons emitting 2 press events when talkbalk is e…
Browse files Browse the repository at this point in the history
…nabled (#3002)

## Description

When talkbalk accessability mode is enabled, pressing on any of the
buttons provided by gesture handler (`BaseButton`, `RawButton`,
`BorderlessButton`, Et Cetera) will cause 2 separate press events to go
off.

This PR fixes that invalid behaviour.

Closes: #2808 

## Test plan

- enable talkback mode
- run the attached example
- see how the button labeled `BaseButton` executes twice on `main` and a
single time on this branch.

## Attached example

```js
import React from 'react';
import { BaseButton } from 'react-native-gesture-handler';
import { Text, View, StyleSheet } from 'react-native';

const press = () => console.log('JS pressed');
const touch = () => console.log('JS touch');

export default function App() {
  return (
    <View style={styles.root}>
      <BaseButton onPress={press}>
        <View
          accessible
          accessibilityRole="button"
          style={styles.button}
          onTouchStart={touch}>
          <Text>BaseButton</Text>
        </View>
      </BaseButton>
    </View>
  );
}

const styles = StyleSheet.create({
  root: {
    justifyContent: 'center',
    flex: 1,
  },
  button: {
    width: 200,
    height: 100,
    margin: 20,
    backgroundColor: 'cyan',
  },
});
```

## Tests

### Normal behaviour (no talkback, no fix):

![image](https://github.com/user-attachments/assets/74912600-cd97-4347-aae5-8930228a725e)

### Before fix:

![image](https://github.com/user-attachments/assets/b0e1083a-d1ec-4ed6-ab57-7a1236f18ed2)

### With fix:

![image](https://github.com/user-attachments/assets/369c0b68-6c08-4d3b-8f04-5c9701bfd8ac)
  • Loading branch information
latekvo committed Jul 24, 2024
1 parent 9bfff5c commit 7999510
Showing 1 changed file with 16 additions and 0 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,8 @@ import android.widget.ScrollView
import com.facebook.react.views.scroll.ReactScrollView
import com.facebook.react.views.swiperefresh.ReactSwipeRefreshLayout
import com.facebook.react.views.textinput.ReactEditText
import com.swmansion.gesturehandler.react.RNGestureHandlerButtonViewManager
import com.swmansion.gesturehandler.react.isScreenReaderOn

class NativeViewGestureHandler : GestureHandler<NativeViewGestureHandler>() {
private var shouldActivateOnStart = false
Expand Down Expand Up @@ -82,6 +84,17 @@ class NativeViewGestureHandler : GestureHandler<NativeViewGestureHandler>() {

override fun onHandle(event: MotionEvent, sourceEvent: MotionEvent) {
val view = view!!

val isTouchExplorationEnabled = view.context.isScreenReaderOn()

if (view is RNGestureHandlerButtonViewManager.ButtonViewGroup && isTouchExplorationEnabled) {
// Fix for: https://github.com/software-mansion/react-native-gesture-handler/issues/2808
// When TalkBack is enabled, events are often not being sent to the orchestrator for processing.
// Instead, states will be changed directly by an alternative mechanism added in this PR:
// https://github.com/software-mansion/react-native-gesture-handler/pull/2234
return
}

if (event.actionMasked == MotionEvent.ACTION_UP) {
if (state == STATE_UNDETERMINED && !hook.canBegin(event)) {
cancel()
Expand All @@ -106,13 +119,16 @@ class NativeViewGestureHandler : GestureHandler<NativeViewGestureHandler>() {
view.onTouchEvent(event)
activate()
}

tryIntercept(view, event) -> {
view.onTouchEvent(event)
activate()
}

hook.wantsToHandleEventBeforeActivation() -> {
hook.handleEventBeforeActivation(event)
}

state != STATE_BEGAN -> {
if (hook.canBegin(event)) {
begin()
Expand Down

0 comments on commit 7999510

Please sign in to comment.