Skip to content

Commit 4279958

Browse files
fabriziocuccimeta-codesync[bot]
authored andcommitted
Set isClickabe=true by default (#54607)
Summary: Pull Request resolved: #54607 Changelog: [Android][Changed] - Set isClickable=true by default or based on the pointerEvents value for all views to improve accessibility tool detection while maintaining the configured focusable state Reviewed By: javache Differential Revision: D87430057 fbshipit-source-id: f488e9306069952338b94e8eb0106ed0dc6107e1
1 parent 79b09ce commit 4279958

21 files changed

+177
-38
lines changed

packages/react-native/ReactAndroid/src/main/java/com/facebook/react/internal/featureflags/ReactNativeFeatureFlags.kt

Lines changed: 7 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -4,7 +4,7 @@
44
* This source code is licensed under the MIT license found in the
55
* LICENSE file in the root directory of this source tree.
66
*
7-
* @generated SignedSource<<b1469e448ca6f773a3095ec2cbf4bc00>>
7+
* @generated SignedSource<<783b32e7fdda85b8577753f0553288d5>>
88
*/
99

1010
/**
@@ -456,6 +456,12 @@ public object ReactNativeFeatureFlags {
456456
@JvmStatic
457457
public fun shouldSetEnabledBasedOnAccessibilityState(): Boolean = accessor.shouldSetEnabledBasedOnAccessibilityState()
458458

459+
/**
460+
* Sets isClickable=true by default on all React Native views on Android to improve UI harvesting detection while maintaining focusable=false to preserve expected behavior.
461+
*/
462+
@JvmStatic
463+
public fun shouldSetIsClickableByDefault(): Boolean = accessor.shouldSetIsClickableByDefault()
464+
459465
/**
460466
* Do not emit touchcancel from Android ScrollView, instead native topScroll event will trigger responder transfer and terminate in RN renderer.
461467
*/

packages/react-native/ReactAndroid/src/main/java/com/facebook/react/internal/featureflags/ReactNativeFeatureFlagsCxxAccessor.kt

Lines changed: 11 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -4,7 +4,7 @@
44
* This source code is licensed under the MIT license found in the
55
* LICENSE file in the root directory of this source tree.
66
*
7-
* @generated SignedSource<<b2c2e874b05283e0ebd62899f7c417d8>>
7+
* @generated SignedSource<<15bb2229e65b6a6ae728d512ff51885f>>
88
*/
99

1010
/**
@@ -91,6 +91,7 @@ internal class ReactNativeFeatureFlagsCxxAccessor : ReactNativeFeatureFlagsAcces
9191
private var preventShadowTreeCommitExhaustionCache: Boolean? = null
9292
private var shouldPressibilityUseW3CPointerEventsForHoverCache: Boolean? = null
9393
private var shouldSetEnabledBasedOnAccessibilityStateCache: Boolean? = null
94+
private var shouldSetIsClickableByDefaultCache: Boolean? = null
9495
private var shouldTriggerResponderTransferOnScrollAndroidCache: Boolean? = null
9596
private var skipActivityIdentityAssertionOnHostPauseCache: Boolean? = null
9697
private var traceTurboModulePromiseRejectionsOnAndroidCache: Boolean? = null
@@ -749,6 +750,15 @@ internal class ReactNativeFeatureFlagsCxxAccessor : ReactNativeFeatureFlagsAcces
749750
return cached
750751
}
751752

753+
override fun shouldSetIsClickableByDefault(): Boolean {
754+
var cached = shouldSetIsClickableByDefaultCache
755+
if (cached == null) {
756+
cached = ReactNativeFeatureFlagsCxxInterop.shouldSetIsClickableByDefault()
757+
shouldSetIsClickableByDefaultCache = cached
758+
}
759+
return cached
760+
}
761+
752762
override fun shouldTriggerResponderTransferOnScrollAndroid(): Boolean {
753763
var cached = shouldTriggerResponderTransferOnScrollAndroidCache
754764
if (cached == null) {

packages/react-native/ReactAndroid/src/main/java/com/facebook/react/internal/featureflags/ReactNativeFeatureFlagsCxxInterop.kt

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -4,7 +4,7 @@
44
* This source code is licensed under the MIT license found in the
55
* LICENSE file in the root directory of this source tree.
66
*
7-
* @generated SignedSource<<ccb22ddcd1a76b7c52cf0f1b23e6152b>>
7+
* @generated SignedSource<<85a6247f6049f60c18efdce5db0cccdf>>
88
*/
99

1010
/**
@@ -170,6 +170,8 @@ public object ReactNativeFeatureFlagsCxxInterop {
170170

171171
@DoNotStrip @JvmStatic public external fun shouldSetEnabledBasedOnAccessibilityState(): Boolean
172172

173+
@DoNotStrip @JvmStatic public external fun shouldSetIsClickableByDefault(): Boolean
174+
173175
@DoNotStrip @JvmStatic public external fun shouldTriggerResponderTransferOnScrollAndroid(): Boolean
174176

175177
@DoNotStrip @JvmStatic public external fun skipActivityIdentityAssertionOnHostPause(): Boolean

packages/react-native/ReactAndroid/src/main/java/com/facebook/react/internal/featureflags/ReactNativeFeatureFlagsDefaults.kt

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -4,7 +4,7 @@
44
* This source code is licensed under the MIT license found in the
55
* LICENSE file in the root directory of this source tree.
66
*
7-
* @generated SignedSource<<30ca2685ceb6f2733531f5e7fce4416d>>
7+
* @generated SignedSource<<614d228af81090b8b1dee65f4bfb67d7>>
88
*/
99

1010
/**
@@ -165,6 +165,8 @@ public open class ReactNativeFeatureFlagsDefaults : ReactNativeFeatureFlagsProvi
165165

166166
override fun shouldSetEnabledBasedOnAccessibilityState(): Boolean = true
167167

168+
override fun shouldSetIsClickableByDefault(): Boolean = false
169+
168170
override fun shouldTriggerResponderTransferOnScrollAndroid(): Boolean = false
169171

170172
override fun skipActivityIdentityAssertionOnHostPause(): Boolean = false

packages/react-native/ReactAndroid/src/main/java/com/facebook/react/internal/featureflags/ReactNativeFeatureFlagsLocalAccessor.kt

Lines changed: 12 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -4,7 +4,7 @@
44
* This source code is licensed under the MIT license found in the
55
* LICENSE file in the root directory of this source tree.
66
*
7-
* @generated SignedSource<<6d1a15e64f42cc7d8869300720276215>>
7+
* @generated SignedSource<<e4971d843cf79d94f8c61394f4c27204>>
88
*/
99

1010
/**
@@ -95,6 +95,7 @@ internal class ReactNativeFeatureFlagsLocalAccessor : ReactNativeFeatureFlagsAcc
9595
private var preventShadowTreeCommitExhaustionCache: Boolean? = null
9696
private var shouldPressibilityUseW3CPointerEventsForHoverCache: Boolean? = null
9797
private var shouldSetEnabledBasedOnAccessibilityStateCache: Boolean? = null
98+
private var shouldSetIsClickableByDefaultCache: Boolean? = null
9899
private var shouldTriggerResponderTransferOnScrollAndroidCache: Boolean? = null
99100
private var skipActivityIdentityAssertionOnHostPauseCache: Boolean? = null
100101
private var traceTurboModulePromiseRejectionsOnAndroidCache: Boolean? = null
@@ -824,6 +825,16 @@ internal class ReactNativeFeatureFlagsLocalAccessor : ReactNativeFeatureFlagsAcc
824825
return cached
825826
}
826827

828+
override fun shouldSetIsClickableByDefault(): Boolean {
829+
var cached = shouldSetIsClickableByDefaultCache
830+
if (cached == null) {
831+
cached = currentProvider.shouldSetIsClickableByDefault()
832+
accessedFeatureFlags.add("shouldSetIsClickableByDefault")
833+
shouldSetIsClickableByDefaultCache = cached
834+
}
835+
return cached
836+
}
837+
827838
override fun shouldTriggerResponderTransferOnScrollAndroid(): Boolean {
828839
var cached = shouldTriggerResponderTransferOnScrollAndroidCache
829840
if (cached == null) {

packages/react-native/ReactAndroid/src/main/java/com/facebook/react/internal/featureflags/ReactNativeFeatureFlagsProvider.kt

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -4,7 +4,7 @@
44
* This source code is licensed under the MIT license found in the
55
* LICENSE file in the root directory of this source tree.
66
*
7-
* @generated SignedSource<<48d0d5486793b60914cfd595f0fc78d1>>
7+
* @generated SignedSource<<e7b49105c0cea6be905be90610f0c7e4>>
88
*/
99

1010
/**
@@ -165,6 +165,8 @@ public interface ReactNativeFeatureFlagsProvider {
165165

166166
@DoNotStrip public fun shouldSetEnabledBasedOnAccessibilityState(): Boolean
167167

168+
@DoNotStrip public fun shouldSetIsClickableByDefault(): Boolean
169+
168170
@DoNotStrip public fun shouldTriggerResponderTransferOnScrollAndroid(): Boolean
169171

170172
@DoNotStrip public fun skipActivityIdentityAssertionOnHostPause(): Boolean

packages/react-native/ReactAndroid/src/main/java/com/facebook/react/uimanager/BaseViewManager.java

Lines changed: 23 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -149,6 +149,9 @@ public BaseViewManager(@Nullable ReactApplicationContext reactContext) {
149149
// https://android.googlesource.com/platform/frameworks/base/+/a175a5b/core/java/android/view/View.java#2712
150150
// `mViewFlags = SOUND_EFFECTS_ENABLED | HAPTIC_FEEDBACK_ENABLED | LAYOUT_DIRECTION_INHERIT`
151151
// Therefore we set the following options as such:
152+
if (ReactNativeFeatureFlags.shouldSetIsClickableByDefault()) {
153+
view.setClickable(true);
154+
}
152155
view.setFocusable(false);
153156
view.setFocusableInTouchMode(false);
154157

@@ -678,6 +681,7 @@ protected void updateViewAccessibility(@NonNull T view) {
678681
@Override
679682
protected void onAfterUpdateTransaction(@NonNull T view) {
680683
super.onAfterUpdateTransaction(view);
684+
configureClickableState(view);
681685
updateViewAccessibility(view);
682686

683687
Boolean invalidateTransform = (Boolean) view.getTag(R.id.invalidate_transform);
@@ -1011,6 +1015,25 @@ public void setTouchCancel(@NonNull T view, boolean value) {
10111015

10121016
// Please add new props to BaseViewManagerDelegate as well!
10131017

1018+
private static <T extends View> void configureClickableState(@NonNull T view) {
1019+
if (!ReactNativeFeatureFlags.shouldSetIsClickableByDefault()) {
1020+
return;
1021+
}
1022+
1023+
boolean shouldBeClickable =
1024+
!(view instanceof ReactPointerEventsView)
1025+
|| PointerEvents.canBeTouchTarget(((ReactPointerEventsView) view).getPointerEvents());
1026+
1027+
// NOTE: In Android O+, setClickable(true) has the side effect of setting focusable=true.
1028+
// We need to preserve the original focusable state to respect the focusable prop.
1029+
boolean wasFocusable = view.isFocusable();
1030+
boolean wasFocusableInTouchMode = view.isFocusableInTouchMode();
1031+
1032+
view.setClickable(shouldBeClickable);
1033+
view.setFocusable(wasFocusable);
1034+
view.setFocusableInTouchMode(wasFocusableInTouchMode);
1035+
}
1036+
10141037
/**
10151038
* A helper class to keep track of the original focus change listener if one is set. This is
10161039
* especially helpful for views that are recycled so we can retain and restore the original

packages/react-native/ReactAndroid/src/main/jni/react/featureflags/JReactNativeFeatureFlagsCxxInterop.cpp

Lines changed: 15 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -4,7 +4,7 @@
44
* This source code is licensed under the MIT license found in the
55
* LICENSE file in the root directory of this source tree.
66
*
7-
* @generated SignedSource<<5e4e22e976ce1724191c7b7e381ea5a1>>
7+
* @generated SignedSource<<2f04d4a48caf61c182d412b2ae335e3a>>
88
*/
99

1010
/**
@@ -465,6 +465,12 @@ class ReactNativeFeatureFlagsJavaProvider
465465
return method(javaProvider_);
466466
}
467467

468+
bool shouldSetIsClickableByDefault() override {
469+
static const auto method =
470+
getReactNativeFeatureFlagsProviderJavaClass()->getMethod<jboolean()>("shouldSetIsClickableByDefault");
471+
return method(javaProvider_);
472+
}
473+
468474
bool shouldTriggerResponderTransferOnScrollAndroid() override {
469475
static const auto method =
470476
getReactNativeFeatureFlagsProviderJavaClass()->getMethod<jboolean()>("shouldTriggerResponderTransferOnScrollAndroid");
@@ -932,6 +938,11 @@ bool JReactNativeFeatureFlagsCxxInterop::shouldSetEnabledBasedOnAccessibilitySta
932938
return ReactNativeFeatureFlags::shouldSetEnabledBasedOnAccessibilityState();
933939
}
934940

941+
bool JReactNativeFeatureFlagsCxxInterop::shouldSetIsClickableByDefault(
942+
facebook::jni::alias_ref<JReactNativeFeatureFlagsCxxInterop> /*unused*/) {
943+
return ReactNativeFeatureFlags::shouldSetIsClickableByDefault();
944+
}
945+
935946
bool JReactNativeFeatureFlagsCxxInterop::shouldTriggerResponderTransferOnScrollAndroid(
936947
facebook::jni::alias_ref<JReactNativeFeatureFlagsCxxInterop> /*unused*/) {
937948
return ReactNativeFeatureFlags::shouldTriggerResponderTransferOnScrollAndroid();
@@ -1266,6 +1277,9 @@ void JReactNativeFeatureFlagsCxxInterop::registerNatives() {
12661277
makeNativeMethod(
12671278
"shouldSetEnabledBasedOnAccessibilityState",
12681279
JReactNativeFeatureFlagsCxxInterop::shouldSetEnabledBasedOnAccessibilityState),
1280+
makeNativeMethod(
1281+
"shouldSetIsClickableByDefault",
1282+
JReactNativeFeatureFlagsCxxInterop::shouldSetIsClickableByDefault),
12691283
makeNativeMethod(
12701284
"shouldTriggerResponderTransferOnScrollAndroid",
12711285
JReactNativeFeatureFlagsCxxInterop::shouldTriggerResponderTransferOnScrollAndroid),

packages/react-native/ReactAndroid/src/main/jni/react/featureflags/JReactNativeFeatureFlagsCxxInterop.h

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -4,7 +4,7 @@
44
* This source code is licensed under the MIT license found in the
55
* LICENSE file in the root directory of this source tree.
66
*
7-
* @generated SignedSource<<1cb8ed4a72e9d2b8dd34694485704f60>>
7+
* @generated SignedSource<<7bfc976ab923620e57f62282272dedb9>>
88
*/
99

1010
/**
@@ -243,6 +243,9 @@ class JReactNativeFeatureFlagsCxxInterop
243243
static bool shouldSetEnabledBasedOnAccessibilityState(
244244
facebook::jni::alias_ref<JReactNativeFeatureFlagsCxxInterop>);
245245

246+
static bool shouldSetIsClickableByDefault(
247+
facebook::jni::alias_ref<JReactNativeFeatureFlagsCxxInterop>);
248+
246249
static bool shouldTriggerResponderTransferOnScrollAndroid(
247250
facebook::jni::alias_ref<JReactNativeFeatureFlagsCxxInterop>);
248251

packages/react-native/ReactCommon/react/featureflags/ReactNativeFeatureFlags.cpp

Lines changed: 5 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -4,7 +4,7 @@
44
* This source code is licensed under the MIT license found in the
55
* LICENSE file in the root directory of this source tree.
66
*
7-
* @generated SignedSource<<9c10486574fdff78933aed6acd90533c>>
7+
* @generated SignedSource<<6f9513178d03ce6e69dfc1b0182af336>>
88
*/
99

1010
/**
@@ -310,6 +310,10 @@ bool ReactNativeFeatureFlags::shouldSetEnabledBasedOnAccessibilityState() {
310310
return getAccessor().shouldSetEnabledBasedOnAccessibilityState();
311311
}
312312

313+
bool ReactNativeFeatureFlags::shouldSetIsClickableByDefault() {
314+
return getAccessor().shouldSetIsClickableByDefault();
315+
}
316+
313317
bool ReactNativeFeatureFlags::shouldTriggerResponderTransferOnScrollAndroid() {
314318
return getAccessor().shouldTriggerResponderTransferOnScrollAndroid();
315319
}

0 commit comments

Comments
 (0)