Skip to content

Commit c7aa6dc

Browse files
Marc Mulcahyfacebook-github-bot
Marc Mulcahy
authored andcommitted
Add onSlidingComplete callbacks when sliders adjusted via a11y (#26600)
Summary: When sliders are adjusted via accessibility, no onSlidingComplete callback is generated. This causes problems for components which perform behavior in this callback, and means that such components don't behave properly when adjusted via accessibility. For example, if an app hosting a volume control slider only commits the volume change to the hardware on onSlidingComplete, it is impossible for a screen reader user to ever actually adjust the volume. Ensure that sliders call the onSlidingComplete callback after adjusted via accessibility. ## Changelog [General] [Fix] - Add onSlidingComplete callbacks when sliders adjusted via a11y. [CATEGORY] [TYPE] - Message Pull Request resolved: #26600 Test Plan: Prior to this change, using the RNTester slider example with a screen reader, the onSlidingComplete callback tests never shows any callbacks when the slider is adjusted. With this change applied, the callback test will show a number of callbacks corresponding to the number of times the slider was adjusted via the screen reader. Differential Revision: D17661157 Pulled By: cpojer fbshipit-source-id: a6eedef099c6c1b571b290c329059ac9b69b53dd
1 parent 113c4e2 commit c7aa6dc

File tree

2 files changed

+50
-1
lines changed

2 files changed

+50
-1
lines changed

React/Views/RCTSlider.m

+20
Original file line numberDiff line numberDiff line change
@@ -84,4 +84,24 @@ - (UIImage *)thumbImage
8484
return [self thumbImageForState:UIControlStateNormal];
8585
}
8686

87+
- (void)accessibilityIncrement
88+
{
89+
[super accessibilityIncrement];
90+
if (_onSlidingComplete) {
91+
_onSlidingComplete(@{
92+
@"value": @(self.value),
93+
});
94+
}
95+
}
96+
97+
- (void)accessibilityDecrement
98+
{
99+
[super accessibilityDecrement];
100+
if (_onSlidingComplete) {
101+
_onSlidingComplete(@{
102+
@"value": @(self.value),
103+
});
104+
}
105+
}
106+
87107
@end

ReactAndroid/src/main/java/com/facebook/react/views/slider/ReactSliderManager.java

+30-1
Original file line numberDiff line numberDiff line change
@@ -10,10 +10,14 @@
1010
import android.graphics.PorterDuff;
1111
import android.graphics.drawable.Drawable;
1212
import android.graphics.drawable.LayerDrawable;
13+
import android.os.Bundle;
1314
import android.view.View;
1415
import android.view.ViewGroup;
1516
import android.widget.SeekBar;
1617
import androidx.annotation.Nullable;
18+
import androidx.core.view.AccessibilityDelegateCompat;
19+
import androidx.core.view.ViewCompat;
20+
import androidx.core.view.accessibility.AccessibilityNodeInfoCompat.AccessibilityActionCompat;
1721
import com.facebook.react.bridge.ReactContext;
1822
import com.facebook.react.bridge.ReadableMap;
1923
import com.facebook.react.common.MapBuilder;
@@ -132,7 +136,9 @@ public Class getShadowNodeClass() {
132136

133137
@Override
134138
protected ReactSlider createViewInstance(ThemedReactContext context) {
135-
return new ReactSlider(context, null, STYLE);
139+
final ReactSlider slider = new ReactSlider(context, null, STYLE);
140+
ViewCompat.setAccessibilityDelegate(slider, sAccessibilityDelegate);
141+
return slider;
136142
}
137143

138144
@Override
@@ -256,4 +262,27 @@ public long measure(
256262
protected ViewManagerDelegate<ReactSlider> getDelegate() {
257263
return mDelegate;
258264
}
265+
266+
protected static class ReactSliderAccessibilityDelegate extends AccessibilityDelegateCompat {
267+
private static boolean isSliderAction(int action) {
268+
return (action == AccessibilityActionCompat.ACTION_SCROLL_FORWARD.getId())
269+
|| (action == AccessibilityActionCompat.ACTION_SCROLL_BACKWARD.getId())
270+
|| (action == AccessibilityActionCompat.ACTION_SET_PROGRESS.getId());
271+
}
272+
273+
@Override
274+
public boolean performAccessibilityAction(View host, int action, Bundle args) {
275+
if (isSliderAction(action)) {
276+
ON_CHANGE_LISTENER.onStartTrackingTouch((SeekBar) host);
277+
}
278+
final boolean rv = super.performAccessibilityAction(host, action, args);
279+
if (isSliderAction(action)) {
280+
ON_CHANGE_LISTENER.onStopTrackingTouch((SeekBar) host);
281+
}
282+
return rv;
283+
}
284+
};
285+
286+
protected static ReactSliderAccessibilityDelegate sAccessibilityDelegate =
287+
new ReactSliderAccessibilityDelegate();
259288
}

0 commit comments

Comments
 (0)