Skip to content

[Android] Selected State does not annonce when TextInput Component selected #31144

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

Closed

Conversation

fabOnReact
Copy link
Contributor

@fabOnReact fabOnReact commented Mar 12, 2021

Summary

This issue fixes #30955 and is a follow up to pr #24608 which added the basic Accessibility functionalities to React Native.

TextInput should announce "selected" to the user when screenreader focused.
The focus is moved to the TextInput by navigating with the screenreader to the TextInput.

This PR adds call to View#setSelected in BaseViewManager https://developer.android.com/reference/android/view/View#setSelected(boolean)
The View#setSelected method definition https://github.com/aosp-mirror/platform_frameworks_base/blob/master/core/java/android/view/View.java

/**
 * Changes the selection state of this view. A view can be selected or not.
 * Note that selection is not the same as focus. Views are typically
 * selected in the context of an AdapterView like ListView or GridView;
 * the selected view is the view that is highlighted.
 *
 * @param selected true if the view must be selected, false otherwise
 */
public void setSelected(boolean selected) {
  if (((mPrivateFlags & PFLAG_SELECTED) != 0) != selected) {
    // ... hidden logic
    if (selected) {
      sendAccessibilityEvent(AccessibilityEvent.TYPE_VIEW_SELECTED);
    } // ... hidden logic
  }
}

VoiceOver and TalkBack was tested with video samples included below.

Changelog

[Android] [Fixed] - Fix Selected State does not announce when TextInput Component selected on Android

Test Plan

CLICK TO OPEN TESTS RESULTS

ENABLE THE AUDIO to hear the TalkBack announcing SELECTED when the user taps on the TextInput

        <TextInput
          accessibilityLabel="element 20"
          accessibilityState={{
            selected: true,
          }} />
selected is true
2021-03-18.16-33-01.mp4
        <TextInput
          accessibilityLabel="element 20"
          accessibilityState={{
            selected: false,
          }} />
selected is false
2021-03-18.16-32-39.mp4

The functionality does not present issues on iOS

iOS testing
RecordIt-1616079277.mp4

@facebook-github-bot facebook-github-bot added the CLA Signed This label is managed by the Facebook bot. Authors need to sign the CLA before a PR can be reviewed. label Mar 12, 2021
@fabOnReact

This comment has been minimized.

@analysis-bot
Copy link

analysis-bot commented Mar 12, 2021

Platform Engine Arch Size (bytes) Diff
ios - universal n/a --

Base commit: ac7ba3e

@analysis-bot
Copy link

analysis-bot commented Mar 12, 2021

Platform Engine Arch Size (bytes) Diff
android hermes arm64-v8a 8,952,105 +338
android hermes armeabi-v7a 8,440,911 +319
android hermes x86 9,445,625 +332
android hermes x86_64 9,388,181 +334
android jsc arm64-v8a 10,684,242 +248
android jsc armeabi-v7a 10,156,166 +256
android jsc x86 10,739,264 +235
android jsc x86_64 11,322,088 +238

Base commit: ac7ba3e

@fabOnReact fabOnReact changed the title Selected State does not annonce when TextInput Component selected [Android] Selected State does not annonce when TextInput Component selected Mar 12, 2021
@fabOnReact fabOnReact marked this pull request as ready for review March 15, 2021 16:26
@amarlette amarlette requested a review from kacieb March 15, 2021 23:14
@@ -166,8 +166,12 @@ public void setViewState(@NonNull T view, @Nullable ReadableMap accessibilitySta
if (accessibilityState == null) {
return;
}
if (accessibilityState.hasKey("selected")) {
view.setSelected(accessibilityState.getBoolean("selected"));
Copy link
Contributor Author

@fabOnReact fabOnReact Mar 16, 2021

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

the java test case does not verify that view.setSelected(true) is invoked, but just verifies view.setTag(R.id.accessibility_state, accessibilityState), I will update the test to verify those scenarios.

This comment was marked as off-topic.

This comment was marked as outdated.

Copy link
Contributor Author

@fabOnReact fabOnReact Mar 18, 2021

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Solved with commit 79486b5 more info at #31144 (comment) 🙏 ☮️

<View>
<TextInput
accessible={true}
accessibilityState={{selected: this.state.focused}}
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

cc @blavalla - Should the TextInput component always announce "selected" if it's focused and always announce "not selected" if it's blurred? Should this behavior be built into the component?

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I think there is some confusion here as to the usage of the "selected" property, which is not surprising given its vague name, and how other things (like text within the input for example) can also be "selected"!

This property is meant to represent an element that is highlighted visually for some reason. Maybe there's a list of text inputs, and only one of them is active at a time for some reason, and is highlighted in yellow to show this visually. This item would be "selected", while the others would be "not selected".

It doesn't really pertain to focus being within the input itself, or whether that input is currently being edited, or anything like that.

This same selected property can be applied to any type of view, and the issue here is simply that it doesn't work for <TextInput> like it does for <View>.

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Oh I see, thank you for the explanation! So the goal of the issue is just to allow the selected state to be set at the user's discretion, and not to always set it at any one time.

@@ -716,10 +725,8 @@ class SetAccessibilityFocusExample extends React.Component<{}> {

const onClose = () => {
if (myRef && myRef.current) {
AccessibilityInfo.sendAccessiblityEvent_unstable(
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This new method does the same thing as setAccessibilityFocus does, with the exception that this unstable method is fabric-friendly! Did this method not work for you? We don't want to change it back if we can avoid it!

Copy link
Contributor Author

@fabOnReact fabOnReact Mar 17, 2021

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@kacieb Yes, I experienced this issue with the current master branch

Do you want me to keep this change, reverse it or to investigate and fix the method?
I can not find sendAccessiblityEvent_unstable in our codebase. Thanks a lot 🙏 ☮️

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Ah got it, maybe this only works internally currently. Since this is an RNTester example, it should be ok to keep this change for now, and we can resolve it internally later.

cc @JoshuaGross and @mdvacca for awareness!

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Thanks @kacieb I reverted this change with commit 8e76ab3 🙏 ☮️

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

There's a missing "i", it should be : "sendAccessibilityEvent_unstable" instead of "sendAccessiblityEvent_unstable"

@kacieb
Copy link
Contributor

kacieb commented Mar 16, 2021

Hey! Thanks so much for working on this! I'm excited for this fix.

I'm adding in @blavalla as a reviewer to help on the Android side!

@kacieb kacieb requested a review from blavalla March 16, 2021 20:32
@blavalla
Copy link
Contributor

Just to add further clarification here, when referring to "focus" in the original issue, it is meant to mean screenreader focus, not keyboard focus. Android unfortunately uses the same term for both of these concepts, so discussing them can get confusing!

Keyboard-focus behavior is currently working as expected on TextInput, so doesn't need to be changed.

Copy link

@analysis-bot analysis-bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Code analysis results:

  • eslint found some issues. Run yarn lint --fix to automatically fix problems.

@fabOnReact fabOnReact marked this pull request as draft March 17, 2021 14:31
Copy link
Contributor

@blavalla blavalla left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The Android code in BaseViewManager.java looks good to me.

I am not really familiar with how the testing system works here, and why it's not able to mock out the view though. @kacieb, is there anyone on the React Core team who knows this system and could take a look?

@kacieb
Copy link
Contributor

kacieb commented Mar 17, 2021

I am not really familiar with how the testing system works here, and why it's not able to mock out the view though. @kacieb, is there anyone on the React Core team who knows this system and could take a look?

Sorry, I think I'm not understanding what you mean by this? Is the test not working as expected?

@blavalla
Copy link
Contributor

I am not really familiar with how the testing system works here, and why it's not able to mock out the view though. @kacieb, is there anyone on the React Core team who knows this system and could take a look?

Sorry, I think I'm not understanding what you mean by this? Is the test not working as expected?

Sorry, I may have been looking at something thats not an issue anymore. There was a few comments about the tests not working here:
https://github.com/facebook/react-native/pull/31144/files#r595064322

@fabriziobertoglio1987, just to confirm, did you get the tests working correctly?

The test below does not fail in the master branch as it does not verify the
new behaviour added with pr facebook#31144
The test previously would just verify that the `mView` had a tag `R.id.accessibility_state`
with value equal to variable `accessibilityState`

```java
assertThat(mView.getTag(R.id.accessibility_state)).isEqualTo(accessibilityState)
````

https://github.com/facebook/react-native/blob/a6cc5871e68b63e2f29bcb3610968dd33b926694/ReactAndroid/src/test/java/com/facebook/react/uimanager/BaseViewManagerTest.java#L72-L77

Now the test verifies that `mView.isSelected()` is true after passing `accessibilityState.selected` true.
I previously tried to run this verification by mocking `mView` instance,
but it is not possible and I later found the public method in the Android
sourcecode.

https://developer.android.com/reference/android/view/View#isSelected()
```java
    /**
     * Indicates the selection state of this view.
     *
     * @return true if the view is selected, false otherwise
     */
    @ViewDebug.ExportedProperty
    @InspectableProperty(hasAttributeId = false)
    public boolean isSelected() {
        return (mPrivateFlags & PFLAG_SELECTED) != 0;
    }
```

Related to https://github.com/facebook/react-native/pull/31144/files#r595064322
@fabOnReact
Copy link
Contributor Author

Thanks a lot @blavalla @kacieb for your Feedback! The comment https://github.com/facebook/react-native/pull/31144/files#r595007827 is now solved with commit 79486b5

The test discussed would not fail in the master branch as it does not verify the new behaviour added with pr #31144 in java.
The test previously would just verify that the mView had a tag R.id.accessibility_state with value equal to variable accessibilityState (the value is { selected: true}), but this behaviour was not changed in pr #31144.

assertThat(mView.getTag(R.id.accessibility_state)).isEqualTo(accessibilityState)

public void testAccessibilityStateSelected() {
WritableMap accessibilityState = Arguments.createMap();
accessibilityState.putBoolean("selected", true);
mViewManager.setViewState(mView, accessibilityState);
assertThat(mView.getTag(R.id.accessibility_state)).isEqualTo(accessibilityState);
}

Now the test verifies that mView.isSelected() returns value of true. The test fails in the current master branch (mView.isSelected() is false), because the bug of Issue #30955 is not fixed in the master branch, while the test does not fail in my pr #31144.
I previously tried to run this verification by mocking mView instance and spy on the setSelected() call, but it was not possible and I later found the public method isSelected while reading the Android sourcecode.

https://developer.android.com/reference/android/view/View#isSelected()

    /**
     * Indicates the selection state of this view.
     *
     * @return true if the view is selected, false otherwise
     */
    @ViewDebug.ExportedProperty
    @InspectableProperty(hasAttributeId = false)
    public boolean isSelected() {
        return (mPrivateFlags & PFLAG_SELECTED) != 0;
    }

Thanks a lot to Blavalla and Kacie Bawiec.
Related to https://github.com/facebook/react-native/pull/31144/files#r595064322

is a useful reminder in the future to fix fabric method sendAccessiblityEvent_unstable in the future
https://github.com/facebook/react-native/pull/31144/files#r596273281
@fabOnReact fabOnReact marked this pull request as ready for review March 18, 2021 11:49
Copy link

@analysis-bot analysis-bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Code analysis results:

  • eslint found some issues. Run yarn lint --fix to automatically fix problems.

@fabOnReact fabOnReact marked this pull request as draft March 18, 2021 11:59
I have been working for a while with typescript and forgot to update my
vim setup :-)
@fabOnReact fabOnReact marked this pull request as ready for review March 18, 2021 12:11
@fabOnReact fabOnReact marked this pull request as draft March 18, 2021 13:54
@fabOnReact fabOnReact marked this pull request as ready for review March 18, 2021 14:01
@fabOnReact fabOnReact marked this pull request as draft March 18, 2021 15:10
@fabOnReact fabOnReact marked this pull request as ready for review March 18, 2021 15:36
@fabOnReact
Copy link
Contributor Author

fabOnReact commented Mar 18, 2021

I removed the prop accessibilityState that I previously added to TextInput. I re-run and updated all video recordings of test cases on Android and iOS #31144 (comment), the accessibilityState prop seems to not be required in the TextInput component for both iOS and Android. Thanks a lot and sorry for my mistake. 🙏 ☮️

export type Props = $ReadOnly<{|
...$Diff<ViewProps, $ReadOnly<{|style: ?ViewStyleProp|}>>,

/**
* Indicates to accessibility services that UI Component is in a specific State.
*/
accessibilityState?: ?AccessibilityState,

@facebook-github-bot
Copy link
Contributor

@kacieb has imported this pull request. If you are a Facebook employee, you can view this diff on Phabricator.

@facebook-github-bot
Copy link
Contributor

@kacieb merged this pull request in 7ee2acc.

@facebook-github-bot facebook-github-bot added the Merged This PR has been merged. label Apr 5, 2021
@amarlette
Copy link

Thank you @fabriziobertoglio1987 for this contribution! I would like to give you a shout-out on Twitter and include you in our end-of-month issues update for your contribution. Do you have a Twitter we can tag?

@fabOnReact
Copy link
Contributor Author

Thanks a lot @amarlette I don't use often twitter and I would prefer that you use my github account https://github.com/fabriziobertoglio1987. I have an old twitter account https://twitter.com/fabri_on_rails. Feel free to use any of those account (twitter and/or github). I wish you a good day ☮️ 🙏 😃

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
Accessibility CLA Signed This label is managed by the Facebook bot. Authors need to sign the CLA before a PR can be reviewed. Merged This PR has been merged.
Projects
None yet
Development

Successfully merging this pull request may close these issues.

Selected State does not annonce when TextInput Component selected
7 participants