Skip to content

Commit

Permalink
1/2 TextInput accessibilityErrorMessage (Talkback, Android) (#33468)
Browse files Browse the repository at this point in the history
Summary:
**Android**: The functionality consists of calling the [AccessibilityNodeInfo#setError][10] and [#setContentInvalid][13] method to display the error message in the TextInput.

**Fixes [https://github.com/facebook/react-native/issues/30848][51] - Adding an accessibilityErrorMessage prop to the TextInput Component**:
**Android**: The prop accessibilityErrorMessage triggers the AccessibilityNodeInfo method [setError][10] which automatically sets the correct properties on the AccessibilityNodeInfo that will inform screen readers of this state. The method calls setContentInvalid(true) and setError(youErrorString) on the AccessibilityNodeInfo.

**Fixes [https://github.com/facebook/react-native/issues/30859][52] -  Detecting changes in the Error state (text inputs)**
**Fabric - Android** - Adding accessibilityErrorMessage to field AndroidTextInputState.
ReactTextInputManager and ReactEditText receive state updates both from [Javascript][32] and [cpp (fabric)][34].
- accessibilityErrorMessage is added to the fabric AndroidTextInputState field
- The updates are received in the ReactAndroid API with method updateState from ReactTextInputManager
- After updating the TextInput text with onChangeText, the update of the accessibilityErrorMessage is triggered with method maybeSetAccessibilityError which triggers [setError][10].

More info:
- An explanation of [state updates between fabric and ReactAndroid for the TextInput component][34]
- [ReactNative renderer state updates][35]

**Paper - Android** - Adding accessibilityErrorMessage to ReactTextInputShadowNode to trigger updates in Paper renderer when accessibilityErrorMessage is changed within the onChange callback.

Related Links (Android):
- [In this diff I'm shipping and deleting mapBufferSerialization for Text measurement][101]
- [This diff implement and integrates Mapbuffer into Fabric text measure system][39]
- [Refactor ViewPropsMapBuffer -> general MapBuffer props mechanism][100]
- [TextInput: support modifying TextInputs with multiple Fragments (Cxx side)][24]
- [TextInput: keep C++ state in-sync with updated AttributedStrings in Java][23]
- [AccessibilityNodeInfo#setError][11]
- [Explanation on how TextInput calls SET_TEXT_AND_SELECTION in Java API][32]
- [Fabric: convertRawProp was extended to accept an optional default value][27]
- [understanding onChangeText callback][31]
- [Editable method replace()][12]
- [Change of error state from onChangeText show/hides a TextInput error][30]
- [AndroidTextInput: support using commands instead of setNativeProps (native change)][25]
- [TextInput: support editing completely empty TextInputs][26]
- [[Android] Fix letters duplication when using autoCapitalize https://github.com/facebook/react-native/issues/29070][40]
- [Support optional types for C++ TurboModules][28]
- [discussion on using announceForAccessibility in ReactEditText][36]
- [ fix annoucement delayed to next character][61]
- [Announce accessibility state changes happening in the background][29]
- [Refactor MountingManager into MountingManager + SurfaceMountingManager][37]

iOS Functionalities are included in separate PR #35908
Documentation PR facebook/react-native-website#3010

Next PR [2/2 TextInput accessibilityErrorMessage (VoiceOver, iOS) https://github.com/facebook/react-native/issues/35908](https://github.com/facebook/react-native/pull/35908)
Related facebook/react-native-deprecated-modules#18

## Changelog

[Android] [Added] - Adding TextInput prop accessibilityErrorMessage to announce with TalkBack screenreaders

Pull Request resolved: #33468

Test Plan:
**Android - 20 Jan 2023**
#33468 (comment)

**iOS - 20 Jan 2023**
#33468 (comment)

<details><summary>CLICK TO OPEN OLD VIDEO TEST CASES</summary>
<p>

**PR Branch - Android and iOS 24th June**
[88]: Android - accessibilityValue announces correctly with/out errorMessage set with onChangeText or with outside event (Fabric) ([link][88])

**PR Branch - Android**
[1]. Test Cases of the functionality (Fabric) ([link][1])
[2]. Test Cases of the functionality (Paper) ([link][2])

**Main Branch**
[6]. Android - Runtime Error in main branch when passing value of 1 to TextInput  placeholder prop ([link][6])

**Issues Solved**
[7]. TalkBack error does not clear error on the next typed character when using onChangeText ([link][7])
**Other Tests**
[8]. Setting the TextInput errorMessage state with setTextAndSelection Java API from JavaScript ([link][8])
[9]. Setting the TextInput errorMessage state from fabric TextInput internal state to Java ReactTextUpdate API ([link][9])

</p>
</details>

[1]: fabOnReact/react-native-notes#12 (comment) "Test Cases of the functionality (Android - Fabric)"
[2]: fabOnReact/react-native-notes#12 (comment) "Test Cases of the functionality (Android - Paper)"
[3]: fabOnReact/react-native-notes#12 (comment) "Test Cases of the functionality (iOS - Fabric)"
[6]: fabOnReact/react-native-notes#12 (comment) "Runtime Error in main branch when passing value of 1 to TextInput  placeholder prop"
[7]: fabOnReact/react-native-notes#12 (comment) "TalkBack error announcement done on next typed character with onChangeText"
[8]: fabOnReact/react-native-notes#12 (comment) "setting the TextInput errorMessage state with setTextAndSelection Java API from JavaScript"
[9]: fabOnReact/react-native-notes#12 (comment) "Setting the TextInput errorMessage state from fabric TextInput internal state to Java ReactTextUpdate API"

[10]: https://developer.android.com/reference/android/view/accessibility/AccessibilityNodeInfo#setError(java.lang.CharSequence) "AOSP setError"
[11]: https://developer.android.com/reference/android/view/accessibility/AccessibilityNodeInfo#setError(java.lang.CharSequence) "AccessibilityNodeInfo#setError"
[12]: https://github.com/aosp-mirror/platform_frameworks_base/blob/1ac46f932ef88a8f96d652580d8105e361ffc842/core/java/android/text/Editable.java#L28-L52 "Editable method replace"
[13]: https://developer.android.com/reference/android/view/accessibility/AccessibilityNodeInfo#setContentInvalid(boolean) "setContentInvalid"

[20]: 60b6c9b "draft implementation of android_errorMessage "
[21]: 012d92d "add errorMessage to ReactTextUpdate and maybeSetAccessibilityError"
[22]: fabOnReact@cad239b "rename android_errorMessage to errorMessageAndroid"
[23]: fabOnReact@0bae474 "TextInput: keep C++ state in-sync with updated AttributedStrings in Java"
[24]: fabOnReact@0556e86 "TextInput: support modifying TextInputs with multiple Fragments (Cxx side)"
[25]: fabOnReact@7ab5eb4 "AndroidTextInput: support using commands instead of setNativeProps (native change)"
[26]: fabOnReact@b9491b7 "TextInput: support editing completely empty TextInputs"
[27]: fabOnReact@7f1ed68 "Fabric: convertRawProp was extended to accept an optional default value"
[28]: 6e0fa5f "Support optional types for C++ TurboModules"
[29]: fabOnReact@baa66f6 "Announce accessibility state changes happening in the background"

[30]: fabOnReact/react-native-notes#12 (comment) "Change of error state from onChangeText show/hides a TextInput error"
[31]: fabOnReact/react-native-notes#12 (comment) "understanding onChangeText callback"
[32]: #29063 (comment) "Explanation on how TextInput calls SET_TEXT_AND_SELECTION in Java API"
[33]: #33468 (comment) "Explanation of TextInput state management with fabric C++ and JAVA API"
[34]: #33468 (comment) "state updates between fabric and ReactAndroid for the TextInput component"
[35]: https://reactnative.dev/architecture/render-pipeline#react-native-renderer-state-updates "ReactNative renderer state updates"
[35]: fabOnReact/react-native-notes#12 (comment) "Analysis on how AndroidTextInputState.cpp sends updates to ReactTextInputManager"
[36]: #33468 (comment) "discussion on using announceForAccessibility in ReactEditText"
[37]: fabOnReact@29eb632 "Refactor MountingManager into MountingManager + SurfaceMountingManager"
[38]: fabOnReact@733f228 "Diff C++ props for Android consumption"
[39]: fabOnReact@91b3f5d "This diff implement and integrates Mapbuffer into Fabric text measure system"

[40]: #29070 "[Android] Fix letters duplication when using autoCapitalize #29070"

[50]: fabOnReact/react-native-notes#12  "Notes from work on iOS/Android: Text input error for screenreaders #12"
[51]: #30848 "iOS/Android: Text input error for screenreaders #30848"
[52]: #30859 "Android: Error state change (text inputs) #30859"

[61]: eb33c93 "fix annoucement delayed to next character"

[70]: fabOnReact/react-native-notes#12 (comment) "iOS - Paper renderer does not update the accessibilityValue"
[71]: fabOnReact/react-native-notes#12 (comment) "Test Cases of the functionality (Fabric) after removing changes to .cpp libs"
[72]: fabOnReact/react-native-notes#12 (comment) "Test Cases of the functionality (Paper) after removing changes to .cpp libs"
[73]: fabOnReact/react-native-notes#12 (comment) "iOS - announcing error onChangeText and screenreader focus"
[74]: fabOnReact/react-native-notes#12 (comment) "iOS - The screenreader announces the TextInput value after the errorMessage is cleared"
[75]: fabOnReact/react-native-notes#12 (comment) "iOS - Exception thrown while executing UI block: - [RCTTextView setOnAccessibiltyAction:]: unrecognized selector sent to instance (Paper) (main branch)"
[76]: #30859 (comment) "iOS - announce lastChar (not entire text) onChangeText and avoid multiple announcements (Fabric)"
[77]: #30859 (comment) "iOS - announces or does not announce the accessibilityError through Button onPress (not onChangeText) (Fabric)"
[78]: #30859 (comment) "iOS - the error is announced with accessibilityInvalid true and does not clear after typing text (onChangeText) (Fabric)"
[79]: #30848 (comment) "iOS - Exception thrown while executing UI block: - RCTUITextView setAccessibilityErrorMessage:]: unrecognized selector sent to instance (iOS - Paper on main branch)"

[80]: fabOnReact@e13b9c6 "RCTTextField was spliited into two classes"
[81]: fabOnReact@ee9697e "Introducing RCTBackedTextInputDelegate"
[82]: fabOnReact@2dd2529 "Add option to hide context menu for TextInput"
[83]: https://github.com/fabriziobertoglio1987/react-native/blob/343eea1e3150cf54d6f7727cd01d13eb7247c7f7/React/Fabric/Mounting/ComponentViews/Text/RCTParagraphComponentAccessibilityProvider.mm#L48-L72 "RCTParagraphComponentAccessibilityProvider accessibilityElements"
[84]: https://github.com/fabriziobertoglio1987/react-native/blob/c8790a114f6f21774c43f0e9b9210e7b35d1c243/React/Fabric/Mounting/ComponentViews/TextInput/RCTTextInputComponentView.mm#L613 "RCTTextInputComponentView method _setAttributedString"
[85]: https://github.com/fabriziobertoglio1987/react-native/blob/c8790a114f6f21774c43f0e9b9210e7b35d1c243/React/Fabric/Mounting/ComponentViews/TextInput/RCTTextInputComponentView.mm#L146 "RCTTextInputComponentView method updateProps"
[86]: https://github.com/fabriziobertoglio1987/react-native/blob/c8790a114f6f21774c43f0e9b9210e7b35d1c243/Libraries/Text/TextInput/RCTBaseTextInputView.m#L150 "RCTBaseTextInputView setAttributedText"
[87]: #30859 (comment) "iOS - accessibilityValue announces correctly with/out errorMessage set with onChangeText or with outside event"
[88]: #30859 (comment) "Android - accessibilityValue announces correctly with/out errorMessage set with onChangeText or with outside event"
[89]: #30859 (comment) "iOS - accessibilityValue announces correctly with/out errorMessage set with onChangeText or with outside event (Fabric)"

[100]: fabOnReact@110b191 "Refactor ViewPropsMapBuffer -> general MapBuffer props mechanism"
[101]: fabOnReact@22b6e1c "In this diff I'm shipping and deleting mapBufferSerialization for Text measurement"

Reviewed By: blavalla

Differential Revision: D38410635

Pulled By: lunaleaps

fbshipit-source-id: cd80e9a1be8f5ca017c979d7907974cf72ca4777
  • Loading branch information
fabOnReact authored and facebook-github-bot committed Mar 1, 2023
1 parent ed39d63 commit cbe934b
Show file tree
Hide file tree
Showing 16 changed files with 280 additions and 10 deletions.
10 changes: 10 additions & 0 deletions Libraries/Components/TextInput/AndroidTextInputNativeComponent.js
Original file line number Diff line number Diff line change
Expand Up @@ -166,6 +166,14 @@ export type NativeProps = $ReadOnly<{|
'off',
>,

/**
* String to be read by screenreaders to indicate an error state. The acceptable parameters
* of accessibilityErrorMessage is a string. Setting accessibilityInvalid to true activates
* the error message. Setting accessibilityInvalid to false removes the error message.
*/
accessibilityErrorMessage?: ?Stringish,
accessibilityInvalid?: ?boolean,

/**
* Sets the return key to the label. Use it instead of `returnKeyType`.
* @platform android
Expand Down Expand Up @@ -730,6 +738,8 @@ export const __INTERNAL_VIEW_CONFIG: PartialViewConfig = {
inlineImageLeft: true,
editable: true,
fontVariant: true,
accessibilityErrorMessage: true,
accessibilityInvalid: true,
borderBottomRightRadius: true,
borderBottomColor: {
process: require('../../StyleSheet/processColor').default,
Expand Down
8 changes: 8 additions & 0 deletions Libraries/Components/TextInput/TextInput.d.ts
Original file line number Diff line number Diff line change
Expand Up @@ -531,6 +531,14 @@ export interface TextInputProps
TextInputIOSProps,
TextInputAndroidProps,
AccessibilityProps {
/**
* String to be read by screenreaders to indicate an error state. The acceptable parameters
* of accessibilityErrorMessage is a string. Setting accessibilityInvalid to true activates
* the error message. Setting accessibilityInvalid to false removes the error message.
*/
accessibilityErrorMessage?: string | undefined;
accessibilityInvalid?: boolean | undefined;

/**
* Specifies whether fonts should scale to respect Text Size accessibility settings.
* The default is `true`.
Expand Down
8 changes: 8 additions & 0 deletions Libraries/Components/TextInput/TextInput.flow.js
Original file line number Diff line number Diff line change
Expand Up @@ -523,6 +523,14 @@ export type Props = $ReadOnly<{|
...IOSProps,
...AndroidProps,

/**
* String to be read by screenreaders to indicate an error state. The acceptable parameters
* of accessibilityErrorMessage is a string. Setting accessibilityInvalid to true activates
* the error message. Setting accessibilityInvalid to false removes the error message.
*/
accessibilityErrorMessage?: ?Stringish,
accessibilityInvalid?: ?boolean,

/**
* Can tell `TextInput` to automatically capitalize certain characters.
*
Expand Down
16 changes: 16 additions & 0 deletions Libraries/Components/TextInput/TextInput.js
Original file line number Diff line number Diff line change
Expand Up @@ -561,6 +561,14 @@ export type Props = $ReadOnly<{|
...IOSProps,
...AndroidProps,

/**
* String to be read by screenreaders to indicate an error state. The acceptable parameters
* of accessibilityErrorMessage is a string. Setting accessibilityInvalid to true activates
* the error message. Setting accessibilityInvalid to false removes the error message.
*/
accessibilityErrorMessage?: ?Stringish,
accessibilityInvalid?: ?boolean,

/**
* Can tell `TextInput` to automatically capitalize certain characters.
*
Expand Down Expand Up @@ -1365,6 +1373,12 @@ function InternalTextInput(props: Props): React.Node {
}

const accessible = props.accessible !== false;

const accessibilityErrorMessage =
props.accessibilityInvalid === true
? props.accessibilityErrorMessage
: null;

const focusable = props.focusable !== false;

const config = React.useMemo(
Expand Down Expand Up @@ -1439,6 +1453,7 @@ function InternalTextInput(props: Props): React.Node {
ref={ref}
{...otherProps}
{...eventHandlers}
accessibilityErrorMessage={accessibilityErrorMessage}
accessibilityState={_accessibilityState}
accessible={accessible}
submitBehavior={submitBehavior}
Expand Down Expand Up @@ -1490,6 +1505,7 @@ function InternalTextInput(props: Props): React.Node {
ref={ref}
{...otherProps}
{...eventHandlers}
accessibilityErrorMessage={accessibilityErrorMessage}
accessibilityState={_accessibilityState}
accessibilityLabelledBy={_accessibilityLabelledBy}
accessible={accessible}
Expand Down
4 changes: 4 additions & 0 deletions Libraries/Components/TextInput/__tests__/TextInput-test.js
Original file line number Diff line number Diff line change
Expand Up @@ -186,6 +186,7 @@ describe('TextInput', () => {

expect(instance.toJSON()).toMatchInlineSnapshot(`
<RCTSinglelineTextInputView
accessibilityErrorMessage={null}
accessible={true}
allowFontScaling={true}
focusable={true}
Expand Down Expand Up @@ -231,6 +232,7 @@ describe('TextInput compat with web', () => {

expect(instance.toJSON()).toMatchInlineSnapshot(`
<RCTSinglelineTextInputView
accessibilityErrorMessage={null}
accessible={true}
allowFontScaling={true}
focusable={true}
Expand Down Expand Up @@ -315,6 +317,7 @@ describe('TextInput compat with web', () => {

expect(instance.toJSON()).toMatchInlineSnapshot(`
<RCTSinglelineTextInputView
accessibilityErrorMessage={null}
accessibilityState={
Object {
"busy": true,
Expand Down Expand Up @@ -407,6 +410,7 @@ describe('TextInput compat with web', () => {

expect(instance.toJSON()).toMatchInlineSnapshot(`
<RCTSinglelineTextInputView
accessibilityErrorMessage={null}
accessible={true}
allowFontScaling={true}
focusable={true}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@

exports[`TextInput tests should render as expected: should deep render when mocked (please verify output manually) 1`] = `
<RCTSinglelineTextInputView
accessibilityErrorMessage={null}
accessible={true}
allowFontScaling={true}
focusable={true}
Expand Down Expand Up @@ -31,6 +32,7 @@ exports[`TextInput tests should render as expected: should deep render when mock

exports[`TextInput tests should render as expected: should deep render when not mocked (please verify output manually) 1`] = `
<RCTSinglelineTextInputView
accessibilityErrorMessage={null}
accessible={true}
allowFontScaling={true}
focusable={true}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -251,6 +251,11 @@ public void setAccessibilityRole(@NonNull T view, @Nullable String accessibility
view.setTag(R.id.accessibility_role, AccessibilityRole.fromValue(accessibilityRole));
}

@ReactProp(name = "accessibilityErrorMessage")
public void setScreenreaderError(@NonNull T view, @Nullable String accessibilityErrorMessage) {
view.setTag(R.id.accessibility_error_message, accessibilityErrorMessage);
}

@Override
@ReactProp(name = ViewProps.ACCESSIBILITY_COLLECTION)
public void setAccessibilityCollection(
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,7 @@

import android.text.Layout;
import android.text.Spannable;
import androidx.annotation.Nullable;

/**
* Class that contains the data needed for a text update. Used by both <Text/> and <TextInput/>
Expand All @@ -30,6 +31,7 @@ public class ReactTextUpdate {
private final int mSelectionStart;
private final int mSelectionEnd;
private final int mJustificationMode;
private @Nullable String mAccessibilityErrorMessage;

public boolean mContainsMultipleFragments;

Expand Down Expand Up @@ -59,7 +61,8 @@ public ReactTextUpdate(
Layout.BREAK_STRATEGY_HIGH_QUALITY,
Layout.JUSTIFICATION_MODE_NONE,
-1,
-1);
-1,
null);
}

public ReactTextUpdate(
Expand All @@ -85,7 +88,8 @@ public ReactTextUpdate(
textBreakStrategy,
justificationMode,
-1,
-1);
-1,
null);
}

public ReactTextUpdate(
Expand All @@ -107,7 +111,8 @@ public ReactTextUpdate(
textBreakStrategy,
justificationMode,
-1,
-1);
-1,
null);
}

public ReactTextUpdate(
Expand Down Expand Up @@ -137,21 +142,56 @@ public ReactTextUpdate(
mJustificationMode = justificationMode;
}

public ReactTextUpdate(
Spannable text,
int jsEventCounter,
boolean containsImages,
float paddingStart,
float paddingTop,
float paddingEnd,
float paddingBottom,
int textAlign,
int textBreakStrategy,
int justificationMode,
int selectionStart,
int selectionEnd,
@Nullable String accessibilityErrorMessage) {
mText = text;
mJsEventCounter = jsEventCounter;
mContainsImages = containsImages;
mPaddingLeft = paddingStart;
mPaddingTop = paddingTop;
mPaddingRight = paddingEnd;
mPaddingBottom = paddingBottom;
mTextAlign = textAlign;
mTextBreakStrategy = textBreakStrategy;
mSelectionStart = selectionStart;
mSelectionEnd = selectionEnd;
mJustificationMode = justificationMode;
mAccessibilityErrorMessage = accessibilityErrorMessage;
}

public static ReactTextUpdate buildReactTextUpdateFromState(
Spannable text,
int jsEventCounter,
int textAlign,
int textBreakStrategy,
int justificationMode,
boolean containsMultipleFragments) {
boolean containsMultipleFragments,
@Nullable String accessibilityErrorMessage) {

ReactTextUpdate reactTextUpdate =
new ReactTextUpdate(
text, jsEventCounter, false, textAlign, textBreakStrategy, justificationMode);
reactTextUpdate.mContainsMultipleFragments = containsMultipleFragments;
reactTextUpdate.mAccessibilityErrorMessage = accessibilityErrorMessage;
return reactTextUpdate;
}

public @Nullable String getScreenreaderError() {
return mAccessibilityErrorMessage;
}

public Spannable getText() {
return mText;
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -34,6 +34,7 @@ rn_android_library(
react_native_target("java/com/facebook/react/common/mapbuffer:mapbuffer"),
react_native_target("java/com/facebook/react/views/view:view"),
react_native_target("java/com/facebook/react/config:config"),
react_native_target("res:uimanager"),
] + KOTLIN_STDLIB_DEPS,
exported_deps = [
react_native_dep("third-party/android/androidx:appcompat"),
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -30,15 +30,18 @@
import android.view.KeyEvent;
import android.view.MotionEvent;
import android.view.View;
import android.view.accessibility.AccessibilityEvent;
import android.view.accessibility.AccessibilityNodeInfo;
import android.view.inputmethod.EditorInfo;
import android.view.inputmethod.InputConnection;
import android.view.inputmethod.InputMethodManager;
import androidx.annotation.Nullable;
import androidx.appcompat.widget.AppCompatEditText;
import androidx.core.view.ViewCompat;
import androidx.core.view.accessibility.AccessibilityNodeInfoCompat;
import com.facebook.common.logging.FLog;
import com.facebook.infer.annotation.Assertions;
import com.facebook.react.R;
import com.facebook.react.bridge.ReactContext;
import com.facebook.react.bridge.ReactSoftExceptionLogger;
import com.facebook.react.common.build.ReactBuildConfig;
Expand Down Expand Up @@ -157,6 +160,36 @@ public ReactEditText(Context context) {
ReactAccessibilityDelegate editTextAccessibilityDelegate =
new ReactAccessibilityDelegate(
this, this.isFocusable(), this.getImportantForAccessibility()) {
@Override
public void onInitializeAccessibilityNodeInfo(
View host, AccessibilityNodeInfoCompat info) {
super.onInitializeAccessibilityNodeInfo(host, info);
final String accessibilityErrorMessage =
(String) host.getTag(R.id.accessibility_error_message);
boolean contentInvalid = accessibilityErrorMessage == null ? false : true;
if (accessibilityErrorMessage != info.getError()) {
info.setError(accessibilityErrorMessage);
info.setContentInvalid(contentInvalid);
}
}

@Override
public void onInitializeAccessibilityEvent(View host, AccessibilityEvent event) {
super.onInitializeAccessibilityEvent(host, event);
if (event.getEventType() == AccessibilityEvent.TYPE_VIEW_TEXT_CHANGED
&& host.getParent() != null) {
try {
host.getParent().requestSendAccessibilityEvent(host, event);
} catch (AbstractMethodError e) {
FLog.w(
TAG,
host.getParent().getClass().getSimpleName()
+ " does not fully implement ViewParent",
e);
}
}
}

@Override
public boolean performAccessibilityAction(View host, int action, Bundle args) {
if (action == AccessibilityNodeInfo.ACTION_CLICK) {
Expand Down Expand Up @@ -539,6 +572,25 @@ public int incrementAndGetEventCounter() {
return ++mNativeEventCount;
}

/**
* Attempt to set an accessibility error or fail silently. EventCounter is the same one used as
* with text.
*
* @param eventCounter
* @param accessibilityErrorMessage
*/
public void maybeSetAccessibilityError(
int eventCounter, @Nullable String accessibilityErrorMessage) {
String previousScreenreaderError = (String) getTag(R.id.accessibility_error_message);
if (!canUpdateWithEventCount(eventCounter)
|| previousScreenreaderError == accessibilityErrorMessage) {
return;
}

setTag(R.id.accessibility_error_message, accessibilityErrorMessage);
sendAccessibilityEvent(AccessibilityEvent.TYPE_WINDOW_CONTENT_CHANGED);
}

public void maybeSetTextFromJS(ReactTextUpdate reactTextUpdate) {
mIsSettingTextFromJS = true;
maybeSetText(reactTextUpdate);
Expand Down
Loading

0 comments on commit cbe934b

Please sign in to comment.