-
Notifications
You must be signed in to change notification settings - Fork 24.4k
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Image does not announce "disabled" #30935
Comments
@kacieb @lunaleaps Image component doesn't have a disabled prop. Edit: I don't think accessibilityState would make sense on image. |
@lunaleaps @blavalla Can you please confirm this ☝️ . My PR is almost ready for this |
@huzaifaaak, just to clarify, the "disabled" field in accessibilityState isn't used to block something from becoming focusable. As you called out, setting accessible={true} (or false) is used for that purpose, and should properly work already. What "disabled" does is let the user know that this element is currently in a disabled state and can't be clicked or interacted with. They can still focus on it however and hear a description of that element. It's most commonly used for form fields that aren't enabled until some requirement has been met, such as filling out another field. While it may be strange to commonly want to make an image "disabled", there are at least some use cases I can think of. For example, those captchas where you select "all the images that contain X". Once you select one, maybe it becomes disabled to make it clear it can no longer be selected. The main issue here is that can currently take the accessibilityState prop, and set |
I have broken this down into two cases that announces the disabled accessibilityState:
I think it would not be a good idea to add it in the BaseViewManager.java (Assumption). I am pausing this. If anyone want's to work on this |
I can not move the focus on the Image CLICK TO OPEN TESTS RESULTS -
|
@fabriziobertoglio1987, do you have a link to the code that rendered that image? It's possible that it had no accessibilityLabel, or was not set as "accessible", so would not get focused by default. |
Thanks a lot @blavalla, I'm trying to troubleshoot this issue and also better understand both I'm taking the new Button functionality as a reference to understand how it should work and debug the native code. CLICK TO OPEN TESTS RESULTS - BUTTON - ACCESSIBILITY DISABLED ANNOUNCED
TEST SCENARIO
RESULT - SUCCESSFUL
<Button
accessibilityState={{disabled: true}}
onPress={() => onButtonPress('submitted')}
testID="accessibilityState_button"
color={theme.LinkColor}
title="Submit Application"
accessibilityLabel="Press to submit your application!"
/> 2021-03-25.12-26-20.mp4CLICK TO OPEN TESTS RESULTS - IMAGE - ACCESSIBILITY DISABLED NOT ANNOUNCED
TEST SCENARIO
RESULT - FAILURE
<Image
accessibilityState={{disabled: true}}
source={fullImage}
style={{height: 300}}
accessibilityLabel="testing"
accessible={true}
/> 2021-03-25.12-26-44.mp4Currently I am investigating and debugging how ReactAndroid generates the string |
The Accessibility info are initially set with the react-native/ReactAndroid/src/main/java/com/facebook/react/uimanager/BaseViewManager.java Lines 163 to 165 in eacc940
The react-native/ReactAndroid/src/main/java/com/facebook/react/uimanager/ViewManager.java Lines 169 to 175 in eacc940
react-native/ReactAndroid/src/main/java/com/facebook/react/uimanager/BaseViewManager.java Lines 410 to 414 in eacc940
react-native/ReactAndroid/src/main/java/com/facebook/react/uimanager/BaseViewManager.java Lines 406 to 408 in eacc940
ReactImageManager enhances this methdo by calling react-native/ReactAndroid/src/main/java/com/facebook/react/views/image/ReactImageManager.java Lines 256 to 260 in eacc940
after this all the Accessibility functionalities are delegated to ReactAccessibilityDelegate react-native/ReactAndroid/src/main/java/com/facebook/react/views/image/ReactImageManager.java Lines 256 to 260 in eacc940
|
@fabriziobertoglio1987 , From an Android standpoint, the thing that matters is whether the "enabled" property of the AccessibilityNodeInfo is set to false. If it is, it will announce "disabled", if it's not, it won't. This property is set in one of two ways: 1.) Setting it on the View directly, which when the view generates the AccessibilityNodeInfo will pull its own value for enabled and populate it to be the same. In React Native, we seem to be doing both of these things, and they seem to potentially conflict. I'm not certain that this conflict is the cause of the issue, but it definitely can't be helping. In BaseViewManager, we are always setting enabled to be "true" here: react-native/ReactAndroid/src/main/java/com/facebook/react/uimanager/BaseViewManager.java Line 171 in eacc940
And in ReactAccessibilityDelegate we are setting it to be the value of the accessibilityState's "disabled" key here: react-native/ReactAndroid/src/main/java/com/facebook/react/uimanager/ReactAccessibilityDelegate.java Line 347 in aad423d
What that means is that if you set I think the more likely culprit here though is the same one that was on the |
Thanks @blavalla, if I comment the react-native/ReactAndroid/src/main/java/com/facebook/react/uimanager/ReactAccessibilityDelegate.java Line 347 in eacc940
The line is executed both with the Image and Button (I debug both). Is it an issue with the ReactImageView?
react-native/ReactAndroid/src/main/java/com/facebook/react/views/image/ReactImageView.java Line 69 in eacc940
The DraweeView inherits from ImageView and View
OPEN TO SEE SOURCECODE View#setEnabled
/**
* Set the enabled state of this view. The interpretation of the enabled state varies by subclass.
*
* @param enabled True if this view is enabled, false otherwise.
*/
@RemotableViewMethod
public void setEnabled(boolean enabled) {
if (enabled == isEnabled()) return;
setFlags(enabled ? ENABLED : DISABLED, ENABLED_MASK);
/*
* The View most likely has to change its appearance, so refresh
* the drawable state.
*/
refreshDrawableState();
// Invalidate too, since the default behavior for views is to be
// be drawn at 50% alpha rather than to change the drawable.
invalidate(true);
if (!enabled) {
cancelPendingInputEvents();
}
} Is this an issue with the ShadowNode (ReactImageManager)?
The definition of ReactShadowNodeImpl
react-native/ReactAndroid/src/main/java/com/facebook/react/uimanager/ReactShadowNodeImpl.java Lines 32 to 54 in eacc940
For example ReactPickerManager over-rides the setEnabled method. react-native/ReactAndroid/src/main/java/com/facebook/react/views/picker/ReactPickerManager.java Lines 47 to 50 in eacc940
Unluckily I don't have a solution for this problem right now, but I plan to work on other related issues and gain a better understanding of the ReactAndroid and Android relevant sourcecode. Thanks :🙏 ☮ |
CLICK TO OPEN TEST CASE
2021-03-26.18-27-36.mp4react-native/ReactAndroid/src/main/java/com/facebook/react/views/view/ReactViewManager.java Lines 36 to 38 in eacc940
react-native/ReactAndroid/src/main/java/com/facebook/react/views/view/ReactViewManager.java Lines 55 to 58 in eacc940
adding definition of ShadowNodes in React Native
react-native/ReactAndroid/src/main/java/com/facebook/react/uimanager/ReactShadowNodeImpl.java Lines 32 to 54 in eacc940
Information available in Android Open Source View Class on Focus
* <p>The framework will handle routine focus movement in response to user input. This includes
* changing the focus as views are removed or hidden, or as new views become available. Views
* indicate their willingness to take focus through the {@link #isFocusable} method. To change
* whether a view can take focus, call {@link #setFocusable(boolean)}. When in touch mode (see notes
* below) views indicate whether they still would like focus via {@link #isFocusableInTouchMode} and
* can change this via {@link #setFocusableInTouchMode(boolean)}.
*
* <p>Focus movement is based on an algorithm which finds the nearest neighbor in a given direction.
* In rare cases, the default algorithm may not match the intended behavior of the developer. In
* these situations, you can provide explicit overrides by using these XML attributes in the layout
* file:
*
* <pre>
* nextFocusDown
* nextFocusLeft
* nextFocusRight
* nextFocusUp
* </pre> /**
* Initializes an {@link AccessibilityNodeInfo} with information about this view. The base
* implementation sets:
*
* <ul>
* <li>{@link AccessibilityNodeInfo#setParent(View)},
* <li>{@link AccessibilityNodeInfo#setBoundsInParent(Rect)},
* <li>{@link AccessibilityNodeInfo#setBoundsInScreen(Rect)},
* <li>{@link AccessibilityNodeInfo#setPackageName(CharSequence)},
* <li>{@link AccessibilityNodeInfo#setClassName(CharSequence)},
* <li>{@link AccessibilityNodeInfo#setContentDescription(CharSequence)},
* <li>{@link AccessibilityNodeInfo#setEnabled(boolean)},
* <li>{@link AccessibilityNodeInfo#setClickable(boolean)},
* <li>{@link AccessibilityNodeInfo#setFocusable(boolean)},
* <li>{@link AccessibilityNodeInfo#setFocused(boolean)},
* <li>{@link AccessibilityNodeInfo#setLongClickable(boolean)},
* <li>{@link AccessibilityNodeInfo#setSelected(boolean)},
* <li>{@link AccessibilityNodeInfo#setContextClickable(boolean)}
* </ul>
*
* <p>Subclasses should override this method, call the super implementation, and set additional
* attributes.
*
* <p>If an {@link AccessibilityDelegate} has been specified via calling {@link
* #setAccessibilityDelegate(AccessibilityDelegate)} its {@link
* AccessibilityDelegate#onInitializeAccessibilityNodeInfo(View, AccessibilityNodeInfo)} is
* responsible for handling this call.
*
* @param info The instance to initialize.
*/
@CallSuper
public void onInitializeAccessibilityNodeInfo(AccessibilityNodeInfo info) {
if (mAccessibilityDelegate != null) {
mAccessibilityDelegate.onInitializeAccessibilityNodeInfo(this, info);
} else {
onInitializeAccessibilityNodeInfoInternal(info);
}
}
/**
* Set whether this view can receive the focus.
*
* <p>Setting this to false will also ensure that this view is not focusable in touch mode.
*
* @param focusable If true, this view can receive the focus.
* @see #setFocusableInTouchMode(boolean)
* @see #setFocusable(int)
* @attr ref android.R.styleable#View_focusable
*/
public void setFocusable(boolean focusable) {
setFocusable(focusable ? FOCUSABLE : NOT_FOCUSABLE);
}
/**
* Sets whether this view can receive focus.
*
* <p>Setting this to {@link #FOCUSABLE_AUTO} tells the framework to determine focusability
* automatically based on the view's interactivity. This is the default.
*
* <p>Setting this to NOT_FOCUSABLE will ensure that this view is also not focusable in touch
* mode.
*
* @param focusable One of {@link #NOT_FOCUSABLE}, {@link #FOCUSABLE}, or {@link #FOCUSABLE_AUTO}.
* @see #setFocusableInTouchMode(boolean)
* @attr ref android.R.styleable#View_focusable
*/
public void setFocusable(@Focusable int focusable) {
if ((focusable & (FOCUSABLE_AUTO | FOCUSABLE)) == 0) {
setFlags(0, FOCUSABLE_IN_TOUCH_MODE);
}
setFlags(focusable, FOCUSABLE_MASK);
} I attach a DRAFT pr to keep track of my progress. Next week I will investigate:
Thanks a lot. :🙏 ☮ |
Adding the prop `accessible` to `ReactTextAnchorViewManager` fixes the problem for this component. The same solution from my previous pr facebook#30935 (comment). See test case at fabOnReact/react-native-notes#1 (comment)
Adding the prop `accessible` to `ReactTextAnchorViewManager` fixes the problem for this component. The same solution from my previous pr facebook#30935 (comment). See test case at fabOnReact/react-native-notes#1 (comment) 666647e
Description
When using a screen reader this component does not announce that it is disabled.
This issue is covered in-depth by parent issue #30840, please check there for more details.
React Native version:
v0.63
The text was updated successfully, but these errors were encountered: